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