Fix for SF bug #2222159: File was not reload when type was changed externaly
[nedit.git] / source / search.c
blob8f473d67108f50f9be6f8a5007c9996b510f5a4c
1 static const char CVSID[] = "$Id: search.c,v 1.90 2008/10/06 04:40:42 ajbj 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(const WindowInfo *window);
176 static void flashTimeoutProc(XtPointer clientData, XtIntervalId *id);
177 static void eraseFlash(WindowInfo *window);
178 static int getReplaceDlogInfo(WindowInfo *window, int *direction,
179 char *searchString, char *replaceString, int *searchType);
180 static int getFindDlogInfo(WindowInfo *window, int *direction,
181 char *searchString, int *searchType);
182 static void selectedSearchCB(Widget w, XtPointer callData, Atom *selection,
183 Atom *type, 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 Boolean replaceUsingRE(const char* searchStr, const char* replaceStr,
220 const char* sourceStr, int beginPos, char* destStr, int maxDestLen,
221 int prevChar, const char* delimiters, int defaultFlags);
222 static void saveSearchHistory(const char *searchString,
223 const char *replaceString, int searchType, int isIncremental);
224 static int historyIndex(int nCycles);
225 static char *searchTypeArg(int searchType);
226 static char *searchWrapArg(int searchWrap);
227 static char *directionArg(int direction);
228 static int isRegexType(int searchType);
229 static int defaultRegexFlags(int searchType);
230 static void findRegExpToggleCB(Widget w, XtPointer clientData,
231 XtPointer callData);
232 static void replaceRegExpToggleCB(Widget w, XtPointer clientData,
233 XtPointer callData);
234 static void iSearchRegExpToggleCB(Widget w, XtPointer clientData,
235 XtPointer callData);
236 static void findCaseToggleCB(Widget w, XtPointer clientData,
237 XtPointer callData);
238 static void replaceCaseToggleCB(Widget w, XtPointer clientData,
239 XtPointer callData);
240 static void iSearchCaseToggleCB(Widget w, XtPointer clientData,
241 XtPointer callData);
242 static void iSearchTryBeepOnWrap(WindowInfo *window, int direction,
243 int beginPos, int startPos);
244 static void iSearchRecordLastBeginPos(WindowInfo *window, int direction,
245 int initPos);
246 static Boolean prefOrUserCancelsSubst(const Widget parent,
247 const Display* display);
249 typedef struct _charMatchTable {
250 char c;
251 char match;
252 char direction;
253 } charMatchTable;
255 #define N_MATCH_CHARS 13
256 #define N_FLASH_CHARS 6
257 static charMatchTable MatchingChars[N_MATCH_CHARS] = {
258 {'{', '}', SEARCH_FORWARD},
259 {'}', '{', SEARCH_BACKWARD},
260 {'(', ')', SEARCH_FORWARD},
261 {')', '(', SEARCH_BACKWARD},
262 {'[', ']', SEARCH_FORWARD},
263 {']', '[', SEARCH_BACKWARD},
264 {'<', '>', SEARCH_FORWARD},
265 {'>', '<', SEARCH_BACKWARD},
266 {'/', '/', SEARCH_FORWARD},
267 {'"', '"', SEARCH_FORWARD},
268 {'\'', '\'', SEARCH_FORWARD},
269 {'`', '`', SEARCH_FORWARD},
270 {'\\', '\\', SEARCH_FORWARD},
274 ** Definitions for the search method strings, used as arguments for
275 ** macro search subroutines and search action routines
277 static char *searchTypeStrings[] = {
278 "literal", /* SEARCH_LITERAL */
279 "case", /* SEARCH_CASE_SENSE */
280 "regex", /* SEARCH_REGEX */
281 "word", /* SEARCH_LITERAL_WORD */
282 "caseWord", /* SEARCH_CASE_SENSE_WORD */
283 "regexNoCase", /* SEARCH_REGEX_NOCASE */
284 NULL
288 ** Window for which a search dialog callback is currently active. That window
289 ** cannot be safely closed because the callback would access the armed button
290 ** after it got destroyed.
291 ** Note that an attempt to close such a window can only happen if the search
292 ** action triggers a modal dialog and the user tries to close the window via
293 ** the window manager without dismissing the dialog first. It is up to the
294 ** close callback of the window to intercept and reject the request by calling
295 ** the WindowCanBeClosed() function.
297 static WindowInfo *windowNotToClose = NULL;
299 Boolean WindowCanBeClosed(WindowInfo *window)
301 if (windowNotToClose &&
302 GetTopDocument(window->shell) ==
303 GetTopDocument(windowNotToClose->shell)) {
304 return False;
306 return True; /* It's safe */
310 ** Shared routine for replace and find dialogs and i-search bar to initialize
311 ** the state of the regex/case/word toggle buttons, and the sticky case
312 ** sensitivity states.
314 static void initToggleButtons(int searchType, Widget regexToggle,
315 Widget caseToggle, Widget* wordToggle,
316 Bool* lastLiteralCase,
317 Bool* lastRegexCase)
319 /* Set the initial search type and remember the corresponding case
320 sensitivity states in case sticky case sensitivity is required. */
321 switch (searchType) {
322 case SEARCH_LITERAL:
323 *lastLiteralCase = False;
324 *lastRegexCase = True;
325 XmToggleButtonSetState(regexToggle, False, False);
326 XmToggleButtonSetState(caseToggle, False, False);
327 if (wordToggle) {
328 XmToggleButtonSetState(*wordToggle, False, False);
329 XtSetSensitive(*wordToggle, True);
331 break;
332 case SEARCH_CASE_SENSE:
333 *lastLiteralCase = True;
334 *lastRegexCase = True;
335 XmToggleButtonSetState(regexToggle, False, False);
336 XmToggleButtonSetState(caseToggle, True, False);
337 if (wordToggle) {
338 XmToggleButtonSetState(*wordToggle, False, False);
339 XtSetSensitive(*wordToggle, True);
341 break;
342 case SEARCH_LITERAL_WORD:
343 *lastLiteralCase = False;
344 *lastRegexCase = True;
345 XmToggleButtonSetState(regexToggle, False, False);
346 XmToggleButtonSetState(caseToggle, False, False);
347 if (wordToggle) {
348 XmToggleButtonSetState(*wordToggle, True, False);
349 XtSetSensitive(*wordToggle, True);
351 break;
352 case SEARCH_CASE_SENSE_WORD:
353 *lastLiteralCase = True;
354 *lastRegexCase = True;
355 XmToggleButtonSetState(regexToggle, False, False);
356 XmToggleButtonSetState(caseToggle, True, False);
357 if (wordToggle) {
358 XmToggleButtonSetState(*wordToggle, True, False);
359 XtSetSensitive(*wordToggle, True);
361 break;
362 case SEARCH_REGEX:
363 *lastLiteralCase = False;
364 *lastRegexCase = True;
365 XmToggleButtonSetState(regexToggle, True, False);
366 XmToggleButtonSetState(caseToggle, True, False);
367 if (wordToggle) {
368 XmToggleButtonSetState(*wordToggle, False, False);
369 XtSetSensitive(*wordToggle, False);
371 break;
372 case SEARCH_REGEX_NOCASE:
373 *lastLiteralCase = False;
374 *lastRegexCase = False;
375 XmToggleButtonSetState(regexToggle, True, False);
376 XmToggleButtonSetState(caseToggle, False, False);
377 if (wordToggle) {
378 XmToggleButtonSetState(*wordToggle, False, False);
379 XtSetSensitive(*wordToggle, False);
381 break;
385 #ifdef REPLACE_SCOPE
387 ** Checks whether a selection spans multiple lines. Used to decide on the
388 ** default scope for replace dialogs.
389 ** This routine introduces a dependency on textDisp.h, which is not so nice,
390 ** but I currently don't have a cleaner solution.
392 static int selectionSpansMultipleLines(WindowInfo *window)
394 int selStart, selEnd, isRect, rectStart, rectEnd, lineStartStart,
395 lineStartEnd;
396 int lineWidth;
397 textDisp *textD;
399 if (!BufGetSelectionPos(window->buffer, &selStart, &selEnd, &isRect,
400 &rectStart, &rectEnd))
401 return FALSE;
403 /* This is kind of tricky. The perception of a line depends on the
404 line wrap mode being used. So in theory, we should take into
405 account the layout of the text on the screen. However, the
406 routine to calculate a line number for a given character position
407 (TextDPosToLineAndCol) only works for displayed lines, so we cannot
408 use it. Therefore, we use this simple heuristic:
409 - If a newline is found between the start and end of the selection,
410 we obviously have a multi-line selection.
411 - If no newline is found, but the distance between the start and the
412 end of the selection is larger than the number of characters
413 displayed on a line, and we're in continuous wrap mode,
414 we also assume a multi-line selection.
417 lineStartStart = BufStartOfLine(window->buffer, selStart);
418 lineStartEnd = BufStartOfLine(window->buffer, selEnd);
419 /* If the line starts differ, we have a "\n" in between. */
420 if (lineStartStart != lineStartEnd )
421 return TRUE;
423 if (window->wrapMode != CONTINUOUS_WRAP)
424 return FALSE; /* Same line */
426 /* Estimate the number of characters on a line */
427 textD = ((TextWidget)window->textArea)->text.textD;
428 if (textD->fontStruct->max_bounds.width > 0)
429 lineWidth = textD->width / textD->fontStruct->max_bounds.width;
430 else
431 lineWidth = 1;
432 if (lineWidth < 1) lineWidth = 1; /* Just in case */
434 /* Estimate the numbers of line breaks from the start of the line to
435 the start and ending positions of the selection and compare.*/
436 if ((selStart-lineStartStart)/lineWidth !=
437 (selEnd-lineStartStart)/lineWidth )
438 return TRUE; /* Spans multiple lines */
440 return FALSE; /* Small selection; probably doesn't span lines */
442 #endif
444 void DoFindReplaceDlog(WindowInfo *window, int direction, int keepDialogs,
445 int searchType, Time time)
448 /* Create the dialog if it doesn't already exist */
449 if (window->replaceDlog == NULL)
450 CreateReplaceDlog(window->shell, window);
452 setTextField(window, time, window->replaceText);
454 /* If the window is already up, just pop it to the top */
455 if (XtIsManaged(window->replaceDlog)) {
456 RaiseDialogWindow(XtParent(window->replaceDlog));
457 return;
460 /* Blank the Replace with field */
461 XmTextSetString(window->replaceWithText, "");
463 /* Set the initial search type */
464 initToggleButtons(searchType, window->replaceRegexToggle,
465 window->replaceCaseToggle, &window->replaceWordToggle,
466 &window->replaceLastLiteralCase,
467 &window->replaceLastRegexCase);
469 /* Set the initial direction based on the direction argument */
470 XmToggleButtonSetState(window->replaceRevToggle,
471 direction == SEARCH_FORWARD ? False: True, True);
473 /* Set the state of the Keep Dialog Up button */
474 XmToggleButtonSetState(window->replaceKeepBtn, keepDialogs, True);
476 #ifdef REPLACE_SCOPE
477 /* Set the state of the scope radio buttons to "In Window".
478 Notify to make sure that callbacks are called.
479 NOTE: due to an apparent bug in OpenMotif, the radio buttons may
480 get stuck after resetting the scope to "In Window". Therefore we must
481 use RadioButtonChangeState(), which contains a workaround. */
482 if (window->wasSelected) {
483 /* If a selection exists, the default scope depends on the preference
484 of the user. */
485 switch(GetPrefReplaceDefScope()) {
486 case REPL_DEF_SCOPE_SELECTION:
487 /* The user prefers selection scope, no matter what the
488 size of the selection is. */
489 RadioButtonChangeState(window->replaceScopeSelToggle,
490 True, True);
491 break;
492 case REPL_DEF_SCOPE_SMART:
493 if (selectionSpansMultipleLines(window)) {
494 /* If the selection spans multiple lines, the user most
495 likely wants to perform a replacement in the selection */
496 RadioButtonChangeState(window->replaceScopeSelToggle,
497 True, True);
499 else {
500 /* It's unlikely that the user wants a replacement in a
501 tiny selection only. */
502 RadioButtonChangeState(window->replaceScopeWinToggle,
503 True, True);
505 break;
506 default:
507 /* The user always wants window scope as default. */
508 RadioButtonChangeState(window->replaceScopeWinToggle,
509 True, True);
510 break;
513 else {
514 /* No selection -> always choose "In Window" as default. */
515 RadioButtonChangeState(window->replaceScopeWinToggle, True, True);
517 #endif
519 UpdateReplaceActionButtons(window);
521 /* Start the search history mechanism at the current history item */
522 window->rHistIndex = 0;
524 /* Display the dialog */
525 ManageDialogCenteredOnPointer(window->replaceDlog);
527 /* Workaround: LessTif (as of version 0.89) needs reminding of who had
528 the focus when the dialog was unmanaged. When re-managed, focus is
529 lost and events fall through to the window below. */
530 XmProcessTraversal(window->replaceText, XmTRAVERSE_CURRENT);
533 static void setTextField(WindowInfo *window, Time time, Widget textField)
535 XEvent nextEvent;
536 char *primary_selection = 0;
537 SelectionInfo *selectionInfo = XtNew(SelectionInfo);
539 if (GetPrefFindReplaceUsesSelection()) {
540 selectionInfo->done = 0;
541 selectionInfo->window = window;
542 selectionInfo->selection = 0;
543 XtGetSelectionValue(window->textArea, XA_PRIMARY, XA_STRING,
544 (XtSelectionCallbackProc)getSelectionCB, selectionInfo, time);
545 while (selectionInfo->done == 0) {
546 XtAppNextEvent(XtWidgetToApplicationContext(window->textArea), &nextEvent);
547 ServerDispatchEvent(&nextEvent);
549 primary_selection = selectionInfo->selection;
551 if (primary_selection == 0) {
552 primary_selection = XtNewString("");
555 /* Update the field */
556 XmTextSetString(textField, primary_selection);
558 XtFree(primary_selection);
559 XtFree((char*)selectionInfo);
562 static void getSelectionCB(Widget w, SelectionInfo *selectionInfo, Atom *selection,
563 Atom *type, char *value, int *length, int *format)
565 WindowInfo *window = selectionInfo->window;
567 /* return an empty string if we can't get the selection data */
568 if (*type == XT_CONVERT_FAIL || *type != XA_STRING || value == NULL || *length == 0) {
569 XtFree(value);
570 selectionInfo->selection = 0;
571 selectionInfo->done = 1;
572 return;
574 /* return an empty string if the data is not of the correct format. */
575 if (*format != 8) {
576 DialogF(DF_WARN, window->shell, 1, "Invalid Format",
577 "NEdit can't handle non 8-bit text", "OK");
578 XtFree(value);
579 selectionInfo->selection = 0;
580 selectionInfo->done = 1;
581 return;
583 selectionInfo->selection = XtMalloc(*length+1);
584 memcpy(selectionInfo->selection, value, *length);
585 selectionInfo->selection[*length] = 0;
586 XtFree(value);
587 selectionInfo->done = 1;
590 void DoFindDlog(WindowInfo *window, int direction, int keepDialogs,
591 int searchType, Time time)
594 /* Create the dialog if it doesn't already exist */
595 if (window->findDlog == NULL)
596 CreateFindDlog(window->shell, window);
598 setTextField(window, time, window->findText);
600 /* If the window is already up, just pop it to the top */
601 if (XtIsManaged(window->findDlog)) {
602 RaiseDialogWindow(XtParent(window->findDlog));
603 return;
606 /* Set the initial search type */
607 initToggleButtons(searchType, window->findRegexToggle,
608 window->findCaseToggle, &window->findWordToggle,
609 &window->findLastLiteralCase,
610 &window->findLastRegexCase);
612 /* Set the initial direction based on the direction argument */
613 XmToggleButtonSetState(window->findRevToggle,
614 direction == SEARCH_FORWARD ? False : True, True);
616 /* Set the state of the Keep Dialog Up button */
617 XmToggleButtonSetState(window->findKeepBtn, keepDialogs, True);
619 /* Set the state of the Find button */
620 fUpdateActionButtons(window);
622 /* start the search history mechanism at the current history item */
623 window->fHistIndex = 0;
625 /* Display the dialog */
626 ManageDialogCenteredOnPointer(window->findDlog);
628 /* Workaround: LessTif (as of version 0.89) needs reminding of who had
629 the focus when the dialog was unmanaged. When re-managed, focus is
630 lost and events fall through to the window below. */
631 XmProcessTraversal(window->findText, XmTRAVERSE_CURRENT);
634 void DoReplaceMultiFileDlog(WindowInfo *window)
636 char searchString[SEARCHMAX], replaceString[SEARCHMAX];
637 int direction, searchType;
639 /* Validate and fetch the find and replace strings from the dialog */
640 if (!getReplaceDlogInfo(window, &direction, searchString, replaceString,
641 &searchType))
642 return;
644 /* Don't let the user select files when no replacement can be made */
645 if (*searchString == '\0') {
646 /* Set the initial focus of the dialog back to the search string */
647 resetReplaceTabGroup(window);
648 /* pop down the replace dialog */
649 if (!XmToggleButtonGetState(window->replaceKeepBtn))
650 unmanageReplaceDialogs(window);
651 return;
654 /* Create the dialog if it doesn't already exist */
655 if (window->replaceMultiFileDlog == NULL)
656 CreateReplaceMultiFileDlog(window);
658 /* Raising the window doesn't make sense. It is modal, so we
659 can't get here unless it is unmanaged */
660 /* Prepare a list of writable windows */
661 collectWritableWindows(window);
663 /* Initialize/update the list of files. */
664 uploadFileListItems(window, False);
666 /* Display the dialog */
667 ManageDialogCenteredOnPointer(window->replaceMultiFileDlog);
671 ** If a window is closed (possibly via the window manager) while it is on the
672 ** multi-file replace dialog list of any other window (or even the same one),
673 ** we must update those lists or we end up with dangling references.
674 ** Normally, there can be only one of those dialogs at the same time
675 ** (application modal), but Lesstif doesn't (always) honor application
676 ** modalness, so there can be more than one dialog.
678 void RemoveFromMultiReplaceDialog(WindowInfo *doomedWindow)
680 WindowInfo *w;
682 for (w=WindowList; w!=NULL; w=w->next)
683 if (w->writableWindows)
684 /* A multi-file replacement dialog is up for this window */
685 checkMultiReplaceListForDoomedW(w, doomedWindow);
688 void CreateReplaceDlog(Widget parent, WindowInfo *window)
690 Arg args[50];
691 int argcnt, defaultBtnOffset;
692 XmString st1;
693 Widget form, btnForm;
694 #ifdef REPLACE_SCOPE
695 Widget scopeForm, replaceAllBtn;
696 #else
697 Widget label3, allForm;
698 #endif
699 Widget inWinBtn, inSelBtn, inMultiBtn;
700 Widget searchTypeBox;
701 Widget label2, label1, label, replaceText, findText;
702 Widget findBtn, cancelBtn, replaceBtn;
703 Widget replaceFindBtn;
704 Widget searchDirBox, reverseBtn, keepBtn;
705 char title[MAXPATHLEN + 19];
706 Dimension shadowThickness;
708 argcnt = 0;
709 XtSetArg(args[argcnt], XmNautoUnmanage, False); argcnt++;
710 form = CreateFormDialog(parent, "replaceDialog", args, argcnt);
711 XtVaSetValues(form, XmNshadowThickness, 0, NULL);
712 if (GetPrefKeepSearchDlogs()) {
713 sprintf(title, "Replace/Find (in %s)", window->filename);
714 XtVaSetValues(XtParent(form), XmNtitle, title, NULL);
715 } else
716 XtVaSetValues(XtParent(form), XmNtitle, "Replace/Find", NULL);
718 argcnt = 0;
719 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
720 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
721 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
722 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_NONE); argcnt++;
723 XtSetArg(args[argcnt], XmNleftOffset, 4); argcnt++;
724 XtSetArg(args[argcnt], XmNtopOffset, 6); argcnt++;
725 XtSetArg(args[argcnt], XmNalignment, XmALIGNMENT_BEGINNING); argcnt++;
726 XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("String to Find:"));
727 argcnt++;
728 XtSetArg(args[argcnt], XmNmnemonic, 't'); argcnt++;
729 label1 = XmCreateLabel(form, "label1", args, argcnt);
730 XmStringFree(st1);
731 XtManageChild(label1);
733 argcnt = 0;
734 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
735 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
736 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_NONE); argcnt++;
737 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_FORM); argcnt++;
738 XtSetArg(args[argcnt], XmNrightOffset, 6); argcnt++;
739 XtSetArg(args[argcnt], XmNtopOffset, 6); argcnt++;
740 XtSetArg(args[argcnt], XmNalignment, XmALIGNMENT_END); argcnt++;
741 XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING(
742 "(use up arrow key to recall previous)")); argcnt++;
743 label2 = XmCreateLabel(form, "label2", args, argcnt);
744 XmStringFree(st1);
745 XtManageChild(label2);
747 argcnt = 0;
748 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
749 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
750 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_WIDGET); argcnt++;
751 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
752 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
753 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_FORM); argcnt++;
754 XtSetArg(args[argcnt], XmNtopWidget, label1); argcnt++;
755 XtSetArg(args[argcnt], XmNleftOffset, 6); argcnt++;
756 XtSetArg(args[argcnt], XmNrightOffset, 6); argcnt++;
757 XtSetArg(args[argcnt], XmNmaxLength, SEARCHMAX); argcnt++;
758 findText = XmCreateText(form, "replaceString", args, argcnt);
759 XtAddCallback(findText, XmNfocusCallback, (XtCallbackProc)rFocusCB, window);
760 XtAddCallback(findText, XmNvalueChangedCallback,
761 (XtCallbackProc)rFindTextValueChangedCB, window);
762 XtAddEventHandler(findText, KeyPressMask, False,
763 (XtEventHandler)rFindArrowKeyCB, window);
764 RemapDeleteKey(findText);
765 XtManageChild(findText);
766 XmAddTabGroup(findText);
767 XtVaSetValues(label1, XmNuserData, findText, NULL); /* mnemonic processing */
769 argcnt = 0;
770 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_WIDGET); argcnt++;
771 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
772 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
773 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_NONE); argcnt++;
774 XtSetArg(args[argcnt], XmNtopWidget, findText); argcnt++;
775 XtSetArg(args[argcnt], XmNleftOffset, 6); argcnt++;
776 XtSetArg(args[argcnt], XmNtopOffset, 6); argcnt++;
777 XtSetArg(args[argcnt], XmNalignment, XmALIGNMENT_BEGINNING); argcnt++;
778 XtSetArg(args[argcnt], XmNlabelString,
779 st1=MKSTRING("Replace With:")); argcnt++;
780 XtSetArg(args[argcnt], XmNmnemonic, 'W'); argcnt++;
781 label = XmCreateLabel(form, "label", args, argcnt);
782 XmStringFree(st1);
783 XtManageChild(label);
785 argcnt = 0;
786 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
787 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
788 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_WIDGET); argcnt++;
789 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
790 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
791 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_FORM); argcnt++;
792 XtSetArg(args[argcnt], XmNtopWidget, label); argcnt++;
793 XtSetArg(args[argcnt], XmNleftOffset, 6); argcnt++;
794 XtSetArg(args[argcnt], XmNrightOffset, 6); argcnt++;
795 XtSetArg(args[argcnt], XmNmaxLength, SEARCHMAX); argcnt++;
796 replaceText = XmCreateText(form, "replaceWithString", args, argcnt);
797 XtAddEventHandler(replaceText, KeyPressMask, False,
798 (XtEventHandler)replaceArrowKeyCB, window);
799 RemapDeleteKey(replaceText);
800 XtManageChild(replaceText);
801 XmAddTabGroup(replaceText);
802 XtVaSetValues(label, XmNuserData, replaceText, NULL); /* mnemonic processing */
804 argcnt = 0;
805 XtSetArg(args[argcnt], XmNorientation, XmHORIZONTAL); argcnt++;
806 XtSetArg(args[argcnt], XmNpacking, XmPACK_TIGHT); argcnt++;
807 XtSetArg(args[argcnt], XmNmarginHeight, 0); argcnt++;
808 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_WIDGET); argcnt++;
809 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
810 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_NONE); argcnt++;
811 XtSetArg(args[argcnt], XmNtopWidget, replaceText); argcnt++;
812 XtSetArg(args[argcnt], XmNleftOffset, 2); argcnt++;
813 XtSetArg(args[argcnt], XmNrightOffset, 4); argcnt++;
814 searchTypeBox = XmCreateRowColumn(form, "searchTypeBox", args, argcnt);
815 XtManageChild(searchTypeBox);
816 XmAddTabGroup(searchTypeBox);
818 argcnt = 0;
819 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
820 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
821 XtSetArg(args[argcnt], XmNlabelString,
822 st1=MKSTRING("Regular Expression")); argcnt++;
823 XtSetArg(args[argcnt], XmNmnemonic, 'R'); argcnt++;
824 window->replaceRegexToggle = XmCreateToggleButton(searchTypeBox, "regExp", args, argcnt);
825 XmStringFree(st1);
826 XtManageChild(window->replaceRegexToggle);
827 XtAddCallback(window->replaceRegexToggle, XmNvalueChangedCallback, (XtCallbackProc) replaceRegExpToggleCB, window);
829 argcnt = 0;
830 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
831 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
832 XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("Case Sensitive")); argcnt++;
833 XtSetArg(args[argcnt], XmNmnemonic, 'C'); argcnt++;
834 window->replaceCaseToggle = XmCreateToggleButton(searchTypeBox, "caseSensitive", args, argcnt);
835 XmStringFree(st1);
836 XtManageChild(window->replaceCaseToggle);
837 XtAddCallback(window->replaceCaseToggle, XmNvalueChangedCallback, (XtCallbackProc) replaceCaseToggleCB, window);
839 argcnt = 0;
840 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
841 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
842 XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("Whole Word")); argcnt++;
843 XtSetArg(args[argcnt], XmNmnemonic, 'h'); argcnt++;
844 window->replaceWordToggle = XmCreateToggleButton(searchTypeBox, "wholeWord", args, argcnt);
845 XmStringFree(st1);
846 XtManageChild(window->replaceWordToggle);
848 argcnt = 0;
849 XtSetArg(args[argcnt], XmNorientation, XmHORIZONTAL); argcnt++;
850 XtSetArg(args[argcnt], XmNpacking, XmPACK_TIGHT); argcnt++;
851 XtSetArg(args[argcnt], XmNmarginHeight, 0); argcnt++;
852 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_WIDGET); argcnt++;
853 XtSetArg(args[argcnt], XmNtopOffset, 0); argcnt++;
854 XtSetArg(args[argcnt], XmNtopWidget, searchTypeBox); argcnt++;
855 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
856 XtSetArg(args[argcnt], XmNleftOffset, 2); argcnt++;
857 XtSetArg(args[argcnt], XmNradioBehavior, False); argcnt++;
858 searchDirBox = XmCreateRowColumn(form, "searchDirBox", args, argcnt);
859 XtManageChild(searchDirBox);
860 XmAddTabGroup(searchDirBox);
862 argcnt = 0;
863 XtSetArg(args[argcnt], XmNlabelString,
864 st1=MKSTRING("Search Backward")); argcnt++;
865 XtSetArg(args[argcnt], XmNmnemonic, 'B'); argcnt++;
866 reverseBtn = XmCreateToggleButton(searchDirBox, "reverse", args, argcnt);
867 XmStringFree(st1);
868 XtManageChild(reverseBtn);
870 argcnt = 0;
871 XtSetArg(args[argcnt], XmNlabelString,
872 st1=MKSTRING("Keep Dialog")); argcnt++;
873 XtSetArg(args[argcnt], XmNmnemonic, 'K'); argcnt++;
874 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_WIDGET); argcnt++;
875 XtSetArg(args[argcnt], XmNtopOffset, 0); argcnt++;
876 XtSetArg(args[argcnt], XmNtopWidget, searchTypeBox); argcnt++;
877 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_FORM); argcnt++;
878 XtSetArg(args[argcnt], XmNrightOffset, 4); argcnt++;
879 keepBtn = XmCreateToggleButton(form, "keep", args, argcnt);
880 XtAddCallback(keepBtn, XmNvalueChangedCallback,
881 (XtCallbackProc)rKeepCB, window);
882 XmStringFree(st1);
883 XtManageChild(keepBtn);
884 XmAddTabGroup(keepBtn);
886 #ifdef REPLACE_SCOPE
887 argcnt = 0;
888 XtSetArg(args[argcnt], XmNorientation, XmHORIZONTAL); argcnt++;
889 XtSetArg(args[argcnt], XmNpacking, XmPACK_TIGHT); argcnt++;
890 XtSetArg(args[argcnt], XmNmarginHeight, 0); argcnt++;
891 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
892 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_NONE); argcnt++;
893 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_WIDGET); argcnt++;
894 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
895 XtSetArg(args[argcnt], XmNtopWidget, searchDirBox); argcnt++;
896 XtSetArg(args[argcnt], XmNleftOffset, 2); argcnt++;
897 XtSetArg(args[argcnt], XmNrightOffset, 6); argcnt++;
898 XtSetArg(args[argcnt], XmNradioBehavior, True); argcnt++;
899 XtSetArg(args[argcnt], XmNradioAlwaysOne, True); argcnt++;
900 scopeForm = XmCreateRowColumn(form, "scope", args, argcnt);
901 XtManageChild(scopeForm);
902 XmAddTabGroup(scopeForm);
904 argcnt = 0;
905 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
906 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
907 XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("In Window"));
908 argcnt++;
909 XtSetArg(args[argcnt], XmNmnemonic, 'i'); argcnt++;
910 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
911 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
912 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
913 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_NONE); argcnt++;
914 inWinBtn = XmCreateToggleButton(scopeForm, "inWindow", args, argcnt);
915 XtAddCallback(inWinBtn, XmNvalueChangedCallback,
916 (XtCallbackProc)rScopeWinCB, window);
917 XmStringFree(st1);
918 XtManageChild(inWinBtn);
920 argcnt = 0;
921 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
922 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
923 XtSetArg(args[argcnt], XmNlabelString,
924 st1=MKSTRING("In Selection")); argcnt++;
925 XtSetArg(args[argcnt], XmNmnemonic, 'S'); argcnt++;
926 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
927 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
928 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_WIDGET); argcnt++;
929 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_NONE); argcnt++;
930 XtSetArg(args[argcnt], XmNleftWidget, inWinBtn); argcnt++;
931 inSelBtn = XmCreateToggleButton(scopeForm, "inSel", args, argcnt);
932 XtAddCallback(inSelBtn, XmNvalueChangedCallback,
933 (XtCallbackProc)rScopeSelCB, window);
934 XmStringFree(st1);
935 XtManageChild(inSelBtn);
937 argcnt = 0;
938 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
939 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
940 XtSetArg(args[argcnt], XmNlabelString,
941 st1=MKSTRING("In Multiple Documents")); argcnt++;
942 XtSetArg(args[argcnt], XmNmnemonic, 'M'); argcnt++;
943 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
944 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
945 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_WIDGET); argcnt++;
946 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_FORM); argcnt++;
947 XtSetArg(args[argcnt], XmNleftWidget, inSelBtn); argcnt++;
948 inMultiBtn = XmCreateToggleButton(scopeForm, "multiFile", args, argcnt);
949 XtAddCallback(inMultiBtn, XmNvalueChangedCallback,
950 (XtCallbackProc)rScopeMultiCB, window);
951 XmStringFree(st1);
952 XtManageChild(inMultiBtn);
953 #else
954 argcnt = 0;
955 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_NONE); argcnt++;
956 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_FORM); argcnt++;
957 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_WIDGET); argcnt++;
958 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
959 XtSetArg(args[argcnt], XmNtopWidget, searchDirBox); argcnt++;
960 XtSetArg(args[argcnt], XmNleftOffset, 6); argcnt++;
961 XtSetArg(args[argcnt], XmNrightOffset, 6); argcnt++;
962 allForm = XmCreateForm(form, "all", args, argcnt);
963 XtManageChild(allForm);
964 XmAddTabGroup(allForm);
966 argcnt = 0;
967 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
968 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
969 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
970 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_NONE); argcnt++;
971 XtSetArg(args[argcnt], XmNleftOffset, 4); argcnt++;
972 XtSetArg(args[argcnt], XmNtopOffset, 6); argcnt++;
973 XtSetArg(args[argcnt], XmNalignment, XmALIGNMENT_BEGINNING); argcnt++;
974 XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("Replace all in:"));
975 argcnt++;
976 label3 = XmCreateLabel(allForm, "label3", args, argcnt);
977 XmStringFree(st1);
978 XtManageChild(label3);
980 argcnt = 0;
981 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
982 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
983 XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("Window"));
984 argcnt++;
985 XtSetArg(args[argcnt], XmNmnemonic, 'i'); argcnt++;
986 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
987 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
988 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_WIDGET); argcnt++;
989 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_NONE); argcnt++;
990 XtSetArg(args[argcnt], XmNleftWidget, label3); argcnt++;
991 inWinBtn = XmCreatePushButton(allForm, "inWindow", args, argcnt);
992 XtAddCallback(inWinBtn, XmNactivateCallback,
993 (XtCallbackProc)replaceAllCB, window);
994 XmStringFree(st1);
995 XtManageChild(inWinBtn);
997 argcnt = 0;
998 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
999 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1000 XtSetArg(args[argcnt], XmNlabelString,
1001 st1=MKSTRING("Selection")); argcnt++;
1002 XtSetArg(args[argcnt], XmNmnemonic, 'S'); argcnt++;
1003 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
1004 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
1005 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_WIDGET); argcnt++;
1006 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_NONE); argcnt++;
1007 XtSetArg(args[argcnt], XmNleftWidget, inWinBtn); argcnt++;
1008 inSelBtn = XmCreatePushButton(allForm, "inSel", args, argcnt);
1009 XtAddCallback(inSelBtn, XmNactivateCallback,
1010 (XtCallbackProc)rInSelCB, window);
1011 XmStringFree(st1);
1012 XtManageChild(inSelBtn);
1014 argcnt = 0;
1015 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1016 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1017 XtSetArg(args[argcnt], XmNlabelString,
1018 st1=MKSTRING("Multiple Documents...")); argcnt++;
1019 XtSetArg(args[argcnt], XmNmnemonic, 'M'); argcnt++;
1020 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
1021 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
1022 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_WIDGET); argcnt++;
1023 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_FORM); argcnt++;
1024 XtSetArg(args[argcnt], XmNleftWidget, inSelBtn); argcnt++;
1025 inMultiBtn = XmCreatePushButton(allForm, "multiFile", args, argcnt);
1026 XtAddCallback(inMultiBtn, XmNactivateCallback,
1027 (XtCallbackProc)replaceMultiFileCB, window);
1028 XmStringFree(st1);
1029 XtManageChild(inMultiBtn);
1031 #endif
1033 argcnt = 0;
1034 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
1035 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_FORM); argcnt++;
1036 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_WIDGET); argcnt++;
1037 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
1038 #ifdef REPLACE_SCOPE
1039 XtSetArg(args[argcnt], XmNtopWidget, scopeForm); argcnt++;
1040 #else
1041 XtSetArg(args[argcnt], XmNtopWidget, allForm); argcnt++;
1042 #endif
1043 XtSetArg(args[argcnt], XmNleftOffset, 6); argcnt++;
1044 XtSetArg(args[argcnt], XmNrightOffset, 6); argcnt++;
1045 btnForm = XmCreateForm(form, "buttons", args, argcnt);
1046 XtManageChild(btnForm);
1047 XmAddTabGroup(btnForm);
1049 argcnt = 0;
1050 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1051 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1052 XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("Replace")); argcnt++;
1053 XtSetArg(args[argcnt], XmNshowAsDefault, (short)1); argcnt++;
1054 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
1055 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_POSITION); argcnt++;
1056 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_POSITION); argcnt++;
1057 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_FORM); argcnt++;
1058 #ifdef REPLACE_SCOPE
1059 XtSetArg(args[argcnt], XmNleftPosition, 0); argcnt++;
1060 XtSetArg(args[argcnt], XmNrightPosition, 21); argcnt++;
1061 #else
1062 XtSetArg(args[argcnt], XmNleftPosition, 0); argcnt++;
1063 XtSetArg(args[argcnt], XmNrightPosition, 25); argcnt++;
1064 #endif
1065 replaceBtn = XmCreatePushButton(btnForm, "replace", args, argcnt);
1066 XtAddCallback(replaceBtn, XmNactivateCallback, (XtCallbackProc)replaceCB, window);
1067 XmStringFree(st1);
1068 XtManageChild(replaceBtn);
1069 XtVaGetValues(replaceBtn, XmNshadowThickness, &shadowThickness, NULL);
1070 defaultBtnOffset = shadowThickness + 4;
1072 argcnt = 0;
1073 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1074 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1075 XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("Find")); argcnt++;
1076 XtSetArg(args[argcnt], XmNmnemonic, 'F'); argcnt++;
1077 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
1078 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_FORM); argcnt++;
1079 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_POSITION); argcnt++;
1080 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_POSITION); argcnt++;
1081 #ifdef REPLACE_SCOPE
1082 XtSetArg(args[argcnt], XmNleftPosition, 21); argcnt++;
1083 XtSetArg(args[argcnt], XmNrightPosition, 33); argcnt++;
1084 #else
1085 XtSetArg(args[argcnt], XmNleftPosition, 25); argcnt++;
1086 XtSetArg(args[argcnt], XmNrightPosition, 42); argcnt++;
1087 #endif
1088 XtSetArg(args[argcnt], XmNtopOffset, defaultBtnOffset); argcnt++;
1089 XtSetArg(args[argcnt], XmNbottomOffset, defaultBtnOffset); argcnt++;
1090 findBtn = XmCreatePushButton(btnForm, "find", args, argcnt);
1091 XtAddCallback(findBtn, XmNactivateCallback, (XtCallbackProc)rFindCB, window);
1092 XmStringFree(st1);
1093 XtManageChild(findBtn);
1095 argcnt = 0;
1096 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1097 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1098 XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("Replace & Find")); argcnt++;
1099 XtSetArg(args[argcnt], XmNmnemonic, 'n'); argcnt++;
1100 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
1101 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_FORM); argcnt++;
1102 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_POSITION); argcnt++;
1103 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_POSITION); argcnt++;
1104 #ifdef REPLACE_SCOPE
1105 XtSetArg(args[argcnt], XmNleftPosition, 33); argcnt++;
1106 XtSetArg(args[argcnt], XmNrightPosition, 62); argcnt++;
1107 #else
1108 XtSetArg(args[argcnt], XmNleftPosition, 42); argcnt++;
1109 XtSetArg(args[argcnt], XmNrightPosition, 79); argcnt++;
1110 #endif
1111 XtSetArg(args[argcnt], XmNtopOffset, defaultBtnOffset); argcnt++;
1112 XtSetArg(args[argcnt], XmNbottomOffset, defaultBtnOffset); argcnt++;
1113 replaceFindBtn = XmCreatePushButton(btnForm, "replacefind", args, argcnt);
1114 XtAddCallback(replaceFindBtn, XmNactivateCallback, (XtCallbackProc)replaceFindCB, window);
1115 XmStringFree(st1);
1116 XtManageChild(replaceFindBtn);
1118 #ifdef REPLACE_SCOPE
1119 argcnt = 0;
1120 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1121 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1122 XtSetArg(args[argcnt], XmNlabelString,
1123 st1=MKSTRING("Replace All")); argcnt++;
1124 XtSetArg(args[argcnt], XmNmnemonic, 'A'); argcnt++;
1125 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
1126 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
1127 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_POSITION); argcnt++;
1128 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_POSITION); argcnt++;
1129 XtSetArg(args[argcnt], XmNleftPosition, 62); argcnt++;
1130 XtSetArg(args[argcnt], XmNrightPosition, 85); argcnt++;
1131 XtSetArg(args[argcnt], XmNtopOffset, defaultBtnOffset); argcnt++;
1132 replaceAllBtn = XmCreatePushButton(btnForm, "all", args, argcnt);
1133 XtAddCallback(replaceAllBtn, XmNactivateCallback,
1134 (XtCallbackProc)replaceAllScopeCB, window);
1135 XmStringFree(st1);
1136 XtManageChild(replaceAllBtn);
1137 #endif
1139 argcnt = 0;
1140 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1141 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1142 XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("Cancel")); argcnt++;
1143 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
1144 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_FORM); argcnt++;
1145 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_POSITION); argcnt++;
1146 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_POSITION); argcnt++;
1147 #ifdef REPLACE_SCOPE
1148 XtSetArg(args[argcnt], XmNleftPosition, 85); argcnt++;
1149 XtSetArg(args[argcnt], XmNrightPosition, 100); argcnt++;
1150 #else
1151 XtSetArg(args[argcnt], XmNleftPosition, 79); argcnt++;
1152 XtSetArg(args[argcnt], XmNrightPosition, 100); argcnt++;
1153 #endif
1154 XtSetArg(args[argcnt], XmNtopOffset, defaultBtnOffset); argcnt++;
1155 XtSetArg(args[argcnt], XmNbottomOffset, defaultBtnOffset); argcnt++;
1156 cancelBtn = XmCreatePushButton(btnForm, "cancel", args, argcnt);
1157 XmStringFree(st1);
1158 XtAddCallback(cancelBtn, XmNactivateCallback, (XtCallbackProc)rCancelCB,
1159 window);
1160 XtManageChild(cancelBtn);
1162 XtVaSetValues(form, XmNcancelButton, cancelBtn, NULL);
1163 AddDialogMnemonicHandler(form, FALSE);
1165 window->replaceDlog = form;
1166 window->replaceText = findText;
1167 window->replaceWithText = replaceText;
1168 window->replaceRevToggle = reverseBtn;
1169 window->replaceKeepBtn = keepBtn;
1170 window->replaceBtns = btnForm;
1171 window->replaceBtn = replaceBtn;
1172 window->replaceAndFindBtn = replaceFindBtn;
1173 window->replaceFindBtn = findBtn;
1174 window->replaceSearchTypeBox = searchTypeBox;
1175 #ifdef REPLACE_SCOPE
1176 window->replaceAllBtn = replaceAllBtn;
1177 window->replaceScopeWinToggle = inWinBtn;
1178 window->replaceScopeSelToggle = inSelBtn;
1179 window->replaceScopeMultiToggle = inMultiBtn;
1180 #else
1181 window->replaceInWinBtn = inWinBtn;
1182 window->replaceAllBtn = inMultiBtn;
1183 window->replaceInSelBtn = inSelBtn;
1184 #endif
1187 void CreateFindDlog(Widget parent, WindowInfo *window)
1189 Arg args[50];
1190 int argcnt, defaultBtnOffset;
1191 XmString st1;
1192 Widget form, btnForm, searchTypeBox;
1193 Widget findText, label1, label2, cancelBtn, findBtn;
1194 Widget searchDirBox, reverseBtn, keepBtn;
1195 char title[MAXPATHLEN + 11];
1196 Dimension shadowThickness;
1198 argcnt = 0;
1199 XtSetArg(args[argcnt], XmNautoUnmanage, False); argcnt++;
1200 form = CreateFormDialog(parent, "findDialog", args, argcnt);
1201 XtVaSetValues(form, XmNshadowThickness, 0, NULL);
1202 if (GetPrefKeepSearchDlogs()) {
1203 sprintf(title, "Find (in %s)", window->filename);
1204 XtVaSetValues(XtParent(form), XmNtitle, title, NULL);
1205 } else
1206 XtVaSetValues(XtParent(form), XmNtitle, "Find", NULL);
1208 argcnt = 0;
1209 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
1210 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
1211 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
1212 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_NONE); argcnt++;
1213 XtSetArg(args[argcnt], XmNleftOffset, 6); argcnt++;
1214 XtSetArg(args[argcnt], XmNtopOffset, 6); argcnt++;
1215 XtSetArg(args[argcnt], XmNalignment, XmALIGNMENT_BEGINNING); argcnt++;
1216 XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("String to Find:"));
1217 argcnt++;
1218 XtSetArg(args[argcnt], XmNmnemonic, 'S'); argcnt++;
1219 label1 = XmCreateLabel(form, "label1", args, argcnt);
1220 XmStringFree(st1);
1221 XtManageChild(label1);
1223 argcnt = 0;
1224 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
1225 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
1226 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_NONE); argcnt++;
1227 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_FORM); argcnt++;
1228 XtSetArg(args[argcnt], XmNrightOffset, 6); argcnt++;
1229 XtSetArg(args[argcnt], XmNtopOffset, 6); argcnt++;
1230 XtSetArg(args[argcnt], XmNalignment, XmALIGNMENT_END); argcnt++;
1231 XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING(
1232 "(use up arrow key to recall previous)")); argcnt++;
1233 label2 = XmCreateLabel(form, "label2", args, argcnt);
1234 XmStringFree(st1);
1235 XtManageChild(label2);
1237 argcnt = 0;
1238 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1239 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1240 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_WIDGET); argcnt++;
1241 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
1242 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
1243 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_FORM); argcnt++;
1244 XtSetArg(args[argcnt], XmNtopWidget, label1); argcnt++;
1245 XtSetArg(args[argcnt], XmNleftOffset, 6); argcnt++;
1246 XtSetArg(args[argcnt], XmNrightOffset, 6); argcnt++;
1247 XtSetArg(args[argcnt], XmNmaxLength, SEARCHMAX); argcnt++;
1248 findText = XmCreateText(form, "searchString", args, argcnt);
1249 XtAddCallback(findText, XmNfocusCallback, (XtCallbackProc)fFocusCB, window);
1250 XtAddCallback(findText, XmNvalueChangedCallback,
1251 (XtCallbackProc)findTextValueChangedCB, window);
1252 XtAddEventHandler(findText, KeyPressMask, False,
1253 (XtEventHandler)findArrowKeyCB, window);
1254 RemapDeleteKey(findText);
1255 XtManageChild(findText);
1256 XmAddTabGroup(findText);
1257 XtVaSetValues(label1, XmNuserData, findText, NULL); /* mnemonic processing */
1259 argcnt = 0;
1260 XtSetArg(args[argcnt], XmNorientation, XmHORIZONTAL); argcnt++;
1261 XtSetArg(args[argcnt], XmNpacking, XmPACK_TIGHT); argcnt++;
1262 XtSetArg(args[argcnt], XmNmarginHeight, 0); argcnt++;
1263 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_WIDGET); argcnt++;
1264 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
1265 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_NONE); argcnt++;
1266 XtSetArg(args[argcnt], XmNtopWidget, findText); argcnt++;
1267 XtSetArg(args[argcnt], XmNleftOffset, 2); argcnt++;
1268 XtSetArg(args[argcnt], XmNrightOffset, 4); argcnt++;
1270 searchTypeBox = XmCreateRowColumn(form, "searchTypeBox", args, argcnt);
1271 XtManageChild(searchTypeBox);
1272 XmAddTabGroup(searchTypeBox);
1274 argcnt = 0;
1275 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1276 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1277 XtSetArg(args[argcnt], XmNlabelString,
1278 st1=MKSTRING("Regular Expression")); argcnt++;
1279 XtSetArg(args[argcnt], XmNmnemonic, 'R'); argcnt++;
1280 window->findRegexToggle = XmCreateToggleButton(searchTypeBox, "regExp", args, argcnt);
1281 XmStringFree(st1);
1282 XtManageChild(window->findRegexToggle);
1283 XtAddCallback(window->findRegexToggle, XmNvalueChangedCallback, (XtCallbackProc) findRegExpToggleCB, window);
1285 argcnt = 0;
1286 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1287 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1288 XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("Case Sensitive")); argcnt++;
1289 XtSetArg(args[argcnt], XmNmnemonic, 'C'); argcnt++;
1290 window->findCaseToggle = XmCreateToggleButton(searchTypeBox, "caseSensitive", args, argcnt);
1291 XmStringFree(st1);
1292 XtManageChild(window->findCaseToggle);
1293 XtAddCallback(window->findCaseToggle, XmNvalueChangedCallback, (XtCallbackProc) findCaseToggleCB, window);
1295 argcnt = 0;
1296 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1297 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1298 XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("Whole Word")); argcnt++;
1299 XtSetArg(args[argcnt], XmNmnemonic, 'h'); argcnt++;
1300 window->findWordToggle = XmCreateToggleButton(searchTypeBox, "wholeWord", args, argcnt);
1301 XmStringFree(st1);
1302 XtManageChild(window->findWordToggle);
1304 argcnt = 0;
1305 XtSetArg(args[argcnt], XmNorientation, XmHORIZONTAL); argcnt++;
1306 XtSetArg(args[argcnt], XmNpacking, XmPACK_TIGHT); argcnt++;
1307 XtSetArg(args[argcnt], XmNmarginHeight, 0); argcnt++;
1308 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_WIDGET); argcnt++;
1309 XtSetArg(args[argcnt], XmNtopOffset, 0); argcnt++;
1310 XtSetArg(args[argcnt], XmNtopWidget, searchTypeBox); argcnt++;
1311 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
1312 XtSetArg(args[argcnt], XmNleftOffset, 2); argcnt++;
1313 XtSetArg(args[argcnt], XmNradioBehavior, False); argcnt++;
1314 searchDirBox = XmCreateRowColumn(form, "searchDirBox", args, argcnt);
1315 XtManageChild(searchDirBox);
1316 XmAddTabGroup(searchDirBox);
1318 argcnt = 0;
1319 XtSetArg(args[argcnt], XmNlabelString,
1320 st1=MKSTRING("Search Backward")); argcnt++;
1321 XtSetArg(args[argcnt], XmNmnemonic, 'B'); argcnt++;
1322 reverseBtn = XmCreateToggleButton(searchDirBox, "reverse", args, argcnt);
1323 XmStringFree(st1);
1324 XtManageChild(reverseBtn);
1326 argcnt = 0;
1327 XtSetArg(args[argcnt], XmNlabelString,
1328 st1=MKSTRING("Keep Dialog")); argcnt++;
1329 XtSetArg(args[argcnt], XmNmnemonic, 'K'); argcnt++;
1330 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_WIDGET); argcnt++;
1331 XtSetArg(args[argcnt], XmNtopOffset, 0); argcnt++;
1332 XtSetArg(args[argcnt], XmNtopWidget, searchTypeBox); argcnt++;
1333 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_FORM); argcnt++;
1334 XtSetArg(args[argcnt], XmNrightOffset, 4); argcnt++;
1335 keepBtn = XmCreateToggleButton(form, "keep", args, argcnt);
1336 XtAddCallback(keepBtn, XmNvalueChangedCallback,
1337 (XtCallbackProc)fKeepCB, window);
1338 XmStringFree(st1);
1339 XtManageChild(keepBtn);
1340 XmAddTabGroup(keepBtn);
1342 argcnt = 0;
1343 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
1344 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_FORM); argcnt++;
1345 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_WIDGET); argcnt++;
1346 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
1347 XtSetArg(args[argcnt], XmNtopWidget, searchDirBox); argcnt++;
1348 XtSetArg(args[argcnt], XmNleftOffset, 2); argcnt++;
1349 XtSetArg(args[argcnt], XmNrightOffset, 4); argcnt++;
1350 btnForm = XmCreateForm(form, "buttons", args, argcnt);
1351 XtManageChild(btnForm);
1352 XmAddTabGroup(btnForm);
1354 argcnt = 0;
1355 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1356 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1357 XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("Find")); argcnt++;
1358 XtSetArg(args[argcnt], XmNshowAsDefault, (short)1); argcnt++;
1359 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
1360 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_POSITION); argcnt++;
1361 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_NONE); argcnt++;
1362 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
1363 XtSetArg(args[argcnt], XmNleftPosition, 20); argcnt++;
1364 XtSetArg(args[argcnt], XmNbottomOffset, 6); argcnt++;
1365 findBtn = XmCreatePushButton(btnForm, "find", args, argcnt);
1366 XtAddCallback(findBtn, XmNactivateCallback, (XtCallbackProc)findCB, window);
1367 XmStringFree(st1);
1368 XtManageChild(findBtn);
1369 XtVaGetValues(findBtn, XmNshadowThickness, &shadowThickness, NULL);
1370 defaultBtnOffset = shadowThickness + 4;
1372 argcnt = 0;
1373 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1374 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1375 XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("Cancel")); argcnt++;
1376 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
1377 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
1378 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_NONE); argcnt++;
1379 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_POSITION); argcnt++;
1380 XtSetArg(args[argcnt], XmNrightPosition, 80); argcnt++;
1381 XtSetArg(args[argcnt], XmNtopOffset, defaultBtnOffset); argcnt++;
1382 cancelBtn = XmCreatePushButton(btnForm, "cancel", args, argcnt);
1383 XtAddCallback(cancelBtn, XmNactivateCallback, (XtCallbackProc)fCancelCB,
1384 window);
1385 XmStringFree(st1);
1386 XtManageChild(cancelBtn);
1387 XtVaSetValues(form, XmNcancelButton, cancelBtn, NULL);
1388 AddDialogMnemonicHandler(form, FALSE);
1390 window->findDlog = form;
1391 window->findText = findText;
1392 window->findRevToggle = reverseBtn;
1393 window->findKeepBtn = keepBtn;
1394 window->findBtns = btnForm;
1395 window->findBtn = findBtn;
1396 window->findSearchTypeBox = searchTypeBox;
1399 void CreateReplaceMultiFileDlog(WindowInfo *window)
1401 Arg args[50];
1402 int argcnt, defaultBtnOffset;
1403 XmString st1;
1404 Widget list, label1, form, pathBtn;
1405 Widget btnForm, replaceBtn, selectBtn, deselectBtn, cancelBtn;
1406 Dimension shadowThickness;
1408 argcnt = 0;
1409 XtSetArg(args[argcnt], XmNautoUnmanage, False); argcnt++;
1410 XtSetArg (args[argcnt], XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL);
1411 argcnt ++;
1413 /* Ideally, we should create the multi-file dialog as a child widget
1414 of the replace dialog. However, if we do this, the main window
1415 can hide the multi-file dialog when raised (I'm not sure why, but
1416 it's something that I observed with fvwm). By using the main window
1417 as the parent, it is possible that the replace dialog _partially_
1418 covers the multi-file dialog, but this much better than the multi-file
1419 dialog being covered completely by the main window */
1420 form = CreateFormDialog(window->shell, "replaceMultiFileDialog",
1421 args, argcnt);
1422 XtVaSetValues(form, XmNshadowThickness, 0, NULL);
1423 XtVaSetValues(XtParent(form), XmNtitle, "Replace All in Multiple Documents",
1424 NULL);
1426 /* Label at top left. */
1427 argcnt = 0;
1428 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
1429 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
1430 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
1431 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_NONE); argcnt++;
1432 /* Offset = 6 + (highlightThickness + detailShadowThickness) of the
1433 toggle button (see below). Unfortunately, detailShadowThickness is
1434 a Motif 2.x property, so we can't measure it. The default is 2 pixels.
1435 To make things even more complicated, the SunOS 5.6 / Solaris 2.6
1436 version of Motif 1.2 seems to use a detailShadowThickness of 0 ...
1437 So we'll have to live with a slight misalignment on that platform
1438 (those Motif libs are known to have many other problems). */
1439 XtSetArg(args[argcnt], XmNtopOffset, 10); argcnt++;
1440 XtSetArg(args[argcnt], XmNleftOffset, 6); argcnt++;
1441 XtSetArg(args[argcnt], XmNalignment, XmALIGNMENT_BEGINNING); argcnt++;
1442 XtSetArg(args[argcnt], XmNlabelString,
1443 st1=MKSTRING("Files in which to Replace All:")); argcnt++;
1444 XtSetArg(args[argcnt], XmNmnemonic, 'F'); argcnt++;
1445 label1 = XmCreateLabel(form, "label1", args, argcnt);
1446 XmStringFree(st1);
1447 XtManageChild(label1);
1449 /* Pathname toggle button at top right (always unset by default) */
1450 argcnt = 0;
1451 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1452 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1453 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_NONE); argcnt++;
1454 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_FORM); argcnt++;
1455 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
1456 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
1457 XtSetArg(args[argcnt], XmNset, False); argcnt++;
1458 XtSetArg(args[argcnt], XmNrightOffset, 6); argcnt++;
1459 XtSetArg(args[argcnt], XmNtopOffset, 6); argcnt++;
1460 XtSetArg(args[argcnt], XmNalignment, XmALIGNMENT_END); argcnt++;
1461 XtSetArg(args[argcnt], XmNlabelString,
1462 st1=MKSTRING("Show Path Names")); argcnt++;
1463 XtSetArg(args[argcnt], XmNmnemonic, 'P'); argcnt++;
1464 pathBtn = XmCreateToggleButton(form, "path", args, argcnt);
1465 XmStringFree(st1);
1466 XtAddCallback(pathBtn, XmNvalueChangedCallback,
1467 (XtCallbackProc)rMultiFilePathCB, window);
1468 XtManageChild(pathBtn);
1471 * Buttons at bottom. Place them before the list, such that we can
1472 * attach the list to the label and the button box. In that way only
1473 * the lists resizes vertically when the dialog is resized; users expect
1474 * the list to resize, not the buttons.
1477 argcnt = 0;
1478 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
1479 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_FORM); argcnt++;
1480 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_NONE); argcnt++;
1481 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_FORM); argcnt++;
1482 XtSetArg(args[argcnt], XmNleftOffset, 6); argcnt++;
1483 XtSetArg(args[argcnt], XmNrightOffset, 6); argcnt++;
1484 XtSetArg(args[argcnt], XmNtopOffset, 6); argcnt++;
1485 XtSetArg(args[argcnt], XmNresizable, (short)0); argcnt++;
1486 btnForm = XmCreateForm(form, "buttons", args, argcnt);
1487 XtManageChild(btnForm);
1489 /* Replace */
1490 argcnt = 0;
1491 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1492 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1493 XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("Replace")); argcnt++;
1494 XtSetArg(args[argcnt], XmNshowAsDefault, (short)1); argcnt++;
1495 XtSetArg(args[argcnt], XmNmnemonic, 'R'); argcnt++;
1496 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
1497 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
1498 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_POSITION); argcnt++;
1499 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_POSITION); argcnt++;
1500 XtSetArg(args[argcnt], XmNleftPosition, 0); argcnt++;
1501 XtSetArg(args[argcnt], XmNrightPosition, 25); argcnt++;
1502 replaceBtn = XmCreatePushButton(btnForm, "replace", args, argcnt);
1503 XmStringFree(st1);
1504 XtAddCallback(replaceBtn, XmNactivateCallback,
1505 (XtCallbackProc)rMultiFileReplaceCB, window);
1507 * _DON'T_ set the replace button as default (as in other dialogs).
1508 * Multi-selection lists have the nasty property of selecting the
1509 * current item when <enter> is pressed.
1510 * In that way, the user could inadvertently select an additional file
1511 * (most likely the last one that was deselected).
1512 * The user has to activate the replace button explictly (either with
1513 * a mouse click or with the shortcut key).
1515 * XtVaSetValues(form, XmNdefaultButton, replaceBtn, NULL); */
1517 XtManageChild(replaceBtn);
1518 XtVaGetValues(replaceBtn, XmNshadowThickness, &shadowThickness, NULL);
1519 defaultBtnOffset = shadowThickness + 4;
1521 /* Select All */
1522 argcnt = 0;
1523 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1524 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1525 XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("Select All"));
1526 argcnt++;
1527 XtSetArg(args[argcnt], XmNmnemonic, 'S'); argcnt++;
1528 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
1529 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
1530 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_POSITION); argcnt++;
1531 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_POSITION); argcnt++;
1532 XtSetArg(args[argcnt], XmNleftPosition, 25); argcnt++;
1533 XtSetArg(args[argcnt], XmNrightPosition, 50); argcnt++;
1534 XtSetArg(args[argcnt], XmNtopOffset, defaultBtnOffset); argcnt++;
1535 selectBtn = XmCreatePushButton(btnForm, "select", args, argcnt);
1536 XmStringFree(st1);
1537 XtAddCallback(selectBtn, XmNactivateCallback,
1538 (XtCallbackProc)rMultiFileSelectAllCB, window);
1539 XtManageChild(selectBtn);
1541 /* Deselect All */
1542 argcnt = 0;
1543 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1544 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1545 XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("Deselect All"));
1546 argcnt++;
1547 XtSetArg(args[argcnt], XmNmnemonic, 'D'); argcnt++;
1548 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
1549 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
1550 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_POSITION); argcnt++;
1551 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_POSITION); argcnt++;
1552 XtSetArg(args[argcnt], XmNleftPosition, 50); argcnt++;
1553 XtSetArg(args[argcnt], XmNrightPosition, 75); argcnt++;
1554 XtSetArg(args[argcnt], XmNtopOffset, defaultBtnOffset); argcnt++;
1555 deselectBtn = XmCreatePushButton(btnForm, "deselect", args, argcnt);
1556 XmStringFree(st1);
1557 XtAddCallback(deselectBtn, XmNactivateCallback,
1558 (XtCallbackProc)rMultiFileDeselectAllCB, window);
1559 XtManageChild(deselectBtn);
1561 /* Cancel */
1562 argcnt = 0;
1563 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1564 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1565 XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("Cancel")); argcnt++;
1566 XtSetArg(args[argcnt], XmNmnemonic, 'C'); argcnt++;
1567 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
1568 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
1569 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_POSITION); argcnt++;
1570 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_POSITION); argcnt++;
1571 XtSetArg(args[argcnt], XmNleftPosition, 75); argcnt++;
1572 XtSetArg(args[argcnt], XmNrightPosition, 100); argcnt++;
1573 XtSetArg(args[argcnt], XmNtopOffset, defaultBtnOffset); argcnt++;
1574 cancelBtn = XmCreatePushButton(btnForm, "cancel", args, argcnt);
1575 XmStringFree(st1);
1576 XtAddCallback(cancelBtn, XmNactivateCallback,
1577 (XtCallbackProc)rMultiFileCancelCB, window);
1578 XtManageChild(cancelBtn);
1580 /* The list of files */
1581 argcnt = 0;
1582 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1583 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_WIDGET); argcnt++;
1584 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_WIDGET); argcnt++;
1585 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
1586 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_FORM); argcnt++;
1587 XtSetArg(args[argcnt], XmNbottomWidget, btnForm); argcnt++;
1588 XtSetArg(args[argcnt], XmNtopWidget, label1); argcnt++;
1589 XtSetArg(args[argcnt], XmNleftOffset, 10); argcnt++;
1590 XtSetArg(args[argcnt], XmNvisibleItemCount, 10); argcnt++;
1591 XtSetArg(args[argcnt], XmNtopOffset, 6); argcnt++;
1592 XtSetArg(args[argcnt], XmNbottomOffset, 6); argcnt++;
1593 XtSetArg(args[argcnt], XmNrightOffset, 10); argcnt++;
1594 /* An alternative is to use the EXTENDED_SELECT, but that one
1595 is less suited for keyboard manipulation (moving the selection cursor
1596 with the keyboard deselects everything). */
1597 XtSetArg(args[argcnt], XmNselectionPolicy, XmMULTIPLE_SELECT); argcnt++;
1598 list = XmCreateScrolledList(form, "list_of_files", args, argcnt);
1599 AddMouseWheelSupport(list);
1600 XtManageChild(list);
1602 /* Traverse: list -> buttons -> path name toggle button */
1603 XmAddTabGroup(list);
1604 XmAddTabGroup(btnForm);
1605 XmAddTabGroup(pathBtn);
1607 XtVaSetValues(label1, XmNuserData, list, NULL); /* mnemonic processing */
1609 /* Cancel/Mnemonic stuff. */
1610 XtVaSetValues(form, XmNcancelButton, cancelBtn, NULL);
1611 AddDialogMnemonicHandler(form, FALSE);
1613 window->replaceMultiFileDlog = form;
1614 window->replaceMultiFileList = list;
1615 window->replaceMultiFilePathBtn = pathBtn;
1617 /* Install a handler that frees the list of writable windows when
1618 the dialog is unmapped. */
1619 XtAddCallback(form, XmNunmapCallback,
1620 (XtCallbackProc)freeWritableWindowsCB, window);
1624 ** Iterates through the list of writable windows of a window, and removes
1625 ** the doomed window if necessary.
1627 static void checkMultiReplaceListForDoomedW(WindowInfo* window,
1628 WindowInfo* doomedWindow)
1630 WindowInfo *w;
1631 int i;
1633 /* If the window owning the list and the doomed window are one and the
1634 same, we just close the multi-file replacement dialog. */
1635 if (window == doomedWindow) {
1636 XtUnmanageChild(window->replaceMultiFileDlog);
1637 return;
1640 /* Check whether the doomed window is currently listed */
1641 for (i = 0; i < window->nWritableWindows; ++i) {
1642 w = window->writableWindows[i];
1643 if (w == doomedWindow) {
1644 removeDoomedWindowFromList(window, i);
1645 break;
1651 ** Removes a window that is about to be closed from the list of files in
1652 ** which to replace. If the list becomes empty, the dialog is popped down.
1654 static void removeDoomedWindowFromList(WindowInfo* window, int index)
1656 int entriesToMove;
1658 /* If the list would become empty, we remove the dialog */
1659 if (window->nWritableWindows <= 1) {
1660 XtUnmanageChild(window->replaceMultiFileDlog);
1661 return;
1664 entriesToMove = window->nWritableWindows - index - 1;
1665 memmove(&(window->writableWindows[index]),
1666 &(window->writableWindows[index+1]),
1667 (size_t)(entriesToMove*sizeof(WindowInfo*)));
1668 window->nWritableWindows -= 1;
1670 XmListDeletePos(window->replaceMultiFileList, index + 1);
1674 ** These callbacks fix a Motif 1.1 problem that the default button gets the
1675 ** keyboard focus when a dialog is created. We want the first text field
1676 ** to get the focus, so we don't set the default button until the text field
1677 ** has the focus for sure. I have tried many other ways and this is by far
1678 ** the least nasty.
1680 static void fFocusCB(Widget w, WindowInfo *window, caddr_t *callData)
1682 window = WidgetToWindow(w);
1683 SET_ONE_RSRC(window->findDlog, XmNdefaultButton, window->findBtn);
1685 static void rFocusCB(Widget w, WindowInfo *window, caddr_t *callData)
1687 window = WidgetToWindow(w);
1688 SET_ONE_RSRC(window->replaceDlog, XmNdefaultButton, window->replaceBtn);
1691 /* when keeping a window up, clue the user what window it's associated with */
1692 static void rKeepCB(Widget w, WindowInfo *window, caddr_t *callData)
1694 char title[MAXPATHLEN + 19];
1696 window = WidgetToWindow(w);
1698 if (XmToggleButtonGetState(w)) {
1699 sprintf(title, "Replace/Find (in %s)", window->filename);
1700 XtVaSetValues(XtParent(window->replaceDlog), XmNtitle, title, NULL);
1701 } else
1702 XtVaSetValues(XtParent(window->replaceDlog), XmNtitle, "Replace/Find", NULL);
1704 static void fKeepCB(Widget w, WindowInfo *window, caddr_t *callData)
1706 char title[MAXPATHLEN + 11];
1708 window = WidgetToWindow(w);
1710 if (XmToggleButtonGetState(w)) {
1711 sprintf(title, "Find (in %s)", window->filename);
1712 XtVaSetValues(XtParent(window->findDlog), XmNtitle, title, NULL);
1713 } else
1714 XtVaSetValues(XtParent(window->findDlog), XmNtitle, "Find", NULL);
1717 static void replaceCB(Widget w, WindowInfo *window,
1718 XmAnyCallbackStruct *callData)
1720 char searchString[SEARCHMAX], replaceString[SEARCHMAX];
1721 int direction, searchType;
1722 char *params[5];
1724 window = WidgetToWindow(w);
1726 /* Validate and fetch the find and replace strings from the dialog */
1727 if (!getReplaceDlogInfo(window, &direction, searchString, replaceString,
1728 &searchType))
1729 return;
1731 /* Set the initial focus of the dialog back to the search string */
1732 resetReplaceTabGroup(window);
1734 /* Find the text and replace it */
1735 params[0] = searchString;
1736 params[1] = replaceString;
1737 params[2] = directionArg(direction);
1738 params[3] = searchTypeArg(searchType);
1739 params[4] = searchWrapArg(GetPrefSearchWraps());
1740 windowNotToClose = window;
1741 XtCallActionProc(window->lastFocus, "replace", callData->event, params, 5);
1742 windowNotToClose = NULL;
1744 /* Pop down the dialog */
1745 if (!XmToggleButtonGetState(window->replaceKeepBtn))
1746 unmanageReplaceDialogs(window);
1749 static void replaceAllCB(Widget w, WindowInfo *window,
1750 XmAnyCallbackStruct *callData)
1752 char searchString[SEARCHMAX], replaceString[SEARCHMAX];
1753 int direction, searchType;
1754 char *params[3];
1756 window = WidgetToWindow(w);
1758 /* Validate and fetch the find and replace strings from the dialog */
1759 if (!getReplaceDlogInfo(window, &direction, searchString, replaceString,
1760 &searchType))
1761 return;
1763 /* Set the initial focus of the dialog back to the search string */
1764 resetReplaceTabGroup(window);
1766 /* do replacement */
1767 params[0] = searchString;
1768 params[1] = replaceString;
1769 params[2] = searchTypeArg(searchType);
1770 windowNotToClose = window;
1771 XtCallActionProc(window->lastFocus, "replace_all", callData->event,
1772 params, 3);
1773 windowNotToClose = NULL;
1775 /* pop down the dialog */
1776 if (!XmToggleButtonGetState(window->replaceKeepBtn))
1777 unmanageReplaceDialogs(window);
1780 static void replaceMultiFileCB(Widget w, WindowInfo *window,
1781 XmAnyCallbackStruct *callData)
1783 window = WidgetToWindow(w);
1784 DoReplaceMultiFileDlog(window);
1788 ** Callback that frees the list of windows the multi-file replace
1789 ** dialog is unmapped.
1791 static void freeWritableWindowsCB(Widget w, WindowInfo* window,
1792 XmAnyCallbackStruct *callData)
1794 window = WidgetToWindow(w);
1795 XtFree((XtPointer)window->writableWindows);
1796 window->writableWindows = NULL;
1797 window->nWritableWindows = 0;
1801 ** Comparison function for sorting windows by title for the window menu
1803 static int compareWindowNames(const void *windowA, const void *windowB)
1805 return strcmp((*((WindowInfo**)windowA))->filename,
1806 (*((WindowInfo**)windowB))->filename);
1810 ** Count no. of windows
1812 static int countWindows(void)
1814 int nWindows;
1815 const WindowInfo *w;
1817 for (w=WindowList, nWindows=0; w!=NULL; w=w->next, ++nWindows);
1819 return nWindows;
1823 ** Count no. of writable windows, but first update the status of all files.
1825 static int countWritableWindows(void)
1827 int nWritable, nBefore, nAfter;
1828 WindowInfo *w;
1830 nBefore = countWindows();
1831 for (w=WindowList, nWritable=0; w!=NULL; w=w->next) {
1832 /* We must be very careful! The status check may trigger a pop-up
1833 dialog when the file has changed on disk, and the user may destroy
1834 arbitrary windows in response. */
1835 CheckForChangesToFile(w);
1836 nAfter = countWindows();
1837 if (nAfter != nBefore) {
1838 /* The user has destroyed a file; start counting all over again */
1839 nBefore = nAfter;
1840 w = WindowList;
1841 nWritable = 0;
1842 continue;
1844 if (!IS_ANY_LOCKED(w->lockReasons)) ++nWritable;
1846 return nWritable;
1850 ** Collects a list of writable windows (sorted by file name).
1851 ** The previous list, if any is freed first.
1853 static void collectWritableWindows(WindowInfo* window)
1855 int nWritable = countWritableWindows();
1856 int i;
1857 WindowInfo *w;
1858 WindowInfo **windows;
1860 XtFree((char*) window->writableWindows);
1862 /* Make a sorted list of writable windows */
1863 windows = (WindowInfo **)XtMalloc(sizeof(WindowInfo *) * nWritable);
1864 for (w=WindowList, i=0; w!=NULL; w=w->next)
1865 if (!IS_ANY_LOCKED(w->lockReasons)) windows[i++] = w;
1866 qsort(windows, nWritable, sizeof(WindowInfo *), compareWindowNames);
1868 window->writableWindows = windows;
1869 window->nWritableWindows = nWritable;
1872 static void rMultiFileReplaceCB(Widget w, WindowInfo *window,
1873 XmAnyCallbackStruct *callData)
1875 char searchString[SEARCHMAX], replaceString[SEARCHMAX];
1876 int direction, searchType;
1877 char *params[4];
1878 int nSelected, i;
1879 WindowInfo *writableWin;
1880 Bool replaceFailed, noWritableLeft;
1882 window = WidgetToWindow(w);
1883 nSelected = 0;
1884 for (i=0; i<window->nWritableWindows; ++i)
1885 if (XmListPosSelected(window->replaceMultiFileList, i+1))
1886 ++nSelected;
1888 if (!nSelected)
1890 DialogF(DF_INF, XtParent(window->replaceMultiFileDlog), 1, "No Files",
1891 "No files selected!", "OK");
1892 return; /* Give the user another chance */
1895 /* Set the initial focus of the dialog back to the search string */
1896 resetReplaceTabGroup(window);
1899 * Protect the user against him/herself; Maybe this is a bit too much?
1901 if (DialogF(DF_QUES, window->shell, 2, "Multi-File Replacement",
1902 "Multi-file replacements are difficult to undo.\n"
1903 "Proceed with the replacement ?", "Yes", "Cancel") != 1)
1905 /* pop down the multi-file dialog only */
1906 XtUnmanageChild(window->replaceMultiFileDlog);
1908 return;
1911 /* Fetch the find and replace strings from the dialog;
1912 they should have been validated already, but since Lesstif may not
1913 honor modal dialogs, it is possible that the user modified the
1914 strings again, so we should verify them again too. */
1915 if (!getReplaceDlogInfo(window, &direction, searchString, replaceString,
1916 &searchType))
1917 return;
1919 /* Set the initial focus of the dialog back to the search string */
1920 resetReplaceTabGroup(window);
1922 params[0] = searchString;
1923 params[1] = replaceString;
1924 params[2] = searchTypeArg(searchType);
1926 replaceFailed = True;
1927 noWritableLeft = True;
1928 /* Perform the replacements and mark the selected files (history) */
1929 for (i=0; i<window->nWritableWindows; ++i) {
1930 writableWin = window->writableWindows[i];
1931 if (XmListPosSelected(window->replaceMultiFileList, i+1)) {
1932 /* First check again whether the file is still writable. If the
1933 file status has changed or the file was locked in the mean time
1934 (possible due to Lesstif modal dialog bug), we just skip the
1935 window. */
1936 if (!IS_ANY_LOCKED(writableWin->lockReasons)) {
1937 noWritableLeft = False;
1938 writableWin->multiFileReplSelected = True;
1939 writableWin->multiFileBusy = True; /* Avoid multi-beep/dialog */
1940 writableWin->replaceFailed = False;
1941 XtCallActionProc(writableWin->lastFocus, "replace_all",
1942 callData->event, params, 3);
1943 writableWin->multiFileBusy = False;
1944 if (!writableWin->replaceFailed)
1945 replaceFailed = False;
1947 } else {
1948 writableWin->multiFileReplSelected = False;
1952 if (!XmToggleButtonGetState(window->replaceKeepBtn)) {
1953 /* Pop down both replace dialogs. */
1954 unmanageReplaceDialogs(window);
1955 } else {
1956 /* pow down only the file selection dialog */
1957 XtUnmanageChild(window->replaceMultiFileDlog);
1960 /* We suppressed multiple beeps/dialogs. If there wasn't any file in
1961 which the replacement succeeded, we should still warn the user */
1962 if (replaceFailed) {
1963 if (GetPrefSearchDlogs()) {
1964 if (noWritableLeft) {
1965 DialogF(DF_INF, window->shell, 1, "Read-only Files",
1966 "All selected files have become read-only.", "OK");
1967 } else {
1968 DialogF(DF_INF, window->shell, 1, "String not found",
1969 "String was not found", "OK");
1971 } else {
1972 XBell(TheDisplay, 0);
1977 static void rMultiFileCancelCB(Widget w, WindowInfo *window, caddr_t callData)
1979 window = WidgetToWindow(w);
1981 /* Set the initial focus of the dialog back to the search string */
1982 resetReplaceTabGroup(window);
1984 /* pop down the multi-window replace dialog */
1985 XtUnmanageChild(window->replaceMultiFileDlog);
1988 static void rMultiFileSelectAllCB(Widget w, WindowInfo *window,
1989 XmAnyCallbackStruct *callData)
1991 int i;
1992 char policy;
1993 Widget list;
1995 window = WidgetToWindow(w);
1996 list = window->replaceMultiFileList;
1999 * If the list is in extended selection mode, we can't select more
2000 * than one item (probably because XmListSelectPos is equivalent
2001 * to a button1 click; I don't think that there is an equivalent
2002 * for CTRL-button1). Therefore, we temporarily put the list into
2003 * multiple selection mode.
2004 * Note: this is not really necessary if the list is in multiple select
2005 * mode all the time (as it currently is).
2007 XtVaGetValues(list, XmNselectionPolicy, &policy, NULL);
2008 XtVaSetValues(list, XmNselectionPolicy, XmMULTIPLE_SELECT, NULL);
2010 /* Is there no other way (like "select all") ? */
2011 XmListDeselectAllItems(window->replaceMultiFileList); /* select toggles */
2013 for (i=0; i<window->nWritableWindows; ++i) {
2014 XmListSelectPos(list, i+1, FALSE);
2017 /* Restore the original policy. */
2018 XtVaSetValues(list, XmNselectionPolicy, policy, NULL);
2021 static void rMultiFileDeselectAllCB(Widget w, WindowInfo *window,
2022 XmAnyCallbackStruct *callData)
2024 window = WidgetToWindow(w);
2025 XmListDeselectAllItems(window->replaceMultiFileList);
2028 static void rMultiFilePathCB(Widget w, WindowInfo *window,
2029 XmAnyCallbackStruct *callData)
2031 window = WidgetToWindow(w);
2032 uploadFileListItems(window, True); /* Replace */
2036 * Uploads the file items to the multi-file replament dialog list.
2037 * A boolean argument indicates whether the elements currently in the
2038 * list have to be replaced or not.
2039 * Depending on the state of the "Show path names" toggle button, either
2040 * the file names or the path names are listed.
2042 static void uploadFileListItems(WindowInfo* window, Bool replace)
2044 XmStringTable names;
2045 int nWritable, i, *selected, selectedCount;
2046 char buf[MAXPATHLEN+1], policy;
2047 Bool usePathNames;
2048 WindowInfo *w;
2049 Widget list;
2051 nWritable = window->nWritableWindows;
2052 list = window->replaceMultiFileList;
2054 names = (XmStringTable) XtMalloc(nWritable * sizeof(XmString*));
2056 usePathNames = XmToggleButtonGetState(window->replaceMultiFilePathBtn);
2058 /* Note: the windows are sorted alphabetically by _file_ name. This
2059 order is _not_ changed when we switch to path names. That
2060 would be confusing for the user */
2062 for (i = 0; i < nWritable; ++i) {
2063 w = window->writableWindows[i];
2064 if (usePathNames && window->filenameSet) {
2065 sprintf(buf, "%s%s", w->path, w->filename);
2066 } else {
2067 sprintf(buf, "%s", w->filename);
2069 names[i] = XmStringCreateSimple(buf);
2073 * If the list is in extended selection mode, we can't pre-select
2074 * more than one item in (probably because XmListSelectPos is
2075 * equivalent to a button1 click; I don't think that there is an
2076 * equivalent for CTRL-button1). Therefore, we temporarily put the
2077 * list into multiple selection mode.
2079 XtVaGetValues(list, XmNselectionPolicy, &policy, NULL);
2080 XtVaSetValues(list, XmNselectionPolicy, XmMULTIPLE_SELECT, NULL);
2081 if (replace) {
2082 /* Note: this function is obsolete in Motif 2.x, but it is available
2083 for compatibility reasons */
2084 XmListGetSelectedPos(list, &selected, &selectedCount);
2086 XmListReplaceItemsPos(list, names, nWritable, 1);
2088 /* Maintain the selections */
2089 XmListDeselectAllItems(list);
2090 for (i = 0; i < selectedCount; ++i) {
2091 XmListSelectPos(list, selected[i], False);
2094 XtFree((char*) selected);
2095 } else {
2096 Arg args[1];
2097 int nVisible;
2098 int firstSelected = 0;
2100 /* Remove the old list, if any */
2101 XmListDeleteAllItems(list);
2103 /* Initial settings */
2104 XmListAddItems(list, names, nWritable, 1);
2106 /* Pre-select the files from the last run. */
2107 selectedCount = 0;
2108 for (i = 0; i < nWritable; ++i) {
2109 if (window->writableWindows[i]->multiFileReplSelected) {
2110 XmListSelectPos(list, i+1, False);
2111 ++selectedCount;
2112 /* Remember the first selected item */
2113 if (firstSelected == 0) firstSelected = i+1;
2116 /* If no files are selected, we select them all. Normally this only
2117 happens the first time the dialog is used, but it looks "silly"
2118 if the dialog pops up with nothing selected. */
2119 if (selectedCount == 0) {
2120 for (i = 0; i < nWritable; ++i) {
2121 XmListSelectPos(list, i+1, False);
2123 firstSelected = 1;
2126 /* Make sure that the first selected item is visible; otherwise, the
2127 user could get the impression that nothing is selected. By
2128 visualizing at least the first selected item, the user will more
2129 easily be confident that the previous selection is still active. */
2130 XtSetArg(args[0], XmNvisibleItemCount, &nVisible);
2131 XtGetValues(list, args, 1);
2132 /* Make sure that we don't create blank lines at the bottom by
2133 positioning too far. */
2134 if (nWritable <= nVisible) {
2135 /* No need to shift the visible position */
2136 firstSelected = 1;
2138 else {
2139 int maxFirst = nWritable - nVisible + 1;
2140 if (firstSelected > maxFirst)
2141 firstSelected = maxFirst;
2143 XmListSetPos(list, firstSelected);
2146 /* Put the list back into its original selection policy. */
2147 XtVaSetValues(list, XmNselectionPolicy, policy, NULL);
2149 for (i = 0; i < nWritable; ++i)
2150 XmStringFree(names[i]);
2151 XtFree((char*) names);
2155 ** Unconditionally pops down the replace dialog and the
2156 ** replace-in-multiple-files dialog, if it exists.
2158 static void unmanageReplaceDialogs(const WindowInfo *window)
2160 /* If the replace dialog goes down, the multi-file replace dialog must
2161 go down too */
2162 if (window->replaceMultiFileDlog &&
2163 XtIsManaged(window->replaceMultiFileDlog)) {
2164 XtUnmanageChild(window->replaceMultiFileDlog);
2167 if (window->replaceDlog &&
2168 XtIsManaged(window->replaceDlog)) {
2169 XtUnmanageChild(window->replaceDlog);
2173 static void rInSelCB(Widget w, WindowInfo *window,
2174 XmAnyCallbackStruct *callData)
2176 char searchString[SEARCHMAX], replaceString[SEARCHMAX];
2177 int direction, searchType;
2178 char *params[3];
2180 window = WidgetToWindow(w);
2182 /* Validate and fetch the find and replace strings from the dialog */
2183 if (!getReplaceDlogInfo(window, &direction, searchString, replaceString,
2184 &searchType))
2185 return;
2187 /* Set the initial focus of the dialog back to the search string */
2188 resetReplaceTabGroup(window);
2190 /* do replacement */
2191 params[0] = searchString;
2192 params[1] = replaceString;
2193 params[2] = searchTypeArg(searchType);
2194 windowNotToClose = window;
2195 XtCallActionProc(window->lastFocus, "replace_in_selection",
2196 callData->event, params, 3);
2197 windowNotToClose = NULL;
2199 /* pop down the dialog */
2200 if (!XmToggleButtonGetState(window->replaceKeepBtn))
2201 unmanageReplaceDialogs(window);
2204 static void rCancelCB(Widget w, WindowInfo *window, caddr_t callData)
2206 window = WidgetToWindow(w);
2208 /* Set the initial focus of the dialog back to the search string */
2209 resetReplaceTabGroup(window);
2211 /* pop down the dialog */
2212 unmanageReplaceDialogs(window);
2215 static void fCancelCB(Widget w, WindowInfo *window, caddr_t callData)
2217 window = WidgetToWindow(w);
2219 /* Set the initial focus of the dialog back to the search string */
2220 resetFindTabGroup(window);
2222 /* pop down the dialog */
2223 XtUnmanageChild(window->findDlog);
2226 static void rFindCB(Widget w, WindowInfo *window,XmAnyCallbackStruct *callData)
2228 char searchString[SEARCHMAX], replaceString[SEARCHMAX];
2229 int direction, searchType;
2230 char *params[4];
2232 window = WidgetToWindow(w);
2234 /* Validate and fetch the find and replace strings from the dialog */
2235 if (!getReplaceDlogInfo(window, &direction, searchString, replaceString,
2236 &searchType))
2237 return;
2239 /* Set the initial focus of the dialog back to the search string */
2240 resetReplaceTabGroup(window);
2242 /* Find the text and mark it */
2243 params[0] = searchString;
2244 params[1] = directionArg(direction);
2245 params[2] = searchTypeArg(searchType);
2246 params[3] = searchWrapArg(GetPrefSearchWraps());
2247 windowNotToClose = window;
2248 XtCallActionProc(window->lastFocus, "find", callData->event, params, 4);
2249 windowNotToClose = NULL;
2251 /* Doctor the search history generated by the action to include the
2252 replace string (if any), so the replace string can be used on
2253 subsequent replaces, even though no actual replacement was done. */
2254 if (historyIndex(1) != -1 &&
2255 !strcmp(SearchHistory[historyIndex(1)], searchString)) {
2256 XtFree(ReplaceHistory[historyIndex(1)]);
2257 ReplaceHistory[historyIndex(1)] = XtNewString(replaceString);
2260 /* Pop down the dialog */
2261 if (!XmToggleButtonGetState(window->replaceKeepBtn))
2262 unmanageReplaceDialogs(window);
2265 static void replaceFindCB(Widget w, WindowInfo *window, XmAnyCallbackStruct *callData)
2267 char searchString[SEARCHMAX+1], replaceString[SEARCHMAX+1];
2268 int direction, searchType;
2269 char *params[4];
2271 window = WidgetToWindow(w);
2273 /* Validate and fetch the find and replace strings from the dialog */
2274 if (!getReplaceDlogInfo(window, &direction, searchString, replaceString,
2275 &searchType))
2276 return;
2278 /* Set the initial focus of the dialog back to the search string */
2279 resetReplaceTabGroup(window);
2281 /* Find the text and replace it */
2282 params[0] = searchString;
2283 params[1] = replaceString;
2284 params[2] = directionArg(direction);
2285 params[3] = searchTypeArg(searchType);
2286 windowNotToClose = window;
2287 XtCallActionProc(window->lastFocus, "replace_find", callData->event, params, 4);
2288 windowNotToClose = NULL;
2290 /* Pop down the dialog */
2291 if (!XmToggleButtonGetState(window->replaceKeepBtn))
2292 unmanageReplaceDialogs(window);
2295 static void rSetActionButtons(WindowInfo* window,
2296 int replaceBtn,
2297 int replaceFindBtn,
2298 int replaceAndFindBtn,
2299 #ifndef REPLACE_SCOPE
2300 int replaceInWinBtn,
2301 int replaceInSelBtn,
2302 #endif
2303 int replaceAllBtn)
2305 XtSetSensitive(window->replaceBtn, replaceBtn);
2306 XtSetSensitive(window->replaceFindBtn, replaceFindBtn);
2307 XtSetSensitive(window->replaceAndFindBtn, replaceAndFindBtn);
2308 #ifndef REPLACE_SCOPE
2309 XtSetSensitive(window->replaceInWinBtn, replaceInWinBtn);
2310 XtSetSensitive(window->replaceInSelBtn, replaceInSelBtn);
2311 #endif
2312 XtSetSensitive(window->replaceAllBtn, replaceAllBtn);
2315 void UpdateReplaceActionButtons(WindowInfo* window)
2317 /* Is there any text in the search for field */
2318 int searchText = textFieldNonEmpty(window->replaceText);
2319 #ifdef REPLACE_SCOPE
2320 switch (window->replaceScope)
2322 case REPL_SCOPE_WIN:
2323 /* Enable all buttons, if there is any text in the search field. */
2324 rSetActionButtons(window, searchText, searchText, searchText, searchText);
2325 break;
2327 case REPL_SCOPE_SEL:
2328 /* Only enable Replace All, if a selection exists and text in search field. */
2329 rSetActionButtons(window, False, False, False, searchText && window->wasSelected);
2330 break;
2332 case REPL_SCOPE_MULTI:
2333 /* Only enable Replace All, if text in search field. */
2334 rSetActionButtons(window, False, False, False, searchText);
2335 break;
2337 #else
2338 rSetActionButtons(window, searchText, searchText, searchText,
2339 searchText, searchText && window->wasSelected,
2340 searchText && (countWritableWindows() > 1));
2341 #endif
2344 #ifdef REPLACE_SCOPE
2346 ** The next 3 callback adapt the sensitivity of the replace dialog push
2347 ** buttons to the state of the scope radio buttons.
2349 static void rScopeWinCB(Widget w, WindowInfo *window,
2350 XmAnyCallbackStruct *callData)
2352 window = WidgetToWindow(w);
2353 if (XmToggleButtonGetState(window->replaceScopeWinToggle)) {
2354 window->replaceScope = REPL_SCOPE_WIN;
2355 UpdateReplaceActionButtons(window);
2359 static void rScopeSelCB(Widget w, WindowInfo *window,
2360 XmAnyCallbackStruct *callData)
2362 window = WidgetToWindow(w);
2363 if (XmToggleButtonGetState(window->replaceScopeSelToggle)) {
2364 window->replaceScope = REPL_SCOPE_SEL;
2365 UpdateReplaceActionButtons(window);
2369 static void rScopeMultiCB(Widget w, WindowInfo *window,
2370 XmAnyCallbackStruct *callData)
2372 window = WidgetToWindow(w);
2373 if (XmToggleButtonGetState(window->replaceScopeMultiToggle)) {
2374 window->replaceScope = REPL_SCOPE_MULTI;
2375 UpdateReplaceActionButtons(window);
2380 ** This routine dispatches a push on the replace-all button to the appropriate
2381 ** callback, depending on the state of the scope radio buttons.
2383 static void replaceAllScopeCB(Widget w, WindowInfo *window,
2384 XmAnyCallbackStruct *callData)
2386 window = WidgetToWindow(w);
2387 switch(window->replaceScope) {
2388 case REPL_SCOPE_WIN:
2389 replaceAllCB(w, window, callData);
2390 break;
2391 case REPL_SCOPE_SEL:
2392 rInSelCB(w, window, callData);
2393 break;
2394 case REPL_SCOPE_MULTI:
2395 replaceMultiFileCB(w, window, callData);
2396 break;
2399 #endif
2401 static int textFieldNonEmpty(Widget w)
2403 char *str = XmTextGetString(w);
2404 int nonEmpty = (str[0] != '\0');
2405 XtFree(str);
2406 return(nonEmpty);
2409 static void rFindTextValueChangedCB(Widget w, WindowInfo *window, XKeyEvent *event)
2411 window = WidgetToWindow(w);
2412 UpdateReplaceActionButtons(window);
2415 static void rFindArrowKeyCB(Widget w, WindowInfo *window, XKeyEvent *event)
2417 KeySym keysym = XLookupKeysym(event, 0);
2418 int index;
2419 char *searchStr, *replaceStr;
2420 int searchType;
2422 window = WidgetToWindow(w);
2423 index = window->rHistIndex;
2425 /* only process up and down arrow keys */
2426 if (keysym != XK_Up && keysym != XK_Down)
2427 return;
2429 /* increment or decrement the index depending on which arrow was pressed */
2430 index += (keysym == XK_Up) ? 1 : -1;
2432 /* if the index is out of range, beep and return */
2433 if (index != 0 && historyIndex(index) == -1) {
2434 XBell(TheDisplay, 0);
2435 return;
2438 window = WidgetToWindow(w);
2440 /* determine the strings and button settings to use */
2441 if (index == 0) {
2442 searchStr = "";
2443 replaceStr = "";
2444 searchType = GetPrefSearch();
2445 } else {
2446 searchStr = SearchHistory[historyIndex(index)];
2447 replaceStr = ReplaceHistory[historyIndex(index)];
2448 searchType = SearchTypeHistory[historyIndex(index)];
2451 /* Set the buttons and fields with the selected search type */
2452 initToggleButtons(searchType, window->replaceRegexToggle,
2453 window->replaceCaseToggle, &window->replaceWordToggle,
2454 &window->replaceLastLiteralCase,
2455 &window->replaceLastRegexCase);
2457 XmTextSetString(window->replaceText, searchStr);
2458 XmTextSetString(window->replaceWithText, replaceStr);
2460 /* Set the state of the Replace, Find ... buttons */
2461 UpdateReplaceActionButtons(window);
2463 window->rHistIndex = index;
2466 static void replaceArrowKeyCB(Widget w, WindowInfo *window, XKeyEvent *event)
2468 KeySym keysym = XLookupKeysym(event, 0);
2469 int index;
2471 window = WidgetToWindow(w);
2472 index = window->rHistIndex;
2474 /* only process up and down arrow keys */
2475 if (keysym != XK_Up && keysym != XK_Down)
2476 return;
2478 /* increment or decrement the index depending on which arrow was pressed */
2479 index += (keysym == XK_Up) ? 1 : -1;
2481 /* if the index is out of range, beep and return */
2482 if (index != 0 && historyIndex(index) == -1) {
2483 XBell(TheDisplay, 0);
2484 return;
2487 window = WidgetToWindow(w);
2489 /* change only the replace field information */
2490 if (index == 0)
2491 XmTextSetString(window->replaceWithText, "");
2492 else
2493 XmTextSetString(window->replaceWithText,
2494 ReplaceHistory[historyIndex(index)]);
2495 window->rHistIndex = index;
2498 static void fUpdateActionButtons(WindowInfo *window)
2500 int buttonState = textFieldNonEmpty(window->findText);
2501 XtSetSensitive(window->findBtn, buttonState);
2504 static void findTextValueChangedCB(Widget w, WindowInfo *window, XKeyEvent *event)
2506 window = WidgetToWindow(w);
2507 fUpdateActionButtons(window);
2510 static void findArrowKeyCB(Widget w, WindowInfo *window, XKeyEvent *event)
2512 KeySym keysym = XLookupKeysym(event, 0);
2513 int index;
2514 char *searchStr;
2515 int searchType;
2517 window = WidgetToWindow(w);
2518 index = window->fHistIndex;
2520 /* only process up and down arrow keys */
2521 if (keysym != XK_Up && keysym != XK_Down)
2522 return;
2524 /* increment or decrement the index depending on which arrow was pressed */
2525 index += (keysym == XK_Up) ? 1 : -1;
2527 /* if the index is out of range, beep and return */
2528 if (index != 0 && historyIndex(index) == -1) {
2529 XBell(TheDisplay, 0);
2530 return;
2534 /* determine the strings and button settings to use */
2535 if (index == 0) {
2536 searchStr = "";
2537 searchType = GetPrefSearch();
2538 } else {
2539 searchStr = SearchHistory[historyIndex(index)];
2540 searchType = SearchTypeHistory[historyIndex(index)];
2543 /* Set the buttons and fields with the selected search type */
2544 initToggleButtons(searchType, window->findRegexToggle,
2545 window->findCaseToggle, &window->findWordToggle,
2546 &window->findLastLiteralCase,
2547 &window->findLastRegexCase);
2548 XmTextSetString(window->findText, searchStr);
2550 /* Set the state of the Find ... button */
2551 fUpdateActionButtons(window);
2553 window->fHistIndex = index;
2556 static void findCB(Widget w, WindowInfo *window,XmAnyCallbackStruct *callData)
2558 char searchString[SEARCHMAX];
2559 int direction, searchType;
2560 char *params[4];
2562 window = WidgetToWindow(w);
2564 /* fetch find string, direction and type from the dialog */
2565 if (!getFindDlogInfo(window, &direction, searchString, &searchType))
2566 return;
2568 /* Set the initial focus of the dialog back to the search string */
2569 resetFindTabGroup(window);
2571 /* find the text and mark it */
2572 params[0] = searchString;
2573 params[1] = directionArg(direction);
2574 params[2] = searchTypeArg(searchType);
2575 params[3] = searchWrapArg(GetPrefSearchWraps());
2576 windowNotToClose = window;
2577 XtCallActionProc(window->lastFocus, "find", callData->event, params, 4);
2578 windowNotToClose = NULL;
2580 /* pop down the dialog */
2581 if (!XmToggleButtonGetState(window->findKeepBtn))
2582 XtUnmanageChild(window->findDlog);
2586 ** Fetch and verify (particularly regular expression) search and replace
2587 ** strings and search type from the Replace dialog. If the strings are ok,
2588 ** save a copy in the search history, copy them in to "searchString",
2589 ** "replaceString', which are assumed to be at least SEARCHMAX in length,
2590 ** return search type in "searchType", and return TRUE as the function
2591 ** value. Otherwise, return FALSE.
2593 static int getReplaceDlogInfo(WindowInfo *window, int *direction,
2594 char *searchString, char *replaceString, int *searchType)
2596 char *replaceText, *replaceWithText;
2597 regexp *compiledRE = NULL;
2598 char *compileMsg;
2600 /* Get the search and replace strings, search type, and direction
2601 from the dialog */
2602 replaceText = XmTextGetString(window->replaceText);
2603 replaceWithText = XmTextGetString(window->replaceWithText);
2605 if(XmToggleButtonGetState(window->replaceRegexToggle)) {
2606 int regexDefault;
2607 if(XmToggleButtonGetState(window->replaceCaseToggle)) {
2608 *searchType = SEARCH_REGEX;
2609 regexDefault = REDFLT_STANDARD;
2610 } else {
2611 *searchType = SEARCH_REGEX_NOCASE;
2612 regexDefault = REDFLT_CASE_INSENSITIVE;
2614 /* If the search type is a regular expression, test compile it
2615 immediately and present error messages */
2616 compiledRE = CompileRE(replaceText, &compileMsg, regexDefault);
2617 if (compiledRE == NULL) {
2618 DialogF(DF_WARN, XtParent(window->replaceDlog), 1, "Search String",
2619 "Please respecify the search string:\n%s", "OK", compileMsg);
2620 XtFree(replaceText);
2621 XtFree(replaceWithText);
2622 return FALSE;
2624 free((char*)compiledRE);
2625 } else {
2626 if(XmToggleButtonGetState(window->replaceCaseToggle)) {
2627 if(XmToggleButtonGetState(window->replaceWordToggle))
2628 *searchType = SEARCH_CASE_SENSE_WORD;
2629 else
2630 *searchType = SEARCH_CASE_SENSE;
2631 } else {
2632 if(XmToggleButtonGetState(window->replaceWordToggle))
2633 *searchType = SEARCH_LITERAL_WORD;
2634 else
2635 *searchType = SEARCH_LITERAL;
2639 *direction = XmToggleButtonGetState(window->replaceRevToggle) ?
2640 SEARCH_BACKWARD : SEARCH_FORWARD;
2642 /* Return strings */
2643 if (strlen(replaceText) >= SEARCHMAX) {
2644 DialogF(DF_WARN, XtParent(window->replaceDlog), 1, "String too long",
2645 "Search string too long.", "OK");
2646 XtFree(replaceText);
2647 XtFree(replaceWithText);
2648 return FALSE;
2650 if (strlen(replaceWithText) >= SEARCHMAX) {
2651 DialogF(DF_WARN, XtParent(window->replaceDlog), 1, "String too long",
2652 "Replace string too long.", "OK");
2653 XtFree(replaceText);
2654 XtFree(replaceWithText);
2655 return FALSE;
2657 strcpy(searchString, replaceText);
2658 strcpy(replaceString, replaceWithText);
2659 XtFree(replaceText);
2660 XtFree(replaceWithText);
2661 return TRUE;
2665 ** Fetch and verify (particularly regular expression) search string,
2666 ** direction, and search type from the Find dialog. If the search string
2667 ** is ok, save a copy in the search history, copy it to "searchString",
2668 ** which is assumed to be at least SEARCHMAX in length, return search type
2669 ** in "searchType", and return TRUE as the function value. Otherwise,
2670 ** return FALSE.
2672 static int getFindDlogInfo(WindowInfo *window, int *direction,
2673 char *searchString, int *searchType)
2675 char *findText;
2676 regexp *compiledRE = NULL;
2677 char *compileMsg;
2679 /* Get the search string, search type, and direction from the dialog */
2680 findText = XmTextGetString(window->findText);
2682 if(XmToggleButtonGetState(window->findRegexToggle)) {
2683 int regexDefault;
2684 if(XmToggleButtonGetState(window->findCaseToggle)) {
2685 *searchType = SEARCH_REGEX;
2686 regexDefault = REDFLT_STANDARD;
2687 } else {
2688 *searchType = SEARCH_REGEX_NOCASE;
2689 regexDefault = REDFLT_CASE_INSENSITIVE;
2691 /* If the search type is a regular expression, test compile it
2692 immediately and present error messages */
2693 compiledRE = CompileRE(findText, &compileMsg, regexDefault);
2694 if (compiledRE == NULL) {
2695 DialogF(DF_WARN, XtParent(window->findDlog), 1, "Regex Error",
2696 "Please respecify the search string:\n%s", "OK", compileMsg);
2697 return FALSE;
2699 free((char *)compiledRE);
2700 } else {
2701 if(XmToggleButtonGetState(window->findCaseToggle)) {
2702 if(XmToggleButtonGetState(window->findWordToggle))
2703 *searchType = SEARCH_CASE_SENSE_WORD;
2704 else
2705 *searchType = SEARCH_CASE_SENSE;
2706 } else {
2707 if(XmToggleButtonGetState(window->findWordToggle))
2708 *searchType = SEARCH_LITERAL_WORD;
2709 else
2710 *searchType = SEARCH_LITERAL;
2714 *direction = XmToggleButtonGetState(window->findRevToggle) ?
2715 SEARCH_BACKWARD : SEARCH_FORWARD;
2717 if (isRegexType(*searchType)) {
2720 /* Return the search string */
2721 if (strlen(findText) >= SEARCHMAX) {
2722 DialogF(DF_WARN, XtParent(window->findDlog), 1, "String too long",
2723 "Search string too long.", "OK");
2724 XtFree(findText);
2725 return FALSE;
2727 strcpy(searchString, findText);
2728 XtFree(findText);
2729 return TRUE;
2732 int SearchAndSelectSame(WindowInfo *window, int direction, int searchWrap)
2734 if (NHist < 1) {
2735 XBell(TheDisplay, 0);
2736 return FALSE;
2739 return SearchAndSelect(window, direction, SearchHistory[historyIndex(1)],
2740 SearchTypeHistory[historyIndex(1)], searchWrap);
2744 ** Search for "searchString" in "window", and select the matching text in
2745 ** the window when found (or beep or put up a dialog if not found). Also
2746 ** adds the search string to the global search history.
2748 int SearchAndSelect(WindowInfo *window, int direction, const char *searchString,
2749 int searchType, int searchWrap)
2751 int startPos, endPos;
2752 int beginPos, cursorPos, selStart, selEnd;
2753 int movedFwd = 0;
2755 /* Save a copy of searchString in the search history */
2756 saveSearchHistory(searchString, NULL, searchType, FALSE);
2758 /* set the position to start the search so we don't find the same
2759 string that was found on the last search */
2760 if (searchMatchesSelection(window, searchString, searchType,
2761 &selStart, &selEnd, NULL, NULL)) {
2762 /* selection matches search string, start before or after sel. */
2763 if (direction == SEARCH_BACKWARD) {
2764 beginPos = selStart - 1;
2765 } else {
2766 beginPos = selStart + 1;
2767 movedFwd = 1;
2769 } else {
2770 selStart = -1; selEnd = -1;
2771 /* no selection, or no match, search relative cursor */
2772 cursorPos = TextGetCursorPos(window->lastFocus);
2773 if (direction == SEARCH_BACKWARD) {
2774 /* use the insert position - 1 for backward searches */
2775 beginPos = cursorPos-1;
2776 } else {
2777 /* use the insert position for forward searches */
2778 beginPos = cursorPos;
2782 /* when the i-search bar is active and search is repeated there
2783 (Return), the action "find" is called (not: "find_incremental").
2784 "find" calls this function SearchAndSelect.
2785 To keep track of the iSearchLastBeginPos correctly in the
2786 repeated i-search case it is necessary to call the following
2787 function here, otherwise there are no beeps on the repeated
2788 incremental search wraps. */
2789 iSearchRecordLastBeginPos(window, direction, beginPos);
2791 /* do the search. SearchWindow does appropriate dialogs and beeps */
2792 if (!SearchWindow(window, direction, searchString, searchType, searchWrap,
2793 beginPos, &startPos, &endPos, NULL, NULL))
2794 return FALSE;
2796 /* if the search matched an empty string (possible with regular exps)
2797 beginning at the start of the search, go to the next occurrence,
2798 otherwise repeated finds will get "stuck" at zero-length matches */
2799 if (direction==SEARCH_FORWARD && beginPos==startPos && beginPos==endPos) {
2800 if (!movedFwd &&
2801 !SearchWindow(window, direction, searchString, searchType,
2802 searchWrap, beginPos+1, &startPos, &endPos, NULL, NULL))
2803 return FALSE;
2806 /* if matched text is already selected, just beep */
2807 if (selStart==startPos && selEnd==endPos) {
2808 XBell(TheDisplay, 0);
2809 return FALSE;
2812 /* select the text found string */
2813 BufSelect(window->buffer, startPos, endPos);
2814 MakeSelectionVisible(window, window->lastFocus);
2815 TextSetCursorPos(window->lastFocus, endPos);
2817 return TRUE;
2820 void SearchForSelected(WindowInfo *window, int direction, int searchType,
2821 int searchWrap, Time time)
2823 SearchSelectedCallData *callData = XtNew(SearchSelectedCallData);
2824 callData->direction = direction;
2825 callData->searchType = searchType;
2826 callData->searchWrap = searchWrap;
2827 XtGetSelectionValue(window->textArea, XA_PRIMARY, XA_STRING,
2828 (XtSelectionCallbackProc)selectedSearchCB, callData, time);
2831 static void selectedSearchCB(Widget w, XtPointer callData, Atom *selection,
2832 Atom *type, char *value, int *length, int *format)
2834 WindowInfo *window = WidgetToWindow(w);
2835 SearchSelectedCallData *callDataItems = (SearchSelectedCallData *)callData;
2836 int searchType;
2837 char searchString[SEARCHMAX+1];
2839 window = WidgetToWindow(w);
2841 /* skip if we can't get the selection data or it's too long */
2842 if (*type == XT_CONVERT_FAIL || value == NULL) {
2843 if (GetPrefSearchDlogs())
2844 DialogF(DF_WARN, window->shell, 1, "Wrong Selection",
2845 "Selection not appropriate for searching", "OK");
2846 else
2847 XBell(TheDisplay, 0);
2848 XtFree(callData);
2849 return;
2851 if (*length > SEARCHMAX) {
2852 if (GetPrefSearchDlogs())
2853 DialogF(DF_WARN, window->shell, 1, "Selection too long",
2854 "Selection too long", "OK");
2855 else
2856 XBell(TheDisplay, 0);
2857 XtFree(value);
2858 XtFree(callData);
2859 return;
2861 if (*length == 0) {
2862 XBell(TheDisplay, 0);
2863 XtFree(value);
2864 XtFree(callData);
2865 return;
2867 /* should be of type text??? */
2868 if (*format != 8) {
2869 fprintf(stderr, "NEdit: can't handle non 8-bit text\n");
2870 XBell(TheDisplay, 0);
2871 XtFree(value);
2872 XtFree(callData);
2873 return;
2875 /* make the selection the current search string */
2876 strncpy(searchString, value, *length);
2877 searchString[*length] = '\0';
2878 XtFree(value);
2880 /* Use the passed method for searching, unless it is regex, since this
2881 kind of search is by definition a literal search */
2882 searchType = callDataItems->searchType;
2883 if (searchType == SEARCH_REGEX )
2884 searchType = SEARCH_CASE_SENSE;
2885 else if (searchType == SEARCH_REGEX_NOCASE)
2886 searchType = SEARCH_LITERAL;
2888 /* search for it in the window */
2889 SearchAndSelect(window, callDataItems->direction, searchString,
2890 searchType, callDataItems->searchWrap);
2891 XtFree(callData);
2895 ** Pop up and clear the incremental search line and prepare to search.
2897 void BeginISearch(WindowInfo *window, int direction)
2899 window->iSearchStartPos = -1;
2900 XmTextSetString(window->iSearchText, "");
2901 XmToggleButtonSetState(window->iSearchRevToggle,
2902 direction == SEARCH_BACKWARD, FALSE);
2903 /* Note: in contrast to the replace and find dialogs, the regex and
2904 case toggles are not reset to their default state when the incremental
2905 search bar is redisplayed. I'm not sure whether this is the best
2906 choice. If not, an initToggleButtons() call should be inserted
2907 here. But in that case, it might be appropriate to have different
2908 default search modes for i-search and replace/find. */
2909 TempShowISearch(window, TRUE);
2910 XmProcessTraversal(window->iSearchText, XmTRAVERSE_CURRENT);
2914 ** Incremental searching is anchored at the position where the cursor
2915 ** was when the user began typing the search string. Call this routine
2916 ** to forget about this original anchor, and if the search bar is not
2917 ** permanently up, pop it down.
2919 void EndISearch(WindowInfo *window)
2921 /* Note: Please maintain this such that it can be freely peppered in
2922 mainline code, without callers having to worry about performance
2923 or visual glitches. */
2925 /* Forget the starting position used for the current run of searches */
2926 window->iSearchStartPos = -1;
2928 /* Mark the end of incremental search history overwriting */
2929 saveSearchHistory("", NULL, 0, FALSE);
2931 /* Pop down the search line (if it's not pegged up in Preferences) */
2932 TempShowISearch(window, FALSE);
2936 ** Reset window->iSearchLastBeginPos to the resulting initial
2937 ** search begin position for incremental searches.
2939 static void iSearchRecordLastBeginPos(WindowInfo *window, int direction,
2940 int initPos)
2942 window->iSearchLastBeginPos = initPos;
2943 if (direction == SEARCH_BACKWARD)
2944 window->iSearchLastBeginPos--;
2948 ** Search for "searchString" in "window", and select the matching text in
2949 ** the window when found (or beep or put up a dialog if not found). If
2950 ** "continued" is TRUE and a prior incremental search starting position is
2951 ** recorded, search from that original position, otherwise, search from the
2952 ** current cursor position.
2954 int SearchAndSelectIncremental(WindowInfo *window, int direction,
2955 const char *searchString, int searchType, int searchWrap, int continued)
2957 int beginPos, startPos, endPos;
2959 /* If there's a search in progress, start the search from the original
2960 starting position, otherwise search from the cursor position. */
2961 if (!continued || window->iSearchStartPos == -1) {
2962 window->iSearchStartPos = TextGetCursorPos(window->lastFocus);
2963 iSearchRecordLastBeginPos(window, direction, window->iSearchStartPos);
2965 beginPos = window->iSearchStartPos;
2967 /* If the search string is empty, beep eventually if text wrapped
2968 back to the initial position, re-init iSearchLastBeginPos,
2969 clear the selection, set the cursor back to what would be the
2970 beginning of the search, and return. */
2971 if(searchString[0] == 0) {
2972 int beepBeginPos = (direction == SEARCH_BACKWARD) ? beginPos-1:beginPos;
2973 iSearchTryBeepOnWrap(window, direction, beepBeginPos, beepBeginPos);
2974 iSearchRecordLastBeginPos(window, direction, window->iSearchStartPos);
2975 BufUnselect(window->buffer);
2976 TextSetCursorPos(window->lastFocus, beginPos);
2977 return TRUE;
2980 /* Save the string in the search history, unless we're cycling thru
2981 the search history itself, which can be detected by matching the
2982 search string with the search string of the current history index. */
2983 if(!(window->iSearchHistIndex > 1 && !strcmp(searchString,
2984 SearchHistory[historyIndex(window->iSearchHistIndex)]))) {
2985 saveSearchHistory(searchString, NULL, searchType, TRUE);
2986 /* Reset the incremental search history pointer to the beginning */
2987 window->iSearchHistIndex = 1;
2990 /* begin at insert position - 1 for backward searches */
2991 if (direction == SEARCH_BACKWARD)
2992 beginPos--;
2994 /* do the search. SearchWindow does appropriate dialogs and beeps */
2995 if (!SearchWindow(window, direction, searchString, searchType, searchWrap,
2996 beginPos, &startPos, &endPos, NULL, NULL))
2997 return FALSE;
2999 window->iSearchLastBeginPos = startPos;
3001 /* if the search matched an empty string (possible with regular exps)
3002 beginning at the start of the search, go to the next occurrence,
3003 otherwise repeated finds will get "stuck" at zero-length matches */
3004 if (direction==SEARCH_FORWARD && beginPos==startPos && beginPos==endPos)
3005 if (!SearchWindow(window, direction, searchString, searchType, searchWrap,
3006 beginPos+1, &startPos, &endPos, NULL, NULL))
3007 return FALSE;
3009 window->iSearchLastBeginPos = startPos;
3011 /* select the text found string */
3012 BufSelect(window->buffer, startPos, endPos);
3013 MakeSelectionVisible(window, window->lastFocus);
3014 TextSetCursorPos(window->lastFocus, endPos);
3016 return TRUE;
3020 ** Attach callbacks to the incremental search bar widgets. This also fudges
3021 ** up the translations on the text widget so Shift+Return will call the
3022 ** activate callback (along with Return and Ctrl+Return). It does this
3023 ** because incremental search uses the activate callback from the text
3024 ** widget to detect when the user has pressed Return to search for the next
3025 ** occurrence of the search string, and Shift+Return, which is the natural
3026 ** command for a reverse search does not naturally trigger this callback.
3028 void SetISearchTextCallbacks(WindowInfo *window)
3030 static XtTranslations tableText = NULL;
3031 static char *translationsText = "Shift<KeyPress>Return: activate()\n";
3033 static XtTranslations tableClear = NULL;
3034 static char *translationsClear =
3035 "<Btn2Down>:Arm()\n<Btn2Up>: isearch_clear_and_paste() Disarm()\n";
3037 static XtActionsRec actions[] = {
3038 { "isearch_clear_and_paste", iSearchTextClearAndPasteAP }
3041 if (tableText == NULL)
3042 tableText = XtParseTranslationTable(translationsText);
3043 XtOverrideTranslations(window->iSearchText, tableText);
3045 if (tableClear == NULL) {
3046 /* make sure actions are loaded */
3047 XtAppAddActions(XtWidgetToApplicationContext(window->iSearchText),
3048 actions, XtNumber(actions));
3049 tableClear = XtParseTranslationTable(translationsClear);
3051 XtOverrideTranslations(window->iSearchClearButton, tableClear);
3053 XtAddCallback(window->iSearchText, XmNactivateCallback,
3054 (XtCallbackProc)iSearchTextActivateCB, window);
3055 XtAddCallback(window->iSearchText, XmNvalueChangedCallback,
3056 (XtCallbackProc)iSearchTextValueChangedCB, window);
3057 XtAddEventHandler(window->iSearchText, KeyPressMask, False,
3058 (XtEventHandler)iSearchTextKeyEH, window);
3060 /* Attach callbacks to deal with the optional sticky case sensitivity
3061 behaviour. Do this before installing the search callbacks to make
3062 sure that the proper search parameters are taken into account. */
3063 XtAddCallback(window->iSearchCaseToggle, XmNvalueChangedCallback,
3064 (XtCallbackProc)iSearchCaseToggleCB, window);
3065 XtAddCallback(window->iSearchRegexToggle, XmNvalueChangedCallback,
3066 (XtCallbackProc)iSearchRegExpToggleCB, window);
3068 /* When search parameters (direction or search type), redo the search */
3069 XtAddCallback(window->iSearchCaseToggle, XmNvalueChangedCallback,
3070 (XtCallbackProc)iSearchTextValueChangedCB, window);
3071 XtAddCallback(window->iSearchRegexToggle, XmNvalueChangedCallback,
3072 (XtCallbackProc)iSearchTextValueChangedCB, window);
3073 XtAddCallback(window->iSearchRevToggle, XmNvalueChangedCallback,
3074 (XtCallbackProc)iSearchTextValueChangedCB, window);
3076 /* find button: just like pressing return */
3077 XtAddCallback(window->iSearchFindButton, XmNactivateCallback,
3078 (XtCallbackProc)iSearchTextActivateCB, window);
3079 /* clear button: empty the search text widget */
3080 XtAddCallback(window->iSearchClearButton, XmNactivateCallback,
3081 (XtCallbackProc)iSearchTextClearCB, window);
3085 ** Remove callbacks before resetting the incremental search text to avoid any
3086 ** cursor movement and/or clearing of selections.
3088 static void iSearchTextSetString(Widget w, WindowInfo *window,
3089 char *str)
3091 /* remove callbacks which would be activated by emptying the text */
3092 XtRemoveAllCallbacks(window->iSearchText, XmNvalueChangedCallback);
3093 XtRemoveAllCallbacks(window->iSearchText, XmNactivateCallback);
3094 /* empty the text */
3095 XmTextSetString(window->iSearchText, str ? str : "");
3096 /* put back the callbacks */
3097 XtAddCallback(window->iSearchText, XmNactivateCallback,
3098 (XtCallbackProc)iSearchTextActivateCB, window);
3099 XtAddCallback(window->iSearchText, XmNvalueChangedCallback,
3100 (XtCallbackProc)iSearchTextValueChangedCB, window);
3104 ** Action routine for Mouse Button 2 on the iSearchClearButton: resets the
3105 ** string then calls the activate callback for the text directly.
3107 static void iSearchTextClearAndPasteAP(Widget w, XEvent *event, String *args,
3108 Cardinal *nArg)
3110 WindowInfo *window;
3111 char *selText;
3112 XmAnyCallbackStruct cbdata;
3114 memset(&cbdata, 0, sizeof (cbdata));
3115 cbdata.event = event;
3117 window = WidgetToWindow(w);
3119 selText = GetAnySelection(window);
3120 iSearchTextSetString(w, window, selText);
3121 if (selText) {
3122 XmTextSetInsertionPosition(window->iSearchText, strlen(selText));
3123 XtFree(selText);
3125 iSearchTextActivateCB(w, window, &cbdata);
3129 ** User pressed the clear incremental search bar button. Remove callbacks
3130 ** before resetting the text to avoid any cursor movement and/or clearing
3131 ** of selections.
3133 static void iSearchTextClearCB(Widget w, WindowInfo *window,
3134 XmAnyCallbackStruct *callData)
3136 window = WidgetToWindow(w);
3138 iSearchTextSetString(w, window, NULL);
3142 ** User pressed return in the incremental search bar. Do a new search with
3143 ** the search string displayed. The direction of the search is toggled if
3144 ** the Ctrl key or the Shift key is pressed when the text field is activated.
3146 static void iSearchTextActivateCB(Widget w, WindowInfo *window,
3147 XmAnyCallbackStruct *callData)
3149 char *params[4];
3150 char *searchString;
3151 int searchType, direction;
3153 window = WidgetToWindow(w);
3155 /* Fetch the string, search type and direction from the incremental
3156 search bar widgets at the top of the window */
3157 searchString = XmTextGetString(window->iSearchText);
3158 if(XmToggleButtonGetState(window->iSearchCaseToggle)) {
3159 if(XmToggleButtonGetState(window->iSearchRegexToggle))
3160 searchType = SEARCH_REGEX;
3161 else
3162 searchType = SEARCH_CASE_SENSE;
3163 } else {
3164 if(XmToggleButtonGetState(window->iSearchRegexToggle))
3165 searchType = SEARCH_REGEX_NOCASE;
3166 else
3167 searchType = SEARCH_LITERAL;
3169 direction = XmToggleButtonGetState(window->iSearchRevToggle) ?
3170 SEARCH_BACKWARD : SEARCH_FORWARD;
3172 /* Reverse the search direction if the Ctrl or Shift key was pressed */
3173 if (callData->event->xbutton.state & (ShiftMask | ControlMask))
3174 direction = direction == SEARCH_FORWARD ?
3175 SEARCH_BACKWARD : SEARCH_FORWARD;
3177 /* find the text and mark it */
3178 params[0] = searchString;
3179 params[1] = directionArg(direction);
3180 params[2] = searchTypeArg(searchType);
3181 params[3] = searchWrapArg(GetPrefSearchWraps());
3182 XtCallActionProc(window->lastFocus, "find", callData->event, params, 4);
3183 XtFree(searchString);
3187 ** Called when user types in the incremental search line. Redoes the
3188 ** search for the new search string.
3190 static void iSearchTextValueChangedCB(Widget w, WindowInfo *window,
3191 XmAnyCallbackStruct *callData)
3193 char *params[5];
3194 char *searchString;
3195 int searchType, direction, nParams;
3197 window = WidgetToWindow(w);
3199 /* Fetch the string, search type and direction from the incremental
3200 search bar widgets at the top of the window */
3201 searchString = XmTextGetString(window->iSearchText);
3202 if(XmToggleButtonGetState(window->iSearchCaseToggle)) {
3203 if(XmToggleButtonGetState(window->iSearchRegexToggle))
3204 searchType = SEARCH_REGEX;
3205 else
3206 searchType = SEARCH_CASE_SENSE;
3207 } else {
3208 if(XmToggleButtonGetState(window->iSearchRegexToggle))
3209 searchType = SEARCH_REGEX_NOCASE;
3210 else
3211 searchType = SEARCH_LITERAL;
3213 direction = XmToggleButtonGetState(window->iSearchRevToggle) ?
3214 SEARCH_BACKWARD : SEARCH_FORWARD;
3216 /* If the search type is a regular expression, test compile it. If it
3217 fails, silently skip it. (This allows users to compose the expression
3218 in peace when they have unfinished syntax, but still get beeps when
3219 correct syntax doesn't match) */
3220 if (isRegexType(searchType)) {
3221 regexp *compiledRE = NULL;
3222 char *compileMsg;
3223 compiledRE = CompileRE(searchString, &compileMsg,
3224 defaultRegexFlags(searchType));
3225 if (compiledRE == NULL) {
3226 XtFree(searchString);
3227 return;
3229 free((char *)compiledRE);
3232 /* Call the incremental search action proc to do the searching and
3233 selecting (this allows it to be recorded for learn/replay). If
3234 there's an incremental search already in progress, mark the operation
3235 as "continued" so the search routine knows to re-start the search
3236 from the original starting position */
3237 nParams = 0;
3238 params[nParams++] = searchString;
3239 params[nParams++] = directionArg(direction);
3240 params[nParams++] = searchTypeArg(searchType);
3241 params[nParams++] = searchWrapArg(GetPrefSearchWraps());
3242 if (window->iSearchStartPos != -1)
3243 params[nParams++] = "continued";
3244 XtCallActionProc(window->lastFocus, "find_incremental",
3245 callData->event, params, nParams);
3246 XtFree(searchString);
3250 ** Process arrow keys for history recall, and escape key for leaving
3251 ** incremental search bar.
3253 static void iSearchTextKeyEH(Widget w, WindowInfo *window,
3254 XKeyEvent *event, Boolean *continueDispatch)
3256 KeySym keysym = XLookupKeysym(event, 0);
3257 int index;
3258 char *searchStr;
3259 int searchType;
3261 /* only process up and down arrow keys */
3262 if (keysym != XK_Up && keysym != XK_Down && keysym != XK_Escape) {
3263 *continueDispatch = TRUE;
3264 return;
3267 window = WidgetToWindow(w);
3268 index = window->iSearchHistIndex;
3269 *continueDispatch = FALSE;
3271 /* allow escape key to cancel search */
3272 if (keysym == XK_Escape) {
3273 XmProcessTraversal(window->lastFocus, XmTRAVERSE_CURRENT);
3274 EndISearch(window);
3275 return;
3278 /* increment or decrement the index depending on which arrow was pressed */
3279 index += (keysym == XK_Up) ? 1 : -1;
3281 /* if the index is out of range, beep and return */
3282 if (index != 0 && historyIndex(index) == -1) {
3283 XBell(TheDisplay, 0);
3284 return;
3287 /* determine the strings and button settings to use */
3288 if (index == 0) {
3289 searchStr = "";
3290 searchType = GetPrefSearch();
3291 } else {
3292 searchStr = SearchHistory[historyIndex(index)];
3293 searchType = SearchTypeHistory[historyIndex(index)];
3296 /* Set the info used in the value changed callback before calling
3297 XmTextSetString(). */
3298 window->iSearchHistIndex = index;
3299 initToggleButtons(searchType, window->iSearchRegexToggle,
3300 window->iSearchCaseToggle, NULL,
3301 &window->iSearchLastLiteralCase,
3302 &window->iSearchLastRegexCase);
3304 /* Beware the value changed callback is processed as part of this call */
3305 XmTextSetString(window->iSearchText, searchStr);
3306 XmTextSetInsertionPosition(window->iSearchText,
3307 XmTextGetLastPosition(window->iSearchText));
3311 ** Check the character before the insertion cursor of textW and flash
3312 ** matching parenthesis, brackets, or braces, by temporarily highlighting
3313 ** the matching character (a timer procedure is scheduled for removing the
3314 ** highlights)
3316 void FlashMatching(WindowInfo *window, Widget textW)
3318 char c;
3319 void *style;
3320 int pos, matchIndex;
3321 int startPos, endPos, searchPos, matchPos;
3322 int constrain;
3324 /* if a marker is already drawn, erase it and cancel the timeout */
3325 if (window->flashTimeoutID != 0) {
3326 eraseFlash(window);
3327 XtRemoveTimeOut(window->flashTimeoutID);
3328 window->flashTimeoutID = 0;
3331 /* no flashing required */
3332 if (window->showMatchingStyle == NO_FLASH) {
3333 return;
3336 /* don't flash matching characters if there's a selection */
3337 if (window->buffer->primary.selected)
3338 return;
3340 /* get the character to match and the position to start from */
3341 pos = TextGetCursorPos(textW) - 1;
3342 if (pos < 0)
3343 return;
3344 c = BufGetCharacter(window->buffer, pos);
3345 style = GetHighlightInfo(window, pos);
3347 /* is the character one we want to flash? */
3348 for (matchIndex = 0; matchIndex<N_FLASH_CHARS; matchIndex++) {
3349 if (MatchingChars[matchIndex].c == c)
3350 break;
3352 if (matchIndex == N_FLASH_CHARS)
3353 return;
3355 /* constrain the search to visible text only when in single-pane mode
3356 AND using delimiter flashing (otherwise search the whole buffer) */
3357 constrain = ((window->nPanes == 0) &&
3358 (window->showMatchingStyle == FLASH_DELIMIT));
3360 if (MatchingChars[matchIndex].direction == SEARCH_BACKWARD) {
3361 startPos = constrain ? TextFirstVisiblePos(textW) : 0;
3362 endPos = pos;
3363 searchPos = endPos;
3364 } else {
3365 startPos = pos;
3366 endPos = constrain ? TextLastVisiblePos(textW) :
3367 window->buffer->length;
3368 searchPos = startPos;
3371 /* do the search */
3372 if (!findMatchingChar(window, c, style, searchPos, startPos, endPos,
3373 &matchPos))
3374 return;
3376 if (window->showMatchingStyle == FLASH_DELIMIT) {
3377 /* Highlight either the matching character ... */
3378 BufHighlight(window->buffer, matchPos, matchPos+1);
3379 } else {
3380 /* ... or the whole range. */
3381 if (MatchingChars[matchIndex].direction == SEARCH_BACKWARD) {
3382 BufHighlight(window->buffer, matchPos, pos+1);
3383 } else {
3384 BufHighlight(window->buffer, matchPos+1, pos);
3388 /* Set up a timer to erase the box after 1.5 seconds */
3389 window->flashTimeoutID = XtAppAddTimeOut(
3390 XtWidgetToApplicationContext(window->shell), 1500,
3391 flashTimeoutProc, window);
3392 window->flashPos = matchPos;
3395 void SelectToMatchingCharacter(WindowInfo *window)
3397 int selStart, selEnd;
3398 int startPos, endPos, 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, buf->length, &matchPos)) {
3422 XBell(TheDisplay, 0);
3423 return;
3425 startPos = (matchPos > selStart) ? selStart : matchPos;
3426 endPos = (matchPos > selStart) ? matchPos : selStart;
3428 /* temporarily shut off autoShowInsertPos before setting the cursor
3429 position so MakeSelectionVisible gets a chance to place the cursor
3430 string at a pleasing position on the screen (otherwise, the cursor would
3431 be automatically scrolled on screen and MakeSelectionVisible would do
3432 nothing) */
3433 XtVaSetValues(window->lastFocus, textNautoShowInsertPos, False, NULL);
3434 /* select the text between the matching characters */
3435 BufSelect(buf, startPos, endPos+1);
3436 MakeSelectionVisible(window, window->lastFocus);
3437 XtVaSetValues(window->lastFocus, textNautoShowInsertPos, True, NULL);
3440 void GotoMatchingCharacter(WindowInfo *window)
3442 int selStart, selEnd;
3443 int matchPos;
3444 textBuffer *buf = window->buffer;
3446 /* get the character to match and its position from the selection, or
3447 the character before the insert point if nothing is selected.
3448 Give up if too many characters are selected */
3449 if (!GetSimpleSelection(buf, &selStart, &selEnd)) {
3450 selEnd = TextGetCursorPos(window->lastFocus);
3451 if (window->overstrike)
3452 selEnd += 1;
3453 selStart = selEnd - 1;
3454 if (selStart < 0) {
3455 XBell(TheDisplay, 0);
3456 return;
3459 if ((selEnd - selStart) != 1) {
3460 XBell(TheDisplay, 0);
3461 return;
3464 /* Search for it in the buffer */
3465 if (!findMatchingChar(window, BufGetCharacter(buf, selStart),
3466 GetHighlightInfo(window, selStart), selStart, 0,
3467 buf->length, &matchPos)) {
3468 XBell(TheDisplay, 0);
3469 return;
3472 /* temporarily shut off autoShowInsertPos before setting the cursor
3473 position so MakeSelectionVisible gets a chance to place the cursor
3474 string at a pleasing position on the screen (otherwise, the cursor would
3475 be automatically scrolled on screen and MakeSelectionVisible would do
3476 nothing) */
3477 XtVaSetValues(window->lastFocus, textNautoShowInsertPos, False, NULL);
3478 TextSetCursorPos(window->lastFocus, matchPos+1);
3479 MakeSelectionVisible(window, window->lastFocus);
3480 XtVaSetValues(window->lastFocus, textNautoShowInsertPos, True, NULL);
3483 static int findMatchingChar(WindowInfo *window, char toMatch,
3484 void* styleToMatch, int charPos, int startLimit, int endLimit,
3485 int *matchPos)
3487 int nestDepth, matchIndex, direction, beginPos, pos;
3488 char matchChar, c;
3489 void *style = NULL;
3490 textBuffer *buf = window->buffer;
3491 int matchSyntaxBased = window->matchSyntaxBased;
3493 /* If we don't match syntax based, fake a matching style. */
3494 if (!matchSyntaxBased) style = styleToMatch;
3496 /* Look up the matching character and match direction */
3497 for (matchIndex = 0; matchIndex<N_MATCH_CHARS; matchIndex++) {
3498 if (MatchingChars[matchIndex].c == toMatch)
3499 break;
3501 if (matchIndex == N_MATCH_CHARS)
3502 return FALSE;
3503 matchChar = MatchingChars[matchIndex].match;
3504 direction = MatchingChars[matchIndex].direction;
3506 /* find it in the buffer */
3507 beginPos = (direction==SEARCH_FORWARD) ? charPos+1 : charPos-1;
3508 nestDepth = 1;
3509 if (direction == SEARCH_FORWARD) {
3510 for (pos=beginPos; pos<endLimit; pos++) {
3511 c=BufGetCharacter(buf, pos);
3512 if (c == matchChar) {
3513 if (matchSyntaxBased) style = GetHighlightInfo(window, pos);
3514 if (style == styleToMatch) {
3515 nestDepth--;
3516 if (nestDepth == 0) {
3517 *matchPos = pos;
3518 return TRUE;
3521 } else if (c == toMatch) {
3522 if (matchSyntaxBased) style = GetHighlightInfo(window, pos);
3523 if (style == styleToMatch)
3524 nestDepth++;
3527 } else { /* SEARCH_BACKWARD */
3528 for (pos=beginPos; pos>=startLimit; pos--) {
3529 c=BufGetCharacter(buf, pos);
3530 if (c == matchChar) {
3531 if (matchSyntaxBased) style = GetHighlightInfo(window, pos);
3532 if (style == styleToMatch) {
3533 nestDepth--;
3534 if (nestDepth == 0) {
3535 *matchPos = pos;
3536 return TRUE;
3539 } else if (c == toMatch) {
3540 if (matchSyntaxBased) style = GetHighlightInfo(window, pos);
3541 if (style == styleToMatch)
3542 nestDepth++;
3546 return FALSE;
3550 ** Xt timer procedure for erasing the matching parenthesis marker.
3552 static void flashTimeoutProc(XtPointer clientData, XtIntervalId *id)
3554 eraseFlash((WindowInfo *)clientData);
3555 ((WindowInfo *)clientData)->flashTimeoutID = 0;
3559 ** Erase the marker drawn on a matching parenthesis bracket or brace
3560 ** character.
3562 static void eraseFlash(WindowInfo *window)
3564 BufUnhighlight(window->buffer);
3568 ** Search and replace using previously entered search strings (from dialog
3569 ** or selection).
3571 int ReplaceSame(WindowInfo *window, int direction, int searchWrap)
3573 if (NHist < 1) {
3574 XBell(TheDisplay, 0);
3575 return FALSE;
3578 return SearchAndReplace(window, direction, SearchHistory[historyIndex(1)],
3579 ReplaceHistory[historyIndex(1)],
3580 SearchTypeHistory[historyIndex(1)], searchWrap);
3584 ** Search and replace using previously entered search strings (from dialog
3585 ** or selection).
3587 int ReplaceFindSame(WindowInfo *window, int direction, int searchWrap)
3589 if (NHist < 1) {
3590 XBell(TheDisplay, 0);
3591 return FALSE;
3594 return ReplaceAndSearch(window, direction, SearchHistory[historyIndex(1)],
3595 ReplaceHistory[historyIndex(1)],
3596 SearchTypeHistory[historyIndex(1)], searchWrap);
3600 ** Replace selection with "replaceString" and search for string "searchString" in window "window",
3601 ** using algorithm "searchType" and direction "direction"
3603 int ReplaceAndSearch(WindowInfo *window, int direction, const char *searchString,
3604 const char *replaceString, int searchType, int searchWrap)
3606 int startPos = 0, endPos = 0, replaceLen = 0;
3607 int searchExtentBW, searchExtentFW;
3608 int replaced;
3610 /* Save a copy of search and replace strings in the search history */
3611 saveSearchHistory(searchString, replaceString, searchType, FALSE);
3613 replaced = 0;
3615 /* Replace the selected text only if it matches the search string */
3616 if (searchMatchesSelection(window, searchString, searchType,
3617 &startPos, &endPos, &searchExtentBW,
3618 &searchExtentFW)) {
3619 /* replace the text */
3620 if (isRegexType(searchType)) {
3621 char replaceResult[SEARCHMAX+1], *foundString;
3622 foundString = BufGetRange(window->buffer, searchExtentBW,
3623 searchExtentFW+1);
3624 replaceUsingRE(searchString, replaceString, foundString,
3625 startPos-searchExtentBW,
3626 replaceResult, SEARCHMAX, startPos == 0 ? '\0' :
3627 BufGetCharacter(window->buffer, startPos-1),
3628 GetWindowDelimiters(window), defaultRegexFlags(searchType));
3629 XtFree(foundString);
3630 BufReplace(window->buffer, startPos, endPos, replaceResult);
3631 replaceLen = strlen(replaceResult);
3632 } else {
3633 BufReplace(window->buffer, startPos, endPos, replaceString);
3634 replaceLen = strlen(replaceString);
3637 /* Position the cursor so the next search will work correctly based */
3638 /* on the direction of the search */
3639 TextSetCursorPos(window->lastFocus, startPos +
3640 ((direction == SEARCH_FORWARD) ? replaceLen : 0));
3641 replaced = 1;
3644 /* do the search; beeps/dialogs are taken care of */
3645 SearchAndSelect(window, direction, searchString, searchType, searchWrap);
3647 return replaced;
3651 ** Search for string "searchString" in window "window", using algorithm
3652 ** "searchType" and direction "direction", and replace it with "replaceString"
3653 ** Also adds the search and replace strings to the global search history.
3655 int SearchAndReplace(WindowInfo *window, int direction, const char *searchString,
3656 const char *replaceString, int searchType, int searchWrap)
3658 int startPos, endPos, replaceLen, searchExtentBW, searchExtentFW;
3659 int found;
3660 int beginPos, cursorPos;
3662 /* Save a copy of search and replace strings in the search history */
3663 saveSearchHistory(searchString, replaceString, searchType, FALSE);
3665 /* If the text selected in the window matches the search string, */
3666 /* the user is probably using search then replace method, so */
3667 /* replace the selected text regardless of where the cursor is. */
3668 /* Otherwise, search for the string. */
3669 if (!searchMatchesSelection(window, searchString, searchType,
3670 &startPos, &endPos, &searchExtentBW, &searchExtentFW)) {
3671 /* get the position to start the search */
3672 cursorPos = TextGetCursorPos(window->lastFocus);
3673 if (direction == SEARCH_BACKWARD) {
3674 /* use the insert position - 1 for backward searches */
3675 beginPos = cursorPos-1;
3676 } else {
3677 /* use the insert position for forward searches */
3678 beginPos = cursorPos;
3680 /* do the search */
3681 found = SearchWindow(window, direction, searchString, searchType, searchWrap,
3682 beginPos, &startPos, &endPos, &searchExtentBW, &searchExtentFW);
3683 if (!found)
3684 return FALSE;
3687 /* replace the text */
3688 if (isRegexType(searchType)) {
3689 char replaceResult[SEARCHMAX], *foundString;
3690 foundString = BufGetRange(window->buffer, searchExtentBW, searchExtentFW+1);
3691 replaceUsingRE(searchString, replaceString, foundString,
3692 startPos - searchExtentBW,
3693 replaceResult, SEARCHMAX, startPos == 0 ? '\0' :
3694 BufGetCharacter(window->buffer, startPos-1),
3695 GetWindowDelimiters(window), defaultRegexFlags(searchType));
3696 XtFree(foundString);
3697 BufReplace(window->buffer, startPos, endPos, replaceResult);
3698 replaceLen = strlen(replaceResult);
3699 } else {
3700 BufReplace(window->buffer, startPos, endPos, replaceString);
3701 replaceLen = strlen(replaceString);
3704 /* after successfully completing a replace, selected text attracts
3705 attention away from the area of the replacement, particularly
3706 when the selection represents a previous search. so deselect */
3707 BufUnselect(window->buffer);
3709 /* temporarily shut off autoShowInsertPos before setting the cursor
3710 position so MakeSelectionVisible gets a chance to place the replaced
3711 string at a pleasing position on the screen (otherwise, the cursor would
3712 be automatically scrolled on screen and MakeSelectionVisible would do
3713 nothing) */
3714 XtVaSetValues(window->lastFocus, textNautoShowInsertPos, False, NULL);
3715 TextSetCursorPos(window->lastFocus, startPos +
3716 ((direction == SEARCH_FORWARD) ? replaceLen : 0));
3717 MakeSelectionVisible(window, window->lastFocus);
3718 XtVaSetValues(window->lastFocus, textNautoShowInsertPos, True, NULL);
3720 return TRUE;
3724 ** Uses the resource nedit.truncSubstitution to determine how to deal with
3725 ** regex failures. This function only knows about the resource (via the usual
3726 ** setting getter) and asks or warns the user depending on that.
3728 ** One could argue that the dialoging should be determined by the setting
3729 ** 'searchDlogs'. However, the incomplete substitution is not just a question
3730 ** of verbosity, but of data loss. The search is successful, only the
3731 ** replacement fails due to an internal limitation of NEdit.
3733 ** The parameters 'parent' and 'display' are only used to put dialogs and
3734 ** beeps at the right place.
3736 ** The result is either predetermined by the resource or the user's choice.
3738 static Boolean prefOrUserCancelsSubst(const Widget parent,
3739 const Display* display)
3741 Boolean cancel = True;
3742 unsigned confirmResult = 0;
3744 switch (GetPrefTruncSubstitution()) {
3745 case TRUNCSUBST_SILENT:
3746 /* silently fail the operation */
3747 cancel = True;
3748 break;
3750 case TRUNCSUBST_FAIL:
3751 /* fail the operation and pop up a dialog informing the user */
3752 XBell((Display*) display, 0);
3753 DialogF(DF_INF, parent, 1, "Substitution Failed",
3754 "The result length of the substitution exceeded an internal limit.\n"
3755 "The substitution is canceled.",
3756 "OK");
3757 cancel = True;
3758 break;
3760 case TRUNCSUBST_WARN:
3761 /* pop up dialog and ask for confirmation */
3762 XBell((Display*) display, 0);
3763 confirmResult = DialogF(DF_WARN, parent, 2,
3764 "Substitution Failed",
3765 "The result length of the substitution exceeded an internal limit.\n"
3766 "Executing the substitution will result in loss of data.",
3767 "Lose Data", "Cancel");
3768 cancel = (1 != confirmResult);
3769 break;
3771 case TRUNCSUBST_IGNORE:
3772 /* silently conclude the operation; THIS WILL DESTROY DATA. */
3773 cancel = False;
3774 break;
3777 return cancel;
3781 ** Replace all occurences of "searchString" in "window" with "replaceString"
3782 ** within the current primary selection in "window". Also adds the search and
3783 ** replace strings to the global search history.
3785 void ReplaceInSelection(const WindowInfo* window, const char* searchString,
3786 const char* replaceString, const int searchType)
3788 int selStart, selEnd, beginPos, startPos, endPos, realOffset, replaceLen;
3789 int found, isRect, rectStart, rectEnd, lineStart, cursorPos;
3790 int extentBW, extentFW;
3791 char *fileString;
3792 textBuffer *tempBuf;
3793 Boolean substSuccess = False;
3794 Boolean anyFound = False;
3795 Boolean cancelSubst = True;
3797 /* save a copy of search and replace strings in the search history */
3798 saveSearchHistory(searchString, replaceString, searchType, FALSE);
3800 /* find out where the selection is */
3801 if (!BufGetSelectionPos(window->buffer, &selStart, &selEnd, &isRect,
3802 &rectStart, &rectEnd))
3803 return;
3805 /* get the selected text */
3806 if (isRect) {
3807 selStart = BufStartOfLine(window->buffer, selStart);
3808 selEnd = BufEndOfLine(window->buffer, selEnd);
3809 fileString = BufGetRange(window->buffer, selStart, selEnd);
3810 } else
3811 fileString = BufGetSelectionText(window->buffer);
3813 /* create a temporary buffer in which to do the replacements to hide the
3814 intermediate steps from the display routines, and so everything can
3815 be undone in a single operation */
3816 tempBuf = BufCreate();
3817 BufSetAll(tempBuf, fileString);
3819 /* search the string and do the replacements in the temporary buffer */
3820 replaceLen = strlen(replaceString);
3821 found = TRUE;
3822 beginPos = 0;
3823 cursorPos = 0;
3824 realOffset = 0;
3825 while (found) {
3826 found = SearchString(fileString, searchString, SEARCH_FORWARD,
3827 searchType, FALSE, beginPos, &startPos, &endPos, &extentBW,
3828 &extentFW, GetWindowDelimiters(window));
3829 if (!found)
3830 break;
3832 anyFound = True;
3833 /* if the selection is rectangular, verify that the found
3834 string is in the rectangle */
3835 if (isRect) {
3836 lineStart = BufStartOfLine(window->buffer, selStart+startPos);
3837 if (BufCountDispChars(window->buffer, lineStart, selStart+startPos) <
3838 rectStart || BufCountDispChars(window->buffer, lineStart,
3839 selStart+endPos) > rectEnd) {
3840 if (fileString[endPos] == '\0')
3841 break;
3842 /* If the match starts before the left boundary of the
3843 selection, and extends past it, we should not continue
3844 search after the end of the (false) match, because we
3845 could miss a valid match starting between the left boundary
3846 and the end of the false match. */
3847 if (BufCountDispChars(window->buffer, lineStart,
3848 selStart+startPos) < rectStart &&
3849 BufCountDispChars(window->buffer, lineStart,
3850 selStart+endPos) > rectStart)
3851 beginPos += 1;
3852 else
3853 beginPos = (startPos == endPos) ? endPos+1 : endPos;
3854 continue;
3858 /* Make sure the match did not start past the end (regular expressions
3859 can consider the artificial end of the range as the end of a line,
3860 and match a fictional whole line beginning there) */
3861 if (startPos == (selEnd - selStart)) {
3862 found = False;
3863 break;
3866 /* replace the string and compensate for length change */
3867 if (isRegexType(searchType)) {
3868 char replaceResult[SEARCHMAX], *foundString;
3869 foundString = BufGetRange(tempBuf, extentBW+realOffset,
3870 extentFW+realOffset+1);
3871 substSuccess = replaceUsingRE(searchString, replaceString,
3872 foundString, startPos - extentBW, replaceResult, SEARCHMAX,
3873 0 == (startPos + realOffset)
3874 ? '\0'
3875 : BufGetCharacter(tempBuf, startPos + realOffset - 1),
3876 GetWindowDelimiters(window), defaultRegexFlags(searchType));
3877 XtFree(foundString);
3879 if (!substSuccess) {
3880 /* The substitution failed. Primary reason for this would be
3881 a result string exceeding SEARCHMAX. */
3883 cancelSubst = prefOrUserCancelsSubst(window->shell, TheDisplay);
3885 if (cancelSubst) {
3886 /* No point in trying other substitutions. */
3887 break;
3891 BufReplace(tempBuf, startPos+realOffset, endPos+realOffset,
3892 replaceResult);
3893 replaceLen = strlen(replaceResult);
3894 } else {
3895 /* at this point plain substitutions (should) always work */
3896 BufReplace(tempBuf, startPos+realOffset, endPos+realOffset,
3897 replaceString);
3898 substSuccess = True;
3901 realOffset += replaceLen - (endPos - startPos);
3902 /* start again after match unless match was empty, then endPos+1 */
3903 beginPos = (startPos == endPos) ? endPos+1 : endPos;
3904 cursorPos = endPos;
3905 if (fileString[endPos] == '\0')
3906 break;
3908 XtFree(fileString);
3910 if (anyFound) {
3911 if (substSuccess || !cancelSubst) {
3912 /* Either the substitution was successful (the common case) or the
3913 user does not care and wants to have a faulty replacement. */
3915 /* replace the selected range in the real buffer */
3916 BufReplace(window->buffer, selStart, selEnd, BufAsString(tempBuf));
3918 /* set the insert point at the end of the last replacement */
3919 TextSetCursorPos(window->lastFocus, selStart + cursorPos + realOffset);
3921 /* leave non-rectangular selections selected (rect. ones after replacement
3922 are less useful since left/right positions are randomly adjusted) */
3923 if (!isRect) {
3924 BufSelect(window->buffer, selStart, selEnd + realOffset);
3927 } else {
3928 /* Nothing found, tell the user about it */
3929 if (GetPrefSearchDlogs()) {
3930 /* Avoid bug in Motif 1.1 by putting away search dialog
3931 before DialogF */
3932 if (window->findDlog && XtIsManaged(window->findDlog) &&
3933 !XmToggleButtonGetState(window->findKeepBtn))
3934 XtUnmanageChild(window->findDlog);
3935 if (window->replaceDlog && XtIsManaged(window->replaceDlog) &&
3936 !XmToggleButtonGetState(window->replaceKeepBtn))
3937 unmanageReplaceDialogs(window);
3938 DialogF(DF_INF, window->shell, 1, "String not found",
3939 "String was not found", "OK");
3940 } else
3941 XBell(TheDisplay, 0);
3944 BufFree(tempBuf);
3945 return;
3949 ** Replace all occurences of "searchString" in "window" with "replaceString".
3950 ** Also adds the search and replace strings to the global search history.
3952 int ReplaceAll(WindowInfo *window, const char *searchString,
3953 const char *replaceString, int searchType)
3955 const char *fileString;
3956 char *newFileString;
3957 int copyStart, copyEnd, replacementLen;
3959 /* reject empty string */
3960 if (*searchString == '\0')
3961 return FALSE;
3963 /* save a copy of search and replace strings in the search history */
3964 saveSearchHistory(searchString, replaceString, searchType, FALSE);
3966 /* view the entire text buffer from the text area widget as a string */
3967 fileString = BufAsString(window->buffer);
3969 newFileString = ReplaceAllInString(fileString, searchString, replaceString,
3970 searchType, &copyStart, &copyEnd, &replacementLen,
3971 GetWindowDelimiters(window));
3973 if (newFileString == NULL) {
3974 if (window->multiFileBusy) {
3975 window->replaceFailed = TRUE; /* only needed during multi-file
3976 replacements */
3977 } else if (GetPrefSearchDlogs()) {
3978 if (window->findDlog && XtIsManaged(window->findDlog) &&
3979 !XmToggleButtonGetState(window->findKeepBtn))
3980 XtUnmanageChild(window->findDlog);
3981 if (window->replaceDlog && XtIsManaged(window->replaceDlog) &&
3982 !XmToggleButtonGetState(window->replaceKeepBtn))
3983 unmanageReplaceDialogs(window);
3984 DialogF(DF_INF, window->shell, 1, "String not found",
3985 "String was not found", "OK");
3986 } else
3987 XBell(TheDisplay, 0);
3988 return FALSE;
3991 /* replace the contents of the text widget with the substituted text */
3992 BufReplace(window->buffer, copyStart, copyEnd, newFileString);
3994 /* Move the cursor to the end of the last replacement */
3995 TextSetCursorPos(window->lastFocus, copyStart + replacementLen);
3997 XtFree(newFileString);
3998 return TRUE;
4002 ** Replace all occurences of "searchString" in "inString" with "replaceString"
4003 ** and return an allocated string covering the range between the start of the
4004 ** first replacement (returned in "copyStart", and the end of the last
4005 ** replacement (returned in "copyEnd")
4007 char *ReplaceAllInString(const char *inString, const char *searchString,
4008 const char *replaceString, int searchType, int *copyStart,
4009 int *copyEnd, int *replacementLength, const char *delimiters)
4011 int beginPos, startPos, endPos, lastEndPos;
4012 int found, nFound, removeLen, replaceLen, copyLen, addLen;
4013 char *outString, *fillPtr;
4014 int searchExtentBW, searchExtentFW;
4016 /* reject empty string */
4017 if (*searchString == '\0')
4018 return NULL;
4020 /* rehearse the search first to determine the size of the buffer needed
4021 to hold the substituted text. No substitution done here yet */
4022 replaceLen = strlen(replaceString);
4023 found = TRUE;
4024 nFound = 0;
4025 removeLen = 0;
4026 addLen = 0;
4027 beginPos = 0;
4028 *copyStart = -1;
4029 while (found) {
4030 found = SearchString(inString, searchString, SEARCH_FORWARD, searchType,
4031 FALSE, beginPos, &startPos, &endPos, &searchExtentBW,
4032 &searchExtentFW, delimiters);
4033 if (found) {
4034 if (*copyStart < 0)
4035 *copyStart = startPos;
4036 *copyEnd = endPos;
4037 /* start next after match unless match was empty, then endPos+1 */
4038 beginPos = (startPos == endPos) ? endPos+1 : endPos;
4039 nFound++;
4040 removeLen += endPos - startPos;
4041 if (isRegexType(searchType)) {
4042 char replaceResult[SEARCHMAX];
4043 replaceUsingRE(searchString, replaceString, &inString[searchExtentBW],
4044 startPos-searchExtentBW,
4045 replaceResult, SEARCHMAX, startPos == 0 ? '\0' :
4046 inString[startPos-1], delimiters,
4047 defaultRegexFlags(searchType));
4048 addLen += strlen(replaceResult);
4049 } else
4050 addLen += replaceLen;
4051 if (inString[endPos] == '\0')
4052 break;
4055 if (nFound == 0)
4056 return NULL;
4058 /* Allocate a new buffer to hold all of the new text between the first
4059 and last substitutions */
4060 copyLen = *copyEnd - *copyStart;
4061 outString = XtMalloc(copyLen - removeLen + addLen + 1);
4063 /* Scan through the text buffer again, substituting the replace string
4064 and copying the part between replaced text to the new buffer */
4065 found = TRUE;
4066 beginPos = 0;
4067 lastEndPos = 0;
4068 fillPtr = outString;
4069 while (found) {
4070 found = SearchString(inString, searchString, SEARCH_FORWARD, searchType,
4071 FALSE, beginPos, &startPos, &endPos, &searchExtentBW,
4072 &searchExtentFW, delimiters);
4073 if (found) {
4074 if (beginPos != 0) {
4075 memcpy(fillPtr, &inString[lastEndPos], startPos - lastEndPos);
4076 fillPtr += startPos - lastEndPos;
4078 if (isRegexType(searchType)) {
4079 char replaceResult[SEARCHMAX];
4080 replaceUsingRE(searchString, replaceString, &inString[searchExtentBW],
4081 startPos-searchExtentBW,
4082 replaceResult, SEARCHMAX, startPos == 0 ? '\0' :
4083 inString[startPos-1], delimiters,
4084 defaultRegexFlags(searchType));
4085 replaceLen = strlen(replaceResult);
4086 memcpy(fillPtr, replaceResult, replaceLen);
4087 } else {
4088 memcpy(fillPtr, replaceString, replaceLen);
4090 fillPtr += replaceLen;
4091 lastEndPos = endPos;
4092 /* start next after match unless match was empty, then endPos+1 */
4093 beginPos = (startPos == endPos) ? endPos+1 : endPos;
4094 if (inString[endPos] == '\0')
4095 break;
4098 *fillPtr = '\0';
4099 *replacementLength = fillPtr - outString;
4100 return outString;
4104 ** If this is an incremental search and BeepOnSearchWrap is on:
4105 ** Emit a beep if the search wrapped over BOF/EOF compared to
4106 ** the last startPos of the current incremental search.
4108 static void iSearchTryBeepOnWrap(WindowInfo *window, int direction,
4109 int beginPos, int startPos)
4111 if (GetPrefBeepOnSearchWrap()) {
4112 if (direction == SEARCH_FORWARD) {
4113 if ((startPos >= beginPos
4114 && window->iSearchLastBeginPos < beginPos)
4115 ||(startPos < beginPos
4116 && window->iSearchLastBeginPos >= beginPos)) {
4117 XBell(TheDisplay, 0);
4119 } else {
4120 if ((startPos <= beginPos
4121 && window->iSearchLastBeginPos > beginPos)
4122 ||(startPos > beginPos
4123 && window->iSearchLastBeginPos <= beginPos)) {
4124 XBell(TheDisplay, 0);
4131 ** Search the text in "window", attempting to match "searchString"
4133 int SearchWindow(WindowInfo *window, int direction, const char *searchString,
4134 int searchType, int searchWrap, int beginPos, int *startPos,
4135 int *endPos, int *extentBW, int *extentFW)
4137 const char *fileString;
4138 int found, resp, fileEnd = window->buffer->length - 1, outsideBounds;
4140 /* reject empty string */
4141 if (*searchString == '\0')
4142 return FALSE;
4144 /* get the entire text buffer from the text area widget */
4145 fileString = BufAsString(window->buffer);
4147 /* If we're already outside the boundaries, we must consider wrapping
4148 immediately (Note: fileEnd+1 is a valid starting position. Consider
4149 searching for $ at the end of a file ending with \n.) */
4150 if ((direction == SEARCH_FORWARD && beginPos > fileEnd + 1)
4151 || (direction == SEARCH_BACKWARD && beginPos < 0))
4153 outsideBounds = TRUE;
4154 } else
4156 outsideBounds = FALSE;
4159 /* search the string copied from the text area widget, and present
4160 dialogs, or just beep. iSearchStartPos is not a perfect indicator that
4161 an incremental search is in progress. A parameter would be better. */
4162 if (window->iSearchStartPos == -1) { /* normal search */
4163 found = !outsideBounds &&
4164 SearchString(fileString, searchString, direction, searchType,
4165 FALSE, beginPos, startPos, endPos, extentBW, extentFW,
4166 GetWindowDelimiters(window));
4167 /* Avoid Motif 1.1 bug by putting away search dialog before DialogF */
4168 if (window->findDlog && XtIsManaged(window->findDlog) &&
4169 !XmToggleButtonGetState(window->findKeepBtn))
4170 XtUnmanageChild(window->findDlog);
4171 if (window->replaceDlog && XtIsManaged(window->replaceDlog) &&
4172 !XmToggleButtonGetState(window->replaceKeepBtn))
4173 unmanageReplaceDialogs(window);
4174 if (!found) {
4175 if (searchWrap) {
4176 if (direction == SEARCH_FORWARD && beginPos != 0) {
4177 if(GetPrefBeepOnSearchWrap()) {
4178 XBell(TheDisplay, 0);
4179 } else if (GetPrefSearchDlogs()) {
4180 resp = DialogF(DF_QUES, window->shell, 2, "Wrap Search",
4181 "Continue search from\nbeginning of file?",
4182 "Continue", "Cancel");
4183 if (resp == 2) {
4184 return False;
4187 found = SearchString(fileString, searchString, direction,
4188 searchType, FALSE, 0, startPos, endPos, extentBW,
4189 extentFW, GetWindowDelimiters(window));
4190 } else if (direction == SEARCH_BACKWARD && beginPos != fileEnd) {
4191 if(GetPrefBeepOnSearchWrap()) {
4192 XBell(TheDisplay, 0);
4193 } else if (GetPrefSearchDlogs()) {
4194 resp = DialogF(DF_QUES, window->shell, 2, "Wrap Search",
4195 "Continue search\nfrom end of file?", "Continue",
4196 "Cancel");
4197 if (resp == 2) {
4198 return False;
4201 found = SearchString(fileString, searchString, direction,
4202 searchType, FALSE, fileEnd + 1, startPos, endPos, extentBW,
4203 extentFW, GetWindowDelimiters(window));
4206 if (!found) {
4207 if (GetPrefSearchDlogs()) {
4208 DialogF(DF_INF, window->shell, 1, "String not found",
4209 "String was not found","OK");
4210 } else {
4211 XBell(TheDisplay, 0);
4215 } else { /* incremental search */
4216 if (outsideBounds && searchWrap) {
4217 if (direction == SEARCH_FORWARD) beginPos = 0;
4218 else beginPos = fileEnd+1;
4219 outsideBounds = FALSE;
4221 found = !outsideBounds &&
4222 SearchString(fileString, searchString, direction,
4223 searchType, searchWrap, beginPos, startPos, endPos,
4224 extentBW, extentFW, GetWindowDelimiters(window));
4225 if (found) {
4226 iSearchTryBeepOnWrap(window, direction, beginPos, *startPos);
4227 } else
4228 XBell(TheDisplay, 0);
4231 return found;
4235 ** Search the null terminated string "string" for "searchString", beginning at
4236 ** "beginPos". Returns the boundaries of the match in "startPos" and "endPos".
4237 ** searchExtentBW and searchExtentFW return the backwardmost and forwardmost
4238 ** positions used to make the match, which are usually startPos and endPos,
4239 ** but may extend further if positive lookahead or lookbehind was used in
4240 ** a regular expression match. "delimiters" may be used to provide an
4241 ** alternative set of word delimiters for regular expression "<" and ">"
4242 ** characters, or simply passed as null for the default delimiter set.
4244 int SearchString(const char *string, const char *searchString, int direction,
4245 int searchType, int wrap, int beginPos, int *startPos, int *endPos,
4246 int *searchExtentBW, int *searchExtentFW, const char *delimiters)
4248 switch (searchType) {
4249 case SEARCH_CASE_SENSE_WORD:
4250 return searchLiteralWord(string, searchString, TRUE, direction, wrap,
4251 beginPos, startPos, endPos, delimiters);
4252 case SEARCH_LITERAL_WORD:
4253 return searchLiteralWord(string, searchString, FALSE, direction, wrap,
4254 beginPos, startPos, endPos, delimiters);
4255 case SEARCH_CASE_SENSE:
4256 return searchLiteral(string, searchString, TRUE, direction, wrap,
4257 beginPos, startPos, endPos, searchExtentBW,
4258 searchExtentFW);
4259 case SEARCH_LITERAL:
4260 return searchLiteral(string, searchString, FALSE, direction, wrap,
4261 beginPos, startPos, endPos, searchExtentBW, searchExtentFW);
4262 case SEARCH_REGEX:
4263 return searchRegex(string, searchString, direction, wrap,
4264 beginPos, startPos, endPos, searchExtentBW, searchExtentFW,
4265 delimiters, REDFLT_STANDARD);
4266 case SEARCH_REGEX_NOCASE:
4267 return searchRegex(string, searchString, direction, wrap,
4268 beginPos, startPos, endPos, searchExtentBW, searchExtentFW,
4269 delimiters, REDFLT_CASE_INSENSITIVE);
4271 return FALSE; /* never reached, just makes compilers happy */
4275 ** Parses a search type description string. If the string contains a valid
4276 ** search type description, returns TRUE and writes the corresponding
4277 ** SearchType in searchType. Returns FALSE and leaves searchType untouched
4278 ** otherwise. (Originally written by Markus Schwarzenberg; slightly adapted).
4280 int StringToSearchType(const char * string, int *searchType)
4282 int i;
4283 for (i = 0; searchTypeStrings[i]; i++) {
4284 if (!strcmp(string, searchTypeStrings[i])) {
4285 break;
4288 if (!searchTypeStrings[i]) {
4289 return FALSE;
4291 *searchType = i;
4292 return TRUE;
4296 ** Searches for whole words (Markus Schwarzenberg).
4298 ** If the first/last character of `searchString' is a "normal
4299 ** word character" (not contained in `delimiters', not a whitespace)
4300 ** then limit search to strings, who's next left/next right character
4301 ** is contained in `delimiters' or is a whitespace or text begin or end.
4303 ** If the first/last character of `searchString' itself is contained
4304 ** in delimiters or is a white space, then the neighbour character of the
4305 ** first/last character will not be checked, just a simple match
4306 ** will suffice in that case.
4309 static int searchLiteralWord(const char *string, const char *searchString, int caseSense,
4310 int direction, int wrap, int beginPos, int *startPos, int *endPos,
4311 const char * delimiters)
4313 /* This is critical code for the speed of searches. */
4314 /* For efficiency, we define the macro DOSEARCH with the guts of the search */
4315 /* routine and repeat it, changing the parameters of the outer loop for the */
4316 /* searching, forwards, backwards, and before and after the begin point */
4317 #define DOSEARCHWORD() \
4318 if (*filePtr == *ucString || *filePtr == *lcString) { \
4319 /* matched first character */ \
4320 ucPtr = ucString; \
4321 lcPtr = lcString; \
4322 tempPtr = filePtr; \
4323 while (*tempPtr == *ucPtr || *tempPtr == *lcPtr) { \
4324 tempPtr++; ucPtr++; lcPtr++; \
4325 if ( *ucPtr == 0 /* matched whole string */ \
4326 && (cignore_R ||\
4327 isspace((unsigned char)*tempPtr) ||\
4328 strchr(delimiters, *tempPtr) ) \
4329 /* next char right delimits word ? */ \
4330 && (cignore_L ||\
4331 filePtr==string || /* border case */ \
4332 isspace((unsigned char)filePtr[-1]) ||\
4333 strchr(delimiters,filePtr[-1]) ))\
4334 /* next char left delimits word ? */ { \
4335 *startPos = filePtr - string; \
4336 *endPos = tempPtr - string; \
4337 return TRUE; \
4342 register const char *filePtr, *tempPtr, *ucPtr, *lcPtr;
4343 char lcString[SEARCHMAX], ucString[SEARCHMAX];
4345 int cignore_L=0, cignore_R=0;
4347 /* SEARCHMAX was fine in the original NEdit, but it should be done away
4348 with now that searching can be done from macros without limits.
4349 Returning search failure here is cheating users. This limit is not
4350 documented. */
4351 if (strlen(searchString) >= SEARCHMAX)
4352 return FALSE;
4354 /* If there is no language mode, we use the default list of delimiters */
4355 if (delimiters==NULL) delimiters = GetPrefDelimiters();
4357 if ( isspace((unsigned char)*searchString)
4358 || strchr(delimiters, *searchString))
4359 cignore_L=1;
4361 if ( isspace((unsigned char)searchString[strlen(searchString)-1])
4362 || strchr(delimiters, searchString[strlen(searchString)-1]) )
4363 cignore_R=1;
4365 if (caseSense) {
4366 strcpy(ucString, searchString);
4367 strcpy(lcString, searchString);
4368 } else {
4369 upCaseString(ucString, searchString);
4370 downCaseString(lcString, searchString);
4373 if (direction == SEARCH_FORWARD) {
4374 /* search from beginPos to end of string */
4375 for (filePtr=string+beginPos; *filePtr!=0; filePtr++) {
4376 DOSEARCHWORD()
4378 if (!wrap)
4379 return FALSE;
4381 /* search from start of file to beginPos */
4382 for (filePtr=string; filePtr<=string+beginPos; filePtr++) {
4383 DOSEARCHWORD()
4385 return FALSE;
4386 } else {
4387 /* SEARCH_BACKWARD */
4388 /* search from beginPos to start of file. A negative begin pos */
4389 /* says begin searching from the far end of the file */
4390 if (beginPos >= 0) {
4391 for (filePtr=string+beginPos; filePtr>=string; filePtr--) {
4392 DOSEARCHWORD()
4395 if (!wrap)
4396 return FALSE;
4397 /* search from end of file to beginPos */
4398 /*... this strlen call is extreme inefficiency, but it's not obvious */
4399 /* how to get the text string length from the text widget (under 1.1)*/
4400 for (filePtr=string+strlen(string); filePtr>=string+beginPos; filePtr--) {
4401 DOSEARCHWORD()
4403 return FALSE;
4408 static int searchLiteral(const char *string, const char *searchString, int caseSense,
4409 int direction, int wrap, int beginPos, int *startPos, int *endPos,
4410 int *searchExtentBW, int *searchExtentFW)
4412 /* This is critical code for the speed of searches. */
4413 /* For efficiency, we define the macro DOSEARCH with the guts of the search */
4414 /* routine and repeat it, changing the parameters of the outer loop for the */
4415 /* searching, forwards, backwards, and before and after the begin point */
4416 #define DOSEARCH() \
4417 if (*filePtr == *ucString || *filePtr == *lcString) { \
4418 /* matched first character */ \
4419 ucPtr = ucString; \
4420 lcPtr = lcString; \
4421 tempPtr = filePtr; \
4422 while (*tempPtr == *ucPtr || *tempPtr == *lcPtr) { \
4423 tempPtr++; ucPtr++; lcPtr++; \
4424 if (*ucPtr == 0) { \
4425 /* matched whole string */ \
4426 *startPos = filePtr - string; \
4427 *endPos = tempPtr - string; \
4428 if (searchExtentBW != NULL) \
4429 *searchExtentBW = *startPos; \
4430 if (searchExtentFW != NULL) \
4431 *searchExtentFW = *endPos; \
4432 return TRUE; \
4437 register const char *filePtr, *tempPtr, *ucPtr, *lcPtr;
4438 char lcString[SEARCHMAX], ucString[SEARCHMAX];
4440 /* SEARCHMAX was fine in the original NEdit, but it should be done away with
4441 now that searching can be done from macros without limits. Returning
4442 search failure here is cheating users. This limit is not documented. */
4443 if (strlen(searchString) >= SEARCHMAX)
4444 return FALSE;
4446 if (caseSense) {
4447 strcpy(ucString, searchString);
4448 strcpy(lcString, searchString);
4449 } else {
4450 upCaseString(ucString, searchString);
4451 downCaseString(lcString, searchString);
4454 if (direction == SEARCH_FORWARD) {
4455 /* search from beginPos to end of string */
4456 for (filePtr=string+beginPos; *filePtr!=0; filePtr++) {
4457 DOSEARCH()
4459 if (!wrap)
4460 return FALSE;
4461 /* search from start of file to beginPos */
4462 for (filePtr=string; filePtr<=string+beginPos; filePtr++) {
4463 DOSEARCH()
4465 return FALSE;
4466 } else {
4467 /* SEARCH_BACKWARD */
4468 /* search from beginPos to start of file. A negative begin pos */
4469 /* says begin searching from the far end of the file */
4470 if (beginPos >= 0) {
4471 for (filePtr=string+beginPos; filePtr>=string; filePtr--) {
4472 DOSEARCH()
4475 if (!wrap)
4476 return FALSE;
4477 /* search from end of file to beginPos */
4478 /*... this strlen call is extreme inefficiency, but it's not obvious */
4479 /* how to get the text string length from the text widget (under 1.1)*/
4480 for (filePtr=string+strlen(string);
4481 filePtr>=string+beginPos; filePtr--) {
4482 DOSEARCH()
4484 return FALSE;
4488 static int searchRegex(const char *string, const char *searchString, int direction,
4489 int wrap, int beginPos, int *startPos, int *endPos, int *searchExtentBW,
4490 int *searchExtentFW, const char *delimiters, int defaultFlags)
4492 if (direction == SEARCH_FORWARD)
4493 return forwardRegexSearch(string, searchString, wrap,
4494 beginPos, startPos, endPos, searchExtentBW, searchExtentFW,
4495 delimiters, defaultFlags);
4496 else
4497 return backwardRegexSearch(string, searchString, wrap,
4498 beginPos, startPos, endPos, searchExtentBW, searchExtentFW,
4499 delimiters, defaultFlags);
4502 static int forwardRegexSearch(const char *string, const char *searchString, int wrap,
4503 int beginPos, int *startPos, int *endPos, int *searchExtentBW,
4504 int *searchExtentFW, const char *delimiters, int defaultFlags)
4506 regexp *compiledRE = NULL;
4507 char *compileMsg;
4509 /* compile the search string for searching with ExecRE. Note that
4510 this does not process errors from compiling the expression. It
4511 assumes that the expression was checked earlier. */
4512 compiledRE = CompileRE(searchString, &compileMsg, defaultFlags);
4513 if (compiledRE == NULL)
4514 return FALSE;
4516 /* search from beginPos to end of string */
4517 if (ExecRE(compiledRE, string + beginPos, NULL, FALSE,
4518 (beginPos == 0) ? '\0' : string[beginPos-1], '\0', delimiters,
4519 string, NULL)) {
4520 *startPos = compiledRE->startp[0] - string;
4521 *endPos = compiledRE->endp[0] - string;
4522 if (searchExtentFW != NULL)
4523 *searchExtentFW = compiledRE->extentpFW - string;
4524 if (searchExtentBW != NULL)
4525 *searchExtentBW = compiledRE->extentpBW - string;
4526 free((char *)compiledRE);
4527 return TRUE;
4530 /* if wrap turned off, we're done */
4531 if (!wrap) {
4532 free((char *)compiledRE);
4533 return FALSE;
4536 /* search from the beginning of the string to beginPos */
4537 if (ExecRE(compiledRE, string, string + beginPos, FALSE, '\0',
4538 string[beginPos], delimiters, string, NULL)) {
4539 *startPos = compiledRE->startp[0] - string;
4540 *endPos = compiledRE->endp[0] - string;
4541 if (searchExtentFW != NULL)
4542 *searchExtentFW = compiledRE->extentpFW - string;
4543 if (searchExtentBW != NULL)
4544 *searchExtentBW = compiledRE->extentpBW - string;
4545 free((char *)compiledRE);
4546 return TRUE;
4549 free((char *)compiledRE);
4550 return FALSE;
4553 static int backwardRegexSearch(const char *string, const char *searchString, int wrap,
4554 int beginPos, int *startPos, int *endPos, int *searchExtentBW,
4555 int *searchExtentFW, const char *delimiters, int defaultFlags)
4557 regexp *compiledRE = NULL;
4558 char *compileMsg;
4559 int length;
4561 /* compile the search string for searching with ExecRE */
4562 compiledRE = CompileRE(searchString, &compileMsg, defaultFlags);
4563 if (compiledRE == NULL)
4564 return FALSE;
4566 /* search from beginPos to start of file. A negative begin pos */
4567 /* says begin searching from the far end of the file. */
4568 if (beginPos >= 0) {
4569 if (ExecRE(compiledRE, string, string + beginPos, TRUE, '\0', '\0',
4570 delimiters, string, NULL)) {
4571 *startPos = compiledRE->startp[0] - string;
4572 *endPos = compiledRE->endp[0] - string;
4573 if (searchExtentFW != NULL)
4574 *searchExtentFW = compiledRE->extentpFW - string;
4575 if (searchExtentBW != NULL)
4576 *searchExtentBW = compiledRE->extentpBW - string;
4577 free((char *)compiledRE);
4578 return TRUE;
4582 /* if wrap turned off, we're done */
4583 if (!wrap) {
4584 free((char *)compiledRE);
4585 return FALSE;
4588 /* search from the end of the string to beginPos */
4589 if (beginPos < 0)
4590 beginPos = 0;
4591 length = strlen(string); /* sadly, this means scanning entire string */
4592 if (ExecRE(compiledRE, string + beginPos, string + length, TRUE,
4593 (beginPos == 0) ? '\0' : string[beginPos-1], '\0', delimiters,
4594 string, NULL)) {
4595 *startPos = compiledRE->startp[0] - string;
4596 *endPos = compiledRE->endp[0] - string;
4597 if (searchExtentFW != NULL)
4598 *searchExtentFW = compiledRE->extentpFW - string;
4599 if (searchExtentBW != NULL)
4600 *searchExtentBW = compiledRE->extentpBW - string;
4601 free((char *)compiledRE);
4602 return TRUE;
4604 free((char *)compiledRE);
4605 return FALSE;
4608 static void upCaseString(char *outString, const char *inString)
4610 char *outPtr;
4611 const char *inPtr;
4613 for (outPtr=outString, inPtr=inString; *inPtr!=0; inPtr++, outPtr++) {
4614 *outPtr = toupper((unsigned char)*inPtr);
4616 *outPtr = 0;
4619 static void downCaseString(char *outString, const char *inString)
4621 char *outPtr;
4622 const char *inPtr;
4624 for (outPtr=outString, inPtr=inString; *inPtr!=0; inPtr++, outPtr++) {
4625 *outPtr = tolower((unsigned char)*inPtr);
4627 *outPtr = 0;
4631 ** resetFindTabGroup & resetReplaceTabGroup are really gruesome kludges to
4632 ** set the keyboard traversal. XmProcessTraversal does not work at
4633 ** all on these dialogs. ...It seems to have started working around
4634 ** Motif 1.1.2
4636 static void resetFindTabGroup(WindowInfo *window)
4638 XmProcessTraversal(window->findText, XmTRAVERSE_CURRENT);
4640 static void resetReplaceTabGroup(WindowInfo *window)
4642 XmProcessTraversal(window->replaceText, XmTRAVERSE_CURRENT);
4646 ** Return TRUE if "searchString" exactly matches the text in the window's
4647 ** current primary selection using search algorithm "searchType". If true,
4648 ** also return the position of the selection in "left" and "right".
4650 static int searchMatchesSelection(WindowInfo *window, const char *searchString,
4651 int searchType, int *left, int *right, int *searchExtentBW,
4652 int *searchExtentFW)
4654 int selLen, selStart, selEnd, startPos, endPos, extentBW, extentFW, beginPos;
4655 int regexLookContext = isRegexType(searchType) ? 1000 : 0;
4656 char *string;
4657 int found, isRect, rectStart, rectEnd, lineStart = 0;
4659 /* find length of selection, give up on no selection or too long */
4660 if (!BufGetEmptySelectionPos(window->buffer, &selStart, &selEnd, &isRect,
4661 &rectStart, &rectEnd))
4662 return FALSE;
4663 if (selEnd - selStart > SEARCHMAX)
4664 return FALSE;
4666 /* if the selection is rectangular, don't match if it spans lines */
4667 if (isRect) {
4668 lineStart = BufStartOfLine(window->buffer, selStart);
4669 if (lineStart != BufStartOfLine(window->buffer, selEnd))
4670 return FALSE;
4673 /* get the selected text plus some additional context for regular
4674 expression lookahead */
4675 if (isRect) {
4676 int stringStart = lineStart + rectStart - regexLookContext;
4677 if (stringStart < 0) stringStart = 0;
4678 string = BufGetRange(window->buffer, stringStart,
4679 lineStart + rectEnd + regexLookContext);
4680 selLen = rectEnd - rectStart;
4681 beginPos = lineStart + rectStart - stringStart;
4682 } else {
4683 int stringStart = selStart - regexLookContext;
4684 if (stringStart < 0) stringStart = 0;
4685 string = BufGetRange(window->buffer, stringStart,
4686 selEnd + regexLookContext);
4687 selLen = selEnd - selStart;
4688 beginPos = selStart - stringStart;
4690 if (*string == '\0') {
4691 XtFree(string);
4692 return FALSE;
4695 /* search for the string in the selection (we are only interested */
4696 /* in an exact match, but the procedure SearchString does important */
4697 /* stuff like applying the correct matching algorithm) */
4698 found = SearchString(string, searchString, SEARCH_FORWARD, searchType,
4699 FALSE, beginPos, &startPos, &endPos, &extentBW, &extentFW,
4700 GetWindowDelimiters(window));
4701 XtFree(string);
4703 /* decide if it is an exact match */
4704 if (!found)
4705 return FALSE;
4706 if (startPos != beginPos || endPos - beginPos != selLen )
4707 return FALSE;
4709 /* return the start and end of the selection */
4710 if (isRect)
4711 GetSimpleSelection(window->buffer, left, right);
4712 else {
4713 *left = selStart;
4714 *right = selEnd;
4716 if (searchExtentBW != NULL)
4717 *searchExtentBW = *left - (startPos - extentBW);
4719 if (searchExtentFW != NULL)
4720 *searchExtentFW = *right + extentFW - endPos;
4721 return TRUE;
4725 ** Substitutes a replace string for a string that was matched using a
4726 ** regular expression. This was added later and is rather ineficient
4727 ** because instead of using the compiled regular expression that was used
4728 ** to make the match in the first place, it re-compiles the expression
4729 ** and redoes the search on the already-matched string. This allows the
4730 ** code to continue using strings to represent the search and replace
4731 ** items.
4733 static Boolean replaceUsingRE(const char* searchStr, const char* replaceStr,
4734 const char* sourceStr, const int beginPos, char* destStr,
4735 const int maxDestLen, const int prevChar, const char* delimiters,
4736 const int defaultFlags)
4738 regexp *compiledRE;
4739 char *compileMsg;
4740 Boolean substResult = False;
4742 compiledRE = CompileRE(searchStr, &compileMsg, defaultFlags);
4743 ExecRE(compiledRE, sourceStr+beginPos, NULL, False, prevChar, '\0',
4744 delimiters, sourceStr, NULL);
4745 substResult = SubstituteRE(compiledRE, replaceStr, destStr, maxDestLen);
4746 free((char *)compiledRE);
4748 return substResult;
4752 ** Store the search and replace strings, and search type for later recall.
4753 ** If replaceString is NULL, duplicate the last replaceString used.
4754 ** Contiguous incremental searches share the same history entry (each new
4755 ** search modifies the current search string, until a non-incremental search
4756 ** is made. To mark the end of an incremental search, call saveSearchHistory
4757 ** again with an empty search string and isIncremental==False.
4759 static void saveSearchHistory(const char *searchString,
4760 const char *replaceString, int searchType, int isIncremental)
4762 char *sStr, *rStr;
4763 static int currentItemIsIncremental = FALSE;
4764 WindowInfo *w;
4766 /* Cancel accumulation of contiguous incremental searches (even if the
4767 information is not worthy of saving) if search is not incremental */
4768 if (!isIncremental)
4769 currentItemIsIncremental = FALSE;
4771 /* Don't save empty search strings */
4772 if (searchString[0] == '\0')
4773 return;
4775 /* If replaceString is NULL, duplicate the last one (if any) */
4776 if (replaceString == NULL)
4777 replaceString = NHist >= 1 ? ReplaceHistory[historyIndex(1)] : "";
4779 /* Compare the current search and replace strings against the saved ones.
4780 If they are identical, don't bother saving */
4781 if (NHist >= 1 && searchType == SearchTypeHistory[historyIndex(1)] &&
4782 !strcmp(SearchHistory[historyIndex(1)], searchString) &&
4783 !strcmp(ReplaceHistory[historyIndex(1)], replaceString)) {
4784 return;
4787 /* If the current history item came from an incremental search, and the
4788 new one is also incremental, just update the entry */
4789 if (currentItemIsIncremental && isIncremental) {
4790 XtFree(SearchHistory[historyIndex(1)]);
4791 SearchHistory[historyIndex(1)] = XtNewString(searchString);
4792 SearchTypeHistory[historyIndex(1)] = searchType;
4793 return;
4795 currentItemIsIncremental = isIncremental;
4797 if (NHist==0) {
4798 for (w=WindowList; w!=NULL; w=w->next) {
4799 if (!IsTopDocument(w))
4800 continue;
4801 XtSetSensitive(w->findAgainItem, True);
4802 XtSetSensitive(w->replaceFindAgainItem, True);
4803 XtSetSensitive(w->replaceAgainItem, True);
4807 /* If there are more than MAX_SEARCH_HISTORY strings saved, recycle
4808 some space, free the entry that's about to be overwritten */
4809 if (NHist == MAX_SEARCH_HISTORY) {
4810 XtFree(SearchHistory[HistStart]);
4811 XtFree(ReplaceHistory[HistStart]);
4812 } else
4813 NHist++;
4815 /* Allocate and copy the search and replace strings and add them to the
4816 circular buffers at HistStart, bump the buffer pointer to next pos. */
4817 sStr = XtMalloc(strlen(searchString) + 1);
4818 rStr = XtMalloc(strlen(replaceString) + 1);
4819 strcpy(sStr, searchString);
4820 strcpy(rStr, replaceString);
4821 SearchHistory[HistStart] = sStr;
4822 ReplaceHistory[HistStart] = rStr;
4823 SearchTypeHistory[HistStart] = searchType;
4824 HistStart++;
4825 if (HistStart >= MAX_SEARCH_HISTORY)
4826 HistStart = 0;
4830 ** return an index into the circular buffer arrays of history information
4831 ** for search strings, given the number of saveSearchHistory cycles back from
4832 ** the current time.
4834 static int historyIndex(int nCycles)
4836 int index;
4838 if (nCycles > NHist || nCycles <= 0)
4839 return -1;
4840 index = HistStart - nCycles;
4841 if (index < 0)
4842 index = MAX_SEARCH_HISTORY + index;
4843 return index;
4847 ** Return a pointer to the string describing search type for search action
4848 ** routine parameters (see menu.c for processing of action routines)
4850 static char *searchTypeArg(int searchType)
4852 if (0 <= searchType && searchType < N_SEARCH_TYPES) {
4853 return searchTypeStrings[searchType];
4855 return searchTypeStrings[SEARCH_LITERAL];
4859 ** Return a pointer to the string describing search wrap for search action
4860 ** routine parameters (see menu.c for processing of action routines)
4862 static char *searchWrapArg(int searchWrap)
4864 if (searchWrap) {
4865 return "wrap";
4867 return "nowrap";
4871 ** Return a pointer to the string describing search direction for search action
4872 ** routine parameters (see menu.c for processing of action routines)
4874 static char *directionArg(int direction)
4876 if (direction == SEARCH_BACKWARD)
4877 return "backward";
4878 return "forward";
4882 ** Checks whether a search mode in one of the regular expression modes.
4884 static int isRegexType(int searchType)
4886 return searchType == SEARCH_REGEX || searchType == SEARCH_REGEX_NOCASE;
4890 ** Returns the default flags for regular expression matching, given a
4891 ** regular expression search mode.
4893 static int defaultRegexFlags(int searchType)
4895 switch (searchType) {
4896 case SEARCH_REGEX:
4897 return REDFLT_STANDARD;
4898 case SEARCH_REGEX_NOCASE:
4899 return REDFLT_CASE_INSENSITIVE;
4900 default:
4901 /* We should never get here, but just in case ... */
4902 return REDFLT_STANDARD;
4907 ** The next 4 callbacks handle the states of find/replace toggle
4908 ** buttons, which depend on the state of the "Regex" button, and the
4909 ** sensitivity of the Whole Word buttons.
4910 ** Callbacks are necessary for both "Regex" and "Case Sensitive"
4911 ** buttons to make sure the states are saved even after a cancel operation.
4913 ** If sticky case sensitivity is requested, the behaviour is as follows:
4914 ** The first time "Regular expression" is checked, "Match case" gets
4915 ** checked too. Thereafter, checking or unchecking "Regular expression"
4916 ** restores the "Match case" button to the setting it had the last
4917 ** time when literals or REs where used.
4918 ** Without sticky behaviour, the state of the Regex button doesn't influence
4919 ** the state of the Case Sensitive button.
4921 ** Independently, the state of the buttons is always restored to the
4922 ** default state when a dialog is popped up, and when the user returns
4923 ** from stepping through the search history.
4925 ** NOTE: similar call-backs exist for the incremental search bar; see window.c.
4927 static void findRegExpToggleCB(Widget w, XtPointer clientData, XtPointer callData)
4929 WindowInfo * window = WidgetToWindow(w);
4930 int searchRegex = XmToggleButtonGetState(w);
4931 int searchCaseSense = XmToggleButtonGetState(window->findCaseToggle);
4933 /* In sticky mode, restore the state of the Case Sensitive button */
4934 if(GetPrefStickyCaseSenseBtn()) {
4935 if(searchRegex) {
4936 window->findLastLiteralCase = searchCaseSense;
4937 XmToggleButtonSetState(window->findCaseToggle,
4938 window->findLastRegexCase, False);
4939 } else {
4940 window->findLastRegexCase = searchCaseSense;
4941 XmToggleButtonSetState(window->findCaseToggle,
4942 window->findLastLiteralCase, False);
4945 /* make the Whole Word button insensitive for regex searches */
4946 XtSetSensitive(window->findWordToggle, !searchRegex);
4949 static void replaceRegExpToggleCB(Widget w, XtPointer clientData, XtPointer callData)
4951 WindowInfo * window = WidgetToWindow(w);
4952 int searchRegex = XmToggleButtonGetState(w);
4953 int searchCaseSense = XmToggleButtonGetState(window->replaceCaseToggle);
4955 /* In sticky mode, restore the state of the Case Sensitive button */
4956 if(GetPrefStickyCaseSenseBtn()) {
4957 if(searchRegex) {
4958 window->replaceLastLiteralCase = searchCaseSense;
4959 XmToggleButtonSetState(window->replaceCaseToggle,
4960 window->replaceLastRegexCase, False);
4961 } else {
4962 window->replaceLastRegexCase = searchCaseSense;
4963 XmToggleButtonSetState(window->replaceCaseToggle,
4964 window->replaceLastLiteralCase, False);
4967 /* make the Whole Word button insensitive for regex searches */
4968 XtSetSensitive(window->replaceWordToggle, !searchRegex);
4971 static void iSearchRegExpToggleCB(Widget w, XtPointer clientData, XtPointer callData)
4973 WindowInfo * window = WidgetToWindow(w);
4974 int searchRegex = XmToggleButtonGetState(w);
4975 int searchCaseSense = XmToggleButtonGetState(window->iSearchCaseToggle);
4977 /* In sticky mode, restore the state of the Case Sensitive button */
4978 if(GetPrefStickyCaseSenseBtn()) {
4979 if(searchRegex) {
4980 window->iSearchLastLiteralCase = searchCaseSense;
4981 XmToggleButtonSetState(window->iSearchCaseToggle,
4982 window->iSearchLastRegexCase, False);
4983 } else {
4984 window->iSearchLastRegexCase = searchCaseSense;
4985 XmToggleButtonSetState(window->iSearchCaseToggle,
4986 window->iSearchLastLiteralCase, False);
4989 /* The iSearch bar has no Whole Word button to enable/disable. */
4991 static void findCaseToggleCB(Widget w, XtPointer clientData, XtPointer callData)
4993 WindowInfo * window = WidgetToWindow(w);
4994 int searchCaseSense = XmToggleButtonGetState(w);
4996 /* Save the state of the Case Sensitive button
4997 depending on the state of the Regex button*/
4998 if(XmToggleButtonGetState(window->findRegexToggle))
4999 window->findLastRegexCase = searchCaseSense;
5000 else
5001 window->findLastLiteralCase = searchCaseSense;
5004 static void replaceCaseToggleCB(Widget w, XtPointer clientData, XtPointer callData)
5006 WindowInfo * window = WidgetToWindow(w);
5007 int searchCaseSense = XmToggleButtonGetState(w);
5009 /* Save the state of the Case Sensitive button
5010 depending on the state of the Regex button*/
5011 if(XmToggleButtonGetState(window->replaceRegexToggle))
5012 window->replaceLastRegexCase = searchCaseSense;
5013 else
5014 window->replaceLastLiteralCase = searchCaseSense;
5017 static void iSearchCaseToggleCB(Widget w, XtPointer clientData, XtPointer callData)
5019 WindowInfo * window = WidgetToWindow(w);
5020 int searchCaseSense = XmToggleButtonGetState(w);
5022 /* Save the state of the Case Sensitive button
5023 depending on the state of the Regex button*/
5024 if(XmToggleButtonGetState(window->iSearchRegexToggle))
5025 window->iSearchLastRegexCase = searchCaseSense;
5026 else
5027 window->iSearchLastLiteralCase = searchCaseSense;