TextDiffCtrl: Line differences now shown (thanks Coolman)

git-svn-id: svn://ultimatepp.org/upp/trunk@10553 f0d560ea-af0d-0410-9eb7-867de7ffcac7
This commit is contained in:
cxl 2016-12-18 08:07:46 +00:00
parent a1d9192520
commit 6d6bc38d15
4 changed files with 527 additions and 432 deletions

View file

@ -1,182 +1,192 @@
#include "TextDiffCtrl.h"
namespace Upp {
#define IMAGECLASS DiffImg
#define IMAGEFILE <TextDiffCtrl/Diff.iml>
#include <Draw/iml_source.h>
TextDiffCtrl::TextDiffCtrl()
{
left.Gutter(30);
right.NoGutter();
Horz(left, right);
left.WhenScroll = right.ScrollWhen(left);
right.WhenScroll = left.ScrollWhen(right);
right.HideSb();
left.WhenLeftDouble = Proxy(WhenLeftLine);
right.WhenLeftDouble = Proxy(WhenRightLine);
}
static bool SmallDiff(const char *s1, const char *s2)
{
for(;;) {
while(*s1 && (byte)*s1 <= ' ')
s1++;
while(*s2 && (byte)*s2 <= ' ')
s2++;
if(!*s1 || !*s2)
return !*s1 && !*s2;
if(*s1 != *s2)
return false;
while(*s1 && *s1 == *s2)
s1++, s2++;
if((byte)s1[-1] <= ' ')
continue;
if((byte)*s1 > ' ' || (byte)*s2 > ' ')
return false;
}
}
void TextDiffCtrl::Set(Stream& l, Stream& r)
{
Vector<String> ll = GetLineMap(l);
Vector<String> rl = GetLineMap(r);
Array<TextSection> sections = CompareLineMaps(ll, rl);
// Point left_pos = left.GetPos();
// Point right_pos = right.GetPos();
// int sb_pos = left.GetSb();
int outln = 0;
left.SetCount(0);
right.SetCount(0);
int firstdiff = -1;
for(int i = 0; i < sections.GetCount(); i++) {
const TextSection& sec = sections[i];
bool diff = !sec.same;
if(firstdiff < 0 && diff)
firstdiff = outln;
int maxcount = max(sec.count1, sec.count2);
left.AddCount(maxcount);
int l;
for(l = 0; l < sec.count1; l++) {
int level = (diff ? l < sec.count2 && SmallDiff(ll[sec.start1 + l], rl[sec.start2 + l]) ? 1 : 2 : 0);
left.Set(outln + l, ll[sec.start1 + l], diff, sec.start1 + l + 1, level);
}
for(; l < maxcount; l++)
left.Set(outln + l, Null, diff, Null, 2);
right.AddCount(maxcount);
for(l = 0; l < sec.count2; l++) {
int level = (diff ? l < sec.count1 && SmallDiff(rl[sec.start2 + l], ll[sec.start1 + l]) ? 1 : 2 : 0);
right.Set(outln + l, rl[sec.start2 + l], diff, sec.start2 + l + 1, level);
}
for(; l < maxcount; l++)
right.Set(outln + l, Null, diff, Null, 2);
outln += maxcount;
}
if(firstdiff >= 0)
left.SetSb(max(firstdiff - 2, 0));
left.ClearSelection();
right.ClearSelection();
}
void TextDiffCtrl::Set(const String& l, const String& r)
{
StringStream sl(l);
StringStream sr(r);
Set(sl, sr);
}
INITBLOCK {
RegisterGlobalConfig("diff");
}
void DiffDlg::Execute(const String& f)
{
editfile = f;
l <<= editfile;
Title(editfile);
String h;
{
LoadFromGlobal(h, "diff");
StringStream ss(h);
SerializePlacement(ss);
}
TopWindow::Execute();
{
StringStream ss;
SerializePlacement(ss);
h = ss;
StoreToGlobal(h, "diff");
}
}
void DiffDlg::Write()
{
if(PromptYesNo("Do you want to overwrite&[* " + DeQtf(editfile) + "] ?")) {
SaveFile(editfile, extfile);
Break(IDOK);
}
}
Event<const String&, Vector<LineEdit::Highlight>&, const WString&> DiffDlg::WhenHighlight;
static void sDoHighlight(Vector<LineEdit::Highlight>& hl, const WString& ln, const String *path)
{
DiffDlg::WhenHighlight(*path, hl, ln);
}
DiffDlg::DiffDlg()
{
Add(diff.SizePos());
Sizeable().Zoomable();
diff.InsertFrameLeft(p);
int cy = EditField::GetStdHeight();
p.Height(cy);
p.Add(l.VSizePos().HSizePos(0, 6 * cy));
p.Add(write.VSizePos().RightPos(0, 6 * cy));
write <<= THISBACK(Write);
write.SetLabel("Overwrite <-");
l.SetReadOnly();
diff.left.WhenHighlight = callback1(sDoHighlight, &editfile);
diff.right.WhenHighlight = callback1(sDoHighlight, &editfile);
}
void FileDiff::Open()
{
if(!fs.ExecuteOpen())
return;
String f = ~fs;
r <<= f;
diff.Set(LoadFile(editfile), extfile = LoadFile(f));
}
void FileDiff::Execute(const String& f)
{
editfile = f;
Open();
if(IsNull(r))
return;
DiffDlg::Execute(f);
}
FileDiff::FileDiff(FileSel& fs_)
: fs(fs_)
{
r.Height(EditField::GetStdHeight());
Icon(DiffImg::Diff());
diff.InsertFrameRight(r);
r <<= THISBACK(Open);
}
FileSel& DiffFs() {
static FileSel fs;
ONCELOCK {
fs.Type("Patch file (*.diff, *.patch)", "*.diff *.patch");
fs.AllFilesType();
}
return fs;
}
};
#include "TextDiffCtrl.h"
namespace Upp {
#define IMAGECLASS DiffImg
#define IMAGEFILE <TextDiffCtrl/Diff.iml>
#include <Draw/iml_source.h>
TextDiffCtrl::TextDiffCtrl()
{
left.Gutter(30);
right.NoGutter();
Horz(left, right);
left.WhenScroll = right.ScrollWhen(left);
right.WhenScroll = left.ScrollWhen(right);
right.HideSb();
left.WhenLeftDouble = THISBACK(GetLeftLine);
right.WhenLeftDouble = THISBACK(GetRightLine);
}
void TextDiffCtrl::GetLeftLine(int number, int line)
{
WhenLeftLine(number);
}
void TextDiffCtrl::GetRightLine(int number, int line)
{
WhenRightLine(number);
}
static bool SmallDiff(const char *s1, const char *s2)
{
for(;;) {
while(*s1 && (byte)*s1 <= ' ')
s1++;
while(*s2 && (byte)*s2 <= ' ')
s2++;
if(!*s1 || !*s2)
return !*s1 && !*s2;
if(*s1 != *s2)
return false;
while(*s1 && *s1 == *s2)
s1++, s2++;
if((byte)s1[-1] <= ' ')
continue;
if((byte)*s1 > ' ' || (byte)*s2 > ' ')
return false;
}
}
void TextDiffCtrl::Set(Stream& l, Stream& r)
{
Vector<String> ll = GetLineMap(l);
Vector<String> rl = GetLineMap(r);
Array<TextSection> sections = CompareLineMaps(ll, rl);
// Point left_pos = left.GetPos();
// Point right_pos = right.GetPos();
// int sb_pos = left.GetSb();
int outln = 0;
left.SetCount(0);
right.SetCount(0);
int firstdiff = -1;
for(int i = 0; i < sections.GetCount(); i++) {
const TextSection& sec = sections[i];
bool diff = !sec.same;
if(firstdiff < 0 && diff)
firstdiff = outln;
int maxcount = max(sec.count1, sec.count2);
left.AddCount(maxcount);
int l;
for(l = 0; l < sec.count1; l++) {
int level = (diff ? l < sec.count2 && SmallDiff(ll[sec.start1 + l], rl[sec.start2 + l]) ? 1 : 2 : 0);
left.Set(outln + l, ll[sec.start1 + l], diff, sec.start1 + l + 1, level, diff && l < sec.count2 ? rl[sec.start2 + l] : Null, sec.start1 + l + 1, true);
}
for(; l < maxcount; l++)
left.Set(outln + l, Null, diff, Null, 2, Null, Null, true);
right.AddCount(maxcount);
for(l = 0; l < sec.count2; l++) {
int level = (diff ? l < sec.count1 && SmallDiff(rl[sec.start2 + l], ll[sec.start1 + l]) ? 1 : 2 : 0);
right.Set(outln + l, rl[sec.start2 + l], diff, sec.start2 + l + 1, level, diff && l < sec.count1 ? ll[sec.start1 + l] : Null, sec.start2 + l + 1, false);
}
for(; l < maxcount; l++)
right.Set(outln + l, Null, diff, Null, 2, Null, Null, false);
outln += maxcount;
}
if(firstdiff >= 0)
left.SetSb(max(firstdiff - 2, 0));
left.ClearSelection();
right.ClearSelection();
}
void TextDiffCtrl::Set(const String& l, const String& r)
{
StringStream sl(l);
StringStream sr(r);
Set(sl, sr);
}
INITBLOCK {
RegisterGlobalConfig("diff");
}
void DiffDlg::Execute(const String& f)
{
editfile = f;
l <<= editfile;
Title(editfile);
String h;
{
LoadFromGlobal(h, "diff");
StringStream ss(h);
SerializePlacement(ss);
}
TopWindow::Execute();
{
StringStream ss;
SerializePlacement(ss);
h = ss;
StoreToGlobal(h, "diff");
}
}
void DiffDlg::Write()
{
if(PromptYesNo("Do you want to overwrite&[* " + DeQtf(editfile) + "] ?")) {
SaveFile(editfile, extfile);
Break(IDOK);
}
}
Event<const String&, Vector<LineEdit::Highlight>&, const WString&> DiffDlg::WhenHighlight;
static void sDoHighlight(Vector<LineEdit::Highlight>& hl, const WString& ln, const String *path)
{
DiffDlg::WhenHighlight(*path, hl, ln);
}
DiffDlg::DiffDlg()
{
Add(diff.SizePos());
Sizeable().Zoomable();
diff.InsertFrameLeft(p);
int cy = EditField::GetStdHeight();
p.Height(cy);
p.Add(l.VSizePos().HSizePos(0, 6 * cy));
p.Add(write.VSizePos().RightPos(0, 6 * cy));
write <<= THISBACK(Write);
write.SetLabel("Overwrite <-");
l.SetReadOnly();
diff.left.WhenHighlight = callback1(sDoHighlight, &editfile);
diff.right.WhenHighlight = callback1(sDoHighlight, &editfile);
}
void FileDiff::Open()
{
if(!fs.ExecuteOpen())
return;
String f = ~fs;
r <<= f;
diff.Set(LoadFile(editfile), extfile = LoadFile(f));
}
void FileDiff::Execute(const String& f)
{
editfile = f;
Open();
if(IsNull(r))
return;
DiffDlg::Execute(f);
}
FileDiff::FileDiff(FileSel& fs_)
: fs(fs_)
{
r.Height(EditField::GetStdHeight());
Icon(DiffImg::Diff());
diff.InsertFrameRight(r);
r <<= THISBACK(Open);
}
FileSel& DiffFs() {
static FileSel fs;
ONCELOCK {
fs.Type("Patch file (*.diff, *.patch)", "*.diff *.patch");
fs.AllFilesType();
}
return fs;
}
};

View file

@ -13,7 +13,7 @@ TextCompareCtrl::TextCompareCtrl()
number_bg = WhiteGray();
SetFrame(FieldFrame());
AddFrame(scroll);
SetFont(CourierZ(14), CourierZ(10));
SetFont(CourierZ(10), CourierZ(10));
scroll.NoAutoHide();
scroll.WhenScroll = THISBACK(SelfScroll);
maxwidth = 0;
@ -23,6 +23,10 @@ TextCompareCtrl::TextCompareCtrl()
gutter_fg = SGreen;
cursor = anchor = Null;
gutter_capture = false;
show_line_number = true;
show_white_space = true;
show_diff_highlight = true;
change_paper_color = true;
}
int TextCompareCtrl::GetLineNo(int y, int& ii)
@ -69,7 +73,7 @@ void TextCompareCtrl::LeftDouble(Point pt, dword keyflags)
int ii;
int i = GetLineNo(pt.y, ii);
if(!IsNull(i))
WhenLeftDouble(i - 1);
WhenLeftDouble(i - 1, ii);
}
void TextCompareCtrl::MouseMove(Point pt, dword flags)
@ -213,27 +217,35 @@ void TextCompareCtrl::Paint(Draw& draw)
draw.DrawRect(gx + gutter_width - 2, ty, 2, by - ty, Black);
}
for(int i = first_line; i <= last_line; i++) {
const Line& l = lines[i];
int y = i * letter.cy - offset.cy;
draw.DrawRect(0, y, number_width, letter.cy, number_bg);
if(!IsNull(l.number))
draw.DrawText(0, y + number_yshift, FormatInt(l.number), number_font, l.diff ? LtBlue() : SColorText());
int n_width = show_line_number ? number_width : 0;
if(show_line_number) {
for(int i = first_line; i <= last_line; i++) {
const Line& l = lines[i];
int y = i * letter.cy - offset.cy;
Color paper = IsNull(l.number) ? LtGray() : l.diff ? l.left ? Blend(LtRed(), White(), 230) : Blend(LtGreen(), White(), 230) : SColorPaper();
Color ink = l.diff ? l.left ? Red(): Green() : Gray();
draw.DrawRect(0, y, n_width, letter.cy, paper);
draw.DrawRect(n_width - 1, y, 1, letter.cy, Gray());
if(!IsNull(l.number))
draw.DrawText(0, y + number_yshift, FormatInt(l.number_diff), number_font, ink);
}
}
draw.Clip(number_width, 0, sz.cx - gutter_width - number_width, sz.cy);
draw.Clip(n_width, 0, sz.cx - gutter_width - n_width, sz.cy);
int sell, selh;
GetSelection(sell, selh);
for(int i = first_line; i <= last_line; i++) {
const Line& l = lines[i];
int y = i * letter.cy - offset.cy;
Color ink = SColorText();
Color paper = IsNull(l.number) ? SGray() : l.diff ? SColorInfo() : SColorPaper();
Color paper = IsNull(l.number) ? LtGray() : l.diff ? l.left ? Blend(LtRed(), White(), 230) : Blend(LtGreen(), White(), 230) : SColorPaper();
bool sel = l.number >= sell && l.number <= selh;
if(sel) {
ink = SColorHighlightText;
paper = SColorHighlight;
}
draw.DrawRect(0, y, sz.cx, letter.cy, paper);
WString ln = l.text.ToWString();
if(ln.GetCount() > 20000)
ln.Trim(20000);
@ -247,24 +259,66 @@ void TextCompareCtrl::Paint(Draw& draw)
h.chr = ln[i];
h.font = StdFont();
}
if(!sel)
if(!sel) {
WhenHighlight(hln, ln);
WString ln_diff = l.text_diff.ToWString();
ln_diff = ExpandTabs(ln_diff);
int diff_start = -1, diff_end = -1;
for(int i = 0; i < ln.GetCount(); ++i) {
LineEdit::Highlight& h = hln[i];
if (change_paper_color)
h.paper = paper;
if(show_diff_highlight
&& (diff_start == -1)
&& (i < ln_diff.GetCount() && (h.chr != ln_diff[i])))
diff_start = i;
}
if(show_diff_highlight && (diff_start >= 0)) {
for(int i = ln.GetCount(), j = ln_diff.GetCount(); i >= 0 && j >=0; --i, --j) {
if(ln[i] != ln_diff[j]) {
diff_end = i;
break;
}
}
for(int i = diff_start; i <= diff_end; ++i) {
LineEdit::Highlight& h = hln[i];
h.paper = l.left ? Blend(LtRed(), White(), 192) : Blend(LtGreen(), White(), 128);
}
}
if(show_white_space) {
for(int i = ln.GetCount(); i >= 0; --i) {
LineEdit::Highlight& h = hln[i];
if (ln[i] == 0)
continue;
if(ln[i] == 32)
h.paper = l.left ? Red() : Green();
else
break;
}
}
}
int x = 0;
int a = StdFont().GetAscent();
for(int i = 0; i < hln.GetCount(); i++) {
for(int i = 0; i < hln.GetCount() - 1; ++i) {
Font fnt = font;
LineEdit::Highlight& h = hln[i];
fnt.Bold(h.font.IsBold());
fnt.Italic(h.font.IsItalic());
fnt.Underline(h.font.IsUnderline());
draw.DrawText(number_width - offset.cx + x, y + a - fnt.GetAscent(), &h.chr, fnt, h.ink, 1);
x += font[h.chr];
int width = fnt[h.chr];
draw.DrawRect(n_width - offset.cx + x, y, width, fnt.GetCy(), h.paper);
draw.DrawText(n_width - offset.cx + x, y, &h.chr, fnt, h.ink, 1);
x += width;
}
}
int lcy = lcnt * letter.cy - offset.cy;
draw.DrawRect(0, lcy, sz.cx, sz.cy - lcy, SGray());
draw.End();
draw.DrawRect(0, lcy, number_width, sz.cy - lcy, number_bg);
draw.DrawRect(0, lcy, n_width, sz.cy - lcy, number_bg);
}
void TextCompareCtrl::TabSize(int t)
@ -294,7 +348,9 @@ void TextCompareCtrl::SetFont(Font f)
void TextCompareCtrl::Layout()
{
scroll.Set(scroll, (scroll.GetReducedViewSize() - Size(number_width + gutter_width, 0)) / letter, Size(maxwidth, lines.GetCount()));
int n_width = show_line_number ? number_width : 0;
scroll.Set(scroll, (scroll.GetReducedViewSize() - Size(n_width + gutter_width, 0)) / letter, Size(maxwidth, lines.GetCount()));
Refresh();
}
@ -318,7 +374,7 @@ void TextCompareCtrl::AddCount(int c)
Layout();
}
void TextCompareCtrl::Set(int line, String text, bool diff, int number, int level)
void TextCompareCtrl::Set(int line, String text, bool diff, int number, int level, String text_diff, int number_diff, bool left)
{
Line& l = lines.At(line);
int tl = MeasureLength(text.ToWString());
@ -328,6 +384,9 @@ void TextCompareCtrl::Set(int line, String text, bool diff, int number, int leve
l.text = text;
l.diff = diff;
l.level = level;
l.text_diff = text_diff;
l.number_diff = number_diff;
l.left = left;
if(rl)
UpdateWidth();
else if(tl > maxwidth) {

View file

@ -22,7 +22,7 @@ Vector<String> GetLineMap(Stream& stream)
while(!stream.IsEof()) {
String s = stream.GetLine();
const char *p = s, *e = s.End(), *f = e;
while(e > p && (byte)e[-1] <= ' ')
while(e > p && (byte)e[-1] != 9 && (byte)e[-1] < ' ')
e--;
if(e == p)
emp++;

View file

@ -1,232 +1,258 @@
#ifndef _TextDiffCtrl_TextDiffCtrl_h
#define _TextDiffCtrl_TextDiffCtrl_h
#include <CtrlLib/CtrlLib.h>
namespace Upp {
#define IMAGECLASS DiffImg
#define IMAGEFILE <TextDiffCtrl/Diff.iml>
#include <Draw/iml_header.h>
class TextSection
{
public:
TextSection(int start1, int count1, int start2, int count2, bool same)
: start1(start1), count1(count1), start2(start2), count2(count2), same(same) {}
public:
int start1;
int count1;
int start2;
int count2 : 31;
unsigned same : 1;
};
Array<TextSection> CompareLineMaps(const Vector<String>& l1, const Vector<String>& l2);
Vector<String> GetLineMap(Stream& stream);
Vector<String> GetFileLineMap(const String& path);
Vector<String> GetStringLineMap(const String &s);
class TextCompareCtrl : public Ctrl {
public:
virtual void Paint(Draw& draw);
virtual void Layout();
virtual void MouseWheel(Point pt, int zdelta, dword keyflags);
virtual void MouseMove(Point pt, dword keyflags);
virtual void LeftDown(Point pt, dword keyflags);
virtual void LeftDouble(Point pt, dword keyflags);
virtual void LeftUp(Point pt, dword keyflags);
virtual void RightDown(Point p, dword keyflags);
virtual bool Key(dword key, int repcnt);
private:
void SelfScroll();
void PairScroll(TextCompareCtrl *ctrl);
void UpdateWidth();
WString ExpandTabs(const wchar *line) const;
int MeasureLength(const wchar *line) const;
bool GetSelection(int& l, int& h);
void DoSelection(int y, bool shift);
void Copy();
int GetLineNo(int y, int& yy);
private:
struct Line {
Line() : number(Null), level(0) {}
int number;
bool diff;
String text;
int level;
};
Array<Line> lines;
int maxwidth;
ScrollBars scroll;
Font font;
Font number_font;
Color number_bg;
Color gutter_fg;
Color gutter_bg;
Size letter;
int tabsize;
int number_width;
int number_yshift;
int gutter_width;
int cursor;
int anchor;
bool gutter_capture;
typedef TextCompareCtrl CLASSNAME;
public:
Event<> WhenScroll;
Event<int> WhenLeftDouble;
Event<Vector<LineEdit::Highlight>&, const WString&> WhenHighlight;
void SetCount(int c);
void AddCount(int c);
int GetCount() const { return lines.GetCount(); }
void SetFont(Font f, Font nf);
void SetFont(Font f);
Font GetFont() const { return font; }
Font GetNumberFont() const { return number_font; }
void NumberBgColor(Color bg) { number_bg = bg; Refresh(); }
Color GetNumberBgColor() const { return number_bg; }
void ShowSb(bool ssb) { scroll.ShowY(ssb); }
void HideSb() { ShowSb(false); }
void Gutter(int size) { gutter_width = size; Refresh(); }
void NoGutter() { gutter_width = 0; Refresh(); }
void TabSize(int t);
int GetTabSize() const { return tabsize; }
void Set(int line, String text, bool diff, int number, int level);
String GetText(int line) const { return lines[line].text; }
bool GetDiff(int line) const { return lines[line].diff; }
int GetNumber(int line) const { return lines[line].number; }
Point GetPos() const;
void SetPos(Point pos);
int GetSb() const { return scroll.Get().y; }
void SetSb(int y) { scroll.Set(0, y); }
void ClearSelection() { cursor = Null; Refresh(); }
void SetSelection(int l, int h) { cursor = l; anchor = h; }
Event<> ScrollWhen(TextCompareCtrl& pair) { return THISBACK1(PairScroll, &pair); }
TextCompareCtrl();
};
struct TextDiffCtrl : public Splitter {
TextCompareCtrl left;
TextCompareCtrl right;
void Set(Stream& l, Stream& r);
void Set(const String& l, const String& r);
void InsertFrameLeft(CtrlFrame& f) { left.InsertFrame(0, f); }
void InsertFrameRight(CtrlFrame& f) { right.InsertFrame(0, f); }
void AddFrameLeft(CtrlFrame& f) { left.AddFrame(f); }
void AddFrameRight(CtrlFrame& f) { right.AddFrame(f); }
void SetFont(Font f, Font nf) { left.SetFont(f, nf); right.SetFont(f, nf); }
void SetFont(Font f) { left.SetFont(f); right.SetFont(f); }
Event<int> WhenLeftLine;
Event<int> WhenRightLine;
TextDiffCtrl();
};
struct DiffDlg : public TopWindow {
TextDiffCtrl diff;
FrameTop<StaticRect> p;
DataPusher l;
Button write;
String editfile;
String extfile;
typedef DiffDlg CLASSNAME;
void Write();
void Execute(const String& f);
static Event<const String&, Vector<LineEdit::Highlight>&, const WString&> WhenHighlight;
DiffDlg();
};
FileSel& DiffFs();
struct FileDiff : DiffDlg {
FrameTop<DataPusher> r;
virtual void Open();
void Execute(const String& f);
typedef FileDiff CLASSNAME;
String GetExtPath() const { return ~r; }
FileDiff(FileSel& fs);
FileSel& fs;
};
struct PatchDiff : FileDiff {
PatchDiff(FileSel& fs) : FileDiff(fs) {}
virtual void Open();
void Copy(FileIn& in, FileIn& oin, int& l, int ln, int n);
void LoadDiff(const char *fn);
};
class DirDiffDlg : public TopWindow {
Splitter files_diff;
ParentCtrl files_pane;
FileList files;
SelectDirButton seldir1;
WithDropChoice<EditString> dir1;
SelectDirButton seldir2;
WithDropChoice<EditString> dir2;
Option hidden;
Button compare;
Label info;
FrameTop<ParentCtrl> left, right;
EditString lfile, rfile;
Button copyleft, copyright;
void GatherFilesDeep(Index<String>& files, const String& base, const String& path);
void Compare();
void ClearFiles();
void File();
void Copy(bool left);
public:
TextDiffCtrl diff;
typedef DirDiffDlg CLASSNAME;
void SetFont(Font fnt) { diff.SetFont(fnt); }
void Dir1(const String& dir) { dir1 <<= dir; }
void Dir2(const String& dir) { dir2 <<= dir; }
void Dir1AddList(const String& dir) { dir1.AddList(dir); }
void Dir2AddList(const String& dir) { dir2.AddList(dir); }
String GetLeftFile() const { return ~lfile; }
String GetRightFile() const { return ~rfile; }
DirDiffDlg();
};
};
#endif
#ifndef _TextDiffCtrl_TextDiffCtrl_h
#define _TextDiffCtrl_TextDiffCtrl_h
#include <CtrlLib/CtrlLib.h>
namespace Upp {
#define IMAGECLASS DiffImg
#define IMAGEFILE <TextDiffCtrl/Diff.iml>
#include <Draw/iml_header.h>
class TextSection
{
public:
TextSection(int start1, int count1, int start2, int count2, bool same)
: start1(start1), count1(count1), start2(start2), count2(count2), same(same) {}
public:
int start1;
int count1;
int start2;
int count2 : 31;
unsigned same : 1;
};
Array<TextSection> CompareLineMaps(const Vector<String>& l1, const Vector<String>& l2);
Vector<String> GetLineMap(Stream& stream);
Vector<String> GetFileLineMap(const String& path);
Vector<String> GetStringLineMap(const String &s);
class TextCompareCtrl : public Ctrl {
public:
virtual void Paint(Draw& draw);
virtual void Layout();
virtual void MouseWheel(Point pt, int zdelta, dword keyflags);
virtual void MouseMove(Point pt, dword keyflags);
virtual void LeftDown(Point pt, dword keyflags);
virtual void LeftDouble(Point pt, dword keyflags);
virtual void LeftUp(Point pt, dword keyflags);
virtual void RightDown(Point p, dword keyflags);
virtual bool Key(dword key, int repcnt);
private:
void SelfScroll();
void PairScroll(TextCompareCtrl *ctrl);
void UpdateWidth();
WString ExpandTabs(const wchar *line) const;
int MeasureLength(const wchar *line) const;
bool GetSelection(int& l, int& h);
void DoSelection(int y, bool shift);
void Copy();
int GetLineNo(int y, int& yy);
private:
struct Line {
Line() : number(Null), level(0) {}
int number;
bool diff;
String text;
int level;
String text_diff;
int number_diff;
bool left;
};
Array<Line> lines;
int maxwidth;
ScrollBars scroll;
Font font;
Font number_font;
Color number_bg;
Color gutter_fg;
Color gutter_bg;
Size letter;
int tabsize;
int number_width;
int number_yshift;
int gutter_width;
int cursor;
int anchor;
bool gutter_capture;
bool show_line_number;
bool show_white_space;
bool show_diff_highlight;
bool change_paper_color;
typedef TextCompareCtrl CLASSNAME;
public:
Event<> WhenScroll;
Callback2<int, int> WhenLeftDouble;
Event<Vector<LineEdit::Highlight>&, const WString&> WhenHighlight;
void SetCount(int c);
void AddCount(int c);
int GetCount() const { return lines.GetCount(); }
void SetFont(Font f, Font nf);
void SetFont(Font f);
Font GetFont() const { return font; }
Font GetNumberFont() const { return number_font; }
void NumberBgColor(Color bg) { number_bg = bg; Refresh(); }
Color GetNumberBgColor() const { return number_bg; }
void AutoHideSb(bool ssb=true){ scroll.AutoHide(ssb); }
void ShowSb(bool ssb) { scroll.ShowY(ssb); }
void HideSb() { ShowSb(false); }
void Gutter(int size) { gutter_width = size; Refresh(); }
void NoGutter() { gutter_width = 0; Refresh(); }
void TabSize(int t);
int GetTabSize() const { return tabsize; }
void Set(int line, String text, bool diff, int number, int level, String text_diff, int number_diff, bool left);
String GetText(int line) const { return lines[line].text; }
bool GetDiff(int line) const { return lines[line].diff; }
int GetNumber(int line) const { return lines[line].number; }
int GetNumberDiff(int line) const { return lines[line].number_diff; }
Point GetPos() const;
void SetPos(Point pos);
int GetSb() const { return scroll.Get().y; }
void SetSb(int y) { scroll.Set(0, y); }
void ClearSelection() { cursor = Null; Refresh(); }
void SetSelection(int l, int h) { cursor = l; anchor = h; }
void ShowLineNumber(bool sln) { show_line_number = sln; Refresh(); }
void HideLineNumber() { ShowLineNumber(false); }
void ShowWhiteSpace(bool sws) { show_white_space = sws; Refresh(); }
void HideWhiteSpace() { ShowWhiteSpace(false); }
void DiffHighlight(bool dh) { show_diff_highlight = dh; Refresh(); }
void NoDiffHighlight() { DiffHighlight(false); }
void ChangePaperColor(bool cpc) { change_paper_color = cpc; Refresh(); }
void NoChangePaperColor() { ChangePaperColor(false); }
Event<> ScrollWhen(TextCompareCtrl& pair) { return THISBACK1(PairScroll, &pair); }
TextCompareCtrl();
};
struct TextDiffCtrl : public Splitter {
TextCompareCtrl left;
TextCompareCtrl right;
typedef TextDiffCtrl CLASSNAME;
void Set(Stream& l, Stream& r);
void Set(const String& l, const String& r);
void InsertFrameLeft(CtrlFrame& f) { left.InsertFrame(0, f); }
void InsertFrameRight(CtrlFrame& f) { right.InsertFrame(0, f); }
void AddFrameLeft(CtrlFrame& f) { left.AddFrame(f); }
void AddFrameRight(CtrlFrame& f) { right.AddFrame(f); }
void SetFont(Font f, Font nf) { left.SetFont(f, nf); right.SetFont(f, nf); }
void SetFont(Font f) { left.SetFont(f); right.SetFont(f); }
void GetLeftLine(int number, int line);
void GetRightLine(int number, int line);
Callback1<int> WhenLeftLine;
Callback1<int> WhenRightLine;
TextDiffCtrl();
};
struct DiffDlg : public TopWindow {
TextDiffCtrl diff;
FrameTop<StaticRect> p;
DataPusher l;
Button write;
String editfile;
String extfile;
typedef DiffDlg CLASSNAME;
void Write();
void Execute(const String& f);
static Event<const String&, Vector<LineEdit::Highlight>&, const WString&> WhenHighlight;
DiffDlg();
};
FileSel& DiffFs();
struct FileDiff : DiffDlg {
FrameTop<DataPusher> r;
virtual void Open();
void Execute(const String& f);
typedef FileDiff CLASSNAME;
String GetExtPath() const { return ~r; }
FileDiff(FileSel& fs);
FileSel& fs;
};
struct PatchDiff : FileDiff {
PatchDiff(FileSel& fs) : FileDiff(fs) {}
virtual void Open();
void Copy(FileIn& in, FileIn& oin, int& l, int ln, int n);
void LoadDiff(const char *fn);
};
class DirDiffDlg : public TopWindow {
Splitter files_diff;
ParentCtrl files_pane;
FileList files;
SelectDirButton seldir1;
WithDropChoice<EditString> dir1;
SelectDirButton seldir2;
WithDropChoice<EditString> dir2;
Option hidden;
Button compare;
Label info;
FrameTop<ParentCtrl> left, right;
EditString lfile, rfile;
Button copyleft, copyright;
void GatherFilesDeep(Index<String>& files, const String& base, const String& path);
void Compare();
void ClearFiles();
void File();
void Copy(bool left);
public:
TextDiffCtrl diff;
typedef DirDiffDlg CLASSNAME;
void SetFont(Font fnt) { diff.SetFont(fnt); }
void Dir1(const String& dir) { dir1 <<= dir; }
void Dir2(const String& dir) { dir2 <<= dir; }
void Dir1AddList(const String& dir) { dir1.AddList(dir); }
void Dir2AddList(const String& dir) { dir2.AddList(dir); }
String GetLeftFile() const { return ~lfile; }
String GetRightFile() const { return ~rfile; }
DirDiffDlg();
};
};
#endif