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