Implement unique IDs and card linking

This commit is contained in:
GenevensiS
2025-08-11 16:17:13 +02:00
committed by GitHub
parent 13406b946c
commit 3bf9de18b1
100 changed files with 2432 additions and 1219 deletions
+3 -2
View File
@@ -1,6 +1,7 @@
cmake_minimum_required(VERSION 3.13)
project(magicseteditor VERSION 2.5.8)
project(magicseteditor VERSION 2.6.0)
add_definitions(-DUNOFFICIAL_BUILD)
set(CMAKE_CXX_STANDARD 17)
@@ -28,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")
+1 -1
View File
@@ -74,4 +74,4 @@
"inheritEnvironments": [ "msvc_x86" ]
}
]
}
}
+60 -60
View File
@@ -322,63 +322,63 @@ POSSIBILITY OF SUCH DAMAGES.
==END OF TERMS AND CONDITIONS==
==<a NAME="SEC4" HREF="#TOC4">How to Apply These Terms to Your New Programs</a>==
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
] <one line to give the program's name and a brief idea of what it does.>
] Copyright (C) <year> <name of author>
]
] This program is free software; you can redistribute it and/or modify
] it under the terms of the GNU General Public License as published by
] the Free Software Foundation; either version 2 of the License, or
] (at your option) any later version.
]
] This program is distributed in the hope that it will be useful,
] but WITHOUT ANY WARRANTY; without even the implied warranty of
] MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
] GNU General Public License for more details.
]
] You should have received a copy of the GNU General Public License
] along with this program; if not, write to the Free Software
] Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
] Gnomovision version 69, Copyright (C) year name of author
] Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
] This is free software, and you are welcome to redistribute it
] under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
] Yoyodyne, Inc., hereby disclaims all copyright interest in the program
] `Gnomovision' (which makes passes at compilers) written by James Hacker.
]
] <signature of Ty Coon>, 1 April 1989
] Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.
==<a NAME="SEC4" HREF="#TOC4">How to Apply These Terms to Your New Programs</a>==
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
] <one line to give the program's name and a brief idea of what it does.>
] Copyright (C) <year> <name of author>
]
] This program is free software; you can redistribute it and/or modify
] it under the terms of the GNU General Public License as published by
] the Free Software Foundation; either version 2 of the License, or
] (at your option) any later version.
]
] This program is distributed in the hope that it will be useful,
] but WITHOUT ANY WARRANTY; without even the implied warranty of
] MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
] GNU General Public License for more details.
]
] You should have received a copy of the GNU General Public License
] along with this program; if not, write to the Free Software
] Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
] Gnomovision version 69, Copyright (C) year name of author
] Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
] This is free software, and you are welcome to redistribute it
] under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
] Yoyodyne, Inc., hereby disclaims all copyright interest in the program
] `Gnomovision' (which makes passes at compilers) written by James Hacker.
]
] <signature of Ty Coon>, 1 April 1989
] Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.
+7 -7
View File
@@ -33,13 +33,13 @@ A heirachical file can contain a reference to another file:
Where filename must be an absolute or relative [[type:filename]].
That file is included literally into the current one; except for indentation, the included file never escapes from the level the 'include file' line is on.
If the file to be included can vary depending on the locale that is selected, use:
>>>include localized file: <em>filename</em>
MSE will take the filename and add "_" followed by the name of the currently selected locale at the end of it.
So for example, if the locale used is the folder "en.mse-locale", the file that will be included is "filename_en"
You must provide a version of the file for each locale found in the data folder, even if it is simply a copy of the english one.
If the file to be included can vary depending on the locale that is selected, use:
>>>include localized file: <em>filename</em>
MSE will take the filename and add "_" followed by the name of the currently selected locale at the end of it.
So for example, if the locale used is the folder "en.mse-locale", the file that will be included is "filename_en"
You must provide a version of the file for each locale found in the data folder, even if it is simply a copy of the english one.
--Example--
For example, a [[type:set]] might look like this:
+6 -4
View File
@@ -5,11 +5,13 @@ Function: crop
Shrink an image by cutting off some of the image, starting at the position denoted by the offsets. The resulting image size is specified in the parameters.
Resulting image can be bigger than the original, if offset_x or offset_y are negative, or if width or height are bigger than the original width and height.
--Parameters--
! Parameter Type Description
| @input@ [[type:image]] Image to enlarge
| @height@ [[type:double]] Height of the resulting image
| @width@ [[type:double]] Width of the resulting image
| @offset_x@ [[type:double]] Offset of crop, horizontally
| @offset_y@ [[type:double]] Offset of crop, vertically
| @height@ [[type:double]] Height of the resulting image, in pixels
| @width@ [[type:double]] Width of the resulting image, in pixels
| @offset_x@ [[type:double]] Offset of crop, horizontally, in pixels
| @offset_y@ [[type:double]] Offset of crop, vertically, in pixels
+11
View File
@@ -0,0 +1,11 @@
Function: dimensions_of
--Usage--
> dimensions_of(input: image)
Returns an array containing the width and height of the image in pixels.
--Parameters--
! Parameter Type Description
| @input@ [[type:image]] Image to whos dimensions we want.
+13
View File
@@ -0,0 +1,13 @@
Function: get_card_from_link
--Usage--
> get_card_from_link(card: card, "link type")
Inspects a given [[type:card]]'s links to find one of the given type, and returns the linked card.
Returns nil if no card was found.
--Parameters--
! Parameter Type Description
| @input@ [[type:string]] The type of link we want to find.
| @card@ [[type:card]] The card whose links we'll inspect.
| @set@ [[type:set]] The set in which to look. This can be omited since 'set' is a predefined variable.
+12
View File
@@ -0,0 +1,12 @@
Function: get_card_from_uid
--Usage--
> get_card_from_uid(input: "uid")
Returns the [[type:card]] with the given uid inside the set.
Returns nil if no card was found.
--Parameters--
! Parameter Type Description
| @input@ [[type:string]] The uid of the card we want to retrieve.
| @set@ [[type:set]] The set in which to look. This can be omited since 'set' is a predefined variable.
+2 -2
View File
@@ -5,12 +5,12 @@ Function: get_card_styling
Get the styling data of a [[type:card]].
This is for use in exporter scripts. In card scripts, use the "styling" predefined variable instead.
This is for use in exporter scripts. In card scripts, use the 'styling' predefined variable instead.
--Parameters--
! Parameter Type Description
| @input@ [[type:card]] The card you want to retrieve the styling data from.
| @set@ [[type:set]] The set the card belongs to. In an exporter script, this can be omited since "set" is a predefined variable.
| @set@ [[type:set]] The set the card belongs to. In an exporter script, this can be omited since 'set' is a predefined variable.
--Examples--
> # Retrieve the value "is foil" from the card's styling options
+6
View File
@@ -0,0 +1,6 @@
Function: get_mse_locale
--Usage--
> get_mse_locale()
Returns the name of the currently selected locale folder.
+14
View File
@@ -0,0 +1,14 @@
Function: has_link
--Usage--
> has_link(card: card, "link type")
Inspects a given [[type:card]]'s links to find one of the given type.
Returns true if such a link was found, false otherwise.
Note that this function does not check if the linked card exists in the set. For that, use get_card_from_link.
--Parameters--
! Parameter Type Description
| @input@ [[type:string]] The type of link we want to find.
| @card@ [[type:card]] The card whose links we'll inspect.
+6
View File
@@ -97,6 +97,8 @@ These functions are built into the program, other [[type:function]]s can be defi
| [[fun:flip_vertical]] Flip an image vertically.
| [[fun:rotate_image]] Rotate an image.
| [[fun:drop_shadow]] Add a drop shadow to an image.
| [[fun:insert_image]] Insert an image inside another.
| [[fun:dimensions_of]] Get the width and height of an image.
| [[fun:symbol_variation]] Render a variation of a [[type:symbol]].
| [[fun:import_image]] Load an image from outside the data folder.
| [[fun:built_in_image]] Return an image built into the program.
@@ -106,6 +108,9 @@ These functions are built into the program, other [[type:function]]s can be defi
| [[fun:add_card_to_set]] Add a [[type:card]] to a [[type:set]].
| [[fun:get_card_styling]] Get the styling data of a [[type:card]].
| [[fun:get_card_stylesheet]] Get the stylesheet of a [[type:card]].
| [[fun:get_card_from_uid]] Find the [[type:card]] with the given uid.
| [[fun:get_card_from_link]] Find a [[type:card]] that has the given link type to the given [[type:card]].
| [[fun:has_link]] Determine if the given the given [[type:card]] has a link of the given type.
! HTML export <<<
| [[fun:to_html]] Convert [[type:tagged text]] to html.
@@ -118,6 +123,7 @@ These functions are built into the program, other [[type:function]]s can be defi
! Other functions <<<
| [[fun:get_mse_version]] Get the MSE app version.
| [[fun:get_mse_locale]] Get the name of the currently selected locale.
| [[fun:get_mse_path]] Get the MSE app folder absolute path.
| [[fun:trace]] Output a message for debugging purposes.
| [[fun:assert]] Check a condition for debugging purposes.
+17
View File
@@ -0,0 +1,17 @@
Function: insert_image
--Usage--
> insert_image(base_image: image, inserted_image: image, offset_x: coordinate, offset_y: coordinate, background_color: color)
Insert an image inside another image.
The inserted image can be put outside the bounds of the base image. The resulting image will be widened accordingly.
--Parameters--
! Parameter Type Description
| @base_image@ [[type:image]] Image that serves as the canvas
| @inserted_image@ [[type:image]] Image inserted on top of the base image
| @offset_x@ [[type:double]] Offset of insertion, horizontally, in pixels
| @offset_y@ [[type:double]] Offset of insertion, vertically, in pixels
| @background_color@ [[type:color]] Background color, optional, defaults to transparent
+2 -2
View File
@@ -9,8 +9,8 @@ Aside from the [[fun:index|built in functions]] the following variables are prov
The current stylesheet
| @card@ [[type:card]] not in @init script@s or when exporting
The current card.
| @card_style@ [[type:indexmap]] of [[type:style]]s where @card@ is available Style properties for the current card, the same as @stylesheet.card_style@.
| @extra_card@ [[type:indexmap]] of [[type:value]]s field values for the current card as defined by the stylesheet.
| @card_style@ [[type:indexmap]] of [[type:style]]s where @card@ is available Style properties for the current card, the same as @stylesheet.card_style@.
| @extra_card@ [[type:indexmap]] of [[type:value]]s field values for the current card as defined by the stylesheet.
| @extra_card_style@ [[type:indexmap]] of [[type:style]] where @card@ is available Style properties for the current card as added by the stylesheet.
| @styling@ [[type:indexmap]] of [[type:value]]s where @card@ is available Styling options for the stylesheet/card.
| @value@ [[type:value]] when evaluating a [[type:field]]'s @script@ or @default@ script Current value in the field.
+5 -5
View File
@@ -43,9 +43,9 @@ Fields are part of the [[file:style triangle]]:
| @card list name@ [[type:localized string]] field name Alternate name to use for the card list, for example an abbreviation.
| @card list alignment@ [[type:alignment]] @left@ Alignment of the card list column.
| @sort script@ [[type:script]] Alternate way to sort the card list when using this column to sort the list.
| @import script@ [[type:script]] Script applied to the value given when creating a card with the new_card function. The script may return a map from field names to values.
For example, the pt field should not be initialized directly, since it is a combination of the power field and toughness field.
So if a value is given for pt, it must be redirected to power and toughness like so: {split := split_text(value, match:"/"); [power:split[0], toughness:split[1]]}.
| @import script@ [[type:script]] Script applied to the value given when creating a card with the new_card function. The script may return a map from field names to values.
For example, the pt field should not be initialized directly, since it is a combination of the power field and toughness field.
So if a value is given for pt, it must be redirected to power and toughness like so: {split := split_text(value, match:"/"); [power:split[0], toughness:split[1]]}.
Use the make_map function to dynamically create maps.
The @type@ determines what values of this field contain:
@@ -85,8 +85,8 @@ Additional properties are available, depending on the type of field:
These choices must appear in the same order as they do in the @choices@ property.
| @"boolean"@ ''A boolean field is a choice field with the choices @"yes"@ and @"no"@.'' <<< <<< <<<
| @"slider"@ ''A slider field is a choice field where the choices are all numbers.'' <<< <<< <<<
| @"slider"@ ''A slider field is a choice field where the choices are all numbers.'' <<< <<< <<<
| ^^^ @script@ [[type:script]] Script to apply to values of this field after each change.<br/>
If the script evaluates to a constant (i.e. doesn't use @value@) then values in this field can effectively not be edited.
| ^^^ @default@ [[type:script]] Script to determine the value when it is in the default state (not edited).
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

+2
View File
@@ -47,6 +47,8 @@ tool/no_auto IMAGE "tool/no_auto.png"
tool/card_add IMAGE "tool/card_add.png"
tool/card_add_multiple IMAGE "tool/card_add_multiple.png"
tool/card_del IMAGE "tool/card_del.png"
tool/card_link IMAGE "tool/card_link.png"
tool/card_copy IMAGE "tool/card_copy.png"
tool/card_rotate IMAGE "tool/card_rotate.png"
tool/card_rotate_0 IMAGE "tool/card_rotate_0.png"
tool/card_rotate_90 IMAGE "tool/card_rotate_90.png"
+83 -2
View File
@@ -13,6 +13,7 @@
#include <data/pack.hpp>
#include <data/stylesheet.hpp>
#include <util/error.hpp>
#include <util/uid.hpp>
// ----------------------------------------------------------------------------- : Add card
@@ -36,10 +37,53 @@ String AddCardAction::getName(bool to_undo) const {
}
void AddCardAction::perform(bool to_undo) {
// If we are adding cards, resolve any uid conflicts
// (If we are re-adding cards, from a remove undo, there shouldn't be any uid conflicts)
// We always assume uid conflicts occur because a card was copy-pasted into the same set,
// and never because two different cards randomly got assigned the same uid
if (action.adding && !to_undo) {
// Tally existing unique ids
unordered_map<String, CardP> all_existing_uids;
FOR_EACH(card, set.cards) {
all_existing_uids.insert({ card->uid, card });
}
// Tally added unique ids
unordered_map<String, CardP> all_added_uids;
for (size_t pos = 0; pos < action.steps.size(); ++pos) {
CardP card = action.steps[pos].item;
all_added_uids.insert({ card->uid, card });
}
FOR_EACH(added_pair, all_added_uids) {
String old_uid = added_pair.first;
CardP added_card = added_pair.second;
// Assign new unique ids
if (all_existing_uids.find(old_uid) != all_existing_uids.end()) {
String new_uid = generate_uid();
added_card->uid = new_uid;
all_added_uids.insert({ new_uid, added_card });
// Update links on linked cards
OTHER_LINKED_PAIRS(linked_pairs, added_card);
FOR_EACH(linked_pair, linked_pairs) {
String& linked_uid = linked_pair.first.get();
String& linked_relation = linked_pair.second.get();
if (linked_uid == wxEmptyString) continue;
// If it's an added card, replace the link
if (all_added_uids.find(linked_uid) != all_added_uids.end()) {
all_added_uids.at(linked_uid)->updateLink(old_uid, new_uid);
}
// Otherwise, if it's an existing card, copy the link
else if (all_existing_uids.find(linked_uid) != all_existing_uids.end()) {
all_existing_uids.at(linked_uid)->copyLink(set, old_uid, new_uid);
}
}
}
}
}
// Add or remove cards
action.perform(set.cards, to_undo);
}
// ----------------------------------------------------------------------------- : Reorder cards
ReorderCardsAction::ReorderCardsAction(Set& set, size_t card_id1, size_t card_id2)
@@ -55,13 +99,50 @@ void ReorderCardsAction::perform(bool to_undo) {
assert(card_id1 < set.cards.size());
assert(card_id2 < set.cards.size());
#endif
if (card_id1 >= set.cards.size() || card_id2 < set.cards.size()) {
if (card_id1 >= set.cards.size() || card_id2 >= set.cards.size()) {
// TODO : Too lazy to fix this right now.
return;
}
swap(set.cards[card_id1], set.cards[card_id2]);
}
// ----------------------------------------------------------------------------- : Link cards
LinkCardsAction::LinkCardsAction(Set& set, const CardP& selected_card, vector<CardP>& linked_cards, const String& selected_relation, const String& linked_relation)
: CardListAction(set), selected_card(selected_card), linked_cards(linked_cards), selected_relation(selected_relation), linked_relation(linked_relation)
{}
String LinkCardsAction::getName(bool to_undo) const {
return _("Link cards");
}
void LinkCardsAction::perform(bool to_undo) {
if (!to_undo) {
selected_card->link(set, linked_cards, selected_relation, linked_relation);
} else {
selected_card->unlink(linked_cards);
}
}
UnlinkCardsAction::UnlinkCardsAction(Set& set, const CardP& selected_card, CardP& unlinked_card)
: CardListAction(set), selected_card(selected_card), unlinked_card(unlinked_card)
{}
String UnlinkCardsAction::getName(bool to_undo) const {
return _("Unlink card");
}
void UnlinkCardsAction::perform(bool to_undo) {
if (!to_undo) {
pair<String, String> relations = selected_card->unlink(unlinked_card);
selected_relation = relations.first;
unlinked_relation = relations.second;
}
else {
selected_card->link(set, unlinked_card, selected_relation, unlinked_relation);
}
}
// ----------------------------------------------------------------------------- : Change stylesheet
String DisplayChangeAction::getName(bool to_undo) const {
+31
View File
@@ -64,6 +64,37 @@ public:
const size_t card_id1, card_id2; ///< Positions of the two cards to swap
};
// ----------------------------------------------------------------------------- : Link cards
/// Add a link between two or more cards
class LinkCardsAction : public CardListAction {
public:
LinkCardsAction(Set& set, const CardP& selected_card, vector<CardP>& linked_cards, const String& selected_relation, const String& linked_relation);
String getName(bool to_undo) const override;
void perform(bool to_undo) override;
//private:
CardP selected_card; ///< The card currently selected in the cards tab
vector<CardP> linked_cards; ///< The cards that will be linked to the selected card
String selected_relation; ///< The nature of the relation of the selected card
String linked_relation; ///< The nature of the relation of the linked cards
};
/// Remove a link between two cards
class UnlinkCardsAction : public CardListAction {
public:
UnlinkCardsAction(Set& set, const CardP& selected_card, CardP& unlinked_card);
String getName(bool to_undo) const override;
void perform(bool to_undo) override;
//private:
CardP selected_card; ///< The card currently selected in the cards tab
CardP unlinked_card; ///< The card that will be unlinked from the selected card
String selected_relation; ///< The nature of the relation of the selected card
String unlinked_relation; ///< The nature of the relation of the unlinked card
};
// ----------------------------------------------------------------------------- : Change stylesheet
/// An action that affects the rendering/display/look of a set or cards in the set
+231 -1
View File
@@ -8,19 +8,23 @@
#include <util/prec.hpp>
#include <data/card.hpp>
#include <data/set.hpp>
#include <data/game.hpp>
#include <data/stylesheet.hpp>
#include <data/field.hpp>
#include <util/error.hpp>
#include <util/reflect.hpp>
#include <util/delayed_index_maps.hpp>
#include <util/uid.hpp>
#include <unordered_set>
// ----------------------------------------------------------------------------- : Card
Card::Card()
// for files made before we saved these times, set the time to 'yesterday'
// for files made before we saved these, set the time to 'yesterday', generate a uid
: time_created (wxDateTime::Now().Subtract(wxDateSpan::Day()).ResetTime())
, time_modified(wxDateTime::Now().Subtract(wxDateSpan::Day()).ResetTime())
, uid(generate_uid())
, has_styling(false)
{
if (!game_for_reading()) {
@@ -32,6 +36,7 @@ Card::Card()
Card::Card(const Game& game)
: time_created (wxDateTime::Now())
, time_modified(wxDateTime::Now())
, uid(generate_uid())
, has_styling(false)
{
data.init(game.card_fields);
@@ -60,6 +65,222 @@ bool Card::contains(QuickFilterPart const& query) const {
return false;
}
void Card::link(const Set& set, const vector<CardP>& linked_cards, const String& selected_relation, const String& linked_relation)
{
unlink(linked_cards);
unordered_set<String> all_existing_uids;
FOR_EACH(card, set.cards) {
all_existing_uids.insert(card->uid);
}
int free_link_count = 0;
THIS_LINKED_PAIRS(this_linked_pairs);
FOR_EACH(this_linked_pair, this_linked_pairs) {
String& this_linked_uid = this_linked_pair.first.get();
if (
this_linked_uid == wxEmptyString || // Not a reference
all_existing_uids.find(this_linked_uid) == all_existing_uids.end() // Reference to nonexistent card
) free_link_count++;
}
if (free_link_count < linked_cards.size()) {
queue_message(MESSAGE_WARNING, _ERROR_("not enough free links"));
return;
}
vector<CardP> all_missed_cards;
FOR_EACH(linked_card, linked_cards) {
bool written = false;
// Try to write to a free spot
FOR_EACH(this_linked_pair, this_linked_pairs) {
String& this_linked_uid = this_linked_pair.first.get();
String& this_linked_relation = this_linked_pair.second.get();
if (this_linked_uid == wxEmptyString) {
this_linked_uid = linked_card->uid;
this_linked_relation = linked_relation;
written = true;
break;
}
}
// Try to write to an erasable spot
if (!written) {
FOR_EACH(this_linked_pair, this_linked_pairs) {
String& this_linked_uid = this_linked_pair.first.get();
String& this_linked_relation = this_linked_pair.second.get();
if (all_existing_uids.find(this_linked_uid) == all_existing_uids.end()) {
this_linked_uid = linked_card->uid;
this_linked_relation = linked_relation;
written = true;
break;
}
}
}
if (!written) {
// Should be impossible to end up here?
}
OTHER_LINKED_PAIRS(linked_pairs, linked_card);
written = false;
// Try to write to a free spot
FOR_EACH(linked_pair, linked_pairs) {
String& linked_uid = linked_pair.first.get();
String& linked_relation = linked_pair.second.get();
if (linked_uid == wxEmptyString) {
linked_uid = uid;
linked_relation = selected_relation;
written = true;
break;
}
}
// Try to write to an erasable spot
if (!written) {
FOR_EACH(linked_pair, linked_pairs) {
String& linked_uid = linked_pair.first.get();
String& linked_relation = linked_pair.second.get();
if (all_existing_uids.find(linked_uid) == all_existing_uids.end()) {
linked_uid = uid;
linked_relation = selected_relation;
written = true;
break;
}
}
}
// Notify we couldn't write
if (!written) {
all_missed_cards.push_back(linked_card);
}
}
if (all_missed_cards.size() > 0) {
std::stringstream ss;
ss << _ERROR_("could not link");
for (size_t pos = 0; pos < all_missed_cards.size(); ++pos) {
ss << all_missed_cards[pos]->identification();
if (pos < all_missed_cards.size() - 1) ss << ", ";
};
String wxString(ss.str().c_str(), wxConvUTF8);
queue_message(MESSAGE_WARNING, wxString);
}
}
void Card::link(const Set& set, CardP& linked_card, const String& selected_relation, const String& linked_relation)
{
vector<CardP> linked_cards { linked_card };
link(set, linked_cards, selected_relation, linked_relation);
}
void Card::unlink(const vector<CardP>& unlinked_cards)
{
for (size_t pos = 0; pos < unlinked_cards.size(); ++pos) {
CardP unlinked_card = unlinked_cards[pos];
unlink(unlinked_card);
}
}
pair<String, String> Card::unlink(CardP& unlinked_card)
{
String old_selected_relation = wxEmptyString;
THIS_LINKED_PAIRS(this_linked_pairs);
FOR_EACH(this_linked_pair, this_linked_pairs) {
String& this_linked_uid = this_linked_pair.first.get();
String& this_linked_relation = this_linked_pair.second.get();
if (this_linked_uid == unlinked_card->uid) {
old_selected_relation = this_linked_relation;
this_linked_uid = wxEmptyString;
this_linked_relation = wxEmptyString;
}
}
String old_unlinked_relation = wxEmptyString;
OTHER_LINKED_PAIRS(unlinked_pairs, unlinked_card);
FOR_EACH(unlinked_pair, unlinked_pairs) {
String& unlinked_uid = unlinked_pair.first.get();
String& unlinked_relation = unlinked_pair.second.get();
if (unlinked_uid == uid) {
old_unlinked_relation = unlinked_relation;
unlinked_uid = wxEmptyString;
unlinked_relation = wxEmptyString;
}
}
return make_pair(old_selected_relation, old_unlinked_relation);
}
void Card::copyLink(const Set& set, String old_uid, String new_uid) {
// Find what relation we need to copy
String relation_copy = wxEmptyString;
THIS_LINKED_PAIRS(this_linked_pairs);
FOR_EACH(this_linked_pair, this_linked_pairs) {
String& this_linked_uid = this_linked_pair.first.get();
String& this_linked_relation = this_linked_pair.second.get();
if (this_linked_uid == old_uid) {
relation_copy = this_linked_relation;
break;
}
}
// Nothing to copy
if (relation_copy == wxEmptyString) {
return;
}
// Try to copy to a free spot
bool written = false;
FOR_EACH(this_linked_pair, this_linked_pairs) {
String& this_linked_uid = this_linked_pair.first.get();
String& this_linked_relation = this_linked_pair.second.get();
if (this_linked_uid == wxEmptyString) {
this_linked_uid = new_uid;
this_linked_relation = relation_copy;
written = true;
break;
}
}
// Try to copy to an erasable spot
if (!written) {
unordered_set<String> all_existing_uids;
FOR_EACH(card, set.cards) {
all_existing_uids.insert(card->uid);
}
FOR_EACH(this_linked_pair, this_linked_pairs) {
String& this_linked_uid = this_linked_pair.first.get();
String& this_linked_relation = this_linked_pair.second.get();
if (all_existing_uids.find(this_linked_uid) == all_existing_uids.end()) {
this_linked_uid = new_uid;
this_linked_relation = relation_copy;
written = true;
break;
}
}
}
// Notify we couldn't copy
if (!written) {
queue_message(MESSAGE_WARNING, _ERROR_("not enough free links for copy"));
}
}
void Card::updateLink(String old_uid, String new_uid) {
THIS_LINKED_PAIRS(this_linked_pairs);
FOR_EACH(this_linked_pair, this_linked_pairs) {
String& this_linked_uid = this_linked_pair.first.get();
if (this_linked_uid == old_uid) {
this_linked_uid = new_uid;
return;
}
}
}
vector<pair<CardP, String>> Card::getLinkedCards(const Set& set) {
unordered_map<String, String> links{
{ linked_card_1, linked_relation_1 },
{ linked_card_2, linked_relation_2 },
{ linked_card_3, linked_relation_3 },
{ linked_card_4, linked_relation_4 }
};
vector<pair<CardP, String>> linked_cards;
FOR_EACH(other_card, set.cards) {
if (links.find(other_card->uid) != links.end()) {
linked_cards.push_back(make_pair(other_card, links.at(other_card->uid)));
}
}
return linked_cards;
}
IndexMap<FieldP, ValueP>& Card::extraDataFor(const StyleSheet& stylesheet) {
return extra_data.get(stylesheet.name(), stylesheet.extra_card_fields);
}
@@ -89,6 +310,15 @@ IMPLEMENT_REFLECTION(Card) {
}
}
REFLECT(notes);
REFLECT(uid);
REFLECT(linked_card_1);
REFLECT(linked_card_2);
REFLECT(linked_card_3);
REFLECT(linked_card_4);
REFLECT(linked_relation_1);
REFLECT(linked_relation_2);
REFLECT(linked_relation_3);
REFLECT(linked_relation_4);
REFLECT(time_created);
REFLECT(time_modified);
REFLECT(extra_data); // don't allow scripts to depend on style specific data
+27
View File
@@ -17,11 +17,15 @@
class Game;
class Dependency;
class Keyword;
DECLARE_POINTER_TYPE(Set);
DECLARE_POINTER_TYPE(Card);
DECLARE_POINTER_TYPE(Field);
DECLARE_POINTER_TYPE(Value);
DECLARE_POINTER_TYPE(StyleSheet);
#define THIS_LINKED_PAIRS(var) vector<pair<reference_wrapper<String>, reference_wrapper<String>>> var { make_pair(ref(linked_card_1), ref(linked_relation_1)), make_pair(ref(linked_card_2), ref(linked_relation_2)), make_pair(ref(linked_card_3), ref(linked_relation_3)), make_pair(ref(linked_card_4), ref(linked_relation_4)) }
#define OTHER_LINKED_PAIRS(var, other_card) vector<pair<reference_wrapper<String>, reference_wrapper<String>>> var { make_pair(ref(other_card->linked_card_1), ref(other_card->linked_relation_1)), make_pair(ref(other_card->linked_card_2), ref(other_card->linked_relation_2)), make_pair(ref(other_card->linked_card_3), ref(other_card->linked_relation_3)), make_pair(ref(other_card->linked_card_4), ref(other_card->linked_relation_4)) }
// ----------------------------------------------------------------------------- : Card
/// A card from a card Set
@@ -37,6 +41,18 @@ public:
IndexMap<FieldP, ValueP> data;
/// Notes for this card
String notes;
/// Unique identifier for this card, so other cards can refer to it, and be linked to it
String uid;
/// Up to four uid of other cards, to encode relations such as front face/back face, or generator/token, etc...
String linked_card_1;
String linked_card_2;
String linked_card_3;
String linked_card_4;
/// Nature of the relatation with the respective linked card, such as back face, or token, etc...
String linked_relation_1;
String linked_relation_2;
String linked_relation_3;
String linked_relation_4;
/// Time the card was created/last modified
wxDateTime time_created, time_modified;
/// Alternative style to use for this card
@@ -64,6 +80,17 @@ public:
/// Does any field contains the given query string?
bool contains(QuickFilterPart const& query) const;
/// Link or unlink other cards to this card
void link(const Set& set, const vector<CardP>& linked_cards, const String& selected_relation, const String& linked_relation);
void link(const Set& set, CardP& linked_card, const String& selected_relation, const String& linked_relation);
void unlink(const vector<CardP>& linked_cards);
pair<String, String> unlink(CardP& unlinked_card); // Returns the relations that were deleted, so we can undo
void copyLink(const Set& set, String old_uid, String new_uid);
void updateLink(String old_uid, String new_uid);
vector<pair<CardP, String>> getLinkedCards(const Set& set);
/// Find a value in the data by name and type
template <typename T> T& value(const String& name) {
for(IndexMap<FieldP, ValueP>::iterator it = data.begin() ; it != data.end() ; ++it) {
+36 -6
View File
@@ -68,14 +68,14 @@ IMPLEMENT_REFLECTION(Field) {
}
void Field::after_reading(Version ver) {
name = canonical_name_form(name);
if(caption.default_.empty()) caption.default_ = tr(package_relative_filename, name, name_to_caption);
name = canonical_name_form(name);
if(caption.default_.empty()) caption.default_ = tr(package_relative_filename, name, name_to_caption);
if(card_list_name.default_.empty()) card_list_name.default_ = tr(package_relative_filename, caption.default_, capitalize);
}
template <>
intrusive_ptr<Field> read_new<Field>(Reader& reader) {
intrusive_ptr<Field> field;
intrusive_ptr<Field> field;
// there must be a type specified
String type;
reader.handle(_("type"), type);
@@ -95,7 +95,7 @@ intrusive_ptr<Field> read_new<Field>(Reader& reader) {
} else {
reader.warning(_ERROR_1_("unsupported field type", type));
throw ParseError(_ERROR_("aborting parsing"));
}
}
field->package_relative_filename = reader.getPackage()->relativeFilename().Clone();
return field;
}
@@ -222,8 +222,38 @@ void Style::checkContentDependencies(Context& ctx, const Dependency& dep) const
void Style::markDependencyMember(const String& name, const Dependency& dep) const {
// mark dependencies on content
if (dep.type == DEP_DUMMY && dep.index == false && (starts_with(name, _("content")) || name == "layout") ) {
// anything that starts with "content_" is a content property
if (
dep.type == DEP_DUMMY && dep.index == false && (
starts_with(name, _("content")) ||
name == "layout" ||
name == "lines" ||
name == "paragraphs" ||
name == "blocks" ||
name == "separators" ||
name == "font" ||
name == "symbol_font" ||
name == "always_symbol" ||
name == "allow_formating" ||
name == "alignment" ||
name == "padding_left" ||
name == "padding_right" ||
name == "padding_top" ||
name == "padding_bottom" ||
name == "padding_left_min" ||
name == "padding_right_min" ||
name == "padding_top_min" ||
name == "padding_bottom_min" ||
name == "line_height_soft" ||
name == "line_height_hard" ||
name == "line_height_line" ||
name == "line_height_soft_max" ||
name == "line_height_hard_max" ||
name == "line_height_line_max" ||
name == "paragraph_height" ||
name == "block_height_min" ||
name == "direction"
)
) {
const_cast<Dependency&>(dep).index = true;
}
}
+1 -1
View File
@@ -61,7 +61,7 @@ public:
Alignment card_list_align; ///< Alignment of the card list colummn.
OptionalScript sort_script; ///< The script to use when sorting this, if not the value.
OptionalScript import_script; ///< The script to apply to the supplied value, when creating a new card.
Dependencies dependent_scripts; ///< Scripts that depend on values of this field
Dependencies dependent_scripts; ///< Scripts that depend on values of this field
String package_relative_filename;
/// Creates a new Value corresponding to this Field
+50 -50
View File
@@ -8,10 +8,10 @@
#include <util/prec.hpp>
#include <data/font.hpp>
#include <wx/stdpaths.h>
#include <wx/dir.h>
#include <wx/font.h>
#include <wx/stdpaths.h>
#include <wx/dir.h>
#include <wx/font.h>
// ----------------------------------------------------------------------------- : Font
Font::Font()
@@ -27,59 +27,59 @@ Font::Font()
, separator_color(Color(0,0,0,128))
, flags(FONT_NORMAL)
{}
bool Font::PreloadResourceFonts(bool recursive) {
#if wxUSE_PRIVATE_FONTS
String pathSeparator(wxFileName::GetPathSeparator());
String appPath(wxFileName(wxStandardPaths::Get().GetExecutablePath()).GetPath());
wxDir appDir(appPath);
if (!appDir.IsOpened()) return true;
bool preloadHadErrors = false;
bool Font::PreloadResourceFonts(bool recursive) {
#if wxUSE_PRIVATE_FONTS
String pathSeparator(wxFileName::GetPathSeparator());
String appPath(wxFileName(wxStandardPaths::Get().GetExecutablePath()).GetPath());
wxDir appDir(appPath);
if (!appDir.IsOpened()) return true;
bool preloadHadErrors = false;
wxString folder;
bool cont = appDir.GetFirst(&folder, wxEmptyString, wxDIR_DIRS);
while (cont)
{
{
if (folder.Lower().Contains("fonts")) {
String folderPath = appPath + pathSeparator + folder + pathSeparator;
// tally fonts
vector<String> fontFilePaths;
TallyResourceFonts(folderPath, fontFilePaths, recursive);
// load fonts
for (const String& fontFilePath : fontFilePaths) {
if (!wxFont::AddPrivateFont(fontFilePath)) {
preloadHadErrors = true;
}
String folderPath = appPath + pathSeparator + folder + pathSeparator;
// tally fonts
vector<String> fontFilePaths;
TallyResourceFonts(folderPath, fontFilePaths, recursive);
// load fonts
for (const String& fontFilePath : fontFilePaths) {
if (!wxFont::AddPrivateFont(fontFilePath)) {
preloadHadErrors = true;
}
}
}
}
cont = appDir.GetNext(&folder);
}
return preloadHadErrors;
#endif // wxUSE_PRIVATE_FONTS
return false;
}
void Font::TallyResourceFonts(String fontsDirectoryPath, vector<String>& fontFilePaths, bool recursive) {
wxDir fontsDirectory(fontsDirectoryPath);
String fontFileName = wxEmptyString;
bool hasNext = fontsDirectory.GetFirst(&fontFileName);
while (hasNext) {
String fontFilePath = fontsDirectoryPath + fontFileName;
if (wxDirExists(fontFilePath)) {
if (recursive) {
TallyResourceFonts(fontFilePath + wxFileName::GetPathSeparator(), fontFilePaths, true);
}
}
else if (fontFilePath.EndsWith(_(".ttf")) || fontFilePath.EndsWith(_(".otf"))) {
fontFilePaths.push_back(fontFilePath);
}
hasNext = fontsDirectory.GetNext(&fontFileName);
}
}
}
return preloadHadErrors;
#endif // wxUSE_PRIVATE_FONTS
return false;
}
void Font::TallyResourceFonts(String fontsDirectoryPath, vector<String>& fontFilePaths, bool recursive) {
wxDir fontsDirectory(fontsDirectoryPath);
String fontFileName = wxEmptyString;
bool hasNext = fontsDirectory.GetFirst(&fontFileName);
while (hasNext) {
String fontFilePath = fontsDirectoryPath + fontFileName;
if (wxDirExists(fontFilePath)) {
if (recursive) {
TallyResourceFonts(fontFilePath + wxFileName::GetPathSeparator(), fontFilePaths, true);
}
}
else if (fontFilePath.EndsWith(_(".ttf")) || fontFilePath.EndsWith(_(".otf"))) {
fontFilePaths.push_back(fontFilePath);
}
hasNext = fontsDirectory.GetNext(&fontFileName);
}
}
bool Font::update(Context& ctx) {
bool changes = false;
+10 -9
View File
@@ -46,15 +46,16 @@ public:
Scriptable<double> shadow_displacement_y;///< Position of the shadow
Scriptable<double> shadow_blur; ///< Blur radius of the shadow
Color separator_color; ///< Color for <sep> text
int flags; ///< FontFlags for this font
int flags; ///< FontFlags for this font
Font();
/// Load fonts (.ttf or .otf) from all directories in the app directory that contain "fonts" in their names,
/// and optionaly their subdirectories, returns true if there were errors
static bool PreloadResourceFonts(bool recursive);
/// Adds font file paths from the given directory into fontFilePaths
static void TallyResourceFonts(String fontsDirectoryPath, vector<String>& fontFilePaths, bool recursive);
/// Load fonts (.ttf or .otf) from all directories in the app directory that contain "fonts" in their names,
/// and optionaly their subdirectories, returns true if there were errors
static bool PreloadResourceFonts(bool recursive);
/// Adds font file paths from the given directory into fontFilePaths
static void TallyResourceFonts(String fontsDirectoryPath, vector<String>& fontFilePaths, bool recursive);
/// Update the scritables, returns true if there is a change
bool update(Context& ctx);
/// Add the given dependency to the dependent_scripts list for the variables this font depends on
@@ -70,7 +71,7 @@ public:
/// Convert this font to a wxFont
wxFont toWxFont(double scale) const;
private:
DECLARE_REFLECTION();
};
+3
View File
@@ -147,6 +147,9 @@ CardsOnClipboard::CardsOnClipboard(const SetP& set, const vector<CardP>& cards)
if (cards.size() == 1) {
Add(new wxBitmapDataObject(export_bitmap(set, cards[0])));
}
else if (cards.size() < 6) {
Add(new wxBitmapDataObject(export_bitmap(set, cards, true, 0, 1.0, 0.0)));
}
// Conversion to serialized card format
Add(new CardsDataObject(set, cards), true);
}
+3 -2
View File
@@ -99,11 +99,12 @@ void export_images(const SetP& set, const vector<CardP>& cards,
void export_image(const SetP& set, const CardP& card, const String& filename);
/// Generate a bitmap image of a card
Bitmap export_bitmap(const SetP& set, const CardP& card);
Bitmap export_bitmap(const SetP& set, const CardP& card);
Bitmap export_bitmap(const SetP& set, const CardP& card, const double zoom, const Radians angle_radians);
Bitmap export_bitmap(const SetP& set, const vector<CardP>& cards, bool scale_to_lowest_dpi, int padding, const double zoom, const Radians angle_radians);
/// Export a set to Magic Workstation format
void export_mws(Window* parent, const SetP& set);
/// Export a set to Apprentice
void export_apprentice(Window* parent, const SetP& set);
void export_apprentice(Window* parent, const SetP& set);
+87 -35
View File
@@ -13,6 +13,7 @@
#include <data/card.hpp>
#include <data/stylesheet.hpp>
#include <data/settings.hpp>
#include <gui/util.hpp>
#include <render/card/viewer.hpp>
#include <wx/filename.h>
@@ -25,47 +26,47 @@ void export_image(const SetP& set, const CardP& card, const String& filename) {
}
class UnzoomedDataViewer : public DataViewer {
public:
UnzoomedDataViewer();
public:
UnzoomedDataViewer();
UnzoomedDataViewer(double zoom, double angle);
virtual ~UnzoomedDataViewer() {};
Rotation getRotation() const override;
private:
private:
double zoom;
double angle;
double angle;
bool declared_values;
};
UnzoomedDataViewer::UnzoomedDataViewer()
: zoom(1.0)
, angle(0.0)
, declared_values(false)
{}
UnzoomedDataViewer::UnzoomedDataViewer(const double zoom, const Radians angle = 0.0)
: zoom(zoom)
, angle(angle)
, declared_values(true)
};
UnzoomedDataViewer::UnzoomedDataViewer()
: zoom(1.0)
, angle(0.0)
, declared_values(false)
{}
UnzoomedDataViewer::UnzoomedDataViewer(const double zoom, const Radians angle = 0.0)
: zoom(zoom)
, angle(angle)
, declared_values(true)
{}
Rotation UnzoomedDataViewer::getRotation() const {
if (!stylesheet) stylesheet = set->stylesheet;
if (declared_values) {
return Rotation(angle, stylesheet->getCardRect(), zoom, 1.0, ROTATION_ATTACH_TOP_LEFT);
}
if (!stylesheet) stylesheet = set->stylesheet;
if (declared_values) {
return Rotation(angle, stylesheet->getCardRect(), zoom, 1.0, ROTATION_ATTACH_TOP_LEFT);
}
double export_zoom = settings.stylesheetSettingsFor(set->stylesheetFor(card)).export_zoom();
bool use_viewer_rotation = !settings.stylesheetSettingsFor(set->stylesheetFor(card)).card_normal_export();
if (use_viewer_rotation) {
double export_zoom = settings.stylesheetSettingsFor(set->stylesheetFor(card)).export_zoom();
bool use_viewer_rotation = !settings.stylesheetSettingsFor(set->stylesheetFor(card)).card_normal_export();
if (use_viewer_rotation) {
return Rotation(DataViewer::getRotation().getAngle(), stylesheet->getCardRect(), export_zoom, 1.0, ROTATION_ATTACH_TOP_LEFT);
} else {
return Rotation(angle, stylesheet->getCardRect(), export_zoom, 1.0, ROTATION_ATTACH_TOP_LEFT);
}
}
Bitmap export_bitmap(const SetP& set, const CardP& card) {
if (!set) throw Error(_("no set"));
}
Bitmap export_bitmap(const SetP& set, const CardP& card) {
if (!set) throw Error(_("no set"));
UnzoomedDataViewer viewer = UnzoomedDataViewer();
viewer.setSet(set);
viewer.setCard(card);
@@ -79,11 +80,11 @@ Bitmap export_bitmap(const SetP& set, const CardP& card) {
// draw
viewer.draw(dc);
dc.SelectObject(wxNullBitmap);
return bitmap;
}
Bitmap export_bitmap(const SetP& set, const CardP& card, const double zoom, const Radians angle = 0.0) {
if (!set) throw Error(_("no set"));
return bitmap;
}
Bitmap export_bitmap(const SetP& set, const CardP& card, const double zoom, const Radians angle = 0.0) {
if (!set) throw Error(_("no set"));
UnzoomedDataViewer viewer = UnzoomedDataViewer(zoom, angle);
viewer.setSet(set);
viewer.setCard(card);
@@ -97,8 +98,59 @@ Bitmap export_bitmap(const SetP& set, const CardP& card, const double zoom, cons
// draw
viewer.draw(dc);
dc.SelectObject(wxNullBitmap);
return bitmap;
}
return bitmap;
}
// put multiple card images into one bitmap
Bitmap export_bitmap(const SetP& set, const vector<CardP>& cards, bool scale_to_lowest_dpi, int padding, const double zoom, const Radians angle = 0.0) {
if (!set) throw Error(_("no set"));
vector<Bitmap> bitmaps;
int width = 0;
int height = 0;
double lowest_dpi = 1200.0;
if (scale_to_lowest_dpi) {
FOR_EACH(card, cards) {
lowest_dpi = min(lowest_dpi, set->stylesheetFor(card).card_dpi);
}
lowest_dpi = max(lowest_dpi, 150.0);
}
// Draw card bitmaps
FOR_EACH(card, cards) {
double scaled_zoom = zoom;
if (scale_to_lowest_dpi) {
double dpi = max(set->stylesheetFor(card).card_dpi, 150.0);
scaled_zoom *= lowest_dpi / dpi;
}
UnzoomedDataViewer viewer = UnzoomedDataViewer(scaled_zoom, angle);
viewer.setSet(set);
viewer.setCard(card);
RealSize size = viewer.getRotation().getExternalSize();
Bitmap bitmap((int)size.width, (int)size.height);
if (!bitmap.Ok()) throw InternalError(_("Unable to create bitmap"));
wxMemoryDC bufferDC;
bufferDC.SelectObject(bitmap);
clearDC(bufferDC, *wxWHITE_BRUSH);
viewer.draw(bufferDC);
bufferDC.SelectObject(wxNullBitmap);
width += (int)size.width;
height = max(height, (int)size.height);
bitmaps.push_back(bitmap);
}
// Draw global bitmap
Bitmap global_bitmap(width + (bitmaps.size()-1) * padding, height);
if (!global_bitmap.Ok()) throw InternalError(_("Unable to create bitmap"));
wxMemoryDC globalDC;
globalDC.SelectObject(global_bitmap);
clearDC(globalDC, *wxWHITE_BRUSH);
int offset = 0;
FOR_EACH(bitmap, bitmaps) {
globalDC.SetDeviceOrigin(offset, 0);
globalDC.DrawBitmap(bitmap, 0, 0);
offset += bitmap.GetWidth() + padding;
}
globalDC.SelectObject(wxNullBitmap);
return global_bitmap;
}
// ----------------------------------------------------------------------------- : Multiple card export
+21 -20
View File
@@ -48,6 +48,7 @@ IMPLEMENT_REFLECTION(Game) {
}
REFLECT_NO_SCRIPT(default_set_style);
REFLECT_NO_SCRIPT(card_fields);
REFLECT_NO_SCRIPT(card_links);
REFLECT_NO_SCRIPT(card_list_color_script);
REFLECT_NO_SCRIPT(import_script);
REFLECT_NO_SCRIPT(json_paths);
@@ -95,26 +96,26 @@ void Game::validate(Version v) {
pack->filter = OptionalScript(_("true"));
pack->select = SELECT_NO_REPLACE;
pack_types.push_back(pack);
}
// alternate card field names map
}
// alternate card field names map
for (auto it = card_fields.begin(); it != card_fields.end(); ++it) {
FieldP field = *it;
String unified_name = unified_form(field->name);
if (card_fields_alt_names.count(unified_name)) {
queue_message(MESSAGE_WARNING, _("Duplicate alternate card field name: ") + unified_name);
}
else {
card_fields_alt_names.emplace(unified_name, field->name);
}
//String column_name = field->card_list_name.get();
//card_fields_alt_names.emplace(unified_form(column_name), field->name);
for (auto it2 = field->alt_names.begin(); it2 != field->alt_names.end(); ++it2) {
unified_name = unified_form(*it2);
if (card_fields_alt_names.count(unified_name)) {
queue_message(MESSAGE_WARNING, _("Duplicate alternate card field name: ") + unified_name);
}
else {
card_fields_alt_names.emplace(unified_name, field->name);
FieldP field = *it;
String unified_name = unified_form(field->name);
if (card_fields_alt_names.count(unified_name)) {
queue_message(MESSAGE_WARNING, _("Duplicate alternate card field name: ") + unified_name);
}
else {
card_fields_alt_names.emplace(unified_name, field->name);
}
//String column_name = field->card_list_name.get();
//card_fields_alt_names.emplace(unified_form(column_name), field->name);
for (auto it2 = field->alt_names.begin(); it2 != field->alt_names.end(); ++it2) {
unified_name = unified_form(*it2);
if (card_fields_alt_names.count(unified_name)) {
queue_message(MESSAGE_WARNING, _("Duplicate alternate card field name: ") + unified_name);
}
else {
card_fields_alt_names.emplace(unified_name, field->name);
}
}
}
@@ -138,7 +139,7 @@ void Game::initCardListColorScript() {
return;
}
}
}
}
// special behaviour of reading/writing GamePs: only read/write the name
+3 -3
View File
@@ -12,7 +12,7 @@
#include <util/io/package.hpp>
#include <script/scriptable.hpp>
#include <script/dependency.hpp>
#include <util/dynamic_arg.hpp>
#include <util/dynamic_arg.hpp>
DECLARE_POINTER_TYPE(Field);
DECLARE_POINTER_TYPE(Style);
@@ -41,6 +41,7 @@ public:
vector<FieldP> set_fields; ///< Fields for set information
IndexMap<FieldP,StyleP> default_set_style; ///< Default style for the set fields, because it is often the same
vector<FieldP> card_fields; ///< Fields on each card
vector<String> card_links; ///< Possible links between cards
OptionalScript card_list_color_script; ///< Script that determines the color of items in the card list
OptionalScript import_script; ///< Script applied as the last step of the new_card function
vector<String> json_paths; ///< Paths inside JSON files to find the card array
@@ -50,8 +51,7 @@ public:
vector<WordListP> word_lists; ///< Word lists for editing with a drop down list
vector<AddCardsScriptP> add_cards_scripts; ///< Scripts for adding multiple cards to the set
vector<AutoReplaceP> auto_replaces; ///< Things to autoreplace in textboxes
map<String,String> card_fields_alt_names; ///< Other names that fields might go by, for example in CSV files
map<String,String> card_fields_alt_names; ///< Other names that fields might go by, for example in CSV files
bool has_keywords; ///< Does this game use keywords?
OptionalScript keyword_match_script; ///< For the keyword editor
vector<KeywordParamP> keyword_parameter_types;///< Types of keyword parameters
+4 -4
View File
@@ -118,8 +118,8 @@ String tr(const Package& pkg, const String& subcat, const String& key, DefaultLo
loc = find_wildcard_and_set(the_locale->package_translations, pkg.relativeFilename());
}
return loc->tr(subcat, key, def);
}
}
String tr(const String& pkg_relative_filename, const String& key, DefaultLocaleFun def) {
if (!the_locale) return def(key);
SubLocaleP loc = the_locale->package_translations[pkg_relative_filename];
@@ -127,8 +127,8 @@ String tr(const String& pkg_relative_filename, const String& key, DefaultLocaleF
loc = find_wildcard_and_set(the_locale->package_translations, pkg_relative_filename);
}
return loc->tr(key, def);
}
}
String tr(const String& pkg_relative_filename, const String& subcat, const String& key, DefaultLocaleFun def) {
if (!the_locale) return def(key);
SubLocaleP loc = the_locale->package_translations[pkg_relative_filename];
+5 -5
View File
@@ -126,7 +126,7 @@ StyleSheetSettings::StyleSheetSettings()
, card_angle (0, true)
, card_anti_alias (true, true)
, card_borders (true, true)
, card_draw_editing (true, true)
, card_draw_editing (true, true)
, card_normal_export (true, true)
, card_spellcheck_enabled(true, true)
{}
@@ -137,7 +137,7 @@ void StyleSheetSettings::useDefault(const StyleSheetSettings& ss) {
if (card_angle .isDefault()) card_angle .assignDefault(ss.card_angle);
if (card_anti_alias .isDefault()) card_anti_alias .assignDefault(ss.card_anti_alias);
if (card_borders .isDefault()) card_borders .assignDefault(ss.card_borders);
if (card_draw_editing .isDefault()) card_draw_editing .assignDefault(ss.card_draw_editing);
if (card_draw_editing .isDefault()) card_draw_editing .assignDefault(ss.card_draw_editing);
if (card_normal_export .isDefault()) card_normal_export .assignDefault(ss.card_normal_export);
if (card_spellcheck_enabled.isDefault()) card_spellcheck_enabled.assignDefault(ss.card_spellcheck_enabled);
}
@@ -148,7 +148,7 @@ IMPLEMENT_REFLECTION_NO_SCRIPT(StyleSheetSettings) {
REFLECT(card_angle);
REFLECT(card_anti_alias);
REFLECT(card_borders);
REFLECT(card_draw_editing);
REFLECT(card_draw_editing);
REFLECT(card_normal_export);
REFLECT(card_spellcheck_enabled);
}
@@ -225,8 +225,8 @@ ColumnSettings& Settings::columnSettingsFor(const Game& game, const Field& field
}
return cs;
}
StyleSheetSettings& Settings::stylesheetSettingsFor(const StyleSheet& stylesheet) {
// Use the canonical form here since the stylesheet name will be used as a stored key.
StyleSheetSettings& Settings::stylesheetSettingsFor(const StyleSheet& stylesheet) {
// Use the canonical form here since the stylesheet name will be used as a stored key.
// This does introduce the possibility of collision if two stylesheets return the same value canonically, but I think that's just a necessary risk.
StyleSheetSettingsP& ss = stylesheet_settings[canonical_name_form(stylesheet.name())];
if (!ss) ss = make_intrusive<StyleSheetSettings>();
+4 -4
View File
@@ -195,10 +195,10 @@ public:
// --------------------------------------------------- : Special game stuff
String apprentice_location;
// --------------------------------------------------- : Internal settings
double internal_scale;
bool internal_image_extension;
// --------------------------------------------------- : Internal settings
double internal_scale;
bool internal_image_extension;
// --------------------------------------------------- : Update checking
#if USE_OLD_STYLE_UPDATE_CHECKER
+6 -11
View File
@@ -38,13 +38,13 @@ StyleSheetP StyleSheet::byGameAndName(const Game& game, const String& name) {
}
} catch (PackageNotFoundError& e) {
queue_message(MESSAGE_ERROR, _("Missing stylesheet: ") + full_name);
// This causes a freeze when the set contains two cards that use the same missing StyleSheet, and the second one has styling_data
// Also, it's probably better to ask the user for an alternative for each missing StyleSheet individually
// This causes a freeze when the set contains two cards that use the same missing StyleSheet, and the second one has styling_data
// Also, it's probably better to ask the user for an alternative for each missing StyleSheet individually
//if (stylesheet_for_reading()) {
// // we already have a stylesheet higher up, so just return a null pointer
// return StyleSheetP();
//}
//}
// load an alternative stylesheet
StyleSheetP ss = select_stylesheet(game, name);
@@ -103,7 +103,7 @@ IMPLEMENT_REFLECTION(StyleSheet) {
REFLECT(card_width);
REFLECT(card_height);
REFLECT(card_dpi);
REFLECT(card_background);
REFLECT(card_background);
REFLECT(card_regions);
REFLECT(init_script);
// styling
@@ -122,12 +122,7 @@ IMPLEMENT_REFLECTION(StyleSheet) {
// extra card fields
REFLECT(extra_card_fields);
REFLECT_IF_READING {
if (extra_card_style.init(extra_card_fields)) {
// if a value is not editable, don't save it
FOR_EACH(f, extra_card_fields) {
if (!f->editable) f->save_value = false;
}
}
extra_card_style.init(extra_card_fields);
}
REFLECT(extra_card_style);
}
+6 -6
View File
@@ -11,16 +11,16 @@
#include <util/prec.hpp>
#include <util/io/package.hpp>
#include <util/real_point.hpp>
#include <script/scriptable.hpp>
// This isn't strictly needed for this file,
// but CardRegion needs to be referenced from _somewhere_ in the codebase for compilation reasons.
// Eventually if somewhere else is using the type then this can be removed.
#include <script/scriptable.hpp>
// This isn't strictly needed for this file,
// but CardRegion needs to be referenced from _somewhere_ in the codebase for compilation reasons.
// Eventually if somewhere else is using the type then this can be removed.
#include <data/card_region.hpp>
DECLARE_POINTER_TYPE(Game);
DECLARE_POINTER_TYPE(StyleSheet);
DECLARE_POINTER_TYPE(Field);
DECLARE_POINTER_TYPE(Style);
DECLARE_POINTER_TYPE(Style);
DECLARE_POINTER_TYPE(CardRegion);
// ----------------------------------------------------------------------------- : StyleSheet
@@ -38,7 +38,7 @@ public:
double card_width; ///< The width of a card in pixels
double card_height; ///< The height of a card in pixels
double card_dpi; ///< The resolution of a card in dots per inch
Color card_background; ///< The background color of cards
Color card_background; ///< The background color of cards
vector<CardRegionP> card_regions;
/// The styling for card fields
/** The indices should correspond to the card_fields in the Game */
+6 -3
View File
@@ -394,7 +394,7 @@ void SymbolFont::getCharInfo(RotatedDC& dc, double font_size, const SplitSymbols
RealSize SymbolFont::symbolSize(double font_size, const DrawableSymbol& sym) {
if (sym.symbol) {
return add_diagonal(sym.symbol->size(*this, font_size), spacing);
return add_diagonal(sym.symbol->size(*this, font_size), spacingSize(font_size));
} else {
return defaultSymbolSize(font_size);
}
@@ -403,12 +403,15 @@ RealSize SymbolFont::symbolSize(double font_size, const DrawableSymbol& sym) {
RealSize SymbolFont::defaultSymbolSize(double font_size) {
SymbolInFont* def = defaultSymbol();
if (def) {
return add_diagonal(def->size(*this, font_size), spacing);
return add_diagonal(def->size(*this, font_size), spacingSize(font_size));
} else {
return add_diagonal(RealSize(1,1), spacing);
return add_diagonal(RealSize(1,1), spacingSize(font_size));
}
}
RealSize SymbolFont::spacingSize(double font_size) {
return RealSize(spacing.width * font_size / 15.0, spacing.height * font_size / 15.0);
}
// ----------------------------------------------------------------------------- : InsertSymbolMenu
+4 -1
View File
@@ -84,7 +84,7 @@ public:
private:
double img_size; ///< Font size that the images use
RealSize spacing; ///< Spacing between sybmols (for the default font size)
RealSize spacing; ///< Spacing between sybmols, in pixels, for a font size of 15
// writing text
bool scale_text; ///< Should text be scaled down to fit in a symbol?
InsertSymbolMenuP insert_symbol_menu;
@@ -107,6 +107,9 @@ public:
/// The default size of symbols, including spacing
RealSize defaultSymbolSize(double font_size);
/// The spacing between symbols, accounting for font size
RealSize SymbolFont::spacingSize(double font_size);
DECLARE_REFLECTION();
};
+3 -3
View File
@@ -55,9 +55,9 @@ void linear_blend(Image& img1, const Image& img2, double x1,double y1, double x2
data1 += 3;
data2 += 3;
}
}
// Blend Alpha for the two images.
}
// Blend Alpha for the two images.
if (img1.HasAlpha() && img2.HasAlpha()) {
Byte *alpha1 = img1.GetAlpha(), *alpha2 = img2.GetAlpha();
for (int y = 0; y < height; ++y) {
+5 -5
View File
@@ -91,7 +91,7 @@ IMPLEMENT_REFLECTION_ENUM(ImageCombine) {
VALUE_N("greater than 235", COMBINE_GREATER_THAN_235);
VALUE_N("greater than 240", COMBINE_GREATER_THAN_240);
VALUE_N("greater than 245", COMBINE_GREATER_THAN_245);
VALUE_N("greater than 250", COMBINE_GREATER_THAN_250);
VALUE_N("greater than 250", COMBINE_GREATER_THAN_250);
VALUE_N("smaller than 5", COMBINE_SMALLER_THAN_5);
VALUE_N("smaller than 10", COMBINE_SMALLER_THAN_10);
VALUE_N("smaller than 15", COMBINE_SMALLER_THAN_15);
@@ -237,7 +237,7 @@ COMBINE_FUN(COMBINE_GREATER_THAN_230, a > 230 ? b : a)
COMBINE_FUN(COMBINE_GREATER_THAN_235, a > 235 ? b : a)
COMBINE_FUN(COMBINE_GREATER_THAN_240, a > 240 ? b : a)
COMBINE_FUN(COMBINE_GREATER_THAN_245, a > 245 ? b : a)
COMBINE_FUN(COMBINE_GREATER_THAN_250, a > 250 ? b : a)
COMBINE_FUN(COMBINE_GREATER_THAN_250, a > 250 ? b : a)
COMBINE_FUN(COMBINE_SMALLER_THAN_5, a < 5 ? b : a)
COMBINE_FUN(COMBINE_SMALLER_THAN_10, a < 10 ? b : a)
COMBINE_FUN(COMBINE_SMALLER_THAN_15, a < 15 ? b : a)
@@ -287,7 +287,7 @@ COMBINE_FUN(COMBINE_SMALLER_THAN_230, a < 230 ? b : a)
COMBINE_FUN(COMBINE_SMALLER_THAN_235, a < 235 ? b : a)
COMBINE_FUN(COMBINE_SMALLER_THAN_240, a < 240 ? b : a)
COMBINE_FUN(COMBINE_SMALLER_THAN_245, a < 245 ? b : a)
COMBINE_FUN(COMBINE_SMALLER_THAN_250, a < 250 ? b : a)
COMBINE_FUN(COMBINE_SMALLER_THAN_250, a < 250 ? b : a)
// ----------------------------------------------------------------------------- : Combining
@@ -306,7 +306,7 @@ void combine_image_do(Image& a, Image b) {
void combine_image(Image& a, const Image& b, ImageCombine combine) {
// Images must have same size
if (a.GetWidth() != b.GetWidth() || a.GetHeight() != b.GetHeight()) {
throw Error(_ERROR_("images used for combine blending must have the same size"));
throw Error(_ERROR_("images used for combine blending must have the same size"));
}
// Copy alpha channel?
if (b.HasAlpha()) {
@@ -392,7 +392,7 @@ void combine_image(Image& a, const Image& b, ImageCombine combine) {
DISPATCH(COMBINE_GREATER_THAN_235);
DISPATCH(COMBINE_GREATER_THAN_240);
DISPATCH(COMBINE_GREATER_THAN_245);
DISPATCH(COMBINE_GREATER_THAN_250);
DISPATCH(COMBINE_GREATER_THAN_250);
DISPATCH(COMBINE_SMALLER_THAN_5);
DISPATCH(COMBINE_SMALLER_THAN_10);
DISPATCH(COMBINE_SMALLER_THAN_15);
+133 -133
View File
@@ -14,7 +14,7 @@
#include <data/field/symbol.hpp>
#include <render/symbol/filter.hpp>
#include <gui/util.hpp> // load_resource_image
#include <wx/wfstream.h>
#include <wx/wfstream.h>
// ----------------------------------------------------------------------------- : GeneratedImage
@@ -308,7 +308,7 @@ bool EnlargeImage::operator == (const GeneratedImage& that) const {
// ----------------------------------------------------------------------------- : ResizeImage
Image ResizeImage::generate(const Options& opt) const {
Image img = image->generate(opt);
Image img = image->generate(opt);
return resample(img, width, height);
}
bool ResizeImage::operator == (const GeneratedImage& that) const {
@@ -321,16 +321,16 @@ bool ResizeImage::operator == (const GeneratedImage& that) const {
// ----------------------------------------------------------------------------- : BleedEdgedImage
Image BleedEdgedImage::generate(const Options& opt) const {
// create enlarged image
// create enlarged image
Image base_img = base_image->generate(opt);
int w = base_img.GetWidth(), h = base_img.GetHeight();
int w = base_img.GetWidth(), h = base_img.GetHeight();
if (w <= 0 || h <= 0) {
queue_message(MESSAGE_ERROR, _("Cannot add bleed edge to empty image"));
queue_message(MESSAGE_ERROR, _("Cannot add bleed edge to empty image"));
return base_img;
}
bool is_landscape = w > h;
int dw = int(w * (horizontal_size > 0.0 ? horizontal_size : is_landscape ? 0.037 : 0.048));
int dh = int(h * (vertical_size > 0.0 ? vertical_size : is_landscape ? 0.048 : 0.037));
}
bool is_landscape = w > h;
int dw = int(w * (horizontal_size > 0.0 ? horizontal_size : is_landscape ? 0.037 : 0.048));
int dh = int(h * (vertical_size > 0.0 ? vertical_size : is_landscape ? 0.048 : 0.037));
if (dw <= 0 && dh <= 0) {
return base_img;
}
@@ -339,119 +339,119 @@ Image BleedEdgedImage::generate(const Options& opt) const {
Image img = wxImage(width, height, false);
img.InitAlpha();
Byte* data = img.GetData();
Byte* alpha = img.GetAlpha();
// fill with background color
Byte* alpha = img.GetAlpha();
// fill with background color
for (UInt i = 0; i < size; ++i) {
data[3 * i + 0] = background_color.Red();
data[3 * i + 1] = background_color.Green();
data[3 * i + 2] = background_color.Blue();
alpha[i] = background_color.Alpha();
}
// paste original image
img.Paste(base_img, dw, dh, wxIMAGE_ALPHA_BLEND_COMPOSE);
// fill top left corner
// paste original image
img.Paste(base_img, dw, dh, wxIMAGE_ALPHA_BLEND_COMPOSE);
// fill top left corner
int pixel;
int x_start = 0;
int y_start = 0;
int ref = dw + dh * width;
int x_start = 0;
int y_start = 0;
int ref = dw + dh * width;
for (int y = 0; y < dh; ++y) {
for (int x = 0; x < dw; ++x) {
for (int x = 0; x < dw; ++x) {
pixel = x_start + x + (y_start + y) * width;
data[3 * pixel + 0] = data[3 * ref + 0];
data[3 * pixel + 1] = data[3 * ref + 1];
data[3 * pixel + 2] = data[3 * ref + 2];
alpha[pixel] = alpha[ref];
}
}
// fill top right corner
x_start = width - dw;
y_start = 0;
ref = x_start - 1 + dh * width;
for (int y = 0; y < dh; ++y) {
for (int x = 0; x < dw; ++x) {
pixel = x_start + x + (y_start + y) * width;
data[3 * pixel + 0] = data[3 * ref + 0];
data[3 * pixel + 1] = data[3 * ref + 1];
data[3 * pixel + 2] = data[3 * ref + 2];
alpha[pixel] = alpha[ref];
}
}
// fill bottom left corner
x_start = 0;
y_start = height - dh;
ref = dw + (y_start - 1) * width;
for (int y = 0; y < dh; ++y) {
for (int x = 0; x < dw; ++x) {
pixel = x_start + x + (y_start + y) * width;
data[3 * pixel + 0] = data[3 * ref + 0];
data[3 * pixel + 1] = data[3 * ref + 1];
data[3 * pixel + 2] = data[3 * ref + 2];
alpha[pixel] = alpha[ref];
alpha[pixel] = alpha[ref];
}
}
// fill bottom right corner
x_start = width - dw;
y_start = height - dh;
// fill top right corner
x_start = width - dw;
y_start = 0;
ref = x_start - 1 + dh * width;
for (int y = 0; y < dh; ++y) {
for (int x = 0; x < dw; ++x) {
pixel = x_start + x + (y_start + y) * width;
data[3 * pixel + 0] = data[3 * ref + 0];
data[3 * pixel + 1] = data[3 * ref + 1];
data[3 * pixel + 2] = data[3 * ref + 2];
alpha[pixel] = alpha[ref];
}
}
// fill bottom left corner
x_start = 0;
y_start = height - dh;
ref = dw + (y_start - 1) * width;
for (int y = 0; y < dh; ++y) {
for (int x = 0; x < dw; ++x) {
pixel = x_start + x + (y_start + y) * width;
data[3 * pixel + 0] = data[3 * ref + 0];
data[3 * pixel + 1] = data[3 * ref + 1];
data[3 * pixel + 2] = data[3 * ref + 2];
alpha[pixel] = alpha[ref];
}
}
// fill bottom right corner
x_start = width - dw;
y_start = height - dh;
ref = (x_start - 1) + (y_start - 1) * width;
for (int y = 0; y < dh; ++y) {
for (int x = 0; x < dw; ++x) {
for (int x = 0; x < dw; ++x) {
pixel = x_start + x + (y_start + y) * width;
data[3 * pixel + 0] = data[3 * ref + 0];
data[3 * pixel + 1] = data[3 * ref + 1];
data[3 * pixel + 2] = data[3 * ref + 2];
alpha[pixel] = alpha[ref];
alpha[pixel] = alpha[ref];
}
}
// fill left border
x_start = 0;
y_start = dh;
for (int y = 0; y < height - dh - dh; ++y) {
}
// fill left border
x_start = 0;
y_start = dh;
for (int y = 0; y < height - dh - dh; ++y) {
ref = dw + (y_start + y) * width;
for (int x = 0; x < dw; ++x) {
for (int x = 0; x < dw; ++x) {
pixel = x_start + x + (y_start + y) * width;
data[3 * pixel + 0] = data[3 * ref + 0];
data[3 * pixel + 1] = data[3 * ref + 1];
data[3 * pixel + 2] = data[3 * ref + 2];
alpha[pixel] = alpha[ref];
alpha[pixel] = alpha[ref];
}
}
// fill top border
x_start = dw;
y_start = 0;
}
// fill top border
x_start = dw;
y_start = 0;
for (int y = 0; y < dh; ++y) {
for (int x = 0; x < width - dw - dw; ++x) {
pixel = x_start + x + (y_start + y) * width;
for (int x = 0; x < width - dw - dw; ++x) {
pixel = x_start + x + (y_start + y) * width;
ref = x_start + x + dh * width;
data[3 * pixel + 0] = data[3 * ref + 0];
data[3 * pixel + 1] = data[3 * ref + 1];
data[3 * pixel + 2] = data[3 * ref + 2];
alpha[pixel] = alpha[ref];
alpha[pixel] = alpha[ref];
}
}
// fill right border
x_start = width - dw;
y_start = dh;
for (int y = 0; y < height - dh - dh; ++y) {
}
// fill right border
x_start = width - dw;
y_start = dh;
for (int y = 0; y < height - dh - dh; ++y) {
ref = width - dw - 1 + (y_start + y) * width;
for (int x = 0; x < dw; ++x) {
for (int x = 0; x < dw; ++x) {
pixel = x_start + x + (y_start + y) * width;
data[3 * pixel + 0] = data[3 * ref + 0];
data[3 * pixel + 1] = data[3 * ref + 1];
data[3 * pixel + 2] = data[3 * ref + 2];
alpha[pixel] = alpha[ref];
alpha[pixel] = alpha[ref];
}
}
// fill bottom border
x_start = dw;
y_start = height - dh;
}
// fill bottom border
x_start = dw;
y_start = height - dh;
for (int y = 0; y < dh; ++y) {
for (int x = 0; x < width - dw - dw; ++x) {
pixel = x_start + x + (y_start + y) * width;
for (int x = 0; x < width - dw - dw; ++x) {
pixel = x_start + x + (y_start + y) * width;
ref = x_start + x + (height - dh - 1) * width;
data[3 * pixel + 0] = data[3 * ref + 0];
data[3 * pixel + 1] = data[3 * ref + 1];
data[3 * pixel + 2] = data[3 * ref + 2];
alpha[pixel] = alpha[ref];
alpha[pixel] = alpha[ref];
}
}
// done
@@ -621,7 +621,7 @@ Image PackagedImage::generate(const Options& opt) const {
if (img.HasMask()) img.InitAlpha(); // we can't handle masks
return img;
} else {
throw ScriptError(_("Unable to load image '") + filename + _("' from '" + opt.package->name() + _("'")));
throw ScriptError(_("Unable to load image '") + filename + _("' from '") + opt.package->name() + _("'"));
}
}
bool PackagedImage::operator == (const GeneratedImage& that) const {
@@ -642,17 +642,17 @@ Image BuiltInImage::generate(const Options& opt) const {
bool BuiltInImage::operator == (const GeneratedImage& that) const {
const BuiltInImage* that2 = dynamic_cast<const BuiltInImage*>(&that);
return that2 && name == that2->name;
}
// ----------------------------------------------------------------------------- : ArbitraryImage
Image ArbitraryImage::generate(const Options& opt) const {
return image;
}
bool ArbitraryImage::operator == (const GeneratedImage& that) const {
const ArbitraryImage* that2 = dynamic_cast<const ArbitraryImage*>(&that);
return that2 && image.IsSameAs(that2->image);
}
}
// ----------------------------------------------------------------------------- : ArbitraryImage
Image ArbitraryImage::generate(const Options& opt) const {
return image;
}
bool ArbitraryImage::operator == (const GeneratedImage& that) const {
const ArbitraryImage* that2 = dynamic_cast<const ArbitraryImage*>(&that);
return that2 && image.IsSameAs(that2->image);
}
// ----------------------------------------------------------------------------- : SymbolToImage
@@ -716,48 +716,48 @@ bool ImageValueToImage::operator == (const GeneratedImage& that) const {
return that2 && filename == that2->filename
&& age == that2->age;
}
// ----------------------------------------------------------------------------- : ExternalImage
Image ExternalImage::generate(const Options& opt) const {
wxFileName fname(filepath, wxPATH_UNIX);
String filePathString = fname.GetAbsolutePath();
// ----------------------------------------------------------------------------- : ExternalImage
Image ExternalImage::generate(const Options& opt) const {
wxFileName fname(filepath, wxPATH_UNIX);
String filePathString = fname.GetAbsolutePath();
// has a pre-existing .mse-set file been loaded?
if (opt.local_package->needSaveAs()) throw ScriptError(_ERROR_1_("can't import image without set", filePathString));
// does the file pointed to by filepath exist?
if (!fname.FileExists()) throw ScriptError(_ERROR_1_("import not found", filePathString));
String fileExt = fname.GetExt();
wxBitmapType bitmapType;
if (fileExt == _("png")) bitmapType = wxBITMAP_TYPE_PNG;
else if (fileExt == _("jpg") || fileExt == _("jpeg")) bitmapType = wxBITMAP_TYPE_JPEG;
else bitmapType = wxBITMAP_TYPE_BMP;
// does the file exist in the package?
String fileNameNoExtension = fname.GetName();
if (!opt.local_package->existsIn(fileNameNoExtension)) {
auto outStream = opt.local_package->openOut(fileNameNoExtension);
wxFileInputStream inStream = wxFileInputStream(filepath.ToStdString());
if (!inStream.IsOk()) throw ScriptError(_ERROR_1_("can't create file stream", filePathString));
outStream->Write(inStream);
if (!outStream->IsOk()) throw ScriptError(_ERROR_1_("can't write image to set", filePathString));
outStream->Close();
}
// save the package with the new image
opt.local_package->save(false);
auto imageInputStream = opt.local_package->openIn(fileNameNoExtension);
Image img(*imageInputStream.get(), bitmapType);
if (!img.IsOk()) throw ScriptError(_ERROR_1_("can't import image", filePathString));
return img;
}
bool ExternalImage::operator == (const GeneratedImage& that) const {
const ExternalImage* that2 = dynamic_cast<const ExternalImage*>(&that);
return that2 && that2->filepath == filepath;
}
if (opt.local_package->needSaveAs()) throw ScriptError(_ERROR_1_("can't import image without set", filePathString));
// does the file pointed to by filepath exist?
if (!fname.FileExists()) throw ScriptError(_ERROR_1_("import not found", filePathString));
String fileExt = fname.GetExt();
wxBitmapType bitmapType;
if (fileExt == _("png")) bitmapType = wxBITMAP_TYPE_PNG;
else if (fileExt == _("jpg") || fileExt == _("jpeg")) bitmapType = wxBITMAP_TYPE_JPEG;
else bitmapType = wxBITMAP_TYPE_BMP;
// does the file exist in the package?
String fileNameNoExtension = fname.GetName();
if (!opt.local_package->existsIn(fileNameNoExtension)) {
auto outStream = opt.local_package->openOut(fileNameNoExtension);
wxFileInputStream inStream = wxFileInputStream(filepath.ToStdString());
if (!inStream.IsOk()) throw ScriptError(_ERROR_1_("can't create file stream", filePathString));
outStream->Write(inStream);
if (!outStream->IsOk()) throw ScriptError(_ERROR_1_("can't write image to set", filePathString));
outStream->Close();
}
// save the package with the new image
opt.local_package->save(false);
auto imageInputStream = opt.local_package->openIn(fileNameNoExtension);
Image img(*imageInputStream.get(), bitmapType);
if (!img.IsOk()) throw ScriptError(_ERROR_1_("can't import image", filePathString));
return img;
}
bool ExternalImage::operator == (const GeneratedImage& that) const {
const ExternalImage* that2 = dynamic_cast<const ExternalImage*>(&that);
return that2 && that2->filepath == filepath;
}
+27 -27
View File
@@ -327,9 +327,9 @@ public:
{}
Image generate(const Options& opt) const override;
bool operator == (const GeneratedImage& that) const override;
private:
private:
GeneratedImageP base_image;
double horizontal_size, vertical_size;
double horizontal_size, vertical_size;
Color background_color;
};
@@ -395,18 +395,18 @@ public:
bool operator == (const GeneratedImage& that) const override;
private:
String name;
};
// ----------------------------------------------------------------------------- : Arbitrary
};
// ----------------------------------------------------------------------------- : Arbitrary
class ArbitraryImage : public GeneratedImage {
public:
inline ArbitraryImage(const Image image)
: image(image)
{}
Image generate(const Options& opt) const override;
bool operator == (const GeneratedImage& that) const override;
private:
public:
inline ArbitraryImage(const Image image)
: image(image)
{}
Image generate(const Options& opt) const override;
bool operator == (const GeneratedImage& that) const override;
private:
Image image;
};
@@ -447,17 +447,17 @@ private:
LocalFileName filename;
Age age; ///< Age the image was last updated
};
// ----------------------------------------------------------------------------- : ExternalImage
/// Load an image from the filesystem
class ExternalImage : public GeneratedImage {
public:
ExternalImage(const String& filepath) : filepath(filepath) {};
Image generate(const Options&) const override;
bool operator == (const GeneratedImage& that) const override;
inline String toString() { return filepath; }
inline String toCode() const override { return _("<image>"); }
private:
String filepath;
};
// ----------------------------------------------------------------------------- : ExternalImage
/// Load an image from the filesystem
class ExternalImage : public GeneratedImage {
public:
ExternalImage(const String& filepath) : filepath(filepath) {};
Image generate(const Options&) const override;
bool operator == (const GeneratedImage& that) const override;
inline String toString() { return filepath; }
inline String toCode() const override { return _("<image>"); }
private:
String filepath;
};
+1 -1
View File
@@ -172,7 +172,7 @@ enum ImageCombine
, COMBINE_GREATER_THAN_235
, COMBINE_GREATER_THAN_240
, COMBINE_GREATER_THAN_245
, COMBINE_GREATER_THAN_250
, COMBINE_GREATER_THAN_250
, COMBINE_SMALLER_THAN_5
, COMBINE_SMALLER_THAN_10
, COMBINE_SMALLER_THAN_15
+111
View File
@@ -0,0 +1,111 @@
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) Twan van Laarhoven and the other MSE developers |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
// ----------------------------------------------------------------------------- : Includes
#include <util/prec.hpp>
#include <data/game.hpp>
#include <gui/card_link_window.hpp>
#include <gui/control/select_card_list.hpp>
#include <util/window_id.hpp>
#include <data/action/set.hpp>
#include <wx/statline.h>
// ----------------------------------------------------------------------------- : ExportCardSelectionChoice
CardLinkWindow::CardLinkWindow(Window* parent, const SetP& set, const CardP& selected_card, bool sizer)
: wxDialog(parent, wxID_ANY, _TITLE_("link cards"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
, set(set), selected_card(selected_card)
{
// init controls
selected_relation = new wxTextCtrl(this, wxID_ANY, wxEmptyString);
linked_relation = new wxTextCtrl(this, wxID_ANY, wxEmptyString);
relation_type = new wxChoice(this, ID_CARD_LINK_TYPE, wxDefaultPosition, wxDefaultSize, 0, nullptr);
relation_type->Clear();
FOR_EACH(link, set->game->card_links) {
relation_type->Append(link);
}
relation_type->Append(_LABEL_("custom link"));
relation_type->SetSelection(0);
setRelationType();
list = new SelectCardList(this, wxID_ANY);
list->setSet(set);
list->selectNone();
sel_none = new wxButton(this, ID_SELECT_NONE, _BUTTON_("select none"));
// init sizers
if (sizer) {
wxSizer* s = new wxBoxSizer(wxVERTICAL);
s->Add(new wxStaticText(this, -1, _LABEL_("linked cards relation")), 0, wxALL, 8);
s->Add(relation_type, 0, wxEXPAND | (wxALL & ~wxTOP), 8);
s->Add(new wxStaticText(this, -1, _(" ") + _LABEL_("selected card")), 0, wxALL, 4);
s->Add(selected_relation, 0, wxEXPAND | (wxALL & ~wxTOP), 8);
s->Add(new wxStaticText(this, -1, _(" ") + _LABEL_("linked cards")), 0, wxALL, 4);
s->Add(linked_relation, 0, wxEXPAND | (wxALL & ~wxTOP), 8);
s->Add(new wxStaticText(this, wxID_ANY, _LABEL_("select linked cards")), 0, wxALL & ~wxBOTTOM, 8);
s->Add(list, 1, wxEXPAND | wxALL, 8);
wxSizer* s2 = new wxBoxSizer(wxHORIZONTAL);
s2->Add(sel_none, 0, wxEXPAND | wxRIGHT, 8);
s2->Add(CreateButtonSizer(wxOK | wxCANCEL), 1, wxEXPAND, 8);
s->Add(s2, 0, wxEXPAND | (wxALL & ~wxTOP), 8);
s->SetSizeHints(this);
SetSizer(s);
SetSize(600,500);
}
}
bool CardLinkWindow::isSelected(const CardP& card) const {
return list->isSelected(card);
}
void CardLinkWindow::getSelection(vector<CardP>& out) const {
list->getSelection(out);
}
void CardLinkWindow::setSelection(const vector<CardP>& cards) {
list->setSelection(cards);
}
void CardLinkWindow::setRelationType() {
int sel = relation_type->GetSelection();
if (sel == relation_type->GetCount() - 1) { // Custom type
selected_relation->ChangeValue(_LABEL_("custom link selected"));
selected_relation->Enable();
linked_relation->ChangeValue(_LABEL_("custom link linked"));
linked_relation->Enable();
}
else {
String relation = relation_type->GetString(sel);
int delimiter_pos = relation.find("//");
selected_relation->ChangeValue(relation.substr(0, delimiter_pos).Trim().Trim(false));
selected_relation->Enable(false);
linked_relation->ChangeValue(delimiter_pos + 2 < relation.Length() ? relation.substr(delimiter_pos + 2).Trim().Trim(false) : _LABEL_("custom link undefined"));
linked_relation->Enable(false);
}
}
void CardLinkWindow::onSelectNone(wxCommandEvent&) {
list->selectNone();
}
void CardLinkWindow::onRelationTypeChange(wxCommandEvent&) {
setRelationType();
}
void CardLinkWindow::onOk(wxCommandEvent&) {
// Perform the linking
// The selected_card is the one selected on the main cards tab
// The linked_cards are the ones selected in this dialogue window
vector<CardP> linked_cards;
getSelection(linked_cards);
set->actions.addAction(make_unique<LinkCardsAction>(*set, selected_card, linked_cards, selected_relation->GetValue(), linked_relation->GetValue()));
// Done
EndModal(wxID_OK);
}
BEGIN_EVENT_TABLE(CardLinkWindow, wxDialog)
EVT_BUTTON (ID_SELECT_NONE, CardLinkWindow::onSelectNone)
EVT_BUTTON (wxID_OK, CardLinkWindow::onOk)
EVT_CHOICE (ID_CARD_LINK_TYPE, CardLinkWindow::onRelationTypeChange)
END_EVENT_TABLE ()
+52
View File
@@ -0,0 +1,52 @@
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| 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 <util/prec.hpp>
DECLARE_POINTER_TYPE(Set);
DECLARE_POINTER_TYPE(Card);
DECLARE_POINTER_TYPE(ExportCardSelectionChoice);
class SelectCardList;
// ----------------------------------------------------------------------------- : CardLinkWindow
/// A window for selecting a subset of the cards from a set,
/** and selecting a link relation type.
/** this is used when linking cards
*/
class CardLinkWindow : public wxDialog {
public:
CardLinkWindow(Window* parent, const SetP& set, const CardP& selected_card, bool sizer=true);
/// Is the given card selected?
bool isSelected(const CardP& card) const;
/// Get a list of all selected cards
void getSelection(vector<CardP>& out) const;
/// Change which cards are selected
void setSelection(const vector<CardP>& cards);
/// Change the type of link relation
void setRelationType();
protected:
DECLARE_EVENT_TABLE();
wxChoice* relation_type;
wxTextCtrl* selected_relation, *linked_relation;
SelectCardList* list;
SetP set;
CardP selected_card;
wxButton* sel_none;
void onRelationTypeChange(wxCommandEvent&);
void onOk(wxCommandEvent&);
void onSelectNone(wxCommandEvent&);
};
+23
View File
@@ -9,6 +9,7 @@
#include <util/prec.hpp>
#include <gui/control/card_editor.hpp>
#include <gui/value/editor.hpp>
#include <gui/set/cards_panel.hpp>
#include <gui/util.hpp>
#include <data/field.hpp>
#include <data/stylesheet.hpp>
@@ -360,6 +361,16 @@ void DataEditor::onMotion(wxMouseEvent& ev) {
}
}
void DataEditor::onMouseEnter(wxMouseEvent& ev) {
ev.Skip();
if (GetId() == ID_CARD_LINK_EDITOR) {
CardsPanel* panel = dynamic_cast<CardsPanel*> (GetParent());
if (panel) {
panel->refreshCard(card);
}
}
}
void DataEditor::onMouseLeave(wxMouseEvent& ev) {
// on mouse leave for editor
if (hovered_viewer) {
@@ -462,6 +473,13 @@ void DataEditor::onChar(wxKeyEvent& ev) {
} else {
ev.Skip();
}
if (GetId() == ID_CARD_LINK_EDITOR) {
CardsPanel* panel = dynamic_cast<CardsPanel*> (GetParent());
if (panel) {
panel->refreshCard(card);
}
}
}
// ----------------------------------------------------------------------------- : Menu events
@@ -503,6 +521,10 @@ void DataEditor::onFocus(wxFocusEvent& ev) {
selectFirst();
}
}
CardsPanel* panel = dynamic_cast<CardsPanel*> (GetParent());
if (panel) {
panel->setFocusedEditor(this);
}
}
void DataEditor::onLoseFocus(wxFocusEvent& ev) {
if (current_editor) {
@@ -520,6 +542,7 @@ BEGIN_EVENT_TABLE(DataEditor, CardViewer)
EVT_RIGHT_DOWN (DataEditor::onRightDown)
EVT_MOTION (DataEditor::onMotion)
EVT_MOUSEWHEEL (DataEditor::onMouseWheel)
EVT_ENTER_WINDOW (DataEditor::onMouseEnter)
EVT_LEAVE_WINDOW (DataEditor::onMouseLeave)
EVT_CONTEXT_MENU (DataEditor::onContextMenu)
EVT_MENU (wxID_ANY, DataEditor::onMenu)
+1
View File
@@ -111,6 +111,7 @@ private:
void onRightDown (wxMouseEvent&);
void onMotion (wxMouseEvent&);
void onMouseWheel(wxMouseEvent&);
void onMouseEnter(wxMouseEvent&);
void onMouseLeave(wxMouseEvent&);
void onLoseCapture(wxMouseCaptureLostEvent&);
+54 -6
View File
@@ -10,8 +10,9 @@
#include <gui/control/card_list.hpp>
#include <gui/control/card_list_column_select.hpp>
#include <gui/set/window.hpp> // for sorting all cardlists in a window
#include <gui/util.hpp>
#include <gui/add_csv_window.hpp>
#include <gui/card_link_window.hpp>
#include <gui/util.hpp>
#include <gui/add_csv_window.hpp>
#include <gui/add_json_window.hpp>
#include <data/game.hpp>
#include <data/field.hpp>
@@ -25,6 +26,7 @@
#include <data/action/value.hpp>
#include <util/window_id.hpp>
#include <wx/clipbrd.h>
#include <unordered_set>
DECLARE_POINTER_TYPE(ChoiceValue);
@@ -157,6 +159,31 @@ bool CardListBase::doCopy() {
wxTheClipboard->Close();
return ok;
}
bool CardListBase::doCopyCardAndLinkedCards() {
if (!canCopy()) return false;
vector<CardP> cards_selected;
getSelection(cards_selected);
if (cards_selected.size() < 1) return false;
if (!wxTheClipboard->Open()) return false;
vector<CardP> cards_to_copy;
unordered_set<CardP> cards_already_added;
FOR_EACH(card, cards_selected) {
if (cards_already_added.find(card) == cards_already_added.end()) {
cards_to_copy.push_back(card);
cards_already_added.insert(card);
}
vector<pair<CardP, String>> linked_cards = card->getLinkedCards(*set);
FOR_EACH(linked_card, linked_cards) {
if (cards_already_added.find(linked_card.first) == cards_already_added.end()) {
cards_to_copy.push_back(linked_card.first);
cards_already_added.insert(linked_card.first);
}
}
}
bool ok = wxTheClipboard->SetData(new CardsOnClipboard(set, cards_to_copy)); // ignore result
wxTheClipboard->Close();
return ok;
}
bool CardListBase::doPaste() {
// get data
if (!canPaste()) return false;
@@ -182,7 +209,26 @@ bool CardListBase::doDelete() {
set->actions.addAction(make_unique<AddCardAction>(REMOVE, *set, cards_to_delete));
return true;
}
// --------------------------------------------------- : CardListBase : Card linking
bool CardListBase::canLink() const {
vector<CardP> selected_cards;
getSelection(selected_cards);
return selected_cards.size() == 1;
}
bool CardListBase::doLink() {
CardLinkWindow wnd(this, set, getCard());
if (wnd.ShowModal() == wxID_OK) {
// The actual linking is done in this window's onOk function
return true;
}
return false;
}
bool CardListBase::doUnlink(CardP unlinked_card) {
set->actions.addAction(make_unique<UnlinkCardsAction>(*set, getCard(), unlinked_card));
return true;
}
bool CardListBase::doAddCSV() {
AddCSVWindow wnd(this, set, true);
if (wnd.ShowModal() == wxID_OK) {
@@ -190,8 +236,8 @@ bool CardListBase::doAddCSV() {
return true;
}
return false;
}
}
bool CardListBase::doAddJSON() {
AddJSONWindow wnd(this, set, true);
if (wnd.ShowModal() == wxID_OK) {
@@ -199,7 +245,7 @@ bool CardListBase::doAddJSON() {
return true;
}
return false;
}
}
// ----------------------------------------------------------------------------- : CardListBase : Building the list
@@ -412,10 +458,12 @@ void CardListBase::onContextMenu(wxContextMenuEvent&) {
wxMenu m;
add_menu_item_tr(&m, wxID_CUT, "cut", "cut_card");
add_menu_item_tr(&m, wxID_COPY, "copy", "copy_card");
add_menu_item_tr(&m, ID_CARD_AND_LINK_COPY, "card_copy", "copy card and links");
add_menu_item_tr(&m, wxID_PASTE, "paste", "paste_card");
m.AppendSeparator();
add_menu_item_tr(&m, ID_CARD_ADD, "card_add", "add card");
add_menu_item_tr(&m, ID_CARD_REMOVE, "card_del", "remove card");
add_menu_item_tr(&m, ID_CARD_LINK, "card_link", "link card");
PopupMenu(&m);
}
}
+12 -5
View File
@@ -64,9 +64,9 @@ public:
// --------------------------------------------------- : Selection
inline CardP getCard() const { return static_pointer_cast<Card>(selected_item); }
inline void setCard(const CardP& card) { selectItem(card, true, false); }
inline CardP getCard() const { return static_pointer_cast<Card>(selected_item); }
inline void setCard(const CardP& card, bool event = false) { selectItem(card, true, event); }
// --------------------------------------------------- : Clipboard
bool canCut() const override;
@@ -75,11 +75,18 @@ public:
bool canDelete() const override;
// Try to perform a clipboard operation, return success
bool doCopy() override;
bool doCopyCardAndLinkedCards();
bool doPaste() override;
bool doDelete() override;
bool doAddCSV();
bool doAddJSON();
bool doAddJSON();
// --------------------------------------------------- : Card linking
bool canLink() const;
bool doLink();
bool doUnlink(CardP unlinked_card);
// --------------------------------------------------- : Set actions
void onBeforeChangeSet() override;
@@ -107,7 +114,7 @@ protected:
/// Send an 'item selected' event for the currently selected item (selected_item)
void sendEvent() override { sendEvent(EVENT_CARD_SELECT); }
void sendEvent(int type = EVENT_CARD_SELECT);
void sendEvent(int type);
/// Compare cards
bool compareItems(void* a, void* b) const override;
+23 -2
View File
@@ -8,10 +8,13 @@
#include <util/prec.hpp>
#include <gui/control/card_viewer.hpp>
#include <gui/control/image_card_list.hpp>
#include <gui/set/cards_panel.hpp>
#include <data/stylesheet.hpp>
#include <data/settings.hpp>
#include <render/value/viewer.hpp>
#include <wx/dcbuffer.h>
#include <util/window_id.hpp>
// ----------------------------------------------------------------------------- : Events
@@ -31,7 +34,11 @@ wxSize CardViewer::DoGetBestSize() const {
if (set) {
if (!stylesheet) stylesheet = set->stylesheet;
StyleSheetSettings& ss = settings.stylesheetSettingsFor(*stylesheet);
wxSize size(int(stylesheet->card_width * (150.0 / stylesheet->card_dpi) * ss.card_zoom()), int(stylesheet->card_height * (150.0 / stylesheet->card_dpi) * ss.card_zoom()));
double dpi_factor = stylesheet->card_dpi <= 150.0 ? 1.0 : 150.0 / stylesheet->card_dpi;
double width = stylesheet->card_width * dpi_factor * ss.card_zoom();
double height = stylesheet->card_height * dpi_factor * ss.card_zoom();
double link_factor = GetId() == ID_CARD_LINK_VIEWER ? (height * 0.5 - 41.0) / height : GetId() == ID_CARD_LINK_EDITOR ? (height * 0.97 - 41.0) / height : 1.0; // Subtract 41 pixels for the link title
wxSize size(int(link_factor * width), int(link_factor * height));
if (is_sideways(deg_to_rad(ss.card_angle()))) swap(size.x, size.y);
return size + ws - cs;
}
@@ -104,6 +111,16 @@ void CardViewer::onPaint(wxPaintEvent&) {
}
}
void CardViewer::onClick(wxMouseEvent& ev) {
ev.Skip(); // allow DataEditor::onLeftDown to process this event as well
if (GetId() == ID_CARD_LINK_VIEWER) {
CardsPanel* panel = dynamic_cast<CardsPanel*> (GetParent());
if (panel) {
panel->setCard(getCard(), true);
}
}
}
void CardViewer::drawViewer(RotatedDC& dc, ValueViewer& v) {
if (shouldDraw(v)) v.draw(dc);
}
@@ -150,11 +167,15 @@ Rotation CardViewer::getRotation() const {
StyleSheetSettings& ss = settings.stylesheetSettingsFor(*stylesheet);
int dx = CanScroll(wxHORIZONTAL) ? GetScrollPos(wxHORIZONTAL) : 0;
int dy = CanScroll(wxVERTICAL) ? GetScrollPos(wxVERTICAL) : 0;
return Rotation(deg_to_rad(ss.card_angle()), stylesheet->getCardRect().move(-dx,-dy,0,0), (150.0 / stylesheet->card_dpi) * ss.card_zoom(), 1.0, ROTATION_ATTACH_TOP_LEFT);
double dpi_factor = stylesheet->card_dpi <= 150.0 ? 1.0 : 150.0 / stylesheet->card_dpi;
double height = stylesheet->card_height * dpi_factor * ss.card_zoom();
double link_factor = GetId() == ID_CARD_LINK_VIEWER ? (height * 0.5 - 41.0) / height : GetId() == ID_CARD_LINK_EDITOR ? (height * 0.97 - 41.0) / height : 1.0; // Subtract 41 pixels for the link title
return Rotation(deg_to_rad(ss.card_angle()), stylesheet->getCardRect().move(-dx,-dy,0,0), link_factor * dpi_factor * ss.card_zoom(), 1.0, ROTATION_ATTACH_TOP_LEFT);
}
// ----------------------------------------------------------------------------- : Event table
BEGIN_EVENT_TABLE(CardViewer, wxControl)
EVT_PAINT(CardViewer::onPaint)
EVT_LEFT_DOWN(CardViewer::onClick)
END_EVENT_TABLE ()
+4 -2
View File
@@ -53,9 +53,11 @@ protected:
private:
DECLARE_EVENT_TABLE();
void onPaint(wxPaintEvent&);
void onClick(wxMouseEvent&);
Bitmap buffer; ///< Off-screen buffer we draw to
bool up_to_date; ///< Is the buffer up to date?
+9 -1
View File
@@ -79,7 +79,7 @@ void ItemList::selectItem(const VoidP& item, bool focus, bool event) {
focusNone();
}
selected_item = item;
if (event) sendEvent();
if (event) sendEvent(); // sending an event will trigger a UI update
findSelectedItemPos();
if (focus) focusSelectedItem();
}
@@ -111,6 +111,14 @@ void ItemList::findSelectedItemPos() {
}
}
}
long ItemList::findGivenItemPos(const VoidP& item) {
long count = GetItemCount();
for (long pos = 0; pos < count; ++pos) {
if (getItem(pos) == item) {
return pos;
}
}
}
void ItemList::focusSelectedItem(bool force_focus) {
if (GetItemCount() > 0) {
if (selected_item_pos == -1 || (size_t)selected_item_pos > sorted_list.size()) {
+3 -1
View File
@@ -42,7 +42,9 @@ public:
void selectFirst();
/// Select all items
void doSelectAll();
/// Find the position for a given item
long findGivenItemPos(const VoidP& item);
// --------------------------------------------------- : Clipboard
virtual bool canCut() const { return canCopy() && canDelete(); }
+9 -9
View File
@@ -48,9 +48,9 @@ protected:
virtual void onHide() {}
inline bool isRoot() { return parent_menu == nullptr; }
/// Should the list of choices be displayed as a slider (if all choices are numbers)
bool is_slider = false;
/// Should the list of choices be displayed as a slider (if all choices are numbers)
bool is_slider = false;
// --------------------------------------------------- : Selection
static const size_t NO_SELECTION = (size_t)-1;
@@ -82,12 +82,12 @@ protected:
static const int marginW = 0;
static const int marginH = 0;
static bool slider_loaded;
static wxBitmap slider_left;
static wxBitmap slider_right;
static wxBitmap slider_center;
static wxBitmap slider_tick;
static bool slider_loaded;
static wxBitmap slider_left;
static wxBitmap slider_right;
static wxBitmap slider_center;
static wxBitmap slider_tick;
// may be changed by derived class
int text_offset; ///< Vertical distance between top of item and text
+152 -85
View File
@@ -13,15 +13,18 @@
#include <util/rotation.hpp>
#include <gfx/gfx.hpp>
#include <wx/spinctrl.h>
#include <wx/dcbuffer.h>
#include <wx/dcbuffer.h>
map<String, String> ImageSliceWindow::previously_used_settings_path;
map<pair<String, String>, pair<wxRect, int>> ImageSliceWindow::previously_used_settings_value;
// ----------------------------------------------------------------------------- : ImageSlice
ImageSlice::ImageSlice(const Image& source, const wxSize& target_size)
: source(source), target_size(target_size)
ImageSlice::ImageSlice(const Image& source, const String& source_path, const String& card_name, const wxSize& target_size)
: source(source), source_path(source_path), card_name(card_name), target_size(target_size)
, selection(0, 0, source.GetWidth(), source.GetHeight())
, allow_outside(false), aspect_fixed(true)
, sharpen(false), sharpen_amount(25)
, sharpen(false), sharpen_amount(0)
{}
void ImageSlice::constrain(PreferedProperty prefer) {
@@ -51,24 +54,24 @@ void ImageSlice::constrain(PreferedProperty prefer) {
}
}
void ImageSlice::centerSelection() {
centerSelectionHorizontally();
void ImageSlice::centerSelection() {
centerSelectionHorizontally();
centerSelectionVertically();
}
void ImageSlice::centerSelectionHorizontally() {
}
void ImageSlice::centerSelectionHorizontally() {
if (selection.GetWidth() < source.GetWidth()) {
selection.x = ((source.GetWidth() - selection.GetWidth()) / 2);
}
}
void ImageSlice::centerSelectionVertically() {
if (selection.GetHeight() < source.GetHeight()) {
selection.y = ((source.GetHeight() - selection.GetHeight()) / 2);
}
}
Image ImageSlice::getSlice(double scale) const {
void ImageSlice::centerSelectionVertically() {
if (selection.GetHeight() < source.GetHeight()) {
selection.y = ((source.GetHeight() - selection.GetHeight()) / 2);
}
}
Image ImageSlice::getSlice(double scale) const {
wxSize scaled_target_size = target_size * scale;
if (selection.width == scaled_target_size.GetWidth() && selection.height == scaled_target_size.GetHeight() && selection.x == 0 && selection.y == 0) {
// exactly the right size
@@ -93,20 +96,31 @@ DEFINE_EVENT_TYPE(EVENT_SLICE_CHANGED);
// ----------------------------------------------------------------------------- : ImageSliceWindow
ImageSliceWindow::ImageSliceWindow(Window* parent, const Image& source, const wxSize& target_size, const AlphaMask& mask)
ImageSliceWindow::ImageSliceWindow(Window* parent, const Image& source, const String& filename, const String& cardname, const wxSize& target_size, const AlphaMask& mask)
: wxDialog(parent,wxID_ANY,_TITLE_("slice image"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxFULL_REPAINT_ON_RESIZE)
, slice(source, target_size)
, slice(source, filename, cardname, target_size)
, initialized(false)
{
// init slice
slice.constrain();
slice.centerSelection();
// init slice
pair<String, String> settings_entry = { filename, cardname };
if (previously_used_settings_value.find(settings_entry) != previously_used_settings_value.end()) {
//slice.allow_outside = true; this currrently crashes
slice.aspect_fixed = false;
slice.sharpen = true;
slice.sharpen_amount = previously_used_settings_value[settings_entry].second;
slice.selection = previously_used_settings_value[settings_entry].first;
slice.constrain();
}
else {
slice.constrain();
slice.centerSelection();
}
// init controls
const wxPoint defPos = wxDefaultPosition;
const wxSize spinSize(80,-1);
selector = new ImageSliceSelector(this, ID_SELECTOR, slice);
preview = new ImageSlicePreview (this, ID_PREVIEW, slice, mask);
preview = new ImageSlicePreview (this, ID_PREVIEW, slice, mask, 0);
String sizes[] = { _LABEL_("original size")
, _LABEL_("size to fit")
@@ -135,7 +149,14 @@ ImageSliceWindow::ImageSliceWindow(Window* parent, const Image& source, const wx
sharpen_amount = new wxSlider(this, ID_SHARPEN_AMOUNT, 0, 0, 100);
// allowOutside= new CheckBox(&this, idSliceAllowOutside, _("Allow selection outside source"))
// bgColor = new ColorSelector(&this, wxID_ANY)
String grids[] = { _LABEL_("none")
, _LABEL_("grid halves")
, _LABEL_("grid thirds")
, _LABEL_("grid fourths")
, _LABEL_("grid fifths") };
grid = new wxRadioBox(this, ID_GRID, _LABEL_("grid"), defPos, wxDefaultSize, 5, grids, 1);
// init sizers
wxSizer* s = new wxBoxSizer(wxVERTICAL);
// top row: image editors
@@ -164,22 +185,22 @@ ImageSliceWindow::ImageSliceWindow(Window* parent, const Image& source, const wx
s7->Add(width, 0, wxEXPAND);
s7->Add(new wxStaticText(this, wxID_ANY, _LABEL_("selection height")), 0, wxALIGN_CENTER_VERTICAL);
s7->Add(height, 0, wxEXPAND);
s7->Add(new wxStaticText(this, wxID_ANY, _LABEL_("selection center")), 0, wxALIGN_CENTER_VERTICAL);
wxBoxSizer* s7A = new wxBoxSizer(wxHORIZONTAL);
wxBitmapButton* center_vertically_button = new wxBitmapButton(this, ID_SELECTION_CENTER_VERTICALLY, wxBitmap(load_resource_image(_("shape_align_middle"))));
center_vertically_button->SetToolTip(_LABEL_("selection center vertically"));
s7A->Add(center_vertically_button);
s7A->AddStretchSpacer();
wxBitmapButton* center_horizontally_button = new wxBitmapButton(this, ID_SELECTION_CENTER_HORIZONTALLY, wxBitmap(load_resource_image(_("shape_align_center"))));
center_horizontally_button->SetToolTip(_LABEL_("selection center horizontally"));
s7A->Add(center_horizontally_button);
s7A->AddStretchSpacer();
wxBitmapButton* center_button = new wxBitmapButton(this, ID_SELECTION_CENTER, wxBitmap(load_resource_image(_("shape_align_both"))));
center_button->SetToolTip(_LABEL_("selection center both"));
s7A->Add(center_button);
s7->Add(new wxStaticText(this, wxID_ANY, _LABEL_("selection center")), 0, wxALIGN_CENTER_VERTICAL);
wxBoxSizer* s7A = new wxBoxSizer(wxHORIZONTAL);
wxBitmapButton* center_vertically_button = new wxBitmapButton(this, ID_SELECTION_CENTER_VERTICALLY, wxBitmap(load_resource_image(_("shape_align_middle"))));
center_vertically_button->SetToolTip(_LABEL_("selection center vertically"));
s7A->Add(center_vertically_button);
s7A->AddStretchSpacer();
wxBitmapButton* center_horizontally_button = new wxBitmapButton(this, ID_SELECTION_CENTER_HORIZONTALLY, wxBitmap(load_resource_image(_("shape_align_center"))));
center_horizontally_button->SetToolTip(_LABEL_("selection center horizontally"));
s7A->Add(center_horizontally_button);
s7A->AddStretchSpacer();
wxBitmapButton* center_button = new wxBitmapButton(this, ID_SELECTION_CENTER, wxBitmap(load_resource_image(_("shape_align_both"))));
center_button->SetToolTip(_LABEL_("selection center both"));
s7A->Add(center_button);
s7->Add(s7A, 1, wxEXPAND, 0);
s6->Add(s7, 1, wxEXPAND | wxALL, 4);
s5->Add(s6, 0, wxEXPAND | wxALL, 4);
@@ -206,6 +227,8 @@ ImageSliceWindow::ImageSliceWindow(Window* parent, const Image& source, const wx
sB->Add(sharpen_amount, 0, wxEXPAND | wxALL, 4);
s5->Add(sB, 0, wxEXPAND | wxALL, 4);
s5->AddStretchSpacer(1);
s5->Add(grid, 0, wxEXPAND | wxALL, 4);
s5->AddStretchSpacer(1);
s->Add(s5, 0, wxEXPAND);
s->Add(CreateButtonSizer(wxOK | wxCANCEL), 0, wxEXPAND | wxALL, 8);
s->SetSizeHints(this);
@@ -219,8 +242,11 @@ void ImageSliceWindow::onOk(wxCommandEvent&) {
EndModal(wxID_OK);
}
Image ImageSliceWindow::getImage(double scale) const {
return slice.getSlice(scale);
Image ImageSliceWindow::getImage(double scale) const {
Image img = slice.getSlice(scale);
previously_used_settings_path[slice.card_name] = slice.source_path;
previously_used_settings_value[{ slice.source_path, slice.card_name }] = { slice.selection, slice.sharpen_amount };
return img;
}
// ----------------------------------------------------------------------------- : ImageSliceWindow : Controls
@@ -249,7 +275,13 @@ void ImageSliceWindow::onChangeSize(wxCommandEvent&) {
slice.aspect_fixed = false;
onUpdateFromControl();
}
}
}
void ImageSliceWindow::onChangeGrid(wxCommandEvent&) {
if (!initialized) return;
preview->grid = grid->GetSelection();
preview->update();
}
void ImageSliceWindow::onChangeLeft(wxCommandEvent&) {
if (!initialized) return;
@@ -322,16 +354,16 @@ void ImageSliceWindow::onUpdateFromControl(PreferedProperty prefer) {
updateControls();
}
void ImageSliceWindow::onSelectionCenter(wxCommandEvent& ev) {
void ImageSliceWindow::onSelectionCenter(wxCommandEvent& ev) {
switch (ev.GetId()) {
case ID_SELECTION_CENTER:
slice.centerSelection();
break;
case ID_SELECTION_CENTER_HORIZONTALLY:
slice.centerSelectionHorizontally();
break;
case ID_SELECTION_CENTER_VERTICALLY:
slice.centerSelectionVertically();
case ID_SELECTION_CENTER:
slice.centerSelection();
break;
case ID_SELECTION_CENTER_HORIZONTALLY:
slice.centerSelectionHorizontally();
break;
case ID_SELECTION_CENTER_VERTICALLY:
slice.centerSelectionVertically();
break;
}
@@ -384,22 +416,23 @@ void ImageSliceWindow::updateControls() {
// ----------------------------------------------------------------------------- : ImageSliceWindow : Event table
BEGIN_EVENT_TABLE(ImageSliceWindow, wxDialog)
EVT_BUTTON (wxID_OK, ImageSliceWindow::onOk)
EVT_RADIOBOX (ID_SIZE, ImageSliceWindow::onChangeSize)
EVT_TEXT (ID_LEFT, ImageSliceWindow::onChangeLeft)
EVT_TEXT (ID_TOP, ImageSliceWindow::onChangeTop)
EVT_TEXT (ID_WIDTH, ImageSliceWindow::onChangeWidth)
EVT_TEXT (ID_HEIGHT, ImageSliceWindow::onChangeHeight)
EVT_BUTTON (ID_SELECTION_CENTER, ImageSliceWindow::onSelectionCenter)
EVT_BUTTON(ID_SELECTION_CENTER_HORIZONTALLY, ImageSliceWindow::onSelectionCenter)
EVT_BUTTON(ID_SELECTION_CENTER_VERTICALLY, ImageSliceWindow::onSelectionCenter)
EVT_CHECKBOX (ID_FIX_ASPECT, ImageSliceWindow::onChangeFixAspect)
EVT_SPINCTRL (ID_ZOOM, ImageSliceWindow::onChangeZoom)
EVT_SPINCTRL (ID_ZOOM_X, ImageSliceWindow::onChangeZoomX)
EVT_SPINCTRL (ID_ZOOM_Y, ImageSliceWindow::onChangeZoomY)
EVT_CHECKBOX (ID_SHARPEN, ImageSliceWindow::onChangeSharpen)
EVT_COMMAND_SCROLL (ID_SHARPEN_AMOUNT, ImageSliceWindow::onChangeSharpenAmount)
EVT_SLICE_CHANGED (wxID_ANY, ImageSliceWindow::onSliceChange)
EVT_BUTTON (wxID_OK, ImageSliceWindow::onOk)
EVT_RADIOBOX (ID_SIZE, ImageSliceWindow::onChangeSize)
EVT_RADIOBOX (ID_GRID, ImageSliceWindow::onChangeGrid)
EVT_TEXT (ID_LEFT, ImageSliceWindow::onChangeLeft)
EVT_TEXT (ID_TOP, ImageSliceWindow::onChangeTop)
EVT_TEXT (ID_WIDTH, ImageSliceWindow::onChangeWidth)
EVT_TEXT (ID_HEIGHT, ImageSliceWindow::onChangeHeight)
EVT_BUTTON (ID_SELECTION_CENTER, ImageSliceWindow::onSelectionCenter)
EVT_BUTTON (ID_SELECTION_CENTER_HORIZONTALLY, ImageSliceWindow::onSelectionCenter)
EVT_BUTTON (ID_SELECTION_CENTER_VERTICALLY, ImageSliceWindow::onSelectionCenter)
EVT_CHECKBOX (ID_FIX_ASPECT, ImageSliceWindow::onChangeFixAspect)
EVT_SPINCTRL (ID_ZOOM, ImageSliceWindow::onChangeZoom)
EVT_SPINCTRL (ID_ZOOM_X, ImageSliceWindow::onChangeZoomX)
EVT_SPINCTRL (ID_ZOOM_Y, ImageSliceWindow::onChangeZoomY)
EVT_CHECKBOX (ID_SHARPEN, ImageSliceWindow::onChangeSharpen)
EVT_COMMAND_SCROLL (ID_SHARPEN_AMOUNT, ImageSliceWindow::onChangeSharpenAmount)
EVT_SLICE_CHANGED (wxID_ANY, ImageSliceWindow::onSliceChange)
// EVT_SIZE ( ImageSliceWindow::onSize)
END_EVENT_TABLE ()
@@ -409,10 +442,11 @@ END_EVENT_TABLE ()
// ----------------------------------------------------------------------------- : ImageSlicePreview
ImageSlicePreview::ImageSlicePreview(Window* parent, int id, ImageSlice& slice, const AlphaMask& mask)
ImageSlicePreview::ImageSlicePreview(Window* parent, int id, ImageSlice& slice, const AlphaMask& mask, const int grid)
: wxControl(parent, id, wxDefaultPosition, wxDefaultSize, wxBORDER_THEME)
, slice(slice)
, mask(mask)
, grid(grid)
, mouse_down(false)
{
SetBackgroundStyle(wxBG_STYLE_PAINT);
@@ -421,22 +455,22 @@ ImageSlicePreview::ImageSlicePreview(Window* parent, int id, ImageSlice& slice,
void ImageSlicePreview::update() {
bitmap = wxNullBitmap;
Refresh(false);
}
}
wxSize ImageSlicePreview::getBestSliceSize() const {
float target_ratio = ((float)slice.target_size.GetWidth()) / ((float)slice.target_size.GetHeight());
if (target_ratio > 1.0) {
return wxSize(500, 500 / target_ratio);
} else {
return wxSize(500 * target_ratio, 500);
if (target_ratio > 1.0) {
return wxSize(500, 500 / target_ratio);
} else {
return wxSize(500 * target_ratio, 500);
}
}
wxSize ImageSlicePreview::DoGetBestSize() const {
// We know the client size we want, calculate the size that goes with that
// We know the client size we want, calculate the size that goes with that
// This helps with applying margins and other spacing necessities.
wxSize ws = GetSize(), cs = GetClientSize();
wxSize ws = GetSize(), cs = GetClientSize();
return getBestSliceSize() + ws - cs;
}
@@ -461,14 +495,47 @@ void ImageSlicePreview::draw(DC& dc) {
mdc.SelectObject(wxNullBitmap);
} else {
bitmap = Bitmap(image);
}
// Rescale the bitmap based on the available size.
auto available_size = getBestSliceSize();
}
// Rescale the bitmap based on the available size.
auto available_size = getBestSliceSize();
bitmap = wxBitmap(bitmap.ConvertToImage().Scale(available_size.GetWidth(), available_size.GetHeight()));
}
if (bitmap.Ok()) {
dc.DrawBitmap(bitmap, 0, 0);
dc.DrawBitmap(bitmap, 0, 0);
if (grid == 1) {
wxSize size = dc.GetSize();
dc.SetPen(*wxRED_PEN);
dc.DrawLine(size.x * 1 / 2, 0, size.x * 1 / 2, size.y);
dc.DrawLine(0, size.y * 1 / 2, size.x, size.y * 1 / 2);
} else if (grid == 2) {
wxSize size = dc.GetSize();
dc.SetPen(*wxRED_PEN);
dc.DrawLine(size.x * 1 / 3, 0, size.x * 1 / 3, size.y);
dc.DrawLine(size.x * 2 / 3, 0, size.x * 2 / 3, size.y);
dc.DrawLine(0, size.y * 1 / 3, size.x, size.y * 1 / 3);
dc.DrawLine(0, size.y * 2 / 3, size.x, size.y * 2 / 3);
} else if (grid == 3) {
wxSize size = dc.GetSize();
dc.SetPen(*wxRED_PEN);
dc.DrawLine(size.x * 1 / 4, 0, size.x * 1 / 4, size.y);
dc.DrawLine(size.x * 2 / 4, 0, size.x * 2 / 4, size.y);
dc.DrawLine(size.x * 3 / 4, 0, size.x * 3 / 4, size.y);
dc.DrawLine(0, size.y * 1 / 4, size.x, size.y * 1 / 4);
dc.DrawLine(0, size.y * 2 / 4, size.x, size.y * 2 / 4);
dc.DrawLine(0, size.y * 3 / 4, size.x, size.y * 3 / 4);
} else if (grid == 4) {
wxSize size = dc.GetSize();
dc.SetPen(*wxRED_PEN);
dc.DrawLine(size.x * 1 / 5, 0, size.x * 1 / 5, size.y);
dc.DrawLine(size.x * 2 / 5, 0, size.x * 2 / 5, size.y);
dc.DrawLine(size.x * 3 / 5, 0, size.x * 3 / 5, size.y);
dc.DrawLine(size.x * 4 / 5, 0, size.x * 4 / 5, size.y);
dc.DrawLine(0, size.y * 1 / 5, size.x, size.y * 1 / 5);
dc.DrawLine(0, size.y * 2 / 5, size.x, size.y * 2 / 5);
dc.DrawLine(0, size.y * 3 / 5, size.x, size.y * 3 / 5);
dc.DrawLine(0, size.y * 4 / 5, size.x, size.y * 4 / 5);
}
}
}
@@ -519,9 +586,9 @@ ImageSliceSelector::ImageSliceSelector(Window* parent, int id, ImageSlice& slice
: wxControl(parent, id, wxDefaultPosition, wxDefaultSize, wxBORDER_THEME)
, slice(slice)
, mouse_down(false)
{
float target_ratio = ((float) slice.source.GetWidth()) / ((float) slice.source.GetHeight());
{
float target_ratio = ((float) slice.source.GetWidth()) / ((float) slice.source.GetHeight());
if (target_ratio > 1.0) {
SetMinSize(wxSize(500, 500 / target_ratio));
} else {
+29 -18
View File
@@ -28,23 +28,26 @@ enum PreferedProperty
/// A slice of an image, i.e. a selected rectangle
class ImageSlice {
public:
ImageSlice(const Image& source, const wxSize& target_size);
ImageSlice(const Image& source, const String& source_path, const String& card_name, const wxSize& target_size);
Image source; ///< The source image
wxSize target_size; ///< Size of the target image
Color background; ///< Color for areas outside the source image
wxRect selection; ///< Area to slect from source
bool allow_outside;
bool aspect_fixed; ///< Aspect ratio lock?
Image source; ///< The source image
String source_path; ///< The filename of the source image (only used to find previously used settings)
String card_name; ///< The identification of the card we're on (only used to find previously used settings)
wxSize target_size; ///< Size of the target image
wxRect selection; ///< Area to slice from source
Color background; ///< Color for areas outside the source image
bool allow_outside; ///< Allow the slice to extend outside the source image?
bool aspect_fixed; ///< Aspect ratio lock?
// Filters
bool sharpen;
int sharpen_amount;
bool sharpen;
int sharpen_amount;
/// Enforce relations between values
void constrain(PreferedProperty prefer = PREFER_NONE);
/// Attempt to center the current constraints
void centerSelection();
void centerSelectionHorizontally();
void centerSelection();
void centerSelectionHorizontally();
void centerSelectionVertically();
/// Get the sliced image
Image getSlice(double scale = 1.0) const;
@@ -62,11 +65,16 @@ public:
/// Dialog for selecting a slice of an image
class ImageSliceWindow : public wxDialog {
public:
ImageSliceWindow(Window* parent, const Image& source, const wxSize& target_size, const AlphaMask& target_mask);
ImageSliceWindow(Window* parent, const Image& source, const String& filename, const String& cardname, const wxSize& target_size, const AlphaMask& target_mask);
/// Return the sliced image
Image getImage(double scale) const;
// --------------------------------------------------- : Previously Used Settings
static map<String, String> previously_used_settings_path; // map from cardname to filename
static map<pair<String, String>, pair<wxRect, int>> previously_used_settings_value; // map from filename+cardname pair to settings
// --------------------------------------------------- : Data
private:
// The slice we are extracting
@@ -74,7 +82,7 @@ private:
// Gui items
ImageSlicePreview* preview;
ImageSliceSelector* selector;
wxRadioBox* size;
wxRadioBox* size, *grid;
wxSpinCtrl* top, *left, *width, *height;
wxCheckBox* fix_aspect;
wxSpinCtrl* zoom, *zoom_x, *zoom_y;
@@ -91,11 +99,12 @@ private:
void onSize (wxSizeEvent&);
void onChangeSize (wxCommandEvent&);
void onChangeGrid (wxCommandEvent&);
void onChangeLeft (wxCommandEvent&);
void onChangeTop (wxCommandEvent&);
void onChangeWidth (wxCommandEvent&);
void onChangeHeight (wxCommandEvent&);
void onSelectionCenter(wxCommandEvent&);
void onSelectionCenter (wxCommandEvent&);
void onChangeFixAspect (wxCommandEvent&);
void onChangeZoom (wxSpinEvent&);
void onChangeZoomX (wxSpinEvent&);
@@ -120,11 +129,13 @@ private:
/// A preview of the sliced image
class ImageSlicePreview : public wxControl {
public:
ImageSlicePreview(Window* parent, int id, ImageSlice& slice, const AlphaMask& mask);
ImageSlicePreview(Window* parent, int id, ImageSlice& slice, const AlphaMask& mask, const int grid);
/// Notify that the slice was updated
void update();
int grid;
// --------------------------------------------------- : Data
private:
Bitmap bitmap;
@@ -137,7 +148,7 @@ private:
// --------------------------------------------------- : Events
DECLARE_EVENT_TABLE();
wxSize getBestSliceSize() const;
wxSize DoGetBestSize() const override;
+7 -7
View File
@@ -182,14 +182,14 @@ SelectStyleSheetWindow::SelectStyleSheetWindow(Window* parent, const Game& game,
// init controls
stylesheet_list = new PackageList (this, ID_STYLESHEET_LIST);
wxStaticText* description = new wxStaticText(this, ID_GAME_LIST, _LABEL_1_("stylesheet not found", failed_name));
wxStaticText* stylesheet_text = new wxStaticText(this, ID_STYLESHEET_LIST, _LABEL_("style type"));
wxStaticText* stylesheet_text = new wxStaticText(this, ID_STYLESHEET_LIST, _LABEL_("style type"));
stylesheet_filter = new FilterCtrl(this, ID_STYLESHEET_FILTER, _LABEL_("search stylesheet list"), _HELP_("search stylesheet list control"));
stylesheet_filter->setFilter(stylesheet_filter_value);
stylesheet_filter->setFilter(stylesheet_filter_value);
// init sizer
wxSizer* s = new wxBoxSizer(wxVERTICAL);
s->Add(description, 0, wxALL, 4);
s->Add(description, 0, wxALL, 4);
wxSizer* s2 = new wxBoxSizer(wxHORIZONTAL);
s2->Add(stylesheet_text, 0, wxALL & ~wxLEFT, 4);
s2->AddStretchSpacer();
@@ -215,8 +215,8 @@ void SelectStyleSheetWindow::onStyleSheetSelect(wxCommandEvent&) {
}
void SelectStyleSheetWindow::onStyleSheetActivate(wxCommandEvent&) {
done();
}
}
void SelectStyleSheetWindow::onStylesheetFilterUpdate(wxCommandEvent&) {
if (stylesheet_list->hasSelection()) {
StyleSheetP existingStylesheetSelection = stylesheet_list->getSelection<StyleSheet>(false);
@@ -257,7 +257,7 @@ void SelectStyleSheetWindow::onIdle(wxIdleEvent& ev) {
BEGIN_EVENT_TABLE(SelectStyleSheetWindow, wxDialog)
EVT_GALLERY_SELECT (ID_STYLESHEET_LIST, SelectStyleSheetWindow::onStyleSheetSelect)
EVT_GALLERY_ACTIVATE(ID_STYLESHEET_LIST, SelectStyleSheetWindow::onStyleSheetActivate)
EVT_GALLERY_ACTIVATE(ID_STYLESHEET_LIST, SelectStyleSheetWindow::onStyleSheetActivate)
EVT_COMMAND_RANGE(ID_STYLESHEET_FILTER, ID_STYLESHEET_FILTER, wxEVT_COMMAND_TEXT_UPDATED, SelectStyleSheetWindow::onStylesheetFilterUpdate)
EVT_BUTTON (wxID_OK, SelectStyleSheetWindow::OnOK)
EVT_UPDATE_UI (wxID_ANY, SelectStyleSheetWindow::onUpdateUI)
+3 -3
View File
@@ -81,15 +81,15 @@ private:
const Game& game;
// gui items
PackageList* stylesheet_list;
PackageList* stylesheet_list;
FilterCtrl* stylesheet_filter;
String stylesheet_filter_value;
// --------------------------------------------------- : events
void onStyleSheetSelect (wxCommandEvent&);
void onStyleSheetActivate(wxCommandEvent&);
void onStyleSheetActivate(wxCommandEvent&);
void onStylesheetFilterUpdate(wxCommandEvent&);
virtual void OnOK(wxCommandEvent&);
+8 -8
View File
@@ -214,9 +214,9 @@ DisplayPreferencesPage::DisplayPreferencesPage(Window* parent)
borders = new wxCheckBox(this, wxID_ANY, _BUTTON_("show lines"));
draw_editing = new wxCheckBox(this, wxID_ANY, _BUTTON_("show editing hints"));
spellcheck_enabled = new wxCheckBox(this, wxID_ANY, _BUTTON_("spellcheck enabled"));
non_normal_export = new wxCheckBox(this, wxID_ANY, _BUTTON_("zoom export"));
zoom = new wxComboBox(this, ID_ZOOM);
export_zoom = new wxComboBox(this, ID_EXPORT_ZOOM);
non_normal_export = new wxCheckBox(this, wxID_ANY, _BUTTON_("zoom export"));
zoom = new wxComboBox(this, ID_ZOOM);
export_zoom = new wxComboBox(this, ID_EXPORT_ZOOM);
//wxButton* columns = new wxButton(this, ID_SELECT_COLUMNS, _BUTTON_("select"));
// set values
@@ -227,14 +227,14 @@ DisplayPreferencesPage::DisplayPreferencesPage(Window* parent)
non_normal_export->SetValue(!settings.default_stylesheet_settings.card_normal_export());
zoom_int = static_cast<int>(settings.default_stylesheet_settings.card_zoom() * 100);
zoom->SetValue(String::Format(_("%d%%"),zoom_int));
int choices[] = { 50,66,75,100,120,150,175,200 };
for (unsigned int i = 0 ; i < sizeof(choices)/sizeof(choices[0]) ; ++i) {
zoom->Append(String::Format(_("%d%%"),choices[i]));
int zoom_choices[] = { 50,66,75,80,100,120,125,150,175,200 };
for (unsigned int i = 0 ; i < sizeof(zoom_choices)/sizeof(zoom_choices[0]) ; ++i) {
zoom->Append(String::Format(_("%d%%"), zoom_choices[i]));
}
export_zoom_int = static_cast<int>(settings.default_stylesheet_settings.export_zoom() * 100);
export_zoom->SetValue(String::Format(_("%d%%"), export_zoom_int));
int export_choices[] = { 50,66,75,100,120,150,175,200 };
int export_choices[] = { 50,66,75,80,100,120,125,150,175,200 };
for (unsigned int i = 0; i < sizeof(export_choices) / sizeof(export_choices[0]); ++i) {
export_zoom->Append(String::Format(_("%d%%"), export_choices[i]));
}
@@ -328,7 +328,7 @@ InternalPreferencesPage::InternalPreferencesPage(Window* parent) : PreferencesPa
internal_scale_int = static_cast<int>(settings.internal_scale * 100);
internal_scale->SetValue(String::Format(_("%d%%"), internal_scale_int));
int choices[] = { 100,200,300,400 };
int choices[] = { 100,120,125,150,175,200 };
for (unsigned int i = 0; i < sizeof(choices) / sizeof(choices[0]); ++i) {
internal_scale->Append(String::Format(_("%d%%"), choices[i]));
}
+228 -228
View File
@@ -14,150 +14,150 @@
#include <data/card.hpp>
#include <data/stylesheet.hpp>
#include <render/card/viewer.hpp>
#include <wx/print.h>
#include <wx/valnum.h>
#include <unordered_set>
#include <wx/print.h>
#include <wx/valnum.h>
#include <unordered_set>
DECLARE_POINTER_TYPE(PageLayout);
// ----------------------------------------------------------------------------- : Layout
void PrintJob::init(const RealSize& page_size) {
this->page_size = page_size;
if (cards.empty()) return;
measure_cards();
layout_cards();
align_cards();
this->page_size = page_size;
if (cards.empty()) return;
measure_cards();
layout_cards();
align_cards();
center_cards();
}
void PrintJob::measure_cards() {
FOR_EACH(card, cards) {
const StyleSheet& stylesheet = set->stylesheetFor(card);
RealSize size_px(stylesheet.card_width, stylesheet.card_height);
RealSize size_mm(stylesheet.card_width * 25.4 / stylesheet.card_dpi, stylesheet.card_height * 25.4 / stylesheet.card_dpi);
Radians rotation = 0.0;
bool rotated = abs(size_mm.width - default_size_mm.height) < abs(size_mm.height - default_size_mm.height); // try to align best to default card height
if (rotated) {
swap(size_mm.width, size_mm.height);
swap(size_px.width, size_px.height);
rotation = rad90;
}
if (abs(size_mm.width - default_size_mm.width) < threshold_size.width) size_mm.width = default_size_mm.width; // snap to default_size_mm if we are close
if (abs(size_mm.height - default_size_mm.height) < threshold_size.height) size_mm.height = default_size_mm.height;
CardLayout layout(card, size_mm, size_px, rotation);
card_layouts.push_back(layout);
}
void PrintJob::measure_cards() {
FOR_EACH(card, cards) {
const StyleSheet& stylesheet = set->stylesheetFor(card);
RealSize size_px(stylesheet.card_width, stylesheet.card_height);
RealSize size_mm(stylesheet.card_width * 25.4 / stylesheet.card_dpi, stylesheet.card_height * 25.4 / stylesheet.card_dpi);
Radians rotation = 0.0;
bool rotated = abs(size_mm.width - default_size_mm.height) < abs(size_mm.height - default_size_mm.height); // try to align best to default card height
if (rotated) {
swap(size_mm.width, size_mm.height);
swap(size_px.width, size_px.height);
rotation = rad90;
}
if (abs(size_mm.width - default_size_mm.width) < threshold_size.width) size_mm.width = default_size_mm.width; // snap to default_size_mm if we are close
if (abs(size_mm.height - default_size_mm.height) < threshold_size.height) size_mm.height = default_size_mm.height;
CardLayout layout(card, size_mm, size_px, rotation);
card_layouts.push_back(layout);
}
std::sort(card_layouts.begin(), card_layouts.end());
}
void PrintJob::layout_cards() {
page_layouts.push_back(vector<CardLayout>());
double row_top = 0.0, row_height = 0.0, row_width = 0.0;
unordered_set<int> already_laidout_cards;
while (true) {
// try to find a card that will fit on the current row
void PrintJob::layout_cards() {
page_layouts.push_back(vector<CardLayout>());
double row_top = 0.0, row_height = 0.0, row_width = 0.0;
unordered_set<int> already_laidout_cards;
while (true) {
// try to find a card that will fit on the current row
for (int i = 0; i < card_layouts.size(); ++i) {
if (already_laidout_cards.find(i) != already_laidout_cards.end()) continue;
if (card_layouts[i].size_mm.width + row_width >= page_size.width) continue;
if (card_layouts[i].size_mm.height + row_top >= page_size.height) continue;
// the card fits
card_layouts[i].pos.width = row_width;
card_layouts[i].pos.height = row_top;
page_layouts[page_layouts.size()-1].push_back(card_layouts[i]);
already_laidout_cards.insert(i);
if (already_laidout_cards.size() == card_layouts.size()) return;
// move to next spot on the row
row_width += card_layouts[i].size_mm.width + settings.print_spacing;
row_height = max(row_height, card_layouts[i].size_mm.height + settings.print_spacing);
if (already_laidout_cards.find(i) != already_laidout_cards.end()) continue;
if (card_layouts[i].size_mm.width + row_width >= page_size.width) continue;
if (card_layouts[i].size_mm.height + row_top >= page_size.height) continue;
// the card fits
card_layouts[i].pos.width = row_width;
card_layouts[i].pos.height = row_top;
page_layouts[page_layouts.size()-1].push_back(card_layouts[i]);
already_laidout_cards.insert(i);
if (already_laidout_cards.size() == card_layouts.size()) return;
// move to next spot on the row
row_width += card_layouts[i].size_mm.width + settings.print_spacing;
row_height = max(row_height, card_layouts[i].size_mm.height + settings.print_spacing);
goto continue_outer;
}
// no card fits
if (row_top == 0.0 && row_height == 0.0 && row_width == 0.0) {
// none of the remaining cards can fit on an empty page, return
page_layouts.pop_back();
queue_message(MESSAGE_WARNING, _ERROR_("cards bigger than page"));
return;
}
if (row_height == 0.0 && row_width == 0.0) {
// none of the remaining cards can fit on an empty row, create a new page
page_layouts.push_back(vector<CardLayout>());
row_top = 0.0;
continue;
}
// none of the remaining cards can fit on this row, create a new row
row_top += row_height;
row_width = row_height = 0.0;
}
// no card fits
if (row_top == 0.0 && row_height == 0.0 && row_width == 0.0) {
// none of the remaining cards can fit on an empty page, return
page_layouts.pop_back();
queue_message(MESSAGE_WARNING, _ERROR_("cards bigger than page"));
return;
}
if (row_height == 0.0 && row_width == 0.0) {
// none of the remaining cards can fit on an empty row, create a new page
page_layouts.push_back(vector<CardLayout>());
row_top = 0.0;
continue;
}
// none of the remaining cards can fit on this row, create a new row
row_top += row_height;
row_width = row_height = 0.0;
continue_outer:;
}
}
void PrintJob::align_cards() {
// for each page
for (int p = 0; p < page_layouts.size(); ++p) {
vector<CardLayout>& page_layout = page_layouts[p];
// for each card on the page
for (int max = 0, j = 0; j < page_layout.size(); ++max, ++j) {
if (max > 100) {
queue_message(MESSAGE_WARNING, _("DEBUG: large amount of iterations when aligning cards for print"));
break;
}
double x = page_layout[j].pos.width;
double y = page_layout[j].pos.height;
// if another card is almost aligned
for (int i = 0; i < page_layout.size(); ++i) {
if (i == j) continue;
double difference = page_layout[i].pos.width - x;
if (threshold_bottom < difference && difference <= threshold_top) {
// get the card, and all cards to the right on the same row
vector<int> cards;
cards.push_back(j);
for (int h = 0; h < page_layout.size(); ++h) {
if (h == j) continue;
double difference_x = page_layout[h].pos.width - x;
double difference_y = abs(page_layout[h].pos.height - y);
if (difference_y < threshold_bottom && difference_x > threshold_bottom) {
cards.push_back(h);
}
}
// check if all these cards can be moved to the right
bool can_move = true;
for (int h = 0; h < cards.size(); ++h) {
if (page_layout[cards[h]].pos.width + page_layout[cards[h]].size_mm.width + difference > page_size.width) {
can_move = false;
break;
}
}
// move the cards
if (can_move) {
for (int h = 0; h < cards.size(); ++h) {
page_layout[cards[h]].pos.width += difference;
}
j = -1; // restart, new cards may be in range now
goto continue_outer;
}
}
}
continue_outer:;
}
}
}
void PrintJob::center_cards() {
for (int p = 0; p < page_layouts.size(); ++p) {
vector<CardLayout>& page_layout = page_layouts[p];
RealSize page_margin(0.0, 0.0);
for (int i = 0; i < page_layout.size(); ++i) {
double width = page_layout[i].pos.width + page_layout[i].size_mm.width;
double height = page_layout[i].pos.height + page_layout[i].size_mm.height;
if (page_margin.width < width) page_margin.width = width;
if (page_margin.height < height) page_margin.height = height;
}
page_margin.width = (page_size.width - page_margin.width) / 2;
page_margin.height = (page_size.height - page_margin.height) / 2;
page_margins.push_back(page_margin);
for (int i = 0; i < page_layout.size(); ++i) {
page_layout[i].pos.width += page_margin.width;
page_layout[i].pos.height += page_margin.height;
void PrintJob::align_cards() {
// for each page
for (int p = 0; p < page_layouts.size(); ++p) {
vector<CardLayout>& page_layout = page_layouts[p];
// for each card on the page
for (int max = 0, j = 0; j < page_layout.size(); ++max, ++j) {
if (max > 100) {
queue_message(MESSAGE_WARNING, _("DEBUG: large amount of iterations when aligning cards for print"));
break;
}
double x = page_layout[j].pos.width;
double y = page_layout[j].pos.height;
// if another card is almost aligned
for (int i = 0; i < page_layout.size(); ++i) {
if (i == j) continue;
double difference = page_layout[i].pos.width - x;
if (threshold_bottom < difference && difference <= threshold_top) {
// get the card, and all cards to the right on the same row
vector<int> cards;
cards.push_back(j);
for (int h = 0; h < page_layout.size(); ++h) {
if (h == j) continue;
double difference_x = page_layout[h].pos.width - x;
double difference_y = abs(page_layout[h].pos.height - y);
if (difference_y < threshold_bottom && difference_x > threshold_bottom) {
cards.push_back(h);
}
}
// check if all these cards can be moved to the right
bool can_move = true;
for (int h = 0; h < cards.size(); ++h) {
if (page_layout[cards[h]].pos.width + page_layout[cards[h]].size_mm.width + difference > page_size.width) {
can_move = false;
break;
}
}
// move the cards
if (can_move) {
for (int h = 0; h < cards.size(); ++h) {
page_layout[cards[h]].pos.width += difference;
}
j = -1; // restart, new cards may be in range now
goto continue_outer;
}
}
}
continue_outer:;
}
}
}
}
void PrintJob::center_cards() {
for (int p = 0; p < page_layouts.size(); ++p) {
vector<CardLayout>& page_layout = page_layouts[p];
RealSize page_margin(0.0, 0.0);
for (int i = 0; i < page_layout.size(); ++i) {
double width = page_layout[i].pos.width + page_layout[i].size_mm.width;
double height = page_layout[i].pos.height + page_layout[i].size_mm.height;
if (page_margin.width < width) page_margin.width = width;
if (page_margin.height < height) page_margin.height = height;
}
page_margin.width = (page_size.width - page_margin.width) / 2;
page_margin.height = (page_size.height - page_margin.height) / 2;
page_margins.push_back(page_margin);
for (int i = 0; i < page_layout.size(); ++i) {
page_layout[i].pos.width += page_margin.width;
page_layout[i].pos.height += page_margin.height;
}
}
}
// ----------------------------------------------------------------------------- : Printout
@@ -182,11 +182,11 @@ private:
int pageCount() {
return job->page_layouts.size();
}
/// Draw cutter lines in the page margins, corresponding to the edges of cards
/// Draw cutter lines in the page margins, corresponding to the edges of cards
void drawCutterLines(DC& dc, PrintJobP& job, int page);
/// Draw cards according to their CardLayout info
void drawCards (DC& dc, PrintJobP& job, int page);
void drawCards (DC& dc, PrintJobP& job, int page);
void drawCard (DC& dc, PrintJob::CardLayout& card_layout);
};
@@ -224,19 +224,19 @@ bool CardsPrintout::OnPrintPage(int page) {
dc.GetSize(&page_width_px, &page_height_px);
// scale factor (pixels per mm)
printer_px_per_mm = RealSize((double)page_width_px / page_width_mm, (double)page_height_px / page_height_mm);
// print the cards that belong on this page
drawCards(dc, job, page);
// print the cards that belong on this page
drawCards(dc, job, page);
if (settings.print_cutter_lines != CUTTER_NONE) drawCutterLines(dc, job, page);
return true;
}
void CardsPrintout::drawCards(DC& dc, PrintJobP& job, int page) {
FOR_EACH(card_layout, job->page_layouts[page - 1]) {
drawCard(dc, card_layout);
}
dc.SetUserScale(printer_px_per_mm.width, printer_px_per_mm.height);
}
void CardsPrintout::drawCard(DC& dc, PrintJob::CardLayout& card_layout) {
void CardsPrintout::drawCards(DC& dc, PrintJobP& job, int page) {
FOR_EACH(card_layout, job->page_layouts[page - 1]) {
drawCard(dc, card_layout);
}
dc.SetUserScale(printer_px_per_mm.width, printer_px_per_mm.height);
}
void CardsPrintout::drawCard(DC& dc, PrintJob::CardLayout& card_layout) {
// draw card to its own buffer
wxBitmap buffer(card_layout.size_px.width, card_layout.size_px.height, 32);
wxMemoryDC bufferDC;
@@ -250,100 +250,100 @@ void CardsPrintout::drawCard(DC& dc, PrintJob::CardLayout& card_layout) {
dc.SetUserScale(printer_px_per_mm.width / card_layout.px_per_mm.width, printer_px_per_mm.height / card_layout.px_per_mm.height);
dc.DrawBitmap(buffer, int(card_layout.pos.width * card_layout.px_per_mm.width), int(card_layout.pos.height * card_layout.px_per_mm.height));
}
void CardsPrintout::drawCutterLines(DC& dc, PrintJobP& job, int page) {
const vector<PrintJob::CardLayout>& page_layout = job->page_layouts[page - 1];
void CardsPrintout::drawCutterLines(DC& dc, PrintJobP& job, int page) {
const vector<PrintJob::CardLayout>& page_layout = job->page_layouts[page - 1];
const RealSize& page_margin = job->page_margins[page - 1];
int page_width, page_height;
GetPageSizeMM(&page_width, &page_height);
double vertical_line_size = min(10.0, page_margin.height - 3.0);
if (vertical_line_size > 0.0) {
for (int i = 0; i < page_layout.size(); ++i) {
double left_line = page_layout[i].pos.width;
double right_line = left_line + page_layout[i].size_mm.width;
bool draw_left_line = true;
bool draw_right_line = true;
if (settings.print_cutter_lines == CUTTER_NO_INTERSECTION) {
// check if another card is in the way of this cutter line
for (int j = 0; j < page_layout.size(); ++j) {
if (i == j) continue;
double other_left_line = page_layout[j].pos.width;
double other_right_line = other_left_line + page_layout[j].size_mm.width;
if (draw_left_line && left_line - other_left_line > job->threshold_bottom && other_right_line - left_line > job->threshold_bottom) {
draw_left_line = false;
if (!draw_right_line) break;
}
if (draw_right_line && right_line - other_left_line > job->threshold_bottom && other_right_line - right_line > job->threshold_bottom) {
draw_right_line = false;
if (!draw_left_line) break;
}
}
}
const RealSize& px_per_mm = page_layout[i].px_per_mm;
dc.SetUserScale(printer_px_per_mm.width / px_per_mm.width, printer_px_per_mm.height / px_per_mm.height);
if (draw_left_line) {
dc.DrawLine(wxPoint(px_per_mm.width * left_line, 0.0), wxPoint(px_per_mm.width * left_line, px_per_mm.height * vertical_line_size));
dc.DrawLine(wxPoint(px_per_mm.width * left_line, px_per_mm.height * page_height), wxPoint(px_per_mm.width * left_line, px_per_mm.height * (page_height - vertical_line_size)));
}
if (draw_right_line) {
dc.DrawLine(wxPoint(px_per_mm.width * right_line, 0.0), wxPoint(px_per_mm.width * right_line, px_per_mm.height * vertical_line_size));
dc.DrawLine(wxPoint(px_per_mm.width * right_line, px_per_mm.height * page_height), wxPoint(px_per_mm.width * right_line, px_per_mm.height * (page_height - vertical_line_size)));
}
}
} else {
queue_message(MESSAGE_WARNING, _ERROR_("v margin too small for cutter"));
}
double horizontal_line_size = min(10.0, page_margin.width - 3.0);
if (horizontal_line_size > 0.0) {
for (int i = 0; i < page_layout.size(); ++i) {
double top_line = page_layout[i].pos.height;
double bottom_line = top_line + page_layout[i].size_mm.height;
bool draw_top_line = true;
bool draw_bottom_line = true;
if (settings.print_cutter_lines == CUTTER_NO_INTERSECTION) {
// check if another card is in the way of this cutter line
for (int j = 0; j < page_layout.size(); ++j) {
if (i == j) continue;
double other_top_line = page_layout[j].pos.height;
double other_bottom_line = other_top_line + page_layout[j].size_mm.height;
if (draw_top_line && top_line - other_top_line > job->threshold_bottom && other_bottom_line - top_line > job->threshold_bottom) {
draw_top_line = false;
if (!draw_bottom_line) break;
}
if (draw_bottom_line && bottom_line - other_top_line > job->threshold_bottom && other_bottom_line - bottom_line > job->threshold_bottom) {
draw_bottom_line = false;
if (!draw_top_line) break;
}
}
}
const RealSize& px_per_mm = page_layout[i].px_per_mm;
dc.SetUserScale(printer_px_per_mm.width / px_per_mm.width, printer_px_per_mm.height / px_per_mm.height);
if (draw_top_line) {
dc.DrawLine(wxPoint(0.0, px_per_mm.height * top_line), wxPoint(px_per_mm.width * horizontal_line_size, px_per_mm.height * top_line));
dc.DrawLine(wxPoint(px_per_mm.width * page_width, px_per_mm.height * top_line), wxPoint(px_per_mm.width * (page_width - horizontal_line_size), px_per_mm.height * top_line));
}
if (draw_bottom_line) {
dc.DrawLine(wxPoint(0.0, px_per_mm.height * bottom_line), wxPoint(px_per_mm.width * horizontal_line_size, px_per_mm.height * bottom_line));
dc.DrawLine(wxPoint(px_per_mm.width * page_width, px_per_mm.height * bottom_line), wxPoint(px_per_mm.width * (page_width - horizontal_line_size), px_per_mm.height * bottom_line));
}
}
} else {
queue_message(MESSAGE_WARNING, _ERROR_("h margin too small for cutter"));
}
dc.SetUserScale(printer_px_per_mm.width, printer_px_per_mm.height);
}
GetPageSizeMM(&page_width, &page_height);
double vertical_line_size = min(10.0, page_margin.height - 3.0);
if (vertical_line_size > 0.0) {
for (int i = 0; i < page_layout.size(); ++i) {
double left_line = page_layout[i].pos.width;
double right_line = left_line + page_layout[i].size_mm.width;
bool draw_left_line = true;
bool draw_right_line = true;
if (settings.print_cutter_lines == CUTTER_NO_INTERSECTION) {
// check if another card is in the way of this cutter line
for (int j = 0; j < page_layout.size(); ++j) {
if (i == j) continue;
double other_left_line = page_layout[j].pos.width;
double other_right_line = other_left_line + page_layout[j].size_mm.width;
if (draw_left_line && left_line - other_left_line > job->threshold_bottom && other_right_line - left_line > job->threshold_bottom) {
draw_left_line = false;
if (!draw_right_line) break;
}
if (draw_right_line && right_line - other_left_line > job->threshold_bottom && other_right_line - right_line > job->threshold_bottom) {
draw_right_line = false;
if (!draw_left_line) break;
}
}
}
const RealSize& px_per_mm = page_layout[i].px_per_mm;
dc.SetUserScale(printer_px_per_mm.width / px_per_mm.width, printer_px_per_mm.height / px_per_mm.height);
if (draw_left_line) {
dc.DrawLine(wxPoint(px_per_mm.width * left_line, 0.0), wxPoint(px_per_mm.width * left_line, px_per_mm.height * vertical_line_size));
dc.DrawLine(wxPoint(px_per_mm.width * left_line, px_per_mm.height * page_height), wxPoint(px_per_mm.width * left_line, px_per_mm.height * (page_height - vertical_line_size)));
}
if (draw_right_line) {
dc.DrawLine(wxPoint(px_per_mm.width * right_line, 0.0), wxPoint(px_per_mm.width * right_line, px_per_mm.height * vertical_line_size));
dc.DrawLine(wxPoint(px_per_mm.width * right_line, px_per_mm.height * page_height), wxPoint(px_per_mm.width * right_line, px_per_mm.height * (page_height - vertical_line_size)));
}
}
} else {
queue_message(MESSAGE_WARNING, _ERROR_("v margin too small for cutter"));
}
double horizontal_line_size = min(10.0, page_margin.width - 3.0);
if (horizontal_line_size > 0.0) {
for (int i = 0; i < page_layout.size(); ++i) {
double top_line = page_layout[i].pos.height;
double bottom_line = top_line + page_layout[i].size_mm.height;
bool draw_top_line = true;
bool draw_bottom_line = true;
if (settings.print_cutter_lines == CUTTER_NO_INTERSECTION) {
// check if another card is in the way of this cutter line
for (int j = 0; j < page_layout.size(); ++j) {
if (i == j) continue;
double other_top_line = page_layout[j].pos.height;
double other_bottom_line = other_top_line + page_layout[j].size_mm.height;
if (draw_top_line && top_line - other_top_line > job->threshold_bottom && other_bottom_line - top_line > job->threshold_bottom) {
draw_top_line = false;
if (!draw_bottom_line) break;
}
if (draw_bottom_line && bottom_line - other_top_line > job->threshold_bottom && other_bottom_line - bottom_line > job->threshold_bottom) {
draw_bottom_line = false;
if (!draw_top_line) break;
}
}
}
const RealSize& px_per_mm = page_layout[i].px_per_mm;
dc.SetUserScale(printer_px_per_mm.width / px_per_mm.width, printer_px_per_mm.height / px_per_mm.height);
if (draw_top_line) {
dc.DrawLine(wxPoint(0.0, px_per_mm.height * top_line), wxPoint(px_per_mm.width * horizontal_line_size, px_per_mm.height * top_line));
dc.DrawLine(wxPoint(px_per_mm.width * page_width, px_per_mm.height * top_line), wxPoint(px_per_mm.width * (page_width - horizontal_line_size), px_per_mm.height * top_line));
}
if (draw_bottom_line) {
dc.DrawLine(wxPoint(0.0, px_per_mm.height * bottom_line), wxPoint(px_per_mm.width * horizontal_line_size, px_per_mm.height * bottom_line));
dc.DrawLine(wxPoint(px_per_mm.width * page_width, px_per_mm.height * bottom_line), wxPoint(px_per_mm.width * (page_width - horizontal_line_size), px_per_mm.height * bottom_line));
}
}
} else {
queue_message(MESSAGE_WARNING, _ERROR_("h margin too small for cutter"));
}
dc.SetUserScale(printer_px_per_mm.width, printer_px_per_mm.height);
}
// ----------------------------------------------------------------------------- : PrintWindow
PrintJobP make_print_job(Window* parent, const SetP& set, const ExportCardSelectionChoices& choices) {
// Let the user choose cards and spacing
// controls
ExportWindowBase wnd(parent, _TITLE_("select cards print"), set, choices);
ExportWindowBase wnd(parent, _TITLE_("select cards print"), set, choices);
wxFloatingPointValidator<float> validator(2, NULL, wxNUM_VAL_ZERO_AS_BLANK);
validator.SetRange(0, 100);
wxTextCtrl* space = new wxTextCtrl(&wnd, wxID_ANY, _(""), wxDefaultPosition, wxDefaultSize, 0L, validator);
wxTextCtrl* space = new wxTextCtrl(&wnd, wxID_ANY, _(""), wxDefaultPosition, wxDefaultSize, 0L, validator);
space->SetValue(wxString::Format(wxT("%lf"), settings.print_spacing));
wxChoice* cutter = new wxChoice(&wnd, wxID_ANY);
cutter->Clear();
@@ -371,10 +371,10 @@ PrintJobP make_print_job(Window* parent, const SetP& set, const ExportCardSelect
if (wnd.ShowModal() != wxID_OK) {
return PrintJobP(); // cancel
} else {
// save settings, make print job
String spacing = space->GetValue();
if (spacing.empty()) spacing = _("0");
spacing.ToDouble(&settings.print_spacing);
// save settings, make print job
String spacing = space->GetValue();
if (spacing.empty()) spacing = _("0");
spacing.ToDouble(&settings.print_spacing);
settings.print_cutter_lines = (CutterLinesType)cutter->GetSelection();
PrintJobP job = make_intrusive<PrintJob>(set, wnd.getSelection());
return job;
+252 -70
View File
@@ -25,6 +25,7 @@
#include <util/tagged_string.hpp>
#include <util/window_id.hpp>
#include <wx/splitter.h>
#include <wx/gbsizer.h>
// ----------------------------------------------------------------------------- : CardsPanel
@@ -32,15 +33,36 @@ CardsPanel::CardsPanel(Window* parent, int id)
: SetWindowPanel(parent, id)
{
// init controls
editor = new CardEditor(this, ID_EDITOR);
splitter = new wxSplitterWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
card_list = new FilteredImageCardList(splitter, ID_CARD_LIST);
nodes_panel = new wxPanel(splitter, wxID_ANY);
notes = new TextCtrl(nodes_panel, ID_NOTES, true);
collapse_notes = new HoverButton(nodes_panel, ID_COLLAPSE_NOTES, _("btn_collapse"), Color(), false);
editor = new CardEditor(this, ID_EDITOR);
link_editor = new CardEditor(this, ID_CARD_LINK_EDITOR);
focused_editor = editor;
link_viewer_1 = new CardViewer(this, ID_CARD_LINK_VIEWER);
link_viewer_2 = new CardViewer(this, ID_CARD_LINK_VIEWER);
link_viewer_3 = new CardViewer(this, ID_CARD_LINK_VIEWER);
link_viewer_4 = new CardViewer(this, ID_CARD_LINK_VIEWER);
link_relation_1 = new wxStaticText(this, ID_CARD_LINK_RELATION_1, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxST_ELLIPSIZE_END);
link_relation_2 = new wxStaticText(this, ID_CARD_LINK_RELATION_2, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxST_ELLIPSIZE_END);
link_relation_3 = new wxStaticText(this, ID_CARD_LINK_RELATION_3, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxST_ELLIPSIZE_END);
link_relation_4 = new wxStaticText(this, ID_CARD_LINK_RELATION_4, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxST_ELLIPSIZE_END);
link_select = new wxButton(this, ID_CARD_LINK_SELECT, _BUTTON_("link select"), wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT);
link_unlink_1 = new wxButton(this, ID_CARD_LINK_UNLINK_1, _BUTTON_("unlink"), wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT);
link_unlink_2 = new wxButton(this, ID_CARD_LINK_UNLINK_2, _BUTTON_("unlink"), wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT);
link_unlink_3 = new wxButton(this, ID_CARD_LINK_UNLINK_3, _BUTTON_("unlink"), wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT);
link_unlink_4 = new wxButton(this, ID_CARD_LINK_UNLINK_4, _BUTTON_("unlink"), wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT);
splitter = new wxSplitterWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
card_list = new FilteredImageCardList(splitter, ID_CARD_LIST);
nodes_panel = new wxPanel(splitter, wxID_ANY);
notes = new TextCtrl(nodes_panel, ID_NOTES, true);
collapse_notes = new HoverButton(nodes_panel, ID_COLLAPSE_NOTES, _("btn_collapse"), Color(), false);
collapse_notes->SetExtraStyle(wxWS_EX_PROCESS_UI_UPDATES);
filter = nullptr;
filter = nullptr;
editor->next_in_tab_order = card_list;
wxFont font = link_relation_1->GetFont();
font.SetWeight(wxFONTWEIGHT_BOLD);
link_relation_1->SetFont(font);
link_relation_2->SetFont(font);
link_relation_3->SetFont(font);
link_relation_4->SetFont(font);
// init sizer for notes panel
wxSizer* sn = new wxBoxSizer(wxVERTICAL);
wxSizer* sc = new wxBoxSizer(wxHORIZONTAL);
@@ -54,10 +76,46 @@ CardsPanel::CardsPanel(Window* parent, int id)
splitter->SetSashGravity(1.0);
splitter->SplitHorizontally(card_list, nodes_panel, -40);
notes_below_editor = false;
// init sizer
wxSizer* s = new wxBoxSizer(wxHORIZONTAL);
s_left = new wxBoxSizer(wxVERTICAL);
s_left->Add(editor);
// init sizer for editors and viewers
wxSizer* s = new wxBoxSizer(wxHORIZONTAL); // Global Sizer
s_left = new wxBoxSizer(wxVERTICAL); // Sizer for the selected card, and it's linked cards
wxSizer* card_and_link = new wxBoxSizer(wxHORIZONTAL);
s_left->Add(card_and_link);
card_and_link->Add(editor);
wxGridBagSizer* link_boxes = new wxGridBagSizer(); // Sizer for the linked cards
card_and_link->Add(link_boxes, 0, wxLEFT, 2);
link_box_1 = new wxStaticBoxSizer(wxVERTICAL, this); // Box around the first linked card, it's relation, and buttons to select and unlink
link_boxes->Add(link_box_1, wxGBPosition(0, 0), wxGBSpan(1, 1));
link_box_2 = new wxStaticBoxSizer(wxVERTICAL, this); // Box around the second linked card, it's relation, and a button to unlink
link_boxes->Add(link_box_2, wxGBPosition(1, 0), wxGBSpan(1, 1));
link_box_3 = new wxStaticBoxSizer(wxVERTICAL, this); // Box around the third linked card, it's relation, and a button to unlink
link_boxes->Add(link_box_3, wxGBPosition(0, 1), wxGBSpan(1, 1));
link_box_4 = new wxStaticBoxSizer(wxVERTICAL, this); // Box around the fourth linked card, it's relation, and a button to unlink
link_boxes->Add(link_box_4, wxGBPosition(1, 1), wxGBSpan(1, 1));
wxGridBagSizer* link_grid_1 = new wxGridBagSizer(); // Sizer for the first linked card, with it's relation, and a button to unlink
link_box_1->Add(link_grid_1);
wxGridBagSizer* link_grid_2 = new wxGridBagSizer();
link_box_2->Add(link_grid_2);
wxGridBagSizer* link_grid_3 = new wxGridBagSizer();
link_box_3->Add(link_grid_3);
wxGridBagSizer* link_grid_4 = new wxGridBagSizer();
link_box_4->Add(link_grid_4);
wxSizer* link_grid_1_buttons = new wxBoxSizer(wxHORIZONTAL);
link_grid_1->Add(link_relation_1, wxGBPosition(0, 0), wxGBSpan(1, 1), wxALIGN_CENTER_VERTICAL | wxLEFT, 4);
link_grid_1->Add(link_viewer_1, wxGBPosition(1, 0), wxGBSpan(1, 2));
link_grid_1->Add(link_editor, wxGBPosition(2, 0), wxGBSpan(1, 2));
link_grid_1->Add(link_grid_1_buttons, wxGBPosition(0, 1), wxGBSpan(1, 1), wxALIGN_RIGHT);
link_grid_1_buttons->Add(link_select);
link_grid_1_buttons->Add(link_unlink_1);
link_grid_2->Add(link_relation_2, wxGBPosition(0, 0), wxGBSpan(1, 1), wxALIGN_CENTER_VERTICAL | wxLEFT, 4);
link_grid_2->Add(link_unlink_2, wxGBPosition(0, 1), wxGBSpan(1, 1), wxALIGN_RIGHT);
link_grid_2->Add(link_viewer_2, wxGBPosition(1, 0), wxGBSpan(1, 2));
link_grid_3->Add(link_relation_3, wxGBPosition(0, 0), wxGBSpan(1, 1), wxALIGN_CENTER_VERTICAL | wxLEFT, 4);
link_grid_3->Add(link_unlink_3, wxGBPosition(0, 1), wxGBSpan(1, 1), wxALIGN_RIGHT);
link_grid_3->Add(link_viewer_3, wxGBPosition(1, 0), wxGBSpan(1, 2));
link_grid_4->Add(link_relation_4, wxGBPosition(0, 0), wxGBSpan(1, 1), wxALIGN_CENTER_VERTICAL | wxLEFT, 4);
link_grid_4->Add(link_unlink_4, wxGBPosition(0, 1), wxGBSpan(1, 1), wxALIGN_RIGHT);
link_grid_4->Add(link_viewer_4, wxGBPosition(1, 0), wxGBSpan(1, 2));
s->Add(s_left, 0, wxEXPAND | wxRIGHT, 2);
s->Add(splitter, 1, wxEXPAND);
s->SetSizeHints(this);
@@ -77,6 +135,8 @@ CardsPanel::CardsPanel(Window* parent, int id)
add_menu_item(menuCard, ID_CARD_ADD_JSON, "card_add_multiple", _MENU_("add card json") + _(" "), _HELP_("add card json"));
add_menu_item_tr(menuCard, ID_CARD_ADD, "card_add", "add_card");
add_menu_item(menuCard, ID_CARD_REMOVE, "card_del", _MENU_("remove card")+_(" "), _HELP_("remove card"));
add_menu_item(menuCard, ID_CARD_LINK, "card_link", _MENU_("link card") + _(" "), _HELP_("link card"));
add_menu_item(menuCard, ID_CARD_AND_LINK_COPY, "card_copy", _MENU_("copy card and links") + _(" "), _HELP_("copy card and links"));
menuCard->AppendSeparator();
auto menuRotate = new wxMenu();
add_menu_item_tr(menuRotate, ID_CARD_ROTATE_0, "card_rotate_0", "rotate_0", wxITEM_CHECK);
@@ -90,7 +150,7 @@ CardsPanel::CardsPanel(Window* parent, int id)
menuFormat = new wxMenu();
add_menu_item_tr(menuFormat, ID_FORMAT_BOLD, "bold", "bold", wxITEM_CHECK);
add_menu_item_tr(menuFormat, ID_FORMAT_ITALIC, "italic", "italic", wxITEM_CHECK);
add_menu_item_tr(menuFormat, ID_FORMAT_ITALIC, "italic", "italic", wxITEM_CHECK);
add_menu_item_tr(menuFormat, ID_FORMAT_UNDERLINE, "underline", "underline", wxITEM_CHECK);
add_menu_item_tr(menuFormat, ID_FORMAT_SYMBOL, "symbol", "symbols", wxITEM_CHECK);
add_menu_item_tr(menuFormat, ID_FORMAT_REMINDER, "reminder", "reminder_text", wxITEM_CHECK);
@@ -99,38 +159,38 @@ CardsPanel::CardsPanel(Window* parent, int id)
menuFormat->Append(insertSymbolMenu);
toolAddCard = nullptr;
}
}
void CardsPanel::updateCardCounts() {
if (counts && card_list && set) {
int selected = card_list->GetSelectedItemCount();
int filtered = card_list->GetItemCount();
int total = set->cards.size();
if (
selected_cards_count == selected
&& filtered_cards_count == filtered
&& total_cards_count == total
&& !counts->GetLabel().empty()
) return;
selected_cards_count = selected;
filtered_cards_count = filtered;
total_cards_count = total;
if (filtered == total) {
counts->SetLabel(_TOOL_2_("card counts 2",
wxString::Format(wxT("%i"), selected),
wxString::Format(wxT("%i"), total)));
}
else {
counts->SetLabel(_TOOL_3_("card counts 3",
wxString::Format(wxT("%i"), selected),
wxString::Format(wxT("%i"), filtered),
wxString::Format(wxT("%i"), total)));
}
void CardsPanel::updateCardCounts() {
if (counts && card_list && set) {
int selected = card_list->GetSelectedItemCount();
int filtered = card_list->GetItemCount();
int total = set->cards.size();
if (
selected_cards_count == selected
&& filtered_cards_count == filtered
&& total_cards_count == total
&& !counts->GetLabel().empty()
) return;
selected_cards_count = selected;
filtered_cards_count = filtered;
total_cards_count = total;
if (filtered == total) {
counts->SetLabel(_TOOL_2_("card counts 2",
wxString::Format(wxT("%i"), selected),
wxString::Format(wxT("%i"), total)));
}
else {
counts->SetLabel(_TOOL_3_("card counts 3",
wxString::Format(wxT("%i"), selected),
wxString::Format(wxT("%i"), filtered),
wxString::Format(wxT("%i"), total)));
}
}
}
}
void CardsPanel::updateNotesPosition() {
wxSize editor_size = editor->GetBestSize();
@@ -177,6 +237,11 @@ CardsPanel::~CardsPanel() {
void CardsPanel::onChangeSet() {
editor->setSet(set);
link_editor->setSet(set);
link_viewer_1->setSet(set);
link_viewer_2->setSet(set);
link_viewer_3->setSet(set);
link_viewer_4->setSet(set);
notes->setSet(set);
card_list->setSet(set);
@@ -187,9 +252,9 @@ void CardsPanel::onChangeSet() {
menuCard->Remove(ID_CARD_ADD_MULT);
((wxMenu*)menuCard)->Insert(4,insertManyCardsMenu); // HACK: the position is hardcoded
// also for the toolbar dropdown menu
if (toolAddCard) {
// Originally this was using the menu directly, but there are compatibility issues apparently.
// At this point it might be possible to just store a reference to the toolbar directly instead.
if (toolAddCard) {
// Originally this was using the menu directly, but there are compatibility issues apparently.
// At this point it might be possible to just store a reference to the toolbar directly instead.
toolAddCard->GetToolBar()->SetDropdownMenu(ID_CARD_ADD, makeAddCardsSubmenu(true));
}
}
@@ -218,7 +283,7 @@ wxMenu* CardsPanel::makeAddCardsSubmenu(bool add_single_card_option) {
void CardsPanel::initUI(wxToolBar* tb, wxMenuBar* mb) {
// Toolbar
add_tool_tr(tb, ID_FORMAT_BOLD, "bold", "bold", false, wxITEM_CHECK);
add_tool_tr(tb, ID_FORMAT_ITALIC, "italic", "italic", false, wxITEM_CHECK);
add_tool_tr(tb, ID_FORMAT_ITALIC, "italic", "italic", false, wxITEM_CHECK);
add_tool_tr(tb, ID_FORMAT_UNDERLINE, "underline", "underline", false, wxITEM_CHECK);
add_tool_tr(tb, ID_FORMAT_SYMBOL, "symbol", "symbols", false, wxITEM_CHECK);
add_tool_tr(tb, ID_FORMAT_REMINDER, "reminder", "reminder_text", false, wxITEM_CHECK);
@@ -226,6 +291,7 @@ void CardsPanel::initUI(wxToolBar* tb, wxMenuBar* mb) {
toolAddCard = add_tool_tr(tb, ID_CARD_ADD, "card_add", "add_card", false, wxITEM_DROPDOWN);
tb->SetDropdownMenu(ID_CARD_ADD, makeAddCardsSubmenu(true));
add_tool_tr(tb, ID_CARD_REMOVE, "card_del", "remove_card");
add_tool_tr(tb, ID_CARD_LINK, "card_link", "link_card");
tb->AddSeparator();
add_tool_tr(tb, ID_CARD_ROTATE, "card_rotate", "rotate_card", false, wxITEM_DROPDOWN);
auto menuRotate = new wxMenu();
@@ -239,10 +305,10 @@ void CardsPanel::initUI(wxToolBar* tb, wxMenuBar* mb) {
assert(!filter);
filter = new FilterCtrl(tb, ID_CARD_FILTER, _TOOL_("search cards"), _HELP_("search cards control"));
filter->setFilter(filter_value);
tb->AddControl(filter);
tb->AddControl(filter);
counts = new wxStaticText(tb, ID_CARD_COUNTER, _(""));
updateCardCounts();
tb->AddControl(counts);
updateCardCounts();
tb->AddControl(counts);
tb->Realize();
// Menus
mb->Insert(2, menuCard, _MENU_("cards"));
@@ -252,12 +318,13 @@ void CardsPanel::initUI(wxToolBar* tb, wxMenuBar* mb) {
void CardsPanel::destroyUI(wxToolBar* tb, wxMenuBar* mb) {
// Toolbar
tb->DeleteTool(ID_FORMAT_BOLD);
tb->DeleteTool(ID_FORMAT_ITALIC);
tb->DeleteTool(ID_FORMAT_ITALIC);
tb->DeleteTool(ID_FORMAT_UNDERLINE);
tb->DeleteTool(ID_FORMAT_SYMBOL);
tb->DeleteTool(ID_FORMAT_REMINDER);
tb->DeleteTool(ID_CARD_ADD);
tb->DeleteTool(ID_CARD_REMOVE);
tb->DeleteTool(ID_CARD_LINK);
tb->DeleteTool(ID_CARD_ROTATE);
tb->DeleteTool(ID_CARD_COUNTER);
// remember the value in the filter control, because the card list remains filtered
@@ -277,8 +344,8 @@ void CardsPanel::destroyUI(wxToolBar* tb, wxMenuBar* mb) {
void CardsPanel::onUpdateUI(wxUpdateUIEvent& ev) {
switch (ev.GetId()) {
case ID_CARD_PREV: ev.Enable(card_list->canSelectPrevious()); break;
case ID_CARD_NEXT: ev.Enable(card_list->canSelectNext()); break;
case ID_CARD_PREV: ev.Enable(card_list->canSelectPrevious()); break;
case ID_CARD_NEXT: ev.Enable(card_list->canSelectNext()); break;
case ID_CARD_ROTATE_0: case ID_CARD_ROTATE_90: case ID_CARD_ROTATE_180: case ID_CARD_ROTATE_270: {
StyleSheetSettings& ss = settings.stylesheetSettingsFor(set->stylesheetFor(card_list->getCard()));
int a = ev.GetId() == ID_CARD_ROTATE_0 ? 0
@@ -292,11 +359,16 @@ void CardsPanel::onUpdateUI(wxUpdateUIEvent& ev) {
ev.Enable(insertManyCardsMenu->GetSubMenu() != nullptr);
break;
}
case ID_CARD_REMOVE: ev.Enable(card_list->canDelete()); break;
case ID_CARD_REMOVE: ev.Enable(card_list->canDelete()); break;
case ID_CARD_LINK: ev.Enable(card_list->canLink()); break;
case ID_CARD_AND_LINK_COPY: ev.Enable(card_list->canCopy()); break;
case ID_FORMAT_BOLD: case ID_FORMAT_ITALIC: case ID_FORMAT_UNDERLINE: case ID_FORMAT_SYMBOL: case ID_FORMAT_REMINDER: {
if (focused_control(this) == ID_EDITOR) {
ev.Enable(editor->canFormat(ev.GetId()));
ev.Check (editor->hasFormat(ev.GetId()));
} else if (focused_control(this) == ID_CARD_LINK_EDITOR) {
ev.Enable(link_editor->canFormat(ev.GetId()));
ev.Check (link_editor->hasFormat(ev.GetId()));
} else {
ev.Enable(false);
ev.Check(false);
@@ -313,18 +385,18 @@ void CardsPanel::onUpdateUI(wxUpdateUIEvent& ev) {
case ID_INSERT_SYMBOL: ev.Enable(false); break;
#else
case ID_INSERT_SYMBOL: {
wxMenu* menu = editor->getMenu(ID_INSERT_SYMBOL);
wxMenu* menu = focused_editor->getMenu(ID_INSERT_SYMBOL);
ev.Enable(menu);
break;
}
#endif
}
}
updateCardCounts();
}
void CardsPanel::onMenuOpen(wxMenuEvent& ev) {
if (ev.GetMenu() != menuFormat) return;
wxMenu* menu = editor->getMenu(ID_INSERT_SYMBOL);
wxMenu* menu = focused_editor->getMenu(ID_INSERT_SYMBOL);
if (insertSymbolMenu->GetSubMenu() != menu || (menu && menu->GetParent() != menuFormat)) {
// re-add the menu
menuFormat->Remove(ID_INSERT_SYMBOL);
@@ -358,6 +430,27 @@ void CardsPanel::onCommand(int id) {
case ID_CARD_REMOVE:
card_list->doDelete();
break;
case ID_CARD_LINK:
card_list->doLink();
setCard(card_list->getCard(), true);
break;
case ID_CARD_LINK_UNLINK_1: case ID_CARD_LINK_UNLINK_2: case ID_CARD_LINK_UNLINK_3: case ID_CARD_LINK_UNLINK_4: {
card_list->doUnlink((
id == ID_CARD_LINK_UNLINK_1 ? link_viewer_1
: id == ID_CARD_LINK_UNLINK_2 ? link_viewer_2
: id == ID_CARD_LINK_UNLINK_3 ? link_viewer_3
: link_viewer_4
)->getCard());
setCard(card_list->getCard(), true);
break;
}
case ID_CARD_LINK_SELECT: {
setCard(link_viewer_1->getCard(), true);
break;
}
case ID_CARD_AND_LINK_COPY:
card_list->doCopyCardAndLinkedCards();
break;
case ID_CARD_ROTATE:
case ID_CARD_ROTATE_0: case ID_CARD_ROTATE_90: case ID_CARD_ROTATE_180: case ID_CARD_ROTATE_270: {
StyleSheetSettings& ss = settings.stylesheetSettingsFor(set->stylesheetFor(card_list->getCard()));
@@ -378,6 +471,9 @@ void CardsPanel::onCommand(int id) {
if (focused_control(this) == ID_EDITOR) {
editor->doFormat(id);
}
else if (focused_control(this) == ID_CARD_LINK_EDITOR) {
link_editor->doFormat(id);
}
break;
}
case ID_COLLAPSE_NOTES: {
@@ -399,7 +495,7 @@ void CardsPanel::onCommand(int id) {
default: {
if (id >= ID_INSERT_SYMBOL_MENU_MIN && id <= ID_INSERT_SYMBOL_MENU_MAX) {
// pass on to editor
editor->onCommand(id);
focused_editor->onCommand(id);
} else if (id >= ID_ADD_CARDS_MENU_MIN && id <= ID_ADD_CARDS_MENU_MAX) {
// add multiple cards
AddCardsScriptP script = set->game->add_cards_scripts.at(id - ID_ADD_CARDS_MENU_MIN);
@@ -420,10 +516,11 @@ bool CardsPanel::wantsToHandle(const Action&, bool undone) const {
// determine what control to use for clipboard actions
#define CUT_COPY_PASTE(op,return) \
int id = focused_control(this); \
if (id == ID_EDITOR) { return editor->op(); } \
else if (id == ID_CARD_LIST) { return card_list->op(); } \
else if (id == ID_NOTES) { return notes->op(); } \
else { return false; }
if (id == ID_EDITOR) { return editor->op(); } \
else if (id == ID_CARD_LINK_EDITOR) { return link_editor->op(); } \
else if (id == ID_CARD_LIST) { return card_list->op(); } \
else if (id == ID_NOTES) { return notes->op(); } \
else { return false; }
bool CardsPanel::canCut() const { CUT_COPY_PASTE(canCut, return) }
bool CardsPanel::canCopy() const { CUT_COPY_PASTE(canCopy, return) }
@@ -434,17 +531,19 @@ void CardsPanel::doCopy() { CUT_COPY_PASTE(doCopy, return (void)) }
bool CardsPanel::canPaste() const {
if (card_list->canPaste()) return true;
int id = focused_control(this);
if (id == ID_EDITOR) return editor->canPaste();
else if (id == ID_NOTES) return notes->canPaste();
else return false;
if (id == ID_EDITOR) return editor->canPaste();
else if (id == ID_CARD_LINK_EDITOR) return link_editor->canPaste();
else if (id == ID_NOTES) return notes->canPaste();
else return false;
}
void CardsPanel::doPaste() {
if (card_list->canPaste()) {
card_list->doPaste();
} else {
int id = focused_control(this);
if (id == ID_EDITOR) editor->doPaste();
else if (id == ID_NOTES) notes->doPaste();
if (id == ID_EDITOR) editor->doPaste();
else if (id == ID_CARD_LINK_EDITOR) link_editor->doPaste();
else if (id == ID_NOTES) notes->doPaste();
}
}
@@ -465,7 +564,7 @@ public:
SearchFindInfo(CardsPanel& panel, wxFindReplaceData& what) : FindInfo(what), panel(panel) {}
bool handle(const CardP& card, const TextValueP& value, size_t pos, bool was_selection) override {
// Select the card
panel.card_list->setCard(card);
panel.setCard(card, true);
return true;
}
private:
@@ -477,7 +576,7 @@ public:
ReplaceFindInfo(CardsPanel& panel, wxFindReplaceData& what) : FindInfo(what), panel(panel) {}
bool handle(const CardP& card, const TextValueP& value, size_t pos, bool was_selection) override {
// Select the card
panel.card_list->setCard(card);
panel.setCard(card, true);
// Replace
if (was_selection) {
panel.editor->insert(escape(what.GetReplaceString()), _("Replace"));
@@ -512,10 +611,12 @@ bool CardsPanel::search(FindInfo& find, bool from_start) {
if (include) {
editor->setCard(card);
if (editor->search(find, from_start || card != current)) {
return true; // done
// found a card, call handle
return true;
}
}
}
// didn't find anything, put editor back in its previous state
editor->setCard(current);
return false;
}
@@ -527,9 +628,76 @@ CardP CardsPanel::selectedCard() const {
}
void CardsPanel::selectCard(const CardP& card) {
if (!set) return; // we want onChangeSet first
card_list->setCard(card);
editor->setCard(card);
vector<pair<CardP, String>> linked_cards = card->getLinkedCards(*set);
int count = linked_cards.size();
if (count >= 1) {
link_box_1->Show(true);
link_editor->setCard(linked_cards[0].first);
link_viewer_1->setCard(linked_cards[0].first);
link_relation_1->SetLabel(linked_cards[0].second);
if (count == 1) {
link_editor->Show(true);
link_viewer_1->Show(false);
link_select->Show(true);
link_editor->InvalidateBestSize();
link_relation_1->SetMaxSize(wxSize(link_editor->GetSize().x - link_unlink_1->GetSize().x, -1));
} else {
link_editor->Show(false);
link_viewer_1->Show(true);
link_select->Show(false);
link_viewer_1->InvalidateBestSize();
link_relation_1->SetMaxSize(wxSize(link_viewer_1->GetSize().x - link_unlink_1->GetSize().x, -1));
}
link_relation_1->InvalidateBestSize();
} else {
link_box_1->Show(false);
link_editor->setCard(card);
link_viewer_1->setCard(card);
//link_relation_1->SetLabel(wxEmptyString);
}
if (count >= 2) {
link_box_2->Show(true);
link_viewer_2->setCard(linked_cards[1].first);
link_relation_2->SetLabel(linked_cards[1].second);
link_relation_2->SetMaxSize(wxSize(link_viewer_2->GetSize().x - link_unlink_2->GetSize().x, -1));
link_relation_2->InvalidateBestSize();
} else {
link_box_2->Show(false);
link_viewer_2->setCard(card);
//link_relation_2->SetLabel(wxEmptyString);
}
if (count >= 3) {
link_box_3->Show(true);
link_viewer_3->setCard(linked_cards[2].first);
link_relation_3->SetLabel(linked_cards[2].second);
link_relation_3->SetMaxSize(wxSize(link_viewer_3->GetSize().x - link_unlink_3->GetSize().x, -1));
link_relation_3->InvalidateBestSize();
} else {
link_box_3->Show(false);
link_viewer_3->setCard(card);
//link_relation_3->SetLabel(wxEmptyString);
}
if (count >= 4) {
link_box_4->Show(true);
link_viewer_4->setCard(linked_cards[3].first);
link_relation_4->SetLabel(linked_cards[3].second);
link_relation_4->SetMaxSize(wxSize(link_viewer_4->GetSize().x - link_unlink_4->GetSize().x, -1));
link_relation_4->InvalidateBestSize();
} else {
link_box_4->Show(false);
link_viewer_4->setCard(card);
//link_relation_4->SetLabel(wxEmptyString);
}
if (count >= 5) {
queue_message(MESSAGE_WARNING, "DEBUG More than 4 linked cards found for card: " + card->identification());
}
notes->setValue(card ? &card->notes : nullptr);
Layout();
updateNotesPosition();
}
@@ -539,6 +707,20 @@ void CardsPanel::selectFirstCard() {
card_list->selectFirst();
}
void CardsPanel::setCard(const CardP& card, bool event) {
if (!set) return; // we want onChangeSet first
card_list->setCard(card, event);
}
void CardsPanel::refreshCard(const CardP& card) {
if (!set) return; // we want onChangeSet first
card_list->RefreshItem(card_list->findGivenItemPos(card));
}
void CardsPanel::getCardLists(vector<CardListBase*>& out) {
out.push_back(card_list);
}
}
void CardsPanel::setFocusedEditor(DataEditor* editor) {
focused_editor = editor;
}
+12 -1
View File
@@ -15,6 +15,9 @@ class wxSplitterWindow;
class FilteredImageCardList;
class DataEditor;
class TextCtrl;
class CardViewer;
class wxSizer;
class wxButton;
class HoverButton;
class FindInfo;
class FilterCtrl;
@@ -74,17 +77,25 @@ public:
CardP selectedCard() const override;
void selectCard(const CardP& card) override;
void selectFirstCard() override;
void setCard(const CardP& card, bool event = false);
void refreshCard(const CardP& card);
void getCardLists(vector<CardListBase*>& out) override;
void setFocusedEditor(DataEditor* editor);
private:
// --------------------------------------------------- : Controls
wxSizer* s_left;
wxSplitterWindow* splitter;
DataEditor* editor;
DataEditor* editor, *link_editor, *focused_editor;
FilteredImageCardList* card_list;
wxPanel* nodes_panel;
TextCtrl* notes;
wxSizer* link_box_1, *link_box_2, *link_box_3, *link_box_4;
wxStaticText* link_relation_1, *link_relation_2, *link_relation_3, *link_relation_4;
CardViewer* link_viewer_1, *link_viewer_2, *link_viewer_3, *link_viewer_4;
wxButton* link_unlink_1, *link_unlink_2, *link_unlink_3, *link_unlink_4, *link_select;
HoverButton* collapse_notes;
FilterCtrl* filter;
String filter_value; // value of filter, need separate variable because the control is destroyed
+45 -45
View File
@@ -25,7 +25,7 @@ DECLARE_POINTER_TYPE(ConsoleMessage);
class ConsoleMessage : public IntrusivePtrBase<ConsoleMessage> {
public:
MessageType type;
String text; // string message
String text; // string message
wxDateTime timestamp;
Bitmap bitmap; // image message instead of string
ScriptValueP value; // other valued message (images? cards?)
@@ -80,13 +80,13 @@ public:
}
void add_message(MessageType type, String const& text, bool joined_to_previous = false) {
add_message(make_intrusive<ConsoleMessage>(type,text,joined_to_previous));
}
}
void clear_console() {
messages.clear();
layout_all();
selection = messages.size();
update_scrollbar();
messages.clear();
layout_all();
selection = messages.size();
update_scrollbar();
Refresh(false);
}
@@ -245,11 +245,11 @@ private:
wxAutoBufferedPaintDC dc(this);
PrepareDC(dc);
draw(dc);
}
}
void draw(wxDC& dc) const {
clearDC(dc, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
dc.SetFont(*wxNORMAL_FONT);
dc.SetFont(*wxNORMAL_FONT);
FOR_EACH_CONST(msg, messages) {
draw(dc, *msg);
@@ -283,23 +283,23 @@ private:
// draw background
dc.SetPen(*wxTRANSPARENT_PEN);
dc.SetBrush(lerp(bg,color, 0.05));
dc.DrawRectangle(left,top,width,msg.height);
// draw foreground
dc.SetTextForeground(fg);
// draw timestamp
dc.DrawText(msg.timestamp.FormatISOTime(), left + TIMESTAMP_PADDING, top + TEXT_PADDING_TOP);
int timestamp_resolved_width;
dc.GetTextExtent(_("55:55:55"), &timestamp_resolved_width, nullptr);
left += timestamp_resolved_width;
left += TIMESTAMP_PADDING * 2;
dc.DrawRectangle(left,top,width,msg.height);
// draw foreground
dc.SetTextForeground(fg);
// draw timestamp
dc.DrawText(msg.timestamp.FormatISOTime(), left + TIMESTAMP_PADDING, top + TEXT_PADDING_TOP);
int timestamp_resolved_width;
dc.GetTextExtent(_("55:55:55"), &timestamp_resolved_width, nullptr);
left += timestamp_resolved_width;
left += TIMESTAMP_PADDING * 2;
// draw line right of timestamp
dc.SetPen(lerp(bg, fg, 0.3));
dc.DrawLine(left, top, left, top + msg.height);
dc.DrawLine(left, top, left, top + msg.height);
// draw icon
if (icons[msg.type].Ok()) {
@@ -331,7 +331,7 @@ private:
if (msg.bitmap.Ok()) {
dc.DrawBitmap(msg.bitmap, text_left, text_top);
text_top += msg.bitmap.GetHeight();
}
}
// draw line below item
dc.SetPen(lerp(bg,fg, 0.3));
@@ -356,9 +356,9 @@ private:
}
if (begin != msg.text.end()) {
text_height += dc.GetCharHeight() + TEXT_LINE_SPACING;
}
// account for the height of a timestamp even if there is no other text content.
}
// account for the height of a timestamp even if there is no other text content.
if (text_height == 0) {
text_height = dc.GetCharHeight() + TEXT_LINE_SPACING;
}
@@ -371,9 +371,9 @@ private:
// --------------------------------------------------- : Layout
static constexpr int LIST_SPACING = 1;
static constexpr int LIST_SPACING = 1;
static constexpr int TIMESTAMP_PADDING = 3;
static constexpr int ICON_PADDING = 3;
static constexpr int ICON_PADDING = 3;
static constexpr int ICON_PADDING_LEFT = TIMESTAMP_PADDING + 3;
static constexpr int TEXT_PADDING_LEFT = ICON_PADDING_LEFT + 16 + 4;
static constexpr int TEXT_PADDING_RIGHT = 4;
@@ -488,7 +488,7 @@ END_EVENT_TABLE()
ConsolePanel::ConsolePanel(Window* parent, int id)
: SetWindowPanel(parent, id)
, menuConsole(nullptr)
, menuConsole(nullptr)
, messages(nullptr)
, entry(nullptr)
, is_active_window(false)
@@ -514,13 +514,13 @@ ConsolePanel::ConsolePanel(Window* parent, int id)
wxSizer* s = new wxBoxSizer(wxVERTICAL);
s->Add(splitter, 1, wxEXPAND);
s->SetSizeHints(this);
SetSizer(s);
// init menus
menuConsole = new wxMenu();
SetSizer(s);
// init menus
menuConsole = new wxMenu();
add_menu_item_tr(menuConsole, ID_CLEAR_CONSOLE, "clear_console", "clear console");
}
}
ConsolePanel::~ConsolePanel() {
delete menuConsole;
}
@@ -542,19 +542,19 @@ void ConsolePanel::initUI(wxToolBar* tb, wxMenuBar* mb) {
// stop blinker
is_active_window = true;
stop_blinker();
add_tool_tr(tb, ID_CLEAR_CONSOLE, "clear_console", "clear console");
tb->Realize();
stop_blinker();
add_tool_tr(tb, ID_CLEAR_CONSOLE, "clear_console", "clear console");
tb->Realize();
mb->Insert(2, menuConsole, _MENU_("console"));
}
void ConsolePanel::destroyUI(wxToolBar* tb, wxMenuBar* mb) {
// Toolbar
// Toolbar
tb->DeleteTool(ID_CLEAR_CONSOLE);
// Menus
mb->Remove(2);
// Menus
mb->Remove(2);
// we are no longer active, allow blinker
is_active_window = false;
+3 -3
View File
@@ -19,7 +19,7 @@ class HistoryTextCtrl;
class ConsolePanel : public SetWindowPanel {
public:
ConsolePanel(Window* parent, int id);
ConsolePanel(Window* parent, int id);
~ConsolePanel();
// --------------------------------------------------- : UI
@@ -49,8 +49,8 @@ private:
wxSplitterWindow* splitter;
MessageCtrl* messages;
wxPanel* entry_panel;
HistoryTextCtrl* entry;
HistoryTextCtrl* entry;
wxMenu* menuConsole;
void get_pending_errors();
+3 -3
View File
@@ -35,7 +35,7 @@ void SetInfoPanel::onChangeSet() {
void SetInfoPanel::initUI(wxToolBar* tb, wxMenuBar* mb) {
// Toolbar
add_tool_tr(tb, ID_FORMAT_BOLD, "bold", "bold", false, wxITEM_CHECK);
add_tool_tr(tb, ID_FORMAT_ITALIC, "italic", "italic", false, wxITEM_CHECK);
add_tool_tr(tb, ID_FORMAT_ITALIC, "italic", "italic", false, wxITEM_CHECK);
add_tool_tr(tb, ID_FORMAT_UNDERLINE, "underline", "underline", false, wxITEM_CHECK);
add_tool_tr(tb, ID_FORMAT_SYMBOL, "symbol", "symbols", false, wxITEM_CHECK);
add_tool_tr(tb, ID_FORMAT_REMINDER, "reminder", "reminder_text", false, wxITEM_CHECK);
@@ -43,7 +43,7 @@ void SetInfoPanel::initUI(wxToolBar* tb, wxMenuBar* mb) {
// Menus
auto menuFormat = new wxMenu();
add_menu_item_tr(menuFormat, ID_FORMAT_BOLD, "bold", "bold", wxITEM_CHECK);
add_menu_item_tr(menuFormat, ID_FORMAT_ITALIC, "italic", "italic", wxITEM_CHECK);
add_menu_item_tr(menuFormat, ID_FORMAT_ITALIC, "italic", "italic", wxITEM_CHECK);
add_menu_item_tr(menuFormat, ID_FORMAT_UNDERLINE, "underline", "underline", wxITEM_CHECK);
add_menu_item_tr(menuFormat, ID_FORMAT_SYMBOL, "symbol", "symbols", wxITEM_CHECK);
add_menu_item_tr(menuFormat, ID_FORMAT_REMINDER, "reminder", "reminder_text", wxITEM_CHECK);
@@ -55,7 +55,7 @@ void SetInfoPanel::initUI(wxToolBar* tb, wxMenuBar* mb) {
void SetInfoPanel::destroyUI(wxToolBar* tb, wxMenuBar* mb) {
// Toolbar
tb->DeleteTool(ID_FORMAT_BOLD);
tb->DeleteTool(ID_FORMAT_ITALIC);
tb->DeleteTool(ID_FORMAT_ITALIC);
tb->DeleteTool(ID_FORMAT_UNDERLINE);
tb->DeleteTool(ID_FORMAT_SYMBOL);
tb->DeleteTool(ID_FORMAT_REMINDER);
+14 -14
View File
@@ -35,20 +35,20 @@ void StylePanel::initControls() {
list = new PackageList (this, wxID_ANY);
use_for_all = new wxButton (this, ID_STYLE_USE_FOR_ALL, _BUTTON_("use for all cards"));
use_custom_options = new wxCheckBox(this, ID_STYLE_USE_CUSTOM, _BUTTON_("use custom styling options"));
editor = new StylingEditor(this, ID_EDITOR, wxNO_BORDER);
stylesheet_filter = new FilterCtrl(this, ID_STYLESHEET_FILTER, _LABEL_("search stylesheet list"), _HELP_("search stylesheet list control"));
stylesheet_filter->setFilter(stylesheet_filter_value);
editor = new StylingEditor(this, ID_EDITOR, wxNO_BORDER);
stylesheet_filter = new FilterCtrl(this, ID_STYLESHEET_FILTER, _LABEL_("search stylesheet list"), _HELP_("search stylesheet list control"));
stylesheet_filter->setFilter(stylesheet_filter_value);
// init sizer
wxSizer* s = new wxBoxSizer(wxHORIZONTAL);
s->Add(preview, 0, wxRIGHT, 2);
wxSizer* s2 = new wxBoxSizer(wxVERTICAL);
s2->Add(list, 0, wxEXPAND | wxBOTTOM, 4);
wxSizer* s3 = new wxBoxSizer(wxHORIZONTAL);
s3->Add(stylesheet_filter, 0, wxBOTTOM | wxLEFT, 4);
s2->Add(list, 0, wxEXPAND | wxBOTTOM, 4);
wxSizer* s3 = new wxBoxSizer(wxHORIZONTAL);
s3->Add(stylesheet_filter, 0, wxBOTTOM | wxLEFT, 4);
s3->AddStretchSpacer();
s3->Add(use_for_all, 0, wxBOTTOM | wxRIGHT, 4);
s3->Add(use_for_all, 0, wxBOTTOM | wxRIGHT, 4);
s2->Add(s3, wxSizerFlags().Expand().Border(wxALL, 6));
wxSizer* s4 = new wxStaticBoxSizer(wxVERTICAL, this, _LABEL_("styling options"));
s4->Add(use_custom_options, 0, wxEXPAND | wxALL, 4);
@@ -81,10 +81,10 @@ void StylePanel::updateListSize() {
if (column_count != list->column_count) {
list->column_count = column_count;
static_cast<SetWindow*>(GetParent())->fixMinWindowSize();
}
}
list_size_already_initialized = true;
}
}
bool StylePanel::Layout() {
updateListSize();
@@ -136,8 +136,8 @@ void StylePanel::onAction(const Action& action, bool undone) {
use_for_all->Enable(card && card->stylesheet);
use_custom_options->Enable((bool)card);
use_custom_options->SetValue(card ? card->has_styling : false);
}
}
void StylePanel::onStylesheetFilterUpdate(wxCommandEvent&) {
if (list->hasSelection()) {
StyleSheetP existingStylesheetSelection = list->getSelection<StyleSheet>(false);
@@ -208,7 +208,7 @@ void StylePanel::onUseCustom(wxCommandEvent&) {
}
BEGIN_EVENT_TABLE(StylePanel, wxPanel)
EVT_GALLERY_SELECT(wxID_ANY, StylePanel::onStyleSelect)
EVT_GALLERY_SELECT(wxID_ANY, StylePanel::onStyleSelect)
EVT_COMMAND_RANGE(ID_STYLESHEET_FILTER, ID_STYLESHEET_FILTER, wxEVT_COMMAND_TEXT_UPDATED, StylePanel::onStylesheetFilterUpdate)
EVT_BUTTON (ID_STYLE_USE_FOR_ALL, StylePanel::onUseForAll)
EVT_CHECKBOX (ID_STYLE_USE_CUSTOM, StylePanel::onUseCustom)
+2 -2
View File
@@ -266,8 +266,8 @@ void SymbolPartList::onChar(wxKeyEvent& ev) {
}
break;
default:
// See gui/value/text.cpp
#if defined UNICODE
// See gui/value/text.cpp
#if defined UNICODE
if (ev.GetUnicodeKey() >= WXK_SPACE) {
#elif defined __WXMSW__
if (ev.GetKeyCode() >= _(' ') && ev.GetKeyCode() == (int)ev.GetRawKeyCode()) {
+25 -10
View File
@@ -11,6 +11,7 @@
#include <gui/image_slice_window.hpp>
#include <data/format/clipboard.hpp>
#include <data/action/value.hpp>
#include <data/card.hpp>
#include <wx/clipbrd.h>
#include <gui/util.hpp>
@@ -19,7 +20,19 @@
IMPLEMENT_VALUE_EDITOR(Image) {}
bool ImageValueEditor::onLeftDClick(const RealPoint&, wxMouseEvent&) {
String filename = wxFileSelector(_("Open image file"), settings.default_image_dir, _(""), _(""),
String directory = settings.default_image_dir;
String filename = _("");
CardP card = parent.getCard();
String cardname = card ? card->identification() : _("clipboard");
if (ImageSliceWindow::previously_used_settings_path.find(cardname) != ImageSliceWindow::previously_used_settings_path.end()) {
String filepath = ImageSliceWindow::previously_used_settings_path[cardname];
size_t pos = filepath.rfind(wxFileName::GetPathSeparator());
if (pos != String::npos) {
directory = filepath.substr(0, pos+1);
filename = filepath.substr(pos+1);
}
}
filename = wxFileSelector(_("Open image file"), directory, filename, _(""),
_("All images|*.bmp;*.jpg;*.jpeg;*.png;*.gif;*.tif;*.tiff|Windows bitmaps (*.bmp)|*.bmp|JPEG images (*.jpg;*.jpeg)|*.jpg;*.jpeg|PNG images (*.png)|*.png|GIF images (*.gif)|*.gif|TIFF images (*.tif;*.tiff)|*.tif;*.tiff"),
wxFD_OPEN, wxGetTopLevelParent(&editor()));
if (!filename.empty()) {
@@ -29,28 +42,28 @@ bool ImageValueEditor::onLeftDClick(const RealPoint&, wxMouseEvent&) {
wxLogNull noLog;
image = wxImage(filename);
}
sliceImage(image);
sliceImage(image, filename, cardname);
}
return true;
}
void ImageValueEditor::sliceImage(const Image& image) {
void ImageValueEditor::sliceImage(const Image& image, const String& filename, const String& cardname) {
if (!image.Ok()) return;
// mask
GeneratedImage::Options options((int)style().width, (int)style().height, &parent.getStylePackage(), &parent.getLocalPackage());
AlphaMask mask;
style().mask.getNoCache(options,mask);
// slice
ImageSliceWindow s(wxGetTopLevelParent(&editor()), image, style().getSize(), mask);
ImageSliceWindow s(wxGetTopLevelParent(&editor()), image, filename, cardname, style().getSize(), mask);
// clicked ok?
if (s.ShowModal() == wxID_OK) {
// store the image into the set
LocalFileName new_image_file = getLocalPackage().newFileName(field().name, settings.internal_image_extension ? _(".png") : _("")); // a new unique name in the package
// Specify a desired size based on the stylesheet and a scale multiplier defined within the user's settings.
// Storing at a greater than 100% resolution allows for better exports >100%, but may change how images look when filters (sharpen) are applied.
// It also disrupts some of the patterns in use for doing popout planeswalkers since you have to do the math at both scales.
// Additionally, this bloats the set file size as even under-resolution images are upscaled to the new minimum size.
// Specify a desired size based on the stylesheet and a scale multiplier defined within the user's settings.
// Storing at a greater than 100% resolution allows for better exports >100%, but may change how images look when filters (sharpen) are applied.
// It also disrupts some of the patterns in use for doing popout planeswalkers since you have to do the math at both scales.
// Additionally, this bloats the set file size as even under-resolution images are upscaled to the new minimum size.
Image img = s.getImage(settings.internal_scale);
img.SaveFile(getLocalPackage().nameOut(new_image_file), wxBITMAP_TYPE_PNG); // always use PNG images, see #69. Disk space is cheap anyway.
addAction(value_action(valueP(), new_image_file));
@@ -88,7 +101,9 @@ bool ImageValueEditor::doPaste() {
wxTheClipboard->Close();
if (!ok) return false;
// slice
sliceImage(data.GetBitmap().ConvertToImage());
CardP card = parent.getCard();
String cardname = card ? card->identification() : _("clipboard");
sliceImage(data.GetBitmap().ConvertToImage(), _("clipboard"), cardname);
return true;
}
+2 -2
View File
@@ -32,7 +32,7 @@ public:
bool onChar(wxKeyEvent&) override;
private:
// Open the image slice window showing the give image
void sliceImage(const Image&);
// Open the image slice window showing the given image
void sliceImage(const Image&, const String& filename, const String& cardname);
};
+9 -9
View File
@@ -482,13 +482,13 @@ bool TextValueEditor::onChar(wxKeyEvent& ev) {
}
}
return true;
default:
#if defined UNICODE
// I think in theory this works because the UnicodeKey is intended to be only character values.
// See the following link for pretty much an exact example of this type of handling.
// https://docs.wxwidgets.org/3.0/classwx_key_event.html#a3dccc5a254770931e5d8066ef47e7fb0
// Most of the special keys (<32) are handled in the case structure above anyways.
// I tried to replicate the Numpad issue mentioned below, but couldn't - so unclear if that's still relevant.
default:
#if defined UNICODE
// I think in theory this works because the UnicodeKey is intended to be only character values.
// See the following link for pretty much an exact example of this type of handling.
// https://docs.wxwidgets.org/3.0/classwx_key_event.html#a3dccc5a254770931e5d8066ef47e7fb0
// Most of the special keys (<32) are handled in the case structure above anyways.
// I tried to replicate the Numpad issue mentioned below, but couldn't - so unclear if that's still relevant.
if (ev.GetUnicodeKey() >= WXK_SPACE) {
#elif defined __WXMSW__
if (ev.GetKeyCode() >= _(' ') && ev.GetKeyCode() == (int)ev.GetRawKeyCode()) {
@@ -822,7 +822,7 @@ bool TextValueEditor::hasFormat(int type) const {
case ID_FORMAT_BOLD:
return is_in_tag(value().value(), _("<b"), selection_start_i, selection_end_i);
case ID_FORMAT_ITALIC:
return is_in_tag(value().value(), _("<i"), selection_start_i, selection_end_i);
return is_in_tag(value().value(), _("<i"), selection_start_i, selection_end_i);
case ID_FORMAT_UNDERLINE:
return is_in_tag(value().value(), _("<u"), selection_start_i, selection_end_i);
case ID_FORMAT_SYMBOL:
@@ -850,7 +850,7 @@ void TextValueEditor::doFormat(int type) {
case ID_FORMAT_ITALIC: {
addAction(toggle_format_action(valueP(), _("i"), selection_start_i, selection_end_i, selection_start, selection_end, _("Italic")));
break;
}
}
case ID_FORMAT_UNDERLINE: {
addAction(toggle_format_action(valueP(), _("u"), selection_start_i, selection_end_i, selection_start, selection_end, _("Underline")));
break;
+5 -5
View File
@@ -15,7 +15,7 @@
#include <data/locale.hpp>
#include <data/installer.hpp>
#include <data/format/formats.hpp>
#include <data/font.hpp>
#include <data/font.hpp>
#include <cli/cli_main.hpp>
#include <cli/text_io_handler.hpp>
#include <gui/welcome_window.hpp>
@@ -81,13 +81,13 @@ void nag_about_ascii_version() {
int MSE::OnRun() {
try {
#ifdef __WXMSW__
#ifdef __WXMSW__
SetAppName(_("Magic Set Editor"));
#else
#else
// Platform friendly appname
SetAppName(_("magicseteditor"));
#endif
Font::PreloadResourceFonts(true);
#endif
Font::PreloadResourceFonts(true);
wxInitAllImageHandlers();
wxFileSystem::AddHandler(new wxInternetFSHandler); // needed for update checker
wxSocketBase::Initialize();
+2 -2
View File
@@ -78,7 +78,7 @@ private:
if (is_tag(text, tag_start, _( "<b"))) bold += 1;
else if (is_tag(text, tag_start, _("</b"))) bold -= 1;
else if (is_tag(text, tag_start, _( "<i"))) italic += 1;
else if (is_tag(text, tag_start, _("</i"))) italic -= 1;
else if (is_tag(text, tag_start, _("</i"))) italic -= 1;
else if (is_tag(text, tag_start, _("<u"))) underline += 1;
else if (is_tag(text, tag_start, _("</u"))) underline -= 1;
else if (is_tag(text, tag_start, _( "<sym"))) symbol += 1;
@@ -301,7 +301,7 @@ private:
(kwpph > 0 ? FONT_SOFT : FONT_NORMAL) |
(code > 0 ? FONT_CODE : FONT_NORMAL) |
(code_kw > 0 ? FONT_CODE_KW : FONT_NORMAL) |
(code_string > 0 ? FONT_CODE_STRING : FONT_NORMAL),
(code_string > 0 ? FONT_CODE_STRING : FONT_NORMAL),
underline > 0,
fonts.empty() ? nullptr : &fonts.back(),
param > 0 || param_ref > 0
+4 -3
View File
@@ -49,9 +49,10 @@ bool prepare_choice_viewer(RotatedDC& dc, ValueViewer& viewer, ChoiceStyle& styl
RealSize size;
img.generateCached(img_options, &style.mask, &combine, &bitmap, &image, &size);
// store content properties
if (style.content_width != size.width || style.content_height != size.height) {
style.content_width = size.width / dc.getZoom();
style.content_height = size.height / dc.getZoom();
double zoom = dc.getZoom();
if (style.content_width != size.width / zoom || style.content_height != size.height / zoom) {
style.content_width = size.width / zoom;
style.content_height = size.height / zoom;
return true;
}
}
+1 -1
View File
@@ -110,7 +110,7 @@ Bitmap ImageValueViewer::imagePlaceholder(const Rotation& rot, UInt w, UInt h, c
if (!default_image.Ok()) {
draw_checker(dc, rect);
}
else {
else {
if (default_image.HasAlpha()) bmp.UseAlpha(true);
dc.DrawImage(default_image, RealPoint(0,0));
}
+11 -11
View File
@@ -34,16 +34,16 @@ void InfoValueViewer::draw(RotatedDC& dc) {
style().padding_top,
-style().padding_left - style().padding_right,
-style().padding_top - style().padding_bottom
);
// for some reason, while inside the style tab, this value is empty
// so as a hack for now, if the value is empty, go fetch the caption instead
if (value().value.empty()) {
RealSize size = dc.GetTextExtent(field().caption.get());
dc.DrawText(field().caption.get(), align_in_rect(style().alignment, size, rect));
}
// this is what should happen
else {
RealSize size = dc.GetTextExtent(value().value);
dc.DrawText(value().value, align_in_rect(style().alignment, size, rect));
);
// for some reason, while inside the style tab, this value is empty
// so as a hack for now, if the value is empty, go fetch the caption instead
if (value().value.empty()) {
RealSize size = dc.GetTextExtent(field().caption.get());
dc.DrawText(field().caption.get(), align_in_rect(style().alignment, size, rect));
}
// this is what should happen
else {
RealSize size = dc.GetTextExtent(value().value);
dc.DrawText(value().value, align_in_rect(style().alignment, size, rect));
}
}
+5 -5
View File
@@ -279,14 +279,14 @@ void Context::setVariable(Variable name, const ScriptValueP& value) {
// keep shadow copy
Binding bind = {name, var};
shadowed.push_back(bind);
}
if (!var.global_scope) {
var.global_scope = false;
}
if (!var.global_scope) {
var.global_scope = false;
}
var.level = level;
var.value = value;
}
}
void Context::setGlobalVariable(Variable name, const ScriptValueP& value) {
#ifdef _DEBUG
assert((size_t)name < variable_names.size());
+2 -2
View File
@@ -53,7 +53,7 @@ public:
/// Set a variable to a new value (in the current scope)
void setVariable(const String& name, const ScriptValueP& value);
/// Set a variable to a new value (in the current scope)
void setVariable(Variable name, const ScriptValueP& value);
void setVariable(Variable name, const ScriptValueP& value);
void setGlobalVariable(Variable name, const ScriptValueP& value);
/// Get the value of a variable, throws if it not set
@@ -89,7 +89,7 @@ public:// public for FOR_EACH
struct VariableValue {
VariableValue() : level(0) {}
unsigned int level; ///< Scope level on which this variable was set
ScriptValueP value; ///< Value of this variable
ScriptValueP value; ///< Value of this variable
bool global_scope = false; ///< Is this variable globally scoped?
};
/// Record of a variable binding that is being shadowed (overwritten) by another binding
+72 -26
View File
@@ -25,21 +25,24 @@
#include <wx/stdpaths.h>
#include <wx/wfstream.h>
#include <boost/json.hpp>
// ----------------------------------------------------------------------------- : Debugging
SCRIPT_FUNCTION(get_mse_version) {
SCRIPT_RETURN(app_version.toString());
}
}
SCRIPT_FUNCTION(get_mse_path) {
wxFileName app_path(wxStandardPaths::Get().GetExecutablePath());
String app_folder = app_path.GetPath();
SCRIPT_FUNCTION(get_mse_path) {
wxFileName app_path(wxStandardPaths::Get().GetExecutablePath());
String app_folder = app_path.GetPath();
app_folder.Replace("\\", "/");
SCRIPT_RETURN(app_folder);
}
SCRIPT_FUNCTION(get_mse_locale) {
SCRIPT_RETURN(settings.locale);
}
SCRIPT_FUNCTION(trace) {
SCRIPT_PARAM_C(String, input);
#if defined(_DEBUG) && 0
@@ -82,13 +85,13 @@ SCRIPT_FUNCTION(error) {
queue_message(MESSAGE_ERROR, input);
}
return script_nil;
}
}
SCRIPT_FUNCTION(exists_in_package) {
SCRIPT_PARAM_C(String, input);
bool result = package_manager.existsInPackage(input);
SCRIPT_RETURN(result);
}
}
// ----------------------------------------------------------------------------- : Conversion
@@ -264,11 +267,11 @@ SCRIPT_FUNCTION(to_code) {
SCRIPT_FUNCTION(to_json) {
SCRIPT_PARAM_C(ScriptValueP, input);
SCRIPT_PARAM_C(Set*, set);
SCRIPT_PARAM_DEFAULT(bool, pretty_print, true);
boost::json::value jv = mse_to_json(input, set);
SCRIPT_PARAM_DEFAULT(bool, pretty_print, true);
boost::json::value jv = mse_to_json(input, set);
queue_message(MESSAGE_ERROR, json_pretty_print(jv));
queue_message(MESSAGE_ERROR, json_pretty_print(jv));
if (pretty_print) return to_script(json_pretty_print(jv));
else return to_script(json_ugly_print(jv));
}
@@ -704,31 +707,31 @@ SCRIPT_FUNCTION(random_select_many) {
ret->value.resize(count);
}
return ret;
}
}
SCRIPT_FUNCTION(make_map) {
SCRIPT_PARAM(ScriptValueP, keys);
SCRIPT_PARAM(ScriptValueP, values);
ScriptValueP keys_it = keys->makeIterator();
ScriptValueP key;
ScriptValueP key;
ScriptValueP values_it = values->makeIterator();
ScriptValueP value;
ScriptCustomCollectionP map = make_intrusive<ScriptCustomCollection>();
while (key = keys_it->next()) {
if (key == script_nil) continue;
if (value = values_it->next()) {
ScriptValueP value;
ScriptCustomCollectionP map = make_intrusive<ScriptCustomCollection>();
while (key = keys_it->next()) {
if (key == script_nil) continue;
if (value = values_it->next()) {
map->key_value[key->toString()] = value;
}
}
else {
queue_message(MESSAGE_WARNING, "More keys than values given in function make_map!");
queue_message(MESSAGE_WARNING, "More keys than values given in function make_map!");
break;
}
}
if (value = values_it->next()) {
}
if (value = values_it->next()) {
queue_message(MESSAGE_WARNING, "More values than keys given in function make_map!");
}
}
return map;
}
}
SCRIPT_FUNCTION(get_card_styling) {
SCRIPT_PARAM_C(ScriptValueP, input);
@@ -752,6 +755,45 @@ SCRIPT_FUNCTION(get_card_stylesheet) {
throw ScriptError(_("invalid set or card argument"));
}
SCRIPT_FUNCTION(get_card_from_uid) {
SCRIPT_PARAM_C(Set*, set);
SCRIPT_PARAM_C(String, input);
FOR_EACH(other_card, set->cards) {
if (other_card->uid == input) SCRIPT_RETURN(other_card);
}
return script_nil;
}
SCRIPT_FUNCTION(get_card_from_link) {
SCRIPT_PARAM_C(Set*, set);
SCRIPT_PARAM_C(CardP, card);
SCRIPT_PARAM_C(String, input);
String trimmed_input = input.Trim().Trim(false);
String uid = card->linked_relation_1 == trimmed_input ? card->linked_card_1 :
card->linked_relation_2 == trimmed_input ? card->linked_card_2 :
card->linked_relation_3 == trimmed_input ? card->linked_card_3 :
card->linked_relation_4 == trimmed_input ? card->linked_card_4 :
wxEmptyString;
if (uid == wxEmptyString) return script_nil;
FOR_EACH(other_card, set->cards) {
if (other_card->uid == uid) SCRIPT_RETURN(other_card);
}
return script_nil;
}
SCRIPT_FUNCTION(has_link) {
SCRIPT_PARAM_C(CardP, card);
SCRIPT_PARAM_C(String, input);
String trimmed_input = input.Trim().Trim(false);
if (
card->linked_relation_1 == trimmed_input ||
card->linked_relation_2 == trimmed_input ||
card->linked_relation_3 == trimmed_input ||
card->linked_relation_4 == trimmed_input
) SCRIPT_RETURN(true);
SCRIPT_RETURN(false);
}
// ----------------------------------------------------------------------------- : Keywords
@@ -826,6 +868,7 @@ SCRIPT_FUNCTION(rule) {
void init_script_basic_functions(Context& ctx) {
// debugging
ctx.setVariable(_("get_mse_version"), script_get_mse_version);
ctx.setVariable(_("get_mse_locale"), script_get_mse_locale);
ctx.setVariable(_("get_mse_path"), script_get_mse_path);
ctx.setVariable(_("trace"), script_trace);
ctx.setVariable(_("warning"), script_warning);
@@ -842,8 +885,8 @@ void init_script_basic_functions(Context& ctx) {
ctx.setVariable(_("to_code"), script_to_code);
ctx.setVariable(_("to_json"), script_to_json);
ctx.setVariable(_("from_json"), script_from_json);
ctx.setVariable(_("type_name"), script_type_name);
ctx.setVariable(_("make_map"), script_make_map);
ctx.setVariable(_("type_name"), script_type_name);
ctx.setVariable(_("make_map"), script_make_map);
ctx.setVariable(_("get_card_styling"), script_get_card_styling);
ctx.setVariable(_("get_card_stylesheet"), script_get_card_stylesheet);
// math
@@ -891,6 +934,9 @@ void init_script_basic_functions(Context& ctx) {
ctx.setVariable(_("random_shuffle"), script_random_shuffle);
ctx.setVariable(_("random_select"), script_random_select);
ctx.setVariable(_("random_select_many"), script_random_select_many);
ctx.setVariable(_("get_card_from_uid"), script_get_card_from_uid);
ctx.setVariable(_("get_card_from_link"), script_get_card_from_link);
ctx.setVariable(_("has_link"), script_has_link);
// keyword
ctx.setVariable(_("expand_keywords"), script_expand_keywords);
ctx.setVariable(_("expand_keywords_rule"), make_intrusive<ScriptRule>(script_expand_keywords));
+81 -81
View File
@@ -15,131 +15,131 @@
#include <data/field/choice.hpp>
#include <data/field/package_choice.hpp>
#include <data/field/color.hpp>
#include <data/field/image.hpp>
#include <data/game.hpp>
#include <data/field/image.hpp>
#include <data/game.hpp>
#include <data/stylesheet.hpp>
#include <data/card.hpp>
#include <util/error.hpp>
// ----------------------------------------------------------------------------- : new_card
// ----------------------------------------------------------------------------- : new_card
SCRIPT_FUNCTION(new_card) {
SCRIPT_PARAM(GameP, game);
SCRIPT_PARAM(GameP, game);
SCRIPT_OPTIONAL_PARAM_(bool, ignore_field_not_found);
// create a new card object
CardP new_card = make_intrusive<Card>(*game);
// iterate on the given key/value pairs
SCRIPT_PARAM(ScriptValueP, input);
ScriptValueP it = input->makeIterator();
ScriptValueP key;
ScriptValueP key;
while (ScriptValueP value = it->next(&key)) {
assert(key);
if (key == script_nil) continue;
String key_name = key->toString();
// check if the given value is for a built-in field
assert(key);
if (key == script_nil) continue;
String key_name = key->toString();
// check if the given value is for a built-in field
if (set_builtin_container(*game, new_card, value, key_name, ignore_field_not_found)) continue;
// find the field value (container) that corresponds to the given value
Value* container = get_card_field_container(*game, new_card->data, key_name, ignore_field_not_found);
if (container == nullptr) continue;
FieldP field = container->fieldP;
// if the field has a construction script, set the value and card context variables to be the given value and this card, run script
// find the field value (container) that corresponds to the given value
Value* container = get_card_field_container(*game, new_card->data, key_name, ignore_field_not_found);
if (container == nullptr) continue;
FieldP field = container->fieldP;
// if the field has a construction script, set the value and card context variables to be the given value and this card, run script
if (field->import_script) {
ScriptValueP ctx_value = ctx.getVariableOpt(SCRIPT_VAR_value);
ScriptValueP ctx_card = ctx.getVariableOpt(SCRIPT_VAR_card);
ctx.setVariable(SCRIPT_VAR_value, value);
ctx.setVariable(SCRIPT_VAR_card, to_script(new_card));
ScriptValueP script_input = field->import_script.invoke(ctx, true);
// if the script result is a collection, iterate on the key/value pairs
// treat the keys as field names and the values as what to populate those fields with
if (script_input->type() == SCRIPT_COLLECTION) {
ctx.setVariable(SCRIPT_VAR_value, value);
ctx.setVariable(SCRIPT_VAR_card, to_script(new_card));
ScriptValueP script_input = field->import_script.invoke(ctx, true);
// if the script result is a collection, iterate on the key/value pairs
// treat the keys as field names and the values as what to populate those fields with
if (script_input->type() == SCRIPT_COLLECTION) {
ScriptValueP script_it = script_input->makeIterator();
ScriptValueP script_key;
while (ScriptValueP script_value = script_it->next(&script_key)) {
assert(script_key);
ScriptValueP script_key;
while (ScriptValueP script_value = script_it->next(&script_key)) {
assert(script_key);
if (script_key == script_nil) continue;
String script_key_name = script_key->toString();
// check if the script value is for a built-in field
String script_key_name = script_key->toString();
// check if the script value is for a built-in field
if (set_builtin_container(*game, new_card, script_value, script_key_name, ignore_field_not_found)) continue;
// find the field value that corresponds to the script value
Value* script_container = get_card_field_container(*game, new_card->data, script_key_name, ignore_field_not_found);
if (script_container == nullptr) continue;
// set the field value to the script value
set_container(script_container, script_value, script_key_name);
}
}
// if the script result is not a collection, simply set the field value to the script value
else {
set_container(container, script_input, key_name);
}
// restore old value and card context variables
if (ctx_value) ctx.setVariable(SCRIPT_VAR_value, ctx_value);
// find the field value that corresponds to the script value
Value* script_container = get_card_field_container(*game, new_card->data, script_key_name, ignore_field_not_found);
if (script_container == nullptr) continue;
// set the field value to the script value
set_container(script_container, script_value, script_key_name);
}
}
// if the script result is not a collection, simply set the field value to the script value
else {
set_container(container, script_input, key_name);
}
// restore old value and card context variables
if (ctx_value) ctx.setVariable(SCRIPT_VAR_value, ctx_value);
if (ctx_card) ctx.setVariable(SCRIPT_VAR_card, ctx_card);
}
// if the field has no construction script, simply set the field value to the given value
else {
set_container(container, value, key_name);
}
}
// if the game has a construction script, set the card context variable to be this card, run script
}
}
// if the game has a construction script, set the card context variable to be this card, run script
if (game->import_script) {
ScriptValueP ctx_card = ctx.getVariableOpt(SCRIPT_VAR_card);
ctx.setVariable(SCRIPT_VAR_card, to_script(new_card));
ScriptValueP script_input = game->import_script.invoke(ctx, true);
if (script_input->type() == SCRIPT_COLLECTION) {
ctx.setVariable(SCRIPT_VAR_card, to_script(new_card));
ScriptValueP script_input = game->import_script.invoke(ctx, true);
if (script_input->type() == SCRIPT_COLLECTION) {
// iterate on the key/value pairs given by the script
ScriptValueP script_it = script_input->makeIterator();
ScriptValueP script_key;
while (ScriptValueP script_value = script_it->next(&script_key)) {
assert(script_key);
ScriptValueP script_key;
while (ScriptValueP script_value = script_it->next(&script_key)) {
assert(script_key);
if (script_key == script_nil) continue;
String script_key_name = script_key->toString();
// check if the script value is for a built-in field
String script_key_name = script_key->toString();
// check if the script value is for a built-in field
if (set_builtin_container(*game, new_card, script_value, script_key_name, ignore_field_not_found)) continue;
// find the field value that corresponds to the script value
Value* script_container = get_card_field_container(*game, new_card->data, script_key_name, ignore_field_not_found);
if (script_container == nullptr) continue;
// set the field value to the script value
set_container(script_container, script_value, script_key_name);
}
}
else {
queue_message(MESSAGE_ERROR, _ERROR_("game import script not map"));
}
// restore old context card
// find the field value that corresponds to the script value
Value* script_container = get_card_field_container(*game, new_card->data, script_key_name, ignore_field_not_found);
if (script_container == nullptr) continue;
// set the field value to the script value
set_container(script_container, script_value, script_key_name);
}
}
else {
queue_message(MESSAGE_ERROR, _ERROR_("game import script not map"));
}
// restore old context card
if (ctx_card) ctx.setVariable(SCRIPT_VAR_card, ctx_card);
}
}
SCRIPT_RETURN(new_card);
}
SCRIPT_FUNCTION(add_card_to_set) {
SCRIPT_PARAM_C(ScriptValueP, input);
SCRIPT_PARAM_C(ScriptValueP, set);
ScriptObject<Set*>* s = dynamic_cast<ScriptObject<Set*>*>(set.get());
if (s) {
if (s) {
Set& _set = *s->getValue();
ScriptObject<CardP>* c = dynamic_cast<ScriptObject<CardP>*>(input.get());
if (c) {
CardP _card = c->getValue();
_set.actions.addAction(make_unique<AddCardAction>(ADD, _set, _card));
SCRIPT_RETURN(true);
}
if (input->type() == SCRIPT_COLLECTION) {
vector<CardP> _cards;
ScriptObject<CardP>* c = dynamic_cast<ScriptObject<CardP>*>(input.get());
if (c) {
CardP _card = c->getValue();
_set.actions.addAction(make_unique<AddCardAction>(ADD, _set, _card));
SCRIPT_RETURN(true);
}
if (input->type() == SCRIPT_COLLECTION) {
vector<CardP> _cards;
ScriptValueP it = input->makeIterator();
ScriptValueP key;
while (ScriptValueP value = it->next(&key)) {
c = dynamic_cast<ScriptObject<CardP>*>(value.get());
if (c) {
_cards.push_back(c->getValue());
}
ScriptValueP key;
while (ScriptValueP value = it->next(&key)) {
c = dynamic_cast<ScriptObject<CardP>*>(value.get());
if (c) {
_cards.push_back(c->getValue());
}
}
if (!_cards.empty()) {
_set.actions.addAction(make_unique<AddCardAction>(ADD, _set, _cards));
_set.actions.addAction(make_unique<AddCardAction>(ADD, _set, _cards));
SCRIPT_RETURN(true);
}
}
}
}
SCRIPT_RETURN(false);
}
SCRIPT_RETURN(false);
}
// ----------------------------------------------------------------------------- : Init
+1 -1
View File
@@ -152,7 +152,7 @@ SCRIPT_FUNCTION(english_number_ordinal) {
// ----------------------------------------------------------------------------- : Singular/plural
String english_singular(const String& str) {
String english_singular(const String& str) {
if (str.Lower() == _("plains")) return str;
if (str.size() > 3 && is_substr(str, str.size()-3, _("ies"))) {
return str.substr(0, str.size() - 3) + _("y");
+6 -6
View File
@@ -54,9 +54,9 @@ String get_export_full_path(String& rel_name) {
return fn.GetFullPath();
}
void ensure_dir_valid(String& path) {
void ensure_dir_valid(String& path) {
if (!wxDirExists(path)) {
wxFileName filename = path;
wxFileName filename = path;
filename.Mkdir(wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL);
}
}
@@ -216,7 +216,7 @@ String to_html(const String& str_in, const SymbolFontP& symbol_font, double symb
String str = remove_tag_contents(str_in,_("<sep-soft"));
String ret;
Tag bold (_("<b>"), _("</b>")),
italic(_("<i>"), _("</i>")),
italic(_("<i>"), _("</i>")),
underline(_("<u>"), _("</u>")),
symbol(_("<span class=\"symbol\">"), _("</span>"));
TagStack tags;
@@ -232,7 +232,7 @@ String to_html(const String& str_in, const SymbolFontP& symbol_font, double symb
} else if (is_substr(str, i, _("i"))) {
tags.open (ret, italic);
} else if (is_substr(str, i, _("/i"))) {
tags.close(ret, italic);
tags.close(ret, italic);
} else if (is_substr(str, i, _("u"))) {
tags.open(ret, underline);
} else if (is_substr(str, i, _("/u"))) {
@@ -312,7 +312,7 @@ String to_bbcode(const String& str_in) {
String str = remove_tag_contents(str_in,_("<sep-soft"));
String ret;
Tag bold (_("[b]"), _("[/b]")),
italic(_("[i]"), _("[/i]")),
italic(_("[i]"), _("[/i]")),
underline(_("[u]"), _("[/u]"));
TagStack tags;
String symbols;
@@ -332,7 +332,7 @@ String to_bbcode(const String& str_in) {
tags.open(ret, underline);
} else if (is_substr(str, i, _("/u"))) {
tags.close(ret, underline);
}
}
/*else if (is_substr(str, i, _("sym"))) {
tags.open (ret, symbol);
} else if (is_substr(str, i, _("/sym"))) {
+55 -44
View File
@@ -15,11 +15,11 @@
#include <data/card.hpp>
#include <data/stylesheet.hpp>
#include <data/symbol.hpp>
#include <data/field/symbol.hpp>
#include <data/field/symbol.hpp>
#include <data/format/formats.hpp>
#include <gfx/generated_image.hpp>
#include <render/symbol/filter.hpp>
#include <cli/text_io_handler.hpp> // for MSE_CLI
#include <cli/text_io_handler.hpp> // for MSE_CLI
void parse_enum(const String&, ImageCombine& out);
@@ -29,45 +29,55 @@ SCRIPT_FUNCTION(to_image) {
SCRIPT_PARAM_C(GeneratedImageP, input);
return input;
}
SCRIPT_FUNCTION(to_card_image) {
SCRIPT_PARAM(Set*, set);
SCRIPT_PARAM(CardP, input);
SCRIPT_PARAM_DEFAULT(double, zoom, 100);
SCRIPT_PARAM_DEFAULT(Degrees, angle, 0);
SCRIPT_PARAM_DEFAULT(bool, use_user_settings, false);
if (use_user_settings) {
// Use the User's Preferences for Export Zoom and Angle settings.
return make_intrusive<ArbitraryImage>(export_bitmap(set, input).ConvertToImage());
} else {
// Use the provided (or defaulted) Zoom and Angle.
return make_intrusive<ArbitraryImage>(export_bitmap(set, input, (zoom / 100), deg_to_rad(angle)).ConvertToImage());
}
}
SCRIPT_FUNCTION(import_image) {
SCRIPT_PARAM(Set*, set);
SCRIPT_PARAM(String, input);
auto extImg = make_intrusive<ExternalImage>(input);
if (cli.haveConsole()) // makes sure generate() is called, but only once, when using the CLI
extImg->generate(GeneratedImage::Options(0, 0, set->stylesheet.get(), set));
return extImg;
}
SCRIPT_FUNCTION(to_card_image) {
SCRIPT_PARAM(Set*, set);
SCRIPT_PARAM(CardP, input);
SCRIPT_PARAM_DEFAULT(double, zoom, 100);
SCRIPT_PARAM_DEFAULT(Degrees, angle, 0);
SCRIPT_PARAM_DEFAULT(bool, use_user_settings, false);
if (use_user_settings) {
// Use the User's Preferences for Export Zoom and Angle settings.
return make_intrusive<ArbitraryImage>(export_bitmap(set, input).ConvertToImage());
} else {
// Use the provided (or defaulted) Zoom and Angle.
return make_intrusive<ArbitraryImage>(export_bitmap(set, input, (zoom / 100), deg_to_rad(angle)).ConvertToImage());
}
}
SCRIPT_FUNCTION(import_image) {
SCRIPT_PARAM(Set*, set);
SCRIPT_PARAM(String, input);
auto extImg = make_intrusive<ExternalImage>(input);
if (cli.haveConsole()) // makes sure generate() is called, but only once, when using the CLI
extImg->generate(GeneratedImage::Options(0, 0, set->stylesheet.get(), set));
return extImg;
}
// ----------------------------------------------------------------------------- : Image functions
SCRIPT_FUNCTION(width_of) {
SCRIPT_PARAM(Set*, set);
SCRIPT_PARAM(GeneratedImageP, input);
Image image = input->generate(GeneratedImage::Options(0, 0, set->stylesheet.get()));
SCRIPT_RETURN(image.GetWidth());
}
SCRIPT_FUNCTION(height_of) {
SCRIPT_PARAM(Set*, set);
SCRIPT_PARAM(GeneratedImageP, input);
Image image = input->generate(GeneratedImage::Options(0, 0, set->stylesheet.get()));
SCRIPT_RETURN(image.GetHeight());
SCRIPT_FUNCTION(width_of) {
SCRIPT_PARAM(Set*, set);
SCRIPT_PARAM(GeneratedImageP, input);
Image image = input->generate(GeneratedImage::Options(0, 0, set->stylesheet.get()));
SCRIPT_RETURN(image.GetWidth());
}
SCRIPT_FUNCTION(height_of) {
SCRIPT_PARAM(Set*, set);
SCRIPT_PARAM(GeneratedImageP, input);
Image image = input->generate(GeneratedImage::Options(0, 0, set->stylesheet.get()));
SCRIPT_RETURN(image.GetHeight());
}
SCRIPT_FUNCTION(dimensions_of) {
SCRIPT_PARAM(Set*, set);
SCRIPT_PARAM(GeneratedImageP, input);
Image image = input->generate(GeneratedImage::Options(0, 0, set->stylesheet.get()));
ScriptCustomCollectionP ret(new ScriptCustomCollection());
ret->value.push_back(to_script(image.GetWidth()));
ret->value.push_back(to_script(image.GetHeight()));
return ret;
}
SCRIPT_FUNCTION(insert_image) {
@@ -77,7 +87,7 @@ SCRIPT_FUNCTION(insert_image) {
SCRIPT_PARAM(int, offset_y);
SCRIPT_OPTIONAL_PARAM_(Color, background_color);
return make_intrusive<InsertedImage>(base_image, inserted_image, offset_x, offset_y, background_color);
}
}
SCRIPT_FUNCTION(linear_blend) {
SCRIPT_PARAM(GeneratedImageP, image1);
@@ -273,10 +283,11 @@ SCRIPT_FUNCTION(built_in_image) {
// ----------------------------------------------------------------------------- : Init
void init_script_image_functions(Context& ctx) {
ctx.setVariable(_("to_image"), script_to_image);
ctx.setVariable(_("to_card_image"), script_to_card_image);
ctx.setVariable(_("width_of"), script_width_of);
ctx.setVariable(_("to_image"), script_to_image);
ctx.setVariable(_("to_card_image"), script_to_card_image);
ctx.setVariable(_("width_of"), script_width_of);
ctx.setVariable(_("height_of"), script_height_of);
ctx.setVariable(_("dimensions_of"), script_dimensions_of);
ctx.setVariable(_("linear_blend"), script_linear_blend);
ctx.setVariable(_("masked_blend"), script_masked_blend);
ctx.setVariable(_("combine_blend"), script_combine_blend);
@@ -298,5 +309,5 @@ void init_script_image_functions(Context& ctx) {
ctx.setVariable(_("drop_shadow"), script_drop_shadow);
ctx.setVariable(_("symbol_variation"), script_symbol_variation);
ctx.setVariable(_("built_in_image"), script_built_in_image);
ctx.setVariable(_("import_image"), script_import_image);
ctx.setVariable(_("import_image"), script_import_image);
}
+1 -1
View File
@@ -181,7 +181,7 @@ void TokenIterator::readToken() {
newline = true;
} else if (isSpace(c)) {
++pos;
// ignore
// ignore
} else if (is_substr(pos, end, "include localized file:")) {
pos += 23; // "include localized file:"
const char* newlines = "\r\n";
+2 -2
View File
@@ -73,8 +73,8 @@ void init_script_variables() {
Var(stylesheet);
Var(card_style);
Var(card);
Var(styling);
Var(extra_card_style);
Var(styling);
Var(extra_card_style);
Var(extra_card);
Var(value);
Var(condition);
+2 -2
View File
@@ -142,8 +142,8 @@ enum Variable
, SCRIPT_VAR_stylesheet
, SCRIPT_VAR_card_style
, SCRIPT_VAR_card
, SCRIPT_VAR_styling
, SCRIPT_VAR_extra_card_style
, SCRIPT_VAR_styling
, SCRIPT_VAR_extra_card_style
, SCRIPT_VAR_extra_card
, SCRIPT_VAR_value
, SCRIPT_VAR_condition
+11 -11
View File
@@ -39,14 +39,14 @@ Context& SetScriptContext::getContext(const StyleSheetP& stylesheet) {
ctx.setVariable(SCRIPT_VAR_set, make_intrusive<ScriptObject<Set*>>(&set));
ctx.setVariable(SCRIPT_VAR_game, to_script(set.game));
ctx.setVariable(SCRIPT_VAR_stylesheet, to_script(stylesheet));
ctx.setVariable(SCRIPT_VAR_card_style, to_script(&stylesheet->card_style));
// I'm not entirely clear on why a "dummy value" is necessary here.
// It doesn't appear that these are getting accessed until a card is found anyways, so they don't trip any errors that I could see.
ctx.setVariable(SCRIPT_VAR_card_style, to_script(&stylesheet->card_style));
// I'm not entirely clear on why a "dummy value" is necessary here.
// It doesn't appear that these are getting accessed until a card is found anyways, so they don't trip any errors that I could see.
// Retaining the format just for consistency in case there's something that I missed.
ctx.setVariable(SCRIPT_VAR_card, set.cards.empty() ? script_nil : to_script(set.cards.front())); // dummy value
ctx.setVariable(SCRIPT_VAR_styling, to_script(&set.stylingDataFor(*stylesheet)));
ctx.setVariable(SCRIPT_VAR_extra_card_style, to_script(&stylesheet->extra_card_style)); // dummy value
ctx.setVariable(SCRIPT_VAR_card, set.cards.empty() ? script_nil : to_script(set.cards.front())); // dummy value
ctx.setVariable(SCRIPT_VAR_styling, to_script(&set.stylingDataFor(*stylesheet)));
ctx.setVariable(SCRIPT_VAR_extra_card_style, to_script(&stylesheet->extra_card_style)); // dummy value
ctx.setVariable(SCRIPT_VAR_extra_card, set.cards.empty() ? script_nil : to_script(&set.cards.front()->extraDataFor(*stylesheet))); // dummy value
try {
@@ -73,13 +73,13 @@ Context& SetScriptContext::getContext(const CardP& card) {
Context& ctx = getContext(stylesheet);
if (card) {
ctx.setVariable(SCRIPT_VAR_card, to_script(card));
ctx.setVariable(SCRIPT_VAR_styling, to_script(&set.stylingDataFor(card)));
ctx.setVariable(SCRIPT_VAR_extra_card_style, to_script(&stylesheet->extra_card_style));
ctx.setVariable(SCRIPT_VAR_styling, to_script(&set.stylingDataFor(card)));
ctx.setVariable(SCRIPT_VAR_extra_card_style, to_script(&stylesheet->extra_card_style));
ctx.setVariable(SCRIPT_VAR_extra_card, to_script(&card->extraDataFor(*stylesheet)));
} else {
ctx.setVariable(SCRIPT_VAR_card, ScriptValueP());
ctx.setVariable(SCRIPT_VAR_styling, to_script(&set.stylingDataFor(*stylesheet)));
ctx.setVariable(SCRIPT_VAR_extra_card_style, script_nil);
ctx.setVariable(SCRIPT_VAR_styling, to_script(&set.stylingDataFor(*stylesheet)));
ctx.setVariable(SCRIPT_VAR_extra_card_style, script_nil);
ctx.setVariable(SCRIPT_VAR_extra_card, script_nil);
}
return ctx;
+2 -2
View File
@@ -196,7 +196,7 @@ bool Package::existsIn(const String& file) {
if (filename.find(_(".mse-")) != String::npos) {
return false;
}
}
}
unique_ptr<wxInputStream> stream;
if (it != files.end() && it->second.wasWritten()) {
@@ -216,7 +216,7 @@ bool Package::existsIn(const String& file) {
return false;
}
return stream && stream->IsOk();
}
}
unique_ptr<wxInputStream> Package::openIn(const String& file) {
if (!file.empty() && file.GetChar(0) == _('/')) {
+1 -1
View File
@@ -107,7 +107,7 @@ bool PackageManager::existsInPackage(const String& name) {
}
}
throw FileNotFoundError(name, _("No package name specified, use '/package/filename'"));
}
}
pair<unique_ptr<wxInputStream>,Packaged*> PackageManager::openFileFromPackage(Packaged* package, const String& name) {
if (!name.empty() && name.GetChar(0) == _('/')) {
+4 -4
View File
@@ -357,10 +357,10 @@ template <> void Reader::handle(Vector2D& vec) {
template <> void Reader::handle(LocalFileName& f) {
f = LocalFileName::fromReadString(this->getValue());
}
String Reader::addLocale(String filename) {
return filename + _("_") + settings.locale;
}
String Reader::addLocale(String filename) {
return filename + _("_") + settings.locale;
}
// ----------------------------------------------------------------------------- : EnumReader
+4 -4
View File
@@ -116,9 +116,9 @@ public:
/// The package being read from
inline Packaged* getPackage() const { return package; }
String addLocale(String);
String addLocale(String);
/// Set the value that will be returned by the next getValue() call (may mess up the state of the reader)
inline void setValue(const String& value) { state = UNHANDLED; previous_value = value; };
@@ -180,7 +180,7 @@ private:
/** Maybe the key is "include file" */
template <typename T>
void unknownKey(T& v) {
if (key == _("include_file") || key == _("include_localized_file")) {
if (key == _("include_file") || key == _("include_localized_file")) {
value = key == _("include_localized_file") ? addLocale(value) : value;
auto [stream, include_package] = openFileFromPackage(package, value);
Reader sub_reader(*stream, include_package, value, ignore_invalid);
+6 -6
View File
@@ -50,14 +50,14 @@ String tr(LocaleCategory cat, const String& key, DefaultLocaleFun def = warn_and
/// Translate 'key' in the for a Package using the current locale
[[deprecated]]
String tr(const Package&, const String& key, DefaultLocaleFun def);
[[deprecated]]
String tr(const Package&, const String& key, DefaultLocaleFun def);
[[deprecated]]
String tr(const String&, const String& key, DefaultLocaleFun def);
/// Translate 'key' in the for a Package using the current locale
[[deprecated]]
String tr(const Package&, const String& subcat, const String& key, DefaultLocaleFun def);
[[deprecated]]
String tr(const Package&, const String& subcat, const String& key, DefaultLocaleFun def);
[[deprecated]]
String tr(const String&, const String& subcat, const String& key, DefaultLocaleFun def);
/// A localized string for menus
@@ -99,9 +99,9 @@ String tr(const String&, const String& subcat, const String& key, DefaultLocaleF
#define _TOOLTIP_1_(s,a) format_string(_TOOLTIP_(s), a)
/// A localized string for tooltip labels, with 1 argument (printf style)
#define _LABEL_1_(s,a) format_string(_LABEL_(s), a)
#define _LABEL_1_(s,a) format_string(_LABEL_(s), a)
/// A localized string for tooltip labels, with 2 argument (printf style)
#define _LABEL_2_(s,a,b) format_string(_LABEL_(s), a, b)
#define _LABEL_2_(s,a,b) format_string(_LABEL_(s), a, b)
/// A localized string for tooltip labels, with 3 argument (printf style)
#define _LABEL_3_(s,a,b,c) format_string(_LABEL_(s), a, b, c)
+2 -2
View File
@@ -137,14 +137,14 @@ void uncanonical_name_form_in_place(String& str) {
}
}
String unified_form(String& str) {
String unified_form(String& str) {
str = trim(str);
for (String::iterator it = str.begin(); it != str.end(); ++it) {
if (*it == ' ') *it = '_';
else *it = toLower(*it);
}
return str;
}
}
String name_to_caption(const String& str) {
String ret;
+3 -3
View File
@@ -241,9 +241,9 @@ void uncanonical_name_form_in_place(String&);
inline String uncanonical_name_form(String s) {
uncanonical_name_form_in_place(s);
return s;
}
/// Convert a field name to canonical form, then to lower case, then trim it
}
/// Convert a field name to canonical form, then to lower case, then trim it
String unified_form(String&);
/// Convert a field name to a string that can be shown to the user
+6 -6
View File
@@ -645,9 +645,9 @@ String simplify_tagged(const String& str) {
// (where </tag> is the negation of tag)
bool add_or_cancel_tag(const String& tag, String& stack, bool all = false) {
if (all || starts_with(tag, _("/")) ||
starts_with(tag, _("b")) ||
starts_with(tag, _("i")) ||
starts_with(tag, _("u")) ||
starts_with(tag, _("b")) ||
starts_with(tag, _("i")) ||
starts_with(tag, _("u")) ||
starts_with(tag, _("sym"))) {
// cancel out all close tags, but not all open tags,
// so <xx></xx> is always removed
@@ -695,9 +695,9 @@ String simplify_tagged_overlap(const String& str) {
Char c = str.GetChar(i);
if (c == _('<')) {
String tag = tag_at(str, i);
if (starts_with(tag, _("b")) || starts_with(tag, _("/b")) ||
starts_with(tag, _("i")) || starts_with(tag, _("/i")) ||
starts_with(tag, _("u")) || starts_with(tag, _("/u")) ||
if (starts_with(tag, _("b")) || starts_with(tag, _("/b")) ||
starts_with(tag, _("i")) || starts_with(tag, _("/i")) ||
starts_with(tag, _("u")) || starts_with(tag, _("/u")) ||
starts_with(tag, _("sym")) || starts_with(tag, _("/sym"))) {
// optimize this tag
if (open_tags.find(_("<") + tag + _(">")) == String::npos) {
+31
View File
@@ -0,0 +1,31 @@
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| 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 <random>
#include <sstream>
// ----------------------------------------------------------------------------- : UID
static std::random_device rd; // Get true random number generator
static std::mt19937_64 gen((static_cast<uint64_t>(rd()) << 32) ^ rd()); // Bitwise XOR two outputs to seed pseudo random number generator
static std::uniform_int_distribution<> dis(0, 9);
// Generate a string consisting of 32 uniformly random digits.
static String generate_uid() {
std::stringstream ss;
int i;
ss << std::hex;
for (i = 0; i < 32; i++) {
ss << dis(gen);
};
//return ss.str();
String wxString(ss.str().c_str(), wxConvUTF8);
return wxString;
}
+8 -8
View File
@@ -51,14 +51,14 @@ template <> void GetDefaultMember::handle(const Version& v) {
// ----------------------------------------------------------------------------- : Versions
// NOTE: Don't use leading zeroes, they mean octal
const Version app_version = 10000 * MSE_VERSION_MAJOR + 100 * MSE_VERSION_MINOR + MSE_VERSION_PATCH;
#if defined UNOFFICIAL_BUILD
const Char* version_suffix = _(" (Unofficial)");
#elif defined UNICODE
const Char* version_suffix = _("");
#else
const Char* version_suffix = _(" (ascii build)");
const Version app_version = 10000 * MSE_VERSION_MAJOR + 100 * MSE_VERSION_MINOR + MSE_VERSION_PATCH;
#if defined UNOFFICIAL_BUILD
const Char* version_suffix = _(" (Unofficial)");
#elif defined UNICODE
const Char* version_suffix = _("");
#else
const Char* version_suffix = _(" (ascii build)");
#endif
/// Which version of MSE are the files we write out compatible with?
+24 -9
View File
@@ -81,7 +81,7 @@ enum MenuID {
ID_MODE_SYMMETRY,
ID_MODE_PAINT,
ID_MODE_MAX,
// Welcome Window
ID_SELECT_LANGUAGE,
};
@@ -108,12 +108,14 @@ enum ChildMenuID {
ID_CARD_ROTATE_270,
// CardList
ID_SELECT_COLUMNS,
ID_CARD_LINK,
ID_CARD_AND_LINK_COPY,
ID_CARD_ADD_CSV,
ID_CARD_ADD_CSV_SEP,
ID_CARD_ADD_CSV_BROWSE,
ID_CARD_ADD_CSV_BROWSE,
ID_CARD_ADD_JSON,
ID_CARD_ADD_JSON_ARRAY,
ID_CARD_ADD_JSON_BROWSE,
ID_CARD_ADD_JSON_BROWSE,
// Keyword menu
ID_KEYWORD_ADD = 6101,
@@ -124,7 +126,7 @@ enum ChildMenuID {
// Format menu
ID_FORMAT_BOLD = 6201,
ID_FORMAT_ITALIC,
ID_FORMAT_ITALIC,
ID_FORMAT_UNDERLINE,
ID_FORMAT_SYMBOL,
ID_FORMAT_REMINDER,
@@ -192,6 +194,12 @@ enum ChildMenuID {
ID_COLLAPSE_NOTES = 8001,
ID_CARD_FILTER,
ID_CARD_COUNTER,
ID_CARD_LINK_TYPE,
ID_CARD_LINK_SELECT,
ID_CARD_LINK_UNLINK_1,
ID_CARD_LINK_UNLINK_2,
ID_CARD_LINK_UNLINK_3,
ID_CARD_LINK_UNLINK_4,
// Style panel
ID_STYLE_USE_FOR_ALL = 8011,
@@ -219,7 +227,7 @@ enum ChildMenuID {
ID_CUSTOM_PACK,
// Console panel
ID_EVALUATE,
ID_EVALUATE,
ID_CLEAR_CONSOLE,
// SymbolFont (Format menu)
@@ -279,12 +287,13 @@ enum ControlID {
ID_PREVIEW,
ID_SELECTOR,
ID_SIZE,
ID_GRID,
ID_LEFT,
ID_TOP,
ID_WIDTH,
ID_HEIGHT,
ID_SELECTION_CENTER,
ID_SELECTION_CENTER_HORIZONTALLY,
ID_SELECTION_CENTER,
ID_SELECTION_CENTER_HORIZONTALLY,
ID_SELECTION_CENTER_VERTICALLY,
ID_FIX_ASPECT,
ID_ZOOM,
@@ -294,8 +303,8 @@ enum ControlID {
ID_EXPORT_ZOOM_X,
ID_EXPORT_ZOOM_Y,
ID_SHARPEN,
ID_SHARPEN_AMOUNT,
// Internal window
ID_SHARPEN_AMOUNT,
// Internal window
ID_INTERNAL_SCALE,
// Updates window
ID_PACKAGE_LIST,
@@ -309,5 +318,11 @@ enum ControlID {
ID_ADD_ITEM,
ID_REMOVE_ITEM,
ID_DEFAULTS,
ID_CARD_LINK_EDITOR,
ID_CARD_LINK_VIEWER,
ID_CARD_LINK_RELATION_1,
ID_CARD_LINK_RELATION_2,
ID_CARD_LINK_RELATION_3,
ID_CARD_LINK_RELATION_4,
};
+17 -17
View File
@@ -1,5 +1,5 @@
<?php
require_once('./modules/mse-drupal-modules/autoformat.inc');
/**
@@ -18,28 +18,28 @@ function autoformat_help($section) {
*/
function autoformat_filter_tips($delta, $format, $long = FALSE) {
if ($long) {
$tips = array(
'p' => array( t('Paragraphs'), "A single line break\nhas no effect.\n\nBut two line breaks start a new paragraph."),
'h2' => array( t('Headings'), '=='. t('Section') ."==\n".
'==='. t('Subsection') ."===\n".
'===='. t('Subsection two') ."===="),
'ul' => array( t('Unordered lists'), "* First list item\n* Second list item\n** A nested list item"),
'ol' => array( t('Ordered lists'), "# First list item\n# Second list item\n## A nested list item"),
'dl' => array( t('Definition lists'), ": A term\n; The defenition of that term\n; can span multiple lines\n: Another term\n; And another definition."),
'mix' => array( t('List types can be mixed'), "* A list\n*: Contains a definition \n*; It is both:\n*;# Inside that list, and\n*;# Correctly formated"),
$tips = array(
'p' => array( t('Paragraphs'), "A single line break\nhas no effect.\n\nBut two line breaks start a new paragraph."),
'h2' => array( t('Headings'), '=='. t('Section') ."==\n".
'==='. t('Subsection') ."===\n".
'===='. t('Subsection two') ."===="),
'ul' => array( t('Unordered lists'), "* First list item\n* Second list item\n** A nested list item"),
'ol' => array( t('Ordered lists'), "# First list item\n# Second list item\n## A nested list item"),
'dl' => array( t('Definition lists'), ": A term\n; The defenition of that term\n; can span multiple lines\n: Another term\n; And another definition."),
'mix' => array( t('List types can be mixed'), "* A list\n*: Contains a definition \n*; It is both:\n*;# Inside that list, and\n*;# Correctly formated"),
'pre' => array( t('Preformated text'), " If you start with a space\n the formating is preserved"),
'em' => array( t('Emphasis'), "''Emphasized text''"),
'strong'=> array( t('Strong Emphasis'), "'''Strongly emphasized text'''"),
);
$header = array(t('Description'), t('You Type'), t('You Get'));
foreach($tips as $tip) {
$rows[] = array(
array('data' => $tip[0], 'class' => 'description'),
array('data' => strpos($tip[1],"\n") !== false
? '<pre>'. check_plain($tip[1]) .'</pre>'
: '<code>'. check_plain($tip[1]) .'</code>',
'class' => 'type'),
array('data' => autoformat($tip[1]), 'class' => 'get')
$rows[] = array(
array('data' => $tip[0], 'class' => 'description'),
array('data' => strpos($tip[1],"\n") !== false
? '<pre>'. check_plain($tip[1]) .'</pre>'
: '<code>'. check_plain($tip[1]) .'</code>',
'class' => 'type'),
array('data' => autoformat($tip[1]), 'class' => 'get')
);
}
$output = t('Automatic formating of text using the same tags as <a href="http://en.wikipedia.org/wiki/Wikipedia:How_to_edit_a_page#Wiki_markup">MediaWiki (wikipedia)</a>.');
@@ -1,7 +1,7 @@
<?php
// Automatic formating of text using the same tags as MediaWiki (wikipedia).
require_once('./modules/mse-drupal-modules/highlight.inc');
// quick and dirty aliasses
@@ -51,8 +51,8 @@ $built_in_functions = array(
'filter_list' =>'',
'random_shuffle' =>'',
'random_select' =>'',
'random_select_many' =>'',
'get_card_styling' =>'',
'random_select_many' =>'',
'get_card_styling' =>'',
'get_card_stylesheet' =>'',
// keywords
'expand_keywords' =>'', 'expand_keywords_rule'=>'expand_keywords',
@@ -93,14 +93,19 @@ $built_in_functions = array(
'flip_vertical' =>'',
'rotate' =>'',
'drop_shadow' =>'',
'insert_image' =>'',
'dimensions_of' =>'',
'symbol_variation' =>'',
'import_image' =>'',
'built_in_image' =>'',
// cards
'new_card' =>'',
'add_card_to_set' =>'',
'has_link' =>'',
'get_card_from_link' =>'',
'get_card_from_uid' =>'',
'get_card_styling' =>'',
'get_card_stylesheet' =>'',
'add_card_to_set' =>'',
// html export
'to_html' =>'',
'symbols_to_html' =>'',
@@ -111,6 +116,7 @@ $built_in_functions = array(
'write_set_file' =>'',
// other
'get_mse_version' =>'',
'get_mse_locale' =>'',
'get_mse_path' =>'',
'trace' =>'',
'assert' =>'',
@@ -248,4 +254,4 @@ function highlight_script_string($code) {
return $code;
}
?>
?>