SVN: UX improvements #136
@ -445,6 +445,7 @@ class SVN_OT_cleanup(SVN_Operator, Operator):
|
|||||||
|
|
||||||
Processes.restart('Status')
|
Processes.restart('Status')
|
||||||
Processes.restart('Log')
|
Processes.restart('Log')
|
||||||
|
Processes.restart('Redraw Viewport')
|
||||||
|
|
||||||
self.report({'INFO'}, "SVN Cleanup complete.")
|
self.report({'INFO'}, "SVN Cleanup complete.")
|
||||||
|
|
||||||
|
@ -28,7 +28,6 @@ class SVN_OT_checkout_initiate(Operator):
|
|||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
prefs = get_addon_prefs(context)
|
prefs = get_addon_prefs(context)
|
||||||
prefs.active_repo_mode = 'SELECTED_REPO'
|
|
||||||
if self.create:
|
if self.create:
|
||||||
prefs.repositories.add()
|
prefs.repositories.add()
|
||||||
prefs.active_repo_idx = len(prefs.repositories)-1
|
prefs.active_repo_idx = len(prefs.repositories)-1
|
||||||
|
@ -46,39 +46,29 @@ class SVN_addon_preferences(AddonPreferences):
|
|||||||
|
|
||||||
repo = self.repositories.add()
|
repo = self.repositories.add()
|
||||||
repo.initialize(root_dir, base_url)
|
repo.initialize(root_dir, base_url)
|
||||||
|
self.active_repo_idx = len(self.repositories)-1
|
||||||
|
|
||||||
return repo
|
return repo
|
||||||
|
|
||||||
def update_active_repo_idx(self, context):
|
def update_active_repo_idx(self, context):
|
||||||
if self.idx_updating or len(self.repositories) == 0:
|
if len(self.repositories) == 0:
|
||||||
return
|
return
|
||||||
self.idx_updating = True
|
|
||||||
active_repo = self.active_repo
|
active_repo = self.active_repo
|
||||||
if self.active_repo_mode == 'CURRENT_BLEND':
|
|
||||||
scene_svn = context.scene.svn
|
|
||||||
scene_svn_idx = self.repositories.find(scene_svn.svn_directory)
|
|
||||||
if scene_svn_idx == -1:
|
|
||||||
self.idx_updating = False
|
|
||||||
return
|
|
||||||
self.active_repo_idx = scene_svn_idx
|
|
||||||
self.idx_updating = False
|
|
||||||
return
|
|
||||||
|
|
||||||
|
# Authenticate when switching repos.
|
||||||
if (
|
if (
|
||||||
active_repo and
|
active_repo and
|
||||||
not active_repo.authenticated and
|
|
||||||
not active_repo.auth_failed and
|
not active_repo.auth_failed and
|
||||||
active_repo.is_cred_entered
|
active_repo.is_cred_entered
|
||||||
):
|
):
|
||||||
active_repo.authenticate(context)
|
Processes.start('Redraw Viewport')
|
||||||
|
if active_repo.authenticated:
|
||||||
|
Processes.restart('Status')
|
||||||
|
else:
|
||||||
|
active_repo.authenticate(context)
|
||||||
|
else:
|
||||||
|
Processes.kill('Status')
|
||||||
|
|
||||||
self.idx_updating = False
|
|
||||||
|
|
||||||
def update_active_repo_mode(self, context):
|
|
||||||
if self.active_repo_mode == 'CURRENT_BLEND':
|
|
||||||
scene_svn = context.scene.svn
|
|
||||||
scene_svn_idx = self.repositories.find(scene_svn.svn_directory)
|
|
||||||
self.active_repo_idx = scene_svn_idx
|
|
||||||
|
|
||||||
checkout_mode: BoolProperty(
|
checkout_mode: BoolProperty(
|
||||||
name="Checkout In Progress",
|
name="Checkout In Progress",
|
||||||
@ -86,30 +76,16 @@ class SVN_addon_preferences(AddonPreferences):
|
|||||||
default=False
|
default=False
|
||||||
)
|
)
|
||||||
|
|
||||||
active_repo_mode: EnumProperty(
|
|
||||||
name="Choose Repository",
|
|
||||||
description="Whether the add-on should communicate with the repository of the currently opened .blend file, or the repository selected in the list below",
|
|
||||||
items=[
|
|
||||||
('CURRENT_BLEND', "Current Blend", "Check if the current .blend file is in an SVN repository, and communicate with that if that is the case. The file list will display only the files of the repository of the current .blend file. If the current .blend is not in a repository, do nothing"),
|
|
||||||
('SELECTED_REPO', "Selected Repo",
|
|
||||||
"Communicate with the selected repository")
|
|
||||||
],
|
|
||||||
default='CURRENT_BLEND',
|
|
||||||
update=update_active_repo_mode
|
|
||||||
)
|
|
||||||
|
|
||||||
active_repo_idx: IntProperty(
|
active_repo_idx: IntProperty(
|
||||||
name="SVN Repositories",
|
name="SVN Repositories",
|
||||||
options=set(),
|
options=set(),
|
||||||
update=update_active_repo_idx
|
update=update_active_repo_idx
|
||||||
)
|
)
|
||||||
idx_updating: BoolProperty(
|
|
||||||
name="Index is Updating",
|
|
||||||
description="Helper flag to avoid infinite looping update callbacks",
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def active_repo(self) -> SVN_repository:
|
def active_repo(self) -> Optional[SVN_repository]:
|
||||||
|
if not self.is_svn_installed:
|
||||||
|
return
|
||||||
if 0 <= self.active_repo_idx <= len(self.repositories)-1:
|
if 0 <= self.active_repo_idx <= len(self.repositories)-1:
|
||||||
return self.repositories[self.active_repo_idx]
|
return self.repositories[self.active_repo_idx]
|
||||||
|
|
||||||
|
@ -28,33 +28,10 @@ class SVN_scene_properties(PropertyGroup):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def get_repo(self, context) -> Optional['SVN_repository']:
|
def get_repo(self, context) -> Optional['SVN_repository']:
|
||||||
"""Return the current repository.
|
"""Return the active repository."""
|
||||||
Depending on preferences, this is either the repo the current .blend file is in,
|
|
||||||
or whatever repo is selected in the preferences UI.
|
|
||||||
"""
|
|
||||||
prefs = get_addon_prefs(context)
|
prefs = get_addon_prefs(context)
|
||||||
if not prefs.is_svn_installed:
|
return prefs.active_repo
|
||||||
return
|
|
||||||
|
|
||||||
if prefs.active_repo_mode == 'CURRENT_BLEND':
|
|
||||||
return self.get_scene_repo(context)
|
|
||||||
else:
|
|
||||||
return prefs.active_repo
|
|
||||||
|
|
||||||
def get_scene_repo(self, context) -> Optional['SVN_repository']:
|
|
||||||
"""Return the repository of the current file, even if the add-on is
|
|
||||||
configured to another repository.
|
|
||||||
"""
|
|
||||||
prefs = get_addon_prefs(context)
|
|
||||||
if not prefs.is_svn_installed:
|
|
||||||
return
|
|
||||||
|
|
||||||
if not self.svn_url or not self.svn_directory:
|
|
||||||
return
|
|
||||||
|
|
||||||
for repo in prefs.repositories:
|
|
||||||
if (repo.url == self.svn_url) and (Path(repo.directory) == Path(self.svn_directory)):
|
|
||||||
return repo
|
|
||||||
|
|
||||||
registry = [
|
registry = [
|
||||||
SVN_scene_properties,
|
SVN_scene_properties,
|
||||||
|
@ -305,9 +305,9 @@ class SVN_repository(PropertyGroup):
|
|||||||
)
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_cred_entered(self):
|
def is_cred_entered(self) -> bool:
|
||||||
"""Check if there's a username and password entered at all."""
|
"""Check if there's a username and password entered at all."""
|
||||||
return self.username and self.password
|
return bool(self.username and self.password)
|
||||||
|
|
||||||
authenticated: BoolProperty(
|
authenticated: BoolProperty(
|
||||||
name="Authenticated",
|
name="Authenticated",
|
||||||
|
@ -44,7 +44,7 @@ class BackgroundProcess:
|
|||||||
# Displayed in the tooltip on mouse-hover in the error message when an error occurs.
|
# Displayed in the tooltip on mouse-hover in the error message when an error occurs.
|
||||||
error_description = "SVN Error:"
|
error_description = "SVN Error:"
|
||||||
|
|
||||||
debug = True
|
debug = False
|
||||||
|
|
||||||
def debug_print(self, msg: str):
|
def debug_print(self, msg: str):
|
||||||
if self.debug:
|
if self.debug:
|
||||||
@ -82,7 +82,7 @@ class BackgroundProcess:
|
|||||||
def handle_error(self, context, error):
|
def handle_error(self, context, error):
|
||||||
self.output = ""
|
self.output = ""
|
||||||
self.error = error.stderr.decode()
|
self.error = error.stderr.decode()
|
||||||
self.is_running = False
|
self.stop()
|
||||||
|
|
||||||
def process_output(self, context, prefs):
|
def process_output(self, context, prefs):
|
||||||
"""
|
"""
|
||||||
@ -113,7 +113,7 @@ class BackgroundProcess:
|
|||||||
repo = context.scene.svn.get_repo(context)
|
repo = context.scene.svn.get_repo(context)
|
||||||
if not repo:
|
if not repo:
|
||||||
self.debug_print("Shutdown: Not in repo.")
|
self.debug_print("Shutdown: Not in repo.")
|
||||||
self.is_running = False
|
self.stop()
|
||||||
return
|
return
|
||||||
|
|
||||||
prefs = get_addon_prefs(context)
|
prefs = get_addon_prefs(context)
|
||||||
@ -127,7 +127,7 @@ class BackgroundProcess:
|
|||||||
|
|
||||||
if self.needs_authentication and not repo.authenticated:
|
if self.needs_authentication and not repo.authenticated:
|
||||||
self.debug_print("Shutdown: Authentication needed.")
|
self.debug_print("Shutdown: Authentication needed.")
|
||||||
self.is_running = False
|
self.stop()
|
||||||
return
|
return
|
||||||
|
|
||||||
if not self.thread or not self.thread.is_alive() and not self.output and not self.error:
|
if not self.thread or not self.thread.is_alive() and not self.output and not self.error:
|
||||||
@ -146,15 +146,14 @@ class BackgroundProcess:
|
|||||||
self.output = ""
|
self.output = ""
|
||||||
redraw_viewport()
|
redraw_viewport()
|
||||||
if self.repeat_delay == 0:
|
if self.repeat_delay == 0:
|
||||||
self.debug_print(
|
self.debug_print("Shutdown: Output was processed, repeat_delay==0.")
|
||||||
"Shutdown: Output was processed, repeat_delay==0.")
|
self.stop()
|
||||||
self.is_running = False
|
|
||||||
return
|
return
|
||||||
self.debug_print(f"Processed output. Waiting {self.repeat_delay}")
|
self.debug_print(f"Processed output. Waiting {self.repeat_delay}")
|
||||||
return self.repeat_delay
|
return self.repeat_delay
|
||||||
elif not self.thread and not self.thread.is_alive() and self.repeat_delay == 0:
|
elif not self.thread and not self.thread.is_alive() and self.repeat_delay == 0:
|
||||||
self.debug_print("Shutdown: Finished.\n")
|
self.debug_print("Shutdown: Finished.\n")
|
||||||
self.is_running = False
|
self.stop()
|
||||||
return
|
return
|
||||||
|
|
||||||
self.debug_print(f"Tick delay: {self.tick_delay}")
|
self.debug_print(f"Tick delay: {self.tick_delay}")
|
||||||
@ -188,6 +187,7 @@ class BackgroundProcess:
|
|||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
"""Stop the process if it isn't running, by unregistering its timer function"""
|
"""Stop the process if it isn't running, by unregistering its timer function"""
|
||||||
|
self.debug_print("stop() function was called.")
|
||||||
self.is_running = False
|
self.is_running = False
|
||||||
if bpy.app.timers.is_registered(self.timer_function):
|
if bpy.app.timers.is_registered(self.timer_function):
|
||||||
# This won't work if the timer has returned None at any point, as that
|
# This won't work if the timer has returned None at any point, as that
|
||||||
|
@ -7,6 +7,7 @@ class BGP_SVN_Redraw_Viewport(BackgroundProcess):
|
|||||||
repeat_delay = 1
|
repeat_delay = 1
|
||||||
debug = False
|
debug = False
|
||||||
tick_delay = 1
|
tick_delay = 1
|
||||||
|
needs_authentication = False
|
||||||
|
|
||||||
def tick(self, context, prefs):
|
def tick(self, context, prefs):
|
||||||
redraw_viewport()
|
redraw_viewport()
|
||||||
|
@ -56,13 +56,12 @@ class SVN_OT_explain_status(Operator):
|
|||||||
|
|
||||||
@bpy.app.handlers.persistent
|
@bpy.app.handlers.persistent
|
||||||
def ensure_svn_of_current_file(_scene=None):
|
def ensure_svn_of_current_file(_scene=None):
|
||||||
"""When opening or saving a .blend file, it's possible that the new .blend
|
"""When opening or saving a .blend file, it's possible that the new .blend
|
||||||
is part of an SVN repository. If this is the case, do the following:
|
is part of an SVN repository. If this is the case, do the following:
|
||||||
- Initialize SVN Scene info
|
- Check if this file's repository is already in our database
|
||||||
- Initialize Repository
|
- If not, create it
|
||||||
- Try to authenticate
|
- Switch to that repo
|
||||||
"""
|
"""
|
||||||
|
|
||||||
context = bpy.context
|
context = bpy.context
|
||||||
prefs = get_addon_prefs(context)
|
prefs = get_addon_prefs(context)
|
||||||
prefs.is_svn_installed = check_svn_installed()
|
prefs.is_svn_installed = check_svn_installed()
|
||||||
@ -71,52 +70,42 @@ def ensure_svn_of_current_file(_scene=None):
|
|||||||
|
|
||||||
scene_svn = context.scene.svn
|
scene_svn = context.scene.svn
|
||||||
|
|
||||||
|
# If we have any repository entries, make sure at least one is active.
|
||||||
prefs.sync_repo_info_file()
|
prefs.sync_repo_info_file()
|
||||||
|
|
||||||
if prefs.active_repo_idx == -1 and len(prefs.repositories) > 0:
|
if prefs.active_repo_idx == -1 and len(prefs.repositories) > 0:
|
||||||
prefs.active_repo_idx = 0
|
prefs.active_repo_idx = 0
|
||||||
|
elif prefs.active_repo_idx > len(prefs.repositories)-1:
|
||||||
|
prefs.active_repo_idx = 0
|
||||||
|
else:
|
||||||
|
prefs.active_repo_idx = prefs.active_repo_idx
|
||||||
|
|
||||||
repo = prefs.active_repo
|
# If the file is unsaved, nothing more to do.
|
||||||
if repo and repo.is_cred_entered and repo.authenticated:
|
if not bpy.data.filepath:
|
||||||
status = Processes.get('Status')
|
scene_svn.svn_url = ""
|
||||||
if status and time.time() - status.timestamp_last_update < status.repeat_delay + 5:
|
return
|
||||||
# If the SVN Status background process has recently processed data,
|
|
||||||
# there is no need to re-initialize everything.
|
|
||||||
# This happens when a file that was already saved is saved again,
|
|
||||||
# or a file from the same repository as the previous one is loaded.
|
|
||||||
Processes.restart('Status')
|
|
||||||
return
|
|
||||||
|
|
||||||
|
# If file is not in a repo, nothing more to do.
|
||||||
|
is_in_repo = set_scene_svn_info(context)
|
||||||
|
if not is_in_repo:
|
||||||
|
return
|
||||||
|
|
||||||
|
# If we switched repos, reset auth flags and authenticate the new repo.
|
||||||
for repo in prefs.repositories:
|
for repo in prefs.repositories:
|
||||||
# This would ideally only run when opening Blender for the first
|
# This would ideally only run when opening Blender for the first
|
||||||
# time, but there is no app handler for that, sadly.
|
# time, but there is no app handler for that, sadly.
|
||||||
repo.authenticated = False
|
repo.authenticated = False
|
||||||
repo.auth_failed = False
|
repo.auth_failed = False
|
||||||
|
|
||||||
if prefs.active_repo_mode == 'CURRENT_BLEND':
|
# If file is in an existing repo, we should switch over to that repo.
|
||||||
if not bpy.data.filepath:
|
for i, existing_repo in enumerate(prefs.repositories):
|
||||||
scene_svn.svn_url = ""
|
if ( existing_repo.url == scene_svn.svn_url and
|
||||||
return
|
existing_repo.directory == scene_svn.svn_directory
|
||||||
|
):
|
||||||
is_in_repo = set_scene_svn_info(context)
|
prefs.active_repo_idx = i
|
||||||
if not is_in_repo:
|
break
|
||||||
return
|
|
||||||
|
|
||||||
repo = scene_svn.get_scene_repo(context)
|
|
||||||
if not repo:
|
|
||||||
repo = prefs.init_repo(context, scene_svn.svn_directory)
|
|
||||||
|
|
||||||
for i, other_repo in enumerate(prefs.repositories):
|
|
||||||
if other_repo == repo:
|
|
||||||
prefs.active_repo_idx = i
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
repo = prefs.active_repo
|
# If file is in a non-existing repo, initialize that repo.
|
||||||
if not repo:
|
repo = prefs.init_repo(context, scene_svn.svn_directory)
|
||||||
return
|
|
||||||
|
|
||||||
if repo.is_cred_entered:
|
|
||||||
repo.authenticate(context)
|
|
||||||
|
|
||||||
|
|
||||||
def set_scene_svn_info(context) -> bool:
|
def set_scene_svn_info(context) -> bool:
|
||||||
|
@ -18,9 +18,6 @@ class SVN_UL_repositories(UIList):
|
|||||||
repo = item
|
repo = item
|
||||||
row = layout.row()
|
row = layout.row()
|
||||||
|
|
||||||
prefs = get_addon_prefs(context)
|
|
||||||
if prefs.active_repo_mode == 'CURRENT_BLEND' and repo != context.scene.svn.get_repo(context):
|
|
||||||
row.enabled = False
|
|
||||||
row.label(text=repo.display_name)
|
row.label(text=repo.display_name)
|
||||||
|
|
||||||
if not repo.dir_exists:
|
if not repo.dir_exists:
|
||||||
@ -198,10 +195,6 @@ def draw_prefs_checkout(self, context):
|
|||||||
def draw_prefs_repos(self, context) -> None:
|
def draw_prefs_repos(self, context) -> None:
|
||||||
layout = self.layout
|
layout = self.layout
|
||||||
|
|
||||||
row = layout.row()
|
|
||||||
row.use_property_split = True
|
|
||||||
row.prop(self, 'active_repo_mode', expand=True)
|
|
||||||
|
|
||||||
auth_in_progress = False
|
auth_in_progress = False
|
||||||
auth_error = False
|
auth_error = False
|
||||||
auth_proc = Processes.get('Authenticate')
|
auth_proc = Processes.get('Authenticate')
|
||||||
@ -209,12 +202,6 @@ def draw_prefs_repos(self, context) -> None:
|
|||||||
auth_in_progress = auth_proc.is_running
|
auth_in_progress = auth_proc.is_running
|
||||||
auth_error = auth_proc.error
|
auth_error = auth_proc.error
|
||||||
|
|
||||||
if self.active_repo_mode == 'CURRENT_BLEND' and not context.scene.svn.get_repo(context):
|
|
||||||
split = layout.split(factor=0.4)
|
|
||||||
split.row()
|
|
||||||
split.row().label(text="Current file is not in a repository.")
|
|
||||||
return
|
|
||||||
|
|
||||||
repo_col = layout.column()
|
repo_col = layout.column()
|
||||||
split = repo_col.row().split()
|
split = repo_col.row().split()
|
||||||
split.row().label(text="SVN Repositories:")
|
split.row().label(text="SVN Repositories:")
|
||||||
|
@ -16,12 +16,7 @@ class VIEW3D_PT_svn_credentials(Panel):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
prefs = get_addon_prefs(context)
|
repo = context.scene.svn.get_repo(context)
|
||||||
if prefs.active_repo_mode == 'CURRENT_BLEND':
|
|
||||||
repo = context.scene.svn.get_scene_repo(context)
|
|
||||||
else:
|
|
||||||
repo = context.scene.svn.get_repo(context)
|
|
||||||
|
|
||||||
return repo and not repo.authenticated
|
return repo and not repo.authenticated
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
|
Loading…
Reference in New Issue
Block a user