Instancefy button manager stuff
[lsnes.git] / src / platform / wxwidgets / main.cpp
blob8693655baa07acdb3939791099d4e94c43e98c50
1 #include <wx/wx.h>
3 #include "lsnes.hpp"
5 #include "core/command.hpp"
6 #include "core/controller.hpp"
7 #include "core/dispatch.hpp"
8 #include "core/framerate.hpp"
9 #include "core/joystickapi.hpp"
10 #include "core/keymapper.hpp"
11 #include "core/loadlib.hpp"
12 #include "lua/lua.hpp"
13 #include "core/advdumper.hpp"
14 #include "core/mainloop.hpp"
15 #include "core/misc.hpp"
16 #include "core/instance.hpp"
17 #include "core/moviefile-common.hpp"
18 #include "core/moviedata.hpp"
19 #include "core/random.hpp"
20 #include "core/rom.hpp"
21 #include "core/settings.hpp"
22 #include "core/window.hpp"
23 #include "interface/romtype.hpp"
24 #include "library/crandom.hpp"
25 #include "library/directory.hpp"
26 #include "library/string.hpp"
27 #include "library/threads.hpp"
28 #include "library/utf8.hpp"
29 #include "library/zip.hpp"
31 #include "platform/wxwidgets/settings-common.hpp"
32 #include "platform/wxwidgets/platform.hpp"
33 #include "platform/wxwidgets/window_messages.hpp"
34 #include "platform/wxwidgets/window_status.hpp"
35 #include "platform/wxwidgets/window_mainwindow.hpp"
37 #include <cassert>
38 #include <boost/lexical_cast.hpp>
40 #include <wx/wx.h>
41 #include <wx/event.h>
42 #include <wx/control.h>
43 #include <wx/combobox.h>
44 #include <wx/cmdline.h>
45 #include <iostream>
47 #define UISERV_REFRESH_TITLE 9990
48 #define UISERV_RESIZED 9991
49 #define UISERV_UIFUN 9992
50 //#define UISERV_UI_IRQ 9993 Not in use anymore, can be recycled.
51 #define UISERV_EXIT 9994
52 #define UISERV_UPDATE_STATUS 9995
53 #define UISERV_UPDATE_MESSAGES 9996
54 #define UISERV_UPDATE_SCREEN 9997
55 #define UISERV_PANIC 9998
56 #define UISERV_ERROR 9999
58 wxwin_messages* msg_window;
59 wxwin_mainwindow* main_window;
60 std::string our_rom_name;
62 bool wxwidgets_exiting = false;
64 namespace
66 threads::id ui_thread;
67 volatile bool panic_ack = false;
68 std::string error_message_text;
69 volatile bool modal_dialog_confirm;
70 volatile bool modal_dialog_active;
71 threads::lock ui_mutex;
72 threads::cv ui_condition;
73 threads::thread* joystick_thread_handle;
74 bool if_update_messages = false;
75 bool if_update_screen = false;
76 bool if_update_status = false;
77 threads::lock if_mutex;
79 void* joystick_thread(int _args)
81 joystick_driver_thread_fn();
82 return NULL;
85 struct uiserv_event : public wxEvent
87 uiserv_event(int code)
89 SetId(code);
92 wxEvent* Clone() const
94 return new uiserv_event(*this);
98 class ui_services_type : public wxEvtHandler
100 bool ProcessEvent(wxEvent& event);
103 struct ui_queue_entry
105 void(*fn)(void*);
106 void* arg;
109 std::list<ui_queue_entry> ui_queue;
111 bool ui_services_type::ProcessEvent(wxEvent& event)
113 int c = event.GetId();
114 if(c == UISERV_PANIC) {
115 //We need to panic.
116 wxMessageBox(_T("Panic: Unrecoverable error, can't continue"), _T("Error"), wxICON_ERROR |
117 wxOK);
118 panic_ack = true;
119 } else if(c == UISERV_REFRESH_TITLE) {
120 if(main_window)
121 main_window->refresh_title();
122 } else if(c == UISERV_RESIZED) {
123 if(main_window)
124 main_window->notify_resized();
125 } else if(c == UISERV_ERROR) {
126 std::string text = error_message_text;
127 wxMessageBox(towxstring(text), _T("lsnes: Error"), wxICON_EXCLAMATION | wxOK, main_window);
128 } else if(c == UISERV_UPDATE_MESSAGES) {
129 { threads::alock h(if_mutex); if_update_messages = false; }
130 if(msg_window)
131 msg_window->notify_update();
132 } else if(c == UISERV_UPDATE_STATUS) {
133 { threads::alock h(if_mutex); if_update_status = false; }
134 if(main_window)
135 main_window->notify_update_status();
136 wxeditor_movie_update();
137 wxeditor_hexeditor_update();
138 } else if(c == UISERV_UPDATE_SCREEN) {
139 { threads::alock h(if_mutex); if_update_screen = false; }
140 if(main_window)
141 main_window->notify_update();
142 wxwindow_memorysearch_update();
143 wxwindow_tasinput_update();
144 } else if(c == UISERV_EXIT) {
145 if(main_window)
146 main_window->notify_exit();
147 } else if(c == UISERV_UIFUN) {
148 std::list<ui_queue_entry>::iterator i;
149 ui_queue_entry e;
150 queue_synchronous_fn_warning = true;
151 back:
153 threads::alock h(ui_mutex);
154 if(ui_queue.empty())
155 goto end;
156 i = ui_queue.begin();
157 e = *i;
158 ui_queue.erase(i);
160 e.fn(e.arg);
161 goto back;
162 end:
163 queue_synchronous_fn_warning = false;
165 return true;
168 ui_services_type* ui_services;
170 void post_ui_event(int code)
172 //Coalesce some messages.
173 if(code == UISERV_UPDATE_MESSAGES) {
174 threads::alock h(if_mutex);
175 if(if_update_messages) return;
176 if_update_messages = true;
178 if(code == UISERV_UPDATE_SCREEN) {
179 threads::alock h(if_mutex);
180 if(if_update_screen) return;
181 if_update_screen = true;
183 if(code == UISERV_UPDATE_STATUS) {
184 threads::alock h(if_mutex);
185 if(if_update_status) return;
186 if_update_status = true;
188 uiserv_event uic(code);
189 wxPostEvent(ui_services, uic);
192 void handle_config_line(std::string line)
194 regex_results r;
195 if(r = regex("SET[ \t]+([^ \t]+)[ \t]+(.*)", line)) {
196 lsnes_instance.setcache->set(r[1], r[2], true);
197 messages << "Setting " << r[1] << " set to " << r[2] << std::endl;
198 } else if(r = regex("ALIAS[ \t]+([^ \t]+)[ \t]+(.*)", line)) {
199 if(!lsnes_instance.command->valid_alias_name(r[1])) {
200 messages << "Illegal alias name " << r[1] << std::endl;
201 return;
203 std::string tmp = lsnes_instance.command->get_alias_for(r[1]);
204 tmp = tmp + r[2] + "\n";
205 lsnes_instance.command->set_alias_for(r[1], tmp);
206 messages << r[1] << " aliased to " << r[2] << std::endl;
207 } else if(r = regex("BIND[ \t]+([^/]*)/([^|]*)\\|([^ \t]+)[ \t]+(.*)", line)) {
208 std::string tmp = r[4];
209 regex_results r2 = regex("(load|load-smart|load-readonly|load-preserve|load-state"
210 "|load-movie|save-state|save-movie)[ \t]+\\$\\{project\\}(.*)\\.lsmv", tmp);
211 if(r2) tmp = r2[1] + " $SLOT:" + r2[2];
212 lsnes_instance.mapper->bind(r[1], r[2], r[3], tmp);
213 if(r[1] != "" || r[2] != "")
214 messages << r[1] << "/" << r[2] << " ";
215 messages << r[3] << " bound to '" << tmp << "'" << std::endl;
216 } else if(r = regex("BUTTON[ \t]+([^ \t]+)[ \t](.*)", line)) {
217 keyboard::ctrlrkey* ckey = lsnes_instance.mapper->get_controllerkey(r[2]);
218 if(ckey) {
219 ckey->append(r[1]);
220 messages << r[1] << " bound (button) to " << r[2] << std::endl;
221 } else
222 lsnes_instance.buttons->button_keys[r[2]] = r[1];
223 } else if(r = regex("PREFER[ \t]+([^ \t]+)[ \t]+(.*)", line)) {
224 if(r[2] != "") {
225 core_selections[r[1]] = r[2];
226 messages << "Prefer " << r[2] << " for " << r[1] << std::endl;
228 } else
229 (stringfmt() << "Unrecognized directive: " << line).throwex();
232 void load_configuration()
234 std::string cfg = get_config_path() + "/lsneswxw.cfg";
235 std::ifstream cfgfile(cfg.c_str());
236 std::string line;
237 size_t lineno = 1;
238 while(std::getline(cfgfile, line)) {
239 try {
240 handle_config_line(line);
241 } catch(std::exception& e) {
242 messages << "Error processing line " << lineno << ": " << e.what() << std::endl;
244 lineno++;
246 (*lsnes_instance.abindmanager)();
247 lsnes_uri_rewrite.load(get_config_path() + "/lsnesurirewrite.cfg");
250 void save_configuration()
252 std::string cfg = get_config_path() + "/lsneswxw.cfg";
253 std::string cfgtmp = cfg + ".tmp";
254 std::ofstream cfgfile(cfgtmp.c_str());
255 //Settings.
256 for(auto i : lsnes_instance.setcache->get_all())
257 cfgfile << "SET " << i.first << " " << i.second << std::endl;
258 //Aliases.
259 for(auto i : lsnes_instance.command->get_aliases()) {
260 std::string old_alias_value = lsnes_instance.command->get_alias_for(i);
261 while(old_alias_value != "") {
262 std::string aliasline;
263 size_t s = old_alias_value.find_first_of("\n");
264 if(s < old_alias_value.length()) {
265 aliasline = old_alias_value.substr(0, s);
266 old_alias_value = old_alias_value.substr(s + 1);
267 } else {
268 aliasline = old_alias_value;
269 old_alias_value = "";
271 cfgfile << "ALIAS " << i << " " << aliasline << std::endl;
274 //Keybindings.
275 for(auto i : lsnes_instance.mapper->get_bindings())
276 cfgfile << "BIND " << std::string(i) << " " << lsnes_instance.mapper->get(i) << std::endl;
277 //Buttons.
278 for(auto i : lsnes_instance.mapper->get_controller_keys()) {
279 std::string b;
280 unsigned idx = 0;
281 while((b = i->get_string(idx++)) != "")
282 cfgfile << "BUTTON " << b << " " << i->get_command() << std::endl;
284 for(auto i : lsnes_instance.buttons->button_keys)
285 cfgfile << "BUTTON " << i.second << " " << i.first << std::endl;
286 for(auto i : core_selections)
287 if(i.second != "")
288 cfgfile << "PREFER " << i.first << " " << i.second << std::endl;
289 if(!cfgfile) {
290 show_message_ok(NULL, "Error Saving configuration", "Error saving configuration",
291 wxICON_EXCLAMATION);
292 return;
294 cfgfile.close();
295 directory::rename_overwrite(cfgtmp.c_str(), cfg.c_str());
296 //Last save.
297 std::ofstream lsave(get_config_path() + "/" + our_rom_name + ".ls");
298 lsave << last_save;
299 lsnes_uri_rewrite.save(get_config_path() + "/lsnesurirewrite.cfg");
302 void* eloop_helper(int x)
304 platform::dummy_event_loop();
305 return NULL;
308 std::string get_loaded_movie(const std::vector<std::string>& cmdline)
310 for(auto i : cmdline)
311 if(!i.empty() && i[0] != '-')
312 return i;
313 return "";
317 wxString towxstring(const std::string& str) throw(std::bad_alloc)
319 return wxString(str.c_str(), wxConvUTF8);
322 std::string tostdstring(const wxString& str) throw(std::bad_alloc)
324 return std::string(str.mb_str(wxConvUTF8));
327 wxString towxstring(const std::u32string& str) throw(std::bad_alloc)
329 return wxString(utf8::to8(str).c_str(), wxConvUTF8);
332 std::u32string tou32string(const wxString& str) throw(std::bad_alloc)
334 return utf8::to32(std::string(str.mb_str(wxConvUTF8)));
337 std::string pick_archive_member(wxWindow* parent, const std::string& filename) throw(std::bad_alloc)
339 //Did we pick a .zip file?
340 std::string f;
341 try {
342 zip::reader zr(filename);
343 std::vector<wxString> files;
344 for(auto i : zr)
345 files.push_back(towxstring(i));
346 wxSingleChoiceDialog* d2 = new wxSingleChoiceDialog(parent, wxT("Select file within .zip"),
347 wxT("Select member"), files.size(), &files[0]);
348 if(d2->ShowModal() == wxID_CANCEL) {
349 d2->Destroy();
350 return "";
352 f = filename + "/" + tostdstring(d2->GetStringSelection());
353 d2->Destroy();
354 } catch(...) {
355 //Ignore error.
356 f = filename;
358 return f;
361 void signal_program_exit()
363 post_ui_event(UISERV_EXIT);
366 void signal_resize_needed()
368 post_ui_event(UISERV_RESIZED);
372 static const wxCmdLineEntryDesc dummy_descriptor_table[] = {
373 { wxCMD_LINE_PARAM, NULL, NULL, NULL, wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL |
374 wxCMD_LINE_PARAM_MULTIPLE },
375 { wxCMD_LINE_NONE }
378 class lsnes_app : public wxApp
380 public:
381 lsnes_app();
382 virtual bool OnInit();
383 virtual int OnExit();
384 virtual void OnInitCmdLine(wxCmdLineParser& parser);
385 virtual bool OnCmdLineParsed(wxCmdLineParser& parser);
386 private:
387 bool settings_mode;
388 bool pluginmanager_mode;
389 std::string c_rom;
390 std::string c_file;
391 std::vector<std::string> cmdline;
392 std::map<std::string, std::string> c_settings;
393 std::vector<std::string> c_lua;
394 bool exit_immediately;
395 bool fullscreen_mode;
396 bool start_unpaused;
399 IMPLEMENT_APP(lsnes_app)
401 lsnes_app::lsnes_app()
403 settings_mode = false;
404 pluginmanager_mode = false;
405 exit_immediately = false;
406 fullscreen_mode = false;
407 start_unpaused = false;
410 void lsnes_app::OnInitCmdLine(wxCmdLineParser& parser)
412 parser.SetDesc(dummy_descriptor_table);
413 parser.SetSwitchChars(wxT(""));
416 bool lsnes_app::OnCmdLineParsed(wxCmdLineParser& parser)
418 for(size_t i = 0; i< parser.GetParamCount(); i++)
419 cmdline.push_back(tostdstring(parser.GetParam(i)));
420 for(auto i: cmdline) {
421 regex_results r;
422 if(i == "--help" || i == "-h") {
423 std::cout << "--settings: Show the settings dialog" << std::endl;
424 std::cout << "--pluginmanager: Show the plugin manager" << std::endl;
425 std::cout << "--fullscreen: Start fullscreen" << std::endl;
426 std::cout << "--unpause: Start unpaused (only if ROM is loaded)" << std::endl;
427 std::cout << "--rom=<filename>: Load specified ROM on startup" << std::endl;
428 std::cout << "--load=<filename>: Load specified save/movie on starup" << std::endl;
429 std::cout << "--lua=<filename>: Load specified Lua script on startup" << std::endl;
430 std::cout << "--set=<a>=<b>: Set setting <a> to value <b>" << std::endl;
431 std::cout << "<filename>: Load specified ROM on startup" << std::endl;
432 exit_immediately = true;
433 return true;
435 if(i == "--settings")
436 settings_mode = true;
437 if(i == "--unpause")
438 start_unpaused = true;
439 if(i == "--fullscreen")
440 fullscreen_mode = true;
441 if(i == "--pluginmanager")
442 pluginmanager_mode = true;
443 if(r = regex("--set=([^=]+)=(.+)", i))
444 c_settings[r[1]] = r[2];
445 if(r = regex("--lua=(.+)", i))
446 c_lua.push_back(r[1]);
448 return true;
451 bool lsnes_app::OnInit()
453 wxApp::OnInit();
454 if(exit_immediately)
455 return false;
457 try {
458 crandom::init();
459 } catch(std::exception& e) {
460 show_message_ok(NULL, "RNG error", "Error initializing system RNG", wxICON_ERROR);
461 return false;
464 reached_main();
465 set_random_seed();
466 bring_app_foreground();
468 if(pluginmanager_mode)
469 if(!wxeditor_plugin_manager_display(NULL))
470 return false;
472 ui_services = new ui_services_type();
474 ui_thread = threads::this_id();
475 platform::init();
477 messages << "lsnes version: lsnes rr" << lsnes_version << std::endl;
479 loaded_rom dummy_rom;
480 std::map<std::string, std::string> settings;
481 auto ctrldata = dummy_rom.rtype->controllerconfig(settings);
482 port_type_set& ports = port_type_set::make(ctrldata.ports, ctrldata.portindex());
484 lsnes_instance.buttons->reinit();
485 lsnes_instance.controls->set_ports(ports);
487 std::string cfgpath = get_config_path();
488 autoload_libraries([](const std::string& libname, const std::string& error, bool system) {
489 show_message_ok(NULL, "Error loading plugin " + libname, "Error loading '" + libname + "'\n\n" +
490 error, wxICON_EXCLAMATION);
491 if(!system)
492 wxeditor_plugin_manager_notify_fail(libname);
494 messages << "Saving per-user data to: " << get_config_path() << std::endl;
495 messages << "--- Loading configuration --- " << std::endl;
496 load_configuration();
497 messages << "--- End running lsnesrc --- " << std::endl;
499 if(settings_mode) {
500 //We got to boot this up quite a bit to get the joystick driver working.
501 //In practicular, we need joystick thread and emulator thread in pause.
502 joystick_thread_handle = new threads::thread(joystick_thread, 6);
503 threads::thread* dummy_loop = new threads::thread(eloop_helper, 8);
504 display_settings_dialog(NULL, NULL);
505 platform::exit_dummy_event_loop();
506 joystick_driver_signal();
507 joystick_thread_handle->join();
508 dummy_loop->join();
509 save_configuration();
510 return false;
512 init_lua();
513 lsnes_instance.mdumper->set_output(&messages.getstream());
515 joystick_thread_handle = new threads::thread(joystick_thread, 7);
517 msg_window = new wxwin_messages();
518 msg_window->Show();
520 init_main_callbacks();
521 const std::string movie_file = get_loaded_movie(cmdline);
522 loaded_rom rom;
523 try {
524 moviefile mov;
525 rom = construct_rom(movie_file, cmdline);
526 rom.load(c_settings, mov.movie_rtc_second, mov.movie_rtc_subsecond);
527 } catch(std::exception& e) {
528 std::cerr << "Can't load ROM: " << e.what() << std::endl;
529 show_message_ok(NULL, "Error loading ROM", std::string("Error loading ROM:\n\n") +
530 e.what(), wxICON_EXCLAMATION);
531 quit_lua(); //Don't crash.
532 return false;
535 moviefile* mov = NULL;
536 if(movie_file != "")
537 try {
538 mov = new moviefile(movie_file, *rom.rtype);
539 rom.load(mov->settings, mov->movie_rtc_second, mov->movie_rtc_subsecond);
540 } catch(std::exception& e) {
541 std::cerr << "Can't load state: " << e.what() << std::endl;
542 show_message_ok(NULL, "Error loading movie", std::string("Error loading movie:\n\n") +
543 e.what(), wxICON_EXCLAMATION);
544 quit_lua(); //Don't crash.
545 return false;
547 else {
548 mov = new moviefile(rom, c_settings, DEFAULT_RTC_SECOND, DEFAULT_RTC_SUBSECOND);
550 our_rom = rom;
551 mov->start_paused = start_unpaused ? !(rom.rtype && !rom.rtype->isnull()) : true;
552 for(auto i : c_lua)
553 lua_add_startup_script(i);
554 boot_emulator(rom, *mov, fullscreen_mode);
555 return true;
558 int lsnes_app::OnExit()
560 if(settings_mode)
561 return 0;
562 //NULL these so no further messages will be sent.
563 auto x = msg_window;
564 msg_window = NULL;
565 main_window = NULL;
566 if(x)
567 x->Destroy();
568 save_configuration();
569 quit_lua();
570 lsnes_instance.mlogic->release_memory();
571 joystick_driver_signal();
572 joystick_thread_handle->join();
573 platform::quit();
574 lsnes_instance.buttons->cleanup();
575 cleanup_keymapper();
576 deinitialize_wx_keyboard();
577 return 0;
580 void do_save_configuration()
582 save_configuration();
585 namespace
587 struct _graphics_driver drv = {
588 .init = []() -> void {
589 initialize_wx_keyboard();
591 .quit = []() -> void {},
592 .notify_message = []() -> void
594 post_ui_event(UISERV_UPDATE_MESSAGES);
596 .notify_status = []() -> void
598 post_ui_event(UISERV_UPDATE_STATUS);
600 .notify_screen = []() -> void
602 post_ui_event(UISERV_UPDATE_SCREEN);
604 .error_message = [](const std::string& text) -> void {
605 error_message_text = text;
606 post_ui_event(UISERV_ERROR);
608 .fatal_error = []() -> void {
609 //Fun: This can be called from any thread!
610 if(ui_thread == threads::this_id()) {
611 //UI thread.
612 platform::set_modal_pause(true);
613 wxMessageBox(_T("Panic: Unrecoverable error, can't continue"), _T("Error"),
614 wxICON_ERROR | wxOK);
615 } else {
616 //Emulation thread panic. Signal the UI thread.
617 post_ui_event(UISERV_PANIC);
618 while(!panic_ack);
621 .name = []() -> const char* { return "wxwidgets graphics plugin"; },
622 .action_updated = []()
624 runuifun([]() -> void { main_window->action_updated(); });
626 .request_rom = [](rom_request& req)
628 rom_request* _req = &req;
629 threads::lock lock;
630 threads::cv cv;
631 bool done = false;
632 threads::alock h(lock);
633 runuifun([_req, &lock, &cv, &done]() -> void {
634 try {
635 main_window->request_rom(*_req);
636 } catch(...) {
637 _req->canceled = true;
639 threads::alock h(lock);
640 done = true;
641 cv.notify_all();
643 while(!done)
644 cv.wait(h);
647 struct graphics_driver _drv(drv);
650 void signal_core_change()
652 post_ui_event(UISERV_REFRESH_TITLE);
655 void _runuifun_async(void (*fn)(void*), void* arg)
657 threads::alock h(ui_mutex);
658 ui_queue_entry e;
659 e.fn = fn;
660 e.arg = arg;
661 ui_queue.push_back(e);
662 post_ui_event(UISERV_UIFUN);
666 canceled_exception::canceled_exception() : std::runtime_error("Dialog canceled") {}
668 std::string pick_file(wxWindow* parent, const std::string& title, const std::string& startdir)
670 wxString _title = towxstring(title);
671 wxString _startdir = towxstring(startdir);
672 std::string filespec;
673 filespec = "All files|*";
674 wxFileDialog* d = new wxFileDialog(parent, _title, _startdir, wxT(""), towxstring(filespec), wxFD_OPEN);
675 if(d->ShowModal() == wxID_CANCEL)
676 throw canceled_exception();
677 std::string filename = tostdstring(d->GetPath());
678 d->Destroy();
679 if(filename == "")
680 throw canceled_exception();
681 return filename;
684 std::string pick_file_member(wxWindow* parent, const std::string& title, const std::string& startdir)
686 std::string filename = pick_file(parent, title, startdir);
687 //Did we pick a .zip file?
688 if(!regex_match(".*\\.[zZ][iI][pP]", filename))
689 return filename; //Not a ZIP.
690 try {
691 zip::reader zr(filename);
692 std::vector<std::string> files;
693 for(auto i : zr)
694 files.push_back(i);
695 filename = filename + "/" + pick_among(parent, "Select member", "Select file within .zip", files);
696 } catch(canceled_exception& e) {
697 //Throw these forward.
698 throw;
699 } catch(...) {
700 //Ignore error.
702 return filename;
705 std::string pick_among(wxWindow* parent, const std::string& title, const std::string& prompt,
706 const std::vector<std::string>& choices, unsigned defaultchoice)
708 std::vector<wxString> _choices;
709 for(auto i : choices)
710 _choices.push_back(towxstring(i));
711 wxSingleChoiceDialog* d2 = new wxSingleChoiceDialog(parent, towxstring(prompt), towxstring(title),
712 _choices.size(), &_choices[0]);
713 d2->SetSelection(defaultchoice);
714 if(d2->ShowModal() == wxID_CANCEL) {
715 d2->Destroy();
716 throw canceled_exception();
718 std::string out = tostdstring(d2->GetStringSelection());
719 d2->Destroy();
720 return out;
723 std::string pick_text(wxWindow* parent, const std::string& title, const std::string& prompt, const std::string& dflt,
724 bool multiline)
726 wxTextEntryDialog* d2 = new wxTextEntryDialog(parent, towxstring(prompt), towxstring(title), towxstring(dflt),
727 wxOK | wxCANCEL | wxCENTRE | (multiline ? wxTE_MULTILINE : 0));
728 if(d2->ShowModal() == wxID_CANCEL) {
729 d2->Destroy();
730 throw canceled_exception();
732 std::string text = tostdstring(d2->GetValue());
733 d2->Destroy();
734 return text;
737 void show_message_ok(wxWindow* parent, const std::string& title, const std::string& text, int icon)
739 wxMessageDialog* d3 = new wxMessageDialog(parent, towxstring(text), towxstring(title), wxOK | icon);
740 d3->ShowModal();
741 d3->Destroy();
744 bool run_show_error(wxWindow* parent, const std::string& title, const std::string& text, std::function<void()> fn)
746 try {
747 fn();
748 return false;
749 } catch(std::exception& e) {
750 std::string err = e.what();
751 std::string _title = title;
752 std::string _text = (text == "") ? err : (text + ": " + err);
753 runuifun([parent, _title, _text]() {
754 show_message_ok(parent, _title, _text, wxICON_EXCLAMATION);
756 return true;
760 void show_exception(wxWindow* parent, const std::string& title, const std::string& text, std::exception& e)
762 std::string err = e.what();
763 std::string _title = title;
764 std::string _text = (text == "") ? err : (text + ": " + err);
765 show_message_ok(parent, _title, _text, wxICON_EXCLAMATION);