mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-15 06:05:58 -06:00
1033 lines
26 KiB
C++
1033 lines
26 KiB
C++
#include "ide.h"
|
|
|
|
#ifdef _DEBUG
|
|
#define LSLOW() // Sleep(20) // Simulate HD seeks to test package cache
|
|
#else
|
|
#define LSLOW()
|
|
#endif
|
|
|
|
void SelectPackageDlg::PackageMenu(Bar& menu)
|
|
{
|
|
bool b = GetCurrentName().GetCount();
|
|
menu.Add("New package..", [=] { OnNew(); });
|
|
menu.Separator();
|
|
if(IsExternalMode()) {
|
|
menu.Add(b, "Delete package..", [=] {
|
|
String p = PackageFile(GetCurrentName());
|
|
if(FileExists(p) && PromptYesNo("Delete package?")) {
|
|
DeleteFile(p);
|
|
Load();
|
|
}
|
|
});
|
|
}
|
|
else {
|
|
menu.Add(b, "Duplicate package..", [=] { RenamePackage(true); });
|
|
menu.Add(b, "Rename package..", [=] { RenamePackage(false); });
|
|
menu.Add(b, "Copy package to..", [=] { MovePackage(true); });
|
|
menu.Add(b, "Move package to..", [=] { MovePackage(false); });
|
|
menu.Add(b, "Delete package..", [=] { DeletePackage(); });
|
|
}
|
|
menu.Add(b, "Change description..", [=] { ChangeDescription(); });
|
|
if(b) {
|
|
menu.Separator();
|
|
String dir = PackageDirectory(GetCurrentName());
|
|
menu.Add(b, "Open package directory", [=] { ShellOpenFolder(dir); });
|
|
menu.Add(b, "Terminal at package directory", IdeImg::Terminal(), [=] { TheIde()->LaunchTerminal(dir); });
|
|
}
|
|
}
|
|
|
|
bool RenamePackageFs(const String& upp, const String& npf, const String& nupp, bool copy)
|
|
{
|
|
String pf = GetFileFolder(upp);
|
|
String temp_pf = AppendFileName(GetFileFolder(pf), AsString(Random()) + AsString(Random()));
|
|
if(!FileMove(pf, temp_pf)) {
|
|
Exclamation("Operation has failed.");
|
|
return false;
|
|
}
|
|
RealizePath(GetFileFolder(npf));
|
|
if(copy) {
|
|
bool b = CopyFolder(npf, temp_pf);
|
|
FileMove(temp_pf, pf);
|
|
if(!b) {
|
|
FileMove(temp_pf, pf);
|
|
DeleteFolderDeep(npf);
|
|
Exclamation("Duplicating package folder has failed.");
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
if(!FileMove(temp_pf, npf)) {
|
|
FileMove(temp_pf, pf);
|
|
Exclamation("Renaming package folder has failed.");
|
|
return false;
|
|
}
|
|
if(!FileMove(npf + "/" + GetFileName(upp), nupp)) {
|
|
FileMove(npf, pf);
|
|
Exclamation("Renaming .upp file has failed.");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool RenamePackageFs(const String& upp, const String& newname, bool duplicate)
|
|
{
|
|
if(IsNull(newname)) {
|
|
Exclamation("Wrong name.");
|
|
return false;
|
|
}
|
|
String npf = AppendFileName(GetPathNest(GetFileFolder(upp)), newname);
|
|
String nupp = npf + "/" + GetFileName(newname) + ".upp";
|
|
|
|
if(FileExists(nupp)) {
|
|
Exclamation("Package [* \1" + newname + "\1] already exists!");
|
|
return false;
|
|
}
|
|
|
|
return RenamePackageFs(upp, npf, nupp, duplicate);
|
|
}
|
|
|
|
void SelectPackageDlg::RenamePackage(bool duplicate)
|
|
{
|
|
String n = GetCurrentName();
|
|
if(IsNull(n))
|
|
return;
|
|
again:
|
|
if(!EditText(n, duplicate ? "Duplicate package:" : "Rename package", "Name", FilterPackageName))
|
|
return;
|
|
if(!RenamePackageFs(PackageFile(GetCurrentName()), n, duplicate))
|
|
goto again;
|
|
search <<= Null;
|
|
Load(n);
|
|
alist.ScrollIntoCursor();
|
|
}
|
|
|
|
void SelectPackageDlg::MovePackage(bool copy)
|
|
{
|
|
if(IsExternalMode())
|
|
return;
|
|
|
|
WithMoveCopyPackageLayout<TopWindow> dlg;
|
|
CtrlLayoutOKCancel(dlg, copy ? "Copy package to" : "Move package to");
|
|
|
|
String d0;
|
|
for(int pass = 0; pass < 2; pass++) {
|
|
Index<String> udir;
|
|
FindFile ff(ConfigFile("*.var"));
|
|
while(ff) {
|
|
if(int(GetFileTitle(ff.GetName()) != base.GetKey()) == pass) {
|
|
VectorMap<String, String> var;
|
|
LoadVarFile(ff.GetPath(), var);
|
|
for(String d : Split(var.Get("UPP", ""), ';'))
|
|
if(DirectoryExists(d)) {
|
|
udir.FindAdd(d);
|
|
d0 = Nvl(d0, d);
|
|
}
|
|
}
|
|
ff.Next();
|
|
}
|
|
|
|
Vector<String> sd = pick(udir.PickKeys());
|
|
Sort(sd, [](const String& a, const String& b) { return ToUpper(a) < ToUpper(b); });
|
|
for(String d : sd)
|
|
dlg.dir.AddList(d);
|
|
}
|
|
|
|
dlg.dir <<= d0;
|
|
DirSelect(dlg.dir, dlg.select);
|
|
|
|
dlg.name <<= GetCurrentName();
|
|
|
|
again:
|
|
if(dlg.Run() != IDOK)
|
|
return;
|
|
|
|
String dir = ~dlg.dir;
|
|
if(!DirectoryExists(dir)) {
|
|
Exclamation("Invalid target directory!");
|
|
goto again;
|
|
}
|
|
String pkg = AppendFileName(dir, ~~dlg.name);
|
|
if(DirectoryExists(pkg)) {
|
|
Exclamation("Target package directory already exists!");
|
|
goto again;
|
|
}
|
|
if(FileExists(pkg)) {
|
|
Exclamation("Invalid target package directory - it is a file!");
|
|
goto again;
|
|
}
|
|
|
|
if(!RenamePackageFs(PackageFile(GetCurrentName()), pkg, pkg + "/" + GetFileName(~~dlg.name) + ".upp", copy))
|
|
goto again;
|
|
|
|
Load(~~dlg.name);
|
|
}
|
|
|
|
void SelectPackageDlg::DeletePackage()
|
|
{
|
|
String n = GetCurrentName();
|
|
if(IsNull(n))
|
|
return;
|
|
String pp = PackageDirectory(GetCurrentName());
|
|
if(!DirectoryExists(pp)) {
|
|
Exclamation("Directory does not exist!");
|
|
return;
|
|
}
|
|
if(!PromptYesNo("Do you really want to delete package [* \1" + GetCurrentName() + "\1]?&&"
|
|
"[/ Warning:] [* Package will not be removed "
|
|
"from uses of any other package!]"))
|
|
return;
|
|
if(!PromptYesNo("This operation is irreversible.&Do you really want to proceed?"))
|
|
return;
|
|
DeleteFolderDeep(pp);
|
|
Load();
|
|
}
|
|
|
|
SelectPackageDlg::SelectPackageDlg(const char *title, bool selectvars_, bool main)
|
|
: selectvars(selectvars_)
|
|
{
|
|
CtrlLayoutOKCancel(*this, title);
|
|
Sizeable().Zoomable();
|
|
Icon(IdeImg::MainPackage(), IdeImg::PackageLarge());
|
|
base.AutoHideSb();
|
|
base.NoGrid();
|
|
base.AddIndex();
|
|
base.AddColumn("Assembly");
|
|
base.WhenCursor = THISBACK(OnBase);
|
|
base.WhenBar = THISBACK(ToolBase);
|
|
base.WhenLeftDouble = THISBACK(OnBaseEdit);
|
|
ok.WhenAction = clist.WhenLeftDouble = alist.WhenLeftDouble = THISBACK(OnOK);
|
|
cancel.WhenAction = WhenClose = THISBACK(OnCancel);
|
|
clist.Columns(4);
|
|
alist.AddColumn("Package").Add(3);
|
|
alist.AddColumn("Nest");
|
|
alist.AddColumn("Description");
|
|
alist.AddIndex();
|
|
alist.EvenRowColor();
|
|
alist.SetLineCy(max(DPI(16), Draw::GetStdFontCy()));
|
|
alist.ColumnWidths("108 79 317");
|
|
list.Add(clist.SizePos());
|
|
list.Add(alist.SizePos());
|
|
|
|
lists_status.SetDefault("");
|
|
lists_status.NoSizeGrip();
|
|
lists_status.Height(Zy(10));
|
|
lists_status.Add(progress.SizePos());
|
|
|
|
parent.Add(list.SizePos());
|
|
parent.AddFrame(splitter.Left(base, Zx(170)));
|
|
|
|
recent.NoSb().NoHyperlinkDecoration();
|
|
LoadLRU();
|
|
if(main && selectvars && lru.GetCount()) {
|
|
String text;
|
|
for(int i = 0; i < lru.GetCount(); i++) {
|
|
const auto& m = lru[i];
|
|
MergeWith(text, ", ", "[@b^A" + AsString(i) + "^ \1" + m.a + "\1]" +
|
|
":_[*^ "+ AsString(i) + "^ \1" + m.b + "\1]");
|
|
}
|
|
if(text.GetCount())
|
|
recent <<= "[gpw [@K/ Recent:] " + text;
|
|
Rect r = recent.GetRect();
|
|
Rect pg = recent.GetPage();
|
|
RichText txt = ParseQTF(recent.GetQTF());
|
|
int cy = 0;
|
|
for(int i = 0; i < txt.GetLength(); i++) { // Find the height of text that completely fits
|
|
Rect cr = txt.GetCaret(i, pg);
|
|
if(cr.bottom > r.GetHeight()) // does not fit
|
|
break;
|
|
cy = cr.bottom;
|
|
}
|
|
r.bottom = r.top + cy;
|
|
recent.SetRect(r);
|
|
recent.WhenLink = [=](const String& s) {
|
|
if(*s == 'A') {
|
|
int i = Atoi(~s + 1);
|
|
if(i >= 0 && lru.GetCount())
|
|
base.FindSetCursor(lru[i].a);
|
|
}
|
|
else {
|
|
int i = Atoi(s);
|
|
if(i >= 0 && lru.GetCount()) {
|
|
selected = lru[i].b;
|
|
LoadVars(lru[i].a);
|
|
selected_nest = GetPathNest(PackageDirectory(selected));
|
|
Break(IDYES);
|
|
}
|
|
}
|
|
};
|
|
LogPos p = parent.GetPos();
|
|
p.y.SetA(r.bottom + DPI(3));
|
|
parent.SetPos(p);
|
|
}
|
|
else {
|
|
LogPos p = parent.GetPos();
|
|
p.y.SetA(recent.GetPos().y.GetA());
|
|
parent.SetPos(p);
|
|
}
|
|
|
|
if (!selectvars)
|
|
splitter.Hide();
|
|
|
|
newu << [=] { OnNew(); };
|
|
kind.Add(MAIN, "Main packages");
|
|
kind.Add(NONMAIN, "Non-main packages");
|
|
kind.Add(ALL, "All packages");
|
|
kind << [=] { OnFilter(); };
|
|
kind <<= main ? MAIN : NONMAIN;
|
|
nest << [=] { OnFilter(); };
|
|
OnFilter();
|
|
nest <<= main ? 0 : ALL;
|
|
brief <<= THISBACK(SyncBrief);
|
|
search.NullText("Search (Ctrl+K)", StdFont().Italic(), SColorDisabled());
|
|
search << [=] { SyncList(Null); };
|
|
search.SetFilter(CharFilterDefaultToUpperAscii);
|
|
SyncBrief();
|
|
ActiveFocus(brief ? (Ctrl&)clist : (Ctrl&)alist);
|
|
clist.BackPaintHint();
|
|
alist.BackPaintHint();
|
|
base.BackPaintHint();
|
|
loadi = 0;
|
|
loading = false;
|
|
clist.WhenBar = alist.WhenBar = THISBACK(PackageMenu);
|
|
|
|
upphub.SetImage(IdeImg::UppHub());
|
|
upphub << [=] {
|
|
String p = UppHub();
|
|
OnBase();
|
|
if(p.GetCount()) {
|
|
alist.FindSetCursor(p);
|
|
clist.FindSetCursor(p);
|
|
}
|
|
};
|
|
|
|
IdeHelpButton(help, "PackagesAssembliesAndNests");
|
|
|
|
String exf = VarFilePath("[external]");
|
|
if(!FileExists(exf))
|
|
SaveFile(exf, "SOURCE_MASKS = \"*.cpp *.h *.hpp *.c *.cxx *.cc *.m *.mm *.cs *.java *.js *.ts *.tsx *.jsx\";");
|
|
}
|
|
|
|
String SelectPackageDlg::LRUFilePath()
|
|
{
|
|
return ConfigFile("main.lru");
|
|
}
|
|
|
|
void SelectPackageDlg::LoadLRU()
|
|
{
|
|
LoadFromFile(lru, LRUFilePath());
|
|
Index<Tuple<String, String>> lu; // prevent duplicates, \ vs /
|
|
for(auto h : lru) {
|
|
h.b = UnixPath(h.b);
|
|
lu.FindAdd(h);
|
|
}
|
|
lru = lu.PickKeys();
|
|
}
|
|
|
|
void SelectPackageDlg::StoreLRU(const String& p)
|
|
{
|
|
auto q = Tuple(GetVarsName(), p);
|
|
LoadLRU();
|
|
int i = FindIndex(lru, q);
|
|
if(i >= 0)
|
|
lru.Remove(i);
|
|
lru.Insert(0, q);
|
|
if(lru.GetCount() > 100)
|
|
lru.Trim(100);
|
|
StoreToFile(lru, LRUFilePath());
|
|
}
|
|
|
|
void SelectPackageDlg::SyncFilter()
|
|
{
|
|
Value n = ~nest;
|
|
nest.ClearList();
|
|
Vector<String> upp = GetUppDirsRaw();
|
|
nest.Add(ALL, AttrText("All").Italic().Bold().NormalInk(SBlue()));
|
|
bool hub = false;
|
|
for(int i = 0; i < upp.GetCount(); i++) {
|
|
if(upp[i].StartsWith(GetHubDir()) && !hub) {
|
|
nest.Add(UPPHUB|i, AttrText("UppHub").Italic().NormalInk(SBlue()));
|
|
hub = true;
|
|
}
|
|
String fn = GetFileName(upp[i]);
|
|
if(hub)
|
|
nest.Add(i, AttrText(fn).NormalInk(SCyan()));
|
|
else
|
|
nest.Add(i, fn);
|
|
}
|
|
if(nest.HasKey(n))
|
|
nest <<= n;
|
|
else
|
|
nest.GoBegin();
|
|
}
|
|
|
|
bool SelectPackageDlg::Key(dword key, int count)
|
|
{
|
|
if(key == K_ALT_ENTER) {
|
|
ChangeDescription();
|
|
return true;
|
|
}
|
|
else if(key == K_CTRL_K) {
|
|
search.SetFocus();
|
|
return true;
|
|
}
|
|
if((clist.HasFocus() || alist.HasFocus()) && search.Key(key, count))
|
|
return true;
|
|
return TopWindow::Key(key, count);
|
|
}
|
|
|
|
void SelectPackageDlg::Serialize(Stream& s)
|
|
{
|
|
SerializePlacement(s);
|
|
s % brief;
|
|
}
|
|
|
|
String SelectPackageDlg::GetCurrentName()
|
|
{
|
|
String n;
|
|
if(clist.IsShown())
|
|
n = clist.GetCurrentName();
|
|
else
|
|
if(alist.IsCursor())
|
|
n = alist.Get(0);
|
|
else
|
|
return Null;
|
|
return n;
|
|
}
|
|
|
|
String SelectPackageDlg::GetCurrentNest()
|
|
{
|
|
int i = clist.IsShown() ? clist.GetCursor() : alist.GetCursor();
|
|
String f = GetCurrentName();
|
|
return i >= 0 && i < nest_list.GetCount() ? nest_list[i] : String();
|
|
}
|
|
|
|
int SelectPackageDlg::GetCurrentIndex()
|
|
{
|
|
String s = GetCurrentName();
|
|
for(int i = 0; i < packages.GetCount(); i++)
|
|
if(packages[i].package == s)
|
|
return i;
|
|
return -1;
|
|
}
|
|
|
|
void SelectPackageDlg::ChangeDescription()
|
|
{
|
|
int ii = GetCurrentIndex();
|
|
if(ii >= 0 && ii < packages.GetCount()) {
|
|
PkInfo& p = packages[ii];
|
|
WithDescriptionLayout<TopWindow> dlg;
|
|
CtrlLayoutOKCancel(dlg, "Package description");
|
|
String pp = PackageFile(p.package);
|
|
Package pkg;
|
|
if(!pkg.Load(pp) && !IsExternalMode()) {
|
|
Exclamation("Package does not exist.");
|
|
return;
|
|
}
|
|
dlg.text <<= pkg.description;
|
|
if(dlg.Run() != IDOK)
|
|
return;
|
|
pkg.description = ~dlg.text;
|
|
pkg.Save(pp);
|
|
p.description = ~dlg.text;
|
|
if(alist.IsCursor())
|
|
alist.Set(2, ~dlg.text);
|
|
}
|
|
}
|
|
|
|
void SelectPackageDlg::SyncBrief()
|
|
{
|
|
bool b = brief;
|
|
alist.Show(!b);
|
|
clist.Show(b);
|
|
}
|
|
|
|
String SelectPackageDlg::Run(String& nest, String startwith)
|
|
{
|
|
finished = canceled = false;
|
|
if(!IsSplashOpen())
|
|
Open();
|
|
if(selectvars)
|
|
SyncBase(GetVarsName());
|
|
else
|
|
OnBase();
|
|
String bkvar = GetVarsName();
|
|
if(finished)
|
|
return GetCurrentName();
|
|
if(canceled)
|
|
return Null;
|
|
alist.FindSetCursor(startwith);
|
|
clist.FindSetCursor(startwith);
|
|
ActiveFocus(alist.IsShown() ? (Ctrl&)alist : (Ctrl&)clist);
|
|
switch(TopWindow::Run()) {
|
|
case IDOK:
|
|
nest = GetCurrentNest();
|
|
return GetCurrentName();
|
|
case IDYES:
|
|
nest = selected_nest;
|
|
return selected;
|
|
default:
|
|
LoadVars(bkvar);
|
|
SyncFilter();
|
|
SyncBase(GetVarsName());
|
|
return Null;
|
|
}
|
|
}
|
|
|
|
void SelectPackageDlg::OnOK()
|
|
{
|
|
Package pkg;
|
|
int fk = ~kind;
|
|
String n = GetCurrentName();
|
|
if(IsExternalMode() && (clist.IsShown() && clist.IsCursor() || alist.IsCursor())
|
|
||
|
|
n.GetCount() && pkg.Load(PackageFile(n)) &&
|
|
(!(fk == MAIN) || pkg.config.GetCount()) &&
|
|
(!(fk == NONMAIN) || !pkg.config.GetCount())) {
|
|
loading = false;
|
|
finished = true;
|
|
AcceptBreak(IDOK);
|
|
}
|
|
}
|
|
|
|
void SelectPackageDlg::OnCancel()
|
|
{
|
|
loading = false;
|
|
canceled = true;
|
|
RejectBreak(IDCANCEL);
|
|
}
|
|
|
|
void SelectPackageDlg::OnFilter()
|
|
{
|
|
SyncList(Null);
|
|
}
|
|
|
|
String SelectExternalPackage(const String& from)
|
|
{
|
|
FileSel fs;
|
|
fs.ActiveDir(GetHomeDirectory());
|
|
LoadFromGlobal(fs, "PackageDirSelector");
|
|
if(from.GetCount())
|
|
fs.ActiveDir(from);
|
|
bool b = fs.ExecuteSelectDir("Select package directory");
|
|
StoreToGlobal(fs, "PackageDirSelector");
|
|
return b ? ~fs : String::GetVoid();
|
|
}
|
|
|
|
void SelectPackageDlg::OnBase()
|
|
{
|
|
if(!finished && !canceled) {
|
|
SyncFilter();
|
|
if(splitter.IsShown())
|
|
nest <<= nest.GetCount() ? 0 : ALL;
|
|
Load();
|
|
newu.SetLabel(IsExternalMode() ? "New project" : "New package");
|
|
}
|
|
}
|
|
|
|
void SelectPackageDlg::OnNew() {
|
|
if(IsExternalMode()) {
|
|
String n = SelectExternalPackage();
|
|
if(IsNull(n))
|
|
return;
|
|
String f = PackageFile(n);
|
|
if(!FileExists(f)) {
|
|
Package p;
|
|
p.config.Add();
|
|
p.Save(f);
|
|
}
|
|
selected = n;
|
|
selected_nest.Clear();
|
|
Break(IDYES);
|
|
return;
|
|
}
|
|
TemplateDlg dlg;
|
|
LoadFromGlobal(dlg, "NewPackage");
|
|
dlg.Load(GetUppDirsRaw(), ~kind == MAIN);
|
|
while(dlg.Run() == IDOK) {
|
|
String nest = ~dlg.nest;
|
|
String name = NativePath(String(~dlg.package));
|
|
String path = AppendFileName(AppendFileName(nest, name), GetFileName(name) + ".upp");
|
|
if(FileExists(path) && !PromptYesNo("Package [* \1" + path + "\1] already exists.&"
|
|
"Do you wish to recreate the files?"))
|
|
continue;
|
|
RealizePath(path);
|
|
if(!SaveFile(path, Null)) {
|
|
Exclamation("Error writing the file [* \1" + path + "\1].");
|
|
continue;
|
|
}
|
|
dlg.Create();
|
|
selected_nest = nest;
|
|
selected = name;
|
|
Break(IDYES);
|
|
break;
|
|
}
|
|
StoreToGlobal(dlg, "NewPackage");
|
|
}
|
|
|
|
Vector<String> SelectPackageDlg::GetRepoDirs()
|
|
{
|
|
Vector<String> r;
|
|
Vector<String> dirs = SplitDirs(GetVar("UPP"));
|
|
for(int i = 0; i < dirs.GetCount(); i++) {
|
|
String d = NormalizePath(dirs[i]);
|
|
if(GetRepoKind(d))
|
|
r.Add(d);
|
|
}
|
|
return r;
|
|
}
|
|
|
|
void SelectPackageDlg::SyncRepoDir(const String& dir)
|
|
{
|
|
RepoSyncDirs(Vector<String>() << dir);
|
|
Load();
|
|
}
|
|
|
|
void SelectPackageDlg::SyncRepoDirs()
|
|
{
|
|
RepoSyncDirs(GetRepoDirs());
|
|
Load();
|
|
}
|
|
|
|
void SelectPackageDlg::ToolBase(Bar& bar)
|
|
{
|
|
bar.Add("New assembly..", THISBACK(OnBaseAdd))
|
|
#ifdef PLATFORM_COCOA
|
|
.Key(K_CTRL_N)
|
|
#else
|
|
.Key(K_INSERT)
|
|
#endif
|
|
;
|
|
bar.Add(base.IsCursor(), "Edit assembly..", THISBACK(OnBaseEdit))
|
|
.Key(K_CTRL_ENTER);
|
|
if(!IsExternalMode()) {
|
|
bar.Add(base.IsCursor(), "Remove assembly..", THISBACK(OnBaseRemove))
|
|
.Key(K_CTRL_DELETE);
|
|
bar.Add("Purge assemblies..", [=] { RemoveInvalid(); });
|
|
Vector<String> dirs = SplitDirs(GetVar("UPP"));
|
|
if(dirs.GetCount()) {
|
|
bar.Separator();
|
|
for(String s : dirs)
|
|
bar.Add("Terminal at " + s, IdeImg::Terminal(), [=] { TheIde()->LaunchTerminal(s); });
|
|
}
|
|
Vector<String> d = GetRepoDirs();
|
|
if(HasGit()) {
|
|
bar.Separator();
|
|
bar.Add("Clone U++ GitHub sources..", [=] {
|
|
String vars = base.Get(0);
|
|
SetupGITMaster();
|
|
SyncBase(vars);
|
|
});
|
|
}
|
|
if(d.GetCount()) {
|
|
bar.Separator();
|
|
for(int i = 0; i < d.GetCount(); i++)
|
|
bar.Add("Synchronize " + d[i], IdeImg::svn_dir(), THISBACK1(SyncRepoDir, d[i]));
|
|
bar.Add("Synchronize everything..", IdeImg::svn(), THISBACK(SyncRepoDirs));
|
|
}
|
|
}
|
|
}
|
|
|
|
void SelectPackageDlg::OnBaseAdd()
|
|
{
|
|
String vars;
|
|
if(BaseSetup(vars))
|
|
SyncBase(vars);
|
|
}
|
|
|
|
void SelectPackageDlg::OnBaseEdit()
|
|
{
|
|
if(!base.IsCursor())
|
|
return;
|
|
if(IsExternalMode()) {
|
|
WithExtSetupLayout<TopWindow> dlg;
|
|
CtrlLayoutOKCancel(dlg, "External mode settings");
|
|
dlg.source_masks <<= GetVar("SOURCE_MASKS");
|
|
if(dlg.ExecuteOK())
|
|
SetVar("SOURCE_MASKS", ~dlg.source_masks);
|
|
return;
|
|
}
|
|
String vars = base.Get(0), oldvars = vars;
|
|
if(BaseSetup(vars)) {
|
|
if(vars != oldvars)
|
|
DeleteFile(VarFilePath(oldvars));
|
|
DeleteFile(CachePath(vars));
|
|
SyncBase(vars);
|
|
}
|
|
}
|
|
|
|
void SelectPackageDlg::OnBaseRemove()
|
|
{
|
|
int c = base.GetCursor();
|
|
if(c < 0)
|
|
return;
|
|
String next;
|
|
if(c + 1 < base.GetCount())
|
|
next = base.Get(c + 1);
|
|
else if(c > 0)
|
|
next = base.Get(c - 1);
|
|
String vars = base.Get(0);
|
|
String varpath = VarFilePath(vars);
|
|
if(PromptOKCancel(Format("Remove base file [* \1%s\1]?", varpath))) {
|
|
if(!FileDelete(varpath))
|
|
Exclamation(Format("Error deleting file [* \1%s\1].", varpath));
|
|
else
|
|
SyncBase(next);
|
|
}
|
|
}
|
|
|
|
void SelectPackageDlg::RemoveInvalid()
|
|
{
|
|
String vars = base.GetKey();
|
|
WithRemoveInvalidAssembliesLayout<TopWindow> dlg;
|
|
CtrlLayoutOKCancel(dlg, "Remove assemblies");
|
|
dlg.list.AddColumn("Remove")
|
|
.Ctrls([=](int, One<Ctrl> &c) { c.Create<Option>().NoWantFocus(); });
|
|
dlg.list.AddColumn("Assembly");
|
|
dlg.list.AddColumn("Error", 7);
|
|
dlg.list.ColumnWidths("53 125 499");
|
|
Vector<String> oks;
|
|
for(int i = 0; i < base.GetCount(); i++) {
|
|
String vars = base.Get(i, 0);
|
|
if(vars == "[external]")
|
|
continue;
|
|
VectorMap<String, String> var;
|
|
LoadVarFile(VarFilePath(vars), var);
|
|
Vector<String> dirs = Split(var.Get("UPP", ""), ';');
|
|
String missing;
|
|
for(String d : dirs)
|
|
if(!DirectoryExists(d)) {
|
|
MergeWith(missing, ", ", d);
|
|
break;
|
|
}
|
|
if(dirs.GetCount() == 0)
|
|
missing = "Empty";
|
|
if(missing.GetCount())
|
|
dlg.list.Add(true, vars, missing);
|
|
else
|
|
oks.Add(vars);
|
|
}
|
|
for(String s : oks)
|
|
dlg.list.Add(false, s);
|
|
again:
|
|
if(dlg.Run() != IDOK)
|
|
return;
|
|
int n = 0;
|
|
for(int i = 0; i < dlg.list.GetCount(); i++)
|
|
if((bool)dlg.list.Get(i, 0))
|
|
n++;
|
|
if(n) {
|
|
if(!PromptYesNo("Remove " + AsString(n) + " assemblies?"))
|
|
goto again;
|
|
for(int i = 0; i < dlg.list.GetCount(); i++)
|
|
if((bool)dlg.list.Get(i, 0))
|
|
DeleteFile(VarFilePath(~dlg.list.Get(i, 1)));
|
|
}
|
|
SyncBase(vars);
|
|
}
|
|
|
|
int DirSep(int c)
|
|
{
|
|
return c == '\\' || c == '/' ? c : 0;
|
|
}
|
|
|
|
struct PackageDisplay : Display {
|
|
Font fnt;
|
|
|
|
virtual Size GetStdSize(const Value& q) const {
|
|
ValueArray va = q;
|
|
Size sz = GetTextSize(String(va[0]), fnt);
|
|
sz.cx += Zx(20);
|
|
sz.cy = max(sz.cy, DPI(16));
|
|
return sz;
|
|
}
|
|
|
|
virtual void Paint(Draw& w, const Rect& r, const Value& q, Color ink, Color paper, dword style) const {
|
|
ValueArray va = q;
|
|
String txt = va[0];
|
|
Image icon = va[1];
|
|
if(IsNull(icon))
|
|
icon = IdeImg::Package();
|
|
else
|
|
icon = DPI(icon, 16);
|
|
w.DrawRect(r, paper);
|
|
w.DrawImage(r.left, r.top + (r.Height() - icon.GetHeight()) / 2, icon);
|
|
w.DrawText(r.left + DPI(20), r.top + (r.Height() - Draw::GetStdFontCy()) / 2, txt, fnt, ink);
|
|
}
|
|
|
|
PackageDisplay() { fnt = StdFont(); }
|
|
};
|
|
|
|
void SelectPackageDlg::SyncList(const String& find)
|
|
{
|
|
String n = Nvl(find, GetCurrentName());
|
|
int asc = alist.GetScroll();
|
|
int csc = clist.GetSbPos();
|
|
|
|
packages.Clear();
|
|
String s = ~search;
|
|
int fn = ~nest;
|
|
int fk = ~kind;
|
|
Index<String> added;
|
|
int from = 0;
|
|
int to = data.GetCount() - 1;
|
|
if(fn & UPPHUB)
|
|
from = fn & NEST_MASK;
|
|
else
|
|
if(fn != ALL)
|
|
from = to = fn & NEST_MASK;
|
|
if(to < data.GetCount())
|
|
for(int i = from; i <= to; i++) {
|
|
const ArrayMap<String, PkData>& nest = data[i];
|
|
for(int i = 0; i < nest.GetCount(); i++) {
|
|
const PkData& d = nest[i];
|
|
if(!nest.IsUnlinked(i) &&
|
|
d.ispackage &&
|
|
(!(fk == MAIN) || d.main) &&
|
|
(!(fk == NONMAIN) || !d.main) &&
|
|
ToUpper(d.package + d.description).Find(s) >= 0 &&
|
|
added.Find(d.package) < 0) {
|
|
packages.Add() = d;
|
|
if(!d.main)
|
|
added.Add(d.package);
|
|
}
|
|
}
|
|
}
|
|
Sort(packages);
|
|
alist.Clear();
|
|
clist.Clear();
|
|
nest_list.Clear();
|
|
static PackageDisplay pd, bpd;
|
|
bpd.fnt.Bold();
|
|
for(int i = 0; i < packages.GetCount(); i++) {
|
|
const PkInfo& pkg = packages[i];
|
|
Image icon = pkg.icon;
|
|
if(IsNull(icon)) {
|
|
if(pkg.main)
|
|
icon = pkg.upphub ? IdeImg::HubMainPackage() : IdeImg::MainPackage();
|
|
else
|
|
icon = pkg.upphub ? IdeImg::HubPackage() : IdeImg::Package();
|
|
}
|
|
nest_list.Add(pkg.nest);
|
|
clist.Add(pkg.package, DPI(icon, 16));
|
|
alist.Add(pkg.package, GetFileName(pkg.nest), pkg.description, icon);
|
|
alist.SetDisplay(alist.GetCount() - 1, 0, pkg.main ? bpd : pd);
|
|
}
|
|
if(!alist.FindSetCursor(n))
|
|
alist.GoBegin();
|
|
if(!clist.FindSetCursor(n) && clist.GetCount())
|
|
clist.SetCursor(0);
|
|
alist.ScrollTo(asc);
|
|
clist.SetSbPos(csc);
|
|
alist.HeaderTab(0).SetText("Package (" + AsString(alist.GetCount()) + ")");
|
|
}
|
|
|
|
void SelectPackageDlg::ScanFolder(const String& path, ArrayMap<String, PkData>& nd,
|
|
const String& nest, Index<String>& dir_exists,
|
|
const String& prefix)
|
|
{
|
|
for(FindFile ff(AppendFileName(path, "*.*")); ff; ff.Next())
|
|
if(ff.IsFolder() && !ff.IsHidden()) {
|
|
String p = ff.GetPath();
|
|
dir_exists.Add(p);
|
|
bool nw = nd.Find(p) < 0; // Do we have any info loaded about this package?
|
|
PkData& d = nd.GetAdd(p);
|
|
d.package = prefix + ff.GetName();
|
|
d.nest = nest;
|
|
d.upphub = InUppHub(p);
|
|
if(nw) { // No cached info available about the folder
|
|
d.ispackage = IsLetter(*d.package) && d.package.Find('.') < 0; // First heuristic guess
|
|
d.main = d.ispackage && prefix.GetCount() == 0; // Expect it is main
|
|
}
|
|
}
|
|
}
|
|
|
|
String SelectPackageDlg::CachePath(const char *vn) const
|
|
{
|
|
return AppendFileName(ConfigFile("cfg"), String(vn) + ".pkg_cache_new");
|
|
}
|
|
|
|
void SelectPackageDlg::Load(const String& find)
|
|
{
|
|
SyncFilter();
|
|
if(selectvars && !base.IsCursor())
|
|
return;
|
|
if(loading) { // If we are called recursively from ProcessEvents, stop current loading and change loadi
|
|
loadi++;
|
|
loading = false;
|
|
return;
|
|
}
|
|
int current_loadi = -1;
|
|
while(current_loadi != loadi) {
|
|
current_loadi = loadi;
|
|
if(selectvars) {
|
|
String assembly = (String)base.Get(0);
|
|
list.Enable(base.IsCursor());
|
|
if(!base.IsCursor())
|
|
return;
|
|
LoadVars(assembly);
|
|
if(GetVar("_all") == "1")
|
|
nest <<= ALL;
|
|
else
|
|
nest <<= 0;
|
|
if(alist_external != IsExternalMode()) {
|
|
alist_external = IsExternalMode();
|
|
alist.ColumnWidths(alist_external ? "300 0 200": "108 79 317");
|
|
}
|
|
alist.HeaderTab(1).Show(!alist_external);
|
|
SyncFilter();
|
|
}
|
|
Vector<String> upp = GetUppDirsRaw();
|
|
packages.Clear();
|
|
list.AddFrame(lists_status);
|
|
loading = true;
|
|
data.Clear();
|
|
Index<String> dir_exists;
|
|
String cache_path = CachePath(GetVarsName());
|
|
if(IsExternalMode()) {
|
|
upp << "";
|
|
for(FindFile ff(ConfigFile("external") + "/*.*"); ff; ff.Next())
|
|
if(ff.IsFile()) {
|
|
String dir = DecodePathFromFileName(ff.GetName());
|
|
if(DirectoryExists(dir)) {
|
|
PkData& d = data.At(0).GetAdd(dir);
|
|
d.package = dir;
|
|
d.ispackage = true;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
LoadFromFile(data, cache_path);
|
|
data.SetCount(upp.GetCount());
|
|
for(int i = 0; i < upp.GetCount(); i++) // Scan nest folders for subfolders (additional package candidates)
|
|
ScanFolder(upp[i], data[i], upp[i], dir_exists, Null);
|
|
}
|
|
int update = msecs();
|
|
for(int i = 0; i < data.GetCount() && loading; i++) { // Now investigate individual sub folders
|
|
ArrayMap<String, PkData>& nest = data[i];
|
|
String nest_dir = NormalizePath(upp[i]);
|
|
for(int i = 0; i < nest.GetCount() && loading; i++) {
|
|
if(msecs(update) >= 100) { // each 100 ms update the list (and open select dialog after splash screen is closed)
|
|
if(!IsSplashOpen() && !IsOpen())
|
|
Open();
|
|
progress++;
|
|
SyncList(find);
|
|
update = msecs();
|
|
}
|
|
ProcessEvents(); // keep GUI running
|
|
|
|
PkData& d = nest[i];
|
|
String path = nest.GetKey(i);
|
|
if((IsExternalMode() || NormalizePath(path).StartsWith(nest_dir)) && DirectoryExists(path)) {
|
|
String upp_path = PackageFile(d.package);
|
|
LSLOW(); // this is used for testing only, normally it is NOP
|
|
Time tm = FileGetTime(upp_path);
|
|
if(IsNull(tm)) // .upp file does not exist - not a package
|
|
d.ispackage = false;
|
|
else
|
|
if(tm != d.tm) { // cached info is outdated
|
|
Package p;
|
|
if(p.Load(upp_path)) {
|
|
d.description = p.description;
|
|
d.main = p.config.GetCount();
|
|
d.tm = tm;
|
|
d.ispackage = true;
|
|
}
|
|
else
|
|
d.ispackage = false;
|
|
}
|
|
else
|
|
d.ispackage = true;
|
|
|
|
if(d.ispackage) {
|
|
String icon_path;
|
|
if(IsUHDMode())
|
|
icon_path = AppendFileName(path, "icon32x32.png");
|
|
if(IsNull(icon_path) || !FileExists(icon_path))
|
|
icon_path = AppendFileName(path, "icon16x16.png");
|
|
tm = FileGetTime(icon_path);
|
|
if(IsNull(tm)) // package icon does not exist
|
|
d.icon = Null;
|
|
else
|
|
if(tm != d.itm || d.icon.GetSize().cx != DPI(16)) { // chached package icon outdated
|
|
d.icon = StreamRaster::LoadFileAny(icon_path);
|
|
d.itm = tm;
|
|
}
|
|
}
|
|
|
|
ScanFolder(path, nest, d.nest, dir_exists, d.package + '/');
|
|
}
|
|
else
|
|
nest.Unlink(i); // cached folder was deleted or is not in nest dir
|
|
}
|
|
nest.Sweep();
|
|
}
|
|
|
|
if(!IsExternalMode())
|
|
StoreToFile(data, cache_path);
|
|
list.RemoveFrame(lists_status);
|
|
while(IsSplashOpen())
|
|
ProcessEvents();
|
|
if(!IsOpen())
|
|
Open();
|
|
if(loading) {
|
|
loading = false;
|
|
SyncList(find);
|
|
}
|
|
}
|
|
}
|
|
|
|
void SelectPackageDlg::SyncBase(String initvars)
|
|
{
|
|
Vector<String> varlist;
|
|
for(FindFile ff(ConfigFile("*.var")); ff; ff.Next())
|
|
if(ff.IsFile()) {
|
|
String n = ff.GetName();
|
|
varlist.Add(GetFileTitle(n));
|
|
}
|
|
|
|
Sort(varlist, &PackageLess);
|
|
base.Clear();
|
|
for(String s : varlist)
|
|
base.Add(s, s);
|
|
base.HeaderTab(0).SetText("Assembly (" + AsString(base.GetCount()) + ")");
|
|
if(!base.FindSetCursor(initvars)) {
|
|
if(base.GetCount() > 0)
|
|
base.SetCursor(0);
|
|
else
|
|
OnBase();
|
|
}
|
|
}
|
|
|
|
bool SelectPackageDlg::Pless(const SelectPackageDlg::PkInfo& a, const SelectPackageDlg::PkInfo& b)
|
|
{
|
|
return PackageLess(a.package, b.package);
|
|
}
|
|
|
|
INITBLOCK
|
|
{
|
|
RegisterGlobalConfig("SelectPkgMain");
|
|
RegisterGlobalConfig("SelectPkg");
|
|
}
|
|
|
|
String SelectPackage(String& nest, const char *title, const char *startwith, bool selectvars, bool main)
|
|
{
|
|
SelectPackageDlg dlg(title, selectvars, main);
|
|
const char *c = main ? "SelectPkgMain" : "SelectPkg";
|
|
LoadFromGlobal(dlg, c);
|
|
dlg.SyncBrief();
|
|
dlg.SyncFilter();
|
|
dlg.CenterScreen();
|
|
String b = dlg.Run(nest, startwith);
|
|
StoreToGlobal(dlg, c);
|
|
if(main && selectvars && b.GetCount())
|
|
dlg.StoreLRU(b);
|
|
|
|
return b;
|
|
}
|
|
|
|
String SelectPackage(const char *title, const char *startwith, bool selectvars, bool main)
|
|
{
|
|
String dummy;
|
|
return SelectPackage(dummy, title, startwith, selectvars, main);
|
|
}
|
|
|