mirror of
https://github.com/amyinspace/MagicSetEditor2.git
synced 2026-06-10 04:57:00 -04:00
* Added console panel for evaluating scripts and showing error messages.
* Rewrite of error queue code: errors are now pulled, instead of being turned into messageboxes automatically. git-svn-id: svn://svn.code.sf.net/p/magicseteditor/code/trunk@1629 0fc631ac-6414-0410-93d0-97cfa31319b6
This commit is contained in:
@@ -53,7 +53,7 @@ menu:
|
||||
previous keyword: Select &Previous Keyword PgUp
|
||||
next keyword: Select &Next Keyword PgDn
|
||||
add keyword: &Add Keyword Ctrl+Enter
|
||||
remove keyword: &Remove Select Keyword Del
|
||||
remove keyword: &Remove Select Keyword Del
|
||||
|
||||
format: F&ormat
|
||||
bold: &Bold Ctrl+B
|
||||
@@ -79,6 +79,7 @@ menu:
|
||||
keywords tab: &Keywords F8
|
||||
stats tab: S&tatistics F9
|
||||
random pack tab: &Random Packs
|
||||
console tab: &Console Ctrl+F9
|
||||
|
||||
help: &Help
|
||||
index: &Index... F1
|
||||
@@ -190,6 +191,7 @@ help:
|
||||
keywords tab: Define extra keywords for this set
|
||||
stats tab: Show statistics about the cards in the set
|
||||
random pack tab: Try how the set works out in practice by generating random booster packs.
|
||||
console tab: Shows error messages and allows executing script commands.
|
||||
|
||||
help:
|
||||
index:
|
||||
@@ -205,6 +207,8 @@ help:
|
||||
seed: Seed number for the random generator. Using the same seed number gives the same 'random' packs.
|
||||
edit pack type: Double click to edit pack type
|
||||
number of packs: The number of %ss to generate
|
||||
# Console panel
|
||||
evaluate: Evaluate the entered script command
|
||||
|
||||
# Preferences
|
||||
app language:
|
||||
@@ -292,6 +296,7 @@ tool:
|
||||
keywords tab: Keywords
|
||||
stats tab: Statistics
|
||||
random pack tab: Random
|
||||
console tab: Console
|
||||
|
||||
# symbol editor
|
||||
store symbol: Store
|
||||
@@ -337,6 +342,7 @@ tooltip:
|
||||
keywords tab:
|
||||
stats tab:
|
||||
random pack tab: Random packs
|
||||
console tab:
|
||||
|
||||
new set: New set
|
||||
open set: Open set
|
||||
@@ -562,6 +568,9 @@ button:
|
||||
fixed seed: &Fixed Seed
|
||||
add custom pack: Add &Custom Pack...
|
||||
|
||||
# Console panel
|
||||
evaluate: &Evaluate
|
||||
|
||||
# Welcome
|
||||
new set: New set
|
||||
open set: Open set
|
||||
|
||||
+17
-8
@@ -75,7 +75,7 @@ void CLISetInterface::setExportInfoCwd() {
|
||||
void CLISetInterface::run() {
|
||||
// show welcome logo
|
||||
if (!quiet) showWelcome();
|
||||
handle_pending_errors();
|
||||
print_pending_errors();
|
||||
// loop
|
||||
running = true;
|
||||
while (running) {
|
||||
@@ -88,6 +88,7 @@ void CLISetInterface::run() {
|
||||
String command = cli.getLine();
|
||||
if (command.empty() && !cli.canGetLine()) break;
|
||||
handleCommand(command);
|
||||
print_pending_errors();
|
||||
cli.flush();
|
||||
cli.flushRaw();
|
||||
}
|
||||
@@ -134,7 +135,7 @@ void CLISetInterface::handleCommand(const String& command) {
|
||||
showUsage();
|
||||
} else if (before == _(":l") || before == _(":load")) {
|
||||
if (arg.empty()) {
|
||||
cli.showError(_("Give a filename to open."));
|
||||
cli.show_message(MESSAGE_ERROR,_("Give a filename to open."));
|
||||
} else {
|
||||
setSet(import_set(arg));
|
||||
}
|
||||
@@ -154,10 +155,10 @@ void CLISetInterface::handleCommand(const String& command) {
|
||||
}
|
||||
} else if (before == _(":c") || before == _(":cd")) {
|
||||
if (arg.empty()) {
|
||||
cli.showError(_("Give a new working directory."));
|
||||
cli.show_message(MESSAGE_ERROR,_("Give a new working directory."));
|
||||
} else {
|
||||
if (!wxSetWorkingDirectory(arg)) {
|
||||
cli.showError(_("Can't change working directory to ")+arg);
|
||||
cli.show_message(MESSAGE_ERROR,_("Can't change working directory to ")+arg);
|
||||
} else {
|
||||
setExportInfoCwd();
|
||||
}
|
||||
@@ -166,7 +167,7 @@ void CLISetInterface::handleCommand(const String& command) {
|
||||
cli << ei.directory_absolute << ENDL;
|
||||
} else if (before == _(":!")) {
|
||||
if (arg.empty()) {
|
||||
cli.showError(_("Give a shell command to execute."));
|
||||
cli.show_message(MESSAGE_ERROR,_("Give a shell command to execute."));
|
||||
} else {
|
||||
#ifdef UNICODE
|
||||
#ifdef __WXMSW__
|
||||
@@ -190,7 +191,7 @@ void CLISetInterface::handleCommand(const String& command) {
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
cli.showError(_("Unknown command, type :help for help."));
|
||||
cli.show_message(MESSAGE_ERROR,_("Unknown command, type :help for help."));
|
||||
}
|
||||
} else if (command == _("exit") || command == _("quit")) {
|
||||
cli << _("Use :quit to quit\n");
|
||||
@@ -201,7 +202,7 @@ void CLISetInterface::handleCommand(const String& command) {
|
||||
vector<ScriptParseError> errors;
|
||||
ScriptP script = parse(command,nullptr,false,errors);
|
||||
if (!errors.empty()) {
|
||||
FOR_EACH(error,errors) cli.showError(error.what());
|
||||
FOR_EACH(error,errors) cli.show_message(MESSAGE_ERROR,error.what());
|
||||
return;
|
||||
}
|
||||
// execute command
|
||||
@@ -212,7 +213,7 @@ void CLISetInterface::handleCommand(const String& command) {
|
||||
cli << result->toCode() << ENDL;
|
||||
}
|
||||
} catch (const Error& e) {
|
||||
cli.showError(e.what());
|
||||
cli.show_message(MESSAGE_ERROR,e.what());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,3 +236,11 @@ void CLISetInterface::handleCommand(const String& command) {
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void CLISetInterface::print_pending_errors() {
|
||||
MessageType type;
|
||||
String msg;
|
||||
while (get_queued_message(type,msg)) {
|
||||
cli.show_message(type,msg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ class CLISetInterface : public SetView {
|
||||
#if USE_SCRIPT_PROFILING
|
||||
void showProfilingStats(const FunctionProfile& parent, int level = 0);
|
||||
#endif
|
||||
void print_pending_errors();
|
||||
|
||||
/// our own context, when no set is loaded
|
||||
Context& getContext();
|
||||
|
||||
+22
-15
@@ -35,7 +35,7 @@ TextIOHandler cli;
|
||||
|
||||
void TextIOHandler::init() {
|
||||
bool have_stderr;
|
||||
#ifdef __WXMSW__
|
||||
#if defined(__WXMSW__)
|
||||
have_console = false;
|
||||
escapes = false;
|
||||
// Detect whether to use console output
|
||||
@@ -51,9 +51,20 @@ void TextIOHandler::init() {
|
||||
}
|
||||
}
|
||||
#else
|
||||
// always use console on *nix (?)
|
||||
have_console = true;
|
||||
have_stderr = true;
|
||||
// TODO: detect console on linux?
|
||||
have_console = false;
|
||||
have_stderr = false;
|
||||
// Use console mode if one of the cli flags is passed
|
||||
static const Char* redirect_flags[] = {_("-?"),_("--help"),_("-v"),_("--version"),_("--cli"),_("-c"),_("--export"),_("--create-installer")};
|
||||
for (int i = 1 ; i < wxTheApp->argc ; ++i) {
|
||||
for (int j = 0 ; j < sizeof(redirect_flags)/sizeof(redirect_flags[0]) ; ++j) {
|
||||
if (String(wxTheApp->argv[i]) == redirect_flags[j]) {
|
||||
have_console = true;
|
||||
have_stderr = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
escapes = true; // TODO: detect output redirection
|
||||
#endif
|
||||
// write to standard output
|
||||
@@ -154,18 +165,14 @@ void TextIOHandler::flushRaw() {
|
||||
|
||||
// ----------------------------------------------------------------------------- : Errors
|
||||
|
||||
void TextIOHandler::showError(const String& message) {
|
||||
void TextIOHandler::show_message(MessageType type, String const& message) {
|
||||
stream = stdout;
|
||||
*this << RED << _("ERROR: ") << NORMAL << replace_all(message,_("\n"),_("\n ")) << ENDL;
|
||||
if (type == MESSAGE_WARNING) {
|
||||
*this << YELLOW << _("WARNING: ") << NORMAL << replace_all(message,_("\n"),_("\n ")) << ENDL;
|
||||
} else {
|
||||
*this << RED << _("ERROR: ") << NORMAL << replace_all(message,_("\n"),_("\n ")) << ENDL;
|
||||
}
|
||||
flush();
|
||||
stream = stdout;
|
||||
if (raw_mode) raw_mode_status = max(raw_mode_status, 2);
|
||||
}
|
||||
|
||||
void TextIOHandler::showWarning(const String& message) {
|
||||
stream = stdout;
|
||||
*this << YELLOW << _("WARNING: ") << NORMAL << replace_all(message,_("\n"),_("\n ")) << ENDL;
|
||||
flush();
|
||||
stream = stdout;
|
||||
if (raw_mode) raw_mode_status = max(raw_mode_status, 1);
|
||||
if (raw_mode) raw_mode_status = max(raw_mode_status, type == MESSAGE_WARNING ? 1 : 2);
|
||||
}
|
||||
|
||||
@@ -36,10 +36,8 @@ class TextIOHandler {
|
||||
/// Flush output
|
||||
void flush();
|
||||
|
||||
/// Show an error message
|
||||
void showError(const String& message);
|
||||
/// Show a warning message
|
||||
void showWarning(const String& message);
|
||||
/// Show an error or warning message
|
||||
void show_message(MessageType type, String const& message);
|
||||
|
||||
/// Enable raw mode
|
||||
void enableRaw();
|
||||
|
||||
+1
-1
@@ -60,7 +60,7 @@ FontP Font::make(int add_flags, AColor* other_color, double* other_size) const {
|
||||
f->color = Color(128,0,0);
|
||||
}
|
||||
if (add_flags & FONT_CODE_KW) {
|
||||
f->color = Color(158,0,0);
|
||||
f->color = Color(158,100,0);
|
||||
f->flags |= FONT_BOLD;
|
||||
}
|
||||
if (add_flags & FONT_SOFT) {
|
||||
|
||||
@@ -215,7 +215,7 @@ void Keyword::prepare(const vector<KeywordParamP>& param_types, bool force) {
|
||||
// throwing an error can mean a set will not be loaded!
|
||||
// instead, simply disable the keyword
|
||||
//throw InternalError(_("Unknown keyword parameter type: ") + type);
|
||||
handle_error(_("Unknown keyword parameter type: ") + type, true, false);
|
||||
handle_error(_("Unknown keyword parameter type: ") + type);
|
||||
valid = false;
|
||||
return;
|
||||
}
|
||||
@@ -700,7 +700,7 @@ bool KeywordDatabase::tryExpand(const Keyword& kw,
|
||||
try {
|
||||
reminder = kw.reminder.invoke(ctx)->toString();
|
||||
} catch (const Error& e) {
|
||||
handle_error(_ERROR_2_("in keyword reminder", e.what(), kw.keyword), true, false);
|
||||
handle_error(_ERROR_2_("in keyword reminder", e.what(), kw.keyword));
|
||||
}
|
||||
ctx.setVariable(_("keyword"), to_script(total));
|
||||
ctx.setVariable(_("reminder"), to_script(reminder));
|
||||
|
||||
+2
-2
@@ -86,7 +86,7 @@ SubLocaleP find_wildcard_and_set(map<String,SubLocaleP>& items, const String& na
|
||||
// ----------------------------------------------------------------------------- : Translation
|
||||
|
||||
String warn_and_identity(const String& key) {
|
||||
handle_warning(_("Missing key in locale: ") + key, false);
|
||||
queue_message(MESSAGE_WARNING, _("Missing key in locale: ") + key);
|
||||
return key;
|
||||
}
|
||||
|
||||
@@ -253,7 +253,7 @@ void Locale::validate(Version ver) {
|
||||
+ _("\n found: ") + ver.toString();
|
||||
}
|
||||
if (!errors.empty()) {
|
||||
handle_warning(errors);
|
||||
queue_message(MESSAGE_WARNING, errors);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -121,7 +121,7 @@ void StatsCategory::find_dimensions(const vector<StatsDimensionP>& available) {
|
||||
}
|
||||
}
|
||||
if (!dim) {
|
||||
handle_error(_ERROR_1_("dimension not found",n),false);
|
||||
handle_error(_ERROR_1_("dimension not found",n));
|
||||
} else {
|
||||
dimensions.push_back(dim);
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ void HtmlExportWindow::onOk(wxCommandEvent&) {
|
||||
void HtmlExportWindow::onTemplateSelect(wxCommandEvent&) {
|
||||
wxBusyCursor wait;
|
||||
ExportTemplateP export_template = list->getSelection<ExportTemplate>();
|
||||
handle_pending_errors();
|
||||
//handle_pending_errors(); // errors are ignored until set window is shown
|
||||
options->showExport(export_template);
|
||||
settings.gameSettingsFor(*set->game).default_export = export_template->name();
|
||||
UpdateWindowUI(wxUPDATE_UI_RECURSE);
|
||||
|
||||
@@ -61,7 +61,7 @@ NewSetWindow::NewSetWindow(Window* parent)
|
||||
void NewSetWindow::onGameSelect(wxCommandEvent&) {
|
||||
wxBusyCursor wait;
|
||||
GameP game = game_list->getSelection<Game>(false);
|
||||
handle_pending_errors();
|
||||
//handle_pending_errors(); // errors are ignored until set window is shown
|
||||
settings.default_game = game->name();
|
||||
stylesheet_list->showData<StyleSheet>(game->name() + _("-*"));
|
||||
stylesheet_list->select(settings.gameSettingsFor(*game).default_stylesheet);
|
||||
@@ -76,7 +76,7 @@ void NewSetWindow::onStyleSheetSelect(wxCommandEvent&) {
|
||||
// store this as default selection
|
||||
GameP game = game_list ->getSelection<Game>(false);
|
||||
StyleSheetP stylesheet = stylesheet_list->getSelection<StyleSheet>(false);
|
||||
handle_pending_errors();
|
||||
//handle_pending_errors(); // errors are ignored until set window is shown
|
||||
settings.gameSettingsFor(*game).default_stylesheet = stylesheet->name();
|
||||
UpdateWindowUI(wxUPDATE_UI_RECURSE);
|
||||
}
|
||||
@@ -113,7 +113,7 @@ void NewSetWindow::onUpdateUI(wxUpdateUIEvent& ev) {
|
||||
|
||||
void NewSetWindow::onIdle(wxIdleEvent& ev) {
|
||||
// Stuff that must be done in the main thread
|
||||
handle_pending_errors();
|
||||
//handle_pending_errors(); // errors are ignored until set window is shown
|
||||
}
|
||||
|
||||
BEGIN_EVENT_TABLE(NewSetWindow, wxDialog)
|
||||
@@ -162,7 +162,7 @@ SelectStyleSheetWindow::SelectStyleSheetWindow(Window* parent, const Game& game,
|
||||
}
|
||||
|
||||
void SelectStyleSheetWindow::onStyleSheetSelect(wxCommandEvent&) {
|
||||
handle_pending_errors();
|
||||
//handle_pending_errors(); // errors are ignored until set window is shown
|
||||
UpdateWindowUI(wxUPDATE_UI_RECURSE);
|
||||
}
|
||||
void SelectStyleSheetWindow::onStyleSheetActivate(wxCommandEvent&) {
|
||||
@@ -193,7 +193,7 @@ void SelectStyleSheetWindow::onUpdateUI(wxUpdateUIEvent& ev) {
|
||||
|
||||
void SelectStyleSheetWindow::onIdle(wxIdleEvent& ev) {
|
||||
// Stuff that must be done in the main thread
|
||||
handle_pending_errors();
|
||||
//handle_pending_errors(); // errors are ignored until set window is shown
|
||||
}
|
||||
|
||||
BEGIN_EVENT_TABLE(SelectStyleSheetWindow, wxDialog)
|
||||
|
||||
@@ -0,0 +1,401 @@
|
||||
//+----------------------------------------------------------------------------+
|
||||
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
|
||||
//| Copyright: (C) 2001 - 2010 Twan van Laarhoven and Sean Hunt |
|
||||
//| License: GNU General Public License 2 or later (see file COPYING) |
|
||||
//+----------------------------------------------------------------------------+
|
||||
|
||||
// ----------------------------------------------------------------------------- : Includes
|
||||
|
||||
#include <util/prec.hpp>
|
||||
#include <gui/set/console_panel.hpp>
|
||||
#include <gui/control/text_ctrl.hpp>
|
||||
#include <gui/util.hpp>
|
||||
#include <util/window_id.hpp>
|
||||
#include <data/stylesheet.hpp>
|
||||
#include <wx/splitter.h>
|
||||
#include <wx/dcbuffer.h>
|
||||
|
||||
DECLARE_POINTER_TYPE(ConsoleMessage);
|
||||
DECLARE_TYPEOF_COLLECTION(ScriptParseError);
|
||||
DECLARE_TYPEOF_COLLECTION(ConsoleMessageP);
|
||||
|
||||
// ----------------------------------------------------------------------------- : MessageControl
|
||||
|
||||
class ConsoleMessage : public IntrusivePtrBase<ConsoleMessage> {
|
||||
public:
|
||||
MessageType type;
|
||||
String text; // string message
|
||||
Bitmap bitmap; // image message instead of string
|
||||
ScriptValueP value; // other valued message (images? cards?)
|
||||
// location of error messages
|
||||
String source_file;
|
||||
int line_number;
|
||||
// layout
|
||||
bool joined_to_previous;
|
||||
int top;
|
||||
int height;
|
||||
int bottom() const { return top+height; }
|
||||
|
||||
ConsoleMessage(MessageType type, String const& text = _(""))
|
||||
: type(type), text(text), line_number(-1), joined_to_previous(false), top(-1), height(-1)
|
||||
{}
|
||||
};
|
||||
|
||||
class MessageCtrl : public wxScrolledWindow {
|
||||
public:
|
||||
MessageCtrl(wxWindow* parent, int id)
|
||||
: wxScrolledWindow(parent, id, wxDefaultPosition, wxDefaultSize, wxBORDER_THEME)
|
||||
{
|
||||
SetBackgroundStyle(wxBG_STYLE_CUSTOM);
|
||||
SetScrollRate(0, 1);
|
||||
EnableScrolling(false,true);
|
||||
// icons
|
||||
BOOST_STATIC_ASSERT(MESSAGE_TYPE_MAX == 6);
|
||||
icons[MESSAGE_INPUT] = wxBitmap(load_resource_image(_("message_input")));
|
||||
icons[MESSAGE_OUTPUT] = wxBitmap();
|
||||
icons[MESSAGE_INFO] = wxBitmap(load_resource_image(_("message_information")));
|
||||
icons[MESSAGE_WARNING] = wxBitmap(load_resource_image(_("message_warning")));
|
||||
icons[MESSAGE_ERROR] = wxBitmap(load_resource_image(_("message_error")));
|
||||
icons[MESSAGE_FATAL_ERROR] = icons[MESSAGE_ERROR];
|
||||
// color
|
||||
colors[MESSAGE_INPUT] = wxColour(0,80,0);
|
||||
colors[MESSAGE_OUTPUT] = wxColour(255,255,255);
|
||||
colors[MESSAGE_INFO] = wxColour(0,0,255);
|
||||
colors[MESSAGE_WARNING] = wxColour(255,255,0);
|
||||
colors[MESSAGE_ERROR] = colors[MESSAGE_FATAL_ERROR] = wxColour(255,0,0);
|
||||
}
|
||||
|
||||
void add_message(ConsoleMessageP const& msg) {
|
||||
messages.push_back(msg);
|
||||
layout_all(messages.size() - 1);
|
||||
// refresh
|
||||
ensure_visible(*messages.back());
|
||||
Refresh(false);
|
||||
}
|
||||
void add_message(MessageType type, String const& text) {
|
||||
add_message(intrusive(new ConsoleMessage(type,text)));
|
||||
}
|
||||
|
||||
private:
|
||||
DECLARE_EVENT_TABLE();
|
||||
|
||||
// --------------------------------------------------- : Data
|
||||
|
||||
// the messages
|
||||
vector<ConsoleMessageP> messages;
|
||||
size_t selection;
|
||||
wxBitmap icons[MESSAGE_TYPE_MAX];
|
||||
wxColour colors[MESSAGE_TYPE_MAX];
|
||||
|
||||
// --------------------------------------------------- : Events
|
||||
|
||||
void onLeftDown(wxMouseEvent& ev) {
|
||||
int ystart; GetViewStart(nullptr,&ystart);
|
||||
selection = find_point(ystart + ev.GetY());
|
||||
if (selection < messages.size()) {
|
||||
ensure_visible(*messages[selection]);
|
||||
}
|
||||
Refresh(false);
|
||||
}
|
||||
|
||||
int find_point(int y) {
|
||||
// TODO: could do a binary search here
|
||||
for (size_t i = 0 ; i < messages.size() ; ++i) {
|
||||
if (y >= messages[i]->top && y < messages[i]->bottom()) return i;
|
||||
}
|
||||
return (size_t)-1;
|
||||
}
|
||||
|
||||
void ensure_visible(ConsoleMessage const& msg) {
|
||||
int ystart; GetViewStart(nullptr,&ystart);
|
||||
int height = GetClientSize().y;
|
||||
if (msg.top < ystart) {
|
||||
Scroll(0, msg.top);
|
||||
} else if (msg.bottom() > ystart + height) {
|
||||
Scroll(0, msg.bottom() - height);
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------------- : Drawing
|
||||
|
||||
void onPaint(wxPaintEvent& ev) {
|
||||
wxAutoBufferedPaintDC dc(this);
|
||||
PrepareDC(dc);
|
||||
draw(dc);
|
||||
}
|
||||
void draw(wxDC& dc) const {
|
||||
clearDC(dc, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
|
||||
dc.SetFont(*wxNORMAL_FONT);
|
||||
FOR_EACH_CONST(msg, messages) {
|
||||
draw(dc, *msg);
|
||||
}
|
||||
if (messages.empty()) {
|
||||
// Say something about no messages?
|
||||
}
|
||||
}
|
||||
|
||||
void draw(wxDC& dc, ConsoleMessage const& msg) const {
|
||||
int left = 0;
|
||||
int top = msg.top;
|
||||
int width = GetClientSize().x;
|
||||
wxColour color = colors[msg.type];
|
||||
wxColour bg, fg;
|
||||
if (selection < messages.size() && messages[selection].get() == &msg) {
|
||||
bg = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT);
|
||||
fg = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT);
|
||||
} else {
|
||||
bg = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
|
||||
fg = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
|
||||
}
|
||||
|
||||
// draw background
|
||||
dc.SetPen(*wxTRANSPARENT_PEN);
|
||||
dc.SetBrush(lerp(bg,color, 0.05));
|
||||
dc.DrawRectangle(left,top,width,msg.height);
|
||||
|
||||
// draw icon
|
||||
if (icons[msg.type].Ok()) {
|
||||
dc.DrawBitmap(icons[msg.type], left + ICON_PADDING,top + ICON_PADDING);
|
||||
}
|
||||
|
||||
// draw text
|
||||
dc.SetTextForeground(fg);
|
||||
int text_left = TEXT_PADDING_LEFT;
|
||||
int text_top = top + TEXT_PADDING_TOP;
|
||||
// find line breaks in the text
|
||||
String::const_iterator begin = msg.text.begin();
|
||||
String::const_iterator it = begin;
|
||||
while (it != msg.text.end()) {
|
||||
if (*it == _('\n')) {
|
||||
// break here
|
||||
dc.DrawText(String(begin,it), text_left, text_top);
|
||||
begin = it = it + 1;
|
||||
text_top += dc.GetCharHeight() + TEXT_LINE_SPACING;
|
||||
} else {
|
||||
it++;
|
||||
}
|
||||
}
|
||||
if (begin != msg.text.end()) {
|
||||
dc.DrawText(String(begin,it), text_left, text_top);
|
||||
text_top += dc.GetCharHeight() + TEXT_LINE_SPACING;
|
||||
}
|
||||
|
||||
// draw bitmap
|
||||
if (msg.bitmap.Ok()) {
|
||||
dc.DrawBitmap(msg.bitmap, text_left, text_top);
|
||||
text_top += msg.bitmap.GetHeight();
|
||||
}
|
||||
|
||||
// draw line below item
|
||||
dc.SetPen(lerp(bg,fg, 0.3));
|
||||
dc.DrawLine(left, top+msg.height, left+width, top+msg.height);
|
||||
}
|
||||
|
||||
int item_height(wxDC& dc, ConsoleMessage const& msg) const {
|
||||
// text height
|
||||
int text_height = 0;
|
||||
// find line breaks in the text
|
||||
String::const_iterator begin = msg.text.begin();
|
||||
String::const_iterator it = begin;
|
||||
while (it != msg.text.end()) {
|
||||
if (*it == _('\n')) {
|
||||
// break here
|
||||
begin = it = it + 1;
|
||||
text_height += dc.GetCharHeight() + TEXT_LINE_SPACING;
|
||||
} else {
|
||||
it++;
|
||||
}
|
||||
// TODO: break long lines
|
||||
}
|
||||
if (begin != msg.text.end()) {
|
||||
text_height += dc.GetCharHeight() + TEXT_LINE_SPACING;
|
||||
}
|
||||
|
||||
// height of bitmap
|
||||
int bitmap_height = msg.bitmap.Ok() ? msg.bitmap.GetHeight() : 0;
|
||||
|
||||
return max(MIN_ITEM_HEIGHT, TEXT_PADDING_TOP + TEXT_PADDING_BOTTOM + text_height + bitmap_height) + LIST_SPACING;
|
||||
}
|
||||
|
||||
// --------------------------------------------------- : Layout
|
||||
|
||||
static const int LIST_SPACING = 1;
|
||||
static const int ICON_PADDING = 3;
|
||||
static const int TEXT_PADDING_LEFT = ICON_PADDING + 16 + 4;
|
||||
static const int TEXT_PADDING_RIGHT = 4;
|
||||
static const int TEXT_PADDING_TOP = 4;
|
||||
static const int TEXT_PADDING_BOTTOM = 2;
|
||||
static const int TEXT_LINE_SPACING = 1;
|
||||
static const int MIN_ITEM_HEIGHT = 16 + 2*ICON_PADDING;
|
||||
|
||||
/// Layout all messages, starting from number start
|
||||
/// layout = determine their height
|
||||
void layout_all(size_t start = 0) {
|
||||
// scratch dc, for finding text sizes
|
||||
wxMemoryDC dc;
|
||||
wxBitmap bmp(1,1);
|
||||
dc.SelectObject(bmp);
|
||||
dc.SetFont(*wxNORMAL_FONT);
|
||||
|
||||
for (size_t i = start ; i < messages.size() ; ++i) {
|
||||
// layout a single item
|
||||
ConsoleMessage& msg = *messages[i];
|
||||
msg.top = start == 0 ? 0 : messages[i-1]->bottom() + LIST_SPACING;
|
||||
if (i > 0 && msg.joined_to_previous) msg.top -= LIST_SPACING;
|
||||
// text size
|
||||
msg.height = item_height(dc, msg);
|
||||
}
|
||||
|
||||
// set size of the control
|
||||
if (messages.empty()) {
|
||||
SetVirtualSize(-1, 0);
|
||||
} else {
|
||||
int height = messages.back()->bottom();
|
||||
SetVirtualSize(-1, height);
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------------- : Layout
|
||||
|
||||
};
|
||||
|
||||
BEGIN_EVENT_TABLE(MessageCtrl,wxScrolledWindow)
|
||||
EVT_PAINT(MessageCtrl::onPaint)
|
||||
EVT_LEFT_DOWN(MessageCtrl::onLeftDown)
|
||||
END_EVENT_TABLE()
|
||||
|
||||
// ----------------------------------------------------------------------------- : ConsolePanel
|
||||
|
||||
ConsolePanel::ConsolePanel(Window* parent, int id)
|
||||
: SetWindowPanel(parent, id)
|
||||
{
|
||||
// init controls
|
||||
splitter = new wxSplitterWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
|
||||
messages = new MessageCtrl(splitter, wxID_ANY);
|
||||
entry_panel = new Panel(splitter, wxID_ANY);
|
||||
entry = new wxTextCtrl(entry_panel, wxID_ANY, _(""), wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER);
|
||||
wxButton* evaluate = new wxButton(entry_panel, ID_EVALUATE, _BUTTON_("evaluate"));
|
||||
// init sizer for entry_panel
|
||||
wxSizer* se = new wxBoxSizer(wxHORIZONTAL);
|
||||
se->Add(entry, 1, wxEXPAND, 2);
|
||||
se->Add(evaluate, 0, wxEXPAND | wxLEFT, 2);
|
||||
entry_panel->SetSizer(se);
|
||||
// init splitter
|
||||
splitter->SetMinimumPaneSize(40);
|
||||
splitter->SetSashGravity(1.0);
|
||||
splitter->SplitHorizontally(messages, entry_panel, -50);
|
||||
// init sizer
|
||||
wxSizer* s = new wxBoxSizer(wxVERTICAL);
|
||||
s->Add(splitter, 1, wxEXPAND);
|
||||
s->SetSizeHints(this);
|
||||
SetSizer(s);
|
||||
}
|
||||
|
||||
void ConsolePanel::onChangeSet() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : UI
|
||||
|
||||
void ConsolePanel::initUI(wxToolBar* tb, wxMenuBar* mb) {
|
||||
// Menus
|
||||
// focus on entry
|
||||
entry->SetFocus();
|
||||
}
|
||||
|
||||
void ConsolePanel::destroyUI(wxToolBar* tb, wxMenuBar* mb) {
|
||||
// Toolbar
|
||||
// Menus
|
||||
}
|
||||
|
||||
void ConsolePanel::onUpdateUI(wxUpdateUIEvent& ev) {
|
||||
if (ev.GetId() == ID_EVALUATE) {
|
||||
ev.Enable(!entry->GetValue().empty());
|
||||
}
|
||||
}
|
||||
|
||||
void ConsolePanel::onEnter(wxCommandEvent& ev) {
|
||||
onCommand(ID_EVALUATE);
|
||||
}
|
||||
void ConsolePanel::onCommand(int id) {
|
||||
if (id == ID_EVALUATE) {
|
||||
exec(entry->GetValue());
|
||||
entry->SetValue(_(""));
|
||||
}
|
||||
}
|
||||
|
||||
void ConsolePanel::onIdle(wxIdleEvent&) {
|
||||
get_pending_errors();
|
||||
}
|
||||
|
||||
void ConsolePanel::get_pending_errors() {
|
||||
// add pending messages
|
||||
MessageType type;
|
||||
String msg;
|
||||
while (get_queued_message(type,msg)) {
|
||||
messages->add_message(type,msg);
|
||||
}
|
||||
// If this panel doesn't have the focus, then highlight it somehow
|
||||
|
||||
}
|
||||
|
||||
void ConsolePanel::exec(String const& command) {
|
||||
if (command.empty()) return;
|
||||
// add input message
|
||||
messages->add_message(MESSAGE_INPUT, command);
|
||||
try {
|
||||
// parse command
|
||||
vector<ScriptParseError> errors;
|
||||
ScriptP script = parse(command,nullptr,false,errors);
|
||||
if (!errors.empty()) {
|
||||
FOR_EACH(error,errors) {
|
||||
// TODO: also squiglify the input?
|
||||
messages->add_message(MESSAGE_ERROR,error.what());
|
||||
}
|
||||
return;
|
||||
}
|
||||
// execute command
|
||||
//WITH_DYNAMIC_ARG(export_info, &ei); // TODO: allow image export
|
||||
Context& ctx = set->getContext();
|
||||
ScriptValueP result = ctx.eval(*script,false);
|
||||
get_pending_errors();
|
||||
// show result
|
||||
ConsoleMessageP message = intrusive(new ConsoleMessage(MESSAGE_OUTPUT));
|
||||
message->joined_to_previous = true;
|
||||
message->value = result;
|
||||
// type of result
|
||||
ScriptType type = result->type();
|
||||
if (type == SCRIPT_IMAGE) {
|
||||
GeneratedImage::Options options(0,0, set->stylesheet.get(), set.get());
|
||||
wxImage image = result->toImage(result)->generate(options);
|
||||
message->bitmap = wxBitmap(image);
|
||||
} else if (type == SCRIPT_COLOR) {
|
||||
message->text = result->toCode();
|
||||
AColor color = (AColor)*result;
|
||||
wxImage image(30,20);
|
||||
fill_image(image,color);
|
||||
set_alpha(image, color.alpha);
|
||||
message->bitmap = wxBitmap(image);
|
||||
} else {
|
||||
message->text = result->toCode();
|
||||
}
|
||||
messages->add_message(message);
|
||||
} catch (ScriptError const& e) {
|
||||
messages->add_message(MESSAGE_ERROR, e.what());
|
||||
}
|
||||
}
|
||||
|
||||
BEGIN_EVENT_TABLE(ConsolePanel, wxPanel)
|
||||
EVT_TEXT_ENTER(wxID_ANY,ConsolePanel::onEnter)
|
||||
EVT_IDLE(ConsolePanel::onIdle)
|
||||
END_EVENT_TABLE ()
|
||||
|
||||
// ----------------------------------------------------------------------------- : Clipboard
|
||||
/*
|
||||
bool ConsolePanel::canCut() const { return entry->canCut(); }
|
||||
bool ConsolePanel::canCopy() const { return entry->canCopy(); }
|
||||
bool ConsolePanel::canPaste() const { return entry->canPaste(); }
|
||||
void ConsolePanel::doCut() { entry->doCut(); }
|
||||
void ConsolePanel::doCopy() { entry->doCopy(); }
|
||||
void ConsolePanel::doPaste() { entry->doPaste(); }
|
||||
*/
|
||||
@@ -0,0 +1,61 @@
|
||||
//+----------------------------------------------------------------------------+
|
||||
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
|
||||
//| Copyright: (C) 2001 - 2010 Twan van Laarhoven and Sean Hunt |
|
||||
//| License: GNU General Public License 2 or later (see file COPYING) |
|
||||
//+----------------------------------------------------------------------------+
|
||||
|
||||
#ifndef HEADER_GUI_SET_CONSOLE_PANEL
|
||||
#define HEADER_GUI_SET_CONSOLE_PANEL
|
||||
|
||||
// ----------------------------------------------------------------------------- : Includes
|
||||
|
||||
#include <util/prec.hpp>
|
||||
#include <gui/set/panel.hpp>
|
||||
|
||||
class wxSplitterWindow;
|
||||
class MessageCtrl;
|
||||
class TextCtrl;
|
||||
|
||||
// ----------------------------------------------------------------------------- : ConsolePanel
|
||||
|
||||
class ConsolePanel : public SetWindowPanel {
|
||||
public:
|
||||
ConsolePanel(Window* parent, int id);
|
||||
|
||||
// --------------------------------------------------- : UI
|
||||
|
||||
void onIdle(wxIdleEvent&);
|
||||
void onEnter(wxCommandEvent&);
|
||||
virtual void initUI (wxToolBar* tb, wxMenuBar* mb);
|
||||
virtual void destroyUI(wxToolBar* tb, wxMenuBar* mb);
|
||||
virtual void onUpdateUI(wxUpdateUIEvent&);
|
||||
virtual void onCommand(int id);
|
||||
|
||||
// --------------------------------------------------- : Clipboard
|
||||
|
||||
/*
|
||||
virtual bool canCut() const;
|
||||
virtual bool canCopy() const;
|
||||
virtual bool canPaste() const;
|
||||
virtual void doCut();
|
||||
virtual void doCopy();
|
||||
virtual void doPaste();
|
||||
*/
|
||||
|
||||
protected:
|
||||
virtual void onChangeSet();
|
||||
|
||||
private:
|
||||
DECLARE_EVENT_TABLE();
|
||||
|
||||
wxSplitterWindow* splitter;
|
||||
MessageCtrl* messages;
|
||||
wxPanel* entry_panel;
|
||||
wxTextCtrl* entry;
|
||||
|
||||
void get_pending_errors();
|
||||
void exec(String const& code);
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : EOF
|
||||
#endif
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <gui/set/keywords_panel.hpp>
|
||||
#include <gui/set/stats_panel.hpp>
|
||||
#include <gui/set/random_pack_panel.hpp>
|
||||
#include <gui/set/console_panel.hpp>
|
||||
#include <gui/control/card_list.hpp>
|
||||
#include <gui/control/card_viewer.hpp>
|
||||
#include <gui/control/gallery_list.hpp>
|
||||
@@ -145,11 +146,12 @@ SetWindow::SetWindow(Window* parent, const SetP& set)
|
||||
|
||||
// panels
|
||||
addPanel(menuWindow, tabBar, new CardsPanel (this, wxID_ANY), 0, _("window_cards"), _("cards tab"));
|
||||
addPanel(menuWindow, tabBar, new SetInfoPanel (this, wxID_ANY), 1, _("window_set_info"), _("set info tab"));
|
||||
addPanel(menuWindow, tabBar, new StylePanel (this, wxID_ANY), 2, _("window_style"), _("style tab"));
|
||||
addPanel(menuWindow, tabBar, new StylePanel (this, wxID_ANY), 1, _("window_style"), _("style tab"));
|
||||
addPanel(menuWindow, tabBar, new SetInfoPanel (this, wxID_ANY), 2, _("window_set_info"), _("set info tab"));
|
||||
addPanel(menuWindow, tabBar, new KeywordsPanel (this, wxID_ANY), 3, _("window_keywords"), _("keywords tab"));
|
||||
addPanel(menuWindow, tabBar, new StatsPanel (this, wxID_ANY), 4, _("window_statistics"), _("stats tab"));
|
||||
addPanel(menuWindow, tabBar, new RandomPackPanel(this, wxID_ANY), 5, _("window_random_pack"),_("random pack tab"));
|
||||
addPanel(menuWindow, tabBar, new ConsolePanel (this, wxID_ANY), 6, _("window_console"), _("console tab"));
|
||||
selectPanel(ID_WINDOW_CARDS); // select cards panel
|
||||
|
||||
// loose ends
|
||||
@@ -778,7 +780,6 @@ void SetWindow::onChildMenu(wxCommandEvent& ev) {
|
||||
|
||||
void SetWindow::onIdle(wxIdleEvent& ev) {
|
||||
// Stuff that must be done in the main thread
|
||||
handle_pending_errors();
|
||||
show_update_dialog(this);
|
||||
}
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ wxThread::ExitCode ThumbnailThreadWorker::Entry() {
|
||||
try {
|
||||
img = current->generate();
|
||||
} catch (const Error& e) {
|
||||
handle_error(e, false, false);
|
||||
handle_error(e);
|
||||
} catch (...) {
|
||||
}
|
||||
// store in cache
|
||||
@@ -150,7 +150,7 @@ void ThumbnailThread::request(const ThumbnailRequestP& request) {
|
||||
try {
|
||||
img = request->generate();
|
||||
} catch (const Error& e) {
|
||||
handle_error(e, false, false);
|
||||
handle_error(e);
|
||||
} catch (...) {
|
||||
}
|
||||
// store in cache
|
||||
|
||||
@@ -186,7 +186,7 @@ void DropDownChoiceListBase::generateThumbnailImages() {
|
||||
GeneratedImageP img = image_from_script(style().image.getValidScriptP()->eval(ctx));
|
||||
style().choice_images.insert(make_pair(name, ScriptableImage(img)));
|
||||
} catch (const Error& e) {
|
||||
handle_error(Error(e.what() + _("\n while generating choice images for drop down list")),true,false);
|
||||
handle_error(Error(e.what() + _("\n while generating choice images for drop down list")));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,9 @@
|
||||
WelcomeWindow::WelcomeWindow()
|
||||
: Frame(nullptr, wxID_ANY, _TITLE_("magic set editor"), wxDefaultPosition, wxSize(520,380), wxDEFAULT_DIALOG_STYLE | wxTAB_TRAVERSAL | wxCLIP_CHILDREN )
|
||||
, logo (load_resource_image(_("about")))
|
||||
#if USE_BETA_LOGO
|
||||
, logo2(load_resource_image(_("two_beta")))
|
||||
#endif
|
||||
{
|
||||
SetIcon(load_resource_icon(_("app")));
|
||||
|
||||
@@ -86,7 +88,9 @@ void WelcomeWindow::draw(DC& dc) {
|
||||
dc.DrawRectangle(0, 0, ws.GetWidth(), ws.GetHeight());
|
||||
// draw logo
|
||||
dc.DrawBitmap(logo, (ws.GetWidth() - logo.GetWidth()) / 2, 5);
|
||||
dc.DrawBitmap(logo2, ws.GetWidth() - logo2.GetWidth(), ws.GetHeight() - logo2.GetHeight());
|
||||
#if USE_BETA_LOGO
|
||||
dc.DrawBitmap(logo2, ws.GetWidth() - logo2.GetWidth(), ws.GetHeight() - logo2.GetHeight());
|
||||
#endif
|
||||
// draw version number
|
||||
dc.SetFont(wxFont(8, wxSWISS, wxNORMAL, wxNORMAL, false, _("Arial")));
|
||||
dc.SetTextForeground(Color(0,126,176));
|
||||
|
||||
@@ -538,6 +538,14 @@
|
||||
RelativePath=".\gui\set\cards_panel.hpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\gui\set\console_panel.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\gui\set\console_panel.hpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\gui\set\keywords_panel.cpp"
|
||||
>
|
||||
|
||||
@@ -53,7 +53,7 @@ void DataViewer::draw(RotatedDC& dc, const Color& background) {
|
||||
changed_content_properties = true;
|
||||
}
|
||||
} catch (const Error& e) {
|
||||
handle_error(e, false, false);
|
||||
handle_error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -67,7 +67,7 @@ void DataViewer::draw(RotatedDC& dc, const Color& background) {
|
||||
try {
|
||||
drawViewer(dc, *v);
|
||||
} catch (const Error& e) {
|
||||
handle_error(e, false, false);
|
||||
handle_error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -91,7 +91,7 @@ void DataViewer::updateStyles(bool only_content_dependent) {
|
||||
}
|
||||
}
|
||||
} catch (const Error& e) {
|
||||
handle_error(e, false, false);
|
||||
handle_error(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,9 +36,7 @@ void ImageValueViewer::draw(RotatedDC& dc) {
|
||||
if (image.LoadFile(*image_file)) {
|
||||
image.Rescale(w, h);
|
||||
}
|
||||
} catch (Error e) {
|
||||
handle_error(e, false, false); // don't handle now, we are in onPaint
|
||||
}
|
||||
} CATCH_ALL_ERRORS(false);
|
||||
}
|
||||
// nice placeholder
|
||||
if (!image.Ok() && style().default_image.isReady()) {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
After Width: | Height: | Size: 701 B |
Binary file not shown.
|
After Width: | Height: | Size: 778 B |
Binary file not shown.
|
After Width: | Height: | Size: 410 B |
Binary file not shown.
|
After Width: | Height: | Size: 666 B |
@@ -66,6 +66,7 @@ tool/window_style IMAGE "tool/window_style.png"
|
||||
tool/window_keywords IMAGE "tool/window_keywords.png"
|
||||
tool/window_statistics IMAGE "tool/window_statistics.png"
|
||||
tool/window_random_pack IMAGE "tool/window_random_pack.png"
|
||||
tool/window_console IMAGE "tool/window_console.png"
|
||||
|
||||
tool/help IMAGE "tool/help.png"
|
||||
|
||||
@@ -186,6 +187,11 @@ installer_locales IMAGE "../common/installer_locales.png"
|
||||
installer_program IMAGE "../common/installer_program.png"
|
||||
//installer_font IMAGE "../common/installer_font.png"
|
||||
|
||||
message_input IMAGE "../common/message_input.png"
|
||||
message_information IMAGE "../common/message_information.png"
|
||||
message_warning IMAGE "../common/message_warning.png"
|
||||
message_error IMAGE "../common/message_error.png"
|
||||
|
||||
// -------------------------------------------------------- : WX
|
||||
|
||||
wxBITMAP_STD_COLOURS BITMAP "wx/msw/colours.bmp"
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 525 B |
@@ -23,10 +23,10 @@ DECLARE_TYPEOF_COLLECTION(pair<String COMMA ScriptValueP>);
|
||||
|
||||
SCRIPT_FUNCTION(trace) {
|
||||
SCRIPT_PARAM_C(String, input);
|
||||
#ifdef _DEBUG
|
||||
#if defined(_DEBUG) && 0
|
||||
wxLogDebug(_("Trace:\t") + input);
|
||||
#else
|
||||
handle_warning(_("Trace:\t") + input, false);
|
||||
queue_message(MESSAGE_INFO, _("Trace: ") + input);
|
||||
#endif
|
||||
SCRIPT_RETURN(input);
|
||||
}
|
||||
@@ -35,7 +35,7 @@ SCRIPT_FUNCTION(warning) {
|
||||
SCRIPT_PARAM_C(String, input);
|
||||
SCRIPT_PARAM_DEFAULT_C(bool, condition, true);
|
||||
if (condition) {
|
||||
handle_warning(input, true);
|
||||
queue_message(MESSAGE_WARNING, input);
|
||||
}
|
||||
return script_nil;
|
||||
}
|
||||
@@ -51,7 +51,7 @@ SCRIPT_FUNCTION(warning_if_neq) {
|
||||
try {
|
||||
s2 = v2->toCode();
|
||||
} catch (...) {}
|
||||
handle_warning(input + s1 + _(" != ") + s2, true);
|
||||
queue_message(MESSAGE_WARNING, input + s1 + _(" != ") + s2);
|
||||
}
|
||||
return script_nil;
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ Context& SetScriptContext::getContext(const StyleSheetP& stylesheet) {
|
||||
set.game ->init_script.invoke(*ctx, false);
|
||||
stylesheet->init_script.invoke(*ctx, false);
|
||||
} catch (const Error& e) {
|
||||
handle_error(e, false, false);
|
||||
handle_error(e);
|
||||
}
|
||||
onInit(stylesheet, ctx);
|
||||
return *ctx;
|
||||
@@ -108,7 +108,7 @@ void SetScriptManager::onInit(const StyleSheetP& stylesheet, Context* ctx) {
|
||||
initDependencies(*ctx, *set.game);
|
||||
initDependencies(*ctx, *stylesheet);
|
||||
} catch (const Error& e) {
|
||||
handle_error(e, false, false);
|
||||
handle_error(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -258,7 +258,7 @@ void SetScriptManager::updateStyles(Context& ctx, const IndexMap<FieldP,StyleP>&
|
||||
}
|
||||
} catch (const ScriptError& e) {
|
||||
// NOTE: don't handle errors now, we are likely in an onPaint handler
|
||||
handle_error(ScriptError(e.what() + _("\n while updating styles for '") + s->fieldP->name + _("'")), false, false);
|
||||
handle_error(ScriptError(e.what() + _("\n while updating styles for '") + s->fieldP->name + _("'")));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -298,7 +298,7 @@ void SetScriptManager::updateAll() {
|
||||
PROFILER2( v->fieldP.get(), _("update set.") + v->fieldP->name );
|
||||
v->update(ctx);
|
||||
} catch (const ScriptError& e) {
|
||||
handle_error(ScriptError(e.what() + _("\n while updating set value '") + v->fieldP->name + _("'")), false, true);
|
||||
handle_error(ScriptError(e.what() + _("\n while updating set value '") + v->fieldP->name + _("'")));
|
||||
}
|
||||
}
|
||||
// update card data of all cards
|
||||
@@ -312,7 +312,7 @@ void SetScriptManager::updateAll() {
|
||||
#endif
|
||||
v->update(ctx);
|
||||
} catch (const ScriptError& e) {
|
||||
handle_error(ScriptError(e.what() + _("\n while updating card value '") + v->fieldP->name + _("'")), false, true);
|
||||
handle_error(ScriptError(e.what() + _("\n while updating card value '") + v->fieldP->name + _("'")));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -347,7 +347,7 @@ void SetScriptManager::updateToUpdate(const ToUpdate& u, deque<ToUpdate>& to_upd
|
||||
try {
|
||||
changes = u.value->update(ctx);
|
||||
} catch (const ScriptError& e) {
|
||||
handle_error(ScriptError(e.what() + _("\n while updating value '") + u.value->fieldP->name + _("'")), false, true);
|
||||
handle_error(ScriptError(e.what() + _("\n while updating value '") + u.value->fieldP->name + _("'")));
|
||||
}
|
||||
if (changes) {
|
||||
// changed, send event
|
||||
|
||||
+30
-78
@@ -13,6 +13,7 @@
|
||||
#if wxUSE_STACKWALKER
|
||||
#include <wx/stackwalk.h>
|
||||
#endif
|
||||
#include <queue>
|
||||
|
||||
DECLARE_TYPEOF_COLLECTION(ScriptParseError);
|
||||
|
||||
@@ -158,94 +159,45 @@ ScriptParseErrors::ScriptParseErrors(const vector<ScriptParseError>& errors)
|
||||
|
||||
// ----------------------------------------------------------------------------- : Error handling
|
||||
|
||||
// Errors for which a message box was already shown
|
||||
vector<String> previous_errors;
|
||||
vector<String> previous_warnings;
|
||||
String pending_errors;
|
||||
String pending_warnings;
|
||||
DECLARE_TYPEOF_COLLECTION(String);
|
||||
// messages can be posted from other threads, this mutex protects the message list
|
||||
wxMutex crit_error_handling;
|
||||
typedef pair<MessageType,String> Message;
|
||||
deque<Message> message_queue;
|
||||
bool show_message_box_for_fatal_errors = true;
|
||||
bool write_errors_to_cli = false;
|
||||
|
||||
void show_pending_errors();
|
||||
void show_pending_warnings();
|
||||
|
||||
void handle_error(const String& e, bool allow_duplicate = true, bool now = true) {
|
||||
{
|
||||
// Thread safety
|
||||
wxMutexLocker lock(crit_error_handling);
|
||||
// Check duplicates
|
||||
if (!allow_duplicate) {
|
||||
FOR_EACH(pe, previous_errors) {
|
||||
if (e == pe) return;
|
||||
}
|
||||
previous_errors.push_back(e);
|
||||
}
|
||||
// Only show errors in the main thread
|
||||
if (!pending_errors.empty()) pending_errors += _("\n\n");
|
||||
pending_errors += e;
|
||||
void queue_message(MessageType type, String const& msg) {
|
||||
if (write_errors_to_cli && wxThread::IsMain()) {
|
||||
cli.show_message(type,msg);
|
||||
return; // TODO: is this the right thing to do?
|
||||
}
|
||||
// show messages
|
||||
if ((write_errors_to_cli || now) && wxThread::IsMain()) {
|
||||
show_pending_warnings(); // warnings are older, show them first
|
||||
show_pending_errors();
|
||||
if (show_message_box_for_fatal_errors && type == MESSAGE_FATAL_ERROR && wxThread::IsMain()) {
|
||||
// bring this to the user's attention right now!
|
||||
wxMessageBox(msg, _("Error"), wxOK | wxICON_ERROR);
|
||||
}
|
||||
// Thread safety
|
||||
wxMutexLocker lock(crit_error_handling);
|
||||
// Only show errors in the main thread
|
||||
message_queue.push_back(make_pair(type,msg));
|
||||
}
|
||||
|
||||
void handle_error(const Error& e, bool allow_duplicate, bool now) {
|
||||
handle_error(e.what(), allow_duplicate, now);
|
||||
void handle_error(const Error& e) {
|
||||
queue_message(e.is_fatal() ? MESSAGE_FATAL_ERROR : MESSAGE_ERROR, e.what());
|
||||
}
|
||||
|
||||
void handle_warning(const String& w, bool now) {
|
||||
{
|
||||
// Check duplicates
|
||||
wxMutexLocker lock(crit_error_handling);
|
||||
// Check duplicates
|
||||
FOR_EACH(pw, previous_warnings) {
|
||||
if (w == pw) return;
|
||||
}
|
||||
previous_warnings.push_back(w);
|
||||
// Only show errors in the main thread
|
||||
if (!pending_warnings.empty()) pending_warnings += _("\n\n");
|
||||
pending_warnings += w;
|
||||
}
|
||||
// show messages
|
||||
if ((write_errors_to_cli || now) && wxThread::IsMain()) {
|
||||
show_pending_errors();
|
||||
show_pending_warnings();
|
||||
}
|
||||
bool have_queued_message() {
|
||||
wxMutexLocker lock(crit_error_handling);
|
||||
return !message_queue.empty();
|
||||
}
|
||||
|
||||
void handle_pending_errors() {
|
||||
show_pending_errors();
|
||||
show_pending_warnings();
|
||||
}
|
||||
|
||||
void show_pending_errors() {
|
||||
assert(wxThread::IsMain());
|
||||
if (crit_error_handling.TryLock() != wxMUTEX_NO_ERROR)
|
||||
return;
|
||||
if (!pending_errors.empty()) {
|
||||
if (write_errors_to_cli) {
|
||||
cli.showError(pending_errors);
|
||||
} else {
|
||||
wxMessageBox(pending_errors, _("Error"), wxOK | wxICON_ERROR);
|
||||
}
|
||||
pending_errors.clear();
|
||||
bool get_queued_message(MessageType& type, String& msg) {
|
||||
wxMutexLocker lock(crit_error_handling);
|
||||
if (message_queue.empty()) {
|
||||
return false;
|
||||
} else {
|
||||
type = message_queue.back().first;
|
||||
msg = message_queue.back().second;
|
||||
message_queue.pop_back();
|
||||
return true;
|
||||
}
|
||||
crit_error_handling.Unlock();
|
||||
}
|
||||
void show_pending_warnings() {
|
||||
assert(wxThread::IsMain());
|
||||
if (crit_error_handling.TryLock() != wxMUTEX_NO_ERROR)
|
||||
return;
|
||||
if (!pending_warnings.empty()) {
|
||||
if (write_errors_to_cli) {
|
||||
cli.showWarning(pending_warnings);
|
||||
} else {
|
||||
wxMessageBox(pending_warnings, _("Warning"), wxOK | wxICON_EXCLAMATION);
|
||||
}
|
||||
pending_warnings.clear();
|
||||
}
|
||||
crit_error_handling.Unlock();
|
||||
}
|
||||
|
||||
+49
-20
@@ -26,6 +26,8 @@ class Error {
|
||||
|
||||
/// Return the error message
|
||||
virtual String what() const;
|
||||
/// Is the message (potentially) fatal?
|
||||
virtual bool is_fatal() const { return false; }
|
||||
|
||||
protected:
|
||||
String message; ///< The error message
|
||||
@@ -36,6 +38,8 @@ class Error {
|
||||
class InternalError : public Error {
|
||||
public:
|
||||
InternalError(const String& str);
|
||||
// not all internal errors are fatal, but we had still better warn the user about them.
|
||||
virtual bool is_fatal() const { return true; }
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : File errors
|
||||
@@ -128,37 +132,62 @@ class ScriptErrorNoMember : public ScriptError {
|
||||
: ScriptError(_ERROR_2_("has no member", type, member)) {}
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : Error handling
|
||||
// ----------------------------------------------------------------------------- : Bounds checking
|
||||
|
||||
/// Should errors be written to stdout?
|
||||
template <typename T>
|
||||
T& at(vector<T>& x, size_t pos) {
|
||||
if (pos < x.size()) {
|
||||
return x[pos];
|
||||
} else {
|
||||
throw InternalError(_("vector<T> index out of bounds: %d > %d, where T = ") + typeid(x).name());
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Error/message handling
|
||||
|
||||
/// Should a popup be shown for internal errors?
|
||||
extern bool show_message_box_for_fatal_errors;
|
||||
extern bool write_errors_to_cli;
|
||||
|
||||
/// Handle an error by showing a message box
|
||||
/** If !allow_duplicate and the error is the same as the previous error, does nothing.
|
||||
* If !now the error is handled by a later call to handle_pending_errors()
|
||||
/// Types/levels of (error)messages
|
||||
enum MessageType
|
||||
{ MESSAGE_INPUT
|
||||
, MESSAGE_OUTPUT
|
||||
, MESSAGE_INFO
|
||||
, MESSAGE_WARNING
|
||||
, MESSAGE_ERROR
|
||||
, MESSAGE_FATAL_ERROR
|
||||
|
||||
, MESSAGE_TYPE_MAX
|
||||
};
|
||||
|
||||
/// Queue an (error) message, it can later be retrieved with get_queued_message
|
||||
/** If the message is a MESSAGE_FATAL_ERROR, and show_message_box_for_fatal_errors==true, then a popup is shown
|
||||
*/
|
||||
void handle_error(const Error& e, bool allow_duplicate = true, bool now = true);
|
||||
void queue_message(MessageType type, String const& msg);
|
||||
/// Handle an error by queuing a message
|
||||
void handle_error(const Error& e);
|
||||
|
||||
/// Handle a warning by showing a message box
|
||||
void handle_warning(const String& w, bool now = true);
|
||||
/// Are there any queued messages?
|
||||
bool have_queued_message();
|
||||
|
||||
/// Get the first queued message, or return false
|
||||
bool get_queued_message(MessageType& type, String& msg);
|
||||
|
||||
/// Handle errors and warnings that were not handled immediatly in handleError
|
||||
/** Should be called repeatedly (e.g. in an onIdle event handler) */
|
||||
void handle_pending_errors();
|
||||
|
||||
/// Make a stack trace for use in InternalErrors
|
||||
String get_stack_trace();
|
||||
|
||||
/// Catch all types of errors, and pass then to handle_error
|
||||
#define CATCH_ALL_ERRORS(handle_now) \
|
||||
catch (const Error& e) { \
|
||||
handle_error(e, false, handle_now); \
|
||||
} catch (const std::exception& e) { \
|
||||
/* we don't throw std::exception ourselfs, so this is probably something serious */ \
|
||||
String message(e.what(), IF_UNICODE(wxConvLocal, wxSTRING_MAXLEN) ); \
|
||||
handle_error(InternalError(message), false, handle_now); \
|
||||
} catch (...) { \
|
||||
handle_error(InternalError(_("An unexpected exception occurred!")), false, handle_now); \
|
||||
#define CATCH_ALL_ERRORS(handle_now) \
|
||||
catch (const Error& e) { \
|
||||
handle_error(e); \
|
||||
} catch (const std::exception& e) { \
|
||||
/* we don't throw std::exception ourselfs, so this is probably something serious */ \
|
||||
String message(e.what(), IF_UNICODE(wxConvLocal, wxSTRING_MAXLEN) ); \
|
||||
handle_error(InternalError(message)); \
|
||||
} catch (...) { \
|
||||
handle_error(InternalError(_("An unexpected exception occurred!"))); \
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : EOF
|
||||
|
||||
@@ -600,16 +600,16 @@ void Packaged::requireDependency(Packaged* package) {
|
||||
FOR_EACH(dep, dependencies) {
|
||||
if (dep->package == n) {
|
||||
if (package->version < dep->version) {
|
||||
handle_warning(_ERROR_3_("package out of date", n, package->version.toString(), dep->version.toString()), false);
|
||||
queue_message(MESSAGE_WARNING,_ERROR_3_("package out of date", n, package->version.toString(), dep->version.toString()));
|
||||
} else if (package->compatible_version > dep->version) {
|
||||
handle_warning(_ERROR_4_("package too new", n, package->version.toString(), dep->version.toString(), relativeFilename()), false);
|
||||
queue_message(MESSAGE_WARNING,_ERROR_4_("package too new", n, package->version.toString(), dep->version.toString(), relativeFilename()));
|
||||
} else {
|
||||
return; // ok
|
||||
}
|
||||
}
|
||||
}
|
||||
// dependency not found
|
||||
handle_warning(_ERROR_4_("dependency not given", name(), package->relativeFilename(), package->relativeFilename(), package->version.toString()), false);
|
||||
queue_message(MESSAGE_WARNING,_ERROR_4_("dependency not given", name(), package->relativeFilename(), package->relativeFilename(), package->version.toString()));
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : IncludePackage
|
||||
|
||||
@@ -153,20 +153,20 @@ bool PackageManager::checkDependency(const PackageDependency& dep, bool report_e
|
||||
// mse package?
|
||||
if (dep.package == mse_package) {
|
||||
if (app_version < dep.version) {
|
||||
handle_warning(_ERROR_3_("package out of date", _("Magic Set Editor"), app_version.toString(), dep.version.toString()),false);
|
||||
queue_message(MESSAGE_WARNING, _ERROR_3_("package out of date", _("Magic Set Editor"), app_version.toString(), dep.version.toString()));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// does the package exist?
|
||||
if (!local.exists(dep.package) && !global.exists(dep.package)) {
|
||||
if (report_errors)
|
||||
handle_warning(_ERROR_1_("package not found", dep.package),false);
|
||||
queue_message(MESSAGE_WARNING, _ERROR_1_("package not found", dep.package));
|
||||
return false;
|
||||
}
|
||||
PackagedP package = openAny(dep.package, true);
|
||||
if (package->version < dep.version) {
|
||||
if (report_errors)
|
||||
handle_warning(_ERROR_3_("package out of date", dep.package, package->version.toString(), dep.version.toString()),false);
|
||||
queue_message(MESSAGE_WARNING, _ERROR_3_("package out of date", dep.package, package->version.toString(), dep.version.toString()));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -378,7 +378,7 @@ bool PackageDirectory::install(const InstallablePackage& package) {
|
||||
bool PackageDirectory::actual_install(const InstallablePackage& package, const String& install_dir) {
|
||||
String name = package.description->name;
|
||||
if (!package.installer->installer) {
|
||||
handle_warning(_("Installer not found for package: ") + name);
|
||||
queue_message(MESSAGE_ERROR, _("Installer not found for package: ") + name);
|
||||
return false;
|
||||
}
|
||||
Installer& installer = *package.installer->installer;
|
||||
|
||||
@@ -61,7 +61,7 @@ void Reader::handleAppVersion() {
|
||||
if (enterBlock(_("mse_version"))) {
|
||||
handle(file_app_version);
|
||||
if (app_version < file_app_version) {
|
||||
handle_warning(_ERROR_2_("newer version", filename, file_app_version.toString()), false);
|
||||
queue_message(MESSAGE_WARNING, _ERROR_2_("newer version", filename, file_app_version.toString()));
|
||||
}
|
||||
exitBlock();
|
||||
}
|
||||
@@ -75,7 +75,7 @@ void Reader::warning(const String& msg, int line_number_delta, bool warn_on_prev
|
||||
|
||||
void Reader::showWarnings() {
|
||||
if (!warnings.empty()) {
|
||||
handle_warning(_("Warnings while reading file:\n") + filename + _("\n") + warnings, false);
|
||||
queue_message(MESSAGE_WARNING, _("Warnings while reading file:\n") + filename + _("\n") + warnings);
|
||||
warnings.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -598,12 +598,12 @@ void check_tagged(const String& str, bool check_balance) {
|
||||
if (str.GetChar(i) == _('<')) {
|
||||
size_t end = skip_tag(str,i);
|
||||
if (end == String::npos) {
|
||||
handle_warning(_("Invalid tagged string: missing '>'"),false);
|
||||
queue_message(MESSAGE_WARNING, _("Invalid tagged string: missing '>'"));
|
||||
}
|
||||
for (size_t j = i + 1 ; j + 1 < end ; ++j) {
|
||||
Char c = str.GetChar(j);
|
||||
if (c == _(' ') || c == _('<')) {
|
||||
handle_warning(_("Invalid character in tag"),false);
|
||||
queue_message(MESSAGE_WARNING, _("Invalid character in tag"));
|
||||
}
|
||||
}
|
||||
if (check_balance) {
|
||||
@@ -614,7 +614,7 @@ void check_tagged(const String& str, bool check_balance) {
|
||||
} else {
|
||||
size_t close = match_close_tag(str,i);
|
||||
if (close == String::npos) {
|
||||
handle_warning(_("Invalid tagged string: missing close tag for <") + tag_at(str,i) + _(">"),false);
|
||||
queue_message(MESSAGE_WARNING, _("Invalid tagged string: missing close tag for <") + tag_at(str,i) + _(">"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ void SubversionVCS::removeFile(const wxFileName& filename)
|
||||
{
|
||||
String name = filename.GetFullPath();
|
||||
const Char* name_c[] = {_("svn"), _("rm"), name.c_str(), nullptr};
|
||||
handle_warning(String(name_c[0]) + name_c[1] + name_c[2]);
|
||||
queue_message(MESSAGE_WARNING, String(name_c[0]) + name_c[1] + name_c[2]);
|
||||
// TODO: do we really need to remove the file before calling "svn remove"?
|
||||
VCS::removeFile(filename);
|
||||
if (!run_svn(name_c)) {
|
||||
|
||||
@@ -207,6 +207,9 @@ enum ChildMenuID {
|
||||
, ID_GENERATE_PACK
|
||||
, ID_CUSTOM_PACK
|
||||
|
||||
// Console panel
|
||||
, ID_EVALUATE
|
||||
|
||||
// SymbolFont (Format menu)
|
||||
, ID_INSERT_SYMBOL_MENU_MIN = 9001
|
||||
, ID_INSERT_SYMBOL_MENU_MAX = 10000
|
||||
|
||||
Reference in New Issue
Block a user