1 /*******************************************************************************
2 *									       *
3 * search.c -- Nirvana Editor search and replace functions		       *
4 *									       *
5 * Copyright (C) 1999 Mark Edel						       *
6 *									       *
7 * This is free software; you can redistribute it and/or modify it under the    *
8 * terms of the GNU General Public License as published by the Free Software    *
9 * Foundation; either version 2 of the License, or (at your option) any later   *
10 * version. In addition, you may distribute version of this program linked to   *
11 * Motif or Open Motif. See README for details.                                 *
12 * 									       *
13 * This software is distributed in the hope that it will be useful, but WITHOUT *
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or        *
15 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License        *
16 * for more details.							       *
17 * 									       *
18 * You should have received a copy of the GNU General Public License along with *
19 * software; if not, write to the Free Software Foundation, Inc., 59 Temple     *
20 * Place, Suite 330, Boston, MA  02111-1307 USA		                       *
21 *									       *
22 * Nirvana Text Editor	    						       *
23 * May 10, 1991								       *
24 *									       *
25 * Written by Mark Edel							       *
26 *									       *
27 *******************************************************************************/
28 
29 #ifdef HAVE_CONFIG_H
30 #include "../config.h"
31 #endif
32 
33 #include "search.h"
34 #include "regularExp.h"
35 #include "textBuf.h"
36 #include "text.h"
37 #include "nedit.h"
38 #include "server.h"
39 #include "window.h"
40 #include "userCmds.h"
41 #include "preferences.h"
42 #include "file.h"
43 #include "highlight.h"
44 #include "selection.h"
45 #ifdef REPLACE_SCOPE
46 #include "textDisp.h"
47 #include "textP.h"
48 #endif
49 #include "../util/DialogF.h"
50 #include "../util/misc.h"
51 #include "../util/nedit_malloc.h"
52 
53 #include <stdlib.h>
54 #include <stdio.h>
55 #include <string.h>
56 #include <ctype.h>
57 #ifdef VMS
58 #include "../util/VMSparam.h"
59 #else
60 #ifndef __MVS__
61 #include <sys/param.h>
62 #endif
63 #endif /*VMS*/
64 
65 #include <Xm/Xm.h>
66 #include <X11/Shell.h>
67 #include <Xm/XmP.h>
68 #include <Xm/Form.h>
69 #include <Xm/Label.h>
70 #ifdef REPLACE_SCOPE
71 #if XmVersion >= 1002
72 #include <Xm/PrimitiveP.h>
73 #endif
74 #endif
75 #include <Xm/PushB.h>
76 #include <Xm/RowColumn.h>
77 #include <Xm/Text.h>
78 #include <Xm/ToggleB.h>
79 #include <Xm/List.h>
80 #include <X11/Xatom.h>		/* for getting selection */
81 #include <X11/keysym.h>
82 #include <X11/X.h>		/* " " */
83 
84 #ifdef HAVE_DEBUG_H
85 #include "../debug.h"
86 #endif
87 
88 
89 int NHist = 0;
90 
91 typedef struct _SelectionInfo {
92     int done;
93     WindowInfo* window;
94     char* selection;
95 } SelectionInfo;
96 
97 typedef struct {
98     int direction;
99     int searchType;
100     int searchWrap;
101 } SearchSelectedCallData;
102 
103 /* History mechanism for search and replace strings */
104 static char *SearchHistory[MAX_SEARCH_HISTORY];
105 static char *ReplaceHistory[MAX_SEARCH_HISTORY];
106 static int SearchTypeHistory[MAX_SEARCH_HISTORY];
107 static int HistStart = 0;
108 
109 static int textFieldNonEmpty(Widget w);
110 static void setTextField(WindowInfo* window, Time time, Widget textField);
111 static void getSelectionCB(Widget w, XtPointer selectionInfo, Atom *selection,
112 	Atom *type, XtPointer value, unsigned long *length, int *format);
113 static void fFocusCB(Widget w, WindowInfo *window, caddr_t *callData);
114 static void rFocusCB(Widget w, WindowInfo *window, caddr_t *callData);
115 static void rKeepCB(Widget w, WindowInfo *window, caddr_t *callData);
116 static void fKeepCB(Widget w, WindowInfo *window, caddr_t *callData);
117 static void replaceCB(Widget w, WindowInfo *window,
118 	XmAnyCallbackStruct *callData);
119 static void replaceAllCB(Widget w, WindowInfo *window,
120 	XmAnyCallbackStruct *callData);
121 static void rInSelCB(Widget w, WindowInfo *window,
122 	XmAnyCallbackStruct *callData);
123 static void rCancelCB(Widget w, WindowInfo *window, caddr_t callData);
124 static void fCancelCB(Widget w, WindowInfo *window, caddr_t callData);
125 static void rFindCB(Widget w,WindowInfo *window,XmAnyCallbackStruct *callData);
126 static void rFindTextValueChangedCB(Widget w, WindowInfo *window, XKeyEvent *event);
127 static void rFindArrowKeyCB(Widget w, WindowInfo *window, XKeyEvent *event);
128 
129 static void rSetActionButtons(WindowInfo* window,
130                               int replaceBtn,
131                               int replaceFindBtn,
132                               int replaceAndFindBtn,
133 #ifndef REPLACE_SCOPE
134                               int replaceInWinBtn,
135                               int replaceInSelBtn,
136 #endif
137                               int replaceAllBtn);
138 #ifdef REPLACE_SCOPE
139 static void rScopeWinCB(Widget w, WindowInfo *window,
140 	XmAnyCallbackStruct *callData);
141 static void rScopeSelCB(Widget w, WindowInfo *window,
142 	XmAnyCallbackStruct *callData);
143 static void rScopeMultiCB(Widget w, WindowInfo *window,
144 	XmAnyCallbackStruct *callData);
145 static void replaceAllScopeCB(Widget w, WindowInfo *window,
146 	XmAnyCallbackStruct *callData);
147 #endif
148 
149 static void replaceArrowKeyCB(Widget w, WindowInfo *window, XKeyEvent *event);
150 static void fUpdateActionButtons(WindowInfo *window);
151 static void findTextValueChangedCB(Widget w, WindowInfo *window, XKeyEvent *event);
152 static void findArrowKeyCB(Widget w, WindowInfo *window, XKeyEvent *event);
153 static void replaceFindCB(Widget w, WindowInfo *window, XmAnyCallbackStruct *callData);
154 static void findCB(Widget w, WindowInfo *window,XmAnyCallbackStruct *callData);
155 static void replaceMultiFileCB(Widget w, WindowInfo *window,
156 	XmAnyCallbackStruct *callData);
157 static void rMultiFileReplaceCB(Widget w, WindowInfo *window,
158        XmAnyCallbackStruct * callData);
159 static void rMultiFileCancelCB(Widget w, WindowInfo *window, caddr_t callData);
160 static void rMultiFileSelectAllCB(Widget w, WindowInfo *window,
161        XmAnyCallbackStruct *callData);
162 static void rMultiFileDeselectAllCB(Widget w, WindowInfo *window,
163        XmAnyCallbackStruct * callData);
164 static void rMultiFilePathCB(Widget w, WindowInfo *window,
165 	XmAnyCallbackStruct *callData);
166 static void uploadFileListItems(WindowInfo* window, Bool replace);
167 static int countWindows(void);
168 static int countWritableWindows(void);
169 static void collectWritableWindows(WindowInfo* window);
170 static void freeWritableWindowsCB(Widget w, WindowInfo* window,
171                                   XmAnyCallbackStruct *callData);
172 static void checkMultiReplaceListForDoomedW(WindowInfo* window,
173                                                      WindowInfo* doomedWindow);
174 static void removeDoomedWindowFromList(WindowInfo* window, int index);
175 static void unmanageReplaceDialogs(const WindowInfo *window);
176 static void flashTimeoutProc(XtPointer clientData, XtIntervalId *id);
177 static void eraseFlash(WindowInfo *window);
178 static int getReplaceDlogInfo(WindowInfo *window, int *direction,
179 	char *searchString, char *replaceString, int *searchType);
180 static int getFindDlogInfo(WindowInfo *window, int *direction,
181 	char *searchString, int *searchType);
182 static void selectedSearchCB(Widget w, XtPointer callData, Atom *selection,
183 	Atom *type, XtPointer value, unsigned long *length, int *format);
184 static void iSearchTextClearAndPasteAP(Widget w, XEvent *event, String *args,
185         Cardinal *nArg);
186 static void iSearchTextClearCB(Widget w, WindowInfo *window,
187 	XmAnyCallbackStruct *callData);
188 static void iSearchTextActivateCB(Widget w, WindowInfo *window,
189 	XmAnyCallbackStruct *callData);
190 static void iSearchTextValueChangedCB(Widget w, WindowInfo *window,
191 	XmAnyCallbackStruct *callData);
192 static void iSearchTextKeyEH(Widget w, WindowInfo *window,
193 	XKeyEvent *event, Boolean *continueDispatch);
194 static int searchLiteral(const char *string, const char *searchString, int caseSense,
195 	int direction, int wrap, int beginPos, int *startPos, int *endPos,
196 	int *searchExtentBW, int *searchExtentFW);
197 static int searchLiteralWord(const char *string, const char *searchString, int caseSense,
198  	int direction, int wrap, int beginPos, int *startPos, int *endPos,
199         const char * delimiters);
200 static int searchRegex(const char *string, const char *searchString, int direction,
201 	int wrap, int beginPos, int *startPos, int *endPos, int *searchExtentBW,
202 	int *searchExtentFW, const char *delimiters, int defaultFlags);
203 static int forwardRegexSearch(const char *string, const char *searchString, int wrap,
204 	int beginPos, int *startPos, int *endPos, int *searchExtentBW,
205         int *searchExtentFW, const char *delimiters, int defaultFlags);
206 static int backwardRegexSearch(const char *string, const char *searchString, int wrap,
207 	int beginPos, int *startPos, int *endPos, int *searchExtentBW,
208         int *searchExtentFW, const char *delimiters, int defaultFlags);
209 static void upCaseString(char *outString, const char *inString);
210 static void downCaseString(char *outString, const char *inString);
211 static void resetFindTabGroup(WindowInfo *window);
212 static void resetReplaceTabGroup(WindowInfo *window);
213 static int searchMatchesSelection(WindowInfo *window, const char *searchString,
214 	int searchType, int *left, int *right, int *searchExtentBW,
215 	int *searchExtentFW);
216 static int findMatchingChar(WindowInfo *window, char toMatch,
217 	void *toMatchStyle, int charPos, int startLimit, int endLimit,
218 	int *matchPos);
219 static Boolean replaceUsingRE(const char* searchStr, const char* replaceStr,
220         const char* sourceStr, int beginPos, char* destStr, int maxDestLen,
221         int prevChar, const char* delimiters, int defaultFlags);
222 static void saveSearchHistory(const char *searchString,
223         const char *replaceString, int searchType, int isIncremental);
224 static int historyIndex(int nCycles);
225 static char *searchTypeArg(int searchType);
226 static char *searchWrapArg(int searchWrap);
227 static char *directionArg(int direction);
228 static int isRegexType(int searchType);
229 static int defaultRegexFlags(int searchType);
230 static void findRegExpToggleCB(Widget w, XtPointer clientData,
231 	XtPointer callData);
232 static void replaceRegExpToggleCB(Widget w, XtPointer clientData,
233 	XtPointer callData);
234 static void iSearchRegExpToggleCB(Widget w, XtPointer clientData,
235 	XtPointer callData);
236 static void findCaseToggleCB(Widget w, XtPointer clientData,
237 	XtPointer callData);
238 static void replaceCaseToggleCB(Widget w, XtPointer clientData,
239 	XtPointer callData);
240 static void iSearchCaseToggleCB(Widget w, XtPointer clientData,
241 	XtPointer callData);
242 static void iSearchTryBeepOnWrap(WindowInfo *window, int direction,
243       	int beginPos, int startPos);
244 static void iSearchRecordLastBeginPos(WindowInfo *window, int direction,
245 	int initPos);
246 static Boolean prefOrUserCancelsSubst(const Widget parent,
247         const Display* display);
248 
249 typedef struct _charMatchTable {
250     char c;
251     char match;
252     char direction;
253 } charMatchTable;
254 
255 #define N_MATCH_CHARS 13
256 #define N_FLASH_CHARS 6
257 static charMatchTable MatchingChars[N_MATCH_CHARS] = {
258     {'{', '}', SEARCH_FORWARD},
259     {'}', '{', SEARCH_BACKWARD},
260     {'(', ')', SEARCH_FORWARD},
261     {')', '(', SEARCH_BACKWARD},
262     {'[', ']', SEARCH_FORWARD},
263     {']', '[', SEARCH_BACKWARD},
264     {'<', '>', SEARCH_FORWARD},
265     {'>', '<', SEARCH_BACKWARD},
266     {'/', '/', SEARCH_FORWARD},
267     {'"', '"', SEARCH_FORWARD},
268     {'\'', '\'', SEARCH_FORWARD},
269     {'`', '`', SEARCH_FORWARD},
270     {'\\', '\\', SEARCH_FORWARD},
271 };
272 
273 /*
274 ** Definitions for the search method strings, used as arguments for
275 ** macro search subroutines and search action routines
276 */
277 static char *searchTypeStrings[] = {
278     "literal",          /* SEARCH_LITERAL         */
279     "case",             /* SEARCH_CASE_SENSE      */
280     "regex",            /* SEARCH_REGEX           */
281     "word",             /* SEARCH_LITERAL_WORD    */
282     "caseWord",         /* SEARCH_CASE_SENSE_WORD */
283     "regexNoCase",      /* SEARCH_REGEX_NOCASE    */
284     NULL
285 };
286 
287 /*
288 ** Window for which a search dialog callback is currently active. That window
289 ** cannot be safely closed because the callback would access the armed button
290 ** after it got destroyed.
291 ** Note that an attempt to close such a window can only happen if the search
292 ** action triggers a modal dialog and the user tries to close the window via
293 ** the window manager without dismissing the dialog first. It is up to the
294 ** close callback of the window to intercept and reject the request by calling
295 ** the WindowCanBeClosed() function.
296 */
297 static WindowInfo *windowNotToClose = NULL;
298 
WindowCanBeClosed(WindowInfo * window)299 Boolean WindowCanBeClosed(WindowInfo *window)
300 {
301     if (windowNotToClose &&
302         GetTopDocument(window->shell) ==
303         GetTopDocument(windowNotToClose->shell)) {
304         return False;
305     }
306     return True; /* It's safe */
307 }
308 
309 /*
310 ** Shared routine for replace and find dialogs and i-search bar to initialize
311 ** the state of the regex/case/word toggle buttons, and the sticky case
312 ** sensitivity states.
313 */
initToggleButtons(int searchType,Widget regexToggle,Widget caseToggle,Widget * wordToggle,Bool * lastLiteralCase,Bool * lastRegexCase)314 static void initToggleButtons(int searchType, Widget regexToggle,
315                               Widget caseToggle, Widget* wordToggle,
316                               Bool* lastLiteralCase,
317                               Bool* lastRegexCase)
318 {
319     /* Set the initial search type and remember the corresponding case
320        sensitivity states in case sticky case sensitivity is required. */
321     switch (searchType) {
322       case SEARCH_LITERAL:
323               *lastLiteralCase = False;
324               *lastRegexCase   = True;
325 	      XmToggleButtonSetState(regexToggle, False, False);
326 	      XmToggleButtonSetState(caseToggle,  False, False);
327 	      if (wordToggle) {
328 		  XmToggleButtonSetState(*wordToggle, False, False);
329                   XtSetSensitive(*wordToggle, True);
330               }
331       break;
332       case SEARCH_CASE_SENSE:
333               *lastLiteralCase = True;
334               *lastRegexCase   = True;
335 	      XmToggleButtonSetState(regexToggle, False, False);
336 	      XmToggleButtonSetState(caseToggle,  True,  False);
337 	      if (wordToggle) {
338                   XmToggleButtonSetState(*wordToggle, False, False);
339                   XtSetSensitive(*wordToggle, True);
340               }
341       break;
342       case SEARCH_LITERAL_WORD:
343               *lastLiteralCase = False;
344               *lastRegexCase   = True;
345 	      XmToggleButtonSetState(regexToggle, False, False);
346 	      XmToggleButtonSetState(caseToggle, False, False);
347 	      if (wordToggle) {
348                   XmToggleButtonSetState(*wordToggle,  True,  False);
349                   XtSetSensitive(*wordToggle, True);
350               }
351       break;
352       case SEARCH_CASE_SENSE_WORD:
353               *lastLiteralCase = True;
354               *lastRegexCase   = True;
355 	      XmToggleButtonSetState(regexToggle, False, False);
356 	      XmToggleButtonSetState(caseToggle,  True,  False);
357 	      if (wordToggle) {
358                   XmToggleButtonSetState(*wordToggle,  True,  False);
359                   XtSetSensitive(*wordToggle, True);
360               }
361       break;
362       case SEARCH_REGEX:
363               *lastLiteralCase = False;
364               *lastRegexCase   = True;
365 	      XmToggleButtonSetState(regexToggle, True,  False);
366 	      XmToggleButtonSetState(caseToggle,  True,  False);
367 	      if (wordToggle) {
368                   XmToggleButtonSetState(*wordToggle,  False, False);
369                   XtSetSensitive(*wordToggle, False);
370               }
371       break;
372       case SEARCH_REGEX_NOCASE:
373               *lastLiteralCase = False;
374               *lastRegexCase   = False;
375 	      XmToggleButtonSetState(regexToggle, True,  False);
376 	      XmToggleButtonSetState(caseToggle,  False, False);
377 	      if (wordToggle) {
378                   XmToggleButtonSetState(*wordToggle,  False, False);
379                   XtSetSensitive(*wordToggle, False);
380               }
381       break;
382     }
383 }
384 
385 #ifdef REPLACE_SCOPE
386 /*
387 ** Checks whether a selection spans multiple lines. Used to decide on the
388 ** default scope for replace dialogs.
389 ** This routine introduces a dependency on textDisp.h, which is not so nice,
390 ** but I currently don't have a cleaner solution.
391 */
selectionSpansMultipleLines(WindowInfo * window)392 static int selectionSpansMultipleLines(WindowInfo *window)
393 {
394     int selStart, selEnd, isRect, rectStart, rectEnd, lineStartStart,
395         lineStartEnd;
396     int lineWidth;
397     textDisp *textD;
398 
399     if (!BufGetSelectionPos(window->buffer, &selStart, &selEnd, &isRect,
400     	    &rectStart, &rectEnd))
401     	return FALSE;
402 
403     /* This is kind of tricky. The perception of a line depends on the
404        line wrap mode being used. So in theory, we should take into
405        account the layout of the text on the screen. However, the
406        routine to calculate a line number for a given character position
407        (TextDPosToLineAndCol) only works for displayed lines, so we cannot
408        use it. Therefore, we use this simple heuristic:
409         - If a newline is found between the start and end of the selection,
410 	  we obviously have a multi-line selection.
411 	- If no newline is found, but the distance between the start and the
412           end of the selection is larger than the number of characters
413 	  displayed on a line, and we're in continuous wrap mode,
414 	  we also assume a multi-line selection.
415     */
416 
417     lineStartStart = BufStartOfLine(window->buffer, selStart);
418     lineStartEnd = BufStartOfLine(window->buffer, selEnd);
419     /* If the line starts differ, we have a "\n" in between. */
420     if (lineStartStart != lineStartEnd )
421 	return TRUE;
422 
423     if (window->wrapMode != CONTINUOUS_WRAP)
424 	return FALSE; /* Same line */
425 
426     /* Estimate the number of characters on a line */
427     textD = ((TextWidget)window->textArea)->text.textD;
428     if (textD->fontStruct->max_bounds.width > 0)
429 	lineWidth = textD->width / textD->fontStruct->max_bounds.width;
430     else
431 	lineWidth = 1;
432     if (lineWidth < 1) lineWidth = 1; /* Just in case */
433 
434     /* Estimate the numbers of line breaks from the start of the line to
435        the start and ending positions of the selection and compare.*/
436     if ((selStart-lineStartStart)/lineWidth !=
437         (selEnd-lineStartStart)/lineWidth )
438        return TRUE; /* Spans multiple lines */
439 
440     return FALSE; /* Small selection; probably doesn't span lines */
441 }
442 #endif
443 
DoFindReplaceDlog(WindowInfo * window,int direction,int keepDialogs,int searchType,Time time)444 void DoFindReplaceDlog(WindowInfo *window, int direction, int keepDialogs,
445         int searchType, Time time)
446 {
447 
448     /* Create the dialog if it doesn't already exist */
449     if (window->replaceDlog == NULL)
450     	CreateReplaceDlog(window->shell, window);
451 
452     setTextField(window, time, window->replaceText);
453 
454     /* If the window is already up, just pop it to the top */
455     if (XtIsManaged(window->replaceDlog)) {
456 	RaiseDialogWindow(XtParent(window->replaceDlog));
457 	return;
458     }
459 
460     /* Blank the Replace with field */
461     XmTextSetString(window->replaceWithText, "");
462 
463     /* Set the initial search type */
464     initToggleButtons(searchType, window->replaceRegexToggle,
465                       window->replaceCaseToggle, &window->replaceWordToggle,
466                       &window->replaceLastLiteralCase,
467                       &window->replaceLastRegexCase);
468 
469     /* Set the initial direction based on the direction argument */
470     XmToggleButtonSetState(window->replaceRevToggle,
471 	direction == SEARCH_FORWARD ? False: True, True);
472 
473     /* Set the state of the Keep Dialog Up button */
474     XmToggleButtonSetState(window->replaceKeepBtn, keepDialogs, True);
475 
476 #ifdef REPLACE_SCOPE
477     /* Set the state of the scope radio buttons to "In Window".
478        Notify to make sure that callbacks are called.
479        NOTE: due to an apparent bug in OpenMotif, the radio buttons may
480        get stuck after resetting the scope to "In Window". Therefore we must
481        use RadioButtonChangeState(), which contains a workaround. */
482     if (window->wasSelected) {
483 	/* If a selection exists, the default scope depends on the preference
484            of the user. */
485 	switch(GetPrefReplaceDefScope()) {
486 	   case REPL_DEF_SCOPE_SELECTION:
487 		/* The user prefers selection scope, no matter what the
488 		   size of the selection is. */
489 		RadioButtonChangeState(window->replaceScopeSelToggle,
490                                        True, True);
491 		break;
492 	   case REPL_DEF_SCOPE_SMART:
493 		if (selectionSpansMultipleLines(window)) {
494 		    /* If the selection spans multiple lines, the user most
495 		       likely wants to perform a replacement in the selection */
496 		    RadioButtonChangeState(window->replaceScopeSelToggle,
497                                            True, True);
498 		}
499 		else {
500 		    /* It's unlikely that the user wants a replacement in a
501 		       tiny selection only. */
502 		    RadioButtonChangeState(window->replaceScopeWinToggle,
503                                            True, True);
504 		}
505 		break;
506 	   default:
507 	   	/* The user always wants window scope as default. */
508 		RadioButtonChangeState(window->replaceScopeWinToggle,
509                                        True, True);
510 		break;
511 	}
512     }
513     else {
514        /* No selection -> always choose "In Window" as default. */
515 	RadioButtonChangeState(window->replaceScopeWinToggle, True, True);
516     }
517 #endif
518 
519     UpdateReplaceActionButtons(window);
520 
521     /* Start the search history mechanism at the current history item */
522     window->rHistIndex = 0;
523 
524     /* Display the dialog */
525     ManageDialogCenteredOnPointer(window->replaceDlog);
526 
527     /* Workaround: LessTif (as of version 0.89) needs reminding of who had
528        the focus when the dialog was unmanaged.  When re-managed, focus is
529        lost and events fall through to the window below. */
530     XmProcessTraversal(window->replaceText, XmTRAVERSE_CURRENT);
531 }
532 
setTextField(WindowInfo * window,Time time,Widget textField)533 static void setTextField(WindowInfo *window, Time time, Widget textField)
534 {
535     XEvent nextEvent;
536     char *primary_selection = 0;
537     SelectionInfo *selectionInfo = NEditNew(SelectionInfo);
538 
539     if (GetPrefFindReplaceUsesSelection()) {
540         selectionInfo->done = 0;
541         selectionInfo->window = window;
542         selectionInfo->selection = 0;
543         XtGetSelectionValue(window->textArea, XA_PRIMARY, XA_STRING,
544                             getSelectionCB, selectionInfo, time);
545         while (selectionInfo->done == 0) {
546             XtAppNextEvent(XtWidgetToApplicationContext(window->textArea), &nextEvent);
547             ServerDispatchEvent(&nextEvent);
548         }
549         primary_selection = selectionInfo->selection;
550     }
551     if (primary_selection == 0) {
552         primary_selection = NEditStrdup("");
553     }
554 
555     /* Update the field */
556     XmTextSetString(textField, primary_selection);
557 
558     NEditFree(primary_selection);
559     NEditFree(selectionInfo);
560 }
561 
getSelectionCB(Widget w,XtPointer si,Atom * selection,Atom * type,XtPointer v,unsigned long * length,int * format)562 static void getSelectionCB(Widget w, XtPointer si, Atom *selection,
563         Atom *type, XtPointer v, unsigned long *length, int *format)
564 {
565     SelectionInfo *selectionInfo = si;
566     char *value = v;
567     WindowInfo *window = selectionInfo->window;
568 
569     /* return an empty string if we can't get the selection data */
570     if (*type == XT_CONVERT_FAIL || *type != XA_STRING || value == NULL || *length == 0) {
571         NEditFree(value);
572         selectionInfo->selection = 0;
573         selectionInfo->done = 1;
574         return;
575     }
576     /* return an empty string if the data is not of the correct format. */
577     if (*format != 8) {
578         DialogF(DF_WARN, window->shell, 1, "Invalid Format",
579                 "NEdit can't handle non 8-bit text", "OK");
580         NEditFree(value);
581         selectionInfo->selection = 0;
582         selectionInfo->done = 1;
583         return;
584     }
585     selectionInfo->selection = (char*)NEditMalloc(*length+1);
586     memcpy(selectionInfo->selection, value, *length);
587     selectionInfo->selection[*length] = 0;
588     NEditFree(value);
589     selectionInfo->done = 1;
590 }
591 
DoFindDlog(WindowInfo * window,int direction,int keepDialogs,int searchType,Time time)592 void DoFindDlog(WindowInfo *window, int direction, int keepDialogs,
593         int searchType, Time time)
594 {
595 
596     /* Create the dialog if it doesn't already exist */
597     if (window->findDlog == NULL)
598     	CreateFindDlog(window->shell, window);
599 
600     setTextField(window, time, window->findText);
601 
602     /* If the window is already up, just pop it to the top */
603     if (XtIsManaged(window->findDlog)) {
604 	RaiseDialogWindow(XtParent(window->findDlog));
605 	return;
606     }
607 
608     /* Set the initial search type */
609     initToggleButtons(searchType, window->findRegexToggle,
610                       window->findCaseToggle, &window->findWordToggle,
611                       &window->findLastLiteralCase,
612                       &window->findLastRegexCase);
613 
614     /* Set the initial direction based on the direction argument */
615     XmToggleButtonSetState(window->findRevToggle,
616 	direction == SEARCH_FORWARD ? False : True, True);
617 
618     /* Set the state of the Keep Dialog Up button */
619     XmToggleButtonSetState(window->findKeepBtn, keepDialogs, True);
620 
621     /* Set the state of the Find button */
622     fUpdateActionButtons(window);
623 
624     /* start the search history mechanism at the current history item */
625     window->fHistIndex = 0;
626 
627     /* Display the dialog */
628     ManageDialogCenteredOnPointer(window->findDlog);
629 
630     /* Workaround: LessTif (as of version 0.89) needs reminding of who had
631        the focus when the dialog was unmanaged.  When re-managed, focus is
632        lost and events fall through to the window below. */
633     XmProcessTraversal(window->findText, XmTRAVERSE_CURRENT);
634 }
635 
DoReplaceMultiFileDlog(WindowInfo * window)636 void DoReplaceMultiFileDlog(WindowInfo *window)
637 {
638     char	searchString[SEARCHMAX], replaceString[SEARCHMAX];
639     int		direction, searchType;
640 
641     /* Validate and fetch the find and replace strings from the dialog */
642     if (!getReplaceDlogInfo(window, &direction, searchString, replaceString,
643     	    &searchType))
644   	return;
645 
646     /* Don't let the user select files when no replacement can be made */
647     if (*searchString == '\0') {
648        /* Set the initial focus of the dialog back to the search string */
649        resetReplaceTabGroup(window);
650        /* pop down the replace dialog */
651        if (!XmToggleButtonGetState(window->replaceKeepBtn))
652     	   unmanageReplaceDialogs(window);
653        return;
654     }
655 
656     /* Create the dialog if it doesn't already exist */
657     if (window->replaceMultiFileDlog == NULL)
658     	CreateReplaceMultiFileDlog(window);
659 
660     /* Raising the window doesn't make sense. It is modal, so we
661        can't get here unless it is unmanaged */
662     /* Prepare a list of writable windows */
663     collectWritableWindows(window);
664 
665     /* Initialize/update the list of files. */
666     uploadFileListItems(window, False);
667 
668     /* Display the dialog */
669     ManageDialogCenteredOnPointer(window->replaceMultiFileDlog);
670 }
671 
672 /*
673 ** If a window is closed (possibly via the window manager) while it is on the
674 ** multi-file replace dialog list of any other window (or even the same one),
675 ** we must update those lists or we end up with dangling references.
676 ** Normally, there can be only one of those dialogs at the same time
677 ** (application modal), but Lesstif doesn't (always) honor application
678 ** modalness, so there can be more than one dialog.
679 */
RemoveFromMultiReplaceDialog(WindowInfo * doomedWindow)680 void RemoveFromMultiReplaceDialog(WindowInfo *doomedWindow)
681 {
682     WindowInfo *w;
683 
684     for (w=WindowList; w!=NULL; w=w->next)
685        if (w->writableWindows)
686           /* A multi-file replacement dialog is up for this window */
687           checkMultiReplaceListForDoomedW(w, doomedWindow);
688 }
689 
CreateReplaceDlog(Widget parent,WindowInfo * window)690 void CreateReplaceDlog(Widget parent, WindowInfo *window)
691 {
692     Arg    	args[50];
693     int    	argcnt, defaultBtnOffset;
694     XmString	st1;
695     Widget	form, btnForm;
696 #ifdef REPLACE_SCOPE
697     Widget	scopeForm, replaceAllBtn;
698 #else
699     Widget	label3, allForm;
700 #endif
701     Widget	inWinBtn, inSelBtn, inMultiBtn;
702     Widget    	searchTypeBox;
703     Widget    	label2, label1, label, replaceText, findText;
704     Widget    	findBtn,  cancelBtn, replaceBtn;
705     Widget    	replaceFindBtn;
706     Widget	searchDirBox, reverseBtn, keepBtn;
707     char 	title[MAXPATHLEN + 19];
708     Dimension	shadowThickness;
709 
710     argcnt = 0;
711     XtSetArg(args[argcnt], XmNautoUnmanage, False); argcnt++;
712     form = CreateFormDialog(parent, "replaceDialog", args, argcnt);
713     XtVaSetValues(form, XmNshadowThickness, 0, NULL);
714     if (GetPrefKeepSearchDlogs()) {
715     	sprintf(title, "Replace/Find (in %s)", window->filename);
716     	XtVaSetValues(XtParent(form), XmNtitle, title, NULL);
717     } else
718     	XtVaSetValues(XtParent(form), XmNtitle, "Replace/Find", NULL);
719 
720     argcnt = 0;
721     XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
722     XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
723     XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
724     XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_NONE); argcnt++;
725     XtSetArg(args[argcnt], XmNleftOffset, 4); argcnt++;
726     XtSetArg(args[argcnt], XmNtopOffset, 6); argcnt++;
727     XtSetArg(args[argcnt], XmNalignment, XmALIGNMENT_BEGINNING); argcnt++;
728     XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("String to Find:"));
729     	    argcnt++;
730     XtSetArg(args[argcnt], XmNmnemonic, 't'); argcnt++;
731     label1 = XmCreateLabel(form, "label1", args, argcnt);
732     XmStringFree(st1);
733     XtManageChild(label1);
734 
735     argcnt = 0;
736     XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
737     XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
738     XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_NONE); argcnt++;
739     XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_FORM); argcnt++;
740     XtSetArg(args[argcnt], XmNrightOffset, 6); argcnt++;
741     XtSetArg(args[argcnt], XmNtopOffset, 6); argcnt++;
742     XtSetArg(args[argcnt], XmNalignment, XmALIGNMENT_END); argcnt++;
743     XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING(
744     	   "(use up arrow key to recall previous)")); argcnt++;
745     label2 = XmCreateLabel(form, "label2", args, argcnt);
746     XmStringFree(st1);
747     XtManageChild(label2);
748 
749     argcnt = 0;
750     XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
751     XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
752     XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_WIDGET); argcnt++;
753     XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
754     XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
755     XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_FORM); argcnt++;
756     XtSetArg(args[argcnt], XmNtopWidget, label1); argcnt++;
757     XtSetArg(args[argcnt], XmNleftOffset, 6); argcnt++;
758     XtSetArg(args[argcnt], XmNrightOffset, 6); argcnt++;
759     XtSetArg(args[argcnt], XmNmaxLength, SEARCHMAX); argcnt++;
760     findText = XmCreateText(form, "replaceString", args, argcnt);
761     XtAddCallback(findText, XmNfocusCallback, (XtCallbackProc)rFocusCB, window);
762     XtAddCallback(findText, XmNvalueChangedCallback,
763       (XtCallbackProc)rFindTextValueChangedCB, window);
764     XtAddEventHandler(findText, KeyPressMask, False,
765     	    (XtEventHandler)rFindArrowKeyCB, window);
766     RemapDeleteKey(findText);
767     XtManageChild(findText);
768     XmAddTabGroup(findText);
769     XtVaSetValues(label1, XmNuserData, findText, NULL); /* mnemonic processing */
770 
771     argcnt = 0;
772     XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_WIDGET); argcnt++;
773     XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
774     XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
775     XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_NONE); argcnt++;
776     XtSetArg(args[argcnt], XmNtopWidget, findText); argcnt++;
777     XtSetArg(args[argcnt], XmNleftOffset, 6); argcnt++;
778     XtSetArg(args[argcnt], XmNtopOffset, 6); argcnt++;
779     XtSetArg(args[argcnt], XmNalignment, XmALIGNMENT_BEGINNING); argcnt++;
780     XtSetArg(args[argcnt], XmNlabelString,
781     	     st1=MKSTRING("Replace With:")); argcnt++;
782     XtSetArg(args[argcnt], XmNmnemonic, 'W'); argcnt++;
783     label = XmCreateLabel(form, "label", args, argcnt);
784     XmStringFree(st1);
785     XtManageChild(label);
786 
787     argcnt = 0;
788     XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
789     XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
790     XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_WIDGET); argcnt++;
791     XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
792     XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
793     XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_FORM); argcnt++;
794     XtSetArg(args[argcnt], XmNtopWidget, label); argcnt++;
795     XtSetArg(args[argcnt], XmNleftOffset, 6); argcnt++;
796     XtSetArg(args[argcnt], XmNrightOffset, 6); argcnt++;
797     XtSetArg(args[argcnt], XmNmaxLength, SEARCHMAX); argcnt++;
798     replaceText = XmCreateText(form, "replaceWithString", args, argcnt);
799     XtAddEventHandler(replaceText, KeyPressMask, False,
800     	    (XtEventHandler)replaceArrowKeyCB, window);
801     RemapDeleteKey(replaceText);
802     XtManageChild(replaceText);
803     XmAddTabGroup(replaceText);
804     XtVaSetValues(label, XmNuserData, replaceText, NULL); /* mnemonic processing */
805 
806     argcnt = 0;
807     XtSetArg(args[argcnt], XmNorientation, XmHORIZONTAL); argcnt++;
808     XtSetArg(args[argcnt], XmNpacking, XmPACK_TIGHT); argcnt++;
809     XtSetArg(args[argcnt], XmNmarginHeight, 0); argcnt++;
810     XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_WIDGET); argcnt++;
811     XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
812     XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_NONE); argcnt++;
813     XtSetArg(args[argcnt], XmNtopWidget, replaceText); argcnt++;
814     XtSetArg(args[argcnt], XmNleftOffset, 2); argcnt++;
815     XtSetArg(args[argcnt], XmNrightOffset, 4); argcnt++;
816     searchTypeBox = XmCreateRowColumn(form, "searchTypeBox", args, argcnt);
817     XtManageChild(searchTypeBox);
818     XmAddTabGroup(searchTypeBox);
819 
820     argcnt = 0;
821     XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
822     XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
823     XtSetArg(args[argcnt], XmNlabelString,
824     	     st1=MKSTRING("Regular Expression")); argcnt++;
825     XtSetArg(args[argcnt], XmNmnemonic, 'R'); argcnt++;
826     window->replaceRegexToggle = XmCreateToggleButton(searchTypeBox, "regExp", args, argcnt);
827     XmStringFree(st1);
828     XtManageChild(window->replaceRegexToggle);
829     XtAddCallback(window->replaceRegexToggle, XmNvalueChangedCallback, (XtCallbackProc) replaceRegExpToggleCB, window);
830 
831     argcnt = 0;
832     XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
833     XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
834     XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("Case Sensitive")); argcnt++;
835     XtSetArg(args[argcnt], XmNmnemonic, 'C'); argcnt++;
836     window->replaceCaseToggle = XmCreateToggleButton(searchTypeBox, "caseSensitive", args, argcnt);
837     XmStringFree(st1);
838     XtManageChild(window->replaceCaseToggle);
839     XtAddCallback(window->replaceCaseToggle, XmNvalueChangedCallback, (XtCallbackProc) replaceCaseToggleCB, window);
840 
841     argcnt = 0;
842     XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
843     XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
844     XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("Whole Word")); argcnt++;
845     XtSetArg(args[argcnt], XmNmnemonic, 'h'); argcnt++;
846     window->replaceWordToggle = XmCreateToggleButton(searchTypeBox, "wholeWord", args, argcnt);
847     XmStringFree(st1);
848     XtManageChild(window->replaceWordToggle);
849 
850     argcnt = 0;
851     XtSetArg(args[argcnt], XmNorientation, XmHORIZONTAL); argcnt++;
852     XtSetArg(args[argcnt], XmNpacking, XmPACK_TIGHT); argcnt++;
853     XtSetArg(args[argcnt], XmNmarginHeight, 0); argcnt++;
854     XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_WIDGET); argcnt++;
855     XtSetArg(args[argcnt], XmNtopOffset, 0); argcnt++;
856     XtSetArg(args[argcnt], XmNtopWidget, searchTypeBox); argcnt++;
857     XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
858     XtSetArg(args[argcnt], XmNleftOffset, 2); argcnt++;
859     XtSetArg(args[argcnt], XmNradioBehavior, False); argcnt++;
860     searchDirBox = XmCreateRowColumn(form, "searchDirBox", args, argcnt);
861     XtManageChild(searchDirBox);
862     XmAddTabGroup(searchDirBox);
863 
864     argcnt = 0;
865     XtSetArg(args[argcnt], XmNlabelString,
866     	     st1=MKSTRING("Search Backward")); argcnt++;
867     XtSetArg(args[argcnt], XmNmnemonic, 'B'); argcnt++;
868     reverseBtn = XmCreateToggleButton(searchDirBox, "reverse", args, argcnt);
869     XmStringFree(st1);
870     XtManageChild(reverseBtn);
871 
872     argcnt = 0;
873     XtSetArg(args[argcnt], XmNlabelString,
874     	     st1=MKSTRING("Keep Dialog")); argcnt++;
875     XtSetArg(args[argcnt], XmNmnemonic, 'K'); argcnt++;
876     XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_WIDGET); argcnt++;
877     XtSetArg(args[argcnt], XmNtopOffset, 0); argcnt++;
878     XtSetArg(args[argcnt], XmNtopWidget, searchTypeBox); argcnt++;
879     XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_FORM); argcnt++;
880     XtSetArg(args[argcnt], XmNrightOffset, 4); argcnt++;
881     keepBtn = XmCreateToggleButton(form, "keep", args, argcnt);
882     XtAddCallback(keepBtn, XmNvalueChangedCallback,
883     	    (XtCallbackProc)rKeepCB, window);
884     XmStringFree(st1);
885     XtManageChild(keepBtn);
886     XmAddTabGroup(keepBtn);
887 
888 #ifdef REPLACE_SCOPE
889     argcnt = 0;
890     XtSetArg(args[argcnt], XmNorientation, XmHORIZONTAL); argcnt++;
891     XtSetArg(args[argcnt], XmNpacking, XmPACK_TIGHT); argcnt++;
892     XtSetArg(args[argcnt], XmNmarginHeight, 0); argcnt++;
893     XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
894     XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_NONE); argcnt++;
895     XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_WIDGET); argcnt++;
896     XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
897     XtSetArg(args[argcnt], XmNtopWidget, searchDirBox); argcnt++;
898     XtSetArg(args[argcnt], XmNleftOffset, 2); argcnt++;
899     XtSetArg(args[argcnt], XmNrightOffset, 6); argcnt++;
900     XtSetArg(args[argcnt], XmNradioBehavior, True); argcnt++;
901     XtSetArg(args[argcnt], XmNradioAlwaysOne, True); argcnt++;
902     scopeForm = XmCreateRowColumn(form, "scope", args, argcnt);
903     XtManageChild(scopeForm);
904     XmAddTabGroup(scopeForm);
905 
906     argcnt = 0;
907     XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
908     XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
909     XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("In Window"));
910         argcnt++;
911     XtSetArg(args[argcnt], XmNmnemonic, 'i'); argcnt++;
912     XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
913     XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
914     XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
915     XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_NONE); argcnt++;
916     inWinBtn = XmCreateToggleButton(scopeForm, "inWindow", args, argcnt);
917     XtAddCallback(inWinBtn, XmNvalueChangedCallback,
918     	(XtCallbackProc)rScopeWinCB, window);
919     XmStringFree(st1);
920     XtManageChild(inWinBtn);
921 
922     argcnt = 0;
923     XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
924     XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
925     XtSetArg(args[argcnt], XmNlabelString,
926     	     st1=MKSTRING("In Selection")); argcnt++;
927     XtSetArg(args[argcnt], XmNmnemonic, 'S'); argcnt++;
928     XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
929     XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
930     XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_WIDGET); argcnt++;
931     XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_NONE); argcnt++;
932     XtSetArg(args[argcnt], XmNleftWidget, inWinBtn); argcnt++;
933     inSelBtn = XmCreateToggleButton(scopeForm, "inSel", args, argcnt);
934     XtAddCallback(inSelBtn, XmNvalueChangedCallback,
935 	(XtCallbackProc)rScopeSelCB, window);
936     XmStringFree(st1);
937     XtManageChild(inSelBtn);
938 
939     argcnt = 0;
940     XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
941     XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
942     XtSetArg(args[argcnt], XmNlabelString,
943     	     st1=MKSTRING("In Multiple Documents")); argcnt++;
944     XtSetArg(args[argcnt], XmNmnemonic, 'M'); argcnt++;
945     XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
946     XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
947     XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_WIDGET); argcnt++;
948     XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_FORM); argcnt++;
949     XtSetArg(args[argcnt], XmNleftWidget, inSelBtn); argcnt++;
950     inMultiBtn = XmCreateToggleButton(scopeForm, "multiFile", args, argcnt);
951     XtAddCallback(inMultiBtn, XmNvalueChangedCallback,
952     	    (XtCallbackProc)rScopeMultiCB, window);
953     XmStringFree(st1);
954     XtManageChild(inMultiBtn);
955 #else
956     argcnt = 0;
957     XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_NONE); argcnt++;
958     XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_FORM); argcnt++;
959     XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_WIDGET); argcnt++;
960     XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
961     XtSetArg(args[argcnt], XmNtopWidget, searchDirBox); argcnt++;
962     XtSetArg(args[argcnt], XmNleftOffset, 6); argcnt++;
963     XtSetArg(args[argcnt], XmNrightOffset, 6); argcnt++;
964     allForm = XmCreateForm(form, "all", args, argcnt);
965     XtManageChild(allForm);
966     XmAddTabGroup(allForm);
967 
968     argcnt = 0;
969     XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
970     XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
971     XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
972     XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_NONE); argcnt++;
973     XtSetArg(args[argcnt], XmNleftOffset, 4); argcnt++;
974     XtSetArg(args[argcnt], XmNtopOffset, 6); argcnt++;
975     XtSetArg(args[argcnt], XmNalignment, XmALIGNMENT_BEGINNING); argcnt++;
976     XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("Replace all in:"));
977     	    argcnt++;
978     label3 = XmCreateLabel(allForm, "label3", args, argcnt);
979     XmStringFree(st1);
980     XtManageChild(label3);
981 
982     argcnt = 0;
983     XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
984     XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
985     XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("Window"));
986         argcnt++;
987     XtSetArg(args[argcnt], XmNmnemonic, 'i'); argcnt++;
988     XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
989     XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
990     XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_WIDGET); argcnt++;
991     XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_NONE); argcnt++;
992     XtSetArg(args[argcnt], XmNleftWidget, label3); argcnt++;
993     inWinBtn = XmCreatePushButton(allForm, "inWindow", args, argcnt);
994     XtAddCallback(inWinBtn, XmNactivateCallback,
995     	(XtCallbackProc)replaceAllCB, window);
996     XmStringFree(st1);
997     XtManageChild(inWinBtn);
998 
999     argcnt = 0;
1000     XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1001     XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1002     XtSetArg(args[argcnt], XmNlabelString,
1003     	     st1=MKSTRING("Selection")); argcnt++;
1004     XtSetArg(args[argcnt], XmNmnemonic, 'S'); argcnt++;
1005     XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
1006     XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
1007     XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_WIDGET); argcnt++;
1008     XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_NONE); argcnt++;
1009     XtSetArg(args[argcnt], XmNleftWidget, inWinBtn); argcnt++;
1010     inSelBtn = XmCreatePushButton(allForm, "inSel", args, argcnt);
1011     XtAddCallback(inSelBtn, XmNactivateCallback,
1012 	(XtCallbackProc)rInSelCB, window);
1013     XmStringFree(st1);
1014     XtManageChild(inSelBtn);
1015 
1016     argcnt = 0;
1017     XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1018     XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1019     XtSetArg(args[argcnt], XmNlabelString,
1020     	     st1=MKSTRING("Multiple Documents...")); argcnt++;
1021     XtSetArg(args[argcnt], XmNmnemonic, 'M'); argcnt++;
1022     XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
1023     XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
1024     XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_WIDGET); argcnt++;
1025     XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_FORM); argcnt++;
1026     XtSetArg(args[argcnt], XmNleftWidget, inSelBtn); argcnt++;
1027     inMultiBtn = XmCreatePushButton(allForm, "multiFile", args, argcnt);
1028     XtAddCallback(inMultiBtn, XmNactivateCallback,
1029     	    (XtCallbackProc)replaceMultiFileCB, window);
1030     XmStringFree(st1);
1031     XtManageChild(inMultiBtn);
1032 
1033 #endif
1034 
1035     argcnt = 0;
1036     XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
1037     XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_FORM); argcnt++;
1038     XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_WIDGET); argcnt++;
1039     XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
1040 #ifdef REPLACE_SCOPE
1041     XtSetArg(args[argcnt], XmNtopWidget, scopeForm); argcnt++;
1042 #else
1043     XtSetArg(args[argcnt], XmNtopWidget, allForm); argcnt++;
1044 #endif
1045     XtSetArg(args[argcnt], XmNleftOffset, 6); argcnt++;
1046     XtSetArg(args[argcnt], XmNrightOffset, 6); argcnt++;
1047     btnForm = XmCreateForm(form, "buttons", args, argcnt);
1048     XtManageChild(btnForm);
1049     XmAddTabGroup(btnForm);
1050 
1051     argcnt = 0;
1052     XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1053     XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1054     XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("Replace")); argcnt++;
1055     XtSetArg(args[argcnt], XmNshowAsDefault, (short)1); argcnt++;
1056     XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
1057     XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_POSITION); argcnt++;
1058     XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_POSITION); argcnt++;
1059     XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_FORM); argcnt++;
1060 #ifdef REPLACE_SCOPE
1061     XtSetArg(args[argcnt], XmNleftPosition, 0); argcnt++;
1062     XtSetArg(args[argcnt], XmNrightPosition, 21); argcnt++;
1063 #else
1064     XtSetArg(args[argcnt], XmNleftPosition, 0); argcnt++;
1065     XtSetArg(args[argcnt], XmNrightPosition, 25); argcnt++;
1066 #endif
1067     replaceBtn = XmCreatePushButton(btnForm, "replace", args, argcnt);
1068     XtAddCallback(replaceBtn, XmNactivateCallback, (XtCallbackProc)replaceCB, window);
1069     XmStringFree(st1);
1070     XtManageChild(replaceBtn);
1071     XtVaGetValues(replaceBtn, XmNshadowThickness, &shadowThickness, NULL);
1072     defaultBtnOffset = shadowThickness + 4;
1073 
1074     argcnt = 0;
1075     XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1076     XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1077     XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("Find")); argcnt++;
1078     XtSetArg(args[argcnt], XmNmnemonic, 'F'); argcnt++;
1079     XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
1080     XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_FORM); argcnt++;
1081     XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_POSITION); argcnt++;
1082     XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_POSITION); argcnt++;
1083 #ifdef REPLACE_SCOPE
1084     XtSetArg(args[argcnt], XmNleftPosition, 21); argcnt++;
1085     XtSetArg(args[argcnt], XmNrightPosition, 33); argcnt++;
1086 #else
1087     XtSetArg(args[argcnt], XmNleftPosition, 25); argcnt++;
1088     XtSetArg(args[argcnt], XmNrightPosition, 42); argcnt++;
1089 #endif
1090     XtSetArg(args[argcnt], XmNtopOffset, defaultBtnOffset); argcnt++;
1091     XtSetArg(args[argcnt], XmNbottomOffset, defaultBtnOffset); argcnt++;
1092     findBtn = XmCreatePushButton(btnForm, "find", args, argcnt);
1093     XtAddCallback(findBtn, XmNactivateCallback, (XtCallbackProc)rFindCB, window);
1094     XmStringFree(st1);
1095     XtManageChild(findBtn);
1096 
1097     argcnt = 0;
1098     XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1099     XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1100     XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("Replace & Find")); argcnt++;
1101     XtSetArg(args[argcnt], XmNmnemonic, 'n'); argcnt++;
1102     XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
1103     XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_FORM); argcnt++;
1104     XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_POSITION); argcnt++;
1105     XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_POSITION); argcnt++;
1106 #ifdef REPLACE_SCOPE
1107     XtSetArg(args[argcnt], XmNleftPosition, 33); argcnt++;
1108     XtSetArg(args[argcnt], XmNrightPosition, 62); argcnt++;
1109 #else
1110     XtSetArg(args[argcnt], XmNleftPosition, 42); argcnt++;
1111     XtSetArg(args[argcnt], XmNrightPosition, 79); argcnt++;
1112 #endif
1113     XtSetArg(args[argcnt], XmNtopOffset, defaultBtnOffset); argcnt++;
1114     XtSetArg(args[argcnt], XmNbottomOffset, defaultBtnOffset); argcnt++;
1115     replaceFindBtn = XmCreatePushButton(btnForm, "replacefind", args, argcnt);
1116     XtAddCallback(replaceFindBtn, XmNactivateCallback, (XtCallbackProc)replaceFindCB, window);
1117     XmStringFree(st1);
1118     XtManageChild(replaceFindBtn);
1119 
1120 #ifdef REPLACE_SCOPE
1121     argcnt = 0;
1122     XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1123     XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1124     XtSetArg(args[argcnt], XmNlabelString,
1125     	     st1=MKSTRING("Replace All")); argcnt++;
1126     XtSetArg(args[argcnt], XmNmnemonic, 'A'); argcnt++;
1127     XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
1128     XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
1129     XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_POSITION); argcnt++;
1130     XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_POSITION); argcnt++;
1131     XtSetArg(args[argcnt], XmNleftPosition, 62); argcnt++;
1132     XtSetArg(args[argcnt], XmNrightPosition, 85); argcnt++;
1133     XtSetArg(args[argcnt], XmNtopOffset, defaultBtnOffset); argcnt++;
1134     replaceAllBtn = XmCreatePushButton(btnForm, "all", args, argcnt);
1135     XtAddCallback(replaceAllBtn, XmNactivateCallback,
1136     	    (XtCallbackProc)replaceAllScopeCB, window);
1137     XmStringFree(st1);
1138     XtManageChild(replaceAllBtn);
1139 #endif
1140 
1141     argcnt = 0;
1142     XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1143     XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1144     XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("Cancel")); argcnt++;
1145     XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
1146     XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_FORM); argcnt++;
1147     XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_POSITION); argcnt++;
1148     XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_POSITION); argcnt++;
1149 #ifdef REPLACE_SCOPE
1150     XtSetArg(args[argcnt], XmNleftPosition, 85); argcnt++;
1151     XtSetArg(args[argcnt], XmNrightPosition, 100); argcnt++;
1152 #else
1153     XtSetArg(args[argcnt], XmNleftPosition, 79); argcnt++;
1154     XtSetArg(args[argcnt], XmNrightPosition, 100); argcnt++;
1155 #endif
1156     XtSetArg(args[argcnt], XmNtopOffset, defaultBtnOffset); argcnt++;
1157     XtSetArg(args[argcnt], XmNbottomOffset, defaultBtnOffset); argcnt++;
1158     cancelBtn = XmCreatePushButton(btnForm, "cancel", args, argcnt);
1159     XmStringFree(st1);
1160     XtAddCallback(cancelBtn, XmNactivateCallback, (XtCallbackProc)rCancelCB,
1161     	    window);
1162     XtManageChild(cancelBtn);
1163 
1164     XtVaSetValues(form, XmNcancelButton, cancelBtn, NULL);
1165     AddDialogMnemonicHandler(form, FALSE);
1166 
1167     window->replaceDlog = form;
1168     window->replaceText = findText;
1169     window->replaceWithText = replaceText;
1170     window->replaceRevToggle = reverseBtn;
1171     window->replaceKeepBtn = keepBtn;
1172     window->replaceBtns = btnForm;
1173     window->replaceBtn = replaceBtn;
1174     window->replaceAndFindBtn = replaceFindBtn;
1175     window->replaceFindBtn = findBtn;
1176     window->replaceSearchTypeBox = searchTypeBox;
1177 #ifdef REPLACE_SCOPE
1178     window->replaceAllBtn = replaceAllBtn;
1179     window->replaceScopeWinToggle = inWinBtn;
1180     window->replaceScopeSelToggle = inSelBtn;
1181     window->replaceScopeMultiToggle = inMultiBtn;
1182 #else
1183     window->replaceInWinBtn = inWinBtn;
1184     window->replaceAllBtn = inMultiBtn;
1185     window->replaceInSelBtn = inSelBtn;
1186 #endif
1187 }
1188 
CreateFindDlog(Widget parent,WindowInfo * window)1189 void CreateFindDlog(Widget parent, WindowInfo *window)
1190 {
1191     Arg    	args[50];
1192     int    	argcnt, defaultBtnOffset;
1193     XmString	st1;
1194     Widget	form, btnForm, searchTypeBox;
1195     Widget	findText, label1, label2, cancelBtn, findBtn;
1196     Widget	searchDirBox, reverseBtn, keepBtn;
1197     char 	title[MAXPATHLEN + 11];
1198     Dimension	shadowThickness;
1199 
1200     argcnt = 0;
1201     XtSetArg(args[argcnt], XmNautoUnmanage, False); argcnt++;
1202     form = CreateFormDialog(parent, "findDialog", args, argcnt);
1203     XtVaSetValues(form, XmNshadowThickness, 0, NULL);
1204     if (GetPrefKeepSearchDlogs()) {
1205     	sprintf(title, "Find (in %s)", window->filename);
1206     	XtVaSetValues(XtParent(form), XmNtitle, title, NULL);
1207     } else
1208     	XtVaSetValues(XtParent(form), XmNtitle, "Find", NULL);
1209 
1210     argcnt = 0;
1211     XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
1212     XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
1213     XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
1214     XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_NONE); argcnt++;
1215     XtSetArg(args[argcnt], XmNleftOffset, 6); argcnt++;
1216     XtSetArg(args[argcnt], XmNtopOffset, 6); argcnt++;
1217     XtSetArg(args[argcnt], XmNalignment, XmALIGNMENT_BEGINNING); argcnt++;
1218     XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("String to Find:"));
1219     	    argcnt++;
1220     XtSetArg(args[argcnt], XmNmnemonic, 'S'); argcnt++;
1221     label1 = XmCreateLabel(form, "label1", args, argcnt);
1222     XmStringFree(st1);
1223     XtManageChild(label1);
1224 
1225     argcnt = 0;
1226     XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
1227     XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
1228     XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_NONE); argcnt++;
1229     XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_FORM); argcnt++;
1230     XtSetArg(args[argcnt], XmNrightOffset, 6); argcnt++;
1231     XtSetArg(args[argcnt], XmNtopOffset, 6); argcnt++;
1232     XtSetArg(args[argcnt], XmNalignment, XmALIGNMENT_END); argcnt++;
1233     XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING(
1234     	   "(use up arrow key to recall previous)")); argcnt++;
1235     label2 = XmCreateLabel(form, "label2", args, argcnt);
1236     XmStringFree(st1);
1237     XtManageChild(label2);
1238 
1239     argcnt = 0;
1240     XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1241     XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1242     XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_WIDGET); argcnt++;
1243     XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
1244     XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
1245     XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_FORM); argcnt++;
1246     XtSetArg(args[argcnt], XmNtopWidget, label1); argcnt++;
1247     XtSetArg(args[argcnt], XmNleftOffset, 6); argcnt++;
1248     XtSetArg(args[argcnt], XmNrightOffset, 6); argcnt++;
1249     XtSetArg(args[argcnt], XmNmaxLength, SEARCHMAX); argcnt++;
1250     findText = XmCreateText(form, "searchString", args, argcnt);
1251     XtAddCallback(findText, XmNfocusCallback, (XtCallbackProc)fFocusCB, window);
1252     XtAddCallback(findText, XmNvalueChangedCallback,
1253       (XtCallbackProc)findTextValueChangedCB, window);
1254     XtAddEventHandler(findText, KeyPressMask, False,
1255     	    (XtEventHandler)findArrowKeyCB, window);
1256     RemapDeleteKey(findText);
1257     XtManageChild(findText);
1258     XmAddTabGroup(findText);
1259     XtVaSetValues(label1, XmNuserData, findText, NULL); /* mnemonic processing */
1260 
1261     argcnt = 0;
1262     XtSetArg(args[argcnt], XmNorientation, XmHORIZONTAL); argcnt++;
1263     XtSetArg(args[argcnt], XmNpacking, XmPACK_TIGHT); argcnt++;
1264     XtSetArg(args[argcnt], XmNmarginHeight, 0); argcnt++;
1265     XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_WIDGET); argcnt++;
1266     XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
1267     XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_NONE); argcnt++;
1268     XtSetArg(args[argcnt], XmNtopWidget, findText); argcnt++;
1269     XtSetArg(args[argcnt], XmNleftOffset, 2); argcnt++;
1270     XtSetArg(args[argcnt], XmNrightOffset, 4); argcnt++;
1271 
1272     searchTypeBox = XmCreateRowColumn(form, "searchTypeBox", args, argcnt);
1273     XtManageChild(searchTypeBox);
1274     XmAddTabGroup(searchTypeBox);
1275 
1276     argcnt = 0;
1277     XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1278     XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1279     XtSetArg(args[argcnt], XmNlabelString,
1280     	     st1=MKSTRING("Regular Expression")); argcnt++;
1281     XtSetArg(args[argcnt], XmNmnemonic, 'R'); argcnt++;
1282     window->findRegexToggle = XmCreateToggleButton(searchTypeBox, "regExp", args, argcnt);
1283     XmStringFree(st1);
1284     XtManageChild(window->findRegexToggle);
1285     XtAddCallback(window->findRegexToggle, XmNvalueChangedCallback, (XtCallbackProc) findRegExpToggleCB, window);
1286 
1287     argcnt = 0;
1288     XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1289     XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1290     XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("Case Sensitive")); argcnt++;
1291     XtSetArg(args[argcnt], XmNmnemonic, 'C'); argcnt++;
1292     window->findCaseToggle = XmCreateToggleButton(searchTypeBox, "caseSensitive", args, argcnt);
1293     XmStringFree(st1);
1294     XtManageChild(window->findCaseToggle);
1295     XtAddCallback(window->findCaseToggle, XmNvalueChangedCallback, (XtCallbackProc) findCaseToggleCB, window);
1296 
1297     argcnt = 0;
1298     XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1299     XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1300     XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("Whole Word")); argcnt++;
1301     XtSetArg(args[argcnt], XmNmnemonic, 'h'); argcnt++;
1302     window->findWordToggle = XmCreateToggleButton(searchTypeBox, "wholeWord", args, argcnt);
1303     XmStringFree(st1);
1304     XtManageChild(window->findWordToggle);
1305 
1306     argcnt = 0;
1307     XtSetArg(args[argcnt], XmNorientation, XmHORIZONTAL); argcnt++;
1308     XtSetArg(args[argcnt], XmNpacking, XmPACK_TIGHT); argcnt++;
1309     XtSetArg(args[argcnt], XmNmarginHeight, 0); argcnt++;
1310     XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_WIDGET); argcnt++;
1311     XtSetArg(args[argcnt], XmNtopOffset, 0); argcnt++;
1312     XtSetArg(args[argcnt], XmNtopWidget, searchTypeBox); argcnt++;
1313     XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
1314     XtSetArg(args[argcnt], XmNleftOffset, 2); argcnt++;
1315     XtSetArg(args[argcnt], XmNradioBehavior, False); argcnt++;
1316     searchDirBox = XmCreateRowColumn(form, "searchDirBox", args, argcnt);
1317     XtManageChild(searchDirBox);
1318     XmAddTabGroup(searchDirBox);
1319 
1320     argcnt = 0;
1321     XtSetArg(args[argcnt], XmNlabelString,
1322     	     st1=MKSTRING("Search Backward")); argcnt++;
1323     XtSetArg(args[argcnt], XmNmnemonic, 'B'); argcnt++;
1324     reverseBtn = XmCreateToggleButton(searchDirBox, "reverse", args, argcnt);
1325     XmStringFree(st1);
1326     XtManageChild(reverseBtn);
1327 
1328     argcnt = 0;
1329     XtSetArg(args[argcnt], XmNlabelString,
1330     	     st1=MKSTRING("Keep Dialog")); argcnt++;
1331     XtSetArg(args[argcnt], XmNmnemonic, 'K'); argcnt++;
1332     XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_WIDGET); argcnt++;
1333     XtSetArg(args[argcnt], XmNtopOffset, 0); argcnt++;
1334     XtSetArg(args[argcnt], XmNtopWidget, searchTypeBox); argcnt++;
1335     XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_FORM); argcnt++;
1336     XtSetArg(args[argcnt], XmNrightOffset, 4); argcnt++;
1337     keepBtn = XmCreateToggleButton(form, "keep", args, argcnt);
1338     XtAddCallback(keepBtn, XmNvalueChangedCallback,
1339     	    (XtCallbackProc)fKeepCB, window);
1340     XmStringFree(st1);
1341     XtManageChild(keepBtn);
1342     XmAddTabGroup(keepBtn);
1343 
1344     argcnt = 0;
1345     XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
1346     XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_FORM); argcnt++;
1347     XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_WIDGET); argcnt++;
1348     XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
1349     XtSetArg(args[argcnt], XmNtopWidget, searchDirBox); argcnt++;
1350     XtSetArg(args[argcnt], XmNleftOffset, 2); argcnt++;
1351     XtSetArg(args[argcnt], XmNrightOffset, 4); argcnt++;
1352     btnForm = XmCreateForm(form, "buttons", args, argcnt);
1353     XtManageChild(btnForm);
1354     XmAddTabGroup(btnForm);
1355 
1356     argcnt = 0;
1357     XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1358     XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1359     XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("Find")); argcnt++;
1360     XtSetArg(args[argcnt], XmNshowAsDefault, (short)1); argcnt++;
1361     XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
1362     XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_POSITION); argcnt++;
1363     XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_NONE); argcnt++;
1364     XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
1365     XtSetArg(args[argcnt], XmNleftPosition, 20); argcnt++;
1366     XtSetArg(args[argcnt], XmNbottomOffset, 6); argcnt++;
1367     findBtn = XmCreatePushButton(btnForm, "find", args, argcnt);
1368     XtAddCallback(findBtn, XmNactivateCallback, (XtCallbackProc)findCB, window);
1369     XmStringFree(st1);
1370     XtManageChild(findBtn);
1371     XtVaGetValues(findBtn, XmNshadowThickness, &shadowThickness, NULL);
1372     defaultBtnOffset = shadowThickness + 4;
1373 
1374     argcnt = 0;
1375     XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1376     XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1377     XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("Cancel")); argcnt++;
1378     XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
1379     XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
1380     XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_NONE); argcnt++;
1381     XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_POSITION); argcnt++;
1382     XtSetArg(args[argcnt], XmNrightPosition, 80); argcnt++;
1383     XtSetArg(args[argcnt], XmNtopOffset, defaultBtnOffset); argcnt++;
1384     cancelBtn = XmCreatePushButton(btnForm, "cancel", args, argcnt);
1385     XtAddCallback(cancelBtn, XmNactivateCallback, (XtCallbackProc)fCancelCB,
1386     	    window);
1387     XmStringFree(st1);
1388     XtManageChild(cancelBtn);
1389     XtVaSetValues(form, XmNcancelButton, cancelBtn, NULL);
1390     AddDialogMnemonicHandler(form, FALSE);
1391 
1392     window->findDlog = form;
1393     window->findText = findText;
1394     window->findRevToggle = reverseBtn;
1395     window->findKeepBtn = keepBtn;
1396     window->findBtns = btnForm;
1397     window->findBtn = findBtn;
1398     window->findSearchTypeBox = searchTypeBox;
1399 }
1400 
CreateReplaceMultiFileDlog(WindowInfo * window)1401 void CreateReplaceMultiFileDlog(WindowInfo *window)
1402 {
1403     Arg		args[50];
1404     int		argcnt, defaultBtnOffset;
1405     XmString	st1;
1406     Widget	list, label1, form, pathBtn;
1407     Widget	btnForm, replaceBtn, selectBtn, deselectBtn, cancelBtn;
1408     Dimension	shadowThickness;
1409 
1410     argcnt = 0;
1411     XtSetArg(args[argcnt], XmNautoUnmanage, False); argcnt++;
1412     XtSetArg (args[argcnt], XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL);
1413 	    argcnt ++;
1414 
1415     /* Ideally, we should create the multi-file dialog as a child widget
1416        of the replace dialog. However, if we do this, the main window
1417        can hide the multi-file dialog when raised (I'm not sure why, but
1418        it's something that I observed with fvwm). By using the main window
1419        as the parent, it is possible that the replace dialog _partially_
1420        covers the multi-file dialog, but this much better than the multi-file
1421        dialog being covered completely by the main window */
1422     form = CreateFormDialog(window->shell, "replaceMultiFileDialog",
1423            			     args, argcnt);
1424     XtVaSetValues(form, XmNshadowThickness, 0, NULL);
1425     XtVaSetValues(XtParent(form), XmNtitle, "Replace All in Multiple Documents",
1426 		  NULL);
1427 
1428     /* Label at top left. */
1429     argcnt = 0;
1430     XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
1431     XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
1432     XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
1433     XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_NONE); argcnt++;
1434     /* Offset = 6 + (highlightThickness + detailShadowThickness) of the
1435        toggle button (see below). Unfortunately, detailShadowThickness is
1436        a Motif 2.x property, so we can't measure it. The default is 2 pixels.
1437        To make things even more complicated, the SunOS 5.6 / Solaris 2.6
1438        version of Motif 1.2 seems to use a detailShadowThickness of 0 ...
1439        So we'll have to live with a slight misalignment on that platform
1440        (those Motif libs are known to have many other problems). */
1441     XtSetArg(args[argcnt], XmNtopOffset, 10); argcnt++;
1442     XtSetArg(args[argcnt], XmNleftOffset, 6); argcnt++;
1443     XtSetArg(args[argcnt], XmNalignment, XmALIGNMENT_BEGINNING); argcnt++;
1444     XtSetArg(args[argcnt], XmNlabelString,
1445        st1=MKSTRING("Files in which to Replace All:")); argcnt++;
1446     XtSetArg(args[argcnt], XmNmnemonic, 'F'); argcnt++;
1447     label1 = XmCreateLabel(form, "label1", args, argcnt);
1448     XmStringFree(st1);
1449     XtManageChild(label1);
1450 
1451     /* Pathname toggle button at top right (always unset by default) */
1452     argcnt = 0;
1453     XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1454     XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1455     XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_NONE); argcnt++;
1456     XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_FORM); argcnt++;
1457     XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
1458     XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
1459     XtSetArg(args[argcnt], XmNset, False); argcnt++;
1460     XtSetArg(args[argcnt], XmNrightOffset, 6); argcnt++;
1461     XtSetArg(args[argcnt], XmNtopOffset, 6); argcnt++;
1462     XtSetArg(args[argcnt], XmNalignment, XmALIGNMENT_END); argcnt++;
1463     XtSetArg(args[argcnt], XmNlabelString,
1464     	     st1=MKSTRING("Show Path Names")); argcnt++;
1465     XtSetArg(args[argcnt], XmNmnemonic, 'P'); argcnt++;
1466     pathBtn = XmCreateToggleButton(form, "path", args, argcnt);
1467     XmStringFree(st1);
1468     XtAddCallback(pathBtn, XmNvalueChangedCallback,
1469     	    (XtCallbackProc)rMultiFilePathCB, window);
1470     XtManageChild(pathBtn);
1471 
1472     /*
1473      * Buttons at bottom. Place them before the list, such that we can
1474      * attach the list to the label and the button box. In that way only
1475      * the lists resizes vertically when the dialog is resized; users expect
1476      * the list to resize, not the buttons.
1477      */
1478 
1479     argcnt = 0;
1480     XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
1481     XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_FORM); argcnt++;
1482     XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_NONE); argcnt++;
1483     XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_FORM); argcnt++;
1484     XtSetArg(args[argcnt], XmNleftOffset, 6); argcnt++;
1485     XtSetArg(args[argcnt], XmNrightOffset, 6); argcnt++;
1486     XtSetArg(args[argcnt], XmNtopOffset, 6); argcnt++;
1487     XtSetArg(args[argcnt], XmNresizable, (short)0); argcnt++;
1488     btnForm = XmCreateForm(form, "buttons", args, argcnt);
1489     XtManageChild(btnForm);
1490 
1491     /* Replace */
1492     argcnt = 0;
1493     XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1494     XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1495     XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("Replace")); argcnt++;
1496     XtSetArg(args[argcnt], XmNshowAsDefault, (short)1); argcnt++;
1497     XtSetArg(args[argcnt], XmNmnemonic, 'R'); argcnt++;
1498     XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
1499     XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
1500     XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_POSITION); argcnt++;
1501     XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_POSITION); argcnt++;
1502     XtSetArg(args[argcnt], XmNleftPosition, 0); argcnt++;
1503     XtSetArg(args[argcnt], XmNrightPosition, 25); argcnt++;
1504     replaceBtn = XmCreatePushButton(btnForm, "replace", args, argcnt);
1505     XmStringFree(st1);
1506     XtAddCallback(replaceBtn, XmNactivateCallback,
1507        (XtCallbackProc)rMultiFileReplaceCB, window);
1508     /*
1509      * _DON'T_ set the replace button as default (as in other dialogs).
1510      * Multi-selection lists have the nasty property of selecting the
1511      * current item when <enter> is pressed.
1512      * In that way, the user could inadvertently select an additional file
1513      * (most likely the last one that was deselected).
1514      * The user has to activate the replace button explictly (either with
1515      * a mouse click or with the shortcut key).
1516      *
1517      * XtVaSetValues(form, XmNdefaultButton, replaceBtn, NULL); */
1518 
1519     XtManageChild(replaceBtn);
1520     XtVaGetValues(replaceBtn, XmNshadowThickness, &shadowThickness, NULL);
1521     defaultBtnOffset = shadowThickness + 4;
1522 
1523     /* Select All */
1524     argcnt = 0;
1525     XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1526     XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1527     XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("Select All"));
1528        argcnt++;
1529     XtSetArg(args[argcnt], XmNmnemonic, 'S'); argcnt++;
1530     XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
1531     XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
1532     XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_POSITION); argcnt++;
1533     XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_POSITION); argcnt++;
1534     XtSetArg(args[argcnt], XmNleftPosition, 25); argcnt++;
1535     XtSetArg(args[argcnt], XmNrightPosition, 50); argcnt++;
1536     XtSetArg(args[argcnt], XmNtopOffset, defaultBtnOffset); argcnt++;
1537     selectBtn = XmCreatePushButton(btnForm, "select", args, argcnt);
1538     XmStringFree(st1);
1539     XtAddCallback(selectBtn, XmNactivateCallback,
1540        (XtCallbackProc)rMultiFileSelectAllCB, window);
1541     XtManageChild(selectBtn);
1542 
1543     /* Deselect All */
1544     argcnt = 0;
1545     XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1546     XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1547     XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("Deselect All"));
1548        argcnt++;
1549     XtSetArg(args[argcnt], XmNmnemonic, 'D'); argcnt++;
1550     XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
1551     XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
1552     XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_POSITION); argcnt++;
1553     XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_POSITION); argcnt++;
1554     XtSetArg(args[argcnt], XmNleftPosition, 50); argcnt++;
1555     XtSetArg(args[argcnt], XmNrightPosition, 75); argcnt++;
1556     XtSetArg(args[argcnt], XmNtopOffset, defaultBtnOffset); argcnt++;
1557     deselectBtn = XmCreatePushButton(btnForm, "deselect", args, argcnt);
1558     XmStringFree(st1);
1559     XtAddCallback(deselectBtn, XmNactivateCallback,
1560        (XtCallbackProc)rMultiFileDeselectAllCB, window);
1561     XtManageChild(deselectBtn);
1562 
1563     /* Cancel */
1564     argcnt = 0;
1565     XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1566     XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1567     XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("Cancel")); argcnt++;
1568     XtSetArg(args[argcnt], XmNmnemonic, 'C'); argcnt++;
1569     XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
1570     XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
1571     XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_POSITION); argcnt++;
1572     XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_POSITION); argcnt++;
1573     XtSetArg(args[argcnt], XmNleftPosition, 75); argcnt++;
1574     XtSetArg(args[argcnt], XmNrightPosition, 100); argcnt++;
1575     XtSetArg(args[argcnt], XmNtopOffset, defaultBtnOffset); argcnt++;
1576     cancelBtn = XmCreatePushButton(btnForm, "cancel", args, argcnt);
1577     XmStringFree(st1);
1578     XtAddCallback(cancelBtn, XmNactivateCallback,
1579        (XtCallbackProc)rMultiFileCancelCB, window);
1580     XtManageChild(cancelBtn);
1581 
1582     /* The list of files */
1583     argcnt = 0;
1584     XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1585     XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_WIDGET); argcnt++;
1586     XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_WIDGET); argcnt++;
1587     XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
1588     XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_FORM); argcnt++;
1589     XtSetArg(args[argcnt], XmNbottomWidget, btnForm); argcnt++;
1590     XtSetArg(args[argcnt], XmNtopWidget, label1); argcnt++;
1591     XtSetArg(args[argcnt], XmNleftOffset, 10); argcnt++;
1592     XtSetArg(args[argcnt], XmNvisibleItemCount, 10); argcnt++;
1593     XtSetArg(args[argcnt], XmNtopOffset, 6); argcnt++;
1594     XtSetArg(args[argcnt], XmNbottomOffset, 6); argcnt++;
1595     XtSetArg(args[argcnt], XmNrightOffset, 10); argcnt++;
1596     /* An alternative is to use the EXTENDED_SELECT, but that one
1597        is less suited for keyboard manipulation (moving the selection cursor
1598        with the keyboard deselects everything). */
1599     XtSetArg(args[argcnt], XmNselectionPolicy, XmMULTIPLE_SELECT); argcnt++;
1600     list = XmCreateScrolledList(form, "list_of_files", args, argcnt);
1601     AddMouseWheelSupport(list);
1602     XtManageChild(list);
1603 
1604     /* Traverse: list -> buttons -> path name toggle button */
1605     XmAddTabGroup(list);
1606     XmAddTabGroup(btnForm);
1607     XmAddTabGroup(pathBtn);
1608 
1609     XtVaSetValues(label1, XmNuserData, list, NULL); /* mnemonic processing */
1610 
1611     /* Cancel/Mnemonic stuff. */
1612     XtVaSetValues(form, XmNcancelButton, cancelBtn, NULL);
1613     AddDialogMnemonicHandler(form, FALSE);
1614 
1615     window->replaceMultiFileDlog = form;
1616     window->replaceMultiFileList = list;
1617     window->replaceMultiFilePathBtn = pathBtn;
1618 
1619     /* Install a handler that frees the list of writable windows when
1620        the dialog is unmapped. */
1621     XtAddCallback(form, XmNunmapCallback,
1622 	    	    (XtCallbackProc)freeWritableWindowsCB, window);
1623 }
1624 
1625 /*
1626 ** Iterates through the list of writable windows of a window, and removes
1627 ** the doomed window if necessary.
1628 */
checkMultiReplaceListForDoomedW(WindowInfo * window,WindowInfo * doomedWindow)1629 static void checkMultiReplaceListForDoomedW(WindowInfo* window,
1630 						     WindowInfo* doomedWindow)
1631 {
1632     WindowInfo        *w;
1633     int               i;
1634 
1635     /* If the window owning the list and the doomed window are one and the
1636        same, we just close the multi-file replacement dialog. */
1637     if (window == doomedWindow) {
1638        XtUnmanageChild(window->replaceMultiFileDlog);
1639        return;
1640     }
1641 
1642     /* Check whether the doomed window is currently listed */
1643     for (i = 0; i < window->nWritableWindows; ++i) {
1644       w = window->writableWindows[i];
1645       if (w == doomedWindow) {
1646           removeDoomedWindowFromList(window, i);
1647           break;
1648       }
1649     }
1650 }
1651 
1652 /*
1653 ** Removes a window that is about to be closed from the list of files in
1654 ** which to replace. If the list becomes empty, the dialog is popped down.
1655 */
removeDoomedWindowFromList(WindowInfo * window,int index)1656 static void removeDoomedWindowFromList(WindowInfo* window, int index)
1657 {
1658     int       entriesToMove;
1659 
1660     /* If the list would become empty, we remove the dialog */
1661     if (window->nWritableWindows <= 1) {
1662       XtUnmanageChild(window->replaceMultiFileDlog);
1663       return;
1664     }
1665 
1666     entriesToMove = window->nWritableWindows - index - 1;
1667     memmove(&(window->writableWindows[index]),
1668             &(window->writableWindows[index+1]),
1669             (size_t)(entriesToMove*sizeof(WindowInfo*)));
1670     window->nWritableWindows -= 1;
1671 
1672     XmListDeletePos(window->replaceMultiFileList, index + 1);
1673 }
1674 
1675 /*
1676 ** These callbacks fix a Motif 1.1 problem that the default button gets the
1677 ** keyboard focus when a dialog is created.  We want the first text field
1678 ** to get the focus, so we don't set the default button until the text field
1679 ** has the focus for sure.  I have tried many other ways and this is by far
1680 ** the least nasty.
1681 */
fFocusCB(Widget w,WindowInfo * window,caddr_t * callData)1682 static void fFocusCB(Widget w, WindowInfo *window, caddr_t *callData)
1683 {
1684     window = WidgetToWindow(w);
1685     SET_ONE_RSRC(window->findDlog, XmNdefaultButton, window->findBtn);
1686 }
rFocusCB(Widget w,WindowInfo * window,caddr_t * callData)1687 static void rFocusCB(Widget w, WindowInfo *window, caddr_t *callData)
1688 {
1689     window = WidgetToWindow(w);
1690     SET_ONE_RSRC(window->replaceDlog, XmNdefaultButton, window->replaceBtn);
1691 }
1692 
1693 /* when keeping a window up, clue the user what window it's associated with */
rKeepCB(Widget w,WindowInfo * window,caddr_t * callData)1694 static void rKeepCB(Widget w, WindowInfo *window, caddr_t *callData)
1695 {
1696     char title[MAXPATHLEN + 19];
1697 
1698     window = WidgetToWindow(w);
1699 
1700     if (XmToggleButtonGetState(w)) {
1701     	sprintf(title, "Replace/Find (in %s)", window->filename);
1702     	XtVaSetValues(XtParent(window->replaceDlog), XmNtitle, title, NULL);
1703     } else
1704     	XtVaSetValues(XtParent(window->replaceDlog), XmNtitle, "Replace/Find", NULL);
1705 }
fKeepCB(Widget w,WindowInfo * window,caddr_t * callData)1706 static void fKeepCB(Widget w, WindowInfo *window, caddr_t *callData)
1707 {
1708     char title[MAXPATHLEN + 11];
1709 
1710     window = WidgetToWindow(w);
1711 
1712     if (XmToggleButtonGetState(w)) {
1713     	sprintf(title, "Find (in %s)", window->filename);
1714     	XtVaSetValues(XtParent(window->findDlog), XmNtitle, title, NULL);
1715     } else
1716     	XtVaSetValues(XtParent(window->findDlog), XmNtitle, "Find", NULL);
1717 }
1718 
replaceCB(Widget w,WindowInfo * window,XmAnyCallbackStruct * callData)1719 static void replaceCB(Widget w, WindowInfo *window,
1720 		      XmAnyCallbackStruct *callData)
1721 {
1722     char searchString[SEARCHMAX], replaceString[SEARCHMAX];
1723     int direction, searchType;
1724     char *params[5];
1725 
1726     window = WidgetToWindow(w);
1727 
1728     /* Validate and fetch the find and replace strings from the dialog */
1729     if (!getReplaceDlogInfo(window, &direction, searchString, replaceString,
1730     	    &searchType))
1731     	return;
1732 
1733     /* Set the initial focus of the dialog back to the search string */
1734     resetReplaceTabGroup(window);
1735 
1736     /* Find the text and replace it */
1737     params[0] = searchString;
1738     params[1] = replaceString;
1739     params[2] = directionArg(direction);
1740     params[3] = searchTypeArg(searchType);
1741     params[4] = searchWrapArg(GetPrefSearchWraps());
1742     windowNotToClose = window;
1743     XtCallActionProc(window->lastFocus, "replace", callData->event, params, 5);
1744     windowNotToClose = NULL;
1745 
1746     /* Pop down the dialog */
1747     if (!XmToggleButtonGetState(window->replaceKeepBtn))
1748     	unmanageReplaceDialogs(window);
1749 }
1750 
replaceAllCB(Widget w,WindowInfo * window,XmAnyCallbackStruct * callData)1751 static void replaceAllCB(Widget w, WindowInfo *window,
1752 			 XmAnyCallbackStruct *callData)
1753 {
1754     char searchString[SEARCHMAX], replaceString[SEARCHMAX];
1755     int direction, searchType;
1756     char *params[3];
1757 
1758     window = WidgetToWindow(w);
1759 
1760     /* Validate and fetch the find and replace strings from the dialog */
1761     if (!getReplaceDlogInfo(window, &direction, searchString, replaceString,
1762     	    &searchType))
1763     	return;
1764 
1765     /* Set the initial focus of the dialog back to the search string	*/
1766     resetReplaceTabGroup(window);
1767 
1768     /* do replacement */
1769     params[0] = searchString;
1770     params[1] = replaceString;
1771     params[2] = searchTypeArg(searchType);
1772     windowNotToClose = window;
1773     XtCallActionProc(window->lastFocus, "replace_all", callData->event,
1774     	    params, 3);
1775     windowNotToClose = NULL;
1776 
1777     /* pop down the dialog */
1778     if (!XmToggleButtonGetState(window->replaceKeepBtn))
1779     	unmanageReplaceDialogs(window);
1780 }
1781 
replaceMultiFileCB(Widget w,WindowInfo * window,XmAnyCallbackStruct * callData)1782 static void replaceMultiFileCB(Widget w, WindowInfo *window,
1783 				   XmAnyCallbackStruct *callData)
1784 {
1785     window = WidgetToWindow(w);
1786     DoReplaceMultiFileDlog(window);
1787 }
1788 
1789 /*
1790 ** Callback that frees the list of windows the multi-file replace
1791 ** dialog is unmapped.
1792 **/
freeWritableWindowsCB(Widget w,WindowInfo * window,XmAnyCallbackStruct * callData)1793 static void freeWritableWindowsCB(Widget w, WindowInfo* window,
1794                                   XmAnyCallbackStruct *callData)
1795 {
1796     window = WidgetToWindow(w);
1797     NEditFree(window->writableWindows);
1798     window->writableWindows = NULL;
1799     window->nWritableWindows = 0;
1800 }
1801 
1802 /*
1803 ** Comparison function for sorting windows by title for the window menu
1804 */
compareWindowNames(const void * windowA,const void * windowB)1805 static int compareWindowNames(const void *windowA, const void *windowB)
1806 {
1807       return strcmp((*((WindowInfo**)windowA))->filename,
1808       	    (*((WindowInfo**)windowB))->filename);
1809 }
1810 
1811 /*
1812 ** Count no. of windows
1813 */
countWindows(void)1814 static int countWindows(void)
1815 {
1816     int nWindows;
1817     const WindowInfo *w;
1818 
1819     for (w=WindowList, nWindows=0; w!=NULL; w=w->next, ++nWindows);
1820 
1821     return nWindows;
1822 }
1823 
1824 /*
1825 ** Count no. of writable windows, but first update the status of all files.
1826 */
countWritableWindows(void)1827 static int countWritableWindows(void)
1828 {
1829     int nWritable, nBefore, nAfter;
1830     WindowInfo *w;
1831 
1832     nBefore = countWindows();
1833     for (w=WindowList, nWritable=0; w!=NULL; w=w->next) {
1834 	/* We must be very careful! The status check may trigger a pop-up
1835 	   dialog when the file has changed on disk, and the user may destroy
1836 	   arbitrary windows in response. */
1837 	CheckForChangesToFile(w);
1838 	nAfter = countWindows();
1839 	if (nAfter != nBefore) {
1840 	    /* The user has destroyed a file; start counting all over again */
1841 	    nBefore = nAfter;
1842 	    w = WindowList;
1843 	    nWritable = 0;
1844 	    continue;
1845 	}
1846 	if (!IS_ANY_LOCKED(w->lockReasons)) ++nWritable;
1847     }
1848     return nWritable;
1849 }
1850 
1851 /*
1852 ** Collects a list of writable windows (sorted by file name).
1853 ** The previous list, if any is freed first.
1854 **/
collectWritableWindows(WindowInfo * window)1855 static void collectWritableWindows(WindowInfo* window)
1856 {
1857     int nWritable = countWritableWindows();
1858     int i;
1859     WindowInfo *w;
1860     WindowInfo **windows;
1861 
1862     NEditFree(window->writableWindows);
1863 
1864     /* Make a sorted list of writable windows */
1865     windows = (WindowInfo **)NEditMalloc(sizeof(WindowInfo *) * nWritable);
1866     for (w=WindowList, i=0; w!=NULL; w=w->next)
1867        if (!IS_ANY_LOCKED(w->lockReasons)) windows[i++] = w;
1868     qsort(windows, nWritable, sizeof(WindowInfo *), compareWindowNames);
1869 
1870     window->writableWindows = windows;
1871     window->nWritableWindows = nWritable;
1872 }
1873 
rMultiFileReplaceCB(Widget w,WindowInfo * window,XmAnyCallbackStruct * callData)1874 static void rMultiFileReplaceCB(Widget w, WindowInfo *window,
1875    XmAnyCallbackStruct *callData)
1876 {
1877     char 	searchString[SEARCHMAX], replaceString[SEARCHMAX];
1878     int 	direction, searchType;
1879     char 	*params[4];
1880     int 	nSelected, i;
1881     WindowInfo 	*writableWin;
1882     Bool 	replaceFailed, noWritableLeft;
1883 
1884     window = WidgetToWindow(w);
1885     nSelected = 0;
1886     for (i=0; i<window->nWritableWindows; ++i)
1887        if (XmListPosSelected(window->replaceMultiFileList, i+1))
1888           ++nSelected;
1889 
1890     if (!nSelected)
1891     {
1892         DialogF(DF_INF, XtParent(window->replaceMultiFileDlog), 1, "No Files",
1893                 "No files selected!", "OK");
1894        return; /* Give the user another chance */
1895     }
1896 
1897     /* Set the initial focus of the dialog back to the search string */
1898     resetReplaceTabGroup(window);
1899 
1900     /*
1901      * Protect the user against him/herself; Maybe this is a bit too much?
1902      */
1903     if (DialogF(DF_QUES, window->shell, 2, "Multi-File Replacement",
1904             "Multi-file replacements are difficult to undo.\n"
1905             "Proceed with the replacement ?", "Yes", "Cancel") != 1)
1906     {
1907         /* pop down the multi-file dialog only */
1908         XtUnmanageChild(window->replaceMultiFileDlog);
1909 
1910         return;
1911     }
1912 
1913     /* Fetch the find and replace strings from the dialog;
1914        they should have been validated already, but since Lesstif may not
1915        honor modal dialogs, it is possible that the user modified the
1916        strings again, so we should verify them again too. */
1917     if (!getReplaceDlogInfo(window, &direction, searchString, replaceString,
1918  	 		  &searchType))
1919 	return;
1920 
1921     /* Set the initial focus of the dialog back to the search string */
1922     resetReplaceTabGroup(window);
1923 
1924     params[0] = searchString;
1925     params[1] = replaceString;
1926     params[2] = searchTypeArg(searchType);
1927 
1928     replaceFailed = True;
1929     noWritableLeft = True;
1930     /* Perform the replacements and mark the selected files (history) */
1931     for (i=0; i<window->nWritableWindows; ++i) {
1932 	writableWin = window->writableWindows[i];
1933 	if (XmListPosSelected(window->replaceMultiFileList, i+1)) {
1934 	/* First check again whether the file is still writable. If the
1935 	   file status has changed or the file was locked in the mean time
1936 	   (possible due to Lesstif modal dialog bug), we just skip the
1937 	   window. */
1938 	    if (!IS_ANY_LOCKED(writableWin->lockReasons)) {
1939 		noWritableLeft = False;
1940 		writableWin->multiFileReplSelected = True;
1941 		writableWin->multiFileBusy = True; /* Avoid multi-beep/dialog */
1942 		writableWin->replaceFailed = False;
1943 		XtCallActionProc(writableWin->lastFocus, "replace_all",
1944 		    callData->event, params, 3);
1945 		writableWin->multiFileBusy = False;
1946 		if (!writableWin->replaceFailed)
1947 		    replaceFailed = False;
1948 	    }
1949 	} else {
1950 	    writableWin->multiFileReplSelected = False;
1951 	}
1952     }
1953 
1954     if (!XmToggleButtonGetState(window->replaceKeepBtn)) {
1955        /* Pop down both replace dialogs. */
1956        unmanageReplaceDialogs(window);
1957     } else {
1958        /* pow down only the file selection dialog */
1959        XtUnmanageChild(window->replaceMultiFileDlog);
1960     }
1961 
1962     /* We suppressed multiple beeps/dialogs. If there wasn't any file in
1963        which the replacement succeeded, we should still warn the user */
1964     if (replaceFailed) {
1965 	if (GetPrefSearchDlogs()) {
1966 	    if (noWritableLeft) {
1967 		DialogF(DF_INF, window->shell, 1, "Read-only Files",
1968                         "All selected files have become read-only.", "OK");
1969 	    } else {
1970 		DialogF(DF_INF, window->shell, 1, "String not found",
1971                         "String was not found", "OK");
1972             }
1973 	} else {
1974            XBell(TheDisplay, 0);
1975         }
1976     }
1977 }
1978 
rMultiFileCancelCB(Widget w,WindowInfo * window,caddr_t callData)1979 static void rMultiFileCancelCB(Widget w, WindowInfo *window, caddr_t callData)
1980 {
1981     window = WidgetToWindow(w);
1982 
1983     /* Set the initial focus of the dialog back to the search string	*/
1984     resetReplaceTabGroup(window);
1985 
1986     /* pop down the multi-window replace dialog */
1987     XtUnmanageChild(window->replaceMultiFileDlog);
1988 }
1989 
rMultiFileSelectAllCB(Widget w,WindowInfo * window,XmAnyCallbackStruct * callData)1990 static void rMultiFileSelectAllCB(Widget w, WindowInfo *window,
1991    XmAnyCallbackStruct *callData)
1992 {
1993     int i;
1994     char policy;
1995     Widget list;
1996 
1997     window = WidgetToWindow(w);
1998     list = window->replaceMultiFileList;
1999 
2000     /*
2001      * If the list is in extended selection mode, we can't select more
2002      * than one item (probably because XmListSelectPos is equivalent
2003      * to a button1 click; I don't think that there is an equivalent
2004      * for CTRL-button1). Therefore, we temporarily put the list into
2005      * multiple selection mode.
2006      * Note: this is not really necessary if the list is in multiple select
2007      *       mode all the time (as it currently is).
2008      */
2009     XtVaGetValues(list, XmNselectionPolicy, &policy, NULL);
2010     XtVaSetValues(list, XmNselectionPolicy, XmMULTIPLE_SELECT, NULL);
2011 
2012     /* Is there no other way (like "select all") ? */
2013     XmListDeselectAllItems(window->replaceMultiFileList); /* select toggles */
2014 
2015     for (i=0; i<window->nWritableWindows; ++i) {
2016        XmListSelectPos(list, i+1, FALSE);
2017     }
2018 
2019     /* Restore the original policy. */
2020     XtVaSetValues(list, XmNselectionPolicy, policy, NULL);
2021 }
2022 
rMultiFileDeselectAllCB(Widget w,WindowInfo * window,XmAnyCallbackStruct * callData)2023 static void rMultiFileDeselectAllCB(Widget w, WindowInfo *window,
2024    XmAnyCallbackStruct *callData)
2025 {
2026     window = WidgetToWindow(w);
2027     XmListDeselectAllItems(window->replaceMultiFileList);
2028 }
2029 
rMultiFilePathCB(Widget w,WindowInfo * window,XmAnyCallbackStruct * callData)2030 static void rMultiFilePathCB(Widget w, WindowInfo *window,
2031    XmAnyCallbackStruct *callData)
2032 {
2033     window = WidgetToWindow(w);
2034     uploadFileListItems(window, True);  /* Replace */
2035 }
2036 
2037 /*
2038  * Uploads the file items to the multi-file replament dialog list.
2039  * A boolean argument indicates whether the elements currently in the
2040  * list have to be replaced or not.
2041  * Depending on the state of the "Show path names" toggle button, either
2042  * the file names or the path names are listed.
2043  */
uploadFileListItems(WindowInfo * window,Bool replace)2044 static void uploadFileListItems(WindowInfo* window, Bool replace)
2045 {
2046     XmStringTable names;
2047     int           nWritable, i, *selected, selectedCount;
2048     char          buf[MAXPATHLEN+1], policy;
2049     Bool          usePathNames;
2050     WindowInfo    *w;
2051     Widget        list;
2052 
2053     nWritable = window->nWritableWindows;
2054     list = window->replaceMultiFileList;
2055 
2056     names = (XmStringTable) NEditMalloc(nWritable * sizeof(XmString*));
2057 
2058     usePathNames = XmToggleButtonGetState(window->replaceMultiFilePathBtn);
2059 
2060     /* Note: the windows are sorted alphabetically by _file_ name. This
2061              order is _not_ changed when we switch to path names. That
2062              would be confusing for the user */
2063 
2064     for (i = 0; i < nWritable; ++i) {
2065        w = window->writableWindows[i];
2066        if (usePathNames && window->filenameSet) {
2067           sprintf(buf, "%s%s", w->path, w->filename);
2068        } else {
2069           sprintf(buf, "%s", w->filename);
2070        }
2071        names[i] = XmStringCreateSimple(buf);
2072     }
2073 
2074     /*
2075      * If the list is in extended selection mode, we can't pre-select
2076      * more than one item in (probably because XmListSelectPos is
2077      * equivalent to a button1 click; I don't think that there is an
2078      * equivalent for CTRL-button1). Therefore, we temporarily put the
2079      * list into multiple selection mode.
2080      */
2081     XtVaGetValues(list, XmNselectionPolicy, &policy, NULL);
2082     XtVaSetValues(list, XmNselectionPolicy, XmMULTIPLE_SELECT, NULL);
2083     if (replace) {
2084        /* Note: this function is obsolete in Motif 2.x, but it is available
2085                 for compatibility reasons */
2086        XmListGetSelectedPos(list, &selected,  &selectedCount);
2087 
2088        XmListReplaceItemsPos(list, names, nWritable, 1);
2089 
2090        /* Maintain the selections */
2091        XmListDeselectAllItems(list);
2092        for (i = 0; i < selectedCount; ++i) {
2093           XmListSelectPos(list, selected[i], False);
2094        }
2095 
2096        NEditFree(selected);
2097     } else {
2098        Arg args[1];
2099        int nVisible;
2100        int firstSelected = 0;
2101 
2102        /* Remove the old list, if any */
2103        XmListDeleteAllItems(list);
2104 
2105        /* Initial settings */
2106        XmListAddItems(list, names, nWritable, 1);
2107 
2108        /* Pre-select the files from the last run. */
2109        selectedCount = 0;
2110        for (i = 0; i < nWritable; ++i) {
2111           if (window->writableWindows[i]->multiFileReplSelected) {
2112              XmListSelectPos(list, i+1, False);
2113              ++selectedCount;
2114              /* Remember the first selected item */
2115              if (firstSelected == 0) firstSelected = i+1;
2116           }
2117        }
2118        /* If no files are selected, we select them all. Normally this only
2119           happens the first time the dialog is used, but it looks "silly"
2120           if the dialog pops up with nothing selected. */
2121        if (selectedCount == 0) {
2122           for (i = 0; i < nWritable; ++i) {
2123              XmListSelectPos(list, i+1, False);
2124           }
2125           firstSelected = 1;
2126        }
2127 
2128        /* Make sure that the first selected item is visible; otherwise, the
2129           user could get the impression that nothing is selected. By
2130           visualizing at least the first selected item, the user will more
2131           easily be confident that the previous selection is still active. */
2132        XtSetArg(args[0], XmNvisibleItemCount, &nVisible);
2133        XtGetValues(list, args, 1);
2134        /* Make sure that we don't create blank lines at the bottom by
2135           positioning too far. */
2136        if (nWritable <= nVisible) {
2137           /* No need to shift the visible position */
2138           firstSelected = 1;
2139        }
2140        else {
2141           int maxFirst = nWritable - nVisible + 1;
2142           if (firstSelected > maxFirst)
2143              firstSelected = maxFirst;
2144        }
2145        XmListSetPos(list, firstSelected);
2146     }
2147 
2148     /* Put the list back into its original selection policy. */
2149     XtVaSetValues(list, XmNselectionPolicy, policy, NULL);
2150 
2151     for (i = 0; i < nWritable; ++i)
2152        XmStringFree(names[i]);
2153     NEditFree(names);
2154 }
2155 
2156 /*
2157 ** Unconditionally pops down the replace dialog and the
2158 ** replace-in-multiple-files dialog, if it exists.
2159 */
unmanageReplaceDialogs(const WindowInfo * window)2160 static void unmanageReplaceDialogs(const WindowInfo *window)
2161 {
2162     /* If the replace dialog goes down, the multi-file replace dialog must
2163        go down too */
2164     if (window->replaceMultiFileDlog &&
2165       XtIsManaged(window->replaceMultiFileDlog)) {
2166           XtUnmanageChild(window->replaceMultiFileDlog);
2167     }
2168 
2169     if (window->replaceDlog &&
2170       XtIsManaged(window->replaceDlog)) {
2171           XtUnmanageChild(window->replaceDlog);
2172     }
2173 }
2174 
rInSelCB(Widget w,WindowInfo * window,XmAnyCallbackStruct * callData)2175 static void rInSelCB(Widget w, WindowInfo *window,
2176 			 XmAnyCallbackStruct *callData)
2177 {
2178     char searchString[SEARCHMAX], replaceString[SEARCHMAX];
2179     int direction, searchType;
2180     char *params[3];
2181 
2182     window = WidgetToWindow(w);
2183 
2184     /* Validate and fetch the find and replace strings from the dialog */
2185     if (!getReplaceDlogInfo(window, &direction, searchString, replaceString,
2186     	    &searchType))
2187     	return;
2188 
2189     /* Set the initial focus of the dialog back to the search string */
2190     resetReplaceTabGroup(window);
2191 
2192     /* do replacement */
2193     params[0] = searchString;
2194     params[1] = replaceString;
2195     params[2] = searchTypeArg(searchType);
2196     windowNotToClose = window;
2197     XtCallActionProc(window->lastFocus, "replace_in_selection",
2198     	    callData->event, params, 3);
2199     windowNotToClose = NULL;
2200 
2201     /* pop down the dialog */
2202     if (!XmToggleButtonGetState(window->replaceKeepBtn))
2203     	unmanageReplaceDialogs(window);
2204 }
2205 
rCancelCB(Widget w,WindowInfo * window,caddr_t callData)2206 static void rCancelCB(Widget w, WindowInfo *window, caddr_t callData)
2207 {
2208     window = WidgetToWindow(w);
2209 
2210     /* Set the initial focus of the dialog back to the search string	*/
2211     resetReplaceTabGroup(window);
2212 
2213     /* pop down the dialog */
2214     unmanageReplaceDialogs(window);
2215 }
2216 
fCancelCB(Widget w,WindowInfo * window,caddr_t callData)2217 static void fCancelCB(Widget w, WindowInfo *window, caddr_t callData)
2218 {
2219     window = WidgetToWindow(w);
2220 
2221     /* Set the initial focus of the dialog back to the search string	*/
2222     resetFindTabGroup(window);
2223 
2224     /* pop down the dialog */
2225     XtUnmanageChild(window->findDlog);
2226 }
2227 
rFindCB(Widget w,WindowInfo * window,XmAnyCallbackStruct * callData)2228 static void rFindCB(Widget w, WindowInfo *window,XmAnyCallbackStruct *callData)
2229 {
2230     char searchString[SEARCHMAX], replaceString[SEARCHMAX];
2231     int direction, searchType;
2232     char *params[4];
2233 
2234     window = WidgetToWindow(w);
2235 
2236     /* Validate and fetch the find and replace strings from the dialog */
2237     if (!getReplaceDlogInfo(window, &direction, searchString, replaceString,
2238     	    &searchType))
2239     	return;
2240 
2241     /* Set the initial focus of the dialog back to the search string	*/
2242     resetReplaceTabGroup(window);
2243 
2244     /* Find the text and mark it */
2245     params[0] = searchString;
2246     params[1] = directionArg(direction);
2247     params[2] = searchTypeArg(searchType);
2248     params[3] = searchWrapArg(GetPrefSearchWraps());
2249     windowNotToClose = window;
2250     XtCallActionProc(window->lastFocus, "find", callData->event, params, 4);
2251     windowNotToClose = NULL;
2252 
2253     /* Doctor the search history generated by the action to include the
2254        replace string (if any), so the replace string can be used on
2255        subsequent replaces, even though no actual replacement was done. */
2256     if (historyIndex(1) != -1 &&
2257     		!strcmp(SearchHistory[historyIndex(1)], searchString)) {
2258 	NEditFree(ReplaceHistory[historyIndex(1)]);
2259 	ReplaceHistory[historyIndex(1)] = NEditStrdup(replaceString);
2260     }
2261 
2262     /* Pop down the dialog */
2263     if (!XmToggleButtonGetState(window->replaceKeepBtn))
2264     	unmanageReplaceDialogs(window);
2265 }
2266 
replaceFindCB(Widget w,WindowInfo * window,XmAnyCallbackStruct * callData)2267 static void replaceFindCB(Widget w, WindowInfo *window, XmAnyCallbackStruct *callData)
2268 {
2269     char searchString[SEARCHMAX+1], replaceString[SEARCHMAX+1];
2270     int direction, searchType;
2271     char *params[4];
2272 
2273     window = WidgetToWindow(w);
2274 
2275     /* Validate and fetch the find and replace strings from the dialog */
2276     if (!getReplaceDlogInfo(window, &direction, searchString, replaceString,
2277             &searchType))
2278         return;
2279 
2280     /* Set the initial focus of the dialog back to the search string */
2281     resetReplaceTabGroup(window);
2282 
2283     /* Find the text and replace it */
2284     params[0] = searchString;
2285     params[1] = replaceString;
2286     params[2] = directionArg(direction);
2287     params[3] = searchTypeArg(searchType);
2288     windowNotToClose = window;
2289     XtCallActionProc(window->lastFocus, "replace_find", callData->event, params, 4);
2290     windowNotToClose = NULL;
2291 
2292     /* Pop down the dialog */
2293     if (!XmToggleButtonGetState(window->replaceKeepBtn))
2294     	unmanageReplaceDialogs(window);
2295 }
2296 
rSetActionButtons(WindowInfo * window,int replaceBtn,int replaceFindBtn,int replaceAndFindBtn,int replaceInWinBtn,int replaceInSelBtn,int replaceAllBtn)2297 static void rSetActionButtons(WindowInfo* window,
2298                               int replaceBtn,
2299                               int replaceFindBtn,
2300                               int replaceAndFindBtn,
2301 #ifndef REPLACE_SCOPE
2302                               int replaceInWinBtn,
2303                               int replaceInSelBtn,
2304 #endif
2305                               int replaceAllBtn)
2306 {
2307     XtSetSensitive(window->replaceBtn,        replaceBtn);
2308     XtSetSensitive(window->replaceFindBtn,    replaceFindBtn);
2309     XtSetSensitive(window->replaceAndFindBtn, replaceAndFindBtn);
2310 #ifndef REPLACE_SCOPE
2311     XtSetSensitive(window->replaceInWinBtn, replaceInWinBtn);
2312     XtSetSensitive(window->replaceInSelBtn, replaceInSelBtn);
2313 #endif
2314     XtSetSensitive(window->replaceAllBtn,     replaceAllBtn);
2315 }
2316 
UpdateReplaceActionButtons(WindowInfo * window)2317 void UpdateReplaceActionButtons(WindowInfo* window)
2318 {
2319     /* Is there any text in the search for field */
2320     int searchText = textFieldNonEmpty(window->replaceText);
2321 #ifdef REPLACE_SCOPE
2322     switch (window->replaceScope)
2323     {
2324         case REPL_SCOPE_WIN:
2325 	    /* Enable all buttons, if there is any text in the search field. */
2326 	    rSetActionButtons(window, searchText, searchText, searchText, searchText);
2327             break;
2328 
2329         case REPL_SCOPE_SEL:
2330 	    /* Only enable Replace All, if a selection exists and text in search field. */
2331 	    rSetActionButtons(window, False, False, False, searchText && window->wasSelected);
2332             break;
2333 
2334         case REPL_SCOPE_MULTI:
2335 	    /* Only enable Replace All, if text in search field. */
2336 	    rSetActionButtons(window, False, False, False, searchText);
2337             break;
2338     }
2339 #else
2340     rSetActionButtons(window, searchText, searchText, searchText,
2341                       searchText, searchText && window->wasSelected,
2342                       searchText && (countWritableWindows() > 1));
2343 #endif
2344 }
2345 
2346 #ifdef REPLACE_SCOPE
2347 /*
2348 ** The next 3 callback adapt the sensitivity of the replace dialog push
2349 ** buttons to the state of the scope radio buttons.
2350 */
rScopeWinCB(Widget w,WindowInfo * window,XmAnyCallbackStruct * callData)2351 static void rScopeWinCB(Widget w, WindowInfo *window,
2352     XmAnyCallbackStruct *callData)
2353 {
2354     window = WidgetToWindow(w);
2355     if (XmToggleButtonGetState(window->replaceScopeWinToggle)) {
2356 	window->replaceScope = REPL_SCOPE_WIN;
2357         UpdateReplaceActionButtons(window);
2358     }
2359 }
2360 
rScopeSelCB(Widget w,WindowInfo * window,XmAnyCallbackStruct * callData)2361 static void rScopeSelCB(Widget w, WindowInfo *window,
2362     XmAnyCallbackStruct *callData)
2363 {
2364     window = WidgetToWindow(w);
2365     if (XmToggleButtonGetState(window->replaceScopeSelToggle)) {
2366 	window->replaceScope = REPL_SCOPE_SEL;
2367         UpdateReplaceActionButtons(window);
2368     }
2369 }
2370 
rScopeMultiCB(Widget w,WindowInfo * window,XmAnyCallbackStruct * callData)2371 static void rScopeMultiCB(Widget w, WindowInfo *window,
2372     XmAnyCallbackStruct *callData)
2373 {
2374     window = WidgetToWindow(w);
2375     if (XmToggleButtonGetState(window->replaceScopeMultiToggle)) {
2376 	window->replaceScope = REPL_SCOPE_MULTI;
2377         UpdateReplaceActionButtons(window);
2378     }
2379 }
2380 
2381 /*
2382 ** This routine dispatches a push on the replace-all button to the appropriate
2383 ** callback, depending on the state of the scope radio buttons.
2384 */
replaceAllScopeCB(Widget w,WindowInfo * window,XmAnyCallbackStruct * callData)2385 static void replaceAllScopeCB(Widget w, WindowInfo *window,
2386     XmAnyCallbackStruct *callData)
2387 {
2388     window = WidgetToWindow(w);
2389     switch(window->replaceScope) {
2390 	case REPL_SCOPE_WIN:
2391            replaceAllCB(w, window, callData);
2392            break;
2393         case REPL_SCOPE_SEL:
2394            rInSelCB(w, window, callData);
2395            break;
2396         case REPL_SCOPE_MULTI:
2397            replaceMultiFileCB(w, window, callData);
2398            break;
2399     }
2400 }
2401 #endif
2402 
textFieldNonEmpty(Widget w)2403 static int textFieldNonEmpty(Widget w)
2404 {
2405     char *str = XmTextGetString(w);
2406     int nonEmpty = (str[0] != '\0');
2407     NEditFree(str);
2408     return(nonEmpty);
2409 }
2410 
rFindTextValueChangedCB(Widget w,WindowInfo * window,XKeyEvent * event)2411 static void rFindTextValueChangedCB(Widget w, WindowInfo *window, XKeyEvent *event)
2412 {
2413     window = WidgetToWindow(w);
2414     UpdateReplaceActionButtons(window);
2415 }
2416 
rFindArrowKeyCB(Widget w,WindowInfo * window,XKeyEvent * event)2417 static void rFindArrowKeyCB(Widget w, WindowInfo *window, XKeyEvent *event)
2418 {
2419     KeySym keysym = XLookupKeysym(event, 0);
2420     int index;
2421     char *searchStr, *replaceStr;
2422     int searchType;
2423 
2424     window = WidgetToWindow(w);
2425     index = window->rHistIndex;
2426 
2427     /* only process up and down arrow keys */
2428     if (keysym != XK_Up && keysym != XK_Down)
2429     	return;
2430 
2431     /* increment or decrement the index depending on which arrow was pressed */
2432     index += (keysym == XK_Up) ? 1 : -1;
2433 
2434     /* if the index is out of range, beep and return */
2435     if (index != 0 && historyIndex(index) == -1) {
2436     	XBell(TheDisplay, 0);
2437     	return;
2438     }
2439 
2440     window = WidgetToWindow(w);
2441 
2442     /* determine the strings and button settings to use */
2443     if (index == 0) {
2444     	searchStr = "";
2445     	replaceStr = "";
2446     	searchType = GetPrefSearch();
2447     } else {
2448 	searchStr = SearchHistory[historyIndex(index)];
2449 	replaceStr = ReplaceHistory[historyIndex(index)];
2450 	searchType = SearchTypeHistory[historyIndex(index)];
2451     }
2452 
2453     /* Set the buttons and fields with the selected search type */
2454     initToggleButtons(searchType, window->replaceRegexToggle,
2455                       window->replaceCaseToggle, &window->replaceWordToggle,
2456                       &window->replaceLastLiteralCase,
2457                       &window->replaceLastRegexCase);
2458 
2459     XmTextSetString(window->replaceText, searchStr);
2460     XmTextSetString(window->replaceWithText, replaceStr);
2461 
2462     /* Set the state of the Replace, Find ... buttons */
2463     UpdateReplaceActionButtons(window);
2464 
2465     window->rHistIndex = index;
2466 }
2467 
replaceArrowKeyCB(Widget w,WindowInfo * window,XKeyEvent * event)2468 static void replaceArrowKeyCB(Widget w, WindowInfo *window, XKeyEvent *event)
2469 {
2470     KeySym keysym = XLookupKeysym(event, 0);
2471     int index;
2472 
2473     window = WidgetToWindow(w);
2474     index = window->rHistIndex;
2475 
2476     /* only process up and down arrow keys */
2477     if (keysym != XK_Up && keysym != XK_Down)
2478     	return;
2479 
2480     /* increment or decrement the index depending on which arrow was pressed */
2481     index += (keysym == XK_Up) ? 1 : -1;
2482 
2483     /* if the index is out of range, beep and return */
2484     if (index != 0 && historyIndex(index) == -1) {
2485     	XBell(TheDisplay, 0);
2486     	return;
2487     }
2488 
2489     window = WidgetToWindow(w);
2490 
2491     /* change only the replace field information */
2492     if (index == 0)
2493     	XmTextSetString(window->replaceWithText, "");
2494     else
2495     	XmTextSetString(window->replaceWithText,
2496     		ReplaceHistory[historyIndex(index)]);
2497     window->rHistIndex = index;
2498 }
2499 
fUpdateActionButtons(WindowInfo * window)2500 static void fUpdateActionButtons(WindowInfo *window)
2501 {
2502     int buttonState = textFieldNonEmpty(window->findText);
2503     XtSetSensitive(window->findBtn, buttonState);
2504 }
2505 
findTextValueChangedCB(Widget w,WindowInfo * window,XKeyEvent * event)2506 static void findTextValueChangedCB(Widget w, WindowInfo *window, XKeyEvent *event)
2507 {
2508     window = WidgetToWindow(w);
2509     fUpdateActionButtons(window);
2510 }
2511 
findArrowKeyCB(Widget w,WindowInfo * window,XKeyEvent * event)2512 static void findArrowKeyCB(Widget w, WindowInfo *window, XKeyEvent *event)
2513 {
2514     KeySym keysym = XLookupKeysym(event, 0);
2515     int index;
2516     char *searchStr;
2517     int searchType;
2518 
2519     window = WidgetToWindow(w);
2520     index = window->fHistIndex;
2521 
2522     /* only process up and down arrow keys */
2523     if (keysym != XK_Up && keysym != XK_Down)
2524     	return;
2525 
2526     /* increment or decrement the index depending on which arrow was pressed */
2527     index += (keysym == XK_Up) ? 1 : -1;
2528 
2529     /* if the index is out of range, beep and return */
2530     if (index != 0 && historyIndex(index) == -1) {
2531     	XBell(TheDisplay, 0);
2532     	return;
2533     }
2534 
2535 
2536     /* determine the strings and button settings to use */
2537     if (index == 0) {
2538     	searchStr = "";
2539     	searchType = GetPrefSearch();
2540     } else {
2541 	searchStr = SearchHistory[historyIndex(index)];
2542 	searchType = SearchTypeHistory[historyIndex(index)];
2543     }
2544 
2545     /* Set the buttons and fields with the selected search type */
2546     initToggleButtons(searchType, window->findRegexToggle,
2547                       window->findCaseToggle, &window->findWordToggle,
2548                       &window->findLastLiteralCase,
2549                       &window->findLastRegexCase);
2550     XmTextSetString(window->findText, searchStr);
2551 
2552     /* Set the state of the Find ... button */
2553     fUpdateActionButtons(window);
2554 
2555     window->fHistIndex = index;
2556 }
2557 
findCB(Widget w,WindowInfo * window,XmAnyCallbackStruct * callData)2558 static void findCB(Widget w, WindowInfo *window,XmAnyCallbackStruct *callData)
2559 {
2560     char searchString[SEARCHMAX];
2561     int direction, searchType;
2562     char *params[4];
2563 
2564     window = WidgetToWindow(w);
2565 
2566     /* fetch find string, direction and type from the dialog */
2567     if (!getFindDlogInfo(window, &direction, searchString, &searchType))
2568     	return;
2569 
2570     /* Set the initial focus of the dialog back to the search string	*/
2571     resetFindTabGroup(window);
2572 
2573     /* find the text and mark it */
2574     params[0] = searchString;
2575     params[1] = directionArg(direction);
2576     params[2] = searchTypeArg(searchType);
2577     params[3] = searchWrapArg(GetPrefSearchWraps());
2578     windowNotToClose = window;
2579     XtCallActionProc(window->lastFocus, "find", callData->event, params, 4);
2580     windowNotToClose = NULL;
2581 
2582     /* pop down the dialog */
2583     if (!XmToggleButtonGetState(window->findKeepBtn))
2584         XtUnmanageChild(window->findDlog);
2585 }
2586 
2587 /*
2588 ** Fetch and verify (particularly regular expression) search and replace
2589 ** strings and search type from the Replace dialog.  If the strings are ok,
2590 ** save a copy in the search history, copy them in to "searchString",
2591 ** "replaceString', which are assumed to be at least SEARCHMAX in length,
2592 ** return search type in "searchType", and return TRUE as the function
2593 ** value.  Otherwise, return FALSE.
2594 */
getReplaceDlogInfo(WindowInfo * window,int * direction,char * searchString,char * replaceString,int * searchType)2595 static int getReplaceDlogInfo(WindowInfo *window, int *direction,
2596 	char *searchString, char *replaceString, int *searchType)
2597 {
2598     char *replaceText, *replaceWithText;
2599     regexp *compiledRE = NULL;
2600     char *compileMsg;
2601 
2602     /* Get the search and replace strings, search type, and direction
2603        from the dialog */
2604     replaceText = XmTextGetString(window->replaceText);
2605     replaceWithText = XmTextGetString(window->replaceWithText);
2606 
2607     if(XmToggleButtonGetState(window->replaceRegexToggle)) {
2608       int regexDefault;
2609       if(XmToggleButtonGetState(window->replaceCaseToggle)) {
2610       	*searchType = SEARCH_REGEX;
2611 	regexDefault = REDFLT_STANDARD;
2612       } else {
2613       	*searchType = SEARCH_REGEX_NOCASE;
2614 	regexDefault = REDFLT_CASE_INSENSITIVE;
2615       }
2616       /* If the search type is a regular expression, test compile it
2617          immediately and present error messages */
2618       compiledRE = CompileRE(replaceText, &compileMsg, regexDefault);
2619       if (compiledRE == NULL) {
2620    	  DialogF(DF_WARN, XtParent(window->replaceDlog), 1, "Search String",
2621                   "Please respecify the search string:\n%s", "OK", compileMsg);
2622 	  NEditFree(replaceText);
2623 	  NEditFree(replaceWithText);
2624  	  return FALSE;
2625       }
2626       NEditFree(compiledRE);
2627     } else {
2628       if(XmToggleButtonGetState(window->replaceCaseToggle)) {
2629       	if(XmToggleButtonGetState(window->replaceWordToggle))
2630 	  *searchType = SEARCH_CASE_SENSE_WORD;
2631 	else
2632 	  *searchType = SEARCH_CASE_SENSE;
2633       } else {
2634       	if(XmToggleButtonGetState(window->replaceWordToggle))
2635 	  *searchType = SEARCH_LITERAL_WORD;
2636 	else
2637 	  *searchType = SEARCH_LITERAL;
2638       }
2639     }
2640 
2641     *direction = XmToggleButtonGetState(window->replaceRevToggle) ?
2642 	SEARCH_BACKWARD : SEARCH_FORWARD;
2643 
2644     /* Return strings */
2645     if (strlen(replaceText) >= SEARCHMAX) {
2646 	DialogF(DF_WARN, XtParent(window->replaceDlog), 1, "String too long",
2647                 "Search string too long.", "OK");
2648 	NEditFree(replaceText);
2649 	NEditFree(replaceWithText);
2650 	return FALSE;
2651     }
2652     if (strlen(replaceWithText) >= SEARCHMAX) {
2653 	DialogF(DF_WARN, XtParent(window->replaceDlog), 1, "String too long",
2654                 "Replace string too long.", "OK");
2655 	NEditFree(replaceText);
2656 	NEditFree(replaceWithText);
2657 	return FALSE;
2658     }
2659     strcpy(searchString, replaceText);
2660     strcpy(replaceString, replaceWithText);
2661     NEditFree(replaceText);
2662     NEditFree(replaceWithText);
2663     return TRUE;
2664 }
2665 
2666 /*
2667 ** Fetch and verify (particularly regular expression) search string,
2668 ** direction, and search type from the Find dialog.  If the search string
2669 ** is ok, save a copy in the search history, copy it to "searchString",
2670 ** which is assumed to be at least SEARCHMAX in length, return search type
2671 ** in "searchType", and return TRUE as the function value.  Otherwise,
2672 ** return FALSE.
2673 */
getFindDlogInfo(WindowInfo * window,int * direction,char * searchString,int * searchType)2674 static int getFindDlogInfo(WindowInfo *window, int *direction,
2675 	char *searchString, int *searchType)
2676 {
2677     char *findText;
2678     regexp *compiledRE = NULL;
2679     char *compileMsg;
2680 
2681     /* Get the search string, search type, and direction from the dialog */
2682     findText = XmTextGetString(window->findText);
2683 
2684     if(XmToggleButtonGetState(window->findRegexToggle)) {
2685       int regexDefault;
2686       if(XmToggleButtonGetState(window->findCaseToggle)) {
2687       	*searchType = SEARCH_REGEX;
2688 	regexDefault = REDFLT_STANDARD;
2689       } else {
2690       	*searchType = SEARCH_REGEX_NOCASE;
2691 	regexDefault = REDFLT_CASE_INSENSITIVE;
2692       }
2693       /* If the search type is a regular expression, test compile it
2694          immediately and present error messages */
2695       compiledRE = CompileRE(findText, &compileMsg, regexDefault);
2696       if (compiledRE == NULL) {
2697    	  DialogF(DF_WARN, XtParent(window->findDlog), 1, "Regex Error",
2698                   "Please respecify the search string:\n%s", "OK", compileMsg);
2699  	  return FALSE;
2700       }
2701       NEditFree(compiledRE);
2702     } else {
2703       if(XmToggleButtonGetState(window->findCaseToggle)) {
2704       	if(XmToggleButtonGetState(window->findWordToggle))
2705 	  *searchType = SEARCH_CASE_SENSE_WORD;
2706 	else
2707 	  *searchType = SEARCH_CASE_SENSE;
2708       } else {
2709       	if(XmToggleButtonGetState(window->findWordToggle))
2710 	  *searchType = SEARCH_LITERAL_WORD;
2711 	else
2712 	  *searchType = SEARCH_LITERAL;
2713       }
2714     }
2715 
2716     *direction = XmToggleButtonGetState(window->findRevToggle) ?
2717 	SEARCH_BACKWARD : SEARCH_FORWARD;
2718 
2719     if (isRegexType(*searchType)) {
2720     }
2721 
2722     /* Return the search string */
2723     if (strlen(findText) >= SEARCHMAX) {
2724 	DialogF(DF_WARN, XtParent(window->findDlog), 1, "String too long",
2725                 "Search string too long.", "OK");
2726 	NEditFree(findText);
2727 	return FALSE;
2728     }
2729     strcpy(searchString, findText);
2730     NEditFree(findText);
2731     return TRUE;
2732 }
2733 
SearchAndSelectSame(WindowInfo * window,int direction,int searchWrap)2734 int SearchAndSelectSame(WindowInfo *window, int direction, int searchWrap)
2735 {
2736     if (NHist < 1) {
2737     	XBell(TheDisplay, 0);
2738     	return FALSE;
2739     }
2740 
2741     return SearchAndSelect(window, direction, SearchHistory[historyIndex(1)],
2742     	    SearchTypeHistory[historyIndex(1)], searchWrap);
2743 }
2744 
2745 /*
2746 ** Search for "searchString" in "window", and select the matching text in
2747 ** the window when found (or beep or put up a dialog if not found).  Also
2748 ** adds the search string to the global search history.
2749 */
SearchAndSelect(WindowInfo * window,int direction,const char * searchString,int searchType,int searchWrap)2750 int SearchAndSelect(WindowInfo *window, int direction, const char *searchString,
2751 	int searchType, int searchWrap)
2752 {
2753     int startPos, endPos;
2754     int beginPos, cursorPos, selStart, selEnd;
2755     int movedFwd = 0;
2756 
2757     /* Save a copy of searchString in the search history */
2758     saveSearchHistory(searchString, NULL, searchType, FALSE);
2759 
2760     /* set the position to start the search so we don't find the same
2761        string that was found on the last search	*/
2762     if (searchMatchesSelection(window, searchString, searchType,
2763     	    &selStart, &selEnd, NULL, NULL)) {
2764     	/* selection matches search string, start before or after sel.	*/
2765 	if (direction == SEARCH_BACKWARD) {
2766 	    beginPos = selStart - 1;
2767 	} else {
2768             beginPos = selStart + 1;
2769             movedFwd = 1;
2770 	}
2771     } else {
2772     	selStart = -1; selEnd = -1;
2773     	/* no selection, or no match, search relative cursor */
2774     	cursorPos = TextGetCursorPos(window->lastFocus);
2775 	if (direction == SEARCH_BACKWARD) {
2776 	    /* use the insert position - 1 for backward searches */
2777 	    beginPos = cursorPos-1;
2778 	} else {
2779 	    /* use the insert position for forward searches */
2780 	    beginPos = cursorPos;
2781 	}
2782     }
2783 
2784     /* when the i-search bar is active and search is repeated there
2785        (Return), the action "find" is called (not: "find_incremental").
2786        "find" calls this function SearchAndSelect.
2787        To keep track of the iSearchLastBeginPos correctly in the
2788        repeated i-search case it is necessary to call the following
2789        function here, otherwise there are no beeps on the repeated
2790        incremental search wraps.  */
2791     iSearchRecordLastBeginPos(window, direction, beginPos);
2792 
2793     /* do the search.  SearchWindow does appropriate dialogs and beeps */
2794     if (!SearchWindow(window, direction, searchString, searchType, searchWrap,
2795     	    beginPos, &startPos, &endPos, NULL, NULL))
2796     	return FALSE;
2797 
2798     /* if the search matched an empty string (possible with regular exps)
2799        beginning at the start of the search, go to the next occurrence,
2800        otherwise repeated finds will get "stuck" at zero-length matches */
2801     if (direction==SEARCH_FORWARD && beginPos==startPos && beginPos==endPos) {
2802         if (!movedFwd &&
2803             !SearchWindow(window, direction, searchString, searchType,
2804                 searchWrap, beginPos+1, &startPos, &endPos, NULL, NULL))
2805     	    return FALSE;
2806     }
2807 
2808     /* if matched text is already selected, just beep */
2809     if (selStart==startPos && selEnd==endPos) {
2810     	XBell(TheDisplay, 0);
2811     	return FALSE;
2812     }
2813 
2814     /* select the text found string */
2815     BufSelect(window->buffer, startPos, endPos);
2816     MakeSelectionVisible(window, window->lastFocus);
2817     TextSetCursorPos(window->lastFocus, endPos);
2818 
2819     return TRUE;
2820 }
2821 
SearchForSelected(WindowInfo * window,int direction,int searchType,int searchWrap,Time time)2822 void SearchForSelected(WindowInfo *window, int direction, int searchType,
2823     int searchWrap, Time time)
2824 {
2825    SearchSelectedCallData *callData = XtNew(SearchSelectedCallData);
2826    callData->direction = direction;
2827    callData->searchType = searchType;
2828    callData->searchWrap = searchWrap;
2829    XtGetSelectionValue(window->textArea, XA_PRIMARY, XA_STRING,
2830     	    selectedSearchCB, callData, time);
2831 }
2832 
selectedSearchCB(Widget w,XtPointer callData,Atom * selection,Atom * type,XtPointer v,unsigned long * length,int * format)2833 static void selectedSearchCB(Widget w, XtPointer callData, Atom *selection,
2834 	Atom *type, XtPointer v, unsigned long *length, int *format)
2835 {
2836     WindowInfo *window = WidgetToWindow(w);
2837     SearchSelectedCallData *callDataItems = (SearchSelectedCallData *)callData;
2838     char *value = v;
2839     int searchType;
2840     char searchString[SEARCHMAX+1];
2841 
2842     window = WidgetToWindow(w);
2843 
2844     /* skip if we can't get the selection data or it's too long */
2845     if (*type == XT_CONVERT_FAIL || value == NULL) {
2846     	if (GetPrefSearchDlogs())
2847    	    DialogF(DF_WARN, window->shell, 1, "Wrong Selection",
2848                     "Selection not appropriate for searching", "OK");
2849     	else
2850     	    XBell(TheDisplay, 0);
2851         NEditFree(callData);
2852 	return;
2853     }
2854     if (*length > SEARCHMAX) {
2855     	if (GetPrefSearchDlogs())
2856    	    DialogF(DF_WARN, window->shell, 1, "Selection too long",
2857                     "Selection too long", "OK");
2858     	else
2859     	    XBell(TheDisplay, 0);
2860 	NEditFree(value);
2861         NEditFree(callData);
2862 	return;
2863     }
2864     if (*length == 0) {
2865     	XBell(TheDisplay, 0);
2866 	NEditFree(value);
2867         NEditFree(callData);
2868 	return;
2869     }
2870     /* should be of type text??? */
2871     if (*format != 8) {
2872     	fprintf(stderr, "NEdit: can't handle non 8-bit text\n");
2873     	XBell(TheDisplay, 0);
2874 	NEditFree(value);
2875         NEditFree(callData);
2876 	return;
2877     }
2878     /* make the selection the current search string */
2879     strncpy(searchString, value, *length);
2880     searchString[*length] = '\0';
2881     NEditFree(value);
2882 
2883     /* Use the passed method for searching, unless it is regex, since this
2884        kind of search is by definition a literal search */
2885     searchType = callDataItems->searchType;
2886     if (searchType == SEARCH_REGEX )
2887       searchType = SEARCH_CASE_SENSE;
2888     else if (searchType == SEARCH_REGEX_NOCASE)
2889 	      searchType = SEARCH_LITERAL;
2890 
2891     /* search for it in the window */
2892     SearchAndSelect(window, callDataItems->direction, searchString,
2893         searchType, callDataItems->searchWrap);
2894     NEditFree(callData);
2895 }
2896 
2897 /*
2898 ** Pop up and clear the incremental search line and prepare to search.
2899 */
BeginISearch(WindowInfo * window,int direction)2900 void BeginISearch(WindowInfo *window, int direction)
2901 {
2902     window->iSearchStartPos = -1;
2903     XmTextSetString(window->iSearchText, "");
2904     XmToggleButtonSetState(window->iSearchRevToggle,
2905 	    direction == SEARCH_BACKWARD, FALSE);
2906     /* Note: in contrast to the replace and find dialogs, the regex and
2907        case toggles are not reset to their default state when the incremental
2908        search bar is redisplayed. I'm not sure whether this is the best
2909        choice. If not, an initToggleButtons() call should be inserted
2910        here. But in that case, it might be appropriate to have different
2911        default search modes for i-search and replace/find. */
2912     TempShowISearch(window, TRUE);
2913     XmProcessTraversal(window->iSearchText, XmTRAVERSE_CURRENT);
2914 }
2915 
2916 /*
2917 ** Incremental searching is anchored at the position where the cursor
2918 ** was when the user began typing the search string.  Call this routine
2919 ** to forget about this original anchor, and if the search bar is not
2920 ** permanently up, pop it down.
2921 */
EndISearch(WindowInfo * window)2922 void EndISearch(WindowInfo *window)
2923 {
2924     /* Note: Please maintain this such that it can be freely peppered in
2925        mainline code, without callers having to worry about performance
2926        or visual glitches.  */
2927 
2928     /* Forget the starting position used for the current run of searches */
2929     window->iSearchStartPos = -1;
2930 
2931     /* Mark the end of incremental search history overwriting */
2932     saveSearchHistory("", NULL, 0, FALSE);
2933 
2934     /* Pop down the search line (if it's not pegged up in Preferences) */
2935     TempShowISearch(window, FALSE);
2936 }
2937 
2938 /*
2939 ** Reset window->iSearchLastBeginPos to the resulting initial
2940 ** search begin position for incremental searches.
2941 */
iSearchRecordLastBeginPos(WindowInfo * window,int direction,int initPos)2942 static void iSearchRecordLastBeginPos(WindowInfo *window, int direction,
2943 	int initPos)
2944 {
2945     window->iSearchLastBeginPos = initPos;
2946     if (direction == SEARCH_BACKWARD)
2947       	window->iSearchLastBeginPos--;
2948 }
2949 
2950 /*
2951 ** Search for "searchString" in "window", and select the matching text in
2952 ** the window when found (or beep or put up a dialog if not found).  If
2953 ** "continued" is TRUE and a prior incremental search starting position is
2954 ** recorded, search from that original position, otherwise, search from the
2955 ** current cursor position.
2956 */
SearchAndSelectIncremental(WindowInfo * window,int direction,const char * searchString,int searchType,int searchWrap,int continued)2957 int SearchAndSelectIncremental(WindowInfo *window, int direction,
2958 	const char *searchString, int searchType, int searchWrap, int continued)
2959 {
2960     int beginPos, startPos, endPos;
2961 
2962     /* If there's a search in progress, start the search from the original
2963        starting position, otherwise search from the cursor position. */
2964     if (!continued || window->iSearchStartPos == -1) {
2965 	window->iSearchStartPos = TextGetCursorPos(window->lastFocus);
2966 	iSearchRecordLastBeginPos(window, direction, window->iSearchStartPos);
2967     }
2968     beginPos = window->iSearchStartPos;
2969 
2970     /* If the search string is empty, beep eventually if text wrapped
2971        back to the initial position, re-init iSearchLastBeginPos,
2972        clear the selection, set the cursor back to what would be the
2973        beginning of the search, and return. */
2974     if(searchString[0] == 0) {
2975      	int beepBeginPos = (direction == SEARCH_BACKWARD) ? beginPos-1:beginPos;
2976       	iSearchTryBeepOnWrap(window, direction, beepBeginPos, beepBeginPos);
2977 	iSearchRecordLastBeginPos(window, direction, window->iSearchStartPos);
2978 	BufUnselect(window->buffer);
2979 	TextSetCursorPos(window->lastFocus, beginPos);
2980 	return TRUE;
2981     }
2982 
2983     /* Save the string in the search history, unless we're cycling thru
2984        the search history itself, which can be detected by matching the
2985        search string with the search string of the current history index. */
2986     if(!(window->iSearchHistIndex > 1 && !strcmp(searchString,
2987 	    SearchHistory[historyIndex(window->iSearchHistIndex)]))) {
2988    	saveSearchHistory(searchString, NULL, searchType, TRUE);
2989 	/* Reset the incremental search history pointer to the beginning */
2990 	window->iSearchHistIndex = 1;
2991     }
2992 
2993     /* begin at insert position - 1 for backward searches */
2994     if (direction == SEARCH_BACKWARD)
2995 	beginPos--;
2996 
2997     /* do the search.  SearchWindow does appropriate dialogs and beeps */
2998     if (!SearchWindow(window, direction, searchString, searchType, searchWrap,
2999 	    beginPos, &startPos, &endPos, NULL, NULL))
3000 	return FALSE;
3001 
3002     window->iSearchLastBeginPos = startPos;
3003 
3004     /* if the search matched an empty string (possible with regular exps)
3005        beginning at the start of the search, go to the next occurrence,
3006        otherwise repeated finds will get "stuck" at zero-length matches */
3007     if (direction==SEARCH_FORWARD && beginPos==startPos && beginPos==endPos)
3008 	if (!SearchWindow(window, direction, searchString, searchType, searchWrap,
3009 	    beginPos+1, &startPos, &endPos, NULL, NULL))
3010 	    return FALSE;
3011 
3012     window->iSearchLastBeginPos = startPos;
3013 
3014     /* select the text found string */
3015     BufSelect(window->buffer, startPos, endPos);
3016     MakeSelectionVisible(window, window->lastFocus);
3017     TextSetCursorPos(window->lastFocus, endPos);
3018 
3019     return TRUE;
3020 }
3021 
3022 /*
3023 ** Attach callbacks to the incremental search bar widgets.  This also fudges
3024 ** up the translations on the text widget so Shift+Return will call the
3025 ** activate callback (along with Return and Ctrl+Return).  It does this
3026 ** because incremental search uses the activate callback from the text
3027 ** widget to detect when the user has pressed Return to search for the next
3028 ** occurrence of the search string, and Shift+Return, which is the natural
3029 ** command for a reverse search does not naturally trigger this callback.
3030 */
SetISearchTextCallbacks(WindowInfo * window)3031 void SetISearchTextCallbacks(WindowInfo *window)
3032 {
3033     static XtTranslations tableText = NULL;
3034     static char *translationsText = "Shift<KeyPress>Return: activate()\n";
3035 
3036     static XtTranslations tableClear = NULL;
3037     static char *translationsClear =
3038         "<Btn2Down>:Arm()\n<Btn2Up>: isearch_clear_and_paste() Disarm()\n";
3039 
3040     static XtActionsRec actions[] = {
3041         { "isearch_clear_and_paste", iSearchTextClearAndPasteAP }
3042     };
3043 
3044     if (tableText == NULL)
3045     	tableText = XtParseTranslationTable(translationsText);
3046     XtOverrideTranslations(window->iSearchText, tableText);
3047 
3048     if (tableClear == NULL) {
3049         /* make sure actions are loaded */
3050         XtAppAddActions(XtWidgetToApplicationContext(window->iSearchText),
3051             actions, XtNumber(actions));
3052         tableClear = XtParseTranslationTable(translationsClear);
3053     }
3054     XtOverrideTranslations(window->iSearchClearButton, tableClear);
3055 
3056     XtAddCallback(window->iSearchText, XmNactivateCallback,
3057       (XtCallbackProc)iSearchTextActivateCB, window);
3058     XtAddCallback(window->iSearchText, XmNvalueChangedCallback,
3059       (XtCallbackProc)iSearchTextValueChangedCB, window);
3060     XtAddEventHandler(window->iSearchText, KeyPressMask, False,
3061       (XtEventHandler)iSearchTextKeyEH, window);
3062 
3063     /* Attach callbacks to deal with the optional sticky case sensitivity
3064        behaviour. Do this before installing the search callbacks to make
3065        sure that the proper search parameters are taken into account. */
3066     XtAddCallback(window->iSearchCaseToggle, XmNvalueChangedCallback,
3067 	    (XtCallbackProc)iSearchCaseToggleCB, window);
3068     XtAddCallback(window->iSearchRegexToggle, XmNvalueChangedCallback,
3069 	    (XtCallbackProc)iSearchRegExpToggleCB, window);
3070 
3071     /* When search parameters (direction or search type), redo the search */
3072     XtAddCallback(window->iSearchCaseToggle, XmNvalueChangedCallback,
3073 	    (XtCallbackProc)iSearchTextValueChangedCB, window);
3074     XtAddCallback(window->iSearchRegexToggle, XmNvalueChangedCallback,
3075 	    (XtCallbackProc)iSearchTextValueChangedCB, window);
3076     XtAddCallback(window->iSearchRevToggle, XmNvalueChangedCallback,
3077 	    (XtCallbackProc)iSearchTextValueChangedCB, window);
3078 
3079     /* find button: just like pressing return */
3080     XtAddCallback(window->iSearchFindButton, XmNactivateCallback,
3081 	    (XtCallbackProc)iSearchTextActivateCB, window);
3082     /* clear button: empty the search text widget */
3083     XtAddCallback(window->iSearchClearButton, XmNactivateCallback,
3084 	    (XtCallbackProc)iSearchTextClearCB, window);
3085 }
3086 
3087 /*
3088 ** Remove callbacks before resetting the incremental search text to avoid any
3089 ** cursor movement and/or clearing of selections.
3090 */
iSearchTextSetString(Widget w,WindowInfo * window,char * str)3091 static void iSearchTextSetString(Widget w, WindowInfo *window,
3092 	char *str)
3093 {
3094     /* remove callbacks which would be activated by emptying the text */
3095     XtRemoveAllCallbacks(window->iSearchText, XmNvalueChangedCallback);
3096     XtRemoveAllCallbacks(window->iSearchText, XmNactivateCallback);
3097     /* empty the text */
3098     XmTextSetString(window->iSearchText, str ? str : "");
3099     /* put back the callbacks */
3100     XtAddCallback(window->iSearchText, XmNactivateCallback,
3101       (XtCallbackProc)iSearchTextActivateCB, window);
3102     XtAddCallback(window->iSearchText, XmNvalueChangedCallback,
3103       (XtCallbackProc)iSearchTextValueChangedCB, window);
3104 }
3105 
3106 /*
3107 ** Action routine for Mouse Button 2 on the iSearchClearButton: resets the
3108 ** string then calls the activate callback for the text directly.
3109 */
iSearchTextClearAndPasteAP(Widget w,XEvent * event,String * args,Cardinal * nArg)3110 static void iSearchTextClearAndPasteAP(Widget w, XEvent *event, String *args,
3111         Cardinal *nArg)
3112 {
3113     WindowInfo *window;
3114     char *selText;
3115     XmAnyCallbackStruct cbdata;
3116 
3117     memset(&cbdata, 0, sizeof (cbdata));
3118     cbdata.event = event;
3119 
3120     window = WidgetToWindow(w);
3121 
3122     selText = GetAnySelection(window);
3123     iSearchTextSetString(w, window, selText);
3124     if (selText) {
3125         XmTextSetInsertionPosition(window->iSearchText, strlen(selText));
3126         NEditFree(selText);
3127     }
3128     iSearchTextActivateCB(w, window, &cbdata);
3129 }
3130 
3131 /*
3132 ** User pressed the clear incremental search bar button. Remove callbacks
3133 ** before resetting the text to avoid any cursor movement and/or clearing
3134 ** of selections.
3135 */
iSearchTextClearCB(Widget w,WindowInfo * window,XmAnyCallbackStruct * callData)3136 static void iSearchTextClearCB(Widget w, WindowInfo *window,
3137 	XmAnyCallbackStruct *callData)
3138 {
3139     window = WidgetToWindow(w);
3140 
3141     iSearchTextSetString(w, window, NULL);
3142 }
3143 
3144 /*
3145 ** User pressed return in the incremental search bar.  Do a new search with
3146 ** the search string displayed.  The direction of the search is toggled if
3147 ** the Ctrl key or the Shift key is pressed when the text field is activated.
3148 */
iSearchTextActivateCB(Widget w,WindowInfo * window,XmAnyCallbackStruct * callData)3149 static void iSearchTextActivateCB(Widget w, WindowInfo *window,
3150 	XmAnyCallbackStruct *callData)
3151 {
3152     char *params[4];
3153     char *searchString;
3154     int searchType, direction;
3155 
3156     window = WidgetToWindow(w);
3157 
3158     /* Fetch the string, search type and direction from the incremental
3159        search bar widgets at the top of the window */
3160     searchString = XmTextGetString(window->iSearchText);
3161     if(XmToggleButtonGetState(window->iSearchCaseToggle)) {
3162       if(XmToggleButtonGetState(window->iSearchRegexToggle))
3163 	searchType = SEARCH_REGEX;
3164       else
3165 	searchType = SEARCH_CASE_SENSE;
3166     } else {
3167       if(XmToggleButtonGetState(window->iSearchRegexToggle))
3168 	searchType = SEARCH_REGEX_NOCASE;
3169       else
3170 	searchType = SEARCH_LITERAL;
3171     }
3172     direction = XmToggleButtonGetState(window->iSearchRevToggle) ?
3173 	    SEARCH_BACKWARD : SEARCH_FORWARD;
3174 
3175     /* Reverse the search direction if the Ctrl or Shift key was pressed */
3176     if (callData->event->xbutton.state & (ShiftMask | ControlMask))
3177 	direction = direction == SEARCH_FORWARD ?
3178 		SEARCH_BACKWARD : SEARCH_FORWARD;
3179 
3180     /* find the text and mark it */
3181     params[0] = searchString;
3182     params[1] = directionArg(direction);
3183     params[2] = searchTypeArg(searchType);
3184     params[3] = searchWrapArg(GetPrefSearchWraps());
3185     XtCallActionProc(window->lastFocus, "find", callData->event, params, 4);
3186     NEditFree(searchString);
3187 }
3188 
3189 /*
3190 ** Called when user types in the incremental search line.  Redoes the
3191 ** search for the new search string.
3192 */
iSearchTextValueChangedCB(Widget w,WindowInfo * window,XmAnyCallbackStruct * callData)3193 static void iSearchTextValueChangedCB(Widget w, WindowInfo *window,
3194 	XmAnyCallbackStruct *callData)
3195 {
3196     char *params[5];
3197     char *searchString;
3198     int searchType, direction, nParams;
3199 
3200     window = WidgetToWindow(w);
3201 
3202     /* Fetch the string, search type and direction from the incremental
3203        search bar widgets at the top of the window */
3204     searchString = XmTextGetString(window->iSearchText);
3205     if(XmToggleButtonGetState(window->iSearchCaseToggle)) {
3206       if(XmToggleButtonGetState(window->iSearchRegexToggle))
3207 	searchType = SEARCH_REGEX;
3208       else
3209 	searchType = SEARCH_CASE_SENSE;
3210     } else {
3211       if(XmToggleButtonGetState(window->iSearchRegexToggle))
3212 	searchType = SEARCH_REGEX_NOCASE;
3213       else
3214 	searchType = SEARCH_LITERAL;
3215     }
3216     direction = XmToggleButtonGetState(window->iSearchRevToggle) ?
3217 	    SEARCH_BACKWARD : SEARCH_FORWARD;
3218 
3219     /* If the search type is a regular expression, test compile it.  If it
3220        fails, silently skip it.  (This allows users to compose the expression
3221        in peace when they have unfinished syntax, but still get beeps when
3222        correct syntax doesn't match) */
3223     if (isRegexType(searchType)) {
3224 	regexp *compiledRE = NULL;
3225 	char *compileMsg;
3226 	compiledRE = CompileRE(searchString, &compileMsg,
3227 	                       defaultRegexFlags(searchType));
3228 	if (compiledRE == NULL) {
3229 	    NEditFree(searchString);
3230 	    return;
3231 	}
3232 	NEditFree(compiledRE);
3233     }
3234 
3235     /* Call the incremental search action proc to do the searching and
3236        selecting (this allows it to be recorded for learn/replay).  If
3237        there's an incremental search already in progress, mark the operation
3238        as "continued" so the search routine knows to re-start the search
3239        from the original starting position */
3240     nParams = 0;
3241     params[nParams++] = searchString;
3242     params[nParams++] = directionArg(direction);
3243     params[nParams++] = searchTypeArg(searchType);
3244     params[nParams++] = searchWrapArg(GetPrefSearchWraps());
3245     if (window->iSearchStartPos != -1)
3246 	params[nParams++] = "continued";
3247     XtCallActionProc(window->lastFocus, "find_incremental",
3248 	    callData->event, params, nParams);
3249     NEditFree(searchString);
3250 }
3251 
3252 /*
3253 ** Process arrow keys for history recall, and escape key for leaving
3254 ** incremental search bar.
3255 */
iSearchTextKeyEH(Widget w,WindowInfo * window,XKeyEvent * event,Boolean * continueDispatch)3256 static void iSearchTextKeyEH(Widget w, WindowInfo *window,
3257 	XKeyEvent *event, Boolean *continueDispatch)
3258 {
3259     KeySym keysym = XLookupKeysym(event, 0);
3260     int index;
3261     char *searchStr;
3262     int searchType;
3263 
3264     /* only process up and down arrow keys */
3265     if (keysym != XK_Up && keysym != XK_Down && keysym != XK_Escape) {
3266 	*continueDispatch = TRUE;
3267 	return;
3268     }
3269 
3270     window = WidgetToWindow(w);
3271     index = window->iSearchHistIndex;
3272     *continueDispatch = FALSE;
3273 
3274     /* allow escape key to cancel search */
3275     if (keysym == XK_Escape) {
3276 	XmProcessTraversal(window->lastFocus, XmTRAVERSE_CURRENT);
3277 	EndISearch(window);
3278 	return;
3279     }
3280 
3281     /* increment or decrement the index depending on which arrow was pressed */
3282     index += (keysym == XK_Up) ? 1 : -1;
3283 
3284     /* if the index is out of range, beep and return */
3285     if (index != 0 && historyIndex(index) == -1) {
3286 	XBell(TheDisplay, 0);
3287 	return;
3288     }
3289 
3290     /* determine the strings and button settings to use */
3291     if (index == 0) {
3292 	searchStr = "";
3293 	searchType = GetPrefSearch();
3294     } else {
3295 	searchStr = SearchHistory[historyIndex(index)];
3296 	searchType = SearchTypeHistory[historyIndex(index)];
3297     }
3298 
3299     /* Set the info used in the value changed callback before calling
3300       XmTextSetString(). */
3301     window->iSearchHistIndex = index;
3302     initToggleButtons(searchType, window->iSearchRegexToggle,
3303                       window->iSearchCaseToggle, NULL,
3304                       &window->iSearchLastLiteralCase,
3305                       &window->iSearchLastRegexCase);
3306 
3307     /* Beware the value changed callback is processed as part of this call */
3308     XmTextSetString(window->iSearchText, searchStr);
3309     XmTextSetInsertionPosition(window->iSearchText,
3310 	    XmTextGetLastPosition(window->iSearchText));
3311 }
3312 
3313 /*
3314 ** Check the character before the insertion cursor of textW and flash
3315 ** matching parenthesis, brackets, or braces, by temporarily highlighting
3316 ** the matching character (a timer procedure is scheduled for removing the
3317 ** highlights)
3318 */
FlashMatching(WindowInfo * window,Widget textW)3319 void FlashMatching(WindowInfo *window, Widget textW)
3320 {
3321     char c;
3322     void *style;
3323     int pos, matchIndex;
3324     int startPos, endPos, searchPos, matchPos;
3325     int constrain;
3326 
3327     /* if a marker is already drawn, erase it and cancel the timeout */
3328     if (window->flashTimeoutID != 0) {
3329     	eraseFlash(window);
3330     	XtRemoveTimeOut(window->flashTimeoutID);
3331     	window->flashTimeoutID = 0;
3332     }
3333 
3334     /* no flashing required */
3335     if (window->showMatchingStyle == NO_FLASH) {
3336 	return;
3337     }
3338 
3339     /* don't flash matching characters if there's a selection */
3340     if (window->buffer->primary.selected)
3341    	return;
3342 
3343     /* get the character to match and the position to start from */
3344     pos = TextGetCursorPos(textW) - 1;
3345     if (pos < 0)
3346     	return;
3347     c = BufGetCharacter(window->buffer, pos);
3348     style = GetHighlightInfo(window, pos);
3349 
3350     /* is the character one we want to flash? */
3351     for (matchIndex = 0; matchIndex<N_FLASH_CHARS; matchIndex++) {
3352         if (MatchingChars[matchIndex].c == c)
3353 	    break;
3354     }
3355     if (matchIndex == N_FLASH_CHARS)
3356 	return;
3357 
3358     /* constrain the search to visible text only when in single-pane mode
3359        AND using delimiter flashing (otherwise search the whole buffer) */
3360     constrain = ((window->nPanes == 0) &&
3361         (window->showMatchingStyle == FLASH_DELIMIT));
3362 
3363     if (MatchingChars[matchIndex].direction == SEARCH_BACKWARD) {
3364     	startPos = constrain ? TextFirstVisiblePos(textW) : 0;
3365     	endPos = pos;
3366     	searchPos = endPos;
3367     } else {
3368     	startPos = pos;
3369     	endPos = constrain ? TextLastVisiblePos(textW) :
3370     	    	window->buffer->length;
3371     	searchPos = startPos;
3372     }
3373 
3374     /* do the search */
3375     if (!findMatchingChar(window, c, style, searchPos, startPos, endPos,
3376         &matchPos))
3377     	return;
3378 
3379     if (window->showMatchingStyle == FLASH_DELIMIT) {
3380 	/* Highlight either the matching character ... */
3381 	BufHighlight(window->buffer, matchPos, matchPos+1);
3382     } else {
3383 	/* ... or the whole range. */
3384   	if (MatchingChars[matchIndex].direction == SEARCH_BACKWARD) {
3385 	    BufHighlight(window->buffer, matchPos, pos+1);
3386 	} else {
3387 	    BufHighlight(window->buffer, matchPos+1, pos);
3388 	}
3389     }
3390 
3391     /* Set up a timer to erase the box after 1.5 seconds */
3392     window->flashTimeoutID = XtAppAddTimeOut(
3393     	    XtWidgetToApplicationContext(window->shell), 1500,
3394     	    flashTimeoutProc, window);
3395     window->flashPos = matchPos;
3396 }
3397 
SelectToMatchingCharacter(WindowInfo * window)3398 void SelectToMatchingCharacter(WindowInfo *window)
3399 {
3400     int selStart, selEnd;
3401     int startPos, endPos, matchPos;
3402     textBuffer *buf = window->buffer;
3403 
3404     /* get the character to match and its position from the selection, or
3405        the character before the insert point if nothing is selected.
3406        Give up if too many characters are selected */
3407     if (!GetSimpleSelection(buf, &selStart, &selEnd)) {
3408 	selEnd = TextGetCursorPos(window->lastFocus);
3409         if (window->overstrike)
3410 	    selEnd += 1;
3411 	selStart = selEnd - 1;
3412 	if (selStart < 0) {
3413 	    XBell(TheDisplay, 0);
3414 	    return;
3415 	}
3416     }
3417     if ((selEnd - selStart) != 1) {
3418     	XBell(TheDisplay, 0);
3419 	return;
3420     }
3421 
3422     /* Search for it in the buffer */
3423     if (!findMatchingChar(window, BufGetCharacter(buf, selStart),
3424         GetHighlightInfo(window, selStart), selStart, 0, buf->length, &matchPos)) {
3425     	XBell(TheDisplay, 0);
3426 	return;
3427     }
3428     startPos = (matchPos > selStart) ? selStart : matchPos;
3429     endPos = (matchPos > selStart) ? matchPos : selStart;
3430 
3431     /* temporarily shut off autoShowInsertPos before setting the cursor
3432        position so MakeSelectionVisible gets a chance to place the cursor
3433        string at a pleasing position on the screen (otherwise, the cursor would
3434        be automatically scrolled on screen and MakeSelectionVisible would do
3435        nothing) */
3436     XtVaSetValues(window->lastFocus, textNautoShowInsertPos, False, NULL);
3437     /* select the text between the matching characters */
3438     BufSelect(buf, startPos, endPos+1);
3439     MakeSelectionVisible(window, window->lastFocus);
3440     XtVaSetValues(window->lastFocus, textNautoShowInsertPos, True, NULL);
3441 }
3442 
GotoMatchingCharacter(WindowInfo * window)3443 void GotoMatchingCharacter(WindowInfo *window)
3444 {
3445     int selStart, selEnd;
3446     int matchPos;
3447     textBuffer *buf = window->buffer;
3448 
3449     /* get the character to match and its position from the selection, or
3450        the character before the insert point if nothing is selected.
3451        Give up if too many characters are selected */
3452     if (!GetSimpleSelection(buf, &selStart, &selEnd)) {
3453 	selEnd = TextGetCursorPos(window->lastFocus);
3454         if (window->overstrike)
3455 	    selEnd += 1;
3456 	selStart = selEnd - 1;
3457 	if (selStart < 0) {
3458 	    XBell(TheDisplay, 0);
3459 	    return;
3460 	}
3461     }
3462     if ((selEnd - selStart) != 1) {
3463     	XBell(TheDisplay, 0);
3464 	return;
3465     }
3466 
3467     /* Search for it in the buffer */
3468     if (!findMatchingChar(window, BufGetCharacter(buf, selStart),
3469 	    GetHighlightInfo(window, selStart), selStart, 0,
3470 	    buf->length, &matchPos)) {
3471     	XBell(TheDisplay, 0);
3472 	return;
3473     }
3474 
3475     /* temporarily shut off autoShowInsertPos before setting the cursor
3476        position so MakeSelectionVisible gets a chance to place the cursor
3477        string at a pleasing position on the screen (otherwise, the cursor would
3478        be automatically scrolled on screen and MakeSelectionVisible would do
3479        nothing) */
3480     XtVaSetValues(window->lastFocus, textNautoShowInsertPos, False, NULL);
3481     TextSetCursorPos(window->lastFocus, matchPos+1);
3482     MakeSelectionVisible(window, window->lastFocus);
3483     XtVaSetValues(window->lastFocus, textNautoShowInsertPos, True, NULL);
3484 }
3485 
findMatchingChar(WindowInfo * window,char toMatch,void * styleToMatch,int charPos,int startLimit,int endLimit,int * matchPos)3486 static int findMatchingChar(WindowInfo *window, char toMatch,
3487     void* styleToMatch, int charPos, int startLimit, int endLimit,
3488     int *matchPos)
3489 {
3490     int nestDepth, matchIndex, direction, beginPos, pos;
3491     char matchChar, c;
3492     void *style = NULL;
3493     textBuffer *buf = window->buffer;
3494     int matchSyntaxBased = window->matchSyntaxBased;
3495 
3496     /* If we don't match syntax based, fake a matching style. */
3497     if (!matchSyntaxBased) style = styleToMatch;
3498 
3499     /* Look up the matching character and match direction */
3500     for (matchIndex = 0; matchIndex<N_MATCH_CHARS; matchIndex++) {
3501         if (MatchingChars[matchIndex].c == toMatch)
3502 	    break;
3503     }
3504     if (matchIndex == N_MATCH_CHARS)
3505 	return FALSE;
3506     matchChar = MatchingChars[matchIndex].match;
3507     direction = MatchingChars[matchIndex].direction;
3508 
3509     /* find it in the buffer */
3510     beginPos = (direction==SEARCH_FORWARD) ? charPos+1 : charPos-1;
3511     nestDepth = 1;
3512     if (direction == SEARCH_FORWARD) {
3513     	for (pos=beginPos; pos<endLimit; pos++) {
3514 	    c=BufGetCharacter(buf, pos);
3515 	    if (c == matchChar) {
3516 		if (matchSyntaxBased) style = GetHighlightInfo(window, pos);
3517 		if (style == styleToMatch) {
3518 		    nestDepth--;
3519 		    if (nestDepth == 0) {
3520 			*matchPos = pos;
3521 			return TRUE;
3522 		    }
3523 		}
3524 	    } else if (c == toMatch) {
3525 		if (matchSyntaxBased) style = GetHighlightInfo(window, pos);
3526 		if (style == styleToMatch)
3527 		    nestDepth++;
3528 	    }
3529 	}
3530     } else { /* SEARCH_BACKWARD */
3531 	for (pos=beginPos; pos>=startLimit; pos--) {
3532 	    c=BufGetCharacter(buf, pos);
3533 	    if (c == matchChar) {
3534 		if (matchSyntaxBased) style = GetHighlightInfo(window, pos);
3535 		if (style == styleToMatch) {
3536 		    nestDepth--;
3537 		    if (nestDepth == 0) {
3538 			*matchPos = pos;
3539 			return TRUE;
3540 		    }
3541 		}
3542 	    } else if (c == toMatch) {
3543 		if (matchSyntaxBased) style = GetHighlightInfo(window, pos);
3544 		if (style == styleToMatch)
3545 		    nestDepth++;
3546 	    }
3547 	}
3548     }
3549     return FALSE;
3550 }
3551 
3552 /*
3553 ** Xt timer procedure for erasing the matching parenthesis marker.
3554 */
flashTimeoutProc(XtPointer clientData,XtIntervalId * id)3555 static void flashTimeoutProc(XtPointer clientData, XtIntervalId *id)
3556 {
3557     eraseFlash((WindowInfo *)clientData);
3558     ((WindowInfo *)clientData)->flashTimeoutID = 0;
3559 }
3560 
3561 /*
3562 ** Erase the marker drawn on a matching parenthesis bracket or brace
3563 ** character.
3564 */
eraseFlash(WindowInfo * window)3565 static void eraseFlash(WindowInfo *window)
3566 {
3567     BufUnhighlight(window->buffer);
3568 }
3569 
3570 /*
3571 ** Search and replace using previously entered search strings (from dialog
3572 ** or selection).
3573 */
ReplaceSame(WindowInfo * window,int direction,int searchWrap)3574 int ReplaceSame(WindowInfo *window, int direction, int searchWrap)
3575 {
3576     if (NHist < 1) {
3577     	XBell(TheDisplay, 0);
3578     	return FALSE;
3579     }
3580 
3581     return SearchAndReplace(window, direction, SearchHistory[historyIndex(1)],
3582     	    ReplaceHistory[historyIndex(1)],
3583     	    SearchTypeHistory[historyIndex(1)], searchWrap);
3584 }
3585 
3586 /*
3587 ** Search and replace using previously entered search strings (from dialog
3588 ** or selection).
3589 */
ReplaceFindSame(WindowInfo * window,int direction,int searchWrap)3590 int ReplaceFindSame(WindowInfo *window, int direction, int searchWrap)
3591 {
3592     if (NHist < 1) {
3593         XBell(TheDisplay, 0);
3594         return FALSE;
3595     }
3596 
3597     return ReplaceAndSearch(window, direction, SearchHistory[historyIndex(1)],
3598                             ReplaceHistory[historyIndex(1)],
3599                             SearchTypeHistory[historyIndex(1)], searchWrap);
3600 }
3601 
3602 /*
3603 ** Replace selection with "replaceString" and search for string "searchString" in window "window",
3604 ** using algorithm "searchType" and direction "direction"
3605 */
ReplaceAndSearch(WindowInfo * window,int direction,const char * searchString,const char * replaceString,int searchType,int searchWrap)3606 int ReplaceAndSearch(WindowInfo *window, int direction, const char *searchString,
3607                      const char *replaceString, int searchType, int searchWrap)
3608 {
3609     int startPos = 0, endPos = 0, replaceLen = 0;
3610     int searchExtentBW, searchExtentFW;
3611     int replaced;
3612 
3613     /* Save a copy of search and replace strings in the search history */
3614     saveSearchHistory(searchString, replaceString, searchType, FALSE);
3615 
3616     replaced = 0;
3617 
3618     /* Replace the selected text only if it matches the search string */
3619     if (searchMatchesSelection(window, searchString, searchType,
3620 	                       &startPos, &endPos, &searchExtentBW,
3621 			       &searchExtentFW)) {
3622 	/* replace the text */
3623 	if (isRegexType(searchType)) {
3624     	    char replaceResult[SEARCHMAX+1], *foundString;
3625 	    foundString = BufGetRange(window->buffer, searchExtentBW,
3626 				      searchExtentFW+1);
3627     	    replaceUsingRE(searchString, replaceString, foundString,
3628 		    startPos-searchExtentBW,
3629 		    replaceResult, SEARCHMAX, startPos == 0 ? '\0' :
3630 		    BufGetCharacter(window->buffer, startPos-1),
3631 		    GetWindowDelimiters(window), defaultRegexFlags(searchType));
3632 	    NEditFree(foundString);
3633     	    BufReplace(window->buffer, startPos, endPos, replaceResult);
3634     	    replaceLen = strlen(replaceResult);
3635 	} else {
3636             BufReplace(window->buffer, startPos, endPos, replaceString);
3637             replaceLen = strlen(replaceString);
3638 	}
3639 
3640         /* Position the cursor so the next search will work correctly based */
3641         /* on the direction of the search */
3642         TextSetCursorPos(window->lastFocus, startPos +
3643                          ((direction == SEARCH_FORWARD) ? replaceLen : 0));
3644         replaced = 1;
3645     }
3646 
3647     /* do the search; beeps/dialogs are taken care of */
3648     SearchAndSelect(window, direction, searchString, searchType, searchWrap);
3649 
3650     return replaced;
3651 }
3652 
3653 /*
3654 ** Search for string "searchString" in window "window", using algorithm
3655 ** "searchType" and direction "direction", and replace it with "replaceString"
3656 ** Also adds the search and replace strings to the global search history.
3657 */
SearchAndReplace(WindowInfo * window,int direction,const char * searchString,const char * replaceString,int searchType,int searchWrap)3658 int SearchAndReplace(WindowInfo *window, int direction, const char *searchString,
3659 	const char *replaceString, int searchType, int searchWrap)
3660 {
3661     int startPos, endPos, replaceLen, searchExtentBW, searchExtentFW;
3662     int found;
3663     int beginPos, cursorPos;
3664 
3665     /* Save a copy of search and replace strings in the search history */
3666     saveSearchHistory(searchString, replaceString, searchType, FALSE);
3667 
3668     /* If the text selected in the window matches the search string, 	*/
3669     /* the user is probably using search then replace method, so	*/
3670     /* replace the selected text regardless of where the cursor is.	*/
3671     /* Otherwise, search for the string.				*/
3672     if (!searchMatchesSelection(window, searchString, searchType,
3673     	    &startPos, &endPos, &searchExtentBW, &searchExtentFW)) {
3674 	/* get the position to start the search */
3675 	cursorPos = TextGetCursorPos(window->lastFocus);
3676 	if (direction == SEARCH_BACKWARD) {
3677 	    /* use the insert position - 1 for backward searches */
3678 	    beginPos = cursorPos-1;
3679 	} else {
3680 	    /* use the insert position for forward searches */
3681 	    beginPos = cursorPos;
3682 	}
3683 	/* do the search */
3684 	found = SearchWindow(window, direction, searchString, searchType, searchWrap,
3685 		beginPos, &startPos, &endPos, &searchExtentBW, &searchExtentFW);
3686 	if (!found)
3687 	    return FALSE;
3688     }
3689 
3690     /* replace the text */
3691     if (isRegexType(searchType)) {
3692     	char replaceResult[SEARCHMAX], *foundString;
3693 	foundString = BufGetRange(window->buffer, searchExtentBW, searchExtentFW+1);
3694     	replaceUsingRE(searchString, replaceString, foundString,
3695 		startPos - searchExtentBW,
3696 		replaceResult, SEARCHMAX, startPos == 0 ? '\0' :
3697 		BufGetCharacter(window->buffer, startPos-1),
3698 		GetWindowDelimiters(window), defaultRegexFlags(searchType));
3699 	NEditFree(foundString);
3700     	BufReplace(window->buffer, startPos, endPos, replaceResult);
3701     	replaceLen = strlen(replaceResult);
3702     } else {
3703     	BufReplace(window->buffer, startPos, endPos, replaceString);
3704     	replaceLen = strlen(replaceString);
3705     }
3706 
3707     /* after successfully completing a replace, selected text attracts
3708        attention away from the area of the replacement, particularly
3709        when the selection represents a previous search. so deselect */
3710     BufUnselect(window->buffer);
3711 
3712     /* temporarily shut off autoShowInsertPos before setting the cursor
3713        position so MakeSelectionVisible gets a chance to place the replaced
3714        string at a pleasing position on the screen (otherwise, the cursor would
3715        be automatically scrolled on screen and MakeSelectionVisible would do
3716        nothing) */
3717     XtVaSetValues(window->lastFocus, textNautoShowInsertPos, False, NULL);
3718     TextSetCursorPos(window->lastFocus, startPos +
3719     	((direction == SEARCH_FORWARD) ? replaceLen : 0));
3720     MakeSelectionVisible(window, window->lastFocus);
3721     XtVaSetValues(window->lastFocus, textNautoShowInsertPos, True, NULL);
3722 
3723     return TRUE;
3724 }
3725 
3726 /*
3727 **  Uses the resource nedit.truncSubstitution to determine how to deal with
3728 **  regex failures. This function only knows about the resource (via the usual
3729 **  setting getter) and asks or warns the user depending on that.
3730 **
3731 **  One could argue that the dialoging should be determined by the setting
3732 **  'searchDlogs'. However, the incomplete substitution is not just a question
3733 **  of verbosity, but of data loss. The search is successful, only the
3734 **  replacement fails due to an internal limitation of NEdit.
3735 **
3736 **  The parameters 'parent' and 'display' are only used to put dialogs and
3737 **  beeps at the right place.
3738 **
3739 **  The result is either predetermined by the resource or the user's choice.
3740 */
prefOrUserCancelsSubst(const Widget parent,const Display * display)3741 static Boolean prefOrUserCancelsSubst(const Widget parent,
3742         const Display* display)
3743 {
3744     Boolean cancel = True;
3745     unsigned confirmResult = 0;
3746 
3747     switch (GetPrefTruncSubstitution()) {
3748         case TRUNCSUBST_SILENT:
3749             /*  silently fail the operation  */
3750             cancel = True;
3751             break;
3752 
3753         case TRUNCSUBST_FAIL:
3754             /*  fail the operation and pop up a dialog informing the user  */
3755             XBell((Display*) display, 0);
3756             DialogF(DF_INF, parent, 1, "Substitution Failed",
3757                     "The result length of the substitution exceeded an internal limit.\n"
3758                     "The substitution is canceled.",
3759                     "OK");
3760             cancel = True;
3761             break;
3762 
3763         case TRUNCSUBST_WARN:
3764             /*  pop up dialog and ask for confirmation  */
3765             XBell((Display*) display, 0);
3766             confirmResult = DialogF(DF_WARN, parent, 2,
3767                     "Substitution Failed",
3768                     "The result length of the substitution exceeded an internal limit.\n"
3769                     "Executing the substitution will result in loss of data.",
3770                     "Lose Data", "Cancel");
3771             cancel = (1 != confirmResult);
3772             break;
3773 
3774         case TRUNCSUBST_IGNORE:
3775             /*  silently conclude the operation; THIS WILL DESTROY DATA.  */
3776             cancel = False;
3777             break;
3778     }
3779 
3780     return cancel;
3781 }
3782 
3783 /*
3784 ** Replace all occurences of "searchString" in "window" with "replaceString"
3785 ** within the current primary selection in "window". Also adds the search and
3786 ** replace strings to the global search history.
3787 */
ReplaceInSelection(const WindowInfo * window,const char * searchString,const char * replaceString,int searchType)3788 void ReplaceInSelection(const WindowInfo* window, const char* searchString,
3789         const char* replaceString, int searchType)
3790 {
3791     int selStart, selEnd, beginPos, startPos, endPos, realOffset, replaceLen;
3792     int found, isRect, rectStart, rectEnd, lineStart, cursorPos;
3793     int extentBW, extentFW;
3794     char *fileString;
3795     textBuffer *tempBuf;
3796     Boolean substSuccess = False;
3797     Boolean anyFound = False;
3798     Boolean cancelSubst = True;
3799 
3800     /* save a copy of search and replace strings in the search history */
3801     saveSearchHistory(searchString, replaceString, searchType, FALSE);
3802 
3803     /* find out where the selection is */
3804     if (!BufGetSelectionPos(window->buffer, &selStart, &selEnd, &isRect,
3805     	    &rectStart, &rectEnd))
3806     	return;
3807 
3808     /* get the selected text */
3809     if (isRect) {
3810     	selStart = BufStartOfLine(window->buffer, selStart);
3811     	selEnd = BufEndOfLine(window->buffer, selEnd);
3812     	fileString = BufGetRange(window->buffer, selStart, selEnd);
3813     } else
3814     	fileString = BufGetSelectionText(window->buffer);
3815 
3816     /* create a temporary buffer in which to do the replacements to hide the
3817        intermediate steps from the display routines, and so everything can
3818        be undone in a single operation */
3819     tempBuf = BufCreate();
3820     BufSetAll(tempBuf, fileString);
3821 
3822     /* search the string and do the replacements in the temporary buffer */
3823     replaceLen = strlen(replaceString);
3824     found = TRUE;
3825     beginPos = 0;
3826     cursorPos = 0;
3827     realOffset = 0;
3828     while (found) {
3829 	found = SearchString(fileString, searchString, SEARCH_FORWARD,
3830 		searchType, FALSE, beginPos, &startPos, &endPos, &extentBW,
3831                 &extentFW, GetWindowDelimiters(window));
3832 	if (!found)
3833 	    break;
3834 
3835         anyFound = True;
3836 	/* if the selection is rectangular, verify that the found
3837 	   string is in the rectangle */
3838 	if (isRect) {
3839 	    lineStart = BufStartOfLine(window->buffer, selStart+startPos);
3840 	    if (BufCountDispChars(window->buffer, lineStart, selStart+startPos) <
3841 		    rectStart || BufCountDispChars(window->buffer, lineStart,
3842 		    selStart+endPos) > rectEnd) {
3843 		if (fileString[endPos] == '\0')
3844 		    break;
3845 		/* If the match starts before the left boundary of the
3846 		   selection, and extends past it, we should not continue
3847 		   search after the end of the (false) match, because we
3848 		   could miss a valid match starting between the left boundary
3849 		   and the end of the false match. */
3850 		if (BufCountDispChars(window->buffer, lineStart,
3851 				      selStart+startPos) < rectStart &&
3852 		    BufCountDispChars(window->buffer, lineStart,
3853 				      selStart+endPos) > rectStart)
3854 		    beginPos += 1;
3855 		else
3856 		    beginPos = (startPos == endPos) ? endPos+1 : endPos;
3857 		continue;
3858 	    }
3859 	}
3860 
3861 	/* Make sure the match did not start past the end (regular expressions
3862 	   can consider the artificial end of the range as the end of a line,
3863 	   and match a fictional whole line beginning there) */
3864         if (startPos == (selEnd - selStart)) {
3865 	    found = False;
3866 	    break;
3867 	}
3868 
3869 	/* replace the string and compensate for length change */
3870 	if (isRegexType(searchType)) {
3871     	    char replaceResult[SEARCHMAX], *foundString;
3872 	    foundString = BufGetRange(tempBuf, extentBW+realOffset,
3873 		    extentFW+realOffset+1);
3874             substSuccess = replaceUsingRE(searchString, replaceString,
3875                     foundString, startPos - extentBW, replaceResult, SEARCHMAX,
3876                     0 == (startPos + realOffset)
3877                         ? '\0'
3878                         : BufGetCharacter(tempBuf, startPos + realOffset - 1),
3879                     GetWindowDelimiters(window), defaultRegexFlags(searchType));
3880 	    NEditFree(foundString);
3881 
3882             if (!substSuccess) {
3883                 /*  The substitution failed. Primary reason for this would be
3884                     a result string exceeding SEARCHMAX. */
3885 
3886                 cancelSubst = prefOrUserCancelsSubst(window->shell, TheDisplay);
3887 
3888                 if (cancelSubst) {
3889                     /*  No point in trying other substitutions.  */
3890                     break;
3891                 }
3892             }
3893 
3894     	    BufReplace(tempBuf, startPos+realOffset, endPos+realOffset,
3895     		    replaceResult);
3896     	    replaceLen = strlen(replaceResult);
3897         } else {
3898             /* at this point plain substitutions (should) always work */
3899     	    BufReplace(tempBuf, startPos+realOffset, endPos+realOffset,
3900     		    replaceString);
3901             substSuccess = True;
3902         }
3903 
3904     	realOffset += replaceLen - (endPos - startPos);
3905     	/* start again after match unless match was empty, then endPos+1 */
3906     	beginPos = (startPos == endPos) ? endPos+1 : endPos;
3907     	cursorPos = endPos;
3908 	if (fileString[endPos] == '\0')
3909 	    break;
3910     }
3911     NEditFree(fileString);
3912 
3913     if (anyFound) {
3914         if (substSuccess || !cancelSubst) {
3915             /*  Either the substitution was successful (the common case) or the
3916                 user does not care and wants to have a faulty replacement.  */
3917 
3918             /* replace the selected range in the real buffer */
3919             BufReplace(window->buffer, selStart, selEnd, BufAsString(tempBuf));
3920 
3921             /* set the insert point at the end of the last replacement */
3922             TextSetCursorPos(window->lastFocus, selStart + cursorPos + realOffset);
3923 
3924             /* leave non-rectangular selections selected (rect. ones after replacement
3925                are less useful since left/right positions are randomly adjusted) */
3926             if (!isRect) {
3927                 BufSelect(window->buffer, selStart, selEnd + realOffset);
3928             }
3929         }
3930     } else {
3931         /*  Nothing found, tell the user about it  */
3932     	if (GetPrefSearchDlogs()) {
3933     	    /* Avoid bug in Motif 1.1 by putting away search dialog
3934     	       before DialogF */
3935     	    if (window->findDlog && XtIsManaged(window->findDlog) &&
3936     	    	    !XmToggleButtonGetState(window->findKeepBtn))
3937     		XtUnmanageChild(window->findDlog);
3938     	    if (window->replaceDlog && XtIsManaged(window->replaceDlog) &&
3939     	    	    !XmToggleButtonGetState(window->replaceKeepBtn))
3940     		unmanageReplaceDialogs(window);
3941    	    DialogF(DF_INF, window->shell, 1, "String not found",
3942                 "String was not found", "OK");
3943     	} else
3944     	    XBell(TheDisplay, 0);
3945     }
3946 
3947     BufFree(tempBuf);
3948     return;
3949 }
3950 
3951 /*
3952 ** Replace all occurences of "searchString" in "window" with "replaceString".
3953 ** Also adds the search and replace strings to the global search history.
3954 */
ReplaceAll(WindowInfo * window,const char * searchString,const char * replaceString,int searchType)3955 int ReplaceAll(WindowInfo *window, const char *searchString,
3956         const char *replaceString, int searchType)
3957 {
3958     const char *fileString;
3959     char *newFileString;
3960     int copyStart, copyEnd, replacementLen;
3961 
3962     /* reject empty string */
3963     if (*searchString == '\0')
3964     	return FALSE;
3965 
3966     /* save a copy of search and replace strings in the search history */
3967     saveSearchHistory(searchString, replaceString, searchType, FALSE);
3968 
3969     /* view the entire text buffer from the text area widget as a string */
3970     fileString = BufAsString(window->buffer);
3971 
3972     newFileString = ReplaceAllInString(fileString, searchString, replaceString,
3973 	    searchType, &copyStart, &copyEnd, &replacementLen,
3974 	    GetWindowDelimiters(window));
3975 
3976     if (newFileString == NULL) {
3977         if (window->multiFileBusy) {
3978             window->replaceFailed = TRUE; /* only needed during multi-file
3979                                              replacements */
3980         } else if (GetPrefSearchDlogs()) {
3981     	    if (window->findDlog && XtIsManaged(window->findDlog) &&
3982     	    	    !XmToggleButtonGetState(window->findKeepBtn))
3983     		XtUnmanageChild(window->findDlog);
3984     	    if (window->replaceDlog && XtIsManaged(window->replaceDlog) &&
3985     	    	    !XmToggleButtonGetState(window->replaceKeepBtn))
3986     		unmanageReplaceDialogs(window);
3987    	    DialogF(DF_INF, window->shell, 1, "String not found",
3988                 "String was not found", "OK");
3989     	} else
3990     	    XBell(TheDisplay, 0);
3991 	return FALSE;
3992     }
3993 
3994     /* replace the contents of the text widget with the substituted text */
3995     BufReplace(window->buffer, copyStart, copyEnd, newFileString);
3996 
3997     /* Move the cursor to the end of the last replacement */
3998     TextSetCursorPos(window->lastFocus, copyStart + replacementLen);
3999 
4000     NEditFree(newFileString);
4001     return TRUE;
4002 }
4003 
4004 /*
4005 ** Replace all occurences of "searchString" in "inString" with "replaceString"
4006 ** and return an allocated string covering the range between the start of the
4007 ** first replacement (returned in "copyStart", and the end of the last
4008 ** replacement (returned in "copyEnd")
4009 */
ReplaceAllInString(const char * inString,const char * searchString,const char * replaceString,int searchType,int * copyStart,int * copyEnd,int * replacementLength,const char * delimiters)4010 char *ReplaceAllInString(const char *inString, const char *searchString,
4011 	const char *replaceString, int searchType, int *copyStart,
4012 	int *copyEnd, int *replacementLength, const char *delimiters)
4013 {
4014     int beginPos, startPos, endPos, lastEndPos;
4015     int found, nFound, removeLen, replaceLen, copyLen, addLen;
4016     char *outString, *fillPtr;
4017     int searchExtentBW, searchExtentFW;
4018 
4019     /* reject empty string */
4020     if (*searchString == '\0')
4021     	return NULL;
4022 
4023     /* rehearse the search first to determine the size of the buffer needed
4024        to hold the substituted text.  No substitution done here yet */
4025     replaceLen = strlen(replaceString);
4026     found = TRUE;
4027     nFound = 0;
4028     removeLen = 0;
4029     addLen = 0;
4030     beginPos = 0;
4031     *copyStart = -1;
4032     while (found) {
4033     	found = SearchString(inString, searchString, SEARCH_FORWARD, searchType,
4034 		FALSE, beginPos, &startPos, &endPos, &searchExtentBW,
4035                 &searchExtentFW, delimiters);
4036 	if (found) {
4037 	    if (*copyStart < 0)
4038 	    	*copyStart = startPos;
4039     	    *copyEnd = endPos;
4040     	    /* start next after match unless match was empty, then endPos+1 */
4041     	    beginPos = (startPos == endPos) ? endPos+1 : endPos;
4042 	    nFound++;
4043 	    removeLen += endPos - startPos;
4044 	    if (isRegexType(searchType)) {
4045     		char replaceResult[SEARCHMAX];
4046     		replaceUsingRE(searchString, replaceString, &inString[searchExtentBW],
4047  			startPos-searchExtentBW,
4048      			replaceResult, SEARCHMAX, startPos == 0 ? '\0' :
4049 			inString[startPos-1], delimiters,
4050                         defaultRegexFlags(searchType));
4051     		addLen += strlen(replaceResult);
4052     	    } else
4053     	    	addLen += replaceLen;
4054 	    if (inString[endPos] == '\0')
4055 		break;
4056 	}
4057     }
4058     if (nFound == 0)
4059 	return NULL;
4060 
4061     /* Allocate a new buffer to hold all of the new text between the first
4062        and last substitutions */
4063     copyLen = *copyEnd - *copyStart;
4064     outString = (char*)NEditMalloc(copyLen - removeLen + addLen + 1);
4065 
4066     /* Scan through the text buffer again, substituting the replace string
4067        and copying the part between replaced text to the new buffer  */
4068     found = TRUE;
4069     beginPos = 0;
4070     lastEndPos = 0;
4071     fillPtr = outString;
4072     while (found) {
4073     	found = SearchString(inString, searchString, SEARCH_FORWARD, searchType,
4074 		FALSE, beginPos, &startPos, &endPos, &searchExtentBW,
4075                 &searchExtentFW, delimiters);
4076 	if (found) {
4077 	    if (beginPos != 0) {
4078 		memcpy(fillPtr, &inString[lastEndPos], startPos - lastEndPos);
4079 		fillPtr += startPos - lastEndPos;
4080 	    }
4081 	    if (isRegexType(searchType)) {
4082     		char replaceResult[SEARCHMAX];
4083     		replaceUsingRE(searchString, replaceString, &inString[searchExtentBW],
4084 			startPos-searchExtentBW,
4085     			replaceResult, SEARCHMAX, startPos == 0 ? '\0' :
4086 			inString[startPos-1], delimiters,
4087 	      	      	defaultRegexFlags(searchType));
4088     		replaceLen = strlen(replaceResult);
4089     		memcpy(fillPtr, replaceResult, replaceLen);
4090 	    } else {
4091 		memcpy(fillPtr, replaceString, replaceLen);
4092 	    }
4093 	    fillPtr += replaceLen;
4094 	    lastEndPos = endPos;
4095 	    /* start next after match unless match was empty, then endPos+1 */
4096 	    beginPos = (startPos == endPos) ? endPos+1 : endPos;
4097 	    if (inString[endPos] == '\0')
4098 		break;
4099 	}
4100     }
4101     *fillPtr = '\0';
4102     *replacementLength = fillPtr - outString;
4103     return outString;
4104 }
4105 
4106 /*
4107 ** If this is an incremental search and BeepOnSearchWrap is on:
4108 ** Emit a beep if the search wrapped over BOF/EOF compared to
4109 ** the last startPos of the current incremental search.
4110 */
iSearchTryBeepOnWrap(WindowInfo * window,int direction,int beginPos,int startPos)4111 static void iSearchTryBeepOnWrap(WindowInfo *window, int direction,
4112 	int beginPos, int startPos)
4113 {
4114     if (GetPrefBeepOnSearchWrap()) {
4115         if (direction == SEARCH_FORWARD) {
4116             if ((startPos >= beginPos
4117                         && window->iSearchLastBeginPos < beginPos)
4118                     ||(startPos < beginPos
4119                         && window->iSearchLastBeginPos >= beginPos)) {
4120                 XBell(TheDisplay, 0);
4121             }
4122         } else {
4123             if ((startPos <= beginPos
4124                         && window->iSearchLastBeginPos > beginPos)
4125                     ||(startPos > beginPos
4126                         && window->iSearchLastBeginPos <= beginPos)) {
4127                 XBell(TheDisplay, 0);
4128             }
4129         }
4130     }
4131 }
4132 
4133 /*
4134 ** Search the text in "window", attempting to match "searchString"
4135 */
SearchWindow(WindowInfo * window,int direction,const char * searchString,int searchType,int searchWrap,int beginPos,int * startPos,int * endPos,int * extentBW,int * extentFW)4136 int SearchWindow(WindowInfo *window, int direction, const char *searchString,
4137 	int searchType, int searchWrap, int beginPos, int *startPos,
4138         int *endPos, int *extentBW, int *extentFW)
4139 {
4140     const char *fileString;
4141     int found, resp, fileEnd = window->buffer->length - 1, outsideBounds;
4142 
4143     /* reject empty string */
4144     if (*searchString == '\0')
4145     	return FALSE;
4146 
4147     /* get the entire text buffer from the text area widget */
4148     fileString = BufAsString(window->buffer);
4149 
4150     /* If we're already outside the boundaries, we must consider wrapping
4151        immediately (Note: fileEnd+1 is a valid starting position. Consider
4152        searching for $ at the end of a file ending with \n.) */
4153     if ((direction == SEARCH_FORWARD && beginPos > fileEnd + 1)
4154             || (direction == SEARCH_BACKWARD && beginPos < 0))
4155     {
4156         outsideBounds = TRUE;
4157     } else
4158     {
4159         outsideBounds = FALSE;
4160     }
4161 
4162     /* search the string copied from the text area widget, and present
4163        dialogs, or just beep.  iSearchStartPos is not a perfect indicator that
4164        an incremental search is in progress.  A parameter would be better. */
4165     if (window->iSearchStartPos == -1) { /* normal search */
4166     	found = !outsideBounds &&
4167 		SearchString(fileString, searchString, direction, searchType,
4168     	    	FALSE, beginPos, startPos, endPos, extentBW, extentFW,
4169 		GetWindowDelimiters(window));
4170     	/* Avoid Motif 1.1 bug by putting away search dialog before DialogF */
4171     	if (window->findDlog && XtIsManaged(window->findDlog) &&
4172     	    	!XmToggleButtonGetState(window->findKeepBtn))
4173     	    XtUnmanageChild(window->findDlog);
4174     	if (window->replaceDlog && XtIsManaged(window->replaceDlog) &&
4175     	    	!XmToggleButtonGetState(window->replaceKeepBtn))
4176     	    unmanageReplaceDialogs(window);
4177         if (!found) {
4178             if (searchWrap) {
4179 		if (direction == SEARCH_FORWARD && beginPos != 0) {
4180 		    if(GetPrefBeepOnSearchWrap()) {
4181 			XBell(TheDisplay, 0);
4182 		    } else if (GetPrefSearchDlogs()) {
4183 			resp = DialogF(DF_QUES, window->shell, 2, "Wrap Search",
4184 				"Continue search from\nbeginning of file?",
4185                                 "Continue", "Cancel");
4186 			if (resp == 2) {
4187 			    return False;
4188 			}
4189 		    }
4190 		    found = SearchString(fileString, searchString, direction,
4191 			searchType, FALSE, 0, startPos, endPos, extentBW,
4192 			extentFW, GetWindowDelimiters(window));
4193 		} else if (direction == SEARCH_BACKWARD && beginPos != fileEnd) {
4194 		    if(GetPrefBeepOnSearchWrap()) {
4195 			XBell(TheDisplay, 0);
4196 		    } else if (GetPrefSearchDlogs()) {
4197 			resp = DialogF(DF_QUES, window->shell, 2, "Wrap Search",
4198 				"Continue search\nfrom end of file?", "Continue",
4199 				"Cancel");
4200 			if (resp == 2) {
4201 			    return False;
4202 			}
4203 		    }
4204                     found = SearchString(fileString, searchString, direction,
4205 			searchType, FALSE, fileEnd + 1, startPos, endPos, extentBW,
4206 			extentFW, GetWindowDelimiters(window));
4207 		}
4208 	    }
4209             if (!found) {
4210 		if (GetPrefSearchDlogs()) {
4211 		    DialogF(DF_INF, window->shell, 1, "String not found",
4212                     "String was not found","OK");
4213 		} else {
4214 		    XBell(TheDisplay, 0);
4215 		}
4216 	    }
4217 	}
4218     } else { /* incremental search */
4219         if (outsideBounds && searchWrap) {
4220 	    if (direction == SEARCH_FORWARD) beginPos = 0;
4221 	    else beginPos = fileEnd+1;
4222             outsideBounds = FALSE;
4223         }
4224 	found = !outsideBounds &&
4225             SearchString(fileString, searchString, direction,
4226 	    searchType, searchWrap, beginPos, startPos, endPos,
4227 	    extentBW, extentFW, GetWindowDelimiters(window));
4228 	if (found) {
4229 	    iSearchTryBeepOnWrap(window, direction, beginPos, *startPos);
4230 	} else
4231 	    XBell(TheDisplay, 0);
4232     }
4233 
4234     return found;
4235 }
4236 
4237 /*
4238 ** Search the null terminated string "string" for "searchString", beginning at
4239 ** "beginPos".  Returns the boundaries of the match in "startPos" and "endPos".
4240 ** searchExtentBW and searchExtentFW return the backwardmost and forwardmost
4241 ** positions used to make the match, which are usually startPos and endPos,
4242 ** but may extend further if positive lookahead or lookbehind was used in
4243 ** a regular expression match.  "delimiters" may be used to provide an
4244 ** alternative set of word delimiters for regular expression "<" and ">"
4245 ** characters, or simply passed as null for the default delimiter set.
4246 */
SearchString(const char * string,const char * searchString,int direction,int searchType,int wrap,int beginPos,int * startPos,int * endPos,int * searchExtentBW,int * searchExtentFW,const char * delimiters)4247 int SearchString(const char *string, const char *searchString, int direction,
4248        int searchType, int wrap, int beginPos, int *startPos, int *endPos,
4249        int *searchExtentBW, int *searchExtentFW, const char *delimiters)
4250 {
4251     switch (searchType) {
4252       case SEARCH_CASE_SENSE_WORD:
4253       	 return searchLiteralWord(string, searchString, TRUE,  direction, wrap,
4254 	 		       beginPos, startPos, endPos, delimiters);
4255       case SEARCH_LITERAL_WORD:
4256       	 return  searchLiteralWord(string, searchString, FALSE, direction, wrap,
4257 	 		       beginPos, startPos, endPos, delimiters);
4258       case SEARCH_CASE_SENSE:
4259       	 return searchLiteral(string, searchString, TRUE, direction, wrap,
4260 	 		       beginPos, startPos, endPos, searchExtentBW,
4261                                searchExtentFW);
4262       case SEARCH_LITERAL:
4263       	 return  searchLiteral(string, searchString, FALSE, direction, wrap,
4264 	 	beginPos, startPos, endPos, searchExtentBW, searchExtentFW);
4265       case SEARCH_REGEX:
4266       	 return  searchRegex(string, searchString, direction, wrap,
4267       	 	beginPos, startPos, endPos, searchExtentBW, searchExtentFW,
4268                 delimiters, REDFLT_STANDARD);
4269       case SEARCH_REGEX_NOCASE:
4270       	 return  searchRegex(string, searchString, direction, wrap,
4271       	 	beginPos, startPos, endPos, searchExtentBW, searchExtentFW,
4272                 delimiters, REDFLT_CASE_INSENSITIVE);
4273     }
4274     return FALSE; /* never reached, just makes compilers happy */
4275 }
4276 
4277 /*
4278 ** Parses a search type description string. If the string contains a valid
4279 ** search type description, returns TRUE and writes the corresponding
4280 ** SearchType in searchType. Returns FALSE and leaves searchType untouched
4281 ** otherwise. (Originally written by Markus Schwarzenberg; slightly adapted).
4282 */
StringToSearchType(const char * string,int * searchType)4283 int StringToSearchType(const char * string, int *searchType)
4284 {
4285     int i;
4286     for (i = 0; searchTypeStrings[i]; i++) {
4287         if (!strcmp(string, searchTypeStrings[i])) {
4288             break;
4289         }
4290     }
4291     if (!searchTypeStrings[i]) {
4292         return FALSE;
4293     }
4294     *searchType = i;
4295     return TRUE;
4296 }
4297 
4298 /*
4299 **  Searches for whole words (Markus Schwarzenberg).
4300 **
4301 **  If the first/last character of `searchString' is a "normal
4302 **  word character" (not contained in `delimiters', not a whitespace)
4303 **  then limit search to strings, who's next left/next right character
4304 **  is contained in `delimiters' or is a whitespace or text begin or end.
4305 **
4306 **  If the first/last character of `searchString' itself is contained
4307 **  in delimiters or is a white space, then the neighbour character of the
4308 **  first/last character will not be checked, just a simple match
4309 **  will suffice in that case.
4310 **
4311 */
searchLiteralWord(const char * string,const char * searchString,int caseSense,int direction,int wrap,int beginPos,int * startPos,int * endPos,const char * delimiters)4312 static int searchLiteralWord(const char *string, const char *searchString, int caseSense,
4313 	int direction, int wrap, int beginPos, int *startPos, int *endPos,
4314         const char * delimiters)
4315 {
4316 /* This is critical code for the speed of searches.			    */
4317 /* For efficiency, we define the macro DOSEARCH with the guts of the search */
4318 /* routine and repeat it, changing the parameters of the outer loop for the */
4319 /* searching, forwards, backwards, and before and after the begin point	    */
4320 #define DOSEARCHWORD() \
4321     if (*filePtr == *ucString || *filePtr == *lcString) { \
4322 	/* matched first character */ \
4323 	ucPtr = ucString; \
4324 	lcPtr = lcString; \
4325 	tempPtr = filePtr; \
4326 	while (*tempPtr == *ucPtr || *tempPtr == *lcPtr) { \
4327 	    tempPtr++; ucPtr++; lcPtr++; \
4328 	    if (   *ucPtr == 0 /* matched whole string */ \
4329 		&& (cignore_R ||\
4330 		    isspace((unsigned char)*tempPtr) ||\
4331 		    strchr(delimiters, *tempPtr) ) \
4332 		    /* next char right delimits word ? */ \
4333 		&& (cignore_L ||\
4334                     filePtr==string || /* border case */ \
4335                     isspace((unsigned char)filePtr[-1]) ||\
4336                     strchr(delimiters,filePtr[-1]) ))\
4337                     /* next char left delimits word ? */ { \
4338 		*startPos = filePtr - string; \
4339 		*endPos = tempPtr - string; \
4340 		return TRUE; \
4341 	    } \
4342 	} \
4343     }
4344 
4345     register const char *filePtr, *tempPtr, *ucPtr, *lcPtr;
4346     char lcString[SEARCHMAX], ucString[SEARCHMAX];
4347 
4348     int cignore_L=0, cignore_R=0;
4349 
4350     /* SEARCHMAX was fine in the original NEdit, but it should be done away
4351        with now that searching can be done from macros without limits.
4352        Returning search failure here is cheating users.  This limit is not
4353        documented. */
4354     if (strlen(searchString) >= SEARCHMAX)
4355 	return FALSE;
4356 
4357     /* If there is no language mode, we use the default list of delimiters */
4358     if (delimiters==NULL) delimiters = GetPrefDelimiters();
4359 
4360     if (   isspace((unsigned char)*searchString)
4361 	|| strchr(delimiters, *searchString))
4362 	cignore_L=1;
4363 
4364     if (   isspace((unsigned char)searchString[strlen(searchString)-1])
4365 	|| strchr(delimiters, searchString[strlen(searchString)-1]) )
4366 	cignore_R=1;
4367 
4368     if (caseSense) {
4369         strcpy(ucString, searchString);
4370         strcpy(lcString, searchString);
4371     } else {
4372     	upCaseString(ucString, searchString);
4373     	downCaseString(lcString, searchString);
4374     }
4375 
4376     if (direction == SEARCH_FORWARD) {
4377 	/* search from beginPos to end of string */
4378 	for (filePtr=string+beginPos; *filePtr!=0; filePtr++) {
4379       	    DOSEARCHWORD()
4380 	}
4381 	if (!wrap)
4382 	    return FALSE;
4383 
4384 	/* search from start of file to beginPos */
4385 	for (filePtr=string; filePtr<=string+beginPos; filePtr++) {
4386       	    DOSEARCHWORD()
4387 	}
4388 	return FALSE;
4389     } else {
4390 	/* SEARCH_BACKWARD */
4391 	/* search from beginPos to start of file. A negative begin pos */
4392 	/* says begin searching from the far end of the file */
4393 	if (beginPos >= 0) {
4394 	    for (filePtr=string+beginPos; filePtr>=string; filePtr--) {
4395 	    	DOSEARCHWORD()
4396 	    }
4397 	}
4398 	if (!wrap)
4399 	    return FALSE;
4400 	/* search from end of file to beginPos */
4401 	/*... this strlen call is extreme inefficiency, but it's not obvious */
4402 	/* how to get the text string length from the text widget (under 1.1)*/
4403 	for (filePtr=string+strlen(string); filePtr>=string+beginPos; filePtr--) {
4404       	    DOSEARCHWORD()
4405 	}
4406 	return FALSE;
4407     }
4408 }
4409 
4410 
searchLiteral(const char * string,const char * searchString,int caseSense,int direction,int wrap,int beginPos,int * startPos,int * endPos,int * searchExtentBW,int * searchExtentFW)4411 static int searchLiteral(const char *string, const char *searchString, int caseSense,
4412 	int direction, int wrap, int beginPos, int *startPos, int *endPos,
4413 	int *searchExtentBW, int *searchExtentFW)
4414 {
4415 /* This is critical code for the speed of searches.			    */
4416 /* For efficiency, we define the macro DOSEARCH with the guts of the search */
4417 /* routine and repeat it, changing the parameters of the outer loop for the */
4418 /* searching, forwards, backwards, and before and after the begin point	    */
4419 #define DOSEARCH() \
4420     if (*filePtr == *ucString || *filePtr == *lcString) { \
4421 	/* matched first character */ \
4422 	ucPtr = ucString; \
4423 	lcPtr = lcString; \
4424 	tempPtr = filePtr; \
4425 	while (*tempPtr == *ucPtr || *tempPtr == *lcPtr) { \
4426 	    tempPtr++; ucPtr++; lcPtr++; \
4427 	    if (*ucPtr == 0) { \
4428 		/* matched whole string */ \
4429 		*startPos = filePtr - string; \
4430 		*endPos = tempPtr - string; \
4431 		if (searchExtentBW != NULL) \
4432 		    *searchExtentBW = *startPos; \
4433 		if (searchExtentFW != NULL) \
4434 		    *searchExtentFW = *endPos; \
4435 		return TRUE; \
4436 	    } \
4437 	} \
4438     } \
4439 
4440     register const char *filePtr, *tempPtr, *ucPtr, *lcPtr;
4441     char lcString[SEARCHMAX], ucString[SEARCHMAX];
4442 
4443     /* SEARCHMAX was fine in the original NEdit, but it should be done away with
4444        now that searching can be done from macros without limits.  Returning
4445        search failure here is cheating users.  This limit is not documented. */
4446     if (strlen(searchString) >= SEARCHMAX)
4447 	return FALSE;
4448 
4449     if (caseSense) {
4450         strcpy(ucString, searchString);
4451         strcpy(lcString, searchString);
4452     } else {
4453     	upCaseString(ucString, searchString);
4454     	downCaseString(lcString, searchString);
4455     }
4456 
4457     if (direction == SEARCH_FORWARD) {
4458 	/* search from beginPos to end of string */
4459 	for (filePtr=string+beginPos; *filePtr!=0; filePtr++) {
4460 	    DOSEARCH()
4461 	}
4462 	if (!wrap)
4463 	    return FALSE;
4464 	/* search from start of file to beginPos	*/
4465 	for (filePtr=string; filePtr<=string+beginPos; filePtr++) {
4466 	    DOSEARCH()
4467 	}
4468 	return FALSE;
4469     } else {
4470     	/* SEARCH_BACKWARD */
4471 	/* search from beginPos to start of file.  A negative begin pos	*/
4472 	/* says begin searching from the far end of the file		*/
4473 	if (beginPos >= 0) {
4474 	    for (filePtr=string+beginPos; filePtr>=string; filePtr--) {
4475 		DOSEARCH()
4476 	    }
4477 	}
4478 	if (!wrap)
4479 	    return FALSE;
4480 	/* search from end of file to beginPos */
4481 	/*... this strlen call is extreme inefficiency, but it's not obvious */
4482 	/* how to get the text string length from the text widget (under 1.1)*/
4483 	for (filePtr=string+strlen(string);
4484 		filePtr>=string+beginPos; filePtr--) {
4485 	    DOSEARCH()
4486 	}
4487 	return FALSE;
4488     }
4489 }
4490 
searchRegex(const char * string,const char * searchString,int direction,int wrap,int beginPos,int * startPos,int * endPos,int * searchExtentBW,int * searchExtentFW,const char * delimiters,int defaultFlags)4491 static int searchRegex(const char *string, const char *searchString, int direction,
4492 	int wrap, int beginPos, int *startPos, int *endPos, int *searchExtentBW,
4493 	int *searchExtentFW, const char *delimiters, int defaultFlags)
4494 {
4495     if (direction == SEARCH_FORWARD)
4496 	return forwardRegexSearch(string, searchString, wrap,
4497             beginPos, startPos, endPos, searchExtentBW, searchExtentFW,
4498 	    delimiters, defaultFlags);
4499     else
4500     	return backwardRegexSearch(string, searchString, wrap,
4501 	    beginPos, startPos, endPos, searchExtentBW, searchExtentFW,
4502             delimiters, defaultFlags);
4503 }
4504 
forwardRegexSearch(const char * string,const char * searchString,int wrap,int beginPos,int * startPos,int * endPos,int * searchExtentBW,int * searchExtentFW,const char * delimiters,int defaultFlags)4505 static int forwardRegexSearch(const char *string, const char *searchString, int wrap,
4506 	int beginPos, int *startPos, int *endPos, int *searchExtentBW,
4507         int *searchExtentFW, const char *delimiters, int defaultFlags)
4508 {
4509     regexp *compiledRE = NULL;
4510     char *compileMsg;
4511 
4512     /* compile the search string for searching with ExecRE.  Note that
4513        this does not process errors from compiling the expression.  It
4514        assumes that the expression was checked earlier. */
4515     compiledRE = CompileRE(searchString, &compileMsg, defaultFlags);
4516     if (compiledRE == NULL)
4517 	return FALSE;
4518 
4519     /* search from beginPos to end of string */
4520     if (ExecRE(compiledRE, string + beginPos, NULL, FALSE,
4521             (beginPos == 0) ? '\0' : string[beginPos-1], '\0', delimiters,
4522             string, NULL)) {
4523 	*startPos = compiledRE->startp[0] - string;
4524 	*endPos = compiledRE->endp[0] - string;
4525 	if (searchExtentFW != NULL)
4526 	    *searchExtentFW = compiledRE->extentpFW - string;
4527 	if (searchExtentBW != NULL)
4528            *searchExtentBW = compiledRE->extentpBW - string;
4529 	NEditFree(compiledRE);
4530 	return TRUE;
4531     }
4532 
4533     /* if wrap turned off, we're done */
4534     if (!wrap) {
4535     	NEditFree(compiledRE);
4536 	return FALSE;
4537     }
4538 
4539     /* search from the beginning of the string to beginPos */
4540     if (ExecRE(compiledRE, string, string + beginPos, FALSE, '\0',
4541             string[beginPos], delimiters, string, NULL)) {
4542 	*startPos = compiledRE->startp[0] - string;
4543 	*endPos = compiledRE->endp[0] - string;
4544 	if (searchExtentFW != NULL)
4545        	    *searchExtentFW = compiledRE->extentpFW - string;
4546 	if (searchExtentBW != NULL)
4547 	    *searchExtentBW = compiledRE->extentpBW - string;
4548 	NEditFree(compiledRE);
4549 	return TRUE;
4550     }
4551 
4552     NEditFree(compiledRE);
4553     return FALSE;
4554 }
4555 
backwardRegexSearch(const char * string,const char * searchString,int wrap,int beginPos,int * startPos,int * endPos,int * searchExtentBW,int * searchExtentFW,const char * delimiters,int defaultFlags)4556 static int backwardRegexSearch(const char *string, const char *searchString, int wrap,
4557 	int beginPos, int *startPos, int *endPos, int *searchExtentBW,
4558 	int *searchExtentFW, const char *delimiters, int defaultFlags)
4559 {
4560     regexp *compiledRE = NULL;
4561     char *compileMsg;
4562     int length;
4563 
4564     /* compile the search string for searching with ExecRE */
4565     compiledRE = CompileRE(searchString, &compileMsg, defaultFlags);
4566     if (compiledRE == NULL)
4567 	return FALSE;
4568 
4569     /* search from beginPos to start of file.  A negative begin pos	*/
4570     /* says begin searching from the far end of the file.		*/
4571     if (beginPos >= 0) {
4572 	if (ExecRE(compiledRE, string, string + beginPos, TRUE, '\0', '\0',
4573                 delimiters, string, NULL)) {
4574 	    *startPos = compiledRE->startp[0] - string;
4575 	    *endPos = compiledRE->endp[0] - string;
4576 	    if (searchExtentFW != NULL)
4577 		*searchExtentFW = compiledRE->extentpFW - string;
4578 	    if (searchExtentBW != NULL)
4579 		*searchExtentBW = compiledRE->extentpBW - string;
4580 	    NEditFree(compiledRE);
4581 	    return TRUE;
4582 	}
4583     }
4584 
4585     /* if wrap turned off, we're done */
4586     if (!wrap) {
4587     	NEditFree(compiledRE);
4588     	return FALSE;
4589     }
4590 
4591     /* search from the end of the string to beginPos */
4592     if (beginPos < 0)
4593     	beginPos = 0;
4594     length = strlen(string); /* sadly, this means scanning entire string */
4595     if (ExecRE(compiledRE, string + beginPos, string + length, TRUE,
4596             (beginPos == 0) ? '\0' : string[beginPos-1], '\0', delimiters,
4597             string, NULL)) {
4598 	*startPos = compiledRE->startp[0] - string;
4599 	*endPos = compiledRE->endp[0] - string;
4600 	if (searchExtentFW != NULL)
4601 	    *searchExtentFW = compiledRE->extentpFW - string;
4602 	if (searchExtentBW != NULL)
4603 	    *searchExtentBW = compiledRE->extentpBW - string;
4604 	NEditFree(compiledRE);
4605 	return TRUE;
4606     }
4607     NEditFree(compiledRE);
4608     return FALSE;
4609 }
4610 
upCaseString(char * outString,const char * inString)4611 static void upCaseString(char *outString, const char *inString)
4612 {
4613     char *outPtr;
4614     const char *inPtr;
4615 
4616     for (outPtr=outString, inPtr=inString; *inPtr!=0; inPtr++, outPtr++) {
4617     	*outPtr = toupper((unsigned char)*inPtr);
4618     }
4619     *outPtr = 0;
4620 }
4621 
downCaseString(char * outString,const char * inString)4622 static void downCaseString(char *outString, const char *inString)
4623 {
4624     char *outPtr;
4625     const char *inPtr;
4626 
4627     for (outPtr=outString, inPtr=inString; *inPtr!=0; inPtr++, outPtr++) {
4628     	*outPtr = tolower((unsigned char)*inPtr);
4629     }
4630     *outPtr = 0;
4631 }
4632 
4633 /*
4634 ** resetFindTabGroup & resetReplaceTabGroup are really gruesome kludges to
4635 ** set the keyboard traversal.  XmProcessTraversal does not work at
4636 ** all on these dialogs.  ...It seems to have started working around
4637 ** Motif 1.1.2
4638 */
resetFindTabGroup(WindowInfo * window)4639 static void resetFindTabGroup(WindowInfo *window)
4640 {
4641     XmProcessTraversal(window->findText, XmTRAVERSE_CURRENT);
4642 }
resetReplaceTabGroup(WindowInfo * window)4643 static void resetReplaceTabGroup(WindowInfo *window)
4644 {
4645     XmProcessTraversal(window->replaceText, XmTRAVERSE_CURRENT);
4646 }
4647 
4648 /*
4649 ** Return TRUE if "searchString" exactly matches the text in the window's
4650 ** current primary selection using search algorithm "searchType".  If true,
4651 ** also return the position of the selection in "left" and "right".
4652 */
searchMatchesSelection(WindowInfo * window,const char * searchString,int searchType,int * left,int * right,int * searchExtentBW,int * searchExtentFW)4653 static int searchMatchesSelection(WindowInfo *window, const char *searchString,
4654 	int searchType, int *left, int *right, int *searchExtentBW,
4655 	int *searchExtentFW)
4656 {
4657     int selLen, selStart, selEnd, startPos, endPos, extentBW, extentFW, beginPos;
4658     int regexLookContext = isRegexType(searchType) ? 1000 : 0;
4659     char *string;
4660     int found, isRect, rectStart, rectEnd, lineStart = 0;
4661 
4662     /* find length of selection, give up on no selection or too long */
4663     if (!BufGetEmptySelectionPos(window->buffer, &selStart, &selEnd, &isRect,
4664     	    &rectStart, &rectEnd))
4665 	return FALSE;
4666     if (selEnd - selStart > SEARCHMAX)
4667 	return FALSE;
4668 
4669     /* if the selection is rectangular, don't match if it spans lines */
4670     if (isRect) {
4671     	lineStart = BufStartOfLine(window->buffer, selStart);
4672     	if (lineStart != BufStartOfLine(window->buffer, selEnd))
4673     	    return FALSE;
4674     }
4675 
4676     /* get the selected text plus some additional context for regular
4677        expression lookahead */
4678     if (isRect) {
4679 	int stringStart = lineStart + rectStart - regexLookContext;
4680 	if (stringStart < 0) stringStart = 0;
4681     	string = BufGetRange(window->buffer, stringStart,
4682 		lineStart + rectEnd + regexLookContext);
4683     	selLen = rectEnd - rectStart;
4684 	beginPos = lineStart + rectStart - stringStart;
4685     } else {
4686 	int stringStart = selStart - regexLookContext;
4687 	if (stringStart < 0) stringStart = 0;
4688 	string = BufGetRange(window->buffer, stringStart,
4689 		selEnd + regexLookContext);
4690     	selLen = selEnd - selStart;
4691 	beginPos = selStart - stringStart;
4692     }
4693     if (*string == '\0') {
4694     	NEditFree(string);
4695     	return FALSE;
4696     }
4697 
4698     /* search for the string in the selection (we are only interested 	*/
4699     /* in an exact match, but the procedure SearchString does important */
4700     /* stuff like applying the correct matching algorithm)		*/
4701     found = SearchString(string, searchString, SEARCH_FORWARD, searchType,
4702     	    FALSE, beginPos, &startPos, &endPos, &extentBW, &extentFW,
4703             GetWindowDelimiters(window));
4704     NEditFree(string);
4705 
4706     /* decide if it is an exact match */
4707     if (!found)
4708     	return FALSE;
4709     if (startPos != beginPos || endPos - beginPos != selLen )
4710     	return FALSE;
4711 
4712     /* return the start and end of the selection */
4713     if (isRect)
4714     	GetSimpleSelection(window->buffer, left, right);
4715     else {
4716     	*left = selStart;
4717     	*right = selEnd;
4718     }
4719     if (searchExtentBW != NULL)
4720 	*searchExtentBW = *left - (startPos - extentBW);
4721 
4722     if (searchExtentFW != NULL)
4723 	*searchExtentFW = *right + extentFW - endPos;
4724     return TRUE;
4725 }
4726 
4727 /*
4728 ** Substitutes a replace string for a string that was matched using a
4729 ** regular expression.  This was added later and is rather ineficient
4730 ** because instead of using the compiled regular expression that was used
4731 ** to make the match in the first place, it re-compiles the expression
4732 ** and redoes the search on the already-matched string.  This allows the
4733 ** code to continue using strings to represent the search and replace
4734 ** items.
4735 */
4736 
replaceUsingRE(const char * searchStr,const char * replaceStr,const char * sourceStr,int beginPos,char * destStr,int maxDestLen,int prevChar,const char * delimiters,int defaultFlags)4737 static Boolean replaceUsingRE(const char* searchStr, const char* replaceStr,
4738         const char* sourceStr, int beginPos, char* destStr,
4739         int maxDestLen, int prevChar, const char* delimiters,
4740         int defaultFlags)
4741 {
4742     regexp *compiledRE;
4743     char *compileMsg;
4744     Boolean substResult = False;
4745 
4746     compiledRE = CompileRE(searchStr, &compileMsg, defaultFlags);
4747     ExecRE(compiledRE, sourceStr+beginPos, NULL, False, prevChar, '\0',
4748             delimiters, sourceStr, NULL);
4749     substResult = SubstituteRE(compiledRE, replaceStr, destStr, maxDestLen);
4750     NEditFree(compiledRE);
4751 
4752     return substResult;
4753 }
4754 
4755 /*
4756 ** Store the search and replace strings, and search type for later recall.
4757 ** If replaceString is NULL, duplicate the last replaceString used.
4758 ** Contiguous incremental searches share the same history entry (each new
4759 ** search modifies the current search string, until a non-incremental search
4760 ** is made.  To mark the end of an incremental search, call saveSearchHistory
4761 ** again with an empty search string and isIncremental==False.
4762 */
saveSearchHistory(const char * searchString,const char * replaceString,int searchType,int isIncremental)4763 static void saveSearchHistory(const char *searchString,
4764         const char *replaceString, int searchType, int isIncremental)
4765 {
4766     char *sStr, *rStr;
4767     static int currentItemIsIncremental = FALSE;
4768     WindowInfo *w;
4769 
4770     /* Cancel accumulation of contiguous incremental searches (even if the
4771        information is not worthy of saving) if search is not incremental */
4772     if (!isIncremental)
4773 	currentItemIsIncremental = FALSE;
4774 
4775     /* Don't save empty search strings */
4776     if (searchString[0] == '\0')
4777 	return;
4778 
4779     /* If replaceString is NULL, duplicate the last one (if any) */
4780     if (replaceString == NULL)
4781     	replaceString = NHist >= 1 ? ReplaceHistory[historyIndex(1)] : "";
4782 
4783     /* Compare the current search and replace strings against the saved ones.
4784        If they are identical, don't bother saving */
4785     if (NHist >= 1 && searchType == SearchTypeHistory[historyIndex(1)] &&
4786     	    !strcmp(SearchHistory[historyIndex(1)], searchString) &&
4787     	    !strcmp(ReplaceHistory[historyIndex(1)], replaceString)) {
4788     	return;
4789     }
4790 
4791     /* If the current history item came from an incremental search, and the
4792        new one is also incremental, just update the entry */
4793     if (currentItemIsIncremental && isIncremental) {
4794     	NEditFree(SearchHistory[historyIndex(1)]);
4795     	SearchHistory[historyIndex(1)] = NEditStrdup(searchString);
4796 	SearchTypeHistory[historyIndex(1)] = searchType;
4797 	return;
4798     }
4799     currentItemIsIncremental = isIncremental;
4800 
4801     if (NHist==0) {
4802     	for (w=WindowList; w!=NULL; w=w->next) {
4803     	    if (!IsTopDocument(w))
4804 		continue;
4805 	    XtSetSensitive(w->findAgainItem, True);
4806 	    XtSetSensitive(w->replaceFindAgainItem, True);
4807 	    XtSetSensitive(w->replaceAgainItem, True);
4808     	}
4809     }
4810 
4811     /* If there are more than MAX_SEARCH_HISTORY strings saved, recycle
4812        some space, free the entry that's about to be overwritten */
4813     if (NHist == MAX_SEARCH_HISTORY) {
4814     	NEditFree(SearchHistory[HistStart]);
4815     	NEditFree(ReplaceHistory[HistStart]);
4816     } else
4817     	NHist++;
4818 
4819     /* Allocate and copy the search and replace strings and add them to the
4820        circular buffers at HistStart, bump the buffer pointer to next pos. */
4821     sStr = NEditStrdup(searchString);
4822     rStr = NEditStrdup(replaceString);
4823     SearchHistory[HistStart] = sStr;
4824     ReplaceHistory[HistStart] = rStr;
4825     SearchTypeHistory[HistStart] = searchType;
4826     HistStart++;
4827     if (HistStart >= MAX_SEARCH_HISTORY)
4828     	HistStart = 0;
4829 }
4830 
4831 /*
4832 ** return an index into the circular buffer arrays of history information
4833 ** for search strings, given the number of saveSearchHistory cycles back from
4834 ** the current time.
4835 */
historyIndex(int nCycles)4836 static int historyIndex(int nCycles)
4837 {
4838     int index;
4839 
4840     if (nCycles > NHist || nCycles <= 0)
4841     	return -1;
4842     index = HistStart - nCycles;
4843     if (index < 0)
4844     	index = MAX_SEARCH_HISTORY + index;
4845     return index;
4846 }
4847 
4848 /*
4849 ** Return a pointer to the string describing search type for search action
4850 ** routine parameters (see menu.c for processing of action routines)
4851 */
searchTypeArg(int searchType)4852 static char *searchTypeArg(int searchType)
4853 {
4854     if (0 <= searchType && searchType < N_SEARCH_TYPES) {
4855         return searchTypeStrings[searchType];
4856     }
4857     return searchTypeStrings[SEARCH_LITERAL];
4858 }
4859 
4860 /*
4861 ** Return a pointer to the string describing search wrap for search action
4862 ** routine parameters (see menu.c for processing of action routines)
4863 */
searchWrapArg(int searchWrap)4864 static char *searchWrapArg(int searchWrap)
4865 {
4866     if (searchWrap) {
4867     	return "wrap";
4868     }
4869     return "nowrap";
4870 }
4871 
4872 /*
4873 ** Return a pointer to the string describing search direction for search action
4874 ** routine parameters (see menu.c for processing of action routines)
4875 */
directionArg(int direction)4876 static char *directionArg(int direction)
4877 {
4878     if (direction == SEARCH_BACKWARD)
4879     	return "backward";
4880     return "forward";
4881 }
4882 
4883 /*
4884 ** Checks whether a search mode in one of the regular expression modes.
4885 */
isRegexType(int searchType)4886 static int isRegexType(int searchType)
4887 {
4888     return searchType == SEARCH_REGEX || searchType == SEARCH_REGEX_NOCASE;
4889 }
4890 
4891 /*
4892 ** Returns the default flags for regular expression matching, given a
4893 ** regular expression search mode.
4894 */
defaultRegexFlags(int searchType)4895 static int defaultRegexFlags(int searchType)
4896 {
4897     switch (searchType) {
4898 	case SEARCH_REGEX:
4899 	    return REDFLT_STANDARD;
4900 	case SEARCH_REGEX_NOCASE:
4901 	    return REDFLT_CASE_INSENSITIVE;
4902 	default:
4903 	    /* We should never get here, but just in case ... */
4904 	    return REDFLT_STANDARD;
4905     }
4906 }
4907 
4908 /*
4909 ** The next 4 callbacks handle the states of find/replace toggle
4910 ** buttons, which depend on the state of the "Regex" button, and the
4911 ** sensitivity of the Whole Word buttons.
4912 ** Callbacks are necessary for both "Regex" and "Case Sensitive"
4913 ** buttons to make sure the states are saved even after a cancel operation.
4914 **
4915 ** If sticky case sensitivity is requested, the behaviour is as follows:
4916 **   The first time "Regular expression" is checked, "Match case" gets
4917 **   checked too. Thereafter, checking or unchecking "Regular expression"
4918 **   restores the "Match case" button to the setting it had the last
4919 **   time when literals or REs where used.
4920 ** Without sticky behaviour, the state of the Regex button doesn't influence
4921 ** the state of the Case Sensitive button.
4922 **
4923 ** Independently, the state of the buttons is always restored to the
4924 ** default state when a dialog is popped up, and when the user returns
4925 ** from stepping through the search history.
4926 **
4927 ** NOTE: similar call-backs exist for the incremental search bar; see window.c.
4928 */
findRegExpToggleCB(Widget w,XtPointer clientData,XtPointer callData)4929 static void findRegExpToggleCB(Widget w, XtPointer clientData, XtPointer callData)
4930 {
4931     WindowInfo * window = WidgetToWindow(w);
4932     int searchRegex = XmToggleButtonGetState(w);
4933     int searchCaseSense = XmToggleButtonGetState(window->findCaseToggle);
4934 
4935     /* In sticky mode, restore the state of the Case Sensitive button */
4936     if(GetPrefStickyCaseSenseBtn()) {
4937 	if(searchRegex) {
4938 	    window->findLastLiteralCase = searchCaseSense;
4939 	    XmToggleButtonSetState(window->findCaseToggle,
4940 		window->findLastRegexCase, False);
4941 	} else {
4942 	    window->findLastRegexCase = searchCaseSense;
4943 	    XmToggleButtonSetState(window->findCaseToggle,
4944 		window->findLastLiteralCase, False);
4945 	}
4946     }
4947     /* make the Whole Word button insensitive for regex searches */
4948     XtSetSensitive(window->findWordToggle, !searchRegex);
4949 }
4950 
replaceRegExpToggleCB(Widget w,XtPointer clientData,XtPointer callData)4951 static void replaceRegExpToggleCB(Widget w, XtPointer clientData, XtPointer callData)
4952 {
4953     WindowInfo * window = WidgetToWindow(w);
4954     int searchRegex = XmToggleButtonGetState(w);
4955     int searchCaseSense = XmToggleButtonGetState(window->replaceCaseToggle);
4956 
4957     /* In sticky mode, restore the state of the Case Sensitive button */
4958     if(GetPrefStickyCaseSenseBtn()) {
4959 	if(searchRegex) {
4960       	    window->replaceLastLiteralCase = searchCaseSense;
4961 	    XmToggleButtonSetState(window->replaceCaseToggle,
4962 		window->replaceLastRegexCase, False);
4963 	} else {
4964       	    window->replaceLastRegexCase = searchCaseSense;
4965 	    XmToggleButtonSetState(window->replaceCaseToggle,
4966 		window->replaceLastLiteralCase, False);
4967 	}
4968     }
4969     /* make the Whole Word button insensitive for regex searches */
4970     XtSetSensitive(window->replaceWordToggle, !searchRegex);
4971 }
4972 
iSearchRegExpToggleCB(Widget w,XtPointer clientData,XtPointer callData)4973 static void iSearchRegExpToggleCB(Widget w, XtPointer clientData, XtPointer callData)
4974 {
4975     WindowInfo * window = WidgetToWindow(w);
4976     int searchRegex = XmToggleButtonGetState(w);
4977     int searchCaseSense = XmToggleButtonGetState(window->iSearchCaseToggle);
4978 
4979     /* In sticky mode, restore the state of the Case Sensitive button */
4980     if(GetPrefStickyCaseSenseBtn()) {
4981 	if(searchRegex) {
4982       	    window->iSearchLastLiteralCase = searchCaseSense;
4983 	    XmToggleButtonSetState(window->iSearchCaseToggle,
4984 		window->iSearchLastRegexCase, False);
4985 	} else {
4986       	    window->iSearchLastRegexCase = searchCaseSense;
4987 	    XmToggleButtonSetState(window->iSearchCaseToggle,
4988 		window->iSearchLastLiteralCase, False);
4989 	}
4990     }
4991     /* The iSearch bar has no Whole Word button to enable/disable. */
4992 }
findCaseToggleCB(Widget w,XtPointer clientData,XtPointer callData)4993 static void findCaseToggleCB(Widget w, XtPointer clientData, XtPointer callData)
4994 {
4995     WindowInfo * window = WidgetToWindow(w);
4996     int searchCaseSense = XmToggleButtonGetState(w);
4997 
4998     /* Save the state of the Case Sensitive button
4999        depending on the state of the Regex button*/
5000     if(XmToggleButtonGetState(window->findRegexToggle))
5001     	window->findLastRegexCase = searchCaseSense;
5002     else
5003 	window->findLastLiteralCase = searchCaseSense;
5004 }
5005 
replaceCaseToggleCB(Widget w,XtPointer clientData,XtPointer callData)5006 static void replaceCaseToggleCB(Widget w, XtPointer clientData, XtPointer callData)
5007 {
5008     WindowInfo * window = WidgetToWindow(w);
5009     int searchCaseSense = XmToggleButtonGetState(w);
5010 
5011     /* Save the state of the Case Sensitive button
5012        depending on the state of the Regex button*/
5013     if(XmToggleButtonGetState(window->replaceRegexToggle))
5014     	window->replaceLastRegexCase = searchCaseSense;
5015     else
5016 	window->replaceLastLiteralCase = searchCaseSense;
5017 }
5018 
iSearchCaseToggleCB(Widget w,XtPointer clientData,XtPointer callData)5019 static void iSearchCaseToggleCB(Widget w, XtPointer clientData, XtPointer callData)
5020 {
5021     WindowInfo * window = WidgetToWindow(w);
5022     int searchCaseSense = XmToggleButtonGetState(w);
5023 
5024     /* Save the state of the Case Sensitive button
5025        depending on the state of the Regex button*/
5026     if(XmToggleButtonGetState(window->iSearchRegexToggle))
5027     	window->iSearchLastRegexCase = searchCaseSense;
5028     else
5029 	window->iSearchLastLiteralCase = searchCaseSense;
5030 }
5031