|
|
1.1 ! root 1: /* ! 2: * $Source: /u1/X11/clients/xterm/RCS/menu.c,v $ ! 3: * $Header: menu.c,v 1.28 87/09/11 22:31:28 rws Exp $ ! 4: */ ! 5: ! 6: #include <X11/copyright.h> ! 7: ! 8: /* ! 9: * Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. ! 10: * ! 11: * All Rights Reserved ! 12: * ! 13: * Permission to use, copy, modify, and distribute this software and its ! 14: * documentation for any purpose and without fee is hereby granted, ! 15: * provided that the above copyright notice appear in all copies and that ! 16: * both that copyright notice and this permission notice appear in ! 17: * supporting documentation, and that the name of Digital Equipment ! 18: * Corporation not be used in advertising or publicity pertaining to ! 19: * distribution of the software without specific, written prior permission. ! 20: * ! 21: * ! 22: * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ! 23: * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL ! 24: * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ! 25: * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, ! 26: * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ! 27: * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS ! 28: * SOFTWARE. ! 29: */ ! 30: ! 31: #include <stdio.h> ! 32: #ifdef MODEMENU ! 33: #include <X11/Xlib.h> ! 34: #include <X11/Intrinsic.h> ! 35: #include <X11/Xutil.h> ! 36: #include <X11/Xatom.h> ! 37: #include <X11/cursorfont.h> ! 38: #include <X11/Xtlib.h> ! 39: #include "menu.h" ! 40: #include <setjmp.h> ! 41: #include "ptyx.h" ! 42: #include "data.h" ! 43: ! 44: #ifndef lint ! 45: static char rcs_id[] = "$Header: menu.c,v 1.28 87/09/11 22:31:28 rws Exp $"; ! 46: #endif lint ! 47: ! 48: #define DEFMENUBORDER 2 ! 49: #define DEFMENUPAD 3 ! 50: ! 51: #define XtNxterm "xterm" ! 52: #define XtCApp "App" ! 53: ! 54: #define XOR(a,b) ((a&(~b)) | ((~a)&b)) ! 55: ! 56: #define SetStateFlags(item) item->itemFlags = (item->itemFlags &\ ! 57: ~(itemStateMask | itemChanged)) |\ ! 58: ((item->itemFlags & itemSetMask) >>\ ! 59: itemSetMaskShift) ! 60: ! 61: ! 62: static char Check_MarkBits[] = { ! 63: 0x00, 0x01, 0x80, 0x01, 0xc0, 0x00, 0x60, 0x00, ! 64: 0x31, 0x00, 0x1b, 0x00, 0x0e, 0x00, 0x04, 0x00 ! 65: }; ! 66: static char Default_CursorBits[] = { ! 67: 0x00, 0x00, 0x02, 0x00, 0x06, 0x00, 0x0e, 0x00, ! 68: 0x1e, 0x00, 0x3e, 0x00, 0x7e, 0x00, 0xfe, 0x00, ! 69: 0xfe, 0x01, 0x3e, 0x00, 0x36, 0x00, 0x62, 0x00, ! 70: 0x60, 0x00, 0xc0, 0x00, 0xc0, 0x00, 0x00, 0x00 ! 71: }; ! 72: static char Default_MaskBits[] = { ! 73: 0x03, 0x00, 0x07, 0x00, 0x0f, 0x00, 0x1f, 0x00, ! 74: 0x3f, 0x00, 0x7f, 0x00, 0xff, 0x00, 0xff, 0x01, ! 75: 0xff, 0x03, 0xff, 0x07, 0x7f, 0x00, 0xf7, 0x00, ! 76: 0xf3, 0x00, 0xe1, 0x01, 0xe0, 0x01, 0xc0, 0x01 ! 77: }; ! 78: ! 79: static GC MenuGC, MenuInverseGC, MenuInvertGC, MenuGrayGC; ! 80: static int gotGCs = FALSE; ! 81: ! 82: Pixmap Gray_Tile, Check_Tile; ! 83: Menu Menu_Default; ! 84: Cursor Menu_DefaultCursor; ! 85: char *Menu_DefaultFont; ! 86: ! 87: extern XrmNameList nameList; ! 88: extern XrmClassList classList; ! 89: extern EventDoNothing(); ! 90: ! 91: static int default_menuBorder = DEFMENUBORDER; ! 92: static int default_menuPad = DEFMENUPAD; ! 93: static char *defaultNULL = NULL; ! 94: ! 95: static Resource resourceList[] = { ! 96: {"menuBorder", "MenuBorder", XrmRInt, sizeof(int), ! 97: (caddr_t) &Menu_Default.menuBorderWidth, (caddr_t) &default_menuBorder}, ! 98: {"menuFont", XtCFont, XrmRString, sizeof(char *), ! 99: (caddr_t) &Menu_DefaultFont, (caddr_t) &defaultNULL}, ! 100: {"menuPad", "MenuPad", XrmRInt, sizeof(int), ! 101: (caddr_t) &Menu_Default.menuItemPad, (caddr_t) &default_menuPad} ! 102: }; ! 103: ! 104: /* ! 105: * AddMenuItem() adds a menu item to an existing menu, at the end of the ! 106: * list, which are number sequentially from zero. The menuitem index is ! 107: * return, or -1 if failed. ! 108: */ ! 109: ! 110: AddMenuItem(menu, text) ! 111: register Menu *menu; ! 112: register char *text; ! 113: { ! 114: register MenuItem *menuitem, **next; ! 115: register int i; ! 116: extern char *malloc(); ! 117: ! 118: if(!menu || !text || (menuitem = (MenuItem *)malloc(sizeof(MenuItem))) ! 119: == (MenuItem *)0) ! 120: return(-1); ! 121: bzero((char *)menuitem, sizeof(MenuItem)); ! 122: menuitem->itemText = text; ! 123: menuitem->itemTextLength = strlen(text); ! 124: for(i = 0, next = &menu->menuItems ; *next ; i++) ! 125: next = &(*next)->nextItem; ! 126: *next = menuitem; ! 127: menu->menuFlags |= menuChanged; ! 128: return(i); ! 129: } ! 130: ! 131: /* ! 132: * DisposeItem() releases the memory allocated for the given indexed ! 133: * menuitem. Nonzero is returned if an item was actual disposed of. ! 134: */ ! 135: DisposeItem(menu, i) ! 136: register Menu *menu; ! 137: register int i; ! 138: { ! 139: register MenuItem **next, **last, *menuitem; ! 140: ! 141: if(!menu || i < 0) ! 142: return(0); ! 143: next = &menu->menuItems; ! 144: do { ! 145: if(!*next) ! 146: return(0); ! 147: last = next; ! 148: next = &(*next)->nextItem; ! 149: } while(i-- > 0); ! 150: menuitem = *last; ! 151: *last = *next; ! 152: free(menuitem); ! 153: return(1); ! 154: } ! 155: ! 156: /* ! 157: * DisposeMenu() releases the memory allocated for the given menu. ! 158: */ ! 159: DisposeMenu(menu) ! 160: register Menu *menu; ! 161: { ! 162: register TScreen *screen = &term.screen; ! 163: static Unmap_Menu(); ! 164: ! 165: if(!menu) ! 166: return; ! 167: if(menu->menuFlags & menuMapped) ! 168: Unmap_Menu(menu); ! 169: while(DisposeItem(menu, 0)); ! 170: if(menu->menuWindow) ! 171: XDestroyWindow(screen->display, menu->menuWindow); ! 172: if(menu->menuSaved) ! 173: XFreePixmap(screen->display, menu->menuSaved); ! 174: free(menu); ! 175: } ! 176: ! 177: InitMenu(name) ! 178: register char *name; ! 179: { ! 180: register TScreen *screen = &term.screen; ! 181: register char *cp; ! 182: extern Pixmap make_gray(); ! 183: Display *dpy = screen->display; ! 184: ! 185: /* ! 186: * If the gray tile hasn't been set up, do it now. ! 187: */ ! 188: if(!Gray_Tile) ! 189: Gray_Tile = make_gray(WhitePixel(dpy, DefaultScreen(dpy)), ! 190: BlackPixel(dpy, DefaultScreen(dpy)), 1); ! 191: if (!Check_Tile) { ! 192: Check_Tile = Make_tile(checkMarkWidth, checkMarkHeight, ! 193: Check_MarkBits, BlackPixel(dpy, DefaultScreen(dpy)), ! 194: WhitePixel(dpy, DefaultScreen(dpy)), ! 195: DefaultDepth(dpy, DefaultScreen(dpy))); ! 196: } ! 197: Menu_Default.menuFlags = menuChanged; ! 198: /* if((cp = XGetDefault(dpy, name, "MenuFreeze")) && XStrCmp(cp, "on") == 0) ! 199: Menu_Default.menuFlags |= menuFreeze; ! 200: if((cp = XGetDefault(dpy, name, "MenuSave")) && XStrCmp(cp, "on") == 0) ! 201: Menu_Default.menuFlags |= menuSaveMenu; ! 202: */ ! 203: Menu_Default.menuInitialItem = -1; ! 204: XtGetResources( dpy, resourceList, XtNumber(resourceList), ! 205: (ArgList) NULL, 0, DefaultRootWindow(dpy), ! 206: XtNxterm, XtCApp, &nameList, &classList ); ! 207: ! 208: /* would be nice if (Xrm)CvtStringToFontStruct didn't complain ! 209: * when the font didn't exist! */ ! 210: if (Menu_DefaultFont != NULL) { ! 211: Menu_Default.menuFontInfo = XLoadQueryFont(dpy, Menu_DefaultFont); ! 212: if (!Menu_Default.menuFontInfo) ! 213: Menu_DefaultFont = NULL; ! 214: } ! 215: ! 216: /* actually, we need to know whether the fid is valid anyway */ ! 217: if (Menu_DefaultFont == NULL) { ! 218: Menu_Default.menuFontInfo = screen->fnt_norm; ! 219: MenugcFontMask = VTgcFontMask; ! 220: } ! 221: }; ! 222: ! 223: /* ! 224: * ItemFlags returns the state of item "n" of the menu. ! 225: */ ! 226: ItemFlags(menu, n) ! 227: register Menu *menu; ! 228: register int n; ! 229: { ! 230: register MenuItem *item; ! 231: ! 232: if(!menu || !menu->menuItems || n < 0) ! 233: return(-1); ! 234: for(item = menu->menuItems ; n > 0 ; n--) ! 235: if(!(item = item->nextItem)) ! 236: return(0); ! 237: return((item->itemFlags & itemSetMask) >> itemSetMaskShift); ! 238: } ! 239: ! 240: /* ! 241: * ItemText changes the text of item "n" of the menu. ! 242: */ ! 243: ItemText(menu, n, text) ! 244: register Menu *menu; ! 245: register int n; ! 246: char *text; ! 247: { ! 248: register MenuItem *item; ! 249: ! 250: if(!menu || !menu->menuItems || n < 0 || !text) ! 251: return(0); ! 252: for(item = menu->menuItems ; n > 0 ; n--) ! 253: if(!(item = item->nextItem)) ! 254: return(0); ! 255: item->itemText = text; ! 256: menu->menuFlags |= menuChanged; ! 257: return(1); ! 258: } ! 259: ! 260: /* ! 261: * NewMenu() returns a pointer to an initialized new Menu structure, or NULL ! 262: * if failed. ! 263: * ! 264: * The Menu structure _menuDefault contains the default menu settings. ! 265: */ ! 266: Menu *NewMenu(name, reverse) ! 267: char *name; ! 268: int reverse; ! 269: { ! 270: register Menu *menu; ! 271: register int fg, bg; ! 272: register TScreen *screen = &term.screen; ! 273: XGCValues xgc; ! 274: extern char *malloc(); ! 275: extern XFontStruct *XLoadQueryFont(); ! 276: register Display *dpy = screen->display; ! 277: ! 278: /* ! 279: * If the GrayTile hasn't been defined, InitMenu() was never ! 280: * run, so exit. ! 281: */ ! 282: if(!Gray_Tile) ! 283: return((Menu *)0); ! 284: /* ! 285: * Allocate the memory for the menu structure. ! 286: */ ! 287: if((menu = (Menu *)malloc(sizeof(Menu))) == (Menu *)0) ! 288: return((Menu *)0); ! 289: /* ! 290: * Initialize to default values. ! 291: */ ! 292: *menu = Menu_Default; ! 293: /* ! 294: * If the menu cursor hasn't been given, make a default one. ! 295: */ ! 296: if(!menu->menuCursor) { ! 297: if(!Menu_DefaultCursor) { ! 298: if(reverse) { ! 299: fg = WhitePixel(dpy, ! 300: DefaultScreen(dpy)); ! 301: bg = BlackPixel(dpy, ! 302: DefaultScreen(dpy)); ! 303: } else { ! 304: fg = BlackPixel(dpy, ! 305: DefaultScreen(dpy)); ! 306: bg = WhitePixel(dpy, ! 307: DefaultScreen(dpy)); ! 308: } ! 309: if(!(Menu_DefaultCursor = ! 310: XCreateFontCursor(dpy, XC_left_ptr))) ! 311: return((Menu *)0); ! 312: } ! 313: menu->menuCursor = Menu_DefaultCursor; ! 314: } ! 315: /* ! 316: * Initialze the default background and border pixmaps and foreground ! 317: * and background colors (black and white). ! 318: */ ! 319: if(reverse) { ! 320: menu->menuFgColor = WhitePixel(dpy, ! 321: DefaultScreen(dpy)); ! 322: menu->menuBgColor = BlackPixel(dpy, ! 323: DefaultScreen(dpy)); ! 324: } else { ! 325: menu->menuFgColor = BlackPixel(dpy, ! 326: DefaultScreen(dpy)); ! 327: menu->menuBgColor = WhitePixel(dpy, ! 328: DefaultScreen(dpy)); ! 329: } ! 330: if(!gotGCs) { ! 331: xgc.foreground = menu->menuFgColor; ! 332: xgc.function = GXinvert; ! 333: xgc.plane_mask = XOR(menu->menuFgColor, menu->menuBgColor); ! 334: MenuInvertGC = XCreateGC(dpy, DefaultRootWindow(dpy), ! 335: GCForeground+GCFunction+GCPlaneMask, &xgc); ! 336: xgc.foreground = menu->menuFgColor; ! 337: xgc.background = menu->menuBgColor; ! 338: xgc.font = menu->menuFontInfo->fid; ! 339: xgc.function = GXcopy; ! 340: xgc.fill_style = FillSolid; ! 341: MenuGC = XCreateGC(dpy, DefaultRootWindow(dpy), ! 342: MenugcFontMask+GCForeground+GCBackground+GCFunction+GCFillStyle, ! 343: &xgc); ! 344: xgc.foreground = menu->menuBgColor; ! 345: xgc.background = menu->menuFgColor; ! 346: xgc.font = menu->menuFontInfo->fid; ! 347: xgc.function = GXcopy; ! 348: xgc.fill_style = FillSolid; ! 349: MenuInverseGC = XCreateGC(dpy, DefaultRootWindow(dpy), ! 350: MenugcFontMask+GCForeground+GCBackground+GCFunction+GCFillStyle, ! 351: &xgc); ! 352: xgc.foreground = menu->menuFgColor; ! 353: xgc.background = menu->menuBgColor; ! 354: xgc.function = menu->menuFgColor ? GXor : GXand; ! 355: /* xgc.function = GXcopy;*/ ! 356: xgc.stipple = Gray_Tile; ! 357: xgc.fill_style = FillStippled; ! 358: MenuGrayGC = XCreateGC(dpy, DefaultRootWindow(dpy), ! 359: GCStipple+GCFillStyle+MenugcFontMask ! 360: +GCForeground+GCBackground+GCFunction, ! 361: &xgc); ! 362: gotGCs = TRUE; ! 363: } ! 364: /* ! 365: * Set the menu title. If name is NULL or is an empty string, no ! 366: * title will be displayed. ! 367: */ ! 368: if(name && *name) { ! 369: menu->menuTitleLength = strlen(menu->menuTitle = name); ! 370: menu->menuTitleWidth = XTextWidth(menu->menuFontInfo, name, ! 371: menu->menuTitleLength); ! 372: menu->menuItemTop = menu->menuFontInfo->ascent + ! 373: menu->menuFontInfo->descent + 2 * menu->menuItemPad + 1; ! 374: } else ! 375: menu->menuTitleLength = menu->menuTitleWidth = ! 376: menu->menuItemTop = 0; ! 377: return(menu); ! 378: } ! 379: ! 380: /* ! 381: * SetItemCheck sets the check state of item "n" of the menu to "state". ! 382: */ ! 383: SetItemCheck(menu, n, state) ! 384: register Menu *menu; ! 385: register int n; ! 386: int state; ! 387: { ! 388: register MenuItem *item; ! 389: ! 390: if(!menu || !menu->menuItems || n < 0) ! 391: return(0); ! 392: for(item = menu->menuItems ; n > 0 ; n--) ! 393: if(!(item = item->nextItem)) ! 394: return(0); ! 395: if(state) ! 396: item->itemFlags |= itemSetChecked; ! 397: else ! 398: item->itemFlags &= ~itemSetChecked; ! 399: if(((item->itemFlags & itemSetMask) >> itemSetMaskShift) != ! 400: (item->itemFlags & itemStateMask)) { ! 401: item->itemFlags |= itemChanged; ! 402: menu->menuFlags |= menuItemChanged; ! 403: } else ! 404: item->itemFlags &= ~itemChanged; ! 405: return(1); ! 406: } ! 407: ! 408: /* ! 409: * SetItemDisable sets the disable state of item "n" of the menu to "state". ! 410: */ ! 411: SetItemDisable(menu, n, state) ! 412: register Menu *menu; ! 413: register int n; ! 414: int state; ! 415: { ! 416: register MenuItem *item; ! 417: ! 418: if(!menu || !menu->menuItems || n < 0) ! 419: return(0); ! 420: for(item = menu->menuItems ; n > 0 ; n--) ! 421: if(!(item = item->nextItem)) ! 422: return(0); ! 423: if(state) ! 424: item->itemFlags |= itemSetDisabled; ! 425: else ! 426: item->itemFlags &= ~itemSetDisabled; ! 427: if(((item->itemFlags & itemSetMask) >> itemSetMaskShift) != ! 428: (item->itemFlags & itemStateMask)) { ! 429: item->itemFlags |= itemChanged; ! 430: menu->menuFlags |= menuItemChanged; ! 431: } else ! 432: item->itemFlags &= ~itemChanged; ! 433: return(1); ! 434: } ! 435: ! 436: static Menu *menu; ! 437: static MenuItem *item; ! 438: static int i; ! 439: static MenuItem *hilited_item; ! 440: static int drawn; ! 441: static int changed; ! 442: int y, n, hilited_y, hilited_n, in_window; ! 443: static MenuItem *Mouse_InItem(), *Y_InItem(); ! 444: static Unmap_Menu(); ! 445: ! 446: XtEventReturnCode MenuExposeWindow(event, eventdata) ! 447: register XEvent *event; ! 448: caddr_t eventdata; ! 449: { ! 450: register TScreen *screen = &term.screen; ! 451: /* ! 452: * If we have a saved pixmap, display it. Otherwise ! 453: * redraw the menu and save it away. ! 454: */ ! 455: if (event->type == NoExpose) return; ! 456: if(menu->menuSaved) { ! 457: XCopyArea(screen->display, menu->menuSaved, menu->menuWindow, ! 458: MenuGC, 0, 0, ! 459: menu->menuWidth, menu->menuHeight, 0, 0); ! 460: /* ! 461: * If the menuItemChanged flag is still set, ! 462: * then we need to redraw certain menu items. ! 463: * ("i" is the vertical position of the top ! 464: * of the current item.) ! 465: */ ! 466: if(changed & menuItemChanged) { ! 467: i = menu->menuItemTop; ! 468: for(item = menu->menuItems ; item ; ! 469: item = item->nextItem) { ! 470: if(item->itemFlags & itemChanged) ! 471: Modify_Item(menu, item, i); ! 472: i += item->itemHeight; ! 473: } ! 474: } ! 475: } else ! 476: Draw_Menu(menu); ! 477: /* ! 478: * If the menu has changed in any way and we want to ! 479: * save the menu, throw away any existing save menu ! 480: * image and make a new one. ! 481: */ ! 482: XFlush(screen->display); ! 483: if(changed && (menu->menuFlags & menuSaveMenu)) { ! 484: if(menu->menuSaved) ! 485: XFreePixmap(screen->display, menu->menuSaved); ! 486: /* menu->menuSaved = XPixmapSave(screen->display, ! 487: menu->menuWindow, 0, 0, menu->menuWidth, menu->menuHeight); ! 488: */ ! 489: } ! 490: /* ! 491: * See which item the cursor may currently be in. If ! 492: * it is in a non-disabled item, hilite it. ! 493: */ ! 494: if(hilited_item = Mouse_InItem(menu, &hilited_y, &hilited_n)) ! 495: XFillRectangle(screen->display, menu->menuWindow, ! 496: MenuInvertGC, 0, hilited_y, ! 497: menu->menuWidth, hilited_item->itemHeight); ! 498: drawn++; ! 499: return (XteventHandled); ! 500: } ! 501: ! 502: XtEventReturnCode MenuMouseMoved(event, eventdata) ! 503: XButtonEvent *event; ! 504: caddr_t eventdata; ! 505: { ! 506: register TScreen *screen = &term.screen; ! 507: if(!drawn || !in_window) ! 508: return; ! 509: /* ! 510: * See which item the cursor may currently be in. If ! 511: * the item has changed, unhilite the old one and ! 512: * then hilited the new one. ! 513: */ ! 514: y = event->y; ! 515: if((item = Y_InItem(menu, &y, &n)) != hilited_item) { ! 516: if(hilited_item) ! 517: XFillRectangle(screen->display, menu->menuWindow, ! 518: MenuInvertGC, 0, hilited_y, ! 519: menu->menuWidth, hilited_item->itemHeight); ! 520: if(hilited_item = item) { ! 521: XFillRectangle(screen->display, menu->menuWindow, ! 522: MenuInvertGC, 0, ! 523: hilited_y = y, menu->menuWidth, item->itemHeight); ! 524: hilited_n = n; ! 525: } ! 526: } ! 527: return (XteventHandled); ! 528: } ! 529: ! 530: XtEventReturnCode MenuEnterWindow(event, eventdata) ! 531: XEvent *event; ! 532: caddr_t eventdata; ! 533: { ! 534: in_window = TRUE; ! 535: return (MenuMouseMoved(event, eventdata)); ! 536: } ! 537: ! 538: XtEventReturnCode MenuLeaveWindow(event, eventdata) ! 539: XEvent *event; ! 540: caddr_t eventdata; ! 541: { ! 542: register TScreen *screen = &term.screen; ! 543: if(!drawn) ! 544: return (XteventHandled); ! 545: /* ! 546: * Unhilite any window that is currently hilited. ! 547: */ ! 548: if(hilited_item) { ! 549: XFillRectangle(screen->display, menu->menuWindow, ! 550: MenuInvertGC, 0, hilited_y, ! 551: menu->menuWidth, hilited_item->itemHeight); ! 552: hilited_item = (MenuItem *)0; ! 553: } ! 554: in_window = FALSE; ! 555: return (XteventHandled); ! 556: } ! 557: ! 558: XtEventReturnCode MenuButtonReleased(event, eventdata) ! 559: XButtonEvent *event; ! 560: caddr_t eventdata; ! 561: { ! 562: register TScreen *screen = &term.screen; ! 563: extern FinishModeMenu(); ! 564: ! 565: /* ! 566: * return the index number of any selected menu ! 567: * item. ! 568: */ ! 569: ! 570: if (! AllButtonsUp(event->state, event->button)) ! 571: return(XteventHandled); ! 572: ! 573: XUngrabPointer(screen->display, CurrentTime); ! 574: ! 575: if(in_window) { ! 576: y = event->y; ! 577: if((item = Y_InItem(menu, &y, &n)) != hilited_item) { ! 578: if(hilited_item) ! 579: XFillRectangle(screen->display, menu->menuWindow, ! 580: MenuInvertGC, 0, ! 581: hilited_y, menu->menuWidth, ! 582: hilited_item->itemHeight); ! 583: if(hilited_item = item) { ! 584: XFillRectangle(screen->display, menu->menuWindow, ! 585: MenuInvertGC, 0, ! 586: hilited_y = y, menu->menuWidth, ! 587: hilited_item->itemHeight); ! 588: hilited_n = n; ! 589: } ! 590: } ! 591: } ! 592: XFlush(screen->display); ! 593: menu->menuFlags &= ~(menuChanged | menuItemChanged); ! 594: Unmap_Menu(menu); ! 595: drawn = 0; ! 596: if(hilited_item) ! 597: FinishModeMenu(menu->menuInitialItem = hilited_n); ! 598: else ! 599: FinishModeMenu(-1); ! 600: return (XteventHandled); ! 601: } ! 602: ! 603: /* ! 604: * TrackMenu does most of the work of displaying the menu and tracking the ! 605: * mouse. ! 606: */ ! 607: TrackMenu(lmenu, event) ! 608: register Menu *lmenu; ! 609: register XButtonPressedEvent *event; ! 610: { ! 611: register TScreen *screen = &term.screen; ! 612: XButtonReleasedEvent ev; ! 613: XSetWindowAttributes attr; ! 614: ! 615: menu = lmenu; ! 616: hilited_item = (MenuItem *)0; ! 617: /* ! 618: * Check that things are reasonable. ! 619: */ ! 620: if(!menu || !event || !menu->menuItems || event->type != ButtonPress) ! 621: return(-1); ! 622: /* ! 623: * Set the changed flag and clear the menu changed flags. ! 624: */ ! 625: changed = menu->menuFlags & (menuChanged | menuItemChanged); ! 626: /* ! 627: * If the entire menu has changed, throw away any saved pixmap and ! 628: * then call RecalcMenu(). ! 629: */ ! 630: if(changed & menuChanged) { ! 631: if(menu->menuSaved) ! 632: XFreePixmap(screen->display, menu->menuSaved); ! 633: menu->menuSaved = (Pixmap)0; ! 634: if(!Recalc_Menu(menu)) ! 635: return(-1); ! 636: changed &= ~menuItemChanged; ! 637: } ! 638: /* ! 639: * Now if the window was never created, go ahead and make it. Otherwise ! 640: * if the menu has changed, resize the window. ! 641: */ ! 642: if(!menu->menuWindow) { ! 643: attr.override_redirect = TRUE; ! 644: attr.border_pixmap = make_gray( ! 645: WhitePixel(screen->display, DefaultScreen(screen->display)), ! 646: BlackPixel(screen->display, DefaultScreen(screen->display)), ! 647: DefaultDepth(screen->display, DefaultScreen(screen->display))); ! 648: attr.background_pixel = menu->menuBgColor; ! 649: attr.cursor = menu->menuCursor; ! 650: if((menu->menuWindow = XCreateWindow(screen->display, ! 651: DefaultRootWindow(screen->display), 0, 0, ! 652: menu->menuWidth, menu->menuHeight, menu->menuBorderWidth, ! 653: 0, CopyFromParent, CopyFromParent, ! 654: CWBorderPixmap+CWBackPixel+CWOverrideRedirect+CWCursor, ! 655: &attr)) == (Window)0) ! 656: return(-1); ! 657: ! 658: ! 659: XtSetEventHandler(screen->display, menu->menuWindow, ! 660: (XtEventHandler) MenuExposeWindow, ExposureMask, ! 661: (caddr_t)NULL); ! 662: XtSetEventHandler(screen->display, menu->menuWindow, ! 663: (XtEventHandler) MenuEnterWindow, EnterWindowMask, ! 664: (caddr_t)NULL); ! 665: XtSetEventHandler(screen->display, menu->menuWindow, ! 666: (XtEventHandler) MenuLeaveWindow, LeaveWindowMask, ! 667: (caddr_t)NULL); ! 668: XtSetEventHandler(screen->display, menu->menuWindow, ! 669: (XtEventHandler) MenuMouseMoved, PointerMotionMask, ! 670: (caddr_t)NULL); ! 671: XtSetEventHandler(screen->display, menu->menuWindow, ! 672: (XtEventHandler) MenuButtonReleased, ButtonReleaseMask, ! 673: (caddr_t)NULL); ! 674: XtSetEventHandler(screen->display, menu->menuWindow, ! 675: (XtEventHandler) EventDoNothing, ButtonPressMask, ! 676: (caddr_t)NULL); ! 677: } else if(changed & menuChanged) ! 678: XResizeWindow(screen->display, menu->menuWindow, ! 679: menu->menuWidth, menu->menuHeight); ! 680: /* ! 681: * Figure out where the menu is supposed to go, from the initial button ! 682: * press, and move the window there. Then map the menu. ! 683: */ ! 684: if(!Move_Menu(menu, event) || !Map_Menu(menu)) ! 685: return(-1); ! 686: ! 687: menuWindow = menu->menuWindow; ! 688: in_window = TRUE; ! 689: XGrabPointer(screen->display, menu->menuWindow, FALSE, ! 690: ExposureMask | EnterWindowMask | LeaveWindowMask | PointerMotionMask ! 691: | ButtonReleaseMask | ButtonPressMask, ! 692: GrabModeAsync, GrabModeAsync, None, menu->menuCursor, CurrentTime ! 693: ); ! 694: } ! 695: ! 696: /* ! 697: * Recalculate all of the various menu and item variables. ! 698: */ ! 699: static Recalc_Menu(menu) ! 700: register Menu *menu; ! 701: { ! 702: register MenuItem *item; ! 703: register int max, i, height, fontheight; ! 704: ! 705: /* ! 706: * We must have already gotten the menu font. ! 707: */ ! 708: if(!menu->menuFontInfo) ! 709: return(0); ! 710: /* ! 711: * Initialize the various max width variables. ! 712: */ ! 713: fontheight = menu->menuFontInfo->ascent + menu->menuFontInfo->descent; ! 714: height = menu->menuItemTop; ! 715: menu->menuMaxTextWidth = menu->menuTitleWidth; ! 716: /* ! 717: * The item height is the maximum of the font height and the ! 718: * checkbox height. ! 719: */ ! 720: max = fontheight; ! 721: if(checkMarkHeight > max) ! 722: max = checkMarkHeight; ! 723: /* ! 724: * Go through the menu item list. ! 725: */ ! 726: for(item = menu->menuItems ; item ; item = item->nextItem) { ! 727: /* ! 728: * If the item text is a single dash, we assume this is ! 729: * a line separator and treat it special. ! 730: */ ! 731: if(XStrCmp(item->itemText, "-") == 0) ! 732: height += (item->itemHeight = lineSeparatorHeight); ! 733: else { ! 734: height += (item->itemHeight = max); ! 735: /* ! 736: * Check the text width with the max value stored in ! 737: * menu. ! 738: */ ! 739: if((item->itemTextWidth = XTextWidth( ! 740: menu->menuFontInfo, item->itemText, ! 741: strlen(item->itemText))) > menu->menuMaxTextWidth) ! 742: menu->menuMaxTextWidth = item->itemTextWidth; ! 743: } ! 744: /* ! 745: * If the itemChanged flag is set, set the state bits. ! 746: */ ! 747: if(item->itemFlags & itemChanged) { ! 748: item->itemFlags = (item->itemFlags & ~itemStateMask) | ! 749: ((item->itemFlags & itemSetMask) >> itemSetMaskShift); ! 750: item->itemFlags &= ~itemChanged; ! 751: } ! 752: } ! 753: /* ! 754: * Set the menu height and then set the menu width. ! 755: */ ! 756: menu->menuHeight = height; ! 757: menu->menuWidth = 3 * menu->menuItemPad + menu->menuMaxTextWidth + ! 758: checkMarkWidth; ! 759: return(1); ! 760: } ! 761: ! 762: /* ! 763: * Figure out where to popup the menu, relative to the where the button was ! 764: * pressed. ! 765: */ ! 766: static Move_Menu(menu, ev) ! 767: register Menu *menu; ! 768: XButtonPressedEvent *ev; ! 769: { ! 770: register MenuItem *item; ! 771: register int n, x, y; ! 772: register TScreen *screen = &term.screen; ! 773: int total_width; ! 774: Window subw; ! 775: /* ! 776: * Try to popup the menu so that the cursor is centered within the ! 777: * width of the menu, but compensate if that would run it outside ! 778: * the display area. ! 779: */ ! 780: total_width = menu->menuWidth + 2 * menu->menuBorderWidth; ! 781: if((x = ev->x_root - total_width / 2) < 0) ! 782: x = 0; ! 783: else if(x + total_width > DisplayWidth(screen->display, ! 784: DefaultScreen(screen->display))) ! 785: x = DisplayWidth(screen->display, ! 786: DefaultScreen(screen->display)) - total_width; ! 787: /* ! 788: * If the menu extends above outside of the display, warp ! 789: * the mouse vertically so the menu will all show up. ! 790: */ ! 791: if((y = ev->y_root) < 0) { ! 792: /* don't bother warping pointer ! 793: XWarpPointer(screen->display, None, ! 794: DefaultRootWindow(screen->display), 0, 0, 0, 0, ev->x_root, ! 795: 0); ! 796: */ ! 797: y = 0; ! 798: } else if((n = y + menu->menuHeight + 2 * menu->menuBorderWidth - ! 799: DisplayHeight(screen->display, DefaultScreen(screen->display))) > 0) { ! 800: /* don't bother warping pointer ! 801: XWarpPointer(screen->display, None, ! 802: DefaultRootWindow(screen->display), 0, 0, 0, 0, ev->x_root, ! 803: ev->y_root - n); ! 804: */ ! 805: y -= n; ! 806: } ! 807: XMoveWindow(screen->display, menu->menuWindow, x, y); ! 808: /* ! 809: * If we are in freeze mode, save what will be the coordinates of ! 810: * the save image. ! 811: */ ! 812: if(menu->menuFlags & menuFreeze) { ! 813: menu->menuSavedImageX = x; ! 814: menu->menuSavedImageY = y; ! 815: } ! 816: return(1); ! 817: } ! 818: ! 819: /* ! 820: * Map the menu window. ! 821: */ ! 822: static Map_Menu(menu) ! 823: register Menu *menu; ! 824: { ! 825: register int i; ! 826: register TScreen *screen = &term.screen; ! 827: ! 828: /* ! 829: * If we are in freeze mode, save the pixmap underneath where the menu ! 830: * will be (including the border). ! 831: */ ! 832: if(menu->menuFlags & menuFreeze) { ! 833: XGrabServer(screen->display); ! 834: i = 2 * menu->menuBorderWidth; ! 835: /* if((menu->menuSavedImage = XPixmapSave(screen->display, ! 836: DefaultRootWindow(screen->display), ! 837: menu->menuSavedImageX, menu->menuSavedImageY, menu->menuWidth ! 838: + i, menu->menuHeight + i)) == (Pixmap)0) ! 839: */ ! 840: return(0); ! 841: } ! 842: /* ! 843: * Actually map the window. ! 844: */ ! 845: XMapRaised(screen->display, menu->menuWindow); ! 846: menu->menuFlags |= menuMapped; ! 847: return(1); ! 848: } ! 849: ! 850: /* ! 851: * Draw the entire menu in the blank window. ! 852: */ ! 853: static Draw_Menu(menu) ! 854: register Menu *menu; ! 855: { ! 856: register MenuItem *item; ! 857: register int top = menu->menuItemTop; ! 858: register int x = menu->menuItemPad; ! 859: register int y, dim; ! 860: register TScreen *screen = &term.screen; ! 861: ! 862: /* ! 863: * If we have a menu title, draw it first, centered and hilited. ! 864: */ ! 865: if(menu->menuTitleLength) { ! 866: XFillRectangle(screen->display, menu->menuWindow, ! 867: MenuGC, 0, 0, menu->menuWidth, top - 1); ! 868: XDrawImageString(screen->display, menu->menuWindow, ! 869: MenuInverseGC, (menu->menuWidth - ! 870: menu->menuTitleWidth) / 2, ! 871: menu->menuItemPad+menu->menuFontInfo->ascent, ! 872: menu->menuTitle, menu->menuTitleLength); ! 873: } ! 874: /* ! 875: * For each item in the list, first draw any check mark and then ! 876: * draw the rest of it. ! 877: */ ! 878: for(item = menu->menuItems ; item ; item = item->nextItem) { ! 879: SetStateFlags(item); ! 880: dim = (item->itemFlags & itemDisabled); ! 881: /* ! 882: * Draw the check mark, possibly dimmed, wherever is necessary. ! 883: */ ! 884: if(item->itemFlags & itemChecked) { ! 885: XCopyArea(screen->display, ! 886: Check_Tile, menu->menuWindow, ! 887: dim ? MenuGrayGC : MenuGC, ! 888: 0, 0, checkMarkWidth, checkMarkHeight, x, ! 889: y = top + (item->itemHeight - checkMarkHeight) / 2); ! 890: } ! 891: /* ! 892: * Draw the item, possibly dimmed. ! 893: */ ! 894: Draw_Item(menu, item, top, dim); ! 895: top += item->itemHeight; ! 896: } ! 897: } ! 898: ! 899: /* ! 900: * Modify the item at vertical position y. This routine is table driven and ! 901: * the state and set bits are each 2 bits long, contiguous, the least ! 902: * significant bits in the flag word and with the state bits in bits 0 & 1. ! 903: */ ! 904: ! 905: #define drawCheck 0x10 ! 906: #define removeCheck 0x08 ! 907: #define dimCheck 0x04 ! 908: #define drawItem 0x02 ! 909: #define dimItem 0x01 ! 910: ! 911: static char Modify_Table[] = { ! 912: 0x00, 0x02, 0x08, 0x0a, 0x01, 0x00, 0x09, 0x08, ! 913: 0x10, 0x12, 0x00, 0x12, 0x15, 0x14, 0x05, 0x00 ! 914: }; ! 915: ! 916: static Modify_Item(menu, item, top) ! 917: register Menu *menu; ! 918: register MenuItem *item; ! 919: int top; ! 920: { ! 921: register int x = menu->menuItemPad; ! 922: register int y; ! 923: register int center = top + item->itemHeight / 2; ! 924: register int func = Modify_Table[item->itemFlags & ! 925: (itemStateMask | itemSetMask)]; ! 926: register TScreen *screen = &term.screen; ! 927: ! 928: /* ! 929: * If we really won't be making a change, return. ! 930: */ ! 931: if(func == 0) ! 932: return; ! 933: /* ! 934: * Draw the check mark if needed, possibly dimmed. ! 935: */ ! 936: y = center - (checkMarkHeight / 2); ! 937: if(func & (drawCheck | dimCheck)) ! 938: XCopyArea(screen->display, ! 939: Check_Tile, menu->menuWindow, ! 940: (func & dimCheck) ? MenuGrayGC : MenuGC, ! 941: 0, 0, checkMarkWidth, checkMarkHeight, x, ! 942: y = top + (item->itemHeight - checkMarkHeight) / 2); ! 943: /* ! 944: * Remove the check mark if needed. ! 945: */ ! 946: if(func & removeCheck) ! 947: XClearArea(screen->display, menu->menuWindow, ! 948: x, y, checkMarkWidth, checkMarkHeight); ! 949: /* ! 950: * Call Draw_Item if we need to draw or dim the item. ! 951: */ ! 952: if((x = func & dimItem) || (func & drawItem)) ! 953: Draw_Item(menu, item, top, x); ! 954: /* ! 955: * Update state flags. ! 956: */ ! 957: SetStateFlags(item); ! 958: } ! 959: ! 960: /* ! 961: * Draw the item (less check mark) at vertical position y. ! 962: * Dim the item if "dim" is set. ! 963: */ ! 964: static Draw_Item(menu, item, y, dim) ! 965: register Menu *menu; ! 966: register MenuItem *item; ! 967: register int y; ! 968: int dim; ! 969: { ! 970: register int x = 2 * menu->menuItemPad + checkMarkWidth; ! 971: register int center = y + item->itemHeight / 2; ! 972: register TScreen *screen = &term.screen; ! 973: ! 974: /* ! 975: * If the item text is a single dash, draw a separating line. ! 976: */ ! 977: if(XStrCmp(item->itemText, "-") == 0) { ! 978: XDrawLine(screen->display, menu->menuWindow, MenuGC, ! 979: 0, center, menu->menuWidth, center); ! 980: return; ! 981: } ! 982: /* ! 983: * Draw and/or dim the text, centered vertically. ! 984: */ ! 985: y = center - ! 986: ((menu->menuFontInfo->ascent + menu->menuFontInfo->descent)/ 2); ! 987: if(dim) { ! 988: XDrawString(screen->display, menu->menuWindow, MenuGrayGC, ! 989: x, y+menu->menuFontInfo->ascent, ! 990: item->itemText, item->itemTextLength); ! 991: } else ! 992: XDrawImageString(screen->display, menu->menuWindow, ! 993: MenuGC, x, y+menu->menuFontInfo->ascent, ! 994: item->itemText, item->itemTextLength); ! 995: } ! 996: ! 997: /* ! 998: * Determine which enabled menu item the mouse is currently in. Return the ! 999: * top position of this item and its item number. Set inwindow to whether ! 1000: * we are or not. ! 1001: */ ! 1002: static MenuItem *Mouse_InItem(menu, top, n) ! 1003: register Menu *menu; ! 1004: int *top, *n; ! 1005: { ! 1006: int x, y, rootx, rooty, mask; ! 1007: Window subw, root; ! 1008: static MenuItem *Y_InItem(); ! 1009: register TScreen *screen = &term.screen; ! 1010: ! 1011: /* ! 1012: * Find out where the mouse is. If its not in the menu window, ! 1013: * return NULL. ! 1014: */ ! 1015: XQueryPointer(screen->display, menu->menuWindow, ! 1016: &root, &subw, &rootx, &rooty, &x, &y, &mask); ! 1017: if((x <0) || (y < 0) || ! 1018: (x > menu->menuWidth) || (y > menu->menuHeight)) { ! 1019: return((MenuItem *)0); ! 1020: } ! 1021: /* ! 1022: * Call Y_InItem(). ! 1023: */ ! 1024: *top = y; ! 1025: return(Y_InItem(menu, top, n)); ! 1026: } ! 1027: ! 1028: /* ! 1029: * Return which enabled item the locator is in. Also return the ! 1030: * top position of this item and its item number. Initial y passed ! 1031: * in top. ! 1032: */ ! 1033: static MenuItem *Y_InItem(menu, top, n) ! 1034: register Menu *menu; ! 1035: int *top, *n; ! 1036: { ! 1037: register MenuItem *item; ! 1038: register int t, i; ! 1039: register int y = *top; ! 1040: Window subw; ! 1041: ! 1042: /* ! 1043: * Go through the item list. "t" is the vertical position of the ! 1044: * current item and "i" is its item number. ! 1045: */ ! 1046: t = menu->menuItemTop; ! 1047: /* ! 1048: * If the mouse is before the first item, return. ! 1049: */ ! 1050: if(y < t) ! 1051: return((MenuItem *)0); ! 1052: for(i = 0, item = menu->menuItems ; item ; i++, item = item->nextItem) { ! 1053: /* ! 1054: * If the y coordinate is within this menu item, then return. ! 1055: * But don't return disable items. ! 1056: */ ! 1057: if(t + item->itemHeight > y) { ! 1058: if(item->itemFlags & itemDisabled) ! 1059: return((MenuItem *)0); ! 1060: *top = t; ! 1061: *n = i; ! 1062: return(item); ! 1063: } ! 1064: t += item->itemHeight; ! 1065: } ! 1066: /* ! 1067: * Should never get here. ! 1068: */ ! 1069: return((MenuItem *)0); ! 1070: } ! 1071: ! 1072: /* ! 1073: * Unmap_Menu() unmaps a menu, if it is currently mapped. ! 1074: */ ! 1075: static Unmap_Menu(menu) ! 1076: register Menu *menu; ! 1077: { ! 1078: register int i; ! 1079: register TScreen *screen = &term.screen; ! 1080: ! 1081: if(!menu || !(menu->menuFlags & menuMapped)) ! 1082: return; ! 1083: if(menu->menuFlags & menuFreeze) { ! 1084: XUnmapWindow(screen->display, menu->menuWindow); ! 1085: i = 2 * menu->menuBorderWidth; ! 1086: XCopyArea(screen->display, ! 1087: menu->menuSavedImage, DefaultRootWindow(screen->display), ! 1088: MenuGC, 0, 0, menu->menuWidth + i, ! 1089: menu->menuHeight + i, menu->menuSavedImageX, ! 1090: menu->menuSavedImageY); ! 1091: XFreePixmap(screen->display, menu->menuSavedImage); ! 1092: XUngrabServer(screen->display); ! 1093: } else ! 1094: XUnmapWindow(screen->display, menu->menuWindow); ! 1095: menu->menuFlags &= ~menuMapped; ! 1096: } ! 1097: #endif MODEMENU
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.