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