|
|
1.1 ! root 1: /* ! 2: * $Source: /u1/X/xterm/RCS/menu.c,v $ ! 3: * $Header: menu.c,v 10.101 86/12/01 17:52:43 swick Rel $ ! 4: */ ! 5: ! 6: #ifdef MODEMENU ! 7: #include "X/Xlib.h" ! 8: #include "menu.h" ! 9: ! 10: #ifndef lint ! 11: static char sccs_id[] = "@(#)menu.c\tX10/6.6B\t12/26/86"; ! 12: #endif lint ! 13: ! 14: #define FALSE 0 ! 15: #define TRUE 1 ! 16: #define InvertPlane 1 ! 17: #define SetStateFlags(item) item->itemFlags = (item->itemFlags &\ ! 18: ~(itemStateMask | itemChanged)) |\ ! 19: ((item->itemFlags & itemSetMask) >>\ ! 20: itemSetMaskShift) ! 21: ! 22: ! 23: static short Check_MarkBits[] = { ! 24: 0x0100, 0x0180, 0x00c0, 0x0060, ! 25: 0x0031, 0x001b, 0x000e, 0x0004 ! 26: }; ! 27: static short Check_GrayBits[] = { ! 28: 0x0100, 0x0080, 0x0040, 0x0020, ! 29: 0x0011, 0x000a, 0x0004, 0x0000 ! 30: }; ! 31: static short Default_CursorBits[] = { ! 32: 0x0000, 0x0002, 0x0006, 0x000e, ! 33: 0x001e, 0x003e, 0x007e, 0x00fe, ! 34: 0x01fe, 0x003e, 0x0036, 0x0062, ! 35: 0x0060, 0x00c0, 0x00c0, 0x0000 ! 36: }; ! 37: static short Default_GrayBits[] = { ! 38: 0xaaaa, 0x5555, 0xaaaa, 0x5555, ! 39: 0xaaaa, 0x5555, 0xaaaa, 0x5555, ! 40: 0xaaaa, 0x5555, 0xaaaa, 0x5555, ! 41: 0xaaaa, 0x5555, 0xaaaa, 0x5555, ! 42: }; ! 43: static short Default_MaskBits[] = { ! 44: 0x0003, 0x0007, 0x000f, 0x001f, ! 45: 0x003f, 0x007f, 0x00ff, 0x01ff, ! 46: 0x03ff, 0x07ff, 0x007f, 0x00f7, ! 47: 0x00f3, 0x01e1, 0x01e0, 0x01c0 ! 48: }; ! 49: static char def_menu_font[] = "vtsingle"; ! 50: ! 51: Pixmap Gray_Tile; ! 52: Menu Menu_Default; ! 53: Cursor Menu_DefaultCursor; ! 54: char *Menu_DefaultFont; ! 55: FontInfo *Menu_DefaultFontInfo; ! 56: ! 57: /* ! 58: * AddMenuItem() adds a menu item to an existing menu, at the end of the ! 59: * list, which are number sequentially from zero. The menuitem index is ! 60: * return, or -1 if failed. ! 61: */ ! 62: ! 63: AddMenuItem(menu, text) ! 64: register Menu *menu; ! 65: register char *text; ! 66: { ! 67: register MenuItem *menuitem, **next; ! 68: register int i; ! 69: extern char *malloc(); ! 70: ! 71: if(!menu || !text || (menuitem = (MenuItem *)malloc(sizeof(MenuItem))) ! 72: == (MenuItem *)0) ! 73: return(-1); ! 74: bzero((char *)menuitem, sizeof(MenuItem)); ! 75: menuitem->itemText = text; ! 76: menuitem->itemTextLength = strlen(text); ! 77: for(i = 0, next = &menu->menuItems ; *next ; i++) ! 78: next = &(*next)->nextItem; ! 79: *next = menuitem; ! 80: menu->menuFlags |= menuChanged; ! 81: return(i); ! 82: } ! 83: ! 84: /* ! 85: * DisposeItem() releases the memory allocated for the given indexed ! 86: * menuitem. Nonzero is returned if an item was actual disposed of. ! 87: */ ! 88: DisposeItem(menu, i) ! 89: register Menu *menu; ! 90: register int i; ! 91: { ! 92: register MenuItem **next, **last, *menuitem; ! 93: ! 94: if(!menu || i < 0) ! 95: return(0); ! 96: next = &menu->menuItems; ! 97: do { ! 98: if(!*next) ! 99: return(0); ! 100: last = next; ! 101: next = &(*next)->nextItem; ! 102: } while(i-- > 0); ! 103: menuitem = *last; ! 104: *last = *next; ! 105: free(menuitem); ! 106: return(1); ! 107: } ! 108: ! 109: /* ! 110: * DisposeMenu() releases the memory allocated for the given menu. ! 111: */ ! 112: DisposeMenu(menu) ! 113: register Menu *menu; ! 114: { ! 115: static Unmap_Menu(); ! 116: ! 117: if(!menu) ! 118: return; ! 119: if(menu->menuFlags & menuMapped) ! 120: Unmap_Menu(menu); ! 121: while(DisposeItem(menu, 0)); ! 122: if(menu->menuWindow) ! 123: XDestroyWindow(menu->menuWindow); ! 124: if(menu->menuSaved) ! 125: XFreePixmap(menu->menuSaved); ! 126: free(menu); ! 127: } ! 128: ! 129: InitMenu(name) ! 130: register char *name; ! 131: { ! 132: register char *cp; ! 133: register Bitmap bit; ! 134: ! 135: /* ! 136: * If the gray tile hasn't been set up, do it now. ! 137: */ ! 138: if(!Gray_Tile) { ! 139: if(!(bit = XStoreBitmap(grayWidth, grayHeight, ! 140: Default_GrayBits))) ! 141: return; ! 142: Gray_Tile = XMakePixmap(bit, WhitePixel, BlackPixel); ! 143: XFreeBitmap(bit); ! 144: } ! 145: Menu_Default.menuFlags = menuChanged; ! 146: if((cp = XGetDefault(name, "MenuFreeze")) && strcmp(cp, "on") == 0) ! 147: Menu_Default.menuFlags |= menuFreeze; ! 148: if((cp = XGetDefault(name, "MenuSave")) && strcmp(cp, "on") == 0) ! 149: Menu_Default.menuFlags |= menuSaveMenu; ! 150: Menu_Default.menuInitialItem = -1; ! 151: Menu_Default.menuBorderWidth = (cp = XGetDefault(name, "MenuBorder")) ? ! 152: atoi(cp) : 2; ! 153: Menu_Default.menuItemPad = (cp = XGetDefault(name, "MenuPad")) ? ! 154: atoi(cp) : 3; ! 155: Menu_DefaultFont = (cp = XGetDefault(name, "MenuFont")) ? cp : ! 156: def_menu_font; ! 157: }; ! 158: ! 159: /* ! 160: * ItemFlags returns the state of item "n" of the menu. ! 161: */ ! 162: ItemFlags(menu, n) ! 163: register Menu *menu; ! 164: register int n; ! 165: { ! 166: register MenuItem *item; ! 167: ! 168: if(!menu || !menu->menuItems || n < 0) ! 169: return(-1); ! 170: for(item = menu->menuItems ; n > 0 ; n--) ! 171: if(!(item = item->nextItem)) ! 172: return(0); ! 173: return((item->itemFlags & itemSetMask) >> itemSetMaskShift); ! 174: } ! 175: ! 176: /* ! 177: * ItemText changes the text of item "n" of the menu. ! 178: */ ! 179: ItemText(menu, n, text) ! 180: register Menu *menu; ! 181: register int n; ! 182: char *text; ! 183: { ! 184: register MenuItem *item; ! 185: ! 186: if(!menu || !menu->menuItems || n < 0 || !text) ! 187: return(0); ! 188: for(item = menu->menuItems ; n > 0 ; n--) ! 189: if(!(item = item->nextItem)) ! 190: return(0); ! 191: item->itemText = text; ! 192: menu->menuFlags |= menuChanged; ! 193: return(1); ! 194: } ! 195: ! 196: /* ! 197: * NewMenu() returns a pointer to an initialized new Menu structure, or NULL ! 198: * if failed. ! 199: * ! 200: * The Menu structure _menuDefault contains the default menu settings. ! 201: */ ! 202: Menu *NewMenu(name, reverse) ! 203: char *name; ! 204: int reverse; ! 205: { ! 206: register Menu *menu; ! 207: register int fg, bg; ! 208: extern char *malloc(); ! 209: ! 210: /* ! 211: * If the GrayTile hasn't been defined, InitMenu() was never ! 212: * run, so exit. ! 213: */ ! 214: if(!Gray_Tile) ! 215: return((Menu *)0); ! 216: /* ! 217: * Allocate the memory for the menu structure. ! 218: */ ! 219: if((menu = (Menu *)malloc(sizeof(Menu))) == (Menu *)0) ! 220: return((Menu *)0); ! 221: /* ! 222: * Initialize to default values. ! 223: */ ! 224: *menu = Menu_Default; ! 225: /* ! 226: * If the menu font hasn't yet been gotten, go get it. ! 227: */ ! 228: if(!menu->menuFontInfo) { ! 229: if(!Menu_DefaultFontInfo && !(Menu_DefaultFontInfo = ! 230: XOpenFont(Menu_DefaultFont))) ! 231: return((Menu *)0); ! 232: menu->menuFontInfo = Menu_DefaultFontInfo; ! 233: } ! 234: /* ! 235: * If the menu cursor hasn't been given, make a default one. ! 236: */ ! 237: if(!menu->menuCursor) { ! 238: if(!Menu_DefaultCursor) { ! 239: if(reverse) { ! 240: fg = WhitePixel; ! 241: bg = BlackPixel; ! 242: } else { ! 243: fg = BlackPixel; ! 244: bg = WhitePixel; ! 245: } ! 246: if(!(Menu_DefaultCursor = ! 247: XCreateCursor(defaultCursorWidth, defaultCursorHeight, ! 248: Default_CursorBits, Default_MaskBits, defaultCursorX, ! 249: defaultCursorY, fg, bg, GXcopy))) ! 250: return((Menu *)0); ! 251: } ! 252: menu->menuCursor = Menu_DefaultCursor; ! 253: } ! 254: /* ! 255: * Initialze the default background and border pixmaps and foreground ! 256: * and background colors (black and white). ! 257: */ ! 258: if(reverse) { ! 259: menu->menuBgTile = BlackPixmap; ! 260: menu->menuFgColor = WhitePixel; ! 261: menu->menuBgColor = BlackPixel; ! 262: } else { ! 263: menu->menuBgTile = WhitePixmap; ! 264: menu->menuFgColor = BlackPixel; ! 265: menu->menuBgColor = WhitePixel; ! 266: } ! 267: /* ! 268: * Set the menu title. If name is NULL or is an empty string, no ! 269: * title will be displayed. ! 270: */ ! 271: if(name && *name) { ! 272: menu->menuTitleLength = strlen(menu->menuTitle = name); ! 273: menu->menuTitleWidth = XStringWidth(name, menu->menuFontInfo, ! 274: 0, 0); ! 275: menu->menuItemTop = menu->menuFontInfo->height + 2 * ! 276: menu->menuItemPad + 1; ! 277: } else ! 278: menu->menuTitleLength = menu->menuTitleWidth = ! 279: menu->menuItemTop = 0; ! 280: return(menu); ! 281: } ! 282: ! 283: /* ! 284: * SetItemCheck sets the check state of item "n" of the menu to "state". ! 285: */ ! 286: SetItemCheck(menu, n, state) ! 287: register Menu *menu; ! 288: register int n; ! 289: int state; ! 290: { ! 291: register MenuItem *item; ! 292: ! 293: if(!menu || !menu->menuItems || n < 0) ! 294: return(0); ! 295: for(item = menu->menuItems ; n > 0 ; n--) ! 296: if(!(item = item->nextItem)) ! 297: return(0); ! 298: if(state) ! 299: item->itemFlags |= itemSetChecked; ! 300: else ! 301: item->itemFlags &= ~itemSetChecked; ! 302: if(((item->itemFlags & itemSetMask) >> itemSetMaskShift) != ! 303: (item->itemFlags & itemStateMask)) { ! 304: item->itemFlags |= itemChanged; ! 305: menu->menuFlags |= menuItemChanged; ! 306: } else ! 307: item->itemFlags &= ~itemChanged; ! 308: return(1); ! 309: } ! 310: ! 311: /* ! 312: * SetItemDisable sets the disable state of item "n" of the menu to "state". ! 313: */ ! 314: SetItemDisable(menu, n, state) ! 315: register Menu *menu; ! 316: register int n; ! 317: int state; ! 318: { ! 319: register MenuItem *item; ! 320: ! 321: if(!menu || !menu->menuItems || n < 0) ! 322: return(0); ! 323: for(item = menu->menuItems ; n > 0 ; n--) ! 324: if(!(item = item->nextItem)) ! 325: return(0); ! 326: if(state) ! 327: item->itemFlags |= itemSetDisabled; ! 328: else ! 329: item->itemFlags &= ~itemSetDisabled; ! 330: if(((item->itemFlags & itemSetMask) >> itemSetMaskShift) != ! 331: (item->itemFlags & itemStateMask)) { ! 332: item->itemFlags |= itemChanged; ! 333: menu->menuFlags |= menuItemChanged; ! 334: } else ! 335: item->itemFlags &= ~itemChanged; ! 336: return(1); ! 337: } ! 338: ! 339: /* ! 340: * TrackMenu does most of the work of displaying the menu and tracking the ! 341: * mouse. ! 342: */ ! 343: TrackMenu(menu, event) ! 344: register Menu *menu; ! 345: register XButtonPressedEvent *event; ! 346: { ! 347: register MenuItem *item; ! 348: register int i, button; ! 349: register MenuItem *hilited_item = (MenuItem *)0; ! 350: register int drawn; ! 351: XButtonReleasedEvent ev; ! 352: register int changed; ! 353: int y, n, hilited_y, hilited_n, in_window; ! 354: static MenuItem *Mouse_InItem(), *Y_InItem(); ! 355: static Unmap_Menu(); ! 356: ! 357: /* ! 358: * Check that things are reasonable. ! 359: */ ! 360: if(!menu || !event || !menu->menuItems || event->type != ButtonPressed) ! 361: return(-1); ! 362: /* ! 363: * Set the changed flag and clear the menu changed flags. ! 364: */ ! 365: changed = menu->menuFlags & (menuChanged | menuItemChanged); ! 366: /* ! 367: * If the entire menu has changed, throw away any saved pixmap and ! 368: * then call RecalcMenu(). ! 369: */ ! 370: if(changed & menuChanged) { ! 371: if(menu->menuSaved) ! 372: XFreePixmap(menu->menuSaved); ! 373: menu->menuSaved = (Pixmap)0; ! 374: if(!Recalc_Menu(menu)) ! 375: return(-1); ! 376: changed &= ~menuItemChanged; ! 377: } ! 378: /* ! 379: * Now if the window was never created, go ahead and make it. Otherwise ! 380: * if the menu has changed, resize the window. ! 381: */ ! 382: if(!menu->menuWindow) { ! 383: if((menu->menuWindow = XCreateWindow(RootWindow, 0, 0, ! 384: menu->menuWidth, menu->menuHeight, menu->menuBorderWidth, ! 385: Gray_Tile, menu->menuBgTile)) == (Window)0) ! 386: return(-1); ! 387: XDefineCursor(menu->menuWindow, menu->menuCursor); ! 388: XSelectInput(menu->menuWindow, ExposeWindow | EnterWindow | ! 389: LeaveWindow | MouseMoved | ButtonReleased); ! 390: } else if(changed & menuChanged) ! 391: XChangeWindow(menu->menuWindow, menu->menuWidth, ! 392: menu->menuHeight); ! 393: /* ! 394: * Figure out where the menu is supposed to go, from the initial button ! 395: * press, and move the window there. Then map the menu. ! 396: */ ! 397: if(!Move_Menu(menu, event) || !Map_Menu(menu)) ! 398: return(-1); ! 399: /* ! 400: * Try to grab the mouse, over a period of 10 seconds. ! 401: */ ! 402: for(i = 10 ; ; ) { ! 403: if(XGrabMouse(menu->menuWindow, menu->menuCursor, ! 404: ButtonReleased | EnterWindow | LeaveWindow | MouseMoved)) ! 405: break; ! 406: if(--i <= 0) { ! 407: Unmap_Menu(menu); ! 408: return(-1); ! 409: } ! 410: sleep(1); ! 411: } ! 412: /* ! 413: * Save away the button that was pressed and use it to match a ! 414: * corresponding ButtonReleased event. ! 415: */ ! 416: button = event->detail & 03; ! 417: /* ! 418: * Now process events for the menu window. ! 419: */ ! 420: drawn = 0; ! 421: for( ; ; ) { ! 422: XNextEvent(&ev); ! 423: if(ev.type != ButtonReleased && ev.window != menu->menuWindow) { ! 424: if(menu->menuEventHandler) ! 425: (*menu->menuEventHandler)(&ev); ! 426: continue; ! 427: } ! 428: switch(ev.type) { ! 429: case ExposeWindow: ! 430: /* ! 431: * If we have a saved pixmap, display it. Otherwise ! 432: * redraw the menu and save it away. ! 433: */ ! 434: if(menu->menuSaved) { ! 435: XPixmapPut(menu->menuWindow, 0, 0, 0, 0, ! 436: menu->menuWidth, menu->menuHeight, ! 437: menu->menuSaved, GXcopy, AllPlanes); ! 438: /* ! 439: * If the menuItemChanged flag is still set, ! 440: * then we need to redraw certain menu items. ! 441: * ("i" is the vertical position of the top ! 442: * of the current item.) ! 443: */ ! 444: if(changed & menuItemChanged) { ! 445: i = menu->menuItemTop; ! 446: for(item = menu->menuItems ; item ; ! 447: item = item->nextItem) { ! 448: if(item->itemFlags & ! 449: itemChanged) ! 450: Modify_Item(menu, item, ! 451: i); ! 452: i += item->itemHeight; ! 453: } ! 454: } ! 455: } else ! 456: Draw_Menu(menu); ! 457: /* ! 458: * If the menu has changed in any way and we want to ! 459: * save the menu, throw away any existing save menu ! 460: * image and make a new one. ! 461: */ ! 462: XFlush(); ! 463: if(changed && (menu->menuFlags & menuSaveMenu)) { ! 464: if(menu->menuSaved) ! 465: XFreePixmap(menu->menuSaved); ! 466: menu->menuSaved = XPixmapSave(menu->menuWindow, ! 467: 0, 0, menu->menuWidth, menu->menuHeight); ! 468: } ! 469: /* ! 470: * See which item the cursor may currently be in. If ! 471: * it is in a non-disabled item, hilite it. ! 472: */ ! 473: if(hilited_item = Mouse_InItem(menu, &hilited_y, ! 474: &hilited_n, &in_window)) ! 475: XPixFill(menu->menuWindow, 0, hilited_y, ! 476: menu->menuWidth, hilited_item->itemHeight, ! 477: BlackPixmap, (Bitmap)0, GXinvert, InvertPlane); ! 478: drawn++; ! 479: break; ! 480: case EnterWindow: ! 481: in_window = TRUE; ! 482: /* drop through */ ! 483: case MouseMoved: ! 484: if(!drawn || !in_window) ! 485: break; ! 486: /* ! 487: * See which item the cursor may currently be in. If ! 488: * the item has changed, unhilite the old one and ! 489: * then hilited the new one. ! 490: */ ! 491: y = ((XEnterWindowEvent *)&ev)->y; ! 492: if((item = Y_InItem(menu, &y, &n)) != hilited_item) { ! 493: if(hilited_item) ! 494: XPixFill(menu->menuWindow, 0, ! 495: hilited_y, menu->menuWidth, ! 496: hilited_item->itemHeight, BlackPixmap, ! 497: (Bitmap)0, GXinvert, InvertPlane); ! 498: if(hilited_item = item) { ! 499: XPixFill(menu->menuWindow, 0, ! 500: hilited_y = y, menu->menuWidth, ! 501: item->itemHeight, BlackPixmap, ! 502: (Bitmap)0, GXinvert, InvertPlane); ! 503: hilited_n = n; ! 504: } ! 505: } ! 506: break; ! 507: case LeaveWindow: ! 508: if(!drawn) ! 509: break; ! 510: /* ! 511: * Unhilite any window that is currently hilited. ! 512: */ ! 513: if(hilited_item) { ! 514: XPixFill(menu->menuWindow, 0, hilited_y, ! 515: menu->menuWidth, hilited_item->itemHeight, ! 516: BlackPixmap, (Bitmap)0, GXinvert, InvertPlane); ! 517: hilited_item = (MenuItem *)0; ! 518: } ! 519: in_window = FALSE; ! 520: break; ! 521: case ButtonReleased: ! 522: /* ! 523: * If the correct button was released, ungrab the mouse ! 524: * and return the index number of any selected menu ! 525: * item. ! 526: */ ! 527: if((ev.detail & 0x3) == button) { ! 528: if(in_window) { ! 529: y = ((XButtonReleasedEvent *)&ev)->y; ! 530: if((item = Y_InItem(menu, &y, &n)) != ! 531: hilited_item) { ! 532: if(hilited_item) ! 533: XPixFill(menu->menuWindow, 0, ! 534: hilited_y, menu->menuWidth, ! 535: hilited_item->itemHeight, ! 536: BlackPixmap, (Bitmap)0, ! 537: GXinvert, InvertPlane); ! 538: if(hilited_item = item) { ! 539: XPixFill(menu->menuWindow, 0, ! 540: hilited_y = y, menu->menuWidth, ! 541: hilited_item->itemHeight, ! 542: BlackPixmap, (Bitmap)0, ! 543: GXinvert, InvertPlane); ! 544: hilited_n = n; ! 545: } ! 546: } ! 547: } ! 548: XUngrabMouse(); ! 549: menu->menuFlags &= ~(menuChanged | ! 550: menuItemChanged); ! 551: Unmap_Menu(menu); ! 552: XFlush(); ! 553: if(hilited_item) ! 554: return(menu->menuInitialItem = ! 555: hilited_n); ! 556: return(-1); ! 557: } ! 558: break; ! 559: } ! 560: } ! 561: } ! 562: ! 563: /* ! 564: * Recalculate all of the various menu and item variables. ! 565: */ ! 566: static Recalc_Menu(menu) ! 567: register Menu *menu; ! 568: { ! 569: register MenuItem *item; ! 570: register int max, i, height, fontheight; ! 571: ! 572: /* ! 573: * We must have already gotten the menu font. ! 574: */ ! 575: if(!menu->menuFontInfo) ! 576: return(0); ! 577: /* ! 578: * Initialize the various max width variables. ! 579: */ ! 580: fontheight = menu->menuFontInfo->height; ! 581: height = menu->menuItemTop; ! 582: menu->menuMaxTextWidth = menu->menuTitleWidth; ! 583: /* ! 584: * The item height is the maximum of the font height and the ! 585: * checkbox height. ! 586: */ ! 587: max = fontheight; ! 588: if(checkMarkHeight > max) ! 589: max = checkMarkHeight; ! 590: /* ! 591: * Go through the menu item list. ! 592: */ ! 593: for(item = menu->menuItems ; item ; item = item->nextItem) { ! 594: /* ! 595: * If the item text is a single dash, we assume this is ! 596: * a line separator and treat it special. ! 597: */ ! 598: if(strcmp(item->itemText, "-") == 0) ! 599: height += (item->itemHeight = lineSeparatorHeight); ! 600: else { ! 601: height += (item->itemHeight = max); ! 602: /* ! 603: * Check the text width with the max value stored in ! 604: * menu. ! 605: */ ! 606: if((item->itemTextWidth = XStringWidth(item->itemText, ! 607: menu->menuFontInfo, 0, 0)) > menu->menuMaxTextWidth) ! 608: menu->menuMaxTextWidth = item->itemTextWidth; ! 609: } ! 610: /* ! 611: * If the itemChanged flag is set, set the state bits. ! 612: */ ! 613: if(item->itemFlags & itemChanged) { ! 614: item->itemFlags = (item->itemFlags & ~itemStateMask) | ! 615: ((item->itemFlags & itemSetMask) >> itemSetMaskShift); ! 616: item->itemFlags &= ~itemChanged; ! 617: } ! 618: } ! 619: /* ! 620: * Set the menu height and then set the menu width. ! 621: */ ! 622: menu->menuHeight = height; ! 623: menu->menuWidth = 3 * menu->menuItemPad + menu->menuMaxTextWidth + ! 624: checkMarkWidth; ! 625: return(1); ! 626: } ! 627: ! 628: /* ! 629: * Figure out where to popup the menu, relative to the where the button was ! 630: * pressed. ! 631: */ ! 632: static Move_Menu(menu, ev) ! 633: register Menu *menu; ! 634: XButtonPressedEvent *ev; ! 635: { ! 636: register MenuItem *item; ! 637: register int n, x, y; ! 638: int ev_x, ev_y; ! 639: int total_width; ! 640: Window subw; ! 641: extern int dropmenu; /* XXX */ ! 642: ! 643: /* ! 644: * Get the coordinates of the mouse when the button was pressed. ! 645: */ ! 646: XInterpretLocator(RootWindow, &ev_x, &ev_y, &subw, ev->location); ! 647: /* ! 648: * Try to popup the menu so that the cursor is centered within the ! 649: * width of the menu, but compensate if that would run it outside ! 650: * the display area. ! 651: */ ! 652: total_width = menu->menuWidth + 2 * menu->menuBorderWidth; ! 653: if((x = ev_x - total_width / 2) < 0) ! 654: x = 0; ! 655: else if(x + total_width > DisplayWidth()) ! 656: x = DisplayWidth() - total_width; ! 657: if (dropmenu) ! 658: y = 0; ! 659: else if(menu->menuInitialItem >= 0) { ! 660: /* ! 661: * If we have an inital item, try to popup the menu centered ! 662: * vertically within this item. ! 663: * ! 664: * Look through the item list. "y" is the vertical position ! 665: * of the top of the current item and "n" is the item number. ! 666: */ ! 667: y = menu->menuItemTop + menu->menuBorderWidth; ! 668: for(n = 0, item = menu->menuItems ; ; n++) { ! 669: /* ! 670: * On finding the intial item, center within this item. ! 671: */ ! 672: if(n == menu->menuInitialItem) { ! 673: y += item->itemHeight / 2; ! 674: break; ! 675: } ! 676: y += item->itemHeight; ! 677: /* ! 678: * If we run out of items, turn off the initial item ! 679: * and treat this as if no initial item. ! 680: */ ! 681: if(!(item = item->nextItem)) { ! 682: menu->menuInitialItem = -1; ! 683: goto noInitial; ! 684: } ! 685: } ! 686: /* ! 687: * If no initial item, try to popup the menu centered in the item ! 688: * nearest the center of the menu. ! 689: */ ! 690: } else { ! 691: noInitial: ! 692: /* ! 693: * Look through the item list. "y" is the vertical position ! 694: * of the top of the current item and "n" is the vertical ! 695: * position of the center of the menu. ! 696: */ ! 697: y = menu->menuItemTop + menu->menuBorderWidth; ! 698: for(n = menu->menuHeight / 2, item = menu->menuItems ; item ; ! 699: item = item->nextItem) ! 700: /* ! 701: * If the center of the menu is in this item, we ! 702: * center within this item. ! 703: */ ! 704: if((y += item->itemHeight) > n) { ! 705: y -= item->itemHeight / 2; ! 706: break; ! 707: } ! 708: } ! 709: /* ! 710: * If the menu extends above outside of the display, warp ! 711: * the mouse vertically so the menu will all show up. ! 712: */ ! 713: if((y = ev_y - y) < 0) { ! 714: XWarpMouse(RootWindow, ev_x, ev_y - y); ! 715: y = 0; ! 716: } else if((n = y + menu->menuHeight + 2 * menu->menuBorderWidth ! 717: - DisplayHeight()) > 0) { ! 718: XWarpMouse(RootWindow, ev_x, ev_y - n); ! 719: y -= n; ! 720: } ! 721: XMoveWindow(menu->menuWindow, x, y); ! 722: /* ! 723: * If we are in freeze mode, save what will be the coordinates of ! 724: * the save image. ! 725: */ ! 726: if(menu->menuFlags & menuFreeze) { ! 727: menu->menuSavedImageX = x; ! 728: menu->menuSavedImageY = y; ! 729: } ! 730: return(1); ! 731: } ! 732: ! 733: /* ! 734: * Map the menu window. ! 735: */ ! 736: static Map_Menu(menu) ! 737: register Menu *menu; ! 738: { ! 739: register int i; ! 740: ! 741: /* ! 742: * If we are in freeze mode, save the pixmap underneath where the menu ! 743: * will be (including the border). ! 744: */ ! 745: if(menu->menuFlags & menuFreeze) { ! 746: XGrabServer(); ! 747: i = 2 * menu->menuBorderWidth; ! 748: if((menu->menuSavedImage = XPixmapSave(RootWindow, ! 749: menu->menuSavedImageX, menu->menuSavedImageY, menu->menuWidth ! 750: + i, menu->menuHeight + i)) == (Pixmap)0) ! 751: return(0); ! 752: } ! 753: /* ! 754: * Actually map the window. ! 755: */ ! 756: XMapWindow(menu->menuWindow); ! 757: menu->menuFlags |= menuMapped; ! 758: return(1); ! 759: } ! 760: ! 761: /* ! 762: * Draw the entire menu in the blank window. ! 763: */ ! 764: static Draw_Menu(menu) ! 765: register Menu *menu; ! 766: { ! 767: register MenuItem *item; ! 768: register int top = menu->menuItemTop; ! 769: register int x = menu->menuItemPad; ! 770: register int y, dim; ! 771: ! 772: /* ! 773: * If we have a menu title, draw it first, centered and hilited. ! 774: */ ! 775: if(menu->menuTitleLength) { ! 776: XPixSet(menu->menuWindow, 0, 0, menu->menuWidth, ! 777: top - 1, menu->menuFgColor); ! 778: XText(menu->menuWindow, (menu->menuWidth - ! 779: menu->menuTitleWidth) / 2, menu->menuItemPad, menu->menuTitle, ! 780: menu->menuTitleLength, menu->menuFontInfo->id, ! 781: menu->menuBgColor, menu->menuFgColor); ! 782: } ! 783: /* ! 784: * For each item in the list, first draw any check mark and then ! 785: * draw the rest of it. ! 786: */ ! 787: for(item = menu->menuItems ; item ; item = item->nextItem) { ! 788: SetStateFlags(item); ! 789: dim = (item->itemFlags & itemDisabled); ! 790: /* ! 791: * Draw the check mark, possibly dimmed, wherever is necessary. ! 792: */ ! 793: if(item->itemFlags & itemChecked) { ! 794: XBitmapBitsPut(menu->menuWindow, x, y = top + ! 795: (item->itemHeight - checkMarkHeight) / 2, ! 796: checkMarkWidth, checkMarkHeight, dim ? Check_GrayBits : ! 797: Check_MarkBits, menu->menuFgColor, menu->menuBgColor, ! 798: (Bitmap)0, GXcopy, AllPlanes); ! 799: } ! 800: /* ! 801: * Draw the item, possibly dimmed. ! 802: */ ! 803: Draw_Item(menu, item, top, dim); ! 804: top += item->itemHeight; ! 805: } ! 806: } ! 807: ! 808: /* ! 809: * Modify the item at vertical position y. This routine is table driven and ! 810: * the state and set bits are each 2 bits long, contiguous, the least ! 811: * significant bits in the flag word and with the state bits in bits 0 & 1. ! 812: */ ! 813: ! 814: #define drawCheck 0x10 ! 815: #define removeCheck 0x08 ! 816: #define dimCheck 0x04 ! 817: #define drawItem 0x02 ! 818: #define dimItem 0x01 ! 819: ! 820: static char Modify_Table[] = { ! 821: 0x00, 0x02, 0x08, 0x0a, 0x01, 0x00, 0x09, 0x08, ! 822: 0x10, 0x12, 0x00, 0x12, 0x15, 0x14, 0x05, 0x00 ! 823: }; ! 824: ! 825: static Modify_Item(menu, item, top) ! 826: register Menu *menu; ! 827: register MenuItem *item; ! 828: int top; ! 829: { ! 830: register int x = menu->menuItemPad; ! 831: register int y; ! 832: register int center = top + item->itemHeight / 2; ! 833: register int func = Modify_Table[item->itemFlags & ! 834: (itemStateMask | itemSetMask)]; ! 835: ! 836: /* ! 837: * If we really won't be making a change, return. ! 838: */ ! 839: if(func == 0) ! 840: return; ! 841: /* ! 842: * Draw the check mark if needed, possibly dimmed. ! 843: */ ! 844: y = center - (checkMarkHeight / 2); ! 845: if(func & (drawCheck | dimCheck)) ! 846: XBitmapBitsPut(menu->menuWindow, x, y, checkMarkWidth, ! 847: checkMarkHeight, (func & dimCheck) ? Check_GrayBits : ! 848: Check_MarkBits, menu->menuFgColor, menu->menuBgColor, ! 849: (Bitmap)0, GXcopy, AllPlanes); ! 850: /* ! 851: * Remove the check mark if needed. ! 852: */ ! 853: if(func & removeCheck) ! 854: XTileSet(menu->menuWindow, x, y, checkMarkWidth, ! 855: checkMarkHeight, menu->menuBgTile); ! 856: /* ! 857: * Call Draw_Item if we need to draw or dim the item. ! 858: */ ! 859: if((x = func & dimItem) || (func & drawItem)) ! 860: Draw_Item(menu, item, top, x); ! 861: /* ! 862: * Update state flags. ! 863: */ ! 864: SetStateFlags(item); ! 865: } ! 866: ! 867: /* ! 868: * Draw the item (less check mark) at vertical position y. ! 869: * Dim the item if "dim" is set. ! 870: */ ! 871: static Draw_Item(menu, item, y, dim) ! 872: register Menu *menu; ! 873: register MenuItem *item; ! 874: register int y; ! 875: int dim; ! 876: { ! 877: register int x = 2 * menu->menuItemPad + checkMarkWidth; ! 878: register int center = y + item->itemHeight / 2; ! 879: ! 880: /* ! 881: * If the item text is a single dash, draw a separating line. ! 882: */ ! 883: if(strcmp(item->itemText, "-") == 0) { ! 884: XLine(menu->menuWindow, 0, center, menu->menuWidth, center, ! 885: 1, 1, menu->menuFgColor, GXcopy, AllPlanes); ! 886: return; ! 887: } ! 888: /* ! 889: * Draw and/or dim the text, centered vertically. ! 890: */ ! 891: y = center - (menu->menuFontInfo->height / 2); ! 892: if(dim) { ! 893: XTileSet(menu->menuWindow, x, y, item->itemTextWidth, ! 894: menu->menuFontInfo->height, Gray_Tile); ! 895: XTextPad(menu->menuWindow, x, y, item->itemText, ! 896: item->itemTextLength, menu->menuFontInfo->id, 0, 0, ! 897: menu->menuFgColor, menu->menuBgColor, menu->menuFgColor ? ! 898: GXand : GXor, AllPlanes); ! 899: } else ! 900: XText(menu->menuWindow, x, y, item->itemText, ! 901: item->itemTextLength, menu->menuFontInfo->id, ! 902: menu->menuFgColor, menu->menuBgColor); ! 903: } ! 904: ! 905: /* ! 906: * Determine which enabled menu item the mouse is currently in. Return the ! 907: * top position of this item and its item number. Set inwindow to whether ! 908: * we are or not. ! 909: */ ! 910: static MenuItem *Mouse_InItem(menu, top, n, inwindow) ! 911: register Menu *menu; ! 912: int *top, *n, *inwindow; ! 913: { ! 914: int x, y; ! 915: Window subw; ! 916: static MenuItem *Y_InItem(); ! 917: ! 918: /* ! 919: * Find out where the mouse is. If its not in the menu window, ! 920: * return NULL. ! 921: */ ! 922: XQueryMouse(RootWindow, &x, &y, &subw); ! 923: if(subw != menu->menuWindow) { ! 924: *inwindow = FALSE; ! 925: return((MenuItem *)0); ! 926: } ! 927: *inwindow = TRUE; ! 928: /* ! 929: * Now get the coordinates relative to the menu window. ! 930: */ ! 931: XInterpretLocator(menu->menuWindow, &x, &y, &subw, (x << 16) | y); ! 932: /* ! 933: * Call Y_InItem(). ! 934: */ ! 935: *top = y; ! 936: return(Y_InItem(menu, top, n)); ! 937: } ! 938: ! 939: /* ! 940: * Return which enabled item the locator is in. Also return the ! 941: * top position of this item and its item number. Initial y passed ! 942: * in top. ! 943: */ ! 944: static MenuItem *Y_InItem(menu, top, n) ! 945: register Menu *menu; ! 946: int *top, *n; ! 947: { ! 948: register MenuItem *item; ! 949: register int t, i; ! 950: register int y = *top; ! 951: Window subw; ! 952: ! 953: /* ! 954: * Go through the item list. "t" is the vertical position of the ! 955: * current item and "i" is its item number. ! 956: */ ! 957: t = menu->menuItemTop; ! 958: /* ! 959: * If the mouse is before the first item, return. ! 960: */ ! 961: if(y < t) ! 962: return((MenuItem *)0); ! 963: for(i = 0, item = menu->menuItems ; item ; i++, item = item->nextItem) { ! 964: /* ! 965: * If the y coordinate is within this menu item, then return. ! 966: * But don't return disable items. ! 967: */ ! 968: if(t + item->itemHeight > y) { ! 969: if(item->itemFlags & itemDisabled) ! 970: return((MenuItem *)0); ! 971: *top = t; ! 972: *n = i; ! 973: return(item); ! 974: } ! 975: t += item->itemHeight; ! 976: } ! 977: /* ! 978: * Should never get here. ! 979: */ ! 980: return((MenuItem *)0); ! 981: } ! 982: ! 983: /* ! 984: * Unmap_Menu() unmaps a menu, if it is currently mapped. ! 985: */ ! 986: static Unmap_Menu(menu) ! 987: register Menu *menu; ! 988: { ! 989: register int i; ! 990: ! 991: if(!menu || !(menu->menuFlags & menuMapped)) ! 992: return; ! 993: if(menu->menuFlags & menuFreeze) { ! 994: XUnmapTransparent(menu->menuWindow); ! 995: i = 2 * menu->menuBorderWidth; ! 996: XPixmapPut(RootWindow, 0, 0, menu->menuSavedImageX, ! 997: menu->menuSavedImageY, menu->menuWidth + i, ! 998: menu->menuHeight + i, menu->menuSavedImage, ! 999: GXcopy, AllPlanes); ! 1000: XFreePixmap(menu->menuSavedImage); ! 1001: XUngrabServer(); ! 1002: } else ! 1003: XUnmapWindow(menu->menuWindow); ! 1004: menu->menuFlags &= ~menuMapped; ! 1005: } ! 1006: #endif MODEMENU
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.