diff options
Diffstat (limited to 'power_sequencer/operators/grab_closest_handle_or_cut.py')
-rw-r--r-- | power_sequencer/operators/grab_closest_handle_or_cut.py | 110 |
1 files changed, 110 insertions, 0 deletions
diff --git a/power_sequencer/operators/grab_closest_handle_or_cut.py b/power_sequencer/operators/grab_closest_handle_or_cut.py new file mode 100644 index 00000000..7d772d79 --- /dev/null +++ b/power_sequencer/operators/grab_closest_handle_or_cut.py @@ -0,0 +1,110 @@ +# +# Copyright (C) 2016-2019 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors +# +# This file is part of Power Sequencer. +# +# Power Sequencer is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# Power Sequencer is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with Power Sequencer. If +# not, see <https://www.gnu.org/licenses/>. +# +""" +Selects and grabs the strip handle or cut closest to the mouse cursor. +Hover near a cut and use this operator to slide it. +""" +import bpy + +from math import floor + +from .utils.functions import calculate_distance +from .utils.doc import doc_name, doc_idname, doc_brief, doc_description + + +class POWER_SEQUENCER_OT_grab_closest_cut(bpy.types.Operator): + """ + *brief* Grab the handles that form the closest cut + + + Selects and grabs the strip handle or cut closest to the mouse cursor. + Hover near a cut and fire this tool to slide it. + """ + + doc = { + "name": doc_name(__qualname__), + "demo": "", + "description": doc_description(__doc__), + "shortcuts": [ + ( + {"type": "G", "value": "PRESS", "shift": True, "alt": True}, + {}, + "Grab closest handle or cut", + ) + ], + "keymap": "Sequencer", + } + bl_idname = doc_idname(__qualname__) + bl_label = doc["name"] + bl_description = doc_brief(doc["description"]) + bl_options = {"REGISTER", "UNDO"} + + select_linked: bpy.props.BoolProperty( + name="Select Linked", description="Select strips that are linked in time", default=True + ) + + @classmethod + def poll(cls, context): + return context.sequences + + def invoke(self, context, event): + sequencer = bpy.ops.sequencer + + mouse_x, mouse_y = event.mouse_region_x, event.mouse_region_y + frame, channel = self.find_cut_closest_to_mouse(context, mouse_x, mouse_y) + + matching_strips = [ + s + for s in context.sequences + if (abs(s.frame_final_start - frame) <= 1 or abs(s.frame_final_end - frame) <= 1) + ] + if not self.select_linked: + matching_strips = [s for s in matching_strips if s.channel == channel] + sequencer.select_all(action="DESELECT") + for s in matching_strips: + s.select = True + return bpy.ops.power_sequencer.grab_sequence_handles(frame=frame) + + def find_cut_closest_to_mouse(self, context, mouse_x, mouse_y): + """ + Takes the mouse's coordinates in the sequencer area and returns the two + strips who share the cut closest to the mouse. Use it to find the + handle(s) to select with the grab on the fly operator + """ + view2d = context.region.view2d + + closest_cut = (None, None) + distance_to_closest_cut = 1000000.0 + + for s in context.sequences: + channel_offset = s.channel + 0.5 + start_x, start_y = view2d.view_to_region(s.frame_final_start, channel_offset) + end_x, end_y = view2d.view_to_region(s.frame_final_start, channel_offset) + + distance_to_start = calculate_distance(start_x, start_y, mouse_x, mouse_y) + distance_to_end = calculate_distance(end_x, end_y, mouse_x, mouse_y) + + if distance_to_start < distance_to_closest_cut: + closest_cut = (start_x, start_y) + distance_to_closest_cut = distance_to_start + if distance_to_end < distance_to_closest_cut: + closest_cut = (end_x, end_y) + distance_to_closest_cut = distance_to_end + + closest_cut_local_coords = view2d.region_to_view(closest_cut[0], closest_cut[1]) + frame, channel = (round(closest_cut_local_coords[0]), floor(closest_cut_local_coords[1])) + return frame, channel |