diff --git a/Makefile.am b/Makefile.am index 04c33cb3..6fd51bff 100644 --- a/Makefile.am +++ b/Makefile.am @@ -158,5 +158,6 @@ magicseteditor_SOURCES += ./src/util/alignment.cpp magicseteditor_SOURCES += ./src/util/tagged_string.cpp magicseteditor_SOURCES += ./src/util/error.cpp magicseteditor_SOURCES += ./src/util/age.cpp +magicseteditor_SOURCES += ./src/util/spec_sort.cpp magicseteditor_SOURCES += ./src/main.cpp magicseteditor_SOURCES += ./src/code_template.cpp diff --git a/data/en.mse-locale/locale b/data/en.mse-locale/locale index 1217e1ca..81bbbfe9 100644 --- a/data/en.mse-locale/locale +++ b/data/en.mse-locale/locale @@ -14,6 +14,7 @@ menu: export images: All Card I&mages... export apprentice: &Apprentice... export mws: Magic &Workstation... + check updates: Check &Updates... print preview: Print Pre&view... print: &Print... Ctrl+P reload data: Reload Data Ctrl+F5 @@ -35,7 +36,7 @@ menu: next card: Select &Next Card PgDn add card: &Add Card Ctrl++ add cards: Add &Multiple Cards... - remove card: &Remove Select Card Del + remove card: &Delete Selected Card orientation: &Orientation rotate 0: &Normal rotate 270: Rotated 90° &Clockwise @@ -105,6 +106,7 @@ help: export images: Export images for all cards export apprentice: Export the set so it can be played with in Apprentice export mws: Export the set so it can be played with in Magic Workstation + check updates: Open the update window to download new packages, such as games, styles, and locales. print preview: Shows cards as they will be printed print: Print cards from this set reload data: Reload all template files (game and style) as well as the set. @@ -516,6 +518,12 @@ title: export cancled: Export Cancled export html: Export to HTML save html: Export to HTML + + # Package Update Window + package list: Package Updates + package name: Package Name + package status: Current Status + new status: New Status ############################################################## Action (undo/redo) names action: @@ -617,6 +625,11 @@ error: # Stats panel dimension not found: There is no statistics dimension '%s' + + # Package update window + no packages: Found no package updates. + checking updates: Checking for updates. + ############################################################## Types used in scripts / shape names type: @@ -663,6 +676,17 @@ type: points: points handle: handle + # Package manager + installed: installed + uninstalled: uninstalled + upgradeable: upgradeable + + install: install + uninstall: uninstalled + upgrade: upgrade + do nothing: do nothing + new mse: MSE outdated + ############################################################## Magic game: diff --git a/data/magic.mse-game/game b/data/magic.mse-game/game index 41d37366..19829578 100644 --- a/data/magic.mse-game/game +++ b/data/magic.mse-game/game @@ -27,7 +27,6 @@ init script: + " pattern(/. WUBRG)" + " cycle(WUBRG)" + ")") - mana_has_guild := sort_rule(order: "") # Is there guild or half mana in the input? mana_filter := to_upper + mana_sort # Like mana filter, only also allow tap symbols: tap_filter := sort_rule(order: "") @@ -171,7 +170,7 @@ init script: sort_index := { card_color := card.card_color casting_cost := card.casting_cost - if card.casting_cost_2 != "" and + if card.shape = "split" and card_color != card.card_color_2 then "H" # multicolor splits else if chosen(choice: "land", card_color) then ( # land @@ -1490,7 +1489,7 @@ keyword: keyword: Regeneration match: Regenerate mode: action - reminder: The next time {if has_pt() then "this creature" else "this"} would be destroyed this turn, it isn't.{if has_pt() then " Instead tap it, remove all damage from it, and remove it from combat." else ""} + reminder: The next time {if has_pt() then "this creature" else "this"} would be destroyed this turn, it isn't.{if has_pt() then " Instead tap it, remove all damage from it, and remove it from combat." else " Instead tap it"} keyword: keyword: Bands with other match: Bands with other name @@ -1510,7 +1509,7 @@ keyword: keyword: Phasing match: Phasing mode: old - reminder: At the beginning of your upkeep, put this and any cards and/or counters attached to it in the phased-out zone. If this is already in the phased-out zone, return it and any cards and/or counters attached to it to play. This ability does not cause comes-into-play or leaves-play abilities to trigger. + reminder: At the beginning of your untap step, put this and any cards and/or counters attached to it in the phased-out zone. If this is already in the phased-out zone, return it and any cards and/or counters attached to it to play. This ability does not cause comes-into-play or leaves-play abilities to trigger. keyword: keyword: Flanking match: Flanking diff --git a/src/gui/set/cards_panel.cpp b/src/gui/set/cards_panel.cpp index f91681b2..a2c0878d 100644 --- a/src/gui/set/cards_panel.cpp +++ b/src/gui/set/cards_panel.cpp @@ -64,7 +64,7 @@ CardsPanel::CardsPanel(Window* parent, int id) menuCard->Append(ID_CARD_ADD_MULT, _("card_add_multiple"), _MENU_("add cards"), _HELP_("add cards")); // NOTE: space after "Del" prevents wx from making del an accellerator // otherwise we delete a card when delete is pressed inside the editor - menuCard->Append(ID_CARD_REMOVE, _("card_del"), _MENU_("remove card")+_(" "),_HELP_("remove card")); + menuCard->Append(ID_CARD_REMOVE, _("card_del"), _MENU_("remove card"),_HELP_("remove card")); menuCard->AppendSeparator(); IconMenu* menuRotate = new IconMenu(); menuRotate->Append(ID_CARD_ROTATE_0, _("card_rotate_0"), _MENU_("rotate 0"), _HELP_("rotate 0"), wxITEM_CHECK); diff --git a/src/gui/set/window.cpp b/src/gui/set/window.cpp index 227bf67c..42d2daec 100644 --- a/src/gui/set/window.cpp +++ b/src/gui/set/window.cpp @@ -65,6 +65,7 @@ SetWindow::SetWindow(Window* parent, const SetP& set) menuExport->Append(ID_FILE_EXPORT_MWS, _("export_mws"), _MENU_("export mws"), _HELP_("export mws")); menuFile->Append(wxID_ANY, _("export"), _MENU_("export"), _HELP_("export"), wxITEM_NORMAL, menuExport); menuFile->AppendSeparator(); + menuFile->Append(ID_FILE_CHECK_UPDATES, _MENU_("check updates"), _HELP_("check updates")); // menuFile->Append(ID_FILE_INSPECT, _("Inspect Internal Data..."), _("Shows a the data in the set using a tree structure")); // menuFile->AppendSeparator(); menuFile->Append(ID_FILE_RELOAD, _MENU_("reload data"), _HELP_("reload data")); @@ -466,7 +467,7 @@ void SetWindow::onFileInspect(wxCommandEvent&) { wnd.show(); }*/ -#if defined(__WXMSW__) +#if defined(__WXMSW__) #include "wx/msw/wrapcctl.h" #endif @@ -527,6 +528,10 @@ void SetWindow::onFileExportMWS(wxCommandEvent&) { export_mws(this, set); } +void SetWindow::onFileCheckUpdates(wxCommandEvent&) { + (new UpdateWindow)->Show(); +} + void SetWindow::onFilePrint(wxCommandEvent&) { print_set(this, set); } @@ -677,6 +682,7 @@ BEGIN_EVENT_TABLE(SetWindow, wxFrame) EVT_MENU (ID_FILE_EXPORT_HTML, SetWindow::onFileExportHTML) EVT_MENU (ID_FILE_EXPORT_APPR, SetWindow::onFileExportApprentice) EVT_MENU (ID_FILE_EXPORT_MWS, SetWindow::onFileExportMWS) + EVT_MENU (ID_FILE_CHECK_UPDATES, SetWindow::onFileCheckUpdates) // EVT_MENU (ID_FILE_INSPECT, SetWindow::onFileInspect) EVT_MENU (ID_FILE_PRINT, SetWindow::onFilePrint) EVT_MENU (ID_FILE_PRINT_PREVIEW, SetWindow::onFilePrintPreview) diff --git a/src/gui/set/window.hpp b/src/gui/set/window.hpp index 884704f1..9caaf02a 100644 --- a/src/gui/set/window.hpp +++ b/src/gui/set/window.hpp @@ -114,6 +114,7 @@ class SetWindow : public wxFrame, public SetView { void onFileExportHTML (wxCommandEvent&); void onFileExportApprentice(wxCommandEvent&); void onFileExportMWS (wxCommandEvent&); + void onFileCheckUpdates (wxCommandEvent&); void onFilePrint (wxCommandEvent&); void onFilePrintPreview (wxCommandEvent&); void onFileReload (wxCommandEvent&); diff --git a/src/gui/update_checker.cpp b/src/gui/update_checker.cpp index 66f5b635..3f7a42f6 100644 --- a/src/gui/update_checker.cpp +++ b/src/gui/update_checker.cpp @@ -15,10 +15,13 @@ #include #include #include +#include DECLARE_POINTER_TYPE(PackageVersionData); DECLARE_POINTER_TYPE(VersionData); +DECLARE_TYPEOF_COLLECTION(PackageVersionDataP); + // ----------------------------------------------------------------------------- : Update data /// Information on available packages @@ -55,7 +58,7 @@ IMPLEMENT_REFLECTION(PackageVersionData) { REFLECT(is_installer); REFLECT(version); REFLECT(app_version); - REFLECT(depends); + REFLECT_N("depends ons", depends); } IMPLEMENT_REFLECTION(VersionData) { @@ -67,6 +70,8 @@ IMPLEMENT_REFLECTION(VersionData) { // The information for the latest version VersionDataP update_version_data; +// Have we shown the update dialog? +bool shown_dialog = false; // Is update checking in progress? volatile bool checking_updates = false; @@ -75,6 +80,12 @@ bool update_available() { return update_version_data && update_version_data->ve // ----------------------------------------------------------------------------- : Update checking +BEGIN_DECLARE_EVENT_TYPES() +DECLARE_EVENT_TYPE(UPDATE_CHECK_FINISHED_EVT, -1) +END_DECLARE_EVENT_TYPES() + +DEFINE_EVENT_TYPE(UPDATE_CHECK_FINISHED_EVT) + // Thread to retrieve update information // Checks if the current version is the latest version // If not, displays a message @@ -142,14 +153,14 @@ 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() ); } }; void show_update_dialog(Window* parent) { - if (!update_available()) return; // we already have the latest version + if (!update_available() || shown_dialog) return; // we already have the latest version, or this has already been displayed. // Show update dialog wxDialog* dlg = new wxDialog(parent, wxID_ANY, _TITLE_("updates availible"), wxDefaultPosition); // controls @@ -165,5 +176,177 @@ void show_update_dialog(Window* parent) { dlg->SetSize(400,400); dlg->Show(); // And never show it again this run - update_version_data = VersionDataP(); + shown_dialog = true; } + +// ----------------------------------------------------------------------------- : UpdateWindow + +class PackageUpdateList: public wxVListBox { + public: + PackageUpdateList(UpdateWindow * parent) + : wxVListBox (parent, wxID_ANY, wxDefaultPosition, wxSize(480,210), wxNO_BORDER | wxVSCROLL) + , parent(parent) + { + if (!checking_updates && !update_version_data) { + check_updates_now(true); + } + SetItemCount(update_version_data ? update_version_data->packages.size() : 1); + } + + virtual void OnDrawItem(wxDC& dc, const wxRect& rect, size_t n) const { + static wxBrush greyBrush(Color(224,224,224)); + if (checking_updates) { + String text = _ERROR_("checking updates"); + wxSize text_size = dc.GetTextExtent(text); + + dc.SetPen(*wxTRANSPARENT_PEN); + dc.SetBrush(greyBrush); + dc.DrawRectangle(rect); + + dc.DrawText(text, rect.GetLeft() + (rect.GetWidth() - text_size.GetWidth()) / 2, rect.GetTop() + (rect.GetHeight() - text_size.GetHeight()) / 2); + } else if (!update_version_data || update_version_data->packages.empty()) { + String text = _ERROR_("no packages"); + wxSize text_size = dc.GetTextExtent(text); + + dc.SetPen(*wxTRANSPARENT_PEN); + dc.SetBrush(greyBrush); + dc.DrawRectangle(rect); + + dc.DrawText(text, rect.GetLeft() + (rect.GetWidth() - text_size.GetWidth()) / 2, rect.GetTop() + (rect.GetHeight() - text_size.GetHeight()) / 2); + } else { + static wxBrush darkBrush(Color(192,224,255)); + static wxBrush selectBrush(Color(96,96,192)); + + PackageVersionDataP pack = update_version_data->packages[n]; + UpdateWindow::PackageStatus status = parent->package_data[pack].first; + UpdateWindow::PackageAction action = parent->package_data[pack].second; + + dc.SetPen(*wxTRANSPARENT_PEN); + dc.SetBrush(IsSelected(n) ? selectBrush : (n % 2 ? *wxWHITE_BRUSH : darkBrush)); + + dc.DrawRectangle(rect); + + // These two arrays correspond to PackageStatus and PackageAction, respectively + static Color status_colors [] = { + Color(32,160,32) + ,Color(32,32,255) + ,Color(192,32,32) + }; + + static Color action_colors [] = { + Color(32,160,32) + ,Color(192,32,32) + ,Color(192,192,32) + ,Color(32,32,255) + ,Color(32,32,32) + }; + + // Ditto here (these are the locale names) + static String status_texts [] = { + _TYPE_("installed") + ,_TYPE_("uninstalled") + ,_TYPE_("upgradeable") + }; + + static String action_texts [] = { + _TYPE_("install") + ,_TYPE_("uninstall") + ,_TYPE_("upgrade") + ,_TYPE_("do nothing") + ,_TYPE_("new mse") + }; + + static Color textBack(0,0,0,wxALPHA_TRANSPARENT); + static Color packageFront(64,64,64); + + #define SELECT_WHITE(color) (IsSelected(n) ? *wxWHITE : color) + + dc.SetTextForeground(SELECT_WHITE(packageFront)); + dc.SetTextBackground(textBack); + dc.DrawText(pack->name, rect.GetLeft() + 1, rect.GetTop()); + + dc.SetTextForeground(SELECT_WHITE(status_colors[status])); + dc.DrawText(status_texts[status], rect.GetLeft() + 240, rect.GetTop()); + + dc.SetTextForeground(SELECT_WHITE(action_colors[action])); + dc.DrawText(action_texts[action], rect.GetLeft() + 360, rect.GetTop()); + + #undef SELECT_INVERT + } + } + + virtual wxCoord OnMeasureItem(size_t) const { + return (update_version_data && !update_version_data->packages.empty()) ? 15 : 210; + } + + void onUpdateCheckingFinished(wxEvent& event) { + SetItemCount(update_version_data ? update_version_data->packages.size() : 1); + } + + virtual wxCoord EstimateTotalHeight() const { + return (update_version_data && !update_version_data->packages.empty()) ? 15 * update_version_data->packages.size() : 210; + } + + private: + DECLARE_EVENT_TABLE() + + UpdateWindow * parent; +}; + +BEGIN_EVENT_TABLE(PackageUpdateList, wxVListBox) + EVT_CUSTOM(UPDATE_CHECK_FINISHED_EVT, -1, PackageUpdateList::onUpdateCheckingFinished) +END_EVENT_TABLE() + +UpdateWindow::UpdateWindow() + : Frame(nullptr, wxID_ANY, _TITLE_("package list"), wxDefaultPosition, wxSize(480,375), wxDEFAULT_DIALOG_STYLE | wxCLIP_CHILDREN) +{ + SetIcon(wxIcon()); + wxBoxSizer *v = new wxBoxSizer(wxVERTICAL); + + package_list = new PackageUpdateList(this); + description_window = new HtmlWindowToBrowser(this, wxID_ANY, wxDefaultPosition, wxSize(480,100), wxHW_SCROLLBAR_AUTO | wxSUNKEN_BORDER); + + wxCommandEvent ev; + SetDefaultPackageStatus(ev); + + package_title = new wxStaticText(this, wxID_ANY, _TITLE_("package name"), wxDefaultPosition, wxSize(120,15), wxALIGN_LEFT | wxST_NO_AUTORESIZE); + status_title = new wxStaticText(this, wxID_ANY, _TITLE_("package status"), wxDefaultPosition, wxSize(120,15), wxALIGN_LEFT | wxST_NO_AUTORESIZE); + new_title = new wxStaticText(this, wxID_ANY, _TITLE_("new status"), wxDefaultPosition, wxSize(120,15), wxALIGN_LEFT | wxST_NO_AUTORESIZE); + package_title->Move(1,0); + status_title->Move(240,0); + new_title->Move(360,0); + + v->AddSpacer(15); + v->Add(package_list); + v->Add(description_window); + + SetSizer(v); +} + +void UpdateWindow::SetDefaultPackageStatus(wxCommandEvent&) +{ + if (!update_version_data) + return; + FOR_EACH(p, update_version_data->packages) { + PackagedP pack; + try { pack = packages.openAny(p->name); } + catch (Error& e) { } // We couldn't open a package... wonder why? + + if (!pack) { + if (p->app_version > file_version) + package_data[p] = PackageData(STATUS_NOT_INSTALLED, ACTION_NEW_MSE); + else + package_data[p] = PackageData(STATUS_NOT_INSTALLED, ACTION_NOTHING); + } else if (pack->version < p->version) { + if (p->app_version > file_version) + package_data[p] = PackageData(STATUS_UPGRADEABLE, ACTION_NEW_MSE); + else + package_data[p] = PackageData(STATUS_UPGRADEABLE, ACTION_UPGRADE); + } else + package_data[p] = PackageData(STATUS_INSTALLED, ACTION_NOTHING); + } +} + +BEGIN_EVENT_TABLE(UpdateWindow, Frame) + EVT_COMMAND(-1, UPDATE_CHECK_FINISHED_EVT, UpdateWindow::SetDefaultPackageStatus) +END_EVENT_TABLE() diff --git a/src/gui/update_checker.hpp b/src/gui/update_checker.hpp index 92b2bad1..e6410d4e 100644 --- a/src/gui/update_checker.hpp +++ b/src/gui/update_checker.hpp @@ -10,6 +10,7 @@ // ----------------------------------------------------------------------------- : Includes #include +#include // ----------------------------------------------------------------------------- : Update checking @@ -26,6 +27,42 @@ void check_updates_now(bool async = true); * Call this function from an onIdle loop */ void show_update_dialog(Window* parent); +class PackageUpdateList; + +DECLARE_POINTER_TYPE(PackageVersionData); + +/// A window that displays the updates and allows the user to select some. +class UpdateWindow : public Frame { + public: + UpdateWindow(); + void DrawTitles(wxPaintEvent&); + + enum PackageStatus { + STATUS_INSTALLED, + STATUS_NOT_INSTALLED, + STATUS_UPGRADEABLE + }; + enum PackageAction { + ACTION_INSTALL, + ACTION_UNINSTALL, + ACTION_UPGRADE, + ACTION_NOTHING, + ACTION_NEW_MSE + }; + + typedef pair PackageData; + + map package_data; + + void SetDefaultPackageStatus(wxCommandEvent&); + private: + DECLARE_EVENT_TABLE(); + PackageUpdateList* package_list; + wxHtmlWindow* description_window; + + wxStaticText *package_title, *status_title, *new_title; +}; + /// Was update data found? bool update_data_found(); /// Is there an update? diff --git a/src/util/alignment.cpp b/src/util/alignment.cpp index 5be303e1..ce148037 100644 --- a/src/util/alignment.cpp +++ b/src/util/alignment.cpp @@ -92,6 +92,10 @@ IMPLEMENT_REFLECTION_ENUM(Direction) { VALUE_N("bottom to top", BOTTOM_TO_TOP); VALUE_N("horizontal", LEFT_TO_RIGHT); VALUE_N("vertical", TOP_TO_BOTTOM); + VALUE_N("top-right to bottom-left", TOP_RIGHT_TO_BOTTOM_LEFT); + VALUE_N("bottom-left to top-right", BOTTOM_LEFT_TO_TOP_RIGHT); + VALUE_N("top-left to bottom-right", TOP_LEFT_TO_BOTTOM_RIGHT); + VALUE_N("bottom-right to top-left", BOTTOM_RIGHT_TO_TOP_LEFT); } RealPoint move_in_direction(Direction dir, const RealPoint& point, const RealSize to_move, double spacing) { @@ -100,6 +104,10 @@ RealPoint move_in_direction(Direction dir, const RealPoint& point, const RealSiz case RIGHT_TO_LEFT: return RealPoint(point.x - to_move.width - spacing, point.y); case TOP_TO_BOTTOM: return RealPoint(point.x, point.y + to_move.height + spacing); case BOTTOM_TO_TOP: return RealPoint(point.x, point.y - to_move.height - spacing); + case TOP_RIGHT_TO_BOTTOM_LEFT: return RealPoint(point.x - to_move.width - spacing, point.y + to_move.height + spacing); + case BOTTOM_LEFT_TO_TOP_RIGHT: return RealPoint(point.x + to_move.width + spacing, point.y - to_move.height - spacing); + case TOP_LEFT_TO_BOTTOM_RIGHT: return RealPoint(point.x + to_move.width + spacing, point.y + to_move.height + spacing); + case BOTTOM_RIGHT_TO_TOP_LEFT: return RealPoint(point.x - to_move.width - spacing, point.y - to_move.height - spacing); default: return point; // should not happen } } diff --git a/src/util/alignment.hpp b/src/util/alignment.hpp index e19a5391..7e580f86 100644 --- a/src/util/alignment.hpp +++ b/src/util/alignment.hpp @@ -59,6 +59,10 @@ RealPoint align_in_rect(Alignment align, const RealSize& to_align, const RealRec enum Direction { LEFT_TO_RIGHT, RIGHT_TO_LEFT, TOP_TO_BOTTOM, BOTTOM_TO_TOP, + BOTTOM_RIGHT_TO_TOP_LEFT, + TOP_LEFT_TO_BOTTOM_RIGHT, + BOTTOM_LEFT_TO_TOP_RIGHT, + TOP_RIGHT_TO_BOTTOM_LEFT, HORIZONTAL = LEFT_TO_RIGHT, VERTICAL = TOP_TO_BOTTOM }; diff --git a/src/util/window_id.hpp b/src/util/window_id.hpp index 96ef7746..9cec1ebf 100644 --- a/src/util/window_id.hpp +++ b/src/util/window_id.hpp @@ -41,6 +41,7 @@ enum MenuID { , ID_FILE_RELOAD = 9 , ID_FILE_RECENT = wxID_FILE1 , ID_FILE_RECENT_MAX = wxID_FILE9 +, ID_FILE_CHECK_UPDATES = 10 // Edit menu , ID_EDIT_UNDO = wxID_UNDO