diff --git a/uppsrc/CtrlLib/FileList.cpp b/uppsrc/CtrlLib/FileList.cpp index bef555eff..9de3e7369 100644 --- a/uppsrc/CtrlLib/FileList.cpp +++ b/uppsrc/CtrlLib/FileList.cpp @@ -298,6 +298,14 @@ void FileList::SetIcon(int ii, const Image& icon) KillEdit(); } +void FileList::Add(const File& f) +{ + Value v; + File& m = CreateRawValue(v); + m = f; + ColumnList::Add(v, !m.isdir); +} + void FileList::Add(const String& name, const Image& icon, Font font, Color ink, bool isdir, int64 length, Time time, Color extink, const String& desc, Font descfont, Value data, Color uln, diff --git a/uppsrc/CtrlLib/FileSel.h b/uppsrc/CtrlLib/FileSel.h index 1d49a7495..802a55dfb 100644 --- a/uppsrc/CtrlLib/FileSel.h +++ b/uppsrc/CtrlLib/FileSel.h @@ -95,6 +95,7 @@ public: Color underline = Null, bool unixexe = false, bool hidden = false); void Set(int ii, const File& f); void SetIcon(int ii, const Image& icon); + void Add(const File& f); void Add(const String& name, const Image& icon = Null, Font font = StdFont(), Color ink = SColorText(), bool isdir = false, int64 length = 0, Time time = Null, Color extink = Null, diff --git a/uppsrc/CtrlLib/src.tpp/FileList_en-us.tpp b/uppsrc/CtrlLib/src.tpp/FileList_en-us.tpp index 75da39170..8b98c33a1 100644 --- a/uppsrc/CtrlLib/src.tpp/FileList_en-us.tpp +++ b/uppsrc/CtrlLib/src.tpp/FileList_en-us.tpp @@ -58,6 +58,8 @@ nt]_[*@3 ii], [@(0.0.255) const]_[_^Image^ Image][@(0.0.255) `&]_[*@3 icon])&] [s2;%% Replaces an icon of file item.&] [s3;%% &] [s4; &] +[s5;:Upp`:`:FileList`:`:Add`(const Upp`:`:FileList`:`:File`&`): [@(0.0.255) void]_[* Add]( +[@(0.0.255) const]_[_^Upp`:`:FileList`:`:File^ File][@(0.0.255) `&]_[*@3 f])&] [s5;:FileList`:`:Add`(const String`&`,const Image`&`,Font`,Color`,bool`,int64`,Time`,Color`,const String`&`,Font`,Value`,Color`,bool`,bool`): [@(0.0.255) v oid]_[* Add]([@(0.0.255) const]_[_^String^ String][@(0.0.255) `&]_[*@3 name], [@(0.0.255) const]_[_^Image^ Image][@(0.0.255) `&]_[*@3 icon]_`=_Null, diff --git a/uppsrc/TextDiffCtrl/Diff.iml b/uppsrc/TextDiffCtrl/Diff.iml index 04c5de5cf..cd0326247 100644 --- a/uppsrc/TextDiffCtrl/Diff.iml +++ b/uppsrc/TextDiffCtrl/Diff.iml @@ -1,20 +1,35 @@ PREMULTIPLIED +IMAGE_ID(Patched) +IMAGE_ID(Failed) IMAGE_ID(Diff) +IMAGE_ID(PatchDiff) IMAGE_ID(DirDiff) IMAGE_ID(CopyLeft) IMAGE_ID(CopyRight) IMAGE_BEGIN_DATA -IMAGE_DATA(120,156,237,150,49,110,131,48,20,134,189,194,80,101,233,214,11,192,33,128,165,75,247,158,160,91,183,74,237,108,46,80) -IMAGE_DATA(169,18,7,200,37,220,161,11,102,234,144,83,160,220,160,39,120,237,131,210,24,236,103,7,155,168,12,252,210,19,9,240) -IMAGE_DATA(253,246,115,126,76,216,142,237,216,138,4,246,42,29,197,160,109,91,99,13,252,235,155,52,214,37,120,245,187,202,227,103) -IMAGE_DATA(23,111,242,82,199,159,122,168,188,123,29,79,117,137,241,151,232,95,31,155,94,255,83,54,116,30,207,53,77,67,242,120) -IMAGE_DATA(254,253,227,139,156,63,178,20,63,176,42,31,152,223,255,213,146,155,65,154,166,247,66,8,144,82,66,85,85,199,56,142) -IMAGE_DATA(175,231,240,121,158,115,41,57,212,117,221,121,12,37,196,11,160,183,139,47,138,130,3,60,3,0,27,213,225,192,0,175) -IMAGE_DATA(253,220,130,30,240,123,36,120,174,241,82,254,241,234,143,171,121,244,252,163,141,87,199,215,60,176,199,253,254,14,202,178) -IMAGE_DATA(24,21,231,183,166,254,173,189,156,169,193,99,170,57,193,164,120,109,94,73,146,140,242,17,69,17,149,15,99,111,89,150) -IMAGE_DATA(145,249,64,111,139,71,119,196,124,81,249,192,107,150,121,116,213,243,230,124,16,252,104,30,61,111,206,135,133,31,173,31) -IMAGE_DATA(149,15,67,255,243,181,178,127,6,43,210,213,13,99,79,199,48,214,103,183,87,89,31,254,225,211,253,6,178,189,145,66) -IMAGE_DATA(199,159,122,248,42,100,253,55,121,105,219,12,124,133,65,197,192,250,10,31,148,16,143,225,97,53,121,204,221,12,112,3) -IMAGE_DATA(89,114,252,115,249,144,254,67,215,127,211,84,223,220,219,227,142,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0) -IMAGE_END_DATA(384, 4) +IMAGE_DATA(120,156,237,153,191,139,26,65,20,199,71,81,113,7,35,54,105,194,165,73,167,157,69,224,42,149,64,8,216,91,166,218) +IMAGE_DATA(46,157,144,64,186,221,127,64,8,136,245,245,87,88,153,34,141,107,37,228,254,137,200,65,32,109,14,82,79,252,174,25) +IMAGE_DATA(111,118,118,222,204,254,240,136,133,15,30,187,206,188,207,123,111,222,60,135,93,221,116,88,135,157,133,180,217,85,97,182) +IMAGE_DATA(199,38,123,250,186,48,251,142,205,10,179,83,118,95,40,119,176,33,19,241,181,40,59,97,183,137,241,118,92,133,169,53) +IMAGE_DATA(31,201,66,77,118,114,222,84,19,149,69,28,74,192,194,6,181,145,118,42,235,179,173,115,141,96,165,189,122,15,205,178) +IMAGE_DATA(215,176,81,25,169,121,246,26,185,171,44,242,200,43,88,107,158,188,117,193,30,33,174,190,215,121,4,181,207,208,163,27) +IMAGE_DATA(211,97,208,225,252,87,171,213,18,89,20,182,58,223,108,54,111,223,55,155,66,48,102,85,216,192,86,231,235,245,250,20) +IMAGE_DATA(190,93,60,108,96,171,243,149,74,229,10,115,203,90,141,100,49,7,27,216,154,10,227,121,222,214,182,6,204,193,134,42) +IMAGE_DATA(108,163,209,152,189,228,252,15,197,99,14,54,20,95,173,86,175,145,223,247,106,53,197,98,12,115,176,161,120,200,51,206) +IMAGE_DATA(127,126,110,52,82,60,198,48,103,99,33,216,155,215,158,247,160,243,24,51,237,155,46,181,90,109,34,247,65,85,140,97) +IMAGE_DATA(206,197,203,53,232,61,151,37,247,83,9,250,227,149,231,253,144,177,113,79,245,140,73,94,112,254,240,198,243,142,107,199) +IMAGE_DATA(61,198,178,242,136,169,238,33,238,49,150,149,151,123,160,42,89,251,179,121,50,56,136,176,107,232,80,38,118,187,157,81) +IMAGE_DATA(37,63,251,18,25,245,41,120,245,179,202,227,222,197,155,124,169,241,117,31,42,239,174,227,163,62,69,252,83,172,63,29) +IMAGE_DATA(155,174,255,99,111,164,121,140,109,54,27,146,199,248,215,111,191,201,252,193,82,188,100,85,190,100,255,254,95,57,229,97) +IMAGE_DATA(208,235,245,38,171,213,74,68,81,36,230,243,249,61,231,252,121,30,126,56,28,6,81,20,136,245,122,29,251,144,186,90) +IMAGE_DATA(125,18,240,237,226,71,163,81,32,196,71,177,63,66,19,122,119,199,4,230,246,38,240,33,254,93,9,62,72,241,81,116) +IMAGE_DATA(228,213,205,77,249,56,240,31,108,188,26,63,229,3,107,188,185,25,139,48,28,37,52,8,222,154,214,111,93,75,70,145) +IMAGE_DATA(62,116,161,26,211,213,240,170,157,41,47,199,97,155,202,75,247,17,219,246,251,125,225,251,190,88,46,151,20,175,251,56) +IMAGE_DATA(94,97,59,30,143,197,98,177,136,213,194,171,62,142,42,227,171,108,24,134,20,159,202,67,198,87,89,75,124,107,253,36) +IMAGE_DATA(155,135,183,234,229,48,208,249,203,97,64,140,81,66,241,169,188,186,221,110,162,63,246,175,89,84,127,24,215,54,24,12) +IMAGE_DATA(200,254,128,111,139,143,248,138,254,162,250,3,115,150,60,98,61,240,230,254,32,248,68,30,7,222,220,31,22,62,81,63) +IMAGE_DATA(170,63,12,235,207,47,103,246,154,112,70,210,222,191,23,78,243,255,96,148,96,139,60,250,169,108,17,222,223,186,31,71) +IMAGE_DATA(109,143,167,101,227,235,62,138,74,153,250,95,164,144,92,14,131,162,130,70,109,23,255,7,36,254,162,148,241,33,191,172) +IMAGE_DATA(38,31,121,15,3,223,253,235,126,174,248,89,249,50,235,47,91,255,139,232,242,23,101,16,240,49,0,0,0,0,0,0) +IMAGE_END_DATA(768, 7) diff --git a/uppsrc/TextDiffCtrl/DirDiff.cpp b/uppsrc/TextDiffCtrl/DirDiff.cpp index 1093891d5..b3f268a75 100644 --- a/uppsrc/TextDiffCtrl/DirDiff.cpp +++ b/uppsrc/TextDiffCtrl/DirDiff.cpp @@ -2,6 +2,98 @@ namespace Upp { +DirDiffDlg::DirDiffDlg() +{ + int div = HorzLayoutZoom(4); + int cy = dir1.GetStdSize().cy; + int bcx = GetTextSize(t_("Compare"), StdFont()).cx * 12 / 10 + 2 * div; + + hidden.SetLabel(t_("Hidden")); + + added.SetColor(Green()).SetLabel(t_("New")); + modified.SetLabel(t_("Modified")); + removed.SetColor(Red()).SetLabel(t_("Removed")); + + compare.SetLabel(t_("Compare")); + int bcy = compare.GetStdSize().cy; + + files_pane.Add(dir1.TopPos(0, cy).HSizePos()); + files_pane.Add(dir2.TopPos(cy + div, cy).HSizePos()); + files_pane.Add(hidden.TopPos(2 * cy + 2 * div, bcy).LeftPos(0, bcx)); + + files_pane.Add( added.TopPos(3 * cy + 3 * div, bcy).LeftPosZ(2, 60)); + files_pane.Add(modified.TopPos(3 * cy + 3 * div, bcy).LeftPosZ(52, 70)); + files_pane.Add( removed.TopPos(3 * cy + 3 * div, bcy).LeftPosZ(128, 80)); + + removed = 1; + added = 1; + modified = 1; + find.NullText(t_("Find (Ctrl+F)")); + clearFind.SetLabel("X"); + clearFind.RightPosZ(1, 16).VSizePosZ(1, 1); + find.AddChild(&clearFind); + + files_pane.Add(info.SetAlign(ALIGN_RIGHT).TopPos(3 * cy + 3 * div, bcy).RightPos(1, Zx(70))); + files_pane.Add(compare.TopPos(2 * cy + 2 * div, bcy).RightPos(0, bcx)); + files_pane.Add(files.VSizePos(3 * cy + bcy + 4 * div, Zy(24)).HSizePos()); + files_pane.Add(find.BottomPosZ(4, 19).HSizePosZ()); + + Add(files_diff.SizePos()); + files_diff.Set(files_pane, diff); + files_diff.SetPos(2000); + files_diff.SetMinPixels(0, Zx(256)); + + Sizeable().Zoomable(); + + seldir1.Attach(dir1); + seldir2.Attach(dir2); + + seldir1.Title("First directory to compare"); + seldir2.Title("Second directory to compare"); + + compare <<= THISBACK(Compare); + dir1 <<= THISBACK(ClearFiles); + dir2 <<= THISBACK(ClearFiles); + + modified << [=] { ShowResult(); }; + removed << [=] { ShowResult(); }; + added << [=] { ShowResult(); }; + find << [=] { ShowResult(); }; + clearFind << [=] { find.Clear(); ShowResult();}; + + files.WhenSel = THISBACK(File); + + diff.InsertFrameLeft(left); + diff.InsertFrameRight(right); + + bcx = GetTextSize(t_("Copy"), StdFont()).cx + HorzLayoutZoom(40); + + left.Height(EditField::GetStdHeight()); + lfile.SetReadOnly(); + left.Add(lfile.VSizePos().HSizePos(0, bcx)); + left.Add(copyright.VSizePos().RightPos(0, bcx)); + copyright.SetImage(DiffImg::CopyRight()); + copyright.SetLabel("Copy"); + copyright <<= THISBACK1(Copy, false); + + right.Height(EditField::GetStdHeight()); + rfile.SetReadOnly(); + right.Add(rfile.VSizePos().HSizePos(bcx, 0)); + right.Add(copyleft.VSizePos().LeftPos(0, bcx)); + copyleft.SetImage(DiffImg::CopyLeft()); + copyleft.SetLabel("Copy"); + copyleft <<= THISBACK1(Copy, true); + + copyleft.Disable(); + copyright.Disable(); + + Icon(DiffImg::DirDiff()); + + WhenIcon = [](const char *path) -> Image { return NativePathIcon(path); }; + + Title("Compare directories"); +}; + void DirDiffDlg::GatherFilesDeep(Index& files, const String& base, const String& path) { FindFile ff(AppendFileName(AppendFileName(base, path), "*.*")); @@ -18,7 +110,7 @@ void DirDiffDlg::GatherFilesDeep(Index& files, const String& base, const } } -bool FileEqual(const String& f1, const String& f2, int& n) +bool DirDiffDlg::FileEqual(const String& f1, const String& f2, int& n) { FileIn in1(f1); FileIn in2(f2); @@ -33,7 +125,7 @@ bool FileEqual(const String& f1, const String& f2, int& n) } else { - n = (in1 ? 1 : 2); + n = (in1 ? DELETED_FILE : NEW_FILE); } return false; @@ -60,7 +152,7 @@ void DirDiffDlg::Compare() break; String p1 = AppendFileName(~dir1, f[i]); String p2 = AppendFileName(~dir2, f[i]); - int n = 0; + int n = NORMAL_FILE; if(!FileEqual(p1, p2, n)) list.Add(MakeTuple(f[i], p1, p2, n)); } @@ -68,18 +160,42 @@ void DirDiffDlg::Compare() ShowResult(); } +FileList::File DirDiffDlg::MakeFile(int i) +{ + static Color cs[] = { SColorText(), SRed(), SGreen(), SRed(), SLtBlue() }; + FileList::File m; + m.isdir = false; + m.unixexe = false; + m.hidden = false; + Image icn = WhenIcon(FileExists(list[i].b) ? list[i].b : list[i].c); + int k = list[i].d; + if(IsNull(icn)) + icn = CtrlImg::File(); + m.icon = decode(k, FAILED_FILE, MakeImage(icn, [] (const Image& m) { return GetOver(m, DiffImg::Failed()); }), + PATCHED_FILE, MakeImage(icn, [] (const Image& m) { return GetOver(m, DiffImg::Patched()); }), + icn); + m.name = list[i].a; + m.font = decode(k, FAILED_FILE, StdFont().Strikeout().Italic(), + PATCHED_FILE, StdFont().Italic(), StdFont()); + m.ink = cs[k]; + m.length = 0; + m.time = Null; + m.extink = m.ink; + m.data = i; + return m; +} + void DirDiffDlg::ShowResult() { - static Color cs[] = { SColorText(), Red(), Green() }; files.Clear(); String sFind = ToLower((String)~find); for(int i = 0; i < list.GetCount(); i++) { int n = list[i].d; - if(n == 0 && modified || n == 1 && removed || n == 2 && added) - if(ToLower(list[i].a).Find(sFind) >= 0) - files.Add(list[i].a, NativePathIcon(FileExists(list[i].b) ? list[i].b : list[i].c), - StdFont(), cs[list[i].d]); + if((n == NORMAL_FILE && modified || n == DELETED_FILE && removed + || n == NEW_FILE && added || n == FAILED_FILE || n == PATCHED_FILE) + && ToLower(list[i].a).Find(sFind) >= 0) + files.Add(MakeFile(i)); } info = AsString(files.GetCount()) + " files"; clearFind.Show(!IsNull(find)); @@ -129,88 +245,4 @@ bool Upp::DirDiffDlg::HotKey(dword key) return false; } -DirDiffDlg::DirDiffDlg() -{ - int div = HorzLayoutZoom(4); - int cy = dir1.GetStdSize().cy; - int bcx = GetTextSize(t_("Compare"), StdFont()).cx * 12 / 10 + 2 * div; - - hidden.SetLabel(t_("Hidden")); - - added.SetColor(Green()).SetLabel(t_("New")); - modified.SetLabel(t_("Modified")); - removed.SetColor(Red()).SetLabel(t_("Removed")); - - compare.SetLabel(t_("Compare")); - int bcy = compare.GetStdSize().cy; - - files_pane.Add(dir1.TopPos(0, cy).HSizePos()); - files_pane.Add(dir2.TopPos(cy + div, cy).HSizePos()); - files_pane.Add(hidden.TopPos(2 * cy + 2 * div, bcy).LeftPos(0, bcx)); - - files_pane.Add( added.TopPos(3 * cy + 3 * div, bcy).LeftPosZ(2, 60)); - files_pane.Add(modified.TopPos(3 * cy + 3 * div, bcy).LeftPosZ(52, 70)); - files_pane.Add( removed.TopPos(3 * cy + 3 * div, bcy).LeftPosZ(128, 80)); - - removed = 1; - added = 1; - modified = 1; - find.NullText(t_("Find (Ctrl+F)")); - clearFind.SetLabel("X"); - clearFind.RightPosZ(1, 16).VSizePosZ(1, 1); - find.AddChild(&clearFind); - - files_pane.Add(info.SetAlign(ALIGN_RIGHT).TopPos(3 * cy + 3 * div, bcy).RightPos(1, Zx(70))); - files_pane.Add(compare.TopPos(2 * cy + 2 * div, bcy).RightPos(0, bcx)); - files_pane.Add(files.VSizePos(3 * cy + bcy + 4 * div, Zy(24)).HSizePos()); - files_pane.Add(find.BottomPosZ(4, 19).HSizePosZ()); - - Add(files_diff.SizePos()); - files_diff.Set(files_pane, diff); - files_diff.SetPos(2000); - - Sizeable().Zoomable(); - - seldir1.Attach(dir1); - seldir2.Attach(dir2); - - compare <<= THISBACK(Compare); - dir1 <<= THISBACK(ClearFiles); - dir2 <<= THISBACK(ClearFiles); - - modified << [=] { ShowResult(); }; - removed << [=] { ShowResult(); }; - added << [=] { ShowResult(); }; - find << [=] { ShowResult(); }; - clearFind << [=] { find.Clear(); ShowResult();}; - - files.WhenSel = THISBACK(File); - - diff.InsertFrameLeft(left); - diff.InsertFrameRight(right); - - bcx = GetTextSize(t_("Copy"), StdFont()).cx + HorzLayoutZoom(40); - - left.Height(EditField::GetStdHeight()); - lfile.SetReadOnly(); - left.Add(lfile.VSizePos().HSizePos(0, bcx)); - left.Add(copyright.VSizePos().RightPos(0, bcx)); - copyright.SetImage(DiffImg::CopyRight()); - copyright.SetLabel("Copy"); - copyright <<= THISBACK1(Copy, false); - - right.Height(EditField::GetStdHeight()); - rfile.SetReadOnly(); - right.Add(rfile.VSizePos().HSizePos(bcx, 0)); - right.Add(copyleft.VSizePos().LeftPos(0, bcx)); - copyleft.SetImage(DiffImg::CopyLeft()); - copyleft.SetLabel("Copy"); - copyleft <<= THISBACK1(Copy, true); - - copyleft.Disable(); - copyright.Disable(); - - Icon(DiffImg::DirDiff()); -} - -}; +}; \ No newline at end of file diff --git a/uppsrc/TextDiffCtrl/PatchDiff.cpp b/uppsrc/TextDiffCtrl/PatchDiff.cpp index 709c40dba..0b097548d 100644 --- a/uppsrc/TextDiffCtrl/PatchDiff.cpp +++ b/uppsrc/TextDiffCtrl/PatchDiff.cpp @@ -2,101 +2,168 @@ namespace Upp { -void PatchDiff::Copy(FileIn& in, FileIn& oin, int& l, int ln, int n) +PatchDiff::PatchDiff() { - if(ln < l) - throw CParser::Error(""); - while(l < ln) { - if(oin.IsEof()) - throw CParser::Error(""); - extfile << oin.GetLine() << "\r\n"; - l++; - } - l += n; - while(n--) - oin.GetLine(); + Icon(DiffImg::PatchDiff()); + + hidden.Hide(); + + copyright.Hide(); + lfile.HSizePos(); + + copyleft.SetLabel("Patch"); + + files.WhenSel = [=] { File(); }; + + dir1.Ctrl::Remove(); + dir2.Ctrl::Remove(); + + int cy = patch_file.GetStdSize().cy; + int div = Zy(4); + files_pane.Add(target_dir.TopPos(0, cy).HSizePos()); + files_pane.Add(patch_file.TopPos(cy + div, cy).HSizePos()); + patch_file.SetReadOnly(); + target_dir.SetReadOnly(); + + seldir.Attach(target_dir); + seldir.Title("Target directory"); + selfile.Attach(patch_file); + selfile.Title("Patch file"); + selfile.Types("Patch files (*.diff *.patch)\t*.diff *.patch\nAll files\t*.*"); + + seldir.WhenSelected = selfile.WhenSelected = [=] { + Open(~~patch_file, Vector() << ~seldir); + }; + + files_pane.Add(failed.TopPos(2 * cy + 2 * div, cy).HSizePos()); + + compare.SetLabel("Patch All"); + compare ^= [=] { + String msg = "Patch everything?"; + if(failed_count) + msg << "&[/ (" << failed_count << " files cannot be patched)"; + if(files.GetCount() == 0 || !PromptYesNo(msg)) + return; + Progress pi("Patching", patch.GetCount()); + for(int i = 0; i < patch.GetCount(); i++) { + if(pi.StepCanceled()) + return; + String h = patch.GetPatchedFile(i); + String p = patch.GetPath(i); + if(!h.IsVoid()) + if(IsNull(h)) + FileDelete(p); + else + SaveFile(p, h); + } + Break(IDOK); + }; + + copyleft ^= [=] { + int ii = GetFileIndex(); + if(ii < 0 || !PromptYesNo("Patch [* \1" + file_path + "\1] ?") || patched_file.IsVoid()) + return; + if(IsNull(patched_file)) + FileDelete(file_path); + else + SaveFile(file_path, patched_file); + list[ii].d = 4; + files.Set(files.GetCursor(), MakeFile(ii)); + File(); + }; + + Title("Patch"); } -void PatchDiff::LoadDiff(const char *fn) +bool PatchDiff::Open(const char *patch_path, const Vector& target_dirs) { - try { - FileIn in(fn); - FileIn oin(editfile); - extfile.Clear(); - int l = 1; - String s = in.GetLine(); - if(IsDigit(*s)) { - in.Seek(0); - while(!in.IsEof()) { - s = in.GetLine(); - if(IsDigit(*s)) { - CParser p(s); - int ln = p.ReadNumber(); - int n = 0; - if(p.Char('a')) - ln++; - else { - n = p.Char(',') ? p.ReadNumber() - ln + 1 : 1; - if(!p.Char('c')) - p.PassChar('d'); - } - Copy(in, oin, l, ln, n); - } - else - if(*s == '>') { - if(s[1] != ' ') - throw CParser::Error(""); - extfile << s.Mid(2) << "\r\n"; - } - else - if(*s != '<' && *s != '-') - throw CParser::Error(""); - } - } - else { - for(;;) { - if(in.IsEof()) - throw CParser::Error(""); - if(in.GetLine().Mid(0, 4) == "+++ ") - break; - } - while(!in.IsEof()) { - String s = in.GetLine(); - if(*s == '@') { - CParser p(s); - p.PassChar2('@', '@'); - p.PassChar('-'); - int ln = p.ReadNumber(); - int n = 1; - if(p.Char(',')) - n = p.ReadNumber(); - Copy(in, oin, l, ln, n); - } - else - if(*s == '+' || *s == ' ') - extfile << s.Mid(1) << "\r\n"; - else - if(*s != '-') - throw CParser::Error(""); - } - } - while(!oin.IsEof()) - extfile << oin.GetLine() << "\r\n"; - } - catch(CParser::Error&) { - Exclamation("Invalid file format!"); - extfile = LoadFile(fn); + failed_count = 0; + list.Clear(); + failed.Hide(); + ShowResult(); + patch_file <<= Null; + target_dir <<= Null; + + Progress pi; + if(!patch.Load(patch_path, pi)) + return false; + + if(!patch.MatchFiles(target_dirs, pi)) + return false; + + String target_dir = patch.GetTargetDir(); + + patch_file <<= patch_path; + this->target_dir <<= target_dir; + + list.Clear(); + pi.SetText("Checking files"); + pi.SetTotal(patch.GetCount()); + for(int i = 0; i < patch.GetCount(); i++) { + if(pi.StepCanceled()) + return false; + String fn = patch.GetFile(i); + String p = patch.GetPath(i); + String h = patch.GetPatchedFile(i); + bool pe = h.GetCount(); + bool x = FileExists(p); + bool failed = h.IsVoid(); + list.Add(MakeTuple(fn, p, p, failed ? 3 : pe && x ? 0 : pe ? 2 : 1)); + if(failed) + failed_count++; } + + failed.Show(failed_count); + failed.SetInk(SRed()).SetFont(StdFont().Bold().Italic()); + failed = String() << failed_count << " file(s) cannot be patched"; + + ShowResult(); + + if(files.GetCount()) + files.SetCursor(0); + + return true; } -void PatchDiff::Open() +int PatchDiff::GetFileIndex() const { - if(!DiffFs().ExecuteOpen()) - return; - String f = ~DiffFs(); - r <<= f; - LoadDiff(f); - diff.Set(LoadFile(editfile), extfile); + int ii = files.GetCursor(); + return ii >= 0 ? files.Get(ii).data : -1; +} + +void PatchDiff::File() +{ + diff.Set(Null, Null); + String p2; + String op = " [PATCHED]"; + copyleft.Disable(); + file_path.Clear(); + patched_file.Clear(); + int ii = GetFileIndex(); + if(ii >= 0) { + file_path = patch.GetPath(ii); + if(GetFileLength(file_path) < 4 * 1024 * 1024) { + String content = LoadFile(file_path); + if(list[ii].d == PATCHED_FILE) { + p2 = "[FILE IS PATCHED]"; + diff.Set(content, content); + } + else { + patched_file = patch.GetPatchedFile(ii); + if(patched_file.IsVoid()) { + diff.Set(content, patch.GetPatch(ii)); + p2 = "[FAILED TO APPLY THE PATCH]"; + } + else { + diff.Set(content, patched_file); + p2 = file_path + " [PATCHED]"; + copyleft.Enable(); + } + } + } + } + lfile <<= file_path; + rfile <<= p2; } }; \ No newline at end of file diff --git a/uppsrc/TextDiffCtrl/TextCtrl.cpp b/uppsrc/TextDiffCtrl/TextCtrl.cpp index d1e3c86f2..5a4100540 100644 --- a/uppsrc/TextDiffCtrl/TextCtrl.cpp +++ b/uppsrc/TextDiffCtrl/TextCtrl.cpp @@ -19,7 +19,7 @@ TextCompareCtrl::TextCompareCtrl() maxwidth = 0; tabsize = 4; gutter_width = 0; - gutter_bg = Color(151, 190, 239); + gutter_bg = AdjustIfDark(Color(151, 190, 239)); gutter_fg = SGreen; cursor = anchor = Null; gutter_capture = false; @@ -264,7 +264,7 @@ void TextCompareCtrl::Paint(Draw& draw) draw.DrawRect(gx + gutter_width - 2, ty, 2, by - ty, Black); } - Color diffpaper = Blend(LtRed(), White(), 192); + Color diffpaper = SYellow; int n_width = show_line_number ? number_width : 0; if(show_line_number) { @@ -272,7 +272,7 @@ void TextCompareCtrl::Paint(Draw& draw) const Line& l = lines[i]; int y = i * letter.cy - offset.cy; Color paper = IsNull(l.number) ? LtGray() : l.diff ? diffpaper : SColorPaper(); - Color ink = l.diff ? Red(): Gray(); + Color ink = l.diff ? SRed(): SGray(); draw.DrawRect(0, y, n_width, letter.cy, paper); draw.DrawRect(n_width - 1, y, 1, letter.cy, Gray()); if(!IsNull(l.number)) diff --git a/uppsrc/TextDiffCtrl/TextDiffCtrl.h b/uppsrc/TextDiffCtrl/TextDiffCtrl.h index ff37be3d8..578ddb172 100644 --- a/uppsrc/TextDiffCtrl/TextDiffCtrl.h +++ b/uppsrc/TextDiffCtrl/TextDiffCtrl.h @@ -209,15 +209,6 @@ struct FileDiff : DiffDlg { 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 { public: virtual bool HotKey(dword key); @@ -243,16 +234,25 @@ private: EditString lfile, rfile; Button copyleft, copyright; + enum { NORMAL_FILE, DELETED_FILE, NEW_FILE, FAILED_FILE, PATCHED_FILE }; + Array> list; + static bool FileEqual(const String& f1, const String& f2, int& n); + void GatherFilesDeep(Index& files, const String& base, const String& path); void Compare(); void ShowResult(); void ClearFiles(); void File(); void Copy(bool left); + FileList::File MakeFile(int i); + + friend class PatchDiff; public: + Function WhenIcon; + TextDiffCtrl diff; typedef DirDiffDlg CLASSNAME; @@ -269,6 +269,57 @@ public: DirDiffDlg(); }; +struct Patch { + struct Chunk { + int line; + Vector orig; + Vector patch; + String src; + }; + + VectorMap> file; + String common_path; + String target_dir; + + static int MatchLen(const String& a, const String& b); + int MatchCount(const char *dir); + +public: + bool Load(Stream& in, Progress& pi); + bool Load(const char *fn, Progress& pi); + bool MatchFiles(const Vector& dir, Progress& pi); + void SetTargetDir(const char *dir) { target_dir = AppendFileName(dir, common_path); } + + int GetCount() const { return file.GetCount(); } + String GetTargetDir() const { return target_dir; } + String GetFile(int i) const { return file.GetKey(i); } + String GetPath(int i) const { return AppendFileName(target_dir, file.GetKey(i)); } + String GetPatch(int i) const; + String GetPatchedFile(int i) const; +}; + +class PatchDiff : public DirDiffDlg { + Patch patch; + String file_path; + String patched_file; + + EditString patch_file; + EditString target_dir; + Label failed; + + OpenFileButton selfile; + SelectDirButton seldir; + + int failed_count = 0; + + void File(); + int GetFileIndex() const; + +public: + bool Open(const char *patch_path, const Vector& target_dirs); + + PatchDiff(); +}; }; diff --git a/uppsrc/TextDiffCtrl/TextDiffCtrl.upp b/uppsrc/TextDiffCtrl/TextDiffCtrl.upp index 68d67fd1f..d2806d314 100644 --- a/uppsrc/TextDiffCtrl/TextDiffCtrl.upp +++ b/uppsrc/TextDiffCtrl/TextDiffCtrl.upp @@ -6,7 +6,8 @@ file TextDiff.cpp, TextCtrl.cpp, DiffCtrl.cpp, - PatchDiff.cpp, DirDiff.cpp, + patch.cpp, + PatchDiff.cpp, Diff.iml; diff --git a/uppsrc/TextDiffCtrl/patch.cpp b/uppsrc/TextDiffCtrl/patch.cpp new file mode 100644 index 000000000..862c45363 --- /dev/null +++ b/uppsrc/TextDiffCtrl/patch.cpp @@ -0,0 +1,195 @@ +#include "TextDiffCtrl.h" + +namespace Upp { + +int Patch::MatchLen(const String& a, const String& b) +{ + int n = min(a.GetLength(), b.GetLength()); + for(int i = 0; i < n; i++) + if(a[i] != b[i]) + return i; + return n; +} + +int Patch::MatchCount(const char *dir) +{ + int count = 0; + for(const String& p : file.GetKeys()) + if(FileExists(AppendFileName(dir, p))) + count++; + return count; +} + +bool Patch::Load(Stream& in, Progress& pi) +{ + pi.SetText("Loading patch file"); + target_dir.Clear(); + file.Clear(); + String ln0; + String ln = in.GetLine(); + for(;;) { + while(!ln.TrimStart("--- ")) { + if(pi.StepCanceled()) + return false; + if(in.IsEof()) + return true; + ln = in.GetLine(); + } + ln = TrimLeft(ln); + int q = ln.Find('\t'); + if(q >= 0) + ln.Trim(q); + String fn = UnixPath(ln); + Array& fp = file.GetAdd(fn); + while(!ln.StartsWith("@@")) { + if(pi.StepCanceled()) + return false; + if(in.IsEof()) + return true; + ln0 = ln = in.GetLine(); + } + while(ln.TrimStart("@@")) { + if(pi.StepCanceled()) + return false; + int line = atoi(ln); + if(line <= 0) { + Chunk& ch = fp.Add(); + ch.src << ln0 << "\n"; + ch.line = max(-line - 1, 0); + for(;;) { + if(pi.StepCanceled()) + return false; + if(in.IsEof()) + return true; + ln0 = ln = in.GetLine(); + if(*ln == ' ') { + ch.orig.Add(ln.Mid(1)); + ch.patch.Add(ln.Mid(1)); + } + else + if(*ln == '+') + ch.patch.Add(ln.Mid(1)); + else + if(*ln == '-') + ch.orig.Add(ln.Mid(1)); + else + break; + ch.src << ln << "\n"; + } + } + } + } + return true; +} + +bool Patch::MatchFiles(const Vector& dir, Progress& pi) +{ + const Vector& h = file.GetKeys(); + if(h.GetCount() == 0) + return false; + common_path = h[0]; + for(int i = 1; i < h.GetCount(); i++) + common_path.Trim(MatchLen(common_path, h[i])); + common_path.TrimEnd("/"); + common_path.TrimStart("/"); + for(int i = 0; i < h.GetCount(); i++) + file.SetKey(i, h[i].Mid(common_path.GetCount() + 1)); + + pi.SetText("Matching directories"); + int best = 0; + String com_path = common_path; + while(com_path.GetCount()) { + for(String d : dir) { + while(d.GetCount() > 3) { + if(pi.StepCanceled()) + return false; + String dir = AppendFileName(d, com_path); + int n = MatchCount(dir); + if(n > best) { + best = n; + target_dir = dir; + } + d = GetFileFolder(d); + } + } + int q = com_path.Find('/'); + if(q >= 0) + com_path = com_path.Mid(q + 1); + else + break; + } + return best; +} + +bool Patch::Load(const char *fn, Progress& pi) +{ + FileIn in(fn); + return Load(in, pi); +} + +String Patch::GetPatch(int i) const +{ + String r; + for(const Chunk& ch : file[i]) + r << ch.src; + return r; +} + +String Patch::GetPatchedFile(int i) const +{ + if(GetFileLength(GetPath(i)) > 10000000) + return String::GetVoid(); + Vector lines; + bool crlf = true; + String path = GetPath(i); + if(FileExists(path)) { + String s = LoadFile(GetPath(i)); + bool crlf = s.Find('\r') >= 0; + lines = Split(Filter(s, [](int c) { return c == '\r' ? 0 : c; }), '\n', false); + } + + const Array& chs = file[i]; + Vector ch_pos; + + for(int i = 0; i < chs.GetCount(); i++) { + const Chunk& ch = chs[i]; + int pos = -1; + for(int d = 0; d < 200; d++) { // 200 lines of tolerance in both directions when matching the patch + auto Match = [&](int li) { + if(li < 0) + return false; + for(int i = 0; i < ch.orig.GetCount(); i++) { + if(li + i < lines.GetCount()) DDUMP(lines[li + i]); + if(li + i >= lines.GetCount() || ch.orig[i] != lines[li + i]) + return false; + } + return true; + }; + if(Match(ch.line + d)) { + pos = ch.line + d; + break; + } + if(d && Match(ch.line - d)) { + pos = ch.line - d; + break; + } + } + + if(pos < 0 || ch_pos.GetCount() && ch_pos.Top() > pos) + return String::GetVoid(); + ch_pos.Add(pos); + } + + for(int i = 0; i < ch_pos.GetCount(); i++) { + const Chunk& ch = chs[i]; + lines.Remove(ch_pos[i], ch.orig.GetCount()); + lines.Insert(ch_pos[i], ch.patch); + int added = ch.patch.GetCount() - ch.orig.GetCount(); // negative if removed + for(int j = i + 1; j < ch_pos.GetCount(); j++) + ch_pos[j] += added; + } + + return Join(lines, crlf ? "\r\n" : "\n"); +} + +}; diff --git a/uppsrc/ide/ide.cpp b/uppsrc/ide/ide.cpp index 9d06aa86c..ec9021c23 100644 --- a/uppsrc/ide/ide.cpp +++ b/uppsrc/ide/ide.cpp @@ -738,14 +738,6 @@ void Ide::DiffFiles(const char *lname, const String& l, const char *rname, const diff.Execute(); } -void Ide::Patch() -{ - if(IsNull(editfile)) - return; - PatchDiff diffdlg(AnySourceFs()); - diffdlg.Execute(editfile); -} - void Ide::SvnHistory() { if(IsNull(editfile)) diff --git a/uppsrc/ide/ide.h b/uppsrc/ide/ide.h index fba366c6f..fc042ded7 100644 --- a/uppsrc/ide/ide.h +++ b/uppsrc/ide/ide.h @@ -787,7 +787,6 @@ public: String LoadConflictFile(const String& n); void GotoDiffLeft(int line, DiffDlg *df); void GotoDiffRight(int line, FileDiff *df); - void Patch(); void SvnHistory(); void Edit(Bar& menu); @@ -947,6 +946,7 @@ public: void GotoDirDiffLeft(int line, DirDiffDlg *df); void GotoDirDiffRight(int line, DirDiffDlg *df); void DoDirDiff(); + void DoPatchDiff(); void AsErrors(); void RemoveDs(); diff --git a/uppsrc/ide/ide.key b/uppsrc/ide/ide.key index 7b884badf..a991c6a55 100644 --- a/uppsrc/ide/ide.key +++ b/uppsrc/ide/ide.key @@ -119,7 +119,7 @@ KEY(REFORMAT_COMMENT, "Reformat comment paragraph", K_ALT_R) KEY(DIFF, "Compare with file..", 0) KEY(SVNDIFF, "Show repository history of file..", 0) -KEY(PATCH, "Patch/diff..", 0) +KEY(PATCH, "Display/apply patch..", 0) KEY(DIRDIFF, "Compare directories..", 0) KEY(RESCANCURRENTFILE, "Rescan current file", 0) diff --git a/uppsrc/ide/idebar.cpp b/uppsrc/ide/idebar.cpp index 4d2bbf9a7..3de513492 100644 --- a/uppsrc/ide/idebar.cpp +++ b/uppsrc/ide/idebar.cpp @@ -489,8 +489,6 @@ void Ide::FilePropertiesMenu(Bar& menu) .Help("Convert actual file to different encoding"); menu.AddMenu(IsActiveFile() && !editfile_isfolder && !designer, AK_DIFF, IdeImg::Diff(), THISBACK(Diff)) .Help("Show differences between the project and arbitrary files"); - menu.AddMenu(IsActiveFile() && !editfile_isfolder && !designer, AK_PATCH, IdeImg::Patch(), THISBACK(Patch)) - .Help("Show differences with patch file applied"); if(editfile_repo) { String txt = String("Show ") + (editfile_repo == SVN_DIR ? "svn" : "git") + " history of file"; menu.AddMenu(IsActiveFile() && !editfile_isfolder && !designer, AK_SVNDIFF, IdeImg::SvnDiff(), THISBACK(SvnHistory)) @@ -770,6 +768,7 @@ void Ide::BrowseMenu(Bar& menu) menu.AddMenu(!designer, AK_JSON, IdeCommonImg::json(), THISBACK(Json)); menu.AddMenu(!designer, AK_ASERRORS, IdeImg::errors(), THISBACK(AsErrors)); menu.AddMenu(AK_DIRDIFF, DiffImg::DirDiff(), THISBACK(DoDirDiff)); + menu.AddMenu(AK_PATCH, DiffImg::PatchDiff(), THISBACK(DoPatchDiff)); } } diff --git a/uppsrc/ide/idetool.cpp b/uppsrc/ide/idetool.cpp index 42ebe7ac3..a01cd7053 100644 --- a/uppsrc/ide/idetool.cpp +++ b/uppsrc/ide/idetool.cpp @@ -507,6 +507,30 @@ void Ide::DoDirDiff() dlg.SetFocus(); } +void Ide::DoPatchDiff() +{ + String patch = SelectFileOpen("Patch files (*.diff *.patch)\t*.diff *.patch\nAll files\t*.*"); + if(IsNull(patch)) + return; + Index dir; + if(editfile.GetCount()) + dir.Add(GetFileFolder(editfile)); + Vector d = GetUppDirs(); + for(int i = 0; i < d.GetCount(); i++) + dir.FindAdd(d[i]); + static PatchDiff dlg; + dlg.diff.WhenLeftLine = THISBACK1(GotoDirDiffLeft, &dlg); + if(!dlg.IsOpen()) { + dlg.SetFont(veditorfont); + dlg.Maximize(); + dlg.Title("Compare directories"); + dlg.Open(patch, dir.GetKeys()); + dlg.OpenMain(); + } + else + dlg.SetFocus(); +} + void Ide::AsErrors() { ClearErrorsPane();