source: trunk/third/wcl/Xmp/Table.c @ 14562

Revision 14562, 72.6 KB checked in by ghudson, 25 years ago (diff)
Make this compile with Motif 2 support turned on.
Line 
1/*LINTLIBRARY*/
2/*
3 * SCCS_data:    %Z% %M%        %I% %E% %U%
4 *
5 * XmpTable -   Forms-based composite widget/geometry manager derived from
6 *              Motif manager widgets.  Class heirarchy:
7 *                      Core
8 *                      Composite
9 *                      Constraint
10 *                      XmManager
11 *                      XmBulletinBoard
12 *                      XmpTable
13 *
14 * Originally implemented by:
15 *      David Harrison
16 *      University of California, Berkeley
17 *      1989
18 *
19 * Many bug fixes and enhancements provided by
20 *      marbru@auto-trol.com    Martin Brunecky
21 *      nazgul@alphalpha.com    Kee Hinckley
22 *      pastor@PRC.Unisys.COM   Jon A. Pastor
23 *
24 * Completely re-implemented by:
25 *      David.Smyth@SniAp.MchP.SNI.De
26 */
27
28/*
29Edit History
30
3125Oct92         david   Geometry management re-work
3201Feb92         david   Re-Implementation
33
34*/
35
36#include <X11/Xmp/COPY.h>
37#include <X11/IntrinsicP.h>
38
39#ifdef sun                      /* really `if OpenWindows'               */
40#include <X11/ObjectP.h>        /* why don't they just use X from mit!?! */
41#include <X11/RectObjP.h>
42#endif
43
44#include <X11/StringDefs.h>
45#include <Xm/DialogS.h>
46
47#include <X11/Xmp/TableP.h>
48
49#include <ctype.h>              /* isalnum() macro */
50
51/* For HP platforms
52**==================**
53*/
54#ifndef XtInheritFocusMovedProc
55#define XtInheritFocusMovedProc XmInheritFocusMovedProc
56#endif  /* !XtInheritFocusMovedProc */
57
58/* For R4 and DECwindows
59**=======================**
60*/
61#ifdef USE_XtResizeWidget
62#define _XmResizeObject(c,w,h,b) XtResizeWidget(c,w,h,b)
63#endif
64#ifdef USE_XtMoveWidget
65#define _XmMoveObject(w,x,y) XtMoveWidget(w,x,y)
66#endif
67
68/* For backward compatibility with old Xt releases
69**=================================================**
70*/
71#ifndef XtIsWidget
72#ifdef XtSpecificationRelease
73#define XtIsWidget(obj) XtIsSubclass(obj,(WidgetClass)coreWidgetClass)
74#else
75#define XtIsWidget(obj) XtIsSubclass(obj,(WidgetClass)widgetClass)
76#endif
77#endif
78
79#ifndef XtSpecificationRelease
80#if NeedFunctionPrototypes
81typedef void*   XtPointer;
82#else
83typedef char*   XtPointer;
84#endif
85#endif
86
87
88/* Resources
89**===========**
90*/
91#ifdef XtOffsetOf
92#define OFFSET(field) XtOffsetOf(XmpTableRec,table.field)
93#else
94#define OFFSET(field) XtOffset(XmpTableWidget,table.field)
95#endif
96
97static XtResource resources[] = {
98 { XtNdefaultOptions, XtCDefaultOptions, XtRXmpTableOpts, sizeof(XmpTableOpts),
99   OFFSET(default_options), XtRImmediate, (XtPointer)0 },
100 { XtNlayout, XtCLayout, XtRXmpTableLoc, sizeof(XmpTableLoc),
101   OFFSET(default_layout), XtRXmpTableLoc, (XtPointer)0 },
102 { XtNsameWidth, XtCSameSize, XtRXrmNameListLists, sizeof(XrmNameListLists),
103   OFFSET(same_width), XtRXrmNameListLists, (XtPointer)0 },
104 { XtNsameHeight, XtCSameSize, XtRXrmNameListLists, sizeof(XrmNameListLists),
105   OFFSET(same_height), XtRXrmNameListLists, (XtPointer)0 },
106 { XtNsameBorder, XtCSameSize, XtRXrmNameListLists, sizeof(XrmNameListLists),
107   OFFSET(same_border), XtRXrmNameListLists, (XtPointer)0 },
108 { XtNforceShrink, XtCForceShrink, XtRBoolean, sizeof(Boolean),
109   OFFSET(force_shrink), XtRImmediate, (XtPointer)True },
110 { XtNshrinkSimple, XtCShrinkSimple, XtRBoolean, sizeof(Boolean),       /*OBS*/
111   OFFSET(shrink_simple), XtRImmediate, (XtPointer)True },              /*OBS*/
112 { XtNcolumnSpacing, XtCSpacing, XtRInt, sizeof(int),
113   OFFSET(col_spacing), XtRImmediate, (XtPointer)0 },
114 { XtNrowSpacing, XtCSpacing, XtRInt, sizeof(int),
115   OFFSET(row_spacing), XtRImmediate, (XtPointer)0 },
116};
117
118#undef OFFSET
119
120/* Core Class Methods
121**====================**
122*/
123static void             XmpTableClassInitialize ();
124static void             XmpTableInitialize _((  Widget, Widget,
125                                                ArgList, Cardinal*      ));
126static void             XmpTableDestroy _((     Widget                  ));
127static void             XmpTableResize _((      Widget                  ));
128#ifdef XtSpecificationRelease
129static Boolean          XmpTableSetValues _((   Widget, Widget, Widget,
130                                                ArgList, Cardinal*      ));
131#else
132static Boolean          XmpTableSetValues _((   Widget, Widget, Widget  ));
133#endif
134static XtGeometryResult XmpTableQueryGeometry _(( Widget,
135                                                XtWidgetGeometry*,
136                                                XtWidgetGeometry*       ));
137
138/* Composite class methods
139**=========================**
140*/
141static XtGeometryResult XmpTableGeometryManager _(( Widget /*child*/,
142                                                XtWidgetGeometry*,
143                                                XtWidgetGeometry*       ));
144static void             XmpTableChangeManaged _(( Widget                ));
145
146
147
148
149XmpTableClassRec xmpTableClassRec = {
150  { /* core_class fields                */
151    /* superclass               */      (WidgetClass)&xmBulletinBoardClassRec,
152    /* class_name                       */      "XmpTable",
153    /* widget_size                      */      sizeof(XmpTableRec),
154    /* class_initialize                 */      XmpTableClassInitialize,
155    /* class_part_initialize            */      NULL,
156    /* class_inited                     */      FALSE,
157    /* initialize                       */      XmpTableInitialize,
158    /* initialize_hook                  */      NULL,
159    /* realize                          */      XtInheritRealize,
160    /* actions                          */      NULL,
161    /* num_actions                      */      0,
162    /* resources                        */      resources,
163    /* num_resources                    */      XtNumber(resources),
164    /* xrm_class                        */      NULLQUARK,
165    /* compress_motion                  */      False,
166    /* compress_exposure                */      TRUE,
167    /* compress_enterleave              */      False,
168    /* visible_interest                 */      FALSE,
169    /* destroy                          */      XmpTableDestroy,
170    /* resize                           */      XmpTableResize,
171    /* expose                           */      XtInheritExpose,
172    /* set_values                       */      XmpTableSetValues,
173    /* set_values_hook                  */      NULL,
174    /* set_values_almost                */      XtInheritSetValuesAlmost,
175    /* get_values_hook                  */      NULL,
176    /* accept_focus                     */      NULL,
177    /* version                          */      XtVersion,
178    /* callback_private                 */      NULL,
179    /* tm_table                         */      XtInheritTranslations,
180    /* query_geometry                   */      XmpTableQueryGeometry,
181    /* display_accelerator              */      NULL,
182    /* extension                        */      NULL
183  },
184  { /* composite_class fields           */
185    /* geometry_manager                 */      XmpTableGeometryManager,
186    /* change_managed                   */      XmpTableChangeManaged,
187    /* insert_child                     */      XtInheritInsertChild,
188    /* delete_child                     */      XtInheritDeleteChild,
189    /* extension                        */      NULL
190  },
191  { /* constraint_class fields          */
192    /* resources                        */      NULL,
193    /* num_resources                    */      0,
194    /* constraint_size                  */      0,
195    /* initialize                       */      NULL,
196    /* destroy                          */      NULL,
197    /* set_values                       */      NULL,
198    /* extension                        */      NULL
199  },
200  { /* manager_class fields             */
201#if XmVersion > 1000
202    /* translations                     */      XtInheritTranslations,
203    /* syn_resources                    */      NULL,
204    /* num_syn_resources                */      0,
205    /* syn_cont_resources               */      NULL,
206    /* num_syn_cont_resources           */      0,
207    /* parent_process                   */      XmInheritParentProcess,
208#else
209    /* translations                     */      (XtTranslations) _XtInherit,
210    /* get_resources                    */      NULL,
211    /* num_get_resources                */      0,
212    /* get_constraint_resources         */      NULL,
213    /* num_get_constraint_resources     */      0,
214#endif
215    /* extension                        */      NULL
216  },
217  { /* bulletin_board_class fields      */
218    /* always_install_accelerators      */      False,
219#if XmVersion > 1000
220    /* geo_matrix_create                */      NULL,
221    /* focus_moved_proc                 */      XtInheritFocusMovedProc,
222#endif
223    /* extension                        */      NULL
224  },
225  { /* table_class fields               */
226    /* extension                        */      NULL
227  }
228};
229
230WidgetClass xmpTableWidgetClass = (WidgetClass) &xmpTableClassRec;
231
232
233/* Converters
234**============**
235*/
236
237/* Convert String to TableOpts
238**=============================**
239   Converts a string representation into a TableOpts, which is small enough
240   to fit entirely into the to->addr.
241*/
242/*ARGSUSED*/
243void XmpCvtStrToXmpTableOpts( args, num_args, from, to )
244    XrmValue* args;             /* Arguments to converter */
245    Cardinal* num_args;         /* Number of arguments    */
246    XrmValue* from;             /* From type              */
247    XrmValue* to;               /* To type                */
248{
249    static XmpTableOpts opts;
250
251    if (*num_args != 0) {
252        XtErrorMsg("XmpCvtStrToXmpTableOpts", "wrongParameters",
253                   "XtToolkitError",
254                   "String to options takes no additional arguments",
255                   (String*)0, (Cardinal*)0);
256    }
257
258    opts = XmpTableOptsParse( (String) from->addr );
259
260    if (opts == (XmpTableOpts)0)
261        XtStringConversionWarning( (String) from->addr, XtRXmpTableOpts );
262
263    to->addr = (caddr_t) &opts;
264    to->size = sizeof( XmpTableOpts );
265}
266
267/* Convert String to TableLocRec Array
268**=====================================**
269   Converts a string representation into an array of TableLocRec structures
270   (ie., TableLocRec*).  This XtCalloc'd array is kept by the resource
271   database: a COPY must be made by the widget.
272*/
273/*ARGSUSED*/
274void XmpCvtStrToXmpTableLoc( args, num_args, from, to )
275    XrmValue* args;             /* Arguments to converter */
276    Cardinal* num_args;         /* Number of arguments    */
277    XrmValue* from;             /* From type              */
278    XrmValue* to;               /* To type                */
279{
280    static XmpTableLoc defLocs;
281
282    if (*num_args != 0) {
283        XtErrorMsg("XmpCvtStrToXmpTableLoc", "wrongParameters",
284                   "XtToolkitError",
285                   "String to layout takes no additional arguments",
286                   (String*)0, (Cardinal*)0);
287    }
288
289    defLocs = XmpTableLocParse( (String) from->addr );
290
291    if (defLocs == (XmpTableLoc)0)
292        XtStringConversionWarning( (String) from->addr, XtRXmpTableLoc );
293
294    to->addr = (caddr_t) &defLocs;
295    to->size = sizeof(caddr_t);
296}
297
298/* Convert String to XrmNameList
299**===============================**
300   Converts a string representation into a null-terminated array of XrmQuarks
301   which will presumably name children widgets.  This XtCalloc'd array is
302   kept by the resource database: a COPY must be made by the widget.
303*/
304static XrmNameList      XrmNameListParse     _(( char*, char* ));
305static XrmNameListLists XrmNameListListsCopy _((XrmNameListLists));
306
307/*ARGSUSED*/
308void XmpCvtStrToXrmNameListLists( args, num_args, from, to )
309    XrmValue* args;             /* Arguments to converter */
310    Cardinal* num_args;         /* Number of arguments    */
311    XrmValue* from;             /* From type              */
312    XrmValue* to;               /* To type                */
313{
314    static XrmNameListLists listOfNameLists;
315
316    if (*num_args != 0) {
317        XtErrorMsg("XmpCvtStrToXrmNameListLists", "wrongParameters",
318                   "XtToolkitError",
319                   "String to XrmNameListLists takes no additional arguments",
320                   (String*)0, (Cardinal*)0);
321    }
322
323    listOfNameLists = XrmNameListListsParse( (String) from->addr );
324
325    if (listOfNameLists == (XrmNameListLists)0)
326        XtStringConversionWarning( (String) from->addr, XtRXrmNameListLists );
327
328    to->addr = (caddr_t) &listOfNameLists;
329    to->size = sizeof(caddr_t);
330}
331
332#define LT_PAREN '('
333#define RT_PAREN ')'
334
335XrmNameListLists XrmNameListListsParse( cp )
336    char*       cp;
337{
338    XrmNameList tempLists[1024];        /* lotsa possible lists... */
339    int         numLists = 0;
340    char*       start;
341    char*       end;
342
343    while ( *cp && isspace(*cp) )
344        ++cp;
345
346    if ( *cp == LT_PAREN )
347    {
348        /* More than one list
349        */
350        while ( *cp == LT_PAREN )
351        {
352            start = cp;         /* start is LT_PAREN */
353
354            while ( *cp && *cp != RT_PAREN )
355                ++cp;
356
357            end = cp;           /* end is RT_PAREN or NUL */
358
359            tempLists[numLists++] = XrmNameListParse( start, end );
360
361            while ( *cp && *cp != LT_PAREN )
362                ++cp;
363        }
364    }
365    else
366    {
367        /* exactly one list
368        */
369        start = cp;             /* start is not space, not LT_PAREN */
370
371        while (*cp)
372            ++cp;
373
374        end = cp;               /* end is NUL */
375
376        tempLists[numLists++] = XrmNameListParse( start, end );
377    }
378
379    if ( numLists > 0 )
380    {
381        int              i;
382        XrmNameListLists retVal;
383
384        retVal = (XrmNameListLists)XtCalloc( numLists+1, sizeof(XrmNameList) );
385
386        for ( i = 0 ; i < numLists ; ++i )
387            retVal[i] = tempLists[i];
388
389        return retVal;
390    }
391    return (XrmNameListLists)0;
392}
393#undef LT_PAREN
394#undef RT_PAREN
395
396/* Parse into an XtCalloc'd array of XrmNames
397************************************************************************
398   start points to a LT_PAREN or to the 1st char of a name.
399   end points to a RT_PAREN of a NUL.
400*/
401#define isname(c) (isalnum(c) || c == '_')
402
403static XrmNameList XrmNameListParse( start, end )
404    char* start;
405    char* end;
406{
407    XrmName tempList[1024];     /* may be lotsa kids */
408    int     numNames = 0;
409    char*   cp = start;
410
411    /* Get to first character of first name
412    */
413    while ( cp < end && !isname(*cp) )
414        ++cp;
415
416    while ( cp < end )
417    {
418        char  nameBuf[1024];
419        char* np = nameBuf;
420
421        /* Get to first character of name
422        */
423        while ( cp < end && !isname(*cp) )
424            ++cp;
425
426        /* Collect name into buffer
427        */
428        while ( cp < end && isname(*cp)  )
429            *np++ = *cp++;
430        *np = '\0';
431
432        tempList[numNames++] = XrmStringToQuark( nameBuf );
433    }
434
435    if ( numNames > 0 )
436    {
437        int         i;
438        XrmNameList retVal;
439
440        retVal = (XrmNameList)XtCalloc( numNames+1, sizeof(XrmName) );
441
442        for ( i = 0 ; i < numNames ; ++i )
443            retVal[i] = tempList[i];
444
445        return retVal;
446    }
447    return (XrmNameList)0;
448}
449#undef isname
450
451static XrmNameListLists XrmNameListListsCopy( orig )
452    XrmNameListLists orig;
453{
454    int              list, name;
455    XrmNameListLists copy;
456
457    if ( orig == (XrmNameListLists)0 )
458        return (XrmNameListLists)0;
459
460    for ( list = 0  ;  orig[list] != (XrmNameList)0  ;  ++list )
461        /*EMPTY*/;
462
463    copy = (XrmNameListLists)XtCalloc( list+1, sizeof(XrmNameList) );
464
465    for ( list = 0  ;  orig[list] != (XrmNameList)0  ;  ++list )
466    {
467        for ( name = 0  ;  orig[list][name] != (XrmName)0  ;  ++name )
468            /*EMPTY*/;
469
470        copy[list] = (XrmNameList)XtCalloc( name+1, sizeof(XrmName) );
471
472        for ( name = 0  ;  orig[list][name] != (XrmName)0  ;  ++name )
473            copy[list][name] = orig[list][name];
474    }
475
476    return copy;
477}
478
479void XrmNameListListsFree( old )
480    XrmNameListLists old;
481{
482    int list;
483
484    if ( old == (XrmNameListLists)0 )
485        return;
486
487    for ( list = 0  ;  old[list] != (XrmNameList)0  ;  ++list )
488        XtFree( (char*)old[list] );
489
490    XtFree( (char*)old );
491}
492
493
494/* Initialization Methods
495**========================**
496   Note that no class part initialization is needed, as there are
497   no inherited methods (yet?).
498*/
499
500static void XmpTableClassInitialize()
501{
502    XtAddConverter(     XtRString, XtRXmpTableOpts,
503                        XmpCvtStrToXmpTableOpts, (XtConvertArgList)0, 0 );
504    XtAddConverter(     XtRString, XtRXmpTableLoc,
505                        XmpCvtStrToXmpTableLoc, (XtConvertArgList)0, 0 );
506    XtAddConverter(     XtRString, XtRXrmNameListLists,
507                        XmpCvtStrToXrmNameListLists, (XtConvertArgList)0, 0 );
508}
509
510/*ARGSUSED*/
511static void XmpTableInitialize( requestWidget, newWidget, args, num_args )
512    Widget      requestWidget;          /* as already set by Xt         */
513    Widget      newWidget;              /* set up by this method        */
514    ArgList     args;
515    Cardinal*   num_args;
516{
517    XmpTableWidget tw = (XmpTableWidget) newWidget;
518
519    /* Copy resource values specified by pointer
520    */
521    tw->table.default_layout = XmpTableLocCopy(tw->table.default_layout);
522    tw->table.same_width     = XrmNameListListsCopy(tw->table.same_width);
523    tw->table.same_height    = XrmNameListListsCopy(tw->table.same_height);
524    tw->table.same_border    = XrmNameListListsCopy(tw->table.same_border);
525
526    /* Initialize internally computed members
527    */
528    tw->table.real_layout = (XmpTableLoc)0;
529    tw->table.num_cols    = 0;
530    tw->table.cols        = (XmpTableVector)0;
531    tw->table.num_rows    = 0;
532    tw->table.rows        = (XmpTableVector)0;
533
534    tw->table.resize_status      = RSinit;
535
536    tw->table.requesting_resize = False;
537    tw->table.requesting_width  = (Dimension)0;
538    tw->table.requesting_height = (Dimension)0;
539
540    tw->table.resize_child        = (Widget)0;
541    tw->table.resize_mode         = (XtGeometryMask)0;
542    tw->table.resize_width        = (Dimension)0;
543    tw->table.resize_height       = (Dimension)0;
544    tw->table.resize_border_width = (Dimension)0;
545
546    tw->table.approved_child        = (Widget)0;
547    tw->table.approved_mode         = (XtGeometryMask)0;
548    tw->table.approved_width        = (Dimension)0;
549    tw->table.approved_height       = (Dimension)0;
550    tw->table.approved_border_width = (Dimension)0;
551    tw->table.approved_cols         = (XmpTableVector)0;
552    tw->table.approved_rows         = (XmpTableVector)0;
553
554    tw->table.current_cols          = (XmpTableVector)0;
555    tw->table.current_rows          = (XmpTableVector)0;
556
557    tw->table.query_mode            = (XtGeometryMask)0;
558    tw->table.query_width           = (Dimension)0;
559    tw->table.query_height          = (Dimension)0;
560
561    tw->table.resize_table_to_size_pre_approved_by_parent = False;
562}
563 
564
565
566/* Destroy Method
567**================**
568   Free any instance data allocated for the TablePart members. 
569*/
570static void XmpTableDestroy( w )
571    Widget w;                   /* Widget to destroy */
572{
573    XmpTableWidget tw = (XmpTableWidget) w;
574
575    XmpTableLocFree(      tw->table.default_layout );
576    XmpTableLocFree(      tw->table.real_layout );
577    XrmNameListListsFree( tw->table.same_width );
578    XrmNameListListsFree( tw->table.same_height );
579    XrmNameListListsFree( tw->table.same_border );
580    XmpTableVectorFree(   tw->table.cols );
581    XmpTableVectorFree(   tw->table.rows );
582    XmpTableVectorFree(   tw->table.approved_cols );
583    XmpTableVectorFree(   tw->table.approved_rows );
584    XmpTableVectorFree(   tw->table.current_cols );
585    XmpTableVectorFree(   tw->table.current_rows );
586}
587
588
589
590/* Geometry Management and Negotiation
591**=====================================**
592   The following methods are involved in geometry management and
593   negotiation:
594
595   TableResize() method:  The parent can issue a resize demand by
596   invoking TableResize() via XtResizeWidget().  This can be due to the
597   parent being resized from above (perhaps the user changed the shell
598   size via the window manager) or it may be due to the Table itself
599   asking to be made a different size for any of several reasons.
600
601   TableSetValues() method:  Geometry management gets involved when a
602   change occurs to any Table resource, or any of several superclass
603   resources (width, height, margin_width).
604
605   TableQueryGeometry() method:  A parent of a Table asks the table for
606   its preferred geometry using this method, invoked via XtQueryGeometry().
607   Usually generates a proposed layout which is valid only if the next
608   geometry management and negotiation method invoked is TableQueryGeometry()
609   for the same child asking for approved sizes.  All other geometry
610   management and negotiation methods must clear the proposed layout.
611
612   TableGeometryManager() method:  This method is invoked when a child
613   wishes to become a different size.
614
615   TableChangeManaged() method: this method is invoked when the set of
616   managed children changes.  This triggers the initial layout of the
617   Table widget, and causes the existing layout to be re-computed.
618*/
619
620/* Recompute Layout Due To Parent Demand
621**=======================================**
622   This gets called when a parent tells the table it must re-size.  We
623   certainly have real_layout, rows, cols, already done (or null if no
624   managed children).
625
626   If we are being resized due to the initial resize request from a child,
627   then we will have alot of the work done IFF the size the table is now
628   the size the parent *said* is would change the table to become.  We
629   cannot really trust this, because all this geometry negotiation stuff
630   is so goddamn complex that one is amazed when anything actually DOES work...
631
632   Therefore, if the table is being resized, and the size is as we expect,
633   then we just resize and the optimizations work as planned.  This means
634   we can use the proposed column and row vectors which have already been
635   adjusted to fit the pre-approved size, but we still size and position
636   all the children, and redisplay.
637
638   If we are being resized for any other reason, or if we are being resized
639   to a size different from that previously approved by this tables's parent,
640   then we need to flush the proposed layout stuff, and do a full resize:
641   adjust the cols and rows to fit the table, size and position all the
642   children, and redisplay.
643
644   Cause the background of the Table widget to be re-displayed.
645*/
646
647static void XmpTableResize( w )
648    Widget w;
649{
650    XmpTableWidget tw = (XmpTableWidget)w;
651
652    if ( tw->table.resize_table_to_size_pre_approved_by_parent
653      && tw->core.width  == tw->table.query_width
654      && tw->core.height == tw->table.query_height )
655    {
656        XmpTableResizeLayout( tw );
657    }
658    else
659    {
660        tw->table.resize_table_to_size_pre_approved_by_parent = False;
661        XmpTableForgetProposedLayout( tw );
662        XmpTableResizeLayout(         tw );
663    }
664}
665
666
667
668/* Set Values
669**============**
670   This method gets called via XtSetValues().  If any table members or
671   margin width have been changed, then geometry management must be done.
672
673   We do not have to worry about geometry changes (core.width etc) because
674   Xt takes care of invoking TableResize directly.
675*/
676
677#ifdef XtSpecificationRelease
678/*ARGSUSED*/
679static Boolean XmpTableSetValues(currentWidget, ignoreRequestWidget, newWidget,
680                                 ignored, notUsed )
681    Widget      currentWidget;          /* Before call to XtSetValues */
682    Widget      ignoreRequestWidget;    /* After call to XtSetValues  */
683    Widget      newWidget;              /* Final version of widget    */
684    ArgList     ignored;
685    Cardinal*   notUsed;
686#else
687static Boolean XmpTableSetValues(currentWidget, ignoreRequestWidget, newWidget)
688    Widget      currentWidget;          /* Before call to XtSetValues */
689    Widget      ignoreRequestWidget;    /* After call to XtSetValues  */
690    Widget      newWidget;              /* Final version of widget    */
691#endif
692{
693    XmpTableWidget current = (XmpTableWidget) currentWidget;
694    XmpTableWidget new     = (XmpTableWidget) newWidget;
695
696    if ( current->table.force_shrink    != new->table.force_shrink
697      || current->table.col_spacing     != new->table.col_spacing
698      || current->table.row_spacing     != new->table.row_spacing
699      || current->table.default_options != new->table.default_options
700      || current->table.default_layout  != new->table.default_layout
701      || current->table.same_width      != new->table.same_width
702      || current->table.same_height     != new->table.same_height
703      || current->table.same_border     != new->table.same_border
704      || current->bulletin_board.margin_width
705                                        != new->bulletin_board.margin_width
706      || current->bulletin_board.margin_height
707                                        != new->bulletin_board.margin_height)
708    {
709        /* We need to do some re-layout
710        */
711        XmpTableForgetProposedLayout( new );
712
713        /* values set by pointers require special handling:
714         * free old value, copy and alloc new value.
715         */
716        if ( current->table.same_width != new->table.same_width )
717        {
718            XrmNameListListsFree( current->table.same_width );
719            new->table.same_width = XrmNameListListsCopy(
720                                                new->table.same_width );
721        }
722
723        if ( current->table.same_height != new->table.same_height )
724        {
725            XrmNameListListsFree( current->table.same_height );
726            new->table.same_height = XrmNameListListsCopy(
727                                                new->table.same_height );
728        }
729
730        if ( current->table.same_border != new->table.same_border )
731        {
732            XrmNameListListsFree( current->table.same_border );
733            new->table.same_border = XrmNameListListsCopy(
734                                                new->table.same_border );
735        }
736
737        if ( current->table.default_layout != new->table.default_layout )
738        {
739            XmpTableLocFree( current->table.default_layout );
740            new->table.default_layout = XmpTableLocCopy(
741                                                new->table.default_layout );
742            /* We need to do a complete recomputation and placement
743            */
744            XmpTableNewLayout( new );
745        }
746        else if ( new->table.real_layout == (XmpTableLoc)0 )
747        {
748            /* SetValues during creation, or before any layout has been
749             * done.  We need to do a complete recomputation and placement
750             */
751            XmpTableNewLayout( new );
752        }
753        else
754        {
755            /* we only need to change some things in the existing real_layout
756            */
757            XmpTableRecomputeLayout( new );
758        }
759        /* Let Xt know exposure is needed
760        */
761        return True;
762    }
763    /* No exposure needed due to Table resources
764    */
765    return False;
766}
767
768
769
770/* Provide Preferred Geometry To Parent
771**======================================**
772   If the parent asks if the table can grow, the answer is always yes.  If
773   the parent asks for the table to shrink to a size smaller than the
774   preferred width or height, then the answer is almost, with the preferred
775   width and height provided.
776*/
777static XtGeometryResult XmpTableQueryGeometry(w, request, geo_return)
778    Widget w;                           /* XmpTable widget      */
779    XtWidgetGeometry *request;          /* Parent intended size */
780    XtWidgetGeometry *geo_return;       /* preferred size       */
781{
782    XmpTableWidget tw = (XmpTableWidget) w;
783    int pref;
784
785    /* First check for queries which would not result in a resize.
786     * According to the spec, "No" means "Don't bother."
787     */
788    if (  request->request_mode & CWWidth
789     &&   request->width  == tw->core.width
790     &&   request->request_mode & CWHeight
791     &&   request->height == tw->core.height )
792        return XtGeometryNo;
793    if (  request->request_mode & CWWidth
794     &&   request->width == tw->core.width )
795        return XtGeometryNo;
796    if (  request->request_mode & CWHeight
797     &&   request->height == tw->core.height )
798        return XtGeometryNo;
799
800    /* Note that this table may be in the process of asking the parent for a
801     * resize, and the parent may (from within the parent's GeometryManager
802     * method) ask the table for its preferred size.  TablePreferredWidth()
803     * and TablePreferredHeight() can tell this is happening, because
804     * TableMakeResizeRequest() sets the flag tw->table.requesting_resize.
805     * In such a case, the preferred size returned is the size the table is
806     * asking to become.
807     */
808    if ( request->request_mode == (XtGeometryMask)0 )
809    {
810        /* Parent is asking for all preferred dimensions
811        */
812        geo_return->request_mode = CWWidth|CWHeight;
813        geo_return->width  = XmpTablePreferredWidth(  tw );
814        geo_return->height = XmpTablePreferredHeight( tw );
815    }
816    if ( request->request_mode & CWWidth )
817    {
818        pref = XmpTablePreferredWidth( tw );
819        if ( request->width < (Dimension)pref )
820        {
821            geo_return->width = (Dimension)pref;
822            geo_return->request_mode |= CWWidth;
823        }
824        else
825        {
826            geo_return->width = request->width;
827        }
828    }
829    if ( request->request_mode & CWHeight )
830    {
831        pref = XmpTablePreferredHeight( tw );
832        if ( request->height < (Dimension)pref )
833        {
834            geo_return->height = (Dimension)pref;
835            geo_return->request_mode |= CWHeight;
836        }
837        else
838        {
839            geo_return->height = request->height;
840        }
841    }
842
843    /* XtGeometryNo means already at preferred (minimum) size (dont't bother)
844    */
845    if ( geo_return->width  == tw->core.width
846     &&  geo_return->height == tw->core.height )
847        return XtGeometryNo;
848
849    /* XtGeometryAlmost means has a preferred size different from current size
850    */
851    if ( geo_return->request_mode & (CWWidth|CWHeight) )
852        return XtGeometryAlmost;
853
854    /* XtGeometryYes is only returned if request is to change geometry in ways
855     * which do not matter to the table layout: x&y position, border_width,
856     * sibling, and stack_mode.
857     */
858    return XtGeometryYes;
859}
860
861
862
863/* Handle Geometry Requests from Children
864**========================================**
865   Only called by Xt if the child is managed.  Therefore, we can trust that
866   the table already has valid row and column vectors.
867
868   Position changes are always rejected.
869
870   Size changes are processed as followed: We store the child and the child's
871   requested size in the Table.  We also save the current row and column
872   vectors.  Then we "pretend" that we are going to accept the requested
873   child size: create new row and column vectors, make them fit the Table's
874   width and height.  Then, instead of changing the geometries of all the
875   children, we see what the size of the requesting child would be.  If (by
876   some miracle) the resulatant size is the requested size, then we really
877   do the layout.  In reality, the size will almost ALWAYS be different from
878   the requested, so we return XtGeometryAlmost, save the newly computed
879   row and column vectors, and remember the approved size of the child.
880
881   In most cases, the child will simply immediately re-invoke this method,
882   passing the approved sizes.  We can then use the pre-computed column and
883   row vectors, and set the geometries of all the children (including the
884   requesting child) and return XtGeometryDone (XtGeometryYes).
885
886   If, however, ANY other geometry method is invoked (due to set values,
887   change managed, or resize command from parent) then the pre-computed
888   vectors are invalid, and the approved size is invalid.
889*/
890
891static XtGeometryResult XmpTableGeometryManager( child, request, reply )
892    Widget child;               /* Widget                    */
893    XtWidgetGeometry *request;  /* Requested geometry change */
894    XtWidgetGeometry *reply;    /* Actual reply to request   */
895{
896    Widget              parent  = child->core.parent;
897    XmpTableWidget      tw      = (XmpTableWidget) parent;
898    Dimension           width, height, border_width;
899
900    if ( !parent || !XmpIsTable( parent ) )
901        XtErrorMsg("XmpTableGeometryManager", "badParent", "XtToolkitError",
902                   "Parent of widget is not an XmpTableWidget",
903                   (String*)0, (Cardinal*)0);
904
905    /* If request is only for geometry things ignored by table, do nothing.
906    */
907    if ( 0 == request->request_mode & (CWWidth|CWHeight|CWBorderWidth))
908        return XtGeometryNo;
909
910    /* Get the relevent dimensions of child: request or current.
911    */
912    if ( request->request_mode & CWWidth )
913        width = request->width;
914    else
915        width = child->core.width;
916
917    if ( request->request_mode & CWHeight )
918        height = request->height;
919    else
920        height = child->core.height;
921
922    if ( request->request_mode & CWBorderWidth )
923        border_width = request->border_width;
924    else
925        border_width = child->core.border_width;
926
927    /* Some widgets are really stupid: they ask to be resized to their
928     * current size.  Silly, but more common than you may think!
929     */
930    if ( width        == child->core.width
931      && height       == child->core.height
932      && border_width == child->core.border_width )
933    {
934        reply->width        = width;
935        reply->height       = height;
936        reply->border_width = border_width;
937        reply->request_mode = request->request_mode &
938                                        ( CWWidth | CWHeight | CWBorderWidth );
939
940        if ( request->request_mode &
941                        !(XtCWQueryOnly|CWWidth|CWHeight|CWBorderWidth) )
942        {
943            /* There are some flags set which are not respected by Table
944            */
945            return XtGeometryAlmost;
946        }
947        else if ( request->request_mode & XtCWQueryOnly )
948        {
949            /* We will honor actual resize request for width, height, border
950            */
951            return XtGeometryYes;
952        }
953        else
954        {
955            /* It is already the queried size.
956            */
957            return XtGeometryDone;
958        }
959    }
960
961    /* We can use the pre-computed proposed layout ONLY if the same child is
962     * requesting pre-approved sizes.  Note: the request_mode must be only
963     * for any combination of the pre-approved sizes and nothing else --
964     * XtCWQueryOnly MUST NOT BE set!
965     *
966     * For awhile, this `if' required the request_mode to match the
967     * approved_mode.  Some widgets (XmFrame) are broken in this regard
968     * (XmFrame queries only width so Table uses request width and current
969     * height, then XmFrame requests resize of both width & height even
970     * though only width changes).  This would cause an endless loop,
971     * because the mode never matched.
972     */
973    if ( tw->table.approved_child        == child
974      && tw->table.approved_width        == width
975      && tw->table.approved_height       == height
976      && tw->table.approved_border_width == border_width
977      && 0 == ( request->request_mode & !(CWWidth|CWHeight|CWBorderWidth) ) )
978    {
979        /* Request is equivalent to approved.
980        */
981        XmpTableUseProposedLayout( tw );
982        return XtGeometryDone;
983    }
984
985    /* No pre-approved layout, or something is different. Need to
986     * re-compute proposed layout.
987     */
988    XmpTableForgetProposedLayout( tw );
989
990    /* Pretend we re-size the child, and go through table layout logic. Then
991     * look at the resulting size of the child, and that will be the size we
992     * approve for the child.  If by some fluke the approved size is the size
993     * requested, then we do the change, otherwise return Almost.
994     */
995    tw->table.resize_child        = child;
996    tw->table.resize_mode         = request->request_mode;
997    tw->table.resize_width        = width;
998    tw->table.resize_height       = height;
999    tw->table.resize_border_width = border_width;
1000
1001    XmpTableNewProposedLayout( tw );
1002
1003    XmpTableForgetResizeChild( tw );
1004
1005    reply->request_mode = tw->table.approved_mode;
1006    reply->width        = tw->table.approved_width;
1007    reply->height       = tw->table.approved_height;
1008    reply->border_width = tw->table.approved_border_width;
1009
1010    /* Note: TableNewProposedLayout() will only set CWWidth or CWHeight or
1011     * CWBorderWidth into approved_mode.  Therefore, if request_mode has
1012     * XtCWQueryOnly or any of the flags ignored by table (like CWStackMode)
1013     * then approved_mode will be different from request_mode
1014     */
1015    if ( ( !(request->request_mode & CWWidth)
1016        ||  (tw->table.approved_width == width) )
1017      && ( !(request->request_mode & CWHeight)
1018        ||  (tw->table.approved_height == height) )
1019      && ( !(request->request_mode & CWBorderWidth)
1020        ||  (tw->table.approved_border_width == border_width) ) )
1021    {
1022        /* Everything which is asked to change matched the approved changes.
1023        */
1024        if ( (tw->table.approved_mode|XtCWQueryOnly) == request->request_mode )
1025        {
1026            /* child only queried: we would grant child's request exactly
1027            */
1028            return XtGeometryYes;
1029        }
1030        else if ( tw->table.approved_mode == request->request_mode )
1031        {
1032            /* Request is exactly as approved.
1033            */
1034            XmpTableUseProposedLayout( tw );
1035            return XtGeometryDone;
1036        }
1037    }
1038
1039    if ( tw->table.approved_mode
1040      && ( tw->table.approved_width        != child->core.width
1041        || tw->table.approved_height       != child->core.height
1042        || tw->table.approved_border_width != child->core.border_width ) )
1043    {
1044        /* Something is approved, and some approved size is different from
1045         * existing size, so some of the child's geometry would change.
1046         * approved_mode as provided by TableNewProposedLayout() already
1047         * reflects those fields which would actually change.
1048         */
1049        return XtGeometryAlmost;
1050    }
1051    else
1052    {
1053        /* Approved size is exactly the same as current size.
1054        */
1055        return XtGeometryNo;
1056    }
1057}
1058
1059/* Handle Increase or Decrease in Managed Children
1060**=================================================**
1061   Called when a child or when children are managed or unmanaged via
1062   XtManageChild(), XtUnmanageChild() etc.
1063*/
1064static void XmpTableChangeManaged( w )
1065    Widget w;
1066{
1067    XmpTableWidget tw = (XmpTableWidget)w;
1068
1069    XmpTableNewLayout( tw );
1070}
1071
1072/*===========================**
1073** End Of Xt Invoked Methods **
1074**===========================*/
1075
1076/* Internal Table Methods
1077**========================**
1078   There are these ways the Table may need to be recomputed:
1079 o ChangedManage: Compute new layout, ask parent for new size.
1080 o SetValues: Compute new layout or recompute layout, ask parent for new size.
1081 o Child change: Recompute layout, ask parent for new size.
1082 o Initial GeometryRequest from child: Compute proposed layout.
1083 o Approved GeometryRequest from child: Use proposed layout, ask parent for
1084   new size.
1085 o Resize Method: Command to change to specific size from above.
1086*/
1087
1088void XmpTableNewLayout( tw )
1089    XmpTableWidget tw;
1090{
1091    XmpTableNewRealLayout(    tw );
1092    XmpTableConsiderSameSize( tw );
1093    XmpTableNewColsAndRows(   tw );
1094    XmpTableRequestResize(    tw );
1095}
1096
1097void XmpTableRecomputeLayout( tw )
1098    XmpTableWidget tw;
1099{
1100    XmpTableConsiderSameSize( tw );
1101    XmpTableNewColsAndRows(   tw );
1102    XmpTableRequestResize(    tw );
1103}
1104
1105void XmpTableNewProposedLayout( tw )
1106    XmpTableWidget tw;
1107{
1108    XmpTableConsiderSameSize(       tw );
1109    XmpTableProposedColsAndRows(    tw );
1110    XmpTableQueryParentForResize(   tw );    /* query only, no resize */
1111
1112    /*
1113     * Since we only made a query, we *should* still need to continue.
1114     * However, Motif is broken so that we actually may already have
1115     * been resized.  In that case, the proposed layout is already
1116     * forgotten, so we should just quietly exit.
1117     */
1118    if ( tw->table.resize_status == RSdueToRequest )
1119    {
1120        XmpTableMakeColsFitQueryWidth(  tw );
1121        XmpTableMakeRowsFitQueryHeight( tw );
1122        XmpTableGetProposedChildSize(   tw );
1123        XmpTableSaveProposedLayout(     tw );
1124    }
1125    /* else the resize has already been done.  Our proposed layout would
1126     * have been forgotten in the process.
1127     */
1128}
1129
1130void XmpTableUseProposedLayout( tw )
1131    XmpTableWidget tw;
1132{
1133    XmpTableGetProposedLayout( tw );
1134
1135    tw->table.resize_table_to_size_pre_approved_by_parent = True;
1136    XmpTableRequestResize( tw );
1137    tw->table.resize_table_to_size_pre_approved_by_parent = False;
1138}
1139   
1140/* Called due to TableRequestResize()
1141*/
1142void XmpTableResizeLayout( tw )
1143    XmpTableWidget tw;
1144{
1145    if ( !tw->table.resize_table_to_size_pre_approved_by_parent )
1146    {
1147        XmpTableMakeColsFitWidth(  tw );
1148        XmpTableMakeRowsFitHeight( tw );
1149    }
1150
1151    XmpTableSetGeometryOfChildren( tw );
1152
1153    /* I have to do this in case someone is using those goddamn gadgets.
1154     * Normally, a manager should not need to redisplay itself, right!?!
1155     */
1156    if ( XtIsRealized( (Widget)tw ) )
1157    {
1158        XClearArea( XtDisplay( (Widget)tw ), XtWindow( (Widget)tw ),
1159                    0, 0, 0, 0,         /* clears entire window         */
1160                    True );             /* we need Expose events        */
1161    }
1162
1163    tw->table.resize_status = RSdone;
1164}
1165
1166
1167/* Proposed Layout Methods
1168**=========================**
1169   A resize request from a child is virtually NEVER granted straight away:
1170   Instead, a proposed layout must be computed, and the resultant size of
1171   the child is then returned.  If the child can accept this size (nearly
1172   always yes in actual existing widgets), then it immediately returns these
1173   approved sizes, and the table can detect this and use the proposed layout.
1174
1175   If any other geometry method is invoked in the meantime, the proposed
1176   layout must be cleared and forgotten.
1177*/
1178
1179void XmpTableSaveProposedLayout( tw )
1180    XmpTableWidget tw;
1181{
1182    tw->table.approved_cols = tw->table.cols;
1183    tw->table.approved_rows = tw->table.rows;
1184
1185    tw->table.cols = tw->table.current_cols;
1186    tw->table.rows = tw->table.current_rows;
1187
1188    tw->table.current_cols = (XmpTableVector)0;
1189    tw->table.current_rows = (XmpTableVector)0;
1190}
1191
1192void XmpTableGetProposedLayout( tw )
1193    XmpTableWidget tw;
1194{
1195    XmpTableVectorFree( tw->table.cols );
1196    XmpTableVectorFree( tw->table.rows );
1197
1198    tw->table.cols = tw->table.approved_cols;
1199    tw->table.rows = tw->table.approved_rows;
1200
1201    tw->table.approved_child        = (Widget)0;
1202    tw->table.approved_mode         = (XtGeometryMask)0;
1203    tw->table.approved_width        = (Dimension)0;
1204    tw->table.approved_height       = (Dimension)0;
1205    tw->table.approved_border_width = (Dimension)0;
1206    tw->table.approved_cols         = (XmpTableVector)0;
1207    tw->table.approved_rows         = (XmpTableVector)0;
1208}
1209
1210void XmpTableForgetProposedLayout( tw )
1211    XmpTableWidget tw;
1212{
1213    XmpTableVectorFree( tw->table.approved_cols );
1214    XmpTableVectorFree( tw->table.approved_rows );
1215
1216    tw->table.approved_child        = (Widget)0;
1217    tw->table.approved_mode         = (XtGeometryMask)0;
1218    tw->table.approved_width        = (Dimension)0;
1219    tw->table.approved_height       = (Dimension)0;
1220    tw->table.approved_border_width = (Dimension)0;
1221    tw->table.approved_cols         = (XmpTableVector)0;
1222    tw->table.approved_rows         = (XmpTableVector)0;
1223    tw->table.current_cols          = (XmpTableVector)0;
1224    tw->table.current_rows          = (XmpTableVector)0;
1225    tw->table.query_mode            = (XtGeometryMask)0;
1226    tw->table.query_width           = (Dimension)0;
1227    tw->table.query_height          = (Dimension)0;
1228    tw->table.resize_table_to_size_pre_approved_by_parent = False;
1229}
1230
1231void XmpTableForgetResizeChild( tw )
1232    XmpTableWidget tw;
1233{
1234    tw->table.resize_child        = (Widget)0;
1235    tw->table.resize_mode         = (XtGeometryMask)0;
1236    tw->table.resize_width        = (Dimension)0;
1237    tw->table.resize_height       = (Dimension)0;
1238    tw->table.resize_border_width = (Dimension)0;
1239}
1240
1241
1242/* Build a new real_layout for all managed children.
1243**==================================================**
1244   Each location is a function of the real_layout, default_layout,
1245   default_options, and automatic positioning.
1246
1247   The list of managed children in traversed: if a managed child already
1248   appears in the current real_layout, that layout is copied.  Otherwise,
1249   location data comes from default_layout, default_options, and automatic
1250   positioning members of the parent table widget.
1251
1252   Since both the default_layout and real_layout are changed by TablePosition(),
1253   TableResize(), TableOptions(), and TableConfig(), changes to the children
1254   remain in effect when the table layout is re-computed, and when children
1255   become managed and unmanaged multiple times.  However, if the default_layout
1256   is changed by a XtSetValues, all the positioning stuff in default_layout is
1257   lost.  OK, since both are done by the client program: if the programmer wants
1258   to change the layout, the programmer will also need to reposition children.
1259*/
1260
1261void XmpTableNewRealLayout( tw )
1262    XmpTableWidget tw;
1263{
1264    int         num_children    = tw->composite.num_children;
1265    WidgetList  children        = tw->composite.children;
1266    XmpTableLoc result          = XmpTableLocNew( num_children );
1267    XmpTableLoc loc             = result;
1268    XmpTableLoc found;
1269
1270    int         child   = 0;    /* index into list of all children */
1271
1272    for ( ;  child < num_children  ;  child++ )
1273    {
1274        Widget w = children[child];
1275
1276        if ( XtIsManaged( w ) )
1277        {
1278            /*ASSIGN_IN_IF*/
1279            if ( found = XmpTableLocFind( tw->table.real_layout, w ) )
1280            {
1281                /* This widget was in previous layout, copy all fields
1282                 */
1283                *loc = *found;
1284            }
1285            /*ASSIGN_IN_IF*/
1286            else if ( found = XmpTableLocFind( tw->table.default_layout, w ) )
1287            {
1288                /* This child has been laid out before, so copy everything.
1289                 */
1290                *loc = *found;
1291            }
1292            /*ASSIGN_IN_IF*/
1293            else if ( found = XmpTableLocFindDefault( tw->table.default_layout,
1294                                                        w ) )
1295            {
1296                /* Never laid out this child, but default layout provides
1297                 * some information.  Copy everything, fill in the blanks
1298                 * (col,row,col_span,row_span already have defaults).
1299                 * No problem if tw->table.default_options is zero.
1300                 */
1301                *loc = *found;
1302                loc->w = w;
1303                loc->orig_width        = w->core.width;
1304                loc->orig_height       = w->core.height;
1305                loc->orig_border_width = w->core.border_width;
1306                if ( !loc->options )
1307                    loc->options = tw->table.default_options;
1308
1309                XmpTableAppendToDefaultLayout( tw, loc );
1310            }
1311            else
1312            {
1313                /* Never laid out this child, not in default layout.  Fill
1314                 * in everything with default values.
1315                 */
1316                loc->w = w;
1317                loc->w_quark = w->core.xrm_name;
1318                loc->col = loc->row = 0;
1319                loc->col_span = loc->row_span = 1;
1320                loc->orig_width        = w->core.width;
1321                loc->orig_height       = w->core.height;
1322                loc->orig_border_width = w->core.border_width;
1323                if ( !loc->options )
1324                    loc->options = tw->table.default_options;
1325
1326                XmpTableAppendToDefaultLayout( tw, loc );
1327            }
1328            loc++;      /* loc only incremented for MANAGED children */
1329        }
1330    }
1331    XmpTableLocFree( tw->table.real_layout );
1332    tw->table.real_layout = result;
1333}
1334
1335/* Append loc to default_layout
1336**==============================**
1337   The loc points to a Widget child.  When that child is destroyed,
1338   we must clear out that pointer, as it will point to deallocated
1339   data.
1340*/
1341static void XmpTableRemoveFromDefaultLayoutCB _((Widget,XtPointer,XtPointer));
1342
1343void XmpTableAppendToDefaultLayout( tw, loc )
1344    XmpTableWidget tw;
1345    XmpTableLoc    loc;
1346{
1347    int inx;
1348
1349    tw->table.default_layout = XmpTableLocGrow( tw->table.default_layout );
1350    inx = XmpTableLocLen( tw->table.default_layout );
1351    tw->table.default_layout[inx] = *loc;
1352    XtAddCallback( loc->w, XtNdestroyCallback,
1353                   XmpTableRemoveFromDefaultLayoutCB, (XtPointer)tw );
1354}
1355
1356/* Remove Destroyed Widget from default_layout
1357**=============================================**
1358*/
1359/*ARGSUSED*/
1360static void XmpTableRemoveFromDefaultLayoutCB( w, client, call )
1361    Widget    w;
1362    XtPointer client, call;
1363{
1364    XmpTableWidget tw  = (XmpTableWidget)client;
1365    XmpTableLoc    loc = XmpTableLocFind( tw->table.default_layout, w );
1366
1367    if ( loc )
1368        loc->w = (Widget)0;
1369}
1370
1371/* Consider Same Size constraints on all children in real_layout
1372**===============================================================**
1373   ALWAYS do this BEFORE creating new or proposed columns and rows.
1374   New TableVectors need this information to determine the `preferred'
1375   sizes of children.   This MAY happen when we are responding to a
1376   resize request from a child: in this case, we need to consider the
1377   resize request size instead of that specific child's current size.
1378*/
1379void XmpTableConsiderSameSize( tw )
1380    XmpTableWidget tw;
1381{
1382    XmpTableLoc loc, real_layout;
1383    int         same_what;
1384
1385    real_layout = tw->table.real_layout;
1386
1387    for ( loc = real_layout  ;  loc->w_quark != NULLQUARK  ;  loc++ )
1388    {
1389        loc->same_width  = 0;
1390        loc->same_height = 0;
1391        loc->same_border = 0;
1392    }
1393
1394    for ( same_what = 1  ;  same_what <= 3  ;  ++same_what )
1395    {
1396        XrmNameListLists lists;
1397        int              list;
1398
1399        switch ( same_what )
1400        {
1401        case 1: lists = tw->table.same_width;   break;
1402        case 2: lists = tw->table.same_height;  break;
1403        case 3: lists = tw->table.same_border;  break;
1404        }
1405
1406        /* Go down all the lists of lists of names ...
1407        */
1408        for ( list = 0  ;  lists != NULL && lists[list] != NULL  ;  ++list )
1409        {
1410            /* Go down all the names on each list to determine the max dimension
1411            */
1412            XrmNameList names = lists[list];
1413            int         name;
1414            Dimension   max;
1415
1416            for ( max = name = 0  ;  names[name] != (XrmName)0  ;  ++name )
1417            {
1418                /* Go down entire list of children, and for each kid
1419                 * with this name get the maximum size.
1420                 */
1421                for ( loc = real_layout ; loc->w_quark != NULLQUARK ; loc++)
1422                {
1423                    Widget kid = loc->w;
1424                    if ( kid != (Widget)0 && kid->core.xrm_name == names[name] )
1425                    {
1426#define MAXX(v) (max>v?max:v)
1427                        if ( kid == tw->table.resize_child )
1428                        {
1429                            switch ( same_what )
1430                            {
1431                            case 1: max = MAXX( tw->table.resize_width );
1432                                    break;
1433                            case 2: max = MAXX( tw->table.resize_height );
1434                                    break;
1435                            case 3: max = MAXX( tw->table.resize_border_width );
1436                                    break;
1437                            }
1438                        }
1439                        else
1440                        {
1441                            switch ( same_what )
1442                            {
1443                            case 1: max = MAXX( kid->core.width );
1444                                    break;
1445                            case 2: max = MAXX( kid->core.height );
1446                                    break;
1447                            case 3: max = MAXX( kid->core.border_width );
1448                                    break;
1449                            }
1450                        }
1451#undef MAXX
1452                    }
1453                }
1454            }
1455            /* OK, we now have the maximum dimension.  Apply to all
1456             * kids with names on this list.
1457             */
1458            for ( name = 0  ;  names[name] != (XrmName)0  ;  ++name )
1459            {
1460                for ( loc = real_layout ; loc->w_quark != NULLQUARK ; loc++)
1461                {
1462                    Widget kid = loc->w;
1463                    if ( kid != (Widget)0 && kid->core.xrm_name == names[name] )
1464                    {
1465                        switch ( same_what )
1466                        {
1467                        case 1: loc->same_width  = max; break;
1468                        case 2: loc->same_height = max; break;
1469                        case 3: loc->same_border = max; break;
1470                        }
1471                    }
1472                }
1473            }
1474        }
1475    }
1476}
1477
1478/* Create New Cols and Rows Vectors
1479**==================================**
1480   This must be done whenever a new real_layout is created, or when
1481   the existing real_layout has been changed.
1482*/
1483void XmpTableNewColsAndRows( tw )
1484    XmpTableWidget tw;
1485{
1486    XmpTableVectorFree( tw->table.cols );
1487    XmpTableVectorFree( tw->table.rows );
1488
1489    tw->table.num_cols = XmpTableLocNumCols( tw->table.real_layout );
1490    tw->table.num_rows = XmpTableLocNumRows( tw->table.real_layout );
1491
1492    if ( tw->table.num_cols && tw->table.num_rows )
1493    {
1494        tw->table.cols = XmpTableVectorNew( tw->table.num_cols, tw, DO_COL );
1495        tw->table.rows = XmpTableVectorNew( tw->table.num_rows, tw, DO_ROW );
1496    }
1497    else
1498    {
1499        tw->table.num_cols = tw->table.num_rows = 0;
1500        tw->table.cols     = tw->table.rows     = (XmpTableVector)0;
1501    }
1502}
1503
1504/* Create New Cols and Rows Vectors for Proposed Layout
1505**======================================================**
1506   Save the "real" valid vectors for later.  Note that we CERTAINLY have
1507   rows and columns, because this is only invoked due to a child's request
1508   to resize, and only managed children will be processed, and the existence
1509   of managed children implies the existence of non-null row and column
1510   vectors.
1511*/
1512void XmpTableProposedColsAndRows( tw )
1513    XmpTableWidget tw;
1514{
1515    tw->table.current_cols = tw->table.cols;
1516    tw->table.current_rows = tw->table.rows;
1517
1518    tw->table.cols = XmpTableVectorNew( tw->table.num_cols, tw, DO_COL );
1519    tw->table.rows = XmpTableVectorNew( tw->table.num_rows, tw, DO_ROW );
1520}
1521
1522/* Adjust rows and columns to fit
1523**================================**
1524   These procedures are called when the Table's parent changes the size of the
1525   Table.  The TableRecomputeLayout() procedure computes the preferred size of
1526   the table based on the layout and the children, with the assumption that the
1527   table could be any size.  Now, we have a specific size, so we will need to
1528   adjust everything to fit.
1529
1530   If the new size is larger, then its easy: just expand the space available to
1531   each row and/or column, and change the geometries of all the children.
1532
1533   If the new size is smaller and force_shrink is true (the new default), then
1534   adjust all children to fit the new size.
1535
1536   If the new size is smaller and force_shrink is false then we must do
1537   something like a better behaved version of the old behavior:  Shrink
1538   to the preferred size but no smaller.
1539*/
1540void XmpTableMakeColsFitWidth( tw )
1541    XmpTableWidget tw;
1542{
1543    XmpTableFitThis( tw, DO_COL, tw->core.width );
1544}
1545
1546void XmpTableMakeColsFitQueryWidth( tw )
1547    XmpTableWidget tw;
1548{
1549    XmpTableFitThis( tw, DO_COL, tw->table.query_width );
1550}
1551
1552void XmpTableMakeRowsFitHeight( tw )
1553    XmpTableWidget tw;
1554{
1555    XmpTableFitThis( tw, DO_ROW, tw->core.height );
1556}
1557
1558void XmpTableMakeRowsFitQueryHeight( tw )
1559    XmpTableWidget tw;
1560{
1561    XmpTableFitThis( tw, DO_ROW, tw->table.query_height );
1562}
1563
1564void XmpTableFitThis( tw, do_col, to_fit )
1565    XmpTableWidget tw;
1566    int            do_col, to_fit;
1567{
1568    int change, current, prefer, num;
1569    XmpTableVector vec;
1570
1571    if ( do_col )
1572    {
1573        vec = tw->table.cols;
1574        num = tw->table.num_cols;
1575    }
1576    else
1577    {
1578        vec = tw->table.rows;
1579        num = tw->table.num_rows;
1580    }
1581    current = XmpTableVectorTotalSize(     vec, num, tw, do_col );
1582    prefer  = XmpTableVectorPreferredSize( vec, num, tw, do_col );
1583
1584    if ( to_fit < prefer  &&  tw->table.force_shrink == False )
1585    {
1586        /* Smallest size is preferred size.  Excess clipped.
1587        */
1588        change = prefer - current;
1589    }
1590    else
1591    {
1592        change = to_fit - current;
1593    }
1594
1595    if ( change != 0 )
1596        XmpTableVectorAdjust( vec, num, change );
1597}
1598
1599/* Determine Preferred (Minimum) Size of Table
1600**=============================================**
1601*/
1602int XmpTablePreferredWidth( tw )
1603    XmpTableWidget tw;
1604{
1605    if ( tw->table.requesting_resize )
1606    {
1607        return tw->table.requesting_width;
1608    }
1609    else
1610    {
1611        XmpTableVector  vec = tw->table.cols;
1612        int             num = tw->table.num_cols;
1613
1614        return XmpTableVectorPreferredSize( vec, num, tw, DO_COL );
1615    }
1616}
1617
1618int XmpTablePreferredHeight( tw )
1619    XmpTableWidget tw;
1620{
1621    if ( tw->table.requesting_resize )
1622    {
1623        return tw->table.requesting_height;
1624    }
1625    else
1626    {
1627        XmpTableVector  vec = tw->table.rows;
1628        int             num = tw->table.num_rows;
1629
1630        return XmpTableVectorPreferredSize( vec, num, tw, DO_ROW );
1631    }
1632}
1633
1634/* Request Resize from Parent
1635**============================**
1636   This procedure gets called by other Table methods when the Table instance
1637   wants to grow or shrink.  Since we cannot yet tell if the desired size is OK,
1638   the children of the Table have NOT been sized or positioned: this is only
1639   done by the TableResize method.
1640
1641   Here is when the wonders of Xt Geometry Management come into play.  We cannot
1642   tell a priori what the hell is going to happen here.  We can ask the parent
1643   to allow the table to resize based on the computed width and height of the
1644   cols and rows.
1645
1646   If the parent says yes, then TableResize may, or then again, may not, have
1647   been called.  Since we don't know, we must keep a bogus little flag in the
1648   instance to indicate what really happened.
1649*/
1650void XmpTableRequestResize( tw )
1651    XmpTableWidget tw;
1652{
1653    XtGeometryResult    result;
1654    XtWidgetGeometry    desired, approved;
1655   
1656    /* If this is RSdone after the call to XtMakeResizeRequest(), then we know
1657     * that TableResize() has been invoked.  Otherwise, we must invoke
1658     * TableResize() directly.
1659     */
1660    tw->table.resize_status = RSdueToRequest;
1661
1662    /* We may be requesting a resize after a child asked for a resize.
1663     * In this case, we already have a size pre-approved by the parent.
1664     * Otherwise, we want to become our preferred size.
1665     */
1666    if ( tw->table.resize_table_to_size_pre_approved_by_parent )
1667    {
1668        desired.width        = tw->table.query_width;
1669        desired.height       = tw->table.query_height;
1670        desired.request_mode = tw->table.query_mode;
1671    }
1672    else
1673    {
1674        desired.width        = XmpTablePreferredWidth(  tw );
1675        desired.height       = XmpTablePreferredHeight( tw );
1676        desired.request_mode = (XtGeometryMask)0;
1677        if ( desired.width != tw->core.width )
1678            desired.request_mode |= CWWidth;
1679        if ( desired.width != tw->core.height )
1680            desired.request_mode |= CWHeight;
1681    }
1682
1683    /* XtMakeResizeRequest() asks the parent to allow this table to resize.
1684     * The parent, a Composite widget, will often need to query all of its
1685     * children (including this table) to see what sizes they want to be.
1686     * Therefore, there is a very good chance that the QueryGeometry method of
1687     * this table widget will be invoked in a few microseconds, and this table
1688     * will need to compute its desired size.  So, for efficiency we remember
1689     * our desired size.
1690     */
1691    tw->table.requesting_resize = True;
1692    tw->table.requesting_width  = desired.width;
1693    tw->table.requesting_height = desired.height;
1694
1695    /* If desired size is current size, then do NOT ask parent, act like its OK.
1696    */
1697    if ( desired.width == tw->core.width && desired.height == tw->core.height )
1698        result = XtGeometryYes;
1699    else
1700        result = XtMakeGeometryRequest( (Widget)tw, &desired, &approved );
1701
1702    /* Nothing special to do if XtGeometryYes or XtGeometryNo
1703    */
1704    if ( result == XtGeometryAlmost )
1705    {
1706        /* Now the desired size is the size our parent will allow.
1707        */
1708        result = XtMakeGeometryRequest( (Widget)tw, &approved, &approved );
1709    }
1710
1711    tw->table.requesting_resize = False;
1712
1713    /* No matter what the outcome, the Table must be "resized", as this is
1714     * where the table looks at its actual width/height and sizes and positions
1715     * the children widgets.
1716     */
1717    if ( tw->table.resize_status == RSdueToRequest )
1718        XmpTableResize( (Widget)tw );
1719}
1720
1721/* Query parent for hypothetical resize.
1722**======================================**
1723   This is called when a child has asked to be resized, and such a request,
1724   if granted, would cause the table to ask its parent for a resize.
1725
1726   We ask the parent to tell us what size the parent would resize the table,
1727   if the table really asked for a resize to its "proposed" size.
1728
1729   Note that we DO NOT ask the parent to resize us, because we are not
1730   COMPLETELY certain the child will want the size we will propose for it:
1731   the child may withdraw its resize.
1732
1733   Unfortunately, the GeometryManager method of the parent may not in fact
1734   treat this as a simple query, but it may treat it as a request, and the
1735   table may well have its size changed by a call to its Resize method!  Yes,
1736   this is a common deficiency in GeometryManager methods of composite widgets.
1737   It is VERY difficult to get all this &$%?%$!?! right.
1738*/
1739
1740void XmpTableQueryParentForResize( tw )
1741    XmpTableWidget tw;
1742{
1743    XtGeometryResult    result;
1744    XtWidgetGeometry    desired, query;
1745   
1746    /* If this is RSdone after the call to XtMakeGeometryRequest(), then we
1747     * know that TableResize() has been invoked - UNFORTUNATELY!
1748     */
1749    tw->table.resize_status = RSdueToRequest;
1750
1751    /* Query only for width and height
1752    */
1753    desired.request_mode = (XtCWQueryOnly|CWWidth|CWHeight);
1754
1755    /* Desired dimensions reflect change due to resize_child's resize request
1756    */
1757    desired.width  = XmpTablePreferredWidth(  tw );
1758    desired.height = XmpTablePreferredHeight( tw );
1759
1760    /* XtMakeGeometryRequest() asks the parent for the result of a hypothetical
1761     * resize request from the table.  The parent, a Composite widget, will
1762     * often need to query all of its children (including this table) to see
1763     * what sizes they want to be.  Therefore, there is a very good chance that
1764     * the QueryGeometry method of this table widget will be invoked in a few
1765     * microseconds, and this table will need to compute its desired size.  So,
1766     * for efficiency we remember our desired size.
1767     */
1768    tw->table.requesting_resize = True;
1769    tw->table.requesting_width  = desired.width;
1770    tw->table.requesting_height = desired.height;
1771
1772    /* If desired size is current size, then do NOT ask parent, act like its OK.
1773    */
1774    if ( desired.width == tw->core.width && desired.height == tw->core.height )
1775        result = XtGeometryYes;
1776    else
1777        result = XtMakeGeometryRequest( (Widget)tw, &desired, &query );
1778
1779    tw->table.requesting_resize = False;
1780
1781    switch ( result )
1782    {
1783    case XtGeometryYes:
1784        tw->table.query_mode   = CWWidth | CWHeight;
1785        tw->table.query_width  = desired.width;
1786        tw->table.query_height = desired.height;
1787        break;
1788    case XtGeometryAlmost:
1789        tw->table.query_mode   = query.request_mode & (CWWidth | CWHeight);
1790        tw->table.query_width  = query.width;
1791        tw->table.query_height = query.height;
1792        break;
1793    case XtGeometryNo:
1794    default:
1795        /* Cannot resize.
1796        */
1797        tw->table.query_mode   = (XtGeometryMask)0;
1798        tw->table.query_width  = tw->core.width;
1799        tw->table.query_height = tw->core.height;
1800        break;
1801    }
1802
1803    if ( result == XtGeometryYes && tw->table.resize_status != RSdueToRequest )
1804    {
1805        /* Oh Shit!  The parent did not notice this was just a QUERY, and
1806         * it went ahead and did the change.  Oh Shit!
1807         */
1808        Widget parent = XtParent( (Widget)tw );
1809        char* class = parent->core.widget_class->core_class.class_name;
1810        String args[3];
1811        Cardinal two = 2;
1812        args[0] = XtName(parent); args[1] = class; args[2] = NULL;     
1813        XtWarningMsg( "brokenParent", "XmpTableQueryParentForResize",
1814                "Widget Library Error",
1815                "Widget %s of class %s\nhas a GeometryManager method which ignores XtCWQueryOnly!",
1816                args, &two );
1817        /* Already resized.
1818        */
1819        tw->table.query_mode = (XtGeometryMask)0;
1820        tw->table.query_width = tw->core.width;
1821        tw->table.query_height = tw->core.height;
1822    }
1823}
1824
1825/* Get Approved Size of Child
1826**============================**
1827   A child has asked to be resized.  We need to compute the size which
1828   the table would make the child if the table itself was resized (from
1829   above).  Assume we will grant the border width request UNLESS the
1830   width or height goes to zero or negative.
1831
1832   The child is managed (or else it could not make the resize request),
1833   so the child is in the real_layout.
1834*/
1835void XmpTableGetProposedChildSize( tw )
1836    XmpTableWidget      tw;
1837{
1838    int         width, height, bw, x, y;
1839    XmpTableLoc loc = tw->table.real_layout;
1840
1841    while ( loc->w && loc->w != tw->table.resize_child )
1842        ++loc;
1843
1844    if ( loc->w != tw->table.resize_child )
1845    {
1846        String args[3];
1847        Cardinal two = 2;
1848        args[0] = XtName(tw->table.resize_child);
1849        args[1] = XtName((Widget)tw);
1850        args[2] = NULL;
1851        XtWarningMsg( "unmanagedChildMadeResizeRequest",
1852                      "XmpTableGetProposedChildSize",
1853                      "Xt Implementation Bug",
1854                      "XtMakeGeometryRequest passed request from unmanaged child %s to table %s\n",
1855                      args, &two );
1856
1857        XmpTableForgetProposedLayout( tw );
1858        return;
1859    }
1860
1861    /* This loc is for the child requesting the resize.
1862     *
1863     * First, try to use all the dimensions: width, height, requested border.
1864     * If the width and height are not minimums, we are done.
1865     */
1866    width = height = 0;                 /* need to compute these */
1867    bw = tw->table.resize_border_width; /* assume we grant this  */
1868
1869    XmpTableComputeChildSize( tw, loc, &width, &height, &bw, &x, &y );
1870
1871    if ( 1 < width &&  1 < height )
1872    {
1873        XmpTableApproveGeometryChanges( tw, loc->w, width, height, bw );
1874        return;
1875    }
1876
1877    /* Try again, this time using the current border width
1878    */
1879    width = height = 0;
1880    bw = loc->w->core.border_width;
1881
1882    XmpTableComputeChildSize( tw, loc, &width, &height, &bw, &x, &y );
1883
1884    if ( width  < 1 ) width  = 1;
1885    if ( height < 1 ) height = 1;
1886    XmpTableApproveGeometryChanges( tw, loc->w, width, height, bw );
1887}
1888
1889void XmpTableApproveGeometryChanges( tw, child, width, height, bw )
1890    XmpTableWidget      tw;
1891    Widget              child;
1892    int                 width, height, bw;
1893{
1894    tw->table.approved_child = child;
1895    tw->table.approved_mode  = (XtGeometryMask)0;
1896
1897    if ( tw->table.resize_mode&CWBorderWidth && bw != child->core.border_width )
1898        tw->table.approved_mode |= CWBorderWidth;
1899
1900    if ( tw->table.resize_mode&CWWidth && width != child->core.width )
1901        tw->table.approved_mode |= CWWidth;
1902
1903    if ( tw->table.resize_mode&CWHeight && height != child->core.height )
1904        tw->table.approved_mode |= CWHeight;
1905
1906    tw->table.approved_border_width = bw;
1907    tw->table.approved_width        = width;
1908    tw->table.approved_height       = height;
1909}
1910
1911/* Set Geometry Of Children
1912**==========================**
1913   Children are placed according to the real_layout, cols, rows, and
1914   row and column spacing.
1915*/
1916void XmpTableSetGeometryOfChildren( tw )
1917    XmpTableWidget tw;
1918{
1919    XmpTableLoc loc;
1920    int         shadow = tw->manager.shadow_thickness;
1921
1922    if ( tw->table.real_layout  == (XmpTableLoc)0
1923      || tw->table.cols         == (XmpTableVector)0
1924      || tw->table.num_cols     == 0
1925      || tw->table.rows         == (XmpTableVector)0
1926      || tw->table.num_rows     == 0 )
1927        return;
1928
1929    XmpTableVectorComputeOffsets( tw->table.cols, tw->table.num_cols,
1930                                  tw->bulletin_board.margin_width + shadow,
1931                                  tw->table.col_spacing );
1932
1933    XmpTableVectorComputeOffsets( tw->table.rows, tw->table.num_rows,
1934                                  tw->bulletin_board.margin_height + shadow,
1935                                  tw->table.row_spacing );
1936
1937    for ( loc = tw->table.real_layout  ;  loc->w  ;  loc++ )
1938    {
1939        int width, height, bw, x, y;
1940
1941        /* This can be invoked due to a child size change request, in which
1942         * case the child has not yet been changed in size, but this size
1943         * has been guaranteed to be OK.
1944         */
1945        if ( loc->w == tw->table.approved_child )
1946        {
1947            width  = tw->table.approved_width;
1948            height = tw->table.approved_height;
1949            bw     = tw->table.approved_border_width;
1950        }
1951        else
1952        {
1953            width  = 0;
1954            height = 0;
1955            bw     = loc->w->core.border_width;
1956        }
1957
1958        XmpTableComputeChildSize( tw, loc, &width, &height, &bw, &x, &y );
1959
1960#if (XmVERSION >= 2)
1961
1962        if ( x      != loc->w->core.x
1963          || y      != loc->w->core.y
1964          || width  != loc->w->core.width
1965          || height != loc->w->core.height
1966          || bw     != loc->w->core.border_width )
1967        {
1968            XmeConfigureObject( loc->w, x, y, width, height, bw );
1969        }
1970
1971#else /* Motif 1.x */
1972
1973        if ( width  != loc->w->core.width
1974          || height != loc->w->core.height
1975          || bw     != loc->w->core.border_width )
1976        {
1977#if (XmVERSION == 1 && XmREVISION >= 2)
1978            _XmResizeObject( loc->w, width, height, bw );
1979#else
1980            _XmResizeObject( (RectObj)(loc->w), width, height, bw );
1981#endif
1982        }
1983
1984        if ( x != loc->w->core.x
1985          || y != loc->w->core.y )
1986        {
1987#if (XmVERSION == 1 && XmREVISION >= 2 && XmUPDATE_LEVEL >= 2) || XmVERSION > 1
1988            _XmMoveObject( loc->w, x, y );
1989#else
1990            _XmMoveObject( (RectObj)(loc->w), x, y );
1991#endif
1992
1993        }
1994#endif /* end of Motif 1.x code */
1995    }
1996}
1997
1998void XmpTableComputeChildSize( tw, loc, wP, hP, bwP, xP, yP )
1999    XmpTableWidget      tw;
2000    XmpTableLoc         loc;
2001    int                 *wP, *hP;               /* in-out, USE if != 0 */
2002    int                 *bwP;                   /* in  */
2003    int                 *xP, *yP;               /* out */
2004{
2005    int cell_x, cell_y, cell_w, cell_h;
2006    int new_x,  new_y,  new_w,  new_h, prefer;
2007    int total_w, total_h;
2008    int pad, i;
2009    int init_w = *wP;   /* non-zero means use it */
2010    int init_h = *hP;   /* non-zero means use it */
2011    int bw     = *bwP;
2012
2013    /* Upper left corner of where we will place the widget
2014    */
2015    cell_x = tw->table.cols[ loc->col ].offset;
2016    cell_y = tw->table.rows[ loc->row ].offset;
2017
2018    /* cell width and height may well span cols and rows and spacing
2019    */
2020    pad = tw->table.col_spacing;
2021    cell_w = -pad;
2022    for ( i = 0  ;  i < loc->col_span  ;  i++ )
2023        cell_w += tw->table.cols[ loc->col + i ].value + pad;
2024
2025    pad = tw->table.row_spacing;
2026    cell_h = -pad;
2027    for ( i = 0  ;  i < loc->row_span  ;  i++ )
2028        cell_h += tw->table.rows[ loc->row + i ].value + pad;
2029
2030    /* If the width or height was given, then use it.  This given dimension
2031     * has already been approved by the table in geometry negotiation (by
2032     * the GeometryManager method).
2033     *
2034     * Otherwise, If size growth is prevented due to TBL_SM_(WIDTH | HEIGHT),
2035     * then use the lesser of the cell size or the preferred size.
2036     *
2037     * Otherwise, use the cell size.
2038     */
2039    if (init_w)
2040    {
2041        new_w = init_w;
2042    }
2043    else if (loc->options & TBL_SM_WIDTH
2044          && cell_w > (prefer = XmpTableLocPreferredWidth( loc, tw )) )
2045    {
2046        new_w = prefer - 2 * bw;
2047    }
2048    else
2049    {
2050        new_w = cell_w - 2 * bw;
2051    }
2052
2053    if (init_h)
2054    {
2055        new_h = init_h;
2056    }
2057    else if (loc->options & TBL_SM_HEIGHT
2058          && cell_h > (prefer = XmpTableLocPreferredHeight( loc, tw )) )
2059    {
2060        new_h = prefer - 2 * bw;
2061    }
2062    else
2063    {
2064        new_h = cell_h - 2 * bw;
2065    }
2066
2067    /* Be certain that the size does not go to zero, or negative!
2068    */
2069    if ( new_w <= 0 ) new_w = 1;        /* value ready for XtResize */
2070    if ( new_h <= 0 ) new_h = 1;        /* value ready for XtResize */
2071
2072    total_w = new_w + 2 * bw;
2073    total_h = new_h + 2 * bw;
2074
2075    if ( loc->options & TBL_LEFT )
2076        new_x = cell_x;                         /* left justify in cell   */
2077    else if ( loc->options & TBL_RIGHT )
2078        new_x = cell_x + cell_w - total_w;      /* right justify in cell  */
2079    else
2080        new_x = cell_x + (cell_w - total_w)/2;  /* center in cell         */
2081
2082    if ( loc->options & TBL_TOP )
2083        new_y = cell_y;                         /* top justify in cell    */
2084    else if ( loc->options & TBL_BOTTOM )
2085        new_y = cell_y + cell_h - total_h;      /* bottom justify in cell */
2086    else
2087        new_y = cell_y + (cell_h - total_h)/2;  /* center in cell         */
2088
2089    *wP = new_w;
2090    *hP = new_h;
2091    *xP = new_x;
2092    *yP = new_y;
2093}
2094
2095
2096/* TableOpts methods
2097**===================**
2098*/
2099
2100XmpTableOpts XmpTableOptsParse( optString )
2101    String optString;
2102{
2103    XmpTableOpts opt = 0;
2104
2105    /* null, Null, NULL, none, NONE, ...  Anything starting with `N'
2106    */
2107    if ( optString && ( *optString == 'n' || *optString == 'N') )
2108        return 0;
2109
2110    for ( ;  *optString;  optString++) {
2111        switch (*optString)
2112        {
2113        case 'l':       opt |= TBL_LEFT;        break;
2114        case 'r':       opt |= TBL_RIGHT;       break;
2115        case 't':       opt |= TBL_TOP;         break;
2116        case 'b':       opt |= TBL_BOTTOM;      break;
2117        case 'w':       opt |= TBL_LK_WIDTH;    break;
2118        case 'h':       opt |= TBL_LK_HEIGHT;   break;
2119        case 'W':       opt |= TBL_SM_WIDTH;    break;
2120        case 'H':       opt |= TBL_SM_HEIGHT;
2121        default:        break;
2122        }
2123    }
2124    return opt;
2125}
2126
2127
2128/* Client Utility Functions
2129**==========================**
2130The following functions are used to reconfigure children of Table widgets
2131*/
2132
2133/* Position Child in Col,Row of Table
2134**====================================**
2135*/
2136#define CHG_POS  0x1
2137#define CHG_SPAN 0x2
2138#define CHG_OPTS 0x4
2139#define CHG_ALL  0x7
2140
2141static void Change( loc, what, col, row, col_span, row_span, opts )
2142    XmpTableLoc  loc;                   /* specific loc to change       */
2143    int          what;                  /* What to change               */
2144    int          col, row;              /* New position in table        */
2145    int          col_span, row_span;    /* New spans of child in table  */
2146    XmpTableOpts opts;                  /* New size/justification opts  */
2147{
2148    if ( what & CHG_POS )
2149    {
2150        if ( 0 <= col ) loc->col = col;
2151        if ( 0 <= row ) loc->row = row;
2152    }
2153    if ( what & CHG_SPAN )
2154    {
2155        if ( 1 <= col_span ) loc->col_span = col_span;
2156        if ( 1 <= row_span ) loc->row_span = row_span;
2157    }
2158    if ( what & CHG_OPTS )
2159    {
2160        loc->options = opts;
2161    }
2162}
2163
2164static void XmpTableChildChange(child, what, col, row, col_span, row_span, opts)
2165    Widget       child;                 /* Child widget to change       */
2166    int          what;                  /* What to change               */
2167    int          col, row;              /* New position in table        */
2168    int          col_span, row_span;    /* New spans of child in table  */
2169    XmpTableOpts opts;                  /* New size/justification opts  */
2170{
2171    if ( !XmpIsTable( child->core.parent ) )
2172    {
2173        Cardinal one = 1;
2174        char* name = XtName( child );
2175        XtWarningMsg( "notChildOfTable", "XmpTableChildChange", "XmpLibError",
2176                "Widget %s is not a child of an XmpTable widget.", &name, &one);
2177        return;
2178    }
2179    else
2180    {
2181        XmpTableWidget  tw   = (XmpTableWidget)child->core.parent;
2182        XmpTableLoc     def  = XmpTableLocFind( tw->table.default_layout,child);
2183        XmpTableLoc     real = XmpTableLocFind( tw->table.real_layout,   child);
2184
2185        if ( def == (XmpTableLoc)0 )
2186        {
2187            /* Never laid out this child before. 
2188            */
2189            static XmpTableLocRec       nullRec;
2190            XmpTableLocRec              newRec;
2191            XmpTableLoc                 initDef;
2192
2193            newRec = nullRec;
2194            def = &newRec;
2195
2196            /* Find the initial default for this name, copy what we can.
2197            */
2198            initDef = XmpTableLocFindDefault( tw->table.default_layout, child );
2199            if ( initDef != (XmpTableLoc)0 )
2200                *def = *initDef;
2201
2202            /* Set up default fields.
2203            */
2204            def->w_quark = child->core.xrm_name;
2205            def->w       = child;
2206            if ( def->col_span <= 0 ) def->col_span = 1;
2207            if ( def->row_span <= 0 ) def->row_span = 1;
2208            if ( def->options  == 0 ) def->options  = tw->table.default_options;
2209            def->orig_width        = child->core.width;
2210            def->orig_height       = child->core.height;
2211            def->orig_border_width = child->core.border_width;
2212
2213            /* Append to default_layout, then get pointer to that loc
2214            */
2215            XmpTableAppendToDefaultLayout( tw, def );
2216            def = XmpTableLocFind( tw->table.default_layout,child );
2217        }
2218
2219        /* Change loc in default_layout to reflect widget position etc.
2220        */
2221        Change( def, what, col, row, col_span, row_span, opts );
2222       
2223        if ( real != (XmpTableLoc)0 )
2224        {
2225            /* In real_layout: Change child's actual position/span/opt
2226            */
2227            Change( real, what, col, row, col_span, row_span, opts );
2228
2229            /* We do not have to create a new real_layout.  Also, we know
2230            ** child is managed, since it is in the real_layout.  Therefore:
2231            */
2232            XmpTableRecomputeLayout( tw );
2233        }
2234        /* If the child is not managed, no re-layout needs to be done: it
2235        ** will be done when the child becomes managed (see ChangeManaged)
2236        */
2237    }
2238}
2239
2240/* Change Position of Child
2241**==========================**
2242*/
2243void XmpTableChildPosition( child, col, row )
2244    Widget child;                       /* Child widget to move         */
2245    int    col, row;                    /* New position in table        */
2246{
2247    XmpTableChildChange( child, CHG_POS, col, row, 0, 0, 0);
2248}
2249
2250/* Change Size (Span) of Child
2251**=============================**
2252*/
2253void XmpTableChildResize( child, col_span, row_span)
2254    Widget child;                       /* Child widget to resize       */
2255    int    col_span, row_span;          /* New widget span              */
2256{
2257    XmpTableChildChange( child, CHG_SPAN, 0, 0, col_span, row_span, 0);
2258}
2259
2260void XmpTableChildOptions( child, opts )
2261    Widget       child;         /* Child widget to get new options      */
2262    XmpTableOpts opts;          /* New option mask                      */
2263{
2264    XmpTableChildChange( child, CHG_OPTS, 0, 0, 0, 0, opts );
2265}
2266
2267void XmpTableChildConfig( child, col, row, col_span, row_span, opts )
2268    Widget child;               /* Child widget to change       */
2269    int col, row;               /* New position in table        */
2270    int col_span, row_span;     /* New spans of child in table  */
2271    XmpTableOpts opts;          /* New size/justification opts  */
2272{
2273    XmpTableChildChange(child, CHG_ALL , col, row, col_span, row_span, opts);
2274}
2275
2276/* Constructors
2277**==============**
2278*/
2279
2280Widget XmpCreateTable( parent, name, arglist, argcount )
2281    Widget   parent;
2282    char*    name;
2283    ArgList  arglist;
2284    Cardinal argcount;
2285{
2286   return XtCreateWidget(name, xmpTableWidgetClass, parent, arglist, argcount);
2287}
2288
2289/* Table Dialog Constructor
2290** Date: Fri, 8 Feb 91 12:23:39 EST
2291** From: pastor@PRC.Unisys.COM (Jon A. Pastor)
2292*/
2293
2294#ifndef DIALOG_SUFFIX
2295#define DIALOG_SUFFIX "_popup"
2296#define DIALOG_SUFFIX_SIZE strlen(DIALOG_SUFFIX)
2297#endif
2298
2299/* Destroy parent dialog shell when the child is destroyed.
2300*/
2301/*ARGSUSED*/
2302static void XmpDestroyParentCallback( w, ignored, unused )
2303    Widget w;
2304    XtPointer ignored, unused;
2305{
2306    XtDestroyWidget( XtParent( w ) );
2307}
2308
2309Widget XmpCreateTableDialog( parent, name, arglist, argcount )
2310    Widget   parent;
2311    char*    name;
2312    ArgList  arglist;
2313    Cardinal argcount;
2314{
2315    int    i;
2316    Arg    shellArgs[2];
2317    Widget tableShell, table;
2318    char*  dsName;
2319
2320    /* Fabricate a name for the dialog shell using Motif 1.1 naming
2321    */
2322    dsName = (char*)XtCalloc( strlen(name)+DIALOG_SUFFIX_SIZE+1, sizeof(char) );
2323    strcpy( dsName, name ); strcat( dsName, DIALOG_SUFFIX );
2324
2325    /* Create a Motif Dialog Shell widget
2326    */
2327    i = 0;
2328    XtSetArg( shellArgs[i], XtNallowShellResize, True); i++;
2329    tableShell = XmCreateDialogShell( parent, dsName, shellArgs, i );
2330    XtFree( dsName );
2331
2332    /* Create the Table widget
2333    */
2334    table = XtCreateWidget( name, xmpTableWidgetClass, tableShell,
2335                            arglist, argcount );
2336    XtAddCallback( table, XtNdestroyCallback,
2337                   XmpDestroyParentCallback, (XtPointer)0);
2338
2339    return table;
2340}
2341
2342Widget XmpCreateTableTransient( parent, name, arglist, argcount )
2343    Widget   parent;
2344    char*    name;
2345    ArgList  arglist;
2346    Cardinal argcount;
2347{
2348    char*  dsName;
2349    int    i;
2350    Arg    shellArgs[2];
2351    Widget tableShell, table;
2352
2353    /* Fabricate a name for the dialog shell using Motif 1.1 naming
2354    */
2355    dsName = (char*)XtCalloc( strlen(name)+DIALOG_SUFFIX_SIZE+1, sizeof(char) );
2356    strcpy( dsName, name ); strcat( dsName, DIALOG_SUFFIX );
2357
2358    /* Create a Transient Shell widget
2359    */
2360    i = 0;
2361    XtSetArg( shellArgs[i], XtNallowShellResize, True); i++;
2362#ifdef XtTransientForBugIsFixed
2363#ifdef XtSpecificationRelease
2364    XtSetArg( shellArgs[i], XtNtransientFor, parent); i++;
2365#endif
2366#endif
2367    tableShell = XtCreatePopupShell( dsName, transientShellWidgetClass,
2368                                        parent, shellArgs, i );
2369    XtFree( dsName );
2370
2371    /* Create the Table widget
2372    */
2373    table = XtCreateWidget( name, xmpTableWidgetClass, tableShell,
2374                            arglist, argcount );
2375    XtManageChild( table );
2376    XtAddCallback( table, XtNdestroyCallback,
2377                   XmpDestroyParentCallback, (XtPointer)0);
2378
2379    return table;
2380}
Note: See TracBrowser for help on using the repository browser.