menus.c revision 29dcb7bf
1/* 2 * Copyright 1988 by Evans & Sutherland Computer Corporation, 3 * Salt Lake City, Utah 4 * Portions Copyright 1989 by the Massachusetts Institute of Technology 5 * Cambridge, Massachusetts 6 * 7 * Copyright 1992 Claude Lecommandeur. 8 */ 9 10/*********************************************************************** 11 * 12 * $XConsortium: menus.c,v 1.186 91/07/17 13:58:00 dave Exp $ 13 * 14 * twm menu code 15 * 16 * 17-Nov-87 Thomas E. LaStrange File created 17 * 18 * Do the necessary modification to be integrated in ctwm. 19 * Can no longer be used for the standard twm. 20 * 21 * 22-April-92 Claude Lecommandeur. 22 * 23 * 24 ***********************************************************************/ 25 26#include "ctwm.h" 27 28#include <stdio.h> 29#include <stdlib.h> 30#include <string.h> 31#include <strings.h> 32 33#include "add_window.h" 34#include "colormaps.h" 35#include "drawing.h" 36#include "events.h" 37#include "functions.h" 38#include "functions_defs.h" 39#include "gram.tab.h" 40#include "iconmgr.h" 41#include "icons_builtin.h" 42#include "icons.h" 43#include "image.h" 44#include "list.h" 45#include "occupation.h" 46#include "otp.h" 47#include "screen.h" 48#ifdef SOUNDS 49# include "sound.h" 50#endif 51#include "util.h" 52#include "vscreen.h" 53#include "win_iconify.h" 54#include "win_resize.h" 55#include "win_utils.h" 56#include "workspace_manager.h" 57 58MenuRoot *ActiveMenu = NULL; /* the active menu */ 59MenuItem *ActiveItem = NULL; /* the active menu item */ 60bool menuFromFrameOrWindowOrTitlebar = false; 61char *CurrentSelectedWorkspace; 62 63/* Should probably move, since nothing in this file uses anymore */ 64int AlternateKeymap; 65bool AlternateContext; 66 67int MenuDepth = 0; /* number of menus up */ 68static struct { 69 int x; 70 int y; 71} MenuOrigins[MAXMENUDEPTH]; 72static bool addingdefaults = false; 73 74 75 76static void Paint3DEntry(MenuRoot *mr, MenuItem *mi, bool exposure); 77static void PaintNormalEntry(MenuRoot *mr, MenuItem *mi, bool exposure); 78static void DestroyMenu(MenuRoot *menu); 79 80 81#define SHADOWWIDTH 5 /* in pixels */ 82#define ENTRY_SPACING 4 83 84 85/*********************************************************************** 86 * 87 * Procedure: 88 * AddFuncKey - add a function key to the list 89 * 90 * Inputs: 91 * name - the name of the key 92 * cont - the context to look for the key press in 93 * nmods - modifier keys that need to be pressed 94 * func - the function to perform 95 * win_name- the window name (if any) 96 * action - the action string associated with the function (if any) 97 * 98 *********************************************************************** 99 */ 100 101bool 102AddFuncKey(char *name, int cont, int nmods, int func, 103 MenuRoot *menu, char *win_name, char *action) 104{ 105 FuncKey *tmp; 106 KeySym keysym = NoSymbol; 107 KeyCode keycode = 0; 108 109 /* 110 * Don't let a 0 keycode go through, since that means AnyKey to the 111 * XGrabKey call in GrabKeys(). Conditionalize on dpy to handle 112 * special cases where we don't have a server to talk to. 113 */ 114 keysym = XStringToKeysym(name); 115 if(dpy) { 116 keycode = XKeysymToKeycode(dpy, keysym); 117 } 118 if(keysym == NoSymbol || (dpy && keycode == 0)) { 119 fprintf(stderr, "ignore %s key binding (%s)\n", name, 120 keysym == NoSymbol 121 ? "key symbol not found" 122 : "key code not found"); 123 return false; 124 } 125 126 /* see if there already is a key defined for this context */ 127 for(tmp = Scr->FuncKeyRoot.next; tmp != NULL; tmp = tmp->next) { 128 if(tmp->keysym == keysym && 129 tmp->cont == cont && 130 tmp->mods == nmods) { 131 break; 132 } 133 } 134 135 if(tmp == NULL) { 136 tmp = malloc(sizeof(FuncKey)); 137 tmp->next = Scr->FuncKeyRoot.next; 138 Scr->FuncKeyRoot.next = tmp; 139 } 140 141 tmp->name = name; 142 tmp->keysym = keysym; 143 tmp->keycode = keycode; 144 tmp->cont = cont; 145 tmp->mods = nmods; 146 tmp->func = func; 147 tmp->menu = menu; 148 tmp->win_name = win_name; 149 tmp->action = action; 150 151 return true; 152} 153 154/*********************************************************************** 155 * 156 * Procedure: 157 * AddFuncButton - add a function button to the list 158 * 159 * Inputs: 160 * num - the num of the button 161 * cont - the context to look for the key press in 162 * nmods - modifier keys that need to be pressed 163 * func - the function to perform 164 * menu - the menu (if any) 165 * item - the menu item (if any) 166 * 167 *********************************************************************** 168 */ 169 170void 171AddFuncButton(int num, int cont, int nmods, int func, 172 MenuRoot *menu, MenuItem *item) 173{ 174 FuncButton *tmp; 175 176 /* Find existing def for this button/context/modifier if any */ 177 for(tmp = Scr->FuncButtonRoot.next; tmp != NULL; tmp = tmp->next) { 178 if((tmp->num == num) && (tmp->cont == cont) && (tmp->mods == nmods)) { 179 break; 180 } 181 } 182 183 /* 184 * If it's already set, and we're addingdefault (i.e., called from 185 * AddDefaultFuncButtons()), just return. This lets us cram on 186 * fallback mappings, without worrying about overriding user choices. 187 */ 188 if(tmp && addingdefaults) { 189 return; 190 } 191 192 /* No mapping yet; create a shell */ 193 if(tmp == NULL) { 194 tmp = malloc(sizeof(FuncButton)); 195 tmp->next = Scr->FuncButtonRoot.next; 196 Scr->FuncButtonRoot.next = tmp; 197 } 198 199 /* Set the new details */ 200 tmp->num = num; 201 tmp->cont = cont; 202 tmp->mods = nmods; 203 tmp->func = func; 204 tmp->menu = menu; 205 tmp->item = item; 206 207 return; 208} 209 210 211/* 212 * AddDefaultFuncButtons - attach default bindings so that naive users 213 * don't get messed up if they provide a minimal twmrc. 214 * 215 * This used to be in add_window.c, and maybe fits better in 216 * decorations_init.c (only place it's called) now, but is currently here 217 * so addingdefaults is in scope. 218 * 219 * XXX Probably better to adjust things so we can do that job _without_ 220 * the magic global var... 221 */ 222void 223AddDefaultFuncButtons(void) 224{ 225 addingdefaults = true; 226 227#define SETDEF(btn, ctx, func) AddFuncButton(btn, ctx, 0, func, NULL, NULL) 228 SETDEF(Button1, C_TITLE, F_MOVE); 229 SETDEF(Button1, C_ICON, F_ICONIFY); 230 SETDEF(Button1, C_ICONMGR, F_ICONIFY); 231 232 SETDEF(Button2, C_TITLE, F_RAISELOWER); 233 SETDEF(Button2, C_ICON, F_ICONIFY); 234 SETDEF(Button2, C_ICONMGR, F_ICONIFY); 235#undef SETDEF 236 237 addingdefaults = false; 238} 239 240 241void 242PaintEntry(MenuRoot *mr, MenuItem *mi, bool exposure) 243{ 244 if(Scr->use3Dmenus) { 245 Paint3DEntry(mr, mi, exposure); 246 } 247 else { 248 PaintNormalEntry(mr, mi, exposure); 249 } 250 if(mi->state) { 251 mr->lastactive = mi; 252 } 253} 254 255static void 256Paint3DEntry(MenuRoot *mr, MenuItem *mi, bool exposure) 257{ 258 int y_offset; 259 int text_y; 260 GC gc; 261 XRectangle ink_rect, logical_rect; 262 XmbTextExtents(Scr->MenuFont.font_set, mi->item, mi->strlen, 263 &ink_rect, &logical_rect); 264 265 y_offset = mi->item_num * Scr->EntryHeight + Scr->MenuShadowDepth; 266 text_y = y_offset + (Scr->EntryHeight - logical_rect.height) / 2 267 - logical_rect.y; 268 269 if(mi->func != F_TITLE) { 270 int x, y; 271 272 gc = Scr->NormalGC; 273 if(mi->state) { 274 Draw3DBorder(mr->w, Scr->MenuShadowDepth, y_offset, 275 mr->width - 2 * Scr->MenuShadowDepth, Scr->EntryHeight, 1, 276 mi->highlight, off, true, false); 277 FB(mi->highlight.fore, mi->highlight.back); 278 XmbDrawImageString(dpy, mr->w, Scr->MenuFont.font_set, gc, 279 mi->x + Scr->MenuShadowDepth, text_y, mi->item, mi->strlen); 280 } 281 else { 282 if(mi->user_colors || !exposure) { 283 XSetForeground(dpy, gc, mi->normal.back); 284 XFillRectangle(dpy, mr->w, gc, 285 Scr->MenuShadowDepth, y_offset, 286 mr->width - 2 * Scr->MenuShadowDepth, Scr->EntryHeight); 287 FB(mi->normal.fore, mi->normal.back); 288 } 289 else { 290 gc = Scr->MenuGC; 291 } 292 XmbDrawImageString(dpy, mr->w, Scr->MenuFont.font_set, gc, 293 mi->x + Scr->MenuShadowDepth, text_y, 294 mi->item, mi->strlen); 295 if(mi->separated) { 296 FB(Scr->MenuC.shadd, Scr->MenuC.shadc); 297 XDrawLine(dpy, mr->w, Scr->NormalGC, 298 Scr->MenuShadowDepth, 299 y_offset + Scr->EntryHeight - 2, 300 mr->width - Scr->MenuShadowDepth, 301 y_offset + Scr->EntryHeight - 2); 302 FB(Scr->MenuC.shadc, Scr->MenuC.shadd); 303 XDrawLine(dpy, mr->w, Scr->NormalGC, 304 Scr->MenuShadowDepth, 305 y_offset + Scr->EntryHeight - 1, 306 mr->width - Scr->MenuShadowDepth, 307 y_offset + Scr->EntryHeight - 1); 308 } 309 } 310 311 if(mi->func == F_MENU) { 312 /* create the pull right pixmap if needed */ 313 if(Scr->pullPm == None) { 314 Scr->pullPm = Create3DMenuIcon(Scr->EntryHeight - ENTRY_SPACING, &Scr->pullW, 315 &Scr->pullH, Scr->MenuC); 316 } 317 x = mr->width - Scr->pullW - Scr->MenuShadowDepth - 2; 318 y = y_offset + ((Scr->EntryHeight - ENTRY_SPACING - Scr->pullH) / 2) + 2; 319 XCopyArea(dpy, Scr->pullPm, mr->w, gc, 0, 0, Scr->pullW, Scr->pullH, x, y); 320 } 321 } 322 else { 323 Draw3DBorder(mr->w, Scr->MenuShadowDepth, y_offset, 324 mr->width - 2 * Scr->MenuShadowDepth, Scr->EntryHeight, 1, 325 mi->normal, off, true, false); 326 FB(mi->normal.fore, mi->normal.back); 327 XmbDrawImageString(dpy, mr->w, Scr->MenuFont.font_set, Scr->NormalGC, 328 mi->x + 2, text_y, mi->item, mi->strlen); 329 } 330} 331 332 333static void 334PaintNormalEntry(MenuRoot *mr, MenuItem *mi, bool exposure) 335{ 336 int y_offset; 337 int text_y; 338 GC gc; 339 XRectangle ink_rect, logical_rect; 340 XmbTextExtents(Scr->MenuFont.font_set, mi->item, mi->strlen, 341 &ink_rect, &logical_rect); 342 343 y_offset = mi->item_num * Scr->EntryHeight; 344 text_y = y_offset + (Scr->EntryHeight - logical_rect.height) / 2 345 - logical_rect.y; 346 347 if(mi->func != F_TITLE) { 348 int x, y; 349 350 if(mi->state) { 351 XSetForeground(dpy, Scr->NormalGC, mi->highlight.back); 352 353 XFillRectangle(dpy, mr->w, Scr->NormalGC, 0, y_offset, 354 mr->width, Scr->EntryHeight); 355 FB(mi->highlight.fore, mi->highlight.back); 356 XmbDrawString(dpy, mr->w, Scr->MenuFont.font_set, Scr->NormalGC, 357 mi->x, text_y, mi->item, mi->strlen); 358 359 gc = Scr->NormalGC; 360 } 361 else { 362 if(mi->user_colors || !exposure) { 363 XSetForeground(dpy, Scr->NormalGC, mi->normal.back); 364 365 XFillRectangle(dpy, mr->w, Scr->NormalGC, 0, y_offset, 366 mr->width, Scr->EntryHeight); 367 368 FB(mi->normal.fore, mi->normal.back); 369 gc = Scr->NormalGC; 370 } 371 else { 372 gc = Scr->MenuGC; 373 } 374 XmbDrawString(dpy, mr->w, Scr->MenuFont.font_set, gc, mi->x, 375 text_y, mi->item, mi->strlen); 376 if(mi->separated) 377 XDrawLine(dpy, mr->w, gc, 0, y_offset + Scr->EntryHeight - 1, 378 mr->width, y_offset + Scr->EntryHeight - 1); 379 } 380 381 if(mi->func == F_MENU) { 382 /* create the pull right pixmap if needed */ 383 if(Scr->pullPm == None) { 384 Scr->pullPm = CreateMenuIcon(Scr->MenuFont.height, 385 &Scr->pullW, &Scr->pullH); 386 } 387 x = mr->width - Scr->pullW - 5; 388 y = y_offset + ((Scr->MenuFont.height - Scr->pullH) / 2); 389 XCopyPlane(dpy, Scr->pullPm, mr->w, gc, 0, 0, 390 Scr->pullW, Scr->pullH, x, y, 1); 391 } 392 } 393 else { 394 int y; 395 396 XSetForeground(dpy, Scr->NormalGC, mi->normal.back); 397 398 /* fill the rectangle with the title background color */ 399 XFillRectangle(dpy, mr->w, Scr->NormalGC, 0, y_offset, 400 mr->width, Scr->EntryHeight); 401 402 { 403 XSetForeground(dpy, Scr->NormalGC, mi->normal.fore); 404 /* now draw the dividing lines */ 405 if(y_offset) 406 XDrawLine(dpy, mr->w, Scr->NormalGC, 0, y_offset, 407 mr->width, y_offset); 408 y = ((mi->item_num + 1) * Scr->EntryHeight) - 1; 409 XDrawLine(dpy, mr->w, Scr->NormalGC, 0, y, mr->width, y); 410 } 411 412 FB(mi->normal.fore, mi->normal.back); 413 /* finally render the title */ 414 XmbDrawString(dpy, mr->w, Scr->MenuFont.font_set, Scr->NormalGC, mi->x, 415 text_y, mi->item, mi->strlen); 416 } 417} 418 419void PaintMenu(MenuRoot *mr, XEvent *e) 420{ 421 MenuItem *mi; 422 423 if(Scr->use3Dmenus) { 424 Draw3DBorder(mr->w, 0, 0, mr->width, mr->height, 425 Scr->MenuShadowDepth, Scr->MenuC, off, false, false); 426 } 427 for(mi = mr->first; mi != NULL; mi = mi->next) { 428 int y_offset = mi->item_num * Scr->EntryHeight; 429 430 /* be smart about handling the expose, redraw only the entries 431 * that we need to 432 */ 433 if(e->xexpose.y <= (y_offset + Scr->EntryHeight) && 434 (e->xexpose.y + e->xexpose.height) >= y_offset) { 435 PaintEntry(mr, mi, true); 436 } 437 } 438 XSync(dpy, 0); 439} 440 441 442void MakeWorkspacesMenu(void) 443{ 444 static char **actions = NULL; 445 WorkSpace *wlist; 446 char **act; 447 448 if(! Scr->Workspaces) { 449 return; 450 } 451 AddToMenu(Scr->Workspaces, "TWM Workspaces", NULL, NULL, F_TITLE, NULL, 452 NULL); 453 if(! actions) { 454 int count = 0; 455 456 for(wlist = Scr->workSpaceMgr.workSpaceList; wlist != NULL; 457 wlist = wlist->next) { 458 count++; 459 } 460 count++; 461 actions = calloc(count, sizeof(char *)); 462 act = actions; 463 for(wlist = Scr->workSpaceMgr.workSpaceList; wlist != NULL; 464 wlist = wlist->next) { 465 asprintf(act, "WGOTO : %s", wlist->name); 466 act++; 467 } 468 *act = NULL; 469 } 470 act = actions; 471 for(wlist = Scr->workSpaceMgr.workSpaceList; wlist != NULL; 472 wlist = wlist->next) { 473 AddToMenu(Scr->Workspaces, wlist->name, *act, Scr->Windows, F_MENU, NULL, NULL); 474 act++; 475 } 476 Scr->Workspaces->pinned = false; 477 MakeMenu(Scr->Workspaces); 478} 479 480static bool fromMenu; 481bool 482cur_fromMenu(void) 483{ 484 return fromMenu; 485} 486 487void UpdateMenu(void) 488{ 489 MenuItem *mi; 490 int i, x, y, x_root, y_root, entry; 491 bool done; 492 MenuItem *badItem = NULL; 493 494 fromMenu = true; 495 496 while(1) { 497 /* block until there is an event */ 498 if(!menuFromFrameOrWindowOrTitlebar) { 499 XMaskEvent(dpy, 500 ButtonPressMask | ButtonReleaseMask | 501 KeyPressMask | KeyReleaseMask | 502 EnterWindowMask | ExposureMask | 503 VisibilityChangeMask | LeaveWindowMask | 504 ButtonMotionMask, &Event); 505 } 506 if(Event.type == MotionNotify) { 507 /* discard any extra motion events before a release */ 508 while(XCheckMaskEvent(dpy, 509 ButtonMotionMask | ButtonReleaseMask, &Event)) 510 if(Event.type == ButtonRelease) { 511 break; 512 } 513 } 514 515 if(!DispatchEvent()) { 516 continue; 517 } 518 519 if((! ActiveMenu) || Cancel) { 520 menuFromFrameOrWindowOrTitlebar = false; 521 fromMenu = false; 522 return; 523 } 524 525 if(Event.type != MotionNotify) { 526 continue; 527 } 528 529 done = false; 530 XQueryPointer(dpy, ActiveMenu->w, &JunkRoot, &JunkChild, 531 &x_root, &y_root, &x, &y, &JunkMask); 532 533 /* if we haven't received the enter notify yet, wait */ 534 if(!ActiveMenu->entered) { 535 continue; 536 } 537 538 XFindContext(dpy, ActiveMenu->w, ScreenContext, (XPointer *)&Scr); 539 540 if(x < 0 || y < 0 || 541 x >= ActiveMenu->width || y >= ActiveMenu->height) { 542 if(ActiveItem && ActiveItem->func != F_TITLE) { 543 ActiveItem->state = false; 544 PaintEntry(ActiveMenu, ActiveItem, false); 545 } 546 ActiveItem = NULL; 547 continue; 548 } 549 550 /* look for the entry that the mouse is in */ 551 entry = y / Scr->EntryHeight; 552 for(i = 0, mi = ActiveMenu->first; mi != NULL; i++, mi = mi->next) { 553 if(i == entry) { 554 break; 555 } 556 } 557 558 /* if there is an active item, we might have to turn it off */ 559 if(ActiveItem) { 560 /* is the active item the one we are on ? */ 561 if(ActiveItem->item_num == entry && ActiveItem->state) { 562 done = true; 563 } 564 565 /* if we weren't on the active entry, let's turn the old 566 * active one off 567 */ 568 if(!done && ActiveItem->func != F_TITLE) { 569 ActiveItem->state = false; 570 PaintEntry(ActiveMenu, ActiveItem, false); 571 } 572 } 573 574 /* if we weren't on the active item, change the active item and turn 575 * it on 576 */ 577 if(!done) { 578 ActiveItem = mi; 579 if(ActiveItem && ActiveItem->func != F_TITLE && !ActiveItem->state) { 580 ActiveItem->state = true; 581 PaintEntry(ActiveMenu, ActiveItem, false); 582 } 583 } 584 585 /* now check to see if we were over the arrow of a pull right entry */ 586 if(ActiveItem && ActiveItem->func == F_MENU && 587 ((ActiveMenu->width - x) < (ActiveMenu->width / 3))) { 588 MenuRoot *save = ActiveMenu; 589 int savex = MenuOrigins[MenuDepth - 1].x; 590 int savey = MenuOrigins[MenuDepth - 1].y; 591 592 if(MenuDepth < MAXMENUDEPTH) { 593 if(ActiveMenu == Scr->Workspaces) { 594 CurrentSelectedWorkspace = ActiveItem->item; 595 } 596 PopUpMenu(ActiveItem->sub, 597 (savex + (((2 * ActiveMenu->width) / 3) - 1)), 598 (savey + ActiveItem->item_num * Scr->EntryHeight) 599 /*(savey + ActiveItem->item_num * Scr->EntryHeight + 600 (Scr->EntryHeight >> 1))*/, False); 601 CurrentSelectedWorkspace = NULL; 602 } 603 else if(!badItem) { 604 XBell(dpy, 0); 605 badItem = ActiveItem; 606 } 607 608 /* if the menu did get popped up, unhighlight the active item */ 609 if(save != ActiveMenu && ActiveItem->state) { 610 ActiveItem->state = false; 611 PaintEntry(save, ActiveItem, false); 612 ActiveItem = NULL; 613 } 614 } 615 if(badItem != ActiveItem) { 616 badItem = NULL; 617 } 618 XFlush(dpy); 619 } 620} 621 622 623/*********************************************************************** 624 * 625 * Procedure: 626 * NewMenuRoot - create a new menu root 627 * 628 * Returned Value: 629 * (MenuRoot *) 630 * 631 * Inputs: 632 * name - the name of the menu root 633 * 634 *********************************************************************** 635 */ 636 637MenuRoot *NewMenuRoot(char *name) 638{ 639 MenuRoot *tmp; 640 641#define UNUSED_PIXEL ((unsigned long) (~0)) /* more than 24 bits */ 642 643 tmp = malloc(sizeof(MenuRoot)); 644 tmp->highlight.fore = UNUSED_PIXEL; 645 tmp->highlight.back = UNUSED_PIXEL; 646 tmp->name = name; 647 tmp->prev = NULL; 648 tmp->first = NULL; 649 tmp->last = NULL; 650 tmp->defaultitem = NULL; 651 tmp->items = 0; 652 tmp->width = 0; 653 tmp->mapped = MRM_NEVER; 654 tmp->pull = false; 655 tmp->w = None; 656 tmp->shadow = None; 657 tmp->real_menu = false; 658 659 if(Scr->MenuList == NULL) { 660 Scr->MenuList = tmp; 661 Scr->MenuList->next = NULL; 662 } 663 664 if(Scr->LastMenu == NULL) { 665 Scr->LastMenu = tmp; 666 Scr->LastMenu->next = NULL; 667 } 668 else { 669 Scr->LastMenu->next = tmp; 670 Scr->LastMenu = tmp; 671 Scr->LastMenu->next = NULL; 672 } 673 674 if(strcmp(name, TWM_WINDOWS) == 0) { 675 Scr->Windows = tmp; 676 } 677 678 if(strcmp(name, TWM_ICONS) == 0) { 679 Scr->Icons = tmp; 680 } 681 682 if(strcmp(name, TWM_WORKSPACES) == 0) { 683 Scr->Workspaces = tmp; 684 if(!Scr->Windows) { 685 NewMenuRoot(TWM_WINDOWS); 686 } 687 } 688 if(strcmp(name, TWM_ALLWINDOWS) == 0) { 689 Scr->AllWindows = tmp; 690 } 691 692 /* Added by dl 2004 */ 693 if(strcmp(name, TWM_ALLICONS) == 0) { 694 Scr->AllIcons = tmp; 695 } 696 697 /* Added by Dan Lilliehorn (dl@dl.nu) 2000-02-29 */ 698 if(strcmp(name, TWM_KEYS) == 0) { 699 Scr->Keys = tmp; 700 } 701 702 if(strcmp(name, TWM_VISIBLE) == 0) { 703 Scr->Visible = tmp; 704 } 705 706 /* End addition */ 707 708 return (tmp); 709} 710 711 712/*********************************************************************** 713 * 714 * Procedure: 715 * AddToMenu - add an item to a root menu 716 * 717 * Returned Value: 718 * (MenuItem *) 719 * 720 * Inputs: 721 * menu - pointer to the root menu to add the item 722 * item - the text to appear in the menu 723 * action - the string to possibly execute 724 * sub - the menu root if it is a pull-right entry 725 * func - the numeric function 726 * fore - foreground color string 727 * back - background color string 728 * 729 *********************************************************************** 730 */ 731 732MenuItem *AddToMenu(MenuRoot *menu, char *item, char *action, 733 MenuRoot *sub, int func, char *fore, char *back) 734{ 735 MenuItem *tmp; 736 int width; 737 char *itemname; 738 XRectangle ink_rect; 739 XRectangle logical_rect; 740 741#ifdef DEBUG_MENUS 742 fprintf(stderr, "adding menu item=\"%s\", action=%s, sub=%d, f=%d\n", 743 item, action, sub, func); 744#endif 745 746 tmp = malloc(sizeof(MenuItem)); 747 tmp->root = menu; 748 749 if(menu->first == NULL) { 750 menu->first = tmp; 751 tmp->prev = NULL; 752 } 753 else { 754 menu->last->next = tmp; 755 tmp->prev = menu->last; 756 } 757 menu->last = tmp; 758 759 if((menu == Scr->Workspaces) || 760 (menu == Scr->Windows) || 761 (menu == Scr->Icons) || 762 (menu == Scr->AllWindows) || 763 764 /* Added by dl 2004 */ 765 (menu == Scr->AllIcons) || 766 767 /* Added by Dan Lillehorn (dl@dl.nu) 2000-02-29 */ 768 (menu == Scr->Keys) || 769 (menu == Scr->Visible)) { 770 771 itemname = item; 772 } 773 else if(*item == '*') { 774 itemname = item + 1; 775 menu->defaultitem = tmp; 776 } 777 else { 778 itemname = item; 779 } 780 781 tmp->item = itemname; 782 tmp->strlen = strlen(itemname); 783 tmp->action = action; 784 tmp->next = NULL; 785 tmp->sub = NULL; 786 tmp->state = false; 787 tmp->func = func; 788 tmp->separated = false; 789 790 if(!Scr->HaveFonts) { 791 CreateFonts(Scr); 792 } 793 794 if(dpy) { 795 XmbTextExtents(Scr->MenuFont.font_set, 796 itemname, tmp->strlen, 797 &ink_rect, &logical_rect); 798 width = logical_rect.width; 799 } 800 else { 801 // Fake for non-dpy cases 802 width = 25; 803 } 804 805 if(width <= 0) { 806 width = 1; 807 } 808 if(width > menu->width) { 809 menu->width = width; 810 } 811 812 tmp->user_colors = false; 813 if(Scr->Monochrome == COLOR && fore != NULL) { 814 bool save; 815 816 save = Scr->FirstTime; 817 Scr->FirstTime = true; 818 GetColor(COLOR, &tmp->normal.fore, fore); 819 GetColor(COLOR, &tmp->normal.back, back); 820 if(Scr->use3Dmenus && !Scr->BeNiceToColormap) { 821 GetShadeColors(&tmp->normal); 822 } 823 Scr->FirstTime = save; 824 tmp->user_colors = true; 825 } 826 if(sub != NULL) { 827 tmp->sub = sub; 828 menu->pull = true; 829 } 830 tmp->item_num = menu->items++; 831 832 return (tmp); 833} 834 835 836void MakeMenus(void) 837{ 838 MenuRoot *mr; 839 840 for(mr = Scr->MenuList; mr != NULL; mr = mr->next) { 841 if(mr->real_menu == false) { 842 continue; 843 } 844 845 mr->pinned = false; 846 MakeMenu(mr); 847 } 848} 849 850 851void MakeMenu(MenuRoot *mr) 852{ 853 MenuItem *start, *tmp; 854 XColor f1, f2, f3; 855 XColor b1, b2, b3; 856 XColor save_fore, save_back; 857 int fred, fgreen, fblue; 858 int bred, bgreen, bblue; 859 int width, borderwidth; 860 unsigned long valuemask; 861 XSetWindowAttributes attributes; 862 Colormap cmap = Scr->RootColormaps.cwins[0]->colormap->c; 863 XRectangle ink_rect; 864 XRectangle logical_rect; 865 866 Scr->EntryHeight = Scr->MenuFont.height + 4; 867 868 /* lets first size the window accordingly */ 869 if(mr->mapped == MRM_NEVER) { 870 int max_entry_height = 0; 871 872 if(mr->pull == true) { 873 mr->width += 16 + 10; 874 } 875 width = mr->width + 10; 876 for(MenuItem *cur = mr->first; cur != NULL; cur = cur->next) { 877 XmbTextExtents(Scr->MenuFont.font_set, cur->item, cur->strlen, 878 &ink_rect, &logical_rect); 879 max_entry_height = MAX(max_entry_height, logical_rect.height); 880 881 if(cur->func != F_TITLE) { 882 cur->x = 5; 883 } 884 else { 885 cur->x = width - logical_rect.width; 886 cur->x /= 2; 887 } 888 } 889 Scr->EntryHeight = max_entry_height + ENTRY_SPACING; 890 mr->height = mr->items * Scr->EntryHeight; 891 mr->width += 10; 892 if(Scr->use3Dmenus) { 893 mr->width += 2 * Scr->MenuShadowDepth; 894 mr->height += 2 * Scr->MenuShadowDepth; 895 } 896 if(Scr->Shadow && ! mr->pinned) { 897 /* 898 * Make sure that you don't draw into the shadow window or else 899 * the background bits there will get saved 900 */ 901 valuemask = (CWBackPixel | CWBorderPixel); 902 attributes.background_pixel = Scr->MenuShadowColor; 903 attributes.border_pixel = Scr->MenuShadowColor; 904 if(Scr->SaveUnder) { 905 valuemask |= CWSaveUnder; 906 attributes.save_under = True; 907 } 908 mr->shadow = XCreateWindow(dpy, Scr->Root, 0, 0, 909 mr->width, 910 mr->height, 911 0, 912 CopyFromParent, 913 CopyFromParent, 914 CopyFromParent, 915 valuemask, &attributes); 916 } 917 918 valuemask = (CWBackPixel | CWBorderPixel | CWEventMask); 919 attributes.background_pixel = Scr->MenuC.back; 920 attributes.border_pixel = Scr->MenuC.fore; 921 if(mr->pinned) { 922 attributes.event_mask = (ExposureMask | EnterWindowMask 923 | LeaveWindowMask | ButtonPressMask 924 | ButtonReleaseMask | PointerMotionMask 925 | ButtonMotionMask 926 ); 927 attributes.cursor = Scr->MenuCursor; 928 valuemask |= CWCursor; 929 } 930 else { 931 attributes.event_mask = (ExposureMask | EnterWindowMask); 932 } 933 934 if(Scr->SaveUnder && ! mr->pinned) { 935 valuemask |= CWSaveUnder; 936 attributes.save_under = True; 937 } 938 if(Scr->BackingStore) { 939 valuemask |= CWBackingStore; 940 attributes.backing_store = Always; 941 } 942 borderwidth = Scr->use3Dmenus ? 0 : 1; 943 mr->w = XCreateWindow(dpy, Scr->Root, 0, 0, mr->width, 944 mr->height, borderwidth, 945 CopyFromParent, CopyFromParent, 946 CopyFromParent, 947 valuemask, &attributes); 948 949 950 XSaveContext(dpy, mr->w, MenuContext, (XPointer)mr); 951 XSaveContext(dpy, mr->w, ScreenContext, (XPointer)Scr); 952 953 mr->mapped = MRM_UNMAPPED; 954 } 955 956 if(Scr->use3Dmenus && (Scr->Monochrome == COLOR) 957 && (mr->highlight.back == UNUSED_PIXEL)) { 958 XColor xcol; 959 char colname [32]; 960 bool save; 961 962 xcol.pixel = Scr->MenuC.back; 963 XQueryColor(dpy, cmap, &xcol); 964 sprintf(colname, "#%04x%04x%04x", 965 5 * ((int)xcol.red / 6), 966 5 * ((int)xcol.green / 6), 967 5 * ((int)xcol.blue / 6)); 968 save = Scr->FirstTime; 969 Scr->FirstTime = true; 970 GetColor(Scr->Monochrome, &mr->highlight.back, colname); 971 Scr->FirstTime = save; 972 } 973 974 if(Scr->use3Dmenus && (Scr->Monochrome == COLOR) 975 && (mr->highlight.fore == UNUSED_PIXEL)) { 976 XColor xcol; 977 char colname [32]; 978 bool save; 979 980 xcol.pixel = Scr->MenuC.fore; 981 XQueryColor(dpy, cmap, &xcol); 982 sprintf(colname, "#%04x%04x%04x", 983 5 * ((int)xcol.red / 6), 984 5 * ((int)xcol.green / 6), 985 5 * ((int)xcol.blue / 6)); 986 save = Scr->FirstTime; 987 Scr->FirstTime = true; 988 GetColor(Scr->Monochrome, &mr->highlight.fore, colname); 989 Scr->FirstTime = save; 990 } 991 if(Scr->use3Dmenus && !Scr->BeNiceToColormap) { 992 GetShadeColors(&mr->highlight); 993 } 994 995 /* get the default colors into the menus */ 996 for(tmp = mr->first; tmp != NULL; tmp = tmp->next) { 997 if(!tmp->user_colors) { 998 if(tmp->func != F_TITLE) { 999 tmp->normal.fore = Scr->MenuC.fore; 1000 tmp->normal.back = Scr->MenuC.back; 1001 } 1002 else { 1003 tmp->normal.fore = Scr->MenuTitleC.fore; 1004 tmp->normal.back = Scr->MenuTitleC.back; 1005 } 1006 } 1007 1008 if(mr->highlight.fore != UNUSED_PIXEL) { 1009 tmp->highlight.fore = mr->highlight.fore; 1010 tmp->highlight.back = mr->highlight.back; 1011 } 1012 else { 1013 tmp->highlight.fore = tmp->normal.back; 1014 tmp->highlight.back = tmp->normal.fore; 1015 } 1016 if(Scr->use3Dmenus && !Scr->BeNiceToColormap) { 1017 if(tmp->func != F_TITLE) { 1018 GetShadeColors(&tmp->highlight); 1019 } 1020 else { 1021 GetShadeColors(&tmp->normal); 1022 } 1023 } 1024 } 1025 mr->pmenu = NULL; 1026 1027 if(Scr->Monochrome == MONOCHROME || !Scr->InterpolateMenuColors) { 1028 return; 1029 } 1030 1031 // Do InterpolateMenuColors magic 1032 start = mr->first; 1033 while(1) { 1034 for(; start != NULL; start = start->next) { 1035 if(start->user_colors) { 1036 break; 1037 } 1038 } 1039 if(start == NULL) { 1040 break; 1041 } 1042 1043 MenuItem *end; 1044 for(end = start->next; end != NULL; end = end->next) { 1045 if(end->user_colors) { 1046 break; 1047 } 1048 } 1049 if(end == NULL) { 1050 break; 1051 } 1052 1053 /* we have a start and end to interpolate between */ 1054 int num = end->item_num - start->item_num; 1055 1056 f1.pixel = start->normal.fore; 1057 XQueryColor(dpy, cmap, &f1); 1058 f2.pixel = end->normal.fore; 1059 XQueryColor(dpy, cmap, &f2); 1060 1061 b1.pixel = start->normal.back; 1062 XQueryColor(dpy, cmap, &b1); 1063 b2.pixel = end->normal.back; 1064 XQueryColor(dpy, cmap, &b2); 1065 1066 fred = ((int)f2.red - (int)f1.red) / num; 1067 fgreen = ((int)f2.green - (int)f1.green) / num; 1068 fblue = ((int)f2.blue - (int)f1.blue) / num; 1069 1070 bred = ((int)b2.red - (int)b1.red) / num; 1071 bgreen = ((int)b2.green - (int)b1.green) / num; 1072 bblue = ((int)b2.blue - (int)b1.blue) / num; 1073 1074 f3 = f1; 1075 f3.flags = DoRed | DoGreen | DoBlue; 1076 1077 b3 = b1; 1078 b3.flags = DoRed | DoGreen | DoBlue; 1079 1080 start->highlight.back = start->normal.fore; 1081 start->highlight.fore = start->normal.back; 1082 num -= 1; 1083 int i = 0; 1084 MenuItem *cur = start->next; 1085 // XXX Should be impossible to run out of cur's before num's, 1086 // unless the item_num's are wrong (which would break other 1087 // stuff), but add condition to quiet static analysis. 1088 for(; cur != NULL && i < num ; i++, cur = cur->next) { 1089 f3.red += fred; 1090 f3.green += fgreen; 1091 f3.blue += fblue; 1092 save_fore = f3; 1093 1094 b3.red += bred; 1095 b3.green += bgreen; 1096 b3.blue += bblue; 1097 save_back = b3; 1098 1099 XAllocColor(dpy, cmap, &f3); 1100 XAllocColor(dpy, cmap, &b3); 1101 cur->highlight.back = cur->normal.fore = f3.pixel; 1102 cur->highlight.fore = cur->normal.back = b3.pixel; 1103 cur->user_colors = true; 1104 1105 f3 = save_fore; 1106 b3 = save_back; 1107 } 1108 start = end; 1109 start->highlight.back = start->normal.fore; 1110 start->highlight.fore = start->normal.back; 1111 } 1112 return; 1113} 1114 1115 1116/*********************************************************************** 1117 * 1118 * Procedure: 1119 * PopUpMenu - pop up a pull down menu 1120 * 1121 * Inputs: 1122 * menu - the root pointer of the menu to pop up 1123 * x, y - location of upper left of menu 1124 * center - whether or not to center horizontally over position 1125 * 1126 *********************************************************************** 1127 */ 1128 1129bool 1130PopUpMenu(MenuRoot *menu, int x, int y, bool center) 1131{ 1132 int WindowNameCount; 1133 TwmWindow **WindowNames; 1134 TwmWindow *tmp_win2, *tmp_win3; 1135 int i; 1136 bool clipped; 1137 if(!menu) { 1138 return false; 1139 } 1140 1141 InstallRootColormap(); 1142 1143 if((menu == Scr->Windows) || 1144 (menu == Scr->Icons) || 1145 (menu == Scr->AllWindows) || 1146 /* Added by Dan 'dl' Lilliehorn 040607 */ 1147 (menu == Scr->AllIcons) || 1148 /* Added by Dan Lilliehorn (dl@dl.nu) 2000-02-29 */ 1149 (menu == Scr->Visible)) { 1150 TwmWindow *tmp_win; 1151 WorkSpace *ws; 1152 bool all, icons, visible_, allicons; /* visible, allicons: 1153 Added by dl */ 1154 int func; 1155 1156 /* this is the twm windows menu, let's go ahead and build it */ 1157 1158 all = (menu == Scr->AllWindows); 1159 icons = (menu == Scr->Icons); 1160 visible_ = (menu == Scr->Visible); /* Added by dl */ 1161 allicons = (menu == Scr->AllIcons); 1162 DestroyMenu(menu); 1163 1164 menu->first = NULL; 1165 menu->last = NULL; 1166 menu->items = 0; 1167 menu->width = 0; 1168 menu->mapped = MRM_NEVER; 1169 menu->highlight.fore = UNUSED_PIXEL; 1170 menu->highlight.back = UNUSED_PIXEL; 1171 if(menu == Scr->Windows) { 1172 AddToMenu(menu, "TWM Windows", NULL, NULL, F_TITLE, NULL, NULL); 1173 } 1174 else if(menu == Scr->Icons) { 1175 AddToMenu(menu, "TWM Icons", NULL, NULL, F_TITLE, NULL, NULL); 1176 } 1177 else if(menu == Scr->Visible) { /* Added by dl 2000 */ 1178 AddToMenu(menu, "TWM Visible", NULL, NULL, F_TITLE, NULL, NULL); 1179 } 1180 else if(menu == Scr->AllIcons) { /* Added by dl 2004 */ 1181 AddToMenu(menu, "TWM All Icons", NULL, NULL, F_TITLE, NULL, NULL); 1182 } 1183 else { 1184 AddToMenu(menu, "TWM All Windows", NULL, NULL, F_TITLE, NULL, NULL); 1185 } 1186 1187 ws = NULL; 1188 1189 if(!(all || allicons) 1190 && CurrentSelectedWorkspace && Scr->workSpaceManagerActive) { 1191 for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) { 1192 if(strcmp(ws->name, CurrentSelectedWorkspace) == 0) { 1193 break; 1194 } 1195 } 1196 } 1197 if(!Scr->currentvs) { 1198 return false; 1199 } 1200 if(!ws) { 1201 ws = Scr->currentvs->wsw->currentwspc; 1202 } 1203 1204 for(tmp_win = Scr->FirstWindow, WindowNameCount = 0; 1205 tmp_win != NULL; 1206 tmp_win = tmp_win->next) { 1207 if(tmp_win == Scr->workSpaceMgr.occupyWindow->twm_win) { 1208 continue; 1209 } 1210 if(Scr->ShortAllWindowsMenus && (tmp_win->iswspmgr || tmp_win->isiconmgr)) { 1211 continue; 1212 } 1213 1214 if(!(all || allicons) && !OCCUPY(tmp_win, ws)) { 1215 continue; 1216 } 1217 if(allicons && !tmp_win->isicon) { 1218 continue; 1219 } 1220 if(icons && !tmp_win->isicon) { 1221 continue; 1222 } 1223 if(visible_ && tmp_win->isicon) { 1224 continue; /* added by dl */ 1225 } 1226 WindowNameCount++; 1227 } 1228 1229 // Hack: always pretend there's at least one window, even if 1230 // there are none; that lets us skip special cases for empty 1231 // lists... 1232 if(WindowNameCount == 0) { 1233 WindowNameCount = 1; 1234 } 1235 WindowNames = calloc(WindowNameCount, sizeof(TwmWindow *)); 1236 1237 WindowNameCount = 0; 1238 for(tmp_win = Scr->FirstWindow; 1239 tmp_win != NULL; 1240 tmp_win = tmp_win->next) { 1241 if(LookInList(Scr->IconMenuDontShow, tmp_win->name, &tmp_win->class)) { 1242 continue; 1243 } 1244 1245 if(tmp_win == Scr->workSpaceMgr.occupyWindow->twm_win) { 1246 continue; 1247 } 1248 if(Scr->ShortAllWindowsMenus && 1249 tmp_win == Scr->currentvs->wsw->twm_win) { 1250 continue; 1251 } 1252 if(Scr->ShortAllWindowsMenus && tmp_win->isiconmgr) { 1253 continue; 1254 } 1255 1256 if(!(all || allicons) && ! OCCUPY(tmp_win, ws)) { 1257 continue; 1258 } 1259 if(allicons && !tmp_win->isicon) { 1260 continue; 1261 } 1262 if(icons && !tmp_win->isicon) { 1263 continue; 1264 } 1265 if(visible_ && tmp_win->isicon) { 1266 continue; /* added by dl */ 1267 } 1268 tmp_win2 = tmp_win; 1269 1270 for(i = 0; i < WindowNameCount; i++) { 1271 int compresult; 1272 char *tmpname1, *tmpname2; 1273 tmpname1 = tmp_win2->name; 1274 tmpname2 = WindowNames[i]->name; 1275 if(Scr->CaseSensitive) { 1276 compresult = strcmp(tmpname1, tmpname2); 1277 } 1278 else { 1279 compresult = strcasecmp(tmpname1, tmpname2); 1280 } 1281 if(compresult < 0) { 1282 tmp_win3 = tmp_win2; 1283 tmp_win2 = WindowNames[i]; 1284 WindowNames[i] = tmp_win3; 1285 } 1286 } 1287 WindowNames[WindowNameCount] = tmp_win2; 1288 WindowNameCount++; 1289 } 1290 func = (all || allicons || CurrentSelectedWorkspace) ? F_WINWARP : 1291 F_POPUP; 1292 for(i = 0; i < WindowNameCount; i++) { 1293 char *tmpname; 1294 tmpname = WindowNames[i]->name; 1295 AddToMenu(menu, tmpname, (char *)WindowNames[i], 1296 NULL, func, NULL, NULL); 1297 } 1298 free(WindowNames); 1299 1300 menu->pinned = false; 1301 MakeMenu(menu); 1302 } 1303 1304 /* Keys added by dl */ 1305 1306 if(menu == Scr->Keys) { 1307 char *oldact = 0; 1308 int oldmod = 0; 1309 1310 DestroyMenu(menu); 1311 1312 menu->first = NULL; 1313 menu->last = NULL; 1314 menu->items = 0; 1315 menu->width = 0; 1316 menu->mapped = MRM_NEVER; 1317 menu->highlight.fore = UNUSED_PIXEL; 1318 menu->highlight.back = UNUSED_PIXEL; 1319 1320 AddToMenu(menu, "Twm Keys", NULL, NULL, F_TITLE, NULL, NULL); 1321 1322 for(const FuncKey *tmpKey = Scr->FuncKeyRoot.next; tmpKey != NULL; 1323 tmpKey = tmpKey->next) { 1324 char *tmpStr; 1325 1326 if(tmpKey->func != F_EXEC) { 1327 continue; 1328 } 1329 if((tmpKey->action == oldact) && (tmpKey->mods == oldmod)) { 1330 continue; 1331 } 1332 1333 tmpStr = mk_twmkeys_entry(tmpKey); 1334 if(tmpStr == NULL) { 1335 tmpStr = strdup("(error)"); 1336 } 1337 1338 AddToMenu(menu, tmpStr, tmpKey->action, NULL, tmpKey->func, NULL, NULL); 1339 oldact = tmpKey->action; 1340 oldmod = tmpKey->mods; 1341 } 1342 menu->pinned = false; 1343 MakeMenu(menu); 1344 } 1345 if(menu->w == None || menu->items == 0) { 1346 return false; 1347 } 1348 1349 /* Prevent recursively bringing up menus. */ 1350 if((!menu->pinned) && (menu->mapped == MRM_MAPPED)) { 1351 return false; 1352 } 1353 1354 /* 1355 * Dynamically set the parent; this allows pull-ups to also be main 1356 * menus, or to be brought up from more than one place. 1357 */ 1358 menu->prev = ActiveMenu; 1359 1360 if(menu->pinned) { 1361 ActiveMenu = menu; 1362 menu->mapped = MRM_MAPPED; 1363 menu->entered = true; 1364 MenuOrigins [MenuDepth].x = menu->x; 1365 MenuOrigins [MenuDepth].y = menu->y; 1366 MenuDepth++; 1367 1368 XRaiseWindow(dpy, menu->w); 1369 return true; 1370 } 1371 1372 XGrabPointer(dpy, Scr->Root, True, 1373 ButtonPressMask | ButtonReleaseMask | PointerMotionMask | 1374 ButtonMotionMask | PointerMotionHintMask, 1375 GrabModeAsync, GrabModeAsync, 1376 Scr->Root, 1377 Scr->MenuCursor, CurrentTime); 1378 1379 XGrabKeyboard(dpy, Scr->Root, True, GrabModeAsync, GrabModeAsync, CurrentTime); 1380 1381 ActiveMenu = menu; 1382 menu->mapped = MRM_MAPPED; 1383 menu->entered = false; 1384 1385 if(center) { 1386 x -= (menu->width / 2); 1387 y -= (Scr->EntryHeight / 2); /* sticky menus would be nice here */ 1388 } 1389 1390 /* 1391 * clip to screen 1392 */ 1393 clipped = ConstrainByLayout(Scr->Layout, -1, &x, menu->width, &y, menu->height); 1394 MenuOrigins[MenuDepth].x = x; 1395 MenuOrigins[MenuDepth].y = y; 1396 MenuDepth++; 1397 1398 1399 /* 1400 * Position and display the menu, and its shadow if it has one. We 1401 * start by positioning and raising (above everything else on screen) 1402 * the shadow. Then position the menu itself, raise it up above 1403 * that, and map it. Then map the shadow; doing that after raising 1404 * and mapping the menu avoids spending time drawing the bulk of the 1405 * window which the menu covers up anyway. 1406 */ 1407 if(Scr->Shadow) { 1408 XMoveWindow(dpy, menu->shadow, x + SHADOWWIDTH, y + SHADOWWIDTH); 1409 XRaiseWindow(dpy, menu->shadow); 1410 } 1411 1412 XMoveWindow(dpy, menu->w, x, y); 1413 XMapRaised(dpy, menu->w); 1414 1415 if(Scr->Shadow) { 1416 XMapWindow(dpy, menu->shadow); 1417 } 1418 1419 /* Move mouse pointer if we're supposed to */ 1420 if(!Scr->NoWarpToMenuTitle && clipped && center) { 1421 const int xl = x + (menu->width / 2); 1422 const int yt = y + (Scr->EntryHeight / 2); 1423 XWarpPointer(dpy, Scr->Root, Scr->Root, x, y, 1424 menu->width, menu->height, xl, yt); 1425 } 1426 1427 1428 XSync(dpy, 0); 1429 return true; 1430} 1431 1432 1433/*********************************************************************** 1434 * 1435 * Procedure: 1436 * PopDownMenu - unhighlight the current menu selection and 1437 * take down the menus 1438 * 1439 *********************************************************************** 1440 */ 1441 1442void PopDownMenu(void) 1443{ 1444 MenuRoot *tmp; 1445 1446 if(ActiveMenu == NULL) { 1447 return; 1448 } 1449 1450 if(ActiveItem) { 1451 ActiveItem->state = false; 1452 PaintEntry(ActiveMenu, ActiveItem, false); 1453 } 1454 1455 for(tmp = ActiveMenu; tmp != NULL; tmp = tmp->prev) { 1456 if(! tmp->pinned) { 1457 HideMenu(tmp); 1458 } 1459 UninstallRootColormap(); 1460 } 1461 1462 XFlush(dpy); 1463 ActiveMenu = NULL; 1464 ActiveItem = NULL; 1465 MenuDepth = 0; 1466 XUngrabKeyboard(dpy, CurrentTime); 1467 if(Context == C_WINDOW || Context == C_FRAME || Context == C_TITLE 1468 || Context == C_ICON) { 1469 menuFromFrameOrWindowOrTitlebar = true; 1470 } 1471 1472 return; 1473} 1474 1475 1476void HideMenu(MenuRoot *menu) 1477{ 1478 if(!menu) { 1479 return; 1480 } 1481 1482 if(Scr->Shadow) { 1483 XUnmapWindow(dpy, menu->shadow); 1484 } 1485 XUnmapWindow(dpy, menu->w); 1486 menu->mapped = MRM_UNMAPPED; 1487} 1488 1489/*********************************************************************** 1490 * 1491 * Procedure: 1492 * FindMenuRoot - look for a menu root 1493 * 1494 * Returned Value: 1495 * (MenuRoot *) - a pointer to the menu root structure 1496 * 1497 * Inputs: 1498 * name - the name of the menu root 1499 * 1500 *********************************************************************** 1501 */ 1502 1503MenuRoot *FindMenuRoot(char *name) 1504{ 1505 MenuRoot *tmp; 1506 1507 for(tmp = Scr->MenuList; tmp != NULL; tmp = tmp->next) { 1508 if(strcmp(name, tmp->name) == 0) { 1509 return (tmp); 1510 } 1511 } 1512 return NULL; 1513} 1514 1515 1516 1517static void DestroyMenu(MenuRoot *menu) 1518{ 1519 MenuItem *item; 1520 1521 if(menu->w) { 1522 XDeleteContext(dpy, menu->w, MenuContext); 1523 XDeleteContext(dpy, menu->w, ScreenContext); 1524 if(Scr->Shadow) { 1525 XDestroyWindow(dpy, menu->shadow); 1526 } 1527 XDestroyWindow(dpy, menu->w); 1528 } 1529 1530 for(item = menu->first; item;) { 1531 MenuItem *tmp = item; 1532 item = item->next; 1533 free(tmp); 1534 } 1535} 1536 1537 1538void MoveMenu(XEvent *eventp) 1539{ 1540 int XW, YW, newX, newY; 1541 bool cont; 1542 bool newev; 1543 unsigned long event_mask; 1544 XEvent ev; 1545 1546 if(! ActiveMenu) { 1547 return; 1548 } 1549 if(! ActiveMenu->pinned) { 1550 return; 1551 } 1552 1553 XW = eventp->xbutton.x_root - ActiveMenu->x; 1554 YW = eventp->xbutton.y_root - ActiveMenu->y; 1555 XGrabPointer(dpy, ActiveMenu->w, True, 1556 ButtonPressMask | ButtonReleaseMask | ButtonMotionMask, 1557 GrabModeAsync, GrabModeAsync, 1558 None, Scr->MoveCursor, CurrentTime); 1559 1560 newX = ActiveMenu->x; 1561 newY = ActiveMenu->y; 1562 cont = true; 1563 event_mask = ButtonPressMask | ButtonMotionMask | ButtonReleaseMask | 1564 ExposureMask; 1565 XMaskEvent(dpy, event_mask, &ev); 1566 while(cont) { 1567 ev.xbutton.x_root -= Scr->rootx; 1568 ev.xbutton.y_root -= Scr->rooty; 1569 switch(ev.xany.type) { 1570 case ButtonRelease : 1571 cont = false; 1572 case MotionNotify : 1573 if(!cont) { 1574 newev = false; 1575 while(XCheckMaskEvent(dpy, ButtonMotionMask | ButtonReleaseMask, &ev)) { 1576 newev = true; 1577 if(ev.type == ButtonRelease) { 1578 break; 1579 } 1580 } 1581 if(ev.type == ButtonRelease) { 1582 continue; 1583 } 1584 if(newev) { 1585 ev.xbutton.x_root -= Scr->rootx; 1586 ev.xbutton.y_root -= Scr->rooty; 1587 } 1588 } 1589 newX = ev.xbutton.x_root - XW; 1590 newY = ev.xbutton.y_root - YW; 1591 if(Scr->DontMoveOff) { 1592 ConstrainByBorders1(&newX, ActiveMenu->width, 1593 &newY, ActiveMenu->height); 1594 } 1595 XMoveWindow(dpy, ActiveMenu->w, newX, newY); 1596 XMaskEvent(dpy, event_mask, &ev); 1597 break; 1598 case ButtonPress : 1599 cont = false; 1600 newX = ActiveMenu->x; 1601 newY = ActiveMenu->y; 1602 break; 1603 case Expose: 1604 case NoExpose: 1605 Event = ev; 1606 DispatchEvent(); 1607 XMaskEvent(dpy, event_mask, &ev); 1608 break; 1609 } 1610 } 1611 XUngrabPointer(dpy, CurrentTime); 1612 if(ev.xany.type == ButtonRelease) { 1613 ButtonPressed = -1; 1614 } 1615 /*XPutBackEvent (dpy, &ev);*/ 1616 XMoveWindow(dpy, ActiveMenu->w, newX, newY); 1617 ActiveMenu->x = newX; 1618 ActiveMenu->y = newY; 1619 MenuOrigins [MenuDepth - 1].x = newX; 1620 MenuOrigins [MenuDepth - 1].y = newY; 1621 1622 return; 1623} 1624 1625 1626void WarpCursorToDefaultEntry(MenuRoot *menu) 1627{ 1628 MenuItem *item; 1629 Window root; 1630 int i, x, y, xl, yt; 1631 unsigned int w, h, bw, d; 1632 1633 for(i = 0, item = menu->first; item != menu->last; item = item->next) { 1634 if(item == menu->defaultitem) { 1635 break; 1636 } 1637 i++; 1638 } 1639 if(!XGetGeometry(dpy, menu->w, &root, &x, &y, &w, &h, &bw, &d)) { 1640 return; 1641 } 1642 xl = x + (menu->width / 2); 1643 yt = y + (i + 0.5) * Scr->EntryHeight; 1644 1645 XWarpPointer(dpy, Scr->Root, Scr->Root, 1646 Event.xbutton.x_root, Event.xbutton.y_root, 1647 menu->width, menu->height, xl, yt); 1648} 1649 1650 1651 1652/** 1653 * Generate up a string representation of a keybinding->action. 1654 * Internally used in generating TwmKeys menu. 1655 */ 1656char * 1657mk_twmkeys_entry(const FuncKey *key) 1658{ 1659 char *ret; 1660 // S+ C+ 5(Mx+) 5(Ax+) 1661#define MSLEN (2 + 2 + 2 + 5 * 3 + 5 * 3 + 1 + 26) 1662 char modStr[MSLEN + 1]; 1663 char *modStrCur = modStr; 1664 1665 // Init 1666 *modStrCur = '\0'; 1667 1668 // Check and add prefixes for each modifier 1669#define DO(mask, str) do { \ 1670 if(key->mods & mask##Mask) { \ 1671 const int tslen = sizeof(str) - 1; \ 1672 if((modStrCur - modStr + tslen) >= MSLEN) { \ 1673 fprintf(stderr, "BUG: No space to add '%s' " \ 1674 "in %s()\n", str, __func__); \ 1675 return NULL; \ 1676 } \ 1677 strcpy(modStrCur, str); \ 1678 modStrCur += tslen; \ 1679 } \ 1680 } while(0) 1681 1682 // Mod1 is Meta (== Alt), so is special and comes first, apart and 1683 // differing from the other more generic ModX's. 1684 DO(Mod1, "M+"); 1685 1686 // Shift/Ctrl are normal common bits. 1687 DO(Shift, "S+"); 1688 DO(Control, "C+"); 1689 1690 // Other Mod's and Alt's are weirder, but possible. 1691 DO(Mod2, "M2+"); 1692 DO(Mod3, "M3+"); 1693 DO(Mod4, "M4+"); 1694 DO(Mod5, "M5+"); 1695 1696 DO(Alt1, "A1+"); 1697 DO(Alt2, "A2+"); 1698 DO(Alt3, "A3+"); 1699 DO(Alt4, "A4+"); 1700 DO(Alt5, "A5+"); 1701 1702 // Overflows for test. Watch out for colliding with X or our *Mask 1703 // defs. 1704 // +1 when combined with above, should be enough 1705#define Over1Mask (1<<30) 1706 DO(Over1, "a"); 1707 // Way too big no matter what 1708#define OverAllMask (1<<31) 1709 DO(OverAll, "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"); 1710 1711#undef OverAllMask 1712#undef Over1Mask 1713 1714#undef DO 1715 1716 asprintf(&ret, "[%s%s] %s", modStr, key->name, key->action); 1717 return ret; 1718} 1719