1 /*
2  * Author:      William Chia-Wei Cheng (bill.cheng@acm.org)
3  *
4  * Copyright (C) 2001-2009, William Chia-Wei Cheng.
5  *
6  * This file may be distributed under the terms of the Q Public License
7  * as defined by Trolltech AS of Norway and appearing in the file
8  * LICENSE.QPL included in the packaging of this file.
9  *
10  * THIS FILE IS PROVIDED AS IS WITH NO WARRANTY OF ANY KIND, INCLUDING
11  * THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
12  * PURPOSE.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL,
13  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
14  * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
15  * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
16  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  *
18  * @(#)$Header: /mm2/home/cvs/bc-src/tgif/menu.c,v 1.17 2011/05/16 16:21:58 william Exp $
19  */
20 
21 #define _INCLUDE_FROM_MENU_C_
22 
23 #include "tgifdefs.h"
24 #include "cmdids.h"
25 
26 #include "align.e"
27 #include "box.e"
28 #include "choice.e"
29 #include "cmd.e"
30 #include "color.e"
31 #include "cursor.e"
32 #include "dialog.e"
33 #include "drawing.e"
34 #include "edit.e"
35 #include "exec.e"
36 #include "file.e"
37 #include "font.e"
38 #include "grid.e"
39 #include "help.e"
40 #include "imgproc.e"
41 #include "mainloop.e"
42 #include "mainmenu.e"
43 #include "menu.e"
44 #include "menuinfo.e"
45 #include "move.e"
46 #include "msg.e"
47 #include "names.e"
48 #include "navigate.e"
49 #include "obj.e"
50 #include "page.e"
51 #include "pattern.e"
52 #include "raster.e"
53 #include "rect.e"
54 #include "remote.e"
55 #include "scroll.e"
56 #include "select.e"
57 #include "setup.e"
58 #include "shape.e"
59 #include "special.e"
60 #include "stk.e"
61 #include "strtbl.e"
62 #include "tangram2.e"
63 #include "text.e"
64 #include "util.e"
65 #include "version.e"
66 
67 #include "xbm/check.xbm"
68 #include "xbm/submenu.xbm"
69 
70 int iconWindowCreated=FALSE;
71 int iconWindowShown=FALSE;
72 int importingIconFile=FALSE;
73 
74 int showVersion=FALSE;
75 int activeMenu=INVALID;
76 
77 #define SEPARATOR_PADDING 1
78 
79 static int separatorHeight=SEPARATOR_PADDING;
80 
81 GC textMenuGC=(GC)0;
82 GC rvPixmapMenuGC=(GC)0;
83 
84 int menuRowsBeforeScroll=21;
85 int menuColsBeforeScroll=27;
86 
87 int maxScrollableMenuWidth=0;
88 int maxScrollableMenuHeight=0;
89 
90 int deleteCmdAsCut=FALSE;
91 
92 MenuDontSendCommandInfo gstMenuDontSendCommandInfo;
93 
94 static int savedZoomScale=0, savedDrawOrigX=0, savedDrawOrigY=0;
95 static int savedZoomedIn=FALSE;
96 static int savedDrawWinW=0, savedDrawWinH=0, savedFileModified=FALSE;
97 
98 static struct BBRec excludeMenubarWinBBox;
99 static int excludeMenubarIndex=INVALID;
100 
101 static int gnNumMenubarItems=0;
102 static int gnNumMainMenuItems=0;
103 /*
104  * gpMenubarItemInfos[i].menu_str and gpMenubarItemInfos[i].status_str
105  *       are raw English strings
106  */
107 static TgMenuItemInfo *gpMenubarItemInfos=NULL;
108 static TgMenuItemInfo *gpMainMenuItemInfos=NULL;
109 
110 static int gnMinimalMenubar=TRUE;
111 static int gnAutoWrapMenubar=FALSE;
112 
113 static
BuildMenubarInfo()114 void BuildMenubarInfo()
115 {
116    static int nInitialized=FALSE;
117    char *c_ptr=NULL;
118    TgMenuItemInfo *item_info=NULL;
119 
120    if (nInitialized) return;
121 
122    nInitialized = TRUE;
123 
124    gnMinimalMenubar = TRUE;
125    if (((c_ptr=XGetDefault(mainDisplay,TOOL_NAME,"MinimalMenubar")) !=
126          NULL) && UtilStrICmp(c_ptr,"false") == 0) {
127       gnMinimalMenubar = FALSE;
128    }
129    gnAutoWrapMenubar = FALSE;
130    if (((c_ptr=XGetDefault(mainDisplay,TOOL_NAME,"AutoWrapMenubar")) !=
131          NULL) && UtilStrICmp(c_ptr,"true") == 0) {
132       gnAutoWrapMenubar = TRUE;
133    }
134    /* menubar */
135    gpMenubarItemInfos = (gnMinimalMenubar ? minimalMenubarMenuInfo.items :
136          maximalMenubarMenuInfo.items);
137    gnNumMenubarItems = 0;
138    for (item_info=gpMenubarItemInfos; item_info->menu_str != NULL;
139          item_info++) {
140       gnNumMenubarItems++;
141    }
142    if (!cmdLineTgrm2) {
143       int i=0, j=0;
144       TgMenuItemInfo *pMenubarItemInfos=(TgMenuItemInfo*)malloc(
145             (gnNumMenubarItems+1)*sizeof(TgMenuItemInfo));
146 
147       for (i=0; i <= gnNumMenubarItems; i++) {
148          if (gpMenubarItemInfos[i].cmdid != MENU_TANGRAM2) {
149             memcpy(&pMenubarItemInfos[j++], &gpMenubarItemInfos[i],
150                   sizeof(TgMenuItemInfo));
151          }
152       }
153       memcpy(&pMenubarItemInfos[j], &gpMenubarItemInfos[i],
154             sizeof(TgMenuItemInfo));
155 
156       gpMenubarItemInfos = pMenubarItemInfos;
157       gnNumMenubarItems--;
158    }
159 
160    /* main menu */
161    gpMainMenuItemInfos = mainMenuInfo.items;
162    gnNumMainMenuItems = 0;
163    for (item_info=gpMainMenuItemInfos; item_info->menu_str != NULL;
164          item_info++) {
165       gnNumMainMenuItems++;
166    }
167    if (!cmdLineTgrm2) {
168       int i=0, j=0;
169       TgMenuItemInfo *pMainMenuItemInfos=(TgMenuItemInfo*)malloc(
170             (gnNumMainMenuItems+1)*sizeof(TgMenuItemInfo));
171 
172       for (i=0; i <= gnNumMainMenuItems; i++) {
173          if (gpMainMenuItemInfos[i].cmdid != MENU_TANGRAM2) {
174             memcpy(&pMainMenuItemInfos[j++], &gpMainMenuItemInfos[i],
175                   sizeof(TgMenuItemInfo));
176          }
177       }
178       memcpy(&pMainMenuItemInfos[j], &gpMainMenuItemInfos[i],
179             sizeof(TgMenuItemInfo));
180 
181       gpMainMenuItemInfos = pMainMenuItemInfos;
182       gnNumMainMenuItems--;
183    }
184 }
185 
186 static
UpdateMenubarItemInfo(x,w,h,MenuX,MenuY,TextBBox)187 void UpdateMenubarItemInfo(x, w, h, MenuX, MenuY, TextBBox)
188    int x, w, h, *MenuX, *MenuY;
189    struct BBRec *TextBBox;
190 {
191    if (MenuX != NULL || MenuY != NULL) {
192       int win_x, win_y, main_win_x, main_win_y;
193       unsigned int win_w, win_h, win_brdr_w, win_d;
194       Window root_win;
195 
196       ComputeMainWinXY(&main_win_x, &main_win_y);
197       XGetGeometry(mainDisplay, menubarWindow, &root_win, &win_x,
198             &win_y, &win_w, &win_h, &win_brdr_w, &win_d);
199       if (MenuX != NULL) {
200          *MenuX = main_win_x + win_x + x - 3 -
201                ((menuFontSet==NULL && menuFontPtr==NULL) ? 0 :
202                (menuFontWidth>>1));
203          if (!threeDLook) {
204             *MenuX += 4;
205          }
206       }
207       if (MenuY != NULL) {
208          *MenuY = main_win_y+brdrW+win_y+h+(win_brdr_w<<1)-1;
209       }
210    }
211    if (TextBBox != NULL) {
212       TextBBox->ltx = x-((menuFontSet==NULL && menuFontPtr==NULL) ? 0 :
213             (menuFontWidth>>1));
214       TextBBox->rbx = x+w+((menuFontSet==NULL && menuFontPtr==NULL) ? 0 :
215             menuFontWidth);
216       TextBBox->lty = h-((menuFontSet==NULL && menuFontPtr == NULL) ?
217             initialMenubarWindowH : menuFontHeight);
218       TextBBox->rby = h;
219    }
220 }
221 
222 static
WhichMenubarItem(X,Y,MenuX,MenuY,TextBBox)223 int WhichMenubarItem(X, Y, MenuX, MenuY, TextBBox)
224    int X, Y, *MenuX, *MenuY;
225    struct BBRec *TextBBox;
226 {
227    int i, x=0, w=0, h=0, len=0, gap=0, padding=(windowPadding>>1);
228    int min_x=((menuFontSet==NULL && menuFontPtr==NULL) ? 2 : menuFontWidth);
229 
230    if (noMenubar ||
231          Y < 0 || Y >= menubarWindowH || X < min_x || X >= menubarWindowW) {
232       return INVALID;
233    }
234    if (menuFontSet != NULL || menuFontPtr != NULL) {
235       x = menuFontWidth;
236       h = menuFontHeight;
237       gap = (x<<1);
238       x += padding;
239       h += padding;
240       for (i=0; i < gnNumMenubarItems; i++) {
241          len = strlen(_(gpMenubarItemInfos[i].menu_str));
242          w = MenuTextWidth(menuFontPtr, _(gpMenubarItemInfos[i].menu_str), len);
243          if ((!noMinWinSize || !gnMinimalMenubar || gnAutoWrapMenubar) &&
244                x+w+padding >= menubarWindowW) {
245             if (Y < h) return INVALID;
246             x = menuFontWidth+padding;
247             h += menuFontHeight+padding;
248          }
249          if (Y < h && X < x+w+gap+padding) {
250             if (!colorDisplay && gpMenubarItemInfos[i].cmdid == MENU_COLOR) {
251                return INVALID;
252             }
253             UpdateMenubarItemInfo(x+padding, w, h, MenuX, MenuY, TextBBox);
254             return i;
255          }
256          x += w+gap+padding;
257       }
258    } else {
259       x = 2;
260       h = initialMenubarWindowH;
261       gap = defaultFontWidth+(defaultFontWidth>>1);
262       x += padding;
263       h += padding;
264       for (i=0; i < gnNumMenubarItems; i++) {
265          len = strlen(_(gpMenubarItemInfos[i].menu_str));
266          w = defaultFontWidth*len;
267          if ((!noMinWinSize || !gnMinimalMenubar || gnAutoWrapMenubar) &&
268                x+w+padding >= menubarWindowW) {
269             if (Y < h) return INVALID;
270             x = 2+padding;
271             h += initialMenubarWindowH+padding;
272          }
273          if (Y < h && X < x+w+gap+padding) {
274             if (!colorDisplay && gpMenubarItemInfos[i].cmdid == MENU_COLOR) {
275                return INVALID;
276             }
277             UpdateMenubarItemInfo(x+padding, w, h, MenuX, MenuY, TextBBox);
278             return i;
279          }
280          x += w+gap+padding;
281       }
282    }
283    return INVALID;
284 }
285 
286 static
GetMenubarItemInfo(index,MenuX,MenuY,TextBBox)287 int GetMenubarItemInfo(index, MenuX, MenuY, TextBBox)
288    int index, *MenuX, *MenuY;
289    struct BBRec *TextBBox;
290 {
291    int i, x=0, w=0, h=0, len=0, gap=0, padding=(windowPadding>>1);
292 
293    if (menuFontSet != NULL || menuFontPtr != NULL) {
294       x = menuFontWidth;
295       h = menuFontHeight;
296       gap = (x<<1);
297       x += padding;
298       h += padding;
299       for (i=0; i < gnNumMenubarItems; i++) {
300          len = strlen(_(gpMenubarItemInfos[i].menu_str));
301          w = MenuTextWidth(menuFontPtr, _(gpMenubarItemInfos[i].menu_str), len);
302          if ((!noMinWinSize || !gnMinimalMenubar || gnAutoWrapMenubar) &&
303                x+w+padding >= menubarWindowW) {
304             x = menuFontWidth+padding;
305             h += menuFontHeight+padding;
306          }
307          if (i == index) {
308             UpdateMenubarItemInfo(x+padding, w, h, MenuX, MenuY, TextBBox);
309             return i;
310          }
311          x += w+gap+padding;
312       }
313    } else {
314       x = 2;
315       h = initialMenubarWindowH;
316       gap = defaultFontWidth+(defaultFontWidth>>1);
317       x += padding;
318       h += padding;
319       for (i=0; i < gnNumMenubarItems; i++) {
320          len = strlen(_(gpMenubarItemInfos[i].menu_str));
321          w = defaultFontWidth*len;
322          if ((!noMinWinSize || !gnMinimalMenubar || gnAutoWrapMenubar) &&
323                x+w+padding >= menubarWindowW) {
324             x = 2+padding;
325             h += initialMenubarWindowH+padding;
326          }
327          if (i == index) {
328             UpdateMenubarItemInfo(x+padding, w, h, MenuX, MenuY, TextBBox);
329             return i;
330          }
331          x += w+gap+padding;
332       }
333    }
334    return INVALID;
335 }
336 
SendCommandToSelf(nCmdId,nIndex)337 void SendCommandToSelf(nCmdId, nIndex)
338    int nCmdId, nIndex;
339 {
340    XClientMessageEvent client_ev;
341 
342    memset(&client_ev, 0, sizeof(XClientMessageEvent));
343    client_ev.type = ClientMessage;
344    client_ev.window = mainWindow;
345    client_ev.message_type = executeCmdByIDAtom;
346    client_ev.format = 16;
347    client_ev.data.s[0] = (short)TG_COMMAND;
348    client_ev.data.s[1] = (short)(nCmdId & 0x0ffff);
349    client_ev.data.s[2] = (short)(nIndex & 0x0ffff);
350    XSendEvent(mainDisplay, mainWindow, False, NoEventMask, (XEvent*)&client_ev);
351 }
352 
FindMenuItemByCmdId(menu,cmdid)353 TgMenuItem *FindMenuItemByCmdId(menu, cmdid)
354    TgMenu *menu;
355    int cmdid;
356 {
357    int i, num_items=menu->num_items;
358    TgMenuItem *menuitems=menu->menuitems;
359 
360    for (i=0; i < num_items; i++) {
361       TgMenuItem *menu_item=(&menuitems[i]);
362 
363       if ((menu_item->flags & TGMU_SEPARATOR) == 0) {
364          if (menu_item->cmdid == cmdid) {
365             return menu_item;
366          }
367       }
368    }
369    return NULL;
370 }
371 
FindMenuItemByIndex(menu,index)372 TgMenuItem *FindMenuItemByIndex(menu, index)
373    TgMenu *menu;
374    int index;
375    /* skip TGMU_SEPARATOR */
376 {
377    int i, num_items=menu->num_items;
378    TgMenuItem *menuitems=menu->menuitems;
379 
380    for (i=0; i < num_items; i++) {
381       TgMenuItem *menu_item=(&menuitems[i]);
382 
383       if ((menu_item->flags & TGMU_SEPARATOR) == 0) {
384          if (i == index) {
385             return menu_item;
386          }
387       }
388    }
389    return NULL;
390 }
391 
FindMenuItemBySubMenuInfoPtr(menu,submenu_info)392 TgMenuItem *FindMenuItemBySubMenuInfoPtr(menu, submenu_info)
393    TgMenu *menu;
394    TgMenuInfo *submenu_info;
395 {
396    int i, num_items=menu->num_items;
397    TgMenuItem *menuitems=menu->menuitems;
398 
399    for (i=0; i < num_items; i++) {
400       TgMenuItem *menu_item=(&menuitems[i]);
401 
402       if ((menu_item->flags & TGMU_HAS_SUBMENU) == TGMU_HAS_SUBMENU) {
403          TgMenuItemInfo *create_info=menu_item->submenu_create_info;
404 
405          if (create_info->submenu_info == submenu_info) {
406             return menu_item;
407          }
408       }
409    }
410    return NULL;
411 }
412 
413 static
TgSetMenuItemCheckOrRadioById(menu,cmdid,checked,mask)414 int TgSetMenuItemCheckOrRadioById(menu, cmdid, checked, mask)
415    TgMenu *menu;
416    int cmdid, checked, mask;
417 {
418    TgMenuItem stMenuItem;
419    TgMenuItem *menu_item=NULL;
420 
421    if (menu == NULL) return FALSE;
422 
423    if ((menu_item=FindMenuItemByCmdId(menu, cmdid)) == NULL) {
424       return FALSE;
425    }
426    memset(&stMenuItem, 0, sizeof(TgMenuItem));
427    stMenuItem.checked = checked;
428    if (!TgSetMenuItemInfo(menu_item, mask, &stMenuItem)) {
429       return FALSE;
430    }
431    return TRUE;
432 }
433 
TgSetMenuItemCheckById(menu,cmdid,checked)434 int TgSetMenuItemCheckById(menu, cmdid, checked)
435    TgMenu *menu;
436    int cmdid, checked;
437 {
438    return TgSetMenuItemCheckOrRadioById(menu, cmdid, checked, TGMU_MASK_CHECK);
439 }
440 
TgSetMenuItemRadioById(menu,cmdid,checked)441 int TgSetMenuItemRadioById(menu, cmdid, checked)
442    TgMenu *menu;
443    int cmdid, checked;
444 {
445    return TgSetMenuItemCheckOrRadioById(menu, cmdid, checked, TGMU_MASK_RADIO);
446 }
447 
TgIsMenuItemChecked(menu,index)448 int TgIsMenuItemChecked(menu, index)
449    TgMenu *menu;
450    int index;
451 {
452    TgMenuItem *menuitems=menu->menuitems;
453 
454    if (index >= 0 && index < menu->num_items) {
455       int flags=(menuitems[index].flags);
456 
457       return (menuitems[index].checked &&
458             (flags & TGMU_HAS_CHECK) == TGMU_HAS_CHECK);
459    }
460    return FALSE;
461 }
462 
TgIsMenuItemRadio(menu,index)463 int TgIsMenuItemRadio(menu, index)
464    TgMenu *menu;
465    int index;
466 {
467    TgMenuItem *menuitems=menu->menuitems;
468 
469    if (index >= 0 && index < menu->num_items) {
470       int flags=(menuitems[index].flags);
471 
472       return (menuitems[index].checked &&
473             (flags & TGMU_HAS_RADIO) == TGMU_HAS_RADIO);
474    }
475    return FALSE;
476 }
477 
TgIsMenuItemEnabled(menu,index)478 int TgIsMenuItemEnabled(menu, index)
479    TgMenu *menu;
480    int index;
481 {
482    TgMenuItem *menuitems=menu->menuitems;
483 
484    if (index >= 0 && index < menu->num_items) {
485       int flags=(menuitems[index].flags);
486 
487       return ((flags & TGMU_DISABLED) != TGMU_DISABLED);
488    }
489    return FALSE;
490 }
491 
TgEnableMenuItemById(menu,cmdid,enabled)492 int TgEnableMenuItemById(menu, cmdid, enabled)
493    TgMenu *menu;
494    int cmdid, enabled;
495 {
496    TgMenuItem *menu_item=NULL;
497 
498    if (menu == NULL) return FALSE;
499 
500    if ((menu_item=FindMenuItemByCmdId(menu, cmdid)) == NULL) {
501       return FALSE;
502    }
503    if (enabled) {
504       menu_item->flags &= (~TGMU_DISABLED);
505    } else {
506       menu_item->flags |= TGMU_DISABLED;
507    }
508    return TRUE;
509 }
510 
TgEnableMenuItemByIndex(menu,index,enabled)511 int TgEnableMenuItemByIndex(menu, index, enabled)
512    TgMenu *menu;
513    int index, enabled;
514 {
515    TgMenuItem *menu_item=NULL;
516 
517    if (menu == NULL) return FALSE;
518 
519    if ((menu_item=FindMenuItemByIndex(menu, index)) == NULL) {
520       return FALSE;
521    }
522    if (enabled) {
523       menu_item->flags &= (~TGMU_DISABLED);
524    } else {
525       menu_item->flags |= TGMU_DISABLED;
526    }
527    return TRUE;
528 }
529 
TgEnableMenuItemBySubMenuInfoPtr(menu,submenu_info,enabled)530 int TgEnableMenuItemBySubMenuInfoPtr(menu, submenu_info, enabled)
531    TgMenu *menu;
532    TgMenuInfo *submenu_info;
533    int enabled;
534 {
535    TgMenuItem *menu_item=NULL;
536 
537    if (menu == NULL) return FALSE;
538 
539    if ((menu_item=FindMenuItemBySubMenuInfoPtr(menu, submenu_info)) == NULL) {
540       return FALSE;
541    }
542    if (enabled) {
543       menu_item->flags &= (~TGMU_DISABLED);
544    } else {
545       menu_item->flags |= TGMU_DISABLED;
546    }
547    return TRUE;
548 }
549 
TgClearThreeDButton(dpy,win,gc,bbox,linewidth)550 void TgClearThreeDButton(dpy, win, gc, bbox, linewidth)
551    Display *dpy;
552    Window win;
553    GC gc;
554    struct BBRec *bbox;
555    int linewidth;
556 {
557    int x=bbox->ltx, y=bbox->lty, w=bbox->rbx-bbox->ltx, h=bbox->rby-bbox->lty;
558 
559    XSetForeground(dpy, gc, myLtGryPixel);
560    if (linewidth == 1) {
561       XDrawLine(dpy, win, gc, x, y+h-1, x+w-1, y+h-1);
562       XDrawLine(dpy, win, gc, x+w-1, y+h-1, x+w-1, y);
563       XDrawLine(dpy, win, gc, x, y+h-2, x, y);
564       XDrawLine(dpy, win, gc, x, y, x+w-2, y);
565    } else if (linewidth >= 2) {
566       XDrawLine(dpy, win, gc, x, y+h-1, x+w-1, y+h-1);
567       XDrawLine(dpy, win, gc, x+w-1, y+h-1, x+w-1, y);
568       XDrawLine(dpy, win, gc, x+1, y+h-2, x+w-2, y+h-2);
569       XDrawLine(dpy, win, gc, x+w-2, y+h-2, x+w-2, y+1);
570       XDrawLine(dpy, win, gc, x+1, y+h-3, x+1, y+1);
571       XDrawLine(dpy, win, gc, x+1, y+1, x+w-3, y+1);
572       XDrawLine(dpy, win, gc, x, y+h-2, x, y);
573       XDrawLine(dpy, win, gc, x, y, x+w-2, y);
574    }
575 }
576 
TgDrawThreeDButton(dpy,win,gc,bbox,state,linewidth,button)577 void TgDrawThreeDButton(dpy, win, gc, bbox, state, linewidth, button)
578    Display *dpy;
579    Window win;
580    GC gc;
581    struct BBRec *bbox;
582    int state, linewidth, button; /* button is TRUE if the item is a button */
583 {
584    int x=bbox->ltx, y=bbox->lty, w=bbox->rbx-bbox->ltx, h=bbox->rby-bbox->lty;
585    int pixels[4];
586 
587    memset(pixels, 0, 4*sizeof(int));
588    switch (state) {
589    case TGBS_NORMAL: return;
590    case TGBS_RAISED:
591       if (linewidth == 1) {
592          pixels[0] = myDkGryPixel; /* inner lower right */
593          pixels[1] = myBlackPixel; /* outer lower right */
594          pixels[2] = myLtGryPixel; /* inner upper left */
595          pixels[3] = myWhitePixel; /* outer upper left */
596       } else {
597          pixels[0] = myBlackPixel; /* outer lower right */
598          pixels[1] = myDkGryPixel; /* inner lower right */
599          if (button) {
600             pixels[2] = myLtGryPixel; /* inner upper left */
601             pixels[3] = myWhitePixel; /* outer upper left */
602          } else {
603             pixels[2] = myWhitePixel; /* inner upper left */
604             pixels[3] = myLtGryPixel; /* outer upper left */
605          }
606       }
607       break;
608    case TGBS_LOWRED:
609       if (linewidth == 1) {
610          if (button) {
611             pixels[0] = myDkGryPixel; /* outer lower right */
612             pixels[1] = myBlackPixel; /* inner lower right */ /* useless here */
613             pixels[2] = myWhitePixel; /* inner upper left */ /* useless here */
614             pixels[3] = myLtGryPixel; /* outer upper left */
615          } else {
616             pixels[0] = myWhitePixel; /* outer lower right */
617             pixels[1] = myLtGryPixel; /* inner lower right */ /* useless here */
618             pixels[2] = myBlackPixel; /* inner upper left */ /* useless here */
619             pixels[3] = myDkGryPixel; /* outer upper left */
620          }
621       } else {
622          if (button) {
623             pixels[0] = myLtGryPixel; /* outer lower right */
624             pixels[1] = myWhitePixel; /* inner lower right */
625             pixels[2] = myDkGryPixel; /* inner upper left */
626             pixels[3] = myBlackPixel; /* outer upper left */
627          } else {
628             pixels[0] = myWhitePixel; /* outer lower right */
629             pixels[1] = myLtGryPixel; /* inner lower right */
630             pixels[2] = myBlackPixel; /* inner upper left */
631             pixels[3] = myDkGryPixel; /* outer upper left */
632          }
633       }
634       break;
635    case TGBS_GRAYED: return;
636    }
637    if (linewidth == 1) {
638       XSetForeground(dpy, gc, pixels[0]);
639       XDrawLine(dpy, win, gc, x, y+h-1, x+w-1, y+h-1);
640       XDrawLine(dpy, win, gc, x+w-1, y+h-1, x+w-1, y);
641       XSetForeground(dpy, gc, pixels[3]);
642       XDrawLine(dpy, win, gc, x, y+h-2, x, y);
643       XDrawLine(dpy, win, gc, x, y, x+w-2, y);
644    } else if (linewidth >= 2) {
645       XSetForeground(dpy, gc, pixels[0]);
646       XDrawLine(dpy, win, gc, x, y+h-1, x+w-1, y+h-1);
647       XDrawLine(dpy, win, gc, x+w-1, y+h-1, x+w-1, y);
648       XSetForeground(dpy, gc, pixels[1]);
649       XDrawLine(dpy, win, gc, x+1, y+h-2, x+w-2, y+h-2);
650       XDrawLine(dpy, win, gc, x+w-2, y+h-2, x+w-2, y+1);
651       XSetForeground(dpy, gc, pixels[2]);
652       XDrawLine(dpy, win, gc, x+1, y+h-3, x+1, y+1);
653       XDrawLine(dpy, win, gc, x+1, y+1, x+w-3, y+1);
654       XSetForeground(dpy, gc, pixels[3]);
655       XDrawLine(dpy, win, gc, x, y+h-2, x, y);
656       XDrawLine(dpy, win, gc, x, y, x+w-2, y);
657    }
658 }
659 
TgDrawStipple(dpy,win,gc,stipple,x,y,w,h,fg_pixel,bg_pixel,fill_style)660 void TgDrawStipple(dpy, win, gc, stipple, x, y, w, h, fg_pixel, bg_pixel,
661       fill_style)
662    Display *dpy;
663    Window win;
664    GC gc;
665    Pixmap stipple;
666    int x, y, w, h, fg_pixel, bg_pixel, fill_style;
667 {
668    XGCValues values;
669 
670    values.foreground = fg_pixel;
671    values.background = bg_pixel;
672    values.fill_style = fill_style;
673    values.stipple = stipple;
674    values.ts_x_origin = x;
675    values.ts_y_origin = y;
676    XChangeGC(dpy, gc,
677          GCForeground | GCBackground | GCFillStyle | GCStipple |
678          GCTileStipXOrigin | GCTileStipYOrigin, &values);
679    XFillRectangle(dpy, win, gc, values.ts_x_origin, values.ts_y_origin, w, h);
680    values.fill_style = FillSolid;
681    values.ts_x_origin = 0;
682    values.ts_y_origin = 0;
683    XChangeGC(dpy, gc,
684          GCFillStyle | GCTileStipXOrigin | GCTileStipYOrigin, &values);
685 }
686 
TgDraw2DGrayedPixmap(dpy,win,bitmap,x,y,w,h,fg_pixel,bg_pixel)687 void TgDraw2DGrayedPixmap(dpy, win, bitmap, x, y, w, h, fg_pixel, bg_pixel)
688    Display *dpy;
689    Window win;
690    Pixmap bitmap;
691    int x, y, w, h, fg_pixel, bg_pixel;
692 {
693    XGCValues values;
694    GC gc=NULL;
695 
696    if (threeDLook) {
697       /* not meant for threeDLook */
698       return;
699    }
700    values.foreground = fg_pixel;
701    values.background = bg_pixel;
702    values.function = GXcopy;
703    values.fill_style = FillStippled;
704    values.stipple = patPixmap[SCROLLPAT];
705    values.ts_x_origin = x;
706    values.ts_y_origin = y;
707    values.clip_mask = bitmap;
708    values.clip_x_origin = x;
709    values.clip_y_origin = y;
710    gc = XCreateGC(dpy, win,
711          GCForeground | GCBackground | GCFunction | GCFillStyle |
712          GCStipple | GCTileStipXOrigin | GCTileStipYOrigin |
713          GCClipMask | GCClipXOrigin | GCClipYOrigin, &values);
714    if (gc != NULL) {
715       XFillRectangle(dpy, win, gc, x, y, w, h);
716       XFreeGC(mainDisplay, gc);
717    }
718 }
719 
720 static struct BBRec gstMenubarWinBBox;
721 
722 static
SetMenubarWinBBox()723 void SetMenubarWinBBox()
724 {
725    unsigned int win_w, win_h, win_brdr_w, win_d;
726    int win_x, win_y, main_win_x, main_win_y;
727    Window root_win=None;
728 
729    ComputeMainWinXY(&main_win_x, &main_win_y);
730    XGetGeometry(mainDisplay, menubarWindow, &root_win, &win_x, &win_y,
731          &win_w, &win_h, &win_brdr_w, &win_d);
732    gstMenubarWinBBox.ltx = main_win_x + win_x;
733    gstMenubarWinBBox.lty = main_win_y + win_y;
734    gstMenubarWinBBox.rbx = gstMenubarWinBBox.ltx + win_w;
735    gstMenubarWinBBox.rby = gstMenubarWinBBox.lty + win_h;
736 }
737 
738 static
SetParentMenuWinBBox(menu)739 void SetParentMenuWinBBox(menu)
740    TgMenu *menu;
741 {
742    memcpy(&gstMenubarWinBBox, &menu->parent_menu->bbox, sizeof(struct BBRec));
743 }
744 
745 static
AdjMenuGeometry(menu,pn_orig_x,pn_orig_y,pn_min_pin,pn_max_pin)746 void AdjMenuGeometry(menu, pn_orig_x, pn_orig_y, pn_min_pin, pn_max_pin)
747    TgMenu *menu;
748    int *pn_orig_x, *pn_orig_y, *pn_min_pin, *pn_max_pin;
749 {
750    int dsp_w=DisplayWidth(mainDisplay, mainScreen);
751    int dsp_h=DisplayHeight(mainDisplay, mainScreen);
752    int orig_x=menu->bbox.ltx, orig_y=menu->bbox.lty;
753    int menu_w=menu->bbox.rbx-orig_x+((menu->padding)<<1);
754    int menu_h=menu->bbox.rby-orig_y+((menu->padding)<<1);
755    int min_pin=0, max_pin=0, dx=0, dy=0;
756    TgMenu *parent_menu=menu->parent_menu;
757 
758    if (menu->scroll_start > 0) {
759       switch (menu->type) {
760       case TGMUTYPE_TEXT:
761          menu_w += scrollBarW + menu->padding;
762          menu_h = maxScrollableMenuHeight;
763          break;
764       case TGMUTYPE_COLOR:
765       case TGMUTYPE_BITMAP:
766          menu_w = maxScrollableMenuWidth;
767          menu_h += scrollBarW + menu->padding;
768          break;
769       }
770    }
771    if (orig_x+menu_w >= dsp_w-1-((menu->brdr_w)<<1)) {
772       orig_x = dsp_w - 1 - menu_w + ((menu->padding)<<1);
773       if (parent_menu != NULL) {
774          orig_x = parent_menu->bbox.ltx - 1 - menu_w + ((menu->padding)<<1);
775       }
776    }
777    if (orig_y+menu_h >= dsp_h-1-((menu->brdr_w)<<1)) {
778       orig_y = dsp_h - 1 - menu_h + ((menu->padding)<<1);
779    }
780    if (parent_menu != NULL) {
781       if (orig_x < parent_menu->bbox.ltx-((menu->brdr_w)<<1)) {
782          min_pin = orig_x - mainMenuPinDistance;
783       } else {
784          min_pin = parent_menu->bbox.ltx - ((menu->brdr_w)<<1) -
785                mainMenuPinDistance;
786       }
787       if (orig_x+menu_w+((menu->brdr_w)<<2) >=
788             parent_menu->bbox.rbx+((menu->brdr_w)<<1)) {
789          max_pin = orig_x + menu_w + ((menu->brdr_w)<<2) + mainMenuPinDistance;
790       } else {
791          max_pin = parent_menu->bbox.rbx + ((menu->brdr_w)<<1) +
792                mainMenuPinDistance;
793       }
794    } else {
795       min_pin = orig_x - mainMenuPinDistance;
796       max_pin = orig_x + menu_w + ((menu->brdr_w)<<2) + mainMenuPinDistance;
797    }
798    dx = orig_x - menu->bbox.ltx;
799    dy = orig_y - menu->bbox.lty;
800    menu->bbox.ltx += dx; menu->bbox.lty += dy;
801    menu->bbox.rbx += dx; menu->bbox.rby += dy;
802    *pn_orig_x = orig_x;
803    *pn_orig_y = orig_y;
804    *pn_min_pin = min_pin;
805    *pn_max_pin = max_pin;
806 }
807 
808 static
TgDrawTextMenuItem(menu,menu_item)809 void TgDrawTextMenuItem(menu, menu_item)
810    TgMenu *menu;
811    TgMenuItem *menu_item;
812 {
813    int x=0, baseline=0, y_offset=0;
814    int item_w=(menu_item->bbox.rbx-menu_item->bbox.ltx);
815    int item_h=(menu_item->bbox.rby-menu_item->bbox.lty);
816    int flags=menu_item->flags; /* TGMU_* */
817    int state=menu_item->state; /* TGBS* */
818    int checked=menu_item->checked;
819    int multicolor=((flags & TGMU_MULTICOLOR)==TGMU_MULTICOLOR);
820    int bg_pixel=(threeDLook ? myLtGryPixel : myBgPixel);
821    int fg_pixels[4], bg_pixels[4];
822    XGCValues values;
823 
824    memset(fg_pixels, 0, 4*sizeof(int));
825    memset(bg_pixels, 0, 4*sizeof(int));
826    if (menu->scroll_start > 0 && menu->first_index > 0) {
827       y_offset = (menu->first_index *
828             (((menuFontSet==NULL && menuFontPtr == NULL) ?
829             initialMenubarWindowH : menuFontHeight) + 1));
830    }
831    if (multicolor) {
832       fg_pixels[TGBS_NORMAL] = menu_item->multicolor_pixel;
833       bg_pixels[TGBS_NORMAL] = bg_pixel;
834       fg_pixels[TGBS_GRAYED] = myDkGryPixel;
835       bg_pixels[TGBS_GRAYED] = bg_pixel;
836       /* use reversed video */
837       fg_pixels[TGBS_RAISED] = myBgPixel;
838       bg_pixels[TGBS_RAISED] = menu_item->multicolor_pixel;
839    } else {
840       fg_pixels[TGBS_NORMAL] = myFgPixel;
841       bg_pixels[TGBS_NORMAL] = bg_pixel;
842       fg_pixels[TGBS_GRAYED] = myDkGryPixel;
843       bg_pixels[TGBS_GRAYED] = bg_pixel;
844       /* use reversed video */
845       fg_pixels[TGBS_RAISED] = myBgPixel;
846       bg_pixels[TGBS_RAISED] = myFgPixel;
847    }
848    if (flags & TGMU_DISABLED) state = TGBS_GRAYED;
849 
850    switch (state) {
851    case TGBS_NORMAL: /* not selected */
852    case TGBS_GRAYED: /* disabled */
853    case TGBS_RAISED: /* selected */
854       break;
855    case TGBS_LOWRED: /* not valid for a text menu item state */ return;
856    }
857    XSetForeground(mainDisplay, textMenuGC, bg_pixels[state]);
858    XFillRectangle(mainDisplay, menu->window, textMenuGC,
859          menu_item->bbox.ltx, menu_item->bbox.lty-y_offset,
860          item_w+WINDOW_PADDING-windowPadding, item_h+(threeDLook?0:1));
861    if ((flags & TGMU_SEPARATOR) == TGMU_SEPARATOR) {
862       if (threeDLook) {
863          XSetForeground(mainDisplay, textMenuGC, myDkGryPixel);
864          XDrawLine(mainDisplay, menu->window, textMenuGC,
865                menu_item->bbox.ltx, menu_item->bbox.lty-y_offset,
866                item_w, menu_item->bbox.lty-y_offset);
867          XSetForeground(mainDisplay, textMenuGC, myWhitePixel);
868          XDrawLine(mainDisplay, menu->window, textMenuGC,
869                menu_item->bbox.ltx, menu_item->bbox.lty+1-y_offset,
870                item_w, menu_item->bbox.lty+1-y_offset);
871       } else {
872          XSetForeground(mainDisplay, textMenuGC, myFgPixel);
873          XDrawLine(mainDisplay, menu->window, textMenuGC,
874                menu_item->bbox.ltx, menu_item->bbox.lty+1-y_offset,
875                item_w+1, menu_item->bbox.lty+1-y_offset);
876       }
877       return;
878    }
879    baseline = menu_item->bbox.lty - y_offset +
880          ((menuFontSet==NULL && menuFontPtr==NULL) ?
881          defaultFontAsc : menuFontAsc);
882    x = menu->check_start;
883    if ((flags & (TGMU_HAS_CHECK|TGMU_HAS_RADIO)) != 0 && checked) {
884       int dy=((item_h-check_height)>>1);
885 
886       values.foreground = fg_pixels[state];
887       values.background = bg_pixels[state];
888       values.fill_style = FillStippled;
889       values.stipple = None;
890       if ((flags & TGMU_HAS_CHECK) == TGMU_HAS_CHECK) {
891          values.stipple = checkBitmap;
892       } else if ((flags & TGMU_HAS_RADIO) == TGMU_HAS_RADIO) {
893          values.stipple = radioBitmap;
894       }
895       if (values.stipple != None) {
896          values.ts_x_origin = x;
897          values.ts_y_origin = menu_item->bbox.lty+dy-y_offset;
898          XChangeGC(mainDisplay, textMenuGC,
899                GCForeground | GCBackground | GCFillStyle | GCStipple |
900                GCTileStipXOrigin | GCTileStipYOrigin, &values);
901          XFillRectangle(mainDisplay, menu->window, textMenuGC,
902                values.ts_x_origin, values.ts_y_origin,
903                check_width, check_height);
904          values.fill_style = FillSolid;
905          values.ts_x_origin = 0;
906          values.ts_y_origin = 0;
907          XChangeGC(mainDisplay, textMenuGC,
908                GCFillStyle | GCTileStipXOrigin | GCTileStipYOrigin, &values);
909       }
910    }
911    x = menu->str_start;
912    if (threeDLook) {
913       if (state == TGBS_GRAYED) {
914          XSetForeground(mainDisplay, textMenuGC, myWhitePixel);
915          DrawMenuString(mainDisplay, menu->window, textMenuGC, x+1,
916                baseline+1, _(menu_item->menu_str),
917                strlen(_(menu_item->menu_str)));
918       }
919       XSetForeground(mainDisplay, textMenuGC, fg_pixels[state]);
920       DrawMenuString(mainDisplay, menu->window, textMenuGC, x,
921             baseline, _(menu_item->menu_str), strlen(_(menu_item->menu_str)));
922    } else {
923       if (state == TGBS_GRAYED) {
924          DrawMenuString(mainDisplay, menu->window, revGrayGC, x, baseline,
925                _(menu_item->menu_str), strlen(_(menu_item->menu_str)));
926       } else {
927          XSetForeground(mainDisplay, textMenuGC, fg_pixels[state]);
928          DrawMenuString(mainDisplay, menu->window, textMenuGC, x, baseline,
929                _(menu_item->menu_str), strlen(_(menu_item->menu_str)));
930       }
931    }
932    if ((flags & TGMU_HAS_SHORTCUT) == TGMU_HAS_SHORTCUT) {
933       x = menu->shortcut_start;
934       if (state == TGBS_GRAYED) {
935          XSetForeground(mainDisplay, textMenuGC, myWhitePixel);
936          DrawMenuString(mainDisplay, menu->window, textMenuGC, x+1,
937                baseline+1, menu_item->detail.shortcut_str,
938                strlen(menu_item->detail.shortcut_str));
939       }
940       XSetForeground(mainDisplay, textMenuGC, fg_pixels[state]);
941       DrawMenuString(mainDisplay, menu->window, textMenuGC, x, baseline,
942             menu_item->detail.shortcut_str,
943             strlen(menu_item->detail.shortcut_str));
944    }
945    if ((flags & TGMU_HAS_SUBMENU) == TGMU_HAS_SUBMENU) {
946       int dy=((item_h-check_height)>>1);
947 
948       x = menu->arrow_start;
949 
950       values.foreground = fg_pixels[state];
951       values.background = bg_pixels[state];
952       values.fill_style = FillStippled;
953       if (!threeDLook && state == TGBS_GRAYED) {
954          values.stipple = graySubmenuBitmap;
955       } else {
956          values.stipple = submenuBitmap;
957       }
958       values.ts_x_origin = x;
959       values.ts_y_origin = menu_item->bbox.lty+dy-y_offset;
960       XChangeGC(mainDisplay, textMenuGC,
961             GCForeground | GCBackground | GCFillStyle | GCStipple |
962             GCTileStipXOrigin | GCTileStipYOrigin, &values);
963       XFillRectangle(mainDisplay, menu->window, textMenuGC,
964             values.ts_x_origin, values.ts_y_origin,
965             check_width, check_height);
966       values.fill_style = FillSolid;
967       values.ts_x_origin = 0;
968       values.ts_y_origin = 0;
969       XChangeGC(mainDisplay, textMenuGC,
970             GCFillStyle | GCTileStipXOrigin | GCTileStipYOrigin, &values);
971    }
972 }
973 
974 static
TgDrawBitmapOrColorMenuItem(menu,menu_item)975 void TgDrawBitmapOrColorMenuItem(menu, menu_item)
976    TgMenu *menu;
977    TgMenuItem *menu_item;
978 {
979    int item_x=menu_item->bbox.ltx, item_y=menu_item->bbox.lty, x_offset=0;
980    int item_w=menu_item->bbox.rbx-item_x, item_h=menu_item->bbox.rby-item_y;
981    int flags=menu_item->flags; /* TGMU_* */
982    int state=menu_item->state; /* TGBS* */
983    int checked=menu_item->checked, color_menu=(menu->type==TGMUTYPE_COLOR);
984    int bg_pixel=(threeDLook ? myLtGryPixel : myBgPixel), color_pixel=INVALID;
985    Pixmap bitmap=None;
986 
987    if (menu->scroll_start > 0 && menu->first_index > 0) {
988       x_offset = ((menu->image_w+(windowPadding<<1)) * menu->first_index);
989    }
990    if (color_menu) {
991       color_pixel = (*(int*)(menu_item->menu_str));
992    } else {
993       bitmap = (*(Pixmap*)(menu_item->menu_str));
994    }
995    if (state == TGBS_NORMAL && checked) state = TGBS_LOWRED;
996    if (flags & TGMU_DISABLED) state = TGBS_GRAYED;
997 
998    switch (state) {
999    case TGBS_NORMAL: /* not selected */
1000    case TGBS_RAISED: /* selected, i.e., mouse overed */
1001    case TGBS_LOWRED: /* checked but not selected */
1002    case TGBS_GRAYED: /* not valid for a bitmap menu item state */
1003       break;
1004    }
1005    XSetForeground(mainDisplay, textMenuGC, bg_pixel);
1006    XFillRectangle(mainDisplay, menu->window, textMenuGC,
1007          menu_item->bbox.ltx-x_offset, menu_item->bbox.lty, item_w, item_h);
1008    if ((flags & TGMU_SEPARATOR) == TGMU_SEPARATOR) {
1009       return;
1010    }
1011    if (threeDLook) {
1012       if (x_offset != 0) {
1013          OffsetBBox(&menu_item->bbox, -x_offset, 0, &menu_item->bbox);
1014          TgDrawThreeDButton(mainDisplay, menu->window, textMenuGC,
1015                &menu_item->bbox, state, 2, TRUE);
1016          OffsetBBox(&menu_item->bbox, +x_offset, 0, &menu_item->bbox);
1017       } else {
1018          TgDrawThreeDButton(mainDisplay, menu->window, textMenuGC,
1019                &menu_item->bbox, state, 2, TRUE);
1020       }
1021       if (color_menu) {
1022          TgDrawStipple(mainDisplay, menu->window, textMenuGC,
1023                patPixmap[SOLIDPAT], item_x+menu->padding-x_offset,
1024                item_y+menu->padding, menu->image_w, menu->image_h, color_pixel,
1025                bg_pixel, FillSolid);
1026       } else {
1027          if (state == TGBS_GRAYED) {
1028             TgDrawStipple(mainDisplay, menu->window, textMenuGC, bitmap,
1029                   item_x+menu->padding-x_offset+1, item_y+menu->padding+1,
1030                   menu->image_w, menu->image_h, myWhitePixel, bg_pixel,
1031                   FillStippled);
1032             TgDrawStipple(mainDisplay, menu->window, textMenuGC, bitmap,
1033                   item_x+menu->padding-x_offset, item_y+menu->padding,
1034                   menu->image_w, menu->image_h, myDkGryPixel, bg_pixel,
1035                   FillStippled);
1036          } else {
1037             TgDrawStipple(mainDisplay, menu->window, textMenuGC, bitmap,
1038                   item_x+menu->padding-x_offset, item_y+menu->padding,
1039                   menu->image_w, menu->image_h, myFgPixel, bg_pixel,
1040                   FillStippled);
1041          }
1042       }
1043    } else if (color_menu) {
1044       XSetForeground(mainDisplay, textMenuGC, color_pixel);
1045       XFillRectangle(mainDisplay, menu->window, textMenuGC,
1046             item_x+menu->padding-x_offset, item_y+menu->padding,
1047             menu->image_w, menu->image_h);
1048    } else {
1049       if (state == TGBS_NORMAL) {
1050          XSetStipple(mainDisplay, rasterGC, bitmap);
1051          XFillRectangle(mainDisplay, menu->window, rasterGC,
1052                item_x+menu->padding-x_offset, item_y+menu->padding,
1053                menu->image_w, menu->image_h);
1054       } else if (state == TGBS_GRAYED) {
1055          TgDraw2DGrayedPixmap(mainDisplay, menu->window, bitmap,
1056                item_x+menu->padding-x_offset, item_y+menu->padding,
1057                menu->image_w, menu->image_h, myFgPixel, myBgPixel);
1058       } else {
1059          XSetForeground(mainDisplay, textMenuGC, myFgPixel);
1060          XFillRectangle(mainDisplay, menu->window, textMenuGC,
1061                item_x+menu->padding-x_offset, item_y+menu->padding,
1062                menu->image_w, menu->image_h);
1063          XSetStipple(mainDisplay, rvPixmapMenuGC, bitmap);
1064          XFillRectangle(mainDisplay, menu->window, rvPixmapMenuGC,
1065                item_x+menu->padding-x_offset, item_y+menu->padding,
1066                menu->image_w, menu->image_h);
1067       }
1068    }
1069 }
1070 
TgDrawMenuItem(menu,menu_item)1071 void TgDrawMenuItem(menu, menu_item)
1072    TgMenu *menu;
1073    TgMenuItem *menu_item;
1074 {
1075    switch (menu->type) {
1076    case TGMUTYPE_TEXT:
1077       TgDrawTextMenuItem(menu, menu_item);
1078       break;
1079    case TGMUTYPE_COLOR:
1080    case TGMUTYPE_BITMAP:
1081       TgDrawBitmapOrColorMenuItem(menu, menu_item);
1082       break;
1083    }
1084 }
1085 
TgDrawEntireMenu(menu)1086 void TgDrawEntireMenu(menu)
1087    TgMenu *menu;
1088 {
1089    int i, num_items=menu->num_items;
1090    TgMenuItem *menuitems=menu->menuitems;
1091 
1092    XClearWindow(mainDisplay, menu->window);
1093    if (menu->scroll_start > 0) {
1094       int min_index=0, max_index=0;
1095 
1096       if (menu->type == TGMUTYPE_COLOR || menu->type == TGMUTYPE_BITMAP) {
1097          min_index = menu->first_index*menu->num_rows;
1098          max_index = (menu->first_index+menuColsBeforeScroll)*menu->num_rows;
1099       }
1100       for (i=0; i < num_items; i++) {
1101          switch (menu->type) {
1102          case TGMUTYPE_TEXT:
1103             if (i >= menu->first_index &&
1104                   i < menu->first_index+menuRowsBeforeScroll) {
1105                TgDrawMenuItem(menu, &menuitems[i]);
1106             }
1107             break;
1108          case TGMUTYPE_COLOR:
1109          case TGMUTYPE_BITMAP:
1110             if (i >= min_index && i < max_index) {
1111                TgDrawMenuItem(menu, &menuitems[i]);
1112             }
1113             break;
1114          }
1115       }
1116    } else {
1117       for (i=0; i < num_items; i++) {
1118          TgDrawMenuItem(menu, &menuitems[i]);
1119       }
1120    }
1121    if (threeDLook) {
1122       struct BBRec bbox;
1123 
1124       bbox.ltx = 0;
1125       bbox.lty = 0;
1126       if (menu->scroll_start > 0) {
1127          double start_frac=(double)0;
1128 
1129          switch (menu->type) {
1130          case TGMUTYPE_TEXT:
1131             start_frac = (double)((double)(menu->first_index) /
1132                   (double)(num_items));
1133             bbox.rbx = menu->scroll_start+(windowPadding<<1);
1134             bbox.rby = maxScrollableMenuHeight;
1135             TgDrawThreeDButton(mainDisplay, menu->window, textMenuGC, &bbox,
1136                   TGBS_RAISED, 2, FALSE);
1137             TgDrawScrollBar(mainDisplay, menu->window, VERT_SCROLLBAR,
1138                   bbox.rbx+(windowPadding>>1), (windowPadding>>1), scrollBarW,
1139                   maxScrollableMenuHeight-windowPadding, start_frac,
1140                   menuRowsBeforeScroll, num_items);
1141             bbox.ltx = bbox.rbx;
1142             bbox.lty = 0;
1143             bbox.rbx = bbox.ltx+scrollBarW+windowPadding;
1144             bbox.rby = maxScrollableMenuHeight;
1145             TgDrawThreeDButton(mainDisplay, menu->window, textMenuGC, &bbox,
1146                   TGBS_RAISED, 1, FALSE);
1147             break;
1148          case TGMUTYPE_COLOR:
1149          case TGMUTYPE_BITMAP:
1150             start_frac = (double)((double)(menu->first_index) /
1151                   (double)(menu->num_cols));
1152             bbox.rbx = maxScrollableMenuWidth;
1153             bbox.rby = menu->scroll_start+(windowPadding<<1);
1154             TgDrawThreeDButton(mainDisplay, menu->window, textMenuGC, &bbox,
1155                   TGBS_RAISED, 2, FALSE);
1156             TgDrawScrollBar(mainDisplay, menu->window, HORI_SCROLLBAR,
1157                   (windowPadding>>1), bbox.rby+(windowPadding>>1),
1158                   maxScrollableMenuWidth-windowPadding, scrollBarW, start_frac,
1159                   menuColsBeforeScroll, menu->num_cols);
1160             bbox.ltx = 0;
1161             bbox.lty = bbox.rby;
1162             bbox.rbx = maxScrollableMenuWidth;
1163             bbox.rby = bbox.lty+scrollBarW+windowPadding;
1164             TgDrawThreeDButton(mainDisplay, menu->window, textMenuGC, &bbox,
1165                   TGBS_LOWRED, 1, TRUE);
1166             break;
1167          }
1168       } else {
1169          bbox.rbx = menu->bbox.rbx-menu->bbox.ltx+(windowPadding<<1);
1170          bbox.rby = menu->bbox.rby-menu->bbox.lty+(windowPadding<<1);
1171          TgDrawThreeDButton(mainDisplay, menu->window, textMenuGC, &bbox,
1172                TGBS_RAISED, 2, FALSE);
1173       }
1174    } else if (menu->scroll_start > 0) {
1175       double start_frac=(double)0;
1176 
1177       switch (menu->type) {
1178       case TGMUTYPE_TEXT:
1179          start_frac = (double)((double)(menu->first_index)/(double)(num_items));
1180          TgDrawScrollBar(mainDisplay, menu->window, VERT_SCROLLBAR,
1181                menu->scroll_start, 0, scrollBarW, maxScrollableMenuHeight,
1182                start_frac, menuRowsBeforeScroll, num_items);
1183          MyBox(menu->window, textMenuGC, menu->scroll_start, 0,
1184                menu->scroll_start+scrollBarW, maxScrollableMenuHeight);
1185          break;
1186       case TGMUTYPE_COLOR:
1187       case TGMUTYPE_BITMAP:
1188          start_frac = (double)((double)(menu->first_index) /
1189                (double)(menu->num_cols));
1190          TgDrawScrollBar(mainDisplay, menu->window, HORI_SCROLLBAR,
1191                0, menu->scroll_start, maxScrollableMenuWidth, scrollBarW,
1192                start_frac, menuColsBeforeScroll, menu->num_cols);
1193          MyBox(menu->window, textMenuGC, 0, menu->scroll_start,
1194                maxScrollableMenuWidth, menu->scroll_start+scrollBarW);
1195          break;
1196       }
1197    }
1198 }
1199 
TgWhichMenuIndex(menu,x,y,must_be_in_menu)1200 int TgWhichMenuIndex(menu, x, y, must_be_in_menu)
1201    TgMenu *menu;
1202    int x, y, must_be_in_menu;
1203    /*
1204     * Returns INVALID if clicked outside of the menu.
1205     * Returns -2 if clicked on a separator.
1206     * Returns -3 if clicked on scrollbar.
1207     * Otherwise, returned a menu index.
1208     */
1209 {
1210    int i=0, num_items=menu->num_items;
1211    int first_index=0, max_index_plus_1=num_items, x_offset=0, y_offset=0;
1212 
1213    if (must_be_in_menu) {
1214       if (!(x >= 0 && x < menu->bbox.rbx-menu->bbox.ltx &&
1215             y >= 0 && y < menu->bbox.rby-menu->bbox.lty)) {
1216          return INVALID;
1217       }
1218    }
1219    switch (menu->type) {
1220    case TGMUTYPE_TEXT:
1221       if (menu->scroll_start > 0) {
1222          if (x >= menu->scroll_start+windowPadding) {
1223             return (-3);
1224          }
1225          first_index = menu->first_index;
1226          max_index_plus_1 = menu->first_index+menuRowsBeforeScroll;
1227          if (first_index > 0) {
1228             y_offset = (first_index *
1229                   (((menuFontSet==NULL && menuFontPtr == NULL) ?
1230                   initialMenubarWindowH : menuFontHeight) + 1));
1231          }
1232       }
1233       for (i=first_index; i < max_index_plus_1; i++) {
1234          TgMenuItem *menu_item=(&menu->menuitems[i]);
1235 
1236          if (y >= menu_item->bbox.lty-y_offset &&
1237                y < menu_item->bbox.rby+1-y_offset) {
1238             if (menu_item->flags & TGMU_SEPARATOR) {
1239                return (-2);
1240             }
1241             return i;
1242          }
1243       }
1244       break;
1245    case TGMUTYPE_COLOR:
1246    case TGMUTYPE_BITMAP:
1247       if (menu->scroll_start > 0) {
1248          if (y >= menu->scroll_start+windowPadding) {
1249             return (-3);
1250          }
1251          first_index = (menu->first_index*menu->num_rows);
1252          max_index_plus_1 =
1253                (menu->first_index+menuColsBeforeScroll)*menu->num_rows;
1254          if (first_index > 0) {
1255             x_offset = ((menu->image_w+(windowPadding<<1)) * menu->first_index);
1256          }
1257       }
1258       for (i=first_index; i < num_items; i++) {
1259          TgMenuItem *menu_item=(&menu->menuitems[i]);
1260 
1261          if ((x >= menu_item->bbox.ltx-x_offset &&
1262                x < menu_item->bbox.rbx+1-x_offset) &&
1263                (y >= menu_item->bbox.lty && y < menu_item->bbox.rby+1)) {
1264             if (menu_item->flags & TGMU_SEPARATOR) {
1265                return (-2);
1266             }
1267             return i;
1268          }
1269          if (i+1 == max_index_plus_1) break;
1270       }
1271       break;
1272    }
1273    return INVALID;
1274 }
1275 
1276 static int gnUncheckWhenMoused=FALSE;
1277 
1278 static
TgPointInAnAncestorMenu(menu,root_x,root_y)1279 int TgPointInAnAncestorMenu(menu, root_x, root_y)
1280    TgMenu *menu;
1281    int root_x, root_y;
1282 {
1283    if (menu == NULL) return FALSE;
1284 
1285    if (PointInBBox(root_x, root_y, menu->bbox)) return TRUE;
1286 
1287    return TgPointInAnAncestorMenu(menu->parent_menu, root_x, root_y);
1288 }
1289 
1290 static
TgPointInParentMenuSelectedItem(parent_menu)1291 int TgPointInParentMenuSelectedItem(parent_menu)
1292    TgMenu *parent_menu;
1293 {
1294    Window root_win=None, child_win=None;
1295    unsigned int status;
1296    int parent_root_x, parent_root_y, x=0, y=0;
1297 
1298    XQueryPointer(mainDisplay, parent_menu->window, &root_win,
1299          &child_win, &parent_root_x, &parent_root_y, &x, &y, &status);
1300    return (TgWhichMenuIndex(parent_menu, x, y, FALSE) ==
1301          parent_menu->selected_index);
1302 }
1303 
TgCreatePopUpSubMenu(menu,selected_index)1304 TgMenu *TgCreatePopUpSubMenu(menu, selected_index)
1305    TgMenu *menu;
1306    int selected_index;
1307 {
1308    TgMenu *submenu=NULL;
1309    TgMenuItem *menuitems=menu->menuitems;
1310    TgMenuItemInfo *create_info=menuitems[selected_index].submenu_create_info;
1311 
1312    if (create_info == NULL) return NULL;
1313 
1314    if (create_info->submenu_info->create_proc == TgCreateMenuFromMenuInfo) {
1315       submenu = menuitems[selected_index].detail.submenu =
1316             (create_info->submenu_info->create_proc)(menu, 0, 0,
1317             create_info->submenu_info, FALSE);
1318    } else {
1319       /*
1320        * if the creation procedure is not the default TgCreateMenuFromMenuInfo,
1321        *       the status_str_xlated will be ignored
1322        */
1323       submenu = menuitems[selected_index].detail.submenu =
1324             (create_info->submenu_info->create_proc)(menu, 0, 0,
1325             create_info->submenu_info, INVALID);
1326    }
1327    return submenu;
1328 }
1329 
TgPopUpSubMenu(menu,win_x,win_y)1330 int TgPopUpSubMenu(menu, win_x, win_y)
1331    TgMenu *menu;
1332    int win_x, win_y;
1333 {
1334    int rc=INVALID, new_selected=menu->selected_index;
1335    TgMenuItem *menuitems=menu->menuitems;
1336    TgMenu *submenu=menuitems[new_selected].detail.submenu;
1337    int menu_w=menu->bbox.rbx-menu->bbox.ltx+((menu->padding)<<1);
1338    int i, x=0, y=0, dx=0, dy=0;
1339    int saved_active_menu=INVALID, need_to_restore_active_menu=FALSE;
1340 
1341    submenu->parent_menu = menu;
1342    submenu->disallow_pinning = ((menuitems[new_selected].flags &
1343          TGMU_SUBMENU_PINNABLE) == 0);
1344    if ((menuitems[new_selected].flags &
1345          TGMU_SUBMENU_PINNABLE) == TGMU_SUBMENU_PINNABLE &&
1346          menuitems[new_selected].cmdid != INVALID) {
1347       saved_active_menu = activeMenu;
1348       activeMenu = menuitems[new_selected].cmdid;
1349       need_to_restore_active_menu = TRUE;
1350    }
1351    x = menu_w+win_x-((menu->padding)<<1);
1352    y = menuitems[new_selected].bbox.lty+win_y-menu->padding;
1353    dx = x-submenu->bbox.ltx;
1354    dy = y-submenu->bbox.lty;
1355    submenu->bbox.ltx += dx; submenu->bbox.lty += dy;
1356    submenu->bbox.rbx += dx; submenu->bbox.rby += dy;
1357    for (i=0; i < submenu->num_items; i++) {
1358       submenu->menuitems[i].state = TGBS_NORMAL;
1359    }
1360    if (submenu->refresh_proc != NULL) {
1361       (submenu->refresh_proc)(submenu);
1362    }
1363    rc = TgMenuLoop(submenu);
1364 
1365    if (need_to_restore_active_menu) {
1366       activeMenu = saved_active_menu;
1367    }
1368    return rc;
1369 }
1370 
1371 static
ScrollItemCallback(pv_userdata)1372 int ScrollItemCallback(pv_userdata)
1373    void *pv_userdata;
1374    /* returns TRUE to cancel scrolling */
1375 {
1376    TgMenu *menu=((TgMenu*)pv_userdata);
1377    int v_scroll=TRUE;
1378 
1379    switch (menu->type) {
1380    case TGMUTYPE_TEXT: v_scroll = TRUE; break;
1381    case TGMUTYPE_COLOR: v_scroll = FALSE; break;
1382    case TGMUTYPE_BITMAP: v_scroll = FALSE; break;
1383    }
1384    if (menu->can_scroll == SCRL_UP || menu->can_scroll == SCRL_LF) {
1385       /* can_scroll temporary used as scroll_dir */
1386       if (menu->first_index == 0) return FALSE;
1387       menu->first_index--;
1388    } else if (v_scroll) {
1389       if (menu->num_items <= menuRowsBeforeScroll ||
1390             menu->first_index+menuRowsBeforeScroll == menu->num_items) {
1391          return FALSE;
1392       }
1393       menu->first_index++;
1394    } else {
1395       if (menu->num_cols <= menuColsBeforeScroll ||
1396             menu->first_index+menuColsBeforeScroll == menu->num_cols) {
1397          return FALSE;
1398       }
1399       menu->first_index++;
1400    }
1401    TgDrawEntireMenu(menu);
1402    XSync(mainDisplay, False);
1403 
1404    return FALSE;
1405 }
1406 
1407 static
ScrollPageCallback(pv_userdata)1408 int ScrollPageCallback(pv_userdata)
1409    void *pv_userdata;
1410    /* returns TRUE to cancel scrolling */
1411 {
1412    TgMenu *menu=((TgMenu*)pv_userdata);
1413    int v_scroll=TRUE;
1414 
1415    switch (menu->type) {
1416    case TGMUTYPE_TEXT: v_scroll = TRUE; break;
1417    case TGMUTYPE_COLOR: v_scroll = FALSE; break;
1418    case TGMUTYPE_BITMAP: v_scroll = FALSE; break;
1419    }
1420    if (menu->can_scroll == SCRL_UP || menu->can_scroll == SCRL_LF) {
1421       /* can_scroll temporary used as scroll_dir */
1422       if (menu->first_index == 0) return FALSE;
1423       menu->first_index -= menuRowsBeforeScroll;
1424       if (menu->first_index < 0) menu->first_index = 0;
1425    } else if (v_scroll) {
1426       if (menu->num_items <= menuRowsBeforeScroll ||
1427             menu->first_index+menuRowsBeforeScroll == menu->num_items) {
1428          return FALSE;
1429       }
1430       menu->first_index += menuRowsBeforeScroll;
1431       if (menu->first_index+menuRowsBeforeScroll >= menu->num_items) {
1432          menu->first_index = menu->num_items-menuRowsBeforeScroll;
1433       }
1434    } else {
1435       if (menu->num_cols <= menuColsBeforeScroll ||
1436             menu->first_index+menuColsBeforeScroll == menu->num_cols) {
1437          return FALSE;
1438       }
1439       menu->first_index += menuColsBeforeScroll;
1440       if (menu->first_index+menuColsBeforeScroll >= menu->num_cols) {
1441          menu->first_index = menu->num_cols-menuColsBeforeScroll;
1442       }
1443    }
1444    TgDrawEntireMenu(menu);
1445    XSync(mainDisplay, False);
1446 
1447    return FALSE;
1448 }
1449 
1450 static
DoScrollMenu(menu,scroll_page,scroll_dir,pbbox)1451 int DoScrollMenu(menu, scroll_page, scroll_dir, pbbox)
1452    TgMenu *menu;
1453    int scroll_page, scroll_dir;
1454    struct BBRec *pbbox;
1455 {
1456    int v_scroll=TRUE;
1457    ScrollBtnCallbackInfo sbci;
1458 
1459    /*
1460     * temporary use can_scroll for scroll_dir
1461     * menu->can_scroll must be restored to TRUE before returning
1462     */
1463    menu->can_scroll = scroll_dir;
1464    memset(&sbci, 0, sizeof(ScrollBtnCallbackInfo));
1465 
1466    switch (menu->type) {
1467    case TGMUTYPE_TEXT: v_scroll = TRUE; break;
1468    case TGMUTYPE_COLOR: v_scroll = FALSE; break;
1469    case TGMUTYPE_BITMAP: v_scroll = FALSE; break;
1470    }
1471    if (scroll_page) {
1472       sbci.ms = 200;
1473       sbci.pv_userdata = menu;
1474       sbci.pf_scroll_btn_callback = ScrollPageCallback;
1475       if (TgPressButtonLoop(mainDisplay, menu->window, NULL, &sbci)) {
1476          if (scroll_dir == SCRL_UP || scroll_dir == SCRL_LF) {
1477             if (menu->first_index == 0) {
1478                menu->can_scroll = TRUE;
1479                return TRUE;
1480             }
1481             menu->first_index -= menuRowsBeforeScroll;
1482             if (menu->first_index < 0) menu->first_index = 0;
1483          } else if (v_scroll) {
1484             if (menu->num_items <= menuRowsBeforeScroll ||
1485                   menu->first_index+menuRowsBeforeScroll == menu->num_items) {
1486                menu->can_scroll = TRUE;
1487                return TRUE;
1488             }
1489             menu->first_index += menuRowsBeforeScroll;
1490             if (menu->first_index+menuRowsBeforeScroll >= menu->num_items) {
1491                menu->first_index = menu->num_items-menuRowsBeforeScroll;
1492             }
1493          } else {
1494             if (menu->num_cols <= menuColsBeforeScroll ||
1495                   menu->first_index+menuColsBeforeScroll == menu->num_cols) {
1496                menu->can_scroll = TRUE;
1497                return TRUE;
1498             }
1499             menu->first_index += menuColsBeforeScroll;
1500             if (menu->first_index+menuColsBeforeScroll >= menu->num_cols) {
1501                menu->first_index = menu->num_cols-menuColsBeforeScroll;
1502             }
1503          }
1504       }
1505    } else {
1506       sbci.ms = 50;
1507       sbci.pv_userdata = menu;
1508       sbci.pf_scroll_btn_callback = ScrollItemCallback;
1509       if (TgPressButtonLoop(mainDisplay, menu->window, pbbox, &sbci)) {
1510          if (scroll_dir == SCRL_UP || scroll_dir == SCRL_LF) {
1511             if (menu->first_index == 0) {
1512                menu->can_scroll = TRUE;
1513                return TRUE;
1514             }
1515             menu->first_index--;
1516          } else if (v_scroll) {
1517             if (menu->num_items <= menuRowsBeforeScroll ||
1518                   menu->first_index+menuRowsBeforeScroll == menu->num_items) {
1519                menu->can_scroll = TRUE;
1520                return TRUE;
1521             }
1522             menu->first_index++;
1523          } else {
1524             if (menu->num_cols <= menuColsBeforeScroll ||
1525                   menu->first_index+menuColsBeforeScroll == menu->num_cols) {
1526                menu->can_scroll = TRUE;
1527                return TRUE;
1528             }
1529             menu->first_index++;
1530          }
1531       }
1532    }
1533    menu->can_scroll = TRUE;
1534    return FALSE;
1535 }
1536 
1537 static
DoDragInScrollMenu(menu,menu_w,menu_h,mouse_x,mouse_y,btn_offset)1538 void DoDragInScrollMenu(menu, menu_w, menu_h, mouse_x, mouse_y, btn_offset)
1539    TgMenu *menu;
1540    int menu_w, menu_h, mouse_x, mouse_y, btn_offset;
1541 {
1542    double start_frac=(double)0, frac=(double)0;
1543    int done=FALSE, block_start=0, block_h=0, block_w=0, v_scroll=TRUE;
1544    XEvent ev;
1545 
1546    switch (menu->type) {
1547    case TGMUTYPE_TEXT: v_scroll = TRUE; break;
1548    case TGMUTYPE_COLOR: v_scroll = FALSE; break;
1549    case TGMUTYPE_BITMAP: v_scroll = FALSE; break;
1550    }
1551    if (v_scroll) {
1552       if (menu->num_items <= menuRowsBeforeScroll) return;
1553 
1554       frac = (double)((double)menuRowsBeforeScroll / (double)(menu->num_items));
1555       if (threeDLook) {
1556          block_h = (int)((menu_h-(scrollBarW<<1)) * frac);
1557       } else {
1558          block_h = (int)(menu_h * frac);
1559       }
1560       if (threeDLook) {
1561          block_start = mouse_y + btn_offset;;
1562          start_frac = (double)((double)(block_start-scrollBarW) /
1563                (double)(menu_h-(scrollBarW<<1)));
1564          if (block_start+block_h >= menu_h-scrollBarW) {
1565             menu->first_index = menu->num_items - menuRowsBeforeScroll;
1566          } else {
1567             menu->first_index = (int)(menu->num_items * start_frac);
1568          }
1569       } else {
1570          block_start = mouse_y;
1571          start_frac = (double)((double)(block_start) / (double)(menu_h));
1572          if (block_start+block_h >= menu_h) {
1573             menu->first_index = menu->num_items - menuRowsBeforeScroll;
1574          } else {
1575             menu->first_index = (int)(menu->num_items * start_frac);
1576          }
1577       }
1578    } else {
1579       if (menu->num_cols <= menuColsBeforeScroll) return;
1580 
1581       frac = (double)((double)menuColsBeforeScroll / (double)(menu->num_cols));
1582       if (threeDLook) {
1583          block_w = (int)((menu_w-(scrollBarW<<1)) * frac);
1584       } else {
1585          block_w = (int)(menu_w * frac);
1586       }
1587       if (threeDLook) {
1588          block_start = mouse_x + btn_offset;;
1589          start_frac = (double)((double)(block_start-scrollBarW) /
1590                (double)(menu_w-(scrollBarW<<1)));
1591          if (block_start+block_w >= menu_w-scrollBarW) {
1592             menu->first_index = menu->num_cols - menuColsBeforeScroll;
1593          } else {
1594             menu->first_index = (int)(menu->num_cols * start_frac);
1595          }
1596       } else {
1597          block_start = mouse_x;
1598          start_frac = (double)((double)(block_start) / (double)(menu_w));
1599          if (block_start+block_w >= menu_w) {
1600             menu->first_index = menu->num_cols - menuColsBeforeScroll;
1601          } else {
1602             menu->first_index = (int)(menu->num_cols * start_frac);
1603          }
1604       }
1605    }
1606    TgDrawEntireMenu(menu);
1607 
1608    if (!(menu->parent_menu == NULL && !debugNoPointerGrab)) {
1609       XGrabPointer(mainDisplay, menu->window, False,
1610             PointerMotionMask | ButtonReleaseMask, GrabModeAsync,
1611             GrabModeAsync, None, handCursor, CurrentTime);
1612    }
1613    while (!done) {
1614       XNextEvent(mainDisplay, &ev);
1615 
1616       if (ev.type == Expose || ev.type == VisibilityNotify) {
1617          ExposeEventHandler(&ev, TRUE);
1618       } else if (ev.type == ButtonRelease) {
1619          if (!(menu->parent_menu == NULL && !debugNoPointerGrab)) {
1620             XUngrabPointer(mainDisplay, CurrentTime);
1621             if (debugNoPointerGrab) XSync(mainDisplay, False);
1622          }
1623          done = TRUE;
1624       } else if (ev.type == MotionNotify) {
1625          int new_name_first=0;
1626 
1627          if (v_scroll) {
1628             int y=ev.xmotion.y;
1629 
1630             if (threeDLook) {
1631                y += btn_offset;
1632                start_frac = (double)(((double)(y-scrollBarW)) /
1633                     ((double)(menu_h-(scrollBarW<<1)-block_h)));
1634 
1635                if (y <= scrollBarW) {
1636                   new_name_first = 0;
1637                } else if (y+block_h >= menu_h-scrollBarW) {
1638                   new_name_first = menu->num_items - menuRowsBeforeScroll;
1639                } else {
1640                   new_name_first =
1641                         (int)((menu->num_items-menuRowsBeforeScroll) *
1642                         start_frac);
1643                }
1644             } else {
1645                start_frac = (double)(((double)y) / ((double)menu_h));
1646 
1647                if (y <= 0) {
1648                   new_name_first = 0;
1649                } else if (y+block_h >= menu_h) {
1650                   new_name_first = menu->num_items - menuRowsBeforeScroll;
1651                } else {
1652                   new_name_first = (int)(menu->num_items * start_frac);
1653                }
1654             }
1655          } else {
1656             int x=ev.xmotion.x;
1657 
1658             if (threeDLook) {
1659                x += btn_offset;
1660                start_frac = (double)(((double)(x-scrollBarW)) /
1661                     ((double)(menu_w-(scrollBarW<<1)-block_w)));
1662 
1663                if (x <= scrollBarW) {
1664                   new_name_first = 0;
1665                } else if (x+block_w >= menu_w-scrollBarW) {
1666                   new_name_first = menu->num_cols - menuColsBeforeScroll;
1667                } else {
1668                   new_name_first =
1669                         (int)((menu->num_cols-menuColsBeforeScroll) *
1670                         start_frac);
1671                }
1672             } else {
1673                start_frac = (double)(((double)x) / ((double)menu_w));
1674 
1675                if (x <= 0) {
1676                   new_name_first = 0;
1677                } else if (x+block_w >= menu_w) {
1678                   new_name_first = menu->num_cols - menuColsBeforeScroll;
1679                } else {
1680                   new_name_first = (int)(menu->num_cols * start_frac);
1681                }
1682             }
1683          }
1684          if (menu->first_index != new_name_first) {
1685             menu->first_index = new_name_first;
1686             TgDrawEntireMenu(menu);
1687          }
1688          while (XCheckMaskEvent(mainDisplay, PointerMotionMask, &ev)) ;
1689       }
1690    }
1691 }
1692 
ScrollMenu(menu,x,y,menu_w,menu_h,button_state)1693 void ScrollMenu(menu, x, y, menu_w, menu_h, button_state)
1694    TgMenu *menu;
1695    int x, y, menu_w, menu_h, button_state;
1696 {
1697    int menu_x=x, menu_y=y, x_off=0, y_off=0;
1698    int do_drag=FALSE, btn_offset=0, v_scroll=TRUE;
1699    unsigned int button=Button1;
1700 
1701    if ((button_state & Button1Mask) == Button1Mask) button = Button1;
1702    if ((button_state & Button2Mask) == Button2Mask) button = Button2;
1703    if ((button_state & Button3Mask) == Button3Mask) button = Button3;
1704 
1705    switch (menu->type) {
1706    case TGMUTYPE_TEXT: v_scroll = TRUE; break;
1707    case TGMUTYPE_COLOR: v_scroll = FALSE; break;
1708    case TGMUTYPE_BITMAP: v_scroll = FALSE; break;
1709    }
1710    if (v_scroll) {
1711       menu_x = x-menu->scroll_start-windowPadding;
1712    } else {
1713       menu_y = y-menu->scroll_start-windowPadding;
1714    }
1715    if (!threeDLook && button == Button3) {
1716       if (DoScrollMenu(menu, FALSE, (v_scroll ? SCRL_UP : SCRL_LF), NULL)) {
1717          return;
1718       }
1719    } else if (!threeDLook && button == Button1) {
1720       if (DoScrollMenu(menu, FALSE, (v_scroll ? SCRL_DN : SCRL_RT), NULL)) {
1721          return;
1722       }
1723    } else if (button == Button1) {
1724       if (v_scroll) {
1725          /* vertical scroll bar */
1726          if (menu_y < scrollBarW || menu_y >= menu_h-scrollBarW) {
1727             int which=0;
1728             struct BBRec bbox;
1729 
1730             x_off = menu->scroll_start+(windowPadding<<1)+(windowPadding>>1);
1731             if (menu_y < scrollBarW) {
1732                y_off = (windowPadding>>1);
1733                which = SCRL_UP;
1734                SetBBRec(&bbox, x_off, y_off, x_off+scrollBarW,
1735                      y_off+scrollBarW);
1736             } else {
1737                y_off = -(windowPadding>>1);
1738                which = SCRL_DN;
1739                SetBBRec(&bbox, x_off, y_off+menu_h-scrollBarW, x_off+scrollBarW,
1740                      y_off+menu_h);
1741             }
1742             if (DoScrollMenu(menu, FALSE, which, &bbox)) {
1743                return;
1744             }
1745          } else {
1746             double start_frac=(double)0;
1747             int hit=0;
1748 
1749             start_frac = (menu->num_items > 0) ?
1750                   (double)((double)(menu->first_index) /
1751                   (double)(menu->num_items)) : ((double)0.0);
1752             hit = TgGetScrollHit(menu_x, menu_y, VERT_SCROLLBAR,
1753                   scrollBarW, menu_h, start_frac, menuRowsBeforeScroll,
1754                   menu->num_items, &btn_offset);
1755             if (hit == 0) {
1756                do_drag = TRUE;
1757             } else {
1758                if (DoScrollMenu(menu, TRUE, (hit<0 ? SCRL_UP : SCRL_DN),
1759                      NULL)) {
1760                   return;
1761                }
1762             }
1763          }
1764       } else {
1765          /* horizontal scroll bar */
1766          if (menu_x < scrollBarW || menu_x >= menu_w-scrollBarW) {
1767             int which=0;
1768             struct BBRec bbox;
1769 
1770             y_off = menu->scroll_start+(windowPadding<<1)+(windowPadding>>1);
1771             if (menu_x < scrollBarW) {
1772                x_off = (windowPadding>>1);
1773                which = SCRL_LF;
1774                SetBBRec(&bbox, x_off, y_off, x_off+scrollBarW,
1775                      y_off+scrollBarW);
1776             } else {
1777                x_off = -(windowPadding>>1);
1778                which = SCRL_RT;
1779                SetBBRec(&bbox, x_off+menu_w-scrollBarW, y_off, x_off+menu_w,
1780                      y_off+scrollBarW);
1781             }
1782             if (DoScrollMenu(menu, FALSE, which, &bbox)) {
1783                return;
1784             }
1785          } else {
1786             double start_frac=(double)0;
1787             int hit=0;
1788 
1789             start_frac = (menu->num_items > 0) ?
1790                   (double)((double)(menu->first_index) /
1791                   (double)(menu->num_cols)) : ((double)0.0);
1792             hit = TgGetScrollHit(menu_x, menu_y, HORI_SCROLLBAR,
1793                   menu_w, scrollBarW, start_frac, menuColsBeforeScroll,
1794                   menu->num_cols, &btn_offset);
1795             if (hit == 0) {
1796                do_drag = TRUE;
1797             } else {
1798                if (DoScrollMenu(menu, TRUE, (hit<0 ? SCRL_LF : SCRL_RT),
1799                      NULL)) {
1800                   return;
1801                }
1802             }
1803          }
1804       }
1805    } else if (!threeDLook && button == Button2) {
1806       do_drag = TRUE;
1807    }
1808    if (do_drag) {
1809       DoDragInScrollMenu(menu, menu_w, menu_h, x, y, btn_offset);
1810       return;
1811    }
1812    TgDrawEntireMenu(menu);
1813 }
1814 
TgMenuLoop(menu)1815 int TgMenuLoop(menu)
1816    TgMenu *menu;
1817 {
1818    int menuing=TRUE;
1819    int orig_x=menu->bbox.ltx, orig_y=menu->bbox.lty;
1820    int menu_w=menu->bbox.rbx-orig_x+((menu->padding)<<1);
1821    int menu_h=menu->bbox.rby-orig_y+((menu->padding)<<1);
1822    int min_pin=0, max_pin=0, suspended=FALSE;
1823    TgMenuItem *menuitems=NULL;
1824    Window root_win=None, child_win=None;
1825    TgMenu *parent_menu=menu->parent_menu;
1826    int x, y, rc=INVALID, saved_root_x, saved_root_y;
1827    int bg_pixel=(threeDLook ? myLtGryPixel : myBgPixel);
1828    unsigned int status;
1829    XEvent ev;
1830    XWMHints wmhints;
1831    XSizeHints sizehints;
1832    XSetWindowAttributes win_attrs;
1833    int pinned_x=0, pinned_y=0, menu_pinned=FALSE;
1834    int tmp_disallow_pinning=inSlideShow;
1835    int one_line_status=FALSE;
1836    char status_buf[MAX_STATUS_BTNS+1][MAXSTRING+1];
1837 
1838    SaveStatusStringsIntoBuf(status_buf, &one_line_status);
1839 
1840    if (menu->scroll_start > 0) {
1841       switch (menu->type) {
1842       case TGMUTYPE_TEXT:
1843          menu_w += scrollBarW + menu->padding;
1844          menu_h = maxScrollableMenuHeight;
1845          break;
1846       case TGMUTYPE_COLOR:
1847       case TGMUTYPE_BITMAP:
1848          menu_w = maxScrollableMenuWidth;
1849          menu_h += scrollBarW + menu->padding;
1850          break;
1851       }
1852    }
1853    menuitems = menu->menuitems;
1854 
1855    if (menu->track_menubar && menubarWindow != None) {
1856       SetMenubarWinBBox();
1857    } else if (menu->parent_menu != NULL && menu->track_parent_menu) {
1858       SetParentMenuWinBBox(menu);
1859    }
1860    AdjMenuGeometry(menu, &orig_x, &orig_y, &min_pin, &max_pin);
1861 
1862    if ((menu->window=XCreateSimpleWindow(mainDisplay, rootWindow,
1863          orig_x+windowPadding, orig_y+windowPadding, menu_w, menu_h,
1864          menu->brdr_w, myBorderPixel, bg_pixel)) == 0) {
1865       FailToCreateWindowMessage("TgMenuLoop()", NULL, FALSE);
1866       return rc;
1867    }
1868    win_attrs.save_under = True;
1869    win_attrs.override_redirect = True;
1870    win_attrs.colormap = mainColormap;
1871    XChangeWindowAttributes(mainDisplay, menu->window,
1872          CWSaveUnder | CWOverrideRedirect | CWColormap, &win_attrs);
1873 
1874    wmhints.flags = InputHint | StateHint;
1875    wmhints.input = True;
1876    wmhints.initial_state = NormalState;
1877    XSetWMHints(mainDisplay, menu->window, &wmhints);
1878 
1879    sizehints.flags = PPosition | PSize | USPosition | PMinSize | PMaxSize;
1880    sizehints.x = orig_x+windowPadding;
1881    sizehints.y = orig_y+windowPadding;
1882    sizehints.width = sizehints.min_width = sizehints.max_width = menu_w;
1883    sizehints.height = sizehints.min_height = sizehints.max_height = menu_h;
1884 #ifdef NOTR4MODE
1885    XSetNormalHints(mainDisplay, menu->window, &sizehints);
1886 #else
1887    XSetWMNormalHints(mainDisplay, menu->window, &sizehints);
1888 #endif
1889 
1890    menu->selected_index = INVALID;
1891 
1892    RegisterWM_DELETE_WINDOW(menu->window);
1893    XSetTransientForHint(mainDisplay, menu->window, mainWindow);
1894 
1895 #ifdef MAPBEFORESELECT
1896    XMapWindow(mainDisplay, menu->window);
1897    XSelectInput(mainDisplay, menu->window, StructureNotifyMask |
1898          ExposureMask | KeyPressMask | ButtonPressMask | ButtonReleaseMask |
1899          PointerMotionMask | EnterWindowMask | LeaveWindowMask);
1900 #else
1901    XSelectInput(mainDisplay, menu->window, StructureNotifyMask |
1902          ExposureMask | KeyPressMask | ButtonPressMask | ButtonReleaseMask |
1903          PointerMotionMask | EnterWindowMask | LeaveWindowMask);
1904    XMapWindow(mainDisplay, menu->window);
1905 #endif
1906 
1907    if (menu->parent_menu == NULL &&
1908          !(menu->track_menubar && menubarWindow != None) &&
1909          !menu->track_parent_menu) {
1910       XWarpPointer(mainDisplay,None,rootWindow,0,0,0,0,orig_x-2,orig_y-2);
1911    }
1912    XQueryPointer(mainDisplay, menu->window, &root_win, &child_win,
1913          &saved_root_x, &saved_root_y, &x, &y, &status);
1914 
1915    if (!debugNoPointerGrab) {
1916       XGrabPointer(mainDisplay, menu->window, FALSE,
1917             PointerMotionMask | ButtonPressMask | ButtonReleaseMask,
1918             GrabModeAsync, GrabModeAsync, None, handCursor, CurrentTime);
1919    }
1920    while (menuing) {
1921       int root_x, root_y, any_button_down;
1922 
1923       XNextEvent(mainDisplay, &ev);
1924       if (ev.type == Expose || ev.type == VisibilityNotify) {
1925          if (ev.xany.window == menu->window) {
1926             TgDrawEntireMenu(menu);
1927          } else {
1928             ExposeEventHandler(&ev, FALSE);
1929          }
1930       }
1931       if (!debugNoPointerGrab &&
1932             (ev.type == ButtonPress || ev.type == ButtonRelease)) {
1933          XButtonEvent *button_ev=(&(ev.xbutton));
1934 
1935          XQueryPointer(mainDisplay, menu->window, &root_win, &child_win,
1936                &root_x, &root_y, &x, &y, &status);
1937          x = button_ev->x;
1938          y = button_ev->y;
1939          if (ev.type == ButtonPress) {
1940             switch (button_ev->button) {
1941             case Button1: status = Button1Mask; break;
1942             case Button2: status = Button2Mask; break;
1943             case Button3: status = Button3Mask; break;
1944             }
1945          }
1946       } else {
1947          XQueryPointer(mainDisplay, menu->window, &root_win, &child_win,
1948                &root_x, &root_y, &x, &y, &status);
1949       }
1950       any_button_down = ((status & BUTTONSMASK) != 0);
1951       if (suspended) {
1952          if (!any_button_down) {
1953             continue;
1954          }
1955          suspended = FALSE;
1956          tmp_disallow_pinning = TRUE;
1957       }
1958       if (!any_button_down) {
1959          if (!menu_pinned &&
1960                abs(root_x-saved_root_x)<=2 && abs(root_y-saved_root_y)<=2) {
1961             suspended = TRUE;
1962             continue;
1963          } else if (menu->parent_menu != NULL &&
1964                PointInBBox(root_x, root_y, menu->parent_menu->bbox) &&
1965                TgPointInParentMenuSelectedItem(menu->parent_menu)) {
1966             suspended = TRUE;
1967             continue;
1968          }
1969          if (menu_pinned) {
1970             XDrawRectangle(mainDisplay, rootWindow, revDefaultGC,
1971                   pinned_x, pinned_y, menu_w+(brdrW<<1), menu_h+(brdrW<<1));
1972             XSetSubwindowMode(mainDisplay, revDefaultGC, ClipByChildren);
1973             pinned_x = root_x;
1974             pinned_y = root_y;
1975             TgRealizePinnedMenuWindow(menu, pinned_x, pinned_y,
1976                      menu_w+(brdrW<<1), menu_h+(brdrW<<1));
1977             rc = INVALID;
1978          } else if (x >= 0 && x < menu_w && y >= 0 && y < menu_h) {
1979             rc = TgWhichMenuIndex(menu, x, y, FALSE);
1980             switch (rc) {
1981             case INVALID: break;
1982             case (-2):
1983                /* separator */
1984                rc = INVALID;
1985                suspended = TRUE;
1986                continue;
1987             case (-3):
1988                /* scrollbar */
1989                rc = INVALID;
1990                suspended = TRUE;
1991                continue;
1992             default:
1993                if (menu->menuitems[rc].cmdid != INVALID &&
1994                      ((menu->menuitems[rc].flags & TGMU_DISABLED) ==
1995                      TGMU_DISABLED)) {
1996                   rc = INVALID;
1997                   suspended = TRUE;
1998                   continue;
1999                } else if (menu->is_main_menu) {
2000                   rc = menu->menuitems[rc].cmdid;
2001                } else if (menu->menuitems[rc].cmdid != INVALID &&
2002                      ((menu->menuitems[rc].flags & TGMU_DISABLED) !=
2003                      TGMU_DISABLED)) {
2004                   if (gstMenuDontSendCommandInfo.dont_send_command) {
2005                      gstMenuDontSendCommandInfo.selected_index = rc;
2006                   } else {
2007                      SendCommandToSelf(menu->menuitems[rc].cmdid, rc);
2008                   }
2009                   /*
2010                    * Nothing catches -4, so all the parent menus will close.
2011                    */
2012                   rc = (-4);
2013                }
2014                break;
2015             }
2016          } else {
2017             rc = INVALID;
2018          }
2019          menuing = FALSE;
2020       } else if (menu_pinned) {
2021          XDrawRectangle(mainDisplay, rootWindow, revDefaultGC,
2022                pinned_x, pinned_y, menu_w+(brdrW<<1), menu_h+(brdrW<<1));
2023          pinned_x = root_x;
2024          pinned_y = root_y;
2025          XDrawRectangle(mainDisplay, rootWindow, revDefaultGC,
2026                pinned_x, pinned_y, menu_w+(brdrW<<1), menu_h+(brdrW<<1));
2027       } else if (x >= 0 && x < menu_w && y >=0 && y < menu_h) {
2028          int new_selected=TgWhichMenuIndex(menu, x, y, FALSE);
2029 
2030          if (new_selected == (-3) || menu->selected_index != new_selected) {
2031             if (menu->selected_index != INVALID) {
2032                if (gnUncheckWhenMoused && !threeDLook &&
2033                      menu->type != TGMUTYPE_TEXT &&
2034                      ((menuitems[menu->selected_index].flags &
2035                      TGMU_SEPARATOR) == 0) &&
2036                      menuitems[menu->selected_index].checked) {
2037                   menuitems[menu->selected_index].checked = FALSE;
2038                }
2039                menuitems[menu->selected_index].state = TGBS_NORMAL;
2040                TgDrawMenuItem(menu, &menuitems[menu->selected_index]);
2041             }
2042             if (new_selected >= 0) {
2043                menuitems[new_selected].state = TGBS_RAISED;
2044                TgDrawMenuItem(menu, &menuitems[new_selected]);
2045                if (menuitems[new_selected].status_str != NULL) {
2046                   /* the status_str has been translated */
2047                   SetStringStatus(menuitems[new_selected].status_str);
2048                }
2049             }
2050             switch (new_selected) {
2051             case INVALID: break;
2052             case (-2): new_selected = INVALID; break; /* separator */
2053             case (-3):
2054                /* scrollbar */
2055                if (menu->scroll_start > 0 && tmp_disallow_pinning) {
2056                   /* loop to scroll */
2057                   ScrollMenu(menu, x, y, menu_w, menu_h, (status&BUTTONSMASK));
2058                }
2059                new_selected = INVALID;
2060                XQueryPointer(mainDisplay, menu->window, &root_win, &child_win,
2061                      &root_x, &root_y, &x, &y, &status);
2062                any_button_down = ((status & BUTTONSMASK) != 0);
2063                if (!any_button_down) {
2064                   suspended = TRUE;
2065                }
2066                if (!debugNoPointerGrab) {
2067                   XGrabPointer(mainDisplay, menu->window, FALSE,
2068                         PointerMotionMask | ButtonPressMask | ButtonReleaseMask,
2069                         GrabModeAsync, GrabModeAsync, None, handCursor,
2070                         CurrentTime);
2071                }
2072                break;
2073             default: break;
2074             }
2075             menu->selected_index = new_selected;
2076             if (new_selected != INVALID &&
2077                   (menuitems[new_selected].flags & TGMU_HAS_SUBMENU) ==
2078                   TGMU_HAS_SUBMENU &&
2079                   (menuitems[new_selected].flags & TGMU_DISABLED) == 0 &&
2080                   menuitems[new_selected].submenu_create_info != NULL) {
2081                TgMenu *submenu=NULL;
2082 
2083                submenu = TgCreatePopUpSubMenu(menu, new_selected);
2084                if (submenu != NULL) {
2085 #ifdef _CHAMELEON_BUG
2086                   if (!debugNoPointerGrab) {
2087                      XUngrabPointer(mainDisplay, CurrentTime);
2088                   }
2089 #endif /* _CHAMELEON_BUG */
2090                   rc = TgPopUpSubMenu(menu, orig_x, orig_y);
2091                   submenu = menuitems[new_selected].detail.submenu;
2092                   if (submenu != NULL) {
2093                      TgDestroyMenu(submenu, TRUE);
2094                      menuitems[new_selected].detail.submenu = NULL;
2095                   }
2096                }
2097                if (rc != INVALID && rc != (-3)) {
2098                   menuing = FALSE;
2099                } else if (!debugNoPointerGrab) {
2100                   XGrabPointer(mainDisplay, menu->window, FALSE,
2101                         PointerMotionMask | ButtonPressMask | ButtonReleaseMask,
2102                         GrabModeAsync, GrabModeAsync, None, handCursor,
2103                         CurrentTime);
2104                }
2105             }
2106          }
2107          tmp_disallow_pinning = inSlideShow;
2108       } else if (menu->parent_menu != NULL && menu->track_parent_menu &&
2109             PointInBBox(root_x, root_y, gstMenubarWinBBox) &&
2110             !TgPointInParentMenuSelectedItem(menu->parent_menu)) {
2111          menuing = FALSE;
2112          rc = BAD;
2113       } else if (menu->parent_menu != NULL &&
2114             PointInBBox(root_x, root_y, menu->parent_menu->bbox)) {
2115          /* we are in the parent menu */
2116          if (!TgPointInParentMenuSelectedItem(menu->parent_menu)) {
2117             menuing = FALSE;
2118             rc = (-3);
2119          }
2120       } else if (menu->parent_menu != NULL &&
2121             TgPointInAnAncestorMenu(menu->parent_menu, root_x, root_y)) {
2122          /* we are in an ancestor menu */
2123          menuing = FALSE;
2124          rc = (-3);
2125       } else if (menu->track_menubar && menubarWindow != None &&
2126             PointInBBox(root_x, root_y, gstMenubarWinBBox) &&
2127             WhichMenubarItem(root_x-gstMenubarWinBBox.ltx,
2128             root_y-gstMenubarWinBBox.lty, NULL, NULL, NULL) != INVALID &&
2129             !PointInBBox(root_x-gstMenubarWinBBox.ltx,
2130             root_y-gstMenubarWinBBox.lty, excludeMenubarWinBBox)) {
2131          menuing = FALSE;
2132          rc = BAD;
2133       } else if (!menu->disallow_pinning && !tmp_disallow_pinning &&
2134             !menu_pinned && (activeMenu != INVALID || menu->is_main_menu) &&
2135             (root_x < min_pin || root_x > max_pin)) {
2136          pinned_x = root_x;
2137          pinned_y = root_y;
2138          XSetSubwindowMode(mainDisplay, revDefaultGC, IncludeInferiors);
2139          XDrawRectangle(mainDisplay, rootWindow, revDefaultGC,
2140                pinned_x, pinned_y, menu_w+(brdrW<<1), menu_h+(brdrW<<1));
2141          SetStringStatus(TgLoadCachedString(CSTID_RELEASE_MOUSE_TO_PIN_MENU));
2142          menu_pinned = TRUE;
2143       } else if (menu->selected_index != INVALID) {
2144          menuitems[menu->selected_index].state = TGBS_NORMAL;
2145          TgDrawMenuItem(menu, &menuitems[menu->selected_index]);
2146          menu->selected_index = INVALID;
2147       }
2148    }
2149    if (parent_menu == NULL && !debugNoPointerGrab) {
2150       XUngrabPointer(mainDisplay, CurrentTime);
2151    }
2152    if (menu->window != None) XDestroyWindow(mainDisplay, menu->window);
2153 
2154    XSync(mainDisplay, False);
2155 
2156    RestoreStatusStringsFromBuf(status_buf, one_line_status);
2157 
2158    return rc;
2159 }
2160 
TgDestroyMenu(menu,free_menu)2161 TgMenu *TgDestroyMenu(menu, free_menu)
2162    TgMenu *menu;
2163    int free_menu;
2164    /* return NULL if menu is freed */
2165 {
2166    int i, num_items=menu->num_items;
2167    TgMenuItem *menuitems=NULL;
2168 
2169    menuitems = menu->menuitems;
2170    if (menuitems != NULL) {
2171       for (i=0; i < num_items; i++) {
2172          TgMenuItem *menu_item=(&menuitems[i]);
2173 
2174          UtilFree(menu_item->status_str);
2175          if (menu_item->menu_str_allocated) {
2176             UtilFree(menu_item->menu_str);
2177             menu_item->menu_str = NULL;
2178          }
2179          menu_item->status_str = NULL;
2180          if ((menu_item->flags & TGMU_HAS_SUBMENU) &&
2181                menu_item->detail.submenu != NULL) {
2182             TgDestroyMenu(menu_item->detail.submenu, TRUE);
2183          }
2184       }
2185       free(menuitems);
2186    }
2187    if (free_menu) {
2188       free(menu);
2189       return NULL;
2190    }
2191    return menu;
2192 }
2193 
2194 static
TgGetTextMenuItemWidths(menu_item,pn_check_w,pn_str_w,pn_shortcut_w)2195 int TgGetTextMenuItemWidths(menu_item, pn_check_w, pn_str_w, pn_shortcut_w)
2196    TgMenuItem *menu_item;
2197    int *pn_check_w, *pn_str_w, *pn_shortcut_w;
2198 {
2199    int flags=menu_item->flags;
2200 
2201    if ((flags & TGMU_SEPARATOR) == TGMU_SEPARATOR) {
2202       if (pn_check_w != NULL) *pn_check_w = 0;
2203       if (pn_str_w != NULL) *pn_str_w = 0;
2204       if (pn_shortcut_w != NULL) *pn_shortcut_w = 0;
2205       return TRUE;
2206    }
2207    if (pn_check_w != NULL) {
2208       if ((flags & (TGMU_HAS_CHECK|TGMU_HAS_RADIO)) != 0) {
2209          *pn_check_w = check_width;
2210       } else {
2211          *pn_check_w = 0;
2212       }
2213    }
2214    if (pn_str_w != NULL) {
2215       char *menu_str=menu_item->menu_str;
2216 
2217       if (menuFontSet != NULL || menuFontPtr != NULL) {
2218          *pn_str_w = MenuTextWidth(menuFontPtr, _(menu_str),
2219                strlen(_(menu_str)));
2220       } else {
2221          *pn_str_w = defaultFontWidth * strlen(_(menu_str));
2222       }
2223    }
2224    if (pn_shortcut_w != NULL) {
2225       if (flags & TGMU_HAS_SHORTCUT) {
2226          char *shortcut_str=menu_item->detail.shortcut_str;
2227 
2228          /* don't translate shortuct_str */
2229          if (menuFontSet != NULL || menuFontPtr != NULL) {
2230             *pn_shortcut_w = MenuTextWidth(menuFontPtr, shortcut_str,
2231                   strlen(shortcut_str));
2232          } else {
2233             *pn_shortcut_w = defaultFontWidth * strlen(shortcut_str);
2234          }
2235       } else {
2236          *pn_shortcut_w = 0;
2237       }
2238    }
2239    return TRUE;
2240 }
2241 
TgSetMenuItemInfo(to_menu_item,mask,from_menu_item)2242 int TgSetMenuItemInfo(to_menu_item, mask, from_menu_item)
2243    TgMenuItem *to_menu_item, *from_menu_item;
2244    int mask;
2245 {
2246    if (mask & TGMU_MASK_LTXY) {
2247       to_menu_item->bbox.ltx = from_menu_item->bbox.ltx;
2248       to_menu_item->bbox.lty = from_menu_item->bbox.lty;
2249    }
2250    if (mask & TGMU_SEPARATOR) {
2251       to_menu_item->flags |= TGMU_SEPARATOR;
2252       return TRUE;
2253    }
2254    if (mask & TGMU_MASK_STATE) to_menu_item->state = from_menu_item->state;
2255    if (mask & TGMU_MASK_CMDID) to_menu_item->cmdid = from_menu_item->cmdid;
2256    if (mask & TGMU_MASK_MULTICOLOR) {
2257       to_menu_item->flags |= TGMU_MULTICOLOR;
2258       to_menu_item->multicolor_pixel = from_menu_item->multicolor_pixel;
2259    }
2260    if (mask & TGMU_MASK_PXMPBTN1) {
2261       to_menu_item->checked_pxmpbtn = from_menu_item->checked_pxmpbtn;
2262    }
2263    if (mask & TGMU_MASK_PXMPBTN2) {
2264       to_menu_item->unchecked_pxmpbtn = from_menu_item->unchecked_pxmpbtn;
2265    }
2266    if (mask & TGMU_MASK_CHECK) {
2267       to_menu_item->flags |= TGMU_HAS_CHECK;
2268       to_menu_item->flags &= (~TGMU_HAS_RADIO);
2269       to_menu_item->checked = from_menu_item->checked;
2270    }
2271    if (mask & TGMU_MASK_RADIO) {
2272       to_menu_item->flags |= TGMU_HAS_RADIO;
2273       to_menu_item->flags &= (~TGMU_HAS_CHECK);
2274       to_menu_item->checked = from_menu_item->checked;
2275    }
2276    if (mask & TGMU_MASK_MENUSTR) {
2277       to_menu_item->menu_str = from_menu_item->menu_str;
2278    }
2279    if (mask & TGMU_MASK_STATUSSTR) {
2280       char *psz=NULL;
2281 
2282       UtilFree(to_menu_item->status_str);
2283 
2284       if (mask & TGMU_MASK_RAWSTATUSSTR) {
2285          /* the from_menu_item->status_str has NOT been translated */
2286          to_menu_item->status_str = UtilStrDup(_(from_menu_item->status_str));
2287       } else {
2288          /* the from_menu_item->status_str has been translated */
2289          to_menu_item->status_str = UtilStrDup(from_menu_item->status_str);
2290       }
2291       if (to_menu_item->status_str == NULL) FailAllocMessage();
2292       /* do not translate -- program constants */
2293       if (from_menu_item->status_str != NULL &&
2294             (psz=strstr(from_menu_item->status_str, "<<PROGRAM_NAME>>")) !=
2295             NULL) {
2296          char *psz1=(&psz[strlen("<<PROGRAM_NAME>>")]);
2297 
2298          sprintf(&to_menu_item->status_str[psz-from_menu_item->status_str],
2299                "%s%s", TOOL_NAME, psz1);
2300       }
2301    }
2302    if (mask & TGMU_MASK_SUBMENU) {
2303       to_menu_item->flags |= TGMU_HAS_SUBMENU;
2304       to_menu_item->flags &= (~TGMU_HAS_SHORTCUT);
2305       to_menu_item->submenu_create_info = from_menu_item->submenu_create_info;
2306       if (mask & TGMU_MASK_PINNABLESUBMENU) {
2307          to_menu_item->flags |= TGMU_SUBMENU_PINNABLE;
2308       }
2309    }
2310    if (mask & TGMU_MASK_SHORTCUTSTR) {
2311       to_menu_item->flags |= TGMU_HAS_SHORTCUT;
2312       to_menu_item->flags &= (~TGMU_HAS_SUBMENU);
2313       to_menu_item->detail.shortcut_str = from_menu_item->detail.shortcut_str;
2314    }
2315    if (mask & TGMU_MASK_USERDATA) {
2316       to_menu_item->userdata = from_menu_item->userdata;
2317    }
2318    return TRUE;
2319 }
2320 
2321 static
TgAdjustMenuItemHeight(menu,menu_item)2322 int TgAdjustMenuItemHeight(menu, menu_item)
2323    TgMenu *menu;
2324    TgMenuItem *menu_item;
2325 {
2326    int flags=menu_item->flags, item_w=0, item_h=0;
2327 
2328    if (flags & TGMU_SEPARATOR) {
2329       menu_item->bbox.rby = menu_item->bbox.lty+separatorHeight;
2330       return TRUE;
2331    }
2332    switch (menu->type) {
2333    case TGMUTYPE_TEXT:
2334       if (menuFontSet != NULL || menuFontPtr != NULL) {
2335          menu_item->bbox.rby = menu_item->bbox.lty+menuFontHeight;
2336       } else {
2337          menu_item->bbox.rby = menu_item->bbox.lty+defaultFontHeight;
2338       }
2339       break;
2340    case TGMUTYPE_COLOR:
2341    case TGMUTYPE_BITMAP:
2342       item_w = menu->image_w;
2343       item_h = menu->image_h;
2344       if (threeDLook) {
2345          item_w += (windowPadding<<1);
2346          item_h += (windowPadding<<1);
2347       }
2348       menu_item->bbox.rbx = menu_item->bbox.ltx+item_w;
2349       menu_item->bbox.rby = menu_item->bbox.lty+item_h;
2350       break;
2351    }
2352    return TRUE;
2353 }
2354 
TgAdjustMenuGeometry(menu,image_w,image_h,max_rows)2355 void TgAdjustMenuGeometry(menu, image_w, image_h, max_rows)
2356    TgMenu *menu;
2357    int image_w, image_h, max_rows;
2358    /* only for TGMUTYPE_BITMAP or TGMUTYPE_COLOR */
2359 {
2360    int max_h=0, item_w=image_w, item_h=image_h;
2361    int i, xoff=0, yoff=0, max_col_w=0, max_w=0, num_items=menu->num_items;
2362 
2363    if (threeDLook) {
2364       item_w += (windowPadding<<1);
2365       item_h += (windowPadding<<1);
2366    }
2367    menu->image_w = image_w;
2368    menu->image_h = image_h;
2369    menu->num_rows = max_rows;
2370    menu->num_cols = (((num_items % max_rows) == 0) ? num_items / max_rows :
2371          ((int)(num_items/max_rows))+1);
2372    xoff = menu->padding;
2373    yoff = menu->padding;
2374    for (i=0; i < num_items; i++) {
2375       int flags=TGMU_MASK_LTXY, col_w=0, item_h=0;
2376       TgMenuItem *menu_item=(&menu->menuitems[i]);
2377       TgMenuItem stMenuItem;
2378 
2379       memset(&stMenuItem, 0, sizeof(TgMenuItem));
2380 
2381       stMenuItem.bbox.ltx = xoff;
2382       stMenuItem.bbox.lty = yoff;
2383       if (menu_item->menu_str == TGMUITEM_SEPARATOR) {
2384          flags |= TGMU_SEPARATOR;
2385       }
2386       if (!TgSetMenuItemInfo(menu_item, flags, &stMenuItem)) {
2387          TgDestroyMenu(menu, TRUE); /* frees menu in TgDestroyMenu() */
2388          return;
2389       }
2390       /* now adjust the width and height of menu_item->bbox */
2391       TgAdjustMenuItemHeight(menu, menu_item);
2392       col_w = menu_item->bbox.rbx-menu_item->bbox.ltx;
2393       item_h = menu_item->bbox.rby-menu_item->bbox.lty;
2394       if (col_w > max_col_w) max_col_w = col_w;
2395       yoff += item_h;
2396       if (xoff+max_col_w > max_w) max_w = xoff+max_col_w;
2397       if (yoff > max_h) max_h = yoff;
2398       if (((i+1) % max_rows) == 0) {
2399          xoff += max_col_w;
2400          yoff = menu->padding;
2401       }
2402    }
2403    menu->check_start = menu->str_start = menu->shortcut_start =
2404          menu->arrow_start = xoff;
2405    menu->bbox.rbx = menu->bbox.ltx+max_w-menu->padding;
2406    menu->bbox.rby = menu->bbox.lty+max_h-menu->padding;
2407 
2408    maxScrollableMenuWidth = (menuColsBeforeScroll*item_w) +
2409          (windowPadding<<1);
2410    if (menu->can_scroll && (max_w+windowPadding) > maxScrollableMenuWidth) {
2411       menu->scroll_start = (item_h*max_rows);
2412    }
2413 }
2414 
2415 static
CreateTextMenuItemsFromMenuItemInfo(menu,menu_iteminfos,status_str_xlated)2416 int CreateTextMenuItemsFromMenuItemInfo(menu, menu_iteminfos, status_str_xlated)
2417    TgMenu *menu;
2418    TgMenuItemInfo *menu_iteminfos;
2419    int status_str_xlated;
2420 {
2421    TgMenuItemInfo *item_info=NULL;
2422    int i, xoff=0, yoff=0, max_str_w=0, max_shortcut_w=submenu_width, total_w=0;
2423    int num_items=0;
2424 
2425    for (item_info=menu_iteminfos; item_info->menu_str != NULL; item_info++) {
2426       num_items++;
2427    }
2428    menu->num_items = num_items;
2429    menu->menuitems = (TgMenuItem*)malloc(num_items*sizeof(TgMenuItem));
2430    if (menu->menuitems == NULL) FailAllocMessage();
2431    memset(menu->menuitems, 0, num_items*sizeof(TgMenuItem));
2432 
2433    xoff = menu->padding;
2434    yoff = menu->padding;
2435    for (item_info=menu_iteminfos, i=0; item_info->menu_str != NULL;
2436          item_info++, i++) {
2437       int flags=0, check_w=0, str_w=0, shortcut_w=0;
2438       TgMenuItem *menu_item=(&menu->menuitems[i]);
2439       TgMenuItem stMenuItem;
2440 
2441       memset(menu_item, 0, sizeof(TgMenuItem));
2442       memset(&stMenuItem, 0, sizeof(TgMenuItem));
2443 
2444       stMenuItem.bbox.ltx = xoff;
2445       stMenuItem.bbox.lty = yoff;
2446       if (item_info->menu_str == TGMUITEM_SEPARATOR) {
2447          flags |= TGMU_SEPARATOR | TGMU_MASK_LTXY;
2448       } else {
2449          stMenuItem.state = TGBS_NORMAL;
2450          stMenuItem.cmdid = item_info->cmdid;
2451          stMenuItem.menu_str = item_info->menu_str;
2452          /* the from_menu_item->status_str has been translated */
2453          stMenuItem.status_str = item_info->status_str;
2454          flags |= TGMU_MASK_LTXY | TGMU_MASK_STATE | TGMU_MASK_CMDID |
2455                TGMU_MASK_MENUSTR | TGMU_MASK_STATUSSTR;
2456          if (!status_str_xlated) {
2457             flags |= TGMU_MASK_RAWSTATUSSTR;
2458          }
2459          if (item_info->shortcut_str == TGMUITEM_SUBMENU ||
2460                item_info->shortcut_str == TGMUITEM_PINNABLESUBMENU) {
2461             flags |= TGMU_MASK_SUBMENU;
2462             if (item_info->shortcut_str == TGMUITEM_PINNABLESUBMENU) {
2463                flags |= TGMU_MASK_PINNABLESUBMENU;
2464             }
2465             stMenuItem.submenu_create_info = item_info;
2466          } else if (item_info->shortcut_str != NULL) {
2467             flags |= TGMU_MASK_SHORTCUTSTR;
2468             stMenuItem.detail.shortcut_str = item_info->shortcut_str;
2469          }
2470       }
2471       if (!TgSetMenuItemInfo(menu_item, flags, &stMenuItem)) {
2472          TgDestroyMenu(menu, TRUE); /* frees menu in TgDestroyMenu() */
2473          return FALSE;
2474       }
2475       TgAdjustMenuItemHeight(menu, menu_item);
2476       TgGetTextMenuItemWidths(menu_item, &check_w, &str_w, &shortcut_w);
2477       if (str_w > max_str_w) max_str_w = str_w;
2478       if (shortcut_w > max_shortcut_w) max_shortcut_w = shortcut_w;
2479       yoff += menu_item->bbox.rby-menu_item->bbox.lty+1;
2480    }
2481    menu->check_start = xoff+2;
2482    menu->str_start = menu->check_start+check_width+2;
2483    menu->shortcut_start = menu->str_start+max_str_w+(menuFontWidth<<1)+2;
2484    menu->arrow_start = menu->shortcut_start+max_shortcut_w-submenu_width;
2485    total_w = menu->arrow_start+submenu_width-xoff+2+menuFontWidth;
2486    menu->bbox.rbx = menu->bbox.ltx + total_w;
2487    menu->bbox.rby = menu->bbox.lty + yoff;
2488    for (i=0; i < num_items; i++) {
2489       TgMenuItem *menu_item=(&menu->menuitems[i]);
2490 
2491       menu_item->bbox.rbx = menu->shortcut_start+max_shortcut_w+menuFontWidth;
2492    }
2493    if (menu->can_scroll && yoff > maxScrollableMenuHeight) {
2494       menu->scroll_start = total_w;
2495    }
2496    return TRUE;
2497 }
2498 
2499 static
SetScrollableMenuSize(menu)2500 void SetScrollableMenuSize(menu)
2501    TgMenu *menu;
2502 {
2503    switch (menu->type) {
2504    case TGMUTYPE_TEXT:
2505       maxScrollableMenuHeight = (menuRowsBeforeScroll *
2506             (((menuFontSet==NULL && menuFontPtr == NULL) ?
2507             initialMenubarWindowH : menuFontHeight) + 1)) + (windowPadding<<1);
2508       break;
2509    case TGMUTYPE_COLOR:
2510    case TGMUTYPE_BITMAP:
2511       /*
2512        * Do nothing!
2513        * maxScrollableMenuWidth will be set in TgAdjustMenuGeometry()
2514        */
2515       break;
2516    }
2517 }
2518 
2519 static
CreateBitmapOrColorMenuItemsFromMenuItemInfo(menu,menu_iteminfos,status_str_xlated)2520 int CreateBitmapOrColorMenuItemsFromMenuItemInfo(menu, menu_iteminfos,
2521       status_str_xlated)
2522    TgMenu *menu;
2523    TgMenuItemInfo *menu_iteminfos;
2524    int status_str_xlated;
2525 {
2526    TgMenuItemInfo *item_info=NULL;
2527    int i, xoff=0, yoff=0, num_items=0;
2528 
2529    for (item_info=menu_iteminfos; item_info->menu_str != NULL; item_info++) {
2530       num_items++;
2531    }
2532    menu->num_items = num_items;
2533    menu->menuitems = (TgMenuItem*)malloc(num_items*sizeof(TgMenuItem));
2534    if (menu->menuitems == NULL) FailAllocMessage();
2535    memset(menu->menuitems, 0, num_items*sizeof(TgMenuItem));
2536 
2537    xoff = menu->padding;
2538    yoff = menu->padding;
2539    for (item_info=menu_iteminfos, i=0; item_info->menu_str != NULL;
2540          item_info++, i++) {
2541       int flags=0;
2542       TgMenuItem *menu_item=(&menu->menuitems[i]);
2543       TgMenuItem stMenuItem;
2544 
2545       memset(menu_item, 0, sizeof(TgMenuItem));
2546       memset(&stMenuItem, 0, sizeof(TgMenuItem));
2547 
2548       stMenuItem.bbox.ltx = xoff;
2549       stMenuItem.bbox.lty = yoff;
2550       if (item_info->menu_str == TGMUITEM_SEPARATOR) {
2551          flags |= TGMU_SEPARATOR | TGMU_MASK_LTXY;
2552       } else {
2553          stMenuItem.state = TGBS_NORMAL;
2554          stMenuItem.cmdid = item_info->cmdid;
2555          stMenuItem.menu_str = item_info->menu_str;
2556          /* the from_menu_item->status_str has been translated */
2557          stMenuItem.status_str = item_info->status_str;
2558          flags |= TGMU_MASK_LTXY | TGMU_MASK_STATE | TGMU_MASK_CMDID |
2559                TGMU_MASK_MENUSTR | TGMU_MASK_STATUSSTR;
2560          if (!status_str_xlated) {
2561             flags |= TGMU_MASK_RAWSTATUSSTR;
2562          }
2563          if (item_info->shortcut_str != NULL) {
2564             flags |= TGMU_MASK_SHORTCUTSTR;
2565             stMenuItem.detail.shortcut_str = item_info->shortcut_str;
2566          }
2567       }
2568       if (!TgSetMenuItemInfo(menu_item, flags, &stMenuItem)) {
2569          TgDestroyMenu(menu, TRUE); /* frees menu in TgDestroyMenu() */
2570          return FALSE;
2571       }
2572       TgAdjustMenuItemHeight(menu, menu_item);
2573    }
2574    return TRUE;
2575 }
2576 
TgCreateMenuFromMenuInfo(parent_menu,x,y,menu_info,status_str_xlated)2577 TgMenu *TgCreateMenuFromMenuInfo(parent_menu, x, y, menu_info,
2578       status_str_xlated)
2579    TgMenu *parent_menu;
2580    int x, y, status_str_xlated;
2581    TgMenuInfo *menu_info;
2582 {
2583    TgMenu *menu=(TgMenu*)malloc(sizeof(TgMenu));
2584    TgMenuItemInfo *menu_iteminfos=menu_info->items;
2585 
2586 #ifdef _TGIF_DBG /* debug, do not translate */
2587    if (status_str_xlated == INVALID) {
2588       XUngrabPointer(mainDisplay, CurrentTime);
2589       sprintf(gszMsgBox, "Error: %s() is called with %s=INVALID.",
2590             "TgCreateMenuFromMenuInfo", "status_str_xlated");
2591       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
2592    }
2593 #endif /* _TGIF_DBG */
2594    if (menu == NULL) FailAllocMessage();
2595    memset(menu, 0, sizeof(TgMenu));
2596 
2597    menu->type = (menu_info->type & TGMUTYPE_MASK);
2598    if ((menu_info->type & TGMUTYPE_CANSCROLL) == TGMUTYPE_CANSCROLL) {
2599       menu->can_scroll = TRUE; /* scroll_start and first_index already 0 */
2600    }
2601    menu_info->type &= TGMUTYPE_MASK;
2602 
2603    menu->num_items = 0;
2604    menu->selected_index = INVALID;
2605    menu->bbox.ltx = menu->bbox.rbx = x;
2606    menu->bbox.lty = menu->bbox.rby = y;
2607    menu->padding = (threeDLook ? windowPadding : 0);
2608    menu->brdr_w = (threeDLook ? 0 : (brdrW<<1));
2609    menu->track_menubar = TRUE;
2610    menu->parent_menu = parent_menu;
2611 
2612    SetScrollableMenuSize(menu);
2613 
2614    switch (menu_info->type) {
2615    case TGMUTYPE_TEXT:
2616       if (!CreateTextMenuItemsFromMenuItemInfo(menu, menu_iteminfos,
2617             status_str_xlated)) {
2618          free(menu);
2619          return NULL;
2620       }
2621       break;
2622    case TGMUTYPE_COLOR:
2623    case TGMUTYPE_BITMAP:
2624       if (!CreateBitmapOrColorMenuItemsFromMenuItemInfo(menu, menu_iteminfos,
2625             status_str_xlated)) {
2626          free(menu);
2627          return NULL;
2628       }
2629       break;
2630    }
2631    return menu;
2632 }
2633 
SetScrollableMenuFirstIndex(menu,selected_item_index)2634 void SetScrollableMenuFirstIndex(menu, selected_item_index)
2635    TgMenu *menu;
2636    int selected_item_index;
2637    /* use this only for radio-button style menu, no separators */
2638 {
2639    if (menu->scroll_start > 0) {
2640       int col=0;
2641 
2642       switch (menu->type) {
2643       case TGMUTYPE_TEXT:
2644          if (selected_item_index >= menuRowsBeforeScroll) {
2645             menu->first_index = selected_item_index;
2646             if (menu->first_index < 0) menu->first_index = 0;
2647             if (menu->first_index+menuRowsBeforeScroll >= menu->num_items) {
2648                menu->first_index = menu->num_items-menuRowsBeforeScroll;
2649             }
2650          }
2651          break;
2652       case TGMUTYPE_COLOR:
2653       case TGMUTYPE_BITMAP:
2654          col = (int)(selected_item_index/menu->num_rows);
2655          if (col >= menuColsBeforeScroll) {
2656             menu->first_index = col;
2657             if (menu->first_index < 0) menu->first_index = 0;
2658             if (menu->first_index+menuColsBeforeScroll >= menu->num_cols) {
2659                menu->first_index = menu->num_cols-menuColsBeforeScroll;
2660             }
2661          }
2662          break;
2663       }
2664    }
2665 }
2666 
RefreshMainMenu(menu)2667 int RefreshMainMenu(menu)
2668    TgMenu *menu;
2669 {
2670    int ok=TRUE;
2671 
2672    ok &= TgEnableMenuItemById(menu, MENU_STACKEDPAGE,
2673          (pageLayoutMode==PAGE_STACK));
2674    ok &= TgEnableMenuItemById(menu, MENU_TILEDPAGE,
2675          (pageLayoutMode!=PAGE_STACK));
2676    ok &= TgEnableMenuItemById(menu, MENU_COLOR, colorDisplay);
2677 
2678    return ok;
2679 }
2680 
CreateMainMenu(parent_menu,x,y,menu_info,status_str_xlated)2681 TgMenu *CreateMainMenu(parent_menu, x, y, menu_info, status_str_xlated)
2682    TgMenu *parent_menu;
2683    int x, y;
2684    TgMenuInfo *menu_info;
2685    int status_str_xlated; /* ignored, always 0 */
2686 {
2687    TgMenu *menu=TgCreateMenuFromMenuInfo(parent_menu, x, y, menu_info, FALSE);
2688 
2689    if (menu != NULL) {
2690       menu->track_menubar = FALSE;
2691       menu->is_main_menu = TRUE;
2692       if (!RefreshMainMenu(menu)) {
2693          return TgDestroyMenu(menu, TRUE);
2694       }
2695       menu->refresh_proc = ((RefreshMenuFunc*)RefreshMainMenu);
2696    }
2697    return menu;
2698 }
2699 
MainMenu()2700 int MainMenu()
2701 {
2702    int x=0, y=0, root_x=0, root_y=0;
2703    int rc=INVALID, index=INVALID;
2704    Window root_win=None, child_win=None;
2705    unsigned int status=0;
2706    TgMenu *menu=NULL;
2707 
2708    Msg("");
2709    XQueryPointer(mainDisplay, rootWindow, &root_win, &child_win, &root_x,
2710          &root_y, &x, &y, &status);
2711 
2712    activeMenu = MENU_MAIN;
2713    if (cmdLineTgrm2) {
2714       menu = (mainMenuInfo.create_proc)(NULL, x, y, &mainMenuInfo, 0);
2715    } else {
2716       TgMenuInfo tgmi;
2717 
2718       memcpy(&tgmi, &mainMenuInfo, sizeof(TgMenuInfo));
2719       tgmi.items = gpMainMenuItemInfos;
2720       menu = (tgmi.create_proc)(NULL, x, y, &tgmi, 0);
2721    }
2722    if (menu != NULL) {
2723       index = TgMenuLoop(menu);
2724       TgDestroyMenu(menu, TRUE);
2725    }
2726    activeMenu = INVALID;
2727    if (index == INVALID || index == (-4)) return INVALID;
2728 
2729    return rc;
2730 }
2731 
IsPrefix(Prefix,Str,Rest)2732 int IsPrefix(Prefix, Str, Rest)
2733    char *Prefix, *Str, **Rest;
2734 {
2735    register char *c_ptr=Prefix;
2736 
2737    for (*Rest=Str; *c_ptr!='\0' && **Rest!='\0'; (*Rest)++, c_ptr++) {
2738       if (**Rest != *c_ptr) {
2739          return FALSE;
2740       }
2741    }
2742    return (*c_ptr == '\0' && **Rest == DIR_SEP);
2743 }
2744 
RedrawTitleWindow()2745 void RedrawTitleWindow()
2746 {
2747    int y, len, amount, left;
2748    char s[MAXPATHLENGTH], name[MAXPATHLENGTH], * c_ptr, * rest;
2749    struct BBRec bbox;
2750 
2751    XClearWindow(mainDisplay, titleWindow);
2752 
2753    s[0] = '\0';
2754    if (curFileDefined) {
2755       if (*curSymDir == '\0') {
2756          sprintf(name, "%s%c%s", curDir, DIR_SEP, curFileName);
2757       } else {
2758          sprintf(name, "%s%c%s", curSymDir, DIR_SEP, curFileName);
2759       }
2760       if (IsPrefix(bootDir, name, &rest)) {
2761          c_ptr = ++rest;
2762       } else {
2763          c_ptr = name;
2764       }
2765       FormatFloat(&printMag, gszMsgBox);
2766       sprintf(s, "%s:%s (%s%%)", curDomainName, c_ptr, gszMsgBox);
2767    } else {
2768       FormatFloat(&printMag, gszMsgBox);
2769       sprintf(s, "%s:%s (%s%%)", curDomainName,
2770             TgLoadCachedString(CSTID_SQUARE_BRACK_UNNAMED), gszMsgBox);
2771    }
2772    if (pageLayoutMode==PAGE_STACK && curPage!=NULL) {
2773       sprintf(&s[strlen(s)], " \"%s\"",
2774             (curPage->name==NULL) ? "" : curPage->name);
2775    }
2776    if (fileModified) {
2777       if (IsFiletUnSavable()) {
2778          sprintf(gszMsgBox, " %s",
2779                TgLoadCachedString(CSTID_SQUARE_BRACK_MODIFIED_UNSAV));
2780       } else {
2781          sprintf(gszMsgBox, " %s",
2782                TgLoadCachedString(CSTID_SQUARE_BRACK_MODIFIED));
2783       }
2784       strcat(s, gszMsgBox);
2785    }
2786    if (s[0] != '\0') {
2787       len = strlen(s);
2788       if (msgFontSet != NULL || msgFontPtr != NULL) {
2789          if (msgFontPtr != NULL) {
2790             XSetFont(mainDisplay, defaultGC, msgFontPtr->fid);
2791          }
2792          if (showVersion) {
2793             DrawMsgString(mainDisplay, titleWindow, defaultGC, 1+windowPadding,
2794                   (titleWindowH>>1)+msgFontAsc+1, s, len);
2795          } else {
2796             DrawMsgString(mainDisplay, titleWindow, defaultGC, 1+windowPadding,
2797                   msgFontAsc+1+windowPadding, s, len);
2798          }
2799          XSetFont(mainDisplay, defaultGC, defaultFontPtr->fid);
2800       } else {
2801          if (showVersion) {
2802             DrawMsgString(mainDisplay, titleWindow, defaultGC, 1+windowPadding,
2803                   (titleWindowH>>1)+defaultFontAsc+1, s, len);
2804          } else {
2805             DrawMsgString(mainDisplay, titleWindow, defaultGC, 1+windowPadding,
2806                   defaultFontAsc+1+windowPadding, s, len);
2807          }
2808       }
2809    }
2810    if (showVersion) {
2811       SetFullVersionString();
2812       strcpy(s, fullToolName);
2813 
2814       len = strlen(s);
2815       if (msgFontSet != NULL || msgFontPtr != NULL) {
2816          amount = MsgTextWidth(msgFontPtr, s, len);
2817          left = ((titleWindowW-amount)>>1);
2818          if (msgFontPtr != NULL) {
2819             XSetFont(mainDisplay, defaultGC, msgFontPtr->fid);
2820          }
2821          DrawMsgString(mainDisplay, titleWindow, defaultGC, left,
2822                msgFontAsc+2+(windowPadding>>1), s, len);
2823          XSetFont(mainDisplay, defaultGC, defaultFontPtr->fid);
2824          for (y=4+(windowPadding>>1); y < (titleWindowH>>1)-4; y += 2) {
2825             XDrawLine(mainDisplay, titleWindow, defaultGC, 2+windowPadding, y,
2826                   left-msgFontWidth, y);
2827             XDrawLine(mainDisplay, titleWindow, defaultGC,
2828                   left+amount+msgFontWidth, y, titleWindowW-3, y);
2829          }
2830       } else {
2831          amount = defaultFontWidth * len;
2832          left = ((titleWindowW-amount)>>1);
2833          DrawMsgString(mainDisplay, titleWindow, defaultGC, left,
2834                defaultFontAsc+2+(windowPadding>>1), s, len);
2835          for (y=4+(windowPadding>>1); y < (titleWindowH>>1)-4; y += 2) {
2836             XDrawLine(mainDisplay, titleWindow, defaultGC, 2+windowPadding, y,
2837                   left-defaultFontWidth, y);
2838             XDrawLine(mainDisplay, titleWindow, defaultGC,
2839                   left+amount+defaultFontWidth, y, titleWindowW-3, y);
2840          }
2841       }
2842    }
2843    if (threeDLook) {
2844       bbox.ltx = 0;
2845       bbox.lty = 0;
2846       bbox.rbx = titleWindowW;
2847       bbox.rby = titleWindowH;
2848       TgDrawThreeDButton(mainDisplay, titleWindow, textMenuGC, &bbox,
2849             TGBS_RAISED, 1, FALSE);
2850    }
2851 }
2852 
2853 static struct ObjRec *iconTopObj=NULL, *iconBotObj=NULL;
2854 static struct ObjRec *iconTgifObj=NULL;
2855 static int justIconified=FALSE;
2856 
RedrawIconWindow()2857 void RedrawIconWindow()
2858 {
2859    register struct ObjRec *obj_ptr;
2860 
2861    numRedrawBBox = 0;
2862    for (obj_ptr = iconBotObj; obj_ptr != NULL; obj_ptr = obj_ptr->prev) {
2863       obj_ptr->tmp_parent = NULL;
2864       DrawObj(iconWindow, obj_ptr);
2865    }
2866    if (justIconified && iconTgifObj != NULL && iconTgifObj->fattr != NULL) {
2867       struct ObjRec *obj_ptr;
2868       struct AttrRec *attr_ptr;
2869 
2870       justIconified = FALSE;
2871       if ((attr_ptr=FindAttrWithName(iconTgifObj,"icon_init_exec_obj=",NULL)) !=
2872             NULL && (obj_ptr=FindObjWithName(iconBotObj, iconTgifObj,
2873             attr_ptr->attr_value.s,FALSE,FALSE,NULL,NULL)) != NULL &&
2874             (attr_ptr=FindAttrWithName(obj_ptr,EXEC_ATTR,NULL)) != NULL) {
2875          int saved_intr_check_interval=intrCheckInterval;
2876          int saved_history_depth=historyDepth;
2877 
2878          intrCheckInterval = 1;
2879          historyDepth = 0;
2880          ShowInterrupt(1);
2881 
2882          DoExec(attr_ptr, obj_ptr);
2883 
2884          HideInterrupt();
2885          intrCheckInterval = saved_intr_check_interval;
2886          historyDepth = saved_history_depth;
2887       }
2888    }
2889    justIconified = FALSE;
2890 }
2891 
2892 static char iconFileName[] = "tgificon";
2893 
2894 static
InitIcon()2895 void InitIcon()
2896 {
2897    struct ObjRec *obj_ptr, *saved_tgif_obj;
2898    char s[MAXPATHLENGTH], *c_ptr, msg[MAXSTRING], ext_str[MAXPATHLENGTH];
2899    FILE *fp=NULL;
2900    int ltx=0, lty=0, rbx=0, rby=0, seen_obj=FALSE;
2901    int dx, dy, w, h, len, ext_len, x, y, read_status;
2902    unsigned int icon_w, icon_h;
2903    XSizeHints sizehints;
2904    int tmp_linenum;
2905    char tmp_filename[MAXPATHLENGTH], tmp_filefullpath[MAXPATHLENGTH];
2906 
2907    DelAllPages();
2908    lastPageNum = 1;
2909    InitPage();
2910 
2911    iconWindowCreated = FALSE;
2912    if (((c_ptr=XGetDefault(mainDisplay,TOOL_NAME,"UseWMIconPixmap")) == NULL) ||
2913          UtilStrICmp(c_ptr,"false") != 0) {
2914       return;
2915    }
2916    if (((c_ptr=XGetDefault(mainDisplay,TOOL_NAME,"NoTgifIcon")) != NULL)
2917          && UtilStrICmp(c_ptr,"True") == 0) {
2918       return;
2919    }
2920    strcpy(s, drawPath);
2921    strcat(s, DIR_SEP_STR);
2922    if ((c_ptr=getenv("TGIFICON")) == NULL) {
2923       if ((c_ptr=XGetDefault(mainDisplay,TOOL_NAME,"TGIFICON")) != NULL) {
2924          if (*c_ptr == DIR_SEP) {
2925             strcpy(s, c_ptr);
2926          } else {
2927             strcat(s, c_ptr);
2928          }
2929       } else {
2930          strcat(s, iconFileName);
2931       }
2932    } else if (((int)strlen(c_ptr)) >= 200) {
2933       /* too long, must be an error */
2934       strcat(s, iconFileName);
2935    } else if (*c_ptr == DIR_SEP) {
2936       strcpy(s, c_ptr);
2937    } else {
2938       strcat(s, c_ptr);
2939    }
2940    sprintf(ext_str, ".%s", OBJ_FILE_EXT);
2941    ext_len = strlen(ext_str);
2942    len = strlen(s);
2943 
2944    if (len < ext_len || strcmp(&s[len-ext_len],ext_str) != 0) {
2945       sprintf(&(s[len]), ".%s", OBJ_FILE_EXT);
2946    }
2947    if ((fp=fopen(s, "r")) == NULL) {
2948       /*
2949        * fprintf(stderr, "Warning:  Cannot open the tgif icon file '%s'.\n", s);
2950        */
2951       return;
2952    }
2953 
2954    strcpy(tmp_filefullpath, scanFileFullPath);
2955    strcpy(tmp_filename, scanFileName);
2956    tmp_linenum = scanLineNum;
2957    UtilStrCpyN(scanFileFullPath, sizeof(scanFileFullPath), s);
2958    strcpy(scanFileName, s);
2959    scanLineNum = 0;
2960 
2961    saved_tgif_obj = tgifObj;
2962    InitTgifObj();
2963 
2964    importingFile = TRUE; /* ignore the 'state' predicate */
2965    importingIconFile = TRUE; /* read the 'file_attr' predicate */
2966    readingPageNum = loadedCurPageNum = 0;
2967    foundGoodStateObject = FALSE;
2968    while ((read_status=ReadObj(fp, &obj_ptr)) == TRUE) {
2969       if (obj_ptr != NULL) {
2970          AddObj(NULL, topObj, obj_ptr);
2971          if (!seen_obj) {
2972             seen_obj = TRUE;
2973             ltx = obj_ptr->bbox.ltx; lty = obj_ptr->bbox.lty;
2974             rbx = obj_ptr->bbox.rbx; rby = obj_ptr->bbox.rby;
2975          } else {
2976             if (obj_ptr->bbox.ltx < ltx) ltx = obj_ptr->bbox.ltx;
2977             if (obj_ptr->bbox.lty < lty) lty = obj_ptr->bbox.lty;
2978             if (obj_ptr->bbox.rbx > rbx) rbx = obj_ptr->bbox.rbx;
2979             if (obj_ptr->bbox.rby > rby) rby = obj_ptr->bbox.rby;
2980          }
2981       }
2982    }
2983    strcpy(scanFileFullPath, tmp_filefullpath);
2984    strcpy(scanFileName, tmp_filename);
2985    scanLineNum = tmp_linenum;
2986 
2987    importingFile = FALSE;
2988    importingIconFile = FALSE;
2989 
2990    fclose(fp);
2991 
2992    if (read_status == INVALID) {
2993       sprintf(msg, TgLoadString(STID_ICON_FILEVER_TOO_LARGE), fileVersion);
2994       Msg(s);
2995       CleanUpTgifObj();
2996       tgifObj = saved_tgif_obj;
2997       return;
2998    }
2999 
3000    w = rbx - ltx;
3001    h = rby - lty;
3002    if (w > iconWindowW) {
3003       dx = -ltx;
3004       iconWindowW = w;
3005    } else {
3006       dx = -ltx+((iconWindowW-w)>>1);
3007    }
3008    if (h > iconWindowH) {
3009       dy = -lty;
3010       iconWindowH = h;
3011    } else {
3012       dy = -lty+((iconWindowH-h)>>1);
3013    }
3014    for (obj_ptr=topObj; obj_ptr != NULL; obj_ptr=obj_ptr->next) {
3015       MoveObj(obj_ptr, dx, dy);
3016    }
3017    iconTgifObj = tgifObj;
3018    tgifObj = saved_tgif_obj;
3019 
3020    iconTopObj = topObj;
3021    iconBotObj = botObj;
3022    curPage->top = curPage->bot = topObj = botObj = NULL;
3023 
3024    CleanUpPage();
3025 
3026    sizehints.x = 0;
3027    sizehints.y = 0;
3028 
3029    if ((c_ptr=XGetDefault(mainDisplay,TOOL_NAME,"IconGeometry")) != NULL) {
3030       int bitmask=XParseGeometry(c_ptr, &x, &y, &icon_w, &icon_h);
3031 
3032       if ((bitmask & XValue) && (bitmask & YValue)) {
3033          if (bitmask & XValue) sizehints.x = x;
3034          if (bitmask & YValue) sizehints.y = y;
3035          if (bitmask & XNegative) sizehints.x += DisplayWidth(mainDisplay,
3036                mainScreen) - iconWindowW - (brdrW<<1) - 1;
3037          if (bitmask & YNegative) sizehints.y += DisplayHeight(mainDisplay,
3038                mainScreen) - iconWindowH - (brdrW<<1) - 1;
3039      }
3040    }
3041    if ((iconBaseWindow = XCreateSimpleWindow(mainDisplay, rootWindow,
3042          sizehints.x, sizehints.y, iconWindowW+(brdrW<<1),
3043          iconWindowH+(brdrW<<1), brdrW, myBorderPixel, myBgPixel)) == 0) {
3044       FailToCreateWindowMessage("InitIcon()", NULL, TRUE);
3045    }
3046    if ((iconWindow = XCreateSimpleWindow(mainDisplay, iconBaseWindow, 0, 0,
3047          iconWindowW, iconWindowH, brdrW, myBorderPixel, myBgPixel)) == 0) {
3048       FailToCreateWindowMessage("InitIcon()", NULL, TRUE);
3049    }
3050    XStoreName(mainDisplay, iconBaseWindow, TOOL_NAME);
3051 
3052    XSelectInput(mainDisplay, iconBaseWindow, StructureNotifyMask |
3053          VisibilityChangeMask);
3054    if (((c_ptr=XGetDefault(mainDisplay,TOOL_NAME,"DoubleClickUnIconify")) !=
3055          NULL) && UtilStrICmp(c_ptr,"True") == 0) {
3056       XSelectInput(mainDisplay, iconWindow,
3057             ButtonPressMask | KeyPressMask | ExposureMask);
3058    } else {
3059       XSelectInput(mainDisplay, iconWindow, KeyPressMask | ExposureMask);
3060    }
3061    iconWindowCreated = TRUE;
3062 }
3063 
InitTitle()3064 void InitTitle()
3065 {
3066    InitIcon();
3067 }
3068 
InitMenu()3069 void InitMenu()
3070 {
3071    XGCValues values;
3072    char *c_ptr=NULL;
3073 
3074    values.foreground = myFgPixel;
3075    values.background = (threeDLook ? myLtGryPixel : myBgPixel);
3076    values.fill_style = FillSolid;
3077    /*
3078     * If (menuFontSet) is not NULL, it's okay to set values.font to
3079     *       whatever because it won't get used.
3080     */
3081    values.font = (menuFontPtr==NULL ? defaultFontPtr->fid : menuFontPtr->fid);
3082    textMenuGC = XCreateGC(mainDisplay, rootWindow,
3083          GCForeground | GCBackground | GCFillStyle | GCFont, &values);
3084 
3085    values.foreground = myBgPixel;
3086    values.background = myFgPixel;
3087    values.fill_style = FillStippled;
3088    rvPixmapMenuGC = XCreateGC(mainDisplay, rootWindow,
3089          GCForeground | GCBackground | GCFillStyle | GCFont, &values);
3090 
3091    InitMainMenu();
3092 
3093    BuildMenubarInfo();
3094 
3095    separatorHeight = (threeDLook ? SEPARATOR_PADDING+2 : SEPARATOR_PADDING+1);
3096 
3097    deleteCmdAsCut = FALSE;
3098    if (((c_ptr=XGetDefault(mainDisplay,TOOL_NAME,"DeleteCmdAsCut")) != NULL) &&
3099          UtilStrICmp(c_ptr,"true") == 0) {
3100       /* this X default is obsolete */
3101       deleteCmdAsCut = TRUE;
3102    }
3103    memset(&gstMenuDontSendCommandInfo, 0, sizeof(MenuDontSendCommandInfo));
3104 }
3105 
CleanUpMenu()3106 void CleanUpMenu()
3107 {
3108    struct ObjRec *saved_top_obj, *saved_bot_obj, *saved_tgif_obj;
3109 
3110    XFreeGC(mainDisplay, textMenuGC);
3111    XFreeGC(mainDisplay, rvPixmapMenuGC);
3112    CleanUpMainMenu();
3113    if (stackingWins != NULL) {
3114       free(stackingWins);
3115       stackingWins = NULL;
3116    }
3117    if (iconTgifObj != NULL) {
3118       saved_tgif_obj = tgifObj;
3119       tgifObj = iconTgifObj;
3120       CleanUpTgifObj();
3121       tgifObj = saved_tgif_obj;
3122    }
3123    if (iconTopObj != NULL) {
3124       saved_top_obj = topObj;
3125       saved_bot_obj = botObj;
3126       topObj = iconTopObj;
3127       botObj = iconBotObj;
3128       DelAllObj();
3129       topObj = saved_top_obj;
3130       botObj = saved_bot_obj;
3131    }
3132    if (check_bits == NULL) { }
3133    if (submenu_bits == NULL) { }
3134 
3135    if (!cmdLineTgrm2) {
3136       free(gpMenubarItemInfos);
3137       gpMenubarItemInfos = NULL;
3138       gnNumMenubarItems = 0;
3139 
3140       free(gpMainMenuItemInfos);
3141       gpMainMenuItemInfos = NULL;
3142       gnNumMainMenuItems = 0;
3143    }
3144 }
3145 
SaveDrawWinInfo()3146 void SaveDrawWinInfo()
3147 {
3148    savedZoomScale = zoomScale;
3149    savedZoomedIn = zoomedIn;
3150    savedDrawOrigX = drawOrigX;
3151    savedDrawOrigY = drawOrigY;
3152    savedDrawWinW = drawWinW;
3153    savedDrawWinH = drawWinH;
3154    savedFileModified = fileModified;
3155 }
3156 
UnIconify()3157 void UnIconify()
3158 {
3159    register int j, i;
3160 
3161    if (!iconWindowShown) return;
3162 
3163    iconWindowShown = FALSE;
3164 
3165    zoomScale = savedZoomScale;
3166    zoomedIn = savedZoomedIn;
3167    drawOrigX = savedDrawOrigX;
3168    drawOrigY = savedDrawOrigY;
3169    drawWinW = savedDrawWinW;
3170    drawWinH = savedDrawWinH;
3171    fileModified = savedFileModified;
3172    UpdDrawWinBBox();
3173    SetDefaultDrawWinClipRecs();
3174 
3175 #ifdef notdef
3176    XUnmapWindow(mainDisplay, iconWindow);
3177 #endif
3178    if (iconWindowCreated) XUnmapWindow(mainDisplay, iconBaseWindow);
3179    XMapWindow(mainDisplay, mainWindow);
3180 
3181    for (i=0; i < numExtraWins; i++) {
3182       if (extraWinInfo[i].raise && !extraWinInfo[i].mapped &&
3183             extraWinInfo[i].window != None) {
3184          XMapRaised(mainDisplay, extraWinInfo[i].window);
3185          extraWinInfo[i].mapped = TRUE;
3186       }
3187    }
3188    for (i=0; i < numStacking; i++) {
3189       for (j=0; j < numExtraWins; j++) {
3190          if (extraWinInfo[j].raise && extraWinInfo[j].window==stackingWins[i]) {
3191             extraWinInfo[j].mapped = TRUE;
3192             break;
3193          }
3194       }
3195       XMapRaised(mainDisplay, stackingWins[i]);
3196    }
3197    XFlush(mainDisplay);
3198    XSync(mainDisplay, False);
3199 }
3200 
Iconify()3201 void Iconify()
3202 {
3203    register int i;
3204 
3205    if (iconWindowShown) return;
3206 
3207    iconWindowShown = TRUE;
3208 
3209    SaveDrawWinInfo();
3210    zoomScale = 0;
3211    zoomedIn = FALSE;
3212    drawOrigX = 0;
3213    drawOrigY = 0;
3214    drawWinW = iconWindowW;
3215    drawWinH = iconWindowH;
3216    UpdDrawWinBBox();
3217    SetDefaultIconWinClipRecs();
3218 
3219    justIconified = TRUE;
3220 
3221 #ifdef notdef
3222    XUnmapWindow(mainDisplay, mainWindow);
3223 #endif
3224 
3225    SaveStackingOrder();
3226 
3227    if (pinnedMainMenu) XUnmapWindow(mainDisplay, mainMenuWindow);
3228    for (i = 0; i < numExtraWins; i++) {
3229       if (extraWinInfo[i].raise && extraWinInfo[i].mapped &&
3230             extraWinInfo[i].window != None) {
3231          XUnmapWindow(mainDisplay, extraWinInfo[i].window);
3232          extraWinInfo[i].mapped = FALSE;
3233       }
3234    }
3235    if (iconWindowCreated) {
3236       XMapWindow(mainDisplay, iconBaseWindow);
3237       XMapWindow(mainDisplay, iconWindow);
3238    }
3239 }
3240 
3241 static int iconJustClicked=FALSE;
3242 static Time iconClickTime;
3243 
IconEventHandler(input)3244 void IconEventHandler(input)
3245    XEvent *input;
3246 {
3247    XEvent ev;
3248    Time click_time;
3249 
3250    if (input->xany.window == iconWindow && input->type == ButtonPress) {
3251       XButtonEvent *button_ev=(&(input->xbutton));
3252 
3253       if (iconWindowShown && !justIconified && button_ev->button == Button2 &&
3254             (button_ev->state & (ShiftMask | ControlMask))) {
3255          justIconified = TRUE;
3256          RedrawIconWindow();
3257       } else {
3258          click_time = input->xbutton.time;
3259          if (iconJustClicked &&
3260                (click_time-iconClickTime)<doubleClickInterval) {
3261             iconJustClicked = FALSE;
3262             UnIconify();
3263          } else {
3264             iconJustClicked = TRUE;
3265             iconClickTime = click_time;
3266          }
3267       }
3268    } else if (input->xany.window==iconBaseWindow && input->type==UnmapNotify) {
3269       UnIconify();
3270    } else if (input->xany.window==iconBaseWindow && input->type==MapNotify) {
3271       Iconify();
3272 /*
3273    } else if (input->xany.window==iconBaseWindow && (input->type==MapNotify ||
3274          input->type == VisibilityNotify && input->xvisibility.state ==
3275          VisibilityUnobscured)) {
3276       Iconify();
3277  */
3278    } else if (input->xany.window == iconWindow && input->type == Expose) {
3279       if (!iconWindowShown) return;
3280 
3281       while (XCheckWindowEvent(mainDisplay, iconWindow, ExposureMask, &ev)) ;
3282       while (XCheckWindowEvent(mainDisplay, iconBaseWindow,
3283             StructureNotifyMask, &ev)) ;
3284       RedrawIconWindow();
3285    }
3286 }
3287 
TitleEventHandler(input)3288 void TitleEventHandler(input)
3289    XEvent *input;
3290 {
3291    XEvent ev;
3292 
3293    if (input->type == Expose) {
3294       XSync(mainDisplay, False);
3295       while (XCheckWindowEvent(mainDisplay, titleWindow, ExposureMask, &ev)) ;
3296       RedrawTitleWindow();
3297    } else if (input->type == EnterNotify) {
3298       SetMouseStatus(TgLoadCachedString(CSTID_PARANED_NONE),
3299             TgLoadCachedString(CSTID_MAIN_MENU),
3300             TgLoadCachedString(CSTID_MAIN_MENU));
3301    } else if (input->type == ButtonPress && (input->xbutton.button == Button2 ||
3302          input->xbutton.button == Button3)) {
3303       MainMenu();
3304    }
3305 }
3306 
CalcMenubarWindowHeight()3307 void CalcMenubarWindowHeight()
3308    /* Given menubarWindowW, set manubarWindowH to fit everything */
3309 {
3310    int i, x=0, w=0, h=0, gap=0, padding=(windowPadding>>1);
3311 
3312    BuildMenubarInfo();
3313 
3314    if (menuFontSet != NULL || menuFontPtr != NULL) {
3315       x = menuFontWidth;
3316       h = menuFontHeight;
3317       gap = (x<<1);
3318       x += padding;
3319       h += padding;
3320       for (i=0; i < gnNumMenubarItems; i++) {
3321          w = MenuTextWidth(menuFontPtr, _(gpMenubarItemInfos[i].menu_str),
3322                strlen(_(gpMenubarItemInfos[i].menu_str)));
3323          if ((!noMinWinSize || !gnMinimalMenubar || gnAutoWrapMenubar) &&
3324                x+w+padding >= menubarWindowW) {
3325             x = menuFontWidth+padding;
3326             h += menuFontHeight+padding;
3327          }
3328          x += w+gap+padding;
3329       }
3330       x += (padding<<1);
3331       h += (padding<<1);
3332    } else {
3333       x = 2;
3334       h = initialMenubarWindowH;
3335       gap = defaultFontWidth+(defaultFontWidth>>1);
3336       x += padding;
3337       h += padding;
3338       for (i=0; i < gnNumMenubarItems; i++) {
3339          w = defaultFontWidth*strlen(_(gpMenubarItemInfos[i].menu_str));
3340          if ((!noMinWinSize || !gnMinimalMenubar || gnAutoWrapMenubar) &&
3341                x+w+padding >= menubarWindowW) {
3342             x = 2+padding;
3343             h += initialMenubarWindowH+padding;
3344          }
3345          x += w+gap+padding;
3346       }
3347       x += (padding<<1);
3348       h += (padding<<1);
3349    }
3350    menubarWindowH = h;
3351 }
3352 
3353 static
HighLightMenubarString(item_str,bbox,highlight)3354 void HighLightMenubarString(item_str, bbox, highlight)
3355    char *item_str;
3356    struct BBRec *bbox;
3357    int highlight;
3358    /* HighLightMenubarString() will use _(item_str) internally */
3359 {
3360    if (threeDLook) {
3361       struct BBRec real_bbox;
3362 
3363       real_bbox.ltx = bbox->ltx-2;
3364       real_bbox.lty = bbox->lty;
3365       real_bbox.rbx = bbox->rbx+2;
3366       real_bbox.rby = bbox->rby+1;
3367       if (highlight) {
3368          TgDrawThreeDButton(mainDisplay, menubarWindow, textMenuGC, &real_bbox,
3369                TGBS_RAISED, 1, FALSE);
3370       } else {
3371          TgClearThreeDButton(mainDisplay, menubarWindow, textMenuGC, &real_bbox,
3372                1);
3373       }
3374    } else {
3375       int fg_pixel=(highlight ? myBgPixel : myFgPixel);
3376       int bg_pixel=(highlight ? myFgPixel :
3377             (threeDLook?myLtGryPixel:myBgPixel));
3378 
3379       XSetForeground(mainDisplay, textMenuGC, bg_pixel);
3380       XFillRectangle(mainDisplay, menubarWindow, textMenuGC,
3381             bbox->ltx-2, bbox->lty, bbox->rbx-bbox->ltx+4, bbox->rby-bbox->lty);
3382       XSetForeground(mainDisplay, textMenuGC, fg_pixel);
3383       if (menuFontSet != NULL || menuFontPtr != NULL) {
3384          DrawMenuString(mainDisplay, menubarWindow, textMenuGC,
3385                bbox->ltx+(menuFontWidth>>1), menuFontAsc+bbox->lty,
3386                _(item_str), strlen(_(item_str)));
3387       } else {
3388          DrawMenuString(mainDisplay, menubarWindow, textMenuGC, bbox->ltx,
3389                defaultFontAsc+bbox->lty, _(item_str), strlen(_(item_str)));
3390       }
3391    }
3392 }
3393 
RedrawMenubarWindow()3394 void RedrawMenubarWindow()
3395 {
3396    int i, x=0, y=0, w=0, len=0, gap=0, padding=(windowPadding>>1);
3397    struct BBRec bbox;
3398 
3399    XClearWindow(mainDisplay, menubarWindow);
3400 
3401    XSetForeground(mainDisplay, textMenuGC, myFgPixel);
3402    if (menuFontSet != NULL || menuFontPtr != NULL) {
3403       x = menuFontWidth;
3404       y = menuFontAsc;
3405       gap = (x<<1);
3406       x += padding;
3407       y += padding;
3408 
3409       for (i=0; i < gnNumMenubarItems; i++) {
3410          len = strlen(_(gpMenubarItemInfos[i].menu_str));
3411          w = MenuTextWidth(menuFontPtr, _(gpMenubarItemInfos[i].menu_str), len);
3412          if ((!noMinWinSize || !gnMinimalMenubar || gnAutoWrapMenubar) &&
3413                x+w+padding >= menubarWindowW) {
3414             x = menuFontWidth+padding;
3415             y += menuFontHeight+padding;
3416          }
3417          if (!colorDisplay && gpMenubarItemInfos[i].cmdid == MENU_COLOR) {
3418             /* disable color menu -- at this point, threeDLook must be FALSE */
3419             DrawMenuString(mainDisplay, menubarWindow, revGrayGC, x+padding,
3420                   y+padding, _(gpMenubarItemInfos[i].menu_str), len);
3421          } else {
3422             DrawMenuString(mainDisplay, menubarWindow, textMenuGC, x+padding,
3423                   y+padding, _(gpMenubarItemInfos[i].menu_str), len);
3424          }
3425          x += w+gap+padding;
3426       }
3427    } else {
3428       x = 2;
3429       y = defaultFontAsc;
3430       gap = defaultFontWidth+(defaultFontWidth>>1);
3431       x += padding;
3432       y += padding;
3433 
3434       for (i=0; i < gnNumMenubarItems; i++) {
3435          len = strlen(_(gpMenubarItemInfos[i].menu_str));
3436          w = defaultFontWidth*len;
3437          if ((!noMinWinSize || !gnMinimalMenubar || gnAutoWrapMenubar) &&
3438                x+w+padding >= menubarWindowW) {
3439             x = 2+padding;
3440             y += initialMenubarWindowH+padding;
3441          }
3442          if (!colorDisplay && gpMenubarItemInfos[i].cmdid == MENU_COLOR) {
3443             /* disable color menu -- at this point, threeDLook must be FALSE */
3444             DrawMenuString(mainDisplay, menubarWindow, revGrayGC, x+padding,
3445                   y+padding, _(gpMenubarItemInfos[i].menu_str), len);
3446          } else {
3447             DrawMenuString(mainDisplay, menubarWindow, textMenuGC, x+padding,
3448                   y+padding, _(gpMenubarItemInfos[i].menu_str), len);
3449          }
3450          x += w+gap+padding;
3451       }
3452    }
3453    if (threeDLook) {
3454       bbox.ltx = 0;
3455       bbox.lty = 0;
3456       bbox.rbx = menubarWindowW;
3457       bbox.rby = menubarWindowH;
3458       TgDrawThreeDButton(mainDisplay, menubarWindow, textMenuGC, &bbox,
3459             TGBS_RAISED, 1, FALSE);
3460    }
3461    if (excludeMenubarIndex != INVALID) {
3462       struct BBRec text_bbox;
3463       int exclude_gap=((menuFontSet==NULL && menuFontPtr==NULL) ?
3464             (defaultFontWidth<<1) : (menuFontWidth<<1));
3465 
3466       SetBBRec(&text_bbox,
3467             excludeMenubarWinBBox.ltx+2+windowPadding,
3468             excludeMenubarWinBBox.lty+2+windowPadding,
3469             excludeMenubarWinBBox.rbx-2-windowPadding-exclude_gap,
3470             excludeMenubarWinBBox.rby-2-windowPadding);
3471       /* HighLightMenubarString() doesn't need _() */
3472       HighLightMenubarString(gpMenubarItemInfos[excludeMenubarIndex].menu_str,
3473             &text_bbox, TRUE);
3474    }
3475 }
3476 
3477 static
PullDownFromMenubar(index,x,y,text_bbox)3478 int PullDownFromMenubar(index, x, y, text_bbox)
3479    int index, x, y;
3480    struct BBRec *text_bbox;
3481 {
3482    int rc=BAD;
3483    int exclude_gap=((menuFontSet==NULL && menuFontPtr==NULL) ?
3484          (defaultFontWidth<<1) : (menuFontWidth<<1));
3485 
3486    while (rc == BAD) {
3487       if (index != INVALID) {
3488          /* HighLightMenubarString() doesn't need _() */
3489          HighLightMenubarString(gpMenubarItemInfos[index].menu_str, text_bbox,
3490                TRUE);
3491          excludeMenubarWinBBox.ltx = text_bbox->ltx-2-windowPadding;
3492          excludeMenubarWinBBox.lty = text_bbox->lty-2-windowPadding;
3493          excludeMenubarWinBBox.rbx = text_bbox->rbx+exclude_gap+2+windowPadding;
3494          excludeMenubarWinBBox.rby = text_bbox->rby+2+windowPadding;
3495          excludeMenubarIndex = index;
3496       }
3497       switch (gpMenubarItemInfos[index].cmdid) {
3498       case MENU_FILE: rc = FileMenu(x, y, TRUE); break;
3499       case MENU_EDIT: rc = EditMenu(x, y, TRUE); break;
3500       case MENU_LAYOUT: rc = LayoutMenu(x, y, TRUE); break;
3501       case MENU_ARRANGE: rc = ArrangeMenu(x, y, TRUE); break;
3502       case MENU_PROPERTIES: rc = PropertiesMenu(x, y, TRUE); break;
3503       case MENU_MOVEMODE: rc = MoveModeMenu(x, y, TRUE); break;
3504       case MENU_PAGE: rc = PageMenu(x, y, TRUE); break;
3505       case MENU_PAGELAYOUT: rc = PageLayoutMenu(x, y, TRUE); break;
3506       case MENU_HORIALIGN: rc = HoriAlignMenu(x, y, TRUE); break;
3507       case MENU_VERTALIGN: rc = VertAlignMenu(x, y, TRUE); break;
3508       case MENU_FONT: rc = FontMenu(x, y, TRUE); break;
3509       case MENU_STYLE: rc = StyleMenu(x, y, TRUE); break;
3510       case MENU_SIZE: rc = SizeMenu(x, y, TRUE); break;
3511       case MENU_SHAPE: rc = ShapeMenu(x, y, TRUE); break;
3512       case MENU_STRETCHTEXT: rc = StretchableTextModeMenu(x, y, TRUE); break;
3513       case MENU_LINEDASH: rc = LineDashMenu(x, y, TRUE); break;
3514       case MENU_LINESTYLE: rc = LineStyleMenu(x, y, TRUE); break;
3515       case MENU_LINETYPE: rc = LineTypeMenu(x, y, TRUE); break;
3516       case MENU_LINEWIDTH: rc = LineWidthMenu(x, y, TRUE); break;
3517       case MENU_FILL: rc = FillMenu(x, y, TRUE); break;
3518       case MENU_PEN: rc = PenMenu(x, y, TRUE); break;
3519       case MENU_TRANSPAT: rc = TransPatModeMenu(x, y, TRUE); break;
3520       case MENU_COLOR: rc = ColorMenu(x, y, TRUE); break;
3521       case MENU_IMAGEPROC: rc = ImageProcMenu(x, y, TRUE); break;
3522       case MENU_NAVIGATE: rc = NavigateMenu(x, y, TRUE); break;
3523       case MENU_SPECIAL: rc = SpecialMenu(x, y, TRUE); break;
3524       case MENU_HELP: rc = HelpMenu(x, y, TRUE); break;
3525       case MENU_TANGRAM2:
3526          if (cmdLineTgrm2) {
3527             rc = Tangram2Menu(x, y, TRUE);
3528          }
3529          break;
3530       }
3531       if (index != INVALID) {
3532          /* HighLightMenubarString() doesn't need _() */
3533          HighLightMenubarString(gpMenubarItemInfos[index].menu_str, text_bbox,
3534                FALSE);
3535       }
3536       if (rc == BAD) {
3537          Window root_win, child_win;
3538          int mouse_x, mouse_y, root_x, root_y;
3539          unsigned int status;
3540 
3541          XQueryPointer(mainDisplay, menubarWindow, &root_win, &child_win,
3542                &root_x, &root_y, &mouse_x, &mouse_y, &status);
3543          index = WhichMenubarItem(mouse_x, mouse_y, &x, &y, text_bbox);
3544          if (!(status & BUTTONSMASK) && index == (-1)) {
3545             return INVALID;
3546          }
3547       } else if (rc == (-4)) {
3548          return INVALID;
3549       } else if (index != MENU_FILE) {
3550          return INVALID;
3551       }
3552    }
3553    return rc;
3554 }
3555 
3556 static int curRaisedMenuItem=INVALID;
3557 
MenubarEventHandler(input)3558 int MenubarEventHandler(input)
3559    XEvent *input;
3560 {
3561    XEvent ev;
3562    int rc=INVALID;
3563 
3564    if (input->type == Expose) {
3565       XSync(mainDisplay, False);
3566       while (XCheckWindowEvent(mainDisplay,menubarWindow,ExposureMask,&ev)) ;
3567       RedrawMenubarWindow();
3568    } else if (input->type == EnterNotify || input->type == LeaveNotify) {
3569       SetMouseStatus("", "", "");
3570       if (curRaisedMenuItem != INVALID) {
3571          struct BBRec text_bbox;
3572 
3573          GetMenubarItemInfo(curRaisedMenuItem, NULL, NULL, &text_bbox);
3574          /* HighLightMenubarString() doesn't need _() */
3575          HighLightMenubarString(gpMenubarItemInfos[curRaisedMenuItem].menu_str,
3576                &text_bbox, FALSE);
3577          curRaisedMenuItem = INVALID;
3578       }
3579    } else if (input->type == MotionNotify) {
3580       int index;
3581 
3582       index = WhichMenubarItem(input->xmotion.x, input->xmotion.y,
3583             NULL, NULL, NULL);
3584       if (index == INVALID) {
3585          SetMouseStatusToAllNone();
3586       } else {
3587          SetMouseStatus("", _(gpMenubarItemInfos[index].status_str), "");
3588       }
3589       if (threeDLook) {
3590          if (index != curRaisedMenuItem) {
3591             struct BBRec text_bbox;
3592 
3593             if (curRaisedMenuItem != INVALID) {
3594                GetMenubarItemInfo(curRaisedMenuItem, NULL, NULL, &text_bbox);
3595                /* HighLightMenubarString() doesn't need _() */
3596                HighLightMenubarString(
3597                      gpMenubarItemInfos[curRaisedMenuItem].menu_str,
3598                      &text_bbox, FALSE);
3599                curRaisedMenuItem = INVALID;
3600             }
3601             if (index != INVALID) {
3602                GetMenubarItemInfo(index, NULL, NULL, &text_bbox);
3603                /* HighLightMenubarString() doesn't need _() */
3604                HighLightMenubarString(gpMenubarItemInfos[index].menu_str,
3605                      &text_bbox, TRUE);
3606                curRaisedMenuItem = index;
3607             }
3608          }
3609       }
3610       XSync(mainDisplay, False);
3611       while (XCheckWindowEvent(mainDisplay, menubarWindow, PointerMotionMask,
3612             &ev)) ;
3613    } else if (input->type == ButtonPress) {
3614       int win_x, win_y, index;
3615       struct BBRec text_bbox;
3616 
3617       index = WhichMenubarItem(input->xbutton.x, input->xbutton.y,
3618             &win_x, &win_y, &text_bbox);
3619       if (index == INVALID) {
3620          SetMouseStatusToAllNone();
3621       } else {
3622          SaveStatusStrings();
3623          rc = PullDownFromMenubar(index, win_x, win_y, &text_bbox);
3624          RestoreStatusStrings();
3625          SetMouseStatus(NULL, NULL, NULL);
3626          SetBBRec(&excludeMenubarWinBBox, -1, -1, -1, -1);
3627          excludeMenubarIndex = INVALID;
3628       }
3629       if (threeDLook) {
3630          if (index != curRaisedMenuItem) {
3631             struct BBRec text_bbox;
3632 
3633             if (curRaisedMenuItem != INVALID) {
3634                GetMenubarItemInfo(curRaisedMenuItem, NULL, NULL, &text_bbox);
3635                /* HighLightMenubarString() doesn't need _() */
3636                HighLightMenubarString(
3637                      gpMenubarItemInfos[curRaisedMenuItem].menu_str,
3638                      &text_bbox, FALSE);
3639                curRaisedMenuItem = INVALID;
3640             }
3641             GetMenubarItemInfo(index, NULL, NULL, &text_bbox);
3642             /* HighLightMenubarString() doesn't need _() */
3643             HighLightMenubarString(gpMenubarItemInfos[index].menu_str,
3644                   &text_bbox, TRUE);
3645             curRaisedMenuItem = index;
3646          }
3647       }
3648       XSync(mainDisplay, False);
3649       while (XCheckWindowEvent(mainDisplay, menubarWindow, ButtonPressMask,
3650             &ev)) ;
3651    }
3652    return rc;
3653 }
3654