10 #include "frontends/LyXView.h"
11 #include "frontends/Alert.h"
12 #include "support/textutils.h"
13 #include "support/lstrings.h"
14 #include "BufferView.h"
18 #include "insets/insettext.h"
26 /// returns true if the specified string is at the specified position
27 bool IsStringInText(Paragraph * par, pos_type pos,
28 string const & str, bool const & = true,
29 bool const & = false);
31 /// if the string is found: return true and set the cursor to the new position
32 SearchResult SearchForward(BufferView *, LyXText * text, string const & str,
33 bool const & = true, bool const & = false);
35 SearchResult SearchBackward(BufferView *, LyXText * text, string const & str,
36 bool const & = true, bool const & = false);
38 int LyXReplace(BufferView * bv,
39 string const & searchstr, string const & replacestr,
40 bool forward, bool casesens, bool matchwrd, bool replaceall,
43 if (!bv->available() || bv->buffer()->isReadonly())
46 // CutSelection cannot cut a single space, so we have to stop
47 // in order to avoid endless loop :-(
48 if (searchstr.length() == 0
49 || (searchstr.length() == 1 && searchstr[0] == ' '))
51 Alert::alert(_("Sorry!"), _("You cannot replace a single space, "
52 "nor an empty character."));
56 // now we can start searching for the first
57 // start at top if replaceall
58 LyXText * text = bv->getLyXText();
61 text->clearSelection();
62 bv->unlockInset(bv->theLockingInset());
65 // override search direction because we search top to bottom
69 // if nothing selected or selection does not equal search string
70 // search and select next occurance and return if no replaceall
75 str2 = text->selectionAsString(bv->buffer(), false);
77 str1 = lowercase(searchstr);
78 str2 = lowercase(text->selectionAsString(bv->buffer(), false));
81 if (!LyXFind(bv, searchstr, fw, casesens, matchwrd) ||
89 int replace_count = 0;
91 text = bv->getLyXText();
92 // We have to do this check only because mathed insets don't
93 // return their own LyXText but the LyXText of it's parent!
94 if (!bv->theLockingInset() ||
95 ((text != bv->text) &&
96 (text->inset_owner == text->inset_owner->getLockingInset())))
99 bv->update(text, BufferView::SELECT|BufferView::FITCUR);
100 bv->toggleSelection(false);
101 text->replaceSelectionWithString(bv, replacestr);
102 text->setSelectionRange(bv, replacestr.length());
103 bv->update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
107 found = LyXFind(bv, searchstr, fw, casesens, matchwrd);
108 } while (!once && replaceall && found);
110 return replace_count;
114 bool LyXFind(BufferView * bv,
115 string const & searchstr, bool forward,
116 bool casesens, bool matchwrd)
118 if (!bv->available() || searchstr.empty())
122 bv->update(bv->getLyXText(), BufferView::SELECT|BufferView::FITCUR);
124 if (bv->theLockingInset()) {
125 bool found = forward ?
126 bv->theLockingInset()->searchForward(bv, searchstr, casesens, matchwrd) :
127 bv->theLockingInset()->searchBackward(bv, searchstr, casesens, matchwrd);
128 // We found the stuff inside the inset so we don't have to
129 // do anything as the inset did all the update for us!
132 // We now are in the main text but if we did a forward
133 // search we have to put the cursor behind the inset.
135 bv->text->cursorRight(bv, true);
138 // If we arrive here we are in the main text again so we
139 // just start searching from the root LyXText at the position
141 LyXText * text = bv->text;
143 if (text->selection.set())
144 text->cursor = forward ?
145 text->selection.end : text->selection.start;
147 bv->toggleSelection();
148 text->clearSelection();
150 SearchResult result = forward ?
151 SearchForward(bv, text, searchstr, casesens, matchwrd) :
152 SearchBackward(bv, text, searchstr, casesens, matchwrd);
155 // If we found the cursor inside an inset we will get back
156 // SR_FOUND_NOUPDATE and we don't have to do anything as the
157 // inset did it already.
158 if (result == SR_FOUND) {
159 bv->unlockInset(bv->theLockingInset());
160 bv->update(text, BufferView::SELECT|BufferView::FITCUR);
161 text->setSelectionRange(bv, searchstr.length());
162 bv->toggleSelection(false);
163 bv->update(text, BufferView::SELECT|BufferView::FITCUR);
164 } else if (result == SR_NOT_FOUND) {
165 bv->unlockInset(bv->theLockingInset());
166 bv->update(text, BufferView::SELECT|BufferView::FITCUR);
174 SearchResult LyXFind(BufferView * bv, LyXText * text,
175 string const & searchstr, bool forward,
176 bool casesens, bool matchwrd)
178 if (text->selection.set())
179 text->cursor = forward ?
180 text->selection.end : text->selection.start;
182 bv->toggleSelection();
183 text->clearSelection();
185 SearchResult result = forward ?
186 SearchForward(bv, text, searchstr, casesens, matchwrd) :
187 SearchBackward(bv, text, searchstr, casesens, matchwrd);
193 // returns true if the specified string is at the specified position
194 bool IsStringInText(Paragraph * par, pos_type pos,
195 string const & str, bool const & cs,
201 string::size_type size = str.length();
203 while (((pos + i) < par->size())
204 && (string::size_type(i) < size)
205 && (cs ? (str[i] == par->getChar(pos + i))
206 : (uppercase(str[i]) == uppercase(par->getChar(pos + i)))))
210 if (size == string::size_type(i)) {
211 // if necessary, check whether string matches word
214 if ((pos <= 0 || !IsLetterCharOrDigit(par->getChar(pos - 1)))
215 && (pos + pos_type(size) >= par->size()
216 || !IsLetterCharOrDigit(par->getChar(pos + size)))) {
224 // if the string can be found: return true and set the cursor to
225 // the new position, cs = casesensitive, mw = matchword
226 SearchResult SearchForward(BufferView * bv, LyXText * text, string const & str,
227 bool const & cs, bool const & mw)
229 Paragraph * par = text->cursor.par();
230 pos_type pos = text->cursor.pos();
231 Paragraph * prev_par = par;
232 UpdatableInset * inset;
234 while (par && !IsStringInText(par, pos, str, cs, mw)) {
235 if (par->isInset(pos) &&
236 (inset = (UpdatableInset *)par->getInset(pos)) &&
237 (inset->isTextInset()))
241 text->setCursor(bv, par, pos);
244 if (inset->searchForward(bv, str, cs, mw))
245 return SR_FOUND_NOUPDATE;
250 if (pos >= par->size()) {
258 text->setCursor(bv, par, pos);
261 // make sure we end up at the end of the text,
262 // not the start point of the last search
263 text->setCursor(bv, prev_par, prev_par->size());
270 // if the string can be found: return true and set the cursor to
271 // the new position, cs = casesensitive, mw = matchword
272 SearchResult SearchBackward(BufferView * bv, LyXText * text,
274 bool const & cs, bool const & mw)
276 Paragraph * par = text->cursor.par();
277 pos_type pos = text->cursor.pos();
278 Paragraph * prev_par = par;
285 // We skip empty paragraphs (Asger)
287 par = par->previous();
289 pos = par->size() - 1;
290 } while (par && pos < 0);
292 UpdatableInset * inset;
293 if (par && par->isInset(pos) &&
294 (inset = (UpdatableInset *)par->getInset(pos)) &&
295 (inset->isTextInset()))
299 text->setCursor(bv, par, pos);
300 inset->edit(bv, false);
302 if (inset->searchBackward(bv, str, cs, mw))
303 return SR_FOUND_NOUPDATE;
305 } while (par && !IsStringInText(par, pos, str, cs, mw));
308 text->setCursor(bv, par, pos);
311 // go to the last part of the unsuccessful search
312 text->setCursor(bv, prev_par, 0);
318 SearchResult nextChange(BufferView * bv, LyXText * text, pos_type & length)
320 Paragraph * par = text->cursor.par();
321 pos_type pos = text->cursor.pos();
322 Paragraph * prev_par = par;
323 UpdatableInset * inset;
326 if ((!par->size() || pos != par->size())
327 && par->lookupChange(pos) != Change::UNCHANGED)
330 if (par->isInset(pos) &&
331 (inset = (UpdatableInset *)par->getInset(pos)) &&
332 (inset->isTextInset())) {
333 if (inset->nextChange(bv, length))
334 return SR_FOUND_NOUPDATE;
339 if (pos >= par->size()) {
347 text->setCursor(bv, par, pos);
348 Change orig_change = par->lookupChangeFull(pos);
351 for (; end != par->size(); ++end) {
352 Change change = par->lookupChangeFull(end);
353 if (change != orig_change) {
354 // slight UI optimisation: for replacements, we get
355 // text like : _old_new. Consider that as one change.
356 if (!(orig_change.type == Change::DELETED &&
357 change.type == Change::INSERTED))
364 // make sure we end up at the end of the text,
365 // not the start point of the last search
366 text->setCursor(bv, prev_par, prev_par->size());
372 SearchResult findNextChange(BufferView * bv, LyXText * text, pos_type & length)
374 if (text->selection.set())
375 text->cursor = text->selection.end;
377 bv->toggleSelection();
378 text->clearSelection();
380 return nextChange(bv, text, length);
384 bool findNextChange(BufferView * bv)
386 if (!bv->available())
390 bv->update(bv->getLyXText(), BufferView::SELECT | BufferView::FITCUR);
394 if (bv->theLockingInset()) {
395 bool found = bv->theLockingInset()->nextChange(bv, length);
397 // We found the stuff inside the inset so we don't have to
398 // do anything as the inset did all the update for us!
402 // We now are in the main text but if we did a forward
403 // search we have to put the cursor behind the inset.
404 bv->text->cursorRight(bv, true);
406 // If we arrive here we are in the main text again so we
407 // just start searching from the root LyXText at the position
409 LyXText * text = bv->text;
411 if (text->selection.set())
412 text->cursor = text->selection.end;
414 bv->toggleSelection();
415 text->clearSelection();
417 SearchResult result = nextChange(bv, text, length);
419 lyxerr << "Result is " << result << endl;
423 // If we found the cursor inside an inset we will get back
424 // SR_FOUND_NOUPDATE and we don't have to do anything as the
425 // inset did it already.
426 if (result == SR_FOUND) {
427 bv->unlockInset(bv->theLockingInset());
428 bv->update(text, BufferView::SELECT|BufferView::FITCUR);
429 text->setSelectionRange(bv, length);
430 bv->toggleSelection(false);
431 bv->update(text, BufferView::SELECT|BufferView::FITCUR);
432 } else if (result == SR_NOT_FOUND) {
433 bv->unlockInset(bv->theLockingInset());
434 bv->update(text, BufferView::SELECT|BufferView::FITCUR);
441 } // end lyxfind namespace