1 # SPDX-License-Identifier: GPL-2.0-or-later
3 from typing
import List
, Sequence
, Tuple
8 from bpy
.app
.handlers
import persistent
10 from bpy
.types
import (
19 from bpy
.props
import (
24 from .scene_tools
import STORYPENCIL_OT_NewScene
25 from .render
import STORYPENCIL_OT_RenderAction
26 from .sound
import delete_sounds
, send_sound_to_strip_scene
28 def window_id(window
: Window
) -> str:
31 :param window: the Window to consider
32 :return: the Window's ID
34 return str(window
.as_pointer())
37 def get_window_from_id(wm
: WindowManager
, win_id
: str) -> Window
:
38 """Get a Window object from its ID (serialized ptr).
40 :param wm: a WindowManager holding Windows
41 :param win_id: the ID of the Window to get
42 :return: the Window matching the given ID, None otherwise
44 return next((w
for w
in wm
.windows
if w
and window_id(w
) == win_id
), None)
47 def get_main_windows_list(wm
: WindowManager
) -> Sequence
[Window
]:
48 """Get all the Main Windows held by the given WindowManager `wm`"""
49 return [w
for w
in wm
.windows
if w
and w
.parent
is None]
52 def join_win_ids(ids
: List
[str]) -> str:
53 """Join Windows IDs in a single string"""
57 def split_win_ids(ids
: str) -> List
[str]:
58 """Split a Windows IDs string into individual IDs"""
62 class STORYPENCIL_OT_SetSyncMainOperator(Operator
):
63 bl_idname
= "storypencil.sync_set_main"
64 bl_label
= "Set as Sync Main"
65 bl_description
= "Set this Window as main for Synchronization"
66 bl_options
= {'INTERNAL'}
68 win_id
: bpy
.props
.StringProperty(
72 description
="Main window ID",
75 def copy_settings(self
, main_window
, secondary_window
):
76 if main_window
is None or secondary_window
is None:
78 secondary_window
.scene
.storypencil_main_workspace
= main_window
.scene
.storypencil_main_workspace
79 secondary_window
.scene
.storypencil_main_scene
= main_window
.scene
.storypencil_main_scene
80 secondary_window
.scene
.storypencil_edit_workspace
= main_window
.scene
.storypencil_edit_workspace
82 def execute(self
, context
):
83 options
= context
.window_manager
.storypencil_settings
84 options
.main_window_id
= self
.win_id
85 wm
= bpy
.context
.window_manager
87 wm
['storypencil_use_new_window'] = scene
.storypencil_use_new_window
89 main_windows
= get_main_windows_list(wm
)
90 main_window
= get_main_window(wm
)
91 secondary_window
= get_secondary_window(wm
)
94 if secondary_window
is None:
96 if len(main_windows
) < 2:
97 bpy
.ops
.storypencil
.create_secondary_window()
98 secondary_window
= get_secondary_window(wm
)
99 self
.copy_settings(get_main_window(wm
), secondary_window
)
102 # Reuse the existing window
103 secondary_window
= get_not_main_window(wm
)
106 if len(main_windows
) < 2:
107 bpy
.ops
.storypencil
.create_secondary_window()
108 secondary_window
= get_secondary_window(wm
)
109 self
.copy_settings(get_main_window(wm
), secondary_window
)
112 # Reuse the existing window
113 secondary_window
= get_not_main_window(wm
)
116 enable_secondary_window(wm
, window_id(secondary_window
))
117 win_id
= window_id(secondary_window
)
118 self
.copy_settings(get_main_window(wm
), secondary_window
)
119 bpy
.ops
.storypencil
.sync_window_bring_front(win_id
=win_id
)
124 class STORYPENCIL_OT_AddSecondaryWindowOperator(Operator
):
125 bl_idname
= "storypencil.create_secondary_window"
126 bl_label
= "Create Secondary Window"
127 bl_description
= "Create a Secondary Main Window and enable Synchronization"
128 bl_options
= {'INTERNAL'}
130 def execute(self
, context
):
131 # store existing windows
132 windows
= set(context
.window_manager
.windows
[:])
133 bpy
.ops
.wm
.window_new_main()
134 # get newly created window by comparing to previous list
135 new_window
= (set(context
.window_manager
.windows
[:]) - windows
).pop()
136 # activate sync system and enable sync for this window
137 toggle_secondary_window(context
.window_manager
, window_id(new_window
))
138 context
.window_manager
.storypencil_settings
.active
= True
139 # trigger initial synchronization to open the current Sequence's Scene
141 # Configure the new window
142 self
.configure_new_secondary_window(context
, new_window
)
146 def configure_new_secondary_window(self
, context
, new_window
):
147 wrk_name
= context
.scene
.storypencil_edit_workspace
.name
148 # Open the 2D workspace
149 blendpath
= os
.path
.dirname(bpy
.app
.binary_path
)
150 version
= bpy
.app
.version
151 version_full
= str(version
[0]) + '.' + str(version
[1])
152 template
= os
.path
.join("scripts", "startup",
153 "bl_app_templates_system")
154 template
= os
.path
.join(template
, wrk_name
, "startup.blend")
155 template_path
= os
.path
.join(blendpath
, version_full
, template
)
156 # Check if workspace exist and add it if missing
157 for wk
in bpy
.data
.workspaces
:
158 if wk
.name
== wrk_name
:
159 new_window
.workspace
= wk
161 with context
.temp_override(window
=new_window
):
162 bpy
.ops
.workspace
.append_activate(context
, idname
=wrk_name
, filepath
=template_path
)
165 class STORYPENCIL_OT_WindowBringFront(Operator
):
166 bl_idname
= "storypencil.sync_window_bring_front"
167 bl_label
= "Bring Window Front"
168 bl_description
= "Bring a Window to Front"
169 bl_options
= {'INTERNAL'}
171 win_id
: bpy
.props
.StringProperty()
173 def execute(self
, context
):
174 win
= get_window_from_id(context
.window_manager
, self
.win_id
)
177 with context
.temp_override(window
=win
):
178 bpy
.ops
.wm
.window_fullscreen_toggle()
179 bpy
.ops
.wm
.window_fullscreen_toggle()
183 class STORYPENCIL_OT_WindowCloseOperator(Operator
):
184 bl_idname
= "storypencil.close_secondary_window"
185 bl_label
= "Close Window"
186 bl_description
= "Close a specific Window"
187 bl_options
= {'INTERNAL'}
189 win_id
: bpy
.props
.StringProperty()
191 def execute(self
, context
):
192 win
= get_window_from_id(context
.window_manager
, self
.win_id
)
195 with context
.temp_override(window
=win
):
196 bpy
.ops
.wm
.window_close()
200 def validate_sync(window_manager
: WindowManager
) -> bool:
202 Ensure synchronization system is functional, with a valid main window.
203 Disable it otherwise and return the system status.
205 if not window_manager
.storypencil_settings
.active
:
207 if not get_window_from_id(window_manager
, window_manager
.storypencil_settings
.main_window_id
):
208 window_manager
.storypencil_settings
.active
= False
209 return window_manager
.storypencil_settings
.active
212 def get_secondary_window_indices(wm
: WindowManager
) -> List
[str]:
213 """Get secondary Windows indices as a list of IDs
215 :param wm: the WindowManager to consider
216 :return: the list of secondary Windows IDs
218 return split_win_ids(wm
.storypencil_settings
.secondary_windows_ids
)
221 def is_secondary_window(window_manager
: WindowManager
, win_id
: str) -> bool:
222 """Return wether the Window identified by 'win_id' is a secondary window.
224 :return: whether this Window is a sync secondary
226 return win_id
in get_secondary_window_indices(window_manager
)
229 def enable_secondary_window(wm
: WindowManager
, win_id
: str):
230 """Enable the secondary status of a Window.
232 :param wm: the WindowManager instance
233 :param win_id: the id of the window
235 secondary_indices
= get_secondary_window_indices(wm
)
237 # Delete old indice if exist
238 if win_id_str
in secondary_indices
:
239 secondary_indices
.remove(win_id_str
)
242 secondary_indices
.append(win_id_str
)
244 # rebuild the whole list of valid secondary windows
245 secondary_indices
= [
246 idx
for idx
in secondary_indices
if get_window_from_id(wm
, idx
)]
248 wm
.storypencil_settings
.secondary_windows_ids
= join_win_ids(secondary_indices
)
251 def toggle_secondary_window(wm
: WindowManager
, win_id
: str):
252 """Toggle the secondary status of a Window.
254 :param wm: the WindowManager instance
255 :param win_id: the id of the window
257 secondary_indices
= get_secondary_window_indices(wm
)
259 if win_id_str
in secondary_indices
:
260 secondary_indices
.remove(win_id_str
)
262 secondary_indices
.append(win_id_str
)
264 # rebuild the whole list of valid secondary windows
265 secondary_indices
= [
266 idx
for idx
in secondary_indices
if get_window_from_id(wm
, idx
)]
268 wm
.storypencil_settings
.secondary_windows_ids
= join_win_ids(secondary_indices
)
271 def get_main_window(wm
: WindowManager
) -> Window
:
272 """Get the Window used to drive the synchronization system
274 :param wm: the WindowManager instance
275 :returns: the main Window or None
277 return get_window_from_id(wm
=wm
, win_id
=wm
.storypencil_settings
.main_window_id
)
280 def get_secondary_window(wm
: WindowManager
) -> Window
:
281 """Get the first secondary Window
283 :param wm: the WindowManager instance
284 :returns: the Window or None
287 win_id
= window_id(w
)
288 if is_secondary_window(wm
, win_id
):
294 def get_not_main_window(wm
: WindowManager
) -> Window
:
295 """Get the first not main Window
297 :param wm: the WindowManager instance
298 :returns: the Window or None
301 win_id
= window_id(w
)
302 if win_id
!= wm
.storypencil_settings
.main_window_id
:
308 def get_main_strip(wm
: WindowManager
) -> SceneSequence
:
309 """Get Scene Strip at current time in Main window
311 :param wm: the WindowManager instance
312 :returns: the Strip at current time or None
314 context
= bpy
.context
315 scene
= context
.scene
316 main_scene
= scene
.storypencil_main_scene
318 if main_scene
.storypencil_use_new_window
:
319 main_window
= get_main_window(wm
=wm
)
320 if not main_window
or not main_window
.scene
.sequence_editor
:
322 seq_editor
= main_window
.scene
.sequence_editor
323 return seq_editor
.sequences
.get(wm
.storypencil_settings
.main_strip_name
, None)
325 seq_editor
= main_scene
.sequence_editor
.sequences
326 for strip
in seq_editor
:
327 if strip
.type != 'SCENE':
329 if strip
.scene
.name
== scene
.name
:
335 class STORYPENCIL_OT_SyncToggleSecondary(Operator
):
336 bl_idname
= "storypencil.sync_toggle_secondary"
337 bl_label
= "Toggle Secondary Window Status"
338 bl_description
= "Enable/Disable synchronization for a specific Window"
339 bl_options
= {'INTERNAL'}
341 win_id
: bpy
.props
.StringProperty(name
="Window Index")
343 def execute(self
, context
):
344 wm
= context
.window_manager
345 toggle_secondary_window(wm
, self
.win_id
)
349 def get_sequences_at_frame(
351 sequences
: Sequence
[Sequence
]) -> Sequence
[bpy
.types
.Sequence
]:
352 """ Get all sequencer strips at given frame.
354 :param frame: the frame to consider
356 return [s
for s
in sequences
if frame
>= s
.frame_start
+ s
.frame_offset_start
and
357 frame
< s
.frame_start
+ s
.frame_offset_start
+ s
.frame_final_duration
and
361 def get_sequence_at_frame(
363 sequences
: Sequence
[bpy
.types
.Sequence
] = None,
364 skip_muted
: bool = True,
365 ) -> Tuple
[bpy
.types
.Sequence
, int]:
367 Get the higher sequence strip in channels stack at current frame.
368 Recursively enters scene sequences and returns the original frame in the
369 returned strip's time referential.
371 :param frame: the frame to consider
372 :param skip_muted: skip muted strips
373 :returns: the sequence strip and the frame in strip's time referential
376 strips
= get_sequences_at_frame(frame
, sequences
or bpy
.context
.sequences
)
378 # exclude muted strips
380 strips
= [strip
for strip
in strips
if not strip
.mute
]
385 # Remove strip not scene type. Switch is only with Scenes
387 if strip
.type != 'SCENE':
390 # consider higher strip in stack
391 strip
= sorted(strips
, key
=lambda x
: x
.channel
)[-1]
392 # go deeper when current strip is a MetaSequence
393 if isinstance(strip
, MetaSequence
):
394 return get_sequence_at_frame(frame
, strip
.sequences
, skip_muted
)
395 if isinstance(strip
, SceneSequence
):
396 # apply time offset to get in sequence's referential
397 frame
= frame
- strip
.frame_start
+ strip
.scene
.frame_start
398 # enter scene's sequencer if used as input
399 if strip
.scene_input
== 'SEQUENCER':
400 return get_sequence_at_frame(frame
, strip
.scene
.sequence_editor
.sequences
)
404 def set_scene_frame(scene
, frame
, force_update_main
=False):
406 Set `scene` frame_current to `frame` if different.
408 :param scene: the scene to update
409 :param frame: the frame value
410 :param force_update_main: whether to force the update of main scene
412 options
= bpy
.context
.window_manager
.storypencil_settings
413 if scene
.frame_current
!= frame
:
414 scene
.frame_current
= int(frame
)
415 scene
.frame_set(int(frame
))
416 if force_update_main
:
418 bpy
.context
, bpy
.context
.window_manager
.storypencil_settings
.main_window_id
)
421 def setup_window_from_scene_strip(window
: Window
, strip
: SceneSequence
):
422 """Change the Scene and camera of `window` based on `strip`.
424 :param window: [description]
425 :param scene_strip: [description]
427 if window
.scene
!= strip
.scene
:
428 window
.scene
= strip
.scene
429 if strip
.scene_camera
and strip
.scene_camera
!= window
.scene
.camera
:
430 strip
.scene
.camera
= strip
.scene_camera
434 def on_frame_changed(*args
):
436 React to current frame changes and synchronize secondary windows.
438 # ensure context is fully initialized, i.e not '_RestrictData
439 if not isinstance(bpy
.context
, Context
):
442 # happens in some cases (not sure why)
443 if not bpy
.context
.window
:
446 wm
= bpy
.context
.window_manager
448 # early return if synchro is disabled / not available
449 if not validate_sync(wm
) or len(bpy
.data
.scenes
) < 2:
452 # get current window id
453 update_sync(bpy
.context
)
456 def update_sync(context
: Context
, win_id
=None):
457 """ Update synchronized Windows based on the current `context`.
459 :param context: the context
460 :param win_id: specify a window id (context.window is used otherwise)
462 wm
= context
.window_manager
465 win_id
= window_id(context
.window
)
467 main_scene
= get_window_from_id(
468 wm
, wm
.storypencil_settings
.main_window_id
).scene
469 if not main_scene
.sequence_editor
:
472 # return if scene's sequence editor has no sequences
473 sequences
= main_scene
.sequence_editor
.sequences
477 # bidirectionnal sync: change main time from secondary window
479 win_id
!= wm
.storypencil_settings
.main_window_id
480 and is_secondary_window(wm
, win_id
)
482 # get strip under time cursor in main window
483 strip
, old_frame
= get_sequence_at_frame(
484 main_scene
.frame_current
,
487 # only do bidirectional sync if secondary window matches the strip at current time in main
488 if not isinstance(strip
, SceneSequence
) or strip
.scene
!= context
.scene
:
492 frame_offset
= context
.scene
.frame_current
- old_frame
493 if frame_offset
== 0:
496 new_main_frame
= main_scene
.frame_current
+ frame_offset
497 update_main_time
= True
498 # check if a valid scene strip is available under new frame before changing main time
499 f_start
= strip
.frame_start
+ strip
.frame_offset_start
500 f_end
= f_start
+ strip
.frame_final_duration
501 if new_main_frame
< f_start
or new_main_frame
>= f_end
:
502 new_strip
, _
= get_sequence_at_frame(
504 main_scene
.sequence_editor
.sequences
,
506 update_main_time
= isinstance(new_strip
, SceneSequence
)
508 # update main time change in the next event loop + force the sync system update
509 # because Blender won't trigger a frame_changed event to avoid infinite recursion
510 bpy
.app
.timers
.register(
511 functools
.partial(set_scene_frame
, main_scene
,
512 new_main_frame
, True)
517 # return if current window is not main window
518 if win_id
!= wm
.storypencil_settings
.main_window_id
:
521 secondary_windows
= [
522 get_window_from_id(wm
, win_id
)
524 in get_secondary_window_indices(wm
)
525 if win_id
and win_id
!= wm
.storypencil_settings
.main_window_id
528 # only work with at least 2 windows
529 if not secondary_windows
:
532 seq
, frame
= get_sequence_at_frame(main_scene
.frame_current
, sequences
)
534 # return if no sequence at current time or not a scene strip
535 if not isinstance(seq
, SceneSequence
) or not seq
.scene
:
536 wm
.storypencil_settings
.main_strip_name
= ""
539 wm
.storypencil_settings
.main_strip_name
= seq
.name
540 # change the scene on secondary windows
541 # warning: only one window's scene can be changed in this event loop,
542 # otherwise it may crashes Blender randomly
543 for idx
, win
in enumerate(secondary_windows
):
546 # change first secondary window immediately
548 setup_window_from_scene_strip(win
, seq
)
550 # trigger change in next event loop for other windows
551 bpy
.app
.timers
.register(
552 functools
.partial(setup_window_from_scene_strip
, win
, seq
)
555 set_scene_frame(seq
.scene
, frame
)
558 def sync_all_windows(wm
: WindowManager
):
559 """Enable synchronization on all main windows held by `wm`."""
560 wm
.storypencil_settings
.secondary_windows_ids
= join_win_ids([
563 in get_main_windows_list(wm
)
568 def sync_autoconfig(*args
):
569 """Autoconfigure synchronization system.
570 If a window contains a VSE area on a scene with a valid sequence_editor,
571 makes it main window and enable synchronization on all other main windows.
573 main_windows
= get_main_windows_list(bpy
.context
.window_manager
)
574 # don't try to go any further if only one main window
575 if len(main_windows
) < 2:
578 # look for a main window with a valid sequence editor
582 for win
in main_windows
583 if win
.scene
.sequence_editor
584 and any(area
.type == 'SEQUENCE_EDITOR' for area
in win
.screen
.areas
)
588 # if any, set as main and activate sync on all other windows
590 bpy
.context
.window_manager
.storypencil_settings
.main_window_id
= window_id(
592 sync_all_windows(bpy
.context
.window_manager
)
593 bpy
.context
.window_manager
.storypencil_settings
.active
= True
596 def sync_active_update(self
, context
):
597 """ Update function for WindowManager.storypencil_settings.active. """
598 # ensure main window is valid, using current context's window if none is set
602 not self
.main_window_id
603 or not get_window_from_id(context
.window_manager
, self
.main_window_id
)
606 self
.main_window_id
= window_id(context
.window
)
607 # automatically sync all other windows if nothing was previously set
608 if not self
.secondary_windows_ids
:
609 sync_all_windows(context
.window_manager
)
614 def draw_sync_header(self
, context
):
615 """Draw Window sync tools header."""
617 wm
= context
.window_manager
618 self
.layout
.separator()
619 if wm
.get('storypencil_use_new_window') is not None:
620 new_window
= wm
['storypencil_use_new_window']
625 if context
.scene
.storypencil_main_workspace
:
626 if context
.scene
.storypencil_main_workspace
.name
!= context
.workspace
.name
:
627 if context
.area
.ui_type
== 'DOPESHEET':
628 row
= self
.layout
.row(align
=True)
629 row
.operator(STORYPENCIL_OT_Switch
.bl_idname
,
633 def draw_sync_sequencer_header(self
, context
):
634 """Draw Window sync tools header."""
635 if context
.space_data
.view_type
!= 'SEQUENCER':
638 wm
= context
.window_manager
641 row
= layout
.row(align
=True)
642 row
.label(text
="Scenes:")
643 if context
.scene
.storypencil_use_new_window
:
644 row
.operator(STORYPENCIL_OT_SetSyncMainOperator
.bl_idname
, text
="Edit")
646 row
.operator(STORYPENCIL_OT_Switch
.bl_idname
, text
="Edit")
649 layout
.operator_context
= 'INVOKE_REGION_WIN'
650 row
.operator(STORYPENCIL_OT_NewScene
.bl_idname
, text
="New")
652 layout
.operator_context
= 'INVOKE_DEFAULT'
653 row
.separator(factor
=0.5)
654 row
.operator(STORYPENCIL_OT_RenderAction
.bl_idname
, text
="Render")
657 class STORYPENCIL_PG_Settings(PropertyGroup
):
659 PropertyGroup with storypencil settings.
661 active
: BoolProperty(
664 "Automatically open current Sequence's Scene in other "
665 "Main Windows and activate Time Synchronization"),
667 update
=sync_active_update
670 main_window_id
: StringProperty(
671 name
="Main Window ID",
672 description
="ID of the window driving the Synchronization",
676 secondary_windows_ids
: StringProperty(
677 name
="Secondary Windows",
678 description
="Serialized Secondary Window Indices",
682 active_window_index
: IntProperty(
683 name
="Active Window Index",
684 description
="Index for using Window Manager's windows in a UIList",
688 main_strip_name
: StringProperty(
689 name
="Main Strip Name",
690 description
="Scene Strip at current time in the Main window",
694 show_main_strip_range
: BoolProperty(
695 name
="Show Main Strip Range in Secondary Windows",
696 description
="Draw main Strip's in/out markers in synchronized secondary Windows",
701 # -------------------------------------------------------------
702 # Switch manually between Main and Edit Scene and Layout
704 # -------------------------------------------------------------
705 class STORYPENCIL_OT_Switch(Operator
):
706 bl_idname
= "storypencil.switch"
708 bl_description
= "Switch workspace"
709 bl_options
= {'REGISTER', 'UNDO'}
712 def act_strip(self
, context
):
713 scene
= context
.scene
714 sequences
= scene
.sequence_editor
.sequences
717 # Get strip under time cursor
718 strip
, old_frame
= get_sequence_at_frame(
719 scene
.frame_current
, sequences
=sequences
)
722 # ------------------------------
724 # ------------------------------
726 def poll(cls
, context
):
727 scene
= context
.scene
728 if scene
.storypencil_main_workspace
is None or scene
.storypencil_main_scene
is None:
730 if scene
.storypencil_edit_workspace
is None:
735 # ------------------------------
736 # Execute button action
737 # ------------------------------
738 def execute(self
, context
):
739 wm
= context
.window_manager
740 scene
= context
.scene
741 wm
['storypencil_use_new_window'] = scene
.storypencil_use_new_window
744 if scene
.storypencil_main_workspace
.name
!= context
.workspace
.name
:
745 cfra_prv
= scene
.frame_current
747 if scene
.storypencil_main_workspace
is not None:
748 if scene
.storypencil_main_workspace
.use_pin_scene
:
749 scene
.storypencil_main_workspace
.use_pin_scene
= False
751 context
.window
.workspace
= scene
.storypencil_main_workspace
753 if scene
.storypencil_main_scene
is not None:
754 context
.window
.scene
= scene
.storypencil_main_scene
755 strip
= self
.act_strip(context
)
757 context
.window
.scene
.frame_current
= int(cfra_prv
+ strip
.frame_start
) - 1
760 if scene
.storypencil_copy_sounds
:
763 #bpy.ops.sequencer.reload()
766 strip
= self
.act_strip(context
)
768 if strip
is not None and strip
.type == "SCENE":
770 strip
.scene
.storypencil_main_workspace
= scene
.storypencil_main_workspace
771 strip
.scene
.storypencil_main_scene
= scene
.storypencil_main_scene
772 strip
.scene
.storypencil_edit_workspace
= scene
.storypencil_edit_workspace
774 # Set workspace and Scene
775 cfra_prv
= scene
.frame_current
776 if scene
.storypencil_edit_workspace
.use_pin_scene
:
777 scene
.storypencil_edit_workspace
.use_pin_scene
= False
779 context
.window
.workspace
= scene
.storypencil_edit_workspace
780 context
.window
.workspace
.update_tag()
782 context
.window
.scene
= strip
.scene
784 if scene
.storypencil_copy_sounds
:
785 send_sound_to_strip_scene(strip
, clear_sequencer
=True, skip_mute
=scene
.storypencil_skip_sound_mute
)
788 active_frame
= cfra_prv
- strip
.frame_start
+ 1
789 if active_frame
< strip
.scene
.frame_start
:
790 active_frame
= strip
.scene
.frame_start
791 context
.window
.scene
.frame_current
= int(active_frame
)
794 if strip
.scene_input
== 'CAMERA':
795 for screen
in bpy
.data
.screens
:
796 for area
in screen
.areas
:
797 if area
.type == 'VIEW_3D':
798 # select camera as view
799 if strip
and strip
.scene
.camera
is not None:
800 area
.spaces
.active
.region_3d
.view_perspective
= 'CAMERA'
802 if scene
.storypencil_copy_sounds
:
803 bpy
.ops
.sequencer
.reload()
808 class STORYPENCIL_OT_TabSwitch(Operator
):
809 bl_idname
= "storypencil.tabswitch"
810 bl_label
= "Switch using tab key"
811 bl_description
= "Wrapper used to handle the Tab key to switch"
812 bl_options
= {'INTERNAL'}
814 def execute(self
, context
):
815 if context
.scene
.storypencil_use_new_window
:
816 bpy
.ops
.storypencil
.sync_set_main('INVOKE_DEFAULT', True)
818 scene
= context
.scene
819 sequences
= scene
.sequence_editor
.sequences
821 # Get strip under time cursor
822 strip
, old_frame
= get_sequence_at_frame(
823 scene
.frame_current
, sequences
=sequences
)
824 # For meta strips the tab key must be processed by other operator, so
825 # just pass through to the next operator in the stack.
826 if strip
is None or strip
.type != 'SCENE':
827 if context
.active_sequence_strip
and context
.active_sequence_strip
.type == 'META':
828 return {'PASS_THROUGH'}
830 if context
.scene
.sequence_editor
and context
.scene
.sequence_editor
.meta_stack
:
831 return {'PASS_THROUGH'}
833 bpy
.ops
.storypencil
.switch('INVOKE_DEFAULT', True)