]> git.lyx.org Git - lyx.git/blob - src/lyxfind.C
More fixes to the autocollapsing of paragraphs.
[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                 found = false;
181    
182         if (bv->focus())
183                 bv->showCursor();
184    
185         return found;
186 }
187
188
189 // returns true if the specified string is at the specified position
190 bool IsStringInText(Paragraph * par, pos_type pos,
191                     string const & str, bool const & cs,
192                     bool const & mw)
193 {
194         if (!par)
195                 return false;
196    
197         string::size_type size = str.length();
198         pos_type i = 0;
199         while (((pos + i) < par->size())
200                && (string::size_type(i) < size)
201                && (cs ? (str[i] == par->getChar(pos + i))
202                    : (toupper(str[i]) == toupper(par->getChar(pos + i)))))
203         {
204                 ++i;
205         }
206         if (size == string::size_type(i)) {
207                 // if necessary, check whether string matches word
208                 if (!mw)
209                         return true;
210                 if ((pos <= 0 || !IsLetterCharOrDigit(par->getChar(pos - 1)))
211                         && (pos + pos_type(size) >= par->size()
212                         || !IsLetterCharOrDigit(par->getChar(pos + size)))) {
213                         return true;
214                 }
215         }
216         return false;
217 }
218
219 // forward search:
220 // if the string can be found: return true and set the cursor to
221 // the new position, cs = casesensitive, mw = matchword
222 SearchResult SearchForward(BufferView * bv, LyXText * text, string const & str,
223                            bool const & cs, bool const & mw)
224 {
225         Paragraph * par = text->cursor.par();
226         pos_type pos = text->cursor.pos();
227         Paragraph * prev_par = par;
228         pos_type prev_pos;
229         UpdatableInset * inset;
230
231         while (par && !IsStringInText(par, pos, str, cs, mw)) {
232                 if (par->isInset(pos) &&
233                         (inset = (UpdatableInset *)par->getInset(pos)) &&
234                         (inset->isTextInset()))
235                 {
236                         // lock the inset!
237                         text->setCursor(bv, par, pos);
238                         inset->edit(bv);
239                         if (inset->searchForward(bv, str, cs, mw))
240                                 return SR_FOUND_NOUPDATE;
241                         text = bv->getLyXText();
242                 }
243  
244                 ++pos;
245  
246                 if (pos >= par->size()) {
247                         prev_par = par;
248                         // consider 0-sized empty pars
249                         prev_pos = std::min(pos, par->size());
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_pos);
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         pos_type prev_pos = pos;
278
279         do {
280                 if (pos > 0)
281                         --pos;
282                 else {
283                         prev_pos = pos;
284                         prev_par = par;
285                         // We skip empty paragraphs (Asger)
286                         do {
287                                 par = par->previous();
288                                 if (par)
289                                         pos = par->size() - 1;
290                         } while (par && pos < 0);
291                 }
292                 UpdatableInset * inset;
293                 if (par && par->isInset(pos) &&
294                         (inset = (UpdatableInset *)par->getInset(pos)) &&
295                         (inset->isTextInset()))
296                 {
297                         // lock the inset!
298                         text->setCursor(bv, par, pos);
299                         inset->edit(bv, false);
300                         if (inset->searchBackward(bv, str, cs, mw))
301                                 return SR_FOUND_NOUPDATE;
302                         text = bv->getLyXText();
303                 }               
304         } while (par && !IsStringInText(par, pos, str, cs, mw));
305   
306         if (par) {
307                 text->setCursor(bv, par, pos);
308                 return SR_FOUND;
309         } else {
310                 // go to the last part of the unsuccessful search
311                 text->setCursor(bv, prev_par, prev_pos);
312                 return SR_NOT_FOUND;
313         }
314 }
315