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