iFix T52050: Empty VSE preview for scene strips with OpenGL preview + Rendered settings.
[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", text="")
46     col.prop(color_balance, "invert_lift", text="Invert", icon="ARROW_LEFTRIGHT")
47     split.template_color_picker(color_balance, "lift", value_slider=True,  cubic=True)
48
49     box = layout.box()
50     split = box.split(percentage=0.35)
51     col = split.column(align=True)
52     col.label(text="Gamma:")
53     col.separator()
54     col.separator()
55     col.prop(color_balance, "gamma", text="")
56     col.prop(color_balance, "invert_gamma", text="Invert", icon="ARROW_LEFTRIGHT")
57     split.template_color_picker(color_balance, "gamma", value_slider=True, lock_luminosity=True, cubic=True)
58
59     box = layout.box()
60     split = box.split(percentage=0.35)
61     col = split.column(align=True)
62     col.label(text="Gain:")
63     col.separator()
64     col.separator()
65     col.prop(color_balance, "gain", text="")
66     col.prop(color_balance, "invert_gain", text="Invert", icon="ARROW_LEFTRIGHT")
67     split.template_color_picker(color_balance, "gain", value_slider=True, lock_luminosity=True, cubic=True)
68
69
70 class SEQUENCER_HT_header(Header):
71     bl_space_type = 'SEQUENCE_EDITOR'
72
73     def draw(self, context):
74         layout = self.layout
75
76         st = context.space_data
77         scene = context.scene
78
79         row = layout.row(align=True)
80         row.template_header()
81
82         SEQUENCER_MT_editor_menus.draw_collapsible(context, layout)
83
84         row = layout.row(align=True)
85         row.prop(scene, "use_preview_range", text="", toggle=True)
86         row.prop(scene, "lock_frame_selection_to_range", text="", toggle=True)
87
88         layout.prop(st, "view_type", expand=True, text="")
89
90         if st.view_type in {'PREVIEW', 'SEQUENCER_PREVIEW'}:
91             layout.prop(st, "display_mode", expand=True, text="")
92
93         if st.view_type == 'SEQUENCER':
94             row = layout.row(align=True)
95             row.operator("sequencer.copy", text="", icon='COPYDOWN')
96             row.operator("sequencer.paste", text="", icon='PASTEDOWN')
97
98             layout.separator()
99             layout.operator("sequencer.refresh_all")
100             layout.prop(st, "show_backdrop")
101         else:
102             if st.view_type == 'SEQUENCER_PREVIEW':
103                 layout.separator()
104                 layout.operator("sequencer.refresh_all")
105
106             layout.prop(st, "preview_channels", expand=True, text="")
107             layout.prop(st, "display_channel", text="Channel")
108
109             ed = 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)
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
536         col = layout.column(align=True)
537         row = col.row(align=True)
538
539         row_sub = row.row(align=True)
540         row_sub.enabled = not strip.lock
541         row_sub.prop(strip, "channel")
542         row.prop(strip, "lock", toggle=True, icon_only=True)
543
544         sub = col.column(align=True)
545         sub.enabled = not strip.lock
546         sub.prop(strip, "frame_start")
547         sub.prop(strip, "frame_final_duration")
548
549         col = layout.column(align=True)
550         row = col.row(align=True)
551         row.label(text=iface_("Final Length: %s") % bpy.utils.smpte_from_frame(strip.frame_final_duration),
552                   translate=False)
553         row = col.row(align=True)
554         row.active = (frame_current >= strip.frame_start and frame_current <= strip.frame_start + strip.frame_duration)
555         row.label(text=iface_("Playhead: %d") % (frame_current - strip.frame_start), translate=False)
556
557         col.label(text=iface_("Frame Offset %d:%d") % (strip.frame_offset_start, strip.frame_offset_end),
558                   translate=False)
559         col.label(text=iface_("Frame Still %d:%d") % (strip.frame_still_start, strip.frame_still_end), translate=False)
560
561         elem = False
562
563         if strip.type == 'IMAGE':
564             elem = strip.strip_elem_from_frame(frame_current)
565         elif strip.type == 'MOVIE':
566             elem = strip.elements[0]
567
568         if elem and elem.orig_width > 0 and elem.orig_height > 0:
569             col.label(text=iface_("Original Dimension: %dx%d") % (elem.orig_width, elem.orig_height), translate=False)
570
571
572 class SEQUENCER_PT_effect(SequencerButtonsPanel, Panel):
573     bl_label = "Effect Strip"
574     bl_category = "Strip"
575
576     @classmethod
577     def poll(cls, context):
578         if not cls.has_sequencer(context):
579             return False
580
581         strip = act_strip(context)
582         if not strip:
583             return False
584
585         return strip.type in {'ADD', 'SUBTRACT', 'ALPHA_OVER', 'ALPHA_UNDER',
586                               'CROSS', 'GAMMA_CROSS', 'MULTIPLY', 'OVER_DROP',
587                               'WIPE', 'GLOW', 'TRANSFORM', 'COLOR', 'SPEED',
588                               'MULTICAM', 'GAUSSIAN_BLUR', 'TEXT'}
589
590     def draw(self, context):
591         layout = self.layout
592
593         strip = act_strip(context)
594
595         if strip.input_count > 0:
596             col = layout.column()
597             col.enabled = False
598             col.prop(strip, "input_1")
599             if strip.input_count > 1:
600                 col.prop(strip, "input_2")
601
602         if strip.type == 'COLOR':
603             layout.prop(strip, "color")
604
605         elif strip.type == 'WIPE':
606             col = layout.column()
607             col.prop(strip, "transition_type")
608             col.label(text="Direction:")
609             col.row().prop(strip, "direction", expand=True)
610
611             col = layout.column()
612             col.prop(strip, "blur_width", slider=True)
613             if strip.transition_type in {'SINGLE', 'DOUBLE'}:
614                 col.prop(strip, "angle")
615
616         elif strip.type == 'GLOW':
617             flow = layout.column_flow()
618             flow.prop(strip, "threshold", slider=True)
619             flow.prop(strip, "clamp", slider=True)
620             flow.prop(strip, "boost_factor")
621             flow.prop(strip, "blur_radius")
622
623             row = layout.row()
624             row.prop(strip, "quality", slider=True)
625             row.prop(strip, "use_only_boost")
626
627         elif strip.type == 'SPEED':
628             layout.prop(strip, "use_default_fade", "Stretch to input strip length")
629             if not strip.use_default_fade:
630                 layout.prop(strip, "use_as_speed")
631                 if strip.use_as_speed:
632                     layout.prop(strip, "speed_factor")
633                 else:
634                     layout.prop(strip, "speed_factor", text="Frame number")
635                     layout.prop(strip, "scale_to_length")
636
637         elif strip.type == 'TRANSFORM':
638             layout = self.layout
639             col = layout.column()
640
641             col.prop(strip, "interpolation")
642             col.prop(strip, "translation_unit")
643             col = layout.column(align=True)
644             col.label(text="Position:")
645             row = col.row(align=True)
646             row.prop(strip, "translate_start_x", text="X")
647             row.prop(strip, "translate_start_y", text="Y")
648
649             layout.separator()
650
651             col = layout.column(align=True)
652             col.prop(strip, "use_uniform_scale")
653             if strip.use_uniform_scale:
654                 col = layout.column(align=True)
655                 col.prop(strip, "scale_start_x", text="Scale")
656             else:
657                 col = layout.column(align=True)
658                 col.label(text="Scale:")
659                 row = col.row(align=True)
660                 row.prop(strip, "scale_start_x", text="X")
661                 row.prop(strip, "scale_start_y", text="Y")
662
663             layout.separator()
664
665             col = layout.column(align=True)
666             col.label(text="Rotation:")
667             col.prop(strip, "rotation_start", text="Rotation")
668
669         elif strip.type == 'MULTICAM':
670             col = layout.column(align=True)
671             strip_channel = strip.channel
672
673             col.prop(strip, "multicam_source", text="Source Channel")
674
675             # The multicam strip needs at least 2 strips to be useful
676             if strip_channel > 2:
677                 BT_ROW = 4
678
679                 col.label("Cut To:")
680                 row = col.row()
681
682                 for i in range(1, strip_channel):
683                     if (i % BT_ROW) == 1:
684                         row = col.row(align=True)
685
686                     # Workaround - .enabled has to have a separate UI block to work
687                     if i == strip.multicam_source:
688                         sub = row.row(align=True)
689                         sub.enabled = False
690                         sub.operator("sequencer.cut_multicam", text="%d" % i).camera = i
691                     else:
692                         sub_1 = row.row(align=True)
693                         sub_1.enabled = True
694                         sub_1.operator("sequencer.cut_multicam", text="%d" % i).camera = i
695
696                 if strip.channel > BT_ROW and (strip_channel - 1) % BT_ROW:
697                     for i in range(strip.channel, strip_channel + ((BT_ROW + 1 - strip_channel) % BT_ROW)):
698                         row.label("")
699             else:
700                 col.separator()
701                 col.label(text="Two or more channels are needed below this strip", icon="INFO")
702
703
704         elif strip.type == 'TEXT':
705             col = layout.column()
706             col.prop(strip, "text")
707             col.prop(strip, "font_size")
708
709             row = col.row()
710             row.prop(strip, "color")
711             row = col.row()
712             row.prop(strip, "use_shadow")
713             rowsub = row.row()
714             rowsub.active = strip.use_shadow
715             rowsub.prop(strip, "shadow_color", text="")
716
717             col.prop(strip, "align_x")
718             col.prop(strip, "align_y")
719             col.label("Location")
720             row = col.row(align=True)
721             row.prop(strip, "location", text="")
722             col.prop(strip, "wrap_width")
723             layout.operator("sequencer.export_subtitles")
724
725         col = layout.column(align=True)
726         if strip.type == 'SPEED':
727             col.prop(strip, "multiply_speed")
728         elif strip.type in {'CROSS', 'GAMMA_CROSS', 'WIPE', 'ALPHA_OVER', 'ALPHA_UNDER', 'OVER_DROP'}:
729             col.prop(strip, "use_default_fade", "Default fade")
730             if not strip.use_default_fade:
731                 col.prop(strip, "effect_fader", text="Effect fader")
732         elif strip.type == 'GAUSSIAN_BLUR':
733             row = col.row(align=True)
734             row.prop(strip, "size_x")
735             row.prop(strip, "size_y")
736
737
738 class SEQUENCER_PT_input(SequencerButtonsPanel, Panel):
739     bl_label = "Strip Input"
740     bl_category = "Strip"
741
742     @classmethod
743     def poll(cls, context):
744         if not cls.has_sequencer(context):
745             return False
746
747         strip = act_strip(context)
748         if not strip:
749             return False
750
751         return strip.type in {'MOVIE', 'IMAGE', 'SCENE', 'MOVIECLIP', 'META',
752                               'ADD', 'SUBTRACT', 'ALPHA_OVER', 'ALPHA_UNDER',
753                               'CROSS', 'GAMMA_CROSS', 'MULTIPLY', 'OVER_DROP',
754                               'WIPE', 'GLOW', 'TRANSFORM', 'COLOR',
755                               'MULTICAM', 'SPEED', 'ADJUSTMENT'}
756
757     def draw(self, context):
758         layout = self.layout
759         scene = context.scene
760
761         strip = act_strip(context)
762
763         seq_type = strip.type
764
765         # draw a filename if we have one
766         if seq_type == 'IMAGE':
767             split = layout.split(percentage=0.2)
768             split.label(text="Path:")
769             split.prop(strip, "directory", text="")
770
771             # Current element for the filename
772
773             elem = strip.strip_elem_from_frame(scene.frame_current)
774             if elem:
775                 split = layout.split(percentage=0.2)
776                 split.label(text="File:")
777                 split.prop(elem, "filename", text="")  # strip.elements[0] could be a fallback
778
779             split = layout.split(percentage=0.4)
780             split.label(text="Color Space:")
781             split.prop(strip.colorspace_settings, "name", text="")
782
783             split = layout.split(percentage=0.4)
784             split.label(text="Alpha:")
785             split.prop(strip, "alpha_mode", text="")
786
787             layout.operator("sequencer.change_path", icon="FILESEL").filter_image = True
788
789         elif seq_type == 'MOVIE':
790             split = layout.split(percentage=0.2)
791             split.label(text="Path:")
792             split.prop(strip, "filepath", text="")
793
794             split = layout.split(percentage=0.4)
795             split.label(text="Color Space:")
796             split.prop(strip.colorspace_settings, "name", text="")
797
798             layout.prop(strip, "mpeg_preseek")
799             layout.prop(strip, "stream_index")
800
801         layout.prop(strip, "use_translation", text="Image Offset")
802         if strip.use_translation:
803             row = layout.row(align=True)
804             row.prop(strip.transform, "offset_x", text="X")
805             row.prop(strip.transform, "offset_y", text="Y")
806
807         layout.prop(strip, "use_crop", text="Image Crop")
808         if strip.use_crop:
809             col = layout.column(align=True)
810             col.prop(strip.crop, "max_y")
811             row = col.row(align=True)
812             row.prop(strip.crop, "min_x")
813             row.prop(strip.crop, "max_x")
814             col.prop(strip.crop, "min_y")
815
816         if not isinstance(strip, bpy.types.EffectSequence):
817             layout.label(text="Trim Duration (hard):")
818             row = layout.row(align=True)
819             row.prop(strip, "animation_offset_start", text="Start")
820             row.prop(strip, "animation_offset_end", text="End")
821
822         layout.label(text="Trim Duration (soft):")
823         row = layout.row(align=True)
824         row.prop(strip, "frame_offset_start", text="Start")
825         row.prop(strip, "frame_offset_end", text="End")
826
827         if scene.render.use_multiview and seq_type in {'IMAGE', 'MOVIE'}:
828             layout.prop(strip, "use_multiview")
829
830             col = layout.column()
831             col.active = strip.use_multiview
832
833             col.label(text="Views Format:")
834             col.row().prop(strip, "views_format", expand=True)
835
836             box = col.box()
837             box.active = strip.views_format == 'STEREO_3D'
838             box.template_image_stereo_3d(strip.stereo_3d_format)
839
840
841 class SEQUENCER_PT_sound(SequencerButtonsPanel, Panel):
842     bl_label = "Sound"
843     bl_category = "Strip"
844
845     @classmethod
846     def poll(cls, context):
847         if not cls.has_sequencer(context):
848             return False
849
850         strip = act_strip(context)
851         if not strip:
852             return False
853
854         return (strip.type == 'SOUND')
855
856     def draw(self, context):
857         layout = self.layout
858
859         st = context.space_data
860         strip = act_strip(context)
861         sound = strip.sound
862
863         layout.template_ID(strip, "sound", open="sound.open")
864         if sound is not None:
865             layout.prop(sound, "filepath", text="")
866
867             row = layout.row()
868             if sound.packed_file:
869                 row.operator("sound.unpack", icon='PACKAGE', text="Unpack")
870             else:
871                 row.operator("sound.pack", icon='UGLYPACKAGE', text="Pack")
872
873             row.prop(sound, "use_memory_cache")
874
875             layout.prop(sound, "use_mono")
876
877         if st.waveform_draw_type == 'DEFAULT_WAVEFORMS':
878             layout.prop(strip, "show_waveform")
879
880         col = layout.column(align=True)
881         col.prop(strip, "volume")
882         col.prop(strip, "pitch")
883         col.prop(strip, "pan")
884
885         col = layout.column(align=True)
886         col.label(text="Trim Duration (hard):")
887         row = layout.row(align=True)
888         row.prop(strip, "animation_offset_start", text="Start")
889         row.prop(strip, "animation_offset_end", text="End")
890
891         col = layout.column(align=True)
892         col.label(text="Trim Duration (soft):")
893         row = layout.row(align=True)
894         row.prop(strip, "frame_offset_start", text="Start")
895         row.prop(strip, "frame_offset_end", text="End")
896
897
898 class SEQUENCER_PT_scene(SequencerButtonsPanel, Panel):
899     bl_label = "Scene"
900     bl_category = "Strip"
901
902     @classmethod
903     def poll(cls, context):
904         if not cls.has_sequencer(context):
905             return False
906
907         strip = act_strip(context)
908         if not strip:
909             return False
910
911         return (strip.type == 'SCENE')
912
913     def draw(self, context):
914         layout = self.layout
915
916         strip = act_strip(context)
917
918         layout.template_ID(strip, "scene")
919
920         scene = strip.scene
921         layout.prop(strip, "use_sequence")
922
923         if not strip.use_sequence:
924             layout.label(text="Camera Override")
925             layout.template_ID(strip, "scene_camera")
926
927             layout.prop(strip, "use_grease_pencil", text="Show Grease Pencil")
928
929         if scene:
930             layout.prop(scene, "audio_volume", text="Audio Volume")
931
932         if not strip.use_sequence:
933             if scene:
934                 # Warning, this is not a good convention to follow.
935                 # Expose here because setting the alpha from the 'Render' menu is very inconvenient.
936                 layout.label("Preview")
937                 layout.prop(scene.render, "alpha_mode")
938
939         if scene:
940             sta = scene.frame_start
941             end = scene.frame_end
942             layout.label(text=iface_("Original frame range: %d-%d (%d)") % (sta, end, end - sta + 1), translate=False)
943
944
945 class SEQUENCER_PT_mask(SequencerButtonsPanel, Panel):
946     bl_label = "Mask"
947     bl_category = "Strip"
948
949     @classmethod
950     def poll(cls, context):
951         if not cls.has_sequencer(context):
952             return False
953
954         strip = act_strip(context)
955         if not strip:
956             return False
957
958         return (strip.type == 'MASK')
959
960     def draw(self, context):
961         layout = self.layout
962
963         strip = act_strip(context)
964
965         layout.template_ID(strip, "mask")
966
967         mask = strip.mask
968
969         if mask:
970             sta = mask.frame_start
971             end = mask.frame_end
972             layout.label(text=iface_("Original frame range: %d-%d (%d)") % (sta, end, end - sta + 1), translate=False)
973
974
975 class SEQUENCER_PT_filter(SequencerButtonsPanel, Panel):
976     bl_label = "Filter"
977     bl_category = "Strip"
978
979     @classmethod
980     def poll(cls, context):
981         if not cls.has_sequencer(context):
982             return False
983
984         strip = act_strip(context)
985         if not strip:
986             return False
987
988         return strip.type in {'MOVIE', 'IMAGE', 'SCENE', 'MOVIECLIP', 'MASK',
989                               'META', 'ADD', 'SUBTRACT', 'ALPHA_OVER',
990                               'ALPHA_UNDER', 'CROSS', 'GAMMA_CROSS', 'MULTIPLY',
991                               'OVER_DROP', 'WIPE', 'GLOW', 'TRANSFORM', 'COLOR',
992                               'MULTICAM', 'SPEED', 'ADJUSTMENT'}
993
994     def draw(self, context):
995         layout = self.layout
996
997         strip = act_strip(context)
998
999         col = layout.column()
1000         col.label(text="Video:")
1001         col.prop(strip, "strobe")
1002
1003         if strip.type == 'MOVIECLIP':
1004             col = layout.column()
1005             col.label(text="Tracker:")
1006             col.prop(strip, "stabilize2d")
1007
1008             col = layout.column()
1009             col.label(text="Distortion:")
1010             col.prop(strip, "undistort")
1011
1012         split = layout.split(percentage=0.6)
1013         col = split.column()
1014         col.prop(strip, "use_reverse_frames", text="Reverse")
1015         col.prop(strip, "use_deinterlace")
1016
1017         col = split.column()
1018         col.prop(strip, "use_flip_x", text="X Flip")
1019         col.prop(strip, "use_flip_y", text="Y Flip")
1020
1021         layout.label("Color:")
1022         col = layout.column(align=True)
1023         col.prop(strip, "color_saturation", text="Saturation")
1024         col.prop(strip, "color_multiply", text="Multiply")
1025         layout.prop(strip, "use_float", text="Convert to Float")
1026
1027
1028 class SEQUENCER_PT_proxy(SequencerButtonsPanel, Panel):
1029     bl_label = "Proxy/Timecode"
1030     bl_category = "Strip"
1031
1032     @classmethod
1033     def poll(cls, context):
1034         if not cls.has_sequencer(context):
1035             return False
1036
1037         strip = act_strip(context)
1038         if not strip:
1039             return False
1040
1041         return strip.type in {'MOVIE', 'IMAGE', 'SCENE', 'META', 'MULTICAM'}
1042
1043     def draw_header(self, context):
1044         strip = act_strip(context)
1045
1046         self.layout.prop(strip, "use_proxy", text="")
1047
1048     def draw(self, context):
1049         layout = self.layout
1050
1051         sequencer = context.scene.sequence_editor
1052
1053         strip = act_strip(context)
1054
1055         if strip.proxy:
1056             proxy = strip.proxy
1057
1058             flow = layout.column_flow()
1059             flow.prop(sequencer, "proxy_storage", text='Storage')
1060             if sequencer.proxy_storage == 'PROJECT':
1061                 flow.prop(sequencer, "proxy_dir", text='Directory')
1062             else:
1063                 flow.prop(proxy, "use_proxy_custom_directory")
1064                 flow.prop(proxy, "use_proxy_custom_file")
1065
1066                 if proxy.use_proxy_custom_directory and not proxy.use_proxy_custom_file:
1067                     flow.prop(proxy, "directory")
1068                 if proxy.use_proxy_custom_file:
1069                     flow.prop(proxy, "filepath")
1070
1071             row = layout.row(align=True)
1072             row.prop(strip.proxy, "build_25", toggle=True)
1073             row.prop(strip.proxy, "build_50", toggle=True)
1074             row.prop(strip.proxy, "build_75", toggle=True)
1075             row.prop(strip.proxy, "build_100", toggle=True)
1076
1077             layout.prop(proxy, "use_overwrite")
1078
1079             col = layout.column()
1080             col.prop(proxy, "quality", text="Build JPEG quality")
1081
1082             if strip.type == 'MOVIE':
1083                 col = layout.column()
1084                 col.label(text="Use timecode index:")
1085
1086                 col.prop(proxy, "timecode")
1087
1088         col = layout.column()
1089         col.operator("sequencer.enable_proxies")
1090         col.operator("sequencer.rebuild_proxy")
1091
1092
1093 class SEQUENCER_PT_preview(SequencerButtonsPanel_Output, Panel):
1094     bl_label = "Scene Preview/Render"
1095     bl_space_type = 'SEQUENCE_EDITOR'
1096     bl_region_type = 'UI'
1097
1098     def draw(self, context):
1099         layout = self.layout
1100
1101         render = context.scene.render
1102
1103         col = layout.column()
1104         col.prop(render, "sequencer_gl_preview", text="")
1105
1106         row = col.row()
1107         row.active = render.sequencer_gl_preview == 'SOLID'
1108         row.prop(render, "use_sequencer_gl_textured_solid")
1109
1110
1111 class SEQUENCER_PT_view(SequencerButtonsPanel_Output, Panel):
1112     bl_label = "View Settings"
1113
1114     def draw(self, context):
1115         layout = self.layout
1116
1117         st = context.space_data
1118
1119         col = layout.column()
1120         if st.display_mode == 'IMAGE':
1121             col.prop(st, "draw_overexposed")
1122             col.separator()
1123
1124         elif st.display_mode == 'WAVEFORM':
1125             col.prop(st, "show_separate_color")
1126
1127         col = layout.column()
1128         col.separator()
1129         col.prop(st, "proxy_render_size")
1130
1131
1132 class SEQUENCER_PT_view_safe_areas(SequencerButtonsPanel_Output, Panel):
1133     bl_label = "Safe Areas"
1134     bl_options = {'DEFAULT_CLOSED'}
1135
1136     @classmethod
1137     def poll(cls, context):
1138         st = context.space_data
1139         is_preview = st.view_type in {'PREVIEW', 'SEQUENCER_PREVIEW'}
1140         return is_preview and (st.display_mode == 'IMAGE')
1141
1142     def draw_header(self, context):
1143         st = context.space_data
1144
1145         self.layout.prop(st, "show_safe_areas", text="")
1146
1147     def draw(self, context):
1148         from bl_ui.properties_data_camera import draw_display_safe_settings
1149
1150         layout = self.layout
1151         st = context.space_data
1152         safe_data = context.scene.safe_areas
1153
1154         draw_display_safe_settings(layout, safe_data, st)
1155
1156
1157 class SEQUENCER_PT_modifiers(SequencerButtonsPanel, Panel):
1158     bl_label = "Modifiers"
1159     bl_category = "Modifiers"
1160
1161     def draw(self, context):
1162         layout = self.layout
1163
1164         strip = act_strip(context)
1165         sequencer = context.scene.sequence_editor
1166
1167         layout.prop(strip, "use_linear_modifiers")
1168
1169         layout.operator_menu_enum("sequencer.strip_modifier_add", "type")
1170         layout.operator("sequencer.strip_modifier_copy")
1171
1172         for mod in strip.modifiers:
1173             box = layout.box()
1174
1175             row = box.row()
1176             row.prop(mod, "show_expanded", text="", emboss=False)
1177             row.prop(mod, "name", text="")
1178
1179             row.prop(mod, "mute", text="")
1180
1181             sub = row.row(align=True)
1182             props = sub.operator("sequencer.strip_modifier_move", text="", icon='TRIA_UP')
1183             props.name = mod.name
1184             props.direction = 'UP'
1185             props = sub.operator("sequencer.strip_modifier_move", text="", icon='TRIA_DOWN')
1186             props.name = mod.name
1187             props.direction = 'DOWN'
1188
1189             row.operator("sequencer.strip_modifier_remove", text="", icon='X', emboss=False).name = mod.name
1190
1191             if mod.show_expanded:
1192                 row = box.row()
1193                 row.prop(mod, "input_mask_type", expand=True)
1194
1195                 if mod.input_mask_type == 'STRIP':
1196                     sequences_object = sequencer
1197                     if sequencer.meta_stack:
1198                         sequences_object = sequencer.meta_stack[-1]
1199                     box.prop_search(mod, "input_mask_strip", sequences_object, "sequences", text="Mask")
1200                 else:
1201                     box.prop(mod, "input_mask_id")
1202                     row = box.row()
1203                     row.prop(mod, "mask_time", expand=True)
1204
1205                 if mod.type == 'COLOR_BALANCE':
1206                     box.prop(mod, "color_multiply")
1207                     draw_color_balance(box, mod.color_balance)
1208                 elif mod.type == 'CURVES':
1209                     box.template_curve_mapping(mod, "curve_mapping", type='COLOR')
1210                 elif mod.type == 'HUE_CORRECT':
1211                     box.template_curve_mapping(mod, "curve_mapping", type='HUE')
1212                 elif mod.type == 'BRIGHT_CONTRAST':
1213                     col = box.column()
1214                     col.prop(mod, "bright")
1215                     col.prop(mod, "contrast")
1216                 elif mod.type == 'WHITE_BALANCE':
1217                     col = box.column()
1218                     col.prop(mod, "white_value")
1219                 elif mod.type == 'TONEMAP':
1220                     col = box.column()
1221                     col.prop(mod, "tonemap_type")
1222                     if mod.tonemap_type == 'RD_PHOTORECEPTOR':
1223                         col.prop(mod, "intensity")
1224                         col.prop(mod, "contrast")
1225                         col.prop(mod, "adaptation")
1226                         col.prop(mod, "correction")
1227                     elif mod.tonemap_type == 'RH_SIMPLE':
1228                         col.prop(mod, "key")
1229                         col.prop(mod, "offset")
1230                         col.prop(mod, "gamma")
1231
1232
1233 class SEQUENCER_PT_grease_pencil(GreasePencilDataPanel, SequencerButtonsPanel_Output, Panel):
1234     bl_space_type = 'SEQUENCE_EDITOR'
1235     bl_region_type = 'UI'
1236
1237     # NOTE: this is just a wrapper around the generic GP Panel
1238     # But, it should only show up when there are images in the preview region
1239
1240
1241 class SEQUENCER_PT_grease_pencil_palettecolor(GreasePencilPaletteColorPanel, SequencerButtonsPanel_Output, Panel):
1242     bl_space_type = 'SEQUENCE_EDITOR'
1243     bl_region_type = 'UI'
1244
1245     # NOTE: this is just a wrapper around the generic GP Panel
1246     # But, it should only show up when there are images in the preview region
1247
1248
1249 class SEQUENCER_PT_grease_pencil_tools(GreasePencilToolsPanel, SequencerButtonsPanel_Output, Panel):
1250     bl_space_type = 'SEQUENCE_EDITOR'
1251     bl_region_type = 'UI'
1252
1253     # NOTE: this is just a wrapper around the generic GP tools panel
1254     # It contains access to some essential tools usually found only in
1255     # toolbar, which doesn't exist here...
1256
1257
1258 class SEQUENCER_PT_custom_props(SequencerButtonsPanel, PropertyPanel, Panel):
1259     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'}
1260     _context_path = "scene.sequence_editor.active_strip"
1261     _property_type = (bpy.types.Sequence,)
1262     bl_category = "Strip"
1263
1264
1265 classes = (
1266     SEQUENCER_HT_header,
1267     SEQUENCER_MT_editor_menus,
1268     SEQUENCER_MT_view,
1269     SEQUENCER_MT_view_toggle,
1270     SEQUENCER_MT_select,
1271     SEQUENCER_MT_marker,
1272     SEQUENCER_MT_change,
1273     SEQUENCER_MT_frame,
1274     SEQUENCER_MT_add,
1275     SEQUENCER_MT_add_effect,
1276     SEQUENCER_MT_strip,
1277     SEQUENCER_PT_edit,
1278     SEQUENCER_PT_effect,
1279     SEQUENCER_PT_input,
1280     SEQUENCER_PT_sound,
1281     SEQUENCER_PT_scene,
1282     SEQUENCER_PT_mask,
1283     SEQUENCER_PT_filter,
1284     SEQUENCER_PT_proxy,
1285     SEQUENCER_PT_preview,
1286     SEQUENCER_PT_view,
1287     SEQUENCER_PT_view_safe_areas,
1288     SEQUENCER_PT_modifiers,
1289     SEQUENCER_PT_grease_pencil,
1290     SEQUENCER_PT_grease_pencil_palettecolor,
1291     SEQUENCER_PT_grease_pencil_tools,
1292     SEQUENCER_PT_custom_props,
1293 )
1294
1295 if __name__ == "__main__":  # only for live edit.
1296     from bpy.utils import register_class
1297     for cls in classes:
1298         register_class(cls)