mirror of
https://github.com/amyinspace/MagicSetEditor2.git
synced 2026-06-12 05:36:59 -04:00
Editing of keywords
git-svn-id: svn://svn.code.sf.net/p/magicseteditor/code/trunk@255 0fc631ac-6414-0410-93d0-97cfa31319b6
This commit is contained in:
@@ -10,6 +10,9 @@
|
|||||||
#include <data/keyword.hpp>
|
#include <data/keyword.hpp>
|
||||||
#include <data/set.hpp>
|
#include <data/set.hpp>
|
||||||
#include <data/game.hpp>
|
#include <data/game.hpp>
|
||||||
|
#include <script/parser.hpp>
|
||||||
|
#include <util/tagged_string.hpp>
|
||||||
|
#include <util/error.hpp>
|
||||||
|
|
||||||
DECLARE_TYPEOF_COLLECTION(KeywordModeP);
|
DECLARE_TYPEOF_COLLECTION(KeywordModeP);
|
||||||
|
|
||||||
@@ -69,3 +72,101 @@ void RemoveKeywordAction::perform(bool to_undo) {
|
|||||||
set.keywords.insert(set.keywords.begin() + keyword_id, keyword);
|
set.keywords.insert(set.keywords.begin() + keyword_id, keyword);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Changing keywords
|
||||||
|
|
||||||
|
KeywordReminderTextValue::KeywordReminderTextValue(const TextFieldP& field, Keyword* keyword, bool editable)
|
||||||
|
: KeywordTextValue(field, keyword, &keyword->reminder.getUnparsed(), editable)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void KeywordReminderTextValue::store() {
|
||||||
|
if (!editable) {
|
||||||
|
retrieve();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Re-highlight
|
||||||
|
String new_value = untag(value);
|
||||||
|
highlight(new_value);
|
||||||
|
// Try to parse the script
|
||||||
|
try {
|
||||||
|
ScriptP new_script = parse(new_value, true);
|
||||||
|
// parsed okay, assign
|
||||||
|
errors.clear();
|
||||||
|
keyword.reminder.getScriptP() = new_script;
|
||||||
|
keyword.reminder.getUnparsed() = new_value;
|
||||||
|
} catch (const Error& e) {
|
||||||
|
// parse errors, report
|
||||||
|
errors = e.what(); // TODO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeywordReminderTextValue::retrieve() {
|
||||||
|
highlight(*underlying);
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeywordReminderTextValue::highlight(const String& code) {
|
||||||
|
// Add tags to indicate code / syntax highlight
|
||||||
|
// i.e. bla {if code "x" } bla
|
||||||
|
// becomes:
|
||||||
|
// bla <code>{<code-kw>if</code-kw> code "<code-string>x</code-string>" } bla
|
||||||
|
String new_value;
|
||||||
|
int in_brace = 0;
|
||||||
|
bool in_string = true;
|
||||||
|
for (size_t pos = 0 ; pos < code.size() ; ) {
|
||||||
|
Char c = code.GetChar(pos);
|
||||||
|
if (c == _('<')) {
|
||||||
|
new_value += _('\1'); // escape
|
||||||
|
++pos;
|
||||||
|
} else if (c == _('{')) {
|
||||||
|
in_brace++;
|
||||||
|
if (in_brace == 1) new_value += _("<code>");
|
||||||
|
if (in_string) in_string = false;
|
||||||
|
new_value += c;
|
||||||
|
++pos;
|
||||||
|
} else if (c == _('}') && !in_string) {
|
||||||
|
new_value += c;
|
||||||
|
in_brace--;
|
||||||
|
if (in_brace == 0) {
|
||||||
|
new_value += _("</code>");
|
||||||
|
in_string = true;
|
||||||
|
}
|
||||||
|
++pos;
|
||||||
|
} else if (c == _('"')) {
|
||||||
|
if (in_string) {
|
||||||
|
in_string = false;
|
||||||
|
new_value += _("\"<code-str>");
|
||||||
|
} else {
|
||||||
|
in_string = true;
|
||||||
|
new_value += _("<code-str>\"");
|
||||||
|
}
|
||||||
|
++pos;
|
||||||
|
} else if (c == _('\\') && in_string && pos + 1 < code.size()) {
|
||||||
|
new_value += c + code.GetChar(pos + 1); // escape code
|
||||||
|
pos += 2;
|
||||||
|
} else if (is_substr(code, pos, _("if ")) && !in_string) {
|
||||||
|
new_value += _("<code-kw>if</code-kw> ");
|
||||||
|
pos += 3;
|
||||||
|
} else if (is_substr(code, pos, _("then ")) && !in_string) {
|
||||||
|
new_value += _("<code-kw>then</code-kw> ");
|
||||||
|
pos += 5;
|
||||||
|
} else if (is_substr(code, pos, _("else ")) && !in_string) {
|
||||||
|
new_value += _("<code-kw>else</code-kw> ");
|
||||||
|
pos += 5;
|
||||||
|
} else if (is_substr(code, pos, _("for ")) && !in_string) {
|
||||||
|
new_value += _("<code-kw>for</code-kw> ");
|
||||||
|
pos += 4;
|
||||||
|
} else if (is_substr(code, pos, _("param")) && !in_string) {
|
||||||
|
// parameter reference
|
||||||
|
size_t end = code.find_first_not_of(_("0123456789"), pos + 5);
|
||||||
|
if (end == String::npos) end = code.size();
|
||||||
|
String param = code.substr(pos, end-pos);
|
||||||
|
new_value += _("<") + param + _(">") + param + _("</") + param + _(">");
|
||||||
|
pos = end;
|
||||||
|
} else {
|
||||||
|
new_value += c;
|
||||||
|
++pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// set
|
||||||
|
value = new_value;
|
||||||
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
#include <util/prec.hpp>
|
#include <util/prec.hpp>
|
||||||
#include <util/action_stack.hpp>
|
#include <util/action_stack.hpp>
|
||||||
|
#include <data/field/text.hpp>
|
||||||
|
|
||||||
class Set;
|
class Set;
|
||||||
DECLARE_POINTER_TYPE(Keyword);
|
DECLARE_POINTER_TYPE(Keyword);
|
||||||
@@ -62,5 +63,38 @@ class RemoveKeywordAction : public KeywordListAction {
|
|||||||
|
|
||||||
// ----------------------------------------------------------------------------- : Changing keywords
|
// ----------------------------------------------------------------------------- : Changing keywords
|
||||||
|
|
||||||
|
/// A FakeTextValue that is used to edit an aspect of a keyword
|
||||||
|
/** These values can be seen in ValueActions.
|
||||||
|
* Can edit one of:
|
||||||
|
* - the keyword name
|
||||||
|
* - the match string
|
||||||
|
* - reminder text
|
||||||
|
*/
|
||||||
|
class KeywordTextValue : public FakeTextValue {
|
||||||
|
public:
|
||||||
|
KeywordTextValue(const TextFieldP& field, Keyword* keyword, String* underlying, bool editable, bool untagged = false)
|
||||||
|
: FakeTextValue(field, underlying, editable, untagged)
|
||||||
|
, keyword(*keyword)
|
||||||
|
{}
|
||||||
|
|
||||||
|
Keyword& keyword; ///< The keyword that is being edited
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A FakeTextValue that is used to edit reminder text scripts
|
||||||
|
class KeywordReminderTextValue : public KeywordTextValue {
|
||||||
|
public:
|
||||||
|
KeywordReminderTextValue(const TextFieldP& field, Keyword* keyword, bool editable);
|
||||||
|
|
||||||
|
String errors; ///< Errors in the script
|
||||||
|
|
||||||
|
/// Try to compile the script
|
||||||
|
virtual void store();
|
||||||
|
/// Add some tags, so the script looks nice
|
||||||
|
virtual void retrieve();
|
||||||
|
|
||||||
|
/// Syntax highlight, and store in value
|
||||||
|
void highlight(const String& code);
|
||||||
|
};
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------- : EOF
|
// ----------------------------------------------------------------------------- : EOF
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+14
-7
@@ -109,18 +109,25 @@ IMPLEMENT_REFLECTION_NAMELESS(TextValue) {
|
|||||||
|
|
||||||
// ----------------------------------------------------------------------------- : FakeTextValue
|
// ----------------------------------------------------------------------------- : FakeTextValue
|
||||||
|
|
||||||
FakeTextValue::FakeTextValue(const TextFieldP& field, String* underlying, bool untagged)
|
FakeTextValue::FakeTextValue(const TextFieldP& field, String* underlying, bool editable, bool untagged)
|
||||||
: TextValue(field), underlying(underlying)
|
: TextValue(field), underlying(underlying)
|
||||||
, untagged(untagged)
|
, editable(editable), untagged(untagged)
|
||||||
{
|
{}
|
||||||
if (underlying) {
|
|
||||||
value.assign(untagged ? escape(*underlying) : *underlying);
|
void FakeTextValue::store() {
|
||||||
|
if (editable) {
|
||||||
|
*underlying = untagged ? untag(value) : value;
|
||||||
|
} else {
|
||||||
|
retrieve();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
void FakeTextValue::retrieve() {
|
||||||
|
value.assign(untagged ? escape(*underlying) : *underlying);
|
||||||
|
}
|
||||||
|
|
||||||
void FakeTextValue::onAction(Action& a, bool undone) {
|
void FakeTextValue::onAction(Action& a, bool undone) {
|
||||||
if (underlying) {
|
if (underlying) {
|
||||||
*underlying = untagged ? untag(value) : value;
|
store();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,6 +137,6 @@ bool FakeTextValue::equals(const Value* that) {
|
|||||||
const FakeTextValue* thatT = dynamic_cast<const FakeTextValue*>(that);
|
const FakeTextValue* thatT = dynamic_cast<const FakeTextValue*>(that);
|
||||||
if (!thatT || underlying != thatT->underlying) return false;
|
if (!thatT || underlying != thatT->underlying) return false;
|
||||||
// update the value
|
// update the value
|
||||||
value.assign(untagged ? escape(*underlying) : *underlying);
|
retrieve();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-2
@@ -107,10 +107,18 @@ class FakeTextValue : public TextValue {
|
|||||||
public:
|
public:
|
||||||
/// Initialize the fake text value
|
/// Initialize the fake text value
|
||||||
/** underlying can be nullptr, in that case there is no underlying value */
|
/** underlying can be nullptr, in that case there is no underlying value */
|
||||||
FakeTextValue(const TextFieldP& field, String* underlying, bool untagged);
|
FakeTextValue(const TextFieldP& field, String* underlying, bool editable, bool untagged);
|
||||||
|
|
||||||
String* const underlying; ///< The underlying actual value, can be null
|
String* const underlying; ///< The underlying actual value, can be null
|
||||||
bool const untagged; ///< The underlying value is untagged
|
bool const editable; ///< The underlying value can be edited
|
||||||
|
bool const untagged; ///< The underlying value is untagged
|
||||||
|
|
||||||
|
/// Store the value in the underlying value.
|
||||||
|
/** May be overloaded to do some transformation */
|
||||||
|
virtual void store();
|
||||||
|
/// Retrieve the value from the underlying value.
|
||||||
|
/** May be overloaded to do some transformation */
|
||||||
|
virtual void retrieve();
|
||||||
|
|
||||||
/// Update underlying data
|
/// Update underlying data
|
||||||
virtual void onAction(Action& a, bool undone);
|
virtual void onAction(Action& a, bool undone);
|
||||||
|
|||||||
+6
-1
@@ -27,7 +27,7 @@ void Font::initDependencies(Context& ctx, const Dependency& dep) const {
|
|||||||
shadow_color.initDependencies(ctx, dep);
|
shadow_color.initDependencies(ctx, dep);
|
||||||
}
|
}
|
||||||
|
|
||||||
FontP Font::make(bool bold, bool italic, bool placeholder_color, Color* other_color) const {
|
FontP Font::make(bool bold, bool italic, bool placeholder_color, bool code_color, Color* other_color) const {
|
||||||
FontP f(new Font(*this));
|
FontP f(new Font(*this));
|
||||||
if (bold) f->font.SetWeight(wxBOLD);
|
if (bold) f->font.SetWeight(wxBOLD);
|
||||||
if (italic) {
|
if (italic) {
|
||||||
@@ -37,6 +37,11 @@ FontP Font::make(bool bold, bool italic, bool placeholder_color, Color* other_co
|
|||||||
f->font.SetWeight(wxBOLD);
|
f->font.SetWeight(wxBOLD);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (code_color) {
|
||||||
|
f->color = Color(128,0,0);
|
||||||
|
f->font.SetFamily(wxFONTFAMILY_TELETYPE);
|
||||||
|
f->font.SetFaceName(_("Courier New"));
|
||||||
|
}
|
||||||
if (placeholder_color) {
|
if (placeholder_color) {
|
||||||
f->color = f->separator_color;
|
f->color = f->separator_color;
|
||||||
f->shadow_displacement = RealSize(0,0); // no shadow
|
f->shadow_displacement = RealSize(0,0); // no shadow
|
||||||
|
|||||||
+1
-1
@@ -41,7 +41,7 @@ class Font {
|
|||||||
inline bool hasShadow() { return shadow_displacement.width != 0 || shadow_displacement.height != 0; }
|
inline bool hasShadow() { return shadow_displacement.width != 0 || shadow_displacement.height != 0; }
|
||||||
|
|
||||||
/// Make a bold/italic/placeholder version of this font
|
/// Make a bold/italic/placeholder version of this font
|
||||||
FontP make(bool bold, bool italic, bool placeholder_color, Color* other_color) const;
|
FontP make(bool bold, bool italic, bool placeholder_color, bool code_color, Color* other_color) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DECLARE_REFLECTION();
|
DECLARE_REFLECTION();
|
||||||
|
|||||||
@@ -40,6 +40,8 @@ class KeywordParam {
|
|||||||
/// Information on when and how to use a keyword
|
/// Information on when and how to use a keyword
|
||||||
class KeywordMode {
|
class KeywordMode {
|
||||||
public:
|
public:
|
||||||
|
KeywordMode() : is_default(false) {}
|
||||||
|
|
||||||
String name; ///< Name of the mode
|
String name; ///< Name of the mode
|
||||||
String description; ///< Description of the type
|
String description; ///< Description of the type
|
||||||
bool is_default; ///< This is the default mode for new keywords
|
bool is_default; ///< This is the default mode for new keywords
|
||||||
|
|||||||
@@ -10,6 +10,8 @@
|
|||||||
#include <data/set.hpp>
|
#include <data/set.hpp>
|
||||||
#include <data/game.hpp>
|
#include <data/game.hpp>
|
||||||
#include <data/keyword.hpp>
|
#include <data/keyword.hpp>
|
||||||
|
#include <data/action/value.hpp>
|
||||||
|
#include <data/action/keyword.hpp>
|
||||||
#include <util/tagged_string.hpp>
|
#include <util/tagged_string.hpp>
|
||||||
#include <gfx/gfx.hpp>
|
#include <gfx/gfx.hpp>
|
||||||
|
|
||||||
@@ -47,8 +49,49 @@ void KeywordList::onChangeSet() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void KeywordList::onAction(const Action& action, bool undone) {
|
void KeywordList::onAction(const Action& action, bool undone) {
|
||||||
//TYPE_CASE(action, AddKeywordAction) {
|
TYPE_CASE(action, AddKeywordAction) {
|
||||||
//}
|
if (undone) {
|
||||||
|
long pos = selected_item_pos;
|
||||||
|
refreshList();
|
||||||
|
if (action.keyword == selected_item) {
|
||||||
|
// select the next keyword, if not possible, select the last
|
||||||
|
if (pos + 1 < GetItemCount()) {
|
||||||
|
selectItemPos(pos, true);
|
||||||
|
} else {
|
||||||
|
selectItemPos(GetItemCount() - 1, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// select the new keyword
|
||||||
|
selectItem(action.keyword, false /*list will be refreshed anyway*/, true);
|
||||||
|
refreshList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TYPE_CASE(action, RemoveKeywordAction) {
|
||||||
|
if (undone) {
|
||||||
|
// select the re-added keyword
|
||||||
|
selectItem(action.keyword, false /*list will be refreshed anyway*/, true);
|
||||||
|
refreshList();
|
||||||
|
} else {
|
||||||
|
long pos = selected_item_pos;
|
||||||
|
refreshList();
|
||||||
|
if (action.keyword == selected_item) {
|
||||||
|
// select the next keyword, if not possible, select the last
|
||||||
|
if (pos + 1 < GetItemCount()) {
|
||||||
|
selectItemPos(pos, true);
|
||||||
|
} else {
|
||||||
|
selectItemPos(GetItemCount() - 1, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TYPE_CASE(action, ValueAction) {
|
||||||
|
KeywordTextValue* value = dynamic_cast<KeywordTextValue*>(action.valueP.get());
|
||||||
|
if (value) {
|
||||||
|
// this is indeed an action on a keyword, refresh
|
||||||
|
refreshList();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------- : KeywordListBase : for ItemList
|
// ----------------------------------------------------------------------------- : KeywordListBase : for ItemList
|
||||||
@@ -83,8 +126,8 @@ bool KeywordList::compareItems(void* a, void* b) const {
|
|||||||
case 1: return ka.match < kb.match;
|
case 1: return ka.match < kb.match;
|
||||||
case 2: return ka.mode < kb.mode;
|
case 2: return ka.mode < kb.mode;
|
||||||
//case 3:
|
//case 3:
|
||||||
//case 4:
|
case 4: return ka.reminder.getUnparsed() < kb.reminder.getUnparsed();
|
||||||
default: // TODO: 3 and 4
|
default: // TODO: 3
|
||||||
return ka.keyword < kb.keyword;
|
return ka.keyword < kb.keyword;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -98,7 +141,7 @@ String KeywordList::OnGetItemText (long pos, long col) const {
|
|||||||
case 1: return match_string(kw);
|
case 1: return match_string(kw);
|
||||||
case 2: return kw.mode;
|
case 2: return kw.mode;
|
||||||
case 3: return _("TODO");
|
case 3: return _("TODO");
|
||||||
case 4: return _("TODO");
|
case 4: return kw.reminder.getUnparsed();
|
||||||
default: return wxEmptyString;
|
default: return wxEmptyString;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,6 +40,10 @@ TextField& TextCtrl::getField() {
|
|||||||
assert(!viewers.empty());
|
assert(!viewers.empty());
|
||||||
return static_cast<TextField&>(*viewers.front()->getField());
|
return static_cast<TextField&>(*viewers.front()->getField());
|
||||||
}
|
}
|
||||||
|
TextFieldP TextCtrl::getFieldP() {
|
||||||
|
assert(!viewers.empty());
|
||||||
|
return static_pointer_cast<TextField>(viewers.front()->getField());
|
||||||
|
}
|
||||||
void TextCtrl::updateSize() {
|
void TextCtrl::updateSize() {
|
||||||
wxSize cs = GetClientSize();
|
wxSize cs = GetClientSize();
|
||||||
Style& style = getStyle();
|
Style& style = getStyle();
|
||||||
@@ -49,36 +53,15 @@ void TextCtrl::updateSize() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void TextCtrl::setValue(String* value, bool untagged) {
|
void TextCtrl::setValue(String* value, bool untagged) {
|
||||||
if (value != this->value) {
|
setValue(new_shared4<FakeTextValue>(getFieldP(), value, true, untagged));
|
||||||
this->value = value;
|
|
||||||
// create a new value, for a different underlying actual value
|
|
||||||
ValueViewer& viewer = *viewers.front();
|
|
||||||
TextValueP new_value(new FakeTextValue(static_pointer_cast<TextField>(viewer.getField()), this->value, untagged));
|
|
||||||
viewer.setValue(new_value);
|
|
||||||
updateSize();
|
|
||||||
valueChanged();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
void TextCtrl::valueChanged() {
|
void TextCtrl::setValue(const FakeTextValueP& value) {
|
||||||
if (!viewers.empty()) {
|
value->retrieve();
|
||||||
TextValue& tv = static_cast<TextValue&>(*viewers.front()->getValue());
|
viewers.front()->setValue(value);
|
||||||
tv.value.assign(value ? String(*value) : String(wxEmptyString));
|
updateSize();
|
||||||
viewers.front()->onValueChange();
|
|
||||||
}
|
|
||||||
onChange();
|
onChange();
|
||||||
}
|
}
|
||||||
void TextCtrl::onAction(const Action& action, bool undone) {
|
|
||||||
DataEditor::onAction(action, undone);
|
|
||||||
/*
|
|
||||||
TYPE_CASE(action, TextValueAction) {
|
|
||||||
FakeTextValue& tv = static_cast<FakeTextValue&>(*viewers.front()->getValue());
|
|
||||||
if (tv.equals(action.valueP.get())) {
|
|
||||||
// the value has changed
|
|
||||||
if (value) *value = tv.value();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
void TextCtrl::onChangeSet() {
|
void TextCtrl::onChangeSet() {
|
||||||
DataEditor::onChangeSet();
|
DataEditor::onChangeSet();
|
||||||
// initialize
|
// initialize
|
||||||
@@ -86,7 +69,7 @@ void TextCtrl::onChangeSet() {
|
|||||||
// create a field, style and value
|
// create a field, style and value
|
||||||
TextFieldP field(new TextField);
|
TextFieldP field(new TextField);
|
||||||
TextStyleP style(new TextStyle(field));
|
TextStyleP style(new TextStyle(field));
|
||||||
TextValueP value(new FakeTextValue(field, nullptr, false));
|
TextValueP value(new FakeTextValue(field, nullptr, false, false));
|
||||||
// set stuff
|
// set stuff
|
||||||
field->index = 0;
|
field->index = 0;
|
||||||
field->multi_line = multi_line;
|
field->multi_line = multi_line;
|
||||||
|
|||||||
@@ -12,8 +12,9 @@
|
|||||||
#include <util/prec.hpp>
|
#include <util/prec.hpp>
|
||||||
#include <gui/control/card_editor.hpp>
|
#include <gui/control/card_editor.hpp>
|
||||||
|
|
||||||
class TextField;
|
|
||||||
class TextStyle;
|
class TextStyle;
|
||||||
|
DECLARE_POINTER_TYPE(TextField);
|
||||||
|
DECLARE_POINTER_TYPE(FakeTextValue);
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------- : TextCtrl
|
// ----------------------------------------------------------------------------- : TextCtrl
|
||||||
|
|
||||||
@@ -31,16 +32,20 @@ class TextCtrl : public DataEditor {
|
|||||||
TextCtrl(Window* parent, int id, bool multi_line, long style = 0);
|
TextCtrl(Window* parent, int id, bool multi_line, long style = 0);
|
||||||
|
|
||||||
/// Set the value that is being edited
|
/// Set the value that is being edited
|
||||||
|
/** value can be a nullptr*/
|
||||||
void setValue(String* value, bool untagged = false);
|
void setValue(String* value, bool untagged = false);
|
||||||
/// Notification that the value has changed outside this control
|
/// Set the value that is being edited
|
||||||
void valueChanged();
|
void setValue(const FakeTextValueP& value);
|
||||||
|
|
||||||
|
/// Update the size, for example after changing the style
|
||||||
|
void updateSize();
|
||||||
|
|
||||||
/// Get access to the field used by the control
|
/// Get access to the field used by the control
|
||||||
TextField& getField();
|
TextField& getField();
|
||||||
|
/// Get access to the field used by the control
|
||||||
|
TextFieldP getFieldP();
|
||||||
/// Get access to the style used by the control
|
/// Get access to the style used by the control
|
||||||
TextStyle& getStyle();
|
TextStyle& getStyle();
|
||||||
/// Update the size, for example after changing the style
|
|
||||||
void updateSize();
|
|
||||||
|
|
||||||
/// Uses a native look
|
/// Uses a native look
|
||||||
virtual bool nativeLook() const { return true; }
|
virtual bool nativeLook() const { return true; }
|
||||||
@@ -49,8 +54,6 @@ class TextCtrl : public DataEditor {
|
|||||||
|
|
||||||
virtual void draw(DC& dc);
|
virtual void draw(DC& dc);
|
||||||
|
|
||||||
/// When an action is received, change the underlying value
|
|
||||||
virtual void onAction(const Action&, bool undone);
|
|
||||||
virtual void onChangeSet();
|
virtual void onChangeSet();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|||||||
@@ -272,6 +272,7 @@ CardP CardsPanel::selectedCard() const {
|
|||||||
return card_list->getCard();
|
return card_list->getCard();
|
||||||
}
|
}
|
||||||
void CardsPanel::selectCard(const CardP& card) {
|
void CardsPanel::selectCard(const CardP& card) {
|
||||||
|
if (!set) return; // we want onChangeSet first
|
||||||
card_list->setCard(card);
|
card_list->setCard(card);
|
||||||
editor->setCard(card);
|
editor->setCard(card);
|
||||||
notes->setValue(card ? &card->notes : nullptr);
|
notes->setValue(card ? &card->notes : nullptr);
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
#include <gui/icon_menu.hpp>
|
#include <gui/icon_menu.hpp>
|
||||||
#include <gui/util.hpp>
|
#include <gui/util.hpp>
|
||||||
#include <data/keyword.hpp>
|
#include <data/keyword.hpp>
|
||||||
|
#include <data/action/value.hpp>
|
||||||
#include <data/action/keyword.hpp>
|
#include <data/action/keyword.hpp>
|
||||||
#include <data/field/text.hpp>
|
#include <data/field/text.hpp>
|
||||||
#include <util/window_id.hpp>
|
#include <util/window_id.hpp>
|
||||||
@@ -29,12 +30,14 @@ KeywordsPanel::KeywordsPanel(Window* parent, int id)
|
|||||||
panel = new Panel(splitter, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 /* no tab traversal*/);
|
panel = new Panel(splitter, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 /* no tab traversal*/);
|
||||||
keyword = new TextCtrl(panel, wxID_ANY, false);
|
keyword = new TextCtrl(panel, wxID_ANY, false);
|
||||||
match = new TextCtrl(panel, wxID_ANY, false);
|
match = new TextCtrl(panel, wxID_ANY, false);
|
||||||
reminder = new TextCtrl(panel, wxID_ANY, false);
|
reminder = new TextCtrl(panel, wxID_ANY, true); // allow multiline for wordwrap
|
||||||
rules = new TextCtrl(panel, wxID_ANY, true);
|
rules = new TextCtrl(panel, wxID_ANY, true);
|
||||||
|
fixed = new wxStaticText(panel, wxID_ANY, _("This is a standard $game keyword, you can not edit it. ")
|
||||||
|
_("If you make a copy of the keyword your copy will take precedent."));
|
||||||
|
errors = new wxStaticText(panel, wxID_ANY, _(""));
|
||||||
// init sizer for panel
|
// init sizer for panel
|
||||||
wxSizer* sp = new wxBoxSizer(wxVERTICAL);
|
sp = new wxBoxSizer(wxVERTICAL);
|
||||||
sp->Add(new wxStaticText(panel, wxID_ANY, _("This is a standard $game keyword, you can not edit it. ")
|
sp->Add(fixed, 0, wxALL, 6);
|
||||||
_("If you make a copy of the keyword your copy will take precedent.")), 0, wxALL, 6);
|
|
||||||
sp->Add(new wxStaticText(panel, wxID_ANY, _("Keyword:")), 0, wxALL, 6);
|
sp->Add(new wxStaticText(panel, wxID_ANY, _("Keyword:")), 0, wxALL, 6);
|
||||||
sp->Add(keyword, 0, wxEXPAND | wxALL & ~wxTOP, 6);
|
sp->Add(keyword, 0, wxEXPAND | wxALL & ~wxTOP, 6);
|
||||||
wxSizer* s2 = new wxStaticBoxSizer(wxVERTICAL, panel, _("Match"));
|
wxSizer* s2 = new wxStaticBoxSizer(wxVERTICAL, panel, _("Match"));
|
||||||
@@ -43,7 +46,8 @@ KeywordsPanel::KeywordsPanel(Window* parent, int id)
|
|||||||
s2->Add(new wxStaticText(panel, wxID_ANY, _("Parameters:")), 0, wxALL, 6);
|
s2->Add(new wxStaticText(panel, wxID_ANY, _("Parameters:")), 0, wxALL, 6);
|
||||||
sp->Add(s2, 0, wxEXPAND | wxALL, 6);
|
sp->Add(s2, 0, wxEXPAND | wxALL, 6);
|
||||||
sp->Add(new wxStaticText(panel, wxID_ANY, _("Reminder:")), 0, wxALL, 6);
|
sp->Add(new wxStaticText(panel, wxID_ANY, _("Reminder:")), 0, wxALL, 6);
|
||||||
sp->Add(reminder, 0, wxEXPAND | wxALL & ~wxTOP, 6);
|
sp->Add(reminder, 1, wxEXPAND | wxALL & ~wxTOP, 6);
|
||||||
|
sp->Add(errors, 0, wxALL & ~wxTOP, 6);
|
||||||
sp->Add(new wxStaticText(panel, wxID_ANY, _("Example:")), 0, wxALL, 6);
|
sp->Add(new wxStaticText(panel, wxID_ANY, _("Example:")), 0, wxALL, 6);
|
||||||
sp->Add(new wxStaticText(panel, wxID_ANY, _("Rules:")), 0, wxALL, 6);
|
sp->Add(new wxStaticText(panel, wxID_ANY, _("Rules:")), 0, wxALL, 6);
|
||||||
sp->Add(rules, 1, wxEXPAND | wxALL & ~wxTOP, 6);
|
sp->Add(rules, 1, wxEXPAND | wxALL & ~wxTOP, 6);
|
||||||
@@ -144,20 +148,43 @@ void KeywordsPanel::onChangeSet() {
|
|||||||
match ->updateSize();
|
match ->updateSize();
|
||||||
reminder->setSet(set);
|
reminder->setSet(set);
|
||||||
reminder->getStyle().padding_bottom = 2;
|
reminder->getStyle().padding_bottom = 2;
|
||||||
|
match ->getStyle().font.size = 10;
|
||||||
|
match ->getStyle().font.font.SetPointSize(10);
|
||||||
reminder->updateSize();
|
reminder->updateSize();
|
||||||
rules ->setSet(set);
|
rules ->setSet(set);
|
||||||
// re-layout
|
// re-layout
|
||||||
panel->Layout();
|
panel->Layout();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void KeywordsPanel::onAction(const Action& action, bool undone) {
|
||||||
|
TYPE_CASE(action, ValueAction) {
|
||||||
|
KeywordReminderTextValue* value = dynamic_cast<KeywordReminderTextValue*>(action.valueP.get());
|
||||||
|
if (value && &value->keyword == list->getKeyword().get()) {
|
||||||
|
// the current keyword's reminder text changed
|
||||||
|
errors->SetLabel(value->errors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void KeywordsPanel::onKeywordSelect(KeywordSelectEvent& ev) {
|
void KeywordsPanel::onKeywordSelect(KeywordSelectEvent& ev) {
|
||||||
if (ev.keyword) {
|
if (ev.keyword) {
|
||||||
panel->Enable(!ev.keyword->fixed);
|
Keyword& kw = *ev.keyword;
|
||||||
keyword->setValue(&ev.keyword->keyword, true);
|
//sp->Show(fixed, kw.fixed);
|
||||||
match ->setValue(&ev.keyword->match);
|
fixed->SetLabel(kw.fixed ? _("This is a standard $game keyword, you can not edit it. ")
|
||||||
rules ->setValue(&ev.keyword->rules);
|
_("If you make a copy of the keyword your copy will take precedent.")
|
||||||
|
: _(""));
|
||||||
|
Layout();
|
||||||
|
keyword ->setValue(new_shared5<KeywordTextValue> (keyword->getFieldP(), &kw, &kw.keyword, !kw.fixed, true));
|
||||||
|
match ->setValue(new_shared4<KeywordTextValue> (match->getFieldP(), &kw, &kw.match, !kw.fixed));
|
||||||
|
rules ->setValue(new_shared4<KeywordTextValue> (rules->getFieldP(), &kw, &kw.rules, !kw.fixed));
|
||||||
|
shared_ptr<KeywordReminderTextValue> reminder_value(new KeywordReminderTextValue(reminder->getFieldP(), &kw, !kw.fixed));
|
||||||
|
reminder->setValue(reminder_value);
|
||||||
|
errors->SetLabel(reminder_value->errors);
|
||||||
} else {
|
} else {
|
||||||
panel->Enable(false);
|
keyword ->setValue(nullptr);
|
||||||
|
match ->setValue(nullptr);
|
||||||
|
rules ->setValue(nullptr);
|
||||||
|
reminder->setValue(nullptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ class KeywordsPanel : public SetWindowPanel {
|
|||||||
~KeywordsPanel();
|
~KeywordsPanel();
|
||||||
|
|
||||||
virtual void onChangeSet();
|
virtual void onChangeSet();
|
||||||
|
virtual void onAction(const Action&, bool);
|
||||||
|
|
||||||
// --------------------------------------------------- : UI
|
// --------------------------------------------------- : UI
|
||||||
|
|
||||||
@@ -41,12 +42,15 @@ class KeywordsPanel : public SetWindowPanel {
|
|||||||
// --------------------------------------------------- : Controls
|
// --------------------------------------------------- : Controls
|
||||||
wxSplitterWindow* splitter;
|
wxSplitterWindow* splitter;
|
||||||
wxPanel* panel;
|
wxPanel* panel;
|
||||||
|
wxSizer* sp;
|
||||||
KeywordList* list;
|
KeywordList* list;
|
||||||
TextCtrl* keyword;
|
TextCtrl* keyword;
|
||||||
TextCtrl* match;
|
TextCtrl* match;
|
||||||
TextCtrl* reminder;
|
TextCtrl* reminder;
|
||||||
TextCtrl* rules;
|
TextCtrl* rules;
|
||||||
IconMenu* menuKeyword;
|
IconMenu* menuKeyword;
|
||||||
|
wxStaticText* fixed;
|
||||||
|
wxStaticText* errors;
|
||||||
|
|
||||||
// --------------------------------------------------- : Events
|
// --------------------------------------------------- : Events
|
||||||
void onKeywordSelect(KeywordSelectEvent& ev);
|
void onKeywordSelect(KeywordSelectEvent& ev);
|
||||||
|
|||||||
+42
-17
@@ -66,17 +66,21 @@ Color param_colors[] =
|
|||||||
, Color(0,170,170)
|
, Color(0,170,170)
|
||||||
, Color(200,0,0)
|
, Color(200,0,0)
|
||||||
};
|
};
|
||||||
|
const size_t param_colors_count = sizeof(param_colors) / sizeof(param_colors[0]);
|
||||||
|
|
||||||
// Helper class for TextElements::fromString, to allow persistent formating state accross recusive calls
|
// Helper class for TextElements::fromString, to allow persistent formating state accross recusive calls
|
||||||
struct TextElementsFromString {
|
struct TextElementsFromString {
|
||||||
// What formatting is enabled?
|
// What formatting is enabled?
|
||||||
int bold, italic, symbol;
|
int bold, italic, symbol;
|
||||||
int soft, kwpph, param, line;
|
int soft, kwpph, param, line;
|
||||||
|
int code, code_kw, code_string, param_ref;
|
||||||
int param_id;
|
int param_id;
|
||||||
bool bracket;
|
bool bracket;
|
||||||
|
|
||||||
TextElementsFromString()
|
TextElementsFromString()
|
||||||
: bold(0), italic(0), symbol(0), soft(0), kwpph(0), param(0), line(0), param_id(0), bracket(false) {}
|
: bold(0), italic(0), symbol(0), soft(0), kwpph(0), param(0), line(0)
|
||||||
|
, code(0), code_kw(0), code_string(0), param_ref(0)
|
||||||
|
, param_id(0), bracket(false) {}
|
||||||
|
|
||||||
// read TextElements from a string
|
// read TextElements from a string
|
||||||
void fromString(TextElements& te, const String& text, size_t start, size_t end, const TextStyle& style, Context& ctx) {
|
void fromString(TextElements& te, const String& text, size_t start, size_t end, const TextStyle& style, Context& ctx) {
|
||||||
@@ -87,20 +91,39 @@ struct TextElementsFromString {
|
|||||||
if (c == _('<')) {
|
if (c == _('<')) {
|
||||||
size_t tag_start = pos;
|
size_t tag_start = pos;
|
||||||
pos = skip_tag(text, tag_start);
|
pos = skip_tag(text, tag_start);
|
||||||
if (is_substr(text, tag_start, _( "<b"))) bold += 1;
|
if (is_substr(text, tag_start, _( "<b"))) bold += 1;
|
||||||
else if (is_substr(text, tag_start, _("</b"))) bold -= 1;
|
else if (is_substr(text, tag_start, _("</b"))) bold -= 1;
|
||||||
else if (is_substr(text, tag_start, _( "<i"))) italic += 1;
|
else if (is_substr(text, tag_start, _( "<i"))) italic += 1;
|
||||||
else if (is_substr(text, tag_start, _("</i"))) italic -= 1;
|
else if (is_substr(text, tag_start, _("</i"))) italic -= 1;
|
||||||
else if (is_substr(text, tag_start, _( "<sym"))) symbol += 1;
|
else if (is_substr(text, tag_start, _( "<sym"))) symbol += 1;
|
||||||
else if (is_substr(text, tag_start, _("</sym"))) symbol -= 1;
|
else if (is_substr(text, tag_start, _("</sym"))) symbol -= 1;
|
||||||
else if (is_substr(text, tag_start, _( "<sep-soft"))) soft += 1;
|
else if (is_substr(text, tag_start, _( "<sep-soft"))) soft += 1;
|
||||||
else if (is_substr(text, tag_start, _("</sep-soft"))) soft -= 1;
|
else if (is_substr(text, tag_start, _("</sep-soft"))) soft -= 1;
|
||||||
else if (is_substr(text, tag_start, _( "<atom-kwpph"))) kwpph += 1;
|
else if (is_substr(text, tag_start, _( "<atom-kwpph"))) kwpph += 1;
|
||||||
else if (is_substr(text, tag_start, _("</atom-kwpph"))) kwpph -= 1;
|
else if (is_substr(text, tag_start, _("</atom-kwpph"))) kwpph -= 1;
|
||||||
else if (is_substr(text, tag_start, _( "<param"))) param += 1;
|
else if (is_substr(text, tag_start, _( "<code-kw"))) code_kw += 1;
|
||||||
else if (is_substr(text, tag_start, _("</param"))) param -= 1;
|
else if (is_substr(text, tag_start, _("</code-kw"))) code_kw -= 1;
|
||||||
else if (is_substr(text, tag_start, _( "<line"))) line += 1;
|
else if (is_substr(text, tag_start, _( "<code-str"))) code_string += 1;
|
||||||
else if (is_substr(text, tag_start, _("</line"))) line -= 1;
|
else if (is_substr(text, tag_start, _("</code-str"))) code_string -= 1;
|
||||||
|
else if (is_substr(text, tag_start, _( "<code"))) code += 1;
|
||||||
|
else if (is_substr(text, tag_start, _("</code"))) code -= 1;
|
||||||
|
else if (is_substr(text, tag_start, _( "<ref-param"))) {
|
||||||
|
// determine the param being referenced
|
||||||
|
// from a tag <param123>
|
||||||
|
if (pos != String::npos) {
|
||||||
|
String ref = text.substr(tag_start + 6, pos - tag_start - 7);
|
||||||
|
long ref_n;
|
||||||
|
if (ref.ToLong(&ref_n)) {
|
||||||
|
param_id = ref_n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
param_ref += 1;
|
||||||
|
}
|
||||||
|
else if (is_substr(text, tag_start, _("</ref-param"))) param_ref -= 1;
|
||||||
|
else if (is_substr(text, tag_start, _( "<param"))) param += 1;
|
||||||
|
else if (is_substr(text, tag_start, _("</param"))) param -= 1;
|
||||||
|
else if (is_substr(text, tag_start, _( "<line"))) line += 1;
|
||||||
|
else if (is_substr(text, tag_start, _("</line"))) line -= 1;
|
||||||
else if (is_substr(text, tag_start, _("<atom"))) {
|
else if (is_substr(text, tag_start, _("<atom"))) {
|
||||||
// 'atomic' indicator
|
// 'atomic' indicator
|
||||||
size_t end = match_close_tag(text, tag_start);
|
size_t end = match_close_tag(text, tag_start);
|
||||||
@@ -129,8 +152,10 @@ struct TextElementsFromString {
|
|||||||
e = new SymbolTextElement(text, pos, pos + 1, style.symbol_font, &ctx);
|
e = new SymbolTextElement(text, pos, pos + 1, style.symbol_font, &ctx);
|
||||||
bracket = false;
|
bracket = false;
|
||||||
} else {
|
} else {
|
||||||
FontP font = style.font.make(bold > 0, italic > 0, soft > 0 || kwpph > 0,
|
FontP font = style.font.make(bold > 0, italic > 0, soft > 0 || kwpph > 0, code > 0,
|
||||||
param > 0 ? ¶m_colors[(param_id++) % (sizeof(param_colors)/sizeof(param_colors[0]))] : nullptr);
|
param > 0 || param_ref > 0
|
||||||
|
? ¶m_colors[(param_id++) % param_colors_count]
|
||||||
|
: nullptr);
|
||||||
bracket = kwpph > 0 || param > 0;
|
bracket = kwpph > 0 || param > 0;
|
||||||
e = new FontTextElement(
|
e = new FontTextElement(
|
||||||
text,
|
text,
|
||||||
|
|||||||
@@ -76,7 +76,11 @@ class OptionalScript {
|
|||||||
void initDependencies(Context&, const Dependency& dep) const;
|
void initDependencies(Context&, const Dependency& dep) const;
|
||||||
|
|
||||||
/// Get access to the script, be careful
|
/// Get access to the script, be careful
|
||||||
Script& getScript();
|
Script& getScript();
|
||||||
|
inline ScriptP& getScriptP() { return script; }
|
||||||
|
/// Get access to the unparsed value
|
||||||
|
inline String& getUnparsed() { return unparsed; }
|
||||||
|
inline const String& getUnparsed() const { return unparsed; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
ScriptP script; ///< The script, may be null if there is no script
|
ScriptP script; ///< The script, may be null if there is no script
|
||||||
|
|||||||
Reference in New Issue
Block a user