]> git.lyx.org Git - lyx.git/blob - src/lyxfr1.C
b455e36fd12e5a187d5da810232274955adbfed7
[lyx.git] / src / lyxfr1.C
1 /* This file is part of
2 * ======================================================
3
4 *           LyX, The Document Processor
5 *        
6 *           Copyright (C) 1995 Matthias Ettrich,
7 *           Copyright (C) 1995-1998 The LyX Team.
8 *
9 *======================================================*/
10
11 #include <config.h>
12
13 #include <ctype.h>
14 #include <string.h>
15 #include <stdlib.h>
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 "error.h"
30 #include "lyxtext.h"
31 #include "gettext.h"
32 #include "LyXView.h"
33 #include "lyx_gui_misc.h"
34 #include "minibuffer.h"
35
36 extern BufferView *current_view; // called too many times in this file...
37 extern MiniBuffer *minibuffer;
38
39 // Maximum length copied from the current selection to the search string
40 const int LYXSEARCH_MAXLEN =  128;
41
42 // function prototypes
43
44 bool IsLetterCharOrDigit(char ch);
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 bool IsLetterCharOrDigit(char ch)
62 {
63         return IsLetterChar(ch) || isdigit(ch);
64 }
65
66
67 // Returns the current selection. If nothing is selected or if the selection
68 // spans 2 paragraphs, an empty string is returned.
69 string const GetCurrentSelectionAsString(LyXText *lt) 
70 {
71         LyXParagraph    *par;
72         int             pos;
73         int             endpos;
74         int             i;
75         char            sz[LYXSEARCH_MAXLEN];
76         char            ch;
77         bool            fPrevIsSpace;
78
79         sz[0] = 0;
80         par = lt->cursor.par;
81         if (lt->selection && (lt->sel_cursor.par == par)) {
82                 // (selected) and (begin/end in same paragraph)
83                 pos = lt->sel_start_cursor.pos;
84                 endpos = lt->sel_end_cursor.pos;
85                 i = 0;
86                 fPrevIsSpace = false;
87                 while ((i < LYXSEARCH_MAXLEN-2) && 
88                         (pos < par->Last()) && (pos < endpos)) {
89                         ch = par->GetChar(pos);
90
91                         //HB??: Maybe (ch <= ' ') 
92                         if ((ch == ' ') || ((unsigned char)ch <= LYX_META_INSET)) {
93                                 // consecutive spaces --> 1 space char
94                                 if (fPrevIsSpace) {
95                                         pos++;          // Next text pos
96                                         continue;       // same search pos
97                                 }
98                                 sz[i] = ' ';
99                                 fPrevIsSpace = true;
100                         } else {
101                                 sz[i] = ch;
102                                 fPrevIsSpace = false;
103                         }
104                         pos++;
105                         i++;
106                 } 
107                 sz[i] = 0;
108         }
109         return string(sz);
110 }
111
112
113 // If nothing selected, select the word at the cursor.
114 // Returns the current selection
115 string const GetSelectionOrWordAtCursor(LyXText *lt) 
116 {
117         lt->SelectWordWhenUnderCursor();
118         return GetCurrentSelectionAsString(lt);
119 }
120
121
122 // This is a copy of SetSelectionOverString from text.C
123 // It does the same, but uses only the length as a parameter
124 void SetSelectionOverLenChars(LyXText *lt, int len)
125 {
126         lt->sel_cursor = lt->cursor;
127         int i;
128         for (i=0; i < len; i++)
129                 lt->CursorRight();
130         lt->SetSelection();
131 }
132
133
134 //------------------------------
135
136 void LyXFindReplace1::StartSearch()
137 {
138         LyXFindReplace0::StartSearch();
139         SetReplaceEnabled(!current_view->currentBuffer()->isReadonly());
140         searchForward = true;
141         if (lsSearch.empty()) 
142                 SetSearchString(GetSelectionOrWordAtCursor(current_view->currentBuffer()->text));
143 }       
144
145
146 // TODO?: the user can insert multiple spaces with this routine (1999-01-11, dnaber)
147 void LyXFindReplace1::SearchReplaceCB()
148 {
149         LyXText         *ltCur;
150
151         if (!current_view->getScreen())
152                 return;
153         if (current_view->currentBuffer()->isReadonly())
154                 return;
155         
156         // CutSelection cannot cut a single space, so we have to stop
157         // in order to avoid endless loop :-(
158         ReInitFromForm();
159         if (SearchString().length() == 0 || (SearchString().length() == 1
160                                              && SearchString()[0] == ' ') ) {
161                 WriteAlert(_("Sorry!"), _("You cannot replace a single space, "
162                                           "nor an empty character."));
163                 return;
164         }
165         
166         string const replacestring = ReplaceString();
167
168         current_view->getScreen()->HideCursor();
169         current_view->currentBuffer()->update(-2);
170
171         ltCur = current_view->currentBuffer()->text;    
172         if (ltCur->selection) {
173                 // clear the selection (if there is any) 
174                 current_view->getScreen()->ToggleSelection(false);
175                 current_view->currentBuffer()->text->
176                         ReplaceSelectionWithString(replacestring.c_str());
177                 current_view->currentBuffer()->text->
178                         SetSelectionOverString(replacestring.c_str());
179                 current_view->currentBuffer()->update(1);
180         }
181         
182         // jump to next match:
183         SearchCB( searchForward );
184 }
185
186
187 // replaces all occurences of a string (1999-01-15, dnaber@mini.gt.owl.de)
188 void LyXFindReplace1::SearchReplaceAllCB()
189 {
190         LyXText         *ltCur;
191
192         if (!current_view->getScreen())
193                 return;
194         if (current_view->currentBuffer()->isReadonly())
195                 return;
196
197         // CutSelection cannot cut a single space, so we have to stop
198         // in order to avoid endless loop :-(
199         ReInitFromForm();
200         if (SearchString().length() == 0 || (SearchString().length() == 1
201                                              && SearchString()[0] == ' ') ) {
202                 WriteAlert(_("Sorry!"), _("You cannot replace a single space, "
203                                           "nor an empty character."));
204                return;
205        }
206
207         string const replacestring = ReplaceString();
208
209         current_view->getScreen()->HideCursor();
210
211         // start at top
212         current_view->currentBuffer()->text->ClearSelection();
213         current_view->currentBuffer()->text->CursorTop();
214
215         int replace_count = 0;
216         do {
217                 ltCur = current_view->currentBuffer()->text;    
218                 if (ltCur->selection) {
219                         current_view->currentBuffer()->update(-2);
220                         current_view->getScreen()->ToggleSelection(false);
221                         current_view->currentBuffer()->text->
222                                 ReplaceSelectionWithString(replacestring.c_str());
223                         current_view->currentBuffer()->text->
224                                 SetSelectionOverString(replacestring.c_str());
225                         current_view->currentBuffer()->update(1); 
226                         replace_count++;
227                 }
228         } while( SearchCB(true) );
229
230         if( replace_count == 0 ) {
231                 LyXBell();      
232                 minibuffer->Set(_("String not found!"));
233         } else {
234                 if( replace_count == 1 ) {
235                         minibuffer->Set(_("1 string has been replaced."));
236                 } else {
237                         string str;
238                         str += 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 }