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) cmake_minimum_required(VERSION 3.13)
project(magicseteditor VERSION 2.5.8) project(magicseteditor VERSION 2.6.0)
add_definitions(-DUNOFFICIAL_BUILD) add_definitions(-DUNOFFICIAL_BUILD)
set(CMAKE_CXX_STANDARD 17) 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 # 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. # 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}") 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") include_directories("${PROJECT_BINARY_DIR}/src")
+1 -1
View File
@@ -74,4 +74,4 @@
"inheritEnvironments": [ "msvc_x86" ] "inheritEnvironments": [ "msvc_x86" ]
} }
] ]
} }
+60 -60
View File
@@ -322,63 +322,63 @@ POSSIBILITY OF SUCH DAMAGES.
==END OF TERMS AND CONDITIONS== ==END OF TERMS AND CONDITIONS==
==<a NAME="SEC4" HREF="#TOC4">How to Apply These Terms to Your New Programs</a>== ==<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 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 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. 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 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 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 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. 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.> ] <one line to give the program's name and a brief idea of what it does.>
] Copyright (C) <year> <name of author> ] Copyright (C) <year> <name of author>
] ]
] This program is free software; you can redistribute it and/or modify ] 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 ] it under the terms of the GNU General Public License as published by
] the Free Software Foundation; either version 2 of the License, or ] the Free Software Foundation; either version 2 of the License, or
] (at your option) any later version. ] (at your option) any later version.
] ]
] This program is distributed in the hope that it will be useful, ] This program is distributed in the hope that it will be useful,
] but WITHOUT ANY WARRANTY; without even the implied warranty of ] but WITHOUT ANY WARRANTY; without even the implied warranty of
] MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ] MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
] GNU General Public License for more details. ] GNU General Public License for more details.
] ]
] You should have received a copy of the GNU General Public License ] You should have received a copy of the GNU General Public License
] along with this program; if not, write to the Free Software ] along with this program; if not, write to the Free Software
] Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ] 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. 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 If the program is interactive, make it output a short notice like this
when it starts in an interactive mode: when it starts in an interactive mode:
] Gnomovision version 69, Copyright (C) year name of author ] Gnomovision version 69, Copyright (C) year name of author
] Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. ] Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
] This is free software, and you are welcome to redistribute it ] This is free software, and you are welcome to redistribute it
] under certain conditions; type `show c' for details. ] under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate 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 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 be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program. mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your 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 school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names: necessary. Here is a sample; alter the names:
] Yoyodyne, Inc., hereby disclaims all copyright interest in the program ] Yoyodyne, Inc., hereby disclaims all copyright interest in the program
] `Gnomovision' (which makes passes at compilers) written by James Hacker. ] `Gnomovision' (which makes passes at compilers) written by James Hacker.
] ]
] <signature of Ty Coon>, 1 April 1989 ] <signature of Ty Coon>, 1 April 1989
] Ty Coon, President of Vice ] Ty Coon, President of Vice
This General Public License does not permit incorporating your program into This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the 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 library. If this is what you want to do, use the GNU Library General
Public License instead of this License. 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]]. 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. 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: If the file to be included can vary depending on the locale that is selected, use:
>>>include localized file: <em>filename</em> >>>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. 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" 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. 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-- --Example--
For example, a [[type:set]] might look like this: 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. 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-- --Parameters--
! Parameter Type Description ! Parameter Type Description
| @input@ [[type:image]] Image to enlarge | @input@ [[type:image]] Image to enlarge
| @height@ [[type:double]] Height of the resulting image | @height@ [[type:double]] Height of the resulting image, in pixels
| @width@ [[type:double]] Width of the resulting image | @width@ [[type:double]] Width of the resulting image, in pixels
| @offset_x@ [[type:double]] Offset of crop, horizontally | @offset_x@ [[type:double]] Offset of crop, horizontally, in pixels
| @offset_y@ [[type:double]] Offset of crop, vertically | @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]]. 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-- --Parameters--
! Parameter Type Description ! Parameter Type Description
| @input@ [[type:card]] The card you want to retrieve the styling data from. | @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-- --Examples--
> # Retrieve the value "is foil" from the card's styling options > # 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:flip_vertical]] Flip an image vertically.
| [[fun:rotate_image]] Rotate an image. | [[fun:rotate_image]] Rotate an image.
| [[fun:drop_shadow]] Add a drop shadow to 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:symbol_variation]] Render a variation of a [[type:symbol]].
| [[fun:import_image]] Load an image from outside the data folder. | [[fun:import_image]] Load an image from outside the data folder.
| [[fun:built_in_image]] Return an image built into the program. | [[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: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_styling]] Get the styling data of a [[type:card]].
| [[fun:get_card_stylesheet]] Get the stylesheet 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 <<< ! HTML export <<<
| [[fun:to_html]] Convert [[type:tagged text]] to html. | [[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 <<< ! Other functions <<<
| [[fun:get_mse_version]] Get the MSE app version. | [[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:get_mse_path]] Get the MSE app folder absolute path.
| [[fun:trace]] Output a message for debugging purposes. | [[fun:trace]] Output a message for debugging purposes.
| [[fun:assert]] Check a condition 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 The current stylesheet
| @card@ [[type:card]] not in @init script@s or when exporting | @card@ [[type:card]] not in @init script@s or when exporting
The current card. 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@. | @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@ [[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. | @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. | @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. | @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 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. | @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. | @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. | @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. 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]]}. 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. Use the make_map function to dynamically create maps.
The @type@ determines what values of this field contain: 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. 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"@.'' <<< <<< <<< | @"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/> | ^^^ @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. 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). | ^^^ @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 IMAGE "tool/card_add.png"
tool/card_add_multiple IMAGE "tool/card_add_multiple.png" tool/card_add_multiple IMAGE "tool/card_add_multiple.png"
tool/card_del IMAGE "tool/card_del.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 IMAGE "tool/card_rotate.png"
tool/card_rotate_0 IMAGE "tool/card_rotate_0.png" tool/card_rotate_0 IMAGE "tool/card_rotate_0.png"
tool/card_rotate_90 IMAGE "tool/card_rotate_90.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/pack.hpp>
#include <data/stylesheet.hpp> #include <data/stylesheet.hpp>
#include <util/error.hpp> #include <util/error.hpp>
#include <util/uid.hpp>
// ----------------------------------------------------------------------------- : Add card // ----------------------------------------------------------------------------- : Add card
@@ -36,10 +37,53 @@ String AddCardAction::getName(bool to_undo) const {
} }
void AddCardAction::perform(bool to_undo) { 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); action.perform(set.cards, to_undo);
} }
// ----------------------------------------------------------------------------- : Reorder cards // ----------------------------------------------------------------------------- : Reorder cards
ReorderCardsAction::ReorderCardsAction(Set& set, size_t card_id1, size_t card_id2) 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_id1 < set.cards.size());
assert(card_id2 < set.cards.size()); assert(card_id2 < set.cards.size());
#endif #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. // TODO : Too lazy to fix this right now.
return; return;
} }
swap(set.cards[card_id1], set.cards[card_id2]); 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 // ----------------------------------------------------------------------------- : Change stylesheet
String DisplayChangeAction::getName(bool to_undo) const { 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 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 // ----------------------------------------------------------------------------- : Change stylesheet
/// An action that affects the rendering/display/look of a set or cards in the set /// 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 <util/prec.hpp>
#include <data/card.hpp> #include <data/card.hpp>
#include <data/set.hpp>
#include <data/game.hpp> #include <data/game.hpp>
#include <data/stylesheet.hpp> #include <data/stylesheet.hpp>
#include <data/field.hpp> #include <data/field.hpp>
#include <util/error.hpp> #include <util/error.hpp>
#include <util/reflect.hpp> #include <util/reflect.hpp>
#include <util/delayed_index_maps.hpp> #include <util/delayed_index_maps.hpp>
#include <util/uid.hpp>
#include <unordered_set>
// ----------------------------------------------------------------------------- : Card // ----------------------------------------------------------------------------- : Card
Card::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_created (wxDateTime::Now().Subtract(wxDateSpan::Day()).ResetTime())
, time_modified(wxDateTime::Now().Subtract(wxDateSpan::Day()).ResetTime()) , time_modified(wxDateTime::Now().Subtract(wxDateSpan::Day()).ResetTime())
, uid(generate_uid())
, has_styling(false) , has_styling(false)
{ {
if (!game_for_reading()) { if (!game_for_reading()) {
@@ -32,6 +36,7 @@ Card::Card()
Card::Card(const Game& game) Card::Card(const Game& game)
: time_created (wxDateTime::Now()) : time_created (wxDateTime::Now())
, time_modified(wxDateTime::Now()) , time_modified(wxDateTime::Now())
, uid(generate_uid())
, has_styling(false) , has_styling(false)
{ {
data.init(game.card_fields); data.init(game.card_fields);
@@ -60,6 +65,222 @@ bool Card::contains(QuickFilterPart const& query) const {
return false; 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) { IndexMap<FieldP, ValueP>& Card::extraDataFor(const StyleSheet& stylesheet) {
return extra_data.get(stylesheet.name(), stylesheet.extra_card_fields); return extra_data.get(stylesheet.name(), stylesheet.extra_card_fields);
} }
@@ -89,6 +310,15 @@ IMPLEMENT_REFLECTION(Card) {
} }
} }
REFLECT(notes); 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_created);
REFLECT(time_modified); REFLECT(time_modified);
REFLECT(extra_data); // don't allow scripts to depend on style specific data REFLECT(extra_data); // don't allow scripts to depend on style specific data
+27
View File
@@ -17,11 +17,15 @@
class Game; class Game;
class Dependency; class Dependency;
class Keyword; class Keyword;
DECLARE_POINTER_TYPE(Set);
DECLARE_POINTER_TYPE(Card); DECLARE_POINTER_TYPE(Card);
DECLARE_POINTER_TYPE(Field); DECLARE_POINTER_TYPE(Field);
DECLARE_POINTER_TYPE(Value); DECLARE_POINTER_TYPE(Value);
DECLARE_POINTER_TYPE(StyleSheet); 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 // ----------------------------------------------------------------------------- : Card
/// A card from a card Set /// A card from a card Set
@@ -37,6 +41,18 @@ public:
IndexMap<FieldP, ValueP> data; IndexMap<FieldP, ValueP> data;
/// Notes for this card /// Notes for this card
String notes; 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 /// Time the card was created/last modified
wxDateTime time_created, time_modified; wxDateTime time_created, time_modified;
/// Alternative style to use for this card /// Alternative style to use for this card
@@ -64,6 +80,17 @@ public:
/// Does any field contains the given query string? /// Does any field contains the given query string?
bool contains(QuickFilterPart const& query) const; 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 /// Find a value in the data by name and type
template <typename T> T& value(const String& name) { template <typename T> T& value(const String& name) {
for(IndexMap<FieldP, ValueP>::iterator it = data.begin() ; it != data.end() ; ++it) { 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) { void Field::after_reading(Version ver) {
name = canonical_name_form(name); name = canonical_name_form(name);
if(caption.default_.empty()) caption.default_ = tr(package_relative_filename, name, name_to_caption); 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); if(card_list_name.default_.empty()) card_list_name.default_ = tr(package_relative_filename, caption.default_, capitalize);
} }
template <> template <>
intrusive_ptr<Field> read_new<Field>(Reader& reader) { intrusive_ptr<Field> read_new<Field>(Reader& reader) {
intrusive_ptr<Field> field; intrusive_ptr<Field> field;
// there must be a type specified // there must be a type specified
String type; String type;
reader.handle(_("type"), type); reader.handle(_("type"), type);
@@ -95,7 +95,7 @@ intrusive_ptr<Field> read_new<Field>(Reader& reader) {
} else { } else {
reader.warning(_ERROR_1_("unsupported field type", type)); reader.warning(_ERROR_1_("unsupported field type", type));
throw ParseError(_ERROR_("aborting parsing")); throw ParseError(_ERROR_("aborting parsing"));
} }
field->package_relative_filename = reader.getPackage()->relativeFilename().Clone(); field->package_relative_filename = reader.getPackage()->relativeFilename().Clone();
return field; 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 { void Style::markDependencyMember(const String& name, const Dependency& dep) const {
// mark dependencies on content // mark dependencies on content
if (dep.type == DEP_DUMMY && dep.index == false && (starts_with(name, _("content")) || name == "layout") ) { if (
// anything that starts with "content_" is a content property 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; 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. 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 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. 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; String package_relative_filename;
/// Creates a new Value corresponding to this Field /// Creates a new Value corresponding to this Field
+50 -50
View File
@@ -8,10 +8,10 @@
#include <util/prec.hpp> #include <util/prec.hpp>
#include <data/font.hpp> #include <data/font.hpp>
#include <wx/stdpaths.h> #include <wx/stdpaths.h>
#include <wx/dir.h> #include <wx/dir.h>
#include <wx/font.h> #include <wx/font.h>
// ----------------------------------------------------------------------------- : Font // ----------------------------------------------------------------------------- : Font
Font::Font() Font::Font()
@@ -27,59 +27,59 @@ Font::Font()
, separator_color(Color(0,0,0,128)) , separator_color(Color(0,0,0,128))
, flags(FONT_NORMAL) , 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; wxString folder;
bool cont = appDir.GetFirst(&folder, wxEmptyString, wxDIR_DIRS); bool cont = appDir.GetFirst(&folder, wxEmptyString, wxDIR_DIRS);
while (cont) while (cont)
{ {
if (folder.Lower().Contains("fonts")) { if (folder.Lower().Contains("fonts")) {
String folderPath = appPath + pathSeparator + folder + pathSeparator; String folderPath = appPath + pathSeparator + folder + pathSeparator;
// tally fonts // tally fonts
vector<String> fontFilePaths; vector<String> fontFilePaths;
TallyResourceFonts(folderPath, fontFilePaths, recursive); TallyResourceFonts(folderPath, fontFilePaths, recursive);
// load fonts // load fonts
for (const String& fontFilePath : fontFilePaths) { for (const String& fontFilePath : fontFilePaths) {
if (!wxFont::AddPrivateFont(fontFilePath)) { if (!wxFont::AddPrivateFont(fontFilePath)) {
preloadHadErrors = true; preloadHadErrors = true;
} }
} }
} }
cont = appDir.GetNext(&folder); cont = appDir.GetNext(&folder);
} }
return preloadHadErrors; return preloadHadErrors;
#endif // wxUSE_PRIVATE_FONTS #endif // wxUSE_PRIVATE_FONTS
return false; return false;
} }
void Font::TallyResourceFonts(String fontsDirectoryPath, vector<String>& fontFilePaths, bool recursive) { void Font::TallyResourceFonts(String fontsDirectoryPath, vector<String>& fontFilePaths, bool recursive) {
wxDir fontsDirectory(fontsDirectoryPath); wxDir fontsDirectory(fontsDirectoryPath);
String fontFileName = wxEmptyString; String fontFileName = wxEmptyString;
bool hasNext = fontsDirectory.GetFirst(&fontFileName); bool hasNext = fontsDirectory.GetFirst(&fontFileName);
while (hasNext) { while (hasNext) {
String fontFilePath = fontsDirectoryPath + fontFileName; String fontFilePath = fontsDirectoryPath + fontFileName;
if (wxDirExists(fontFilePath)) { if (wxDirExists(fontFilePath)) {
if (recursive) { if (recursive) {
TallyResourceFonts(fontFilePath + wxFileName::GetPathSeparator(), fontFilePaths, true); TallyResourceFonts(fontFilePath + wxFileName::GetPathSeparator(), fontFilePaths, true);
} }
} }
else if (fontFilePath.EndsWith(_(".ttf")) || fontFilePath.EndsWith(_(".otf"))) { else if (fontFilePath.EndsWith(_(".ttf")) || fontFilePath.EndsWith(_(".otf"))) {
fontFilePaths.push_back(fontFilePath); fontFilePaths.push_back(fontFilePath);
} }
hasNext = fontsDirectory.GetNext(&fontFileName); hasNext = fontsDirectory.GetNext(&fontFileName);
} }
} }
bool Font::update(Context& ctx) { bool Font::update(Context& ctx) {
bool changes = false; bool changes = false;
+10 -9
View File
@@ -46,15 +46,16 @@ public:
Scriptable<double> shadow_displacement_y;///< Position of the shadow Scriptable<double> shadow_displacement_y;///< Position of the shadow
Scriptable<double> shadow_blur; ///< Blur radius of the shadow Scriptable<double> shadow_blur; ///< Blur radius of the shadow
Color separator_color; ///< Color for <sep> text Color separator_color; ///< Color for <sep> text
int flags; ///< FontFlags for this font int flags; ///< FontFlags for this font
Font(); Font();
/// Load fonts (.ttf or .otf) from all directories in the app directory that contain "fonts" in their names, /// 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 /// and optionaly their subdirectories, returns true if there were errors
static bool PreloadResourceFonts(bool recursive); static bool PreloadResourceFonts(bool recursive);
/// Adds font file paths from the given directory into fontFilePaths /// Adds font file paths from the given directory into fontFilePaths
static void TallyResourceFonts(String fontsDirectoryPath, vector<String>& fontFilePaths, bool recursive); static void TallyResourceFonts(String fontsDirectoryPath, vector<String>& fontFilePaths, bool recursive);
/// Update the scritables, returns true if there is a change /// Update the scritables, returns true if there is a change
bool update(Context& ctx); bool update(Context& ctx);
/// Add the given dependency to the dependent_scripts list for the variables this font depends on /// 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 /// Convert this font to a wxFont
wxFont toWxFont(double scale) const; wxFont toWxFont(double scale) const;
private: private:
DECLARE_REFLECTION(); DECLARE_REFLECTION();
}; };
+3
View File
@@ -147,6 +147,9 @@ CardsOnClipboard::CardsOnClipboard(const SetP& set, const vector<CardP>& cards)
if (cards.size() == 1) { if (cards.size() == 1) {
Add(new wxBitmapDataObject(export_bitmap(set, cards[0]))); 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 // Conversion to serialized card format
Add(new CardsDataObject(set, cards), true); 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); void export_image(const SetP& set, const CardP& card, const String& filename);
/// Generate a bitmap image of a card /// 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 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 /// Export a set to Magic Workstation format
void export_mws(Window* parent, const SetP& set); void export_mws(Window* parent, const SetP& set);
/// Export a set to Apprentice /// 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/card.hpp>
#include <data/stylesheet.hpp> #include <data/stylesheet.hpp>
#include <data/settings.hpp> #include <data/settings.hpp>
#include <gui/util.hpp>
#include <render/card/viewer.hpp> #include <render/card/viewer.hpp>
#include <wx/filename.h> #include <wx/filename.h>
@@ -25,47 +26,47 @@ void export_image(const SetP& set, const CardP& card, const String& filename) {
} }
class UnzoomedDataViewer : public DataViewer { class UnzoomedDataViewer : public DataViewer {
public: public:
UnzoomedDataViewer(); UnzoomedDataViewer();
UnzoomedDataViewer(double zoom, double angle); UnzoomedDataViewer(double zoom, double angle);
virtual ~UnzoomedDataViewer() {}; virtual ~UnzoomedDataViewer() {};
Rotation getRotation() const override; Rotation getRotation() const override;
private: private:
double zoom; double zoom;
double angle; double angle;
bool declared_values; bool declared_values;
}; };
UnzoomedDataViewer::UnzoomedDataViewer() UnzoomedDataViewer::UnzoomedDataViewer()
: zoom(1.0) : zoom(1.0)
, angle(0.0) , angle(0.0)
, declared_values(false) , declared_values(false)
{} {}
UnzoomedDataViewer::UnzoomedDataViewer(const double zoom, const Radians angle = 0.0) UnzoomedDataViewer::UnzoomedDataViewer(const double zoom, const Radians angle = 0.0)
: zoom(zoom) : zoom(zoom)
, angle(angle) , angle(angle)
, declared_values(true) , declared_values(true)
{} {}
Rotation UnzoomedDataViewer::getRotation() const { Rotation UnzoomedDataViewer::getRotation() const {
if (!stylesheet) stylesheet = set->stylesheet; if (!stylesheet) stylesheet = set->stylesheet;
if (declared_values) { if (declared_values) {
return Rotation(angle, stylesheet->getCardRect(), zoom, 1.0, ROTATION_ATTACH_TOP_LEFT); return Rotation(angle, stylesheet->getCardRect(), zoom, 1.0, ROTATION_ATTACH_TOP_LEFT);
} }
double export_zoom = settings.stylesheetSettingsFor(set->stylesheetFor(card)).export_zoom(); double export_zoom = settings.stylesheetSettingsFor(set->stylesheetFor(card)).export_zoom();
bool use_viewer_rotation = !settings.stylesheetSettingsFor(set->stylesheetFor(card)).card_normal_export(); bool use_viewer_rotation = !settings.stylesheetSettingsFor(set->stylesheetFor(card)).card_normal_export();
if (use_viewer_rotation) { if (use_viewer_rotation) {
return Rotation(DataViewer::getRotation().getAngle(), stylesheet->getCardRect(), export_zoom, 1.0, ROTATION_ATTACH_TOP_LEFT); return Rotation(DataViewer::getRotation().getAngle(), stylesheet->getCardRect(), export_zoom, 1.0, ROTATION_ATTACH_TOP_LEFT);
} else { } else {
return Rotation(angle, stylesheet->getCardRect(), export_zoom, 1.0, ROTATION_ATTACH_TOP_LEFT); return Rotation(angle, stylesheet->getCardRect(), export_zoom, 1.0, ROTATION_ATTACH_TOP_LEFT);
} }
} }
Bitmap export_bitmap(const SetP& set, const CardP& card) { Bitmap export_bitmap(const SetP& set, const CardP& card) {
if (!set) throw Error(_("no set")); if (!set) throw Error(_("no set"));
UnzoomedDataViewer viewer = UnzoomedDataViewer(); UnzoomedDataViewer viewer = UnzoomedDataViewer();
viewer.setSet(set); viewer.setSet(set);
viewer.setCard(card); viewer.setCard(card);
@@ -79,11 +80,11 @@ Bitmap export_bitmap(const SetP& set, const CardP& card) {
// draw // draw
viewer.draw(dc); viewer.draw(dc);
dc.SelectObject(wxNullBitmap); dc.SelectObject(wxNullBitmap);
return bitmap; return bitmap;
} }
Bitmap export_bitmap(const SetP& set, const CardP& card, const double zoom, const Radians angle = 0.0) { Bitmap export_bitmap(const SetP& set, const CardP& card, const double zoom, const Radians angle = 0.0) {
if (!set) throw Error(_("no set")); if (!set) throw Error(_("no set"));
UnzoomedDataViewer viewer = UnzoomedDataViewer(zoom, angle); UnzoomedDataViewer viewer = UnzoomedDataViewer(zoom, angle);
viewer.setSet(set); viewer.setSet(set);
viewer.setCard(card); viewer.setCard(card);
@@ -97,8 +98,59 @@ Bitmap export_bitmap(const SetP& set, const CardP& card, const double zoom, cons
// draw // draw
viewer.draw(dc); viewer.draw(dc);
dc.SelectObject(wxNullBitmap); 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 // ----------------------------------------------------------------------------- : Multiple card export
+21 -20
View File
@@ -48,6 +48,7 @@ IMPLEMENT_REFLECTION(Game) {
} }
REFLECT_NO_SCRIPT(default_set_style); REFLECT_NO_SCRIPT(default_set_style);
REFLECT_NO_SCRIPT(card_fields); REFLECT_NO_SCRIPT(card_fields);
REFLECT_NO_SCRIPT(card_links);
REFLECT_NO_SCRIPT(card_list_color_script); REFLECT_NO_SCRIPT(card_list_color_script);
REFLECT_NO_SCRIPT(import_script); REFLECT_NO_SCRIPT(import_script);
REFLECT_NO_SCRIPT(json_paths); REFLECT_NO_SCRIPT(json_paths);
@@ -95,26 +96,26 @@ void Game::validate(Version v) {
pack->filter = OptionalScript(_("true")); pack->filter = OptionalScript(_("true"));
pack->select = SELECT_NO_REPLACE; pack->select = SELECT_NO_REPLACE;
pack_types.push_back(pack); 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) { for (auto it = card_fields.begin(); it != card_fields.end(); ++it) {
FieldP field = *it; FieldP field = *it;
String unified_name = unified_form(field->name); String unified_name = unified_form(field->name);
if (card_fields_alt_names.count(unified_name)) { if (card_fields_alt_names.count(unified_name)) {
queue_message(MESSAGE_WARNING, _("Duplicate alternate card field name: ") + unified_name); queue_message(MESSAGE_WARNING, _("Duplicate alternate card field name: ") + unified_name);
} }
else { else {
card_fields_alt_names.emplace(unified_name, field->name); card_fields_alt_names.emplace(unified_name, field->name);
} }
//String column_name = field->card_list_name.get(); //String column_name = field->card_list_name.get();
//card_fields_alt_names.emplace(unified_form(column_name), field->name); //card_fields_alt_names.emplace(unified_form(column_name), field->name);
for (auto it2 = field->alt_names.begin(); it2 != field->alt_names.end(); ++it2) { for (auto it2 = field->alt_names.begin(); it2 != field->alt_names.end(); ++it2) {
unified_name = unified_form(*it2); unified_name = unified_form(*it2);
if (card_fields_alt_names.count(unified_name)) { if (card_fields_alt_names.count(unified_name)) {
queue_message(MESSAGE_WARNING, _("Duplicate alternate card field name: ") + unified_name); queue_message(MESSAGE_WARNING, _("Duplicate alternate card field name: ") + unified_name);
} }
else { else {
card_fields_alt_names.emplace(unified_name, field->name); card_fields_alt_names.emplace(unified_name, field->name);
} }
} }
} }
@@ -138,7 +139,7 @@ void Game::initCardListColorScript() {
return; return;
} }
} }
} }
// special behaviour of reading/writing GamePs: only read/write the name // 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 <util/io/package.hpp>
#include <script/scriptable.hpp> #include <script/scriptable.hpp>
#include <script/dependency.hpp> #include <script/dependency.hpp>
#include <util/dynamic_arg.hpp> #include <util/dynamic_arg.hpp>
DECLARE_POINTER_TYPE(Field); DECLARE_POINTER_TYPE(Field);
DECLARE_POINTER_TYPE(Style); DECLARE_POINTER_TYPE(Style);
@@ -41,6 +41,7 @@ public:
vector<FieldP> set_fields; ///< Fields for set information 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 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<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 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 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 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<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<AddCardsScriptP> add_cards_scripts; ///< Scripts for adding multiple cards to the set
vector<AutoReplaceP> auto_replaces; ///< Things to autoreplace in textboxes 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? bool has_keywords; ///< Does this game use keywords?
OptionalScript keyword_match_script; ///< For the keyword editor OptionalScript keyword_match_script; ///< For the keyword editor
vector<KeywordParamP> keyword_parameter_types;///< Types of keyword parameters 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()); loc = find_wildcard_and_set(the_locale->package_translations, pkg.relativeFilename());
} }
return loc->tr(subcat, key, def); return loc->tr(subcat, key, def);
} }
String tr(const String& pkg_relative_filename, const String& key, DefaultLocaleFun def) { String tr(const String& pkg_relative_filename, const String& key, DefaultLocaleFun def) {
if (!the_locale) return def(key); if (!the_locale) return def(key);
SubLocaleP loc = the_locale->package_translations[pkg_relative_filename]; 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); loc = find_wildcard_and_set(the_locale->package_translations, pkg_relative_filename);
} }
return loc->tr(key, def); return loc->tr(key, def);
} }
String tr(const String& pkg_relative_filename, const String& subcat, const String& key, DefaultLocaleFun def) { String tr(const String& pkg_relative_filename, const String& subcat, const String& key, DefaultLocaleFun def) {
if (!the_locale) return def(key); if (!the_locale) return def(key);
SubLocaleP loc = the_locale->package_translations[pkg_relative_filename]; SubLocaleP loc = the_locale->package_translations[pkg_relative_filename];
+5 -5
View File
@@ -126,7 +126,7 @@ StyleSheetSettings::StyleSheetSettings()
, card_angle (0, true) , card_angle (0, true)
, card_anti_alias (true, true) , card_anti_alias (true, true)
, card_borders (true, true) , card_borders (true, true)
, card_draw_editing (true, true) , card_draw_editing (true, true)
, card_normal_export (true, true) , card_normal_export (true, true)
, card_spellcheck_enabled(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_angle .isDefault()) card_angle .assignDefault(ss.card_angle);
if (card_anti_alias .isDefault()) card_anti_alias .assignDefault(ss.card_anti_alias); 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_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_normal_export .isDefault()) card_normal_export .assignDefault(ss.card_normal_export);
if (card_spellcheck_enabled.isDefault()) card_spellcheck_enabled.assignDefault(ss.card_spellcheck_enabled); 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_angle);
REFLECT(card_anti_alias); REFLECT(card_anti_alias);
REFLECT(card_borders); REFLECT(card_borders);
REFLECT(card_draw_editing); REFLECT(card_draw_editing);
REFLECT(card_normal_export); REFLECT(card_normal_export);
REFLECT(card_spellcheck_enabled); REFLECT(card_spellcheck_enabled);
} }
@@ -225,8 +225,8 @@ ColumnSettings& Settings::columnSettingsFor(const Game& game, const Field& field
} }
return cs; return cs;
} }
StyleSheetSettings& Settings::stylesheetSettingsFor(const StyleSheet& stylesheet) { StyleSheetSettings& Settings::stylesheetSettingsFor(const StyleSheet& stylesheet) {
// Use the canonical form here since the stylesheet name will be used as a stored key. // 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. // 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())]; StyleSheetSettingsP& ss = stylesheet_settings[canonical_name_form(stylesheet.name())];
if (!ss) ss = make_intrusive<StyleSheetSettings>(); if (!ss) ss = make_intrusive<StyleSheetSettings>();
+4 -4
View File
@@ -195,10 +195,10 @@ public:
// --------------------------------------------------- : Special game stuff // --------------------------------------------------- : Special game stuff
String apprentice_location; String apprentice_location;
// --------------------------------------------------- : Internal settings // --------------------------------------------------- : Internal settings
double internal_scale; double internal_scale;
bool internal_image_extension; bool internal_image_extension;
// --------------------------------------------------- : Update checking // --------------------------------------------------- : Update checking
#if USE_OLD_STYLE_UPDATE_CHECKER #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) { } catch (PackageNotFoundError& e) {
queue_message(MESSAGE_ERROR, _("Missing stylesheet: ") + full_name); 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 // 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 // Also, it's probably better to ask the user for an alternative for each missing StyleSheet individually
//if (stylesheet_for_reading()) { //if (stylesheet_for_reading()) {
// // we already have a stylesheet higher up, so just return a null pointer // // we already have a stylesheet higher up, so just return a null pointer
// return StyleSheetP(); // return StyleSheetP();
//} //}
// load an alternative stylesheet // load an alternative stylesheet
StyleSheetP ss = select_stylesheet(game, name); StyleSheetP ss = select_stylesheet(game, name);
@@ -103,7 +103,7 @@ IMPLEMENT_REFLECTION(StyleSheet) {
REFLECT(card_width); REFLECT(card_width);
REFLECT(card_height); REFLECT(card_height);
REFLECT(card_dpi); REFLECT(card_dpi);
REFLECT(card_background); REFLECT(card_background);
REFLECT(card_regions); REFLECT(card_regions);
REFLECT(init_script); REFLECT(init_script);
// styling // styling
@@ -122,12 +122,7 @@ IMPLEMENT_REFLECTION(StyleSheet) {
// extra card fields // extra card fields
REFLECT(extra_card_fields); REFLECT(extra_card_fields);
REFLECT_IF_READING { REFLECT_IF_READING {
if (extra_card_style.init(extra_card_fields)) { 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;
}
}
} }
REFLECT(extra_card_style); REFLECT(extra_card_style);
} }
+6 -6
View File
@@ -11,16 +11,16 @@
#include <util/prec.hpp> #include <util/prec.hpp>
#include <util/io/package.hpp> #include <util/io/package.hpp>
#include <util/real_point.hpp> #include <util/real_point.hpp>
#include <script/scriptable.hpp> #include <script/scriptable.hpp>
// This isn't strictly needed for this file, // This isn't strictly needed for this file,
// but CardRegion needs to be referenced from _somewhere_ in the codebase for compilation reasons. // 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. // Eventually if somewhere else is using the type then this can be removed.
#include <data/card_region.hpp> #include <data/card_region.hpp>
DECLARE_POINTER_TYPE(Game); DECLARE_POINTER_TYPE(Game);
DECLARE_POINTER_TYPE(StyleSheet); DECLARE_POINTER_TYPE(StyleSheet);
DECLARE_POINTER_TYPE(Field); DECLARE_POINTER_TYPE(Field);
DECLARE_POINTER_TYPE(Style); DECLARE_POINTER_TYPE(Style);
DECLARE_POINTER_TYPE(CardRegion); DECLARE_POINTER_TYPE(CardRegion);
// ----------------------------------------------------------------------------- : StyleSheet // ----------------------------------------------------------------------------- : StyleSheet
@@ -38,7 +38,7 @@ public:
double card_width; ///< The width of a card in pixels double card_width; ///< The width of a card in pixels
double card_height; ///< The height 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 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; vector<CardRegionP> card_regions;
/// The styling for card fields /// The styling for card fields
/** The indices should correspond to the card_fields in the Game */ /** 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) { RealSize SymbolFont::symbolSize(double font_size, const DrawableSymbol& sym) {
if (sym.symbol) { 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 { } else {
return defaultSymbolSize(font_size); return defaultSymbolSize(font_size);
} }
@@ -403,12 +403,15 @@ RealSize SymbolFont::symbolSize(double font_size, const DrawableSymbol& sym) {
RealSize SymbolFont::defaultSymbolSize(double font_size) { RealSize SymbolFont::defaultSymbolSize(double font_size) {
SymbolInFont* def = defaultSymbol(); SymbolInFont* def = defaultSymbol();
if (def) { if (def) {
return add_diagonal(def->size(*this, font_size), spacing); return add_diagonal(def->size(*this, font_size), spacingSize(font_size));
} else { } 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 // ----------------------------------------------------------------------------- : InsertSymbolMenu
+4 -1
View File
@@ -84,7 +84,7 @@ public:
private: private:
double img_size; ///< Font size that the images use 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 // writing text
bool scale_text; ///< Should text be scaled down to fit in a symbol? bool scale_text; ///< Should text be scaled down to fit in a symbol?
InsertSymbolMenuP insert_symbol_menu; InsertSymbolMenuP insert_symbol_menu;
@@ -107,6 +107,9 @@ public:
/// The default size of symbols, including spacing /// The default size of symbols, including spacing
RealSize defaultSymbolSize(double font_size); RealSize defaultSymbolSize(double font_size);
/// The spacing between symbols, accounting for font size
RealSize SymbolFont::spacingSize(double font_size);
DECLARE_REFLECTION(); 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; data1 += 3;
data2 += 3; data2 += 3;
} }
} }
// Blend Alpha for the two images. // Blend Alpha for the two images.
if (img1.HasAlpha() && img2.HasAlpha()) { if (img1.HasAlpha() && img2.HasAlpha()) {
Byte *alpha1 = img1.GetAlpha(), *alpha2 = img2.GetAlpha(); Byte *alpha1 = img1.GetAlpha(), *alpha2 = img2.GetAlpha();
for (int y = 0; y < height; ++y) { 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 235", COMBINE_GREATER_THAN_235);
VALUE_N("greater than 240", COMBINE_GREATER_THAN_240); VALUE_N("greater than 240", COMBINE_GREATER_THAN_240);
VALUE_N("greater than 245", COMBINE_GREATER_THAN_245); 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 5", COMBINE_SMALLER_THAN_5);
VALUE_N("smaller than 10", COMBINE_SMALLER_THAN_10); VALUE_N("smaller than 10", COMBINE_SMALLER_THAN_10);
VALUE_N("smaller than 15", COMBINE_SMALLER_THAN_15); 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_235, a > 235 ? b : a)
COMBINE_FUN(COMBINE_GREATER_THAN_240, a > 240 ? 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_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_5, a < 5 ? b : a)
COMBINE_FUN(COMBINE_SMALLER_THAN_10, a < 10 ? b : a) COMBINE_FUN(COMBINE_SMALLER_THAN_10, a < 10 ? b : a)
COMBINE_FUN(COMBINE_SMALLER_THAN_15, a < 15 ? 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_235, a < 235 ? b : a)
COMBINE_FUN(COMBINE_SMALLER_THAN_240, a < 240 ? 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_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 // ----------------------------------------------------------------------------- : Combining
@@ -306,7 +306,7 @@ void combine_image_do(Image& a, Image b) {
void combine_image(Image& a, const Image& b, ImageCombine combine) { void combine_image(Image& a, const Image& b, ImageCombine combine) {
// Images must have same size // Images must have same size
if (a.GetWidth() != b.GetWidth() || a.GetHeight() != b.GetHeight()) { 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? // Copy alpha channel?
if (b.HasAlpha()) { 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_235);
DISPATCH(COMBINE_GREATER_THAN_240); DISPATCH(COMBINE_GREATER_THAN_240);
DISPATCH(COMBINE_GREATER_THAN_245); DISPATCH(COMBINE_GREATER_THAN_245);
DISPATCH(COMBINE_GREATER_THAN_250); DISPATCH(COMBINE_GREATER_THAN_250);
DISPATCH(COMBINE_SMALLER_THAN_5); DISPATCH(COMBINE_SMALLER_THAN_5);
DISPATCH(COMBINE_SMALLER_THAN_10); DISPATCH(COMBINE_SMALLER_THAN_10);
DISPATCH(COMBINE_SMALLER_THAN_15); DISPATCH(COMBINE_SMALLER_THAN_15);
+133 -133
View File
@@ -14,7 +14,7 @@
#include <data/field/symbol.hpp> #include <data/field/symbol.hpp>
#include <render/symbol/filter.hpp> #include <render/symbol/filter.hpp>
#include <gui/util.hpp> // load_resource_image #include <gui/util.hpp> // load_resource_image
#include <wx/wfstream.h> #include <wx/wfstream.h>
// ----------------------------------------------------------------------------- : GeneratedImage // ----------------------------------------------------------------------------- : GeneratedImage
@@ -308,7 +308,7 @@ bool EnlargeImage::operator == (const GeneratedImage& that) const {
// ----------------------------------------------------------------------------- : ResizeImage // ----------------------------------------------------------------------------- : ResizeImage
Image ResizeImage::generate(const Options& opt) const { Image ResizeImage::generate(const Options& opt) const {
Image img = image->generate(opt); Image img = image->generate(opt);
return resample(img, width, height); return resample(img, width, height);
} }
bool ResizeImage::operator == (const GeneratedImage& that) const { bool ResizeImage::operator == (const GeneratedImage& that) const {
@@ -321,16 +321,16 @@ bool ResizeImage::operator == (const GeneratedImage& that) const {
// ----------------------------------------------------------------------------- : BleedEdgedImage // ----------------------------------------------------------------------------- : BleedEdgedImage
Image BleedEdgedImage::generate(const Options& opt) const { Image BleedEdgedImage::generate(const Options& opt) const {
// create enlarged image // create enlarged image
Image base_img = base_image->generate(opt); 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) { 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; return base_img;
} }
bool is_landscape = w > h; bool is_landscape = w > h;
int dw = int(w * (horizontal_size > 0.0 ? horizontal_size : is_landscape ? 0.037 : 0.048)); 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)); int dh = int(h * (vertical_size > 0.0 ? vertical_size : is_landscape ? 0.048 : 0.037));
if (dw <= 0 && dh <= 0) { if (dw <= 0 && dh <= 0) {
return base_img; return base_img;
} }
@@ -339,119 +339,119 @@ Image BleedEdgedImage::generate(const Options& opt) const {
Image img = wxImage(width, height, false); Image img = wxImage(width, height, false);
img.InitAlpha(); img.InitAlpha();
Byte* data = img.GetData(); Byte* data = img.GetData();
Byte* alpha = img.GetAlpha(); Byte* alpha = img.GetAlpha();
// fill with background color // fill with background color
for (UInt i = 0; i < size; ++i) { for (UInt i = 0; i < size; ++i) {
data[3 * i + 0] = background_color.Red(); data[3 * i + 0] = background_color.Red();
data[3 * i + 1] = background_color.Green(); data[3 * i + 1] = background_color.Green();
data[3 * i + 2] = background_color.Blue(); data[3 * i + 2] = background_color.Blue();
alpha[i] = background_color.Alpha(); alpha[i] = background_color.Alpha();
} }
// paste original image // paste original image
img.Paste(base_img, dw, dh, wxIMAGE_ALPHA_BLEND_COMPOSE); img.Paste(base_img, dw, dh, wxIMAGE_ALPHA_BLEND_COMPOSE);
// fill top left corner // fill top left corner
int pixel; int pixel;
int x_start = 0; int x_start = 0;
int y_start = 0; int y_start = 0;
int ref = dw + dh * width; int ref = dw + dh * width;
for (int y = 0; y < dh; ++y) { 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; pixel = x_start + x + (y_start + y) * width;
data[3 * pixel + 0] = data[3 * ref + 0]; data[3 * pixel + 0] = data[3 * ref + 0];
data[3 * pixel + 1] = data[3 * ref + 1]; data[3 * pixel + 1] = data[3 * ref + 1];
data[3 * pixel + 2] = data[3 * ref + 2]; data[3 * pixel + 2] = data[3 * ref + 2];
alpha[pixel] = alpha[ref]; 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];
} }
} }
// fill bottom right corner // fill top right corner
x_start = width - dw; x_start = width - dw;
y_start = height - dh; 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; ref = (x_start - 1) + (y_start - 1) * width;
for (int y = 0; y < dh; ++y) { 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; pixel = x_start + x + (y_start + y) * width;
data[3 * pixel + 0] = data[3 * ref + 0]; data[3 * pixel + 0] = data[3 * ref + 0];
data[3 * pixel + 1] = data[3 * ref + 1]; data[3 * pixel + 1] = data[3 * ref + 1];
data[3 * pixel + 2] = data[3 * ref + 2]; data[3 * pixel + 2] = data[3 * ref + 2];
alpha[pixel] = alpha[ref]; alpha[pixel] = alpha[ref];
} }
} }
// fill left border // fill left border
x_start = 0; x_start = 0;
y_start = dh; y_start = dh;
for (int y = 0; y < height - dh - dh; ++y) { for (int y = 0; y < height - dh - dh; ++y) {
ref = dw + (y_start + y) * width; 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; pixel = x_start + x + (y_start + y) * width;
data[3 * pixel + 0] = data[3 * ref + 0]; data[3 * pixel + 0] = data[3 * ref + 0];
data[3 * pixel + 1] = data[3 * ref + 1]; data[3 * pixel + 1] = data[3 * ref + 1];
data[3 * pixel + 2] = data[3 * ref + 2]; data[3 * pixel + 2] = data[3 * ref + 2];
alpha[pixel] = alpha[ref]; alpha[pixel] = alpha[ref];
} }
} }
// fill top border // fill top border
x_start = dw; x_start = dw;
y_start = 0; y_start = 0;
for (int y = 0; y < dh; ++y) { for (int y = 0; y < dh; ++y) {
for (int x = 0; x < width - dw - dw; ++x) { for (int x = 0; x < width - dw - dw; ++x) {
pixel = x_start + x + (y_start + y) * width; pixel = x_start + x + (y_start + y) * width;
ref = x_start + x + dh * width; ref = x_start + x + dh * width;
data[3 * pixel + 0] = data[3 * ref + 0]; data[3 * pixel + 0] = data[3 * ref + 0];
data[3 * pixel + 1] = data[3 * ref + 1]; data[3 * pixel + 1] = data[3 * ref + 1];
data[3 * pixel + 2] = data[3 * ref + 2]; data[3 * pixel + 2] = data[3 * ref + 2];
alpha[pixel] = alpha[ref]; alpha[pixel] = alpha[ref];
} }
} }
// fill right border // fill right border
x_start = width - dw; x_start = width - dw;
y_start = dh; y_start = dh;
for (int y = 0; y < height - dh - dh; ++y) { for (int y = 0; y < height - dh - dh; ++y) {
ref = width - dw - 1 + (y_start + y) * width; 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; pixel = x_start + x + (y_start + y) * width;
data[3 * pixel + 0] = data[3 * ref + 0]; data[3 * pixel + 0] = data[3 * ref + 0];
data[3 * pixel + 1] = data[3 * ref + 1]; data[3 * pixel + 1] = data[3 * ref + 1];
data[3 * pixel + 2] = data[3 * ref + 2]; data[3 * pixel + 2] = data[3 * ref + 2];
alpha[pixel] = alpha[ref]; alpha[pixel] = alpha[ref];
} }
} }
// fill bottom border // fill bottom border
x_start = dw; x_start = dw;
y_start = height - dh; y_start = height - dh;
for (int y = 0; y < dh; ++y) { for (int y = 0; y < dh; ++y) {
for (int x = 0; x < width - dw - dw; ++x) { for (int x = 0; x < width - dw - dw; ++x) {
pixel = x_start + x + (y_start + y) * width; pixel = x_start + x + (y_start + y) * width;
ref = x_start + x + (height - dh - 1) * width; ref = x_start + x + (height - dh - 1) * width;
data[3 * pixel + 0] = data[3 * ref + 0]; data[3 * pixel + 0] = data[3 * ref + 0];
data[3 * pixel + 1] = data[3 * ref + 1]; data[3 * pixel + 1] = data[3 * ref + 1];
data[3 * pixel + 2] = data[3 * ref + 2]; data[3 * pixel + 2] = data[3 * ref + 2];
alpha[pixel] = alpha[ref]; alpha[pixel] = alpha[ref];
} }
} }
// done // done
@@ -621,7 +621,7 @@ Image PackagedImage::generate(const Options& opt) const {
if (img.HasMask()) img.InitAlpha(); // we can't handle masks if (img.HasMask()) img.InitAlpha(); // we can't handle masks
return img; return img;
} else { } 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 { 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 { bool BuiltInImage::operator == (const GeneratedImage& that) const {
const BuiltInImage* that2 = dynamic_cast<const BuiltInImage*>(&that); const BuiltInImage* that2 = dynamic_cast<const BuiltInImage*>(&that);
return that2 && name == that2->name; return that2 && name == that2->name;
} }
// ----------------------------------------------------------------------------- : ArbitraryImage // ----------------------------------------------------------------------------- : ArbitraryImage
Image ArbitraryImage::generate(const Options& opt) const { Image ArbitraryImage::generate(const Options& opt) const {
return image; return image;
} }
bool ArbitraryImage::operator == (const GeneratedImage& that) const { bool ArbitraryImage::operator == (const GeneratedImage& that) const {
const ArbitraryImage* that2 = dynamic_cast<const ArbitraryImage*>(&that); const ArbitraryImage* that2 = dynamic_cast<const ArbitraryImage*>(&that);
return that2 && image.IsSameAs(that2->image); return that2 && image.IsSameAs(that2->image);
} }
// ----------------------------------------------------------------------------- : SymbolToImage // ----------------------------------------------------------------------------- : SymbolToImage
@@ -716,48 +716,48 @@ bool ImageValueToImage::operator == (const GeneratedImage& that) const {
return that2 && filename == that2->filename return that2 && filename == that2->filename
&& age == that2->age; && age == that2->age;
} }
// ----------------------------------------------------------------------------- : ExternalImage // ----------------------------------------------------------------------------- : ExternalImage
Image ExternalImage::generate(const Options& opt) const { Image ExternalImage::generate(const Options& opt) const {
wxFileName fname(filepath, wxPATH_UNIX); wxFileName fname(filepath, wxPATH_UNIX);
String filePathString = fname.GetAbsolutePath(); String filePathString = fname.GetAbsolutePath();
// has a pre-existing .mse-set file been loaded? // 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)); if (opt.local_package->needSaveAs()) throw ScriptError(_ERROR_1_("can't import image without set", filePathString));
// does the file pointed to by filepath exist? // does the file pointed to by filepath exist?
if (!fname.FileExists()) throw ScriptError(_ERROR_1_("import not found", filePathString)); if (!fname.FileExists()) throw ScriptError(_ERROR_1_("import not found", filePathString));
String fileExt = fname.GetExt(); String fileExt = fname.GetExt();
wxBitmapType bitmapType; wxBitmapType bitmapType;
if (fileExt == _("png")) bitmapType = wxBITMAP_TYPE_PNG; if (fileExt == _("png")) bitmapType = wxBITMAP_TYPE_PNG;
else if (fileExt == _("jpg") || fileExt == _("jpeg")) bitmapType = wxBITMAP_TYPE_JPEG; else if (fileExt == _("jpg") || fileExt == _("jpeg")) bitmapType = wxBITMAP_TYPE_JPEG;
else bitmapType = wxBITMAP_TYPE_BMP; else bitmapType = wxBITMAP_TYPE_BMP;
// does the file exist in the package? // does the file exist in the package?
String fileNameNoExtension = fname.GetName(); String fileNameNoExtension = fname.GetName();
if (!opt.local_package->existsIn(fileNameNoExtension)) { if (!opt.local_package->existsIn(fileNameNoExtension)) {
auto outStream = opt.local_package->openOut(fileNameNoExtension); auto outStream = opt.local_package->openOut(fileNameNoExtension);
wxFileInputStream inStream = wxFileInputStream(filepath.ToStdString()); wxFileInputStream inStream = wxFileInputStream(filepath.ToStdString());
if (!inStream.IsOk()) throw ScriptError(_ERROR_1_("can't create file stream", filePathString)); if (!inStream.IsOk()) throw ScriptError(_ERROR_1_("can't create file stream", filePathString));
outStream->Write(inStream); outStream->Write(inStream);
if (!outStream->IsOk()) throw ScriptError(_ERROR_1_("can't write image to set", filePathString)); if (!outStream->IsOk()) throw ScriptError(_ERROR_1_("can't write image to set", filePathString));
outStream->Close(); outStream->Close();
} }
// save the package with the new image // save the package with the new image
opt.local_package->save(false); opt.local_package->save(false);
auto imageInputStream = opt.local_package->openIn(fileNameNoExtension); auto imageInputStream = opt.local_package->openIn(fileNameNoExtension);
Image img(*imageInputStream.get(), bitmapType); Image img(*imageInputStream.get(), bitmapType);
if (!img.IsOk()) throw ScriptError(_ERROR_1_("can't import image", filePathString)); if (!img.IsOk()) throw ScriptError(_ERROR_1_("can't import image", filePathString));
return img; return img;
} }
bool ExternalImage::operator == (const GeneratedImage& that) const { bool ExternalImage::operator == (const GeneratedImage& that) const {
const ExternalImage* that2 = dynamic_cast<const ExternalImage*>(&that); const ExternalImage* that2 = dynamic_cast<const ExternalImage*>(&that);
return that2 && that2->filepath == filepath; return that2 && that2->filepath == filepath;
} }
+27 -27
View File
@@ -327,9 +327,9 @@ public:
{} {}
Image generate(const Options& opt) const override; Image generate(const Options& opt) const override;
bool operator == (const GeneratedImage& that) const override; bool operator == (const GeneratedImage& that) const override;
private: private:
GeneratedImageP base_image; GeneratedImageP base_image;
double horizontal_size, vertical_size; double horizontal_size, vertical_size;
Color background_color; Color background_color;
}; };
@@ -395,18 +395,18 @@ public:
bool operator == (const GeneratedImage& that) const override; bool operator == (const GeneratedImage& that) const override;
private: private:
String name; String name;
}; };
// ----------------------------------------------------------------------------- : Arbitrary // ----------------------------------------------------------------------------- : Arbitrary
class ArbitraryImage : public GeneratedImage { class ArbitraryImage : public GeneratedImage {
public: public:
inline ArbitraryImage(const Image image) inline ArbitraryImage(const Image image)
: image(image) : image(image)
{} {}
Image generate(const Options& opt) const override; Image generate(const Options& opt) const override;
bool operator == (const GeneratedImage& that) const override; bool operator == (const GeneratedImage& that) const override;
private: private:
Image image; Image image;
}; };
@@ -447,17 +447,17 @@ private:
LocalFileName filename; LocalFileName filename;
Age age; ///< Age the image was last updated Age age; ///< Age the image was last updated
}; };
// ----------------------------------------------------------------------------- : ExternalImage // ----------------------------------------------------------------------------- : ExternalImage
/// Load an image from the filesystem /// Load an image from the filesystem
class ExternalImage : public GeneratedImage { class ExternalImage : public GeneratedImage {
public: public:
ExternalImage(const String& filepath) : filepath(filepath) {}; ExternalImage(const String& filepath) : filepath(filepath) {};
Image generate(const Options&) const override; Image generate(const Options&) const override;
bool operator == (const GeneratedImage& that) const override; bool operator == (const GeneratedImage& that) const override;
inline String toString() { return filepath; } inline String toString() { return filepath; }
inline String toCode() const override { return _("<image>"); } inline String toCode() const override { return _("<image>"); }
private: private:
String filepath; String filepath;
}; };
+1 -1
View File
@@ -172,7 +172,7 @@ enum ImageCombine
, COMBINE_GREATER_THAN_235 , COMBINE_GREATER_THAN_235
, COMBINE_GREATER_THAN_240 , COMBINE_GREATER_THAN_240
, COMBINE_GREATER_THAN_245 , COMBINE_GREATER_THAN_245
, COMBINE_GREATER_THAN_250 , COMBINE_GREATER_THAN_250
, COMBINE_SMALLER_THAN_5 , COMBINE_SMALLER_THAN_5
, COMBINE_SMALLER_THAN_10 , COMBINE_SMALLER_THAN_10
, COMBINE_SMALLER_THAN_15 , 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 <util/prec.hpp>
#include <gui/control/card_editor.hpp> #include <gui/control/card_editor.hpp>
#include <gui/value/editor.hpp> #include <gui/value/editor.hpp>
#include <gui/set/cards_panel.hpp>
#include <gui/util.hpp> #include <gui/util.hpp>
#include <data/field.hpp> #include <data/field.hpp>
#include <data/stylesheet.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) { void DataEditor::onMouseLeave(wxMouseEvent& ev) {
// on mouse leave for editor // on mouse leave for editor
if (hovered_viewer) { if (hovered_viewer) {
@@ -462,6 +473,13 @@ void DataEditor::onChar(wxKeyEvent& ev) {
} else { } else {
ev.Skip(); ev.Skip();
} }
if (GetId() == ID_CARD_LINK_EDITOR) {
CardsPanel* panel = dynamic_cast<CardsPanel*> (GetParent());
if (panel) {
panel->refreshCard(card);
}
}
} }
// ----------------------------------------------------------------------------- : Menu events // ----------------------------------------------------------------------------- : Menu events
@@ -503,6 +521,10 @@ void DataEditor::onFocus(wxFocusEvent& ev) {
selectFirst(); selectFirst();
} }
} }
CardsPanel* panel = dynamic_cast<CardsPanel*> (GetParent());
if (panel) {
panel->setFocusedEditor(this);
}
} }
void DataEditor::onLoseFocus(wxFocusEvent& ev) { void DataEditor::onLoseFocus(wxFocusEvent& ev) {
if (current_editor) { if (current_editor) {
@@ -520,6 +542,7 @@ BEGIN_EVENT_TABLE(DataEditor, CardViewer)
EVT_RIGHT_DOWN (DataEditor::onRightDown) EVT_RIGHT_DOWN (DataEditor::onRightDown)
EVT_MOTION (DataEditor::onMotion) EVT_MOTION (DataEditor::onMotion)
EVT_MOUSEWHEEL (DataEditor::onMouseWheel) EVT_MOUSEWHEEL (DataEditor::onMouseWheel)
EVT_ENTER_WINDOW (DataEditor::onMouseEnter)
EVT_LEAVE_WINDOW (DataEditor::onMouseLeave) EVT_LEAVE_WINDOW (DataEditor::onMouseLeave)
EVT_CONTEXT_MENU (DataEditor::onContextMenu) EVT_CONTEXT_MENU (DataEditor::onContextMenu)
EVT_MENU (wxID_ANY, DataEditor::onMenu) EVT_MENU (wxID_ANY, DataEditor::onMenu)
+1
View File
@@ -111,6 +111,7 @@ private:
void onRightDown (wxMouseEvent&); void onRightDown (wxMouseEvent&);
void onMotion (wxMouseEvent&); void onMotion (wxMouseEvent&);
void onMouseWheel(wxMouseEvent&); void onMouseWheel(wxMouseEvent&);
void onMouseEnter(wxMouseEvent&);
void onMouseLeave(wxMouseEvent&); void onMouseLeave(wxMouseEvent&);
void onLoseCapture(wxMouseCaptureLostEvent&); void onLoseCapture(wxMouseCaptureLostEvent&);
+54 -6
View File
@@ -10,8 +10,9 @@
#include <gui/control/card_list.hpp> #include <gui/control/card_list.hpp>
#include <gui/control/card_list_column_select.hpp> #include <gui/control/card_list_column_select.hpp>
#include <gui/set/window.hpp> // for sorting all cardlists in a window #include <gui/set/window.hpp> // for sorting all cardlists in a window
#include <gui/util.hpp> #include <gui/card_link_window.hpp>
#include <gui/add_csv_window.hpp> #include <gui/util.hpp>
#include <gui/add_csv_window.hpp>
#include <gui/add_json_window.hpp> #include <gui/add_json_window.hpp>
#include <data/game.hpp> #include <data/game.hpp>
#include <data/field.hpp> #include <data/field.hpp>
@@ -25,6 +26,7 @@
#include <data/action/value.hpp> #include <data/action/value.hpp>
#include <util/window_id.hpp> #include <util/window_id.hpp>
#include <wx/clipbrd.h> #include <wx/clipbrd.h>
#include <unordered_set>
DECLARE_POINTER_TYPE(ChoiceValue); DECLARE_POINTER_TYPE(ChoiceValue);
@@ -157,6 +159,31 @@ bool CardListBase::doCopy() {
wxTheClipboard->Close(); wxTheClipboard->Close();
return ok; 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() { bool CardListBase::doPaste() {
// get data // get data
if (!canPaste()) return false; if (!canPaste()) return false;
@@ -182,7 +209,26 @@ bool CardListBase::doDelete() {
set->actions.addAction(make_unique<AddCardAction>(REMOVE, *set, cards_to_delete)); set->actions.addAction(make_unique<AddCardAction>(REMOVE, *set, cards_to_delete));
return true; 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() { bool CardListBase::doAddCSV() {
AddCSVWindow wnd(this, set, true); AddCSVWindow wnd(this, set, true);
if (wnd.ShowModal() == wxID_OK) { if (wnd.ShowModal() == wxID_OK) {
@@ -190,8 +236,8 @@ bool CardListBase::doAddCSV() {
return true; return true;
} }
return false; return false;
} }
bool CardListBase::doAddJSON() { bool CardListBase::doAddJSON() {
AddJSONWindow wnd(this, set, true); AddJSONWindow wnd(this, set, true);
if (wnd.ShowModal() == wxID_OK) { if (wnd.ShowModal() == wxID_OK) {
@@ -199,7 +245,7 @@ bool CardListBase::doAddJSON() {
return true; return true;
} }
return false; return false;
} }
// ----------------------------------------------------------------------------- : CardListBase : Building the list // ----------------------------------------------------------------------------- : CardListBase : Building the list
@@ -412,10 +458,12 @@ void CardListBase::onContextMenu(wxContextMenuEvent&) {
wxMenu m; wxMenu m;
add_menu_item_tr(&m, wxID_CUT, "cut", "cut_card"); 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, 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"); add_menu_item_tr(&m, wxID_PASTE, "paste", "paste_card");
m.AppendSeparator(); m.AppendSeparator();
add_menu_item_tr(&m, ID_CARD_ADD, "card_add", "add card"); 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_REMOVE, "card_del", "remove card");
add_menu_item_tr(&m, ID_CARD_LINK, "card_link", "link card");
PopupMenu(&m); PopupMenu(&m);
} }
} }
+12 -5
View File
@@ -64,9 +64,9 @@ public:
// --------------------------------------------------- : Selection // --------------------------------------------------- : Selection
inline CardP getCard() const { return static_pointer_cast<Card>(selected_item); } inline CardP getCard() const { return static_pointer_cast<Card>(selected_item); }
inline void setCard(const CardP& card) { selectItem(card, true, false); } inline void setCard(const CardP& card, bool event = false) { selectItem(card, true, event); }
// --------------------------------------------------- : Clipboard // --------------------------------------------------- : Clipboard
bool canCut() const override; bool canCut() const override;
@@ -75,11 +75,18 @@ public:
bool canDelete() const override; bool canDelete() const override;
// Try to perform a clipboard operation, return success // Try to perform a clipboard operation, return success
bool doCopy() override; bool doCopy() override;
bool doCopyCardAndLinkedCards();
bool doPaste() override; bool doPaste() override;
bool doDelete() override; bool doDelete() override;
bool doAddCSV(); bool doAddCSV();
bool doAddJSON(); bool doAddJSON();
// --------------------------------------------------- : Card linking
bool canLink() const;
bool doLink();
bool doUnlink(CardP unlinked_card);
// --------------------------------------------------- : Set actions // --------------------------------------------------- : Set actions
void onBeforeChangeSet() override; void onBeforeChangeSet() override;
@@ -107,7 +114,7 @@ protected:
/// Send an 'item selected' event for the currently selected item (selected_item) /// Send an 'item selected' event for the currently selected item (selected_item)
void sendEvent() override { sendEvent(EVENT_CARD_SELECT); } void sendEvent() override { sendEvent(EVENT_CARD_SELECT); }
void sendEvent(int type = EVENT_CARD_SELECT); void sendEvent(int type);
/// Compare cards /// Compare cards
bool compareItems(void* a, void* b) const override; bool compareItems(void* a, void* b) const override;
+23 -2
View File
@@ -8,10 +8,13 @@
#include <util/prec.hpp> #include <util/prec.hpp>
#include <gui/control/card_viewer.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/stylesheet.hpp>
#include <data/settings.hpp> #include <data/settings.hpp>
#include <render/value/viewer.hpp> #include <render/value/viewer.hpp>
#include <wx/dcbuffer.h> #include <wx/dcbuffer.h>
#include <util/window_id.hpp>
// ----------------------------------------------------------------------------- : Events // ----------------------------------------------------------------------------- : Events
@@ -31,7 +34,11 @@ wxSize CardViewer::DoGetBestSize() const {
if (set) { if (set) {
if (!stylesheet) stylesheet = set->stylesheet; if (!stylesheet) stylesheet = set->stylesheet;
StyleSheetSettings& ss = settings.stylesheetSettingsFor(*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); if (is_sideways(deg_to_rad(ss.card_angle()))) swap(size.x, size.y);
return size + ws - cs; 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) { void CardViewer::drawViewer(RotatedDC& dc, ValueViewer& v) {
if (shouldDraw(v)) v.draw(dc); if (shouldDraw(v)) v.draw(dc);
} }
@@ -150,11 +167,15 @@ Rotation CardViewer::getRotation() const {
StyleSheetSettings& ss = settings.stylesheetSettingsFor(*stylesheet); StyleSheetSettings& ss = settings.stylesheetSettingsFor(*stylesheet);
int dx = CanScroll(wxHORIZONTAL) ? GetScrollPos(wxHORIZONTAL) : 0; int dx = CanScroll(wxHORIZONTAL) ? GetScrollPos(wxHORIZONTAL) : 0;
int dy = CanScroll(wxVERTICAL) ? GetScrollPos(wxVERTICAL) : 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 // ----------------------------------------------------------------------------- : Event table
BEGIN_EVENT_TABLE(CardViewer, wxControl) BEGIN_EVENT_TABLE(CardViewer, wxControl)
EVT_PAINT(CardViewer::onPaint) EVT_PAINT(CardViewer::onPaint)
EVT_LEFT_DOWN(CardViewer::onClick)
END_EVENT_TABLE () END_EVENT_TABLE ()
+4 -2
View File
@@ -53,9 +53,11 @@ protected:
private: private:
DECLARE_EVENT_TABLE(); DECLARE_EVENT_TABLE();
void onPaint(wxPaintEvent&); void onPaint(wxPaintEvent&);
void onClick(wxMouseEvent&);
Bitmap buffer; ///< Off-screen buffer we draw to Bitmap buffer; ///< Off-screen buffer we draw to
bool up_to_date; ///< Is the buffer up to date? 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(); focusNone();
} }
selected_item = item; selected_item = item;
if (event) sendEvent(); if (event) sendEvent(); // sending an event will trigger a UI update
findSelectedItemPos(); findSelectedItemPos();
if (focus) focusSelectedItem(); 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) { void ItemList::focusSelectedItem(bool force_focus) {
if (GetItemCount() > 0) { if (GetItemCount() > 0) {
if (selected_item_pos == -1 || (size_t)selected_item_pos > sorted_list.size()) { if (selected_item_pos == -1 || (size_t)selected_item_pos > sorted_list.size()) {
+3 -1
View File
@@ -42,7 +42,9 @@ public:
void selectFirst(); void selectFirst();
/// Select all items /// Select all items
void doSelectAll(); void doSelectAll();
/// Find the position for a given item
long findGivenItemPos(const VoidP& item);
// --------------------------------------------------- : Clipboard // --------------------------------------------------- : Clipboard
virtual bool canCut() const { return canCopy() && canDelete(); } virtual bool canCut() const { return canCopy() && canDelete(); }
+9 -9
View File
@@ -48,9 +48,9 @@ protected:
virtual void onHide() {} virtual void onHide() {}
inline bool isRoot() { return parent_menu == nullptr; } inline bool isRoot() { return parent_menu == nullptr; }
/// Should the list of choices be displayed as a slider (if all choices are numbers) /// Should the list of choices be displayed as a slider (if all choices are numbers)
bool is_slider = false; bool is_slider = false;
// --------------------------------------------------- : Selection // --------------------------------------------------- : Selection
static const size_t NO_SELECTION = (size_t)-1; static const size_t NO_SELECTION = (size_t)-1;
@@ -82,12 +82,12 @@ protected:
static const int marginW = 0; static const int marginW = 0;
static const int marginH = 0; static const int marginH = 0;
static bool slider_loaded; static bool slider_loaded;
static wxBitmap slider_left; static wxBitmap slider_left;
static wxBitmap slider_right; static wxBitmap slider_right;
static wxBitmap slider_center; static wxBitmap slider_center;
static wxBitmap slider_tick; static wxBitmap slider_tick;
// may be changed by derived class // may be changed by derived class
int text_offset; ///< Vertical distance between top of item and text int text_offset; ///< Vertical distance between top of item and text
+152 -85
View File
@@ -13,15 +13,18 @@
#include <util/rotation.hpp> #include <util/rotation.hpp>
#include <gfx/gfx.hpp> #include <gfx/gfx.hpp>
#include <wx/spinctrl.h> #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::ImageSlice(const Image& source, const wxSize& target_size) ImageSlice::ImageSlice(const Image& source, const String& source_path, const String& card_name, const wxSize& target_size)
: source(source), target_size(target_size) : source(source), source_path(source_path), card_name(card_name), target_size(target_size)
, selection(0, 0, source.GetWidth(), source.GetHeight()) , selection(0, 0, source.GetWidth(), source.GetHeight())
, allow_outside(false), aspect_fixed(true) , allow_outside(false), aspect_fixed(true)
, sharpen(false), sharpen_amount(25) , sharpen(false), sharpen_amount(0)
{} {}
void ImageSlice::constrain(PreferedProperty prefer) { void ImageSlice::constrain(PreferedProperty prefer) {
@@ -51,24 +54,24 @@ void ImageSlice::constrain(PreferedProperty prefer) {
} }
} }
void ImageSlice::centerSelection() { void ImageSlice::centerSelection() {
centerSelectionHorizontally(); centerSelectionHorizontally();
centerSelectionVertically(); centerSelectionVertically();
} }
void ImageSlice::centerSelectionHorizontally() { void ImageSlice::centerSelectionHorizontally() {
if (selection.GetWidth() < source.GetWidth()) { if (selection.GetWidth() < source.GetWidth()) {
selection.x = ((source.GetWidth() - selection.GetWidth()) / 2); 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; 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) { if (selection.width == scaled_target_size.GetWidth() && selection.height == scaled_target_size.GetHeight() && selection.x == 0 && selection.y == 0) {
// exactly the right size // exactly the right size
@@ -93,20 +96,31 @@ DEFINE_EVENT_TYPE(EVENT_SLICE_CHANGED);
// ----------------------------------------------------------------------------- : ImageSliceWindow // ----------------------------------------------------------------------------- : 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) : 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) , initialized(false)
{ {
// init slice // init slice
slice.constrain(); pair<String, String> settings_entry = { filename, cardname };
slice.centerSelection(); 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 // init controls
const wxPoint defPos = wxDefaultPosition; const wxPoint defPos = wxDefaultPosition;
const wxSize spinSize(80,-1); const wxSize spinSize(80,-1);
selector = new ImageSliceSelector(this, ID_SELECTOR, slice); 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") String sizes[] = { _LABEL_("original size")
, _LABEL_("size to fit") , _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); sharpen_amount = new wxSlider(this, ID_SHARPEN_AMOUNT, 0, 0, 100);
// allowOutside= new CheckBox(&this, idSliceAllowOutside, _("Allow selection outside source")) // allowOutside= new CheckBox(&this, idSliceAllowOutside, _("Allow selection outside source"))
// bgColor = new ColorSelector(&this, wxID_ANY) // 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 // init sizers
wxSizer* s = new wxBoxSizer(wxVERTICAL); wxSizer* s = new wxBoxSizer(wxVERTICAL);
// top row: image editors // top row: image editors
@@ -164,22 +185,22 @@ ImageSliceWindow::ImageSliceWindow(Window* parent, const Image& source, const wx
s7->Add(width, 0, wxEXPAND); s7->Add(width, 0, wxEXPAND);
s7->Add(new wxStaticText(this, wxID_ANY, _LABEL_("selection height")), 0, wxALIGN_CENTER_VERTICAL); s7->Add(new wxStaticText(this, wxID_ANY, _LABEL_("selection height")), 0, wxALIGN_CENTER_VERTICAL);
s7->Add(height, 0, wxEXPAND); s7->Add(height, 0, wxEXPAND);
s7->Add(new wxStaticText(this, wxID_ANY, _LABEL_("selection center")), 0, wxALIGN_CENTER_VERTICAL); s7->Add(new wxStaticText(this, wxID_ANY, _LABEL_("selection center")), 0, wxALIGN_CENTER_VERTICAL);
wxBoxSizer* s7A = new wxBoxSizer(wxHORIZONTAL); wxBoxSizer* s7A = new wxBoxSizer(wxHORIZONTAL);
wxBitmapButton* center_vertically_button = new wxBitmapButton(this, ID_SELECTION_CENTER_VERTICALLY, wxBitmap(load_resource_image(_("shape_align_middle")))); 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")); center_vertically_button->SetToolTip(_LABEL_("selection center vertically"));
s7A->Add(center_vertically_button); s7A->Add(center_vertically_button);
s7A->AddStretchSpacer(); s7A->AddStretchSpacer();
wxBitmapButton* center_horizontally_button = new wxBitmapButton(this, ID_SELECTION_CENTER_HORIZONTALLY, wxBitmap(load_resource_image(_("shape_align_center")))); 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")); center_horizontally_button->SetToolTip(_LABEL_("selection center horizontally"));
s7A->Add(center_horizontally_button); s7A->Add(center_horizontally_button);
s7A->AddStretchSpacer(); s7A->AddStretchSpacer();
wxBitmapButton* center_button = new wxBitmapButton(this, ID_SELECTION_CENTER, wxBitmap(load_resource_image(_("shape_align_both")))); wxBitmapButton* center_button = new wxBitmapButton(this, ID_SELECTION_CENTER, wxBitmap(load_resource_image(_("shape_align_both"))));
center_button->SetToolTip(_LABEL_("selection center both")); center_button->SetToolTip(_LABEL_("selection center both"));
s7A->Add(center_button); s7A->Add(center_button);
s7->Add(s7A, 1, wxEXPAND, 0); s7->Add(s7A, 1, wxEXPAND, 0);
s6->Add(s7, 1, wxEXPAND | wxALL, 4); s6->Add(s7, 1, wxEXPAND | wxALL, 4);
s5->Add(s6, 0, 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); sB->Add(sharpen_amount, 0, wxEXPAND | wxALL, 4);
s5->Add(sB, 0, wxEXPAND | wxALL, 4); s5->Add(sB, 0, wxEXPAND | wxALL, 4);
s5->AddStretchSpacer(1); s5->AddStretchSpacer(1);
s5->Add(grid, 0, wxEXPAND | wxALL, 4);
s5->AddStretchSpacer(1);
s->Add(s5, 0, wxEXPAND); s->Add(s5, 0, wxEXPAND);
s->Add(CreateButtonSizer(wxOK | wxCANCEL), 0, wxEXPAND | wxALL, 8); s->Add(CreateButtonSizer(wxOK | wxCANCEL), 0, wxEXPAND | wxALL, 8);
s->SetSizeHints(this); s->SetSizeHints(this);
@@ -219,8 +242,11 @@ void ImageSliceWindow::onOk(wxCommandEvent&) {
EndModal(wxID_OK); EndModal(wxID_OK);
} }
Image ImageSliceWindow::getImage(double scale) const { Image ImageSliceWindow::getImage(double scale) const {
return slice.getSlice(scale); 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 // ----------------------------------------------------------------------------- : ImageSliceWindow : Controls
@@ -249,7 +275,13 @@ void ImageSliceWindow::onChangeSize(wxCommandEvent&) {
slice.aspect_fixed = false; slice.aspect_fixed = false;
onUpdateFromControl(); onUpdateFromControl();
} }
} }
void ImageSliceWindow::onChangeGrid(wxCommandEvent&) {
if (!initialized) return;
preview->grid = grid->GetSelection();
preview->update();
}
void ImageSliceWindow::onChangeLeft(wxCommandEvent&) { void ImageSliceWindow::onChangeLeft(wxCommandEvent&) {
if (!initialized) return; if (!initialized) return;
@@ -322,16 +354,16 @@ void ImageSliceWindow::onUpdateFromControl(PreferedProperty prefer) {
updateControls(); updateControls();
} }
void ImageSliceWindow::onSelectionCenter(wxCommandEvent& ev) { void ImageSliceWindow::onSelectionCenter(wxCommandEvent& ev) {
switch (ev.GetId()) { switch (ev.GetId()) {
case ID_SELECTION_CENTER: case ID_SELECTION_CENTER:
slice.centerSelection(); slice.centerSelection();
break; break;
case ID_SELECTION_CENTER_HORIZONTALLY: case ID_SELECTION_CENTER_HORIZONTALLY:
slice.centerSelectionHorizontally(); slice.centerSelectionHorizontally();
break; break;
case ID_SELECTION_CENTER_VERTICALLY: case ID_SELECTION_CENTER_VERTICALLY:
slice.centerSelectionVertically(); slice.centerSelectionVertically();
break; break;
} }
@@ -384,22 +416,23 @@ void ImageSliceWindow::updateControls() {
// ----------------------------------------------------------------------------- : ImageSliceWindow : Event table // ----------------------------------------------------------------------------- : ImageSliceWindow : Event table
BEGIN_EVENT_TABLE(ImageSliceWindow, wxDialog) BEGIN_EVENT_TABLE(ImageSliceWindow, wxDialog)
EVT_BUTTON (wxID_OK, ImageSliceWindow::onOk) EVT_BUTTON (wxID_OK, ImageSliceWindow::onOk)
EVT_RADIOBOX (ID_SIZE, ImageSliceWindow::onChangeSize) EVT_RADIOBOX (ID_SIZE, ImageSliceWindow::onChangeSize)
EVT_TEXT (ID_LEFT, ImageSliceWindow::onChangeLeft) EVT_RADIOBOX (ID_GRID, ImageSliceWindow::onChangeGrid)
EVT_TEXT (ID_TOP, ImageSliceWindow::onChangeTop) EVT_TEXT (ID_LEFT, ImageSliceWindow::onChangeLeft)
EVT_TEXT (ID_WIDTH, ImageSliceWindow::onChangeWidth) EVT_TEXT (ID_TOP, ImageSliceWindow::onChangeTop)
EVT_TEXT (ID_HEIGHT, ImageSliceWindow::onChangeHeight) EVT_TEXT (ID_WIDTH, ImageSliceWindow::onChangeWidth)
EVT_BUTTON (ID_SELECTION_CENTER, ImageSliceWindow::onSelectionCenter) EVT_TEXT (ID_HEIGHT, ImageSliceWindow::onChangeHeight)
EVT_BUTTON(ID_SELECTION_CENTER_HORIZONTALLY, ImageSliceWindow::onSelectionCenter) EVT_BUTTON (ID_SELECTION_CENTER, ImageSliceWindow::onSelectionCenter)
EVT_BUTTON(ID_SELECTION_CENTER_VERTICALLY, ImageSliceWindow::onSelectionCenter) EVT_BUTTON (ID_SELECTION_CENTER_HORIZONTALLY, ImageSliceWindow::onSelectionCenter)
EVT_CHECKBOX (ID_FIX_ASPECT, ImageSliceWindow::onChangeFixAspect) EVT_BUTTON (ID_SELECTION_CENTER_VERTICALLY, ImageSliceWindow::onSelectionCenter)
EVT_SPINCTRL (ID_ZOOM, ImageSliceWindow::onChangeZoom) EVT_CHECKBOX (ID_FIX_ASPECT, ImageSliceWindow::onChangeFixAspect)
EVT_SPINCTRL (ID_ZOOM_X, ImageSliceWindow::onChangeZoomX) EVT_SPINCTRL (ID_ZOOM, ImageSliceWindow::onChangeZoom)
EVT_SPINCTRL (ID_ZOOM_Y, ImageSliceWindow::onChangeZoomY) EVT_SPINCTRL (ID_ZOOM_X, ImageSliceWindow::onChangeZoomX)
EVT_CHECKBOX (ID_SHARPEN, ImageSliceWindow::onChangeSharpen) EVT_SPINCTRL (ID_ZOOM_Y, ImageSliceWindow::onChangeZoomY)
EVT_COMMAND_SCROLL (ID_SHARPEN_AMOUNT, ImageSliceWindow::onChangeSharpenAmount) EVT_CHECKBOX (ID_SHARPEN, ImageSliceWindow::onChangeSharpen)
EVT_SLICE_CHANGED (wxID_ANY, ImageSliceWindow::onSliceChange) EVT_COMMAND_SCROLL (ID_SHARPEN_AMOUNT, ImageSliceWindow::onChangeSharpenAmount)
EVT_SLICE_CHANGED (wxID_ANY, ImageSliceWindow::onSliceChange)
// EVT_SIZE ( ImageSliceWindow::onSize) // EVT_SIZE ( ImageSliceWindow::onSize)
END_EVENT_TABLE () END_EVENT_TABLE ()
@@ -409,10 +442,11 @@ END_EVENT_TABLE ()
// ----------------------------------------------------------------------------- : ImageSlicePreview // ----------------------------------------------------------------------------- : 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) : wxControl(parent, id, wxDefaultPosition, wxDefaultSize, wxBORDER_THEME)
, slice(slice) , slice(slice)
, mask(mask) , mask(mask)
, grid(grid)
, mouse_down(false) , mouse_down(false)
{ {
SetBackgroundStyle(wxBG_STYLE_PAINT); SetBackgroundStyle(wxBG_STYLE_PAINT);
@@ -421,22 +455,22 @@ ImageSlicePreview::ImageSlicePreview(Window* parent, int id, ImageSlice& slice,
void ImageSlicePreview::update() { void ImageSlicePreview::update() {
bitmap = wxNullBitmap; bitmap = wxNullBitmap;
Refresh(false); Refresh(false);
} }
wxSize ImageSlicePreview::getBestSliceSize() const { wxSize ImageSlicePreview::getBestSliceSize() const {
float target_ratio = ((float)slice.target_size.GetWidth()) / ((float)slice.target_size.GetHeight()); float target_ratio = ((float)slice.target_size.GetWidth()) / ((float)slice.target_size.GetHeight());
if (target_ratio > 1.0) { if (target_ratio > 1.0) {
return wxSize(500, 500 / target_ratio); return wxSize(500, 500 / target_ratio);
} else { } else {
return wxSize(500 * target_ratio, 500); return wxSize(500 * target_ratio, 500);
} }
} }
wxSize ImageSlicePreview::DoGetBestSize() const { 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. // This helps with applying margins and other spacing necessities.
wxSize ws = GetSize(), cs = GetClientSize(); wxSize ws = GetSize(), cs = GetClientSize();
return getBestSliceSize() + ws - cs; return getBestSliceSize() + ws - cs;
} }
@@ -461,14 +495,47 @@ void ImageSlicePreview::draw(DC& dc) {
mdc.SelectObject(wxNullBitmap); mdc.SelectObject(wxNullBitmap);
} else { } else {
bitmap = Bitmap(image); bitmap = Bitmap(image);
} }
// Rescale the bitmap based on the available size. // Rescale the bitmap based on the available size.
auto available_size = getBestSliceSize(); auto available_size = getBestSliceSize();
bitmap = wxBitmap(bitmap.ConvertToImage().Scale(available_size.GetWidth(), available_size.GetHeight())); bitmap = wxBitmap(bitmap.ConvertToImage().Scale(available_size.GetWidth(), available_size.GetHeight()));
} }
if (bitmap.Ok()) { 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) : wxControl(parent, id, wxDefaultPosition, wxDefaultSize, wxBORDER_THEME)
, slice(slice) , slice(slice)
, mouse_down(false) , 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) { if (target_ratio > 1.0) {
SetMinSize(wxSize(500, 500 / target_ratio)); SetMinSize(wxSize(500, 500 / target_ratio));
} else { } else {
+29 -18
View File
@@ -28,23 +28,26 @@ enum PreferedProperty
/// A slice of an image, i.e. a selected rectangle /// A slice of an image, i.e. a selected rectangle
class ImageSlice { class ImageSlice {
public: 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 Image source; ///< The source image
wxSize target_size; ///< Size of the target image String source_path; ///< The filename of the source image (only used to find previously used settings)
Color background; ///< Color for areas outside the source image String card_name; ///< The identification of the card we're on (only used to find previously used settings)
wxRect selection; ///< Area to slect from source wxSize target_size; ///< Size of the target image
bool allow_outside; wxRect selection; ///< Area to slice from source
bool aspect_fixed; ///< Aspect ratio lock? 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 // Filters
bool sharpen; bool sharpen;
int sharpen_amount; int sharpen_amount;
/// Enforce relations between values /// Enforce relations between values
void constrain(PreferedProperty prefer = PREFER_NONE); void constrain(PreferedProperty prefer = PREFER_NONE);
/// Attempt to center the current constraints /// Attempt to center the current constraints
void centerSelection(); void centerSelection();
void centerSelectionHorizontally(); void centerSelectionHorizontally();
void centerSelectionVertically(); void centerSelectionVertically();
/// Get the sliced image /// Get the sliced image
Image getSlice(double scale = 1.0) const; Image getSlice(double scale = 1.0) const;
@@ -62,11 +65,16 @@ public:
/// Dialog for selecting a slice of an image /// Dialog for selecting a slice of an image
class ImageSliceWindow : public wxDialog { class ImageSliceWindow : public wxDialog {
public: 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 /// Return the sliced image
Image getImage(double scale) const; 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 // --------------------------------------------------- : Data
private: private:
// The slice we are extracting // The slice we are extracting
@@ -74,7 +82,7 @@ private:
// Gui items // Gui items
ImageSlicePreview* preview; ImageSlicePreview* preview;
ImageSliceSelector* selector; ImageSliceSelector* selector;
wxRadioBox* size; wxRadioBox* size, *grid;
wxSpinCtrl* top, *left, *width, *height; wxSpinCtrl* top, *left, *width, *height;
wxCheckBox* fix_aspect; wxCheckBox* fix_aspect;
wxSpinCtrl* zoom, *zoom_x, *zoom_y; wxSpinCtrl* zoom, *zoom_x, *zoom_y;
@@ -91,11 +99,12 @@ private:
void onSize (wxSizeEvent&); void onSize (wxSizeEvent&);
void onChangeSize (wxCommandEvent&); void onChangeSize (wxCommandEvent&);
void onChangeGrid (wxCommandEvent&);
void onChangeLeft (wxCommandEvent&); void onChangeLeft (wxCommandEvent&);
void onChangeTop (wxCommandEvent&); void onChangeTop (wxCommandEvent&);
void onChangeWidth (wxCommandEvent&); void onChangeWidth (wxCommandEvent&);
void onChangeHeight (wxCommandEvent&); void onChangeHeight (wxCommandEvent&);
void onSelectionCenter(wxCommandEvent&); void onSelectionCenter (wxCommandEvent&);
void onChangeFixAspect (wxCommandEvent&); void onChangeFixAspect (wxCommandEvent&);
void onChangeZoom (wxSpinEvent&); void onChangeZoom (wxSpinEvent&);
void onChangeZoomX (wxSpinEvent&); void onChangeZoomX (wxSpinEvent&);
@@ -120,11 +129,13 @@ private:
/// A preview of the sliced image /// A preview of the sliced image
class ImageSlicePreview : public wxControl { class ImageSlicePreview : public wxControl {
public: 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 /// Notify that the slice was updated
void update(); void update();
int grid;
// --------------------------------------------------- : Data // --------------------------------------------------- : Data
private: private:
Bitmap bitmap; Bitmap bitmap;
@@ -137,7 +148,7 @@ private:
// --------------------------------------------------- : Events // --------------------------------------------------- : Events
DECLARE_EVENT_TABLE(); DECLARE_EVENT_TABLE();
wxSize getBestSliceSize() const; wxSize getBestSliceSize() const;
wxSize DoGetBestSize() const override; wxSize DoGetBestSize() const override;
+7 -7
View File
@@ -182,14 +182,14 @@ SelectStyleSheetWindow::SelectStyleSheetWindow(Window* parent, const Game& game,
// init controls // init controls
stylesheet_list = new PackageList (this, ID_STYLESHEET_LIST); stylesheet_list = new PackageList (this, ID_STYLESHEET_LIST);
wxStaticText* description = new wxStaticText(this, ID_GAME_LIST, _LABEL_1_("stylesheet not found", failed_name)); 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 = 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 // init sizer
wxSizer* s = new wxBoxSizer(wxVERTICAL); wxSizer* s = new wxBoxSizer(wxVERTICAL);
s->Add(description, 0, wxALL, 4); s->Add(description, 0, wxALL, 4);
wxSizer* s2 = new wxBoxSizer(wxHORIZONTAL); wxSizer* s2 = new wxBoxSizer(wxHORIZONTAL);
s2->Add(stylesheet_text, 0, wxALL & ~wxLEFT, 4); s2->Add(stylesheet_text, 0, wxALL & ~wxLEFT, 4);
s2->AddStretchSpacer(); s2->AddStretchSpacer();
@@ -215,8 +215,8 @@ void SelectStyleSheetWindow::onStyleSheetSelect(wxCommandEvent&) {
} }
void SelectStyleSheetWindow::onStyleSheetActivate(wxCommandEvent&) { void SelectStyleSheetWindow::onStyleSheetActivate(wxCommandEvent&) {
done(); done();
} }
void SelectStyleSheetWindow::onStylesheetFilterUpdate(wxCommandEvent&) { void SelectStyleSheetWindow::onStylesheetFilterUpdate(wxCommandEvent&) {
if (stylesheet_list->hasSelection()) { if (stylesheet_list->hasSelection()) {
StyleSheetP existingStylesheetSelection = stylesheet_list->getSelection<StyleSheet>(false); StyleSheetP existingStylesheetSelection = stylesheet_list->getSelection<StyleSheet>(false);
@@ -257,7 +257,7 @@ void SelectStyleSheetWindow::onIdle(wxIdleEvent& ev) {
BEGIN_EVENT_TABLE(SelectStyleSheetWindow, wxDialog) BEGIN_EVENT_TABLE(SelectStyleSheetWindow, wxDialog)
EVT_GALLERY_SELECT (ID_STYLESHEET_LIST, SelectStyleSheetWindow::onStyleSheetSelect) 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_COMMAND_RANGE(ID_STYLESHEET_FILTER, ID_STYLESHEET_FILTER, wxEVT_COMMAND_TEXT_UPDATED, SelectStyleSheetWindow::onStylesheetFilterUpdate)
EVT_BUTTON (wxID_OK, SelectStyleSheetWindow::OnOK) EVT_BUTTON (wxID_OK, SelectStyleSheetWindow::OnOK)
EVT_UPDATE_UI (wxID_ANY, SelectStyleSheetWindow::onUpdateUI) EVT_UPDATE_UI (wxID_ANY, SelectStyleSheetWindow::onUpdateUI)
+3 -3
View File
@@ -81,15 +81,15 @@ private:
const Game& game; const Game& game;
// gui items // gui items
PackageList* stylesheet_list; PackageList* stylesheet_list;
FilterCtrl* stylesheet_filter; FilterCtrl* stylesheet_filter;
String stylesheet_filter_value; String stylesheet_filter_value;
// --------------------------------------------------- : events // --------------------------------------------------- : events
void onStyleSheetSelect (wxCommandEvent&); void onStyleSheetSelect (wxCommandEvent&);
void onStyleSheetActivate(wxCommandEvent&); void onStyleSheetActivate(wxCommandEvent&);
void onStylesheetFilterUpdate(wxCommandEvent&); void onStylesheetFilterUpdate(wxCommandEvent&);
virtual void OnOK(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")); borders = new wxCheckBox(this, wxID_ANY, _BUTTON_("show lines"));
draw_editing = new wxCheckBox(this, wxID_ANY, _BUTTON_("show editing hints")); draw_editing = new wxCheckBox(this, wxID_ANY, _BUTTON_("show editing hints"));
spellcheck_enabled = new wxCheckBox(this, wxID_ANY, _BUTTON_("spellcheck enabled")); spellcheck_enabled = new wxCheckBox(this, wxID_ANY, _BUTTON_("spellcheck enabled"));
non_normal_export = new wxCheckBox(this, wxID_ANY, _BUTTON_("zoom export")); non_normal_export = new wxCheckBox(this, wxID_ANY, _BUTTON_("zoom export"));
zoom = new wxComboBox(this, ID_ZOOM); zoom = new wxComboBox(this, ID_ZOOM);
export_zoom = new wxComboBox(this, ID_EXPORT_ZOOM); export_zoom = new wxComboBox(this, ID_EXPORT_ZOOM);
//wxButton* columns = new wxButton(this, ID_SELECT_COLUMNS, _BUTTON_("select")); //wxButton* columns = new wxButton(this, ID_SELECT_COLUMNS, _BUTTON_("select"));
// set values // set values
@@ -227,14 +227,14 @@ DisplayPreferencesPage::DisplayPreferencesPage(Window* parent)
non_normal_export->SetValue(!settings.default_stylesheet_settings.card_normal_export()); non_normal_export->SetValue(!settings.default_stylesheet_settings.card_normal_export());
zoom_int = static_cast<int>(settings.default_stylesheet_settings.card_zoom() * 100); zoom_int = static_cast<int>(settings.default_stylesheet_settings.card_zoom() * 100);
zoom->SetValue(String::Format(_("%d%%"),zoom_int)); zoom->SetValue(String::Format(_("%d%%"),zoom_int));
int choices[] = { 50,66,75,100,120,150,175,200 }; int zoom_choices[] = { 50,66,75,80,100,120,125,150,175,200 };
for (unsigned int i = 0 ; i < sizeof(choices)/sizeof(choices[0]) ; ++i) { for (unsigned int i = 0 ; i < sizeof(zoom_choices)/sizeof(zoom_choices[0]) ; ++i) {
zoom->Append(String::Format(_("%d%%"),choices[i])); zoom->Append(String::Format(_("%d%%"), zoom_choices[i]));
} }
export_zoom_int = static_cast<int>(settings.default_stylesheet_settings.export_zoom() * 100); export_zoom_int = static_cast<int>(settings.default_stylesheet_settings.export_zoom() * 100);
export_zoom->SetValue(String::Format(_("%d%%"), export_zoom_int)); 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) { for (unsigned int i = 0; i < sizeof(export_choices) / sizeof(export_choices[0]); ++i) {
export_zoom->Append(String::Format(_("%d%%"), export_choices[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_int = static_cast<int>(settings.internal_scale * 100);
internal_scale->SetValue(String::Format(_("%d%%"), internal_scale_int)); 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) { for (unsigned int i = 0; i < sizeof(choices) / sizeof(choices[0]); ++i) {
internal_scale->Append(String::Format(_("%d%%"), choices[i])); internal_scale->Append(String::Format(_("%d%%"), choices[i]));
} }
+228 -228
View File
@@ -14,150 +14,150 @@
#include <data/card.hpp> #include <data/card.hpp>
#include <data/stylesheet.hpp> #include <data/stylesheet.hpp>
#include <render/card/viewer.hpp> #include <render/card/viewer.hpp>
#include <wx/print.h> #include <wx/print.h>
#include <wx/valnum.h> #include <wx/valnum.h>
#include <unordered_set> #include <unordered_set>
DECLARE_POINTER_TYPE(PageLayout); DECLARE_POINTER_TYPE(PageLayout);
// ----------------------------------------------------------------------------- : Layout // ----------------------------------------------------------------------------- : Layout
void PrintJob::init(const RealSize& page_size) { void PrintJob::init(const RealSize& page_size) {
this->page_size = page_size; this->page_size = page_size;
if (cards.empty()) return; if (cards.empty()) return;
measure_cards(); measure_cards();
layout_cards(); layout_cards();
align_cards(); align_cards();
center_cards(); center_cards();
} }
void PrintJob::measure_cards() { void PrintJob::measure_cards() {
FOR_EACH(card, cards) { FOR_EACH(card, cards) {
const StyleSheet& stylesheet = set->stylesheetFor(card); const StyleSheet& stylesheet = set->stylesheetFor(card);
RealSize size_px(stylesheet.card_width, stylesheet.card_height); 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); RealSize size_mm(stylesheet.card_width * 25.4 / stylesheet.card_dpi, stylesheet.card_height * 25.4 / stylesheet.card_dpi);
Radians rotation = 0.0; 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 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) { if (rotated) {
swap(size_mm.width, size_mm.height); swap(size_mm.width, size_mm.height);
swap(size_px.width, size_px.height); swap(size_px.width, size_px.height);
rotation = rad90; 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.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; 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); CardLayout layout(card, size_mm, size_px, rotation);
card_layouts.push_back(layout); card_layouts.push_back(layout);
} }
std::sort(card_layouts.begin(), card_layouts.end()); std::sort(card_layouts.begin(), card_layouts.end());
} }
void PrintJob::layout_cards() { void PrintJob::layout_cards() {
page_layouts.push_back(vector<CardLayout>()); page_layouts.push_back(vector<CardLayout>());
double row_top = 0.0, row_height = 0.0, row_width = 0.0; double row_top = 0.0, row_height = 0.0, row_width = 0.0;
unordered_set<int> already_laidout_cards; unordered_set<int> already_laidout_cards;
while (true) { while (true) {
// try to find a card that will fit on the current row // try to find a card that will fit on the current row
for (int i = 0; i < card_layouts.size(); ++i) { for (int i = 0; i < card_layouts.size(); ++i) {
if (already_laidout_cards.find(i) != already_laidout_cards.end()) continue; 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.width + row_width >= page_size.width) continue;
if (card_layouts[i].size_mm.height + row_top >= page_size.height) continue; if (card_layouts[i].size_mm.height + row_top >= page_size.height) continue;
// the card fits // the card fits
card_layouts[i].pos.width = row_width; card_layouts[i].pos.width = row_width;
card_layouts[i].pos.height = row_top; card_layouts[i].pos.height = row_top;
page_layouts[page_layouts.size()-1].push_back(card_layouts[i]); page_layouts[page_layouts.size()-1].push_back(card_layouts[i]);
already_laidout_cards.insert(i); already_laidout_cards.insert(i);
if (already_laidout_cards.size() == card_layouts.size()) return; if (already_laidout_cards.size() == card_layouts.size()) return;
// move to next spot on the row // move to next spot on the row
row_width += card_layouts[i].size_mm.width + settings.print_spacing; 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); row_height = max(row_height, card_layouts[i].size_mm.height + settings.print_spacing);
goto continue_outer; goto continue_outer;
} }
// no card fits // no card fits
if (row_top == 0.0 && row_height == 0.0 && row_width == 0.0) { 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 // none of the remaining cards can fit on an empty page, return
page_layouts.pop_back(); page_layouts.pop_back();
queue_message(MESSAGE_WARNING, _ERROR_("cards bigger than page")); queue_message(MESSAGE_WARNING, _ERROR_("cards bigger than page"));
return; return;
} }
if (row_height == 0.0 && row_width == 0.0) { if (row_height == 0.0 && row_width == 0.0) {
// none of the remaining cards can fit on an empty row, create a new page // none of the remaining cards can fit on an empty row, create a new page
page_layouts.push_back(vector<CardLayout>()); page_layouts.push_back(vector<CardLayout>());
row_top = 0.0; row_top = 0.0;
continue; continue;
} }
// none of the remaining cards can fit on this row, create a new row // none of the remaining cards can fit on this row, create a new row
row_top += row_height; row_top += row_height;
row_width = row_height = 0.0; row_width = row_height = 0.0;
continue_outer:; 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() { void PrintJob::align_cards() {
for (int p = 0; p < page_layouts.size(); ++p) { // for each page
vector<CardLayout>& page_layout = page_layouts[p]; for (int p = 0; p < page_layouts.size(); ++p) {
RealSize page_margin(0.0, 0.0); vector<CardLayout>& page_layout = page_layouts[p];
for (int i = 0; i < page_layout.size(); ++i) { // for each card on the page
double width = page_layout[i].pos.width + page_layout[i].size_mm.width; for (int max = 0, j = 0; j < page_layout.size(); ++max, ++j) {
double height = page_layout[i].pos.height + page_layout[i].size_mm.height; if (max > 100) {
if (page_margin.width < width) page_margin.width = width; queue_message(MESSAGE_WARNING, _("DEBUG: large amount of iterations when aligning cards for print"));
if (page_margin.height < height) page_margin.height = height; break;
} }
page_margin.width = (page_size.width - page_margin.width) / 2; double x = page_layout[j].pos.width;
page_margin.height = (page_size.height - page_margin.height) / 2; double y = page_layout[j].pos.height;
page_margins.push_back(page_margin); // if another card is almost aligned
for (int i = 0; i < page_layout.size(); ++i) { for (int i = 0; i < page_layout.size(); ++i) {
page_layout[i].pos.width += page_margin.width; if (i == j) continue;
page_layout[i].pos.height += page_margin.height; 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 // ----------------------------------------------------------------------------- : Printout
@@ -182,11 +182,11 @@ private:
int pageCount() { int pageCount() {
return job->page_layouts.size(); 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); void drawCutterLines(DC& dc, PrintJobP& job, int page);
/// Draw cards according to their CardLayout info /// 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); 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); dc.GetSize(&page_width_px, &page_height_px);
// scale factor (pixels per mm) // scale factor (pixels per mm)
printer_px_per_mm = RealSize((double)page_width_px / page_width_mm, (double)page_height_px / page_height_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 // print the cards that belong on this page
drawCards(dc, job, page); drawCards(dc, job, page);
if (settings.print_cutter_lines != CUTTER_NONE) drawCutterLines(dc, job, page); if (settings.print_cutter_lines != CUTTER_NONE) drawCutterLines(dc, job, page);
return true; 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 // draw card to its own buffer
wxBitmap buffer(card_layout.size_px.width, card_layout.size_px.height, 32); wxBitmap buffer(card_layout.size_px.width, card_layout.size_px.height, 32);
wxMemoryDC bufferDC; 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.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)); 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) { void CardsPrintout::drawCutterLines(DC& dc, PrintJobP& job, int page) {
const vector<PrintJob::CardLayout>& page_layout = job->page_layouts[page - 1]; const vector<PrintJob::CardLayout>& page_layout = job->page_layouts[page - 1];
const RealSize& page_margin = job->page_margins[page - 1]; const RealSize& page_margin = job->page_margins[page - 1];
int page_width, page_height; int page_width, page_height;
GetPageSizeMM(&page_width, &page_height); GetPageSizeMM(&page_width, &page_height);
double vertical_line_size = min(10.0, page_margin.height - 3.0); double vertical_line_size = min(10.0, page_margin.height - 3.0);
if (vertical_line_size > 0.0) { if (vertical_line_size > 0.0) {
for (int i = 0; i < page_layout.size(); ++i) { for (int i = 0; i < page_layout.size(); ++i) {
double left_line = page_layout[i].pos.width; double left_line = page_layout[i].pos.width;
double right_line = left_line + page_layout[i].size_mm.width; double right_line = left_line + page_layout[i].size_mm.width;
bool draw_left_line = true; bool draw_left_line = true;
bool draw_right_line = true; bool draw_right_line = true;
if (settings.print_cutter_lines == CUTTER_NO_INTERSECTION) { if (settings.print_cutter_lines == CUTTER_NO_INTERSECTION) {
// check if another card is in the way of this cutter line // check if another card is in the way of this cutter line
for (int j = 0; j < page_layout.size(); ++j) { for (int j = 0; j < page_layout.size(); ++j) {
if (i == j) continue; if (i == j) continue;
double other_left_line = page_layout[j].pos.width; double other_left_line = page_layout[j].pos.width;
double other_right_line = other_left_line + page_layout[j].size_mm.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) { if (draw_left_line && left_line - other_left_line > job->threshold_bottom && other_right_line - left_line > job->threshold_bottom) {
draw_left_line = false; draw_left_line = false;
if (!draw_right_line) break; 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) { if (draw_right_line && right_line - other_left_line > job->threshold_bottom && other_right_line - right_line > job->threshold_bottom) {
draw_right_line = false; draw_right_line = false;
if (!draw_left_line) break; if (!draw_left_line) break;
} }
} }
} }
const RealSize& px_per_mm = page_layout[i].px_per_mm; 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); dc.SetUserScale(printer_px_per_mm.width / px_per_mm.width, printer_px_per_mm.height / px_per_mm.height);
if (draw_left_line) { 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, 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))); 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) { 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, 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))); 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 { } else {
queue_message(MESSAGE_WARNING, _ERROR_("v margin too small for cutter")); queue_message(MESSAGE_WARNING, _ERROR_("v margin too small for cutter"));
} }
double horizontal_line_size = min(10.0, page_margin.width - 3.0); double horizontal_line_size = min(10.0, page_margin.width - 3.0);
if (horizontal_line_size > 0.0) { if (horizontal_line_size > 0.0) {
for (int i = 0; i < page_layout.size(); ++i) { for (int i = 0; i < page_layout.size(); ++i) {
double top_line = page_layout[i].pos.height; double top_line = page_layout[i].pos.height;
double bottom_line = top_line + page_layout[i].size_mm.height; double bottom_line = top_line + page_layout[i].size_mm.height;
bool draw_top_line = true; bool draw_top_line = true;
bool draw_bottom_line = true; bool draw_bottom_line = true;
if (settings.print_cutter_lines == CUTTER_NO_INTERSECTION) { if (settings.print_cutter_lines == CUTTER_NO_INTERSECTION) {
// check if another card is in the way of this cutter line // check if another card is in the way of this cutter line
for (int j = 0; j < page_layout.size(); ++j) { for (int j = 0; j < page_layout.size(); ++j) {
if (i == j) continue; if (i == j) continue;
double other_top_line = page_layout[j].pos.height; double other_top_line = page_layout[j].pos.height;
double other_bottom_line = other_top_line + page_layout[j].size_mm.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) { if (draw_top_line && top_line - other_top_line > job->threshold_bottom && other_bottom_line - top_line > job->threshold_bottom) {
draw_top_line = false; draw_top_line = false;
if (!draw_bottom_line) break; 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) { if (draw_bottom_line && bottom_line - other_top_line > job->threshold_bottom && other_bottom_line - bottom_line > job->threshold_bottom) {
draw_bottom_line = false; draw_bottom_line = false;
if (!draw_top_line) break; if (!draw_top_line) break;
} }
} }
} }
const RealSize& px_per_mm = page_layout[i].px_per_mm; 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); dc.SetUserScale(printer_px_per_mm.width / px_per_mm.width, printer_px_per_mm.height / px_per_mm.height);
if (draw_top_line) { 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(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)); 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) { 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(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)); 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 { } else {
queue_message(MESSAGE_WARNING, _ERROR_("h margin too small for cutter")); queue_message(MESSAGE_WARNING, _ERROR_("h margin too small for cutter"));
} }
dc.SetUserScale(printer_px_per_mm.width, printer_px_per_mm.height); dc.SetUserScale(printer_px_per_mm.width, printer_px_per_mm.height);
} }
// ----------------------------------------------------------------------------- : PrintWindow // ----------------------------------------------------------------------------- : PrintWindow
PrintJobP make_print_job(Window* parent, const SetP& set, const ExportCardSelectionChoices& choices) { PrintJobP make_print_job(Window* parent, const SetP& set, const ExportCardSelectionChoices& choices) {
// Let the user choose cards and spacing // Let the user choose cards and spacing
// controls // 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); wxFloatingPointValidator<float> validator(2, NULL, wxNUM_VAL_ZERO_AS_BLANK);
validator.SetRange(0, 100); 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)); space->SetValue(wxString::Format(wxT("%lf"), settings.print_spacing));
wxChoice* cutter = new wxChoice(&wnd, wxID_ANY); wxChoice* cutter = new wxChoice(&wnd, wxID_ANY);
cutter->Clear(); cutter->Clear();
@@ -371,10 +371,10 @@ PrintJobP make_print_job(Window* parent, const SetP& set, const ExportCardSelect
if (wnd.ShowModal() != wxID_OK) { if (wnd.ShowModal() != wxID_OK) {
return PrintJobP(); // cancel return PrintJobP(); // cancel
} else { } else {
// save settings, make print job // save settings, make print job
String spacing = space->GetValue(); String spacing = space->GetValue();
if (spacing.empty()) spacing = _("0"); if (spacing.empty()) spacing = _("0");
spacing.ToDouble(&settings.print_spacing); spacing.ToDouble(&settings.print_spacing);
settings.print_cutter_lines = (CutterLinesType)cutter->GetSelection(); settings.print_cutter_lines = (CutterLinesType)cutter->GetSelection();
PrintJobP job = make_intrusive<PrintJob>(set, wnd.getSelection()); PrintJobP job = make_intrusive<PrintJob>(set, wnd.getSelection());
return job; return job;
+252 -70
View File
@@ -25,6 +25,7 @@
#include <util/tagged_string.hpp> #include <util/tagged_string.hpp>
#include <util/window_id.hpp> #include <util/window_id.hpp>
#include <wx/splitter.h> #include <wx/splitter.h>
#include <wx/gbsizer.h>
// ----------------------------------------------------------------------------- : CardsPanel // ----------------------------------------------------------------------------- : CardsPanel
@@ -32,15 +33,36 @@ CardsPanel::CardsPanel(Window* parent, int id)
: SetWindowPanel(parent, id) : SetWindowPanel(parent, id)
{ {
// init controls // init controls
editor = new CardEditor(this, ID_EDITOR); editor = new CardEditor(this, ID_EDITOR);
splitter = new wxSplitterWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); link_editor = new CardEditor(this, ID_CARD_LINK_EDITOR);
card_list = new FilteredImageCardList(splitter, ID_CARD_LIST); focused_editor = editor;
nodes_panel = new wxPanel(splitter, wxID_ANY); link_viewer_1 = new CardViewer(this, ID_CARD_LINK_VIEWER);
notes = new TextCtrl(nodes_panel, ID_NOTES, true); link_viewer_2 = new CardViewer(this, ID_CARD_LINK_VIEWER);
collapse_notes = new HoverButton(nodes_panel, ID_COLLAPSE_NOTES, _("btn_collapse"), Color(), false); 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); collapse_notes->SetExtraStyle(wxWS_EX_PROCESS_UI_UPDATES);
filter = nullptr; filter = nullptr;
editor->next_in_tab_order = card_list; 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 // init sizer for notes panel
wxSizer* sn = new wxBoxSizer(wxVERTICAL); wxSizer* sn = new wxBoxSizer(wxVERTICAL);
wxSizer* sc = new wxBoxSizer(wxHORIZONTAL); wxSizer* sc = new wxBoxSizer(wxHORIZONTAL);
@@ -54,10 +76,46 @@ CardsPanel::CardsPanel(Window* parent, int id)
splitter->SetSashGravity(1.0); splitter->SetSashGravity(1.0);
splitter->SplitHorizontally(card_list, nodes_panel, -40); splitter->SplitHorizontally(card_list, nodes_panel, -40);
notes_below_editor = false; notes_below_editor = false;
// init sizer // init sizer for editors and viewers
wxSizer* s = new wxBoxSizer(wxHORIZONTAL); wxSizer* s = new wxBoxSizer(wxHORIZONTAL); // Global Sizer
s_left = new wxBoxSizer(wxVERTICAL); s_left = new wxBoxSizer(wxVERTICAL); // Sizer for the selected card, and it's linked cards
s_left->Add(editor); 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(s_left, 0, wxEXPAND | wxRIGHT, 2);
s->Add(splitter, 1, wxEXPAND); s->Add(splitter, 1, wxEXPAND);
s->SetSizeHints(this); 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(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_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_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(); menuCard->AppendSeparator();
auto menuRotate = new wxMenu(); auto menuRotate = new wxMenu();
add_menu_item_tr(menuRotate, ID_CARD_ROTATE_0, "card_rotate_0", "rotate_0", wxITEM_CHECK); 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(); menuFormat = new wxMenu();
add_menu_item_tr(menuFormat, ID_FORMAT_BOLD, "bold", "bold", wxITEM_CHECK); 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_UNDERLINE, "underline", "underline", wxITEM_CHECK);
add_menu_item_tr(menuFormat, ID_FORMAT_SYMBOL, "symbol", "symbols", 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); 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); menuFormat->Append(insertSymbolMenu);
toolAddCard = nullptr; toolAddCard = nullptr;
} }
void CardsPanel::updateCardCounts() { void CardsPanel::updateCardCounts() {
if (counts && card_list && set) { if (counts && card_list && set) {
int selected = card_list->GetSelectedItemCount(); int selected = card_list->GetSelectedItemCount();
int filtered = card_list->GetItemCount(); int filtered = card_list->GetItemCount();
int total = set->cards.size(); int total = set->cards.size();
if ( if (
selected_cards_count == selected selected_cards_count == selected
&& filtered_cards_count == filtered && filtered_cards_count == filtered
&& total_cards_count == total && total_cards_count == total
&& !counts->GetLabel().empty() && !counts->GetLabel().empty()
) return; ) return;
selected_cards_count = selected; selected_cards_count = selected;
filtered_cards_count = filtered; filtered_cards_count = filtered;
total_cards_count = total; total_cards_count = total;
if (filtered == total) { if (filtered == total) {
counts->SetLabel(_TOOL_2_("card counts 2", counts->SetLabel(_TOOL_2_("card counts 2",
wxString::Format(wxT("%i"), selected), wxString::Format(wxT("%i"), selected),
wxString::Format(wxT("%i"), total))); wxString::Format(wxT("%i"), total)));
} }
else { else {
counts->SetLabel(_TOOL_3_("card counts 3", counts->SetLabel(_TOOL_3_("card counts 3",
wxString::Format(wxT("%i"), selected), wxString::Format(wxT("%i"), selected),
wxString::Format(wxT("%i"), filtered), wxString::Format(wxT("%i"), filtered),
wxString::Format(wxT("%i"), total))); wxString::Format(wxT("%i"), total)));
} }
} }
} }
void CardsPanel::updateNotesPosition() { void CardsPanel::updateNotesPosition() {
wxSize editor_size = editor->GetBestSize(); wxSize editor_size = editor->GetBestSize();
@@ -177,6 +237,11 @@ CardsPanel::~CardsPanel() {
void CardsPanel::onChangeSet() { void CardsPanel::onChangeSet() {
editor->setSet(set); 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); notes->setSet(set);
card_list->setSet(set); card_list->setSet(set);
@@ -187,9 +252,9 @@ void CardsPanel::onChangeSet() {
menuCard->Remove(ID_CARD_ADD_MULT); menuCard->Remove(ID_CARD_ADD_MULT);
((wxMenu*)menuCard)->Insert(4,insertManyCardsMenu); // HACK: the position is hardcoded ((wxMenu*)menuCard)->Insert(4,insertManyCardsMenu); // HACK: the position is hardcoded
// also for the toolbar dropdown menu // also for the toolbar dropdown menu
if (toolAddCard) { if (toolAddCard) {
// Originally this was using the menu directly, but there are compatibility issues apparently. // 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. // 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)); 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) { void CardsPanel::initUI(wxToolBar* tb, wxMenuBar* mb) {
// Toolbar // Toolbar
add_tool_tr(tb, ID_FORMAT_BOLD, "bold", "bold", false, wxITEM_CHECK); 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_UNDERLINE, "underline", "underline", false, wxITEM_CHECK);
add_tool_tr(tb, ID_FORMAT_SYMBOL, "symbol", "symbols", 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); 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); toolAddCard = add_tool_tr(tb, ID_CARD_ADD, "card_add", "add_card", false, wxITEM_DROPDOWN);
tb->SetDropdownMenu(ID_CARD_ADD, makeAddCardsSubmenu(true)); tb->SetDropdownMenu(ID_CARD_ADD, makeAddCardsSubmenu(true));
add_tool_tr(tb, ID_CARD_REMOVE, "card_del", "remove_card"); add_tool_tr(tb, ID_CARD_REMOVE, "card_del", "remove_card");
add_tool_tr(tb, ID_CARD_LINK, "card_link", "link_card");
tb->AddSeparator(); tb->AddSeparator();
add_tool_tr(tb, ID_CARD_ROTATE, "card_rotate", "rotate_card", false, wxITEM_DROPDOWN); add_tool_tr(tb, ID_CARD_ROTATE, "card_rotate", "rotate_card", false, wxITEM_DROPDOWN);
auto menuRotate = new wxMenu(); auto menuRotate = new wxMenu();
@@ -239,10 +305,10 @@ void CardsPanel::initUI(wxToolBar* tb, wxMenuBar* mb) {
assert(!filter); assert(!filter);
filter = new FilterCtrl(tb, ID_CARD_FILTER, _TOOL_("search cards"), _HELP_("search cards control")); filter = new FilterCtrl(tb, ID_CARD_FILTER, _TOOL_("search cards"), _HELP_("search cards control"));
filter->setFilter(filter_value); filter->setFilter(filter_value);
tb->AddControl(filter); tb->AddControl(filter);
counts = new wxStaticText(tb, ID_CARD_COUNTER, _("")); counts = new wxStaticText(tb, ID_CARD_COUNTER, _(""));
updateCardCounts(); updateCardCounts();
tb->AddControl(counts); tb->AddControl(counts);
tb->Realize(); tb->Realize();
// Menus // Menus
mb->Insert(2, menuCard, _MENU_("cards")); mb->Insert(2, menuCard, _MENU_("cards"));
@@ -252,12 +318,13 @@ void CardsPanel::initUI(wxToolBar* tb, wxMenuBar* mb) {
void CardsPanel::destroyUI(wxToolBar* tb, wxMenuBar* mb) { void CardsPanel::destroyUI(wxToolBar* tb, wxMenuBar* mb) {
// Toolbar // Toolbar
tb->DeleteTool(ID_FORMAT_BOLD); tb->DeleteTool(ID_FORMAT_BOLD);
tb->DeleteTool(ID_FORMAT_ITALIC); tb->DeleteTool(ID_FORMAT_ITALIC);
tb->DeleteTool(ID_FORMAT_UNDERLINE); tb->DeleteTool(ID_FORMAT_UNDERLINE);
tb->DeleteTool(ID_FORMAT_SYMBOL); tb->DeleteTool(ID_FORMAT_SYMBOL);
tb->DeleteTool(ID_FORMAT_REMINDER); tb->DeleteTool(ID_FORMAT_REMINDER);
tb->DeleteTool(ID_CARD_ADD); tb->DeleteTool(ID_CARD_ADD);
tb->DeleteTool(ID_CARD_REMOVE); tb->DeleteTool(ID_CARD_REMOVE);
tb->DeleteTool(ID_CARD_LINK);
tb->DeleteTool(ID_CARD_ROTATE); tb->DeleteTool(ID_CARD_ROTATE);
tb->DeleteTool(ID_CARD_COUNTER); tb->DeleteTool(ID_CARD_COUNTER);
// remember the value in the filter control, because the card list remains filtered // 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) { void CardsPanel::onUpdateUI(wxUpdateUIEvent& ev) {
switch (ev.GetId()) { switch (ev.GetId()) {
case ID_CARD_PREV: ev.Enable(card_list->canSelectPrevious()); break; case ID_CARD_PREV: ev.Enable(card_list->canSelectPrevious()); break;
case ID_CARD_NEXT: ev.Enable(card_list->canSelectNext()); 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: { 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())); StyleSheetSettings& ss = settings.stylesheetSettingsFor(set->stylesheetFor(card_list->getCard()));
int a = ev.GetId() == ID_CARD_ROTATE_0 ? 0 int a = ev.GetId() == ID_CARD_ROTATE_0 ? 0
@@ -292,11 +359,16 @@ void CardsPanel::onUpdateUI(wxUpdateUIEvent& ev) {
ev.Enable(insertManyCardsMenu->GetSubMenu() != nullptr); ev.Enable(insertManyCardsMenu->GetSubMenu() != nullptr);
break; 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: { 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) { if (focused_control(this) == ID_EDITOR) {
ev.Enable(editor->canFormat(ev.GetId())); ev.Enable(editor->canFormat(ev.GetId()));
ev.Check (editor->hasFormat(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 { } else {
ev.Enable(false); ev.Enable(false);
ev.Check(false); ev.Check(false);
@@ -313,18 +385,18 @@ void CardsPanel::onUpdateUI(wxUpdateUIEvent& ev) {
case ID_INSERT_SYMBOL: ev.Enable(false); break; case ID_INSERT_SYMBOL: ev.Enable(false); break;
#else #else
case ID_INSERT_SYMBOL: { case ID_INSERT_SYMBOL: {
wxMenu* menu = editor->getMenu(ID_INSERT_SYMBOL); wxMenu* menu = focused_editor->getMenu(ID_INSERT_SYMBOL);
ev.Enable(menu); ev.Enable(menu);
break; break;
} }
#endif #endif
} }
updateCardCounts(); updateCardCounts();
} }
void CardsPanel::onMenuOpen(wxMenuEvent& ev) { void CardsPanel::onMenuOpen(wxMenuEvent& ev) {
if (ev.GetMenu() != menuFormat) return; 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)) { if (insertSymbolMenu->GetSubMenu() != menu || (menu && menu->GetParent() != menuFormat)) {
// re-add the menu // re-add the menu
menuFormat->Remove(ID_INSERT_SYMBOL); menuFormat->Remove(ID_INSERT_SYMBOL);
@@ -358,6 +430,27 @@ void CardsPanel::onCommand(int id) {
case ID_CARD_REMOVE: case ID_CARD_REMOVE:
card_list->doDelete(); card_list->doDelete();
break; 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:
case ID_CARD_ROTATE_0: case ID_CARD_ROTATE_90: case ID_CARD_ROTATE_180: case ID_CARD_ROTATE_270: { 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())); StyleSheetSettings& ss = settings.stylesheetSettingsFor(set->stylesheetFor(card_list->getCard()));
@@ -378,6 +471,9 @@ void CardsPanel::onCommand(int id) {
if (focused_control(this) == ID_EDITOR) { if (focused_control(this) == ID_EDITOR) {
editor->doFormat(id); editor->doFormat(id);
} }
else if (focused_control(this) == ID_CARD_LINK_EDITOR) {
link_editor->doFormat(id);
}
break; break;
} }
case ID_COLLAPSE_NOTES: { case ID_COLLAPSE_NOTES: {
@@ -399,7 +495,7 @@ void CardsPanel::onCommand(int id) {
default: { default: {
if (id >= ID_INSERT_SYMBOL_MENU_MIN && id <= ID_INSERT_SYMBOL_MENU_MAX) { if (id >= ID_INSERT_SYMBOL_MENU_MIN && id <= ID_INSERT_SYMBOL_MENU_MAX) {
// pass on to editor // 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) { } else if (id >= ID_ADD_CARDS_MENU_MIN && id <= ID_ADD_CARDS_MENU_MAX) {
// add multiple cards // add multiple cards
AddCardsScriptP script = set->game->add_cards_scripts.at(id - ID_ADD_CARDS_MENU_MIN); 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 // determine what control to use for clipboard actions
#define CUT_COPY_PASTE(op,return) \ #define CUT_COPY_PASTE(op,return) \
int id = focused_control(this); \ int id = focused_control(this); \
if (id == ID_EDITOR) { return editor->op(); } \ if (id == ID_EDITOR) { return editor->op(); } \
else if (id == ID_CARD_LIST) { return card_list->op(); } \ else if (id == ID_CARD_LINK_EDITOR) { return link_editor->op(); } \
else if (id == ID_NOTES) { return notes->op(); } \ else if (id == ID_CARD_LIST) { return card_list->op(); } \
else { return false; } else if (id == ID_NOTES) { return notes->op(); } \
else { return false; }
bool CardsPanel::canCut() const { CUT_COPY_PASTE(canCut, return) } bool CardsPanel::canCut() const { CUT_COPY_PASTE(canCut, return) }
bool CardsPanel::canCopy() const { CUT_COPY_PASTE(canCopy, 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 { bool CardsPanel::canPaste() const {
if (card_list->canPaste()) return true; if (card_list->canPaste()) return true;
int id = focused_control(this); int id = focused_control(this);
if (id == ID_EDITOR) return editor->canPaste(); if (id == ID_EDITOR) return editor->canPaste();
else if (id == ID_NOTES) return notes->canPaste(); else if (id == ID_CARD_LINK_EDITOR) return link_editor->canPaste();
else return false; else if (id == ID_NOTES) return notes->canPaste();
else return false;
} }
void CardsPanel::doPaste() { void CardsPanel::doPaste() {
if (card_list->canPaste()) { if (card_list->canPaste()) {
card_list->doPaste(); card_list->doPaste();
} else { } else {
int id = focused_control(this); int id = focused_control(this);
if (id == ID_EDITOR) editor->doPaste(); if (id == ID_EDITOR) editor->doPaste();
else if (id == ID_NOTES) notes->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) {} SearchFindInfo(CardsPanel& panel, wxFindReplaceData& what) : FindInfo(what), panel(panel) {}
bool handle(const CardP& card, const TextValueP& value, size_t pos, bool was_selection) override { bool handle(const CardP& card, const TextValueP& value, size_t pos, bool was_selection) override {
// Select the card // Select the card
panel.card_list->setCard(card); panel.setCard(card, true);
return true; return true;
} }
private: private:
@@ -477,7 +576,7 @@ public:
ReplaceFindInfo(CardsPanel& panel, wxFindReplaceData& what) : FindInfo(what), panel(panel) {} ReplaceFindInfo(CardsPanel& panel, wxFindReplaceData& what) : FindInfo(what), panel(panel) {}
bool handle(const CardP& card, const TextValueP& value, size_t pos, bool was_selection) override { bool handle(const CardP& card, const TextValueP& value, size_t pos, bool was_selection) override {
// Select the card // Select the card
panel.card_list->setCard(card); panel.setCard(card, true);
// Replace // Replace
if (was_selection) { if (was_selection) {
panel.editor->insert(escape(what.GetReplaceString()), _("Replace")); panel.editor->insert(escape(what.GetReplaceString()), _("Replace"));
@@ -512,10 +611,12 @@ bool CardsPanel::search(FindInfo& find, bool from_start) {
if (include) { if (include) {
editor->setCard(card); editor->setCard(card);
if (editor->search(find, from_start || card != current)) { 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); editor->setCard(current);
return false; return false;
} }
@@ -527,9 +628,76 @@ CardP CardsPanel::selectedCard() const {
} }
void CardsPanel::selectCard(const CardP& card) { void CardsPanel::selectCard(const CardP& card) {
if (!set) return; // we want onChangeSet first if (!set) return; // we want onChangeSet first
card_list->setCard(card); card_list->setCard(card);
editor->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); notes->setValue(card ? &card->notes : nullptr);
Layout(); Layout();
updateNotesPosition(); updateNotesPosition();
} }
@@ -539,6 +707,20 @@ void CardsPanel::selectFirstCard() {
card_list->selectFirst(); 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) { void CardsPanel::getCardLists(vector<CardListBase*>& out) {
out.push_back(card_list); 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 FilteredImageCardList;
class DataEditor; class DataEditor;
class TextCtrl; class TextCtrl;
class CardViewer;
class wxSizer;
class wxButton;
class HoverButton; class HoverButton;
class FindInfo; class FindInfo;
class FilterCtrl; class FilterCtrl;
@@ -74,17 +77,25 @@ public:
CardP selectedCard() const override; CardP selectedCard() const override;
void selectCard(const CardP& card) override; void selectCard(const CardP& card) override;
void selectFirstCard() override; void selectFirstCard() override;
void setCard(const CardP& card, bool event = false);
void refreshCard(const CardP& card);
void getCardLists(vector<CardListBase*>& out) override; void getCardLists(vector<CardListBase*>& out) override;
void setFocusedEditor(DataEditor* editor);
private: private:
// --------------------------------------------------- : Controls // --------------------------------------------------- : Controls
wxSizer* s_left; wxSizer* s_left;
wxSplitterWindow* splitter; wxSplitterWindow* splitter;
DataEditor* editor; DataEditor* editor, *link_editor, *focused_editor;
FilteredImageCardList* card_list; FilteredImageCardList* card_list;
wxPanel* nodes_panel; wxPanel* nodes_panel;
TextCtrl* notes; 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; HoverButton* collapse_notes;
FilterCtrl* filter; FilterCtrl* filter;
String filter_value; // value of filter, need separate variable because the control is destroyed 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> { class ConsoleMessage : public IntrusivePtrBase<ConsoleMessage> {
public: public:
MessageType type; MessageType type;
String text; // string message String text; // string message
wxDateTime timestamp; wxDateTime timestamp;
Bitmap bitmap; // image message instead of string Bitmap bitmap; // image message instead of string
ScriptValueP value; // other valued message (images? cards?) 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) { void add_message(MessageType type, String const& text, bool joined_to_previous = false) {
add_message(make_intrusive<ConsoleMessage>(type,text,joined_to_previous)); add_message(make_intrusive<ConsoleMessage>(type,text,joined_to_previous));
} }
void clear_console() { void clear_console() {
messages.clear(); messages.clear();
layout_all(); layout_all();
selection = messages.size(); selection = messages.size();
update_scrollbar(); update_scrollbar();
Refresh(false); Refresh(false);
} }
@@ -245,11 +245,11 @@ private:
wxAutoBufferedPaintDC dc(this); wxAutoBufferedPaintDC dc(this);
PrepareDC(dc); PrepareDC(dc);
draw(dc); draw(dc);
} }
void draw(wxDC& dc) const { void draw(wxDC& dc) const {
clearDC(dc, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); clearDC(dc, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
dc.SetFont(*wxNORMAL_FONT); dc.SetFont(*wxNORMAL_FONT);
FOR_EACH_CONST(msg, messages) { FOR_EACH_CONST(msg, messages) {
draw(dc, *msg); draw(dc, *msg);
@@ -283,23 +283,23 @@ private:
// draw background // draw background
dc.SetPen(*wxTRANSPARENT_PEN); dc.SetPen(*wxTRANSPARENT_PEN);
dc.SetBrush(lerp(bg,color, 0.05)); dc.SetBrush(lerp(bg,color, 0.05));
dc.DrawRectangle(left,top,width,msg.height); dc.DrawRectangle(left,top,width,msg.height);
// draw foreground // draw foreground
dc.SetTextForeground(fg); dc.SetTextForeground(fg);
// draw timestamp // draw timestamp
dc.DrawText(msg.timestamp.FormatISOTime(), left + TIMESTAMP_PADDING, top + TEXT_PADDING_TOP); dc.DrawText(msg.timestamp.FormatISOTime(), left + TIMESTAMP_PADDING, top + TEXT_PADDING_TOP);
int timestamp_resolved_width; int timestamp_resolved_width;
dc.GetTextExtent(_("55:55:55"), &timestamp_resolved_width, nullptr); dc.GetTextExtent(_("55:55:55"), &timestamp_resolved_width, nullptr);
left += timestamp_resolved_width; left += timestamp_resolved_width;
left += TIMESTAMP_PADDING * 2; left += TIMESTAMP_PADDING * 2;
// draw line right of timestamp // draw line right of timestamp
dc.SetPen(lerp(bg, fg, 0.3)); 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 // draw icon
if (icons[msg.type].Ok()) { if (icons[msg.type].Ok()) {
@@ -331,7 +331,7 @@ private:
if (msg.bitmap.Ok()) { if (msg.bitmap.Ok()) {
dc.DrawBitmap(msg.bitmap, text_left, text_top); dc.DrawBitmap(msg.bitmap, text_left, text_top);
text_top += msg.bitmap.GetHeight(); text_top += msg.bitmap.GetHeight();
} }
// draw line below item // draw line below item
dc.SetPen(lerp(bg,fg, 0.3)); dc.SetPen(lerp(bg,fg, 0.3));
@@ -356,9 +356,9 @@ private:
} }
if (begin != msg.text.end()) { if (begin != msg.text.end()) {
text_height += dc.GetCharHeight() + TEXT_LINE_SPACING; 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) { if (text_height == 0) {
text_height = dc.GetCharHeight() + TEXT_LINE_SPACING; text_height = dc.GetCharHeight() + TEXT_LINE_SPACING;
} }
@@ -371,9 +371,9 @@ private:
// --------------------------------------------------- : Layout // --------------------------------------------------- : Layout
static constexpr int LIST_SPACING = 1; static constexpr int LIST_SPACING = 1;
static constexpr int TIMESTAMP_PADDING = 3; 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 ICON_PADDING_LEFT = TIMESTAMP_PADDING + 3;
static constexpr int TEXT_PADDING_LEFT = ICON_PADDING_LEFT + 16 + 4; static constexpr int TEXT_PADDING_LEFT = ICON_PADDING_LEFT + 16 + 4;
static constexpr int TEXT_PADDING_RIGHT = 4; static constexpr int TEXT_PADDING_RIGHT = 4;
@@ -488,7 +488,7 @@ END_EVENT_TABLE()
ConsolePanel::ConsolePanel(Window* parent, int id) ConsolePanel::ConsolePanel(Window* parent, int id)
: SetWindowPanel(parent, id) : SetWindowPanel(parent, id)
, menuConsole(nullptr) , menuConsole(nullptr)
, messages(nullptr) , messages(nullptr)
, entry(nullptr) , entry(nullptr)
, is_active_window(false) , is_active_window(false)
@@ -514,13 +514,13 @@ ConsolePanel::ConsolePanel(Window* parent, int id)
wxSizer* s = new wxBoxSizer(wxVERTICAL); wxSizer* s = new wxBoxSizer(wxVERTICAL);
s->Add(splitter, 1, wxEXPAND); s->Add(splitter, 1, wxEXPAND);
s->SetSizeHints(this); s->SetSizeHints(this);
SetSizer(s); SetSizer(s);
// init menus // init menus
menuConsole = new wxMenu(); menuConsole = new wxMenu();
add_menu_item_tr(menuConsole, ID_CLEAR_CONSOLE, "clear_console", "clear console"); add_menu_item_tr(menuConsole, ID_CLEAR_CONSOLE, "clear_console", "clear console");
} }
ConsolePanel::~ConsolePanel() { ConsolePanel::~ConsolePanel() {
delete menuConsole; delete menuConsole;
} }
@@ -542,19 +542,19 @@ void ConsolePanel::initUI(wxToolBar* tb, wxMenuBar* mb) {
// stop blinker // stop blinker
is_active_window = true; is_active_window = true;
stop_blinker(); stop_blinker();
add_tool_tr(tb, ID_CLEAR_CONSOLE, "clear_console", "clear console"); add_tool_tr(tb, ID_CLEAR_CONSOLE, "clear_console", "clear console");
tb->Realize(); tb->Realize();
mb->Insert(2, menuConsole, _MENU_("console")); mb->Insert(2, menuConsole, _MENU_("console"));
} }
void ConsolePanel::destroyUI(wxToolBar* tb, wxMenuBar* mb) { void ConsolePanel::destroyUI(wxToolBar* tb, wxMenuBar* mb) {
// Toolbar // Toolbar
tb->DeleteTool(ID_CLEAR_CONSOLE); tb->DeleteTool(ID_CLEAR_CONSOLE);
// Menus // Menus
mb->Remove(2); mb->Remove(2);
// we are no longer active, allow blinker // we are no longer active, allow blinker
is_active_window = false; is_active_window = false;
+3 -3
View File
@@ -19,7 +19,7 @@ class HistoryTextCtrl;
class ConsolePanel : public SetWindowPanel { class ConsolePanel : public SetWindowPanel {
public: public:
ConsolePanel(Window* parent, int id); ConsolePanel(Window* parent, int id);
~ConsolePanel(); ~ConsolePanel();
// --------------------------------------------------- : UI // --------------------------------------------------- : UI
@@ -49,8 +49,8 @@ private:
wxSplitterWindow* splitter; wxSplitterWindow* splitter;
MessageCtrl* messages; MessageCtrl* messages;
wxPanel* entry_panel; wxPanel* entry_panel;
HistoryTextCtrl* entry; HistoryTextCtrl* entry;
wxMenu* menuConsole; wxMenu* menuConsole;
void get_pending_errors(); void get_pending_errors();
+3 -3
View File
@@ -35,7 +35,7 @@ void SetInfoPanel::onChangeSet() {
void SetInfoPanel::initUI(wxToolBar* tb, wxMenuBar* mb) { void SetInfoPanel::initUI(wxToolBar* tb, wxMenuBar* mb) {
// Toolbar // Toolbar
add_tool_tr(tb, ID_FORMAT_BOLD, "bold", "bold", false, wxITEM_CHECK); 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_UNDERLINE, "underline", "underline", false, wxITEM_CHECK);
add_tool_tr(tb, ID_FORMAT_SYMBOL, "symbol", "symbols", 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); 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 // Menus
auto menuFormat = new wxMenu(); auto menuFormat = new wxMenu();
add_menu_item_tr(menuFormat, ID_FORMAT_BOLD, "bold", "bold", wxITEM_CHECK); 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_UNDERLINE, "underline", "underline", wxITEM_CHECK);
add_menu_item_tr(menuFormat, ID_FORMAT_SYMBOL, "symbol", "symbols", 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); 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) { void SetInfoPanel::destroyUI(wxToolBar* tb, wxMenuBar* mb) {
// Toolbar // Toolbar
tb->DeleteTool(ID_FORMAT_BOLD); tb->DeleteTool(ID_FORMAT_BOLD);
tb->DeleteTool(ID_FORMAT_ITALIC); tb->DeleteTool(ID_FORMAT_ITALIC);
tb->DeleteTool(ID_FORMAT_UNDERLINE); tb->DeleteTool(ID_FORMAT_UNDERLINE);
tb->DeleteTool(ID_FORMAT_SYMBOL); tb->DeleteTool(ID_FORMAT_SYMBOL);
tb->DeleteTool(ID_FORMAT_REMINDER); tb->DeleteTool(ID_FORMAT_REMINDER);
+14 -14
View File
@@ -35,20 +35,20 @@ void StylePanel::initControls() {
list = new PackageList (this, wxID_ANY); list = new PackageList (this, wxID_ANY);
use_for_all = new wxButton (this, ID_STYLE_USE_FOR_ALL, _BUTTON_("use for all cards")); 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")); use_custom_options = new wxCheckBox(this, ID_STYLE_USE_CUSTOM, _BUTTON_("use custom styling options"));
editor = new StylingEditor(this, ID_EDITOR, wxNO_BORDER); 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 = 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 // init sizer
wxSizer* s = new wxBoxSizer(wxHORIZONTAL); wxSizer* s = new wxBoxSizer(wxHORIZONTAL);
s->Add(preview, 0, wxRIGHT, 2); s->Add(preview, 0, wxRIGHT, 2);
wxSizer* s2 = new wxBoxSizer(wxVERTICAL); wxSizer* s2 = new wxBoxSizer(wxVERTICAL);
s2->Add(list, 0, wxEXPAND | wxBOTTOM, 4); s2->Add(list, 0, wxEXPAND | wxBOTTOM, 4);
wxSizer* s3 = new wxBoxSizer(wxHORIZONTAL); wxSizer* s3 = new wxBoxSizer(wxHORIZONTAL);
s3->Add(stylesheet_filter, 0, wxBOTTOM | wxLEFT, 4); s3->Add(stylesheet_filter, 0, wxBOTTOM | wxLEFT, 4);
s3->AddStretchSpacer(); 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)); s2->Add(s3, wxSizerFlags().Expand().Border(wxALL, 6));
wxSizer* s4 = new wxStaticBoxSizer(wxVERTICAL, this, _LABEL_("styling options")); wxSizer* s4 = new wxStaticBoxSizer(wxVERTICAL, this, _LABEL_("styling options"));
s4->Add(use_custom_options, 0, wxEXPAND | wxALL, 4); s4->Add(use_custom_options, 0, wxEXPAND | wxALL, 4);
@@ -81,10 +81,10 @@ void StylePanel::updateListSize() {
if (column_count != list->column_count) { if (column_count != list->column_count) {
list->column_count = column_count; list->column_count = column_count;
static_cast<SetWindow*>(GetParent())->fixMinWindowSize(); static_cast<SetWindow*>(GetParent())->fixMinWindowSize();
} }
list_size_already_initialized = true; list_size_already_initialized = true;
} }
bool StylePanel::Layout() { bool StylePanel::Layout() {
updateListSize(); updateListSize();
@@ -136,8 +136,8 @@ void StylePanel::onAction(const Action& action, bool undone) {
use_for_all->Enable(card && card->stylesheet); use_for_all->Enable(card && card->stylesheet);
use_custom_options->Enable((bool)card); use_custom_options->Enable((bool)card);
use_custom_options->SetValue(card ? card->has_styling : false); use_custom_options->SetValue(card ? card->has_styling : false);
} }
void StylePanel::onStylesheetFilterUpdate(wxCommandEvent&) { void StylePanel::onStylesheetFilterUpdate(wxCommandEvent&) {
if (list->hasSelection()) { if (list->hasSelection()) {
StyleSheetP existingStylesheetSelection = list->getSelection<StyleSheet>(false); StyleSheetP existingStylesheetSelection = list->getSelection<StyleSheet>(false);
@@ -208,7 +208,7 @@ void StylePanel::onUseCustom(wxCommandEvent&) {
} }
BEGIN_EVENT_TABLE(StylePanel, wxPanel) 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_COMMAND_RANGE(ID_STYLESHEET_FILTER, ID_STYLESHEET_FILTER, wxEVT_COMMAND_TEXT_UPDATED, StylePanel::onStylesheetFilterUpdate)
EVT_BUTTON (ID_STYLE_USE_FOR_ALL, StylePanel::onUseForAll) EVT_BUTTON (ID_STYLE_USE_FOR_ALL, StylePanel::onUseForAll)
EVT_CHECKBOX (ID_STYLE_USE_CUSTOM, StylePanel::onUseCustom) EVT_CHECKBOX (ID_STYLE_USE_CUSTOM, StylePanel::onUseCustom)
+2 -2
View File
@@ -266,8 +266,8 @@ void SymbolPartList::onChar(wxKeyEvent& ev) {
} }
break; break;
default: default:
// See gui/value/text.cpp // See gui/value/text.cpp
#if defined UNICODE #if defined UNICODE
if (ev.GetUnicodeKey() >= WXK_SPACE) { if (ev.GetUnicodeKey() >= WXK_SPACE) {
#elif defined __WXMSW__ #elif defined __WXMSW__
if (ev.GetKeyCode() >= _(' ') && ev.GetKeyCode() == (int)ev.GetRawKeyCode()) { if (ev.GetKeyCode() >= _(' ') && ev.GetKeyCode() == (int)ev.GetRawKeyCode()) {
+25 -10
View File
@@ -11,6 +11,7 @@
#include <gui/image_slice_window.hpp> #include <gui/image_slice_window.hpp>
#include <data/format/clipboard.hpp> #include <data/format/clipboard.hpp>
#include <data/action/value.hpp> #include <data/action/value.hpp>
#include <data/card.hpp>
#include <wx/clipbrd.h> #include <wx/clipbrd.h>
#include <gui/util.hpp> #include <gui/util.hpp>
@@ -19,7 +20,19 @@
IMPLEMENT_VALUE_EDITOR(Image) {} IMPLEMENT_VALUE_EDITOR(Image) {}
bool ImageValueEditor::onLeftDClick(const RealPoint&, wxMouseEvent&) { 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"), _("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())); wxFD_OPEN, wxGetTopLevelParent(&editor()));
if (!filename.empty()) { if (!filename.empty()) {
@@ -29,28 +42,28 @@ bool ImageValueEditor::onLeftDClick(const RealPoint&, wxMouseEvent&) {
wxLogNull noLog; wxLogNull noLog;
image = wxImage(filename); image = wxImage(filename);
} }
sliceImage(image); sliceImage(image, filename, cardname);
} }
return true; return true;
} }
void ImageValueEditor::sliceImage(const Image& image) { void ImageValueEditor::sliceImage(const Image& image, const String& filename, const String& cardname) {
if (!image.Ok()) return; if (!image.Ok()) return;
// mask // mask
GeneratedImage::Options options((int)style().width, (int)style().height, &parent.getStylePackage(), &parent.getLocalPackage()); GeneratedImage::Options options((int)style().width, (int)style().height, &parent.getStylePackage(), &parent.getLocalPackage());
AlphaMask mask; AlphaMask mask;
style().mask.getNoCache(options,mask); style().mask.getNoCache(options,mask);
// slice // slice
ImageSliceWindow s(wxGetTopLevelParent(&editor()), image, style().getSize(), mask); ImageSliceWindow s(wxGetTopLevelParent(&editor()), image, filename, cardname, style().getSize(), mask);
// clicked ok? // clicked ok?
if (s.ShowModal() == wxID_OK) { if (s.ShowModal() == wxID_OK) {
// store the image into the set // 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 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. // 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. // 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. // 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. // 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); 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. 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)); addAction(value_action(valueP(), new_image_file));
@@ -88,7 +101,9 @@ bool ImageValueEditor::doPaste() {
wxTheClipboard->Close(); wxTheClipboard->Close();
if (!ok) return false; if (!ok) return false;
// slice // slice
sliceImage(data.GetBitmap().ConvertToImage()); CardP card = parent.getCard();
String cardname = card ? card->identification() : _("clipboard");
sliceImage(data.GetBitmap().ConvertToImage(), _("clipboard"), cardname);
return true; return true;
} }
+2 -2
View File
@@ -32,7 +32,7 @@ public:
bool onChar(wxKeyEvent&) override; bool onChar(wxKeyEvent&) override;
private: private:
// Open the image slice window showing the give image // Open the image slice window showing the given image
void sliceImage(const 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; return true;
default: default:
#if defined UNICODE #if defined UNICODE
// I think in theory this works because the UnicodeKey is intended to be only character values. // 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. // 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 // 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. // 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. // I tried to replicate the Numpad issue mentioned below, but couldn't - so unclear if that's still relevant.
if (ev.GetUnicodeKey() >= WXK_SPACE) { if (ev.GetUnicodeKey() >= WXK_SPACE) {
#elif defined __WXMSW__ #elif defined __WXMSW__
if (ev.GetKeyCode() >= _(' ') && ev.GetKeyCode() == (int)ev.GetRawKeyCode()) { if (ev.GetKeyCode() >= _(' ') && ev.GetKeyCode() == (int)ev.GetRawKeyCode()) {
@@ -822,7 +822,7 @@ bool TextValueEditor::hasFormat(int type) const {
case ID_FORMAT_BOLD: case ID_FORMAT_BOLD:
return is_in_tag(value().value(), _("<b"), selection_start_i, selection_end_i); return is_in_tag(value().value(), _("<b"), selection_start_i, selection_end_i);
case ID_FORMAT_ITALIC: 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: case ID_FORMAT_UNDERLINE:
return is_in_tag(value().value(), _("<u"), selection_start_i, selection_end_i); return is_in_tag(value().value(), _("<u"), selection_start_i, selection_end_i);
case ID_FORMAT_SYMBOL: case ID_FORMAT_SYMBOL:
@@ -850,7 +850,7 @@ void TextValueEditor::doFormat(int type) {
case ID_FORMAT_ITALIC: { case ID_FORMAT_ITALIC: {
addAction(toggle_format_action(valueP(), _("i"), selection_start_i, selection_end_i, selection_start, selection_end, _("Italic"))); addAction(toggle_format_action(valueP(), _("i"), selection_start_i, selection_end_i, selection_start, selection_end, _("Italic")));
break; break;
} }
case ID_FORMAT_UNDERLINE: { case ID_FORMAT_UNDERLINE: {
addAction(toggle_format_action(valueP(), _("u"), selection_start_i, selection_end_i, selection_start, selection_end, _("Underline"))); addAction(toggle_format_action(valueP(), _("u"), selection_start_i, selection_end_i, selection_start, selection_end, _("Underline")));
break; break;
+5 -5
View File
@@ -15,7 +15,7 @@
#include <data/locale.hpp> #include <data/locale.hpp>
#include <data/installer.hpp> #include <data/installer.hpp>
#include <data/format/formats.hpp> #include <data/format/formats.hpp>
#include <data/font.hpp> #include <data/font.hpp>
#include <cli/cli_main.hpp> #include <cli/cli_main.hpp>
#include <cli/text_io_handler.hpp> #include <cli/text_io_handler.hpp>
#include <gui/welcome_window.hpp> #include <gui/welcome_window.hpp>
@@ -81,13 +81,13 @@ void nag_about_ascii_version() {
int MSE::OnRun() { int MSE::OnRun() {
try { try {
#ifdef __WXMSW__ #ifdef __WXMSW__
SetAppName(_("Magic Set Editor")); SetAppName(_("Magic Set Editor"));
#else #else
// Platform friendly appname // Platform friendly appname
SetAppName(_("magicseteditor")); SetAppName(_("magicseteditor"));
#endif #endif
Font::PreloadResourceFonts(true); Font::PreloadResourceFonts(true);
wxInitAllImageHandlers(); wxInitAllImageHandlers();
wxFileSystem::AddHandler(new wxInternetFSHandler); // needed for update checker wxFileSystem::AddHandler(new wxInternetFSHandler); // needed for update checker
wxSocketBase::Initialize(); wxSocketBase::Initialize();
+2 -2
View File
@@ -78,7 +78,7 @@ private:
if (is_tag(text, tag_start, _( "<b"))) bold += 1; 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, _("</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, _("</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, _("</u"))) underline -= 1; else if (is_tag(text, tag_start, _("</u"))) underline -= 1;
else if (is_tag(text, tag_start, _( "<sym"))) symbol += 1; else if (is_tag(text, tag_start, _( "<sym"))) symbol += 1;
@@ -301,7 +301,7 @@ private:
(kwpph > 0 ? FONT_SOFT : FONT_NORMAL) | (kwpph > 0 ? FONT_SOFT : FONT_NORMAL) |
(code > 0 ? FONT_CODE : FONT_NORMAL) | (code > 0 ? FONT_CODE : FONT_NORMAL) |
(code_kw > 0 ? FONT_CODE_KW : 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, underline > 0,
fonts.empty() ? nullptr : &fonts.back(), fonts.empty() ? nullptr : &fonts.back(),
param > 0 || param_ref > 0 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; RealSize size;
img.generateCached(img_options, &style.mask, &combine, &bitmap, &image, &size); img.generateCached(img_options, &style.mask, &combine, &bitmap, &image, &size);
// store content properties // store content properties
if (style.content_width != size.width || style.content_height != size.height) { double zoom = dc.getZoom();
style.content_width = size.width / dc.getZoom(); if (style.content_width != size.width / zoom || style.content_height != size.height / zoom) {
style.content_height = size.height / dc.getZoom(); style.content_width = size.width / zoom;
style.content_height = size.height / zoom;
return true; 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()) { if (!default_image.Ok()) {
draw_checker(dc, rect); draw_checker(dc, rect);
} }
else { else {
if (default_image.HasAlpha()) bmp.UseAlpha(true); if (default_image.HasAlpha()) bmp.UseAlpha(true);
dc.DrawImage(default_image, RealPoint(0,0)); 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_top,
-style().padding_left - style().padding_right, -style().padding_left - style().padding_right,
-style().padding_top - style().padding_bottom -style().padding_top - style().padding_bottom
); );
// for some reason, while inside the style tab, this value is empty // 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 // so as a hack for now, if the value is empty, go fetch the caption instead
if (value().value.empty()) { if (value().value.empty()) {
RealSize size = dc.GetTextExtent(field().caption.get()); RealSize size = dc.GetTextExtent(field().caption.get());
dc.DrawText(field().caption.get(), align_in_rect(style().alignment, size, rect)); dc.DrawText(field().caption.get(), align_in_rect(style().alignment, size, rect));
} }
// this is what should happen // this is what should happen
else { else {
RealSize size = dc.GetTextExtent(value().value); RealSize size = dc.GetTextExtent(value().value);
dc.DrawText(value().value, align_in_rect(style().alignment, size, rect)); 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 // keep shadow copy
Binding bind = {name, var}; Binding bind = {name, var};
shadowed.push_back(bind); shadowed.push_back(bind);
} }
if (!var.global_scope) { if (!var.global_scope) {
var.global_scope = false; var.global_scope = false;
} }
var.level = level; var.level = level;
var.value = value; var.value = value;
} }
void Context::setGlobalVariable(Variable name, const ScriptValueP& value) { void Context::setGlobalVariable(Variable name, const ScriptValueP& value) {
#ifdef _DEBUG #ifdef _DEBUG
assert((size_t)name < variable_names.size()); 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) /// Set a variable to a new value (in the current scope)
void setVariable(const String& name, const ScriptValueP& value); void setVariable(const String& name, const ScriptValueP& value);
/// Set a variable to a new value (in the current scope) /// 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); void setGlobalVariable(Variable name, const ScriptValueP& value);
/// Get the value of a variable, throws if it not set /// Get the value of a variable, throws if it not set
@@ -89,7 +89,7 @@ public:// public for FOR_EACH
struct VariableValue { struct VariableValue {
VariableValue() : level(0) {} VariableValue() : level(0) {}
unsigned int level; ///< Scope level on which this variable was set 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? bool global_scope = false; ///< Is this variable globally scoped?
}; };
/// Record of a variable binding that is being shadowed (overwritten) by another binding /// 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/stdpaths.h>
#include <wx/wfstream.h> #include <wx/wfstream.h>
#include <boost/json.hpp> #include <boost/json.hpp>
// ----------------------------------------------------------------------------- : Debugging // ----------------------------------------------------------------------------- : Debugging
SCRIPT_FUNCTION(get_mse_version) { SCRIPT_FUNCTION(get_mse_version) {
SCRIPT_RETURN(app_version.toString()); SCRIPT_RETURN(app_version.toString());
} }
SCRIPT_FUNCTION(get_mse_path) { SCRIPT_FUNCTION(get_mse_path) {
wxFileName app_path(wxStandardPaths::Get().GetExecutablePath()); wxFileName app_path(wxStandardPaths::Get().GetExecutablePath());
String app_folder = app_path.GetPath(); String app_folder = app_path.GetPath();
app_folder.Replace("\\", "/"); app_folder.Replace("\\", "/");
SCRIPT_RETURN(app_folder); SCRIPT_RETURN(app_folder);
} }
SCRIPT_FUNCTION(get_mse_locale) {
SCRIPT_RETURN(settings.locale);
}
SCRIPT_FUNCTION(trace) { SCRIPT_FUNCTION(trace) {
SCRIPT_PARAM_C(String, input); SCRIPT_PARAM_C(String, input);
#if defined(_DEBUG) && 0 #if defined(_DEBUG) && 0
@@ -82,13 +85,13 @@ SCRIPT_FUNCTION(error) {
queue_message(MESSAGE_ERROR, input); queue_message(MESSAGE_ERROR, input);
} }
return script_nil; return script_nil;
} }
SCRIPT_FUNCTION(exists_in_package) { SCRIPT_FUNCTION(exists_in_package) {
SCRIPT_PARAM_C(String, input); SCRIPT_PARAM_C(String, input);
bool result = package_manager.existsInPackage(input); bool result = package_manager.existsInPackage(input);
SCRIPT_RETURN(result); SCRIPT_RETURN(result);
} }
// ----------------------------------------------------------------------------- : Conversion // ----------------------------------------------------------------------------- : Conversion
@@ -264,11 +267,11 @@ SCRIPT_FUNCTION(to_code) {
SCRIPT_FUNCTION(to_json) { SCRIPT_FUNCTION(to_json) {
SCRIPT_PARAM_C(ScriptValueP, input); SCRIPT_PARAM_C(ScriptValueP, input);
SCRIPT_PARAM_C(Set*, set); SCRIPT_PARAM_C(Set*, set);
SCRIPT_PARAM_DEFAULT(bool, pretty_print, true); SCRIPT_PARAM_DEFAULT(bool, pretty_print, true);
boost::json::value jv = mse_to_json(input, set); 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)); if (pretty_print) return to_script(json_pretty_print(jv));
else return to_script(json_ugly_print(jv)); else return to_script(json_ugly_print(jv));
} }
@@ -704,31 +707,31 @@ SCRIPT_FUNCTION(random_select_many) {
ret->value.resize(count); ret->value.resize(count);
} }
return ret; return ret;
} }
SCRIPT_FUNCTION(make_map) { SCRIPT_FUNCTION(make_map) {
SCRIPT_PARAM(ScriptValueP, keys); SCRIPT_PARAM(ScriptValueP, keys);
SCRIPT_PARAM(ScriptValueP, values); SCRIPT_PARAM(ScriptValueP, values);
ScriptValueP keys_it = keys->makeIterator(); ScriptValueP keys_it = keys->makeIterator();
ScriptValueP key; ScriptValueP key;
ScriptValueP values_it = values->makeIterator(); ScriptValueP values_it = values->makeIterator();
ScriptValueP value; ScriptValueP value;
ScriptCustomCollectionP map = make_intrusive<ScriptCustomCollection>(); ScriptCustomCollectionP map = make_intrusive<ScriptCustomCollection>();
while (key = keys_it->next()) { while (key = keys_it->next()) {
if (key == script_nil) continue; if (key == script_nil) continue;
if (value = values_it->next()) { if (value = values_it->next()) {
map->key_value[key->toString()] = value; map->key_value[key->toString()] = value;
} }
else { 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; break;
} }
} }
if (value = values_it->next()) { if (value = values_it->next()) {
queue_message(MESSAGE_WARNING, "More values than keys given in function make_map!"); queue_message(MESSAGE_WARNING, "More values than keys given in function make_map!");
} }
return map; return map;
} }
SCRIPT_FUNCTION(get_card_styling) { SCRIPT_FUNCTION(get_card_styling) {
SCRIPT_PARAM_C(ScriptValueP, input); SCRIPT_PARAM_C(ScriptValueP, input);
@@ -752,6 +755,45 @@ SCRIPT_FUNCTION(get_card_stylesheet) {
throw ScriptError(_("invalid set or card argument")); 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 // ----------------------------------------------------------------------------- : Keywords
@@ -826,6 +868,7 @@ SCRIPT_FUNCTION(rule) {
void init_script_basic_functions(Context& ctx) { void init_script_basic_functions(Context& ctx) {
// debugging // debugging
ctx.setVariable(_("get_mse_version"), script_get_mse_version); 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(_("get_mse_path"), script_get_mse_path);
ctx.setVariable(_("trace"), script_trace); ctx.setVariable(_("trace"), script_trace);
ctx.setVariable(_("warning"), script_warning); 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_code"), script_to_code);
ctx.setVariable(_("to_json"), script_to_json); ctx.setVariable(_("to_json"), script_to_json);
ctx.setVariable(_("from_json"), script_from_json); ctx.setVariable(_("from_json"), script_from_json);
ctx.setVariable(_("type_name"), script_type_name); ctx.setVariable(_("type_name"), script_type_name);
ctx.setVariable(_("make_map"), script_make_map); ctx.setVariable(_("make_map"), script_make_map);
ctx.setVariable(_("get_card_styling"), script_get_card_styling); ctx.setVariable(_("get_card_styling"), script_get_card_styling);
ctx.setVariable(_("get_card_stylesheet"), script_get_card_stylesheet); ctx.setVariable(_("get_card_stylesheet"), script_get_card_stylesheet);
// math // math
@@ -891,6 +934,9 @@ void init_script_basic_functions(Context& ctx) {
ctx.setVariable(_("random_shuffle"), script_random_shuffle); ctx.setVariable(_("random_shuffle"), script_random_shuffle);
ctx.setVariable(_("random_select"), script_random_select); ctx.setVariable(_("random_select"), script_random_select);
ctx.setVariable(_("random_select_many"), script_random_select_many); 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 // keyword
ctx.setVariable(_("expand_keywords"), script_expand_keywords); ctx.setVariable(_("expand_keywords"), script_expand_keywords);
ctx.setVariable(_("expand_keywords_rule"), make_intrusive<ScriptRule>(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/choice.hpp>
#include <data/field/package_choice.hpp> #include <data/field/package_choice.hpp>
#include <data/field/color.hpp> #include <data/field/color.hpp>
#include <data/field/image.hpp> #include <data/field/image.hpp>
#include <data/game.hpp> #include <data/game.hpp>
#include <data/stylesheet.hpp> #include <data/stylesheet.hpp>
#include <data/card.hpp> #include <data/card.hpp>
#include <util/error.hpp> #include <util/error.hpp>
// ----------------------------------------------------------------------------- : new_card // ----------------------------------------------------------------------------- : new_card
SCRIPT_FUNCTION(new_card) { SCRIPT_FUNCTION(new_card) {
SCRIPT_PARAM(GameP, game); SCRIPT_PARAM(GameP, game);
SCRIPT_OPTIONAL_PARAM_(bool, ignore_field_not_found); SCRIPT_OPTIONAL_PARAM_(bool, ignore_field_not_found);
// create a new card object // create a new card object
CardP new_card = make_intrusive<Card>(*game); CardP new_card = make_intrusive<Card>(*game);
// iterate on the given key/value pairs // iterate on the given key/value pairs
SCRIPT_PARAM(ScriptValueP, input); SCRIPT_PARAM(ScriptValueP, input);
ScriptValueP it = input->makeIterator(); ScriptValueP it = input->makeIterator();
ScriptValueP key; ScriptValueP key;
while (ScriptValueP value = it->next(&key)) { while (ScriptValueP value = it->next(&key)) {
assert(key); assert(key);
if (key == script_nil) continue; if (key == script_nil) continue;
String key_name = key->toString(); String key_name = key->toString();
// check if the given value is for a built-in field // 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; 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 // 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); Value* container = get_card_field_container(*game, new_card->data, key_name, ignore_field_not_found);
if (container == nullptr) continue; if (container == nullptr) continue;
FieldP field = container->fieldP; 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 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) { if (field->import_script) {
ScriptValueP ctx_value = ctx.getVariableOpt(SCRIPT_VAR_value); ScriptValueP ctx_value = ctx.getVariableOpt(SCRIPT_VAR_value);
ScriptValueP ctx_card = ctx.getVariableOpt(SCRIPT_VAR_card); ScriptValueP ctx_card = ctx.getVariableOpt(SCRIPT_VAR_card);
ctx.setVariable(SCRIPT_VAR_value, value); ctx.setVariable(SCRIPT_VAR_value, value);
ctx.setVariable(SCRIPT_VAR_card, to_script(new_card)); ctx.setVariable(SCRIPT_VAR_card, to_script(new_card));
ScriptValueP script_input = field->import_script.invoke(ctx, true); ScriptValueP script_input = field->import_script.invoke(ctx, true);
// if the script result is a collection, iterate on the key/value pairs // 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 // treat the keys as field names and the values as what to populate those fields with
if (script_input->type() == SCRIPT_COLLECTION) { if (script_input->type() == SCRIPT_COLLECTION) {
ScriptValueP script_it = script_input->makeIterator(); ScriptValueP script_it = script_input->makeIterator();
ScriptValueP script_key; ScriptValueP script_key;
while (ScriptValueP script_value = script_it->next(&script_key)) { while (ScriptValueP script_value = script_it->next(&script_key)) {
assert(script_key); assert(script_key);
if (script_key == script_nil) continue; if (script_key == script_nil) continue;
String script_key_name = script_key->toString(); String script_key_name = script_key->toString();
// check if the script value is for a built-in field // 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; 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 // 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); Value* script_container = get_card_field_container(*game, new_card->data, script_key_name, ignore_field_not_found);
if (script_container == nullptr) continue; if (script_container == nullptr) continue;
// set the field value to the script value // set the field value to the script value
set_container(script_container, script_value, script_key_name); 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 // if the script result is not a collection, simply set the field value to the script value
else { else {
set_container(container, script_input, key_name); set_container(container, script_input, key_name);
} }
// restore old value and card context variables // restore old value and card context variables
if (ctx_value) ctx.setVariable(SCRIPT_VAR_value, ctx_value); if (ctx_value) ctx.setVariable(SCRIPT_VAR_value, ctx_value);
if (ctx_card) ctx.setVariable(SCRIPT_VAR_card, ctx_card); 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 // if the field has no construction script, simply set the field value to the given value
else { else {
set_container(container, value, key_name); 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) { if (game->import_script) {
ScriptValueP ctx_card = ctx.getVariableOpt(SCRIPT_VAR_card); ScriptValueP ctx_card = ctx.getVariableOpt(SCRIPT_VAR_card);
ctx.setVariable(SCRIPT_VAR_card, to_script(new_card)); ctx.setVariable(SCRIPT_VAR_card, to_script(new_card));
ScriptValueP script_input = game->import_script.invoke(ctx, true); ScriptValueP script_input = game->import_script.invoke(ctx, true);
if (script_input->type() == SCRIPT_COLLECTION) { if (script_input->type() == SCRIPT_COLLECTION) {
// iterate on the key/value pairs given by the script // iterate on the key/value pairs given by the script
ScriptValueP script_it = script_input->makeIterator(); ScriptValueP script_it = script_input->makeIterator();
ScriptValueP script_key; ScriptValueP script_key;
while (ScriptValueP script_value = script_it->next(&script_key)) { while (ScriptValueP script_value = script_it->next(&script_key)) {
assert(script_key); assert(script_key);
if (script_key == script_nil) continue; if (script_key == script_nil) continue;
String script_key_name = script_key->toString(); String script_key_name = script_key->toString();
// check if the script value is for a built-in field // 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; 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 // 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); Value* script_container = get_card_field_container(*game, new_card->data, script_key_name, ignore_field_not_found);
if (script_container == nullptr) continue; if (script_container == nullptr) continue;
// set the field value to the script value // set the field value to the script value
set_container(script_container, script_value, script_key_name); set_container(script_container, script_value, script_key_name);
} }
} }
else { else {
queue_message(MESSAGE_ERROR, _ERROR_("game import script not map")); queue_message(MESSAGE_ERROR, _ERROR_("game import script not map"));
} }
// restore old context card // restore old context card
if (ctx_card) ctx.setVariable(SCRIPT_VAR_card, ctx_card); if (ctx_card) ctx.setVariable(SCRIPT_VAR_card, ctx_card);
} }
SCRIPT_RETURN(new_card); SCRIPT_RETURN(new_card);
} }
SCRIPT_FUNCTION(add_card_to_set) { SCRIPT_FUNCTION(add_card_to_set) {
SCRIPT_PARAM_C(ScriptValueP, input); SCRIPT_PARAM_C(ScriptValueP, input);
SCRIPT_PARAM_C(ScriptValueP, set); SCRIPT_PARAM_C(ScriptValueP, set);
ScriptObject<Set*>* s = dynamic_cast<ScriptObject<Set*>*>(set.get()); ScriptObject<Set*>* s = dynamic_cast<ScriptObject<Set*>*>(set.get());
if (s) { if (s) {
Set& _set = *s->getValue(); Set& _set = *s->getValue();
ScriptObject<CardP>* c = dynamic_cast<ScriptObject<CardP>*>(input.get()); ScriptObject<CardP>* c = dynamic_cast<ScriptObject<CardP>*>(input.get());
if (c) { if (c) {
CardP _card = c->getValue(); CardP _card = c->getValue();
_set.actions.addAction(make_unique<AddCardAction>(ADD, _set, _card)); _set.actions.addAction(make_unique<AddCardAction>(ADD, _set, _card));
SCRIPT_RETURN(true); SCRIPT_RETURN(true);
} }
if (input->type() == SCRIPT_COLLECTION) { if (input->type() == SCRIPT_COLLECTION) {
vector<CardP> _cards; vector<CardP> _cards;
ScriptValueP it = input->makeIterator(); ScriptValueP it = input->makeIterator();
ScriptValueP key; ScriptValueP key;
while (ScriptValueP value = it->next(&key)) { while (ScriptValueP value = it->next(&key)) {
c = dynamic_cast<ScriptObject<CardP>*>(value.get()); c = dynamic_cast<ScriptObject<CardP>*>(value.get());
if (c) { if (c) {
_cards.push_back(c->getValue()); _cards.push_back(c->getValue());
} }
} }
if (!_cards.empty()) { 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(true);
} }
} }
} }
SCRIPT_RETURN(false); SCRIPT_RETURN(false);
} }
// ----------------------------------------------------------------------------- : Init // ----------------------------------------------------------------------------- : Init
+1 -1
View File
@@ -152,7 +152,7 @@ SCRIPT_FUNCTION(english_number_ordinal) {
// ----------------------------------------------------------------------------- : Singular/plural // ----------------------------------------------------------------------------- : Singular/plural
String english_singular(const String& str) { String english_singular(const String& str) {
if (str.Lower() == _("plains")) return str; if (str.Lower() == _("plains")) return str;
if (str.size() > 3 && is_substr(str, str.size()-3, _("ies"))) { if (str.size() > 3 && is_substr(str, str.size()-3, _("ies"))) {
return str.substr(0, str.size() - 3) + _("y"); 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(); return fn.GetFullPath();
} }
void ensure_dir_valid(String& path) { void ensure_dir_valid(String& path) {
if (!wxDirExists(path)) { if (!wxDirExists(path)) {
wxFileName filename = path; wxFileName filename = path;
filename.Mkdir(wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL); 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 str = remove_tag_contents(str_in,_("<sep-soft"));
String ret; String ret;
Tag bold (_("<b>"), _("</b>")), Tag bold (_("<b>"), _("</b>")),
italic(_("<i>"), _("</i>")), italic(_("<i>"), _("</i>")),
underline(_("<u>"), _("</u>")), underline(_("<u>"), _("</u>")),
symbol(_("<span class=\"symbol\">"), _("</span>")); symbol(_("<span class=\"symbol\">"), _("</span>"));
TagStack tags; 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"))) { } else if (is_substr(str, i, _("i"))) {
tags.open (ret, italic); tags.open (ret, italic);
} else if (is_substr(str, i, _("/i"))) { } else if (is_substr(str, i, _("/i"))) {
tags.close(ret, italic); tags.close(ret, italic);
} else if (is_substr(str, i, _("u"))) { } else if (is_substr(str, i, _("u"))) {
tags.open(ret, underline); tags.open(ret, underline);
} else if (is_substr(str, i, _("/u"))) { } 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 str = remove_tag_contents(str_in,_("<sep-soft"));
String ret; String ret;
Tag bold (_("[b]"), _("[/b]")), Tag bold (_("[b]"), _("[/b]")),
italic(_("[i]"), _("[/i]")), italic(_("[i]"), _("[/i]")),
underline(_("[u]"), _("[/u]")); underline(_("[u]"), _("[/u]"));
TagStack tags; TagStack tags;
String symbols; String symbols;
@@ -332,7 +332,7 @@ String to_bbcode(const String& str_in) {
tags.open(ret, underline); tags.open(ret, underline);
} else if (is_substr(str, i, _("/u"))) { } else if (is_substr(str, i, _("/u"))) {
tags.close(ret, underline); tags.close(ret, underline);
} }
/*else if (is_substr(str, i, _("sym"))) { /*else if (is_substr(str, i, _("sym"))) {
tags.open (ret, symbol); tags.open (ret, symbol);
} else if (is_substr(str, i, _("/sym"))) { } else if (is_substr(str, i, _("/sym"))) {
+55 -44
View File
@@ -15,11 +15,11 @@
#include <data/card.hpp> #include <data/card.hpp>
#include <data/stylesheet.hpp> #include <data/stylesheet.hpp>
#include <data/symbol.hpp> #include <data/symbol.hpp>
#include <data/field/symbol.hpp> #include <data/field/symbol.hpp>
#include <data/format/formats.hpp> #include <data/format/formats.hpp>
#include <gfx/generated_image.hpp> #include <gfx/generated_image.hpp>
#include <render/symbol/filter.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); void parse_enum(const String&, ImageCombine& out);
@@ -29,45 +29,55 @@ SCRIPT_FUNCTION(to_image) {
SCRIPT_PARAM_C(GeneratedImageP, input); SCRIPT_PARAM_C(GeneratedImageP, input);
return input; return input;
} }
SCRIPT_FUNCTION(to_card_image) { SCRIPT_FUNCTION(to_card_image) {
SCRIPT_PARAM(Set*, set); SCRIPT_PARAM(Set*, set);
SCRIPT_PARAM(CardP, input); SCRIPT_PARAM(CardP, input);
SCRIPT_PARAM_DEFAULT(double, zoom, 100); SCRIPT_PARAM_DEFAULT(double, zoom, 100);
SCRIPT_PARAM_DEFAULT(Degrees, angle, 0); SCRIPT_PARAM_DEFAULT(Degrees, angle, 0);
SCRIPT_PARAM_DEFAULT(bool, use_user_settings, false); SCRIPT_PARAM_DEFAULT(bool, use_user_settings, false);
if (use_user_settings) { if (use_user_settings) {
// Use the User's Preferences for Export Zoom and Angle settings. // Use the User's Preferences for Export Zoom and Angle settings.
return make_intrusive<ArbitraryImage>(export_bitmap(set, input).ConvertToImage()); return make_intrusive<ArbitraryImage>(export_bitmap(set, input).ConvertToImage());
} else { } else {
// Use the provided (or defaulted) Zoom and Angle. // Use the provided (or defaulted) Zoom and Angle.
return make_intrusive<ArbitraryImage>(export_bitmap(set, input, (zoom / 100), deg_to_rad(angle)).ConvertToImage()); return make_intrusive<ArbitraryImage>(export_bitmap(set, input, (zoom / 100), deg_to_rad(angle)).ConvertToImage());
} }
} }
SCRIPT_FUNCTION(import_image) { SCRIPT_FUNCTION(import_image) {
SCRIPT_PARAM(Set*, set); SCRIPT_PARAM(Set*, set);
SCRIPT_PARAM(String, input); SCRIPT_PARAM(String, input);
auto extImg = make_intrusive<ExternalImage>(input); auto extImg = make_intrusive<ExternalImage>(input);
if (cli.haveConsole()) // makes sure generate() is called, but only once, when using the CLI 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)); extImg->generate(GeneratedImage::Options(0, 0, set->stylesheet.get(), set));
return extImg; return extImg;
} }
// ----------------------------------------------------------------------------- : Image functions // ----------------------------------------------------------------------------- : Image functions
SCRIPT_FUNCTION(width_of) { SCRIPT_FUNCTION(width_of) {
SCRIPT_PARAM(Set*, set); SCRIPT_PARAM(Set*, set);
SCRIPT_PARAM(GeneratedImageP, input); SCRIPT_PARAM(GeneratedImageP, input);
Image image = input->generate(GeneratedImage::Options(0, 0, set->stylesheet.get())); Image image = input->generate(GeneratedImage::Options(0, 0, set->stylesheet.get()));
SCRIPT_RETURN(image.GetWidth()); SCRIPT_RETURN(image.GetWidth());
} }
SCRIPT_FUNCTION(height_of) { SCRIPT_FUNCTION(height_of) {
SCRIPT_PARAM(Set*, set); SCRIPT_PARAM(Set*, set);
SCRIPT_PARAM(GeneratedImageP, input); SCRIPT_PARAM(GeneratedImageP, input);
Image image = input->generate(GeneratedImage::Options(0, 0, set->stylesheet.get())); Image image = input->generate(GeneratedImage::Options(0, 0, set->stylesheet.get()));
SCRIPT_RETURN(image.GetHeight()); 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) { SCRIPT_FUNCTION(insert_image) {
@@ -77,7 +87,7 @@ SCRIPT_FUNCTION(insert_image) {
SCRIPT_PARAM(int, offset_y); SCRIPT_PARAM(int, offset_y);
SCRIPT_OPTIONAL_PARAM_(Color, background_color); SCRIPT_OPTIONAL_PARAM_(Color, background_color);
return make_intrusive<InsertedImage>(base_image, inserted_image, offset_x, offset_y, background_color); return make_intrusive<InsertedImage>(base_image, inserted_image, offset_x, offset_y, background_color);
} }
SCRIPT_FUNCTION(linear_blend) { SCRIPT_FUNCTION(linear_blend) {
SCRIPT_PARAM(GeneratedImageP, image1); SCRIPT_PARAM(GeneratedImageP, image1);
@@ -273,10 +283,11 @@ SCRIPT_FUNCTION(built_in_image) {
// ----------------------------------------------------------------------------- : Init // ----------------------------------------------------------------------------- : Init
void init_script_image_functions(Context& ctx) { void init_script_image_functions(Context& ctx) {
ctx.setVariable(_("to_image"), script_to_image); ctx.setVariable(_("to_image"), script_to_image);
ctx.setVariable(_("to_card_image"), script_to_card_image); ctx.setVariable(_("to_card_image"), script_to_card_image);
ctx.setVariable(_("width_of"), script_width_of); ctx.setVariable(_("width_of"), script_width_of);
ctx.setVariable(_("height_of"), script_height_of); ctx.setVariable(_("height_of"), script_height_of);
ctx.setVariable(_("dimensions_of"), script_dimensions_of);
ctx.setVariable(_("linear_blend"), script_linear_blend); ctx.setVariable(_("linear_blend"), script_linear_blend);
ctx.setVariable(_("masked_blend"), script_masked_blend); ctx.setVariable(_("masked_blend"), script_masked_blend);
ctx.setVariable(_("combine_blend"), script_combine_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(_("drop_shadow"), script_drop_shadow);
ctx.setVariable(_("symbol_variation"), script_symbol_variation); ctx.setVariable(_("symbol_variation"), script_symbol_variation);
ctx.setVariable(_("built_in_image"), script_built_in_image); 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; newline = true;
} else if (isSpace(c)) { } else if (isSpace(c)) {
++pos; ++pos;
// ignore // ignore
} else if (is_substr(pos, end, "include localized file:")) { } else if (is_substr(pos, end, "include localized file:")) {
pos += 23; // "include localized file:" pos += 23; // "include localized file:"
const char* newlines = "\r\n"; const char* newlines = "\r\n";
+2 -2
View File
@@ -73,8 +73,8 @@ void init_script_variables() {
Var(stylesheet); Var(stylesheet);
Var(card_style); Var(card_style);
Var(card); Var(card);
Var(styling); Var(styling);
Var(extra_card_style); Var(extra_card_style);
Var(extra_card); Var(extra_card);
Var(value); Var(value);
Var(condition); Var(condition);
+2 -2
View File
@@ -142,8 +142,8 @@ enum Variable
, SCRIPT_VAR_stylesheet , SCRIPT_VAR_stylesheet
, SCRIPT_VAR_card_style , SCRIPT_VAR_card_style
, SCRIPT_VAR_card , SCRIPT_VAR_card
, SCRIPT_VAR_styling , SCRIPT_VAR_styling
, SCRIPT_VAR_extra_card_style , SCRIPT_VAR_extra_card_style
, SCRIPT_VAR_extra_card , SCRIPT_VAR_extra_card
, SCRIPT_VAR_value , SCRIPT_VAR_value
, SCRIPT_VAR_condition , 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_set, make_intrusive<ScriptObject<Set*>>(&set));
ctx.setVariable(SCRIPT_VAR_game, to_script(set.game)); ctx.setVariable(SCRIPT_VAR_game, to_script(set.game));
ctx.setVariable(SCRIPT_VAR_stylesheet, to_script(stylesheet)); ctx.setVariable(SCRIPT_VAR_stylesheet, to_script(stylesheet));
ctx.setVariable(SCRIPT_VAR_card_style, to_script(&stylesheet->card_style)); ctx.setVariable(SCRIPT_VAR_card_style, to_script(&stylesheet->card_style));
// I'm not entirely clear on why a "dummy value" is necessary here. // 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. // 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. // 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_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_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_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 ctx.setVariable(SCRIPT_VAR_extra_card, set.cards.empty() ? script_nil : to_script(&set.cards.front()->extraDataFor(*stylesheet))); // dummy value
try { try {
@@ -73,13 +73,13 @@ Context& SetScriptContext::getContext(const CardP& card) {
Context& ctx = getContext(stylesheet); Context& ctx = getContext(stylesheet);
if (card) { if (card) {
ctx.setVariable(SCRIPT_VAR_card, to_script(card)); ctx.setVariable(SCRIPT_VAR_card, to_script(card));
ctx.setVariable(SCRIPT_VAR_styling, to_script(&set.stylingDataFor(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_extra_card_style, to_script(&stylesheet->extra_card_style));
ctx.setVariable(SCRIPT_VAR_extra_card, to_script(&card->extraDataFor(*stylesheet))); ctx.setVariable(SCRIPT_VAR_extra_card, to_script(&card->extraDataFor(*stylesheet)));
} else { } else {
ctx.setVariable(SCRIPT_VAR_card, ScriptValueP()); ctx.setVariable(SCRIPT_VAR_card, ScriptValueP());
ctx.setVariable(SCRIPT_VAR_styling, to_script(&set.stylingDataFor(*stylesheet))); 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_style, script_nil);
ctx.setVariable(SCRIPT_VAR_extra_card, script_nil); ctx.setVariable(SCRIPT_VAR_extra_card, script_nil);
} }
return ctx; return ctx;
+2 -2
View File
@@ -196,7 +196,7 @@ bool Package::existsIn(const String& file) {
if (filename.find(_(".mse-")) != String::npos) { if (filename.find(_(".mse-")) != String::npos) {
return false; return false;
} }
} }
unique_ptr<wxInputStream> stream; unique_ptr<wxInputStream> stream;
if (it != files.end() && it->second.wasWritten()) { if (it != files.end() && it->second.wasWritten()) {
@@ -216,7 +216,7 @@ bool Package::existsIn(const String& file) {
return false; return false;
} }
return stream && stream->IsOk(); return stream && stream->IsOk();
} }
unique_ptr<wxInputStream> Package::openIn(const String& file) { unique_ptr<wxInputStream> Package::openIn(const String& file) {
if (!file.empty() && file.GetChar(0) == _('/')) { 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'")); throw FileNotFoundError(name, _("No package name specified, use '/package/filename'"));
} }
pair<unique_ptr<wxInputStream>,Packaged*> PackageManager::openFileFromPackage(Packaged* package, const String& name) { pair<unique_ptr<wxInputStream>,Packaged*> PackageManager::openFileFromPackage(Packaged* package, const String& name) {
if (!name.empty() && name.GetChar(0) == _('/')) { 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) { template <> void Reader::handle(LocalFileName& f) {
f = LocalFileName::fromReadString(this->getValue()); f = LocalFileName::fromReadString(this->getValue());
} }
String Reader::addLocale(String filename) { String Reader::addLocale(String filename) {
return filename + _("_") + settings.locale; return filename + _("_") + settings.locale;
} }
// ----------------------------------------------------------------------------- : EnumReader // ----------------------------------------------------------------------------- : EnumReader
+4 -4
View File
@@ -116,9 +116,9 @@ public:
/// The package being read from /// The package being read from
inline Packaged* getPackage() const { return package; } 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) /// 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; }; inline void setValue(const String& value) { state = UNHANDLED; previous_value = value; };
@@ -180,7 +180,7 @@ private:
/** Maybe the key is "include file" */ /** Maybe the key is "include file" */
template <typename T> template <typename T>
void unknownKey(T& v) { 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; value = key == _("include_localized_file") ? addLocale(value) : value;
auto [stream, include_package] = openFileFromPackage(package, value); auto [stream, include_package] = openFileFromPackage(package, value);
Reader sub_reader(*stream, include_package, value, ignore_invalid); 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 /// Translate 'key' in the for a Package using the current locale
[[deprecated]] [[deprecated]]
String tr(const Package&, const String& key, DefaultLocaleFun def); String tr(const Package&, const String& key, DefaultLocaleFun def);
[[deprecated]] [[deprecated]]
String tr(const String&, const String& key, DefaultLocaleFun def); String tr(const String&, const String& key, DefaultLocaleFun def);
/// Translate 'key' in the for a Package using the current locale /// Translate 'key' in the for a Package using the current locale
[[deprecated]] [[deprecated]]
String tr(const Package&, const String& subcat, const String& key, DefaultLocaleFun def); String tr(const Package&, const String& subcat, const String& key, DefaultLocaleFun def);
[[deprecated]] [[deprecated]]
String tr(const String&, const String& subcat, const String& key, DefaultLocaleFun def); String tr(const String&, const String& subcat, const String& key, DefaultLocaleFun def);
/// A localized string for menus /// 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) #define _TOOLTIP_1_(s,a) format_string(_TOOLTIP_(s), a)
/// A localized string for tooltip labels, with 1 argument (printf style) /// 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) /// 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) /// 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) #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); str = trim(str);
for (String::iterator it = str.begin(); it != str.end(); ++it) { for (String::iterator it = str.begin(); it != str.end(); ++it) {
if (*it == ' ') *it = '_'; if (*it == ' ') *it = '_';
else *it = toLower(*it); else *it = toLower(*it);
} }
return str; return str;
} }
String name_to_caption(const String& str) { String name_to_caption(const String& str) {
String ret; String ret;
+3 -3
View File
@@ -241,9 +241,9 @@ void uncanonical_name_form_in_place(String&);
inline String uncanonical_name_form(String s) { inline String uncanonical_name_form(String s) {
uncanonical_name_form_in_place(s); uncanonical_name_form_in_place(s);
return 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&); String unified_form(String&);
/// Convert a field name to a string that can be shown to the user /// 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) // (where </tag> is the negation of tag)
bool add_or_cancel_tag(const String& tag, String& stack, bool all = false) { bool add_or_cancel_tag(const String& tag, String& stack, bool all = false) {
if (all || starts_with(tag, _("/")) || if (all || starts_with(tag, _("/")) ||
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"))) {
// cancel out all close tags, but not all open tags, // cancel out all close tags, but not all open tags,
// so <xx></xx> is always removed // so <xx></xx> is always removed
@@ -695,9 +695,9 @@ String simplify_tagged_overlap(const String& str) {
Char c = str.GetChar(i); Char c = str.GetChar(i);
if (c == _('<')) { if (c == _('<')) {
String tag = tag_at(str, i); String tag = tag_at(str, i);
if (starts_with(tag, _("b")) || starts_with(tag, _("/b")) || if (starts_with(tag, _("b")) || starts_with(tag, _("/b")) ||
starts_with(tag, _("i")) || starts_with(tag, _("/i")) || starts_with(tag, _("i")) || starts_with(tag, _("/i")) ||
starts_with(tag, _("u")) || starts_with(tag, _("/u")) || starts_with(tag, _("u")) || starts_with(tag, _("/u")) ||
starts_with(tag, _("sym")) || starts_with(tag, _("/sym"))) { starts_with(tag, _("sym")) || starts_with(tag, _("/sym"))) {
// optimize this tag // optimize this tag
if (open_tags.find(_("<") + tag + _(">")) == String::npos) { 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 // ----------------------------------------------------------------------------- : Versions
// NOTE: Don't use leading zeroes, they mean octal // NOTE: Don't use leading zeroes, they mean octal
const Version app_version = 10000 * MSE_VERSION_MAJOR + 100 * MSE_VERSION_MINOR + MSE_VERSION_PATCH; const Version app_version = 10000 * MSE_VERSION_MAJOR + 100 * MSE_VERSION_MINOR + MSE_VERSION_PATCH;
#if defined UNOFFICIAL_BUILD #if defined UNOFFICIAL_BUILD
const Char* version_suffix = _(" (Unofficial)"); const Char* version_suffix = _(" (Unofficial)");
#elif defined UNICODE #elif defined UNICODE
const Char* version_suffix = _(""); const Char* version_suffix = _("");
#else #else
const Char* version_suffix = _(" (ascii build)"); const Char* version_suffix = _(" (ascii build)");
#endif #endif
/// Which version of MSE are the files we write out compatible with? /// 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_SYMMETRY,
ID_MODE_PAINT, ID_MODE_PAINT,
ID_MODE_MAX, ID_MODE_MAX,
// Welcome Window // Welcome Window
ID_SELECT_LANGUAGE, ID_SELECT_LANGUAGE,
}; };
@@ -108,12 +108,14 @@ enum ChildMenuID {
ID_CARD_ROTATE_270, ID_CARD_ROTATE_270,
// CardList // CardList
ID_SELECT_COLUMNS, ID_SELECT_COLUMNS,
ID_CARD_LINK,
ID_CARD_AND_LINK_COPY,
ID_CARD_ADD_CSV, ID_CARD_ADD_CSV,
ID_CARD_ADD_CSV_SEP, ID_CARD_ADD_CSV_SEP,
ID_CARD_ADD_CSV_BROWSE, ID_CARD_ADD_CSV_BROWSE,
ID_CARD_ADD_JSON, ID_CARD_ADD_JSON,
ID_CARD_ADD_JSON_ARRAY, ID_CARD_ADD_JSON_ARRAY,
ID_CARD_ADD_JSON_BROWSE, ID_CARD_ADD_JSON_BROWSE,
// Keyword menu // Keyword menu
ID_KEYWORD_ADD = 6101, ID_KEYWORD_ADD = 6101,
@@ -124,7 +126,7 @@ enum ChildMenuID {
// Format menu // Format menu
ID_FORMAT_BOLD = 6201, ID_FORMAT_BOLD = 6201,
ID_FORMAT_ITALIC, ID_FORMAT_ITALIC,
ID_FORMAT_UNDERLINE, ID_FORMAT_UNDERLINE,
ID_FORMAT_SYMBOL, ID_FORMAT_SYMBOL,
ID_FORMAT_REMINDER, ID_FORMAT_REMINDER,
@@ -192,6 +194,12 @@ enum ChildMenuID {
ID_COLLAPSE_NOTES = 8001, ID_COLLAPSE_NOTES = 8001,
ID_CARD_FILTER, ID_CARD_FILTER,
ID_CARD_COUNTER, 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 // Style panel
ID_STYLE_USE_FOR_ALL = 8011, ID_STYLE_USE_FOR_ALL = 8011,
@@ -219,7 +227,7 @@ enum ChildMenuID {
ID_CUSTOM_PACK, ID_CUSTOM_PACK,
// Console panel // Console panel
ID_EVALUATE, ID_EVALUATE,
ID_CLEAR_CONSOLE, ID_CLEAR_CONSOLE,
// SymbolFont (Format menu) // SymbolFont (Format menu)
@@ -279,12 +287,13 @@ enum ControlID {
ID_PREVIEW, ID_PREVIEW,
ID_SELECTOR, ID_SELECTOR,
ID_SIZE, ID_SIZE,
ID_GRID,
ID_LEFT, ID_LEFT,
ID_TOP, ID_TOP,
ID_WIDTH, ID_WIDTH,
ID_HEIGHT, ID_HEIGHT,
ID_SELECTION_CENTER, ID_SELECTION_CENTER,
ID_SELECTION_CENTER_HORIZONTALLY, ID_SELECTION_CENTER_HORIZONTALLY,
ID_SELECTION_CENTER_VERTICALLY, ID_SELECTION_CENTER_VERTICALLY,
ID_FIX_ASPECT, ID_FIX_ASPECT,
ID_ZOOM, ID_ZOOM,
@@ -294,8 +303,8 @@ enum ControlID {
ID_EXPORT_ZOOM_X, ID_EXPORT_ZOOM_X,
ID_EXPORT_ZOOM_Y, ID_EXPORT_ZOOM_Y,
ID_SHARPEN, ID_SHARPEN,
ID_SHARPEN_AMOUNT, ID_SHARPEN_AMOUNT,
// Internal window // Internal window
ID_INTERNAL_SCALE, ID_INTERNAL_SCALE,
// Updates window // Updates window
ID_PACKAGE_LIST, ID_PACKAGE_LIST,
@@ -309,5 +318,11 @@ enum ControlID {
ID_ADD_ITEM, ID_ADD_ITEM,
ID_REMOVE_ITEM, ID_REMOVE_ITEM,
ID_DEFAULTS, 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 <?php
require_once('./modules/mse-drupal-modules/autoformat.inc'); require_once('./modules/mse-drupal-modules/autoformat.inc');
/** /**
@@ -18,28 +18,28 @@ function autoformat_help($section) {
*/ */
function autoformat_filter_tips($delta, $format, $long = FALSE) { function autoformat_filter_tips($delta, $format, $long = FALSE) {
if ($long) { if ($long) {
$tips = array( $tips = array(
'p' => array( t('Paragraphs'), "A single line break\nhas no effect.\n\nBut two line breaks start a new paragraph."), '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". 'h2' => array( t('Headings'), '=='. t('Section') ."==\n".
'==='. t('Subsection') ."===\n". '==='. t('Subsection') ."===\n".
'===='. t('Subsection two') ."===="), '===='. t('Subsection two') ."===="),
'ul' => array( t('Unordered lists'), "* First list item\n* Second list item\n** A nested list item"), '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"), '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."), '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"), '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"), 'pre' => array( t('Preformated text'), " If you start with a space\n the formating is preserved"),
'em' => array( t('Emphasis'), "''Emphasized text''"), 'em' => array( t('Emphasis'), "''Emphasized text''"),
'strong'=> array( t('Strong Emphasis'), "'''Strongly emphasized text'''"), 'strong'=> array( t('Strong Emphasis'), "'''Strongly emphasized text'''"),
); );
$header = array(t('Description'), t('You Type'), t('You Get')); $header = array(t('Description'), t('You Type'), t('You Get'));
foreach($tips as $tip) { foreach($tips as $tip) {
$rows[] = array( $rows[] = array(
array('data' => $tip[0], 'class' => 'description'), array('data' => $tip[0], 'class' => 'description'),
array('data' => strpos($tip[1],"\n") !== false array('data' => strpos($tip[1],"\n") !== false
? '<pre>'. check_plain($tip[1]) .'</pre>' ? '<pre>'. check_plain($tip[1]) .'</pre>'
: '<code>'. check_plain($tip[1]) .'</code>', : '<code>'. check_plain($tip[1]) .'</code>',
'class' => 'type'), 'class' => 'type'),
array('data' => autoformat($tip[1]), 'class' => 'get') 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>.'); $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 <?php
// Automatic formating of text using the same tags as MediaWiki (wikipedia). // Automatic formating of text using the same tags as MediaWiki (wikipedia).
require_once('./modules/mse-drupal-modules/highlight.inc'); require_once('./modules/mse-drupal-modules/highlight.inc');
// quick and dirty aliasses // quick and dirty aliasses
@@ -51,8 +51,8 @@ $built_in_functions = array(
'filter_list' =>'', 'filter_list' =>'',
'random_shuffle' =>'', 'random_shuffle' =>'',
'random_select' =>'', 'random_select' =>'',
'random_select_many' =>'', 'random_select_many' =>'',
'get_card_styling' =>'', 'get_card_styling' =>'',
'get_card_stylesheet' =>'', 'get_card_stylesheet' =>'',
// keywords // keywords
'expand_keywords' =>'', 'expand_keywords_rule'=>'expand_keywords', 'expand_keywords' =>'', 'expand_keywords_rule'=>'expand_keywords',
@@ -93,14 +93,19 @@ $built_in_functions = array(
'flip_vertical' =>'', 'flip_vertical' =>'',
'rotate' =>'', 'rotate' =>'',
'drop_shadow' =>'', 'drop_shadow' =>'',
'insert_image' =>'',
'dimensions_of' =>'',
'symbol_variation' =>'', 'symbol_variation' =>'',
'import_image' =>'', 'import_image' =>'',
'built_in_image' =>'', 'built_in_image' =>'',
// cards // cards
'new_card' =>'', 'new_card' =>'',
'add_card_to_set' =>'', 'has_link' =>'',
'get_card_from_link' =>'',
'get_card_from_uid' =>'',
'get_card_styling' =>'', 'get_card_styling' =>'',
'get_card_stylesheet' =>'', 'get_card_stylesheet' =>'',
'add_card_to_set' =>'',
// html export // html export
'to_html' =>'', 'to_html' =>'',
'symbols_to_html' =>'', 'symbols_to_html' =>'',
@@ -111,6 +116,7 @@ $built_in_functions = array(
'write_set_file' =>'', 'write_set_file' =>'',
// other // other
'get_mse_version' =>'', 'get_mse_version' =>'',
'get_mse_locale' =>'',
'get_mse_path' =>'', 'get_mse_path' =>'',
'trace' =>'', 'trace' =>'',
'assert' =>'', 'assert' =>'',
@@ -248,4 +254,4 @@ function highlight_script_string($code) {
return $code; return $code;
} }
?> ?>