Improved the dialog for comparing two cells.

1. Устранена проблема с отображением номеров строк.
2. Добавлена опция "Cleanup semantic" для более красивого отображения различий.
3. Добавлена кнопка "Copy diff to HTML" копирования правого окна с объединением различий левого.
   Жёлтым фоном выделяются добавления и изменения, красным удаленные элементы левого.
   Могут быть артефакты и искажения в каких то экзотических случаях.
4. Добавлена кнопка "Copy Left/Right to HTML" в зависимости от фокуса копируется левая или правая
   часть.
This commit is contained in:
lsv 2026-05-26 14:29:30 +05:00
parent 2bcd61f10c
commit 814731a6df
4 changed files with 162 additions and 31 deletions

View file

@ -95,6 +95,7 @@ ctlSQLBox::ctlSQLBox(wxWindow *parent, wxWindowID id, const wxPoint &pos, const
process = 0;
processID = 0;
m_hint_mode = false;
issimple=false;
Create(parent, id, pos, size, style);
}
//void ctlSQLBox::OnBackGround(wxEraseEvent &event) {
@ -317,7 +318,9 @@ wxString ctlSQLBox::GetFilename()
{
return m_filename;
}
void ctlSQLBox::SetSimpleMode(bool issimplemode) {
issimple=issimplemode;
}
void ctlSQLBox::SetTitle(wxString &title)
{
m_title = title;
@ -1281,7 +1284,7 @@ void ctlSQLBox::OnKillFocus(wxFocusEvent &event)
void ctlSQLBox::UpdateLineNumber()
{
bool showlinenumber;
if (GetSimpleMode()) return;
settings->Read(wxT("frmQuery/ShowLineNumber"), &showlinenumber, false);
if (showlinenumber)
{
@ -1825,6 +1828,7 @@ wxString ctlSQLBox::TextToHtml(int start, int end,bool isAddNewLine, const std::
str = wxString::Format("<div style=\"font-family: %s; font-size: %spx\"><span>", fontName, sz);
int k = 0;
int l = 1;
int indic=9;
wxString newline = "<br>";
if (isAddNewLine) newline = L"\u2936<br>";
//if (isAddNewLine) newline = L"&ldca;<br>";
@ -1834,7 +1838,10 @@ wxString ctlSQLBox::TextToHtml(int start, int end,bool isAddNewLine, const std::
int pos=999999999;
wxString obj;
int lenobj;
if (listobj.size()>0) {pos=listobj[IndexObj].startIndex; obj=listobj[IndexObj].table;lenobj=obj.Len();}
if (listobj.size()>0) {
pos=listobj[IndexObj].startIndex; obj=listobj[IndexObj].table;lenobj=obj.Len();
if (GetSimpleMode()) indic=listobj[IndexObj].level;
}
wxString lstr;
wxString bgclr="";
// find Indicator bookmark
@ -1843,30 +1850,31 @@ wxString ctlSQLBox::TextToHtml(int start, int end,bool isAddNewLine, const std::
int tmppos=0;
int textlen=GetTextLength();
while (tmppos<textlen) {
tmppos=IndicatorEnd(9,tmppos);
tmppos=IndicatorEnd(indic,tmppos);
if (tmppos==0 || tmppos==textlen) break;
if (tmppos>0 && tmppos!=textlen) {
spos=tmppos;
epos=IndicatorEnd(9,tmppos);
epos=IndicatorEnd(indic,tmppos);
if (epos>=start) break;
tmppos=epos;
spos=epos=-1;
} else break;
}
bool refreshgbcolor=false;
bool newlineadd=false;
while (k<lenstr) {
int st = GetStyleAt(startp);
if (st < 34) tColor = frColor[st].GetAsString(wxC2S_HTML_SYNTAX);
if (startp>=spos && spos!=-1) {
refreshgbcolor=true;
bgclr="#ffff00";
tmppos=IndicatorEnd(9,epos);
tmppos=IndicatorEnd(indic,epos);
if (tmppos==textlen) spos=-1;
else spos=tmppos;
} else if (startp>=epos && epos!=-1) {
bgclr="";
refreshgbcolor=true;
tmppos=IndicatorEnd(9,spos);
tmppos=IndicatorEnd(indic,spos);
if (tmppos==textlen) epos=-1;
else epos=tmppos;
}
@ -1886,28 +1894,55 @@ wxString ctlSQLBox::TextToHtml(int start, int end,bool isAddNewLine, const std::
//wxUniChar c = selText[k].GetValue();
if (c == '\r') { k++; continue; };
if (c == '\n') { lstr += newline; k++; continue; };
if (c == '\n') {
lstr += newline; newlineadd=true; k++;
continue;
};
if (c == 9) s = 5;
if (c == 32) s = 1;
if (c == '<') { lstr+="&lt;"; k++; continue; };
if (c == '>') { lstr+="&gt;"; k++; continue; };
if (c == '&') { lstr+="&amp;"; k++; continue; };
if (s > 0) for (int tt = 0; tt < s; tt++) lstr += "&nbsp;";
if (s > 0)
for (int tt = 0; tt < s; tt++) lstr += "&nbsp;";
else lstr += c;
k++;
if ((k-1)>=pos) {
lenobj--;
if (lenobj==0) {
wxString s,n;
make_identifier(obj,s,n,true);
if (!s.IsEmpty()) obj=s+'.'+n; else obj=n;
lstr=wxString::Format("<a href=\"%s\">%s</a>", obj, lstr);
IndexObj++;
if (listobj.size()>IndexObj) { pos=listobj[IndexObj].startIndex; obj=listobj[IndexObj].table; lenobj=obj.Len();}
else pos=999999999;
} else
continue; // link not ready
if (GetSimpleMode()) {
// this compare dialog Append Delete text
wxString bgclrDel="#ebb1c4";
if (newlineadd) {
size_t p44=0,p45=0;
wxString searchStr = "<br>";
p44=lstr.Find('<',true);
if (p44!=-1) {
p45=p44;
p44++;
}
wxString lleft=lstr.Left(p45+4);
wxString rright=lstr.Mid(p45+4);
lstr=wxString::Format("%s<span style=\"background-color: %s\">%s</span>%s",lleft,bgclrDel, obj,rright);
}
else
//lstr=wxString::Format("%s<span style=\"background-color: %s\">%s</span>",lstr,bgclrDel, obj);
lstr=wxString::Format("<span style=\"background-color: %s\">%s</span>%s",bgclrDel, obj,lstr);
} else {
// links
lenobj--;
if (lenobj==0) {
wxString s,n;
make_identifier(obj,s,n,true);
if (!s.IsEmpty()) obj=s+'.'+n; else obj=n;
lstr=wxString::Format("<a href=\"%s\">%s</a>", obj, lstr);
} else
continue; // link not ready
}
IndexObj++;
if (listobj.size()>IndexObj) { pos=listobj[IndexObj].startIndex; obj=listobj[IndexObj].table; lenobj=obj.Len();}
else pos=999999999;
}
newlineadd=false;
str += lstr;
lstr="";
}

View file

@ -10,11 +10,18 @@ class SourceViewDialog : public wxFrame
wxCheckBox* m_visibleSpace;
wxCheckBox* m_showNumber;
wxCheckBox* m_linescompare;
wxCheckBox* m_symantecclean;
wxButton* m_btn_next;
wxButton* m_btn_html;
wxButton* m_btn_htmlcopy;
wxWindow* lastfocus=NULL;
int s_indicHighlight;
bool isdiff=false;
int pos = 0;
int prev_line = -1;
wxString sqlr;
wxString sqll;
std::vector<FSQL::complite_element> listdelete;
public:
~SourceViewDialog() {
//delete m_text1;
@ -36,6 +43,7 @@ public:
text->SetMarginWidth(0, width);
text->Update();
}
}
else {
text->SetMarginWidth(0, 0);
@ -50,10 +58,8 @@ public:
// text->StyleSetForeground(wxSTC_STYLE_LINENUMBER, wxColour(75, 75, 75));
// text->StyleSetBackground(wxSTC_STYLE_LINENUMBER, wxColour(220, 220, 220));
// text->SetMarginType(MARGIN_LINE_NUMBERS, wxSTC_MARGIN_NUMBER);
text->SetSimpleMode(true);
text->SetEOLMode(2);
showNumber(text, true);
//text->StyleSetForeground(wxSTC_H_DOUBLESTRING, wxColour(255, 0, 0));
//text->StyleSetForeground(wxSTC_H_SINGLESTRING, wxColour(255, 0, 0));
//text->StyleSetForeground(wxSTC_H_ENTITY, wxColour(255, 0, 0));
@ -70,7 +76,7 @@ public:
int start=ctl->GetLength();
ctl->AddText(t);
if (indic > 0) {
isdiff=true;
ctl->IndicatorFillRange(start, ctl->GetLength()-start);
}
};
@ -79,6 +85,13 @@ public:
//Match_Threshold = 0.5;
//Match_Distance = 1000;
diff_match_patch dmp(4, 0.5, 1000);
listdelete.clear();
ctlL->ClearAll();
ctlR->ClearAll();
isdiff=false;
m_btn_next->Disable();
m_btn_html->Disable();
m_btn_htmlcopy->Disable();
std::list<Diff> diffs;
if (sL == sR) {
return;
@ -93,6 +106,7 @@ public:
dmp.diff_charsToLines(diffs, lineArray);
} else
diffs = dmp.diff_main(sL.wc_str(), sR.wc_str(), true);
if (m_symantecclean && m_symantecclean->IsChecked()) dmp.diff_cleanupSemantic(diffs);
int nstart = 0;
int pos = 0;
std::wstring cur_l;
@ -136,6 +150,21 @@ public:
cur_r += L"<span class=\"differencei\">" + t + L"</span>"; addIndicText(ctlR, t, s_indicHighlight);
}
if (aDiff.operation == 0) {
FSQL::complite_element cl={};
cl.table=escapeHtml(t,false);
cl.table.Replace("\t","&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;");
cl.level=s_indicHighlight;
cl.startIndex=ctlR->GetTextLength();
FSQL::complite_element prev;
if (listdelete.size()>0) {
prev=listdelete[listdelete.size()-1];
if (prev.startIndex==cl.startIndex) {
prev.table+=cl.table;
listdelete[listdelete.size()-1]=prev;
} else
listdelete.push_back(cl);
} else
listdelete.push_back(cl);
cur_l += L"<span class=\"differenced\">" + t + L"</span>"; addIndicText(ctlL, t, s_indicHighlight);
}
if (aDiff.operation == Operation::EQUAL) {
@ -160,6 +189,20 @@ public:
std::wstring t_cur_r = cur_r;
if (aDiff.operation == 0) {
t_cur_r = L"";
FSQL::complite_element cl={};
cl.table=escapeHtml("\n",false);
cl.level=s_indicHighlight;
cl.startIndex=ctlR->GetTextLength();
FSQL::complite_element prev;
if (listdelete.size()>0) {
prev=listdelete[listdelete.size()-1];
if (prev.startIndex==cl.startIndex) {
prev.table+=cl.table;
listdelete[listdelete.size()-1]=prev;
} else
listdelete.push_back(cl);
} else
listdelete.push_back(cl);
addIndicText(ctlL, L"\n", s_indicHighlight);
ncur_l = std::to_wstring(lline);
modify = true;
@ -210,6 +253,9 @@ public:
} // <20><><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> Diff
++it;
}
m_btn_next->Enable(isdiff);
m_btn_html->Enable(isdiff);
m_btn_htmlcopy->Enable(isdiff);
}
SourceViewDialog(wxWindow* parent, wxString sqlL, wxString sqlR, wxString title) :
@ -229,11 +275,14 @@ public:
//m_text1->SetText(sqlL);
m_text2 = createSTC(m_panelSql);
//m_text2->Bind(wxID_ANY, wxEVT_SET_FOCUS, wxFocusEventHandler(OnFocus));
m_text2->Bind(wxEVT_SET_FOCUS, &SourceViewDialog::OnFocus, this);
m_text1->Bind(wxEVT_SET_FOCUS, &SourceViewDialog::OnFocus, this);
//m_text2->SetText(sqlR);
sqll=sqlL;
sqlr=sqlR;
m_linescompare=NULL;
difftext(m_text1, m_text2, sqll, sqlr);
m_symantecclean=NULL;
bSizer2->Add(m_text1, 1, wxEXPAND, 5);
@ -255,10 +304,17 @@ public:
m_showNumber = new wxCheckBox(m_panelOpt, wxID_ANY, wxT("Show number line"), wxDefaultPosition, wxDefaultSize, 0);
bSizer4->Add(m_showNumber, 0, wxALL, 5);
m_linescompare = new wxCheckBox(m_panelOpt, wxID_ANY, wxT("Words compare"), wxDefaultPosition, wxDefaultSize, 0);
m_symantecclean= new wxCheckBox(m_panelOpt, wxID_ANY, wxT("Cleanup semantic"), wxDefaultPosition, wxDefaultSize, 0);
bSizer4->Add(m_linescompare, 0, wxALL, 5);
wxButton* m_btn_next = new wxButton(m_panelOpt, wxID_ANY, wxT("Next"), wxDefaultPosition, wxDefaultSize, 0);
bSizer4->Add(m_symantecclean, 0, wxALL, 5);
m_btn_next = new wxButton(m_panelOpt, wxID_ANY, wxT("Next"), wxDefaultPosition, wxDefaultSize, 0);
m_btn_html = new wxButton(m_panelOpt, wxID_ANY, wxT("Copy diff to HTML"), wxDefaultPosition, wxDefaultSize, 0);
m_btn_htmlcopy = new wxButton(m_panelOpt, wxID_ANY, wxT("Copy to HTML"), wxDefaultPosition, wxDefaultSize, 0);
bSizer4->Add(m_btn_next, 0, wxALL, 5);
bSizer4->Add(m_btn_html, 0, wxALL, 5);
bSizer4->Add(m_btn_htmlcopy, 0, wxALL, 5);
bSizer4->Add(0, 0, 1, wxEXPAND, 5);
@ -268,7 +324,7 @@ public:
// wxButton *m_btn_cancel = new wxButton(m_panelOpt, wxID_ANY, wxT("Cancel"), wxDefaultPosition, wxDefaultSize, 0);
// bSizer4->Add(m_btn_cancel, 0, wxALL, 5);
difftext(m_text1, m_text2, sqll, sqlr);
m_panelOpt->SetSizer(bSizer4);
m_panelOpt->Layout();
bSizer4->Fit(m_panelOpt);
@ -282,10 +338,13 @@ public:
m_visibleSpace->Bind(wxEVT_CHECKBOX, &SourceViewDialog::OnShowCheckBoxSpace, this);
m_linescompare->Bind(wxEVT_CHECKBOX, &SourceViewDialog::OnLinesCompare, this);
m_symantecclean->Bind(wxEVT_CHECKBOX, &SourceViewDialog::OnLinesCompare, this);
m_showNumber->Bind(wxEVT_CHECKBOX, &SourceViewDialog::OnShowCheckShowNumber, this);
m_btn_close->Bind(wxEVT_BUTTON, &SourceViewDialog::onClose2, this);
m_btn_next->Bind(wxEVT_BUTTON, &SourceViewDialog::onNext, this);
m_btn_html->Bind(wxEVT_BUTTON, &SourceViewDialog::onHtmlDiff, this);
m_btn_htmlcopy->Bind(wxEVT_BUTTON, &SourceViewDialog::onHtmlCopy, this);
//Connect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(SourceViewDialog::onClose), NULL, this);
//m_text1->Connect(wxEVT_STC_PAINTED, wxStyledTextEventHandler(SourceViewDialog::onScrollLeft), NULL, this);
//m_text2->Connect(wxEVT_STC_PAINTED, wxStyledTextEventHandler(SourceViewDialog::onScrollRight), NULL, this);
@ -355,12 +414,16 @@ public:
prev_line = -1;
}
}
void OnLinesCompare(wxCommandEvent& evt) {
bool bVal = m_linescompare->IsChecked();
void Recompare()
{
m_text1->ClearAll();
m_text2->ClearAll();
difftext(m_text1, m_text2, sqll, sqlr);
}
void OnLinesCompare(wxCommandEvent& evt) {
Recompare();
OnShowCheckShowNumber(evt);
}
void OnShowCheckBoxSpace(wxCommandEvent& evt) {
bool bVal = m_visibleSpace->IsChecked();
@ -405,4 +468,35 @@ public:
m_changing_values = false;
}
void onHtmlDiff(wxCommandEvent& evt) {
if (m_text2->GetLength()>0) {
wxString html=m_text2->TextToHtml(0,m_text2->GetLength(),false,listdelete);
if (wxTheClipboard->Open())
{
wxTheClipboard->SetData(new wxHTMLDataObject(html));
wxTheClipboard->Close();
}
}
}
void onHtmlCopy(wxCommandEvent& evt) {
wxWindow *wnd = lastfocus;
wxString html;
if (wnd) {
if (m_text2==wnd) html=m_text2->TextToHtml(0,m_text2->GetLength(),false);
if (m_text1==wnd) html=m_text1->TextToHtml(0,m_text1->GetLength(),false);
if (html.Length()>0 && wxTheClipboard->Open())
{
wxTheClipboard->SetData(new wxHTMLDataObject(html));
wxTheClipboard->Close();
}
}
}
void OnFocus(wxFocusEvent &ev) {
lastfocus = FindFocus();
if (m_text2==lastfocus) m_btn_htmlcopy->SetLabelText("Copy Right to HTML");
if (m_text1==lastfocus) m_btn_htmlcopy->SetLabelText("Copy Left to HTML");
}
};

View file

@ -100,6 +100,8 @@ public:
int GetOrigin();
void SetFilename(wxString &filename);
bool IsFileModification();
void SetSimpleMode(bool issimplemode);
bool GetSimpleMode() {return issimple;};
wxString GetFilename();
void SetTitle(wxString &title);
wxString GetTitle(bool withChangeInd = true);
@ -152,7 +154,7 @@ private:
bool m_changed;
int m_origin;
int fix_pos;
bool issimple=false;
friend class QueryPrintout;
};

View file

@ -89,7 +89,7 @@ wxString escapeHtml(wxString text, bool pre) {
text.Replace("&", "&amp;");
text.Replace("<", "&lt;");
text.Replace(">", "&gt;");
if (!pre) text.Replace("\n", "&para;<br>");
if (!pre) text.Replace("\n", "<br>");
return text;
}