source: trunk/third/wcl/Xmp/TableVec.c @ 8837

Revision 8837, 16.2 KB checked in by ghudson, 28 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r8836, which included commits to RCS files with non-trunk default branches.
Line 
1/*LINTLIBRARY*/
2/*
3 * SCCS_data:    %Z% %M%        %I% %E% %U%
4 */
5
6#include <X11/Xmp/COPY.h>
7#include <X11/IntrinsicP.h>
8
9#ifdef sun
10#include <X11/ObjectP.h>        /* why don't they just use X from mit!?! */
11#include <X11/RectObjP.h>
12#endif
13
14#include <X11/Xmp/TableP.h>
15
16/* TableVector Methods
17**=====================**
18   Each Table instance has two TableVectors: one describes the columns, and the
19   other describes the rows.
20
21   The table vectors are created based on information in the real_layout
22   TableLoc array, hence they must be created after the real_layout, and they
23   must be updated when the real_layout changes.  The real_layout data upon
24   which the vectors depend is: number of cols, number of rows, options (only
25   TBL_SM_WIDTH and TBL_SM_HEIGHT).
26*/
27
28XmpTableVector XmpTableVectorNew( size, tw, do_col )
29    int                 size;
30    XmpTableWidget      tw;
31    int                 do_col;
32{
33    int                 minimize, dontStretch, first_slot, last_slot, slot;
34    XmpTableLoc         loc = tw->table.real_layout;
35    XmpTableVector      vec;
36
37    if ( (XmpTableLoc)0 == loc || 0 == size )
38        return (XmpTableVector)0;
39
40    vec = (XmpTableVector)XtCalloc( size+1, sizeof(XmpTableVectorRec));
41
42    /* Determine which slots need to be minimized
43    */
44    for (  ;  loc->w  ;  loc++ )
45    {
46        if ( do_col )
47        {
48            minimize    = loc->options & TBL_SM_WIDTH;
49            dontStretch = loc->options & TBL_LK_WIDTH;
50            first_slot  = loc->col;
51            last_slot   = loc->col + loc->col_span;
52        }
53        else
54        {
55            minimize    = loc->options & TBL_SM_HEIGHT;
56            dontStretch = loc->options & TBL_LK_HEIGHT;
57            first_slot  = loc->row;
58            last_slot   = loc->row + loc->row_span;
59        }
60
61        if ( minimize )
62            for ( slot = first_slot ; slot < last_slot ; slot++ )
63                vec[ slot ].options |= TBL_VEC_MINIMIZE;
64
65        if ( dontStretch )
66            for ( slot = first_slot ; slot < last_slot ; slot++ )
67                vec[ slot ].options |= TBL_VEC_LOCK;
68    }
69    XmpTableVectorMinimize( vec, size, tw, do_col );
70    return vec;
71}
72
73void XmpTableVectorFree( vec )
74    XmpTableVector vec ;
75{
76    XtFree( (char*)vec );
77}
78
79
80/* Minimize Column Widths and Row Heights
81**========================================**
82   Change the vector to be its minimum size in the direction indicated.
83   If TBL_VEC_MINIMIZE (i.e., TBL_SM_WIDTH (W) or TBL_SM_HEIGHT (H)) then
84   the widget is kept to its original size.
85   TBL_VEC_LOCK (i.e., TBL_LK_WIDTH (w) or TBL_LK_HEIGHT (h) ) not checked,
86   because such widgets DO grow to the minimum size of the column or row.
87*/
88void XmpTableVectorMinimize( vec, vec_len, tw, do_col )
89    XmpTableVector      vec;
90    int                 vec_len;
91    XmpTableWidget      tw;
92    int                 do_col;
93{
94    int         i;
95    XmpTableLoc loc                 = tw->table.real_layout;
96    Widget      resize_child        = tw->table.resize_child;
97    int         resize_width        = tw->table.resize_width;
98    int         resize_height       = tw->table.resize_height;
99    int         resize_border_width = tw->table.resize_border_width;
100
101    if ( (XmpTableVector)0 == vec || 0 == vec_len )
102        return;
103
104    /* Sort real_layout (in-place) by the number of columns or rows each child
105    ** spans so we first compute sizes of individual columns or rows, then
106    ** compute the spanned columns or rows.
107    */
108    if ( do_col )
109        qsort( (char*)loc,                      /* sort real_layout     */
110                XmpTableLocLen(loc),
111                sizeof(XmpTableLocRec),
112                XmpTableLocCompareColSpan );    /* compare column spans */
113    else
114        qsort( (char*)loc,                      /* sort real_layout     */
115                XmpTableLocLen(loc),
116                sizeof(XmpTableLocRec),
117                XmpTableLocCompareRowSpan );    /* compare row spans    */
118
119    /* Reset all width|heights to zero, then expand to fit
120    */
121    for ( i = 0 ; i < vec_len ; i++ )
122        vec[i].value = 0;
123
124    for (  ;  loc->w  ;  loc++ )
125    {
126        int pref;
127
128        /* Check for simple case (span of 1), where col or row just becomes
129        ** large enough for largest child in that col or row.
130        */
131        if (  do_col && loc->col_span == 1 )
132        {
133            pref = XmpTableLocPreferredWidth( loc, tw );
134
135            if ( pref > vec[ loc->col ].value )
136                vec[ loc->col ].value = pref;
137        }
138        else if ( !do_col && loc->row_span == 1 )
139        {
140            pref = XmpTableLocPreferredHeight( loc, tw );
141
142            if ( pref > vec[ loc->row ].value )
143                vec[ loc->row ].value = pref;
144        }
145
146        else
147        {
148            /* Spans multiple columns or rows.  We have already set each
149            ** column or row to the individual size requirements, now we can
150            ** see which spanned columns or rows need to be stretched.  The
151            ** span width includes inter-column or inter-row spacing.
152            */
153            int to_stretch, span_size, first_slot, stop_before, slot;
154            int can_stretch = 0;
155
156            if ( do_col )
157            {
158                span_size   = tw->table.col_spacing * (loc->col_span-1);
159                first_slot  = loc->col;
160                stop_before = loc->col + loc->col_span;
161            }
162            else
163            {
164                span_size   = tw->table.row_spacing * (loc->row_span-1);
165                first_slot  = loc->row;
166                stop_before = loc->row + loc->row_span;
167            }
168            for ( slot = first_slot  ;  slot < stop_before  ;  slot++ )
169            {
170                if ( 0 == (vec[ slot ].options & TBL_VEC_LOCK) )
171                    can_stretch++;
172                span_size += vec[ slot ].value;
173            }
174
175            /* If none of the slots can stretch, then we still must force
176            ** them all to stretch at least to the orig_size of this widget.
177            */
178            if ( 0 == can_stretch )
179            {
180                if ( do_col )
181                {
182                    to_stretch  = loc->col_span;
183                    if ( loc->w == resize_child )
184                        pref = resize_width + 2 * resize_border_width;
185                    else
186                        pref = loc->orig_width + 2 * loc->orig_border_width;
187                }
188                else
189                {
190                    to_stretch  = loc->row_span;
191                    if ( loc->w == resize_child )
192                        pref = resize_height + 2 * resize_border_width;
193                    else
194                        pref = loc->orig_height + 2 * loc->orig_border_width;
195                }
196            }
197            else
198            {
199                to_stretch = can_stretch;
200                if ( do_col )
201                {
202                    pref = XmpTableLocPreferredWidth( loc, tw );
203                }
204                else
205                {
206                    pref = XmpTableLocPreferredHeight( loc, tw );
207                }
208            }
209
210            if ( span_size < pref )
211            {
212                /* Increase size of some or all slots: if nothing
213                ** can stretch, expand every slot, else expand only
214                ** those which are not locked small.
215                */
216                int excess      = pref - span_size;
217                int amt         = excess / to_stretch;
218                int truncated   = excess - amt*to_stretch;
219
220                for ( slot = first_slot  ;  slot < stop_before  ;  slot++ )
221                {
222                    if ( 0 == can_stretch
223                      || 0 == (vec[ slot ].options & TBL_VEC_LOCK) )
224                    {
225                        if ( truncated )
226                        {
227                            vec[ slot ].value += amt + 1;
228                            --truncated;
229                        }
230                        else
231                            vec[ slot ].value += amt;
232                    }
233                }
234            }
235        }
236    }
237    /* The vector is minimized: set pref_value from value
238    */
239    for ( i = 0 ; i < vec_len ; i++ )
240        vec[i].pref_value = vec[i].value;
241}
242
243/* Total Width or Height
244**=======================**
245    Including inter-column and inter-row spacing, and margins.  Works
246    even when there are no columns or rows (vec==num==0).
247*/
248#define DO_ACTUAL 1
249#define DO_PREFERRED 0
250
251static int XmpTableVectorSize( vec, num, tw, do_col, do_actual )
252    XmpTableVector      vec;
253    int                 num;
254    XmpTableWidget      tw;
255    int                 do_col;
256    int                 do_actual;
257{
258    int slot, size, space;
259
260    size = 2*tw->manager.shadow_thickness;
261    if (do_col)
262    {
263        space = tw->table.col_spacing;
264        size += 2*tw->bulletin_board.margin_width;
265    }
266    else
267    {
268        space = tw->table.row_spacing;
269        size += 2*tw->bulletin_board.margin_height;
270    }
271
272    if ( 0 != num && (XmpTableVector)0 != vec )
273    {
274        if (do_actual)
275        {
276            for ( size -= space, slot = 0  ;  slot < num  ;  slot++ )
277                size += vec[ slot ].value + space;
278        }
279        else
280        {
281            for ( size -= space, slot = 0  ;  slot < num  ;  slot++ )
282                size += vec[ slot ].pref_value + space;
283        }
284    }
285
286    if ( size > 0 )
287        return size;
288    else
289        return 1;       /* minimum size */
290}
291
292int XmpTableVectorTotalSize( vec, num, tw, do_col )
293    XmpTableVector      vec;
294    int                 num;
295    XmpTableWidget      tw;
296    int                 do_col;
297{
298    return XmpTableVectorSize( vec, num, tw, do_col, DO_ACTUAL );
299}
300
301int XmpTableVectorPreferredSize( vec, num, tw, do_col )
302    XmpTableVector      vec;
303    int                 num;
304    XmpTableWidget      tw;
305    int                 do_col;
306{
307    return XmpTableVectorSize( vec, num, tw, do_col, DO_PREFERRED );
308}
309
310#undef DO_ACTUAL
311#undef DO_PREFERRED
312
313
314/* Adjust rows or columns
315**========================**
316   When a parent re-sizes a Table, it can make it larger or smaller.  The
317   adjustment is distributed as a ratio of the preferred sizes of the
318   col/row, so small ones change slower than larger ones.
319
320   If the table wants to restrict making things smaller than preferred,
321   then it must simply never respond to resize commands which make
322   the table smaller than its preferred size.  Nowhere in the logic below
323   is there any mechanism which prevents things from shrinking smaller
324   than the preferred size.  There is, however, mechanisms to prevent any
325   col or row from becoming smaller than 1.
326
327   If resize makes the Table larger than before:  First, all col/row
328   smaller that preferred size are stretched up until their preferred
329   size.  The rest of the change is distributed evenly to un-locked col/row,
330   but if all are locked, then all are stretched.
331
332   If the table is being made smaller, then the inverse is applied: all
333   unlocked (or all if all are locked) are made smaller down to their
334   preferred sizes, then all are made smaller by the same amount.
335
336   Adjustments to the vectors are made on a relative basis.  Big slots
337   change more than small slots.  Therefore, the adjustment delta is
338   computed for each slot which might change.
339
340   While adjusting the vectors, there are too things to watch out for: lots of
341   slots to change, yet not much change, integer truncation then leaves the
342   delta zero.  In this case we make the delta 1, which means the change gets
343   used up before all the slots are seen.  We use the same algorithm for
344   growing and shrinking, so there should be no perceivable problems.  The
345   second problem is when the delta would consume too much change, again due
346   to integer truncation.  In this case, we must simply make the delta equal
347   to the remaining change.
348
349   Notice that there are two resize algorithms used: one applies when everything
350   is smaller than preferred, and another applies when everything is bigger.
351   When smaller, everything gets changed relatively.  When larger, change
352   is influenced by the table slot being locked (TBL_VEC_LOCK).  Slots which
353   are locked are not adjusted unless ALL slots are locked, then all are
354   adjusted relatively.
355*/
356
357void XmpTableVectorAdjust( vec, vec_len, change )
358    XmpTableVector vec;
359    int            vec_len, change;
360{
361    int vec_inx, remaining, amt;
362    int total_pref;
363    int can_change, can_change_pref;
364    int too_small;
365    int too_big, too_big_pref;
366
367    if ( (XmpTableVector)0 == vec || 0 == vec_len || 0 == change )
368        return;
369
370    total_pref = can_change = can_change_pref = 0;
371    too_small  = too_big    = too_big_pref    = 0;
372
373    for ( vec_inx=0  ;  vec_inx < vec_len  ;  vec_inx++ )
374    {
375        /* NOTE: total_pref can be zero if all pref_value are 0!
376        */
377        total_pref += vec[ vec_inx ].pref_value;
378        if ( change > 0 )
379        {
380            if ( 0 == ( vec[ vec_inx ].options & TBL_VEC_LOCK ) )
381            {
382                /* NOTE: can_change_pref can be zero if all pref_value are 0!
383                */
384                can_change++;
385                can_change_pref += vec[ vec_inx ].pref_value;
386            }
387            if ( vec[ vec_inx ].value < vec[ vec_inx ].pref_value )
388            {
389                too_small++;
390            }
391        }
392        else
393        {
394            if ( vec[ vec_inx ].value > vec[ vec_inx ].pref_value )
395            {
396                /* NOTE: too_big_pref can be zero if all pref_value are 0!
397                */
398                too_big++;
399                too_big_pref += vec[ vec_inx ].pref_value;
400            }
401        }
402    }
403
404    if ( change > 0 )
405    {
406        /**************** Make columns wider or rows taller ***************
407        */
408        int still_too_small;
409        remaining = change;
410        do
411        {
412            /* Expand everything smaller than preferred up to preferred
413            */
414            still_too_small = 0;
415
416            for ( vec_inx=0  ;  vec_inx < vec_len  ;  vec_inx++ )
417            {
418                if ( vec[ vec_inx ].value < vec[ vec_inx ].pref_value )
419                {
420                    /* Make this one bigger, up to preferred size
421                    */
422                    if ( 0 == total_pref )
423                        amt = change / (too_small?too_small:vec_len);
424                    else
425                        amt = change * vec[ vec_inx ].pref_value / total_pref;
426                    if ( 0 == amt )
427                        amt = 1;
428                    else if ( remaining < amt )
429                        amt = remaining;
430
431                    if (  vec[vec_inx].value + amt
432                        < vec[vec_inx].pref_value )
433                    {
434                        vec[ vec_inx ].value += amt;
435                        ++still_too_small;
436                    }
437                    else
438                    {
439                        amt = vec[vec_inx].pref_value - vec[vec_inx].value;
440                        vec[vec_inx].value = vec[vec_inx].pref_value;
441                    }
442                    remaining -= amt;
443
444                    if ( remaining <= 0 )
445                        return; /* used up all change */
446                }
447            }
448
449            change = remaining;
450        }
451        while ( still_too_small );
452
453        /* All are at least preferred size, and there is change remaining.
454         * If none of the vector slots can stretch, then we still must
455         * force them all to stretch.
456         */
457        if ( 0 == can_change )
458            can_change_pref = total_pref;       /* maintain relative sizes */
459
460        do
461        {
462            for ( vec_inx = 0  ;  vec_inx < vec_len  ;  vec_inx++ )
463            {
464                if ( 0 == can_change
465                  || 0 == ( vec[ vec_inx ].options & TBL_VEC_LOCK ) )
466                {
467                    /* Add relative amount to all which can change.
468                    */
469                    if ( 0 == can_change_pref )
470                        amt = change / (can_change?can_change:vec_len);
471                    else
472                        amt = change * vec[vec_inx].pref_value/can_change_pref;
473                    if ( 0 == amt )
474                        amt = 1;
475                    else if ( remaining < amt )
476                        amt = remaining;
477
478                    vec[ vec_inx ].value += amt;
479                    remaining -= amt;
480
481                    if ( remaining <= 0 )
482                        return; /* used up all change */
483                }
484            }
485
486            /* We have gone thru vector, adding space, but due to truncation
487             * there may still be more change to distribute.
488             */
489            change = remaining;
490        }
491        while ( 1 ); /* until remaining goes to zero or negative above */
492        /*NOTREACHED*/
493    }
494    else /*  (change < 0)  */
495    {
496        /**************** Make columns narrower or rows shorter ***************
497        */
498        int still_too_big, num_larger_than_1;
499
500        /* For conceptual clarity, switch the sign on change
501        */
502        change = -change;
503        remaining = change;
504
505        still_too_big = too_big;
506        while ( still_too_big )
507        {
508            /* Shrink all which are larger than preferred
509            */
510            still_too_big = 0;
511            for ( vec_inx = 0  ;  vec_inx < vec_len  ;  vec_inx++ )
512            {
513                if ( vec[ vec_inx ].value > vec[ vec_inx ].pref_value )
514                {
515                    if ( 0 == too_big_pref )
516                        amt = change / (too_big?too_big:vec_len);
517                    else
518                        amt = change * vec[vec_inx].pref_value / too_big_pref;
519                    if ( 0 == amt )
520                        amt = 1;
521                    else if ( remaining < amt )
522                        amt = remaining;
523
524                    if ( vec[ vec_inx ].value - amt < vec[ vec_inx ].pref_value)
525                    {
526                        amt = vec[ vec_inx ].value - vec[ vec_inx ].pref_value;
527                        vec[ vec_inx ].value = vec[ vec_inx ].pref_value;
528                    }
529                    else
530                    {
531                        vec[ vec_inx ].value -= amt;
532                        still_too_big++;
533                    }
534
535                    remaining -= amt;
536
537                    if ( remaining <= 0 )
538                        return; /* used up all change */
539                }
540            }
541
542            /* We have made a pass through all slots
543            */
544            change = remaining;
545        }
546        /* Now all stretchable are preferred sizes, or all were already smaller
547         * than preferred sizes, yet more change is remaining to be absorbed.
548         *
549         * Shrink evenly, but since none can become smaller than 1, we may need
550         * to make multiple passes over vector until total change is absorbed,
551         * or all are of size 1.
552         */
553        do
554        {
555            num_larger_than_1 = 0;
556
557            for ( vec_inx = 0  ;  vec_inx < vec_len  ;  vec_inx++ )
558            {
559                if ( 0 == total_pref )
560                    amt = change / vec_len;
561                else
562                    amt = change * vec[vec_inx].pref_value / total_pref;
563                if ( 0 == amt )
564                    amt = 1;
565                else if ( remaining < amt )
566                    amt = remaining;
567
568                if ( amt < vec[vec_inx].value )
569                {
570                    vec[vec_inx].value -= amt;
571                    ++num_larger_than_1;
572                }
573                else
574                {
575                    amt = vec[vec_inx].value - 1;
576                    vec[vec_inx].value = 1;
577                }
578                remaining -= amt;
579                if ( remaining <= 0 )
580                    return; /* used up all change */
581            }
582
583            /* We have made a pass through all slots
584            */
585            change = remaining;
586        }
587        while ( num_larger_than_1 );
588        return; /* all are shrunk to absolute minimum size (1) */
589    }
590    /*NOTREACHED*/
591}
592
593/* Set Upper Left Corner Coordinates of Each Cell
594**================================================**
595   Note that it is not worth doing this until the actual correct size of
596   the rows and columns have been computed.
597*/
598
599void XmpTableVectorComputeOffsets( vec, vec_len, margin, gap )
600    XmpTableVector vec;
601    int            vec_len, margin, gap;
602{
603    int i;
604    int offset = margin;
605
606    if ( (XmpTableVector)0 == vec || 0 == vec_len )
607        return;
608
609    for ( i = 0  ;  i < vec_len  ;  i++ )
610    {
611        vec[i].offset = offset;
612        offset = offset + vec[i].value + gap;
613    }
614}
Note: See TracBrowser for help on using the repository browser.