OSDN Git Service

Please enter the commit message for your changes. Lines starting
[eos/base.git] / util / src / TclTk / tk8.6.12 / unix / tkUnixButton.c
1 /*
2  * tkUnixButton.c --
3  *
4  *      This file implements the Unix specific portion of the button widgets.
5  *
6  * Copyright (c) 1996-1997 by Sun Microsystems, Inc.
7  *
8  * See the file "license.terms" for information on usage and redistribution of
9  * this file, and for a DISCLAIMER OF ALL WARRANTIES.
10  */
11
12 #include "tkInt.h"
13 #include "tkButton.h"
14 #include "tk3d.h"
15
16 /*
17  * Shared with menu widget.
18  */
19
20 MODULE_SCOPE void       TkpDrawCheckIndicator(Tk_Window tkwin,
21                             Display *display, Drawable d, int x, int y,
22                             Tk_3DBorder bgBorder, XColor *indicatorColor,
23                             XColor *selectColor, XColor *disColor, int on,
24                             int disabled, int mode);
25
26 /*
27  * Declaration of Unix specific button structure.
28  */
29
30 typedef struct UnixButton {
31     TkButton info;              /* Generic button info. */
32 } UnixButton;
33
34 /*
35  * The class function table for the button widgets.
36  */
37
38 const Tk_ClassProcs tkpButtonProcs = {
39     sizeof(Tk_ClassProcs),      /* size */
40     TkButtonWorldChanged,       /* worldChangedProc */
41     NULL,                                       /* createProc */
42     NULL                                        /* modalProc */
43 };
44
45 /*
46  * The button image.
47  * The header info here is ignored, it's the image that's important. The
48  * colors will be applied as follows:
49  *   A = Background
50  *   B = Background
51  *   C = 3D light
52  *   D = selectColor
53  *   E = 3D dark
54  *   F = Background
55  *   G = Indicator Color
56  *   H = disabled Indicator Color
57  */
58
59 /* XPM */
60 static const char *const button_images[] = {
61     /* width height ncolors chars_per_pixel */
62     "52 26 7 1",
63     /* colors */
64     "A c #808000000000",
65     "B c #000080800000",
66     "C c #808080800000",
67     "D c #000000008080",
68     "E c #808000008080",
69     "F c #000080808080",
70     "G c #000000000000",
71     "H c #000080800000",
72     /* pixels */
73     "AAAAAAAAAAAABAAAAAAAAAAAABAAAAAAAAAAAABAAAAAAAAAAAAB",
74     "AEEEEEEEEEECBAEEEEEEEEEECBAEEEEEEEEEECBAEEEEEEEEEECB",
75     "AEDDDDDDDDDCBAEDDDDDDDDDCBAEFFFFFFFFFCBAEFFFFFFFFFCB",
76     "AEDDDDDDDDDCBAEDDDDDDDGDCBAEFFFFFFFFFCBAEFFFFFFFHFCB",
77     "AEDDDDDDDDDCBAEDDDDDDGGDCBAEFFFFFFFFFCBAEFFFFFFHHFCB",
78     "AEDDDDDDDDDCBAEDGDDDGGGDCBAEFFFFFFFFFCBAEFHFFFHHHFCB",
79     "AEDDDDDDDDDCBAEDGGDGGGDDCBAEFFFFFFFFFCBAEFHHFHHHFFCB",
80     "AEDDDDDDDDDCBAEDGGGGGDDDCBAEFFFFFFFFFCBAEFHHHHHFFFCB",
81     "AEDDDDDDDDDCBAEDDGGGDDDDCBAEFFFFFFFFFCBAEFFHHHFFFFCB",
82     "AEDDDDDDDDDCBAEDDDGDDDDDCBAEFFFFFFFFFCBAEFFFHFFFFFCB",
83     "AEDDDDDDDDDCBAEDDDDDDDDDCBAEFFFFFFFFFCBAEFFFFFFFFFCB",
84     "ACCCCCCCCCCCBACCCCCCCCCCCBACCCCCCCCCCCBACCCCCCCCCCCB",
85     "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
86     "FFFFAAAAFFFFFFFFFAAAAFFFFFFFFFAAAAFFFFFFFFFAAAAFFFFF",
87     "FFAAEEEEAAFFFFFAAEEEEAAFFFFFAAEEEEAAFFFFFAAEEEEAAFFF",
88     "FAEEDDDDEEBFFFAEEDDDDEEBFFFAEEFFFFEEBFFFAEEFFFFEEBFF",
89     "FAEDDDDDDCBFFFAEDDDDDDCBFFFAEFFFFFFCBFFFAEFFFFFFCBFF",
90     "AEDDDDDDDDCBFAEDDDGGDDDCBFAEFFFFFFFFCBFAEFFFHHFFFCBF",
91     "AEDDDDDDDDCBFAEDDGGGGDDCBFAEFFFFFFFFCBFAEFFHHHHFFCBF",
92     "AEDDDDDDDDCBFAEDDGGGGDDCBFAEFFFFFFFFCBFAEFFHHHHFFCBF",
93     "AEDDDDDDDDCBFAEDDDGGDDDCBFAEFFFFFFFFCBFAEFFFHHFFFCBF",
94     "FAEDDDDDDCBFFFAEDDDDDDCBFFFAEFFFFFFCBFFFAEFFFFFFCBFF",
95     "FACCDDDDCCBFFFACCDDDDCCBFFFACCFFFFCCBFFFACCFFFFCCBFF",
96     "FFBBCCCCBBFFFFFBBCCCCBBFFFFFBBCCCCBBFFFFFBBCCCCBBFFF",
97     "FFFFBBBBFFFFFFFFFBBBBFFFFFFFFFBBBBFFFFFFFFFBBBBFFFFF",
98     "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
99 };
100
101 /*
102  * Sizes and offsets into above XPM file.
103  */
104
105 #define CHECK_BUTTON_DIM    13
106 #define CHECK_MENU_DIM       9
107 #define CHECK_START          9
108 #define CHECK_ON_OFFSET     13
109 #define CHECK_OFF_OFFSET     0
110 #define CHECK_DISON_OFFSET  39
111 #define CHECK_DISOFF_OFFSET 26
112 #define RADIO_BUTTON_DIM    12
113 #define RADIO_MENU_DIM       6
114 #define RADIO_WIDTH         13
115 #define RADIO_START         22
116 #define RADIO_ON_OFFSET     13
117 #define RADIO_OFF_OFFSET     0
118 #define RADIO_DISON_OFFSET  39
119 #define RADIO_DISOFF_OFFSET 26
120
121 /*
122  * Indicator Draw Modes
123  */
124
125 #define CHECK_BUTTON 0
126 #define CHECK_MENU   1
127 #define RADIO_BUTTON 2
128 #define RADIO_MENU   3
129 \f
130 /*
131  *----------------------------------------------------------------------
132  *
133  * TkpDrawCheckIndicator -
134  *
135  *      Draws the checkbox image in the drawable at the (x,y) location, value,
136  *      and state given. This routine is use by the button and menu widgets
137  *
138  * Results:
139  *      None.
140  *
141  * Side effects:
142  *      An image is drawn in the drawable at the location given.
143  *
144  *----------------------------------------------------------------------
145  */
146
147 void
148 TkpDrawCheckIndicator(
149     Tk_Window tkwin,            /* handle for resource alloc */
150     Display *display,
151     Drawable d,                 /* what to draw on */
152     int x, int y,               /* where to draw */
153     Tk_3DBorder bgBorder,       /* colors of the border */
154     XColor *indicatorColor,     /* color of the indicator */
155     XColor *selectColor,        /* color when selected */
156     XColor *disableColor,       /* color when disabled */
157     int on,                     /* are we on? */
158     int disabled,               /* are we disabled? */
159     int mode)                   /* kind of indicator to draw */
160 {
161     int ix, iy;
162     int dim;
163     int imgsel, imgstart;
164     TkBorder *bg_brdr = (TkBorder*)bgBorder;
165     XGCValues gcValues;
166     GC copyGC;
167     unsigned long imgColors[8];
168     XImage *img;
169     Pixmap pixmap;
170     int depth;
171
172     /*
173      * Sanity check.
174      */
175
176     if (tkwin == NULL || display == NULL || d == None || bgBorder == NULL
177             || indicatorColor == NULL) {
178         return;
179     }
180
181     if (disableColor == NULL) {
182         disableColor = bg_brdr->bgColorPtr;
183     }
184
185     if (selectColor == NULL) {
186         selectColor = bg_brdr->bgColorPtr;
187     }
188
189     depth = Tk_Depth(tkwin);
190
191     /*
192      * Compute starting point and dimensions of image inside button_images to
193      * be used.
194      */
195
196     switch (mode) {
197     default:
198     case CHECK_BUTTON:
199         imgsel = on == 2 ? CHECK_DISON_OFFSET :
200                 on == 1 ? CHECK_ON_OFFSET : CHECK_OFF_OFFSET;
201         imgsel += disabled && on != 2 ? CHECK_DISOFF_OFFSET : 0;
202         imgstart = CHECK_START;
203         dim = CHECK_BUTTON_DIM;
204         break;
205
206     case CHECK_MENU:
207         imgsel = on == 2 ? CHECK_DISOFF_OFFSET :
208                 on == 1 ? CHECK_ON_OFFSET : CHECK_OFF_OFFSET;
209         imgsel += disabled && on != 2 ? CHECK_DISOFF_OFFSET : 0;
210         imgstart = CHECK_START + 2;
211         imgsel += 2;
212         dim = CHECK_MENU_DIM;
213         break;
214
215     case RADIO_BUTTON:
216         imgsel = on == 2 ? RADIO_DISON_OFFSET :
217                 on==1 ? RADIO_ON_OFFSET : RADIO_OFF_OFFSET;
218         imgsel += disabled && on != 2 ? RADIO_DISOFF_OFFSET : 0;
219         imgstart = RADIO_START;
220         dim = RADIO_BUTTON_DIM;
221         break;
222
223     case RADIO_MENU:
224         imgsel = on == 2 ? RADIO_DISOFF_OFFSET :
225                 on==1 ? RADIO_ON_OFFSET : RADIO_OFF_OFFSET;
226         imgsel += disabled && on != 2 ? RADIO_DISOFF_OFFSET : 0;
227         imgstart = RADIO_START + 3;
228         imgsel += 3;
229         dim = RADIO_MENU_DIM;
230         break;
231     }
232
233     /*
234      * Allocate the drawing areas to use. Note that we use double-buffering
235      * here because not all code paths leading to this function do so.
236      */
237
238     pixmap = Tk_GetPixmap(display, d, dim, dim, depth);
239     if (pixmap == None) {
240         return;
241     }
242
243     x -= dim/2;
244     y -= dim/2;
245
246     img = XGetImage(display, pixmap, 0, 0,
247             (unsigned int)dim, (unsigned int)dim, AllPlanes, ZPixmap);
248     if (img == NULL) {
249         return;
250     }
251
252     /*
253      * Set up the color mapping table.
254      */
255
256     TkpGetShadows(bg_brdr, tkwin);
257
258     imgColors[0 /*A*/] =
259             Tk_GetColorByValue(tkwin, bg_brdr->bgColorPtr)->pixel;
260     imgColors[1 /*B*/] =
261             Tk_GetColorByValue(tkwin, bg_brdr->bgColorPtr)->pixel;
262     imgColors[2 /*C*/] = (bg_brdr->lightColorPtr != NULL) ?
263             Tk_GetColorByValue(tkwin, bg_brdr->lightColorPtr)->pixel :
264             WhitePixelOfScreen(bg_brdr->screen);
265     imgColors[3 /*D*/] =
266             Tk_GetColorByValue(tkwin, selectColor)->pixel;
267     imgColors[4 /*E*/] = (bg_brdr->darkColorPtr != NULL) ?
268             Tk_GetColorByValue(tkwin, bg_brdr->darkColorPtr)->pixel :
269             BlackPixelOfScreen(bg_brdr->screen);
270     imgColors[5 /*F*/] =
271             Tk_GetColorByValue(tkwin, bg_brdr->bgColorPtr)->pixel;
272     imgColors[6 /*G*/] =
273             Tk_GetColorByValue(tkwin, indicatorColor)->pixel;
274     imgColors[7 /*H*/] =
275             Tk_GetColorByValue(tkwin, disableColor)->pixel;
276
277     /*
278      * Create the image, painting it into an XImage one pixel at a time.
279      */
280
281     for (iy=0 ; iy<dim ; iy++) {
282         for (ix=0 ; ix<dim ; ix++) {
283             XPutPixel(img, ix, iy,
284                     imgColors[button_images[imgstart+iy][imgsel+ix] - 'A']);
285         }
286     }
287
288     /*
289      * Copy onto our target drawable surface.
290      */
291
292     memset(&gcValues, 0, sizeof(gcValues));
293     gcValues.background = bg_brdr->bgColorPtr->pixel;
294     gcValues.graphics_exposures = False;
295     copyGC = Tk_GetGC(tkwin, 0, &gcValues);
296
297     XPutImage(display, pixmap, copyGC, img, 0, 0, 0, 0,
298             (unsigned)dim, (unsigned)dim);
299     XCopyArea(display, pixmap, d, copyGC, 0, 0,
300             (unsigned)dim, (unsigned)dim, x, y);
301
302     /*
303      * Tidy up.
304      */
305
306     Tk_FreeGC(display, copyGC);
307     XDestroyImage(img);
308     Tk_FreePixmap(display, pixmap);
309 }
310 \f
311 /*
312  *----------------------------------------------------------------------
313  *
314  * TkpCreateButton --
315  *
316  *      Allocate a new TkButton structure.
317  *
318  * Results:
319  *      Returns a newly allocated TkButton structure.
320  *
321  * Side effects:
322  *      Registers an event handler for the widget.
323  *
324  *----------------------------------------------------------------------
325  */
326
327 TkButton *
328 TkpCreateButton(
329     TCL_UNUSED(Tk_Window))
330 {
331     return (TkButton *)ckalloc(sizeof(UnixButton));
332 }
333 \f
334 /*
335  *----------------------------------------------------------------------
336  *
337  * TkpDisplayButton --
338  *
339  *      This function is invoked to display a button widget. It is normally
340  *      invoked as an idle handler.
341  *
342  * Results:
343  *      None.
344  *
345  * Side effects:
346  *      Commands are output to X to display the button in its current mode.
347  *      The REDRAW_PENDING flag is cleared.
348  *
349  *----------------------------------------------------------------------
350  */
351
352 static void
353 ShiftByOffset(
354     TkButton *butPtr,
355     int relief,
356     int *x,             /* shift this x coordinate */
357     int *y,             /* shift this y coordinate */
358     int width,          /* width of image/text */
359     int height)         /* height of image/text */
360 {
361     if (relief != TK_RELIEF_RAISED
362             && butPtr->type == TYPE_BUTTON
363             && !Tk_StrictMotif(butPtr->tkwin)) {
364         int shiftX;
365         int shiftY;
366
367         /*
368          * This is an (unraised) button widget, so we offset the text to make
369          * the button appear to move up and down as the relief changes.
370          */
371
372         shiftX = shiftY = (relief == TK_RELIEF_SUNKEN) ? 2 : 1;
373
374         if (relief != TK_RELIEF_RIDGE) {
375             /*
376              * Take back one pixel if the padding is even, otherwise the
377              * content will be displayed too far right/down.
378              */
379
380             if ((Tk_Width(butPtr->tkwin) - width) % 2 == 0) {
381                 shiftX -= 1;
382             }
383             if ((Tk_Height(butPtr->tkwin) - height) % 2 == 0) {
384                 shiftY -= 1;
385             }
386         }
387
388         *x += shiftX;
389         *y += shiftY;
390     }
391 }
392
393 void
394 TkpDisplayButton(
395     ClientData clientData)      /* Information about widget. */
396 {
397     TkButton *butPtr = (TkButton *)clientData;
398     GC gc;
399     Tk_3DBorder border;
400     Pixmap pixmap;
401     int x = 0;                  /* Initialization only needed to stop compiler
402                                  * warning. */
403     int y, relief;
404     Tk_Window tkwin = butPtr->tkwin;
405     int width = 0, height = 0, fullWidth, fullHeight;
406     int textXOffset, textYOffset;
407     int haveImage = 0, haveText = 0;
408     int imageWidth, imageHeight;
409     int imageXOffset = 0, imageYOffset = 0;
410                                 /* image information that will be used to
411                                  * restrict disabled pixmap as well */
412
413     butPtr->flags &= ~REDRAW_PENDING;
414     if ((butPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
415         return;
416     }
417
418     border = butPtr->normalBorder;
419     if ((butPtr->state == STATE_DISABLED) && (butPtr->disabledFg != NULL)) {
420         gc = butPtr->disabledGC;
421     } else if ((butPtr->state == STATE_ACTIVE)
422             && !Tk_StrictMotif(butPtr->tkwin)) {
423         gc = butPtr->activeTextGC;
424         border = butPtr->activeBorder;
425     } else {
426         gc = butPtr->normalTextGC;
427     }
428     if ((butPtr->flags & SELECTED) && (butPtr->selectBorder != NULL)
429             && !butPtr->indicatorOn) {
430         border = butPtr->selectBorder;
431     }
432
433     /*
434      * Override the relief specified for the button if this is a checkbutton
435      * or radiobutton and there's no indicator. The new relief is as follows:
436      *      If the button is select  --> "sunken"
437      *      If relief==overrelief    --> relief
438      *      Otherwise                --> overrelief
439      *
440      * The effect we are trying to achieve is as follows:
441      *
442      *      value    mouse-over?   -->   relief
443      *     -------  ------------        --------
444      *       off        no               flat
445      *       off        yes              raised
446      *       on         no               sunken
447      *       on         yes              sunken
448      *
449      * This is accomplished by configuring the checkbutton or radiobutton like
450      * this:
451      *
452      *     -indicatoron 0 -overrelief raised -offrelief flat
453      *
454      * Bindings (see library/button.tcl) will copy the -overrelief into
455      * -relief on mouseover. Hence, we can tell if we are in mouse-over by
456      * comparing relief against overRelief. This is an aweful kludge, but it
457      * gives use the desired behavior while keeping the code backwards
458      * compatible.
459      */
460
461     relief = butPtr->relief;
462     if ((butPtr->type >= TYPE_CHECK_BUTTON) && !butPtr->indicatorOn) {
463         if (butPtr->flags & SELECTED) {
464             relief = TK_RELIEF_SUNKEN;
465         } else if (butPtr->overRelief != relief) {
466             relief = butPtr->offRelief;
467         }
468     }
469
470     /*
471      * In order to avoid screen flashes, this function redraws the button in a
472      * pixmap, then copies the pixmap to the screen in a single operation.
473      * This means that there's no point in time where the on-screen image has
474      * been cleared.
475      */
476
477     pixmap = Tk_GetPixmap(butPtr->display, Tk_WindowId(tkwin),
478             Tk_Width(tkwin), Tk_Height(tkwin), Tk_Depth(tkwin));
479     Tk_Fill3DRectangle(tkwin, pixmap, border, 0, 0, Tk_Width(tkwin),
480             Tk_Height(tkwin), 0, TK_RELIEF_FLAT);
481
482     /*
483      * Display image or bitmap or text for button.
484      */
485
486     if (butPtr->image != NULL) {
487         Tk_SizeOfImage(butPtr->image, &width, &height);
488         haveImage = 1;
489     } else if (butPtr->bitmap != None) {
490         Tk_SizeOfBitmap(butPtr->display, butPtr->bitmap, &width, &height);
491         haveImage = 1;
492     }
493     imageWidth = width;
494     imageHeight = height;
495
496     haveText = (butPtr->textWidth != 0 && butPtr->textHeight != 0);
497
498     if (butPtr->compound != COMPOUND_NONE && haveImage && haveText) {
499         textXOffset = 0;
500         textYOffset = 0;
501         fullWidth = 0;
502         fullHeight = 0;
503
504         switch ((enum compound) butPtr->compound) {
505         case COMPOUND_TOP:
506         case COMPOUND_BOTTOM:
507             /*
508              * Image is above or below text.
509              */
510
511             if (butPtr->compound == COMPOUND_TOP) {
512                 textYOffset = height + butPtr->padY;
513             } else {
514                 imageYOffset = butPtr->textHeight + butPtr->padY;
515             }
516             fullHeight = height + butPtr->textHeight + butPtr->padY;
517             fullWidth = (width > butPtr->textWidth ? width :
518                     butPtr->textWidth);
519             textXOffset = (fullWidth - butPtr->textWidth)/2;
520             imageXOffset = (fullWidth - width)/2;
521             break;
522         case COMPOUND_LEFT:
523         case COMPOUND_RIGHT:
524             /*
525              * Image is left or right of text.
526              */
527
528             if (butPtr->compound == COMPOUND_LEFT) {
529                 textXOffset = width + butPtr->padX;
530             } else {
531                 imageXOffset = butPtr->textWidth + butPtr->padX;
532             }
533             fullWidth = butPtr->textWidth + butPtr->padX + width;
534             fullHeight = (height > butPtr->textHeight ? height :
535                     butPtr->textHeight);
536             textYOffset = (fullHeight - butPtr->textHeight)/2;
537             imageYOffset = (fullHeight - height)/2;
538             break;
539         case COMPOUND_CENTER:
540             /*
541              * Image and text are superimposed.
542              */
543
544             fullWidth = (width > butPtr->textWidth ? width :
545                     butPtr->textWidth);
546             fullHeight = (height > butPtr->textHeight ? height :
547                     butPtr->textHeight);
548             textXOffset = (fullWidth - butPtr->textWidth)/2;
549             imageXOffset = (fullWidth - width)/2;
550             textYOffset = (fullHeight - butPtr->textHeight)/2;
551             imageYOffset = (fullHeight - height)/2;
552             break;
553         case COMPOUND_NONE:
554             break;
555         }
556
557         TkComputeAnchor(butPtr->anchor, tkwin, butPtr->padX, butPtr->padY,
558                 butPtr->indicatorSpace + fullWidth, fullHeight, &x, &y);
559
560         x += butPtr->indicatorSpace;
561         ShiftByOffset(butPtr, relief, &x, &y, width, height);
562         imageXOffset += x;
563         imageYOffset += y;
564
565         if (butPtr->image != NULL) {
566             /*
567              * Do boundary clipping, so that Tk_RedrawImage is passed valid
568              * coordinates. [Bug 979239]
569              */
570
571             if (imageXOffset < 0) {
572                 imageXOffset = 0;
573             }
574             if (imageYOffset < 0) {
575                 imageYOffset = 0;
576             }
577             if (width > Tk_Width(tkwin)) {
578                 width = Tk_Width(tkwin);
579             }
580             if (height > Tk_Height(tkwin)) {
581                 height = Tk_Height(tkwin);
582             }
583             if ((width + imageXOffset) > Tk_Width(tkwin)) {
584                 imageXOffset = Tk_Width(tkwin) - width;
585             }
586             if ((height + imageYOffset) > Tk_Height(tkwin)) {
587                 imageYOffset = Tk_Height(tkwin) - height;
588             }
589
590             if ((butPtr->selectImage != NULL) && (butPtr->flags & SELECTED)) {
591                 Tk_RedrawImage(butPtr->selectImage, 0, 0,
592                         width, height, pixmap, imageXOffset, imageYOffset);
593             } else if ((butPtr->tristateImage != NULL) && (butPtr->flags & TRISTATED)) {
594                 Tk_RedrawImage(butPtr->tristateImage, 0, 0,
595                         width, height, pixmap, imageXOffset, imageYOffset);
596             } else {
597                 Tk_RedrawImage(butPtr->image, 0, 0, width,
598                         height, pixmap, imageXOffset, imageYOffset);
599             }
600         } else {
601             XSetClipOrigin(butPtr->display, gc, imageXOffset, imageYOffset);
602             XCopyPlane(butPtr->display, butPtr->bitmap, pixmap, gc,
603                     0, 0, (unsigned int) width, (unsigned int) height,
604                     imageXOffset, imageYOffset, 1);
605             XSetClipOrigin(butPtr->display, gc, 0, 0);
606         }
607
608         Tk_DrawTextLayout(butPtr->display, pixmap, gc,
609                 butPtr->textLayout, x + textXOffset, y + textYOffset, 0, -1);
610         Tk_UnderlineTextLayout(butPtr->display, pixmap, gc,
611                 butPtr->textLayout, x + textXOffset, y + textYOffset,
612                 butPtr->underline);
613         y += fullHeight/2;
614     } else {
615         if (haveImage) {
616             TkComputeAnchor(butPtr->anchor, tkwin, 0, 0,
617                     butPtr->indicatorSpace + width, height, &x, &y);
618             x += butPtr->indicatorSpace;
619             ShiftByOffset(butPtr, relief, &x, &y, width, height);
620             imageXOffset += x;
621             imageYOffset += y;
622             if (butPtr->image != NULL) {
623                 /*
624                  * Do boundary clipping, so that Tk_RedrawImage is passed
625                  * valid coordinates. [Bug 979239]
626                  */
627
628                 if (imageXOffset < 0) {
629                     imageXOffset = 0;
630                 }
631                 if (imageYOffset < 0) {
632                     imageYOffset = 0;
633                 }
634                 if (width > Tk_Width(tkwin)) {
635                     width = Tk_Width(tkwin);
636                 }
637                 if (height > Tk_Height(tkwin)) {
638                     height = Tk_Height(tkwin);
639                 }
640                 if ((width + imageXOffset) > Tk_Width(tkwin)) {
641                     imageXOffset = Tk_Width(tkwin) - width;
642                 }
643                 if ((height + imageYOffset) > Tk_Height(tkwin)) {
644                     imageYOffset = Tk_Height(tkwin) - height;
645                 }
646
647                 if ((butPtr->selectImage != NULL) &&
648                         (butPtr->flags & SELECTED)) {
649                     Tk_RedrawImage(butPtr->selectImage, 0, 0, width,
650                             height, pixmap, imageXOffset, imageYOffset);
651                 } else if ((butPtr->tristateImage != NULL) &&
652                         (butPtr->flags & TRISTATED)) {
653                     Tk_RedrawImage(butPtr->tristateImage, 0, 0, width,
654                             height, pixmap, imageXOffset, imageYOffset);
655                 } else {
656                     Tk_RedrawImage(butPtr->image, 0, 0, width, height, pixmap,
657                             imageXOffset, imageYOffset);
658                 }
659             } else {
660                 XSetClipOrigin(butPtr->display, gc, x, y);
661                 XCopyPlane(butPtr->display, butPtr->bitmap, pixmap, gc, 0, 0,
662                         (unsigned int) width, (unsigned int) height, x, y, 1);
663                 XSetClipOrigin(butPtr->display, gc, 0, 0);
664             }
665             y += height/2;
666         } else {
667             TkComputeAnchor(butPtr->anchor, tkwin, butPtr->padX, butPtr->padY,
668                     butPtr->indicatorSpace + butPtr->textWidth,
669                     butPtr->textHeight, &x, &y);
670
671             x += butPtr->indicatorSpace;
672             ShiftByOffset(butPtr, relief, &x, &y, width, height);
673             Tk_DrawTextLayout(butPtr->display, pixmap, gc, butPtr->textLayout,
674                     x, y, 0, -1);
675             Tk_UnderlineTextLayout(butPtr->display, pixmap, gc,
676                     butPtr->textLayout, x, y, butPtr->underline);
677             y += butPtr->textHeight/2;
678         }
679     }
680
681     /*
682      * Draw the indicator for check buttons and radio buttons. At this point,
683      * x and y refer to the top-left corner of the text or image or bitmap.
684      */
685
686     if ((butPtr->type == TYPE_CHECK_BUTTON) && butPtr->indicatorOn) {
687         if (butPtr->indicatorDiameter > 2*butPtr->borderWidth) {
688             TkBorder *selBorder = (TkBorder *) butPtr->selectBorder;
689             XColor *selColor = NULL;
690
691             if (selBorder != NULL) {
692                 selColor = selBorder->bgColorPtr;
693             }
694             x -= butPtr->indicatorSpace/2;
695             y = Tk_Height(tkwin)/2;
696             TkpDrawCheckIndicator(tkwin, butPtr->display, pixmap, x, y,
697                     border, butPtr->normalFg, selColor, butPtr->disabledFg,
698                     ((butPtr->flags & SELECTED) ? 1 :
699                             (butPtr->flags & TRISTATED) ? 2 : 0),
700                     (butPtr->state == STATE_DISABLED), CHECK_BUTTON);
701         }
702     } else if ((butPtr->type == TYPE_RADIO_BUTTON) && butPtr->indicatorOn) {
703         if (butPtr->indicatorDiameter > 2*butPtr->borderWidth) {
704             TkBorder *selBorder = (TkBorder *) butPtr->selectBorder;
705             XColor *selColor = NULL;
706
707             if (selBorder != NULL) {
708                 selColor = selBorder->bgColorPtr;
709             }
710             x -= butPtr->indicatorSpace/2;
711             y = Tk_Height(tkwin)/2;
712             TkpDrawCheckIndicator(tkwin, butPtr->display, pixmap, x, y,
713                     border, butPtr->normalFg, selColor, butPtr->disabledFg,
714                     ((butPtr->flags & SELECTED) ? 1 :
715                             (butPtr->flags & TRISTATED) ? 2 : 0),
716                     (butPtr->state == STATE_DISABLED), RADIO_BUTTON);
717         }
718     }
719
720     /*
721      * If the button is disabled with a stipple rather than a special
722      * foreground color, generate the stippled effect. If the widget is
723      * selected and we use a different background color when selected, must
724      * temporarily modify the GC so the stippling is the right color.
725      */
726
727     if ((butPtr->state == STATE_DISABLED)
728             && ((butPtr->disabledFg == NULL) || (butPtr->image != NULL))) {
729         if ((butPtr->flags & SELECTED) && !butPtr->indicatorOn
730                 && (butPtr->selectBorder != NULL)) {
731             XSetForeground(butPtr->display, butPtr->stippleGC,
732                     Tk_3DBorderColor(butPtr->selectBorder)->pixel);
733         }
734
735         /*
736          * Stipple the whole button if no disabledFg was specified, otherwise
737          * restrict stippling only to displayed image
738          */
739
740         if (butPtr->disabledFg == NULL) {
741             XFillRectangle(butPtr->display, pixmap, butPtr->stippleGC, 0, 0,
742                     (unsigned) Tk_Width(tkwin), (unsigned) Tk_Height(tkwin));
743         } else {
744             XFillRectangle(butPtr->display, pixmap, butPtr->stippleGC,
745                     imageXOffset, imageYOffset,
746                     (unsigned) imageWidth, (unsigned) imageHeight);
747         }
748         if ((butPtr->flags & SELECTED) && !butPtr->indicatorOn
749                 && (butPtr->selectBorder != NULL)) {
750             XSetForeground(butPtr->display, butPtr->stippleGC,
751                     Tk_3DBorderColor(butPtr->normalBorder)->pixel);
752         }
753     }
754
755     /*
756      * Draw the border and traversal highlight last. This way, if the button's
757      * contents overflow they'll be covered up by the border. This code is
758      * complicated by the possible combinations of focus highlight and default
759      * rings. We draw the focus and highlight rings using the highlight border
760      * and highlight foreground color.
761      */
762
763     if (relief != TK_RELIEF_FLAT) {
764         int inset = butPtr->highlightWidth;
765
766         if (butPtr->defaultState == DEFAULT_ACTIVE) {
767             /*
768              * Draw the default ring with 2 pixels of space between the
769              * default ring and the button and the default ring and the focus
770              * ring. Note that we need to explicitly draw the space in the
771              * highlightBorder color to ensure that we overwrite any overflow
772              * text and/or a different button background color.
773              */
774
775             Tk_Draw3DRectangle(tkwin, pixmap, butPtr->highlightBorder, inset,
776                     inset, Tk_Width(tkwin) - 2*inset,
777                     Tk_Height(tkwin) - 2*inset, 2, TK_RELIEF_FLAT);
778             inset += 2;
779             Tk_Draw3DRectangle(tkwin, pixmap, butPtr->highlightBorder, inset,
780                     inset, Tk_Width(tkwin) - 2*inset,
781                     Tk_Height(tkwin) - 2*inset, 1, TK_RELIEF_SUNKEN);
782             inset++;
783             Tk_Draw3DRectangle(tkwin, pixmap, butPtr->highlightBorder, inset,
784                     inset, Tk_Width(tkwin) - 2*inset,
785                     Tk_Height(tkwin) - 2*inset, 2, TK_RELIEF_FLAT);
786
787             inset += 2;
788         } else if (butPtr->defaultState == DEFAULT_NORMAL) {
789             /*
790              * Leave room for the default ring and write over any text or
791              * background color.
792              */
793
794             Tk_Draw3DRectangle(tkwin, pixmap, butPtr->highlightBorder, 0,
795                     0, Tk_Width(tkwin), Tk_Height(tkwin), 5, TK_RELIEF_FLAT);
796             inset += 5;
797         }
798
799         /*
800          * Draw the button border.
801          */
802
803         Tk_Draw3DRectangle(tkwin, pixmap, border, inset, inset,
804                 Tk_Width(tkwin) - 2*inset, Tk_Height(tkwin) - 2*inset,
805                 butPtr->borderWidth, relief);
806     }
807     if (butPtr->highlightWidth > 0) {
808         if (butPtr->flags & GOT_FOCUS) {
809             gc = Tk_GCForColor(butPtr->highlightColorPtr, pixmap);
810         } else {
811             gc = Tk_GCForColor(Tk_3DBorderColor(butPtr->highlightBorder),
812                     pixmap);
813         }
814
815         /*
816          * Make sure the focus ring shrink-wraps the actual button, not the
817          * padding space left for a default ring.
818          */
819
820         if (butPtr->defaultState == DEFAULT_NORMAL) {
821             TkDrawInsetFocusHighlight(tkwin, gc, butPtr->highlightWidth,
822                     pixmap, 5);
823         } else {
824             Tk_DrawFocusHighlight(tkwin, gc, butPtr->highlightWidth, pixmap);
825         }
826     }
827
828     /*
829      * Copy the information from the off-screen pixmap onto the screen, then
830      * delete the pixmap.
831      */
832
833     XCopyArea(butPtr->display, pixmap, Tk_WindowId(tkwin),
834             butPtr->copyGC, 0, 0, (unsigned) Tk_Width(tkwin),
835             (unsigned) Tk_Height(tkwin), 0, 0);
836     Tk_FreePixmap(butPtr->display, pixmap);
837 }
838 \f
839 /*
840  *----------------------------------------------------------------------
841  *
842  * TkpComputeButtonGeometry --
843  *
844  *      After changes in a button's text or bitmap, this function recomputes
845  *      the button's geometry and passes this information along to the
846  *      geometry manager for the window.
847  *
848  * Results:
849  *      None.
850  *
851  * Side effects:
852  *      The button's window may change size.
853  *
854  *----------------------------------------------------------------------
855  */
856
857 void
858 TkpComputeButtonGeometry(
859     TkButton *butPtr)   /* Button whose geometry may have changed. */
860 {
861     int width, height, avgWidth, txtWidth, txtHeight;
862     int haveImage = 0, haveText = 0;
863     Tk_FontMetrics fm;
864
865     butPtr->inset = butPtr->highlightWidth + butPtr->borderWidth;
866
867     /*
868      * Leave room for the default ring if needed.
869      */
870
871     if (butPtr->defaultState != DEFAULT_DISABLED) {
872         butPtr->inset += 5;
873     }
874     butPtr->indicatorSpace = 0;
875
876     width = 0;
877     height = 0;
878     txtWidth = 0;
879     txtHeight = 0;
880     avgWidth = 0;
881
882     if (butPtr->image != NULL) {
883         Tk_SizeOfImage(butPtr->image, &width, &height);
884         haveImage = 1;
885     } else if (butPtr->bitmap != None) {
886         Tk_SizeOfBitmap(butPtr->display, butPtr->bitmap, &width, &height);
887         haveImage = 1;
888     }
889
890     if (haveImage == 0 || butPtr->compound != COMPOUND_NONE) {
891         Tk_FreeTextLayout(butPtr->textLayout);
892
893         butPtr->textLayout = Tk_ComputeTextLayout(butPtr->tkfont,
894                 Tcl_GetString(butPtr->textPtr), -1, butPtr->wrapLength,
895                 butPtr->justify, 0, &butPtr->textWidth, &butPtr->textHeight);
896
897         txtWidth = butPtr->textWidth;
898         txtHeight = butPtr->textHeight;
899         avgWidth = Tk_TextWidth(butPtr->tkfont, "0", 1);
900         Tk_GetFontMetrics(butPtr->tkfont, &fm);
901         haveText = (txtWidth != 0 && txtHeight != 0);
902     }
903
904     /*
905      * If the button is compound (i.e., it shows both an image and text), the
906      * new geometry is a combination of the image and text geometry. We only
907      * honor the compound bit if the button has both text and an image,
908      * because otherwise it is not really a compound button.
909      */
910
911     if (butPtr->compound != COMPOUND_NONE && haveImage && haveText) {
912         switch ((enum compound) butPtr->compound) {
913         case COMPOUND_TOP:
914         case COMPOUND_BOTTOM:
915             /*
916              * Image is above or below text.
917              */
918
919             height += txtHeight + butPtr->padY;
920             width = (width > txtWidth ? width : txtWidth);
921             break;
922         case COMPOUND_LEFT:
923         case COMPOUND_RIGHT:
924             /*
925              * Image is left or right of text.
926              */
927
928             width += txtWidth + butPtr->padX;
929             height = (height > txtHeight ? height : txtHeight);
930             break;
931         case COMPOUND_CENTER:
932             /*
933              * Image and text are superimposed.
934              */
935
936             width = (width > txtWidth ? width : txtWidth);
937             height = (height > txtHeight ? height : txtHeight);
938             break;
939         case COMPOUND_NONE:
940             break;
941         }
942         if (butPtr->width > 0) {
943             width = butPtr->width;
944         }
945         if (butPtr->height > 0) {
946             height = butPtr->height;
947         }
948
949         if ((butPtr->type >= TYPE_CHECK_BUTTON) && butPtr->indicatorOn) {
950             butPtr->indicatorSpace = height;
951             if (butPtr->type == TYPE_CHECK_BUTTON) {
952                 butPtr->indicatorDiameter = (65*height)/100;
953             } else {
954                 butPtr->indicatorDiameter = (75*height)/100;
955             }
956         }
957
958         width += 2*butPtr->padX;
959         height += 2*butPtr->padY;
960     } else {
961         if (haveImage) {
962             if (butPtr->width > 0) {
963                 width = butPtr->width;
964             }
965             if (butPtr->height > 0) {
966                 height = butPtr->height;
967             }
968
969             if ((butPtr->type >= TYPE_CHECK_BUTTON) && butPtr->indicatorOn) {
970                 butPtr->indicatorSpace = height;
971                 if (butPtr->type == TYPE_CHECK_BUTTON) {
972                     butPtr->indicatorDiameter = (65*height)/100;
973                 } else {
974                     butPtr->indicatorDiameter = (75*height)/100;
975                 }
976             }
977         } else {
978             width = txtWidth;
979             height = txtHeight;
980
981             if (butPtr->width > 0) {
982                 width = butPtr->width * avgWidth;
983             }
984             if (butPtr->height > 0) {
985                 height = butPtr->height * fm.linespace;
986             }
987             if ((butPtr->type >= TYPE_CHECK_BUTTON) && butPtr->indicatorOn) {
988                 butPtr->indicatorDiameter = fm.linespace;
989                 if (butPtr->type == TYPE_CHECK_BUTTON) {
990                     butPtr->indicatorDiameter =
991                         (80*butPtr->indicatorDiameter)/100;
992                 }
993                 butPtr->indicatorSpace = butPtr->indicatorDiameter + avgWidth;
994             }
995         }
996     }
997
998     /*
999      * When issuing the geometry request, add extra space for the indicator,
1000      * if any, and for the border and padding, plus two extra pixels so the
1001      * display can be offset by 1 pixel in either direction for the raised or
1002      * lowered effect.
1003      */
1004
1005     if ((butPtr->image == NULL) && (butPtr->bitmap == None)) {
1006         width += 2*butPtr->padX;
1007         height += 2*butPtr->padY;
1008     }
1009     if ((butPtr->type == TYPE_BUTTON) && !Tk_StrictMotif(butPtr->tkwin)) {
1010         width += 2;
1011         height += 2;
1012     }
1013     Tk_GeometryRequest(butPtr->tkwin, (int) (width + butPtr->indicatorSpace
1014             + 2*butPtr->inset), (int) (height + 2*butPtr->inset));
1015     Tk_SetInternalBorder(butPtr->tkwin, butPtr->inset);
1016 }
1017 \f
1018 /*
1019  * Local Variables:
1020  * mode: c
1021  * c-basic-offset: 4
1022  * fill-column: 78
1023  * End:
1024  */