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