]> git.lyx.org Git - lyx.git/blob - src/lyxfind.C
64 bit compile fixes.
[lyx.git] / src / lyxfind.C
1 /**
2  * \file lyxfind.C
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Lars Gullik Bjønnes
7  * \author John Levon
8  * \author Jürgen Vigna
9  * \author Alfredo Braunstein
10  *
11  * Full author contact details are available in file CREDITS.
12  */
13
14 #include <config.h>
15
16 #include "lyxfind.h"
17
18 #include "buffer.h"
19 #include "cursor.h"
20 #include "BufferView.h"
21 #include "debug.h"
22 #include "iterators.h"
23 #include "funcrequest.h"
24 #include "gettext.h"
25 #include "lyxtext.h"
26 #include "paragraph.h"
27 #include "PosIterator.h"
28 #include "undo.h"
29
30 #include "frontends/Alert.h"
31 #include "frontends/LyXView.h"
32
33 #include "support/textutils.h"
34 #include "support/tostr.h"
35
36 #include "support/std_sstream.h"
37
38 using lyx::support::lowercase;
39 using lyx::support::uppercase;
40 using lyx::support::split;
41
42 using std::advance;
43 using std::ostringstream;
44 using std::string;
45
46
47 namespace {
48
49 bool parse_bool(string & howto)
50 {
51         if (howto.empty())
52                 return false;
53         string var;
54         howto = split(howto, var, ' ');
55         return (var == "1");
56 }
57
58
59 bool find(BufferView * bv,
60           string const & searchstr, bool cs, bool mw, bool fw);
61
62
63 int replace(BufferView * bv,
64             string const & searchstr, string const & replacestr,
65             bool cs, bool mw, bool fw);
66
67
68 int replaceAll(BufferView * bv,
69                string const & searchstr, string const & replacestr,
70                bool cs, bool mw);
71
72
73 bool findChange(PosIterator & cur, PosIterator const & end);
74
75 } // namespace anon
76
77
78 namespace lyx {
79 namespace find {
80
81 string const find2string(string const & search,
82                          bool casesensitive, bool matchword, bool forward)
83 {
84         ostringstream ss;
85         ss << search << '\n'
86            << int(casesensitive) << ' '
87            << int(matchword) << ' '
88            << int(forward);
89
90         return ss.str();
91 }
92
93
94 string const replace2string(string const & search, string const & replace,
95                             bool casesensitive, bool matchword,
96                             bool all, bool forward)
97 {
98         ostringstream ss;
99         ss << search << '\n'
100            << replace << '\n'
101            << int(casesensitive) << ' '
102            << int(matchword) << ' '
103            << int(all) << ' '
104            << int(forward);
105
106         return ss.str();
107 }
108
109
110 void find(BufferView * bv, FuncRequest const & ev)
111 {
112         if (!bv || ev.action != LFUN_WORD_FIND)
113                 return;
114
115         lyxerr << "find called, cmd: " << ev << std::endl;
116
117         // data is of the form
118         // "<search>
119         //  <casesensitive> <matchword> <forward>"
120         string search;
121         string howto = split(ev.argument, search, '\n');
122
123         bool casesensitive = parse_bool(howto);
124         bool matchword     = parse_bool(howto);
125         bool forward       = parse_bool(howto);
126
127         bool const found = ::find(bv, search,
128                                   casesensitive, matchword, forward);
129
130         if (!found)
131                 bv->owner()->message(_("String not found!"));
132 }
133
134
135 void replace(BufferView * bv, FuncRequest const & ev)
136 {
137         if (!bv || ev.action != LFUN_WORD_REPLACE)
138                 return;
139
140         // data is of the form
141         // "<search>
142         //  <replace>
143         //  <casesensitive> <matchword> <all> <forward>"
144         string search;
145         string replace;
146         string howto = split(ev.argument, search, '\n');
147         howto = split(howto, replace, '\n');
148
149         bool casesensitive = parse_bool(howto);
150         bool matchword     = parse_bool(howto);
151         bool all           = parse_bool(howto);
152         bool forward       = parse_bool(howto);
153
154         LyXView * lv = bv->owner();
155
156         int const replace_count = all ?
157                 ::replaceAll(bv, search, replace,
158                              casesensitive, matchword) :
159                 ::replace(bv, search, replace,
160                           casesensitive, matchword, forward);
161
162         if (replace_count == 0) {
163                 lv->message(_("String not found!"));
164         } else {
165                 if (replace_count == 1) {
166                         lv->message(_("String has been replaced."));
167                 } else {
168                         string str = tostr(replace_count);
169                         str += _(" strings have been replaced.");
170                         lv->message(str);
171                 }
172         }
173 }
174
175
176 bool findNextChange(BufferView * bv)
177 {
178         if (!bv->available())
179                 return false;
180
181         PosIterator cur = PosIterator(*bv);
182         PosIterator const endit = bv->buffer()->pos_iterator_end();
183
184         if (!findChange(cur, endit))
185                 return false;
186
187         ParagraphList::iterator pit = cur.pit();
188         pos_type pos = cur.pos();
189
190         Change orig_change = pit->lookupChangeFull(pos);
191         pos_type parsize = pit->size();
192         pos_type end = pos;
193
194         for (; end != parsize; ++end) {
195                 Change change = pit->lookupChangeFull(end);
196                 if (change != orig_change) {
197                         // slight UI optimisation: for replacements, we get
198                         // text like : _old_new. Consider that as one change.
199                         if (!(orig_change.type == Change::DELETED &&
200                                 change.type == Change::INSERTED))
201                                 break;
202                 }
203         }
204         pos_type length = end - pos;
205         bv->putSelectionAt(cur, length, true);
206         return true;
207 }
208
209 } // find namespace
210 } // lyx namespace
211
212
213 namespace {
214
215 class MatchString : public std::binary_function<Paragraph, lyx::pos_type, bool>
216 {
217 public:
218         MatchString(string const & str, bool cs, bool mw)
219                 : str(str), cs(cs), mw(mw)
220         {}
221
222         // returns true if the specified string is at the specified position
223         bool operator()(Paragraph const & par, lyx::pos_type pos) const
224         {
225                 string::size_type const size = str.length();
226                 lyx::pos_type i = 0;
227                 lyx::pos_type const parsize = par.size();
228                 for (i = 0; pos + i < parsize; ++i) {
229                         if (string::size_type(i) >= size)
230                                 break;
231                   if (cs && str[i] != par.getChar(pos + i))
232                                 break;
233                         if (!cs && uppercase(str[i]) != uppercase(par.getChar(pos + i)))
234                                 break;
235                 }
236
237                 if (size != string::size_type(i))
238                         return false;
239
240                 // if necessary, check whether string matches word
241                 if (mw) {
242                         if (pos > 0     && IsLetterCharOrDigit(par.getChar(pos - 1)))
243                                 return false;
244                         if (pos + lyx::pos_type(size) < parsize
245                                         && IsLetterCharOrDigit(par.getChar(pos + size)));
246                                 return false;
247                 }
248
249                 return true;
250         }
251
252 private:
253         // search string
254         string str;
255         // case sensitive
256         bool cs;
257         // match whole words only
258         bool mw;
259 };
260
261
262 bool findForward(PosIterator & cur, PosIterator const & end,
263                  MatchString const & match)
264 {
265         for (; cur != end; ++cur) {
266                 if (match(*cur.pit(), cur.pos()))
267                         return true;
268         }
269         return false;
270 }
271
272
273 bool findBackwards(PosIterator & cur, PosIterator const & beg,
274                    MatchString const & match)
275 {
276         while (beg != cur) {
277                 --cur;
278                 if (match(*cur.pit(), cur.pos()))
279                         return true;
280         }
281         return false;
282 }
283
284
285 bool findChange(PosIterator & cur, PosIterator const & end)
286 {
287         for (; cur != end; ++cur) {
288                 if ((cur.pit()->empty() || !cur.at_end())
289                     && cur.pit()->lookupChange(cur.pos()) != Change::UNCHANGED)
290                         return true;
291         }
292         return false;
293 }
294
295
296 bool searchAllowed(BufferView * bv, string const & str)
297 {
298         if (str.empty()) {
299                 Alert::error(_("Search error"), _("Search string is empty"));
300                 return false;
301         }
302         return bv->available();
303 }
304
305
306 bool find(BufferView * bv, string const & searchstr, bool cs, bool mw, bool fw)
307 {
308         if (!searchAllowed(bv, searchstr))
309                 return false;
310
311         PosIterator cur = PosIterator(*bv);
312
313         MatchString const match(searchstr, cs, mw);
314
315         PosIterator const end = bv->buffer()->pos_iterator_end();
316         PosIterator const beg = bv->buffer()->pos_iterator_begin();
317
318         bool found = fw ? findForward(cur, end, match)
319                 : findBackwards(cur, beg, match);
320
321         if (found)
322                 bv->putSelectionAt(cur, searchstr.length(), !fw);
323
324         return found;
325 }
326
327
328 int replaceAll(BufferView * bv,
329                string const & searchstr, string const & replacestr,
330                bool cs, bool mw)
331 {
332         Buffer & buf = *bv->buffer();
333
334         if (!searchAllowed(bv, searchstr) || buf.isReadonly())
335                 return 0;
336
337         recordUndoFullDocument(bv->cursor());
338
339         PosIterator cur = buf.pos_iterator_begin();
340         PosIterator const end = buf.pos_iterator_end();
341         MatchString const match(searchstr, cs, mw);
342         int num = 0;
343
344         int const rsize = replacestr.size();
345         int const ssize = searchstr.size();
346
347         while (findForward(cur, end, match)) {
348                 lyx::pos_type pos = cur.pos();
349                 LyXFont const font
350                         = cur.pit()->getFontSettings(buf.params(), pos);
351                 int striked = ssize - cur.pit()->erase(pos, pos + ssize);
352                 cur.pit()->insert(pos, replacestr, font);
353                 advance(cur, rsize + striked);
354                 ++num;
355         }
356
357         PosIterator beg = buf.pos_iterator_begin();
358         bv->text()->init(bv);
359         bv->putSelectionAt(beg, 0, false);
360         if (num)
361                 buf.markDirty();
362         return num;
363 }
364
365
366 bool stringSelected(BufferView * bv, string const & searchstr,
367                     bool cs, bool mw, bool fw)
368 {
369         // if nothing selected or selection does not equal search
370         // string search and select next occurance and return
371         string const & str1 = searchstr;
372         string const str2 = bv->cursor().selectionAsString(false);
373         if ((cs && str1 != str2) || lowercase(str1) != lowercase(str2)) {
374                 find(bv, searchstr, cs, mw, fw);
375                 return false;
376         }
377
378         return true;
379 }
380
381
382 int replace(BufferView * bv, string const & searchstr,
383       string const & replacestr, bool cs, bool mw, bool fw)
384 {
385         if (!searchAllowed(bv, searchstr) || bv->buffer()->isReadonly())
386                 return 0;
387
388         if (!stringSelected(bv, searchstr, cs, mw, fw))
389                 return 0;
390
391         LyXText * text = bv->getLyXText();
392
393         text->replaceSelectionWithString(bv->cursor(), replacestr);
394         text->setSelectionRange(bv->cursor(), replacestr.length());
395         bv->cursor().top() = fw ? bv->cursor().selEnd() : bv->cursor().selBegin();
396         bv->buffer()->markDirty();
397         find(bv, searchstr, cs, mw, fw);
398         bv->update();
399
400         return 1;
401 }
402
403 } //namespace anon