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