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