]> git.lyx.org Git - lyx.git/blob - src/lyxfind.C
the spellcheck cleanup
[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 "BufferView.h"
20 #include "debug.h"
21 #include "iterators.h"
22 #include "gettext.h"
23 #include "lyxtext.h"
24 #include "paragraph.h"
25 #include "PosIterator.h"
26 #include "undo.h"
27
28 #include "frontends/Alert.h"
29
30 #include "insets/insettext.h"
31
32 #include "support/textutils.h"
33
34 using lyx::support::lowercase;
35 using lyx::support::uppercase;
36 using bv_funcs::put_selection_at;
37
38 using std::string;
39
40 namespace lyx {
41 namespace find {
42
43
44 namespace {
45
46 class MatchString
47 {
48 public:
49         MatchString(string const & str, bool cs, bool mw)
50                 : str(str), cs(cs), mw(mw) {};
51 // returns true if the specified string is at the specified position
52         bool operator()(Paragraph const & par, pos_type pos) const
53         {                       
54                 string::size_type size = str.length();
55                 pos_type i = 0;
56                 pos_type parsize = par.size();
57                 while ((pos + i < parsize)
58                        && (string::size_type(i) < size)
59                        && (cs ? (str[i] == par.getChar(pos + i))
60                            : (uppercase(str[i]) == uppercase(par.getChar(pos + i))))) {
61                         ++i;
62                 }
63                 if (size == string::size_type(i)) {
64                         // if necessary, check whether string matches word
65                         if (!mw)
66                                 return true;
67                         if ((pos <= 0 || !IsLetterCharOrDigit(par.getChar(pos - 1)))
68                             && (pos + pos_type(size) >= parsize
69                                 || !IsLetterCharOrDigit(par.getChar(pos + size)))) {
70                                 return true;
71                         }
72                 }
73                 return false;
74         }
75         
76 private:
77         string str;
78         bool cs;
79         bool mw;
80 };
81
82
83
84 } //namespace anon
85
86
87
88
89
90 namespace {
91
92
93
94
95 bool findForward(PosIterator & cur, PosIterator const & end,
96                  MatchString & match)
97 {
98         for (; cur != end && !match(*cur.pit(), cur.pos()); ++cur)
99                 ;
100
101         return cur != end;
102 }
103
104
105 bool findBackwards(PosIterator & cur, PosIterator const & beg,
106                    MatchString & match)
107 {
108         if (beg == cur)
109                 return false;
110         do {
111                 --cur;
112                 if (match(*cur.pit(), cur.pos()))
113                         break;
114         } while (cur != beg);
115
116         return match(*cur.pit(), cur.pos());
117 }
118
119
120 bool findChange(PosIterator & cur, PosIterator const & end)
121 {
122         for (; cur != end; ++cur) {
123                 if ((!cur.pit()->size() || !cur.at_end())
124                     && cur.pit()->lookupChange(cur.pos()) != Change::UNCHANGED)
125                         break;
126         }
127         
128         return cur != end;
129 }
130
131
132 bool searchAllowed(BufferView * bv, string const & str)
133 {
134         if (str.empty()) {
135                 Alert::error(_("Search error"), _("Search string is empty"));
136                 return false;
137         }
138         if (!bv->available())
139                 return false;
140         return true;
141             
142 }
143
144 } // namespace anon
145
146
147 bool find(BufferView * bv, string const & searchstr,
148           bool cs, bool mw, bool fw)
149 {
150         if (!searchAllowed(bv, searchstr))
151                 return false;
152
153         PosIterator cur = PosIterator(*bv);
154
155         MatchString match(searchstr, cs, mw);
156         
157         bool found;
158
159         if (fw) {
160                 PosIterator const end = bv->buffer()->pos_iterator_end();
161                 found = findForward(cur, end, match);
162         } else {
163                 PosIterator const beg = bv->buffer()->pos_iterator_begin();
164                 found = findBackwards(cur, beg, match);
165         }
166         
167         if (found)
168                 put_selection_at(bv, cur, searchstr.length(), !fw);
169
170         return found;
171 }
172
173 namespace {
174         
175
176
177  
178 } //namespace anon
179
180
181 int replaceAll(BufferView * bv,
182                string const & searchstr, string const & replacestr,
183                bool cs, bool mw)
184 {
185         Buffer & buf = *bv->buffer();
186
187         if (!searchAllowed(bv, searchstr) || buf.isReadonly())
188                 return 0;
189         
190         recordUndo(Undo::ATOMIC, bv->text, 0,
191                    buf.paragraphs().size() - 1);
192         
193         PosIterator cur = buf.pos_iterator_begin();
194         PosIterator const end = buf.pos_iterator_end();
195         MatchString match(searchstr, cs, mw);
196         int num = 0;
197
198         int const rsize = replacestr.size();
199         int const ssize = searchstr.size();
200         while (findForward(cur, end, match)) {
201                 pos_type pos = cur.pos();
202                 LyXFont const font
203                         = cur.pit()->getFontSettings(buf.params(), pos);
204                 int striked = ssize - cur.pit()->erase(pos, pos + ssize);
205                 cur.pit()->insert(pos, replacestr, font);
206                 advance(cur, rsize + striked);
207                 ++num;
208         }
209         PosIterator beg = buf.pos_iterator_begin();
210         bv->text->init(bv);
211         put_selection_at(bv, beg, 0, false);
212         return num;
213 }
214
215
216 int replace(BufferView * bv,
217             string const & searchstr, string const & replacestr,
218             bool cs, bool mw, bool fw)
219 {
220         if (!searchAllowed(bv, searchstr) || bv->buffer()->isReadonly())
221                 return 0;
222         
223         {
224                 LyXText * text = bv->getLyXText();
225                 // if nothing selected or selection does not equal search
226                 // string search and select next occurance and return
227                 string const str1 = searchstr;
228                 string const str2 = text->selectionAsString(*bv->buffer(),
229                                                             false);
230                 if ((cs && str1 != str2)
231                     || lowercase(str1) != lowercase(str2)) {
232                         find(bv, searchstr, cs, mw, fw);
233                         return 0;
234                 }
235         }
236
237         LyXText * text = bv->getLyXText();
238         // We have to do this check only because mathed insets don't
239         // return their own LyXText but the LyXText of it's parent!
240         if (!bv->theLockingInset() ||
241             ((text != bv->text) &&
242              (text->inset_owner == text->inset_owner->getLockingInset()))) {
243                 text->replaceSelectionWithString(replacestr);
244                 text->setSelectionRange(replacestr.length());
245                 text->cursor = fw ? text->selection.end
246                         : text->selection.start;
247         }
248
249         // FIXME: should be called via an LFUN
250         bv->buffer()->markDirty();
251
252         find(bv, searchstr, cs, mw, fw);
253         bv->update();
254         
255         return 1;
256 }
257
258
259 bool findNextChange(BufferView * bv)
260 {
261         if (!bv->available())
262                 return false;
263
264         PosIterator cur = PosIterator(*bv);
265         PosIterator const endit = bv->buffer()->pos_iterator_end();
266
267         if (!findChange(cur, endit))
268                 return false;
269         
270         
271         ParagraphList::iterator pit = cur.pit();
272         pos_type pos = cur.pos();
273         
274         Change orig_change = pit->lookupChangeFull(pos);
275         pos_type parsize = pit->size();
276         pos_type end = pos;
277
278         for (; end != parsize; ++end) {
279                 Change change = pit->lookupChangeFull(end);
280                 if (change != orig_change) {
281                         // slight UI optimisation: for replacements, we get
282                         // text like : _old_new. Consider that as one change.
283                         if (!(orig_change.type == Change::DELETED &&
284                                 change.type == Change::INSERTED))
285                                 break;
286                 }
287         }
288         pos_type length = end - pos;
289         bv->text->init(bv);
290         put_selection_at(bv, cur, length, true);
291         return true;
292 }
293
294 } // find namespace
295 } // lyx namespace