]> git.lyx.org Git - lyx.git/blob - src/lyxfind.C
Fixed cut&paste bugs and added freespacing for ERT Insets.
[lyx.git] / src / lyxfind.C
1 #include <config.h>
2
3 #ifdef __GNUG__
4 #pragma implementation
5 #endif
6
7 #include "lyxtext.h"
8 #include "lyxfind.h"
9 #include "LyXView.h"
10 #include "frontends/Alert.h"
11 #include "support/textutils.h"
12 #include "support/lstrings.h"
13 #include "BufferView.h"
14 #include "buffer.h"
15 #include "gettext.h"
16
17 using lyx::pos_type;
18
19
20 ///
21 // locally used enum
22 ///
23 enum SearchResult {
24         //
25         SR_NOT_FOUND = 0,
26         //
27         SR_FOUND,
28         //
29         SR_FOUND_NOUPDATE
30 };
31
32
33 /// returns true if the specified string is at the specified  position
34 bool IsStringInText(Paragraph * par, pos_type pos,
35                     string const & str, bool const & = true,
36                     bool const & = false);
37
38 /// if the string is found: return true and set the cursor to the new position
39 SearchResult SearchForward(BufferView *, LyXText * text, string const & str,
40                            bool const & = true, bool const & = false);
41 ///
42 SearchResult SearchBackward(BufferView *, LyXText * text, string const & str,
43                             bool const & = true, bool const & = false);
44
45
46 int LyXReplace(BufferView * bv,
47                string const & searchstr, string const & replacestr,
48                bool forward, bool casesens, bool matchwrd, bool replaceall,
49                bool once)
50 {
51         if (!bv->available() || bv->buffer()->isReadonly()) 
52                 return 0;
53    
54         // CutSelection cannot cut a single space, so we have to stop
55         // in order to avoid endless loop :-(
56         if (searchstr.length() == 0
57                 || (searchstr.length() == 1 && searchstr[0] == ' '))
58         {
59                 Alert::alert(_("Sorry!"), _("You cannot replace a single space, "
60                                           "nor an empty character."));
61                 return 0;
62         }
63         
64         LyXText * text = bv->getLyXText();
65
66         // now we can start searching for the first 
67         // start at top if replaceall
68         bool fw = forward;
69         if (replaceall) {
70                 text->clearSelection();
71                 if (text->inset_owner) {
72                         bv->unlockInset(bv->theLockingInset());
73                         text = bv->text;
74                 }
75                 text->cursorTop(bv);
76                 // override search direction because we search top to bottom
77                 fw = true;
78         }
79         
80         // if nothing selected or selection does not equal search string
81         // search and select next occurance and return if no replaceall
82         string str1;
83         string str2;
84         if (casesens) {
85                 str1 = searchstr;
86                 str2 = text->selectionAsString(bv->buffer(), false);
87         } else {
88                 str1 = lowercase(searchstr);
89                 str2 = lowercase(text->selectionAsString(bv->buffer(), false));
90         }
91         if (str1 != str2) {
92                 if (!LyXFind(bv, searchstr, fw, false, casesens, matchwrd) ||
93                         !replaceall)
94                 {
95                         return 0;
96                 }
97         }
98
99         bool found = false;
100         int replace_count = 0;
101         do {
102                 bv->hideCursor();
103                 bv->update(bv->getLyXText(), BufferView::SELECT|BufferView::FITCUR);
104                 bv->toggleSelection(false);
105                 bv->getLyXText()->replaceSelectionWithString(bv, replacestr);
106                 bv->getLyXText()->setSelectionOverString(bv, replacestr);
107                 bv->update(bv->getLyXText(), BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
108                 ++replace_count;
109                 if (!once)
110                         found = LyXFind(bv, searchstr, fw, false, casesens, matchwrd);
111         } while (!once && replaceall && found);
112    
113         if (bv->focus())
114                 bv->showCursor();
115         
116         return replace_count;
117 }
118
119 bool LyXFind(BufferView * bv,
120              string const & searchstr, bool forward,
121              bool frominset, bool casesens, bool matchwrd)
122 {
123         if (!bv->available() || searchstr.empty())
124                 return false;
125         
126         LyXText * text = bv->getLyXText();
127
128         bv->hideCursor();
129         bv->update(text, BufferView::SELECT|BufferView::FITCUR);
130         
131         if (text->selection.set())
132                 text->cursor = forward ?
133                         text->selection.end : text->selection.start;
134
135         SearchResult result = SR_NOT_FOUND;
136
137         if (!frominset && bv->theLockingInset()) {
138                 bool found = forward ?
139                         bv->theLockingInset()->searchForward(bv, searchstr, casesens, matchwrd) :
140                         bv->theLockingInset()->searchBackward(bv, searchstr, casesens, matchwrd);
141                 if (found)
142                         result = SR_FOUND_NOUPDATE;
143                 else {
144                         text = bv->getLyXText();
145                         Paragraph * par = text->cursor.par();
146                         pos_type pos = text->cursor.pos();
147                         if (forward) {
148                                 if (pos < par->size() - 1)
149                                         ++pos;
150                                 else {
151                                         pos = 0;
152                                         par = par->next();
153                                 }
154                                 if (par)
155                                         text->setCursor(bv, par, pos);
156                         }
157                         if (par) {
158                                 result = forward ? 
159                                         SearchForward(bv, text, searchstr, casesens, matchwrd) :
160                                         SearchBackward(bv, text, searchstr, casesens, matchwrd);
161                         }
162                 }
163         } else {
164                 result = forward ? 
165                         SearchForward(bv, text, searchstr, casesens, matchwrd) :
166                         SearchBackward(bv, text, searchstr, casesens, matchwrd);
167         }
168
169         bool found = true;
170         if (result == SR_FOUND) {
171                 // the actual text pointer could have changed!
172                 bv->update(bv->getLyXText(), BufferView::SELECT|BufferView::FITCUR);
173                 bv->toggleSelection();
174                 bv->getLyXText()->clearSelection();
175                 bv->getLyXText()->setSelectionOverString(bv, searchstr);
176                 bv->toggleSelection(false);
177                 bv->update(bv->getLyXText(), BufferView::SELECT|BufferView::FITCUR);
178         } else if (result == SR_NOT_FOUND)
179                 found = false;
180    
181         if (bv->focus())
182                 bv->showCursor();
183    
184         return found;
185 }
186
187
188 // returns true if the specified string is at the specified position
189 bool IsStringInText(Paragraph * par, pos_type pos,
190                     string const & str, bool const & cs,
191                     bool const & mw)
192 {
193         if (!par)
194                 return false;
195    
196         string::size_type size = str.length();
197         pos_type i = 0;
198         while (((pos + i) < par->size())
199                && (string::size_type(i) < size)
200                && (cs ? (str[i] == par->getChar(pos + i))
201                    : (toupper(str[i]) == toupper(par->getChar(pos + i)))))
202         {
203                 ++i;
204         }
205         if (size == string::size_type(i)) {
206                 // if necessary, check whether string matches word
207                 if (!mw)
208                         return true;
209                 if ((pos <= 0 || !IsLetterCharOrDigit(par->getChar(pos - 1)))
210                         && (pos + pos_type(size) >= par->size()
211                         || !IsLetterCharOrDigit(par->getChar(pos + size)))) {
212                         return true;
213                 }
214         }
215         return false;
216 }
217
218 // forward search:
219 // if the string can be found: return true and set the cursor to
220 // the new position, cs = casesensitive, mw = matchword
221 SearchResult SearchForward(BufferView * bv, LyXText * text, string const & str,
222                            bool const & cs, bool const & mw)
223 {
224         Paragraph * par = text->cursor.par();
225         pos_type pos = text->cursor.pos();
226         UpdatableInset * inset;
227
228         while (par && !IsStringInText(par, pos, str, cs, mw)) {
229                 if (par->isInset(pos) &&
230                         (inset = (UpdatableInset *)par->getInset(pos)) &&
231                         (inset->isTextInset()))
232                 {
233                         // lock the inset!
234                         text->setCursor(bv, par, pos);
235                         inset->edit(bv);
236                         if (inset->searchForward(bv, str, cs, mw))
237                                 return SR_FOUND_NOUPDATE;
238                         text = bv->getLyXText();
239                 }
240                 if (pos < par->size() - 1)
241                         ++pos;
242                 else {
243                         pos = 0;
244                         par = par->next();
245                 }
246         }
247         if (par) {
248                 text->setCursor(bv, par, pos);
249                 return SR_FOUND;
250 #if 0
251         } else if (text->inset_owner) {
252                 // test if we're inside an inset if yes unlock the inset
253                 // and recall us with the outside LyXText!
254                 bv->unlockInset((UpdatableInset *)text->inset_owner);
255                 if (!bv->theLockingInset()) {
256                         text = bv->getLyXText();
257                         par = text->cursor.par();
258                         pos = text->cursor.pos();
259                         if (pos < par->size() - 1)
260                                 ++pos;
261                         else {
262                                 pos = 0;
263                                 par = par->next();
264                         }
265                         if (!par)
266                                 return SR_NOT_FOUND;
267                         text->setCursor(bv, par, pos);
268                         return SearchForward(bv, text, str, cs, mw);
269                 } else {
270                         return SR_NOT_FOUND;
271                 }
272 #endif
273         } else
274                 return SR_NOT_FOUND;
275 }
276
277
278 // backward search:
279 // if the string can be found: return true and set the cursor to
280 // the new position, cs = casesensitive, mw = matchword
281 SearchResult SearchBackward(BufferView * bv, LyXText * text,
282                             string const & str,
283                             bool const & cs, bool const & mw)
284 {
285         Paragraph * par = text->cursor.par();
286         pos_type pos = text->cursor.pos();
287
288         do {
289                 if (pos > 0)
290                         --pos;
291                 else {
292                         // We skip empty paragraphs (Asger)
293                         do {
294                                 par = par->previous();
295                                 if (par)
296                                         pos = par->size() - 1;
297                         } while (par && pos < 0);
298                 }
299                 UpdatableInset * inset;
300                 if (par && par->isInset(pos) &&
301                         (inset = (UpdatableInset *)par->getInset(pos)) &&
302                         (inset->isTextInset()))
303                 {
304                         // lock the inset!
305                         text->setCursor(bv, par, pos);
306                         inset->edit(bv, false);
307                         if (inset->searchBackward(bv, str, cs, mw))
308                                 return SR_FOUND_NOUPDATE;
309                         text = bv->getLyXText();
310                 }               
311         } while (par && !IsStringInText(par, pos, str, cs, mw));
312   
313         if (par) {
314                 text->setCursor(bv, par, pos);
315                 return SR_FOUND;
316         }
317 #if 0
318         else if (text->inset_owner) {
319                 // test if we're inside an inset if yes unlock the inset
320                 // and recall us with the outside LyXText!
321                 bv->unlockInset((UpdatableInset *)text->inset_owner);
322                 if (!bv->theLockingInset()) {
323                         return SearchBackward(bv, bv->getLyXText(), str, cs, mw);
324                 }
325         }
326 #endif
327         return SR_NOT_FOUND;
328 }
329