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