wxwidgets: GUI for memory search
[lsnes.git] / src / plat-wxwidgets / mainwindow.cpp
blob22ee2acde676ee3b286bed19ce22d1595d4126e1
1 #include "lsnes.hpp"
3 #include "core/command.hpp"
4 #include "core/controller.hpp"
5 #include "core/controllerframe.hpp"
6 #include "core/dispatch.hpp"
7 #include "core/framebuffer.hpp"
8 #include "core/framerate.hpp"
9 #include "core/lua.hpp"
10 #include "core/mainloop.hpp"
11 #include "core/memorywatch.hpp"
12 #include "core/misc.hpp"
13 #include "core/moviedata.hpp"
14 #include "core/window.hpp"
15 #include "core/zip.hpp"
17 #include <vector>
18 #include <string>
20 #include "plat-wxwidgets/menu_dump.hpp"
21 #include "plat-wxwidgets/platform.hpp"
22 #include "plat-wxwidgets/window_mainwindow.hpp"
23 #include "plat-wxwidgets/window_status.hpp"
25 #define MAXCONTROLLERS MAX_PORTS * MAX_CONTROLLERS_PER_PORT
27 extern "C"
29 #ifndef UINT64_C
30 #define UINT64_C(val) val##ULL
31 #endif
32 #include <libswscale/swscale.h>
35 enum
37 wxID_PAUSE = wxID_HIGHEST + 1,
38 wxID_FRAMEADVANCE,
39 wxID_SUBFRAMEADVANCE,
40 wxID_NEXTPOLL,
41 wxID_ERESET,
42 wxID_AUDIO_ENABLED,
43 wxID_SHOW_AUDIO_STATUS,
44 wxID_AUDIODEV_FIRST,
45 wxID_AUDIODEV_LAST = wxID_AUDIODEV_FIRST + 255,
46 wxID_SAVE_STATE,
47 wxID_SAVE_MOVIE,
48 wxID_LOAD_STATE,
49 wxID_LOAD_STATE_RO,
50 wxID_LOAD_STATE_RW,
51 wxID_LOAD_STATE_P,
52 wxID_LOAD_MOVIE,
53 wxID_RUN_SCRIPT,
54 wxID_RUN_LUA,
55 wxID_EVAL_LUA,
56 wxID_SAVE_SCREENSHOT,
57 wxID_READONLY_MODE,
58 wxID_EDIT_AUTHORS,
59 wxID_AUTOHOLD_FIRST,
60 wxID_AUTOHOLD_LAST = wxID_AUTOHOLD_FIRST + 127,
61 wxID_EDIT_AXES,
62 wxID_EDIT_SETTINGS,
63 wxID_EDIT_KEYBINDINGS,
64 wxID_EDIT_ALIAS,
65 wxID_EDIT_MEMORYWATCH,
66 wxID_SAVE_MEMORYWATCH,
67 wxID_LOAD_MEMORYWATCH,
68 wxID_DUMP_FIRST,
69 wxID_DUMP_LAST = wxID_DUMP_FIRST + 1023,
70 wxID_REWIND_MOVIE,
71 wxID_EDIT_JUKEBOX,
72 wxID_MEMORY_SEARCH
76 namespace
78 unsigned char* screen_buffer;
79 uint32_t old_width;
80 uint32_t old_height;
81 bool main_window_dirty;
82 struct thread* emulation_thread;
84 wxString getname()
86 std::string windowname = "lsnes rr" + lsnes_version + "[" + bsnes_core_version + "]";
87 return towxstring(windowname);
90 struct emu_args
92 struct loaded_rom* rom;
93 struct moviefile* initial;
94 bool load_has_to_succeed;
97 void* emulator_main(void* _args)
99 struct emu_args* args = reinterpret_cast<struct emu_args*>(_args);
100 try {
101 our_rom = args->rom;
102 struct moviefile* movie = args->initial;
103 bool has_to_succeed = args->load_has_to_succeed;
104 platform::flush_command_queue();
105 main_loop(*our_rom, *movie, has_to_succeed);
106 signal_program_exit();
107 } catch(std::bad_alloc& e) {
108 OOM_panic();
109 } catch(std::exception& e) {
110 messages << "FATAL: " << e.what() << std::endl;
111 platform::fatal_error();
113 return NULL;
116 void join_emulator_thread()
118 emulation_thread->join();
121 void handle_wx_mouse(wxMouseEvent& e)
123 static uint32_t mask = 0;
124 if(e.LeftDown())
125 mask |= 1;
126 if(e.LeftUp())
127 mask &= ~1;
128 if(e.MiddleDown())
129 mask |= 2;
130 if(e.MiddleUp())
131 mask &= ~2;
132 if(e.RightDown())
133 mask |= 4;
134 if(e.RightUp())
135 mask &= ~4;
136 send_mouse_click(e.GetX(), e.GetY(), mask);
139 bool is_readonly_mode()
141 bool ret;
142 runemufn([&ret]() { ret = movb.get_movie().readonly_mode(); });
143 return ret;
146 bool UI_get_autohold(unsigned pid, unsigned idx)
148 bool ret;
149 runemufn([&ret, pid, idx]() { ret = controls.autohold(pid, idx); });
150 return ret;
153 void UI_change_autohold(unsigned pid, unsigned idx, bool newstate)
155 runemufn([pid, idx, newstate]() { controls.autohold(pid, idx, newstate); });
158 int UI_controller_index_by_logical(unsigned lid)
160 int ret;
161 runemufn([&ret, lid]() { ret = controls.lcid_to_pcid(lid); });
162 return ret;
165 int UI_button_id(unsigned pcid, unsigned lidx)
167 int ret;
168 runemufn([&ret, pcid, lidx]() { ret = controls.button_id(pcid, lidx); });
169 return ret;
172 class controller_autohold_menu : public wxMenu
174 public:
175 controller_autohold_menu(unsigned lid, enum devicetype_t dtype);
176 void change_type();
177 bool is_dummy();
178 void on_select(wxCommandEvent& e);
179 void update(unsigned pid, unsigned ctrlnum, bool newstate);
180 private:
181 unsigned our_lid;
182 wxMenuItem* entries[MAX_LOGICAL_BUTTONS];
183 unsigned enabled_entries;
186 class autohold_menu : public wxMenu
188 public:
189 autohold_menu(wxwin_mainwindow* win);
190 void reconfigure();
191 void on_select(wxCommandEvent& e);
192 void update(unsigned pid, unsigned ctrlnum, bool newstate);
193 private:
194 controller_autohold_menu* menus[MAXCONTROLLERS];
195 wxMenuItem* entries[MAXCONTROLLERS];
198 class sound_select_menu : public wxMenu
200 public:
201 sound_select_menu(wxwin_mainwindow* win);
202 void update(const std::string& dev);
203 void on_select(wxCommandEvent& e);
204 private:
205 std::map<std::string, wxMenuItem*> items;
206 std::map<int, std::string> devices;
209 class sound_select_menu;
211 class broadcast_listener : public information_dispatch
213 public:
214 broadcast_listener(wxwin_mainwindow* win);
215 void set_sound_select(sound_select_menu* sdev);
216 void set_autohold_menu(autohold_menu* ah);
217 void on_sound_unmute(bool unmute) throw();
218 void on_sound_change(const std::string& dev) throw();
219 void on_mode_change(bool readonly) throw();
220 void on_autohold_update(unsigned pid, unsigned ctrlnum, bool newstate);
221 void on_autohold_reconfigure();
222 private:
223 wxwin_mainwindow* mainw;
224 sound_select_menu* sounddev;
225 autohold_menu* ahmenu;
228 controller_autohold_menu::controller_autohold_menu(unsigned lid, enum devicetype_t dtype)
230 platform::set_modal_pause(true);
231 our_lid = lid;
232 for(unsigned i = 0; i < MAX_LOGICAL_BUTTONS; i++) {
233 int id = wxID_AUTOHOLD_FIRST + MAX_LOGICAL_BUTTONS * lid + i;
234 entries[i] = AppendCheckItem(id, towxstring(get_logical_button_name(i)));
236 change_type();
237 platform::set_modal_pause(false);
240 void controller_autohold_menu::change_type()
242 enabled_entries = 0;
243 int pid = controls.lcid_to_pcid(our_lid);
244 for(unsigned i = 0; i < MAX_LOGICAL_BUTTONS; i++) {
245 int pidx = -1;
246 if(pid >= 0)
247 pidx = controls.button_id(pid, i);
248 if(pidx >= 0) {
249 entries[i]->Check(pid > 0 && UI_get_autohold(pid, pidx));
250 entries[i]->Enable();
251 enabled_entries++;
252 } else {
253 entries[i]->Check(false);
254 entries[i]->Enable(false);
259 bool controller_autohold_menu::is_dummy()
261 return !enabled_entries;
264 void controller_autohold_menu::on_select(wxCommandEvent& e)
266 int x = e.GetId();
267 if(x < wxID_AUTOHOLD_FIRST + our_lid * MAX_LOGICAL_BUTTONS || x >= wxID_AUTOHOLD_FIRST *
268 (our_lid + 1) * MAX_LOGICAL_BUTTONS) {
269 return;
271 unsigned lidx = (x - wxID_AUTOHOLD_FIRST) % MAX_LOGICAL_BUTTONS;
272 platform::set_modal_pause(true);
273 int pid = controls.lcid_to_pcid(our_lid);
274 if(pid < 0 || !entries[lidx]) {
275 platform::set_modal_pause(false);
276 return;
278 int pidx = controls.button_id(pid, lidx);
279 if(pidx < 0) {
280 platform::set_modal_pause(false);
281 return;
283 //Autohold change on pid=pid, ctrlindx=idx, state
284 bool newstate = entries[lidx]->IsChecked();
285 UI_change_autohold(pid, pidx, newstate);
286 platform::set_modal_pause(false);
289 void controller_autohold_menu::update(unsigned pid, unsigned ctrlnum, bool newstate)
291 platform::set_modal_pause(true);
292 int pid2 = UI_controller_index_by_logical(our_lid);
293 if(pid2 < 0 || static_cast<unsigned>(pid) != pid2) {
294 platform::set_modal_pause(false);
295 return;
297 for(unsigned i = 0; i < MAX_LOGICAL_BUTTONS; i++) {
298 int idx = UI_button_id(pid2, i);
299 if(idx < 0 || static_cast<unsigned>(idx) != ctrlnum)
300 continue;
301 entries[i]->Check(newstate);
303 platform::set_modal_pause(false);
307 autohold_menu::autohold_menu(wxwin_mainwindow* win)
309 for(unsigned i = 0; i < MAXCONTROLLERS; i++) {
310 std::ostringstream str;
311 str << "Controller #&" << (i + 1);
312 menus[i] = new controller_autohold_menu(i, DT_NONE);
313 entries[i] = AppendSubMenu(menus[i], towxstring(str.str()));
314 entries[i]->Enable(!menus[i]->is_dummy());
316 win->Connect(wxID_AUTOHOLD_FIRST, wxID_AUTOHOLD_LAST, wxEVT_COMMAND_MENU_SELECTED,
317 wxCommandEventHandler(autohold_menu::on_select), NULL, this);
318 reconfigure();
321 void autohold_menu::reconfigure()
323 platform::set_modal_pause(true);
324 for(unsigned i = 0; i < MAXCONTROLLERS; i++) {
325 menus[i]->change_type();
326 entries[i]->Enable(!menus[i]->is_dummy());
328 platform::set_modal_pause(false);
331 void autohold_menu::on_select(wxCommandEvent& e)
333 for(unsigned i = 0; i < MAXCONTROLLERS; i++)
334 menus[i]->on_select(e);
337 void autohold_menu::update(unsigned pid, unsigned ctrlnum, bool newstate)
339 for(unsigned i = 0; i < MAXCONTROLLERS; i++)
340 menus[i]->update(pid, ctrlnum, newstate);
343 sound_select_menu::sound_select_menu(wxwin_mainwindow* win)
345 std::string curdev = platform::get_sound_device();
346 int j = wxID_AUDIODEV_FIRST;
347 for(auto i : platform::get_sound_devices()) {
348 items[i.first] = AppendRadioItem(j, towxstring(i.first + "(" + i.second + ")"));
349 devices[j] = i.first;
350 if(i.first == curdev)
351 items[i.first]->Check();
352 win->Connect(j, wxEVT_COMMAND_MENU_SELECTED,
353 wxCommandEventHandler(sound_select_menu::on_select), NULL, this);
354 j++;
358 void sound_select_menu::update(const std::string& dev)
360 items[dev]->Check();
363 void _do_sound_select(void* args)
365 std::string* x = reinterpret_cast<std::string*>(args);
366 platform::set_sound_device(*x);
369 void sound_select_menu::on_select(wxCommandEvent& e)
371 std::string devname = devices[e.GetId()];
372 if(devname != "")
373 runemufn([devname]() { platform::set_sound_device(devname); });
376 broadcast_listener::broadcast_listener(wxwin_mainwindow* win)
377 : information_dispatch("wxwidgets-broadcast-listener")
379 mainw = win;
382 void broadcast_listener::set_sound_select(sound_select_menu* sdev)
384 sounddev = sdev;
387 void broadcast_listener::set_autohold_menu(autohold_menu* ah)
389 ahmenu = ah;
392 void broadcast_listener::on_sound_unmute(bool unmute) throw()
394 runuifun([unmute, mainw]() { mainw->menu_check(wxID_AUDIO_ENABLED, unmute); });
397 void broadcast_listener::on_sound_change(const std::string& dev) throw()
399 runuifun([dev, sounddev]() { if(sounddev) sounddev->update(dev); });
402 void broadcast_listener::on_mode_change(bool readonly) throw()
404 runuifun([readonly, mainw]() { mainw->menu_check(wxID_READONLY_MODE, readonly); });
407 void broadcast_listener::on_autohold_update(unsigned pid, unsigned ctrlnum, bool newstate)
409 runuifun([pid, ctrlnum, newstate, ahmenu]() { ahmenu->update(pid, ctrlnum, newstate); });
412 void broadcast_listener::on_autohold_reconfigure()
414 runuifun([ahmenu]() { ahmenu->reconfigure(); });
417 void _set_readonly(void* args)
419 bool s = *reinterpret_cast<bool*>(args);
420 movb.get_movie().readonly_mode(s);
421 if(!s)
422 lua_callback_do_readwrite();
423 update_movie_state();
426 struct keyentry_mod_data
428 wxCheckBox* pressed;
429 wxCheckBox* unmasked;
430 unsigned tmpflags;
433 class wxdialog_keyentry : public wxDialog
435 public:
436 wxdialog_keyentry(wxWindow* parent);
437 void on_change_setting(wxCommandEvent& e);
438 void on_ok(wxCommandEvent& e);
439 void on_cancel(wxCommandEvent& e);
440 std::string getkey();
441 private:
442 std::map<std::string, keyentry_mod_data> modifiers;
443 wxComboBox* mainkey;
444 wxButton* ok;
445 wxButton* cancel;
448 wxdialog_keyentry::wxdialog_keyentry(wxWindow* parent)
449 : wxDialog(parent, wxID_ANY, wxT("Specify key"), wxDefaultPosition, wxSize(-1, -1))
451 std::vector<wxString> keych;
452 std::set<std::string> mods, keys;
454 runemufn([&mods, &keys]() { mods = modifier::get_set(); keys = keygroup::get_keys(); });
455 Centre();
456 wxFlexGridSizer* top_s = new wxFlexGridSizer(2, 1, 0, 0);
457 SetSizer(top_s);
459 wxFlexGridSizer* t_s = new wxFlexGridSizer(mods.size() + 1, 3, 0, 0);
460 for(auto i : mods) {
461 t_s->Add(new wxStaticText(this, wxID_ANY, towxstring(i)), 0, wxGROW);
462 keyentry_mod_data m;
463 t_s->Add(m.pressed = new wxCheckBox(this, wxID_ANY, wxT("Pressed")), 0, wxGROW);
464 t_s->Add(m.unmasked = new wxCheckBox(this, wxID_ANY, wxT("Unmasked")), 0, wxGROW);
465 m.pressed->Disable();
466 modifiers[i] = m;
467 m.pressed->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED,
468 wxCommandEventHandler(wxdialog_keyentry::on_change_setting), NULL, this);
469 m.unmasked->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED,
470 wxCommandEventHandler(wxdialog_keyentry::on_change_setting), NULL, this);
472 for(auto i : keys)
473 keych.push_back(towxstring(i));
474 t_s->Add(new wxStaticText(this, wxID_ANY, wxT("Key")), 0, wxGROW);
475 t_s->Add(mainkey = new wxComboBox(this, wxID_ANY, keych[0], wxDefaultPosition, wxDefaultSize,
476 keych.size(), &keych[0], wxCB_READONLY), 1, wxGROW);
477 mainkey->Connect(wxEVT_COMMAND_COMBOBOX_SELECTED,
478 wxCommandEventHandler(wxdialog_keyentry::on_change_setting), NULL, this);
479 top_s->Add(t_s);
481 wxBoxSizer* pbutton_s = new wxBoxSizer(wxHORIZONTAL);
482 pbutton_s->AddStretchSpacer();
483 pbutton_s->Add(ok = new wxButton(this, wxID_OK, wxT("OK")), 0, wxGROW);
484 pbutton_s->Add(cancel = new wxButton(this, wxID_CANCEL, wxT("Cancel")), 0, wxGROW);
485 ok->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
486 wxCommandEventHandler(wxdialog_keyentry::on_ok), NULL, this);
487 cancel->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
488 wxCommandEventHandler(wxdialog_keyentry::on_cancel), NULL, this);
489 top_s->Add(pbutton_s, 0, wxGROW);
491 t_s->SetSizeHints(this);
492 top_s->SetSizeHints(this);
493 Fit();
496 #define TMPFLAG_UNMASKED 65
497 #define TMPFLAG_UNMASKED_LINK_CHILD 2
498 #define TMPFLAG_UNMASKED_LINK_PARENT 68
499 #define TMPFLAG_PRESSED 8
500 #define TMPFLAG_PRESSED_LINK_CHILD 16
501 #define TMPFLAG_PRESSED_LINK_PARENT 32
503 void wxdialog_keyentry::on_change_setting(wxCommandEvent& e)
505 for(auto& i : modifiers)
506 i.second.tmpflags = 0;
507 for(auto& i : modifiers) {
508 modifier* m = NULL;
509 try {
510 m = &modifier::lookup(i.first);
511 } catch(...) {
512 i.second.pressed->Disable();
513 i.second.unmasked->Disable();
514 continue;
516 std::string j = m->linked_name();
517 if(i.second.unmasked->GetValue())
518 i.second.tmpflags |= TMPFLAG_UNMASKED;
519 if(j != "") {
520 if(modifiers[j].unmasked->GetValue())
521 i.second.tmpflags |= TMPFLAG_UNMASKED_LINK_PARENT;
522 if(i.second.unmasked->GetValue())
523 modifiers[j].tmpflags |= TMPFLAG_UNMASKED_LINK_CHILD;
525 if(i.second.pressed->GetValue())
526 i.second.tmpflags |= TMPFLAG_PRESSED;
527 if(j != "") {
528 if(modifiers[j].pressed->GetValue())
529 i.second.tmpflags |= TMPFLAG_PRESSED_LINK_PARENT;
530 if(i.second.pressed->GetValue())
531 modifiers[j].tmpflags |= TMPFLAG_PRESSED_LINK_CHILD;
534 for(auto& i : modifiers) {
535 //Unmasked is to be enabled if neither unmasked link flag is set.
536 if(i.second.tmpflags & ((TMPFLAG_UNMASKED_LINK_CHILD | TMPFLAG_UNMASKED_LINK_PARENT) & ~64)) {
537 i.second.unmasked->SetValue(false);
538 i.second.unmasked->Disable();
539 } else
540 i.second.unmasked->Enable();
541 //Pressed is to be enabled if:
542 //- This modifier is unmasked or parent is unmasked.
543 //- Parent nor child is not pressed.
544 if(((i.second.tmpflags & (TMPFLAG_UNMASKED | TMPFLAG_UNMASKED_LINK_PARENT |
545 TMPFLAG_PRESSED_LINK_CHILD | TMPFLAG_PRESSED_LINK_PARENT)) & 112) == 64)
546 i.second.pressed->Enable();
547 else {
548 i.second.pressed->SetValue(false);
549 i.second.pressed->Disable();
554 void wxdialog_keyentry::on_ok(wxCommandEvent& e)
556 EndModal(wxID_OK);
559 void wxdialog_keyentry::on_cancel(wxCommandEvent& e)
561 EndModal(wxID_CANCEL);
564 std::string wxdialog_keyentry::getkey()
566 std::string x;
567 bool f;
568 f = true;
569 for(auto i : modifiers) {
570 if(i.second.pressed->GetValue()) {
571 if(!f)
572 x = x + ",";
573 f = false;
574 x = x + i.first;
577 x = x + "/";
578 f = true;
579 for(auto i : modifiers) {
580 if(i.second.unmasked->GetValue()) {
581 if(!f)
582 x = x + ",";
583 f = false;
584 x = x + i.first;
587 x = x + "|" + tostdstring(mainkey->GetValue());
588 return x;
592 void boot_emulator(loaded_rom& rom, moviefile& movie)
594 try {
595 struct emu_args* a = new emu_args;
596 a->rom = &rom;
597 a->initial = &movie;
598 a->load_has_to_succeed = false;
599 platform::set_modal_pause(true);
600 emulation_thread = &thread::create(emulator_main, a);
601 main_window = new wxwin_mainwindow();
602 main_window->Show();
603 platform::set_modal_pause(false);
604 } catch(std::bad_alloc& e) {
605 OOM_panic();
609 wxwin_mainwindow::panel::panel(wxWindow* win)
610 : wxPanel(win)
612 this->Connect(wxEVT_PAINT, wxPaintEventHandler(panel::on_paint), NULL, this);
613 this->Connect(wxEVT_ERASE_BACKGROUND, wxEraseEventHandler(panel::on_erase), NULL, this);
614 this->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(panel::on_keyboard_down), NULL, this);
615 this->Connect(wxEVT_KEY_UP, wxKeyEventHandler(panel::on_keyboard_up), NULL, this);
616 this->Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler(panel::on_mouse), NULL, this);
617 this->Connect(wxEVT_LEFT_UP, wxMouseEventHandler(panel::on_mouse), NULL, this);
618 this->Connect(wxEVT_MIDDLE_DOWN, wxMouseEventHandler(panel::on_mouse), NULL, this);
619 this->Connect(wxEVT_MIDDLE_UP, wxMouseEventHandler(panel::on_mouse), NULL, this);
620 this->Connect(wxEVT_RIGHT_DOWN, wxMouseEventHandler(panel::on_mouse), NULL, this);
621 this->Connect(wxEVT_RIGHT_UP, wxMouseEventHandler(panel::on_mouse), NULL, this);
622 SetMinSize(wxSize(512, 448));
625 void wxwin_mainwindow::menu_start(wxString name)
627 while(!upper.empty())
628 upper.pop();
629 current_menu = new wxMenu();
630 menubar->Append(current_menu, name);
633 void wxwin_mainwindow::menu_special(wxString name, wxMenu* menu)
635 while(!upper.empty())
636 upper.pop();
637 menubar->Append(menu, name);
638 current_menu = NULL;
641 void wxwin_mainwindow::menu_special_sub(wxString name, wxMenu* menu)
643 current_menu->AppendSubMenu(menu, name);
646 void wxwin_mainwindow::menu_entry(int id, wxString name)
648 current_menu->Append(id, name);
649 Connect(id, wxEVT_COMMAND_MENU_SELECTED,
650 wxCommandEventHandler(wxwin_mainwindow::wxwin_mainwindow::handle_menu_click), NULL, this);
653 void wxwin_mainwindow::menu_entry_check(int id, wxString name)
655 checkitems[id] = current_menu->AppendCheckItem(id, name);
656 Connect(id, wxEVT_COMMAND_MENU_SELECTED,
657 wxCommandEventHandler(wxwin_mainwindow::wxwin_mainwindow::handle_menu_click), NULL, this);
660 void wxwin_mainwindow::menu_start_sub(wxString name)
662 wxMenu* old = current_menu;
663 upper.push(current_menu);
664 current_menu = new wxMenu();
665 old->AppendSubMenu(current_menu, name);
668 void wxwin_mainwindow::menu_end_sub(wxString name)
670 current_menu = upper.top();
671 upper.pop();
674 bool wxwin_mainwindow::menu_ischecked(int id)
676 if(checkitems.count(id))
677 return checkitems[id]->IsChecked();
678 else
679 return false;
682 void wxwin_mainwindow::menu_check(int id, bool newstate)
684 if(checkitems.count(id))
685 return checkitems[id]->Check(newstate);
686 else
687 return;
690 void wxwin_mainwindow::menu_separator()
692 current_menu->AppendSeparator();
695 void wxwin_mainwindow::panel::request_paint()
697 Refresh();
700 void wxwin_mainwindow::panel::on_paint(wxPaintEvent& e)
702 render_framebuffer();
703 static struct SwsContext* ctx;
704 uint8_t* srcp[1];
705 int srcs[1];
706 uint8_t* dstp[1];
707 int dsts[1];
708 wxPaintDC dc(this);
709 if(!screen_buffer || main_screen.width != old_width || main_screen.height != old_height) {
710 if(screen_buffer)
711 delete[] screen_buffer;
712 screen_buffer = new unsigned char[main_screen.width * main_screen.height * 3];
713 old_height = main_screen.height;
714 old_width = main_screen.width;
715 uint32_t w = main_screen.width;
716 uint32_t h = main_screen.height;
717 if(w && h)
718 ctx = sws_getCachedContext(ctx, w, h, PIX_FMT_RGBA, w, h, PIX_FMT_BGR24, SWS_POINT |
719 SWS_CPU_CAPS_MMX2, NULL, NULL, NULL);
720 if(w < 512)
721 w = 512;
722 if(h < 448)
723 h = 448;
724 SetMinSize(wxSize(w, h));
725 main_window->Fit();
727 srcs[0] = 4 * main_screen.width;
728 dsts[0] = 3 * main_screen.width;
729 srcp[0] = reinterpret_cast<unsigned char*>(main_screen.memory);
730 dstp[0] = screen_buffer;
731 memset(screen_buffer, 0, main_screen.width * main_screen.height * 3);
732 uint64_t t1 = get_utime();
733 if(main_screen.width && main_screen.height)
734 sws_scale(ctx, srcp, srcs, 0, main_screen.height, dstp, dsts);
735 uint64_t t2 = get_utime();
736 wxBitmap bmp(wxImage(main_screen.width, main_screen.height, screen_buffer, true));
737 uint64_t t3 = get_utime();
738 dc.DrawBitmap(bmp, 0, 0, false);
739 main_window_dirty = false;
742 void wxwin_mainwindow::panel::on_erase(wxEraseEvent& e)
744 //Blank.
747 void wxwin_mainwindow::panel::on_keyboard_down(wxKeyEvent& e)
749 handle_wx_keyboard(e, true);
752 void wxwin_mainwindow::panel::on_keyboard_up(wxKeyEvent& e)
754 handle_wx_keyboard(e, false);
757 void wxwin_mainwindow::panel::on_mouse(wxMouseEvent& e)
759 handle_wx_mouse(e);
762 wxwin_mainwindow::wxwin_mainwindow()
763 : wxFrame(NULL, wxID_ANY, getname(), wxDefaultPosition, wxSize(-1, -1),
764 wxMINIMIZE_BOX | wxSYSTEM_MENU | wxCAPTION | wxCLIP_CHILDREN | wxCLOSE_BOX)
766 broadcast_listener* blistener = new broadcast_listener(this);
767 Centre();
768 wxFlexGridSizer* toplevel = new wxFlexGridSizer(1, 1, 0, 0);
769 toplevel->Add(gpanel = new panel(this), 1, wxGROW);
770 toplevel->SetSizeHints(this);
771 SetSizer(toplevel);
772 Fit();
773 gpanel->SetFocus();
774 Connect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(wxwin_mainwindow::on_close));
775 menubar = new wxMenuBar;
776 SetMenuBar(menubar);
778 //TOP-level accels: ACFOS.
779 //System menu: (ACFOS)EMNPQRU
780 menu_start(wxT("&System"));
781 menu_entry(wxID_FRAMEADVANCE, wxT("Fra&me advance"));
782 menu_entry(wxID_SUBFRAMEADVANCE, wxT("S&ubframe advance"));
783 menu_entry(wxID_NEXTPOLL, wxT("&Next poll"));
784 menu_entry(wxID_PAUSE, wxT("&Pause/Unpause"));
785 menu_separator();
786 menu_entry(wxID_ERESET, wxT("&Reset"));
787 menu_separator();
788 menu_entry(wxID_EDIT_AUTHORS, wxT("&Edit game name && authors"));
789 menu_separator();
790 menu_entry(wxID_EXIT, wxT("&Quit"));
791 menu_separator();
792 menu_entry(wxID_ABOUT, wxT("About"));
793 //File menu: (ACFOS)DELMNPRTUVW
794 menu_start(wxT("&File"));
795 menu_entry_check(wxID_READONLY_MODE, wxT("Reado&nly mode"));
796 menu_check(wxID_READONLY_MODE, is_readonly_mode());
797 menu_separator();
798 menu_entry(wxID_SAVE_STATE, wxT("Save stat&e"));
799 menu_entry(wxID_SAVE_MOVIE, wxT("Sa&ve movie"));
800 menu_separator();
801 menu_entry(wxID_LOAD_STATE, wxT("&Load state"));
802 menu_entry(wxID_LOAD_STATE_RO, wxT("Loa&d state (readonly)"));
803 menu_entry(wxID_LOAD_STATE_RW, wxT("Load s&tate (read-write)"));
804 menu_entry(wxID_LOAD_STATE_P, wxT("Load state (&preserve)"));
805 menu_entry(wxID_LOAD_MOVIE, wxT("Load &movie"));
806 menu_entry(wxID_REWIND_MOVIE, wxT("Re&wind movie"));
807 menu_separator();
808 menu_entry(wxID_SAVE_SCREENSHOT, wxT("Save sc&reenshot"));
809 menu_separator();
810 menu_special_sub(wxT("D&ump video"), reinterpret_cast<dumper_menu*>(dmenu = new dumper_menu(this,
811 wxID_DUMP_FIRST, wxID_DUMP_LAST)));
812 //Autohold menu: (ACFOS)
813 menu_special(wxT("&Autohold"), reinterpret_cast<autohold_menu*>(ahmenu = new autohold_menu(this)));
814 blistener->set_autohold_menu(reinterpret_cast<autohold_menu*>(ahmenu));
815 //Scripting menu: (ACFOS)ERU
816 menu_start(wxT("S&cripting"));
817 menu_entry(wxID_RUN_SCRIPT, wxT("&Run script"));
818 if(lua_supported) {
819 menu_separator();
820 menu_entry(wxID_EVAL_LUA, wxT("&Evaluate Lua statement"));
821 menu_entry(wxID_RUN_LUA, wxT("R&un Lua script"));
823 menu_separator();
824 menu_entry(wxID_EDIT_MEMORYWATCH, wxT("Edit memory watch"));
825 menu_separator();
826 menu_entry(wxID_LOAD_MEMORYWATCH, wxT("Load memory watch"));
827 menu_entry(wxID_SAVE_MEMORYWATCH, wxT("Save memory watch"));
828 menu_separator();
829 menu_entry(wxID_MEMORY_SEARCH, wxT("Memory Search"));
830 //Settings menu: (ACFOS)
831 menu_start(wxT("Settings"));
832 menu_entry(wxID_EDIT_AXES, wxT("Configure axes"));
833 menu_entry(wxID_EDIT_SETTINGS, wxT("Configure settings"));
834 menu_entry(wxID_EDIT_KEYBINDINGS, wxT("Configure keybindings"));
835 menu_entry(wxID_EDIT_ALIAS, wxT("Configure aliases"));
836 menu_entry(wxID_EDIT_JUKEBOX, wxT("Configure jukebox"));
837 if(platform::sound_initialized()) {
838 //Sound menu: (ACFOS)EHU
839 menu_start(wxT("S&ound"));
840 menu_entry_check(wxID_AUDIO_ENABLED, wxT("So&unds enabled"));
841 menu_check(wxID_AUDIO_ENABLED, platform::is_sound_enabled());
842 menu_entry(wxID_SHOW_AUDIO_STATUS, wxT("S&how audio status"));
843 menu_separator();
844 menu_special_sub(wxT("S&elect sound device"), reinterpret_cast<sound_select_menu*>(sounddev =
845 new sound_select_menu(this)));
846 blistener->set_sound_select(reinterpret_cast<sound_select_menu*>(sounddev));
850 void wxwin_mainwindow::request_paint()
852 gpanel->Refresh();
855 void wxwin_mainwindow::on_close(wxCloseEvent& e)
857 //Veto it for now, latter things will delete it.
858 e.Veto();
859 platform::queue("quit-emulator");
862 void wxwin_mainwindow::notify_update() throw()
864 if(!main_window_dirty) {
865 main_window_dirty = true;
866 gpanel->Refresh();
870 void wxwin_mainwindow::notify_exit() throw()
872 join_emulator_thread();
873 Destroy();
876 void wxwin_mainwindow::handle_menu_click(wxCommandEvent& e)
878 wxFileDialog* d;
879 wxTextEntryDialog* d2;
880 std::string filename;
881 bool s;
882 switch(e.GetId()) {
883 case wxID_FRAMEADVANCE:
884 platform::queue("+advance-frame");
885 platform::queue("-advance-frame");
886 return;
887 case wxID_SUBFRAMEADVANCE:
888 platform::queue("+advance-poll");
889 platform::queue("-advance-poll");
890 return;
891 case wxID_NEXTPOLL:
892 platform::queue("advance-skiplag");
893 return;
894 case wxID_PAUSE:
895 platform::queue("pause-emulator");
896 return;
897 case wxID_RESET:
898 platform::queue("reset");
899 return;
900 case wxID_EXIT:
901 platform::queue("quit-emulator");
902 return;
903 case wxID_AUDIO_ENABLED:
904 platform::sound_enable(menu_ischecked(wxID_AUDIO_ENABLED));
905 return;
906 case wxID_SHOW_AUDIO_STATUS:
907 platform::queue("show-sound-status");
908 return;
909 case wxID_LOAD_MOVIE:
910 d = new wxFileDialog(this, wxT("Load Movie"), wxT("."));
911 if(d->ShowModal() == wxID_OK)
912 filename = tostdstring(d->GetPath());
913 d->Destroy();
914 if(filename == "")
915 break;
916 platform::queue("load-movie " + filename);
917 break;
918 case wxID_LOAD_STATE:
919 d = new wxFileDialog(this, wxT("Load State"), wxT("."));
920 if(d->ShowModal() == wxID_OK)
921 filename = tostdstring(d->GetPath());
922 d->Destroy();
923 if(filename == "")
924 break;
925 platform::queue("load " + filename);
926 break;
927 case wxID_LOAD_STATE_RO:
928 d = new wxFileDialog(this, wxT("Load State (Read-Only)"), wxT("."));
929 if(d->ShowModal() == wxID_OK)
930 filename = tostdstring(d->GetPath());
931 d->Destroy();
932 if(filename == "")
933 break;
934 platform::queue("load-readonly " + filename);
935 break;
936 case wxID_LOAD_STATE_RW:
937 d = new wxFileDialog(this, wxT("Load State (Read-Write)"), wxT("."));
938 if(d->ShowModal() == wxID_OK)
939 filename = tostdstring(d->GetPath());
940 d->Destroy();
941 if(filename == "")
942 break;
943 platform::queue("load-state " + filename);
944 break;
945 case wxID_LOAD_STATE_P:
946 d = new wxFileDialog(this, wxT("Load State (Preserve)"), wxT("."));
947 if(d->ShowModal() == wxID_OK)
948 filename = tostdstring(d->GetPath());
949 d->Destroy();
950 if(filename == "")
951 break;
952 platform::queue("load-preserve " + filename);
953 break;
954 case wxID_REWIND_MOVIE:
955 platform::queue("rewind-movie");
956 break;
957 case wxID_SAVE_MOVIE:
958 d = new wxFileDialog(this, wxT("Save Movie"), wxT("."));
959 if(d->ShowModal() == wxID_OK)
960 filename = tostdstring(d->GetPath());
961 d->Destroy();
962 if(filename == "")
963 break;
964 platform::queue("save-movie " + filename);
965 break;
966 case wxID_SAVE_STATE:
967 d = new wxFileDialog(this, wxT("Save State"), wxT("."));
968 if(d->ShowModal() == wxID_OK)
969 filename = tostdstring(d->GetPath());
970 d->Destroy();
971 if(filename == "")
972 break;
973 platform::queue("save-state " + filename);
974 break;
975 case wxID_SAVE_SCREENSHOT:
976 d = new wxFileDialog(this, wxT("Save Screenshot"), wxT("."));
977 if(d->ShowModal() == wxID_OK)
978 filename = tostdstring(d->GetPath());
979 d->Destroy();
980 if(filename == "")
981 break;
982 platform::queue("take-screenshot " + filename);
983 break;
984 case wxID_RUN_SCRIPT:
985 d = new wxFileDialog(this, wxT("Select Script"), wxT("."));
986 if(d->ShowModal() == wxID_OK)
987 filename = tostdstring(d->GetPath());
988 d->Destroy();
989 if(filename == "")
990 break;
991 platform::queue("run-script " + filename);
992 break;
993 case wxID_RUN_LUA:
994 d = new wxFileDialog(this, wxT("Select Lua Script"), wxT("."));
995 if(d->ShowModal() == wxID_OK)
996 filename = tostdstring(d->GetPath());
997 d->Destroy();
998 if(filename == "")
999 break;
1000 platform::queue("run-lua " + filename);
1001 break;
1002 case wxID_EVAL_LUA:
1003 d2 = new wxTextEntryDialog(this, wxT("Enter Lua statement:"), wxT("Evaluate Lua"));
1004 if(d2->ShowModal() == wxID_OK)
1005 filename = tostdstring(d2->GetValue());
1006 d2->Destroy();
1007 platform::queue("evaluate-lua " + filename);
1008 break;
1009 case wxID_READONLY_MODE:
1010 s = menu_ischecked(wxID_READONLY_MODE);
1011 platform::queue(_set_readonly, &s, true);
1012 break;
1013 case wxID_EDIT_AXES:
1014 wxeditor_axes_display(this);
1015 break;
1016 case wxID_EDIT_AUTHORS:
1017 wxeditor_authors_display(this);
1018 break;
1019 case wxID_EDIT_SETTINGS:
1020 wxeditor_settings_display(this);
1021 break;
1022 case wxID_EDIT_KEYBINDINGS:
1023 menu_edit_keybindings(e);
1024 break;
1025 case wxID_EDIT_ALIAS:
1026 menu_edit_aliases(e);
1027 break;
1028 case wxID_EDIT_JUKEBOX:
1029 menu_edit_jukebox(e);
1030 break;
1031 case wxID_EDIT_MEMORYWATCH:
1032 menu_edit_memorywatch(e);
1033 break;
1034 case wxID_SAVE_MEMORYWATCH:
1035 menu_save_memorywatch(e);
1036 break;
1037 case wxID_LOAD_MEMORYWATCH:
1038 menu_load_memorywatch(e);
1039 break;
1040 case wxID_MEMORY_SEARCH:
1041 wxwindow_memorysearch_display();
1042 break;
1043 case wxID_ABOUT: {
1044 std::ostringstream str;
1045 str << "Version: lsnes rr" << lsnes_version << std::endl;
1046 str << "Revision: " << lsnes_git_revision << std::endl;
1047 str << "Core: " << bsnes_core_version << std::endl;
1048 wxMessageBox(towxstring(str.str()), _T("About"), wxICON_INFORMATION | wxOK, this);
1050 break;
1054 #define NEW_KEYBINDING "A new binding..."
1055 #define NEW_ALIAS "A new alias..."
1056 #define NEW_WATCH "A new watch..."
1058 void wxwin_mainwindow::menu_edit_keybindings(wxCommandEvent& e)
1060 platform::set_modal_pause(true);
1061 std::set<std::string> bind;
1062 runemufn([&bind]() { bind = keymapper::get_bindings(); });
1063 std::vector<wxString> choices;
1064 choices.push_back(wxT(NEW_KEYBINDING));
1065 for(auto i : bind)
1066 choices.push_back(towxstring(i));
1067 wxSingleChoiceDialog* d = new wxSingleChoiceDialog(this, wxT("Select keybinding to edit"),
1068 wxT("Select binding"), choices.size(), &choices[0]);
1069 if(d->ShowModal() == wxID_CANCEL) {
1070 d->Destroy();
1071 platform::set_modal_pause(false);
1072 return;
1074 std::string key = tostdstring(d->GetStringSelection());
1075 d->Destroy();
1076 if(key == NEW_KEYBINDING) {
1077 wxdialog_keyentry* d2 = new wxdialog_keyentry(this);
1078 //wxTextEntryDialog* d2 = new wxTextEntryDialog(this, wxT("Enter key for binding:"),
1079 // wxT("Edit binding"), wxT(""));
1080 if(d2->ShowModal() == wxID_CANCEL) {
1081 d2->Destroy();
1082 platform::set_modal_pause(false);
1083 return;
1085 key = d2->getkey();
1086 //key = tostdstring(d2->GetValue());
1087 d2->Destroy();
1089 std::string old_command_value;
1090 runemufn([&old_command_value, key]() { old_command_value = keymapper::get_command_for(key); });
1091 wxTextEntryDialog* d4 = new wxTextEntryDialog(this, wxT("Enter new command for binding:"), wxT("Edit binding"),
1092 towxstring(old_command_value));
1093 if(d4->ShowModal() == wxID_CANCEL) {
1094 d4->Destroy();
1095 platform::set_modal_pause(false);
1096 return;
1098 bool fault = false;
1099 std::string faulttext;
1100 std::string newcommand = tostdstring(d4->GetValue());
1101 runemufn([&fault, &faulttext, key, newcommand]() {
1102 try {
1103 keymapper::bind_for(key, newcommand);
1104 } catch(std::exception& e) {
1107 if(fault) {
1108 wxMessageDialog* d3 = new wxMessageDialog(this, towxstring(std::string("Can't bind key: ") +
1109 faulttext), wxT("Error"), wxOK | wxICON_EXCLAMATION);
1110 d3->ShowModal();
1111 d3->Destroy();
1113 d4->Destroy();
1114 platform::set_modal_pause(false);
1117 void strip_CR(std::string& x) throw(std::bad_alloc);
1119 void wxwin_mainwindow::menu_edit_aliases(wxCommandEvent& e)
1121 platform::set_modal_pause(true);
1122 std::set<std::string> bind;
1123 runemufn([&bind]() { bind = command::get_aliases(); });
1124 std::vector<wxString> choices;
1125 choices.push_back(wxT(NEW_ALIAS));
1126 for(auto i : bind)
1127 choices.push_back(towxstring(i));
1128 wxSingleChoiceDialog* d = new wxSingleChoiceDialog(this, wxT("Select alias to edit"),
1129 wxT("Select alias"), choices.size(), &choices[0]);
1130 if(d->ShowModal() == wxID_CANCEL) {
1131 d->Destroy();
1132 platform::set_modal_pause(false);
1133 return;
1135 std::string alias = tostdstring(d->GetStringSelection());
1136 d->Destroy();
1137 if(alias == NEW_ALIAS) {
1138 wxTextEntryDialog* d2 = new wxTextEntryDialog(this, wxT("Enter name for the new alias:"),
1139 wxT("Enter alias name"));
1140 if(d2->ShowModal() == wxID_CANCEL) {
1141 d2->Destroy();
1142 platform::set_modal_pause(false);
1143 return;
1145 alias = tostdstring(d2->GetValue());
1146 d2->Destroy();
1147 if(!command::valid_alias_name(alias)) {
1148 wxMessageDialog* d3 = new wxMessageDialog(this, towxstring(std::string("Not a valid alias "
1149 "name: ") + alias), wxT("Error"), wxOK | wxICON_EXCLAMATION);
1150 d3->ShowModal();
1151 d3->Destroy();
1152 platform::set_modal_pause(false);
1153 return;
1156 std::string old_alias_value = command::get_alias_for(alias);
1157 wxTextEntryDialog* d4 = new wxTextEntryDialog(this, wxT("Enter new commands for alias:"), wxT("Edit alias"),
1158 towxstring(old_alias_value), wxOK | wxCANCEL | wxCENTRE | wxTE_MULTILINE);
1159 if(d4->ShowModal() == wxID_CANCEL) {
1160 d4->Destroy();
1161 platform::set_modal_pause(false);
1162 return;
1164 std::string newcmd = tostdstring(d4->GetValue());
1165 runemufn([alias, newcmd]() { command::set_alias_for(alias, newcmd); });
1166 d4->Destroy();
1167 platform::set_modal_pause(false);
1170 void wxwin_mainwindow::menu_edit_jukebox(wxCommandEvent& e)
1172 platform::set_modal_pause(true);
1173 std::string x;
1174 std::vector<std::string> new_jukebox;
1175 runemufn([&x]() {
1176 for(auto i : get_jukebox_names())
1177 x = x + i + "\n";
1180 wxTextEntryDialog* dialog = new wxTextEntryDialog(this, wxT("List jukebox entries"), wxT("Configure jukebox"),
1181 towxstring(x), wxOK | wxCANCEL | wxCENTRE | wxTE_MULTILINE);
1182 if(dialog->ShowModal() == wxID_CANCEL) {
1183 dialog->Destroy();
1184 platform::set_modal_pause(false);
1185 return;
1187 x = tostdstring(dialog->GetValue());
1188 dialog->Destroy();
1190 while(x != "") {
1191 size_t split = x.find_first_of("\n");
1192 std::string l;
1193 if(split < x.length()) {
1194 l = x.substr(0, split);
1195 x = x.substr(split + 1);
1196 } else {
1197 l = x;
1198 x = "";
1200 strip_CR(l);
1201 if(l != "")
1202 new_jukebox.push_back(l);
1204 runemufn([&new_jukebox]() { set_jukebox_names(new_jukebox); });
1205 status_window->notify_update();
1206 platform::set_modal_pause(false);
1209 void wxwin_mainwindow::menu_load_memorywatch(wxCommandEvent& e)
1211 platform::set_modal_pause(true);
1212 std::set<std::string> old_watches;
1213 runemufn([&old_watches]() { old_watches = get_watches(); });
1214 std::map<std::string, std::string> new_watches;
1215 std::string filename;
1217 wxFileDialog* d = new wxFileDialog(this, towxstring("Choose memory watch file"), wxT("."));
1218 if(d->ShowModal() == wxID_CANCEL) {
1219 d->Destroy();
1220 platform::set_modal_pause(false);
1221 return;
1223 filename = tostdstring(d->GetPath());
1224 d->Destroy();
1225 //Did we pick a .zip file?
1226 try {
1227 zip_reader zr(filename);
1228 std::vector<wxString> files;
1229 for(auto i : zr)
1230 files.push_back(towxstring(i));
1231 wxSingleChoiceDialog* d2 = new wxSingleChoiceDialog(this, wxT("Select file within .zip"),
1232 wxT("Select member"), files.size(), &files[0]);
1233 if(d2->ShowModal() == wxID_CANCEL) {
1234 d2->Destroy();
1235 platform::set_modal_pause(false);
1236 return;
1238 filename = filename + "/" + tostdstring(d2->GetStringSelection());
1239 d2->Destroy();
1240 } catch(...) {
1241 //Ignore error.
1244 try {
1245 std::istream& in = open_file_relative(filename, "");
1246 while(in) {
1247 std::string wname;
1248 std::string wexpr;
1249 std::getline(in, wname);
1250 std::getline(in, wexpr);
1251 new_watches[wname] = wexpr;
1253 delete &in;
1254 } catch(std::exception& e) {
1255 wxMessageDialog* d3 = new wxMessageDialog(this, towxstring(std::string("Can't load memory "
1256 "watch: ") + e.what()), wxT("Error"), wxOK | wxICON_EXCLAMATION);
1257 d3->ShowModal();
1258 d3->Destroy();
1259 platform::set_modal_pause(false);
1260 return;
1263 runemufn([&new_watches, &old_watches]() {
1264 for(auto i : new_watches)
1265 set_watchexpr_for(i.first, i.second);
1266 for(auto i : old_watches)
1267 if(!new_watches.count(i))
1268 set_watchexpr_for(i, "");
1270 platform::set_modal_pause(false);
1273 void wxwin_mainwindow::menu_save_memorywatch(wxCommandEvent& e)
1275 platform::set_modal_pause(true);
1276 std::set<std::string> old_watches;
1277 runemufn([&old_watches]() { old_watches = get_watches(); });
1278 std::string filename;
1280 wxFileDialog* d = new wxFileDialog(this, towxstring("Save watches to file"), wxT("."));
1281 if(d->ShowModal() == wxID_CANCEL) {
1282 d->Destroy();
1283 platform::set_modal_pause(false);
1284 return;
1286 filename = tostdstring(d->GetPath());
1287 d->Destroy();
1289 std::ofstream out(filename.c_str());
1290 for(auto i : old_watches)
1291 out << i << std::endl << get_watchexpr_for(i) << std::endl;
1292 out.close();
1293 platform::set_modal_pause(false);
1297 void wxwin_mainwindow::menu_edit_memorywatch(wxCommandEvent& e)
1299 platform::set_modal_pause(true);
1300 std::set<std::string> bind;
1301 runemufn([&bind]() { bind = get_watches(); });
1302 std::vector<wxString> choices;
1303 choices.push_back(wxT(NEW_WATCH));
1304 for(auto i : bind)
1305 choices.push_back(towxstring(i));
1306 wxSingleChoiceDialog* d = new wxSingleChoiceDialog(this, wxT("Select watch to edit"),
1307 wxT("Select watch"), choices.size(), &choices[0]);
1308 if(d->ShowModal() == wxID_CANCEL) {
1309 d->Destroy();
1310 platform::set_modal_pause(false);
1311 return;
1313 std::string watch = tostdstring(d->GetStringSelection());
1314 d->Destroy();
1315 if(watch == NEW_WATCH) {
1316 wxTextEntryDialog* d2 = new wxTextEntryDialog(this, wxT("Enter name for the new watch:"),
1317 wxT("Enter watch name"));
1318 if(d2->ShowModal() == wxID_CANCEL) {
1319 d2->Destroy();
1320 platform::set_modal_pause(false);
1321 return;
1323 watch = tostdstring(d2->GetValue());
1324 d2->Destroy();
1326 std::string old_watch_value = get_watchexpr_for(watch);
1327 wxTextEntryDialog* d4 = new wxTextEntryDialog(this, wxT("Enter new expression for watch:"), wxT("Edit watch"),
1328 towxstring(old_watch_value), wxOK | wxCANCEL | wxCENTRE);
1329 if(d4->ShowModal() == wxID_CANCEL) {
1330 d4->Destroy();
1331 platform::set_modal_pause(false);
1332 return;
1334 std::string newexpr = tostdstring(d4->GetValue());
1335 runemufn([watch, newexpr]() { set_watchexpr_for(watch, newexpr); });
1336 platform::set_modal_pause(false);
1337 d4->Destroy();