3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Lars Gullik Bjønnes
9 * \author Alfredo Braunstein
11 * Full author contact details are available in file CREDITS.
20 #include "CutAndPaste.h"
21 #include "BufferView.h"
23 #include "funcrequest.h"
26 #include "paragraph.h"
27 #include "pariterator.h"
30 #include "frontends/Alert.h"
32 #include "support/convert.h"
33 #include "support/docstream.h"
37 using support::lowercase;
38 using support::uppercase;
46 bool parse_bool(docstring & howto)
51 howto = split(howto, var, ' ');
56 class MatchString : public std::binary_function<Paragraph, pos_type, bool>
59 MatchString(docstring const & str, bool cs, bool mw)
60 : str(str), cs(cs), mw(mw)
63 // returns true if the specified string is at the specified position
64 bool operator()(Paragraph const & par, pos_type pos) const
66 docstring::size_type const size = str.length();
68 pos_type const parsize = par.size();
69 for (i = 0; pos + i < parsize; ++i) {
70 if (docstring::size_type(i) >= size)
72 if (cs && str[i] != par.getChar(pos + i))
74 if (!cs && uppercase(str[i]) != uppercase(par.getChar(pos + i)))
78 if (size != docstring::size_type(i))
81 // if necessary, check whether string matches word
83 if (pos > 0 && par.isLetter(pos - 1))
85 if (pos + pos_type(size) < parsize
86 && par.isLetter(pos + size))
98 // match whole words only
103 bool findForward(DocIterator & cur, MatchString const & match)
105 for (; cur; cur.forwardChar())
106 if (cur.inTexted() && match(cur.paragraph(), cur.pos()))
112 bool findBackwards(DocIterator & cur, MatchString const & match)
116 if (cur.inTexted() && match(cur.paragraph(), cur.pos()))
123 bool findChange(DocIterator & cur)
125 for (; cur; cur.forwardPos())
126 if (cur.inTexted() && !cur.paragraph().isUnchanged(cur.pos()))
132 bool searchAllowed(BufferView * bv, docstring const & str)
135 frontend::Alert::error(_("Search error"),
136 _("Search string is empty"));
143 bool find(BufferView * bv, docstring const & searchstr, bool cs, bool mw, bool fw)
145 if (!searchAllowed(bv, searchstr))
148 DocIterator cur = bv->cursor();
150 MatchString const match(searchstr, cs, mw);
152 bool found = fw ? findForward(cur, match) : findBackwards(cur, match);
155 bv->putSelectionAt(cur, searchstr.length(), !fw);
161 int replaceAll(BufferView * bv,
162 docstring const & searchstr, docstring const & replacestr,
165 Buffer & buf = *bv->buffer();
167 if (!searchAllowed(bv, searchstr) || buf.isReadonly())
170 recordUndoFullDocument(bv);
172 MatchString const match(searchstr, cs, mw);
175 int const rsize = replacestr.size();
176 int const ssize = searchstr.size();
178 DocIterator cur = doc_iterator_begin(buf.inset());
179 while (findForward(cur, match)) {
180 pos_type pos = cur.pos();
182 = cur.paragraph().getFontSettings(buf.params(), pos);
183 int striked = ssize - cur.paragraph().eraseChars(pos, pos + ssize,
184 buf.params().trackChanges);
185 cur.paragraph().insert(pos, replacestr, font,
186 Change(buf.params().trackChanges ?
187 Change::INSERTED : Change::UNCHANGED));
188 for (int i = 0; i < rsize + striked; ++i)
193 bv->buffer()->text().init(bv);
194 bv->putSelectionAt(doc_iterator_begin(buf.inset()), 0, false);
201 bool stringSelected(BufferView * bv, docstring const & searchstr,
202 bool cs, bool mw, bool fw)
204 // if nothing selected or selection does not equal search
205 // string search and select next occurance and return
206 docstring const & str1 = searchstr;
207 docstring const str2 = bv->cursor().selectionAsString(false);
208 if ((cs && str1 != str2) || lowercase(str1) != lowercase(str2)) {
209 find(bv, searchstr, cs, mw, fw);
217 int replace(BufferView * bv, docstring const & searchstr,
218 docstring const & replacestr, bool cs, bool mw, bool fw)
220 if (!searchAllowed(bv, searchstr) || bv->buffer()->isReadonly())
223 if (!stringSelected(bv, searchstr, cs, mw, fw))
226 LCursor & cur = bv->cursor();
227 cap::replaceSelectionWithString(cur, replacestr, fw);
228 bv->buffer()->markDirty();
229 find(bv, searchstr, cs, mw, fw);
238 docstring const find2string(docstring const & search,
239 bool casesensitive, bool matchword, bool forward)
243 << int(casesensitive) << ' '
244 << int(matchword) << ' '
250 docstring const replace2string(docstring const & search, docstring const & replace,
251 bool casesensitive, bool matchword,
252 bool all, bool forward)
257 << int(casesensitive) << ' '
258 << int(matchword) << ' '
265 void find(BufferView * bv, FuncRequest const & ev)
267 if (!bv || ev.action != LFUN_WORD_FIND)
270 //lyxerr << "find called, cmd: " << ev << std::endl;
272 // data is of the form
274 // <casesensitive> <matchword> <forward>"
276 docstring howto = split(ev.argument(), search, '\n');
278 bool casesensitive = parse_bool(howto);
279 bool matchword = parse_bool(howto);
280 bool forward = parse_bool(howto);
282 bool const found = find(bv, search,
283 casesensitive, matchword, forward);
286 // emit message signal.
287 bv->message(_("String not found!"));
291 void replace(BufferView * bv, FuncRequest const & ev)
293 if (!bv || ev.action != LFUN_WORD_REPLACE)
296 // data is of the form
299 // <casesensitive> <matchword> <all> <forward>"
302 docstring howto = split(ev.argument(), search, '\n');
303 howto = split(howto, rplc, '\n');
305 bool casesensitive = parse_bool(howto);
306 bool matchword = parse_bool(howto);
307 bool all = parse_bool(howto);
308 bool forward = parse_bool(howto);
310 Buffer * buf = bv->buffer();
312 int const replace_count = all
313 ? replaceAll(bv, search, rplc, casesensitive, matchword)
314 : replace(bv, search, rplc, casesensitive, matchword, forward);
316 if (replace_count == 0) {
317 // emit message signal.
318 buf->message(_("String not found!"));
320 if (replace_count == 1) {
321 // emit message signal.
322 buf->message(_("String has been replaced."));
324 docstring str = convert<docstring>(replace_count);
325 str += _(" strings have been replaced.");
326 // emit message signal.
333 bool findNextChange(BufferView * bv)
338 DocIterator cur = bv->cursor();
340 if (!findChange(cur))
343 bv->cursor().setCursor(cur);
344 bv->cursor().resetAnchor();
346 Change orig_change = cur.paragraph().lookupChange(cur.pos());
348 DocIterator et = doc_iterator_end(cur.inset());
349 for (; cur != et; cur.forwardPosNoDescend()) {
350 Change change = cur.paragraph().lookupChange(cur.pos());
351 if (change != orig_change) {
355 // Now put cursor to end of selection:
356 bv->cursor().setCursor(cur);
357 bv->cursor().setSelection();
358 // if we used a lfun like in find/replace, dispatch would do