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