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 * 6 * Do ":help uganda" in Vim to read copying and usage conditions. 7 * Do ":help credits" in Vim to see a list of people who contributed. 8 * See README.txt for an overview of the Vim source code. 9 */ 10 11 #include <Xm/Form.h> 12 #include <Xm/RowColumn.h> 13 #include <Xm/PushB.h> 14 #include <Xm/Text.h> 15 #include <Xm/TextF.h> 16 #include <Xm/Separator.h> 17 #include <Xm/Label.h> 18 #include <Xm/CascadeB.h> 19 #include <Xm/ScrollBar.h> 20 #include <Xm/MenuShell.h> 21 #include <Xm/DrawingA.h> 22 #if (XmVersion >= 1002) 23 # include <Xm/RepType.h> 24 #endif 25 #include <Xm/Frame.h> 26 #include <Xm/LabelG.h> 27 #include <Xm/ToggleBG.h> 28 #include <Xm/SeparatoG.h> 29 #include <Xm/XmP.h> 30 31 #include <X11/keysym.h> 32 #include <X11/Xatom.h> 33 #include <X11/StringDefs.h> 34 #include <X11/Intrinsic.h> 35 36 #include "vim.h" 37 38 #ifdef HAVE_X11_XPM_H 39 # include <X11/xpm.h> 40 #else 41 # ifdef HAVE_XM_XPMP_H 42 # include <Xm/XpmP.h> 43 # endif 44 #endif 45 #ifdef HAVE_XM_NOTEBOOK_H 46 # include <Xm/Notebook.h> 47 #endif 48 49 #include "gui_xmebw.h" /* for our Enhanced Button Widget */ 50 51 #if defined(FEAT_GUI_DIALOG) && defined(HAVE_XPM) 52 # include "../pixmaps/alert.xpm" 53 # include "../pixmaps/error.xpm" 54 # include "../pixmaps/generic.xpm" 55 # include "../pixmaps/info.xpm" 56 # include "../pixmaps/quest.xpm" 57 #endif 58 59 #define MOTIF_POPUP 60 61 extern Widget vimShell; 62 63 static Widget vimForm; 64 static Widget textAreaForm; 65 Widget textArea; 66 #ifdef FEAT_TOOLBAR 67 static Widget toolBarFrame; 68 static Widget toolBar; 69 #endif 70 #ifdef FEAT_GUI_TABLINE 71 static Widget tabLine; 72 static Widget tabLine_menu = 0; 73 static int showing_tabline = 0; 74 #endif 75 #ifdef FEAT_FOOTER 76 static Widget footer; 77 #endif 78 #ifdef FEAT_MENU 79 # if (XmVersion >= 1002) 80 /* remember the last set value for the tearoff item */ 81 static int tearoff_val = (int)XmTEAR_OFF_ENABLED; 82 # endif 83 static Widget menuBar; 84 #endif 85 86 static void scroll_cb __ARGS((Widget w, XtPointer client_data, XtPointer call_data)); 87 #ifdef FEAT_GUI_TABLINE 88 static void tabline_cb __ARGS((Widget w, XtPointer client_data, XtPointer call_data)); 89 static void tabline_button_cb __ARGS((Widget w, XtPointer client_data, XtPointer call_data)); 90 static void tabline_menu_cb __ARGS((Widget w, XtPointer closure, XEvent *e, Boolean *continue_dispatch)); 91 static void tabline_balloon_cb __ARGS((BalloonEval *beval, int state)); 92 #endif 93 #ifdef FEAT_TOOLBAR 94 # ifdef FEAT_FOOTER 95 static void toolbarbutton_enter_cb __ARGS((Widget, XtPointer, XEvent *, Boolean *)); 96 static void toolbarbutton_leave_cb __ARGS((Widget, XtPointer, XEvent *, Boolean *)); 97 # endif 98 static void reset_focus __ARGS((void)); 99 #endif 100 #ifdef FEAT_FOOTER 101 static int gui_mch_compute_footer_height __ARGS((void)); 102 #endif 103 #ifdef WSDEBUG 104 static void attachDump(Widget, char *); 105 #endif 106 107 static void gui_motif_menu_colors __ARGS((Widget id)); 108 static void gui_motif_scroll_colors __ARGS((Widget id)); 109 110 #if (XmVersion >= 1002) 111 # define STRING_TAG XmFONTLIST_DEFAULT_TAG 112 #else 113 # define STRING_TAG XmSTRING_DEFAULT_CHARSET 114 #endif 115 116 /* 117 * Call-back routines. 118 */ 119 120 static void 121 scroll_cb(w, client_data, call_data) 122 Widget w UNUSED; 123 XtPointer client_data, call_data; 124 { 125 scrollbar_T *sb; 126 long value; 127 int dragging; 128 129 sb = gui_find_scrollbar((long)client_data); 130 131 value = ((XmScrollBarCallbackStruct *)call_data)->value; 132 dragging = (((XmScrollBarCallbackStruct *)call_data)->reason == 133 (int)XmCR_DRAG); 134 gui_drag_scrollbar(sb, value, dragging); 135 } 136 137 #ifdef FEAT_GUI_TABLINE 138 static void 139 tabline_cb(w, client_data, call_data) 140 Widget w UNUSED; 141 XtPointer client_data UNUSED; 142 XtPointer call_data; 143 { 144 XmNotebookCallbackStruct *nptr; 145 146 nptr = (XmNotebookCallbackStruct *)call_data; 147 if (nptr->reason != (int)XmCR_NONE) 148 send_tabline_event(nptr->page_number); 149 } 150 151 static void 152 tabline_button_cb(w, client_data, call_data) 153 Widget w; 154 XtPointer client_data UNUSED; 155 XtPointer call_data UNUSED; 156 { 157 int cmd, tab_idx; 158 159 XtVaGetValues(w, XmNuserData, &cmd, NULL); 160 XtVaGetValues(tabLine_menu, XmNuserData, &tab_idx, NULL); 161 162 send_tabline_menu_event(tab_idx, cmd); 163 } 164 165 /* 166 * Tabline single mouse click timeout handler 167 */ 168 static void 169 motif_tabline_timer_cb (timed_out, interval_id) 170 XtPointer timed_out; 171 XtIntervalId *interval_id UNUSED; 172 { 173 *((int *)timed_out) = TRUE; 174 } 175 176 /* 177 * check if the tabline tab scroller is clicked 178 */ 179 static int 180 tabline_scroller_clicked(scroller_name, event) 181 char *scroller_name; 182 XButtonPressedEvent *event; 183 { 184 Widget tab_scroll_w; 185 Position pos_x, pos_y; 186 Dimension width, height; 187 188 tab_scroll_w = XtNameToWidget(tabLine, scroller_name); 189 if (tab_scroll_w != (Widget)0) { 190 XtVaGetValues(tab_scroll_w, XmNx, &pos_x, XmNy, &pos_y, XmNwidth, 191 &width, XmNheight, &height, NULL); 192 if (pos_x >= 0) { 193 /* Tab scroller (next) is visible */ 194 if ((event->x >= pos_x) && (event->x <= pos_x + width) && 195 (event->y >= pos_y) && (event->y <= pos_y + height)) { 196 /* Clicked on the scroller */ 197 return TRUE; 198 } 199 } 200 } 201 return FALSE; 202 } 203 204 static void 205 tabline_menu_cb(w, closure, e, continue_dispatch) 206 Widget w; 207 XtPointer closure UNUSED; 208 XEvent *e; 209 Boolean *continue_dispatch UNUSED; 210 { 211 Widget tab_w; 212 XButtonPressedEvent *event; 213 int tab_idx = 0; 214 WidgetList children; 215 Cardinal numChildren; 216 static XtIntervalId timer = (XtIntervalId)0; 217 static int timed_out = TRUE; 218 219 event = (XButtonPressedEvent *)e; 220 221 if (event->button == Button1) 222 { 223 if (tabline_scroller_clicked("MajorTabScrollerNext", event) 224 || tabline_scroller_clicked("MajorTabScrollerPrevious", event)) 225 return; 226 227 if (!timed_out) 228 { 229 XtRemoveTimeOut(timer); 230 timed_out = TRUE; 231 232 /* 233 * Double click on the tabline gutter, add a new tab 234 */ 235 send_tabline_menu_event(0, TABLINE_MENU_NEW); 236 } 237 else 238 { 239 /* 240 * Single click on the tabline gutter, start a timer to check 241 * for double clicks 242 */ 243 timer = XtAppAddTimeOut(app_context, (long_u)p_mouset, 244 motif_tabline_timer_cb, &timed_out); 245 timed_out = FALSE; 246 } 247 return; 248 } 249 250 if (event->button != Button3) 251 return; 252 253 /* When ignoring events don't show the menu. */ 254 if (hold_gui_events 255 # ifdef FEAT_CMDWIN 256 || cmdwin_type != 0 257 # endif 258 ) 259 return; 260 261 if (event->subwindow != None) 262 { 263 tab_w = XtWindowToWidget(XtDisplay(w), event->subwindow); 264 /* LINTED: avoid warning: dubious operation on enum */ 265 if (tab_w != (Widget)0 && XmIsPushButton(tab_w)) 266 XtVaGetValues(tab_w, XmNpageNumber, &tab_idx, NULL); 267 } 268 269 XtVaSetValues(tabLine_menu, XmNuserData, tab_idx, NULL); 270 XtVaGetValues(tabLine_menu, XmNchildren, &children, XmNnumChildren, 271 &numChildren, NULL); 272 XtManageChildren(children, numChildren); 273 XmMenuPosition(tabLine_menu, (XButtonPressedEvent *)e) ; 274 XtManageChild(tabLine_menu); 275 } 276 277 static void 278 tabline_balloon_cb(beval, state) 279 BalloonEval *beval; 280 int state UNUSED; 281 { 282 int nr; 283 tabpage_T *tp; 284 285 if (beval->target == (Widget)0) 286 return; 287 288 XtVaGetValues(beval->target, XmNpageNumber, &nr, NULL); 289 tp = find_tabpage(nr); 290 if (tp == NULL) 291 return; 292 293 get_tabline_label(tp, TRUE); 294 gui_mch_post_balloon(beval, NameBuff); 295 } 296 297 #endif 298 299 /* 300 * End of call-back routines 301 */ 302 303 /* 304 * Implement three dimensional shading of insensitive labels. 305 * By Marcin Dalecki. 306 */ 307 308 #include <Xm/XmP.h> 309 #include <Xm/LabelP.h> 310 311 static XtExposeProc old_label_expose = NULL; 312 313 static void label_expose __ARGS((Widget _w, XEvent *_event, Region _region)); 314 315 static void 316 label_expose(_w, _event, _region) 317 Widget _w; 318 XEvent *_event; 319 Region _region; 320 { 321 GC insensitiveGC; 322 XmLabelWidget lw = (XmLabelWidget)_w; 323 unsigned char label_type = (int)XmSTRING; 324 325 XtVaGetValues(_w, XmNlabelType, &label_type, (XtPointer)0); 326 327 if (XtIsSensitive(_w) || label_type != (int)XmSTRING) 328 (*old_label_expose)(_w, _event, _region); 329 else 330 { 331 XGCValues values; 332 XtGCMask mask; 333 XtGCMask dynamic; 334 XFontStruct *fs; 335 336 _XmFontListGetDefaultFont(lw->label.font, &fs); 337 338 /* FIXME: we should be doing the whole drawing ourself here. */ 339 insensitiveGC = lw->label.insensitive_GC; 340 341 mask = GCForeground | GCBackground | GCGraphicsExposures; 342 dynamic = GCClipMask | GCClipXOrigin | GCClipYOrigin; 343 values.graphics_exposures = False; 344 345 if (fs != 0) 346 { 347 mask |= GCFont; 348 values.font = fs->fid; 349 } 350 351 if (lw->primitive.top_shadow_pixmap != None 352 && lw->primitive.top_shadow_pixmap != XmUNSPECIFIED_PIXMAP) 353 { 354 mask |= GCFillStyle | GCTile; 355 values.fill_style = FillTiled; 356 values.tile = lw->primitive.top_shadow_pixmap; 357 } 358 359 lw->label.TextRect.x += 1; 360 lw->label.TextRect.y += 1; 361 if (lw->label._acc_text != 0) 362 { 363 lw->label.acc_TextRect.x += 1; 364 lw->label.acc_TextRect.y += 1; 365 } 366 367 values.foreground = lw->primitive.top_shadow_color; 368 values.background = lw->core.background_pixel; 369 370 lw->label.insensitive_GC = XtAllocateGC((Widget)lw, 0, mask, 371 &values, dynamic, (XtGCMask)0); 372 (*old_label_expose)(_w, _event, _region); 373 XtReleaseGC(_w, lw->label.insensitive_GC); 374 375 lw->label.TextRect.x -= 1; 376 lw->label.TextRect.y -= 1; 377 if (lw->label._acc_text != 0) 378 { 379 lw->label.acc_TextRect.x -= 1; 380 lw->label.acc_TextRect.y -= 1; 381 } 382 383 values.foreground = lw->primitive.bottom_shadow_color; 384 values.background = lw->core.background_pixel; 385 386 lw->label.insensitive_GC = XtAllocateGC((Widget) lw, 0, mask, 387 &values, dynamic, (XtGCMask)0); 388 (*old_label_expose)(_w, _event, _region); 389 XtReleaseGC(_w, lw->label.insensitive_GC); 390 391 lw->label.insensitive_GC = insensitiveGC; 392 } 393 } 394 395 /* 396 * Create all the motif widgets necessary. 397 */ 398 void 399 gui_x11_create_widgets() 400 { 401 #ifdef FEAT_GUI_TABLINE 402 Widget button, scroller; 403 Arg args[10]; 404 int n; 405 XmString xms; 406 #endif 407 408 /* 409 * Install the 3D shade effect drawing routines. 410 */ 411 if (old_label_expose == NULL) 412 { 413 old_label_expose = xmLabelWidgetClass->core_class.expose; 414 xmLabelWidgetClass->core_class.expose = label_expose; 415 } 416 417 /* 418 * Start out by adding the configured border width into the border offset 419 */ 420 gui.border_offset = gui.border_width; 421 422 /* 423 * Install the tearOffModel resource converter. 424 */ 425 #if (XmVersion >= 1002) 426 XmRepTypeInstallTearOffModelConverter(); 427 #endif 428 429 /* Make sure the "Quit" menu entry of the window manager is ignored */ 430 XtVaSetValues(vimShell, XmNdeleteResponse, XmDO_NOTHING, NULL); 431 432 vimForm = XtVaCreateManagedWidget("vimForm", 433 xmFormWidgetClass, vimShell, 434 XmNborderWidth, 0, 435 XmNhighlightThickness, 0, 436 XmNshadowThickness, 0, 437 XmNmarginWidth, 0, 438 XmNmarginHeight, 0, 439 XmNresizePolicy, XmRESIZE_ANY, 440 NULL); 441 gui_motif_menu_colors(vimForm); 442 443 #ifdef FEAT_MENU 444 { 445 Arg al[7]; /* Make sure there is enough room for arguments! */ 446 int ac = 0; 447 448 # if (XmVersion >= 1002) 449 XtSetArg(al[ac], XmNtearOffModel, tearoff_val); ac++; 450 # endif 451 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM); ac++; 452 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_FORM); ac++; 453 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++; 454 # ifndef FEAT_TOOLBAR 455 /* Always stick to right hand side. */ 456 XtSetArg(al[ac], XmNrightOffset, 0); ac++; 457 # endif 458 XtSetArg(al[ac], XmNmarginHeight, 0); ac++; 459 menuBar = XmCreateMenuBar(vimForm, "menuBar", al, ac); 460 XtManageChild(menuBar); 461 462 /* Remember the default colors, needed for ":hi clear". */ 463 XtVaGetValues(menuBar, 464 XmNbackground, &gui.menu_def_bg_pixel, 465 XmNforeground, &gui.menu_def_fg_pixel, 466 NULL); 467 gui_motif_menu_colors(menuBar); 468 } 469 #endif 470 471 #ifdef FEAT_TOOLBAR 472 /* 473 * Create an empty ToolBar. We should get buttons defined from menu.vim. 474 */ 475 toolBarFrame = XtVaCreateWidget("toolBarFrame", 476 xmFrameWidgetClass, vimForm, 477 XmNshadowThickness, 0, 478 XmNmarginHeight, 0, 479 XmNmarginWidth, 0, 480 XmNleftAttachment, XmATTACH_FORM, 481 XmNrightAttachment, XmATTACH_FORM, 482 NULL); 483 gui_motif_menu_colors(toolBarFrame); 484 485 toolBar = XtVaCreateManagedWidget("toolBar", 486 xmRowColumnWidgetClass, toolBarFrame, 487 XmNchildType, XmFRAME_WORKAREA_CHILD, 488 XmNrowColumnType, XmWORK_AREA, 489 XmNorientation, XmHORIZONTAL, 490 XmNtraversalOn, False, 491 XmNisHomogeneous, False, 492 XmNpacking, XmPACK_TIGHT, 493 XmNspacing, 0, 494 XmNshadowThickness, 0, 495 XmNhighlightThickness, 0, 496 XmNmarginHeight, 0, 497 XmNmarginWidth, 0, 498 XmNadjustLast, True, 499 NULL); 500 gui_motif_menu_colors(toolBar); 501 502 #endif 503 504 #ifdef FEAT_GUI_TABLINE 505 /* Create the Vim GUI tabline */ 506 n = 0; 507 XtSetArg(args[n], XmNbindingType, XmNONE); n++; 508 XtSetArg(args[n], XmNorientation, XmVERTICAL); n++; 509 XtSetArg(args[n], XmNbackPageSize, XmNONE); n++; 510 XtSetArg(args[n], XmNbackPageNumber, 0); n++; 511 XtSetArg(args[n], XmNbackPagePlacement, XmTOP_RIGHT); n++; 512 XtSetArg(args[n], XmNmajorTabSpacing, 0); n++; 513 XtSetArg(args[n], XmNshadowThickness, 0); n++; 514 XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++; 515 XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++; 516 tabLine = XmCreateNotebook(vimForm, "Vim tabline", args, n); 517 518 XtAddCallback(tabLine, XmNpageChangedCallback, (XtCallbackProc)tabline_cb, 519 NULL); 520 XtAddEventHandler(tabLine, ButtonPressMask, False, 521 (XtEventHandler)tabline_menu_cb, NULL); 522 523 gui.tabline_height = TABLINE_HEIGHT; 524 525 /* 526 * Set the size of the minor next/prev scrollers to zero, so 527 * that they are not displayed. Due to a bug in OpenMotif 2.3, 528 * even if these children widget are unmanaged, they are again 529 * managed by the Notebook widget and the notebook widget geometry 530 * is adjusted to account for the minor scroller widgets. 531 */ 532 scroller = XtNameToWidget(tabLine, "MinorTabScrollerNext"); 533 XtVaSetValues(scroller, XmNwidth, 0, XmNresizable, False, 534 XmNtraversalOn, False, NULL); 535 scroller = XtNameToWidget(tabLine, "MinorTabScrollerPrevious"); 536 XtVaSetValues(scroller, XmNwidth, 0, XmNresizable, False, 537 XmNtraversalOn, False, NULL); 538 539 /* Create the tabline popup menu */ 540 tabLine_menu = XmCreatePopupMenu(tabLine, "tabline popup", NULL, 0); 541 542 /* Add the buttons to the menu */ 543 if (first_tabpage->tp_next != NULL) 544 { 545 n = 0; 546 XtSetArg(args[n], XmNuserData, TABLINE_MENU_CLOSE); n++; 547 xms = XmStringCreate((char *)"Close tab", STRING_TAG); 548 XtSetArg(args[n], XmNlabelString, xms); n++; 549 button = XmCreatePushButton(tabLine_menu, "Close", args, n); 550 XtAddCallback(button, XmNactivateCallback, 551 (XtCallbackProc)tabline_button_cb, NULL); 552 XmStringFree(xms); 553 } 554 555 n = 0; 556 XtSetArg(args[n], XmNuserData, TABLINE_MENU_NEW); n++; 557 xms = XmStringCreate((char *)"New Tab", STRING_TAG); 558 XtSetArg(args[n], XmNlabelString, xms); n++; 559 button = XmCreatePushButton(tabLine_menu, "New Tab", args, n); 560 XtAddCallback(button, XmNactivateCallback, 561 (XtCallbackProc)tabline_button_cb, NULL); 562 XmStringFree(xms); 563 564 n = 0; 565 XtSetArg(args[n], XmNuserData, TABLINE_MENU_OPEN); n++; 566 xms = XmStringCreate((char *)"Open tab...", STRING_TAG); 567 XtSetArg(args[n], XmNlabelString, xms); n++; 568 button = XmCreatePushButton(tabLine_menu, "Open tab...", args, n); 569 XtAddCallback(button, XmNactivateCallback, 570 (XtCallbackProc)tabline_button_cb, NULL); 571 XmStringFree(xms); 572 #endif 573 574 textAreaForm = XtVaCreateManagedWidget("textAreaForm", 575 xmFormWidgetClass, vimForm, 576 XmNleftAttachment, XmATTACH_FORM, 577 XmNrightAttachment, XmATTACH_FORM, 578 XmNbottomAttachment, XmATTACH_FORM, 579 XmNtopAttachment, XmATTACH_FORM, 580 XmNmarginWidth, 0, 581 XmNmarginHeight, 0, 582 XmNresizePolicy, XmRESIZE_ANY, 583 NULL); 584 gui_motif_scroll_colors(textAreaForm); 585 586 textArea = XtVaCreateManagedWidget("textArea", 587 xmDrawingAreaWidgetClass, textAreaForm, 588 XmNforeground, gui.norm_pixel, 589 XmNbackground, gui.back_pixel, 590 XmNleftAttachment, XmATTACH_FORM, 591 XmNtopAttachment, XmATTACH_FORM, 592 XmNrightAttachment, XmATTACH_FORM, 593 XmNbottomAttachment, XmATTACH_FORM, 594 595 /* 596 * These take some control away from the user, but avoids making them 597 * add resources to get a decent looking setup. 598 */ 599 XmNborderWidth, 0, 600 XmNhighlightThickness, 0, 601 XmNshadowThickness, 0, 602 NULL); 603 604 #ifdef FEAT_FOOTER 605 /* 606 * Create the Footer. 607 */ 608 footer = XtVaCreateWidget("footer", 609 xmLabelGadgetClass, vimForm, 610 XmNalignment, XmALIGNMENT_BEGINNING, 611 XmNmarginHeight, 0, 612 XmNmarginWidth, 0, 613 XmNtraversalOn, False, 614 XmNrecomputeSize, False, 615 XmNleftAttachment, XmATTACH_FORM, 616 XmNleftOffset, 5, 617 XmNrightAttachment, XmATTACH_FORM, 618 XmNbottomAttachment, XmATTACH_FORM, 619 NULL); 620 gui_mch_set_footer((char_u *) ""); 621 #endif 622 623 /* 624 * Install the callbacks. 625 */ 626 gui_x11_callbacks(textArea, vimForm); 627 628 /* Pretend we don't have input focus, we will get an event if we do. */ 629 gui.in_focus = FALSE; 630 } 631 632 /* 633 * Called when the GUI is not going to start after all. 634 */ 635 void 636 gui_x11_destroy_widgets() 637 { 638 textArea = NULL; 639 #ifdef FEAT_MENU 640 menuBar = NULL; 641 #endif 642 } 643 644 void 645 gui_mch_set_text_area_pos(x, y, w, h) 646 int x UNUSED; 647 int y UNUSED; 648 int w UNUSED; 649 int h UNUSED; 650 { 651 #ifdef FEAT_TOOLBAR 652 /* Give keyboard focus to the textArea instead of the toolbar. */ 653 reset_focus(); 654 #endif 655 } 656 657 void 658 gui_x11_set_back_color() 659 { 660 if (textArea != NULL) 661 #if (XmVersion >= 1002) 662 XmChangeColor(textArea, gui.back_pixel); 663 #else 664 XtVaSetValues(textArea, 665 XmNbackground, gui.back_pixel, 666 NULL); 667 #endif 668 } 669 670 /* 671 * Manage dialog centered on pointer. This could be used by the Athena code as 672 * well. 673 */ 674 void 675 manage_centered(dialog_child) 676 Widget dialog_child; 677 { 678 Widget shell = XtParent(dialog_child); 679 Window root, child; 680 unsigned int mask; 681 unsigned int width, height, border_width, depth; 682 int x, y, win_x, win_y, maxX, maxY; 683 Boolean mappedWhenManaged; 684 685 /* Temporarily set value of XmNmappedWhenManaged 686 to stop the dialog from popping up right away */ 687 XtVaGetValues(shell, XmNmappedWhenManaged, &mappedWhenManaged, NULL); 688 XtVaSetValues(shell, XmNmappedWhenManaged, False, NULL); 689 690 XtManageChild(dialog_child); 691 692 /* Get the pointer position (x, y) */ 693 XQueryPointer(XtDisplay(shell), XtWindow(shell), &root, &child, 694 &x, &y, &win_x, &win_y, &mask); 695 696 /* Translate the pointer position (x, y) into a position for the new 697 window that will place the pointer at its center */ 698 XGetGeometry(XtDisplay(shell), XtWindow(shell), &root, &win_x, &win_y, 699 &width, &height, &border_width, &depth); 700 width += 2 * border_width; 701 height += 2 * border_width; 702 x -= width / 2; 703 y -= height / 2; 704 705 /* Ensure that the dialog remains on screen */ 706 maxX = XtScreen(shell)->width - width; 707 maxY = XtScreen(shell)->height - height; 708 if (x < 0) 709 x = 0; 710 if (x > maxX) 711 x = maxX; 712 if (y < 0) 713 y = 0; 714 if (y > maxY) 715 y = maxY; 716 717 /* Set desired window position in the DialogShell */ 718 XtVaSetValues(shell, XmNx, x, XmNy, y, NULL); 719 720 /* Map the widget */ 721 XtMapWidget(shell); 722 723 /* Restore the value of XmNmappedWhenManaged */ 724 XtVaSetValues(shell, XmNmappedWhenManaged, mappedWhenManaged, NULL); 725 } 726 727 #if defined(FEAT_MENU) || defined(FEAT_SUN_WORKSHOP) \ 728 || defined(FEAT_GUI_DIALOG) || defined(PROTO) 729 730 /* 731 * Encapsulate the way an XmFontList is created. 732 */ 733 XmFontList 734 gui_motif_create_fontlist(font) 735 XFontStruct *font; 736 { 737 XmFontList font_list; 738 739 # if (XmVersion <= 1001) 740 /* Motif 1.1 method */ 741 font_list = XmFontListCreate(font, STRING_TAG); 742 # else 743 /* Motif 1.2 method */ 744 XmFontListEntry font_list_entry; 745 746 font_list_entry = XmFontListEntryCreate(STRING_TAG, XmFONT_IS_FONT, 747 (XtPointer)font); 748 font_list = XmFontListAppendEntry(NULL, font_list_entry); 749 XmFontListEntryFree(&font_list_entry); 750 # endif 751 return font_list; 752 } 753 754 # if ((XmVersion > 1001) && defined(FEAT_XFONTSET)) || defined(PROTO) 755 XmFontList 756 gui_motif_fontset2fontlist(fontset) 757 XFontSet *fontset; 758 { 759 XmFontList font_list; 760 761 /* Motif 1.2 method */ 762 XmFontListEntry font_list_entry; 763 764 font_list_entry = XmFontListEntryCreate(STRING_TAG, 765 XmFONT_IS_FONTSET, 766 (XtPointer)*fontset); 767 font_list = XmFontListAppendEntry(NULL, font_list_entry); 768 XmFontListEntryFree(&font_list_entry); 769 return font_list; 770 } 771 # endif 772 773 #endif 774 775 #if defined(FEAT_MENU) || defined(PROTO) 776 /* 777 * Menu stuff. 778 */ 779 780 static void gui_motif_add_actext __ARGS((vimmenu_T *menu)); 781 #if (XmVersion >= 1002) 782 static void toggle_tearoff __ARGS((Widget wid)); 783 static void gui_mch_recurse_tearoffs __ARGS((vimmenu_T *menu)); 784 #endif 785 static void submenu_change __ARGS((vimmenu_T *mp, int colors)); 786 787 static void do_set_mnemonics __ARGS((int enable)); 788 static int menu_enabled = TRUE; 789 790 void 791 gui_mch_enable_menu(flag) 792 int flag; 793 { 794 if (flag) 795 { 796 XtManageChild(menuBar); 797 #ifdef FEAT_TOOLBAR 798 if (XtIsManaged(XtParent(toolBar))) 799 { 800 /* toolBar is attached to top form */ 801 XtVaSetValues(XtParent(toolBar), 802 XmNtopAttachment, XmATTACH_WIDGET, 803 XmNtopWidget, menuBar, 804 NULL); 805 #ifdef FEAT_GUI_TABLINE 806 if (showing_tabline) 807 { 808 XtVaSetValues(tabLine, 809 XmNtopAttachment, XmATTACH_WIDGET, 810 XmNtopWidget, XtParent(toolBar), 811 NULL); 812 XtVaSetValues(textAreaForm, 813 XmNtopAttachment, XmATTACH_WIDGET, 814 XmNtopWidget, tabLine, 815 NULL); 816 } 817 else 818 #endif 819 XtVaSetValues(textAreaForm, 820 XmNtopAttachment, XmATTACH_WIDGET, 821 XmNtopWidget, XtParent(toolBar), 822 NULL); 823 } 824 else 825 #endif 826 { 827 #ifdef FEAT_GUI_TABLINE 828 if (showing_tabline) 829 { 830 XtVaSetValues(tabLine, 831 XmNtopAttachment, XmATTACH_WIDGET, 832 XmNtopWidget, menuBar, 833 NULL); 834 XtVaSetValues(textAreaForm, 835 XmNtopAttachment, XmATTACH_WIDGET, 836 XmNtopWidget, tabLine, 837 NULL); 838 } 839 else 840 #endif 841 XtVaSetValues(textAreaForm, 842 XmNtopAttachment, XmATTACH_WIDGET, 843 XmNtopWidget, menuBar, 844 NULL); 845 } 846 } 847 else 848 { 849 XtUnmanageChild(menuBar); 850 #ifdef FEAT_TOOLBAR 851 if (XtIsManaged(XtParent(toolBar))) 852 { 853 XtVaSetValues(XtParent(toolBar), 854 XmNtopAttachment, XmATTACH_FORM, 855 NULL); 856 #ifdef FEAT_GUI_TABLINE 857 if (showing_tabline) 858 { 859 XtVaSetValues(tabLine, 860 XmNtopAttachment, XmATTACH_WIDGET, 861 XmNtopWidget, XtParent(toolBar), 862 NULL); 863 XtVaSetValues(textAreaForm, 864 XmNtopAttachment, XmATTACH_WIDGET, 865 XmNtopWidget, tabLine, 866 NULL); 867 } 868 else 869 #endif 870 XtVaSetValues(textAreaForm, 871 XmNtopAttachment, XmATTACH_WIDGET, 872 XmNtopWidget, XtParent(toolBar), 873 NULL); 874 } 875 else 876 #endif 877 { 878 #ifdef FEAT_GUI_TABLINE 879 if (showing_tabline) 880 { 881 XtVaSetValues(tabLine, 882 XmNtopAttachment, XmATTACH_FORM, 883 NULL); 884 XtVaSetValues(textAreaForm, 885 XmNtopAttachment, XmATTACH_WIDGET, 886 XmNtopWidget, tabLine, 887 NULL); 888 } 889 else 890 #endif 891 XtVaSetValues(textAreaForm, 892 XmNtopAttachment, XmATTACH_FORM, 893 NULL); 894 } 895 } 896 897 } 898 899 /* 900 * Enable or disable mnemonics for the toplevel menus. 901 */ 902 void 903 gui_motif_set_mnemonics(enable) 904 int enable; 905 { 906 /* 907 * Don't enable menu mnemonics when the menu bar is disabled, LessTif 908 * crashes when using a mnemonic then. 909 */ 910 if (!menu_enabled) 911 enable = FALSE; 912 do_set_mnemonics(enable); 913 } 914 915 static void 916 do_set_mnemonics(enable) 917 int enable; 918 { 919 vimmenu_T *menu; 920 921 for (menu = root_menu; menu != NULL; menu = menu->next) 922 if (menu->id != (Widget)0) 923 XtVaSetValues(menu->id, 924 XmNmnemonic, enable ? menu->mnemonic : NUL, 925 NULL); 926 } 927 928 void 929 gui_mch_add_menu(menu, idx) 930 vimmenu_T *menu; 931 int idx; 932 { 933 XmString label; 934 Widget shell; 935 vimmenu_T *parent = menu->parent; 936 937 #ifdef MOTIF_POPUP 938 if (menu_is_popup(menu->name)) 939 { 940 Arg arg[2]; 941 int n = 0; 942 943 /* Only create the popup menu when it's actually used, otherwise there 944 * is a delay when using the right mouse button. */ 945 # if (XmVersion <= 1002) 946 if (mouse_model_popup()) 947 # endif 948 { 949 if (gui.menu_bg_pixel != INVALCOLOR) 950 { 951 XtSetArg(arg[0], XmNbackground, gui.menu_bg_pixel); n++; 952 } 953 if (gui.menu_fg_pixel != INVALCOLOR) 954 { 955 XtSetArg(arg[1], XmNforeground, gui.menu_fg_pixel); n++; 956 } 957 menu->submenu_id = XmCreatePopupMenu(textArea, "contextMenu", 958 arg, n); 959 menu->id = (Widget)0; 960 } 961 return; 962 } 963 #endif 964 965 if (!menu_is_menubar(menu->name) 966 || (parent != NULL && parent->submenu_id == (Widget)0)) 967 return; 968 969 label = XmStringCreate((char *)menu->dname, STRING_TAG); 970 if (label == NULL) 971 return; 972 menu->id = XtVaCreateWidget("subMenu", 973 xmCascadeButtonWidgetClass, 974 (parent == NULL) ? menuBar : parent->submenu_id, 975 XmNlabelString, label, 976 XmNmnemonic, p_wak[0] == 'n' ? NUL : menu->mnemonic, 977 #if (XmVersion >= 1002) 978 /* submenu: count the tearoff item (needed for LessTif) */ 979 XmNpositionIndex, idx + (parent != NULL 980 && tearoff_val == (int)XmTEAR_OFF_ENABLED ? 1 : 0), 981 #endif 982 NULL); 983 gui_motif_menu_colors(menu->id); 984 gui_motif_menu_fontlist(menu->id); 985 XmStringFree(label); 986 987 if (menu->id == (Widget)0) /* failed */ 988 return; 989 990 /* add accelerator text */ 991 gui_motif_add_actext(menu); 992 993 shell = XtVaCreateWidget("subMenuShell", 994 xmMenuShellWidgetClass, menu->id, 995 XmNwidth, 1, 996 XmNheight, 1, 997 NULL); 998 gui_motif_menu_colors(shell); 999 menu->submenu_id = XtVaCreateWidget("rowColumnMenu", 1000 xmRowColumnWidgetClass, shell, 1001 XmNrowColumnType, XmMENU_PULLDOWN, 1002 NULL); 1003 gui_motif_menu_colors(menu->submenu_id); 1004 1005 if (menu->submenu_id == (Widget)0) /* failed */ 1006 return; 1007 1008 #if (XmVersion >= 1002) 1009 /* Set the colors for the tear off widget */ 1010 toggle_tearoff(menu->submenu_id); 1011 #endif 1012 1013 XtVaSetValues(menu->id, 1014 XmNsubMenuId, menu->submenu_id, 1015 NULL); 1016 1017 /* 1018 * The "Help" menu is a special case, and should be placed at the far 1019 * right hand side of the menu-bar. It's recognized by its high priority. 1020 */ 1021 if (parent == NULL && menu->priority >= 9999) 1022 XtVaSetValues(menuBar, 1023 XmNmenuHelpWidget, menu->id, 1024 NULL); 1025 1026 /* 1027 * When we add a top-level item to the menu bar, we can figure out how 1028 * high the menu bar should be. 1029 */ 1030 if (parent == NULL) 1031 gui_mch_compute_menu_height(menu->id); 1032 } 1033 1034 1035 /* 1036 * Add mnemonic and accelerator text to a menu button. 1037 */ 1038 static void 1039 gui_motif_add_actext(menu) 1040 vimmenu_T *menu; 1041 { 1042 XmString label; 1043 1044 /* Add accelerator text, if there is one */ 1045 if (menu->actext != NULL && menu->id != (Widget)0) 1046 { 1047 label = XmStringCreate((char *)menu->actext, STRING_TAG); 1048 if (label == NULL) 1049 return; 1050 XtVaSetValues(menu->id, XmNacceleratorText, label, NULL); 1051 XmStringFree(label); 1052 } 1053 } 1054 1055 void 1056 gui_mch_toggle_tearoffs(enable) 1057 int enable; 1058 { 1059 #if (XmVersion >= 1002) 1060 if (enable) 1061 tearoff_val = (int)XmTEAR_OFF_ENABLED; 1062 else 1063 tearoff_val = (int)XmTEAR_OFF_DISABLED; 1064 toggle_tearoff(menuBar); 1065 gui_mch_recurse_tearoffs(root_menu); 1066 #endif 1067 } 1068 1069 #if (XmVersion >= 1002) 1070 /* 1071 * Set the tearoff for one menu widget on or off, and set the color of the 1072 * tearoff widget. 1073 */ 1074 static void 1075 toggle_tearoff(wid) 1076 Widget wid; 1077 { 1078 Widget w; 1079 1080 XtVaSetValues(wid, XmNtearOffModel, tearoff_val, NULL); 1081 if (tearoff_val == (int)XmTEAR_OFF_ENABLED 1082 && (w = XmGetTearOffControl(wid)) != (Widget)0) 1083 gui_motif_menu_colors(w); 1084 } 1085 1086 static void 1087 gui_mch_recurse_tearoffs(menu) 1088 vimmenu_T *menu; 1089 { 1090 while (menu != NULL) 1091 { 1092 if (!menu_is_popup(menu->name)) 1093 { 1094 if (menu->submenu_id != (Widget)0) 1095 toggle_tearoff(menu->submenu_id); 1096 gui_mch_recurse_tearoffs(menu->children); 1097 } 1098 menu = menu->next; 1099 } 1100 } 1101 #endif 1102 1103 int 1104 gui_mch_text_area_extra_height() 1105 { 1106 Dimension shadowHeight; 1107 1108 XtVaGetValues(textAreaForm, XmNshadowThickness, &shadowHeight, NULL); 1109 return shadowHeight; 1110 } 1111 1112 /* 1113 * Compute the height of the menu bar. 1114 * We need to check all the items for their position and height, for the case 1115 * there are several rows, and/or some characters extend higher or lower. 1116 */ 1117 void 1118 gui_mch_compute_menu_height(id) 1119 Widget id; /* can be NULL when deleting menu */ 1120 { 1121 Dimension y, maxy; 1122 Dimension margin, shadow; 1123 vimmenu_T *mp; 1124 static Dimension height = 21; /* normal height of a menu item */ 1125 1126 /* 1127 * Get the height of the new item, before managing it, because it will 1128 * still reflect the font size. After managing it depends on the menu 1129 * height, which is what we just wanted to get!. 1130 */ 1131 if (id != (Widget)0) 1132 XtVaGetValues(id, XmNheight, &height, NULL); 1133 1134 /* Find any menu Widget, to be able to call XtManageChild() */ 1135 else 1136 for (mp = root_menu; mp != NULL; mp = mp->next) 1137 if (mp->id != (Widget)0 && menu_is_menubar(mp->name)) 1138 { 1139 id = mp->id; 1140 break; 1141 } 1142 1143 /* 1144 * Now manage the menu item, to make them all be positioned (makes an 1145 * extra row when needed, removes it when not needed). 1146 */ 1147 if (id != (Widget)0) 1148 XtManageChild(id); 1149 1150 /* 1151 * Now find the menu item that is the furthest down, and get it's position. 1152 */ 1153 maxy = 0; 1154 for (mp = root_menu; mp != NULL; mp = mp->next) 1155 { 1156 if (mp->id != (Widget)0 && menu_is_menubar(mp->name)) 1157 { 1158 XtVaGetValues(mp->id, XmNy, &y, NULL); 1159 if (y > maxy) 1160 maxy = y; 1161 } 1162 } 1163 1164 XtVaGetValues(menuBar, 1165 XmNmarginHeight, &margin, 1166 XmNshadowThickness, &shadow, 1167 NULL); 1168 1169 /* 1170 * This computation is the result of trial-and-error: 1171 * maxy = The maximum position of an item; required for when there are 1172 * two or more rows 1173 * height = height of an item, before managing it; Hopefully this will 1174 * change with the font height. Includes shadow-border. 1175 * shadow = shadow-border; must be subtracted from the height. 1176 * margin = margin around the menu buttons; Must be added. 1177 * Add 4 for the underlining of shortcut keys. 1178 */ 1179 gui.menu_height = maxy + height - 2 * shadow + 2 * margin + 4; 1180 1181 /* Somehow the menu bar doesn't resize automatically. Set it here, 1182 * even though this is a catch 22. Don't do this when starting up, 1183 * somehow the menu gets very high then. */ 1184 if (gui.shell_created) 1185 XtVaSetValues(menuBar, XmNheight, gui.menu_height, NULL); 1186 } 1187 1188 #ifdef FEAT_TOOLBAR 1189 1190 /* 1191 * Icons used by the toolbar code. 1192 */ 1193 #include "gui_x11_pm.h" 1194 1195 static int check_xpm __ARGS((char_u *path)); 1196 static char **get_toolbar_pixmap __ARGS((vimmenu_T *menu, char **fname)); 1197 static int add_pixmap_args __ARGS((vimmenu_T *menu, Arg *args, int n)); 1198 1199 /* 1200 * Read an Xpm file. Return OK or FAIL. 1201 */ 1202 static int 1203 check_xpm(path) 1204 char_u *path; 1205 { 1206 XpmAttributes attrs; 1207 int status; 1208 Pixmap mask; 1209 Pixmap map; 1210 1211 attrs.valuemask = 0; 1212 1213 /* Create the "sensitive" pixmap */ 1214 status = XpmReadFileToPixmap(gui.dpy, 1215 RootWindow(gui.dpy, DefaultScreen(gui.dpy)), 1216 (char *)path, &map, &mask, &attrs); 1217 XpmFreeAttributes(&attrs); 1218 1219 if (status == XpmSuccess) 1220 return OK; 1221 return FAIL; 1222 } 1223 1224 1225 /* 1226 * Allocated a pixmap for toolbar menu "menu". 1227 * When it's to be read from a file, "fname" is set to the file name 1228 * (in allocated memory). 1229 * Return a blank pixmap if it fails. 1230 */ 1231 static char ** 1232 get_toolbar_pixmap(menu, fname) 1233 vimmenu_T *menu; 1234 char **fname; 1235 { 1236 char_u buf[MAXPATHL]; /* buffer storing expanded pathname */ 1237 char **xpm = NULL; /* xpm array */ 1238 int res; 1239 1240 *fname = NULL; 1241 buf[0] = NUL; /* start with NULL path */ 1242 1243 if (menu->iconfile != NULL) 1244 { 1245 /* Use the "icon=" argument. */ 1246 gui_find_iconfile(menu->iconfile, buf, "xpm"); 1247 res = check_xpm(buf); 1248 1249 /* If it failed, try using the menu name. */ 1250 if (res == FAIL && gui_find_bitmap(menu->name, buf, "xpm") == OK) 1251 res = check_xpm(buf); 1252 if (res == OK) 1253 { 1254 *fname = (char *)vim_strsave(buf); 1255 return tb_blank_xpm; 1256 } 1257 } 1258 1259 if (menu->icon_builtin || gui_find_bitmap(menu->name, buf, "xpm") == FAIL) 1260 { 1261 if (menu->iconidx >= 0 && menu->iconidx 1262 < (int)(sizeof(built_in_pixmaps) / sizeof(built_in_pixmaps[0]))) 1263 xpm = built_in_pixmaps[menu->iconidx]; 1264 else 1265 xpm = tb_blank_xpm; 1266 } 1267 1268 return xpm; 1269 } 1270 1271 /* 1272 * Add arguments for the toolbar pixmap to a menu item. 1273 */ 1274 static int 1275 add_pixmap_args(menu, args, n) 1276 vimmenu_T *menu; 1277 Arg *args; 1278 int n; 1279 { 1280 vim_free(menu->xpm_fname); 1281 menu->xpm = get_toolbar_pixmap(menu, &menu->xpm_fname); 1282 if (menu->xpm == NULL) 1283 { 1284 XtSetArg(args[n], XmNlabelType, XmSTRING); n++; 1285 } 1286 else 1287 { 1288 if (menu->xpm_fname != NULL) 1289 { 1290 XtSetArg(args[n], XmNpixmapFile, menu->xpm_fname); n++; 1291 } 1292 XtSetArg(args[n], XmNpixmapData, menu->xpm); n++; 1293 XtSetArg(args[n], XmNlabelLocation, XmBOTTOM); n++; 1294 } 1295 return n; 1296 } 1297 #endif /* FEAT_TOOLBAR */ 1298 1299 void 1300 gui_mch_add_menu_item(menu, idx) 1301 vimmenu_T *menu; 1302 int idx; 1303 { 1304 XmString label; 1305 vimmenu_T *parent = menu->parent; 1306 1307 # ifdef EBCDIC 1308 menu->mnemonic = 0; 1309 # endif 1310 1311 # if (XmVersion <= 1002) 1312 /* Don't add Popup menu items when the popup menu isn't used. */ 1313 if (menu_is_child_of_popup(menu) && !mouse_model_popup()) 1314 return; 1315 # endif 1316 1317 # ifdef FEAT_TOOLBAR 1318 if (menu_is_toolbar(parent->name)) 1319 { 1320 WidgetClass type; 1321 XmString xms = NULL; /* fallback label if pixmap not found */ 1322 int n; 1323 Arg args[18]; 1324 1325 n = 0; 1326 if (menu_is_separator(menu->name)) 1327 { 1328 char *cp; 1329 Dimension wid; 1330 1331 /* 1332 * A separator has the format "-sep%d[:%d]-". The optional :%d is 1333 * a width specifier. If no width is specified then we choose one. 1334 */ 1335 cp = (char *)vim_strchr(menu->name, ':'); 1336 if (cp != NULL) 1337 wid = (Dimension)atoi(++cp); 1338 else 1339 wid = 4; 1340 1341 type = xmSeparatorWidgetClass; 1342 XtSetArg(args[n], XmNwidth, wid); n++; 1343 XtSetArg(args[n], XmNminWidth, wid); n++; 1344 XtSetArg(args[n], XmNorientation, XmVERTICAL); n++; 1345 XtSetArg(args[n], XmNseparatorType, XmSHADOW_ETCHED_IN); n++; 1346 } 1347 else 1348 { 1349 /* Without shadows one can't sense whatever the button has been 1350 * pressed or not! However we want to save a bit of space... 1351 * Need the highlightThickness to see the focus. 1352 */ 1353 XtSetArg(args[n], XmNhighlightThickness, 1); n++; 1354 XtSetArg(args[n], XmNhighlightOnEnter, True); n++; 1355 XtSetArg(args[n], XmNmarginWidth, 0); n++; 1356 XtSetArg(args[n], XmNmarginHeight, 0); n++; 1357 XtSetArg(args[n], XmNtraversalOn, False); n++; 1358 /* Set the label here, so that we can switch between icons/text 1359 * by changing the XmNlabelType resource. */ 1360 xms = XmStringCreate((char *)menu->dname, STRING_TAG); 1361 XtSetArg(args[n], XmNlabelString, xms); n++; 1362 1363 n = add_pixmap_args(menu, args, n); 1364 1365 type = xmEnhancedButtonWidgetClass; 1366 } 1367 1368 XtSetArg(args[n], XmNpositionIndex, idx); n++; 1369 if (menu->id == NULL) 1370 { 1371 menu->id = XtCreateManagedWidget((char *)menu->dname, 1372 type, toolBar, args, n); 1373 if (menu->id != NULL && type == xmEnhancedButtonWidgetClass) 1374 { 1375 XtAddCallback(menu->id, 1376 XmNactivateCallback, gui_x11_menu_cb, menu); 1377 # ifdef FEAT_FOOTER 1378 XtAddEventHandler(menu->id, EnterWindowMask, False, 1379 toolbarbutton_enter_cb, menu); 1380 XtAddEventHandler(menu->id, LeaveWindowMask, False, 1381 toolbarbutton_leave_cb, menu); 1382 # endif 1383 } 1384 } 1385 else 1386 XtSetValues(menu->id, args, n); 1387 if (xms != NULL) 1388 XmStringFree(xms); 1389 1390 # ifdef FEAT_BEVAL 1391 gui_mch_menu_set_tip(menu); 1392 # endif 1393 1394 menu->parent = parent; 1395 menu->submenu_id = NULL; 1396 /* When adding first item to toolbar it might have to be enabled .*/ 1397 if (!XtIsManaged(XtParent(toolBar)) 1398 && vim_strchr(p_go, GO_TOOLBAR) != NULL) 1399 gui_mch_show_toolbar(TRUE); 1400 gui.toolbar_height = gui_mch_compute_toolbar_height(); 1401 return; 1402 } /* toolbar menu item */ 1403 # endif 1404 1405 /* No parent, must be a non-menubar menu */ 1406 if (parent->submenu_id == (Widget)0) 1407 return; 1408 1409 menu->submenu_id = (Widget)0; 1410 1411 /* Add menu separator */ 1412 if (menu_is_separator(menu->name)) 1413 { 1414 menu->id = XtVaCreateWidget("subMenu", 1415 xmSeparatorGadgetClass, parent->submenu_id, 1416 #if (XmVersion >= 1002) 1417 /* count the tearoff item (needed for LessTif) */ 1418 XmNpositionIndex, idx + (tearoff_val == (int)XmTEAR_OFF_ENABLED 1419 ? 1 : 0), 1420 #endif 1421 NULL); 1422 gui_motif_menu_colors(menu->id); 1423 return; 1424 } 1425 1426 label = XmStringCreate((char *)menu->dname, STRING_TAG); 1427 if (label == NULL) 1428 return; 1429 menu->id = XtVaCreateWidget("subMenu", 1430 xmPushButtonWidgetClass, parent->submenu_id, 1431 XmNlabelString, label, 1432 XmNmnemonic, menu->mnemonic, 1433 #if (XmVersion >= 1002) 1434 /* count the tearoff item (needed for LessTif) */ 1435 XmNpositionIndex, idx + (tearoff_val == (int)XmTEAR_OFF_ENABLED 1436 ? 1 : 0), 1437 #endif 1438 NULL); 1439 gui_motif_menu_colors(menu->id); 1440 gui_motif_menu_fontlist(menu->id); 1441 XmStringFree(label); 1442 1443 if (menu->id != (Widget)0) 1444 { 1445 XtAddCallback(menu->id, XmNactivateCallback, gui_x11_menu_cb, 1446 (XtPointer)menu); 1447 /* add accelerator text */ 1448 gui_motif_add_actext(menu); 1449 } 1450 } 1451 1452 #if (XmVersion <= 1002) || defined(PROTO) 1453 /* 1454 * This function will destroy/create the popup menus dynamically, 1455 * according to the value of 'mousemodel'. 1456 * This will fix the "right mouse button freeze" that occurs when 1457 * there exists a popup menu but it isn't managed. 1458 */ 1459 void 1460 gui_motif_update_mousemodel(menu) 1461 vimmenu_T *menu; 1462 { 1463 int idx = 0; 1464 1465 /* When GUI hasn't started the menus have not been created. */ 1466 if (!gui.in_use) 1467 return; 1468 1469 while (menu) 1470 { 1471 if (menu->children != NULL) 1472 { 1473 if (menu_is_popup(menu->name)) 1474 { 1475 if (mouse_model_popup()) 1476 { 1477 /* Popup menu will be used. Create the popup menus. */ 1478 gui_mch_add_menu(menu, idx); 1479 gui_motif_update_mousemodel(menu->children); 1480 } 1481 else 1482 { 1483 /* Popup menu will not be used. Destroy the popup menus. */ 1484 gui_motif_update_mousemodel(menu->children); 1485 gui_mch_destroy_menu(menu); 1486 } 1487 } 1488 } 1489 else if (menu_is_child_of_popup(menu)) 1490 { 1491 if (mouse_model_popup()) 1492 gui_mch_add_menu_item(menu, idx); 1493 else 1494 gui_mch_destroy_menu(menu); 1495 } 1496 menu = menu->next; 1497 ++idx; 1498 } 1499 } 1500 #endif 1501 1502 void 1503 gui_mch_new_menu_colors() 1504 { 1505 if (menuBar == (Widget)0) 1506 return; 1507 gui_motif_menu_colors(menuBar); 1508 #ifdef FEAT_TOOLBAR 1509 gui_motif_menu_colors(toolBarFrame); 1510 gui_motif_menu_colors(toolBar); 1511 #endif 1512 1513 submenu_change(root_menu, TRUE); 1514 } 1515 1516 void 1517 gui_mch_new_menu_font() 1518 { 1519 if (menuBar == (Widget)0) 1520 return; 1521 submenu_change(root_menu, FALSE); 1522 { 1523 Dimension height; 1524 Position w, h; 1525 1526 XtVaGetValues(menuBar, XmNheight, &height, NULL); 1527 gui.menu_height = height; 1528 1529 XtVaGetValues(vimShell, XtNwidth, &w, XtNheight, &h, NULL); 1530 gui_resize_shell(w, h 1531 #ifdef FEAT_XIM 1532 - xim_get_status_area_height() 1533 #endif 1534 ); 1535 } 1536 gui_set_shellsize(FALSE, TRUE, RESIZE_VERT); 1537 ui_new_shellsize(); 1538 } 1539 1540 #if defined(FEAT_BEVAL) || defined(PROTO) 1541 void 1542 gui_mch_new_tooltip_font() 1543 { 1544 # ifdef FEAT_TOOLBAR 1545 vimmenu_T *menu; 1546 1547 if (toolBar == (Widget)0) 1548 return; 1549 1550 menu = gui_find_menu((char_u *)"ToolBar"); 1551 if (menu != NULL) 1552 submenu_change(menu, FALSE); 1553 # endif 1554 } 1555 1556 void 1557 gui_mch_new_tooltip_colors() 1558 { 1559 # ifdef FEAT_TOOLBAR 1560 vimmenu_T *toolbar; 1561 1562 if (toolBar == (Widget)0) 1563 return; 1564 1565 toolbar = gui_find_menu((char_u *)"ToolBar"); 1566 if (toolbar != NULL) 1567 submenu_change(toolbar, TRUE); 1568 # endif 1569 } 1570 #endif 1571 1572 static void 1573 submenu_change(menu, colors) 1574 vimmenu_T *menu; 1575 int colors; /* TRUE for colors, FALSE for font */ 1576 { 1577 vimmenu_T *mp; 1578 1579 for (mp = menu; mp != NULL; mp = mp->next) 1580 { 1581 if (mp->id != (Widget)0) 1582 { 1583 if (colors) 1584 { 1585 gui_motif_menu_colors(mp->id); 1586 #ifdef FEAT_TOOLBAR 1587 /* For a toolbar item: Free the pixmap and allocate a new one, 1588 * so that the background color is right. */ 1589 if (mp->xpm != NULL) 1590 { 1591 int n = 0; 1592 Arg args[18]; 1593 1594 n = add_pixmap_args(mp, args, n); 1595 XtSetValues(mp->id, args, n); 1596 } 1597 # ifdef FEAT_BEVAL 1598 /* If we have a tooltip, then we need to change it's font */ 1599 if (mp->tip != NULL) 1600 { 1601 Arg args[2]; 1602 1603 args[0].name = XmNbackground; 1604 args[0].value = gui.tooltip_bg_pixel; 1605 args[1].name = XmNforeground; 1606 args[1].value = gui.tooltip_fg_pixel; 1607 XtSetValues(mp->tip->balloonLabel, &args[0], XtNumber(args)); 1608 } 1609 # endif 1610 #endif 1611 } 1612 else 1613 { 1614 gui_motif_menu_fontlist(mp->id); 1615 #ifdef FEAT_BEVAL 1616 /* If we have a tooltip, then we need to change it's font */ 1617 if (mp->tip != NULL) 1618 { 1619 Arg args[1]; 1620 1621 args[0].name = XmNfontList; 1622 args[0].value = (XtArgVal)gui_motif_fontset2fontlist( 1623 &gui.tooltip_fontset); 1624 XtSetValues(mp->tip->balloonLabel, &args[0], XtNumber(args)); 1625 } 1626 #endif 1627 } 1628 } 1629 1630 if (mp->children != NULL) 1631 { 1632 #if (XmVersion >= 1002) 1633 /* Set the colors/font for the tear off widget */ 1634 if (mp->submenu_id != (Widget)0) 1635 { 1636 if (colors) 1637 gui_motif_menu_colors(mp->submenu_id); 1638 else 1639 gui_motif_menu_fontlist(mp->submenu_id); 1640 toggle_tearoff(mp->submenu_id); 1641 } 1642 #endif 1643 /* Set the colors for the children */ 1644 submenu_change(mp->children, colors); 1645 } 1646 } 1647 } 1648 1649 /* 1650 * Destroy the machine specific menu widget. 1651 */ 1652 void 1653 gui_mch_destroy_menu(menu) 1654 vimmenu_T *menu; 1655 { 1656 /* Please be sure to destroy the parent widget first (i.e. menu->id). 1657 * On the other hand, problems have been reported that the submenu must be 1658 * deleted first... 1659 * 1660 * This code should be basically identical to that in the file gui_athena.c 1661 * because they are both Xt based. 1662 */ 1663 if (menu->submenu_id != (Widget)0) 1664 { 1665 XtDestroyWidget(menu->submenu_id); 1666 menu->submenu_id = (Widget)0; 1667 } 1668 1669 if (menu->id != (Widget)0) 1670 { 1671 Widget parent; 1672 1673 parent = XtParent(menu->id); 1674 #if defined(FEAT_TOOLBAR) && defined(FEAT_BEVAL) 1675 if (parent == toolBar && menu->tip != NULL) 1676 { 1677 /* We try to destroy this before the actual menu, because there are 1678 * callbacks, etc. that will be unregistered during the tooltip 1679 * destruction. 1680 * 1681 * If you call "gui_mch_destroy_beval_area()" after destroying 1682 * menu->id, then the tooltip's window will have already been 1683 * deallocated by Xt, and unknown behaviour will ensue (probably 1684 * a core dump). 1685 */ 1686 gui_mch_destroy_beval_area(menu->tip); 1687 menu->tip = NULL; 1688 } 1689 #endif 1690 XtDestroyWidget(menu->id); 1691 menu->id = (Widget)0; 1692 if (parent == menuBar) 1693 gui_mch_compute_menu_height((Widget)0); 1694 #ifdef FEAT_TOOLBAR 1695 else if (parent == toolBar) 1696 { 1697 Cardinal num_children; 1698 1699 /* When removing last toolbar item, don't display the toolbar. */ 1700 XtVaGetValues(toolBar, XmNnumChildren, &num_children, NULL); 1701 if (num_children == 0) 1702 gui_mch_show_toolbar(FALSE); 1703 else 1704 gui.toolbar_height = gui_mch_compute_toolbar_height(); 1705 } 1706 #endif 1707 } 1708 } 1709 1710 void 1711 gui_mch_show_popupmenu(menu) 1712 vimmenu_T *menu UNUSED; 1713 { 1714 #ifdef MOTIF_POPUP 1715 XmMenuPosition(menu->submenu_id, gui_x11_get_last_mouse_event()); 1716 XtManageChild(menu->submenu_id); 1717 #endif 1718 } 1719 1720 #endif /* FEAT_MENU */ 1721 1722 /* 1723 * Set the menu and scrollbar colors to their default values. 1724 */ 1725 void 1726 gui_mch_def_colors() 1727 { 1728 if (gui.in_use) 1729 { 1730 /* Use the values saved when starting up. These should come from the 1731 * window manager or a resources file. */ 1732 gui.menu_fg_pixel = gui.menu_def_fg_pixel; 1733 gui.menu_bg_pixel = gui.menu_def_bg_pixel; 1734 gui.scroll_fg_pixel = gui.scroll_def_fg_pixel; 1735 gui.scroll_bg_pixel = gui.scroll_def_bg_pixel; 1736 #ifdef FEAT_BEVAL 1737 gui.tooltip_fg_pixel = 1738 gui_get_color((char_u *)gui.rsrc_tooltip_fg_name); 1739 gui.tooltip_bg_pixel = 1740 gui_get_color((char_u *)gui.rsrc_tooltip_bg_name); 1741 #endif 1742 } 1743 } 1744 1745 1746 /* 1747 * Scrollbar stuff. 1748 */ 1749 1750 void 1751 gui_mch_set_scrollbar_thumb(sb, val, size, max) 1752 scrollbar_T *sb; 1753 long val; 1754 long size; 1755 long max; 1756 { 1757 if (sb->id != (Widget)0) 1758 XtVaSetValues(sb->id, 1759 XmNvalue, val, 1760 XmNsliderSize, size, 1761 XmNpageIncrement, (size > 2 ? size - 2 : 1), 1762 XmNmaximum, max + 1, /* Motif has max one past the end */ 1763 NULL); 1764 } 1765 1766 void 1767 gui_mch_set_scrollbar_pos(sb, x, y, w, h) 1768 scrollbar_T *sb; 1769 int x; 1770 int y; 1771 int w; 1772 int h; 1773 { 1774 if (sb->id != (Widget)0) 1775 { 1776 if (sb->type == SBAR_LEFT || sb->type == SBAR_RIGHT) 1777 { 1778 if (y == 0) 1779 h -= gui.border_offset; 1780 else 1781 y -= gui.border_offset; 1782 XtVaSetValues(sb->id, 1783 XmNtopOffset, y, 1784 XmNbottomOffset, -y - h, 1785 XmNwidth, w, 1786 NULL); 1787 } 1788 else 1789 XtVaSetValues(sb->id, 1790 XmNtopOffset, y, 1791 XmNleftOffset, x, 1792 XmNrightOffset, gui.which_scrollbars[SBAR_RIGHT] 1793 ? gui.scrollbar_width : 0, 1794 XmNheight, h, 1795 NULL); 1796 XtManageChild(sb->id); 1797 } 1798 } 1799 1800 void 1801 gui_mch_enable_scrollbar(sb, flag) 1802 scrollbar_T *sb; 1803 int flag; 1804 { 1805 Arg args[16]; 1806 int n; 1807 1808 if (sb->id != (Widget)0) 1809 { 1810 n = 0; 1811 if (flag) 1812 { 1813 switch (sb->type) 1814 { 1815 case SBAR_LEFT: 1816 XtSetArg(args[n], XmNleftOffset, gui.scrollbar_width); n++; 1817 break; 1818 1819 case SBAR_RIGHT: 1820 XtSetArg(args[n], XmNrightOffset, gui.scrollbar_width); n++; 1821 break; 1822 1823 case SBAR_BOTTOM: 1824 XtSetArg(args[n], XmNbottomOffset, gui.scrollbar_height);n++; 1825 break; 1826 } 1827 XtSetValues(textArea, args, n); 1828 XtManageChild(sb->id); 1829 } 1830 else 1831 { 1832 if (!gui.which_scrollbars[sb->type]) 1833 { 1834 /* The scrollbars of this type are all disabled, adjust the 1835 * textArea attachment offset. */ 1836 switch (sb->type) 1837 { 1838 case SBAR_LEFT: 1839 XtSetArg(args[n], XmNleftOffset, 0); n++; 1840 break; 1841 1842 case SBAR_RIGHT: 1843 XtSetArg(args[n], XmNrightOffset, 0); n++; 1844 break; 1845 1846 case SBAR_BOTTOM: 1847 XtSetArg(args[n], XmNbottomOffset, 0);n++; 1848 break; 1849 } 1850 XtSetValues(textArea, args, n); 1851 } 1852 XtUnmanageChild(sb->id); 1853 } 1854 } 1855 } 1856 1857 void 1858 gui_mch_create_scrollbar(sb, orient) 1859 scrollbar_T *sb; 1860 int orient; /* SBAR_VERT or SBAR_HORIZ */ 1861 { 1862 Arg args[16]; 1863 int n; 1864 1865 n = 0; 1866 XtSetArg(args[n], XmNminimum, 0); n++; 1867 XtSetArg(args[n], XmNorientation, 1868 (orient == SBAR_VERT) ? XmVERTICAL : XmHORIZONTAL); n++; 1869 1870 switch (sb->type) 1871 { 1872 case SBAR_LEFT: 1873 XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++; 1874 XtSetArg(args[n], XmNbottomAttachment, XmATTACH_OPPOSITE_FORM); n++; 1875 XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++; 1876 break; 1877 1878 case SBAR_RIGHT: 1879 XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++; 1880 XtSetArg(args[n], XmNbottomAttachment, XmATTACH_OPPOSITE_FORM); n++; 1881 XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++; 1882 break; 1883 1884 case SBAR_BOTTOM: 1885 XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++; 1886 XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++; 1887 XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++; 1888 break; 1889 } 1890 1891 sb->id = XtCreateWidget("scrollBar", 1892 xmScrollBarWidgetClass, textAreaForm, args, n); 1893 1894 /* Remember the default colors, needed for ":hi clear". */ 1895 if (gui.scroll_def_bg_pixel == (guicolor_T)0 1896 && gui.scroll_def_fg_pixel == (guicolor_T)0) 1897 XtVaGetValues(sb->id, 1898 XmNbackground, &gui.scroll_def_bg_pixel, 1899 XmNforeground, &gui.scroll_def_fg_pixel, 1900 NULL); 1901 1902 if (sb->id != (Widget)0) 1903 { 1904 gui_mch_set_scrollbar_colors(sb); 1905 XtAddCallback(sb->id, XmNvalueChangedCallback, 1906 scroll_cb, (XtPointer)sb->ident); 1907 XtAddCallback(sb->id, XmNdragCallback, 1908 scroll_cb, (XtPointer)sb->ident); 1909 XtAddEventHandler(sb->id, KeyPressMask, FALSE, gui_x11_key_hit_cb, 1910 (XtPointer)0); 1911 } 1912 } 1913 1914 #if defined(FEAT_WINDOWS) || defined(PROTO) 1915 void 1916 gui_mch_destroy_scrollbar(sb) 1917 scrollbar_T *sb; 1918 { 1919 if (sb->id != (Widget)0) 1920 XtDestroyWidget(sb->id); 1921 } 1922 #endif 1923 1924 void 1925 gui_mch_set_scrollbar_colors(sb) 1926 scrollbar_T *sb; 1927 { 1928 if (sb->id != (Widget)0) 1929 { 1930 if (gui.scroll_bg_pixel != INVALCOLOR) 1931 { 1932 #if (XmVersion>=1002) 1933 XmChangeColor(sb->id, gui.scroll_bg_pixel); 1934 #else 1935 XtVaSetValues(sb->id, 1936 XmNtroughColor, gui.scroll_bg_pixel, 1937 NULL); 1938 #endif 1939 } 1940 1941 if (gui.scroll_fg_pixel != INVALCOLOR) 1942 XtVaSetValues(sb->id, 1943 XmNforeground, gui.scroll_fg_pixel, 1944 #if (XmVersion<1002) 1945 XmNbackground, gui.scroll_fg_pixel, 1946 #endif 1947 NULL); 1948 } 1949 1950 /* This is needed for the rectangle below the vertical scrollbars. */ 1951 if (sb == &gui.bottom_sbar && textAreaForm != (Widget)0) 1952 gui_motif_scroll_colors(textAreaForm); 1953 } 1954 1955 /* 1956 * Miscellaneous stuff: 1957 */ 1958 1959 Window 1960 gui_x11_get_wid() 1961 { 1962 return(XtWindow(textArea)); 1963 } 1964 1965 /* 1966 * Look for a widget in the widget tree w, with a mnemonic matching keycode. 1967 * When one is found, simulate a button press on that widget and give it the 1968 * keyboard focus. If the mnemonic is on a label, look in the userData field 1969 * of the label to see if it points to another widget, and give that the focus. 1970 */ 1971 static void 1972 do_mnemonic(Widget w, unsigned int keycode) 1973 { 1974 WidgetList children; 1975 int numChildren, i; 1976 Boolean isMenu; 1977 KeySym mnemonic = '\0'; 1978 char mneString[2]; 1979 Widget userData; 1980 unsigned char rowColType; 1981 1982 if (XtIsComposite(w)) 1983 { 1984 if (XtClass(w) == xmRowColumnWidgetClass) 1985 { 1986 XtVaGetValues(w, XmNrowColumnType, &rowColType, NULL); 1987 isMenu = (rowColType != (unsigned char)XmWORK_AREA); 1988 } 1989 else 1990 isMenu = False; 1991 if (!isMenu) 1992 { 1993 XtVaGetValues(w, XmNchildren, &children, XmNnumChildren, 1994 &numChildren, NULL); 1995 for (i = 0; i < numChildren; i++) 1996 do_mnemonic(children[i], keycode); 1997 } 1998 } 1999 else 2000 { 2001 XtVaGetValues(w, XmNmnemonic, &mnemonic, NULL); 2002 if (mnemonic != '\0') 2003 { 2004 mneString[0] = mnemonic; 2005 mneString[1] = '\0'; 2006 if (XKeysymToKeycode(XtDisplay(XtParent(w)), 2007 XStringToKeysym(mneString)) == keycode) 2008 { 2009 if (XtClass(w) == xmLabelWidgetClass 2010 || XtClass(w) == xmLabelGadgetClass) 2011 { 2012 XtVaGetValues(w, XmNuserData, &userData, NULL); 2013 if (userData != NULL && XtIsWidget(userData)) 2014 XmProcessTraversal(userData, XmTRAVERSE_CURRENT); 2015 } 2016 else 2017 { 2018 XKeyPressedEvent keyEvent; 2019 2020 XmProcessTraversal(w, XmTRAVERSE_CURRENT); 2021 2022 vim_memset((char *) &keyEvent, 0, sizeof(XKeyPressedEvent)); 2023 keyEvent.type = KeyPress; 2024 keyEvent.serial = 1; 2025 keyEvent.send_event = True; 2026 keyEvent.display = XtDisplay(w); 2027 keyEvent.window = XtWindow(w); 2028 XtCallActionProc(w, "Activate", (XEvent *) & keyEvent, 2029 NULL, 0); 2030 } 2031 } 2032 } 2033 } 2034 } 2035 2036 /* 2037 * Callback routine for dialog mnemonic processing. 2038 */ 2039 static void 2040 mnemonic_event(Widget w, XtPointer call_data UNUSED, XKeyEvent *event) 2041 { 2042 do_mnemonic(w, event->keycode); 2043 } 2044 2045 2046 /* 2047 * Search the widget tree under w for widgets with mnemonics. When found, add 2048 * a passive grab to the dialog widget for the mnemonic character, thus 2049 * directing mnemonic events to the dialog widget. 2050 */ 2051 static void 2052 add_mnemonic_grabs(Widget dialog, Widget w) 2053 { 2054 char mneString[2]; 2055 WidgetList children; 2056 int numChildren, i; 2057 Boolean isMenu; 2058 KeySym mnemonic = '\0'; 2059 unsigned char rowColType; 2060 2061 if (XtIsComposite(w)) 2062 { 2063 if (XtClass(w) == xmRowColumnWidgetClass) 2064 { 2065 XtVaGetValues(w, XmNrowColumnType, &rowColType, NULL); 2066 isMenu = (rowColType != (unsigned char)XmWORK_AREA); 2067 } 2068 else 2069 isMenu = False; 2070 if (!isMenu) 2071 { 2072 XtVaGetValues(w, XmNchildren, &children, XmNnumChildren, 2073 &numChildren, NULL); 2074 for (i = 0; i < numChildren; i++) 2075 add_mnemonic_grabs(dialog, children[i]); 2076 } 2077 } 2078 else 2079 { 2080 XtVaGetValues(w, XmNmnemonic, &mnemonic, NULL); 2081 if (mnemonic != '\0') 2082 { 2083 mneString[0] = mnemonic; 2084 mneString[1] = '\0'; 2085 XtGrabKey(dialog, XKeysymToKeycode(XtDisplay(dialog), 2086 XStringToKeysym(mneString)), 2087 Mod1Mask, True, GrabModeAsync, GrabModeAsync); 2088 } 2089 } 2090 } 2091 2092 /* 2093 * Add a handler for mnemonics in a dialog. Motif itself only handles 2094 * mnemonics in menus. Mnemonics added or changed after this call will be 2095 * ignored. 2096 * 2097 * To add a mnemonic to a text field or list, set the XmNmnemonic resource on 2098 * the appropriate label and set the XmNuserData resource of the label to the 2099 * widget to get the focus when the mnemonic is typed. 2100 */ 2101 static void 2102 activate_dialog_mnemonics(Widget dialog) 2103 { 2104 if (!dialog) 2105 return; 2106 2107 XtAddEventHandler(dialog, KeyPressMask, False, 2108 (XtEventHandler) mnemonic_event, (XtPointer) NULL); 2109 add_mnemonic_grabs(dialog, dialog); 2110 } 2111 2112 /* 2113 * Removes the event handler and key-grabs for dialog mnemonic handling. 2114 */ 2115 static void 2116 suppress_dialog_mnemonics(Widget dialog) 2117 { 2118 if (!dialog) 2119 return; 2120 2121 XtUngrabKey(dialog, AnyKey, Mod1Mask); 2122 XtRemoveEventHandler(dialog, KeyPressMask, False, 2123 (XtEventHandler) mnemonic_event, (XtPointer) NULL); 2124 } 2125 2126 #if defined(FEAT_BROWSE) || defined(FEAT_GUI_DIALOG) 2127 static void set_fontlist __ARGS((Widget wg)); 2128 2129 /* 2130 * Use the 'guifont' or 'guifontset' as a fontlist for a dialog widget. 2131 */ 2132 static void 2133 set_fontlist(id) 2134 Widget id; 2135 { 2136 XmFontList fl; 2137 2138 #ifdef FONTSET_ALWAYS 2139 if (gui.fontset != NOFONTSET) 2140 { 2141 fl = gui_motif_fontset2fontlist((XFontSet *)&gui.fontset); 2142 if (fl != NULL) 2143 { 2144 if (XtIsManaged(id)) 2145 { 2146 XtUnmanageChild(id); 2147 XtVaSetValues(id, XmNfontList, fl, NULL); 2148 /* We should force the widget to recalculate it's 2149 * geometry now. */ 2150 XtManageChild(id); 2151 } 2152 else 2153 XtVaSetValues(id, XmNfontList, fl, NULL); 2154 XmFontListFree(fl); 2155 } 2156 } 2157 #else 2158 if (gui.norm_font != NOFONT) 2159 { 2160 fl = gui_motif_create_fontlist((XFontStruct *)gui.norm_font); 2161 if (fl != NULL) 2162 { 2163 if (XtIsManaged(id)) 2164 { 2165 XtUnmanageChild(id); 2166 XtVaSetValues(id, XmNfontList, fl, NULL); 2167 /* We should force the widget to recalculate it's 2168 * geometry now. */ 2169 XtManageChild(id); 2170 } 2171 else 2172 XtVaSetValues(id, XmNfontList, fl, NULL); 2173 XmFontListFree(fl); 2174 } 2175 } 2176 #endif 2177 } 2178 #endif 2179 2180 #if defined(FEAT_BROWSE) || defined(PROTO) 2181 2182 /* 2183 * file selector related stuff 2184 */ 2185 2186 #include <Xm/FileSB.h> 2187 #include <Xm/XmStrDefs.h> 2188 2189 typedef struct dialog_callback_arg 2190 { 2191 char * args; /* not used right now */ 2192 int id; 2193 } dcbarg_T; 2194 2195 static Widget dialog_wgt; 2196 static char *browse_fname = NULL; 2197 static XmStringCharSet charset = (XmStringCharSet) XmSTRING_DEFAULT_CHARSET; 2198 /* used to set up XmStrings */ 2199 2200 static void DialogCancelCB __ARGS((Widget, XtPointer, XtPointer)); 2201 static void DialogAcceptCB __ARGS((Widget, XtPointer, XtPointer)); 2202 2203 /* 2204 * This function is used to translate the predefined label text of the 2205 * precomposed dialogs. We do this explicitly to allow: 2206 * 2207 * - usage of gettext for translation, as in all the other places. 2208 * 2209 * - equalize the messages between different GUI implementations as far as 2210 * possible. 2211 */ 2212 static void set_predefined_label __ARGS((Widget parent, String name, char *new_label)); 2213 2214 static void 2215 set_predefined_label(parent, name, new_label) 2216 Widget parent; 2217 String name; 2218 char *new_label; 2219 { 2220 XmString str; 2221 Widget w; 2222 char_u *p, *next; 2223 KeySym mnemonic = NUL; 2224 2225 w = XtNameToWidget(parent, name); 2226 2227 if (!w) 2228 return; 2229 2230 p = vim_strsave((char_u *)new_label); 2231 if (p == NULL) 2232 return; 2233 for (next = p; *next; ++next) 2234 { 2235 if (*next == DLG_HOTKEY_CHAR) 2236 { 2237 int len = STRLEN(next); 2238 2239 if (len > 0) 2240 { 2241 mch_memmove(next, next + 1, len); 2242 mnemonic = next[0]; 2243 } 2244 } 2245 } 2246 2247 str = XmStringCreate((char *)p, STRING_TAG); 2248 vim_free(p); 2249 2250 if (str != NULL) 2251 { 2252 XtVaSetValues(w, 2253 XmNlabelString, str, 2254 XmNmnemonic, mnemonic, 2255 NULL); 2256 XmStringFree(str); 2257 } 2258 gui_motif_menu_fontlist(w); 2259 } 2260 2261 static void 2262 set_predefined_fontlist(parent, name) 2263 Widget parent; 2264 String name; 2265 { 2266 Widget w; 2267 w = XtNameToWidget(parent, name); 2268 2269 if (!w) 2270 return; 2271 2272 set_fontlist(w); 2273 } 2274 2275 /* 2276 * Put up a file requester. 2277 * Returns the selected name in allocated memory, or NULL for Cancel. 2278 */ 2279 char_u * 2280 gui_mch_browse(saving, title, dflt, ext, initdir, filter) 2281 int saving UNUSED; /* select file to write */ 2282 char_u *title; /* title for the window */ 2283 char_u *dflt; /* default name */ 2284 char_u *ext UNUSED; /* not used (extension added) */ 2285 char_u *initdir; /* initial directory, NULL for current dir */ 2286 char_u *filter; /* file name filter */ 2287 { 2288 char_u dirbuf[MAXPATHL]; 2289 char_u dfltbuf[MAXPATHL]; 2290 char_u *pattern; 2291 char_u *tofree = NULL; 2292 2293 /* There a difference between the resource name and value, Therefore, we 2294 * avoid to (ab-)use the (maybe internationalized!) dialog title as a 2295 * dialog name. 2296 */ 2297 2298 dialog_wgt = XmCreateFileSelectionDialog(vimShell, "browseDialog", NULL, 0); 2299 2300 if (initdir == NULL || *initdir == NUL) 2301 { 2302 mch_dirname(dirbuf, MAXPATHL); 2303 initdir = dirbuf; 2304 } 2305 2306 if (dflt == NULL) 2307 dflt = (char_u *)""; 2308 else if (STRLEN(initdir) + STRLEN(dflt) + 2 < MAXPATHL) 2309 { 2310 /* The default selection should be the full path, "dflt" is only the 2311 * file name. */ 2312 STRCPY(dfltbuf, initdir); 2313 add_pathsep(dfltbuf); 2314 STRCAT(dfltbuf, dflt); 2315 dflt = dfltbuf; 2316 } 2317 2318 /* Can only use one pattern for a file name. Get the first pattern out of 2319 * the filter. An empty pattern means everything matches. */ 2320 if (filter == NULL) 2321 pattern = (char_u *)""; 2322 else 2323 { 2324 char_u *s, *p; 2325 2326 s = filter; 2327 for (p = filter; *p != NUL; ++p) 2328 { 2329 if (*p == '\t') /* end of description, start of pattern */ 2330 s = p + 1; 2331 if (*p == ';' || *p == '\n') /* end of (first) pattern */ 2332 break; 2333 } 2334 pattern = vim_strnsave(s, p - s); 2335 tofree = pattern; 2336 if (pattern == NULL) 2337 pattern = (char_u *)""; 2338 } 2339 2340 XtVaSetValues(dialog_wgt, 2341 XtVaTypedArg, 2342 XmNdirectory, XmRString, (char *)initdir, STRLEN(initdir) + 1, 2343 XtVaTypedArg, 2344 XmNdirSpec, XmRString, (char *)dflt, STRLEN(dflt) + 1, 2345 XtVaTypedArg, 2346 XmNpattern, XmRString, (char *)pattern, STRLEN(pattern) + 1, 2347 XtVaTypedArg, 2348 XmNdialogTitle, XmRString, (char *)title, STRLEN(title) + 1, 2349 NULL); 2350 2351 set_predefined_label(dialog_wgt, "Apply", _("&Filter")); 2352 set_predefined_label(dialog_wgt, "Cancel", _("&Cancel")); 2353 set_predefined_label(dialog_wgt, "Dir", _("Directories")); 2354 set_predefined_label(dialog_wgt, "FilterLabel", _("Filter")); 2355 set_predefined_label(dialog_wgt, "Help", _("&Help")); 2356 set_predefined_label(dialog_wgt, "Items", _("Files")); 2357 set_predefined_label(dialog_wgt, "OK", _("&OK")); 2358 set_predefined_label(dialog_wgt, "Selection", _("Selection")); 2359 2360 /* This is to save us from silly external settings using not fixed with 2361 * fonts for file selection. 2362 */ 2363 set_predefined_fontlist(dialog_wgt, "DirListSW.DirList"); 2364 set_predefined_fontlist(dialog_wgt, "ItemsListSW.ItemsList"); 2365 2366 gui_motif_menu_colors(dialog_wgt); 2367 if (gui.scroll_bg_pixel != INVALCOLOR) 2368 XtVaSetValues(dialog_wgt, XmNtroughColor, gui.scroll_bg_pixel, NULL); 2369 2370 XtAddCallback(dialog_wgt, XmNokCallback, DialogAcceptCB, (XtPointer)0); 2371 XtAddCallback(dialog_wgt, XmNcancelCallback, DialogCancelCB, (XtPointer)0); 2372 /* We have no help in this window, so hide help button */ 2373 XtUnmanageChild(XmFileSelectionBoxGetChild(dialog_wgt, 2374 (unsigned char)XmDIALOG_HELP_BUTTON)); 2375 2376 manage_centered(dialog_wgt); 2377 activate_dialog_mnemonics(dialog_wgt); 2378 2379 /* sit in a loop until the dialog box has gone away */ 2380 do 2381 { 2382 XtAppProcessEvent(XtWidgetToApplicationContext(dialog_wgt), 2383 (XtInputMask)XtIMAll); 2384 } while (XtIsManaged(dialog_wgt)); 2385 2386 suppress_dialog_mnemonics(dialog_wgt); 2387 XtDestroyWidget(dialog_wgt); 2388 vim_free(tofree); 2389 2390 if (browse_fname == NULL) 2391 return NULL; 2392 return vim_strsave((char_u *)browse_fname); 2393 } 2394 2395 /* 2396 * The code below was originally taken from 2397 * /usr/examples/motif/xmsamplers/xmeditor.c 2398 * on Digital Unix 4.0d, but heavily modified. 2399 */ 2400 2401 /* 2402 * Process callback from Dialog cancel actions. 2403 */ 2404 static void 2405 DialogCancelCB(w, client_data, call_data) 2406 Widget w UNUSED; /* widget id */ 2407 XtPointer client_data UNUSED; /* data from application */ 2408 XtPointer call_data UNUSED; /* data from widget class */ 2409 { 2410 if (browse_fname != NULL) 2411 { 2412 XtFree(browse_fname); 2413 browse_fname = NULL; 2414 } 2415 XtUnmanageChild(dialog_wgt); 2416 } 2417 2418 /* 2419 * Process callback from Dialog actions. 2420 */ 2421 static void 2422 DialogAcceptCB(w, client_data, call_data) 2423 Widget w UNUSED; /* widget id */ 2424 XtPointer client_data UNUSED; /* data from application */ 2425 XtPointer call_data; /* data from widget class */ 2426 { 2427 XmFileSelectionBoxCallbackStruct *fcb; 2428 2429 if (browse_fname != NULL) 2430 { 2431 XtFree(browse_fname); 2432 browse_fname = NULL; 2433 } 2434 fcb = (XmFileSelectionBoxCallbackStruct *)call_data; 2435 2436 /* get the filename from the file selection box */ 2437 XmStringGetLtoR(fcb->value, charset, &browse_fname); 2438 2439 /* popdown the file selection box */ 2440 XtUnmanageChild(dialog_wgt); 2441 } 2442 2443 #endif /* FEAT_BROWSE */ 2444 2445 #if defined(FEAT_GUI_DIALOG) || defined(PROTO) 2446 2447 static int dialogStatus; 2448 2449 static void keyhit_callback __ARGS((Widget w, XtPointer client_data, XEvent *event, Boolean *cont)); 2450 static void butproc __ARGS((Widget w, XtPointer client_data, XtPointer call_data)); 2451 2452 /* 2453 * Callback function for the textfield. When CR is hit this works like 2454 * hitting the "OK" button, ESC like "Cancel". 2455 */ 2456 static void 2457 keyhit_callback(w, client_data, event, cont) 2458 Widget w; 2459 XtPointer client_data UNUSED; 2460 XEvent *event; 2461 Boolean *cont UNUSED; 2462 { 2463 char buf[2]; 2464 KeySym key_sym; 2465 2466 if (XLookupString(&(event->xkey), buf, 2, &key_sym, NULL) == 1) 2467 { 2468 if (*buf == CAR) 2469 dialogStatus = 1; 2470 else if (*buf == ESC) 2471 dialogStatus = 2; 2472 } 2473 if ((key_sym == XK_Left || key_sym == XK_Right) 2474 && !(event->xkey.state & ShiftMask)) 2475 XmTextFieldClearSelection(w, XtLastTimestampProcessed(gui.dpy)); 2476 } 2477 2478 static void 2479 butproc(w, client_data, call_data) 2480 Widget w UNUSED; 2481 XtPointer client_data; 2482 XtPointer call_data UNUSED; 2483 { 2484 dialogStatus = (int)(long)client_data + 1; 2485 } 2486 2487 #ifdef HAVE_XPM 2488 2489 static Widget create_pixmap_label(Widget parent, String name, char **data, ArgList args, Cardinal arg); 2490 2491 static Widget 2492 create_pixmap_label(parent, name, data, args, arg) 2493 Widget parent; 2494 String name; 2495 char **data; 2496 ArgList args; 2497 Cardinal arg; 2498 { 2499 Widget label; 2500 Display *dsp; 2501 Screen *scr; 2502 int depth; 2503 Pixmap pixmap = 0; 2504 XpmAttributes attr; 2505 Boolean rs; 2506 XpmColorSymbol color[5] = 2507 { 2508 {"none", NULL, 0}, 2509 {"iconColor1", NULL, 0}, 2510 {"bottomShadowColor", NULL, 0}, 2511 {"topShadowColor", NULL, 0}, 2512 {"selectColor", NULL, 0} 2513 }; 2514 2515 label = XmCreateLabelGadget(parent, name, args, arg); 2516 2517 /* 2518 * We need to be careful here, since in case of gadgets, there is 2519 * no way to get the background color directly from the widget itself. 2520 * In such cases we get it from The Core part of his parent instead. 2521 */ 2522 dsp = XtDisplayOfObject(label); 2523 scr = XtScreenOfObject(label); 2524 XtVaGetValues(XtIsSubclass(label, coreWidgetClass) 2525 ? label : XtParent(label), 2526 XmNdepth, &depth, 2527 XmNbackground, &color[0].pixel, 2528 XmNforeground, &color[1].pixel, 2529 XmNbottomShadowColor, &color[2].pixel, 2530 XmNtopShadowColor, &color[3].pixel, 2531 XmNhighlight, &color[4].pixel, 2532 NULL); 2533 2534 attr.valuemask = XpmColorSymbols | XpmCloseness | XpmDepth; 2535 attr.colorsymbols = color; 2536 attr.numsymbols = 5; 2537 attr.closeness = 65535; 2538 attr.depth = depth; 2539 XpmCreatePixmapFromData(dsp, RootWindowOfScreen(scr), 2540 data, &pixmap, NULL, &attr); 2541 2542 XtVaGetValues(label, XmNrecomputeSize, &rs, NULL); 2543 XtVaSetValues(label, XmNrecomputeSize, True, NULL); 2544 XtVaSetValues(label, 2545 XmNlabelType, XmPIXMAP, 2546 XmNlabelPixmap, pixmap, 2547 NULL); 2548 XtVaSetValues(label, XmNrecomputeSize, rs, NULL); 2549 2550 return label; 2551 } 2552 #endif 2553 2554 int 2555 gui_mch_dialog(type, title, message, button_names, dfltbutton, textfield, ex_cmd) 2556 int type UNUSED; 2557 char_u *title; 2558 char_u *message; 2559 char_u *button_names; 2560 int dfltbutton; 2561 char_u *textfield; /* buffer of size IOSIZE */ 2562 int ex_cmd UNUSED; 2563 { 2564 char_u *buts; 2565 char_u *p, *next; 2566 XtAppContext app; 2567 XmString label; 2568 int butcount; 2569 Widget w; 2570 Widget dialogform = NULL; 2571 Widget form = NULL; 2572 Widget dialogtextfield = NULL; 2573 Widget *buttons; 2574 Widget sep_form = NULL; 2575 Boolean vertical; 2576 Widget separator = NULL; 2577 int n; 2578 Arg args[6]; 2579 #ifdef HAVE_XPM 2580 char **icon_data = NULL; 2581 Widget dialogpixmap = NULL; 2582 #endif 2583 2584 if (title == NULL) 2585 title = (char_u *)_("Vim dialog"); 2586 2587 /* if our pointer is currently hidden, then we should show it. */ 2588 gui_mch_mousehide(FALSE); 2589 2590 dialogform = XmCreateFormDialog(vimShell, (char *)"dialog", NULL, 0); 2591 2592 /* Check 'v' flag in 'guioptions': vertical button placement. */ 2593 vertical = (vim_strchr(p_go, GO_VERTICAL) != NULL); 2594 2595 /* Set the title of the Dialog window */ 2596 label = XmStringCreateSimple((char *)title); 2597 if (label == NULL) 2598 return -1; 2599 XtVaSetValues(dialogform, 2600 XmNdialogTitle, label, 2601 XmNhorizontalSpacing, 4, 2602 XmNverticalSpacing, vertical ? 0 : 4, 2603 NULL); 2604 XmStringFree(label); 2605 2606 /* make a copy, so that we can insert NULs */ 2607 buts = vim_strsave(button_names); 2608 if (buts == NULL) 2609 return -1; 2610 2611 /* Count the number of buttons and allocate buttons[]. */ 2612 butcount = 1; 2613 for (p = buts; *p; ++p) 2614 if (*p == DLG_BUTTON_SEP) 2615 ++butcount; 2616 buttons = (Widget *)alloc((unsigned)(butcount * sizeof(Widget))); 2617 if (buttons == NULL) 2618 { 2619 vim_free(buts); 2620 return -1; 2621 } 2622 2623 /* 2624 * Create the buttons. 2625 */ 2626 sep_form = (Widget) 0; 2627 p = buts; 2628 for (butcount = 0; *p; ++butcount) 2629 { 2630 KeySym mnemonic = NUL; 2631 2632 for (next = p; *next; ++next) 2633 { 2634 if (*next == DLG_HOTKEY_CHAR) 2635 { 2636 int len = STRLEN(next); 2637 2638 if (len > 0) 2639 { 2640 mch_memmove(next, next + 1, len); 2641 mnemonic = next[0]; 2642 } 2643 } 2644 if (*next == DLG_BUTTON_SEP) 2645 { 2646 *next++ = NUL; 2647 break; 2648 } 2649 } 2650 label = XmStringCreate(_((char *)p), STRING_TAG); 2651 if (label == NULL) 2652 break; 2653 2654 buttons[butcount] = XtVaCreateManagedWidget("button", 2655 xmPushButtonWidgetClass, dialogform, 2656 XmNlabelString, label, 2657 XmNmnemonic, mnemonic, 2658 XmNbottomAttachment, XmATTACH_FORM, 2659 XmNbottomOffset, 4, 2660 XmNshowAsDefault, butcount == dfltbutton - 1, 2661 XmNdefaultButtonShadowThickness, 1, 2662 NULL); 2663 XmStringFree(label); 2664 gui_motif_menu_fontlist(buttons[butcount]); 2665 2666 /* Layout properly. */ 2667 2668 if (butcount > 0) 2669 { 2670 if (vertical) 2671 XtVaSetValues(buttons[butcount], 2672 XmNtopWidget, buttons[butcount - 1], 2673 NULL); 2674 else 2675 { 2676 if (*next == NUL) 2677 { 2678 XtVaSetValues(buttons[butcount], 2679 XmNrightAttachment, XmATTACH_FORM, 2680 XmNrightOffset, 4, 2681 NULL); 2682 2683 /* fill in a form as invisible separator */ 2684 sep_form = XtVaCreateWidget("separatorForm", 2685 xmFormWidgetClass, dialogform, 2686 XmNleftAttachment, XmATTACH_WIDGET, 2687 XmNleftWidget, buttons[butcount - 1], 2688 XmNrightAttachment, XmATTACH_WIDGET, 2689 XmNrightWidget, buttons[butcount], 2690 XmNbottomAttachment, XmATTACH_FORM, 2691 XmNbottomOffset, 4, 2692 NULL); 2693 XtManageChild(sep_form); 2694 } 2695 else 2696 { 2697 XtVaSetValues(buttons[butcount], 2698 XmNleftAttachment, XmATTACH_WIDGET, 2699 XmNleftWidget, buttons[butcount - 1], 2700 NULL); 2701 } 2702 } 2703 } 2704 else if (!vertical) 2705 { 2706 if (*next == NUL) 2707 { 2708 XtVaSetValues(buttons[0], 2709 XmNrightAttachment, XmATTACH_FORM, 2710 XmNrightOffset, 4, 2711 NULL); 2712 2713 /* fill in a form as invisible separator */ 2714 sep_form = XtVaCreateWidget("separatorForm", 2715 xmFormWidgetClass, dialogform, 2716 XmNleftAttachment, XmATTACH_FORM, 2717 XmNleftOffset, 4, 2718 XmNrightAttachment, XmATTACH_WIDGET, 2719 XmNrightWidget, buttons[0], 2720 XmNbottomAttachment, XmATTACH_FORM, 2721 XmNbottomOffset, 4, 2722 NULL); 2723 XtManageChild(sep_form); 2724 } 2725 else 2726 XtVaSetValues(buttons[0], 2727 XmNleftAttachment, XmATTACH_FORM, 2728 XmNleftOffset, 4, 2729 NULL); 2730 } 2731 2732 XtAddCallback(buttons[butcount], XmNactivateCallback, 2733 (XtCallbackProc)butproc, (XtPointer)(long)butcount); 2734 p = next; 2735 } 2736 vim_free(buts); 2737 2738 separator = (Widget) 0; 2739 if (butcount > 0) 2740 { 2741 /* Create the separator for beauty. */ 2742 n = 0; 2743 XtSetArg(args[n], XmNorientation, XmHORIZONTAL); n++; 2744 XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++; 2745 XtSetArg(args[n], XmNbottomWidget, buttons[0]); n++; 2746 XtSetArg(args[n], XmNbottomOffset, 4); n++; 2747 XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++; 2748 XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++; 2749 separator = XmCreateSeparatorGadget(dialogform, "separator", args, n); 2750 XtManageChild(separator); 2751 } 2752 2753 if (textfield != NULL) 2754 { 2755 dialogtextfield = XtVaCreateWidget("textField", 2756 xmTextFieldWidgetClass, dialogform, 2757 XmNleftAttachment, XmATTACH_FORM, 2758 XmNrightAttachment, XmATTACH_FORM, 2759 NULL); 2760 if (butcount > 0) 2761 XtVaSetValues(dialogtextfield, 2762 XmNbottomAttachment, XmATTACH_WIDGET, 2763 XmNbottomWidget, separator, 2764 NULL); 2765 else 2766 XtVaSetValues(dialogtextfield, 2767 XmNbottomAttachment, XmATTACH_FORM, 2768 NULL); 2769 2770 set_fontlist(dialogtextfield); 2771 XmTextFieldSetString(dialogtextfield, (char *)textfield); 2772 XtManageChild(dialogtextfield); 2773 XtAddEventHandler(dialogtextfield, KeyPressMask, False, 2774 (XtEventHandler)keyhit_callback, (XtPointer)NULL); 2775 } 2776 2777 /* Form holding both message and pixmap labels */ 2778 form = XtVaCreateWidget("separatorForm", 2779 xmFormWidgetClass, dialogform, 2780 XmNleftAttachment, XmATTACH_FORM, 2781 XmNrightAttachment, XmATTACH_FORM, 2782 XmNtopAttachment, XmATTACH_FORM, 2783 NULL); 2784 XtManageChild(form); 2785 2786 #ifdef HAVE_XPM 2787 /* Add a pixmap, left of the message. */ 2788 switch (type) 2789 { 2790 case VIM_GENERIC: 2791 icon_data = generic_xpm; 2792 break; 2793 case VIM_ERROR: 2794 icon_data = error_xpm; 2795 break; 2796 case VIM_WARNING: 2797 icon_data = alert_xpm; 2798 break; 2799 case VIM_INFO: 2800 icon_data = info_xpm; 2801 break; 2802 case VIM_QUESTION: 2803 icon_data = quest_xpm; 2804 break; 2805 default: 2806 icon_data = generic_xpm; 2807 } 2808 2809 n = 0; 2810 XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++; 2811 XtSetArg(args[n], XmNtopOffset, 8); n++; 2812 XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++; 2813 XtSetArg(args[n], XmNbottomOffset, 8); n++; 2814 XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++; 2815 XtSetArg(args[n], XmNleftOffset, 8); n++; 2816 2817 dialogpixmap = create_pixmap_label(form, "dialogPixmap", 2818 icon_data, args, n); 2819 XtManageChild(dialogpixmap); 2820 #endif 2821 2822 /* Create the dialog message. 2823 * Since LessTif is apparently having problems with the creation of 2824 * properly localized string, we use LtoR here. The symptom is that the 2825 * string sill not show properly in multiple lines as it does in native 2826 * Motif. 2827 */ 2828 label = XmStringCreateLtoR((char *)message, STRING_TAG); 2829 if (label == NULL) 2830 return -1; 2831 w = XtVaCreateManagedWidget("dialogMessage", 2832 xmLabelGadgetClass, form, 2833 XmNlabelString, label, 2834 XmNalignment, XmALIGNMENT_BEGINNING, 2835 XmNtopAttachment, XmATTACH_FORM, 2836 XmNtopOffset, 8, 2837 #ifdef HAVE_XPM 2838 XmNleftAttachment, XmATTACH_WIDGET, 2839 XmNleftWidget, dialogpixmap, 2840 #else 2841 XmNleftAttachment, XmATTACH_FORM, 2842 #endif 2843 XmNleftOffset, 8, 2844 XmNrightAttachment, XmATTACH_FORM, 2845 XmNrightOffset, 8, 2846 XmNbottomAttachment, XmATTACH_FORM, 2847 XmNbottomOffset, 8, 2848 NULL); 2849 XmStringFree(label); 2850 set_fontlist(w); 2851 2852 if (textfield != NULL) 2853 { 2854 XtVaSetValues(form, 2855 XmNbottomAttachment, XmATTACH_WIDGET, 2856 XmNbottomWidget, dialogtextfield, 2857 NULL); 2858 } 2859 else 2860 { 2861 if (butcount > 0) 2862 XtVaSetValues(form, 2863 XmNbottomAttachment, XmATTACH_WIDGET, 2864 XmNbottomWidget, separator, 2865 NULL); 2866 else 2867 XtVaSetValues(form, 2868 XmNbottomAttachment, XmATTACH_FORM, 2869 NULL); 2870 } 2871 2872 if (dfltbutton < 1) 2873 dfltbutton = 1; 2874 if (dfltbutton > butcount) 2875 dfltbutton = butcount; 2876 XtVaSetValues(dialogform, 2877 XmNdefaultButton, buttons[dfltbutton - 1], NULL); 2878 if (textfield != NULL) 2879 XtVaSetValues(dialogform, XmNinitialFocus, dialogtextfield, NULL); 2880 else 2881 XtVaSetValues(dialogform, XmNinitialFocus, buttons[dfltbutton - 1], 2882 NULL); 2883 2884 manage_centered(dialogform); 2885 activate_dialog_mnemonics(dialogform); 2886 2887 if (textfield != NULL && *textfield != NUL) 2888 { 2889 /* This only works after the textfield has been realised. */ 2890 XmTextFieldSetSelection(dialogtextfield, 2891 (XmTextPosition)0, (XmTextPosition)STRLEN(textfield), 2892 XtLastTimestampProcessed(gui.dpy)); 2893 XmTextFieldSetCursorPosition(dialogtextfield, 2894 (XmTextPosition)STRLEN(textfield)); 2895 } 2896 2897 app = XtWidgetToApplicationContext(dialogform); 2898 2899 /* Loop until a button is pressed or the dialog is killed somehow. */ 2900 dialogStatus = -1; 2901 for (;;) 2902 { 2903 XtAppProcessEvent(app, (XtInputMask)XtIMAll); 2904 if (dialogStatus >= 0 || !XtIsManaged(dialogform)) 2905 break; 2906 } 2907 2908 vim_free(buttons); 2909 2910 if (textfield != NULL) 2911 { 2912 p = (char_u *)XmTextGetString(dialogtextfield); 2913 if (p == NULL || dialogStatus < 0) 2914 *textfield = NUL; 2915 else 2916 vim_strncpy(textfield, p, IOSIZE - 1); 2917 XtFree((char *)p); 2918 } 2919 2920 suppress_dialog_mnemonics(dialogform); 2921 XtDestroyWidget(dialogform); 2922 2923 return dialogStatus; 2924 } 2925 #endif /* FEAT_GUI_DIALOG */ 2926 2927 #if defined(FEAT_FOOTER) || defined(PROTO) 2928 2929 static int 2930 gui_mch_compute_footer_height() 2931 { 2932 Dimension height; /* total Toolbar height */ 2933 Dimension top; /* XmNmarginTop */ 2934 Dimension bottom; /* XmNmarginBottom */ 2935 Dimension shadow; /* XmNshadowThickness */ 2936 2937 XtVaGetValues(footer, 2938 XmNheight, &height, 2939 XmNmarginTop, &top, 2940 XmNmarginBottom, &bottom, 2941 XmNshadowThickness, &shadow, 2942 NULL); 2943 2944 return (int) height + top + bottom + (shadow << 1); 2945 } 2946 2947 void 2948 gui_mch_enable_footer(showit) 2949 int showit; 2950 { 2951 if (showit) 2952 { 2953 gui.footer_height = gui_mch_compute_footer_height(); 2954 XtManageChild(footer); 2955 } 2956 else 2957 { 2958 gui.footer_height = 0; 2959 XtUnmanageChild(footer); 2960 } 2961 XtVaSetValues(textAreaForm, XmNbottomOffset, gui.footer_height, NULL); 2962 } 2963 2964 void 2965 gui_mch_set_footer(s) 2966 char_u *s; 2967 { 2968 XmString xms; 2969 2970 xms = XmStringCreate((char *)s, STRING_TAG); 2971 if (xms != NULL) 2972 { 2973 XtVaSetValues(footer, XmNlabelString, xms, NULL); 2974 XmStringFree(xms); 2975 } 2976 } 2977 2978 #endif 2979 2980 2981 #if defined(FEAT_TOOLBAR) || defined(PROTO) 2982 void 2983 gui_mch_show_toolbar(int showit) 2984 { 2985 Cardinal numChildren; /* how many children toolBar has */ 2986 2987 if (toolBar == (Widget)0) 2988 return; 2989 XtVaGetValues(toolBar, XmNnumChildren, &numChildren, NULL); 2990 if (showit && numChildren > 0) 2991 { 2992 /* Assume that we want to show the toolbar if p_toolbar contains 2993 * valid option settings, therefore p_toolbar must not be NULL. 2994 */ 2995 WidgetList children; 2996 2997 XtVaGetValues(toolBar, XmNchildren, &children, NULL); 2998 { 2999 void (*action)(BalloonEval *); 3000 int text = 0; 3001 3002 if (strstr((const char *)p_toolbar, "tooltips")) 3003 action = &gui_mch_enable_beval_area; 3004 else 3005 action = &gui_mch_disable_beval_area; 3006 if (strstr((const char *)p_toolbar, "text")) 3007 text = 1; 3008 else if (strstr((const char *)p_toolbar, "icons")) 3009 text = -1; 3010 if (text != 0) 3011 { 3012 vimmenu_T *toolbar; 3013 vimmenu_T *cur; 3014 3015 for (toolbar = root_menu; toolbar; toolbar = toolbar->next) 3016 if (menu_is_toolbar(toolbar->dname)) 3017 break; 3018 /* Assumption: toolbar is NULL if there is no toolbar, 3019 * otherwise it contains the toolbar menu structure. 3020 * 3021 * Assumption: "numChildren" == the number of items in the list 3022 * of items beginning with toolbar->children. 3023 */ 3024 if (toolbar) 3025 { 3026 for (cur = toolbar->children; cur; cur = cur->next) 3027 { 3028 Arg args[1]; 3029 int n = 0; 3030 3031 /* Enable/Disable tooltip (OK to enable while 3032 * currently enabled). */ 3033 if (cur->tip != NULL) 3034 (*action)(cur->tip); 3035 if (!menu_is_separator(cur->name)) 3036 { 3037 if (text == 1 || cur->xpm == NULL) 3038 { 3039 XtSetArg(args[n], XmNlabelType, XmSTRING); 3040 ++n; 3041 } 3042 if (cur->id != NULL) 3043 { 3044 XtUnmanageChild(cur->id); 3045 XtSetValues(cur->id, args, n); 3046 XtManageChild(cur->id); 3047 } 3048 } 3049 } 3050 } 3051 } 3052 } 3053 gui.toolbar_height = gui_mch_compute_toolbar_height(); 3054 XtManageChild(XtParent(toolBar)); 3055 #ifdef FEAT_GUI_TABLINE 3056 if (showing_tabline) 3057 { 3058 XtVaSetValues(tabLine, 3059 XmNtopAttachment, XmATTACH_WIDGET, 3060 XmNtopWidget, XtParent(toolBar), 3061 NULL); 3062 XtVaSetValues(textAreaForm, 3063 XmNtopAttachment, XmATTACH_WIDGET, 3064 XmNtopWidget, tabLine, 3065 NULL); 3066 } 3067 else 3068 #endif 3069 XtVaSetValues(textAreaForm, 3070 XmNtopAttachment, XmATTACH_WIDGET, 3071 XmNtopWidget, XtParent(toolBar), 3072 NULL); 3073 if (XtIsManaged(menuBar)) 3074 XtVaSetValues(XtParent(toolBar), 3075 XmNtopAttachment, XmATTACH_WIDGET, 3076 XmNtopWidget, menuBar, 3077 NULL); 3078 else 3079 XtVaSetValues(XtParent(toolBar), 3080 XmNtopAttachment, XmATTACH_FORM, 3081 NULL); 3082 } 3083 else 3084 { 3085 gui.toolbar_height = 0; 3086 if (XtIsManaged(menuBar)) 3087 { 3088 #ifdef FEAT_GUI_TABLINE 3089 if (showing_tabline) 3090 { 3091 XtVaSetValues(tabLine, 3092 XmNtopAttachment, XmATTACH_WIDGET, 3093 XmNtopWidget, menuBar, 3094 NULL); 3095 XtVaSetValues(textAreaForm, 3096 XmNtopAttachment, XmATTACH_WIDGET, 3097 XmNtopWidget, tabLine, 3098 NULL); 3099 } 3100 else 3101 #endif 3102 XtVaSetValues(textAreaForm, 3103 XmNtopAttachment, XmATTACH_WIDGET, 3104 XmNtopWidget, menuBar, 3105 NULL); 3106 } 3107 else 3108 { 3109 #ifdef FEAT_GUI_TABLINE 3110 if (showing_tabline) 3111 { 3112 XtVaSetValues(tabLine, 3113 XmNtopAttachment, XmATTACH_FORM, 3114 NULL); 3115 XtVaSetValues(textAreaForm, 3116 XmNtopAttachment, XmATTACH_WIDGET, 3117 XmNtopWidget, tabLine, 3118 NULL); 3119 } 3120 else 3121 #endif 3122 XtVaSetValues(textAreaForm, 3123 XmNtopAttachment, XmATTACH_FORM, 3124 NULL); 3125 } 3126 3127 XtUnmanageChild(XtParent(toolBar)); 3128 } 3129 gui_set_shellsize(FALSE, FALSE, RESIZE_VERT); 3130 } 3131 3132 /* 3133 * A toolbar button has been pushed; now reset the input focus 3134 * such that the user can type page up/down etc. and have the 3135 * input go to the editor window, not the button 3136 */ 3137 static void 3138 reset_focus() 3139 { 3140 if (textArea != NULL) 3141 XmProcessTraversal(textArea, XmTRAVERSE_CURRENT); 3142 } 3143 3144 int 3145 gui_mch_compute_toolbar_height() 3146 { 3147 Dimension borders; 3148 Dimension height; /* total Toolbar height */ 3149 Dimension whgt; /* height of each widget */ 3150 WidgetList children; /* list of toolBar's children */ 3151 Cardinal numChildren; /* how many children toolBar has */ 3152 int i; 3153 3154 borders = 0; 3155 height = 0; 3156 if (toolBar != (Widget)0 && toolBarFrame != (Widget)0) 3157 { /* get height of XmFrame parent */ 3158 Dimension fst; 3159 Dimension fmh; 3160 Dimension tst; 3161 Dimension tmh; 3162 3163 XtVaGetValues(toolBarFrame, 3164 XmNshadowThickness, &fst, 3165 XmNmarginHeight, &fmh, 3166 NULL); 3167 borders += fst + fmh; 3168 XtVaGetValues(toolBar, 3169 XmNshadowThickness, &tst, 3170 XmNmarginHeight, &tmh, 3171 XmNchildren, &children, 3172 XmNnumChildren, &numChildren, NULL); 3173 borders += tst + tmh; 3174 for (i = 0; i < (int)numChildren; i++) 3175 { 3176 whgt = 0; 3177 XtVaGetValues(children[i], XmNheight, &whgt, NULL); 3178 if (height < whgt) 3179 height = whgt; 3180 } 3181 } 3182 #ifdef LESSTIF_VERSION 3183 /* Hack: When starting up we get wrong dimensions. */ 3184 if (height < 10) 3185 height = 24; 3186 #endif 3187 3188 return (int)(height + (borders << 1)); 3189 } 3190 3191 void 3192 motif_get_toolbar_colors(bgp, fgp, bsp, tsp, hsp) 3193 Pixel *bgp; 3194 Pixel *fgp; 3195 Pixel *bsp; 3196 Pixel *tsp; 3197 Pixel *hsp; 3198 { 3199 XtVaGetValues(toolBar, 3200 XmNbackground, bgp, 3201 XmNforeground, fgp, 3202 XmNbottomShadowColor, bsp, 3203 XmNtopShadowColor, tsp, 3204 XmNhighlightColor, hsp, 3205 NULL); 3206 } 3207 3208 # ifdef FEAT_FOOTER 3209 /* 3210 * The next toolbar enter/leave callbacks should really do balloon help. But 3211 * I have to use footer help for backwards compatibility. Hopefully both will 3212 * get implemented and the user will have a choice. 3213 */ 3214 static void 3215 toolbarbutton_enter_cb(w, client_data, event, cont) 3216 Widget w UNUSED; 3217 XtPointer client_data; 3218 XEvent *event UNUSED; 3219 Boolean *cont UNUSED; 3220 { 3221 vimmenu_T *menu = (vimmenu_T *) client_data; 3222 3223 if (menu->strings[MENU_INDEX_TIP] != NULL) 3224 { 3225 if (vim_strchr(p_go, GO_FOOTER) != NULL) 3226 gui_mch_set_footer(menu->strings[MENU_INDEX_TIP]); 3227 } 3228 } 3229 3230 static void 3231 toolbarbutton_leave_cb(w, client_data, event, cont) 3232 Widget w UNUSED; 3233 XtPointer client_data UNUSED; 3234 XEvent *event UNUSED; 3235 Boolean *cont UNUSED; 3236 { 3237 gui_mch_set_footer((char_u *) ""); 3238 } 3239 # endif 3240 #endif 3241 3242 #if defined(FEAT_GUI_TABLINE) || defined(PROTO) 3243 /* 3244 * Show or hide the tabline. 3245 */ 3246 void 3247 gui_mch_show_tabline(int showit) 3248 { 3249 if (tabLine == (Widget)0) 3250 return; 3251 3252 if (!showit != !showing_tabline) 3253 { 3254 if (showit) 3255 { 3256 XtManageChild(tabLine); 3257 XtUnmanageChild(XtNameToWidget(tabLine, "PageScroller")); 3258 XtUnmanageChild(XtNameToWidget(tabLine, "MinorTabScrollerNext")); 3259 XtUnmanageChild(XtNameToWidget(tabLine, 3260 "MinorTabScrollerPrevious")); 3261 #ifdef FEAT_MENU 3262 # ifdef FEAT_TOOLBAR 3263 if (XtIsManaged(XtParent(toolBar))) 3264 XtVaSetValues(tabLine, 3265 XmNtopAttachment, XmATTACH_WIDGET, 3266 XmNtopWidget, XtParent(toolBar), NULL); 3267 else 3268 # endif 3269 if (XtIsManaged(menuBar)) 3270 XtVaSetValues(tabLine, 3271 XmNtopAttachment, XmATTACH_WIDGET, 3272 XmNtopWidget, menuBar, NULL); 3273 else 3274 #endif 3275 XtVaSetValues(tabLine, 3276 XmNtopAttachment, XmATTACH_FORM, NULL); 3277 XtVaSetValues(textAreaForm, 3278 XmNtopAttachment, XmATTACH_WIDGET, 3279 XmNtopWidget, tabLine, 3280 NULL); 3281 } 3282 else 3283 { 3284 XtUnmanageChild(tabLine); 3285 #ifdef FEAT_MENU 3286 # ifdef FEAT_TOOLBAR 3287 if (XtIsManaged(XtParent(toolBar))) 3288 XtVaSetValues(textAreaForm, 3289 XmNtopAttachment, XmATTACH_WIDGET, 3290 XmNtopWidget, XtParent(toolBar), NULL); 3291 else 3292 # endif 3293 if (XtIsManaged(menuBar)) 3294 XtVaSetValues(textAreaForm, 3295 XmNtopAttachment, XmATTACH_WIDGET, 3296 XmNtopWidget, menuBar, NULL); 3297 else 3298 #endif 3299 XtVaSetValues(textAreaForm, 3300 XmNtopAttachment, XmATTACH_FORM, NULL); 3301 } 3302 showing_tabline = showit; 3303 } 3304 } 3305 3306 /* 3307 * Return TRUE when tabline is displayed. 3308 */ 3309 int 3310 gui_mch_showing_tabline(void) 3311 { 3312 return tabLine != (Widget)0 && showing_tabline; 3313 } 3314 3315 /* 3316 * Update the labels of the tabline. 3317 */ 3318 void 3319 gui_mch_update_tabline(void) 3320 { 3321 tabpage_T *tp; 3322 int nr = 1, n; 3323 Arg args[10]; 3324 int curtabidx = 0, currentpage; 3325 Widget tab; 3326 XmNotebookPageInfo page_info; 3327 XmNotebookPageStatus page_status; 3328 int last_page, tab_count; 3329 XmString label_str; 3330 char *label_cstr; 3331 BalloonEval *beval; 3332 3333 if (tabLine == (Widget)0) 3334 return; 3335 3336 /* Add a label for each tab page. They all contain the same text area. */ 3337 for (tp = first_tabpage; tp != NULL; tp = tp->tp_next, ++nr) 3338 { 3339 if (tp == curtab) 3340 curtabidx = nr; 3341 3342 page_status = XmNotebookGetPageInfo(tabLine, nr, &page_info); 3343 if (page_status == XmPAGE_INVALID 3344 || page_info.major_tab_widget == (Widget)0) 3345 { 3346 /* Add the tab */ 3347 n = 0; 3348 XtSetArg(args[n], XmNnotebookChildType, XmMAJOR_TAB); n++; 3349 XtSetArg(args[n], XmNtraversalOn, False); n++; 3350 XtSetArg(args[n], XmNalignment, XmALIGNMENT_BEGINNING); n++; 3351 XtSetArg(args[n], XmNhighlightThickness, 1); n++; 3352 XtSetArg(args[n], XmNshadowThickness , 1); n++; 3353 tab = XmCreatePushButton(tabLine, "-Empty-", args, n); 3354 XtManageChild(tab); 3355 beval = gui_mch_create_beval_area(tab, NULL, tabline_balloon_cb, 3356 NULL); 3357 XtVaSetValues(tab, XmNuserData, beval, NULL); 3358 } 3359 else 3360 tab = page_info.major_tab_widget; 3361 3362 XtVaSetValues(tab, XmNpageNumber, nr, NULL); 3363 3364 /* 3365 * Change the label text only if it is different 3366 */ 3367 XtVaGetValues(tab, XmNlabelString, &label_str, NULL); 3368 if (XmStringGetLtoR(label_str, XmSTRING_DEFAULT_CHARSET, &label_cstr)) 3369 { 3370 get_tabline_label(tp, FALSE); 3371 if (STRCMP(label_cstr, NameBuff) != 0) 3372 { 3373 XtVaSetValues(tab, XtVaTypedArg, XmNlabelString, XmRString, 3374 NameBuff, STRLEN(NameBuff) + 1, NULL); 3375 /* 3376 * Force a resize of the tab label button 3377 */ 3378 XtUnmanageChild(tab); 3379 XtManageChild(tab); 3380 } 3381 XtFree(label_cstr); 3382 } 3383 } 3384 3385 tab_count = nr - 1; 3386 3387 XtVaGetValues(tabLine, XmNlastPageNumber, &last_page, NULL); 3388 3389 /* Remove any old labels. */ 3390 while (nr <= last_page) 3391 { 3392 if (XmNotebookGetPageInfo(tabLine, nr, &page_info) != XmPAGE_INVALID 3393 && page_info.page_number == nr 3394 && page_info.major_tab_widget != (Widget)0) 3395 { 3396 XtVaGetValues(page_info.major_tab_widget, XmNuserData, &beval, NULL); 3397 if (beval != NULL) 3398 gui_mch_destroy_beval_area(beval); 3399 XtUnmanageChild(page_info.major_tab_widget); 3400 XtDestroyWidget(page_info.major_tab_widget); 3401 } 3402 nr++; 3403 } 3404 3405 XtVaSetValues(tabLine, XmNlastPageNumber, tab_count, NULL); 3406 3407 XtVaGetValues(tabLine, XmNcurrentPageNumber, ¤tpage, NULL); 3408 if (currentpage != curtabidx) 3409 XtVaSetValues(tabLine, XmNcurrentPageNumber, curtabidx, NULL); 3410 } 3411 3412 /* 3413 * Set the current tab to "nr". First tab is 1. 3414 */ 3415 void 3416 gui_mch_set_curtab(nr) 3417 int nr; 3418 { 3419 int currentpage; 3420 3421 if (tabLine == (Widget)0) 3422 return; 3423 3424 XtVaGetValues(tabLine, XmNcurrentPageNumber, ¤tpage, NULL); 3425 if (currentpage != nr) 3426 XtVaSetValues(tabLine, XmNcurrentPageNumber, nr, NULL); 3427 } 3428 #endif 3429 3430 /* 3431 * Set the colors of Widget "id" to the menu colors. 3432 */ 3433 static void 3434 gui_motif_menu_colors(id) 3435 Widget id; 3436 { 3437 if (gui.menu_bg_pixel != INVALCOLOR) 3438 #if (XmVersion >= 1002) 3439 XmChangeColor(id, gui.menu_bg_pixel); 3440 #else 3441 XtVaSetValues(id, XmNbackground, gui.menu_bg_pixel, NULL); 3442 #endif 3443 if (gui.menu_fg_pixel != INVALCOLOR) 3444 XtVaSetValues(id, XmNforeground, gui.menu_fg_pixel, NULL); 3445 } 3446 3447 /* 3448 * Set the colors of Widget "id" to the scrollbar colors. 3449 */ 3450 static void 3451 gui_motif_scroll_colors(id) 3452 Widget id; 3453 { 3454 if (gui.scroll_bg_pixel != INVALCOLOR) 3455 #if (XmVersion >= 1002) 3456 XmChangeColor(id, gui.scroll_bg_pixel); 3457 #else 3458 XtVaSetValues(id, XmNbackground, gui.scroll_bg_pixel, NULL); 3459 #endif 3460 if (gui.scroll_fg_pixel != INVALCOLOR) 3461 XtVaSetValues(id, XmNforeground, gui.scroll_fg_pixel, NULL); 3462 } 3463 3464 /* 3465 * Set the fontlist for Widget "id" to use gui.menu_fontset or gui.menu_font. 3466 */ 3467 void 3468 gui_motif_menu_fontlist(id) 3469 Widget id UNUSED; 3470 { 3471 #ifdef FEAT_MENU 3472 #ifdef FONTSET_ALWAYS 3473 if (gui.menu_fontset != NOFONTSET) 3474 { 3475 XmFontList fl; 3476 3477 fl = gui_motif_fontset2fontlist((XFontSet *)&gui.menu_fontset); 3478 if (fl != NULL) 3479 { 3480 if (XtIsManaged(id)) 3481 { 3482 XtUnmanageChild(id); 3483 XtVaSetValues(id, XmNfontList, fl, NULL); 3484 /* We should force the widget to recalculate it's 3485 * geometry now. */ 3486 XtManageChild(id); 3487 } 3488 else 3489 XtVaSetValues(id, XmNfontList, fl, NULL); 3490 XmFontListFree(fl); 3491 } 3492 } 3493 #else 3494 if (gui.menu_font != NOFONT) 3495 { 3496 XmFontList fl; 3497 3498 fl = gui_motif_create_fontlist((XFontStruct *)gui.menu_font); 3499 if (fl != NULL) 3500 { 3501 if (XtIsManaged(id)) 3502 { 3503 XtUnmanageChild(id); 3504 XtVaSetValues(id, XmNfontList, fl, NULL); 3505 /* We should force the widget to recalculate it's 3506 * geometry now. */ 3507 XtManageChild(id); 3508 } 3509 else 3510 XtVaSetValues(id, XmNfontList, fl, NULL); 3511 XmFontListFree(fl); 3512 } 3513 } 3514 #endif 3515 #endif 3516 } 3517 3518 3519 /* 3520 * We don't create it twice for the sake of speed. 3521 */ 3522 3523 typedef struct _SharedFindReplace 3524 { 3525 Widget dialog; /* the main dialog widget */ 3526 Widget wword; /* 'Exact match' check button */ 3527 Widget mcase; /* 'match case' check button */ 3528 Widget up; /* search direction 'Up' radio button */ 3529 Widget down; /* search direction 'Down' radio button */ 3530 Widget what; /* 'Find what' entry text widget */ 3531 Widget with; /* 'Replace with' entry text widget */ 3532 Widget find; /* 'Find Next' action button */ 3533 Widget replace; /* 'Replace With' action button */ 3534 Widget all; /* 'Replace All' action button */ 3535 Widget undo; /* 'Undo' action button */ 3536 3537 Widget cancel; 3538 } SharedFindReplace; 3539 3540 static SharedFindReplace find_widgets = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}; 3541 static SharedFindReplace repl_widgets = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}; 3542 3543 static void find_replace_destroy_callback __ARGS((Widget w, XtPointer client_data, XtPointer call_data)); 3544 static void find_replace_dismiss_callback __ARGS((Widget w, XtPointer client_data, XtPointer call_data)); 3545 static void entry_activate_callback __ARGS((Widget w, XtPointer client_data, XtPointer call_data)); 3546 static void find_replace_callback __ARGS((Widget w, XtPointer client_data, XtPointer call_data)); 3547 static void find_replace_keypress __ARGS((Widget w, SharedFindReplace * frdp, XKeyEvent * event)); 3548 static void find_replace_dialog_create __ARGS((char_u *entry_text, int do_replace)); 3549 3550 static void 3551 find_replace_destroy_callback(w, client_data, call_data) 3552 Widget w UNUSED; 3553 XtPointer client_data; 3554 XtPointer call_data UNUSED; 3555 { 3556 SharedFindReplace *cd = (SharedFindReplace *)client_data; 3557 3558 if (cd != NULL) 3559 /* suppress_dialog_mnemonics(cd->dialog); */ 3560 cd->dialog = (Widget)0; 3561 } 3562 3563 static void 3564 find_replace_dismiss_callback(w, client_data, call_data) 3565 Widget w UNUSED; 3566 XtPointer client_data; 3567 XtPointer call_data UNUSED; 3568 { 3569 SharedFindReplace *cd = (SharedFindReplace *)client_data; 3570 3571 if (cd != NULL) 3572 XtUnmanageChild(cd->dialog); 3573 } 3574 3575 static void 3576 entry_activate_callback(w, client_data, call_data) 3577 Widget w UNUSED; 3578 XtPointer client_data; 3579 XtPointer call_data UNUSED; 3580 { 3581 XmProcessTraversal((Widget)client_data, XmTRAVERSE_CURRENT); 3582 } 3583 3584 static void 3585 find_replace_callback(w, client_data, call_data) 3586 Widget w UNUSED; 3587 XtPointer client_data; 3588 XtPointer call_data UNUSED; 3589 { 3590 long_u flags = (long_u)client_data; 3591 char *find_text, *repl_text; 3592 Boolean direction_down = TRUE; 3593 Boolean wword; 3594 Boolean mcase; 3595 SharedFindReplace *sfr; 3596 3597 if (flags == FRD_UNDO) 3598 { 3599 char_u *save_cpo = p_cpo; 3600 3601 /* No need to be Vi compatible here. */ 3602 p_cpo = (char_u *)""; 3603 u_undo(1); 3604 p_cpo = save_cpo; 3605 gui_update_screen(); 3606 return; 3607 } 3608 3609 /* Get the search/replace strings from the dialog */ 3610 if (flags == FRD_FINDNEXT) 3611 { 3612 repl_text = NULL; 3613 sfr = &find_widgets; 3614 } 3615 else 3616 { 3617 repl_text = XmTextFieldGetString(repl_widgets.with); 3618 sfr = &repl_widgets; 3619 } 3620 find_text = XmTextFieldGetString(sfr->what); 3621 XtVaGetValues(sfr->down, XmNset, &direction_down, NULL); 3622 XtVaGetValues(sfr->wword, XmNset, &wword, NULL); 3623 XtVaGetValues(sfr->mcase, XmNset, &mcase, NULL); 3624 if (wword) 3625 flags |= FRD_WHOLE_WORD; 3626 if (mcase) 3627 flags |= FRD_MATCH_CASE; 3628 3629 (void)gui_do_findrepl((int)flags, (char_u *)find_text, (char_u *)repl_text, 3630 direction_down); 3631 3632 if (find_text != NULL) 3633 XtFree(find_text); 3634 if (repl_text != NULL) 3635 XtFree(repl_text); 3636 } 3637 3638 static void 3639 find_replace_keypress(w, frdp, event) 3640 Widget w UNUSED; 3641 SharedFindReplace *frdp; 3642 XKeyEvent *event; 3643 { 3644 KeySym keysym; 3645 3646 if (frdp == NULL) 3647 return; 3648 3649 keysym = XLookupKeysym(event, 0); 3650 3651 /* the scape key pops the whole dialog down */ 3652 if (keysym == XK_Escape) 3653 XtUnmanageChild(frdp->dialog); 3654 } 3655 3656 static void 3657 set_label(w, label) 3658 Widget w; 3659 char *label; 3660 { 3661 XmString str; 3662 char_u *p, *next; 3663 KeySym mnemonic = NUL; 3664 3665 if (!w) 3666 return; 3667 3668 p = vim_strsave((char_u *)label); 3669 if (p == NULL) 3670 return; 3671 for (next = p; *next; ++next) 3672 { 3673 if (*next == DLG_HOTKEY_CHAR) 3674 { 3675 int len = STRLEN(next); 3676 3677 if (len > 0) 3678 { 3679 mch_memmove(next, next + 1, len); 3680 mnemonic = next[0]; 3681 } 3682 } 3683 } 3684 3685 str = XmStringCreateSimple((char *)p); 3686 vim_free(p); 3687 if (str) 3688 { 3689 XtVaSetValues(w, 3690 XmNlabelString, str, 3691 XmNmnemonic, mnemonic, 3692 NULL); 3693 XmStringFree(str); 3694 } 3695 gui_motif_menu_fontlist(w); 3696 } 3697 3698 static void 3699 find_replace_dialog_create(arg, do_replace) 3700 char_u *arg; 3701 int do_replace; 3702 { 3703 SharedFindReplace *frdp; 3704 Widget separator; 3705 Widget input_form; 3706 Widget button_form; 3707 Widget toggle_form; 3708 Widget frame; 3709 XmString str; 3710 int n; 3711 Arg args[6]; 3712 int wword = FALSE; 3713 int mcase = !p_ic; 3714 Dimension width; 3715 Dimension widest; 3716 char_u *entry_text; 3717 3718 frdp = do_replace ? &repl_widgets : &find_widgets; 3719 3720 /* Get the search string to use. */ 3721 entry_text = get_find_dialog_text(arg, &wword, &mcase); 3722 3723 /* If the dialog already exists, just raise it. */ 3724 if (frdp->dialog) 3725 { 3726 gui_motif_synch_fonts(); 3727 3728 /* If the window is already up, just pop it to the top */ 3729 if (XtIsManaged(frdp->dialog)) 3730 XMapRaised(XtDisplay(frdp->dialog), 3731 XtWindow(XtParent(frdp->dialog))); 3732 else 3733 XtManageChild(frdp->dialog); 3734 XtPopup(XtParent(frdp->dialog), XtGrabNone); 3735 XmProcessTraversal(frdp->what, XmTRAVERSE_CURRENT); 3736 3737 if (entry_text != NULL) 3738 XmTextFieldSetString(frdp->what, (char *)entry_text); 3739 vim_free(entry_text); 3740 3741 XtVaSetValues(frdp->wword, XmNset, wword, NULL); 3742 return; 3743 } 3744 3745 /* Create a fresh new dialog window */ 3746 if (do_replace) 3747 str = XmStringCreateSimple(_("VIM - Search and Replace...")); 3748 else 3749 str = XmStringCreateSimple(_("VIM - Search...")); 3750 3751 n = 0; 3752 XtSetArg(args[n], XmNautoUnmanage, False); n++; 3753 XtSetArg(args[n], XmNnoResize, True); n++; 3754 XtSetArg(args[n], XmNdialogTitle, str); n++; 3755 3756 frdp->dialog = XmCreateFormDialog(vimShell, "findReplaceDialog", args, n); 3757 XmStringFree(str); 3758 XtAddCallback(frdp->dialog, XmNdestroyCallback, 3759 find_replace_destroy_callback, frdp); 3760 3761 button_form = XtVaCreateWidget("buttonForm", 3762 xmFormWidgetClass, frdp->dialog, 3763 XmNrightAttachment, XmATTACH_FORM, 3764 XmNrightOffset, 4, 3765 XmNtopAttachment, XmATTACH_FORM, 3766 XmNtopOffset, 4, 3767 XmNbottomAttachment, XmATTACH_FORM, 3768 XmNbottomOffset, 4, 3769 NULL); 3770 3771 frdp->find = XtVaCreateManagedWidget("findButton", 3772 xmPushButtonWidgetClass, button_form, 3773 XmNsensitive, True, 3774 XmNtopAttachment, XmATTACH_FORM, 3775 XmNleftAttachment, XmATTACH_FORM, 3776 XmNrightAttachment, XmATTACH_FORM, 3777 NULL); 3778 set_label(frdp->find, _("Find &Next")); 3779 3780 XtAddCallback(frdp->find, XmNactivateCallback, 3781 find_replace_callback, 3782 (do_replace ? (XtPointer)FRD_R_FINDNEXT : (XtPointer)FRD_FINDNEXT)); 3783 3784 if (do_replace) 3785 { 3786 frdp->replace = XtVaCreateManagedWidget("replaceButton", 3787 xmPushButtonWidgetClass, button_form, 3788 XmNtopAttachment, XmATTACH_WIDGET, 3789 XmNtopWidget, frdp->find, 3790 XmNleftAttachment, XmATTACH_FORM, 3791 XmNrightAttachment, XmATTACH_FORM, 3792 NULL); 3793 set_label(frdp->replace, _("&Replace")); 3794 XtAddCallback(frdp->replace, XmNactivateCallback, 3795 find_replace_callback, (XtPointer)FRD_REPLACE); 3796 3797 frdp->all = XtVaCreateManagedWidget("replaceAllButton", 3798 xmPushButtonWidgetClass, button_form, 3799 XmNtopAttachment, XmATTACH_WIDGET, 3800 XmNtopWidget, frdp->replace, 3801 XmNleftAttachment, XmATTACH_FORM, 3802 XmNrightAttachment, XmATTACH_FORM, 3803 NULL); 3804 set_label(frdp->all, _("Replace &All")); 3805 XtAddCallback(frdp->all, XmNactivateCallback, 3806 find_replace_callback, (XtPointer)FRD_REPLACEALL); 3807 3808 frdp->undo = XtVaCreateManagedWidget("undoButton", 3809 xmPushButtonWidgetClass, button_form, 3810 XmNtopAttachment, XmATTACH_WIDGET, 3811 XmNtopWidget, frdp->all, 3812 XmNleftAttachment, XmATTACH_FORM, 3813 XmNrightAttachment, XmATTACH_FORM, 3814 NULL); 3815 set_label(frdp->undo, _("&Undo")); 3816 XtAddCallback(frdp->undo, XmNactivateCallback, 3817 find_replace_callback, (XtPointer)FRD_UNDO); 3818 } 3819 3820 frdp->cancel = XtVaCreateManagedWidget("closeButton", 3821 xmPushButtonWidgetClass, button_form, 3822 XmNleftAttachment, XmATTACH_FORM, 3823 XmNrightAttachment, XmATTACH_FORM, 3824 XmNbottomAttachment, XmATTACH_FORM, 3825 NULL); 3826 set_label(frdp->cancel, _("&Cancel")); 3827 XtAddCallback(frdp->cancel, XmNactivateCallback, 3828 find_replace_dismiss_callback, frdp); 3829 gui_motif_menu_fontlist(frdp->cancel); 3830 3831 XtManageChild(button_form); 3832 3833 n = 0; 3834 XtSetArg(args[n], XmNorientation, XmVERTICAL); n++; 3835 XtSetArg(args[n], XmNrightAttachment, XmATTACH_WIDGET); n++; 3836 XtSetArg(args[n], XmNrightWidget, button_form); n++; 3837 XtSetArg(args[n], XmNrightOffset, 4); n++; 3838 XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++; 3839 XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++; 3840 separator = XmCreateSeparatorGadget(frdp->dialog, "separator", args, n); 3841 XtManageChild(separator); 3842 3843 input_form = XtVaCreateWidget("inputForm", 3844 xmFormWidgetClass, frdp->dialog, 3845 XmNleftAttachment, XmATTACH_FORM, 3846 XmNleftOffset, 4, 3847 XmNrightAttachment, XmATTACH_WIDGET, 3848 XmNrightWidget, separator, 3849 XmNrightOffset, 4, 3850 XmNtopAttachment, XmATTACH_FORM, 3851 XmNtopOffset, 4, 3852 NULL); 3853 3854 { 3855 Widget label_what; 3856 Widget label_with = (Widget)0; 3857 3858 str = XmStringCreateSimple(_("Find what:")); 3859 label_what = XtVaCreateManagedWidget("whatLabel", 3860 xmLabelGadgetClass, input_form, 3861 XmNlabelString, str, 3862 XmNleftAttachment, XmATTACH_FORM, 3863 XmNtopAttachment, XmATTACH_FORM, 3864 XmNtopOffset, 4, 3865 NULL); 3866 XmStringFree(str); 3867 gui_motif_menu_fontlist(label_what); 3868 3869 frdp->what = XtVaCreateManagedWidget("whatText", 3870 xmTextFieldWidgetClass, input_form, 3871 XmNtopAttachment, XmATTACH_FORM, 3872 XmNrightAttachment, XmATTACH_FORM, 3873 XmNleftAttachment, XmATTACH_FORM, 3874 NULL); 3875 3876 if (do_replace) 3877 { 3878 frdp->with = XtVaCreateManagedWidget("withText", 3879 xmTextFieldWidgetClass, input_form, 3880 XmNtopAttachment, XmATTACH_WIDGET, 3881 XmNtopWidget, frdp->what, 3882 XmNtopOffset, 4, 3883 XmNleftAttachment, XmATTACH_FORM, 3884 XmNrightAttachment, XmATTACH_FORM, 3885 XmNbottomAttachment, XmATTACH_FORM, 3886 NULL); 3887 3888 XtAddCallback(frdp->with, XmNactivateCallback, 3889 find_replace_callback, (XtPointer) FRD_R_FINDNEXT); 3890 3891 str = XmStringCreateSimple(_("Replace with:")); 3892 label_with = XtVaCreateManagedWidget("withLabel", 3893 xmLabelGadgetClass, input_form, 3894 XmNlabelString, str, 3895 XmNleftAttachment, XmATTACH_FORM, 3896 XmNtopAttachment, XmATTACH_WIDGET, 3897 XmNtopWidget, frdp->what, 3898 XmNtopOffset, 4, 3899 XmNbottomAttachment, XmATTACH_FORM, 3900 NULL); 3901 XmStringFree(str); 3902 gui_motif_menu_fontlist(label_with); 3903 3904 /* 3905 * Make the entry activation only change the input focus onto the 3906 * with item. 3907 */ 3908 XtAddCallback(frdp->what, XmNactivateCallback, 3909 entry_activate_callback, frdp->with); 3910 XtAddEventHandler(frdp->with, KeyPressMask, False, 3911 (XtEventHandler)find_replace_keypress, 3912 (XtPointer) frdp); 3913 3914 } 3915 else 3916 { 3917 /* 3918 * Make the entry activation do the search. 3919 */ 3920 XtAddCallback(frdp->what, XmNactivateCallback, 3921 find_replace_callback, (XtPointer)FRD_FINDNEXT); 3922 } 3923 XtAddEventHandler(frdp->what, KeyPressMask, False, 3924 (XtEventHandler)find_replace_keypress, 3925 (XtPointer)frdp); 3926 3927 /* Get the maximum width between the label widgets and line them up. 3928 */ 3929 n = 0; 3930 XtSetArg(args[n], XmNwidth, &width); n++; 3931 XtGetValues(label_what, args, n); 3932 widest = width; 3933 if (do_replace) 3934 { 3935 XtGetValues(label_with, args, n); 3936 if (width > widest) 3937 widest = width; 3938 } 3939 3940 XtVaSetValues(frdp->what, XmNleftOffset, widest, NULL); 3941 if (do_replace) 3942 XtVaSetValues(frdp->with, XmNleftOffset, widest, NULL); 3943 3944 } 3945 3946 XtManageChild(input_form); 3947 3948 { 3949 Widget radio_box; 3950 Widget w; 3951 3952 frame = XtVaCreateWidget("directionFrame", 3953 xmFrameWidgetClass, frdp->dialog, 3954 XmNtopAttachment, XmATTACH_WIDGET, 3955 XmNtopWidget, input_form, 3956 XmNtopOffset, 4, 3957 XmNbottomAttachment, XmATTACH_FORM, 3958 XmNbottomOffset, 4, 3959 XmNrightAttachment, XmATTACH_OPPOSITE_WIDGET, 3960 XmNrightWidget, input_form, 3961 NULL); 3962 3963 str = XmStringCreateSimple(_("Direction")); 3964 w = XtVaCreateManagedWidget("directionFrameLabel", 3965 xmLabelGadgetClass, frame, 3966 XmNlabelString, str, 3967 XmNchildHorizontalAlignment, XmALIGNMENT_BEGINNING, 3968 XmNchildType, XmFRAME_TITLE_CHILD, 3969 NULL); 3970 XmStringFree(str); 3971 gui_motif_menu_fontlist(w); 3972 3973 radio_box = XmCreateRadioBox(frame, "radioBox", 3974 (ArgList)NULL, 0); 3975 3976 str = XmStringCreateSimple( _("Up")); 3977 frdp->up = XtVaCreateManagedWidget("upRadioButton", 3978 xmToggleButtonGadgetClass, radio_box, 3979 XmNlabelString, str, 3980 XmNset, False, 3981 NULL); 3982 XmStringFree(str); 3983 gui_motif_menu_fontlist(frdp->up); 3984 3985 str = XmStringCreateSimple(_("Down")); 3986 frdp->down = XtVaCreateManagedWidget("downRadioButton", 3987 xmToggleButtonGadgetClass, radio_box, 3988 XmNlabelString, str, 3989 XmNset, True, 3990 NULL); 3991 XmStringFree(str); 3992 gui_motif_menu_fontlist(frdp->down); 3993 3994 XtManageChild(radio_box); 3995 XtManageChild(frame); 3996 } 3997 3998 toggle_form = XtVaCreateWidget("toggleForm", 3999 xmFormWidgetClass, frdp->dialog, 4000 XmNleftAttachment, XmATTACH_FORM, 4001 XmNleftOffset, 4, 4002 XmNrightAttachment, XmATTACH_WIDGET, 4003 XmNrightWidget, frame, 4004 XmNrightOffset, 4, 4005 XmNtopAttachment, XmATTACH_WIDGET, 4006 XmNtopWidget, input_form, 4007 XmNtopOffset, 4, 4008 XmNbottomAttachment, XmATTACH_FORM, 4009 XmNbottomOffset, 4, 4010 NULL); 4011 4012 str = XmStringCreateSimple(_("Match whole word only")); 4013 frdp->wword = XtVaCreateManagedWidget("wordToggle", 4014 xmToggleButtonGadgetClass, toggle_form, 4015 XmNlabelString, str, 4016 XmNtopAttachment, XmATTACH_FORM, 4017 XmNtopOffset, 4, 4018 XmNleftAttachment, XmATTACH_FORM, 4019 XmNleftOffset, 4, 4020 XmNset, wword, 4021 NULL); 4022 XmStringFree(str); 4023 4024 str = XmStringCreateSimple(_("Match case")); 4025 frdp->mcase = XtVaCreateManagedWidget("caseToggle", 4026 xmToggleButtonGadgetClass, toggle_form, 4027 XmNlabelString, str, 4028 XmNleftAttachment, XmATTACH_FORM, 4029 XmNleftOffset, 4, 4030 XmNtopAttachment, XmATTACH_WIDGET, 4031 XmNtopWidget, frdp->wword, 4032 XmNtopOffset, 4, 4033 XmNset, mcase, 4034 NULL); 4035 XmStringFree(str); 4036 gui_motif_menu_fontlist(frdp->wword); 4037 gui_motif_menu_fontlist(frdp->mcase); 4038 4039 XtManageChild(toggle_form); 4040 4041 if (entry_text != NULL) 4042 XmTextFieldSetString(frdp->what, (char *)entry_text); 4043 vim_free(entry_text); 4044 4045 gui_motif_synch_fonts(); 4046 4047 manage_centered(frdp->dialog); 4048 activate_dialog_mnemonics(frdp->dialog); 4049 XmProcessTraversal(frdp->what, XmTRAVERSE_CURRENT); 4050 } 4051 4052 void 4053 gui_mch_find_dialog(eap) 4054 exarg_T *eap; 4055 { 4056 if (!gui.in_use) 4057 return; 4058 4059 find_replace_dialog_create(eap->arg, FALSE); 4060 } 4061 4062 4063 void 4064 gui_mch_replace_dialog(eap) 4065 exarg_T *eap; 4066 { 4067 if (!gui.in_use) 4068 return; 4069 4070 find_replace_dialog_create(eap->arg, TRUE); 4071 } 4072 4073 /* 4074 * Synchronize all gui elements, which are dependant upon the 4075 * main text font used. Those are in esp. the find/replace dialogs. 4076 * If you don't understand why this should be needed, please try to 4077 * search for "pi��" in iso8859-2. 4078 */ 4079 void 4080 gui_motif_synch_fonts(void) 4081 { 4082 SharedFindReplace *frdp; 4083 int do_replace; 4084 XFontStruct *font; 4085 XmFontList font_list; 4086 4087 /* FIXME: Unless we find out how to create a XmFontList from a XFontSet, 4088 * we just give up here on font synchronization. */ 4089 font = (XFontStruct *)gui.norm_font; 4090 if (font == NULL) 4091 return; 4092 4093 font_list = gui_motif_create_fontlist(font); 4094 4095 /* OK this loop is a bit tricky... */ 4096 for (do_replace = 0; do_replace <= 1; ++do_replace) 4097 { 4098 frdp = (do_replace) ? (&repl_widgets) : (&find_widgets); 4099 if (frdp->dialog) 4100 { 4101 XtVaSetValues(frdp->what, XmNfontList, font_list, NULL); 4102 if (do_replace) 4103 XtVaSetValues(frdp->with, XmNfontList, font_list, NULL); 4104 } 4105 } 4106 4107 XmFontListFree(font_list); 4108 } 4109