1/*	$NetBSD: m_search.c,v 1.1.1.2 2008/05/18 14:31:28 aymeric Exp $ */
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_search.c,v 8.14 2003/11/05 17:10:00 skimo Exp (Berkeley) Date: 2003/11/05 17:10:00";
16#endif /* not lint */
17
18#include <sys/queue.h>
19
20/* context */
21#include <X11/X.h>
22#include <X11/Intrinsic.h>
23#include <Xm/DialogS.h>
24#include <Xm/Form.h>
25#include <Xm/Label.h>
26#include <Xm/PushBG.h>
27#include <Xm/TextF.h>
28#include <Xm/ToggleB.h>
29#include <Xm/RowColumn.h>
30
31#include <bitstring.h>
32#include <stdio.h>
33#include <stdlib.h>
34
35#undef LOCK_SUCCESS
36#include "../common/common.h"
37#include "../ipc/ip.h"
38#include "m_motif.h"
39
40extern int vi_ofd;
41
42
43/* types */
44
45typedef struct sds {
46    struct sds	*next;
47    Widget	shell;
48} save_dialog;
49
50static	save_dialog	*dialogs = NULL;
51
52typedef struct	{
53    String	name;
54    void	(*cb)();
55} ButtonData;
56
57
58/* globals and constants */
59
60static	String	PatternWidget = "text";
61static	String	pattern = NULL;
62
63static optData  search_toggles[] = {
64	{ optToggle,	"extended",	NULL,	VI_SEARCH_EXT	},
65	{ optToggle,	"iclower",	NULL,	VI_SEARCH_ICL	},
66	{ optToggle,	"ignorecase",	NULL,	VI_SEARCH_IC	},
67	{ optToggle,	"literal",	NULL,	VI_SEARCH_LIT	},
68	{ optToggle,	"searchincr",	NULL,	VI_SEARCH_INCR	},
69	{ optToggle,	"wrapscan",	NULL,	VI_SEARCH_WR	},
70	{ optTerminator,		},
71};
72
73static void done_func __P((Widget));
74static void next_func __P((Widget));
75static void prev_func __P((Widget));
76static void search __P((Widget, int));
77
78static	ButtonData button_data[] = {
79    { "Next",		next_func	},
80    { "Previous", 	prev_func	},
81    { "Cancel", 	done_func	}	/* always last */
82};
83
84
85/* Xt utilities */
86
87#if defined(__STDC__)
88static	Widget	get_child_widget( Widget parent, String name )
89#else
90static	Widget	get_child_widget( parent, name )
91	Widget	parent;
92	String	name;
93#endif
94{
95    char buffer[1024];
96
97    strcpy( buffer, "*" );
98    strcat( buffer, name );
99    return XtNameToWidget( parent, buffer );
100}
101
102
103/* sync the global state */
104
105#if defined(__STDC__)
106static	void	get_state( Widget w )
107#else
108static	void	get_state( w )
109	Widget	w;
110#endif
111{
112#if defined(SelfTest)
113    int		i;
114#endif
115    Widget	shell = w;
116
117    /* get all the data from the root of the widget tree */
118    while ( ! XtIsShell(shell) ) shell = XtParent(shell);
119
120#if defined(SelfTest)
121    /* which flags? */
122    for (i=0; i<XtNumber(toggle_data); i++) {
123	if (( w = get_child_widget( shell, toggle_data[i].name )) != NULL ) {
124	    XtVaGetValues( w, XmNset, &toggle_data[i].value, 0 );
125	}
126    }
127#endif
128
129    /* what's the pattern? */
130    if (( w = get_child_widget( shell, PatternWidget )) != NULL ) {
131	if ( pattern != NULL ) XtFree( pattern );
132	pattern = XmTextFieldGetString( w );
133    }
134}
135
136
137/* Translate the user's actions into nvi commands */
138/*
139 * next_func --
140 *	Action for next button.
141 */
142static void
143next_func(Widget w)
144{
145	search(w, 0);
146}
147
148/*
149 * prev_func --
150 *	Action for previous button.
151 */
152static void
153prev_func(Widget w)
154{
155	search(w, VI_SEARCH_REV);
156}
157
158/*
159 * search --
160 *	Perform the search.
161 */
162static void
163search(Widget w, int flags)
164{
165	IP_BUF ipb;
166	optData *opt;
167	Widget shell;
168
169	shell = w;
170	while ( ! XtIsShell(shell) ) shell = XtParent(shell);
171
172	/* Get current data from the root of the widget tree?
173	 * Do it if we are a child of a dialog shell (assume we
174	 * are a 'Find' dialog).  Otherwise don't (we are the child
175	 * of a menu and being invoked via accelerator)
176	 */
177	if (XmIsDialogShell(shell))
178		get_state(w);
179
180	/* no pattern? probably, we haven't posted a search dialog yet.
181	 * there ought to be a better thing to do here.
182	 */
183	if ( pattern == NULL ) {
184	    vi_info_message( w, "No previous string specified" );
185	    return;
186	}
187
188	ipb.str1 = pattern;
189	ipb.len1 = strlen(pattern);
190
191	/* Initialize the search flags based on the buttons. */
192	ipb.val1 = flags;
193	for (opt = search_toggles; opt->kind != optTerminator; ++opt)
194		if (opt->value != NULL)
195			ipb.val1 |= opt->flags;
196
197	ipb.code = VI_C_SEARCH;
198	vi_send(vi_ofd, "a1", &ipb);
199}
200
201#if defined(__STDC__)
202static	void	done_func( Widget w )
203#else
204static	void	done_func( w )
205	Widget	w;
206#endif
207{
208    save_dialog	*ptr;
209
210#if defined(SelfTest)
211    puts( XtName(w) );
212#endif
213
214    while ( ! XtIsShell(w) ) w = XtParent(w);
215    XtPopdown( w );
216
217    /* save it for later */
218    ptr		= (save_dialog *) malloc( sizeof(save_dialog) );
219    ptr->next	= dialogs;
220    ptr->shell	= w;
221    dialogs	= ptr;
222}
223
224
225/* create a set of push buttons */
226
227#define	SpacingRatio	4	/* 3:1 button to spaces */
228
229#if defined(__STDC__)
230static	Widget	create_push_buttons( Widget parent,
231				     ButtonData *data,
232				     int count
233				    )
234#else
235static	Widget	create_push_buttons( parent, data, count )
236    Widget	parent;
237    ButtonData	*data;
238    int		count;
239#endif
240{
241    Widget	w, form;
242    int		pos = 1, base;
243
244    base = SpacingRatio*count + 1;
245    form = XtVaCreateManagedWidget( "buttons",
246				    xmFormWidgetClass,
247				    parent,
248				    XmNleftAttachment,	XmATTACH_FORM,
249				    XmNrightAttachment,	XmATTACH_FORM,
250				    XmNfractionBase,	base,
251				    XmNshadowType,	XmSHADOW_ETCHED_IN,
252				    XmNshadowThickness,	2,
253				    XmNverticalSpacing,	4,
254				    0
255				    );
256
257    while ( count-- > 0 ) {
258	w = XtVaCreateManagedWidget(data->name,
259				    xmPushButtonGadgetClass,
260				    form,
261				    XmNtopAttachment,	XmATTACH_FORM,
262				    XmNbottomAttachment,XmATTACH_FORM,
263				    XmNleftAttachment,	XmATTACH_POSITION,
264				    XmNleftPosition,	pos,
265				    XmNshowAsDefault,	False,
266				    XmNrightAttachment,	XmATTACH_POSITION,
267				    XmNrightPosition,	pos+SpacingRatio-1,
268				    0
269				    );
270	XtAddCallback( w, XmNactivateCallback, data->cb, 0 );
271	pos += SpacingRatio;
272	data++;
273    }
274
275    /* last button is 'cancel' */
276    XtVaSetValues( XtParent(form), XmNcancelButton, w, 0 );
277
278    return form;
279}
280
281
282/* create a set of check boxes */
283
284#if defined(SelfTest)
285
286#if defined(__STDC__)
287static	Widget	create_check_boxes( Widget parent,
288				    ToggleData *toggles,
289				    int count
290				    )
291#else
292static	Widget	create_check_boxes( parent, toggles, count )
293	Widget	parent;
294	ToggleData *toggles;
295	int	count;
296#endif
297{
298    Widget	form;
299    int		pos = 1, base;
300
301    base = SpacingRatio*count +1;
302    form = XtVaCreateManagedWidget( "toggles",
303				    xmFormWidgetClass,
304				    parent,
305				    XmNleftAttachment,	XmATTACH_FORM,
306				    XmNrightAttachment,	XmATTACH_FORM,
307				    XmNfractionBase,	base,
308				    XmNverticalSpacing,	4,
309				    0
310				    );
311
312    while ( count-- > 0 ) {
313	XtVaCreateManagedWidget(toggles->name,
314				xmToggleButtonWidgetClass,
315				form,
316				XmNtopAttachment,	XmATTACH_FORM,
317				XmNbottomAttachment,	XmATTACH_FORM,
318				XmNleftAttachment,	XmATTACH_POSITION,
319				XmNleftPosition,	pos,
320				XmNrightAttachment,	XmATTACH_POSITION,
321				XmNrightPosition,	pos+SpacingRatio-1,
322				0
323				);
324	pos += SpacingRatio;
325	++toggles;
326    }
327
328    return form;
329}
330
331#endif
332
333
334/* Routines to handle the text field widget */
335
336/* when the user hits 'CR' in a text widget, fire the default pushbutton */
337#if defined(__STDC__)
338static	void	text_cr( Widget w, void *ptr, void *ptr2 )
339#else
340static	void	text_cr( w, ptr, ptr2 )
341	Widget	w;
342	void	*ptr;
343	void	*ptr2;
344#endif
345{
346    next_func( w );
347}
348
349
350#ifdef notdef
351/*
352 * when the user hits any other character, if we are doing incremental
353 * search, send the updated string to nvi
354 *
355 * XXX
356 * I don't currently see any way to make this work -- incremental search
357 * is going to be really nasty.  What makes it worse is that the dialog
358 * box almost certainly obscured a chunk of the text file, so there's no
359 * way to use it even if it works.
360 */
361#if defined(__STDC__)
362static	void	value_changed( Widget w, void *ptr, void *ptr2 )
363#else
364static	void	value_changed( w, ptr, ptr2 )
365	Widget	w;
366	void	*ptr;
367	void	*ptr2;
368#endif
369{
370    /* get all the data from the root of the widget tree */
371    get_state( w );
372
373    /* send it along? */
374#if defined(SelfTest)
375    if ( incremental_search ) send_command( w );
376#else
377    if ( __vi_incremental_search() ) send_command( w );
378#endif
379}
380#endif /* notdef */
381
382
383/* Draw and display a dialog the describes nvi search capability */
384
385#if defined(__STDC__)
386static	Widget	create_search_dialog( Widget parent, String title )
387#else
388static	Widget	create_search_dialog( parent, title )
389	Widget	parent;
390	String	title;
391#endif
392{
393    Widget	box, form, label, text, checks, buttons, form2;
394    save_dialog	*ptr;
395
396    /* use an existing one? */
397    if ( dialogs != NULL ) {
398	box = dialogs->shell;
399	ptr = dialogs->next;
400	free( dialogs );
401	dialogs = ptr;
402	return box;
403    }
404
405    box = XtVaCreatePopupShell( title,
406				xmDialogShellWidgetClass,
407				parent,
408				XmNtitle,		title,
409				XmNallowShellResize,	False,
410				0
411				);
412
413    form = XtVaCreateWidget( "form",
414				xmFormWidgetClass,
415				box,
416				XmNverticalSpacing,	4,
417				XmNhorizontalSpacing,	4,
418				0
419				);
420
421    form2 = XtVaCreateManagedWidget( "form",
422				xmFormWidgetClass,
423				form,
424				XmNtopAttachment,	XmATTACH_FORM,
425				XmNleftAttachment,	XmATTACH_FORM,
426				XmNrightAttachment,	XmATTACH_FORM,
427				0
428				);
429
430    label = XtVaCreateManagedWidget( "Pattern:",
431				    xmLabelWidgetClass,
432				    form2,
433				    XmNtopAttachment,	XmATTACH_FORM,
434				    XmNbottomAttachment,XmATTACH_FORM,
435				    XmNleftAttachment,	XmATTACH_FORM,
436				    0
437				    );
438
439    text = XtVaCreateManagedWidget( PatternWidget,
440				    xmTextFieldWidgetClass,
441				    form2,
442				    XmNtopAttachment,	XmATTACH_FORM,
443				    XmNbottomAttachment,XmATTACH_FORM,
444				    XmNleftAttachment,	XmATTACH_WIDGET,
445				    XmNleftWidget,	label,
446				    XmNrightAttachment,	XmATTACH_FORM,
447				    0
448				    );
449#ifdef notdef
450    XtAddCallback( text, XmNvalueChangedCallback, value_changed, 0 );
451#endif
452    XtAddCallback( text, XmNactivateCallback, text_cr, 0 );
453
454    buttons = create_push_buttons( form, button_data, XtNumber(button_data) );
455    XtVaSetValues( buttons,
456		   XmNbottomAttachment,	XmATTACH_FORM,
457		   0
458		   );
459
460#if defined(SelfTest)
461    checks = create_check_boxes( form, toggle_data, XtNumber(toggle_data) );
462#else
463    checks = (Widget) __vi_create_search_toggles( form, search_toggles );
464#endif
465    XtVaSetValues( checks,
466		   XmNtopAttachment,	XmATTACH_WIDGET,
467		   XmNtopWidget,	form2,
468		   XmNbottomAttachment,	XmATTACH_WIDGET,
469		   XmNbottomWidget,	buttons,
470		   0
471		   );
472
473    XtManageChild( form );
474    return box;
475}
476
477
478/* Module interface to the outside world
479 *
480 *	xip_show_search_dialog( parent, title )
481 *	pops up a search dialog
482 *
483 *	xip_next_search()
484 *	simulates a 'next' assuming that a search has been done
485 */
486
487#if defined(__STDC__)
488void 	__vi_show_search_dialog( Widget parent, String title )
489#else
490void	__vi_show_search_dialog( parent, data, cbs )
491Widget	parent;
492String	title;
493#endif
494{
495    Widget 	db = create_search_dialog( parent, title );
496    Dimension	height;
497
498    /* we can handle getting taller and wider or narrower, but not shorter */
499    XtVaGetValues( db, XmNheight, &height, 0 );
500    XtVaSetValues( db, XmNmaxHeight, height, XmNminHeight, height, 0 );
501
502    /* post the dialog */
503    XtPopup( db, XtGrabNone );
504
505    /* request initial focus to the text widget */
506    XmProcessTraversal( get_child_widget( db, PatternWidget ),
507			XmTRAVERSE_CURRENT
508			);
509}
510
511
512/*
513 * __vi_search --
514 *
515 * PUBLIC: void __vi_search __P((Widget));
516 */
517void
518__vi_search(Widget w)
519{
520    next_func( w );
521}
522
523
524#if defined(SelfTest)
525
526#if defined(__STDC__)
527static void show_search( Widget w, XtPointer data, XtPointer cbs )
528#else
529static void show_search( w, data, cbs )
530Widget w;
531XtPointer	data;
532XtPointer	cbs;
533#endif
534{
535    __vi_show_search_dialog( data, "Search" );
536}
537
538main( int argc, char *argv[] )
539{
540    XtAppContext	ctx;
541    Widget		top_level, rc, button;
542    extern		exit();
543
544    /* create a top-level shell for the window manager */
545    top_level = XtVaAppInitialize( &ctx,
546				   argv[0],
547				   NULL, 0,	/* options */
548				   (ArgcType) &argc,
549				   argv,	/* might get modified */
550				   NULL,
551				   NULL
552				   );
553
554    rc = XtVaCreateManagedWidget( "rc",
555				  xmRowColumnWidgetClass,
556				  top_level,
557				  0
558				  );
559
560    button = XtVaCreateManagedWidget( "Pop up search dialog",
561				      xmPushButtonGadgetClass,
562				      rc,
563				      0
564				      );
565    XtAddCallback( button, XmNactivateCallback, show_search, rc );
566
567    button = XtVaCreateManagedWidget( "Quit",
568				      xmPushButtonGadgetClass,
569				      rc,
570				      0
571				      );
572    XtAddCallback( button, XmNactivateCallback, exit, 0 );
573
574    XtRealizeWidget(top_level);
575    XtAppMainLoop(ctx);
576}
577#endif
578