UNIXworkcode

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