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