1 #include "ide.h"
2
3 #ifdef _DEBUG
4 #define LSLOW() // Sleep(20) // Simulate HD seeks to test package cache
5 #else
6 #define LSLOW()
7 #endif
8
PackageMenu(Bar & menu)9 void SelectPackageDlg::PackageMenu(Bar& menu)
10 {
11 bool b = GetCurrentName().GetCount();
12 menu.Add("New package..", [=] { OnNew(); });
13 menu.Separator();
14 menu.Add(b, "Duplicate package..", [=] { RenamePackage(true); });
15 menu.Add(b, "Rename package..", [=] { RenamePackage(false); });
16 menu.Add(b, "Copy package to..", [=] { MovePackage(true); });
17 menu.Add(b, "Move package to..", [=] { MovePackage(false); });
18 menu.Add(b, "Delete package..", [=] { DeletePackage(); });
19 }
20
RenamePackageFs(const String & upp,const String & npf,const String & nupp,bool copy)21 bool RenamePackageFs(const String& upp, const String& npf, const String& nupp, bool copy)
22 {
23 String pf = GetFileFolder(upp);
24 String temp_pf = AppendFileName(GetFileFolder(pf), AsString(Random()) + AsString(Random()));
25 if(!FileMove(pf, temp_pf)) {
26 Exclamation("Operation has failed.");
27 return false;
28 }
29 RealizePath(GetFileFolder(npf));
30 if(copy) {
31 bool b = CopyFolder(npf, temp_pf);
32 FileMove(temp_pf, pf);
33 if(!b) {
34 FileMove(temp_pf, pf);
35 DeleteFolderDeep(npf);
36 Exclamation("Duplicating package folder has failed.");
37 return false;
38 }
39 }
40 else
41 if(!FileMove(temp_pf, npf)) {
42 FileMove(temp_pf, pf);
43 Exclamation("Renaming package folder has failed.");
44 return false;
45 }
46 if(!FileMove(npf + "/" + GetFileName(upp), nupp)) {
47 FileMove(npf, pf);
48 Exclamation("Renaming .upp file has failed.");
49 return false;
50 }
51 return true;
52 }
53
RenamePackageFs(const String & upp,const String & newname,bool duplicate)54 bool RenamePackageFs(const String& upp, const String& newname, bool duplicate)
55 {
56 if(IsNull(newname)) {
57 Exclamation("Wrong name.");
58 return false;
59 }
60 String npf = AppendFileName(GetPackagePathNest(GetFileFolder(upp)), newname);
61 String nupp = npf + "/" + GetFileName(newname) + ".upp";
62
63 if(FileExists(nupp)) {
64 Exclamation("Package [* \1" + newname + "\1] already exists!");
65 return false;
66 }
67
68 return RenamePackageFs(upp, npf, nupp, duplicate);
69 }
70
RenamePackage(bool duplicate)71 void SelectPackageDlg::RenamePackage(bool duplicate)
72 {
73 String n = GetCurrentName();
74 if(IsNull(n))
75 return;
76 again:
77 if(!EditText(n, duplicate ? "Duplicate package:" : "Rename package", "Name", FilterPackageName))
78 return;
79 if(!RenamePackageFs(PackagePath(GetCurrentName()), n, duplicate))
80 goto again;
81 search <<= Null;
82 Load(n);
83 alist.ScrollIntoCursor();
84 }
85
MovePackage(bool copy)86 void SelectPackageDlg::MovePackage(bool copy)
87 {
88 WithMoveCopyPackageLayout<TopWindow> dlg;
89 CtrlLayoutOKCancel(dlg, copy ? "Copy package to" : "Move package to");
90
91 String d0;
92 for(int pass = 0; pass < 2; pass++) {
93 Index<String> udir;
94 FindFile ff(ConfigFile("*.var"));
95 while(ff) {
96 if(int(GetFileTitle(ff.GetName()) != base.GetKey()) == pass) {
97 VectorMap<String, String> var;
98 LoadVarFile(ff.GetPath(), var);
99 for(String d : Split(var.Get("UPP", ""), ';'))
100 if(DirectoryExists(d)) {
101 udir.FindAdd(d);
102 d0 = Nvl(d0, d);
103 }
104 }
105 ff.Next();
106 }
107
108 Vector<String> sd = pick(udir.PickKeys());
109 Sort(sd, [](const String& a, const String& b) { return ToUpper(a) < ToUpper(b); });
110 for(String d : sd)
111 dlg.dir.AddList(d);
112 }
113
114 dlg.dir <<= d0;
115 dlg.select.SetImage(CtrlImg::Dir());
116 dlg.select << [&] { String d = SelectDirectory(); if(d.GetCount()) dlg.dir <<= d; };
117
118 dlg.name <<= GetCurrentName();
119
120 again:
121 if(dlg.Run() != IDOK)
122 return;
123
124 String dir = ~dlg.dir;
125 if(!DirectoryExists(dir)) {
126 Exclamation("Invalid target directory!");
127 goto again;
128 }
129 String pkg = AppendFileName(dir, ~~dlg.name);
130 if(DirectoryExists(pkg)) {
131 Exclamation("Target package directory already exists!");
132 goto again;
133 }
134 if(FileExists(pkg)) {
135 Exclamation("Invalid target package directory - it is a file!");
136 goto again;
137 }
138
139 if(!RenamePackageFs(PackagePath(GetCurrentName()), pkg, pkg + "/" + GetFileName(~~dlg.name) + ".upp", copy))
140 goto again;
141
142 Load(~~dlg.name);
143 }
144
DeletePackage()145 void SelectPackageDlg::DeletePackage()
146 {
147 String n = GetCurrentName();
148 if(IsNull(n))
149 return;
150 String pp = GetFileFolder(PackagePath(GetCurrentName()));
151 if(!DirectoryExists(pp)) {
152 Exclamation("Directory does not exist!");
153 return;
154 }
155 if(!PromptYesNo("Do you really want to delete package [* \1" + GetCurrentName() + "\1]?&&"
156 "[/ Warning:] [* Package will not be removed "
157 "from uses of any other package!]"))
158 return;
159 if(!PromptYesNo("This operation is irreversible.&Do you really want to proceed?"))
160 return;
161 DeleteFolderDeep(pp);
162 Load();
163 }
164
SelectPackageDlg(const char * title,bool selectvars_,bool main)165 SelectPackageDlg::SelectPackageDlg(const char *title, bool selectvars_, bool main)
166 : selectvars(selectvars_)
167 {
168 CtrlLayoutOKCancel(*this, title);
169 Sizeable().Zoomable();
170 Icon(IdeImg::MainPackage(), IdeImg::PackageLarge());
171 base.AutoHideSb();
172 base.NoGrid();
173 base.AddColumn("Assembly");
174 base.WhenCursor = THISBACK(OnBase);
175 base.WhenBar = THISBACK(ToolBase);
176 base.WhenLeftDouble = THISBACK(OnBaseEdit);
177 ok.WhenAction = clist.WhenLeftDouble = alist.WhenLeftDouble = THISBACK(OnOK);
178 cancel.WhenAction = WhenClose = THISBACK(OnCancel);
179 clist.Columns(4);
180 clist.WhenEnterItem = clist.WhenKillCursor = THISBACK(ListCursor);
181 alist.AddColumn("Package").Add(3);
182 alist.AddColumn("Nest");
183 alist.AddColumn("Description");
184 alist.AddIndex();
185 alist.ColumnWidths("108 79 317");
186 alist.WhenCursor = THISBACK(ListCursor);
187 alist.EvenRowColor();
188 alist.SetLineCy(max(Zy(16), Draw::GetStdFontCy()));
189 list.Add(clist.SizePos());
190 list.Add(alist.SizePos());
191
192 parent.Add(list.SizePos());
193 parent.AddFrame(splitter.Left(base, Zx(170)));
194 if (!selectvars)
195 splitter.Hide();
196
197 newu <<= THISBACK(OnNew);
198 filter <<= THISBACK(OnFilter);
199 filter.Add(MAIN|FIRST, "Main packages of first nest");
200 filter.Add(MAIN, "All main packages");
201 filter.Add(FIRST, "All packages of first nest");
202 filter.Add(0, "All packages");
203 filter <<= main ? MAIN|FIRST : 0;
204 progress.Hide();
205 brief <<= THISBACK(SyncBrief);
206 search.NullText("Search (Ctrl+K)", StdFont().Italic(), SColorDisabled());
207 search << [=] { SyncList(Null); };
208 search.SetFilter(CharFilterDefaultToUpperAscii);
209 SyncBrief();
210 description.NullText("Package description (Alt+Enter)", StdFont().Italic(), SColorDisabled());
211 description <<= THISBACK(ChangeDescription);
212 ActiveFocus(brief ? (Ctrl&)clist : (Ctrl&)alist);
213 clist.BackPaintHint();
214 alist.BackPaintHint();
215 base.BackPaintHint();
216 loadi = 0;
217 loading = false;
218 clist.WhenBar = alist.WhenBar = THISBACK(PackageMenu);
219
220 help << [&] { LaunchWebBrowser("https://www.ultimatepp.org/app$ide$PackagesAssembliesAndNests$en-us.html"); };
221 }
222
Key(dword key,int count)223 bool SelectPackageDlg::Key(dword key, int count)
224 {
225 if(key == K_ALT_ENTER) {
226 ChangeDescription();
227 return true;
228 }
229 else if(key == K_CTRL_K) {
230 search.SetFocus();
231 return true;
232 }
233 if((clist.HasFocus() || alist.HasFocus()) && search.Key(key, count))
234 return true;
235 return TopWindow::Key(key, count);
236 }
237
Serialize(Stream & s)238 void SelectPackageDlg::Serialize(Stream& s)
239 {
240 SerializePlacement(s);
241 s % brief;
242 }
243
GetCurrentName()244 String SelectPackageDlg::GetCurrentName()
245 {
246 if(clist.IsShown())
247 return clist.GetCurrentName();
248 else
249 if(alist.IsCursor())
250 return alist.Get(0);
251 return Null;
252 }
253
GetCurrentIndex()254 int SelectPackageDlg::GetCurrentIndex()
255 {
256 String s = GetCurrentName();
257 for(int i = 0; i < packages.GetCount(); i++)
258 if(packages[i].package == s)
259 return i;
260 return -1;
261 }
262
ChangeDescription()263 void SelectPackageDlg::ChangeDescription()
264 {
265 int ii = GetCurrentIndex();
266 if(ii >= 0 && ii < packages.GetCount()) {
267 PkInfo& p = packages[ii];
268 WithDescriptionLayout<TopWindow> dlg;
269 CtrlLayoutOKCancel(dlg, "Package description");
270 String pp = PackagePath(p.package);
271 Package pkg;
272 if(!pkg.Load(pp)) {
273 Exclamation("Package does not exist.");
274 return;
275 }
276 dlg.text <<= pkg.description;
277 if(dlg.Run() != IDOK)
278 return;
279 pkg.description = ~dlg.text;
280 pkg.Save(pp);
281 p.description = description <<= ~dlg.text;
282 if(alist.IsCursor())
283 alist.Set(2, ~dlg.text);
284 }
285 }
286
ListCursor()287 void SelectPackageDlg::ListCursor()
288 {
289 int c = GetCurrentIndex();
290 if(c >= 0 && c < packages.GetCount()) {
291 String pp = PackagePath(GetCurrentName());
292 Package pkg;
293 pkg.Load(pp);
294 description <<= pkg.description;
295 }
296 else
297 description <<= Null;
298 }
299
SyncBrief()300 void SelectPackageDlg::SyncBrief()
301 {
302 bool b = brief;
303 alist.Show(!b);
304 clist.Show(b);
305 }
306
Run(String startwith)307 String SelectPackageDlg::Run(String startwith)
308 {
309 finished = canceled = false;
310 if(!IsSplashOpen())
311 Open();
312 if(selectvars)
313 SyncBase(GetVarsName());
314 else
315 OnBase();
316 String bkvar = GetVarsName();
317 if(finished)
318 return GetCurrentName();
319 if(canceled)
320 return Null;
321 alist.FindSetCursor(startwith);
322 clist.FindSetCursor(startwith);
323 ActiveFocus(alist.IsShown() ? (Ctrl&)alist : (Ctrl&)clist);
324 switch(TopWindow::Run()) {
325 case IDOK: return GetCurrentName();
326 case IDYES: return selected;
327 default:
328 LoadVars(bkvar);
329 SyncBase(GetVarsName());
330 return Null;
331 }
332 }
333
OnOK()334 void SelectPackageDlg::OnOK()
335 {
336 Package pkg;
337 int f = ~filter;
338 String n = GetCurrentName();
339 if(n.GetCount() && pkg.Load(PackagePath(n)) &&
340 (!(f & MAIN) || pkg.config.GetCount())) {
341 loading = false;
342 finished = true;
343 AcceptBreak(IDOK);
344 }
345 }
346
OnCancel()347 void SelectPackageDlg::OnCancel()
348 {
349 loading = false;
350 canceled = true;
351 RejectBreak(IDCANCEL);
352 }
353
OnFilter()354 void SelectPackageDlg::OnFilter()
355 {
356 SyncList(Null);
357 }
358
OnBase()359 void SelectPackageDlg::OnBase()
360 {
361 if(!finished && !canceled)
362 Load();
363 }
364
OnNew()365 void SelectPackageDlg::OnNew() {
366 TemplateDlg dlg;
367 LoadFromGlobal(dlg, "NewPackage");
368 int f = ~filter;
369 dlg.Load(GetUppDirs(), f & MAIN);
370 while(dlg.Run() == IDOK) {
371 String nest = ~dlg.nest;
372 String name = NativePath(String(~dlg.package));
373 String path = AppendFileName(nest, AppendFileName(name, GetFileName(name) + ".upp"));
374 if(FileExists(path) && !PromptYesNo("Package [* \1" + path + "\1] already exists.&"
375 "Do you wish to recreate the files?"))
376 continue;
377 RealizePath(path);
378 if(!SaveFile(path, Null)) {
379 Exclamation("Error writing the file [* \1" + path + "\1].");
380 continue;
381 }
382 dlg.Create();
383 selected = name;
384 Break(IDYES);
385 break;
386 }
387 StoreToGlobal(dlg, "NewPackage");
388 }
389
GetSvnDirs()390 Vector<String> SelectPackageDlg::GetSvnDirs()
391 {
392 Vector<String> r;
393 Vector<String> dirs = SplitDirs(GetVar("UPP"));
394 for(int i = 0; i < dirs.GetCount(); i++) {
395 String d = NormalizePath(dirs[i]);
396 if(GetRepoKind(d))
397 r.Add(d);
398 }
399 return r;
400 }
401
SyncSvnDir(const String & dir)402 void SelectPackageDlg::SyncSvnDir(const String& dir)
403 {
404 RepoSyncDirs(Vector<String>() << dir);
405 Load();
406 }
407
SyncSvnDirs()408 void SelectPackageDlg::SyncSvnDirs()
409 {
410 RepoSyncDirs(GetSvnDirs());
411 Load();
412 }
413
ToolBase(Bar & bar)414 void SelectPackageDlg::ToolBase(Bar& bar)
415 {
416 bar.Add("New assembly..", THISBACK(OnBaseAdd))
417 #ifdef PLATFORM_COCOA
418 .Key(K_CTRL_N)
419 #else
420 .Key(K_INSERT)
421 #endif
422 ;
423 bar.Add(base.IsCursor(), "Edit assembly..", THISBACK(OnBaseEdit))
424 .Key(K_CTRL_ENTER);
425 bar.Add(base.IsCursor(), "Remove assembly..", THISBACK(OnBaseRemove))
426 .Key(K_CTRL_DELETE);
427 Vector<String> d = GetSvnDirs();
428 if(HasSvn()) {
429 bar.Separator();
430 bar.Add("Checkout and setup U++ SVN trunk sources..", [=] {
431 String vars = base.Get(0);
432 SetupSVNTrunk();
433 SyncBase(vars);
434 });
435 }
436 if(d.GetCount()) {
437 bar.Separator();
438 for(int i = 0; i < d.GetCount(); i++)
439 bar.Add("Synchronize " + d[i], IdeImg::svn_dir(), THISBACK1(SyncSvnDir, d[i]));
440 bar.Add("Synchronize everything..", IdeImg::svn(), THISBACK(SyncSvnDirs));
441 }
442 }
443
OnBaseAdd()444 void SelectPackageDlg::OnBaseAdd()
445 {
446 String vars;
447 if(BaseSetup(vars))
448 SyncBase(vars);
449 }
450
OnBaseEdit()451 void SelectPackageDlg::OnBaseEdit()
452 {
453 if(!base.IsCursor())
454 return;
455 String vars = base.Get(0), oldvars = vars;
456 if(BaseSetup(vars)) {
457 if(vars != oldvars)
458 DeleteFile(VarFilePath(oldvars));
459 DeleteFile(CachePath(vars));
460 SyncBase(vars);
461 }
462 }
463
OnBaseRemove()464 void SelectPackageDlg::OnBaseRemove()
465 {
466 int c = base.GetCursor();
467 if(c < 0)
468 return;
469 String next;
470 if(c + 1 < base.GetCount())
471 next = base.Get(c + 1);
472 else if(c > 0)
473 next = base.Get(c - 1);
474 String vars = base.Get(0);
475 String varpath = VarFilePath(vars);
476 if(PromptOKCancel(Format("Remove base file [* \1%s\1]?", varpath))) {
477 if(!FileDelete(varpath))
478 Exclamation(Format("Error deleting file [* \1%s\1].", varpath));
479 else
480 SyncBase(next);
481 }
482 }
483
DirSep(int c)484 int DirSep(int c)
485 {
486 return c == '\\' || c == '/' ? c : 0;
487 }
488
489 struct PackageDisplay : Display {
490 Font fnt;
491
GetStdSizePackageDisplay492 virtual Size GetStdSize(const Value& q) const {
493 ValueArray va = q;
494 Size sz = GetTextSize(String(va[0]), fnt);
495 sz.cx += Zx(20);
496 sz.cy = max(sz.cy, Zy(16));
497 return sz;
498 }
499
PaintPackageDisplay500 virtual void Paint(Draw& w, const Rect& r, const Value& q, Color ink, Color paper, dword style) const {
501 ValueArray va = q;
502 String txt = va[0];
503 Image icon = va[1];
504 if(IsNull(icon))
505 icon = IdeImg::Package();
506 else
507 icon = DPI(icon, 16);
508 w.DrawRect(r, paper);
509 w.DrawImage(r.left, r.top + (r.Height() - icon.GetHeight()) / 2, icon);
510 w.DrawText(r.left + DPI(20), r.top + (r.Height() - Draw::GetStdFontCy()) / 2, txt, fnt, ink);
511 }
512
PackageDisplayPackageDisplay513 PackageDisplay() { fnt = StdFont(); }
514 };
515
SyncList(const String & find)516 void SelectPackageDlg::SyncList(const String& find)
517 {
518 String n = Nvl(find, GetCurrentName());
519 int asc = alist.GetScroll();
520 int csc = clist.GetSbPos();
521
522 packages.Clear();
523 String s = ~search;
524 int f = ~filter;
525 Index<String> added;
526 for(int i = 0; i < min((f & FIRST) ? 1 : data.GetCount(), data.GetCount()); i++) {
527 const ArrayMap<String, PkData>& nest = data[i];
528 for(int i = 0; i < nest.GetCount(); i++) {
529 const PkData& d = nest[i];
530 if(!nest.IsUnlinked(i) &&
531 d.ispackage &&
532 (!(f & MAIN) || d.main) &&
533 ToUpper(d.package + d.description + d.nest).Find(s) >= 0 &&
534 added.Find(d.package) < 0) {
535 packages.Add() = d;
536 added.Add(d.package);
537 }
538 }
539 }
540 Sort(packages);
541 alist.Clear();
542 clist.Clear();
543 ListCursor();
544 static PackageDisplay pd, bpd;
545 bpd.fnt.Bold();
546 for(int i = 0; i < packages.GetCount(); i++) {
547 const PkInfo& pkg = packages[i];
548 Image icon = pkg.icon;
549 if(IsNull(icon))
550 icon = pkg.main ? IdeImg::MainPackage() : IdeImg::Package();
551 clist.Add(pkg.package, DPI(icon, 16));
552 alist.Add(pkg.package, pkg.nest, pkg.description, icon);
553 alist.SetDisplay(alist.GetCount() - 1, 0, pkg.main ? bpd : pd);
554 }
555 if(!alist.FindSetCursor(n))
556 alist.GoBegin();
557 if(!clist.FindSetCursor(n) && clist.GetCount())
558 clist.SetCursor(0);
559 alist.ScrollTo(asc);
560 clist.SetSbPos(csc);
561 alist.HeaderTab(0).SetText("Package (" + AsString(alist.GetCount()) + ")");
562 }
563
ScanFolder(const String & path,ArrayMap<String,PkData> & nd,const String & nest,Index<String> & dir_exists,const String & prefix)564 void SelectPackageDlg::ScanFolder(const String& path, ArrayMap<String, PkData>& nd,
565 const String& nest, Index<String>& dir_exists,
566 const String& prefix)
567 {
568 for(FindFile ff(AppendFileName(path, "*.*")); ff; ff.Next())
569 if(ff.IsFolder() && !ff.IsHidden()) {
570 dir_exists.Add(ff.GetPath());
571 String p = ff.GetPath();
572 bool nw = nd.Find(p) < 0; // Do we have any info loaded about this package?
573 PkData& d = nd.GetAdd(ff.GetPath());
574 d.package = prefix + ff.GetName();
575 d.nest = nest;
576 if(nw) { // No cached info available about the folder
577 d.ispackage = IsLetter(*d.package) && d.package.Find('.') < 0; // First heuristic guess
578 d.main = d.ispackage && prefix.GetCount() == 0; // Expect it is main
579 }
580 }
581 }
582
CachePath(const char * vn) const583 String SelectPackageDlg::CachePath(const char *vn) const
584 {
585 return AppendFileName(ConfigFile("cfg"), String(vn) + ".pkg_cache");
586 }
587
Load(const String & find)588 void SelectPackageDlg::Load(const String& find)
589 {
590 if(selectvars && !base.IsCursor())
591 return;
592 if(loading) { // If we are called recursively from ProcessEvents, stop current loading and change loadi
593 loadi++;
594 loading = false;
595 return;
596 }
597 int current_loadi = -1;
598 while(current_loadi != loadi) {
599 current_loadi = loadi;
600 if(selectvars) {
601 String assembly = (String)base.Get(0);
602 list.Enable(base.IsCursor());
603 if(!base.IsCursor())
604 return;
605 LoadVars(assembly);
606 }
607 Vector<String> upp = GetUppDirs();
608 packages.Clear();
609 description.Hide();
610 progress.Show();
611 loading = true;
612 data.Clear();
613 Index<String> dir_exists;
614 String cache_path = CachePath(GetVarsName());
615 LoadFromFile(data, cache_path);
616 data.SetCount(upp.GetCount());
617 for(int i = 0; i < upp.GetCount(); i++) // Scan nest folders for subfolders (additional package candidates)
618 ScanFolder(upp[i], data[i], GetFileName(upp[i]), dir_exists, Null);
619 int update = msecs();
620 for(int i = 0; i < data.GetCount() && loading; i++) { // Now investigate individual sub folders
621 ArrayMap<String, PkData>& nest = data[i];
622 String nest_dir = NormalizePath(upp[i]);
623 for(int i = 0; i < nest.GetCount() && loading; i++) {
624 if(msecs(update) >= 100) { // each 100 ms update the list (and open select dialog after splash screen is closed)
625 if(!IsSplashOpen() && !IsOpen())
626 Open();
627 progress++;
628 SyncList(find);
629 update = msecs();
630 }
631 ProcessEvents(); // keep GUI running
632
633 PkData& d = nest[i];
634 String path = nest.GetKey(i);
635 if(NormalizePath(path).StartsWith(nest_dir) && DirectoryExists(path)) {
636 String upp_path = AppendFileName(path, GetFileName(d.package) + ".upp");
637 LSLOW(); // this is used for testing only, normally it is NOP
638 Time tm = FileGetTime(upp_path);
639 if(IsNull(tm)) // .upp file does not exist - not a package
640 d.ispackage = false;
641 else
642 if(tm != d.tm) { // cached info is outdated
643 Package p;
644 if(p.Load(upp_path)) {
645 d.description = p.description;
646 d.main = p.config.GetCount();
647 d.tm = tm;
648 d.ispackage = true;
649 }
650 else
651 d.ispackage = false;
652 }
653 else
654 d.ispackage = true;
655 if(d.ispackage) {
656 String icon_path;
657 if(IsUHDMode())
658 icon_path = AppendFileName(path, "icon32x32.png");
659 if(IsNull(icon_path) || !FileExists(icon_path))
660 icon_path = AppendFileName(path, "icon16x16.png");
661 tm = FileGetTime(icon_path);
662 if(IsNull(tm)) // package icon does not exist
663 d.icon = Null;
664 else
665 if(tm != d.itm) { // chached package icon outdated
666 d.icon = StreamRaster::LoadFileAny(icon_path);
667 d.itm = tm;
668 }
669 }
670 ScanFolder(path, nest, d.nest, dir_exists, d.package + '/');
671 }
672 else
673 nest.Unlink(i); // cached folder was deleted or is not in nest dir
674 }
675 nest.Sweep();
676 }
677
678 StoreToFile(data, cache_path);
679 progress.Hide();
680 while(IsSplashOpen())
681 ProcessEvents();
682 if(!IsOpen())
683 Open();
684 description.Show();
685 if(loading) {
686 loading = false;
687 SyncList(find);
688 }
689 }
690 }
691
SyncBase(String initvars)692 void SelectPackageDlg::SyncBase(String initvars)
693 {
694 Vector<String> varlist;
695 for(FindFile ff(ConfigFile("*.var")); ff; ff.Next())
696 if(ff.IsFile())
697 varlist.Add(GetFileTitle(ff.GetName()));
698 Sort(varlist, &PackageLess);
699 base.Clear();
700 Append(base, varlist);
701 if(!base.FindSetCursor(initvars)) {
702 if(base.GetCount() > 0)
703 base.SetCursor(0);
704 else
705 OnBase();
706 }
707 }
708
Pless(const SelectPackageDlg::PkInfo & a,const SelectPackageDlg::PkInfo & b)709 bool SelectPackageDlg::Pless(const SelectPackageDlg::PkInfo& a, const SelectPackageDlg::PkInfo& b)
710 {
711 return PackageLess(a.package, b.package);
712 }
713
714 INITBLOCK
715 {
716 RegisterGlobalConfig("SelectPkgMain");
717 RegisterGlobalConfig("SelectPkg");
718 }
719
720 String SelectPackage(const char *title, const char *startwith, bool selectvars, bool main)
721 {
722 SelectPackageDlg dlg(title, selectvars, main);
723 const char *c = main ? "SelectPkgMain" : "SelectPkg";
724 LoadFromGlobal(dlg, c);
725 dlg.SyncBrief();
726 String b = dlg.Run(startwith);
727 StoreToGlobal(dlg, c);
728 return b;
729 }
730