mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-15 06:05:58 -06:00
This might bring some incompatibilities in the code that expects wchar to be 16 bit, which escpecially involves dealing with Win32 (and to lesser extend MacOS) APIs, so if your application is doing that, please check all instances of WCHAR (UniChar on MacOS) or even wchar especially type casts. To support host APIs, char16 is introduced (but there is no 16-bit String varian). Use ToSystemCharsetW, FromSystemCharsetW to convert texts to Win32 API. - Support of drawing non-BMP characters in GUI - Vastly improved character font replacement code (when drawing characters missing with requested font, replacement font is used) - Last instances of Win32 ANSI calls (those ending with A) are removed - UTF handling routines are refactored and their's naming is unified - RTF is now being able to handle non-BMP characters (RTF is used as clipboard format for RichText) Other minor changes: - fixed TryRealloc issue - improved MemoryCheck - Removed MemoryAlloc48/MemoryFree48 - In theide Background parsing should less often cause delays in the main thread
961 lines
22 KiB
C++
961 lines
22 KiB
C++
#include "CodeEditor.h"
|
|
|
|
namespace Upp {
|
|
|
|
void CodeEditor::InitFindReplace()
|
|
{
|
|
findreplace.find.AddButton().SetMonoImage(CtrlImg::smallright()).Tip("Wildcard")
|
|
<<= THISBACK(FindWildcard);
|
|
findreplace.replace.AddButton().SetMonoImage(CtrlImg::smallright()).Tip("Wildcard")
|
|
<<= THISBACK(ReplaceWildcard);
|
|
PutI(findreplace.find);
|
|
PutI(findreplace.replace);
|
|
findreplace.amend <<= THISBACK(Replace);
|
|
findreplace.amend_all <<= THISBACK1(ReplaceAll, false);
|
|
findreplace.amend_rest <<= THISBACK1(ReplaceAll, true);
|
|
findreplace.prev <<= THISBACK(DoFindBack);
|
|
findreplace.replacing = false;
|
|
found = notfoundfw = notfoundbk = foundsel = false;
|
|
persistent_find_replace = false;
|
|
findreplace.find <<= findreplace.wholeword <<= findreplace.wildcards
|
|
<<= findreplace.incremental <<= findreplace.regexp
|
|
<<= findreplace.ignorecase <<= THISBACK(IncrementalFind);
|
|
ff_start_pos = -1;
|
|
findreplace.find_all << [=] { FindAll(); };
|
|
}
|
|
|
|
FindReplaceDlg::FindReplaceDlg()
|
|
{
|
|
find.NoUpDownKeys();
|
|
samecase <<= true;
|
|
close.Cancel();
|
|
prev.SetImage(CtrlImg::SmallUp());
|
|
amend.SetImage(CodeEditorImg::Replace());
|
|
amend_all.SetImage(CodeEditorImg::ReplaceAll());
|
|
amend_rest.SetImage(CodeEditorImg::ReplaceRest());
|
|
find_all.SetImage(CodeEditorImg::FindAll());
|
|
incremental <<= true;
|
|
mode <<= THISBACK(Sync);
|
|
mode.Hide();
|
|
Sync();
|
|
|
|
find.NullText("Find");
|
|
replace.NullText("Replace");
|
|
}
|
|
|
|
void FindReplaceDlg::Sync()
|
|
{
|
|
samecase.Enable(ignorecase);
|
|
bool b = !regexp;
|
|
wildcards.Enable(b);
|
|
prev.Enable(b);
|
|
ignorecase.Enable(b);
|
|
wholeword.Enable(b);
|
|
incremental_from_cursor.Enable(IsIncremental());
|
|
b = !mode.IsVisible() || ~mode == 0;
|
|
replace.Enable(b);
|
|
}
|
|
|
|
dword CodeEditor::find_next_key = K_F3;
|
|
dword CodeEditor::find_prev_key = K_SHIFT_F3;
|
|
dword CodeEditor::replace_key = K_CTRL_R;
|
|
|
|
bool FindReplaceDlg::Key(dword key, int cnt) {
|
|
if(key == K_CTRL_I) {
|
|
if(find.HasFocus()) {
|
|
find <<= itext;
|
|
return true;
|
|
}
|
|
if(replace.HasFocus()) {
|
|
replace <<= itext;
|
|
return true;
|
|
}
|
|
}
|
|
if(key == K_ENTER) {
|
|
next.WhenAction();
|
|
return true;
|
|
}
|
|
if(findarg(key, K_TAB, K_SHIFT_TAB) >= 0 && replace.IsShown()) {
|
|
if(find.HasFocus())
|
|
replace.SetFocus();
|
|
else
|
|
find.SetFocus();
|
|
return true;
|
|
}
|
|
if(key == K_ESCAPE) {
|
|
close.WhenAction();
|
|
return true;
|
|
}
|
|
return TopWindow::Key(key, cnt);
|
|
}
|
|
|
|
void FindReplaceDlg::Setup(bool doreplace)
|
|
{
|
|
CtrlLayout(*this);
|
|
close.SetImage(CodeEditorImg::Cancel());
|
|
close.Tip("Close (Esc)");
|
|
close.Normal();
|
|
close.SetLabel("");
|
|
next.SetImage(CtrlImg::SmallDown());
|
|
next.Tip("Find next (" + GetKeyDesc(CodeEditor::find_next_key) + ")");
|
|
next.Normal();
|
|
next.SetLabel("");
|
|
prev.Tip("Find prev (" + GetKeyDesc(CodeEditor::find_prev_key) + ")");
|
|
amend.Tip("Replace (" + GetKeyDesc(CodeEditor::replace_key) + ")");
|
|
find_all.Show();
|
|
find_all.Tip("Find all");
|
|
amend.Disable();
|
|
replacing = doreplace;
|
|
replace.Show(replacing);
|
|
amend.Show(replacing);
|
|
amend_all.Show(replacing);
|
|
amend_rest.Show(replacing);
|
|
Height(doreplace ? GetLayoutSize().cy : replace.GetRect().top);
|
|
SetFrame(TopSeparatorFrame());
|
|
Sync();
|
|
}
|
|
|
|
void CodeEditor::SetFound(int fi, int type, const WString& text)
|
|
{
|
|
Found& f = foundwild.At(fi);
|
|
f.type = type;
|
|
f.text = text;
|
|
}
|
|
|
|
int CodeEditor::Match(const wchar *f, const wchar *s, int line, bool we, bool ignorecase, int fi)
|
|
{
|
|
const wchar *b = s;
|
|
int n = 0;
|
|
WString ln;
|
|
while(*f) {
|
|
if(*f == WILDANY) {
|
|
f++;
|
|
WString wild;
|
|
for(;;) {
|
|
int nn = Match(f, s, line, we, ignorecase, fi + 1);
|
|
if(nn >= 0) {
|
|
SetFound(fi, WILDANY, wild);
|
|
return int(s - b) + n + nn;
|
|
}
|
|
wild.Cat(*s ? *s : '\n');
|
|
if(!*s++) return -1;
|
|
}
|
|
return -1;
|
|
}
|
|
else
|
|
if(*f == WILDONE) {
|
|
if(!*s) return -1;
|
|
SetFound(fi++, WILDONE, WString(*s, 1));
|
|
s++;
|
|
}
|
|
else
|
|
if(*f == WILDSPACE) {
|
|
const wchar *wb = s;
|
|
if(*s != ' ' && *s != '\t') return -1;
|
|
s++;
|
|
while(*s == ' ' || *s == '\t')
|
|
s++;
|
|
SetFound(fi++, WILDSPACE, WString(wb, s));
|
|
}
|
|
else
|
|
if(*f == WILDNUMBER) {
|
|
const wchar *wb = s;
|
|
if(*s < '0' || *s > '9') return -1;
|
|
s++;
|
|
while(*s >= '0' && *s <= '9')
|
|
s++;
|
|
SetFound(fi++, WILDNUMBER, WString(wb, s));
|
|
}
|
|
else
|
|
if(*f == WILDID) {
|
|
const wchar *wb = s;
|
|
if(!iscib(*s)) return -1;
|
|
s++;
|
|
while(iscidl(*s)) s++;
|
|
SetFound(fi++, WILDID, WString(wb, s));
|
|
}
|
|
else
|
|
if(*f == '\n') {
|
|
if(*s != '\0' || ++line >= GetLineCount()) return -1;
|
|
n += int(s - b) + 1;
|
|
ln = GetWLine(line);
|
|
s = b = ln;
|
|
}
|
|
else {
|
|
if(ignorecase ? ToUpper(*s) != ToUpper(*f) : *s != *f) return -1;
|
|
s++;
|
|
}
|
|
f++;
|
|
}
|
|
return we && iscidl(*s) ? -1 : int(s - b) + n;
|
|
}
|
|
|
|
bool CodeEditor::Find(bool back, bool block)
|
|
{
|
|
foundsel = true;
|
|
if(notfoundfw) MoveTextBegin();
|
|
if(notfoundbk) MoveTextEnd();
|
|
foundsel = false;
|
|
int64 cursor, pos;
|
|
if(found)
|
|
GetSelection(pos, cursor);
|
|
else
|
|
GetSelection(cursor, pos);
|
|
pos = cursor;
|
|
bool b = FindFrom(pos, back, block);
|
|
findreplace.amend.Enable(b);
|
|
return b;
|
|
}
|
|
|
|
bool CodeEditor::RegExpFind(int64 pos, bool block)
|
|
{
|
|
RegExp regex((String)~findreplace.find);
|
|
|
|
int line = GetLinePos64(pos);
|
|
String ln = ToUtf8(GetWLine(line).Mid(LimitSize(pos)));
|
|
for(;;) {
|
|
if(regex.Match(ln)) {
|
|
for(int i = 0; i < regex.GetCount(); i++)
|
|
SetFound(i, WILDANY, regex.GetString(i).ToWString());
|
|
int off = regex.GetOffset();
|
|
int len = Utf32Len(~ln + off, regex.GetLength());
|
|
pos = GetPos64(line, Utf32Len(~ln, off) + (int)pos);
|
|
foundtext = GetW(pos, len);
|
|
if(!block) {
|
|
foundsel = true;
|
|
SetSelection(pos, pos + len);
|
|
foundsel = false;
|
|
CenterCursor();
|
|
}
|
|
foundpos = pos;
|
|
foundsize = len;
|
|
found = true;
|
|
return true;
|
|
}
|
|
if(++line >= GetLineCount()) {
|
|
WaitView(line);
|
|
if(line >= GetLineCount())
|
|
return false;
|
|
}
|
|
ln = GetUtf8Line(line);
|
|
pos = 0;
|
|
if(!SearchProgress(line))
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void CodeEditor::FindAll()
|
|
{
|
|
Vector<Tuple<int64, int>> found;
|
|
foundpos = 0;
|
|
while(FindFrom(foundpos, false, true)) {
|
|
found.Add(MakeTuple(foundpos, foundsize));
|
|
foundpos += foundsize;
|
|
if(found.GetCount() >= 10000) {
|
|
Exclamation("Too many matches, only first 10000 will be shown.");
|
|
break;
|
|
}
|
|
}
|
|
WhenFindAll(found);
|
|
}
|
|
|
|
bool CodeEditor::FindFrom(int64 pos, bool back, bool block)
|
|
{
|
|
notfoundbk = notfoundfw = false;
|
|
if(findreplace.regexp) {
|
|
if(RegExpFind(pos, block))
|
|
return true;
|
|
if(back)
|
|
notfoundbk = true;
|
|
else
|
|
notfoundfw = true;
|
|
return false;
|
|
}
|
|
WString text = ~findreplace.find;
|
|
WString ft;
|
|
const wchar *s = text;
|
|
while(*s) {
|
|
int c = *s++;
|
|
if(c == '\\') {
|
|
c = *s++;
|
|
if(c == '\0') break;
|
|
if(c == 'n')
|
|
ft.Cat('\n');
|
|
else
|
|
if(c == 't')
|
|
ft.Cat('\t');
|
|
else
|
|
if(c >= ' ')
|
|
ft.Cat(c);
|
|
}
|
|
else
|
|
if(c >= ' ') {
|
|
if(findreplace.wildcards)
|
|
ft.Cat(c == '*' ? WILDANY :
|
|
c == '?' ? WILDONE :
|
|
c == '%' ? WILDSPACE :
|
|
c == '#' ? WILDNUMBER :
|
|
c == '$' ? WILDID :
|
|
c
|
|
);
|
|
else
|
|
ft.Cat(c);
|
|
}
|
|
}
|
|
bool wb = findreplace.wholeword ? iscidl(*ft) : false;
|
|
bool we = findreplace.wholeword ? iscidl(*ft.Last()) : false;
|
|
if(ft.IsEmpty()) return false;
|
|
foundwild.Clear();
|
|
int line = GetLinePos64(pos);
|
|
WString ln = GetWLine(line);
|
|
const wchar *l = ln;
|
|
s = l + pos;
|
|
bool ignorecase = findreplace.ignorecase;
|
|
for(;;) {
|
|
for(;;) {
|
|
if(!wb || (s == l || !iscidl(s[-1]))) {
|
|
int n = Match(ft, s, line, we, ignorecase);
|
|
if(n >= 0) {
|
|
int64 pos = GetPos64(line, int(s - l));
|
|
foundtext = GetW(pos, n);
|
|
if(!back || pos + n < cursor) {
|
|
if(!block) {
|
|
foundsel = true;
|
|
SetSelection(pos, pos + n);
|
|
foundsel = false;
|
|
CenterCursor();
|
|
}
|
|
foundpos = pos;
|
|
foundsize = n;
|
|
found = true;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
if(back) {
|
|
if(s-- == l) break;
|
|
}
|
|
else
|
|
if(!*s++) break;
|
|
}
|
|
if(back) {
|
|
if(--line < 0) break;
|
|
ln = GetWLine(line);
|
|
l = ln;
|
|
s = ln.End();
|
|
}
|
|
else {
|
|
if(++line >= GetLineCount()) {
|
|
WaitView(line);
|
|
if(line >= GetLineCount())
|
|
break;
|
|
}
|
|
ln = GetWLine(line);
|
|
l = s = ln;
|
|
}
|
|
if(!SearchProgress(back ? GetLineCount() - line : line))
|
|
break;
|
|
}
|
|
if(back)
|
|
notfoundbk = true;
|
|
else
|
|
notfoundfw = true;
|
|
return false;
|
|
}
|
|
|
|
void CodeEditor::FindReplaceAddHistory()
|
|
{
|
|
if(!IsNull(findreplace.find))
|
|
findreplace.find.AddHistory();
|
|
if(!IsNull(findreplace.replace))
|
|
findreplace.replace.AddHistory();
|
|
}
|
|
|
|
void CodeEditor::NoFindError()
|
|
{
|
|
findreplace.find.Error(false);
|
|
}
|
|
|
|
void CodeEditor::NotFound()
|
|
{
|
|
findreplace.find.Error();
|
|
if(!findreplace.IsIncremental())
|
|
SetFocus();
|
|
findreplace.amend.Disable();
|
|
}
|
|
|
|
bool CodeEditor::Find(bool back, bool blockreplace, bool replace)
|
|
{
|
|
NoFindError();
|
|
if(Find(back, blockreplace)) {
|
|
if(!blockreplace) {
|
|
if(!findreplace.IsOpen())
|
|
OpenNormalFindReplace(replace);
|
|
if(!findreplace.IsIncremental())
|
|
SetFocus();
|
|
}
|
|
return true;
|
|
}
|
|
else {
|
|
NotFound();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
WString CodeEditor::GetWild(int type, int& i)
|
|
{
|
|
for(;;) {
|
|
if(i >= foundwild.GetCount()) break;
|
|
Found& f = foundwild[i++];
|
|
if(f.type == type) return f.text;
|
|
}
|
|
for(int j = 0; j < foundwild.GetCount(); j++) {
|
|
Found& f = foundwild[j++];
|
|
if(f.type == type) return f.text;
|
|
}
|
|
return WString();
|
|
}
|
|
|
|
WString CodeEditor::GetReplaceText()
|
|
{
|
|
WString rs = ~findreplace.replace;
|
|
bool wildcards = findreplace.wildcards;
|
|
bool samecase = findreplace.ignorecase && findreplace.samecase;
|
|
|
|
int anyi = 0, onei = 0, spacei = 0, numberi = 0, idi = 0;
|
|
WString rt;
|
|
const wchar *s = rs;
|
|
while(*s) {
|
|
int c = *s++;
|
|
if(c == '\\') {
|
|
c = *s++;
|
|
if(c == '\0') break;
|
|
if(c == 'n')
|
|
rt.Cat('\n');
|
|
else
|
|
if(c == 't')
|
|
rt.Cat('\t');
|
|
else
|
|
if(c >= ' ')
|
|
rt.Cat(c);
|
|
}
|
|
else
|
|
if(c >= ' ') {
|
|
if(wildcards) {
|
|
WString w;
|
|
if(c == '*')
|
|
w = GetWild(WILDANY, anyi);
|
|
else
|
|
if(c == '?')
|
|
w = GetWild(WILDONE, onei);
|
|
else
|
|
if(c == '%')
|
|
w = GetWild(WILDSPACE, spacei);
|
|
else
|
|
if(c == '#')
|
|
w = GetWild(WILDNUMBER, numberi);
|
|
else
|
|
if(c == '$')
|
|
w = GetWild(WILDID, idi);
|
|
else
|
|
if(c == '@') {
|
|
c = *s++;
|
|
if(c == '\0') break;
|
|
if(c == '@') {
|
|
rt << AsString(replacei).ToWString();
|
|
continue;
|
|
}
|
|
if(c == '#') {
|
|
rt << AsString(replacei + 1).ToWString();
|
|
continue;
|
|
}
|
|
if(c >= '1' && c <= '9') {
|
|
c -= '1';
|
|
w = c < foundwild.GetCount() ? foundwild[c].text : WString();
|
|
}
|
|
else {
|
|
rt.Cat('@');
|
|
if(c >= ' ' && c < 255) rt.Cat(c);
|
|
continue;
|
|
}
|
|
}
|
|
else {
|
|
rt.Cat(c);
|
|
continue;
|
|
}
|
|
if(*s == '+') {
|
|
w = ToUpper(w);
|
|
s++;
|
|
}
|
|
else
|
|
if(*s == '-') {
|
|
w = ToLower(w);
|
|
s++;
|
|
}
|
|
else
|
|
if(*s == '!') {
|
|
w = InitCaps(w);
|
|
s++;
|
|
}
|
|
rt.Cat(w);
|
|
}
|
|
else
|
|
rt.Cat(c);
|
|
}
|
|
}
|
|
if(samecase) {
|
|
if(foundtext.GetCount() && rt.GetCount()) {
|
|
if(IsUpper(foundtext[0]))
|
|
rt.Set(0, ToUpper(rt[0]));
|
|
if(IsLower(foundtext[0]))
|
|
rt.Set(0, ToLower(rt[0]));
|
|
}
|
|
if(foundtext.GetCount() > 1) {
|
|
if(IsUpper(foundtext[1]))
|
|
for(int i = 1; i < rt.GetCount(); i++)
|
|
rt.Set(i, ToUpper(rt[i]));
|
|
if(IsLower(foundtext[1]))
|
|
for(int i = 1; i < rt.GetCount(); i++)
|
|
rt.Set(i, ToLower(rt[i]));
|
|
}
|
|
}
|
|
replacei++;
|
|
return rt;
|
|
}
|
|
|
|
void CodeEditor::Replace()
|
|
{
|
|
if(!findreplace.replacing)
|
|
return;
|
|
NextUndo();
|
|
FindReplaceAddHistory();
|
|
if(!found) return;
|
|
bool h = persistent_find_replace;
|
|
persistent_find_replace = true; // avoid closing of findreplace by selection change
|
|
if(RemoveSelection()) {
|
|
Paste(GetReplaceText());
|
|
Find(false, false, true);
|
|
}
|
|
persistent_find_replace = h;
|
|
}
|
|
|
|
int CodeEditor::BlockReplace()
|
|
{
|
|
NextUndo();
|
|
Refresh(); // Setting full-refresh here avoids Pre/Post Remove/Insert costs
|
|
int l, h;
|
|
if(!GetSelection32(l, h)) return 0;
|
|
PlaceCaret(l);
|
|
int count = 0;
|
|
foundpos = l;
|
|
Index<int> ln;
|
|
StartSearchProgress(l, h);
|
|
while(foundpos < GetLength() && FindFrom(foundpos, false, true) && foundpos + foundsize <= h) {
|
|
CachePos(foundpos);
|
|
if(~findreplace.mode == 0) {
|
|
Remove((int)foundpos, foundsize);
|
|
WString rt = GetReplaceText();
|
|
Insert((int)foundpos, rt);
|
|
foundpos += rt.GetCount();
|
|
if(foundsize + rt.GetCount() == 0 && foundpos < GetLength())
|
|
foundpos++;
|
|
h = h - foundsize + rt.GetCount();
|
|
count++;
|
|
}
|
|
else {
|
|
ln.FindAdd(GetLine(foundpos));
|
|
foundpos += foundsize;
|
|
}
|
|
}
|
|
if(SearchCanceled())
|
|
return count;
|
|
EndSearchProgress();
|
|
Progress pi("Removing lines");
|
|
if(~findreplace.mode != 0) {
|
|
ClearSelection();
|
|
int ll = GetLine(l);
|
|
int lh = GetLine(h);
|
|
if(GetPos64(lh) == h)
|
|
lh--;
|
|
bool mm = ~findreplace.mode == 1;
|
|
String replace;
|
|
pi.SetTotal(lh - ll + 1);
|
|
for(int i = ll; i <= lh; i++) {
|
|
pi.Set(i, lh - ll + 1);
|
|
if(pi.Canceled())
|
|
return 0;
|
|
if((ln.Find(i) >= 0) == mm) {
|
|
replace << GetUtf8Line(i) << "\n";
|
|
count++;
|
|
}
|
|
}
|
|
int pos = GetPos32(ll);
|
|
Remove(pos, GetPos32(GetLine(h)) - pos);
|
|
SetSelection(pos, pos + Insert(pos, replace));
|
|
}
|
|
else
|
|
SetSelection(l, h);
|
|
return count;
|
|
}
|
|
|
|
void CodeEditor::OpenNormalFindReplace0(bool replace)
|
|
{
|
|
findreplace.incremental.Enable(GetLength64() < 2000000);
|
|
findreplace.Setup(replace);
|
|
findreplace.find_all.Show(!replace && WhenFindAll);
|
|
findreplace.itext = GetI();
|
|
findreplace.prev.Show();
|
|
findreplace.next <<= THISBACK(DoFind);
|
|
findreplace.close <<= THISBACK(EscapeFindReplace);
|
|
if(!findreplace.IsOpen())
|
|
InsertFrame(FindFrame(sb), findreplace);
|
|
WhenOpenFindReplace();
|
|
findreplace.find.SetFocus();
|
|
}
|
|
|
|
void CodeEditor::OpenNormalFindReplace(bool replace)
|
|
{
|
|
OpenNormalFindReplace0(replace);
|
|
// if(!findreplace.incremental_from_cursor)
|
|
// IncrementalFind();
|
|
}
|
|
|
|
void CodeEditor::FindReplace(bool pick_selection, bool pick_text, bool replace)
|
|
{
|
|
if(IsReadOnly())
|
|
replace = false;
|
|
|
|
if(findreplace.IsOpen())
|
|
CloseFindReplace();
|
|
|
|
ff_start_pos = GetCursor32();
|
|
|
|
replacei = 0;
|
|
WString find_text;
|
|
int find_pos = -1;
|
|
|
|
findreplace.Setup(replace);
|
|
|
|
if(pick_text || pick_selection)
|
|
{
|
|
if(IsSelection()) {
|
|
int l, h;
|
|
GetSelection32(l, h);
|
|
if(h - l < 100) {
|
|
find_text = GetSelectionW();
|
|
if(find_text.Find('\n') >= 0)
|
|
find_text.Clear();
|
|
}
|
|
}
|
|
else
|
|
if(pick_text) {
|
|
int l, h;
|
|
l = h = GetCursor32();
|
|
while(l > 0 && CharFilterCIdent(GetChar(l - 1)))
|
|
l--;
|
|
while(h < GetLength64() && CharFilterCIdent(GetChar(h)))
|
|
h++;
|
|
find_text = Get(l, h - l).ToWString();
|
|
find_pos = h;
|
|
}
|
|
if(find_text.GetCount())
|
|
findreplace.find <<= find_text;
|
|
}
|
|
if(IsSelection() && replace) {
|
|
findreplace.itext = GetI();
|
|
SetLayout_BlockReplaceLayout(findreplace);
|
|
findreplace.SetRect(WithBlockReplaceLayout<EmptyClass>::GetLayoutSize());
|
|
findreplace.Title(t_("Replace in selection"));
|
|
findreplace.find_all.Hide();
|
|
findreplace.amend.Hide();
|
|
findreplace.amend_all.Hide();
|
|
findreplace.amend_rest.Hide();
|
|
findreplace.prev.Hide();
|
|
findreplace.next.Ok() <<= findreplace.Breaker(IDOK);
|
|
findreplace.close.Cancel() <<= findreplace.Breaker(IDCANCEL);
|
|
findreplace.close.SetImage(Null);
|
|
findreplace.close.Tip("");
|
|
findreplace.next.SetImage(Null);
|
|
findreplace.next.Tip("");
|
|
findreplace.mode.Show();
|
|
findreplace.mode <<= 0;
|
|
findreplace.Sync();
|
|
if(findreplace.Execute() == IDOK)
|
|
BlockReplace();
|
|
findreplace.mode.Hide();
|
|
}
|
|
else {
|
|
if(find_pos >= 0)
|
|
SetCursor(find_pos);
|
|
OpenNormalFindReplace(replace);
|
|
findreplace.find.SetFocus();
|
|
}
|
|
}
|
|
|
|
void CodeEditor::ReplaceAll(bool rest)
|
|
{
|
|
int l, h;
|
|
GetSelection32(l, h);
|
|
int c = min(l, h);
|
|
findreplace.mode <<= 0;
|
|
SetSelection(rest * c, GetLength64());
|
|
BlockReplace();
|
|
SetCursor(c);
|
|
}
|
|
|
|
void CodeEditor::InsertWildcard(const char *s)
|
|
{
|
|
iwc = s;
|
|
}
|
|
|
|
void FindWildcardMenu(Callback1<const char *> cb, Point p, bool tablf, Ctrl *owner, bool regexp)
|
|
{
|
|
MenuBar menu;
|
|
if(regexp) {
|
|
menu.Add(t_("One or more spaces"), callback1(cb, " +"));
|
|
menu.Add(t_("One or more any characters"), callback1(cb, ".+"));
|
|
menu.Add(t_("Word"), callback1(cb, "\\w+"));
|
|
menu.Add(t_("Number"), callback1(cb, "\\d+"));
|
|
menu.Add(t_("Any character"), callback1(cb, "."));
|
|
if(tablf) {
|
|
menu.Separator();
|
|
menu.Add(t_("Tab"), callback1(cb, "\\t"));
|
|
}
|
|
}
|
|
else {
|
|
menu.Add(t_("One or more spaces"), callback1(cb, "%"));
|
|
menu.Add(t_("One or more any characters"), callback1(cb, "*"));
|
|
menu.Add(t_("C++ identifier"), callback1(cb, "$"));
|
|
menu.Add(t_("Number"), callback1(cb, "#"));
|
|
menu.Add(t_("Any character"), callback1(cb, "?"));
|
|
if(tablf) {
|
|
menu.Separator();
|
|
menu.Add(t_("Tab"), callback1(cb, "\\t"));
|
|
menu.Add(t_("Line feed"), callback1(cb, "\\n"));
|
|
}
|
|
}
|
|
menu.Execute(owner, p);
|
|
}
|
|
|
|
void CodeEditor::FindWildcard()
|
|
{
|
|
int l, h;
|
|
findreplace.find.GetSelection(l, h);
|
|
iwc.Clear();
|
|
FindWildcardMenu(THISBACK(InsertWildcard), findreplace.find.GetPushScreenRect().TopRight(), true,
|
|
&findreplace, findreplace.regexp);
|
|
if(iwc.GetCount()) {
|
|
if(!findreplace.regexp)
|
|
findreplace.wildcards = true;
|
|
findreplace.find.SetFocus();
|
|
findreplace.find.SetSelection(l, h);
|
|
findreplace.find.RemoveSelection();
|
|
findreplace.find.Insert(iwc);
|
|
}
|
|
}
|
|
|
|
void CodeEditor::ReplaceWildcard()
|
|
{
|
|
MenuBar menu;
|
|
String ptxt;
|
|
if(findreplace.regexp)
|
|
ptxt = t_("Matched subpattern %d");
|
|
else {
|
|
menu.Add(t_("Matched spaces"), THISBACK1(InsertWildcard, "%"));
|
|
menu.Add(t_("Matched one or more any characters"), THISBACK1(InsertWildcard, "*"));
|
|
menu.Add(t_("Matched C++ identifier"), THISBACK1(InsertWildcard, "$"));
|
|
menu.Add(t_("Matched number"), THISBACK1(InsertWildcard, "#"));
|
|
menu.Add(t_("Matched any character"), THISBACK1(InsertWildcard, "?"));
|
|
ptxt = t_("Matched wildcard %d");
|
|
}
|
|
menu.Add(t_("0-based replace index"), THISBACK1(InsertWildcard, "@@"));
|
|
menu.Add(t_("1-based replace index"), THISBACK1(InsertWildcard, "@#"));
|
|
menu.Separator();
|
|
for(int i = 1; i <= 9; i++)
|
|
menu.Add(Format(ptxt, i), THISBACK1(InsertWildcard, "@"+AsString(i)));
|
|
menu.Separator();
|
|
menu.Add(t_("To upper"), THISBACK1(InsertWildcard, "+"));
|
|
menu.Add(t_("To lower"), THISBACK1(InsertWildcard, "-"));
|
|
menu.Add(t_("InitCaps"), THISBACK1(InsertWildcard, "!"));
|
|
menu.Separator();
|
|
menu.Add(t_("Tab"), THISBACK1(InsertWildcard, "\\t"));
|
|
menu.Add(t_("Line feed"), THISBACK1(InsertWildcard, "\\n"));
|
|
int l, h;
|
|
findreplace.replace.GetSelection(l, h);
|
|
iwc.Clear();
|
|
menu.Execute(&findreplace, findreplace.replace.GetPushScreenRect().TopRight());
|
|
if(iwc.GetCount()) {
|
|
if(!findreplace.regexp)
|
|
findreplace.wildcards = true;
|
|
findreplace.replace.SetFocus();
|
|
findreplace.replace.SetSelection(l, h);
|
|
findreplace.replace.RemoveSelection();
|
|
findreplace.replace.Insert(iwc);
|
|
}
|
|
}
|
|
|
|
void CodeEditor::CloseFindReplace()
|
|
{
|
|
if(findreplace.IsOpen()) {
|
|
FindReplaceAddHistory();
|
|
RemoveFrame(findreplace);
|
|
}
|
|
}
|
|
|
|
void CodeEditor::EscapeFindReplace()
|
|
{
|
|
CloseFindReplace();
|
|
if(ff_start_pos >= 0 && ff_start_pos < GetLength64() && findreplace.IsIncremental() && do_ff_restore_pos) {
|
|
SetCursor(ff_start_pos);
|
|
ff_start_pos = -1;
|
|
}
|
|
}
|
|
|
|
void CodeEditor::IncrementalFind()
|
|
{
|
|
NoFindError();
|
|
findreplace.Sync();
|
|
if(!findreplace.IsIncremental() || findreplace.GetTopCtrl() == &findreplace) // || we are block replace
|
|
return;
|
|
bool b = FindFrom(ff_start_pos >= 0 && ff_start_pos < GetLength64()
|
|
&& findreplace.incremental_from_cursor ? ff_start_pos : 0, false, false);
|
|
findreplace.amend.Enable(b);
|
|
if(!b)
|
|
NotFound();
|
|
}
|
|
|
|
void CodeEditor::DoFind()
|
|
{
|
|
Find(false, false);
|
|
}
|
|
|
|
void CodeEditor::DoFindBack()
|
|
{
|
|
Find(true, false);
|
|
}
|
|
|
|
void CodeEditor::SerializeFind(Stream& s)
|
|
{
|
|
int version = 2;
|
|
s / version;
|
|
s % findreplace.find;
|
|
findreplace.find.SerializeList(s);
|
|
s % findreplace.wholeword % findreplace.ignorecase % findreplace.wildcards;
|
|
if(version >= 0)
|
|
s % findreplace.samecase;
|
|
s % findreplace.replace;
|
|
if(version >= 1)
|
|
s % findreplace.incremental;
|
|
if(version >= 2)
|
|
s % findreplace.regexp;
|
|
findreplace.replace.SerializeList(s);
|
|
}
|
|
|
|
String ReadList(WithDropChoice<EditString>& e)
|
|
{
|
|
StringStream ss;
|
|
e.SerializeList(ss);
|
|
return ss;
|
|
}
|
|
|
|
void WriteList(WithDropChoice<EditString>& e, const String& data)
|
|
{
|
|
StringStream ss(data);
|
|
e.SerializeList(ss);
|
|
}
|
|
|
|
CodeEditor::FindReplaceData CodeEditor::GetFindReplaceData()
|
|
{
|
|
FindReplaceData r;
|
|
r.find = ~findreplace.find;
|
|
r.replace = ~findreplace.replace;
|
|
r.wholeword = ~findreplace.wholeword;
|
|
r.ignorecase = ~findreplace.ignorecase;
|
|
r.wildcards = ~findreplace.wildcards;
|
|
r.samecase = ~findreplace.samecase;
|
|
r.regexp = ~findreplace.regexp;
|
|
r.find_list = ReadList(findreplace.find);
|
|
r.replace_list = ReadList(findreplace.replace);
|
|
return r;
|
|
}
|
|
|
|
void CodeEditor::SetFindReplaceData(const FindReplaceData& r)
|
|
{
|
|
findreplace.find <<= r.find;
|
|
findreplace.replace <<= r.replace;
|
|
findreplace.wholeword <<= r.wholeword;
|
|
findreplace.ignorecase <<= r.ignorecase;
|
|
findreplace.wildcards <<= r.wildcards;
|
|
findreplace.samecase <<= r.samecase;
|
|
findreplace.regexp <<= r.regexp;
|
|
findreplace.mode <<= 0;
|
|
WriteList(findreplace.find, r.find_list);
|
|
WriteList(findreplace.replace, r.replace_list);
|
|
}
|
|
|
|
void CodeEditor::FindPrevNext(bool prev)
|
|
{
|
|
StartSearchProgress(-1, -1);
|
|
if(!findreplace.IsOpen()) {
|
|
WString find_text;
|
|
if(IsSelection()) {
|
|
int l, h;
|
|
GetSelection32(l, h);
|
|
if(h - l < 100) {
|
|
find_text = GetSelectionW();
|
|
if(find_text.Find('\n') >= 0)
|
|
find_text.Clear();
|
|
}
|
|
}
|
|
if(find_text.GetCount())
|
|
findreplace.find <<= find_text;
|
|
OpenNormalFindReplace0(false);
|
|
}
|
|
if(Find(prev, false))
|
|
NoFindError();
|
|
else
|
|
NotFound();
|
|
EndSearchProgress();
|
|
}
|
|
|
|
void CodeEditor::FindNext()
|
|
{
|
|
FindPrevNext(false);
|
|
}
|
|
|
|
void CodeEditor::FindPrev()
|
|
{
|
|
FindPrevNext(true);
|
|
}
|
|
|
|
void CodeEditor::StartSearchProgress(int64, int64)
|
|
{
|
|
search_canceled = false;
|
|
search_progress.Create();
|
|
search_progress->SetText("Scanning the file");
|
|
search_time0 = msecs();
|
|
}
|
|
|
|
bool CodeEditor::SearchProgress(int line)
|
|
{
|
|
if(search_progress && !search_canceled && msecs(search_time0) > 20) {
|
|
search_time0 = msecs();
|
|
search_progress->Create();
|
|
search_canceled = IsView() ? search_progress->SetCanceled(int(GetPos64(line) >> 8), int(GetViewSize() >> 8))
|
|
: search_progress->SetCanceled(line, GetLineCount());
|
|
}
|
|
return !search_canceled;
|
|
}
|
|
|
|
bool CodeEditor::SearchCanceled()
|
|
{
|
|
return search_canceled;
|
|
}
|
|
|
|
void CodeEditor::EndSearchProgress()
|
|
{
|
|
search_progress.Clear();
|
|
search_canceled = false;
|
|
}
|
|
|
|
}
|