1 /* This file is part of
2 * ======================================================
4 * LyX, The Document Processor
6 * Copyright 1995 Matthias Ettrich,
7 * Copyright 1995-1999 The LyX Team.
9 * ======================================================*/
18 #pragma implementation
23 #include FORMS_H_LOCATION
28 #include "lyxscreen.h"
33 #include "lyx_gui_misc.h"
34 #include "minibuffer.h"
35 #include "support/lstrings.h"
37 extern BufferView *current_view; // called too many times in this file...
38 extern MiniBuffer *minibuffer;
40 // Maximum length copied from the current selection to the search string
41 const int LYXSEARCH_MAXLEN = 128;
43 // function prototypes
45 bool IsLetterCharOrDigit(char ch);
47 // If nothing selected, select the word at the cursor.
48 // Returns the current selection
49 // Note: this function should be in LyXText!
50 string const GetSelectionOrWordAtCursor(LyXText *lt);
52 // Returns the current selection. If nothing is selected or if the selection
53 // spans 2 paragraphs, an empty string is returned.
54 string const GetCurrentSelectionAsString(LyXText *lt);
56 // This is a copy of SetSelectionOverString from text.C
57 // It does the same, but uses only the length as a parameter
58 void SetSelectionOverLenChars(LyXText *lt, int len);
60 //-------------------------------------------------------------
62 bool IsLetterCharOrDigit(char ch)
64 return IsLetterChar(ch) || isdigit(ch);
68 // Returns the current selection. If nothing is selected or if the selection
69 // spans 2 paragraphs, an empty string is returned.
70 string const GetCurrentSelectionAsString(LyXText *lt)
76 char sz[LYXSEARCH_MAXLEN];
82 if (lt->selection && (lt->sel_cursor.par == par)) {
83 // (selected) and (begin/end in same paragraph)
84 pos = lt->sel_start_cursor.pos;
85 endpos = lt->sel_end_cursor.pos;
88 while ((i < LYXSEARCH_MAXLEN-2) &&
89 (pos < par->Last()) && (pos < endpos)) {
90 ch = par->GetChar(pos);
92 //HB??: Maybe (ch <= ' ')
93 if ((ch == ' ') || ((unsigned char)ch <= LYX_META_INSET)) {
94 // consecutive spaces --> 1 space char
96 pos++; // Next text pos
97 continue; // same search pos
103 fPrevIsSpace = false;
114 // If nothing selected, select the word at the cursor.
115 // Returns the current selection
116 string const GetSelectionOrWordAtCursor(LyXText *lt)
118 lt->SelectWordWhenUnderCursor();
119 return GetCurrentSelectionAsString(lt);
123 // This is a copy of SetSelectionOverString from text.C
124 // It does the same, but uses only the length as a parameter
125 void SetSelectionOverLenChars(LyXText *lt, int len)
127 lt->sel_cursor = lt->cursor;
129 for (i=0; i < len; i++)
135 //------------------------------
137 void LyXFindReplace1::StartSearch()
139 LyXFindReplace0::StartSearch();
140 SetReplaceEnabled(!current_view->currentBuffer()->isReadonly());
141 searchForward = true;
142 if (lsSearch.empty())
143 SetSearchString(GetSelectionOrWordAtCursor(current_view->currentBuffer()->text));
147 // TODO?: the user can insert multiple spaces with this routine (1999-01-11, dnaber)
148 void LyXFindReplace1::SearchReplaceCB()
152 if (!current_view->getScreen())
154 if (current_view->currentBuffer()->isReadonly())
157 // CutSelection cannot cut a single space, so we have to stop
158 // in order to avoid endless loop :-(
160 if (SearchString().length() == 0 || (SearchString().length() == 1
161 && SearchString()[0] == ' ') ) {
162 WriteAlert(_("Sorry!"), _("You cannot replace a single space, "
163 "nor an empty character."));
167 string const replacestring = ReplaceString();
169 current_view->getScreen()->HideCursor();
170 current_view->currentBuffer()->update(-2);
172 ltCur = current_view->currentBuffer()->text;
173 if (ltCur->selection) {
174 // clear the selection (if there is any)
175 current_view->getScreen()->ToggleSelection(false);
176 current_view->currentBuffer()->text->
177 ReplaceSelectionWithString(replacestring.c_str());
178 current_view->currentBuffer()->text->
179 SetSelectionOverString(replacestring.c_str());
180 current_view->currentBuffer()->update(1);
183 // jump to next match:
184 SearchCB( searchForward );
188 // replaces all occurences of a string (1999-01-15, dnaber@mini.gt.owl.de)
189 void LyXFindReplace1::SearchReplaceAllCB()
193 if (!current_view->getScreen())
195 if (current_view->currentBuffer()->isReadonly())
198 // CutSelection cannot cut a single space, so we have to stop
199 // in order to avoid endless loop :-(
201 if (SearchString().length() == 0 || (SearchString().length() == 1
202 && SearchString()[0] == ' ') ) {
203 WriteAlert(_("Sorry!"), _("You cannot replace a single space, "
204 "nor an empty character."));
208 string const replacestring = ReplaceString();
210 current_view->getScreen()->HideCursor();
213 current_view->currentBuffer()->text->ClearSelection();
214 current_view->currentBuffer()->text->CursorTop();
216 int replace_count = 0;
218 ltCur = current_view->currentBuffer()->text;
219 if (ltCur->selection) {
220 current_view->currentBuffer()->update(-2);
221 current_view->getScreen()->ToggleSelection(false);
222 current_view->currentBuffer()->text->
223 ReplaceSelectionWithString(replacestring.c_str());
224 current_view->currentBuffer()->text->
225 SetSelectionOverString(replacestring.c_str());
226 current_view->currentBuffer()->update(1);
229 } while( SearchCB(true) );
231 if( replace_count == 0 ) {
233 minibuffer->Set(_("String not found!"));
235 if( replace_count == 1 ) {
236 minibuffer->Set(_("1 string has been replaced."));
238 string str = tostr(replace_count);
239 str += _(" strings have been replaced.");
240 minibuffer->Set(str);
246 bool LyXFindReplace1::SearchCB(bool fForward)
251 // store search direction
252 searchForward = fForward;
254 if (!current_view->getScreen())
257 current_view->getScreen()->HideCursor();
258 current_view->currentBuffer()->update(-2);
259 ltCur = current_view->currentBuffer()->text;
260 if (ltCur->selection)
261 ltCur->cursor = fForward ? ltCur->sel_end_cursor :
262 ltCur->sel_start_cursor;
265 iLenSelected = SearchString().length();
267 if (!ValidSearchData() ||
268 (fForward ? SearchForward(ltCur) : SearchBackward(ltCur))) {
269 current_view->currentBuffer()->update(-2);
271 // clear the selection (if there is any)
272 current_view->getScreen()->ToggleSelection();
273 current_view->currentBuffer()->text->ClearSelection();
275 // set the new selection
276 SetSelectionOverLenChars(current_view->currentBuffer()->text, iLenSelected);
277 current_view->getScreen()->ToggleSelection(false);
278 minibuffer->Set(_("Found."));
282 minibuffer->Set(_("String not found!"));
286 if (current_view->getWorkArea()->focus)
287 current_view->getScreen()->ShowCursor();
293 // if the string can be found: return true and set the cursor to
295 // (was: LyXText::SearchForward(char const* string) in text2.C )
296 bool LyXFindReplace1::SearchForward(LyXText *lt)
301 par = lt->cursor.par;
302 pos = lt->cursor.pos;
304 while (par && !IsSearchStringInText(par,pos)) {
305 if (pos<par->Last()-1)
313 lt->SetCursor(par,pos);
320 // if the string can be found: return true and set the cursor to
322 // (was: LyXText::SearchBackward(char const* string) in text2.C )
323 bool LyXFindReplace1::SearchBackward(LyXText *lt)
325 LyXParagraph *par = lt->cursor.par;
326 int pos = lt->cursor.pos;
332 // We skip empty paragraphs (Asger)
334 par = par->Previous();
337 } while (par && pos<0);
339 } while (par && !IsSearchStringInText(par,pos));
342 lt->SetCursor(par,pos);
349 /* Compares 2 char values.
351 > 0 if chSearch > ch2
352 = 0 if chSearch == ch2
353 < 0 if chSearch < ch2
355 int LyXFindReplace1::CompareChars(char chSearch, char chText)
358 return (chSearch - chText);
359 return (toupper((unsigned char) chSearch) - toupper((unsigned char) chText));
363 // returns true if the search string is at the specified position
364 // (Copied from the original "LyXText::IsStringInText" in text2.C )
365 bool LyXFindReplace1::IsSearchStringInText(LyXParagraph *par, int pos)
371 string::size_type iSrch;
376 fPrevIsSpace = false;
377 iText = 0; iSrch = 0;
378 while (pos+iText < par->Last() &&
379 iSrch < SearchString().length()) {
380 chSrch = SearchString()[iSrch];
381 chText = par->GetChar(pos+iText);
382 // Why was this code there ??? Insets are *not* spaces!
383 // It seems that there is some code here to handle multiple spaces. I
384 // wonder why this is useful... (JMarc)
385 // if ((chText == ' ') ||
386 // ((unsigned char)chText <= LYX_META_INSET))
389 iText++; // next Text pos
390 continue; // same search pos
395 fPrevIsSpace = false;
396 if (CompareChars(chSrch, chText) != 0)
403 if (iSrch < SearchString().length())
407 || ((pos <= 0 || !IsLetterCharOrDigit(par->GetChar(pos-1)))
408 && (pos+iText >= par->Last()
409 || !IsLetterCharOrDigit(par->GetChar(pos + iText))))) {
410 iLenSelected = iText;