diff --git a/src/data/export_template.cpp b/src/data/export_template.cpp index 53590752..3ba929a3 100644 --- a/src/data/export_template.cpp +++ b/src/data/export_template.cpp @@ -12,7 +12,10 @@ // ----------------------------------------------------------------------------- : Export template, basics -ExportTemplate::ExportTemplate() {} +ExportTemplate::ExportTemplate() + : create_directory(false) + , file_type(_("HTML files (*.html)|*.html")) +{} String ExportTemplate::typeNameStatic() { return _("export-template"); } String ExportTemplate::typeName() const { return _("export-template"); } @@ -28,4 +31,6 @@ IMPLEMENT_REFLECTION(ExportTemplate) { REFLECT(script); } -// ----------------------------------------------------------------------------- : +// ----------------------------------------------------------------------------- : ExportInfo + +IMPLEMENT_DYNAMIC_ARG(ExportInfo*, export_info, nullptr); diff --git a/src/data/export_template.hpp b/src/data/export_template.hpp index 3e982989..cdaddb02 100644 --- a/src/data/export_template.hpp +++ b/src/data/export_template.hpp @@ -16,6 +16,7 @@ DECLARE_POINTER_TYPE(Game); DECLARE_POINTER_TYPE(Field); DECLARE_POINTER_TYPE(Style); +DECLARE_POINTER_TYPE(ExportTemplate); // ----------------------------------------------------------------------------- : ExportTemplate @@ -26,7 +27,7 @@ class ExportTemplate : public Packaged { GameP game; ///< Game this template is for String file_type; ///< Type of the created file, in "name|*.ext" format - bool create_directory; ///< The export creates an entire directory + bool create_directory; ///< The export creates a directory for additional data files vector option_fields; ///< Options for exporting IndexMap option_style; ///< Style of the options OptionalScript script; ///< Export script, for multi file templates and initialization @@ -37,11 +38,17 @@ class ExportTemplate : public Packaged { DECLARE_REFLECTION(); }; -// ----------------------------------------------------------------------------- : ExportPackage +// ----------------------------------------------------------------------------- : ExportInfo -/// A package that is being written to when exporting -class ExportingPackage : public Package { +/// Information that can be used by export functions +struct ExportInfo { + ExportTemplateP export_template; ///< The export template used + String directory_relative; ///< The directory for storing extra files (or "" if !export->create_directory) + /// This is just the directory name + String directory_absolute; ///< The absolute path of the directory }; +DECLARE_DYNAMIC_ARG(ExportInfo*, export_info); + // ----------------------------------------------------------------------------- : EOF #endif diff --git a/src/gui/control/package_list.hpp b/src/gui/control/package_list.hpp index 12e43499..407424a7 100644 --- a/src/gui/control/package_list.hpp +++ b/src/gui/control/package_list.hpp @@ -38,10 +38,10 @@ class PackageList : public GalleryList { /** @pre hasSelection() * Throws if the selection is not of type T */ template - intrusive_ptr getSelection() const { + intrusive_ptr getSelection(bool load_fully = true) const { intrusive_ptr ret = dynamic_pointer_cast(packages.at(selection).package); if (!ret) throw InternalError(_("PackageList: Selected package has the wrong type")); - ret->loadFully(); + if (load_fully) ret->loadFully(); return ret; } diff --git a/src/gui/html_export_window.cpp b/src/gui/html_export_window.cpp index 6ec60bb3..321267de 100644 --- a/src/gui/html_export_window.cpp +++ b/src/gui/html_export_window.cpp @@ -15,6 +15,8 @@ #include #include #include +#include +#include DECLARE_POINTER_TYPE(ExportTemplate); @@ -45,17 +47,35 @@ HtmlExportWindow::HtmlExportWindow(Window* parent, const SetP& set) } void HtmlExportWindow::onOk(wxCommandEvent&) { - handle_error(Error(_("HTML export is not implemented yet, sorry"))); - /*;//%% - String name = fileSelector(_("Exort to html"),_(""),_(""),_(""), { - _("HTML files (*.html)|*.html"), - wxSAVE | wxOVERWRITE_PROMPT); + ExportTemplateP exp = list->getSelection(); + // get filename + String name = wxFileSelector(_TITLE_("save html"),_(""),_(""),_(""),exp->file_type, wxSAVE | wxOVERWRITE_PROMPT); + if (name.empty()) return; + // export info for script + ExportInfo info; + info.export_template = exp; + WITH_DYNAMIC_ARG(export_info, &info); + // create directory? + if (exp->create_directory) { + wxFileName fn(name); + info.directory_relative = fn.GetName() + _("-files"); + fn.SetFullName(info.directory_relative); + info.directory_absolute = fn.GetFullPath(); + wxMkDir(info.directory_absolute); } - if (!name.empty()) { - HtmlExportWindow wnd(&this, set, name); - wnd.showModal(); + // run export script + Context& ctx = set->getContext(); + LocalScope scope(ctx); + ctx.setVariable(_("options"), to_script(&settings.exportOptionsFor(*exp))); + ctx.setVariable(_("directory"), to_script(info.directory_relative)); + ScriptValueP result = exp->script.invoke(ctx); + // Save to file + wxFileOutputStream file(name); + { // TODO: write as image? + // write as string + wxTextOutputStream stream(file); + stream.WriteString(*result); } - */ // Done EndModal(wxID_OK); } diff --git a/src/gui/new_window.cpp b/src/gui/new_window.cpp index 4923f582..f66c4d0a 100644 --- a/src/gui/new_window.cpp +++ b/src/gui/new_window.cpp @@ -59,7 +59,7 @@ NewSetWindow::NewSetWindow(Window* parent) void NewSetWindow::onGameSelect(wxCommandEvent&) { wxBusyCursor wait; - GameP game = game_list->getSelection(); + GameP game = game_list->getSelection(false); handle_pending_errors(); settings.default_game = game->name(); stylesheet_list->showData(game->name() + _("-*")); @@ -76,8 +76,8 @@ void NewSetWindow::onGameSelect(wxCommandEvent&) { void NewSetWindow::onStyleSheetSelect(wxCommandEvent&) { // store this as default selection - GameP game = game_list ->getSelection(); - StyleSheetP stylesheet = stylesheet_list->getSelection(); + GameP game = game_list ->getSelection(false); + StyleSheetP stylesheet = stylesheet_list->getSelection(false); handle_pending_errors(); settings.gameSettingsFor(*game).default_stylesheet = stylesheet->name(); UpdateWindowUI(wxUPDATE_UI_RECURSE); diff --git a/src/gui/set/window.cpp b/src/gui/set/window.cpp index 125635b8..8849c149 100644 --- a/src/gui/set/window.cpp +++ b/src/gui/set/window.cpp @@ -508,7 +508,9 @@ void SetWindow::onFileReload(wxCommandEvent&) { String filename = set->absoluteFilename(); if (filename.empty()) return; wxBusyCursor busy; + settings.write(); // save settings packages.destroy(); // unload all packages + settings.read(); // reload settings setSet(import_set(filename)); } diff --git a/src/script/context.cpp b/src/script/context.cpp index 910b060c..193613ae 100644 --- a/src/script/context.cpp +++ b/src/script/context.cpp @@ -91,8 +91,7 @@ ScriptValueP Context::eval(const Script& script, bool useScope) { // Loop over a container, push next value or jump case I_LOOP: { ScriptValueP& it = stack[stack.size() - 2]; // second element of stack - assert(dynamic_pointer_cast(it)); // top of stack must be an iterator - ScriptValueP val = static_pointer_cast(it)->next(); + ScriptValueP val = it->next(); if (val) { stack.push_back(val); } else { diff --git a/src/script/context.hpp b/src/script/context.hpp index 19f30949..07557aeb 100644 --- a/src/script/context.hpp +++ b/src/script/context.hpp @@ -57,6 +57,12 @@ class Context { /// Get the value of a variable, returns ScriptValue() if it is not set ScriptValueP getVariableOpt(const String& name); + /// Open a new scope + /** returns the number of shadowed binding before that scope */ + size_t openScope(); + /// Close a scope, must be passed a value from openScope + void closeScope(size_t scope); + public:// public for FOR_EACH /// Record of a variable struct Variable { @@ -85,11 +91,6 @@ class Context { /// Set a variable to a new value (in the current scope) void setVariable(int name, const ScriptValueP& value); - /// Open a new scope - /** returns the number of shadowed binding before that scope */ - size_t openScope(); - /// Close a scope, must be passed a value from openScope - void closeScope(size_t scope); /// Return the bindings in the current scope void getBindings(size_t scope, vector&); /// Remove all bindings made in the current scope @@ -98,5 +99,15 @@ class Context { void makeObject(size_t n); }; +/// A class that creates a local scope +class LocalScope { + public: + inline LocalScope(Context& ctx) : ctx(ctx), scope(ctx.openScope()) {} + inline ~LocalScope() { ctx.closeScope(scope); } + private: + Context& ctx; + size_t scope; +}; + // ----------------------------------------------------------------------------- : EOF #endif diff --git a/src/script/dependency.cpp b/src/script/dependency.cpp index 5cbbd29d..1be27977 100644 --- a/src/script/dependency.cpp +++ b/src/script/dependency.cpp @@ -220,8 +220,7 @@ ScriptValueP Context::dependencies(const Dependency& dep, const Script& script) // Loop over a container, push next value or jump (almost as normal) case I_LOOP: { ScriptValueP& it = stack[stack.size() - 2]; // second element of stack - assert(dynamic_pointer_cast(it)); // top of stack must be an iterator - ScriptValueP val = static_pointer_cast(it)->next(); + ScriptValueP val = it->next(); if (val) { it = dependency_dummy; // invalidate iterator, so we loop only once stack.push_back(val); diff --git a/src/script/functions/basic.cpp b/src/script/functions/basic.cpp index 4cace690..fcd14667 100644 --- a/src/script/functions/basic.cpp +++ b/src/script/functions/basic.cpp @@ -206,7 +206,7 @@ ScriptValueP sort_script(Context& ctx, const ScriptValueP& list, ScriptValue& or sort(s.begin(), s.end()); SCRIPT_RETURN(s); } else { - // are we sorting a set + // are we sorting a set? ScriptObject* set = dynamic_cast*>(list.get()); // sort a collection vector > values; @@ -254,6 +254,22 @@ SCRIPT_FUNCTION(number_of_items) { SCRIPT_RETURN(ctx.getVariable(_("in"))->itemCount()); } +// filtering items from a list +SCRIPT_FUNCTION(filter_list) { + SCRIPT_PARAM(ScriptValueP, input); + SCRIPT_PARAM(ScriptValueP, filter); + // filter a collection + intrusive_ptr ret(new ScriptCustomCollection()); + ScriptValueP it = input->makeIterator(input); + while (ScriptValueP v = it->next()) { + ctx.setVariable(_("input"), v); + if (*filter->eval(ctx)) { + ret->value.push_back(v); + } + } + // TODO : somehow preserve keys + return ret; +} // ----------------------------------------------------------------------------- : Keywords @@ -560,8 +576,11 @@ class ScriptRule_sort_order: public ScriptValue { virtual ScriptType type() const { return SCRIPT_FUNCTION; } virtual String typeName() const { return _("sort_rule"); } virtual ScriptValueP eval(Context& ctx) const { - SCRIPT_PARAM(String, input); - SCRIPT_RETURN(spec_sort(order, input)); + SCRIPT_PARAM(ScriptValueP, input); + if (input->type() == SCRIPT_COLLECTION) { + handle_warning(_("Sorting a collection as a string, this is probably not intended, if it is use 'collection+\"\"' to force conversion"), false); + } + SCRIPT_RETURN(spec_sort(order, input->toString())); } private: String order; @@ -585,9 +604,13 @@ class ScriptRule_sort: public ScriptValue { virtual ScriptType type() const { return SCRIPT_FUNCTION; } virtual String typeName() const { return _("sort_rule"); } virtual ScriptValueP eval(Context& ctx) const { - SCRIPT_PARAM(String, input); - sort(input.begin(), input.end()); - SCRIPT_RETURN(input); + SCRIPT_PARAM(ScriptValueP, input); + if (input->type() == SCRIPT_COLLECTION) { + handle_warning(_("Sorting a collection as a string, this is probably not intended, if it is use 'collection+\"\"' to force conversion"), false); + } + String input_str = input->toString(); + sort(input_str.begin(), input_str.end()); + SCRIPT_RETURN(input_str); } private: ScriptValueP order_by; @@ -597,7 +620,7 @@ SCRIPT_FUNCTION(sort_rule) { SCRIPT_OPTIONAL_PARAM(String, order) { return new_intrusive1(order); } - SCRIPT_OPTIONAL_PARAM(ScriptValueP, order_by) { + SCRIPT_OPTIONAL_PARAM_N(ScriptValueP, _("order by"), order_by) { return new_intrusive1(order_by); } else { return new_intrusive (); @@ -607,7 +630,7 @@ SCRIPT_FUNCTION(sort) { SCRIPT_OPTIONAL_PARAM(String, order) { return ScriptRule_sort_order (order ).eval(ctx); } - SCRIPT_OPTIONAL_PARAM(ScriptValueP, order_by) { + SCRIPT_OPTIONAL_PARAM_N(ScriptValueP, _("order by"), order_by) { return ScriptRule_sort_order_by(order_by).eval(ctx); } else { return ScriptRule_sort ( ).eval(ctx); @@ -637,6 +660,7 @@ void init_script_basic_functions(Context& ctx) { // collection ctx.setVariable(_("position"), script_position_of); ctx.setVariable(_("number of items"), script_number_of_items); + ctx.setVariable(_("filter list"), script_filter_list); // keyword ctx.setVariable(_("expand keywords"), script_expand_keywords); ctx.setVariable(_("expand keywords rule"), script_expand_keywords_rule); @@ -645,6 +669,7 @@ void init_script_basic_functions(Context& ctx) { ctx.setVariable(_("filter"), script_filter); ctx.setVariable(_("match"), script_match); ctx.setVariable(_("sort"), script_sort); + ctx.setVariable(_("sort list"), script_sort); ctx.setVariable(_("replace rule"), script_replace_rule); ctx.setVariable(_("filter rule"), script_filter_rule); ctx.setVariable(_("match rule"), script_match_rule); diff --git a/src/script/functions/export.cpp b/src/script/functions/export.cpp index b8916a64..30a3d2ea 100644 --- a/src/script/functions/export.cpp +++ b/src/script/functions/export.cpp @@ -56,7 +56,9 @@ class TagStack { } // Close all tags, should be called at end of input void close_all(String& ret) { - pending_tags.clear(); + // cancel out tags with pending tags + write_pending_tags(ret); + // close all open tags while (!tags.empty()) { tags.back()->write(ret, true); tags.pop_back(); @@ -77,7 +79,7 @@ class TagStack { void add(String& ret, const NegTag& tag) { // Cancel out with pending tag? - for (size_t i = pending_tags.size() - 1 ; i >= 0 ; --i) { + for (int i = (int)pending_tags.size() - 1 ; i >= 0 ; --i) { if (pending_tags[i].tag == tag.tag) { if (pending_tags[i].neg != tag.neg) { pending_tags.erase(pending_tags.begin() + i); @@ -89,18 +91,18 @@ class TagStack { } // Cancel out with existing tag? if (tag.neg) { - for (size_t i = tags.size() - 1 ; i >= 0 ; --i) { + for (int i = (int)tags.size() - 1 ; i >= 0 ; --i) { if (tags[i] == tag.tag) { // cancel out with existing tag i, e.g. : // situation was text // situation will become text vector reopen; - for (size_t j = tags.size() - 1 ; j > i ; --j) { + for (int j = (int)tags.size() - 1 ; j > i ; --j) { pending_tags.push_back(NegTag(tags[j], true)); // close tag, top down tags.pop_back(); } pending_tags.push_back(tag); // now close tag i - for (size_t j = i + 1 ; j < tags.size() ; ++j) { + for (int j = i + 1 ; j < (int)tags.size() ; ++j) { pending_tags.push_back(NegTag(tags[j], false)); // reopen later, bottom up tags.pop_back(); } @@ -119,7 +121,7 @@ String symbols_to_html(const String& str, const SymbolFontP& symbol_font) { } String to_html(const String& str_in, const SymbolFontP& symbol_font) { - String str = remove_tag_contents(str,_(""), _("")), italic(_(""), _("")), @@ -163,6 +165,8 @@ String to_html(const String& str_in, const SymbolFontP& symbol_font) { ret += _("&"); } else if (c >= 0x80) { // escape non ascii ret += String(_("&#")) << (int)c << _(';'); + } else if (c == _('\n')) { + ret += _("
\n"); } else { ret += c; } diff --git a/src/script/parser.cpp b/src/script/parser.cpp index 6feb63f3..5910f3e4 100644 --- a/src/script/parser.cpp +++ b/src/script/parser.cpp @@ -524,7 +524,8 @@ void parseOper(TokenIterator& input, Script& script, Precedence minPrec, Instruc // without left recursion: expr = expr (oper expr)* while (true) { const Token& token = input.read(); - if (token != TOK_OPER && token != TOK_NAME && token!=TOK_LPAREN) { + if (token != TOK_OPER && token != TOK_NAME && token!=TOK_LPAREN && + !((token == TOK_STRING || token == TOK_INT || token == TOK_DOUBLE) && minPrec <= PREC_NEWLINE && token.newline)) { // not an operator-like token input.putBack(); break; diff --git a/src/script/value.cpp b/src/script/value.cpp index 7929e18c..80316cf6 100644 --- a/src/script/value.cpp +++ b/src/script/value.cpp @@ -172,7 +172,7 @@ class ScriptString : public ScriptValue { public: ScriptString(const String& v) : value(v) {} virtual ScriptType type() const { return SCRIPT_STRING; } - virtual String typeName() const { return _TYPE_("string"); } + virtual String typeName() const { return _TYPE_("string") + _(" (\"") + (value.size() < 30 ? value : value.substr(0,30) + _("...")) + _("\")"); } virtual operator String() const { return value; } virtual operator double() const { double d; @@ -209,7 +209,7 @@ class ScriptString : public ScriptValue { if (name.ToLong(&index) && index >= 0 && (size_t)index < value.size()) { return to_script(String(1,value[index])); } else { - throw ScriptError(_ERROR_2_("has no member value", value, name)); + return delayError(_ERROR_2_("has no member value", value, name)); } } private: