]> git.lyx.org Git - lyx.git/blob - src/lyxfind.C
Hotfix to move cursor to the right when pressing M-m g x in text mode.
[lyx.git] / src / lyxfind.C
1 #include <config.h>
2
3 #ifdef __GNUG__
4 #pragma implementation
5 #endif
6
7 #include "lyxtext.h"
8 #include "lyxfind.h"
9 #include "LyXView.h"
10 #include "frontends/Alert.h"
11 #include "support/textutils.h"
12 #include "support/lstrings.h"
13 #include "BufferView.h"
14 #include "buffer.h"
15 #include "debug.h"
16 #include "gettext.h"
17
18 using lyx::pos_type;
19
20
21 ///
22 // locally used enum
23 ///
24 enum SearchResult {
25         //
26         SR_NOT_FOUND = 0,
27         //
28         SR_FOUND,
29         //
30         SR_FOUND_NOUPDATE
31 };
32
33
34 /// returns true if the specified string is at the specified  position
35 bool IsStringInText(Paragraph * par, pos_type pos,
36                     string const & str, bool const & = true,
37                     bool const & = false);
38
39 /// if the string is found: return true and set the cursor to the new position
40 SearchResult SearchForward(BufferView *, LyXText * text, string const & str,
41                            bool const & = true, bool const & = false);
42 ///
43 SearchResult SearchBackward(BufferView *, LyXText * text, string const & str,
44                             bool const & = true, bool const & = false);
45
46
47 int LyXReplace(BufferView * bv,
48                string const & searchstr, string const & replacestr,
49                bool forward, bool casesens, bool matchwrd, bool replaceall,
50                bool once)
51 {
52         if (!bv->available() || bv->buffer()->isReadonly())
53                 return 0;
54
55         // CutSelection cannot cut a single space, so we have to stop
56         // in order to avoid endless loop :-(
57         if (searchstr.length() == 0
58                 || (searchstr.length() == 1 && searchstr[0] == ' '))
59         {
60                 Alert::alert(_("Sorry!"), _("You cannot replace a single space, "
61                                           "nor an empty character."));
62                 return 0;
63         }
64
65         LyXText * text = bv->getLyXText();
66
67         // now we can start searching for the first
68         // start at top if replaceall
69         bool fw = forward;
70         if (replaceall) {
71                 text->clearSelection();
72                 if (text->inset_owner) {
73                         bv->unlockInset(bv->theLockingInset());
74                         text = bv->text;
75                 }
76                 text->cursorTop(bv);
77                 // override search direction because we search top to bottom
78                 fw = true;
79         }
80
81         // if nothing selected or selection does not equal search string
82         // search and select next occurance and return if no replaceall
83         string str1;
84         string str2;
85         if (casesens) {
86                 str1 = searchstr;
87                 str2 = text->selectionAsString(bv->buffer(), false);
88         } else {
89                 str1 = lowercase(searchstr);
90                 str2 = lowercase(text->selectionAsString(bv->buffer(), false));
91         }
92         if (str1 != str2) {
93                 if (!LyXFind(bv, searchstr, fw, false, casesens, matchwrd) ||
94                         !replaceall)
95                 {
96                         return 0;
97                 }
98         }
99
100         bool found = false;
101         int replace_count = 0;
102         do {
103                 bv->hideCursor();
104                 bv->update(bv->getLyXText(), BufferView::SELECT|BufferView::FITCUR);
105                 bv->toggleSelection(false);
106                 bv->getLyXText()->replaceSelectionWithString(bv, replacestr);
107                 bv->getLyXText()->setSelectionOverString(bv, replacestr);
108                 bv->update(bv->getLyXText(), BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
109                 ++replace_count;
110                 if (!once)
111                         found = LyXFind(bv, searchstr, fw, false, casesens, matchwrd);
112         } while (!once && replaceall && found);
113
114         if (bv->focus())
115                 bv->showCursor();
116
117         return replace_count;
118 }
119
120 bool LyXFind(BufferView * bv,
121              string const & searchstr, bool forward,
122              bool frominset, bool casesens, bool matchwrd)
123 {
124         if (!bv->available() || searchstr.empty())
125                 return false;
126
127         LyXText * text = bv->getLyXText();
128
129         bv->hideCursor();
130         bv->update(text, BufferView::SELECT|BufferView::FITCUR);
131
132         if (text->selection.set())
133                 text->cursor = forward ?
134                         text->selection.end : text->selection.start;
135
136         SearchResult result = SR_NOT_FOUND;
137
138         if (!frominset && bv->theLockingInset()) {
139                 bool found = forward ?
140                         bv->theLockingInset()->searchForward(bv, searchstr, casesens, matchwrd) :
141                         bv->theLockingInset()->searchBackward(bv, searchstr, casesens, matchwrd);
142                 if (found)
143                         result = SR_FOUND_NOUPDATE;
144                 else {
145                         text = bv->getLyXText();
146                         Paragraph * par = text->cursor.par();
147                         pos_type pos = text->cursor.pos();
148                         if (forward) {
149                                 if (pos < par->size() - 1)
150                                         ++pos;
151                                 else {
152                                         pos = 0;
153                                         par = par->next();
154                                 }
155                                 if (par)
156                                         text->setCursor(bv, par, pos);
157                         }
158                         if (par) {
159                                 result = forward ?
160                                         SearchForward(bv, text, searchstr, casesens, matchwrd) :
161                                         SearchBackward(bv, text, searchstr, casesens, matchwrd);
162                         }
163                 }
164         } else {
165                 result = forward ?
166                         SearchForward(bv, text, searchstr, casesens, matchwrd) :
167                         SearchBackward(bv, text, searchstr, casesens, matchwrd);
168         }
169
170         bool found = true;
171         if (result == SR_FOUND) {
172                 // the actual text pointer could have changed!
173                 bv->update(bv->getLyXText(), BufferView::SELECT|BufferView::FITCUR);
174                 bv->toggleSelection();
175                 bv->getLyXText()->clearSelection();
176                 bv->getLyXText()->setSelectionOverString(bv, searchstr);
177                 bv->toggleSelection(false);
178                 bv->update(bv->getLyXText(), BufferView::SELECT|BufferView::FITCUR);
179         } else if (result == SR_NOT_FOUND) {
180                 bv->toggleSelection();
181                 bv->getLyXText()->clearSelection();
182                 bv->update(bv->getLyXText(), BufferView::SELECT|BufferView::FITCUR);
183                 found = false;
184         }
185         if (bv->focus())
186                 bv->showCursor();
187
188         return found;
189 }
190
191
192 // returns true if the specified string is at the specified position
193 bool IsStringInText(Paragraph * par, pos_type pos,
194                     string const & str, bool const & cs,
195                     bool const & mw)
196 {
197         if (!par)
198                 return false;
199
200         string::size_type size = str.length();
201         pos_type i = 0;
202         while (((pos + i) < par->size())
203                && (string::size_type(i) < size)
204                && (cs ? (str[i] == par->getChar(pos + i))
205                    : (uppercase(str[i]) == uppercase(par->getChar(pos + i)))))
206         {
207                 ++i;
208         }
209         if (size == string::size_type(i)) {
210                 // if necessary, check whether string matches word
211                 if (!mw)
212                         return true;
213                 if ((pos <= 0 || !IsLetterCharOrDigit(par->getChar(pos - 1)))
214                         && (pos + pos_type(size) >= par->size()
215                         || !IsLetterCharOrDigit(par->getChar(pos + size)))) {
216                         return true;
217                 }
218         }
219         return false;
220 }
221
222 // forward search:
223 // if the string can be found: return true and set the cursor to
224 // the new position, cs = casesensitive, mw = matchword
225 SearchResult SearchForward(BufferView * bv, LyXText * text, string const & str,
226                            bool const & cs, bool const & mw)
227 {
228         Paragraph * par = text->cursor.par();
229         pos_type pos = text->cursor.pos();
230         Paragraph * prev_par = par;
231         UpdatableInset * inset;
232
233         while (par && !IsStringInText(par, pos, str, cs, mw)) {
234                 if (par->isInset(pos) &&
235                         (inset = (UpdatableInset *)par->getInset(pos)) &&
236                         (inset->isTextInset()))
237                 {
238                         // lock the inset!
239                         text->setCursor(bv, par, pos);
240                         inset->edit(bv);
241                         if (inset->searchForward(bv, str, cs, mw))
242                                 return SR_FOUND_NOUPDATE;
243                         text = bv->getLyXText();
244                 }
245
246                 ++pos;
247
248                 if (pos >= par->size()) {
249                         prev_par = par;
250                         par = par->next();
251                         pos = 0;
252                 }
253         }
254
255         if (par) {
256                 text->setCursor(bv, par, pos);
257                 return SR_FOUND;
258         } else {
259                 // make sure we end up at the end of the text,
260                 // not the start point of the last search
261                 text->setCursor(bv, prev_par, prev_par->size());
262                 return SR_NOT_FOUND;
263         }
264 }
265
266
267 // backward search:
268 // if the string can be found: return true and set the cursor to
269 // the new position, cs = casesensitive, mw = matchword
270 SearchResult SearchBackward(BufferView * bv, LyXText * text,
271                             string const & str,
272                             bool const & cs, bool const & mw)
273 {
274         Paragraph * par = text->cursor.par();
275         pos_type pos = text->cursor.pos();
276         Paragraph * prev_par = par;
277
278         do {
279                 if (pos > 0)
280                         --pos;
281                 else {
282                         prev_par = par;
283                         // We skip empty paragraphs (Asger)
284                         do {
285                                 par = par->previous();
286                                 if (par)
287                                         pos = par->size() - 1;
288                         } while (par && pos < 0);
289                 }
290                 UpdatableInset * inset;
291                 if (par && par->isInset(pos) &&
292                         (inset = (UpdatableInset *)par->getInset(pos)) &&
293                         (inset->isTextInset()))
294                 {
295                         // lock the inset!
296                         text->setCursor(bv, par, pos);
297                         inset->edit(bv, false);
298                         if (inset->searchBackward(bv, str, cs, mw))
299                                 return SR_FOUND_NOUPDATE;
300                         text = bv->getLyXText();
301                 }
302         } while (par && !IsStringInText(par, pos, str, cs, mw));
303
304         if (par) {
305                 text->setCursor(bv, par, pos);
306                 return SR_FOUND;
307         } else {
308                 // go to the last part of the unsuccessful search
309                 text->setCursor(bv, prev_par, 0);
310                 return SR_NOT_FOUND;
311         }
312 }