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