1 #include "core/framebuffer.hpp"
2 #include "core/movie.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
,
51 wxID_SCROLL_CURRENT_FRAME
,
56 wxID_INSERT_CONTROLLER_AFTER
,
57 wxID_DELETE_CONTROLLER_SUBFRAMES
,
64 wxID_MBRANCH_LAST
= wxID_MBRANCH_FIRST
+ 1024
67 void update_movie_state();
71 unsigned lines_to_display
= 28;
72 uint64_t divs
[] = {1000000, 100000, 10000, 1000, 100, 10, 1};
73 uint64_t divsl
[] = {1000000, 100000, 10000, 1000, 100, 10, 0};
74 const unsigned divcnt
= sizeof(divs
)/sizeof(divs
[0]);
79 typedef std::pair
<std::string
, int> returntype
;
83 filedialog_input_params
input(bool save
) const
85 filedialog_input_params ip
;
86 ip
.types
.push_back(filedialog_type_entry("Input tracks (text)", "*.lstt", "lstt"));
87 ip
.types
.push_back(filedialog_type_entry("Input tracks (binary)", "*.lstb", "lstb"));
89 ip
.types
.push_back(filedialog_type_entry("Movie files", "*.lsmv", "lsmv"));
93 std::pair
<std::string
, int> output(const filedialog_output_params
& p
, bool save
) const
96 switch(p
.typechoice
) {
97 case 0: m
= MBRANCH_IMPORT_TEXT
; break;
98 case 1: m
= MBRANCH_IMPORT_BINARY
; break;
99 case 2: m
= MBRANCH_IMPORT_MOVIE
; break;
101 return std::make_pair(p
.path
, m
);
109 unsigned position_left
;
110 unsigned reserved
; //Must be at least 6 for axes.
111 unsigned index
; //Index in poll vector.
112 int type
; //-2 => Port, -1 => Fixed, 0 => Button, 1 => axis.
114 std::u32string title
;
117 port_controller_button::_type axistype
;
120 static control_info
portinfo(unsigned& p
, unsigned port
, unsigned controller
);
121 static control_info
fixedinfo(unsigned& p
, const std::u32string
& str
);
122 static control_info
buttoninfo(unsigned& p
, char32_t character
, const std::u32string
& title
, unsigned idx
,
123 unsigned port
, unsigned controller
);
124 static control_info
axisinfo(unsigned& p
, const std::u32string
& title
, unsigned idx
,
125 unsigned port
, unsigned controller
, port_controller_button::_type _axistype
, int _rmin
, int _rmax
);
128 control_info
control_info::portinfo(unsigned& p
, unsigned port
, unsigned controller
)
132 i
.reserved
= (stringfmt() << port
<< "-" << controller
).str32().length();
139 i
.controller
= controller
;
143 control_info
control_info::fixedinfo(unsigned& p
, const std::u32string
& str
)
147 i
.reserved
= str
.length();
158 control_info
control_info::buttoninfo(unsigned& p
, char32_t character
, const std::u32string
& title
, unsigned idx
,
159 unsigned port
, unsigned controller
)
170 i
.controller
= controller
;
174 control_info
control_info::axisinfo(unsigned& p
, const std::u32string
& title
, unsigned idx
,
175 unsigned port
, unsigned controller
, port_controller_button::_type _axistype
, int _rmin
, int _rmax
)
179 i
.reserved
= title
.length();
188 i
.controller
= controller
;
189 i
.axistype
= _axistype
;
199 void set_types(controller_frame
& f
);
200 short read_index(controller_frame
& f
, unsigned idx
);
201 void write_index(controller_frame
& f
, unsigned idx
, short value
);
202 uint32_t read_pollcount(pollcounter_vector
& v
, unsigned idx
);
203 const std::list
<control_info
>& get_controlinfo() { return controlinfo
; }
204 std::u32string
line1() { return _line1
; }
205 std::u32string
line2() { return _line2
; }
206 size_t width() { return _width
; }
209 std::u32string _line1
;
210 std::u32string _line2
;
212 void add_port(unsigned& c
, unsigned pid
, const port_type
& p
, const port_type_set
& pts
);
213 std::list
<control_info
> controlinfo
;
217 frame_controls::frame_controls()
222 void frame_controls::set_types(controller_frame
& f
)
226 const port_type_set
& pts
= f
.porttypes();
227 unsigned pcnt
= pts
.ports();
228 for(unsigned i
= 0; i
< pcnt
; i
++)
229 add_port(nextp
, i
, pts
.port_type(i
), pts
);
233 void frame_controls::add_port(unsigned& c
, unsigned pid
, const port_type
& p
, const port_type_set
& pts
)
235 const port_controller_set
& pci
= *(p
.controller_info
);
236 for(unsigned i
= 0; i
< pci
.controllers
.size(); i
++) {
237 const port_controller
& pc
= pci
.controllers
[i
];
239 controlinfo
.push_back(control_info::fixedinfo(c
, U
"\u2502"));
241 controlinfo
.push_back(control_info::portinfo(nextp
, pid
, i
+ 1));
242 bool last_multibyte
= false;
243 for(unsigned j
= 0; j
< pc
.buttons
.size(); j
++) {
244 const port_controller_button
& pcb
= pc
.buttons
[j
];
245 unsigned idx
= pts
.triple_to_index(pid
, i
, j
);
246 if(idx
== 0xFFFFFFFFUL
)
248 if(pcb
.type
== port_controller_button::TYPE_BUTTON
) {
251 controlinfo
.push_back(control_info::buttoninfo(c
, pcb
.symbol
, utf8::to32(pcb
.name
),
253 last_multibyte
= false;
254 } else if(pcb
.type
== port_controller_button::TYPE_AXIS
||
255 pcb
.type
== port_controller_button::TYPE_RAXIS
||
256 pcb
.type
== port_controller_button::TYPE_TAXIS
||
257 pcb
.type
== port_controller_button::TYPE_LIGHTGUN
) {
260 controlinfo
.push_back(control_info::axisinfo(c
, utf8::to32(pcb
.name
), idx
, pid
, i
,
261 pcb
.type
, pcb
.rmin
, pcb
.rmax
));
262 last_multibyte
= true;
270 short frame_controls::read_index(controller_frame
& f
, unsigned idx
)
273 return f
.sync() ? 1 : 0;
277 void frame_controls::write_index(controller_frame
& f
, unsigned idx
, short value
)
280 return f
.sync(value
);
281 return f
.axis2(idx
, value
);
284 uint32_t frame_controls::read_pollcount(pollcounter_vector
& v
, unsigned idx
)
287 return max(v
.max_polls(), (uint32_t)1);
288 for(auto i
: controlinfo
)
289 if(idx
== i
.index
&& i
.port
== 0 && i
.controller
== 0)
290 return max(v
.get_polls(idx
), (uint32_t)(v
.get_framepflag() ? 1 : 0));
291 return v
.get_polls(idx
);
294 void frame_controls::format_lines()
297 for(auto i
: controlinfo
) {
298 if(i
.position_left
+ i
.reserved
> _width
)
299 _width
= i
.position_left
+ i
.reserved
;
303 uint32_t off
= divcnt
+ 1;
304 cp1
.resize(_width
+ divcnt
+ 1);
305 cp2
.resize(_width
+ divcnt
+ 1);
306 for(unsigned i
= 0; i
< cp1
.size(); i
++)
307 cp1
[i
] = cp2
[i
] = 32;
308 cp1
[divcnt
] = 0x2502;
309 cp2
[divcnt
] = 0x2502;
311 //For every port-controller, find the least coordinate.
312 for(auto i
: controlinfo
) {
314 auto _title
= i
.title
;
315 std::copy(_title
.begin(), _title
.end(), &cp1
[i
.position_left
+ off
]);
316 } else if(i
.type
== -2) {
317 auto _title
= (stringfmt() << i
.port
<< "-" << i
.controller
).str32();
318 std::copy(_title
.begin(), _title
.end(), &cp1
[i
.position_left
+ off
]);
322 for(auto i
: controlinfo
) {
323 auto _title
= i
.title
;
324 if(i
.type
== -1 || i
.type
== 1)
325 std::copy(_title
.begin(), _title
.end(), &cp2
[i
.position_left
+ off
]);
327 cp2
[i
.position_left
+ off
] = i
.ch
;
335 //TODO: Use real clipboard.
336 std::string clipboard
;
338 void copy_to_clipboard(const std::string
& text
)
343 bool clipboard_has_text()
345 return (clipboard
.length() > 0);
348 void clear_clipboard()
353 std::string
copy_from_clipboard()
358 std::string
encode_line(controller_frame
& f
)
365 std::string
encode_line(frame_controls
& info
, controller_frame
& f
, unsigned port
, unsigned controller
)
367 std::ostringstream x
;
368 bool last_axis
= false;
370 for(auto i
: info
.get_controlinfo()) {
373 if(i
.controller
!= controller
)
379 if(info
.read_index(f
, i
.index
)) {
383 x
<< utf8::to8(std::u32string(tmp1
));
392 x
<< info
.read_index(f
, i
.index
);
401 short read_short(const std::u32string
& s
, size_t& r
)
403 unsigned short _res
= 0;
404 bool negative
= false;
405 if(r
< s
.length() && s
[r
] == '-') {
409 while(r
< s
.length() && s
[r
] >= 48 && s
[r
] <= 57) {
410 _res
= _res
* 10 + (s
[r
] - 48);
413 return negative
? -_res
: _res
;
416 void decode_line(frame_controls
& info
, controller_frame
& f
, std::string line
, unsigned port
,
419 std::u32string _line
= utf8::to32(line
);
420 bool last_axis
= false;
425 for(auto i
: info
.get_controlinfo()) {
428 if(i
.controller
!= controller
)
434 while(ridx
< _line
.length() && (_line
[ridx
] == 9 || _line
[ridx
] == 10 ||
435 _line
[ridx
] == 13 || _line
[ridx
] == 32))
438 y2
= (ridx
< _line
.length()) ? _line
[ridx
++] : 0;
439 if(y2
== U
'-' || y2
== 0)
440 info
.write_index(f
, i
.index
, 0);
442 info
.write_index(f
, i
.index
, 1);
449 while(ridx
< _line
.length() && (_line
[ridx
] == 9 || _line
[ridx
] == 10 ||
450 _line
[ridx
] == 13 || _line
[ridx
] == 32))
452 y
= read_short(_line
, ridx
);
453 info
.write_index(f
, i
.index
, y
);
461 std::string
encode_lines(controller_frame_vector
& fv
, uint64_t start
, uint64_t end
)
463 std::ostringstream x
;
464 x
<< "lsnes-moviedata-whole" << std::endl
;
465 for(uint64_t i
= start
; i
< end
; i
++) {
466 controller_frame tmp
= fv
[i
];
467 x
<< encode_line(tmp
) << std::endl
;
472 std::string
encode_lines(frame_controls
& info
, controller_frame_vector
& fv
, uint64_t start
, uint64_t end
,
473 unsigned port
, unsigned controller
)
475 std::ostringstream x
;
476 x
<< "lsnes-moviedata-controller" << std::endl
;
477 for(uint64_t i
= start
; i
< end
; i
++) {
478 controller_frame tmp
= fv
[i
];
479 x
<< encode_line(info
, tmp
, port
, controller
) << std::endl
;
484 int clipboard_get_data_type()
486 if(!clipboard_has_text())
488 std::string y
= copy_from_clipboard();
489 std::istringstream
x(y
);
491 std::getline(x
, hdr
);
492 if(hdr
== "lsnes-moviedata-whole")
494 if(hdr
== "lsnes-moviedata-controller")
499 std::set
<unsigned> controller_index_set(frame_controls
& info
, unsigned port
, unsigned controller
)
501 std::set
<unsigned> r
;
502 for(auto i
: info
.get_controlinfo()) {
503 if(i
.port
== port
&& i
.controller
== controller
&& (i
.type
== 0 || i
.type
== 1))
509 void move_index_set(frame_controls
& info
, controller_frame_vector
& fv
, uint64_t src
, uint64_t dst
,
510 uint64_t len
, const std::set
<unsigned>& indices
)
514 controller_frame_vector::notify_freeze
freeze(fv
);
517 uint64_t shift
= src
- dst
;
518 for(uint64_t i
= dst
; i
< dst
+ len
; i
++) {
519 controller_frame _src
= fv
[i
+ shift
];
520 controller_frame _dst
= fv
[i
];
521 for(auto j
: indices
)
522 info
.write_index(_dst
, j
, info
.read_index(_src
, j
));
526 uint64_t shift
= dst
- src
;
527 for(uint64_t i
= src
+ len
- 1; i
>= src
&& i
< src
+ len
; i
--) {
528 controller_frame _src
= fv
[i
];
529 controller_frame _dst
= fv
[i
+ shift
];
530 for(auto j
: indices
)
531 info
.write_index(_dst
, j
, info
.read_index(_src
, j
));
536 void zero_index_set(frame_controls
& info
, controller_frame_vector
& fv
, uint64_t dst
, uint64_t len
,
537 const std::set
<unsigned>& indices
)
539 controller_frame_vector::notify_freeze
freeze(fv
);
540 for(uint64_t i
= dst
; i
< dst
+ len
; i
++) {
541 controller_frame _dst
= fv
[i
];
542 for(auto j
: indices
)
543 info
.write_index(_dst
, j
, 0);
547 control_info
find_paired(control_info ci
, const std::list
<control_info
>& info
)
549 if(ci
.axistype
== port_controller_button::TYPE_TAXIS
)
552 bool next_flag
= false;
553 control_info previous
;
555 if(i
.port
!= ci
.port
|| i
.controller
!= ci
.controller
)
557 if(i
.axistype
!= port_controller_button::TYPE_AXIS
&&
558 i
.axistype
!= port_controller_button::TYPE_RAXIS
&&
559 i
.axistype
!= port_controller_button::TYPE_LIGHTGUN
)
563 if(i
.index
== ci
.index
) {
566 next_flag
= true; //Next.
568 return previous
; //Pevious.
577 int32_t value_to_coordinate(int32_t rmin
, int32_t rmax
, int32_t val
, int32_t dim
)
579 //Scale the values to be zero-based.
580 val
= min(max(val
, rmin
), rmax
);
583 int32_t center
= rmax
/ 2;
584 int32_t cc
= (dim
- 1) / 2;
588 //0 => 0, center => cc.
589 return (val
* (int64_t)cc
+ (center
/ 2)) / center
;
592 //center => cc, rmax => dim - 1.
595 int32_t cc2
= (dim
- 1 - cc
);
596 return (val
* (int64_t)cc2
+ (rmax
/ 2)) / rmax
+ cc
;
598 return 0; //NOTREACHED.
601 int32_t coordinate_to_value(int32_t rmin
, int32_t rmax
, int32_t val
, int32_t dim
)
603 if(dim
== rmin
- rmax
+ 1) {
606 val
= min(max(val
, (int32_t)0), dim
- 1);
607 int32_t center
= (rmax
+ rmin
) / 2;
608 int32_t cc
= (dim
- 1) / 2;
612 //0 => rmin, cc => center.
613 return ((center
- rmin
) * (int64_t)val
+ cc
/ 2) / cc
+ rmin
;
616 //cc => center, dim - 1 => rmax.
617 uint32_t cc2
= (dim
- 1 - cc
);
618 return ((rmax
- center
) * (int64_t)(val
- cc
) + cc2
/ 2) / cc2
+ center
;
620 return 0; //NOTREACHED.
623 std::string
windowname(control_info X
, control_info Y
)
625 if(X
.index
== Y
.index
)
626 return (stringfmt() << utf8::to8(X
.title
)).str();
628 return (stringfmt() << utf8::to8(X
.title
) << "/" << utf8::to8(Y
.title
)).str();
631 class window_prompt
: public wxDialog
634 window_prompt(wxWindow
* parent
, uint8_t* _bitmap
, unsigned _width
,
635 unsigned _height
, control_info X
, control_info Y
, unsigned posX
, unsigned posY
)
636 : wxDialog(parent
, wxID_ANY
, towxstring(windowname(X
, Y
)), wxPoint(posX
, posY
))
645 if(X
.index
== Y
.index
) {
646 //One-axis never has a bitmap.
651 wxSizer
* s
= new wxBoxSizer(wxVERTICAL
);
653 s
->Add(panel
= new wxPanel(this, wxID_ANY
, wxDefaultPosition
, wxSize(width
, height
)), 0,
655 panel
->Connect(wxEVT_PAINT
, wxPaintEventHandler(window_prompt::on_paint
), NULL
, this);
656 panel
->Connect(wxEVT_ERASE_BACKGROUND
, wxEraseEventHandler(window_prompt::on_erase
), NULL
,
658 Connect(wxEVT_CLOSE_WINDOW
, wxCloseEventHandler(window_prompt::on_wclose
));
659 panel
->Connect(wxEVT_LEFT_DOWN
, wxMouseEventHandler(window_prompt::on_mouse
), NULL
, this);
660 panel
->Connect(wxEVT_MOTION
, wxMouseEventHandler(window_prompt::on_mouse
), NULL
, this);
663 void on_wclose(wxCloseEvent
& e
)
665 EndModal(wxID_CANCEL
);
667 void on_erase(wxEraseEvent
& e
)
671 void on_paint(wxPaintEvent
& e
)
675 wxBitmap
bmp(wxImage(width
, height
, bitmap
, true));
676 dc
.DrawBitmap(bmp
, 0, 0, false);
678 dc
.SetBackground(*wxWHITE_BRUSH
);
680 auto xval
= value_to_coordinate(cX
.rmin
, cX
.rmax
, 0, width
);
681 auto yval
= value_to_coordinate(cY
.rmin
, cY
.rmax
, 0, height
);
682 dc
.SetPen(*wxBLACK_PEN
);
683 if(cX
.rmin
< 0 && cX
.rmax
> 0)
684 dc
.DrawLine(xval
, 0, xval
, height
);
685 if(!oneaxis
&& cY
.rmin
< 0 && cY
.rmax
> 0)
686 dc
.DrawLine(0, yval
, width
, yval
);
688 dc
.SetPen(*wxRED_PEN
);
689 dc
.DrawLine(mouseX
, 0, mouseX
, height
);
691 dc
.DrawLine(0, mouseY
, width
, mouseY
);
694 void on_mouse(wxMouseEvent
& e
)
697 result
.first
= coordinate_to_value(cX
.rmin
, cX
.rmax
, e
.GetX(), width
);
699 result
.second
= coordinate_to_value(cY
.rmin
, cY
.rmax
, e
.GetY(), height
);
711 std::pair
<int, int> get_results()
716 std::pair
<int, int> result
;
729 std::pair
<int, int> prompt_coodinates_window(wxWindow
* parent
, uint8_t* bitmap
, unsigned width
,
730 unsigned height
, control_info X
, control_info Y
, unsigned posX
, unsigned posY
)
732 window_prompt
* p
= new window_prompt(parent
, bitmap
, width
, height
, X
, Y
, posX
, posY
);
733 if(p
->ShowModal() == wxID_CANCEL
) {
735 throw canceled_exception();
737 auto r
= p
->get_results();
743 class wxeditor_movie
: public wxDialog
746 wxeditor_movie(wxWindow
* parent
);
747 ~wxeditor_movie() throw();
748 bool ShouldPreventAppExit() const;
749 void on_close(wxCommandEvent
& e
);
750 void on_wclose(wxCloseEvent
& e
);
751 void on_focus_wrong(wxFocusEvent
& e
);
752 void on_keyboard_down(wxKeyEvent
& e
);
753 void on_keyboard_up(wxKeyEvent
& e
);
754 scroll_bar
* get_scroll();
757 struct _moviepanel
: public wxPanel
759 _moviepanel(wxeditor_movie
* v
);
760 ~_moviepanel() throw();
761 void signal_repaint();
762 void on_paint(wxPaintEvent
& e
);
763 void on_erase(wxEraseEvent
& e
);
764 void on_mouse(wxMouseEvent
& e
);
765 void on_popup_menu(wxCommandEvent
& e
);
769 void render(text_framebuffer
& fb
, unsigned long long pos
);
770 void on_mouse0(unsigned x
, unsigned y
, bool polarity
, bool shift
, unsigned X
, unsigned Y
);
771 void on_mouse1(unsigned x
, unsigned y
, bool polarity
);
772 void on_mouse2(unsigned x
, unsigned y
, bool polarity
);
773 void popup_axis_panel(uint64_t row
, control_info ci
, unsigned screenX
, unsigned screenY
);
774 void do_toggle_buttons(unsigned idx
, uint64_t row1
, uint64_t row2
);
775 void do_alter_axis(unsigned idx
, uint64_t row1
, uint64_t row2
);
776 void do_sweep_axis(unsigned idx
, uint64_t row1
, uint64_t row2
);
777 void do_append_frames(uint64_t count
);
778 void do_append_frames();
779 void do_insert_frame_after(uint64_t row
);
780 void do_delete_frame(uint64_t row1
, uint64_t row2
, bool wholeframe
);
781 void do_truncate(uint64_t row
);
782 void do_set_stop_at_frame();
783 void do_scroll_to_frame();
784 void do_scroll_to_current_frame();
785 void do_copy(uint64_t row1
, uint64_t row2
, unsigned port
, unsigned controller
);
786 void do_copy(uint64_t row1
, uint64_t row2
);
787 void do_cut(uint64_t row1
, uint64_t row2
, unsigned port
, unsigned controller
);
788 void do_cut(uint64_t row1
, uint64_t row2
);
789 void do_paste(uint64_t row
, unsigned port
, unsigned controller
, bool append
);
790 void do_paste(uint64_t row
, bool append
);
791 void do_insert_controller(uint64_t row
, unsigned port
, unsigned controller
);
792 void do_delete_controller(uint64_t row1
, uint64_t row2
, unsigned port
, unsigned controller
);
793 uint64_t first_editable(unsigned index
);
794 uint64_t first_nextframe();
795 int width(controller_frame
& f
);
796 std::u32string
render_line1(controller_frame
& f
);
797 std::u32string
render_line2(controller_frame
& f
);
798 void render_linen(text_framebuffer
& fb
, controller_frame
& f
, uint64_t sfn
, int y
);
799 unsigned long long spos
;
803 std::map
<uint64_t, uint64_t> subframe_to_frame
;
804 uint64_t max_subframe
;
805 frame_controls fcontrols
;
812 std::vector
<uint8_t> pixels
;
815 uint64_t rpress_line
;
816 unsigned press_index
;
820 uint64_t cached_cffs
;
821 bool position_locked
;
822 wxMenu
* current_popup
;
823 std::map
<int, std::string
> branch_names
;
825 _moviepanel
* moviepanel
;
826 wxButton
* closebutton
;
827 scroll_bar
* moviescroll
;
833 wxeditor_movie
* movieeditor_open
;
835 //Find the first real editable subframe.
836 //Call only in emulator thread.
837 uint64_t real_first_editable(frame_controls
& fc
, unsigned idx
)
839 uint64_t cffs
= movb
.get_movie().get_current_frame_first_subframe();
840 controller_frame_vector
& fv
= *movb
.get_mfile().input
;
841 pollcounter_vector
& pv
= movb
.get_movie().get_pollcounters();
842 uint64_t vsize
= fv
.size();
843 uint32_t pc
= fc
.read_pollcount(pv
, idx
);
844 for(uint32_t i
= 1; i
< pc
; i
++)
845 if(cffs
+ i
>= vsize
|| fv
[cffs
+ i
].sync())
850 uint64_t real_first_editable(frame_controls
& fc
, std::set
<unsigned> idx
)
854 m
= max(m
, real_first_editable(fc
, i
));
858 //Find the first real editable whole frame.
859 //Call only in emulator thread.
860 uint64_t real_first_nextframe(frame_controls
& fc
)
862 uint64_t base
= real_first_editable(fc
, 0);
863 controller_frame_vector
& fv
= *movb
.get_mfile().input
;
864 uint64_t vsize
= fv
.size();
865 for(uint32_t i
= 0;; i
++)
866 if(base
+ i
>= vsize
|| fv
[base
+ i
].sync())
871 wxeditor_movie::_moviepanel::~_moviepanel() throw() {}
872 wxeditor_movie::~wxeditor_movie() throw() {}
874 wxeditor_movie::_moviepanel::_moviepanel(wxeditor_movie
* v
)
875 : wxPanel(v
, wxID_ANY
, wxDefaultPosition
, wxSize(100, 100), wxWANTS_CHARS
)
878 Connect(wxEVT_PAINT
, wxPaintEventHandler(_moviepanel::on_paint
), NULL
, this);
879 Connect(wxEVT_ERASE_BACKGROUND
, wxEraseEventHandler(_moviepanel::on_erase
), NULL
, this);
888 position_locked
= true;
889 current_popup
= NULL
;
891 Connect(wxEVT_LEFT_DOWN
, wxMouseEventHandler(_moviepanel::on_mouse
), NULL
, this);
892 Connect(wxEVT_LEFT_UP
, wxMouseEventHandler(_moviepanel::on_mouse
), NULL
, this);
893 Connect(wxEVT_MIDDLE_DOWN
, wxMouseEventHandler(_moviepanel::on_mouse
), NULL
, this);
894 Connect(wxEVT_MIDDLE_UP
, wxMouseEventHandler(_moviepanel::on_mouse
), NULL
, this);
895 Connect(wxEVT_RIGHT_DOWN
, wxMouseEventHandler(_moviepanel::on_mouse
), NULL
, this);
896 Connect(wxEVT_RIGHT_UP
, wxMouseEventHandler(_moviepanel::on_mouse
), NULL
, this);
897 Connect(wxEVT_MOUSEWHEEL
, wxMouseEventHandler(_moviepanel::on_mouse
), NULL
, this);
903 void wxeditor_movie::_moviepanel::update_cache()
905 movie
& m
= movb
.get_movie();
906 controller_frame_vector
& fv
= *movb
.get_mfile().input
;
907 if(&m
== prev_obj
&& prev_seqno
== m
.get_seqno()) {
908 //Just process new subframes if any.
909 for(uint64_t i
= max_subframe
; i
< fv
.size(); i
++) {
910 uint64_t prev
= (i
> 0) ? subframe_to_frame
[i
- 1] : 0;
911 controller_frame f
= fv
[i
];
913 subframe_to_frame
[i
] = prev
+ 1;
915 subframe_to_frame
[i
] = prev
;
917 max_subframe
= fv
.size();
920 //Reprocess all subframes.
921 for(uint64_t i
= 0; i
< fv
.size(); i
++) {
922 uint64_t prev
= (i
> 0) ? subframe_to_frame
[i
- 1] : 0;
923 controller_frame f
= fv
[i
];
925 subframe_to_frame
[i
] = prev
+ 1;
927 subframe_to_frame
[i
] = prev
;
929 max_subframe
= fv
.size();
930 controller_frame model
= fv
.blank_frame(false);
931 fcontrols
.set_types(model
);
933 prev_seqno
= m
.get_seqno();
936 int wxeditor_movie::_moviepanel::width(controller_frame
& f
)
939 return divcnt
+ 1 + fcontrols
.width();
942 std::u32string
wxeditor_movie::_moviepanel::render_line1(controller_frame
& f
)
945 return fcontrols
.line1();
948 std::u32string
wxeditor_movie::_moviepanel::render_line2(controller_frame
& f
)
951 return fcontrols
.line2();
954 void wxeditor_movie::_moviepanel::render_linen(text_framebuffer
& fb
, controller_frame
& f
, uint64_t sfn
, int y
)
957 size_t fbstride
= fb
.get_stride();
958 text_framebuffer::element
* _fb
= fb
.get_buffer();
959 text_framebuffer::element e
;
962 for(unsigned i
= 0; i
< divcnt
; i
++) {
963 uint64_t fn
= subframe_to_frame
[sfn
];
964 e
.ch
= (fn
>= divsl
[i
]) ? (((fn
/ divs
[i
]) % 10) + 48) : 32;
965 _fb
[y
* fbstride
+ i
] = e
;
968 _fb
[y
* fbstride
+ divcnt
] = e
;
969 const std::list
<control_info
>& ctrlinfo
= fcontrols
.get_controlinfo();
970 uint64_t curframe
= movb
.get_movie().get_current_frame();
971 pollcounter_vector
& pv
= movb
.get_movie().get_pollcounters();
972 uint64_t cffs
= movb
.get_movie().get_current_frame_first_subframe();
975 if(!movb
.get_movie().readonly_mode())
977 else if(subframe_to_frame
[sfn
] < curframe
)
979 else if(subframe_to_frame
[sfn
] > curframe
)
981 bool now
= (subframe_to_frame
[sfn
] == curframe
);
982 unsigned xcord
= 32768;
986 for(auto i
: ctrlinfo
) {
988 unsigned off
= divcnt
+ 1;
989 bool cselected
= (xcord
>= i
.position_left
+ off
&& xcord
< i
.position_left
+ i
.reserved
+ off
);
991 unsigned polls
= fcontrols
.read_pollcount(pv
, i
.index
);
992 rpast
= ((cffs
+ polls
) > sfn
) ? 1 : 0;
994 uint32_t bgc
= 0xC0C0C0;
1005 fb
.write(i
.title
, 0, divcnt
+ 1 + i
.position_left
, y
, 0x000000, 0xFFFFFF);
1006 } else if(i
.type
== 0) {
1009 bool v
= (fcontrols
.read_index(f
, i
.index
) != 0);
1012 fb
.write(c
, 0, divcnt
+ 1 + i
.position_left
, y
, v
? 0x000000 : 0xC8C8C8, bgc
);
1013 } else if(i
.type
== 1) {
1016 sprintf(c
, "%6d", fcontrols
.read_index(f
, i
.index
));
1017 fb
.write(c
, 0, divcnt
+ 1 + i
.position_left
, y
, 0x000000, bgc
);
1022 void wxeditor_movie::_moviepanel::render(text_framebuffer
& fb
, unsigned long long pos
)
1025 controller_frame_vector
& fv
= *movb
.get_mfile().input
;
1026 controller_frame cf
= fv
.blank_frame(false);
1027 int _width
= width(cf
);
1028 fb
.set_size(_width
, lines_to_display
+ 3);
1029 size_t fbstride
= fb
.get_stride();
1030 auto fbsize
= fb
.get_characters();
1031 text_framebuffer::element
* _fb
= fb
.get_buffer();
1032 fb
.write((stringfmt() << "Current frame: " << movb
.get_movie().get_current_frame() << " of "
1033 << movb
.get_movie().get_frame_count()).str(), _width
, 0, 0,
1034 0x000000, 0xFFFFFF);
1035 fb
.write(render_line1(cf
), _width
, 0, 1, 0x000000, 0xFFFFFF);
1036 fb
.write(render_line2(cf
), _width
, 0, 2, 0x000000, 0xFFFFFF);
1037 unsigned long long lines
= fv
.size();
1038 unsigned long long i
;
1040 for(i
= pos
, j
= 3; i
< pos
+ lines_to_display
; i
++, j
++) {
1041 text_framebuffer::element e
;
1047 for(unsigned k
= 0; k
< fbsize
.first
; k
++)
1048 _fb
[j
* fbstride
+ k
] = e
;
1050 controller_frame frame
= fv
[i
];
1051 render_linen(fb
, frame
, i
, j
);
1056 void wxeditor_movie::_moviepanel::do_toggle_buttons(unsigned idx
, uint64_t row1
, uint64_t row2
)
1058 frame_controls
* _fcontrols
= &fcontrols
;
1059 uint64_t _press_line
= row1
;
1060 uint64_t line
= row2
;
1061 if(_press_line
> line
)
1062 std::swap(_press_line
, line
);
1064 runemufn([idx
, _press_line
, line
, _fcontrols
]() {
1066 if(!movb
.get_movie().readonly_mode())
1068 uint64_t fedit
= real_first_editable(*_fcontrols
, idx
);
1069 controller_frame_vector
& fv
= *movb
.get_mfile().input
;
1070 controller_frame_vector::notify_freeze
freeze(fv
);
1071 for(uint64_t i
= _press_line
; i
<= line
; i
++) {
1072 if(i
< fedit
|| i
>= fv
.size())
1074 controller_frame cf
= fv
[i
];
1075 bool v
= _fcontrols
->read_index(cf
, idx
);
1076 _fcontrols
->write_index(cf
, idx
, !v
);
1077 adjust
+= (v
? -1 : 1);
1082 max_subframe
= _press_line
; //Reparse.
1086 void wxeditor_movie::_moviepanel::do_alter_axis(unsigned idx
, uint64_t row1
, uint64_t row2
)
1088 frame_controls
* _fcontrols
= &fcontrols
;
1089 uint64_t line
= row1
;
1090 uint64_t line2
= row2
;
1093 runemufn([idx
, line
, &value
, _fcontrols
, &valid
]() {
1094 if(!movb
.get_movie().readonly_mode()) {
1098 uint64_t fedit
= real_first_editable(*_fcontrols
, idx
);
1099 controller_frame_vector
& fv
= *movb
.get_mfile().input
;
1100 if(line
< fedit
|| line
>= fv
.size()) {
1104 controller_frame_vector::notify_freeze
freeze(fv
);
1105 controller_frame cf
= fv
[line
];
1106 value
= _fcontrols
->read_index(cf
, idx
);
1111 std::string text
= pick_text(m
, "Set value", "Enter new value:", (stringfmt() << value
).str());
1112 value
= parse_value
<short>(text
);
1113 } catch(canceled_exception
& e
) {
1115 } catch(std::exception
& e
) {
1116 wxMessageBox(wxT("Invalid value"), _T("Error"), wxICON_EXCLAMATION
| wxOK
, m
);
1120 std::swap(line
, line2
);
1121 runemufn([idx
, line
, line2
, value
, _fcontrols
]() {
1122 uint64_t fedit
= real_first_editable(*_fcontrols
, idx
);
1123 controller_frame_vector
& fv
= *movb
.get_mfile().input
;
1124 controller_frame_vector::notify_freeze
freeze(fv
);
1125 for(uint64_t i
= line
; i
<= line2
; i
++) {
1126 if(i
< fedit
|| i
>= fv
.size())
1128 controller_frame cf
= fv
[i
];
1129 _fcontrols
->write_index(cf
, idx
, value
);
1135 void wxeditor_movie::_moviepanel::do_sweep_axis(unsigned idx
, uint64_t row1
, uint64_t row2
)
1137 frame_controls
* _fcontrols
= &fcontrols
;
1138 uint64_t line
= row1
;
1139 uint64_t line2
= row2
;
1144 std::swap(line
, line2
);
1145 runemufn([idx
, line
, line2
, &value
, &value2
, _fcontrols
, &valid
]() {
1146 if(!movb
.get_movie().readonly_mode()) {
1150 uint64_t fedit
= real_first_editable(*_fcontrols
, idx
);
1151 controller_frame_vector
& fv
= *movb
.get_mfile().input
;
1152 if(line2
< fedit
|| line2
>= fv
.size()) {
1156 controller_frame cf
= fv
[line
];
1157 value
= _fcontrols
->read_index(cf
, idx
);
1158 controller_frame cf2
= fv
[line2
];
1159 value2
= _fcontrols
->read_index(cf2
, idx
);
1163 runemufn([idx
, line
, line2
, value
, value2
, _fcontrols
]() {
1164 uint64_t fedit
= real_first_editable(*_fcontrols
, idx
);
1165 controller_frame_vector
& fv
= *movb
.get_mfile().input
;
1166 controller_frame_vector::notify_freeze
freeze(fv
);
1167 for(uint64_t i
= line
+ 1; i
<= line2
- 1; i
++) {
1168 if(i
< fedit
|| i
>= fv
.size())
1170 controller_frame cf
= fv
[i
];
1171 auto tmp2
= static_cast<int64_t>(i
- line
) * (value2
- value
) /
1172 static_cast<int64_t>(line2
- line
);
1173 short tmp
= value
+ tmp2
;
1174 _fcontrols
->write_index(cf
, idx
, tmp
);
1180 void wxeditor_movie::_moviepanel::do_append_frames(uint64_t count
)
1183 uint64_t _count
= count
;
1184 runemufn([_count
]() {
1185 if(!movb
.get_movie().readonly_mode())
1187 controller_frame_vector
& fv
= *movb
.get_mfile().input
;
1188 controller_frame_vector::notify_freeze
freeze(fv
);
1189 for(uint64_t i
= 0; i
< _count
; i
++)
1190 fv
.append(fv
.blank_frame(true));
1196 void wxeditor_movie::_moviepanel::do_append_frames()
1200 std::string text
= pick_text(m
, "Append frames", "Enter number of frames to append:", "");
1201 value
= parse_value
<uint64_t>(text
);
1202 } catch(canceled_exception
& e
) {
1204 } catch(std::exception
& e
) {
1205 wxMessageBox(wxT("Invalid value"), _T("Error"), wxICON_EXCLAMATION
| wxOK
, m
);
1208 do_append_frames(value
);
1212 void wxeditor_movie::_moviepanel::do_insert_frame_after(uint64_t row
)
1215 frame_controls
* _fcontrols
= &fcontrols
;
1216 uint64_t _row
= row
;
1217 runemufn([_row
, _fcontrols
]() {
1218 if(!movb
.get_movie().readonly_mode())
1220 controller_frame_vector
& fv
= *movb
.get_mfile().input
;
1221 uint64_t fedit
= real_first_editable(*_fcontrols
, 0);
1222 //Find the start of the next frame.
1223 uint64_t nframe
= _row
+ 1;
1224 uint64_t vsize
= fv
.size();
1225 while(nframe
< vsize
&& !fv
[nframe
].sync())
1229 controller_frame_vector::notify_freeze
freeze(fv
);
1230 fv
.append(fv
.blank_frame(true));
1231 if(nframe
< vsize
) {
1232 //Okay, gotta copy all data after this point. nframe has to be at least 1.
1233 for(uint64_t i
= vsize
- 1; i
>= nframe
; i
--)
1235 fv
[nframe
] = fv
.blank_frame(true);
1243 void wxeditor_movie::_moviepanel::do_delete_frame(uint64_t row1
, uint64_t row2
, bool wholeframe
)
1246 uint64_t _row1
= row1
;
1247 uint64_t _row2
= row2
;
1248 bool _wholeframe
= wholeframe
;
1249 frame_controls
* _fcontrols
= &fcontrols
;
1250 if(_row1
> _row2
) std::swap(_row1
, _row2
);
1251 runemufn([_row1
, _row2
, _wholeframe
, _fcontrols
]() {
1252 controller_frame_vector
& fv
= *movb
.get_mfile().input
;
1253 uint64_t vsize
= fv
.size();
1255 return; //Nothing to do.
1256 controller_frame_vector::notify_freeze
freeze(fv
);
1257 uint64_t row2
= min(_row2
, vsize
- 1);
1258 uint64_t row1
= min(_row1
, vsize
- 1);
1259 row1
= max(row1
, real_first_editable(*_fcontrols
, 0));
1261 if(_row2
< real_first_nextframe(*_fcontrols
))
1262 return; //Nothing to do.
1263 //Scan backwards for the first subframe of this frame and forwards for the last.
1264 uint64_t fsf
= row1
;
1265 uint64_t lsf
= row2
;
1266 if(fv
[_row2
].sync())
1267 lsf
++; //Bump by one so it finds the end.
1268 while(fsf
< vsize
&& !fv
[fsf
].sync())
1270 while(lsf
< vsize
&& !fv
[lsf
].sync())
1272 fsf
= max(fsf
, real_first_editable(*_fcontrols
, 0));
1273 uint64_t tonuke
= lsf
- fsf
;
1274 int64_t frames_tonuke
= 0;
1275 //Count frames nuked.
1276 for(uint64_t i
= fsf
; i
< lsf
; i
++)
1279 //Nuke from fsf to lsf.
1280 for(uint64_t i
= fsf
; i
< vsize
- tonuke
; i
++)
1281 fv
[i
] = fv
[i
+ tonuke
];
1282 fv
.resize(vsize
- tonuke
);
1284 if(row2
< real_first_editable(*_fcontrols
, 0))
1285 return; //Nothing to do.
1286 //The sync flag needs to be inherited if:
1287 //1) Some deleted subframe has sync flag AND
1288 //2) The subframe immediately after deleted region doesn't.
1289 bool inherit_sync
= false;
1290 for(uint64_t i
= row1
; i
<= row2
; i
++)
1291 inherit_sync
= inherit_sync
|| fv
[i
].sync();
1292 inherit_sync
= inherit_sync
&& (row2
+ 1 < vsize
&& !fv
[_row2
+ 1].sync());
1293 int64_t frames_tonuke
= 0;
1294 //Count frames nuked.
1295 for(uint64_t i
= row1
; i
<= row2
; i
++)
1298 //If sync is inherited, one less frame is nuked.
1299 if(inherit_sync
) frames_tonuke
--;
1300 //Nuke the subframes.
1301 uint64_t tonuke
= row2
- row1
+ 1;
1302 for(uint64_t i
= row1
; i
< vsize
- tonuke
; i
++)
1303 fv
[i
] = fv
[i
+ tonuke
];
1304 fv
.resize(vsize
- tonuke
);
1305 //Next subframe inherits the sync flag.
1307 fv
[row1
].sync(true);
1310 max_subframe
= _row1
;
1315 void wxeditor_movie::_moviepanel::do_truncate(uint64_t row
)
1318 uint64_t _row
= row
;
1319 frame_controls
* _fcontrols
= &fcontrols
;
1320 runemufn([_row
, _fcontrols
]() {
1321 controller_frame_vector
& fv
= *movb
.get_mfile().input
;
1322 uint64_t vsize
= fv
.size();
1325 if(_row
< real_first_editable(*_fcontrols
, 0))
1327 int64_t delete_count
= 0;
1328 for(uint64_t i
= _row
; i
< vsize
; i
++)
1338 void wxeditor_movie::_moviepanel::do_set_stop_at_frame()
1342 runemufn([&curframe
]() {
1343 curframe
= movb
.get_movie().get_current_frame();
1346 std::string text
= pick_text(m
, "Frame", (stringfmt() << "Enter frame to stop at (currently at "
1347 << curframe
<< "):").str(), "");
1348 frame
= parse_value
<uint64_t>(text
);
1349 } catch(canceled_exception
& e
) {
1351 } catch(std::exception
& e
) {
1352 wxMessageBox(wxT("Invalid value"), _T("Error"), wxICON_EXCLAMATION
| wxOK
, m
);
1355 if(frame
< curframe
) {
1356 wxMessageBox(wxT("The movie is already past that point"), _T("Error"), wxICON_EXCLAMATION
| wxOK
, m
);
1359 runemufn([frame
]() {
1360 set_stop_at_frame(frame
);
1364 void wxeditor_movie::_moviepanel::on_mouse0(unsigned x
, unsigned y
, bool polarity
, bool shift
, unsigned X
, unsigned Y
)
1370 press_line
= spos
+ y
- 3;
1375 uint64_t line
= spos
+ y
- 3;
1376 if(press_x
< divcnt
&& x
< divcnt
) {
1377 //Press on frame count.
1378 uint64_t row1
= press_line
;
1379 uint64_t row2
= line
;
1381 std::swap(row1
, row2
);
1382 do_append_frames(row2
- row1
+ 1);
1384 for(auto i
: fcontrols
.get_controlinfo()) {
1385 unsigned off
= divcnt
+ 1;
1386 unsigned idx
= i
.index
;
1387 if((press_x
>= i
.position_left
+ off
&& press_x
< i
.position_left
+ i
.reserved
+ off
) &&
1388 (x
>= i
.position_left
+ off
&& x
< i
.position_left
+ i
.reserved
+ off
)) {
1390 do_toggle_buttons(idx
, press_line
, line
);
1391 else if(i
.type
== 1) {
1393 if(press_line
== line
&& (i
.port
|| i
.controller
))
1395 wxPoint spos
= GetScreenPosition();
1396 popup_axis_panel(line
, i
, spos
.x
+ X
, spos
.y
+ Y
);
1397 } catch(canceled_exception
& e
) {
1400 do_alter_axis(idx
, press_line
, line
);
1406 void wxeditor_movie::_moviepanel::popup_axis_panel(uint64_t row
, control_info ci
, unsigned screenX
, unsigned screenY
)
1410 control_info ci2
= find_paired(ci
, fcontrols
.get_controlinfo());
1411 if(ci
.index
== ci2
.index
) {
1413 } else if(ci2
.index
< ci
.index
) {
1420 frame_controls
* _fcontrols
= &fcontrols
;
1421 if(ciX
.index
== ciY
.index
) {
1422 auto c
= prompt_coodinates_window(m
, NULL
, 256, 0, ciX
, ciX
, screenX
, screenY
);
1423 runemufn([ciX
, row
, c
, _fcontrols
]() {
1424 uint64_t fedit
= real_first_editable(*_fcontrols
, ciX
.index
);
1425 if(row
< fedit
) return;
1426 controller_frame_vector
& fv
= *movb
.get_mfile().input
;
1427 controller_frame cf
= fv
[row
];
1428 _fcontrols
->write_index(cf
, ciX
.index
, c
.first
);
1431 } else if(ci
.axistype
== port_controller_button::TYPE_LIGHTGUN
) {
1432 framebuffer::raw
& _fb
= render_get_latest_screen();
1433 framebuffer::fb
<false> fb
;
1434 auto osize
= std::make_pair(_fb
.get_width(), _fb
.get_height());
1435 auto size
= our_rom
.rtype
->lightgun_scale();
1436 fb
.reallocate(osize
.first
, osize
.second
, false);
1437 fb
.copy_from(_fb
, 1, 1);
1438 render_get_latest_screen_end();
1439 std::vector
<uint8_t> buf
;
1440 buf
.resize(3 * (ciX
.rmax
- ciX
.rmin
+ 1) * (ciY
.rmax
- ciY
.rmin
+ 1));
1441 unsigned offX
= -ciX
.rmin
;
1442 unsigned offY
= -ciY
.rmin
;
1443 struct SwsContext
* ctx
= sws_getContext(osize
.first
, osize
.second
, PIX_FMT_RGBA
,
1444 size
.first
, size
.second
, PIX_FMT_BGR24
, SWS_POINT
, NULL
, NULL
, NULL
);
1449 srcs
[0] = 4 * (fb
.rowptr(1) - fb
.rowptr(0));
1450 dsts
[0] = 3 * (ciX
.rmax
- ciX
.rmin
+ 1);
1451 srcp
[0] = reinterpret_cast<unsigned char*>(fb
.rowptr(0));
1452 dstp
[0] = &buf
[3 * (offY
* (ciX
.rmax
- ciX
.rmin
+ 1) + offX
)];
1453 memset(&buf
[0], 0, buf
.size());
1454 sws_scale(ctx
, srcp
, srcs
, 0, size
.second
, dstp
, dsts
);
1455 sws_freeContext(ctx
);
1456 auto c
= prompt_coodinates_window(m
, &buf
[0], (ciX
.rmax
- ciX
.rmin
+ 1), (ciY
.rmax
- ciY
.rmin
+ 1),
1457 ciX
, ciY
, screenX
, screenY
);
1458 runemufn([ciX
, ciY
, row
, c
, _fcontrols
]() {
1459 uint64_t fedit
= real_first_editable(*_fcontrols
, ciX
.index
);
1460 fedit
= max(fedit
, real_first_editable(*_fcontrols
, ciY
.index
));
1461 if(row
< fedit
) return;
1462 controller_frame_vector
& fv
= *movb
.get_mfile().input
;
1463 controller_frame cf
= fv
[row
];
1464 _fcontrols
->write_index(cf
, ciX
.index
, c
.first
);
1465 _fcontrols
->write_index(cf
, ciY
.index
, c
.second
);
1469 auto c
= prompt_coodinates_window(m
, NULL
, 256, 256, ciX
, ciY
, screenX
, screenY
);
1470 runemufn([ciX
, ciY
, row
, c
, _fcontrols
]() {
1471 uint64_t fedit
= real_first_editable(*_fcontrols
, ciX
.index
);
1472 fedit
= max(fedit
, real_first_editable(*_fcontrols
, ciY
.index
));
1473 if(row
< fedit
) return;
1474 controller_frame_vector
& fv
= *movb
.get_mfile().input
;
1475 controller_frame cf
= fv
[row
];
1476 _fcontrols
->write_index(cf
, ciX
.index
, c
.first
);
1477 _fcontrols
->write_index(cf
, ciY
.index
, c
.second
);
1483 void wxeditor_movie::_moviepanel::do_scroll_to_frame()
1487 std::string text
= pick_text(m
, "Frame", (stringfmt() << "Enter frame to scroll to:").str(), "");
1488 frame
= parse_value
<uint64_t>(text
);
1489 } catch(canceled_exception
& e
) {
1491 } catch(std::exception
& e
) {
1492 wxMessageBox(wxT("Invalid value"), _T("Error"), wxICON_EXCLAMATION
| wxOK
, m
);
1495 uint64_t wouldbe
= 0;
1497 uint64_t high
= max_subframe
;
1499 wouldbe
= (low
+ high
) / 2;
1500 if(subframe_to_frame
[wouldbe
] < frame
)
1502 else if(subframe_to_frame
[wouldbe
] > frame
)
1507 while(wouldbe
> 1 && subframe_to_frame
[wouldbe
- 1] == frame
)
1513 void wxeditor_movie::_moviepanel::do_scroll_to_current_frame()
1515 moviepos
= cached_cffs
;
1519 void wxeditor_movie::_moviepanel::on_popup_menu(wxCommandEvent
& e
)
1521 wxMenuItem
* tmpitem
;
1525 unsigned controller
= 0;
1526 for(auto i
: fcontrols
.get_controlinfo())
1527 if(i
.index
== press_index
) {
1529 controller
= i
.controller
;
1534 do_toggle_buttons(press_index
, rpress_line
, press_line
);
1537 do_alter_axis(press_index
, rpress_line
, press_line
);
1540 do_sweep_axis(press_index
, rpress_line
, press_line
);
1542 case wxID_APPEND_FRAME
:
1543 do_append_frames(1);
1545 case wxID_APPEND_FRAMES
:
1548 case wxID_INSERT_AFTER
:
1549 do_insert_frame_after(press_line
);
1551 case wxID_DELETE_FRAME
:
1552 do_delete_frame(press_line
, rpress_line
, true);
1554 case wxID_DELETE_SUBFRAME
:
1555 do_delete_frame(press_line
, rpress_line
, false);
1558 do_truncate(press_line
);
1560 case wxID_RUN_TO_FRAME
:
1561 do_set_stop_at_frame();
1563 case wxID_SCROLL_FRAME
:
1564 do_scroll_to_frame();
1566 case wxID_SCROLL_CURRENT_FRAME
:
1567 do_scroll_to_current_frame();
1569 case wxID_POSITION_LOCK
:
1572 tmpitem
= current_popup
->FindItem(wxID_POSITION_LOCK
);
1573 position_locked
= tmpitem
->IsChecked();
1575 case wxID_CHANGE_LINECOUNT
:
1577 std::string text
= pick_text(m
, "Set number of lines", "Set number of lines visible:",
1578 (stringfmt() << lines_to_display
).str());
1579 unsigned tmp
= parse_value
<unsigned>(text
);
1580 if(tmp
< 1 || tmp
> 255)
1581 throw std::runtime_error("Value out of range");
1582 lines_to_display
= tmp
;
1583 m
->get_scroll()->set_page_size(lines_to_display
);
1584 } catch(canceled_exception
& e
) {
1586 } catch(std::exception
& e
) {
1587 wxMessageBox(wxT("Invalid value"), _T("Error"), wxICON_EXCLAMATION
| wxOK
, m
);
1592 case wxID_COPY_FRAMES
:
1593 if(press_index
== std::numeric_limits
<unsigned>::max())
1594 do_copy(rpress_line
, press_line
);
1596 do_copy(rpress_line
, press_line
, port
, controller
);
1598 case wxID_CUT_FRAMES
:
1599 if(press_index
== std::numeric_limits
<unsigned>::max())
1600 do_cut(rpress_line
, press_line
);
1602 do_cut(rpress_line
, press_line
, port
, controller
);
1604 case wxID_PASTE_FRAMES
:
1605 if(press_index
== std::numeric_limits
<unsigned>::max() || clipboard_get_data_type() == 1)
1606 do_paste(press_line
, false);
1608 do_paste(press_line
, port
, controller
, false);
1610 case wxID_PASTE_APPEND
:
1611 if(press_index
== std::numeric_limits
<unsigned>::max() || clipboard_get_data_type() == 1)
1612 do_paste(press_line
, true);
1614 do_paste(press_line
, port
, controller
, true);
1616 case wxID_INSERT_CONTROLLER_AFTER
:
1617 if(press_index
== std::numeric_limits
<unsigned>::max())
1620 do_insert_controller(press_line
, port
, controller
);
1622 case wxID_DELETE_CONTROLLER_SUBFRAMES
:
1623 if(press_index
== std::numeric_limits
<unsigned>::max())
1626 do_delete_controller(press_line
, rpress_line
, port
, controller
);
1628 case wxID_MBRANCH_NEW
:
1630 std::string newname
;
1631 std::string oldname
;
1632 runemufn([&oldname
]() { oldname
= mbranch_get(); });
1633 newname
= pick_text(this, "Enter new branch name", "Enter name for a new branch (to fork "
1634 "from " + mbranch_name(oldname
) + "):", "", false);
1635 runemufn_async([this, oldname
, newname
] {
1637 mbranch_new(newname
, oldname
);
1638 } catch(std::exception
& e
) {
1639 std::string error
= e
.what();
1640 runuifun([this, error
]() {
1641 show_message_ok(this, "Can't create branch",
1642 "Can't create branch: " + error
, wxICON_EXCLAMATION
);
1646 } catch(canceled_exception
& e
) {
1649 case wxID_MBRANCH_IMPORT
:
1652 std::string filename
;
1654 std::string dbranch
;
1655 auto g
= choose_file_load(this, "Choose file to import", project_moviepath(),
1659 if(mode
== MBRANCH_IMPORT_MOVIE
) {
1660 std::set
<std::string
> brlist
;
1661 bool failed
= false;
1662 runemufn([this, filename
, &brlist
, &failed
]() {
1664 brlist
= mbranch_movie_branches(filename
);
1665 } catch(std::exception
& e
) {
1666 std::string error
= e
.what();
1668 runuifun([this, error
]() {
1669 show_message_ok(this, "Can't get branches in movie",
1670 error
, wxICON_EXCLAMATION
);
1677 if(brlist
.size() == 0) {
1678 show_message_ok(this, "No branches in movie file",
1679 "Can't import movie file as it has no branches", wxICON_EXCLAMATION
);
1681 } else if(brlist
.size() == 1) {
1682 branch
= *brlist
.begin();
1684 std::vector
<std::string
> choices(brlist
.begin(), brlist
.end());
1685 branch
= pick_among(this, "Select branch to import",
1686 "Select branch to import", choices
, 0);
1688 //Import from movie.
1690 dbranch
= pick_text(this, "Enter new branch name", "Enter name for an imported branch:",
1692 runemufn_async([this, filename
, branch
, dbranch
, mode
]() {
1694 mbranch_import(filename
, branch
, dbranch
, mode
);
1695 } catch(std::exception
& e
) {
1696 std::string error
= e
.what();
1697 runuifun([this, error
]() {
1698 show_message_ok(this, "Can't import branch",
1699 error
, wxICON_EXCLAMATION
);
1703 } catch(canceled_exception
& e
) {
1706 case wxID_MBRANCH_EXPORT
:
1710 auto g
= choose_file_save(this, "Choose file to export", project_moviepath(),
1714 runemufn_async([this, file
, mode
]() {
1716 std::string bname
= mbranch_get();
1717 mbranch_export(file
, bname
, mode
== MBRANCH_IMPORT_BINARY
);
1718 } catch(std::exception
& e
) {
1719 std::string error
= e
.what();
1720 runuifun([this, error
]() {
1721 show_message_ok(this, "Can't export branch",
1722 error
, wxICON_EXCLAMATION
);
1726 } catch(canceled_exception
& e
) {
1729 case wxID_MBRANCH_RENAME
:
1731 std::string newname
;
1732 std::string oldname
;
1733 std::set
<std::string
> list
;
1734 runemufn([&list
]() { list
= mbranch_enumerate(); });
1735 std::vector
<std::string
> choices(list
.begin(), list
.end());
1736 oldname
= pick_among(this, "Select branch to rename", "Select branch to rename",
1738 newname
= pick_text(this, "Enter new branch name", "Enter name for a new branch (to rename "
1739 "'" + mbranch_name(oldname
) + "'):", oldname
, false);
1740 runemufn_async([this, oldname
, newname
] {
1742 mbranch_rename(oldname
, newname
);
1743 } catch(std::exception
& e
) {
1744 std::string error
= e
.what();
1745 runuifun([this, error
]() {
1746 show_message_ok(this, "Can't rename branch",
1747 "Can't rename branch: " + error
, wxICON_EXCLAMATION
);
1751 } catch(canceled_exception
& e
) {
1754 case wxID_MBRANCH_DELETE
:
1756 std::string oldname
;
1757 std::set
<std::string
> list
;
1758 runemufn([&list
]() { list
= mbranch_enumerate(); });
1759 std::vector
<std::string
> choices(list
.begin(), list
.end());
1760 oldname
= pick_among(this, "Select branch to delete", "Select branch to delete",
1762 runemufn_async([this, oldname
] {
1764 mbranch_delete(oldname
);
1765 } catch(std::exception
& e
) {
1766 std::string error
= e
.what();
1767 runuifun([this, error
]() {
1768 show_message_ok(this, "Can't delete branch",
1769 "Can't delete branch: " + error
, wxICON_EXCLAMATION
);
1773 } catch(canceled_exception
& e
) {
1777 if(id
>= wxID_MBRANCH_FIRST
&& id
<= wxID_MBRANCH_LAST
) {
1778 if(!branch_names
.count(id
)) return;
1779 std::string name
= branch_names
[id
];
1780 runemufn_async([this, name
]() {
1783 } catch(std::exception
& e
) {
1784 std::string err
= e
.what();
1785 runuifun([this, err
]() {
1786 show_message_ok(this, "Error changing branch",
1787 "Can't change branch: " + err
, wxICON_EXCLAMATION
);
1794 uint64_t wxeditor_movie::_moviepanel::first_editable(unsigned index
)
1796 uint64_t cffs
= cached_cffs
;
1797 if(!subframe_to_frame
.count(cffs
))
1799 uint64_t f
= subframe_to_frame
[cffs
];
1800 pollcounter_vector
& pv
= movb
.get_movie().get_pollcounters();
1801 uint32_t pc
= fcontrols
.read_pollcount(pv
, index
);
1802 for(uint32_t i
= 1; i
< pc
; i
++)
1803 if(!subframe_to_frame
.count(cffs
+ i
) || subframe_to_frame
[cffs
+ i
] > f
)
1808 uint64_t wxeditor_movie::_moviepanel::first_nextframe()
1810 uint64_t base
= first_editable(0);
1811 if(!subframe_to_frame
.count(cached_cffs
))
1813 uint64_t f
= subframe_to_frame
[cached_cffs
];
1814 for(uint32_t i
= 0;; i
++)
1815 if(!subframe_to_frame
.count(base
+ i
) || subframe_to_frame
[base
+ i
] > f
)
1819 void wxeditor_movie::_moviepanel::on_mouse1(unsigned x
, unsigned y
, bool polarity
) {}
1820 void wxeditor_movie::_moviepanel::on_mouse2(unsigned x
, unsigned y
, bool polarity
)
1823 //Pressing mouse, just record line it was pressed on.
1824 rpress_line
= spos
+ y
- 3;
1827 //Releasing mouse, open popup menu.
1828 unsigned off
= divcnt
+ 1;
1832 press_line
= spos
+ y
- 3;
1834 current_popup
= &menu
;
1835 menu
.Connect(wxEVT_COMMAND_MENU_SELECTED
, wxCommandEventHandler(wxeditor_movie::_moviepanel::on_popup_menu
),
1838 //Find what controller is the click on.
1839 bool clicked_button
= false;
1840 control_info clicked
;
1841 std::string controller_name
;
1843 clicked_button
= false;
1844 press_index
= std::numeric_limits
<unsigned>::max();
1846 for(auto i
: fcontrols
.get_controlinfo())
1847 if(press_x
>= i
.position_left
+ off
&& press_x
< i
.position_left
+ i
.reserved
+ off
) {
1848 if(i
.type
== 0 || i
.type
== 1) {
1849 clicked_button
= true;
1851 controller_name
= (stringfmt() << "controller " << i
.port
<< "-"
1852 << (i
.controller
+ 1)).str();
1853 press_index
= i
.index
;
1858 //Find first editable frame, controllerframe and buttonframe.
1859 bool not_editable
= !movb
.get_movie().readonly_mode();
1860 uint64_t eframe_low
= first_editable(0);
1861 uint64_t ebutton_low
= clicked_button
? first_editable(clicked
.index
) : std::numeric_limits
<uint64_t>::max();
1862 uint64_t econtroller_low
= ebutton_low
;
1863 for(auto i
: fcontrols
.get_controlinfo())
1864 if(i
.port
== clicked
.port
&& i
.controller
== clicked
.controller
&& (i
.type
== 0 || i
.type
== 1))
1865 econtroller_low
= max(econtroller_low
, first_editable(i
.index
));
1867 bool click_zero
= (clicked_button
&& !clicked
.port
&& !clicked
.controller
);
1868 bool enable_append_frame
= !not_editable
;
1869 bool enable_toggle_button
= false;
1870 bool enable_change_axis
= false;
1871 bool enable_sweep_axis
= false;
1872 bool enable_insert_frame
= false;
1873 bool enable_insert_controller
= false;
1874 bool enable_delete_frame
= false;
1875 bool enable_delete_subframe
= false;
1876 bool enable_delete_controller_subframe
= false;
1877 bool enable_truncate_movie
= false;
1878 bool enable_cut_frame
= false;
1879 bool enable_copy_frame
= false;
1880 bool enable_paste_frame
= false;
1881 bool enable_paste_append
= false;
1882 std::string copy_title
;
1883 std::string paste_title
;
1885 //Toggle button is enabled if clicked on button and either end is in valid range.
1886 enable_toggle_button
= (!not_editable
&& clicked_button
&& clicked
.type
== 0 && ((press_line
>= ebutton_low
&&
1887 press_line
< linecount
) || (rpress_line
>= ebutton_low
&& rpress_line
< linecount
)));
1888 //Change axis is enabled in similar conditions, except if type is axis.
1889 enable_change_axis
= (!not_editable
&& clicked_button
&& clicked
.type
== 1 && ((press_line
>= ebutton_low
&&
1890 press_line
< linecount
) || (rpress_line
>= ebutton_low
&& rpress_line
< linecount
)));
1891 //Sweep axis is enabled if change axis is enabled and lines don't match.
1892 enable_sweep_axis
= (enable_change_axis
&& press_line
!= rpress_line
);
1893 //Insert frame is enabled if this frame is completely editable and press and release lines match.
1894 enable_insert_frame
= (!not_editable
&& press_line
+ 1 >= eframe_low
&& press_line
< linecount
&&
1895 press_line
== rpress_line
);
1896 //Insert controller frame is enabled if controller is completely editable and lines match.
1897 enable_insert_controller
= (!not_editable
&& clicked_button
&& press_line
>= econtroller_low
&&
1898 press_line
< linecount
&& press_line
== rpress_line
);
1899 enable_insert_controller
= enable_insert_controller
&& (clicked
.port
|| clicked
.controller
);
1900 //Delete frame is enabled if range is completely editable (relative to next-frame).
1901 enable_delete_frame
= (!not_editable
&& press_line
>= first_nextframe() && press_line
< linecount
&&
1902 rpress_line
>= first_nextframe() && rpress_line
< linecount
);
1903 //Delete subframe is enabled if range is completely editable.
1904 enable_delete_subframe
= (!not_editable
&& press_line
>= eframe_low
&& press_line
< linecount
&&
1905 rpress_line
>= eframe_low
&& rpress_line
< linecount
);
1906 //Delete controller subframe is enabled if range is completely controller-editable.
1907 enable_delete_controller_subframe
= (!not_editable
&& clicked_button
&& press_line
>= econtroller_low
&&
1908 press_line
< linecount
&& rpress_line
>= econtroller_low
&& rpress_line
< linecount
);
1909 enable_delete_controller_subframe
= enable_delete_controller_subframe
&& (clicked
.port
|| clicked
.controller
);
1910 //Truncate movie is enabled if lines match and is completely editable.
1911 enable_truncate_movie
= (!not_editable
&& press_line
== rpress_line
&& press_line
>= eframe_low
&&
1912 press_line
< linecount
);
1913 //Cut frames is enabled if range is editable (possibly controller-editable).
1915 enable_cut_frame
= (!not_editable
&& press_line
>= econtroller_low
&& press_line
< linecount
1916 && rpress_line
>= econtroller_low
&& rpress_line
< linecount
&& !click_zero
);
1918 enable_cut_frame
= (!not_editable
&& press_line
>= eframe_low
&& press_line
< linecount
1919 && rpress_line
>= eframe_low
&& rpress_line
< linecount
);
1920 if(clicked_button
&& clipboard_get_data_type() == 0) {
1921 enable_paste_append
= (!not_editable
&& linecount
>= eframe_low
);
1922 enable_paste_frame
= (!not_editable
&& press_line
>= econtroller_low
&& press_line
< linecount
1923 && rpress_line
>= econtroller_low
&& rpress_line
< linecount
&& !click_zero
);
1924 } else if(clipboard_get_data_type() == 1) {
1925 enable_paste_append
= (!not_editable
&& linecount
>= econtroller_low
);
1926 enable_paste_frame
= (!not_editable
&& press_line
>= eframe_low
&& press_line
< linecount
1927 && rpress_line
>= eframe_low
&& rpress_line
< linecount
);
1929 //Copy frames is enabled if range exists.
1930 enable_copy_frame
= (press_line
< linecount
&& rpress_line
< linecount
);
1931 copy_title
= (clicked_button
? controller_name
: "frames");
1932 paste_title
= ((clipboard_get_data_type() == 0) ? copy_title
: "frames");
1934 if(clipboard_get_data_type() == 0 && click_zero
) enable_paste_append
= enable_paste_frame
= false;
1936 if(enable_toggle_button
)
1937 menu
.Append(wxID_TOGGLE
, towxstring(U
"Toggle " + clicked
.title
));
1938 if(enable_change_axis
)
1939 menu
.Append(wxID_CHANGE
, towxstring(U
"Change " + clicked
.title
));
1940 if(enable_sweep_axis
)
1941 menu
.Append(wxID_SWEEP
, towxstring(U
"Sweep " + clicked
.title
));
1942 if(enable_toggle_button
|| enable_change_axis
|| enable_sweep_axis
)
1943 menu
.AppendSeparator();
1944 menu
.Append(wxID_INSERT_AFTER
, wxT("Insert frame after"))->Enable(enable_insert_frame
);
1945 menu
.Append(wxID_INSERT_CONTROLLER_AFTER
, wxT("Insert controller frame"))
1946 ->Enable(enable_insert_controller
);
1947 menu
.Append(wxID_APPEND_FRAME
, wxT("Append frame"))->Enable(enable_append_frame
);
1948 menu
.Append(wxID_APPEND_FRAMES
, wxT("Append frames..."))->Enable(enable_append_frame
);
1949 menu
.AppendSeparator();
1950 menu
.Append(wxID_DELETE_FRAME
, wxT("Delete frame(s)"))->Enable(enable_delete_frame
);
1951 menu
.Append(wxID_DELETE_SUBFRAME
, wxT("Delete subframe(s)"))->Enable(enable_delete_subframe
);
1952 menu
.Append(wxID_DELETE_CONTROLLER_SUBFRAMES
, wxT("Delete controller subframes(s)"))
1953 ->Enable(enable_delete_controller_subframe
);
1954 menu
.AppendSeparator();
1955 menu
.Append(wxID_TRUNCATE
, wxT("Truncate movie"))->Enable(enable_truncate_movie
);
1956 menu
.AppendSeparator();
1957 menu
.Append(wxID_CUT_FRAMES
, towxstring("Cut " + copy_title
))->Enable(enable_cut_frame
);
1958 menu
.Append(wxID_COPY_FRAMES
, towxstring("Copy " + copy_title
))->Enable(enable_copy_frame
);
1959 menu
.Append(wxID_PASTE_FRAMES
, towxstring("Paste " + paste_title
))->Enable(enable_paste_frame
);
1960 menu
.Append(wxID_PASTE_APPEND
, towxstring("Paste append " + paste_title
))->Enable(enable_paste_append
);
1961 menu
.AppendSeparator();
1962 menu
.Append(wxID_SCROLL_FRAME
, wxT("Scroll to frame..."));
1963 menu
.Append(wxID_SCROLL_CURRENT_FRAME
, wxT("Scroll to current frame"));
1964 menu
.Append(wxID_RUN_TO_FRAME
, wxT("Run to frame..."));
1965 menu
.Append(wxID_CHANGE_LINECOUNT
, wxT("Change number of lines visible"));
1966 menu
.AppendCheckItem(wxID_POSITION_LOCK
, wxT("Lock scroll to playback"))->Check(position_locked
);
1967 menu
.AppendSeparator();
1969 wxMenu
* branches_submenu
= new wxMenu();
1970 branches_submenu
->Append(wxID_MBRANCH_NEW
, wxT("New branch..."));
1971 branches_submenu
->Append(wxID_MBRANCH_IMPORT
, wxT("Import branch..."));
1972 branches_submenu
->Append(wxID_MBRANCH_EXPORT
, wxT("Export branch..."));
1973 branches_submenu
->Append(wxID_MBRANCH_RENAME
, wxT("Rename branch..."));
1974 branches_submenu
->Append(wxID_MBRANCH_DELETE
, wxT("Delete branch..."));
1975 branches_submenu
->AppendSeparator();
1976 std::set
<std::string
> list
;
1977 std::string current
;
1979 runemufn([&list
, ¤t
, &ro
]() {
1980 list
= mbranch_enumerate();
1981 current
= mbranch_get();
1982 ro
= movb
.get_movie().readonly_mode();
1984 int ass_id
= wxID_MBRANCH_FIRST
;
1985 for(auto i
: list
) {
1986 bool selected
= (i
== current
);
1988 it
= branches_submenu
->AppendCheckItem(ass_id
, towxstring(mbranch_name(i
)));
1989 branch_names
[ass_id
++] = i
;
1990 if(selected
) it
->Check(selected
);
1993 menu
.AppendSubMenu(branches_submenu
, wxT("Branches"));
1994 menu
.Connect(wxEVT_COMMAND_MENU_SELECTED
, wxCommandEventHandler(wxeditor_movie::_moviepanel::on_popup_menu
),
1996 branches_submenu
->Connect(wxEVT_COMMAND_MENU_SELECTED
,
1997 wxCommandEventHandler(wxeditor_movie::_moviepanel::on_popup_menu
), NULL
, this);
1999 //delete branches_submenu;
2002 int wxeditor_movie::_moviepanel::get_lines()
2004 controller_frame_vector
& fv
= *movb
.get_mfile().input
;
2008 void wxeditor_movie::_moviepanel::signal_repaint()
2010 if(requested
|| recursing
)
2012 auto s
= m
->get_scroll();
2014 uint32_t width
, height
;
2016 wxeditor_movie
* m2
= m
;
2017 uint64_t old_cached_cffs
= cached_cffs
;
2018 uint32_t prev_width
, prev_height
;
2019 bool done_again
= false;
2021 runemufn([&lines
, &width
, &height
, m2
, this]() {
2022 lines
= this->get_lines();
2023 if(lines
< lines_to_display
)
2025 else if(this->moviepos
> lines
- lines_to_display
)
2026 this->moviepos
= lines
- lines_to_display
;
2027 this->render(fb
, moviepos
);
2028 auto x
= fb
.get_characters();
2032 if(old_cached_cffs
!= cached_cffs
&& position_locked
&& !done_again
) {
2033 moviepos
= cached_cffs
;
2037 prev_width
= new_width
;
2038 prev_height
= new_height
;
2040 new_height
= height
;
2043 s
->set_range(lines
);
2044 s
->set_position(moviepos
);
2046 auto size
= fb
.get_pixels();
2047 pixels
.resize(size
.first
* size
.second
* 3);
2048 fb
.render((char*)&pixels
[0]);
2049 if(prev_width
!= new_width
|| prev_height
!= new_height
) {
2050 auto cell
= fb
.get_cell();
2051 SetMinSize(wxSize(new_width
* cell
.first
, (lines_to_display
+ 3) * cell
.second
));
2052 if(new_width
> 0 && s
)
2059 void wxeditor_movie::_moviepanel::on_mouse(wxMouseEvent
& e
)
2061 auto cell
= fb
.get_cell();
2062 if(e
.LeftDown() && !e
.ControlDown())
2063 on_mouse0(e
.GetX() / cell
.first
, e
.GetY() / cell
.second
, true, e
.ShiftDown(), e
.GetX(), e
.GetY());
2064 if(e
.LeftUp() && !e
.ControlDown())
2065 on_mouse0(e
.GetX() / cell
.first
, e
.GetY() / cell
.second
, false, e
.ShiftDown(), e
.GetX(), e
.GetY());
2067 on_mouse1(e
.GetX() / cell
.first
, e
.GetY() / cell
.second
, true);
2069 on_mouse1(e
.GetX() / cell
.first
, e
.GetY() / cell
.second
, false);
2070 if(e
.RightDown() || (e
.LeftDown() && e
.ControlDown()))
2071 on_mouse2(e
.GetX() / cell
.first
, e
.GetY() / cell
.second
, true);
2072 if(e
.RightUp() || (e
.LeftUp() && e
.ControlDown()))
2073 on_mouse2(e
.GetX() / cell
.first
, e
.GetY() / cell
.second
, false);
2074 auto s
= m
->get_scroll();
2078 if(e
.ShiftDown() && e
.ControlDown())
2080 s
->apply_wheel(e
.GetWheelRotation(), e
.GetWheelDelta(), speed
);
2083 void wxeditor_movie::_moviepanel::on_erase(wxEraseEvent
& e
)
2088 void wxeditor_movie::_moviepanel::on_paint(wxPaintEvent
& e
)
2090 auto size
= fb
.get_pixels();
2091 if(!size
.first
|| !size
.second
) {
2098 wxBitmap
bmp(wxImage(size
.first
, size
.second
, &pixels
[0], true));
2099 dc
.DrawBitmap(bmp
, 0, 0, false);
2103 void wxeditor_movie::_moviepanel::do_copy(uint64_t row1
, uint64_t row2
, unsigned port
, unsigned controller
)
2105 frame_controls
* _fcontrols
= &fcontrols
;
2106 uint64_t line
= row1
;
2107 uint64_t line2
= row2
;
2109 std::swap(line
, line2
);
2111 runemufn([port
, controller
, line
, line2
, _fcontrols
, &copied
]() {
2112 controller_frame_vector
& fv
= *movb
.get_mfile().input
;
2113 uint64_t vsize
= fv
.size();
2116 uint64_t _line
= min(line
, vsize
- 1);
2117 uint64_t _line2
= min(line2
, vsize
- 1);
2118 copied
= encode_lines(*_fcontrols
, fv
, _line
, _line2
+ 1, port
, controller
);
2120 copy_to_clipboard(copied
);
2123 void wxeditor_movie::_moviepanel::do_copy(uint64_t row1
, uint64_t row2
)
2125 uint64_t line
= row1
;
2126 uint64_t line2
= row2
;
2128 std::swap(line
, line2
);
2130 runemufn([line
, line2
, &copied
]() {
2131 controller_frame_vector
& fv
= *movb
.get_mfile().input
;
2132 uint64_t vsize
= fv
.size();
2135 uint64_t _line
= min(line
, vsize
- 1);
2136 uint64_t _line2
= min(line2
, vsize
- 1);
2137 copied
= encode_lines(fv
, _line
, _line2
+ 1);
2139 copy_to_clipboard(copied
);
2142 void wxeditor_movie::_moviepanel::do_cut(uint64_t row1
, uint64_t row2
, unsigned port
, unsigned controller
)
2144 do_copy(row1
, row2
, port
, controller
);
2145 do_delete_controller(row1
, row2
, port
, controller
);
2148 void wxeditor_movie::_moviepanel::do_cut(uint64_t row1
, uint64_t row2
)
2150 do_copy(row1
, row2
);
2151 do_delete_frame(row1
, row2
, false);
2154 void wxeditor_movie::_moviepanel::do_paste(uint64_t row
, bool append
)
2156 frame_controls
* _fcontrols
= &fcontrols
;
2158 uint64_t _gapstart
= row
;
2159 std::string cliptext
= copy_from_clipboard();
2160 runemufn([_fcontrols
, &cliptext
, _gapstart
, append
]() {
2161 //Insert enough lines for the pasted content.
2162 uint64_t gapstart
= _gapstart
;
2163 if(!movb
.get_movie().readonly_mode())
2165 uint64_t gaplen
= 0;
2166 int64_t newframes
= 0;
2168 std::istringstream
y(cliptext
);
2170 if(!std::getline(y
, z
))
2173 if(z
!= "lsnes-moviedata-whole")
2175 while(std::getline(y
, z
))
2178 controller_frame_vector
& fv
= *movb
.get_mfile().input
;
2179 uint64_t vsize
= fv
.size();
2180 if(gapstart
< real_first_editable(*_fcontrols
, 0))
2182 if(gapstart
> vsize
)
2184 controller_frame_vector::notify_freeze
freeze(fv
);
2185 if(append
) gapstart
= vsize
;
2186 for(uint64_t i
= 0; i
< gaplen
; i
++)
2187 fv
.append(fv
.blank_frame(false));
2188 for(uint64_t i
= vsize
- 1; i
>= gapstart
&& i
<= vsize
; i
--)
2189 fv
[i
+ gaplen
] = fv
[i
];
2190 //Write the pasted frames.
2192 std::istringstream
y(cliptext
);
2195 uint64_t idx
= gapstart
;
2196 while(std::getline(y
, z
)) {
2197 fv
[idx
++].deserialize(z
.c_str());
2198 if(fv
[idx
- 1].sync())
2207 void wxeditor_movie::_moviepanel::do_paste(uint64_t row
, unsigned port
, unsigned controller
, bool append
)
2209 if(!port
&& !controller
)
2211 frame_controls
* _fcontrols
= &fcontrols
;
2212 auto iset
= controller_index_set(fcontrols
, port
, controller
);
2214 uint64_t _gapstart
= row
;
2215 std::string cliptext
= copy_from_clipboard();
2216 runemufn([_fcontrols
, iset
, &cliptext
, _gapstart
, port
, controller
, append
]() {
2217 //Insert enough lines for the pasted content.
2218 uint64_t gapstart
= _gapstart
;
2219 if(!movb
.get_movie().readonly_mode())
2221 uint64_t gaplen
= 0;
2222 int64_t newframes
= 0;
2224 std::istringstream
y(cliptext
);
2226 if(!std::getline(y
, z
))
2229 if(z
!= "lsnes-moviedata-controller")
2231 while(std::getline(y
, z
)) {
2236 controller_frame_vector
& fv
= *movb
.get_mfile().input
;
2237 uint64_t vsize
= fv
.size();
2238 if(gapstart
< real_first_editable(*_fcontrols
, iset
))
2240 if(gapstart
> vsize
)
2242 controller_frame_vector::notify_freeze
freeze(fv
);
2243 if(append
) gapstart
= vsize
;
2244 for(uint64_t i
= 0; i
< gaplen
; i
++)
2245 fv
.append(fv
.blank_frame(true));
2246 move_index_set(*_fcontrols
, fv
, gapstart
, gapstart
+ gaplen
, vsize
- gapstart
, iset
);
2247 //Write the pasted frames.
2249 std::istringstream
y(cliptext
);
2252 uint64_t idx
= gapstart
;
2253 while(std::getline(y
, z
)) {
2254 controller_frame f
= fv
[idx
++];
2255 decode_line(*_fcontrols
, f
, z
, port
, controller
);
2263 void wxeditor_movie::_moviepanel::do_insert_controller(uint64_t row
, unsigned port
, unsigned controller
)
2265 if(!port
&& !controller
)
2267 frame_controls
* _fcontrols
= &fcontrols
;
2268 auto iset
= controller_index_set(fcontrols
, port
, controller
);
2270 uint64_t gapstart
= row
;
2271 runemufn([_fcontrols
, iset
, gapstart
, port
, controller
]() {
2272 //Insert enough lines for the pasted content.
2273 if(!movb
.get_movie().readonly_mode())
2275 controller_frame_vector
& fv
= *movb
.get_mfile().input
;
2276 uint64_t vsize
= fv
.size();
2277 if(gapstart
< real_first_editable(*_fcontrols
, iset
))
2279 if(gapstart
> vsize
)
2281 fv
.append(fv
.blank_frame(true));
2282 move_index_set(*_fcontrols
, fv
, gapstart
, gapstart
+ 1, vsize
- gapstart
, iset
);
2283 zero_index_set(*_fcontrols
, fv
, gapstart
, 1, iset
);
2289 void wxeditor_movie::_moviepanel::do_delete_controller(uint64_t row1
, uint64_t row2
, unsigned port
,
2290 unsigned controller
)
2292 if(!port
&& !controller
)
2294 frame_controls
* _fcontrols
= &fcontrols
;
2295 auto iset
= controller_index_set(fcontrols
, port
, controller
);
2297 if(row1
> row2
) std::swap(row1
, row2
);
2298 uint64_t gapstart
= row1
;
2299 uint64_t gaplen
= row2
- row1
+ 1;
2300 runemufn([_fcontrols
, iset
, gapstart
, gaplen
, port
, controller
]() {
2301 //Insert enough lines for the pasted content.
2302 if(!movb
.get_movie().readonly_mode())
2304 controller_frame_vector
& fv
= *movb
.get_mfile().input
;
2305 uint64_t vsize
= fv
.size();
2306 if(gapstart
< real_first_editable(*_fcontrols
, iset
))
2308 if(gapstart
> vsize
)
2310 move_index_set(*_fcontrols
, fv
, gapstart
+ gaplen
, gapstart
, vsize
- gapstart
- gaplen
, iset
);
2311 zero_index_set(*_fcontrols
, fv
, vsize
- gaplen
, gaplen
, iset
);
2318 wxeditor_movie::wxeditor_movie(wxWindow
* parent
)
2319 : wxDialog(parent
, wxID_ANY
, wxT("lsnes: Edit movie"), wxDefaultPosition
, wxSize(-1, -1))
2323 wxFlexGridSizer
* top_s
= new wxFlexGridSizer(2, 1, 0, 0);
2326 wxBoxSizer
* panel_s
= new wxBoxSizer(wxHORIZONTAL
);
2328 panel_s
->Add(moviepanel
= new _moviepanel(this), 1, wxGROW
);
2329 panel_s
->Add(moviescroll
= new scroll_bar(this, wxID_ANY
, true), 0, wxGROW
);
2330 top_s
->Add(panel_s
, 1, wxGROW
);
2332 moviescroll
->set_page_size(lines_to_display
);
2333 moviescroll
->set_handler([this](scroll_bar
& s
) {
2334 this->moviepanel
->moviepos
= s
.get_position();
2335 this->moviepanel
->signal_repaint();
2337 moviepanel
->Connect(wxEVT_KEY_DOWN
, wxKeyEventHandler(wxeditor_movie::on_keyboard_down
), NULL
, this);
2338 moviepanel
->Connect(wxEVT_KEY_UP
, wxKeyEventHandler(wxeditor_movie::on_keyboard_up
), NULL
, this);
2340 wxBoxSizer
* pbutton_s
= new wxBoxSizer(wxHORIZONTAL
);
2341 pbutton_s
->AddStretchSpacer();
2342 pbutton_s
->Add(closebutton
= new wxButton(this, wxID_OK
, wxT("Close")), 0, wxGROW
);
2343 closebutton
->Connect(wxEVT_COMMAND_BUTTON_CLICKED
,
2344 wxCommandEventHandler(wxeditor_movie::on_close
), NULL
, this);
2345 top_s
->Add(pbutton_s
, 0, wxGROW
);
2347 moviepanel
->SetFocus();
2348 moviescroll
->Connect(wxEVT_SET_FOCUS
, wxFocusEventHandler(wxeditor_movie::on_focus_wrong
), NULL
, this);
2349 closebutton
->Connect(wxEVT_SET_FOCUS
, wxFocusEventHandler(wxeditor_movie::on_focus_wrong
), NULL
, this);
2350 Connect(wxEVT_SET_FOCUS
, wxFocusEventHandler(wxeditor_movie::on_focus_wrong
), NULL
, this);
2352 panel_s
->SetSizeHints(this);
2353 pbutton_s
->SetSizeHints(this);
2354 top_s
->SetSizeHints(this);
2355 Connect(wxEVT_CLOSE_WINDOW
, wxCloseEventHandler(wxeditor_movie::on_wclose
));
2358 moviepanel
->signal_repaint();
2361 bool wxeditor_movie::ShouldPreventAppExit() const { return false; }
2363 void wxeditor_movie::on_close(wxCommandEvent
& e
)
2365 movieeditor_open
= NULL
;
2370 void wxeditor_movie::on_wclose(wxCloseEvent
& e
)
2372 bool wasc
= closing
;
2374 movieeditor_open
= NULL
;
2379 void wxeditor_movie::update()
2381 moviepanel
->signal_repaint();
2384 scroll_bar
* wxeditor_movie::get_scroll()
2389 void wxeditor_movie::on_focus_wrong(wxFocusEvent
& e
)
2391 moviepanel
->SetFocus();
2394 void wxeditor_movie_display(wxWindow
* parent
)
2396 if(movieeditor_open
)
2398 wxeditor_movie
* v
= new wxeditor_movie(parent
);
2400 movieeditor_open
= v
;
2403 void wxeditor_movie::on_keyboard_down(wxKeyEvent
& e
)
2405 handle_wx_keyboard(e
, true);
2408 void wxeditor_movie::on_keyboard_up(wxKeyEvent
& e
)
2410 handle_wx_keyboard(e
, false);
2413 void wxeditor_movie_update()
2415 if(movieeditor_open
)
2416 movieeditor_open
->update();