1 #include "core/framebuffer.hpp"
2 #include "core/instance.hpp"
3 #include "core/moviedata.hpp"
4 #include "core/dispatch.hpp"
5 #include "core/window.hpp"
7 #include "interface/controller.hpp"
8 #include "core/mainloop.hpp"
9 #include "core/mbranch.hpp"
10 #include "core/project.hpp"
11 #include "platform/wxwidgets/loadsave.hpp"
12 #include "platform/wxwidgets/platform.hpp"
13 #include "platform/wxwidgets/scrollbar.hpp"
14 #include "platform/wxwidgets/textrender.hpp"
15 #include "library/minmax.hpp"
16 #include "library/string.hpp"
17 #include "library/utf8.hpp"
24 #include <wx/control.h>
25 #include <wx/combobox.h>
26 #include <wx/clipbrd.h>
31 #define UINT64_C(val) val##ULL
33 #include <libswscale/swscale.h>
38 wxID_TOGGLE
= wxID_HIGHEST
+ 1,
42 wxID_CHANGE_LINECOUNT
,
44 wxID_INSERT_AFTER_MULTIPLE
,
52 wxID_SCROLL_CURRENT_FRAME
,
57 wxID_INSERT_CONTROLLER_AFTER
,
58 wxID_DELETE_CONTROLLER_SUBFRAMES
,
65 wxID_MBRANCH_LAST
= wxID_MBRANCH_FIRST
+ 1024
68 void update_movie_state();
72 unsigned lines_to_display
= 28;
73 uint64_t divs
[] = {1000000, 100000, 10000, 1000, 100, 10, 1};
74 uint64_t divsl
[] = {1000000, 100000, 10000, 1000, 100, 10, 0};
75 const unsigned divcnt
= sizeof(divs
)/sizeof(divs
[0]);
80 typedef std::pair
<std::string
, int> returntype
;
84 filedialog_input_params
input(bool save
) const
86 filedialog_input_params ip
;
87 ip
.types
.push_back(filedialog_type_entry("Input tracks (text)", "*.lstt", "lstt"));
88 ip
.types
.push_back(filedialog_type_entry("Input tracks (binary)", "*.lstb", "lstb"));
90 ip
.types
.push_back(filedialog_type_entry("Movie files", "*.lsmv", "lsmv"));
94 std::pair
<std::string
, int> output(const filedialog_output_params
& p
, bool save
) const
97 switch(p
.typechoice
) {
98 case 0: m
= MBRANCH_IMPORT_TEXT
; break;
99 case 1: m
= MBRANCH_IMPORT_BINARY
; break;
100 case 2: m
= MBRANCH_IMPORT_MOVIE
; break;
102 return std::make_pair(p
.path
, m
);
110 unsigned position_left
;
111 unsigned reserved
; //Must be at least 6 for axes.
112 unsigned index
; //Index in poll vector.
113 int type
; //-2 => Port, -1 => Fixed, 0 => Button, 1 => axis.
115 std::u32string title
;
118 port_controller_button::_type axistype
;
121 static control_info
portinfo(unsigned& p
, unsigned port
, unsigned controller
);
122 static control_info
fixedinfo(unsigned& p
, const std::u32string
& str
);
123 static control_info
buttoninfo(unsigned& p
, char32_t character
, const std::u32string
& title
, unsigned idx
,
124 unsigned port
, unsigned controller
);
125 static control_info
axisinfo(unsigned& p
, const std::u32string
& title
, unsigned idx
,
126 unsigned port
, unsigned controller
, port_controller_button::_type _axistype
, int _rmin
, int _rmax
);
129 control_info
control_info::portinfo(unsigned& p
, unsigned port
, unsigned controller
)
133 i
.reserved
= (stringfmt() << port
<< "-" << controller
).str32().length();
140 i
.controller
= controller
;
144 control_info
control_info::fixedinfo(unsigned& p
, const std::u32string
& str
)
148 i
.reserved
= str
.length();
159 control_info
control_info::buttoninfo(unsigned& p
, char32_t character
, const std::u32string
& title
, unsigned idx
,
160 unsigned port
, unsigned controller
)
171 i
.controller
= controller
;
175 control_info
control_info::axisinfo(unsigned& p
, const std::u32string
& title
, unsigned idx
,
176 unsigned port
, unsigned controller
, port_controller_button::_type _axistype
, int _rmin
, int _rmax
)
180 i
.reserved
= title
.length();
189 i
.controller
= controller
;
190 i
.axistype
= _axistype
;
200 void set_types(controller_frame
& f
);
201 short read_index(controller_frame
& f
, unsigned idx
);
202 void write_index(controller_frame
& f
, unsigned idx
, short value
);
203 uint32_t read_pollcount(pollcounter_vector
& v
, unsigned idx
);
204 const std::list
<control_info
>& get_controlinfo() { return controlinfo
; }
205 std::u32string
line1() { return _line1
; }
206 std::u32string
line2() { return _line2
; }
207 size_t width() { return _width
; }
210 std::u32string _line1
;
211 std::u32string _line2
;
213 void add_port(unsigned& c
, unsigned pid
, const port_type
& p
, const port_type_set
& pts
);
214 std::list
<control_info
> controlinfo
;
218 frame_controls::frame_controls()
223 void frame_controls::set_types(controller_frame
& f
)
227 const port_type_set
& pts
= f
.porttypes();
228 unsigned pcnt
= pts
.ports();
229 for(unsigned i
= 0; i
< pcnt
; i
++)
230 add_port(nextp
, i
, pts
.port_type(i
), pts
);
234 void frame_controls::add_port(unsigned& c
, unsigned pid
, const port_type
& p
, const port_type_set
& pts
)
236 const port_controller_set
& pci
= *(p
.controller_info
);
237 for(unsigned i
= 0; i
< pci
.controllers
.size(); i
++) {
238 const port_controller
& pc
= pci
.controllers
[i
];
240 controlinfo
.push_back(control_info::fixedinfo(c
, U
"\u2502"));
242 controlinfo
.push_back(control_info::portinfo(nextp
, pid
, i
+ 1));
243 bool last_multibyte
= false;
244 for(unsigned j
= 0; j
< pc
.buttons
.size(); j
++) {
245 const port_controller_button
& pcb
= pc
.buttons
[j
];
246 unsigned idx
= pts
.triple_to_index(pid
, i
, j
);
247 if(idx
== 0xFFFFFFFFUL
)
249 if(pcb
.type
== port_controller_button::TYPE_BUTTON
) {
252 controlinfo
.push_back(control_info::buttoninfo(c
, pcb
.symbol
, utf8::to32(pcb
.name
),
254 last_multibyte
= false;
255 } else if(pcb
.type
== port_controller_button::TYPE_AXIS
||
256 pcb
.type
== port_controller_button::TYPE_RAXIS
||
257 pcb
.type
== port_controller_button::TYPE_TAXIS
||
258 pcb
.type
== port_controller_button::TYPE_LIGHTGUN
) {
261 controlinfo
.push_back(control_info::axisinfo(c
, utf8::to32(pcb
.name
), idx
, pid
, i
,
262 pcb
.type
, pcb
.rmin
, pcb
.rmax
));
263 last_multibyte
= true;
271 short frame_controls::read_index(controller_frame
& f
, unsigned idx
)
274 return f
.sync() ? 1 : 0;
278 void frame_controls::write_index(controller_frame
& f
, unsigned idx
, short value
)
281 return f
.sync(value
);
282 return f
.axis2(idx
, value
);
285 uint32_t frame_controls::read_pollcount(pollcounter_vector
& v
, unsigned idx
)
288 return max(v
.max_polls(), (uint32_t)1);
289 for(auto i
: controlinfo
)
290 if(idx
== i
.index
&& i
.port
== 0 && i
.controller
== 0)
291 return max(v
.get_polls(idx
), (uint32_t)(v
.get_framepflag() ? 1 : 0));
292 return v
.get_polls(idx
);
295 void frame_controls::format_lines()
298 for(auto i
: controlinfo
) {
299 if(i
.position_left
+ i
.reserved
> _width
)
300 _width
= i
.position_left
+ i
.reserved
;
304 uint32_t off
= divcnt
+ 1;
305 cp1
.resize(_width
+ divcnt
+ 1);
306 cp2
.resize(_width
+ divcnt
+ 1);
307 for(unsigned i
= 0; i
< cp1
.size(); i
++)
308 cp1
[i
] = cp2
[i
] = 32;
309 cp1
[divcnt
] = 0x2502;
310 cp2
[divcnt
] = 0x2502;
312 //For every port-controller, find the least coordinate.
313 for(auto i
: controlinfo
) {
315 auto _title
= i
.title
;
316 std::copy(_title
.begin(), _title
.end(), &cp1
[i
.position_left
+ off
]);
317 } else if(i
.type
== -2) {
318 auto _title
= (stringfmt() << i
.port
<< "-" << i
.controller
).str32();
319 std::copy(_title
.begin(), _title
.end(), &cp1
[i
.position_left
+ off
]);
323 for(auto i
: controlinfo
) {
324 auto _title
= i
.title
;
325 if(i
.type
== -1 || i
.type
== 1)
326 std::copy(_title
.begin(), _title
.end(), &cp2
[i
.position_left
+ off
]);
328 cp2
[i
.position_left
+ off
] = i
.ch
;
336 //TODO: Use real clipboard.
337 std::string clipboard
;
339 void copy_to_clipboard(const std::string
& text
)
344 bool clipboard_has_text()
346 return (clipboard
.length() > 0);
349 void clear_clipboard()
354 std::string
copy_from_clipboard()
359 std::string
encode_line(controller_frame
& f
)
366 std::string
encode_line(frame_controls
& info
, controller_frame
& f
, unsigned port
, unsigned controller
)
368 std::ostringstream x
;
369 bool last_axis
= false;
371 for(auto i
: info
.get_controlinfo()) {
374 if(i
.controller
!= controller
)
380 if(info
.read_index(f
, i
.index
)) {
384 x
<< utf8::to8(std::u32string(tmp1
));
393 x
<< info
.read_index(f
, i
.index
);
402 short read_short(const std::u32string
& s
, size_t& r
)
404 unsigned short _res
= 0;
405 bool negative
= false;
406 if(r
< s
.length() && s
[r
] == '-') {
410 while(r
< s
.length() && s
[r
] >= 48 && s
[r
] <= 57) {
411 _res
= _res
* 10 + (s
[r
] - 48);
414 return negative
? -_res
: _res
;
417 void decode_line(frame_controls
& info
, controller_frame
& f
, std::string line
, unsigned port
,
420 std::u32string _line
= utf8::to32(line
);
421 bool last_axis
= false;
426 for(auto i
: info
.get_controlinfo()) {
429 if(i
.controller
!= controller
)
435 while(ridx
< _line
.length() && (_line
[ridx
] == 9 || _line
[ridx
] == 10 ||
436 _line
[ridx
] == 13 || _line
[ridx
] == 32))
439 y2
= (ridx
< _line
.length()) ? _line
[ridx
++] : 0;
440 if(y2
== U
'-' || y2
== 0)
441 info
.write_index(f
, i
.index
, 0);
443 info
.write_index(f
, i
.index
, 1);
450 while(ridx
< _line
.length() && (_line
[ridx
] == 9 || _line
[ridx
] == 10 ||
451 _line
[ridx
] == 13 || _line
[ridx
] == 32))
453 y
= read_short(_line
, ridx
);
454 info
.write_index(f
, i
.index
, y
);
462 std::string
encode_lines(controller_frame_vector
& fv
, uint64_t start
, uint64_t end
)
464 std::ostringstream x
;
465 x
<< "lsnes-moviedata-whole" << std::endl
;
466 for(uint64_t i
= start
; i
< end
; i
++) {
467 controller_frame tmp
= fv
[i
];
468 x
<< encode_line(tmp
) << std::endl
;
473 std::string
encode_lines(frame_controls
& info
, controller_frame_vector
& fv
, uint64_t start
, uint64_t end
,
474 unsigned port
, unsigned controller
)
476 std::ostringstream x
;
477 x
<< "lsnes-moviedata-controller" << std::endl
;
478 for(uint64_t i
= start
; i
< end
; i
++) {
479 controller_frame tmp
= fv
[i
];
480 x
<< encode_line(info
, tmp
, port
, controller
) << std::endl
;
485 int clipboard_get_data_type()
487 if(!clipboard_has_text())
489 std::string y
= copy_from_clipboard();
490 std::istringstream
x(y
);
492 std::getline(x
, hdr
);
493 if(hdr
== "lsnes-moviedata-whole")
495 if(hdr
== "lsnes-moviedata-controller")
500 std::set
<unsigned> controller_index_set(frame_controls
& info
, unsigned port
, unsigned controller
)
502 std::set
<unsigned> r
;
503 for(auto i
: info
.get_controlinfo()) {
504 if(i
.port
== port
&& i
.controller
== controller
&& (i
.type
== 0 || i
.type
== 1))
510 void move_index_set(frame_controls
& info
, controller_frame_vector
& fv
, uint64_t src
, uint64_t dst
,
511 uint64_t len
, const std::set
<unsigned>& indices
)
515 controller_frame_vector::notify_freeze
freeze(fv
);
518 uint64_t shift
= src
- dst
;
519 for(uint64_t i
= dst
; i
< dst
+ len
; i
++) {
520 controller_frame _src
= fv
[i
+ shift
];
521 controller_frame _dst
= fv
[i
];
522 for(auto j
: indices
)
523 info
.write_index(_dst
, j
, info
.read_index(_src
, j
));
527 uint64_t shift
= dst
- src
;
528 for(uint64_t i
= src
+ len
- 1; i
>= src
&& i
< src
+ len
; i
--) {
529 controller_frame _src
= fv
[i
];
530 controller_frame _dst
= fv
[i
+ shift
];
531 for(auto j
: indices
)
532 info
.write_index(_dst
, j
, info
.read_index(_src
, j
));
537 void zero_index_set(frame_controls
& info
, controller_frame_vector
& fv
, uint64_t dst
, uint64_t len
,
538 const std::set
<unsigned>& indices
)
540 controller_frame_vector::notify_freeze
freeze(fv
);
541 for(uint64_t i
= dst
; i
< dst
+ len
; i
++) {
542 controller_frame _dst
= fv
[i
];
543 for(auto j
: indices
)
544 info
.write_index(_dst
, j
, 0);
548 control_info
find_paired(control_info ci
, const std::list
<control_info
>& info
)
550 if(ci
.axistype
== port_controller_button::TYPE_TAXIS
)
553 bool next_flag
= false;
554 control_info previous
;
556 if(i
.port
!= ci
.port
|| i
.controller
!= ci
.controller
)
558 if(i
.axistype
!= port_controller_button::TYPE_AXIS
&&
559 i
.axistype
!= port_controller_button::TYPE_RAXIS
&&
560 i
.axistype
!= port_controller_button::TYPE_LIGHTGUN
)
564 if(i
.index
== ci
.index
) {
567 next_flag
= true; //Next.
569 return previous
; //Pevious.
578 int32_t value_to_coordinate(int32_t rmin
, int32_t rmax
, int32_t val
, int32_t dim
)
580 //Scale the values to be zero-based.
581 val
= min(max(val
, rmin
), rmax
);
584 int32_t center
= rmax
/ 2;
585 int32_t cc
= (dim
- 1) / 2;
589 //0 => 0, center => cc.
590 return (val
* (int64_t)cc
+ (center
/ 2)) / center
;
593 //center => cc, rmax => dim - 1.
596 int32_t cc2
= (dim
- 1 - cc
);
597 return (val
* (int64_t)cc2
+ (rmax
/ 2)) / rmax
+ cc
;
599 return 0; //NOTREACHED.
602 int32_t coordinate_to_value(int32_t rmin
, int32_t rmax
, int32_t val
, int32_t dim
)
604 if(dim
== rmin
- rmax
+ 1) {
607 val
= min(max(val
, (int32_t)0), dim
- 1);
608 int32_t center
= (rmax
+ rmin
) / 2;
609 int32_t cc
= (dim
- 1) / 2;
613 //0 => rmin, cc => center.
614 return ((center
- rmin
) * (int64_t)val
+ cc
/ 2) / cc
+ rmin
;
617 //cc => center, dim - 1 => rmax.
618 uint32_t cc2
= (dim
- 1 - cc
);
619 return ((rmax
- center
) * (int64_t)(val
- cc
) + cc2
/ 2) / cc2
+ center
;
621 return 0; //NOTREACHED.
624 std::string
windowname(control_info X
, control_info Y
)
626 if(X
.index
== Y
.index
)
627 return (stringfmt() << utf8::to8(X
.title
)).str();
629 return (stringfmt() << utf8::to8(X
.title
) << "/" << utf8::to8(Y
.title
)).str();
632 class window_prompt
: public wxDialog
635 window_prompt(wxWindow
* parent
, uint8_t* _bitmap
, unsigned _width
,
636 unsigned _height
, control_info X
, control_info Y
, unsigned posX
, unsigned posY
)
637 : wxDialog(parent
, wxID_ANY
, towxstring(windowname(X
, Y
)), wxPoint(posX
, posY
))
646 if(X
.index
== Y
.index
) {
647 //One-axis never has a bitmap.
652 wxSizer
* s
= new wxBoxSizer(wxVERTICAL
);
654 s
->Add(panel
= new wxPanel(this, wxID_ANY
, wxDefaultPosition
, wxSize(width
, height
)), 0,
656 panel
->Connect(wxEVT_PAINT
, wxPaintEventHandler(window_prompt::on_paint
), NULL
, this);
657 panel
->Connect(wxEVT_ERASE_BACKGROUND
, wxEraseEventHandler(window_prompt::on_erase
), NULL
,
659 Connect(wxEVT_CLOSE_WINDOW
, wxCloseEventHandler(window_prompt::on_wclose
));
660 panel
->Connect(wxEVT_LEFT_DOWN
, wxMouseEventHandler(window_prompt::on_mouse
), NULL
, this);
661 panel
->Connect(wxEVT_MOTION
, wxMouseEventHandler(window_prompt::on_mouse
), NULL
, this);
664 void on_wclose(wxCloseEvent
& e
)
666 EndModal(wxID_CANCEL
);
668 void on_erase(wxEraseEvent
& e
)
672 void on_paint(wxPaintEvent
& e
)
676 wxBitmap
bmp(wxImage(width
, height
, bitmap
, true));
677 dc
.DrawBitmap(bmp
, 0, 0, false);
679 dc
.SetBackground(*wxWHITE_BRUSH
);
681 auto xval
= value_to_coordinate(cX
.rmin
, cX
.rmax
, 0, width
);
682 auto yval
= value_to_coordinate(cY
.rmin
, cY
.rmax
, 0, height
);
683 dc
.SetPen(*wxBLACK_PEN
);
684 if(cX
.rmin
< 0 && cX
.rmax
> 0)
685 dc
.DrawLine(xval
, 0, xval
, height
);
686 if(!oneaxis
&& cY
.rmin
< 0 && cY
.rmax
> 0)
687 dc
.DrawLine(0, yval
, width
, yval
);
689 dc
.SetPen(*wxRED_PEN
);
690 dc
.DrawLine(mouseX
, 0, mouseX
, height
);
692 dc
.DrawLine(0, mouseY
, width
, mouseY
);
695 void on_mouse(wxMouseEvent
& e
)
698 result
.first
= coordinate_to_value(cX
.rmin
, cX
.rmax
, e
.GetX(), width
);
700 result
.second
= coordinate_to_value(cY
.rmin
, cY
.rmax
, e
.GetY(), height
);
712 std::pair
<int, int> get_results()
717 std::pair
<int, int> result
;
730 std::pair
<int, int> prompt_coodinates_window(wxWindow
* parent
, uint8_t* bitmap
, unsigned width
,
731 unsigned height
, control_info X
, control_info Y
, unsigned posX
, unsigned posY
)
733 window_prompt
* p
= new window_prompt(parent
, bitmap
, width
, height
, X
, Y
, posX
, posY
);
734 if(p
->ShowModal() == wxID_CANCEL
) {
736 throw canceled_exception();
738 auto r
= p
->get_results();
744 class wxeditor_movie
: public wxDialog
747 wxeditor_movie(wxWindow
* parent
);
748 ~wxeditor_movie() throw();
749 bool ShouldPreventAppExit() const;
750 void on_close(wxCommandEvent
& e
);
751 void on_wclose(wxCloseEvent
& e
);
752 void on_focus_wrong(wxFocusEvent
& e
);
753 void on_keyboard_down(wxKeyEvent
& e
);
754 void on_keyboard_up(wxKeyEvent
& e
);
755 scroll_bar
* get_scroll();
758 struct _moviepanel
: public wxPanel
760 _moviepanel(wxeditor_movie
* v
);
761 ~_moviepanel() throw();
762 void signal_repaint();
763 void on_paint(wxPaintEvent
& e
);
764 void on_erase(wxEraseEvent
& e
);
765 void on_mouse(wxMouseEvent
& e
);
766 void on_popup_menu(wxCommandEvent
& e
);
770 void render(text_framebuffer
& fb
, unsigned long long pos
);
771 void on_mouse0(unsigned x
, unsigned y
, bool polarity
, bool shift
, unsigned X
, unsigned Y
);
772 void on_mouse1(unsigned x
, unsigned y
, bool polarity
);
773 void on_mouse2(unsigned x
, unsigned y
, bool polarity
);
774 void popup_axis_panel(uint64_t row
, control_info ci
, unsigned screenX
, unsigned screenY
);
775 void do_toggle_buttons(unsigned idx
, uint64_t row1
, uint64_t row2
, bool force_false
);
776 void do_alter_axis(unsigned idx
, uint64_t row1
, uint64_t row2
);
777 void do_sweep_axis(unsigned idx
, uint64_t row1
, uint64_t row2
);
778 void do_append_frames(uint64_t count
);
779 void do_append_frames();
780 void do_insert_frame_after(uint64_t row
, bool multi
);
781 void do_delete_frame(uint64_t row1
, uint64_t row2
, bool wholeframe
);
782 void do_truncate(uint64_t row
);
783 void do_set_stop_at_frame();
784 void do_scroll_to_frame();
785 void do_scroll_to_current_frame();
786 void do_copy(uint64_t row1
, uint64_t row2
, unsigned port
, unsigned controller
);
787 void do_copy(uint64_t row1
, uint64_t row2
);
788 void do_cut(uint64_t row1
, uint64_t row2
, unsigned port
, unsigned controller
);
789 void do_cut(uint64_t row1
, uint64_t row2
);
790 void do_paste(uint64_t row
, unsigned port
, unsigned controller
, bool append
);
791 void do_paste(uint64_t row
, bool append
);
792 void do_insert_controller(uint64_t row
, unsigned port
, unsigned controller
);
793 void do_delete_controller(uint64_t row1
, uint64_t row2
, unsigned port
, unsigned controller
);
794 uint64_t first_editable(unsigned index
);
795 uint64_t first_nextframe();
796 int width(controller_frame
& f
);
797 std::u32string
render_line1(controller_frame
& f
);
798 std::u32string
render_line2(controller_frame
& f
);
799 void render_linen(text_framebuffer
& fb
, controller_frame
& f
, uint64_t sfn
, int y
);
800 unsigned long long spos
;
804 std::map
<uint64_t, uint64_t> subframe_to_frame
;
805 uint64_t max_subframe
;
806 frame_controls fcontrols
;
813 std::vector
<uint8_t> pixels
;
816 uint64_t rpress_line
;
817 unsigned press_index
;
821 uint64_t cached_cffs
;
822 bool position_locked
;
823 wxMenu
* current_popup
;
824 std::map
<int, std::string
> branch_names
;
826 _moviepanel
* moviepanel
;
827 wxButton
* closebutton
;
828 scroll_bar
* moviescroll
;
834 wxeditor_movie
* movieeditor_open
;
836 //Find the first real editable subframe.
837 //Call only in emulator thread.
838 uint64_t real_first_editable(frame_controls
& fc
, unsigned idx
)
840 uint64_t cffs
= lsnes_instance
.mlogic
.get_movie().get_current_frame_first_subframe();
841 controller_frame_vector
& fv
= *lsnes_instance
.mlogic
.get_mfile().input
;
842 pollcounter_vector
& pv
= lsnes_instance
.mlogic
.get_movie().get_pollcounters();
843 uint64_t vsize
= fv
.size();
844 uint32_t pc
= fc
.read_pollcount(pv
, idx
);
845 for(uint32_t i
= 1; i
< pc
; i
++)
846 if(cffs
+ i
>= vsize
|| fv
[cffs
+ i
].sync())
851 uint64_t real_first_editable(frame_controls
& fc
, std::set
<unsigned> idx
)
855 m
= max(m
, real_first_editable(fc
, i
));
859 //Find the first real editable whole frame.
860 //Call only in emulator thread.
861 uint64_t real_first_nextframe(frame_controls
& fc
)
863 uint64_t base
= real_first_editable(fc
, 0);
864 controller_frame_vector
& fv
= *lsnes_instance
.mlogic
.get_mfile().input
;
865 uint64_t vsize
= fv
.size();
866 for(uint32_t i
= 0;; i
++)
867 if(base
+ i
>= vsize
|| fv
[base
+ i
].sync())
872 wxeditor_movie::_moviepanel::~_moviepanel() throw() {}
873 wxeditor_movie::~wxeditor_movie() throw() {}
875 wxeditor_movie::_moviepanel::_moviepanel(wxeditor_movie
* v
)
876 : wxPanel(v
, wxID_ANY
, wxDefaultPosition
, wxSize(100, 100), wxWANTS_CHARS
)
879 Connect(wxEVT_PAINT
, wxPaintEventHandler(_moviepanel::on_paint
), NULL
, this);
880 Connect(wxEVT_ERASE_BACKGROUND
, wxEraseEventHandler(_moviepanel::on_erase
), NULL
, this);
889 position_locked
= true;
890 current_popup
= NULL
;
892 Connect(wxEVT_LEFT_DOWN
, wxMouseEventHandler(_moviepanel::on_mouse
), NULL
, this);
893 Connect(wxEVT_LEFT_UP
, wxMouseEventHandler(_moviepanel::on_mouse
), NULL
, this);
894 Connect(wxEVT_MIDDLE_DOWN
, wxMouseEventHandler(_moviepanel::on_mouse
), NULL
, this);
895 Connect(wxEVT_MIDDLE_UP
, wxMouseEventHandler(_moviepanel::on_mouse
), NULL
, this);
896 Connect(wxEVT_RIGHT_DOWN
, wxMouseEventHandler(_moviepanel::on_mouse
), NULL
, this);
897 Connect(wxEVT_RIGHT_UP
, wxMouseEventHandler(_moviepanel::on_mouse
), NULL
, this);
898 Connect(wxEVT_MOUSEWHEEL
, wxMouseEventHandler(_moviepanel::on_mouse
), NULL
, this);
904 void wxeditor_movie::_moviepanel::update_cache()
906 movie
& m
= lsnes_instance
.mlogic
.get_movie();
907 controller_frame_vector
& fv
= *lsnes_instance
.mlogic
.get_mfile().input
;
908 if(&m
== prev_obj
&& prev_seqno
== m
.get_seqno()) {
909 //Just process new subframes if any.
910 for(uint64_t i
= max_subframe
; i
< fv
.size(); i
++) {
911 uint64_t prev
= (i
> 0) ? subframe_to_frame
[i
- 1] : 0;
912 controller_frame f
= fv
[i
];
914 subframe_to_frame
[i
] = prev
+ 1;
916 subframe_to_frame
[i
] = prev
;
918 max_subframe
= fv
.size();
921 //Reprocess all subframes.
922 for(uint64_t i
= 0; i
< fv
.size(); i
++) {
923 uint64_t prev
= (i
> 0) ? subframe_to_frame
[i
- 1] : 0;
924 controller_frame f
= fv
[i
];
926 subframe_to_frame
[i
] = prev
+ 1;
928 subframe_to_frame
[i
] = prev
;
930 max_subframe
= fv
.size();
931 controller_frame model
= fv
.blank_frame(false);
932 fcontrols
.set_types(model
);
934 prev_seqno
= m
.get_seqno();
937 int wxeditor_movie::_moviepanel::width(controller_frame
& f
)
940 return divcnt
+ 1 + fcontrols
.width();
943 std::u32string
wxeditor_movie::_moviepanel::render_line1(controller_frame
& f
)
946 return fcontrols
.line1();
949 std::u32string
wxeditor_movie::_moviepanel::render_line2(controller_frame
& f
)
952 return fcontrols
.line2();
955 void wxeditor_movie::_moviepanel::render_linen(text_framebuffer
& fb
, controller_frame
& f
, uint64_t sfn
, int y
)
958 size_t fbstride
= fb
.get_stride();
959 text_framebuffer::element
* _fb
= fb
.get_buffer();
960 text_framebuffer::element e
;
963 for(unsigned i
= 0; i
< divcnt
; i
++) {
964 uint64_t fn
= subframe_to_frame
[sfn
];
965 e
.ch
= (fn
>= divsl
[i
]) ? (((fn
/ divs
[i
]) % 10) + 48) : 32;
966 _fb
[y
* fbstride
+ i
] = e
;
969 _fb
[y
* fbstride
+ divcnt
] = e
;
970 const std::list
<control_info
>& ctrlinfo
= fcontrols
.get_controlinfo();
971 uint64_t curframe
= lsnes_instance
.mlogic
.get_movie().get_current_frame();
972 pollcounter_vector
& pv
= lsnes_instance
.mlogic
.get_movie().get_pollcounters();
973 uint64_t cffs
= lsnes_instance
.mlogic
.get_movie().get_current_frame_first_subframe();
976 if(!lsnes_instance
.mlogic
.get_movie().readonly_mode())
978 else if(subframe_to_frame
[sfn
] < curframe
)
980 else if(subframe_to_frame
[sfn
] > curframe
)
982 bool now
= (subframe_to_frame
[sfn
] == curframe
);
983 unsigned xcord
= 32768;
987 for(auto i
: ctrlinfo
) {
989 unsigned off
= divcnt
+ 1;
990 bool cselected
= (xcord
>= i
.position_left
+ off
&& xcord
< i
.position_left
+ i
.reserved
+ off
);
992 unsigned polls
= fcontrols
.read_pollcount(pv
, i
.index
);
993 rpast
= ((cffs
+ polls
) > sfn
) ? 1 : 0;
995 uint32_t bgc
= 0xC0C0C0;
1006 fb
.write(i
.title
, 0, divcnt
+ 1 + i
.position_left
, y
, 0x000000, 0xFFFFFF);
1007 } else if(i
.type
== 0) {
1010 bool v
= (fcontrols
.read_index(f
, i
.index
) != 0);
1013 fb
.write(c
, 0, divcnt
+ 1 + i
.position_left
, y
, v
? 0x000000 : 0xC8C8C8, bgc
);
1014 } else if(i
.type
== 1) {
1017 sprintf(c
, "%6d", fcontrols
.read_index(f
, i
.index
));
1018 fb
.write(c
, 0, divcnt
+ 1 + i
.position_left
, y
, 0x000000, bgc
);
1023 void wxeditor_movie::_moviepanel::render(text_framebuffer
& fb
, unsigned long long pos
)
1026 controller_frame_vector
& fv
= *lsnes_instance
.mlogic
.get_mfile().input
;
1027 controller_frame cf
= fv
.blank_frame(false);
1028 int _width
= width(cf
);
1029 fb
.set_size(_width
, lines_to_display
+ 3);
1030 size_t fbstride
= fb
.get_stride();
1031 auto fbsize
= fb
.get_characters();
1032 text_framebuffer::element
* _fb
= fb
.get_buffer();
1033 fb
.write((stringfmt() << "Current frame: " << lsnes_instance
.mlogic
.get_movie().get_current_frame() << " of "
1034 << lsnes_instance
.mlogic
.get_movie().get_frame_count()).str(), _width
, 0, 0,
1035 0x000000, 0xFFFFFF);
1036 fb
.write(render_line1(cf
), _width
, 0, 1, 0x000000, 0xFFFFFF);
1037 fb
.write(render_line2(cf
), _width
, 0, 2, 0x000000, 0xFFFFFF);
1038 unsigned long long lines
= fv
.size();
1039 unsigned long long i
;
1041 for(i
= pos
, j
= 3; i
< pos
+ lines_to_display
; i
++, j
++) {
1042 text_framebuffer::element e
;
1048 for(unsigned k
= 0; k
< fbsize
.first
; k
++)
1049 _fb
[j
* fbstride
+ k
] = e
;
1051 controller_frame frame
= fv
[i
];
1052 render_linen(fb
, frame
, i
, j
);
1057 void wxeditor_movie::_moviepanel::do_toggle_buttons(unsigned idx
, uint64_t row1
, uint64_t row2
, bool force_false
)
1059 frame_controls
* _fcontrols
= &fcontrols
;
1060 uint64_t _press_line
= row1
;
1061 uint64_t line
= row2
;
1062 bool _force_false
= force_false
;
1063 if(_press_line
> line
)
1064 std::swap(_press_line
, line
);
1066 lsnes_instance
.iqueue
.run([idx
, _press_line
, line
, _fcontrols
, _force_false
]() {
1067 if(!lsnes_instance
.mlogic
.get_movie().readonly_mode())
1069 uint64_t fedit
= real_first_editable(*_fcontrols
, idx
);
1070 controller_frame_vector
& fv
= *lsnes_instance
.mlogic
.get_mfile().input
;
1071 controller_frame_vector::notify_freeze
freeze(fv
);
1072 for(uint64_t i
= _press_line
; i
<= line
; i
++) {
1073 if(i
< fedit
|| i
>= fv
.size())
1075 controller_frame cf
= fv
[i
];
1077 _fcontrols
->write_index(cf
, idx
, !_fcontrols
->read_index(cf
, idx
));
1079 _fcontrols
->write_index(cf
, idx
, 0);
1084 max_subframe
= _press_line
; //Reparse.
1088 void wxeditor_movie::_moviepanel::do_alter_axis(unsigned idx
, uint64_t row1
, uint64_t row2
)
1090 frame_controls
* _fcontrols
= &fcontrols
;
1091 uint64_t line
= row1
;
1092 uint64_t line2
= row2
;
1095 lsnes_instance
.iqueue
.run([idx
, line
, &value
, _fcontrols
, &valid
]() {
1096 if(!lsnes_instance
.mlogic
.get_movie().readonly_mode()) {
1100 uint64_t fedit
= real_first_editable(*_fcontrols
, idx
);
1101 controller_frame_vector
& fv
= *lsnes_instance
.mlogic
.get_mfile().input
;
1102 if(line
< fedit
|| line
>= fv
.size()) {
1106 controller_frame_vector::notify_freeze
freeze(fv
);
1107 controller_frame cf
= fv
[line
];
1108 value
= _fcontrols
->read_index(cf
, idx
);
1113 std::string text
= pick_text(m
, "Set value", "Enter new value:", (stringfmt() << value
).str());
1114 value
= parse_value
<short>(text
);
1115 } catch(canceled_exception
& e
) {
1117 } catch(std::exception
& e
) {
1118 wxMessageBox(wxT("Invalid value"), _T("Error"), wxICON_EXCLAMATION
| wxOK
, m
);
1122 std::swap(line
, line2
);
1123 lsnes_instance
.iqueue
.run([idx
, line
, line2
, value
, _fcontrols
]() {
1124 uint64_t fedit
= real_first_editable(*_fcontrols
, idx
);
1125 controller_frame_vector
& fv
= *lsnes_instance
.mlogic
.get_mfile().input
;
1126 controller_frame_vector::notify_freeze
freeze(fv
);
1127 for(uint64_t i
= line
; i
<= line2
; i
++) {
1128 if(i
< fedit
|| i
>= fv
.size())
1130 controller_frame cf
= fv
[i
];
1131 _fcontrols
->write_index(cf
, idx
, value
);
1137 void wxeditor_movie::_moviepanel::do_sweep_axis(unsigned idx
, uint64_t row1
, uint64_t row2
)
1139 frame_controls
* _fcontrols
= &fcontrols
;
1140 uint64_t line
= row1
;
1141 uint64_t line2
= row2
;
1146 std::swap(line
, line2
);
1147 lsnes_instance
.iqueue
.run([idx
, line
, line2
, &value
, &value2
, _fcontrols
, &valid
]() {
1148 if(!lsnes_instance
.mlogic
.get_movie().readonly_mode()) {
1152 uint64_t fedit
= real_first_editable(*_fcontrols
, idx
);
1153 controller_frame_vector
& fv
= *lsnes_instance
.mlogic
.get_mfile().input
;
1154 if(line2
< fedit
|| line2
>= fv
.size()) {
1158 controller_frame cf
= fv
[line
];
1159 value
= _fcontrols
->read_index(cf
, idx
);
1160 controller_frame cf2
= fv
[line2
];
1161 value2
= _fcontrols
->read_index(cf2
, idx
);
1165 lsnes_instance
.iqueue
.run([idx
, line
, line2
, value
, value2
, _fcontrols
]() {
1166 uint64_t fedit
= real_first_editable(*_fcontrols
, idx
);
1167 controller_frame_vector
& fv
= *lsnes_instance
.mlogic
.get_mfile().input
;
1168 controller_frame_vector::notify_freeze
freeze(fv
);
1169 for(uint64_t i
= line
+ 1; i
<= line2
- 1; i
++) {
1170 if(i
< fedit
|| i
>= fv
.size())
1172 controller_frame cf
= fv
[i
];
1173 auto tmp2
= static_cast<int64_t>(i
- line
) * (value2
- value
) /
1174 static_cast<int64_t>(line2
- line
);
1175 short tmp
= value
+ tmp2
;
1176 _fcontrols
->write_index(cf
, idx
, tmp
);
1182 void wxeditor_movie::_moviepanel::do_append_frames(uint64_t count
)
1185 uint64_t _count
= count
;
1186 lsnes_instance
.iqueue
.run([_count
]() {
1187 if(!lsnes_instance
.mlogic
.get_movie().readonly_mode())
1189 controller_frame_vector
& fv
= *lsnes_instance
.mlogic
.get_mfile().input
;
1190 controller_frame_vector::notify_freeze
freeze(fv
);
1191 for(uint64_t i
= 0; i
< _count
; i
++)
1192 fv
.append(fv
.blank_frame(true));
1198 void wxeditor_movie::_moviepanel::do_append_frames()
1202 std::string text
= pick_text(m
, "Append frames", "Enter number of frames to append:", "");
1203 value
= parse_value
<uint64_t>(text
);
1204 } catch(canceled_exception
& e
) {
1206 } catch(std::exception
& e
) {
1207 wxMessageBox(wxT("Invalid value"), _T("Error"), wxICON_EXCLAMATION
| wxOK
, m
);
1210 do_append_frames(value
);
1214 void wxeditor_movie::_moviepanel::do_insert_frame_after(uint64_t row
, bool multi
)
1216 uint64_t multicount
= 1;
1219 std::string text
= pick_text(m
, "Append frames", "Enter number of frames to insert:", "");
1220 multicount
= parse_value
<uint64_t>(text
);
1221 } catch(canceled_exception
& e
) {
1223 } catch(std::exception
& e
) {
1224 wxMessageBox(wxT("Invalid value"), _T("Error"), wxICON_EXCLAMATION
| wxOK
, m
);
1229 frame_controls
* _fcontrols
= &fcontrols
;
1230 uint64_t _row
= row
;
1231 lsnes_instance
.iqueue
.run([_row
, _fcontrols
, multicount
]() {
1232 if(!lsnes_instance
.mlogic
.get_movie().readonly_mode())
1234 controller_frame_vector
& fv
= *lsnes_instance
.mlogic
.get_mfile().input
;
1235 uint64_t fedit
= real_first_editable(*_fcontrols
, 0);
1236 //Find the start of the next frame.
1237 uint64_t nframe
= _row
+ 1;
1238 uint64_t vsize
= fv
.size();
1239 while(nframe
< vsize
&& !fv
[nframe
].sync())
1243 controller_frame_vector::notify_freeze
freeze(fv
);
1244 for(uint64_t k
= 0; k
< multicount
; k
++)
1245 fv
.append(fv
.blank_frame(true));
1246 if(nframe
< vsize
) {
1247 //Okay, gotta copy all data after this point. nframe has to be at least 1.
1248 for(uint64_t i
= vsize
- 1; i
>= nframe
; i
--)
1249 fv
[i
+ multicount
] = fv
[i
];
1250 for(uint64_t k
= 0; k
< multicount
; k
++)
1251 fv
[nframe
+ k
] = fv
.blank_frame(true);
1259 void wxeditor_movie::_moviepanel::do_delete_frame(uint64_t row1
, uint64_t row2
, bool wholeframe
)
1262 uint64_t _row1
= row1
;
1263 uint64_t _row2
= row2
;
1264 bool _wholeframe
= wholeframe
;
1265 frame_controls
* _fcontrols
= &fcontrols
;
1266 if(_row1
> _row2
) std::swap(_row1
, _row2
);
1267 lsnes_instance
.iqueue
.run([_row1
, _row2
, _wholeframe
, _fcontrols
]() {
1268 controller_frame_vector
& fv
= *lsnes_instance
.mlogic
.get_mfile().input
;
1269 uint64_t vsize
= fv
.size();
1271 return; //Nothing to do.
1272 controller_frame_vector::notify_freeze
freeze(fv
);
1273 uint64_t row2
= min(_row2
, vsize
- 1);
1274 uint64_t row1
= min(_row1
, vsize
- 1);
1275 row1
= max(row1
, real_first_editable(*_fcontrols
, 0));
1277 if(_row2
< real_first_nextframe(*_fcontrols
))
1278 return; //Nothing to do.
1279 //Scan backwards for the first subframe of this frame and forwards for the last.
1280 uint64_t fsf
= row1
;
1281 uint64_t lsf
= row2
;
1282 if(fv
[_row2
].sync())
1283 lsf
++; //Bump by one so it finds the end.
1284 while(fsf
< vsize
&& !fv
[fsf
].sync())
1286 while(lsf
< vsize
&& !fv
[lsf
].sync())
1288 fsf
= max(fsf
, real_first_editable(*_fcontrols
, 0));
1289 uint64_t tonuke
= lsf
- fsf
;
1290 int64_t frames_tonuke
= 0;
1291 //Count frames nuked.
1292 for(uint64_t i
= fsf
; i
< lsf
; i
++)
1295 //Nuke from fsf to lsf.
1296 for(uint64_t i
= fsf
; i
< vsize
- tonuke
; i
++)
1297 fv
[i
] = fv
[i
+ tonuke
];
1298 fv
.resize(vsize
- tonuke
);
1300 if(row2
< real_first_editable(*_fcontrols
, 0))
1301 return; //Nothing to do.
1302 //The sync flag needs to be inherited if:
1303 //1) Some deleted subframe has sync flag AND
1304 //2) The subframe immediately after deleted region doesn't.
1305 bool inherit_sync
= false;
1306 for(uint64_t i
= row1
; i
<= row2
; i
++)
1307 inherit_sync
= inherit_sync
|| fv
[i
].sync();
1308 inherit_sync
= inherit_sync
&& (row2
+ 1 < vsize
&& !fv
[_row2
+ 1].sync());
1309 int64_t frames_tonuke
= 0;
1310 //Count frames nuked.
1311 for(uint64_t i
= row1
; i
<= row2
; i
++)
1314 //If sync is inherited, one less frame is nuked.
1315 if(inherit_sync
) frames_tonuke
--;
1316 //Nuke the subframes.
1317 uint64_t tonuke
= row2
- row1
+ 1;
1318 for(uint64_t i
= row1
; i
< vsize
- tonuke
; i
++)
1319 fv
[i
] = fv
[i
+ tonuke
];
1320 fv
.resize(vsize
- tonuke
);
1321 //Next subframe inherits the sync flag.
1323 fv
[row1
].sync(true);
1326 max_subframe
= _row1
;
1331 void wxeditor_movie::_moviepanel::do_truncate(uint64_t row
)
1334 uint64_t _row
= row
;
1335 frame_controls
* _fcontrols
= &fcontrols
;
1336 lsnes_instance
.iqueue
.run([_row
, _fcontrols
]() {
1337 controller_frame_vector
& fv
= *lsnes_instance
.mlogic
.get_mfile().input
;
1338 uint64_t vsize
= fv
.size();
1341 if(_row
< real_first_editable(*_fcontrols
, 0))
1343 int64_t delete_count
= 0;
1344 for(uint64_t i
= _row
; i
< vsize
; i
++)
1354 void wxeditor_movie::_moviepanel::do_set_stop_at_frame()
1358 lsnes_instance
.iqueue
.run([&curframe
]() {
1359 curframe
= lsnes_instance
.mlogic
.get_movie().get_current_frame();
1362 std::string text
= pick_text(m
, "Frame", (stringfmt() << "Enter frame to stop at (currently at "
1363 << curframe
<< "):").str(), "");
1364 frame
= parse_value
<uint64_t>(text
);
1365 } catch(canceled_exception
& e
) {
1367 } catch(std::exception
& e
) {
1368 wxMessageBox(wxT("Invalid value"), _T("Error"), wxICON_EXCLAMATION
| wxOK
, m
);
1371 if(frame
< curframe
) {
1372 wxMessageBox(wxT("The movie is already past that point"), _T("Error"), wxICON_EXCLAMATION
| wxOK
, m
);
1375 lsnes_instance
.iqueue
.run([frame
]() {
1376 set_stop_at_frame(frame
);
1380 void wxeditor_movie::_moviepanel::on_mouse0(unsigned x
, unsigned y
, bool polarity
, bool shift
, unsigned X
, unsigned Y
)
1386 press_line
= spos
+ y
- 3;
1393 uint64_t line
= spos
+ y
- 3;
1394 if(press_x
< divcnt
&& x
< divcnt
) {
1395 //Press on frame count.
1396 uint64_t row1
= press_line
;
1397 uint64_t row2
= line
;
1399 std::swap(row1
, row2
);
1400 do_append_frames(row2
- row1
+ 1);
1402 for(auto i
: fcontrols
.get_controlinfo()) {
1403 unsigned off
= divcnt
+ 1;
1404 unsigned idx
= i
.index
;
1405 if((press_x
>= i
.position_left
+ off
&& press_x
< i
.position_left
+ i
.reserved
+ off
) &&
1406 (x
>= i
.position_left
+ off
&& x
< i
.position_left
+ i
.reserved
+ off
)) {
1408 do_toggle_buttons(idx
, press_line
, line
, false);
1409 else if(i
.type
== 1) {
1411 if(press_line
== line
&& (i
.port
|| i
.controller
))
1413 wxPoint spos
= GetScreenPosition();
1414 popup_axis_panel(line
, i
, spos
.x
+ X
, spos
.y
+ Y
);
1415 } catch(canceled_exception
& e
) {
1418 do_alter_axis(idx
, press_line
, line
);
1424 void wxeditor_movie::_moviepanel::popup_axis_panel(uint64_t row
, control_info ci
, unsigned screenX
, unsigned screenY
)
1428 control_info ci2
= find_paired(ci
, fcontrols
.get_controlinfo());
1429 if(ci
.index
== ci2
.index
) {
1431 } else if(ci2
.index
< ci
.index
) {
1438 frame_controls
* _fcontrols
= &fcontrols
;
1439 if(ciX
.index
== ciY
.index
) {
1440 auto c
= prompt_coodinates_window(m
, NULL
, 256, 0, ciX
, ciX
, screenX
, screenY
);
1441 lsnes_instance
.iqueue
.run([ciX
, row
, c
, _fcontrols
]() {
1442 uint64_t fedit
= real_first_editable(*_fcontrols
, ciX
.index
);
1443 if(row
< fedit
) return;
1444 controller_frame_vector
& fv
= *lsnes_instance
.mlogic
.get_mfile().input
;
1445 controller_frame cf
= fv
[row
];
1446 _fcontrols
->write_index(cf
, ciX
.index
, c
.first
);
1449 } else if(ci
.axistype
== port_controller_button::TYPE_LIGHTGUN
) {
1450 framebuffer::raw
& _fb
= lsnes_instance
.fbuf
.render_get_latest_screen();
1451 framebuffer::fb
<false> fb
;
1452 auto osize
= std::make_pair(_fb
.get_width(), _fb
.get_height());
1453 auto size
= our_rom
.rtype
->lightgun_scale();
1454 fb
.reallocate(osize
.first
, osize
.second
, false);
1455 fb
.copy_from(_fb
, 1, 1);
1456 lsnes_instance
.fbuf
.render_get_latest_screen_end();
1457 std::vector
<uint8_t> buf
;
1458 buf
.resize(3 * (ciX
.rmax
- ciX
.rmin
+ 1) * (ciY
.rmax
- ciY
.rmin
+ 1));
1459 unsigned offX
= -ciX
.rmin
;
1460 unsigned offY
= -ciY
.rmin
;
1461 struct SwsContext
* ctx
= sws_getContext(osize
.first
, osize
.second
, PIX_FMT_RGBA
,
1462 size
.first
, size
.second
, PIX_FMT_BGR24
, SWS_POINT
, NULL
, NULL
, NULL
);
1467 srcs
[0] = 4 * (fb
.rowptr(1) - fb
.rowptr(0));
1468 dsts
[0] = 3 * (ciX
.rmax
- ciX
.rmin
+ 1);
1469 srcp
[0] = reinterpret_cast<unsigned char*>(fb
.rowptr(0));
1470 dstp
[0] = &buf
[3 * (offY
* (ciX
.rmax
- ciX
.rmin
+ 1) + offX
)];
1471 memset(&buf
[0], 0, buf
.size());
1472 sws_scale(ctx
, srcp
, srcs
, 0, size
.second
, dstp
, dsts
);
1473 sws_freeContext(ctx
);
1474 auto c
= prompt_coodinates_window(m
, &buf
[0], (ciX
.rmax
- ciX
.rmin
+ 1), (ciY
.rmax
- ciY
.rmin
+ 1),
1475 ciX
, ciY
, screenX
, screenY
);
1476 lsnes_instance
.iqueue
.run([ciX
, ciY
, row
, c
, _fcontrols
]() {
1477 uint64_t fedit
= real_first_editable(*_fcontrols
, ciX
.index
);
1478 fedit
= max(fedit
, real_first_editable(*_fcontrols
, ciY
.index
));
1479 if(row
< fedit
) return;
1480 controller_frame_vector
& fv
= *lsnes_instance
.mlogic
.get_mfile().input
;
1481 controller_frame cf
= fv
[row
];
1482 _fcontrols
->write_index(cf
, ciX
.index
, c
.first
);
1483 _fcontrols
->write_index(cf
, ciY
.index
, c
.second
);
1487 auto c
= prompt_coodinates_window(m
, NULL
, 256, 256, ciX
, ciY
, screenX
, screenY
);
1488 lsnes_instance
.iqueue
.run([ciX
, ciY
, row
, c
, _fcontrols
]() {
1489 uint64_t fedit
= real_first_editable(*_fcontrols
, ciX
.index
);
1490 fedit
= max(fedit
, real_first_editable(*_fcontrols
, ciY
.index
));
1491 if(row
< fedit
) return;
1492 controller_frame_vector
& fv
= *lsnes_instance
.mlogic
.get_mfile().input
;
1493 controller_frame cf
= fv
[row
];
1494 _fcontrols
->write_index(cf
, ciX
.index
, c
.first
);
1495 _fcontrols
->write_index(cf
, ciY
.index
, c
.second
);
1501 void wxeditor_movie::_moviepanel::do_scroll_to_frame()
1505 std::string text
= pick_text(m
, "Frame", (stringfmt() << "Enter frame to scroll to:").str(), "");
1506 frame
= parse_value
<uint64_t>(text
);
1507 } catch(canceled_exception
& e
) {
1509 } catch(std::exception
& e
) {
1510 wxMessageBox(wxT("Invalid value"), _T("Error"), wxICON_EXCLAMATION
| wxOK
, m
);
1513 uint64_t wouldbe
= 0;
1515 uint64_t high
= max_subframe
;
1517 wouldbe
= (low
+ high
) / 2;
1518 if(subframe_to_frame
[wouldbe
] < frame
)
1520 else if(subframe_to_frame
[wouldbe
] > frame
)
1525 while(wouldbe
> 1 && subframe_to_frame
[wouldbe
- 1] == frame
)
1531 void wxeditor_movie::_moviepanel::do_scroll_to_current_frame()
1533 moviepos
= cached_cffs
;
1537 void wxeditor_movie::_moviepanel::on_popup_menu(wxCommandEvent
& e
)
1539 wxMenuItem
* tmpitem
;
1543 unsigned controller
= 0;
1544 for(auto i
: fcontrols
.get_controlinfo())
1545 if(i
.index
== press_index
) {
1547 controller
= i
.controller
;
1552 do_toggle_buttons(press_index
, rpress_line
, press_line
, false);
1555 do_alter_axis(press_index
, rpress_line
, press_line
);
1558 do_toggle_buttons(press_index
, rpress_line
, press_line
, true);
1561 do_sweep_axis(press_index
, rpress_line
, press_line
);
1563 case wxID_APPEND_FRAME
:
1564 do_append_frames(1);
1566 case wxID_APPEND_FRAMES
:
1569 case wxID_INSERT_AFTER
:
1570 do_insert_frame_after(press_line
, false);
1572 case wxID_INSERT_AFTER_MULTIPLE
:
1573 do_insert_frame_after(press_line
, true);
1575 case wxID_DELETE_FRAME
:
1576 do_delete_frame(press_line
, rpress_line
, true);
1578 case wxID_DELETE_SUBFRAME
:
1579 do_delete_frame(press_line
, rpress_line
, false);
1582 do_truncate(press_line
);
1584 case wxID_RUN_TO_FRAME
:
1585 do_set_stop_at_frame();
1587 case wxID_SCROLL_FRAME
:
1588 do_scroll_to_frame();
1590 case wxID_SCROLL_CURRENT_FRAME
:
1591 do_scroll_to_current_frame();
1593 case wxID_POSITION_LOCK
:
1596 tmpitem
= current_popup
->FindItem(wxID_POSITION_LOCK
);
1597 position_locked
= tmpitem
->IsChecked();
1599 case wxID_CHANGE_LINECOUNT
:
1601 std::string text
= pick_text(m
, "Set number of lines", "Set number of lines visible:",
1602 (stringfmt() << lines_to_display
).str());
1603 unsigned tmp
= parse_value
<unsigned>(text
);
1604 if(tmp
< 1 || tmp
> 255)
1605 throw std::runtime_error("Value out of range");
1606 lines_to_display
= tmp
;
1607 m
->get_scroll()->set_page_size(lines_to_display
);
1608 } catch(canceled_exception
& e
) {
1610 } catch(std::exception
& e
) {
1611 wxMessageBox(wxT("Invalid value"), _T("Error"), wxICON_EXCLAMATION
| wxOK
, m
);
1616 case wxID_COPY_FRAMES
:
1617 if(press_index
== std::numeric_limits
<unsigned>::max())
1618 do_copy(rpress_line
, press_line
);
1620 do_copy(rpress_line
, press_line
, port
, controller
);
1622 case wxID_CUT_FRAMES
:
1623 if(press_index
== std::numeric_limits
<unsigned>::max())
1624 do_cut(rpress_line
, press_line
);
1626 do_cut(rpress_line
, press_line
, port
, controller
);
1628 case wxID_PASTE_FRAMES
:
1629 if(press_index
== std::numeric_limits
<unsigned>::max() || clipboard_get_data_type() == 1)
1630 do_paste(press_line
, false);
1632 do_paste(press_line
, port
, controller
, false);
1634 case wxID_PASTE_APPEND
:
1635 if(press_index
== std::numeric_limits
<unsigned>::max() || clipboard_get_data_type() == 1)
1636 do_paste(press_line
, true);
1638 do_paste(press_line
, port
, controller
, true);
1640 case wxID_INSERT_CONTROLLER_AFTER
:
1641 if(press_index
== std::numeric_limits
<unsigned>::max())
1644 do_insert_controller(press_line
, port
, controller
);
1646 case wxID_DELETE_CONTROLLER_SUBFRAMES
:
1647 if(press_index
== std::numeric_limits
<unsigned>::max())
1650 do_delete_controller(press_line
, rpress_line
, port
, controller
);
1652 case wxID_MBRANCH_NEW
:
1654 std::string newname
;
1655 std::string oldname
;
1656 lsnes_instance
.iqueue
.run([&oldname
]() { oldname
= lsnes_instance
.mbranch
.get(); });
1657 newname
= pick_text(this, "Enter new branch name", "Enter name for a new branch (to fork "
1658 "from " + lsnes_instance
.mbranch
.name(oldname
) + "):", "", false);
1659 lsnes_instance
.iqueue
.run_async([this, oldname
, newname
] {
1660 lsnes_instance
.mbranch
._new(newname
, oldname
);
1661 }, [this](std::exception
& e
) {
1662 show_exception(this, "Error creating branch", "Can't create branch", e
);
1664 } catch(canceled_exception
& e
) {
1667 case wxID_MBRANCH_IMPORT
:
1670 std::string filename
;
1672 std::string dbranch
;
1673 auto g
= choose_file_load(this, "Choose file to import", lsnes_instance
.project
.moviepath(),
1677 if(mode
== MBRANCH_IMPORT_MOVIE
) {
1678 std::set
<std::string
> brlist
;
1680 lsnes_instance
.iqueue
.run([this, filename
, &brlist
]() {
1681 brlist
= lsnes_instance
.mbranch
._movie_branches(filename
);
1683 } catch(std::exception
& e
) {
1684 show_exception(this, "Can't get branches in movie", "", e
);
1687 if(brlist
.size() == 0) {
1688 show_message_ok(this, "No branches in movie file",
1689 "Can't import movie file as it has no branches", wxICON_EXCLAMATION
);
1691 } else if(brlist
.size() == 1) {
1692 branch
= *brlist
.begin();
1694 std::vector
<std::string
> choices(brlist
.begin(), brlist
.end());
1695 branch
= pick_among(this, "Select branch to import",
1696 "Select branch to import", choices
, 0);
1698 //Import from movie.
1700 dbranch
= pick_text(this, "Enter new branch name", "Enter name for an imported branch:",
1702 lsnes_instance
.iqueue
.run_async([this, filename
, branch
, dbranch
, mode
]() {
1703 lsnes_instance
.mbranch
.import_branch(filename
, branch
, dbranch
, mode
);
1704 }, [this](std::exception
& e
) {
1705 show_exception(this, "Can't import branch", "", e
);
1707 } catch(canceled_exception
& e
) {
1710 case wxID_MBRANCH_EXPORT
:
1714 auto g
= choose_file_save(this, "Choose file to export", lsnes_instance
.project
.moviepath(),
1718 lsnes_instance
.iqueue
.run_async([this, file
, mode
]() {
1719 std::string bname
= lsnes_instance
.mbranch
.get();
1720 lsnes_instance
.mbranch
.export_branch(file
, bname
, mode
== MBRANCH_IMPORT_BINARY
);
1721 }, [this](std::exception
& e
) {
1722 show_exception(this, "Can't export branch", "", e
);
1724 } catch(canceled_exception
& e
) {
1727 case wxID_MBRANCH_RENAME
:
1729 std::string newname
;
1730 std::string oldname
;
1731 std::set
<std::string
> list
;
1732 lsnes_instance
.iqueue
.run([&list
]() { list
= lsnes_instance
.mbranch
.enumerate(); });
1733 std::vector
<std::string
> choices(list
.begin(), list
.end());
1734 oldname
= pick_among(this, "Select branch to rename", "Select branch to rename",
1736 newname
= pick_text(this, "Enter new branch name", "Enter name for a new branch (to rename "
1737 "'" + lsnes_instance
.mbranch
.name(oldname
) + "'):", oldname
, false);
1738 lsnes_instance
.iqueue
.run_async([this, oldname
, newname
] {
1739 lsnes_instance
.mbranch
.rename(oldname
, newname
);
1740 }, [this](std::exception
& e
) {
1741 show_exception(this, "Error renaming branch", "Can't rename branch", e
);
1743 } catch(canceled_exception
& e
) {
1746 case wxID_MBRANCH_DELETE
:
1748 std::string oldname
;
1749 std::set
<std::string
> list
;
1750 lsnes_instance
.iqueue
.run([&list
]() { list
= lsnes_instance
.mbranch
.enumerate(); });
1751 std::vector
<std::string
> choices(list
.begin(), list
.end());
1752 oldname
= pick_among(this, "Select branch to delete", "Select branch to delete",
1754 lsnes_instance
.iqueue
.run_async([this, oldname
] {
1755 lsnes_instance
.mbranch
._delete(oldname
);
1756 }, [this](std::exception
& e
) {
1757 show_exception(this, "Error deleting branch", "Can't delete branch", e
);
1759 } catch(canceled_exception
& e
) {
1763 if(id
>= wxID_MBRANCH_FIRST
&& id
<= wxID_MBRANCH_LAST
) {
1764 if(!branch_names
.count(id
)) return;
1765 std::string name
= branch_names
[id
];
1766 lsnes_instance
.iqueue
.run_async([this, name
]() {
1767 lsnes_instance
.mbranch
.set(name
);
1768 }, [this](std::exception
& e
) {
1769 show_exception(this, "Error changing branch", "Can't change branch", e
);
1774 uint64_t wxeditor_movie::_moviepanel::first_editable(unsigned index
)
1776 uint64_t cffs
= cached_cffs
;
1777 if(!subframe_to_frame
.count(cffs
))
1779 uint64_t f
= subframe_to_frame
[cffs
];
1780 pollcounter_vector
& pv
= lsnes_instance
.mlogic
.get_movie().get_pollcounters();
1781 uint32_t pc
= fcontrols
.read_pollcount(pv
, index
);
1782 for(uint32_t i
= 1; i
< pc
; i
++)
1783 if(!subframe_to_frame
.count(cffs
+ i
) || subframe_to_frame
[cffs
+ i
] > f
)
1788 uint64_t wxeditor_movie::_moviepanel::first_nextframe()
1790 uint64_t base
= first_editable(0);
1791 if(!subframe_to_frame
.count(cached_cffs
))
1793 uint64_t f
= subframe_to_frame
[cached_cffs
];
1794 for(uint32_t i
= 0;; i
++)
1795 if(!subframe_to_frame
.count(base
+ i
) || subframe_to_frame
[base
+ i
] > f
)
1799 void wxeditor_movie::_moviepanel::on_mouse1(unsigned x
, unsigned y
, bool polarity
) {}
1800 void wxeditor_movie::_moviepanel::on_mouse2(unsigned x
, unsigned y
, bool polarity
)
1803 //Pressing mouse, just record line it was pressed on.
1804 rpress_line
= spos
+ y
- 3;
1810 //Releasing mouse, open popup menu.
1812 unsigned off
= divcnt
+ 1;
1818 press_line
= spos
+ y
- 3;
1820 current_popup
= &menu
;
1821 menu
.Connect(wxEVT_COMMAND_MENU_SELECTED
, wxCommandEventHandler(wxeditor_movie::_moviepanel::on_popup_menu
),
1824 //Find what controller is the click on.
1825 bool clicked_button
= false;
1826 control_info clicked
;
1827 std::string controller_name
;
1829 clicked_button
= false;
1830 press_index
= std::numeric_limits
<unsigned>::max();
1832 for(auto i
: fcontrols
.get_controlinfo())
1833 if(press_x
>= i
.position_left
+ off
&& press_x
< i
.position_left
+ i
.reserved
+ off
) {
1834 if(i
.type
== 0 || i
.type
== 1) {
1835 clicked_button
= true;
1837 controller_name
= (stringfmt() << "controller " << i
.port
<< "-"
1838 << (i
.controller
+ 1)).str();
1839 press_index
= i
.index
;
1844 //Find first editable frame, controllerframe and buttonframe.
1845 bool not_editable
= !lsnes_instance
.mlogic
.get_movie().readonly_mode();
1846 uint64_t eframe_low
= first_editable(0);
1847 uint64_t ebutton_low
= clicked_button
? first_editable(clicked
.index
) : std::numeric_limits
<uint64_t>::max();
1848 uint64_t econtroller_low
= ebutton_low
;
1849 for(auto i
: fcontrols
.get_controlinfo())
1850 if(i
.port
== clicked
.port
&& i
.controller
== clicked
.controller
&& (i
.type
== 0 || i
.type
== 1))
1851 econtroller_low
= max(econtroller_low
, first_editable(i
.index
));
1853 bool click_zero
= (clicked_button
&& !clicked
.port
&& !clicked
.controller
);
1854 bool enable_append_frame
= !not_editable
;
1855 bool enable_toggle_button
= false;
1856 bool enable_change_axis
= false;
1857 bool enable_sweep_axis
= false;
1858 bool enable_insert_frame
= false;
1859 bool enable_insert_controller
= false;
1860 bool enable_delete_frame
= false;
1861 bool enable_delete_subframe
= false;
1862 bool enable_delete_controller_subframe
= false;
1863 bool enable_truncate_movie
= false;
1864 bool enable_cut_frame
= false;
1865 bool enable_copy_frame
= false;
1866 bool enable_paste_frame
= false;
1867 bool enable_paste_append
= false;
1868 std::string copy_title
;
1869 std::string paste_title
;
1871 //Toggle button is enabled if clicked on button and either end is in valid range.
1872 enable_toggle_button
= (!not_editable
&& clicked_button
&& clicked
.type
== 0 && ((press_line
>= ebutton_low
&&
1873 press_line
< linecount
) || (rpress_line
>= ebutton_low
&& rpress_line
< linecount
)));
1874 //Change axis is enabled in similar conditions, except if type is axis.
1875 enable_change_axis
= (!not_editable
&& clicked_button
&& clicked
.type
== 1 && ((press_line
>= ebutton_low
&&
1876 press_line
< linecount
) || (rpress_line
>= ebutton_low
&& rpress_line
< linecount
)));
1877 //Sweep axis is enabled if change axis is enabled and lines don't match.
1878 enable_sweep_axis
= (enable_change_axis
&& press_line
!= rpress_line
);
1879 //Insert frame is enabled if this frame is completely editable and press and release lines match.
1880 enable_insert_frame
= (!not_editable
&& press_line
+ 1 >= eframe_low
&& press_line
< linecount
&&
1881 press_line
== rpress_line
);
1882 //Insert controller frame is enabled if controller is completely editable and lines match.
1883 enable_insert_controller
= (!not_editable
&& clicked_button
&& press_line
>= econtroller_low
&&
1884 press_line
< linecount
&& press_line
== rpress_line
);
1885 enable_insert_controller
= enable_insert_controller
&& (clicked
.port
|| clicked
.controller
);
1886 //Delete frame is enabled if range is completely editable (relative to next-frame).
1887 enable_delete_frame
= (!not_editable
&& press_line
>= first_nextframe() && press_line
< linecount
&&
1888 rpress_line
>= first_nextframe() && rpress_line
< linecount
);
1889 //Delete subframe is enabled if range is completely editable.
1890 enable_delete_subframe
= (!not_editable
&& press_line
>= eframe_low
&& press_line
< linecount
&&
1891 rpress_line
>= eframe_low
&& rpress_line
< linecount
);
1892 //Delete controller subframe is enabled if range is completely controller-editable.
1893 enable_delete_controller_subframe
= (!not_editable
&& clicked_button
&& press_line
>= econtroller_low
&&
1894 press_line
< linecount
&& rpress_line
>= econtroller_low
&& rpress_line
< linecount
);
1895 enable_delete_controller_subframe
= enable_delete_controller_subframe
&& (clicked
.port
|| clicked
.controller
);
1896 //Truncate movie is enabled if lines match and is completely editable.
1897 enable_truncate_movie
= (!not_editable
&& press_line
== rpress_line
&& press_line
>= eframe_low
&&
1898 press_line
< linecount
);
1899 //Cut frames is enabled if range is editable (possibly controller-editable).
1901 enable_cut_frame
= (!not_editable
&& press_line
>= econtroller_low
&& press_line
< linecount
1902 && rpress_line
>= econtroller_low
&& rpress_line
< linecount
&& !click_zero
);
1904 enable_cut_frame
= (!not_editable
&& press_line
>= eframe_low
&& press_line
< linecount
1905 && rpress_line
>= eframe_low
&& rpress_line
< linecount
);
1906 if(clicked_button
&& clipboard_get_data_type() == 0) {
1907 enable_paste_append
= (!not_editable
&& linecount
>= eframe_low
);
1908 enable_paste_frame
= (!not_editable
&& press_line
>= econtroller_low
&& press_line
< linecount
1909 && rpress_line
>= econtroller_low
&& rpress_line
< linecount
&& !click_zero
);
1910 } else if(clipboard_get_data_type() == 1) {
1911 enable_paste_append
= (!not_editable
&& linecount
>= econtroller_low
);
1912 enable_paste_frame
= (!not_editable
&& press_line
>= eframe_low
&& press_line
< linecount
1913 && rpress_line
>= eframe_low
&& rpress_line
< linecount
);
1915 //Copy frames is enabled if range exists.
1916 enable_copy_frame
= (press_line
< linecount
&& rpress_line
< linecount
);
1917 copy_title
= (clicked_button
? controller_name
: "frames");
1918 paste_title
= ((clipboard_get_data_type() == 0) ? copy_title
: "frames");
1920 if(clipboard_get_data_type() == 0 && click_zero
) enable_paste_append
= enable_paste_frame
= false;
1922 if(enable_toggle_button
)
1923 menu
.Append(wxID_TOGGLE
, towxstring(U
"Toggle " + clicked
.title
));
1924 if(enable_change_axis
)
1925 menu
.Append(wxID_CHANGE
, towxstring(U
"Change " + clicked
.title
));
1926 if(enable_sweep_axis
)
1927 menu
.Append(wxID_SWEEP
, towxstring(U
"Sweep " + clicked
.title
));
1928 if(enable_toggle_button
|| enable_change_axis
)
1929 menu
.Append(wxID_CLEAR
, towxstring(U
"Clear " + clicked
.title
));
1930 if(enable_toggle_button
|| enable_change_axis
|| enable_sweep_axis
)
1931 menu
.AppendSeparator();
1932 menu
.Append(wxID_INSERT_AFTER
, wxT("Insert frame after"))->Enable(enable_insert_frame
);
1933 menu
.Append(wxID_INSERT_AFTER_MULTIPLE
, wxT("Insert frames after"))->Enable(enable_insert_frame
);
1934 menu
.Append(wxID_INSERT_CONTROLLER_AFTER
, wxT("Insert controller frame"))
1935 ->Enable(enable_insert_controller
);
1936 menu
.Append(wxID_APPEND_FRAME
, wxT("Append frame"))->Enable(enable_append_frame
);
1937 menu
.Append(wxID_APPEND_FRAMES
, wxT("Append frames..."))->Enable(enable_append_frame
);
1938 menu
.AppendSeparator();
1939 menu
.Append(wxID_DELETE_FRAME
, wxT("Delete frame(s)"))->Enable(enable_delete_frame
);
1940 menu
.Append(wxID_DELETE_SUBFRAME
, wxT("Delete subframe(s)"))->Enable(enable_delete_subframe
);
1941 menu
.Append(wxID_DELETE_CONTROLLER_SUBFRAMES
, wxT("Delete controller subframes(s)"))
1942 ->Enable(enable_delete_controller_subframe
);
1943 menu
.AppendSeparator();
1944 menu
.Append(wxID_TRUNCATE
, wxT("Truncate movie"))->Enable(enable_truncate_movie
);
1945 menu
.AppendSeparator();
1946 menu
.Append(wxID_CUT_FRAMES
, towxstring("Cut " + copy_title
))->Enable(enable_cut_frame
);
1947 menu
.Append(wxID_COPY_FRAMES
, towxstring("Copy " + copy_title
))->Enable(enable_copy_frame
);
1948 menu
.Append(wxID_PASTE_FRAMES
, towxstring("Paste " + paste_title
))->Enable(enable_paste_frame
);
1949 menu
.Append(wxID_PASTE_APPEND
, towxstring("Paste append " + paste_title
))->Enable(enable_paste_append
);
1950 menu
.AppendSeparator();
1951 menu
.Append(wxID_SCROLL_FRAME
, wxT("Scroll to frame..."));
1952 menu
.Append(wxID_SCROLL_CURRENT_FRAME
, wxT("Scroll to current frame"));
1953 menu
.Append(wxID_RUN_TO_FRAME
, wxT("Run to frame..."));
1954 menu
.Append(wxID_CHANGE_LINECOUNT
, wxT("Change number of lines visible"));
1955 menu
.AppendCheckItem(wxID_POSITION_LOCK
, wxT("Lock scroll to playback"))->Check(position_locked
);
1956 menu
.AppendSeparator();
1958 wxMenu
* branches_submenu
= new wxMenu();
1959 branches_submenu
->Append(wxID_MBRANCH_NEW
, wxT("New branch..."));
1960 branches_submenu
->Append(wxID_MBRANCH_IMPORT
, wxT("Import branch..."));
1961 branches_submenu
->Append(wxID_MBRANCH_EXPORT
, wxT("Export branch..."));
1962 branches_submenu
->Append(wxID_MBRANCH_RENAME
, wxT("Rename branch..."));
1963 branches_submenu
->Append(wxID_MBRANCH_DELETE
, wxT("Delete branch..."));
1964 branches_submenu
->AppendSeparator();
1965 std::set
<std::string
> list
;
1966 std::string current
;
1968 lsnes_instance
.iqueue
.run([&list
, ¤t
, &ro
]() {
1969 list
= lsnes_instance
.mbranch
.enumerate();
1970 current
= lsnes_instance
.mbranch
.get();
1971 ro
= lsnes_instance
.mlogic
.get_movie().readonly_mode();
1973 int ass_id
= wxID_MBRANCH_FIRST
;
1974 for(auto i
: list
) {
1975 bool selected
= (i
== current
);
1977 it
= branches_submenu
->AppendCheckItem(ass_id
, towxstring(lsnes_instance
.mbranch
.name(i
)));
1978 branch_names
[ass_id
++] = i
;
1979 if(selected
) it
->Check(selected
);
1982 menu
.AppendSubMenu(branches_submenu
, wxT("Branches"));
1983 menu
.Connect(wxEVT_COMMAND_MENU_SELECTED
, wxCommandEventHandler(wxeditor_movie::_moviepanel::on_popup_menu
),
1985 branches_submenu
->Connect(wxEVT_COMMAND_MENU_SELECTED
,
1986 wxCommandEventHandler(wxeditor_movie::_moviepanel::on_popup_menu
), NULL
, this);
1988 //delete branches_submenu;
1992 int wxeditor_movie::_moviepanel::get_lines()
1994 controller_frame_vector
& fv
= *lsnes_instance
.mlogic
.get_mfile().input
;
1998 void wxeditor_movie::_moviepanel::signal_repaint()
2000 if(requested
|| recursing
)
2002 auto s
= m
->get_scroll();
2004 uint32_t width
, height
;
2006 wxeditor_movie
* m2
= m
;
2007 uint64_t old_cached_cffs
= cached_cffs
;
2008 uint32_t prev_width
, prev_height
;
2009 bool done_again
= false;
2011 lsnes_instance
.iqueue
.run([&lines
, &width
, &height
, m2
, this]() {
2012 lines
= this->get_lines();
2013 if(lines
< lines_to_display
)
2015 else if(this->moviepos
> lines
- lines_to_display
)
2016 this->moviepos
= lines
- lines_to_display
;
2017 this->render(fb
, moviepos
);
2018 auto x
= fb
.get_characters();
2022 if(old_cached_cffs
!= cached_cffs
&& position_locked
&& !done_again
) {
2023 moviepos
= cached_cffs
;
2027 prev_width
= new_width
;
2028 prev_height
= new_height
;
2030 new_height
= height
;
2033 s
->set_range(lines
);
2034 s
->set_position(moviepos
);
2036 auto size
= fb
.get_pixels();
2037 pixels
.resize(size
.first
* size
.second
* 3);
2038 fb
.render((char*)&pixels
[0]);
2039 if(prev_width
!= new_width
|| prev_height
!= new_height
) {
2040 auto cell
= fb
.get_cell();
2041 SetMinSize(wxSize(new_width
* cell
.first
, (lines_to_display
+ 3) * cell
.second
));
2042 if(new_width
> 0 && s
)
2049 void wxeditor_movie::_moviepanel::on_mouse(wxMouseEvent
& e
)
2051 auto cell
= fb
.get_cell();
2052 if(e
.LeftDown() && !e
.ControlDown())
2053 on_mouse0(e
.GetX() / cell
.first
, e
.GetY() / cell
.second
, true, e
.ShiftDown(), e
.GetX(), e
.GetY());
2054 if(e
.LeftUp() && !e
.ControlDown())
2055 on_mouse0(e
.GetX() / cell
.first
, e
.GetY() / cell
.second
, false, e
.ShiftDown(), e
.GetX(), e
.GetY());
2057 on_mouse1(e
.GetX() / cell
.first
, e
.GetY() / cell
.second
, true);
2059 on_mouse1(e
.GetX() / cell
.first
, e
.GetY() / cell
.second
, false);
2060 if(e
.RightDown() || (e
.LeftDown() && e
.ControlDown()))
2061 on_mouse2(e
.GetX() / cell
.first
, e
.GetY() / cell
.second
, true);
2062 if(e
.RightUp() || (e
.LeftUp() && e
.ControlDown()))
2063 on_mouse2(e
.GetX() / cell
.first
, e
.GetY() / cell
.second
, false);
2064 auto s
= m
->get_scroll();
2068 if(e
.ShiftDown() && e
.ControlDown())
2070 s
->apply_wheel(e
.GetWheelRotation(), e
.GetWheelDelta(), speed
);
2073 void wxeditor_movie::_moviepanel::on_erase(wxEraseEvent
& e
)
2078 void wxeditor_movie::_moviepanel::on_paint(wxPaintEvent
& e
)
2080 auto size
= fb
.get_pixels();
2081 if(!size
.first
|| !size
.second
) {
2088 wxBitmap
bmp(wxImage(size
.first
, size
.second
, &pixels
[0], true));
2089 dc
.DrawBitmap(bmp
, 0, 0, false);
2093 void wxeditor_movie::_moviepanel::do_copy(uint64_t row1
, uint64_t row2
, unsigned port
, unsigned controller
)
2095 frame_controls
* _fcontrols
= &fcontrols
;
2096 uint64_t line
= row1
;
2097 uint64_t line2
= row2
;
2099 std::swap(line
, line2
);
2101 lsnes_instance
.iqueue
.run([port
, controller
, line
, line2
, _fcontrols
, &copied
]() {
2102 controller_frame_vector
& fv
= *lsnes_instance
.mlogic
.get_mfile().input
;
2103 uint64_t vsize
= fv
.size();
2106 uint64_t _line
= min(line
, vsize
- 1);
2107 uint64_t _line2
= min(line2
, vsize
- 1);
2108 copied
= encode_lines(*_fcontrols
, fv
, _line
, _line2
+ 1, port
, controller
);
2110 copy_to_clipboard(copied
);
2113 void wxeditor_movie::_moviepanel::do_copy(uint64_t row1
, uint64_t row2
)
2115 uint64_t line
= row1
;
2116 uint64_t line2
= row2
;
2118 std::swap(line
, line2
);
2120 lsnes_instance
.iqueue
.run([line
, line2
, &copied
]() {
2121 controller_frame_vector
& fv
= *lsnes_instance
.mlogic
.get_mfile().input
;
2122 uint64_t vsize
= fv
.size();
2125 uint64_t _line
= min(line
, vsize
- 1);
2126 uint64_t _line2
= min(line2
, vsize
- 1);
2127 copied
= encode_lines(fv
, _line
, _line2
+ 1);
2129 copy_to_clipboard(copied
);
2132 void wxeditor_movie::_moviepanel::do_cut(uint64_t row1
, uint64_t row2
, unsigned port
, unsigned controller
)
2134 do_copy(row1
, row2
, port
, controller
);
2135 do_delete_controller(row1
, row2
, port
, controller
);
2138 void wxeditor_movie::_moviepanel::do_cut(uint64_t row1
, uint64_t row2
)
2140 do_copy(row1
, row2
);
2141 do_delete_frame(row1
, row2
, false);
2144 void wxeditor_movie::_moviepanel::do_paste(uint64_t row
, bool append
)
2146 frame_controls
* _fcontrols
= &fcontrols
;
2148 uint64_t _gapstart
= row
;
2149 std::string cliptext
= copy_from_clipboard();
2150 lsnes_instance
.iqueue
.run([_fcontrols
, &cliptext
, _gapstart
, append
]() {
2151 //Insert enough lines for the pasted content.
2152 uint64_t gapstart
= _gapstart
;
2153 if(!lsnes_instance
.mlogic
.get_movie().readonly_mode())
2155 uint64_t gaplen
= 0;
2156 int64_t newframes
= 0;
2158 std::istringstream
y(cliptext
);
2160 if(!std::getline(y
, z
))
2163 if(z
!= "lsnes-moviedata-whole")
2165 while(std::getline(y
, z
))
2168 controller_frame_vector
& fv
= *lsnes_instance
.mlogic
.get_mfile().input
;
2169 uint64_t vsize
= fv
.size();
2170 if(gapstart
< real_first_editable(*_fcontrols
, 0))
2172 if(gapstart
> vsize
)
2174 controller_frame_vector::notify_freeze
freeze(fv
);
2175 if(append
) gapstart
= vsize
;
2176 for(uint64_t i
= 0; i
< gaplen
; i
++)
2177 fv
.append(fv
.blank_frame(false));
2178 for(uint64_t i
= vsize
- 1; i
>= gapstart
&& i
<= vsize
; i
--)
2179 fv
[i
+ gaplen
] = fv
[i
];
2180 //Write the pasted frames.
2182 std::istringstream
y(cliptext
);
2185 uint64_t idx
= gapstart
;
2186 while(std::getline(y
, z
)) {
2187 fv
[idx
++].deserialize(z
.c_str());
2188 if(fv
[idx
- 1].sync())
2197 void wxeditor_movie::_moviepanel::do_paste(uint64_t row
, unsigned port
, unsigned controller
, bool append
)
2199 if(!port
&& !controller
)
2201 frame_controls
* _fcontrols
= &fcontrols
;
2202 auto iset
= controller_index_set(fcontrols
, port
, controller
);
2204 uint64_t _gapstart
= row
;
2205 std::string cliptext
= copy_from_clipboard();
2206 lsnes_instance
.iqueue
.run([_fcontrols
, iset
, &cliptext
, _gapstart
, port
, controller
, append
]() {
2207 //Insert enough lines for the pasted content.
2208 uint64_t gapstart
= _gapstart
;
2209 if(!lsnes_instance
.mlogic
.get_movie().readonly_mode())
2211 uint64_t gaplen
= 0;
2212 int64_t newframes
= 0;
2214 std::istringstream
y(cliptext
);
2216 if(!std::getline(y
, z
))
2219 if(z
!= "lsnes-moviedata-controller")
2221 while(std::getline(y
, z
)) {
2226 controller_frame_vector
& fv
= *lsnes_instance
.mlogic
.get_mfile().input
;
2227 uint64_t vsize
= fv
.size();
2228 if(gapstart
< real_first_editable(*_fcontrols
, iset
))
2230 if(gapstart
> vsize
)
2232 controller_frame_vector::notify_freeze
freeze(fv
);
2233 if(append
) gapstart
= vsize
;
2234 for(uint64_t i
= 0; i
< gaplen
; i
++)
2235 fv
.append(fv
.blank_frame(true));
2236 move_index_set(*_fcontrols
, fv
, gapstart
, gapstart
+ gaplen
, vsize
- gapstart
, iset
);
2237 //Write the pasted frames.
2239 std::istringstream
y(cliptext
);
2242 uint64_t idx
= gapstart
;
2243 while(std::getline(y
, z
)) {
2244 controller_frame f
= fv
[idx
++];
2245 decode_line(*_fcontrols
, f
, z
, port
, controller
);
2253 void wxeditor_movie::_moviepanel::do_insert_controller(uint64_t row
, unsigned port
, unsigned controller
)
2255 if(!port
&& !controller
)
2257 frame_controls
* _fcontrols
= &fcontrols
;
2258 auto iset
= controller_index_set(fcontrols
, port
, controller
);
2260 uint64_t gapstart
= row
;
2261 lsnes_instance
.iqueue
.run([_fcontrols
, iset
, gapstart
, port
, controller
]() {
2262 //Insert enough lines for the pasted content.
2263 if(!lsnes_instance
.mlogic
.get_movie().readonly_mode())
2265 controller_frame_vector
& fv
= *lsnes_instance
.mlogic
.get_mfile().input
;
2266 uint64_t vsize
= fv
.size();
2267 if(gapstart
< real_first_editable(*_fcontrols
, iset
))
2269 if(gapstart
> vsize
)
2271 fv
.append(fv
.blank_frame(true));
2272 move_index_set(*_fcontrols
, fv
, gapstart
, gapstart
+ 1, vsize
- gapstart
, iset
);
2273 zero_index_set(*_fcontrols
, fv
, gapstart
, 1, iset
);
2279 void wxeditor_movie::_moviepanel::do_delete_controller(uint64_t row1
, uint64_t row2
, unsigned port
,
2280 unsigned controller
)
2282 if(!port
&& !controller
)
2284 frame_controls
* _fcontrols
= &fcontrols
;
2285 auto iset
= controller_index_set(fcontrols
, port
, controller
);
2287 if(row1
> row2
) std::swap(row1
, row2
);
2288 uint64_t gapstart
= row1
;
2289 uint64_t gaplen
= row2
- row1
+ 1;
2290 lsnes_instance
.iqueue
.run([_fcontrols
, iset
, gapstart
, gaplen
, port
, controller
]() {
2291 //Insert enough lines for the pasted content.
2292 if(!lsnes_instance
.mlogic
.get_movie().readonly_mode())
2294 controller_frame_vector
& fv
= *lsnes_instance
.mlogic
.get_mfile().input
;
2295 uint64_t vsize
= fv
.size();
2296 if(gapstart
< real_first_editable(*_fcontrols
, iset
))
2298 if(gapstart
> vsize
)
2300 move_index_set(*_fcontrols
, fv
, gapstart
+ gaplen
, gapstart
, vsize
- gapstart
- gaplen
, iset
);
2301 zero_index_set(*_fcontrols
, fv
, vsize
- gaplen
, gaplen
, iset
);
2308 wxeditor_movie::wxeditor_movie(wxWindow
* parent
)
2309 : wxDialog(parent
, wxID_ANY
, wxT("lsnes: Edit movie"), wxDefaultPosition
, wxSize(-1, -1))
2313 wxFlexGridSizer
* top_s
= new wxFlexGridSizer(2, 1, 0, 0);
2316 wxBoxSizer
* panel_s
= new wxBoxSizer(wxHORIZONTAL
);
2318 panel_s
->Add(moviepanel
= new _moviepanel(this), 1, wxGROW
);
2319 panel_s
->Add(moviescroll
= new scroll_bar(this, wxID_ANY
, true), 0, wxGROW
);
2320 top_s
->Add(panel_s
, 1, wxGROW
);
2322 moviescroll
->set_page_size(lines_to_display
);
2323 moviescroll
->set_handler([this](scroll_bar
& s
) {
2324 this->moviepanel
->moviepos
= s
.get_position();
2325 this->moviepanel
->signal_repaint();
2327 moviepanel
->Connect(wxEVT_KEY_DOWN
, wxKeyEventHandler(wxeditor_movie::on_keyboard_down
), NULL
, this);
2328 moviepanel
->Connect(wxEVT_KEY_UP
, wxKeyEventHandler(wxeditor_movie::on_keyboard_up
), NULL
, this);
2330 wxBoxSizer
* pbutton_s
= new wxBoxSizer(wxHORIZONTAL
);
2331 pbutton_s
->AddStretchSpacer();
2332 pbutton_s
->Add(closebutton
= new wxButton(this, wxID_OK
, wxT("Close")), 0, wxGROW
);
2333 closebutton
->Connect(wxEVT_COMMAND_BUTTON_CLICKED
,
2334 wxCommandEventHandler(wxeditor_movie::on_close
), NULL
, this);
2335 top_s
->Add(pbutton_s
, 0, wxGROW
);
2337 moviepanel
->SetFocus();
2338 moviescroll
->Connect(wxEVT_SET_FOCUS
, wxFocusEventHandler(wxeditor_movie::on_focus_wrong
), NULL
, this);
2339 closebutton
->Connect(wxEVT_SET_FOCUS
, wxFocusEventHandler(wxeditor_movie::on_focus_wrong
), NULL
, this);
2340 Connect(wxEVT_SET_FOCUS
, wxFocusEventHandler(wxeditor_movie::on_focus_wrong
), NULL
, this);
2342 panel_s
->SetSizeHints(this);
2343 pbutton_s
->SetSizeHints(this);
2344 top_s
->SetSizeHints(this);
2345 Connect(wxEVT_CLOSE_WINDOW
, wxCloseEventHandler(wxeditor_movie::on_wclose
));
2348 moviepanel
->signal_repaint();
2351 bool wxeditor_movie::ShouldPreventAppExit() const { return false; }
2353 void wxeditor_movie::on_close(wxCommandEvent
& e
)
2355 movieeditor_open
= NULL
;
2360 void wxeditor_movie::on_wclose(wxCloseEvent
& e
)
2362 bool wasc
= closing
;
2364 movieeditor_open
= NULL
;
2369 void wxeditor_movie::update()
2371 moviepanel
->signal_repaint();
2374 scroll_bar
* wxeditor_movie::get_scroll()
2379 void wxeditor_movie::on_focus_wrong(wxFocusEvent
& e
)
2381 moviepanel
->SetFocus();
2384 void wxeditor_movie_display(wxWindow
* parent
)
2386 if(movieeditor_open
)
2388 wxeditor_movie
* v
= new wxeditor_movie(parent
);
2390 movieeditor_open
= v
;
2393 void wxeditor_movie::on_keyboard_down(wxKeyEvent
& e
)
2395 handle_wx_keyboard(e
, true);
2398 void wxeditor_movie::on_keyboard_up(wxKeyEvent
& e
)
2400 handle_wx_keyboard(e
, false);
2403 void wxeditor_movie_update()
2405 if(movieeditor_open
)
2406 movieeditor_open
->update();