1999-09-27 18:44:28 +00:00
|
|
|
/* This file is part of
|
1999-11-15 12:01:38 +00:00
|
|
|
* ======================================================
|
1999-10-07 18:44:17 +00:00
|
|
|
*
|
|
|
|
* LyX, The Document Processor
|
|
|
|
*
|
|
|
|
* Copyright 1995 Matthias Ettrich,
|
2000-03-16 04:29:22 +00:00
|
|
|
* Copyright 1995-2000 The LyX Team.
|
1999-10-07 18:44:17 +00:00
|
|
|
*
|
1999-11-15 12:01:38 +00:00
|
|
|
* ====================================================== */
|
1999-09-27 18:44:28 +00:00
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
#ifdef __GNUG__
|
|
|
|
#pragma implementation
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "lyxfr1.h"
|
2000-03-01 04:56:55 +00:00
|
|
|
|
1999-09-27 18:44:28 +00:00
|
|
|
#include "lyxtext.h"
|
|
|
|
#include "LyXView.h"
|
|
|
|
#include "minibuffer.h"
|
2000-04-08 17:02:02 +00:00
|
|
|
#include "lyx_gui_misc.h"
|
1999-11-15 12:01:38 +00:00
|
|
|
#include "support/textutils.h"
|
2000-04-08 17:02:02 +00:00
|
|
|
#include "support/lstrings.h"
|
|
|
|
#include "BufferView.h"
|
|
|
|
#include "buffer.h"
|
1999-09-27 18:44:28 +00:00
|
|
|
|
|
|
|
// Returns the current selection. If nothing is selected or if the selection
|
|
|
|
// spans 2 paragraphs, an empty string is returned.
|
2000-03-01 04:56:55 +00:00
|
|
|
static
|
|
|
|
string GetCurrentSelectionAsString(LyXText * lt)
|
1999-09-27 18:44:28 +00:00
|
|
|
{
|
2000-03-01 04:56:55 +00:00
|
|
|
string sz;
|
1999-12-16 06:43:25 +00:00
|
|
|
|
2000-06-08 23:16:16 +00:00
|
|
|
LyXParagraph * par = lt->cursor.par();
|
|
|
|
if (lt->selection && lt->sel_cursor.par() == par) {
|
1999-09-27 18:44:28 +00:00
|
|
|
// (selected) and (begin/end in same paragraph)
|
1999-11-15 12:01:38 +00:00
|
|
|
LyXParagraph::size_type pos =
|
2000-06-08 23:16:16 +00:00
|
|
|
lt->sel_start_cursor.pos();
|
1999-11-15 12:01:38 +00:00
|
|
|
LyXParagraph::size_type endpos =
|
2000-06-08 23:16:16 +00:00
|
|
|
lt->sel_end_cursor.pos();
|
1999-11-04 01:40:20 +00:00
|
|
|
bool fPrevIsSpace = false;
|
|
|
|
char ch;
|
2000-03-01 04:56:55 +00:00
|
|
|
while (pos < par->Last() && pos < endpos) {
|
1999-09-27 18:44:28 +00:00
|
|
|
ch = par->GetChar(pos);
|
2000-03-01 04:56:55 +00:00
|
|
|
|
1999-09-27 18:44:28 +00:00
|
|
|
//HB??: Maybe (ch <= ' ')
|
1999-11-15 12:01:38 +00:00
|
|
|
if ((ch == ' ') || (ch <= LyXParagraph::META_INSET)) {
|
1999-09-27 18:44:28 +00:00
|
|
|
// consecutive spaces --> 1 space char
|
|
|
|
if (fPrevIsSpace) {
|
1999-12-16 06:43:25 +00:00
|
|
|
++pos; // Next text pos
|
1999-09-27 18:44:28 +00:00
|
|
|
continue; // same search pos
|
|
|
|
}
|
2000-03-01 04:56:55 +00:00
|
|
|
sz += ' ';
|
1999-09-27 18:44:28 +00:00
|
|
|
fPrevIsSpace = true;
|
|
|
|
} else {
|
2000-03-01 04:56:55 +00:00
|
|
|
sz += ch;
|
1999-09-27 18:44:28 +00:00
|
|
|
fPrevIsSpace = false;
|
|
|
|
}
|
1999-12-16 06:43:25 +00:00
|
|
|
++pos;
|
2000-03-01 04:56:55 +00:00
|
|
|
}
|
1999-09-27 18:44:28 +00:00
|
|
|
}
|
2000-03-01 04:56:55 +00:00
|
|
|
return sz;
|
1999-09-27 18:44:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// If nothing selected, select the word at the cursor.
|
|
|
|
// Returns the current selection
|
2000-03-01 04:56:55 +00:00
|
|
|
static inline
|
2000-06-12 11:27:15 +00:00
|
|
|
string GetSelectionOrWordAtCursor(BufferView * bv)
|
1999-09-27 18:44:28 +00:00
|
|
|
{
|
2000-06-12 11:27:15 +00:00
|
|
|
bv->text->SelectWordWhenUnderCursor(bv);
|
|
|
|
return GetCurrentSelectionAsString(bv->text);
|
1999-09-27 18:44:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// This is a copy of SetSelectionOverString from text.C
|
|
|
|
// It does the same, but uses only the length as a parameter
|
2000-03-01 04:56:55 +00:00
|
|
|
static inline
|
2000-06-12 11:27:15 +00:00
|
|
|
void SetSelectionOverLenChars(BufferView * bv, int len)
|
1999-09-27 18:44:28 +00:00
|
|
|
{
|
2000-06-12 11:27:15 +00:00
|
|
|
bv->text->sel_cursor = bv->text->cursor;
|
1999-12-16 06:43:25 +00:00
|
|
|
for (int i = 0; i < len; ++i)
|
2000-06-12 11:27:15 +00:00
|
|
|
bv->text->CursorRight(bv);
|
2000-06-16 15:13:25 +00:00
|
|
|
bv->text->SetSelection();
|
1999-09-27 18:44:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//------------------------------
|
|
|
|
|
2000-03-01 04:56:55 +00:00
|
|
|
|
|
|
|
LyXFindReplace::LyXFindReplace()
|
|
|
|
: bv(0)
|
|
|
|
{}
|
|
|
|
|
|
|
|
|
|
|
|
LyXFindReplace::~LyXFindReplace()
|
|
|
|
{}
|
|
|
|
|
|
|
|
|
|
|
|
void LyXFindReplace::StartSearch(BufferView * b)
|
1999-09-27 18:44:28 +00:00
|
|
|
{
|
2000-03-01 04:56:55 +00:00
|
|
|
bv = b;
|
|
|
|
SF.StartSearch(this);
|
|
|
|
SF.replaceEnabled(!bv->buffer()->isReadonly());
|
1999-09-27 18:44:28 +00:00
|
|
|
searchForward = true;
|
2000-03-01 04:56:55 +00:00
|
|
|
if (SF.SearchString().empty())
|
2000-06-12 11:27:15 +00:00
|
|
|
SF.SetSearchString(GetSelectionOrWordAtCursor(bv));
|
1999-09-27 18:44:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
1999-12-16 06:43:25 +00:00
|
|
|
// TODO?: the user can insert multiple spaces with this
|
|
|
|
// routine (1999-01-11, dnaber)
|
2000-03-01 04:56:55 +00:00
|
|
|
void LyXFindReplace::SearchReplaceCB()
|
1999-09-27 18:44:28 +00:00
|
|
|
{
|
2000-03-01 04:56:55 +00:00
|
|
|
if (!bv->available()) return;
|
|
|
|
if (bv->buffer()->isReadonly()) return;
|
1999-09-27 18:44:28 +00:00
|
|
|
|
|
|
|
// CutSelection cannot cut a single space, so we have to stop
|
|
|
|
// in order to avoid endless loop :-(
|
2000-03-01 04:56:55 +00:00
|
|
|
if (SF.SearchString().length() == 0
|
|
|
|
|| (SF.SearchString().length() == 1
|
|
|
|
&& SF.SearchString()[0] == ' ') ) {
|
1999-09-27 18:44:28 +00:00
|
|
|
WriteAlert(_("Sorry!"), _("You cannot replace a single space, "
|
|
|
|
"nor an empty character."));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2000-03-01 04:56:55 +00:00
|
|
|
string const replacestring = SF.ReplaceString();
|
1999-09-27 18:44:28 +00:00
|
|
|
|
2000-03-01 04:56:55 +00:00
|
|
|
bv->hideCursor();
|
2000-06-12 11:27:15 +00:00
|
|
|
bv->update(BufferView::SELECT|BufferView::FITCUR);
|
1999-12-10 00:07:59 +00:00
|
|
|
|
2000-03-01 04:56:55 +00:00
|
|
|
LyXText * ltCur = bv->text;
|
1999-12-10 00:07:59 +00:00
|
|
|
if (ltCur->selection) {
|
|
|
|
// clear the selection (if there is any)
|
2000-03-01 04:56:55 +00:00
|
|
|
bv->toggleSelection(false);
|
|
|
|
bv->text->
|
2000-06-12 11:27:15 +00:00
|
|
|
ReplaceSelectionWithString(bv, replacestring.c_str());
|
2000-03-01 04:56:55 +00:00
|
|
|
bv->text->
|
2000-06-12 11:27:15 +00:00
|
|
|
SetSelectionOverString(bv, replacestring.c_str());
|
|
|
|
bv->update(BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
|
1999-12-10 00:07:59 +00:00
|
|
|
}
|
1999-09-27 18:44:28 +00:00
|
|
|
|
|
|
|
// jump to next match:
|
2000-03-01 04:56:55 +00:00
|
|
|
SearchCB(searchForward);
|
1999-09-27 18:44:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// replaces all occurences of a string (1999-01-15, dnaber@mini.gt.owl.de)
|
2000-03-01 04:56:55 +00:00
|
|
|
void LyXFindReplace::SearchReplaceAllCB()
|
1999-09-27 18:44:28 +00:00
|
|
|
{
|
2000-03-01 04:56:55 +00:00
|
|
|
if (!bv->available()) return;
|
|
|
|
if (bv->buffer()->isReadonly()) return;
|
1999-09-27 18:44:28 +00:00
|
|
|
|
|
|
|
// CutSelection cannot cut a single space, so we have to stop
|
|
|
|
// in order to avoid endless loop :-(
|
2000-03-01 04:56:55 +00:00
|
|
|
if (SF.SearchString().length() == 0
|
|
|
|
|| (SF.SearchString().length() == 1
|
|
|
|
&& SF.SearchString()[0] == ' ') ) {
|
1999-09-27 18:44:28 +00:00
|
|
|
WriteAlert(_("Sorry!"), _("You cannot replace a single space, "
|
|
|
|
"nor an empty character."));
|
1999-12-16 06:43:25 +00:00
|
|
|
return;
|
|
|
|
}
|
1999-09-27 18:44:28 +00:00
|
|
|
|
2000-03-01 04:56:55 +00:00
|
|
|
string const replacestring = SF.ReplaceString();
|
1999-09-27 18:44:28 +00:00
|
|
|
|
2000-03-01 04:56:55 +00:00
|
|
|
bv->hideCursor();
|
1999-09-27 18:44:28 +00:00
|
|
|
|
1999-12-10 00:07:59 +00:00
|
|
|
// start at top
|
2000-03-01 04:56:55 +00:00
|
|
|
bv->text->ClearSelection();
|
2000-06-12 11:27:15 +00:00
|
|
|
bv->text->CursorTop(bv);
|
1999-12-10 00:07:59 +00:00
|
|
|
|
|
|
|
int replace_count = 0;
|
1999-12-16 06:43:25 +00:00
|
|
|
LyXText * ltCur;
|
1999-12-10 00:07:59 +00:00
|
|
|
do {
|
2000-03-01 04:56:55 +00:00
|
|
|
ltCur = bv->text;
|
1999-12-10 00:07:59 +00:00
|
|
|
if (ltCur->selection) {
|
2000-06-12 11:27:15 +00:00
|
|
|
bv->update(BufferView::SELECT|BufferView::FITCUR);
|
2000-03-01 04:56:55 +00:00
|
|
|
bv->toggleSelection(false);
|
|
|
|
bv->text->
|
2000-06-12 11:27:15 +00:00
|
|
|
ReplaceSelectionWithString(bv, replacestring.c_str());
|
2000-03-01 04:56:55 +00:00
|
|
|
bv->text->
|
2000-06-12 11:27:15 +00:00
|
|
|
SetSelectionOverString(bv, replacestring.c_str());
|
|
|
|
bv->update(BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
|
1999-12-10 00:07:59 +00:00
|
|
|
++replace_count;
|
|
|
|
}
|
2000-03-01 04:56:55 +00:00
|
|
|
} while (SearchCB(true));
|
1999-09-27 18:44:28 +00:00
|
|
|
if( replace_count == 0 ) {
|
|
|
|
LyXBell();
|
2000-03-01 04:56:55 +00:00
|
|
|
bv->owner()->getMiniBuffer()->Set(
|
1999-12-19 22:35:36 +00:00
|
|
|
_("String not found!"));
|
1999-09-27 18:44:28 +00:00
|
|
|
} else {
|
1999-12-19 22:35:36 +00:00
|
|
|
if (replace_count == 1) {
|
2000-03-01 04:56:55 +00:00
|
|
|
bv->owner()->getMiniBuffer()->Set(
|
1999-12-19 22:35:36 +00:00
|
|
|
_("1 string has been replaced."));
|
1999-09-27 18:44:28 +00:00
|
|
|
} else {
|
1999-10-19 16:48:35 +00:00
|
|
|
string str = tostr(replace_count);
|
1999-09-27 18:44:28 +00:00
|
|
|
str += _(" strings have been replaced.");
|
2000-03-01 04:56:55 +00:00
|
|
|
bv->owner()->getMiniBuffer()->Set(str);
|
1999-09-27 18:44:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2000-03-01 04:56:55 +00:00
|
|
|
bool LyXFindReplace::SearchCB(bool fForward)
|
1999-09-27 18:44:28 +00:00
|
|
|
{
|
|
|
|
// store search direction
|
|
|
|
searchForward = fForward;
|
|
|
|
|
2000-03-01 04:56:55 +00:00
|
|
|
if (!bv->available())
|
1999-12-16 06:43:25 +00:00
|
|
|
return false;
|
1999-09-27 18:44:28 +00:00
|
|
|
|
2000-03-01 04:56:55 +00:00
|
|
|
bv->hideCursor();
|
2000-06-12 11:27:15 +00:00
|
|
|
bv->update(BufferView::SELECT|BufferView::FITCUR);
|
2000-03-01 04:56:55 +00:00
|
|
|
LyXText * ltCur = bv->text;
|
1999-09-27 18:44:28 +00:00
|
|
|
if (ltCur->selection)
|
|
|
|
ltCur->cursor = fForward ? ltCur->sel_end_cursor :
|
1999-12-16 06:43:25 +00:00
|
|
|
ltCur->sel_start_cursor;
|
1999-09-27 18:44:28 +00:00
|
|
|
|
2000-03-01 04:56:55 +00:00
|
|
|
iLenSelected = SF.SearchString().length();
|
1999-12-16 06:43:25 +00:00
|
|
|
bool result;
|
1999-09-27 18:44:28 +00:00
|
|
|
|
2000-03-01 04:56:55 +00:00
|
|
|
if (!SF.ValidSearchData() ||
|
2000-06-12 11:27:15 +00:00
|
|
|
(fForward ? SearchForward(bv):SearchBackward(bv))) {
|
|
|
|
bv->update(BufferView::SELECT|BufferView::FITCUR);
|
1999-12-10 00:07:59 +00:00
|
|
|
|
|
|
|
// clear the selection (if there is any)
|
2000-03-01 04:56:55 +00:00
|
|
|
bv->toggleSelection();
|
|
|
|
bv->text->ClearSelection();
|
1999-12-10 00:07:59 +00:00
|
|
|
|
|
|
|
// set the new selection
|
2000-06-12 11:27:15 +00:00
|
|
|
SetSelectionOverLenChars(bv, iLenSelected);
|
2000-03-01 04:56:55 +00:00
|
|
|
bv->toggleSelection(false);
|
|
|
|
bv->owner()->getMiniBuffer()->Set(_("Found."));
|
1999-09-27 18:44:28 +00:00
|
|
|
result = true;
|
|
|
|
} else {
|
|
|
|
LyXBell();
|
2000-03-01 04:56:55 +00:00
|
|
|
bv->owner()->getMiniBuffer()->Set(_("String not found!"));
|
1999-09-27 18:44:28 +00:00
|
|
|
result = false;
|
|
|
|
}
|
2000-02-10 17:53:36 +00:00
|
|
|
|
2000-03-01 04:56:55 +00:00
|
|
|
if (bv->focus())
|
|
|
|
bv->showCursor();
|
2000-02-15 14:28:15 +00:00
|
|
|
|
1999-12-16 06:43:25 +00:00
|
|
|
return result;
|
1999-09-27 18:44:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// if the string can be found: return true and set the cursor to
|
|
|
|
// the new position
|
|
|
|
// (was: LyXText::SearchForward(char const* string) in text2.C )
|
2000-06-12 11:27:15 +00:00
|
|
|
bool LyXFindReplace::SearchForward(BufferView * bv)
|
1999-09-27 18:44:28 +00:00
|
|
|
{
|
2000-06-12 11:27:15 +00:00
|
|
|
LyXParagraph * par = bv->text->cursor.par();
|
|
|
|
LyXParagraph::size_type pos = bv->text->cursor.pos();
|
1999-09-27 18:44:28 +00:00
|
|
|
|
1999-11-15 12:01:38 +00:00
|
|
|
while (par && !IsSearchStringInText(par, pos)) {
|
1999-12-16 06:43:25 +00:00
|
|
|
if (pos < par->Last() - 1)
|
|
|
|
++pos;
|
1999-09-27 18:44:28 +00:00
|
|
|
else {
|
|
|
|
pos = 0;
|
|
|
|
par = par->Next();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (par) {
|
2000-06-12 11:27:15 +00:00
|
|
|
bv->text->SetCursor(bv, par, pos);
|
1999-09-27 18:44:28 +00:00
|
|
|
return true;
|
|
|
|
} else
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// if the string can be found: return true and set the cursor to
|
|
|
|
// the new position
|
|
|
|
// (was: LyXText::SearchBackward(char const* string) in text2.C )
|
2000-06-12 11:27:15 +00:00
|
|
|
bool LyXFindReplace::SearchBackward(BufferView * bv)
|
1999-09-27 18:44:28 +00:00
|
|
|
{
|
2000-06-12 11:27:15 +00:00
|
|
|
LyXParagraph * par = bv->text->cursor.par();
|
|
|
|
int pos = bv->text->cursor.pos();
|
1999-09-27 18:44:28 +00:00
|
|
|
|
|
|
|
do {
|
1999-12-16 06:43:25 +00:00
|
|
|
if (pos > 0)
|
|
|
|
--pos;
|
1999-09-27 18:44:28 +00:00
|
|
|
else {
|
|
|
|
// We skip empty paragraphs (Asger)
|
|
|
|
do {
|
|
|
|
par = par->Previous();
|
|
|
|
if (par)
|
1999-12-16 06:43:25 +00:00
|
|
|
pos = par->Last() - 1;
|
|
|
|
} while (par && pos < 0);
|
1999-09-27 18:44:28 +00:00
|
|
|
}
|
1999-11-15 12:01:38 +00:00
|
|
|
} while (par && !IsSearchStringInText(par, pos));
|
1999-09-27 18:44:28 +00:00
|
|
|
|
|
|
|
if (par) {
|
2000-06-12 11:27:15 +00:00
|
|
|
bv->text->SetCursor(bv, par, pos);
|
1999-09-27 18:44:28 +00:00
|
|
|
return true;
|
|
|
|
} else
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Compares 2 char values.
|
|
|
|
return value is
|
|
|
|
> 0 if chSearch > ch2
|
|
|
|
= 0 if chSearch == ch2
|
|
|
|
< 0 if chSearch < ch2
|
|
|
|
*/
|
2000-03-01 04:56:55 +00:00
|
|
|
int LyXFindReplace::CompareChars(char chSearch, char chText) const
|
1999-09-27 18:44:28 +00:00
|
|
|
{
|
2000-03-01 04:56:55 +00:00
|
|
|
if (SF.CaseSensitive())
|
1999-09-27 18:44:28 +00:00
|
|
|
return (chSearch - chText);
|
1999-11-04 01:40:20 +00:00
|
|
|
return (toupper(chSearch) - toupper(chText));
|
1999-09-27 18:44:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// returns true if the search string is at the specified position
|
|
|
|
// (Copied from the original "LyXText::IsStringInText" in text2.C )
|
2000-03-01 04:56:55 +00:00
|
|
|
bool LyXFindReplace::IsSearchStringInText(LyXParagraph * par,
|
|
|
|
LyXParagraph::size_type pos) const
|
1999-09-27 18:44:28 +00:00
|
|
|
{
|
1999-12-16 06:43:25 +00:00
|
|
|
if (!par) return false;
|
1999-09-27 18:44:28 +00:00
|
|
|
|
1999-12-16 06:43:25 +00:00
|
|
|
char chSrch = 0;
|
|
|
|
char chText;
|
1999-11-04 01:40:20 +00:00
|
|
|
bool fPrevIsSpace = false;
|
|
|
|
int iText = 0;
|
|
|
|
string::size_type iSrch = 0;
|
|
|
|
while (pos + iText < par->Last() &&
|
2000-03-01 04:56:55 +00:00
|
|
|
iSrch < SF.SearchString().length()) {
|
|
|
|
chSrch = SF.SearchString()[iSrch];
|
1999-09-27 18:44:28 +00:00
|
|
|
chText = par->GetChar(pos+iText);
|
|
|
|
if (chText == ' ') {
|
|
|
|
if (fPrevIsSpace) {
|
1999-12-16 06:43:25 +00:00
|
|
|
++iText; // next Text pos
|
1999-09-27 18:44:28 +00:00
|
|
|
continue; // same search pos
|
|
|
|
}
|
|
|
|
fPrevIsSpace = true;
|
|
|
|
} else
|
|
|
|
fPrevIsSpace = false;
|
|
|
|
if (CompareChars(chSrch, chText) != 0)
|
|
|
|
break;
|
|
|
|
|
1999-11-04 01:40:20 +00:00
|
|
|
++iSrch;
|
|
|
|
++iText;
|
1999-09-27 18:44:28 +00:00
|
|
|
}
|
|
|
|
|
2000-03-01 04:56:55 +00:00
|
|
|
if (iSrch < SF.SearchString().length())
|
1999-09-27 18:44:28 +00:00
|
|
|
return false;
|
|
|
|
|
2000-03-01 04:56:55 +00:00
|
|
|
if (!SF.MatchWord()
|
1999-12-16 06:43:25 +00:00
|
|
|
|| ((pos <= 0 || !IsLetterCharOrDigit(par->GetChar(pos - 1)))
|
1999-11-04 01:40:20 +00:00
|
|
|
&& (pos + iText >= par->Last()
|
1999-09-27 18:44:28 +00:00
|
|
|
|| !IsLetterCharOrDigit(par->GetChar(pos + iText))))) {
|
|
|
|
iLenSelected = iText;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|