1 #include "core/movie.hpp"
2 #include "core/moviedata.hpp"
3 #include "core/dispatch.hpp"
4 #include "core/window.hpp"
6 #include "interface/controller.hpp"
7 #include "core/mainloop.hpp"
8 #include "platform/wxwidgets/platform.hpp"
9 #include "platform/wxwidgets/textrender.hpp"
10 #include "library/minmax.hpp"
11 #include "library/string.hpp"
12 #include "library/utf8.hpp"
18 #include <wx/control.h>
19 #include <wx/combobox.h>
23 wxID_TOGGLE
= wxID_HIGHEST
+ 1,
27 wxID_CHANGE_LINECOUNT
,
36 wxID_SCROLL_CURRENT_FRAME
39 void update_movie_state();
43 unsigned lines_to_display
= 28;
44 uint64_t divs
[] = {1000000, 100000, 10000, 1000, 100, 10, 1};
45 uint64_t divsl
[] = {1000000, 100000, 10000, 1000, 100, 10, 0};
46 const unsigned divcnt
= sizeof(divs
)/sizeof(divs
[0]);
48 void connect_events(wxScrollBar
* s
, wxObjectEventFunction fun
, wxEvtHandler
* obj
)
50 s
->Connect(wxEVT_SCROLL_THUMBTRACK
, fun
, NULL
, obj
);
51 s
->Connect(wxEVT_SCROLL_PAGEDOWN
, fun
, NULL
, obj
);
52 s
->Connect(wxEVT_SCROLL_PAGEUP
, fun
, NULL
, obj
);
53 s
->Connect(wxEVT_SCROLL_LINEDOWN
, fun
, NULL
, obj
);
54 s
->Connect(wxEVT_SCROLL_LINEUP
, fun
, NULL
, obj
);
55 s
->Connect(wxEVT_SCROLL_TOP
, fun
, NULL
, obj
);
56 s
->Connect(wxEVT_SCROLL_BOTTOM
, fun
, NULL
, obj
);
62 unsigned position_left
;
63 unsigned reserved
; //Must be at least 6 for axes.
64 unsigned index
; //Index in poll vector.
65 int type
; //-2 => Port, -1 => Fixed, 0 => Button, 1 => axis.
70 static control_info
portinfo(unsigned& p
, unsigned port
, unsigned controller
);
71 static control_info
fixedinfo(unsigned& p
, const std::u32string
& str
);
72 static control_info
buttoninfo(unsigned& p
, char32_t character
, const std::u32string
& title
, unsigned idx
);
73 static control_info
axisinfo(unsigned& p
, const std::u32string
& title
, unsigned idx
);
76 control_info
control_info::portinfo(unsigned& p
, unsigned port
, unsigned controller
)
80 i
.reserved
= (stringfmt() << port
<< "-" << controller
).str32().length();
87 i
.controller
= controller
;
91 control_info
control_info::fixedinfo(unsigned& p
, const std::u32string
& str
)
95 i
.reserved
= str
.length();
106 control_info
control_info::buttoninfo(unsigned& p
, char32_t character
, const std::u32string
& title
, unsigned idx
)
121 control_info
control_info::axisinfo(unsigned& p
, const std::u32string
& title
, unsigned idx
)
125 i
.reserved
= title
.length();
142 void set_types(controller_frame
& f
);
143 short read_index(controller_frame
& f
, unsigned idx
);
144 void write_index(controller_frame
& f
, unsigned idx
, short value
);
145 uint32_t read_pollcount(pollcounter_vector
& v
, unsigned idx
);
146 const std::list
<control_info
>& get_controlinfo() { return controlinfo
; }
147 std::u32string
line1() { return _line1
; }
148 std::u32string
line2() { return _line2
; }
149 size_t width() { return _width
; }
152 std::u32string _line1
;
153 std::u32string _line2
;
155 void add_port(unsigned& c
, unsigned pid
, const port_type
& p
, const port_type_set
& pts
);
156 std::list
<control_info
> controlinfo
;
160 frame_controls::frame_controls()
165 void frame_controls::set_types(controller_frame
& f
)
169 const port_type_set
& pts
= f
.porttypes();
170 unsigned pcnt
= pts
.ports();
171 for(unsigned i
= 0; i
< pcnt
; i
++)
172 add_port(nextp
, i
, pts
.port_type(i
), pts
);
176 void frame_controls::add_port(unsigned& c
, unsigned pid
, const port_type
& p
, const port_type_set
& pts
)
178 const port_controller_set
& pci
= *(p
.controller_info
);
179 for(unsigned i
= 0; i
< pci
.controller_count
; i
++) {
180 if(!pci
.controllers
[i
])
182 const port_controller
& pc
= *(pci
.controllers
[i
]);
184 controlinfo
.push_back(control_info::fixedinfo(c
, U
"\u2502"));
186 controlinfo
.push_back(control_info::portinfo(nextp
, pid
, i
+ 1));
187 bool last_multibyte
= false;
188 for(unsigned j
= 0; j
< pc
.button_count
; j
++) {
191 const port_controller_button
& pcb
= *(pc
.buttons
[j
]);
192 unsigned idx
= pts
.triple_to_index(pid
, i
, j
);
193 if(idx
== 0xFFFFFFFFUL
)
195 if(pcb
.type
== port_controller_button::TYPE_BUTTON
) {
198 controlinfo
.push_back(control_info::buttoninfo(c
, pcb
.symbol
, to_u32string(pcb
.name
),
200 last_multibyte
= false;
201 } else if(pcb
.type
== port_controller_button::TYPE_AXIS
||
202 pcb
.type
== port_controller_button::TYPE_RAXIS
||
203 pcb
.type
== port_controller_button::TYPE_TAXIS
) {
206 controlinfo
.push_back(control_info::axisinfo(c
, to_u32string(pcb
.name
), idx
));
207 last_multibyte
= true;
215 short frame_controls::read_index(controller_frame
& f
, unsigned idx
)
218 return f
.sync() ? 1 : 0;
222 void frame_controls::write_index(controller_frame
& f
, unsigned idx
, short value
)
225 return f
.sync(value
);
226 return f
.axis2(idx
, value
);
229 uint32_t frame_controls::read_pollcount(pollcounter_vector
& v
, unsigned idx
)
232 return max(v
.max_polls(), (uint32_t)1);
233 return v
.get_polls(idx
);
236 void frame_controls::format_lines()
239 for(auto i
: controlinfo
) {
240 if(i
.position_left
+ i
.reserved
> _width
)
241 _width
= i
.position_left
+ i
.reserved
;
245 uint32_t off
= divcnt
+ 1;
246 cp1
.resize(_width
+ divcnt
+ 1);
247 cp2
.resize(_width
+ divcnt
+ 1);
248 for(unsigned i
= 0; i
< cp1
.size(); i
++)
249 cp1
[i
] = cp2
[i
] = 32;
250 cp1
[divcnt
] = 0x2502;
251 cp2
[divcnt
] = 0x2502;
253 //For every port-controller, find the least coordinate.
254 for(auto i
: controlinfo
) {
256 auto _title
= i
.title
;
257 std::copy(_title
.begin(), _title
.end(), &cp1
[i
.position_left
+ off
]);
258 } else if(i
.type
== -2) {
259 auto _title
= (stringfmt() << i
.port
<< "-" << i
.controller
).str32();
260 std::copy(_title
.begin(), _title
.end(), &cp1
[i
.position_left
+ off
]);
264 for(auto i
: controlinfo
) {
265 auto _title
= i
.title
;
266 if(i
.type
== -1 || i
.type
== 1)
267 std::copy(_title
.begin(), _title
.end(), &cp2
[i
.position_left
+ off
]);
269 cp2
[i
.position_left
+ off
] = i
.ch
;
276 class wxeditor_movie
: public wxDialog
279 wxeditor_movie(wxWindow
* parent
);
280 ~wxeditor_movie() throw();
281 bool ShouldPreventAppExit() const;
282 void on_close(wxCommandEvent
& e
);
283 void on_wclose(wxCloseEvent
& e
);
284 void on_focus_wrong(wxFocusEvent
& e
);
285 void on_keyboard_down(wxKeyEvent
& e
);
286 void on_keyboard_up(wxKeyEvent
& e
);
287 wxScrollBar
* get_scroll();
290 struct _moviepanel
: public wxPanel
, public information_dispatch
292 _moviepanel(wxeditor_movie
* v
);
293 ~_moviepanel() throw();
294 void signal_repaint();
295 void on_scroll(wxScrollEvent
& e
);
296 void on_paint(wxPaintEvent
& e
);
297 void on_erase(wxEraseEvent
& e
);
298 void on_mouse(wxMouseEvent
& e
);
299 void on_popup_menu(wxCommandEvent
& e
);
302 void render(text_framebuffer
& fb
, unsigned long long pos
);
303 void on_mouse0(unsigned x
, unsigned y
, bool polarity
);
304 void on_mouse1(unsigned x
, unsigned y
, bool polarity
);
305 void on_mouse2(unsigned x
, unsigned y
, bool polarity
);
306 void do_toggle_buttons(unsigned idx
, uint64_t row1
, uint64_t row2
);
307 void do_alter_axis(unsigned idx
, uint64_t row1
, uint64_t row2
);
308 void do_sweep_axis(unsigned idx
, uint64_t row1
, uint64_t row2
);
309 void do_append_frames(uint64_t count
);
310 void do_append_frames();
311 void do_insert_frame_after(uint64_t row
);
312 void do_delete_frame(uint64_t row
, bool wholeframe
);
313 void do_truncate(uint64_t row
);
314 void do_set_stop_at_frame();
315 void do_scroll_to_frame();
316 void do_scroll_to_current_frame();
317 uint64_t first_editable(unsigned index
);
318 uint64_t first_nextframe();
319 int width(controller_frame
& f
);
320 std::u32string
render_line1(controller_frame
& f
);
321 std::u32string
render_line2(controller_frame
& f
);
322 void render_linen(text_framebuffer
& fb
, controller_frame
& f
, uint64_t sfn
, int y
);
323 unsigned long long spos
;
327 std::map
<uint64_t, uint64_t> subframe_to_frame
;
328 uint64_t max_subframe
;
329 frame_controls fcontrols
;
337 std::vector
<uint8_t> pixels
;
341 uint64_t rpress_line
;
342 unsigned press_index
;
346 uint64_t cached_cffs
;
347 bool position_locked
;
348 wxMenu
* current_popup
;
350 _moviepanel
* moviepanel
;
351 wxButton
* closebutton
;
352 wxScrollBar
* moviescroll
;
358 wxeditor_movie
* movieeditor_open
;
360 //Find the first real editable subframe.
361 //Call only in emulator thread.
362 uint64_t real_first_editable(frame_controls
& fc
, unsigned idx
)
364 uint64_t cffs
= movb
.get_movie().get_current_frame_first_subframe();
365 controller_frame_vector
& fv
= movb
.get_movie().get_frame_vector();
366 pollcounter_vector
& pv
= movb
.get_movie().get_pollcounters();
367 uint64_t vsize
= fv
.size();
368 uint32_t pc
= fc
.read_pollcount(pv
, idx
);
369 for(uint32_t i
= 1; i
< pc
; i
++)
370 if(cffs
+ i
>= vsize
|| fv
[cffs
+ i
].sync())
375 //Find the first real editable whole frame.
376 //Call only in emulator thread.
377 uint64_t real_first_nextframe(frame_controls
& fc
)
379 uint64_t base
= real_first_editable(fc
, 0);
380 controller_frame_vector
& fv
= movb
.get_movie().get_frame_vector();
381 uint64_t vsize
= fv
.size();
382 for(uint32_t i
= 0;; i
++)
383 if(base
+ i
>= vsize
|| fv
[base
+ i
].sync())
387 //Adjust movie length by specified number of frames.
388 //Call only in emulator thread.
389 void movie_framecount_change(int64_t adjust
, bool known
= true);
390 void movie_framecount_change(int64_t adjust
, bool known
)
393 movb
.get_movie().adjust_frame_count(adjust
);
395 movb
.get_movie().recount_frames();
396 update_movie_state();
397 graphics_driver_notify_status();
401 wxeditor_movie::_moviepanel::~_moviepanel() throw() {}
402 wxeditor_movie::~wxeditor_movie() throw() {}
404 wxeditor_movie::_moviepanel::_moviepanel(wxeditor_movie
* v
)
405 : wxPanel(v
, wxID_ANY
, wxDefaultPosition
, wxSize(100, 100), wxWANTS_CHARS
),
406 information_dispatch("movieeditor-listener")
409 Connect(wxEVT_PAINT
, wxPaintEventHandler(_moviepanel::on_paint
), NULL
, this);
410 Connect(wxEVT_ERASE_BACKGROUND
, wxEraseEventHandler(_moviepanel::on_erase
), NULL
, this);
420 position_locked
= true;
421 current_popup
= NULL
;
423 Connect(wxEVT_LEFT_DOWN
, wxMouseEventHandler(_moviepanel::on_mouse
), NULL
, this);
424 Connect(wxEVT_LEFT_UP
, wxMouseEventHandler(_moviepanel::on_mouse
), NULL
, this);
425 Connect(wxEVT_MIDDLE_DOWN
, wxMouseEventHandler(_moviepanel::on_mouse
), NULL
, this);
426 Connect(wxEVT_MIDDLE_UP
, wxMouseEventHandler(_moviepanel::on_mouse
), NULL
, this);
427 Connect(wxEVT_RIGHT_DOWN
, wxMouseEventHandler(_moviepanel::on_mouse
), NULL
, this);
428 Connect(wxEVT_RIGHT_UP
, wxMouseEventHandler(_moviepanel::on_mouse
), NULL
, this);
429 Connect(wxEVT_MOUSEWHEEL
, wxMouseEventHandler(_moviepanel::on_mouse
), NULL
, this);
435 void wxeditor_movie::_moviepanel::update_cache()
437 movie
& m
= movb
.get_movie();
438 controller_frame_vector
& fv
= m
.get_frame_vector();
439 if(&m
== prev_obj
&& prev_seqno
== m
.get_seqno()) {
440 //Just process new subframes if any.
441 for(uint64_t i
= max_subframe
; i
< fv
.size(); i
++) {
442 uint64_t prev
= (i
> 0) ? subframe_to_frame
[i
- 1] : 0;
443 controller_frame f
= fv
[i
];
445 subframe_to_frame
[i
] = prev
+ 1;
447 subframe_to_frame
[i
] = prev
;
449 max_subframe
= fv
.size();
452 //Reprocess all subframes.
453 for(uint64_t i
= 0; i
< fv
.size(); i
++) {
454 uint64_t prev
= (i
> 0) ? subframe_to_frame
[i
- 1] : 0;
455 controller_frame f
= fv
[i
];
457 subframe_to_frame
[i
] = prev
+ 1;
459 subframe_to_frame
[i
] = prev
;
461 max_subframe
= fv
.size();
462 controller_frame model
= fv
.blank_frame(false);
463 fcontrols
.set_types(model
);
465 prev_seqno
= m
.get_seqno();
468 int wxeditor_movie::_moviepanel::width(controller_frame
& f
)
471 return divcnt
+ 1 + fcontrols
.width();
474 std::u32string
wxeditor_movie::_moviepanel::render_line1(controller_frame
& f
)
477 return fcontrols
.line1();
480 std::u32string
wxeditor_movie::_moviepanel::render_line2(controller_frame
& f
)
483 return fcontrols
.line2();
486 void wxeditor_movie::_moviepanel::render_linen(text_framebuffer
& fb
, controller_frame
& f
, uint64_t sfn
, int y
)
489 size_t fbstride
= fb
.get_stride();
490 text_framebuffer::element
* _fb
= fb
.get_buffer();
491 text_framebuffer::element e
;
494 for(unsigned i
= 0; i
< divcnt
; i
++) {
495 uint64_t fn
= subframe_to_frame
[sfn
];
496 e
.ch
= (fn
>= divsl
[i
]) ? (((fn
/ divs
[i
]) % 10) + 48) : 32;
497 _fb
[y
* fbstride
+ i
] = e
;
500 _fb
[y
* fbstride
+ divcnt
] = e
;
501 const std::list
<control_info
>& ctrlinfo
= fcontrols
.get_controlinfo();
502 uint64_t curframe
= movb
.get_movie().get_current_frame();
503 pollcounter_vector
& pv
= movb
.get_movie().get_pollcounters();
504 uint64_t cffs
= movb
.get_movie().get_current_frame_first_subframe();
507 if(!movb
.get_movie().readonly_mode())
509 else if(subframe_to_frame
[sfn
] < curframe
)
511 else if(subframe_to_frame
[sfn
] > curframe
)
513 bool now
= (subframe_to_frame
[sfn
] == curframe
);
514 unsigned xcord
= 32768;
518 for(auto i
: ctrlinfo
) {
520 unsigned off
= divcnt
+ 1;
521 bool cselected
= (xcord
>= i
.position_left
+ off
&& xcord
< i
.position_left
+ i
.reserved
+ off
);
523 unsigned polls
= fcontrols
.read_pollcount(pv
, i
.index
);
524 rpast
= ((cffs
+ polls
) > sfn
) ? 1 : 0;
526 uint32_t bgc
= 0xC0C0C0;
537 fb
.write(i
.title
, 0, divcnt
+ 1 + i
.position_left
, y
, 0x000000, 0xFFFFFF);
538 } else if(i
.type
== 0) {
541 bool v
= (fcontrols
.read_index(f
, i
.index
) != 0);
544 fb
.write(c
, 0, divcnt
+ 1 + i
.position_left
, y
, v
? 0x000000 : 0xC8C8C8, bgc
);
545 } else if(i
.type
== 1) {
548 sprintf(c
, "%6d", fcontrols
.read_index(f
, i
.index
));
549 fb
.write(c
, 0, divcnt
+ 1 + i
.position_left
, y
, 0x000000, bgc
);
554 void wxeditor_movie::_moviepanel::render(text_framebuffer
& fb
, unsigned long long pos
)
557 controller_frame_vector
& fv
= movb
.get_movie().get_frame_vector();
558 controller_frame cf
= fv
.blank_frame(false);
559 int _width
= width(cf
);
560 fb
.set_size(_width
, lines_to_display
+ 3);
561 size_t fbstride
= fb
.get_stride();
562 auto fbsize
= fb
.get_characters();
563 text_framebuffer::element
* _fb
= fb
.get_buffer();
564 fb
.write((stringfmt() << "Current frame: " << movb
.get_movie().get_current_frame() << " of "
565 << movb
.get_movie().get_frame_count()).str(), _width
, 0, 0,
567 fb
.write(render_line1(cf
), _width
, 0, 1, 0x000000, 0xFFFFFF);
568 fb
.write(render_line2(cf
), _width
, 0, 2, 0x000000, 0xFFFFFF);
569 unsigned long long lines
= fv
.size();
570 unsigned long long i
;
572 for(i
= pos
, j
= 3; i
< pos
+ lines_to_display
; i
++, j
++) {
573 text_framebuffer::element e
;
579 for(unsigned k
= 0; k
< fbsize
.first
; k
++)
580 _fb
[j
* fbstride
+ k
] = e
;
582 controller_frame frame
= fv
[i
];
583 render_linen(fb
, frame
, i
, j
);
588 void wxeditor_movie::_moviepanel::do_toggle_buttons(unsigned idx
, uint64_t row1
, uint64_t row2
)
590 frame_controls
* _fcontrols
= &fcontrols
;
591 uint64_t _press_line
= row1
;
592 uint64_t line
= row2
;
593 if(_press_line
> line
)
594 std::swap(_press_line
, line
);
596 runemufn([idx
, _press_line
, line
, _fcontrols
]() {
598 if(!movb
.get_movie().readonly_mode())
600 uint64_t fedit
= real_first_editable(*_fcontrols
, idx
);
601 controller_frame_vector
& fv
= movb
.get_movie().get_frame_vector();
602 for(uint64_t i
= _press_line
; i
<= line
; i
++) {
603 if(i
< fedit
|| i
>= fv
.size())
605 controller_frame cf
= fv
[i
];
606 bool v
= _fcontrols
->read_index(cf
, idx
);
607 _fcontrols
->write_index(cf
, idx
, !v
);
608 adjust
+= (v
? -1 : 1);
611 movie_framecount_change(adjust
);
615 max_subframe
= _press_line
; //Reparse.
618 void wxeditor_movie::_moviepanel::do_alter_axis(unsigned idx
, uint64_t row1
, uint64_t row2
)
620 frame_controls
* _fcontrols
= &fcontrols
;
621 uint64_t line
= row1
;
622 uint64_t line2
= row2
;
625 runemufn([idx
, line
, &value
, _fcontrols
, &valid
]() {
626 if(!movb
.get_movie().readonly_mode()) {
630 uint64_t fedit
= real_first_editable(*_fcontrols
, idx
);
631 controller_frame_vector
& fv
= movb
.get_movie().get_frame_vector();
632 if(line
< fedit
|| line
>= fv
.size()) {
636 controller_frame cf
= fv
[line
];
637 value
= _fcontrols
->read_index(cf
, idx
);
642 std::string text
= pick_text(m
, "Set value", "Enter new value:", (stringfmt() << value
).str());
643 value
= parse_value
<short>(text
);
644 } catch(canceled_exception
& e
) {
646 } catch(std::exception
& e
) {
647 wxMessageBox(wxT("Invalid value"), _T("Error"), wxICON_EXCLAMATION
| wxOK
, m
);
651 std::swap(line
, line2
);
652 runemufn([idx
, line
, line2
, value
, _fcontrols
]() {
653 uint64_t fedit
= real_first_editable(*_fcontrols
, idx
);
654 controller_frame_vector
& fv
= movb
.get_movie().get_frame_vector();
655 for(uint64_t i
= line
; i
<= line2
; i
++) {
656 if(i
< fedit
|| i
>= fv
.size())
658 controller_frame cf
= fv
[i
];
659 _fcontrols
->write_index(cf
, idx
, value
);
664 void wxeditor_movie::_moviepanel::do_sweep_axis(unsigned idx
, uint64_t row1
, uint64_t row2
)
666 frame_controls
* _fcontrols
= &fcontrols
;
667 uint64_t line
= row1
;
668 uint64_t line2
= row2
;
673 std::swap(line
, line2
);
674 runemufn([idx
, line
, line2
, &value
, &value2
, _fcontrols
, &valid
]() {
675 if(!movb
.get_movie().readonly_mode()) {
679 uint64_t fedit
= real_first_editable(*_fcontrols
, idx
);
680 controller_frame_vector
& fv
= movb
.get_movie().get_frame_vector();
681 if(line2
< fedit
|| line2
>= fv
.size()) {
685 controller_frame cf
= fv
[line
];
686 value
= _fcontrols
->read_index(cf
, idx
);
687 controller_frame cf2
= fv
[line2
];
688 value2
= _fcontrols
->read_index(cf2
, idx
);
692 runemufn([idx
, line
, line2
, value
, value2
, _fcontrols
]() {
693 uint64_t fedit
= real_first_editable(*_fcontrols
, idx
);
694 controller_frame_vector
& fv
= movb
.get_movie().get_frame_vector();
695 for(uint64_t i
= line
+ 1; i
<= line2
- 1; i
++) {
696 if(i
< fedit
|| i
>= fv
.size())
698 controller_frame cf
= fv
[i
];
699 auto tmp2
= static_cast<int64_t>(i
- line
) * (value2
- value
) /
700 static_cast<int64_t>(line2
- line
);
701 short tmp
= value
+ tmp2
;
702 _fcontrols
->write_index(cf
, idx
, tmp
);
707 void wxeditor_movie::_moviepanel::do_append_frames(uint64_t count
)
710 uint64_t _count
= count
;
711 runemufn([_count
]() {
712 if(!movb
.get_movie().readonly_mode())
714 controller_frame_vector
& fv
= movb
.get_movie().get_frame_vector();
715 for(uint64_t i
= 0; i
< _count
; i
++)
716 fv
.append(fv
.blank_frame(true));
717 movie_framecount_change(_count
);
722 void wxeditor_movie::_moviepanel::do_append_frames()
726 std::string text
= pick_text(m
, "Append frames", "Enter number of frames to append:", "");
727 value
= parse_value
<uint64_t>(text
);
728 } catch(canceled_exception
& e
) {
730 } catch(std::exception
& e
) {
731 wxMessageBox(wxT("Invalid value"), _T("Error"), wxICON_EXCLAMATION
| wxOK
, m
);
734 do_append_frames(value
);
737 void wxeditor_movie::_moviepanel::do_insert_frame_after(uint64_t row
)
740 frame_controls
* _fcontrols
= &fcontrols
;
742 runemufn([_row
, _fcontrols
]() {
743 if(!movb
.get_movie().readonly_mode())
745 controller_frame_vector
& fv
= movb
.get_movie().get_frame_vector();
746 uint64_t fedit
= real_first_editable(*_fcontrols
, 0);
747 //Find the start of the next frame.
748 uint64_t nframe
= _row
+ 1;
749 uint64_t vsize
= fv
.size();
750 while(nframe
< vsize
&& !fv
[nframe
].sync())
754 fv
.append(fv
.blank_frame(true));
756 //Okay, gotta copy all data after this point. nframe has to be at least 1.
757 for(uint64_t i
= vsize
- 1; i
>= nframe
; i
--)
759 fv
[nframe
] = fv
.blank_frame(true);
761 movie_framecount_change(1);
767 void wxeditor_movie::_moviepanel::do_delete_frame(uint64_t row
, bool wholeframe
)
771 bool _wholeframe
= wholeframe
;
772 frame_controls
* _fcontrols
= &fcontrols
;
773 runemufn([_row
, _wholeframe
, _fcontrols
]() {
774 controller_frame_vector
& fv
= movb
.get_movie().get_frame_vector();
775 uint64_t vsize
= fv
.size();
779 if(_row
< real_first_nextframe(*_fcontrols
))
781 //Scan backwards for the first subframe of this frame and forwards for the last.
785 lsf
++; //Bump by one so it finds the end.
786 while(fsf
< vsize
&& !fv
[fsf
].sync())
788 while(lsf
< vsize
&& !fv
[lsf
].sync())
790 uint64_t tonuke
= lsf
- fsf
;
791 //Nuke from fsf to lsf.
792 for(uint64_t i
= fsf
; i
< vsize
- tonuke
; i
++)
793 fv
[i
] = fv
[i
+ tonuke
];
794 fv
.resize(vsize
- tonuke
);
795 movie_framecount_change(-1);
797 if(_row
< real_first_editable(*_fcontrols
, 0))
799 //Is the nuked frame a first subframe?
800 bool is_first
= fv
[_row
].sync();
802 for(uint64_t i
= _row
; i
< vsize
- 1; i
++)
804 fv
.resize(vsize
- 1);
805 //Next subframe inherits the sync flag.
807 if(_row
< vsize
- 1 && !fv
[_row
].sync())
810 movie_framecount_change(-1);
819 void wxeditor_movie::_moviepanel::do_truncate(uint64_t row
)
823 frame_controls
* _fcontrols
= &fcontrols
;
824 runemufn([_row
, _fcontrols
]() {
825 controller_frame_vector
& fv
= movb
.get_movie().get_frame_vector();
826 uint64_t vsize
= fv
.size();
829 if(_row
< real_first_editable(*_fcontrols
, 0))
831 int64_t delete_count
= 0;
832 for(uint64_t i
= _row
; i
< vsize
; i
++)
836 movie_framecount_change(delete_count
);
842 void wxeditor_movie::_moviepanel::do_set_stop_at_frame()
846 runemufn([&curframe
]() {
847 curframe
= movb
.get_movie().get_current_frame();
850 std::string text
= pick_text(m
, "Frame", (stringfmt() << "Enter frame to stop at (currently at "
851 << curframe
<< "):").str(), "");
852 frame
= parse_value
<uint64_t>(text
);
853 } catch(canceled_exception
& e
) {
855 } catch(std::exception
& e
) {
856 wxMessageBox(wxT("Invalid value"), _T("Error"), wxICON_EXCLAMATION
| wxOK
, m
);
859 if(frame
< curframe
) {
860 wxMessageBox(wxT("The movie is already past that point"), _T("Error"), wxICON_EXCLAMATION
| wxOK
, m
);
864 set_stop_at_frame(frame
);
868 void wxeditor_movie::_moviepanel::on_mouse0(unsigned x
, unsigned y
, bool polarity
)
874 press_line
= spos
+ y
- 3;
879 uint64_t line
= spos
+ y
- 3;
880 if(press_x
< divcnt
&& x
< divcnt
) {
881 //Press on frame count.
882 uint64_t row1
= press_line
;
883 uint64_t row2
= line
;
885 std::swap(row1
, row2
);
886 do_append_frames(row2
- row1
+ 1);
888 for(auto i
: fcontrols
.get_controlinfo()) {
889 unsigned off
= divcnt
+ 1;
890 unsigned idx
= i
.index
;
891 if((press_x
>= i
.position_left
+ off
&& press_x
< i
.position_left
+ i
.reserved
+ off
) &&
892 (x
>= i
.position_left
+ off
&& x
< i
.position_left
+ i
.reserved
+ off
)) {
894 do_toggle_buttons(idx
, press_line
, line
);
896 do_alter_axis(idx
, press_line
, line
);
901 void wxeditor_movie::_moviepanel::do_scroll_to_frame()
905 std::string text
= pick_text(m
, "Frame", (stringfmt() << "Enter frame to scroll to:").str(), "");
906 frame
= parse_value
<uint64_t>(text
);
907 } catch(canceled_exception
& e
) {
909 } catch(std::exception
& e
) {
910 wxMessageBox(wxT("Invalid value"), _T("Error"), wxICON_EXCLAMATION
| wxOK
, m
);
915 uint64_t high
= max_subframe
;
917 wouldbe
= (low
+ high
) / 2;
918 if(subframe_to_frame
[wouldbe
] < frame
)
920 else if(subframe_to_frame
[wouldbe
] > frame
)
925 while(wouldbe
> 1 && subframe_to_frame
[wouldbe
- 1] == frame
)
931 void wxeditor_movie::_moviepanel::do_scroll_to_current_frame()
933 moviepos
= cached_cffs
;
937 void wxeditor_movie::_moviepanel::on_popup_menu(wxCommandEvent
& e
)
943 do_toggle_buttons(press_index
, press_line
, press_line
);
946 do_alter_axis(press_index
, press_line
, press_line
);
949 do_sweep_axis(press_index
, rpress_line
, press_line
);
951 case wxID_APPEND_FRAME
:
954 case wxID_APPEND_FRAMES
:
957 case wxID_INSERT_AFTER
:
958 do_insert_frame_after(press_line
);
960 case wxID_DELETE_FRAME
:
961 do_delete_frame(press_line
, true);
963 case wxID_DELETE_SUBFRAME
:
964 do_delete_frame(press_line
, false);
967 do_truncate(press_line
);
969 case wxID_RUN_TO_FRAME
:
970 do_set_stop_at_frame();
972 case wxID_SCROLL_FRAME
:
973 do_scroll_to_frame();
975 case wxID_SCROLL_CURRENT_FRAME
:
976 do_scroll_to_current_frame();
978 case wxID_POSITION_LOCK
:
981 tmpitem
= current_popup
->FindItem(wxID_POSITION_LOCK
);
982 position_locked
= tmpitem
->IsChecked();
984 case wxID_CHANGE_LINECOUNT
:
986 std::string text
= pick_text(m
, "Set number of lines", "Set number of lines visible:",
987 (stringfmt() << lines_to_display
).str());
988 unsigned tmp
= parse_value
<unsigned>(text
);
989 if(tmp
< 1 || tmp
> 255)
990 throw std::runtime_error("Value out of range");
991 lines_to_display
= tmp
;
992 } catch(canceled_exception
& e
) {
994 } catch(std::exception
& e
) {
995 wxMessageBox(wxT("Invalid value"), _T("Error"), wxICON_EXCLAMATION
| wxOK
, m
);
1003 uint64_t wxeditor_movie::_moviepanel::first_editable(unsigned index
)
1005 uint64_t cffs
= cached_cffs
;
1006 if(!subframe_to_frame
.count(cffs
))
1008 uint64_t f
= subframe_to_frame
[cffs
];
1009 pollcounter_vector
& pv
= movb
.get_movie().get_pollcounters();
1010 uint32_t pc
= fcontrols
.read_pollcount(pv
, index
);
1011 for(uint32_t i
= 1; i
< pc
; i
++)
1012 if(!subframe_to_frame
.count(cffs
+ i
) || subframe_to_frame
[cffs
+ i
] > f
)
1017 uint64_t wxeditor_movie::_moviepanel::first_nextframe()
1019 uint64_t base
= first_editable(0);
1020 if(!subframe_to_frame
.count(cached_cffs
))
1022 uint64_t f
= subframe_to_frame
[cached_cffs
];
1023 for(uint32_t i
= 0;; i
++)
1024 if(!subframe_to_frame
.count(base
+ i
) || subframe_to_frame
[base
+ i
] > f
)
1028 void wxeditor_movie::_moviepanel::on_mouse1(unsigned x
, unsigned y
, bool polarity
) {}
1029 void wxeditor_movie::_moviepanel::on_mouse2(unsigned x
, unsigned y
, bool polarity
)
1032 rpress_line
= spos
+ y
- 3;
1036 current_popup
= &menu
;
1037 bool enable_toggle_button
= false;
1038 bool enable_change_axis
= false;
1039 bool enable_insert_frame
= false;
1040 bool enable_delete_frame
= false;
1041 bool enable_delete_subframe
= false;
1042 std::u32string title
;
1045 if(!movb
.get_movie().readonly_mode())
1048 press_line
= spos
+ y
- 3;
1049 for(auto i
: fcontrols
.get_controlinfo()) {
1050 unsigned off
= divcnt
+ 1;
1051 if(press_x
>= i
.position_left
+ off
&& press_x
< i
.position_left
+ i
.reserved
+ off
) {
1052 if(i
.type
== 0 && press_line
>= first_editable(i
.index
) &&
1053 press_line
< linecount
) {
1054 enable_toggle_button
= true;
1055 press_index
= i
.index
;
1058 if(i
.type
== 1 && press_line
>= first_editable(i
.index
) &&
1059 press_line
< linecount
) {
1060 enable_change_axis
= true;
1061 press_index
= i
.index
;
1066 if(press_line
+ 1 >= first_editable(0) && press_line
< linecount
)
1067 enable_insert_frame
= true;
1068 if(press_line
>= first_editable(0) && press_line
< linecount
)
1069 enable_delete_subframe
= true;
1070 if(press_line
>= first_nextframe() && press_line
< linecount
)
1071 enable_delete_frame
= true;
1072 if(enable_toggle_button
)
1073 menu
.Append(wxID_TOGGLE
, towxstring(U
"Toggle " + title
));
1074 if(enable_change_axis
)
1075 menu
.Append(wxID_CHANGE
, towxstring(U
"Change " + title
));
1076 if(enable_change_axis
&& rpress_line
!= press_line
)
1077 menu
.Append(wxID_SWEEP
, towxstring(U
"Sweep " + title
));
1078 if(enable_toggle_button
|| enable_change_axis
)
1079 menu
.AppendSeparator();
1080 menu
.Append(wxID_INSERT_AFTER
, wxT("Insert frame after"))->Enable(enable_insert_frame
);
1081 menu
.Append(wxID_APPEND_FRAME
, wxT("Append frame"));
1082 menu
.Append(wxID_APPEND_FRAMES
, wxT("Append frames..."));
1083 menu
.AppendSeparator();
1084 menu
.Append(wxID_DELETE_FRAME
, wxT("Delete frame"))->Enable(enable_delete_frame
);
1085 menu
.Append(wxID_DELETE_SUBFRAME
, wxT("Delete subframe"))->Enable(enable_delete_subframe
);
1086 menu
.AppendSeparator();
1087 menu
.Append(wxID_TRUNCATE
, wxT("Truncate movie"))->Enable(enable_delete_subframe
);
1088 menu
.AppendSeparator();
1090 menu
.Append(wxID_SCROLL_FRAME
, wxT("Scroll to frame..."));
1091 menu
.Append(wxID_SCROLL_CURRENT_FRAME
, wxT("Scroll to current frame"));
1092 menu
.Append(wxID_RUN_TO_FRAME
, wxT("Run to frame..."));
1093 menu
.Append(wxID_CHANGE_LINECOUNT
, wxT("Change number of lines visible"));
1094 menu
.AppendCheckItem(wxID_POSITION_LOCK
, wxT("Lock scroll to playback"))->Check(position_locked
);
1095 menu
.Connect(wxEVT_COMMAND_MENU_SELECTED
, wxCommandEventHandler(wxeditor_movie::_moviepanel::on_popup_menu
),
1100 int wxeditor_movie::_moviepanel::get_lines()
1102 controller_frame_vector
& fv
= movb
.get_movie().get_frame_vector();
1106 void wxeditor_movie::_moviepanel::signal_repaint()
1108 if(requested
|| recursing
)
1110 auto s
= m
->get_scroll();
1112 uint32_t width
, height
;
1114 wxeditor_movie
* m2
= m
;
1115 uint64_t old_cached_cffs
= cached_cffs
;
1116 uint32_t prev_width
, prev_height
;
1117 bool done_again
= false;
1119 runemufn([&lines
, &width
, &height
, m2
, this]() {
1120 lines
= this->get_lines();
1121 if(lines
< lines_to_display
)
1123 else if(this->moviepos
> lines
- lines_to_display
)
1124 this->moviepos
= lines
- lines_to_display
;
1125 this->render(fb
, moviepos
);
1126 auto x
= fb
.get_characters();
1130 if(old_cached_cffs
!= cached_cffs
&& position_locked
&& !done_again
) {
1131 moviepos
= cached_cffs
;
1135 prev_width
= new_width
;
1136 prev_height
= new_height
;
1138 new_height
= height
;
1141 s
->SetScrollbar(moviepos
, lines_to_display
, lines
, lines_to_display
- 1);
1142 auto size
= fb
.get_pixels();
1143 pixels
.resize(size
.first
* size
.second
* 3);
1144 fb
.render((char*)&pixels
[0]);
1145 if(prev_width
!= new_width
|| prev_height
!= new_height
) {
1146 auto cell
= fb
.get_cell();
1147 SetMinSize(wxSize(new_width
* cell
.first
, (lines_to_display
+ 3) * cell
.second
));
1148 if(new_width
> 0 && s
)
1155 void wxeditor_movie::_moviepanel::on_mouse(wxMouseEvent
& e
)
1157 auto cell
= fb
.get_cell();
1158 if(e
.LeftDown() && !e
.ControlDown())
1159 on_mouse0(e
.GetX() / cell
.first
, e
.GetY() / cell
.second
, true);
1160 if(e
.LeftUp() && !e
.ControlDown())
1161 on_mouse0(e
.GetX() / cell
.first
, e
.GetY() / cell
.second
, false);
1163 on_mouse1(e
.GetX() / cell
.first
, e
.GetY() / cell
.second
, true);
1165 on_mouse1(e
.GetX() / cell
.first
, e
.GetY() / cell
.second
, false);
1166 if(e
.RightDown() || (e
.LeftDown() && e
.ControlDown()))
1167 on_mouse2(e
.GetX() / cell
.first
, e
.GetY() / cell
.second
, true);
1168 if(e
.RightUp() || (e
.LeftUp() && e
.ControlDown()))
1169 on_mouse2(e
.GetX() / cell
.first
, e
.GetY() / cell
.second
, false);
1170 int wrotate
= e
.GetWheelRotation();
1171 int threshold
= e
.GetWheelDelta();
1172 bool scrolled
= false;
1173 auto s
= m
->get_scroll();
1175 scroll_delta
+= wrotate
;
1176 while(wrotate
&& threshold
&& scroll_delta
<= -threshold
) {
1177 //Scroll down by line.
1179 if(movielines
<= lines_to_display
)
1181 else if(moviepos
> movielines
- lines_to_display
+ 1)
1182 moviepos
= movielines
- lines_to_display
+ 1;
1184 scroll_delta
+= threshold
;
1186 while(wrotate
&& threshold
&& scroll_delta
>= threshold
) {
1187 //Scroll up by line.
1191 scroll_delta
-= threshold
;
1194 s
->SetThumbPosition(moviepos
);
1198 void wxeditor_movie::_moviepanel::on_scroll(wxScrollEvent
& e
)
1200 auto s
= m
->get_scroll();
1202 moviepos
= s
->GetThumbPosition();
1208 void wxeditor_movie::_moviepanel::on_erase(wxEraseEvent
& e
)
1213 void wxeditor_movie::_moviepanel::on_paint(wxPaintEvent
& e
)
1215 auto size
= fb
.get_pixels();
1216 if(!size
.first
|| !size
.second
) {
1223 wxBitmap
bmp(wxImage(size
.first
, size
.second
, &pixels
[0], true));
1224 dc
.DrawBitmap(bmp
, 0, 0, false);
1228 wxeditor_movie::wxeditor_movie(wxWindow
* parent
)
1229 : wxDialog(parent
, wxID_ANY
, wxT("lsnes: Edit movie"), wxDefaultPosition
, wxSize(-1, -1))
1233 wxFlexGridSizer
* top_s
= new wxFlexGridSizer(2, 1, 0, 0);
1236 wxBoxSizer
* panel_s
= new wxBoxSizer(wxHORIZONTAL
);
1238 panel_s
->Add(moviepanel
= new _moviepanel(this), 1, wxGROW
);
1239 panel_s
->Add(moviescroll
= new wxScrollBar(this, wxID_ANY
, wxDefaultPosition
, wxDefaultSize
,
1240 wxSB_VERTICAL
), 0, wxGROW
);
1241 top_s
->Add(panel_s
, 1, wxGROW
);
1242 connect_events(moviescroll
, wxScrollEventHandler(wxeditor_movie::_moviepanel::on_scroll
), moviepanel
);
1243 moviepanel
->Connect(wxEVT_KEY_DOWN
, wxKeyEventHandler(wxeditor_movie::on_keyboard_down
), NULL
, this);
1244 moviepanel
->Connect(wxEVT_KEY_UP
, wxKeyEventHandler(wxeditor_movie::on_keyboard_up
), NULL
, this);
1246 wxBoxSizer
* pbutton_s
= new wxBoxSizer(wxHORIZONTAL
);
1247 pbutton_s
->AddStretchSpacer();
1248 pbutton_s
->Add(closebutton
= new wxButton(this, wxID_OK
, wxT("Close")), 0, wxGROW
);
1249 closebutton
->Connect(wxEVT_COMMAND_BUTTON_CLICKED
,
1250 wxCommandEventHandler(wxeditor_movie::on_close
), NULL
, this);
1251 top_s
->Add(pbutton_s
, 0, wxGROW
);
1253 moviepanel
->SetFocus();
1254 moviescroll
->Connect(wxEVT_SET_FOCUS
, wxFocusEventHandler(wxeditor_movie::on_focus_wrong
), NULL
, this);
1255 closebutton
->Connect(wxEVT_SET_FOCUS
, wxFocusEventHandler(wxeditor_movie::on_focus_wrong
), NULL
, this);
1256 Connect(wxEVT_SET_FOCUS
, wxFocusEventHandler(wxeditor_movie::on_focus_wrong
), NULL
, this);
1258 panel_s
->SetSizeHints(this);
1259 pbutton_s
->SetSizeHints(this);
1260 top_s
->SetSizeHints(this);
1261 Connect(wxEVT_CLOSE_WINDOW
, wxCloseEventHandler(wxeditor_movie::on_wclose
));
1264 moviepanel
->signal_repaint();
1267 bool wxeditor_movie::ShouldPreventAppExit() const { return false; }
1269 void wxeditor_movie::on_close(wxCommandEvent
& e
)
1271 movieeditor_open
= NULL
;
1276 void wxeditor_movie::on_wclose(wxCloseEvent
& e
)
1278 bool wasc
= closing
;
1280 movieeditor_open
= NULL
;
1285 void wxeditor_movie::update()
1287 moviepanel
->signal_repaint();
1290 wxScrollBar
* wxeditor_movie::get_scroll()
1295 void wxeditor_movie::on_focus_wrong(wxFocusEvent
& e
)
1297 moviepanel
->SetFocus();
1300 void wxeditor_movie_display(wxWindow
* parent
)
1302 if(movieeditor_open
)
1304 wxeditor_movie
* v
= new wxeditor_movie(parent
);
1306 movieeditor_open
= v
;
1309 void wxeditor_movie::on_keyboard_down(wxKeyEvent
& e
)
1311 handle_wx_keyboard(e
, true);
1314 void wxeditor_movie::on_keyboard_up(wxKeyEvent
& e
)
1316 handle_wx_keyboard(e
, false);
1319 void wxeditor_movie_update()
1321 if(movieeditor_open
)
1322 movieeditor_open
->update();