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