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