diff --git a/aegisub/src/command/video.cpp b/aegisub/src/command/video.cpp index ef0474c5f..702f31f21 100644 --- a/aegisub/src/command/video.cpp +++ b/aegisub/src/command/video.cpp @@ -43,6 +43,8 @@ #include "command.h" +#include "../ass_dialogue.h" +#include "../ass_time.h" #include "../compat.h" #include "../frame_main.h" #include "../main.h" @@ -254,6 +256,66 @@ struct video_frame_next : public Command { } }; +/// Seek to the next subtitle boundary. +struct video_frame_next_boundary : public Command { + CMD_NAME("video/frame/next/boundary") + STR_MENU("Next Boundary") + STR_DISP("Next Boundary") + STR_HELP("Seek to the next subtitle boundary.") + + void operator()(agi::Context *c) { + AssDialogue *active_line = c->selectionController->GetActiveLine(); + if (!active_line) return; + + int target = c->videoController->FrameAtTime(active_line->Start.GetMS(), agi::vfr::START); + if (target > c->videoController->GetFrameN()) { + c->videoController->JumpToFrame(target); + return; + } + + target = c->videoController->FrameAtTime(active_line->End.GetMS(), agi::vfr::END); + if (target > c->videoController->GetFrameN()) { + c->videoController->JumpToFrame(target); + return; + } + + c->selectionController->NextLine(); + AssDialogue *new_line = c->selectionController->GetActiveLine(); + if (new_line != active_line) + c->videoController->JumpToTime(new_line->Start.GetMS()); + } +}; + +/// Seek to the next keyframe. +struct video_frame_next_keyframe : public Command { + CMD_NAME("video/frame/next/keyframe") + STR_MENU("Next Keyframe") + STR_DISP("Next Keyframe") + STR_HELP("Seek to the next keyframe.") + + void operator()(agi::Context *c) { + std::vector const& kf = c->videoController->GetKeyFrames(); + std::vector::const_iterator pos = lower_bound(kf.begin(), kf.end(), c->videoController->GetFrameN()); + if (pos != kf.end()) ++pos; + + c->videoController->JumpToFrame(pos == kf.end() ? c->videoController->GetFrameN() - 1 : *pos); + } +}; + +/// Fast jump forward +struct video_frame_next_large : public Command { + CMD_NAME("video/frame/next/large") + STR_MENU("Fast jump forward") + STR_DISP("Fast jump forward") + STR_HELP("Fast jump forward.") + + void operator()(agi::Context *c) { + c->videoController->JumpToFrame( + c->videoController->GetFrameN() + + OPT_GET("Video/Slider/Fast Jump Step")->GetInt()); + } +}; + /// Seek to the previous frame. struct video_frame_prev : public Command { CMD_NAME("video/frame/prev") @@ -266,6 +328,68 @@ struct video_frame_prev : public Command { } }; +/// Seek to the previous subtitle boundary. +struct video_frame_prev_boundary : public Command { + CMD_NAME("video/frame/prev/boundary") + STR_MENU("Previous Boundary") + STR_DISP("Previous Boundary") + STR_HELP("Seek to the previous subtitle boundary.") + + void operator()(agi::Context *c) { + AssDialogue *active_line = c->selectionController->GetActiveLine(); + if (!active_line) return; + + int target = c->videoController->FrameAtTime(active_line->End.GetMS(), agi::vfr::END); + if (target < c->videoController->GetFrameN()) { + c->videoController->JumpToFrame(target); + return; + } + + target = c->videoController->FrameAtTime(active_line->Start.GetMS(), agi::vfr::START); + if (target < c->videoController->GetFrameN()) { + c->videoController->JumpToFrame(target); + return; + } + + c->selectionController->PrevLine(); + AssDialogue *new_line = c->selectionController->GetActiveLine(); + if (new_line != active_line) + c->videoController->JumpToTime(new_line->End.GetMS(), agi::vfr::END); + } +}; + +/// Seek to the previous keyframe. +struct video_frame_prev_keyframe : public Command { + CMD_NAME("video/frame/prev/keyframe") + STR_MENU("Previous Keyframe") + STR_DISP("Previous Keyframe") + STR_HELP("Seek to the previous keyframe.") + + void operator()(agi::Context *c) { + int frame = c->videoController->GetFrameN(); + std::vector const& kf = c->videoController->GetKeyFrames(); + std::vector::const_iterator pos = lower_bound(kf.begin(), kf.end(), frame); + + if (frame != 0 && (pos == kf.end() || *pos == frame)) + --pos; + + c->videoController->JumpToFrame(*pos); + } +}; + +/// Fast jump backwards +struct video_frame_prev_large : public Command { + CMD_NAME("video/frame/prev/large") + STR_MENU("Fast jump backwards") + STR_DISP("Fast jump backwards") + STR_HELP("Fast jump backwards") + + void operator()(agi::Context *c) { + c->videoController->JumpToFrame( + c->videoController->GetFrameN() - + OPT_GET("Video/Slider/Fast Jump Step")->GetInt()); + } +}; /// Jump to frame or time. struct video_jump : public Command { @@ -493,7 +617,13 @@ void init_video(CommandManager *cm) { cm->reg(new video_details); cm->reg(new video_focus_seek); cm->reg(new video_frame_next); + cm->reg(new video_frame_next_boundary); + cm->reg(new video_frame_next_keyframe); + cm->reg(new video_frame_next_large); cm->reg(new video_frame_prev); + cm->reg(new video_frame_prev_boundary); + cm->reg(new video_frame_prev_keyframe); + cm->reg(new video_frame_prev_large); cm->reg(new video_jump); cm->reg(new video_jump_end); cm->reg(new video_jump_start); diff --git a/aegisub/src/dialog_detached_video.cpp b/aegisub/src/dialog_detached_video.cpp index 3c618289b..452059839 100644 --- a/aegisub/src/dialog_detached_video.cpp +++ b/aegisub/src/dialog_detached_video.cpp @@ -79,7 +79,6 @@ DialogDetachedVideo::DialogDetachedVideo(FrameMain *parent, agi::Context *contex videoBox = new VideoBox(panel, true, NULL, context); videoBox->videoDisplay->freeSize = true; videoBox->videoDisplay->SetClientSize(initialDisplaySize); - videoBox->videoSlider->grid = context->subsGrid; // Set sizer wxSizer *mainSizer = new wxBoxSizer(wxVERTICAL); diff --git a/aegisub/src/frame_main.cpp b/aegisub/src/frame_main.cpp index fa8a5410b..b13839dd1 100644 --- a/aegisub/src/frame_main.cpp +++ b/aegisub/src/frame_main.cpp @@ -298,7 +298,6 @@ void FrameMain::InitContents() { StartupLog("Create subtitles grid"); context->subsGrid = SubsGrid = new SubtitlesGrid(Panel,context.get(),wxSize(600,100),wxWANTS_CHARS | wxSUNKEN_BORDER,"Subs grid"); context->selectionController = context->subsGrid; - context->videoBox->videoSlider->grid = SubsGrid; Search.context = context.get(); StartupLog("Create tool area splitter window"); diff --git a/aegisub/src/libresrc/default_hotkey.json b/aegisub/src/libresrc/default_hotkey.json index 19a8b735a..7d9b5a3d6 100644 --- a/aegisub/src/libresrc/default_hotkey.json +++ b/aegisub/src/libresrc/default_hotkey.json @@ -504,6 +504,27 @@ "enable" : true } ], + "video/frame/next/boundary" : [ + { + "modifiers" : [ "Ctrl" ], + "key" : "Right", + "enable" : true + } + ], + "video/frame/next/keyframe" : [ + { + "modifiers" : [ "Shift" ], + "key" : "Right", + "enable" : true + } + ], + "video/frame/next/large" : [ + { + "modifiers" : [ "Alt" ], + "key" : "Right", + "enable" : true + } + ], "video/frame/prev" : [ { "modifiers" : [], @@ -511,6 +532,27 @@ "enable" : true } ], + "video/frame/prev/boundary" : [ + { + "modifiers" : [ "Ctrl" ], + "key" : "Left", + "enable" : true + } + ], + "video/frame/prev/keyframe" : [ + { + "modifiers" : [ "Shift" ], + "key" : "Left", + "enable" : true + } + ], + "video/frame/prev/large" : [ + { + "modifiers" : [ "Alt" ], + "key" : "Left", + "enable" : true + } + ], "visual typesetting set tool crosshair" : [ { "modifiers" : [], diff --git a/aegisub/src/video_box.cpp b/aegisub/src/video_box.cpp index 6b2519e72..1d6de281e 100644 --- a/aegisub/src/video_box.cpp +++ b/aegisub/src/video_box.cpp @@ -90,7 +90,7 @@ VideoBox::VideoBox(wxWindow *parent, bool isDetached, wxComboBox *zoomBox, agi:: add_option(this, videoBottomSizer, "video/opt/autoscroll", "Video/Subtitle Sync"); // Seek - videoSlider = new VideoSlider(this,-1); + videoSlider = new VideoSlider(this, context); videoSlider->SetToolTip(_("Seek video.")); // Position @@ -120,9 +120,6 @@ VideoBox::VideoBox(wxWindow *parent, bool isDetached, wxComboBox *zoomBox, agi:: // Display videoDisplay = new VideoDisplay(this,VideoPosition,VideoSubsPos,zoomBox,this,context); - // Set display - videoSlider->Display = videoDisplay; - // Top sizer // Detached and attached video needs different flags, see bugs #742 and #853 int highSizerFlags = isDetached ? wxEXPAND : 0; diff --git a/aegisub/src/video_context.cpp b/aegisub/src/video_context.cpp index 4c5c5a497..a4ca450e5 100644 --- a/aegisub/src/video_context.cpp +++ b/aegisub/src/video_context.cpp @@ -266,7 +266,7 @@ void VideoContext::JumpToFrame(int n) { // Prevent intervention during playback if (isPlaying && n != playNextFrame) return; - frame_n = n; + frame_n = mid(0, n, GetLength() - 1); Seek(n); } diff --git a/aegisub/src/video_slider.cpp b/aegisub/src/video_slider.cpp index 81dc01454..e0ff0a449 100644 --- a/aegisub/src/video_slider.cpp +++ b/aegisub/src/video_slider.cpp @@ -40,52 +40,30 @@ #include #endif -#include "ass_dialogue.h" +#include "include/aegisub/context.h" +#include "include/aegisub/hotkey.h" #include "main.h" -#include "subs_edit_box.h" #include "selection_controller.h" #include "subs_grid.h" #include "utils.h" #include "video_context.h" -#include "video_display.h" #include "video_slider.h" -/// IDs -enum { - NextFrame = 1300, - PrevFrame -}; - -/// @brief Constructor -/// @param parent -/// @param id -/// -VideoSlider::VideoSlider (wxWindow* parent, wxWindowID id) -: wxWindow (parent,id,wxDefaultPosition,wxDefaultSize,wxWANTS_CHARS | wxFULL_REPAINT_ON_RESIZE) +VideoSlider::VideoSlider (wxWindow* parent, agi::Context *c) +: wxWindow(parent, -1, wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS | wxFULL_REPAINT_ON_RESIZE) +, vc(c->videoController) +, grid(c->subsGrid) +, val(0) +, max(1) { - val = 0; - max = 1; - Display = NULL; SetClientSize(20,25); SetMinSize(wxSize(20, 25)); - locked = false; - OPT_SUB("Video/Slider/Show Keyframes", &wxWindow::Refresh, this, false, (wxRect*)NULL); - VideoContext *vc = VideoContext::Get(); - assert(vc); - vc->AddSeekListener(&VideoSlider::SetValue, this); - vc->AddVideoOpenListener(&VideoSlider::VideoOpened, this); - vc->AddKeyframesListener(&VideoSlider::KeyframesChanged, this); + slots.push_back(OPT_SUB("Video/Slider/Show Keyframes", &wxWindow::Refresh, this, false, (wxRect*)NULL)); + slots.push_back(vc->AddSeekListener(&VideoSlider::SetValue, this)); + slots.push_back(vc->AddVideoOpenListener(&VideoSlider::VideoOpened, this)); + slots.push_back(vc->AddKeyframesListener(&VideoSlider::KeyframesChanged, this)); } -VideoSlider::~VideoSlider() { - VideoContext *vc = VideoContext::Get(); - assert(vc); -} - -/// @brief Set value -/// @param value -/// @return -/// void VideoSlider::SetValue(int value) { if (val == value) return; val = mid(0, value, max); @@ -93,7 +71,6 @@ void VideoSlider::SetValue(int value) { } void VideoSlider::VideoOpened() { - VideoContext *vc = VideoContext::Get(); max = vc->GetLength() - 1; keyframes = vc->GetKeyFrames(); Refresh(false); @@ -104,37 +81,21 @@ void VideoSlider::KeyframesChanged(std::vector const& newKeyframes) { Refresh(false); } -/// @brief Get value at X -/// @param x -/// @return -/// int VideoSlider::GetValueAtX(int x) { - // Get dimensions int w,h; GetClientSize(&w,&h); // Special case if (w <= 10) return 0; - // Calculate return (int64_t)(x-5)*(int64_t)max/(int64_t)(w-10); } - - -/// @brief Get X at value -/// @param value -/// @return -/// int VideoSlider::GetXAtValue(int value) { - // Get dimensions - int w,h; - GetClientSize(&w,&h); - - // Special case if (max <= 0) return 0; - // Calculate + int w,h; + GetClientSize(&w,&h); return (int64_t)value*(int64_t)(w-10)/(int64_t)max+5; } @@ -147,223 +108,79 @@ BEGIN_EVENT_TABLE(VideoSlider, wxWindow) EVT_ERASE_BACKGROUND(VideoSlider::OnEraseBackground) END_EVENT_TABLE() -/// @brief Mouse events -/// @param event -/// @return -/// void VideoSlider::OnMouse(wxMouseEvent &event) { - // Coordinates int x = event.GetX(); - //int y = event.GetY(); - bool shift = event.m_shiftDown; - // Left click if (event.ButtonIsDown(wxMOUSE_BTN_LEFT)) { - // Check if it's OK to drag - bool canDrag = wxWindow::FindFocus() == this; - if (!canDrag) { - int tolerance = 4; - int curX = GetXAtValue(val); - if (x-curX < -tolerance || x-curX > tolerance) canDrag = true; + // If the slider didn't already have focus, don't seek if the user + // clicked very close to the current location as they were probably + // just trying to focus the slider + if (wxWindow::FindFocus() != this && abs(x - GetXAtValue(val)) < 4) { + SetFocus(); + return; } - // Drag - if (canDrag) { - // Shift click to snap to keyframe - if (shift && Display) { - std::vector KeyFrames = VideoContext::Get()->GetKeyFrames(); - int keys = KeyFrames.size(); - int clickedFrame = GetValueAtX(x); - int closest = 0; - int cur; + // Shift click to snap to keyframe + if (event.m_shiftDown) { + int clickedFrame = GetValueAtX(x); + std::vector::const_iterator pos = lower_bound(keyframes.begin(), keyframes.end(), clickedFrame); + if (pos == keyframes.end()) + --pos; + else if (pos + 1 != keyframes.end() && clickedFrame - *pos > (*pos + 1) - clickedFrame) + ++pos; - // Find closest - for (int i=0;iIsPlaying()) { - VideoContext::Get()->Stop(); - VideoContext::Get()->JumpToFrame(val); - VideoContext::Get()->Play(); - } - else VideoContext::Get()->JumpToFrame(val); + if (*pos == val) return; + SetValue(*pos); } - // Get focus + // Normal click + else { + int go = GetValueAtX(x); + if (go == val) return; + SetValue(go); + } + + if (vc->IsPlaying()) { + vc->Stop(); + vc->JumpToFrame(val); + vc->Play(); + } + else + vc->JumpToFrame(val); SetFocus(); + return; } - // Right/middle click if (event.ButtonDown(wxMOUSE_BTN_RIGHT) || event.ButtonDown(wxMOUSE_BTN_MIDDLE)) { SetFocus(); } - // Something else - else if (!VideoContext::Get()->IsPlaying()) event.Skip(); + else if (!vc->IsPlaying()) + event.Skip(); } - - -/// @brief Key down event -/// @param event -/// @return -/// void VideoSlider::OnKeyDown(wxKeyEvent &event) { - if (VideoContext::Get()->IsPlaying()) return; + if (vc->IsPlaying()) return; - // Get flags - int key = event.GetKeyCode(); -#ifdef __APPLE__ - bool ctrl = event.m_metaDown; -#else - bool ctrl = event.m_controlDown; -#endif - bool alt = event.m_altDown; - bool shift = event.m_shiftDown; - int direction = 0; - - // Pick direction - if (key == WXK_LEFT) direction = -1; - else if (key == WXK_RIGHT) direction = 1; - - // If a direction was actually pressed - if (direction) { - // Standard move - if (!ctrl && !shift && !alt) { - if (direction == 1) VideoContext::Get()->NextFrame(); - else VideoContext::Get()->PrevFrame(); - return; - } - - // Fast move - if (!ctrl && !shift && alt) { - if (VideoContext::Get()->IsPlaying()) return; - int target = mid(0,val + direction * OPT_GET("Video/Slider/Fast Jump Step")->GetInt(),max); - if (target != val) VideoContext::Get()->JumpToFrame(target); - return; - } - - // Boundaries - if (ctrl && !shift && !alt) { - // Prepare - wxArrayInt sel = grid->GetSelection(); - int cur; - if (sel.Count() > 0) cur = sel[0]; - else { - grid->SetActiveLine(grid->GetDialogue(0)); - grid->SelectRow(0); - cur = 0; - } - AssDialogue *curDiag = grid->GetDialogue(cur); - if (!curDiag) return; - - // Jump to next sub boundary - if (direction != 0) { - int target1 = VideoContext::Get()->FrameAtTime(curDiag->Start.GetMS(),agi::vfr::START); - int target2 = VideoContext::Get()->FrameAtTime(curDiag->End.GetMS(),agi::vfr::END); - bool drawn = false; - - // Forward - if (direction == 1) { - if (VideoContext::Get()->GetFrameN() < target1) VideoContext::Get()->JumpToFrame(target1); - else if (VideoContext::Get()->GetFrameN() < target2) VideoContext::Get()->JumpToFrame(target2); - else { - if (cur+1 >= grid->GetRows()) return; - grid->SetActiveLine(grid->GetDialogue(cur+1)); - grid->SelectRow(cur+1); - grid->MakeCellVisible(cur+1,0); - grid->SetVideoToSubs(true); - grid->Refresh(false); - drawn = true; - } - return; - } - - // Backward - else { - if (VideoContext::Get()->GetFrameN() > target2) VideoContext::Get()->JumpToFrame(target2); - else if (VideoContext::Get()->GetFrameN() > target1) VideoContext::Get()->JumpToFrame(target1); - else { - if (cur-1 < 0) return; - grid->SetActiveLine(grid->GetDialogue(cur-1)); - grid->SelectRow(cur-1); - grid->MakeCellVisible(cur-1,0); - grid->SetVideoToSubs(false); - grid->Refresh(false); - drawn = true; - } - return; - } - } - } - - // Snap to keyframe - if (shift && !ctrl && !alt) { - if (direction != 0) { - std::vector::iterator pos = std::lower_bound(keyframes.begin(), keyframes.end(), val); - - if (direction < 0 && val != 0 && (pos == keyframes.end() || *pos == val)) - --pos; - if (direction > 0 && pos != keyframes.end()) - ++pos; - - VideoContext::Get()->JumpToFrame(pos == keyframes.end() ? max : *pos); - return; - } - } - } + if (hotkey::check("Video", event.GetKeyCode(), event.GetUnicodeKey(), event.GetModifiers())) + return; // Forward up/down to grid - if (key == WXK_UP || key == WXK_DOWN) { + if (event.GetKeyCode() == WXK_UP || event.GetKeyCode() == WXK_DOWN) { grid->GetEventHandler()->ProcessEvent(event); grid->SetFocus(); return; } - // Forward other keys to video display - if (Display) { - Display->GetEventHandler()->ProcessEvent(event); - return; - } - event.Skip(); } - - -/// @brief Paint event -/// @param event -/// void VideoSlider::OnPaint(wxPaintEvent &event) { wxPaintDC dc(this); DrawImage(dc); } - - -/// @brief Draw image -/// @param destdc -/// void VideoSlider::DrawImage(wxDC &destdc) { - // Get dimensions int w,h; GetClientSize(&w,&h); @@ -408,7 +225,7 @@ void VideoSlider::DrawImage(wxDC &destdc) { // Draw keyframes int curX; - if (Display && OPT_GET("Video/Slider/Show Keyframes")->GetBool()) { + if (OPT_GET("Video/Slider/Show Keyframes")->GetBool()) { dc.SetPen(wxPen(shad)); for (size_t i=0;i + #include #endif -class VideoDisplay; +#include + +class VideoContext; class SubtitlesGrid; /// DOCME @@ -47,24 +51,26 @@ class SubtitlesGrid; /// /// DOCME class VideoSlider: public wxWindow { - std::vector keyframes; + VideoContext *vc; ///< Video controller + SubtitlesGrid *grid; ///< temp hack; remove this once event forwarding is killed + std::vector keyframes; ///< Currently loaded keyframes + std::vector slots; - /// DOCME - int val; - - /// DOCME - int max; - - /// DOCME - bool locked; + int val; ///< Current frame number + int max; ///< Last frame number + /// Get the frame number for the given x coordinate int GetValueAtX(int x); + /// Get the x-coordinate for a frame number int GetXAtValue(int value); + /// Render the slider void DrawImage(wxDC &dc); - void UpdateImage(); + /// Set the position of the slider void SetValue(int value); + /// Video open event handler void VideoOpened(); + /// Keyframe open even handler void KeyframesChanged(std::vector const& newKeyframes); void OnMouse(wxMouseEvent &event); @@ -74,14 +80,7 @@ class VideoSlider: public wxWindow { void OnEraseBackground(wxEraseEvent &event) {} public: - /// DOCME - VideoDisplay *Display; - - /// DOCME - SubtitlesGrid *grid; - - VideoSlider(wxWindow* parent, wxWindowID id); - ~VideoSlider(); + VideoSlider(wxWindow* parent, agi::Context *c); DECLARE_EVENT_TABLE() };