Applied the "OpenMotif sticky radio button" workaround to all radio buttons.
[nedit.git] / source / search.c
bloba49d2486eafa815351f0f2d5c606d9475ea8ca19
1 static const char CVSID[] = "$Id: search.c,v 1.64 2004/01/16 09:18:28 edg Exp $";
2 /*******************************************************************************
3 * *
4 * search.c -- Nirvana Editor search and replace functions *
5 * *
6 * Copyright (C) 1999 Mark Edel *
7 * *
8 * This is free software; you can redistribute it and/or modify it under the *
9 * terms of the GNU General Public License as published by the Free Software *
10 * Foundation; either version 2 of the License, or (at your option) any later *
11 * version. *
12 * *
13 * This software is distributed in the hope that it will be useful, but WITHOUT *
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License *
16 * for more details. *
17 * *
18 * You should have received a copy of the GNU General Public License along with *
19 * software; if not, write to the Free Software Foundation, Inc., 59 Temple *
20 * Place, Suite 330, Boston, MA 02111-1307 USA *
21 * *
22 * Nirvana Text Editor *
23 * May 10, 1991 *
24 * *
25 * Written by Mark Edel *
26 * *
27 *******************************************************************************/
29 #ifdef HAVE_CONFIG_H
30 #include "../config.h"
31 #endif
33 #include "search.h"
34 #include "regularExp.h"
35 #include "textBuf.h"
36 #include "text.h"
37 #include "nedit.h"
38 #include "server.h"
39 #include "window.h"
40 #include "userCmds.h"
41 #include "preferences.h"
42 #include "file.h"
43 #include "highlight.h"
44 #ifdef REPLACE_SCOPE
45 #include "textDisp.h"
46 #include "textP.h"
47 #endif
48 #include "../util/DialogF.h"
49 #include "../util/misc.h"
51 #include <stdlib.h>
52 #include <stdio.h>
53 #include <string.h>
54 #include <ctype.h>
55 #ifdef VMS
56 #include "../util/VMSparam.h"
57 #else
58 #ifndef __MVS__
59 #include <sys/param.h>
60 #endif
61 #endif /*VMS*/
63 #include <Xm/Xm.h>
64 #include <X11/Shell.h>
65 #include <Xm/XmP.h>
66 #include <Xm/Form.h>
67 #include <Xm/Label.h>
68 #ifdef REPLACE_SCOPE
69 #if XmVersion >= 1002
70 #include <Xm/PrimitiveP.h>
71 #endif
72 #endif
73 #include <Xm/PushB.h>
74 #include <Xm/RowColumn.h>
75 #include <Xm/Text.h>
76 #include <Xm/ToggleB.h>
77 #include <Xm/List.h>
78 #include <X11/Xatom.h> /* for getting selection */
79 #include <X11/keysym.h>
80 #include <X11/X.h> /* " " */
82 #ifdef HAVE_DEBUG_H
83 #include "../debug.h"
84 #endif
87 int NHist = 0;
89 typedef struct _SelectionInfo {
90 int done;
91 WindowInfo* window;
92 char* selection;
93 } SelectionInfo;
95 typedef struct {
96 int direction;
97 int searchType;
98 int searchWrap;
99 } SearchSelectedCallData;
101 /* History mechanism for search and replace strings */
102 static char *SearchHistory[MAX_SEARCH_HISTORY];
103 static char *ReplaceHistory[MAX_SEARCH_HISTORY];
104 static int SearchTypeHistory[MAX_SEARCH_HISTORY];
105 static int HistStart = 0;
107 static int textFieldNonEmpty(Widget w);
108 static void setTextField(WindowInfo* window, Time time, Widget textField);
109 static void getSelectionCB(Widget w, SelectionInfo *selectionInfo, Atom *selection,
110 Atom *type, char *value, int *length, int *format);
111 static void fFocusCB(Widget w, WindowInfo *window, caddr_t *callData);
112 static void rFocusCB(Widget w, WindowInfo *window, caddr_t *callData);
113 static void rKeepCB(Widget w, WindowInfo *window, caddr_t *callData);
114 static void fKeepCB(Widget w, WindowInfo *window, caddr_t *callData);
115 static void replaceCB(Widget w, WindowInfo *window,
116 XmAnyCallbackStruct *callData);
117 static void replaceAllCB(Widget w, WindowInfo *window,
118 XmAnyCallbackStruct *callData);
119 static void rInSelCB(Widget w, WindowInfo *window,
120 XmAnyCallbackStruct *callData);
121 static void rCancelCB(Widget w, WindowInfo *window, caddr_t callData);
122 static void fCancelCB(Widget w, WindowInfo *window, caddr_t callData);
123 static void rFindCB(Widget w,WindowInfo *window,XmAnyCallbackStruct *callData);
124 static void rFindTextValueChangedCB(Widget w, WindowInfo *window, XKeyEvent *event);
125 static void rFindArrowKeyCB(Widget w, WindowInfo *window, XKeyEvent *event);
127 static void rSetActionButtons(WindowInfo* window,
128 int replaceBtn,
129 int replaceFindBtn,
130 int replaceAndFindBtn,
131 #ifndef REPLACE_SCOPE
132 int replaceInWinBtn,
133 int replaceInSelBtn,
134 #endif
135 int replaceAllBtn);
136 #ifdef REPLACE_SCOPE
137 static void rScopeWinCB(Widget w, WindowInfo *window,
138 XmAnyCallbackStruct *callData);
139 static void rScopeSelCB(Widget w, WindowInfo *window,
140 XmAnyCallbackStruct *callData);
141 static void rScopeMultiCB(Widget w, WindowInfo *window,
142 XmAnyCallbackStruct *callData);
143 static void replaceAllScopeCB(Widget w, WindowInfo *window,
144 XmAnyCallbackStruct *callData);
145 #endif
147 static void replaceArrowKeyCB(Widget w, WindowInfo *window, XKeyEvent *event);
148 static void fUpdateActionButtons(WindowInfo *window);
149 static void findTextValueChangedCB(Widget w, WindowInfo *window, XKeyEvent *event);
150 static void findArrowKeyCB(Widget w, WindowInfo *window, XKeyEvent *event);
151 static void replaceFindCB(Widget w, WindowInfo *window, XmAnyCallbackStruct *callData);
152 static void findCB(Widget w, WindowInfo *window,XmAnyCallbackStruct *callData);
153 static void replaceMultiFileCB(Widget w, WindowInfo *window,
154 XmAnyCallbackStruct *callData);
155 static void rMultiFileReplaceCB(Widget w, WindowInfo *window,
156 XmAnyCallbackStruct * callData);
157 static void rMultiFileCancelCB(Widget w, WindowInfo *window, caddr_t callData);
158 static void rMultiFileSelectAllCB(Widget w, WindowInfo *window,
159 XmAnyCallbackStruct *callData);
160 static void rMultiFileDeselectAllCB(Widget w, WindowInfo *window,
161 XmAnyCallbackStruct * callData);
162 static void rMultiFilePathCB(Widget w, WindowInfo *window,
163 XmAnyCallbackStruct *callData);
164 static void uploadFileListItems(WindowInfo* window, Bool replace);
165 static int countWindows(void);
166 static int countWritableWindows(void);
167 static void collectWritableWindows(WindowInfo* window);
168 static void freeWritableWindowsCB(Widget w, WindowInfo* window,
169 XmAnyCallbackStruct *callData);
170 static void checkMultiReplaceListForDoomedW(WindowInfo* window,
171 WindowInfo* doomedWindow);
172 static void removeDoomedWindowFromList(WindowInfo* window, int index);
173 static void unmanageReplaceDialogs(WindowInfo *window);
174 static void flashTimeoutProc(XtPointer clientData, XtIntervalId *id);
175 static void eraseFlash(WindowInfo *window);
176 static int getReplaceDlogInfo(WindowInfo *window, int *direction,
177 char *searchString, char *replaceString, int *searchType);
178 static int getFindDlogInfo(WindowInfo *window, int *direction,
179 char *searchString, int *searchType);
180 static void selectedSearchCB(Widget w, XtPointer callData, Atom *selection,
181 Atom *type, char *value, int *length, int *format);
182 static void iSearchTextActivateCB(Widget w, WindowInfo *window,
183 XmAnyCallbackStruct *callData);
184 static void iSearchTextValueChangedCB(Widget w, WindowInfo *window,
185 XmAnyCallbackStruct *callData);
186 static void iSearchTextKeyEH(Widget w, WindowInfo *window,
187 XKeyEvent *event, Boolean *continueDispatch);
188 static int searchLiteral(const char *string, const char *searchString, int caseSense,
189 int direction, int wrap, int beginPos, int *startPos, int *endPos,
190 int *searchExtentBW, int *searchExtentFW);
191 static int searchLiteralWord(const char *string, const char *searchString, int caseSense,
192 int direction, int wrap, int beginPos, int *startPos, int *endPos,
193 const char * delimiters);
194 static int searchRegex(const char *string, const char *searchString, int direction,
195 int wrap, int beginPos, int *startPos, int *endPos, int *searchExtentBW,
196 int *searchExtentFW, const char *delimiters, int defaultFlags);
197 static int forwardRegexSearch(const char *string, const char *searchString, int wrap,
198 int beginPos, int *startPos, int *endPos, int *searchExtentBW,
199 int *searchExtentFW, const char *delimiters, int defaultFlags);
200 static int backwardRegexSearch(const char *string, const char *searchString, int wrap,
201 int beginPos, int *startPos, int *endPos, int *searchExtentBW,
202 int *searchExtentFW, const char *delimiters, int defaultFlags);
203 static void upCaseString(char *outString, const char *inString);
204 static void downCaseString(char *outString, const char *inString);
205 static void resetFindTabGroup(WindowInfo *window);
206 static void resetReplaceTabGroup(WindowInfo *window);
207 static int searchMatchesSelection(WindowInfo *window, const char *searchString,
208 int searchType, int *left, int *right, int *searchExtentBW,
209 int *searchExtentFW);
210 static int findMatchingChar(WindowInfo *window, char toMatch,
211 void *toMatchStyle, int charPos, int startLimit, int endLimit,
212 int *matchPos);
213 static void replaceUsingRE(const char *searchStr, const char *replaceStr,
214 const char *sourceStr, int beginPos, char *destStr,
215 int maxDestLen, int prevChar, const char *delimiters, int defaultFlags);
216 static void saveSearchHistory(const char *searchString,
217 const char *replaceString, int searchType, int isIncremental);
218 static int historyIndex(int nCycles);
219 static char *searchTypeArg(int searchType);
220 static char *searchWrapArg(int searchWrap);
221 static char *directionArg(int direction);
222 static int isRegexType(int searchType);
223 static int defaultRegexFlags(int searchType);
224 static void findRegExpToggleCB(Widget w, XtPointer clientData,
225 XtPointer callData);
226 static void replaceRegExpToggleCB(Widget w, XtPointer clientData,
227 XtPointer callData);
228 static void iSearchRegExpToggleCB(Widget w, XtPointer clientData,
229 XtPointer callData);
230 static void findCaseToggleCB(Widget w, XtPointer clientData,
231 XtPointer callData);
232 static void replaceCaseToggleCB(Widget w, XtPointer clientData,
233 XtPointer callData);
234 static void iSearchCaseToggleCB(Widget w, XtPointer clientData,
235 XtPointer callData);
236 static void iSearchTryBeepOnWrap(WindowInfo *window, int direction,
237 int beginPos, int startPos);
238 static void iSearchRecordLastBeginPos(WindowInfo *window, int direction,
239 int initPos);
241 typedef struct _charMatchTable {
242 char c;
243 char match;
244 char direction;
245 } charMatchTable;
247 #define N_MATCH_CHARS 13
248 #define N_FLASH_CHARS 6
249 static charMatchTable MatchingChars[N_MATCH_CHARS] = {
250 {'{', '}', SEARCH_FORWARD},
251 {'}', '{', SEARCH_BACKWARD},
252 {'(', ')', SEARCH_FORWARD},
253 {')', '(', SEARCH_BACKWARD},
254 {'[', ']', SEARCH_FORWARD},
255 {']', '[', SEARCH_BACKWARD},
256 {'<', '>', SEARCH_FORWARD},
257 {'>', '<', SEARCH_BACKWARD},
258 {'/', '/', SEARCH_FORWARD},
259 {'"', '"', SEARCH_FORWARD},
260 {'\'', '\'', SEARCH_FORWARD},
261 {'`', '`', SEARCH_FORWARD},
262 {'\\', '\\', SEARCH_FORWARD},
266 ** Definitions for the search method strings, used as arguments for
267 ** macro search subroutines and search action routines
269 static char *searchTypeStrings[] = {
270 "literal", /* SEARCH_LITERAL */
271 "case", /* SEARCH_CASE_SENSE */
272 "regex", /* SEARCH_REGEX */
273 "word", /* SEARCH_LITERAL_WORD */
274 "caseWord", /* SEARCH_CASE_SENSE_WORD */
275 "regexNoCase", /* SEARCH_REGEX_NOCASE */
276 NULL
280 ** Shared routine for replace and find dialogs and i-search bar to initialize
281 ** the state of the regex/case/word toggle buttons, and the sticky case
282 ** sensitivity states.
284 static void initToggleButtons(int searchType, Widget regexToggle,
285 Widget caseToggle, Widget* wordToggle,
286 Bool* lastLiteralCase,
287 Bool* lastRegexCase)
289 /* Set the initial search type and remember the corresponding case
290 sensitivity states in case sticky case sensitivity is required. */
291 switch (searchType) {
292 case SEARCH_LITERAL:
293 *lastLiteralCase = False;
294 *lastRegexCase = True;
295 XmToggleButtonSetState(regexToggle, False, False);
296 XmToggleButtonSetState(caseToggle, False, False);
297 if (wordToggle) {
298 XmToggleButtonSetState(*wordToggle, False, False);
299 XtSetSensitive(*wordToggle, True);
301 break;
302 case SEARCH_CASE_SENSE:
303 *lastLiteralCase = True;
304 *lastRegexCase = True;
305 XmToggleButtonSetState(regexToggle, False, False);
306 XmToggleButtonSetState(caseToggle, True, False);
307 if (wordToggle) {
308 XmToggleButtonSetState(*wordToggle, False, False);
309 XtSetSensitive(*wordToggle, True);
311 break;
312 case SEARCH_LITERAL_WORD:
313 *lastLiteralCase = False;
314 *lastRegexCase = True;
315 XmToggleButtonSetState(regexToggle, False, False);
316 XmToggleButtonSetState(caseToggle, False, False);
317 if (wordToggle) {
318 XmToggleButtonSetState(*wordToggle, True, False);
319 XtSetSensitive(*wordToggle, True);
321 break;
322 case SEARCH_CASE_SENSE_WORD:
323 *lastLiteralCase = True;
324 *lastRegexCase = True;
325 XmToggleButtonSetState(regexToggle, False, False);
326 XmToggleButtonSetState(caseToggle, True, False);
327 if (wordToggle) {
328 XmToggleButtonSetState(*wordToggle, True, False);
329 XtSetSensitive(*wordToggle, True);
331 break;
332 case SEARCH_REGEX:
333 *lastLiteralCase = False;
334 *lastRegexCase = True;
335 XmToggleButtonSetState(regexToggle, True, False);
336 XmToggleButtonSetState(caseToggle, True, False);
337 if (wordToggle) {
338 XmToggleButtonSetState(*wordToggle, False, False);
339 XtSetSensitive(*wordToggle, False);
341 break;
342 case SEARCH_REGEX_NOCASE:
343 *lastLiteralCase = False;
344 *lastRegexCase = False;
345 XmToggleButtonSetState(regexToggle, True, False);
346 XmToggleButtonSetState(caseToggle, False, False);
347 if (wordToggle) {
348 XmToggleButtonSetState(*wordToggle, False, False);
349 XtSetSensitive(*wordToggle, False);
351 break;
355 #ifdef REPLACE_SCOPE
357 ** Checks whether a selection spans multiple lines. Used to decide on the
358 ** default scope for replace dialogs.
359 ** This routine introduces a dependency on textDisp.h, which is not so nice,
360 ** but I currently don't have a cleaner solution.
362 static int selectionSpansMultipleLines(WindowInfo *window)
364 int selStart, selEnd, isRect, rectStart, rectEnd, lineStartStart,
365 lineStartEnd;
366 int lineWidth;
367 textDisp *textD;
369 if (!BufGetSelectionPos(window->buffer, &selStart, &selEnd, &isRect,
370 &rectStart, &rectEnd))
371 return FALSE;
373 /* This is kind of tricky. The perception of a line depends on the
374 line wrap mode being used. So in theory, we should take into
375 account the layout of the text on the screen. However, the
376 routine to calculate a line number for a given character position
377 (TextDPosToLineAndCol) only works for displayed lines, so we cannot
378 use it. Therefore, we use this simple heuristic:
379 - If a newline is found between the start and end of the selection,
380 we obviously have a multi-line selection.
381 - If no newline is found, but the distance between the start and the
382 end of the selection is larger than the number of characters
383 displayed on a line, and we're in continuous wrap mode,
384 we also assume a multi-line selection.
387 lineStartStart = BufStartOfLine(window->buffer, selStart);
388 lineStartEnd = BufStartOfLine(window->buffer, selEnd);
389 /* If the line starts differ, we have a "\n" in between. */
390 if (lineStartStart != lineStartEnd )
391 return TRUE;
393 if (window->wrapMode != CONTINUOUS_WRAP)
394 return FALSE; /* Same line */
396 /* Estimate the number of characters on a line */
397 textD = ((TextWidget)window->textArea)->text.textD;
398 if (textD->fontStruct->max_bounds.width > 0)
399 lineWidth = textD->width / textD->fontStruct->max_bounds.width;
400 else
401 lineWidth = 1;
402 if (lineWidth < 1) lineWidth = 1; /* Just in case */
404 /* Estimate the numbers of line breaks from the start of the line to
405 the start and ending positions of the selection and compare.*/
406 if ((selStart-lineStartStart)/lineWidth !=
407 (selEnd-lineStartStart)/lineWidth )
408 return TRUE; /* Spans multiple lines */
410 return FALSE; /* Small selection; probably doesn't span lines */
412 #endif
414 void DoFindReplaceDlog(WindowInfo *window, int direction, int keepDialogs,
415 int searchType, Time time)
418 /* Create the dialog if it doesn't already exist */
419 if (window->replaceDlog == NULL)
420 CreateReplaceDlog(window->shell, window);
422 setTextField(window, time, window->replaceText);
424 /* If the window is already up, just pop it to the top */
425 if (XtIsManaged(window->replaceDlog)) {
426 RaiseShellWindow(XtParent(window->replaceDlog));
427 return;
430 /* Blank the Replace with field */
431 XmTextSetString(window->replaceWithText, "");
433 /* Set the initial search type */
434 initToggleButtons(searchType, window->replaceRegexToggle,
435 window->replaceCaseToggle, &window->replaceWordToggle,
436 &window->replaceLastLiteralCase,
437 &window->replaceLastRegexCase);
439 /* Set the initial direction based on the direction argument */
440 XmToggleButtonSetState(window->replaceRevToggle,
441 direction == SEARCH_FORWARD ? False: True, True);
443 /* Set the state of the Keep Dialog Up button */
444 XmToggleButtonSetState(window->replaceKeepBtn, keepDialogs, True);
446 #ifdef REPLACE_SCOPE
447 /* Set the state of the scope radio buttons to "In Window".
448 Notify to make sure that callbacks are called.
449 NOTE: due to an apparent bug in OpenMotif, the radio buttons may
450 get stuck after resetting the scope to "In Window". Therefore we must
451 use RadioButtonChangeState(), which contains a workaround. */
452 if (window->wasSelected) {
453 /* If a selection exists, the default scope depends on the preference
454 of the user. */
455 switch(GetPrefReplaceDefScope()) {
456 case REPL_DEF_SCOPE_SELECTION:
457 /* The user prefers selection scope, no matter what the
458 size of the selection is. */
459 RadioButtonChangeState(window->replaceScopeSelToggle,
460 True, True);
461 break;
462 case REPL_DEF_SCOPE_SMART:
463 if (selectionSpansMultipleLines(window)) {
464 /* If the selection spans multiple lines, the user most
465 likely wants to perform a replacement in the selection */
466 RadioButtonChangeState(window->replaceScopeSelToggle,
467 True, True);
469 else {
470 /* It's unlikely that the user wants a replacement in a
471 tiny selection only. */
472 RadioButtonChangeState(window->replaceScopeWinToggle,
473 True, True);
475 break;
476 default:
477 /* The user always wants window scope as default. */
478 RadioButtonChangeState(window->replaceScopeWinToggle,
479 True, True);
480 break;
483 else {
484 /* No selection -> always choose "In Window" as default. */
485 RadioButtonChangeState(window->replaceScopeWinToggle, True, True);
487 #endif
489 UpdateReplaceActionButtons(window);
491 /* Start the search history mechanism at the current history item */
492 window->rHistIndex = 0;
494 /* Display the dialog */
495 ManageDialogCenteredOnPointer(window->replaceDlog);
497 /* Workaround: LessTif (as of version 0.89) needs reminding of who had
498 the focus when the dialog was unmanaged. When re-managed, focus is
499 lost and events fall through to the window below. */
500 XmProcessTraversal(window->replaceText, XmTRAVERSE_CURRENT);
503 static void setTextField(WindowInfo *window, Time time, Widget textField)
505 XEvent nextEvent;
506 char *primary_selection = 0;
507 SelectionInfo *selectionInfo = XtNew(SelectionInfo);
509 if (GetPrefFindReplaceUsesSelection()) {
510 selectionInfo->done = 0;
511 selectionInfo->window = window;
512 selectionInfo->selection = 0;
513 XtGetSelectionValue(window->textArea, XA_PRIMARY, XA_STRING,
514 (XtSelectionCallbackProc)getSelectionCB, selectionInfo, time);
515 while (selectionInfo->done == 0) {
516 XtAppNextEvent(XtWidgetToApplicationContext(window->textArea), &nextEvent);
517 ServerDispatchEvent(&nextEvent);
519 primary_selection = selectionInfo->selection;
521 if (primary_selection == 0) {
522 primary_selection = XtNewString("");
525 /* Update the field */
526 XmTextSetString(textField, primary_selection);
528 XtFree(primary_selection);
529 XtFree((char*)selectionInfo);
532 static void getSelectionCB(Widget w, SelectionInfo *selectionInfo, Atom *selection,
533 Atom *type, char *value, int *length, int *format)
535 WindowInfo *window = selectionInfo->window;
537 /* return an empty string if we can't get the selection data */
538 if (*type == XT_CONVERT_FAIL || *type != XA_STRING || value == NULL || *length == 0) {
539 XtFree(value);
540 selectionInfo->selection = 0;
541 selectionInfo->done = 1;
542 return;
544 /* return an empty string if the data is not of the correct format. */
545 if (*format != 8) {
546 DialogF(DF_WARN, window->shell, 1, "Invalid Format",
547 "NEdit can't handle non 8-bit text", "OK");
548 XtFree(value);
549 selectionInfo->selection = 0;
550 selectionInfo->done = 1;
551 return;
553 selectionInfo->selection = XtMalloc(*length+1);
554 memcpy(selectionInfo->selection, value, *length);
555 selectionInfo->selection[*length] = 0;
556 XtFree(value);
557 selectionInfo->done = 1;
560 void DoFindDlog(WindowInfo *window, int direction, int keepDialogs,
561 int searchType, Time time)
564 /* Create the dialog if it doesn't already exist */
565 if (window->findDlog == NULL)
566 CreateFindDlog(window->shell, window);
568 setTextField(window, time, window->findText);
570 /* If the window is already up, just pop it to the top */
571 if (XtIsManaged(window->findDlog)) {
572 RaiseShellWindow(XtParent(window->findDlog));
573 return;
576 /* Set the initial search type */
577 initToggleButtons(searchType, window->findRegexToggle,
578 window->findCaseToggle, &window->findWordToggle,
579 &window->findLastLiteralCase,
580 &window->findLastRegexCase);
582 /* Set the initial direction based on the direction argument */
583 XmToggleButtonSetState(window->findRevToggle,
584 direction == SEARCH_FORWARD ? False : True, True);
586 /* Set the state of the Keep Dialog Up button */
587 XmToggleButtonSetState(window->findKeepBtn, keepDialogs, True);
589 /* Set the state of the Find button */
590 fUpdateActionButtons(window);
592 /* start the search history mechanism at the current history item */
593 window->fHistIndex = 0;
595 /* Display the dialog */
596 ManageDialogCenteredOnPointer(window->findDlog);
598 /* Workaround: LessTif (as of version 0.89) needs reminding of who had
599 the focus when the dialog was unmanaged. When re-managed, focus is
600 lost and events fall through to the window below. */
601 XmProcessTraversal(window->findText, XmTRAVERSE_CURRENT);
604 void DoReplaceMultiFileDlog(WindowInfo *window)
606 char searchString[SEARCHMAX], replaceString[SEARCHMAX];
607 int direction, searchType;
609 /* Validate and fetch the find and replace strings from the dialog */
610 if (!getReplaceDlogInfo(window, &direction, searchString, replaceString,
611 &searchType))
612 return;
614 /* Don't let the user select files when no replacement can be made */
615 if (*searchString == '\0') {
616 /* Set the initial focus of the dialog back to the search string */
617 resetReplaceTabGroup(window);
618 /* pop down the replace dialog */
619 if (!XmToggleButtonGetState(window->replaceKeepBtn))
620 unmanageReplaceDialogs(window);
621 return;
624 /* Create the dialog if it doesn't already exist */
625 if (window->replaceMultiFileDlog == NULL)
626 CreateReplaceMultiFileDlog(window);
628 /* Raising the window doesn't make sense. It is modal, so we
629 can't get here unless it is unmanaged */
630 /* Prepare a list of writable windows */
631 collectWritableWindows(window);
633 /* Initialize/update the list of files. */
634 uploadFileListItems(window, False);
636 /* Display the dialog */
637 ManageDialogCenteredOnPointer(window->replaceMultiFileDlog);
641 ** If a window is closed (possibly via the window manager) while it is on the
642 ** multi-file replace dialog list of any other window (or even the same one),
643 ** we must update those lists or we end up with dangling references.
644 ** Normally, there can be only one of those dialogs at the same time
645 ** (application modal), but Lesstif doesn't (always) honor application
646 ** modalness, so there can be more than one dialog.
648 void RemoveFromMultiReplaceDialog(WindowInfo *doomedWindow)
650 WindowInfo *w;
652 for (w=WindowList; w!=NULL; w=w->next)
653 if (w->writableWindows)
654 /* A multi-file replacement dialog is up for this window */
655 checkMultiReplaceListForDoomedW(w, doomedWindow);
658 void CreateReplaceDlog(Widget parent, WindowInfo *window)
660 Arg args[50];
661 int argcnt, defaultBtnOffset;
662 XmString st1;
663 Widget form, btnForm;
664 #ifdef REPLACE_SCOPE
665 Widget scopeForm, replaceAllBtn;
666 #else
667 Widget label3, allForm;
668 #endif
669 Widget inWinBtn, inSelBtn, inMultiBtn;
670 Widget searchTypeBox;
671 Widget label2, label1, label, replaceText, findText;
672 Widget findBtn, cancelBtn, replaceBtn;
673 Widget replaceFindBtn;
674 Widget searchDirBox, reverseBtn, keepBtn;
675 char title[MAXPATHLEN + 19];
676 Dimension shadowThickness;
678 argcnt = 0;
679 XtSetArg(args[argcnt], XmNautoUnmanage, False); argcnt++;
680 form = CreateFormDialog(parent, "replaceDialog", args, argcnt);
681 XtVaSetValues(form, XmNshadowThickness, 0, NULL);
682 if (GetPrefKeepSearchDlogs()) {
683 sprintf(title, "Replace/Find (in %s)", window->filename);
684 XtVaSetValues(XtParent(form), XmNtitle, title, NULL);
685 } else
686 XtVaSetValues(XtParent(form), XmNtitle, "Replace/Find", NULL);
688 argcnt = 0;
689 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
690 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
691 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
692 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_NONE); argcnt++;
693 XtSetArg(args[argcnt], XmNleftOffset, 4); argcnt++;
694 XtSetArg(args[argcnt], XmNtopOffset, 6); argcnt++;
695 XtSetArg(args[argcnt], XmNalignment, XmALIGNMENT_BEGINNING); argcnt++;
696 XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("String to Find:"));
697 argcnt++;
698 XtSetArg(args[argcnt], XmNmnemonic, 't'); argcnt++;
699 label1 = XmCreateLabel(form, "label1", args, argcnt);
700 XmStringFree(st1);
701 XtManageChild(label1);
703 argcnt = 0;
704 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
705 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
706 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_NONE); argcnt++;
707 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_FORM); argcnt++;
708 XtSetArg(args[argcnt], XmNrightOffset, 6); argcnt++;
709 XtSetArg(args[argcnt], XmNtopOffset, 6); argcnt++;
710 XtSetArg(args[argcnt], XmNalignment, XmALIGNMENT_END); argcnt++;
711 XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING(
712 "(use up arrow key to recall previous)")); argcnt++;
713 label2 = XmCreateLabel(form, "label2", args, argcnt);
714 XmStringFree(st1);
715 XtManageChild(label2);
717 argcnt = 0;
718 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
719 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
720 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_WIDGET); argcnt++;
721 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
722 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
723 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_FORM); argcnt++;
724 XtSetArg(args[argcnt], XmNtopWidget, label1); argcnt++;
725 XtSetArg(args[argcnt], XmNleftOffset, 6); argcnt++;
726 XtSetArg(args[argcnt], XmNrightOffset, 6); argcnt++;
727 XtSetArg(args[argcnt], XmNmaxLength, SEARCHMAX); argcnt++;
728 findText = XmCreateText(form, "replaceString", args, argcnt);
729 XtAddCallback(findText, XmNfocusCallback, (XtCallbackProc)rFocusCB, window);
730 XtAddCallback(findText, XmNvalueChangedCallback,
731 (XtCallbackProc)rFindTextValueChangedCB, window);
732 XtAddEventHandler(findText, KeyPressMask, False,
733 (XtEventHandler)rFindArrowKeyCB, window);
734 RemapDeleteKey(findText);
735 XtManageChild(findText);
736 XmAddTabGroup(findText);
737 XtVaSetValues(label1, XmNuserData, findText, NULL); /* mnemonic processing */
739 argcnt = 0;
740 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_WIDGET); argcnt++;
741 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
742 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
743 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_NONE); argcnt++;
744 XtSetArg(args[argcnt], XmNtopWidget, findText); argcnt++;
745 XtSetArg(args[argcnt], XmNleftOffset, 6); argcnt++;
746 XtSetArg(args[argcnt], XmNtopOffset, 6); argcnt++;
747 XtSetArg(args[argcnt], XmNalignment, XmALIGNMENT_BEGINNING); argcnt++;
748 XtSetArg(args[argcnt], XmNlabelString,
749 st1=MKSTRING("Replace With:")); argcnt++;
750 XtSetArg(args[argcnt], XmNmnemonic, 'W'); argcnt++;
751 label = XmCreateLabel(form, "label", args, argcnt);
752 XmStringFree(st1);
753 XtManageChild(label);
755 argcnt = 0;
756 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
757 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
758 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_WIDGET); argcnt++;
759 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
760 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
761 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_FORM); argcnt++;
762 XtSetArg(args[argcnt], XmNtopWidget, label); argcnt++;
763 XtSetArg(args[argcnt], XmNleftOffset, 6); argcnt++;
764 XtSetArg(args[argcnt], XmNrightOffset, 6); argcnt++;
765 XtSetArg(args[argcnt], XmNmaxLength, SEARCHMAX); argcnt++;
766 replaceText = XmCreateText(form, "replaceWithString", args, argcnt);
767 XtAddEventHandler(replaceText, KeyPressMask, False,
768 (XtEventHandler)replaceArrowKeyCB, window);
769 RemapDeleteKey(replaceText);
770 XtManageChild(replaceText);
771 XmAddTabGroup(replaceText);
772 XtVaSetValues(label, XmNuserData, replaceText, NULL); /* mnemonic processing */
774 argcnt = 0;
775 XtSetArg(args[argcnt], XmNorientation, XmHORIZONTAL); argcnt++;
776 XtSetArg(args[argcnt], XmNpacking, XmPACK_TIGHT); argcnt++;
777 XtSetArg(args[argcnt], XmNmarginHeight, 0); argcnt++;
778 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_WIDGET); argcnt++;
779 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
780 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_NONE); argcnt++;
781 XtSetArg(args[argcnt], XmNtopWidget, replaceText); argcnt++;
782 XtSetArg(args[argcnt], XmNleftOffset, 2); argcnt++;
783 XtSetArg(args[argcnt], XmNrightOffset, 4); argcnt++;
784 searchTypeBox = XmCreateRowColumn(form, "searchTypeBox", args, argcnt);
785 XtManageChild(searchTypeBox);
786 XmAddTabGroup(searchTypeBox);
788 argcnt = 0;
789 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
790 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
791 XtSetArg(args[argcnt], XmNlabelString,
792 st1=MKSTRING("Regular Expression")); argcnt++;
793 XtSetArg(args[argcnt], XmNmnemonic, 'R'); argcnt++;
794 window->replaceRegexToggle = XmCreateToggleButton(searchTypeBox, "regExp", args, argcnt);
795 XmStringFree(st1);
796 XtManageChild(window->replaceRegexToggle);
797 XtAddCallback(window->replaceRegexToggle, XmNvalueChangedCallback, (XtCallbackProc) replaceRegExpToggleCB, window);
799 argcnt = 0;
800 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
801 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
802 XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("Case Sensitive")); argcnt++;
803 XtSetArg(args[argcnt], XmNmnemonic, 'C'); argcnt++;
804 window->replaceCaseToggle = XmCreateToggleButton(searchTypeBox, "caseSensitive", args, argcnt);
805 XmStringFree(st1);
806 XtManageChild(window->replaceCaseToggle);
807 XtAddCallback(window->replaceCaseToggle, XmNvalueChangedCallback, (XtCallbackProc) replaceCaseToggleCB, window);
809 argcnt = 0;
810 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
811 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
812 XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("Whole Word")); argcnt++;
813 XtSetArg(args[argcnt], XmNmnemonic, 'h'); argcnt++;
814 window->replaceWordToggle = XmCreateToggleButton(searchTypeBox, "wholeWord", args, argcnt);
815 XmStringFree(st1);
816 XtManageChild(window->replaceWordToggle);
818 argcnt = 0;
819 XtSetArg(args[argcnt], XmNorientation, XmHORIZONTAL); argcnt++;
820 XtSetArg(args[argcnt], XmNpacking, XmPACK_TIGHT); argcnt++;
821 XtSetArg(args[argcnt], XmNmarginHeight, 0); argcnt++;
822 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_WIDGET); argcnt++;
823 XtSetArg(args[argcnt], XmNtopOffset, 0); argcnt++;
824 XtSetArg(args[argcnt], XmNtopWidget, searchTypeBox); argcnt++;
825 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
826 XtSetArg(args[argcnt], XmNleftOffset, 2); argcnt++;
827 XtSetArg(args[argcnt], XmNradioBehavior, False); argcnt++;
828 searchDirBox = XmCreateRowColumn(form, "searchDirBox", args, argcnt);
829 XtManageChild(searchDirBox);
830 XmAddTabGroup(searchDirBox);
832 argcnt = 0;
833 XtSetArg(args[argcnt], XmNlabelString,
834 st1=MKSTRING("Search Backward")); argcnt++;
835 XtSetArg(args[argcnt], XmNmnemonic, 'B'); argcnt++;
836 reverseBtn = XmCreateToggleButton(searchDirBox, "reverse", args, argcnt);
837 XmStringFree(st1);
838 XtManageChild(reverseBtn);
840 argcnt = 0;
841 XtSetArg(args[argcnt], XmNlabelString,
842 st1=MKSTRING("Keep Dialog")); argcnt++;
843 XtSetArg(args[argcnt], XmNmnemonic, 'K'); argcnt++;
844 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_WIDGET); argcnt++;
845 XtSetArg(args[argcnt], XmNtopOffset, 0); argcnt++;
846 XtSetArg(args[argcnt], XmNtopWidget, searchTypeBox); argcnt++;
847 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_FORM); argcnt++;
848 XtSetArg(args[argcnt], XmNrightOffset, 4); argcnt++;
849 keepBtn = XmCreateToggleButton(form, "keep", args, argcnt);
850 XtAddCallback(keepBtn, XmNvalueChangedCallback,
851 (XtCallbackProc)rKeepCB, window);
852 XmStringFree(st1);
853 XtManageChild(keepBtn);
854 XmAddTabGroup(keepBtn);
856 #ifdef REPLACE_SCOPE
857 argcnt = 0;
858 XtSetArg(args[argcnt], XmNorientation, XmHORIZONTAL); argcnt++;
859 XtSetArg(args[argcnt], XmNpacking, XmPACK_TIGHT); argcnt++;
860 XtSetArg(args[argcnt], XmNmarginHeight, 0); argcnt++;
861 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
862 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_NONE); argcnt++;
863 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_WIDGET); argcnt++;
864 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
865 XtSetArg(args[argcnt], XmNtopWidget, searchDirBox); argcnt++;
866 XtSetArg(args[argcnt], XmNleftOffset, 2); argcnt++;
867 XtSetArg(args[argcnt], XmNrightOffset, 6); argcnt++;
868 XtSetArg(args[argcnt], XmNradioBehavior, True); argcnt++;
869 XtSetArg(args[argcnt], XmNradioAlwaysOne, True); argcnt++;
870 scopeForm = XmCreateRowColumn(form, "scope", args, argcnt);
871 XtManageChild(scopeForm);
872 XmAddTabGroup(scopeForm);
874 argcnt = 0;
875 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
876 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
877 XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("In Window"));
878 argcnt++;
879 XtSetArg(args[argcnt], XmNmnemonic, 'i'); argcnt++;
880 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
881 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
882 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
883 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_NONE); argcnt++;
884 inWinBtn = XmCreateToggleButton(scopeForm, "inWindow", args, argcnt);
885 XtAddCallback(inWinBtn, XmNvalueChangedCallback,
886 (XtCallbackProc)rScopeWinCB, window);
887 XmStringFree(st1);
888 XtManageChild(inWinBtn);
890 argcnt = 0;
891 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
892 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
893 XtSetArg(args[argcnt], XmNlabelString,
894 st1=MKSTRING("In Selection")); argcnt++;
895 XtSetArg(args[argcnt], XmNmnemonic, 'S'); argcnt++;
896 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
897 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
898 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_WIDGET); argcnt++;
899 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_NONE); argcnt++;
900 XtSetArg(args[argcnt], XmNleftWidget, inWinBtn); argcnt++;
901 inSelBtn = XmCreateToggleButton(scopeForm, "inSel", args, argcnt);
902 XtAddCallback(inSelBtn, XmNvalueChangedCallback,
903 (XtCallbackProc)rScopeSelCB, window);
904 XmStringFree(st1);
905 XtManageChild(inSelBtn);
907 argcnt = 0;
908 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
909 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
910 XtSetArg(args[argcnt], XmNlabelString,
911 st1=MKSTRING("In Multiple Files")); argcnt++;
912 XtSetArg(args[argcnt], XmNmnemonic, 'M'); argcnt++;
913 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
914 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
915 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_WIDGET); argcnt++;
916 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_FORM); argcnt++;
917 XtSetArg(args[argcnt], XmNleftWidget, inSelBtn); argcnt++;
918 inMultiBtn = XmCreateToggleButton(scopeForm, "multiFile", args, argcnt);
919 XtAddCallback(inMultiBtn, XmNvalueChangedCallback,
920 (XtCallbackProc)rScopeMultiCB, window);
921 XmStringFree(st1);
922 XtManageChild(inMultiBtn);
923 #else
924 argcnt = 0;
925 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_NONE); argcnt++;
926 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_FORM); argcnt++;
927 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_WIDGET); argcnt++;
928 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
929 XtSetArg(args[argcnt], XmNtopWidget, searchDirBox); argcnt++;
930 XtSetArg(args[argcnt], XmNleftOffset, 6); argcnt++;
931 XtSetArg(args[argcnt], XmNrightOffset, 6); argcnt++;
932 allForm = XmCreateForm(form, "all", args, argcnt);
933 XtManageChild(allForm);
934 XmAddTabGroup(allForm);
936 argcnt = 0;
937 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
938 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
939 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
940 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_NONE); argcnt++;
941 XtSetArg(args[argcnt], XmNleftOffset, 4); argcnt++;
942 XtSetArg(args[argcnt], XmNtopOffset, 6); argcnt++;
943 XtSetArg(args[argcnt], XmNalignment, XmALIGNMENT_BEGINNING); argcnt++;
944 XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("Replace all in:"));
945 argcnt++;
946 label3 = XmCreateLabel(allForm, "label3", args, argcnt);
947 XmStringFree(st1);
948 XtManageChild(label3);
950 argcnt = 0;
951 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
952 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
953 XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("Window"));
954 argcnt++;
955 XtSetArg(args[argcnt], XmNmnemonic, 'i'); argcnt++;
956 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
957 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
958 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_WIDGET); argcnt++;
959 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_NONE); argcnt++;
960 XtSetArg(args[argcnt], XmNleftWidget, label3); argcnt++;
961 inWinBtn = XmCreatePushButton(allForm, "inWindow", args, argcnt);
962 XtAddCallback(inWinBtn, XmNactivateCallback,
963 (XtCallbackProc)replaceAllCB, window);
964 XmStringFree(st1);
965 XtManageChild(inWinBtn);
967 argcnt = 0;
968 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
969 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
970 XtSetArg(args[argcnt], XmNlabelString,
971 st1=MKSTRING("Selection")); argcnt++;
972 XtSetArg(args[argcnt], XmNmnemonic, 'S'); argcnt++;
973 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
974 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
975 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_WIDGET); argcnt++;
976 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_NONE); argcnt++;
977 XtSetArg(args[argcnt], XmNleftWidget, inWinBtn); argcnt++;
978 inSelBtn = XmCreatePushButton(allForm, "inSel", args, argcnt);
979 XtAddCallback(inSelBtn, XmNactivateCallback,
980 (XtCallbackProc)rInSelCB, window);
981 XmStringFree(st1);
982 XtManageChild(inSelBtn);
984 argcnt = 0;
985 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
986 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
987 XtSetArg(args[argcnt], XmNlabelString,
988 st1=MKSTRING("Multiple Files...")); argcnt++;
989 XtSetArg(args[argcnt], XmNmnemonic, 'M'); argcnt++;
990 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
991 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
992 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_WIDGET); argcnt++;
993 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_FORM); argcnt++;
994 XtSetArg(args[argcnt], XmNleftWidget, inSelBtn); argcnt++;
995 inMultiBtn = XmCreatePushButton(allForm, "multiFile", args, argcnt);
996 XtAddCallback(inMultiBtn, XmNactivateCallback,
997 (XtCallbackProc)replaceMultiFileCB, window);
998 XmStringFree(st1);
999 XtManageChild(inMultiBtn);
1001 #endif
1003 argcnt = 0;
1004 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
1005 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_FORM); argcnt++;
1006 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_WIDGET); argcnt++;
1007 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
1008 #ifdef REPLACE_SCOPE
1009 XtSetArg(args[argcnt], XmNtopWidget, scopeForm); argcnt++;
1010 #else
1011 XtSetArg(args[argcnt], XmNtopWidget, allForm); argcnt++;
1012 #endif
1013 XtSetArg(args[argcnt], XmNleftOffset, 6); argcnt++;
1014 XtSetArg(args[argcnt], XmNrightOffset, 6); argcnt++;
1015 btnForm = XmCreateForm(form, "buttons", args, argcnt);
1016 XtManageChild(btnForm);
1017 XmAddTabGroup(btnForm);
1019 argcnt = 0;
1020 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1021 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1022 XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("Replace")); argcnt++;
1023 XtSetArg(args[argcnt], XmNshowAsDefault, (short)1); argcnt++;
1024 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
1025 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_POSITION); argcnt++;
1026 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_POSITION); argcnt++;
1027 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_FORM); argcnt++;
1028 #ifdef REPLACE_SCOPE
1029 XtSetArg(args[argcnt], XmNleftPosition, 0); argcnt++;
1030 XtSetArg(args[argcnt], XmNrightPosition, 21); argcnt++;
1031 #else
1032 XtSetArg(args[argcnt], XmNleftPosition, 0); argcnt++;
1033 XtSetArg(args[argcnt], XmNrightPosition, 25); argcnt++;
1034 #endif
1035 replaceBtn = XmCreatePushButton(btnForm, "replace", args, argcnt);
1036 XtAddCallback(replaceBtn, XmNactivateCallback, (XtCallbackProc)replaceCB, window);
1037 XmStringFree(st1);
1038 XtManageChild(replaceBtn);
1039 XtVaGetValues(replaceBtn, XmNshadowThickness, &shadowThickness, 0);
1040 defaultBtnOffset = shadowThickness + 4;
1042 argcnt = 0;
1043 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1044 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1045 XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("Find")); argcnt++;
1046 XtSetArg(args[argcnt], XmNmnemonic, 'F'); argcnt++;
1047 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
1048 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_FORM); argcnt++;
1049 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_POSITION); argcnt++;
1050 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_POSITION); argcnt++;
1051 #ifdef REPLACE_SCOPE
1052 XtSetArg(args[argcnt], XmNleftPosition, 21); argcnt++;
1053 XtSetArg(args[argcnt], XmNrightPosition, 33); argcnt++;
1054 #else
1055 XtSetArg(args[argcnt], XmNleftPosition, 25); argcnt++;
1056 XtSetArg(args[argcnt], XmNrightPosition, 42); argcnt++;
1057 #endif
1058 XtSetArg(args[argcnt], XmNtopOffset, defaultBtnOffset); argcnt++;
1059 XtSetArg(args[argcnt], XmNbottomOffset, defaultBtnOffset); argcnt++;
1060 findBtn = XmCreatePushButton(btnForm, "find", args, argcnt);
1061 XtAddCallback(findBtn, XmNactivateCallback, (XtCallbackProc)rFindCB, window);
1062 XmStringFree(st1);
1063 XtManageChild(findBtn);
1065 argcnt = 0;
1066 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1067 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1068 XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("Replace & Find")); argcnt++;
1069 XtSetArg(args[argcnt], XmNmnemonic, 'n'); argcnt++;
1070 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
1071 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_FORM); argcnt++;
1072 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_POSITION); argcnt++;
1073 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_POSITION); argcnt++;
1074 #ifdef REPLACE_SCOPE
1075 XtSetArg(args[argcnt], XmNleftPosition, 33); argcnt++;
1076 XtSetArg(args[argcnt], XmNrightPosition, 62); argcnt++;
1077 #else
1078 XtSetArg(args[argcnt], XmNleftPosition, 42); argcnt++;
1079 XtSetArg(args[argcnt], XmNrightPosition, 79); argcnt++;
1080 #endif
1081 XtSetArg(args[argcnt], XmNtopOffset, defaultBtnOffset); argcnt++;
1082 XtSetArg(args[argcnt], XmNbottomOffset, defaultBtnOffset); argcnt++;
1083 replaceFindBtn = XmCreatePushButton(btnForm, "replacefind", args, argcnt);
1084 XtAddCallback(replaceFindBtn, XmNactivateCallback, (XtCallbackProc)replaceFindCB, window);
1085 XmStringFree(st1);
1086 XtManageChild(replaceFindBtn);
1088 #ifdef REPLACE_SCOPE
1089 argcnt = 0;
1090 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1091 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1092 XtSetArg(args[argcnt], XmNlabelString,
1093 st1=MKSTRING("Replace All")); argcnt++;
1094 XtSetArg(args[argcnt], XmNmnemonic, 'A'); argcnt++;
1095 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
1096 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
1097 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_POSITION); argcnt++;
1098 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_POSITION); argcnt++;
1099 XtSetArg(args[argcnt], XmNleftPosition, 62); argcnt++;
1100 XtSetArg(args[argcnt], XmNrightPosition, 85); argcnt++;
1101 XtSetArg(args[argcnt], XmNtopOffset, defaultBtnOffset); argcnt++;
1102 replaceAllBtn = XmCreatePushButton(btnForm, "all", args, argcnt);
1103 XtAddCallback(replaceAllBtn, XmNactivateCallback,
1104 (XtCallbackProc)replaceAllScopeCB, window);
1105 XmStringFree(st1);
1106 XtManageChild(replaceAllBtn);
1107 #endif
1109 argcnt = 0;
1110 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1111 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1112 XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("Cancel")); argcnt++;
1113 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
1114 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_FORM); argcnt++;
1115 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_POSITION); argcnt++;
1116 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_POSITION); argcnt++;
1117 #ifdef REPLACE_SCOPE
1118 XtSetArg(args[argcnt], XmNleftPosition, 85); argcnt++;
1119 XtSetArg(args[argcnt], XmNrightPosition, 100); argcnt++;
1120 #else
1121 XtSetArg(args[argcnt], XmNleftPosition, 79); argcnt++;
1122 XtSetArg(args[argcnt], XmNrightPosition, 100); argcnt++;
1123 #endif
1124 XtSetArg(args[argcnt], XmNtopOffset, defaultBtnOffset); argcnt++;
1125 XtSetArg(args[argcnt], XmNbottomOffset, defaultBtnOffset); argcnt++;
1126 cancelBtn = XmCreatePushButton(btnForm, "cancel", args, argcnt);
1127 XmStringFree(st1);
1128 XtAddCallback(cancelBtn, XmNactivateCallback, (XtCallbackProc)rCancelCB,
1129 window);
1130 XtManageChild(cancelBtn);
1132 XtVaSetValues(form, XmNcancelButton, cancelBtn, NULL);
1133 AddDialogMnemonicHandler(form, FALSE);
1135 window->replaceDlog = form;
1136 window->replaceText = findText;
1137 window->replaceWithText = replaceText;
1138 window->replaceRevToggle = reverseBtn;
1139 window->replaceKeepBtn = keepBtn;
1140 window->replaceBtns = btnForm;
1141 window->replaceBtn = replaceBtn;
1142 window->replaceAndFindBtn = replaceFindBtn;
1143 window->replaceFindBtn = findBtn;
1144 window->replaceSearchTypeBox = searchTypeBox;
1145 #ifdef REPLACE_SCOPE
1146 window->replaceAllBtn = replaceAllBtn;
1147 window->replaceScopeWinToggle = inWinBtn;
1148 window->replaceScopeSelToggle = inSelBtn;
1149 window->replaceScopeMultiToggle = inMultiBtn;
1150 #else
1151 window->replaceInWinBtn = inWinBtn;
1152 window->replaceAllBtn = inMultiBtn;
1153 window->replaceInSelBtn = inSelBtn;
1154 #endif
1157 void CreateFindDlog(Widget parent, WindowInfo *window)
1159 Arg args[50];
1160 int argcnt, defaultBtnOffset;
1161 XmString st1;
1162 Widget form, btnForm, searchTypeBox;
1163 Widget findText, label1, label2, cancelBtn, findBtn;
1164 Widget searchDirBox, reverseBtn, keepBtn;
1165 char title[MAXPATHLEN + 11];
1166 Dimension shadowThickness;
1168 argcnt = 0;
1169 XtSetArg(args[argcnt], XmNautoUnmanage, False); argcnt++;
1170 form = CreateFormDialog(parent, "findDialog", args, argcnt);
1171 XtVaSetValues(form, XmNshadowThickness, 0, NULL);
1172 if (GetPrefKeepSearchDlogs()) {
1173 sprintf(title, "Find (in %s)", window->filename);
1174 XtVaSetValues(XtParent(form), XmNtitle, title, NULL);
1175 } else
1176 XtVaSetValues(XtParent(form), XmNtitle, "Find", NULL);
1178 argcnt = 0;
1179 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
1180 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
1181 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
1182 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_NONE); argcnt++;
1183 XtSetArg(args[argcnt], XmNleftOffset, 6); argcnt++;
1184 XtSetArg(args[argcnt], XmNtopOffset, 6); argcnt++;
1185 XtSetArg(args[argcnt], XmNalignment, XmALIGNMENT_BEGINNING); argcnt++;
1186 XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("String to Find:"));
1187 argcnt++;
1188 XtSetArg(args[argcnt], XmNmnemonic, 'S'); argcnt++;
1189 label1 = XmCreateLabel(form, "label1", args, argcnt);
1190 XmStringFree(st1);
1191 XtManageChild(label1);
1193 argcnt = 0;
1194 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
1195 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
1196 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_NONE); argcnt++;
1197 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_FORM); argcnt++;
1198 XtSetArg(args[argcnt], XmNrightOffset, 6); argcnt++;
1199 XtSetArg(args[argcnt], XmNtopOffset, 6); argcnt++;
1200 XtSetArg(args[argcnt], XmNalignment, XmALIGNMENT_END); argcnt++;
1201 XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING(
1202 "(use up arrow key to recall previous)")); argcnt++;
1203 label2 = XmCreateLabel(form, "label2", args, argcnt);
1204 XmStringFree(st1);
1205 XtManageChild(label2);
1207 argcnt = 0;
1208 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1209 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1210 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_WIDGET); argcnt++;
1211 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
1212 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
1213 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_FORM); argcnt++;
1214 XtSetArg(args[argcnt], XmNtopWidget, label1); argcnt++;
1215 XtSetArg(args[argcnt], XmNleftOffset, 6); argcnt++;
1216 XtSetArg(args[argcnt], XmNrightOffset, 6); argcnt++;
1217 XtSetArg(args[argcnt], XmNmaxLength, SEARCHMAX); argcnt++;
1218 findText = XmCreateText(form, "searchString", args, argcnt);
1219 XtAddCallback(findText, XmNfocusCallback, (XtCallbackProc)fFocusCB, window);
1220 XtAddCallback(findText, XmNvalueChangedCallback,
1221 (XtCallbackProc)findTextValueChangedCB, window);
1222 XtAddEventHandler(findText, KeyPressMask, False,
1223 (XtEventHandler)findArrowKeyCB, window);
1224 RemapDeleteKey(findText);
1225 XtManageChild(findText);
1226 XmAddTabGroup(findText);
1227 XtVaSetValues(label1, XmNuserData, findText, NULL); /* mnemonic processing */
1229 argcnt = 0;
1230 XtSetArg(args[argcnt], XmNorientation, XmHORIZONTAL); argcnt++;
1231 XtSetArg(args[argcnt], XmNpacking, XmPACK_TIGHT); argcnt++;
1232 XtSetArg(args[argcnt], XmNmarginHeight, 0); argcnt++;
1233 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_WIDGET); argcnt++;
1234 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
1235 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_NONE); argcnt++;
1236 XtSetArg(args[argcnt], XmNtopWidget, findText); argcnt++;
1237 XtSetArg(args[argcnt], XmNleftOffset, 2); argcnt++;
1238 XtSetArg(args[argcnt], XmNrightOffset, 4); argcnt++;
1240 searchTypeBox = XmCreateRowColumn(form, "searchTypeBox", args, argcnt);
1241 XtManageChild(searchTypeBox);
1242 XmAddTabGroup(searchTypeBox);
1244 argcnt = 0;
1245 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1246 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1247 XtSetArg(args[argcnt], XmNlabelString,
1248 st1=MKSTRING("Regular Expression")); argcnt++;
1249 XtSetArg(args[argcnt], XmNmnemonic, 'R'); argcnt++;
1250 window->findRegexToggle = XmCreateToggleButton(searchTypeBox, "regExp", args, argcnt);
1251 XmStringFree(st1);
1252 XtManageChild(window->findRegexToggle);
1253 XtAddCallback(window->findRegexToggle, XmNvalueChangedCallback, (XtCallbackProc) findRegExpToggleCB, window);
1255 argcnt = 0;
1256 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1257 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1258 XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("Case Sensitive")); argcnt++;
1259 XtSetArg(args[argcnt], XmNmnemonic, 'C'); argcnt++;
1260 window->findCaseToggle = XmCreateToggleButton(searchTypeBox, "caseSensitive", args, argcnt);
1261 XmStringFree(st1);
1262 XtManageChild(window->findCaseToggle);
1263 XtAddCallback(window->findCaseToggle, XmNvalueChangedCallback, (XtCallbackProc) findCaseToggleCB, window);
1265 argcnt = 0;
1266 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1267 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1268 XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("Whole Word")); argcnt++;
1269 XtSetArg(args[argcnt], XmNmnemonic, 'h'); argcnt++;
1270 window->findWordToggle = XmCreateToggleButton(searchTypeBox, "wholeWord", args, argcnt);
1271 XmStringFree(st1);
1272 XtManageChild(window->findWordToggle);
1274 argcnt = 0;
1275 XtSetArg(args[argcnt], XmNorientation, XmHORIZONTAL); argcnt++;
1276 XtSetArg(args[argcnt], XmNpacking, XmPACK_TIGHT); argcnt++;
1277 XtSetArg(args[argcnt], XmNmarginHeight, 0); argcnt++;
1278 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_WIDGET); argcnt++;
1279 XtSetArg(args[argcnt], XmNtopOffset, 0); argcnt++;
1280 XtSetArg(args[argcnt], XmNtopWidget, searchTypeBox); argcnt++;
1281 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
1282 XtSetArg(args[argcnt], XmNleftOffset, 2); argcnt++;
1283 XtSetArg(args[argcnt], XmNradioBehavior, False); argcnt++;
1284 searchDirBox = XmCreateRowColumn(form, "searchDirBox", args, argcnt);
1285 XtManageChild(searchDirBox);
1286 XmAddTabGroup(searchDirBox);
1288 argcnt = 0;
1289 XtSetArg(args[argcnt], XmNlabelString,
1290 st1=MKSTRING("Search Backward")); argcnt++;
1291 XtSetArg(args[argcnt], XmNmnemonic, 'B'); argcnt++;
1292 reverseBtn = XmCreateToggleButton(searchDirBox, "reverse", args, argcnt);
1293 XmStringFree(st1);
1294 XtManageChild(reverseBtn);
1296 argcnt = 0;
1297 XtSetArg(args[argcnt], XmNlabelString,
1298 st1=MKSTRING("Keep Dialog")); argcnt++;
1299 XtSetArg(args[argcnt], XmNmnemonic, 'K'); argcnt++;
1300 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_WIDGET); argcnt++;
1301 XtSetArg(args[argcnt], XmNtopOffset, 0); argcnt++;
1302 XtSetArg(args[argcnt], XmNtopWidget, searchTypeBox); argcnt++;
1303 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_FORM); argcnt++;
1304 XtSetArg(args[argcnt], XmNrightOffset, 4); argcnt++;
1305 keepBtn = XmCreateToggleButton(form, "keep", args, argcnt);
1306 XtAddCallback(keepBtn, XmNvalueChangedCallback,
1307 (XtCallbackProc)fKeepCB, window);
1308 XmStringFree(st1);
1309 XtManageChild(keepBtn);
1310 XmAddTabGroup(keepBtn);
1312 argcnt = 0;
1313 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
1314 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_FORM); argcnt++;
1315 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_WIDGET); argcnt++;
1316 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
1317 XtSetArg(args[argcnt], XmNtopWidget, searchDirBox); argcnt++;
1318 XtSetArg(args[argcnt], XmNleftOffset, 2); argcnt++;
1319 XtSetArg(args[argcnt], XmNrightOffset, 4); argcnt++;
1320 btnForm = XmCreateForm(form, "buttons", args, argcnt);
1321 XtManageChild(btnForm);
1322 XmAddTabGroup(btnForm);
1324 argcnt = 0;
1325 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1326 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1327 XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("Find")); argcnt++;
1328 XtSetArg(args[argcnt], XmNshowAsDefault, (short)1); argcnt++;
1329 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
1330 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_POSITION); argcnt++;
1331 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_NONE); argcnt++;
1332 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
1333 XtSetArg(args[argcnt], XmNleftPosition, 20); argcnt++;
1334 XtSetArg(args[argcnt], XmNbottomOffset, 6); argcnt++;
1335 findBtn = XmCreatePushButton(btnForm, "find", args, argcnt);
1336 XtAddCallback(findBtn, XmNactivateCallback, (XtCallbackProc)findCB, window);
1337 XmStringFree(st1);
1338 XtManageChild(findBtn);
1339 XtVaGetValues(findBtn, XmNshadowThickness, &shadowThickness, NULL);
1340 defaultBtnOffset = shadowThickness + 4;
1342 argcnt = 0;
1343 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1344 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1345 XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("Cancel")); argcnt++;
1346 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
1347 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
1348 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_NONE); argcnt++;
1349 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_POSITION); argcnt++;
1350 XtSetArg(args[argcnt], XmNrightPosition, 80); argcnt++;
1351 XtSetArg(args[argcnt], XmNtopOffset, defaultBtnOffset); argcnt++;
1352 cancelBtn = XmCreatePushButton(btnForm, "cancel", args, argcnt);
1353 XtAddCallback(cancelBtn, XmNactivateCallback, (XtCallbackProc)fCancelCB,
1354 window);
1355 XmStringFree(st1);
1356 XtManageChild(cancelBtn);
1357 XtVaSetValues(form, XmNcancelButton, cancelBtn, NULL);
1358 AddDialogMnemonicHandler(form, FALSE);
1360 window->findDlog = form;
1361 window->findText = findText;
1362 window->findRevToggle = reverseBtn;
1363 window->findKeepBtn = keepBtn;
1364 window->findBtns = btnForm;
1365 window->findBtn = findBtn;
1366 window->findSearchTypeBox = searchTypeBox;
1369 void CreateReplaceMultiFileDlog(WindowInfo *window)
1371 Arg args[50];
1372 int argcnt, defaultBtnOffset;
1373 XmString st1;
1374 Widget list, label1, form, pathBtn;
1375 Widget btnForm, replaceBtn, selectBtn, deselectBtn, cancelBtn;
1376 Dimension shadowThickness;
1378 argcnt = 0;
1379 XtSetArg(args[argcnt], XmNautoUnmanage, False); argcnt++;
1380 XtSetArg (args[argcnt], XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL);
1381 argcnt ++;
1383 /* Ideally, we should create the multi-file dialog as a child widget
1384 of the replace dialog. However, if we do this, the main window
1385 can hide the multi-file dialog when raised (I'm not sure why, but
1386 it's something that I observed with fvwm). By using the main window
1387 as the parent, it is possible that the replace dialog _partially_
1388 covers the multi-file dialog, but this much better than the multi-file
1389 dialog being covered completely by the main window */
1390 form = CreateFormDialog(window->shell, "replaceMultiFileDialog",
1391 args, argcnt);
1392 XtVaSetValues(form, XmNshadowThickness, 0, NULL);
1393 XtVaSetValues(XtParent(form), XmNtitle, "Replace All in Multiple Files",
1394 NULL);
1396 /* Label at top left. */
1397 argcnt = 0;
1398 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
1399 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
1400 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
1401 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_NONE); argcnt++;
1402 /* Offset = 6 + (highlightThickness + detailShadowThickness) of the
1403 toggle button (see below). Unfortunately, detailShadowThickness is
1404 a Motif 2.x property, so we can't measure it. The default is 2 pixels.
1405 To make things even more complicated, the SunOS 5.6 / Solaris 2.6
1406 version of Motif 1.2 seems to use a detailShadowThickness of 0 ...
1407 So we'll have to live with a slight misalignment on that platform
1408 (those Motif libs are known to have many other problems). */
1409 XtSetArg(args[argcnt], XmNtopOffset, 10); argcnt++;
1410 XtSetArg(args[argcnt], XmNleftOffset, 6); argcnt++;
1411 XtSetArg(args[argcnt], XmNalignment, XmALIGNMENT_BEGINNING); argcnt++;
1412 XtSetArg(args[argcnt], XmNlabelString,
1413 st1=MKSTRING("Files in which to Replace All:")); argcnt++;
1414 XtSetArg(args[argcnt], XmNmnemonic, 'F'); argcnt++;
1415 label1 = XmCreateLabel(form, "label1", args, argcnt);
1416 XmStringFree(st1);
1417 XtManageChild(label1);
1419 /* Pathname toggle button at top right (always unset by default) */
1420 argcnt = 0;
1421 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1422 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1423 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_NONE); argcnt++;
1424 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_FORM); argcnt++;
1425 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
1426 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
1427 XtSetArg(args[argcnt], XmNset, False); argcnt++;
1428 XtSetArg(args[argcnt], XmNrightOffset, 6); argcnt++;
1429 XtSetArg(args[argcnt], XmNtopOffset, 6); argcnt++;
1430 XtSetArg(args[argcnt], XmNalignment, XmALIGNMENT_END); argcnt++;
1431 XtSetArg(args[argcnt], XmNlabelString,
1432 st1=MKSTRING("Show Path Names")); argcnt++;
1433 XtSetArg(args[argcnt], XmNmnemonic, 'P'); argcnt++;
1434 pathBtn = XmCreateToggleButton(form, "path", args, argcnt);
1435 XmStringFree(st1);
1436 XtAddCallback(pathBtn, XmNvalueChangedCallback,
1437 (XtCallbackProc)rMultiFilePathCB, window);
1438 XtManageChild(pathBtn);
1441 * Buttons at bottom. Place them before the list, such that we can
1442 * attach the list to the label and the button box. In that way only
1443 * the lists resizes vertically when the dialog is resized; users expect
1444 * the list to resize, not the buttons.
1447 argcnt = 0;
1448 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
1449 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_FORM); argcnt++;
1450 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_NONE); argcnt++;
1451 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_FORM); argcnt++;
1452 XtSetArg(args[argcnt], XmNleftOffset, 6); argcnt++;
1453 XtSetArg(args[argcnt], XmNrightOffset, 6); argcnt++;
1454 XtSetArg(args[argcnt], XmNtopOffset, 6); argcnt++;
1455 XtSetArg(args[argcnt], XmNresizable, (short)0); argcnt++;
1456 btnForm = XmCreateForm(form, "buttons", args, argcnt);
1457 XtManageChild(btnForm);
1459 /* Replace */
1460 argcnt = 0;
1461 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1462 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1463 XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("Replace")); argcnt++;
1464 XtSetArg(args[argcnt], XmNshowAsDefault, (short)1); argcnt++;
1465 XtSetArg(args[argcnt], XmNmnemonic, 'R'); argcnt++;
1466 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
1467 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
1468 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_POSITION); argcnt++;
1469 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_POSITION); argcnt++;
1470 XtSetArg(args[argcnt], XmNleftPosition, 0); argcnt++;
1471 XtSetArg(args[argcnt], XmNrightPosition, 25); argcnt++;
1472 replaceBtn = XmCreatePushButton(btnForm, "replace", args, argcnt);
1473 XmStringFree(st1);
1474 XtAddCallback(replaceBtn, XmNactivateCallback,
1475 (XtCallbackProc)rMultiFileReplaceCB, window);
1477 * _DON'T_ set the replace button as default (as in other dialogs).
1478 * Multi-selection lists have the nasty property of selecting the
1479 * current item when <enter> is pressed.
1480 * In that way, the user could inadvertently select an additional file
1481 * (most likely the last one that was deselected).
1482 * The user has to activate the replace button explictly (either with
1483 * a mouse click or with the shortcut key).
1485 * XtVaSetValues(form, XmNdefaultButton, replaceBtn, NULL); */
1487 XtManageChild(replaceBtn);
1488 XtVaGetValues(replaceBtn, XmNshadowThickness, &shadowThickness, NULL);
1489 defaultBtnOffset = shadowThickness + 4;
1491 /* Select All */
1492 argcnt = 0;
1493 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1494 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1495 XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("Select All"));
1496 argcnt++;
1497 XtSetArg(args[argcnt], XmNmnemonic, 'S'); argcnt++;
1498 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
1499 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
1500 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_POSITION); argcnt++;
1501 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_POSITION); argcnt++;
1502 XtSetArg(args[argcnt], XmNleftPosition, 25); argcnt++;
1503 XtSetArg(args[argcnt], XmNrightPosition, 50); argcnt++;
1504 XtSetArg(args[argcnt], XmNtopOffset, defaultBtnOffset); argcnt++;
1505 selectBtn = XmCreatePushButton(btnForm, "select", args, argcnt);
1506 XmStringFree(st1);
1507 XtAddCallback(selectBtn, XmNactivateCallback,
1508 (XtCallbackProc)rMultiFileSelectAllCB, window);
1509 XtManageChild(selectBtn);
1511 /* Deselect All */
1512 argcnt = 0;
1513 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1514 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1515 XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("Deselect All"));
1516 argcnt++;
1517 XtSetArg(args[argcnt], XmNmnemonic, 'D'); argcnt++;
1518 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
1519 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
1520 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_POSITION); argcnt++;
1521 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_POSITION); argcnt++;
1522 XtSetArg(args[argcnt], XmNleftPosition, 50); argcnt++;
1523 XtSetArg(args[argcnt], XmNrightPosition, 75); argcnt++;
1524 XtSetArg(args[argcnt], XmNtopOffset, defaultBtnOffset); argcnt++;
1525 deselectBtn = XmCreatePushButton(btnForm, "deselect", args, argcnt);
1526 XmStringFree(st1);
1527 XtAddCallback(deselectBtn, XmNactivateCallback,
1528 (XtCallbackProc)rMultiFileDeselectAllCB, window);
1529 XtManageChild(deselectBtn);
1531 /* Cancel */
1532 argcnt = 0;
1533 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1534 XtSetArg(args[argcnt], XmNhighlightThickness, 2); argcnt++;
1535 XtSetArg(args[argcnt], XmNlabelString, st1=MKSTRING("Cancel")); argcnt++;
1536 XtSetArg(args[argcnt], XmNmnemonic, 'C'); argcnt++;
1537 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
1538 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_NONE); argcnt++;
1539 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_POSITION); argcnt++;
1540 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_POSITION); argcnt++;
1541 XtSetArg(args[argcnt], XmNleftPosition, 75); argcnt++;
1542 XtSetArg(args[argcnt], XmNrightPosition, 100); argcnt++;
1543 XtSetArg(args[argcnt], XmNtopOffset, defaultBtnOffset); argcnt++;
1544 cancelBtn = XmCreatePushButton(btnForm, "cancel", args, argcnt);
1545 XmStringFree(st1);
1546 XtAddCallback(cancelBtn, XmNactivateCallback,
1547 (XtCallbackProc)rMultiFileCancelCB, window);
1548 XtManageChild(cancelBtn);
1550 /* The list of files */
1551 argcnt = 0;
1552 XtSetArg(args[argcnt], XmNtraversalOn, True); argcnt++;
1553 XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_WIDGET); argcnt++;
1554 XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_WIDGET); argcnt++;
1555 XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
1556 XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_FORM); argcnt++;
1557 XtSetArg(args[argcnt], XmNbottomWidget, btnForm); argcnt++;
1558 XtSetArg(args[argcnt], XmNtopWidget, label1); argcnt++;
1559 XtSetArg(args[argcnt], XmNleftOffset, 10); argcnt++;
1560 XtSetArg(args[argcnt], XmNvisibleItemCount, 10); argcnt++;
1561 XtSetArg(args[argcnt], XmNtopOffset, 6); argcnt++;
1562 XtSetArg(args[argcnt], XmNbottomOffset, 6); argcnt++;
1563 XtSetArg(args[argcnt], XmNrightOffset, 10); argcnt++;
1564 /* An alternative is to use the EXTENDED_SELECT, but that one
1565 is less suited for keyboard manipulation (moving the selection cursor
1566 with the keyboard deselects everything). */
1567 XtSetArg(args[argcnt], XmNselectionPolicy, XmMULTIPLE_SELECT); argcnt++;
1568 list = XmCreateScrolledList(form, "list_of_files", args, argcnt);
1569 AddMouseWheelSupport(list);
1570 XtManageChild(list);
1572 /* Traverse: list -> buttons -> path name toggle button */
1573 XmAddTabGroup(list);
1574 XmAddTabGroup(btnForm);
1575 XmAddTabGroup(pathBtn);
1577 XtVaSetValues(label1, XmNuserData, list, NULL); /* mnemonic processing */
1579 /* Cancel/Mnemonic stuff. */
1580 XtVaSetValues(form, XmNcancelButton, cancelBtn, NULL);
1581 AddDialogMnemonicHandler(form, FALSE);
1583 window->replaceMultiFileDlog = form;
1584 window->replaceMultiFileList = list;
1585 window->replaceMultiFilePathBtn = pathBtn;
1587 /* Install a handler that frees the list of writable windows when
1588 the dialog is unmapped. */
1589 XtAddCallback(form, XmNunmapCallback,
1590 (XtCallbackProc)freeWritableWindowsCB, window);
1594 ** Iterates through the list of writable windows of a window, and removes
1595 ** the doomed window if necessary.
1597 static void checkMultiReplaceListForDoomedW(WindowInfo* window,
1598 WindowInfo* doomedWindow)
1600 WindowInfo *w;
1601 int i;
1603 /* If the window owning the list and the doomed window are one and the
1604 same, we just close the multi-file replacement dialog. */
1605 if (window == doomedWindow) {
1606 XtUnmanageChild(window->replaceMultiFileDlog);
1607 return;
1610 /* Check whether the doomed window is currently listed */
1611 for (i = 0; i < window->nWritableWindows; ++i) {
1612 w = window->writableWindows[i];
1613 if (w == doomedWindow) {
1614 removeDoomedWindowFromList(window, i);
1615 break;
1621 ** Removes a window that is about to be closed from the list of files in
1622 ** which to replace. If the list becomes empty, the dialog is popped down.
1624 static void removeDoomedWindowFromList(WindowInfo* window, int index)
1626 int entriesToMove;
1628 /* If the list would become empty, we remove the dialog */
1629 if (window->nWritableWindows <= 1) {
1630 XtUnmanageChild(window->replaceMultiFileDlog);
1631 return;
1634 entriesToMove = window->nWritableWindows - index - 1;
1635 memmove(&(window->writableWindows[index]),
1636 &(window->writableWindows[index+1]),
1637 (size_t)(entriesToMove*sizeof(WindowInfo*)));
1638 window->nWritableWindows -= 1;
1640 XmListDeletePos(window->replaceMultiFileList, index + 1);
1644 ** These callbacks fix a Motif 1.1 problem that the default button gets the
1645 ** keyboard focus when a dialog is created. We want the first text field
1646 ** to get the focus, so we don't set the default button until the text field
1647 ** has the focus for sure. I have tried many other ways and this is by far
1648 ** the least nasty.
1650 static void fFocusCB(Widget w, WindowInfo *window, caddr_t *callData)
1652 window = WidgetToWindow(w);
1653 SET_ONE_RSRC(window->findDlog, XmNdefaultButton, window->findBtn);
1655 static void rFocusCB(Widget w, WindowInfo *window, caddr_t *callData)
1657 window = WidgetToWindow(w);
1658 SET_ONE_RSRC(window->replaceDlog, XmNdefaultButton, window->replaceBtn);
1661 /* when keeping a window up, clue the user what window it's associated with */
1662 static void rKeepCB(Widget w, WindowInfo *window, caddr_t *callData)
1664 char title[MAXPATHLEN + 19];
1666 window = WidgetToWindow(w);
1668 if (XmToggleButtonGetState(w)) {
1669 sprintf(title, "Replace/Find (in %s)", window->filename);
1670 XtVaSetValues(XtParent(window->replaceDlog), XmNtitle, title, NULL);
1671 } else
1672 XtVaSetValues(XtParent(window->replaceDlog), XmNtitle, "Replace/Find", NULL);
1674 static void fKeepCB(Widget w, WindowInfo *window, caddr_t *callData)
1676 char title[MAXPATHLEN + 11];
1678 window = WidgetToWindow(w);
1680 if (XmToggleButtonGetState(w)) {
1681 sprintf(title, "Find (in %s)", window->filename);
1682 XtVaSetValues(XtParent(window->findDlog), XmNtitle, title, NULL);
1683 } else
1684 XtVaSetValues(XtParent(window->findDlog), XmNtitle, "Find", NULL);
1687 static void replaceCB(Widget w, WindowInfo *window,
1688 XmAnyCallbackStruct *callData)
1690 char searchString[SEARCHMAX], replaceString[SEARCHMAX];
1691 int direction, searchType;
1692 char *params[5];
1694 window = WidgetToWindow(w);
1696 /* Validate and fetch the find and replace strings from the dialog */
1697 if (!getReplaceDlogInfo(window, &direction, searchString, replaceString,
1698 &searchType))
1699 return;
1701 /* Set the initial focus of the dialog back to the search string */
1702 resetReplaceTabGroup(window);
1704 /* Find the text and replace it */
1705 params[0] = searchString;
1706 params[1] = replaceString;
1707 params[2] = directionArg(direction);
1708 params[3] = searchTypeArg(searchType);
1709 params[4] = searchWrapArg(GetPrefSearchWraps());
1710 XtCallActionProc(window->lastFocus, "replace", callData->event, params, 5);
1712 /* Pop down the dialog */
1713 if (!XmToggleButtonGetState(window->replaceKeepBtn))
1714 unmanageReplaceDialogs(window);
1717 static void replaceAllCB(Widget w, WindowInfo *window,
1718 XmAnyCallbackStruct *callData)
1720 char searchString[SEARCHMAX], replaceString[SEARCHMAX];
1721 int direction, searchType;
1722 char *params[3];
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 /* do replacement */
1735 params[0] = searchString;
1736 params[1] = replaceString;
1737 params[2] = searchTypeArg(searchType);
1738 XtCallActionProc(window->lastFocus, "replace_all", callData->event,
1739 params, 3);
1741 /* pop down the dialog */
1742 if (!XmToggleButtonGetState(window->replaceKeepBtn))
1743 unmanageReplaceDialogs(window);
1746 static void replaceMultiFileCB(Widget w, WindowInfo *window,
1747 XmAnyCallbackStruct *callData)
1749 window = WidgetToWindow(w);
1750 DoReplaceMultiFileDlog(window);
1754 ** Callback that frees the list of windows the multi-file replace
1755 ** dialog is unmapped.
1757 static void freeWritableWindowsCB(Widget w, WindowInfo* window,
1758 XmAnyCallbackStruct *callData)
1760 window = WidgetToWindow(w);
1761 XtFree((XtPointer)window->writableWindows);
1762 window->writableWindows = NULL;
1763 window->nWritableWindows = 0;
1767 ** Comparison function for sorting windows by title for the window menu
1769 static int compareWindowNames(const void *windowA, const void *windowB)
1771 return strcmp((*((WindowInfo**)windowA))->filename,
1772 (*((WindowInfo**)windowB))->filename);
1776 ** Count no. of windows
1778 static int countWindows(void)
1780 int nWindows;
1781 const WindowInfo *w;
1783 for (w=WindowList, nWindows=0; w!=NULL; w=w->next, ++nWindows);
1785 return nWindows;
1789 ** Count no. of writable windows, but first update the status of all files.
1791 static int countWritableWindows(void)
1793 int nWritable, nBefore, nAfter;
1794 WindowInfo *w;
1796 nBefore = countWindows();
1797 for (w=WindowList, nWritable=0; w!=NULL; w=w->next) {
1798 /* We must be very careful! The status check may trigger a pop-up
1799 dialog when the file has changed on disk, and the user may destroy
1800 arbitrary windows in response. */
1801 CheckForChangesToFile(w);
1802 nAfter = countWindows();
1803 if (nAfter != nBefore) {
1804 /* The user has destroyed a file; start counting all over again */
1805 nBefore = nAfter;
1806 w = WindowList;
1807 nWritable = 0;
1808 continue;
1810 if (!IS_ANY_LOCKED(w->lockReasons)) ++nWritable;
1812 return nWritable;
1816 ** Collects a list of writable windows (sorted by file name).
1817 ** The previous list, if any is freed first.
1819 static void collectWritableWindows(WindowInfo* window)
1821 int nWritable = countWritableWindows();
1822 int i;
1823 WindowInfo *w;
1824 WindowInfo **windows;
1826 if (window->writableWindows)
1828 XtFree((XtPointer)window->writableWindows);
1831 /* Make a sorted list of writable windows */
1832 windows = (WindowInfo **)XtMalloc(sizeof(WindowInfo *) * nWritable);
1833 for (w=WindowList, i=0; w!=NULL; w=w->next)
1834 if (!IS_ANY_LOCKED(w->lockReasons)) windows[i++] = w;
1835 qsort(windows, nWritable, sizeof(WindowInfo *), compareWindowNames);
1837 window->writableWindows = windows;
1838 window->nWritableWindows = nWritable;
1841 static void rMultiFileReplaceCB(Widget w, WindowInfo *window,
1842 XmAnyCallbackStruct *callData)
1844 char searchString[SEARCHMAX], replaceString[SEARCHMAX];
1845 int direction, searchType;
1846 char *params[4];
1847 int nSelected, i;
1848 WindowInfo *writableWin;
1849 Bool replaceFailed, noWritableLeft;
1851 window = WidgetToWindow(w);
1852 nSelected = 0;
1853 for (i=0; i<window->nWritableWindows; ++i)
1854 if (XmListPosSelected(window->replaceMultiFileList, i+1))
1855 ++nSelected;
1857 if (!nSelected)
1859 DialogF(DF_INF, XtParent(window->replaceMultiFileDlog), 1, "No Files",
1860 "No files selected!", "OK");
1861 return; /* Give the user another chance */
1864 /* Set the initial focus of the dialog back to the search string */
1865 resetReplaceTabGroup(window);
1868 * Protect the user against him/herself; Maybe this is a bit too much?
1870 if (DialogF(DF_QUES, window->shell, 2, "Multi-File Replacement",
1871 "Multi-file replacements are difficult to undo.\n"
1872 "Proceed with the replacement ?", "Yes", "Cancel") != 1)
1874 /* pop down the multi-file dialog only */
1875 XtUnmanageChild(window->replaceMultiFileDlog);
1877 return;
1880 /* Fetch the find and replace strings from the dialog;
1881 they should have been validated already, but since Lesstif may not
1882 honor modal dialogs, it is possible that the user modified the
1883 strings again, so we should verify them again too. */
1884 if (!getReplaceDlogInfo(window, &direction, searchString, replaceString,
1885 &searchType))
1886 return;
1888 /* Set the initial focus of the dialog back to the search string */
1889 resetReplaceTabGroup(window);
1891 params[0] = searchString;
1892 params[1] = replaceString;
1893 params[2] = searchTypeArg(searchType);
1895 replaceFailed = True;
1896 noWritableLeft = True;
1897 /* Perform the replacements and mark the selected files (history) */
1898 for (i=0; i<window->nWritableWindows; ++i) {
1899 writableWin = window->writableWindows[i];
1900 if (XmListPosSelected(window->replaceMultiFileList, i+1)) {
1901 /* First check again whether the file is still writable. If the
1902 file status has changed or the file was locked in the mean time
1903 (possible due to Lesstif modal dialog bug), we just skip the
1904 window. */
1905 if (!IS_ANY_LOCKED(writableWin->lockReasons)) {
1906 noWritableLeft = False;
1907 writableWin->multiFileReplSelected = True;
1908 writableWin->multiFileBusy = True; /* Avoid multi-beep/dialog */
1909 writableWin->replaceFailed = False;
1910 XtCallActionProc(writableWin->lastFocus, "replace_all",
1911 callData->event, params, 3);
1912 writableWin->multiFileBusy = False;
1913 if (!writableWin->replaceFailed)
1914 replaceFailed = False;
1916 } else {
1917 writableWin->multiFileReplSelected = False;
1921 /* doing replace on non-top buffers will modified certain shell
1922 properties, in this case the menu bar, owned by the top
1923 buffers. we need to restore them for the top buffers */
1924 if (GetPrefTabbedMode()) {
1925 for (i=0; i<window->nWritableWindows; ++i) {
1926 if (XmListPosSelected(window->replaceMultiFileList, i+1)) {
1927 WindowInfo *topBuf =
1928 GetTopDocument(window->writableWindows[i]->shell);
1930 DimSelectionDepUserMenuItems(topBuf, topBuf->wasSelected);
1931 RefreshMenuToggleStates(topBuf);
1936 if (!XmToggleButtonGetState(window->replaceKeepBtn)) {
1937 /* Pop down both replace dialogs. */
1938 unmanageReplaceDialogs(window);
1939 } else {
1940 /* pow down only the file selection dialog */
1941 XtUnmanageChild(window->replaceMultiFileDlog);
1944 /* We suppressed multiple beeps/dialogs. If there wasn't any file in
1945 which the replacement succeeded, we should still warn the user */
1946 if (replaceFailed) {
1947 if (GetPrefSearchDlogs()) {
1948 if (noWritableLeft) {
1949 DialogF(DF_INF, window->shell, 1, "Read-only Files",
1950 "All selected files have become read-only.", "OK");
1951 } else {
1952 DialogF(DF_INF, window->shell, 1, "String not found",
1953 "String was not found", "OK");
1955 } else {
1956 XBell(TheDisplay, 0);
1961 static void rMultiFileCancelCB(Widget w, WindowInfo *window, caddr_t callData)
1963 window = WidgetToWindow(w);
1965 /* Set the initial focus of the dialog back to the search string */
1966 resetReplaceTabGroup(window);
1968 /* pop down the multi-window replace dialog */
1969 XtUnmanageChild(window->replaceMultiFileDlog);
1972 static void rMultiFileSelectAllCB(Widget w, WindowInfo *window,
1973 XmAnyCallbackStruct *callData)
1975 int i;
1976 char policy;
1977 Widget list;
1979 window = WidgetToWindow(w);
1980 list = window->replaceMultiFileList;
1983 * If the list is in extended selection mode, we can't select more
1984 * than one item (probably because XmListSelectPos is equivalent
1985 * to a button1 click; I don't think that there is an equivalent
1986 * for CTRL-button1). Therefore, we temporarily put the list into
1987 * multiple selection mode.
1988 * Note: this is not really necessary if the list is in multiple select
1989 * mode all the time (as it currently is).
1991 XtVaGetValues(list, XmNselectionPolicy, &policy, NULL);
1992 XtVaSetValues(list, XmNselectionPolicy, XmMULTIPLE_SELECT, NULL);
1994 /* Is there no other way (like "select all") ? */
1995 XmListDeselectAllItems(window->replaceMultiFileList); /* select toggles */
1997 for (i=0; i<window->nWritableWindows; ++i) {
1998 XmListSelectPos(list, i+1, FALSE);
2001 /* Restore the original policy. */
2002 XtVaSetValues(list, XmNselectionPolicy, policy, NULL);
2005 static void rMultiFileDeselectAllCB(Widget w, WindowInfo *window,
2006 XmAnyCallbackStruct *callData)
2008 window = WidgetToWindow(w);
2009 XmListDeselectAllItems(window->replaceMultiFileList);
2012 static void rMultiFilePathCB(Widget w, WindowInfo *window,
2013 XmAnyCallbackStruct *callData)
2015 window = WidgetToWindow(w);
2016 uploadFileListItems(window, True); /* Replace */
2020 * Uploads the file items to the multi-file replament dialog list.
2021 * A boolean argument indicates whether the elements currently in the
2022 * list have to be replaced or not.
2023 * Depending on the state of the "Show path names" toggle button, either
2024 * the file names or the path names are listed.
2026 static void uploadFileListItems(WindowInfo* window, Bool replace)
2028 XmStringTable names;
2029 int nWritable, i, *selected, selectedCount;
2030 char buf[MAXPATHLEN+1], policy;
2031 Bool usePathNames;
2032 WindowInfo *w;
2033 Widget list;
2035 nWritable = window->nWritableWindows;
2036 list = window->replaceMultiFileList;
2038 names = (XmStringTable) XtMalloc(nWritable * sizeof(XmString*));
2040 usePathNames = XmToggleButtonGetState(window->replaceMultiFilePathBtn);
2042 /* Note: the windows are sorted alphabetically by _file_ name. This
2043 order is _not_ changed when we switch to path names. That
2044 would be confusing for the user */
2046 for (i = 0; i < nWritable; ++i) {
2047 w = window->writableWindows[i];
2048 if (usePathNames && window->filenameSet) {
2049 sprintf(buf, "%s%s", w->path, w->filename);
2050 } else {
2051 sprintf(buf, "%s", w->filename);
2053 names[i] = XmStringCreateSimple(buf);
2057 * If the list is in extended selection mode, we can't pre-select
2058 * more than one item in (probably because XmListSelectPos is
2059 * equivalent to a button1 click; I don't think that there is an
2060 * equivalent for CTRL-button1). Therefore, we temporarily put the
2061 * list into multiple selection mode.
2063 XtVaGetValues(list, XmNselectionPolicy, &policy, NULL);
2064 XtVaSetValues(list, XmNselectionPolicy, XmMULTIPLE_SELECT, NULL);
2065 if (replace) {
2066 /* Note: this function is obsolete in Motif 2.x, but it is available
2067 for compatibility reasons */
2068 XmListGetSelectedPos(list, &selected, &selectedCount);
2070 XmListReplaceItemsPos(list, names, nWritable, 1);
2072 /* Maintain the selections */
2073 XmListDeselectAllItems(list);
2074 for (i = 0; i < selectedCount; ++i) {
2075 XmListSelectPos(list, selected[i], False);
2078 XtFree((XtPointer)selected);
2079 } else {
2080 Arg args[1];
2081 int nVisible;
2082 int firstSelected = 0;
2084 /* Remove the old list, if any */
2085 XmListDeleteAllItems(list);
2087 /* Initial settings */
2088 XmListAddItems(list, names, nWritable, 1);
2090 /* Pre-select the files from the last run. */
2091 selectedCount = 0;
2092 for (i = 0; i < nWritable; ++i) {
2093 if (window->writableWindows[i]->multiFileReplSelected) {
2094 XmListSelectPos(list, i+1, False);
2095 ++selectedCount;
2096 /* Remember the first selected item */
2097 if (firstSelected == 0) firstSelected = i+1;
2100 /* If no files are selected, we select them all. Normally this only
2101 happens the first time the dialog is used, but it looks "silly"
2102 if the dialog pops up with nothing selected. */
2103 if (selectedCount == 0) {
2104 for (i = 0; i < nWritable; ++i) {
2105 XmListSelectPos(list, i+1, False);
2107 firstSelected = 1;
2110 /* Make sure that the first selected item is visible; otherwise, the
2111 user could get the impression that nothing is selected. By
2112 visualizing at least the first selected item, the user will more
2113 easily be confident that the previous selection is still active. */
2114 XtSetArg(args[0], XmNvisibleItemCount, &nVisible);
2115 XtGetValues(list, args, 1);
2116 /* Make sure that we don't create blank lines at the bottom by
2117 positioning too far. */
2118 if (nWritable <= nVisible) {
2119 /* No need to shift the visible position */
2120 firstSelected = 1;
2122 else {
2123 int maxFirst = nWritable - nVisible + 1;
2124 if (firstSelected > maxFirst)
2125 firstSelected = maxFirst;
2127 XmListSetPos(list, firstSelected);
2130 /* Put the list back into its original selection policy. */
2131 XtVaSetValues(list, XmNselectionPolicy, policy, NULL);
2133 for (i = 0; i < nWritable; ++i)
2134 XmStringFree(names[i]);
2135 XtFree((XtPointer)names);
2139 ** Unconditionally pops down the replace dialog and the
2140 ** replace-in-multiple-files dialog, if it exists.
2142 static void unmanageReplaceDialogs(WindowInfo *window)
2144 /* If the replace dialog goes down, the multi-file replace dialog must
2145 go down too */
2146 if (window->replaceMultiFileDlog &&
2147 XtIsManaged(window->replaceMultiFileDlog)) {
2148 XtUnmanageChild(window->replaceMultiFileDlog);
2151 if (window->replaceDlog &&
2152 XtIsManaged(window->replaceDlog)) {
2153 XtUnmanageChild(window->replaceDlog);
2157 static void rInSelCB(Widget w, WindowInfo *window,
2158 XmAnyCallbackStruct *callData)
2160 char searchString[SEARCHMAX], replaceString[SEARCHMAX];
2161 int direction, searchType;
2162 char *params[3];
2164 window = WidgetToWindow(w);
2166 /* Validate and fetch the find and replace strings from the dialog */
2167 if (!getReplaceDlogInfo(window, &direction, searchString, replaceString,
2168 &searchType))
2169 return;
2171 /* Set the initial focus of the dialog back to the search string */
2172 resetReplaceTabGroup(window);
2174 /* do replacement */
2175 params[0] = searchString;
2176 params[1] = replaceString;
2177 params[2] = searchTypeArg(searchType);
2178 XtCallActionProc(window->lastFocus, "replace_in_selection",
2179 callData->event, params, 3);
2181 /* pop down the dialog */
2182 if (!XmToggleButtonGetState(window->replaceKeepBtn))
2183 unmanageReplaceDialogs(window);
2186 static void rCancelCB(Widget w, WindowInfo *window, caddr_t callData)
2188 window = WidgetToWindow(w);
2190 /* Set the initial focus of the dialog back to the search string */
2191 resetReplaceTabGroup(window);
2193 /* pop down the dialog */
2194 unmanageReplaceDialogs(window);
2197 static void fCancelCB(Widget w, WindowInfo *window, caddr_t callData)
2199 window = WidgetToWindow(w);
2201 /* Set the initial focus of the dialog back to the search string */
2202 resetFindTabGroup(window);
2204 /* pop down the dialog */
2205 XtUnmanageChild(window->findDlog);
2208 static void rFindCB(Widget w, WindowInfo *window,XmAnyCallbackStruct *callData)
2210 char searchString[SEARCHMAX], replaceString[SEARCHMAX];
2211 int direction, searchType;
2212 char *params[4];
2214 window = WidgetToWindow(w);
2216 /* Validate and fetch the find and replace strings from the dialog */
2217 if (!getReplaceDlogInfo(window, &direction, searchString, replaceString,
2218 &searchType))
2219 return;
2221 /* Set the initial focus of the dialog back to the search string */
2222 resetReplaceTabGroup(window);
2224 /* Find the text and mark it */
2225 params[0] = searchString;
2226 params[1] = directionArg(direction);
2227 params[2] = searchTypeArg(searchType);
2228 params[3] = searchWrapArg(GetPrefSearchWraps());
2229 XtCallActionProc(window->lastFocus, "find", callData->event, params, 4);
2231 /* Doctor the search history generated by the action to include the
2232 replace string (if any), so the replace string can be used on
2233 subsequent replaces, even though no actual replacement was done. */
2234 if (historyIndex(1) != -1 &&
2235 !strcmp(SearchHistory[historyIndex(1)], searchString)) {
2236 XtFree(ReplaceHistory[historyIndex(1)]);
2237 ReplaceHistory[historyIndex(1)] = XtNewString(replaceString);
2240 /* Pop down the dialog */
2241 if (!XmToggleButtonGetState(window->replaceKeepBtn))
2242 unmanageReplaceDialogs(window);
2245 static void replaceFindCB(Widget w, WindowInfo *window, XmAnyCallbackStruct *callData)
2247 char searchString[SEARCHMAX+1], replaceString[SEARCHMAX+1];
2248 int direction, searchType;
2249 char *params[4];
2251 window = WidgetToWindow(w);
2253 /* Validate and fetch the find and replace strings from the dialog */
2254 if (!getReplaceDlogInfo(window, &direction, searchString, replaceString,
2255 &searchType))
2256 return;
2258 /* Set the initial focus of the dialog back to the search string */
2259 resetReplaceTabGroup(window);
2261 /* Find the text and replace it */
2262 params[0] = searchString;
2263 params[1] = replaceString;
2264 params[2] = directionArg(direction);
2265 params[3] = searchTypeArg(searchType);
2266 XtCallActionProc(window->lastFocus, "replace_find", callData->event, params, 4);
2268 /* Pop down the dialog */
2269 if (!XmToggleButtonGetState(window->replaceKeepBtn))
2270 unmanageReplaceDialogs(window);
2273 static void rSetActionButtons(WindowInfo* window,
2274 int replaceBtn,
2275 int replaceFindBtn,
2276 int replaceAndFindBtn,
2277 #ifndef REPLACE_SCOPE
2278 int replaceInWinBtn,
2279 int replaceInSelBtn,
2280 #endif
2281 int replaceAllBtn)
2283 XtSetSensitive(window->replaceBtn, replaceBtn);
2284 XtSetSensitive(window->replaceFindBtn, replaceFindBtn);
2285 XtSetSensitive(window->replaceAndFindBtn, replaceAndFindBtn);
2286 #ifndef REPLACE_SCOPE
2287 XtSetSensitive(window->replaceInWinBtn, replaceInWinBtn);
2288 XtSetSensitive(window->replaceInSelBtn, replaceInSelBtn);
2289 #endif
2290 XtSetSensitive(window->replaceAllBtn, replaceAllBtn);
2293 void UpdateReplaceActionButtons(WindowInfo* window)
2295 /* Is there any text in the search for field */
2296 int searchText = textFieldNonEmpty(window->replaceText);
2297 #ifdef REPLACE_SCOPE
2298 switch (window->replaceScope)
2300 case REPL_SCOPE_WIN:
2301 /* Enable all buttons, if there is any text in the search field. */
2302 rSetActionButtons(window, searchText, searchText, searchText, searchText);
2303 break;
2305 case REPL_SCOPE_SEL:
2306 /* Only enable Replace All, if a selection exists and text in search field. */
2307 rSetActionButtons(window, False, False, False, searchText && window->wasSelected);
2308 break;
2310 case REPL_SCOPE_MULTI:
2311 /* Only enable Replace All, if text in search field. */
2312 rSetActionButtons(window, False, False, False, searchText);
2313 break;
2315 #else
2316 rSetActionButtons(window, searchText, searchText, searchText,
2317 searchText, searchText && window->wasSelected,
2318 searchText && (countWritableWindows() > 1));
2319 #endif
2322 #ifdef REPLACE_SCOPE
2324 ** The next 3 callback adapt the sensitivity of the replace dialog push
2325 ** buttons to the state of the scope radio buttons.
2327 static void rScopeWinCB(Widget w, WindowInfo *window,
2328 XmAnyCallbackStruct *callData)
2330 window = WidgetToWindow(w);
2331 if (XmToggleButtonGetState(window->replaceScopeWinToggle)) {
2332 window->replaceScope = REPL_SCOPE_WIN;
2333 UpdateReplaceActionButtons(window);
2337 static void rScopeSelCB(Widget w, WindowInfo *window,
2338 XmAnyCallbackStruct *callData)
2340 window = WidgetToWindow(w);
2341 if (XmToggleButtonGetState(window->replaceScopeSelToggle)) {
2342 window->replaceScope = REPL_SCOPE_SEL;
2343 UpdateReplaceActionButtons(window);
2347 static void rScopeMultiCB(Widget w, WindowInfo *window,
2348 XmAnyCallbackStruct *callData)
2350 window = WidgetToWindow(w);
2351 if (XmToggleButtonGetState(window->replaceScopeMultiToggle)) {
2352 window->replaceScope = REPL_SCOPE_MULTI;
2353 UpdateReplaceActionButtons(window);
2358 ** This routine dispatches a push on the replace-all button to the appropriate
2359 ** callback, depending on the state of the scope radio buttons.
2361 static void replaceAllScopeCB(Widget w, WindowInfo *window,
2362 XmAnyCallbackStruct *callData)
2364 window = WidgetToWindow(w);
2365 switch(window->replaceScope) {
2366 case REPL_SCOPE_WIN:
2367 replaceAllCB(w, window, callData);
2368 break;
2369 case REPL_SCOPE_SEL:
2370 rInSelCB(w, window, callData);
2371 break;
2372 case REPL_SCOPE_MULTI:
2373 replaceMultiFileCB(w, window, callData);
2374 break;
2377 #endif
2379 static int textFieldNonEmpty(Widget w)
2381 char *str = XmTextGetString(w);
2382 int nonEmpty = (str[0] != '\0');
2383 XtFree(str);
2384 return(nonEmpty);
2387 static void rFindTextValueChangedCB(Widget w, WindowInfo *window, XKeyEvent *event)
2389 window = WidgetToWindow(w);
2390 UpdateReplaceActionButtons(window);
2393 static void rFindArrowKeyCB(Widget w, WindowInfo *window, XKeyEvent *event)
2395 KeySym keysym = XLookupKeysym(event, 0);
2396 int index;
2397 char *searchStr, *replaceStr;
2398 int searchType;
2400 window = WidgetToWindow(w);
2401 index = window->rHistIndex;
2403 /* only process up and down arrow keys */
2404 if (keysym != XK_Up && keysym != XK_Down)
2405 return;
2407 /* increment or decrement the index depending on which arrow was pressed */
2408 index += (keysym == XK_Up) ? 1 : -1;
2410 /* if the index is out of range, beep and return */
2411 if (index != 0 && historyIndex(index) == -1) {
2412 XBell(TheDisplay, 0);
2413 return;
2416 window = WidgetToWindow(w);
2418 /* determine the strings and button settings to use */
2419 if (index == 0) {
2420 searchStr = "";
2421 replaceStr = "";
2422 searchType = GetPrefSearch();
2423 } else {
2424 searchStr = SearchHistory[historyIndex(index)];
2425 replaceStr = ReplaceHistory[historyIndex(index)];
2426 searchType = SearchTypeHistory[historyIndex(index)];
2429 /* Set the buttons and fields with the selected search type */
2430 initToggleButtons(searchType, window->replaceRegexToggle,
2431 window->replaceCaseToggle, &window->replaceWordToggle,
2432 &window->replaceLastLiteralCase,
2433 &window->replaceLastRegexCase);
2435 XmTextSetString(window->replaceText, searchStr);
2436 XmTextSetString(window->replaceWithText, replaceStr);
2438 /* Set the state of the Replace, Find ... buttons */
2439 UpdateReplaceActionButtons(window);
2441 window->rHistIndex = index;
2444 static void replaceArrowKeyCB(Widget w, WindowInfo *window, XKeyEvent *event)
2446 KeySym keysym = XLookupKeysym(event, 0);
2447 int index;
2449 window = WidgetToWindow(w);
2450 index = window->rHistIndex;
2452 /* only process up and down arrow keys */
2453 if (keysym != XK_Up && keysym != XK_Down)
2454 return;
2456 /* increment or decrement the index depending on which arrow was pressed */
2457 index += (keysym == XK_Up) ? 1 : -1;
2459 /* if the index is out of range, beep and return */
2460 if (index != 0 && historyIndex(index) == -1) {
2461 XBell(TheDisplay, 0);
2462 return;
2465 window = WidgetToWindow(w);
2467 /* change only the replace field information */
2468 if (index == 0)
2469 XmTextSetString(window->replaceWithText, "");
2470 else
2471 XmTextSetString(window->replaceWithText,
2472 ReplaceHistory[historyIndex(index)]);
2473 window->rHistIndex = index;
2476 static void fUpdateActionButtons(WindowInfo *window)
2478 int buttonState = textFieldNonEmpty(window->findText);
2479 XtSetSensitive(window->findBtn, buttonState);
2482 static void findTextValueChangedCB(Widget w, WindowInfo *window, XKeyEvent *event)
2484 window = WidgetToWindow(w);
2485 fUpdateActionButtons(window);
2488 static void findArrowKeyCB(Widget w, WindowInfo *window, XKeyEvent *event)
2490 KeySym keysym = XLookupKeysym(event, 0);
2491 int index;
2492 char *searchStr;
2493 int searchType;
2495 window = WidgetToWindow(w);
2496 index = window->fHistIndex;
2498 /* only process up and down arrow keys */
2499 if (keysym != XK_Up && keysym != XK_Down)
2500 return;
2502 /* increment or decrement the index depending on which arrow was pressed */
2503 index += (keysym == XK_Up) ? 1 : -1;
2505 /* if the index is out of range, beep and return */
2506 if (index != 0 && historyIndex(index) == -1) {
2507 XBell(TheDisplay, 0);
2508 return;
2512 /* determine the strings and button settings to use */
2513 if (index == 0) {
2514 searchStr = "";
2515 searchType = GetPrefSearch();
2516 } else {
2517 searchStr = SearchHistory[historyIndex(index)];
2518 searchType = SearchTypeHistory[historyIndex(index)];
2521 /* Set the buttons and fields with the selected search type */
2522 initToggleButtons(searchType, window->findRegexToggle,
2523 window->findCaseToggle, &window->findWordToggle,
2524 &window->findLastLiteralCase,
2525 &window->findLastRegexCase);
2526 XmTextSetString(window->findText, searchStr);
2528 /* Set the state of the Find ... button */
2529 fUpdateActionButtons(window);
2531 window->fHistIndex = index;
2534 static void findCB(Widget w, WindowInfo *window,XmAnyCallbackStruct *callData)
2536 char searchString[SEARCHMAX];
2537 int direction, searchType;
2538 char *params[4];
2540 window = WidgetToWindow(w);
2542 /* fetch find string, direction and type from the dialog */
2543 if (!getFindDlogInfo(window, &direction, searchString, &searchType))
2544 return;
2546 /* Set the initial focus of the dialog back to the search string */
2547 resetFindTabGroup(window);
2549 /* find the text and mark it */
2550 params[0] = searchString;
2551 params[1] = directionArg(direction);
2552 params[2] = searchTypeArg(searchType);
2553 params[3] = searchWrapArg(GetPrefSearchWraps());
2554 XtCallActionProc(window->lastFocus, "find", callData->event, params, 4);
2556 /* pop down the dialog */
2557 if (!XmToggleButtonGetState(window->findKeepBtn))
2558 XtUnmanageChild(window->findDlog);
2562 ** Fetch and verify (particularly regular expression) search and replace
2563 ** strings and search type from the Replace dialog. If the strings are ok,
2564 ** save a copy in the search history, copy them in to "searchString",
2565 ** "replaceString', which are assumed to be at least SEARCHMAX in length,
2566 ** return search type in "searchType", and return TRUE as the function
2567 ** value. Otherwise, return FALSE.
2569 static int getReplaceDlogInfo(WindowInfo *window, int *direction,
2570 char *searchString, char *replaceString, int *searchType)
2572 char *replaceText, *replaceWithText;
2573 regexp *compiledRE = NULL;
2574 char *compileMsg;
2576 /* Get the search and replace strings, search type, and direction
2577 from the dialog */
2578 replaceText = XmTextGetString(window->replaceText);
2579 replaceWithText = XmTextGetString(window->replaceWithText);
2581 if(XmToggleButtonGetState(window->replaceRegexToggle)) {
2582 int regexDefault;
2583 if(XmToggleButtonGetState(window->replaceCaseToggle)) {
2584 *searchType = SEARCH_REGEX;
2585 regexDefault = REDFLT_STANDARD;
2586 } else {
2587 *searchType = SEARCH_REGEX_NOCASE;
2588 regexDefault = REDFLT_CASE_INSENSITIVE;
2590 /* If the search type is a regular expression, test compile it
2591 immediately and present error messages */
2592 compiledRE = CompileRE(replaceText, &compileMsg, regexDefault);
2593 if (compiledRE == NULL) {
2594 DialogF(DF_WARN, XtParent(window->replaceDlog), 1, "Search String",
2595 "Please respecify the search string:\n%s", "OK", compileMsg);
2596 XtFree(replaceText);
2597 XtFree(replaceWithText);
2598 return FALSE;
2600 free((char*)compiledRE);
2601 } else {
2602 if(XmToggleButtonGetState(window->replaceCaseToggle)) {
2603 if(XmToggleButtonGetState(window->replaceWordToggle))
2604 *searchType = SEARCH_CASE_SENSE_WORD;
2605 else
2606 *searchType = SEARCH_CASE_SENSE;
2607 } else {
2608 if(XmToggleButtonGetState(window->replaceWordToggle))
2609 *searchType = SEARCH_LITERAL_WORD;
2610 else
2611 *searchType = SEARCH_LITERAL;
2615 *direction = XmToggleButtonGetState(window->replaceRevToggle) ?
2616 SEARCH_BACKWARD : SEARCH_FORWARD;
2618 /* Return strings */
2619 if (strlen(replaceText) >= SEARCHMAX) {
2620 DialogF(DF_WARN, XtParent(window->replaceDlog), 1, "String too long",
2621 "Search string too long.", "OK");
2622 XtFree(replaceText);
2623 XtFree(replaceWithText);
2624 return FALSE;
2626 if (strlen(replaceWithText) >= SEARCHMAX) {
2627 DialogF(DF_WARN, XtParent(window->replaceDlog), 1, "String too long",
2628 "Replace string too long.", "OK");
2629 XtFree(replaceText);
2630 XtFree(replaceWithText);
2631 return FALSE;
2633 strcpy(searchString, replaceText);
2634 strcpy(replaceString, replaceWithText);
2635 XtFree(replaceText);
2636 XtFree(replaceWithText);
2637 return TRUE;
2641 ** Fetch and verify (particularly regular expression) search string,
2642 ** direction, and search type from the Find dialog. If the search string
2643 ** is ok, save a copy in the search history, copy it to "searchString",
2644 ** which is assumed to be at least SEARCHMAX in length, return search type
2645 ** in "searchType", and return TRUE as the function value. Otherwise,
2646 ** return FALSE.
2648 static int getFindDlogInfo(WindowInfo *window, int *direction,
2649 char *searchString, int *searchType)
2651 char *findText;
2652 regexp *compiledRE = NULL;
2653 char *compileMsg;
2655 /* Get the search string, search type, and direction from the dialog */
2656 findText = XmTextGetString(window->findText);
2658 if(XmToggleButtonGetState(window->findRegexToggle)) {
2659 int regexDefault;
2660 if(XmToggleButtonGetState(window->findCaseToggle)) {
2661 *searchType = SEARCH_REGEX;
2662 regexDefault = REDFLT_STANDARD;
2663 } else {
2664 *searchType = SEARCH_REGEX_NOCASE;
2665 regexDefault = REDFLT_CASE_INSENSITIVE;
2667 /* If the search type is a regular expression, test compile it
2668 immediately and present error messages */
2669 compiledRE = CompileRE(findText, &compileMsg, regexDefault);
2670 if (compiledRE == NULL) {
2671 DialogF(DF_WARN, XtParent(window->findDlog), 1, "Regex Error",
2672 "Please respecify the search string:\n%s", "OK", compileMsg);
2673 return FALSE;
2675 free((char *)compiledRE);
2676 } else {
2677 if(XmToggleButtonGetState(window->findCaseToggle)) {
2678 if(XmToggleButtonGetState(window->findWordToggle))
2679 *searchType = SEARCH_CASE_SENSE_WORD;
2680 else
2681 *searchType = SEARCH_CASE_SENSE;
2682 } else {
2683 if(XmToggleButtonGetState(window->findWordToggle))
2684 *searchType = SEARCH_LITERAL_WORD;
2685 else
2686 *searchType = SEARCH_LITERAL;
2690 *direction = XmToggleButtonGetState(window->findRevToggle) ?
2691 SEARCH_BACKWARD : SEARCH_FORWARD;
2693 if (isRegexType(*searchType)) {
2696 /* Return the search string */
2697 if (strlen(findText) >= SEARCHMAX) {
2698 DialogF(DF_WARN, XtParent(window->findDlog), 1, "String too long",
2699 "Search string too long.", "OK");
2700 XtFree(findText);
2701 return FALSE;
2703 strcpy(searchString, findText);
2704 XtFree(findText);
2705 return TRUE;
2708 int SearchAndSelectSame(WindowInfo *window, int direction, int searchWrap)
2710 if (NHist < 1) {
2711 XBell(TheDisplay, 0);
2712 return FALSE;
2715 return SearchAndSelect(window, direction, SearchHistory[historyIndex(1)],
2716 SearchTypeHistory[historyIndex(1)], searchWrap);
2720 ** Search for "searchString" in "window", and select the matching text in
2721 ** the window when found (or beep or put up a dialog if not found). Also
2722 ** adds the search string to the global search history.
2724 int SearchAndSelect(WindowInfo *window, int direction, const char *searchString,
2725 int searchType, int searchWrap)
2727 int startPos, endPos;
2728 int beginPos, cursorPos, selStart, selEnd;
2730 /* Save a copy of searchString in the search history */
2731 saveSearchHistory(searchString, NULL, searchType, FALSE);
2733 /* set the position to start the search so we don't find the same
2734 string that was found on the last search */
2735 if (searchMatchesSelection(window, searchString, searchType,
2736 &selStart, &selEnd, NULL, NULL)) {
2737 /* selection matches search string, start before or after sel. */
2738 if (direction == SEARCH_BACKWARD) {
2739 beginPos = selStart-1;
2740 } else {
2741 beginPos = selEnd;
2743 } else {
2744 selStart = -1; selEnd = -1;
2745 /* no selection, or no match, search relative cursor */
2746 cursorPos = TextGetCursorPos(window->lastFocus);
2747 if (direction == SEARCH_BACKWARD) {
2748 /* use the insert position - 1 for backward searches */
2749 beginPos = cursorPos-1;
2750 } else {
2751 /* use the insert position for forward searches */
2752 beginPos = cursorPos;
2756 /* when the i-search bar is active and search is repeated there
2757 (Return), the action "find" is called (not: "find_incremental").
2758 "find" calls this function SearchAndSelect.
2759 To keep track of the iSearchLastBeginPos correctly in the
2760 repeated i-search case it is necessary to call the following
2761 function here, otherwise there are no beeps on the repeated
2762 incremental search wraps. */
2763 iSearchRecordLastBeginPos(window, direction, beginPos);
2765 /* do the search. SearchWindow does appropriate dialogs and beeps */
2766 if (!SearchWindow(window, direction, searchString, searchType, searchWrap,
2767 beginPos, &startPos, &endPos, NULL, NULL))
2768 return FALSE;
2770 /* if the search matched an empty string (possible with regular exps)
2771 beginning at the start of the search, go to the next occurrence,
2772 otherwise repeated finds will get "stuck" at zero-length matches */
2773 if (direction==SEARCH_FORWARD && beginPos==startPos && beginPos==endPos)
2774 if (!SearchWindow(window, direction, searchString, searchType, searchWrap,
2775 beginPos+1, &startPos, &endPos, NULL, NULL))
2776 return FALSE;
2778 /* if matched text is already selected, just beep */
2779 if (selStart==startPos && selEnd==endPos) {
2780 XBell(TheDisplay, 0);
2781 return FALSE;
2784 /* select the text found string */
2785 BufSelect(window->buffer, startPos, endPos);
2786 MakeSelectionVisible(window, window->lastFocus);
2787 TextSetCursorPos(window->lastFocus, endPos);
2789 return TRUE;
2792 void SearchForSelected(WindowInfo *window, int direction, int searchType,
2793 int searchWrap, Time time)
2795 SearchSelectedCallData *callData = XtNew(SearchSelectedCallData);
2796 callData->direction = direction;
2797 callData->searchType = searchType;
2798 callData->searchWrap = searchWrap;
2799 XtGetSelectionValue(window->textArea, XA_PRIMARY, XA_STRING,
2800 (XtSelectionCallbackProc)selectedSearchCB, callData, time);
2803 static void selectedSearchCB(Widget w, XtPointer callData, Atom *selection,
2804 Atom *type, char *value, int *length, int *format)
2806 WindowInfo *window = WidgetToWindow(w);
2807 SearchSelectedCallData *callDataItems = (SearchSelectedCallData *)callData;
2808 int searchType;
2809 char searchString[SEARCHMAX+1];
2811 window = WidgetToWindow(w);
2813 /* skip if we can't get the selection data or it's too long */
2814 if (*type == XT_CONVERT_FAIL || value == NULL) {
2815 if (GetPrefSearchDlogs())
2816 DialogF(DF_WARN, window->shell, 1, "Wrong Selection",
2817 "Selection not appropriate for searching", "OK");
2818 else
2819 XBell(TheDisplay, 0);
2820 XtFree(callData);
2821 return;
2823 if (*length > SEARCHMAX) {
2824 if (GetPrefSearchDlogs())
2825 DialogF(DF_WARN, window->shell, 1, "Selection too long",
2826 "Selection too long", "OK");
2827 else
2828 XBell(TheDisplay, 0);
2829 XtFree(value);
2830 XtFree(callData);
2831 return;
2833 if (*length == 0) {
2834 XBell(TheDisplay, 0);
2835 XtFree(value);
2836 XtFree(callData);
2837 return;
2839 /* should be of type text??? */
2840 if (*format != 8) {
2841 fprintf(stderr, "NEdit: can't handle non 8-bit text\n");
2842 XBell(TheDisplay, 0);
2843 XtFree(value);
2844 XtFree(callData);
2845 return;
2847 /* make the selection the current search string */
2848 strncpy(searchString, value, *length);
2849 searchString[*length] = '\0';
2850 XtFree(value);
2852 /* Use the passed method for searching, unless it is regex, since this
2853 kind of search is by definition a literal search */
2854 searchType = callDataItems->searchType;
2855 if (searchType == SEARCH_REGEX )
2856 searchType = SEARCH_CASE_SENSE;
2857 else if (searchType == SEARCH_REGEX_NOCASE)
2858 searchType = SEARCH_LITERAL;
2860 /* search for it in the window */
2861 SearchAndSelect(window, callDataItems->direction, searchString,
2862 searchType, callDataItems->searchWrap);
2863 XtFree(callData);
2867 ** Pop up and clear the incremental search line and prepare to search.
2869 void BeginISearch(WindowInfo *window, int direction)
2871 window->iSearchStartPos = -1;
2872 XmTextSetString(window->iSearchText, "");
2873 XmToggleButtonSetState(window->iSearchRevToggle,
2874 direction == SEARCH_BACKWARD, FALSE);
2875 /* Note: in contrast to the replace and find dialogs, the regex and
2876 case toggles are not reset to their default state when the incremental
2877 search bar is redisplayed. I'm not sure whether this is the best
2878 choice. If not, an initToggleButtons() call should be inserted
2879 here. But in that case, it might be appropriate to have different
2880 default search modes for i-search and replace/find. */
2881 TempShowISearch(window, TRUE);
2882 XmProcessTraversal(window->iSearchText, XmTRAVERSE_CURRENT);
2886 ** Incremental searching is anchored at the position where the cursor
2887 ** was when the user began typing the search string. Call this routine
2888 ** to forget about this original anchor, and if the search bar is not
2889 ** permanently up, pop it down.
2891 void EndISearch(WindowInfo *window)
2893 /* Note: Please maintain this such that it can be freely peppered in
2894 mainline code, without callers having to worry about performance
2895 or visual glitches. */
2897 /* Forget the starting position used for the current run of searches */
2898 window->iSearchStartPos = -1;
2900 /* Mark the end of incremental search history overwriting */
2901 saveSearchHistory("", NULL, 0, FALSE);
2903 /* Pop down the search line (if it's not pegged up in Preferences) */
2904 TempShowISearch(window, FALSE);
2908 ** Reset window->iSearchLastBeginPos to the resulting initial
2909 ** search begin position for incremental searches.
2911 static void iSearchRecordLastBeginPos(WindowInfo *window, int direction,
2912 int initPos)
2914 window->iSearchLastBeginPos = initPos;
2915 if (direction == SEARCH_BACKWARD)
2916 window->iSearchLastBeginPos--;
2920 ** Search for "searchString" in "window", and select the matching text in
2921 ** the window when found (or beep or put up a dialog if not found). If
2922 ** "continued" is TRUE and a prior incremental search starting position is
2923 ** recorded, search from that original position, otherwise, search from the
2924 ** current cursor position.
2926 int SearchAndSelectIncremental(WindowInfo *window, int direction,
2927 const char *searchString, int searchType, int searchWrap, int continued)
2929 int beginPos, startPos, endPos;
2931 /* If there's a search in progress, start the search from the original
2932 starting position, otherwise search from the cursor position. */
2933 if (!continued || window->iSearchStartPos == -1) {
2934 window->iSearchStartPos = TextGetCursorPos(window->lastFocus);
2935 iSearchRecordLastBeginPos(window, direction, window->iSearchStartPos);
2937 beginPos = window->iSearchStartPos;
2939 /* If the search string is empty, beep eventually if text wrapped
2940 back to the initial position, re-init iSearchLastBeginPos,
2941 clear the selection, set the cursor back to what would be the
2942 beginning of the search, and return. */
2943 if(searchString[0] == 0) {
2944 int beepBeginPos = (direction == SEARCH_BACKWARD) ? beginPos-1:beginPos;
2945 iSearchTryBeepOnWrap(window, direction, beepBeginPos, beepBeginPos);
2946 iSearchRecordLastBeginPos(window, direction, window->iSearchStartPos);
2947 BufUnselect(window->buffer);
2948 TextSetCursorPos(window->lastFocus, beginPos);
2949 return TRUE;
2952 /* Save the string in the search history, unless we're cycling thru
2953 the search history itself, which can be detected by matching the
2954 search string with the search string of the current history index. */
2955 if(!(window->iSearchHistIndex > 1 && !strcmp(searchString,
2956 SearchHistory[historyIndex(window->iSearchHistIndex)]))) {
2957 saveSearchHistory(searchString, NULL, searchType, TRUE);
2958 /* Reset the incremental search history pointer to the beginning */
2959 window->iSearchHistIndex = 1;
2962 /* begin at insert position - 1 for backward searches */
2963 if (direction == SEARCH_BACKWARD)
2964 beginPos--;
2966 /* do the search. SearchWindow does appropriate dialogs and beeps */
2967 if (!SearchWindow(window, direction, searchString, searchType, searchWrap,
2968 beginPos, &startPos, &endPos, NULL, NULL))
2969 return FALSE;
2971 window->iSearchLastBeginPos = startPos;
2973 /* if the search matched an empty string (possible with regular exps)
2974 beginning at the start of the search, go to the next occurrence,
2975 otherwise repeated finds will get "stuck" at zero-length matches */
2976 if (direction==SEARCH_FORWARD && beginPos==startPos && beginPos==endPos)
2977 if (!SearchWindow(window, direction, searchString, searchType, searchWrap,
2978 beginPos+1, &startPos, &endPos, NULL, NULL))
2979 return FALSE;
2981 window->iSearchLastBeginPos = startPos;
2983 /* select the text found string */
2984 BufSelect(window->buffer, startPos, endPos);
2985 MakeSelectionVisible(window, window->lastFocus);
2986 TextSetCursorPos(window->lastFocus, endPos);
2988 return TRUE;
2992 ** Attach callbacks to the incremental search bar widgets. This also fudges
2993 ** up the translations on the text widget so Shift+Return will call the
2994 ** activate callback (along with Return and Ctrl+Return). It does this
2995 ** because incremental search uses the activate callback from the text
2996 ** widget to detect when the user has pressed Return to search for the next
2997 ** occurrence of the search string, and Shift+Return, which is the natural
2998 ** command for a reverse search does not naturally trigger this callback.
3000 void SetISearchTextCallbacks(WindowInfo *window)
3002 static XtTranslations table = NULL;
3003 static char *translations = "Shift<KeyPress>Return: activate()\n";
3005 if (table == NULL)
3006 table = XtParseTranslationTable(translations);
3007 XtOverrideTranslations(window->iSearchText, table);
3009 XtAddCallback(window->iSearchText, XmNactivateCallback,
3010 (XtCallbackProc)iSearchTextActivateCB, window);
3011 XtAddCallback(window->iSearchText, XmNvalueChangedCallback,
3012 (XtCallbackProc)iSearchTextValueChangedCB, window);
3013 XtAddEventHandler(window->iSearchText, KeyPressMask, False,
3014 (XtEventHandler)iSearchTextKeyEH, window);
3016 /* Attach callbacks to deal with the optional sticky case sensitivity
3017 behaviour. Do this before installing the search callbacks to make
3018 sure that the proper search parameters are taken into account. */
3019 XtAddCallback(window->iSearchCaseToggle, XmNvalueChangedCallback,
3020 (XtCallbackProc)iSearchCaseToggleCB, window);
3021 XtAddCallback(window->iSearchRegexToggle, XmNvalueChangedCallback,
3022 (XtCallbackProc)iSearchRegExpToggleCB, window);
3024 /* When search parameters (direction or search type), redo the search */
3025 XtAddCallback(window->iSearchCaseToggle, XmNvalueChangedCallback,
3026 (XtCallbackProc)iSearchTextValueChangedCB, window);
3027 XtAddCallback(window->iSearchRegexToggle, XmNvalueChangedCallback,
3028 (XtCallbackProc)iSearchTextValueChangedCB, window);
3029 XtAddCallback(window->iSearchRevToggle, XmNvalueChangedCallback,
3030 (XtCallbackProc)iSearchTextValueChangedCB, window);
3034 ** User pressed return in the incremental search bar. Do a new search with
3035 ** the search string displayed. The direction of the search is toggled if
3036 ** the Ctrl key or the Shift key is pressed when the text field is activated.
3038 static void iSearchTextActivateCB(Widget w, WindowInfo *window,
3039 XmAnyCallbackStruct *callData)
3041 char *params[4];
3042 char *searchString;
3043 int searchType, direction;
3045 window = WidgetToWindow(w);
3047 /* Fetch the string, search type and direction from the incremental
3048 search bar widgets at the top of the window */
3049 searchString = XmTextGetString(window->iSearchText);
3050 if(XmToggleButtonGetState(window->iSearchCaseToggle)) {
3051 if(XmToggleButtonGetState(window->iSearchRegexToggle))
3052 searchType = SEARCH_REGEX;
3053 else
3054 searchType = SEARCH_CASE_SENSE;
3055 } else {
3056 if(XmToggleButtonGetState(window->iSearchRegexToggle))
3057 searchType = SEARCH_REGEX_NOCASE;
3058 else
3059 searchType = SEARCH_LITERAL;
3061 direction = XmToggleButtonGetState(window->iSearchRevToggle) ?
3062 SEARCH_BACKWARD : SEARCH_FORWARD;
3064 /* Reverse the search direction if the Ctrl or Shift key was pressed */
3065 if (callData->event->xbutton.state & (ShiftMask | ControlMask))
3066 direction = direction == SEARCH_FORWARD ?
3067 SEARCH_BACKWARD : SEARCH_FORWARD;
3069 /* find the text and mark it */
3070 params[0] = searchString;
3071 params[1] = directionArg(direction);
3072 params[2] = searchTypeArg(searchType);
3073 params[3] = searchWrapArg(GetPrefSearchWraps());
3074 XtCallActionProc(window->lastFocus, "find", callData->event, params, 4);
3075 XtFree(searchString);
3079 ** Called when user types in the incremental search line. Redoes the
3080 ** search for the new search string.
3082 static void iSearchTextValueChangedCB(Widget w, WindowInfo *window,
3083 XmAnyCallbackStruct *callData)
3085 char *params[5];
3086 char *searchString;
3087 int searchType, direction, nParams;
3089 window = WidgetToWindow(w);
3091 /* Fetch the string, search type and direction from the incremental
3092 search bar widgets at the top of the window */
3093 searchString = XmTextGetString(window->iSearchText);
3094 if(XmToggleButtonGetState(window->iSearchCaseToggle)) {
3095 if(XmToggleButtonGetState(window->iSearchRegexToggle))
3096 searchType = SEARCH_REGEX;
3097 else
3098 searchType = SEARCH_CASE_SENSE;
3099 } else {
3100 if(XmToggleButtonGetState(window->iSearchRegexToggle))
3101 searchType = SEARCH_REGEX_NOCASE;
3102 else
3103 searchType = SEARCH_LITERAL;
3105 direction = XmToggleButtonGetState(window->iSearchRevToggle) ?
3106 SEARCH_BACKWARD : SEARCH_FORWARD;
3108 /* If the search type is a regular expression, test compile it. If it
3109 fails, silently skip it. (This allows users to compose the expression
3110 in peace when they have unfinished syntax, but still get beeps when
3111 correct syntax doesn't match) */
3112 if (isRegexType(searchType)) {
3113 regexp *compiledRE = NULL;
3114 char *compileMsg;
3115 compiledRE = CompileRE(searchString, &compileMsg,
3116 defaultRegexFlags(searchType));
3117 if (compiledRE == NULL) {
3118 XtFree(searchString);
3119 return;
3121 free((char *)compiledRE);
3124 /* Call the incremental search action proc to do the searching and
3125 selecting (this allows it to be recorded for learn/replay). If
3126 there's an incremental search already in progress, mark the operation
3127 as "continued" so the search routine knows to re-start the search
3128 from the original starting position */
3129 nParams = 0;
3130 params[nParams++] = searchString;
3131 params[nParams++] = directionArg(direction);
3132 params[nParams++] = searchTypeArg(searchType);
3133 params[nParams++] = searchWrapArg(GetPrefSearchWraps());
3134 if (window->iSearchStartPos != -1)
3135 params[nParams++] = "continued";
3136 XtCallActionProc(window->lastFocus, "find_incremental",
3137 callData->event, params, nParams);
3138 XtFree(searchString);
3142 ** Process arrow keys for history recall, and escape key for leaving
3143 ** incremental search bar.
3145 static void iSearchTextKeyEH(Widget w, WindowInfo *window,
3146 XKeyEvent *event, Boolean *continueDispatch)
3148 KeySym keysym = XLookupKeysym(event, 0);
3149 int index;
3150 char *searchStr;
3151 int searchType;
3153 /* only process up and down arrow keys */
3154 if (keysym != XK_Up && keysym != XK_Down && keysym != XK_Escape) {
3155 *continueDispatch = TRUE;
3156 return;
3159 window = WidgetToWindow(w);
3160 index = window->iSearchHistIndex;
3161 *continueDispatch = FALSE;
3163 /* allow escape key to cancel search */
3164 if (keysym == XK_Escape) {
3165 XmProcessTraversal(window->lastFocus, XmTRAVERSE_CURRENT);
3166 EndISearch(window);
3167 return;
3170 /* increment or decrement the index depending on which arrow was pressed */
3171 index += (keysym == XK_Up) ? 1 : -1;
3173 /* if the index is out of range, beep and return */
3174 if (index != 0 && historyIndex(index) == -1) {
3175 XBell(TheDisplay, 0);
3176 return;
3179 /* determine the strings and button settings to use */
3180 if (index == 0) {
3181 searchStr = "";
3182 searchType = GetPrefSearch();
3183 } else {
3184 searchStr = SearchHistory[historyIndex(index)];
3185 searchType = SearchTypeHistory[historyIndex(index)];
3188 /* Set the info used in the value changed callback before calling
3189 XmTextSetString(). */
3190 window->iSearchHistIndex = index;
3191 initToggleButtons(searchType, window->iSearchRegexToggle,
3192 window->iSearchCaseToggle, NULL,
3193 &window->iSearchLastLiteralCase,
3194 &window->iSearchLastRegexCase);
3196 /* Beware the value changed callback is processed as part of this call */
3197 XmTextSetString(window->iSearchText, searchStr);
3198 XmTextSetInsertionPosition(window->iSearchText,
3199 XmTextGetLastPosition(window->iSearchText));
3203 ** Check the character before the insertion cursor of textW and flash
3204 ** matching parenthesis, brackets, or braces, by temporarily highlighting
3205 ** the matching character (a timer procedure is scheduled for removing the
3206 ** highlights)
3208 void FlashMatching(WindowInfo *window, Widget textW)
3210 char c;
3211 void *style;
3212 int pos, matchIndex;
3213 int startPos, endPos, searchPos, matchPos;
3214 int constrain;
3216 /* if a marker is already drawn, erase it and cancel the timeout */
3217 if (window->flashTimeoutID != 0) {
3218 eraseFlash(window);
3219 XtRemoveTimeOut(window->flashTimeoutID);
3220 window->flashTimeoutID = 0;
3223 /* no flashing required */
3224 if (window->showMatchingStyle == NO_FLASH) {
3225 return;
3228 /* don't flash matching characters if there's a selection */
3229 if (window->buffer->primary.selected)
3230 return;
3232 /* get the character to match and the position to start from */
3233 pos = TextGetCursorPos(textW) - 1;
3234 if (pos < 0)
3235 return;
3236 c = BufGetCharacter(window->buffer, pos);
3237 style = GetHighlightInfo(window, pos);
3239 /* is the character one we want to flash? */
3240 for (matchIndex = 0; matchIndex<N_FLASH_CHARS; matchIndex++) {
3241 if (MatchingChars[matchIndex].c == c)
3242 break;
3244 if (matchIndex == N_FLASH_CHARS)
3245 return;
3247 /* constrain the search to visible text only when in single-pane mode
3248 AND using delimiter flashing (otherwise search the whole buffer) */
3249 constrain = ((window->nPanes == 0) &&
3250 (window->showMatchingStyle == FLASH_DELIMIT));
3252 if (MatchingChars[matchIndex].direction == SEARCH_BACKWARD) {
3253 startPos = constrain ? TextFirstVisiblePos(textW) : 0;
3254 endPos = pos;
3255 searchPos = endPos;
3256 } else {
3257 startPos = pos;
3258 endPos = constrain ? TextLastVisiblePos(textW) :
3259 window->buffer->length;
3260 searchPos = startPos;
3263 /* do the search */
3264 if (!findMatchingChar(window, c, style, searchPos, startPos, endPos,
3265 &matchPos))
3266 return;
3268 if (window->showMatchingStyle == FLASH_DELIMIT) {
3269 /* Highlight either the matching character ... */
3270 BufHighlight(window->buffer, matchPos, matchPos+1);
3271 } else {
3272 /* ... or the whole range. */
3273 if (MatchingChars[matchIndex].direction == SEARCH_BACKWARD) {
3274 BufHighlight(window->buffer, matchPos, pos+1);
3275 } else {
3276 BufHighlight(window->buffer, matchPos+1, pos);
3280 /* Set up a timer to erase the box after 1.5 seconds */
3281 window->flashTimeoutID = XtAppAddTimeOut(
3282 XtWidgetToApplicationContext(window->shell), 1500,
3283 flashTimeoutProc, window);
3284 window->flashPos = matchPos;
3287 void SelectToMatchingCharacter(WindowInfo *window)
3289 int selStart, selEnd;
3290 int startPos, endPos, matchPos;
3291 textBuffer *buf = window->buffer;
3293 /* get the character to match and its position from the selection, or
3294 the character before the insert point if nothing is selected.
3295 Give up if too many characters are selected */
3296 if (!GetSimpleSelection(buf, &selStart, &selEnd)) {
3297 selEnd = TextGetCursorPos(window->lastFocus);
3298 if (window->overstrike)
3299 selEnd += 1;
3300 selStart = selEnd - 1;
3301 if (selStart < 0) {
3302 XBell(TheDisplay, 0);
3303 return;
3306 if ((selEnd - selStart) != 1) {
3307 XBell(TheDisplay, 0);
3308 return;
3311 /* Search for it in the buffer */
3312 if (!findMatchingChar(window, BufGetCharacter(buf, selStart),
3313 GetHighlightInfo(window, selStart), selStart, 0, buf->length, &matchPos)) {
3314 XBell(TheDisplay, 0);
3315 return;
3317 startPos = (matchPos > selStart) ? selStart : matchPos;
3318 endPos = (matchPos > selStart) ? matchPos : selStart;
3320 /* select the text between the matching characters */
3321 BufSelect(buf, startPos, endPos+1);
3324 void GotoMatchingCharacter(WindowInfo *window)
3326 int selStart, selEnd;
3327 int matchPos;
3328 textBuffer *buf = window->buffer;
3330 /* get the character to match and its position from the selection, or
3331 the character before the insert point if nothing is selected.
3332 Give up if too many characters are selected */
3333 if (!GetSimpleSelection(buf, &selStart, &selEnd)) {
3334 selEnd = TextGetCursorPos(window->lastFocus);
3335 if (window->overstrike)
3336 selEnd += 1;
3337 selStart = selEnd - 1;
3338 if (selStart < 0) {
3339 XBell(TheDisplay, 0);
3340 return;
3343 if ((selEnd - selStart) != 1) {
3344 XBell(TheDisplay, 0);
3345 return;
3348 /* Search for it in the buffer */
3349 if (!findMatchingChar(window, BufGetCharacter(buf, selStart),
3350 GetHighlightInfo(window, selStart), selStart, 0,
3351 buf->length, &matchPos)) {
3352 XBell(TheDisplay, 0);
3353 return;
3356 /* temporarily shut off autoShowInsertPos before setting the cursor
3357 position so MakeSelectionVisible gets a chance to place the cursor
3358 string at a pleasing position on the screen (otherwise, the cursor would
3359 be automatically scrolled on screen and MakeSelectionVisible would do
3360 nothing) */
3361 XtVaSetValues(window->lastFocus, textNautoShowInsertPos, False, NULL);
3362 TextSetCursorPos(window->lastFocus, matchPos+1);
3363 MakeSelectionVisible(window, window->lastFocus);
3364 XtVaSetValues(window->lastFocus, textNautoShowInsertPos, True, NULL);
3367 static int findMatchingChar(WindowInfo *window, char toMatch,
3368 void* styleToMatch, int charPos, int startLimit, int endLimit,
3369 int *matchPos)
3371 int nestDepth, matchIndex, direction, beginPos, pos;
3372 char matchChar, c;
3373 void *style = NULL;
3374 textBuffer *buf = window->buffer;
3375 int matchSyntaxBased = window->matchSyntaxBased;
3377 /* If we don't match syntax based, fake a matching style. */
3378 if (!matchSyntaxBased) style = styleToMatch;
3380 /* Look up the matching character and match direction */
3381 for (matchIndex = 0; matchIndex<N_MATCH_CHARS; matchIndex++) {
3382 if (MatchingChars[matchIndex].c == toMatch)
3383 break;
3385 if (matchIndex == N_MATCH_CHARS)
3386 return FALSE;
3387 matchChar = MatchingChars[matchIndex].match;
3388 direction = MatchingChars[matchIndex].direction;
3390 /* find it in the buffer */
3391 beginPos = (direction==SEARCH_FORWARD) ? charPos+1 : charPos-1;
3392 nestDepth = 1;
3393 if (direction == SEARCH_FORWARD) {
3394 for (pos=beginPos; pos<endLimit; pos++) {
3395 c=BufGetCharacter(buf, pos);
3396 if (c == matchChar) {
3397 if (matchSyntaxBased) style = GetHighlightInfo(window, pos);
3398 if (style == styleToMatch) {
3399 nestDepth--;
3400 if (nestDepth == 0) {
3401 *matchPos = pos;
3402 return TRUE;
3405 } else if (c == toMatch) {
3406 if (matchSyntaxBased) style = GetHighlightInfo(window, pos);
3407 if (style == styleToMatch)
3408 nestDepth++;
3411 } else { /* SEARCH_BACKWARD */
3412 for (pos=beginPos; pos>=startLimit; pos--) {
3413 c=BufGetCharacter(buf, pos);
3414 if (c == matchChar) {
3415 if (matchSyntaxBased) style = GetHighlightInfo(window, pos);
3416 if (style == styleToMatch) {
3417 nestDepth--;
3418 if (nestDepth == 0) {
3419 *matchPos = pos;
3420 return TRUE;
3423 } else if (c == toMatch) {
3424 if (matchSyntaxBased) style = GetHighlightInfo(window, pos);
3425 if (style == styleToMatch)
3426 nestDepth++;
3430 return FALSE;
3434 ** Xt timer procedure for erasing the matching parenthesis marker.
3436 static void flashTimeoutProc(XtPointer clientData, XtIntervalId *id)
3438 eraseFlash((WindowInfo *)clientData);
3439 ((WindowInfo *)clientData)->flashTimeoutID = 0;
3443 ** Erase the marker drawn on a matching parenthesis bracket or brace
3444 ** character.
3446 static void eraseFlash(WindowInfo *window)
3448 BufUnhighlight(window->buffer);
3452 ** Search and replace using previously entered search strings (from dialog
3453 ** or selection).
3455 int ReplaceSame(WindowInfo *window, int direction, int searchWrap)
3457 if (NHist < 1) {
3458 XBell(TheDisplay, 0);
3459 return FALSE;
3462 return SearchAndReplace(window, direction, SearchHistory[historyIndex(1)],
3463 ReplaceHistory[historyIndex(1)],
3464 SearchTypeHistory[historyIndex(1)], searchWrap);
3468 ** Search and replace using previously entered search strings (from dialog
3469 ** or selection).
3471 int ReplaceFindSame(WindowInfo *window, int direction, int searchWrap)
3473 if (NHist < 1) {
3474 XBell(TheDisplay, 0);
3475 return FALSE;
3478 return ReplaceAndSearch(window, direction, SearchHistory[historyIndex(1)],
3479 ReplaceHistory[historyIndex(1)],
3480 SearchTypeHistory[historyIndex(1)], searchWrap);
3484 ** Replace selection with "replaceString" and search for string "searchString" in window "window",
3485 ** using algorithm "searchType" and direction "direction"
3487 int ReplaceAndSearch(WindowInfo *window, int direction, const char *searchString,
3488 const char *replaceString, int searchType, int searchWrap)
3490 int startPos = 0, endPos = 0, replaceLen = 0;
3491 int searchExtentBW, searchExtentFW;
3492 int replaced;
3494 /* Save a copy of search and replace strings in the search history */
3495 saveSearchHistory(searchString, replaceString, searchType, FALSE);
3497 replaced = 0;
3499 /* Replace the selected text only if it matches the search string */
3500 if (searchMatchesSelection(window, searchString, searchType,
3501 &startPos, &endPos, &searchExtentBW,
3502 &searchExtentFW)) {
3503 /* replace the text */
3504 if (isRegexType(searchType)) {
3505 char replaceResult[SEARCHMAX+1], *foundString;
3506 foundString = BufGetRange(window->buffer, searchExtentBW,
3507 searchExtentFW+1);
3508 replaceUsingRE(searchString, replaceString, foundString,
3509 startPos-searchExtentBW,
3510 replaceResult, SEARCHMAX, startPos == 0 ? '\0' :
3511 BufGetCharacter(window->buffer, startPos-1),
3512 GetWindowDelimiters(window), defaultRegexFlags(searchType));
3513 XtFree(foundString);
3514 BufReplace(window->buffer, startPos, endPos, replaceResult);
3515 replaceLen = strlen(replaceResult);
3516 } else {
3517 BufReplace(window->buffer, startPos, endPos, replaceString);
3518 replaceLen = strlen(replaceString);
3521 /* Position the cursor so the next search will work correctly based */
3522 /* on the direction of the search */
3523 TextSetCursorPos(window->lastFocus, startPos +
3524 ((direction == SEARCH_FORWARD) ? replaceLen : 0));
3525 replaced = 1;
3528 /* do the search; beeps/dialogs are taken care of */
3529 SearchAndSelect(window, direction, searchString, searchType, searchWrap);
3531 return replaced;
3535 ** Search for string "searchString" in window "window", using algorithm
3536 ** "searchType" and direction "direction", and replace it with "replaceString"
3537 ** Also adds the search and replace strings to the global search history.
3539 int SearchAndReplace(WindowInfo *window, int direction, const char *searchString,
3540 const char *replaceString, int searchType, int searchWrap)
3542 int startPos, endPos, replaceLen, searchExtentBW, searchExtentFW;
3543 int found;
3544 int beginPos, cursorPos;
3546 /* Save a copy of search and replace strings in the search history */
3547 saveSearchHistory(searchString, replaceString, searchType, FALSE);
3549 /* If the text selected in the window matches the search string, */
3550 /* the user is probably using search then replace method, so */
3551 /* replace the selected text regardless of where the cursor is. */
3552 /* Otherwise, search for the string. */
3553 if (!searchMatchesSelection(window, searchString, searchType,
3554 &startPos, &endPos, &searchExtentBW, &searchExtentFW)) {
3555 /* get the position to start the search */
3556 cursorPos = TextGetCursorPos(window->lastFocus);
3557 if (direction == SEARCH_BACKWARD) {
3558 /* use the insert position - 1 for backward searches */
3559 beginPos = cursorPos-1;
3560 } else {
3561 /* use the insert position for forward searches */
3562 beginPos = cursorPos;
3564 /* do the search */
3565 found = SearchWindow(window, direction, searchString, searchType, searchWrap,
3566 beginPos, &startPos, &endPos, &searchExtentBW, &searchExtentFW);
3567 if (!found)
3568 return FALSE;
3571 /* replace the text */
3572 if (isRegexType(searchType)) {
3573 char replaceResult[SEARCHMAX], *foundString;
3574 foundString = BufGetRange(window->buffer, searchExtentBW, searchExtentFW+1);
3575 replaceUsingRE(searchString, replaceString, foundString,
3576 startPos - searchExtentBW,
3577 replaceResult, SEARCHMAX, startPos == 0 ? '\0' :
3578 BufGetCharacter(window->buffer, startPos-1),
3579 GetWindowDelimiters(window), defaultRegexFlags(searchType));
3580 XtFree(foundString);
3581 BufReplace(window->buffer, startPos, endPos, replaceResult);
3582 replaceLen = strlen(replaceResult);
3583 } else {
3584 BufReplace(window->buffer, startPos, endPos, replaceString);
3585 replaceLen = strlen(replaceString);
3588 /* after successfully completing a replace, selected text attracts
3589 attention away from the area of the replacement, particularly
3590 when the selection represents a previous search. so deselect */
3591 BufUnselect(window->buffer);
3593 /* temporarily shut off autoShowInsertPos before setting the cursor
3594 position so MakeSelectionVisible gets a chance to place the replaced
3595 string at a pleasing position on the screen (otherwise, the cursor would
3596 be automatically scrolled on screen and MakeSelectionVisible would do
3597 nothing) */
3598 XtVaSetValues(window->lastFocus, textNautoShowInsertPos, False, NULL);
3599 TextSetCursorPos(window->lastFocus, startPos +
3600 ((direction == SEARCH_FORWARD) ? replaceLen : 0));
3601 MakeSelectionVisible(window, window->lastFocus);
3602 XtVaSetValues(window->lastFocus, textNautoShowInsertPos, True, NULL);
3604 return TRUE;
3608 ** Replace all occurences of "searchString" in "window" with "replaceString"
3609 ** within the current primary selection in "window". Also adds the search and
3610 ** replace strings to the global search history.
3612 int ReplaceInSelection(WindowInfo *window, const char *searchString,
3613 const char *replaceString, int searchType)
3615 int selStart, selEnd, beginPos, startPos, endPos, realOffset, replaceLen;
3616 int found, anyFound, isRect, rectStart, rectEnd, lineStart, cursorPos;
3617 int extentBW, extentFW;
3618 char *fileString;
3619 textBuffer *tempBuf;
3621 /* save a copy of search and replace strings in the search history */
3622 saveSearchHistory(searchString, replaceString, searchType, FALSE);
3624 /* find out where the selection is */
3625 if (!BufGetSelectionPos(window->buffer, &selStart, &selEnd, &isRect,
3626 &rectStart, &rectEnd))
3627 return FALSE;
3629 /* get the selected text */
3630 if (isRect) {
3631 selStart = BufStartOfLine(window->buffer, selStart);
3632 selEnd = BufEndOfLine(window->buffer, selEnd);
3633 fileString = BufGetRange(window->buffer, selStart, selEnd);
3634 } else
3635 fileString = BufGetSelectionText(window->buffer);
3637 /* create a temporary buffer in which to do the replacements to hide the
3638 intermediate steps from the display routines, and so everything can
3639 be undone in a single operation */
3640 tempBuf = BufCreate();
3641 BufSetAll(tempBuf, fileString);
3643 /* search the string and do the replacements in the temporary buffer */
3644 replaceLen = strlen(replaceString);
3645 found = TRUE;
3646 anyFound = FALSE;
3647 beginPos = 0;
3648 cursorPos = 0;
3649 realOffset = 0;
3650 while (found) {
3651 found = SearchString(fileString, searchString, SEARCH_FORWARD,
3652 searchType, FALSE, beginPos, &startPos, &endPos, &extentBW,
3653 &extentFW, GetWindowDelimiters(window));
3654 if (!found)
3655 break;
3656 /* if the selection is rectangular, verify that the found
3657 string is in the rectangle */
3658 if (isRect) {
3659 lineStart = BufStartOfLine(window->buffer, selStart+startPos);
3660 if (BufCountDispChars(window->buffer, lineStart, selStart+startPos) <
3661 rectStart || BufCountDispChars(window->buffer, lineStart,
3662 selStart+endPos) > rectEnd) {
3663 if (fileString[endPos] == '\0')
3664 break;
3665 /* If the match starts before the left boundary of the
3666 selection, and extends past it, we should not continue
3667 search after the end of the (false) match, because we
3668 could miss a valid match starting between the left boundary
3669 and the end of the false match. */
3670 if (BufCountDispChars(window->buffer, lineStart,
3671 selStart+startPos) < rectStart &&
3672 BufCountDispChars(window->buffer, lineStart,
3673 selStart+endPos) > rectStart)
3674 beginPos += 1;
3675 else
3676 beginPos = (startPos == endPos) ? endPos+1 : endPos;
3677 continue;
3680 /* Make sure the match did not start past the end (regular expressions
3681 can consider the artificial end of the range as the end of a line,
3682 and match a fictional whole line beginning there) */
3683 if (startPos == selEnd - selStart) {
3684 found = False;
3685 break;
3687 /* replace the string and compensate for length change */
3688 if (isRegexType(searchType)) {
3689 char replaceResult[SEARCHMAX], *foundString;
3690 foundString = BufGetRange(tempBuf, extentBW+realOffset,
3691 extentFW+realOffset+1);
3692 replaceUsingRE(searchString, replaceString, foundString,
3693 startPos-extentBW,
3694 replaceResult, SEARCHMAX, startPos+realOffset == 0 ? '\0' :
3695 BufGetCharacter(tempBuf, startPos+realOffset-1),
3696 GetWindowDelimiters(window), defaultRegexFlags(searchType));
3697 XtFree(foundString);
3698 BufReplace(tempBuf, startPos+realOffset, endPos+realOffset,
3699 replaceResult);
3700 replaceLen = strlen(replaceResult);
3701 } else
3702 BufReplace(tempBuf, startPos+realOffset, endPos+realOffset,
3703 replaceString);
3704 realOffset += replaceLen - (endPos - startPos);
3705 /* start again after match unless match was empty, then endPos+1 */
3706 beginPos = (startPos == endPos) ? endPos+1 : endPos;
3707 cursorPos = endPos;
3708 anyFound = TRUE;
3709 if (fileString[endPos] == '\0')
3710 break;
3712 XtFree(fileString);
3714 /* if nothing was found, tell user and return */
3715 if (!anyFound) {
3716 if (GetPrefSearchDlogs()) {
3717 /* Avoid bug in Motif 1.1 by putting away search dialog
3718 before DialogF */
3719 if (window->findDlog && XtIsManaged(window->findDlog) &&
3720 !XmToggleButtonGetState(window->findKeepBtn))
3721 XtUnmanageChild(window->findDlog);
3722 if (window->replaceDlog && XtIsManaged(window->replaceDlog) &&
3723 !XmToggleButtonGetState(window->replaceKeepBtn))
3724 unmanageReplaceDialogs(window);
3725 DialogF(DF_INF, window->shell, 1, "String not found",
3726 "String was not found", "OK");
3727 } else
3728 XBell(TheDisplay, 0);
3729 BufFree(tempBuf);
3730 return FALSE;
3733 /* replace the selected range in the real buffer */
3734 fileString = BufGetAll(tempBuf);
3735 BufFree(tempBuf);
3736 BufReplace(window->buffer, selStart, selEnd, fileString);
3737 XtFree(fileString);
3739 /* set the insert point at the end of the last replacement */
3740 TextSetCursorPos(window->lastFocus, selStart + cursorPos + realOffset);
3742 /* leave non-rectangular selections selected (rect. ones after replacement
3743 are less useful since left/right positions are randomly adjusted) */
3744 if (!isRect)
3745 BufSelect(window->buffer, selStart, selEnd + realOffset);
3747 return TRUE;
3751 ** Replace all occurences of "searchString" in "window" with "replaceString".
3752 ** Also adds the search and replace strings to the global search history.
3754 int ReplaceAll(WindowInfo *window, const char *searchString,
3755 const char *replaceString, int searchType)
3757 char *fileString, *newFileString;
3758 int copyStart, copyEnd, replacementLen;
3760 /* reject empty string */
3761 if (*searchString == '\0')
3762 return FALSE;
3764 /* save a copy of search and replace strings in the search history */
3765 saveSearchHistory(searchString, replaceString, searchType, FALSE);
3767 /* get the entire text buffer from the text area widget */
3768 fileString = BufGetAll(window->buffer);
3770 newFileString = ReplaceAllInString(fileString, searchString, replaceString,
3771 searchType, &copyStart, &copyEnd, &replacementLen,
3772 GetWindowDelimiters(window));
3773 XtFree(fileString);
3775 if (newFileString == NULL) {
3776 if (window->multiFileBusy) {
3777 window->replaceFailed = TRUE; /* only needed during multi-file
3778 replacements */
3779 } else if (GetPrefSearchDlogs()) {
3780 if (window->findDlog && XtIsManaged(window->findDlog) &&
3781 !XmToggleButtonGetState(window->findKeepBtn))
3782 XtUnmanageChild(window->findDlog);
3783 if (window->replaceDlog && XtIsManaged(window->replaceDlog) &&
3784 !XmToggleButtonGetState(window->replaceKeepBtn))
3785 unmanageReplaceDialogs(window);
3786 DialogF(DF_INF, window->shell, 1, "String not found",
3787 "String was not found", "OK");
3788 } else
3789 XBell(TheDisplay, 0);
3790 return FALSE;
3793 /* replace the contents of the text widget with the substituted text */
3794 BufReplace(window->buffer, copyStart, copyEnd, newFileString);
3796 /* Move the cursor to the end of the last replacement */
3797 TextSetCursorPos(window->lastFocus, copyStart + replacementLen);
3799 XtFree(newFileString);
3800 return TRUE;
3804 ** Replace all occurences of "searchString" in "inString" with "replaceString"
3805 ** and return an allocated string covering the range between the start of the
3806 ** first replacement (returned in "copyStart", and the end of the last
3807 ** replacement (returned in "copyEnd")
3809 char *ReplaceAllInString(char *inString, const char *searchString,
3810 const char *replaceString, int searchType, int *copyStart,
3811 int *copyEnd, int *replacementLength, const char *delimiters)
3813 int beginPos, startPos, endPos, lastEndPos;
3814 int found, nFound, removeLen, replaceLen, copyLen, addLen;
3815 char *outString, *fillPtr;
3816 int searchExtentBW, searchExtentFW;
3818 /* reject empty string */
3819 if (*searchString == '\0')
3820 return NULL;
3822 /* rehearse the search first to determine the size of the buffer needed
3823 to hold the substituted text. No substitution done here yet */
3824 replaceLen = strlen(replaceString);
3825 found = TRUE;
3826 nFound = 0;
3827 removeLen = 0;
3828 addLen = 0;
3829 beginPos = 0;
3830 *copyStart = -1;
3831 while (found) {
3832 found = SearchString(inString, searchString, SEARCH_FORWARD, searchType,
3833 FALSE, beginPos, &startPos, &endPos, &searchExtentBW,
3834 &searchExtentFW, delimiters);
3835 if (found) {
3836 if (*copyStart < 0)
3837 *copyStart = startPos;
3838 *copyEnd = endPos;
3839 /* start next after match unless match was empty, then endPos+1 */
3840 beginPos = (startPos == endPos) ? endPos+1 : endPos;
3841 nFound++;
3842 removeLen += endPos - startPos;
3843 if (isRegexType(searchType)) {
3844 char replaceResult[SEARCHMAX];
3845 replaceUsingRE(searchString, replaceString, &inString[searchExtentBW],
3846 startPos-searchExtentBW,
3847 replaceResult, SEARCHMAX, startPos == 0 ? '\0' :
3848 inString[startPos-1], delimiters,
3849 defaultRegexFlags(searchType));
3850 addLen += strlen(replaceResult);
3851 } else
3852 addLen += replaceLen;
3853 if (inString[endPos] == '\0')
3854 break;
3857 if (nFound == 0)
3858 return NULL;
3860 /* Allocate a new buffer to hold all of the new text between the first
3861 and last substitutions */
3862 copyLen = *copyEnd - *copyStart;
3863 outString = XtMalloc(copyLen - removeLen + addLen + 1);
3865 /* Scan through the text buffer again, substituting the replace string
3866 and copying the part between replaced text to the new buffer */
3867 found = TRUE;
3868 beginPos = 0;
3869 lastEndPos = 0;
3870 fillPtr = outString;
3871 while (found) {
3872 found = SearchString(inString, searchString, SEARCH_FORWARD, searchType,
3873 FALSE, beginPos, &startPos, &endPos, &searchExtentBW,
3874 &searchExtentFW, delimiters);
3875 if (found) {
3876 if (beginPos != 0) {
3877 memcpy(fillPtr, &inString[lastEndPos], startPos - lastEndPos);
3878 fillPtr += startPos - lastEndPos;
3880 if (isRegexType(searchType)) {
3881 char replaceResult[SEARCHMAX];
3882 replaceUsingRE(searchString, replaceString, &inString[searchExtentBW],
3883 startPos-searchExtentBW,
3884 replaceResult, SEARCHMAX, startPos == 0 ? '\0' :
3885 inString[startPos-1], delimiters,
3886 defaultRegexFlags(searchType));
3887 replaceLen = strlen(replaceResult);
3888 memcpy(fillPtr, replaceResult, replaceLen);
3889 } else {
3890 memcpy(fillPtr, replaceString, replaceLen);
3892 fillPtr += replaceLen;
3893 lastEndPos = endPos;
3894 /* start next after match unless match was empty, then endPos+1 */
3895 beginPos = (startPos == endPos) ? endPos+1 : endPos;
3896 if (inString[endPos] == '\0')
3897 break;
3900 *fillPtr = '\0';
3901 *replacementLength = fillPtr - outString;
3902 return outString;
3906 ** If this is an incremental search and BeepOnSearchWrap is on:
3907 ** Emit a beep if the search wrapped over BOF/EOF compared to
3908 ** the last startPos of the current incremental search.
3910 static void iSearchTryBeepOnWrap(WindowInfo *window, int direction,
3911 int beginPos, int startPos)
3913 if(GetPrefBeepOnSearchWrap()) {
3914 if(direction == SEARCH_FORWARD) {
3915 if( (startPos >= beginPos && window->iSearchLastBeginPos < beginPos)
3916 ||(startPos < beginPos && window->iSearchLastBeginPos >= beginPos))
3917 XBell(TheDisplay, 0);
3918 } else {
3919 if( (startPos <= beginPos && window->iSearchLastBeginPos > beginPos)
3920 ||(startPos > beginPos && window->iSearchLastBeginPos <= beginPos))
3921 XBell(TheDisplay, 0);
3927 ** Search the text in "window", attempting to match "searchString"
3929 int SearchWindow(WindowInfo *window, int direction, const char *searchString,
3930 int searchType, int searchWrap, int beginPos, int *startPos,
3931 int *endPos, int *extentBW, int *extentFW)
3933 char *fileString;
3934 int found, resp, fileEnd = window->buffer->length - 1, outsideBounds;
3936 /* reject empty string */
3937 if (*searchString == '\0')
3938 return FALSE;
3940 /* get the entire text buffer from the text area widget */
3941 fileString = BufGetAll(window->buffer);
3943 /* If we're already outside the boundaries, we must consider wrapping
3944 immediately (Note: fileEnd+1 is a valid starting position. Consider
3945 searching for $ at the end of a file ending with \n.) */
3946 if ((direction == SEARCH_FORWARD && beginPos > fileEnd + 1)
3947 || (direction == SEARCH_BACKWARD && beginPos < 0))
3949 outsideBounds = TRUE;
3950 } else
3952 outsideBounds = FALSE;
3955 /* search the string copied from the text area widget, and present
3956 dialogs, or just beep. iSearchStartPos is not a perfect indicator that
3957 an incremental search is in progress. A parameter would be better. */
3958 if (window->iSearchStartPos == -1) { /* normal search */
3959 found = !outsideBounds &&
3960 SearchString(fileString, searchString, direction, searchType,
3961 FALSE, beginPos, startPos, endPos, extentBW, extentFW,
3962 GetWindowDelimiters(window));
3963 /* Avoid Motif 1.1 bug by putting away search dialog before DialogF */
3964 if (window->findDlog && XtIsManaged(window->findDlog) &&
3965 !XmToggleButtonGetState(window->findKeepBtn))
3966 XtUnmanageChild(window->findDlog);
3967 if (window->replaceDlog && XtIsManaged(window->replaceDlog) &&
3968 !XmToggleButtonGetState(window->replaceKeepBtn))
3969 unmanageReplaceDialogs(window);
3970 if (!found) {
3971 if (searchWrap) {
3972 if (direction == SEARCH_FORWARD && beginPos != 0) {
3973 if(GetPrefBeepOnSearchWrap()) {
3974 XBell(TheDisplay, 0);
3975 } else if (GetPrefSearchDlogs()) {
3976 resp = DialogF(DF_QUES, window->shell, 2, "Wrap Search",
3977 "Continue search from\nbeginning of file?",
3978 "Continue", "Cancel");
3979 if (resp == 2) {
3980 XtFree(fileString);
3981 return False;
3984 found = SearchString(fileString, searchString, direction,
3985 searchType, FALSE, 0, startPos, endPos, extentBW,
3986 extentFW, GetWindowDelimiters(window));
3987 } else if (direction == SEARCH_BACKWARD && beginPos != fileEnd) {
3988 if(GetPrefBeepOnSearchWrap()) {
3989 XBell(TheDisplay, 0);
3990 } else if (GetPrefSearchDlogs()) {
3991 resp = DialogF(DF_QUES, window->shell, 2, "Wrap Search",
3992 "Continue search\nfrom end of file?", "Continue",
3993 "Cancel");
3994 if (resp == 2) {
3995 XtFree(fileString);
3996 return False;
3999 found = SearchString(fileString, searchString, direction,
4000 searchType, FALSE, fileEnd + 1, startPos, endPos, extentBW,
4001 extentFW, GetWindowDelimiters(window));
4004 if (!found) {
4005 if (GetPrefSearchDlogs()) {
4006 DialogF(DF_INF, window->shell, 1, "String not found",
4007 "String was not found","OK");
4008 } else {
4009 XBell(TheDisplay, 0);
4013 } else { /* incremental search */
4014 if (outsideBounds && searchWrap) {
4015 if (direction == SEARCH_FORWARD) beginPos = 0;
4016 else beginPos = fileEnd+1;
4017 outsideBounds = FALSE;
4019 found = !outsideBounds &&
4020 SearchString(fileString, searchString, direction,
4021 searchType, searchWrap, beginPos, startPos, endPos,
4022 extentBW, extentFW, GetWindowDelimiters(window));
4023 if (found) {
4024 iSearchTryBeepOnWrap(window, direction, beginPos, *startPos);
4025 } else
4026 XBell(TheDisplay, 0);
4029 /* Free the text buffer copy returned from BufGetAll */
4030 XtFree(fileString);
4032 return found;
4036 ** Search the null terminated string "string" for "searchString", beginning at
4037 ** "beginPos". Returns the boundaries of the match in "startPos" and "endPos".
4038 ** searchExtentBW and searchExtentFW return the backwardmost and forwardmost
4039 ** positions used to make the match, which are usually startPos and endPos,
4040 ** but may extend further if positive lookahead or lookbehind was used in
4041 ** a regular expression match. "delimiters" may be used to provide an
4042 ** alternative set of word delimiters for regular expression "<" and ">"
4043 ** characters, or simply passed as null for the default delimiter set.
4045 int SearchString(const char *string, const char *searchString, int direction,
4046 int searchType, int wrap, int beginPos, int *startPos, int *endPos,
4047 int *searchExtentBW, int *searchExtentFW, const char *delimiters)
4049 switch (searchType) {
4050 case SEARCH_CASE_SENSE_WORD:
4051 return searchLiteralWord(string, searchString, TRUE, direction, wrap,
4052 beginPos, startPos, endPos, delimiters);
4053 case SEARCH_LITERAL_WORD:
4054 return searchLiteralWord(string, searchString, FALSE, direction, wrap,
4055 beginPos, startPos, endPos, delimiters);
4056 case SEARCH_CASE_SENSE:
4057 return searchLiteral(string, searchString, TRUE, direction, wrap,
4058 beginPos, startPos, endPos, searchExtentBW,
4059 searchExtentFW);
4060 case SEARCH_LITERAL:
4061 return searchLiteral(string, searchString, FALSE, direction, wrap,
4062 beginPos, startPos, endPos, searchExtentBW, searchExtentFW);
4063 case SEARCH_REGEX:
4064 return searchRegex(string, searchString, direction, wrap,
4065 beginPos, startPos, endPos, searchExtentBW, searchExtentFW,
4066 delimiters, REDFLT_STANDARD);
4067 case SEARCH_REGEX_NOCASE:
4068 return searchRegex(string, searchString, direction, wrap,
4069 beginPos, startPos, endPos, searchExtentBW, searchExtentFW,
4070 delimiters, REDFLT_CASE_INSENSITIVE);
4072 return FALSE; /* never reached, just makes compilers happy */
4076 ** Parses a search type description string. If the string contains a valid
4077 ** search type description, returns TRUE and writes the corresponding
4078 ** SearchType in searchType. Returns FALSE and leaves searchType untouched
4079 ** otherwise. (Originally written by Markus Schwarzenberg; slightly adapted).
4081 int StringToSearchType(const char * string, int *searchType)
4083 int i;
4084 for (i = 0; searchTypeStrings[i]; i++) {
4085 if (!strcmp(string, searchTypeStrings[i])) {
4086 break;
4089 if (!searchTypeStrings[i]) {
4090 return FALSE;
4092 *searchType = i;
4093 return TRUE;
4097 ** Searches for whole words (Markus Schwarzenberg).
4099 ** If the first/last character of `searchString' is a "normal
4100 ** word character" (not contained in `delimiters', not a whitespace)
4101 ** then limit search to strings, who's next left/next right character
4102 ** is contained in `delimiters' or is a whitespace or text begin or end.
4104 ** If the first/last character of `searchString' itself is contained
4105 ** in delimiters or is a white space, then the neighbour character of the
4106 ** first/last character will not be checked, just a simple match
4107 ** will suffice in that case.
4110 static int searchLiteralWord(const char *string, const char *searchString, int caseSense,
4111 int direction, int wrap, int beginPos, int *startPos, int *endPos,
4112 const char * delimiters)
4114 /* This is critical code for the speed of searches. */
4115 /* For efficiency, we define the macro DOSEARCH with the guts of the search */
4116 /* routine and repeat it, changing the parameters of the outer loop for the */
4117 /* searching, forwards, backwards, and before and after the begin point */
4118 #define DOSEARCHWORD() \
4119 if (*filePtr == *ucString || *filePtr == *lcString) { \
4120 /* matched first character */ \
4121 ucPtr = ucString; \
4122 lcPtr = lcString; \
4123 tempPtr = filePtr; \
4124 while (*tempPtr == *ucPtr || *tempPtr == *lcPtr) { \
4125 tempPtr++; ucPtr++; lcPtr++; \
4126 if ( *ucPtr == 0 /* matched whole string */ \
4127 && (cignore_R ||\
4128 isspace((unsigned char)*tempPtr) ||\
4129 strchr(delimiters, *tempPtr) ) \
4130 /* next char right delimits word ? */ \
4131 && (cignore_L ||\
4132 filePtr==string || /* border case */ \
4133 isspace((unsigned char)filePtr[-1]) ||\
4134 strchr(delimiters,filePtr[-1]) ))\
4135 /* next char left delimits word ? */ { \
4136 *startPos = filePtr - string; \
4137 *endPos = tempPtr - string; \
4138 return TRUE; \
4143 register const char *filePtr, *tempPtr, *ucPtr, *lcPtr;
4144 char lcString[SEARCHMAX], ucString[SEARCHMAX];
4146 int cignore_L=0, cignore_R=0;
4148 /* SEARCHMAX was fine in the original NEdit, but it should be done away
4149 with now that searching can be done from macros without limits.
4150 Returning search failure here is cheating users. This limit is not
4151 documented. */
4152 if (strlen(searchString) >= SEARCHMAX)
4153 return FALSE;
4155 /* If there is no language mode, we use the default list of delimiters */
4156 if (delimiters==NULL) delimiters = GetPrefDelimiters();
4158 if ( isspace((unsigned char)*searchString)
4159 || strchr(delimiters, *searchString))
4160 cignore_L=1;
4162 if ( isspace((unsigned char)searchString[strlen(searchString)-1])
4163 || strchr(delimiters, searchString[strlen(searchString)-1]) )
4164 cignore_R=1;
4166 if (caseSense) {
4167 strcpy(ucString, searchString);
4168 strcpy(lcString, searchString);
4169 } else {
4170 upCaseString(ucString, searchString);
4171 downCaseString(lcString, searchString);
4174 if (direction == SEARCH_FORWARD) {
4175 /* search from beginPos to end of string */
4176 for (filePtr=string+beginPos; *filePtr!=0; filePtr++) {
4177 DOSEARCHWORD()
4179 if (!wrap)
4180 return FALSE;
4182 /* search from start of file to beginPos */
4183 for (filePtr=string; filePtr<=string+beginPos; filePtr++) {
4184 DOSEARCHWORD()
4186 return FALSE;
4187 } else {
4188 /* SEARCH_BACKWARD */
4189 /* search from beginPos to start of file. A negative begin pos */
4190 /* says begin searching from the far end of the file */
4191 if (beginPos >= 0) {
4192 for (filePtr=string+beginPos; filePtr>=string; filePtr--) {
4193 DOSEARCHWORD()
4196 if (!wrap)
4197 return FALSE;
4198 /* search from end of file to beginPos */
4199 /*... this strlen call is extreme inefficiency, but it's not obvious */
4200 /* how to get the text string length from the text widget (under 1.1)*/
4201 for (filePtr=string+strlen(string); filePtr>=string+beginPos; filePtr--) {
4202 DOSEARCHWORD()
4204 return FALSE;
4209 static int searchLiteral(const char *string, const char *searchString, int caseSense,
4210 int direction, int wrap, int beginPos, int *startPos, int *endPos,
4211 int *searchExtentBW, int *searchExtentFW)
4213 /* This is critical code for the speed of searches. */
4214 /* For efficiency, we define the macro DOSEARCH with the guts of the search */
4215 /* routine and repeat it, changing the parameters of the outer loop for the */
4216 /* searching, forwards, backwards, and before and after the begin point */
4217 #define DOSEARCH() \
4218 if (*filePtr == *ucString || *filePtr == *lcString) { \
4219 /* matched first character */ \
4220 ucPtr = ucString; \
4221 lcPtr = lcString; \
4222 tempPtr = filePtr; \
4223 while (*tempPtr == *ucPtr || *tempPtr == *lcPtr) { \
4224 tempPtr++; ucPtr++; lcPtr++; \
4225 if (*ucPtr == 0) { \
4226 /* matched whole string */ \
4227 *startPos = filePtr - string; \
4228 *endPos = tempPtr - string; \
4229 if (searchExtentBW != NULL) \
4230 *searchExtentBW = *startPos; \
4231 if (searchExtentFW != NULL) \
4232 *searchExtentFW = *endPos; \
4233 return TRUE; \
4238 register const char *filePtr, *tempPtr, *ucPtr, *lcPtr;
4239 char lcString[SEARCHMAX], ucString[SEARCHMAX];
4241 /* SEARCHMAX was fine in the original NEdit, but it should be done away with
4242 now that searching can be done from macros without limits. Returning
4243 search failure here is cheating users. This limit is not documented. */
4244 if (strlen(searchString) >= SEARCHMAX)
4245 return FALSE;
4247 if (caseSense) {
4248 strcpy(ucString, searchString);
4249 strcpy(lcString, searchString);
4250 } else {
4251 upCaseString(ucString, searchString);
4252 downCaseString(lcString, searchString);
4255 if (direction == SEARCH_FORWARD) {
4256 /* search from beginPos to end of string */
4257 for (filePtr=string+beginPos; *filePtr!=0; filePtr++) {
4258 DOSEARCH()
4260 if (!wrap)
4261 return FALSE;
4262 /* search from start of file to beginPos */
4263 for (filePtr=string; filePtr<=string+beginPos; filePtr++) {
4264 DOSEARCH()
4266 return FALSE;
4267 } else {
4268 /* SEARCH_BACKWARD */
4269 /* search from beginPos to start of file. A negative begin pos */
4270 /* says begin searching from the far end of the file */
4271 if (beginPos >= 0) {
4272 for (filePtr=string+beginPos; filePtr>=string; filePtr--) {
4273 DOSEARCH()
4276 if (!wrap)
4277 return FALSE;
4278 /* search from end of file to beginPos */
4279 /*... this strlen call is extreme inefficiency, but it's not obvious */
4280 /* how to get the text string length from the text widget (under 1.1)*/
4281 for (filePtr=string+strlen(string);
4282 filePtr>=string+beginPos; filePtr--) {
4283 DOSEARCH()
4285 return FALSE;
4289 static int searchRegex(const char *string, const char *searchString, int direction,
4290 int wrap, int beginPos, int *startPos, int *endPos, int *searchExtentBW,
4291 int *searchExtentFW, const char *delimiters, int defaultFlags)
4293 if (direction == SEARCH_FORWARD)
4294 return forwardRegexSearch(string, searchString, wrap,
4295 beginPos, startPos, endPos, searchExtentBW, searchExtentFW,
4296 delimiters, defaultFlags);
4297 else
4298 return backwardRegexSearch(string, searchString, wrap,
4299 beginPos, startPos, endPos, searchExtentBW, searchExtentFW,
4300 delimiters, defaultFlags);
4303 static int forwardRegexSearch(const char *string, const char *searchString, int wrap,
4304 int beginPos, int *startPos, int *endPos, int *searchExtentBW,
4305 int *searchExtentFW, const char *delimiters, int defaultFlags)
4307 regexp *compiledRE = NULL;
4308 char *compileMsg;
4310 /* compile the search string for searching with ExecRE. Note that
4311 this does not process errors from compiling the expression. It
4312 assumes that the expression was checked earlier. */
4313 compiledRE = CompileRE(searchString, &compileMsg, defaultFlags);
4314 if (compiledRE == NULL)
4315 return FALSE;
4317 /* search from beginPos to end of string */
4318 if (ExecRE(compiledRE, NULL, string + beginPos, NULL, FALSE,
4319 beginPos==0 ? '\0' : string[beginPos-1], '\0', delimiters, string)) {
4320 *startPos = compiledRE->startp[0] - string;
4321 *endPos = compiledRE->endp[0] - string;
4322 if (searchExtentFW != NULL)
4323 *searchExtentFW = compiledRE->extentpFW - string;
4324 if (searchExtentBW != NULL)
4325 *searchExtentBW = compiledRE->extentpBW - string;
4326 free((char *)compiledRE);
4327 return TRUE;
4330 /* if wrap turned off, we're done */
4331 if (!wrap) {
4332 free((char *)compiledRE);
4333 return FALSE;
4336 /* search from the beginning of the string to beginPos */
4337 if (ExecRE(compiledRE, NULL, string, string + beginPos, FALSE, '\0',
4338 string[beginPos], delimiters, string)) {
4339 *startPos = compiledRE->startp[0] - string;
4340 *endPos = compiledRE->endp[0] - string;
4341 if (searchExtentFW != NULL)
4342 *searchExtentFW = compiledRE->extentpFW - string;
4343 if (searchExtentBW != NULL)
4344 *searchExtentBW = compiledRE->extentpBW - string;
4345 free((char *)compiledRE);
4346 return TRUE;
4349 free((char *)compiledRE);
4350 return FALSE;
4353 static int backwardRegexSearch(const char *string, const char *searchString, int wrap,
4354 int beginPos, int *startPos, int *endPos, int *searchExtentBW,
4355 int *searchExtentFW, const char *delimiters, int defaultFlags)
4357 regexp *compiledRE = NULL;
4358 char *compileMsg;
4359 int length;
4361 /* compile the search string for searching with ExecRE */
4362 compiledRE = CompileRE(searchString, &compileMsg, defaultFlags);
4363 if (compiledRE == NULL)
4364 return FALSE;
4366 /* search from beginPos to start of file. A negative begin pos */
4367 /* says begin searching from the far end of the file. */
4368 if (beginPos >= 0) {
4369 if (ExecRE(compiledRE, NULL, string, string + beginPos, TRUE, '\0',
4370 '\0', delimiters, string)) {
4371 *startPos = compiledRE->startp[0] - string;
4372 *endPos = compiledRE->endp[0] - string;
4373 if (searchExtentFW != NULL)
4374 *searchExtentFW = compiledRE->extentpFW - string;
4375 if (searchExtentBW != NULL)
4376 *searchExtentBW = compiledRE->extentpBW - string;
4377 free((char *)compiledRE);
4378 return TRUE;
4382 /* if wrap turned off, we're done */
4383 if (!wrap) {
4384 free((char *)compiledRE);
4385 return FALSE;
4388 /* search from the end of the string to beginPos */
4389 if (beginPos < 0)
4390 beginPos = 0;
4391 length = strlen(string); /* sadly, this means scanning entire string */
4392 if (ExecRE(compiledRE, NULL, string + beginPos, string + length, TRUE,
4393 beginPos==0 ? '\0' : string[beginPos-1], '\0', delimiters, string)) {
4394 *startPos = compiledRE->startp[0] - string;
4395 *endPos = compiledRE->endp[0] - string;
4396 if (searchExtentFW != NULL)
4397 *searchExtentFW = compiledRE->extentpFW - string;
4398 if (searchExtentBW != NULL)
4399 *searchExtentBW = compiledRE->extentpBW - string;
4400 free((char *)compiledRE);
4401 return TRUE;
4403 free((char *)compiledRE);
4404 return FALSE;
4407 static void upCaseString(char *outString, const char *inString)
4409 char *outPtr;
4410 const char *inPtr;
4412 for (outPtr=outString, inPtr=inString; *inPtr!=0; inPtr++, outPtr++) {
4413 *outPtr = toupper((unsigned char)*inPtr);
4415 *outPtr = 0;
4418 static void downCaseString(char *outString, const char *inString)
4420 char *outPtr;
4421 const char *inPtr;
4423 for (outPtr=outString, inPtr=inString; *inPtr!=0; inPtr++, outPtr++) {
4424 *outPtr = tolower((unsigned char)*inPtr);
4426 *outPtr = 0;
4430 ** resetFindTabGroup & resetReplaceTabGroup are really gruesome kludges to
4431 ** set the keyboard traversal. XmProcessTraversal does not work at
4432 ** all on these dialogs. ...It seems to have started working around
4433 ** Motif 1.1.2
4435 static void resetFindTabGroup(WindowInfo *window)
4437 XmProcessTraversal(window->findText, XmTRAVERSE_CURRENT);
4439 static void resetReplaceTabGroup(WindowInfo *window)
4441 XmProcessTraversal(window->replaceText, XmTRAVERSE_CURRENT);
4445 ** Return TRUE if "searchString" exactly matches the text in the window's
4446 ** current primary selection using search algorithm "searchType". If true,
4447 ** also return the position of the selection in "left" and "right".
4449 static int searchMatchesSelection(WindowInfo *window, const char *searchString,
4450 int searchType, int *left, int *right, int *searchExtentBW,
4451 int *searchExtentFW)
4453 int selLen, selStart, selEnd, startPos, endPos, extentBW, extentFW, beginPos;
4454 int regexLookContext = isRegexType(searchType) ? 1000 : 0;
4455 char *string;
4456 int found, isRect, rectStart, rectEnd, lineStart = 0;
4458 /* find length of selection, give up on no selection or too long */
4459 if (!BufGetEmptySelectionPos(window->buffer, &selStart, &selEnd, &isRect,
4460 &rectStart, &rectEnd))
4461 return FALSE;
4462 if (selEnd - selStart > SEARCHMAX)
4463 return FALSE;
4465 /* if the selection is rectangular, don't match if it spans lines */
4466 if (isRect) {
4467 lineStart = BufStartOfLine(window->buffer, selStart);
4468 if (lineStart != BufStartOfLine(window->buffer, selEnd))
4469 return FALSE;
4472 /* get the selected text plus some additional context for regular
4473 expression lookahead */
4474 if (isRect) {
4475 int stringStart = lineStart + rectStart - regexLookContext;
4476 if (stringStart < 0) stringStart = 0;
4477 string = BufGetRange(window->buffer, stringStart,
4478 lineStart + rectEnd + regexLookContext);
4479 selLen = rectEnd - rectStart;
4480 beginPos = lineStart + rectStart - stringStart;
4481 } else {
4482 int stringStart = selStart - regexLookContext;
4483 if (stringStart < 0) stringStart = 0;
4484 string = BufGetRange(window->buffer, stringStart,
4485 selEnd + regexLookContext);
4486 selLen = selEnd - selStart;
4487 beginPos = selStart - stringStart;
4489 if (*string == '\0') {
4490 XtFree(string);
4491 return FALSE;
4494 /* search for the string in the selection (we are only interested */
4495 /* in an exact match, but the procedure SearchString does important */
4496 /* stuff like applying the correct matching algorithm) */
4497 found = SearchString(string, searchString, SEARCH_FORWARD, searchType,
4498 FALSE, beginPos, &startPos, &endPos, &extentBW, &extentFW,
4499 GetWindowDelimiters(window));
4500 XtFree(string);
4502 /* decide if it is an exact match */
4503 if (!found)
4504 return FALSE;
4505 if (startPos != beginPos || endPos - beginPos != selLen )
4506 return FALSE;
4508 /* return the start and end of the selection */
4509 if (isRect)
4510 GetSimpleSelection(window->buffer, left, right);
4511 else {
4512 *left = selStart;
4513 *right = selEnd;
4515 if (searchExtentBW != NULL)
4516 *searchExtentBW = *left - (startPos - extentBW);
4518 if (searchExtentFW != NULL)
4519 *searchExtentFW = *right + extentFW - endPos;
4520 return TRUE;
4524 ** Substitutes a replace string for a string that was matched using a
4525 ** regular expression. This was added later and is rather ineficient
4526 ** because instead of using the compiled regular expression that was used
4527 ** to make the match in the first place, it re-compiles the expression
4528 ** and redoes the search on the already-matched string. This allows the
4529 ** code to continue using strings to represent the search and replace
4530 ** items.
4532 static void replaceUsingRE(const char *searchStr, const char *replaceStr,
4533 const char *sourceStr, int beginPos, char *destStr,
4534 int maxDestLen, int prevChar, const char *delimiters, int defaultFlags)
4536 regexp *compiledRE;
4537 char *compileMsg;
4539 compiledRE = CompileRE(searchStr, &compileMsg, defaultFlags);
4540 ExecRE(compiledRE, NULL, sourceStr+beginPos, NULL, False, prevChar,
4541 '\0', delimiters, sourceStr);
4542 SubstituteRE(compiledRE, replaceStr, destStr, maxDestLen);
4543 free((char *)compiledRE);
4547 ** Store the search and replace strings, and search type for later recall.
4548 ** If replaceString is NULL, duplicate the last replaceString used.
4549 ** Contiguous incremental searches share the same history entry (each new
4550 ** search modifies the current search string, until a non-incremental search
4551 ** is made. To mark the end of an incremental search, call saveSearchHistory
4552 ** again with an empty search string and isIncremental==False.
4554 static void saveSearchHistory(const char *searchString,
4555 const char *replaceString, int searchType, int isIncremental)
4557 char *sStr, *rStr;
4558 static int currentItemIsIncremental = FALSE;
4559 WindowInfo *w;
4561 /* Cancel accumulation of contiguous incremental searches (even if the
4562 information is not worthy of saving) if search is not incremental */
4563 if (!isIncremental)
4564 currentItemIsIncremental = FALSE;
4566 /* Don't save empty search strings */
4567 if (searchString[0] == '\0')
4568 return;
4570 /* If replaceString is NULL, duplicate the last one (if any) */
4571 if (replaceString == NULL)
4572 replaceString = NHist >= 1 ? ReplaceHistory[historyIndex(1)] : "";
4574 /* Compare the current search and replace strings against the saved ones.
4575 If they are identical, don't bother saving */
4576 if (NHist >= 1 && searchType == SearchTypeHistory[historyIndex(1)] &&
4577 !strcmp(SearchHistory[historyIndex(1)], searchString) &&
4578 !strcmp(ReplaceHistory[historyIndex(1)], replaceString)) {
4579 return;
4582 /* If the current history item came from an incremental search, and the
4583 new one is also incremental, just update the entry */
4584 if (currentItemIsIncremental && isIncremental) {
4585 XtFree(SearchHistory[historyIndex(1)]);
4586 SearchHistory[historyIndex(1)] = XtNewString(searchString);
4587 SearchTypeHistory[historyIndex(1)] = searchType;
4588 return;
4590 currentItemIsIncremental = isIncremental;
4592 if (NHist==0) {
4593 for (w=WindowList; w!=NULL; w=w->next) {
4594 XtSetSensitive(w->findAgainItem, True);
4595 XtSetSensitive(w->replaceFindAgainItem, True);
4596 XtSetSensitive(w->replaceAgainItem, True);
4600 /* If there are more than MAX_SEARCH_HISTORY strings saved, recycle
4601 some space, free the entry that's about to be overwritten */
4602 if (NHist == MAX_SEARCH_HISTORY) {
4603 XtFree(SearchHistory[HistStart]);
4604 XtFree(ReplaceHistory[HistStart]);
4605 } else
4606 NHist++;
4608 /* Allocate and copy the search and replace strings and add them to the
4609 circular buffers at HistStart, bump the buffer pointer to next pos. */
4610 sStr = XtMalloc(strlen(searchString) + 1);
4611 rStr = XtMalloc(strlen(replaceString) + 1);
4612 strcpy(sStr, searchString);
4613 strcpy(rStr, replaceString);
4614 SearchHistory[HistStart] = sStr;
4615 ReplaceHistory[HistStart] = rStr;
4616 SearchTypeHistory[HistStart] = searchType;
4617 HistStart++;
4618 if (HistStart >= MAX_SEARCH_HISTORY)
4619 HistStart = 0;
4623 ** return an index into the circular buffer arrays of history information
4624 ** for search strings, given the number of saveSearchHistory cycles back from
4625 ** the current time.
4628 static int historyIndex(int nCycles)
4630 int index;
4632 if (nCycles > NHist || nCycles <= 0)
4633 return -1;
4634 index = HistStart - nCycles;
4635 if (index < 0)
4636 index = MAX_SEARCH_HISTORY + index;
4637 return index;
4641 ** Return a pointer to the string describing search type for search action
4642 ** routine parameters (see menu.c for processing of action routines)
4644 static char *searchTypeArg(int searchType)
4646 if (0 <= searchType && searchType < N_SEARCH_TYPES) {
4647 return searchTypeStrings[searchType];
4649 return searchTypeStrings[SEARCH_LITERAL];
4653 ** Return a pointer to the string describing search wrap for search action
4654 ** routine parameters (see menu.c for processing of action routines)
4656 static char *searchWrapArg(int searchWrap)
4658 if (searchWrap) {
4659 return "wrap";
4661 return "nowrap";
4665 ** Return a pointer to the string describing search direction for search action
4666 ** routine parameters (see menu.c for processing of action routines)
4668 static char *directionArg(int direction)
4670 if (direction == SEARCH_BACKWARD)
4671 return "backward";
4672 return "forward";
4676 ** Checks whether a search mode in one of the regular expression modes.
4678 static int isRegexType(int searchType)
4680 return searchType == SEARCH_REGEX || searchType == SEARCH_REGEX_NOCASE;
4684 ** Returns the default flags for regular expression matching, given a
4685 ** regular expression search mode.
4687 static int defaultRegexFlags(int searchType)
4689 switch (searchType) {
4690 case SEARCH_REGEX:
4691 return REDFLT_STANDARD;
4692 case SEARCH_REGEX_NOCASE:
4693 return REDFLT_CASE_INSENSITIVE;
4694 default:
4695 /* We should never get here, but just in case ... */
4696 return REDFLT_STANDARD;
4701 ** The next 4 callbacks handle the states of find/replace toggle
4702 ** buttons, which depend on the state of the "Regex" button, and the
4703 ** sensitivity of the Whole Word buttons.
4704 ** Callbacks are necessary for both "Regex" and "Case Sensitive"
4705 ** buttons to make sure the states are saved even after a cancel operation.
4707 ** If sticky case sensitivity is requested, the behaviour is as follows:
4708 ** The first time "Regular expression" is checked, "Match case" gets
4709 ** checked too. Thereafter, checking or unchecking "Regular expression"
4710 ** restores the "Match case" button to the setting it had the last
4711 ** time when literals or REs where used.
4712 ** Without sticky behaviour, the state of the Regex button doesn't influence
4713 ** the state of the Case Sensitive button.
4715 ** Independently, the state of the buttons is always restored to the
4716 ** default state when a dialog is popped up, and when the user returns
4717 ** from stepping through the search history.
4719 ** NOTE: similar call-backs exist for the incremental search bar; see window.c.
4721 static void findRegExpToggleCB(Widget w, XtPointer clientData, XtPointer callData)
4723 WindowInfo * window = WidgetToWindow(w);
4724 int searchRegex = XmToggleButtonGetState(w);
4725 int searchCaseSense = XmToggleButtonGetState(window->findCaseToggle);
4727 /* In sticky mode, restore the state of the Case Sensitive button */
4728 if(GetPrefStickyCaseSenseBtn()) {
4729 if(searchRegex) {
4730 window->findLastLiteralCase = searchCaseSense;
4731 XmToggleButtonSetState(window->findCaseToggle,
4732 window->findLastRegexCase, False);
4733 } else {
4734 window->findLastRegexCase = searchCaseSense;
4735 XmToggleButtonSetState(window->findCaseToggle,
4736 window->findLastLiteralCase, False);
4739 /* make the Whole Word button insensitive for regex searches */
4740 XtSetSensitive(window->findWordToggle, !searchRegex);
4743 static void replaceRegExpToggleCB(Widget w, XtPointer clientData, XtPointer callData)
4745 WindowInfo * window = WidgetToWindow(w);
4746 int searchRegex = XmToggleButtonGetState(w);
4747 int searchCaseSense = XmToggleButtonGetState(window->replaceCaseToggle);
4749 /* In sticky mode, restore the state of the Case Sensitive button */
4750 if(GetPrefStickyCaseSenseBtn()) {
4751 if(searchRegex) {
4752 window->replaceLastLiteralCase = searchCaseSense;
4753 XmToggleButtonSetState(window->replaceCaseToggle,
4754 window->replaceLastRegexCase, False);
4755 } else {
4756 window->replaceLastRegexCase = searchCaseSense;
4757 XmToggleButtonSetState(window->replaceCaseToggle,
4758 window->replaceLastLiteralCase, False);
4761 /* make the Whole Word button insensitive for regex searches */
4762 XtSetSensitive(window->replaceWordToggle, !searchRegex);
4765 static void iSearchRegExpToggleCB(Widget w, XtPointer clientData, XtPointer callData)
4767 WindowInfo * window = WidgetToWindow(w);
4768 int searchRegex = XmToggleButtonGetState(w);
4769 int searchCaseSense = XmToggleButtonGetState(window->iSearchCaseToggle);
4771 /* In sticky mode, restore the state of the Case Sensitive button */
4772 if(GetPrefStickyCaseSenseBtn()) {
4773 if(searchRegex) {
4774 window->iSearchLastLiteralCase = searchCaseSense;
4775 XmToggleButtonSetState(window->iSearchCaseToggle,
4776 window->iSearchLastRegexCase, False);
4777 } else {
4778 window->iSearchLastRegexCase = searchCaseSense;
4779 XmToggleButtonSetState(window->iSearchCaseToggle,
4780 window->iSearchLastLiteralCase, False);
4783 /* The iSearch bar has no Whole Word button to enable/disable. */
4785 static void findCaseToggleCB(Widget w, XtPointer clientData, XtPointer callData)
4787 WindowInfo * window = WidgetToWindow(w);
4788 int searchCaseSense = XmToggleButtonGetState(w);
4790 /* Save the state of the Case Sensitive button
4791 depending on the state of the Regex button*/
4792 if(XmToggleButtonGetState(window->findRegexToggle))
4793 window->findLastRegexCase = searchCaseSense;
4794 else
4795 window->findLastLiteralCase = searchCaseSense;
4798 static void replaceCaseToggleCB(Widget w, XtPointer clientData, XtPointer callData)
4800 WindowInfo * window = WidgetToWindow(w);
4801 int searchCaseSense = XmToggleButtonGetState(w);
4803 /* Save the state of the Case Sensitive button
4804 depending on the state of the Regex button*/
4805 if(XmToggleButtonGetState(window->replaceRegexToggle))
4806 window->replaceLastRegexCase = searchCaseSense;
4807 else
4808 window->replaceLastLiteralCase = searchCaseSense;
4811 static void iSearchCaseToggleCB(Widget w, XtPointer clientData, XtPointer callData)
4813 WindowInfo * window = WidgetToWindow(w);
4814 int searchCaseSense = XmToggleButtonGetState(w);
4816 /* Save the state of the Case Sensitive button
4817 depending on the state of the Regex button*/
4818 if(XmToggleButtonGetState(window->iSearchRegexToggle))
4819 window->iSearchLastRegexCase = searchCaseSense;
4820 else
4821 window->iSearchLastLiteralCase = searchCaseSense;