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