File: table.lisp

package info (click to toggle)
clue 20011230
  • links: PTS
  • area: main
  • in suites: woody
  • size: 6,112 kB
  • ctags: 2,646
  • sloc: lisp: 31,991; makefile: 40; sh: 24
file content (1621 lines) | stat: -rw-r--r-- 63,155 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
;;; -*- Mode:Lisp; Package:CLIO-OPEN; Base:10; Lowercase:T; Syntax:Common-Lisp -*-


;;;----------------------------------------------------------------------------------+
;;;                                                                                  |
;;;                          TEXAS INSTRUMENTS INCORPORATED                          |
;;;                                  P.O. BOX 149149                                 |
;;;                                AUSTIN, TEXAS 78714                               |
;;;                                                                                  |
;;;             Copyright (C) 1989, 1990 Texas Instruments Incorporated.             |
;;;                                                                                  |
;;; Permission is granted to any individual or institution to use, copy, modify, and |
;;; distribute this software, provided that  this complete copyright and  permission |
;;; notice is maintained, intact, in all copies and supporting documentation.        |
;;;                                                                                  |
;;; Texas Instruments Incorporated provides this software "as is" without express or |
;;; implied warranty.                                                                |
;;;                                                                                  |
;;;----------------------------------------------------------------------------------+

(in-package "CLIO-OPEN")


(export '(
	  table
	  make-table
	  table-column-alignment
	  table-column-width
	  table-columns
	  table-delete-policy
	  table-layout-size-policy
	  table-member
	  table-row-alignment
	  table-row-height
	  table-same-height-in-row
	  table-same-width-in-column
	  table-separator
	  table-row
	  table-column
	  )
	'clio-open)

;;;
;;;  Call-Tree...
;;;


;;;   Preferred-Size (Table)
;;;   .  check-for-existing-wis
;;;   .  place-children-physically
;;;   .  .  put-kids-into-maximum-unaligned-columns
;;;   .  .  .  find-first-parents-width 
;;;   .  .  .  assign-kids-to-rows-and-columns
;;;   .  .  .  preferred-size (child)
;;;   .  .  .  move (child)
;;;   .  .  .  resize (child)
;;;   .  .  put-kids-into-maximum-aligned-columns
;;;   .  .  .  assign-kids-to-rows-and-columns
;;;   .  .  .  .  assign-a-kid-to-a-row-and-column
;;;   .  .  .  .  build-sorted-list-of-children
;;;   .  .  .  get-maximum-possible-ncolumns
;;;   .  .  .  .  preferred-size (child)
;;;   .  .  .  preferred-size (child)
;;;   .  .  .  adjust-column-widths-so-child-fits
;;;   .  .  put-kids-into-specified-number-of-columns   
;;;   .  .  .  assign-kids-to-rows-and-columns
;;;   .  .  .  preferred-size (child)
;;;   .  .  scan-for-largest-children
;;;   .  .  .  preferred-size (child)
;;;   .  .  determine-a-rows-height
;;;   .  .  preferred-size (child)
;;;   .  .  move (child)
;;;   .  .  resize (child)
;;;   .  .  calculate-preferred-height
;;;   .  .  determine-a-rows-height
;;;   .  .  .  preferred-size (child)
;;;   .  .  calculate-preferred-width
;;;   .
;;;   Change-Layout(Table)
;;;   .  check-for-existing-wis
;;;   .  place-children-physically
;;;   .  change-geometry (Table)
;;;   .
;;;   Resize :after (Table)
;;;   .  change-layout (Table)
;;;   .
;;;   Manage-Geometry (Table)
;;;   .  Change-Geometry (Table)

;;;  Basic Organization and Flow:
;;;	The Table contact lays out its children per the values of its policy resources and the
;;;	row/column constraints of its children, with the resource values always taking precedence
;;;	over the children's constraint values.  
;;;
;;;	The function place-children-physically does the real work of Table.  
;;;
;;;	The differences in Table's logical flow for the possible values for the :columns resource
;;;	are embodied primarily in the three routines
;;;
;;;		put-kids-into-maximum-unaligned-columns
;;;		put-kids-into-maximum-aligned-columns
;;;		put-kids-into-specified-number-of-columns
;;;
;;;	There are 5 ways into the Table contact's logic:
;;;
;;;		Preferred-Size (Table)
;;;		Change-Layout (Table)
;;;		Resize :after (Table)
;;;		Manage-Geometry (Table)
;;;		(SETF layout-policy-resource)
;;;


;;;  ===========================================================================
;;;		T h e   T A B L E   L a y o u t   C o n t a c t 
;;;  ===========================================================================

(DEFCONTACT table (gravity-mixin spacing-mixin core composite)
  ((column-alignment	:type		(MEMBER :left :center :right)
			:reader  	table-column-alignment	; SETF method defined below.
			:initarg	:column-alignment
			:initform	:left)
   
   (column-width	:type		(OR (MEMBER :maximum) cons (integer 1 *))
			:reader  	table-column-width	; SETF method defined below.
			:initarg	:column-width
			:initform	:maximum)
   
   (columns		:type		(OR (integer 1 *) (MEMBER :maximum :none))
			:reader  	table-columns	        ; SETF method defined below.
			:initarg	:columns
			:initform	:maximum)

   (delete-policy	:type	      (MEMBER :shrink-list :shrink-column :shrink-none :shrink-row)
			:reader  	table-delete-policy	; SETF method defined below.
			:initarg	:delete-policy
			:initform	:shrink-none)

   (layout-size-policy	:type		(MEMBER :maximum :minimum :none)
			:reader  	table-layout-size-policy ; SETF method defined below.
			:initarg	layout-size-policy
			:initform	:maximum)

   (row-height		:type		(OR (MEMBER :maximum) cons (integer 1 *))
			:reader  	table-row-height	; SETF method defined below.
			:initarg	:row-height
			:initform	:maximum)

   (row-alignment	:type		(MEMBER :top :center :bottom)
			:reader  	table-row-alignment	; SETF method defined below.
			:initarg	:row-alignment
			:initform	:bottom)

   (same-height-in-row
     			:type		(MEMBER :on :off)
			:reader  	table-same-height-in-row ; SETF method defined below.
			:initarg	:same-height-in-row
			:initform       :off)

   (same-width-in-column
     			:type		(MEMBER :on :off)
			:reader  	table-same-width-in-column ; SETF method defined below.
			:initarg	:same-width-in-column
			:initform	:off)

   (separators		:type		list
			:initarg	:separators
			:initform	nil))

  (:resources
    (border-width :initform 0)
    column-alignment
    column-width
    columns
    delete-policy
    layout-size-policy
    row-alignment    
    row-height
    same-height-in-row
    same-width-in-column
    separators)


  (:constraints
    (row 		:type 		(integer 0 *))
    (column 		:type 		(integer 0 *)))


  (:documentation
    "Arranges its children in an array of rows and columns."
    ))



(DEFUN make-table (&rest initargs &key &allow-other-keys)
  (APPLY #'make-contact 'table initargs))

;;;  ===========================================================================  ;;; 
;;;	      ORG-ENTRY: the entries on the what-if-organization list		  ;;;
;;;  ===========================================================================  ;;;

(DEFSTRUCT (org-entry :named (:type vector) (:conc-name "ORG-ENTRY-"))
  kid
  row
  column
  width
  height
  border-width)

(DEFUN establish-org-entry (kid row column)
  (MULTIPLE-VALUE-BIND (p-w p-h p-b-w)
      (preferred-size kid)
    (make-org-entry :kid kid :row row :column column
		    :width p-w :height p-h :border-width p-b-w)))


;;;  ===========================================================================  ;;; 
;;;		       What-if Structures and Their management			  ;;;
;;;  ===========================================================================  ;;;

;;;
;;;   Structures of this kind are placed on the Table's plist under the :what-if-structures
;;;   property to record already-performed preferred-size calculations for the current set of
;;;   policy resource values but different widths/heights.  Any change to a policy resource
;;;   destroys this cache of what-if structures, as does a call to change-layout.
;;;

;;;   Hmmmm...  We must keep the children's sizes here, have all the layout logic look here
;;;   rather than at the kids' preferred-size methods.  Where to keep this info?  In organization
;;;   (which is already a list of the kids) or in another list of kids, widths, heights, and
;;;   border-widths.  Or in an array...

(DEFSTRUCT (what-if-structure :named (:type vector) (:conc-name "WHAT-IF-"))
  width
  height
  border-width
  organization					; org-entrys for :mapped children only!
  column-widths
  nrows
  ncolumns
  (preferred-width 0)
  (preferred-height 0)
  in-use
  )

(DEFUN check-for-existing-wis (table width height border-width &optional dont-create-p)
  ;;  Returns the first (newest) wis found with width/height.
  ;;  If no wis satisfying width/height exists, create a new one unless DONT-CREATE-P
  ;;  is true, in which case return NIL.
  (LET ((old-wis-list (GETF (window-plist table) :what-if-structures)) wis)
    (SETF wis (FIND-IF #'(lambda (wis)
			   (AND (EQL (what-if-width wis) width)
				(EQL (what-if-height wis) height)
				(EQL (what-if-border-width wis) border-width)))
		       old-wis-list))
    (UNLESS (OR wis dont-create-p)
      (SETF (GETF (window-plist table) :what-if-structures)
	    (PUSH (SETF wis (make-what-if-structure :width width
						    :height height
						    :border-width border-width
						    :preferred-width 0
						    :preferred-height 0))
		  old-wis-list))
      )
    wis))


;;;  ===========================================================================  ;;; 
;;;		         A Table's Constraint's Accessors			  ;;;
;;;  ===========================================================================  ;;;


(defun table-row (member)
  (contact-constraint member :row))

(defsetf table-row setf-table-row)
(defun setf-table-row (member row)
  (check-type row (or null (integer 0 *)))
  (setf (contact-constraint member :row) row))

(defun table-column (member)
  (contact-constraint member :column))

(defsetf table-column setf-table-column)
(defun setf-table-column (member column)
  (check-type column (or null (integer 0 *)))
  (setf (contact-constraint member :column) column))




;;;  ===========================================================================  ;;; 
;;;		       SETF functions for a Table's Resources			  ;;;
;;;  ===========================================================================  ;;; 

(defmethod (setf display-left-margin) :after (new-value (table table))
  (declare (ignore new-value))
  (change-layout table))

(defmethod (setf display-right-margin) :after (new-value (table table))
  (declare (ignore new-value))
  (change-layout table))

(defmethod (setf display-top-margin) :after (new-value (table table))
  (declare (ignore new-value))
  (change-layout table))

(defmethod (setf display-bottom-margin) :after (new-value (table table))
  (declare (ignore new-value))
  (change-layout table))



(FLET ((force-relayout (table)
	 (SETF (GETF (window-plist table) :what-if-structures) nil)
	 (change-layout table))
       )

  (DEFMETHOD (SETF display-horizontal-space) :after (new-value (table table))
    (DECLARE (IGNORE new-value))
    (force-relayout table))

  (DEFMETHOD (SETF table-column-alignment) (new-value (table table))
    (with-slots (column-alignment) table
      (SETF column-alignment new-value)
      (force-relayout table)
      new-value))
  
  (DEFMETHOD (SETF table-column-width) (new-value (table table))
    (with-slots (column-width) table
      (SETF column-width new-value)
      (force-relayout table)
      new-value))
  
  (DEFMETHOD (SETF table-columns) (new-value (table table))
    (with-slots (columns) table
      (SETF columns new-value)
      (DOLIST (kid (composite-children table))
	(SETF (table-column kid) nil
	      (table-row kid) nil))
      (force-relayout table)
      new-value))
  
  (DEFMETHOD (SETF table-delete-policy) (new-value (table table))
    (with-slots (delete-policy) table
      (SETF delete-policy new-value)
      (force-relayout table)
      new-value))
  
  (DEFMETHOD (SETF table-layout-size-policy) (new-value (table table))
    (with-slots (layout-size-policy) table
      (SETF layout-size-policy new-value)
      (force-relayout table)
      new-value))
  
  (DEFMETHOD (SETF table-row-height) (new-value (table table))
    (with-slots (row-height) table
      (SETF row-height new-value)
      (force-relayout table)
      new-value))
  
  (DEFMETHOD (SETF table-row-alignment) (new-value (table table))
    (with-slots (row-alignment) table
      (SETF row-alignment new-value)
      (force-relayout table)
      new-value))
  
  (DEFMETHOD (SETF table-same-width-in-column) (new-value (table table))
    (CHECK-TYPE new-value (MEMBER :on :off))
    (with-slots (same-width-in-column) table
      (SETF same-width-in-column new-value)
      (force-relayout table)
      new-value))
  
  (DEFMETHOD (SETF table-same-height-in-row) (new-value (table table))
    (CHECK-TYPE new-value (MEMBER :on :off))
    (with-slots (same-height-in-row) table
      (SETF same-height-in-row new-value)
      (force-relayout table)
      new-value))
  
  
;;;  ===========================================================================  ;;; 
;;;		         A Table's Separator Methods 			  	  ;;;
;;;  ===========================================================================  ;;;
  
;;;  Note: The physical size of an OL UI separator (white-space) will be defined 
;;;	   to be half the height of the row it follows.
  
  (DEFMETHOD table-separator ((table table) row-number)
    (DECLARE (type integer row-number))
    (check-type row-number (integer 0 *))
    (with-slots (separators) table
      (IF (MEMBER row-number separators) :on :off)))
  
  
  (DEFMETHOD (SETF table-separator) (on-or-off (table table) row-number)
    (DECLARE (type integer row-number))
    (check-type row-number (integer 0 *))
    (with-slots (separators) table
      (LET ((already-there-p (MEMBER row-number separators)))
	(ECASE on-or-off
	  (:on (UNLESS already-there-p
		 (PUSH row-number separators)
		 (force-relayout table)))
	  (:off (WHEN already-there-p
		  (SETF separators (DELETE row-number separators))
		  (force-relayout table))))))	
    on-or-off)
  
  
  
  
;;;  ===========================================================================  ;;; 
;;;		         A Table's Table-Member Method 			  	  ;;;
;;;  ===========================================================================  ;;;
  
  (DEFMETHOD table-member ((table table) row column)
    ;;  Return NIL if there is no child at position row/column.
    (LET ((wis (check-for-existing-wis table (contact-width table) (contact-height table)
				       (contact-border-width table))))
      (WHEN wis
	(org-entry-kid (FIND-IF #'(lambda (x)
				    (AND (= (org-entry-row x) row)
					 (= (org-entry-column x) column)))
				(REST (what-if-organization wis)))))))
  
  (DEFMETHOD (SETF table-member) (new-value (table table) row column)
    ;;  What should we do with the child currently at position row/column?
    ;;  Set its constraints to NIL?  Set just one of its constraints to NIL?
    ;;  Error if there's one there?  I've chosen to blast its constraints.
    (LET ((existing-child-at-that-position (table-member table row column)))
      (WHEN existing-child-at-that-position
	(SETF (table-row existing-child-at-that-position) nil
	      (table-column existing-child-at-that-position) nil))
      (SETF (table-row new-value) row)
      (SETF (table-column new-value) column)
      (force-relayout table)
      new-value))
  )



;;;  ===========================================================================  ;;; 
;;;		         A Table's Preferred-Size Method                          ;;;
;;;  ===========================================================================  ;;; 

(DEFMETHOD preferred-size ((table table) &key width height border-width)

  ;;
  ;;  Handle the case where we have no children...
  ;;
  (with-slots (children) table
    (UNLESS children
      (RETURN-FROM preferred-size
	(VALUES (+ (display-left-margin table) (display-right-margin table))
		(+ (display-top-margin table) (display-bottom-margin table))
		(contact-border-width table)))))
    
  
  (with-slots ((old-width width) (old-height height) (old-border-width border-width)) table    

    ;;
    ;;  When the caller specifies no what-if values and we have a good width & height, always
    ;;  return our current values...
    ;;
    (WHEN (AND (NULL width) (NULL height) (/= 0 old-width) (/= 0 old-height))

      (RETURN-FROM preferred-size (VALUES old-width old-height old-border-width)))

    ;;
    ;;  We need to what-if.  Figure out the width, height, and border-width to use...
    ;;
    (SETF width (OR width old-width)
	  height (OR height old-height)
	  border-width (OR border-width old-border-width))

    
    (LET ((wis (check-for-existing-wis table width height border-width)))

      (UNLESS (AND (what-if-organization wis)
		   (= (what-if-preferred-width wis) width)
		   (= (what-if-preferred-height wis) height))
	(place-children-physically table wis nil))

      (VALUES (what-if-preferred-width wis)
	      (what-if-preferred-height wis)
	      border-width))))


;;;  ===========================================================================  ;;; 
;;;		         A Table's Change-Layout Method                           ;;;
;;;  ===========================================================================  ;;; 

(DEFMETHOD change-layout ((table table) &optional newly-managed)
  (declare (type (or null contact) newly-managed))
  (DECLARE (SPECIAL *called-from-resize-method*))
  
  (with-slots (width height border-width) table
    
    ;;  Just update the current wis if a single child is being withdrawn...
    (when (AND newly-managed (EQ (contact-state newly-managed) :withdrawn))
      (LET ((wis (check-for-existing-wis table width height border-width)))
	(WHEN wis
	  (SETF (REST (what-if-organization wis))
		(DELETE newly-managed (REST (what-if-organization wis))
			:key #'org-entry-kid)))))
    
    (LET (p-width p-height
	  (wis (check-for-existing-wis table width height border-width)))
      ;;  With a change in layout we must really re-layout our children...
      (unless (what-if-in-use wis)
	(SETF (what-if-in-use wis) t)
	(place-children-physically table wis t)
	
	;;
	;;  Update the children's row/column constraints...
	;;
	(DOLIST (o-e (REST (what-if-organization wis)))
	  (SETF (table-row (org-entry-kid o-e)) (org-entry-row o-e)
		(table-column (org-entry-kid o-e)) (org-entry-column o-e)))
	
	(UNLESS (AND (BOUNDP '*called-from-resize-method*) *called-from-resize-method*)
	  (SETF p-width (what-if-preferred-width wis)
		p-height (what-if-preferred-height wis))     
	  
	  (UNLESS (AND (= height p-height) (= width p-width))
	    (SETF (what-if-width wis) p-width
		  (what-if-height wis) p-height)	      
	    (change-geometry table :width p-width :height p-height :accept-p t)))
	(SETF (what-if-in-use wis) nil)))))


;;;  ===========================================================================  ;;; 
;;;		         A Table's Resize :after Method                           ;;;
;;;  ===========================================================================  ;;; 

(DEFMETHOD resize :after ((table table) width height b-width)
  (DECLARE (IGNORE  width height b-width))
  (LET ((*called-from-resize-method* t))
    (DECLARE (SPECIAL *called-from-resize-method*))
    (change-layout table)))


						
;;;  ===========================================================================  ;;; 
;;;		         A Table's Manage-Geometry Method                         ;;;
;;;  ===========================================================================  ;;; 

;;;  This is not right yet.  It should run a what-if to get a Table size for the child's 
;;;  size change, but this is not possible yet -- the wis doesn't keep all children's
;;;  sizes.  Then it must call change-geometry to see if its parent will let it be that
;;;  size.  If so, it should return a thunk that invokes resize, not change-geometry.

(defmethod manage-geometry ((table table) child x y width height border-width &key)
  (values
      (if
	(or (and x (/= x (contact-x child)))
	    (and y (/= y (contact-y child)))
	    (and width (/= width (contact-width child)))
	    (and height (/= height (contact-height child)))
	    (and border-width (/= border-width (contact-border-width child))))
	#'(lambda (self)
	    (multiple-value-bind (p-w p-h p-b-w)
		(preferred-size self)
	      (change-geometry self
			       :width p-w
			       :height p-h
			       :border-width p-b-w
			       :accept-p t)
	      (change-layout self)
	      (display-force-output (contact-display self))))
	t)
      (or x (contact-x child))
      (or y (contact-y child))
      (or width (contact-width child))
      (or height (contact-height child))
      (or border-width (contact-border-width child))))



;;;
;;;   Internal routines that calculate the width/height of a table, given a What-if-Structure...
;;;		Calculate-Preferred-Width 
;;;		Calculate-Preferred-Height 

(DEFUN calculate-preferred-width (table wis)
  (LET* ((ncolumns (what-if-ncolumns wis))
	 (column-widths (what-if-column-widths wis))
	 (table-width (+ (display-left-margin table)
			 (display-right-margin table)
			 (* (1- ncolumns) (display-horizontal-space table)))))
    (DOTIMES (column ncolumns)
      (INCF table-width (AREF column-widths column 0)))
    table-width))


(DEFUN calculate-preferred-height (table wis)
  (with-slots (row-height separators) (THE table table)

    (LET* ((nrows (what-if-nrows wis))
	   (organization (what-if-organization wis))
	   (table-height (+ (display-top-margin table)
			    (display-bottom-margin table)
			    (* (1- nrows) (display-vertical-space table))))
	   (org-list (REST organization))
	   (fixed-row-heights row-height) height-for-this-row)

      (DO ((row 0 (1+ row)))
	  ((= row nrows))

	(MULTIPLE-VALUE-SETQ (height-for-this-row fixed-row-heights org-list)
	  (determine-a-rows-height row fixed-row-heights org-list))
	
	(INCF table-height height-for-this-row)

	;;  Note:  The physical size of an OL UI separator (white-space) will be defined 
	;;	   to be half the height of the row it follows.  A separator placed after
	;;	   the last row will result in extra white-space at the bottom of the table.
	(WHEN (MEMBER row separators)
	  (INCF table-height (FLOOR (+ height-for-this-row (display-vertical-space table)) 2))))
      
      table-height)))


(DEFUN determine-a-rows-height (row fixed-row-heights org-list1)
  (LET (fixed-height-for-this-row (height-for-this-row 0) found-a-kid-in-this-row-p)
    
    (TYPECASE fixed-row-heights
      (integer
       (SETF fixed-height-for-this-row fixed-row-heights))
      (cons
       (SETF fixed-height-for-this-row (FIRST fixed-row-heights))
       (SETF fixed-row-heights (REST fixed-row-heights))))
    
    (IF fixed-height-for-this-row
	(SETF height-for-this-row fixed-height-for-this-row)
	
	;;else find the tallest element and the largest border width in this row...
	(progn
	  (DO ((org-list1 org-list1 (REST org-list1))
	       kid1 org-entry1 (kid1s-row row))
	      ((OR (NULL org-list1) (AND found-a-kid-in-this-row-p (/= row kid1s-row))))
	    (SETF org-entry1 (FIRST org-list1))
	    (SETF kid1 (org-entry-kid org-entry1)
		  kid1s-row (org-entry-row org-entry1))
	    (WHEN (= row kid1s-row)
	      (SETF found-a-kid-in-this-row-p t)
	      (SETF height-for-this-row
		    (MAX height-for-this-row
			 (+ (org-entry-height org-entry1)
			    (org-entry-border-width org-entry1)
			    (org-entry-border-width org-entry1))))))))
    ;;
    ;;  Because all the members of a row may be withdrawn (and therefore not on the
    ;;  what-if-organization list) it is quite possible to find no children in a row.  For now
    ;;  such a row collapses to zero-height...
    (VALUES height-for-this-row fixed-row-heights org-list1)))






;;;  ===========================================================================  ;;; 
;;;	          The Guts of Table: Place-Children-Physically			  ;;;
;;;  ===========================================================================  ;;; 

(DEFUN place-children-physically (table wis really-p)  
  
  (with-slots (children same-width-in-column same-height-in-row columns
			column-alignment row-alignment
			column-width row-height
			separators) (THE table table)
    
    (LET (kid last-kid-processed height-for-this-row x1 y1
	  (fixed-row-heights (UNLESS (EQ row-height :maximum) row-height))
	  fixed-column-widths
	  width-for-this-column
	  childs-horizontal-size       ; Including border-widths.
	  childs-vertical-size	       ; Including border-widths.
	  max-child-heights-by-row
	  max-child-widths-by-columns
	  org-entry kids-row kids-column
	  y)

      (UNLESS children
	(RETURN-FROM place-children-physically))

      (CASE columns
	(:none
	 (put-kids-into-maximum-unaligned-columns table wis really-p)
	 (RETURN-FROM place-children-physically))

	(:maximum
	 ;; XtNmaximumColumns.
	 ;; Must scan the kids to figure out what width each column should be.
	 (put-kids-into-maximum-aligned-columns table wis))
	
	(otherwise
	 (UNLESS (INTEGERP columns)
	   (ERROR "~s is not a legal value for :columns" columns))
	 ;; XtNrequestedColumns.
	 (put-kids-into-specified-number-of-columns table wis)))
      
      ;;
      ;;  Position the children on the test sheet per the columnarization...
      ;;
      (WHEN really-p
	(MULTIPLE-VALUE-SETQ (max-child-heights-by-row max-child-widths-by-columns)
	  (scan-for-largest-children wis))

	(LET ((org-list (REST (what-if-organization wis)))
	      (column-widths (what-if-column-widths wis)))
	  (SETF y (display-top-margin table))
	  (CATCH 'out-of-kids
	    (DOTIMES (row (what-if-nrows wis))
	      (SETF fixed-column-widths (UNLESS (EQ column-width :maximum) column-width))
	      
	      (MULTIPLE-VALUE-SETQ (height-for-this-row fixed-row-heights)
		(determine-a-rows-height row fixed-row-heights org-list))
	      
	      (LET ((fixed-width-for-this-column
		      (AND (INTEGERP fixed-column-widths) fixed-column-widths))
		    (x (display-left-margin table)))
		
		;;  Now set the row's elements' geometries...
		(DOTIMES (column (what-if-ncolumns wis))
		  (WHEN (EQ kid last-kid-processed)
		    (SETF org-entry (FIRST org-list))
		    (WHEN (NULL org-entry)
		      (THROW 'out-of-kids t))
		    (SETF kid (org-entry-kid org-entry)
			  kids-row (org-entry-row org-entry)
			  kids-column (org-entry-column org-entry)))
		  
		  ;;  Figure out what width WE want this column to be...
		  (WHEN (CONSP fixed-column-widths)
		    (SETF fixed-width-for-this-column (FIRST fixed-column-widths)))
		  (SETF width-for-this-column
			(OR fixed-width-for-this-column (AREF column-widths column 0)))
		  (WHEN (AND (= row kids-row) (= column kids-column))		      
		    (SETF childs-horizontal-size (+ (org-entry-width org-entry)
						    (org-entry-border-width org-entry)
						    (org-entry-border-width org-entry))
			  childs-vertical-size (+ (org-entry-height org-entry)
						  (org-entry-border-width org-entry)
						  (org-entry-border-width org-entry)))
		    
		    (IF (EQ same-width-in-column :on)
			(SETF childs-horizontal-size width-for-this-column
			      x1 x)
		      ;; else...
		      (SETF childs-horizontal-size (MIN childs-horizontal-size
							width-for-this-column)
			    x1 (CASE column-alignment
				 (:left x)
				 (:right (+ x (- width-for-this-column
						 childs-horizontal-size)))
				 (:center (+ x (FLOOR (- width-for-this-column
							 childs-horizontal-size) 2))))))
		    
		    (IF (EQ same-height-in-row :on)
			(SETF childs-vertical-size height-for-this-row
			      y1 y)
		      ;; else...
		      (SETF childs-vertical-size (MIN childs-vertical-size
						      height-for-this-row)
			    y1 (CASE row-alignment
				 (:top y)
				 (:bottom (+ y (- height-for-this-row
						  childs-vertical-size)))
				 (:center (+ y (FLOOR (- height-for-this-row
							 childs-vertical-size) 2))))))
		    
		    ;;
		    ;;   Reposition and/or resize the child iff needed...
		    ;;
		    (LET ((desired-width (- childs-horizontal-size
					    (org-entry-border-width org-entry)
					    (org-entry-border-width org-entry)))
			  (desired-height (- childs-vertical-size
					     (org-entry-border-width org-entry)
					     (org-entry-border-width org-entry))))
		      (with-state (kid)
			(UNLESS (AND (= x1 (contact-x kid))
				     (= y1 (contact-y kid)))
			  (move kid x1 y1))
			(UNLESS (AND (= desired-width (contact-width kid))
				     (= desired-height (contact-height kid))
				     (= (org-entry-border-width org-entry)
					 (contact-border-width kid)))
			  (resize kid desired-width desired-height
				  (org-entry-border-width org-entry))))

		      ;;
		      ;;   Done with this child, move on to the next...
		      ;;
		      (SETF org-list (REST org-list))
		      (SETF last-kid-processed kid)))	  

		    ;;
		    ;;   Whether or not a kid was placed at this row/column, move on to the
		    ;;   next column...
		    (INCF x (+ width-for-this-column
			       (display-horizontal-space table)))
		    (WHEN (CONSP fixed-column-widths)
		      (SETF fixed-column-widths (REST fixed-column-widths))))
		    
		;;
		;;   Get vertical position of top of borders of next row's elements...
		;;
		(INCF y (+ height-for-this-row
			   (display-vertical-space table)))
		(WHEN (MEMBER row separators)
		  (INCF y (FLOOR (+ height-for-this-row
				    (display-vertical-space table)) 2))))))
	  ))
	
	;;
	;;   Having finished placing the kids we can put our preferred size into our wis...
	;;
      (SETF (what-if-preferred-height wis) (calculate-preferred-height table wis)
	    (what-if-preferred-width wis) (calculate-preferred-width table wis))
	)))

    
  
(DEFUN scan-for-largest-children (wis)
  
  (LET* ((max-child-heights-by-row (MAKE-ARRAY (what-if-nrows wis) :initial-element 0))
	 (max-child-widths-by-column (MAKE-ARRAY (what-if-ncolumns wis) :initial-element 0)))
    
    (DOLIST (org-entry (REST (what-if-organization wis)))
      (LET ((row (org-entry-row org-entry))
	    (column (org-entry-column org-entry))
	    (total-child-width (+ (org-entry-width org-entry)
				  (org-entry-border-width org-entry)
				  (org-entry-border-width org-entry)))
	    (total-child-height (+ (org-entry-height org-entry)
				   (org-entry-border-width org-entry)
				   (org-entry-border-width org-entry))))
	(SETF (SVREF max-child-heights-by-row row)
	      (MAX (SVREF max-child-heights-by-row row) total-child-height))
	(SETF (SVREF max-child-widths-by-column column)
	      (MAX (SVREF max-child-widths-by-column column) total-child-width))))
    
    (VALUES max-child-heights-by-row max-child-widths-by-column)))



(DEFUN put-kids-into-specified-number-of-columns (table wis)

  
  (with-slots (column-width columns children) (THE table table)
    (LET* (fixed-width-for-this-column total-kid-width 
	   (fixed-widths-for-columns column-width))

      (SETF (what-if-ncolumns wis) 	 columns
	    (what-if-nrows wis)	  	(CEILING (LENGTH children) columns)
	    (what-if-column-widths wis) (MAKE-ARRAY `(,columns 2) :initial-element 0))

      ;;  Construct the organization list by assigning the children to specific row/column
      ;;  positions in the Table...
      (assign-kids-to-rows-and-columns table wis)

      ;;  Ncolumns was specified by the user.  Nrows was determined from this and by
      ;;  assign-kids-to-rows-and-columns.  This routine scans the organization and builds the array
      ;;  of (list column-width width-of-widest-entry-column) entries.  This array is left in the
      ;;  column-widths slot.

      ;;
      ;;  Find the widest child in each row, set the 2nd element of each width-of-columns
      ;;  entry to the width of the widest child in that column...
      ;;
      (DO ((org-list1 (REST (what-if-organization wis)) (REST org-list1))
	   kid1 org-entry1 kid1s-column kid1s-row)
	  ((NULL org-list1))
	(SETF org-entry1 (FIRST org-list1))
	(SETF kid1 (org-entry-kid org-entry1)
	      kid1s-row (org-entry-row org-entry1)
	      kid1s-column (org-entry-column org-entry1))
	(SETF total-kid-width (+ (org-entry-width org-entry1)
				 (org-entry-border-width org-entry1)
				 (org-entry-border-width org-entry1)))
	(Setf (AREF (what-if-column-widths wis) kid1s-column 1)
	      (MAX (AREF (what-if-column-widths wis) kid1s-column 1) total-kid-width)))
      

      ;;
      ;;  Now go through the columns looking for those with pre-set widths.  Use any pre-set
      ;;  width as the column's width, otherwise use the width of the column's widest child.
      ;;
      (SETF fixed-widths-for-columns column-width)
      (DOTIMES (current-column (what-if-ncolumns wis))
	;;  Get current-column's fixed width, if any...
	(SETF fixed-width-for-this-column 
	      (TYPECASE fixed-widths-for-columns
		(integer fixed-widths-for-columns)
		(CONS (PROG1 (FIRST fixed-widths-for-columns)
			     (SETF fixed-widths-for-columns (REST fixed-widths-for-columns))))))
	(SETF (AREF (what-if-column-widths wis) current-column 0)
	      (OR fixed-width-for-this-column (AREF (what-if-column-widths wis) current-column 1)))))))

(DEFUN find-first-parents-width (table)
  (DO ((parent (contact-parent table) (contact-parent parent)))
      ((NULL parent))
    (UNLESS (ZEROP (contact-width parent))
      (RETURN (contact-width parent)))))

(DEFUN put-kids-into-maximum-unaligned-columns (table wis really-p)

  (with-slots (children same-width-in-column) (THE table table)
      
    (LET* ((org-list (LIST nil))
	   (working-width (what-if-width wis))
	   (border-width (what-if-border-width wis)))
      
      (WHEN (ZEROP working-width)
	  (SETF working-width (- (find-first-parents-width table) border-width border-width)))
      
      ;;  Start by sorting the list of children by their row/column constraints.  Once this is
      ;;  done we ignore the constraints from here on for :none layout policy...
      (LET ((nkids (LENGTH children)))
	(SETF (what-if-nrows wis) nkids
	      (what-if-ncolumns wis) nkids)
	(assign-kids-to-rows-and-columns table wis))
      
      (LET ((next-x-pos (display-left-margin table))
	    (next-y-pos (display-top-margin table))
	    (largest-height-this-row 0)
	    (columns-this-row 0)
	    (ncolumns-in-table 0)
	    (nrows-in-table 0)
	    (preferred-width-of-table 0))
	
	(FLET
	  ((handle-the-end-of-a-row ()
	     (SETF ncolumns-in-table (MAX ncolumns-in-table columns-this-row))
	     (SETF preferred-width-of-table
		   (MAX preferred-width-of-table
			(+ next-x-pos
			   (- (display-right-margin table)
			      (display-horizontal-space table)))))
	     (SETF next-x-pos (display-left-margin table))
	     (INCF nrows-in-table)
	     (INCF next-y-pos (+ largest-height-this-row
				 (display-vertical-space table)))
	     (SETF columns-this-row 0
		   largest-height-this-row 0))
	   )
	  
	  (DOLIST (child children)
	    (UNLESS (EQ (contact-state child) :withdrawn)
	      (MULTIPLE-VALUE-BIND (childs-p-width childs-p-height childs-p-border-width)
		  (preferred-size child)
		(LET ((childs-total-width (+ childs-p-width (* 2  childs-p-border-width)))
		      (childs-total-height (+ childs-p-height (* 2  childs-p-border-width))))
		  
		  ;;
		  ;;  If cannot place this child at the end of this row, finish off this row and move
		  ;;  on to the next row...
		  ;;
		  (WHEN (< (- working-width next-x-pos (display-right-margin table))
			   childs-total-width)
		    (handle-the-end-of-a-row))
		  ;;
		  ;;  Position this child where we've decided it should go...
		  ;;
		  (WHEN really-p
		    (with-state (child)
		      (UNLESS (AND (= next-x-pos (contact-x child))
				   (= next-y-pos (contact-y child)))
			(move child next-x-pos next-y-pos))
		      (UNLESS (AND (= childs-p-width (contact-width child))
				   (= childs-p-height (contact-height child))
				   (= childs-p-border-width (contact-border-width child)))
			(resize child childs-p-width childs-p-height childs-p-border-width))))
		  
		  ;;
		  ;;  Done with this child, move on to the next child and the next position in this
		  ;;  row...
		  ;;
		  (PUSH (make-org-entry :kid child
					:row  nrows-in-table
					:column columns-this-row
					:width childs-p-width
					:height childs-p-height
					:border-width childs-p-border-width) org-list)
		  (INCF next-x-pos (+ childs-total-width
				      (display-horizontal-space table)))
		  (SETF largest-height-this-row (MAX largest-height-this-row childs-total-height))
		  (INCF columns-this-row)))))

	  ;;
	  ;;  Set into the what-if structure the height, width, and organization just calculated...
	  ;;
	  (handle-the-end-of-a-row)
	  (SETF (what-if-nrows wis) nrows-in-table)
	  (SETF (what-if-ncolumns wis) ncolumns-in-table)
	  (SETF (what-if-preferred-height wis)
		(+ next-y-pos (- (display-vertical-space table))
		   (display-bottom-margin table)))
	  (SETF (what-if-preferred-width wis) preferred-width-of-table)
	  (SETF (what-if-organization wis) (NREVERSE org-list))
	  ;;
	  ;;  Set up a fake column-widths array for others...
	  ;;
	  (SETF (what-if-column-widths wis)
		(MAKE-ARRAY `(,ncolumns-in-table 2) :initial-element 0))
	  
	  (SETF (AREF (what-if-column-widths wis) 0 0) (what-if-preferred-width wis)))))))


(DEFUN put-kids-into-maximum-aligned-columns (table wis)
  ;; This is a guessing procedure that implements the XtNmaximumColumns policy for row and column
  ;; layout.  Keep an array of items (column-width max-width-of-columns-items).  Create and
  ;; initialize it from the 1st child: identical column widths = 1st child's preferred width,
  ;; max-width-of-columns-items = 0.  Set NROWS to 0.  Then start trying to place the children
  ;; into these columns.  The 1st child will fit for sure, updating the 1st column's max-width.
  ;; The 2nd-Nth children may or may not fit.  If it does, update max-width.  If not, see if
  ;; other columns' can be made narrower to allow this column to be made wide enough for him to
  ;; fit.  If so, do it.  If not, we must reduce the number of columns by one, assigning them
  ;; equal widths, then start the layout process from the top.  Each time we try to place a child
  ;; in the first column, increment NROWS.
  
  ;; Note that while this routine tends to give about the same amount of space to each column,
  ;; the slack space for the columns may differ considerably.  After we find a child the cannot
  ;; fit in a column and reduce the number of columns to get more space, we give each column the
  ;; same, new, enlarged space.  If one column is actually fairly narrow and doesn't need more
  ;; space it'll end up with extra slack space around it.  A slack-space-smoothing routine should
  ;; be written to improve this.
  
  
  (with-slots (children column-width) (THE table table)
    
      (LET ((nkids (LENGTH children))
	    (working-width (what-if-width wis))
	    (working-border-width (what-if-border-width wis)))
	
	(WHEN (<= working-width 0)
	  (SETF working-width (- (find-first-parents-width table)
				 working-border-width working-border-width)))

	;;
	;;  Start by sorting the list of children by their row/column constraints.  Once this is
	;;  done we ignore the constraints from here on for :maximum layout policy...
	;;
	(SETF (what-if-nrows wis) nkids
	      (what-if-ncolumns wis) nkids)
	(assign-kids-to-rows-and-columns table wis)
	
	
	;;  Start with an upper bound on the number of columns...
	(LET* ((ncolumns (MIN nkids (get-maximum-possible-ncolumns table working-width)))
	       (column-widths (MAKE-ARRAY `(,ncolumns 2)))
	       (column-widths-vector (MAKE-ARRAY (* 2 ncolumns) :displaced-to column-widths)))
					    

	  ;;
	  ;;  Each execution of this outer loop represents an attempt at fitting the children
	  ;;  into a given number of columns.  The inner loop below does the actual laying out of
	  ;;  the children; if it succeeds, it sets FINISHED to T as it exits.  If it fails, it
	  ;;  decrements NCOLUMNS and leaves FINISHED NIL.
	  ;;
	  (DO* (finished
		(org-list (LIST nil))
		(org-tail org-list)
		next-row next-column)
	       (finished
		 ;;
		 ;;  Make each column's real width equal to the widest child we've placed in it,
		 ;;  adjust ncolumns by the number of unused columns...
		 ;;
		 (DOTIMES (column ncolumns)
		   (IF (ZEROP (AREF column-widths column 1))
		       (DECF ncolumns)
		       (SETF (AREF column-widths column 0) (AREF column-widths column 1))))
		 
		 (SETF (what-if-column-widths wis) column-widths)
		 (SETF (what-if-ncolumns wis) ncolumns)
		 (SETF (what-if-organization wis) org-list)
		 (SETF (what-if-nrows wis) (1+ next-row)))
	       
	    ;;  Initialize the first ncolumns elements of the column-widths array...
	    ;;  Total horizontal space available for the columns:
	    ;;  	width - right-margin - left-margin - (n - 1)*horizontal-space.
	    ;;  This total is divided into ncolumns equal chunks, with any extra white space
	    ;;  being given a pixel at a time to the left-most columns.
	    
	    ;;  But not quite.  We need to handle fixed-width columns specially.  At this point
	    ;;  we know how many columns we're (tentatively) giving the table, call it N.  We
	    ;;  need to see how much of our space is occupied by fixed-width columns in the
	    ;;  first N columns and how many there are, call it M.  The remaining N-M columns
	    ;;  each gets 1/(N-M) of the remaining space.  Be careful abaout N=M!  And each
	    ;;  fixed-width column gets *both* of its column-width entries initialized here to
	    ;;  its fixed width so it'll look like there's no slack in that column (which there
	    ;;  isn't).  Unlike a variable-width column, a fixed-width column never gets its
	    ;;  2nd column-widths entry changed as we place kids in it.

	    (LET ((total-fixed-width 0) (n-fixed-width-columns 0)
		  (fixed-column-widths (UNLESS (EQ column-width :maximum) column-width)))

	      ;;  Forget the column widths calculated last time through the loop...
	      (FILL (THE vector column-widths-vector) nil)
	      
	      ;;  Calculate how much of the total table width is allocated to fixed-width
	      ;;  columns...
	      (COND
		((NULL fixed-column-widths))
		((INTEGERP fixed-column-widths)
		 (SETF total-fixed-width (* ncolumns fixed-column-widths)
		       n-fixed-width-columns ncolumns)
		 (DOTIMES (column-number ncolumns)
		   (SETF (AREF column-widths column-number 0)
			 (SETF (AREF column-widths column-number 1) fixed-column-widths))))
		((CONSP fixed-column-widths)
		 (DO ((fixed-column-widths fixed-column-widths (REST fixed-column-widths))
		      (column-number 0 (1+ column-number))
		      fixed-width)
		     ((OR (= column-number ncolumns)
			  (ENDP fixed-column-widths)))
		   (SETF fixed-width (FIRST fixed-column-widths))
		   (WHEN fixed-width
		     (INCF n-fixed-width-columns)
		     (INCF total-fixed-width fixed-width)
		     (SETF (AREF column-widths column-number 0)
			   (SETF (AREF column-widths column-number 1) fixed-width)))))
		(t (ERROR "column-width is ~a." fixed-column-widths)))
	      
	      ;;  Now n-fixed-width-columns = # of fixed width columns in first ncolumns
	      ;;      total-fixed-width     = # of pixels occupied by those columns
	      ;;  and for each fixed-width column both column-widths entries = the fixed width.

	      ;;  Take the remaining space and give it to the non-fixed-width columns...	      
	      (UNLESS  (ZEROP (- ncolumns n-fixed-width-columns))
		(MULTIPLE-VALUE-BIND (horizontal-space-for-each-var-column extra-white-space)
		    (FLOOR (- working-width
			      (display-left-margin table)
			      (display-right-margin table)
			      (* (1- ncolumns) (display-horizontal-space table))
			      total-fixed-width)
			   (- ncolumns n-fixed-width-columns))
		  
		  ;;  Assign the non-fixed-width space to the non-fixed-width columns.  Because
		  ;;  we FILL column-widths with NIL each time through the main loop, only
		  ;;  fixed-width columns will have none-NIL values in them.  Give the extra
		  ;;  white-space to the left-most variable-width columns a pixel at a time.
		  (DOTIMES (i ncolumns)
		    (WHEN (NULL (AREF column-widths i 0))
		      (SETF (AREF column-widths i 0)
			    (+ horizontal-space-for-each-var-column
			       (IF (ZEROP extra-white-space)
				   0
				   (PROGN (DECF extra-white-space) 1))))
		      (SETF (AREF column-widths i 1) 0)))))

	       
	       (SETF org-list (LIST nil)
		     org-tail org-list
		     next-row -1
		     next-column (1- ncolumns))
	       
	       ;;
	       ;;  Try to lay the children into the columns sized as they are now...
	       ;;
	       (DOLIST (child children (SETF finished t))
		 
		 (UNLESS (EQ (contact-state child) :withdrawn)
		   ;;
		   ;;  If the column this child's to go in is beyond ncolumns, wrap to the first
		   ;;  column of the next row...
		   ;;
		   (INCF next-column)
		   (WHEN (= next-column ncolumns)
		     (SETF next-column 0)
		     (INCF next-row)
		     (SETF fixed-column-widths (UNLESS (EQ column-width :maximum) column-width)))
		   
		   (LET* ((columns-width-right-now (AREF column-widths next-column 0))
			  (fixed-width-for-this-column
			    (IF (LISTP fixed-column-widths)      ;; ERCM
				(FIRST fixed-column-widths)
				fixed-column-widths)))

		     (UNLESS fixed-width-for-this-column			
			;;  Find out what width the child thinks he should be...
			(MULTIPLE-VALUE-BIND (childs-width childs-height childs-border-width)
			    (preferred-size child :width columns-width-right-now)
			  (DECLARE (IGNORE childs-height))
			  
			  ;;  Calculate how much horizontal space this child needs...
			  (LET ((horizontal-space-for-this-child
				  (+ childs-width childs-border-width childs-border-width)))
			    
			    (COND
			      ((OR (<= horizontal-space-for-this-child columns-width-right-now)
				   (adjust-column-widths-so-child-fits
				     column-widths horizontal-space-for-this-child
				     next-column ncolumns))
			       (SETF (AREF column-widths next-column 1)
				     (MAX (AREF column-widths next-column 1)
					  horizontal-space-for-this-child)))
			      (t			   
			       ;; else child can't fit in this column.  Reduce the number of
			       ;; columns and try again.
			       (DECF ncolumns)
			       (RETURN nil)))))))

		   ;;  To get here we must have decided we can successfully place this kid at
		   ;;  this position, so add an entry for it onto the org-list...
		   (SETF (REST org-tail)
			 (LIST (establish-org-entry child next-row next-column)))
		   (SETF org-tail (REST org-tail))
		   
		   ;;  Advance to the next column's entry in the fixed-width list if there is
		   ;;  one...
		   (WHEN (CONSP fixed-column-widths)
		     (SETF fixed-column-widths (REST fixed-column-widths)))))))))))


(DEFUN adjust-column-widths-so-child-fits (column-widths childs-width next-column ncolumns)
  
  (DO ((npixels-needed (- childs-width (AREF column-widths next-column 0))))
      ((ZEROP npixels-needed)
       (SETF (AREF column-widths next-column 0) childs-width)
       t)
    
    ;; Find column with greatest slack, if any...
    (LET ((max-slack 0) (max-slack-col nil))
      (DOTIMES (col ncolumns)
	(UNLESS (= next-column col)		; Don't look at column child goes in
	  (LET ((slack (- (AREF column-widths col 0) (AREF column-widths col 1))))
	    (WHEN (> slack max-slack)
	      (SETF max-slack slack
		    max-slack-col col)))))
      
      ;;  If no column had any slack, return NIL...
      (UNLESS max-slack-col (RETURN nil))

      ;;  Otherwise take a pixel from the max-slack-col's width, reduce our goal by one, try
      ;;  again...
      (DECF (AREF column-widths max-slack-col 0))
      (DECF npixels-needed))))



(DEFUN get-maximum-possible-ncolumns (table width)
  "Returns the maximum number of columns possible given the specified constraints."
  (with-slots (children column-width) (THE table table)
    
    (LET* ((fixed-column-widths (UNLESS (EQ column-width :maximum) column-width))
	   (minimum-column-width
	     (- width (display-left-margin table) (display-right-margin table))))

      ;;
      ;;  If the caller specified a single fixed width for all columns, then that's it...
      ;;
      (IF (INTEGERP fixed-column-widths)
	  (SETF minimum-column-width (MIN minimum-column-width fixed-column-widths))

	;; else...
	(PROGN
	  ;;
	  ;;  If the caller specified a list of fixed widths (and nil's) for (some of) the
	  ;;  columns, first find the minimum of these fixed column widths...
	  ;;
	  (WHEN (CONSP fixed-column-widths)
	    (DOLIST (this-fixed-column-width fixed-column-widths)
	      (WHEN this-fixed-column-width
		(SETF minimum-column-width
		      (MIN minimum-column-width this-fixed-column-width)))))
	  
	  ;;
	  ;;  Then as a crude approximation, find the narrowest child, not knowing what column
	  ;;  the child will go in...
	  ;;
	  (DOLIST (kid children)
	    (UNLESS (EQ (contact-state kid) :withdrawn)
	      (MULTIPLE-VALUE-BIND (preferred-width preferred-height preferred-border-width)
		  (preferred-size kid)
		(DECLARE (IGNORE preferred-height))
		(SETF minimum-column-width
		      (MIN minimum-column-width
			   (+ preferred-width preferred-border-width preferred-border-width))))))))
      
      ;;  Now that we have the smallest column width we could ever get, calculate and return the
      ;;  maximum number of columns we could ever have...
      (MIN (LENGTH children)
	   (FLOOR (+ (- width
			(display-left-margin table)
			(display-right-margin table))
		     (display-horizontal-space table))
		  (+ minimum-column-width (display-horizontal-space table)))))))



;;;
;;;   These routines construct the ORGANIZATION list by placing each child at a specific
;;;   row/column position
;;;
;;;.  Lexical variables:
;;;	hole-pointer	where in the existing organization list to rplacd-in an entry for an
;;;			unconstrained child -- the current "hole".  All entries in the
;;;			organization list preceding this one are contiguous starting from row 0,
;;;			column 0, so all attempts at child placement, regardless of the
;;;			constraints, start from here.  Hole-row & hole-column are one row/col
;;;			position beyond the row/col of (FIRST hole-pointer), unless (first
;;;			hole-pointer) is NIL, in which case they are (0,0).
;;;	hole-row	the row-number of the current hole.
;;;	hole-column	the column-number of the current hole.
;;;	ncolumns	the number of columns in the table.  Fixed.
;;;	nrows		the number of rows in the table.  Can change if a child specifies a big
;;;			row-constraint.
;;;

(DEFUN assign-kids-to-rows-and-columns (table wis)
  (LET (hole-pointer hole-row hole-column ncolumns nrows)
    
    
    #-cmu ;; Python will make local function. Not sure inline works here.
    (DECLARE (inline insert-into-organization-list))
    (LABELS
      (
       ;;
       ;;   Makes sure the hole-pointer/row/column actually point at a hole.  If they currently
       ;;   point at an allocated table row/column, moves them over until they point at an
       ;;   unallocated one. 
       ;;
       (find-next-hole
	 ()
	 (DO* (org-entry org-row org-column
	       (org-list hole-pointer))
	      (nil)
	   ;;
	   ;;  Look at the next org-entry, the one just beyond the hole pointer.  The second -
	   ;;  Nth times through the loop this also advances the hole-pointer...
	   ;;
	   (SETF hole-pointer org-list
		 org-list (REST org-list))
	   (WHEN org-list
	     (SETF org-entry (FIRST org-list)
		   org-row (org-entry-row org-entry)
		   org-column (org-entry-column org-entry)))
	   (WHEN (OR (NULL org-list)		; Exhausted org-list.  Leave hole pointing at
						;    row/col one beyond the last org-entry.
		     (/= org-row hole-row)	; There's space between the previous org-entry
		     (/= org-column hole-column))	;    and this one.  Leave hole pointing
						;    at row/col one beyond the previous
						;    org-entry. 
	     (RETURN))
	   ;;
	   ;;   The row/column position of the hole is occupied.  Move the row/column of the hole
	   ;;   over one position, try again...
	   ;;
	   (WHEN (= (INCF hole-column) ncolumns)
	     (INCF hole-row)
	     (SETF hole-column 0))))

       ;;
       ;;   Insert KID into the organization list at INSERTION-POINT at ROW/COLUMN...
       ;;
       (insert-into-organization-list
	 (kid insertion-point row column)
	 (RPLACD insertion-point
		 (CONS (establish-org-entry kid row column)
		       (REST insertion-point)))
	 (find-next-hole)	 
	 (WHEN (>= row nrows)			; Update nrows if necessary.
	   (SETF nrows (1+ row))))		;   *
       
       ;;
       ;;   Inserts a kid with no constraints in the next hole, moves the hole pointers.  Always
       ;;   successful, so always returns T.
       ;;
       (place-a-kid-at-any-row-and-column
	 (kid)
	 (insert-into-organization-list kid hole-pointer hole-row hole-column)
	 t)
       
       ;;
       ;;   Tries to insert a kid into a specific row/column, returning T if successful, NIL if
       ;;   not.  Fails if that row/column is already occupied or specified column is outside
       ;;   ncolumns.
       ;;
       (place-a-kid-at-a-specific-row-and-column
	 (kid kid-row kid-column)
	 
	 (LET ((kid-position (+ (* ncolumns kid-row) kid-column))
	       (last-occupied-position
		 (IF (FIRST hole-pointer)
		     (+ (* ncolumns (org-entry-row (FIRST hole-pointer)))
			(org-entry-column (FIRST hole-pointer)))
		     -1)))
	   (WHEN (OR (>= kid-column ncolumns)
		     (>= last-occupied-position kid-position))
	     (RETURN-FROM place-a-kid-at-a-specific-row-and-column nil))
	   
	   
	   (DO ((org-list hole-pointer) insertion-point org-position)
	       (nil)
	     
	     (SETF insertion-point org-list
		   org-list (REST org-list))
	     
	     (SETF org-position
		   (IF org-list
		       (+ (* ncolumns (org-entry-row (FIRST org-list)))
			  (org-entry-column (FIRST org-list)))
		       (1+ kid-position)))
	     
	     (COND
	       ((= org-position kid-position)	; Kid's row/column occupied: failure.
		(RETURN-FROM place-a-kid-at-a-specific-row-and-column nil))
	       ((> org-position kid-position)	; Kid's row/column free: success.
		(insert-into-organization-list kid insertion-point kid-row kid-column)
		(RETURN-FROM place-a-kid-at-a-specific-row-and-column t))
	       (t nil)))))
       
       ;;
       ;;   Tries to insert a kid into a specific row.
       ;;   Fails if row is full, returns NIL, otherwise is successful, returns T.
       ;;
       (place-a-kid-in-a-specific-row
	 (kid kid-row)

	 (WHEN (< kid-row hole-row)
	   (RETURN-FROM place-a-kid-in-a-specific-row nil))
	 
	 (DO ((org-list hole-pointer) insertion-point
	      (last-occupied-column
		(IF (FIRST hole-pointer) (org-entry-column (FIRST hole-pointer)) -1) org-column)
	      org-entry (org-row kid-row) org-column)
	     ((OR (NULL org-list)
		  (> org-row kid-row))
	      ;; Failure -- exit here iff couldn't insert child
	      nil)
	   (SETF insertion-point org-list
		 org-list (REST org-list))
	   (IF org-list
	       (SETF org-entry (FIRST org-list)
		     org-row (org-entry-row org-entry)
		     org-column (org-entry-column org-entry))
	     ;; else no more org-entries so fake one way out there...
	     (SETF org-row (1+ kid-row)))
	   
	   (WHEN (OR (AND (= org-row kid-row)	        ; In kid's row and there's a hole.
			  (< (1+ last-occupied-column)	;   *
			     org-column))		;   *
		     (AND (> org-row kid-row)		; First org-entry beyond kid's row
			  (< last-occupied-column	;   and there's a hole at the end
			     (1- ncolumns))))		;   of the kid's row.
	     (insert-into-organization-list
	       kid insertion-point kid-row (1+ last-occupied-column))
	     (RETURN-FROM place-a-kid-in-a-specific-row t))))
       
       ;;
       ;;   Inserts a kid into a specific column.
       ;;   Fails if column is not within ncolumns, returns NIL, otherwise always successful,
       ;;   returns T.
       ;;
       (place-a-kid-in-a-specific-column
	 (kid kids-column)
	 	   
	 (WHEN (>= kids-column ncolumns)
	   (RETURN-FROM place-a-kid-in-a-specific-column nil))
	 
	 (DO* ((org-list hole-pointer) insertion-point
	       (last-org-position -1 org-position) org-position
	       (insertion-row (IF (< kids-column hole-column) (1+ hole-row) hole-row))
	       (position-of-next-occurrence-of-kids-column
		 (+ (* ncolumns insertion-row) kids-column)))
	      (nil)
	   
	   (SETF insertion-point org-list
		 org-list (REST org-list))
	   
	   (SETF org-position
		 (IF org-list
		     (+ (* ncolumns (org-entry-row (FIRST org-list)))
			(org-entry-column (FIRST org-list)))
		     (1+ position-of-next-occurrence-of-kids-column)))
	   
	   (WHEN (< last-org-position
		    position-of-next-occurrence-of-kids-column
		    org-position)
	     (insert-into-organization-list kid insertion-point insertion-row kids-column)
	     (RETURN-FROM place-a-kid-in-a-specific-column t))
	   ;; Calculate a new position-of-next-occurrence-of-kids-column if this org-entry is at
	   ;; or beyond the current value...
	   (WHEN (>= org-position position-of-next-occurrence-of-kids-column)
	     (INCF position-of-next-occurrence-of-kids-column ncolumns)
	     (INCF insertion-row))))
       
       ;;
       ;;  This is called by assign-kids-to-rows-and-columns when it realizes it is dealing with
       ;;  a :maximum or :none table.  The Table's children list is rebuilt to be
       ;;  the (already sorted) kids in the org-list followed by the kids in the free-list.
       ;;  Where unconstrained kids would normally be used to fill in holes in a
       ;;  fixed-number-of-columns table, there really are no holes for a :maximum or
       ;;  :none table so such children are just placed at the end of the Table's
       ;;  children list.
       ;;
       (build-sorted-list-of-children
	 (table org-list free-list withdrawn-children)
	 (with-slots (children) (THE table table)
	   (LET* ((sorted-children-list (MAKE-LIST (LENGTH org-list)))	; includes leading NIL.
		  (next-sorted-children-list sorted-children-list)
		  (last-sorted-children-list sorted-children-list))
	     
	     (DOLIST (org-entry (REST org-list))
	       (SETF last-sorted-children-list next-sorted-children-list
		     next-sorted-children-list (REST next-sorted-children-list))
	       (RPLACA next-sorted-children-list (org-entry-kid org-entry)))
	     
	     (WHEN free-list
	       (RPLACD last-sorted-children-list (NCONC free-list withdrawn-children)))
	     (SETF children (REST sorted-children-list)))))

       )					; ...end of labels...
      
      ;; ====================================================================================
      ;;   The code for assign-kids-to-rows-and-columns (table wis):
      ;;   Constructs the what-if-organization list by assigning each kid to a specific
      ;;   row/column position in the table.
      ;;
      (with-slots (children) (THE table table)
	(LET (free-row free-col free (old-org-list (REST (what-if-organization wis)))
	      withdrawn-children)
	  (SETF (what-if-organization wis) (LIST nil)
		hole-pointer (what-if-organization wis)
		hole-row 0
		hole-column 0
		ncolumns (what-if-ncolumns wis)
		nrows (what-if-nrows wis))
	  ;;  First try to place all the kids with definite row/column constraints.
	  ;;  Any child specifying only a row goes on the free-col list.
	  ;;  Any child specifying only a column goes on the free-row list.
	  ;;  Any child specifying neither row nor column, or any child unable to be placed where
	  ;;      its definite row/column constraints placed it, goes on the free list.
	  (DOLIST (kid children)
	    (COND
	      ((NOT (EQ (contact-state kid) :withdrawn))
	       (UNLESS (OR (NULL old-org-list)
			   (EQ kid (org-entry-kid (FIRST old-org-list))))
		 (CERROR "continue" "children and org-list don't match"))
	       (LET ((row (OR (table-row kid)
			      (AND old-org-list (org-entry-row (FIRST old-org-list)))))
		     (column (OR (table-column kid)
				 (AND old-org-list (org-entry-column (FIRST old-org-list))))))
		 (SETF old-org-list (REST old-org-list))
		 (COND
		   ((AND row column)
		    (UNLESS (place-a-kid-at-a-specific-row-and-column kid row column)
		      (PUSH kid free)))
		   (row
		    (PUSH `(,kid ,row) free-col))
		   (column
		    (PUSH `(,kid ,column) free-row))
		   (t
		    (PUSH kid free)))))
	      (t
	       (PUSH kid withdrawn-children))))
	  
	  ;;  Now try to place all the kids specifying only a column.  Since it is always OK to
	  ;;  create a new row, such kids can always be placed...
	  (DOLIST (kid-and-column (NREVERSE free-row))
	    (place-a-kid-in-a-specific-column (FIRST kid-and-column) (SECOND kid-and-column)))
	  
	  ;;  Now try to place all the kids specifying only a row.  If that row is full, place
	  ;;  the child on the free list...
	  (DOLIST (kid-and-row (NREVERSE free-col))
	    (UNLESS (place-a-kid-in-a-specific-row (FIRST kid-and-row) (SECOND kid-and-row))
	      (PUSH (FIRST kid-and-row) free)))
	  
	  ;;  Finally, place the kids that are on the free list.  These kids have no constraints,
	  ;;  so they'll all be placed in holes scanning from top-left to bottom-right or new
	  ;;  rows will be created to hold them...
	  (IF (SYMBOLP (table-columns table))
	      (build-sorted-list-of-children
		table (what-if-organization wis) (NREVERSE free) withdrawn-children)
	      
	      ;; else...
	      (PROGN 
		(DOLIST (kid (NREVERSE free))
		  (place-a-kid-at-any-row-and-column kid))
		;;
		;;  Rebuild the children list in the order of the what-if-organization
		;;  followed by any :withdrawn children not on the what-if-organization list.
		;;
		(DO ((children children (REST children))
		     (organization (REST (what-if-organization wis)) (REST organization)))
		    ((NULL organization)
		     (DOLIST (withdrawn-child withdrawn-children)
		       (RPLACA children withdrawn-child)
		       (SETF children (REST children))))
		  (RPLACA children (org-entry-kid (FIRST organization))))))
	  
	  (SETF (what-if-nrows wis) nrows))))))

;;  This is called by assign-kids-to-rows-and-columns when it realizes it is dealing with a
;;  :maximum or :none table.  The Table's children list is rebuilt to be the
;;  (already sorted) kids in the org-list followed by the kids in the free-list.  Where
;;  unconstrained kids would normally be used to fill in holes in a fixed-number-of-columns
;;  table, there really are no holes for a :maximum or :none table so such children
;;  are just placed at the end of the Table's children list.

(DEFUN build-sorted-list-of-children (table org-list free-list withdrawn-children)
  (with-slots (children) (THE table table)
    (LET* ((sorted-children-list (MAKE-LIST (LENGTH org-list)))	; includes leading NIL.
	   (next-sorted-children-list sorted-children-list)
	   (last-sorted-children-list sorted-children-list))
      
      (DOLIST (org-entry (REST org-list))
	(SETF last-sorted-children-list next-sorted-children-list
	      next-sorted-children-list (REST next-sorted-children-list))
	(RPLACA next-sorted-children-list (org-entry-kid org-entry)))
      
      (WHEN free-list
	(RPLACD last-sorted-children-list (NCONC free-list withdrawn-children)))
      (SETF children (REST sorted-children-list)))))