1 |
/* $Header$ */ |
2 |
|
3 |
/* |
4 |
* tkMenuDraw.c -- |
5 |
* |
6 |
* This module implements the platform-independent drawing and |
7 |
* geometry calculations of menu widgets. |
8 |
* |
9 |
* Copyright (c) 1996-1997 by Sun Microsystems, Inc. |
10 |
* |
11 |
* See the file "license.terms" for information on usage and redistribution |
12 |
* of this file, and for a DISCLAIMER OF ALL WARRANTIES. |
13 |
* |
14 |
* RCS: @(#) $Id: tkmenudraw.c,v 1.1.1.1 2001/06/13 05:06:08 dtashley Exp $ |
15 |
*/ |
16 |
|
17 |
#include "tkMenu.h" |
18 |
|
19 |
/* |
20 |
* Forward declarations for procedures defined later in this file: |
21 |
*/ |
22 |
|
23 |
static void AdjustMenuCoords _ANSI_ARGS_ ((TkMenu *menuPtr, |
24 |
TkMenuEntry *mePtr, int *xPtr, int *yPtr, |
25 |
char *string)); |
26 |
static void ComputeMenuGeometry _ANSI_ARGS_(( |
27 |
ClientData clientData)); |
28 |
static void DisplayMenu _ANSI_ARGS_((ClientData clientData)); |
29 |
|
30 |
/* |
31 |
*---------------------------------------------------------------------- |
32 |
* |
33 |
* TkMenuInitializeDrawingFields -- |
34 |
* |
35 |
* Fills in drawing fields of a new menu. Called when new menu is |
36 |
* created by MenuCmd. |
37 |
* |
38 |
* Results: |
39 |
* None. |
40 |
* |
41 |
* Side effects: |
42 |
* menuPtr fields are initialized. |
43 |
* |
44 |
*---------------------------------------------------------------------- |
45 |
*/ |
46 |
|
47 |
void |
48 |
TkMenuInitializeDrawingFields(menuPtr) |
49 |
TkMenu *menuPtr; /* The menu we are initializing. */ |
50 |
{ |
51 |
menuPtr->textGC = None; |
52 |
menuPtr->gray = None; |
53 |
menuPtr->disabledGC = None; |
54 |
menuPtr->activeGC = None; |
55 |
menuPtr->indicatorGC = None; |
56 |
menuPtr->disabledImageGC = None; |
57 |
menuPtr->totalWidth = menuPtr->totalHeight = 0; |
58 |
} |
59 |
|
60 |
/* |
61 |
*---------------------------------------------------------------------- |
62 |
* |
63 |
* TkMenuInitializeEntryDrawingFields -- |
64 |
* |
65 |
* Fills in drawing fields of a new menu entry. Called when an |
66 |
* entry is created. |
67 |
* |
68 |
* Results: |
69 |
* None. |
70 |
* |
71 |
* Side effects: |
72 |
* None. |
73 |
* |
74 |
*---------------------------------------------------------------------- |
75 |
*/ |
76 |
|
77 |
void |
78 |
TkMenuInitializeEntryDrawingFields(mePtr) |
79 |
TkMenuEntry *mePtr; /* The menu we are initializing. */ |
80 |
{ |
81 |
mePtr->width = 0; |
82 |
mePtr->height = 0; |
83 |
mePtr->x = 0; |
84 |
mePtr->y = 0; |
85 |
mePtr->indicatorSpace = 0; |
86 |
mePtr->labelWidth = 0; |
87 |
mePtr->textGC = None; |
88 |
mePtr->activeGC = None; |
89 |
mePtr->disabledGC = None; |
90 |
mePtr->indicatorGC = None; |
91 |
} |
92 |
|
93 |
/* |
94 |
*---------------------------------------------------------------------- |
95 |
* |
96 |
* TkMenuFreeDrawOptions -- |
97 |
* |
98 |
* Frees up any structures allocated for the drawing of a menu. |
99 |
* Called when menu is deleted. |
100 |
* |
101 |
* Results: |
102 |
* None. |
103 |
* |
104 |
* Side effects: |
105 |
* Storage is released. |
106 |
* |
107 |
*---------------------------------------------------------------------- |
108 |
*/ |
109 |
|
110 |
void |
111 |
TkMenuFreeDrawOptions(menuPtr) |
112 |
TkMenu *menuPtr; |
113 |
{ |
114 |
if (menuPtr->textGC != None) { |
115 |
Tk_FreeGC(menuPtr->display, menuPtr->textGC); |
116 |
} |
117 |
if (menuPtr->disabledImageGC != None) { |
118 |
Tk_FreeGC(menuPtr->display, menuPtr->disabledImageGC); |
119 |
} |
120 |
if (menuPtr->gray != None) { |
121 |
Tk_FreeBitmap(menuPtr->display, menuPtr->gray); |
122 |
} |
123 |
if (menuPtr->disabledGC != None) { |
124 |
Tk_FreeGC(menuPtr->display, menuPtr->disabledGC); |
125 |
} |
126 |
if (menuPtr->activeGC != None) { |
127 |
Tk_FreeGC(menuPtr->display, menuPtr->activeGC); |
128 |
} |
129 |
if (menuPtr->indicatorGC != None) { |
130 |
Tk_FreeGC(menuPtr->display, menuPtr->indicatorGC); |
131 |
} |
132 |
} |
133 |
|
134 |
/* |
135 |
*---------------------------------------------------------------------- |
136 |
* |
137 |
* TkMenuEntryFreeDrawOptions -- |
138 |
* |
139 |
* Frees up drawing structures for a menu entry. Called when |
140 |
* menu entry is freed. |
141 |
* |
142 |
* RESULTS: |
143 |
* None. |
144 |
* |
145 |
* Side effects: |
146 |
* Storage is freed. |
147 |
* |
148 |
*---------------------------------------------------------------------- |
149 |
*/ |
150 |
|
151 |
void |
152 |
TkMenuEntryFreeDrawOptions(mePtr) |
153 |
TkMenuEntry *mePtr; |
154 |
{ |
155 |
if (mePtr->textGC != None) { |
156 |
Tk_FreeGC(mePtr->menuPtr->display, mePtr->textGC); |
157 |
} |
158 |
if (mePtr->disabledGC != None) { |
159 |
Tk_FreeGC(mePtr->menuPtr->display, mePtr->disabledGC); |
160 |
} |
161 |
if (mePtr->activeGC != None) { |
162 |
Tk_FreeGC(mePtr->menuPtr->display, mePtr->activeGC); |
163 |
} |
164 |
if (mePtr->indicatorGC != None) { |
165 |
Tk_FreeGC(mePtr->menuPtr->display, mePtr->indicatorGC); |
166 |
} |
167 |
} |
168 |
|
169 |
/* |
170 |
*---------------------------------------------------------------------- |
171 |
* |
172 |
* TkMenuConfigureDrawOptions -- |
173 |
* |
174 |
* Sets the menu's drawing attributes in preparation for drawing |
175 |
* the menu. |
176 |
* |
177 |
* RESULTS: |
178 |
* None. |
179 |
* |
180 |
* Side effects: |
181 |
* Storage is allocated. |
182 |
* |
183 |
*---------------------------------------------------------------------- |
184 |
*/ |
185 |
|
186 |
void |
187 |
TkMenuConfigureDrawOptions(menuPtr) |
188 |
TkMenu *menuPtr; /* The menu we are configuring. */ |
189 |
{ |
190 |
XGCValues gcValues; |
191 |
GC newGC; |
192 |
unsigned long mask; |
193 |
Tk_3DBorder border, activeBorder; |
194 |
Tk_Font tkfont; |
195 |
XColor *fg, *activeFg, *indicatorFg; |
196 |
|
197 |
/* |
198 |
* A few options need special processing, such as setting the |
199 |
* background from a 3-D border, or filling in complicated |
200 |
* defaults that couldn't be specified to Tk_ConfigureWidget. |
201 |
*/ |
202 |
|
203 |
border = Tk_Get3DBorderFromObj(menuPtr->tkwin, menuPtr->borderPtr); |
204 |
Tk_SetBackgroundFromBorder(menuPtr->tkwin, border); |
205 |
|
206 |
tkfont = Tk_GetFontFromObj(menuPtr->tkwin, menuPtr->fontPtr); |
207 |
gcValues.font = Tk_FontId(tkfont); |
208 |
fg = Tk_GetColorFromObj(menuPtr->tkwin, menuPtr->fgPtr); |
209 |
gcValues.foreground = fg->pixel; |
210 |
gcValues.background = Tk_3DBorderColor(border)->pixel; |
211 |
newGC = Tk_GetGC(menuPtr->tkwin, GCForeground|GCBackground|GCFont, |
212 |
&gcValues); |
213 |
if (menuPtr->textGC != None) { |
214 |
Tk_FreeGC(menuPtr->display, menuPtr->textGC); |
215 |
} |
216 |
menuPtr->textGC = newGC; |
217 |
|
218 |
gcValues.font = Tk_FontId(tkfont); |
219 |
gcValues.background = Tk_3DBorderColor(border)->pixel; |
220 |
if (menuPtr->disabledFgPtr != NULL) { |
221 |
XColor *disabledFg; |
222 |
|
223 |
disabledFg = Tk_GetColorFromObj(menuPtr->tkwin, |
224 |
menuPtr->disabledFgPtr); |
225 |
gcValues.foreground = disabledFg->pixel; |
226 |
mask = GCForeground|GCBackground|GCFont; |
227 |
} else { |
228 |
gcValues.foreground = gcValues.background; |
229 |
mask = GCForeground; |
230 |
if (menuPtr->gray == None) { |
231 |
menuPtr->gray = Tk_GetBitmap(menuPtr->interp, menuPtr->tkwin, |
232 |
"gray50"); |
233 |
} |
234 |
if (menuPtr->gray != None) { |
235 |
gcValues.fill_style = FillStippled; |
236 |
gcValues.stipple = menuPtr->gray; |
237 |
mask = GCForeground|GCFillStyle|GCStipple; |
238 |
} |
239 |
} |
240 |
newGC = Tk_GetGC(menuPtr->tkwin, mask, &gcValues); |
241 |
if (menuPtr->disabledGC != None) { |
242 |
Tk_FreeGC(menuPtr->display, menuPtr->disabledGC); |
243 |
} |
244 |
menuPtr->disabledGC = newGC; |
245 |
|
246 |
gcValues.foreground = Tk_3DBorderColor(border)->pixel; |
247 |
if (menuPtr->gray == None) { |
248 |
menuPtr->gray = Tk_GetBitmap(menuPtr->interp, menuPtr->tkwin, |
249 |
"gray50"); |
250 |
} |
251 |
if (menuPtr->gray != None) { |
252 |
gcValues.fill_style = FillStippled; |
253 |
gcValues.stipple = menuPtr->gray; |
254 |
newGC = Tk_GetGC(menuPtr->tkwin, |
255 |
GCForeground|GCFillStyle|GCStipple, &gcValues); |
256 |
} |
257 |
if (menuPtr->disabledImageGC != None) { |
258 |
Tk_FreeGC(menuPtr->display, menuPtr->disabledImageGC); |
259 |
} |
260 |
menuPtr->disabledImageGC = newGC; |
261 |
|
262 |
gcValues.font = Tk_FontId(tkfont); |
263 |
activeFg = Tk_GetColorFromObj(menuPtr->tkwin, menuPtr->activeFgPtr); |
264 |
gcValues.foreground = activeFg->pixel; |
265 |
activeBorder = Tk_Get3DBorderFromObj(menuPtr->tkwin, |
266 |
menuPtr->activeBorderPtr); |
267 |
gcValues.background = Tk_3DBorderColor(activeBorder)->pixel; |
268 |
newGC = Tk_GetGC(menuPtr->tkwin, GCForeground|GCBackground|GCFont, |
269 |
&gcValues); |
270 |
if (menuPtr->activeGC != None) { |
271 |
Tk_FreeGC(menuPtr->display, menuPtr->activeGC); |
272 |
} |
273 |
menuPtr->activeGC = newGC; |
274 |
|
275 |
indicatorFg = Tk_GetColorFromObj(menuPtr->tkwin, |
276 |
menuPtr->indicatorFgPtr); |
277 |
gcValues.foreground = indicatorFg->pixel; |
278 |
gcValues.background = Tk_3DBorderColor(border)->pixel; |
279 |
newGC = Tk_GetGC(menuPtr->tkwin, GCForeground|GCBackground|GCFont, |
280 |
&gcValues); |
281 |
if (menuPtr->indicatorGC != None) { |
282 |
Tk_FreeGC(menuPtr->display, menuPtr->indicatorGC); |
283 |
} |
284 |
menuPtr->indicatorGC = newGC; |
285 |
} |
286 |
|
287 |
/* |
288 |
*---------------------------------------------------------------------- |
289 |
* |
290 |
* TkMenuConfigureEntryDrawOptions -- |
291 |
* |
292 |
* Calculates any entry-specific draw options for the given menu |
293 |
* entry. |
294 |
* |
295 |
* Results: |
296 |
* Returns a standard Tcl error. |
297 |
* |
298 |
* Side effects: |
299 |
* Storage may be allocated. |
300 |
* |
301 |
*---------------------------------------------------------------------- |
302 |
*/ |
303 |
|
304 |
int |
305 |
TkMenuConfigureEntryDrawOptions(mePtr, index) |
306 |
TkMenuEntry *mePtr; |
307 |
int index; |
308 |
{ |
309 |
|
310 |
XGCValues gcValues; |
311 |
GC newGC, newActiveGC, newDisabledGC, newIndicatorGC; |
312 |
unsigned long mask; |
313 |
Tk_Font tkfont; |
314 |
TkMenu *menuPtr = mePtr->menuPtr; |
315 |
|
316 |
tkfont = Tk_GetFontFromObj(menuPtr->tkwin, |
317 |
(mePtr->fontPtr != NULL) ? mePtr->fontPtr : menuPtr->fontPtr); |
318 |
|
319 |
if (mePtr->state == ENTRY_ACTIVE) { |
320 |
if (index != menuPtr->active) { |
321 |
TkActivateMenuEntry(menuPtr, index); |
322 |
} |
323 |
} else { |
324 |
if (index == menuPtr->active) { |
325 |
TkActivateMenuEntry(menuPtr, -1); |
326 |
} |
327 |
} |
328 |
|
329 |
if ((mePtr->fontPtr != NULL) |
330 |
|| (mePtr->borderPtr != NULL) |
331 |
|| (mePtr->fgPtr != NULL) |
332 |
|| (mePtr->activeBorderPtr != NULL) |
333 |
|| (mePtr->activeFgPtr != NULL) |
334 |
|| (mePtr->indicatorFgPtr != NULL)) { |
335 |
XColor *fg, *indicatorFg, *activeFg; |
336 |
Tk_3DBorder border, activeBorder; |
337 |
|
338 |
fg = Tk_GetColorFromObj(menuPtr->tkwin, (mePtr->fgPtr != NULL) |
339 |
? mePtr->fgPtr : menuPtr->fgPtr); |
340 |
gcValues.foreground = fg->pixel; |
341 |
border = Tk_Get3DBorderFromObj(menuPtr->tkwin, |
342 |
(mePtr->borderPtr != NULL) ? mePtr->borderPtr |
343 |
: menuPtr->borderPtr); |
344 |
gcValues.background = Tk_3DBorderColor(border)->pixel; |
345 |
|
346 |
gcValues.font = Tk_FontId(tkfont); |
347 |
|
348 |
/* |
349 |
* Note: disable GraphicsExpose events; we know there won't be |
350 |
* obscured areas when copying from an off-screen pixmap to the |
351 |
* screen and this gets rid of unnecessary events. |
352 |
*/ |
353 |
|
354 |
gcValues.graphics_exposures = False; |
355 |
newGC = Tk_GetGC(menuPtr->tkwin, |
356 |
GCForeground|GCBackground|GCFont|GCGraphicsExposures, |
357 |
&gcValues); |
358 |
|
359 |
indicatorFg = Tk_GetColorFromObj(menuPtr->tkwin, |
360 |
(mePtr->indicatorFgPtr != NULL) ? mePtr->indicatorFgPtr |
361 |
: menuPtr->indicatorFgPtr); |
362 |
gcValues.foreground = indicatorFg->pixel; |
363 |
newIndicatorGC = Tk_GetGC(menuPtr->tkwin, |
364 |
GCForeground|GCBackground|GCGraphicsExposures, |
365 |
&gcValues); |
366 |
|
367 |
if ((menuPtr->disabledFgPtr != NULL) || (mePtr->image != NULL)) { |
368 |
XColor *disabledFg; |
369 |
|
370 |
disabledFg = Tk_GetColorFromObj(menuPtr->tkwin, |
371 |
menuPtr->disabledFgPtr); |
372 |
gcValues.foreground = disabledFg->pixel; |
373 |
mask = GCForeground|GCBackground|GCFont|GCGraphicsExposures; |
374 |
} else { |
375 |
gcValues.foreground = gcValues.background; |
376 |
gcValues.fill_style = FillStippled; |
377 |
gcValues.stipple = menuPtr->gray; |
378 |
mask = GCForeground|GCFillStyle|GCStipple; |
379 |
} |
380 |
newDisabledGC = Tk_GetGC(menuPtr->tkwin, mask, &gcValues); |
381 |
|
382 |
activeFg = Tk_GetColorFromObj(menuPtr->tkwin, |
383 |
(mePtr->activeFgPtr != NULL) ? mePtr->activeFgPtr |
384 |
: menuPtr->activeFgPtr); |
385 |
activeBorder = Tk_Get3DBorderFromObj(menuPtr->tkwin, |
386 |
(mePtr->activeBorderPtr != NULL) ? mePtr->activeBorderPtr |
387 |
: menuPtr->activeBorderPtr); |
388 |
|
389 |
gcValues.foreground = activeFg->pixel; |
390 |
gcValues.background = Tk_3DBorderColor(activeBorder)->pixel; |
391 |
newActiveGC = Tk_GetGC(menuPtr->tkwin, |
392 |
GCForeground|GCBackground|GCFont|GCGraphicsExposures, |
393 |
&gcValues); |
394 |
} else { |
395 |
newGC = None; |
396 |
newActiveGC = None; |
397 |
newDisabledGC = None; |
398 |
newIndicatorGC = None; |
399 |
} |
400 |
if (mePtr->textGC != None) { |
401 |
Tk_FreeGC(menuPtr->display, mePtr->textGC); |
402 |
} |
403 |
mePtr->textGC = newGC; |
404 |
if (mePtr->activeGC != None) { |
405 |
Tk_FreeGC(menuPtr->display, mePtr->activeGC); |
406 |
} |
407 |
mePtr->activeGC = newActiveGC; |
408 |
if (mePtr->disabledGC != None) { |
409 |
Tk_FreeGC(menuPtr->display, mePtr->disabledGC); |
410 |
} |
411 |
mePtr->disabledGC = newDisabledGC; |
412 |
if (mePtr->indicatorGC != None) { |
413 |
Tk_FreeGC(menuPtr->display, mePtr->indicatorGC); |
414 |
} |
415 |
mePtr->indicatorGC = newIndicatorGC; |
416 |
return TCL_OK; |
417 |
} |
418 |
|
419 |
/* |
420 |
*---------------------------------------------------------------------- |
421 |
* |
422 |
* TkEventuallyRecomputeMenu -- |
423 |
* |
424 |
* Tells Tcl to redo the geometry because this menu has changed. |
425 |
* |
426 |
* Results: |
427 |
* None. |
428 |
* |
429 |
* Side effects: |
430 |
* Menu geometry is recomputed at idle time, and the menu will be |
431 |
* redisplayed. |
432 |
* |
433 |
*---------------------------------------------------------------------- |
434 |
*/ |
435 |
|
436 |
void |
437 |
TkEventuallyRecomputeMenu(menuPtr) |
438 |
TkMenu *menuPtr; |
439 |
{ |
440 |
if (!(menuPtr->menuFlags & RESIZE_PENDING)) { |
441 |
menuPtr->menuFlags |= RESIZE_PENDING; |
442 |
Tcl_DoWhenIdle(ComputeMenuGeometry, (ClientData) menuPtr); |
443 |
} |
444 |
} |
445 |
|
446 |
/* |
447 |
*---------------------------------------------------------------------- |
448 |
* |
449 |
* TkRecomputeMenu -- |
450 |
* |
451 |
* Tells Tcl to redo the geometry because this menu has changed. |
452 |
* Does it now; removes any ComputeMenuGeometries from the idler. |
453 |
* |
454 |
* Results: |
455 |
* None. |
456 |
* |
457 |
* Side effects: |
458 |
* Menu geometry is immediately reconfigured. |
459 |
* |
460 |
*---------------------------------------------------------------------- |
461 |
*/ |
462 |
|
463 |
void |
464 |
TkRecomputeMenu(menuPtr) |
465 |
TkMenu *menuPtr; |
466 |
{ |
467 |
if (menuPtr->menuFlags & RESIZE_PENDING) { |
468 |
Tcl_CancelIdleCall(ComputeMenuGeometry, (ClientData) menuPtr); |
469 |
ComputeMenuGeometry((ClientData) menuPtr); |
470 |
} |
471 |
} |
472 |
|
473 |
/* |
474 |
*---------------------------------------------------------------------- |
475 |
* |
476 |
* TkEventuallyRedrawMenu -- |
477 |
* |
478 |
* Arrange for an entry of a menu, or the whole menu, to be |
479 |
* redisplayed at some point in the future. |
480 |
* |
481 |
* Results: |
482 |
* None. |
483 |
* |
484 |
* Side effects: |
485 |
* A when-idle hander is scheduled to do the redisplay, if there |
486 |
* isn't one already scheduled. |
487 |
* |
488 |
*---------------------------------------------------------------------- |
489 |
*/ |
490 |
|
491 |
void |
492 |
TkEventuallyRedrawMenu(menuPtr, mePtr) |
493 |
register TkMenu *menuPtr; /* Information about menu to redraw. */ |
494 |
register TkMenuEntry *mePtr;/* Entry to redraw. NULL means redraw |
495 |
* all the entries in the menu. */ |
496 |
{ |
497 |
int i; |
498 |
|
499 |
if (menuPtr->tkwin == NULL) { |
500 |
return; |
501 |
} |
502 |
if (mePtr != NULL) { |
503 |
mePtr->entryFlags |= ENTRY_NEEDS_REDISPLAY; |
504 |
} else { |
505 |
for (i = 0; i < menuPtr->numEntries; i++) { |
506 |
menuPtr->entries[i]->entryFlags |= ENTRY_NEEDS_REDISPLAY; |
507 |
} |
508 |
} |
509 |
if (!Tk_IsMapped(menuPtr->tkwin) |
510 |
|| (menuPtr->menuFlags & REDRAW_PENDING)) { |
511 |
return; |
512 |
} |
513 |
Tcl_DoWhenIdle(DisplayMenu, (ClientData) menuPtr); |
514 |
menuPtr->menuFlags |= REDRAW_PENDING; |
515 |
} |
516 |
|
517 |
/* |
518 |
*-------------------------------------------------------------- |
519 |
* |
520 |
* ComputeMenuGeometry -- |
521 |
* |
522 |
* This procedure is invoked to recompute the size and |
523 |
* layout of a menu. It is called as a when-idle handler so |
524 |
* that it only gets done once, even if a group of changes is |
525 |
* made to the menu. |
526 |
* |
527 |
* Results: |
528 |
* None. |
529 |
* |
530 |
* Side effects: |
531 |
* Fields of menu entries are changed to reflect their |
532 |
* current positions, and the size of the menu window |
533 |
* itself may be changed. |
534 |
* |
535 |
*-------------------------------------------------------------- |
536 |
*/ |
537 |
|
538 |
static void |
539 |
ComputeMenuGeometry(clientData) |
540 |
ClientData clientData; /* Structure describing menu. */ |
541 |
{ |
542 |
TkMenu *menuPtr = (TkMenu *) clientData; |
543 |
|
544 |
if (menuPtr->tkwin == NULL) { |
545 |
return; |
546 |
} |
547 |
|
548 |
if (menuPtr->menuType == MENUBAR) { |
549 |
TkpComputeMenubarGeometry(menuPtr); |
550 |
} else { |
551 |
TkpComputeStandardMenuGeometry(menuPtr); |
552 |
} |
553 |
|
554 |
if ((menuPtr->totalWidth != Tk_ReqWidth(menuPtr->tkwin)) || |
555 |
(menuPtr->totalHeight != Tk_ReqHeight(menuPtr->tkwin))) { |
556 |
Tk_GeometryRequest(menuPtr->tkwin, menuPtr->totalWidth, |
557 |
menuPtr->totalHeight); |
558 |
} |
559 |
|
560 |
/* |
561 |
* Must always force a redisplay here if the window is mapped |
562 |
* (even if the size didn't change, something else might have |
563 |
* changed in the menu, such as a label or accelerator). The |
564 |
* resize will force a redisplay above. |
565 |
*/ |
566 |
|
567 |
TkEventuallyRedrawMenu(menuPtr, (TkMenuEntry *) NULL); |
568 |
|
569 |
menuPtr->menuFlags &= ~RESIZE_PENDING; |
570 |
} |
571 |
|
572 |
/* |
573 |
*---------------------------------------------------------------------- |
574 |
* |
575 |
* TkMenuSelectImageProc -- |
576 |
* |
577 |
* This procedure is invoked by the image code whenever the manager |
578 |
* for an image does something that affects the size of contents |
579 |
* of an image displayed in a menu entry when it is selected. |
580 |
* |
581 |
* Results: |
582 |
* None. |
583 |
* |
584 |
* Side effects: |
585 |
* Arranges for the menu to get redisplayed. |
586 |
* |
587 |
*---------------------------------------------------------------------- |
588 |
*/ |
589 |
|
590 |
void |
591 |
TkMenuSelectImageProc(clientData, x, y, width, height, imgWidth, |
592 |
imgHeight) |
593 |
ClientData clientData; /* Pointer to widget record. */ |
594 |
int x, y; /* Upper left pixel (within image) |
595 |
* that must be redisplayed. */ |
596 |
int width, height; /* Dimensions of area to redisplay |
597 |
* (may be <= 0). */ |
598 |
int imgWidth, imgHeight; /* New dimensions of image. */ |
599 |
{ |
600 |
register TkMenuEntry *mePtr = (TkMenuEntry *) clientData; |
601 |
|
602 |
if ((mePtr->entryFlags & ENTRY_SELECTED) |
603 |
&& !(mePtr->menuPtr->menuFlags & |
604 |
REDRAW_PENDING)) { |
605 |
mePtr->menuPtr->menuFlags |= REDRAW_PENDING; |
606 |
Tcl_DoWhenIdle(DisplayMenu, (ClientData) mePtr->menuPtr); |
607 |
} |
608 |
} |
609 |
|
610 |
/* |
611 |
*---------------------------------------------------------------------- |
612 |
* |
613 |
* DisplayMenu -- |
614 |
* |
615 |
* This procedure is invoked to display a menu widget. |
616 |
* |
617 |
* Results: |
618 |
* None. |
619 |
* |
620 |
* Side effects: |
621 |
* Commands are output to X to display the menu in its |
622 |
* current mode. |
623 |
* |
624 |
*---------------------------------------------------------------------- |
625 |
*/ |
626 |
|
627 |
static void |
628 |
DisplayMenu(clientData) |
629 |
ClientData clientData; /* Information about widget. */ |
630 |
{ |
631 |
register TkMenu *menuPtr = (TkMenu *) clientData; |
632 |
register TkMenuEntry *mePtr; |
633 |
register Tk_Window tkwin = menuPtr->tkwin; |
634 |
int index, strictMotif; |
635 |
Tk_Font tkfont; |
636 |
Tk_FontMetrics menuMetrics; |
637 |
int width; |
638 |
int borderWidth; |
639 |
Tk_3DBorder border; |
640 |
int activeBorderWidth; |
641 |
int relief; |
642 |
|
643 |
|
644 |
menuPtr->menuFlags &= ~REDRAW_PENDING; |
645 |
if ((menuPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) { |
646 |
return; |
647 |
} |
648 |
|
649 |
Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, menuPtr->borderWidthPtr, |
650 |
&borderWidth); |
651 |
border = Tk_Get3DBorderFromObj(menuPtr->tkwin, menuPtr->borderPtr); |
652 |
Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, |
653 |
menuPtr->activeBorderWidthPtr, &activeBorderWidth); |
654 |
|
655 |
if (menuPtr->menuType == MENUBAR) { |
656 |
Tk_Fill3DRectangle(tkwin, Tk_WindowId(tkwin), border, borderWidth, |
657 |
borderWidth, Tk_Width(tkwin) - 2 * borderWidth, |
658 |
Tk_Height(tkwin) - 2 * borderWidth, 0, TK_RELIEF_FLAT); |
659 |
} |
660 |
|
661 |
strictMotif = Tk_StrictMotif(menuPtr->tkwin); |
662 |
|
663 |
/* |
664 |
* See note in ComputeMenuGeometry. We don't want to be doing font metrics |
665 |
* all of the time. |
666 |
*/ |
667 |
|
668 |
tkfont = Tk_GetFontFromObj(menuPtr->tkwin, menuPtr->fontPtr); |
669 |
Tk_GetFontMetrics(tkfont, &menuMetrics); |
670 |
|
671 |
/* |
672 |
* Loop through all of the entries, drawing them one at a time. |
673 |
*/ |
674 |
|
675 |
for (index = 0; index < menuPtr->numEntries; index++) { |
676 |
mePtr = menuPtr->entries[index]; |
677 |
if (menuPtr->menuType != MENUBAR) { |
678 |
if (!(mePtr->entryFlags & ENTRY_NEEDS_REDISPLAY)) { |
679 |
continue; |
680 |
} |
681 |
} |
682 |
mePtr->entryFlags &= ~ENTRY_NEEDS_REDISPLAY; |
683 |
|
684 |
if (menuPtr->menuType == MENUBAR) { |
685 |
width = mePtr->width; |
686 |
} else { |
687 |
if (mePtr->entryFlags & ENTRY_LAST_COLUMN) { |
688 |
width = Tk_Width(menuPtr->tkwin) - mePtr->x |
689 |
- activeBorderWidth; |
690 |
} else { |
691 |
width = mePtr->width + borderWidth; |
692 |
} |
693 |
} |
694 |
TkpDrawMenuEntry(mePtr, Tk_WindowId(menuPtr->tkwin), tkfont, |
695 |
&menuMetrics, mePtr->x, mePtr->y, width, |
696 |
mePtr->height, strictMotif, 1); |
697 |
if ((index > 0) && (menuPtr->menuType != MENUBAR) |
698 |
&& mePtr->columnBreak) { |
699 |
mePtr = menuPtr->entries[index - 1]; |
700 |
Tk_Fill3DRectangle(tkwin, Tk_WindowId(tkwin), border, |
701 |
mePtr->x, mePtr->y + mePtr->height, |
702 |
mePtr->width, |
703 |
Tk_Height(tkwin) - mePtr->y - mePtr->height - |
704 |
activeBorderWidth, 0, |
705 |
TK_RELIEF_FLAT); |
706 |
} |
707 |
} |
708 |
|
709 |
if (menuPtr->menuType != MENUBAR) { |
710 |
int x, y, height; |
711 |
|
712 |
if (menuPtr->numEntries == 0) { |
713 |
x = y = borderWidth; |
714 |
width = Tk_Width(tkwin) - 2 * activeBorderWidth; |
715 |
height = Tk_Height(tkwin) - 2 * activeBorderWidth; |
716 |
} else { |
717 |
mePtr = menuPtr->entries[menuPtr->numEntries - 1]; |
718 |
Tk_Fill3DRectangle(tkwin, Tk_WindowId(tkwin), |
719 |
border, mePtr->x, mePtr->y + mePtr->height, mePtr->width, |
720 |
Tk_Height(tkwin) - mePtr->y - mePtr->height |
721 |
- activeBorderWidth, 0, |
722 |
TK_RELIEF_FLAT); |
723 |
x = mePtr->x + mePtr->width; |
724 |
y = mePtr->y + mePtr->height; |
725 |
width = Tk_Width(tkwin) - x - activeBorderWidth; |
726 |
height = Tk_Height(tkwin) - y - activeBorderWidth; |
727 |
} |
728 |
Tk_Fill3DRectangle(tkwin, Tk_WindowId(tkwin), border, x, y, |
729 |
width, height, 0, TK_RELIEF_FLAT); |
730 |
} |
731 |
|
732 |
Tk_GetReliefFromObj(NULL, menuPtr->reliefPtr, &relief); |
733 |
Tk_Draw3DRectangle(menuPtr->tkwin, Tk_WindowId(tkwin), |
734 |
border, 0, 0, Tk_Width(tkwin), Tk_Height(tkwin), borderWidth, |
735 |
relief); |
736 |
} |
737 |
|
738 |
/* |
739 |
*-------------------------------------------------------------- |
740 |
* |
741 |
* TkMenuEventProc -- |
742 |
* |
743 |
* This procedure is invoked by the Tk dispatcher for various |
744 |
* events on menus. |
745 |
* |
746 |
* Results: |
747 |
* None. |
748 |
* |
749 |
* Side effects: |
750 |
* When the window gets deleted, internal structures get |
751 |
* cleaned up. When it gets exposed, it is redisplayed. |
752 |
* |
753 |
*-------------------------------------------------------------- |
754 |
*/ |
755 |
|
756 |
void |
757 |
TkMenuEventProc(clientData, eventPtr) |
758 |
ClientData clientData; /* Information about window. */ |
759 |
XEvent *eventPtr; /* Information about event. */ |
760 |
{ |
761 |
TkMenu *menuPtr = (TkMenu *) clientData; |
762 |
|
763 |
if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) { |
764 |
TkEventuallyRedrawMenu(menuPtr, (TkMenuEntry *) NULL); |
765 |
} else if (eventPtr->type == ConfigureNotify) { |
766 |
TkEventuallyRecomputeMenu(menuPtr); |
767 |
TkEventuallyRedrawMenu(menuPtr, (TkMenuEntry *) NULL); |
768 |
} else if (eventPtr->type == ActivateNotify) { |
769 |
if (menuPtr->menuType == TEAROFF_MENU) { |
770 |
TkpSetMainMenubar(menuPtr->interp, menuPtr->tkwin, NULL); |
771 |
} |
772 |
} else if (eventPtr->type == DestroyNotify) { |
773 |
if (menuPtr->tkwin != NULL) { |
774 |
TkDestroyMenu(menuPtr); |
775 |
menuPtr->tkwin = NULL; |
776 |
Tcl_DeleteCommandFromToken(menuPtr->interp, menuPtr->widgetCmd); |
777 |
} |
778 |
if (menuPtr->menuFlags & REDRAW_PENDING) { |
779 |
Tcl_CancelIdleCall(DisplayMenu, (ClientData) menuPtr); |
780 |
} |
781 |
if (menuPtr->menuFlags & RESIZE_PENDING) { |
782 |
Tcl_CancelIdleCall(ComputeMenuGeometry, (ClientData) menuPtr); |
783 |
} |
784 |
Tcl_EventuallyFree((ClientData) menuPtr, TCL_DYNAMIC); |
785 |
} |
786 |
} |
787 |
|
788 |
/* |
789 |
*---------------------------------------------------------------------- |
790 |
* |
791 |
* TkMenuImageProc -- |
792 |
* |
793 |
* This procedure is invoked by the image code whenever the manager |
794 |
* for an image does something that affects the size of contents |
795 |
* of an image displayed in a menu entry. |
796 |
* |
797 |
* Results: |
798 |
* None. |
799 |
* |
800 |
* Side effects: |
801 |
* Arranges for the menu to get redisplayed. |
802 |
* |
803 |
*---------------------------------------------------------------------- |
804 |
*/ |
805 |
|
806 |
void |
807 |
TkMenuImageProc(clientData, x, y, width, height, imgWidth, |
808 |
imgHeight) |
809 |
ClientData clientData; /* Pointer to widget record. */ |
810 |
int x, y; /* Upper left pixel (within image) |
811 |
* that must be redisplayed. */ |
812 |
int width, height; /* Dimensions of area to redisplay |
813 |
* (may be <= 0). */ |
814 |
int imgWidth, imgHeight; /* New dimensions of image. */ |
815 |
{ |
816 |
register TkMenu *menuPtr = ((TkMenuEntry *)clientData)->menuPtr; |
817 |
|
818 |
if ((menuPtr->tkwin != NULL) && !(menuPtr->menuFlags |
819 |
& RESIZE_PENDING)) { |
820 |
menuPtr->menuFlags |= RESIZE_PENDING; |
821 |
Tcl_DoWhenIdle(ComputeMenuGeometry, (ClientData) menuPtr); |
822 |
} |
823 |
} |
824 |
|
825 |
/* |
826 |
*---------------------------------------------------------------------- |
827 |
* |
828 |
* TkPostTearoffMenu -- |
829 |
* |
830 |
* Posts a menu on the screen. Used to post tearoff menus. On Unix, |
831 |
* all menus are posted this way. Adjusts the menu's position |
832 |
* so that it fits on the screen, and maps and raises the menu. |
833 |
* |
834 |
* Results: |
835 |
* Returns a standard Tcl Error. |
836 |
* |
837 |
* Side effects: |
838 |
* The menu is posted. |
839 |
* |
840 |
*---------------------------------------------------------------------- |
841 |
*/ |
842 |
|
843 |
int |
844 |
TkPostTearoffMenu(interp, menuPtr, x, y) |
845 |
Tcl_Interp *interp; /* The interpreter of the menu */ |
846 |
TkMenu *menuPtr; /* The menu we are posting */ |
847 |
int x; /* The root X coordinate where we |
848 |
* are posting */ |
849 |
int y; /* The root Y coordinate where we |
850 |
* are posting */ |
851 |
{ |
852 |
int vRootX, vRootY, vRootWidth, vRootHeight; |
853 |
int tmp, result; |
854 |
|
855 |
TkActivateMenuEntry(menuPtr, -1); |
856 |
TkRecomputeMenu(menuPtr); |
857 |
result = TkPostCommand(menuPtr); |
858 |
if (result != TCL_OK) { |
859 |
return result; |
860 |
} |
861 |
|
862 |
/* |
863 |
* The post commands could have deleted the menu, which means |
864 |
* we are dead and should go away. |
865 |
*/ |
866 |
|
867 |
if (menuPtr->tkwin == NULL) { |
868 |
return TCL_OK; |
869 |
} |
870 |
|
871 |
/* |
872 |
* Adjust the position of the menu if necessary to keep it |
873 |
* visible on the screen. There are two special tricks to |
874 |
* make this work right: |
875 |
* |
876 |
* 1. If a virtual root window manager is being used then |
877 |
* the coordinates are in the virtual root window of |
878 |
* menuPtr's parent; since the menu uses override-redirect |
879 |
* mode it will be in the *real* root window for the screen, |
880 |
* so we have to map the coordinates from the virtual root |
881 |
* (if any) to the real root. Can't get the virtual root |
882 |
* from the menu itself (it will never be seen by the wm) |
883 |
* so use its parent instead (it would be better to have an |
884 |
* an option that names a window to use for this...). |
885 |
* 2. The menu may not have been mapped yet, so its current size |
886 |
* might be the default 1x1. To compute how much space it |
887 |
* needs, use its requested size, not its actual size. |
888 |
* |
889 |
* Note that this code assumes square screen regions and all |
890 |
* positive coordinates. This does not work on a Mac with |
891 |
* multiple monitors. But then again, Tk has other problems |
892 |
* with this. |
893 |
*/ |
894 |
|
895 |
Tk_GetVRootGeometry(Tk_Parent(menuPtr->tkwin), &vRootX, &vRootY, |
896 |
&vRootWidth, &vRootHeight); |
897 |
x += vRootX; |
898 |
y += vRootY; |
899 |
tmp = WidthOfScreen(Tk_Screen(menuPtr->tkwin)) |
900 |
- Tk_ReqWidth(menuPtr->tkwin); |
901 |
if (x > tmp) { |
902 |
x = tmp; |
903 |
} |
904 |
if (x < 0) { |
905 |
x = 0; |
906 |
} |
907 |
tmp = HeightOfScreen(Tk_Screen(menuPtr->tkwin)) |
908 |
- Tk_ReqHeight(menuPtr->tkwin); |
909 |
if (y > tmp) { |
910 |
y = tmp; |
911 |
} |
912 |
if (y < 0) { |
913 |
y = 0; |
914 |
} |
915 |
Tk_MoveToplevelWindow(menuPtr->tkwin, x, y); |
916 |
if (!Tk_IsMapped(menuPtr->tkwin)) { |
917 |
Tk_MapWindow(menuPtr->tkwin); |
918 |
} |
919 |
TkWmRestackToplevel((TkWindow *) menuPtr->tkwin, Above, NULL); |
920 |
return TCL_OK; |
921 |
} |
922 |
|
923 |
/* |
924 |
*-------------------------------------------------------------- |
925 |
* |
926 |
* TkPostSubmenu -- |
927 |
* |
928 |
* This procedure arranges for a particular submenu (i.e. the |
929 |
* menu corresponding to a given cascade entry) to be |
930 |
* posted. |
931 |
* |
932 |
* Results: |
933 |
* A standard Tcl return result. Errors may occur in the |
934 |
* Tcl commands generated to post and unpost submenus. |
935 |
* |
936 |
* Side effects: |
937 |
* If there is already a submenu posted, it is unposted. |
938 |
* The new submenu is then posted. |
939 |
* |
940 |
*-------------------------------------------------------------- |
941 |
*/ |
942 |
|
943 |
int |
944 |
TkPostSubmenu(interp, menuPtr, mePtr) |
945 |
Tcl_Interp *interp; /* Used for invoking sub-commands and |
946 |
* reporting errors. */ |
947 |
register TkMenu *menuPtr; /* Information about menu as a whole. */ |
948 |
register TkMenuEntry *mePtr; /* Info about submenu that is to be |
949 |
* posted. NULL means make sure that |
950 |
* no submenu is posted. */ |
951 |
{ |
952 |
int result, x, y; |
953 |
|
954 |
if (mePtr == menuPtr->postedCascade) { |
955 |
return TCL_OK; |
956 |
} |
957 |
|
958 |
if (menuPtr->postedCascade != NULL) { |
959 |
char *name = Tcl_GetStringFromObj(menuPtr->postedCascade->namePtr, |
960 |
NULL); |
961 |
|
962 |
/* |
963 |
* Note: when unposting a submenu, we have to redraw the entire |
964 |
* parent menu. This is because of a combination of the following |
965 |
* things: |
966 |
* (a) the submenu partially overlaps the parent. |
967 |
* (b) the submenu specifies "save under", which causes the X |
968 |
* server to make a copy of the information under it when it |
969 |
* is posted. When the submenu is unposted, the X server |
970 |
* copies this data back and doesn't generate any Expose |
971 |
* events for the parent. |
972 |
* (c) the parent may have redisplayed itself after the submenu |
973 |
* was posted, in which case the saved information is no |
974 |
* longer correct. |
975 |
* The simplest solution is just force a complete redisplay of |
976 |
* the parent. |
977 |
*/ |
978 |
|
979 |
TkEventuallyRedrawMenu(menuPtr, (TkMenuEntry *) NULL); |
980 |
result = Tcl_VarEval(interp, name, " unpost", (char *) NULL); |
981 |
menuPtr->postedCascade = NULL; |
982 |
if (result != TCL_OK) { |
983 |
return result; |
984 |
} |
985 |
} |
986 |
|
987 |
if ((mePtr != NULL) && (mePtr->namePtr != NULL) |
988 |
&& Tk_IsMapped(menuPtr->tkwin)) { |
989 |
/* |
990 |
* Position the cascade with its upper left corner slightly |
991 |
* below and to the left of the upper right corner of the |
992 |
* menu entry (this is an attempt to match Motif behavior). |
993 |
* |
994 |
* The menu has to redrawn so that the entry can change relief. |
995 |
*/ |
996 |
|
997 |
char string[TCL_INTEGER_SPACE * 2]; |
998 |
char *name; |
999 |
|
1000 |
name = Tcl_GetStringFromObj(mePtr->namePtr, NULL); |
1001 |
Tk_GetRootCoords(menuPtr->tkwin, &x, &y); |
1002 |
AdjustMenuCoords(menuPtr, mePtr, &x, &y, string); |
1003 |
result = Tcl_VarEval(interp, name, " post ", string, (char *) NULL); |
1004 |
if (result != TCL_OK) { |
1005 |
return result; |
1006 |
} |
1007 |
menuPtr->postedCascade = mePtr; |
1008 |
TkEventuallyRedrawMenu(menuPtr, mePtr); |
1009 |
} |
1010 |
return TCL_OK; |
1011 |
} |
1012 |
|
1013 |
/* |
1014 |
*---------------------------------------------------------------------- |
1015 |
* |
1016 |
* AdjustMenuCoords -- |
1017 |
* |
1018 |
* Adjusts the given coordinates down and the left to give a Motif |
1019 |
* look. |
1020 |
* |
1021 |
* Results: |
1022 |
* None. |
1023 |
* |
1024 |
* Side effects: |
1025 |
* The menu is eventually redrawn if necessary. |
1026 |
* |
1027 |
*---------------------------------------------------------------------- |
1028 |
*/ |
1029 |
|
1030 |
static void |
1031 |
AdjustMenuCoords(menuPtr, mePtr, xPtr, yPtr, string) |
1032 |
TkMenu *menuPtr; |
1033 |
TkMenuEntry *mePtr; |
1034 |
int *xPtr; |
1035 |
int *yPtr; |
1036 |
char *string; |
1037 |
{ |
1038 |
if (menuPtr->menuType == MENUBAR) { |
1039 |
*xPtr += mePtr->x; |
1040 |
*yPtr += mePtr->y + mePtr->height; |
1041 |
} else { |
1042 |
int borderWidth, activeBorderWidth; |
1043 |
|
1044 |
Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, menuPtr->borderWidthPtr, |
1045 |
&borderWidth); |
1046 |
Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, |
1047 |
menuPtr->activeBorderWidthPtr, &activeBorderWidth); |
1048 |
*xPtr += Tk_Width(menuPtr->tkwin) - borderWidth - activeBorderWidth |
1049 |
- 2; |
1050 |
*yPtr += mePtr->y + activeBorderWidth + 2; |
1051 |
} |
1052 |
sprintf(string, "%d %d", *xPtr, *yPtr); |
1053 |
} |
1054 |
|
1055 |
/* End of tkmenudraw.c */ |