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