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