Cleanup: use f-strings
[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             toolsettings = 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(toolsettings, "proportional_edit", icon_only=True)
128                 if toolsettings.proportional_edit != 'DISABLED':
129                     row.prop(toolsettings, "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_frame(Menu):
284     bl_label = "Frame"
285
286     def draw(self, context):
287         layout = self.layout
288
289         layout.operator("anim.previewrange_clear")
290         layout.operator("anim.previewrange_set")
291
292         layout.separator()
293
294         props = layout.operator("sequencer.strip_jump", text="Jump to Previous Strip")
295         props.next = False
296         props.center = False
297         props = layout.operator("sequencer.strip_jump", text="Jump to Next Strip")
298         props.next = True
299         props.center = False
300
301         layout.separator()
302
303         props = layout.operator("sequencer.strip_jump", text="Jump to Previous Strip (Center)")
304         props.next = False
305         props.center = True
306         props = layout.operator("sequencer.strip_jump", text="Jump to Next Strip (Center)")
307         props.next = True
308         props.center = True
309
310
311 class SEQUENCER_MT_add(Menu):
312     bl_label = "Add"
313
314     def draw(self, context):
315         layout = self.layout
316
317         layout.operator_context = 'INVOKE_REGION_WIN'
318
319         if len(bpy.data.scenes) > 10:
320             layout.operator_context = 'INVOKE_DEFAULT'
321             layout.operator("sequencer.scene_strip_add", text="Scene...")
322         else:
323             layout.operator_menu_enum("sequencer.scene_strip_add", "scene", text="Scene")
324
325         if len(bpy.data.movieclips) > 10:
326             layout.operator_context = 'INVOKE_DEFAULT'
327             layout.operator("sequencer.movieclip_strip_add", text="Clips...")
328         else:
329             layout.operator_menu_enum("sequencer.movieclip_strip_add", "clip", text="Clip")
330
331         if len(bpy.data.masks) > 10:
332             layout.operator_context = 'INVOKE_DEFAULT'
333             layout.operator("sequencer.mask_strip_add", text="Masks...")
334         else:
335             layout.operator_menu_enum("sequencer.mask_strip_add", "mask", text="Mask")
336
337         layout.operator("sequencer.movie_strip_add", text="Movie")
338         layout.operator("sequencer.image_strip_add", text="Image")
339         layout.operator("sequencer.sound_strip_add", text="Sound")
340
341         layout.menu("SEQUENCER_MT_add_effect")
342
343
344 class SEQUENCER_MT_add_effect(Menu):
345     bl_label = "Effect Strip"
346
347     def draw(self, context):
348         layout = self.layout
349
350         layout.operator_context = 'INVOKE_REGION_WIN'
351
352         layout.operator("sequencer.effect_strip_add", text="Add").type = 'ADD'
353         layout.operator("sequencer.effect_strip_add", text="Subtract").type = 'SUBTRACT'
354         layout.operator("sequencer.effect_strip_add", text="Alpha Over").type = 'ALPHA_OVER'
355         layout.operator("sequencer.effect_strip_add", text="Alpha Under").type = 'ALPHA_UNDER'
356         layout.operator("sequencer.effect_strip_add", text="Cross").type = 'CROSS'
357         layout.operator("sequencer.effect_strip_add", text="Gamma Cross").type = 'GAMMA_CROSS'
358         layout.operator("sequencer.effect_strip_add", text="Gaussian Blur").type = 'GAUSSIAN_BLUR'
359         layout.operator("sequencer.effect_strip_add", text="Multiply").type = 'MULTIPLY'
360         layout.operator("sequencer.effect_strip_add", text="Over Drop").type = 'OVER_DROP'
361         layout.operator("sequencer.effect_strip_add", text="Wipe").type = 'WIPE'
362         layout.operator("sequencer.effect_strip_add", text="Glow").type = 'GLOW'
363         layout.operator("sequencer.effect_strip_add", text="Text").type = 'TEXT'
364         layout.operator("sequencer.effect_strip_add", text="Color Mix").type = 'COLORMIX'
365         layout.operator("sequencer.effect_strip_add", text="Transform").type = 'TRANSFORM'
366         layout.operator("sequencer.effect_strip_add", text="Color").type = 'COLOR'
367         layout.operator("sequencer.effect_strip_add", text="Speed Control").type = 'SPEED'
368         layout.operator("sequencer.effect_strip_add", text="Multicam Selector").type = 'MULTICAM'
369         layout.operator("sequencer.effect_strip_add", text="Adjustment Layer").type = 'ADJUSTMENT'
370
371
372 class SEQUENCER_MT_strip_transform(Menu):
373     bl_label = "Transform"
374
375     def draw(self, context):
376         layout = self.layout
377
378         layout.operator("transform.transform", text="Grab/Move").mode = 'TRANSLATION'
379         layout.operator("transform.transform", text="Grab/Extend from Frame").mode = 'TIME_EXTEND'
380         layout.operator("sequencer.slip", text="Slip Strip Contents")
381
382         layout.separator()
383         layout.operator_menu_enum("sequencer.swap", "side")
384
385         layout.separator()
386         layout.operator("sequencer.gap_remove").all = False
387         layout.operator("sequencer.gap_insert")
388
389
390 class SEQUENCER_MT_strip_input(Menu):
391     bl_label = "Inputs"
392
393     def draw(self, context):
394         layout = self.layout
395         strip = act_strip(context)
396
397         layout.operator("sequencer.reload", text="Reload Strips")
398         layout.operator("sequencer.reload", text="Reload Strips and Adjust Length").adjust_length = True
399         prop = layout.operator("sequencer.change_path", text="Change Path/Files")
400         layout.operator("sequencer.swap_data", text="Swap Data")
401
402         if strip:
403             stype = strip.type
404
405             if stype == 'IMAGE':
406                 prop.filter_image = True
407             elif stype == 'MOVIE':
408                 prop.filter_movie = True
409             elif stype == 'SOUND':
410                 prop.filter_sound = True
411
412
413 class SEQUENCER_MT_strip_lock_mute(Menu):
414     bl_label = "Lock/Mute"
415
416     def draw(self, context):
417         layout = self.layout
418
419         layout.operator("sequencer.lock")
420         layout.operator("sequencer.unlock")
421         layout.operator("sequencer.mute").unselected = False
422         layout.operator("sequencer.unmute").unselected = False
423         layout.operator("sequencer.mute", text="Mute Deselected Strips").unselected = True
424
425
426 class SEQUENCER_MT_strip(Menu):
427     bl_label = "Strip"
428
429     def draw(self, context):
430         layout = self.layout
431
432         layout.operator_context = 'INVOKE_REGION_WIN'
433
434         layout.operator("ed.undo")
435         layout.operator("ed.redo")
436         layout.operator("ed.undo_history")
437
438         layout.separator()
439         layout.menu("SEQUENCER_MT_strip_transform")
440         layout.operator("sequencer.snap")
441         layout.operator("sequencer.offset_clear")
442
443         layout.separator()
444         layout.operator("sequencer.duplicate_move")
445         layout.operator("sequencer.delete", text="Delete...")
446
447         layout.separator()
448         layout.operator("sequencer.cut", text="Cut (Hard) at frame").type = 'HARD'
449         layout.operator("sequencer.cut", text="Cut (Soft) at frame").type = 'SOFT'
450
451         layout.separator()
452         layout.operator("sequencer.deinterlace_selected_movies")
453         layout.operator("sequencer.rebuild_proxy")
454
455         strip = act_strip(context)
456
457         if strip:
458             stype = strip.type
459
460             if stype in {
461                     'CROSS', 'ADD', 'SUBTRACT', 'ALPHA_OVER', 'ALPHA_UNDER',
462                     'GAMMA_CROSS', 'MULTIPLY', 'OVER_DROP', 'WIPE', 'GLOW',
463                     'TRANSFORM', 'COLOR', 'SPEED', 'MULTICAM', 'ADJUSTMENT',
464                     'GAUSSIAN_BLUR', 'TEXT',
465             }:
466                 layout.separator()
467                 layout.operator_menu_enum("sequencer.change_effect_input", "swap")
468                 layout.operator_menu_enum("sequencer.change_effect_type", "type")
469                 layout.operator("sequencer.reassign_inputs")
470                 layout.operator("sequencer.swap_inputs")
471             elif stype in {'IMAGE', 'MOVIE'}:
472                 layout.separator()
473                 layout.operator("sequencer.rendersize")
474                 layout.operator("sequencer.images_separate")
475             elif stype == 'SOUND':
476                 layout.separator()
477                 layout.operator("sequencer.crossfade_sounds")
478             elif stype == 'META':
479                 layout.separator()
480                 layout.operator("sequencer.meta_separate")
481
482         layout.separator()
483         layout.operator("sequencer.meta_make")
484
485         layout.separator()
486         layout.menu("SEQUENCER_MT_strip_input")
487
488         layout.separator()
489         layout.menu("SEQUENCER_MT_strip_lock_mute")
490
491
492 class SequencerButtonsPanel:
493     bl_space_type = 'SEQUENCE_EDITOR'
494     bl_region_type = 'UI'
495
496     @staticmethod
497     def has_sequencer(context):
498         return (context.space_data.view_type in {'SEQUENCER', 'SEQUENCER_PREVIEW'})
499
500     @classmethod
501     def poll(cls, context):
502         return cls.has_sequencer(context) and (act_strip(context) is not None)
503
504
505 class SequencerButtonsPanel_Output:
506     bl_space_type = 'SEQUENCE_EDITOR'
507     bl_region_type = 'UI'
508
509     @staticmethod
510     def has_preview(context):
511         st = context.space_data
512         return (st.view_type in {'PREVIEW', 'SEQUENCER_PREVIEW'}) or st.show_backdrop
513
514     @classmethod
515     def poll(cls, context):
516         return cls.has_preview(context)
517
518
519 class SEQUENCER_PT_edit(SequencerButtonsPanel, Panel):
520     bl_label = "Edit Strip"
521     bl_category = "Strip"
522
523     def draw(self, context):
524         layout = self.layout
525
526         scene = context.scene
527         frame_current = scene.frame_current
528         strip = act_strip(context)
529
530         split = layout.split(percentage=0.25)
531         split.label(text="Name:")
532         split.prop(strip, "name", text="")
533
534         split = layout.split(percentage=0.25)
535         split.label(text="Type:")
536         split.prop(strip, "type", text="")
537
538         if strip.type != 'SOUND':
539             split = layout.split(percentage=0.25)
540             split.label(text="Blend:")
541             split.prop(strip, "blend_type", text="")
542
543             row = layout.row(align=True)
544             sub = row.row(align=True)
545             sub.active = (not strip.mute)
546             sub.prop(strip, "blend_alpha", text="Opacity", slider=True)
547             row.prop(strip, "mute", toggle=True, icon_only=True)
548
549         else:
550             row = layout.row()
551             row.prop(strip, "mute", toggle=True, icon_only=True, icon='MUTE_IPO_OFF')
552
553         col = layout.column(align=True)
554         row = col.row(align=True)
555
556         row_sub = row.row(align=True)
557         row_sub.enabled = not strip.lock
558         row_sub.prop(strip, "channel")
559         row.prop(strip, "lock", toggle=True, icon_only=True)
560
561         sub = col.column(align=True)
562         sub.enabled = not strip.lock
563         sub.prop(strip, "frame_start")
564         sub.prop(strip, "frame_final_duration")
565
566         col = layout.column(align=True)
567         row = col.row(align=True)
568         row.label(text=iface_("Final Length: %s") % bpy.utils.smpte_from_frame(strip.frame_final_duration),
569                   translate=False)
570         row = col.row(align=True)
571         row.active = (frame_current >= strip.frame_start and frame_current <= strip.frame_start + strip.frame_duration)
572         row.label(text=iface_("Playhead: %d") % (frame_current - strip.frame_start), translate=False)
573
574         col.label(text=iface_("Frame Offset %d:%d") % (strip.frame_offset_start, strip.frame_offset_end),
575                   translate=False)
576         col.label(text=iface_("Frame Still %d:%d") % (strip.frame_still_start, strip.frame_still_end), translate=False)
577
578         elem = False
579
580         if strip.type == 'IMAGE':
581             elem = strip.strip_elem_from_frame(frame_current)
582         elif strip.type == 'MOVIE':
583             elem = strip.elements[0]
584
585         if elem and elem.orig_width > 0 and elem.orig_height > 0:
586             col.label(text=iface_("Original Dimension: %dx%d") % (elem.orig_width, elem.orig_height), translate=False)
587         else:
588             col.label(text="Original Dimension: None")
589
590
591 class SEQUENCER_PT_effect(SequencerButtonsPanel, Panel):
592     bl_label = "Effect Strip"
593     bl_category = "Strip"
594
595     @classmethod
596     def poll(cls, context):
597         if not cls.has_sequencer(context):
598             return False
599
600         strip = act_strip(context)
601         if not strip:
602             return False
603
604         return strip.type in {
605             'ADD', 'SUBTRACT', 'ALPHA_OVER', 'ALPHA_UNDER',
606             'CROSS', 'GAMMA_CROSS', 'MULTIPLY', 'OVER_DROP',
607             'WIPE', 'GLOW', 'TRANSFORM', 'COLOR', 'SPEED',
608             'MULTICAM', 'GAUSSIAN_BLUR', 'TEXT', 'COLORMIX'
609         }
610
611     def draw(self, context):
612         layout = self.layout
613
614         strip = act_strip(context)
615
616         if strip.input_count > 0:
617             col = layout.column()
618             col.enabled = False
619             col.prop(strip, "input_1")
620             if strip.input_count > 1:
621                 col.prop(strip, "input_2")
622
623         if strip.type == 'COLOR':
624             layout.prop(strip, "color")
625
626         elif strip.type == 'WIPE':
627             col = layout.column()
628             col.prop(strip, "transition_type")
629             col.label(text="Direction:")
630             col.row().prop(strip, "direction", expand=True)
631
632             col = layout.column()
633             col.prop(strip, "blur_width", slider=True)
634             if strip.transition_type in {'SINGLE', 'DOUBLE'}:
635                 col.prop(strip, "angle")
636
637         elif strip.type == 'GLOW':
638             flow = layout.column_flow()
639             flow.prop(strip, "threshold", slider=True)
640             flow.prop(strip, "clamp", slider=True)
641             flow.prop(strip, "boost_factor")
642             flow.prop(strip, "blur_radius")
643
644             row = layout.row()
645             row.prop(strip, "quality", slider=True)
646             row.prop(strip, "use_only_boost")
647
648         elif strip.type == 'SPEED':
649             layout.prop(strip, "use_default_fade", "Stretch to input strip length")
650             if not strip.use_default_fade:
651                 layout.prop(strip, "use_as_speed")
652                 if strip.use_as_speed:
653                     layout.prop(strip, "speed_factor")
654                 else:
655                     layout.prop(strip, "speed_factor", text="Frame Number")
656                     layout.prop(strip, "scale_to_length")
657
658         elif strip.type == 'TRANSFORM':
659             layout = self.layout
660             col = layout.column()
661
662             col.prop(strip, "interpolation")
663             col.prop(strip, "translation_unit")
664             col = layout.column(align=True)
665             col.label(text="Position:")
666             row = col.row(align=True)
667             row.prop(strip, "translate_start_x", text="X")
668             row.prop(strip, "translate_start_y", text="Y")
669
670             layout.separator()
671
672             col = layout.column(align=True)
673             col.prop(strip, "use_uniform_scale")
674             if strip.use_uniform_scale:
675                 col = layout.column(align=True)
676                 col.prop(strip, "scale_start_x", text="Scale")
677             else:
678                 col = layout.column(align=True)
679                 col.label(text="Scale:")
680                 row = col.row(align=True)
681                 row.prop(strip, "scale_start_x", text="X")
682                 row.prop(strip, "scale_start_y", text="Y")
683
684             layout.separator()
685
686             col = layout.column(align=True)
687             col.label(text="Rotation:")
688             col.prop(strip, "rotation_start", text="Rotation")
689
690         elif strip.type == 'MULTICAM':
691             col = layout.column(align=True)
692             strip_channel = strip.channel
693
694             col.prop(strip, "multicam_source", text="Source Channel")
695
696             # The multicam strip needs at least 2 strips to be useful
697             if strip_channel > 2:
698                 BT_ROW = 4
699
700                 col.label("Cut To:")
701                 row = col.row()
702
703                 for i in range(1, strip_channel):
704                     if (i % BT_ROW) == 1:
705                         row = col.row(align=True)
706
707                     # Workaround - .enabled has to have a separate UI block to work
708                     if i == strip.multicam_source:
709                         sub = row.row(align=True)
710                         sub.enabled = False
711                         sub.operator("sequencer.cut_multicam", text=f"{i:d}").camera = i
712                     else:
713                         sub_1 = row.row(align=True)
714                         sub_1.enabled = True
715                         sub_1.operator("sequencer.cut_multicam", text=f"{i:d}").camera = i
716
717                 if strip.channel > BT_ROW and (strip_channel - 1) % BT_ROW:
718                     for i in range(strip.channel, strip_channel + ((BT_ROW + 1 - strip_channel) % BT_ROW)):
719                         row.label("")
720             else:
721                 col.separator()
722                 col.label(text="Two or more channels are needed below this strip", icon='INFO')
723
724         elif strip.type == 'TEXT':
725             col = layout.column()
726             col.prop(strip, "text")
727             col.prop(strip, "font_size")
728
729             row = col.row()
730             row.prop(strip, "color")
731             row = col.row()
732             row.prop(strip, "use_shadow")
733             rowsub = row.row()
734             rowsub.active = strip.use_shadow
735             rowsub.prop(strip, "shadow_color", text="")
736
737             col.prop(strip, "align_x")
738             col.prop(strip, "align_y")
739             col.label("Location")
740             row = col.row(align=True)
741             row.prop(strip, "location", text="")
742             col.prop(strip, "wrap_width")
743             layout.operator("sequencer.export_subtitles")
744
745         col = layout.column(align=True)
746         if strip.type == 'SPEED':
747             col.prop(strip, "multiply_speed")
748         elif strip.type in {'CROSS', 'GAMMA_CROSS', 'WIPE', 'ALPHA_OVER', 'ALPHA_UNDER', 'OVER_DROP'}:
749             col.prop(strip, "use_default_fade", "Default fade")
750             if not strip.use_default_fade:
751                 col.prop(strip, "effect_fader", text="Effect Fader")
752         elif strip.type == 'GAUSSIAN_BLUR':
753             row = col.row(align=True)
754             row.prop(strip, "size_x")
755             row.prop(strip, "size_y")
756         elif strip.type == 'COLORMIX':
757             split = layout.split(percentage=0.35)
758             split.label(text="Blend Mode:")
759             split.prop(strip, "blend_effect", text="")
760             row = layout.row(align=True)
761             row.prop(strip, "factor", slider=True)
762
763
764 class SEQUENCER_PT_input(SequencerButtonsPanel, Panel):
765     bl_label = "Strip Input"
766     bl_category = "Strip"
767
768     @classmethod
769     def poll(cls, context):
770         if not cls.has_sequencer(context):
771             return False
772
773         strip = act_strip(context)
774         if not strip:
775             return False
776
777         return strip.type in {
778             'MOVIE', 'IMAGE', 'SCENE', 'MOVIECLIP', 'META',
779             'ADD', 'SUBTRACT', 'ALPHA_OVER', 'ALPHA_UNDER',
780             'CROSS', 'GAMMA_CROSS', 'MULTIPLY', 'OVER_DROP',
781             'WIPE', 'GLOW', 'TRANSFORM', 'COLOR',
782             'MULTICAM', 'SPEED', 'ADJUSTMENT', 'COLORMIX'
783         }
784
785     def draw(self, context):
786         layout = self.layout
787         scene = context.scene
788
789         strip = act_strip(context)
790
791         seq_type = strip.type
792
793         # draw a filename if we have one
794         if seq_type == 'IMAGE':
795             split = layout.split(percentage=0.2)
796             split.label(text="Path:")
797             split.prop(strip, "directory", text="")
798
799             # Current element for the filename
800
801             elem = strip.strip_elem_from_frame(scene.frame_current)
802             if elem:
803                 split = layout.split(percentage=0.2)
804                 split.label(text="File:")
805                 split.prop(elem, "filename", text="")  # strip.elements[0] could be a fallback
806
807             split = layout.split(percentage=0.4)
808             split.label(text="Color Space:")
809             split.prop(strip.colorspace_settings, "name", text="")
810
811             split = layout.split(percentage=0.4)
812             split.label(text="Alpha:")
813             split.prop(strip, "alpha_mode", text="")
814
815             layout.operator("sequencer.change_path", icon='FILESEL').filter_image = True
816
817         elif seq_type == 'MOVIE':
818             split = layout.split(percentage=0.2)
819             split.label(text="Path:")
820             split.prop(strip, "filepath", text="")
821
822             split = layout.split(percentage=0.4)
823             split.label(text="Color Space:")
824             split.prop(strip.colorspace_settings, "name", text="")
825
826             layout.prop(strip, "mpeg_preseek")
827             layout.prop(strip, "stream_index")
828
829         layout.prop(strip, "use_translation", text="Image Offset")
830         if strip.use_translation:
831             row = layout.row(align=True)
832             row.prop(strip.transform, "offset_x", text="X")
833             row.prop(strip.transform, "offset_y", text="Y")
834
835         layout.prop(strip, "use_crop", text="Image Crop")
836         if strip.use_crop:
837             col = layout.column(align=True)
838             col.prop(strip.crop, "max_y")
839             row = col.row(align=True)
840             row.prop(strip.crop, "min_x")
841             row.prop(strip.crop, "max_x")
842             col.prop(strip.crop, "min_y")
843
844         if not isinstance(strip, bpy.types.EffectSequence):
845             layout.label(text="Trim Duration (hard):")
846             row = layout.row(align=True)
847             row.prop(strip, "animation_offset_start", text="Start")
848             row.prop(strip, "animation_offset_end", text="End")
849
850         layout.label(text="Trim Duration (soft):")
851         row = layout.row(align=True)
852         row.prop(strip, "frame_offset_start", text="Start")
853         row.prop(strip, "frame_offset_end", text="End")
854
855         if scene.render.use_multiview and seq_type in {'IMAGE', 'MOVIE'}:
856             layout.prop(strip, "use_multiview")
857
858             col = layout.column()
859             col.active = strip.use_multiview
860
861             col.label(text="Views Format:")
862             col.row().prop(strip, "views_format", expand=True)
863
864             box = col.box()
865             box.active = strip.views_format == 'STEREO_3D'
866             box.template_image_stereo_3d(strip.stereo_3d_format)
867
868
869 class SEQUENCER_PT_sound(SequencerButtonsPanel, Panel):
870     bl_label = "Sound"
871     bl_category = "Strip"
872
873     @classmethod
874     def poll(cls, context):
875         if not cls.has_sequencer(context):
876             return False
877
878         strip = act_strip(context)
879         if not strip:
880             return False
881
882         return (strip.type == 'SOUND')
883
884     def draw(self, context):
885         layout = self.layout
886
887         st = context.space_data
888         strip = act_strip(context)
889         sound = strip.sound
890
891         layout.template_ID(strip, "sound", open="sound.open")
892         if sound is not None:
893             layout.prop(sound, "filepath", text="")
894
895             row = layout.row()
896             if sound.packed_file:
897                 row.operator("sound.unpack", icon='PACKAGE', text="Unpack")
898             else:
899                 row.operator("sound.pack", icon='UGLYPACKAGE', text="Pack")
900
901             row.prop(sound, "use_memory_cache")
902
903             layout.prop(sound, "use_mono")
904
905         if st.waveform_draw_type == 'DEFAULT_WAVEFORMS':
906             layout.prop(strip, "show_waveform")
907
908         col = layout.column(align=True)
909         col.prop(strip, "volume")
910         col.prop(strip, "pitch")
911         col.prop(strip, "pan")
912
913         col = layout.column(align=True)
914         col.label(text="Trim Duration (hard):")
915         row = layout.row(align=True)
916         row.prop(strip, "animation_offset_start", text="Start")
917         row.prop(strip, "animation_offset_end", text="End")
918
919         col = layout.column(align=True)
920         col.label(text="Trim Duration (soft):")
921         row = layout.row(align=True)
922         row.prop(strip, "frame_offset_start", text="Start")
923         row.prop(strip, "frame_offset_end", text="End")
924
925
926 class SEQUENCER_PT_scene(SequencerButtonsPanel, Panel):
927     bl_label = "Scene"
928     bl_category = "Strip"
929
930     @classmethod
931     def poll(cls, context):
932         if not cls.has_sequencer(context):
933             return False
934
935         strip = act_strip(context)
936         if not strip:
937             return False
938
939         return (strip.type == 'SCENE')
940
941     def draw(self, context):
942         layout = self.layout
943
944         strip = act_strip(context)
945
946         layout.template_ID(strip, "scene")
947
948         scene = strip.scene
949         layout.prop(strip, "use_sequence")
950
951         if not strip.use_sequence:
952             layout.label(text="Camera Override")
953             layout.template_ID(strip, "scene_camera")
954
955             layout.prop(strip, "use_grease_pencil", text="Show Grease Pencil")
956
957         if scene:
958             layout.prop(scene, "audio_volume", text="Audio Volume")
959
960         if not strip.use_sequence:
961             if scene:
962                 # Warning, this is not a good convention to follow.
963                 # Expose here because setting the alpha from the 'Render' menu is very inconvenient.
964                 layout.label("Preview")
965                 layout.prop(scene.render, "alpha_mode")
966
967         if scene:
968             sta = scene.frame_start
969             end = scene.frame_end
970             layout.label(text=iface_("Original frame range: %d-%d (%d)") % (sta, end, end - sta + 1), translate=False)
971
972
973 class SEQUENCER_PT_mask(SequencerButtonsPanel, Panel):
974     bl_label = "Mask"
975     bl_category = "Strip"
976
977     @classmethod
978     def poll(cls, context):
979         if not cls.has_sequencer(context):
980             return False
981
982         strip = act_strip(context)
983         if not strip:
984             return False
985
986         return (strip.type == 'MASK')
987
988     def draw(self, context):
989         layout = self.layout
990
991         strip = act_strip(context)
992
993         layout.template_ID(strip, "mask")
994
995         mask = strip.mask
996
997         if mask:
998             sta = mask.frame_start
999             end = mask.frame_end
1000             layout.label(text=iface_("Original frame range: %d-%d (%d)") % (sta, end, end - sta + 1), translate=False)
1001
1002
1003 class SEQUENCER_PT_filter(SequencerButtonsPanel, Panel):
1004     bl_label = "Filter"
1005     bl_category = "Strip"
1006
1007     @classmethod
1008     def poll(cls, context):
1009         if not cls.has_sequencer(context):
1010             return False
1011
1012         strip = act_strip(context)
1013         if not strip:
1014             return False
1015
1016         return strip.type in {
1017             'MOVIE', 'IMAGE', 'SCENE', 'MOVIECLIP', 'MASK',
1018             'META', 'ADD', 'SUBTRACT', 'ALPHA_OVER',
1019             'ALPHA_UNDER', 'CROSS', 'GAMMA_CROSS', 'MULTIPLY',
1020             'OVER_DROP', 'WIPE', 'GLOW', 'TRANSFORM', 'COLOR',
1021             'MULTICAM', 'SPEED', 'ADJUSTMENT', 'COLORMIX'
1022         }
1023
1024     def draw(self, context):
1025         layout = self.layout
1026
1027         strip = act_strip(context)
1028
1029         col = layout.column()
1030         col.label(text="Video:")
1031         col.prop(strip, "strobe")
1032
1033         if strip.type == 'MOVIECLIP':
1034             col = layout.column()
1035             col.label(text="Tracker:")
1036             col.prop(strip, "stabilize2d")
1037
1038             col = layout.column()
1039             col.label(text="Distortion:")
1040             col.prop(strip, "undistort")
1041
1042         split = layout.split(percentage=0.6)
1043         col = split.column()
1044         col.prop(strip, "use_reverse_frames", text="Reverse")
1045         col.prop(strip, "use_deinterlace")
1046
1047         col = split.column()
1048         col.prop(strip, "use_flip_x", text="X Flip")
1049         col.prop(strip, "use_flip_y", text="Y Flip")
1050
1051         layout.label("Color:")
1052         col = layout.column(align=True)
1053         col.prop(strip, "color_saturation", text="Saturation")
1054         col.prop(strip, "color_multiply", text="Multiply")
1055         layout.prop(strip, "use_float", text="Convert to Float")
1056
1057
1058 class SEQUENCER_PT_proxy(SequencerButtonsPanel, Panel):
1059     bl_label = "Proxy/Timecode"
1060     bl_category = "Strip"
1061
1062     @classmethod
1063     def poll(cls, context):
1064         if not cls.has_sequencer(context):
1065             return False
1066
1067         strip = act_strip(context)
1068         if not strip:
1069             return False
1070
1071         return strip.type in {'MOVIE', 'IMAGE', 'SCENE', 'META', 'MULTICAM'}
1072
1073     def draw_header(self, context):
1074         strip = act_strip(context)
1075
1076         self.layout.prop(strip, "use_proxy", text="")
1077
1078     def draw(self, context):
1079         layout = self.layout
1080
1081         ed = context.scene.sequence_editor
1082
1083         strip = act_strip(context)
1084
1085         if strip.proxy:
1086             proxy = strip.proxy
1087
1088             flow = layout.column_flow()
1089             flow.prop(ed, "proxy_storage", text="Storage")
1090             if ed.proxy_storage == 'PROJECT':
1091                 flow.prop(ed, "proxy_dir", text="Directory")
1092             else:
1093                 flow.prop(proxy, "use_proxy_custom_directory")
1094                 flow.prop(proxy, "use_proxy_custom_file")
1095
1096                 if proxy.use_proxy_custom_directory and not proxy.use_proxy_custom_file:
1097                     flow.prop(proxy, "directory")
1098                 if proxy.use_proxy_custom_file:
1099                     flow.prop(proxy, "filepath")
1100
1101             row = layout.row(align=True)
1102             row.prop(strip.proxy, "build_25", toggle=True)
1103             row.prop(strip.proxy, "build_50", toggle=True)
1104             row.prop(strip.proxy, "build_75", toggle=True)
1105             row.prop(strip.proxy, "build_100", toggle=True)
1106
1107             layout.prop(proxy, "use_overwrite")
1108
1109             col = layout.column()
1110             col.prop(proxy, "quality", text="Build JPEG Quality")
1111
1112             if strip.type == 'MOVIE':
1113                 col = layout.column()
1114                 col.label(text="Use timecode index:")
1115
1116                 col.prop(proxy, "timecode")
1117
1118         col = layout.column()
1119         col.operator("sequencer.enable_proxies")
1120         col.operator("sequencer.rebuild_proxy")
1121
1122
1123 class SEQUENCER_PT_preview(SequencerButtonsPanel_Output, Panel):
1124     bl_label = "Scene Preview/Render"
1125     bl_space_type = 'SEQUENCE_EDITOR'
1126     bl_region_type = 'UI'
1127
1128     def draw(self, context):
1129         layout = self.layout
1130
1131         render = context.scene.render
1132
1133         col = layout.column()
1134         col.prop(render, "sequencer_gl_preview", text="")
1135
1136         row = col.row()
1137         row.active = render.sequencer_gl_preview == 'SOLID'
1138         row.prop(render, "use_sequencer_gl_textured_solid")
1139
1140         col.prop(render, "use_sequencer_gl_dof")
1141
1142
1143 class SEQUENCER_PT_view(SequencerButtonsPanel_Output, Panel):
1144     bl_label = "View Settings"
1145
1146     def draw(self, context):
1147         layout = self.layout
1148
1149         st = context.space_data
1150
1151         col = layout.column()
1152         if st.display_mode == 'IMAGE':
1153             col.prop(st, "draw_overexposed")
1154             col.separator()
1155
1156         elif st.display_mode == 'WAVEFORM':
1157             col.prop(st, "show_separate_color")
1158
1159         col = layout.column()
1160         col.separator()
1161         col.prop(st, "proxy_render_size")
1162
1163
1164 class SEQUENCER_PT_view_safe_areas(SequencerButtonsPanel_Output, Panel):
1165     bl_label = "Safe Areas"
1166     bl_options = {'DEFAULT_CLOSED'}
1167
1168     @classmethod
1169     def poll(cls, context):
1170         st = context.space_data
1171         is_preview = st.view_type in {'PREVIEW', 'SEQUENCER_PREVIEW'}
1172         return is_preview and (st.display_mode == 'IMAGE')
1173
1174     def draw_header(self, context):
1175         st = context.space_data
1176
1177         self.layout.prop(st, "show_safe_areas", text="")
1178
1179     def draw(self, context):
1180         from .properties_data_camera import draw_display_safe_settings
1181
1182         layout = self.layout
1183         st = context.space_data
1184         safe_data = context.scene.safe_areas
1185
1186         draw_display_safe_settings(layout, safe_data, st)
1187
1188
1189 class SEQUENCER_PT_modifiers(SequencerButtonsPanel, Panel):
1190     bl_label = "Modifiers"
1191     bl_category = "Modifiers"
1192
1193     def draw(self, context):
1194         layout = self.layout
1195
1196         strip = act_strip(context)
1197         ed = context.scene.sequence_editor
1198
1199         layout.prop(strip, "use_linear_modifiers")
1200
1201         layout.operator_menu_enum("sequencer.strip_modifier_add", "type")
1202         layout.operator("sequencer.strip_modifier_copy")
1203
1204         for mod in strip.modifiers:
1205             box = layout.box()
1206
1207             row = box.row()
1208             row.prop(mod, "show_expanded", text="", emboss=False)
1209             row.prop(mod, "name", text="")
1210
1211             row.prop(mod, "mute", text="")
1212
1213             sub = row.row(align=True)
1214             props = sub.operator("sequencer.strip_modifier_move", text="", icon='TRIA_UP')
1215             props.name = mod.name
1216             props.direction = 'UP'
1217             props = sub.operator("sequencer.strip_modifier_move", text="", icon='TRIA_DOWN')
1218             props.name = mod.name
1219             props.direction = 'DOWN'
1220
1221             row.operator("sequencer.strip_modifier_remove", text="", icon='X', emboss=False).name = mod.name
1222
1223             if mod.show_expanded:
1224                 row = box.row()
1225                 row.prop(mod, "input_mask_type", expand=True)
1226
1227                 if mod.input_mask_type == 'STRIP':
1228                     sequences_object = ed
1229                     if ed.meta_stack:
1230                         sequences_object = ed.meta_stack[-1]
1231                     box.prop_search(mod, "input_mask_strip", sequences_object, "sequences", text="Mask")
1232                 else:
1233                     box.prop(mod, "input_mask_id")
1234                     row = box.row()
1235                     row.prop(mod, "mask_time", expand=True)
1236
1237                 if mod.type == 'COLOR_BALANCE':
1238                     box.prop(mod, "color_multiply")
1239                     draw_color_balance(box, mod.color_balance)
1240                 elif mod.type == 'CURVES':
1241                     box.template_curve_mapping(mod, "curve_mapping", type='COLOR')
1242                 elif mod.type == 'HUE_CORRECT':
1243                     box.template_curve_mapping(mod, "curve_mapping", type='HUE')
1244                 elif mod.type == 'BRIGHT_CONTRAST':
1245                     col = box.column()
1246                     col.prop(mod, "bright")
1247                     col.prop(mod, "contrast")
1248                 elif mod.type == 'WHITE_BALANCE':
1249                     col = box.column()
1250                     col.prop(mod, "white_value")
1251                 elif mod.type == 'TONEMAP':
1252                     col = box.column()
1253                     col.prop(mod, "tonemap_type")
1254                     if mod.tonemap_type == 'RD_PHOTORECEPTOR':
1255                         col.prop(mod, "intensity")
1256                         col.prop(mod, "contrast")
1257                         col.prop(mod, "adaptation")
1258                         col.prop(mod, "correction")
1259                     elif mod.tonemap_type == 'RH_SIMPLE':
1260                         col.prop(mod, "key")
1261                         col.prop(mod, "offset")
1262                         col.prop(mod, "gamma")
1263
1264
1265 class SEQUENCER_PT_grease_pencil(GreasePencilDataPanel, SequencerButtonsPanel_Output, Panel):
1266     bl_space_type = 'SEQUENCE_EDITOR'
1267     bl_region_type = 'UI'
1268
1269     # NOTE: this is just a wrapper around the generic GP Panel
1270     # But, it should only show up when there are images in the preview region
1271
1272
1273 class SEQUENCER_PT_grease_pencil_palettecolor(GreasePencilPaletteColorPanel, SequencerButtonsPanel_Output, Panel):
1274     bl_space_type = 'SEQUENCE_EDITOR'
1275     bl_region_type = 'UI'
1276
1277     # NOTE: this is just a wrapper around the generic GP Panel
1278     # But, it should only show up when there are images in the preview region
1279
1280
1281 class SEQUENCER_PT_grease_pencil_tools(GreasePencilToolsPanel, SequencerButtonsPanel_Output, Panel):
1282     bl_space_type = 'SEQUENCE_EDITOR'
1283     bl_region_type = 'UI'
1284
1285     # NOTE: this is just a wrapper around the generic GP tools panel
1286     # It contains access to some essential tools usually found only in
1287     # toolbar, which doesn't exist here...
1288
1289
1290 class SEQUENCER_PT_custom_props(SequencerButtonsPanel, PropertyPanel, Panel):
1291     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'}
1292     _context_path = "scene.sequence_editor.active_strip"
1293     _property_type = (bpy.types.Sequence,)
1294     bl_category = "Strip"
1295
1296
1297 classes = (
1298     SEQUENCER_HT_header,
1299     SEQUENCER_MT_editor_menus,
1300     SEQUENCER_MT_view,
1301     SEQUENCER_MT_view_toggle,
1302     SEQUENCER_MT_select,
1303     SEQUENCER_MT_marker,
1304     SEQUENCER_MT_frame,
1305     SEQUENCER_MT_add,
1306     SEQUENCER_MT_add_effect,
1307     SEQUENCER_MT_strip,
1308     SEQUENCER_MT_strip_transform,
1309     SEQUENCER_MT_strip_input,
1310     SEQUENCER_MT_strip_lock_mute,
1311     SEQUENCER_PT_edit,
1312     SEQUENCER_PT_effect,
1313     SEQUENCER_PT_input,
1314     SEQUENCER_PT_sound,
1315     SEQUENCER_PT_scene,
1316     SEQUENCER_PT_mask,
1317     SEQUENCER_PT_filter,
1318     SEQUENCER_PT_proxy,
1319     SEQUENCER_PT_preview,
1320     SEQUENCER_PT_view,
1321     SEQUENCER_PT_view_safe_areas,
1322     SEQUENCER_PT_modifiers,
1323     SEQUENCER_PT_grease_pencil,
1324     SEQUENCER_PT_grease_pencil_palettecolor,
1325     SEQUENCER_PT_grease_pencil_tools,
1326     SEQUENCER_PT_custom_props,
1327 )
1328
1329 if __name__ == "__main__":  # only for live edit.
1330     from bpy.utils import register_class
1331     for cls in classes:
1332         register_class(cls)