Sequencer: Restore change menu
[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 rna_prop_ui import PropertyPanel
23 from .properties_grease_pencil_common import (
24     GreasePencilDataPanel,
25     GreasePencilPaletteColorPanel,
26     GreasePencilToolsPanel,
27 )
28 from bpy.app.translations import pgettext_iface as iface_
29
30
31 def act_strip(context):
32     try:
33         return context.scene.sequence_editor.active_strip
34     except AttributeError:
35         return None
36
37
38 def draw_color_balance(layout, color_balance):
39     box = layout.box()
40     split = box.split(percentage=0.35)
41     col = split.column(align=True)
42     col.label(text="Lift:")
43     col.separator()
44     col.separator()
45     col.prop(color_balance, "lift", text="")
46     col.prop(color_balance, "invert_lift", text="Invert", icon='ARROW_LEFTRIGHT')
47     split.template_color_picker(color_balance, "lift", value_slider=True, cubic=True)
48
49     box = layout.box()
50     split = box.split(percentage=0.35)
51     col = split.column(align=True)
52     col.label(text="Gamma:")
53     col.separator()
54     col.separator()
55     col.prop(color_balance, "gamma", text="")
56     col.prop(color_balance, "invert_gamma", text="Invert", icon='ARROW_LEFTRIGHT')
57     split.template_color_picker(color_balance, "gamma", value_slider=True, lock_luminosity=True, cubic=True)
58
59     box = layout.box()
60     split = box.split(percentage=0.35)
61     col = split.column(align=True)
62     col.label(text="Gain:")
63     col.separator()
64     col.separator()
65     col.prop(color_balance, "gain", text="")
66     col.prop(color_balance, "invert_gain", text="Invert", icon='ARROW_LEFTRIGHT')
67     split.template_color_picker(color_balance, "gain", value_slider=True, lock_luminosity=True, cubic=True)
68
69
70 class SEQUENCER_HT_header(Header):
71     bl_space_type = 'SEQUENCE_EDITOR'
72
73     def draw(self, context):
74         layout = self.layout
75
76         st = context.space_data
77         scene = context.scene
78
79         row = layout.row(align=True)
80         row.template_header()
81
82         SEQUENCER_MT_editor_menus.draw_collapsible(context, layout)
83
84         row = layout.row(align=True)
85         row.prop(scene, "use_preview_range", text="", toggle=True)
86         row.prop(scene, "lock_frame_selection_to_range", text="", toggle=True)
87
88         layout.prop(st, "view_type", expand=True, text="")
89
90         if st.view_type in {'PREVIEW', 'SEQUENCER_PREVIEW'}:
91             layout.prop(st, "display_mode", expand=True, text="")
92
93         if st.view_type == 'SEQUENCER':
94             row = layout.row(align=True)
95             row.operator("sequencer.copy", text="", icon='COPYDOWN')
96             row.operator("sequencer.paste", text="", icon='PASTEDOWN')
97
98             layout.separator()
99             layout.operator("sequencer.refresh_all")
100             layout.prop(st, "show_backdrop")
101         else:
102             if st.view_type == 'SEQUENCER_PREVIEW':
103                 layout.separator()
104                 layout.operator("sequencer.refresh_all")
105
106             layout.prop(st, "preview_channels", expand=True, text="")
107             layout.prop(st, "display_channel", text="Channel")
108
109             ed = scene.sequence_editor
110             if ed:
111                 row = layout.row(align=True)
112                 row.prop(ed, "show_overlay", text="", icon='GHOST_ENABLED')
113                 if ed.show_overlay:
114                     row.prop(ed, "overlay_frame", text="")
115                     row.prop(ed, "use_overlay_lock", text="", icon='LOCKED')
116
117                     row = layout.row()
118                     row.prop(st, "overlay_type", text="")
119
120         if st.view_type in {'PREVIEW', 'SEQUENCER_PREVIEW'}:
121             gpd = context.gpencil_data
122             tool_settings = context.tool_settings
123
124             # Proportional editing
125             if gpd and gpd.use_stroke_edit_mode:
126                 row = layout.row(align=True)
127                 row.prop(tool_settings, "proportional_edit", icon_only=True)
128                 if tool_settings.proportional_edit != 'DISABLED':
129                     row.prop(tool_settings, "proportional_edit_falloff", icon_only=True)
130
131         row = layout.row(align=True)
132         row.operator("render.opengl", text="", icon='RENDER_STILL').sequencer = True
133         props = row.operator("render.opengl", text="", icon='RENDER_ANIMATION')
134         props.animation = True
135         props.sequencer = True
136
137         layout.template_running_jobs()
138
139
140 class SEQUENCER_MT_editor_menus(Menu):
141     bl_idname = "SEQUENCER_MT_editor_menus"
142     bl_label = ""
143
144     def draw(self, context):
145         self.draw_menus(self.layout, context)
146
147     @staticmethod
148     def draw_menus(layout, context):
149         st = context.space_data
150
151         layout.menu("SEQUENCER_MT_view")
152
153         if st.view_type in {'SEQUENCER', 'SEQUENCER_PREVIEW'}:
154             layout.menu("SEQUENCER_MT_select")
155             layout.menu("SEQUENCER_MT_marker")
156             layout.menu("SEQUENCER_MT_add")
157             layout.menu("SEQUENCER_MT_frame")
158             layout.menu("SEQUENCER_MT_strip")
159
160
161 class SEQUENCER_MT_view_toggle(Menu):
162     bl_label = "View Type"
163
164     def draw(self, context):
165         layout = self.layout
166
167         layout.operator("sequencer.view_toggle").type = 'SEQUENCER'
168         layout.operator("sequencer.view_toggle").type = 'PREVIEW'
169         layout.operator("sequencer.view_toggle").type = 'SEQUENCER_PREVIEW'
170
171
172 class SEQUENCER_MT_view(Menu):
173     bl_label = "View"
174
175     def draw(self, context):
176         layout = self.layout
177
178         st = context.space_data
179         is_preview = st.view_type in {'PREVIEW', 'SEQUENCER_PREVIEW'}
180         is_sequencer_view = st.view_type in {'SEQUENCER', 'SEQUENCER_PREVIEW'}
181
182         if st.view_type == 'PREVIEW':
183             # Specifying the REGION_PREVIEW context is needed in preview-only
184             # mode, else the lookup for the shortcut will fail in
185             # wm_keymap_item_find_props() (see #32595).
186             layout.operator_context = 'INVOKE_REGION_PREVIEW'
187         layout.operator("sequencer.properties", icon='MENU_PANEL')
188         layout.operator_context = 'INVOKE_DEFAULT'
189
190         layout.separator()
191
192         if is_sequencer_view:
193             layout.operator_context = 'INVOKE_REGION_WIN'
194             layout.operator("sequencer.view_all", text="View all Sequences")
195             layout.operator("sequencer.view_selected")
196             layout.operator("sequencer.view_frame")
197             layout.operator_context = 'INVOKE_DEFAULT'
198         if is_preview:
199             layout.operator_context = 'INVOKE_REGION_PREVIEW'
200             layout.operator("sequencer.view_all_preview", text="Fit Preview in window")
201
202             layout.separator()
203
204             ratios = ((1, 8), (1, 4), (1, 2), (1, 1), (2, 1), (4, 1), (8, 1))
205
206             for a, b in ratios:
207                 layout.operator(
208                     "sequencer.view_zoom_ratio",
209                     text=iface_("Zoom %d:%d") % (a, b),
210                     translate=False,
211                 ).ratio = a / b
212
213             layout.separator()
214
215             layout.operator_context = 'INVOKE_DEFAULT'
216
217             # # XXX, invokes in the header view
218             # layout.operator("sequencer.view_ghost_border", text="Overlay Border")
219
220         if is_sequencer_view:
221             layout.prop(st, "show_seconds")
222             layout.prop(st, "show_frame_indicator")
223             layout.prop(st, "show_strip_offset")
224
225             layout.prop_menu_enum(st, "waveform_draw_type")
226
227         if is_preview:
228             if st.display_mode == 'IMAGE':
229                 layout.prop(st, "show_safe_areas")
230                 layout.prop(st, "show_metadata")
231             elif st.display_mode == 'WAVEFORM':
232                 layout.prop(st, "show_separate_color")
233
234         layout.separator()
235
236         if is_sequencer_view:
237             layout.prop(st, "use_marker_sync")
238             layout.separator()
239
240         layout.operator("screen.area_dupli")
241         layout.operator("screen.screen_full_area")
242         layout.operator("screen.screen_full_area", text="Toggle Fullscreen Area").use_hide_panels = True
243
244
245 class SEQUENCER_MT_select(Menu):
246     bl_label = "Select"
247
248     def draw(self, context):
249         layout = self.layout
250
251         layout.operator("sequencer.select_active_side", text="Strips to the Left").side = 'LEFT'
252         layout.operator("sequencer.select_active_side", text="Strips to the Right").side = 'RIGHT'
253         props = layout.operator("sequencer.select", text="All Strips to the Left")
254         props.left_right = 'LEFT'
255         props.linked_time = True
256         props = layout.operator("sequencer.select", text="All Strips to the Right")
257         props.left_right = 'RIGHT'
258         props.linked_time = True
259
260         layout.separator()
261         layout.operator("sequencer.select_handles", text="Surrounding Handles").side = 'BOTH'
262         layout.operator("sequencer.select_handles", text="Left Handle").side = 'LEFT'
263         layout.operator("sequencer.select_handles", text="Right Handle").side = 'RIGHT'
264         layout.separator()
265         layout.operator_menu_enum("sequencer.select_grouped", "type", text="Grouped")
266         layout.operator("sequencer.select_linked")
267         layout.operator("sequencer.select_less")
268         layout.operator("sequencer.select_more")
269         layout.operator("sequencer.select_all").action = 'TOGGLE'
270         layout.operator("sequencer.select_all", text="Inverse").action = 'INVERT'
271
272
273 class SEQUENCER_MT_marker(Menu):
274     bl_label = "Marker"
275
276     def draw(self, context):
277         layout = self.layout
278
279         from .space_time import marker_menu_generic
280         marker_menu_generic(layout)
281
282
283 class SEQUENCER_MT_change(Menu):
284     bl_label = "Change"
285
286     def draw(self, context):
287         layout = self.layout
288         strip = act_strip(context)
289
290         layout.operator_context = 'INVOKE_REGION_WIN'
291
292         layout.operator_menu_enum("sequencer.change_effect_input", "swap")
293         layout.operator_menu_enum("sequencer.change_effect_type", "type")
294         prop = layout.operator("sequencer.change_path", text="Path/Files")
295
296         if strip:
297             stype = strip.type
298
299             if stype == 'IMAGE':
300                 prop.filter_image = True
301             elif stype == 'MOVIE':
302                 prop.filter_movie = True
303             elif stype == 'SOUND':
304                 prop.filter_sound = True
305
306
307 class SEQUENCER_MT_frame(Menu):
308     bl_label = "Frame"
309
310     def draw(self, context):
311         layout = self.layout
312
313         layout.operator("anim.previewrange_clear")
314         layout.operator("anim.previewrange_set")
315
316         layout.separator()
317
318         props = layout.operator("sequencer.strip_jump", text="Jump to Previous Strip")
319         props.next = False
320         props.center = False
321         props = layout.operator("sequencer.strip_jump", text="Jump to Next Strip")
322         props.next = True
323         props.center = False
324
325         layout.separator()
326
327         props = layout.operator("sequencer.strip_jump", text="Jump to Previous Strip (Center)")
328         props.next = False
329         props.center = True
330         props = layout.operator("sequencer.strip_jump", text="Jump to Next Strip (Center)")
331         props.next = True
332         props.center = True
333
334
335 class SEQUENCER_MT_add(Menu):
336     bl_label = "Add"
337
338     def draw(self, context):
339         layout = self.layout
340
341         layout.operator_context = 'INVOKE_REGION_WIN'
342
343         if len(bpy.data.scenes) > 10:
344             layout.operator_context = 'INVOKE_DEFAULT'
345             layout.operator("sequencer.scene_strip_add", text="Scene...")
346         else:
347             layout.operator_menu_enum("sequencer.scene_strip_add", "scene", text="Scene")
348
349         if len(bpy.data.movieclips) > 10:
350             layout.operator_context = 'INVOKE_DEFAULT'
351             layout.operator("sequencer.movieclip_strip_add", text="Clips...")
352         else:
353             layout.operator_menu_enum("sequencer.movieclip_strip_add", "clip", text="Clip")
354
355         if len(bpy.data.masks) > 10:
356             layout.operator_context = 'INVOKE_DEFAULT'
357             layout.operator("sequencer.mask_strip_add", text="Masks...")
358         else:
359             layout.operator_menu_enum("sequencer.mask_strip_add", "mask", text="Mask")
360
361         layout.operator("sequencer.movie_strip_add", text="Movie")
362         layout.operator("sequencer.image_strip_add", text="Image")
363         layout.operator("sequencer.sound_strip_add", text="Sound")
364
365         layout.menu("SEQUENCER_MT_add_generate")
366         layout.menu("SEQUENCER_MT_add_effect")
367
368
369 class SEQUENCER_MT_add_generate(Menu):
370     bl_label = "Generate"
371
372     def draw(self, context):
373         layout = self.layout
374
375         layout.operator_context = 'INVOKE_REGION_WIN'
376         layout.operator("sequencer.effect_strip_add", text="Color").type = 'COLOR'
377         layout.operator("sequencer.effect_strip_add", text="Text").type = 'TEXT'
378
379
380 class SEQUENCER_MT_add_effect(Menu):
381     bl_label = "Effect Strip"
382
383     def draw(self, context):
384         layout = self.layout
385
386         layout.operator_context = 'INVOKE_REGION_WIN'
387
388         layout.operator("sequencer.effect_strip_add", text="Add").type = 'ADD'
389         layout.operator("sequencer.effect_strip_add", text="Subtract").type = 'SUBTRACT'
390         layout.operator("sequencer.effect_strip_add", text="Alpha Over").type = 'ALPHA_OVER'
391         layout.operator("sequencer.effect_strip_add", text="Alpha Under").type = 'ALPHA_UNDER'
392         layout.operator("sequencer.effect_strip_add", text="Cross").type = 'CROSS'
393         layout.operator("sequencer.effect_strip_add", text="Gamma Cross").type = 'GAMMA_CROSS'
394         layout.operator("sequencer.effect_strip_add", text="Gaussian Blur").type = 'GAUSSIAN_BLUR'
395         layout.operator("sequencer.effect_strip_add", text="Multiply").type = 'MULTIPLY'
396         layout.operator("sequencer.effect_strip_add", text="Over Drop").type = 'OVER_DROP'
397         layout.operator("sequencer.effect_strip_add", text="Wipe").type = 'WIPE'
398         layout.operator("sequencer.effect_strip_add", text="Glow").type = 'GLOW'
399         layout.operator("sequencer.effect_strip_add", text="Color Mix").type = 'COLORMIX'
400         layout.operator("sequencer.effect_strip_add", text="Transform").type = 'TRANSFORM'
401         layout.operator("sequencer.effect_strip_add", text="Speed Control").type = 'SPEED'
402         layout.operator("sequencer.effect_strip_add", text="Multicam Selector").type = 'MULTICAM'
403         layout.operator("sequencer.effect_strip_add", text="Adjustment Layer").type = 'ADJUSTMENT'
404
405
406 class SEQUENCER_MT_strip_transform(Menu):
407     bl_label = "Transform"
408
409     def draw(self, context):
410         layout = self.layout
411
412         layout.operator("transform.transform", text="Grab/Move").mode = 'TRANSLATION'
413         layout.operator("transform.transform", text="Grab/Extend from Frame").mode = 'TIME_EXTEND'
414         layout.operator("sequencer.slip", text="Slip Strip Contents")
415
416         layout.separator()
417         layout.operator_menu_enum("sequencer.swap", "side")
418
419         layout.separator()
420         layout.operator("sequencer.gap_remove").all = False
421         layout.operator("sequencer.gap_insert")
422
423
424 class SEQUENCER_MT_strip_input(Menu):
425     bl_label = "Inputs"
426
427     def draw(self, context):
428         layout = self.layout
429         strip = act_strip(context)
430
431         layout.operator("sequencer.reload", text="Reload Strips")
432         layout.operator("sequencer.reload", text="Reload Strips and Adjust Length").adjust_length = True
433         prop = layout.operator("sequencer.change_path", text="Change Path/Files")
434         layout.operator("sequencer.swap_data", text="Swap Data")
435
436         if strip:
437             stype = strip.type
438
439             if stype == 'IMAGE':
440                 prop.filter_image = True
441             elif stype == 'MOVIE':
442                 prop.filter_movie = True
443             elif stype == 'SOUND':
444                 prop.filter_sound = True
445
446
447 class SEQUENCER_MT_strip_lock_mute(Menu):
448     bl_label = "Lock/Mute"
449
450     def draw(self, context):
451         layout = self.layout
452
453         layout.operator("sequencer.lock")
454         layout.operator("sequencer.unlock")
455         layout.operator("sequencer.mute").unselected = False
456         layout.operator("sequencer.unmute").unselected = False
457         layout.operator("sequencer.mute", text="Mute Deselected Strips").unselected = True
458
459
460 class SEQUENCER_MT_strip(Menu):
461     bl_label = "Strip"
462
463     def draw(self, context):
464         layout = self.layout
465
466         layout.operator_context = 'INVOKE_REGION_WIN'
467
468         layout.operator("ed.undo")
469         layout.operator("ed.redo")
470         layout.operator("ed.undo_history")
471
472         layout.separator()
473         layout.menu("SEQUENCER_MT_strip_transform")
474         layout.operator("sequencer.snap")
475         layout.operator("sequencer.offset_clear")
476
477         layout.separator()
478         layout.operator("sequencer.duplicate_move")
479         layout.operator("sequencer.delete", text="Delete...")
480
481         layout.separator()
482         layout.operator("sequencer.cut", text="Cut (Hard) at frame").type = 'HARD'
483         layout.operator("sequencer.cut", text="Cut (Soft) at frame").type = 'SOFT'
484
485         layout.separator()
486         layout.operator("sequencer.deinterlace_selected_movies")
487         layout.operator("sequencer.rebuild_proxy")
488
489         strip = act_strip(context)
490
491         if strip:
492             stype = strip.type
493
494             if stype in {
495                     'CROSS', 'ADD', 'SUBTRACT', 'ALPHA_OVER', 'ALPHA_UNDER',
496                     'GAMMA_CROSS', 'MULTIPLY', 'OVER_DROP', 'WIPE', 'GLOW',
497                     'TRANSFORM', 'COLOR', 'SPEED', 'MULTICAM', 'ADJUSTMENT',
498                     'GAUSSIAN_BLUR', 'TEXT',
499             }:
500                 layout.separator()
501                 layout.operator_menu_enum("sequencer.change_effect_input", "swap")
502                 layout.operator_menu_enum("sequencer.change_effect_type", "type")
503                 layout.operator("sequencer.reassign_inputs")
504                 layout.operator("sequencer.swap_inputs")
505             elif stype in {'IMAGE', 'MOVIE'}:
506                 layout.separator()
507                 layout.operator("sequencer.rendersize")
508                 layout.operator("sequencer.images_separate")
509             elif stype == 'SOUND':
510                 layout.separator()
511                 layout.operator("sequencer.crossfade_sounds")
512             elif stype == 'META':
513                 layout.separator()
514                 layout.operator("sequencer.meta_separate")
515
516         layout.separator()
517         layout.operator("sequencer.meta_make")
518
519         layout.separator()
520         layout.menu("SEQUENCER_MT_strip_input")
521
522         layout.separator()
523         layout.menu("SEQUENCER_MT_strip_lock_mute")
524
525
526 class SequencerButtonsPanel:
527     bl_space_type = 'SEQUENCE_EDITOR'
528     bl_region_type = 'UI'
529
530     @staticmethod
531     def has_sequencer(context):
532         return (context.space_data.view_type in {'SEQUENCER', 'SEQUENCER_PREVIEW'})
533
534     @classmethod
535     def poll(cls, context):
536         return cls.has_sequencer(context) and (act_strip(context) is not None)
537
538
539 class SequencerButtonsPanel_Output:
540     bl_space_type = 'SEQUENCE_EDITOR'
541     bl_region_type = 'UI'
542
543     @staticmethod
544     def has_preview(context):
545         st = context.space_data
546         return (st.view_type in {'PREVIEW', 'SEQUENCER_PREVIEW'}) or st.show_backdrop
547
548     @classmethod
549     def poll(cls, context):
550         return cls.has_preview(context)
551
552
553 class SEQUENCER_PT_edit(SequencerButtonsPanel, Panel):
554     bl_label = "Edit Strip"
555     bl_category = "Strip"
556
557     def draw(self, context):
558         layout = self.layout
559
560         scene = context.scene
561         frame_current = scene.frame_current
562         strip = act_strip(context)
563
564         split = layout.split(percentage=0.25)
565         split.label(text="Name:")
566         split.prop(strip, "name", text="")
567
568         split = layout.split(percentage=0.25)
569         split.label(text="Type:")
570         split.prop(strip, "type", text="")
571
572         if strip.type != 'SOUND':
573             split = layout.split(percentage=0.25)
574             split.label(text="Blend:")
575             split.prop(strip, "blend_type", text="")
576
577             row = layout.row(align=True)
578             sub = row.row(align=True)
579             sub.active = (not strip.mute)
580             sub.prop(strip, "blend_alpha", text="Opacity", slider=True)
581             row.prop(strip, "mute", toggle=True, icon_only=True)
582
583         else:
584             row = layout.row()
585             row.prop(strip, "mute", toggle=True, icon_only=True, icon='MUTE_IPO_OFF')
586
587         col = layout.column(align=True)
588         row = col.row(align=True)
589
590         row_sub = row.row(align=True)
591         row_sub.enabled = not strip.lock
592         row_sub.prop(strip, "channel")
593         row.prop(strip, "lock", toggle=True, icon_only=True)
594
595         sub = col.column(align=True)
596         sub.enabled = not strip.lock
597         sub.prop(strip, "frame_start")
598         sub.prop(strip, "frame_final_duration")
599
600         col = layout.column(align=True)
601         row = col.row(align=True)
602         row.label(text=iface_("Final Length: %s") % bpy.utils.smpte_from_frame(strip.frame_final_duration),
603                   translate=False)
604         row = col.row(align=True)
605         row.active = (frame_current >= strip.frame_start and frame_current <= strip.frame_start + strip.frame_duration)
606         row.label(text=iface_("Playhead: %d") % (frame_current - strip.frame_start), translate=False)
607
608         col.label(text=iface_("Frame Offset %d:%d") % (strip.frame_offset_start, strip.frame_offset_end),
609                   translate=False)
610         col.label(text=iface_("Frame Still %d:%d") % (strip.frame_still_start, strip.frame_still_end), translate=False)
611
612         elem = False
613
614         if strip.type == 'IMAGE':
615             elem = strip.strip_elem_from_frame(frame_current)
616         elif strip.type == 'MOVIE':
617             elem = strip.elements[0]
618
619         if elem and elem.orig_width > 0 and elem.orig_height > 0:
620             col.label(text=iface_("Original Dimension: %dx%d") % (elem.orig_width, elem.orig_height), translate=False)
621         else:
622             col.label(text="Original Dimension: None")
623
624
625 class SEQUENCER_PT_effect(SequencerButtonsPanel, Panel):
626     bl_label = "Effect Strip"
627     bl_category = "Strip"
628
629     @classmethod
630     def poll(cls, context):
631         if not cls.has_sequencer(context):
632             return False
633
634         strip = act_strip(context)
635         if not strip:
636             return False
637
638         return strip.type in {
639             'ADD', 'SUBTRACT', 'ALPHA_OVER', 'ALPHA_UNDER',
640             'CROSS', 'GAMMA_CROSS', 'MULTIPLY', 'OVER_DROP',
641             'WIPE', 'GLOW', 'TRANSFORM', 'COLOR', 'SPEED',
642             'MULTICAM', 'GAUSSIAN_BLUR', 'TEXT', 'COLORMIX'
643         }
644
645     def draw(self, context):
646         layout = self.layout
647
648         strip = act_strip(context)
649
650         if strip.input_count > 0:
651             col = layout.column()
652             col.enabled = False
653             col.prop(strip, "input_1")
654             if strip.input_count > 1:
655                 col.prop(strip, "input_2")
656
657         if strip.type == 'COLOR':
658             layout.prop(strip, "color")
659
660         elif strip.type == 'WIPE':
661             col = layout.column()
662             col.prop(strip, "transition_type")
663             col.label(text="Direction:")
664             col.row().prop(strip, "direction", expand=True)
665
666             col = layout.column()
667             col.prop(strip, "blur_width", slider=True)
668             if strip.transition_type in {'SINGLE', 'DOUBLE'}:
669                 col.prop(strip, "angle")
670
671         elif strip.type == 'GLOW':
672             flow = layout.column_flow()
673             flow.prop(strip, "threshold", slider=True)
674             flow.prop(strip, "clamp", slider=True)
675             flow.prop(strip, "boost_factor")
676             flow.prop(strip, "blur_radius")
677
678             row = layout.row()
679             row.prop(strip, "quality", slider=True)
680             row.prop(strip, "use_only_boost")
681
682         elif strip.type == 'SPEED':
683             layout.prop(strip, "use_default_fade", "Stretch to input strip length")
684             if not strip.use_default_fade:
685                 layout.prop(strip, "use_as_speed")
686                 if strip.use_as_speed:
687                     layout.prop(strip, "speed_factor")
688                 else:
689                     layout.prop(strip, "speed_factor", text="Frame Number")
690                     layout.prop(strip, "scale_to_length")
691
692         elif strip.type == 'TRANSFORM':
693             layout = self.layout
694             col = layout.column()
695
696             col.prop(strip, "interpolation")
697             col.prop(strip, "translation_unit")
698             col = layout.column(align=True)
699             col.label(text="Position:")
700             row = col.row(align=True)
701             row.prop(strip, "translate_start_x", text="X")
702             row.prop(strip, "translate_start_y", text="Y")
703
704             layout.separator()
705
706             col = layout.column(align=True)
707             col.prop(strip, "use_uniform_scale")
708             if strip.use_uniform_scale:
709                 col = layout.column(align=True)
710                 col.prop(strip, "scale_start_x", text="Scale")
711             else:
712                 col = layout.column(align=True)
713                 col.label(text="Scale:")
714                 row = col.row(align=True)
715                 row.prop(strip, "scale_start_x", text="X")
716                 row.prop(strip, "scale_start_y", text="Y")
717
718             layout.separator()
719
720             col = layout.column(align=True)
721             col.label(text="Rotation:")
722             col.prop(strip, "rotation_start", text="Rotation")
723
724         elif strip.type == 'MULTICAM':
725             col = layout.column(align=True)
726             strip_channel = strip.channel
727
728             col.prop(strip, "multicam_source", text="Source Channel")
729
730             # The multicam strip needs at least 2 strips to be useful
731             if strip_channel > 2:
732                 BT_ROW = 4
733
734                 col.label("Cut To:")
735                 row = col.row()
736
737                 for i in range(1, strip_channel):
738                     if (i % BT_ROW) == 1:
739                         row = col.row(align=True)
740
741                     # Workaround - .enabled has to have a separate UI block to work
742                     if i == strip.multicam_source:
743                         sub = row.row(align=True)
744                         sub.enabled = False
745                         sub.operator("sequencer.cut_multicam", text=f"{i:d}").camera = i
746                     else:
747                         sub_1 = row.row(align=True)
748                         sub_1.enabled = True
749                         sub_1.operator("sequencer.cut_multicam", text=f"{i:d}").camera = i
750
751                 if strip.channel > BT_ROW and (strip_channel - 1) % BT_ROW:
752                     for i in range(strip.channel, strip_channel + ((BT_ROW + 1 - strip_channel) % BT_ROW)):
753                         row.label("")
754             else:
755                 col.separator()
756                 col.label(text="Two or more channels are needed below this strip", icon='INFO')
757
758         elif strip.type == 'TEXT':
759             col = layout.column()
760             col.prop(strip, "text")
761             col.prop(strip, "font_size")
762
763             row = col.row()
764             row.prop(strip, "color")
765             row = col.row()
766             row.prop(strip, "use_shadow")
767             rowsub = row.row()
768             rowsub.active = strip.use_shadow
769             rowsub.prop(strip, "shadow_color", text="")
770
771             col.prop(strip, "align_x")
772             col.prop(strip, "align_y")
773             col.label("Location")
774             row = col.row(align=True)
775             row.prop(strip, "location", text="")
776             col.prop(strip, "wrap_width")
777             layout.operator("sequencer.export_subtitles")
778
779         col = layout.column(align=True)
780         if strip.type == 'SPEED':
781             col.prop(strip, "multiply_speed")
782         elif strip.type in {'CROSS', 'GAMMA_CROSS', 'WIPE', 'ALPHA_OVER', 'ALPHA_UNDER', 'OVER_DROP'}:
783             col.prop(strip, "use_default_fade", "Default fade")
784             if not strip.use_default_fade:
785                 col.prop(strip, "effect_fader", text="Effect Fader")
786         elif strip.type == 'GAUSSIAN_BLUR':
787             row = col.row(align=True)
788             row.prop(strip, "size_x")
789             row.prop(strip, "size_y")
790         elif strip.type == 'COLORMIX':
791             split = layout.split(percentage=0.35)
792             split.label(text="Blend Mode:")
793             split.prop(strip, "blend_effect", text="")
794             row = layout.row(align=True)
795             row.prop(strip, "factor", slider=True)
796
797
798 class SEQUENCER_PT_input(SequencerButtonsPanel, Panel):
799     bl_label = "Strip Input"
800     bl_category = "Strip"
801
802     @classmethod
803     def poll(cls, context):
804         if not cls.has_sequencer(context):
805             return False
806
807         strip = act_strip(context)
808         if not strip:
809             return False
810
811         return strip.type in {
812             'MOVIE', 'IMAGE', 'SCENE', 'MOVIECLIP', 'META',
813             'ADD', 'SUBTRACT', 'ALPHA_OVER', 'ALPHA_UNDER',
814             'CROSS', 'GAMMA_CROSS', 'MULTIPLY', 'OVER_DROP',
815             'WIPE', 'GLOW', 'TRANSFORM', 'COLOR',
816             'MULTICAM', 'SPEED', 'ADJUSTMENT', 'COLORMIX'
817         }
818
819     def draw(self, context):
820         layout = self.layout
821         scene = context.scene
822
823         strip = act_strip(context)
824
825         seq_type = strip.type
826
827         # draw a filename if we have one
828         if seq_type == 'IMAGE':
829             split = layout.split(percentage=0.2)
830             split.label(text="Path:")
831             split.prop(strip, "directory", text="")
832
833             # Current element for the filename
834
835             elem = strip.strip_elem_from_frame(scene.frame_current)
836             if elem:
837                 split = layout.split(percentage=0.2)
838                 split.label(text="File:")
839                 split.prop(elem, "filename", text="")  # strip.elements[0] could be a fallback
840
841             split = layout.split(percentage=0.4)
842             split.label(text="Color Space:")
843             split.prop(strip.colorspace_settings, "name", text="")
844
845             split = layout.split(percentage=0.4)
846             split.label(text="Alpha:")
847             split.prop(strip, "alpha_mode", text="")
848
849             layout.operator("sequencer.change_path", icon='FILESEL').filter_image = True
850
851         elif seq_type == 'MOVIE':
852             split = layout.split(percentage=0.2)
853             split.label(text="Path:")
854             split.prop(strip, "filepath", text="")
855
856             split = layout.split(percentage=0.4)
857             split.label(text="Color Space:")
858             split.prop(strip.colorspace_settings, "name", text="")
859
860             layout.prop(strip, "mpeg_preseek")
861             layout.prop(strip, "stream_index")
862
863         layout.prop(strip, "use_translation", text="Image Offset")
864         if strip.use_translation:
865             row = layout.row(align=True)
866             row.prop(strip.transform, "offset_x", text="X")
867             row.prop(strip.transform, "offset_y", text="Y")
868
869         layout.prop(strip, "use_crop", text="Image Crop")
870         if strip.use_crop:
871             col = layout.column(align=True)
872             col.prop(strip.crop, "max_y")
873             row = col.row(align=True)
874             row.prop(strip.crop, "min_x")
875             row.prop(strip.crop, "max_x")
876             col.prop(strip.crop, "min_y")
877
878         if not isinstance(strip, bpy.types.EffectSequence):
879             layout.label(text="Trim Duration (hard):")
880             row = layout.row(align=True)
881             row.prop(strip, "animation_offset_start", text="Start")
882             row.prop(strip, "animation_offset_end", text="End")
883
884         layout.label(text="Trim Duration (soft):")
885         row = layout.row(align=True)
886         row.prop(strip, "frame_offset_start", text="Start")
887         row.prop(strip, "frame_offset_end", text="End")
888
889         if scene.render.use_multiview and seq_type in {'IMAGE', 'MOVIE'}:
890             layout.prop(strip, "use_multiview")
891
892             col = layout.column()
893             col.active = strip.use_multiview
894
895             col.label(text="Views Format:")
896             col.row().prop(strip, "views_format", expand=True)
897
898             box = col.box()
899             box.active = strip.views_format == 'STEREO_3D'
900             box.template_image_stereo_3d(strip.stereo_3d_format)
901
902
903 class SEQUENCER_PT_sound(SequencerButtonsPanel, Panel):
904     bl_label = "Sound"
905     bl_category = "Strip"
906
907     @classmethod
908     def poll(cls, context):
909         if not cls.has_sequencer(context):
910             return False
911
912         strip = act_strip(context)
913         if not strip:
914             return False
915
916         return (strip.type == 'SOUND')
917
918     def draw(self, context):
919         layout = self.layout
920
921         st = context.space_data
922         strip = act_strip(context)
923         sound = strip.sound
924
925         layout.template_ID(strip, "sound", open="sound.open")
926         if sound is not None:
927             layout.prop(sound, "filepath", text="")
928
929             row = layout.row()
930             if sound.packed_file:
931                 row.operator("sound.unpack", icon='PACKAGE', text="Unpack")
932             else:
933                 row.operator("sound.pack", icon='UGLYPACKAGE', text="Pack")
934
935             row.prop(sound, "use_memory_cache")
936
937             layout.prop(sound, "use_mono")
938
939         if st.waveform_draw_type == 'DEFAULT_WAVEFORMS':
940             layout.prop(strip, "show_waveform")
941
942         col = layout.column(align=True)
943         col.prop(strip, "volume")
944         col.prop(strip, "pitch")
945         col.prop(strip, "pan")
946
947         col = layout.column(align=True)
948         col.label(text="Trim Duration (hard):")
949         row = layout.row(align=True)
950         row.prop(strip, "animation_offset_start", text="Start")
951         row.prop(strip, "animation_offset_end", text="End")
952
953         col = layout.column(align=True)
954         col.label(text="Trim Duration (soft):")
955         row = layout.row(align=True)
956         row.prop(strip, "frame_offset_start", text="Start")
957         row.prop(strip, "frame_offset_end", text="End")
958
959
960 class SEQUENCER_PT_scene(SequencerButtonsPanel, Panel):
961     bl_label = "Scene"
962     bl_category = "Strip"
963
964     @classmethod
965     def poll(cls, context):
966         if not cls.has_sequencer(context):
967             return False
968
969         strip = act_strip(context)
970         if not strip:
971             return False
972
973         return (strip.type == 'SCENE')
974
975     def draw(self, context):
976         layout = self.layout
977
978         strip = act_strip(context)
979
980         layout.template_ID(strip, "scene")
981
982         scene = strip.scene
983         layout.prop(strip, "use_sequence")
984
985         if not strip.use_sequence:
986             layout.label(text="Camera Override")
987             layout.template_ID(strip, "scene_camera")
988
989             layout.prop(strip, "use_grease_pencil", text="Show Grease Pencil")
990
991         if scene:
992             layout.prop(scene, "audio_volume", text="Audio Volume")
993
994         if not strip.use_sequence:
995             if scene:
996                 # Warning, this is not a good convention to follow.
997                 # Expose here because setting the alpha from the 'Render' menu is very inconvenient.
998                 layout.label("Preview")
999                 layout.prop(scene.render, "alpha_mode")
1000
1001         if scene:
1002             sta = scene.frame_start
1003             end = scene.frame_end
1004             layout.label(text=iface_("Original frame range: %d-%d (%d)") % (sta, end, end - sta + 1), translate=False)
1005
1006
1007 class SEQUENCER_PT_mask(SequencerButtonsPanel, Panel):
1008     bl_label = "Mask"
1009     bl_category = "Strip"
1010
1011     @classmethod
1012     def poll(cls, context):
1013         if not cls.has_sequencer(context):
1014             return False
1015
1016         strip = act_strip(context)
1017         if not strip:
1018             return False
1019
1020         return (strip.type == 'MASK')
1021
1022     def draw(self, context):
1023         layout = self.layout
1024
1025         strip = act_strip(context)
1026
1027         layout.template_ID(strip, "mask")
1028
1029         mask = strip.mask
1030
1031         if mask:
1032             sta = mask.frame_start
1033             end = mask.frame_end
1034             layout.label(text=iface_("Original frame range: %d-%d (%d)") % (sta, end, end - sta + 1), translate=False)
1035
1036
1037 class SEQUENCER_PT_filter(SequencerButtonsPanel, Panel):
1038     bl_label = "Filter"
1039     bl_category = "Strip"
1040
1041     @classmethod
1042     def poll(cls, context):
1043         if not cls.has_sequencer(context):
1044             return False
1045
1046         strip = act_strip(context)
1047         if not strip:
1048             return False
1049
1050         return strip.type in {
1051             'MOVIE', 'IMAGE', 'SCENE', 'MOVIECLIP', 'MASK',
1052             'META', 'ADD', 'SUBTRACT', 'ALPHA_OVER',
1053             'ALPHA_UNDER', 'CROSS', 'GAMMA_CROSS', 'MULTIPLY',
1054             'OVER_DROP', 'WIPE', 'GLOW', 'TRANSFORM', 'COLOR',
1055             'MULTICAM', 'SPEED', 'ADJUSTMENT', 'COLORMIX'
1056         }
1057
1058     def draw(self, context):
1059         layout = self.layout
1060
1061         strip = act_strip(context)
1062
1063         col = layout.column()
1064         col.label(text="Video:")
1065         col.prop(strip, "strobe")
1066
1067         if strip.type == 'MOVIECLIP':
1068             col = layout.column()
1069             col.label(text="Tracker:")
1070             col.prop(strip, "stabilize2d")
1071
1072             col = layout.column()
1073             col.label(text="Distortion:")
1074             col.prop(strip, "undistort")
1075
1076         split = layout.split(percentage=0.6)
1077         col = split.column()
1078         col.prop(strip, "use_reverse_frames", text="Reverse")
1079         col.prop(strip, "use_deinterlace")
1080
1081         col = split.column()
1082         col.prop(strip, "use_flip_x", text="X Flip")
1083         col.prop(strip, "use_flip_y", text="Y Flip")
1084
1085         layout.label("Color:")
1086         col = layout.column(align=True)
1087         col.prop(strip, "color_saturation", text="Saturation")
1088         col.prop(strip, "color_multiply", text="Multiply")
1089         layout.prop(strip, "use_float", text="Convert to Float")
1090
1091
1092 class SEQUENCER_PT_proxy(SequencerButtonsPanel, Panel):
1093     bl_label = "Proxy/Timecode"
1094     bl_category = "Strip"
1095
1096     @classmethod
1097     def poll(cls, context):
1098         if not cls.has_sequencer(context):
1099             return False
1100
1101         strip = act_strip(context)
1102         if not strip:
1103             return False
1104
1105         return strip.type in {'MOVIE', 'IMAGE', 'SCENE', 'META', 'MULTICAM'}
1106
1107     def draw_header(self, context):
1108         strip = act_strip(context)
1109
1110         self.layout.prop(strip, "use_proxy", text="")
1111
1112     def draw(self, context):
1113         layout = self.layout
1114
1115         ed = context.scene.sequence_editor
1116
1117         strip = act_strip(context)
1118
1119         if strip.proxy:
1120             proxy = strip.proxy
1121
1122             flow = layout.column_flow()
1123             flow.prop(ed, "proxy_storage", text="Storage")
1124             if ed.proxy_storage == 'PROJECT':
1125                 flow.prop(ed, "proxy_dir", text="Directory")
1126             else:
1127                 flow.prop(proxy, "use_proxy_custom_directory")
1128                 flow.prop(proxy, "use_proxy_custom_file")
1129
1130                 if proxy.use_proxy_custom_directory and not proxy.use_proxy_custom_file:
1131                     flow.prop(proxy, "directory")
1132                 if proxy.use_proxy_custom_file:
1133                     flow.prop(proxy, "filepath")
1134
1135             row = layout.row(align=True)
1136             row.prop(strip.proxy, "build_25", toggle=True)
1137             row.prop(strip.proxy, "build_50", toggle=True)
1138             row.prop(strip.proxy, "build_75", toggle=True)
1139             row.prop(strip.proxy, "build_100", toggle=True)
1140
1141             layout.prop(proxy, "use_overwrite")
1142
1143             col = layout.column()
1144             col.prop(proxy, "quality", text="Build JPEG Quality")
1145
1146             if strip.type == 'MOVIE':
1147                 col = layout.column()
1148                 col.label(text="Use timecode index:")
1149
1150                 col.prop(proxy, "timecode")
1151
1152         col = layout.column()
1153         col.operator("sequencer.enable_proxies")
1154         col.operator("sequencer.rebuild_proxy")
1155
1156
1157 class SEQUENCER_PT_preview(SequencerButtonsPanel_Output, Panel):
1158     bl_label = "Scene Preview/Render"
1159     bl_space_type = 'SEQUENCE_EDITOR'
1160     bl_region_type = 'UI'
1161
1162     def draw(self, context):
1163         layout = self.layout
1164
1165         render = context.scene.render
1166
1167         col = layout.column()
1168         col.prop(render, "sequencer_gl_preview", text="")
1169
1170         row = col.row()
1171         row.active = render.sequencer_gl_preview == 'SOLID'
1172         row.prop(render, "use_sequencer_gl_textured_solid")
1173
1174         col.prop(render, "use_sequencer_gl_dof")
1175
1176
1177 class SEQUENCER_PT_view(SequencerButtonsPanel_Output, Panel):
1178     bl_label = "View Settings"
1179
1180     def draw(self, context):
1181         layout = self.layout
1182
1183         st = context.space_data
1184
1185         col = layout.column()
1186         if st.display_mode == 'IMAGE':
1187             col.prop(st, "draw_overexposed")
1188             col.separator()
1189
1190         elif st.display_mode == 'WAVEFORM':
1191             col.prop(st, "show_separate_color")
1192
1193         col = layout.column()
1194         col.separator()
1195         col.prop(st, "proxy_render_size")
1196
1197
1198 class SEQUENCER_PT_view_safe_areas(SequencerButtonsPanel_Output, Panel):
1199     bl_label = "Safe Areas"
1200     bl_options = {'DEFAULT_CLOSED'}
1201
1202     @classmethod
1203     def poll(cls, context):
1204         st = context.space_data
1205         is_preview = st.view_type in {'PREVIEW', 'SEQUENCER_PREVIEW'}
1206         return is_preview and (st.display_mode == 'IMAGE')
1207
1208     def draw_header(self, context):
1209         st = context.space_data
1210
1211         self.layout.prop(st, "show_safe_areas", text="")
1212
1213     def draw(self, context):
1214         from .properties_data_camera import draw_display_safe_settings
1215
1216         layout = self.layout
1217         st = context.space_data
1218         safe_data = context.scene.safe_areas
1219
1220         draw_display_safe_settings(layout, safe_data, st)
1221
1222
1223 class SEQUENCER_PT_modifiers(SequencerButtonsPanel, Panel):
1224     bl_label = "Modifiers"
1225     bl_category = "Modifiers"
1226
1227     def draw(self, context):
1228         layout = self.layout
1229
1230         strip = act_strip(context)
1231         ed = context.scene.sequence_editor
1232
1233         layout.prop(strip, "use_linear_modifiers")
1234
1235         layout.operator_menu_enum("sequencer.strip_modifier_add", "type")
1236         layout.operator("sequencer.strip_modifier_copy")
1237
1238         for mod in strip.modifiers:
1239             box = layout.box()
1240
1241             row = box.row()
1242             row.prop(mod, "show_expanded", text="", emboss=False)
1243             row.prop(mod, "name", text="")
1244
1245             row.prop(mod, "mute", text="")
1246
1247             sub = row.row(align=True)
1248             props = sub.operator("sequencer.strip_modifier_move", text="", icon='TRIA_UP')
1249             props.name = mod.name
1250             props.direction = 'UP'
1251             props = sub.operator("sequencer.strip_modifier_move", text="", icon='TRIA_DOWN')
1252             props.name = mod.name
1253             props.direction = 'DOWN'
1254
1255             row.operator("sequencer.strip_modifier_remove", text="", icon='X', emboss=False).name = mod.name
1256
1257             if mod.show_expanded:
1258                 row = box.row()
1259                 row.prop(mod, "input_mask_type", expand=True)
1260
1261                 if mod.input_mask_type == 'STRIP':
1262                     sequences_object = ed
1263                     if ed.meta_stack:
1264                         sequences_object = ed.meta_stack[-1]
1265                     box.prop_search(mod, "input_mask_strip", sequences_object, "sequences", text="Mask")
1266                 else:
1267                     box.prop(mod, "input_mask_id")
1268                     row = box.row()
1269                     row.prop(mod, "mask_time", expand=True)
1270
1271                 if mod.type == 'COLOR_BALANCE':
1272                     box.prop(mod, "color_multiply")
1273                     draw_color_balance(box, mod.color_balance)
1274                 elif mod.type == 'CURVES':
1275                     box.template_curve_mapping(mod, "curve_mapping", type='COLOR')
1276                 elif mod.type == 'HUE_CORRECT':
1277                     box.template_curve_mapping(mod, "curve_mapping", type='HUE')
1278                 elif mod.type == 'BRIGHT_CONTRAST':
1279                     col = box.column()
1280                     col.prop(mod, "bright")
1281                     col.prop(mod, "contrast")
1282                 elif mod.type == 'WHITE_BALANCE':
1283                     col = box.column()
1284                     col.prop(mod, "white_value")
1285                 elif mod.type == 'TONEMAP':
1286                     col = box.column()
1287                     col.prop(mod, "tonemap_type")
1288                     if mod.tonemap_type == 'RD_PHOTORECEPTOR':
1289                         col.prop(mod, "intensity")
1290                         col.prop(mod, "contrast")
1291                         col.prop(mod, "adaptation")
1292                         col.prop(mod, "correction")
1293                     elif mod.tonemap_type == 'RH_SIMPLE':
1294                         col.prop(mod, "key")
1295                         col.prop(mod, "offset")
1296                         col.prop(mod, "gamma")
1297
1298
1299 class SEQUENCER_PT_grease_pencil(GreasePencilDataPanel, SequencerButtonsPanel_Output, Panel):
1300     bl_space_type = 'SEQUENCE_EDITOR'
1301     bl_region_type = 'UI'
1302
1303     # NOTE: this is just a wrapper around the generic GP Panel
1304     # But, it should only show up when there are images in the preview region
1305
1306
1307 class SEQUENCER_PT_grease_pencil_palettecolor(GreasePencilPaletteColorPanel, SequencerButtonsPanel_Output, Panel):
1308     bl_space_type = 'SEQUENCE_EDITOR'
1309     bl_region_type = 'UI'
1310
1311     # NOTE: this is just a wrapper around the generic GP Panel
1312     # But, it should only show up when there are images in the preview region
1313
1314
1315 class SEQUENCER_PT_grease_pencil_tools(GreasePencilToolsPanel, SequencerButtonsPanel_Output, Panel):
1316     bl_space_type = 'SEQUENCE_EDITOR'
1317     bl_region_type = 'UI'
1318
1319     # NOTE: this is just a wrapper around the generic GP tools panel
1320     # It contains access to some essential tools usually found only in
1321     # toolbar, which doesn't exist here...
1322
1323
1324 class SEQUENCER_PT_custom_props(SequencerButtonsPanel, PropertyPanel, Panel):
1325     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'}
1326     _context_path = "scene.sequence_editor.active_strip"
1327     _property_type = (bpy.types.Sequence,)
1328     bl_category = "Strip"
1329
1330
1331 classes = (
1332     SEQUENCER_MT_change,
1333     SEQUENCER_HT_header,
1334     SEQUENCER_MT_editor_menus,
1335     SEQUENCER_MT_view,
1336     SEQUENCER_MT_view_toggle,
1337     SEQUENCER_MT_select,
1338     SEQUENCER_MT_marker,
1339     SEQUENCER_MT_frame,
1340     SEQUENCER_MT_add,
1341     SEQUENCER_MT_add_generate,
1342     SEQUENCER_MT_add_effect,
1343     SEQUENCER_MT_strip,
1344     SEQUENCER_MT_strip_transform,
1345     SEQUENCER_MT_strip_input,
1346     SEQUENCER_MT_strip_lock_mute,
1347     SEQUENCER_PT_edit,
1348     SEQUENCER_PT_effect,
1349     SEQUENCER_PT_input,
1350     SEQUENCER_PT_sound,
1351     SEQUENCER_PT_scene,
1352     SEQUENCER_PT_mask,
1353     SEQUENCER_PT_filter,
1354     SEQUENCER_PT_proxy,
1355     SEQUENCER_PT_preview,
1356     SEQUENCER_PT_view,
1357     SEQUENCER_PT_view_safe_areas,
1358     SEQUENCER_PT_modifiers,
1359     SEQUENCER_PT_grease_pencil,
1360     SEQUENCER_PT_grease_pencil_palettecolor,
1361     SEQUENCER_PT_grease_pencil_tools,
1362     SEQUENCER_PT_custom_props,
1363 )
1364
1365 if __name__ == "__main__":  # only for live edit.
1366     from bpy.utils import register_class
1367     for cls in classes:
1368         register_class(cls)