diff --git a/CMakeLists.txt b/CMakeLists.txt index ba346f9a..0725971e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,7 +29,7 @@ endif() # You will most likely get a message about being unable to open hunspell-1.7.lib because pkgconf forgets to add the actual path to # HUNSPELL_LIBRARIES. If so, uncomment the below line and point it to the correct vcpkg root folder/library. -#set(HUNSPELL_LIBRARIES "C:\\PATH\\TO\\ROOT\\vcpkg\\installed\\${VCPKG_TARGET_TRIPLET}\\lib\\hunspell-1.7.lib") +set(HUNSPELL_LIBRARIES "C:\\src\\vcpkg\\installed\\${VCPKG_TARGET_TRIPLET}\\lib\\hunspell-1.7.lib") message("-- Does this have a full path? If not, and it's just a file name, it's broken: Found Hunspell at ${HUNSPELL_LIBRARIES}") include_directories("${PROJECT_BINARY_DIR}/src") @@ -109,6 +109,11 @@ if(WIN32) ) endif() +# Magic Set Editor Updater executable + +add_executable(magicseteditor-updater WIN32) +target_link_libraries(magicseteditor-updater PRIVATE wxWidgets::wxWidgets) +target_sources(magicseteditor-updater PRIVATE src_updater/main.cpp src_updater/win32_res.rc src_updater/updater.ico) # warnings if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) diff --git a/resource/icon/updater.ico b/resource/icon/updater.ico new file mode 100644 index 00000000..bb10d2f3 Binary files /dev/null and b/resource/icon/updater.ico differ diff --git a/resource/tool/check_updates.png b/resource/tool/check_updates.png index 56e3adf5..a03d215a 100644 Binary files a/resource/tool/check_updates.png and b/resource/tool/check_updates.png differ diff --git a/resource/tool/dark_check_updates.png b/resource/tool/dark_check_updates.png deleted file mode 100644 index 8c4351cf..00000000 Binary files a/resource/tool/dark_check_updates.png and /dev/null differ diff --git a/resource/tool/dark_reload_data.png b/resource/tool/dark_reload_data.png deleted file mode 100644 index d6043b6c..00000000 Binary files a/resource/tool/dark_reload_data.png and /dev/null differ diff --git a/resource/tool/reload_data.png b/resource/tool/reload_data.png index fecb2722..8a0ec43d 100644 Binary files a/resource/tool/reload_data.png and b/resource/tool/reload_data.png differ diff --git a/resource/win32_res.rc b/resource/win32_res.rc index aabd5a9e..bfffb4e4 100644 --- a/resource/win32_res.rc +++ b/resource/win32_res.rc @@ -11,6 +11,7 @@ icon/app ICON "icon/app.ico" // has to come first in alphabet!! icon/installer ICON "icon/installer.ico" +icon/updater ICON "icon/updater.ico" icon/set ICON "icon/set.ico" icon/symbol ICON "icon/symbol.ico" @@ -28,9 +29,7 @@ tool/export_images IMAGE "tool/export_images.png" tool/export_mws IMAGE "tool/export_mws.png" tool/export_apprentice IMAGE "tool/export_apprentice.png" tool/reload_data IMAGE "tool/reload_data.png" -tool/dark_reload_data IMAGE "tool/dark_reload_data.png" tool/check_updates IMAGE "tool/check_updates.png" -tool/dark_check_updates IMAGE "tool/dark_check_updates.png" tool/print IMAGE "tool/print.png" tool/print_preview IMAGE "tool/print_preview.png" diff --git a/src/data/installer.cpp b/src/data/installer.cpp index 9f23fc61..fc09d795 100644 --- a/src/data/installer.cpp +++ b/src/data/installer.cpp @@ -20,6 +20,7 @@ #include #include #include +#include // Don't do this check for now, because we can't bless packages #define USE_MODIFIED_CHECK 0 @@ -36,17 +37,17 @@ IMPLEMENT_REFLECTION(Installer) { void Installer::validate(Version file_app_version) { Packaged::validate(file_app_version); - // load icons where possible + // load icons if it's a disk path FOR_EACH(p,packages) { - String url = p->icon_url; + String filename = p->icon_url; if (settings.darkMode() && !p->dark_icon_url.empty()) { - url = p->dark_icon_url; + filename = p->dark_icon_url; } - if (!url.empty() && !starts_with(url,_("http:"))) { + if (!filename.empty() && !starts_with(filename,_("http"))) { // TODO: support absolute icon names try{ - String filename = p->name + _("/") + url; - auto img_stream = openIn(p->name + _("/") + url); + String filepath = p->name + _("/") + filename; + auto img_stream = openIn(filepath); image_load_file(p->icon, *img_stream); } catch (...) { // ignore errors, it's just an image @@ -251,7 +252,7 @@ IMPLEMENT_REFLECTION_NO_SCRIPT(PackageDescription) { } void PackageDescription::merge(const PackageDescription& p2) { - if (!icon.Ok() && !icon_url) icon = p2.icon; + if (!icon.Ok()) icon = p2.icon; if (installer_group.empty()) installer_group = p2.installer_group; if (short_name.empty()) short_name = p2.short_name; if (full_name.empty()) full_name = p2.full_name; @@ -505,33 +506,44 @@ void remove_package_dependency(InstallablePackages& packages, Dep dep, PackageAc // ----------------------------------------------------------------------------- : Installable package : dependency stuff (OLD) -bool add_package_dependency(InstallablePackages& packages, const PackageDependency& dep, PackageAction where, bool set) { +bool add_package_dependency(InstallablePackages& packages, const InstallablePackageP& package, const PackageDependency& dep, PackageAction where, bool set, unordered_set& already_checked) { FOR_EACH(p, packages) { - if (p->description->name == dep.package) { - // Some package depends on this package, so install it if needed + if (p->description->name == dep.package) { + if (already_checked.find(p->description->name) != already_checked.end()) return true; + already_checked.insert(p->description->name); + // package depends on p, so install p if needed // Mark the installation as "automatically needed for X packages" - // if !set then instead the dependency is no longer needed because we are not installing the package + // if !set then instead the dependency is no longer needed because we are no longer installing package if (!p->installed || p->installed->version < dep.version) { - bool change = false; if (p->action & PACKAGE_ACT_INSTALL) { - // this package is already scheduled for installation + // p is already scheduled for installation if (p->automatic) { - // we are already automatically depending on this package + // we are already automatically depending on p p->automatic += set ? +1 : -1; if (p->automatic == 0) { - // no one needs this package anymore + // no one needs p anymore p->action = PACKAGE_ACT_NOTHING; - change = true; + } + } + // handle circular dependencies + if (!set) { + FOR_EACH(pdep, p->description->dependencies) { + if (package->description->name == pdep->package && (!package->installed || package->installed->version < pdep->version)) { + // p depends on package, so we can no longer install p + // because it depends on a version of package that we are no longer installing + p->automatic = 0; + p->action = PACKAGE_ACT_NOTHING; + break; + } } } } else if (set) { p->action = where | PACKAGE_ACT_INSTALL; p->automatic = 1; - change = true; } // recursively add/remove dependencies FOR_EACH(dep, p->description->dependencies) { - if (!add_package_dependency(packages, *dep, where, set)) { + if (!add_package_dependency(packages, p, *dep, where, set, already_checked)) { return false; // failed } } @@ -573,9 +585,10 @@ bool set_package_action_unsafe(InstallablePackages& packages, const InstallableP // need the package package->automatic = 0; package->action = action; - // check dependencies + // check dependencies + unordered_set already_checked; FOR_EACH(dep, package->description->dependencies) { - if (!add_package_dependency(packages, *dep, where, !(action & PACKAGE_ACT_NOTHING))) return false; + if (!add_package_dependency(packages, package, *dep, where, !(action & PACKAGE_ACT_NOTHING), already_checked)) return false; } return true; } else if ((action & PACKAGE_ACT_REMOVE) || ((action & PACKAGE_ACT_NOTHING) && !package->has(PACKAGE_INSTALLED))) { @@ -608,7 +621,7 @@ bool set_package_action(InstallablePackages& packages, const InstallablePackageP // ----------------------------------------------------------------------------- : MSE package -String mse_package = _("magicseteditor.exe"); +String mse_package = _("Magic Set Editor"); InstallablePackageP mse_installable_package() { PackageVersionP mse_version(new PackageVersion( @@ -618,11 +631,10 @@ InstallablePackageP mse_installable_package() { mse_version->name = mse_package; mse_version->version = app_version; PackageDescriptionP mse_description(new PackageDescription); - mse_description->name = mse_package; - mse_description->short_name = mse_description->full_name = mse_description->installer_group - = _TITLE_("magic set editor"); - mse_description->position_hint = -100; - mse_description->icon = load_resource_image(_("installer_program")); + mse_description->name = mse_description->installer_group = mse_package; + mse_description->short_name = mse_description->full_name = _TITLE_("magic set editor"); + mse_description->position_hint = -100; + mse_description->icon = load_resource_image(_("installer_program")); //mse_description->description = _LABEL_("magic set editor package"); return make_intrusive(mse_description, mse_version); } diff --git a/src/data/installer.hpp b/src/data/installer.hpp index ac446c9e..c6f1c87f 100644 --- a/src/data/installer.hpp +++ b/src/data/installer.hpp @@ -191,7 +191,7 @@ bool set_package_action(InstallablePackages& packages, const InstallablePackageP // ----------------------------------------------------------------------------- : Program package -/// The "magicseteditor.exe" package is special, it refers to the program +/// The "Magic Set Editor" package is special, it refers to the program extern String mse_package; InstallablePackageP mse_installable_package(); diff --git a/src/data/settings.cpp b/src/data/settings.cpp index cff49b1e..a72e5dd9 100644 --- a/src/data/settings.cpp +++ b/src/data/settings.cpp @@ -26,9 +26,16 @@ // ----------------------------------------------------------------------------- : Extra types IMPLEMENT_REFLECTION_ENUM(CheckUpdates) { - VALUE_N("if connected", CHECK_IF_CONNECTED); //default - VALUE_N("always", CHECK_ALWAYS); - VALUE_N("never", CHECK_NEVER); + VALUE_N("always", CHECK_ALWAYS); + VALUE_N("every 5", CHECK_5); //default + VALUE_N("every 10", CHECK_10); + VALUE_N("never", CHECK_NEVER); +} + +IMPLEMENT_REFLECTION_ENUM(CheckUpdatesTargets) { + VALUE_N("update app", CHECK_APP); + VALUE_N("update games", CHECK_GAMES); + VALUE_N("update everything", CHECK_EVERYTHING); //default } IMPLEMENT_REFLECTION_ENUM(InstallType) { @@ -203,13 +210,10 @@ Settings::Settings() , dark_mode_type (DARKMODE_SYSTEM) , import_scale_selection (0) , allow_image_download (true) - #if USE_OLD_STYLE_UPDATE_CHECKER - , updates_url (_("https://magicseteditor.boards.net/page/downloads")) - #endif - , package_versions_url (_("https://magicseteditor.boards.net/page/downloads")) - , installer_list_url (_("https://magicseteditor.boards.net/page/downloads")) - , check_updates (CHECK_IF_CONNECTED) - , check_updates_all (true) + , installer_list_url (_("https://raw.githubusercontent.com/MagicSetEditorPacks/Installer-Pack/refs/heads/main/packages.txt")) + , check_updates_what (CHECK_EVERYTHING) + , check_updates_when (CHECK_5) + , check_updates_counter (0) , website_url (_("https://magicseteditor.boards.net/")) , documentation_url (_("https://mseverse.miraheze.org/wiki/Dev:Documentation#Topics")) , install_type (INSTALL_DEFAULT) @@ -344,15 +348,9 @@ IMPLEMENT_REFLECTION_NO_SCRIPT(Settings) { REFLECT(apprentice_location); REFLECT(import_scale_selection); REFLECT(allow_image_download); - #if USE_OLD_STYLE_UPDATE_CHECKER - REFLECT(updates_url); - #else - REFLECT_COMPAT_IGNORE(<306,"updates_url",String); - #endif - REFLECT(package_versions_url); - REFLECT(installer_list_url); - REFLECT(check_updates); - REFLECT(check_updates_all); + REFLECT(check_updates_what); + REFLECT(check_updates_when); + REFLECT(check_updates_counter); REFLECT(install_type); REFLECT(website_url); REFLECT(documentation_url); diff --git a/src/data/settings.hpp b/src/data/settings.hpp index 522108f2..870387a0 100644 --- a/src/data/settings.hpp +++ b/src/data/settings.hpp @@ -24,18 +24,23 @@ DECLARE_POINTER_TYPE(Field); DECLARE_POINTER_TYPE(Value); DECLARE_POINTER_TYPE(AutoReplace); -// For now, use the old style update checker -#define USE_OLD_STYLE_UPDATE_CHECKER 1 - // ----------------------------------------------------------------------------- : Extra data structures /// When to check for updates? enum CheckUpdates { CHECK_ALWAYS -, CHECK_IF_CONNECTED +, CHECK_5 +, CHECK_10 , CHECK_NEVER }; +/// What to check for updates? +enum CheckUpdatesTargets +{ CHECK_APP +, CHECK_GAMES +, CHECK_EVERYTHING +}; + /// Where to install to? enum InstallType { INSTALL_DEFAULT // the platform default. @@ -236,13 +241,13 @@ public: // --------------------------------------------------- : Update checking - #if USE_OLD_STYLE_UPDATE_CHECKER - String updates_url; - #endif - String package_versions_url; ///< latest package versions String installer_list_url; ///< available installers - CheckUpdates check_updates; - bool check_updates_all; ///< Check updates of all packages, not just the program + CheckUpdatesTargets check_updates_what; + CheckUpdates check_updates_when; + int check_updates_counter; + + // --------------------------------------------------- : Help links + String website_url; String documentation_url; diff --git a/src/data/updater.cpp b/src/data/updater.cpp new file mode 100644 index 00000000..8d0963cb --- /dev/null +++ b/src/data/updater.cpp @@ -0,0 +1,50 @@ +//+----------------------------------------------------------------------------+ +//| 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 + +// ----------------------------------------------------------------------------- : Updater + +void Updater::updateApplication(String argv) { + String path = absoluteFilename() + wxFileName::GetPathSeparator() + updater_name; + path = path + _(".exe"); + if (wxFileExists(path)) { + wxExecute(path + _(" ") + argv, wxEXEC_ASYNC); + } + else { + queue_message(MESSAGE_ERROR, _("Executable file '" + path + "' not found!")); + } +} + +UpdaterP Updater::byName(const String& name) { + return package_manager.open(name + _(".mse-updater")); +} + +IMPLEMENT_REFLECTION_NO_SCRIPT(Updater) { + REFLECT_BASE(Packaged); + REFLECT(updater_name); +} + +String Updater::typeNameStatic() { return _("updater"); } +String Updater::typeName() const { return _("updater"); } +Version Updater::fileVersion() const { return app_version; } + +void Updater::validate(Version file_app_version) { + Packaged::validate(file_app_version); +} + +// special behaviour of reading/writing UpdaterPs: only read/write the name + +void Reader::handle(UpdaterP& updater) { + updater = Updater::byName(getValue()); +} +void Writer::handle(const UpdaterP& updater) { + if (updater) handle(updater->name()); +} diff --git a/src/data/updater.hpp b/src/data/updater.hpp new file mode 100644 index 00000000..1292eb65 --- /dev/null +++ b/src/data/updater.hpp @@ -0,0 +1,44 @@ +//+----------------------------------------------------------------------------+ +//| 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) | +//+----------------------------------------------------------------------------+ + +#pragma once + +// ----------------------------------------------------------------------------- : Includes + +#include +#include +#include + +DECLARE_POINTER_TYPE(Updater); + +// ----------------------------------------------------------------------------- : Updater + +/// A description of an updater for the app (There should really only ever be one updater) +class Updater : public Packaged { +public: + Updater() {}; + + String updater_name; + + // Close MSE and run the updater exe + void updateApplication(String argv); + + static UpdaterP byName(const String& name); + + static String typeNameStatic(); + String typeName() const override; + Version fileVersion() const override; + +protected: + void validate(Version) override; + + DECLARE_REFLECTION(); +}; + +inline String type_name(const Updater&) { + return _TYPE_("updater"); +} + diff --git a/src/gfx/generated_image.cpp b/src/gfx/generated_image.cpp index 504fffaa..848db230 100644 --- a/src/gfx/generated_image.cpp +++ b/src/gfx/generated_image.cpp @@ -111,9 +111,9 @@ ImageCombine LinearBlendImage::combine() const { bool LinearBlendImage::operator == (const GeneratedImage& that) const { const LinearBlendImage* that2 = dynamic_cast(&that); return that2 && *image1 == *that2->image1 - && *image2 == *that2->image2 - && x1 == that2->x1 && y1 == that2->y1 - && x2 == that2->x2 && y2 == that2->y2; + && *image2 == *that2->image2 + && x1 == that2->x1 && y1 == that2->y1 + && x2 == that2->x2 && y2 == that2->y2; } // ----------------------------------------------------------------------------- : MaskedBlendImage @@ -129,8 +129,8 @@ ImageCombine MaskedBlendImage::combine() const { bool MaskedBlendImage::operator == (const GeneratedImage& that) const { const MaskedBlendImage* that2 = dynamic_cast(&that); return that2 && *light == *that2->light - && *dark == *that2->dark - && *mask == *that2->mask; + && *dark == *that2->dark + && *mask == *that2->mask; } // ----------------------------------------------------------------------------- : CombineBlendImage @@ -146,8 +146,8 @@ ImageCombine CombineBlendImage::combine() const { bool CombineBlendImage::operator == (const GeneratedImage& that) const { const CombineBlendImage* that2 = dynamic_cast(&that); return that2 && *image1 == *that2->image1 - && *image2 == *that2->image2 - && image_combine == that2->image_combine; + && *image2 == *that2->image2 + && image_combine == that2->image_combine; } // ----------------------------------------------------------------------------- : SetMaskImage @@ -160,7 +160,7 @@ Image SetMaskImage::generate(const Options& opt) { bool SetMaskImage::operator == (const GeneratedImage& that) const { const SetMaskImage* that2 = dynamic_cast(&that); return that2 && *image == *that2->image - && *mask == *that2->mask; + && *mask == *that2->mask; } Image SetAlphaImage::generate(const Options& opt) { @@ -171,7 +171,7 @@ Image SetAlphaImage::generate(const Options& opt) { bool SetAlphaImage::operator == (const GeneratedImage& that) const { const SetAlphaImage* that2 = dynamic_cast(&that); return that2 && *image == *that2->image - && alpha == that2->alpha; + && alpha == that2->alpha; } // ----------------------------------------------------------------------------- : SetCombineImage @@ -185,7 +185,7 @@ ImageCombine SetCombineImage::combine() const { bool SetCombineImage::operator == (const GeneratedImage& that) const { const SetCombineImage* that2 = dynamic_cast(&that); return that2 && *image == *that2->image - && image_combine == that2->image_combine; + && image_combine == that2->image_combine; } // ----------------------------------------------------------------------------- : SaturateImage @@ -198,7 +198,7 @@ Image SaturateImage::generate(const Options& opt) { bool SaturateImage::operator == (const GeneratedImage& that) const { const SaturateImage* that2 = dynamic_cast(&that); return that2 && *image == *that2->image - && amount == that2->amount; + && amount == that2->amount; } // ----------------------------------------------------------------------------- : InvertImage @@ -223,7 +223,7 @@ Image RecolorImage::generate(const Options& opt) { bool RecolorImage::operator == (const GeneratedImage& that) const { const RecolorImage* that2 = dynamic_cast(&that); return that2 && *image == *that2->image - && color == that2->color; + && color == that2->color; } Image RecolorImage2::generate(const Options& opt) { @@ -234,10 +234,10 @@ Image RecolorImage2::generate(const Options& opt) { bool RecolorImage2::operator == (const GeneratedImage& that) const { const RecolorImage2* that2 = dynamic_cast(&that); return that2 && *image == *that2->image - && red == that2->red - && green == that2->green - && blue == that2->blue - && white == that2->white; + && red == that2->red + && green == that2->green + && blue == that2->blue + && white == that2->white; } // ----------------------------------------------------------------------------- : FlipImage @@ -267,7 +267,7 @@ Image RotateImage::generate(const Options& opt) { bool RotateImage::operator == (const GeneratedImage& that) const { const RotateImage* that2 = dynamic_cast(&that); return that2 && *image == *that2->image - && angle == that2->angle; + && angle == that2->angle; } // ----------------------------------------------------------------------------- : EnlargeImage @@ -275,7 +275,7 @@ bool RotateImage::operator == (const GeneratedImage& that) const { Image EnlargeImage::generate(const Options& opt) { // generate 'sub' image Options sub_opt - ( int(opt.width * (border_size < 0.5 ? 1 - 2 * border_size : 0)) + ( int(opt.width * (border_size < 0.5 ? 1 - 2 * border_size : 0)) , int(opt.height * (border_size < 0.5 ? 1 - 2 * border_size : 0)) , opt.package , opt.local_package @@ -310,7 +310,7 @@ Image EnlargeImage::generate(const Options& opt) { bool EnlargeImage::operator == (const GeneratedImage& that) const { const EnlargeImage* that2 = dynamic_cast(&that); return that2 && *image == *that2->image - && border_size == that2->border_size; + && border_size == that2->border_size; } // ----------------------------------------------------------------------------- : ResizeImage @@ -509,26 +509,9 @@ bool InsertedImage::operator == (const GeneratedImage& that) const { Image CropImage::generate(const Options& opt) { if (width <= 0) throw ScriptError(_ERROR_1_("negative image width", "crop_image")); - if (height <= 0) throw ScriptError(_ERROR_1_("negative image height", "crop_image")); - UInt size = width * height; - Image img = wxImage(width, height, false); - img.InitAlpha(); - Byte* data = img.GetData(); - Byte* alpha = img.GetAlpha(); - Byte r = background_color.Red(); - Byte g = background_color.Green(); - Byte b = background_color.Blue(); - Byte a = background_color.Alpha(); - for (UInt i = 0; i < size; ++i) { - data[0] = r; - data[1] = g; - data[2] = b; - data += 3; - alpha[0] = a; - alpha += 1; - } - Image base_img = image->generate(opt); - img.Paste(base_img, -(int)offset_x, -(int)offset_y, wxIMAGE_ALPHA_BLEND_COMPOSE); + if (height <= 0) throw ScriptError(_ERROR_1_("negative image height", "crop_image")); + Image base_img = image->generate(opt); + Image img = base_img.Size(wxSize((int)width, (int)height), wxPoint(-(int)offset_x, -(int)offset_y)); //Image img = base_img.Size(wxSize((int)width, (int)height), wxPoint(-(int)offset_x, -(int)offset_y), background_color.Red(), background_color.Green(), background_color.Blue()); // transfer metadata if (base_img.HasOption(wxIMAGE_OPTION_PNG_DESCRIPTION)) { String metadata = transformAllEncodedRects(base_img.GetOption(wxIMAGE_OPTION_PNG_DESCRIPTION), RealRect::translate, -offset_x, -offset_y); @@ -556,9 +539,9 @@ Image CropImage::generate(const Options& opt) { bool CropImage::operator == (const GeneratedImage& that) const { const CropImage* that2 = dynamic_cast(&that); return that2 && *image == *that2->image - && width == that2->width && height == that2->height - && offset_x == that2->offset_x && offset_y == that2->offset_y - && background_color == that2->background_color; + && width == that2->width && height == that2->height + && offset_x == that2->offset_x && offset_y == that2->offset_y + && background_color == that2->background_color; } // ----------------------------------------------------------------------------- : DropShadowImage @@ -648,9 +631,9 @@ Image DropShadowImage::generate(const Options& opt) { bool DropShadowImage::operator == (const GeneratedImage& that) const { const DropShadowImage* that2 = dynamic_cast(&that); return that2 && *image == *that2->image - && offset_x == that2->offset_x && offset_y == that2->offset_y - && shadow_alpha == that2->shadow_alpha && shadow_blur_radius == that2->shadow_blur_radius - && shadow_color == that2->shadow_color; + && offset_x == that2->offset_x && offset_y == that2->offset_y + && shadow_alpha == that2->shadow_alpha && shadow_blur_radius == that2->shadow_blur_radius + && shadow_color == that2->shadow_color; } // ----------------------------------------------------------------------------- : PackagedImage @@ -728,11 +711,11 @@ Image SymbolToImage::generate(const Options& opt) { bool SymbolToImage::operator == (const GeneratedImage& that) const { const SymbolToImage* that2 = dynamic_cast(&that); return that2 && is_local == that2->is_local - && filename == that2->filename - && age == that2->age - && (variation == that2->variation || - *variation == *that2->variation // custom variation - ); + && filename == that2->filename + && age == that2->age + && (variation == that2->variation || + *variation == *that2->variation // custom variation + ); } // ----------------------------------------------------------------------------- : ImageValueToImage @@ -758,7 +741,7 @@ Image ImageValueToImage::generate(const Options& opt) { bool ImageValueToImage::operator == (const GeneratedImage& that) const { const ImageValueToImage* that2 = dynamic_cast(&that); return that2 && filename == that2->filename - && age == that2->age; + && age == that2->age; } // ----------------------------------------------------------------------------- : SetMetadataImage @@ -771,7 +754,7 @@ Image SetMetadataImage::generate(const Options& opt) { bool SetMetadataImage::operator == (const GeneratedImage& that) const { const SetMetadataImage* that2 = dynamic_cast(&that); return that2 && *image == *that2->image - && metadata == that2->metadata; + && metadata == that2->metadata; } // ----------------------------------------------------------------------------- : ImportedImage diff --git a/src/gui/control/tree_list.cpp b/src/gui/control/tree_list.cpp index 517ae792..2d77399d 100644 --- a/src/gui/control/tree_list.cpp +++ b/src/gui/control/tree_list.cpp @@ -138,7 +138,7 @@ TreeList::TreeList(Window* parent, int id, long style) void TreeList::onPaint(wxPaintEvent& ev) { wxBufferedPaintDC dc(this); size_t cols = columnCount(); - wxRendererNative& rn = wxRendererNative::GetDefault(); + //wxRendererNative& rn = wxRendererNative::GetDefault(); // clear background wxSize cs = GetClientSize(); dc.SetPen(*wxTRANSPARENT_PEN); @@ -149,11 +149,13 @@ void TreeList::onPaint(wxPaintEvent& ev) { // draw header dc.SetFont(*wxNORMAL_FONT); dc.SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNTEXT)); + dc.SetPen(lerp(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW), + wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT),0.4)); int x = 0, y = 0; for (size_t j = 0 ; j < cols ; ++j) { int w = columnWidth(j); - wxRect rect(x,0,w-1,header_height-1); - rn.DrawHeaderButton(this, dc, rect); + wxRect rect(x,0,w-1,header_height-1); + if (j>0) dc.DrawLine(x-1,2,x-1,header_height-2); //rn.DrawHeaderButton(this, dc, rect); dc.DrawText(columnText(j),x+3,2); x += w; } @@ -183,9 +185,16 @@ void TreeList::onPaint(wxPaintEvent& ev) { dc.DrawLine(8 + level_width*item.level,y,8 + level_width*item.level,y+item_height/2+1); } // draw expand button - if (hasChildren(i)) { - wxRect rect(x - 13, y + (item_height - 9)/2, 9, 9); - rn.DrawTreeItemButton(this, dc, rect, item.expanded ? wxCONTROL_EXPANDED : 0); + if (hasChildren(i)) { + int left = x - 13, top = y + (item_height - 9)/2; + wxRect rect(left, top, 9, 9); + dc.SetPen(lerp(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW), + wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT),0.4)); + dc.SetBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE)); + dc.DrawRectangle(rect); + dc.DrawLine(left+2,top+4,left+7,top+4); + if (!item.expanded) dc.DrawLine(left+4,top+2,left+4,top+7); + //rn.DrawTreeItemButton(this, dc, rect, item.expanded ? wxCONTROL_EXPANDED : 0); } if (selection == i) { dc.SetPen(*wxTRANSPARENT_PEN); diff --git a/src/gui/control/tree_list.hpp b/src/gui/control/tree_list.hpp index 1b109529..4545b9d8 100644 --- a/src/gui/control/tree_list.hpp +++ b/src/gui/control/tree_list.hpp @@ -44,8 +44,8 @@ public: size_t position; // NOTHING if invisible, otherwise the line the item is on UInt lines; // lines in front of this item (bit set) }; - typedef intrusive_ptr ItemP; - + typedef intrusive_ptr ItemP; + protected: /// The items in the tree list @@ -68,7 +68,7 @@ protected: virtual int columnWidth(size_t column) const = 0; int item_height; - static const int header_height = 17; + static const int header_height = 21; static const int level_width = 17; private: diff --git a/src/gui/downloadable_installers.hpp b/src/gui/downloadable_installers.hpp new file mode 100644 index 00000000..53c3c6a3 --- /dev/null +++ b/src/gui/downloadable_installers.hpp @@ -0,0 +1,160 @@ +//+----------------------------------------------------------------------------+ +//| 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 + +class DownloadableInstallerList; + +// ----------------------------------------------------------------------------- : DownloadableInstallers + +/// The global installer downloader +extern DownloadableInstallerList downloadable_installers; + +/// Handle downloading of installers +class DownloadableInstallerList { +public: + inline DownloadableInstallerList() : download_status(NOT_DOWNLOADED), check_status(NOT_CHECKED), shown_dialog(false) {} + + /// Check for updates if the settings say so + inline void check_updates() { + settings.check_updates_counter++; + if ( + (settings.check_updates_when == CHECK_ALWAYS) + || (settings.check_updates_when == CHECK_5 && settings.check_updates_counter > 4) + || (settings.check_updates_when == CHECK_10 && settings.check_updates_counter > 9) + ) { + settings.check_updates_counter = 0; + check_updates_now(); + } + } + /// Check for updates + /// If async==true then checking is done in another thread + inline void check_updates_now(bool async = true) { + if (async) { + CheckUpdateThread* thread = new CheckUpdateThread; + thread->Create(); + thread->Run(); + } else { + CheckUpdateThread::Work(); + } + } + + /// Start downloading the list of updates, return true if we are done + inline bool download() { + if (download_status == DONE) return true; + if (download_status == NOT_DOWNLOADED) { + download_status = DOWNLOADING; + DownloadThread* thread = new DownloadThread(); + thread->Create(); + thread->Run(); + } + return false; + } + + /// Show a dialog to inform the user that updates are available (if there are any) + /// Call check_updates first. Call this function from an onIdle loop + inline void show_update_dialog(Window* parent) { + if (shown_dialog || check_status != FOUND) return; // we already have the latest version, or this has already been displayed. + shown_dialog = true; + wxMessageDialog dial = wxMessageDialog(parent, _LABEL_("updates found"), _TITLE_("updates available"), wxYES_NO); + if (dial.ShowModal() == wxID_YES) { + (new PackagesWindow(parent))->Show(); + } + } + + // Have we shown the update dialog? + bool shown_dialog; + + vector installers; + + enum DownloadStatus { NOT_DOWNLOADED, DOWNLOADING, DONE } download_status; + enum CheckStatus { NOT_CHECKED, CHECKING, FAILED, FOUND, NOT_FOUND } check_status; +private: + wxMutex lock; + + struct DownloadThread : public wxThread { + inline ExitCode Entry() override { + // fetch list + wxWebRequestSync request = wxWebSessionSync::GetDefault().CreateRequest(settings.installer_list_url); + auto const result = request.Execute(); + if (!result) { + wxMutexLocker l(downloadable_installers.lock); + downloadable_installers.download_status = DONE; + downloadable_installers.check_status = FAILED; + return 0; + } + wxInputStream* is = request.GetResponse().GetStream(); + // 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.download_status = DONE; + return 0; + } + }; + + struct CheckUpdateThread : public wxThread { + public: + inline void* Entry() override { +#ifndef __APPLE__ + Work(); +#endif + return 0; + } + + inline static void Work() { + if (downloadable_installers.check_status > NOT_CHECKED) return; // don't check multiple times simultaneously + downloadable_installers.check_status = CHECKING; + try { + while (!downloadable_installers.download()) { + wxMilliSleep(30); + } + if (downloadable_installers.check_status == FAILED) return; + InstallablePackages installable_packages; + FOR_EACH(inst, downloadable_installers.installers) { + merge(installable_packages, inst); + } + FOR_EACH(p, installable_packages) { + if (p->description->name == mse_package && app_version < p->description->version) { + downloadable_installers.check_status = FOUND; + return; + } + Version v; + if (package_manager.installedVersion(p->description->name, v) && v < p->description->version) { + if ( + (settings.check_updates_what == CHECK_EVERYTHING) + || (settings.check_updates_what == CHECK_GAMES && ( p->description->name.EndsWith("mse-updater") + || p->description->name.EndsWith("mse-locale") + || p->description->name.EndsWith("mse-game") + || p->description->name.EndsWith("mse-include") + || p->description->name.EndsWith("mse-style") + || p->description->name.EndsWith("mse-symbol-font"))) + || (settings.check_updates_what == CHECK_APP && ( p->description->name.EndsWith("mse-updater") + || p->description->name.EndsWith("mse-locale"))) + ) + downloadable_installers.check_status = FOUND; + return; + } + } + downloadable_installers.check_status = NOT_FOUND; + } catch (...) { + // ignore all errors, we don't want problems if update checking fails + downloadable_installers.check_status = FAILED; + } + } + }; +}; + diff --git a/src/gui/package_update_list.cpp b/src/gui/package_update_list.cpp index fc485963..11b5f423 100644 --- a/src/gui/package_update_list.cpp +++ b/src/gui/package_update_list.cpp @@ -11,7 +11,7 @@ #include #include #include -#include +#include // ----------------------------------------------------------------------------- : PackageUpdateList::TreeItem @@ -85,12 +85,14 @@ PackageUpdateList::TreeItem::PackageType PackageUpdateList::TreeItem::package_ty if (desc.name == mse_package) return TYPE_PROG; size_t pos = desc.name.find_last_of(_('.')); if (pos == String::npos) return TYPE_OTHER; - if (is_substr(desc.name,pos,_(".mse-locale"))) return TYPE_LOCALE; - if (is_substr(desc.name,pos,_(".mse-game"))) return TYPE_GAME; if (is_substr(desc.name,pos,_(".mse-style"))) return TYPE_STYLESHEET; - if (is_substr(desc.name,pos,_(".mse-export-template"))) return TYPE_EXPORT_TEMPLATE; if (is_substr(desc.name,pos,_(".mse-symbol-font"))) return TYPE_SYMBOL_FONT; + if (is_substr(desc.name,pos,_(".mse-game"))) return TYPE_GAME; + if (is_substr(desc.name,pos,_(".mse-locale"))) return TYPE_LOCALE; + if (is_substr(desc.name,pos,_(".mse-export-template"))) return TYPE_EXPORT_TEMPLATE; + if (is_substr(desc.name,pos,_(".mse-import-template"))) return TYPE_IMPORT_TEMPLATE; if (is_substr(desc.name,pos,_(".mse-include"))) return TYPE_INCLUDE; + if (is_substr(desc.name,pos,_(".mse-updater"))) return TYPE_PROG; if (is_substr(desc.name,pos,_(".ttf"))) return TYPE_FONT; return TYPE_OTHER; } @@ -164,13 +166,17 @@ public: , list(list), ti(ti) {} - Image generate() override { - wxURL url(settings.darkMode() && !ti->package->description->dark_icon_url.empty() ? ti->package->description->dark_icon_url : ti->package->description->icon_url); - unique_ptr isP(url.GetInputStream()); - if (!isP) return wxImage(); - SeekAtStartInputStream is2(*isP); - Image result(is2); - return result; + Image generate() override { + String url(settings.darkMode() && !ti->package->description->dark_icon_url.empty() ? ti->package->description->dark_icon_url : ti->package->description->icon_url); + wxWebRequestSync request = wxWebSessionSync::GetDefault().CreateRequest(url); + auto const result = request.Execute(); + if (!result) return Image(); + wxInputStream* is(request.GetResponse().GetStream()); + if (!is) return Image(); + Image img(*is); + if (!img.IsOk()) return Image(); + ti->setIcon(img); + return img; } void store(const Image& image) override { if (!image.Ok()) return; @@ -247,7 +253,7 @@ void PackageUpdateList::drawItem(DC& dc, size_t index, size_t column, int x, int dc.DrawText(capitalize_sentence(ti.label), x+17, y); } else if (column == 1 && ti.package) { // Status - InstallablePackage& package = *ti.package; + InstallablePackage& package = *ti.package; if (package.has(PACKAGE_CONFLICTS)) { dc.SetTextForeground(lerp(color,Color(255,0,0),0.8)); dc.DrawText(_LABEL_("package conflicts"), x,y); @@ -255,7 +261,7 @@ void PackageUpdateList::drawItem(DC& dc, size_t index, size_t column, int x, int dc.SetTextForeground(lerp(color,Color(255,255,0),0.5)); dc.DrawText(_LABEL_("package modified"), x,y); } else if (package.has(PACKAGE_UPDATES)) { - dc.SetTextForeground(lerp(color,Color(0,0,255),0.5)); + dc.SetTextForeground(lerp(color,settings.darkMode() ? Color(80,80,255) : Color(0,0,255),0.5)); dc.DrawText(_LABEL_("package updates"), x,y); } else if (package.has(PACKAGE_INSTALLED)) { dc.SetTextForeground(color); @@ -265,15 +271,20 @@ void PackageUpdateList::drawItem(DC& dc, size_t index, size_t column, int x, int dc.SetTextForeground(color); dc.DrawText(_LABEL_("package installable"), x,y); } + } else if (column == 1 && !ti.expanded) { + if (CheckChildrenForUpdates(ti)) { + dc.SetTextForeground(lerp(color, settings.darkMode() ? Color(80, 80, 255) : Color(0, 0, 255), 0.5)); + dc.DrawText(_LABEL_("package updates"), x, y); + } } else if (column == 2 && ti.package) { // Action InstallablePackage& package = *ti.package; if (package.has(PACKAGE_ACT_INSTALL)) { if (package.has(PACKAGE_UPDATES)) { - dc.SetTextForeground(lerp(color,Color(0,0,255),0.6)); + dc.SetTextForeground(lerp(color,settings.darkMode() ? Color(80,80,255) : Color(0,0,255),0.6)); dc.DrawText(_LABEL_("upgrade package"), x,y); } else if (package.has(PACKAGE_INSTALLED)) { - dc.SetTextForeground(lerp(color,Color(0,0,255),0.2)); + dc.SetTextForeground(lerp(color,settings.darkMode() ? Color(80,80,255) : Color(0,0,255),0.2)); dc.DrawText(_LABEL_("reinstall package"), x,y); } else { dc.SetTextForeground(lerp(color,Color(0,255,0),0.6)); @@ -286,6 +297,14 @@ void PackageUpdateList::drawItem(DC& dc, size_t index, size_t column, int x, int } } +bool PackageUpdateList::CheckChildrenForUpdates(const TreeItem& ti) const { + for (auto& c : ti.children) { + if (c->package && c->package->has(PACKAGE_UPDATES)) return true; + if (CheckChildrenForUpdates(*c)) return true; + } + return false; +} + String PackageUpdateList::columnText(size_t column) const { if (column == 0) return _LABEL_("package name"); else if (column == 1) return _LABEL_("package status"); @@ -296,9 +315,9 @@ String PackageUpdateList::columnText(size_t column) const { int PackageUpdateList::columnWidth(size_t column) const { if (column == 0) { wxSize cs = GetClientSize(); - return cs.x - 240; + return cs.x - 280; } else { - return 120; + return 140; } } diff --git a/src/gui/package_update_list.hpp b/src/gui/package_update_list.hpp index e0decdfa..cca35977 100644 --- a/src/gui/package_update_list.hpp +++ b/src/gui/package_update_list.hpp @@ -32,7 +32,7 @@ protected: // overridden methods from TreeList void initItems() override; void drawItem(DC& dc, size_t index, size_t column, int x, int y, bool selected) const override; - + size_t columnCount() const override { return 3; } String columnText(size_t column) const override; int columnWidth(size_t column) const override; @@ -61,6 +61,7 @@ private: TYPE_GAME, TYPE_STYLESHEET, TYPE_EXPORT_TEMPLATE, + TYPE_IMPORT_TEMPLATE, TYPE_SYMBOL_FONT, TYPE_INCLUDE, TYPE_FONT, @@ -73,8 +74,12 @@ private: void setIcon(const Image& image); bool highlight() const; - static PackageType package_type(const PackageDescription& desc); - }; + static PackageType package_type(const PackageDescription& desc); + + }; + + bool CheckChildrenForUpdates(const TreeItem& ti) const; + friend class PackageIconRequest; }; diff --git a/src/gui/packages_window.cpp b/src/gui/packages_window.cpp index 7ac0822a..572b219a 100644 --- a/src/gui/packages_window.cpp +++ b/src/gui/packages_window.cpp @@ -9,91 +9,24 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include -#include -#include -#include +#include #include #include -#include +#include +#include DECLARE_POINTER_TYPE(Installer); -// ----------------------------------------------------------------------------- : 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 { - ExitCode Entry() override; - }; -}; - -/// 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); - unique_ptr stream(url.GetInputStream()); - if (!stream) { - wxMutexLocker l(downloadable_installers.lock); - downloadable_installers.status = DONE; - return 0; - } - // Read installer list - Reader reader(*stream, 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; -} +DownloadableInstallerList downloadable_installers; // ----------------------------------------------------------------------------- : PackageInfoPanel @@ -237,7 +170,9 @@ void PackagesWindow::init(Window* parent, bool show_only_installable) { 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")); @@ -262,8 +197,13 @@ void PackagesWindow::init(Window* parent, bool show_only_installable) { 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); + 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(800,600); + SetSizerAndFit(v); wxUpdateUIEvent::SetMode(wxUPDATE_UI_PROCESS_SPECIFIED); UpdateWindowUI(wxUPDATE_UI_RECURSE); @@ -293,13 +233,17 @@ void PackagesWindow::onActionChange(wxCommandEvent& ev) { } void PackagesWindow::onOk(wxCommandEvent& ev) { - // Do we need a new version of MSE first? - // count number of packages to change + // 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) { + 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)) { @@ -322,28 +266,30 @@ void PackagesWindow::onOk(wxCommandEvent& ev) { } // progress dialog wxProgressDialog progress( - _TITLE_("installing updates"), - String::Format(_ERROR_("downloading updates"), 0, to_download), - to_change + to_download, - this, - wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT | wxPD_SMOOTH - ); + _TITLE_("installing updates"), + String::Format(_ERROR_("downloading updates"), 0, to_download), + to_change + to_download, + this, + wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT | wxPD_SMOOTH | wxSTAY_ON_TOP + ); // Clear package list package_manager.reset(); // Download installers int package_pos = 0, step = 0; - FOR_EACH(ip, installable_packages) { + FOR_EACH(ip, installable_packages) { + if (ip->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 - wxURL url(ip->installer->installer_url); - unique_ptr is(url.GetInputStream()); - if (!is) { + // 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)); - } - ip->installer->installer_file = wxFileName::CreateTempFileName(_("mse-installer")); + } + wxInputStream* is(request.GetResponse().GetStream()); + ip->installer->installer_file = wxFileName::CreateTempFileName(_("mse-installer")); wxFileOutputStream os(ip->installer->installer_file); os.Write(*is); os.Close(); @@ -355,7 +301,8 @@ void PackagesWindow::onOk(wxCommandEvent& ev) { // Install stuff package_pos = 0; int success = 0, install = 0, remove = 0; - FOR_EACH(ip, installable_packages) { + 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. @@ -367,13 +314,52 @@ void PackagesWindow::onOk(wxCommandEvent& ev) { success += 1; } } - // Done - progress.Update(step++); - wxMessageBox( - 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? @@ -393,9 +379,9 @@ void PackagesWindow::onUpdateUI(wxUpdateUIEvent& ev) { 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")); + w->SetLabel(!(package && package->installed) ? _BUTTON_("install package") + : (package && package->has(PACKAGE_UPDATES)) ? _BUTTON_("upgrade package") + : _BUTTON_("reinstall package")); break; case ID_REMOVE: w->SetValue(package && package->has(PACKAGE_ACT_REMOVE | where)); @@ -407,7 +393,8 @@ void PackagesWindow::onUpdateUI(wxUpdateUIEvent& ev) { } void PackagesWindow::onIdle(wxIdleEvent& ev) { - ev.RequestMore(!checkInstallerList()); + ev.RequestMore(!checkInstallerList()); + if (waiting_info && !waiting_for_list) waiting_info->SetLabel(_("")); } bool PackagesWindow::checkInstallerList(bool refresh) { @@ -429,12 +416,12 @@ bool PackagesWindow::checkInstallerList(bool refresh) { } 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) + 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() diff --git a/src/gui/packages_window.hpp b/src/gui/packages_window.hpp index 996b8189..7333642c 100644 --- a/src/gui/packages_window.hpp +++ b/src/gui/packages_window.hpp @@ -28,6 +28,7 @@ public: private: PackageUpdateList* package_list; ///< List of available packages PackageInfoPanel* package_info; ///< Description of the selected package + wxStaticText* waiting_info; ///< Did we get the list of installers? /// List of the packages shown in this window InstallablePackages installable_packages; diff --git a/src/gui/preferences_window.cpp b/src/gui/preferences_window.cpp index 802de1e4..6de24cc4 100644 --- a/src/gui/preferences_window.cpp +++ b/src/gui/preferences_window.cpp @@ -8,10 +8,11 @@ #include #include -#include #include #include #include +#include +#include #include #include #include @@ -97,9 +98,10 @@ public: private: DECLARE_EVENT_TABLE(); - - wxChoice* check_at_startup; - + + wxChoice* check_what; + wxChoice* check_when; + // check for updates void onCheckUpdatesNow(wxCommandEvent&); }; @@ -401,37 +403,51 @@ UpdatePreferencesPage::UpdatePreferencesPage(Window* parent) : PreferencesPage(parent) { // init controls - check_at_startup = new wxChoice(this, wxID_ANY); + check_what = new wxChoice(this, wxID_ANY); + check_when = new wxChoice(this, wxID_ANY); wxButton* check_now = new wxButton(this, ID_CHECK_UPDATES_NOW, _BUTTON_("check now")); // set values - check_at_startup->Append(_BUTTON_("always")); // 0 - check_at_startup->Append(_BUTTON_("if internet connection exists")); // 1 - check_at_startup->Append(_BUTTON_("never")); // 2 - check_at_startup->SetSelection(settings.check_updates); + check_when->Append(_BUTTON_("always")); // 0 + check_when->Append(_BUTTON_("every 5 startups")); // 1 + check_when->Append(_BUTTON_("every 10 startups")); // 2 + check_when->Append(_BUTTON_("never")); // 3 + check_when->SetSelection(settings.check_updates_when); + check_what->Append(_BUTTON_("check app")); // 0 + check_what->Append(_BUTTON_("check games")); // 1 + check_what->Append(_BUTTON_("check everything")); // 2 + check_what->SetSelection(settings.check_updates_what); // init sizer wxSizer* s = new wxBoxSizer(wxVERTICAL); - s->Add(new wxStaticText(this, wxID_ANY, _LABEL_("check at startup")), 0, wxALL, 8); - s->Add(check_at_startup, 0, wxALL & ~wxTOP, 8); - s->Add(check_now, 0, wxALL & ~wxTOP, 8); - s->Add(new wxStaticText(this, wxID_ANY, _LABEL_("checking requires internet")), 0, wxALL & ~wxTOP, 8); + s->Add(new wxStaticText(this, wxID_ANY, _LABEL_("check at startup")), 0, wxALL, 8); + s->Add(check_when, 0, wxALL & ~wxTOP, 8); + s->Add(check_now, 0, wxALL & ~wxTOP, 8); + s->Add(new wxStaticText(this, wxID_ANY, _LABEL_("check what targets")), 0, wxALL, 8); + s->Add(check_what, 0, wxALL & ~wxTOP, 8); + s->Add(new wxStaticText(this, wxID_ANY, _LABEL_("checking requires internet")), 0, wxALL, 8); SetSizer(s); } void UpdatePreferencesPage::store() { - int sel = check_at_startup->GetSelection(); - if (sel == 0) settings.check_updates = CHECK_ALWAYS; - else if (sel == 1) settings.check_updates = CHECK_IF_CONNECTED; - else settings.check_updates = CHECK_NEVER; + int sel1 = check_when->GetSelection(); + if (sel1 == 0) settings.check_updates_when = CHECK_ALWAYS; + else if (sel1 == 1) settings.check_updates_when = CHECK_5; + else if (sel1 == 2) settings.check_updates_when = CHECK_10; + else settings.check_updates_when = CHECK_NEVER; + + int sel2 = check_what->GetSelection(); + if (sel2 == 0) settings.check_updates_what = CHECK_APP; + else if (sel2 == 1) settings.check_updates_what = CHECK_GAMES; + else settings.check_updates_what = CHECK_EVERYTHING; } void UpdatePreferencesPage::onCheckUpdatesNow(wxCommandEvent&) { - check_updates_now(false); - if (!update_data_found()) { + downloadable_installers.check_updates_now(false); + if (downloadable_installers.check_status == DownloadableInstallerList::CheckStatus::FAILED) { wxMessageBox(_ERROR_("checking updates failed"), _TITLE_("update check"), wxICON_ERROR | wxOK); - } else if (!update_available()) { + } else if (downloadable_installers.check_status == DownloadableInstallerList::CheckStatus::NOT_FOUND) { wxMessageBox(_ERROR_("no updates"), _TITLE_("update check"), wxICON_INFORMATION | wxOK); } else { - show_update_dialog(GetParent()); + (new PackagesWindow(GetParent()))->Show(); } } diff --git a/src/gui/set/cards_panel.cpp b/src/gui/set/cards_panel.cpp index 6259313c..c8840aba 100644 --- a/src/gui/set/cards_panel.cpp +++ b/src/gui/set/cards_panel.cpp @@ -13,7 +13,6 @@ #include #include #include // for HoverButton -#include #include #include #include diff --git a/src/gui/set/window.cpp b/src/gui/set/window.cpp index 626fbc1c..2b0017ce 100644 --- a/src/gui/set/window.cpp +++ b/src/gui/set/window.cpp @@ -20,8 +20,8 @@ #include #include #include -#include #include +#include #include #include #include @@ -62,13 +62,13 @@ SetWindow::SetWindow(Window* parent, const SetP& set) add_menu_item_tr(menuFile, ID_FILE_SAVE_AS_DIRECTORY, "save", "save_set_as_directory"); add_menu_item_tr(menuFile, wxID_ANY, "export", "export", wxITEM_NORMAL, makeExportMenu()); menuFile->AppendSeparator(); - add_menu_item_tr(menuFile, ID_FILE_CHECK_UPDATES, settings.darkModePrefix() + "check_updates", "check_updates"); + add_menu_item_tr(menuFile, ID_FILE_CHECK_UPDATES, "check_updates", "check_updates"); #if USE_SCRIPT_PROFILING add_menu_item_tr(menuFile, ID_FILE_PROFILER, nullptr, "show_profiler"); #endif // menuFile->Append(ID_FILE_INSPECT, _("Inspect Internal Data..."), _("Shows a the data in the set using a tree structure")); // menuFile->AppendSeparator(); - add_menu_item_tr(menuFile, ID_FILE_RELOAD, settings.darkModePrefix() + "reload_data", "reload_data"); + add_menu_item_tr(menuFile, ID_FILE_RELOAD, "reload_data", "reload_data"); menuFile->AppendSeparator(); add_menu_item_tr(menuFile, ID_FILE_PRINT_PREVIEW, "print_preview", "print_preview"); add_menu_item_tr(menuFile, ID_FILE_PRINT, "print", "print"); @@ -854,7 +854,7 @@ void SetWindow::onMenuOpen(wxMenuEvent& ev) { void SetWindow::onIdle(wxIdleEvent& ev) { // Stuff that must be done in the main thread - show_update_dialog(this); + downloadable_installers.show_update_dialog(this); } // ----------------------------------------------------------------------------- : Event table diff --git a/src/gui/update_checker.cpp b/src/gui/update_checker.cpp deleted file mode 100644 index 13442a4f..00000000 --- a/src/gui/update_checker.cpp +++ /dev/null @@ -1,204 +0,0 @@ -//+----------------------------------------------------------------------------+ -//| 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