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 42extern Widget vimShell; 43 44static Widget vimForm = (Widget)0; 45Widget textArea = (Widget)0; 46#ifdef FEAT_MENU 47static Widget menuBar = (Widget)0; 48static XtIntervalId timer = 0; /* 0 = expired, otherwise active */ 49 50/* Used to figure out menu ordering */ 51static vimmenu_T *a_cur_menu = NULL; 52static Cardinal athena_calculate_ins_pos __ARGS((Widget)); 53 54static Pixmap gui_athena_create_pullright_pixmap __ARGS((Widget)); 55static void gui_athena_menu_timeout __ARGS((XtPointer, XtIntervalId *)); 56static void gui_athena_popup_callback __ARGS((Widget, XtPointer, XtPointer)); 57static void gui_athena_delayed_arm_action __ARGS((Widget, XEvent *, String *, 58 Cardinal *)); 59static void gui_athena_popdown_submenus_action __ARGS((Widget, XEvent *, 60 String *, Cardinal *)); 61static 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 68static void gui_mch_reset_focus __ARGS((void)); 69static Widget toolBar = (Widget)0; 70#endif 71 72static void gui_athena_scroll_cb_jump __ARGS((Widget, XtPointer, XtPointer)); 73static void gui_athena_scroll_cb_scroll __ARGS((Widget, XtPointer, XtPointer)); 74#if defined(FEAT_GUI_DIALOG) || defined(FEAT_MENU) 75static void gui_athena_menu_colors __ARGS((Widget id)); 76#endif 77static void gui_athena_scroll_colors __ARGS((Widget id)); 78 79#ifdef FEAT_MENU 80static XtTranslations popupTrans, parentTrans, menuTrans, supermenuTrans; 81static Pixmap pullerBitmap = None; 82static 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 90gui_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 125gui_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 231gui_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 328gui_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 428gui_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 445static void createXpmImages __ARGS((char_u *path, char **xpm, Pixmap *sen)); 446static 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 453get_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 495createXpmImages(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 subsititution 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 569gui_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 598gui_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 625gui_mch_reset_focus() 626{ 627 XtSetKeyboardFocus(vimForm, textArea); 628} 629#endif 630 631 632 void 633gui_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 646static char_u *make_pull_name __ARGS((char_u * name)); 647static Widget get_popup_entry __ARGS((Widget w)); 648static Widget submenu_widget __ARGS((Widget)); 649static Boolean has_submenu __ARGS((Widget)); 650static void gui_mch_submenu_change __ARGS((vimmenu_T *mp, int colors)); 651static void gui_athena_menu_font __ARGS((Widget id)); 652static Boolean gui_athena_menu_has_submenus __ARGS((Widget, Widget)); 653 654 void 655gui_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 688gui_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 720athena_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 767gui_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 887gui_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 909gui_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 957gui_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 1034gui_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 1049gui_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 1065gui_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 * 1144make_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 1161gui_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 1283gui_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 1407gui_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 1442gui_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 1458gui_mch_toggle_tearoffs(enable) 1459 int enable UNUSED; 1460{ 1461 /* no tearoff menus */ 1462} 1463 1464 void 1465gui_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 1483gui_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 1629gui_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 1661gui_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 1693gui_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 1722has_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 1737gui_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 1781get_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 1797submenu_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 1817gui_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 1853gui_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 1878gui_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 1914gui_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 1935gui_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 1949gui_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 1987gui_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 1996gui_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 2014gui_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 * 2025gui_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 2066static int dialogStatus; 2067static Atom dialogatom; 2068 2069static void keyhit_callback __ARGS((Widget w, XtPointer client_data, XEvent *event, Boolean *cont)); 2070static void butproc __ARGS((Widget w, XtPointer client_data, XtPointer call_data)); 2071static 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 2078keyhit_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 2096butproc(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 2108dialog_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 2120gui_mch_dialog(type, title, message, buttons, dfltbutton, textfield) 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{ 2128 char_u *buts; 2129 char_u *p, *next; 2130 XtAppContext app; 2131 XEvent event; 2132 Position wd, hd; 2133 Position wv, hv; 2134 Position x, y; 2135 Widget dialog; 2136 Widget dialogshell; 2137 Widget dialogmessage; 2138 Widget dialogtextfield = 0; 2139 Widget dialogButton; 2140 Widget prev_dialogButton = NULL; 2141 int butcount; 2142 int vertical; 2143 2144 if (title == NULL) 2145 title = (char_u *)_("Vim dialog"); 2146 dialogStatus = -1; 2147 2148 /* if our pointer is currently hidden, then we should show it. */ 2149 gui_mch_mousehide(FALSE); 2150 2151 /* Check 'v' flag in 'guioptions': vertical button placement. */ 2152 vertical = (vim_strchr(p_go, GO_VERTICAL) != NULL); 2153 2154 /* The shell is created each time, to make sure it is resized properly */ 2155 dialogshell = XtVaCreatePopupShell("dialogShell", 2156 transientShellWidgetClass, vimShell, 2157 XtNtitle, title, 2158 NULL); 2159 if (dialogshell == (Widget)0) 2160 goto error; 2161 2162 dialog = XtVaCreateManagedWidget("dialog", 2163 formWidgetClass, dialogshell, 2164 XtNdefaultDistance, 20, 2165 NULL); 2166 if (dialog == (Widget)0) 2167 goto error; 2168 gui_athena_menu_colors(dialog); 2169 dialogmessage = XtVaCreateManagedWidget("dialogMessage", 2170 labelWidgetClass, dialog, 2171 XtNlabel, message, 2172 XtNtop, XtChainTop, 2173 XtNbottom, XtChainTop, 2174 XtNleft, XtChainLeft, 2175 XtNright, XtChainLeft, 2176 XtNresizable, True, 2177 XtNborderWidth, 0, 2178 NULL); 2179 gui_athena_menu_colors(dialogmessage); 2180 2181 if (textfield != NULL) 2182 { 2183 dialogtextfield = XtVaCreateManagedWidget("textfield", 2184 asciiTextWidgetClass, dialog, 2185 XtNwidth, 400, 2186 XtNtop, XtChainTop, 2187 XtNbottom, XtChainTop, 2188 XtNleft, XtChainLeft, 2189 XtNright, XtChainRight, 2190 XtNfromVert, dialogmessage, 2191 XtNresizable, True, 2192 XtNstring, textfield, 2193 XtNlength, IOSIZE, 2194 XtNuseStringInPlace, True, 2195 XtNeditType, XawtextEdit, 2196 XtNwrap, XawtextWrapNever, 2197 XtNresize, XawtextResizeHeight, 2198 NULL); 2199 XtManageChild(dialogtextfield); 2200 XtAddEventHandler(dialogtextfield, KeyPressMask, False, 2201 (XtEventHandler)keyhit_callback, (XtPointer)NULL); 2202 XawTextSetInsertionPoint(dialogtextfield, 2203 (XawTextPosition)STRLEN(textfield)); 2204 XtSetKeyboardFocus(dialog, dialogtextfield); 2205 } 2206 2207 /* make a copy, so that we can insert NULs */ 2208 buts = vim_strsave(buttons); 2209 if (buts == NULL) 2210 return -1; 2211 2212 p = buts; 2213 for (butcount = 0; *p; ++butcount) 2214 { 2215 for (next = p; *next; ++next) 2216 { 2217 if (*next == DLG_HOTKEY_CHAR) 2218 STRMOVE(next, next + 1); 2219 if (*next == DLG_BUTTON_SEP) 2220 { 2221 *next++ = NUL; 2222 break; 2223 } 2224 } 2225 dialogButton = XtVaCreateManagedWidget("button", 2226 commandWidgetClass, dialog, 2227 XtNlabel, p, 2228 XtNtop, XtChainBottom, 2229 XtNbottom, XtChainBottom, 2230 XtNleft, XtChainLeft, 2231 XtNright, XtChainLeft, 2232 XtNfromVert, textfield == NULL ? dialogmessage : dialogtextfield, 2233 XtNvertDistance, vertical ? 4 : 20, 2234 XtNresizable, False, 2235 NULL); 2236 gui_athena_menu_colors(dialogButton); 2237 if (butcount > 0) 2238 XtVaSetValues(dialogButton, 2239 vertical ? XtNfromVert : XtNfromHoriz, prev_dialogButton, 2240 NULL); 2241 2242 XtAddCallback(dialogButton, XtNcallback, butproc, (XtPointer)(long_u)butcount); 2243 p = next; 2244 prev_dialogButton = dialogButton; 2245 } 2246 vim_free(buts); 2247 2248 XtRealizeWidget(dialogshell); 2249 2250 /* Setup for catching the close-window event, don't let it close Vim! */ 2251 dialogatom = XInternAtom(gui.dpy, "WM_DELETE_WINDOW", False); 2252 XSetWMProtocols(gui.dpy, XtWindow(dialogshell), &dialogatom, 1); 2253 XtAddEventHandler(dialogshell, NoEventMask, True, dialog_wm_handler, NULL); 2254 2255 XtVaGetValues(dialogshell, 2256 XtNwidth, &wd, 2257 XtNheight, &hd, 2258 NULL); 2259 XtVaGetValues(vimShell, 2260 XtNwidth, &wv, 2261 XtNheight, &hv, 2262 NULL); 2263 XtTranslateCoords(vimShell, 2264 (Position)((wv - wd) / 2), 2265 (Position)((hv - hd) / 2), 2266 &x, &y); 2267 if (x < 0) 2268 x = 0; 2269 if (y < 0) 2270 y = 0; 2271 XtVaSetValues(dialogshell, XtNx, x, XtNy, y, NULL); 2272 2273 /* Position the mouse pointer in the dialog, required for when focus 2274 * follows mouse. */ 2275 XWarpPointer(gui.dpy, (Window)0, XtWindow(dialogshell), 0, 0, 0, 0, 20, 40); 2276 2277 2278 app = XtWidgetToApplicationContext(dialogshell); 2279 2280 XtPopup(dialogshell, XtGrabNonexclusive); 2281 2282 for (;;) 2283 { 2284 XtAppNextEvent(app, &event); 2285 XtDispatchEvent(&event); 2286 if (dialogStatus >= 0) 2287 break; 2288 } 2289 2290 XtPopdown(dialogshell); 2291 2292 if (textfield != NULL && dialogStatus < 0) 2293 *textfield = NUL; 2294 2295error: 2296 XtDestroyWidget(dialogshell); 2297 2298 return dialogStatus; 2299} 2300#endif 2301 2302#if defined(FEAT_GUI_DIALOG) || defined(FEAT_MENU) 2303/* 2304 * Set the colors of Widget "id" to the menu colors. 2305 */ 2306 static void 2307gui_athena_menu_colors(id) 2308 Widget id; 2309{ 2310 if (gui.menu_bg_pixel != INVALCOLOR) 2311 XtVaSetValues(id, XtNbackground, gui.menu_bg_pixel, NULL); 2312 if (gui.menu_fg_pixel != INVALCOLOR) 2313 XtVaSetValues(id, XtNforeground, gui.menu_fg_pixel, NULL); 2314} 2315#endif 2316 2317/* 2318 * Set the colors of Widget "id" to the scroll colors. 2319 */ 2320 static void 2321gui_athena_scroll_colors(id) 2322 Widget id; 2323{ 2324 if (gui.scroll_bg_pixel != INVALCOLOR) 2325 XtVaSetValues(id, XtNbackground, gui.scroll_bg_pixel, NULL); 2326 if (gui.scroll_fg_pixel != INVALCOLOR) 2327 XtVaSetValues(id, XtNforeground, gui.scroll_fg_pixel, NULL); 2328} 2329