1 # SPDX-FileCopyrightText: 2022-2023 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
5 from typing
import List
, Sequence
, Tuple
10 from bpy
.app
.handlers
import persistent
12 from bpy
.types
import (
21 from bpy
.props
import (
26 from .scene_tools
import STORYPENCIL_OT_NewScene
27 from .render
import STORYPENCIL_OT_RenderAction
28 from .sound
import delete_sounds
, send_sound_to_strip_scene
30 def window_id(window
: Window
) -> str:
33 :param window: the Window to consider
34 :return: the Window's ID
36 return str(window
.as_pointer())
39 def get_window_from_id(wm
: WindowManager
, win_id
: str) -> Window
:
40 """Get a Window object from its ID (serialized ptr).
42 :param wm: a WindowManager holding Windows
43 :param win_id: the ID of the Window to get
44 :return: the Window matching the given ID, None otherwise
46 return next((w
for w
in wm
.windows
if w
and window_id(w
) == win_id
), None)
49 def get_main_windows_list(wm
: WindowManager
) -> Sequence
[Window
]:
50 """Get all the Main Windows held by the given WindowManager `wm`"""
51 return [w
for w
in wm
.windows
if w
and w
.parent
is None]
54 def join_win_ids(ids
: List
[str]) -> str:
55 """Join Windows IDs in a single string"""
59 def split_win_ids(ids
: str) -> List
[str]:
60 """Split a Windows IDs string into individual IDs"""
64 class STORYPENCIL_OT_SetSyncMainOperator(Operator
):
65 bl_idname
= "storypencil.sync_set_main"
66 bl_label
= "Set as Sync Main"
67 bl_description
= "Set this Window as main for Synchronization"
68 bl_options
= {'INTERNAL'}
70 win_id
: bpy
.props
.StringProperty(
74 description
="Main window ID",
77 def copy_settings(self
, main_window
, secondary_window
):
78 if main_window
is None or secondary_window
is None:
80 secondary_window
.scene
.storypencil_main_workspace
= main_window
.scene
.storypencil_main_workspace
81 secondary_window
.scene
.storypencil_main_scene
= main_window
.scene
.storypencil_main_scene
82 secondary_window
.scene
.storypencil_edit_workspace
= main_window
.scene
.storypencil_edit_workspace
84 def execute(self
, context
):
85 options
= context
.window_manager
.storypencil_settings
86 options
.main_window_id
= self
.win_id
87 wm
= bpy
.context
.window_manager
89 wm
['storypencil_use_new_window'] = scene
.storypencil_use_new_window
91 main_windows
= get_main_windows_list(wm
)
92 main_window
= get_main_window(wm
)
93 secondary_window
= get_secondary_window(wm
)
96 if secondary_window
is None:
98 if len(main_windows
) < 2:
99 bpy
.ops
.storypencil
.create_secondary_window()
100 secondary_window
= get_secondary_window(wm
)
101 self
.copy_settings(get_main_window(wm
), secondary_window
)
104 # Reuse the existing window
105 secondary_window
= get_not_main_window(wm
)
108 if len(main_windows
) < 2:
109 bpy
.ops
.storypencil
.create_secondary_window()
110 secondary_window
= get_secondary_window(wm
)
111 self
.copy_settings(get_main_window(wm
), secondary_window
)
114 # Reuse the existing window
115 secondary_window
= get_not_main_window(wm
)
118 enable_secondary_window(wm
, window_id(secondary_window
))
119 win_id
= window_id(secondary_window
)
120 self
.copy_settings(get_main_window(wm
), secondary_window
)
121 bpy
.ops
.storypencil
.sync_window_bring_front(win_id
=win_id
)
126 class STORYPENCIL_OT_AddSecondaryWindowOperator(Operator
):
127 bl_idname
= "storypencil.create_secondary_window"
128 bl_label
= "Create Secondary Window"
129 bl_description
= "Create a Secondary Main Window and enable Synchronization"
130 bl_options
= {'INTERNAL'}
132 def execute(self
, context
):
133 # store existing windows
134 windows
= set(context
.window_manager
.windows
[:])
135 bpy
.ops
.wm
.window_new_main()
136 # get newly created window by comparing to previous list
137 new_window
= (set(context
.window_manager
.windows
[:]) - windows
).pop()
138 # activate sync system and enable sync for this window
139 toggle_secondary_window(context
.window_manager
, window_id(new_window
))
140 context
.window_manager
.storypencil_settings
.active
= True
141 # trigger initial synchronization to open the current Sequence's Scene
143 # Configure the new window
144 self
.configure_new_secondary_window(context
, new_window
)
148 def configure_new_secondary_window(self
, context
, new_window
):
149 wrk_name
= context
.scene
.storypencil_edit_workspace
.name
150 # Open the 2D workspace
151 blendpath
= os
.path
.dirname(bpy
.app
.binary_path
)
152 version
= bpy
.app
.version
153 version_full
= str(version
[0]) + '.' + str(version
[1])
154 template
= os
.path
.join("scripts", "startup",
155 "bl_app_templates_system")
156 template
= os
.path
.join(template
, wrk_name
, "startup.blend")
157 template_path
= os
.path
.join(blendpath
, version_full
, template
)
158 # Check if workspace exist and add it if missing
159 for wk
in bpy
.data
.workspaces
:
160 if wk
.name
== wrk_name
:
161 new_window
.workspace
= wk
163 with context
.temp_override(window
=new_window
):
164 bpy
.ops
.workspace
.append_activate(idname
=wrk_name
, filepath
=template_path
)
167 class STORYPENCIL_OT_WindowBringFront(Operator
):
168 bl_idname
= "storypencil.sync_window_bring_front"
169 bl_label
= "Bring Window Front"
170 bl_description
= "Bring a Window to Front"
171 bl_options
= {'INTERNAL'}
173 win_id
: bpy
.props
.StringProperty()
175 def execute(self
, context
):
176 win
= get_window_from_id(context
.window_manager
, self
.win_id
)
179 with context
.temp_override(window
=win
):
180 bpy
.ops
.wm
.window_fullscreen_toggle()
181 bpy
.ops
.wm
.window_fullscreen_toggle()
185 class STORYPENCIL_OT_WindowCloseOperator(Operator
):
186 bl_idname
= "storypencil.close_secondary_window"
187 bl_label
= "Close Window"
188 bl_description
= "Close a specific Window"
189 bl_options
= {'INTERNAL'}
191 win_id
: bpy
.props
.StringProperty()
193 def execute(self
, context
):
194 win
= get_window_from_id(context
.window_manager
, self
.win_id
)
197 with context
.temp_override(window
=win
):
198 bpy
.ops
.wm
.window_close()
202 def validate_sync(window_manager
: WindowManager
) -> bool:
204 Ensure synchronization system is functional, with a valid main window.
205 Disable it otherwise and return the system status.
207 if not window_manager
.storypencil_settings
.active
:
209 if not get_window_from_id(window_manager
, window_manager
.storypencil_settings
.main_window_id
):
210 window_manager
.storypencil_settings
.active
= False
211 return window_manager
.storypencil_settings
.active
214 def get_secondary_window_indices(wm
: WindowManager
) -> List
[str]:
215 """Get secondary Windows indices as a list of IDs
217 :param wm: the WindowManager to consider
218 :return: the list of secondary Windows IDs
220 return split_win_ids(wm
.storypencil_settings
.secondary_windows_ids
)
223 def is_secondary_window(window_manager
: WindowManager
, win_id
: str) -> bool:
224 """Return wether the Window identified by 'win_id' is a secondary window.
226 :return: whether this Window is a sync secondary
228 return win_id
in get_secondary_window_indices(window_manager
)
231 def enable_secondary_window(wm
: WindowManager
, win_id
: str):
232 """Enable the secondary status of a Window.
234 :param wm: the WindowManager instance
235 :param win_id: the id of the window
237 secondary_indices
= get_secondary_window_indices(wm
)
239 # Delete old indice if exist
240 if win_id_str
in secondary_indices
:
241 secondary_indices
.remove(win_id_str
)
244 secondary_indices
.append(win_id_str
)
246 # rebuild the whole list of valid secondary windows
247 secondary_indices
= [
248 idx
for idx
in secondary_indices
if get_window_from_id(wm
, idx
)]
250 wm
.storypencil_settings
.secondary_windows_ids
= join_win_ids(secondary_indices
)
253 def toggle_secondary_window(wm
: WindowManager
, win_id
: str):
254 """Toggle the secondary status of a Window.
256 :param wm: the WindowManager instance
257 :param win_id: the id of the window
259 secondary_indices
= get_secondary_window_indices(wm
)
261 if win_id_str
in secondary_indices
:
262 secondary_indices
.remove(win_id_str
)
264 secondary_indices
.append(win_id_str
)
266 # rebuild the whole list of valid secondary windows
267 secondary_indices
= [
268 idx
for idx
in secondary_indices
if get_window_from_id(wm
, idx
)]
270 wm
.storypencil_settings
.secondary_windows_ids
= join_win_ids(secondary_indices
)
273 def get_main_window(wm
: WindowManager
) -> Window
:
274 """Get the Window used to drive the synchronization system
276 :param wm: the WindowManager instance
277 :returns: the main Window or None
279 return get_window_from_id(wm
=wm
, win_id
=wm
.storypencil_settings
.main_window_id
)
282 def get_secondary_window(wm
: WindowManager
) -> Window
:
283 """Get the first secondary Window
285 :param wm: the WindowManager instance
286 :returns: the Window or None
289 win_id
= window_id(w
)
290 if is_secondary_window(wm
, win_id
):
296 def get_not_main_window(wm
: WindowManager
) -> Window
:
297 """Get the first not main Window
299 :param wm: the WindowManager instance
300 :returns: the Window or None
303 win_id
= window_id(w
)
304 if win_id
!= wm
.storypencil_settings
.main_window_id
:
310 def get_main_strip(wm
: WindowManager
) -> SceneSequence
:
311 """Get Scene Strip at current time in Main window
313 :param wm: the WindowManager instance
314 :returns: the Strip at current time or None
316 context
= bpy
.context
317 scene
= context
.scene
318 main_scene
= scene
.storypencil_main_scene
320 if main_scene
.storypencil_use_new_window
:
321 main_window
= get_main_window(wm
=wm
)
322 if not main_window
or not main_window
.scene
.sequence_editor
:
324 seq_editor
= main_window
.scene
.sequence_editor
325 return seq_editor
.sequences
.get(wm
.storypencil_settings
.main_strip_name
, None)
327 seq_editor
= main_scene
.sequence_editor
.sequences
328 for strip
in seq_editor
:
329 if strip
.type != 'SCENE':
331 if strip
.scene
.name
== scene
.name
:
337 class STORYPENCIL_OT_SyncToggleSecondary(Operator
):
338 bl_idname
= "storypencil.sync_toggle_secondary"
339 bl_label
= "Toggle Secondary Window Status"
340 bl_description
= "Enable/Disable synchronization for a specific Window"
341 bl_options
= {'INTERNAL'}
343 win_id
: bpy
.props
.StringProperty(name
="Window Index")
345 def execute(self
, context
):
346 wm
= context
.window_manager
347 toggle_secondary_window(wm
, self
.win_id
)
351 def get_sequences_at_frame(
353 sequences
: Sequence
[Sequence
]) -> Sequence
[bpy
.types
.Sequence
]:
354 """ Get all sequencer strips at given frame.
356 :param frame: the frame to consider
358 return [s
for s
in sequences
if frame
>= s
.frame_start
+ s
.frame_offset_start
and
359 frame
< s
.frame_start
+ s
.frame_offset_start
+ s
.frame_final_duration
and
363 def get_sequence_at_frame(
365 sequences
: Sequence
[bpy
.types
.Sequence
] = None,
366 skip_muted
: bool = True,
367 ) -> Tuple
[bpy
.types
.Sequence
, int]:
369 Get the higher sequence strip in channels stack at current frame.
370 Recursively enters scene sequences and returns the original frame in the
371 returned strip's time referential.
373 :param frame: the frame to consider
374 :param skip_muted: skip muted strips
375 :returns: the sequence strip and the frame in strip's time referential
378 strips
= get_sequences_at_frame(frame
, sequences
or bpy
.context
.sequences
)
380 # exclude muted strips
382 strips
= [strip
for strip
in strips
if not strip
.mute
]
387 # Remove strip not scene type. Switch is only with Scenes
389 if strip
.type != 'SCENE':
392 # consider higher strip in stack
393 strip
= sorted(strips
, key
=lambda x
: x
.channel
)[-1]
394 # go deeper when current strip is a MetaSequence
395 if isinstance(strip
, MetaSequence
):
396 return get_sequence_at_frame(frame
, strip
.sequences
, skip_muted
)
397 if isinstance(strip
, SceneSequence
):
398 # apply time offset to get in sequence's referential
399 frame
= frame
- strip
.frame_start
+ strip
.scene
.frame_start
400 # enter scene's sequencer if used as input
401 if strip
.scene_input
== 'SEQUENCER':
402 return get_sequence_at_frame(frame
, strip
.scene
.sequence_editor
.sequences
)
406 def set_scene_frame(scene
, frame
, force_update_main
=False):
408 Set `scene` frame_current to `frame` if different.
410 :param scene: the scene to update
411 :param frame: the frame value
412 :param force_update_main: whether to force the update of main scene
414 options
= bpy
.context
.window_manager
.storypencil_settings
415 if scene
.frame_current
!= frame
:
416 scene
.frame_current
= int(frame
)
417 scene
.frame_set(int(frame
))
418 if force_update_main
:
420 bpy
.context
, bpy
.context
.window_manager
.storypencil_settings
.main_window_id
)
423 def setup_window_from_scene_strip(window
: Window
, strip
: SceneSequence
):
424 """Change the Scene and camera of `window` based on `strip`.
426 :param window: [description]
427 :param scene_strip: [description]
429 if window
.scene
!= strip
.scene
:
430 window
.scene
= strip
.scene
431 if strip
.scene_camera
and strip
.scene_camera
!= window
.scene
.camera
:
432 strip
.scene
.camera
= strip
.scene_camera
436 def on_frame_changed(*args
):
438 React to current frame changes and synchronize secondary windows.
440 # ensure context is fully initialized, i.e not '_RestrictData
441 if not isinstance(bpy
.context
, Context
):
444 # happens in some cases (not sure why)
445 if not bpy
.context
.window
:
448 wm
= bpy
.context
.window_manager
450 # early return if synchro is disabled / not available
451 if not validate_sync(wm
) or len(bpy
.data
.scenes
) < 2:
454 # get current window id
455 update_sync(bpy
.context
)
458 def update_sync(context
: Context
, win_id
=None):
459 """ Update synchronized Windows based on the current `context`.
461 :param context: the context
462 :param win_id: specify a window id (context.window is used otherwise)
464 wm
= context
.window_manager
467 win_id
= window_id(context
.window
)
469 main_scene
= get_window_from_id(
470 wm
, wm
.storypencil_settings
.main_window_id
).scene
471 if not main_scene
.sequence_editor
:
474 # return if scene's sequence editor has no sequences
475 sequences
= main_scene
.sequence_editor
.sequences
479 # bidirectionnal sync: change main time from secondary window
481 win_id
!= wm
.storypencil_settings
.main_window_id
482 and is_secondary_window(wm
, win_id
)
484 # get strip under time cursor in main window
485 strip
, old_frame
= get_sequence_at_frame(
486 main_scene
.frame_current
,
489 # only do bidirectional sync if secondary window matches the strip at current time in main
490 if not isinstance(strip
, SceneSequence
) or strip
.scene
!= context
.scene
:
494 frame_offset
= context
.scene
.frame_current
- old_frame
495 if frame_offset
== 0:
498 new_main_frame
= main_scene
.frame_current
+ frame_offset
499 update_main_time
= True
500 # check if a valid scene strip is available under new frame before changing main time
501 f_start
= strip
.frame_start
+ strip
.frame_offset_start
502 f_end
= f_start
+ strip
.frame_final_duration
503 if new_main_frame
< f_start
or new_main_frame
>= f_end
:
504 new_strip
, _
= get_sequence_at_frame(
506 main_scene
.sequence_editor
.sequences
,
508 update_main_time
= isinstance(new_strip
, SceneSequence
)
510 # update main time change in the next event loop + force the sync system update
511 # because Blender won't trigger a frame_changed event to avoid infinite recursion
512 bpy
.app
.timers
.register(
513 functools
.partial(set_scene_frame
, main_scene
,
514 new_main_frame
, True)
519 # return if current window is not main window
520 if win_id
!= wm
.storypencil_settings
.main_window_id
:
523 secondary_windows
= [
524 get_window_from_id(wm
, win_id
)
526 in get_secondary_window_indices(wm
)
527 if win_id
and win_id
!= wm
.storypencil_settings
.main_window_id
530 # only work with at least 2 windows
531 if not secondary_windows
:
534 seq
, frame
= get_sequence_at_frame(main_scene
.frame_current
, sequences
)
536 # return if no sequence at current time or not a scene strip
537 if not isinstance(seq
, SceneSequence
) or not seq
.scene
:
538 wm
.storypencil_settings
.main_strip_name
= ""
541 wm
.storypencil_settings
.main_strip_name
= seq
.name
542 # change the scene on secondary windows
543 # warning: only one window's scene can be changed in this event loop,
544 # otherwise it may crashes Blender randomly
545 for idx
, win
in enumerate(secondary_windows
):
548 # change first secondary window immediately
550 setup_window_from_scene_strip(win
, seq
)
552 # trigger change in next event loop for other windows
553 bpy
.app
.timers
.register(
554 functools
.partial(setup_window_from_scene_strip
, win
, seq
)
557 set_scene_frame(seq
.scene
, frame
)
560 def sync_all_windows(wm
: WindowManager
):
561 """Enable synchronization on all main windows held by `wm`."""
562 wm
.storypencil_settings
.secondary_windows_ids
= join_win_ids([
565 in get_main_windows_list(wm
)
570 def sync_autoconfig(*args
):
571 """Autoconfigure synchronization system.
572 If a window contains a VSE area on a scene with a valid sequence_editor,
573 makes it main window and enable synchronization on all other main windows.
575 main_windows
= get_main_windows_list(bpy
.context
.window_manager
)
576 # don't try to go any further if only one main window
577 if len(main_windows
) < 2:
580 # look for a main window with a valid sequence editor
584 for win
in main_windows
585 if win
.scene
.sequence_editor
586 and any(area
.type == 'SEQUENCE_EDITOR' for area
in win
.screen
.areas
)
590 # if any, set as main and activate sync on all other windows
592 bpy
.context
.window_manager
.storypencil_settings
.main_window_id
= window_id(
594 sync_all_windows(bpy
.context
.window_manager
)
595 bpy
.context
.window_manager
.storypencil_settings
.active
= True
598 def sync_active_update(self
, context
):
599 """ Update function for WindowManager.storypencil_settings.active. """
600 # ensure main window is valid, using current context's window if none is set
604 not self
.main_window_id
605 or not get_window_from_id(context
.window_manager
, self
.main_window_id
)
608 self
.main_window_id
= window_id(context
.window
)
609 # automatically sync all other windows if nothing was previously set
610 if not self
.secondary_windows_ids
:
611 sync_all_windows(context
.window_manager
)
616 def draw_sync_header(self
, context
):
617 """Draw Window sync tools header."""
619 wm
= context
.window_manager
620 self
.layout
.separator()
621 if wm
.get('storypencil_use_new_window') is not None:
622 new_window
= wm
['storypencil_use_new_window']
627 if context
.scene
.storypencil_main_workspace
:
628 if context
.scene
.storypencil_main_workspace
.name
!= context
.workspace
.name
:
629 if context
.area
.ui_type
== 'DOPESHEET':
630 row
= self
.layout
.row(align
=True)
631 row
.operator(STORYPENCIL_OT_Switch
.bl_idname
,
635 def draw_sync_sequencer_header(self
, context
):
636 """Draw Window sync tools header."""
637 if context
.space_data
.view_type
!= 'SEQUENCER':
640 wm
= context
.window_manager
643 row
= layout
.row(align
=True)
644 row
.label(text
="Scenes:")
645 if context
.scene
.storypencil_use_new_window
:
646 row
.operator(STORYPENCIL_OT_SetSyncMainOperator
.bl_idname
, text
="Edit")
648 row
.operator(STORYPENCIL_OT_Switch
.bl_idname
, text
="Edit")
651 layout
.operator_context
= 'INVOKE_REGION_WIN'
652 row
.operator(STORYPENCIL_OT_NewScene
.bl_idname
, text
="New")
654 layout
.operator_context
= 'INVOKE_DEFAULT'
655 row
.separator(factor
=0.5)
656 row
.operator(STORYPENCIL_OT_RenderAction
.bl_idname
, text
="Render")
659 class STORYPENCIL_PG_Settings(PropertyGroup
):
661 PropertyGroup with storypencil settings.
663 active
: BoolProperty(
666 "Automatically open current Sequence's Scene in other "
667 "Main Windows and activate Time Synchronization"),
669 update
=sync_active_update
672 main_window_id
: StringProperty(
673 name
="Main Window ID",
674 description
="ID of the window driving the Synchronization",
678 secondary_windows_ids
: StringProperty(
679 name
="Secondary Windows",
680 description
="Serialized Secondary Window Indices",
684 active_window_index
: IntProperty(
685 name
="Active Window Index",
686 description
="Index for using Window Manager's windows in a UIList",
690 main_strip_name
: StringProperty(
691 name
="Main Strip Name",
692 description
="Scene Strip at current time in the Main window",
696 show_main_strip_range
: BoolProperty(
697 name
="Show Main Strip Range in Secondary Windows",
698 description
="Draw main Strip's in/out markers in synchronized secondary Windows",
703 # -------------------------------------------------------------
704 # Switch manually between Main and Edit Scene and Layout
706 # -------------------------------------------------------------
707 class STORYPENCIL_OT_Switch(Operator
):
708 bl_idname
= "storypencil.switch"
710 bl_description
= "Switch workspace"
711 bl_options
= {'REGISTER', 'UNDO'}
714 def act_strip(self
, context
):
715 scene
= context
.scene
716 sequences
= scene
.sequence_editor
.sequences
719 # Get strip under time cursor
720 strip
, old_frame
= get_sequence_at_frame(
721 scene
.frame_current
, sequences
=sequences
)
724 # ------------------------------
726 # ------------------------------
728 def poll(cls
, context
):
729 scene
= context
.scene
730 if scene
.storypencil_main_workspace
is None or scene
.storypencil_main_scene
is None:
732 if scene
.storypencil_edit_workspace
is None:
737 # ------------------------------
738 # Execute button action
739 # ------------------------------
740 def execute(self
, context
):
741 wm
= context
.window_manager
742 scene
= context
.scene
743 wm
['storypencil_use_new_window'] = scene
.storypencil_use_new_window
746 if scene
.storypencil_main_workspace
.name
!= context
.workspace
.name
:
747 cfra_prv
= scene
.frame_current
749 if scene
.storypencil_main_workspace
is not None:
750 if scene
.storypencil_main_workspace
.use_pin_scene
:
751 scene
.storypencil_main_workspace
.use_pin_scene
= False
753 context
.window
.workspace
= scene
.storypencil_main_workspace
755 if scene
.storypencil_main_scene
is not None:
756 context
.window
.scene
= scene
.storypencil_main_scene
757 strip
= self
.act_strip(context
)
759 context
.window
.scene
.frame_current
= int(cfra_prv
+ strip
.frame_start
) - 1
762 if scene
.storypencil_copy_sounds
:
765 #bpy.ops.sequencer.reload()
768 strip
= self
.act_strip(context
)
770 if strip
is not None and strip
.type == "SCENE":
772 strip
.scene
.storypencil_main_workspace
= scene
.storypencil_main_workspace
773 strip
.scene
.storypencil_main_scene
= scene
.storypencil_main_scene
774 strip
.scene
.storypencil_edit_workspace
= scene
.storypencil_edit_workspace
776 # Set workspace and Scene
777 cfra_prv
= scene
.frame_current
778 if scene
.storypencil_edit_workspace
.use_pin_scene
:
779 scene
.storypencil_edit_workspace
.use_pin_scene
= False
781 context
.window
.workspace
= scene
.storypencil_edit_workspace
782 context
.window
.workspace
.update_tag()
784 context
.window
.scene
= strip
.scene
786 if scene
.storypencil_copy_sounds
:
787 send_sound_to_strip_scene(strip
, clear_sequencer
=True, skip_mute
=scene
.storypencil_skip_sound_mute
)
790 active_frame
= cfra_prv
- strip
.frame_start
+ 1
791 if active_frame
< strip
.scene
.frame_start
:
792 active_frame
= strip
.scene
.frame_start
793 context
.window
.scene
.frame_current
= int(active_frame
)
796 if strip
.scene_input
== 'CAMERA':
797 for screen
in bpy
.data
.screens
:
798 for area
in screen
.areas
:
799 if area
.type == 'VIEW_3D':
800 # select camera as view
801 if strip
and strip
.scene
.camera
is not None:
802 area
.spaces
.active
.region_3d
.view_perspective
= 'CAMERA'
804 if scene
.storypencil_copy_sounds
:
805 bpy
.ops
.sequencer
.reload()
810 class STORYPENCIL_OT_TabSwitch(Operator
):
811 bl_idname
= "storypencil.tabswitch"
812 bl_label
= "Switch using tab key"
813 bl_description
= "Wrapper used to handle the Tab key to switch"
814 bl_options
= {'INTERNAL'}
816 def execute(self
, context
):
817 # For meta strips the tab key must be processed by other operator, so
818 # just pass through to the next operator in the stack.
819 if context
.active_sequence_strip
and context
.active_sequence_strip
.type == 'META':
820 return {'PASS_THROUGH'}
822 if context
.scene
.sequence_editor
and context
.scene
.sequence_editor
.meta_stack
:
823 return {'PASS_THROUGH'}
825 if context
.scene
.storypencil_use_new_window
:
826 bpy
.ops
.storypencil
.sync_set_main('INVOKE_DEFAULT', True)
828 scene
= context
.scene
829 sequences
= scene
.sequence_editor
.sequences
831 # Get strip under time cursor
832 strip
, old_frame
= get_sequence_at_frame(
833 scene
.frame_current
, sequences
=sequences
)
834 if strip
and strip
.type == 'SCENE':
835 bpy
.ops
.storypencil
.switch('INVOKE_DEFAULT', True)