]> git.lyx.org Git - lyx.git/blob - src/lyxfind.C
039d34985d035b3b5681906f84b4cb6380a7db59
[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         if (bv->focus())
100                 bv->showCursor();
101
102         return replace_count;
103 }
104
105 bool LyXFind(BufferView * bv,
106              string const & searchstr, bool forward,
107              bool casesens, bool matchwrd)
108 {
109         if (!bv->available() || searchstr.empty())
110                 return false;
111
112         bv->hideCursor();
113         bv->update(bv->getLyXText(), BufferView::SELECT|BufferView::FITCUR);
114
115         if (bv->theLockingInset()) {
116                 bool found = forward ?
117                         bv->theLockingInset()->searchForward(bv, searchstr, casesens, matchwrd) :
118                         bv->theLockingInset()->searchBackward(bv, searchstr, casesens, matchwrd);
119                 // We found the stuff inside the inset so we don't have to
120                 // do anything as the inset did all the update for us!
121                 if (found)
122                         return true;
123                 // We now are in the main text but if we did a forward
124                 // search we have to put the cursor behind the inset.
125                 if (forward) {
126                         bv->text->cursorRight(bv, true);
127                 }
128         }
129         // If we arrive here we are in the main text again so we
130         // just start searching from the root LyXText at the position
131         // we are!
132         LyXText * text = bv->text;
133
134         if (text->selection.set())
135                 text->cursor = forward ?
136                         text->selection.end : text->selection.start;
137
138         bv->toggleSelection();
139         text->clearSelection();
140         
141         SearchResult result = forward ?
142                 SearchForward(bv, text, searchstr, casesens, matchwrd) :
143                 SearchBackward(bv, text, searchstr, casesens, matchwrd);
144
145         bool found = true;
146         // If we found the cursor inside an inset we will get back
147         // SR_FOUND_NOUPDATE and we don't have to do anything as the
148         // inset did it already.
149         if (result == SR_FOUND) {
150                 bv->unlockInset(bv->theLockingInset());
151                 bv->update(text, BufferView::SELECT|BufferView::FITCUR);
152                 text->setSelectionOverString(bv, searchstr);
153                 bv->toggleSelection(false);
154                 bv->update(text, BufferView::SELECT|BufferView::FITCUR);
155         } else if (result == SR_NOT_FOUND) {
156                 bv->unlockInset(bv->theLockingInset());
157                 bv->update(text, BufferView::SELECT|BufferView::FITCUR);
158                 found = false;
159         }
160         if (result != SR_FOUND_NOUPDATE && bv->focus())
161                 bv->showCursor();
162
163         return found;
164 }
165
166 SearchResult LyXFind(BufferView * bv, LyXText * text,
167                      string const & searchstr, bool forward,
168                      bool casesens, bool matchwrd)
169 {
170         if (text->selection.set())
171                 text->cursor = forward ?
172                         text->selection.end : text->selection.start;
173
174         bv->toggleSelection();
175         text->clearSelection();
176
177         SearchResult result = forward ?
178                 SearchForward(bv, text, searchstr, casesens, matchwrd) :
179                 SearchBackward(bv, text, searchstr, casesens, matchwrd);
180
181         return result;
182 }
183
184
185 // returns true if the specified string is at the specified position
186 bool IsStringInText(Paragraph * par, pos_type pos,
187                     string const & str, bool const & cs,
188                     bool const & mw)
189 {
190         if (!par)
191                 return false;
192
193         string::size_type size = str.length();
194         pos_type i = 0;
195         while (((pos + i) < par->size())
196                && (string::size_type(i) < size)
197                && (cs ? (str[i] == par->getChar(pos + i))
198                    : (uppercase(str[i]) == uppercase(par->getChar(pos + i)))))
199         {
200                 ++i;
201         }
202         if (size == string::size_type(i)) {
203                 // if necessary, check whether string matches word
204                 if (!mw)
205                         return true;
206                 if ((pos <= 0 || !IsLetterCharOrDigit(par->getChar(pos - 1)))
207                         && (pos + pos_type(size) >= par->size()
208                         || !IsLetterCharOrDigit(par->getChar(pos + size)))) {
209                         return true;
210                 }
211         }
212         return false;
213 }
214
215 // forward search:
216 // if the string can be found: return true and set the cursor to
217 // the new position, cs = casesensitive, mw = matchword
218 SearchResult SearchForward(BufferView * bv, LyXText * text, string const & str,
219                            bool const & cs, bool const & mw)
220 {
221         Paragraph * par = text->cursor.par();
222         pos_type pos = text->cursor.pos();
223         Paragraph * prev_par = par;
224         UpdatableInset * inset;
225
226         while (par && !IsStringInText(par, pos, str, cs, mw)) {
227                 if (par->isInset(pos) &&
228                         (inset = (UpdatableInset *)par->getInset(pos)) &&
229                         (inset->isTextInset()))
230                 {
231 #if 0
232                         // lock the inset!
233                         text->setCursor(bv, par, pos);
234                         inset->edit(bv);
235 #endif
236                         if (inset->searchForward(bv, str, cs, mw))
237                                 return SR_FOUND_NOUPDATE;
238                 }
239
240                 ++pos;
241
242                 if (pos >= par->size()) {
243                         prev_par = par;
244                         par = par->next();
245                         pos = 0;
246                 }
247         }
248
249         if (par) {
250                 text->setCursor(bv, par, pos);
251                 return SR_FOUND;
252         } else {
253                 // make sure we end up at the end of the text,
254                 // not the start point of the last search
255                 text->setCursor(bv, prev_par, prev_par->size());
256                 return SR_NOT_FOUND;
257         }
258 }
259
260
261 // backward search:
262 // if the string can be found: return true and set the cursor to
263 // the new position, cs = casesensitive, mw = matchword
264 SearchResult SearchBackward(BufferView * bv, LyXText * text,
265                             string const & str,
266                             bool const & cs, bool const & mw)
267 {
268         Paragraph * par = text->cursor.par();
269         pos_type pos = text->cursor.pos();
270         Paragraph * prev_par = par;
271
272         do {
273                 if (pos > 0)
274                         --pos;
275                 else {
276                         prev_par = par;
277                         // We skip empty paragraphs (Asger)
278                         do {
279                                 par = par->previous();
280                                 if (par)
281                                         pos = par->size() - 1;
282                         } while (par && pos < 0);
283                 }
284                 UpdatableInset * inset;
285                 if (par && par->isInset(pos) &&
286                         (inset = (UpdatableInset *)par->getInset(pos)) &&
287                         (inset->isTextInset()))
288                 {
289 #if 0
290                         // lock the inset!
291                         text->setCursor(bv, par, pos);
292                         inset->edit(bv, false);
293 #endif
294                         if (inset->searchBackward(bv, str, cs, mw))
295                                 return SR_FOUND_NOUPDATE;
296                 }
297         } while (par && !IsStringInText(par, pos, str, cs, mw));
298
299         if (par) {
300                 text->setCursor(bv, par, pos);
301                 return SR_FOUND;
302         } else {
303                 // go to the last part of the unsuccessful search
304                 text->setCursor(bv, prev_par, 0);
305                 return SR_NOT_FOUND;
306         }
307 }
308
309 } // end lyxfind namespace