This commit was manufactured by cvs2svn to create tag
[lyx.git] / src / lyxfr1.C
blob233ac3cfbb44faf2b988da32438ed47f32417236
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  * ======================================================*/
11 #include <config.h>
13 #include <cctype>
14 #include <cstring>
15 #include <cstdlib>
17 #ifdef __GNUG__
18 #pragma implementation
19 #endif
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"
37 extern BufferView *current_view; // called too many times in this file...
38 extern MiniBuffer *minibuffer;
40 // Maximum length copied from the current selection to the search string
41 const int LYXSEARCH_MAXLEN =  128;
43 // function prototypes
45 bool IsLetterCharOrDigit(char ch);
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);
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);
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);
60 //-------------------------------------------------------------
62 bool IsLetterCharOrDigit(char ch)
64         return IsLetterChar(ch) || isdigit(ch);
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) 
72         char            sz[LYXSEARCH_MAXLEN];
74         sz[0] = 0;
75         LyXParagraph * par = lt->cursor.par;
76         if (lt->selection && (lt->sel_cursor.par == par)) {
77                 // (selected) and (begin/end in same paragraph)
78 #ifdef NEW_TEXT
79                 LyXParagraph::size_type pos =
80                         lt->sel_start_cursor.pos;
81                 LyXParagraph::size_type endpos =
82                         lt->sel_end_cursor.pos;
83 #else
84                 int pos =
85                         lt->sel_start_cursor.pos;
86                 int endpos =
87                         lt->sel_end_cursor.pos;
88 #endif
89                 int i = 0;
90                 bool fPrevIsSpace = false;
91                 char ch;
92                 while ((i < LYXSEARCH_MAXLEN-2) && 
93                         (pos < par->Last()) && (pos < endpos)) {
94                         ch = par->GetChar(pos);
96                         //HB??: Maybe (ch <= ' ') 
97                         if ((ch == ' ') || (ch <= LYX_META_INSET)) {
98                                 // consecutive spaces --> 1 space char
99                                 if (fPrevIsSpace) {
100                                         pos++;          // Next text pos
101                                         continue;       // same search pos
102                                 }
103                                 sz[i] = ' ';
104                                 fPrevIsSpace = true;
105                         } else {
106                                 sz[i] = ch;
107                                 fPrevIsSpace = false;
108                         }
109                         pos++;
110                         i++;
111                 } 
112                 sz[i] = 0;
113         }
114         return string(sz);
118 // If nothing selected, select the word at the cursor.
119 // Returns the current selection
120 string const GetSelectionOrWordAtCursor(LyXText *lt) 
122         lt->SelectWordWhenUnderCursor();
123         return GetCurrentSelectionAsString(lt);
127 // This is a copy of SetSelectionOverString from text.C
128 // It does the same, but uses only the length as a parameter
129 void SetSelectionOverLenChars(LyXText *lt, int len)
131         lt->sel_cursor = lt->cursor;
132         int i;
133         for (i=0; i < len; i++)
134                 lt->CursorRight();
135         lt->SetSelection();
139 //------------------------------
141 void LyXFindReplace1::StartSearch()
143         LyXFindReplace0::StartSearch();
144         SetReplaceEnabled(!current_view->currentBuffer()->isReadonly());
145         searchForward = true;
146         if (lsSearch.empty()) 
147                 SetSearchString(GetSelectionOrWordAtCursor(current_view->currentBuffer()->text));
148 }       
151 // TODO?: the user can insert multiple spaces with this routine (1999-01-11, dnaber)
152 void LyXFindReplace1::SearchReplaceCB()
154         LyXText         *ltCur;
156         if (!current_view->getScreen())
157                 return;
158         if (current_view->currentBuffer()->isReadonly())
159                 return;
160         
161         // CutSelection cannot cut a single space, so we have to stop
162         // in order to avoid endless loop :-(
163         ReInitFromForm();
164         if (SearchString().length() == 0 || (SearchString().length() == 1
165                                              && SearchString()[0] == ' ') ) {
166                 WriteAlert(_("Sorry!"), _("You cannot replace a single space, "
167                                           "nor an empty character."));
168                 return;
169         }
170         
171         string const replacestring = ReplaceString();
173         current_view->getScreen()->HideCursor();
174         current_view->currentBuffer()->update(-2);
176         ltCur = current_view->currentBuffer()->text;    
177         if (ltCur->selection) {
178                 // clear the selection (if there is any) 
179                 current_view->getScreen()->ToggleSelection(false);
180                 current_view->currentBuffer()->text->
181                         ReplaceSelectionWithString(replacestring.c_str());
182                 current_view->currentBuffer()->text->
183                         SetSelectionOverString(replacestring.c_str());
184                 current_view->currentBuffer()->update(1);
185         }
186         
187         // jump to next match:
188         SearchCB( searchForward );
192 // replaces all occurences of a string (1999-01-15, dnaber@mini.gt.owl.de)
193 void LyXFindReplace1::SearchReplaceAllCB()
195         LyXText         *ltCur;
197         if (!current_view->getScreen())
198                 return;
199         if (current_view->currentBuffer()->isReadonly())
200                 return;
202         // CutSelection cannot cut a single space, so we have to stop
203         // in order to avoid endless loop :-(
204         ReInitFromForm();
205         if (SearchString().length() == 0 || (SearchString().length() == 1
206                                              && SearchString()[0] == ' ') ) {
207                 WriteAlert(_("Sorry!"), _("You cannot replace a single space, "
208                                           "nor an empty character."));
209                return;
210        }
212         string const replacestring = ReplaceString();
214         current_view->getScreen()->HideCursor();
216         // start at top
217         current_view->currentBuffer()->text->ClearSelection();
218         current_view->currentBuffer()->text->CursorTop();
220         int replace_count = 0;
221         do {
222                 ltCur = current_view->currentBuffer()->text;    
223                 if (ltCur->selection) {
224                         current_view->currentBuffer()->update(-2);
225                         current_view->getScreen()->ToggleSelection(false);
226                         current_view->currentBuffer()->text->
227                                 ReplaceSelectionWithString(replacestring.c_str());
228                         current_view->currentBuffer()->text->
229                                 SetSelectionOverString(replacestring.c_str());
230                         current_view->currentBuffer()->update(1); 
231                         replace_count++;
232                 }
233         } while( SearchCB(true) );
235         if( replace_count == 0 ) {
236                 LyXBell();      
237                 minibuffer->Set(_("String not found!"));
238         } else {
239                 if( replace_count == 1 ) {
240                         minibuffer->Set(_("1 string has been replaced."));
241                 } else {
242                         string str = tostr(replace_count);
243                         str += _(" strings have been replaced.");
244                         minibuffer->Set(str);
245                 }
246         }
250 bool LyXFindReplace1::SearchCB(bool fForward)
252         LyXText         *ltCur;
253         bool result;
255         // store search direction
256         searchForward = fForward;
257         
258         if (!current_view->getScreen())
259                 return(false);
260    
261         current_view->getScreen()->HideCursor();
262         current_view->currentBuffer()->update(-2);
263         ltCur = current_view->currentBuffer()->text;
264         if (ltCur->selection) 
265                 ltCur->cursor = fForward ? ltCur->sel_end_cursor :
266                                                  ltCur->sel_start_cursor;
268         ReInitFromForm();
269         iLenSelected = SearchString().length();
270    
271         if (!ValidSearchData() ||
272             (fForward ? SearchForward(ltCur) : SearchBackward(ltCur))) {
273                 current_view->currentBuffer()->update(-2);
275                 // clear the selection (if there is any) 
276                 current_view->getScreen()->ToggleSelection();
277                 current_view->currentBuffer()->text->ClearSelection();
279                 // set the new selection 
280                 SetSelectionOverLenChars(current_view->currentBuffer()->text, iLenSelected);
281                 current_view->getScreen()->ToggleSelection(false);
282                 minibuffer->Set(_("Found."));
283                 result = true;
284         } else {
285                 LyXBell();
286                 minibuffer->Set(_("String not found!"));
287                 result = false;
288         }
289    
290         if (current_view->getWorkArea()->focus)
291                 current_view->getScreen()->ShowCursor();
293         return(result);
297 // if the string can be found: return true and set the cursor to
298 // the new position 
299 // (was: LyXText::SearchForward(char const* string) in text2.C )
300 bool LyXFindReplace1::SearchForward(LyXText * lt)
302         LyXParagraph * par = lt->cursor.par;
303 #ifdef NEW_TEXT
304         LyXParagraph::size_type pos = lt->cursor.pos;
305 #else
306         int pos = lt->cursor.pos;
307 #endif
309         while (par && !IsSearchStringInText(par,pos)) {
310                 if (pos<par->Last()-1)
311                         pos++;
312                 else {
313                         pos = 0;
314                         par = par->Next();
315                 }
316         }
317         if (par) {
318                 lt->SetCursor(par,pos);
319                 return true;
320         } else
321                 return false;
325 // if the string can be found: return true and set the cursor to
326 // the new position 
327 // (was: LyXText::SearchBackward(char const* string) in text2.C )
328 bool LyXFindReplace1::SearchBackward(LyXText *lt)
330         LyXParagraph *par = lt->cursor.par;
331         int pos = lt->cursor.pos;
333         do {
334                 if (pos>0)
335                         pos--;
336                 else {
337                         // We skip empty paragraphs (Asger)
338                         do {
339                                 par = par->Previous();
340                                 if (par)
341                                         pos = par->Last()-1;
342                         } while (par && pos<0);
343                 }
344         } while (par && !IsSearchStringInText(par,pos));
345   
346         if (par) {
347                 lt->SetCursor(par,pos);
348                 return true;
349         } else
350                 return false;
354 /* Compares 2 char values. 
355 return value is
356     > 0 if chSearch > ch2
357     = 0 if chSearch == ch2
358     < 0 if chSearch < ch2
360 int LyXFindReplace1::CompareChars(char chSearch, char chText)
362         if (CaseSensitive())
363                 return (chSearch - chText);
364         return (toupper(chSearch) - toupper(chText));
368 // returns true if the search string is at the specified position 
369 // (Copied from the original "LyXText::IsStringInText" in text2.C )
370 #ifdef NEW_TEXT
371 bool LyXFindReplace1::IsSearchStringInText(LyXParagraph * par,
372                                            LyXParagraph::size_type pos)
373 #else
374 bool LyXFindReplace1::IsSearchStringInText(LyXParagraph * par,
375                                            int pos)
376 #endif
378         char            chSrch = 0;
379         char            chText;
381         if (!par) 
382                 return false;
384         bool fPrevIsSpace = false;
385         int iText = 0;
386         string::size_type iSrch = 0;
387         while (pos + iText < par->Last() && 
388                iSrch < SearchString().length()) {
389                 chSrch = SearchString()[iSrch];
390                 chText = par->GetChar(pos+iText);
391                 if (chText == ' ') {
392                         if (fPrevIsSpace) {
393                                 iText++;  // next Text pos
394                                 continue; // same search pos
395                         }
396                         fPrevIsSpace = true;
397                 } else
398                         fPrevIsSpace = false;
399                 if (CompareChars(chSrch, chText) != 0)
400                         break;
401                 
402                 ++iSrch;
403                 ++iText;
404         }
406         if (iSrch < SearchString().length())
407                 return false;
409         if (!MatchWord() 
410             || ((pos <= 0 || !IsLetterCharOrDigit(par->GetChar(pos-1)))
411                 && (pos + iText >= par->Last() 
412                     || !IsLetterCharOrDigit(par->GetChar(pos + iText))))) {
413                 iLenSelected = iText;
414                 return true;
415         }
417         return false;