1 #include "ide.h"
2 
3 const char tempaux[] = "<temp-aux>";
4 const char prjaux[] = "<prj-aux>";
5 const char ideaux[] = "<ide-aux>";
6 
OverLtRed(const Image & m)7 Image OverLtRed(const Image& m)
8 {
9 	Image red = CreateImage(m.GetSize(), Blend(LtRed, SColorPaper));
10 	Over(red, Point(0, 0), m, m.GetSize());
11 	return red;
12 }
13 
ImageOverRed(const Image & m)14 Image ImageOverRed(const Image& m)
15 {
16 	return MakeImage(m, OverLtRed);
17 }
18 
ListFont()19 Font WorkspaceWork::ListFont()
20 {
21 	return StdFont();
22 };
23 
SetErrorFiles(const Vector<String> & files)24 void WorkspaceWork::SetErrorFiles(const Vector<String>& files)
25 {
26 	errorfiles <<= Index<String>(files, 1);
27 	int i = filelist.GetCursor();
28 	int s = filelist.GetSbPos();
29 	SaveLoadPackage(false);
30 	filelist.SetSbPos(s);
31 	filelist.SetCursor(i);
32 	SyncErrorPackages();
33 }
34 
PackagePathA(const String & pn)35 String WorkspaceWork::PackagePathA(const String& pn) {
36 	if(pn == prjaux) {
37 		String nm;
38 		String cfg = ConfigFile("cfg");
39 		for(const char *s = main; *s; s++)
40 			nm.Cat(*s == '\\' || *s == '/' ? '$' : *s);
41 		RealizeDirectory(cfg);
42 		return AppendFileName(cfg, ForceExt(nm + '@' + GetVarsName(), ".aux"));
43 	}
44 	if(pn == ideaux)
45 		return ConfigFile("ide.aux");
46 	if(pn == tempaux)
47 		return ConfigFile(Sprintf("aux%x.tmp",
48 #ifdef PLATFORM_WIN32
49 		          GetCurrentProcessId()
50 #endif
51 #ifdef PLATFORM_POSIX
52 		          getpid()
53 #endif
54 		       ));
55 	if(pn == METAPACKAGE)
56 		return Null;
57 	return PackagePath(pn);
58 }
59 
60 struct PackageOrder {
61 	String mainpath;
62 
GetMatchLenPackageOrder63 	int GetMatchLen(const String& x) const {
64 		if(*x == '<')
65 			return 0;
66 		String h = PackagePath(x);
67 		for(int i = 0; i < mainpath.GetCount(); i++)
68 			if(mainpath[i] != h[i])
69 				return i;
70 		return mainpath.GetCount();
71 	}
72 
operator ()PackageOrder73 	bool operator()(const String& p1, const String& p2) const {
74 		int l1 = GetMatchLen(p1);
75 		int l2 = GetMatchLen(p2);
76 		return l1 != l2 ? l1 > l2 : p1 < p2;
77 	}
78 };
79 
SyncErrorPackages()80 void WorkspaceWork::SyncErrorPackages()
81 {
82 	for(int i = 0; i < package.GetCount(); i++) {
83 		FileList::File f = package.Get(i);
84 		if(!IsAux(f.name)) {
85 			FileList::File ff = f;
86 			String path = GetFileFolder(PackagePath(f.name));
87 		#ifdef PLATFORM_WIN32
88 			path = ToLower(path);
89 		#endif
90 			ff.icon = i ? IdeImg::Package() : IdeImg::MainPackage();
91 			ff.underline = Null;
92 			for(int q = 0; q < errorfiles.GetCount(); q++) {
93 				if(errorfiles[q].StartsWith(path)) {
94 					ff.icon = ImageOverRed(ff.icon);
95 					ff.underline = LtRed;
96 					break;
97 				}
98 			}
99 			ff.icon = DPI(ff.icon);
100 			package.Set(i, ff);
101 		}
102 	}
103 }
104 
ScanWorkspace()105 void WorkspaceWork::ScanWorkspace() {
106 	Workspace wspc;
107 	if(main.GetCount())
108 		wspc.Scan(main);
109 	actualpackage.Clear();
110 	actualfileindex = -1;
111 	filelist.Clear();
112 	package.Clear();
113 	Vector<String> pks;
114 	for(int i = 0; i < wspc.package.GetCount(); i++)
115 		pks.Add(wspc.package.GetKey(i));
116 	if(sort && wspc.GetCount()) {
117 		PackageOrder po;
118 		po.mainpath = PackagePath(pks[0]);
119 		Sort(SubRange(pks, 1, pks.GetCount() - 1), po);
120 	}
121 	for(int i = 0; i < wspc.package.GetCount(); i++) {
122 		String pk = pks[i];
123 		Font fnt = ListFont();
124 		if(i == 0)
125 			fnt.Bold();
126 		PackageInfo pi = GetPackageInfo(pk);
127 		if(pi.bold)
128 			fnt.Bold();
129 		if(pi.italic)
130 			fnt.Italic();
131 		package.Add(pk, Null, fnt, Nvl(AdjustIfDark(pi.ink), SColorText()), false, 0, Null, SColorMark);
132 	}
133 	if(!organizer) {
134 		if(main.GetCount())
135 			package.Add(prjaux, IdeImg::PrjAux(), ListFont(), AdjustIfDark(Magenta));
136 		package.Add(ideaux, IdeImg::IdeAux(), ListFont(), AdjustIfDark(Magenta));
137 		package.Add(tempaux, IdeImg::TempAux(), ListFont(), AdjustIfDark(Magenta));
138 		if(main.GetCount())
139 			package.Add(METAPACKAGE, IdeImg::Meta(), ListFont(), AdjustIfDark(Red));
140 	}
141 	package.SetCursor(0);
142 
143 	SyncErrorPackages();
144 }
145 
SavePackage()146 void WorkspaceWork::SavePackage()
147 {
148 	if(IsNull(actualpackage) || actualpackage == METAPACKAGE)
149 		return;
150 	InvalidatePackageInfo(actualpackage);
151 	String pp = PackagePathA(actualpackage);
152 	RealizePath(pp);
153 	if(organizer && backup.Find(pp) < 0) {
154 		Backup& b = backup.Add(pp);
155 		FindFile ff(pp);
156 		if(ff) {
157 			b.time = ff.GetLastWriteTime();
158 			b.data = LoadFile(pp);
159 		}
160 		else
161 			b.data = String::GetVoid();
162 	}
163 	actual.Save(pp);
164 }
165 
RestoreBackup()166 void WorkspaceWork::RestoreBackup()
167 {
168 	for(int i = 0; i < backup.GetCount(); i++) {
169 		Backup& b = backup[i];
170 		String fn = backup.GetKey(i);
171 		if(b.data.IsVoid())
172 			DeleteFile(fn);
173 		else {
174 			SaveFile(fn, b.data);
175 			SetFileTime(fn, b.time);
176 		}
177 	}
178 }
179 
SaveLoadPackageNS(bool sel)180 void WorkspaceWork::SaveLoadPackageNS(bool sel)
181 {
182 	SavePackage();
183 	String p = actualpackage;
184 	String f;
185 	if(IsActiveFile())
186 		f = ActiveFile();
187 	int psc = package.GetSbPos();
188 	int fsc = filelist.GetSbPos();
189 	ScanWorkspace();
190 	package.SetSbPos(psc);
191 	package.FindSetCursor(p);
192 	if (sel) {
193 		filelist.SetSbPos(fsc);
194 		filelist.FindSetCursor(f);
195 	}
196 }
197 
SaveLoadPackage(bool sel)198 void WorkspaceWork::SaveLoadPackage(bool sel)
199 {
200 	SaveLoadPackageNS(sel);
201 	SyncWorkspace();
202 }
203 
PathIsLocal(const String & path)204 bool PathIsLocal(const String& path)
205 {
206 #ifdef PLATFORM_WIN32
207 	char drive[4] = "?:\\";
208 	*drive = *path;
209 	return GetDriveType(drive) == DRIVE_FIXED;
210 #else
211 	return false;
212 #endif
213 }
214 
LoadActualPackage()215 void WorkspaceWork::LoadActualPackage()
216 {
217 	Time utime = FileGetTime(ConfigFile("version"));
218 	filelist.Clear();
219 	fileindex.Clear();
220 	bool open = true;
221 	Time tm = GetSysTime();
222 	for(int i = 0; i < actual.file.GetCount(); i++) {
223 		Package::File& f = actual.file[i];
224 		if(f.separator) {
225 			open = closed.Find(Sepfo(actualpackage, f)) < 0;
226 			filelist.Add(f, open ? IdeImg::SeparatorClose() : IdeImg::SeparatorOpen(),
227 			             ListFont().Bold(), open ? SColorMark : SColorText, true, 0, Null);
228 			fileindex.Add(i);
229 		}
230 		else
231 		if(open) {
232 			Color uln = Null;
233 			String p = SourcePath(GetActivePackage(), f);
234 			if(showtime && (findarg(actualpackage, "<ide-aux>", "<prj-aux>", "<temp-aux>") < 0 || PathIsLocal(p))) {
235 				FindFile ff(p);
236 				if(ff) {
237 					Time ftm = Time(ff.GetLastWriteTime());
238 					if(ftm > utime) {
239 						int64 t = tm - ftm;
240 						if(t < 24 * 3600)
241 							uln = SColorMark;
242 						else
243 						if(t < 32 * 24 * 3600)
244 							uln = SColorDisabled;
245 					}
246 				}
247 			}
248 			Image m = IdeFileImage(f, false, f.pch);
249 			if(GetFileExt(p) == ".tpp" && IsFolder(p)) {
250 				if(FileExists(AppendFileName(p, "all.i")))
251 					m = TopicImg::IGroup();
252 				else
253 					m = TopicImg::Group();
254 			}
255 		#ifdef PLATFORM_WIN32
256 			p = ToLower(p);
257 		#endif
258 			if(errorfiles.Find(p) >= 0) {
259 				m = ImageOverRed(m);
260 				uln = LtRed;
261 			}
262 			filelist.Add(f, m, ListFont(), SColorText, false, 0,
263 			             Null, SColorMark, Null, Null, Null, uln);
264 			fileindex.Add(i);
265 		}
266 	}
267 }
268 
TouchFile(const String & path)269 void WorkspaceWork::TouchFile(const String& path)
270 {
271 	if(!showtime)
272 		return;
273 	String n = GetFileName(path);
274 	for(int i = 0; i < filelist.GetCount(); i++) {
275 		FileList::File f = filelist[i];
276 		if(f.name == n && path == SourcePath(GetActivePackage(), f.name))
277 			filelist.Set(i, f.name, f.icon, f.font, f.ink, false, 0,
278 			             Null, SColorMark, Null, Null, Null, SColorMark);
279 	}
280 }
281 
PackageCursor()282 void WorkspaceWork::PackageCursor()
283 {
284 	InvalidatePackageCache();
285 	filelist.WhenBar.Clear();
286 	actualpackage = GetActivePackage();
287 	repo_dirs = false;
288 	if(actualpackage.IsEmpty()) return;
289 	if(actualpackage == METAPACKAGE) {
290 		actual.file.Clear();
291 		actual.file.AddPick(Package::File(String(HELPNAME)));
292 		Vector<String> d = GetUppDirs();
293 		for(int i = 0; i < d.GetCount(); i++)
294 			actual.file.AddPick(Package::File(AppendFileName(d[i], "_.tpp")));
295 		actual.file.AddPick(Package::File(ConfigFile("global.defs")));
296 	}
297 	else {
298 		String pp = PackagePathA(actualpackage);
299 		RealizePath(pp);
300 		actual.Load(pp);
301 	}
302 	LoadActualPackage();
303 	filelist.Enable();
304 	if(actualpackage != METAPACKAGE)
305 		filelist.WhenBar = THISBACK(FileMenu);
306 	repo_dirs = RepoDirs(true).GetCount();
307 }
308 
RepoDirs(bool actual)309 Vector<String> WorkspaceWork::RepoDirs(bool actual)
310 {
311 	Vector<String> d = GetUppDirs();
312 	if (actual && !IsAux())
313 		d.Insert(0, GetFileFolder(PackagePath(actualpackage)));
314 	Vector<String> r;
315 	for(int i = 0; i < d.GetCount(); i++)
316 		if(GetRepoKind(d[i]))
317 			r.Add(d[i]);
318 	return r;
319 }
320 
FileCursor()321 void WorkspaceWork::FileCursor()
322 {
323 	int i = filelist.GetCursor();
324 	actualfileindex = -1;
325 	if(i >= 0 && i < fileindex.GetCount())
326 		actualfileindex = fileindex[i];
327 }
328 
FileName(int i) const329 String WorkspaceWork::FileName(int i) const
330 {
331 	return i >= 0 && i < fileindex.GetCount() ? (String)actual.file[fileindex[i]] : Null;
332 }
333 
IsSeparator(int i) const334 bool WorkspaceWork::IsSeparator(int i) const
335 {
336 	return i >= 0 && i < fileindex.GetCount() ? actual.file[fileindex[i]].separator : true;
337 }
338 
GetActiveFileName() const339 String WorkspaceWork::GetActiveFileName() const
340 {
341 	return FileName(filelist.GetCursor());
342 }
343 
GetActiveFilePath() const344 String WorkspaceWork::GetActiveFilePath() const
345 {
346 	return SourcePath(GetActivePackage(), GetActiveFileName());
347 }
348 
IsActiveFile() const349 bool   WorkspaceWork::IsActiveFile() const
350 {
351 	int i = filelist.GetCursor();
352 	return i >= 0 && i < fileindex.GetCount() && fileindex[i] < actual.file.GetCount();
353 }
354 
ActiveFile()355 Package::File& WorkspaceWork::ActiveFile()
356 {
357 	return actual.file[fileindex[filelist.GetCursor()]];
358 }
359 
AddFile(ADDFILE af)360 void WorkspaceWork::AddFile(ADDFILE af)
361 {
362 	String active = GetActivePackage();
363 	if(active.IsEmpty()) return;
364 	FileSel *fs = &OutputFs();
365 	RealizeDirectory(GetLocalDir());
366 	switch(af)
367 	{
368 	case PACKAGE_FILE: fs = &BasedSourceFs(); fs->BaseDir(GetFileFolder(PackagePathA(active))); break;
369 	case ANY_FILE:     fs = &AnySourceFs(); break;
370 	case OUTPUT_FILE:  fs->ActiveDir(GetOutputDir()); break;
371 	case CONFIG_FILE:  fs->ActiveDir(GetConfigDir()); break;
372 	case HOME_FILE:    fs->ActiveDir(GetHomeDirectory()); break;
373 	case LOCAL_FILE:   fs->ActiveDir(GetLocalDir()); break;
374 	default: ; // GCC warns otherwise
375 	}
376 	if(!fs->ExecuteOpen("Add files to package..")) return;
377 	int fci = filelist.GetCursor();
378 	int cs = filelist.GetSbPos();
379 	int ci = fci >= 0 && fci < fileindex.GetCount() ? fileindex[fci] : -1;
380 	for(int i = 0; i < fs->GetCount(); i++) {
381 		Package::File& f = ci >= 0 ? actual.file.Insert(ci++) : actual.file.Add();
382 		f = (*fs)[i];
383 		f.readonly = fs->GetReadOnly();
384 	}
385 	SaveLoadPackage(false);
386 	filelist.SetSbPos(cs);
387 	filelist.SetCursor(fci >= 0 ? fci : filelist.GetCount() - 1);
388 	FileSelected();
389 }
390 
AddItem(const String & name,bool separator,bool readonly)391 void WorkspaceWork::AddItem(const String& name, bool separator, bool readonly)
392 {
393 	int fci = filelist.GetCursor();
394 	int cs = filelist.GetSbPos();
395 	int ci = fci >= 0 && fci < fileindex.GetCount() ? fileindex[fci] : -1;
396 	Package::File& f = ci >= 0 ? actual.file.Insert(ci) : actual.file.Add();
397 	f = name;
398 	f.separator = separator;
399 	f.readonly = readonly;
400 	if(separator)
401 		SaveLoadPackageNS(false);
402 	else
403 		SaveLoadPackage(false);
404 	filelist.SetSbPos(cs);
405 	filelist.SetCursor(fci >= 0 ? fci : filelist.GetCount() - 1);
406 	FileSelected();
407 }
408 
AddSeparator()409 void WorkspaceWork::AddSeparator()
410 {
411 	String active = GetActivePackage();
412 	if(active.IsEmpty()) return;
413 	String name;
414 	if(!EditText(name, "Add separator", "Name"))
415 		return;
416 	AddItem(~name, true, true);
417 }
418 
419 class ImportDlg : public WithImportLayout<TopWindow> {
420 	typedef ImportDlg CLASSNAME;
421 
422 	FrameRight<Button> dir;
423 
424 	void SetFolder();
425 
426 public:
427 	ImportDlg();
428 };
429 
SetFolder()430 void ImportDlg::SetFolder()
431 {
432 	if(!AnySourceFs().ExecuteSelectDir()) return;
433 	folder <<= ~AnySourceFs();
434 }
435 
ImportDlg()436 ImportDlg::ImportDlg()
437 {
438 	CtrlLayoutOKCancel(*this, "Import directory tree into package");
439 	folder.AddFrame(dir);
440 	dir <<= THISBACK(SetFolder);
441 	dir.SetImage(CtrlImg::smallright()).NoWantFocus();
442 	files <<= "*.cpp *.h *.hpp *.c *.C *.cxx *.cc";
443 }
444 
FileOrder_(const String & a,const String & b)445 bool FileOrder_(const String& a, const String& b)
446 {
447 	return stricmp(a, b) < 0;
448 }
449 
DoImportTree(const String & dir,const String & mask,bool sep,Progress & pi,int from)450 void WorkspaceWork::DoImportTree(const String& dir, const String& mask, bool sep, Progress& pi, int from)
451 {
452 	String active = GetActivePackage();
453 	if(active.IsEmpty()) return;
454 	FindFile ff(AppendFileName(dir, "*.*"));
455 	Vector<String> dirs, files;
456 	while(ff) {
457 		String p = AppendFileName(dir, ff.GetName());
458 		if(ff.IsFile() && PatternMatchMulti(mask, ff.GetName()))
459 			files.Add(p);
460 		if(ff.IsFolder())
461 			dirs.Add(p);
462 		ff.Next();
463 	}
464 	String relPath(dir.Mid(from)),
465 		absPath = SourcePath(active, relPath);
466 	if(sep && files.GetCount()) {
467 		if(!DirectoryExists(absPath))
468 			if(!RealizeDirectory(absPath))
469 				throw Format("An error occurred while creating the directory:&\1%s", absPath);
470 		Package::File& f = actual.file.Add();
471 		f = relPath;
472 		f.separator = f.readonly = true;
473 	}
474 	Sort(files, &FileOrder_);
475 	Sort(dirs, &FileOrder_);
476 	for(int i = 0; i < files.GetCount(); i++) {
477 		if(pi.StepCanceled())
478 			throw String();
479 		String name = GetFileName(files[i]);
480 		if(FileCopy(files[i], AppendFileName(absPath, name))) {
481 			Package::File& f = actual.file.Add();
482 			f = AppendFileName(relPath, name);
483 			f.separator = f.readonly = false;
484 		}
485 		else
486 			throw Format("An error occurred while copying the file:&\1%s", files[i]);
487 	}
488 	for(int i = 0; i < dirs.GetCount(); i++)
489 		DoImportTree(dirs[i], mask, true, pi, from);
490 }
491 
DoImportTree(const String & dir,const String & mask,bool sep,Progress & pi)492 void WorkspaceWork::DoImportTree(const String& dir, const String& mask, bool sep, Progress& pi)
493 {
494 	int from = dir.EndsWith(AsString(DIR_SEP)) ? dir.GetCount() : dir.GetCount() + 1;
495 	DoImportTree(dir, mask, sep, pi, from);
496 }
497 
DoImport(const String & dir,const String & mask,bool sep,Progress & pi)498 void WorkspaceWork::DoImport(const String& dir, const String& mask, bool sep, Progress& pi)
499 {
500 	String active = GetActivePackage();
501 	if(active.IsEmpty()) return;
502 	FindFile ff(AppendFileName(dir, "*.*"));
503 	Vector<String> dirs, files;
504 	while(ff) {
505 		String p = AppendFileName(dir, ff.GetName());
506 		if(ff.IsFile() && PatternMatchMulti(mask, ff.GetName()))
507 			files.Add(p);
508 		if(ff.IsFolder())
509 			dirs.Add(p);
510 		ff.Next();
511 	}
512 	if(sep && files.GetCount()) {
513 		Package::File& f = actual.file.Add();
514 		f = GetFileTitle(dir);
515 		f.separator = f.readonly = true;
516 	}
517 	Sort(files, &FileOrder_);
518 	Sort(dirs, &FileOrder_);
519 	for(int i = 0; i < files.GetCount(); i++) {
520 		if(pi.StepCanceled())
521 			throw String();
522 		String name = GetFileName(files[i]);
523 		if(FileCopy(files[i], SourcePath(active, name))) {
524 			Package::File& f = actual.file.Add();
525 			f = name;
526 			f.separator = f.readonly = false;
527 		}
528 		else
529 			throw Format("An error occurred while copying the file:&\1%s", files[i]);
530 	}
531 	for(int i = 0; i < dirs.GetCount(); i++)
532 		DoImport(dirs[i], mask, true, pi);
533 }
534 
DoImport(const String & dir,const String & mask,bool sep,Progress & pi,bool tree)535 void WorkspaceWork::DoImport(const String& dir, const String& mask, bool sep, Progress& pi, bool tree)
536 {
537 	if(tree)
538 		DoImportTree(dir, mask, sep, pi);
539 	else
540 		DoImport(dir, mask, sep, pi);
541 }
542 
Import()543 void WorkspaceWork::Import()
544 {
545 	String active = GetActivePackage();
546 	if(active.IsEmpty()) return;
547 	ImportDlg dlg;
548 	if(dlg.Execute() != IDOK)
549 		return;
550 	Progress pi("Importing file %d");
551 	int fci = filelist.GetCursor();
552 	int cs = filelist.GetSbPos();
553 	try {
554 		DoImport(~dlg.folder, ~dlg.files, false, pi, ~dlg.tree);
555 	}
556 	catch(String msg) {
557 		if(!msg.IsEmpty())
558 			Exclamation(msg);
559 	}
560 	SaveLoadPackage(false);
561 	filelist.SetSbPos(cs);
562 	filelist.SetCursor(fci >= 0 ? fci : filelist.GetCount() - 1);
563 	FileSelected();
564 }
565 
TppName(const String & s)566 String TppName(const String& s)
567 {
568 	if(s == "src")
569 		return "Reference - src";
570 	if(s == "srcdoc")
571 		return "Documents - srcdoc";
572 	if(s == "srcimp")
573 		return "Implementation - srcimp";
574 	return s;
575 }
576 
577 class Tpp : public WithTppLayout<TopWindow> {
578 public:
Sync()579 	void Sync() {
580 		bool en = group.IsCursor() && IsNull(group.GetKey());
581 		name_lbl.Enable(en);
582 		name.Enable(en);
583 		name_tpp.Enable(en);
584 	}
585 
Load(const char * dir)586 	void Load(const char *dir)
587 	{
588 		Index<String> exist;
589 		FindFile ff(AppendFileName(dir, "*.tpp"));
590 		while(ff) {
591 			if(ff.IsFolder()) {
592 				String s = GetFileTitle(ff.GetName());
593 				group.Add(s, AttrText(TppName(s)).SetFont(StdFont().Bold()));
594 				exist.Add(s);
595 			}
596 			ff.Next();
597 		}
598 		static const char *h[4] = { "src.tpp", "srcdoc.tpp", "srcimp.tpp", "app.tpp" };
599 		for(int i = 0; i < __countof(h); i++) {
600 			String s = GetFileTitle(h[i]);
601 			if(exist.Find(s) < 0)
602 				group.Add(s, TppName(s) + " (new)");
603 		}
604 		group.Add(Null, "<other new>");
605 		group.GoBegin();
606 	}
607 
GetName()608 	String GetName()
609 	{
610 		String s;
611 		if(group.IsCursor()) {
612 			s = group.GetKey();
613 			if(IsNull(s))
614 				s << ~name;
615 			s << ".tpp";
616 		}
617 		return s;
618 	}
619 
620 	typedef Tpp CLASSNAME;
621 
Tpp()622 	Tpp() {
623 		CtrlLayoutOKCancel(*this, "Insert topic group");
624 		group.AddKey();
625 		group.AddColumn("Group");
626 		group.WhenSel = THISBACK(Sync);
627 		name.SetFilter(CharFilterAlpha);
628 	}
629 };
630 
AddTopicGroup()631 void WorkspaceWork::AddTopicGroup()
632 {
633 	String package = GetActivePackage();
634 	if(IsNull(package)) return;
635 	Tpp dlg;
636 	dlg.Load(PackageDirectory(package));
637 	if(dlg.Run() != IDOK) return;
638 	String g = dlg.GetName();
639 	if(g == "app.tpp") {
640 		String h = SourcePath(package, g);
641 		RealizeDirectory(h);
642 		SaveFile(AppendFileName(h, "all.i"), "");
643 	}
644 	if(g.GetCount())
645 		AddItem(g, false, false);
646 }
647 
RemoveFile()648 void WorkspaceWork::RemoveFile()
649 {
650 	int i = filelist.GetCursor();
651 	int s = filelist.GetSbPos();
652 	bool separator = false;
653 	if(i >= 0 && i < fileindex.GetCount()) {
654 		int fx = fileindex[i];
655 		separator = actual.file[fx].separator;
656 		if(separator && closed.Find(GetActiveSepfo()) >= 0) {
657 			int px = fx;
658 			while(--px >= 0 && !actual.file[fx].separator)
659 				;
660 			if(px >= 0) {
661 				int c = closed.Find(Sepfo(GetActivePackage(), actual.file[px]));
662 				if(c >= 0)
663 					closed.Unlink(c);
664 			}
665 		}
666 		actual.file.Remove(fx);
667 	}
668 	if(separator || IsAux())
669 		SaveLoadPackageNS(false);
670 	else
671 		SaveLoadPackage(false);
672 
673 	if (separator || FileRemove()) {
674 		filelist.SetSbPos(s);
675 		filelist.SetCursor(i);
676 	}
677 }
678 
DelFile()679 void WorkspaceWork::DelFile()
680 {
681 	if(!filelist.IsCursor() || filelist[filelist.GetCursor()].isdir) return;
682 	String file = GetActiveFilePath();
683 	if(IsFolder(file)) {
684 		if(!PromptYesNo("Remove the topic group and discard ALL topics?")) return;
685 		RemoveFile();
686 		DeleteFolderDeep(file);
687 	}
688 	else {
689 		if(!PromptYesNo("Remove the file from package and discard it?")) return;
690 		RemoveFile();
691 		::DeleteFile(file);
692 	}
693 }
694 
RenameFile()695 void WorkspaceWork::RenameFile()
696 {
697 	if(!filelist.IsCursor()) return;
698 	String n = GetActiveFileName();
699 	int i = filelist.GetCursor();
700 	if(i < 0 || i >= fileindex.GetCount())
701 		return;
702 	int ii = fileindex[i];
703 	WithEditStringLayout<TopWindow> dlg;
704 	CtrlLayoutOKCancel(dlg, "Rename file");
705 	dlg.lbl = "New name";
706 	dlg.text <<= n;
707 	dlg.Open();
708 	dlg.text.SetFocus();
709 	int l = int(GetFileNamePos(n) - ~n);
710 	int h = int(GetFileExtPos(n) - ~n);
711 	if(l >= h)
712 		l = 0;
713 	dlg.text.SetSelection(l, h);
714 	if(dlg.Run() != IDOK)
715 		return;
716 	n = ~dlg.text;
717 	String spath = GetActiveFilePath();
718 	String dpath = SourcePath(GetActivePackage(), n);
719 	if(!filelist[i].isdir && GetFileLength(spath) >= 0) {
720 		if(!::MoveFile(spath, dpath)) {
721 			Exclamation("Failed to rename the file.&&" + GetErrorMessage(GetLastError()));
722 			return;
723 		}
724 	}
725 	FileRename(dpath);
726 	int s = filelist.GetSbPos();
727 	(String &)actual.file[ii] = n;
728 	SaveLoadPackage(false);
729 	filelist.SetSbPos(s);
730 	filelist.SetCursor(i);
731 	if(GetFileExt(spath) == ".iml" || GetFileExt(dpath) == ".iml")
732 		SyncWorkspace();
733 }
734 
GetActiveSepfo()735 WorkspaceWork::Sepfo WorkspaceWork::GetActiveSepfo()
736 {
737 	return Sepfo(GetActivePackage(), GetActiveFileName());
738 }
739 
GroupOrFile(Point pos)740 void WorkspaceWork::GroupOrFile(Point pos)
741 {
742 	if(pos.x < filelist.GetIconWidth())
743 		Group();
744 	if(filelist.IsCursor() && !filelist[filelist.GetCursor()].isdir)
745 		FileSelected();
746 }
747 
Group()748 void   WorkspaceWork::Group()
749 {
750 	if(!filelist.IsCursor() || !filelist[filelist.GetCursor()].isdir)
751 		return;
752 	int i = filelist.GetCursor();
753 	int s = filelist.GetSbPos();
754 	Sepfo as = GetActiveSepfo();
755 	if(closed.Find(as) >= 0)
756 		closed.UnlinkKey(as);
757 	else
758 		closed.Put(as);
759 	SaveLoadPackage(false);
760 	filelist.SetSbPos(s);
761 	filelist.SetCursor(i);
762 }
763 
OpenAllGroups()764 void WorkspaceWork::OpenAllGroups()
765 {
766 	for(int i = 0; i < actual.file.GetCount(); i++)
767 		if(actual.file[i].separator)
768 			closed.UnlinkKey(Sepfo(GetActivePackage(), actual.file[i]));
769 	SaveLoadPackage();
770 }
771 
CloseAllGroups()772 void WorkspaceWork::CloseAllGroups()
773 {
774 	for(int i = 0; i < actual.file.GetCount(); i++)
775 		if(actual.file[i].separator)
776 			closed.FindPut(Sepfo(GetActivePackage(), actual.file[i]));
777 	SaveLoadPackage();
778 }
779 
ShowFile(int pi)780 void  WorkspaceWork::ShowFile(int pi)
781 {
782 	bool open = true;
783 	Sepfo sf;
784 	for(int i = 0; i < actual.file.GetCount(); i++) {
785 		if(actual.file[i].separator) {
786 			sf = Sepfo(GetActivePackage(), actual.file[i]);
787 			open = closed.Find(sf) < 0;
788 		}
789 		else
790 		if(i == pi) {
791 			if(!open) {
792 				int i = filelist.GetCursor();
793 				int s = filelist.GetSbPos();
794 				closed.UnlinkKey(sf);
795 				SaveLoadPackage(false);
796 				filelist.SetSbPos(s);
797 				filelist.SetCursor(i);
798 			}
799 			return;
800 		}
801 	}
802 }
803 
IsAux(const String & p)804 bool WorkspaceWork::IsAux(const String& p)
805 {
806 	return p == tempaux || p == prjaux || p == ideaux || p == METAPACKAGE;
807 }
808 
IsAux()809 bool WorkspaceWork::IsAux()
810 {
811 	return IsAux(actualpackage);
812 }
813 
InsertSpecialMenu(Bar & menu)814 void WorkspaceWork::InsertSpecialMenu(Bar& menu)
815 {
816 	bool isaux = IsAux();
817 	menu.Add("Insert any file(s)..", THISBACK1(AddFile, ANY_FILE))
818 		.Key(K_SHIFT|K_CTRL_I)
819 		.Help("Insert files from anywhere on disk (discouraged in portable packages)");
820 	menu.Add(isaux && GetOutputDir().GetCount(), "Insert output directory file(s)..", THISBACK1(AddFile, OUTPUT_FILE))
821 		.Help("Open file selector in output / intermediate directory for current package");
822 #ifdef PLATFORM_POSIX
823 	menu.Add(isaux && GetConfigDir().GetCount(), "Insert config directory file(s)..", THISBACK1(AddFile, CONFIG_FILE))
824 		.Help("Open file selector in output / intermediate directory for current package");
825 #endif
826 	menu.Add(isaux, "Insert Local directory file(s)..", THISBACK1(AddFile, LOCAL_FILE))
827 		.Help("Open file selector in Local directory for current package");
828 	menu.Add("Insert home directory file(s)..", THISBACK1(AddFile, HOME_FILE))
829 		.Help("Open file selector in current user's HOME directory");
830 }
831 
SpecialFileMenu(Bar & menu)832 void WorkspaceWork::SpecialFileMenu(Bar& menu)
833 {
834 	InsertSpecialMenu(menu);
835 	menu.Add("Import directory tree sources..", THISBACK(Import));
836 }
837 
OpenFileFolder()838 void WorkspaceWork::OpenFileFolder()
839 {
840 	ShellOpenFolder(GetFileDirectory(GetActiveFilePath()));
841 }
842 
OpenPackageFolder()843 void WorkspaceWork::OpenPackageFolder()
844 {
845 	ShellOpenFolder(GetFileDirectory(GetActivePackagePath()));
846 }
847 
FileMenu(Bar & menu)848 void WorkspaceWork::FileMenu(Bar& menu)
849 {
850 	bool sel = filelist.IsCursor() && filelist[filelist.GetCursor()].isdir;
851 
852 	bool isaux = IsAux();
853 	if(isaux)
854 		InsertSpecialMenu(menu);
855 	else {
856 		menu.Add("New package file..", IdeCommonImg::PageAdd(), [=] { NewPackageFile(); });
857 		menu.Add(!isaux, "Insert package directory file(s)..", THISBACK1(AddFile, PACKAGE_FILE))
858 			.Help("Insert file relative to current package");
859 		menu.Add(!isaux, "Insert topic++ group..", TopicImg::IGroup(), THISBACK(AddTopicGroup));
860 	}
861 	menu.Add("Insert separator..", IdeImg::Separator(), [=] { AddSeparator(); })
862 		.Help("Add text separator line");
863 	if(!isaux) {
864 		menu.Add("Insert special", THISBACK(SpecialFileMenu))
865 		    .Help("Less frequently used methods of adding files to the package");
866 	}
867 	menu.Separator();
868 	if(!organizer) {
869 		if(sel)
870 			menu.Add(closed.Find(GetActiveSepfo()) < 0 ? "Close group\t[double-click]"
871 			                                           : "Open group\t[double-click]", THISBACK(Group));
872 		menu.Add("Open all groups", THISBACK(OpenAllGroups));
873 		menu.Add("Close all groups", THISBACK(CloseAllGroups));
874 		menu.Separator();
875 		BuildFileMenu(menu);
876 		menu.Separator();
877 	}
878 	menu.Add("Rename...", THISBACK(RenameFile))
879 	    .Help("Rename file / separator / topic group");
880 	menu.Add("Remove", THISBACK(RemoveFile))
881 		.Key(organizer ? K_DELETE : K_ALT_DELETE)
882 		.Help("Remove file / separator / topic group from package");
883 	menu.Add(filelist.IsCursor(), "Delete", sel ? THISBACK(RemoveFile) : THISBACK(DelFile))
884 		.Help("Remove file / topic group reference from package & delete file / folder on disk");
885 	menu.Separator();
886 	menu.Add("Open File Directory",THISBACK(OpenFileFolder));
887 	menu.Add("Copy File Path", callback1(WriteClipboardText, GetActiveFilePath()));
888 	menu.Separator();
889 	menu.Add(filelist.GetCursor() > 0, "Move up", THISBACK1(MoveFile, -1))
890 		.Key(organizer ? K_CTRL_UP : K_SHIFT_CTRL_UP)
891 		.Help("Move current file one position towards package beginning");
892 	menu.Add(filelist.IsCursor() && filelist.GetCursor() < filelist.GetCount() - 1, "Move down",
893 	         THISBACK1(MoveFile, 1))
894 		.Key(organizer ? K_CTRL_DOWN : K_SHIFT_CTRL_DOWN)
895 		.Help("Move current file one position towards package end");
896 	if(IsActiveFile()) {
897 		menu.Separator();
898 		String p = GetActiveFilePath();
899 		String ext = GetFileExt(p);
900 		if(ext == ".tpp" && IsFolder(p)) {
901 			menu.Add("Includeable topic group", THISBACK(ToggleIncludeable))
902 			    .Check(FileExists(AppendFileName(p, "all.i")));
903 			if(GetRepoKind(p))
904 				menu.Add("Repo Synchronize " + p, THISBACK1(SyncSvnDir, p));
905 		}
906 		else {
907 			if(IsHeaderExt(ext))
908 				menu.Add("Precompile header", THISBACK(TogglePCH))
909 				    .Check(ActiveFile().pch);
910 		}
911 	}
912 	FilePropertiesMenu(menu);
913 }
914 
TogglePCH()915 void WorkspaceWork::TogglePCH()
916 {
917 	if(IsActiveFile()) {
918 		ActiveFile().pch = !ActiveFile().pch;
919 		SaveLoadPackageNS();
920 	}
921 }
922 
ToggleIncludeable()923 void WorkspaceWork::ToggleIncludeable()
924 {
925 	if(IsActiveFile()) {
926 		String p = GetActiveFilePath();
927 		SetTopicGroupIncludeable(p, !FileExists(AppendFileName(p, "all.i")));
928 		SaveLoadPackageNS();
929 	}
930 }
931 
AddNormalUses()932 void WorkspaceWork::AddNormalUses()
933 {
934 	String p = SelectPackage("Select package");
935 	if(p.IsEmpty()) return;
936 	OptItem& m = actual.uses.Add();
937 	m.text = p;
938 	SaveLoadPackage();
939 }
940 
RemovePackageMenu(Bar & bar)941 void WorkspaceWork::RemovePackageMenu(Bar& bar)
942 {
943 	if(bar.IsScanKeys() || bar.IsScanHelp() || !bar.IsMenuBar())
944 		return;
945 	String active = UnixPath(GetActivePackage());
946 	int usecnt = 0;
947 	for(int i = 0; i < package.GetCount(); i++) {
948 		String pn = UnixPath(package[i].name);
949 		Package prj;
950 		String pp = PackagePath(pn);
951 		prj.Load(pp);
952 		for(int i = 0; i < prj.uses.GetCount(); i++)
953 			if(UnixPath(prj.uses[i].text) == active) {
954 				usecnt++;
955 				bar.Add("Remove from '" + pn + '\'', THISBACK1(RemovePackage, pn))
956 					.Help(Format("Remove package '%s' from uses section in '%s'", active, pp));
957 			}
958 	}
959 	if(usecnt > 1) {
960 		bar.MenuSeparator();
961 		bar.Add("Remove all uses", THISBACK1(RemovePackage, String(Null)))
962 			.Help(Format("Remove package '%s' from all uses in active project and its submodules", active));
963 	}
964 }
965 
PackageOp(String active,String from_package,String rename)966 void WorkspaceWork::PackageOp(String active, String from_package, String rename)
967 {
968 	active = UnixPath(active);
969 	from_package = UnixPath(from_package);
970 	rename = UnixPath(rename);
971 	for(int i = 0; i < package.GetCount(); i++)
972 		if(*package[i].name != '<' &&
973 		   (IsNull(from_package) || UnixPath(package[i].name) == from_package)) {
974 			String pp = PackagePath(package[i].name);
975 			Package prj;
976 			if(prj.Load(pp)) {
977 				for(int i = prj.uses.GetCount(); --i >= 0;)
978 					if(UnixPath(prj.uses[i].text) == active) {
979 						if(rename.GetCount())
980 							prj.uses[i].text = rename;
981 						else
982 							prj.uses.Remove(i);
983 					}
984 				prj.Save(pp);
985 			}
986 		}
987 	ScanWorkspace();
988 	SyncWorkspace();
989 }
990 
RemovePackage(String from_package)991 void WorkspaceWork::RemovePackage(String from_package)
992 {
993 	String active = UnixPath(GetActivePackage());
994 	if(IsNull(from_package) && !PromptYesNo(Format(
995 		"Remove package [* \1%s\1] from uses sections of all current packages ?", active)))
996 		return;
997 	PackageOp(GetActivePackage(), from_package, Null);
998 }
999 
TogglePackageSpeed()1000 void WorkspaceWork::TogglePackageSpeed()
1001 {
1002 	if(!package.IsCursor())
1003 		return;
1004 	SaveLoadPackageNS();
1005 }
1006 
RenamePackage()1007 void WorkspaceWork::RenamePackage()
1008 {
1009 	if(IsAux() || !package.IsCursor())
1010 		return;
1011 	WithRenamePackageLayout<TopWindow> dlg;
1012 	CtrlLayoutOKCancel(dlg, "Rename package");
1013 	dlg.name.SetFilter(FilterPackageName);
1014 	dlg.name <<= package.Get(package.GetCursor()).name;
1015 	dlg.name.SelectAll();
1016 again:
1017 	if(dlg.Execute() != IDOK)
1018 		return;
1019 	String pn = ~dlg.name;
1020 	String ap = GetActivePackage();
1021 	if(!RenamePackageFs(PackagePath(ap), pn))
1022 		goto again;
1023 	PackageOp(ap, Null, pn);
1024 }
1025 
DeletePackage()1026 void WorkspaceWork::DeletePackage()
1027 {
1028 	String active = GetActivePackage();
1029 	if(package.GetCursor() == 0) {
1030 		Exclamation("Cannot delete the main package!");
1031 		return;
1032 	}
1033 	if(IsAux() || !package.IsCursor() ||
1034 	   !PromptYesNo("Do you really want to delete package [* \1" + active + "\1]?&&"
1035 	                "[/ Warning:] [* Package will only be removed&"
1036 	                "from packages of current workspace!]"))
1037 		return;
1038 	if(!PromptYesNo("This operation is irreversible.&Do you really want to proceed?"))
1039 		return;
1040 	if(!DeleteFolderDeep(GetFileFolder(GetActivePackagePath()))) {
1041 		Exclamation("Deleting directory has failed.");
1042 		return;
1043 	}
1044 	PackageOp(active, Null, Null);
1045 }
1046 
PackageMenu(Bar & menu)1047 void WorkspaceWork::PackageMenu(Bar& menu)
1048 {
1049 	if(!menu.IsScanKeys()) {
1050 		bool cando = !IsAux() && package.IsCursor();
1051 		String act = UnixPath(GetActivePackage());
1052 		menu.Add(cando, ~Format("Add package to '%s'", act), IdeImg::package_add(), THISBACK(AddNormalUses));
1053 		RemovePackageMenu(menu);
1054 		if(menu.IsMenuBar()) {
1055 			bool main = package.GetCursor() == 0;
1056 
1057 			menu.Add(cando, "Rename package..", THISBACK(RenamePackage));
1058 			menu.Add(cando && !main, "Delete package", THISBACK(DeletePackage));
1059 			menu.Separator();
1060 			BuildPackageMenu(menu);
1061 			menu.Add("Open Package Directory",THISBACK(OpenPackageFolder));
1062 		}
1063 	}
1064 }
1065 
DoMove(int b,bool drag)1066 void WorkspaceWork::DoMove(int b, bool drag)
1067 {
1068 	int fi = filelist.GetCursor();
1069 	if(fi < 0 || fi >= fileindex.GetCount())
1070 		return;
1071 	int a = fileindex[fi];
1072 	if(a < 0 || b < 0 || a >= actual.file.GetCount() ||
1073 	   (drag ? b > actual.file.GetCount() : b >= actual.file.GetCount()))
1074 		return;
1075 	int s = filelist.GetSbPos();
1076 	ShowFile(a);
1077 	ShowFile(b);
1078 	if(drag) {
1079 		actual.file.Move(a, b);
1080 		if(b >= a)
1081 			b--;
1082 	}
1083 	else
1084 		Swap(actual.file[a], actual.file[b]);
1085 	ShowFile(a);
1086 	ShowFile(b);
1087 	SavePackage();
1088 	LoadActualPackage();
1089 	filelist.SetSbPos(s);
1090 	for(int i = 0; i < fileindex.GetCount(); i++)
1091 		if(fileindex[i] == b) {
1092 			filelist.SetCursor(i);
1093 			break;
1094 		}
1095 	filelist.Sync();
1096 }
1097 
MoveFile(int d)1098 void WorkspaceWork::MoveFile(int d)
1099 {
1100 	int bi = filelist.GetCursor() + d;
1101 	if(bi < 0 || bi >= fileindex.GetCount())
1102 		return;
1103 	DoMove(fileindex[bi], false);
1104 }
1105 
DnDInsert(int line,PasteClip & d)1106 void WorkspaceWork::DnDInsert(int line, PasteClip& d)
1107 {
1108 	if(GetActivePackage() == METAPACKAGE)
1109 		return;
1110 	if(GetInternalPtr<UppList>(d, "package-file") == &filelist && d.Accept())
1111 		DoMove(line < fileindex.GetCount() ? fileindex[line] : actual.file.GetCount(), true);
1112 }
1113 
Drag()1114 void WorkspaceWork::Drag()
1115 {
1116 	filelist.DoDragAndDrop(InternalClip(filelist, "package-file"),
1117 	                       filelist.GetDragSample(), DND_MOVE);
1118 }
1119 
WorkspaceWork()1120 WorkspaceWork::WorkspaceWork()
1121 {
1122 	package <<= THISBACK(PackageCursor);
1123 	package.WhenBar = THISBACK(PackageMenu);
1124 	package.NoRoundSize();
1125 	package.Columns(2);
1126 	filelist <<= THISBACK(FileCursor);
1127 	filelist.WhenLeftClickPos = THISBACK(GroupOrFile);
1128 	filelist.WhenLeftDouble = THISBACK(Group);
1129 	filelist.Columns(2);
1130 	filelist.NoRoundSize();
1131 	actualfileindex = -1;
1132 	organizer = false;
1133 	package.BackPaintHint();
1134 	filelist.BackPaintHint();
1135 	filelist.WhenDrag = THISBACK(Drag);
1136 	filelist.WhenDropInsert = THISBACK(DnDInsert);
1137 	showtime = false;
1138 	sort = true;
1139 	repo_dirs = false;
1140 }
1141 
SerializeClosed(Stream & s)1142 void WorkspaceWork::SerializeClosed(Stream& s)
1143 {
1144 	Workspace wspc;
1145 	wspc.Scan(main);
1146 	Vector<Sepfo> list;
1147 	for(int i = 0; i < wspc.GetCount(); i++) {
1148 		String pk = wspc[i];
1149 		const Package& p = wspc.GetPackage(i);
1150 		for(int i = 0; i < p.GetCount(); i++)
1151 			if(p[i].separator) {
1152 				Sepfo sf(pk, p[i]);
1153 				if(closed.Find(sf) >= 0)
1154 					list.Add(sf);
1155 			}
1156 	}
1157 	s % list;
1158 	closed = pick(list);
1159 }
1160 
Paint(Draw & w,const Rect & r,const Value & q,Color ink,Color paper,dword style) const1161 void UppList::Paint(Draw& w, const Rect& r, const Value& q, Color ink, Color paper, dword style) const
1162 {
1163 	FileList::File file = ValueTo<FileList::File>(q);
1164 	if(GetFileName(file.name) == "$.tpp" && IsFolder(file.name))
1165 		file.name = GetFileName(GetFileFolder(file.name)) + " templates";
1166 	if(file.name == ConfigFile("global.defs"))
1167 		file.name = "Fixed macros";
1168 	FileList::Paint(w, r, RawToValue(file), ink, paper, style);
1169 }
1170