From 814731a6dfc3ac2071a0b4cfbc85a3901c0be86a Mon Sep 17 00:00:00 2001 From: lsv Date: Tue, 26 May 2026 14:29:30 +0500 Subject: [PATCH] Improved the dialog for comparing two cells. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Устранена проблема с отображением номеров строк. 2. Добавлена опция "Cleanup semantic" для более красивого отображения различий. 3. Добавлена кнопка "Copy diff to HTML" копирования правого окна с объединением различий левого. Жёлтым фоном выделяются добавления и изменения, красным удаленные элементы левого. Могут быть артефакты и искажения в каких то экзотических случаях. 4. Добавлена кнопка "Copy Left/Right to HTML" в зависимости от фокуса копируется левая или правая часть. --- ctl/ctlSQLBox.cpp | 75 ++++++++++++++++------ include/ctl/SourceViewDialog.h | 112 ++++++++++++++++++++++++++++++--- include/ctl/ctlSQLBox.h | 4 +- utils/misc.cpp | 2 +- 4 files changed, 162 insertions(+), 31 deletions(-) diff --git a/ctl/ctlSQLBox.cpp b/ctl/ctlSQLBox.cpp index 0e07056..1f2df6f 100644 --- a/ctl/ctlSQLBox.cpp +++ b/ctl/ctlSQLBox.cpp @@ -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("
", fontName, sz); int k = 0; int l = 1; + int indic=9; wxString newline = "
"; if (isAddNewLine) newline = L"\u2936
"; //if (isAddNewLine) newline = L"⤶
"; @@ -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 (tmppos0 && 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=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+="<"; k++; continue; }; if (c == '>') { lstr+=">"; k++; continue; }; if (c == '&') { lstr+="&"; k++; continue; }; - if (s > 0) for (int tt = 0; tt < s; tt++) lstr += " "; + if (s > 0) + for (int tt = 0; tt < s; tt++) lstr += " "; 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("%s", 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 = "
"; + 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%s%s",lleft,bgclrDel, obj,rright); + } + else + //lstr=wxString::Format("%s%s",lstr,bgclrDel, obj); + lstr=wxString::Format("%s%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("%s", 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=""; } diff --git a/include/ctl/SourceViewDialog.h b/include/ctl/SourceViewDialog.h index 9d02aaa..59b7841 100644 --- a/include/ctl/SourceViewDialog.h +++ b/include/ctl/SourceViewDialog.h @@ -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 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 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"" + t + L""; addIndicText(ctlR, t, s_indicHighlight); } if (aDiff.operation == 0) { + FSQL::complite_element cl={}; + cl.table=escapeHtml(t,false); + cl.table.Replace("\t","     "); + 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"" + t + L""; 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: } // ���� �� ������� ������ ������ 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"); + } }; diff --git a/include/ctl/ctlSQLBox.h b/include/ctl/ctlSQLBox.h index 070d140..7b6390b 100644 --- a/include/ctl/ctlSQLBox.h +++ b/include/ctl/ctlSQLBox.h @@ -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; }; diff --git a/utils/misc.cpp b/utils/misc.cpp index fc61af6..0e70b3b 100644 --- a/utils/misc.cpp +++ b/utils/misc.cpp @@ -89,7 +89,7 @@ wxString escapeHtml(wxString text, bool pre) { text.Replace("&", "&"); text.Replace("<", "<"); text.Replace(">", ">"); - if (!pre) text.Replace("\n", "¶
"); + if (!pre) text.Replace("\n", "
"); return text; }