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