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_tags.c,v 8.9 2003/11/05 17:10:00 skimo Exp (Berkeley) Date: 2003/11/05 17:10:00"; 16#endif /* not lint */ 17 18/* 19 * This module implements a dialog for navigating the tag stack 20 * 21 * Interface: 22 * void __vi_show_tags_dialog( Widget parent, String title ) 23 * Pops up a Tags dialog. We allow one per session. It is not modal. 24 * 25 * void __vi_push_tag( String text ) 26 * void __vi_pop_tag() 27 * void __vi_clear_tag() 28 * When core changes the tag stack we will change our representation 29 * of it. When this dialog appears, we need core to send a slew of 30 * messages so we can display the current tag stack. 31 * 32 * void __vi_set_tag_text( String text ) 33 * the text field in the dialog is set to the string. ideally, 34 * this should be kept in sync with the word following the caret 35 * in the current editor window 36 * 37 * To Do: 38 * The push-buttons should activate and deactivate according to 39 * the state of the tag stack and the text field. 40 * 41 * Need to send VI commands rather than strings 42 * 43 * Need IPO command to start/stop asking for the current tag stack 44 */ 45 46 47/* context */ 48#include <X11/X.h> 49#include <X11/Intrinsic.h> 50#include <Xm/DialogS.h> 51#include <Xm/Form.h> 52#include <Xm/LabelG.h> 53#include <Xm/TextF.h> 54#include <Xm/List.h> 55#include <Xm/RowColumn.h> 56#include <Xm/PushBG.h> 57 58#if ! defined(SelfTest) 59#include <sys/types.h> 60#include <sys/queue.h> 61 62#include <bitstring.h> 63#include <stdio.h> 64#include <string.h> 65 66#undef LOCK_SUCCESS 67#include "../common/common.h" 68#include "../ipc/ip.h" 69#include "m_motif.h" 70#endif 71 72extern int vi_ofd; 73 74 75/* globals */ 76 77static Widget db_tabs = NULL, 78 db_text, 79 db_list; 80 81static Boolean active = False; 82 83typedef struct { 84 String name; 85 Boolean is_default; 86 void (*cb)(); 87} ButtonData; 88 89static void go_to_tag(Widget w); 90static void split_to_tag(Widget w); 91static void pop_tag(Widget w); 92 93static ButtonData button_data[] = { 94 { "Go To Tag", True, go_to_tag }, 95 { "Split To Tag", False, split_to_tag }, 96 { "Pop Tag", False, pop_tag }, 97}; 98 99 100/* manage the tags stack list */ 101 102void __vi_pop_tag(void) 103{ 104 if ( ! active ) return; 105 106 XmListDeletePos( db_list, 1 ); 107} 108 109 110void __vi_clear_tag(void) 111{ 112 if ( ! active ) return; 113 114 XmListDeleteAllItems( db_list ); 115} 116 117 118void __vi_push_tag(String text) 119{ 120 XmString str; 121 122 if ( ! active ) return; 123 124 str = XmStringCreateSimple( text ); 125 XmListAddItem( db_list, str, 1 ); 126 XmStringFree( str ); 127} 128 129 130/* create a set of push buttons */ 131 132#define SpacingRatio 4 /* 3:1 button to spaces */ 133 134#if defined(__STDC__) 135static Widget create_push_buttons( Widget parent, 136 ButtonData *data, 137 int count 138 ) 139#else 140static Widget create_push_buttons( parent, data, count ) 141 Widget parent; 142 ButtonData *data; 143 int count; 144#endif 145{ 146 Widget w, form; 147 int pos = 1, base; 148 149 base = SpacingRatio*count + 1; 150 form = XtVaCreateManagedWidget( "buttons", 151 xmFormWidgetClass, 152 parent, 153 XmNleftAttachment, XmATTACH_FORM, 154 XmNrightAttachment, XmATTACH_FORM, 155 XmNfractionBase, base, 156 XmNshadowType, XmSHADOW_ETCHED_IN, 157 XmNshadowThickness, 2, 158 XmNverticalSpacing, 4, 159 0 160 ); 161 162 while ( count-- > 0 ) { 163 w = XtVaCreateManagedWidget(data->name, 164 xmPushButtonGadgetClass, 165 form, 166 XmNtopAttachment, XmATTACH_FORM, 167 XmNbottomAttachment,XmATTACH_FORM, 168 XmNleftAttachment, XmATTACH_POSITION, 169 XmNleftPosition, pos, 170 XmNshowAsDefault, data->is_default, 171 XmNdefaultButtonShadowThickness, data->is_default, 172 XmNrightAttachment, XmATTACH_POSITION, 173 XmNrightPosition, pos+SpacingRatio-1, 174 0 175 ); 176 if ( data->is_default ) 177 XtVaSetValues( form, XmNdefaultButton, w, 0 ); 178 XtAddCallback( w, XmNactivateCallback, data->cb, 0 ); 179 pos += SpacingRatio; 180 data++; 181 } 182 183 return form; 184} 185 186 187/* callbacks */ 188 189static void 190cancel_cb(void) 191{ 192#if defined(SelfTest) 193 puts( "cancelled" ); 194#endif 195 active = False; 196} 197 198 199static void set_text_field(Widget w, XtPointer ptr, XmListCallbackStruct *cbs) 200{ 201 String str; 202 203 XmStringGetLtoR( cbs->item, XmSTRING_DEFAULT_CHARSET, &str ); 204 XmTextFieldSetString( db_text, str ); 205 XtFree( str ); 206} 207 208 209void __vi_set_tag_text(String text) 210{ 211 if ( active ) XmTextFieldSetString( db_text, text ); 212} 213 214 215static void destroyed(void) 216{ 217#if defined(SelfTest) 218 puts( "destroyed" ); 219#endif 220 221 /* some window managers destroy us upon popdown */ 222 db_tabs = NULL; 223 active = False; 224} 225 226 227#if defined(__STDC__) 228static void pop_tag( Widget w ) 229#else 230static void pop_tag( w ) 231 Widget w; 232#endif 233{ 234 static String buffer = ":tagpop"; 235 236#if defined(SelfTest) 237 printf( "sending command <<%s>>\n", buffer ); 238 __vi_pop_tag(); 239#else 240 __vi_send_command_string( buffer ); 241#endif 242} 243 244 245#if defined(__STDC__) 246static void split_to_tag( Widget w ) 247#else 248static void split_to_tag( w ) 249 Widget w; 250#endif 251{ 252 IP_BUF ipb; 253 String str; 254 255 str = XmTextFieldGetString( db_text ); 256 257#if defined(SelfTest) 258 printf( "sending command <<:Tag %s>>\n", str ); 259#else 260 /* 261 * XXX 262 * This is REALLY sleazy. We pass the nul along with the 263 * string so that the core editor doesn't have to copy the 264 * string to get a nul termination. This should be fixed 265 * as part of making the editor fully 8-bit clean. 266 */ 267 ipb.code = VI_TAGSPLIT; 268 ipb.str1 = str; 269 ipb.len1 = strlen(str) + 1; 270 vi_send(vi_ofd, "a", &ipb); 271#endif 272 273 XtFree( str ); 274} 275 276 277#if defined(__STDC__) 278static void go_to_tag( Widget w ) 279#else 280static void go_to_tag( w ) 281 Widget w; 282#endif 283{ 284 IP_BUF ipb; 285 String str; 286 287 str = XmTextFieldGetString( db_text ); 288 289#if defined(SelfTest) 290 printf( "sending command <<:tag %s>>\n", str ); 291#else 292 /* 293 * XXX 294 * This is REALLY sleazy. We pass the nul along with the 295 * string so that the core editor doesn't have to copy the 296 * string to get a nul termination. This should be fixed 297 * as part of making the editor fully 8-bit clean. 298 */ 299 ipb.code = VI_TAGAS; 300 ipb.str1 = str; 301 ipb.len1 = strlen(str) + 1; 302 vi_send(vi_ofd, "a", &ipb); 303#endif 304 305 XtFree( str ); 306} 307 308 309 310/* Draw and display a dialog the describes nvi options */ 311 312#if defined(__STDC__) 313static Widget create_tags_dialog( Widget parent, String title ) 314#else 315static Widget create_tags_dialog( parent, title ) 316 Widget parent; 317 String title; 318#endif 319{ 320 Widget box, form, form2, form3, buttons; 321 322 /* already built? */ 323 if ( db_tabs != NULL ) return db_tabs; 324 325 box = XtVaCreatePopupShell( title, 326 xmDialogShellWidgetClass, 327 parent, 328 XmNtitle, title, 329 XmNallowShellResize, False, 330 0 331 ); 332 XtAddCallback( box, XmNpopdownCallback, cancel_cb, 0 ); 333 XtAddCallback( box, XmNdestroyCallback, destroyed, 0 ); 334 335 form = XtVaCreateWidget( "Tags", 336 xmFormWidgetClass, 337 box, 338 0 339 ); 340 341 buttons = create_push_buttons( form, button_data, XtNumber(button_data) ); 342 XtVaSetValues( buttons, 343 XmNbottomAttachment, XmATTACH_FORM, 344 0 345 ); 346 347 form3 = XtVaCreateWidget( "form", 348 xmFormWidgetClass, 349 form, 350 XmNleftAttachment, XmATTACH_FORM, 351 XmNrightAttachment, XmATTACH_FORM, 352 XmNbottomAttachment, XmATTACH_WIDGET, 353 XmNbottomWidget, buttons, 354 0 355 ); 356 357 form2 = XtVaCreateWidget( "form", 358 xmFormWidgetClass, 359 form, 360 XmNtopAttachment, XmATTACH_FORM, 361 XmNleftAttachment, XmATTACH_FORM, 362 XmNrightAttachment, XmATTACH_FORM, 363 XmNbottomAttachment, XmATTACH_WIDGET, 364 XmNbottomWidget, form3, 365 0 366 ); 367 368 db_list = XtVaCreateManagedWidget( "list", 369 xmListWidgetClass, 370 form2, 371 XmNtopAttachment, XmATTACH_FORM, 372 XmNleftAttachment, XmATTACH_POSITION, 373 XmNleftPosition, 20, 374 XmNrightAttachment, XmATTACH_FORM, 375 XmNbottomAttachment, XmATTACH_FORM, 376#if defined(SelfTest) 377 XmNvisibleItemCount, 5, 378#endif 379 XmNselectionPolicy, XmSINGLE_SELECT, 380 0 381 ); 382 XtAddCallback( db_list, XmNsingleSelectionCallback, set_text_field, 0 ); 383 XtAddCallback( db_list, XmNdefaultActionCallback, go_to_tag, 0 ); 384 385 XtVaCreateManagedWidget( "Tag Stack", 386 xmLabelGadgetClass, 387 form2, 388 XmNtopAttachment, XmATTACH_FORM, 389 XmNbottomAttachment, XmATTACH_FORM, 390 XmNleftAttachment, XmATTACH_FORM, 391 XmNrightAttachment, XmATTACH_POSITION, 392 XmNrightPosition, 20, 393 XmNalignment, XmALIGNMENT_END, 394 0 395 ); 396 397 XtVaCreateManagedWidget( "Tag", 398 xmLabelGadgetClass, 399 form3, 400 XmNtopAttachment, XmATTACH_FORM, 401 XmNbottomAttachment, XmATTACH_FORM, 402 XmNleftAttachment, XmATTACH_FORM, 403 XmNrightAttachment, XmATTACH_POSITION, 404 XmNrightPosition, 20, 405 XmNalignment, XmALIGNMENT_END, 406 0 407 ); 408 409 db_text = XtVaCreateManagedWidget( "text", 410 xmTextFieldWidgetClass, 411 form3, 412 XmNtopAttachment, XmATTACH_FORM, 413 XmNbottomAttachment, XmATTACH_FORM, 414 XmNrightAttachment, XmATTACH_FORM, 415 XmNleftAttachment, XmATTACH_POSITION, 416 XmNleftPosition, 20, 417 0 418 ); 419 XtAddCallback( db_text, XmNactivateCallback, go_to_tag, 0 ); 420 421 /* keep this global, we might destroy it later */ 422 db_tabs = form; 423 424 /* done */ 425 XtManageChild( form3 ); 426 XtManageChild( form2 ); 427 return form; 428} 429 430 431 432/* module entry point 433 * __vi_show_tags_dialog( parent, title ) 434 */ 435 436#if defined(__STDC__) 437void __vi_show_tags_dialog( Widget parent, String title ) 438#else 439void __vi_show_tags_dialog( parent, title ) 440Widget parent; 441String title; 442#endif 443{ 444 Widget db = create_tags_dialog( parent, title ), 445 shell = XtParent(db); 446 447 XtManageChild( db ); 448 449 /* get the current window's text */ 450 __vi_set_tag_text( (String) __vi_get_word_at_caret( NULL ) ); 451 452 /* TODO: ask vi core for the current tag stack now */ 453 454 /* leave this guy up (or just raise it) */ 455 XtPopup( shell, XtGrabNone ); 456 XMapRaised( XtDisplay( shell ), XtWindow( shell ) ); 457 458 active = True; 459} 460 461 462 463#if defined(SelfTest) 464 465#if XtSpecificationRelease == 4 466#define ArgcType Cardinal * 467#else 468#define ArgcType int * 469#endif 470 471static void add_a_tag( Widget w ) 472{ 473 static String tags[] = { "first", "second", "this is the third" }; 474 static int i = 0; 475 476 __vi_push_tag( tags[i] ); 477 i = (i+1) % XtNumber(tags); 478} 479 480#if defined(__STDC__) 481static void show_tags( Widget w, XtPointer data, XtPointer cbs ) 482#else 483static void show_tags( w, data, cbs ) 484Widget w; 485XtPointer data; 486XtPointer cbs; 487#endif 488{ 489 __vi_show_tags_dialog( data, "Tags" ); 490} 491 492main( int argc, char *argv[] ) 493{ 494 XtAppContext ctx; 495 Widget top_level, rc, button; 496 extern exit(); 497 498 /* create a top-level shell for the window manager */ 499 top_level = XtVaAppInitialize( &ctx, 500 argv[0], 501 NULL, 0, /* options */ 502 (ArgcType) &argc, 503 argv, /* might get modified */ 504 NULL, 505 NULL 506 ); 507 508 rc = XtVaCreateManagedWidget( "rc", 509 xmRowColumnWidgetClass, 510 top_level, 511 0 512 ); 513 514 button = XtVaCreateManagedWidget( "Pop up tags dialog", 515 xmPushButtonGadgetClass, 516 rc, 517 0 518 ); 519 XtAddCallback( button, XmNactivateCallback, show_tags, rc ); 520 521 button = XtVaCreateManagedWidget( "Add a tag", 522 xmPushButtonGadgetClass, 523 rc, 524 0 525 ); 526 XtAddCallback( button, XmNactivateCallback, add_a_tag, rc ); 527 528 button = XtVaCreateManagedWidget( "Quit", 529 xmPushButtonGadgetClass, 530 rc, 531 0 532 ); 533 XtAddCallback( button, XmNactivateCallback, exit, 0 ); 534 535 XtRealizeWidget(top_level); 536 XtAppMainLoop(ctx); 537} 538#endif 539