]> git.lyx.org Git - lyx.git/blob - src/lyxfr1.C
eaeae2856b726a4c8d71a80c165044a9150f74e5
[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         sz[0] = 0;
69         
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         for (int i = 0; i < len; ++i)
121                 lt->CursorRight();
122         lt->SetSelection();
123 }
124
125
126 //------------------------------
127
128 void LyXFindReplace1::StartSearch()
129 {
130         LyXFindReplace0::StartSearch();
131         SetReplaceEnabled(!current_view->buffer()->isReadonly());
132         searchForward = true;
133         if (lsSearch.empty()) 
134                 SetSearchString(GetSelectionOrWordAtCursor(current_view->text));
135 }       
136
137
138 // TODO?: the user can insert multiple spaces with this
139 // routine (1999-01-11, dnaber)
140 void LyXFindReplace1::SearchReplaceCB()
141 {
142         if (!current_view->getScreen()) return;
143         if (current_view->buffer()->isReadonly()) return;
144         
145         // CutSelection cannot cut a single space, so we have to stop
146         // in order to avoid endless loop :-(
147         ReInitFromForm();
148         if (SearchString().length() == 0 || (SearchString().length() == 1
149                                              && SearchString()[0] == ' ') ) {
150                 WriteAlert(_("Sorry!"), _("You cannot replace a single space, "
151                                           "nor an empty character."));
152                 return;
153         }
154         
155         string const replacestring = ReplaceString();
156
157         current_view->getScreen()->HideCursor();
158         current_view->update(-2);
159
160         LyXText * ltCur = current_view->text;   
161         if (ltCur->selection) {
162                 // clear the selection (if there is any) 
163                 current_view->getScreen()->ToggleSelection(false);
164                 current_view->text->
165                         ReplaceSelectionWithString(replacestring.c_str());
166                 current_view->text->
167                         SetSelectionOverString(replacestring.c_str());
168                 current_view->update(1);
169         }
170         
171         // jump to next match:
172         SearchCB( searchForward );
173 }
174
175
176 // replaces all occurences of a string (1999-01-15, dnaber@mini.gt.owl.de)
177 void LyXFindReplace1::SearchReplaceAllCB()
178 {
179         if (!current_view->getScreen()) return;
180         if (current_view->buffer()->isReadonly()) return;
181
182         // CutSelection cannot cut a single space, so we have to stop
183         // in order to avoid endless loop :-(
184         ReInitFromForm();
185         if (SearchString().length() == 0 || (SearchString().length() == 1
186                                              && SearchString()[0] == ' ') ) {
187                 WriteAlert(_("Sorry!"), _("You cannot replace a single space, "
188                                           "nor an empty character."));
189                 return;
190         }
191
192         string const replacestring = ReplaceString();
193
194         current_view->getScreen()->HideCursor();
195
196         // start at top
197         current_view->text->ClearSelection();
198         current_view->text->CursorTop();
199
200         int replace_count = 0;
201         LyXText * ltCur;
202         do {
203                 ltCur = current_view->text;     
204                 if (ltCur->selection) {
205                         current_view->update(-2);
206                         current_view->getScreen()->ToggleSelection(false);
207                         current_view->text->
208                                 ReplaceSelectionWithString(replacestring.c_str());
209                         current_view->text->
210                                 SetSelectionOverString(replacestring.c_str());
211                         current_view->update(1); 
212                         ++replace_count;
213                 }
214         } while( SearchCB(true) );
215         if( replace_count == 0 ) {
216                 LyXBell();      
217                 minibuffer->Set(_("String not found!"));
218         } else {
219                 if( replace_count == 1 ) {
220                         minibuffer->Set(_("1 string has been replaced."));
221                 } else {
222                         string str = tostr(replace_count);
223                         str += _(" strings have been replaced.");
224                         minibuffer->Set(str);
225                 }
226         }
227 }
228
229
230 bool LyXFindReplace1::SearchCB(bool fForward)
231 {
232         // store search direction
233         searchForward = fForward;
234         
235         if (!current_view->getScreen())
236                 return false;
237    
238         current_view->getScreen()->HideCursor();
239         current_view->update(-2);
240         LyXText * ltCur = current_view->text;
241         if (ltCur->selection) 
242                 ltCur->cursor = fForward ? ltCur->sel_end_cursor :
243                 ltCur->sel_start_cursor;
244
245         ReInitFromForm();
246         iLenSelected = SearchString().length();
247         bool result;
248    
249         if (!ValidSearchData() ||
250             (fForward ? SearchForward(ltCur) : SearchBackward(ltCur))) {
251                 current_view->update(-2);
252
253                 // clear the selection (if there is any) 
254                 current_view->getScreen()->ToggleSelection();
255                 current_view->text->ClearSelection();
256
257                 // set the new selection 
258                 SetSelectionOverLenChars(current_view->text, iLenSelected);
259                 current_view->getScreen()->ToggleSelection(false);
260                 minibuffer->Set(_("Found."));
261                 result = true;
262         } else {
263                 LyXBell();
264                 minibuffer->Set(_("String not found!"));
265                 result = false;
266         }
267    
268         if (current_view->getWorkArea()->focus)
269                 current_view->getScreen()->ShowCursor();
270
271         return result;
272 }
273
274
275 // if the string can be found: return true and set the cursor to
276 // the new position 
277 // (was: LyXText::SearchForward(char const* string) in text2.C )
278 bool LyXFindReplace1::SearchForward(LyXText * lt)
279 {
280         LyXParagraph * par = lt->cursor.par;
281         LyXParagraph::size_type pos = lt->cursor.pos;
282
283         while (par && !IsSearchStringInText(par, pos)) {
284                 if (pos < par->Last() - 1)
285                         ++pos;
286                 else {
287                         pos = 0;
288                         par = par->Next();
289                 }
290         }
291         if (par) {
292                 lt->SetCursor(par, pos);
293                 return true;
294         } else
295                 return false;
296 }
297
298
299 // if the string can be found: return true and set the cursor to
300 // the new position 
301 // (was: LyXText::SearchBackward(char const* string) in text2.C )
302 bool LyXFindReplace1::SearchBackward(LyXText * lt)
303 {
304         LyXParagraph * par = lt->cursor.par;
305         int pos = lt->cursor.pos;
306
307         do {
308                 if (pos > 0)
309                         --pos;
310                 else {
311                         // We skip empty paragraphs (Asger)
312                         do {
313                                 par = par->Previous();
314                                 if (par)
315                                         pos = par->Last() - 1;
316                         } while (par && pos < 0);
317                 }
318         } while (par && !IsSearchStringInText(par, pos));
319   
320         if (par) {
321                 lt->SetCursor(par, pos);
322                 return true;
323         } else
324                 return false;
325 }
326
327
328 /* Compares 2 char values. 
329 return value is
330     > 0 if chSearch > ch2
331     = 0 if chSearch == ch2
332     < 0 if chSearch < ch2
333 */
334 int LyXFindReplace1::CompareChars(char chSearch, char chText)
335 {
336         if (CaseSensitive())
337                 return (chSearch - chText);
338         return (toupper(chSearch) - toupper(chText));
339 }
340
341
342 // returns true if the search string is at the specified position 
343 // (Copied from the original "LyXText::IsStringInText" in text2.C )
344 bool LyXFindReplace1::IsSearchStringInText(LyXParagraph * par,
345                                            LyXParagraph::size_type pos)
346 {
347         if (!par) return false;
348
349         char chSrch = 0;
350         char chText;
351         bool fPrevIsSpace = false;
352         int iText = 0;
353         string::size_type iSrch = 0;
354         while (pos + iText < par->Last() && 
355                iSrch < SearchString().length()) {
356                 chSrch = SearchString()[iSrch];
357                 chText = par->GetChar(pos+iText);
358                 if (chText == ' ') {
359                         if (fPrevIsSpace) {
360                                 ++iText;  // next Text pos
361                                 continue; // same search pos
362                         }
363                         fPrevIsSpace = true;
364                 } else
365                         fPrevIsSpace = false;
366                 if (CompareChars(chSrch, chText) != 0)
367                         break;
368                 
369                 ++iSrch;
370                 ++iText;
371         }
372
373         if (iSrch < SearchString().length())
374                 return false;
375
376         if (!MatchWord() 
377             || ((pos <= 0 || !IsLetterCharOrDigit(par->GetChar(pos - 1)))
378                 && (pos + iText >= par->Last() 
379                     || !IsLetterCharOrDigit(par->GetChar(pos + iText))))) {
380                 iLenSelected = iText;
381                 return true;
382         }
383
384         return false;
385 }