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