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