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