diff --git a/src/data/field/choice.cpp b/src/data/field/choice.cpp index 3f6ce6bc..bddcee9e 100644 --- a/src/data/field/choice.cpp +++ b/src/data/field/choice.cpp @@ -166,14 +166,19 @@ ChoiceStyle::ChoiceStyle(const ChoiceFieldP& field) , combine(COMBINE_NORMAL) , alignment(ALIGN_STRETCH) , colors_card_list(false) + , thumbnails(nullptr) {} +ChoiceStyle::~ChoiceStyle() { + delete thumbnails; +} + // TODO /* void ChoiceStyle::invalidate() { // rebuild choice images } -} + */ bool ChoiceStyle::update(Context& ctx) { // Don't update the choice images, leave that to invalidate() diff --git a/src/data/field/choice.hpp b/src/data/field/choice.hpp index cdc49700..f23759be 100644 --- a/src/data/field/choice.hpp +++ b/src/data/field/choice.hpp @@ -115,6 +115,7 @@ class ChoiceStyle : public Style { public: ChoiceStyle(const ChoiceFieldP& field); DECLARE_STYLE_TYPE(Choice); + ~ChoiceStyle(); ChoicePopupStyle popup_style; ///< Style of popups/menus ChoiceRenderStyle render_style; ///< Style of rendering @@ -126,6 +127,8 @@ class ChoiceStyle : public Style { ImageCombine combine; ///< Combining mode for drawing the images Alignment alignment; ///< Alignment of images Image mask; ///< The actual mask image + wxImageList* thumbnails; ///< Thumbnails for the choices + Age thumbnail_age; ///< Age the thumbnails were generated /// Load the mask image, if it's not already done void loadMask(Package& pkg); diff --git a/src/data/set.cpp b/src/data/set.cpp index ae2498eb..d76bfd23 100644 --- a/src/data/set.cpp +++ b/src/data/set.cpp @@ -48,15 +48,39 @@ Set::~Set() {} Context& Set::getContext() { + assert(wxThread::IsMain()); return script_manager->getContext(stylesheet); } Context& Set::getContext(const CardP& card) { + assert(wxThread::IsMain()); return script_manager->getContext(card); } void Set::updateFor(const CardP& card) { script_manager->updateStyles(card); } +Context& Set::getContextForThumbnails() { + assert(!wxThread::IsMain()); + if (!thumbnail_script_context) { + thumbnail_script_context.reset(new SetScriptContext(*this)); + } + return thumbnail_script_context->getContext(stylesheet); +} +Context& Set::getContextForThumbnails(const CardP& card) { + assert(!wxThread::IsMain()); + if (!thumbnail_script_context) { + thumbnail_script_context.reset(new SetScriptContext(*this)); + } + return thumbnail_script_context->getContext(card); +} +Context& Set::getContextForThumbnails(const StyleSheetP& stylesheet) { + assert(!wxThread::IsMain()); + if (!thumbnail_script_context) { + thumbnail_script_context.reset(new SetScriptContext(*this)); + } + return thumbnail_script_context->getContext(stylesheet); +} + StyleSheetP Set::stylesheetFor(const CardP& card) { if (card && card->stylesheet) return card->stylesheet; else return stylesheet; diff --git a/src/data/set.hpp b/src/data/set.hpp index b965cf07..b889f5d6 100644 --- a/src/data/set.hpp +++ b/src/data/set.hpp @@ -71,6 +71,9 @@ class Set : public Packaged { /// A context for performing scripts on a particular card /** Should only be used from the thumbnail thread! */ Context& getContextForThumbnails(const CardP& card); + /// A context for performing scripts on a particular stylesheet + /** Should only be used from the thumbnail thread! */ + Context& getContextForThumbnails(const StyleSheetP& stylesheet); /// Stylesheet to use for a particular card /** card may be null */ diff --git a/src/gui/control/card_list.cpp b/src/gui/control/card_list.cpp index c4023086..b591f4fc 100644 --- a/src/gui/control/card_list.cpp +++ b/src/gui/control/card_list.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -60,6 +61,10 @@ void CardListBase::onAction(const Action& action, bool undone) { TYPE_CASE(action, AddCardAction) { if (undone) { refreshList(); + if (!allowModify()) { + // Let some other card list else do the selecting + return; + } selectCardPos((long)sorted_card_list.size() - 1, true); } else { // select the new card @@ -75,6 +80,10 @@ void CardListBase::onAction(const Action& action, bool undone) { } else { long pos = selected_card_pos; refreshList(); + if (!allowModify()) { + // Let some other card list else do the selecting + return; + } if (action.card == selected_card) { // select the next card, if not possible, select the last if ((size_t)pos + 1 < sorted_card_list.size()) { @@ -140,8 +149,8 @@ void CardListBase::selectCard(const CardP& card, bool focus, bool event) { CardSelectEvent ev(card); ProcessEvent(ev); } + findSelectedCardPos(); if (focus) { - findSelectedCardPos(); selectCurrentCard(); } } @@ -443,6 +452,19 @@ void CardListBase::onDrag(wxMouseEvent& ev) { } } +void CardListBase::onContextMenu(wxContextMenuEvent&) { + if (allowModify()) { + IconMenu m; + m.Append(wxID_CUT, _("TOOL_CUT"), _("Cu&t"), _("Move the selected card to the clipboard")); + m.Append(wxID_COPY, _("TOOL_COPY"), _("&Copy"), _("Place the selected card on the clipboard")); + m.Append(wxID_PASTE, _("TOOL_PASTE"), _("&Paste"), _("Inserts the card from the clipboard")); + m.AppendSeparator(); + m.Append(ID_CARD_ADD, _("TOOL_CARD_ADD"), _("&Add Card"), _("Add a new, blank, card to this set")); + m.Append(ID_CARD_REMOVE,_("TOOL_CARD_DEL"), _("&Remove Select Card"), _("Delete the selected card from this set")); + PopupMenu(&m); + } +} + // ----------------------------------------------------------------------------- : CardListBase : Event table BEGIN_EVENT_TABLE(CardListBase, wxListView) @@ -452,4 +474,5 @@ BEGIN_EVENT_TABLE(CardListBase, wxListView) EVT_CHAR ( CardListBase::onChar) EVT_MOTION ( CardListBase::onDrag) EVT_MENU (ID_SELECT_COLUMNS, CardListBase::onSelectColumns) + EVT_CONTEXT_MENU ( CardListBase::onContextMenu) END_EVENT_TABLE () diff --git a/src/gui/control/card_list.hpp b/src/gui/control/card_list.hpp index 97f9b787..17c29905 100644 --- a/src/gui/control/card_list.hpp +++ b/src/gui/control/card_list.hpp @@ -154,6 +154,7 @@ class CardListBase : public wxListView, public SetView { void onItemFocus (wxListEvent& ev); void onChar (wxKeyEvent& ev); void onDrag (wxMouseEvent& ev); + void onContextMenu (wxContextMenuEvent&); }; // ----------------------------------------------------------------------------- : EOF diff --git a/src/gui/thumbnail_thread.cpp b/src/gui/thumbnail_thread.cpp index e4872f46..06e42e15 100644 --- a/src/gui/thumbnail_thread.cpp +++ b/src/gui/thumbnail_thread.cpp @@ -12,6 +12,30 @@ typedef pair pair_ThumbnailRequestP_Image; DECLARE_TYPEOF_COLLECTION(pair_ThumbnailRequestP_Image); +// ----------------------------------------------------------------------------- : Image Cache + +String user_settings_dir(); +String image_cache_dir() { + String dir = user_settings_dir() + _("/cache"); + if (!wxDirExists(dir)) wxMkDir(dir); + return dir + _("/"); +} + +/// A name that is safe to use as a filename, for the cache +String safe_filename(const String& str) { + String ret; ret.reserve(str.size()); + FOR_EACH_CONST(c, str) { + if (isAlnum(c)) { + ret += c; + } else if (c==_(' ') || c==_('-')) { + ret += _('-'); + } else { + ret += _('_'); + } + } + return ret; +} + // ----------------------------------------------------------------------------- : ThumbnailThreadWorker class ThumbnailThreadWorker : public wxThread { @@ -50,7 +74,15 @@ wxThread::ExitCode ThumbnailThreadWorker::Entry() { if (TestDestroy()) return 0; Image img = current->generate(); if (TestDestroy()) return 0; - // store result + // store in cache + if (img.Ok()) { + String filename = image_cache_dir() + safe_filename(current->cache_name) + _(".png"); + img.SaveFile(filename, wxBITMAP_TYPE_PNG); + // set modification time + wxFileName fn(filename); + fn.SetTimes(0, ¤t->modified, 0); + } + // store result in closed request list { wxMutexLocker lock(parent->mutex); parent->closed_requests.push_back(make_pair(current,img)); @@ -79,27 +111,6 @@ ThumbnailThread::~ThumbnailThread() { abortAll(); } -String user_settings_dir(); -String image_cache_dir() { - String dir = user_settings_dir() + _("/cache"); - if (!wxDirExists(dir)) wxMkDir(dir); - return dir + _("/"); -} - -String ThumbnailThread::safeFilename(const String& str) { - String ret; ret.reserve(str.size()); - FOR_EACH_CONST(c, str) { - if (isAlnum(c)) { - ret += c; - } else if (c==_(' ') || c==_('-')) { - ret += _('-'); - } else { - ret += _('_'); - } - } - return ret; -} - void ThumbnailThread::request(const ThumbnailRequestP& request) { assert(wxThread::IsMain()); // Is the request in progress? @@ -108,7 +119,7 @@ void ThumbnailThread::request(const ThumbnailRequestP& request) { } request_names.insert(request); // Is the image in the cache? - String filename = image_cache_dir() + safeFilename(request->cache_name) + _(".png"); + String filename = image_cache_dir() + safe_filename(request->cache_name) + _(".png"); wxFileName fn(filename); if (fn.FileExists()) { wxDateTime modified; @@ -152,14 +163,6 @@ bool ThumbnailThread::done(void* owner) { FOR_EACH(r, finished) { // store image r.first->store(r.second); - // store in cache - if (r.second.Ok()) { - String filename = image_cache_dir() + safeFilename(r.first->cache_name) + _(".png"); - r.second.SaveFile(filename, wxBITMAP_TYPE_PNG); - // set modification time - wxFileName fn(filename); - fn.SetTimes(0, &r.first->modified, 0); - } // remove from name list request_names.erase(r.first); } diff --git a/src/gui/thumbnail_thread.hpp b/src/gui/thumbnail_thread.hpp index 64777bb0..5a8b4124 100644 --- a/src/gui/thumbnail_thread.hpp +++ b/src/gui/thumbnail_thread.hpp @@ -69,9 +69,6 @@ class ThumbnailThread { set request_names; ///< Requests that haven't been stored yet, to prevent duplicates friend class ThumbnailThreadWorker; ThumbnailThreadWorker* worker; ///< The worker thread. invariant: no requests ==> worker==nullptr - - /// A name that is safe to use as a filename, for the cache - static String safeFilename(const String& str); }; /// The global thumbnail generator thread diff --git a/src/gui/value/choice.cpp b/src/gui/value/choice.cpp index 3d1af626..f8f94a40 100644 --- a/src/gui/value/choice.cpp +++ b/src/gui/value/choice.cpp @@ -8,17 +8,66 @@ #include #include +#include #include +#include +#include