Merge branch 'blender-v4.0-release'
[blender-addons.git] / power_sequencer / operators / channel_offset.py
blob3c9b46af5389bbed104ae8f933907e0e745948b8
1 # SPDX-FileCopyrightText: 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
3 # SPDX-License-Identifier: GPL-3.0-or-later
5 from operator import attrgetter
7 import bpy
9 from .utils.doc import doc_brief, doc_description, doc_idname, doc_name
10 from .utils.functions import find_strips_in_range, move_selection, trim_strips
13 class POWER_SEQUENCER_OT_channel_offset(bpy.types.Operator):
14 """
15 Move selected strip to the nearest open channel above/down
16 """
18 doc = {
19 "name": doc_name(__qualname__),
20 "demo": "",
21 "description": doc_description(__doc__),
22 "shortcuts": [
24 {"type": "UP_ARROW", "value": "PRESS", "alt": True},
25 {"direction": "up", "trim_target_channel": False},
26 "Move to Open Channel Above",
29 {"type": "UP_ARROW", "value": "PRESS", "ctrl": True, "alt": True},
30 {"direction": "up", "trim_target_channel": True},
31 "Move to Channel Above and Trim",
34 {"type": "DOWN_ARROW", "value": "PRESS", "alt": True},
35 {"direction": "down", "trim_target_channel": False},
36 "Move to Open Channel Below",
39 {"type": "DOWN_ARROW", "value": "PRESS", "ctrl": True, "alt": True},
40 {"direction": "down", "trim_target_channel": True},
41 "Move to Channel Below and Trim",
44 "keymap": "Sequencer",
46 bl_idname = doc_idname(__qualname__)
47 bl_label = doc["name"]
48 bl_description = doc_brief(doc["description"])
49 bl_options = {"REGISTER", "UNDO"}
51 direction: bpy.props.EnumProperty(
52 items=[
53 ("up", "up", "Move the selection 1 channel up"),
54 ("down", "down", "Move the selection 1 channel down"),
56 name="Direction",
57 description="Move the sequences up or down",
58 default="up",
60 trim_target_channel: bpy.props.BoolProperty(
61 name="Trim strips",
62 description="Trim strips to make space in the target channel",
63 default=False,
65 keep_selection_offset: bpy.props.BoolProperty(
66 name="Keep selection offset",
67 description="The selected strips preserve their relative positions",
68 default=True,
71 @classmethod
72 def poll(cls, context):
73 return context.selected_sequences
75 def execute(self, context):
77 max_channel = 32
78 min_channel = 1
80 if self.direction == "up":
81 channel_offset = +1
82 limit_channel = max_channel
83 comparison_function = min
85 if self.direction == "down":
86 channel_offset = -1
87 limit_channel = min_channel
88 comparison_function = max
90 selection = [s for s in context.selected_sequences if not s.lock]
92 if not selection:
93 return {"FINISHED"}
95 sequences = sorted(selection, key=attrgetter("channel", "frame_final_start"))
96 if self.direction == "up":
97 sequences = [s for s in reversed(sequences)]
99 head = sequences[0]
100 if not self.keep_selection_offset or (
101 head.channel != limit_channel and self.keep_selection_offset
103 for s in sequences:
104 if self.trim_target_channel:
105 channel_trim = s.channel + channel_offset
106 strips_in_trim_channel = [
107 sequence
108 for sequence in context.sequences
109 if (sequence.channel == channel_trim)
111 if strips_in_trim_channel:
112 to_delete, to_trim = find_strips_in_range(
113 s.frame_final_start, s.frame_final_end, strips_in_trim_channel
115 trim_strips(
116 context, s.frame_final_start, s.frame_final_end, to_trim, to_delete
119 s.channel = comparison_function(limit_channel, s.channel + channel_offset)
120 if s.channel == limit_channel:
121 move_selection(context, [s], 0, 0)
123 if self.keep_selection_offset and not self.trim_target_channel:
124 start_frame = head.frame_final_start
125 x_difference = 0
126 while not head.channel == limit_channel:
127 move_selection(context, sequences, -x_difference, channel_offset)
128 x_difference = head.frame_final_start - start_frame
129 if x_difference == 0:
130 break
131 return {"FINISHED"}