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.
19 #include "BufferParams.h"
21 #include "CutAndPaste.h"
22 #include "buffer_funcs.h"
23 #include "BufferView.h"
26 #include "FuncRequest.h"
29 #include "Paragraph.h"
30 #include "ParIterator.h"
32 #include "frontends/alert.h"
34 #include "support/convert.h"
35 #include "support/docstream.h"
39 using support::compare_no_case;
40 using support::uppercase;
48 bool parse_bool(docstring & howto)
53 howto = split(howto, var, ' ');
58 class MatchString : public std::binary_function<Paragraph, pos_type, bool>
61 MatchString(docstring const & str, bool cs, bool mw)
62 : str(str), cs(cs), mw(mw)
65 // returns true if the specified string is at the specified position
66 // del specifies whether deleted strings in ct mode will be considered
67 bool operator()(Paragraph const & par, pos_type pos, bool del = true) const
69 return par.find(str, cs, mw, pos, del);
77 // match whole words only
82 bool findForward(DocIterator & cur, MatchString const & match,
85 for (; cur; cur.forwardChar())
87 match(cur.paragraph(), cur.pos(), find_del))
93 bool findBackwards(DocIterator & cur, MatchString const & match,
99 match(cur.paragraph(), cur.pos(), find_del))
106 bool findChange(DocIterator & cur)
108 for (; cur; cur.forwardPos())
109 if (cur.inTexted() && !cur.paragraph().isUnchanged(cur.pos()))
115 bool searchAllowed(BufferView * /*bv*/, docstring const & str)
118 frontend::Alert::error(_("Search error"),
119 _("Search string is empty"));
126 bool find(BufferView * bv, docstring const & searchstr, bool cs, bool mw, bool fw,
127 bool find_del = true)
129 if (!searchAllowed(bv, searchstr))
132 DocIterator cur = bv->cursor();
134 MatchString const match(searchstr, cs, mw);
136 bool found = fw ? findForward(cur, match, find_del) :
137 findBackwards(cur, match, find_del);
140 bv->putSelectionAt(cur, searchstr.length(), !fw);
146 int replaceAll(BufferView * bv,
147 docstring const & searchstr, docstring const & replacestr,
150 Buffer & buf = bv->buffer();
152 if (!searchAllowed(bv, searchstr) || buf.isReadonly())
155 bv->cursor().recordUndoFullDocument();
157 MatchString const match(searchstr, cs, mw);
160 int const rsize = replacestr.size();
161 int const ssize = searchstr.size();
163 DocIterator cur = doc_iterator_begin(buf.inset());
164 while (findForward(cur, match, false)) {
165 pos_type pos = cur.pos();
167 = cur.paragraph().getFontSettings(buf.params(), pos);
168 int striked = ssize - cur.paragraph().eraseChars(pos, pos + ssize,
169 buf.params().trackChanges);
170 cur.paragraph().insert(pos, replacestr, font,
171 Change(buf.params().trackChanges ?
172 Change::INSERTED : Change::UNCHANGED));
173 for (int i = 0; i < rsize + striked; ++i)
179 bv->putSelectionAt(doc_iterator_begin(buf.inset()), 0, false);
186 bool stringSelected(BufferView * bv, docstring const & searchstr,
187 bool cs, bool mw, bool fw)
189 // if nothing selected or selection does not equal search
190 // string search and select next occurance and return
191 docstring const & str1 = searchstr;
192 docstring const str2 = bv->cursor().selectionAsString(false);
193 if ((cs && str1 != str2) || compare_no_case(str1, str2) != 0) {
194 find(bv, searchstr, cs, mw, fw);
202 int replace(BufferView * bv, docstring const & searchstr,
203 docstring const & replacestr, bool cs, bool mw, bool fw)
205 if (!searchAllowed(bv, searchstr) || bv->buffer().isReadonly())
208 if (!stringSelected(bv, searchstr, cs, mw, fw))
211 Cursor & cur = bv->cursor();
212 cap::replaceSelectionWithString(cur, replacestr, fw);
213 bv->buffer().markDirty();
214 find(bv, searchstr, cs, mw, fw, false);
215 bv->processUpdateFlags(Update::Force | Update::FitCursor);
223 docstring const find2string(docstring const & search,
224 bool casesensitive, bool matchword, bool forward)
228 << int(casesensitive) << ' '
229 << int(matchword) << ' '
235 docstring const replace2string(docstring const & search, docstring const & replace,
236 bool casesensitive, bool matchword,
237 bool all, bool forward)
242 << int(casesensitive) << ' '
243 << int(matchword) << ' '
250 void find(BufferView * bv, FuncRequest const & ev)
252 if (!bv || ev.action != LFUN_WORD_FIND)
255 //lyxerr << "find called, cmd: " << ev << std::endl;
257 // data is of the form
259 // <casesensitive> <matchword> <forward>"
261 docstring howto = split(ev.argument(), search, '\n');
263 bool casesensitive = parse_bool(howto);
264 bool matchword = parse_bool(howto);
265 bool forward = parse_bool(howto);
267 bool const found = find(bv, search,
268 casesensitive, matchword, forward);
271 // emit message signal.
272 bv->message(_("String not found!"));
276 void replace(BufferView * bv, FuncRequest const & ev, bool has_deleted)
278 if (!bv || ev.action != LFUN_WORD_REPLACE)
281 // data is of the form
284 // <casesensitive> <matchword> <all> <forward>"
287 docstring howto = split(ev.argument(), search, '\n');
288 howto = split(howto, rplc, '\n');
290 bool casesensitive = parse_bool(howto);
291 bool matchword = parse_bool(howto);
292 bool all = parse_bool(howto);
293 bool forward = parse_bool(howto);
296 int const replace_count = all
297 ? replaceAll(bv, search, rplc, casesensitive, matchword)
298 : replace(bv, search, rplc, casesensitive, matchword, forward);
300 Buffer & buf = bv->buffer();
301 if (replace_count == 0) {
302 // emit message signal.
303 buf.message(_("String not found!"));
305 if (replace_count == 1) {
306 // emit message signal.
307 buf.message(_("String has been replaced."));
309 docstring str = convert<docstring>(replace_count);
310 str += _(" strings have been replaced.");
311 // emit message signal.
316 // if we have deleted characters, we do not replace at all, but
317 // rather search for the next occurence
318 bool const found = find(bv, search,
319 casesensitive, matchword, forward);
322 // emit message signal.
323 bv->message(_("String not found!"));
328 bool findNextChange(BufferView * bv)
330 DocIterator cur = bv->cursor();
332 if (!findChange(cur))
335 bv->cursor().setCursor(cur);
336 bv->cursor().resetAnchor();
338 Change orig_change = cur.paragraph().lookupChange(cur.pos());
340 CursorSlice & tip = cur.top();
341 for (; !tip.at_end(); tip.forwardPos()) {
342 Change change = tip.paragraph().lookupChange(tip.pos());
343 if (change != orig_change)
346 // avoid crash (assertion violation) if the imaginary end-of-par
347 // character of the last paragraph of the document is marked as changed
351 // Now put cursor to end of selection:
352 bv->cursor().setCursor(cur);
353 bv->cursor().setSelection();