Propogate colors to new windows on opening (744294)
[nedit.git] / source / search.c
blob2bd3e03861412a9806a5beca77c09c2db31a7a49
1 static const char CVSID[] = "$Id: search.c,v 1.58 2003/05/26 08:16:37 edg Exp $";
2 /*******************************************************************************
3 * *
4 * search.c -- Nirvana Editor search and replace functions *
5 * *
6 * Copyright (C) 1999 Mark Edel *
7 * *
8 * This is free software; you can redistribute it and/or modify it under the *
9 * terms of the GNU General Public License as published by the Free Software *
10 * Foundation; either version 2 of the License, or (at your option) any later *
11 * version. *
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 *******************************************************************************/
29 #ifdef HAVE_CONFIG_H
30 #include "../config.h"
31 #endif
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 "preferences.h"
41 #include "file.h"
42 #include "highlight.h"
43 #ifdef REPLACE_SCOPE
44 #include "textDisp.h"
45 #include "textP.h"
46 #endif
47 #include "../util/DialogF.h"
48 #include "../util/misc.h"
50 #include <stdlib.h>
51 #include <stdio.h>
52 #include <string.h>
53 #include <ctype.h>
54 #ifdef VMS
55 #include "../util/VMSparam.h"
56 #else
57 #ifndef __MVS__
58 #include <sys/param.h>
59 #endif
60 #endif /*VMS*/
62 #include <Xm/Xm.h>
63 #include <X11/Shell.h>
64 #include <Xm/XmP.h>
65 #include <Xm/Form.h>
66 #include <Xm/Label.h>
67 #ifdef REPLACE_SCOPE
68 #if XmVersion >= 1002
69 #include <Xm/PrimitiveP.h>
70 #endif
71 #endif
72 #include <Xm/PushB.h>
73 #include <Xm/RowColumn.h>
74 #include <Xm/Text.h>
75 #include <Xm/ToggleB.h>
76 #include <Xm/List.h>
77 #include <X11/Xatom.h> /* for getting selection */
78 #include <X11/keysym.h>
79 #include <X11/X.h> /* " " */
81 #ifdef HAVE_DEBUG_H
82 #include "../debug.h"
83 #endif
86 int NHist = 0;
88 typedef struct _SelectionInfo {
89 int done;
90 WindowInfo* window;
91 char* selection;
92 } SelectionInfo;
94 typedef struct {
95 int direction;
96 int searchType;
97 int searchWrap;
98 } SearchSelectedCallData;
100 /* History mechanism for search and replace strings */
101 static char *SearchHistory[MAX_SEARCH_HISTORY];
102 static char *ReplaceHistory[MAX_SEARCH_HISTORY];
103 static int SearchTypeHistory[MAX_SEARCH_HISTORY];
104 static int HistStart = 0;
106 static int textFieldNonEmpty(Widget w);
107 static void setTextField(WindowInfo* window, Time time, Widget textField);
108 static void getSelectionCB(Widget w, SelectionInfo *selectionInfo, Atom *selection,
109 Atom *type, char *value, int *length, int *format);
110 static void createReplaceDlog(Widget parent, WindowInfo *window);
111 static void createFindDlog(Widget parent, WindowInfo *window);
112 static void createReplaceMultiFileDlog(WindowInfo *window);
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);
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
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(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, char *value, int *length, int *format);
184 static void iSearchTextActivateCB(Widget w, WindowInfo *window,
185 XmAnyCallbackStruct *callData);
186 static void iSearchTextValueChangedCB(Widget w, WindowInfo *window,
187 XmAnyCallbackStruct *callData);
188 static void iSearchTextKeyEH(Widget w, WindowInfo *window,
189 XKeyEvent *event, Boolean *continueDispatch);
190 static int searchLiteral(const char *string, const char *searchString, int caseSense,
191 int direction, int wrap, int beginPos, int *startPos, int *endPos,
192 int *searchExtentBW, int *searchExtentFW);
193 static int searchLiteralWord(const char *string, const char *searchString, int caseSense,
194 int direction, int wrap, int beginPos, int *startPos, int *endPos,
195 const char * delimiters);
196 static int searchRegex(const char *string, const char *searchString, int direction,
197 int wrap, int beginPos, int *startPos, int *endPos, int *searchExtentBW,
198 int *searchExtentFW, const char *delimiters, int defaultFlags);
199 static int forwardRegexSearch(const char *string, const char *searchString, int wrap,
200 int beginPos, int *startPos, int *endPos, int *searchExtentBW,
201 int *searchExtentFW, const char *delimiters, int defaultFlags);
202 static int backwardRegexSearch(const char *string, const char *searchString, int wrap,
203 int beginPos, int *startPos, int *endPos, int *searchExtentBW,
204 int *searchExtentFW, const char *delimiters, int defaultFlags);
205 static void upCaseString(char *outString, const char *inString);
206 static void downCaseString(char *outString, const char *inString);
207 static void resetFindTabGroup(WindowInfo *window);
208 static void resetReplaceTabGroup(WindowInfo *window);
209 static int searchMatchesSelection(WindowInfo *window, const char *searchString,
210 int searchType, int *left, int *right, int *searchExtentBW,
211 int *searchExtentFW);
212 static int findMatchingChar(WindowInfo *window, char toMatch,
213 void *toMatchStyle, int charPos, int startLimit, int endLimit,
214 int *matchPos);
215 static void replaceUsingRE(const char *searchStr, const char *replaceStr,
216 const char *sourceStr, int beginPos, char *destStr,
217 int maxDestLen, int prevChar, const char *delimiters, int defaultFlags);
218 static void saveSearchHistory(const char *searchString,
219 const char *replaceString, int searchType, int isIncremental);
220 static int historyIndex(int nCycles);
221 static char *searchTypeArg(int searchType);
222 static char *searchWrapArg(int searchWrap);
223 static char *directionArg(int direction);
224 static int isRegexType(int searchType);
225 static int defaultRegexFlags(int searchType);
226 static void findRegExpToggleCB(Widget w, XtPointer clientData,
227 XtPointer callData);
228 static void replaceRegExpToggleCB(Widget w, XtPointer clientData,
229 XtPointer callData);
230 static void iSearchRegExpToggleCB(Widget w, XtPointer clientData,
231 XtPointer callData);
232 static void findCaseToggleCB(Widget w, XtPointer clientData,
233 XtPointer callData);
234 static void replaceCaseToggleCB(Widget w, XtPointer clientData,
235 XtPointer callData);
236 static void iSearchCaseToggleCB(Widget w, XtPointer clientData,
237 XtPointer callData);
238 static void iSearchTryBeepOnWrap(WindowInfo *window, int direction,
239 int beginPos, int startPos);
240 static void iSearchRecordLastBeginPos(WindowInfo *window, int direction,
241 int initPos);
243 typedef struct _charMatchTable {
244 char c;
245 char match;
246 char direction;
247 } charMatchTable;
249 #define N_MATCH_CHARS 13
250 #define N_FLASH_CHARS 6
251 static charMatchTable MatchingChars[N_MATCH_CHARS] = {
252 {'{', '}', SEARCH_FORWARD},
253 {'}', '{', SEARCH_BACKWARD},
254 {'(', ')', SEARCH_FORWARD},
255 {')', '(', SEARCH_BACKWARD},
256 {'[', ']', SEARCH_FORWARD},
257 {']', '[', SEARCH_BACKWARD},
258 {'<', '>', SEARCH_FORWARD},
259 {'>', '<', SEARCH_BACKWARD},
260 {'/', '/', SEARCH_FORWARD},
261 {'"', '"', SEARCH_FORWARD},
262 {'\'', '\'', SEARCH_FORWARD},
263 {'`', '`', SEARCH_FORWARD},
264 {'\\', '\\', SEARCH_FORWARD},
268 ** Definitions for the search method strings, used as arguments for
269 ** macro search subroutines and search action routines
271 static char *searchTypeStrings[] = {
272 "literal", /* SEARCH_LITERAL */
273 "case", /* SEARCH_CASE_SENSE */
274 "regex", /* SEARCH_REGEX */
275 "word", /* SEARCH_LITERAL_WORD */
276 "caseWord", /* SEARCH_CASE_SENSE_WORD */
277 "regexNoCase", /* SEARCH_REGEX_NOCASE */
278 NULL
282 ** Shared routine for replace and find dialogs and i-search bar to initialize
283 ** the state of the regex/case/word toggle buttons, and the sticky case
284 ** sensitivity states.
286 static void initToggleButtons(int searchType, Widget regexToggle,
287 Widget caseToggle, Widget* wordToggle,
288 Bool* lastLiteralCase,
289 Bool* lastRegexCase)
291 /* Set the initial search type and remember the corresponding case
292 sensitivity states in case sticky case sensitivity is required. */
293 switch (searchType) {
294 case SEARCH_LITERAL:
295 *lastLiteralCase = False;
296 *lastRegexCase = True;
297 XmToggleButtonSetState(regexToggle, False, False);
298 XmToggleButtonSetState(caseToggle, False, False);
299 if (wordToggle) {
300 XmToggleButtonSetState(*wordToggle, False, False);
301 XtSetSensitive(*wordToggle, True);
303 break;
304 case SEARCH_CASE_SENSE:
305 *lastLiteralCase = True;
306 *lastRegexCase = True;
307 XmToggleButtonSetState(regexToggle, False, False);
308 XmToggleButtonSetState(caseToggle, True, False);
309 if (wordToggle) {
310 XmToggleButtonSetState(*wordToggle, False, False);
311 XtSetSensitive(*wordToggle, True);
313 break;
314 case SEARCH_LITERAL_WORD:
315 *lastLiteralCase = False;
316 *lastRegexCase = True;
317 XmToggleButtonSetState(regexToggle, False, False);
318 XmToggleButtonSetState(caseToggle, False, False);
319 if (wordToggle) {
320 XmToggleButtonSetState(*wordToggle, True, False);
321 XtSetSensitive(*wordToggle, True);
323 break;
324 case SEARCH_CASE_SENSE_WORD:
325 *lastLiteralCase = True;
326 *lastRegexCase = True;
327 XmToggleButtonSetState(regexToggle, False, False);
328 XmToggleButtonSetState(caseToggle, True, False);
329 if (wordToggle) {
330 XmToggleButtonSetState(*wordToggle, True, False);
331 XtSetSensitive(*wordToggle, True);
333 break;
334 case SEARCH_REGEX:
335 *lastLiteralCase = False;
336 *lastRegexCase = True;
337 XmToggleButtonSetState(regexToggle, True, False);
338 XmToggleButtonSetState(caseToggle, True, False);
339 if (wordToggle) {
340 XmToggleButtonSetState(*wordToggle, False, False);
341 XtSetSensitive(*wordToggle, False);
343 break;
344 case SEARCH_REGEX_NOCASE:
345 *lastLiteralCase = False;
346 *lastRegexCase = False;
347 XmToggleButtonSetState(regexToggle, True, False);
348 XmToggleButtonSetState(caseToggle, False, False);
349 if (wordToggle) {
350 XmToggleButtonSetState(*wordToggle, False, False);
351 XtSetSensitive(*wordToggle, False);
353 break;
357 #ifdef REPLACE_SCOPE
359 ** Checks whether a selection spans multiple lines. Used to decide on the
360 ** default scope for replace dialogs.
361 ** This routine introduces a dependency on textDisp.h, which is not so nice,
362 ** but I currently don't have a cleaner solution.
364 static int selectionSpansMultipleLines(WindowInfo *window)
366 int selStart, selEnd, isRect, rectStart, rectEnd, lineStartStart,
367 lineStartEnd;
368 int lineWidth;
369 textDisp *textD;
371 if (!BufGetSelectionPos(window->buffer, &selStart, &selEnd, &isRect,
372 &rectStart, &rectEnd))
373 return FALSE;
375 /* This is kind of tricky. The perception of a line depends on the
376 line wrap mode being used. So in theory, we should take into
377 account the layout of the text on the screen. However, the
378 routine to calculate a line number for a given character position
379 (TextDPosToLineAndCol) only works for displayed lines, so we cannot
380 use it. Therefore, we use this simple heuristic:
381 - If a newline is found between the start and end of the selection,
382 we obviously have a multi-line selection.
383 - If no newline is found, but the distance between the start and the
384 end of the selection is larger than the number of characters
385 displayed on a line, and we're in continuous wrap mode,
386 we also assume a multi-line selection.
389 lineStartStart = BufStartOfLine(window->buffer, selStart);
390 lineStartEnd = BufStartOfLine(window->buffer, selEnd);
391 /* If the line starts differ, we have a "\n" in between. */
392 if (lineStartStart != lineStartEnd )
393 return TRUE;
395 if (window->wrapMode != CONTINUOUS_WRAP)
396 return FALSE; /* Same line */
398 /* Estimate the number of characters on a line */
399 textD = ((TextWidget)window->textArea)->text.textD;
400 if (textD->fontStruct->max_bounds.width > 0)
401 lineWidth = textD->width / textD->fontStruct->max_bounds.width;
402 else
403 lineWidth = 1;
404 if (lineWidth < 1) lineWidth = 1; /* Just in case */
406 /* Estimate the numbers of line breaks from the start of the line to
407 the start and ending positions of the selection and compare.*/
408 if ((selStart-lineStartStart)/lineWidth !=
409 (selEnd-lineStartStart)/lineWidth )
410 return TRUE; /* Spans multiple lines */
412 return FALSE; /* Small selection; probably doesn't span lines */
414 #endif
416 void DoFindReplaceDlog(WindowInfo *window, int direction, int keepDialogs,
417 Time time)
420 /* Create the dialog if it doesn't already exist */
421 if (window->replaceDlog == NULL)
422 createReplaceDlog(window->shell, window);
424 setTextField(window, time, window->replaceText);
426 /* If the window is already up, just pop it to the top */
427 if (XtIsManaged(window->replaceDlog)) {
428 RaiseShellWindow(XtParent(window->replaceDlog));
429 return;
432 /* Blank the Replace with field */
433 XmTextSetString(window->replaceWithText, "");
435 /* Set the initial search type */
436 initToggleButtons(GetPrefSearch(), window->replaceRegexToggle,
437 window->replaceCaseToggle, &window->replaceWordToggle,
438 &window->replaceLastLiteralCase,
439 &window->replaceLastRegexCase);
441 /* Set the initial direction based on the direction argument */
442 XmToggleButtonSetState(window->replaceRevToggle,
443 direction == SEARCH_FORWARD ? False: True, True);
445 /* Set the state of the Keep Dialog Up button */
446 XmToggleButtonSetState(window->replaceKeepBtn, keepDialogs, True);
448 #ifdef REPLACE_SCOPE
449 /* Set the state of the scope radio buttons to "In Window".
450 Notify to make sure that callbacks are called.
451 NOTE: due to an apparent bug in OpenMotif, the radio buttons may
452 get stuck after resetting the scope to "In Window". I currently
453 don't have a workaround. */
454 if (window->wasSelected) {
455 /* If a selection exists, the default scope depends on the preference
456 of the user. */
457 switch(GetPrefReplaceDefScope()) {
458 case REPL_DEF_SCOPE_SELECTION:
459 /* The user prefers selection scope, no matter what the
460 size of the selection is. */
461 XmToggleButtonSetState(window->replaceScopeSelToggle,
462 True, True);
463 break;
464 case REPL_DEF_SCOPE_SMART:
465 if (selectionSpansMultipleLines(window)) {
466 /* If the selection spans multiple lines, the user most
467 likely wants to perform a replacement in the selection */
468 XmToggleButtonSetState(window->replaceScopeSelToggle,
469 True, True);
471 else {
472 /* It's unlikely that the user wants a replacement in a
473 tiny selection only. */
474 XmToggleButtonSetState(window->replaceScopeWinToggle,
475 True, True);
477 break;
478 default:
479 /* The user always wants window scope as default. */
480 XmToggleButtonSetState(window->replaceScopeWinToggle,
481 True, True);
482 break;
485 else {
486 /* No selection -> always choose "In Window" as default. */
487 XmToggleButtonSetState(window->replaceScopeWinToggle, True, True);
489 #endif
491 UpdateReplaceActionButtons(window);
493 /* Start the search history mechanism at the current history item */
494 window->rHistIndex = 0;
496 /* Display the dialog */
497 ManageDialogCenteredOnPointer(window->replaceDlog);
499 /* Workaround: LessTif (as of version 0.89) needs reminding of who had
500 the focus when the dialog was unmanaged. When re-managed, focus is
501 lost and events fall through to the window below. */
502 XmProcessTraversal(window->replaceText, XmTRAVERSE_CURRENT);
505 static void setTextField(WindowInfo *window, Time time, Widget textField)
507 XEvent nextEvent;
508 char *primary_selection = 0;
509 SelectionInfo *selectionInfo = XtNew(SelectionInfo);
511 if (GetPrefFindReplaceUsesSelection()) {
512 selectionInfo->done = 0;
513 selectionInfo->window = window;
514 selectionInfo->selection = 0;
515 XtGetSelectionValue(window->textArea, XA_PRIMARY, XA_STRING,
516 (XtSelectionCallbackProc)getSelectionCB, selectionInfo, time);
517 while (selectionInfo->done == 0) {
518 XtAppNextEvent(XtWidgetToApplicationContext(window->textArea), &nextEvent);
519 ServerDispatchEvent(&nextEvent);
521 primary_selection = selectionInfo->selection;
523 if (primary_selection == 0) {
524 primary_selection = XtNewString("");
527 /* Update the field */
528 XmTextSetString(textField, primary_selection);
530 XtFree(primary_selection);
531 XtFree((char*)selectionInfo);
534 static void getSelectionCB(Widget w, SelectionInfo *selectionInfo, Atom *selection,
535 Atom *type, char *value, int *length, int *format)
537 WindowInfo *window = selectionInfo->window;
539 /* return an empty string if we can't get the selection data */
540 if (*type == XT_CONVERT_FAIL || *type != XA_STRING || value == NULL || *length == 0) {
541 XtFree(value);
542 selectionInfo->selection = 0;
543 selectionInfo->done = 1;
544 return;
546 /* return an empty string if the data is not of the correct format. */
547 if (*format != 8) {
548 DialogF(DF_WARN, window->shell, 1, "Invalid Format",
549 "NEdit can't handle non 8-bit text", "OK");
550 XtFree(value);
551 selectionInfo->selection = 0;
552 selectionInfo->done = 1;
553 return;
555 selectionInfo->selection = XtMalloc(*length+1);
556 memcpy(selectionInfo->selection, value, *length);
557 selectionInfo->selection[*length] = 0;
558 XtFree(value);
559 selectionInfo->done = 1;
562 void DoFindDlog(WindowInfo *window, int direction, int keepDialogs, Time time)
565 /* Create the dialog if it doesn't already exist */
566 if (window->findDlog == NULL)
567 createFindDlog(window->shell, window);
569 setTextField(window, time, window->findText);
571 /* If the window is already up, just pop it to the top */
572 if (XtIsManaged(window->findDlog)) {
573 RaiseShellWindow(XtParent(window->findDlog));
574 return;
577 /* Set the initial search type */
578 initToggleButtons(GetPrefSearch(), window->findRegexToggle,
579 window->findCaseToggle, &window->findWordToggle,
580 &window->findLastLiteralCase,
581 &window->findLastRegexCase);
583 /* Set the initial direction based on the direction argument */
584 XmToggleButtonSetState(window->findRevToggle,
585 direction == SEARCH_FORWARD ? False : True, True);
587 /* Set the state of the Keep Dialog Up button */
588 XmToggleButtonSetState(window->findKeepBtn, keepDialogs, True);
590 /* Set the state of the Find button */
591 fUpdateActionButtons(window);
593 /* start the search history mechanism at the current history item */
594 window->fHistIndex = 0;
596 /* Display the dialog */
597 ManageDialogCenteredOnPointer(window->findDlog);
600 void DoReplaceMultiFileDlog(WindowInfo *window)
602 char searchString[SEARCHMAX], replaceString[SEARCHMAX];
603 int direction, searchType;
605 /* Validate and fetch the find and replace strings from the dialog */
606 if (!getReplaceDlogInfo(window, &direction, searchString, replaceString,
607 &searchType))
608 return;
610 /* Don't let the user select files when no replacement can be made */
611 if (*searchString == '\0') {
612 /* Set the initial focus of the dialog back to the search string */
613 resetReplaceTabGroup(window);
614 /* pop down the replace dialog */
615 if (!XmToggleButtonGetState(window->replaceKeepBtn))
616 unmanageReplaceDialogs(window);
617 return;
620 /* Create the dialog if it doesn't already exist */
621 if (window->replaceMultiFileDlog == NULL)
622 createReplaceMultiFileDlog(window);
624 /* Raising the window doesn't make sense. It is modal, so we
625 can't get here unless it is unmanaged */
626 /* Prepare a list of writable windows */
627 collectWritableWindows(window);
629 /* Initialize/update the list of files. */
630 uploadFileListItems(window, False);
632 /* Display the dialog */
633 ManageDialogCenteredOnPointer(window->replaceMultiFileDlog);
637 ** If a window is closed (possibly via the window manager) while it is on the
638 ** multi-file replace dialog list of any other window (or even the same one),
639 ** we must update those lists or we end up with dangling references.
640 ** Normally, there can be only one of those dialogs at the same time
641 ** (application modal), but Lesstif doesn't (always) honor application
642 ** modalness, so there can be more than one dialog.
644 void RemoveFromMultiReplaceDialog(WindowInfo *doomedWindow)
646 WindowInfo *w;
648 for (w=WindowList; w!=NULL; w=w->next)
649 if (w->writableWindows)
650 /* A multi-file replacement dialog is up for this window */
651 checkMultiReplaceListForDoomedW(w, doomedWindow);
654 static void createReplaceDlog(Widget parent, WindowInfo *window)
656 Arg args[50];
657 int argcnt, defaultBtnOffset;
658 XmString st1;
659 Widget form, btnForm;
660 #ifdef REPLACE_SCOPE
661 Widget scopeForm, replaceAllBtn;
662 #else
663 Widget label3, allForm;
664 #endif
665 Widget inWinBtn, inSelBtn, inMultiBtn;
666 Widget searchTypeBox;
667 Widget label2, label1, label, replaceText, findText;
668 Widget findBtn, cancelBtn, replaceBtn;
669 Widget replaceFindBtn;
670 Widget searchDirBox, reverseBtn, keepBtn;
671 char title[MAXPATHLEN + 19];
672 Dimension shadowThickness;
674 argcnt = 0;
675 XtSetArg(args[argcnt], XmNautoUnmanage, False); argcnt++;
676 form = CreateFormDialog(parent, "replaceDialog", args, argcnt);
677 XtVaSetValues(form, XmNshadowThickness, 0, NULL);
678 if (GetPrefKeepSearchDlogs()) {
679 sprintf(title, "Replace/Find (in %s)", window->filename);
680 XtVaSetValues(XtParent(form), XmNtitle, title, NULL);
681 } else
682 XtVaSetValues(XtParent(form), XmNtitle, "Replace/Find", NULL);
684 argcnt = 0;
685 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
686 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
687 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
688 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_NONE); argcnt++;
689 XtSetArg(args[argcnt], XmNleftOffset, 4); argcnt++;
690 XtSetArg(args[argcnt], XmNtopOffset, 6); argcnt++;
691 XtSetArg(args[argcnt], XmNalignment, XmALIGNMENT_BEGINNING); argcnt++;
692 XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("String to Find:"));
693 argcnt++;
694 XtSetArg(args[argcnt], XmNmnemonic, 't'); argcnt++;
695 label1 = XmCreateLabel(form, "label1", args, argcnt);
696 XmStringFree(st1);
697 XtManageChild(label1);
699 argcnt = 0;
700 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
701 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
702 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_NONE); argcnt++;
703 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_FORM); argcnt++;
704 XtSetArg(args[argcnt], XmNrightOffset, 6); argcnt++;
705 XtSetArg(args[argcnt], XmNtopOffset, 6); argcnt++;
706 XtSetArg(args[argcnt], XmNalignment, XmALIGNMENT_END); argcnt++;
707 XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING(
708 "(use up arrow key to recall previous)")); argcnt++;
709 label2 = XmCreateLabel(form, "label2", args, argcnt);
710 XmStringFree(st1);
711 XtManageChild(label2);
713 argcnt = 0;
714 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
715 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
716 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_WIDGET); argcnt++;
717 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
718 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
719 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_FORM); argcnt++;
720 XtSetArg(args[argcnt], XmNtopWidget, label1); argcnt++;
721 XtSetArg(args[argcnt], XmNleftOffset, 6); argcnt++;
722 XtSetArg(args[argcnt], XmNrightOffset, 6); argcnt++;
723 XtSetArg(args[argcnt], XmNmaxLength, SEARCHMAX); argcnt++;
724 findText = XmCreateText(form, "replaceString", args, argcnt);
725 XtAddCallback(findText, XmNfocusCallback, (XtCallbackProc)rFocusCB, window);
726 XtAddCallback(findText, XmNvalueChangedCallback,
727 (XtCallbackProc)rFindTextValueChangedCB, window);
728 XtAddEventHandler(findText, KeyPressMask, False,
729 (XtEventHandler)rFindArrowKeyCB, window);
730 RemapDeleteKey(findText);
731 XtManageChild(findText);
732 XmAddTabGroup(findText);
733 XtVaSetValues(label1, XmNuserData, findText, NULL); /* mnemonic processing */
735 argcnt = 0;
736 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_WIDGET); argcnt++;
737 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
738 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
739 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_NONE); argcnt++;
740 XtSetArg(args[argcnt], XmNtopWidget, findText); argcnt++;
741 XtSetArg(args[argcnt], XmNleftOffset, 6); argcnt++;
742 XtSetArg(args[argcnt], XmNtopOffset, 6); argcnt++;
743 XtSetArg(args[argcnt], XmNalignment, XmALIGNMENT_BEGINNING); argcnt++;
744 XtSetArg(args[argcnt], XmNlabelString,
745 st1=MKSTRING("Replace With:")); argcnt++;
746 XtSetArg(args[argcnt], XmNmnemonic, 'W'); argcnt++;
747 label = XmCreateLabel(form, "label", args, argcnt);
748 XmStringFree(st1);
749 XtManageChild(label);
751 argcnt = 0;
752 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
753 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
754 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_WIDGET); argcnt++;
755 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
756 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
757 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_FORM); argcnt++;
758 XtSetArg(args[argcnt], XmNtopWidget, label); argcnt++;
759 XtSetArg(args[argcnt], XmNleftOffset, 6); argcnt++;
760 XtSetArg(args[argcnt], XmNrightOffset, 6); argcnt++;
761 XtSetArg(args[argcnt], XmNmaxLength, SEARCHMAX); argcnt++;
762 replaceText = XmCreateText(form, "replaceWithString", args, argcnt);
763 XtAddEventHandler(replaceText, KeyPressMask, False,
764 (XtEventHandler)replaceArrowKeyCB, window);
765 RemapDeleteKey(replaceText);
766 XtManageChild(replaceText);
767 XmAddTabGroup(replaceText);
768 XtVaSetValues(label, XmNuserData, replaceText, NULL); /* mnemonic processing */
770 argcnt = 0;
771 XtSetArg(args[argcnt], XmNorientation, XmHORIZONTAL); argcnt++;
772 XtSetArg(args[argcnt], XmNpacking, XmPACK_TIGHT); argcnt++;
773 XtSetArg(args[argcnt], XmNmarginHeight, 0); argcnt++;
774 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_WIDGET); argcnt++;
775 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
776 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_NONE); argcnt++;
777 XtSetArg(args[argcnt], XmNtopWidget, replaceText); argcnt++;
778 XtSetArg(args[argcnt], XmNleftOffset, 2); argcnt++;
779 XtSetArg(args[argcnt], XmNrightOffset, 4); argcnt++;
780 searchTypeBox = XmCreateRowColumn(form, "searchTypeBox", args, argcnt);
781 XtManageChild(searchTypeBox);
782 XmAddTabGroup(searchTypeBox);
784 argcnt = 0;
785 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
786 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
787 XtSetArg(args[argcnt], XmNlabelString,
788 st1=MKSTRING("Regular Expression")); argcnt++;
789 XtSetArg(args[argcnt], XmNmnemonic, 'R'); argcnt++;
790 window->replaceRegexToggle = XmCreateToggleButton(searchTypeBox, "regExp", args, argcnt);
791 XmStringFree(st1);
792 XtManageChild(window->replaceRegexToggle);
793 XtAddCallback(window->replaceRegexToggle, XmNvalueChangedCallback, (XtCallbackProc) replaceRegExpToggleCB, window);
795 argcnt = 0;
796 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
797 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
798 XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("Case Sensitive")); argcnt++;
799 XtSetArg(args[argcnt], XmNmnemonic, 'C'); argcnt++;
800 window->replaceCaseToggle = XmCreateToggleButton(searchTypeBox, "caseSensitive", args, argcnt);
801 XmStringFree(st1);
802 XtManageChild(window->replaceCaseToggle);
803 XtAddCallback(window->replaceCaseToggle, XmNvalueChangedCallback, (XtCallbackProc) replaceCaseToggleCB, window);
805 argcnt = 0;
806 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
807 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
808 XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("Whole Word")); argcnt++;
809 XtSetArg(args[argcnt], XmNmnemonic, 'h'); argcnt++;
810 window->replaceWordToggle = XmCreateToggleButton(searchTypeBox, "wholeWord", args, argcnt);
811 XmStringFree(st1);
812 XtManageChild(window->replaceWordToggle);
814 argcnt = 0;
815 XtSetArg(args[argcnt], XmNorientation, XmHORIZONTAL); argcnt++;
816 XtSetArg(args[argcnt], XmNpacking, XmPACK_TIGHT); argcnt++;
817 XtSetArg(args[argcnt], XmNmarginHeight, 0); argcnt++;
818 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_WIDGET); argcnt++;
819 XtSetArg(args[argcnt], XmNtopOffset, 0); argcnt++;
820 XtSetArg(args[argcnt], XmNtopWidget, searchTypeBox); argcnt++;
821 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
822 XtSetArg(args[argcnt], XmNleftOffset, 2); argcnt++;
823 XtSetArg(args[argcnt], XmNradioBehavior, False); argcnt++;
824 searchDirBox = XmCreateRowColumn(form, "searchDirBox", args, argcnt);
825 XtManageChild(searchDirBox);
826 XmAddTabGroup(searchDirBox);
828 argcnt = 0;
829 XtSetArg(args[argcnt], XmNlabelString,
830 st1=MKSTRING("Search Backward")); argcnt++;
831 XtSetArg(args[argcnt], XmNmnemonic, 'B'); argcnt++;
832 reverseBtn = XmCreateToggleButton(searchDirBox, "reverse", args, argcnt);
833 XmStringFree(st1);
834 XtManageChild(reverseBtn);
836 argcnt = 0;
837 XtSetArg(args[argcnt], XmNlabelString,
838 st1=MKSTRING("Keep Dialog")); argcnt++;
839 XtSetArg(args[argcnt], XmNmnemonic, 'K'); argcnt++;
840 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_WIDGET); argcnt++;
841 XtSetArg(args[argcnt], XmNtopOffset, 0); argcnt++;
842 XtSetArg(args[argcnt], XmNtopWidget, searchTypeBox); argcnt++;
843 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_FORM); argcnt++;
844 XtSetArg(args[argcnt], XmNrightOffset, 4); argcnt++;
845 keepBtn = XmCreateToggleButton(form, "keep", args, argcnt);
846 XtAddCallback(keepBtn, XmNvalueChangedCallback,
847 (XtCallbackProc)rKeepCB, window);
848 XmStringFree(st1);
849 XtManageChild(keepBtn);
850 XmAddTabGroup(keepBtn);
852 #ifdef REPLACE_SCOPE
853 argcnt = 0;
854 XtSetArg(args[argcnt], XmNorientation, XmHORIZONTAL); argcnt++;
855 XtSetArg(args[argcnt], XmNpacking, XmPACK_TIGHT); argcnt++;
856 XtSetArg(args[argcnt], XmNmarginHeight, 0); argcnt++;
857 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
858 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_NONE); argcnt++;
859 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_WIDGET); argcnt++;
860 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
861 XtSetArg(args[argcnt], XmNtopWidget, searchDirBox); argcnt++;
862 XtSetArg(args[argcnt], XmNleftOffset, 2); argcnt++;
863 XtSetArg(args[argcnt], XmNrightOffset, 6); argcnt++;
864 XtSetArg(args[argcnt], XmNradioBehavior, True); argcnt++;
865 XtSetArg(args[argcnt], XmNradioAlwaysOne, True); argcnt++;
866 scopeForm = XmCreateRowColumn(form, "scope", args, argcnt);
867 XtManageChild(scopeForm);
868 XmAddTabGroup(scopeForm);
870 argcnt = 0;
871 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
872 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
873 XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("In Window"));
874 argcnt++;
875 XtSetArg(args[argcnt], XmNmnemonic, 'i'); argcnt++;
876 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
877 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
878 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
879 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_NONE); argcnt++;
880 inWinBtn = XmCreateToggleButton(scopeForm, "inWindow", args, argcnt);
881 XtAddCallback(inWinBtn, XmNvalueChangedCallback,
882 (XtCallbackProc)rScopeWinCB, window);
883 XmStringFree(st1);
884 XtManageChild(inWinBtn);
886 argcnt = 0;
887 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
888 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
889 XtSetArg(args[argcnt], XmNlabelString,
890 st1=MKSTRING("In Selection")); argcnt++;
891 XtSetArg(args[argcnt], XmNmnemonic, 'S'); argcnt++;
892 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
893 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
894 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_WIDGET); argcnt++;
895 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_NONE); argcnt++;
896 XtSetArg(args[argcnt], XmNleftWidget, inWinBtn); argcnt++;
897 inSelBtn = XmCreateToggleButton(scopeForm, "inSel", args, argcnt);
898 XtAddCallback(inSelBtn, XmNvalueChangedCallback,
899 (XtCallbackProc)rScopeSelCB, window);
900 XmStringFree(st1);
901 XtManageChild(inSelBtn);
903 argcnt = 0;
904 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
905 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
906 XtSetArg(args[argcnt], XmNlabelString,
907 st1=MKSTRING("In Multiple Files")); argcnt++;
908 XtSetArg(args[argcnt], XmNmnemonic, 'M'); argcnt++;
909 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
910 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
911 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_WIDGET); argcnt++;
912 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_FORM); argcnt++;
913 XtSetArg(args[argcnt], XmNleftWidget, inSelBtn); argcnt++;
914 inMultiBtn = XmCreateToggleButton(scopeForm, "multiFile", args, argcnt);
915 XtAddCallback(inMultiBtn, XmNvalueChangedCallback,
916 (XtCallbackProc)rScopeMultiCB, window);
917 XmStringFree(st1);
918 XtManageChild(inMultiBtn);
919 #else
920 argcnt = 0;
921 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_NONE); argcnt++;
922 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_FORM); argcnt++;
923 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_WIDGET); argcnt++;
924 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
925 XtSetArg(args[argcnt], XmNtopWidget, searchDirBox); argcnt++;
926 XtSetArg(args[argcnt], XmNleftOffset, 6); argcnt++;
927 XtSetArg(args[argcnt], XmNrightOffset, 6); argcnt++;
928 allForm = XmCreateForm(form, "all", args, argcnt);
929 XtManageChild(allForm);
930 XmAddTabGroup(allForm);
932 argcnt = 0;
933 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
934 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
935 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
936 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_NONE); argcnt++;
937 XtSetArg(args[argcnt], XmNleftOffset, 4); argcnt++;
938 XtSetArg(args[argcnt], XmNtopOffset, 6); argcnt++;
939 XtSetArg(args[argcnt], XmNalignment, XmALIGNMENT_BEGINNING); argcnt++;
940 XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("Replace all in:"));
941 argcnt++;
942 label3 = XmCreateLabel(allForm, "label3", args, argcnt);
943 XmStringFree(st1);
944 XtManageChild(label3);
946 argcnt = 0;
947 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
948 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
949 XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("Window"));
950 argcnt++;
951 XtSetArg(args[argcnt], XmNmnemonic, 'i'); argcnt++;
952 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
953 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
954 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_WIDGET); argcnt++;
955 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_NONE); argcnt++;
956 XtSetArg(args[argcnt], XmNleftWidget, label3); argcnt++;
957 inWinBtn = XmCreatePushButton(allForm, "inWindow", args, argcnt);
958 XtAddCallback(inWinBtn, XmNactivateCallback,
959 (XtCallbackProc)replaceAllCB, window);
960 XmStringFree(st1);
961 XtManageChild(inWinBtn);
963 argcnt = 0;
964 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
965 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
966 XtSetArg(args[argcnt], XmNlabelString,
967 st1=MKSTRING("Selection")); argcnt++;
968 XtSetArg(args[argcnt], XmNmnemonic, 'S'); argcnt++;
969 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
970 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
971 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_WIDGET); argcnt++;
972 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_NONE); argcnt++;
973 XtSetArg(args[argcnt], XmNleftWidget, inWinBtn); argcnt++;
974 inSelBtn = XmCreatePushButton(allForm, "inSel", args, argcnt);
975 XtAddCallback(inSelBtn, XmNactivateCallback,
976 (XtCallbackProc)rInSelCB, window);
977 XmStringFree(st1);
978 XtManageChild(inSelBtn);
980 argcnt = 0;
981 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
982 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
983 XtSetArg(args[argcnt], XmNlabelString,
984 st1=MKSTRING("Multiple Files...")); argcnt++;
985 XtSetArg(args[argcnt], XmNmnemonic, 'M'); argcnt++;
986 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
987 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
988 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_WIDGET); argcnt++;
989 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_FORM); argcnt++;
990 XtSetArg(args[argcnt], XmNleftWidget, inSelBtn); argcnt++;
991 inMultiBtn = XmCreatePushButton(allForm, "multiFile", args, argcnt);
992 XtAddCallback(inMultiBtn, XmNactivateCallback,
993 (XtCallbackProc)replaceMultiFileCB, window);
994 XmStringFree(st1);
995 XtManageChild(inMultiBtn);
997 #endif
999 argcnt = 0;
1000 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
1001 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_FORM); argcnt++;
1002 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_WIDGET); argcnt++;
1003 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
1004 #ifdef REPLACE_SCOPE
1005 XtSetArg(args[argcnt], XmNtopWidget, scopeForm); argcnt++;
1006 #else
1007 XtSetArg(args[argcnt], XmNtopWidget, allForm); argcnt++;
1008 #endif
1009 XtSetArg(args[argcnt], XmNleftOffset, 6); argcnt++;
1010 XtSetArg(args[argcnt], XmNrightOffset, 6); argcnt++;
1011 btnForm = XmCreateForm(form, "buttons", args, argcnt);
1012 XtManageChild(btnForm);
1013 XmAddTabGroup(btnForm);
1015 argcnt = 0;
1016 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1017 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1018 XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("Replace")); argcnt++;
1019 XtSetArg(args[argcnt], XmNshowAsDefault, (short)1); argcnt++;
1020 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
1021 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_POSITION); argcnt++;
1022 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_POSITION); argcnt++;
1023 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_FORM); argcnt++;
1024 #ifdef REPLACE_SCOPE
1025 XtSetArg(args[argcnt], XmNleftPosition, 0); argcnt++;
1026 XtSetArg(args[argcnt], XmNrightPosition, 21); argcnt++;
1027 #else
1028 XtSetArg(args[argcnt], XmNleftPosition, 0); argcnt++;
1029 XtSetArg(args[argcnt], XmNrightPosition, 25); argcnt++;
1030 #endif
1031 replaceBtn = XmCreatePushButton(btnForm, "replace", args, argcnt);
1032 XtAddCallback(replaceBtn, XmNactivateCallback, (XtCallbackProc)replaceCB, window);
1033 XmStringFree(st1);
1034 XtManageChild(replaceBtn);
1035 XtVaGetValues(replaceBtn, XmNshadowThickness, &shadowThickness, 0);
1036 defaultBtnOffset = shadowThickness + 4;
1038 argcnt = 0;
1039 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1040 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1041 XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("Find")); argcnt++;
1042 XtSetArg(args[argcnt], XmNmnemonic, 'F'); argcnt++;
1043 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
1044 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_FORM); argcnt++;
1045 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_POSITION); argcnt++;
1046 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_POSITION); argcnt++;
1047 #ifdef REPLACE_SCOPE
1048 XtSetArg(args[argcnt], XmNleftPosition, 21); argcnt++;
1049 XtSetArg(args[argcnt], XmNrightPosition, 33); argcnt++;
1050 #else
1051 XtSetArg(args[argcnt], XmNleftPosition, 25); argcnt++;
1052 XtSetArg(args[argcnt], XmNrightPosition, 42); argcnt++;
1053 #endif
1054 XtSetArg(args[argcnt], XmNtopOffset, defaultBtnOffset); argcnt++;
1055 XtSetArg(args[argcnt], XmNbottomOffset, defaultBtnOffset); argcnt++;
1056 findBtn = XmCreatePushButton(btnForm, "find", args, argcnt);
1057 XtAddCallback(findBtn, XmNactivateCallback, (XtCallbackProc)rFindCB, window);
1058 XmStringFree(st1);
1059 XtManageChild(findBtn);
1061 argcnt = 0;
1062 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1063 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1064 XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("Replace & Find")); argcnt++;
1065 XtSetArg(args[argcnt], XmNmnemonic, 'n'); argcnt++;
1066 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
1067 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_FORM); argcnt++;
1068 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_POSITION); argcnt++;
1069 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_POSITION); argcnt++;
1070 #ifdef REPLACE_SCOPE
1071 XtSetArg(args[argcnt], XmNleftPosition, 33); argcnt++;
1072 XtSetArg(args[argcnt], XmNrightPosition, 62); argcnt++;
1073 #else
1074 XtSetArg(args[argcnt], XmNleftPosition, 42); argcnt++;
1075 XtSetArg(args[argcnt], XmNrightPosition, 79); argcnt++;
1076 #endif
1077 XtSetArg(args[argcnt], XmNtopOffset, defaultBtnOffset); argcnt++;
1078 XtSetArg(args[argcnt], XmNbottomOffset, defaultBtnOffset); argcnt++;
1079 replaceFindBtn = XmCreatePushButton(btnForm, "replacefind", args, argcnt);
1080 XtAddCallback(replaceFindBtn, XmNactivateCallback, (XtCallbackProc)replaceFindCB, window);
1081 XmStringFree(st1);
1082 XtManageChild(replaceFindBtn);
1084 #ifdef REPLACE_SCOPE
1085 argcnt = 0;
1086 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1087 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1088 XtSetArg(args[argcnt], XmNlabelString,
1089 st1=MKSTRING("Replace All")); argcnt++;
1090 XtSetArg(args[argcnt], XmNmnemonic, 'A'); argcnt++;
1091 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
1092 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
1093 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_POSITION); argcnt++;
1094 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_POSITION); argcnt++;
1095 XtSetArg(args[argcnt], XmNleftPosition, 62); argcnt++;
1096 XtSetArg(args[argcnt], XmNrightPosition, 85); argcnt++;
1097 XtSetArg(args[argcnt], XmNtopOffset, defaultBtnOffset); argcnt++;
1098 replaceAllBtn = XmCreatePushButton(btnForm, "all", args, argcnt);
1099 XtAddCallback(replaceAllBtn, XmNactivateCallback,
1100 (XtCallbackProc)replaceAllScopeCB, window);
1101 XmStringFree(st1);
1102 XtManageChild(replaceAllBtn);
1103 #endif
1105 argcnt = 0;
1106 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1107 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1108 XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("Cancel")); argcnt++;
1109 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
1110 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_FORM); argcnt++;
1111 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_POSITION); argcnt++;
1112 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_POSITION); argcnt++;
1113 #ifdef REPLACE_SCOPE
1114 XtSetArg(args[argcnt], XmNleftPosition, 85); argcnt++;
1115 XtSetArg(args[argcnt], XmNrightPosition, 100); argcnt++;
1116 #else
1117 XtSetArg(args[argcnt], XmNleftPosition, 79); argcnt++;
1118 XtSetArg(args[argcnt], XmNrightPosition, 100); argcnt++;
1119 #endif
1120 XtSetArg(args[argcnt], XmNtopOffset, defaultBtnOffset); argcnt++;
1121 XtSetArg(args[argcnt], XmNbottomOffset, defaultBtnOffset); argcnt++;
1122 cancelBtn = XmCreatePushButton(btnForm, "cancel", args, argcnt);
1123 XmStringFree(st1);
1124 XtAddCallback(cancelBtn, XmNactivateCallback, (XtCallbackProc)rCancelCB,
1125 window);
1126 XtManageChild(cancelBtn);
1128 XtVaSetValues(form, XmNcancelButton, cancelBtn, NULL);
1129 AddDialogMnemonicHandler(form, FALSE);
1131 window->replaceDlog = form;
1132 window->replaceText = findText;
1133 window->replaceWithText = replaceText;
1134 window->replaceRevToggle = reverseBtn;
1135 window->replaceKeepBtn = keepBtn;
1136 window->replaceBtns = btnForm;
1137 window->replaceBtn = replaceBtn;
1138 window->replaceAndFindBtn = replaceFindBtn;
1139 window->replaceFindBtn = findBtn;
1140 window->replaceSearchTypeBox = searchTypeBox;
1141 #ifdef REPLACE_SCOPE
1142 window->replaceAllBtn = replaceAllBtn;
1143 window->replaceScopeWinToggle = inWinBtn;
1144 window->replaceScopeSelToggle = inSelBtn;
1145 window->replaceScopeMultiToggle = inMultiBtn;
1146 #else
1147 window->replaceInWinBtn = inWinBtn;
1148 window->replaceAllBtn = inMultiBtn;
1149 window->replaceInSelBtn = inSelBtn;
1150 #endif
1153 static void createFindDlog(Widget parent, WindowInfo *window)
1155 Arg args[50];
1156 int argcnt, defaultBtnOffset;
1157 XmString st1;
1158 Widget form, btnForm, searchTypeBox;
1159 Widget findText, label1, label2, cancelBtn, findBtn;
1160 Widget searchDirBox, reverseBtn, keepBtn;
1161 char title[MAXPATHLEN + 11];
1162 Dimension shadowThickness;
1164 argcnt = 0;
1165 XtSetArg(args[argcnt], XmNautoUnmanage, False); argcnt++;
1166 form = CreateFormDialog(parent, "findDialog", args, argcnt);
1167 XtVaSetValues(form, XmNshadowThickness, 0, NULL);
1168 if (GetPrefKeepSearchDlogs()) {
1169 sprintf(title, "Find (in %s)", window->filename);
1170 XtVaSetValues(XtParent(form), XmNtitle, title, NULL);
1171 } else
1172 XtVaSetValues(XtParent(form), XmNtitle, "Find", NULL);
1174 argcnt = 0;
1175 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
1176 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
1177 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
1178 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_NONE); argcnt++;
1179 XtSetArg(args[argcnt], XmNleftOffset, 6); argcnt++;
1180 XtSetArg(args[argcnt], XmNtopOffset, 6); argcnt++;
1181 XtSetArg(args[argcnt], XmNalignment, XmALIGNMENT_BEGINNING); argcnt++;
1182 XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("String to Find:"));
1183 argcnt++;
1184 XtSetArg(args[argcnt], XmNmnemonic, 'S'); argcnt++;
1185 label1 = XmCreateLabel(form, "label1", args, argcnt);
1186 XmStringFree(st1);
1187 XtManageChild(label1);
1189 argcnt = 0;
1190 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
1191 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
1192 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_NONE); argcnt++;
1193 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_FORM); argcnt++;
1194 XtSetArg(args[argcnt], XmNrightOffset, 6); argcnt++;
1195 XtSetArg(args[argcnt], XmNtopOffset, 6); argcnt++;
1196 XtSetArg(args[argcnt], XmNalignment, XmALIGNMENT_END); argcnt++;
1197 XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING(
1198 "(use up arrow key to recall previous)")); argcnt++;
1199 label2 = XmCreateLabel(form, "label2", args, argcnt);
1200 XmStringFree(st1);
1201 XtManageChild(label2);
1203 argcnt = 0;
1204 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1205 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1206 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_WIDGET); argcnt++;
1207 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
1208 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
1209 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_FORM); argcnt++;
1210 XtSetArg(args[argcnt], XmNtopWidget, label1); argcnt++;
1211 XtSetArg(args[argcnt], XmNleftOffset, 6); argcnt++;
1212 XtSetArg(args[argcnt], XmNrightOffset, 6); argcnt++;
1213 XtSetArg(args[argcnt], XmNmaxLength, SEARCHMAX); argcnt++;
1214 findText = XmCreateText(form, "searchString", args, argcnt);
1215 XtAddCallback(findText, XmNfocusCallback, (XtCallbackProc)fFocusCB, window);
1216 XtAddCallback(findText, XmNvalueChangedCallback,
1217 (XtCallbackProc)findTextValueChangedCB, window);
1218 XtAddEventHandler(findText, KeyPressMask, False,
1219 (XtEventHandler)findArrowKeyCB, window);
1220 RemapDeleteKey(findText);
1221 XtManageChild(findText);
1222 XmAddTabGroup(findText);
1223 XtVaSetValues(label1, XmNuserData, findText, NULL); /* mnemonic processing */
1225 argcnt = 0;
1226 XtSetArg(args[argcnt], XmNorientation, XmHORIZONTAL); argcnt++;
1227 XtSetArg(args[argcnt], XmNpacking, XmPACK_TIGHT); argcnt++;
1228 XtSetArg(args[argcnt], XmNmarginHeight, 0); argcnt++;
1229 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_WIDGET); argcnt++;
1230 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
1231 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_NONE); argcnt++;
1232 XtSetArg(args[argcnt], XmNtopWidget, findText); argcnt++;
1233 XtSetArg(args[argcnt], XmNleftOffset, 2); argcnt++;
1234 XtSetArg(args[argcnt], XmNrightOffset, 4); argcnt++;
1236 searchTypeBox = XmCreateRowColumn(form, "searchTypeBox", args, argcnt);
1237 XtManageChild(searchTypeBox);
1238 XmAddTabGroup(searchTypeBox);
1240 argcnt = 0;
1241 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1242 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1243 XtSetArg(args[argcnt], XmNlabelString,
1244 st1=MKSTRING("Regular Expression")); argcnt++;
1245 XtSetArg(args[argcnt], XmNmnemonic, 'R'); argcnt++;
1246 window->findRegexToggle = XmCreateToggleButton(searchTypeBox, "regExp", args, argcnt);
1247 XmStringFree(st1);
1248 XtManageChild(window->findRegexToggle);
1249 XtAddCallback(window->findRegexToggle, XmNvalueChangedCallback, (XtCallbackProc) findRegExpToggleCB, window);
1251 argcnt = 0;
1252 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1253 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1254 XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("Case Sensitive")); argcnt++;
1255 XtSetArg(args[argcnt], XmNmnemonic, 'C'); argcnt++;
1256 window->findCaseToggle = XmCreateToggleButton(searchTypeBox, "caseSensitive", args, argcnt);
1257 XmStringFree(st1);
1258 XtManageChild(window->findCaseToggle);
1259 XtAddCallback(window->findCaseToggle, XmNvalueChangedCallback, (XtCallbackProc) findCaseToggleCB, window);
1261 argcnt = 0;
1262 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1263 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1264 XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("Whole Word")); argcnt++;
1265 XtSetArg(args[argcnt], XmNmnemonic, 'h'); argcnt++;
1266 window->findWordToggle = XmCreateToggleButton(searchTypeBox, "wholeWord", args, argcnt);
1267 XmStringFree(st1);
1268 XtManageChild(window->findWordToggle);
1270 argcnt = 0;
1271 XtSetArg(args[argcnt], XmNorientation, XmHORIZONTAL); argcnt++;
1272 XtSetArg(args[argcnt], XmNpacking, XmPACK_TIGHT); argcnt++;
1273 XtSetArg(args[argcnt], XmNmarginHeight, 0); argcnt++;
1274 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_WIDGET); argcnt++;
1275 XtSetArg(args[argcnt], XmNtopOffset, 0); argcnt++;
1276 XtSetArg(args[argcnt], XmNtopWidget, searchTypeBox); argcnt++;
1277 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
1278 XtSetArg(args[argcnt], XmNleftOffset, 2); argcnt++;
1279 XtSetArg(args[argcnt], XmNradioBehavior, False); argcnt++;
1280 searchDirBox = XmCreateRowColumn(form, "searchDirBox", args, argcnt);
1281 XtManageChild(searchDirBox);
1282 XmAddTabGroup(searchDirBox);
1284 argcnt = 0;
1285 XtSetArg(args[argcnt], XmNlabelString,
1286 st1=MKSTRING("Search Backward")); argcnt++;
1287 XtSetArg(args[argcnt], XmNmnemonic, 'B'); argcnt++;
1288 reverseBtn = XmCreateToggleButton(searchDirBox, "reverse", args, argcnt);
1289 XmStringFree(st1);
1290 XtManageChild(reverseBtn);
1292 argcnt = 0;
1293 XtSetArg(args[argcnt], XmNlabelString,
1294 st1=MKSTRING("Keep Dialog")); argcnt++;
1295 XtSetArg(args[argcnt], XmNmnemonic, 'K'); argcnt++;
1296 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_WIDGET); argcnt++;
1297 XtSetArg(args[argcnt], XmNtopOffset, 0); argcnt++;
1298 XtSetArg(args[argcnt], XmNtopWidget, searchTypeBox); argcnt++;
1299 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_FORM); argcnt++;
1300 XtSetArg(args[argcnt], XmNrightOffset, 4); argcnt++;
1301 keepBtn = XmCreateToggleButton(form, "keep", args, argcnt);
1302 XtAddCallback(keepBtn, XmNvalueChangedCallback,
1303 (XtCallbackProc)fKeepCB, window);
1304 XmStringFree(st1);
1305 XtManageChild(keepBtn);
1306 XmAddTabGroup(keepBtn);
1308 argcnt = 0;
1309 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
1310 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_FORM); argcnt++;
1311 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_WIDGET); argcnt++;
1312 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
1313 XtSetArg(args[argcnt], XmNtopWidget, searchDirBox); argcnt++;
1314 XtSetArg(args[argcnt], XmNleftOffset, 2); argcnt++;
1315 XtSetArg(args[argcnt], XmNrightOffset, 4); argcnt++;
1316 btnForm = XmCreateForm(form, "buttons", args, argcnt);
1317 XtManageChild(btnForm);
1318 XmAddTabGroup(btnForm);
1320 argcnt = 0;
1321 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1322 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1323 XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("Find")); argcnt++;
1324 XtSetArg(args[argcnt], XmNshowAsDefault, (short)1); argcnt++;
1325 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
1326 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_POSITION); argcnt++;
1327 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_NONE); argcnt++;
1328 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
1329 XtSetArg(args[argcnt], XmNleftPosition, 20); argcnt++;
1330 XtSetArg(args[argcnt], XmNbottomOffset, 6); argcnt++;
1331 findBtn = XmCreatePushButton(btnForm, "find", args, argcnt);
1332 XtAddCallback(findBtn, XmNactivateCallback, (XtCallbackProc)findCB, window);
1333 XmStringFree(st1);
1334 XtManageChild(findBtn);
1335 XtVaGetValues(findBtn, XmNshadowThickness, &shadowThickness, NULL);
1336 defaultBtnOffset = shadowThickness + 4;
1338 argcnt = 0;
1339 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1340 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1341 XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("Cancel")); argcnt++;
1342 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
1343 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
1344 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_NONE); argcnt++;
1345 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_POSITION); argcnt++;
1346 XtSetArg(args[argcnt], XmNrightPosition, 80); argcnt++;
1347 XtSetArg(args[argcnt], XmNtopOffset, defaultBtnOffset); argcnt++;
1348 cancelBtn = XmCreatePushButton(btnForm, "cancel", args, argcnt);
1349 XtAddCallback(cancelBtn, XmNactivateCallback, (XtCallbackProc)fCancelCB,
1350 window);
1351 XmStringFree(st1);
1352 XtManageChild(cancelBtn);
1353 XtVaSetValues(form, XmNcancelButton, cancelBtn, NULL);
1354 AddDialogMnemonicHandler(form, FALSE);
1356 window->findDlog = form;
1357 window->findText = findText;
1358 window->findRevToggle = reverseBtn;
1359 window->findKeepBtn = keepBtn;
1360 window->findBtns = btnForm;
1361 window->findBtn = findBtn;
1362 window->findSearchTypeBox = searchTypeBox;
1365 static void createReplaceMultiFileDlog(WindowInfo *window)
1367 Arg args[50];
1368 int argcnt, defaultBtnOffset;
1369 XmString st1;
1370 Widget list, label1, form, pathBtn;
1371 Widget btnForm, replaceBtn, selectBtn, deselectBtn, cancelBtn;
1372 Dimension shadowThickness;
1374 argcnt = 0;
1375 XtSetArg(args[argcnt], XmNautoUnmanage, False); argcnt++;
1376 XtSetArg (args[argcnt], XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL);
1377 argcnt ++;
1379 /* Ideally, we should create the multi-file dialog as a child widget
1380 of the replace dialog. However, if we do this, the main window
1381 can hide the multi-file dialog when raised (I'm not sure why, but
1382 it's something that I observed with fvwm). By using the main window
1383 as the parent, it is possible that the replace dialog _partially_
1384 covers the multi-file dialog, but this much better than the multi-file
1385 dialog being covered completely by the main window */
1386 form = CreateFormDialog(window->shell, "replaceMultiFileDialog",
1387 args, argcnt);
1388 XtVaSetValues(form, XmNshadowThickness, 0, NULL);
1389 XtVaSetValues(XtParent(form), XmNtitle, "Replace All in Multiple Files",
1390 NULL);
1392 /* Label at top left. */
1393 argcnt = 0;
1394 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
1395 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
1396 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
1397 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_NONE); argcnt++;
1398 /* Offset = 6 + (highlightThickness + detailShadowThickness) of the
1399 toggle button (see below). Unfortunately, detailShadowThickness is
1400 a Motif 2.x property, so we can't measure it. The default is 2 pixels.
1401 To make things even more complicated, the SunOS 5.6 / Solaris 2.6
1402 version of Motif 1.2 seems to use a detailShadowThickness of 0 ...
1403 So we'll have to live with a slight misalignment on that platform
1404 (those Motif libs are known to have many other problems). */
1405 XtSetArg(args[argcnt], XmNtopOffset, 10); argcnt++;
1406 XtSetArg(args[argcnt], XmNleftOffset, 6); argcnt++;
1407 XtSetArg(args[argcnt], XmNalignment, XmALIGNMENT_BEGINNING); argcnt++;
1408 XtSetArg(args[argcnt], XmNlabelString,
1409 st1=MKSTRING("Files in which to Replace All:")); argcnt++;
1410 XtSetArg(args[argcnt], XmNmnemonic, 'F'); argcnt++;
1411 label1 = XmCreateLabel(form, "label1", args, argcnt);
1412 XmStringFree(st1);
1413 XtManageChild(label1);
1415 /* Pathname toggle button at top right (always unset by default) */
1416 argcnt = 0;
1417 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1418 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1419 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_NONE); argcnt++;
1420 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_FORM); argcnt++;
1421 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
1422 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
1423 XtSetArg(args[argcnt], XmNset, False); argcnt++;
1424 XtSetArg(args[argcnt], XmNrightOffset, 6); argcnt++;
1425 XtSetArg(args[argcnt], XmNtopOffset, 6); argcnt++;
1426 XtSetArg(args[argcnt], XmNalignment, XmALIGNMENT_END); argcnt++;
1427 XtSetArg(args[argcnt], XmNlabelString,
1428 st1=MKSTRING("Show Path Names")); argcnt++;
1429 XtSetArg(args[argcnt], XmNmnemonic, 'P'); argcnt++;
1430 pathBtn = XmCreateToggleButton(form, "path", args, argcnt);
1431 XmStringFree(st1);
1432 XtAddCallback(pathBtn, XmNvalueChangedCallback,
1433 (XtCallbackProc)rMultiFilePathCB, window);
1434 XtManageChild(pathBtn);
1437 * Buttons at bottom. Place them before the list, such that we can
1438 * attach the list to the label and the button box. In that way only
1439 * the lists resizes vertically when the dialog is resized; users expect
1440 * the list to resize, not the buttons.
1443 argcnt = 0;
1444 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
1445 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_FORM); argcnt++;
1446 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_NONE); argcnt++;
1447 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_FORM); argcnt++;
1448 XtSetArg(args[argcnt], XmNleftOffset, 6); argcnt++;
1449 XtSetArg(args[argcnt], XmNrightOffset, 6); argcnt++;
1450 XtSetArg(args[argcnt], XmNtopOffset, 6); argcnt++;
1451 XtSetArg(args[argcnt], XmNresizable, (short)0); argcnt++;
1452 btnForm = XmCreateForm(form, "buttons", args, argcnt);
1453 XtManageChild(btnForm);
1455 /* Replace */
1456 argcnt = 0;
1457 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1458 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1459 XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("Replace")); argcnt++;
1460 XtSetArg(args[argcnt], XmNshowAsDefault, (short)1); argcnt++;
1461 XtSetArg(args[argcnt], XmNmnemonic, 'R'); argcnt++;
1462 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
1463 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
1464 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_POSITION); argcnt++;
1465 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_POSITION); argcnt++;
1466 XtSetArg(args[argcnt], XmNleftPosition, 0); argcnt++;
1467 XtSetArg(args[argcnt], XmNrightPosition, 25); argcnt++;
1468 replaceBtn = XmCreatePushButton(btnForm, "replace", args, argcnt);
1469 XmStringFree(st1);
1470 XtAddCallback(replaceBtn, XmNactivateCallback,
1471 (XtCallbackProc)rMultiFileReplaceCB, window);
1473 * _DON'T_ set the replace button as default (as in other dialogs).
1474 * Multi-selection lists have the nasty property of selecting the
1475 * current item when <enter> is pressed.
1476 * In that way, the user could inadvertently select an additional file
1477 * (most likely the last one that was deselected).
1478 * The user has to activate the replace button explictly (either with
1479 * a mouse click or with the shortcut key).
1481 * XtVaSetValues(form, XmNdefaultButton, replaceBtn, NULL); */
1483 XtManageChild(replaceBtn);
1484 XtVaGetValues(replaceBtn, XmNshadowThickness, &shadowThickness, NULL);
1485 defaultBtnOffset = shadowThickness + 4;
1487 /* Select All */
1488 argcnt = 0;
1489 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1490 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1491 XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("Select All"));
1492 argcnt++;
1493 XtSetArg(args[argcnt], XmNmnemonic, 'S'); argcnt++;
1494 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
1495 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
1496 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_POSITION); argcnt++;
1497 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_POSITION); argcnt++;
1498 XtSetArg(args[argcnt], XmNleftPosition, 25); argcnt++;
1499 XtSetArg(args[argcnt], XmNrightPosition, 50); argcnt++;
1500 XtSetArg(args[argcnt], XmNtopOffset, defaultBtnOffset); argcnt++;
1501 selectBtn = XmCreatePushButton(btnForm, "select", args, argcnt);
1502 XmStringFree(st1);
1503 XtAddCallback(selectBtn, XmNactivateCallback,
1504 (XtCallbackProc)rMultiFileSelectAllCB, window);
1505 XtManageChild(selectBtn);
1507 /* Deselect All */
1508 argcnt = 0;
1509 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1510 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1511 XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("Deselect All"));
1512 argcnt++;
1513 XtSetArg(args[argcnt], XmNmnemonic, 'D'); argcnt++;
1514 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
1515 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
1516 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_POSITION); argcnt++;
1517 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_POSITION); argcnt++;
1518 XtSetArg(args[argcnt], XmNleftPosition, 50); argcnt++;
1519 XtSetArg(args[argcnt], XmNrightPosition, 75); argcnt++;
1520 XtSetArg(args[argcnt], XmNtopOffset, defaultBtnOffset); argcnt++;
1521 deselectBtn = XmCreatePushButton(btnForm, "deselect", args, argcnt);
1522 XmStringFree(st1);
1523 XtAddCallback(deselectBtn, XmNactivateCallback,
1524 (XtCallbackProc)rMultiFileDeselectAllCB, window);
1525 XtManageChild(deselectBtn);
1527 /* Cancel */
1528 argcnt = 0;
1529 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1530 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1531 XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("Cancel")); argcnt++;
1532 XtSetArg(args[argcnt], XmNmnemonic, 'C'); argcnt++;
1533 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
1534 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
1535 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_POSITION); argcnt++;
1536 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_POSITION); argcnt++;
1537 XtSetArg(args[argcnt], XmNleftPosition, 75); argcnt++;
1538 XtSetArg(args[argcnt], XmNrightPosition, 100); argcnt++;
1539 XtSetArg(args[argcnt], XmNtopOffset, defaultBtnOffset); argcnt++;
1540 cancelBtn = XmCreatePushButton(btnForm, "cancel", args, argcnt);
1541 XmStringFree(st1);
1542 XtAddCallback(cancelBtn, XmNactivateCallback,
1543 (XtCallbackProc)rMultiFileCancelCB, window);
1544 XtManageChild(cancelBtn);
1546 /* The list of files */
1547 argcnt = 0;
1548 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1549 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_WIDGET); argcnt++;
1550 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_WIDGET); argcnt++;
1551 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
1552 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_FORM); argcnt++;
1553 XtSetArg(args[argcnt], XmNbottomWidget, btnForm); argcnt++;
1554 XtSetArg(args[argcnt], XmNtopWidget, label1); argcnt++;
1555 XtSetArg(args[argcnt], XmNleftOffset, 10); argcnt++;
1556 XtSetArg(args[argcnt], XmNvisibleItemCount, 10); argcnt++;
1557 XtSetArg(args[argcnt], XmNtopOffset, 6); argcnt++;
1558 XtSetArg(args[argcnt], XmNbottomOffset, 6); argcnt++;
1559 XtSetArg(args[argcnt], XmNrightOffset, 10); argcnt++;
1560 /* An alternative is to use the EXTENDED_SELECT, but that one
1561 is less suited for keyboard manipulation (moving the selection cursor
1562 with the keyboard deselects everything). */
1563 XtSetArg(args[argcnt], XmNselectionPolicy, XmMULTIPLE_SELECT); argcnt++;
1564 list = XmCreateScrolledList(form, "list_of_files", args, argcnt);
1565 AddMouseWheelSupport(list);
1566 XtManageChild(list);
1568 /* Traverse: list -> buttons -> path name toggle button */
1569 XmAddTabGroup(list);
1570 XmAddTabGroup(btnForm);
1571 XmAddTabGroup(pathBtn);
1573 XtVaSetValues(label1, XmNuserData, list, NULL); /* mnemonic processing */
1575 /* Cancel/Mnemonic stuff. */
1576 XtVaSetValues(form, XmNcancelButton, cancelBtn, NULL);
1577 AddDialogMnemonicHandler(form, FALSE);
1579 window->replaceMultiFileDlog = form;
1580 window->replaceMultiFileList = list;
1581 window->replaceMultiFilePathBtn = pathBtn;
1583 /* Install a handler that frees the list of writable windows when
1584 the dialog is unmapped. */
1585 XtAddCallback(form, XmNunmapCallback,
1586 (XtCallbackProc)freeWritableWindowsCB, window);
1590 ** Iterates through the list of writable windows of a window, and removes
1591 ** the doomed window if necessary.
1593 static void checkMultiReplaceListForDoomedW(WindowInfo* window,
1594 WindowInfo* doomedWindow)
1596 WindowInfo *w;
1597 int i;
1599 /* If the window owning the list and the doomed window are one and the
1600 same, we just close the multi-file replacement dialog. */
1601 if (window == doomedWindow) {
1602 XtUnmanageChild(window->replaceMultiFileDlog);
1603 return;
1606 /* Check whether the doomed window is currently listed */
1607 for (i = 0; i < window->nWritableWindows; ++i) {
1608 w = window->writableWindows[i];
1609 if (w == doomedWindow) {
1610 removeDoomedWindowFromList(window, i);
1611 break;
1617 ** Removes a window that is about to be closed from the list of files in
1618 ** which to replace. If the list becomes empty, the dialog is popped down.
1620 static void removeDoomedWindowFromList(WindowInfo* window, int index)
1622 int entriesToMove;
1624 /* If the list would become empty, we remove the dialog */
1625 if (window->nWritableWindows <= 1) {
1626 XtUnmanageChild(window->replaceMultiFileDlog);
1627 return;
1630 entriesToMove = window->nWritableWindows - index - 1;
1631 memmove(&(window->writableWindows[index]),
1632 &(window->writableWindows[index+1]),
1633 (size_t)(entriesToMove*sizeof(WindowInfo*)));
1634 window->nWritableWindows -= 1;
1636 XmListDeletePos(window->replaceMultiFileList, index + 1);
1640 ** These callbacks fix a Motif 1.1 problem that the default button gets the
1641 ** keyboard focus when a dialog is created. We want the first text field
1642 ** to get the focus, so we don't set the default button until the text field
1643 ** has the focus for sure. I have tried many other ways and this is by far
1644 ** the least nasty.
1646 static void fFocusCB(Widget w, WindowInfo *window, caddr_t *callData)
1648 SET_ONE_RSRC(window->findDlog, XmNdefaultButton, window->findBtn);
1650 static void rFocusCB(Widget w, WindowInfo *window, caddr_t *callData)
1652 SET_ONE_RSRC(window->replaceDlog, XmNdefaultButton, window->replaceBtn);
1655 /* when keeping a window up, clue the user what window it's associated with */
1656 static void rKeepCB(Widget w, WindowInfo *window, caddr_t *callData)
1658 char title[MAXPATHLEN + 19];
1660 if (XmToggleButtonGetState(w)) {
1661 sprintf(title, "Replace/Find (in %s)", window->filename);
1662 XtVaSetValues(XtParent(window->replaceDlog), XmNtitle, title, NULL);
1663 } else
1664 XtVaSetValues(XtParent(window->replaceDlog), XmNtitle, "Replace/Find", NULL);
1666 static void fKeepCB(Widget w, WindowInfo *window, caddr_t *callData)
1668 char title[MAXPATHLEN + 11];
1670 if (XmToggleButtonGetState(w)) {
1671 sprintf(title, "Find (in %s)", window->filename);
1672 XtVaSetValues(XtParent(window->findDlog), XmNtitle, title, NULL);
1673 } else
1674 XtVaSetValues(XtParent(window->findDlog), XmNtitle, "Find", NULL);
1677 static void replaceCB(Widget w, WindowInfo *window,
1678 XmAnyCallbackStruct *callData)
1680 char searchString[SEARCHMAX], replaceString[SEARCHMAX];
1681 int direction, searchType;
1682 char *params[5];
1684 /* Validate and fetch the find and replace strings from the dialog */
1685 if (!getReplaceDlogInfo(window, &direction, searchString, replaceString,
1686 &searchType))
1687 return;
1689 /* Set the initial focus of the dialog back to the search string */
1690 resetReplaceTabGroup(window);
1692 /* Find the text and replace it */
1693 params[0] = searchString;
1694 params[1] = replaceString;
1695 params[2] = directionArg(direction);
1696 params[3] = searchTypeArg(searchType);
1697 params[4] = searchWrapArg(GetPrefSearchWraps());
1698 XtCallActionProc(window->lastFocus, "replace", callData->event, params, 5);
1700 /* Pop down the dialog */
1701 if (!XmToggleButtonGetState(window->replaceKeepBtn))
1702 unmanageReplaceDialogs(window);
1705 static void replaceAllCB(Widget w, WindowInfo *window,
1706 XmAnyCallbackStruct *callData)
1708 char searchString[SEARCHMAX], replaceString[SEARCHMAX];
1709 int direction, searchType;
1710 char *params[3];
1712 /* Validate and fetch the find and replace strings from the dialog */
1713 if (!getReplaceDlogInfo(window, &direction, searchString, replaceString,
1714 &searchType))
1715 return;
1717 /* Set the initial focus of the dialog back to the search string */
1718 resetReplaceTabGroup(window);
1720 /* do replacement */
1721 params[0] = searchString;
1722 params[1] = replaceString;
1723 params[2] = searchTypeArg(searchType);
1724 XtCallActionProc(window->lastFocus, "replace_all", callData->event,
1725 params, 3);
1727 /* pop down the dialog */
1728 if (!XmToggleButtonGetState(window->replaceKeepBtn))
1729 unmanageReplaceDialogs(window);
1732 static void replaceMultiFileCB(Widget w, WindowInfo *window,
1733 XmAnyCallbackStruct *callData)
1735 DoReplaceMultiFileDlog(window);
1739 ** Callback that frees the list of windows the multi-file replace
1740 ** dialog is unmapped.
1742 static void freeWritableWindowsCB(Widget* w, WindowInfo* window,
1743 XmAnyCallbackStruct *callData)
1745 XtFree((XtPointer)window->writableWindows);
1746 window->writableWindows = NULL;
1747 window->nWritableWindows = 0;
1751 ** Comparison function for sorting windows by title for the window menu
1753 static int compareWindowNames(const void *windowA, const void *windowB)
1755 return strcmp((*((WindowInfo**)windowA))->filename,
1756 (*((WindowInfo**)windowB))->filename);
1760 ** Count no. of windows
1762 static int countWindows(void)
1764 int nWindows;
1765 const WindowInfo *w;
1767 for (w=WindowList, nWindows=0; w!=NULL; w=w->next, ++nWindows);
1769 return nWindows;
1773 ** Count no. of writable windows, but first update the status of all files.
1775 static int countWritableWindows(void)
1777 int nWritable, nBefore, nAfter;
1778 WindowInfo *w;
1780 nBefore = countWindows();
1781 for (w=WindowList, nWritable=0; w!=NULL; w=w->next) {
1782 /* We must be very careful! The status check may trigger a pop-up
1783 dialog when the file has changed on disk, and the user may destroy
1784 arbitrary windows in response. */
1785 CheckForChangesToFile(w);
1786 nAfter = countWindows();
1787 if (nAfter != nBefore) {
1788 /* The user has destroyed a file; start counting all over again */
1789 nBefore = nAfter;
1790 w = WindowList;
1791 nWritable = 0;
1792 continue;
1794 if (!IS_ANY_LOCKED(w->lockReasons)) ++nWritable;
1796 return nWritable;
1800 ** Collects a list of writable windows (sorted by file name).
1801 ** The previous list, if any is freed first.
1803 static void collectWritableWindows(WindowInfo* window)
1805 int nWritable = countWritableWindows();
1806 int i;
1807 WindowInfo *w;
1808 WindowInfo **windows;
1810 if (window->writableWindows)
1812 XtFree((XtPointer)window->writableWindows);
1815 /* Make a sorted list of writable windows */
1816 windows = (WindowInfo **)XtMalloc(sizeof(WindowInfo *) * nWritable);
1817 for (w=WindowList, i=0; w!=NULL; w=w->next)
1818 if (!IS_ANY_LOCKED(w->lockReasons)) windows[i++] = w;
1819 qsort(windows, nWritable, sizeof(WindowInfo *), compareWindowNames);
1821 window->writableWindows = windows;
1822 window->nWritableWindows = nWritable;
1825 static void rMultiFileReplaceCB(Widget w, WindowInfo *window,
1826 XmAnyCallbackStruct *callData)
1828 char searchString[SEARCHMAX], replaceString[SEARCHMAX];
1829 int direction, searchType;
1830 char *params[4];
1831 int nSelected, i;
1832 WindowInfo *writableWin;
1833 Bool replaceFailed, noWritableLeft;
1835 nSelected = 0;
1836 for (i=0; i<window->nWritableWindows; ++i)
1837 if (XmListPosSelected(window->replaceMultiFileList, i+1))
1838 ++nSelected;
1840 if (!nSelected)
1842 DialogF(DF_INF, XtParent(window->replaceMultiFileDlog), 1, "No Files",
1843 "No files selected!", "OK");
1844 return; /* Give the user another chance */
1847 /* Set the initial focus of the dialog back to the search string */
1848 resetReplaceTabGroup(window);
1851 * Protect the user against him/herself; Maybe this is a bit too much?
1853 if (DialogF(DF_QUES, window->shell, 2, "Multi-File Replacement",
1854 "Multi-file replacements are difficult to undo.\n"
1855 "Proceed with the replacement ?", "Yes", "Cancel") != 1)
1857 /* pop down the multi-file dialog only */
1858 XtUnmanageChild(window->replaceMultiFileDlog);
1860 return;
1863 /* Fetch the find and replace strings from the dialog;
1864 they should have been validated already, but since Lesstif may not
1865 honor modal dialogs, it is possible that the user modified the
1866 strings again, so we should verify them again too. */
1867 if (!getReplaceDlogInfo(window, &direction, searchString, replaceString,
1868 &searchType))
1869 return;
1871 /* Set the initial focus of the dialog back to the search string */
1872 resetReplaceTabGroup(window);
1874 params[0] = searchString;
1875 params[1] = replaceString;
1876 params[2] = searchTypeArg(searchType);
1878 replaceFailed = True;
1879 noWritableLeft = True;
1880 /* Perform the replacements and mark the selected files (history) */
1881 for (i=0; i<window->nWritableWindows; ++i) {
1882 writableWin = window->writableWindows[i];
1883 if (XmListPosSelected(window->replaceMultiFileList, i+1)) {
1884 /* First check again whether the file is still writable. If the
1885 file status has changed or the file was locked in the mean time
1886 (possible due to Lesstif modal dialog bug), we just skip the
1887 window. */
1888 if (!IS_ANY_LOCKED(writableWin->lockReasons)) {
1889 noWritableLeft = False;
1890 writableWin->multiFileReplSelected = True;
1891 writableWin->multiFileBusy = True; /* Avoid multi-beep/dialog */
1892 writableWin->replaceFailed = False;
1893 XtCallActionProc(writableWin->lastFocus, "replace_all",
1894 callData->event, params, 3);
1895 writableWin->multiFileBusy = False;
1896 if (!writableWin->replaceFailed) replaceFailed = False;
1898 } else {
1899 writableWin->multiFileReplSelected = False;
1903 if (!XmToggleButtonGetState(window->replaceKeepBtn)) {
1904 /* Pop down both replace dialogs. */
1905 unmanageReplaceDialogs(window);
1906 } else {
1907 /* pow down only the file selection dialog */
1908 XtUnmanageChild(window->replaceMultiFileDlog);
1911 /* We suppressed multiple beeps/dialogs. If there wasn't any file in
1912 which the replacement succeeded, we should still warn the user */
1913 if (replaceFailed) {
1914 if (GetPrefSearchDlogs()) {
1915 if (noWritableLeft) {
1916 DialogF(DF_INF, window->shell, 1, "Read-only Files",
1917 "All selected files have become read-only.", "OK");
1918 } else {
1919 DialogF(DF_INF, window->shell, 1, "String not found",
1920 "String was not found", "OK");
1922 } else {
1923 XBell(TheDisplay, 0);
1928 static void rMultiFileCancelCB(Widget w, WindowInfo *window, caddr_t callData)
1930 /* Set the initial focus of the dialog back to the search string */
1931 resetReplaceTabGroup(window);
1933 /* pop down the multi-window replace dialog */
1934 XtUnmanageChild(window->replaceMultiFileDlog);
1937 static void rMultiFileSelectAllCB(Widget w, WindowInfo *window,
1938 XmAnyCallbackStruct *callData)
1940 int i;
1941 char policy;
1942 Widget list = window->replaceMultiFileList;
1945 * If the list is in extended selection mode, we can't select more
1946 * than one item (probably because XmListSelectPos is equivalent
1947 * to a button1 click; I don't think that there is an equivalent
1948 * for CTRL-button1). Therefore, we temporarily put the list into
1949 * multiple selection mode.
1950 * Note: this is not really necessary if the list is in multiple select
1951 * mode all the time (as it currently is).
1953 XtVaGetValues(list, XmNselectionPolicy, &policy, NULL);
1954 XtVaSetValues(list, XmNselectionPolicy, XmMULTIPLE_SELECT, NULL);
1956 /* Is there no other way (like "select all") ? */
1957 XmListDeselectAllItems(window->replaceMultiFileList); /* select toggles */
1959 for (i=0; i<window->nWritableWindows; ++i) {
1960 XmListSelectPos(list, i+1, FALSE);
1963 /* Restore the original policy. */
1964 XtVaSetValues(list, XmNselectionPolicy, policy, NULL);
1967 static void rMultiFileDeselectAllCB(Widget w, WindowInfo *window,
1968 XmAnyCallbackStruct *callData)
1970 XmListDeselectAllItems(window->replaceMultiFileList);
1973 static void rMultiFilePathCB(Widget w, WindowInfo *window,
1974 XmAnyCallbackStruct *callData)
1976 uploadFileListItems(window, True); /* Replace */
1980 * Uploads the file items to the multi-file replament dialog list.
1981 * A boolean argument indicates whether the elements currently in the
1982 * list have to be replaced or not.
1983 * Depending on the state of the "Show path names" toggle button, either
1984 * the file names or the path names are listed.
1986 static void uploadFileListItems(WindowInfo* window, Bool replace)
1988 XmStringTable names;
1989 int nWritable, i, *selected, selectedCount;
1990 char buf[MAXPATHLEN+1], policy;
1991 Bool usePathNames;
1992 WindowInfo *w;
1993 Widget list;
1995 nWritable = window->nWritableWindows;
1996 list = window->replaceMultiFileList;
1998 names = (XmStringTable) XtMalloc(nWritable * sizeof(XmString*));
2000 usePathNames = XmToggleButtonGetState(window->replaceMultiFilePathBtn);
2002 /* Note: the windows are sorted alphabetically by _file_ name. This
2003 order is _not_ changed when we switch to path names. That
2004 would be confusing for the user */
2006 for (i = 0; i < nWritable; ++i) {
2007 w = window->writableWindows[i];
2008 if (usePathNames && window->filenameSet) {
2009 sprintf(buf, "%s%s", w->path, w->filename);
2010 } else {
2011 sprintf(buf, "%s", w->filename);
2013 names[i] = XmStringCreateSimple(buf);
2017 * If the list is in extended selection mode, we can't pre-select
2018 * more than one item in (probably because XmListSelectPos is
2019 * equivalent to a button1 click; I don't think that there is an
2020 * equivalent for CTRL-button1). Therefore, we temporarily put the
2021 * list into multiple selection mode.
2023 XtVaGetValues(list, XmNselectionPolicy, &policy, NULL);
2024 XtVaSetValues(list, XmNselectionPolicy, XmMULTIPLE_SELECT, NULL);
2025 if (replace) {
2026 /* Note: this function is obsolete in Motif 2.x, but it is available
2027 for compatibility reasons */
2028 XmListGetSelectedPos(list, &selected, &selectedCount);
2030 XmListReplaceItemsPos(list, names, nWritable, 1);
2032 /* Maintain the selections */
2033 XmListDeselectAllItems(list);
2034 for (i = 0; i < selectedCount; ++i) {
2035 XmListSelectPos(list, selected[i], False);
2038 XtFree((XtPointer)selected);
2039 } else {
2040 Arg args[1];
2041 int nVisible;
2042 int firstSelected = 0;
2044 /* Remove the old list, if any */
2045 XmListDeleteAllItems(list);
2047 /* Initial settings */
2048 XmListAddItems(list, names, nWritable, 1);
2050 /* Pre-select the files from the last run. */
2051 selectedCount = 0;
2052 for (i = 0; i < nWritable; ++i) {
2053 if (window->writableWindows[i]->multiFileReplSelected) {
2054 XmListSelectPos(list, i+1, False);
2055 ++selectedCount;
2056 /* Remember the first selected item */
2057 if (firstSelected == 0) firstSelected = i+1;
2060 /* If no files are selected, we select them all. Normally this only
2061 happens the first time the dialog is used, but it looks "silly"
2062 if the dialog pops up with nothing selected. */
2063 if (selectedCount == 0) {
2064 for (i = 0; i < nWritable; ++i) {
2065 XmListSelectPos(list, i+1, False);
2067 firstSelected = 1;
2070 /* Make sure that the first selected item is visible; otherwise, the
2071 user could get the impression that nothing is selected. By
2072 visualizing at least the first selected item, the user will more
2073 easily be confident that the previous selection is still active. */
2074 XtSetArg(args[0], XmNvisibleItemCount, &nVisible);
2075 XtGetValues(list, args, 1);
2076 /* Make sure that we don't create blank lines at the bottom by
2077 positioning too far. */
2078 if (nWritable <= nVisible) {
2079 /* No need to shift the visible position */
2080 firstSelected = 1;
2082 else {
2083 int maxFirst = nWritable - nVisible + 1;
2084 if (firstSelected > maxFirst)
2085 firstSelected = maxFirst;
2087 XmListSetPos(list, firstSelected);
2090 /* Put the list back into its original selection policy. */
2091 XtVaSetValues(list, XmNselectionPolicy, policy, NULL);
2093 for (i = 0; i < nWritable; ++i)
2094 XmStringFree(names[i]);
2095 XtFree((XtPointer)names);
2099 ** Unconditionally pops down the replace dialog and the
2100 ** replace-in-multiple-files dialog, if it exists.
2102 static void unmanageReplaceDialogs(WindowInfo *window)
2104 /* If the replace dialog goes down, the multi-file replace dialog must
2105 go down too */
2106 if (window->replaceMultiFileDlog &&
2107 XtIsManaged(window->replaceMultiFileDlog)) {
2108 XtUnmanageChild(window->replaceMultiFileDlog);
2111 if (window->replaceDlog &&
2112 XtIsManaged(window->replaceDlog)) {
2113 XtUnmanageChild(window->replaceDlog);
2117 static void rInSelCB(Widget w, WindowInfo *window,
2118 XmAnyCallbackStruct *callData)
2120 char searchString[SEARCHMAX], replaceString[SEARCHMAX];
2121 int direction, searchType;
2122 char *params[3];
2124 /* Validate and fetch the find and replace strings from the dialog */
2125 if (!getReplaceDlogInfo(window, &direction, searchString, replaceString,
2126 &searchType))
2127 return;
2129 /* Set the initial focus of the dialog back to the search string */
2130 resetReplaceTabGroup(window);
2132 /* do replacement */
2133 params[0] = searchString;
2134 params[1] = replaceString;
2135 params[2] = searchTypeArg(searchType);
2136 XtCallActionProc(window->lastFocus, "replace_in_selection",
2137 callData->event, params, 3);
2139 /* pop down the dialog */
2140 if (!XmToggleButtonGetState(window->replaceKeepBtn))
2141 unmanageReplaceDialogs(window);
2144 static void rCancelCB(Widget w, WindowInfo *window, caddr_t callData)
2146 /* Set the initial focus of the dialog back to the search string */
2147 resetReplaceTabGroup(window);
2149 /* pop down the dialog */
2150 unmanageReplaceDialogs(window);
2153 static void fCancelCB(Widget w, WindowInfo *window, caddr_t callData)
2155 /* Set the initial focus of the dialog back to the search string */
2156 resetFindTabGroup(window);
2158 /* pop down the dialog */
2159 XtUnmanageChild(window->findDlog);
2162 static void rFindCB(Widget w, WindowInfo *window,XmAnyCallbackStruct *callData)
2164 char searchString[SEARCHMAX], replaceString[SEARCHMAX];
2165 int direction, searchType;
2166 char *params[4];
2168 /* Validate and fetch the find and replace strings from the dialog */
2169 if (!getReplaceDlogInfo(window, &direction, searchString, replaceString,
2170 &searchType))
2171 return;
2173 /* Set the initial focus of the dialog back to the search string */
2174 resetReplaceTabGroup(window);
2176 /* Find the text and mark it */
2177 params[0] = searchString;
2178 params[1] = directionArg(direction);
2179 params[2] = searchTypeArg(searchType);
2180 params[3] = searchWrapArg(GetPrefSearchWraps());
2181 XtCallActionProc(window->lastFocus, "find", callData->event, params, 4);
2183 /* Doctor the search history generated by the action to include the
2184 replace string (if any), so the replace string can be used on
2185 subsequent replaces, even though no actual replacement was done. */
2186 if (historyIndex(1) != -1 &&
2187 !strcmp(SearchHistory[historyIndex(1)], searchString)) {
2188 XtFree(ReplaceHistory[historyIndex(1)]);
2189 ReplaceHistory[historyIndex(1)] = XtNewString(replaceString);
2192 /* Pop down the dialog */
2193 if (!XmToggleButtonGetState(window->replaceKeepBtn))
2194 unmanageReplaceDialogs(window);
2197 static void replaceFindCB(Widget w, WindowInfo *window, XmAnyCallbackStruct *callData)
2199 char searchString[SEARCHMAX+1], replaceString[SEARCHMAX+1];
2200 int direction, searchType;
2201 char *params[4];
2203 /* Validate and fetch the find and replace strings from the dialog */
2204 if (!getReplaceDlogInfo(window, &direction, searchString, replaceString,
2205 &searchType))
2206 return;
2208 /* Set the initial focus of the dialog back to the search string */
2209 resetReplaceTabGroup(window);
2211 /* Find the text and replace it */
2212 params[0] = searchString;
2213 params[1] = replaceString;
2214 params[2] = directionArg(direction);
2215 params[3] = searchTypeArg(searchType);
2216 XtCallActionProc(window->lastFocus, "replace_find", callData->event, params, 4);
2218 /* Pop down the dialog */
2219 if (!XmToggleButtonGetState(window->replaceKeepBtn))
2220 unmanageReplaceDialogs(window);
2223 static void rSetActionButtons(WindowInfo* window,
2224 int replaceBtn,
2225 int replaceFindBtn,
2226 int replaceAndFindBtn,
2227 #ifndef REPLACE_SCOPE
2228 int replaceInWinBtn,
2229 int replaceInSelBtn,
2230 #endif
2231 int replaceAllBtn)
2233 XtSetSensitive(window->replaceBtn, replaceBtn);
2234 XtSetSensitive(window->replaceFindBtn, replaceFindBtn);
2235 XtSetSensitive(window->replaceAndFindBtn, replaceAndFindBtn);
2236 #ifndef REPLACE_SCOPE
2237 XtSetSensitive(window->replaceInWinBtn, replaceInWinBtn);
2238 XtSetSensitive(window->replaceInSelBtn, replaceInSelBtn);
2239 #endif
2240 XtSetSensitive(window->replaceAllBtn, replaceAllBtn);
2243 void UpdateReplaceActionButtons(WindowInfo* window)
2245 /* Is there any text in the search for field */
2246 int searchText = textFieldNonEmpty(window->replaceText);
2247 #ifdef REPLACE_SCOPE
2248 switch (window->replaceScope)
2250 case REPL_SCOPE_WIN:
2251 /* Enable all buttons, if there is any text in the search field. */
2252 rSetActionButtons(window, searchText, searchText, searchText, searchText);
2253 break;
2255 case REPL_SCOPE_SEL:
2256 /* Only enable Replace All, if a selection exists and text in search field. */
2257 rSetActionButtons(window, False, False, False, searchText && window->wasSelected);
2258 break;
2260 case REPL_SCOPE_MULTI:
2261 /* Only enable Replace All, if text in search field. */
2262 rSetActionButtons(window, False, False, False, searchText);
2263 break;
2265 #else
2266 rSetActionButtons(window, searchText, searchText, searchText,
2267 searchText, searchText && window->wasSelected,
2268 searchText && (countWritableWindows() > 1));
2269 #endif
2272 #ifdef REPLACE_SCOPE
2274 ** The next 3 callback adapt the sensitivity of the replace dialog push
2275 ** buttons to the state of the scope radio buttons.
2277 static void rScopeWinCB(Widget w, WindowInfo *window,
2278 XmAnyCallbackStruct *callData)
2280 if (XmToggleButtonGetState(window->replaceScopeWinToggle)) {
2281 window->replaceScope = REPL_SCOPE_WIN;
2282 UpdateReplaceActionButtons(window);
2286 static void rScopeSelCB(Widget w, WindowInfo *window,
2287 XmAnyCallbackStruct *callData)
2289 if (XmToggleButtonGetState(window->replaceScopeSelToggle)) {
2290 window->replaceScope = REPL_SCOPE_SEL;
2291 UpdateReplaceActionButtons(window);
2295 static void rScopeMultiCB(Widget w, WindowInfo *window,
2296 XmAnyCallbackStruct *callData)
2298 if (XmToggleButtonGetState(window->replaceScopeMultiToggle)) {
2299 window->replaceScope = REPL_SCOPE_MULTI;
2300 UpdateReplaceActionButtons(window);
2305 ** This routine dispatches a push on the replace-all button to the appropriate
2306 ** callback, depending on the state of the scope radio buttons.
2308 static void replaceAllScopeCB(Widget w, WindowInfo *window,
2309 XmAnyCallbackStruct *callData)
2311 switch(window->replaceScope) {
2312 case REPL_SCOPE_WIN:
2313 replaceAllCB(w, window, callData);
2314 break;
2315 case REPL_SCOPE_SEL:
2316 rInSelCB(w, window, callData);
2317 break;
2318 case REPL_SCOPE_MULTI:
2319 replaceMultiFileCB(w, window, callData);
2320 break;
2323 #endif
2325 static int textFieldNonEmpty(Widget w)
2327 char *str = XmTextGetString(w);
2328 int nonEmpty = (str[0] != '\0');
2329 XtFree(str);
2330 return(nonEmpty);
2333 static void rFindTextValueChangedCB(Widget w, WindowInfo *window, XKeyEvent *event)
2335 UpdateReplaceActionButtons(window);
2338 static void rFindArrowKeyCB(Widget w, WindowInfo *window, XKeyEvent *event)
2340 KeySym keysym = XLookupKeysym(event, 0);
2341 int index = window->rHistIndex;
2342 char *searchStr, *replaceStr;
2343 int searchType;
2345 /* only process up and down arrow keys */
2346 if (keysym != XK_Up && keysym != XK_Down)
2347 return;
2349 /* increment or decrement the index depending on which arrow was pressed */
2350 index += (keysym == XK_Up) ? 1 : -1;
2352 /* if the index is out of range, beep and return */
2353 if (index != 0 && historyIndex(index) == -1) {
2354 XBell(TheDisplay, 0);
2355 return;
2358 /* determine the strings and button settings to use */
2359 if (index == 0) {
2360 searchStr = "";
2361 replaceStr = "";
2362 searchType = GetPrefSearch();
2363 } else {
2364 searchStr = SearchHistory[historyIndex(index)];
2365 replaceStr = ReplaceHistory[historyIndex(index)];
2366 searchType = SearchTypeHistory[historyIndex(index)];
2369 /* Set the buttons and fields with the selected search type */
2370 initToggleButtons(searchType, window->replaceRegexToggle,
2371 window->replaceCaseToggle, &window->replaceWordToggle,
2372 &window->replaceLastLiteralCase,
2373 &window->replaceLastRegexCase);
2375 XmTextSetString(window->replaceText, searchStr);
2376 XmTextSetString(window->replaceWithText, replaceStr);
2378 /* Set the state of the Replace, Find ... buttons */
2379 UpdateReplaceActionButtons(window);
2381 window->rHistIndex = index;
2384 static void replaceArrowKeyCB(Widget w, WindowInfo *window, XKeyEvent *event)
2386 KeySym keysym = XLookupKeysym(event, 0);
2387 int index = window->rHistIndex;
2389 /* only process up and down arrow keys */
2390 if (keysym != XK_Up && keysym != XK_Down)
2391 return;
2393 /* increment or decrement the index depending on which arrow was pressed */
2394 index += (keysym == XK_Up) ? 1 : -1;
2396 /* if the index is out of range, beep and return */
2397 if (index != 0 && historyIndex(index) == -1) {
2398 XBell(TheDisplay, 0);
2399 return;
2402 /* change only the replace field information */
2403 if (index == 0)
2404 XmTextSetString(window->replaceWithText, "");
2405 else
2406 XmTextSetString(window->replaceWithText,
2407 ReplaceHistory[historyIndex(index)]);
2408 window->rHistIndex = index;
2411 static void fUpdateActionButtons(WindowInfo *window)
2413 int buttonState = textFieldNonEmpty(window->findText);
2414 XtSetSensitive(window->findBtn, buttonState);
2417 static void findTextValueChangedCB(Widget w, WindowInfo *window, XKeyEvent *event)
2419 fUpdateActionButtons(window);
2422 static void findArrowKeyCB(Widget w, WindowInfo *window, XKeyEvent *event)
2424 KeySym keysym = XLookupKeysym(event, 0);
2425 int index = window->fHistIndex;
2426 char *searchStr;
2427 int searchType;
2429 /* only process up and down arrow keys */
2430 if (keysym != XK_Up && keysym != XK_Down)
2431 return;
2433 /* increment or decrement the index depending on which arrow was pressed */
2434 index += (keysym == XK_Up) ? 1 : -1;
2436 /* if the index is out of range, beep and return */
2437 if (index != 0 && historyIndex(index) == -1) {
2438 XBell(TheDisplay, 0);
2439 return;
2442 /* determine the strings and button settings to use */
2443 if (index == 0) {
2444 searchStr = "";
2445 searchType = GetPrefSearch();
2446 } else {
2447 searchStr = SearchHistory[historyIndex(index)];
2448 searchType = SearchTypeHistory[historyIndex(index)];
2451 /* Set the buttons and fields with the selected search type */
2452 initToggleButtons(searchType, window->findRegexToggle,
2453 window->findCaseToggle, &window->findWordToggle,
2454 &window->findLastLiteralCase,
2455 &window->findLastRegexCase);
2456 XmTextSetString(window->findText, searchStr);
2458 /* Set the state of the Find ... button */
2459 fUpdateActionButtons(window);
2461 window->fHistIndex = index;
2464 static void findCB(Widget w, WindowInfo *window,XmAnyCallbackStruct *callData)
2466 char searchString[SEARCHMAX];
2467 int direction, searchType;
2468 char *params[4];
2470 /* fetch find string, direction and type from the dialog */
2471 if (!getFindDlogInfo(window, &direction, searchString, &searchType))
2472 return;
2474 /* Set the initial focus of the dialog back to the search string */
2475 resetFindTabGroup(window);
2477 /* find the text and mark it */
2478 params[0] = searchString;
2479 params[1] = directionArg(direction);
2480 params[2] = searchTypeArg(searchType);
2481 params[3] = searchWrapArg(GetPrefSearchWraps());
2482 XtCallActionProc(window->lastFocus, "find", callData->event, params, 4);
2484 /* pop down the dialog */
2485 if (!XmToggleButtonGetState(window->findKeepBtn))
2486 XtUnmanageChild(window->findDlog);
2490 ** Fetch and verify (particularly regular expression) search and replace
2491 ** strings and search type from the Replace dialog. If the strings are ok,
2492 ** save a copy in the search history, copy them in to "searchString",
2493 ** "replaceString', which are assumed to be at least SEARCHMAX in length,
2494 ** return search type in "searchType", and return TRUE as the function
2495 ** value. Otherwise, return FALSE.
2497 static int getReplaceDlogInfo(WindowInfo *window, int *direction,
2498 char *searchString, char *replaceString, int *searchType)
2500 char *replaceText, *replaceWithText;
2501 regexp *compiledRE = NULL;
2502 char *compileMsg;
2504 /* Get the search and replace strings, search type, and direction
2505 from the dialog */
2506 replaceText = XmTextGetString(window->replaceText);
2507 replaceWithText = XmTextGetString(window->replaceWithText);
2509 if(XmToggleButtonGetState(window->replaceRegexToggle)) {
2510 int regexDefault;
2511 if(XmToggleButtonGetState(window->replaceCaseToggle)) {
2512 *searchType = SEARCH_REGEX;
2513 regexDefault = REDFLT_STANDARD;
2514 } else {
2515 *searchType = SEARCH_REGEX_NOCASE;
2516 regexDefault = REDFLT_CASE_INSENSITIVE;
2518 /* If the search type is a regular expression, test compile it
2519 immediately and present error messages */
2520 compiledRE = CompileRE(replaceText, &compileMsg, regexDefault);
2521 if (compiledRE == NULL) {
2522 DialogF(DF_WARN, XtParent(window->replaceDlog), 1, "Search String",
2523 "Please respecify the search string:\n%s", "OK", compileMsg);
2524 XtFree(replaceText);
2525 XtFree(replaceWithText);
2526 return FALSE;
2528 free((char*)compiledRE);
2529 } else {
2530 if(XmToggleButtonGetState(window->replaceCaseToggle)) {
2531 if(XmToggleButtonGetState(window->replaceWordToggle))
2532 *searchType = SEARCH_CASE_SENSE_WORD;
2533 else
2534 *searchType = SEARCH_CASE_SENSE;
2535 } else {
2536 if(XmToggleButtonGetState(window->replaceWordToggle))
2537 *searchType = SEARCH_LITERAL_WORD;
2538 else
2539 *searchType = SEARCH_LITERAL;
2543 *direction = XmToggleButtonGetState(window->replaceRevToggle) ?
2544 SEARCH_BACKWARD : SEARCH_FORWARD;
2546 /* Return strings */
2547 if (strlen(replaceText) >= SEARCHMAX) {
2548 DialogF(DF_WARN, XtParent(window->replaceDlog), 1, "String too long",
2549 "Search string too long.", "OK");
2550 XtFree(replaceText);
2551 XtFree(replaceWithText);
2552 return FALSE;
2554 if (strlen(replaceWithText) >= SEARCHMAX) {
2555 DialogF(DF_WARN, XtParent(window->replaceDlog), 1, "String too long",
2556 "Replace string too long.", "OK");
2557 XtFree(replaceText);
2558 XtFree(replaceWithText);
2559 return FALSE;
2561 strcpy(searchString, replaceText);
2562 strcpy(replaceString, replaceWithText);
2563 XtFree(replaceText);
2564 XtFree(replaceWithText);
2565 return TRUE;
2569 ** Fetch and verify (particularly regular expression) search string,
2570 ** direction, and search type from the Find dialog. If the search string
2571 ** is ok, save a copy in the search history, copy it to "searchString",
2572 ** which is assumed to be at least SEARCHMAX in length, return search type
2573 ** in "searchType", and return TRUE as the function value. Otherwise,
2574 ** return FALSE.
2576 static int getFindDlogInfo(WindowInfo *window, int *direction,
2577 char *searchString, int *searchType)
2579 char *findText;
2580 regexp *compiledRE = NULL;
2581 char *compileMsg;
2583 /* Get the search string, search type, and direction from the dialog */
2584 findText = XmTextGetString(window->findText);
2586 if(XmToggleButtonGetState(window->findRegexToggle)) {
2587 int regexDefault;
2588 if(XmToggleButtonGetState(window->findCaseToggle)) {
2589 *searchType = SEARCH_REGEX;
2590 regexDefault = REDFLT_STANDARD;
2591 } else {
2592 *searchType = SEARCH_REGEX_NOCASE;
2593 regexDefault = REDFLT_CASE_INSENSITIVE;
2595 /* If the search type is a regular expression, test compile it
2596 immediately and present error messages */
2597 compiledRE = CompileRE(findText, &compileMsg, regexDefault);
2598 if (compiledRE == NULL) {
2599 DialogF(DF_WARN, XtParent(window->findDlog), 1, "Regex Error",
2600 "Please respecify the search string:\n%s", "OK", compileMsg);
2601 return FALSE;
2603 free((char *)compiledRE);
2604 } else {
2605 if(XmToggleButtonGetState(window->findCaseToggle)) {
2606 if(XmToggleButtonGetState(window->findWordToggle))
2607 *searchType = SEARCH_CASE_SENSE_WORD;
2608 else
2609 *searchType = SEARCH_CASE_SENSE;
2610 } else {
2611 if(XmToggleButtonGetState(window->findWordToggle))
2612 *searchType = SEARCH_LITERAL_WORD;
2613 else
2614 *searchType = SEARCH_LITERAL;
2618 *direction = XmToggleButtonGetState(window->findRevToggle) ?
2619 SEARCH_BACKWARD : SEARCH_FORWARD;
2621 if (isRegexType(*searchType)) {
2624 /* Return the search string */
2625 if (strlen(findText) >= SEARCHMAX) {
2626 DialogF(DF_WARN, XtParent(window->findDlog), 1, "String too long",
2627 "Search string too long.", "OK");
2628 XtFree(findText);
2629 return FALSE;
2631 strcpy(searchString, findText);
2632 XtFree(findText);
2633 return TRUE;
2636 int SearchAndSelectSame(WindowInfo *window, int direction, int searchWrap)
2638 if (NHist < 1) {
2639 XBell(TheDisplay, 0);
2640 return FALSE;
2643 return SearchAndSelect(window, direction, SearchHistory[historyIndex(1)],
2644 SearchTypeHistory[historyIndex(1)], searchWrap);
2648 ** Search for "searchString" in "window", and select the matching text in
2649 ** the window when found (or beep or put up a dialog if not found). Also
2650 ** adds the search string to the global search history.
2652 int SearchAndSelect(WindowInfo *window, int direction, const char *searchString,
2653 int searchType, int searchWrap)
2655 int startPos, endPos;
2656 int beginPos, cursorPos, selStart, selEnd;
2658 /* Save a copy of searchString in the search history */
2659 saveSearchHistory(searchString, NULL, searchType, FALSE);
2661 /* set the position to start the search so we don't find the same
2662 string that was found on the last search */
2663 if (searchMatchesSelection(window, searchString, searchType,
2664 &selStart, &selEnd, NULL, NULL)) {
2665 /* selection matches search string, start before or after sel. */
2666 if (direction == SEARCH_BACKWARD) {
2667 beginPos = selStart-1;
2668 } else {
2669 beginPos = selEnd;
2671 } else {
2672 selStart = -1; selEnd = -1;
2673 /* no selection, or no match, search relative cursor */
2674 cursorPos = TextGetCursorPos(window->lastFocus);
2675 if (direction == SEARCH_BACKWARD) {
2676 /* use the insert position - 1 for backward searches */
2677 beginPos = cursorPos-1;
2678 } else {
2679 /* use the insert position for forward searches */
2680 beginPos = cursorPos;
2684 /* when the i-search bar is active and search is repeated there
2685 (Return), the action "find" is called (not: "find_incremental").
2686 "find" calls this function SearchAndSelect.
2687 To keep track of the iSearchLastBeginPos correctly in the
2688 repeated i-search case it is necessary to call the following
2689 function here, otherwise there are no beeps on the repeated
2690 incremental search wraps. */
2691 iSearchRecordLastBeginPos(window, direction, beginPos);
2693 /* do the search. SearchWindow does appropriate dialogs and beeps */
2694 if (!SearchWindow(window, direction, searchString, searchType, searchWrap,
2695 beginPos, &startPos, &endPos, NULL, NULL))
2696 return FALSE;
2698 /* if the search matched an empty string (possible with regular exps)
2699 beginning at the start of the search, go to the next occurrence,
2700 otherwise repeated finds will get "stuck" at zero-length matches */
2701 if (direction==SEARCH_FORWARD && beginPos==startPos && beginPos==endPos)
2702 if (!SearchWindow(window, direction, searchString, searchType, searchWrap,
2703 beginPos+1, &startPos, &endPos, NULL, NULL))
2704 return FALSE;
2706 /* if matched text is already selected, just beep */
2707 if (selStart==startPos && selEnd==endPos) {
2708 XBell(TheDisplay, 0);
2709 return FALSE;
2712 /* select the text found string */
2713 BufSelect(window->buffer, startPos, endPos);
2714 MakeSelectionVisible(window, window->lastFocus);
2715 TextSetCursorPos(window->lastFocus, endPos);
2717 return TRUE;
2720 void SearchForSelected(WindowInfo *window, int direction, int searchType,
2721 int searchWrap, Time time)
2723 SearchSelectedCallData *callData = XtNew(SearchSelectedCallData);
2724 callData->direction = direction;
2725 callData->searchType = searchType;
2726 callData->searchWrap = searchWrap;
2727 XtGetSelectionValue(window->textArea, XA_PRIMARY, XA_STRING,
2728 (XtSelectionCallbackProc)selectedSearchCB, callData, time);
2731 static void selectedSearchCB(Widget w, XtPointer callData, Atom *selection,
2732 Atom *type, char *value, int *length, int *format)
2734 WindowInfo *window = WidgetToWindow(w);
2735 SearchSelectedCallData *callDataItems = (SearchSelectedCallData *)callData;
2736 int searchType;
2737 char searchString[SEARCHMAX+1];
2739 /* skip if we can't get the selection data or it's too long */
2740 if (*type == XT_CONVERT_FAIL || value == NULL) {
2741 if (GetPrefSearchDlogs())
2742 DialogF(DF_WARN, window->shell, 1, "Wrong Selection",
2743 "Selection not appropriate for searching", "OK");
2744 else
2745 XBell(TheDisplay, 0);
2746 XtFree(callData);
2747 return;
2749 if (*length > SEARCHMAX) {
2750 if (GetPrefSearchDlogs())
2751 DialogF(DF_WARN, window->shell, 1, "Selection too long",
2752 "Selection too long", "OK");
2753 else
2754 XBell(TheDisplay, 0);
2755 XtFree(value);
2756 XtFree(callData);
2757 return;
2759 if (*length == 0) {
2760 XBell(TheDisplay, 0);
2761 XtFree(value);
2762 XtFree(callData);
2763 return;
2765 /* should be of type text??? */
2766 if (*format != 8) {
2767 fprintf(stderr, "NEdit: can't handle non 8-bit text\n");
2768 XBell(TheDisplay, 0);
2769 XtFree(value);
2770 XtFree(callData);
2771 return;
2773 /* make the selection the current search string */
2774 strncpy(searchString, value, *length);
2775 searchString[*length] = '\0';
2776 XtFree(value);
2778 /* Use the passed method for searching, unless it is regex, since this
2779 kind of search is by definition a literal search */
2780 searchType = callDataItems->searchType;
2781 if (searchType == SEARCH_REGEX )
2782 searchType = SEARCH_CASE_SENSE;
2783 else if (searchType == SEARCH_REGEX_NOCASE)
2784 searchType = SEARCH_LITERAL;
2786 /* search for it in the window */
2787 SearchAndSelect(window, callDataItems->direction, searchString,
2788 searchType, callDataItems->searchWrap);
2789 XtFree(callData);
2793 ** Pop up and clear the incremental search line and prepare to search.
2795 void BeginISearch(WindowInfo *window, int direction)
2797 window->iSearchStartPos = -1;
2798 XmTextSetString(window->iSearchText, "");
2799 XmToggleButtonSetState(window->iSearchRevToggle,
2800 direction == SEARCH_BACKWARD, FALSE);
2801 /* Note: in contrast to the replace and find dialogs, the regex and
2802 case toggles are not reset to their default state when the incremental
2803 search bar is redisplayed. I'm not sure whether this is the best
2804 choice. If not, an initToggleButtons() call should be inserted
2805 here. But in that case, it might be appropriate to have different
2806 default search modes for i-search and replace/find. */
2807 TempShowISearch(window, TRUE);
2808 XmProcessTraversal(window->iSearchText, XmTRAVERSE_CURRENT);
2812 ** Incremental searching is anchored at the position where the cursor
2813 ** was when the user began typing the search string. Call this routine
2814 ** to forget about this original anchor, and if the search bar is not
2815 ** permanently up, pop it down.
2817 void EndISearch(WindowInfo *window)
2819 /* Note: Please maintain this such that it can be freely peppered in
2820 mainline code, without callers having to worry about performance
2821 or visual glitches. */
2823 /* Forget the starting position used for the current run of searches */
2824 window->iSearchStartPos = -1;
2826 /* Mark the end of incremental search history overwriting */
2827 saveSearchHistory("", NULL, 0, FALSE);
2829 /* Pop down the search line (if it's not pegged up in Preferences) */
2830 TempShowISearch(window, FALSE);
2834 ** Reset window->iSearchLastBeginPos to the resulting initial
2835 ** search begin position for incremental searches.
2837 static void iSearchRecordLastBeginPos(WindowInfo *window, int direction,
2838 int initPos)
2840 window->iSearchLastBeginPos = initPos;
2841 if (direction == SEARCH_BACKWARD)
2842 window->iSearchLastBeginPos--;
2846 ** Search for "searchString" in "window", and select the matching text in
2847 ** the window when found (or beep or put up a dialog if not found). If
2848 ** "continued" is TRUE and a prior incremental search starting position is
2849 ** recorded, search from that original position, otherwise, search from the
2850 ** current cursor position.
2852 int SearchAndSelectIncremental(WindowInfo *window, int direction,
2853 const char *searchString, int searchType, int searchWrap, int continued)
2855 int beginPos, startPos, endPos;
2857 /* If there's a search in progress, start the search from the original
2858 starting position, otherwise search from the cursor position. */
2859 if (!continued || window->iSearchStartPos == -1) {
2860 window->iSearchStartPos = TextGetCursorPos(window->lastFocus);
2861 iSearchRecordLastBeginPos(window, direction, window->iSearchStartPos);
2863 beginPos = window->iSearchStartPos;
2865 /* If the search string is empty, beep eventually if text wrapped
2866 back to the initial position, re-init iSearchLastBeginPos,
2867 clear the selection, set the cursor back to what would be the
2868 beginning of the search, and return. */
2869 if(searchString[0] == 0) {
2870 int beepBeginPos = (direction == SEARCH_BACKWARD) ? beginPos-1:beginPos;
2871 iSearchTryBeepOnWrap(window, direction, beepBeginPos, beepBeginPos);
2872 iSearchRecordLastBeginPos(window, direction, window->iSearchStartPos);
2873 BufUnselect(window->buffer);
2874 TextSetCursorPos(window->lastFocus, beginPos);
2875 return TRUE;
2878 /* Save the string in the search history, unless we're cycling thru
2879 the search history itself, which can be detected by matching the
2880 search string with the search string of the current history index. */
2881 if(!(window->iSearchHistIndex > 1 && !strcmp(searchString,
2882 SearchHistory[historyIndex(window->iSearchHistIndex)]))) {
2883 saveSearchHistory(searchString, NULL, searchType, TRUE);
2884 /* Reset the incremental search history pointer to the beginning */
2885 window->iSearchHistIndex = 1;
2888 /* begin at insert position - 1 for backward searches */
2889 if (direction == SEARCH_BACKWARD)
2890 beginPos--;
2892 /* do the search. SearchWindow does appropriate dialogs and beeps */
2893 if (!SearchWindow(window, direction, searchString, searchType, searchWrap,
2894 beginPos, &startPos, &endPos, NULL, NULL))
2895 return FALSE;
2897 window->iSearchLastBeginPos = startPos;
2899 /* if the search matched an empty string (possible with regular exps)
2900 beginning at the start of the search, go to the next occurrence,
2901 otherwise repeated finds will get "stuck" at zero-length matches */
2902 if (direction==SEARCH_FORWARD && beginPos==startPos && beginPos==endPos)
2903 if (!SearchWindow(window, direction, searchString, searchType, searchWrap,
2904 beginPos+1, &startPos, &endPos, NULL, NULL))
2905 return FALSE;
2907 window->iSearchLastBeginPos = startPos;
2909 /* select the text found string */
2910 BufSelect(window->buffer, startPos, endPos);
2911 MakeSelectionVisible(window, window->lastFocus);
2912 TextSetCursorPos(window->lastFocus, endPos);
2914 return TRUE;
2918 ** Attach callbacks to the incremental search bar widgets. This also fudges
2919 ** up the translations on the text widget so Shift+Return will call the
2920 ** activate callback (along with Return and Ctrl+Return). It does this
2921 ** because incremental search uses the activate callback from the text
2922 ** widget to detect when the user has pressed Return to search for the next
2923 ** occurrence of the search string, and Shift+Return, which is the natural
2924 ** command for a reverse search does not naturally trigger this callback.
2926 void SetISearchTextCallbacks(WindowInfo *window)
2928 static XtTranslations table = NULL;
2929 static char *translations = "Shift<KeyPress>Return: activate()\n";
2931 if (table == NULL)
2932 table = XtParseTranslationTable(translations);
2933 XtOverrideTranslations(window->iSearchText, table);
2935 XtAddCallback(window->iSearchText, XmNactivateCallback,
2936 (XtCallbackProc)iSearchTextActivateCB, window);
2937 XtAddCallback(window->iSearchText, XmNvalueChangedCallback,
2938 (XtCallbackProc)iSearchTextValueChangedCB, window);
2939 XtAddEventHandler(window->iSearchText, KeyPressMask, False,
2940 (XtEventHandler)iSearchTextKeyEH, window);
2942 /* Attach callbacks to deal with the optional sticky case sensitivity
2943 behaviour. Do this before installing the search callbacks to make
2944 sure that the proper search parameters are taken into account. */
2945 XtAddCallback(window->iSearchCaseToggle, XmNvalueChangedCallback,
2946 (XtCallbackProc)iSearchCaseToggleCB, window);
2947 XtAddCallback(window->iSearchRegexToggle, XmNvalueChangedCallback,
2948 (XtCallbackProc)iSearchRegExpToggleCB, window);
2950 /* When search parameters (direction or search type), redo the search */
2951 XtAddCallback(window->iSearchCaseToggle, XmNvalueChangedCallback,
2952 (XtCallbackProc)iSearchTextValueChangedCB, window);
2953 XtAddCallback(window->iSearchRegexToggle, XmNvalueChangedCallback,
2954 (XtCallbackProc)iSearchTextValueChangedCB, window);
2955 XtAddCallback(window->iSearchRevToggle, XmNvalueChangedCallback,
2956 (XtCallbackProc)iSearchTextValueChangedCB, window);
2960 ** User pressed return in the incremental search bar. Do a new search with
2961 ** the search string displayed. The direction of the search is toggled if
2962 ** the Ctrl key or the Shift key is pressed when the text field is activated.
2964 static void iSearchTextActivateCB(Widget w, WindowInfo *window,
2965 XmAnyCallbackStruct *callData)
2967 char *params[4];
2968 char *searchString;
2969 int searchType, direction;
2971 /* Fetch the string, search type and direction from the incremental
2972 search bar widgets at the top of the window */
2973 searchString = XmTextGetString(window->iSearchText);
2974 if(XmToggleButtonGetState(window->iSearchCaseToggle)) {
2975 if(XmToggleButtonGetState(window->iSearchRegexToggle))
2976 searchType = SEARCH_REGEX;
2977 else
2978 searchType = SEARCH_CASE_SENSE;
2979 } else {
2980 if(XmToggleButtonGetState(window->iSearchRegexToggle))
2981 searchType = SEARCH_REGEX_NOCASE;
2982 else
2983 searchType = SEARCH_LITERAL;
2985 direction = XmToggleButtonGetState(window->iSearchRevToggle) ?
2986 SEARCH_BACKWARD : SEARCH_FORWARD;
2988 /* Reverse the search direction if the Ctrl or Shift key was pressed */
2989 if (callData->event->xbutton.state & (ShiftMask | ControlMask))
2990 direction = direction == SEARCH_FORWARD ?
2991 SEARCH_BACKWARD : SEARCH_FORWARD;
2993 /* find the text and mark it */
2994 params[0] = searchString;
2995 params[1] = directionArg(direction);
2996 params[2] = searchTypeArg(searchType);
2997 params[3] = searchWrapArg(GetPrefSearchWraps());
2998 XtCallActionProc(window->lastFocus, "find", callData->event, params, 4);
2999 XtFree(searchString);
3003 ** Called when user types in the incremental search line. Redoes the
3004 ** search for the new search string.
3006 static void iSearchTextValueChangedCB(Widget w, WindowInfo *window,
3007 XmAnyCallbackStruct *callData)
3009 char *params[5];
3010 char *searchString;
3011 int searchType, direction, nParams;
3013 /* Fetch the string, search type and direction from the incremental
3014 search bar widgets at the top of the window */
3015 searchString = XmTextGetString(window->iSearchText);
3016 if(XmToggleButtonGetState(window->iSearchCaseToggle)) {
3017 if(XmToggleButtonGetState(window->iSearchRegexToggle))
3018 searchType = SEARCH_REGEX;
3019 else
3020 searchType = SEARCH_CASE_SENSE;
3021 } else {
3022 if(XmToggleButtonGetState(window->iSearchRegexToggle))
3023 searchType = SEARCH_REGEX_NOCASE;
3024 else
3025 searchType = SEARCH_LITERAL;
3027 direction = XmToggleButtonGetState(window->iSearchRevToggle) ?
3028 SEARCH_BACKWARD : SEARCH_FORWARD;
3030 /* If the search type is a regular expression, test compile it. If it
3031 fails, silently skip it. (This allows users to compose the expression
3032 in peace when they have unfinished syntax, but still get beeps when
3033 correct syntax doesn't match) */
3034 if (isRegexType(searchType)) {
3035 regexp *compiledRE = NULL;
3036 char *compileMsg;
3037 compiledRE = CompileRE(searchString, &compileMsg,
3038 defaultRegexFlags(searchType));
3039 if (compiledRE == NULL) {
3040 XtFree(searchString);
3041 return;
3043 free((char *)compiledRE);
3046 /* Call the incremental search action proc to do the searching and
3047 selecting (this allows it to be recorded for learn/replay). If
3048 there's an incremental search already in progress, mark the operation
3049 as "continued" so the search routine knows to re-start the search
3050 from the original starting position */
3051 nParams = 0;
3052 params[nParams++] = searchString;
3053 params[nParams++] = directionArg(direction);
3054 params[nParams++] = searchTypeArg(searchType);
3055 params[nParams++] = searchWrapArg(GetPrefSearchWraps());
3056 if (window->iSearchStartPos != -1)
3057 params[nParams++] = "continued";
3058 XtCallActionProc(window->lastFocus, "find_incremental",
3059 callData->event, params, nParams);
3060 XtFree(searchString);
3064 ** Process arrow keys for history recall, and escape key for leaving
3065 ** incremental search bar.
3067 static void iSearchTextKeyEH(Widget w, WindowInfo *window,
3068 XKeyEvent *event, Boolean *continueDispatch)
3070 KeySym keysym = XLookupKeysym(event, 0);
3071 int index = window->iSearchHistIndex;
3072 char *searchStr;
3073 int searchType;
3075 /* only process up and down arrow keys */
3076 if (keysym != XK_Up && keysym != XK_Down && keysym != XK_Escape) {
3077 *continueDispatch = TRUE;
3078 return;
3080 *continueDispatch = FALSE;
3082 /* allow escape key to cancel search */
3083 if (keysym == XK_Escape) {
3084 XmProcessTraversal(window->lastFocus, XmTRAVERSE_CURRENT);
3085 EndISearch(window);
3086 return;
3089 /* increment or decrement the index depending on which arrow was pressed */
3090 index += (keysym == XK_Up) ? 1 : -1;
3092 /* if the index is out of range, beep and return */
3093 if (index != 0 && historyIndex(index) == -1) {
3094 XBell(TheDisplay, 0);
3095 return;
3098 /* determine the strings and button settings to use */
3099 if (index == 0) {
3100 searchStr = "";
3101 searchType = GetPrefSearch();
3102 } else {
3103 searchStr = SearchHistory[historyIndex(index)];
3104 searchType = SearchTypeHistory[historyIndex(index)];
3107 /* Set the info used in the value changed callback before calling
3108 XmTextSetString(). */
3109 window->iSearchHistIndex = index;
3110 initToggleButtons(searchType, window->iSearchRegexToggle,
3111 window->iSearchCaseToggle, NULL,
3112 &window->iSearchLastLiteralCase,
3113 &window->iSearchLastRegexCase);
3115 /* Beware the value changed callback is processed as part of this call */
3116 XmTextSetString(window->iSearchText, searchStr);
3117 XmTextSetInsertionPosition(window->iSearchText,
3118 XmTextGetLastPosition(window->iSearchText));
3122 ** Check the character before the insertion cursor of textW and flash
3123 ** matching parenthesis, brackets, or braces, by temporarily highlighting
3124 ** the matching character (a timer procedure is scheduled for removing the
3125 ** highlights)
3127 void FlashMatching(WindowInfo *window, Widget textW)
3129 char c;
3130 void *style;
3131 int pos, matchIndex;
3132 int startPos, endPos, searchPos, matchPos;
3133 int constrain;
3135 /* if a marker is already drawn, erase it and cancel the timeout */
3136 if (window->flashTimeoutID != 0) {
3137 eraseFlash(window);
3138 XtRemoveTimeOut(window->flashTimeoutID);
3139 window->flashTimeoutID = 0;
3142 /* no flashing required */
3143 if (window->showMatchingStyle == NO_FLASH) {
3144 return;
3147 /* don't flash matching characters if there's a selection */
3148 if (window->buffer->primary.selected)
3149 return;
3151 /* get the character to match and the position to start from */
3152 pos = TextGetCursorPos(textW) - 1;
3153 if (pos < 0)
3154 return;
3155 c = BufGetCharacter(window->buffer, pos);
3156 style = GetHighlightInfo(window, pos);
3158 /* is the character one we want to flash? */
3159 for (matchIndex = 0; matchIndex<N_FLASH_CHARS; matchIndex++) {
3160 if (MatchingChars[matchIndex].c == c)
3161 break;
3163 if (matchIndex == N_FLASH_CHARS)
3164 return;
3166 /* constrain the search to visible text only when in single-pane mode
3167 AND using delimiter flashing (otherwise search the whole buffer) */
3168 constrain = ((window->nPanes == 0) &&
3169 (window->showMatchingStyle == FLASH_DELIMIT));
3171 if (MatchingChars[matchIndex].direction == SEARCH_BACKWARD) {
3172 startPos = constrain ? TextFirstVisiblePos(textW) : 0;
3173 endPos = pos;
3174 searchPos = endPos;
3175 } else {
3176 startPos = pos;
3177 endPos = constrain ? TextLastVisiblePos(textW) :
3178 window->buffer->length;
3179 searchPos = startPos;
3182 /* do the search */
3183 if (!findMatchingChar(window, c, style, searchPos, startPos, endPos,
3184 &matchPos))
3185 return;
3187 if (window->showMatchingStyle == FLASH_DELIMIT) {
3188 /* Highlight either the matching character ... */
3189 BufHighlight(window->buffer, matchPos, matchPos+1);
3190 } else {
3191 /* ... or the whole range. */
3192 if (MatchingChars[matchIndex].direction == SEARCH_BACKWARD) {
3193 BufHighlight(window->buffer, matchPos, pos+1);
3194 } else {
3195 BufHighlight(window->buffer, matchPos+1, pos);
3199 /* Set up a timer to erase the box after 1.5 seconds */
3200 window->flashTimeoutID = XtAppAddTimeOut(
3201 XtWidgetToApplicationContext(window->shell), 1500,
3202 flashTimeoutProc, window);
3203 window->flashPos = matchPos;
3206 void SelectToMatchingCharacter(WindowInfo *window)
3208 int selStart, selEnd;
3209 int startPos, endPos, matchPos;
3210 textBuffer *buf = window->buffer;
3212 /* get the character to match and its position from the selection, or
3213 the character before the insert point if nothing is selected.
3214 Give up if too many characters are selected */
3215 if (!GetSimpleSelection(buf, &selStart, &selEnd)) {
3216 selEnd = TextGetCursorPos(window->lastFocus);
3217 if (window->overstrike)
3218 selEnd += 1;
3219 selStart = selEnd - 1;
3220 if (selStart < 0) {
3221 XBell(TheDisplay, 0);
3222 return;
3225 if ((selEnd - selStart) != 1) {
3226 XBell(TheDisplay, 0);
3227 return;
3230 /* Search for it in the buffer */
3231 if (!findMatchingChar(window, BufGetCharacter(buf, selStart),
3232 GetHighlightInfo(window, selStart), selStart, 0, buf->length, &matchPos)) {
3233 XBell(TheDisplay, 0);
3234 return;
3236 startPos = (matchPos > selStart) ? selStart : matchPos;
3237 endPos = (matchPos > selStart) ? matchPos : selStart;
3239 /* select the text between the matching characters */
3240 BufSelect(buf, startPos, endPos+1);
3243 void GotoMatchingCharacter(WindowInfo *window)
3245 int selStart, selEnd;
3246 int matchPos;
3247 textBuffer *buf = window->buffer;
3249 /* get the character to match and its position from the selection, or
3250 the character before the insert point if nothing is selected.
3251 Give up if too many characters are selected */
3252 if (!GetSimpleSelection(buf, &selStart, &selEnd)) {
3253 selEnd = TextGetCursorPos(window->lastFocus);
3254 if (window->overstrike)
3255 selEnd += 1;
3256 selStart = selEnd - 1;
3257 if (selStart < 0) {
3258 XBell(TheDisplay, 0);
3259 return;
3262 if ((selEnd - selStart) != 1) {
3263 XBell(TheDisplay, 0);
3264 return;
3267 /* Search for it in the buffer */
3268 if (!findMatchingChar(window, BufGetCharacter(buf, selStart),
3269 GetHighlightInfo(window, selStart), selStart, 0,
3270 buf->length, &matchPos)) {
3271 XBell(TheDisplay, 0);
3272 return;
3275 /* temporarily shut off autoShowInsertPos before setting the cursor
3276 position so MakeSelectionVisible gets a chance to place the cursor
3277 string at a pleasing position on the screen (otherwise, the cursor would
3278 be automatically scrolled on screen and MakeSelectionVisible would do
3279 nothing) */
3280 XtVaSetValues(window->lastFocus, textNautoShowInsertPos, False, NULL);
3281 TextSetCursorPos(window->lastFocus, matchPos+1);
3282 MakeSelectionVisible(window, window->lastFocus);
3283 XtVaSetValues(window->lastFocus, textNautoShowInsertPos, True, NULL);
3286 static int findMatchingChar(WindowInfo *window, char toMatch,
3287 void* styleToMatch, int charPos, int startLimit, int endLimit,
3288 int *matchPos)
3290 int nestDepth, matchIndex, direction, beginPos, pos;
3291 char matchChar, c;
3292 void *style = NULL;
3293 textBuffer *buf = window->buffer;
3294 int matchSyntaxBased = window->matchSyntaxBased;
3296 /* If we don't match syntax based, fake a matching style. */
3297 if (!matchSyntaxBased) style = styleToMatch;
3299 /* Look up the matching character and match direction */
3300 for (matchIndex = 0; matchIndex<N_MATCH_CHARS; matchIndex++) {
3301 if (MatchingChars[matchIndex].c == toMatch)
3302 break;
3304 if (matchIndex == N_MATCH_CHARS)
3305 return FALSE;
3306 matchChar = MatchingChars[matchIndex].match;
3307 direction = MatchingChars[matchIndex].direction;
3309 /* find it in the buffer */
3310 beginPos = (direction==SEARCH_FORWARD) ? charPos+1 : charPos-1;
3311 nestDepth = 1;
3312 if (direction == SEARCH_FORWARD) {
3313 for (pos=beginPos; pos<endLimit; pos++) {
3314 c=BufGetCharacter(buf, pos);
3315 if (c == matchChar) {
3316 if (matchSyntaxBased) style = GetHighlightInfo(window, pos);
3317 if (style == styleToMatch) {
3318 nestDepth--;
3319 if (nestDepth == 0) {
3320 *matchPos = pos;
3321 return TRUE;
3324 } else if (c == toMatch) {
3325 if (matchSyntaxBased) style = GetHighlightInfo(window, pos);
3326 if (style == styleToMatch)
3327 nestDepth++;
3330 } else { /* SEARCH_BACKWARD */
3331 for (pos=beginPos; pos>=startLimit; pos--) {
3332 c=BufGetCharacter(buf, pos);
3333 if (c == matchChar) {
3334 if (matchSyntaxBased) style = GetHighlightInfo(window, pos);
3335 if (style == styleToMatch) {
3336 nestDepth--;
3337 if (nestDepth == 0) {
3338 *matchPos = pos;
3339 return TRUE;
3342 } else if (c == toMatch) {
3343 if (matchSyntaxBased) style = GetHighlightInfo(window, pos);
3344 if (style == styleToMatch)
3345 nestDepth++;
3349 return FALSE;
3353 ** Xt timer procedure for erasing the matching parenthesis marker.
3355 static void flashTimeoutProc(XtPointer clientData, XtIntervalId *id)
3357 eraseFlash((WindowInfo *)clientData);
3358 ((WindowInfo *)clientData)->flashTimeoutID = 0;
3362 ** Erase the marker drawn on a matching parenthesis bracket or brace
3363 ** character.
3365 static void eraseFlash(WindowInfo *window)
3367 BufUnhighlight(window->buffer);
3371 ** Search and replace using previously entered search strings (from dialog
3372 ** or selection).
3374 int ReplaceSame(WindowInfo *window, int direction, int searchWrap)
3376 if (NHist < 1) {
3377 XBell(TheDisplay, 0);
3378 return FALSE;
3381 return SearchAndReplace(window, direction, SearchHistory[historyIndex(1)],
3382 ReplaceHistory[historyIndex(1)],
3383 SearchTypeHistory[historyIndex(1)], searchWrap);
3387 ** Search and replace using previously entered search strings (from dialog
3388 ** or selection).
3390 int ReplaceFindSame(WindowInfo *window, int direction, int searchWrap)
3392 if (NHist < 1) {
3393 XBell(TheDisplay, 0);
3394 return FALSE;
3397 return ReplaceAndSearch(window, direction, SearchHistory[historyIndex(1)],
3398 ReplaceHistory[historyIndex(1)],
3399 SearchTypeHistory[historyIndex(1)], searchWrap);
3403 ** Replace selection with "replaceString" and search for string "searchString" in window "window",
3404 ** using algorithm "searchType" and direction "direction"
3406 int ReplaceAndSearch(WindowInfo *window, int direction, const char *searchString,
3407 const char *replaceString, int searchType, int searchWrap)
3409 int startPos = 0, endPos = 0, replaceLen = 0;
3410 int searchExtentBW, searchExtentFW;
3411 int replaced;
3413 /* Save a copy of search and replace strings in the search history */
3414 saveSearchHistory(searchString, replaceString, searchType, FALSE);
3416 replaced = 0;
3418 /* Replace the selected text only if it matches the search string */
3419 if (searchMatchesSelection(window, searchString, searchType,
3420 &startPos, &endPos, &searchExtentBW,
3421 &searchExtentFW)) {
3422 /* replace the text */
3423 if (isRegexType(searchType)) {
3424 char replaceResult[SEARCHMAX+1], *foundString;
3425 foundString = BufGetRange(window->buffer, searchExtentBW,
3426 searchExtentFW+1);
3427 replaceUsingRE(searchString, replaceString, foundString,
3428 startPos-searchExtentBW,
3429 replaceResult, SEARCHMAX, startPos == 0 ? '\0' :
3430 BufGetCharacter(window->buffer, startPos-1),
3431 GetWindowDelimiters(window), defaultRegexFlags(searchType));
3432 XtFree(foundString);
3433 BufReplace(window->buffer, startPos, endPos, replaceResult);
3434 replaceLen = strlen(replaceResult);
3435 } else {
3436 BufReplace(window->buffer, startPos, endPos, replaceString);
3437 replaceLen = strlen(replaceString);
3440 /* Position the cursor so the next search will work correctly based */
3441 /* on the direction of the search */
3442 TextSetCursorPos(window->lastFocus, startPos +
3443 ((direction == SEARCH_FORWARD) ? replaceLen : 0));
3444 replaced = 1;
3447 /* do the search; beeps/dialogs are taken care of */
3448 SearchAndSelect(window, direction, searchString, searchType, searchWrap);
3450 return replaced;
3454 ** Search for string "searchString" in window "window", using algorithm
3455 ** "searchType" and direction "direction", and replace it with "replaceString"
3456 ** Also adds the search and replace strings to the global search history.
3458 int SearchAndReplace(WindowInfo *window, int direction, const char *searchString,
3459 const char *replaceString, int searchType, int searchWrap)
3461 int startPos, endPos, replaceLen, searchExtentBW, searchExtentFW;
3462 int found;
3463 int beginPos, cursorPos;
3465 /* Save a copy of search and replace strings in the search history */
3466 saveSearchHistory(searchString, replaceString, searchType, FALSE);
3468 /* If the text selected in the window matches the search string, */
3469 /* the user is probably using search then replace method, so */
3470 /* replace the selected text regardless of where the cursor is. */
3471 /* Otherwise, search for the string. */
3472 if (!searchMatchesSelection(window, searchString, searchType,
3473 &startPos, &endPos, &searchExtentBW, &searchExtentFW)) {
3474 /* get the position to start the search */
3475 cursorPos = TextGetCursorPos(window->lastFocus);
3476 if (direction == SEARCH_BACKWARD) {
3477 /* use the insert position - 1 for backward searches */
3478 beginPos = cursorPos-1;
3479 } else {
3480 /* use the insert position for forward searches */
3481 beginPos = cursorPos;
3483 /* do the search */
3484 found = SearchWindow(window, direction, searchString, searchType, searchWrap,
3485 beginPos, &startPos, &endPos, &searchExtentBW, &searchExtentFW);
3486 if (!found)
3487 return FALSE;
3490 /* replace the text */
3491 if (isRegexType(searchType)) {
3492 char replaceResult[SEARCHMAX], *foundString;
3493 foundString = BufGetRange(window->buffer, searchExtentBW, searchExtentFW+1);
3494 replaceUsingRE(searchString, replaceString, foundString,
3495 startPos - searchExtentBW,
3496 replaceResult, SEARCHMAX, startPos == 0 ? '\0' :
3497 BufGetCharacter(window->buffer, startPos-1),
3498 GetWindowDelimiters(window), defaultRegexFlags(searchType));
3499 XtFree(foundString);
3500 BufReplace(window->buffer, startPos, endPos, replaceResult);
3501 replaceLen = strlen(replaceResult);
3502 } else {
3503 BufReplace(window->buffer, startPos, endPos, replaceString);
3504 replaceLen = strlen(replaceString);
3507 /* after successfully completing a replace, selected text attracts
3508 attention away from the area of the replacement, particularly
3509 when the selection represents a previous search. so deselect */
3510 BufUnselect(window->buffer);
3512 /* temporarily shut off autoShowInsertPos before setting the cursor
3513 position so MakeSelectionVisible gets a chance to place the replaced
3514 string at a pleasing position on the screen (otherwise, the cursor would
3515 be automatically scrolled on screen and MakeSelectionVisible would do
3516 nothing) */
3517 XtVaSetValues(window->lastFocus, textNautoShowInsertPos, False, NULL);
3518 TextSetCursorPos(window->lastFocus, startPos +
3519 ((direction == SEARCH_FORWARD) ? replaceLen : 0));
3520 MakeSelectionVisible(window, window->lastFocus);
3521 XtVaSetValues(window->lastFocus, textNautoShowInsertPos, True, NULL);
3523 return TRUE;
3527 ** Replace all occurences of "searchString" in "window" with "replaceString"
3528 ** within the current primary selection in "window". Also adds the search and
3529 ** replace strings to the global search history.
3531 int ReplaceInSelection(WindowInfo *window, const char *searchString,
3532 const char *replaceString, int searchType)
3534 int selStart, selEnd, beginPos, startPos, endPos, realOffset, replaceLen;
3535 int found, anyFound, isRect, rectStart, rectEnd, lineStart, cursorPos;
3536 int extentBW, extentFW;
3537 char *fileString;
3538 textBuffer *tempBuf;
3540 /* save a copy of search and replace strings in the search history */
3541 saveSearchHistory(searchString, replaceString, searchType, FALSE);
3543 /* find out where the selection is */
3544 if (!BufGetSelectionPos(window->buffer, &selStart, &selEnd, &isRect,
3545 &rectStart, &rectEnd))
3546 return FALSE;
3548 /* get the selected text */
3549 if (isRect) {
3550 selStart = BufStartOfLine(window->buffer, selStart);
3551 selEnd = BufEndOfLine(window->buffer, selEnd);
3552 fileString = BufGetRange(window->buffer, selStart, selEnd);
3553 } else
3554 fileString = BufGetSelectionText(window->buffer);
3556 /* create a temporary buffer in which to do the replacements to hide the
3557 intermediate steps from the display routines, and so everything can
3558 be undone in a single operation */
3559 tempBuf = BufCreate();
3560 BufSetAll(tempBuf, fileString);
3562 /* search the string and do the replacements in the temporary buffer */
3563 replaceLen = strlen(replaceString);
3564 found = TRUE;
3565 anyFound = FALSE;
3566 beginPos = 0;
3567 cursorPos = 0;
3568 realOffset = 0;
3569 while (found) {
3570 found = SearchString(fileString, searchString, SEARCH_FORWARD,
3571 searchType, FALSE, beginPos, &startPos, &endPos, &extentBW,
3572 &extentFW, GetWindowDelimiters(window));
3573 if (!found)
3574 break;
3575 /* if the selection is rectangular, verify that the found
3576 string is in the rectangle */
3577 if (isRect) {
3578 lineStart = BufStartOfLine(window->buffer, selStart+startPos);
3579 if (BufCountDispChars(window->buffer, lineStart, selStart+startPos) <
3580 rectStart || BufCountDispChars(window->buffer, lineStart,
3581 selStart+endPos) > rectEnd) {
3582 if (fileString[endPos] == '\0')
3583 break;
3584 /* If the match starts before the left boundary of the
3585 selection, and extends past it, we should not continue
3586 search after the end of the (false) match, because we
3587 could miss a valid match starting between the left boundary
3588 and the end of the false match. */
3589 if (BufCountDispChars(window->buffer, lineStart,
3590 selStart+startPos) < rectStart &&
3591 BufCountDispChars(window->buffer, lineStart,
3592 selStart+endPos) > rectStart)
3593 beginPos += 1;
3594 else
3595 beginPos = (startPos == endPos) ? endPos+1 : endPos;
3596 continue;
3599 /* Make sure the match did not start past the end (regular expressions
3600 can consider the artificial end of the range as the end of a line,
3601 and match a fictional whole line beginning there) */
3602 if (startPos == selEnd - selStart) {
3603 found = False;
3604 break;
3606 /* replace the string and compensate for length change */
3607 if (isRegexType(searchType)) {
3608 char replaceResult[SEARCHMAX], *foundString;
3609 foundString = BufGetRange(tempBuf, extentBW+realOffset,
3610 extentFW+realOffset+1);
3611 replaceUsingRE(searchString, replaceString, foundString,
3612 startPos-extentBW,
3613 replaceResult, SEARCHMAX, startPos+realOffset == 0 ? '\0' :
3614 BufGetCharacter(tempBuf, startPos+realOffset-1),
3615 GetWindowDelimiters(window), defaultRegexFlags(searchType));
3616 XtFree(foundString);
3617 BufReplace(tempBuf, startPos+realOffset, endPos+realOffset,
3618 replaceResult);
3619 replaceLen = strlen(replaceResult);
3620 } else
3621 BufReplace(tempBuf, startPos+realOffset, endPos+realOffset,
3622 replaceString);
3623 realOffset += replaceLen - (endPos - startPos);
3624 /* start again after match unless match was empty, then endPos+1 */
3625 beginPos = (startPos == endPos) ? endPos+1 : endPos;
3626 cursorPos = endPos;
3627 anyFound = TRUE;
3628 if (fileString[endPos] == '\0')
3629 break;
3631 XtFree(fileString);
3633 /* if nothing was found, tell user and return */
3634 if (!anyFound) {
3635 if (GetPrefSearchDlogs()) {
3636 /* Avoid bug in Motif 1.1 by putting away search dialog
3637 before DialogF */
3638 if (window->findDlog && XtIsManaged(window->findDlog) &&
3639 !XmToggleButtonGetState(window->findKeepBtn))
3640 XtUnmanageChild(window->findDlog);
3641 if (window->replaceDlog && XtIsManaged(window->replaceDlog) &&
3642 !XmToggleButtonGetState(window->replaceKeepBtn))
3643 unmanageReplaceDialogs(window);
3644 DialogF(DF_INF, window->shell, 1, "String not found",
3645 "String was not found", "OK");
3646 } else
3647 XBell(TheDisplay, 0);
3648 BufFree(tempBuf);
3649 return FALSE;
3652 /* replace the selected range in the real buffer */
3653 fileString = BufGetAll(tempBuf);
3654 BufFree(tempBuf);
3655 BufReplace(window->buffer, selStart, selEnd, fileString);
3656 XtFree(fileString);
3658 /* set the insert point at the end of the last replacement */
3659 TextSetCursorPos(window->lastFocus, selStart + cursorPos + realOffset);
3661 /* leave non-rectangular selections selected (rect. ones after replacement
3662 are less useful since left/right positions are randomly adjusted) */
3663 if (!isRect)
3664 BufSelect(window->buffer, selStart, selEnd + realOffset);
3666 return TRUE;
3670 ** Replace all occurences of "searchString" in "window" with "replaceString".
3671 ** Also adds the search and replace strings to the global search history.
3673 int ReplaceAll(WindowInfo *window, const char *searchString,
3674 const char *replaceString, int searchType)
3676 char *fileString, *newFileString;
3677 int copyStart, copyEnd, replacementLen;
3679 /* reject empty string */
3680 if (*searchString == '\0')
3681 return FALSE;
3683 /* save a copy of search and replace strings in the search history */
3684 saveSearchHistory(searchString, replaceString, searchType, FALSE);
3686 /* get the entire text buffer from the text area widget */
3687 fileString = BufGetAll(window->buffer);
3689 newFileString = ReplaceAllInString(fileString, searchString, replaceString,
3690 searchType, &copyStart, &copyEnd, &replacementLen,
3691 GetWindowDelimiters(window));
3692 XtFree(fileString);
3694 if (newFileString == NULL) {
3695 if (window->multiFileBusy) {
3696 window->replaceFailed = TRUE; /* only needed during multi-file
3697 replacements */
3698 } else if (GetPrefSearchDlogs()) {
3699 if (window->findDlog && XtIsManaged(window->findDlog) &&
3700 !XmToggleButtonGetState(window->findKeepBtn))
3701 XtUnmanageChild(window->findDlog);
3702 if (window->replaceDlog && XtIsManaged(window->replaceDlog) &&
3703 !XmToggleButtonGetState(window->replaceKeepBtn))
3704 unmanageReplaceDialogs(window);
3705 DialogF(DF_INF, window->shell, 1, "String not found",
3706 "String was not found", "OK");
3707 } else
3708 XBell(TheDisplay, 0);
3709 return FALSE;
3712 /* replace the contents of the text widget with the substituted text */
3713 BufReplace(window->buffer, copyStart, copyEnd, newFileString);
3715 /* Move the cursor to the end of the last replacement */
3716 TextSetCursorPos(window->lastFocus, copyStart + replacementLen);
3718 XtFree(newFileString);
3719 return TRUE;
3723 ** Replace all occurences of "searchString" in "inString" with "replaceString"
3724 ** and return an allocated string covering the range between the start of the
3725 ** first replacement (returned in "copyStart", and the end of the last
3726 ** replacement (returned in "copyEnd")
3728 char *ReplaceAllInString(char *inString, const char *searchString,
3729 const char *replaceString, int searchType, int *copyStart,
3730 int *copyEnd, int *replacementLength, const char *delimiters)
3732 int beginPos, startPos, endPos, lastEndPos;
3733 int found, nFound, removeLen, replaceLen, copyLen, addLen;
3734 char *outString, *fillPtr;
3735 int searchExtentBW, searchExtentFW;
3737 /* reject empty string */
3738 if (*searchString == '\0')
3739 return NULL;
3741 /* rehearse the search first to determine the size of the buffer needed
3742 to hold the substituted text. No substitution done here yet */
3743 replaceLen = strlen(replaceString);
3744 found = TRUE;
3745 nFound = 0;
3746 removeLen = 0;
3747 addLen = 0;
3748 beginPos = 0;
3749 *copyStart = -1;
3750 while (found) {
3751 found = SearchString(inString, searchString, SEARCH_FORWARD, searchType,
3752 FALSE, beginPos, &startPos, &endPos, &searchExtentBW,
3753 &searchExtentFW, delimiters);
3754 if (found) {
3755 if (*copyStart < 0)
3756 *copyStart = startPos;
3757 *copyEnd = endPos;
3758 /* start next after match unless match was empty, then endPos+1 */
3759 beginPos = (startPos == endPos) ? endPos+1 : endPos;
3760 nFound++;
3761 removeLen += endPos - startPos;
3762 if (isRegexType(searchType)) {
3763 char replaceResult[SEARCHMAX];
3764 replaceUsingRE(searchString, replaceString, &inString[searchExtentBW],
3765 startPos-searchExtentBW,
3766 replaceResult, SEARCHMAX, startPos == 0 ? '\0' :
3767 inString[startPos-1], delimiters,
3768 defaultRegexFlags(searchType));
3769 addLen += strlen(replaceResult);
3770 } else
3771 addLen += replaceLen;
3772 if (inString[endPos] == '\0')
3773 break;
3776 if (nFound == 0)
3777 return NULL;
3779 /* Allocate a new buffer to hold all of the new text between the first
3780 and last substitutions */
3781 copyLen = *copyEnd - *copyStart;
3782 outString = XtMalloc(copyLen - removeLen + addLen + 1);
3784 /* Scan through the text buffer again, substituting the replace string
3785 and copying the part between replaced text to the new buffer */
3786 found = TRUE;
3787 beginPos = 0;
3788 lastEndPos = 0;
3789 fillPtr = outString;
3790 while (found) {
3791 found = SearchString(inString, searchString, SEARCH_FORWARD, searchType,
3792 FALSE, beginPos, &startPos, &endPos, &searchExtentBW,
3793 &searchExtentFW, delimiters);
3794 if (found) {
3795 if (beginPos != 0) {
3796 memcpy(fillPtr, &inString[lastEndPos], startPos - lastEndPos);
3797 fillPtr += startPos - lastEndPos;
3799 if (isRegexType(searchType)) {
3800 char replaceResult[SEARCHMAX];
3801 replaceUsingRE(searchString, replaceString, &inString[searchExtentBW],
3802 startPos-searchExtentBW,
3803 replaceResult, SEARCHMAX, startPos == 0 ? '\0' :
3804 inString[startPos-1], delimiters,
3805 defaultRegexFlags(searchType));
3806 replaceLen = strlen(replaceResult);
3807 memcpy(fillPtr, replaceResult, replaceLen);
3808 } else {
3809 memcpy(fillPtr, replaceString, replaceLen);
3811 fillPtr += replaceLen;
3812 lastEndPos = endPos;
3813 /* start next after match unless match was empty, then endPos+1 */
3814 beginPos = (startPos == endPos) ? endPos+1 : endPos;
3815 if (inString[endPos] == '\0')
3816 break;
3819 *fillPtr = '\0';
3820 *replacementLength = fillPtr - outString;
3821 return outString;
3825 ** If this is an incremental search and BeepOnSearchWrap is on:
3826 ** Emit a beep if the search wrapped over BOF/EOF compared to
3827 ** the last startPos of the current incremental search.
3829 static void iSearchTryBeepOnWrap(WindowInfo *window, int direction,
3830 int beginPos, int startPos)
3832 if(GetPrefBeepOnSearchWrap()) {
3833 if(direction == SEARCH_FORWARD) {
3834 if( (startPos >= beginPos && window->iSearchLastBeginPos < beginPos)
3835 ||(startPos < beginPos && window->iSearchLastBeginPos >= beginPos))
3836 XBell(TheDisplay, 0);
3837 } else {
3838 if( (startPos <= beginPos && window->iSearchLastBeginPos > beginPos)
3839 ||(startPos > beginPos && window->iSearchLastBeginPos <= beginPos))
3840 XBell(TheDisplay, 0);
3846 ** Search the text in "window", attempting to match "searchString"
3848 int SearchWindow(WindowInfo *window, int direction, const char *searchString,
3849 int searchType, int searchWrap, int beginPos, int *startPos,
3850 int *endPos, int *extentBW, int *extentFW)
3852 char *fileString;
3853 int found, resp, fileEnd = window->buffer->length - 1, outsideBounds;
3855 /* reject empty string */
3856 if (*searchString == '\0')
3857 return FALSE;
3859 /* get the entire text buffer from the text area widget */
3860 fileString = BufGetAll(window->buffer);
3862 /* If we're already outside the boundaries, we must consider wrapping
3863 immediately (Note: fileEnd+1 is a valid starting position. Consider
3864 searching for $ at the end of a file ending with \n.) */
3865 if ((direction == SEARCH_FORWARD && beginPos > fileEnd + 1)
3866 || (direction == SEARCH_BACKWARD && beginPos < 0))
3868 outsideBounds = TRUE;
3869 } else
3871 outsideBounds = FALSE;
3874 /* search the string copied from the text area widget, and present
3875 dialogs, or just beep. iSearchStartPos is not a perfect indicator that
3876 an incremental search is in progress. A parameter would be better. */
3877 if (window->iSearchStartPos == -1) { /* normal search */
3878 found = !outsideBounds &&
3879 SearchString(fileString, searchString, direction, searchType,
3880 FALSE, beginPos, startPos, endPos, extentBW, extentFW,
3881 GetWindowDelimiters(window));
3882 /* Avoid Motif 1.1 bug by putting away search dialog before DialogF */
3883 if (window->findDlog && XtIsManaged(window->findDlog) &&
3884 !XmToggleButtonGetState(window->findKeepBtn))
3885 XtUnmanageChild(window->findDlog);
3886 if (window->replaceDlog && XtIsManaged(window->replaceDlog) &&
3887 !XmToggleButtonGetState(window->replaceKeepBtn))
3888 unmanageReplaceDialogs(window);
3889 if (!found) {
3890 if (searchWrap) {
3891 if (direction == SEARCH_FORWARD && beginPos != 0) {
3892 if(GetPrefBeepOnSearchWrap()) {
3893 XBell(TheDisplay, 0);
3894 } else if (GetPrefSearchDlogs()) {
3895 resp = DialogF(DF_QUES, window->shell, 2, "Wrap Search",
3896 "Continue search from\nbeginning of file?",
3897 "Continue", "Cancel");
3898 if (resp == 2) {
3899 XtFree(fileString);
3900 return False;
3903 found = SearchString(fileString, searchString, direction,
3904 searchType, FALSE, 0, startPos, endPos, extentBW,
3905 extentFW, GetWindowDelimiters(window));
3906 } else if (direction == SEARCH_BACKWARD && beginPos != fileEnd) {
3907 if(GetPrefBeepOnSearchWrap()) {
3908 XBell(TheDisplay, 0);
3909 } else if (GetPrefSearchDlogs()) {
3910 resp = DialogF(DF_QUES, window->shell, 2, "Wrap Search",
3911 "Continue search\nfrom end of file?", "Continue",
3912 "Cancel");
3913 if (resp == 2) {
3914 XtFree(fileString);
3915 return False;
3918 found = SearchString(fileString, searchString, direction,
3919 searchType, FALSE, fileEnd + 1, startPos, endPos, extentBW,
3920 extentFW, GetWindowDelimiters(window));
3923 if (!found) {
3924 if (GetPrefSearchDlogs()) {
3925 DialogF(DF_INF, window->shell, 1, "String not found",
3926 "String was not found","OK");
3927 } else {
3928 XBell(TheDisplay, 0);
3932 } else { /* incremental search */
3933 if (outsideBounds && searchWrap) {
3934 if (direction == SEARCH_FORWARD) beginPos = 0;
3935 else beginPos = fileEnd+1;
3936 outsideBounds = FALSE;
3938 found = !outsideBounds &&
3939 SearchString(fileString, searchString, direction,
3940 searchType, searchWrap, beginPos, startPos, endPos,
3941 extentBW, extentFW, GetWindowDelimiters(window));
3942 if (found) {
3943 iSearchTryBeepOnWrap(window, direction, beginPos, *startPos);
3944 } else
3945 XBell(TheDisplay, 0);
3948 /* Free the text buffer copy returned from BufGetAll */
3949 XtFree(fileString);
3951 return found;
3955 ** Search the null terminated string "string" for "searchString", beginning at
3956 ** "beginPos". Returns the boundaries of the match in "startPos" and "endPos".
3957 ** searchExtentBW and searchExtentFW return the backwardmost and forwardmost
3958 ** positions used to make the match, which are usually startPos and endPos,
3959 ** but may extend further if positive lookahead or lookbehind was used in
3960 ** a regular expression match. "delimiters" may be used to provide an
3961 ** alternative set of word delimiters for regular expression "<" and ">"
3962 ** characters, or simply passed as null for the default delimiter set.
3964 int SearchString(const char *string, const char *searchString, int direction,
3965 int searchType, int wrap, int beginPos, int *startPos, int *endPos,
3966 int *searchExtentBW, int *searchExtentFW, const char *delimiters)
3968 switch (searchType) {
3969 case SEARCH_CASE_SENSE_WORD:
3970 return searchLiteralWord(string, searchString, TRUE, direction, wrap,
3971 beginPos, startPos, endPos, delimiters);
3972 case SEARCH_LITERAL_WORD:
3973 return searchLiteralWord(string, searchString, FALSE, direction, wrap,
3974 beginPos, startPos, endPos, delimiters);
3975 case SEARCH_CASE_SENSE:
3976 return searchLiteral(string, searchString, TRUE, direction, wrap,
3977 beginPos, startPos, endPos, searchExtentBW,
3978 searchExtentFW);
3979 case SEARCH_LITERAL:
3980 return searchLiteral(string, searchString, FALSE, direction, wrap,
3981 beginPos, startPos, endPos, searchExtentBW, searchExtentFW);
3982 case SEARCH_REGEX:
3983 return searchRegex(string, searchString, direction, wrap,
3984 beginPos, startPos, endPos, searchExtentBW, searchExtentFW,
3985 delimiters, REDFLT_STANDARD);
3986 case SEARCH_REGEX_NOCASE:
3987 return searchRegex(string, searchString, direction, wrap,
3988 beginPos, startPos, endPos, searchExtentBW, searchExtentFW,
3989 delimiters, REDFLT_CASE_INSENSITIVE);
3991 return FALSE; /* never reached, just makes compilers happy */
3995 ** Parses a search type description string. If the string contains a valid
3996 ** search type description, returns TRUE and writes the corresponding
3997 ** SearchType in searchType. Returns FALSE and leaves searchType untouched
3998 ** otherwise. (Originally written by Markus Schwarzenberg; slightly adapted).
4000 int StringToSearchType(const char * string, int *searchType)
4002 int i;
4003 for (i = 0; searchTypeStrings[i]; i++) {
4004 if (!strcmp(string, searchTypeStrings[i])) {
4005 break;
4008 if (!searchTypeStrings[i]) {
4009 return FALSE;
4011 *searchType = i;
4012 return TRUE;
4016 ** Searches for whole words (Markus Schwarzenberg).
4018 ** If the first/last character of `searchString' is a "normal
4019 ** word character" (not contained in `delimiters', not a whitespace)
4020 ** then limit search to strings, who's next left/next right character
4021 ** is contained in `delimiters' or is a whitespace or text begin or end.
4023 ** If the first/last character of `searchString' itself is contained
4024 ** in delimiters or is a white space, then the neighbour character of the
4025 ** first/last character will not be checked, just a simple match
4026 ** will suffice in that case.
4029 static int searchLiteralWord(const char *string, const char *searchString, int caseSense,
4030 int direction, int wrap, int beginPos, int *startPos, int *endPos,
4031 const char * delimiters)
4033 /* This is critical code for the speed of searches. */
4034 /* For efficiency, we define the macro DOSEARCH with the guts of the search */
4035 /* routine and repeat it, changing the parameters of the outer loop for the */
4036 /* searching, forwards, backwards, and before and after the begin point */
4037 #define DOSEARCHWORD() \
4038 if (*filePtr == *ucString || *filePtr == *lcString) { \
4039 /* matched first character */ \
4040 ucPtr = ucString; \
4041 lcPtr = lcString; \
4042 tempPtr = filePtr; \
4043 while (*tempPtr == *ucPtr || *tempPtr == *lcPtr) { \
4044 tempPtr++; ucPtr++; lcPtr++; \
4045 if ( *ucPtr == 0 /* matched whole string */ \
4046 && (cignore_R ||\
4047 isspace((unsigned char)*tempPtr) ||\
4048 strchr(delimiters, *tempPtr) ) \
4049 /* next char right delimits word ? */ \
4050 && (cignore_L ||\
4051 filePtr==string || /* border case */ \
4052 isspace((unsigned char)filePtr[-1]) ||\
4053 strchr(delimiters,filePtr[-1]) ))\
4054 /* next char left delimits word ? */ { \
4055 *startPos = filePtr - string; \
4056 *endPos = tempPtr - string; \
4057 return TRUE; \
4062 register const char *filePtr, *tempPtr, *ucPtr, *lcPtr;
4063 char lcString[SEARCHMAX], ucString[SEARCHMAX];
4065 int cignore_L=0, cignore_R=0;
4067 /* SEARCHMAX was fine in the original NEdit, but it should be done away
4068 with now that searching can be done from macros without limits.
4069 Returning search failure here is cheating users. This limit is not
4070 documented. */
4071 if (strlen(searchString) >= SEARCHMAX)
4072 return FALSE;
4074 /* If there is no language mode, we use the default list of delimiters */
4075 if (delimiters==NULL) delimiters = GetPrefDelimiters();
4077 if ( isspace((unsigned char)*searchString)
4078 || strchr(delimiters, *searchString))
4079 cignore_L=1;
4081 if ( isspace((unsigned char)searchString[strlen(searchString)-1])
4082 || strchr(delimiters, searchString[strlen(searchString)-1]) )
4083 cignore_R=1;
4085 if (caseSense) {
4086 strcpy(ucString, searchString);
4087 strcpy(lcString, searchString);
4088 } else {
4089 upCaseString(ucString, searchString);
4090 downCaseString(lcString, searchString);
4093 if (direction == SEARCH_FORWARD) {
4094 /* search from beginPos to end of string */
4095 for (filePtr=string+beginPos; *filePtr!=0; filePtr++) {
4096 DOSEARCHWORD()
4098 if (!wrap)
4099 return FALSE;
4101 /* search from start of file to beginPos */
4102 for (filePtr=string; filePtr<=string+beginPos; filePtr++) {
4103 DOSEARCHWORD()
4105 return FALSE;
4106 } else {
4107 /* SEARCH_BACKWARD */
4108 /* search from beginPos to start of file. A negative begin pos */
4109 /* says begin searching from the far end of the file */
4110 if (beginPos >= 0) {
4111 for (filePtr=string+beginPos; filePtr>=string; filePtr--) {
4112 DOSEARCHWORD()
4115 if (!wrap)
4116 return FALSE;
4117 /* search from end of file to beginPos */
4118 /*... this strlen call is extreme inefficiency, but it's not obvious */
4119 /* how to get the text string length from the text widget (under 1.1)*/
4120 for (filePtr=string+strlen(string); filePtr>=string+beginPos; filePtr--) {
4121 DOSEARCHWORD()
4123 return FALSE;
4128 static int searchLiteral(const char *string, const char *searchString, int caseSense,
4129 int direction, int wrap, int beginPos, int *startPos, int *endPos,
4130 int *searchExtentBW, int *searchExtentFW)
4132 /* This is critical code for the speed of searches. */
4133 /* For efficiency, we define the macro DOSEARCH with the guts of the search */
4134 /* routine and repeat it, changing the parameters of the outer loop for the */
4135 /* searching, forwards, backwards, and before and after the begin point */
4136 #define DOSEARCH() \
4137 if (*filePtr == *ucString || *filePtr == *lcString) { \
4138 /* matched first character */ \
4139 ucPtr = ucString; \
4140 lcPtr = lcString; \
4141 tempPtr = filePtr; \
4142 while (*tempPtr == *ucPtr || *tempPtr == *lcPtr) { \
4143 tempPtr++; ucPtr++; lcPtr++; \
4144 if (*ucPtr == 0) { \
4145 /* matched whole string */ \
4146 *startPos = filePtr - string; \
4147 *endPos = tempPtr - string; \
4148 if (searchExtentBW != NULL) \
4149 *searchExtentBW = *startPos; \
4150 if (searchExtentFW != NULL) \
4151 *searchExtentFW = *endPos; \
4152 return TRUE; \
4157 register const char *filePtr, *tempPtr, *ucPtr, *lcPtr;
4158 char lcString[SEARCHMAX], ucString[SEARCHMAX];
4160 /* SEARCHMAX was fine in the original NEdit, but it should be done away with
4161 now that searching can be done from macros without limits. Returning
4162 search failure here is cheating users. This limit is not documented. */
4163 if (strlen(searchString) >= SEARCHMAX)
4164 return FALSE;
4166 if (caseSense) {
4167 strcpy(ucString, searchString);
4168 strcpy(lcString, searchString);
4169 } else {
4170 upCaseString(ucString, searchString);
4171 downCaseString(lcString, searchString);
4174 if (direction == SEARCH_FORWARD) {
4175 /* search from beginPos to end of string */
4176 for (filePtr=string+beginPos; *filePtr!=0; filePtr++) {
4177 DOSEARCH()
4179 if (!wrap)
4180 return FALSE;
4181 /* search from start of file to beginPos */
4182 for (filePtr=string; filePtr<=string+beginPos; filePtr++) {
4183 DOSEARCH()
4185 return FALSE;
4186 } else {
4187 /* SEARCH_BACKWARD */
4188 /* search from beginPos to start of file. A negative begin pos */
4189 /* says begin searching from the far end of the file */
4190 if (beginPos >= 0) {
4191 for (filePtr=string+beginPos; filePtr>=string; filePtr--) {
4192 DOSEARCH()
4195 if (!wrap)
4196 return FALSE;
4197 /* search from end of file to beginPos */
4198 /*... this strlen call is extreme inefficiency, but it's not obvious */
4199 /* how to get the text string length from the text widget (under 1.1)*/
4200 for (filePtr=string+strlen(string);
4201 filePtr>=string+beginPos; filePtr--) {
4202 DOSEARCH()
4204 return FALSE;
4208 static int searchRegex(const char *string, const char *searchString, int direction,
4209 int wrap, int beginPos, int *startPos, int *endPos, int *searchExtentBW,
4210 int *searchExtentFW, const char *delimiters, int defaultFlags)
4212 if (direction == SEARCH_FORWARD)
4213 return forwardRegexSearch(string, searchString, wrap,
4214 beginPos, startPos, endPos, searchExtentBW, searchExtentFW,
4215 delimiters, defaultFlags);
4216 else
4217 return backwardRegexSearch(string, searchString, wrap,
4218 beginPos, startPos, endPos, searchExtentBW, searchExtentFW,
4219 delimiters, defaultFlags);
4222 static int forwardRegexSearch(const char *string, const char *searchString, int wrap,
4223 int beginPos, int *startPos, int *endPos, int *searchExtentBW,
4224 int *searchExtentFW, const char *delimiters, int defaultFlags)
4226 regexp *compiledRE = NULL;
4227 char *compileMsg;
4229 /* compile the search string for searching with ExecRE. Note that
4230 this does not process errors from compiling the expression. It
4231 assumes that the expression was checked earlier. */
4232 compiledRE = CompileRE(searchString, &compileMsg, defaultFlags);
4233 if (compiledRE == NULL)
4234 return FALSE;
4236 /* search from beginPos to end of string */
4237 if (ExecRE(compiledRE, NULL, string + beginPos, NULL, FALSE,
4238 beginPos==0 ? '\0' : string[beginPos-1], '\0', delimiters, string)) {
4239 *startPos = compiledRE->startp[0] - string;
4240 *endPos = compiledRE->endp[0] - string;
4241 if (searchExtentFW != NULL)
4242 *searchExtentFW = compiledRE->extentpFW - string;
4243 if (searchExtentBW != NULL)
4244 *searchExtentBW = compiledRE->extentpBW - string;
4245 free((char *)compiledRE);
4246 return TRUE;
4249 /* if wrap turned off, we're done */
4250 if (!wrap) {
4251 free((char *)compiledRE);
4252 return FALSE;
4255 /* search from the beginning of the string to beginPos */
4256 if (ExecRE(compiledRE, NULL, string, string + beginPos, FALSE, '\0',
4257 string[beginPos], delimiters, string)) {
4258 *startPos = compiledRE->startp[0] - string;
4259 *endPos = compiledRE->endp[0] - string;
4260 if (searchExtentFW != NULL)
4261 *searchExtentFW = compiledRE->extentpFW - string;
4262 if (searchExtentBW != NULL)
4263 *searchExtentBW = compiledRE->extentpBW - string;
4264 free((char *)compiledRE);
4265 return TRUE;
4268 free((char *)compiledRE);
4269 return FALSE;
4272 static int backwardRegexSearch(const char *string, const char *searchString, int wrap,
4273 int beginPos, int *startPos, int *endPos, int *searchExtentBW,
4274 int *searchExtentFW, const char *delimiters, int defaultFlags)
4276 regexp *compiledRE = NULL;
4277 char *compileMsg;
4278 int length;
4280 /* compile the search string for searching with ExecRE */
4281 compiledRE = CompileRE(searchString, &compileMsg, defaultFlags);
4282 if (compiledRE == NULL)
4283 return FALSE;
4285 /* search from beginPos to start of file. A negative begin pos */
4286 /* says begin searching from the far end of the file. */
4287 if (beginPos >= 0) {
4288 if (ExecRE(compiledRE, NULL, string, string + beginPos, TRUE, '\0',
4289 '\0', delimiters, string)) {
4290 *startPos = compiledRE->startp[0] - string;
4291 *endPos = compiledRE->endp[0] - string;
4292 if (searchExtentFW != NULL)
4293 *searchExtentFW = compiledRE->extentpFW - string;
4294 if (searchExtentBW != NULL)
4295 *searchExtentBW = compiledRE->extentpBW - string;
4296 free((char *)compiledRE);
4297 return TRUE;
4301 /* if wrap turned off, we're done */
4302 if (!wrap) {
4303 free((char *)compiledRE);
4304 return FALSE;
4307 /* search from the end of the string to beginPos */
4308 if (beginPos < 0)
4309 beginPos = 0;
4310 length = strlen(string); /* sadly, this means scanning entire string */
4311 if (ExecRE(compiledRE, NULL, string + beginPos, string + length, TRUE,
4312 beginPos==0 ? '\0' : string[beginPos-1], '\0', delimiters, string)) {
4313 *startPos = compiledRE->startp[0] - string;
4314 *endPos = compiledRE->endp[0] - string;
4315 if (searchExtentFW != NULL)
4316 *searchExtentFW = compiledRE->extentpFW - string;
4317 if (searchExtentBW != NULL)
4318 *searchExtentBW = compiledRE->extentpBW - string;
4319 free((char *)compiledRE);
4320 return TRUE;
4322 free((char *)compiledRE);
4323 return FALSE;
4326 static void upCaseString(char *outString, const char *inString)
4328 char *outPtr;
4329 const char *inPtr;
4331 for (outPtr=outString, inPtr=inString; *inPtr!=0; inPtr++, outPtr++) {
4332 *outPtr = toupper((unsigned char)*inPtr);
4334 *outPtr = 0;
4337 static void downCaseString(char *outString, const char *inString)
4339 char *outPtr;
4340 const char *inPtr;
4342 for (outPtr=outString, inPtr=inString; *inPtr!=0; inPtr++, outPtr++) {
4343 *outPtr = tolower((unsigned char)*inPtr);
4345 *outPtr = 0;
4349 ** resetFindTabGroup & resetReplaceTabGroup are really gruesome kludges to
4350 ** set the keyboard traversal. XmProcessTraversal does not work at
4351 ** all on these dialogs. ...It seems to have started working around
4352 ** Motif 1.1.2
4354 static void resetFindTabGroup(WindowInfo *window)
4356 XmProcessTraversal(window->findText, XmTRAVERSE_CURRENT);
4358 static void resetReplaceTabGroup(WindowInfo *window)
4360 XmProcessTraversal(window->replaceText, XmTRAVERSE_CURRENT);
4364 ** Return TRUE if "searchString" exactly matches the text in the window's
4365 ** current primary selection using search algorithm "searchType". If true,
4366 ** also return the position of the selection in "left" and "right".
4368 static int searchMatchesSelection(WindowInfo *window, const char *searchString,
4369 int searchType, int *left, int *right, int *searchExtentBW,
4370 int *searchExtentFW)
4372 int selLen, selStart, selEnd, startPos, endPos, extentBW, extentFW, beginPos;
4373 int regexLookContext = isRegexType(searchType) ? 1000 : 0;
4374 char *string;
4375 int found, isRect, rectStart, rectEnd, lineStart = 0;
4377 /* find length of selection, give up on no selection or too long */
4378 if (!BufGetEmptySelectionPos(window->buffer, &selStart, &selEnd, &isRect,
4379 &rectStart, &rectEnd))
4380 return FALSE;
4381 if (selEnd - selStart > SEARCHMAX)
4382 return FALSE;
4384 /* if the selection is rectangular, don't match if it spans lines */
4385 if (isRect) {
4386 lineStart = BufStartOfLine(window->buffer, selStart);
4387 if (lineStart != BufStartOfLine(window->buffer, selEnd))
4388 return FALSE;
4391 /* get the selected text plus some additional context for regular
4392 expression lookahead */
4393 if (isRect) {
4394 int stringStart = lineStart + rectStart - regexLookContext;
4395 if (stringStart < 0) stringStart = 0;
4396 string = BufGetRange(window->buffer, stringStart,
4397 lineStart + rectEnd + regexLookContext);
4398 selLen = rectEnd - rectStart;
4399 beginPos = lineStart + rectStart - stringStart;
4400 } else {
4401 int stringStart = selStart - regexLookContext;
4402 if (stringStart < 0) stringStart = 0;
4403 string = BufGetRange(window->buffer, stringStart,
4404 selEnd + regexLookContext);
4405 selLen = selEnd - selStart;
4406 beginPos = selStart - stringStart;
4408 if (*string == '\0') {
4409 XtFree(string);
4410 return FALSE;
4413 /* search for the string in the selection (we are only interested */
4414 /* in an exact match, but the procedure SearchString does important */
4415 /* stuff like applying the correct matching algorithm) */
4416 found = SearchString(string, searchString, SEARCH_FORWARD, searchType,
4417 FALSE, beginPos, &startPos, &endPos, &extentBW, &extentFW,
4418 GetWindowDelimiters(window));
4419 XtFree(string);
4421 /* decide if it is an exact match */
4422 if (!found)
4423 return FALSE;
4424 if (startPos != beginPos || endPos - beginPos != selLen )
4425 return FALSE;
4427 /* return the start and end of the selection */
4428 if (isRect)
4429 GetSimpleSelection(window->buffer, left, right);
4430 else {
4431 *left = selStart;
4432 *right = selEnd;
4434 if (searchExtentBW != NULL)
4435 *searchExtentBW = *left - (startPos - extentBW);
4437 if (searchExtentFW != NULL)
4438 *searchExtentFW = *right + extentFW - endPos;
4439 return TRUE;
4443 ** Substitutes a replace string for a string that was matched using a
4444 ** regular expression. This was added later and is rather ineficient
4445 ** because instead of using the compiled regular expression that was used
4446 ** to make the match in the first place, it re-compiles the expression
4447 ** and redoes the search on the already-matched string. This allows the
4448 ** code to continue using strings to represent the search and replace
4449 ** items.
4451 static void replaceUsingRE(const char *searchStr, const char *replaceStr,
4452 const char *sourceStr, int beginPos, char *destStr,
4453 int maxDestLen, int prevChar, const char *delimiters, int defaultFlags)
4455 regexp *compiledRE;
4456 char *compileMsg;
4458 compiledRE = CompileRE(searchStr, &compileMsg, defaultFlags);
4459 ExecRE(compiledRE, NULL, sourceStr+beginPos, NULL, False, prevChar,
4460 '\0', delimiters, sourceStr);
4461 SubstituteRE(compiledRE, replaceStr, destStr, maxDestLen);
4462 free((char *)compiledRE);
4466 ** Store the search and replace strings, and search type for later recall.
4467 ** If replaceString is NULL, duplicate the last replaceString used.
4468 ** Contiguous incremental searches share the same history entry (each new
4469 ** search modifies the current search string, until a non-incremental search
4470 ** is made. To mark the end of an incremental search, call saveSearchHistory
4471 ** again with an empty search string and isIncremental==False.
4473 static void saveSearchHistory(const char *searchString,
4474 const char *replaceString, int searchType, int isIncremental)
4476 char *sStr, *rStr;
4477 static int currentItemIsIncremental = FALSE;
4478 WindowInfo *w;
4480 /* Cancel accumulation of contiguous incremental searches (even if the
4481 information is not worthy of saving) if search is not incremental */
4482 if (!isIncremental)
4483 currentItemIsIncremental = FALSE;
4485 /* Don't save empty search strings */
4486 if (searchString[0] == '\0')
4487 return;
4489 /* If replaceString is NULL, duplicate the last one (if any) */
4490 if (replaceString == NULL)
4491 replaceString = NHist >= 1 ? ReplaceHistory[historyIndex(1)] : "";
4493 /* Compare the current search and replace strings against the saved ones.
4494 If they are identical, don't bother saving */
4495 if (NHist >= 1 && searchType == SearchTypeHistory[historyIndex(1)] &&
4496 !strcmp(SearchHistory[historyIndex(1)], searchString) &&
4497 !strcmp(ReplaceHistory[historyIndex(1)], replaceString)) {
4498 return;
4501 /* If the current history item came from an incremental search, and the
4502 new one is also incremental, just update the entry */
4503 if (currentItemIsIncremental && isIncremental) {
4504 XtFree(SearchHistory[historyIndex(1)]);
4505 SearchHistory[historyIndex(1)] = XtNewString(searchString);
4506 SearchTypeHistory[historyIndex(1)] = searchType;
4507 return;
4509 currentItemIsIncremental = isIncremental;
4511 if (NHist==0) {
4512 for (w=WindowList; w!=NULL; w=w->next) {
4513 XtSetSensitive(w->findAgainItem, True);
4514 XtSetSensitive(w->replaceFindAgainItem, True);
4515 XtSetSensitive(w->replaceAgainItem, True);
4519 /* If there are more than MAX_SEARCH_HISTORY strings saved, recycle
4520 some space, free the entry that's about to be overwritten */
4521 if (NHist == MAX_SEARCH_HISTORY) {
4522 XtFree(SearchHistory[HistStart]);
4523 XtFree(ReplaceHistory[HistStart]);
4524 } else
4525 NHist++;
4527 /* Allocate and copy the search and replace strings and add them to the
4528 circular buffers at HistStart, bump the buffer pointer to next pos. */
4529 sStr = XtMalloc(strlen(searchString) + 1);
4530 rStr = XtMalloc(strlen(replaceString) + 1);
4531 strcpy(sStr, searchString);
4532 strcpy(rStr, replaceString);
4533 SearchHistory[HistStart] = sStr;
4534 ReplaceHistory[HistStart] = rStr;
4535 SearchTypeHistory[HistStart] = searchType;
4536 HistStart++;
4537 if (HistStart >= MAX_SEARCH_HISTORY)
4538 HistStart = 0;
4542 ** return an index into the circular buffer arrays of history information
4543 ** for search strings, given the number of saveSearchHistory cycles back from
4544 ** the current time.
4547 static int historyIndex(int nCycles)
4549 int index;
4551 if (nCycles > NHist || nCycles <= 0)
4552 return -1;
4553 index = HistStart - nCycles;
4554 if (index < 0)
4555 index = MAX_SEARCH_HISTORY + index;
4556 return index;
4560 ** Return a pointer to the string describing search type for search action
4561 ** routine parameters (see menu.c for processing of action routines)
4563 static char *searchTypeArg(int searchType)
4565 if (0 <= searchType && searchType < N_SEARCH_TYPES) {
4566 return searchTypeStrings[searchType];
4568 return searchTypeStrings[SEARCH_LITERAL];
4572 ** Return a pointer to the string describing search wrap for search action
4573 ** routine parameters (see menu.c for processing of action routines)
4575 static char *searchWrapArg(int searchWrap)
4577 if (searchWrap) {
4578 return "wrap";
4580 return "nowrap";
4584 ** Return a pointer to the string describing search direction for search action
4585 ** routine parameters (see menu.c for processing of action routines)
4587 static char *directionArg(int direction)
4589 if (direction == SEARCH_BACKWARD)
4590 return "backward";
4591 return "forward";
4595 ** Checks whether a search mode in one of the regular expression modes.
4597 static int isRegexType(int searchType)
4599 return searchType == SEARCH_REGEX || searchType == SEARCH_REGEX_NOCASE;
4603 ** Returns the default flags for regular expression matching, given a
4604 ** regular expression search mode.
4606 static int defaultRegexFlags(int searchType)
4608 switch (searchType) {
4609 case SEARCH_REGEX:
4610 return REDFLT_STANDARD;
4611 case SEARCH_REGEX_NOCASE:
4612 return REDFLT_CASE_INSENSITIVE;
4613 default:
4614 /* We should never get here, but just in case ... */
4615 return REDFLT_STANDARD;
4620 ** The next 4 callbacks handle the states of find/replace toggle
4621 ** buttons, which depend on the state of the "Regex" button, and the
4622 ** sensitivity of the Whole Word buttons.
4623 ** Callbacks are necessary for both "Regex" and "Case Sensitive"
4624 ** buttons to make sure the states are saved even after a cancel operation.
4626 ** If sticky case sensitivity is requested, the behaviour is as follows:
4627 ** The first time "Regular expression" is checked, "Match case" gets
4628 ** checked too. Thereafter, checking or unchecking "Regular expression"
4629 ** restores the "Match case" button to the setting it had the last
4630 ** time when literals or REs where used.
4631 ** Without sticky behaviour, the state of the Regex button doesn't influence
4632 ** the state of the Case Sensitive button.
4634 ** Independently, the state of the buttons is always restored to the
4635 ** default state when a dialog is popped up, and when the user returns
4636 ** from stepping through the search history.
4638 ** NOTE: similar call-backs exist for the incremental search bar; see window.c.
4640 static void findRegExpToggleCB(Widget w, XtPointer clientData, XtPointer callData)
4642 WindowInfo * window = (WindowInfo *) clientData;
4643 int searchRegex = XmToggleButtonGetState(w);
4644 int searchCaseSense = XmToggleButtonGetState(window->findCaseToggle);
4646 /* In sticky mode, restore the state of the Case Sensitive button */
4647 if(GetPrefStickyCaseSenseBtn()) {
4648 if(searchRegex) {
4649 window->findLastLiteralCase = searchCaseSense;
4650 XmToggleButtonSetState(window->findCaseToggle,
4651 window->findLastRegexCase, False);
4652 } else {
4653 window->findLastRegexCase = searchCaseSense;
4654 XmToggleButtonSetState(window->findCaseToggle,
4655 window->findLastLiteralCase, False);
4658 /* make the Whole Word button insensitive for regex searches */
4659 XtSetSensitive(window->findWordToggle, !searchRegex);
4662 static void replaceRegExpToggleCB(Widget w, XtPointer clientData, XtPointer callData)
4664 WindowInfo * window = (WindowInfo *) clientData;
4665 int searchRegex = XmToggleButtonGetState(w);
4666 int searchCaseSense = XmToggleButtonGetState(window->replaceCaseToggle);
4668 /* In sticky mode, restore the state of the Case Sensitive button */
4669 if(GetPrefStickyCaseSenseBtn()) {
4670 if(searchRegex) {
4671 window->replaceLastLiteralCase = searchCaseSense;
4672 XmToggleButtonSetState(window->replaceCaseToggle,
4673 window->replaceLastRegexCase, False);
4674 } else {
4675 window->replaceLastRegexCase = searchCaseSense;
4676 XmToggleButtonSetState(window->replaceCaseToggle,
4677 window->replaceLastLiteralCase, False);
4680 /* make the Whole Word button insensitive for regex searches */
4681 XtSetSensitive(window->replaceWordToggle, !searchRegex);
4684 static void iSearchRegExpToggleCB(Widget w, XtPointer clientData, XtPointer callData)
4686 WindowInfo * window = (WindowInfo *) clientData;
4687 int searchRegex = XmToggleButtonGetState(w);
4688 int searchCaseSense = XmToggleButtonGetState(window->iSearchCaseToggle);
4690 /* In sticky mode, restore the state of the Case Sensitive button */
4691 if(GetPrefStickyCaseSenseBtn()) {
4692 if(searchRegex) {
4693 window->iSearchLastLiteralCase = searchCaseSense;
4694 XmToggleButtonSetState(window->iSearchCaseToggle,
4695 window->iSearchLastRegexCase, False);
4696 } else {
4697 window->iSearchLastRegexCase = searchCaseSense;
4698 XmToggleButtonSetState(window->iSearchCaseToggle,
4699 window->iSearchLastLiteralCase, False);
4702 /* The iSearch bar has no Whole Word button to enable/disable. */
4704 static void findCaseToggleCB(Widget w, XtPointer clientData, XtPointer callData)
4706 WindowInfo * window = (WindowInfo *) clientData;
4707 int searchCaseSense = XmToggleButtonGetState(w);
4709 /* Save the state of the Case Sensitive button
4710 depending on the state of the Regex button*/
4711 if(XmToggleButtonGetState(window->findRegexToggle))
4712 window->findLastRegexCase = searchCaseSense;
4713 else
4714 window->findLastLiteralCase = searchCaseSense;
4717 static void replaceCaseToggleCB(Widget w, XtPointer clientData, XtPointer callData)
4719 WindowInfo * window = (WindowInfo *) clientData;
4720 int searchCaseSense = XmToggleButtonGetState(w);
4722 /* Save the state of the Case Sensitive button
4723 depending on the state of the Regex button*/
4724 if(XmToggleButtonGetState(window->replaceRegexToggle))
4725 window->replaceLastRegexCase = searchCaseSense;
4726 else
4727 window->replaceLastLiteralCase = searchCaseSense;
4730 static void iSearchCaseToggleCB(Widget w, XtPointer clientData, XtPointer callData)
4732 WindowInfo * window = (WindowInfo *) clientData;
4733 int searchCaseSense = XmToggleButtonGetState(w);
4735 /* Save the state of the Case Sensitive button
4736 depending on the state of the Regex button*/
4737 if(XmToggleButtonGetState(window->iSearchRegexToggle))
4738 window->iSearchLastRegexCase = searchCaseSense;
4739 else
4740 window->iSearchLastLiteralCase = searchCaseSense;