]> git.lyx.org Git - lyx.git/blob - src/lyxfind.C
Fix for bug: #260 special rules if partially linking
[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                    : (uppercase(str[i]) == uppercase(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         UpdatableInset * inset;
229
230         while (par && !IsStringInText(par, pos, str, cs, mw)) {
231                 if (par->isInset(pos) &&
232                         (inset = (UpdatableInset *)par->getInset(pos)) &&
233                         (inset->isTextInset()))
234                 {
235                         // lock the inset!
236                         text->setCursor(bv, par, pos);
237                         inset->edit(bv);
238                         if (inset->searchForward(bv, str, cs, mw))
239                                 return SR_FOUND_NOUPDATE;
240                         text = bv->getLyXText();
241                 }
242  
243                 ++pos;
244  
245                 if (pos >= par->size()) {
246                         prev_par = par;
247                         par = par->next();
248                         pos = 0;
249                 }
250         }
251  
252         if (par) {
253                 text->setCursor(bv, par, pos);
254                 return SR_FOUND;
255         } else {
256                 // make sure we end up at the end of the text,
257                 // not the start point of the last search
258                 text->setCursor(bv, prev_par, prev_par->size());
259                 return SR_NOT_FOUND;
260         }
261 }
262
263
264 // backward search:
265 // if the string can be found: return true and set the cursor to
266 // the new position, cs = casesensitive, mw = matchword
267 SearchResult SearchBackward(BufferView * bv, LyXText * text,
268                             string const & str,
269                             bool const & cs, bool const & mw)
270 {
271         Paragraph * par = text->cursor.par();
272         pos_type pos = text->cursor.pos();
273         Paragraph * prev_par = par;
274
275         do {
276                 if (pos > 0)
277                         --pos;
278                 else {
279                         prev_par = par;
280                         // We skip empty paragraphs (Asger)
281                         do {
282                                 par = par->previous();
283                                 if (par)
284                                         pos = par->size() - 1;
285                         } while (par && pos < 0);
286                 }
287                 UpdatableInset * inset;
288                 if (par && par->isInset(pos) &&
289                         (inset = (UpdatableInset *)par->getInset(pos)) &&
290                         (inset->isTextInset()))
291                 {
292                         // lock the inset!
293                         text->setCursor(bv, par, pos);
294                         inset->edit(bv, false);
295                         if (inset->searchBackward(bv, str, cs, mw))
296                                 return SR_FOUND_NOUPDATE;
297                         text = bv->getLyXText();
298                 }               
299         } while (par && !IsStringInText(par, pos, str, cs, mw));
300   
301         if (par) {
302                 text->setCursor(bv, par, pos);
303                 return SR_FOUND;
304         } else {
305                 // go to the last part of the unsuccessful search
306                 text->setCursor(bv, prev_par, 0);
307                 return SR_NOT_FOUND;
308         }
309 }
310