mirror of
https://github.com/amyinspace/MagicSetEditor2.git
synced 2026-06-12 13:37:00 -04:00
Reader now warns about invalid UTF-8 files;
Fixed possible hang when reading multiline strings with incorrect indentation; Warnings from reading are shown also in NewSetWindow; Script parse errors get reported with the correct line number; Added support for showing multiple choice fields as a single image; Added 'line_below' to ChoiceField::Choice, for putting a line below menu items. git-svn-id: svn://svn.code.sf.net/p/magicseteditor/code/trunk@420 0fc631ac-6414-0410-93d0-97cfa31319b6
This commit is contained in:
@@ -49,9 +49,11 @@ IMPLEMENT_REFLECTION(ChoiceField) {
|
|||||||
|
|
||||||
ChoiceField::Choice::Choice()
|
ChoiceField::Choice::Choice()
|
||||||
: first_id(0)
|
: first_id(0)
|
||||||
|
, line_below(false), enabled(true)
|
||||||
{}
|
{}
|
||||||
ChoiceField::Choice::Choice(const String& name)
|
ChoiceField::Choice::Choice(const String& name)
|
||||||
: name(name), first_id(0)
|
: name(name), first_id(0)
|
||||||
|
, line_below(false), enabled(true)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
|
||||||
@@ -138,11 +140,13 @@ String ChoiceField::Choice::choiceNameNice(int id) const {
|
|||||||
|
|
||||||
|
|
||||||
IMPLEMENT_REFLECTION_NO_GET_MEMBER(ChoiceField::Choice) {
|
IMPLEMENT_REFLECTION_NO_GET_MEMBER(ChoiceField::Choice) {
|
||||||
if (isGroup() || (tag.reading() && tag.isComplex())) {
|
if (isGroup() || line_below || enabled.isScripted() || (tag.reading() && tag.isComplex())) {
|
||||||
// complex values are groups
|
// complex values are groups
|
||||||
REFLECT(name);
|
REFLECT(name);
|
||||||
REFLECT_N("group_choice", default_name);
|
REFLECT_N("group_choice", default_name);
|
||||||
REFLECT(choices);
|
REFLECT(choices);
|
||||||
|
REFLECT(line_below);
|
||||||
|
REFLECT(enabled);
|
||||||
} else {
|
} else {
|
||||||
REFLECT_NAMELESS(name);
|
REFLECT_NAMELESS(name);
|
||||||
}
|
}
|
||||||
@@ -237,6 +241,9 @@ IMPLEMENT_REFLECTION_ENUM(ChoiceRenderStyle) {
|
|||||||
VALUE_N("checklist", RENDER_TEXT_CHECKLIST);
|
VALUE_N("checklist", RENDER_TEXT_CHECKLIST);
|
||||||
VALUE_N("image checklist", RENDER_IMAGE_CHECKLIST);
|
VALUE_N("image checklist", RENDER_IMAGE_CHECKLIST);
|
||||||
VALUE_N("both checklist", RENDER_BOTH_CHECKLIST);
|
VALUE_N("both checklist", RENDER_BOTH_CHECKLIST);
|
||||||
|
VALUE_N("text list", RENDER_IMAGE_LIST);
|
||||||
|
VALUE_N("image list", RENDER_IMAGE_LIST);
|
||||||
|
VALUE_N("both list", RENDER_IMAGE_LIST);
|
||||||
}
|
}
|
||||||
|
|
||||||
IMPLEMENT_REFLECTION(ChoiceStyle) {
|
IMPLEMENT_REFLECTION(ChoiceStyle) {
|
||||||
|
|||||||
@@ -52,9 +52,11 @@ class ChoiceField::Choice : public IntrusivePtrBase<ChoiceField::Choice> {
|
|||||||
Choice();
|
Choice();
|
||||||
Choice(const String& name);
|
Choice(const String& name);
|
||||||
|
|
||||||
String name; ///< Name/value of the item
|
String name; ///< Name/value of the item
|
||||||
String default_name; ///< A default item, if this is a group and default_name.empty() there is no default
|
String default_name; ///< A default item, if this is a group and default_name.empty() there is no default
|
||||||
vector<ChoiceP> choices; ///< Choices and sub groups in this group
|
vector<ChoiceP> choices; ///< Choices and sub groups in this group
|
||||||
|
bool line_below; ///< Show a line after this item?
|
||||||
|
Scriptable<bool> enabled; ///< Is this item enabled?
|
||||||
/// First item-id in this group (can be the default item)
|
/// First item-id in this group (can be the default item)
|
||||||
/** Item-ids are consecutive integers, a group uses all ids [first_id..lastId()).
|
/** Item-ids are consecutive integers, a group uses all ids [first_id..lastId()).
|
||||||
* The top level group has first_id 0.
|
* The top level group has first_id 0.
|
||||||
@@ -109,11 +111,15 @@ enum ChoiceRenderStyle
|
|||||||
, RENDER_IMAGE = 0x10 // render an image
|
, RENDER_IMAGE = 0x10 // render an image
|
||||||
, RENDER_HIDDEN = 0x20 // don't render anything, only have a menu
|
, RENDER_HIDDEN = 0x20 // don't render anything, only have a menu
|
||||||
, RENDER_CHECKLIST = 0x100 // render as a checklist, intended for multiple choice
|
, RENDER_CHECKLIST = 0x100 // render as a checklist, intended for multiple choice
|
||||||
|
, RENDER_LIST = 0x200 // render as a list of images/text, intended for multiple choice
|
||||||
, RENDER_BOTH = RENDER_TEXT | RENDER_IMAGE
|
, RENDER_BOTH = RENDER_TEXT | RENDER_IMAGE
|
||||||
, RENDER_HIDDEN_IMAGE = RENDER_HIDDEN | RENDER_IMAGE
|
, RENDER_HIDDEN_IMAGE = RENDER_HIDDEN | RENDER_IMAGE
|
||||||
, RENDER_TEXT_CHECKLIST = RENDER_CHECKLIST | RENDER_TEXT
|
, RENDER_TEXT_CHECKLIST = RENDER_CHECKLIST | RENDER_TEXT
|
||||||
, RENDER_IMAGE_CHECKLIST = RENDER_CHECKLIST | RENDER_IMAGE
|
, RENDER_IMAGE_CHECKLIST = RENDER_CHECKLIST | RENDER_IMAGE
|
||||||
, RENDER_BOTH_CHECKLIST = RENDER_CHECKLIST | RENDER_BOTH
|
, RENDER_BOTH_CHECKLIST = RENDER_CHECKLIST | RENDER_BOTH
|
||||||
|
, RENDER_TEXT_LIST = RENDER_LIST | RENDER_TEXT
|
||||||
|
, RENDER_IMAGE_LIST = RENDER_LIST | RENDER_IMAGE
|
||||||
|
, RENDER_BOTH_LIST = RENDER_LIST | RENDER_BOTH
|
||||||
};
|
};
|
||||||
|
|
||||||
enum ThumbnailStatus
|
enum ThumbnailStatus
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ IMPLEMENT_REFLECTION(MultipleChoiceField) {
|
|||||||
REFLECT_BASE(ChoiceField);
|
REFLECT_BASE(ChoiceField);
|
||||||
REFLECT(minimum_selection);
|
REFLECT(minimum_selection);
|
||||||
REFLECT(maximum_selection);
|
REFLECT(maximum_selection);
|
||||||
|
REFLECT(empty_choice);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------- : MultipleChoiceStyle
|
// ----------------------------------------------------------------------------- : MultipleChoiceStyle
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ class MultipleChoiceField : public ChoiceField {
|
|||||||
DECLARE_FIELD_TYPE(MultipleChoiceField);
|
DECLARE_FIELD_TYPE(MultipleChoiceField);
|
||||||
|
|
||||||
UInt minimum_selection, maximum_selection; ///< How many choices can be selected simultaniously?
|
UInt minimum_selection, maximum_selection; ///< How many choices can be selected simultaniously?
|
||||||
|
String empty_choice; ///< Name to use when nothing is selected
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DECLARE_REFLECTION();
|
DECLARE_REFLECTION();
|
||||||
@@ -56,7 +57,7 @@ class MultipleChoiceValue : public ChoiceValue {
|
|||||||
inline MultipleChoiceValue(const MultipleChoiceFieldP& field) : ChoiceValue(field, true) {}
|
inline MultipleChoiceValue(const MultipleChoiceFieldP& field) : ChoiceValue(field, true) {}
|
||||||
DECLARE_HAS_FIELD(MultipleChoice);
|
DECLARE_HAS_FIELD(MultipleChoice);
|
||||||
|
|
||||||
// no extra data
|
String last_choice; ///< Which of the choices was selected/deselected last?
|
||||||
|
|
||||||
/// Splits the value, stores the selected choices in the out parameter
|
/// Splits the value, stores the selected choices in the out parameter
|
||||||
void get(vector<String>& out) const;
|
void get(vector<String>& out) const;
|
||||||
@@ -65,6 +66,10 @@ class MultipleChoiceValue : public ChoiceValue {
|
|||||||
DECLARE_REFLECTION();
|
DECLARE_REFLECTION();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Utilities
|
||||||
|
|
||||||
|
/// Is the given choice selected in the value?
|
||||||
|
bool chosen(const String& multiple_choice_value, const String& chioce);
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------- : EOF
|
// ----------------------------------------------------------------------------- : EOF
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ DropDownList::~DropDownList() {
|
|||||||
|
|
||||||
void DropDownList::show(bool in_place, wxPoint pos) {
|
void DropDownList::show(bool in_place, wxPoint pos) {
|
||||||
if (IsShown()) return;
|
if (IsShown()) return;
|
||||||
|
onShow();
|
||||||
// find selection
|
// find selection
|
||||||
selected_item = selection();
|
selected_item = selection();
|
||||||
// width
|
// width
|
||||||
@@ -248,6 +249,9 @@ void DropDownList::drawItem(DC& dc, int y, size_t item) {
|
|||||||
dc.SetBrush (wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT));
|
dc.SetBrush (wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT));
|
||||||
dc.SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
|
dc.SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
|
||||||
dc.DrawRectangle(marginW, y, (int)item_size.width, (int)item_size.height);
|
dc.DrawRectangle(marginW, y, (int)item_size.width, (int)item_size.height);
|
||||||
|
} else if (!itemEnabled(item)) {
|
||||||
|
// mix between foreground and background
|
||||||
|
dc.SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT));
|
||||||
} else if (highlightItem(item)) {
|
} else if (highlightItem(item)) {
|
||||||
// mix a color between selection and window
|
// mix a color between selection and window
|
||||||
dc.SetBrush (lerp(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT),
|
dc.SetBrush (lerp(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT),
|
||||||
@@ -266,7 +270,7 @@ void DropDownList::drawItem(DC& dc, int y, size_t item) {
|
|||||||
}
|
}
|
||||||
// draw line below
|
// draw line below
|
||||||
if (lineBelow(item)) {
|
if (lineBelow(item)) {
|
||||||
dc.SetPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW));
|
dc.SetPen(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
|
||||||
dc.DrawLine(marginW, y + (int)item_size.height, marginW + (int)item_size.width, y + (int)item_size.height);
|
dc.DrawLine(marginW, y + (int)item_size.height, marginW + (int)item_size.width, y + (int)item_size.height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -295,8 +299,10 @@ void DropDownList::onMotion(wxMouseEvent& ev) {
|
|||||||
for (size_t i = 0 ; i < count ; ++i) {
|
for (size_t i = 0 ; i < count ; ++i) {
|
||||||
int endY = startY + (int)item_size.height;
|
int endY = startY + (int)item_size.height;
|
||||||
if (ev.GetY() >= startY && ev.GetY() < endY) {
|
if (ev.GetY() >= startY && ev.GetY() < endY) {
|
||||||
selected_item = i;
|
if (itemEnabled(i)) {
|
||||||
showSubMenu(i, startY);
|
selected_item = i;
|
||||||
|
showSubMenu(i, startY);
|
||||||
|
}
|
||||||
Refresh(false);
|
Refresh(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -321,18 +327,27 @@ bool DropDownList::onCharInParent(wxKeyEvent& ev) {
|
|||||||
// sub menu always takes keys
|
// sub menu always takes keys
|
||||||
return open_sub_menu->onCharInParent(ev);
|
return open_sub_menu->onCharInParent(ev);
|
||||||
} else {
|
} else {
|
||||||
|
size_t old_sel = selected_item;
|
||||||
switch (k) {
|
switch (k) {
|
||||||
case WXK_UP:
|
case WXK_UP:
|
||||||
if (selected_item > 0) {
|
while (selected_item > 0) {
|
||||||
selected_item -= 1;
|
selected_item -= 1;
|
||||||
Refresh(false);
|
if (itemEnabled(selected_item)) {
|
||||||
|
Refresh(false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
selected_item = old_sel;
|
||||||
break;
|
break;
|
||||||
case WXK_DOWN:
|
case WXK_DOWN:
|
||||||
if (selected_item + 1 < itemCount()) {
|
while (selected_item + 1 < itemCount()) {
|
||||||
selected_item += 1;
|
selected_item += 1;
|
||||||
Refresh(false);
|
if (itemEnabled(selected_item)) {
|
||||||
|
Refresh(false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
selected_item = old_sel;
|
||||||
break;
|
break;
|
||||||
case WXK_RETURN:
|
case WXK_RETURN:
|
||||||
if (!showSubMenu()) {
|
if (!showSubMenu()) {
|
||||||
@@ -351,6 +366,7 @@ bool DropDownList::onCharInParent(wxKeyEvent& ev) {
|
|||||||
size_t count = itemCount();
|
size_t count = itemCount();
|
||||||
for (size_t i = 0 ; i < count ; ++i) {
|
for (size_t i = 0 ; i < count ; ++i) {
|
||||||
size_t index = (si + i) % count;
|
size_t index = (si + i) % count;
|
||||||
|
if (!itemEnabled(index)) continue;
|
||||||
String c = itemText(index);
|
String c = itemText(index);
|
||||||
#ifdef UNICODE
|
#ifdef UNICODE
|
||||||
if (!c.empty() && toUpper(c.GetChar(0)) == toUpper(ev.GetUnicodeKey())) {
|
if (!c.empty() && toUpper(c.GetChar(0)) == toUpper(ev.GetUnicodeKey())) {
|
||||||
|
|||||||
@@ -40,6 +40,10 @@ class DropDownList : public wxPopupWindow {
|
|||||||
bool onMouseInParent(wxMouseEvent&, bool open_in_place);
|
bool onMouseInParent(wxMouseEvent&, bool open_in_place);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
|
/// Prepare for showing the list
|
||||||
|
virtual void onShow() {}
|
||||||
|
|
||||||
// --------------------------------------------------- : Selection
|
// --------------------------------------------------- : Selection
|
||||||
static const size_t NO_SELECTION = (size_t)-1;
|
static const size_t NO_SELECTION = (size_t)-1;
|
||||||
|
|
||||||
@@ -59,6 +63,8 @@ class DropDownList : public wxPopupWindow {
|
|||||||
virtual bool lineBelow(size_t item) const { return false; }
|
virtual bool lineBelow(size_t item) const { return false; }
|
||||||
/// Should the item be highlighted?
|
/// Should the item be highlighted?
|
||||||
virtual bool highlightItem(size_t item) const { return false; }
|
virtual bool highlightItem(size_t item) const { return false; }
|
||||||
|
/// Is the item enabled?
|
||||||
|
virtual bool itemEnabled(size_t item) const { return true; }
|
||||||
// An extra submenu that pops up from an item, or null if there is no popup menu
|
// An extra submenu that pops up from an item, or null if there is no popup menu
|
||||||
virtual DropDownList* submenu(size_t item) const { return nullptr; }
|
virtual DropDownList* submenu(size_t item) const { return nullptr; }
|
||||||
|
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ NewSetWindow::NewSetWindow(Window* parent)
|
|||||||
void NewSetWindow::onGameSelect(wxCommandEvent&) {
|
void NewSetWindow::onGameSelect(wxCommandEvent&) {
|
||||||
wxBusyCursor wait;
|
wxBusyCursor wait;
|
||||||
GameP game = game_list->getSelection<Game>();
|
GameP game = game_list->getSelection<Game>();
|
||||||
|
handle_pending_errors();
|
||||||
settings.default_game = game->name();
|
settings.default_game = game->name();
|
||||||
GameSettings& gs = settings.gameSettingsFor(*game);
|
GameSettings& gs = settings.gameSettingsFor(*game);
|
||||||
stylesheet_list->showData<StyleSheet>(game->name() + _("-*"));
|
stylesheet_list->showData<StyleSheet>(game->name() + _("-*"));
|
||||||
@@ -78,6 +79,7 @@ void NewSetWindow::onStyleSheetSelect(wxCommandEvent&) {
|
|||||||
// store this as default selection
|
// store this as default selection
|
||||||
GameP game = game_list ->getSelection<Game>();
|
GameP game = game_list ->getSelection<Game>();
|
||||||
StyleSheetP stylesheet = stylesheet_list->getSelection<StyleSheet>();
|
StyleSheetP stylesheet = stylesheet_list->getSelection<StyleSheet>();
|
||||||
|
handle_pending_errors();
|
||||||
GameSettings& gs = settings.gameSettingsFor(*game);
|
GameSettings& gs = settings.gameSettingsFor(*game);
|
||||||
gs.default_stylesheet = stylesheet->name();
|
gs.default_stylesheet = stylesheet->name();
|
||||||
UpdateWindowUI(wxUPDATE_UI_RECURSE);
|
UpdateWindowUI(wxUPDATE_UI_RECURSE);
|
||||||
@@ -113,10 +115,16 @@ void NewSetWindow::onUpdateUI(wxUpdateUIEvent& ev) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NewSetWindow::onIdle(wxIdleEvent& ev) {
|
||||||
|
// Stuff that must be done in the main thread
|
||||||
|
handle_pending_errors();
|
||||||
|
}
|
||||||
|
|
||||||
BEGIN_EVENT_TABLE(NewSetWindow, wxDialog)
|
BEGIN_EVENT_TABLE(NewSetWindow, wxDialog)
|
||||||
EVT_GALLERY_SELECT (ID_GAME_LIST, NewSetWindow::onGameSelect)
|
EVT_GALLERY_SELECT (ID_GAME_LIST, NewSetWindow::onGameSelect)
|
||||||
EVT_GALLERY_SELECT (ID_STYLESHEET_LIST, NewSetWindow::onStyleSheetSelect)
|
EVT_GALLERY_SELECT (ID_STYLESHEET_LIST, NewSetWindow::onStyleSheetSelect)
|
||||||
EVT_GALLERY_ACTIVATE(ID_STYLESHEET_LIST, NewSetWindow::onStyleSheetActivate)
|
EVT_GALLERY_ACTIVATE(ID_STYLESHEET_LIST, NewSetWindow::onStyleSheetActivate)
|
||||||
EVT_BUTTON (wxID_OK, NewSetWindow::OnOK)
|
EVT_BUTTON (wxID_OK, NewSetWindow::OnOK)
|
||||||
EVT_UPDATE_UI (wxID_ANY, NewSetWindow::onUpdateUI)
|
EVT_UPDATE_UI (wxID_ANY, NewSetWindow::onUpdateUI)
|
||||||
|
EVT_IDLE ( NewSetWindow::onIdle)
|
||||||
END_EVENT_TABLE ()
|
END_EVENT_TABLE ()
|
||||||
|
|||||||
@@ -42,10 +42,11 @@ class NewSetWindow : public wxDialog {
|
|||||||
|
|
||||||
void onStyleSheetSelect (wxCommandEvent&);
|
void onStyleSheetSelect (wxCommandEvent&);
|
||||||
void onStyleSheetActivate(wxCommandEvent&);
|
void onStyleSheetActivate(wxCommandEvent&);
|
||||||
|
|
||||||
virtual void OnOK(wxCommandEvent&);
|
virtual void OnOK(wxCommandEvent&);
|
||||||
|
|
||||||
void onUpdateUI(wxUpdateUIEvent&);
|
void onUpdateUI(wxUpdateUIEvent&);
|
||||||
|
void onIdle(wxIdleEvent&);
|
||||||
|
|
||||||
// we are done, close the window
|
// we are done, close the window
|
||||||
void done();
|
void done();
|
||||||
|
|||||||
+2
-2
@@ -202,7 +202,7 @@ void draw_drop_down_arrow(Window* win, DC& dc, const wxRect& rect, bool active)
|
|||||||
, active ? wxCONTROL_PRESSED : 0);
|
, active ? wxCONTROL_PRESSED : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void draw_checkbox(Window* win, DC& dc, const wxRect& rect, bool checked) {
|
void draw_checkbox(Window* win, DC& dc, const wxRect& rect, bool checked, bool enabled) {
|
||||||
#if wxUSE_UXTHEME && defined(__WXMSW__)
|
#if wxUSE_UXTHEME && defined(__WXMSW__)
|
||||||
// TODO: Windows version?
|
// TODO: Windows version?
|
||||||
#endif
|
#endif
|
||||||
@@ -210,7 +210,7 @@ void draw_checkbox(Window* win, DC& dc, const wxRect& rect, bool checked) {
|
|||||||
if (checked) {
|
if (checked) {
|
||||||
dc.DrawCheckMark(wxRect(rect.x-1,rect.y-1,rect.width+2,rect.height+2));
|
dc.DrawCheckMark(wxRect(rect.x-1,rect.y-1,rect.width+2,rect.height+2));
|
||||||
}
|
}
|
||||||
dc.SetPen(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
|
dc.SetPen(wxSystemSettings::GetColour(enabled ? wxSYS_COLOUR_WINDOWTEXT: wxSYS_COLOUR_GRAYTEXT));
|
||||||
dc.SetBrush(*wxTRANSPARENT_BRUSH);
|
dc.SetBrush(*wxTRANSPARENT_BRUSH);
|
||||||
dc.DrawRectangle(rect.x, rect.y, rect.width, rect.height);
|
dc.DrawRectangle(rect.x, rect.y, rect.width, rect.height);
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -61,7 +61,7 @@ void draw_menu_arrow(Window* win, DC& dc, const wxRect& rect, bool active);
|
|||||||
void draw_drop_down_arrow(Window* win, DC& dc, const wxRect& rect, bool active);
|
void draw_drop_down_arrow(Window* win, DC& dc, const wxRect& rect, bool active);
|
||||||
|
|
||||||
/// Draws a check box
|
/// Draws a check box
|
||||||
void draw_checkbox(Window* win, DC& dc, const wxRect& rect, bool checked);
|
void draw_checkbox(Window* win, DC& dc, const wxRect& rect, bool checked, bool enabled = true);
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------- : EOF
|
// ----------------------------------------------------------------------------- : EOF
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -106,6 +106,14 @@ DropDownChoiceListBase::DropDownChoiceListBase
|
|||||||
item_size.height = max(16., item_size.height);
|
item_size.height = max(16., item_size.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DropDownChoiceListBase::onShow() {
|
||||||
|
// update 'enabled'
|
||||||
|
Context& ctx = cve.viewer.getContext();
|
||||||
|
FOR_EACH(c, group->choices) {
|
||||||
|
c->enabled.update(ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
size_t DropDownChoiceListBase::itemCount() const {
|
size_t DropDownChoiceListBase::itemCount() const {
|
||||||
return group->choices.size() + hasDefault();
|
return group->choices.size() + hasDefault();
|
||||||
}
|
}
|
||||||
@@ -129,7 +137,10 @@ String DropDownChoiceListBase::itemText(size_t item) const {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
bool DropDownChoiceListBase::lineBelow(size_t item) const {
|
bool DropDownChoiceListBase::lineBelow(size_t item) const {
|
||||||
return isDefault(item);
|
return isDefault(item) || getChoice(item)->line_below;
|
||||||
|
}
|
||||||
|
bool DropDownChoiceListBase::itemEnabled(size_t item) const {
|
||||||
|
return isDefault(item) || getChoice(item)->enabled;
|
||||||
}
|
}
|
||||||
DropDownList* DropDownChoiceListBase::submenu(size_t item) const {
|
DropDownList* DropDownChoiceListBase::submenu(size_t item) const {
|
||||||
if (isDefault(item)) return nullptr;
|
if (isDefault(item)) return nullptr;
|
||||||
@@ -157,7 +168,7 @@ void DropDownChoiceListBase::drawIcon(DC& dc, int x, int y, size_t item, bool se
|
|||||||
}
|
}
|
||||||
// draw image
|
// draw image
|
||||||
if (image_id < il->GetImageCount()) {
|
if (image_id < il->GetImageCount()) {
|
||||||
il->Draw(image_id, dc, x, y);
|
il->Draw(image_id, dc, x, y, itemEnabled(item) ? wxIMAGELIST_DRAW_NORMAL : wxIMAGELIST_DRAW_TRANSPARENT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,6 +207,12 @@ DropDownChoiceList::DropDownChoiceList(Window* parent, bool is_submenu, ValueVie
|
|||||||
: DropDownChoiceListBase(parent, is_submenu, cve, group)
|
: DropDownChoiceListBase(parent, is_submenu, cve, group)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
void DropDownChoiceList::onShow() {
|
||||||
|
DropDownChoiceListBase::onShow();
|
||||||
|
// we need thumbnail images soon
|
||||||
|
generateThumbnailImages();
|
||||||
|
}
|
||||||
|
|
||||||
void DropDownChoiceList::select(size_t item) {
|
void DropDownChoiceList::select(size_t item) {
|
||||||
if (isFieldDefault(item)) {
|
if (isFieldDefault(item)) {
|
||||||
dynamic_cast<ChoiceValueEditor&>(cve).change( Defaultable<String>() );
|
dynamic_cast<ChoiceValueEditor&>(cve).change( Defaultable<String>() );
|
||||||
@@ -206,8 +223,6 @@ void DropDownChoiceList::select(size_t item) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t DropDownChoiceList::selection() const {
|
size_t DropDownChoiceList::selection() const {
|
||||||
// we need thumbnail images soon
|
|
||||||
const_cast<DropDownChoiceList*>(this)->generateThumbnailImages();
|
|
||||||
// selected item
|
// selected item
|
||||||
const Defaultable<String>& value = dynamic_cast<ChoiceValueEditor&>(cve).value().value();
|
const Defaultable<String>& value = dynamic_cast<ChoiceValueEditor&>(cve).value().value();
|
||||||
int id = field().choices->choiceId(value);
|
int id = field().choices->choiceId(value);
|
||||||
|
|||||||
@@ -48,9 +48,11 @@ class DropDownChoiceListBase : public DropDownList {
|
|||||||
public:
|
public:
|
||||||
DropDownChoiceListBase(Window* parent, bool is_submenu, ValueViewer& cve, ChoiceField::ChoiceP group);
|
DropDownChoiceListBase(Window* parent, bool is_submenu, ValueViewer& cve, ChoiceField::ChoiceP group);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
virtual void onShow();
|
||||||
virtual size_t itemCount() const;
|
virtual size_t itemCount() const;
|
||||||
virtual bool lineBelow(size_t item) const;
|
virtual bool lineBelow(size_t item) const;
|
||||||
|
virtual bool itemEnabled(size_t item) const;
|
||||||
virtual String itemText(size_t item) const;
|
virtual String itemText(size_t item) const;
|
||||||
virtual void drawIcon(DC& dc, int x, int y, size_t item, bool selected) const;
|
virtual void drawIcon(DC& dc, int x, int y, size_t item, bool selected) const;
|
||||||
virtual DropDownList* submenu(size_t item) const;
|
virtual DropDownList* submenu(size_t item) const;
|
||||||
@@ -92,6 +94,7 @@ class DropDownChoiceList : public DropDownChoiceListBase {
|
|||||||
DropDownChoiceList(Window* parent, bool is_submenu, ValueViewer& cve, ChoiceField::ChoiceP group);
|
DropDownChoiceList(Window* parent, bool is_submenu, ValueViewer& cve, ChoiceField::ChoiceP group);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
virtual void onShow();
|
||||||
virtual void select(size_t item);
|
virtual void select(size_t item);
|
||||||
virtual size_t selection() const;
|
virtual size_t selection() const;
|
||||||
virtual DropDownList* createSubMenu(ChoiceField::ChoiceP group) const;
|
virtual DropDownList* createSubMenu(ChoiceField::ChoiceP group) const;
|
||||||
|
|||||||
@@ -33,27 +33,31 @@ DropDownMultipleChoiceList::DropDownMultipleChoiceList
|
|||||||
}
|
}
|
||||||
|
|
||||||
void DropDownMultipleChoiceList::select(size_t item) {
|
void DropDownMultipleChoiceList::select(size_t item) {
|
||||||
|
MultipleChoiceValueEditor& mcve = dynamic_cast<MultipleChoiceValueEditor&>(cve);
|
||||||
if (isFieldDefault(item)) {
|
if (isFieldDefault(item)) {
|
||||||
// should not happen
|
// make default
|
||||||
|
mcve.getSet().actions.add(value_action(mcve.valueP(), Defaultable<String>()));
|
||||||
} else {
|
} else {
|
||||||
ChoiceField::ChoiceP choice = getChoice(item);
|
ChoiceField::ChoiceP choice = getChoice(item);
|
||||||
dynamic_cast<MultipleChoiceValueEditor&>(cve).toggle(choice->first_id);
|
mcve.toggle(choice->first_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DropDownMultipleChoiceList::drawIcon(DC& dc, int x, int y, size_t item, bool selected) const {
|
void DropDownMultipleChoiceList::drawIcon(DC& dc, int x, int y, size_t item, bool selected) const {
|
||||||
// is this item active?
|
// is this item active/checked?
|
||||||
bool active = false;
|
bool active = false;
|
||||||
if (!isFieldDefault(item)) {
|
if (!isFieldDefault(item)) {
|
||||||
ChoiceField::ChoiceP choice = getChoice(item);
|
ChoiceField::ChoiceP choice = getChoice(item);
|
||||||
active = dynamic_cast<MultipleChoiceValueEditor&>(cve).active[choice->first_id];
|
active = dynamic_cast<MultipleChoiceValueEditor&>(cve).active[choice->first_id];
|
||||||
|
} else {
|
||||||
|
active = dynamic_cast<MultipleChoiceValueEditor&>(cve).value().value.isDefault();
|
||||||
}
|
}
|
||||||
// draw checkbox
|
// draw checkbox
|
||||||
dc.SetPen(*wxTRANSPARENT_PEN);
|
dc.SetPen(*wxTRANSPARENT_PEN);
|
||||||
dc.SetBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
|
dc.SetBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
|
||||||
dc.DrawRectangle(x,y,16,16);
|
dc.DrawRectangle(x,y,16,16);
|
||||||
wxRect rect = RealRect(x+2,y+2,12,12);
|
wxRect rect = RealRect(x+2,y+2,12,12);
|
||||||
draw_checkbox(nullptr, dc, rect, active);
|
draw_checkbox(nullptr, dc, rect, active, itemEnabled(item));
|
||||||
// draw icon
|
// draw icon
|
||||||
DropDownChoiceListBase::drawIcon(dc, x + 16, y, item, selected);
|
DropDownChoiceListBase::drawIcon(dc, x + 16, y, item, selected);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,11 +32,54 @@ void MultipleChoiceValueViewer::draw(RotatedDC& dc) {
|
|||||||
if (active) select_it++;
|
if (active) select_it++;
|
||||||
drawChoice(dc, pos, choice, active);
|
drawChoice(dc, pos, choice, active);
|
||||||
}
|
}
|
||||||
} else {
|
} else if (style().render_style & RENDER_LIST) {
|
||||||
// render only selected choices
|
// render only selected choices
|
||||||
FOR_EACH(choice, selected) {
|
FOR_EACH(choice, selected) {
|
||||||
drawChoice(dc, pos, choice);
|
drawChoice(dc, pos, choice);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// COPY FROM ChoiceValueViewer
|
||||||
|
if (value().value().empty()) return;
|
||||||
|
double margin = 0;
|
||||||
|
if (style().render_style & RENDER_IMAGE) {
|
||||||
|
// draw image
|
||||||
|
map<String,ScriptableImage>::iterator it = style().choice_images.find(cannocial_name_form(value().value()));
|
||||||
|
if (it != style().choice_images.end() && it->second.isReady()) {
|
||||||
|
ScriptableImage& img = it->second;
|
||||||
|
GeneratedImage::Options img_options(0,0, viewer.stylesheet.get(), &getSet());
|
||||||
|
if (nativeLook()) {
|
||||||
|
img_options.width = img_options.height = 16;
|
||||||
|
img_options.preserve_aspect = ASPECT_BORDER;
|
||||||
|
} else if(style().render_style & RENDER_TEXT) {
|
||||||
|
// also drawing text, use original size
|
||||||
|
} else {
|
||||||
|
img_options.width = (int) dc.trS(style().width);
|
||||||
|
img_options.height = (int) dc.trS(style().height);
|
||||||
|
img_options.preserve_aspect = style().alignment == ALIGN_STRETCH ? ASPECT_STRETCH : ASPECT_FIT;
|
||||||
|
}
|
||||||
|
Image image = img.generate(img_options, true);
|
||||||
|
ImageCombine combine = img.combine();
|
||||||
|
// apply mask?
|
||||||
|
style().loadMask(*viewer.stylesheet);
|
||||||
|
if (style().mask.Ok()) {
|
||||||
|
set_alpha(image, style().mask);
|
||||||
|
}
|
||||||
|
// draw
|
||||||
|
dc.DrawImage(image,
|
||||||
|
align_in_rect(style().alignment, RealSize(image.GetWidth(), image.GetHeight()), style().getRect()),
|
||||||
|
combine == COMBINE_NORMAL ? style().combine : combine,
|
||||||
|
style().angle
|
||||||
|
);
|
||||||
|
margin = dc.trInvS(image.GetWidth()) + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (style().render_style & RENDER_TEXT) {
|
||||||
|
// draw text
|
||||||
|
dc.DrawText(tr(*viewer.stylesheet, value().value(), capitalize(value().value())),
|
||||||
|
align_in_rect(ALIGN_MIDDLE_LEFT, RealSize(0, dc.GetCharHeight()), style().getRect()) + RealSize(margin, 0)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// COPY ENDS HERE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
#include <data/game.hpp>
|
#include <data/game.hpp>
|
||||||
#include <data/field/text.hpp>
|
#include <data/field/text.hpp>
|
||||||
#include <data/field/choice.hpp>
|
#include <data/field/choice.hpp>
|
||||||
|
#include <data/field/multiple_choice.hpp>
|
||||||
|
|
||||||
DECLARE_TYPEOF_COLLECTION(FieldP);
|
DECLARE_TYPEOF_COLLECTION(FieldP);
|
||||||
DECLARE_TYPEOF_COLLECTION(TextValue*);
|
DECLARE_TYPEOF_COLLECTION(TextValue*);
|
||||||
@@ -140,7 +141,7 @@ SCRIPT_FUNCTION(primary_choice) {
|
|||||||
SCRIPT_PARAM(ValueP,input);
|
SCRIPT_PARAM(ValueP,input);
|
||||||
ChoiceValueP value = dynamic_pointer_cast<ChoiceValue>(input);
|
ChoiceValueP value = dynamic_pointer_cast<ChoiceValue>(input);
|
||||||
if (!value) {
|
if (!value) {
|
||||||
throw ScriptError(_("Argument to 'primary_choice' should be a choice field"));
|
throw ScriptError(_("Argument to 'primary_choice' should be a choice value"));
|
||||||
}
|
}
|
||||||
// determine choice
|
// determine choice
|
||||||
int id = value->field().choices->choiceId(value->value);
|
int id = value->field().choices->choiceId(value->value);
|
||||||
@@ -154,10 +155,46 @@ SCRIPT_FUNCTION(primary_choice) {
|
|||||||
SCRIPT_RETURN(_(""));
|
SCRIPT_RETURN(_(""));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Multiple choice values
|
||||||
|
|
||||||
|
// is the given choice active?
|
||||||
|
SCRIPT_FUNCTION(chosen) {
|
||||||
|
SCRIPT_PARAM(String,choice);
|
||||||
|
SCRIPT_PARAM(String,input);
|
||||||
|
for (size_t pos = 0 ; pos < input.size() ; ) {
|
||||||
|
if (input.GetChar(pos) == _(' ')) {
|
||||||
|
++pos; // ingore whitespace
|
||||||
|
} else {
|
||||||
|
// does this choice match the one asked about?
|
||||||
|
size_t end = input.find_first_of(_(','), pos);
|
||||||
|
if (end == String::npos) end = input.size();
|
||||||
|
if (end - pos == choice.size() && is_substr(input, pos, choice)) {
|
||||||
|
SCRIPT_RETURN(true);
|
||||||
|
}
|
||||||
|
pos = end + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SCRIPT_RETURN(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// add the given choice if it is not already active
|
||||||
|
SCRIPT_FUNCTION(require_choice) {
|
||||||
|
SCRIPT_PARAM(ValueP,input);
|
||||||
|
MultipleChoiceValueP value = dynamic_pointer_cast<MultipleChoiceValue>(input);
|
||||||
|
if (!value) {
|
||||||
|
throw ScriptError(_("Argument 'input' to 'require_choice' should be a multiple choice value"));
|
||||||
|
}
|
||||||
|
SCRIPT_PARAM(String,choice);
|
||||||
|
// TODO
|
||||||
|
SCRIPT_RETURN(input);
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------- : Init
|
// ----------------------------------------------------------------------------- : Init
|
||||||
|
|
||||||
void init_script_editor_functions(Context& ctx) {
|
void init_script_editor_functions(Context& ctx) {
|
||||||
ctx.setVariable(_("forward editor"), script_combined_editor); // combatability
|
ctx.setVariable(_("forward editor"), script_combined_editor); // combatability
|
||||||
ctx.setVariable(_("combined editor"), script_combined_editor);
|
ctx.setVariable(_("combined editor"), script_combined_editor);
|
||||||
ctx.setVariable(_("primary choice"), script_primary_choice);
|
ctx.setVariable(_("primary choice"), script_primary_choice);
|
||||||
|
ctx.setVariable(_("chosen"), script_chosen);
|
||||||
|
ctx.setVariable(_("require choice"), script_require_choice);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -567,7 +567,7 @@ void parseOper(TokenIterator& input, Script& script, Precedence minPrec, Instruc
|
|||||||
t = input.peek();
|
t = input.peek();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
input.read(); // skip the )
|
expectToken(input, _(")"));
|
||||||
// generate instruction
|
// generate instruction
|
||||||
script.addInstruction(I_CALL, (unsigned int)arguments.size());
|
script.addInstruction(I_CALL, (unsigned int)arguments.size());
|
||||||
FOR_EACH(arg,arguments) {
|
FOR_EACH(arg,arguments) {
|
||||||
|
|||||||
@@ -14,6 +14,8 @@
|
|||||||
|
|
||||||
Alignment from_string(const String&);
|
Alignment from_string(const String&);
|
||||||
|
|
||||||
|
DECLARE_TYPEOF_COLLECTION(ScriptParseError);
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------- : Store
|
// ----------------------------------------------------------------------------- : Store
|
||||||
|
|
||||||
void store(const ScriptValueP& val, String& var) { var = val->toString(); }
|
void store(const ScriptValueP& val, String& var) { var = val->toString(); }
|
||||||
@@ -43,10 +45,16 @@ ScriptValueP OptionalScript::invoke(Context& ctx, bool open_scope) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void OptionalScript::parse(Reader& reader, bool string_mode) {
|
void OptionalScript::parse(Reader& reader, bool string_mode) {
|
||||||
try {
|
vector<ScriptParseError> errors;
|
||||||
script = ::parse(unparsed, string_mode);
|
script = ::parse(unparsed, string_mode, errors);
|
||||||
} catch (const ParseError& e) {
|
// show parse errors as warnings
|
||||||
reader.warning(e.what());
|
FOR_EACH(e, errors) {
|
||||||
|
// find line number
|
||||||
|
int line = 0;
|
||||||
|
for (size_t i = 0 ; i < unparsed.size() && i < e.start ; ++i) {
|
||||||
|
if (unparsed.GetChar(i) == _('\n')) line++;
|
||||||
|
}
|
||||||
|
reader.warning(e.ParseError::what(), line); // use ParseError::what because we don't want e.start in the error message
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -147,6 +147,7 @@ void Reader::handle(Scriptable<T>& s) {
|
|||||||
} else if (s.script.unparsed.find_first_of('{') != String::npos) {
|
} else if (s.script.unparsed.find_first_of('{') != String::npos) {
|
||||||
s.script.parse(*this, true);
|
s.script.parse(*this, true);
|
||||||
} else {
|
} else {
|
||||||
|
unhandle();
|
||||||
handle(s.value);
|
handle(s.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+102
-30
@@ -14,19 +14,19 @@
|
|||||||
// ----------------------------------------------------------------------------- : Reader
|
// ----------------------------------------------------------------------------- : Reader
|
||||||
|
|
||||||
Reader::Reader(const InputStreamP& input, const String& filename, bool ignore_invalid)
|
Reader::Reader(const InputStreamP& input, const String& filename, bool ignore_invalid)
|
||||||
: indent(0), expected_indent(0), just_opened(false)
|
: indent(0), expected_indent(0), state(OUTSIDE)
|
||||||
, filename(filename), line_number(0)
|
, filename(filename), line_number(0), previous_line_number(0)
|
||||||
, ignore_invalid(ignore_invalid)
|
, ignore_invalid(ignore_invalid)
|
||||||
, input(input), stream(*input)
|
, input(input)
|
||||||
{
|
{
|
||||||
moveNext();
|
moveNext();
|
||||||
handleAppVersion();
|
handleAppVersion();
|
||||||
}
|
}
|
||||||
|
|
||||||
Reader::Reader(const String& filename)
|
Reader::Reader(const String& filename)
|
||||||
: indent(0), expected_indent(0), just_opened(false)
|
: indent(0), expected_indent(0), state(OUTSIDE)
|
||||||
, filename(filename), line_number(0)
|
, filename(filename), line_number(0), previous_line_number(0)
|
||||||
, input(packages.openFileFromPackage(filename)), stream(*input)
|
, input(packages.openFileFromPackage(filename))
|
||||||
{
|
{
|
||||||
moveNext();
|
moveNext();
|
||||||
handleAppVersion();
|
handleAppVersion();
|
||||||
@@ -48,8 +48,10 @@ void Reader::handleAppVersion() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Reader::warning(const String& msg) {
|
void Reader::warning(const String& msg, int line_number_delta, bool warn_on_previous_line) {
|
||||||
warnings += String(_("\nOn line ")) << line_number << _(": \t") << msg;
|
warnings += String(_("\nOn line "))
|
||||||
|
<< ((warn_on_previous_line ? previous_line_number : line_number) + line_number_delta)
|
||||||
|
<< _(": \t") << msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Reader::showWarnings() {
|
void Reader::showWarnings() {
|
||||||
@@ -59,11 +61,19 @@ void Reader::showWarnings() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Reader::enterAnyBlock() {
|
||||||
|
if (state == ENTERED) moveNext(); // on the key of the parent block, first move inside it
|
||||||
|
if (indent != expected_indent) return false; // not enough indentation
|
||||||
|
state = ENTERED;
|
||||||
|
expected_indent += 1; // the indent inside the block must be at least this much
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool Reader::enterBlock(const Char* name) {
|
bool Reader::enterBlock(const Char* name) {
|
||||||
if (just_opened) moveNext(); // on the key of the parent block, first move inside it
|
if (state == ENTERED) moveNext(); // on the key of the parent block, first move inside it
|
||||||
if (indent != expected_indent) return false; // not enough indentation
|
if (indent != expected_indent) return false; // not enough indentation
|
||||||
if (cannocial_name_compare(key, name)) {
|
if (cannocial_name_compare(key, name)) {
|
||||||
just_opened = true;
|
state = ENTERED;
|
||||||
expected_indent += 1; // the indent inside the block must be at least this much
|
expected_indent += 1; // the indent inside the block must be at least this much
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
@@ -74,20 +84,21 @@ bool Reader::enterBlock(const Char* name) {
|
|||||||
void Reader::exitBlock() {
|
void Reader::exitBlock() {
|
||||||
assert(expected_indent > 0);
|
assert(expected_indent > 0);
|
||||||
expected_indent -= 1;
|
expected_indent -= 1;
|
||||||
multi_line_str.clear();
|
assert(state != UNHANDLED);
|
||||||
if (just_opened) moveNext(); // leave this key
|
previous_value.clear();
|
||||||
|
if (state == ENTERED) moveNext(); // leave this key
|
||||||
// Dump the remainder of the block
|
// Dump the remainder of the block
|
||||||
// TODO: issue warnings?
|
// TODO: issue warnings?
|
||||||
while (indent > expected_indent) {
|
while (indent > expected_indent) {
|
||||||
moveNext();
|
moveNext();
|
||||||
}
|
}
|
||||||
handled = true;
|
state = HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Reader::moveNext() {
|
void Reader::moveNext() {
|
||||||
just_opened = false;
|
previous_line_number = line_number;
|
||||||
|
state = HANDLED;
|
||||||
key.clear();
|
key.clear();
|
||||||
multi_line_str.clear();
|
|
||||||
indent = -1; // if no line is read it never has the expected indentation
|
indent = -1; // if no line is read it never has the expected indentation
|
||||||
// repeat until we have a good line
|
// repeat until we have a good line
|
||||||
while (key.empty() && !input->Eof()) {
|
while (key.empty() && !input->Eof()) {
|
||||||
@@ -100,10 +111,55 @@ void Reader::moveNext() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Read an UTF-8 encoded line from an input stream
|
||||||
|
/** As opposed to wx functions, this one actually reports errors
|
||||||
|
*/
|
||||||
|
String read_utf8_line(wxInputStream& input, bool eat_bom = true, bool until_eof = false);
|
||||||
|
String read_utf8_line(wxInputStream& input, bool eat_bom, bool until_eof) {
|
||||||
|
vector<char> buffer;
|
||||||
|
while (!input.Eof()) {
|
||||||
|
Byte c = input.GetC(); if (input.LastRead() <= 0) break;
|
||||||
|
if (!until_eof) {
|
||||||
|
if (c == '\n') break;
|
||||||
|
if (c == '\r') {
|
||||||
|
if (input.Eof()) break;
|
||||||
|
c = input.GetC(); if (input.LastRead() <= 0) break;
|
||||||
|
if (c != '\n') {
|
||||||
|
input.Ungetch(c); // \r but not \r\n
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buffer.push_back(c);
|
||||||
|
}
|
||||||
|
// convert to string
|
||||||
|
buffer.push_back('\0');
|
||||||
|
size_t size = wxConvUTF8.MB2WC(nullptr, &buffer[0], 0);
|
||||||
|
if (size == -1) {
|
||||||
|
throw ParseError(_("Invalid UTF-8 sequence"));
|
||||||
|
} else if (size == 0) {
|
||||||
|
return _("");
|
||||||
|
}
|
||||||
|
String result;
|
||||||
|
#ifdef UNICODE
|
||||||
|
// NOTE: wx doc is wrong, parameter to GetWritableChar is numer of characters, not bytes
|
||||||
|
Char* result_buf = result.GetWriteBuf(size + 1);
|
||||||
|
wxConvUTF8.MB2WC(result_buf, &buffer[0], size + 1);
|
||||||
|
result.UngetWriteBuf(size);
|
||||||
|
#else
|
||||||
|
// TODO!
|
||||||
|
#endif
|
||||||
|
return eat_bom ? decodeUTF8BOM(result) : result;
|
||||||
|
}
|
||||||
|
|
||||||
void Reader::readLine(bool in_string) {
|
void Reader::readLine(bool in_string) {
|
||||||
// fix UTF8 in ascii builds; skip BOM
|
|
||||||
line = decodeUTF8BOM(stream.ReadLine());
|
|
||||||
line_number += 1;
|
line_number += 1;
|
||||||
|
// We have to do our own line reading, because wxTextInputStream is insane
|
||||||
|
try {
|
||||||
|
line = read_utf8_line(*input, line_number == 1);
|
||||||
|
} catch (const ParseError& e) {
|
||||||
|
throw ParseError(e.what() + String(_(" on line ")) << line_number);
|
||||||
|
}
|
||||||
// read indentation
|
// read indentation
|
||||||
indent = 0;
|
indent = 0;
|
||||||
while ((UInt)indent < line.size() && line.GetChar(indent) == _('\t')) {
|
while ((UInt)indent < line.size() && line.GetChar(indent) == _('\t')) {
|
||||||
@@ -118,7 +174,7 @@ void Reader::readLine(bool in_string) {
|
|||||||
}
|
}
|
||||||
key = line.substr(indent, pos - indent);
|
key = line.substr(indent, pos - indent);
|
||||||
if (!ignore_invalid && !in_string && starts_with(key, _(" "))) {
|
if (!ignore_invalid && !in_string && starts_with(key, _(" "))) {
|
||||||
warning(_("key: '") + key + _("' starts with a space; only use TABs for indentation!"));
|
warning(_("key: '") + key + _("' starts with a space; only use TABs for indentation!"), 0, false);
|
||||||
// try to fix up: 8 spaces is a tab
|
// try to fix up: 8 spaces is a tab
|
||||||
while (starts_with(key, _(" "))) {
|
while (starts_with(key, _(" "))) {
|
||||||
key = key.substr(8);
|
key = key.substr(8);
|
||||||
@@ -146,7 +202,7 @@ void Reader::unknownKey() {
|
|||||||
} else if (it->second.end_version <= file_app_version) {
|
} else if (it->second.end_version <= file_app_version) {
|
||||||
// alias not used for this version, use in warning
|
// alias not used for this version, use in warning
|
||||||
if (indent == expected_indent) {
|
if (indent == expected_indent) {
|
||||||
warning(_("Unexpected key: '") + key + _("' use '") + it->second.new_key + _("'"));
|
warning(_("Unexpected key: '") + key + _("' use '") + it->second.new_key + _("'"), 0, false);
|
||||||
do {
|
do {
|
||||||
moveNext();
|
moveNext();
|
||||||
} while (indent > expected_indent);
|
} while (indent > expected_indent);
|
||||||
@@ -159,7 +215,7 @@ void Reader::unknownKey() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (indent >= expected_indent) {
|
if (indent >= expected_indent) {
|
||||||
warning(_("Unexpected key: '") + key + _("'"));
|
warning(_("Unexpected key: '") + key + _("'"), 0, false);
|
||||||
do {
|
do {
|
||||||
moveNext();
|
moveNext();
|
||||||
} while (indent > expected_indent);
|
} while (indent > expected_indent);
|
||||||
@@ -169,23 +225,31 @@ void Reader::unknownKey() {
|
|||||||
|
|
||||||
// ----------------------------------------------------------------------------- : Handling basic types
|
// ----------------------------------------------------------------------------- : Handling basic types
|
||||||
|
|
||||||
|
void Reader::unhandle() {
|
||||||
|
assert(state == HANDLED);
|
||||||
|
state = UNHANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
const String& Reader::getValue() {
|
const String& Reader::getValue() {
|
||||||
handled = true;
|
assert(state != HANDLED); // don't try to handle things twice
|
||||||
if (!multi_line_str.empty()) {
|
if (state == UNHANDLED) {
|
||||||
return multi_line_str;
|
state = HANDLED;
|
||||||
|
return previous_value;
|
||||||
} else if (value.empty()) {
|
} else if (value.empty()) {
|
||||||
// a multiline string
|
// a multiline string
|
||||||
|
previous_value.clear();
|
||||||
bool first = true;
|
bool first = true;
|
||||||
// read all lines that are indented enough
|
// read all lines that are indented enough
|
||||||
readLine();
|
readLine();
|
||||||
|
previous_line_number = line_number;
|
||||||
while (indent >= expected_indent && !input->Eof()) {
|
while (indent >= expected_indent && !input->Eof()) {
|
||||||
if (!first) multi_line_str += _('\n');
|
if (!first) previous_value += _('\n');
|
||||||
first = false;
|
first = false;
|
||||||
multi_line_str += line.substr(expected_indent); // strip expected indent
|
previous_value += line.substr(expected_indent); // strip expected indent
|
||||||
readLine(true);
|
readLine(true);
|
||||||
}
|
}
|
||||||
// moveNext(), but without emptying multi_line_str
|
// moveNext(), but without the initial readLine()
|
||||||
just_opened = false;
|
state = HANDLED;
|
||||||
while (key.empty() && !input->Eof()) {
|
while (key.empty() && !input->Eof()) {
|
||||||
readLine();
|
readLine();
|
||||||
}
|
}
|
||||||
@@ -194,9 +258,16 @@ const String& Reader::getValue() {
|
|||||||
line_number += 1;
|
line_number += 1;
|
||||||
indent = -1;
|
indent = -1;
|
||||||
}
|
}
|
||||||
return multi_line_str;
|
if (indent >= expected_indent) {
|
||||||
|
warning(_("Blank line or comment in text block, that is insufficiently indented.\n")
|
||||||
|
_("\t\tEither indent the comment/blank line, or add a 'key:' after it.\n")
|
||||||
|
_("\t\tThis could cause more more error messages.\n"), -1, false);
|
||||||
|
}
|
||||||
|
return previous_value;
|
||||||
} else {
|
} else {
|
||||||
return value;
|
previous_value = value;
|
||||||
|
moveNext();
|
||||||
|
return previous_value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,7 +288,8 @@ template <> void Reader::handle(double& d) {
|
|||||||
getValue().ToDouble(&d);
|
getValue().ToDouble(&d);
|
||||||
}
|
}
|
||||||
template <> void Reader::handle(bool& b) {
|
template <> void Reader::handle(bool& b) {
|
||||||
b = (getValue()==_("true") || getValue()==_("1") || getValue()==_("yes"));
|
const String& v = getValue();
|
||||||
|
b = (v==_("true") || v==_("1") || v==_("yes"));
|
||||||
}
|
}
|
||||||
// ----------------------------------------------------------------------------- : Handling less basic util types
|
// ----------------------------------------------------------------------------- : Handling less basic util types
|
||||||
|
|
||||||
|
|||||||
+23
-24
@@ -11,7 +11,6 @@
|
|||||||
|
|
||||||
#include <util/prec.hpp>
|
#include <util/prec.hpp>
|
||||||
#include <util/version.hpp>
|
#include <util/version.hpp>
|
||||||
#include <wx/txtstrm.h>
|
|
||||||
|
|
||||||
template <typename T> class Defaultable;
|
template <typename T> class Defaultable;
|
||||||
template <typename T> class Scriptable;
|
template <typename T> class Scriptable;
|
||||||
@@ -57,7 +56,7 @@ class Reader {
|
|||||||
void handleAppVersion();
|
void handleAppVersion();
|
||||||
|
|
||||||
/// Add a warning message, but continue reading
|
/// Add a warning message, but continue reading
|
||||||
void warning(const String& msg);
|
void warning(const String& msg, int line_number_delta = 0, bool warn_on_previous_line = true);
|
||||||
/// Show all warning messages, but continue reading
|
/// Show all warning messages, but continue reading
|
||||||
void showWarnings();
|
void showWarnings();
|
||||||
|
|
||||||
@@ -66,11 +65,9 @@ class Reader {
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
void handle_greedy(T& object) {
|
void handle_greedy(T& object) {
|
||||||
do {
|
do {
|
||||||
// UInt l = line_number;
|
|
||||||
handled = false;
|
|
||||||
handle(object);
|
handle(object);
|
||||||
// if (l == line_number && !handled) unknownKey(object);
|
if (state != HANDLED) unknownKey(object);
|
||||||
if (!handled) unknownKey(object);
|
state = OUTSIDE;
|
||||||
} while (indent >= expected_indent);
|
} while (indent >= expected_indent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,6 +103,9 @@ class Reader {
|
|||||||
void handle(GameP&);
|
void handle(GameP&);
|
||||||
void handle(StyleSheetP&);
|
void handle(StyleSheetP&);
|
||||||
|
|
||||||
|
/// Indicate that the last value from getValue() was not handled, allowing it to be handled again
|
||||||
|
void unhandle();
|
||||||
|
|
||||||
// --------------------------------------------------- : Data
|
// --------------------------------------------------- : Data
|
||||||
/// App version this file was made with
|
/// App version this file was made with
|
||||||
Version file_app_version;
|
Version file_app_version;
|
||||||
@@ -114,16 +114,19 @@ class Reader {
|
|||||||
String line;
|
String line;
|
||||||
/// The key and value of the last line we read
|
/// The key and value of the last line we read
|
||||||
String key, value;
|
String key, value;
|
||||||
/// A string spanning multiple lines
|
/// Value of the *previous* line, only valid in state==HANDLED
|
||||||
String multi_line_str;
|
String previous_value;
|
||||||
/// Has the current line been handled?
|
|
||||||
bool handled;
|
|
||||||
/// Indentation of the last line we read
|
/// Indentation of the last line we read
|
||||||
int indent;
|
int indent;
|
||||||
/// Indentation of the block we are in
|
/// Indentation of the block we are in
|
||||||
int expected_indent;
|
int expected_indent;
|
||||||
/// Did we just open a block (i.e. not read any more lines of it)?
|
/// State of the reader
|
||||||
bool just_opened;
|
enum State {
|
||||||
|
OUTSIDE, ///< We have not entered the block of the current key
|
||||||
|
ENTERED, ///< We just entered the block of the current key
|
||||||
|
HANDLED, ///< We have handled a value, and moved to the next line, previous_value is the value we just handled
|
||||||
|
UNHANDLED, ///< Something has been 'unhandled()'
|
||||||
|
} state;
|
||||||
/// Aliasses for compatability
|
/// Aliasses for compatability
|
||||||
struct Alias {
|
struct Alias {
|
||||||
String new_key;
|
String new_key;
|
||||||
@@ -136,19 +139,21 @@ class Reader {
|
|||||||
|
|
||||||
/// Filename for error messages
|
/// Filename for error messages
|
||||||
String filename;
|
String filename;
|
||||||
/// Line number for error messages
|
/// Line number of the current line for error messages
|
||||||
UInt line_number;
|
int line_number;
|
||||||
|
/// Line number of the previous_line
|
||||||
|
int previous_line_number;
|
||||||
/// Input stream we are reading from
|
/// Input stream we are reading from
|
||||||
InputStreamP input;
|
InputStreamP input;
|
||||||
/// Text stream wrapping the input stream
|
|
||||||
wxTextInputStream stream;
|
|
||||||
/// Accumulated warning messages
|
/// Accumulated warning messages
|
||||||
String warnings;
|
String warnings;
|
||||||
|
|
||||||
// --------------------------------------------------- : Reading the stream
|
// --------------------------------------------------- : Reading the stream
|
||||||
|
|
||||||
/// Is there a block with the given key under the current cursor?
|
/// Is there a block with the given key under the current cursor? if so, enter it
|
||||||
bool enterBlock(const Char* name);
|
bool enterBlock(const Char* name);
|
||||||
|
/// Enter any block, no matter what the key
|
||||||
|
bool enterAnyBlock();
|
||||||
/// Leave the block we are in
|
/// Leave the block we are in
|
||||||
void exitBlock();
|
void exitBlock();
|
||||||
|
|
||||||
@@ -208,13 +213,7 @@ void Reader::handle(intrusive_ptr<T>& pointer) {
|
|||||||
|
|
||||||
template <typename V>
|
template <typename V>
|
||||||
void Reader::handle(map<String, V>& m) {
|
void Reader::handle(map<String, V>& m) {
|
||||||
while (true) {
|
while (enterAnyBlock()) {
|
||||||
// same as enterBlock
|
|
||||||
if (just_opened) moveNext(); // on the key of the parent block, first move inside it
|
|
||||||
if (indent != expected_indent) return; // not enough indentation
|
|
||||||
just_opened = true;
|
|
||||||
expected_indent += 1;
|
|
||||||
// now read the value
|
|
||||||
handle_greedy(m[key]);
|
handle_greedy(m[key]);
|
||||||
exitBlock();
|
exitBlock();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user