UI: Sequencer menus
[blender.git] / release / scripts / startup / bl_ui / space_sequencer.py
1 # ##### BEGIN GPL LICENSE BLOCK #####
2 #
3 #  This program is free software; you can redistribute it and/or
4 #  modify it under the terms of the GNU General Public License
5 #  as published by the Free Software Foundation; either version 2
6 #  of the License, or (at your option) any later version.
7 #
8 #  This program is distributed in the hope that it will be useful,
9 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
10 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 #  GNU General Public License for more details.
12 #
13 #  You should have received a copy of the GNU General Public License
14 #  along with this program; if not, write to the Free Software Foundation,
15 #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 #
17 # ##### END GPL LICENSE BLOCK #####
18
19 # <pep8 compliant>
20 import bpy
21 from bpy.types import Header, Menu, Panel
22 from bpy.app.translations import contexts as i18n_contexts
23 from rna_prop_ui import PropertyPanel
24 from .properties_grease_pencil_common import (
25     AnnotationDataPanel,
26     AnnotationOnionSkin,
27     GreasePencilToolsPanel,
28 )
29 from bpy.app.translations import pgettext_iface as iface_
30
31
32 def act_strip(context):
33     try:
34         return context.scene.sequence_editor.active_strip
35     except AttributeError:
36         return None
37
38
39 def selected_sequences_len(context):
40     selected_sequences = getattr(context, "selected_sequences", None)
41     if selected_sequences is None:
42         return 0
43     return len(selected_sequences)
44
45
46 def draw_color_balance(layout, color_balance):
47
48     layout.use_property_split = False
49
50     flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
51     col = flow.column()
52
53     box = col.box()
54     split = box.split(factor=0.35)
55     col = split.column(align=True)
56     col.label(text="Lift:")
57     col.separator()
58     col.separator()
59     col.prop(color_balance, "lift", text="")
60     col.prop(color_balance, "invert_lift", text="Invert", icon='ARROW_LEFTRIGHT')
61     split.template_color_picker(color_balance, "lift", value_slider=True, cubic=True)
62
63     col = flow.column()
64
65     box = col.box()
66     split = box.split(factor=0.35)
67     col = split.column(align=True)
68     col.label(text="Gamma:")
69     col.separator()
70     col.separator()
71     col.prop(color_balance, "gamma", text="")
72     col.prop(color_balance, "invert_gamma", text="Invert", icon='ARROW_LEFTRIGHT')
73     split.template_color_picker(color_balance, "gamma", value_slider=True, lock_luminosity=True, cubic=True)
74
75     col = flow.column()
76
77     box = col.box()
78     split = box.split(factor=0.35)
79     col = split.column(align=True)
80     col.label(text="Gain:")
81     col.separator()
82     col.separator()
83     col.prop(color_balance, "gain", text="")
84     col.prop(color_balance, "invert_gain", text="Invert", icon='ARROW_LEFTRIGHT')
85     split.template_color_picker(color_balance, "gain", value_slider=True, lock_luminosity=True, cubic=True)
86
87
88 class SEQUENCER_HT_header(Header):
89     bl_space_type = 'SEQUENCE_EDITOR'
90
91     def draw(self, context):
92         layout = self.layout
93
94         st = context.space_data
95         scene = context.scene
96
97         layout.template_header()
98
99         layout.prop(st, "view_type", text="")
100
101         SEQUENCER_MT_editor_menus.draw_collapsible(context, layout)
102
103         layout.separator_spacer()
104
105         layout.template_running_jobs()
106
107         if st.view_type in {'SEQUENCER', 'SEQUENCER_PREVIEW'}:
108             layout.separator()
109             layout.operator("sequencer.refresh_all", icon='FILE_REFRESH', text="")
110
111         if st.view_type in {'PREVIEW', 'SEQUENCER_PREVIEW'}:
112             layout.prop(st, "display_mode", text="", icon_only=True)
113
114         if st.view_type != 'SEQUENCER':
115             layout.prop(st, "preview_channels", text="", icon_only=True)
116
117         if st.view_type in {'PREVIEW', 'SEQUENCER_PREVIEW'}:
118             gpd = context.gpencil_data
119             tool_settings = context.tool_settings
120
121             # Proportional editing
122             if gpd and gpd.use_stroke_edit_mode:
123                 row = layout.row(align=True)
124                 row.prop(tool_settings, "use_proportional_edit", icon_only=True)
125                 if tool_settings.use_proportional_edit:
126                     row.prop(tool_settings, "proportional_edit_falloff", icon_only=True)
127
128
129 class SEQUENCER_MT_editor_menus(Menu):
130     bl_idname = "SEQUENCER_MT_editor_menus"
131     bl_label = ""
132
133     def draw(self, context):
134         layout = self.layout
135         st = context.space_data
136
137         layout.menu("SEQUENCER_MT_view")
138
139         if st.view_type in {'SEQUENCER', 'SEQUENCER_PREVIEW'}:
140             layout.menu("SEQUENCER_MT_select")
141             layout.menu("SEQUENCER_MT_marker")
142             layout.menu("SEQUENCER_MT_add")
143             layout.menu("SEQUENCER_MT_strip")
144
145
146 class SEQUENCER_MT_view_toggle(Menu):
147     bl_label = "View Type"
148
149     def draw(self, _context):
150         layout = self.layout
151
152         layout.operator("sequencer.view_toggle").type = 'SEQUENCER'
153         layout.operator("sequencer.view_toggle").type = 'PREVIEW'
154         layout.operator("sequencer.view_toggle").type = 'SEQUENCER_PREVIEW'
155
156
157 class SEQUENCER_MT_view_cache(Menu):
158     bl_label = "Cache"
159
160     def draw(self, context):
161         layout = self.layout
162
163         ed = context.scene.sequence_editor
164         layout.prop(ed, "show_cache")
165         layout.separator()
166
167         col = layout.column()
168         col.enabled = ed.show_cache
169
170         col.prop(ed, "show_cache_final_out")
171         col.prop(ed, "show_cache_raw")
172         col.prop(ed, "show_cache_preprocessed")
173         col.prop(ed, "show_cache_composite")
174
175
176 class SEQUENCER_MT_range(Menu):
177     bl_label = "Range"
178
179     def draw(self, context):
180         layout = self.layout
181
182         layout.operator("anim.previewrange_set", text="Set Preview Range")
183         layout.operator("anim.previewrange_clear", text="Clear Preview Range")
184
185         layout.separator()
186
187         layout.operator("anim.start_frame_set", text="Set Start Frame")
188         layout.operator("anim.end_frame_set", text="Set End Frame")
189
190
191 class SEQUENCER_MT_view(Menu):
192     bl_label = "View"
193
194     def draw(self, context):
195         layout = self.layout
196
197         st = context.space_data
198         is_preview = st.view_type in {'PREVIEW', 'SEQUENCER_PREVIEW'}
199         is_sequencer_view = st.view_type in {'SEQUENCER', 'SEQUENCER_PREVIEW'}
200
201         if st.view_type == 'PREVIEW':
202             # Specifying the REGION_PREVIEW context is needed in preview-only
203             # mode, else the lookup for the shortcut will fail in
204             # wm_keymap_item_find_props() (see #32595).
205             layout.operator_context = 'INVOKE_REGION_PREVIEW'
206         layout.prop(st, "show_region_ui")
207         layout.operator_context = 'INVOKE_DEFAULT'
208
209         if st.view_type == 'SEQUENCER':
210             layout.prop(st, "show_backdrop", text="Preview as Backdrop")
211
212         layout.separator()
213
214         if is_sequencer_view:
215             layout.operator_context = 'INVOKE_REGION_WIN'
216             layout.operator("sequencer.view_selected", text="Frame Selected")
217             layout.operator("sequencer.view_all", text="Frame All")
218             layout.operator("view2d.zoom_border", text="Zoom")
219
220             layout.separator()
221
222             layout.operator_context = 'INVOKE_DEFAULT'
223             layout.menu("SEQUENCER_MT_navigation")
224             layout.menu("SEQUENCER_MT_range")
225
226             layout.separator()
227
228         if is_preview:
229             layout.operator_context = 'INVOKE_REGION_PREVIEW'
230             layout.operator("sequencer.view_all_preview", text="Fit Preview in window")
231             layout.operator("view2d.zoom_border", text="Zoom")
232
233             layout.separator()
234
235             ratios = ((1, 8), (1, 4), (1, 2), (1, 1), (2, 1), (4, 1), (8, 1))
236
237             for a, b in ratios:
238                 layout.operator(
239                     "sequencer.view_zoom_ratio",
240                     text=iface_("Zoom %d:%d") % (a, b),
241                     translate=False,
242                 ).ratio = a / b
243
244             layout.separator()
245
246             layout.operator_context = 'INVOKE_DEFAULT'
247
248             # # XXX, invokes in the header view
249             # layout.operator("sequencer.view_ghost_border", text="Overlay Border")
250
251         if is_sequencer_view:
252             layout.prop(st, "show_seconds")
253             layout.prop(st, "show_frame_indicator")
254             layout.prop(st, "show_strip_offset")
255             layout.prop(st, "show_marker_lines")
256
257             layout.separator()
258
259             layout.menu("SEQUENCER_MT_view_cache")
260             layout.prop_menu_enum(st, "waveform_display_type")
261
262         if is_preview:
263             if st.display_mode == 'IMAGE':
264                 layout.prop(st, "show_safe_areas")
265                 layout.prop(st, "show_metadata")
266             elif st.display_mode == 'WAVEFORM':
267                 layout.prop(st, "show_separate_color")
268
269         layout.separator()
270
271         layout.operator("render.opengl", text="Sequence Render Image", icon='RENDER_STILL').sequencer = True
272         props = layout.operator("render.opengl", text="Sequence Render Animation", icon='RENDER_ANIMATION')
273         props.animation = True
274         props.sequencer = True
275
276         layout.separator()
277
278         layout.menu("INFO_MT_area")
279
280
281 class SEQUENCER_MT_select_handle(Menu):
282     bl_label = "Select Handle"
283
284     def draw(self, context):
285         layout = self.layout
286
287         layout.operator("sequencer.select_handles", text="Both").side = 'BOTH'
288         layout.operator("sequencer.select_handles", text="Left").side = 'LEFT'
289         layout.operator("sequencer.select_handles", text="Right").side = 'RIGHT'
290
291
292 class SEQUENCER_MT_select_channel(Menu):
293     bl_label = "Select Channel"
294
295     def draw(self, context):
296         layout = self.layout
297
298         layout.operator("sequencer.select_active_side", text="Left").side = 'LEFT'
299         layout.operator("sequencer.select_active_side", text="Right").side = 'RIGHT'
300
301
302 class SEQUENCER_MT_select_linked(Menu):
303     bl_label = "Select Linked"
304
305     def draw(self, context):
306         layout = self.layout
307
308         layout.operator("sequencer.select_linked", text="All")
309         layout.operator("sequencer.select_less", text="Less")
310         layout.operator("sequencer.select_more", text="More")
311
312
313 class SEQUENCER_MT_select_playhead(Menu):
314     bl_label = "Select Playhead"
315
316     def draw(self, context):
317         layout = self.layout
318
319         props = layout.operator("sequencer.select", text="Left")
320         props.left_right = 'LEFT'
321         props.linked_time = True
322         props = layout.operator("sequencer.select", text="Right")
323         props.left_right = 'RIGHT'
324         props.linked_time = True
325
326
327 class SEQUENCER_MT_select(Menu):
328     bl_label = "Select"
329
330     def draw(self, _context):
331         layout = self.layout
332
333         layout.operator("sequencer.select_all", text="All").action = 'SELECT'
334         layout.operator("sequencer.select_all", text="None").action = 'DESELECT'
335         layout.operator("sequencer.select_all", text="Invert").action = 'INVERT'
336
337         layout.separator()
338
339         layout.operator("sequencer.select_box", text="Box Select")
340
341         layout.separator()
342
343         layout.menu("SEQUENCER_MT_select_playhead", text="Playhead")
344         layout.menu("SEQUENCER_MT_select_handle", text="Handle")
345         layout.menu("SEQUENCER_MT_select_channel", text="Channel")
346         layout.menu("SEQUENCER_MT_select_linked", text="Linked")
347
348         layout.separator()
349         layout.operator_menu_enum("sequencer.select_grouped", "type", text="Grouped")
350
351
352 class SEQUENCER_MT_marker(Menu):
353     bl_label = "Marker"
354
355     def draw(self, context):
356         layout = self.layout
357
358         st = context.space_data
359         is_sequencer_view = st.view_type in {'SEQUENCER', 'SEQUENCER_PREVIEW'}
360
361         from .space_time import marker_menu_generic
362         marker_menu_generic(layout, context)
363
364         if is_sequencer_view:
365             layout.prop(st, "use_marker_sync")
366
367
368 class SEQUENCER_MT_change(Menu):
369     bl_label = "Change"
370
371     def draw(self, context):
372         layout = self.layout
373         strip = act_strip(context)
374
375         layout.operator_context = 'INVOKE_REGION_WIN'
376
377         layout.operator_menu_enum("sequencer.change_effect_input", "swap")
378         layout.operator_menu_enum("sequencer.change_effect_type", "type")
379         prop = layout.operator("sequencer.change_path", text="Path/Files")
380
381         if strip:
382             stype = strip.type
383
384             if stype == 'IMAGE':
385                 prop.filter_image = True
386             elif stype == 'MOVIE':
387                 prop.filter_movie = True
388             elif stype == 'SOUND':
389                 prop.filter_sound = True
390
391
392 class SEQUENCER_MT_navigation(Menu):
393     bl_label = "Navigation"
394
395     def draw(self, context):
396         layout = self.layout
397
398         layout.operator("screen.animation_play")
399
400         layout.separator()
401
402         layout.operator("sequencer.view_frame", text="Go to Playhead")
403
404         layout.separator()
405
406         props = layout.operator("sequencer.strip_jump", text="Jump to Previous Strip")
407         props.next = False
408         props.center = False
409         props = layout.operator("sequencer.strip_jump", text="Jump to Next Strip")
410         props.next = True
411         props.center = False
412
413         layout.separator()
414
415         props = layout.operator("sequencer.strip_jump", text="Jump to Previous Strip (Center)")
416         props.next = False
417         props.center = True
418         props = layout.operator("sequencer.strip_jump", text="Jump to Next Strip (Center)")
419         props.next = True
420         props.center = True
421
422
423 class SEQUENCER_MT_add(Menu):
424     bl_label = "Add"
425     bl_translation_context = i18n_contexts.operator_default
426
427     def draw(self, context):
428
429         layout = self.layout
430         layout.operator_context = 'INVOKE_REGION_WIN'
431
432         bpy_data_scenes_len = len(bpy.data.scenes)
433         if bpy_data_scenes_len > 10:
434             layout.operator_context = 'INVOKE_DEFAULT'
435             layout.operator("sequencer.scene_strip_add", text="Scene...", icon='SCENE_DATA')
436         elif bpy_data_scenes_len > 1:
437             layout.operator_menu_enum("sequencer.scene_strip_add", "scene", text="Scene", icon='SCENE_DATA')
438         else:
439             layout.menu("SEQUENCER_MT_add_empty", text="Scene", icon='SCENE_DATA')
440         del bpy_data_scenes_len
441
442         bpy_data_movieclips_len = len(bpy.data.movieclips)
443         if bpy_data_movieclips_len > 10:
444             layout.operator_context = 'INVOKE_DEFAULT'
445             layout.operator("sequencer.movieclip_strip_add", text="Clip...", icon='TRACKER')
446         elif bpy_data_movieclips_len > 0:
447             layout.operator_menu_enum("sequencer.movieclip_strip_add", "clip", text="Clip", icon='TRACKER')
448         else:
449             layout.menu("SEQUENCER_MT_add_empty", text="Clip", icon='TRACKER')
450         del bpy_data_movieclips_len
451
452         bpy_data_masks_len = len(bpy.data.masks)
453         if bpy_data_masks_len > 10:
454             layout.operator_context = 'INVOKE_DEFAULT'
455             layout.operator("sequencer.mask_strip_add", text="Mask...", icon='MOD_MASK')
456         elif bpy_data_masks_len > 0:
457             layout.operator_menu_enum("sequencer.mask_strip_add", "mask", text="Mask", icon='MOD_MASK')
458         else:
459             layout.menu("SEQUENCER_MT_add_empty", text="Mask", icon='MOD_MASK')
460         del bpy_data_masks_len
461
462         layout.separator()
463
464         layout.operator("sequencer.movie_strip_add", text="Movie", icon='FILE_MOVIE')
465         layout.operator("sequencer.sound_strip_add", text="Sound", icon='FILE_SOUND')
466         layout.operator("sequencer.image_strip_add", text="Image/Sequence", icon='FILE_IMAGE')
467
468         layout.separator()
469
470         layout.operator_context = 'INVOKE_REGION_WIN'
471         layout.operator("sequencer.effect_strip_add", text="Color", icon='COLOR').type = 'COLOR'
472         layout.operator("sequencer.effect_strip_add", text="Text", icon='FONT_DATA').type = 'TEXT'
473
474         layout.separator()
475
476         layout.operator("sequencer.effect_strip_add", text="Adjustment Layer", icon='COLOR').type = 'ADJUSTMENT'
477
478         layout.operator_context = 'INVOKE_DEFAULT'
479         layout.menu("SEQUENCER_MT_add_effect", icon='SHADERFX')
480
481         col = layout.column()
482         col.menu("SEQUENCER_MT_add_transitions", icon='ARROW_LEFTRIGHT')
483         col.enabled = selected_sequences_len(context) >= 2
484
485
486 class SEQUENCER_MT_add_empty(Menu):
487     bl_label = "Empty"
488
489     def draw(self, _context):
490         layout = self.layout
491
492         layout.label(text="No Items Available")
493
494
495 class SEQUENCER_MT_add_transitions(Menu):
496     bl_label = "Transitions"
497
498     def draw(self, context):
499
500         layout = self.layout
501
502         col = layout.column()
503
504         col.operator("sequencer.crossfade_sounds", text="Sound Crossfade")
505
506         col.separator()
507
508         col.operator("sequencer.effect_strip_add", text="Cross").type = 'CROSS'
509         col.operator("sequencer.effect_strip_add", text="Gamma Cross").type = 'GAMMA_CROSS'
510
511         col.separator()
512
513         col.operator("sequencer.effect_strip_add", text="Wipe").type = 'WIPE'
514         col.enabled = selected_sequences_len(context) >= 2
515
516
517 class SEQUENCER_MT_add_effect(Menu):
518     bl_label = "Effect Strip"
519
520     def draw(self, context):
521
522         layout = self.layout
523         layout.operator_context = 'INVOKE_REGION_WIN'
524
525         col = layout.column()
526         col.operator("sequencer.effect_strip_add", text="Add").type = 'ADD'
527         col.operator("sequencer.effect_strip_add", text="Subtract").type = 'SUBTRACT'
528         col.operator("sequencer.effect_strip_add", text="Multiply").type = 'MULTIPLY'
529         col.operator("sequencer.effect_strip_add", text="Over Drop").type = 'OVER_DROP'
530         col.operator("sequencer.effect_strip_add", text="Alpha Over").type = 'ALPHA_OVER'
531         col.operator("sequencer.effect_strip_add", text="Alpha Under").type = 'ALPHA_UNDER'
532         col.operator("sequencer.effect_strip_add", text="Color Mix").type = 'COLORMIX'
533         col.enabled = selected_sequences_len(context) >= 2
534
535         layout.separator()
536
537         layout.operator("sequencer.effect_strip_add", text="Multicam Selector").type = 'MULTICAM'
538
539         layout.separator()
540
541         col = layout.column()
542         col.operator("sequencer.effect_strip_add", text="Transform").type = 'TRANSFORM'
543         col.operator("sequencer.effect_strip_add", text="Speed Control").type = 'SPEED'
544
545         col.separator()
546
547         col.operator("sequencer.effect_strip_add", text="Glow").type = 'GLOW'
548         col.operator("sequencer.effect_strip_add", text="Gaussian Blur").type = 'GAUSSIAN_BLUR'
549         col.enabled = selected_sequences_len(context) != 0
550
551
552 class SEQUENCER_MT_strip_transform(Menu):
553     bl_label = "Transform"
554
555     def draw(self, _context):
556         layout = self.layout
557
558         layout.operator("transform.transform", text="Move").mode = 'TRANSLATION'
559         layout.operator("transform.transform", text="Move/Extend from Playhead").mode = 'TIME_EXTEND'
560         layout.operator("sequencer.slip", text="Slip Strip Contents")
561
562         layout.separator()
563         layout.operator("sequencer.snap")
564         layout.operator("sequencer.offset_clear")
565
566         layout.separator()
567         layout.operator_menu_enum("sequencer.swap", "side")
568
569         layout.separator()
570         layout.operator("sequencer.gap_remove").all = False
571         layout.operator("sequencer.gap_insert")
572
573
574 class SEQUENCER_MT_strip_input(Menu):
575     bl_label = "Inputs"
576
577     def draw(self, context):
578         layout = self.layout
579         strip = act_strip(context)
580
581         layout.operator("sequencer.reload", text="Reload Strips")
582         layout.operator("sequencer.reload", text="Reload Strips and Adjust Length").adjust_length = True
583         prop = layout.operator("sequencer.change_path", text="Change Path/Files")
584         layout.operator("sequencer.swap_data", text="Swap Data")
585
586         if strip:
587             stype = strip.type
588
589             if stype == 'IMAGE':
590                 prop.filter_image = True
591             elif stype == 'MOVIE':
592                 prop.filter_movie = True
593             elif stype == 'SOUND':
594                 prop.filter_sound = True
595
596
597 class SEQUENCER_MT_strip_lock_mute(Menu):
598     bl_label = "Lock/Mute"
599
600     def draw(self, _context):
601         layout = self.layout
602
603         layout.operator("sequencer.lock", icon='LOCKED')
604         layout.operator("sequencer.unlock")
605
606         layout.separator()
607
608         layout.operator("sequencer.mute").unselected = False
609         layout.operator("sequencer.unmute").unselected = False
610         layout.operator("sequencer.mute", text="Mute Unselected Strips").unselected = True
611         layout.operator("sequencer.unmute", text="Unmute Deselected Strips").unselected = True
612
613
614 class SEQUENCER_MT_strip_effect(Menu):
615     bl_label = "Effect Strip"
616
617     def draw(self, context):
618         layout = self.layout
619
620         layout.operator_menu_enum("sequencer.change_effect_input", "swap")
621         layout.operator_menu_enum("sequencer.change_effect_type", "type")
622         layout.operator("sequencer.reassign_inputs")
623         layout.operator("sequencer.swap_inputs")
624
625
626 class SEQUENCER_MT_strip_movie(Menu):
627     bl_label = "Movie Strip"
628
629     def draw(self, context):
630         layout = self.layout
631
632         layout.operator("sequencer.rendersize")
633         layout.operator("sequencer.images_separate")
634         layout.operator("sequencer.deinterlace_selected_movies")
635
636
637 class SEQUENCER_MT_strip(Menu):
638     bl_label = "Strip"
639
640     def draw(self, context):
641         layout = self.layout
642
643         layout.operator_context = 'INVOKE_REGION_WIN'
644
645         layout.separator()
646         layout.menu("SEQUENCER_MT_strip_transform")
647
648         layout.separator()
649         layout.operator("sequencer.cut", text="Cut").type = 'SOFT'
650         layout.operator("sequencer.cut", text="Hold Cut").type = 'HARD'
651
652         layout.separator()
653         layout.operator("sequencer.copy", text="Copy")
654         layout.operator("sequencer.paste", text="Paste")
655         layout.operator("sequencer.duplicate_move")
656         layout.operator("sequencer.delete", text="Delete...")
657
658         layout.separator()
659         layout.menu("SEQUENCER_MT_strip_lock_mute")
660
661         strip = act_strip(context)
662
663         if strip:
664             stype = strip.type
665
666             if stype != 'SOUND':
667                 layout.separator()
668                 layout.operator_menu_enum("sequencer.strip_modifier_add", "type", text="Add Modifier")
669                 layout.operator("sequencer.strip_modifier_copy", text="Copy Modifiers to Selection")
670
671             if stype in {
672                     'CROSS', 'ADD', 'SUBTRACT', 'ALPHA_OVER', 'ALPHA_UNDER',
673                     'GAMMA_CROSS', 'MULTIPLY', 'OVER_DROP', 'WIPE', 'GLOW',
674                     'TRANSFORM', 'COLOR', 'SPEED', 'MULTICAM', 'ADJUSTMENT',
675                     'GAUSSIAN_BLUR', 'TEXT',
676             }:
677                 layout.separator()
678                 layout.menu("SEQUENCER_MT_strip_effect")
679             elif stype in {'MOVIE'}:
680                 layout.separator()
681                 layout.menu("SEQUENCER_MT_strip_movie")
682             elif stype in {'IMAGE'}:
683                 layout.separator()
684                 layout.operator("sequencer.rendersize")
685                 layout.operator("sequencer.images_separate")
686             elif stype == 'META':
687                 layout.separator()
688                 layout.operator("sequencer.meta_make")
689                 layout.operator("sequencer.meta_separate")
690                 layout.operator("sequencer.meta_toggle", text="Toggle Meta")
691             if stype != 'META':
692                 layout.separator()
693                 layout.operator("sequencer.meta_make")
694
695         layout.separator()
696         layout.menu("SEQUENCER_MT_strip_input")
697
698         layout.separator()
699         layout.operator("sequencer.rebuild_proxy")
700
701
702 class SEQUENCER_MT_context_menu(Menu):
703     bl_label = "Sequencer Context Menu"
704
705     def draw(self, context):
706         layout = self.layout
707
708         layout.operator_context = 'INVOKE_REGION_WIN'
709
710         layout.operator("sequencer.cut", text="Cut").type = 'SOFT'
711
712         layout.separator()
713
714         layout.operator("sequencer.copy", text="Copy", icon='COPYDOWN')
715         layout.operator("sequencer.paste", text="Paste", icon='PASTEDOWN')
716         layout.operator("sequencer.duplicate_move")
717         layout.operator("sequencer.delete", text="Delete...")
718
719         layout.separator()
720
721         layout.operator("sequencer.slip", text="Slip Strip Contents")
722         layout.operator("sequencer.snap")
723
724         layout.separator()
725
726         layout.operator("sequencer.gap_remove").all = False
727         layout.operator("sequencer.gap_insert")
728
729         strip = act_strip(context)
730
731         if strip:
732             stype = strip.type
733
734             if stype != 'SOUND':
735
736                 layout.separator()
737                 layout.operator_menu_enum("sequencer.strip_modifier_add", "type", text="Add Modifier")
738                 layout.operator("sequencer.strip_modifier_copy", text="Copy Modifiers to Selection")
739
740                 if selected_sequences_len(context) >= 2:
741                     layout.separator()
742                     col = layout.column()
743                     col.menu("SEQUENCER_MT_add_transitions", text="Add Transition")
744
745             elif selected_sequences_len(context) >= 2:
746                 layout.separator()
747                 layout.operator("sequencer.crossfade_sounds", text="Crossfade Sounds")
748
749             if stype in {
750                     'CROSS', 'ADD', 'SUBTRACT', 'ALPHA_OVER', 'ALPHA_UNDER',
751                     'GAMMA_CROSS', 'MULTIPLY', 'OVER_DROP', 'WIPE', 'GLOW',
752                     'TRANSFORM', 'COLOR', 'SPEED', 'MULTICAM', 'ADJUSTMENT',
753                     'GAUSSIAN_BLUR', 'TEXT',
754             }:
755                 layout.separator()
756                 layout.menu("SEQUENCER_MT_strip_effect")
757             elif stype in {'MOVIE'}:
758                 layout.separator()
759                 layout.menu("SEQUENCER_MT_strip_movie")
760             elif stype in {'IMAGE'}:
761                 layout.separator()
762                 layout.operator("sequencer.rendersize")
763                 layout.operator("sequencer.images_separate")
764             elif stype == 'META':
765                 layout.separator()
766                 layout.operator("sequencer.meta_make")
767                 layout.operator("sequencer.meta_separate")
768                 layout.operator("sequencer.meta_toggle", text="Toggle Meta")
769             if stype != 'META':
770                 layout.separator()
771                 layout.operator("sequencer.meta_make")
772                 layout.operator("sequencer.meta_toggle", text="Toggle Meta")
773
774         layout.separator()
775
776         layout.menu("SEQUENCER_MT_strip_lock_mute")
777
778
779 class SequencerButtonsPanel:
780     bl_space_type = 'SEQUENCE_EDITOR'
781     bl_region_type = 'UI'
782
783     @staticmethod
784     def has_sequencer(context):
785         return (context.space_data.view_type in {'SEQUENCER', 'SEQUENCER_PREVIEW'})
786
787     @classmethod
788     def poll(cls, context):
789         return cls.has_sequencer(context) and (act_strip(context) is not None)
790
791
792 class SequencerButtonsPanel_Output:
793     bl_space_type = 'SEQUENCE_EDITOR'
794     bl_region_type = 'UI'
795
796     @staticmethod
797     def has_preview(context):
798         st = context.space_data
799         return (st.view_type in {'PREVIEW', 'SEQUENCER_PREVIEW'}) or st.show_backdrop
800
801     @classmethod
802     def poll(cls, context):
803         return cls.has_preview(context)
804
805
806 class SEQUENCER_PT_info(SequencerButtonsPanel, Panel):
807     bl_label = "Info"
808     bl_options = {'DEFAULT_CLOSED'}
809     bl_category = "Strip"
810
811     def draw(self, context):
812         layout = self.layout
813         layout.use_property_split = True
814         layout.use_property_decorate = False
815
816         scene = context.scene
817         strip = act_strip(context)
818
819         row = layout.row(align=True)
820         row.prop(strip, "name", text=strip.type.title())
821         row.prop(strip, "lock", toggle=True, icon_only=True)
822
823
824 class SEQUENCER_PT_adjust_offset(SequencerButtonsPanel, Panel):
825     bl_label = "Offset"
826     bl_parent_id = "SEQUENCER_PT_adjust"
827     bl_options = {'DEFAULT_CLOSED'}
828     bl_category = "Strip"
829
830     @classmethod
831     def poll(cls, context):
832         strip = act_strip(context)
833         return strip.type != 'SOUND'
834
835     def draw_header(self, context):
836         strip = act_strip(context)
837         self.layout.prop(strip, "use_translation", text="")
838
839     def draw(self, context):
840         strip = act_strip(context)
841         layout = self.layout
842         layout.use_property_split = True
843         layout.use_property_decorate = False
844
845         if strip.use_translation:
846             col = layout.column(align=True)
847             col.prop(strip.transform, "offset_x", text="Position X")
848             col.prop(strip.transform, "offset_y", text="Y")
849             col.active = strip.use_translation
850         else:
851             layout.separator()
852
853
854 class SEQUENCER_PT_adjust_crop(SequencerButtonsPanel, Panel):
855     bl_label = "Crop"
856     bl_parent_id = "SEQUENCER_PT_adjust"
857     bl_options = {'DEFAULT_CLOSED'}
858     bl_category = "Strip"
859
860     @classmethod
861     def poll(cls, context):
862         strip = act_strip(context)
863         return strip.type != 'SOUND'
864
865     def draw_header(self, context):
866         strip = act_strip(context)
867         self.layout.prop(strip, "use_crop", text="")
868
869     def draw(self, context):
870         strip = act_strip(context)
871         layout = self.layout
872         layout.use_property_split = True
873         layout.use_property_decorate = False
874
875         if strip.use_crop:
876             col = layout.column(align=True)
877             col.prop(strip.crop, "min_x")
878             col.prop(strip.crop, "max_x")
879             col.prop(strip.crop, "max_y")
880             col.prop(strip.crop, "min_y")
881         else:
882             layout.separator()
883
884
885 class SEQUENCER_PT_effect(SequencerButtonsPanel, Panel):
886     bl_label = "Effect Strip"
887     bl_category = "Strip"
888
889     @classmethod
890     def poll(cls, context):
891         if not cls.has_sequencer(context):
892             return False
893
894         strip = act_strip(context)
895         if not strip:
896             return False
897
898         return strip.type in {
899             'ADD', 'SUBTRACT', 'ALPHA_OVER', 'ALPHA_UNDER',
900             'CROSS', 'GAMMA_CROSS', 'MULTIPLY', 'OVER_DROP',
901             'WIPE', 'GLOW', 'TRANSFORM', 'COLOR', 'SPEED',
902             'MULTICAM', 'GAUSSIAN_BLUR', 'TEXT', 'COLORMIX'
903         }
904
905     def draw(self, context):
906         layout = self.layout
907         layout.use_property_split = True
908         layout.use_property_decorate = False
909
910         strip = act_strip(context)
911
912         if strip.input_count > 0:
913             col = layout.column()
914             col.enabled = False
915             col.prop(strip, "input_1")
916             if strip.input_count > 1:
917                 col.prop(strip, "input_2")
918
919         if strip.type == 'COLOR':
920             layout.prop(strip, "color")
921
922         elif strip.type == 'WIPE':
923             col = layout.column()
924             col.prop(strip, "transition_type")
925             col.alignment = 'RIGHT'
926             col.row().prop(strip, "direction", expand=True)
927
928             col = layout.column()
929             col.prop(strip, "blur_width", slider=True)
930             if strip.transition_type in {'SINGLE', 'DOUBLE'}:
931                 col.prop(strip, "angle")
932
933         elif strip.type == 'GLOW':
934             flow = layout.column_flow()
935             flow.prop(strip, "threshold", slider=True)
936             flow.prop(strip, "clamp", slider=True)
937             flow.prop(strip, "boost_factor")
938             flow.prop(strip, "blur_radius")
939
940             row = layout.row()
941             row.prop(strip, "quality", slider=True)
942             row.prop(strip, "use_only_boost")
943
944         elif strip.type == 'SPEED':
945             layout.prop(strip, "use_default_fade", text="Stretch to input strip length")
946             if not strip.use_default_fade:
947                 layout.prop(strip, "use_as_speed")
948                 if strip.use_as_speed:
949                     layout.prop(strip, "speed_factor")
950                 else:
951                     layout.prop(strip, "speed_factor", text="Frame Number")
952                     layout.prop(strip, "use_scale_to_length")
953
954         elif strip.type == 'TRANSFORM':
955             layout = self.layout
956             col = layout.column()
957
958             col.prop(strip, "interpolation")
959             col.prop(strip, "translation_unit")
960             layout = layout.column(align=True)
961             layout.prop(strip, "translate_start_x", text="Position X")
962             layout.prop(strip, "translate_start_y", text="Y")
963
964             layout.separator()
965
966             col = layout.column(align=True)
967             col.prop(strip, "use_uniform_scale")
968             if strip.use_uniform_scale:
969                 col = layout.column(align=True)
970                 col.prop(strip, "scale_start_x", text="Scale")
971             else:
972                 layout.prop(strip, "scale_start_x", text="Scale X")
973                 layout.prop(strip, "scale_start_y", text="Y")
974
975             layout.separator()
976
977             layout.prop(strip, "rotation_start", text="Rotation")
978
979         elif strip.type == 'MULTICAM':
980             col = layout.column(align=True)
981             strip_channel = strip.channel
982
983             col.prop(strip, "multicam_source", text="Source Channel")
984
985             # The multicam strip needs at least 2 strips to be useful
986             if strip_channel > 2:
987                 BT_ROW = 4
988                 col.label(text="    Cut to")
989                 row = col.row()
990
991                 for i in range(1, strip_channel):
992                     if (i % BT_ROW) == 1:
993                         row = col.row(align=True)
994
995                     # Workaround - .enabled has to have a separate UI block to work
996                     if i == strip.multicam_source:
997                         sub = row.row(align=True)
998                         sub.enabled = False
999                         sub.operator("sequencer.cut_multicam", text=f"{i:d}").camera = i
1000                     else:
1001                         sub_1 = row.row(align=True)
1002                         sub_1.enabled = True
1003                         sub_1.operator("sequencer.cut_multicam", text=f"{i:d}").camera = i
1004
1005                 if strip.channel > BT_ROW and (strip_channel - 1) % BT_ROW:
1006                     for i in range(strip.channel, strip_channel + ((BT_ROW + 1 - strip_channel) % BT_ROW)):
1007                         row.label(text="")
1008             else:
1009                 col.separator()
1010                 col.label(text="Two or more channels are needed below this strip", icon='INFO')
1011
1012         elif strip.type == 'TEXT':
1013             col = layout.column()
1014             col.prop(strip, "text")
1015             col.template_ID(strip, "font", open="font.open", unlink="font.unlink")
1016             col.prop(strip, "font_size")
1017
1018             row = col.row()
1019             row.prop(strip, "color")
1020             row = col.row()
1021             row.prop(strip, "use_shadow")
1022             rowsub = row.row()
1023             rowsub.active = strip.use_shadow
1024             rowsub.prop(strip, "shadow_color", text="")
1025
1026             col.prop(strip, "align_x")
1027             col.prop(strip, "align_y", text="Y")
1028             row = col.row(align=True)
1029             row.prop(strip, "location", text="Location")
1030             col.prop(strip, "wrap_width")
1031
1032             layout.operator("sequencer.export_subtitles", text="Export Subtitles", icon='EXPORT')
1033
1034         col = layout.column(align=True)
1035         if strip.type == 'SPEED':
1036             col.prop(strip, "multiply_speed")
1037         elif strip.type in {'CROSS', 'GAMMA_CROSS', 'WIPE', 'ALPHA_OVER', 'ALPHA_UNDER', 'OVER_DROP'}:
1038             col.prop(strip, "use_default_fade", text="Default fade")
1039             if not strip.use_default_fade:
1040                 col.prop(strip, "effect_fader", text="Effect Fader")
1041         elif strip.type == 'GAUSSIAN_BLUR':
1042             layout = layout.column(align=True)
1043             layout.prop(strip, "size_x", text="Size X")
1044             layout.prop(strip, "size_y", text="Y")
1045         elif strip.type == 'COLORMIX':
1046             layout.prop(strip, "blend_effect", text="Blend Mode")
1047             row = layout.row(align=True)
1048             row.prop(strip, "factor", slider=True)
1049
1050
1051 class SEQUENCER_PT_info_input(SequencerButtonsPanel, Panel):
1052     bl_label = "Input"
1053     bl_parent_id = "SEQUENCER_PT_info"
1054     bl_options = {'DEFAULT_CLOSED'}
1055     bl_category = "Strip"
1056
1057     @classmethod
1058     def poll(cls, context):
1059         if not cls.has_sequencer(context):
1060             return False
1061
1062         strip = act_strip(context)
1063         if not strip:
1064             return False
1065
1066         return strip.type in {'MOVIE', 'IMAGE', 'SOUND'}
1067
1068         ''', 'SCENE', 'MOVIECLIP', 'META',
1069         'ADD', 'SUBTRACT', 'ALPHA_OVER', 'ALPHA_UNDER',
1070         'CROSS', 'GAMMA_CROSS', 'MULTIPLY', 'OVER_DROP',
1071         'WIPE', 'GLOW', 'TRANSFORM', 'COLOR',
1072         'MULTICAM', 'SPEED', 'ADJUSTMENT', 'COLORMIX' }'''
1073
1074     def draw(self, context):
1075         layout = self.layout
1076         layout.use_property_split = True
1077         layout.use_property_decorate = False
1078
1079         st = context.space_data
1080         scene = context.scene
1081         strip = act_strip(context)
1082         seq_type = strip.type
1083
1084         # draw a filename if we have one
1085         if seq_type == 'IMAGE':
1086             layout.prop(strip, "directory", text="")
1087
1088             # Current element for the filename
1089
1090             elem = strip.strip_elem_from_frame(scene.frame_current)
1091             if elem:
1092                 layout.prop(elem, "filename", text="")  # strip.elements[0] could be a fallback
1093
1094             layout.prop(strip.colorspace_settings, "name", text="Color Space")
1095
1096             layout.prop(strip, "alpha_mode", text="Alpha")
1097             sub = layout.column(align=True)
1098             sub.operator("sequencer.change_path", text="Change Data/Files", icon='FILEBROWSER').filter_image = True
1099
1100         elif seq_type == 'MOVIE':
1101             layout.prop(strip, "filepath", text="")
1102
1103             layout.prop(strip.colorspace_settings, "name", text="Color Space")
1104
1105             layout.prop(strip, "mpeg_preseek")
1106             layout.prop(strip, "stream_index")
1107
1108         elif seq_type == 'SOUND':
1109             sound = strip.sound
1110             layout.template_ID(strip, "sound", open="sound.open")
1111             if sound is not None:
1112                 layout.prop(sound, "filepath", text="")
1113
1114                 layout.use_property_split = True
1115                 layout.use_property_decorate = False
1116
1117                 layout.alignment = 'RIGHT'
1118                 sub = layout.column(align=True)
1119                 split = sub.split(factor=0.5, align=True)
1120                 split.alignment = 'RIGHT'
1121                 if sound.packed_file:
1122                     split.label(text="Unpack")
1123                     split.operator("sound.unpack", icon='PACKAGE', text="")
1124                 else:
1125                     split.label(text="Pack")
1126                     split.operator("sound.pack", icon='UGLYPACKAGE', text="")
1127
1128                 layout.prop(sound, "use_memory_cache")
1129
1130         if scene.render.use_multiview and seq_type in {'IMAGE', 'MOVIE'}:
1131             layout.prop(strip, "use_multiview")
1132
1133             col = layout.column()
1134             col.active = strip.use_multiview
1135
1136             col.row().prop(strip, "views_format", expand=True)
1137
1138             box = col.box()
1139             box.active = strip.views_format == 'STEREO_3D'
1140             box.template_image_stereo_3d(strip.stereo_3d_format)
1141
1142
1143 class SEQUENCER_PT_sound(SequencerButtonsPanel, Panel):
1144     bl_label = "Sound"
1145     bl_parent_id = ""
1146     bl_category = "Strip"
1147
1148     @classmethod
1149     def poll(cls, context):
1150         if not cls.has_sequencer(context):
1151             return False
1152
1153         strip = act_strip(context)
1154         if not strip:
1155             return False
1156
1157         return (strip.type == 'SOUND')
1158
1159     def draw(self, context):
1160         layout = self.layout
1161         layout.use_property_split = True
1162         layout.use_property_decorate = False
1163
1164         st = context.space_data
1165         strip = act_strip(context)
1166         sound = strip.sound
1167
1168         layout.template_ID(strip, "sound", open="sound.open")
1169         if sound is not None:
1170             layout.prop(sound, "filepath", text="")
1171
1172             layout.use_property_split = True
1173             layout.use_property_decorate = False
1174
1175             layout.alignment = 'RIGHT'
1176             sub = layout.column(align=True)
1177             split = sub.split(factor=0.5, align=True)
1178             split.alignment = 'RIGHT'
1179             if sound.packed_file:
1180                 split.label(text="Unpack")
1181                 split.operator("sound.unpack", icon='PACKAGE', text="")
1182             else:
1183                 split.label(text="Pack")
1184                 split.operator("sound.pack", icon='UGLYPACKAGE', text="")
1185
1186             layout.prop(sound, "use_memory_cache")
1187
1188
1189 class SEQUENCER_PT_scene(SequencerButtonsPanel, Panel):
1190     bl_label = "Scene"
1191     bl_category = "Strip"
1192
1193     @classmethod
1194     def poll(cls, context):
1195         if not cls.has_sequencer(context):
1196             return False
1197
1198         strip = act_strip(context)
1199         if not strip:
1200             return False
1201
1202         return (strip.type == 'SCENE')
1203
1204     def draw(self, context):
1205         layout = self.layout
1206         layout.use_property_split = True
1207         layout.use_property_decorate = False
1208
1209         strip = act_strip(context)
1210
1211         layout.template_ID(strip, "scene")
1212
1213         scene = strip.scene
1214         layout.prop(strip, "use_sequence")
1215
1216         layout.prop(scene, "audio_volume", text="Volume")
1217
1218         if not strip.use_sequence:
1219             layout.alignment = 'RIGHT'
1220             sub = layout.column(align=True)
1221             split = sub.split(factor=0.5, align=True)
1222             split.alignment = 'RIGHT'
1223             split.label(text="Camera Override")
1224             split.template_ID(strip, "scene_camera")
1225
1226             layout.prop(strip, "use_grease_pencil", text="Show Grease Pencil")
1227
1228         if not strip.use_sequence:
1229             if scene:
1230                 # Warning, this is not a good convention to follow.
1231                 # Expose here because setting the alpha from the 'Render' menu is very inconvenient.
1232                 # layout.label(text="Preview")
1233                 layout.prop(scene.render, "film_transparent")
1234
1235
1236 class SEQUENCER_PT_mask(SequencerButtonsPanel, Panel):
1237     bl_label = "Mask"
1238     bl_category = "Strip"
1239
1240     @classmethod
1241     def poll(cls, context):
1242         if not cls.has_sequencer(context):
1243             return False
1244
1245         strip = act_strip(context)
1246         if not strip:
1247             return False
1248
1249         return (strip.type == 'MASK')
1250
1251     def draw(self, context):
1252         layout = self.layout
1253         layout.use_property_split = True
1254         layout.use_property_decorate = False
1255
1256         strip = act_strip(context)
1257
1258         layout.template_ID(strip, "mask")
1259
1260         mask = strip.mask
1261
1262         if mask:
1263             sta = mask.frame_start
1264             end = mask.frame_end
1265             layout.label(text=iface_("Original frame range: %d-%d (%d)") % (sta, end, end - sta + 1), translate=False)
1266
1267
1268 class SEQUENCER_PT_info_data(SequencerButtonsPanel, Panel):
1269     bl_label = "Data"
1270     bl_category = "Strip"
1271     bl_parent_id = "SEQUENCER_PT_info"
1272
1273     @classmethod
1274     def poll(cls, context):
1275         if not cls.has_sequencer(context):
1276             return False
1277
1278         strip = act_strip(context)
1279         if not strip:
1280             return False
1281
1282         return strip.type
1283
1284     def draw(self, context):
1285         layout = self.layout
1286         layout.use_property_split = False
1287         layout.use_property_decorate = False
1288
1289         scene = context.scene
1290         frame_current = scene.frame_current
1291         strip = act_strip(context)
1292
1293         length_list = (
1294             str(strip.frame_start),
1295             str(strip.frame_final_end),
1296             str(strip.frame_final_duration),
1297             str(strip.frame_offset_start),
1298             str(strip.frame_offset_end),
1299         )
1300
1301         if not isinstance(strip, bpy.types.EffectSequence):
1302             length_list = length_list + (
1303                 str(strip.animation_offset_start),
1304                 str(strip.animation_offset_end),
1305             )
1306
1307         max_length = max(len(x) for x in length_list)
1308         max_factor = (1.9 - max_length) / 30
1309
1310         sub = layout.row(align=True)
1311         sub.enabled = not strip.lock
1312         split = sub.split(factor=0.5 + max_factor)
1313         split.alignment = 'RIGHT'
1314         split.label(text='Channel')
1315         split.prop(strip, "channel", text="")
1316
1317         sub = layout.column(align=True)
1318         sub.enabled = not strip.lock
1319         split = sub.split(factor=0.5 + max_factor)
1320         split.alignment = 'RIGHT'
1321         split.label(text="Start")
1322         split.prop(strip, "frame_start", text=str(bpy.utils.smpte_from_frame(strip.frame_start)).replace(':', ' '))
1323         split = sub.split(factor=0.5 + max_factor)
1324         split.alignment = 'RIGHT'
1325         split.label(text="End")
1326         split.prop(strip, "frame_final_end", text=str(bpy.utils.smpte_from_frame(strip.frame_final_end)).replace(':', ' '))
1327         split = sub.split(factor=0.5 + max_factor)
1328         split.alignment = 'RIGHT'
1329         split.label(text="Duration")
1330         split.prop(strip, "frame_final_duration", text=str(bpy.utils.smpte_from_frame(strip.frame_final_duration)).replace(':', ' '))
1331
1332         if not isinstance(strip, bpy.types.EffectSequence):
1333             layout.alignment = 'RIGHT'
1334             sub = layout.column(align=True)
1335             split = sub.split(factor=0.5 + max_factor, align=True)
1336             split.alignment = 'RIGHT'
1337             split.label(text="Soft Trim Start")
1338             split.prop(strip, "frame_offset_start", text=str(bpy.utils.smpte_from_frame(strip.frame_offset_start)).replace(':', ' '))
1339             split = sub.split(factor=0.5+max_factor, align=True)
1340             split.alignment = 'RIGHT'
1341             split.label(text='End')
1342             split.prop(strip, "frame_offset_end", text=str(bpy.utils.smpte_from_frame(strip.frame_offset_end)).replace(':', ' '))
1343
1344             layout.alignment = 'RIGHT'
1345             sub = layout.column(align=True)
1346             split = sub.split(factor=0.5 + max_factor)
1347             split.alignment = 'RIGHT'
1348             split.label(text="Hard Trim Start")
1349             split.prop(strip, "animation_offset_start", text=str(bpy.utils.smpte_from_frame(strip.animation_offset_start)).replace(':', ' '))
1350             split = sub.split(factor=0.5 + max_factor, align=True)
1351             split.alignment = 'RIGHT'
1352             split.label(text='End')
1353             split.prop(strip, "animation_offset_end", text=str(bpy.utils.smpte_from_frame(strip.animation_offset_end)).replace(':', ' '))
1354
1355         playhead = frame_current - strip.frame_start
1356         col = layout.column(align=True)
1357         col = col.box()
1358         col.active = (
1359             (frame_current >= strip.frame_start) and
1360             (frame_current <= strip.frame_start + strip.frame_final_duration)
1361         )
1362         split = col.split(factor=0.5 + max_factor)
1363         split.alignment = 'RIGHT'
1364         split.label(text="Playhead")
1365         split.label(text="%s:   %s" % ((bpy.utils.smpte_from_frame(playhead).replace(':', ' ')), (str(playhead))))
1366
1367         ''' Old data - anyone missing this data?
1368         col.label(text=iface_("Frame Offset %d:%d") % (strip.frame_offset_start, strip.frame_offset_end),
1369                   translate=False)
1370         col.label(text=iface_("Frame Still %d:%d") % (strip.frame_still_start, strip.frame_still_end), translate=False)'''
1371
1372         elem = False
1373
1374         if strip.type == 'IMAGE':
1375             elem = strip.strip_elem_from_frame(frame_current)
1376         elif strip.type == 'MOVIE':
1377             elem = strip.elements[0]
1378
1379         if strip.type != 'SOUND':
1380             split = col.split(factor=0.5 + max_factor)
1381             split.alignment = 'RIGHT'
1382             split.label(text="Resolution")
1383             if elem and elem.orig_width > 0 and elem.orig_height > 0:
1384                 split.label(text="%dx%d" % (elem.orig_width, elem.orig_height), translate=False)
1385             else:
1386                 split.label(text="None")
1387
1388         if strip.type == 'SCENE':
1389             scene = strip.scene
1390
1391             if scene:
1392                 sta = scene.frame_start
1393                 end = scene.frame_end
1394                 split = col.split(factor=0.5 + max_factor)
1395                 split.alignment = 'RIGHT'
1396                 split.label(text="Original Frame Range")
1397                 split.alignment = 'LEFT'
1398                 split.label(text="%d-%d (%d)" % (sta, end, end - sta + 1), translate=False)
1399
1400
1401 class SEQUENCER_PT_adjust(SequencerButtonsPanel, Panel):
1402     bl_label = "Adjust"
1403     bl_category = "Strip"
1404
1405     def draw(self, context):
1406         pass
1407
1408
1409 class SEQUENCER_PT_adjust_sound(SequencerButtonsPanel, Panel):
1410     bl_label = "Sound"
1411     bl_parent_id = "SEQUENCER_PT_adjust"
1412     bl_category = "Strip"
1413
1414     @classmethod
1415     def poll(cls, context):
1416         strip = act_strip(context)
1417         return strip.type == 'SOUND'
1418
1419     def draw(self, context):
1420         layout = self.layout
1421         layout.use_property_split = True
1422         layout.use_property_decorate = False
1423
1424         st = context.space_data
1425         strip = act_strip(context)
1426
1427         sound = strip.sound
1428
1429         col = layout.column()
1430
1431         row = col.row(align=True)
1432         sub = row.row(align=True)
1433         sub.active = (not strip.mute)
1434
1435         sub.prop(strip, "volume", text="Volume")
1436         sub.prop(strip, "mute", toggle=True, icon_only=True, icon='MUTE_IPO_ON')
1437
1438         col.prop(strip, "pitch")
1439         col.prop(strip, "pan")
1440
1441         if sound is not None:
1442
1443             if st.waveform_display_type == 'DEFAULT_WAVEFORMS':
1444                 col.prop(strip, "show_waveform")
1445             col.prop(sound, "use_mono")
1446
1447
1448 class SEQUENCER_PT_adjust_comp(SequencerButtonsPanel, Panel):
1449     bl_label = "Compositing"
1450     bl_parent_id = "SEQUENCER_PT_adjust"
1451     bl_category = "Strip"
1452
1453     @classmethod
1454     def poll(cls, context):
1455         strip = act_strip(context)
1456         return strip.type != 'SOUND'
1457
1458     def draw(self, context):
1459         layout = self.layout
1460         layout.use_property_split = True
1461         layout.use_property_decorate = False
1462
1463         strip = act_strip(context)
1464
1465         layout.prop(strip, "blend_type", text="Blend")
1466
1467         row = layout.row(align=True)
1468         sub = row.row(align=True)
1469         sub.active = (not strip.mute)
1470
1471         sub.prop(strip, "blend_alpha", text="Opacity", slider=True)
1472         sub.prop(strip, "mute", toggle=True, icon_only=True)
1473
1474
1475 class SEQUENCER_PT_adjust_video(SequencerButtonsPanel, Panel):
1476     bl_label = "Video"
1477     bl_parent_id = "SEQUENCER_PT_adjust"
1478     bl_options = {'DEFAULT_CLOSED'}
1479     bl_category = "Strip"
1480
1481     @classmethod
1482     def poll(cls, context):
1483         if not cls.has_sequencer(context):
1484             return False
1485
1486         strip = act_strip(context)
1487         if not strip:
1488             return False
1489
1490         return strip.type in {
1491             'MOVIE', 'IMAGE', 'SCENE', 'MOVIECLIP', 'MASK',
1492             'META', 'ADD', 'SUBTRACT', 'ALPHA_OVER',
1493             'ALPHA_UNDER', 'CROSS', 'GAMMA_CROSS', 'MULTIPLY',
1494             'OVER_DROP', 'WIPE', 'GLOW', 'TRANSFORM', 'COLOR',
1495             'MULTICAM', 'SPEED', 'ADJUSTMENT', 'COLORMIX'
1496         }
1497
1498     def draw(self, context):
1499         layout = self.layout
1500         layout.use_property_split = True
1501         layout.use_property_decorate = False
1502
1503         strip = act_strip(context)
1504
1505         col = layout.column()
1506         col.prop(strip, "strobe")
1507
1508         if strip.type == 'MOVIECLIP':
1509             col = layout.column()
1510             col.label(text="Tracker")
1511             col.prop(strip, "stabilize2d")
1512
1513             col = layout.column()
1514             col.label(text="Distortion")
1515             col.prop(strip, "undistort")
1516             col.separator()
1517
1518         col.prop(strip, "use_reverse_frames", text="Backwards")
1519         col.prop(strip, "use_deinterlace")
1520
1521         col.separator()
1522
1523         col.prop(strip, "use_flip_x", text="Flip X")
1524         col.prop(strip, "use_flip_y", text="Flip Y")
1525
1526
1527 class SEQUENCER_PT_adjust_color(SequencerButtonsPanel, Panel):
1528     bl_label = "Color"
1529     bl_parent_id = "SEQUENCER_PT_adjust"
1530     bl_options = {'DEFAULT_CLOSED'}
1531     bl_category = "Strip"
1532
1533     @classmethod
1534     def poll(cls, context):
1535         if not cls.has_sequencer(context):
1536             return False
1537
1538         strip = act_strip(context)
1539         if not strip:
1540             return False
1541
1542         return strip.type in {
1543             'MOVIE', 'IMAGE', 'SCENE', 'MOVIECLIP', 'MASK',
1544             'META', 'ADD', 'SUBTRACT', 'ALPHA_OVER',
1545             'ALPHA_UNDER', 'CROSS', 'GAMMA_CROSS', 'MULTIPLY',
1546             'OVER_DROP', 'WIPE', 'GLOW', 'TRANSFORM', 'COLOR',
1547             'MULTICAM', 'SPEED', 'ADJUSTMENT', 'COLORMIX'
1548         }
1549
1550     def draw(self, context):
1551         layout = self.layout
1552         layout.use_property_split = True
1553         layout.use_property_decorate = False
1554
1555         strip = act_strip(context)
1556
1557         col = layout.column()
1558         col.prop(strip, "color_saturation", text="Saturation")
1559         col.prop(strip, "color_multiply", text="Multiply")
1560         col.prop(strip, "use_float", text="Convert to Float")
1561
1562
1563 class SEQUENCER_PT_cache_settings(SequencerButtonsPanel, Panel):
1564     bl_label = "Cache Settings"
1565     bl_category = "Proxy & Cache"
1566
1567     @classmethod
1568     def poll(cls, context):
1569         return cls.has_sequencer(context) and context.scene.sequence_editor
1570
1571     def draw(self, context):
1572         layout = self.layout
1573         ed = context.scene.sequence_editor
1574
1575         layout.prop(ed, "use_cache_raw")
1576         layout.prop(ed, "use_cache_preprocessed")
1577         layout.prop(ed, "use_cache_composite")
1578         layout.prop(ed, "use_cache_final")
1579         layout.separator()
1580         layout.prop(ed, "recycle_max_cost")
1581
1582
1583 class SEQUENCER_PT_proxy_settings(SequencerButtonsPanel, Panel):
1584     bl_label = "Proxy Settings"
1585     bl_category = "Proxy & Cache"
1586
1587     @classmethod
1588     def poll(cls, context):
1589         return cls.has_sequencer(context) and context.scene.sequence_editor
1590
1591     def draw(self, context):
1592         layout = self.layout
1593         ed = context.scene.sequence_editor
1594         flow = layout.column_flow()
1595         flow.prop(ed, "proxy_storage", text="Storage")
1596
1597         if ed.proxy_storage == 'PROJECT':
1598             flow.prop(ed, "proxy_dir", text="Directory")
1599
1600         col = layout.column()
1601         col.operator("sequencer.enable_proxies")
1602         col.operator("sequencer.rebuild_proxy")
1603
1604
1605 class SEQUENCER_PT_strip_proxy(SequencerButtonsPanel, Panel):
1606     bl_label = "Strip Proxy & Timecode"
1607     bl_category = "Proxy & Cache"
1608
1609     @classmethod
1610     def poll(cls, context):
1611         if not cls.has_sequencer(context) and context.scene.sequence_editor:
1612             return False
1613
1614         strip = act_strip(context)
1615         if not strip:
1616             return False
1617
1618         return strip.type in {'MOVIE', 'IMAGE', 'SCENE', 'META', 'MULTICAM'}
1619
1620     def draw_header(self, context):
1621         strip = act_strip(context)
1622
1623         self.layout.prop(strip, "use_proxy", text="")
1624
1625     def draw(self, context):
1626         layout = self.layout
1627         layout.use_property_split = True
1628         layout.use_property_decorate = False
1629
1630         ed = context.scene.sequence_editor
1631
1632         strip = act_strip(context)
1633
1634         if strip.proxy:
1635             proxy = strip.proxy
1636
1637             flow = layout.column_flow()
1638             if ed.proxy_storage == 'PER_STRIP':
1639                 flow.prop(proxy, "use_proxy_custom_directory")
1640                 flow.prop(proxy, "use_proxy_custom_file")
1641
1642                 if proxy.use_proxy_custom_directory and not proxy.use_proxy_custom_file:
1643                     flow.prop(proxy, "directory")
1644                 if proxy.use_proxy_custom_file:
1645                     flow.prop(proxy, "filepath")
1646
1647             layout = layout.box()
1648             row = layout.row(align=True)
1649             row.prop(strip.proxy, "build_25")
1650             row.prop(strip.proxy, "build_75")
1651             row = layout.row(align=True)
1652             row.prop(strip.proxy, "build_50")
1653             row.prop(strip.proxy, "build_100")
1654
1655             layout = self.layout
1656             layout.use_property_split = True
1657             layout.use_property_decorate = False
1658
1659             layout.prop(proxy, "use_overwrite")
1660
1661             col = layout.column()
1662             col.prop(proxy, "quality", text="Build JPEG Quality")
1663
1664             if strip.type == 'MOVIE':
1665                 col = layout.column()
1666
1667                 col.prop(proxy, "timecode", text="Timecode Index")
1668
1669
1670 class SEQUENCER_PT_strip_cache(SequencerButtonsPanel, Panel):
1671     bl_label = "Strip Cache"
1672     bl_category = "Proxy & Cache"
1673     bl_options = {'DEFAULT_CLOSED'}
1674
1675     @classmethod
1676     def poll(cls, context):
1677         if not cls.has_sequencer(context):
1678             return False
1679         if act_strip(context) is not None:
1680             return True
1681
1682     def draw_header(self, context):
1683         strip = act_strip(context)
1684         self.layout.prop(strip, "override_cache_settings", text="")
1685
1686     def draw(self, context):
1687         layout = self.layout
1688         strip = act_strip(context)
1689         layout.active = strip.override_cache_settings
1690
1691         layout.prop(strip, "use_cache_raw")
1692         layout.prop(strip, "use_cache_preprocessed")
1693         layout.prop(strip, "use_cache_composite")
1694
1695
1696 class SEQUENCER_PT_preview(SequencerButtonsPanel_Output, Panel):
1697     bl_label = "Scene Preview/Render"
1698     bl_space_type = 'SEQUENCE_EDITOR'
1699     bl_region_type = 'UI'
1700     bl_options = {'DEFAULT_CLOSED'}
1701     bl_category = "View"
1702
1703     def draw(self, context):
1704         layout = self.layout
1705         layout.use_property_split = True
1706         layout.use_property_decorate = False
1707         render = context.scene.render
1708
1709         col = layout.column()
1710         col.prop(render, "sequencer_gl_preview", text="Preview Shading")
1711
1712         if render.sequencer_gl_preview in ['SOLID', 'WIREFRAME']:
1713             col.prop(render, "use_sequencer_override_scene_strip")
1714
1715
1716 class SEQUENCER_PT_view(SequencerButtonsPanel_Output, Panel):
1717     bl_label = "View Settings"
1718     bl_category = "View"
1719
1720     def draw(self, context):
1721         layout = self.layout
1722         layout.use_property_split = True
1723         layout.use_property_decorate = False
1724
1725         st = context.space_data
1726
1727         col = layout.column()
1728         col.prop(st, "display_channel", text="Channel")
1729
1730         if st.display_mode == 'IMAGE':
1731             col.prop(st, "show_overexposed")
1732
1733         elif st.display_mode == 'WAVEFORM':
1734             col.prop(st, "show_separate_color")
1735
1736         col.prop(st, "proxy_render_size")
1737
1738
1739 class SEQUENCER_PT_frame_overlay(SequencerButtonsPanel_Output, Panel):
1740     bl_label = "Frame Overlay"
1741     bl_category = "View"
1742     bl_options = {'DEFAULT_CLOSED'}
1743
1744     def draw_header(self, context):
1745         scene = context.scene
1746         ed = scene.sequence_editor
1747
1748         self.layout.prop(ed, "show_overlay", text="")
1749
1750     def draw(self, context):
1751         layout = self.layout
1752         layout.use_property_split = True
1753         layout.use_property_decorate = False
1754
1755         st = context.space_data
1756         scene = context.scene
1757         ed = scene.sequence_editor
1758
1759         layout.active = ed.show_overlay
1760
1761         col = layout.column()
1762         col.prop(ed, "overlay_frame", text="Frame Offset")
1763         col.prop(st, "overlay_type")
1764         col.prop(ed, "use_overlay_lock")
1765
1766
1767 class SEQUENCER_PT_view_safe_areas(SequencerButtonsPanel_Output, Panel):
1768     bl_label = "Safe Areas"
1769     bl_options = {'DEFAULT_CLOSED'}
1770     bl_category = "View"
1771
1772     @classmethod
1773     def poll(cls, context):
1774         st = context.space_data
1775         is_preview = st.view_type in {'PREVIEW', 'SEQUENCER_PREVIEW'}
1776         return is_preview and (st.display_mode == 'IMAGE')
1777
1778     def draw_header(self, context):
1779         st = context.space_data
1780
1781         self.layout.prop(st, "show_safe_areas", text="")
1782
1783     def draw(self, context):
1784         layout = self.layout
1785         layout.use_property_split = True
1786         st = context.space_data
1787         safe_data = context.scene.safe_areas
1788
1789         layout.active = st.show_safe_areas
1790
1791         col = layout.column()
1792
1793         sub = col.column()
1794         sub.prop(safe_data, "title", slider=True)
1795         sub.prop(safe_data, "action", slider=True)
1796
1797
1798 class SEQUENCER_PT_view_safe_areas_center_cut(SequencerButtonsPanel_Output, Panel):
1799     bl_label = "Center-Cut Safe Areas"
1800     bl_parent_id = "SEQUENCER_PT_view_safe_areas"
1801     bl_options = {'DEFAULT_CLOSED'}
1802     bl_category = "View"
1803
1804     def draw_header(self, context):
1805         st = context.space_data
1806
1807         layout = self.layout
1808         layout.active = st.show_safe_areas
1809         layout.prop(st, "show_safe_center", text="")
1810
1811     def draw(self, context):
1812         layout = self.layout
1813         layout.use_property_split = True
1814         safe_data = context.scene.safe_areas
1815         st = context.space_data
1816
1817         layout.active = st.show_safe_areas and st.show_safe_center
1818
1819         col = layout.column()
1820         col.prop(safe_data, "title_center", slider=True)
1821
1822
1823 class SEQUENCER_PT_modifiers(SequencerButtonsPanel, Panel):
1824     bl_label = "Modifiers"
1825     bl_category = "Modifiers"
1826
1827     def draw(self, context):
1828         layout = self.layout
1829         layout.use_property_split = True
1830         layout.use_property_decorate = False
1831
1832         strip = act_strip(context)
1833         ed = context.scene.sequence_editor
1834
1835         layout.prop(strip, "use_linear_modifiers")
1836
1837         layout.operator_menu_enum("sequencer.strip_modifier_add", "type")
1838         layout.operator("sequencer.strip_modifier_copy")
1839
1840         for mod in strip.modifiers:
1841             box = layout.box()
1842
1843             row = box.row()
1844             row.prop(mod, "show_expanded", text="", emboss=False)
1845             row.prop(mod, "name", text="")
1846
1847             row.prop(mod, "mute", text="")
1848
1849             sub = row.row(align=True)
1850             props = sub.operator("sequencer.strip_modifier_move", text="", icon='TRIA_UP')
1851             props.name = mod.name
1852             props.direction = 'UP'
1853             props = sub.operator("sequencer.strip_modifier_move", text="", icon='TRIA_DOWN')
1854             props.name = mod.name
1855             props.direction = 'DOWN'
1856
1857             row.operator("sequencer.strip_modifier_remove", text="", icon='X', emboss=False).name = mod.name
1858
1859             if mod.show_expanded:
1860                 row = box.row()
1861                 row.prop(mod, "input_mask_type", expand=True)
1862
1863                 if mod.input_mask_type == 'STRIP':
1864                     sequences_object = ed
1865                     if ed.meta_stack:
1866                         sequences_object = ed.meta_stack[-1]
1867                     box.prop_search(mod, "input_mask_strip", sequences_object, "sequences", text="Mask")
1868                 else:
1869                     box.prop(mod, "input_mask_id")
1870                     row = box.row()
1871                     row.prop(mod, "mask_time", expand=True)
1872
1873                 if mod.type == 'COLOR_BALANCE':
1874                     box.prop(mod, "color_multiply")
1875                     draw_color_balance(box, mod.color_balance)
1876                 elif mod.type == 'CURVES':
1877                     box.template_curve_mapping(mod, "curve_mapping", type='COLOR', show_tone=True)
1878                 elif mod.type == 'HUE_CORRECT':
1879                     box.template_curve_mapping(mod, "curve_mapping", type='HUE')
1880                 elif mod.type == 'BRIGHT_CONTRAST':
1881                     col = box.column()
1882                     col.prop(mod, "bright")
1883                     col.prop(mod, "contrast")
1884                 elif mod.type == 'WHITE_BALANCE':
1885                     col = box.column()
1886                     col.prop(mod, "white_value")
1887                 elif mod.type == 'TONEMAP':
1888                     col = box.column()
1889                     col.prop(mod, "tonemap_type")
1890                     if mod.tonemap_type == 'RD_PHOTORECEPTOR':
1891                         col.prop(mod, "intensity")
1892                         col.prop(mod, "contrast")
1893                         col.prop(mod, "adaptation")
1894                         col.prop(mod, "correction")
1895                     elif mod.tonemap_type == 'RH_SIMPLE':
1896                         col.prop(mod, "key")
1897                         col.prop(mod, "offset")
1898                         col.prop(mod, "gamma")
1899
1900
1901 class SEQUENCER_PT_grease_pencil(AnnotationDataPanel, SequencerButtonsPanel_Output, Panel):
1902     bl_space_type = 'SEQUENCE_EDITOR'
1903     bl_region_type = 'UI'
1904     bl_category = "View"
1905
1906     # NOTE: this is just a wrapper around the generic GP Panel
1907     # But, it should only show up when there are images in the preview region
1908
1909
1910 class SEQUENCER_PT_grease_pencil_tools(GreasePencilToolsPanel, SequencerButtonsPanel_Output, Panel):
1911     bl_space_type = 'SEQUENCE_EDITOR'
1912     bl_region_type = 'UI'
1913     bl_category = "View"
1914
1915     # NOTE: this is just a wrapper around the generic GP tools panel
1916     # It contains access to some essential tools usually found only in
1917     # toolbar, which doesn't exist here...
1918
1919
1920 class SEQUENCER_PT_custom_props(SequencerButtonsPanel, PropertyPanel, Panel):
1921     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
1922     _context_path = "scene.sequence_editor.active_strip"
1923     _property_type = (bpy.types.Sequence,)
1924     bl_category = "Strip"
1925
1926
1927 classes = (
1928     SEQUENCER_MT_change,
1929     SEQUENCER_HT_header,
1930     SEQUENCER_MT_editor_menus,
1931     SEQUENCER_MT_range,
1932     SEQUENCER_MT_view,
1933     SEQUENCER_MT_view_cache,
1934     SEQUENCER_MT_view_toggle,
1935     SEQUENCER_MT_select_playhead,
1936     SEQUENCER_MT_select_handle,
1937     SEQUENCER_MT_select_channel,
1938     SEQUENCER_MT_select_linked,
1939     SEQUENCER_MT_select,
1940     SEQUENCER_MT_marker,
1941     SEQUENCER_MT_navigation,
1942     SEQUENCER_MT_add,
1943     SEQUENCER_MT_add_effect,
1944     SEQUENCER_MT_add_transitions,
1945     SEQUENCER_MT_add_empty,
1946     SEQUENCER_MT_strip_effect,
1947     SEQUENCER_MT_strip_movie,
1948     SEQUENCER_MT_strip,
1949     SEQUENCER_MT_strip_transform,
1950     SEQUENCER_MT_strip_input,
1951     SEQUENCER_MT_strip_lock_mute,
1952     SEQUENCER_MT_context_menu,
1953
1954     SEQUENCER_PT_adjust,
1955     SEQUENCER_PT_adjust_comp,
1956     SEQUENCER_PT_adjust_offset,
1957     SEQUENCER_PT_adjust_crop,
1958     SEQUENCER_PT_adjust_video,
1959     SEQUENCER_PT_adjust_color,
1960     SEQUENCER_PT_adjust_sound,
1961
1962     SEQUENCER_PT_info,
1963     SEQUENCER_PT_info_input,
1964     SEQUENCER_PT_info_data,
1965
1966     SEQUENCER_PT_effect,
1967     SEQUENCER_PT_scene,
1968     SEQUENCER_PT_mask,
1969
1970     SEQUENCER_PT_cache_settings,
1971     SEQUENCER_PT_strip_cache,
1972     SEQUENCER_PT_proxy_settings,
1973     SEQUENCER_PT_strip_proxy,
1974
1975     SEQUENCER_PT_custom_props,
1976
1977     SEQUENCER_PT_modifiers,
1978
1979     SEQUENCER_PT_preview,
1980     SEQUENCER_PT_view,
1981     SEQUENCER_PT_frame_overlay,
1982     SEQUENCER_PT_view_safe_areas,
1983     SEQUENCER_PT_view_safe_areas_center_cut,
1984
1985     SEQUENCER_PT_grease_pencil,
1986     SEQUENCER_PT_grease_pencil_tools,
1987 )
1988
1989 if __name__ == "__main__":  # only for live edit.
1990     from bpy.utils import register_class
1991     for cls in classes:
1992         register_class(cls)