]> git.lyx.org Git - lyx.git/blob - src/lyxfr1.C
GUI-indep toolbar and menus mostly work !
[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();
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(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.c_str());
140                 bv->text->
141                         SetSelectionOverString(bv, replacestring.c_str());
142                 bv->update(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(BufferView::SELECT|BufferView::FITCUR);
180                         bv->toggleSelection(false);
181                         bv->text->
182                                 ReplaceSelectionWithString(bv, replacestring.c_str());
183                         bv->text->
184                                 SetSelectionOverString(bv, replacestring.c_str());
185                         bv->update(BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE); 
186                         ++replace_count;
187                 }
188         } while (SearchCB(true));
189         if( replace_count == 0 ) {
190                 LyXBell();      
191                 bv->owner()->getMiniBuffer()->Set(
192                         _("String not found!"));
193         } else {
194                 if (replace_count == 1) {
195                         bv->owner()->getMiniBuffer()->Set(
196                                 _("1 string has been replaced."));
197                 } else {
198                         string str = tostr(replace_count);
199                         str += _(" strings have been replaced.");
200                         bv->owner()->getMiniBuffer()->Set(str);
201                 }
202         }
203 }
204
205
206 bool LyXFindReplace::SearchCB(bool fForward)
207 {
208         // store search direction
209         searchForward = fForward;
210         
211         if (!bv->available())
212                 return false;
213    
214         bv->hideCursor();
215         bv->update(BufferView::SELECT|BufferView::FITCUR);
216         LyXText * ltCur = bv->text;
217         if (ltCur->selection) 
218                 ltCur->cursor = fForward ? ltCur->sel_end_cursor :
219                 ltCur->sel_start_cursor;
220
221         iLenSelected = SF.SearchString().length();
222         bool result;
223    
224         if (!SF.ValidSearchData() ||
225             (fForward ? SearchForward(bv):SearchBackward(bv))) {
226                 bv->update(BufferView::SELECT|BufferView::FITCUR);
227
228                 // clear the selection (if there is any) 
229                 bv->toggleSelection();
230                 bv->text->ClearSelection();
231
232                 // set the new selection 
233                 SetSelectionOverLenChars(bv, iLenSelected);
234                 bv->toggleSelection(false);
235                 bv->owner()->getMiniBuffer()->Set(_("Found."));
236                 result = true;
237         } else {
238                 LyXBell();
239                 bv->owner()->getMiniBuffer()->Set(_("String not found!"));
240                 result = false;
241         }
242
243         if (bv->focus())
244                 bv->showCursor();
245
246         return result;
247 }
248
249
250 // if the string can be found: return true and set the cursor to
251 // the new position 
252 // (was: LyXText::SearchForward(char const* string) in text2.C )
253 bool LyXFindReplace::SearchForward(BufferView * bv)
254 {
255         LyXParagraph * par = bv->text->cursor.par();
256         LyXParagraph::size_type pos = bv->text->cursor.pos();
257
258         while (par && !IsSearchStringInText(par, pos)) {
259                 if (pos < par->Last() - 1)
260                         ++pos;
261                 else {
262                         pos = 0;
263                         par = par->Next();
264                 }
265         }
266         if (par) {
267                 bv->text->SetCursor(bv, par, pos);
268                 return true;
269         } else
270                 return false;
271 }
272
273
274 // if the string can be found: return true and set the cursor to
275 // the new position 
276 // (was: LyXText::SearchBackward(char const* string) in text2.C )
277 bool LyXFindReplace::SearchBackward(BufferView * bv)
278 {
279         LyXParagraph * par = bv->text->cursor.par();
280         int pos = bv->text->cursor.pos();
281
282         do {
283                 if (pos > 0)
284                         --pos;
285                 else {
286                         // We skip empty paragraphs (Asger)
287                         do {
288                                 par = par->Previous();
289                                 if (par)
290                                         pos = par->Last() - 1;
291                         } while (par && pos < 0);
292                 }
293         } while (par && !IsSearchStringInText(par, pos));
294   
295         if (par) {
296                 bv->text->SetCursor(bv, par, pos);
297                 return true;
298         } else
299                 return false;
300 }
301
302
303 /* Compares 2 char values. 
304 return value is
305     > 0 if chSearch > ch2
306     = 0 if chSearch == ch2
307     < 0 if chSearch < ch2
308 */
309 int LyXFindReplace::CompareChars(char chSearch, char chText) const
310 {
311         if (SF.CaseSensitive())
312                 return (chSearch - chText);
313         return (toupper(chSearch) - toupper(chText));
314 }
315
316
317 // returns true if the search string is at the specified position 
318 // (Copied from the original "LyXText::IsStringInText" in text2.C )
319 bool LyXFindReplace::IsSearchStringInText(LyXParagraph * par,
320                                           LyXParagraph::size_type pos) const
321 {
322         if (!par) return false;
323
324         char chSrch = 0;
325         char chText;
326         bool fPrevIsSpace = false;
327         int iText = 0;
328         string::size_type iSrch = 0;
329         while (pos + iText < par->Last() && 
330                iSrch < SF.SearchString().length()) {
331                 chSrch = SF.SearchString()[iSrch];
332                 chText = par->GetChar(pos+iText);
333                 if (chText == ' ') {
334                         if (fPrevIsSpace) {
335                                 ++iText;  // next Text pos
336                                 continue; // same search pos
337                         }
338                         fPrevIsSpace = true;
339                 } else
340                         fPrevIsSpace = false;
341                 if (CompareChars(chSrch, chText) != 0)
342                         break;
343                 
344                 ++iSrch;
345                 ++iText;
346         }
347
348         if (iSrch < SF.SearchString().length())
349                 return false;
350
351         if (!SF.MatchWord() 
352             || ((pos <= 0 || !IsLetterCharOrDigit(par->GetChar(pos - 1)))
353                 && (pos + iText >= par->Last() 
354                     || !IsLetterCharOrDigit(par->GetChar(pos + iText))))) {
355                 iLenSelected = iText;
356                 return true;
357         }
358
359         return false;
360 }