1 /* vi:set ts=8 sts=4 sw=4: 2 * 3 * VIM - Vi IMproved by Bram Moolenaar 4 * GUI/Motif support by Robert Webb 5 * Athena port by Bill Foster 6 * 7 * Do ":help uganda" in Vim to read copying and usage conditions. 8 * Do ":help credits" in Vim to see a list of people who contributed. 9 * See README.txt for an overview of the Vim source code. 10 */ 11 12 #include <X11/StringDefs.h> 13 #include <X11/Intrinsic.h> 14 #ifdef FEAT_GUI_NEXTAW 15 # include <X11/neXtaw/Form.h> 16 # include <X11/neXtaw/SimpleMenu.h> 17 # include <X11/neXtaw/MenuButton.h> 18 # include <X11/neXtaw/SmeBSB.h> 19 # include <X11/neXtaw/SmeLine.h> 20 # include <X11/neXtaw/Box.h> 21 # include <X11/neXtaw/Dialog.h> 22 # include <X11/neXtaw/Text.h> 23 # include <X11/neXtaw/AsciiText.h> 24 # include <X11/neXtaw/Scrollbar.h> 25 #else 26 # include <X11/Xaw/Form.h> 27 # include <X11/Xaw/SimpleMenu.h> 28 # include <X11/Xaw/MenuButton.h> 29 # include <X11/Xaw/SmeBSB.h> 30 # include <X11/Xaw/SmeLine.h> 31 # include <X11/Xaw/Box.h> 32 # include <X11/Xaw/Dialog.h> 33 # include <X11/Xaw/Text.h> 34 # include <X11/Xaw/AsciiText.h> 35 #endif /* FEAT_GUI_NEXTAW */ 36 37 #include "vim.h" 38 #ifndef FEAT_GUI_NEXTAW 39 # include "gui_at_sb.h" 40 #endif 41 42 extern Widget vimShell; 43 44 static Widget vimForm = (Widget)0; 45 Widget textArea = (Widget)0; 46 #ifdef FEAT_MENU 47 static Widget menuBar = (Widget)0; 48 static XtIntervalId timer = 0; /* 0 = expired, otherwise active */ 49 50 /* Used to figure out menu ordering */ 51 static vimmenu_T *a_cur_menu = NULL; 52 static Cardinal athena_calculate_ins_pos __ARGS((Widget)); 53 54 static Pixmap gui_athena_create_pullright_pixmap __ARGS((Widget)); 55 static void gui_athena_menu_timeout __ARGS((XtPointer, XtIntervalId *)); 56 static void gui_athena_popup_callback __ARGS((Widget, XtPointer, XtPointer)); 57 static void gui_athena_delayed_arm_action __ARGS((Widget, XEvent *, String *, 58 Cardinal *)); 59 static void gui_athena_popdown_submenus_action __ARGS((Widget, XEvent *, 60 String *, Cardinal *)); 61 static XtActionsRec pullAction[2] = { 62 { "menu-delayedpopup", (XtActionProc)gui_athena_delayed_arm_action}, 63 { "menu-popdownsubmenus", (XtActionProc)gui_athena_popdown_submenus_action} 64 }; 65 #endif 66 67 #ifdef FEAT_TOOLBAR 68 static void gui_mch_reset_focus __ARGS((void)); 69 static Widget toolBar = (Widget)0; 70 #endif 71 72 static void gui_athena_scroll_cb_jump __ARGS((Widget, XtPointer, XtPointer)); 73 static void gui_athena_scroll_cb_scroll __ARGS((Widget, XtPointer, XtPointer)); 74 #if defined(FEAT_GUI_DIALOG) || defined(FEAT_MENU) 75 static void gui_athena_menu_colors __ARGS((Widget id)); 76 #endif 77 static void gui_athena_scroll_colors __ARGS((Widget id)); 78 79 #ifdef FEAT_MENU 80 static XtTranslations popupTrans, parentTrans, menuTrans, supermenuTrans; 81 static Pixmap pullerBitmap = None; 82 static int puller_width = 0; 83 #endif 84 85 /* 86 * Scrollbar callback (XtNjumpProc) for when the scrollbar is dragged with the 87 * left or middle mouse button. 88 */ 89 static void 90 gui_athena_scroll_cb_jump(w, client_data, call_data) 91 Widget w UNUSED; 92 XtPointer client_data, call_data; 93 { 94 scrollbar_T *sb, *sb_info; 95 long value; 96 97 sb = gui_find_scrollbar((long)client_data); 98 99 if (sb == NULL) 100 return; 101 else if (sb->wp != NULL) /* Left or right scrollbar */ 102 { 103 /* 104 * Careful: need to get scrollbar info out of first (left) scrollbar 105 * for window, but keep real scrollbar too because we must pass it to 106 * gui_drag_scrollbar(). 107 */ 108 sb_info = &sb->wp->w_scrollbars[0]; 109 } 110 else /* Bottom scrollbar */ 111 sb_info = sb; 112 113 value = (long)(*((float *)call_data) * (float)(sb_info->max + 1) + 0.001); 114 if (value > sb_info->max) 115 value = sb_info->max; 116 117 gui_drag_scrollbar(sb, value, TRUE); 118 } 119 120 /* 121 * Scrollbar callback (XtNscrollProc) for paging up or down with the left or 122 * right mouse buttons. 123 */ 124 static void 125 gui_athena_scroll_cb_scroll(w, client_data, call_data) 126 Widget w UNUSED; 127 XtPointer client_data, call_data; 128 { 129 scrollbar_T *sb, *sb_info; 130 long value; 131 int data = (int)(long)call_data; 132 int page; 133 134 sb = gui_find_scrollbar((long)client_data); 135 136 if (sb == NULL) 137 return; 138 if (sb->wp != NULL) /* Left or right scrollbar */ 139 { 140 /* 141 * Careful: need to get scrollbar info out of first (left) scrollbar 142 * for window, but keep real scrollbar too because we must pass it to 143 * gui_drag_scrollbar(). 144 */ 145 sb_info = &sb->wp->w_scrollbars[0]; 146 147 if (sb_info->size > 5) 148 page = sb_info->size - 2; /* use two lines of context */ 149 else 150 page = sb_info->size; 151 #ifdef FEAT_GUI_NEXTAW 152 if (data < 0) 153 { 154 data = (data - gui.char_height + 1) / gui.char_height; 155 if (data > -sb_info->size) 156 data = -1; 157 else 158 data = -page; 159 } 160 else if (data > 0) 161 { 162 data = (data + gui.char_height - 1) / gui.char_height; 163 if (data < sb_info->size) 164 data = 1; 165 else 166 data = page; 167 } 168 #else 169 switch (data) 170 { 171 case ONE_LINE_DATA: data = 1; break; 172 case -ONE_LINE_DATA: data = -1; break; 173 case ONE_PAGE_DATA: data = page; break; 174 case -ONE_PAGE_DATA: data = -page; break; 175 case END_PAGE_DATA: data = sb_info->max; break; 176 case -END_PAGE_DATA: data = -sb_info->max; break; 177 default: data = 0; break; 178 } 179 #endif 180 } 181 else /* Bottom scrollbar */ 182 { 183 sb_info = sb; 184 #ifdef FEAT_GUI_NEXTAW 185 if (data < 0) 186 { 187 data = (data - gui.char_width + 1) / gui.char_width; 188 if (data > -sb->size) 189 data = -1; 190 } 191 else if (data > 0) 192 { 193 data = (data + gui.char_width - 1) / gui.char_width; 194 if (data < sb->size) 195 data = 1; 196 } 197 #endif 198 if (data < -1) /* page-width left */ 199 { 200 if (sb->size > 8) 201 data = -(sb->size - 5); 202 else 203 data = -sb->size; 204 } 205 else if (data > 1) /* page-width right */ 206 { 207 if (sb->size > 8) 208 data = (sb->size - 5); 209 else 210 data = sb->size; 211 } 212 } 213 214 value = sb_info->value + data; 215 if (value > sb_info->max) 216 value = sb_info->max; 217 else if (value < 0) 218 value = 0; 219 220 /* Update the bottom scrollbar an extra time (why is this needed?? */ 221 if (sb->wp == NULL) /* Bottom scrollbar */ 222 gui_mch_set_scrollbar_thumb(sb, value, sb->size, sb->max); 223 224 gui_drag_scrollbar(sb, value, FALSE); 225 } 226 227 /* 228 * Create all the Athena widgets necessary. 229 */ 230 void 231 gui_x11_create_widgets() 232 { 233 /* 234 * We don't have any borders handled internally by the textArea to worry 235 * about so only skip over the configured border width. 236 */ 237 gui.border_offset = gui.border_width; 238 239 /* The form containing all the other widgets */ 240 vimForm = XtVaCreateManagedWidget("vimForm", 241 formWidgetClass, vimShell, 242 XtNborderWidth, 0, 243 NULL); 244 gui_athena_scroll_colors(vimForm); 245 246 #ifdef FEAT_MENU 247 /* The top menu bar */ 248 menuBar = XtVaCreateManagedWidget("menuBar", 249 boxWidgetClass, vimForm, 250 XtNresizable, True, 251 XtNtop, XtChainTop, 252 XtNbottom, XtChainTop, 253 XtNleft, XtChainLeft, 254 XtNright, XtChainRight, 255 XtNinsertPosition, athena_calculate_ins_pos, 256 NULL); 257 gui_athena_menu_colors(menuBar); 258 if (gui.menu_fg_pixel != INVALCOLOR) 259 XtVaSetValues(menuBar, XtNborderColor, gui.menu_fg_pixel, NULL); 260 #endif 261 262 #ifdef FEAT_TOOLBAR 263 /* Don't create it Managed, it will be managed when creating the first 264 * item. Otherwise an empty toolbar shows up. */ 265 toolBar = XtVaCreateWidget("toolBar", 266 boxWidgetClass, vimForm, 267 XtNresizable, True, 268 XtNtop, XtChainTop, 269 XtNbottom, XtChainTop, 270 XtNleft, XtChainLeft, 271 XtNright, XtChainRight, 272 XtNorientation, XtorientHorizontal, 273 XtNhSpace, 1, 274 XtNvSpace, 3, 275 XtNinsertPosition, athena_calculate_ins_pos, 276 NULL); 277 gui_athena_menu_colors(toolBar); 278 #endif 279 280 /* The text area. */ 281 textArea = XtVaCreateManagedWidget("textArea", 282 coreWidgetClass, vimForm, 283 XtNresizable, True, 284 XtNtop, XtChainTop, 285 XtNbottom, XtChainTop, 286 XtNleft, XtChainLeft, 287 XtNright, XtChainLeft, 288 XtNbackground, gui.back_pixel, 289 XtNborderWidth, 0, 290 NULL); 291 292 /* 293 * Install the callbacks. 294 */ 295 gui_x11_callbacks(textArea, vimForm); 296 297 #ifdef FEAT_MENU 298 popupTrans = XtParseTranslationTable( 299 "<EnterWindow>: menu-popdownsubmenus() highlight() menu-delayedpopup()\n" 300 "<LeaveWindow>: unhighlight()\n" 301 "<BtnUp>: menu-popdownsubmenus() XtMenuPopdown() notify() unhighlight()\n" 302 "<Motion>: highlight() menu-delayedpopup()"); 303 parentTrans = XtParseTranslationTable("<LeaveWindow>: unhighlight()"); 304 menuTrans = XtParseTranslationTable( 305 "<EnterWindow>: menu-popdownsubmenus() highlight() menu-delayedpopup()\n" 306 "<LeaveWindow>: menu-popdownsubmenus() XtMenuPopdown() unhighlight()\n" 307 "<BtnUp>: notify() unhighlight()\n" 308 "<BtnMotion>: highlight() menu-delayedpopup()"); 309 supermenuTrans = XtParseTranslationTable( 310 "<EnterWindow>: menu-popdownsubmenus() highlight() menu-delayedpopup()\n" 311 "<LeaveWindow>: unhighlight()\n" 312 "<BtnUp>: menu-popdownsubmenus() XtMenuPopdown() notify() unhighlight()\n" 313 "<BtnMotion>: highlight() menu-delayedpopup()"); 314 315 XtAppAddActions(XtWidgetToApplicationContext(vimForm), pullAction, 316 XtNumber(pullAction)); 317 #endif 318 319 /* Pretend we don't have input focus, we will get an event if we do. */ 320 gui.in_focus = FALSE; 321 } 322 323 #ifdef FEAT_MENU 324 /* 325 * Calculates the Pixmap based on the size of the current menu font. 326 */ 327 static Pixmap 328 gui_athena_create_pullright_pixmap(w) 329 Widget w; 330 { 331 Pixmap retval; 332 #ifdef FONTSET_ALWAYS 333 XFontSet font = None; 334 #else 335 XFontStruct *font = NULL; 336 #endif 337 338 #ifdef FONTSET_ALWAYS 339 if (gui.menu_fontset == NOFONTSET) 340 #else 341 if (gui.menu_font == NOFONT) 342 #endif 343 { 344 XrmValue from, to; 345 WidgetList children; 346 Cardinal num_children; 347 348 #ifdef FONTSET_ALWAYS 349 from.size = strlen(from.addr = XtDefaultFontSet); 350 to.addr = (XtPointer)&font; 351 to.size = sizeof(XFontSet); 352 #else 353 from.size = strlen(from.addr = XtDefaultFont); 354 to.addr = (XtPointer)&font; 355 to.size = sizeof(XFontStruct *); 356 #endif 357 /* Assumption: The menuBar children will use the same font as the 358 * pulldown menu items AND they will all be of type 359 * XtNfont. 360 */ 361 XtVaGetValues(menuBar, XtNchildren, &children, 362 XtNnumChildren, &num_children, 363 NULL); 364 if (XtConvertAndStore(w ? w : 365 (num_children > 0) ? children[0] : menuBar, 366 XtRString, &from, 367 #ifdef FONTSET_ALWAYS 368 XtRFontSet, &to 369 #else 370 XtRFontStruct, &to 371 #endif 372 ) == False) 373 return None; 374 /* "font" should now contain data */ 375 } 376 else 377 #ifdef FONTSET_ALWAYS 378 font = (XFontSet)gui.menu_fontset; 379 #else 380 font = (XFontStruct *)gui.menu_font; 381 #endif 382 383 { 384 int width, height; 385 GC draw_gc, undraw_gc; 386 XGCValues gc_values; 387 XPoint points[3]; 388 389 #ifdef FONTSET_ALWAYS 390 height = fontset_height2(font); 391 #else 392 height = font->max_bounds.ascent + font->max_bounds.descent; 393 #endif 394 width = height - 2; 395 puller_width = width + 4; 396 retval = XCreatePixmap(gui.dpy,DefaultRootWindow(gui.dpy),width, 397 height, 1); 398 gc_values.foreground = 1; 399 gc_values.background = 0; 400 draw_gc = XCreateGC(gui.dpy, retval, 401 GCForeground | GCBackground, 402 &gc_values); 403 gc_values.foreground = 0; 404 gc_values.background = 1; 405 undraw_gc = XCreateGC(gui.dpy, retval, 406 GCForeground | GCBackground, 407 &gc_values); 408 points[0].x = 0; 409 points[0].y = 0; 410 points[1].x = width - 1; 411 points[1].y = (height - 1) / 2; 412 points[2].x = 0; 413 points[2].y = height - 1; 414 XFillRectangle(gui.dpy, retval, undraw_gc, 0, 0, height, height); 415 XFillPolygon(gui.dpy, retval, draw_gc, points, XtNumber(points), 416 Convex, CoordModeOrigin); 417 XFreeGC(gui.dpy, draw_gc); 418 XFreeGC(gui.dpy, undraw_gc); 419 } 420 return retval; 421 } 422 #endif 423 424 /* 425 * Called when the GUI is not going to start after all. 426 */ 427 void 428 gui_x11_destroy_widgets() 429 { 430 textArea = NULL; 431 #ifdef FEAT_MENU 432 menuBar = NULL; 433 #endif 434 #ifdef FEAT_TOOLBAR 435 toolBar = NULL; 436 #endif 437 } 438 439 #if defined(FEAT_TOOLBAR) || defined(PROTO) 440 # include "gui_x11_pm.h" 441 # ifdef HAVE_X11_XPM_H 442 # include <X11/xpm.h> 443 # endif 444 445 static void createXpmImages __ARGS((char_u *path, char **xpm, Pixmap *sen)); 446 static void get_toolbar_pixmap __ARGS((vimmenu_T *menu, Pixmap *sen)); 447 448 /* 449 * Allocated a pixmap for toolbar menu "menu". 450 * Return in "sen". 451 */ 452 static void 453 get_toolbar_pixmap(menu, sen) 454 vimmenu_T *menu; 455 Pixmap *sen; 456 { 457 char_u buf[MAXPATHL]; /* buffer storing expanded pathname */ 458 char **xpm = NULL; /* xpm array */ 459 460 buf[0] = NUL; /* start with NULL path */ 461 462 if (menu->iconfile != NULL) 463 { 464 /* Use the "icon=" argument. */ 465 gui_find_iconfile(menu->iconfile, buf, "xpm"); 466 createXpmImages(buf, NULL, sen); 467 468 /* If it failed, try using the menu name. */ 469 if (*sen == (Pixmap)0 && gui_find_bitmap(menu->name, buf, "xpm") == OK) 470 createXpmImages(buf, NULL, sen); 471 if (*sen != (Pixmap)0) 472 return; 473 } 474 475 if (menu->icon_builtin || gui_find_bitmap(menu->name, buf, "xpm") == FAIL) 476 { 477 if (menu->iconidx >= 0 && menu->iconidx 478 < (int)(sizeof(built_in_pixmaps) / sizeof(built_in_pixmaps[0]))) 479 xpm = built_in_pixmaps[menu->iconidx]; 480 else 481 xpm = tb_blank_xpm; 482 } 483 484 if (xpm != NULL || buf[0] != NUL) 485 createXpmImages(buf, xpm, sen); 486 } 487 488 /* 489 * Read an Xpm file, doing color substitutions for the foreground and 490 * background colors. If there is an error reading a color xpm file, 491 * drop back and read the monochrome file. If successful, create the 492 * insensitive Pixmap too. 493 */ 494 static void 495 createXpmImages(path, xpm, sen) 496 char_u *path; 497 char **xpm; 498 Pixmap *sen; 499 { 500 Window rootWindow; 501 XpmAttributes attrs; 502 XpmColorSymbol color[5] = 503 { 504 {"none", "none", 0}, 505 {"iconColor1", NULL, 0}, 506 {"bottomShadowColor", NULL, 0}, 507 {"topShadowColor", NULL, 0}, 508 {"selectColor", NULL, 0} 509 }; 510 int screenNum; 511 int status; 512 Pixmap mask; 513 Pixmap map; 514 515 gui_mch_get_toolbar_colors( 516 &color[BACKGROUND].pixel, 517 &color[FOREGROUND].pixel, 518 &color[BOTTOM_SHADOW].pixel, 519 &color[TOP_SHADOW].pixel, 520 &color[HIGHLIGHT].pixel); 521 522 /* Setup the color substitution table */ 523 attrs.valuemask = XpmColorSymbols; 524 attrs.colorsymbols = color; 525 attrs.numsymbols = 5; 526 527 screenNum = DefaultScreen(gui.dpy); 528 rootWindow = RootWindow(gui.dpy, screenNum); 529 530 /* Create the "sensitive" pixmap */ 531 if (xpm != NULL) 532 status = XpmCreatePixmapFromData(gui.dpy, rootWindow, xpm, 533 &map, &mask, &attrs); 534 else 535 status = XpmReadFileToPixmap(gui.dpy, rootWindow, (char *)path, 536 &map, &mask, &attrs); 537 if (status == XpmSuccess && map != 0) 538 { 539 XGCValues gcvalues; 540 GC back_gc; 541 GC mask_gc; 542 543 /* Need to create new Pixmaps with the mask applied. */ 544 gcvalues.foreground = color[BACKGROUND].pixel; 545 back_gc = XCreateGC(gui.dpy, map, GCForeground, &gcvalues); 546 mask_gc = XCreateGC(gui.dpy, map, GCForeground, &gcvalues); 547 XSetClipMask(gui.dpy, mask_gc, mask); 548 549 /* Create the "sensitive" pixmap. */ 550 *sen = XCreatePixmap(gui.dpy, rootWindow, 551 attrs.width, attrs.height, 552 DefaultDepth(gui.dpy, screenNum)); 553 XFillRectangle(gui.dpy, *sen, back_gc, 0, 0, 554 attrs.width, attrs.height); 555 XCopyArea(gui.dpy, map, *sen, mask_gc, 0, 0, 556 attrs.width, attrs.height, 0, 0); 557 558 XFreeGC(gui.dpy, back_gc); 559 XFreeGC(gui.dpy, mask_gc); 560 XFreePixmap(gui.dpy, map); 561 } 562 else 563 *sen = 0; 564 565 XpmFreeAttributes(&attrs); 566 } 567 568 void 569 gui_mch_set_toolbar_pos(x, y, w, h) 570 int x; 571 int y; 572 int w; 573 int h; 574 { 575 Dimension border; 576 int height; 577 578 if (!XtIsManaged(toolBar)) /* nothing to do */ 579 return; 580 XtUnmanageChild(toolBar); 581 XtVaGetValues(toolBar, 582 XtNborderWidth, &border, 583 NULL); 584 height = h - 2 * border; 585 if (height < 0) 586 height = 1; 587 XtVaSetValues(toolBar, 588 XtNhorizDistance, x, 589 XtNvertDistance, y, 590 XtNwidth, w - 2 * border, 591 XtNheight, height, 592 NULL); 593 XtManageChild(toolBar); 594 } 595 #endif 596 597 void 598 gui_mch_set_text_area_pos(x, y, w, h) 599 int x; 600 int y; 601 int w; 602 int h; 603 { 604 XtUnmanageChild(textArea); 605 XtVaSetValues(textArea, 606 XtNhorizDistance, x, 607 XtNvertDistance, y, 608 XtNwidth, w, 609 XtNheight, h, 610 NULL); 611 XtManageChild(textArea); 612 #ifdef FEAT_TOOLBAR 613 /* Give keyboard focus to the textArea instead of the toolbar. */ 614 gui_mch_reset_focus(); 615 #endif 616 } 617 618 #ifdef FEAT_TOOLBAR 619 /* 620 * A toolbar button has been pushed; now reset the input focus 621 * such that the user can type page up/down etc. and have the 622 * input go to the editor window, not the button 623 */ 624 static void 625 gui_mch_reset_focus() 626 { 627 XtSetKeyboardFocus(vimForm, textArea); 628 } 629 #endif 630 631 632 void 633 gui_x11_set_back_color() 634 { 635 if (textArea != NULL) 636 XtVaSetValues(textArea, 637 XtNbackground, gui.back_pixel, 638 NULL); 639 } 640 641 #if defined(FEAT_MENU) || defined(PROTO) 642 /* 643 * Menu stuff. 644 */ 645 646 static char_u *make_pull_name __ARGS((char_u * name)); 647 static Widget get_popup_entry __ARGS((Widget w)); 648 static Widget submenu_widget __ARGS((Widget)); 649 static Boolean has_submenu __ARGS((Widget)); 650 static void gui_mch_submenu_change __ARGS((vimmenu_T *mp, int colors)); 651 static void gui_athena_menu_font __ARGS((Widget id)); 652 static Boolean gui_athena_menu_has_submenus __ARGS((Widget, Widget)); 653 654 void 655 gui_mch_enable_menu(flag) 656 int flag; 657 { 658 if (flag) 659 { 660 XtManageChild(menuBar); 661 # ifdef FEAT_TOOLBAR 662 if (XtIsManaged(toolBar)) 663 { 664 XtVaSetValues(toolBar, 665 XtNvertDistance, gui.menu_height, 666 NULL); 667 XtVaSetValues(textArea, 668 XtNvertDistance, gui.menu_height + gui.toolbar_height, 669 NULL); 670 } 671 # endif 672 } 673 else 674 { 675 XtUnmanageChild(menuBar); 676 # ifdef FEAT_TOOLBAR 677 if (XtIsManaged(toolBar)) 678 { 679 XtVaSetValues(toolBar, 680 XtNvertDistance, 0, 681 NULL); 682 } 683 # endif 684 } 685 } 686 687 void 688 gui_mch_set_menu_pos(x, y, w, h) 689 int x; 690 int y; 691 int w; 692 int h; 693 { 694 Dimension border; 695 int height; 696 697 XtUnmanageChild(menuBar); 698 XtVaGetValues(menuBar, XtNborderWidth, &border, NULL); 699 /* avoid trouble when there are no menu items, and h is 1 */ 700 height = h - 2 * border; 701 if (height < 0) 702 height = 1; 703 XtVaSetValues(menuBar, 704 XtNhorizDistance, x, 705 XtNvertDistance, y, 706 XtNwidth, w - 2 * border, 707 XtNheight, height, 708 NULL); 709 XtManageChild(menuBar); 710 } 711 712 /* 713 * Used to calculate the insertion position of a widget with respect to its 714 * neighbors. 715 * 716 * Valid range of return values is: 0 (beginning of children) to 717 * numChildren (end of children). 718 */ 719 static Cardinal 720 athena_calculate_ins_pos(widget) 721 Widget widget; 722 { 723 /* Assume that if the parent of the vimmenu_T is NULL, then we can get 724 * to this menu by traversing "next", starting at "root_menu". 725 * 726 * This holds true for popup menus, toolbar, and toplevel menu items. 727 */ 728 729 /* Popup menus: "id" is NULL. Only submenu_id is valid */ 730 731 /* Menus that are not toplevel: "parent" will be non-NULL, "id" & 732 * "submenu_id" will be non-NULL. 733 */ 734 735 /* Toplevel menus: "parent" is NULL, id is the widget of the menu item */ 736 737 WidgetList children; 738 Cardinal num_children = 0; 739 int retval; 740 Arg args[2]; 741 int n = 0; 742 int i; 743 744 XtSetArg(args[n], XtNchildren, &children); n++; 745 XtSetArg(args[n], XtNnumChildren, &num_children); n++; 746 XtGetValues(XtParent(widget), args, n); 747 748 retval = num_children; 749 for (i = 0; i < (int)num_children; ++i) 750 { 751 Widget current = children[i]; 752 vimmenu_T *menu = NULL; 753 754 for (menu = (a_cur_menu->parent == NULL) 755 ? root_menu : a_cur_menu->parent->children; 756 menu != NULL; 757 menu = menu->next) 758 if (current == menu->id 759 && a_cur_menu->priority < menu->priority 760 && i < retval) 761 retval = i; 762 } 763 return retval; 764 } 765 766 void 767 gui_mch_add_menu(menu, idx) 768 vimmenu_T *menu; 769 int idx UNUSED; 770 { 771 char_u *pullright_name; 772 Dimension height, space, border; 773 vimmenu_T *parent = menu->parent; 774 775 a_cur_menu = menu; 776 if (parent == NULL) 777 { 778 if (menu_is_popup(menu->dname)) 779 { 780 menu->submenu_id = XtVaCreatePopupShell((char *)menu->dname, 781 simpleMenuWidgetClass, vimShell, 782 XtNinsertPosition, athena_calculate_ins_pos, 783 XtNtranslations, popupTrans, 784 NULL); 785 gui_athena_menu_colors(menu->submenu_id); 786 } 787 else if (menu_is_menubar(menu->dname)) 788 { 789 menu->id = XtVaCreateManagedWidget((char *)menu->dname, 790 menuButtonWidgetClass, menuBar, 791 XtNmenuName, menu->dname, 792 #ifdef FONTSET_ALWAYS 793 XtNinternational, True, 794 #endif 795 NULL); 796 if (menu->id == (Widget)0) 797 return; 798 gui_athena_menu_colors(menu->id); 799 gui_athena_menu_font(menu->id); 800 801 menu->submenu_id = XtVaCreatePopupShell((char *)menu->dname, 802 simpleMenuWidgetClass, menu->id, 803 XtNinsertPosition, athena_calculate_ins_pos, 804 XtNtranslations, supermenuTrans, 805 NULL); 806 gui_athena_menu_colors(menu->submenu_id); 807 gui_athena_menu_font(menu->submenu_id); 808 809 /* Don't update the menu height when it was set at a fixed value */ 810 if (!gui.menu_height_fixed) 811 { 812 /* 813 * When we add a top-level item to the menu bar, we can figure 814 * out how high the menu bar should be. 815 */ 816 XtVaGetValues(menuBar, 817 XtNvSpace, &space, 818 XtNborderWidth, &border, 819 NULL); 820 XtVaGetValues(menu->id, 821 XtNheight, &height, 822 NULL); 823 gui.menu_height = height + 2 * (space + border); 824 } 825 } 826 } 827 else if (parent->submenu_id != (Widget)0) 828 { 829 menu->id = XtVaCreateManagedWidget((char *)menu->dname, 830 smeBSBObjectClass, parent->submenu_id, 831 XtNlabel, menu->dname, 832 #ifdef FONTSET_ALWAYS 833 XtNinternational, True, 834 #endif 835 NULL); 836 if (menu->id == (Widget)0) 837 return; 838 if (pullerBitmap == None) 839 pullerBitmap = gui_athena_create_pullright_pixmap(menu->id); 840 841 XtVaSetValues(menu->id, XtNrightBitmap, pullerBitmap, 842 NULL); 843 /* If there are other menu items that are not pulldown menus, 844 * we need to adjust the right margins of those, too. 845 */ 846 { 847 WidgetList children; 848 Cardinal num_children; 849 int i; 850 851 XtVaGetValues(parent->submenu_id, XtNchildren, &children, 852 XtNnumChildren, &num_children, 853 NULL); 854 for (i = 0; i < (int)num_children; ++i) 855 { 856 XtVaSetValues(children[i], 857 XtNrightMargin, puller_width, 858 NULL); 859 } 860 } 861 gui_athena_menu_colors(menu->id); 862 gui_athena_menu_font(menu->id); 863 864 pullright_name = make_pull_name(menu->dname); 865 menu->submenu_id = XtVaCreatePopupShell((char *)pullright_name, 866 simpleMenuWidgetClass, parent->submenu_id, 867 XtNtranslations, menuTrans, 868 NULL); 869 gui_athena_menu_colors(menu->submenu_id); 870 gui_athena_menu_font(menu->submenu_id); 871 vim_free(pullright_name); 872 XtAddCallback(menu->submenu_id, XtNpopupCallback, 873 gui_athena_popup_callback, (XtPointer)menu); 874 875 if (parent->parent != NULL) 876 XtOverrideTranslations(parent->submenu_id, parentTrans); 877 } 878 a_cur_menu = NULL; 879 } 880 881 /* Used to determine whether a SimpleMenu has pulldown entries. 882 * 883 * "id" is the parent of the menu items. 884 * Ignore widget "ignore" in the pane. 885 */ 886 static Boolean 887 gui_athena_menu_has_submenus(id, ignore) 888 Widget id; 889 Widget ignore; 890 { 891 WidgetList children; 892 Cardinal num_children; 893 int i; 894 895 XtVaGetValues(id, XtNchildren, &children, 896 XtNnumChildren, &num_children, 897 NULL); 898 for (i = 0; i < (int)num_children; ++i) 899 { 900 if (children[i] == ignore) 901 continue; 902 if (has_submenu(children[i])) 903 return True; 904 } 905 return False; 906 } 907 908 static void 909 gui_athena_menu_font(id) 910 Widget id; 911 { 912 #ifdef FONTSET_ALWAYS 913 if (gui.menu_fontset != NOFONTSET) 914 { 915 if (XtIsManaged(id)) 916 { 917 XtUnmanageChild(id); 918 XtVaSetValues(id, XtNfontSet, gui.menu_fontset, NULL); 919 /* We should force the widget to recalculate it's 920 * geometry now. */ 921 XtManageChild(id); 922 } 923 else 924 XtVaSetValues(id, XtNfontSet, gui.menu_fontset, NULL); 925 if (has_submenu(id)) 926 XtVaSetValues(id, XtNrightBitmap, pullerBitmap, NULL); 927 } 928 #else 929 int managed = FALSE; 930 931 if (gui.menu_font != NOFONT) 932 { 933 if (XtIsManaged(id)) 934 { 935 XtUnmanageChild(id); 936 managed = TRUE; 937 } 938 939 # ifdef FEAT_XFONTSET 940 if (gui.fontset != NOFONTSET) 941 XtVaSetValues(id, XtNfontSet, gui.menu_font, NULL); 942 else 943 # endif 944 XtVaSetValues(id, XtNfont, gui.menu_font, NULL); 945 if (has_submenu(id)) 946 XtVaSetValues(id, XtNrightBitmap, pullerBitmap, NULL); 947 948 /* Force the widget to recalculate it's geometry now. */ 949 if (managed) 950 XtManageChild(id); 951 } 952 #endif 953 } 954 955 956 void 957 gui_mch_new_menu_font() 958 { 959 Pixmap oldpuller = None; 960 961 if (menuBar == (Widget)0) 962 return; 963 964 if (pullerBitmap != None) 965 { 966 oldpuller = pullerBitmap; 967 pullerBitmap = gui_athena_create_pullright_pixmap(NULL); 968 } 969 gui_mch_submenu_change(root_menu, FALSE); 970 971 { 972 /* Iterate through the menubar menu items and get the height of 973 * each one. The menu bar height is set to the maximum of all 974 * the heights. 975 */ 976 vimmenu_T *mp; 977 int max_height = 9999; 978 979 for (mp = root_menu; mp != NULL; mp = mp->next) 980 { 981 if (menu_is_menubar(mp->dname)) 982 { 983 Dimension height; 984 985 XtVaGetValues(mp->id, 986 XtNheight, &height, 987 NULL); 988 if (height < max_height) 989 max_height = height; 990 } 991 } 992 if (max_height != 9999) 993 { 994 /* Don't update the menu height when it was set at a fixed value */ 995 if (!gui.menu_height_fixed) 996 { 997 Dimension space, border; 998 999 XtVaGetValues(menuBar, 1000 XtNvSpace, &space, 1001 XtNborderWidth, &border, 1002 NULL); 1003 gui.menu_height = max_height + 2 * (space + border); 1004 } 1005 } 1006 } 1007 /* Now, to simulate the window being resized. Only, this 1008 * will resize the window to it's current state. 1009 * 1010 * There has to be a better way, but I do not see one at this time. 1011 * (David Harrison) 1012 */ 1013 { 1014 Position w, h; 1015 1016 XtVaGetValues(vimShell, 1017 XtNwidth, &w, 1018 XtNheight, &h, 1019 NULL); 1020 gui_resize_shell(w, h 1021 #ifdef FEAT_XIM 1022 - xim_get_status_area_height() 1023 #endif 1024 ); 1025 } 1026 gui_set_shellsize(FALSE, TRUE, RESIZE_VERT); 1027 ui_new_shellsize(); 1028 if (oldpuller != None) 1029 XFreePixmap(gui.dpy, oldpuller); 1030 } 1031 1032 #if defined(FEAT_BEVAL) || defined(PROTO) 1033 void 1034 gui_mch_new_tooltip_font() 1035 { 1036 # ifdef FEAT_TOOLBAR 1037 vimmenu_T *menu; 1038 1039 if (toolBar == (Widget)0) 1040 return; 1041 1042 menu = gui_find_menu((char_u *)"ToolBar"); 1043 if (menu != NULL) 1044 gui_mch_submenu_change(menu, FALSE); 1045 # endif 1046 } 1047 1048 void 1049 gui_mch_new_tooltip_colors() 1050 { 1051 # ifdef FEAT_TOOLBAR 1052 vimmenu_T *menu; 1053 1054 if (toolBar == (Widget)0) 1055 return; 1056 1057 menu = gui_find_menu((char_u *)"ToolBar"); 1058 if (menu != NULL) 1059 gui_mch_submenu_change(menu, TRUE); 1060 # endif 1061 } 1062 #endif 1063 1064 static void 1065 gui_mch_submenu_change(menu, colors) 1066 vimmenu_T *menu; 1067 int colors; /* TRUE for colors, FALSE for font */ 1068 { 1069 vimmenu_T *mp; 1070 1071 for (mp = menu; mp != NULL; mp = mp->next) 1072 { 1073 if (mp->id != (Widget)0) 1074 { 1075 if (colors) 1076 { 1077 gui_athena_menu_colors(mp->id); 1078 #ifdef FEAT_TOOLBAR 1079 /* For a toolbar item: Free the pixmap and allocate a new one, 1080 * so that the background color is right. */ 1081 if (mp->image != (Pixmap)0) 1082 { 1083 XFreePixmap(gui.dpy, mp->image); 1084 get_toolbar_pixmap(mp, &mp->image); 1085 if (mp->image != (Pixmap)0) 1086 XtVaSetValues(mp->id, XtNbitmap, mp->image, NULL); 1087 } 1088 1089 # ifdef FEAT_BEVAL 1090 /* If we have a tooltip, then we need to change it's colors */ 1091 if (mp->tip != NULL) 1092 { 1093 Arg args[2]; 1094 1095 args[0].name = XtNbackground; 1096 args[0].value = gui.tooltip_bg_pixel; 1097 args[1].name = XtNforeground; 1098 args[1].value = gui.tooltip_fg_pixel; 1099 XtSetValues(mp->tip->balloonLabel, &args[0], XtNumber(args)); 1100 } 1101 # endif 1102 #endif 1103 } 1104 else 1105 { 1106 gui_athena_menu_font(mp->id); 1107 #ifdef FEAT_BEVAL 1108 /* If we have a tooltip, then we need to change it's font */ 1109 /* Assume XtNinternational == True (in createBalloonEvalWindow) 1110 */ 1111 if (mp->tip != NULL) 1112 { 1113 Arg args[1]; 1114 1115 args[0].name = XtNfontSet; 1116 args[0].value = (XtArgVal)gui.tooltip_fontset; 1117 XtSetValues(mp->tip->balloonLabel, &args[0], XtNumber(args)); 1118 } 1119 #endif 1120 } 1121 } 1122 1123 if (mp->children != NULL) 1124 { 1125 /* Set the colors/font for the tear off widget */ 1126 if (mp->submenu_id != (Widget)0) 1127 { 1128 if (colors) 1129 gui_athena_menu_colors(mp->submenu_id); 1130 else 1131 gui_athena_menu_font(mp->submenu_id); 1132 } 1133 /* Set the colors for the children */ 1134 gui_mch_submenu_change(mp->children, colors); 1135 } 1136 } 1137 } 1138 1139 /* 1140 * Make a submenu name into a pullright name. 1141 * Replace '.' by '_', can't include '.' in the submenu name. 1142 */ 1143 static char_u * 1144 make_pull_name(name) 1145 char_u * name; 1146 { 1147 char_u *pname; 1148 char_u *p; 1149 1150 pname = vim_strnsave(name, STRLEN(name) + strlen("-pullright")); 1151 if (pname != NULL) 1152 { 1153 strcat((char *)pname, "-pullright"); 1154 while ((p = vim_strchr(pname, '.')) != NULL) 1155 *p = '_'; 1156 } 1157 return pname; 1158 } 1159 1160 void 1161 gui_mch_add_menu_item(menu, idx) 1162 vimmenu_T *menu; 1163 int idx UNUSED; 1164 { 1165 vimmenu_T *parent = menu->parent; 1166 1167 a_cur_menu = menu; 1168 # ifdef FEAT_TOOLBAR 1169 if (menu_is_toolbar(parent->name)) 1170 { 1171 WidgetClass type; 1172 int n; 1173 Arg args[21]; 1174 1175 n = 0; 1176 if (menu_is_separator(menu->name)) 1177 { 1178 XtSetArg(args[n], XtNlabel, ""); n++; 1179 XtSetArg(args[n], XtNborderWidth, 0); n++; 1180 } 1181 else 1182 { 1183 get_toolbar_pixmap(menu, &menu->image); 1184 XtSetArg(args[n], XtNlabel, menu->dname); n++; 1185 XtSetArg(args[n], XtNinternalHeight, 1); n++; 1186 XtSetArg(args[n], XtNinternalWidth, 1); n++; 1187 XtSetArg(args[n], XtNborderWidth, 1); n++; 1188 if (menu->image != 0) 1189 XtSetArg(args[n], XtNbitmap, menu->image); n++; 1190 } 1191 XtSetArg(args[n], XtNhighlightThickness, 0); n++; 1192 type = commandWidgetClass; 1193 /* TODO: figure out the position in the toolbar? 1194 * This currently works fine for the default toolbar, but 1195 * what if we add/remove items during later runtime? 1196 */ 1197 1198 /* NOTE: "idx" isn't used here. The position is calculated by 1199 * athena_calculate_ins_pos(). The position it calculates 1200 * should be equal to "idx". 1201 */ 1202 /* TODO: Could we just store "idx" and use that as the child 1203 * placement? 1204 */ 1205 1206 if (menu->id == NULL) 1207 { 1208 menu->id = XtCreateManagedWidget((char *)menu->dname, 1209 type, toolBar, args, n); 1210 XtAddCallback(menu->id, 1211 XtNcallback, gui_x11_menu_cb, menu); 1212 } 1213 else 1214 XtSetValues(menu->id, args, n); 1215 gui_athena_menu_colors(menu->id); 1216 1217 #ifdef FEAT_BEVAL 1218 gui_mch_menu_set_tip(menu); 1219 #endif 1220 1221 menu->parent = parent; 1222 menu->submenu_id = NULL; 1223 if (!XtIsManaged(toolBar) 1224 && vim_strchr(p_go, GO_TOOLBAR) != NULL) 1225 gui_mch_show_toolbar(TRUE); 1226 gui.toolbar_height = gui_mch_compute_toolbar_height(); 1227 return; 1228 } /* toolbar menu item */ 1229 # endif 1230 1231 /* Add menu separator */ 1232 if (menu_is_separator(menu->name)) 1233 { 1234 menu->submenu_id = (Widget)0; 1235 menu->id = XtVaCreateManagedWidget((char *)menu->dname, 1236 smeLineObjectClass, parent->submenu_id, 1237 NULL); 1238 if (menu->id == (Widget)0) 1239 return; 1240 gui_athena_menu_colors(menu->id); 1241 } 1242 else 1243 { 1244 if (parent != NULL && parent->submenu_id != (Widget)0) 1245 { 1246 menu->submenu_id = (Widget)0; 1247 menu->id = XtVaCreateManagedWidget((char *)menu->dname, 1248 smeBSBObjectClass, parent->submenu_id, 1249 XtNlabel, menu->dname, 1250 #ifdef FONTSET_ALWAYS 1251 XtNinternational, True, 1252 #endif 1253 NULL); 1254 if (menu->id == (Widget)0) 1255 return; 1256 1257 /* If there are other "pulldown" items in this pane, then adjust 1258 * the right margin to accommodate the arrow pixmap, otherwise 1259 * the right margin will be the same as the left margin. 1260 */ 1261 { 1262 Dimension left_margin; 1263 1264 XtVaGetValues(menu->id, XtNleftMargin, &left_margin, NULL); 1265 XtVaSetValues(menu->id, XtNrightMargin, 1266 gui_athena_menu_has_submenus(parent->submenu_id, NULL) ? 1267 puller_width : 1268 left_margin, 1269 NULL); 1270 } 1271 1272 gui_athena_menu_colors(menu->id); 1273 gui_athena_menu_font(menu->id); 1274 XtAddCallback(menu->id, XtNcallback, gui_x11_menu_cb, 1275 (XtPointer)menu); 1276 } 1277 } 1278 a_cur_menu = NULL; 1279 } 1280 1281 #if defined(FEAT_TOOLBAR) || defined(PROTO) 1282 void 1283 gui_mch_show_toolbar(int showit) 1284 { 1285 Cardinal numChildren; /* how many children toolBar has */ 1286 1287 if (toolBar == (Widget)0) 1288 return; 1289 XtVaGetValues(toolBar, XtNnumChildren, &numChildren, NULL); 1290 if (showit && numChildren > 0) 1291 { 1292 /* Assume that we want to show the toolbar if p_toolbar contains valid 1293 * option settings, therefore p_toolbar must not be NULL. 1294 */ 1295 WidgetList children; 1296 1297 XtVaGetValues(toolBar, XtNchildren, &children, NULL); 1298 { 1299 void (*action)(BalloonEval *); 1300 int text = 0; 1301 1302 if (strstr((const char *)p_toolbar, "tooltips")) 1303 action = &gui_mch_enable_beval_area; 1304 else 1305 action = &gui_mch_disable_beval_area; 1306 if (strstr((const char *)p_toolbar, "text")) 1307 text = 1; 1308 else if (strstr((const char *)p_toolbar, "icons")) 1309 text = -1; 1310 if (text != 0) 1311 { 1312 vimmenu_T *toolbar; 1313 vimmenu_T *cur; 1314 1315 for (toolbar = root_menu; toolbar; toolbar = toolbar->next) 1316 if (menu_is_toolbar(toolbar->dname)) 1317 break; 1318 /* Assumption: toolbar is NULL if there is no toolbar, 1319 * otherwise it contains the toolbar menu structure. 1320 * 1321 * Assumption: "numChildren" == the number of items in the list 1322 * of items beginning with toolbar->children. 1323 */ 1324 if (toolbar) 1325 { 1326 for (cur = toolbar->children; cur; cur = cur->next) 1327 { 1328 Arg args[2]; 1329 int n = 0; 1330 1331 /* Enable/Disable tooltip (OK to enable while currently 1332 * enabled) 1333 */ 1334 if (cur->tip != NULL) 1335 (*action)(cur->tip); 1336 if (text == 1) 1337 { 1338 XtSetArg(args[n], XtNbitmap, None); 1339 n++; 1340 XtSetArg(args[n], XtNlabel, 1341 menu_is_separator(cur->name) ? "" : 1342 (char *)cur->dname); 1343 n++; 1344 } 1345 else 1346 { 1347 XtSetArg(args[n], XtNbitmap, cur->image); 1348 n++; 1349 XtSetArg(args[n], XtNlabel, (cur->image == None) ? 1350 menu_is_separator(cur->name) ? 1351 "" : 1352 (char *)cur->dname 1353 : 1354 (char *)None); 1355 n++; 1356 } 1357 if (cur->id != NULL) 1358 { 1359 XtUnmanageChild(cur->id); 1360 XtSetValues(cur->id, args, n); 1361 XtManageChild(cur->id); 1362 } 1363 } 1364 } 1365 } 1366 } 1367 gui.toolbar_height = gui_mch_compute_toolbar_height(); 1368 XtManageChild(toolBar); 1369 if (XtIsManaged(menuBar)) 1370 { 1371 XtVaSetValues(textArea, 1372 XtNvertDistance, gui.toolbar_height + gui.menu_height, 1373 NULL); 1374 XtVaSetValues(toolBar, 1375 XtNvertDistance, gui.menu_height, 1376 NULL); 1377 } 1378 else 1379 { 1380 XtVaSetValues(textArea, 1381 XtNvertDistance, gui.toolbar_height, 1382 NULL); 1383 XtVaSetValues(toolBar, 1384 XtNvertDistance, 0, 1385 NULL); 1386 } 1387 } 1388 else 1389 { 1390 gui.toolbar_height = 0; 1391 if (XtIsManaged(menuBar)) 1392 XtVaSetValues(textArea, 1393 XtNvertDistance, gui.menu_height, 1394 NULL); 1395 else 1396 XtVaSetValues(textArea, 1397 XtNvertDistance, 0, 1398 NULL); 1399 1400 XtUnmanageChild(toolBar); 1401 } 1402 gui_set_shellsize(FALSE, FALSE, RESIZE_VERT); 1403 } 1404 1405 1406 int 1407 gui_mch_compute_toolbar_height() 1408 { 1409 Dimension height; /* total Toolbar height */ 1410 Dimension whgt; /* height of each widget */ 1411 Dimension marginHeight; /* XmNmarginHeight of toolBar */ 1412 Dimension shadowThickness; /* thickness of Xtparent(toolBar) */ 1413 WidgetList children; /* list of toolBar's children */ 1414 Cardinal numChildren; /* how many children toolBar has */ 1415 int i; 1416 1417 height = 0; 1418 shadowThickness = 0; 1419 marginHeight = 0; 1420 if (toolBar != (Widget)0) 1421 { 1422 XtVaGetValues(toolBar, 1423 XtNborderWidth, &shadowThickness, 1424 XtNvSpace, &marginHeight, 1425 XtNchildren, &children, 1426 XtNnumChildren, &numChildren, 1427 NULL); 1428 for (i = 0; i < (int)numChildren; i++) 1429 { 1430 whgt = 0; 1431 1432 XtVaGetValues(children[i], XtNheight, &whgt, NULL); 1433 if (height < whgt) 1434 height = whgt; 1435 } 1436 } 1437 1438 return (int)(height + (marginHeight << 1) + (shadowThickness << 1)); 1439 } 1440 1441 void 1442 gui_mch_get_toolbar_colors(bgp, fgp, bsp, tsp, hsp) 1443 Pixel *bgp; 1444 Pixel *fgp; 1445 Pixel *bsp; 1446 Pixel *tsp; 1447 Pixel *hsp; 1448 { 1449 XtVaGetValues(toolBar, XtNbackground, bgp, XtNborderColor, fgp, NULL); 1450 *bsp = *bgp; 1451 *tsp = *fgp; 1452 *hsp = *tsp; 1453 } 1454 #endif 1455 1456 1457 void 1458 gui_mch_toggle_tearoffs(enable) 1459 int enable UNUSED; 1460 { 1461 /* no tearoff menus */ 1462 } 1463 1464 void 1465 gui_mch_new_menu_colors() 1466 { 1467 if (menuBar == (Widget)0) 1468 return; 1469 if (gui.menu_fg_pixel != INVALCOLOR) 1470 XtVaSetValues(menuBar, XtNborderColor, gui.menu_fg_pixel, NULL); 1471 gui_athena_menu_colors(menuBar); 1472 #ifdef FEAT_TOOLBAR 1473 gui_athena_menu_colors(toolBar); 1474 #endif 1475 1476 gui_mch_submenu_change(root_menu, TRUE); 1477 } 1478 1479 /* 1480 * Destroy the machine specific menu widget. 1481 */ 1482 void 1483 gui_mch_destroy_menu(menu) 1484 vimmenu_T *menu; 1485 { 1486 Widget parent; 1487 1488 /* There is no item for the toolbar. */ 1489 if (menu->id == (Widget)0) 1490 return; 1491 1492 parent = XtParent(menu->id); 1493 1494 /* When removing the last "pulldown" menu item from a pane, adjust the 1495 * right margins of the remaining widgets. 1496 */ 1497 if (menu->submenu_id != (Widget)0) 1498 { 1499 /* Go through the menu items in the parent of this item and 1500 * adjust their margins, if necessary. 1501 * This takes care of the case when we delete the last menu item in a 1502 * pane that has a submenu. In this case, there will be no arrow 1503 * pixmaps shown anymore. 1504 */ 1505 { 1506 WidgetList children; 1507 Cardinal num_children; 1508 int i; 1509 Dimension right_margin = 0; 1510 Boolean get_left_margin = False; 1511 1512 XtVaGetValues(parent, XtNchildren, &children, 1513 XtNnumChildren, &num_children, 1514 NULL); 1515 if (gui_athena_menu_has_submenus(parent, menu->id)) 1516 right_margin = puller_width; 1517 else 1518 get_left_margin = True; 1519 1520 for (i = 0; i < (int)num_children; ++i) 1521 { 1522 if (children[i] == menu->id) 1523 continue; 1524 if (get_left_margin == True) 1525 { 1526 Dimension left_margin; 1527 1528 XtVaGetValues(children[i], XtNleftMargin, &left_margin, 1529 NULL); 1530 XtVaSetValues(children[i], XtNrightMargin, left_margin, 1531 NULL); 1532 } 1533 else 1534 XtVaSetValues(children[i], XtNrightMargin, right_margin, 1535 NULL); 1536 } 1537 } 1538 } 1539 /* Please be sure to destroy the parent widget first (i.e. menu->id). 1540 * 1541 * This code should be basically identical to that in the file gui_motif.c 1542 * because they are both Xt based. 1543 */ 1544 if (menu->id != (Widget)0) 1545 { 1546 Cardinal num_children; 1547 Dimension height, space, border; 1548 1549 XtVaGetValues(menuBar, 1550 XtNvSpace, &space, 1551 XtNborderWidth, &border, 1552 NULL); 1553 XtVaGetValues(menu->id, 1554 XtNheight, &height, 1555 NULL); 1556 #if defined(FEAT_TOOLBAR) && defined(FEAT_BEVAL) 1557 if (parent == toolBar && menu->tip != NULL) 1558 { 1559 /* We try to destroy this before the actual menu, because there are 1560 * callbacks, etc. that will be unregistered during the tooltip 1561 * destruction. 1562 * 1563 * If you call "gui_mch_destroy_beval_area()" after destroying 1564 * menu->id, then the tooltip's window will have already been 1565 * deallocated by Xt, and unknown behaviour will ensue (probably 1566 * a core dump). 1567 */ 1568 gui_mch_destroy_beval_area(menu->tip); 1569 menu->tip = NULL; 1570 } 1571 #endif 1572 /* 1573 * This is a hack to stop the Athena simpleMenuWidget from getting a 1574 * BadValue error when a menu's last child is destroyed. We check to 1575 * see if this is the last child and if so, don't delete it. The parent 1576 * will be deleted soon anyway, and it will delete it's children like 1577 * all good widgets do. 1578 */ 1579 /* NOTE: The cause of the BadValue X Protocol Error is because when the 1580 * last child is destroyed, it is first unmanaged, thus causing a 1581 * geometry resize request from the parent Shell widget. 1582 * Since the Shell widget has no more children, it is resized to have 1583 * width/height of 0. XConfigureWindow() is then called with the 1584 * width/height of 0, which generates the BadValue. 1585 * 1586 * This happens in phase two of the widget destruction process. 1587 */ 1588 { 1589 if (parent != menuBar 1590 #ifdef FEAT_TOOLBAR 1591 && parent != toolBar 1592 #endif 1593 ) 1594 { 1595 XtVaGetValues(parent, XtNnumChildren, &num_children, NULL); 1596 if (num_children > 1) 1597 XtDestroyWidget(menu->id); 1598 } 1599 else 1600 XtDestroyWidget(menu->id); 1601 menu->id = (Widget)0; 1602 } 1603 1604 if (parent == menuBar) 1605 { 1606 if (!gui.menu_height_fixed) 1607 gui.menu_height = height + 2 * (space + border); 1608 } 1609 #ifdef FEAT_TOOLBAR 1610 else if (parent == toolBar) 1611 { 1612 /* When removing last toolbar item, don't display the toolbar. */ 1613 XtVaGetValues(toolBar, XtNnumChildren, &num_children, NULL); 1614 if (num_children == 0) 1615 gui_mch_show_toolbar(FALSE); 1616 else 1617 gui.toolbar_height = gui_mch_compute_toolbar_height(); 1618 } 1619 #endif 1620 } 1621 if (menu->submenu_id != (Widget)0) 1622 { 1623 XtDestroyWidget(menu->submenu_id); 1624 menu->submenu_id = (Widget)0; 1625 } 1626 } 1627 1628 static void 1629 gui_athena_menu_timeout(client_data, id) 1630 XtPointer client_data; 1631 XtIntervalId *id UNUSED; 1632 { 1633 Widget w = (Widget)client_data; 1634 Widget popup; 1635 1636 timer = 0; 1637 if (XtIsSubclass(w,smeBSBObjectClass)) 1638 { 1639 Pixmap p; 1640 1641 XtVaGetValues(w, XtNrightBitmap, &p, NULL); 1642 if ((p != None) && (p != XtUnspecifiedPixmap)) 1643 { 1644 /* We are dealing with an item that has a submenu */ 1645 popup = get_popup_entry(XtParent(w)); 1646 if (popup == (Widget)0) 1647 return; 1648 XtPopup(popup, XtGrabNonexclusive); 1649 } 1650 } 1651 } 1652 1653 /* This routine is used to calculate the position (in screen coordinates) 1654 * where a submenu should appear relative to the menu entry that popped it 1655 * up. It should appear even with and just slightly to the left of the 1656 * rightmost end of the menu entry that caused the popup. 1657 * 1658 * This is called when XtPopup() is called. 1659 */ 1660 static void 1661 gui_athena_popup_callback(w, client_data, call_data) 1662 Widget w; 1663 XtPointer client_data; 1664 XtPointer call_data UNUSED; 1665 { 1666 /* Assumption: XtIsSubclass(XtParent(w),simpleMenuWidgetClass) */ 1667 vimmenu_T *menu = (vimmenu_T *)client_data; 1668 Dimension width; 1669 Position root_x, root_y; 1670 1671 /* First, popdown any siblings that may have menus popped up */ 1672 { 1673 vimmenu_T *i; 1674 1675 for (i = menu->parent->children; i != NULL; i = i->next) 1676 { 1677 if (i->submenu_id != NULL && XtIsManaged(i->submenu_id)) 1678 XtPopdown(i->submenu_id); 1679 } 1680 } 1681 XtVaGetValues(XtParent(w), 1682 XtNwidth, &width, 1683 NULL); 1684 /* Assumption: XawSimpleMenuGetActiveEntry(XtParent(w)) == menu->id */ 1685 /* i.e. This IS the active entry */ 1686 XtTranslateCoords(menu->id,width - 5, 0, &root_x, &root_y); 1687 XtVaSetValues(w, XtNx, root_x, 1688 XtNy, root_y, 1689 NULL); 1690 } 1691 1692 static void 1693 gui_athena_popdown_submenus_action(w, event, args, nargs) 1694 Widget w; 1695 XEvent *event; 1696 String *args; 1697 Cardinal *nargs; 1698 { 1699 WidgetList children; 1700 Cardinal num_children; 1701 1702 XtVaGetValues(w, XtNchildren, &children, 1703 XtNnumChildren, &num_children, 1704 NULL); 1705 for (; num_children > 0; --num_children) 1706 { 1707 Widget child = children[num_children - 1]; 1708 1709 if (has_submenu(child)) 1710 { 1711 Widget temp_w; 1712 1713 temp_w = submenu_widget(child); 1714 gui_athena_popdown_submenus_action(temp_w,event,args,nargs); 1715 XtPopdown(temp_w); 1716 } 1717 } 1718 } 1719 1720 /* Used to determine if the given widget has a submenu that can be popped up. */ 1721 static Boolean 1722 has_submenu(widget) 1723 Widget widget; 1724 { 1725 if ((widget != NULL) && XtIsSubclass(widget,smeBSBObjectClass)) 1726 { 1727 Pixmap p; 1728 1729 XtVaGetValues(widget, XtNrightBitmap, &p, NULL); 1730 if ((p != None) && (p != XtUnspecifiedPixmap)) 1731 return True; 1732 } 1733 return False; 1734 } 1735 1736 static void 1737 gui_athena_delayed_arm_action(w, event, args, nargs) 1738 Widget w; 1739 XEvent *event; 1740 String *args; 1741 Cardinal *nargs; 1742 { 1743 Dimension width, height; 1744 1745 if (event->type != MotionNotify) 1746 return; 1747 1748 XtVaGetValues(w, 1749 XtNwidth, &width, 1750 XtNheight, &height, 1751 NULL); 1752 1753 if (event->xmotion.x >= (int)width || event->xmotion.y >= (int)height) 1754 return; 1755 1756 { 1757 static Widget previous_active_widget = NULL; 1758 Widget current; 1759 1760 current = XawSimpleMenuGetActiveEntry(w); 1761 if (current != previous_active_widget) 1762 { 1763 if (timer) 1764 { 1765 /* If the timeout hasn't been triggered, remove it */ 1766 XtRemoveTimeOut(timer); 1767 } 1768 gui_athena_popdown_submenus_action(w,event,args,nargs); 1769 if (has_submenu(current)) 1770 { 1771 XtAppAddTimeOut(XtWidgetToApplicationContext(w), 600L, 1772 gui_athena_menu_timeout, 1773 (XtPointer)current); 1774 } 1775 previous_active_widget = current; 1776 } 1777 } 1778 } 1779 1780 static Widget 1781 get_popup_entry(w) 1782 Widget w; 1783 { 1784 Widget menuw; 1785 1786 /* Get the active entry for the current menu */ 1787 if ((menuw = XawSimpleMenuGetActiveEntry(w)) == (Widget)0) 1788 return NULL; 1789 1790 return submenu_widget(menuw); 1791 } 1792 1793 /* Given the widget that has been determined to have a submenu, return the submenu widget 1794 * that is to be popped up. 1795 */ 1796 static Widget 1797 submenu_widget(widget) 1798 Widget widget; 1799 { 1800 /* Precondition: has_submenu(widget) == True 1801 * XtIsSubclass(XtParent(widget),simpleMenuWidgetClass) == True 1802 */ 1803 1804 char_u *pullright_name; 1805 Widget popup; 1806 1807 pullright_name = make_pull_name((char_u *)XtName(widget)); 1808 popup = XtNameToWidget(XtParent(widget), (char *)pullright_name); 1809 vim_free(pullright_name); 1810 1811 return popup; 1812 /* Postcondition: (popup != NULL) implies 1813 * (XtIsSubclass(popup,simpleMenuWidgetClass) == True) */ 1814 } 1815 1816 void 1817 gui_mch_show_popupmenu(menu) 1818 vimmenu_T *menu; 1819 { 1820 int rootx, rooty, winx, winy; 1821 Window root, child; 1822 unsigned int mask; 1823 1824 if (menu->submenu_id == (Widget)0) 1825 return; 1826 1827 /* Position the popup menu at the pointer */ 1828 if (XQueryPointer(gui.dpy, XtWindow(vimShell), &root, &child, 1829 &rootx, &rooty, &winx, &winy, &mask)) 1830 { 1831 rootx -= 30; 1832 if (rootx < 0) 1833 rootx = 0; 1834 rooty -= 5; 1835 if (rooty < 0) 1836 rooty = 0; 1837 XtVaSetValues(menu->submenu_id, 1838 XtNx, rootx, 1839 XtNy, rooty, 1840 NULL); 1841 } 1842 1843 XtOverrideTranslations(menu->submenu_id, popupTrans); 1844 XtPopupSpringLoaded(menu->submenu_id); 1845 } 1846 1847 #endif /* FEAT_MENU */ 1848 1849 /* 1850 * Set the menu and scrollbar colors to their default values. 1851 */ 1852 void 1853 gui_mch_def_colors() 1854 { 1855 /* 1856 * Get the colors ourselves. Using the automatic conversion doesn't 1857 * handle looking for approximate colors. 1858 */ 1859 if (gui.in_use) 1860 { 1861 gui.menu_fg_pixel = gui_get_color((char_u *)gui.rsrc_menu_fg_name); 1862 gui.menu_bg_pixel = gui_get_color((char_u *)gui.rsrc_menu_bg_name); 1863 gui.scroll_fg_pixel = gui_get_color((char_u *)gui.rsrc_scroll_fg_name); 1864 gui.scroll_bg_pixel = gui_get_color((char_u *)gui.rsrc_scroll_bg_name); 1865 #ifdef FEAT_BEVAL 1866 gui.tooltip_fg_pixel = gui_get_color((char_u *)gui.rsrc_tooltip_fg_name); 1867 gui.tooltip_bg_pixel = gui_get_color((char_u *)gui.rsrc_tooltip_bg_name); 1868 #endif 1869 } 1870 } 1871 1872 1873 /* 1874 * Scrollbar stuff. 1875 */ 1876 1877 void 1878 gui_mch_set_scrollbar_thumb(sb, val, size, max) 1879 scrollbar_T *sb; 1880 long val; 1881 long size; 1882 long max; 1883 { 1884 double v, s; 1885 1886 if (sb->id == (Widget)0) 1887 return; 1888 1889 /* 1890 * Athena scrollbar must go from 0.0 to 1.0. 1891 */ 1892 if (max == 0) 1893 { 1894 /* So you can't scroll it at all (normally it scrolls past end) */ 1895 #ifdef FEAT_GUI_NEXTAW 1896 XawScrollbarSetThumb(sb->id, 0.0, 1.0); 1897 #else 1898 vim_XawScrollbarSetThumb(sb->id, 0.0, 1.0, 0.0); 1899 #endif 1900 } 1901 else 1902 { 1903 v = (double)val / (double)(max + 1); 1904 s = (double)size / (double)(max + 1); 1905 #ifdef FEAT_GUI_NEXTAW 1906 XawScrollbarSetThumb(sb->id, v, s); 1907 #else 1908 vim_XawScrollbarSetThumb(sb->id, v, s, 1.0); 1909 #endif 1910 } 1911 } 1912 1913 void 1914 gui_mch_set_scrollbar_pos(sb, x, y, w, h) 1915 scrollbar_T *sb; 1916 int x; 1917 int y; 1918 int w; 1919 int h; 1920 { 1921 if (sb->id == (Widget)0) 1922 return; 1923 1924 XtUnmanageChild(sb->id); 1925 XtVaSetValues(sb->id, 1926 XtNhorizDistance, x, 1927 XtNvertDistance, y, 1928 XtNwidth, w, 1929 XtNheight, h, 1930 NULL); 1931 XtManageChild(sb->id); 1932 } 1933 1934 void 1935 gui_mch_enable_scrollbar(sb, flag) 1936 scrollbar_T *sb; 1937 int flag; 1938 { 1939 if (sb->id != (Widget)0) 1940 { 1941 if (flag) 1942 XtManageChild(sb->id); 1943 else 1944 XtUnmanageChild(sb->id); 1945 } 1946 } 1947 1948 void 1949 gui_mch_create_scrollbar(sb, orient) 1950 scrollbar_T *sb; 1951 int orient; /* SBAR_VERT or SBAR_HORIZ */ 1952 { 1953 sb->id = XtVaCreateWidget("scrollBar", 1954 #ifdef FEAT_GUI_NEXTAW 1955 scrollbarWidgetClass, vimForm, 1956 #else 1957 vim_scrollbarWidgetClass, vimForm, 1958 #endif 1959 XtNresizable, True, 1960 XtNtop, XtChainTop, 1961 XtNbottom, XtChainTop, 1962 XtNleft, XtChainLeft, 1963 XtNright, XtChainLeft, 1964 XtNborderWidth, 0, 1965 XtNorientation, (orient == SBAR_VERT) ? XtorientVertical 1966 : XtorientHorizontal, 1967 XtNforeground, gui.scroll_fg_pixel, 1968 XtNbackground, gui.scroll_bg_pixel, 1969 NULL); 1970 if (sb->id == (Widget)0) 1971 return; 1972 1973 XtAddCallback(sb->id, XtNjumpProc, 1974 gui_athena_scroll_cb_jump, (XtPointer)sb->ident); 1975 XtAddCallback(sb->id, XtNscrollProc, 1976 gui_athena_scroll_cb_scroll, (XtPointer)sb->ident); 1977 1978 #ifdef FEAT_GUI_NEXTAW 1979 XawScrollbarSetThumb(sb->id, 0.0, 1.0); 1980 #else 1981 vim_XawScrollbarSetThumb(sb->id, 0.0, 1.0, 0.0); 1982 #endif 1983 } 1984 1985 #if defined(FEAT_WINDOWS) || defined(PROTO) 1986 void 1987 gui_mch_destroy_scrollbar(sb) 1988 scrollbar_T *sb; 1989 { 1990 if (sb->id != (Widget)0) 1991 XtDestroyWidget(sb->id); 1992 } 1993 #endif 1994 1995 void 1996 gui_mch_set_scrollbar_colors(sb) 1997 scrollbar_T *sb; 1998 { 1999 if (sb->id != (Widget)0) 2000 XtVaSetValues(sb->id, 2001 XtNforeground, gui.scroll_fg_pixel, 2002 XtNbackground, gui.scroll_bg_pixel, 2003 NULL); 2004 2005 /* This is needed for the rectangle below the vertical scrollbars. */ 2006 if (sb == &gui.bottom_sbar && vimForm != (Widget)0) 2007 gui_athena_scroll_colors(vimForm); 2008 } 2009 2010 /* 2011 * Miscellaneous stuff: 2012 */ 2013 Window 2014 gui_x11_get_wid() 2015 { 2016 return XtWindow(textArea); 2017 } 2018 2019 #if defined(FEAT_BROWSE) || defined(PROTO) 2020 /* 2021 * Put up a file requester. 2022 * Returns the selected name in allocated memory, or NULL for Cancel. 2023 */ 2024 char_u * 2025 gui_mch_browse(saving, title, dflt, ext, initdir, filter) 2026 int saving UNUSED; /* select file to write */ 2027 char_u *title; /* title for the window */ 2028 char_u *dflt; /* default name */ 2029 char_u *ext UNUSED; /* extension added */ 2030 char_u *initdir; /* initial directory, NULL for current dir */ 2031 char_u *filter UNUSED; /* file name filter */ 2032 { 2033 Position x, y; 2034 char_u dirbuf[MAXPATHL]; 2035 2036 /* Concatenate "initdir" and "dflt". */ 2037 if (initdir == NULL || *initdir == NUL) 2038 mch_dirname(dirbuf, MAXPATHL); 2039 else if (STRLEN(initdir) + 2 < MAXPATHL) 2040 STRCPY(dirbuf, initdir); 2041 else 2042 dirbuf[0] = NUL; 2043 if (dflt != NULL && *dflt != NUL 2044 && STRLEN(dirbuf) + 2 + STRLEN(dflt) < MAXPATHL) 2045 { 2046 add_pathsep(dirbuf); 2047 STRCAT(dirbuf, dflt); 2048 } 2049 2050 /* Position the file selector just below the menubar */ 2051 XtTranslateCoords(vimShell, (Position)0, (Position) 2052 #ifdef FEAT_MENU 2053 gui.menu_height 2054 #else 2055 0 2056 #endif 2057 , &x, &y); 2058 return (char_u *)vim_SelFile(vimShell, (char *)title, (char *)dirbuf, 2059 NULL, (int)x, (int)y, gui.menu_fg_pixel, gui.menu_bg_pixel, 2060 gui.scroll_fg_pixel, gui.scroll_bg_pixel); 2061 } 2062 #endif 2063 2064 #if defined(FEAT_GUI_DIALOG) || defined(PROTO) 2065 2066 static int dialogStatus; 2067 static Atom dialogatom; 2068 2069 static void keyhit_callback __ARGS((Widget w, XtPointer client_data, XEvent *event, Boolean *cont)); 2070 static void butproc __ARGS((Widget w, XtPointer client_data, XtPointer call_data)); 2071 static void dialog_wm_handler __ARGS((Widget w, XtPointer client_data, XEvent *event, Boolean *dum)); 2072 2073 /* 2074 * Callback function for the textfield. When CR is hit this works like 2075 * hitting the "OK" button, ESC like "Cancel". 2076 */ 2077 static void 2078 keyhit_callback(w, client_data, event, cont) 2079 Widget w UNUSED; 2080 XtPointer client_data UNUSED; 2081 XEvent *event; 2082 Boolean *cont UNUSED; 2083 { 2084 char buf[2]; 2085 2086 if (XLookupString(&(event->xkey), buf, 2, NULL, NULL) == 1) 2087 { 2088 if (*buf == CAR) 2089 dialogStatus = 1; 2090 else if (*buf == ESC) 2091 dialogStatus = 0; 2092 } 2093 } 2094 2095 static void 2096 butproc(w, client_data, call_data) 2097 Widget w UNUSED; 2098 XtPointer client_data; 2099 XtPointer call_data UNUSED; 2100 { 2101 dialogStatus = (int)(long)client_data + 1; 2102 } 2103 2104 /* 2105 * Function called when dialog window closed. 2106 */ 2107 static void 2108 dialog_wm_handler(w, client_data, event, dum) 2109 Widget w UNUSED; 2110 XtPointer client_data UNUSED; 2111 XEvent *event; 2112 Boolean *dum UNUSED; 2113 { 2114 if (event->type == ClientMessage 2115 && (Atom)((XClientMessageEvent *)event)->data.l[0] == dialogatom) 2116 dialogStatus = 0; 2117 } 2118 2119 int 2120 gui_mch_dialog(type, title, message, buttons, dfltbutton, textfield, ex_cmd) 2121 int type UNUSED; 2122 char_u *title; 2123 char_u *message; 2124 char_u *buttons; 2125 int dfltbutton UNUSED; 2126 char_u *textfield; 2127 int ex_cmd UNUSED; 2128 { 2129 char_u *buts; 2130 char_u *p, *next; 2131 XtAppContext app; 2132 XEvent event; 2133 Position wd, hd; 2134 Position wv, hv; 2135 Position x, y; 2136 Widget dialog; 2137 Widget dialogshell; 2138 Widget dialogmessage; 2139 Widget dialogtextfield = 0; 2140 Widget dialogButton; 2141 Widget prev_dialogButton = NULL; 2142 int butcount; 2143 int vertical; 2144 2145 if (title == NULL) 2146 title = (char_u *)_("Vim dialog"); 2147 dialogStatus = -1; 2148 2149 /* if our pointer is currently hidden, then we should show it. */ 2150 gui_mch_mousehide(FALSE); 2151 2152 /* Check 'v' flag in 'guioptions': vertical button placement. */ 2153 vertical = (vim_strchr(p_go, GO_VERTICAL) != NULL); 2154 2155 /* The shell is created each time, to make sure it is resized properly */ 2156 dialogshell = XtVaCreatePopupShell("dialogShell", 2157 transientShellWidgetClass, vimShell, 2158 XtNtitle, title, 2159 NULL); 2160 if (dialogshell == (Widget)0) 2161 goto error; 2162 2163 dialog = XtVaCreateManagedWidget("dialog", 2164 formWidgetClass, dialogshell, 2165 XtNdefaultDistance, 20, 2166 NULL); 2167 if (dialog == (Widget)0) 2168 goto error; 2169 gui_athena_menu_colors(dialog); 2170 dialogmessage = XtVaCreateManagedWidget("dialogMessage", 2171 labelWidgetClass, dialog, 2172 XtNlabel, message, 2173 XtNtop, XtChainTop, 2174 XtNbottom, XtChainTop, 2175 XtNleft, XtChainLeft, 2176 XtNright, XtChainLeft, 2177 XtNresizable, True, 2178 XtNborderWidth, 0, 2179 NULL); 2180 gui_athena_menu_colors(dialogmessage); 2181 2182 if (textfield != NULL) 2183 { 2184 dialogtextfield = XtVaCreateManagedWidget("textfield", 2185 asciiTextWidgetClass, dialog, 2186 XtNwidth, 400, 2187 XtNtop, XtChainTop, 2188 XtNbottom, XtChainTop, 2189 XtNleft, XtChainLeft, 2190 XtNright, XtChainRight, 2191 XtNfromVert, dialogmessage, 2192 XtNresizable, True, 2193 XtNstring, textfield, 2194 XtNlength, IOSIZE, 2195 XtNuseStringInPlace, True, 2196 XtNeditType, XawtextEdit, 2197 XtNwrap, XawtextWrapNever, 2198 XtNresize, XawtextResizeHeight, 2199 NULL); 2200 XtManageChild(dialogtextfield); 2201 XtAddEventHandler(dialogtextfield, KeyPressMask, False, 2202 (XtEventHandler)keyhit_callback, (XtPointer)NULL); 2203 XawTextSetInsertionPoint(dialogtextfield, 2204 (XawTextPosition)STRLEN(textfield)); 2205 XtSetKeyboardFocus(dialog, dialogtextfield); 2206 } 2207 2208 /* make a copy, so that we can insert NULs */ 2209 buts = vim_strsave(buttons); 2210 if (buts == NULL) 2211 return -1; 2212 2213 p = buts; 2214 for (butcount = 0; *p; ++butcount) 2215 { 2216 for (next = p; *next; ++next) 2217 { 2218 if (*next == DLG_HOTKEY_CHAR) 2219 STRMOVE(next, next + 1); 2220 if (*next == DLG_BUTTON_SEP) 2221 { 2222 *next++ = NUL; 2223 break; 2224 } 2225 } 2226 dialogButton = XtVaCreateManagedWidget("button", 2227 commandWidgetClass, dialog, 2228 XtNlabel, p, 2229 XtNtop, XtChainBottom, 2230 XtNbottom, XtChainBottom, 2231 XtNleft, XtChainLeft, 2232 XtNright, XtChainLeft, 2233 XtNfromVert, textfield == NULL ? dialogmessage : dialogtextfield, 2234 XtNvertDistance, vertical ? 4 : 20, 2235 XtNresizable, False, 2236 NULL); 2237 gui_athena_menu_colors(dialogButton); 2238 if (butcount > 0) 2239 XtVaSetValues(dialogButton, 2240 vertical ? XtNfromVert : XtNfromHoriz, prev_dialogButton, 2241 NULL); 2242 2243 XtAddCallback(dialogButton, XtNcallback, butproc, (XtPointer)(long_u)butcount); 2244 p = next; 2245 prev_dialogButton = dialogButton; 2246 } 2247 vim_free(buts); 2248 2249 XtRealizeWidget(dialogshell); 2250 2251 /* Setup for catching the close-window event, don't let it close Vim! */ 2252 dialogatom = XInternAtom(gui.dpy, "WM_DELETE_WINDOW", False); 2253 XSetWMProtocols(gui.dpy, XtWindow(dialogshell), &dialogatom, 1); 2254 XtAddEventHandler(dialogshell, NoEventMask, True, dialog_wm_handler, NULL); 2255 2256 XtVaGetValues(dialogshell, 2257 XtNwidth, &wd, 2258 XtNheight, &hd, 2259 NULL); 2260 XtVaGetValues(vimShell, 2261 XtNwidth, &wv, 2262 XtNheight, &hv, 2263 NULL); 2264 XtTranslateCoords(vimShell, 2265 (Position)((wv - wd) / 2), 2266 (Position)((hv - hd) / 2), 2267 &x, &y); 2268 if (x < 0) 2269 x = 0; 2270 if (y < 0) 2271 y = 0; 2272 XtVaSetValues(dialogshell, XtNx, x, XtNy, y, NULL); 2273 2274 /* Position the mouse pointer in the dialog, required for when focus 2275 * follows mouse. */ 2276 XWarpPointer(gui.dpy, (Window)0, XtWindow(dialogshell), 0, 0, 0, 0, 20, 40); 2277 2278 2279 app = XtWidgetToApplicationContext(dialogshell); 2280 2281 XtPopup(dialogshell, XtGrabNonexclusive); 2282 2283 for (;;) 2284 { 2285 XtAppNextEvent(app, &event); 2286 XtDispatchEvent(&event); 2287 if (dialogStatus >= 0) 2288 break; 2289 } 2290 2291 XtPopdown(dialogshell); 2292 2293 if (textfield != NULL && dialogStatus < 0) 2294 *textfield = NUL; 2295 2296 error: 2297 XtDestroyWidget(dialogshell); 2298 2299 return dialogStatus; 2300 } 2301 #endif 2302 2303 #if defined(FEAT_GUI_DIALOG) || defined(FEAT_MENU) 2304 /* 2305 * Set the colors of Widget "id" to the menu colors. 2306 */ 2307 static void 2308 gui_athena_menu_colors(id) 2309 Widget id; 2310 { 2311 if (gui.menu_bg_pixel != INVALCOLOR) 2312 XtVaSetValues(id, XtNbackground, gui.menu_bg_pixel, NULL); 2313 if (gui.menu_fg_pixel != INVALCOLOR) 2314 XtVaSetValues(id, XtNforeground, gui.menu_fg_pixel, NULL); 2315 } 2316 #endif 2317 2318 /* 2319 * Set the colors of Widget "id" to the scroll colors. 2320 */ 2321 static void 2322 gui_athena_scroll_colors(id) 2323 Widget id; 2324 { 2325 if (gui.scroll_bg_pixel != INVALCOLOR) 2326 XtVaSetValues(id, XtNbackground, gui.scroll_bg_pixel, NULL); 2327 if (gui.scroll_fg_pixel != INVALCOLOR) 2328 XtVaSetValues(id, XtNforeground, gui.scroll_fg_pixel, NULL); 2329 } 2330