Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNathan Lovato <nathan@gdquest.com>2021-01-24 03:17:35 +0300
committerNathan Lovato <nathan@gdquest.com>2021-01-24 03:17:35 +0300
commitcd176b2617bd3ede969c3aa218ee54a79fc69f27 (patch)
treeb4f51b2c46a95f1ebb8a001269cd8ae6f417bf2a /power_sequencer/operators
parentb482ca0078d5640306985f478bb1dec3dbe7219b (diff)
Update power sequencer to v2.0.1
Changelog: https://github.com/GDQuest/blender-power-sequencer/blob/master/CHANGELOG.md#power-sequencer-201 Commit range: https://github.com/GDQuest/blender-power-sequencer/compare/1.5.0...2.0.1
Diffstat (limited to 'power_sequencer/operators')
-rwxr-xr-xpower_sequencer/operators/__init__.py12
-rw-r--r--power_sequencer/operators/channel_offset.py95
-rw-r--r--power_sequencer/operators/cut_strips_under_cursor.py13
-rw-r--r--power_sequencer/operators/delete_direct.py13
-rw-r--r--power_sequencer/operators/expand_to_surrounding_cuts.py35
-rw-r--r--power_sequencer/operators/fade_add.py49
-rw-r--r--power_sequencer/operators/fade_clear.py10
-rw-r--r--power_sequencer/operators/gap_remove.py16
-rw-r--r--power_sequencer/operators/make_hold_frame.py8
-rw-r--r--power_sequencer/operators/scene_create_from_selection.py7
-rw-r--r--power_sequencer/operators/scene_merge_from.py4
-rw-r--r--power_sequencer/operators/select_all_left_or_right.py2
-rw-r--r--power_sequencer/operators/snap.py6
-rw-r--r--power_sequencer/operators/snap_selection.py14
-rw-r--r--power_sequencer/operators/trim_left_or_right_handles.py11
-rw-r--r--power_sequencer/operators/utils/functions.py149
-rw-r--r--power_sequencer/operators/value_offset.py94
17 files changed, 270 insertions, 268 deletions
diff --git a/power_sequencer/operators/__init__.py b/power_sequencer/operators/__init__.py
index 064ba9b3..406c8635 100755
--- a/power_sequencer/operators/__init__.py
+++ b/power_sequencer/operators/__init__.py
@@ -22,17 +22,13 @@ def get_operator_classes():
"""Returns the list of operators in the add-on"""
this_file = os.path.dirname(__file__)
module_files = [
- f
- for f in os.listdir(this_file)
- if f.endswith(".py") and not f.startswith("__init__")
+ f for f in os.listdir(this_file) if f.endswith(".py") and not f.startswith("__init__")
]
module_paths = ["." + os.path.splitext(f)[0] for f in module_files]
classes = []
for path in module_paths:
module = importlib.import_module(path, package="power_sequencer.operators")
- operator_names = [
- entry for entry in dir(module) if entry.startswith("POWER_SEQUENCER_OT")
- ]
+ operator_names = [entry for entry in dir(module) if entry.startswith("POWER_SEQUENCER_OT")]
classes.extend([getattr(module, name) for name in operator_names])
return classes
@@ -41,9 +37,7 @@ doc = {
"sequencer.refresh_all": {
"name": "Refresh All",
"description": "",
- "shortcuts": [
- ({"type": "R", "value": "PRESS", "shift": True}, {}, "Refresh All")
- ],
+ "shortcuts": [({"type": "R", "value": "PRESS", "shift": True}, {}, "Refresh All")],
"demo": "",
"keymap": "Sequencer",
}
diff --git a/power_sequencer/operators/channel_offset.py b/power_sequencer/operators/channel_offset.py
index 35da1b47..48305c4b 100644
--- a/power_sequencer/operators/channel_offset.py
+++ b/power_sequencer/operators/channel_offset.py
@@ -14,17 +14,12 @@
# You should have received a copy of the GNU General Public License along with Power Sequencer. If
# not, see <https://www.gnu.org/licenses/>.
#
-import bpy
from operator import attrgetter
-from .utils.doc import doc_name, doc_idname, doc_brief, doc_description
-from .utils.functions import (
- slice_selection,
- get_frame_range,
- get_channel_range,
- trim_strips,
- find_strips_in_range,
-)
+import bpy
+
+from .utils.doc import doc_brief, doc_description, doc_idname, doc_name
+from .utils.functions import find_strips_in_range, move_selection, trim_strips
class POWER_SEQUENCER_OT_channel_offset(bpy.types.Operator):
@@ -39,7 +34,7 @@ class POWER_SEQUENCER_OT_channel_offset(bpy.types.Operator):
"shortcuts": [
(
{"type": "UP_ARROW", "value": "PRESS", "alt": True},
- {"direction": "up"},
+ {"direction": "up", "trim_target_channel": False},
"Move to Open Channel Above",
),
(
@@ -49,7 +44,7 @@ class POWER_SEQUENCER_OT_channel_offset(bpy.types.Operator):
),
(
{"type": "DOWN_ARROW", "value": "PRESS", "alt": True},
- {"direction": "down"},
+ {"direction": "down", "trim_target_channel": False},
"Move to Open Channel Below",
),
(
@@ -79,35 +74,71 @@ class POWER_SEQUENCER_OT_channel_offset(bpy.types.Operator):
description="Trim strips to make space in the target channel",
default=False,
)
+ keep_selection_offset: bpy.props.BoolProperty(
+ name="Keep selection offset",
+ description="The selected strips preserve their relative positions",
+ default=True,
+ )
@classmethod
def poll(cls, context):
return context.selected_sequences
def execute(self, context):
+
+ max_channel = 32
+ min_channel = 1
+
+ if self.direction == "up":
+ channel_offset = +1
+ limit_channel = max_channel
+ comparison_function = min
+
+ if self.direction == "down":
+ channel_offset = -1
+ limit_channel = min_channel
+ comparison_function = max
+
selection = [s for s in context.selected_sequences if not s.lock]
+
if not selection:
return {"FINISHED"}
- selection_blocks = slice_selection(context, selection)
- for block in selection_blocks:
- sequences = sorted(block, key=attrgetter("channel", "frame_final_start"))
- frame_start, frame_end = get_frame_range(sequences)
- channel_start, channel_end = get_channel_range(sequences)
-
- if self.trim_target_channel:
- to_delete, to_trim = find_strips_in_range(frame_start, frame_end, context.sequences)
- channel_trim = (
- channel_end + 1 if self.direction == "up" else max(1, channel_start - 1)
- )
- to_trim = [s for s in to_trim if s.channel == channel_trim]
- to_delete = [s for s in to_delete if s.channel == channel_trim]
- trim_strips(context, frame_start, frame_end, to_trim, to_delete)
-
- if self.direction == "up":
- for s in reversed(sequences):
- s.channel += 1
- elif self.direction == "down":
- for s in sequences:
- s.channel = max(1, s.channel - 1)
+ sequences = sorted(selection, key=attrgetter("channel", "frame_final_start"))
+ if self.direction == "up":
+ sequences = [s for s in reversed(sequences)]
+
+ head = sequences[0]
+ if not self.keep_selection_offset or (
+ head.channel != limit_channel and self.keep_selection_offset
+ ):
+ for s in sequences:
+ if self.trim_target_channel:
+ channel_trim = s.channel + channel_offset
+ strips_in_trim_channel = [
+ sequence
+ for sequence in context.sequences
+ if (sequence.channel == channel_trim)
+ ]
+ if strips_in_trim_channel:
+ to_delete, to_trim = find_strips_in_range(
+ s.frame_final_start, s.frame_final_end, strips_in_trim_channel
+ )
+ trim_strips(
+ context, s.frame_final_start, s.frame_final_end, to_trim, to_delete
+ )
+
+ if not self.keep_selection_offset:
+ s.channel = comparison_function(limit_channel, s.channel + channel_offset)
+ if s.channel == limit_channel:
+ move_selection(context, [s], 0, 0)
+
+ if self.keep_selection_offset:
+ start_frame = head.frame_final_start
+ x_difference = 0
+ while not head.channel == limit_channel:
+ move_selection(context, sequences, -x_difference, channel_offset)
+ x_difference = head.frame_final_start - start_frame
+ if x_difference == 0:
+ break
return {"FINISHED"}
diff --git a/power_sequencer/operators/cut_strips_under_cursor.py b/power_sequencer/operators/cut_strips_under_cursor.py
index d7e1141a..3b54a17b 100644
--- a/power_sequencer/operators/cut_strips_under_cursor.py
+++ b/power_sequencer/operators/cut_strips_under_cursor.py
@@ -30,9 +30,7 @@ class POWER_SEQUENCER_OT_split_strips_under_cursor(bpy.types.Operator):
"name": doc_name(__qualname__),
"demo": "https://i.imgur.com/ZyEd0jD.gif",
"description": doc_description(__doc__),
- "shortcuts": [
- ({"type": "K", "value": "PRESS"}, {}, "Cut All Strips Under Cursor")
- ],
+ "shortcuts": [({"type": "K", "value": "PRESS"}, {}, "Cut All Strips Under Cursor")],
"keymap": "Sequencer",
}
bl_idname = doc_idname(__qualname__)
@@ -66,10 +64,5 @@ class POWER_SEQUENCER_OT_split_strips_under_cursor(bpy.types.Operator):
deselect = False
if deselect:
bpy.ops.sequencer.select_all(action="DESELECT")
- (
- context.selected_sequences
- or bpy.ops.power_sequencer.select_strips_under_cursor()
- )
- return bpy.ops.sequencer.split(
- frame=context.scene.frame_current, side=self.side
- )
+ (context.selected_sequences or bpy.ops.power_sequencer.select_strips_under_cursor())
+ return bpy.ops.sequencer.split(frame=context.scene.frame_current, side=self.side)
diff --git a/power_sequencer/operators/delete_direct.py b/power_sequencer/operators/delete_direct.py
index b39ae12f..4c6e4d2d 100644
--- a/power_sequencer/operators/delete_direct.py
+++ b/power_sequencer/operators/delete_direct.py
@@ -50,9 +50,7 @@ class POWER_SEQUENCER_OT_delete_direct(bpy.types.Operator):
bl_description = doc_brief(doc["description"])
bl_options = {"REGISTER", "UNDO"}
- is_removing_transitions: bpy.props.BoolProperty(
- name="Remove Transitions", default=False
- )
+ is_removing_transitions: bpy.props.BoolProperty(name="Remove Transitions", default=False)
@classmethod
def poll(cls, context):
@@ -61,17 +59,12 @@ class POWER_SEQUENCER_OT_delete_direct(bpy.types.Operator):
def invoke(self, context, event):
frame, channel = get_mouse_frame_and_channel(context, event)
if not context.selected_sequences:
- bpy.ops.power_sequencer.select_closest_to_mouse(
- frame=frame, channel=channel
- )
+ bpy.ops.power_sequencer.select_closest_to_mouse(frame=frame, channel=channel)
return self.execute(context)
def execute(self, context):
selection = context.selected_sequences
- if (
- self.is_removing_transitions
- and bpy.ops.power_sequencer.transitions_remove.poll()
- ):
+ if self.is_removing_transitions and bpy.ops.power_sequencer.transitions_remove.poll():
bpy.ops.power_sequencer.transitions_remove()
bpy.ops.sequencer.delete()
diff --git a/power_sequencer/operators/expand_to_surrounding_cuts.py b/power_sequencer/operators/expand_to_surrounding_cuts.py
index 1435b23c..53de8bf7 100644
--- a/power_sequencer/operators/expand_to_surrounding_cuts.py
+++ b/power_sequencer/operators/expand_to_surrounding_cuts.py
@@ -33,11 +33,7 @@ class POWER_SEQUENCER_OT_expand_to_surrounding_cuts(bpy.types.Operator):
"demo": "",
"description": doc_description(__doc__),
"shortcuts": [
- (
- {"type": "E", "value": "PRESS", "ctrl": True},
- {},
- "Expand to Surrounding Cuts",
- )
+ ({"type": "E", "value": "PRESS", "ctrl": True}, {}, "Expand to Surrounding Cuts",)
],
"keymap": "Sequencer",
}
@@ -68,37 +64,24 @@ class POWER_SEQUENCER_OT_expand_to_surrounding_cuts(bpy.types.Operator):
sequences_frame_start = min(
sequences, key=lambda s: s.frame_final_start
).frame_final_start
- sequences_frame_end = max(
- sequences, key=lambda s: s.frame_final_end
- ).frame_final_end
+ sequences_frame_end = max(sequences, key=lambda s: s.frame_final_end).frame_final_end
frame_left, frame_right = find_closest_cuts(
context, sequences_frame_start, sequences_frame_end
)
- if (
- sequences_frame_start == frame_left
- and sequences_frame_end == frame_right
- ):
+ if sequences_frame_start == frame_left and sequences_frame_end == frame_right:
continue
- to_extend_left = [
- s for s in sequences if s.frame_final_start == sequences_frame_start
- ]
- to_extend_right = [
- s for s in sequences if s.frame_final_end == sequences_frame_end
- ]
+ to_extend_left = [s for s in sequences if s.frame_final_start == sequences_frame_start]
+ to_extend_right = [s for s in sequences if s.frame_final_end == sequences_frame_end]
for s in to_extend_left:
s.frame_final_start = (
- frame_left
- if frame_left < sequences_frame_start
- else sequences_frame_start
+ frame_left if frame_left < sequences_frame_start else sequences_frame_start
)
for s in to_extend_right:
s.frame_final_end = (
- frame_right
- if frame_right > sequences_frame_end
- else sequences_frame_end
+ frame_right if frame_right > sequences_frame_end else sequences_frame_end
)
return {"FINISHED"}
@@ -110,8 +93,6 @@ def find_closest_cuts(context, frame_min, frame_max):
).frame_final_end
frame_right = min(
context.sequences,
- key=lambda s: s.frame_final_start
- if s.frame_final_start >= frame_max
- else 1000000,
+ key=lambda s: s.frame_final_start if s.frame_final_start >= frame_max else 1000000,
).frame_final_start
return frame_left, frame_right
diff --git a/power_sequencer/operators/fade_add.py b/power_sequencer/operators/fade_add.py
index 1f4d0bfa..dd75c864 100644
--- a/power_sequencer/operators/fade_add.py
+++ b/power_sequencer/operators/fade_add.py
@@ -51,10 +51,7 @@ class POWER_SEQUENCER_OT_fade_add(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
duration_seconds: bpy.props.FloatProperty(
- name="Fade Duration",
- description="Duration of the fade in seconds",
- default=1.0,
- min=0.01,
+ name="Fade Duration", description="Duration of the fade in seconds", default=1.0, min=0.01,
)
type: bpy.props.EnumProperty(
items=[
@@ -98,12 +95,8 @@ class POWER_SEQUENCER_OT_fade_add(bpy.types.Operator):
if s.frame_final_start < context.scene.frame_current < s.frame_final_end
]
- max_duration = min(
- sequences, key=lambda s: s.frame_final_duration
- ).frame_final_duration
- max_duration = (
- floor(max_duration / 2.0) if self.type == "IN_OUT" else max_duration
- )
+ max_duration = min(sequences, key=lambda s: s.frame_final_duration).frame_final_duration
+ max_duration = floor(max_duration / 2.0) if self.type == "IN_OUT" else max_duration
faded_sequences = []
for sequence in sequences:
@@ -113,15 +106,9 @@ class POWER_SEQUENCER_OT_fade_add(bpy.types.Operator):
if not self.is_long_enough(sequence, duration):
continue
- animated_property = (
- "volume" if hasattr(sequence, "volume") else "blend_alpha"
- )
- fade_fcurve = fade_find_or_create_fcurve(
- context, sequence, animated_property
- )
- fades = self.calculate_fades(
- sequence, fade_fcurve, animated_property, duration
- )
+ animated_property = "volume" if hasattr(sequence, "volume") else "blend_alpha"
+ fade_fcurve = fade_find_or_create_fcurve(context, sequence, animated_property)
+ fades = self.calculate_fades(sequence, fade_fcurve, animated_property, duration)
fade_animation_clear(context, fade_fcurve, fades)
fade_animation_create(fade_fcurve, fades)
faded_sequences.append(sequence)
@@ -129,9 +116,7 @@ class POWER_SEQUENCER_OT_fade_add(bpy.types.Operator):
sequence_string = "sequence" if len(faded_sequences) == 1 else "sequences"
self.report(
{"INFO"},
- "Added fade animation to {} {}.".format(
- len(faded_sequences), sequence_string
- ),
+ "Added fade animation to {} {}.".format(len(faded_sequences), sequence_string),
)
return {"FINISHED"}
@@ -232,13 +217,9 @@ class Fade:
if type == "IN":
self.start = Vector((sequence.frame_final_start, 0.0))
- self.end = Vector(
- (sequence.frame_final_start + self.duration, self.max_value)
- )
+ self.end = Vector((sequence.frame_final_start + self.duration, self.max_value))
elif type == "OUT":
- self.start = Vector(
- (sequence.frame_final_end - self.duration, self.max_value)
- )
+ self.start = Vector((sequence.frame_final_end - self.duration, self.max_value))
self.end = Vector((sequence.frame_final_end, 0.0))
def calculate_max_value(self, sequence, fade_fcurve):
@@ -253,15 +234,11 @@ class Fade:
else:
if self.type == "IN":
fade_end = sequence.frame_final_start + self.duration
- keyframes = (
- k for k in fade_fcurve.keyframe_points if k.co[0] >= fade_end
- )
+ keyframes = (k for k in fade_fcurve.keyframe_points if k.co[0] >= fade_end)
if self.type == "OUT":
fade_start = sequence.frame_final_end - self.duration
keyframes = (
- k
- for k in reversed(fade_fcurve.keyframe_points)
- if k.co[0] <= fade_start
+ k for k in reversed(fade_fcurve.keyframe_points) if k.co[0] <= fade_start
)
try:
max_value = next(keyframes).co[1]
@@ -275,6 +252,4 @@ class Fade:
def calculate_duration_frames(context, duration_seconds):
- return round(
- duration_seconds * context.scene.render.fps / context.scene.render.fps_base
- )
+ return round(duration_seconds * context.scene.render.fps / context.scene.render.fps_base)
diff --git a/power_sequencer/operators/fade_clear.py b/power_sequencer/operators/fade_clear.py
index 06b320b0..02516633 100644
--- a/power_sequencer/operators/fade_clear.py
+++ b/power_sequencer/operators/fade_clear.py
@@ -32,11 +32,7 @@ class POWER_SEQUENCER_OT_fade_clear(bpy.types.Operator):
"demo": "",
"description": doc_description(__doc__),
"shortcuts": [
- (
- {"type": "F", "value": "PRESS", "alt": True, "ctrl": True},
- {},
- "Clear Fades",
- )
+ ({"type": "F", "value": "PRESS", "alt": True, "ctrl": True}, {}, "Clear Fades",)
],
"keymap": "Sequencer",
}
@@ -53,9 +49,7 @@ class POWER_SEQUENCER_OT_fade_clear(bpy.types.Operator):
fcurves = context.scene.animation_data.action.fcurves
for sequence in context.selected_sequences:
- animated_property = (
- "volume" if hasattr(sequence, "volume") else "blend_alpha"
- )
+ animated_property = "volume" if hasattr(sequence, "volume") else "blend_alpha"
data_path = sequence.path_from_id() + "." + animated_property
fcurve_map = {
curve.data_path: curve
diff --git a/power_sequencer/operators/gap_remove.py b/power_sequencer/operators/gap_remove.py
index 4a736051..825bfc0d 100644
--- a/power_sequencer/operators/gap_remove.py
+++ b/power_sequencer/operators/gap_remove.py
@@ -71,9 +71,7 @@ class POWER_SEQUENCER_OT_gap_remove(bpy.types.Operator):
else context.sequences
)
sequences = [
- s
- for s in sequences
- if s.frame_final_start >= frame or s.frame_final_end > frame
+ s for s in sequences if s.frame_final_start >= frame or s.frame_final_end > frame
]
sequence_blocks = slice_selection(context, sequences)
if not sequence_blocks:
@@ -100,18 +98,12 @@ class POWER_SEQUENCER_OT_gap_remove(bpy.types.Operator):
Finds and returns the frame at which the gap starts.
Takes a list sequences sorted by frame_final_start.
"""
- strips_start = min(
- sorted_sequences, key=attrgetter("frame_final_start")
- ).frame_final_start
- strips_end = max(
- sorted_sequences, key=attrgetter("frame_final_end")
- ).frame_final_end
+ strips_start = min(sorted_sequences, key=attrgetter("frame_final_start")).frame_final_start
+ strips_end = max(sorted_sequences, key=attrgetter("frame_final_end")).frame_final_end
gap_frame = -1
if strips_start > frame:
- strips_before_frame_start = [
- s for s in context.sequences if s.frame_final_end <= frame
- ]
+ strips_before_frame_start = [s for s in context.sequences if s.frame_final_end <= frame]
frame_target = 0
if strips_before_frame_start:
frame_target = max(
diff --git a/power_sequencer/operators/make_hold_frame.py b/power_sequencer/operators/make_hold_frame.py
index c6893d04..22c2eecf 100644
--- a/power_sequencer/operators/make_hold_frame.py
+++ b/power_sequencer/operators/make_hold_frame.py
@@ -77,9 +77,7 @@ class POWER_SEQUENCER_OT_make_hold_frame(bpy.types.Operator):
try:
next_strip_start = next(
s
- for s in sorted(
- context.sequences, key=operator.attrgetter("frame_final_start")
- )
+ for s in sorted(context.sequences, key=operator.attrgetter("frame_final_start"))
if s.frame_final_start > active.frame_final_end
).frame_final_start
offset = next_strip_start - active.frame_final_end
@@ -90,9 +88,7 @@ class POWER_SEQUENCER_OT_make_hold_frame(bpy.types.Operator):
source_blend_type = active.blend_type
sequencer.split(frame=scene.frame_current, type="SOFT", side="RIGHT")
transform.seq_slide(value=(offset, 0))
- sequencer.split(
- frame=scene.frame_current + offset + 1, type="SOFT", side="LEFT"
- )
+ sequencer.split(frame=scene.frame_current + offset + 1, type="SOFT", side="LEFT")
transform.seq_slide(value=(-offset, 0))
sequencer.meta_make()
diff --git a/power_sequencer/operators/scene_create_from_selection.py b/power_sequencer/operators/scene_create_from_selection.py
index b615dae9..cd774d0d 100644
--- a/power_sequencer/operators/scene_create_from_selection.py
+++ b/power_sequencer/operators/scene_create_from_selection.py
@@ -67,7 +67,6 @@ class POWER_SEQUENCER_OT_scene_create_from_selection(bpy.types.Operator):
context.window.scene.name = context.selected_sequences[0].name
new_scene_name = context.window.scene.name
-
###after full copy also unselected strips are in the sequencer... Delete those strips
bpy.ops.sequencer.select_all(action="INVERT")
bpy.ops.power_sequencer.delete_direct()
@@ -85,8 +84,10 @@ class POWER_SEQUENCER_OT_scene_create_from_selection(bpy.types.Operator):
bpy.ops.power_sequencer.delete_direct()
bpy.ops.sequencer.scene_strip_add(
- frame_start=selection_start_frame, channel=selection_start_channel, scene=new_scene_name
+ frame_start=selection_start_frame,
+ channel=selection_start_channel,
+ scene=new_scene_name,
)
scene_strip = context.selected_sequences[0]
- # scene_strip.use_sequence = True
+ # scene_strip.use_sequence = True
return {"FINISHED"}
diff --git a/power_sequencer/operators/scene_merge_from.py b/power_sequencer/operators/scene_merge_from.py
index 16b2710d..5c3f40b0 100644
--- a/power_sequencer/operators/scene_merge_from.py
+++ b/power_sequencer/operators/scene_merge_from.py
@@ -73,9 +73,7 @@ class POWER_SEQUENCER_OT_merge_from_scene_strip(bpy.types.Operator):
context.window.scene = strip_scene
bpy.ops.scene.delete()
context.window.scene = start_scene
- self.report(
- type={"WARNING"}, message="Merged scenes lose all their animation data."
- )
+ self.report(type={"WARNING"}, message="Merged scenes lose all their animation data.")
return {"FINISHED"}
diff --git a/power_sequencer/operators/select_all_left_or_right.py b/power_sequencer/operators/select_all_left_or_right.py
index b2e08bb8..01e35306 100644
--- a/power_sequencer/operators/select_all_left_or_right.py
+++ b/power_sequencer/operators/select_all_left_or_right.py
@@ -62,4 +62,4 @@ class POWER_SEQUENCER_OT_select_all_left_or_right(bpy.types.Operator):
return context.sequences
def execute(self, context):
- return bpy.ops.sequencer.select_side_of_frame("INVOKE_DEFAULT", side=self.side)
+ return bpy.ops.sequencer.select("INVOKE_DEFAULT", left_right=self.side)
diff --git a/power_sequencer/operators/snap.py b/power_sequencer/operators/snap.py
index 42b029d1..13d5e66e 100644
--- a/power_sequencer/operators/snap.py
+++ b/power_sequencer/operators/snap.py
@@ -32,11 +32,7 @@ class POWER_SEQUENCER_OT_snap(bpy.types.Operator):
"demo": "",
"description": doc_description(__doc__),
"shortcuts": [
- (
- {"type": "S", "value": "PRESS", "shift": True},
- {},
- "Snap sequences to cursor",
- )
+ ({"type": "S", "value": "PRESS", "shift": True}, {}, "Snap sequences to cursor",)
],
"keymap": "Sequencer",
}
diff --git a/power_sequencer/operators/snap_selection.py b/power_sequencer/operators/snap_selection.py
index 5900579b..eb2851b5 100644
--- a/power_sequencer/operators/snap_selection.py
+++ b/power_sequencer/operators/snap_selection.py
@@ -15,7 +15,7 @@
# not, see <https://www.gnu.org/licenses/>.
#
import bpy
-from .utils.functions import get_sequences_under_cursor, apply_time_offset
+from .utils.functions import get_sequences_under_cursor, move_selection
from .utils.doc import doc_name, doc_idname, doc_brief, doc_description
@@ -32,11 +32,7 @@ class POWER_SEQUENCER_OT_snap_selection(bpy.types.Operator):
"demo": "",
"description": doc_description(__doc__),
"shortcuts": [
- (
- {"type": "S", "value": "PRESS", "alt": True},
- {},
- "Snap selection to cursor",
- )
+ ({"type": "S", "value": "PRESS", "alt": True}, {}, "Snap selection to cursor",)
],
"keymap": "Sequencer",
}
@@ -55,9 +51,7 @@ class POWER_SEQUENCER_OT_snap_selection(bpy.types.Operator):
if context.selected_sequences
else get_sequences_under_cursor(context)
)
- frame_first = min(
- sequences, key=lambda s: s.frame_final_start
- ).frame_final_start
+ frame_first = min(sequences, key=lambda s: s.frame_final_start).frame_final_start
time_offset = context.scene.frame_current - frame_first
- apply_time_offset(context, sequences, time_offset)
+ move_selection(context, sequences, time_offset)
return {"FINISHED"}
diff --git a/power_sequencer/operators/trim_left_or_right_handles.py b/power_sequencer/operators/trim_left_or_right_handles.py
index b93bee53..bbbb8bd5 100644
--- a/power_sequencer/operators/trim_left_or_right_handles.py
+++ b/power_sequencer/operators/trim_left_or_right_handles.py
@@ -78,16 +78,9 @@ class POWER_SEQUENCER_OT_trim_left_or_right_handles(bpy.types.Operator):
frame_current = context.scene.frame_current
# Only select sequences under the time cursor
- sequences = (
- context.selected_sequences
- if context.selected_sequences
- else context.sequences
- )
+ sequences = context.selected_sequences if context.selected_sequences else context.sequences
for s in sequences:
- s.select = (
- s.frame_final_start <= frame_current
- and s.frame_final_end >= frame_current
- )
+ s.select = s.frame_final_start <= frame_current and s.frame_final_end >= frame_current
sequences = [s for s in sequences if s.select]
if not sequences:
return {"FINISHED"}
diff --git a/power_sequencer/operators/utils/functions.py b/power_sequencer/operators/utils/functions.py
index 3462b767..5b33067c 100644
--- a/power_sequencer/operators/utils/functions.py
+++ b/power_sequencer/operators/utils/functions.py
@@ -21,6 +21,9 @@ import bpy
from .global_settings import SequenceTypes
+max_channel = 32
+min_channel = 1
+
def calculate_distance(x1, y1, x2, y2):
return sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)
@@ -34,10 +37,8 @@ def find_linked(context, sequences, selected_sequences):
"""
Takes a list of sequences and returns a list of all the sequences
and effects that are linked in time
-
Args:
- - sequences: a list of sequences
-
+ - sequences: a list of sequences
Returns a list of all the linked sequences, but not the sequences passed to the function
"""
start, end = get_frame_range(sequences, selected_sequences)
@@ -99,7 +100,7 @@ def find_sequences_after(context, sequence):
"""
Finds the strips following the sequences passed to the function
Args:
- - Sequences, the sequences to check
+ - Sequences, the sequences to check
Returns all the strips after the sequence in the current context
"""
return [s for s in context.sequences if s.frame_final_start > sequence.frame_final_start]
@@ -133,12 +134,10 @@ def find_snap_candidate(context, frame=0):
def find_strips_mouse(context, frame, channel, select_linked=False):
"""
Finds a list of sequences to select based on the frame and channel the mouse cursor is at
-
Args:
- - frame: the frame the mouse or cursor is on
- - channel: the channel the mouse is hovering
- - select_linked: find and append the sequences linked in time if True
-
+ - frame: the frame the mouse or cursor is on
+ - channel: the channel the mouse is hovering
+ - select_linked: find and append the sequences linked in time if True
Returns the sequence(s) under the mouse cursor as a list
Returns an empty list if nothing found
"""
@@ -201,10 +200,9 @@ def get_mouse_frame_and_channel(context, event):
def is_in_range(context, sequence, start, end):
"""
Checks if a single sequence's start or end is in the range
-
Args:
- - sequence: the sequence to check for
- - start, end: the start and end frames
+ - sequence: the sequence to check for
+ - start, end: the start and end frames
Returns True if the sequence is within the range, False otherwise
"""
s_start = sequence.frame_final_start
@@ -225,47 +223,32 @@ def set_preview_range(context, start, end):
scene.frame_preview_end = end
-def slice_selection(context, sequences):
+def slice_selection(context, sequences, range_block=0):
"""
Takes a list of sequences and breaks it down
into multiple lists of connected sequences
-
Returns a list of lists of sequences,
each list corresponding to a block of sequences
that are connected in time and sorted by frame_final_start
"""
- # Find when 2 sequences are not connected in time
if not sequences:
return []
- break_ids = [0]
+ # Indicates the index number of the lists from the "broken_selection" list
+ index = -1
+ block_end = 0
+ broken_selection = []
sorted_sequences = sorted(sequences, key=attrgetter("frame_final_start"))
- last_sequence = sorted_sequences[0]
- last_biggest_frame_end = last_sequence.frame_final_end
- index = 0
+
for s in sorted_sequences:
- if s.frame_final_start > last_biggest_frame_end + 1:
- break_ids.append(index)
- last_biggest_frame_end = max(last_biggest_frame_end, s.frame_final_end)
- last_sequence = s
- index += 1
-
- # Create lists
- break_ids.append(len(sorted_sequences))
- cuts_count = len(break_ids) - 1
- broken_selection = []
- index = 0
- while index < cuts_count:
- temp_list = []
- index_range = range(break_ids[index], break_ids[index + 1] - 1)
- if len(index_range) == 0:
- temp_list.append(sorted_sequences[break_ids[index]])
- else:
- for counter in range(break_ids[index], break_ids[index + 1]):
- temp_list.append(sorted_sequences[counter])
- if temp_list:
- broken_selection.append(temp_list)
- index += 1
+ if not broken_selection or (block_end + 1 + range_block < s.frame_final_start):
+ broken_selection.append([s])
+ block_end = s.frame_final_end
+ index += 1
+ continue
+ block_end = max(block_end, s.frame_final_end)
+ broken_selection[index].append(s)
+
return broken_selection
@@ -278,8 +261,10 @@ def trim_strips(context, frame_start, frame_end, to_trim, to_delete=[]):
trim_end = max(frame_start, frame_end)
to_trim = [s for s in to_trim if s.type in SequenceTypes.CUTABLE]
+ initial_selection = context.selected_sequences
for s in to_trim:
+ strips_in_target_channel = []
# Cut strip longer than the trim range in three
is_strip_longer_than_trim_range = (
s.frame_final_start < trim_start and s.frame_final_end > trim_end
@@ -290,6 +275,13 @@ def trim_strips(context, frame_start, frame_end, to_trim, to_delete=[]):
bpy.ops.sequencer.split(frame=trim_start, type="SOFT", side="RIGHT")
bpy.ops.sequencer.split(frame=trim_end, type="SOFT", side="LEFT")
to_delete.append(context.selected_sequences[0])
+
+ for c in context.sequences:
+ if c.channel == s.channel:
+ strips_in_target_channel.append(c)
+
+ if s in initial_selection:
+ initial_selection.append(strips_in_target_channel[0])
continue
# Resize strips that overlap the trim range
@@ -298,20 +290,12 @@ def trim_strips(context, frame_start, frame_end, to_trim, to_delete=[]):
elif s.frame_final_end > trim_start and s.frame_final_start < trim_start:
s.frame_final_end = trim_start
- delete_strips(to_delete)
- return {"FINISHED"}
-
-
-def delete_strips(to_delete):
- """
- Deletes the list of sequences `to_delete`
- """
- if not to_delete:
- return
- bpy.ops.sequencer.select_all(action="DESELECT")
for s in to_delete:
+ bpy.context.sequences.remove(s)
+
+ for s in initial_selection:
s.select = True
- bpy.ops.sequencer.delete()
+ return {"FINISHED"}
def find_closest_surrounding_cuts(context, frame):
@@ -378,30 +362,7 @@ def ripple_move(context, sequences, duration_frames, delete=False):
else:
to_ripple = set(to_ripple + sequences)
- # Use the built-in seq_slide operator to move strips, for best performances
- initial_selection = context.selected_sequences
- bpy.ops.sequencer.select_all(action="DESELECT")
- for s in to_ripple:
- s.select = True
- bpy.ops.transform.seq_slide(value=(duration_frames, 0))
- bpy.ops.sequencer.select_all(action="DESELECT")
- for s in initial_selection:
- s.select = True
-
-
-def apply_time_offset(context, sequences=[], offset=0):
- """Offsets a list of sequences in time using bpy.ops.transform.seq_slide. Mutates and restores the
- user's selection. Use this function to ensure maximum performances and avoid having to figure
- out the logic to move strips in the right order.
- """
- selection = context.selected_sequences
- bpy.ops.sequencer.select_all(action="DESELECT")
- for s in sequences:
- s.select = True
- bpy.ops.transform.seq_slide(value=(offset, 0))
- bpy.ops.sequencer.select_all(action="DESELECT")
- for s in selection:
- s.select = True
+ move_selection(context, to_ripple, duration_frames, 0)
def find_strips_in_range(frame_start, frame_end, sequences, find_overlapping=True):
@@ -409,18 +370,18 @@ def find_strips_in_range(frame_start, frame_end, sequences, find_overlapping=Tru
Returns a tuple of two lists: (strips_inside_range, strips_overlapping_range)
strips_inside_range are strips entirely contained in the frame range.
strips_overlapping_range are strips that only overlap the frame range.
-
Args:
- - frame_start, the start of the frame range
- - frame_end, the end of the frame range
- - sequences (optional): only work with these sequences.
- If it doesn't receive any, the function works with all the sequences in the current context
- - find_overlapping (optional): find and return a list of strips that overlap the
+ - frame_start, the start of the frame range
+ - frame_end, the end of the frame range
+ - sequences (optional): only work with these sequences.
+ If it doesn't receive any, the function works with all the sequences in the current context
+ - find_overlapping (optional): find and return a list of strips that overlap the
frame range
-
"""
strips_inside_range = []
strips_overlapping_range = []
+ if not sequences:
+ sequences = bpy.context.sequences
for s in sequences:
if (
frame_start <= s.frame_final_start <= frame_end
@@ -429,8 +390,8 @@ def find_strips_in_range(frame_start, frame_end, sequences, find_overlapping=Tru
strips_inside_range.append(s)
elif find_overlapping:
if (
- frame_start <= s.frame_final_end <= frame_end
- or frame_start <= s.frame_final_start <= frame_end
+ frame_start < s.frame_final_end <= frame_end
+ or frame_start <= s.frame_final_start < frame_end
):
strips_overlapping_range.append(s)
@@ -438,3 +399,19 @@ def find_strips_in_range(frame_start, frame_end, sequences, find_overlapping=Tru
if s.frame_final_start < frame_start and s.frame_final_end > frame_end:
strips_overlapping_range.append(s)
return strips_inside_range, strips_overlapping_range
+
+
+def move_selection(context, sequences, frame_offset, channel_offset=0):
+ """Offsets the selected `sequences` horizontally and vertically and preserves
+ the current selected sequences.
+ """
+ if not sequences:
+ return
+ initial_selection = context.selected_sequences
+ bpy.ops.sequencer.select_all(action="DESELECT")
+ for s in sequences:
+ s.select = True
+ bpy.ops.transform.seq_slide(value=(frame_offset, channel_offset))
+ bpy.ops.sequencer.select_all(action="DESELECT")
+ for s in initial_selection:
+ s.select = True
diff --git a/power_sequencer/operators/value_offset.py b/power_sequencer/operators/value_offset.py
new file mode 100644
index 00000000..debd2ef6
--- /dev/null
+++ b/power_sequencer/operators/value_offset.py
@@ -0,0 +1,94 @@
+#
+# Copyright (C) 2016-2020 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/>.
+#
+import bpy
+
+from .utils.doc import doc_brief, doc_description, doc_idname, doc_name
+from .utils.functions import convert_duration_to_frames
+
+
+class POWER_SEQUENCER_OT_value_offset(bpy.types.Operator):
+ """Instantly offset selected strips, either using frames or seconds. Allows to
+ nudge the selection quickly, using keyboard shortcuts.
+ """
+
+ doc = {
+ "name": doc_name(__qualname__),
+ "demo": "",
+ "description": doc_description(__doc__),
+ "shortcuts": [
+ (
+ {"type": "LEFT_ARROW", "value": "PRESS", "shift": True, "alt": True},
+ {"direction": "left"},
+ "Offset the selection to the left.",
+ ),
+ (
+ {"type": "RIGHT_ARROW", "value": "PRESS", "shift": True, "alt": True},
+ {"direction": "right"},
+ "Offset the selection to the right.",
+ ),
+ ],
+ "keymap": "Sequencer",
+ }
+ bl_idname = doc_idname(__qualname__)
+ bl_label = doc["name"]
+ bl_description = doc_brief(doc["description"])
+ bl_options = {"REGISTER", "UNDO"}
+
+ direction: bpy.props.EnumProperty(
+ items=[
+ ("left", "left", "Move the selection to the left"),
+ ("right", "right", "Move the selection to the right"),
+ ],
+ name="Direction",
+ description="Move the selection given frames or seconds",
+ default="right",
+ options={"HIDDEN"},
+ )
+ value_type: bpy.props.EnumProperty(
+ items=[
+ ("seconds", "Seconds", "Move with the value as seconds"),
+ ("frames", "Frames", "Move with the value as frames"),
+ ],
+ name="Value Type",
+ description="Toggle between offset in frames or seconds",
+ default="seconds",
+ )
+ offset: bpy.props.FloatProperty(
+ name="Offset",
+ description="Offset amount to apply",
+ default=1.0,
+ step=5,
+ precision=3,
+ )
+
+ @classmethod
+ def poll(cls, context):
+ return context.selected_sequences
+
+ def invoke(self, context, event):
+ self.offset = abs(self.offset)
+ if self.direction == "left":
+ self.offset *= -1.0
+ return self.execute(context)
+
+ def execute(self, context):
+ offset_frames = (
+ convert_duration_to_frames(context, self.offset)
+ if self.value_type == "seconds"
+ else self.offset
+ )
+ return bpy.ops.transform.seq_slide(value=(offset_frames, 0))