]> git.lyx.org Git - lyx.git/blob - src/lyxfind.C
Fix working of the spellchecker dialog with ispell when there are no
[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 "lyx_gui_misc.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                 WriteAlert(_("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                     (mw && ((pos <= 0 || !IsLetterCharOrDigit(par->getChar(pos - 1)))
206                              && (pos + Paragraph::size_type(size) >= par->size()
207                                  || !IsLetterCharOrDigit(par->getChar(pos + size))))
208                      ))
209                 {
210                         return true;
211                 }
212         }
213         return false;
214 }
215
216 // forward search:
217 // if the string can be found: return true and set the cursor to
218 // the new position, cs = casesensitive, mw = matchword
219 SearchResult SearchForward(BufferView * bv, LyXText * text, string const & str,
220                            bool const & cs, bool const & mw)
221 {
222         Paragraph * par = text->cursor.par();
223         Paragraph::size_type pos = text->cursor.pos();
224         UpdatableInset * inset;
225
226         while (par && !IsStringInText(par, pos, str, cs, mw)) {
227                 if (par->isInset(pos) &&
228                         (inset = (UpdatableInset *)par->getInset(pos)) &&
229                         (inset->isTextInset()))
230                 {
231                         // lock the inset!
232                         text->setCursor(bv, par, pos);
233                         inset->edit(bv);
234                         if (inset->searchForward(bv, str, cs, mw))
235                                 return SR_FOUND_NOUPDATE;
236                         text = bv->getLyXText();
237                 }
238                 if (pos < par->size() - 1)
239                         ++pos;
240                 else {
241                         pos = 0;
242                         par = par->next();
243                 }
244         }
245         if (par) {
246                 text->setCursor(bv, par, pos);
247                 return SR_FOUND;
248 #if 0
249         } else if (text->inset_owner) {
250                 // test if we're inside an inset if yes unlock the inset
251                 // and recall us with the outside LyXText!
252                 bv->unlockInset((UpdatableInset *)text->inset_owner);
253                 if (!bv->theLockingInset()) {
254                         text = bv->getLyXText();
255                         par = text->cursor.par();
256                         pos = text->cursor.pos();
257                         if (pos < par->size() - 1)
258                                 ++pos;
259                         else {
260                                 pos = 0;
261                                 par = par->next();
262                         }
263                         if (!par)
264                                 return SR_NOT_FOUND;
265                         text->setCursor(bv, par, pos);
266                         return SearchForward(bv, text, str, cs, mw);
267                 } else {
268                         return SR_NOT_FOUND;
269                 }
270 #endif
271         } else
272                 return SR_NOT_FOUND;
273 }
274
275
276 // backward search:
277 // if the string can be found: return true and set the cursor to
278 // the new position, cs = casesensitive, mw = matchword
279 SearchResult SearchBackward(BufferView * bv, LyXText * text,
280                             string const & str,
281                             bool const & cs, bool const & mw)
282 {
283         Paragraph * par = text->cursor.par();
284         Paragraph::size_type pos = text->cursor.pos();
285
286         do {
287                 if (pos > 0)
288                         --pos;
289                 else {
290                         // We skip empty paragraphs (Asger)
291                         do {
292                                 par = par->previous();
293                                 if (par)
294                                         pos = par->size() - 1;
295                         } while (par && pos < 0);
296                 }
297                 UpdatableInset * inset;
298                 if (par && par->isInset(pos) &&
299                         (inset = (UpdatableInset *)par->getInset(pos)) &&
300                         (inset->isTextInset()))
301                 {
302                         // lock the inset!
303                         text->setCursor(bv, par, pos);
304                         inset->edit(bv, false);
305                         if (inset->searchBackward(bv, str, cs, mw))
306                                 return SR_FOUND_NOUPDATE;
307                         text = bv->getLyXText();
308                 }               
309         } while (par && !IsStringInText(par, pos, str, cs, mw));
310   
311         if (par) {
312                 text->setCursor(bv, par, pos);
313                 return SR_FOUND;
314         }
315 #if 0
316         else if (text->inset_owner) {
317                 // test if we're inside an inset if yes unlock the inset
318                 // and recall us with the outside LyXText!
319                 bv->unlockInset((UpdatableInset *)text->inset_owner);
320                 if (!bv->theLockingInset()) {
321                         return SearchBackward(bv, bv->getLyXText(), str, cs, mw);
322                 }
323         }
324 #endif
325         return SR_NOT_FOUND;
326 }
327