1 # SPDX-FileCopyrightText: 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
3 # SPDX-License-Identifier: GPL-3.0-or-later
6 from operator
import attrgetter
8 from .utils
.global_settings
import SequenceTypes
9 from .utils
.functions
import (
11 get_mouse_frame_and_channel
,
14 from .utils
.doc
import doc_name
, doc_idname
, doc_brief
, doc_description
17 def find_sequences_before(context
, strip
):
19 Returns a list of sequences that are before the strip in the current context
21 return [s
for s
in context
.sequences
if s
.frame_final_end
<= strip
.frame_final_start
]
24 class POWER_SEQUENCER_OT_concatenate_strips(bpy
.types
.Operator
):
26 *brief* Remove space between strips
28 Concatenates selected strips in a channel, i.e. removes the gap between them. If a single
29 strip is selected, either the next strip in the channel will be concatenated, or all
30 strips in the channel will be concatenated depending on which shortcut is used.
34 "name": doc_name(__qualname__
),
35 "demo": "https://i.imgur.com/YyEL8YP.gif",
36 "description": doc_description(__doc__
),
39 {"type": "C", "value": "PRESS"},
40 {"concatenate_all": False, "is_towards_left": True},
41 ("Concatenate and select the next strip in the channel"),
44 {"type": "C", "value": "PRESS", "shift": True},
45 {"concatenate_all": True, "is_towards_left": True},
46 "Concatenate all strips in selected channels",
49 {"type": "C", "value": "PRESS", "alt": True},
50 {"concatenate_all": False, "is_towards_left": False},
51 ("Concatenate and select the previous strip in the channel towards the right"),
54 {"type": "C", "value": "PRESS", "shift": True, "alt": True},
55 {"concatenate_all": True, "is_towards_left": False},
56 "Shift Alt C; Concatenate all strips in channel towards the right",
59 "keymap": "Sequencer",
61 bl_idname
= doc_idname(__qualname__
)
62 bl_label
= doc
["name"]
63 bl_description
= doc_brief(doc
["description"])
64 bl_options
= {"REGISTER", "UNDO"}
66 concatenate_all
: bpy
.props
.BoolProperty(
67 name
="Concatenate all strips in channel",
68 description
=("If only one strip selected, concatenate" " the entire channel"),
71 is_towards_left
: bpy
.props
.BoolProperty(
73 description
="Concatenate strips moving them back in time (default) or forward in time",
76 do_ripple
: bpy
.props
.BoolProperty(
78 description
="Ripple the time offset to strips after the concatenated one",
83 def poll(cls
, context
):
84 return context
.sequences
86 def invoke(self
, context
, event
):
87 if not context
.selected_sequences
:
88 frame
, channel
= get_mouse_frame_and_channel(context
, event
)
89 bpy
.ops
.power_sequencer
.select_closest_to_mouse(frame
=frame
, channel
=channel
)
90 return self
.execute(context
)
92 def execute(self
, context
):
93 selection
= context
.selected_sequences
94 channels
= {s
.channel
for s
in selection
}
96 is_one_strip_per_channel
= len(selection
) == len(channels
)
97 if is_one_strip_per_channel
:
100 find_sequences_before(context
, s
)
101 if not self
.is_towards_left
102 else find_sequences_after(context
, s
)
106 for strip
in candidates
107 if strip
.channel
== s
.channel
109 and strip
.type in SequenceTypes
.CUTABLE
111 if not to_concatenate
:
113 self
.concatenate(context
, s
, to_concatenate
)
116 for channel
in channels
:
117 to_concatenate
= [s
for s
in selection
if s
.channel
== channel
]
119 min(to_concatenate
, key
=lambda s
: s
.frame_final_start
)
120 if self
.is_towards_left
121 else max(to_concatenate
, key
=lambda s
: s
.frame_final_start
)
123 to_concatenate
.remove(strip_target
)
124 self
.concatenate(context
, strip_target
, to_concatenate
, force_all
=True)
128 def concatenate(self
, context
, strip_target
, sequences
, force_all
=False):
129 to_concatenate
= sorted(sequences
, key
=attrgetter("frame_final_start"))
131 list(reversed(to_concatenate
)) if not self
.is_towards_left
else to_concatenate
134 [to_concatenate
[0]] if not (self
.concatenate_all
or force_all
) else to_concatenate
137 attribute_target
= "frame_final_end" if self
.is_towards_left
else "frame_final_start"
138 attribute_concat
= "frame_final_start" if self
.is_towards_left
else "frame_final_end"
139 concatenate_start
= getattr(strip_target
, attribute_target
)
141 for s
in to_concatenate
:
142 if isinstance(s
, bpy
.types
.EffectSequence
):
143 concatenate_start
= (
144 s
.frame_final_end
- last_gap
145 if self
.is_towards_left
146 else s
.frame_final_start
- last_gap
149 concat_strip_frame
= getattr(s
, attribute_concat
)
150 gap
= concat_strip_frame
- concatenate_start
151 if self
.do_ripple
and self
.is_towards_left
:
152 ripple_move(context
, [s
], -gap
)
155 concatenate_start
= s
.frame_final_end
if self
.is_towards_left
else s
.frame_final_start
158 if not (self
.concatenate_all
or force_all
):
159 strip_target
.select
= False
160 to_concatenate
[0].select
= True