]> git.lyx.org Git - features.git/blob - src/lyxfr1.C
6143c9d8f1aebd05ab9c0af1b2af583e41ea3df0
[features.git] / src / lyxfr1.C
1 /* This file is part of
2  * ====================================================== 
3  * 
4  *           LyX, The Document Processor
5  *       
6  *           Copyright 1995 Matthias Ettrich,
7  *           Copyright 1995-1999 The LyX Team.
8  *
9  * ====================================================== */
10
11 #include <config.h>
12
13 #include <cctype>
14 #include <cstring>
15 #include <cstdlib>
16
17 #ifdef __GNUG__
18 #pragma implementation
19 #endif
20
21 #include "LString.h"
22 #include "lyx_main.h"
23 #include FORMS_H_LOCATION
24 #include "form1.h"
25 #include "lyxfr0.h"
26 #include "lyxfr1.h"
27 #include "lyxfunc.h"
28 #include "lyxscreen.h"
29 #include "debug.h"
30 #include "lyxtext.h"
31 #include "gettext.h"
32 #include "LyXView.h"
33 #include "lyx_gui_misc.h"
34 #include "minibuffer.h"
35 #include "support/lstrings.h"
36 #include "support/textutils.h"
37
38 extern BufferView * current_view; // called too many times in this file...
39
40 // Maximum length copied from the current selection to the search string
41 const int LYXSEARCH_MAXLEN =  128;
42
43 // function prototypes
44
45 // If nothing selected, select the word at the cursor.
46 // Returns the current selection
47 // Note: this function should be in LyXText!
48 string const GetSelectionOrWordAtCursor(LyXText * lt);
49
50 // Returns the current selection. If nothing is selected or if the selection
51 // spans 2 paragraphs, an empty string is returned.
52 string const GetCurrentSelectionAsString(LyXText * lt);
53
54 // This is a copy of SetSelectionOverString from text.C
55 // It does the same, but uses only the length as a parameter
56 void SetSelectionOverLenChars(LyXText * lt, int len);
57
58 //-------------------------------------------------------------
59
60
61
62 // Returns the current selection. If nothing is selected or if the selection
63 // spans 2 paragraphs, an empty string is returned.
64 string const GetCurrentSelectionAsString(LyXText * lt) 
65 {
66         char sz[LYXSEARCH_MAXLEN];
67         sz[0] = 0;
68         
69         LyXParagraph * par = lt->cursor.par;
70         if (lt->selection && (lt->sel_cursor.par == par)) {
71                 // (selected) and (begin/end in same paragraph)
72                 LyXParagraph::size_type pos = 
73                         lt->sel_start_cursor.pos;
74                 LyXParagraph::size_type endpos = 
75                         lt->sel_end_cursor.pos;
76                 int i = 0;
77                 bool fPrevIsSpace = false;
78                 char ch;
79                 while ((i < LYXSEARCH_MAXLEN - 2) && 
80                        (pos < par->Last()) && (pos < endpos)) {
81                         ch = par->GetChar(pos);
82
83                         //HB??: Maybe (ch <= ' ') 
84                         if ((ch == ' ') || (ch <= LyXParagraph::META_INSET)) {
85                                 // consecutive spaces --> 1 space char
86                                 if (fPrevIsSpace) {
87                                         ++pos;          // Next text pos
88                                         continue;       // same search pos
89                                 }
90                                 sz[i] = ' ';
91                                 fPrevIsSpace = true;
92                         } else {
93                                 sz[i] = ch;
94                                 fPrevIsSpace = false;
95                         }
96                         ++pos;
97                         ++i;
98                 } 
99                 sz[i] = 0;
100         }
101         return string(sz);
102 }
103
104
105 // If nothing selected, select the word at the cursor.
106 // Returns the current selection
107 string const GetSelectionOrWordAtCursor(LyXText * lt) 
108 {
109         lt->SelectWordWhenUnderCursor();
110         return GetCurrentSelectionAsString(lt);
111 }
112
113
114 // This is a copy of SetSelectionOverString from text.C
115 // It does the same, but uses only the length as a parameter
116 void SetSelectionOverLenChars(LyXText * lt, int len)
117 {
118         lt->sel_cursor = lt->cursor;
119         for (int i = 0; i < len; ++i)
120                 lt->CursorRight();
121         lt->SetSelection();
122 }
123
124
125 //------------------------------
126
127 void LyXFindReplace1::StartSearch()
128 {
129         LyXFindReplace0::StartSearch();
130         SetReplaceEnabled(!current_view->buffer()->isReadonly());
131         searchForward = true;
132         if (lsSearch.empty()) 
133                 SetSearchString(GetSelectionOrWordAtCursor(current_view->text));
134 }       
135
136
137 // TODO?: the user can insert multiple spaces with this
138 // routine (1999-01-11, dnaber)
139 void LyXFindReplace1::SearchReplaceCB()
140 {
141         if (!current_view->getScreen()) return;
142         if (current_view->buffer()->isReadonly()) return;
143         
144         // CutSelection cannot cut a single space, so we have to stop
145         // in order to avoid endless loop :-(
146         ReInitFromForm();
147         if (SearchString().length() == 0 || (SearchString().length() == 1
148                                              && SearchString()[0] == ' ') ) {
149                 WriteAlert(_("Sorry!"), _("You cannot replace a single space, "
150                                           "nor an empty character."));
151                 return;
152         }
153         
154         string const replacestring = ReplaceString();
155
156         current_view->getScreen()->HideCursor();
157         current_view->update(-2);
158
159         LyXText * ltCur = current_view->text;   
160         if (ltCur->selection) {
161                 // clear the selection (if there is any) 
162                 current_view->getScreen()->ToggleSelection(false);
163                 current_view->text->
164                         ReplaceSelectionWithString(replacestring.c_str());
165                 current_view->text->
166                         SetSelectionOverString(replacestring.c_str());
167                 current_view->update(1);
168         }
169         
170         // jump to next match:
171         SearchCB( searchForward );
172 }
173
174
175 // replaces all occurences of a string (1999-01-15, dnaber@mini.gt.owl.de)
176 void LyXFindReplace1::SearchReplaceAllCB()
177 {
178         if (!current_view->getScreen()) return;
179         if (current_view->buffer()->isReadonly()) return;
180
181         // CutSelection cannot cut a single space, so we have to stop
182         // in order to avoid endless loop :-(
183         ReInitFromForm();
184         if (SearchString().length() == 0 || (SearchString().length() == 1
185                                              && SearchString()[0] == ' ') ) {
186                 WriteAlert(_("Sorry!"), _("You cannot replace a single space, "
187                                           "nor an empty character."));
188                 return;
189         }
190
191         string const replacestring = ReplaceString();
192
193         current_view->getScreen()->HideCursor();
194
195         // start at top
196         current_view->text->ClearSelection();
197         current_view->text->CursorTop();
198
199         int replace_count = 0;
200         LyXText * ltCur;
201         do {
202                 ltCur = current_view->text;     
203                 if (ltCur->selection) {
204                         current_view->update(-2);
205                         current_view->getScreen()->ToggleSelection(false);
206                         current_view->text->
207                                 ReplaceSelectionWithString(replacestring.c_str());
208                         current_view->text->
209                                 SetSelectionOverString(replacestring.c_str());
210                         current_view->update(1); 
211                         ++replace_count;
212                 }
213         } while( SearchCB(true) );
214         if( replace_count == 0 ) {
215                 LyXBell();      
216                 current_view->owner()->getMiniBuffer()->Set(
217                         _("String not found!"));
218         } else {
219                 if (replace_count == 1) {
220                         current_view->owner()->getMiniBuffer()->Set(
221                                 _("1 string has been replaced."));
222                 } else {
223                         string str = tostr(replace_count);
224                         str += _(" strings have been replaced.");
225                         current_view->owner()->getMiniBuffer()->Set(str);
226                 }
227         }
228 }
229
230
231 bool LyXFindReplace1::SearchCB(bool fForward)
232 {
233         // store search direction
234         searchForward = fForward;
235         
236         if (!current_view->getScreen())
237                 return false;
238    
239         current_view->getScreen()->HideCursor();
240         current_view->update(-2);
241         LyXText * ltCur = current_view->text;
242         if (ltCur->selection) 
243                 ltCur->cursor = fForward ? ltCur->sel_end_cursor :
244                 ltCur->sel_start_cursor;
245
246         ReInitFromForm();
247         iLenSelected = SearchString().length();
248         bool result;
249    
250         if (!ValidSearchData() ||
251             (fForward ? SearchForward(ltCur) : SearchBackward(ltCur))) {
252                 current_view->update(-2);
253
254                 // clear the selection (if there is any) 
255                 current_view->getScreen()->ToggleSelection();
256                 current_view->text->ClearSelection();
257
258                 // set the new selection 
259                 SetSelectionOverLenChars(current_view->text, iLenSelected);
260                 current_view->getScreen()->ToggleSelection(false);
261                 current_view->owner()->getMiniBuffer()->Set(_("Found."));
262                 result = true;
263         } else {
264                 LyXBell();
265                 current_view->owner()->getMiniBuffer()->Set(_("String not found!"));
266                 result = false;
267         }
268
269 #ifdef NEW_WA
270         if (current_view->focus())
271                 current_view->getScreen()->ShowCursor();
272 #else
273         if (current_view->getWorkArea()->focus)
274                 current_view->getScreen()->ShowCursor();
275 #endif
276         return result;
277 }
278
279
280 // if the string can be found: return true and set the cursor to
281 // the new position 
282 // (was: LyXText::SearchForward(char const* string) in text2.C )
283 bool LyXFindReplace1::SearchForward(LyXText * lt)
284 {
285         LyXParagraph * par = lt->cursor.par;
286         LyXParagraph::size_type pos = lt->cursor.pos;
287
288         while (par && !IsSearchStringInText(par, pos)) {
289                 if (pos < par->Last() - 1)
290                         ++pos;
291                 else {
292                         pos = 0;
293                         par = par->Next();
294                 }
295         }
296         if (par) {
297                 lt->SetCursor(par, pos);
298                 return true;
299         } else
300                 return false;
301 }
302
303
304 // if the string can be found: return true and set the cursor to
305 // the new position 
306 // (was: LyXText::SearchBackward(char const* string) in text2.C )
307 bool LyXFindReplace1::SearchBackward(LyXText * lt)
308 {
309         LyXParagraph * par = lt->cursor.par;
310         int pos = lt->cursor.pos;
311
312         do {
313                 if (pos > 0)
314                         --pos;
315                 else {
316                         // We skip empty paragraphs (Asger)
317                         do {
318                                 par = par->Previous();
319                                 if (par)
320                                         pos = par->Last() - 1;
321                         } while (par && pos < 0);
322                 }
323         } while (par && !IsSearchStringInText(par, pos));
324   
325         if (par) {
326                 lt->SetCursor(par, pos);
327                 return true;
328         } else
329                 return false;
330 }
331
332
333 /* Compares 2 char values. 
334 return value is
335     > 0 if chSearch > ch2
336     = 0 if chSearch == ch2
337     < 0 if chSearch < ch2
338 */
339 int LyXFindReplace1::CompareChars(char chSearch, char chText)
340 {
341         if (CaseSensitive())
342                 return (chSearch - chText);
343         return (toupper(chSearch) - toupper(chText));
344 }
345
346
347 // returns true if the search string is at the specified position 
348 // (Copied from the original "LyXText::IsStringInText" in text2.C )
349 bool LyXFindReplace1::IsSearchStringInText(LyXParagraph * par,
350                                            LyXParagraph::size_type pos)
351 {
352         if (!par) return false;
353
354         char chSrch = 0;
355         char chText;
356         bool fPrevIsSpace = false;
357         int iText = 0;
358         string::size_type iSrch = 0;
359         while (pos + iText < par->Last() && 
360                iSrch < SearchString().length()) {
361                 chSrch = SearchString()[iSrch];
362                 chText = par->GetChar(pos+iText);
363                 if (chText == ' ') {
364                         if (fPrevIsSpace) {
365                                 ++iText;  // next Text pos
366                                 continue; // same search pos
367                         }
368                         fPrevIsSpace = true;
369                 } else
370                         fPrevIsSpace = false;
371                 if (CompareChars(chSrch, chText) != 0)
372                         break;
373                 
374                 ++iSrch;
375                 ++iText;
376         }
377
378         if (iSrch < SearchString().length())
379                 return false;
380
381         if (!MatchWord() 
382             || ((pos <= 0 || !IsLetterCharOrDigit(par->GetChar(pos - 1)))
383                 && (pos + iText >= par->Last() 
384                     || !IsLetterCharOrDigit(par->GetChar(pos + iText))))) {
385                 iLenSelected = iText;
386                 return true;
387         }
388
389         return false;
390 }