//+----------------------------------------------------------------------------+ //| Description: Magic Set Editor - Program to make card games | //| Copyright: (C) Twan van Laarhoven and the other MSE developers | //| 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 #include DECLARE_POINTER_TYPE(Installer); DownloadableInstallerList downloadable_installers; // ----------------------------------------------------------------------------- : PackageInfoPanel /// Information on a package class PackageInfoPanel : public wxPanel { public: PackageInfoPanel(Window* parent); void setPackage(const InstallablePackageP& package); wxSize DoGetBestSize() const override; private: InstallablePackageP package; DECLARE_EVENT_TABLE(); void onPaint(wxPaintEvent&); void draw(DC&); }; PackageInfoPanel::PackageInfoPanel(Window* parent) : wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBORDER_THEME) {} 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 + 2); x += max_size; } // package info x += 7; dc.SetFont(wxFont(16, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, _("Arial"))); dc.DrawText(d.short_name, x, y); y += dc.GetCharHeight() + 7; dc.SetFont(wxFont(12, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, _("Arial"))); if (d.full_name != d.short_name) dc.DrawText(d.full_name, x, y); y += dc.GetCharHeight() + 7; dc.SetFont(*wxNORMAL_FONT); int dy = dc.GetCharHeight() + 3; dc.DrawText(_LABEL_("folder name"), x, y); dc.DrawText(_LABEL_("installed version"), x, y + 1*dy); dc.DrawText(_LABEL_("installable version"), x, y + 2*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_("folder name"), &dx, nullptr); max_dx = max(max_dx, dx); 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(d.name, x, y); dc.DrawText(package->installed ? package->installed->version.toString() : _LABEL_("no version"), x, y + 1*dy); dc.DrawText(package->installer ? package->description->version.toString() : _LABEL_("no version"), x, y + 2*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, make_intrusive(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); waiting_info = new wxStaticText(this, wxID_ANY, waiting_for_list ? _LABEL_("awaiting package list") : _("")); 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); v2->SetMinSize(wxSize(170, -1)); h->Add(v2); v->Add(h, 0, wxEXPAND | (wxALL & ~wxTOP), 8); wxBoxSizer* h2 = new wxBoxSizer(wxHORIZONTAL); h2->Add(waiting_info, 0, wxEXPAND | (wxALL & ~wxTOP), 8); h2->AddStretchSpacer(); h2->Add(CreateButtonSizer(wxOK | wxCANCEL), 0, wxEXPAND | (wxALL & ~wxTOP), 8); v->Add(h2, 0, wxEXPAND); v->SetMinSize(820,650); SetSizerAndFit(v); wxUpdateUIEvent::SetMode(wxUPDATE_UI_PROCESS_SPECIFIED); UpdateWindowUI(wxUPDATE_UI_RECURSE); } PackagesWindow::~PackagesWindow() { } void PackagesWindow::onPackageSelect(wxCommandEvent& ev) { package_info->setPackage(package = package_list->getSelectedPackage()); UpdateWindowUI(wxUPDATE_UI_RECURSE); } void PackagesWindow::onActionChange(wxCommandEvent& ev) { 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 package_list->forEachSelectedPackage( [&](const InstallablePackageP& p) { if (p->can(act)) { set_package_action(installable_packages, p, act); } } ); package_list->Refresh(false); UpdateWindowUI(wxUPDATE_UI_RECURSE); } void PackagesWindow::onOk(wxCommandEvent& ev) { // count number of packages to change bool app_change = false; int to_change = 0; int to_download = 0; int to_remove = 0; int with_modifications = 0; FOR_EACH(ip, installable_packages) { if (ip->description->name == mse_package) { if (ip->has(PACKAGE_ACT_INSTALL)) app_change = true; continue; } 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()<description->name == mse_package) continue; if (ip->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 wxWebRequestSync request = wxWebSessionSync::GetDefault().CreateRequest(ip->installer->installer_url); auto const result = request.Execute(); if (!result) { throw Error(_ERROR_2_("can't download installer", ip->description->name, ip->installer->installer_url)); } wxInputStream* is(request.GetResponse().GetStream()); ip->installer->installer_file = wxFileName::CreateTempFileName(_("mse-installer")); wxFileOutputStream os(ip->installer->installer_file); os.Write(*is); os.Close(); // open installer ip->installer->installer = make_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->description->name == mse_package) continue; // app, we'll do that last 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; } } // Report on package status progress.Update(step++); String report_message = install == success ? _ERROR_1_("install packages successful",String()<(updater_package.get()); if (updater) { String darkmode = String("darkmode") << settings.darkMode(); String locale = String("locale") << settings.locale; locale.Replace("-", ""); updater->updateApplication(darkmode + _(" ") + locale); } else { queue_message(MESSAGE_ERROR, _("Failed to load updater package 'github-zip-extractor.mse-updater'.")); } } else { queue_message(MESSAGE_ERROR, _("Failed to load updater package 'github-zip-extractor.mse-updater'.")); } // Check for any updater, for later (very slow) //vector installed_packages; //package_manager.findAllInstalledPackages(installed_packages); //FOR_EACH(p, installed_packages) { // if (!p->description->name.EndsWith(".mse-updater")) continue; // PackagedP updater_package = package_manager.openAny(p->description->name); // Updater* updater = dynamic_cast(updater_package.get()); // if (updater) { // String darkmode = String("darkmode") << settings.darkMode(); // String locale = String("locale") << settings.locale; // locale.Replace("-", ""); // updater->updateApplication(darkmode + _(" ") + locale); // break; // } // else { // queue_message(MESSAGE_ERROR, _("Failed to load updater package '" + p->description->name + "'.")); // } //} } // Continue event propagation into the dialog window so that it closes. ev.Skip(); //%% TODO: will we delete packages? //%% If so, we need to warn with _ERROR_1_("remove packages") and _ERROR_2_("remove packages modified") //%% We probably also refer to the type of package, _TYPE_("package"), for example _TYPE_("locale") //%% NOTE: The above text is for the locale.pl program } void PackagesWindow::onUpdateUI(wxUpdateUIEvent& ev) { bool is_group = package_list->selectionIsGroup(); wxToggleButton* w = (wxToggleButton*)ev.GetEventObject(); switch (ev.GetId()) { case ID_KEEP: w->SetValue( package_list->allSelectedPackages([&](const InstallablePackageP& p) { return p->has(PACKAGE_ACT_NOTHING); }) ); w->Enable( package_list->anySelectedPackage([&](const InstallablePackageP& p) { return p->can(PACKAGE_ACT_NOTHING | where); }) ); w->SetLabel(is_group ? _BUTTON_("keep group") : package && package->installed ? _BUTTON_("keep package") : _BUTTON_("don't install package")); break; case ID_INSTALL: w->SetValue( package_list->allSelectedPackages([&](const InstallablePackageP& p) { return p->has(PACKAGE_ACT_INSTALL | where) || (p->has(PACKAGE_INSTALLED) && !p->has(PACKAGE_ACT_REMOVE) && !p->has(PACKAGE_UPDATES)); }) && package_list->anySelectedPackage([&](const InstallablePackageP& p) { return p->has(PACKAGE_ACT_INSTALL | where); }) ); w->Enable( package_list->anySelectedPackage([&](const InstallablePackageP& p) { return p->can(PACKAGE_ACT_INSTALL | where); }) ); w->SetLabel(is_group ? _BUTTON_("install group") : !(package && package->installed) ? _BUTTON_("install package") : (package && package->has(PACKAGE_UPDATES)) ? _BUTTON_("upgrade package") : _BUTTON_("reinstall package")); break; case ID_REMOVE: w->SetValue( package_list->allSelectedPackages([&](const InstallablePackageP& p) { return p->has(PACKAGE_ACT_REMOVE | where) || (!p->has(PACKAGE_INSTALLED) && !p->has(PACKAGE_ACT_INSTALL)); }) ); w->Enable( package_list->anySelectedPackage([&](const InstallablePackageP& p) { return p->can(PACKAGE_ACT_REMOVE | where); }) ); w->SetLabel(is_group ? _BUTTON_("remove group") : _BUTTON_("remove package")); break; } } void PackagesWindow::onIdle(wxIdleEvent& ev) { ev.RequestMore(!checkInstallerList()); if (waiting_info && !waiting_for_list) waiting_info->SetLabel(_("")); } 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->getSelectedPackage()); 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()