add cast to XtCalloc() call
[nedit-bw.git] / SmartCaseSearch.patch
blob652db0e47c6dfc9014691a010640427ff6897c93
1 From: Thorsten Haude <yoo@vranx.de>
2 Subject: Smart Case Search
4 This adds the option 'Regular Expression, Smart Case' to Preferences ->
5 Default Settings -> Searching -> Default Search Style.
7 If this option is activated, the behavior of the iSearch bar changes as
8 follows: The search defaults to 'regex, no case' but changes to 'case
9 sensitive' as soon as an upper-case letter is entered in the iSearch line.
11 As seen on Mutt, other applications probably have similar things.
13 One problem I have with this is the lack of visual feedback. A separate
14 button is out of the question of course, so how should this be signaled to
15 the user? Color?
17 I added this only to the iSearch, I somehow don't see this in the
18 find/replace dialog boxes.
20 ---
22 doc/help.etx | 23 ++++++++++++------
23 source/menu.c | 36 +++++++++++++++++++++++++++++
24 source/nedit.h | 1
25 source/preferences.c | 17 +++++++------
26 source/search.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++-----
27 source/search.h | 1
28 source/window.c | 3 +-
29 7 files changed, 122 insertions(+), 22 deletions(-)
31 diff --quilt old/doc/help.etx new/doc/help.etx
32 --- old/doc/help.etx
33 +++ new/doc/help.etx
34 @@ -260,10 +260,15 @@ Finding and Replacing Text
35 button with an icon resembling "|<". Clicking on it empties the search text widget
36 without disturbing selections. A middle click on the clear button copies the
37 content of any existing selection into the search text widget and triggers a new
38 search.
40 + All searches will default to the search mode selected in the preference menu.
41 + (Default Setting -> Searching -> Default Search Style). You can choose a smart
42 + case sensitivity here; this let all searches start insensitive, but changes to
43 + case sensitive as soon as you enter an upper-case letter.
45 3>Searching Backwards
47 Holding down the shift key while choosing any of the search or replace
48 commands from the menu (or using the keyboard shortcut), will search in the
49 reverse direction. Users who have set the search direction using the buttons
50 @@ -2764,23 +2769,24 @@ Macro Subroutines
51 Searches silently in a window without dialogs, beeps, or changes to the
52 selection. Arguments are: 1: string to search for, 2: starting position.
53 Optional arguments may include the strings: "wrap" to make the search wrap
54 around the beginning or end of the string, "backward" or "forward" to change
55 the search direction ("forward" is the default), "literal", "case", "word",
56 - "caseWord", "regex", or "regexNoCase" to change the search type (default is
57 - "literal"). Returns the starting position of the match, or -1 if nothing
58 - matched. Also returns the ending position of the match in $search_end.
59 + "caseWord", "regex", "regexNoCase", or "regexSmartCase" to change the search
60 + type (default is "literal"). Returns the starting position of the match, or
61 + -1 if nothing matched. Also returns the ending position of the match in
62 + $search_end.
64 **search_string( string, search_for, start [, search_type, direction] )**
65 Built-in macro subroutine for searching a string. Arguments are 1: string to
66 search in, 2: string to search for, 3: starting position. Optional arguments
67 may include the strings: "wrap" to make the search wrap around the beginning
68 or end of the string, "backward" or "forward" to change the search direction
69 ("forward" is the default), "literal", "case", "word", "caseWord", "regex",
70 - or "regexNoCase" to change the search type (default is "literal"). Returns
71 - the starting position of the match, or -1 if nothing matched. Also returns
72 - the ending position of the match in $search_end.
73 + "regexNoCase", or "regexSmartCase" to change the search type (default is
74 + "literal"). Returns the starting position of the match, or -1 if nothing
75 + matched. Also returns the ending position of the match in $search_end.
77 **select( start, end )**
78 Selects (with the primary selection) text in the current buffer between a
79 starting and ending position.
81 @@ -3319,12 +3325,13 @@ Action Routines
82 "caseWord".
84 ~search-direction~
85 Either "forward" or "backward".
87 - ~search-type~ Either "literal", "case", "word",
88 - "caseWord", "regex", or "regexNoCase".
89 + ~search-type~ Either "literal", "case", "word",
90 + "caseWord", "regex", "regexNoCase",
91 + or "regexSmartCase".
93 ~search-wrap~ Either "wrap" or "nowrap".
95 ~shell-menu-item-name~
96 Name of the command exactly as specified in
97 diff --quilt old/source/menu.c new/source/menu.c
98 --- old/source/menu.c
99 +++ new/source/menu.c
100 @@ -191,10 +191,12 @@ static void exitWarnDefCB(Widget w, Wind
101 static void searchLiteralCB(Widget w, WindowInfo *window, caddr_t callData);
102 static void searchCaseSenseCB(Widget w, WindowInfo *window, caddr_t callData);
103 static void searchLiteralWordCB(Widget w, WindowInfo *window, caddr_t callData);
104 static void searchCaseSenseWordCB(Widget w, WindowInfo *window, caddr_t callData);
105 static void searchRegexNoCaseCB(Widget w, WindowInfo *window, caddr_t callData);
106 +static void searchRegexSmartCaseCB(Widget toggleButton, WindowInfo *windowInfo,
107 + caddr_t callData);
108 static void searchRegexCB(Widget w, WindowInfo *window, caddr_t callData);
109 #ifdef REPLACE_SCOPE
110 static void replaceScopeWindowCB(Widget w, WindowInfo *window, caddr_t callData);
111 static void replaceScopeSelectionCB(Widget w, WindowInfo *window, caddr_t callData);
112 static void replaceScopeSmartCB(Widget w, WindowInfo *window, caddr_t callData);
113 @@ -933,10 +935,14 @@ Widget CreateMenuBar(Widget parent, Wind
114 "regularExpression", "Regular Expression", 'R', searchRegexCB,
115 window, GetPrefSearch() == SEARCH_REGEX, FULL);
116 window->searchRegexNoCaseDefItem = createMenuToggle(subSubSubPane,
117 "regularExpressionNoCase", "Regular Expression, Case Insensitive", 'I', searchRegexNoCaseCB, window,
118 GetPrefSearch() == SEARCH_REGEX_NOCASE, FULL);
119 + window->searchRegexSmartCaseDefItem = createMenuToggle(subSubSubPane,
120 + "regularExpressionSmartCase", "Regular Expression, Smart Case",
121 + 'S', searchRegexSmartCaseCB, window,
122 + GetPrefSearch() == SEARCH_REGEX_SMARTCASE, FULL);
123 #ifdef REPLACE_SCOPE
124 subSubSubPane = createMenu(subSubPane, "defaultReplaceScope",
125 "Default Replace Scope", 'R', NULL, FULL);
126 XtVaSetValues(subSubSubPane, XmNradioBehavior, True, NULL);
127 window->replScopeWinDefItem = createMenuToggle(subSubSubPane, "window",
128 @@ -2566,10 +2572,40 @@ static void searchRegexNoCaseCB(Widget w
129 XmToggleButtonSetState(win->searchRegexNoCaseDefItem, True, False);
134 +static void searchRegexSmartCaseCB(Widget toggleButton, WindowInfo *window,
135 + caddr_t callData)
137 + WindowInfo *windowInfo;
139 + /* Set the preference and make the other windows' menus agree */
140 + if (XmToggleButtonGetState(toggleButton)) {
141 + SetPrefSearch(SEARCH_REGEX_SMARTCASE);
143 + for (windowInfo = WindowList;
144 + windowInfo != NULL;
145 + windowInfo = windowInfo->next) {
146 + XmToggleButtonSetState(windowInfo->searchLiteralDefItem,
147 + False, False);
148 + XmToggleButtonSetState(windowInfo->searchCaseSenseDefItem,
149 + False, False);
150 + XmToggleButtonSetState(windowInfo->searchLiteralWordDefItem,
151 + False, False);
152 + XmToggleButtonSetState(windowInfo->searchCaseSenseWordDefItem,
153 + False, False);
154 + XmToggleButtonSetState(windowInfo->searchRegexDefItem,
155 + False, False);
156 + XmToggleButtonSetState(windowInfo->searchRegexNoCaseDefItem,
157 + False, False);
158 + XmToggleButtonSetState(windowInfo->searchRegexSmartCaseDefItem,
159 + True, False);
164 #ifdef REPLACE_SCOPE
165 static void replaceScopeWindowCB(Widget w, WindowInfo *window, caddr_t callData)
167 WindowInfo *win;
169 diff --quilt old/source/nedit.h new/source/nedit.h
170 --- old/source/nedit.h
171 +++ new/source/nedit.h
172 @@ -423,10 +423,11 @@ typedef struct _WindowInfo {
173 Widget searchLiteralDefItem;
174 Widget searchCaseSenseDefItem;
175 Widget searchLiteralWordDefItem;
176 Widget searchCaseSenseWordDefItem;
177 Widget searchRegexNoCaseDefItem;
178 + Widget searchRegexSmartCaseDefItem;
179 Widget searchRegexDefItem;
180 #ifdef REPLACE_SCOPE
181 Widget replScopeWinDefItem;
182 Widget replScopeSelDefItem;
183 Widget replScopeSmartDefItem;
184 diff --quilt old/source/preferences.c new/source/preferences.c
185 --- old/source/preferences.c
186 +++ new/source/preferences.c
187 @@ -117,10 +117,11 @@ enum fontStatus {GOOD_FONT, BAD_PRIMARY,
188 ** defined in search.h (!!)
190 static char *SearchMethodStrings[] = {
191 "Literal", "CaseSense", "RegExp",
192 "LiteralWord", "CaseSenseWord", "RegExpNoCase",
193 + "RegExpSmartCase",
194 NULL
197 #ifdef REPLACE_SCOPE
198 /* enumerated default scope for replace dialog if a selection exists when
199 @@ -2217,18 +2218,10 @@ int GetPrefOverrideVirtKeyBindings(void)
200 int GetPrefTruncSubstitution(void)
202 return PrefData.truncSubstitution;
206 -** If preferences don't get saved, ask the user on exit whether to save
208 -void MarkPrefsChanged(void)
210 - PrefsHaveChanged = True;
213 void SetPrefShowCursorline(Boolean value)
215 setIntPref(&PrefData.showCursorline, (int)value);
218 @@ -2236,10 +2229,18 @@ Boolean GetPrefShowCursorline(void)
220 return PrefData.showCursorline;
224 +** If preferences don't get saved, ask the user on exit whether to save
226 +void MarkPrefsChanged(void)
228 + PrefsHaveChanged = True;
232 ** Check if preferences have changed, and if so, ask the user if he wants
233 ** to re-save. Returns False if user requests cancelation of Exit (or whatever
234 ** operation triggered this call to be made).
236 int CheckPrefsChangesSaved(Widget dialogParent)
237 diff --quilt old/source/search.c new/source/search.c
238 --- old/source/search.c
239 +++ new/source/search.c
240 @@ -243,10 +243,11 @@ static void iSearchTryBeepOnWrap(WindowI
241 int beginPos, int startPos);
242 static void iSearchRecordLastBeginPos(WindowInfo *window, int direction,
243 int initPos);
244 static Boolean prefOrUserCancelsSubst(const Widget parent,
245 const Display* display);
246 +static void smartCaseToggle(const char *searchString, Widget toggleButton);
248 typedef struct _charMatchTable {
249 char c;
250 char match;
251 char direction;
252 @@ -279,10 +280,11 @@ static char *searchTypeStrings[] = {
253 "case", /* SEARCH_CASE_SENSE */
254 "regex", /* SEARCH_REGEX */
255 "word", /* SEARCH_LITERAL_WORD */
256 "caseWord", /* SEARCH_CASE_SENSE_WORD */
257 "regexNoCase", /* SEARCH_REGEX_NOCASE */
258 + "regexSmartCase", /* SEARCH_REGEX_SMARTCASE */
259 NULL
263 ** Window for which a search dialog callback is currently active. That window
264 @@ -368,10 +370,12 @@ static void initToggleButtons(int search
265 XmToggleButtonSetState(*wordToggle, False, False);
266 XtSetSensitive(*wordToggle, False);
268 break;
269 case SEARCH_REGEX_NOCASE:
270 + case SEARCH_REGEX_SMARTCASE:
271 + /* Smart case is no case for default setting */
272 *lastLiteralCase = False;
273 *lastRegexCase = False;
274 XmToggleButtonSetState(regexToggle, True, False);
275 XmToggleButtonSetState(caseToggle, False, False);
276 if (wordToggle) {
277 @@ -2406,12 +2410,20 @@ static int textFieldNonEmpty(Widget w)
278 return(nonEmpty);
281 static void rFindTextValueChangedCB(Widget w, WindowInfo *window, XKeyEvent *event)
283 + char *searchString = XmTextGetString(w);
285 window = WidgetToWindow(w);
286 UpdateReplaceActionButtons(window);
288 + /* Switch to case-sensitive as soon as the user enters an upper-case
289 + letter. We have check the whole string each time to catch clipboard
290 + or selection inserts. */
291 + smartCaseToggle(searchString, window->replaceCaseToggle);
292 + XtFree(searchString);
295 static void rFindArrowKeyCB(Widget w, WindowInfo *window, XKeyEvent *event)
297 KeySym keysym = XLookupKeysym(event, 0);
298 @@ -2501,12 +2513,20 @@ static void fUpdateActionButtons(WindowI
299 XtSetSensitive(window->findBtn, buttonState);
302 static void findTextValueChangedCB(Widget w, WindowInfo *window, XKeyEvent *event)
304 + char *searchString = XmTextGetString(w);
306 window = WidgetToWindow(w);
307 fUpdateActionButtons(window);
309 + /* Switch to case-sensitive as soon as the user enters an upper-case
310 + letter. We have check the whole string each time to catch clipboard
311 + or selection inserts. */
312 + smartCaseToggle(searchString, window->findCaseToggle);
313 + XtFree(searchString);
316 static void findArrowKeyCB(Widget w, WindowInfo *window, XKeyEvent *event)
318 KeySym keysym = XLookupKeysym(event, 0);
319 @@ -2874,14 +2894,17 @@ static void selectedSearchCB(Widget w, X
320 XtFree(value);
322 /* Use the passed method for searching, unless it is regex, since this
323 kind of search is by definition a literal search */
324 searchType = callDataItems->searchType;
325 - if (searchType == SEARCH_REGEX )
326 - searchType = SEARCH_CASE_SENSE;
327 - else if (searchType == SEARCH_REGEX_NOCASE)
328 - searchType = SEARCH_LITERAL;
329 + if (searchType == SEARCH_REGEX || searchType == SEARCH_REGEX_SMARTCASE) {
330 + /* Here smart case must be sensitive, because the user
331 + cannot modify the string to search for. */
332 + searchType = SEARCH_CASE_SENSE;
333 + } else if (searchType == SEARCH_REGEX_NOCASE) {
334 + searchType = SEARCH_LITERAL;
337 /* search for it in the window */
338 SearchAndSelect(window, callDataItems->direction, searchString,
339 searchType, callDataItems->searchWrap);
340 XtFree(callData);
341 @@ -2900,10 +2923,15 @@ void BeginISearch(WindowInfo *window, in
342 case toggles are not reset to their default state when the incremental
343 search bar is redisplayed. I'm not sure whether this is the best
344 choice. If not, an initToggleButtons() call should be inserted
345 here. But in that case, it might be appropriate to have different
346 default search modes for i-search and replace/find. */
348 + /* The 'Case' toggle button must be deactivated for smart cases. */
349 + if (GetPrefSearch() == SEARCH_REGEX_SMARTCASE) {
350 + XmToggleButtonSetState(window->iSearchCaseToggle, False, False);
352 TempShowISearch(window, TRUE);
353 XmProcessTraversal(window->iSearchText, XmTRAVERSE_CURRENT);
357 @@ -3193,10 +3221,16 @@ static void iSearchTextValueChangedCB(Wi
358 window = WidgetToWindow(w);
360 /* Fetch the string, search type and direction from the incremental
361 search bar widgets at the top of the window */
362 searchString = XmTextGetString(window->iSearchText);
364 + /* Switch to case-sensitive as soon as the user enters an upper-case
365 + letter. We have check the whole string each time to catch clipboard
366 + or selection inserts. */
367 + smartCaseToggle(searchString, window->iSearchCaseToggle);
369 if(XmToggleButtonGetState(window->iSearchCaseToggle)) {
370 if(XmToggleButtonGetState(window->iSearchRegexToggle))
371 searchType = SEARCH_REGEX;
372 else
373 searchType = SEARCH_CASE_SENSE;
374 @@ -4877,11 +4911,13 @@ static char *directionArg(int direction)
376 ** Checks whether a search mode in one of the regular expression modes.
378 static int isRegexType(int searchType)
380 - return searchType == SEARCH_REGEX || searchType == SEARCH_REGEX_NOCASE;
381 + return searchType == SEARCH_REGEX
382 + || searchType == SEARCH_REGEX_NOCASE
383 + || searchType == SEARCH_REGEX_SMARTCASE;
387 ** Returns the default flags for regular expression matching, given a
388 ** regular expression search mode.
389 @@ -5020,5 +5056,22 @@ static void iSearchCaseToggleCB(Widget w
390 if(XmToggleButtonGetState(window->iSearchRegexToggle))
391 window->iSearchLastRegexCase = searchCaseSense;
392 else
393 window->iSearchLastLiteralCase = searchCaseSense;
397 +** Will search a string for uppercase characters. If there is one,
398 +** The toggleButton will be set to True.
400 +static void smartCaseToggle(const char *searchString, Widget toggleButton)
402 + if (GetPrefSearch() == SEARCH_REGEX_SMARTCASE) {
403 + int i;
404 + for (i = 0; i < strlen(searchString); i++) {
405 + if (isupper(*(searchString + i))) {
406 + XmToggleButtonSetState(toggleButton, True, False);
407 + break;
412 diff --quilt old/source/search.h new/source/search.h
413 --- old/source/search.h
414 +++ new/source/search.h
415 @@ -88,10 +88,11 @@ Boolean WindowCanBeClosed(WindowInfo *wi
418 enum SearchType {
419 SEARCH_LITERAL, SEARCH_CASE_SENSE, SEARCH_REGEX,
420 SEARCH_LITERAL_WORD, SEARCH_CASE_SENSE_WORD, SEARCH_REGEX_NOCASE,
421 + SEARCH_REGEX_SMARTCASE,
422 N_SEARCH_TYPES /* must be last in enum SearchType */ };
424 #ifdef REPLACE_SCOPE
425 /* Scope on which the replace operations apply */
426 enum ReplaceScope { REPL_SCOPE_WIN, REPL_SCOPE_SEL, REPL_SCOPE_MULTI };
427 diff --quilt old/source/window.c new/source/window.c
428 --- old/source/window.c
429 +++ new/source/window.c
430 @@ -488,11 +488,12 @@ WindowInfo *CreateWindow(const char *nam
432 window->iSearchRegexToggle = XtVaCreateManagedWidget("iSearchREToggle",
433 xmToggleButtonWidgetClass, window->iSearchForm,
434 XmNlabelString, s1=XmStringCreateSimple("RegExp"),
435 XmNset, GetPrefSearch() == SEARCH_REGEX_NOCASE
436 - || GetPrefSearch() == SEARCH_REGEX,
437 + || GetPrefSearch() == SEARCH_REGEX
438 + || GetPrefSearch() == SEARCH_REGEX_SMARTCASE,
439 XmNtopAttachment, XmATTACH_FORM,
440 XmNbottomAttachment, XmATTACH_FORM,
441 XmNtopOffset, 1, /* see openmotif note above */
442 XmNrightAttachment, XmATTACH_WIDGET,
443 XmNrightWidget, window->iSearchCaseToggle,