1 #include "CompDir.h"
2 #pragma hdrstop
3
4 #define LAYOUTFILE <CompDir/CompDir.lay>
5 #include <CtrlCore/lay.h>
6
7 #define IMAGEFILE <CompDir/CompDir.iml>
8 #define IMAGECLASS CompDirImg
9 #include <Draw/iml.h>
10
NormalizePathCase(String fn)11 String NormalizePathCase(String fn)
12 {
13 #ifdef PLATFORM_WIN32 // !PATH_CASE
14 return ToLower(fn);
15 #else
16 return fn;
17 #endif
18 }
19
ExpandTabs(String line,int tabsize=4)20 static String ExpandTabs(String line, int tabsize = 4)
21 {
22 String out;
23 int pos = 0;
24 for(const char *p = line; *p; p++)
25 if(*p == '\t') {
26 int left = tabsize - pos % tabsize;
27 out.Cat(' ', left);
28 pos += left;
29 }
30 else {
31 out.Cat(*p);
32 pos++;
33 }
34 return out;
35 }
36
37 class DlgCompareDir : public WithCompareDirLayout<TopWindow> {
38 public:
39 typedef DlgCompareDir CLASSNAME;
40 DlgCompareDir();
41
42 void Run();
43
44 void Serialize(Stream& stream);
45
46 private:
47 void CmdRefresh();
48 void DoTreeCursor();
49 int Refresh(String rel_path, int parent);
50 void DoBrowse(Ctrl *field);
51 void ToolTree(Bar& bar);
52 String GetTreePath() const;
53
54 private:
55 struct FileInfo : Moveable<FileInfo>
56 {
FileInfoDlgCompareDir::FileInfo57 FileInfo() {}
FileInfoDlgCompareDir::FileInfo58 FileInfo(String name, int64 size, Time time) : name(name), size(size), time(time) {}
59
60 String name;
61 int64 size;
62 Time time;
63 };
64
65 bool FetchDir(String dir, VectorMap<String, FileInfo>& files, VectorMap<String, String>& dirs);
66
67 FrameRight<Button> browse_a, browse_b;
68 TreeCtrl tree;
69 StaticRect editor;
70 LineEdit lineedit;
71 RichTextCtrl qtf;
72 String pa, pb, fm;
73 };
74
DlgCompareDir()75 DlgCompareDir::DlgCompareDir()
76 {
77 CtrlLayout(*this, "Compare directories");
78 Sizeable().Zoomable();
79 refresh <<= THISBACK(CmdRefresh);
80 splitter.Vert(tree, editor);
81 editor << lineedit.SizePos() << qtf.SizePos();
82 qtf.Background(White());
83 qtf.SetFrame(InsetFrame());
84 path_a.AddFrame(browse_a);
85 browse_a.SetImage(CtrlImg::right_arrow());
86 browse_a <<= THISBACK1(DoBrowse, &path_a);
87 path_b.AddFrame(browse_b);
88 browse_b.SetImage(CtrlImg::right_arrow());
89 browse_b <<= THISBACK1(DoBrowse, &path_b);
90 file_mask <<= "*.cpp *.h *.hpp *.c *.C *.cxx *.cc *.lay *.iml *.upp *.sch *.dph";
91 tree.WhenCursor = THISBACK(DoTreeCursor);
92 lineedit.SetReadOnly();
93 lineedit.SetFont(Courier(14));
94 }
95
Run()96 void DlgCompareDir::Run()
97 {
98 TopWindow::Run();
99 }
100
Serialize(Stream & stream)101 void DlgCompareDir::Serialize(Stream& stream)
102 {
103 int version = 1;
104 stream / version;
105 stream % path_a % path_b % file_mask;
106 SerializePlacement(stream);
107 stream % splitter;
108 }
109
CmdRefresh()110 void DlgCompareDir::CmdRefresh()
111 {
112 pa = ~path_a;
113 pb = ~path_b;
114 fm = ~file_mask;
115 tree.Clear();
116 Image icon;
117 switch(Refresh(Null, 0)) {
118 case 0: icon = CtrlImg::Dir(); break;
119 case 1: icon = CompDirImg::a_dir(); break;
120 case 2: icon = CompDirImg::b_dir(); break;
121 case 3: icon = CompDirImg::ab_dir(); break;
122 }
123 tree.SetRoot(icon, "Root");
124 }
125
FetchDir(String dir,VectorMap<String,FileInfo> & files,VectorMap<String,String> & dirs)126 bool DlgCompareDir::FetchDir(String dir, VectorMap<String, FileInfo>& files, VectorMap<String, String>& dirs)
127 {
128 FindFile ff;
129 if(!ff.Search(AppendFileName(dir, "*")))
130 return false;
131 do
132 if(ff.IsFile() && PatternMatchMulti(fm, ff.GetName()))
133 files.Add(NormalizePathCase(ff.GetName()), FileInfo(ff.GetName(), ff.GetLength(), ff.GetLastWriteTime()));
134 else if(ff.IsFolder())
135 dirs.Add(NormalizePathCase(ff.GetName()), ff.GetName());
136 while(ff.Next());
137 return true;
138 }
139
Refresh(String rel_path,int parent)140 int DlgCompareDir::Refresh(String rel_path, int parent)
141 {
142 FindFile ff;
143 VectorMap<String, FileInfo> afile, bfile;
144 VectorMap<String, String> adir, bdir;
145 String arel = AppendFileName(pa, rel_path);
146 String brel = AppendFileName(pb, rel_path);
147 int done = 0;
148 if(!FetchDir(arel, afile, adir))
149 done |= 2;
150 if(!FetchDir(brel, bfile, bdir))
151 done |= 1;
152
153 Index<String> dir_index;
154 dir_index <<= adir.GetIndex();
155 FindAppend(dir_index, bdir.GetKeys());
156 Vector<String> dirs(dir_index.PickKeys());
157 Sort(dirs, GetLanguageInfo());
158 for(int i = 0; i < dirs.GetCount(); i++) {
159 int fa = adir.Find(dirs[i]), fb = bdir.Find(dirs[i]);
160 String dn = (fb >= 0 ? bdir[fb] : adir[fa]);
161 int dirpar = tree.Add(parent, CtrlImg::Dir(), dn);
162 int dirdone = Refresh(AppendFileName(rel_path, dirs[i]), dirpar);
163 done |= dirdone;
164 switch(dirdone) {
165 case 0: tree.Remove(dirpar); break;
166 case 1: tree.SetNode(dirpar, TreeCtrl::Node().SetImage(CompDirImg::a_dir()).Set(dn)); break;
167 case 2: tree.SetNode(dirpar, TreeCtrl::Node().SetImage(CompDirImg::b_dir()).Set(dn)); break;
168 case 3: tree.SetNode(dirpar, TreeCtrl::Node().SetImage(CompDirImg::ab_dir()).Set(dn)); break;
169 }
170 }
171 Index<String> name_index;
172 name_index <<= afile.GetIndex();
173 FindAppend(name_index, bfile.GetKeys());
174 Vector<String> names(name_index.PickKeys());
175 Sort(names, GetLanguageInfo());
176 for(int i = 0; i < names.GetCount(); i++) {
177 int fa = afile.Find(names[i]), fb = bfile.Find(names[i]);
178 if(fa < 0) {
179 tree.Add(parent, CompDirImg::b_file(), NFormat("%s: B (%`, %0n)", bfile[fb].name, bfile[fb].time, bfile[fb].size));
180 done |= 2;
181 }
182 else if(fb < 0) {
183 tree.Add(parent, CompDirImg::a_file(), NFormat("%s: A (%`, %0n)", afile[fa].name, afile[fa].time, afile[fa].size));
184 done |= 1;
185 }
186 else if(afile[fa].size != bfile[fb].size
187 || LoadFile(AppendFileName(arel, names[i])) != LoadFile(AppendFileName(brel, names[i]))) {
188 tree.Add(parent, CompDirImg::ab_file(), NFormat("%s: A (%`, %0n), B (%`, %0n)",
189 bfile[fb].name, afile[fa].time, afile[fa].size, bfile[fb].time, bfile[fb].size));
190 done |= 3;
191 }
192 }
193 return done;
194 }
195
GetTreePath() const196 String DlgCompareDir::GetTreePath() const
197 {
198 int i = tree.GetCursor();
199 if(i < 0)
200 return String::GetVoid();
201 if(i == 0)
202 return Null;
203 String s = tree.Get(i);
204 int f = s.Find(':');
205 if(f >= 0)
206 s.Trim(f);
207 while((i = tree.GetParent(i)) != 0)
208 s = AppendFileName(String(tree.Get(i)), s);
209 return s;
210 }
211
DoTreeCursor()212 void DlgCompareDir::DoTreeCursor()
213 {
214 String s = GetTreePath();
215 if(IsNull(s))
216 return;
217 String fa = AppendFileName(pa, s), fb = AppendFileName(pb, s);
218 String da = LoadFile(fa), db = LoadFile(fb);
219 if(!IsNull(da) || !IsNull(db)) {
220 if(IsNull(da) || IsNull(db)) {
221 qtf.Hide();
222 lineedit.Show();
223 lineedit <<= Nvl(db, da);
224 }
225 else {
226 lineedit.Hide();
227 qtf.Show();
228 String comptext = "[C2 ";
229 Vector<String> la = GetStringLineMap(da), lb = GetStringLineMap(db);
230 Array<TextSection> sections = CompareLineMaps(la, lb);
231 for(int s = 0; s < sections.GetCount(); s++) {
232 const TextSection& sec = sections[s];
233 if(sec.same) {
234 comptext << "[@(0.0.0) \1";
235 if(sec.count1 <= 6)
236 for(int i = 0; i < sec.count1; i++)
237 comptext << ExpandTabs(la[i + sec.start1]) << '\n';
238 else {
239 for(int i = 0; i < 3; i++)
240 comptext << ExpandTabs(la[i + sec.start1]) << '\n';
241 comptext << "...\n";
242 for(int i = -3; i < 0; i++)
243 comptext << ExpandTabs(la[i + sec.start1 + sec.count1]) << '\n';
244 }
245 comptext << "\1]";
246 }
247 else {
248 if(sec.count1) {
249 comptext << "[@(0.160.0) \1";
250 for(int i = 0; i < sec.count1; i++)
251 comptext << ExpandTabs(la[sec.start1 + i]) << '\n';
252 comptext << "\1]";
253 }
254 if(sec.count2) {
255 comptext << "[@(0.0.255) \1";
256 for(int i = 0; i < sec.count2; i++)
257 comptext << ExpandTabs(lb[sec.start2 + i]) << '\n';
258 comptext << "\1]";
259 }
260 }
261 }
262 qtf.SetQTF(comptext);
263 }
264 }
265 }
266
DoBrowse(Ctrl * field)267 void DlgCompareDir::DoBrowse(Ctrl *field)
268 {
269 FileSel fsel;
270 fsel.AllFilesType();
271 static String recent_dir;
272 fsel <<= Nvl((String)~*field, recent_dir);
273 if(fsel.ExecuteSelectDir())
274 *field <<= recent_dir = ~fsel;
275 }
276
ToolTree(Bar & bar)277 void DlgCompareDir::ToolTree(Bar& bar)
278 {
279 }
280
281 GUI_APP_MAIN
282 {
283 DlgCompareDir cmpdlg;
284 LoadFromFile(cmpdlg, ConfigFile());
285 cmpdlg.Run();
286 StoreToFile(cmpdlg, ConfigFile());
287 }
288