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