//+----------------------------------------------------------------------------+ //| Description: Magic Set Editor - Program to make Magic (tm) cards | //| Copyright: (C) 2001 - 2008 Twan van Laarhoven and "coppro" | //| License: GNU General Public License 2 or later (see file COPYING) | //+----------------------------------------------------------------------------+ // ----------------------------------------------------------------------------- : Includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include DECLARE_POINTER_TYPE(Installer); DECLARE_TYPEOF_COLLECTION(PackageDependencyP); DECLARE_TYPEOF_COLLECTION(InstallablePackageP); DECLARE_TYPEOF_COLLECTION(DownloadableInstallerP); // ----------------------------------------------------------------------------- : TODO: MOVE /* // A HTML control that opens all pages in an actual browser struct HtmlWindowToBrowser : public wxHtmlWindow { HtmlWindowToBrowser(Window* parent, int id, const wxPoint& pos, const wxSize& size, long flags) : wxHtmlWindow(parent, id, pos, size, flags) {} virtual void OnLinkClicked(const wxHtmlLinkInfo& info) { wxLaunchDefaultBrowser( info.GetHref() ); } }; */ // ----------------------------------------------------------------------------- : DownloadableInstallers /// Handle downloading of installers class DownloadableInstallerList { public: DownloadableInstallerList() : status(NONE) {} /// start downloading, return true if we are done bool download(); vector installers; private: enum Status { NONE, DOWNLOADING, DONE } status; wxMutex lock; struct Thread : public wxThread { virtual ExitCode Entry(); }; }; /// The global installer downloader DownloadableInstallerList downloadable_installers; bool DownloadableInstallerList::download() { if (status == DONE) return true; if (status == NONE) { status = DOWNLOADING; Thread* thread = new Thread(); thread->Create(); thread->Run(); } return false; } wxThread::ExitCode DownloadableInstallerList::Thread::Entry() { // open url wxURL url(settings.installer_list_url); wxInputStream* isP = url.GetInputStream(); if (!isP) { wxMutexLocker l(downloadable_installers.lock); downloadable_installers.status = DONE; return 0; } InputStreamP is(isP); // Read installer list Reader reader(is, nullptr, _("installers"), true); vector installers; reader.handle(_("installers"),installers); // done wxMutexLocker l(downloadable_installers.lock); swap(installers, downloadable_installers.installers); downloadable_installers.status = DONE; return 0; } // ----------------------------------------------------------------------------- : PackageInfoPanel /// Information on a package class PackageInfoPanel : public wxPanel { public: PackageInfoPanel(Window* parent); void setPackage(const InstallablePackageP& package); virtual wxSize DoGetBestSize() const; private: InstallablePackageP package; DECLARE_EVENT_TABLE(); void onPaint(wxPaintEvent&); void draw(DC&); }; PackageInfoPanel::PackageInfoPanel(Window* parent) : wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER) {} void PackageInfoPanel::setPackage(const InstallablePackageP& package) { this->package = package; Refresh(false); } void PackageInfoPanel::onPaint(wxPaintEvent&) { wxBufferedPaintDC dc(this); try { draw(dc); } CATCH_ALL_ERRORS(false); // don't show message boxes in onPaint! } void PackageInfoPanel::draw(DC& dc) { wxSize cs = GetClientSize(); dc.SetPen(*wxTRANSPARENT_PEN); dc.SetBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); dc.SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); dc.DrawRectangle(0,0,cs.x,cs.y); // draw package info if (!package) return; PackageDescription& d = *package->description; // some borders //%int width = cs.x - 10, height = cs.y - 10; int x = 5, y = 5; // draw icon if (d.icon.Ok()) { int max_size = 105; Image icon = d.icon; int icon_w = icon.GetWidth(); int icon_h = icon.GetHeight(); if (icon_w <= 20 && icon_h <= 20) { // upsample icon = resample_preserve_aspect(icon, 96, 96); icon_w = icon.GetWidth(); icon_h = icon.GetHeight(); } dc.DrawBitmap(icon, x+(max_size-icon_w)/2, y+(max_size-icon_h)/2); x += max_size; } // package name x += 7; dc.SetFont(wxFont(16, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, _("Arial"))); dc.DrawText(d.full_name, x, y); y += dc.GetCharHeight() + 7; // version dc.SetFont(*wxNORMAL_FONT); int dy = dc.GetCharHeight() + 3; dc.DrawText(_LABEL_("installed version"), x, y); dc.DrawText(_LABEL_("installable version"), x, y + 1*dy); //dc.DrawText(_LABEL_("installer size"), x, y + 2*dy); //dc.DrawText(_LABEL_("installer status"), x, y + 3*dy); // text size? int dx = 0, max_dx = 0; dc.GetTextExtent(_LABEL_("installed version"), &dx, nullptr); max_dx = max(max_dx, dx); dc.GetTextExtent(_LABEL_("installable version"), &dx, nullptr); max_dx = max(max_dx, dx); //dc.GetTextExtent(_LABEL_("installer size"), &dx, nullptr); max_dx = max(max_dx, dx); //dc.GetTextExtent(_LABEL_("installer status"), &dx, nullptr); max_dx = max(max_dx, dx); x += max_dx + 5; dc.DrawText(package->installed ? package->installed->version.toString() : _LABEL_("no version"), x, y); dc.DrawText(package->installer ? package->description->version.toString() : _LABEL_("no version"), x, y + 1*dy); //dc.DrawText(_("?"), x, y + 2*dy); //dc.DrawText(_("?"), x, y + 3*dy); } wxSize PackageInfoPanel::DoGetBestSize() const { return wxSize(200, 120); } BEGIN_EVENT_TABLE(PackageInfoPanel, wxPanel) EVT_PAINT(PackageInfoPanel::onPaint) END_EVENT_TABLE() // ----------------------------------------------------------------------------- : PackagesWindow enum Action { KEEP, INSTALL, UPGRADE, REMOVE }; PackagesWindow::PackagesWindow(Window* parent, bool download_package_list) : waiting_for_list(download_package_list) { // request download before searching disk so we do two things at once if (download_package_list) downloadable_installers.download(); init(parent, false); } PackagesWindow::PackagesWindow(Window* parent, const InstallerP& installer) : waiting_for_list(false) { init(parent, true); // add installer merge(installable_packages, new_intrusive1(installer)); FOR_EACH(p, installable_packages) p->determineStatus(); // mark all packages in the installer for installation FOR_EACH(ip, installable_packages) { if (ip->can(PACKAGE_ACT_INSTALL)) { set_package_action(installable_packages, ip, PACKAGE_ACT_INSTALL | where); } } // update window package_list->rebuild(); package_list->expandAll(); UpdateWindowUI(wxUPDATE_UI_RECURSE); } void PackagesWindow::init(Window* parent, bool show_only_installable) { where = is_install_local(settings.install_type) ? PACKAGE_ACT_LOCAL : PACKAGE_ACT_GLOBAL; Create(parent, wxID_ANY, _TITLE_("packages window"), wxDefaultPosition, wxSize(640,580), wxDEFAULT_DIALOG_STYLE | wxCLIP_CHILDREN | wxRESIZE_BORDER); // get packages wxBusyCursor busy; package_manager.findAllInstalledPackages(installable_packages); FOR_EACH(p, installable_packages) p->determineStatus(); checkInstallerList(false); // ui elements SetIcon(wxIcon()); package_list = new PackageUpdateList(this, installable_packages, show_only_installable, ID_PACKAGE_LIST); package_info = new PackageInfoPanel(this); wxToggleButton* keep_button = new wxToggleButton(this, ID_KEEP, _BUTTON_("keep package")); wxToggleButton* install_button = new wxToggleButton(this, ID_INSTALL, _BUTTON_("install package")); wxToggleButton* remove_button = new wxToggleButton(this, ID_REMOVE, _BUTTON_("remove package")); /* wxRadioButton* keep_button = new wxRadioButton(this, ID_KEEP, _BUTTON_("keep package")); wxRadioButton* install_button = new wxRadioButton(this, ID_INSTALL, _BUTTON_("install package")); wxRadioButton* upgrade_button = new wxRadioButton(this, ID_UPGRADE, _BUTTON_("upgrade package")); wxRadioButton* remove_button = new wxRadioButton(this, ID_REMOVE, _BUTTON_("remove package")); */ // Init sizer wxBoxSizer* v = new wxBoxSizer(wxVERTICAL); v->Add(package_list, 1, wxEXPAND | wxALL & ~wxBOTTOM, 8); v->AddSpacer(4); wxBoxSizer* h = new wxBoxSizer(wxHORIZONTAL); h->Add(package_info, 1, wxRIGHT, 4); wxBoxSizer* v2 = new wxBoxSizer(wxVERTICAL); v2->Add(install_button, 0, wxEXPAND | wxBOTTOM, 4); v2->AddStretchSpacer(); v2->Add(keep_button, 0, wxEXPAND | wxBOTTOM, 4); v2->AddStretchSpacer(); v2->Add(remove_button, 0, wxEXPAND | wxBOTTOM, 0); h->Add(v2); v->Add(h, 0, wxEXPAND | wxALL & ~wxTOP, 8); v->Add(CreateButtonSizer(wxOK | wxCANCEL), 0, wxEXPAND | wxALL & ~wxTOP, 8); SetSizer(v); wxUpdateUIEvent::SetMode(wxUPDATE_UI_PROCESS_SPECIFIED); UpdateWindowUI(wxUPDATE_UI_RECURSE); } PackagesWindow::~PackagesWindow() { } void PackagesWindow::onPackageSelect(wxCommandEvent& ev) { package_info->setPackage(package = package_list->getSelection()); UpdateWindowUI(wxUPDATE_UI_RECURSE); } void PackagesWindow::onActionChange(wxCommandEvent& ev) { if (package) { PackageAction act = ev.GetId() == ID_INSTALL ? PACKAGE_ACT_INSTALL : ev.GetId() == ID_UPGRADE ? PACKAGE_ACT_INSTALL : ev.GetId() == ID_REMOVE ? PACKAGE_ACT_REMOVE : PACKAGE_ACT_NOTHING; act = act | where; // set action set_package_action(installable_packages, package, act); package_list->Refresh(false); UpdateWindowUI(wxUPDATE_UI_RECURSE); } } void PackagesWindow::onOk(wxCommandEvent& ev) { // Do we need a new version of MSE first? // count number of packages to change int to_change = 0; int to_download = 0; int to_remove = 0; int with_modifications = 0; FOR_EACH(ip, installable_packages) { if (!ip->has(PACKAGE_ACT_NOTHING)) ++to_change; if (ip->has(PACKAGE_ACT_INSTALL) && ip->installer && !ip->installer->installer) ++to_download; if (ip->has(PACKAGE_ACT_REMOVE)) { to_remove++; if (ip->has(PACKAGE_MODIFIED)) with_modifications++; } } // anything to do? if (!to_change) { ev.Skip(); return; } // Warn about removing if (to_remove) { int result = wxMessageBox( with_modifications == 0 ? _ERROR_1_("remove packages", String()<has(PACKAGE_ACT_INSTALL) && ip->installer && !ip->installer->installer) { if (!progress.Update(step++, String::Format(_ERROR_("downloading updates"), ++package_pos, to_download))) { return; // aborted } // download installer wxURL url(ip->installer->installer_url); wxInputStream* is = url.GetInputStream(); if (!is) { throw Error(_ERROR_2_("can't download installer", ip->description->name, ip->installer->installer_url)); } ip->installer->installer_file = wxFileName::CreateTempFileName(_("mse-installer")); wxFileOutputStream os(ip->installer->installer_file); os.Write(*is); os.Close(); // open installer ip->installer->installer = new_intrusive(); ip->installer->installer->open(ip->installer->installer_file); } } // Install stuff package_pos = 0; int success = 0, install = 0, remove = 0; FOR_EACH(ip, installable_packages) { if (ip->has(PACKAGE_ACT_NOTHING)) continue; // package unchanged if (!progress.Update(step++, String::Format(_ERROR_("installing updates"), ++package_pos, to_change))) { // don't allow abort. } bool ok = package_manager.install(*ip); if (ok) { install += ip->has(PACKAGE_ACT_INSTALL) && !ip->installed; remove += ip->has(PACKAGE_ACT_REMOVE); success += 1; } } // Done progress.Update(step++); wxMessageBox( install == success ? _ERROR_1_("install packages successful",String()<SetValue(package && package->has(PACKAGE_ACT_NOTHING)); w->Enable (package && package->can(PACKAGE_ACT_NOTHING | where)); w->SetLabel(package && package->installed ? _BUTTON_("keep package") : _BUTTON_("don't install package")); break; case ID_INSTALL: w->SetValue(package && package->has(PACKAGE_ACT_INSTALL | where)); w->Enable (package && package->can(PACKAGE_ACT_INSTALL | where)); w->SetLabel( !package || !package->installed ? _BUTTON_("install package") : package->has(PACKAGE_UPDATES) ? _BUTTON_("upgrade package") : _BUTTON_("reinstall package")); break; case ID_REMOVE: w->SetValue(package && package->has(PACKAGE_ACT_REMOVE | where)); w->Enable (package && package->can(PACKAGE_ACT_REMOVE | where)); //w->SetLabel(package && package->... ? _BUTTON_("remove group") : _BUTTON_("remove package")); break; } // TODO: change labels to _BUTTON_("install group"), _BUTTON_("remove group"), _BUTTON_("upgrade group") } void PackagesWindow::onIdle(wxIdleEvent& ev) { ev.RequestMore(!checkInstallerList()); } bool PackagesWindow::checkInstallerList(bool refresh) { if (!waiting_for_list) return true; if (!downloadable_installers.download()) return false; waiting_for_list = false; // merge installer lists FOR_EACH(inst, downloadable_installers.installers) { merge(installable_packages, inst); } FOR_EACH(p, installable_packages) p->determineStatus(); // refresh if (refresh) { package_list->rebuild(); package_info->setPackage(package = package_list->getSelection()); UpdateWindowUI(wxUPDATE_UI_RECURSE); } return true; } BEGIN_EVENT_TABLE(PackagesWindow, wxDialog) EVT_LISTBOX(ID_PACKAGE_LIST, PackagesWindow::onPackageSelect) EVT_TOGGLEBUTTON(ID_KEEP, PackagesWindow::onActionChange) EVT_TOGGLEBUTTON(ID_INSTALL, PackagesWindow::onActionChange) EVT_TOGGLEBUTTON(ID_REMOVE, PackagesWindow::onActionChange) EVT_TOGGLEBUTTON(ID_UPGRADE, PackagesWindow::onActionChange) EVT_BUTTON(wxID_OK, PackagesWindow::onOk) EVT_UPDATE_UI(wxID_ANY, PackagesWindow::onUpdateUI) EVT_IDLE ( PackagesWindow::onIdle) END_EVENT_TABLE()