]> git.lyx.org Git - lyx.git/blob - src/lyxfr1.C
5e8461fee0a3a9310998374302eb3cc293bdf07e
[lyx.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         if (current_view->getWorkArea()->focus)
270                 current_view->getScreen()->ShowCursor();
271
272         return result;
273 }
274
275
276 // if the string can be found: return true and set the cursor to
277 // the new position 
278 // (was: LyXText::SearchForward(char const* string) in text2.C )
279 bool LyXFindReplace1::SearchForward(LyXText * lt)
280 {
281         LyXParagraph * par = lt->cursor.par;
282         LyXParagraph::size_type pos = lt->cursor.pos;
283
284         while (par && !IsSearchStringInText(par, pos)) {
285                 if (pos < par->Last() - 1)
286                         ++pos;
287                 else {
288                         pos = 0;
289                         par = par->Next();
290                 }
291         }
292         if (par) {
293                 lt->SetCursor(par, pos);
294                 return true;
295         } else
296                 return false;
297 }
298
299
300 // if the string can be found: return true and set the cursor to
301 // the new position 
302 // (was: LyXText::SearchBackward(char const* string) in text2.C )
303 bool LyXFindReplace1::SearchBackward(LyXText * lt)
304 {
305         LyXParagraph * par = lt->cursor.par;
306         int pos = lt->cursor.pos;
307
308         do {
309                 if (pos > 0)
310                         --pos;
311                 else {
312                         // We skip empty paragraphs (Asger)
313                         do {
314                                 par = par->Previous();
315                                 if (par)
316                                         pos = par->Last() - 1;
317                         } while (par && pos < 0);
318                 }
319         } while (par && !IsSearchStringInText(par, pos));
320   
321         if (par) {
322                 lt->SetCursor(par, pos);
323                 return true;
324         } else
325                 return false;
326 }
327
328
329 /* Compares 2 char values. 
330 return value is
331     > 0 if chSearch > ch2
332     = 0 if chSearch == ch2
333     < 0 if chSearch < ch2
334 */
335 int LyXFindReplace1::CompareChars(char chSearch, char chText)
336 {
337         if (CaseSensitive())
338                 return (chSearch - chText);
339         return (toupper(chSearch) - toupper(chText));
340 }
341
342
343 // returns true if the search string is at the specified position 
344 // (Copied from the original "LyXText::IsStringInText" in text2.C )
345 bool LyXFindReplace1::IsSearchStringInText(LyXParagraph * par,
346                                            LyXParagraph::size_type pos)
347 {
348         if (!par) return false;
349
350         char chSrch = 0;
351         char chText;
352         bool fPrevIsSpace = false;
353         int iText = 0;
354         string::size_type iSrch = 0;
355         while (pos + iText < par->Last() && 
356                iSrch < SearchString().length()) {
357                 chSrch = SearchString()[iSrch];
358                 chText = par->GetChar(pos+iText);
359                 if (chText == ' ') {
360                         if (fPrevIsSpace) {
361                                 ++iText;  // next Text pos
362                                 continue; // same search pos
363                         }
364                         fPrevIsSpace = true;
365                 } else
366                         fPrevIsSpace = false;
367                 if (CompareChars(chSrch, chText) != 0)
368                         break;
369                 
370                 ++iSrch;
371                 ++iText;
372         }
373
374         if (iSrch < SearchString().length())
375                 return false;
376
377         if (!MatchWord() 
378             || ((pos <= 0 || !IsLetterCharOrDigit(par->GetChar(pos - 1)))
379                 && (pos + iText >= par->Last() 
380                     || !IsLetterCharOrDigit(par->GetChar(pos + iText))))) {
381                 iLenSelected = iText;
382                 return true;
383         }
384
385         return false;
386 }