1 /*- 2 * Copyright (c) 1996 3 * Rob Zimmermann. All rights reserved. 4 * Copyright (c) 1996 5 * Keith Bostic. All rights reserved. 6 * 7 * See the LICENSE file for redistribution information. 8 */ 9 10 #include "config.h" 11 12 #include <sys/cdefs.h> 13 #if 0 14 #ifndef lint 15 static const char sccsid[] = "Id: m_menu.c,v 8.26 2003/11/05 17:09:59 skimo Exp (Berkeley) Date: 2003/11/05 17:09:59 "; 16 #endif /* not lint */ 17 #else 18 __RCSID("$NetBSD: m_menu.c,v 1.2 2014/01/26 21:43:45 christos Exp $"); 19 #endif 20 21 #include <sys/queue.h> 22 23 #include <X11/Intrinsic.h> 24 #include <X11/StringDefs.h> 25 #include <Xm/PushB.h> 26 #include <Xm/CascadeB.h> 27 #include <Xm/RowColumn.h> 28 #include <Xm/Separator.h> 29 #include <Xm/FileSB.h> 30 #include <Xm/SelectioB.h> 31 32 #include <bitstring.h> 33 #include <stdio.h> 34 35 #undef LOCK_SUCCESS 36 #include "../common/common.h" 37 #include "../ipc/ip.h" 38 #include "m_motif.h" 39 40 extern int vi_ofd; 41 42 /* save this for creation of children */ 43 static Widget main_widget = NULL; 44 45 /* This module defines the menu structure for vi. Each menu 46 * item has an action routine associated with it. For the most 47 * part, those actions will simply call vi_send with vi commands. 48 * others will pop up file selection dialogs and use them for 49 * vi commands, and other will have to have special actions. 50 * 51 * Future: 52 * vi core will have to let us know when to be sensitive 53 * change VI_STRING to VI_COMMAND so that menu actions cannot 54 * be confusing when in insert mode 55 * need VI_CUT, VI_COPY, and VI_PASTE to perform the appropriate 56 * operations on the visible text of yank buffer. VI_COPY 57 * is likely a NOP, but it will make users happy 58 * add mnemonics 59 * add accelerators 60 * implement file selection dialog boxes 61 * implement string prompt dialog boxes (e.g. for 'find') 62 * 63 * Interface: 64 * Widget create_menubar( Widget parent ) creates and returns the 65 * X menu structure. The caller can place this 66 * anywhere in the widget heirarchy. 67 */ 68 69 #define BufferSize 1024 70 71 /* 72 * __vi_send_command_string -- 73 * Utility: Send a menu command to vi 74 * 75 * Future: 76 * Change VI_STRING to VI_COMMAND so that menu actions cannot be confusing 77 * when in insert mode. 78 * 79 * XXX 80 * THIS SHOULD GO AWAY -- WE SHOULDN'T SEND UNINTERPRETED STRINGS TO THE 81 * CORE. 82 * 83 * PUBLIC: void __vi_send_command_string __P((String)); 84 */ 85 void 86 __vi_send_command_string(String str) 87 { 88 IP_BUF ipb; 89 char buffer[BufferSize]; 90 91 /* Future: Need VI_COMMAND so vi knows this is not text to insert 92 * At that point, appending a cr/lf will not be necessary. For now, 93 * append iff we are a colon or slash command. Of course, if we are in 94 * insert mode, all bets are off. 95 */ 96 strcpy( buffer, str ); 97 switch ( *str ) { 98 case ':': 99 case '/': 100 strcat( buffer, "\n" ); 101 break; 102 } 103 104 ipb.code = VI_STRING; 105 ipb.str1 = buffer; 106 ipb.len1 = strlen(buffer); 107 vi_send(vi_ofd, "a", &ipb); 108 } 109 110 111 /* Utility: beep for unimplemented command */ 112 113 #if defined(__STDC__) 114 static void send_beep( Widget w ) 115 #else 116 static void send_beep( w ) 117 Widget w; 118 #endif 119 { 120 XBell( XtDisplay(w), 0 ); 121 } 122 123 124 /* 125 * __vi_cancel_cb -- 126 * Utility: make a dialog box go Modal 127 * 128 * PUBLIC: void __vi_cancel_cb __P((Widget, XtPointer, XtPointer)); 129 */ 130 static Bool have_answer; 131 void 132 __vi_cancel_cb(Widget w, XtPointer client_data, XtPointer call_data) 133 { 134 have_answer = True; 135 } 136 137 /* 138 * PUBLIC: void __vi_modal_dialog __P((Widget)); 139 */ 140 void 141 __vi_modal_dialog(Widget db) 142 { 143 XtAppContext ctx; 144 145 /* post the dialog */ 146 XtManageChild( db ); 147 XtPopup( XtParent(db), XtGrabExclusive ); 148 149 /* wait for a response */ 150 ctx = XtWidgetToApplicationContext(db); 151 XtAddGrab( XtParent(db), TRUE, FALSE ); 152 for ( have_answer = False; ! have_answer; ) 153 XtAppProcessEvent( ctx, XtIMAll ); 154 155 /* done with db */ 156 XtPopdown( XtParent(db) ); 157 XtRemoveGrab( XtParent(db) ); 158 } 159 160 161 /* Utility: Get a file (using standard File Selection Dialog Box) */ 162 163 static String file_name; 164 165 166 #if defined(__STDC__) 167 static void ok_file_name( Widget w, 168 XtPointer client_data, 169 XtPointer call_data 170 ) 171 #else 172 static void ok_file_name( w, client_data, call_data ) 173 Widget w; 174 XtPointer client_data; 175 XtPointer call_data; 176 #endif 177 { 178 XmFileSelectionBoxCallbackStruct *cbs; 179 180 cbs = (XmFileSelectionBoxCallbackStruct *) call_data; 181 XmStringGetLtoR( cbs->value, XmSTRING_DEFAULT_CHARSET, &file_name ); 182 183 have_answer = True; 184 } 185 186 187 #if defined(__STDC__) 188 static String get_file( Widget w, String prompt ) 189 #else 190 static String get_file( w, prompt ) 191 Widget w; 192 String prompt; 193 #endif 194 { 195 /* make it static so we can reuse it */ 196 static Widget db; 197 198 /* our return parameter */ 199 if ( file_name != NULL ) { 200 XtFree( file_name ); 201 file_name = NULL; 202 } 203 204 /* create one? */ 205 if ( db == NULL ){ 206 db = XmCreateFileSelectionDialog( main_widget, "file", NULL, 0 ); 207 XtAddCallback( db, XmNokCallback, ok_file_name, NULL ); 208 XtAddCallback( db, XmNcancelCallback, __vi_cancel_cb, NULL ); 209 } 210 211 /* use the title as a prompt */ 212 XtVaSetValues( XtParent(db), XmNtitle, prompt, 0 ); 213 214 /* wait for a response */ 215 __vi_modal_dialog( db ); 216 217 /* done */ 218 return file_name; 219 } 220 221 222 /* 223 * file_command -- 224 * Get a file name and send it with the command to the core. 225 */ 226 static void 227 file_command(Widget w, int code, String prompt) 228 { 229 IP_BUF ipb; 230 char *file; 231 232 if ((file = get_file(w, prompt)) != NULL) { 233 ipb.code = code; 234 ipb.str1 = file; 235 ipb.len1 = strlen(file); 236 vi_send(vi_ofd, "a", &ipb); 237 } 238 } 239 240 241 /* 242 * Menu action routines (one per menu entry) 243 * 244 * These are in the order in which they appear in the menu structure. 245 */ 246 static void 247 ma_edit_file(Widget w, XtPointer call_data, XtPointer client_data) 248 { 249 file_command(w, VI_EDIT, "Edit"); 250 } 251 252 static void 253 ma_split(Widget w, XtPointer call_data, XtPointer client_data) 254 { 255 file_command(w, VI_EDITSPLIT, "Edit"); 256 } 257 258 static void 259 ma_save(Widget w, XtPointer call_data, XtPointer client_data) 260 { 261 IP_BUF ipb; 262 263 ipb.code = VI_WRITE; 264 (void)vi_send(vi_ofd, NULL, &ipb); 265 } 266 267 static void 268 ma_save_as(Widget w, XtPointer call_data, XtPointer client_data) 269 { 270 file_command(w, VI_WRITEAS, "Save As"); 271 } 272 273 static void 274 ma_wq(Widget w, XtPointer call_data, XtPointer client_data) 275 { 276 IP_BUF ipb; 277 278 ipb.code = VI_WQ; 279 (void)vi_send(vi_ofd, NULL, &ipb); 280 } 281 282 static void 283 ma_quit(Widget w, XtPointer call_data, XtPointer client_data) 284 { 285 IP_BUF ipb; 286 287 ipb.code = VI_QUIT; 288 (void)vi_send(vi_ofd, NULL, &ipb); 289 } 290 291 static void 292 ma_undo(Widget w, XtPointer call_data, XtPointer client_data) 293 { 294 IP_BUF ipb; 295 296 ipb.code = VI_UNDO; 297 (void)vi_send(vi_ofd, NULL, &ipb); 298 } 299 300 #if defined(__STDC__) 301 static void ma_cut( Widget w, 302 XtPointer call_data, 303 XtPointer client_data 304 ) 305 #else 306 static void ma_cut( w, call_data, client_data ) 307 Widget w; 308 XtPointer call_data; 309 XtPointer client_data; 310 #endif 311 { 312 /* future */ 313 send_beep( w ); 314 } 315 316 317 #if defined(__STDC__) 318 static void ma_copy( Widget w, 319 XtPointer call_data, 320 XtPointer client_data 321 ) 322 #else 323 static void ma_copy( w, call_data, client_data ) 324 Widget w; 325 XtPointer call_data; 326 XtPointer client_data; 327 #endif 328 { 329 /* future */ 330 send_beep( w ); 331 } 332 333 334 #if defined(__STDC__) 335 static void ma_paste( Widget w, 336 XtPointer call_data, 337 XtPointer client_data 338 ) 339 #else 340 static void ma_paste( w, call_data, client_data ) 341 Widget w; 342 XtPointer call_data; 343 XtPointer client_data; 344 #endif 345 { 346 /* future */ 347 send_beep( w ); 348 } 349 350 static void 351 ma_find(Widget w, XtPointer call_data, XtPointer client_data) 352 { 353 __vi_show_search_dialog( main_widget, "Find" ); 354 } 355 356 static void 357 ma_find_next(Widget w, XtPointer call_data, XtPointer client_data) 358 { 359 __vi_search( w ); 360 } 361 362 static void 363 ma_tags(Widget w, XtPointer call_data, XtPointer client_data) 364 { 365 __vi_show_tags_dialog( main_widget, "Tag Stack" ); 366 } 367 368 static void 369 ma_tagpop(Widget w, XtPointer call_data, XtPointer client_data) 370 { 371 __vi_send_command_string( "\024" ); 372 } 373 374 static void 375 ma_tagtop(Widget w, XtPointer call_data, XtPointer client_data) 376 { 377 __vi_send_command_string( ":tagtop" ); 378 } 379 380 #if defined(__STDC__) 381 static void ma_preferences( Widget w, 382 XtPointer call_data, 383 XtPointer client_data 384 ) 385 #else 386 static void ma_preferences( w, call_data, client_data ) 387 Widget w; 388 XtPointer call_data; 389 XtPointer client_data; 390 #endif 391 { 392 __vi_show_options_dialog( main_widget, "Preferences" ); 393 } 394 395 396 /* Menu construction routines */ 397 398 typedef struct { 399 String title; 400 void (*action)(); 401 String accel; /* for Motif */ 402 String accel_text; /* for the user */ 403 } pull_down; 404 405 typedef struct { 406 char mnemonic; 407 String title; 408 pull_down *actions; 409 } menu_bar; 410 411 static pull_down file_menu[] = { 412 { "Edit File...", ma_edit_file, "Alt<Key>e", "Alt+E" }, 413 { "", NULL, NULL, NULL }, 414 { "Split Window...", ma_split, NULL, NULL }, 415 { "", NULL, NULL, NULL }, 416 { "Save ", ma_save, "Alt<Key>s", "Alt+S" }, 417 { "Save As...", ma_save_as, "Shift Alt<Key>s", "Shift+Alt+S" }, 418 { "", NULL, NULL, NULL }, 419 { "Write and Quit", ma_wq, "Shift Alt<Key>q", "Shift+Alt+Q" }, 420 { "Quit", ma_quit, "Alt<Key>q", "Alt+Q" }, 421 { NULL, NULL, NULL, NULL }, 422 }; 423 424 static pull_down edit_menu[] = { 425 { "Undo", ma_undo, NULL, NULL }, 426 { "", NULL, NULL, NULL }, 427 { "Cut", ma_cut, "Alt<Key>x", "Alt+X" }, 428 { "Copy", ma_copy, "Alt<Key>c", "Alt+C" }, 429 { "Paste", ma_paste, "Alt<Key>v", "Alt+V" }, 430 { "", NULL, NULL, NULL }, 431 { "Find", ma_find, "Alt<Key>f", "Alt+F" }, 432 { "Find Next", ma_find_next, "Alt<Key>g", "Alt+G" }, 433 { NULL, NULL, NULL, NULL }, 434 }; 435 436 static pull_down options_menu[] = { 437 { "Preferences", ma_preferences, NULL, NULL }, 438 { "Command Mode Maps", NULL, NULL, NULL }, 439 { "Insert Mode Maps", NULL, NULL, NULL }, 440 { NULL, NULL, NULL, NULL }, 441 }; 442 443 static pull_down tag_menu[] = { 444 { "Show Tag Stack", ma_tags, "Alt<Key>t", "Alt+T" }, 445 { "", NULL, NULL, NULL }, 446 { "Pop Tag", ma_tagpop, NULL, NULL }, 447 { "Clear Stack", ma_tagtop, NULL, NULL }, 448 { NULL, NULL, NULL, NULL }, 449 }; 450 451 static pull_down help_menu[] = { 452 { NULL, NULL, NULL, NULL }, 453 }; 454 455 static menu_bar main_menu[] = { 456 { 'F', "File", file_menu }, 457 { 'E', "Edit", edit_menu }, 458 { 'O', "Options", options_menu }, 459 { 'T', "Tag", tag_menu }, 460 { 'H', "Help", help_menu }, 461 { 0, NULL, NULL }, 462 }; 463 464 465 #if defined(__STDC__) 466 static void add_entries( Widget parent, pull_down *actions ) 467 #else 468 static void add_entries( parent, actions ) 469 Widget parent; 470 pull_down *actions; 471 #endif 472 { 473 Widget w; 474 XmString str; 475 476 for ( ; actions->title != NULL; actions++ ) { 477 478 /* a separator? */ 479 if ( *actions->title != '\0' ) { 480 w = XmCreatePushButton( parent, actions->title, NULL, 0 ); 481 if ( actions->action == NULL ) 482 XtSetSensitive( w, False ); 483 else 484 XtAddCallback( w, 485 XmNactivateCallback, 486 (XtCallbackProc) actions->action, 487 actions 488 ); 489 if ( actions->accel != NULL ) { 490 str = XmStringCreateSimple( actions->accel_text ); 491 XtVaSetValues( w, 492 XmNaccelerator, actions->accel, 493 XmNacceleratorText, str, 494 0 495 ); 496 XmStringFree( str ); 497 } 498 } 499 else { 500 w = XmCreateSeparator( parent, "separator", NULL, 0 ); 501 } 502 503 XtManageChild( w ); 504 505 } 506 } 507 508 /* 509 * vi_create_menubar -- 510 * 511 * PUBLIC: Widget vi_create_menubar __P((Widget)); 512 */ 513 Widget 514 vi_create_menubar(Widget parent) 515 { 516 Widget menu, pull, button; 517 menu_bar *ptr; 518 519 /* save this for creation of children */ 520 main_widget = parent; 521 522 menu = XmCreateMenuBar( parent, "Menu", NULL, 0 ); 523 524 for ( ptr=main_menu; ptr->title != NULL; ptr++ ) { 525 526 pull = XmCreatePulldownMenu( menu, "pull", NULL, 0 ); 527 add_entries( pull, ptr->actions ); 528 button = XmCreateCascadeButton( menu, ptr->title, NULL, 0 ); 529 XtVaSetValues( button, XmNsubMenuId, pull, 0 ); 530 531 if ( strcmp( ptr->title, "Help" ) == 0 ) 532 XtVaSetValues( menu, XmNmenuHelpWidget, button, 0 ); 533 534 #if 0 535 /* These screw up accelerator processing. Punt for now */ 536 if ( ptr->mnemonic ) 537 XtVaSetValues( button, XmNmnemonic, ptr->mnemonic, 0 ); 538 #endif 539 540 XtManageChild( button ); 541 } 542 543 return menu; 544 } 545