280 lines
8.8 KiB
Python
280 lines
8.8 KiB
Python
# SPDX-FileCopyrightText: 2023 Blender Studio Tools Authors
|
|
#
|
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
from bpy.props import IntProperty, BoolProperty
|
|
from bpy.types import UIList, Panel, Operator
|
|
from ..util import get_addon_prefs
|
|
|
|
|
|
class SVN_UL_log(UIList):
|
|
show_all_logs: BoolProperty(
|
|
name='Show All Logs',
|
|
description='Show the complete SVN Log, instead of only entries that affected the currently selected file',
|
|
default=False
|
|
)
|
|
|
|
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
|
if self.layout_type != 'DEFAULT':
|
|
raise NotImplemented
|
|
|
|
svn = data
|
|
log_entry = item
|
|
|
|
num, auth, date, msg = layout_log_split(layout.row())
|
|
|
|
active_file = svn.active_file
|
|
num.label(text=str(log_entry.revision_number))
|
|
if item.revision_number == active_file.revision:
|
|
num.operator('svn.tooltip_log', text="", icon='LAYER_ACTIVE',
|
|
emboss=False).log_rev = log_entry.revision_number
|
|
elif log_entry.changes_file(active_file):
|
|
get_older = num.operator(
|
|
'svn.download_file_revision', text="", icon='IMPORT', emboss=False)
|
|
get_older.revision = log_entry.revision_number
|
|
get_older.file_rel_path = active_file.svn_path
|
|
auth.label(text=log_entry.revision_author)
|
|
date.label(text=log_entry.revision_date_simple)
|
|
|
|
commit_msg = log_entry.commit_message
|
|
commit_msg = commit_msg.split(
|
|
"\n")[0] if "\n" in commit_msg else commit_msg
|
|
commit_msg = commit_msg[:50] + \
|
|
"..." if len(commit_msg) > 52 else commit_msg
|
|
msg.alignment = 'LEFT'
|
|
msg.operator("svn.display_commit_message", text=commit_msg,
|
|
emboss=False).log_rev = log_entry.revision_number
|
|
|
|
def filter_items(self, context, data, propname):
|
|
"""Custom filtering functionality:
|
|
- Always sort by descending revision number
|
|
- Allow searching for various criteria
|
|
"""
|
|
svn = data
|
|
log_entries = getattr(data, propname)
|
|
|
|
# Start off with all entries flagged as visible.
|
|
flt_flags = [self.bitflag_filter_item] * len(log_entries)
|
|
# Always sort by descending revision number
|
|
flt_neworder = sorted(range(len(log_entries)),
|
|
key=lambda i: log_entries[i].revision_number)
|
|
flt_neworder.reverse()
|
|
|
|
if not self.show_all_logs:
|
|
flt_flags = [
|
|
log_entry.affects_active_file * self.bitflag_filter_item
|
|
for log_entry in log_entries
|
|
]
|
|
|
|
if self.filter_name:
|
|
# Filtering: Allow comma-separated keywords.
|
|
# ALL keywords must be found somewhere in the log entry for it to show up.
|
|
filter_words = [word.strip().lower()
|
|
for word in self.filter_name.split(",")]
|
|
for idx, log_entry in enumerate(log_entries):
|
|
for filter_word in filter_words:
|
|
if filter_word not in log_entry.text_to_search:
|
|
flt_flags[idx] = 0
|
|
break
|
|
|
|
return flt_flags, flt_neworder
|
|
|
|
def draw_filter(self, context, layout):
|
|
"""Custom filtering UI.
|
|
"""
|
|
main_row = layout.row()
|
|
main_row.prop(self, 'filter_name', text="")
|
|
main_row.prop(self, 'show_all_logs', text="",
|
|
toggle=True, icon='ALIGN_JUSTIFY')
|
|
|
|
|
|
def is_log_useful(context) -> bool:
|
|
"""Return whether the log has any useful info to display."""
|
|
|
|
prefs = get_addon_prefs(context)
|
|
repo = prefs.active_repo
|
|
|
|
if not repo or not repo.authenticated:
|
|
return False
|
|
|
|
if len(repo.log) == 0 or len(repo.external_files) == 0:
|
|
return False
|
|
active_file = repo.active_file
|
|
if active_file.status in ['unversioned', 'added']:
|
|
return False
|
|
|
|
any_visible = any([file.show_in_filelist for file in repo.external_files])
|
|
if not any_visible:
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
class VIEW3D_PT_svn_log(Panel):
|
|
"""Display the revision history of the selected file."""
|
|
bl_space_type = 'VIEW_3D'
|
|
bl_region_type = 'UI'
|
|
bl_category = 'SVN'
|
|
bl_label = 'Revision History'
|
|
bl_parent_id = "VIEW3D_PT_svn_files"
|
|
bl_options = {'DEFAULT_CLOSED'}
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return is_log_useful(context)
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
layout.use_property_split = True
|
|
layout.use_property_decorate = False
|
|
|
|
draw_svn_log(context, layout)
|
|
|
|
|
|
def layout_log_split(layout):
|
|
main = layout.split(factor=0.4)
|
|
num_and_auth = main.row()
|
|
date_and_msg = main.row()
|
|
|
|
num_and_auth_split = num_and_auth.split(factor=0.5)
|
|
num = num_and_auth_split.row()
|
|
auth = num_and_auth_split.row()
|
|
|
|
date_and_msg_split = date_and_msg.split(factor=0.3)
|
|
date = date_and_msg_split.row()
|
|
msg = date_and_msg_split.row()
|
|
|
|
return num, auth, date, msg
|
|
|
|
|
|
def draw_svn_log(context, layout):
|
|
num, auth, date, msg = layout_log_split(layout.row())
|
|
num.label(text="Rev. #")
|
|
auth.label(text="Author")
|
|
date.label(text="Date")
|
|
msg.label(text="Message")
|
|
|
|
prefs = get_addon_prefs(context)
|
|
repo = prefs.active_repo
|
|
layout.template_list(
|
|
"SVN_UL_log",
|
|
"svn_log",
|
|
repo,
|
|
"log",
|
|
repo,
|
|
"log_active_index",
|
|
)
|
|
|
|
active_log = repo.active_log
|
|
if not active_log:
|
|
return
|
|
layout.label(text="Revision Date: " + active_log.revision_date)
|
|
|
|
layout.label(
|
|
text=f"Files changed in revision `r{active_log.revision_number}`:")
|
|
|
|
col = layout.column(align=True)
|
|
row = col.row()
|
|
split = row.split(factor=0.80)
|
|
split.label(text=" Filepath")
|
|
row = split.row()
|
|
row.alignment = 'RIGHT'
|
|
row.label(text="Action")
|
|
for f in active_log.changed_files:
|
|
row = col.row()
|
|
split = row.split(factor=0.90)
|
|
split.prop(f, 'name', emboss=False, text="", icon=f.file_icon)
|
|
row = split.row()
|
|
row.alignment = 'RIGHT'
|
|
row.operator('svn.explain_status', text="",
|
|
icon=f.status_icon, emboss=False).status = f.status
|
|
|
|
|
|
def execute_tooltip_log(self, context):
|
|
"""Set the index on click, to act as if this operator button was
|
|
click-through in the UIList."""
|
|
repo = context.scene.svn.get_repo(context)
|
|
tup = repo.get_log_by_revision(self.log_rev)
|
|
if tup:
|
|
repo.log_active_index = tup[0]
|
|
return {'FINISHED'}
|
|
|
|
|
|
class SVN_OT_log_tooltip(Operator):
|
|
bl_idname = "svn.tooltip_log"
|
|
bl_label = "" # Don't want the first line of the tooltip on mouse hover.
|
|
# bl_description = "An operator to be drawn in the log list, that can display a dynamic tooltip"
|
|
bl_options = {'INTERNAL'}
|
|
|
|
log_rev: IntProperty(
|
|
description="Revision number of the log entry to show in the tooltip"
|
|
)
|
|
|
|
@classmethod
|
|
def description(cls, context, properties):
|
|
return "This is the currently checked out version of the file"
|
|
|
|
execute = execute_tooltip_log
|
|
|
|
|
|
class SVN_OT_log_show_commit_msg(Operator):
|
|
bl_idname = "svn.display_commit_message"
|
|
bl_label = "" # Don't want the first line of the tooltip on mouse hover.
|
|
# bl_description = "Show the currently active commit, using a dynamic tooltip"
|
|
bl_options = {'INTERNAL'}
|
|
|
|
log_rev: IntProperty(
|
|
description="Revision number of the log entry to show in the tooltip"
|
|
)
|
|
|
|
@classmethod
|
|
def description(cls, context, properties):
|
|
log_entry = context.scene.svn.get_repo(context).get_log_by_revision(properties.log_rev)[
|
|
1]
|
|
commit_msg = log_entry.commit_message
|
|
|
|
# Prettify the tooltips.
|
|
pretty_msg = ""
|
|
for line in commit_msg.split("\n"):
|
|
# Remove leading/trailing whitespace
|
|
line = line.strip()
|
|
|
|
# Add punctuation mark
|
|
if not (line.endswith(".") or line.endswith("!") or line.endswith("?")):
|
|
line = line + "."
|
|
|
|
# Split long lines into several
|
|
limit = 300
|
|
if len(line) > limit:
|
|
words = line.split(" ")
|
|
sub_lines = []
|
|
|
|
new_line = ""
|
|
for word in words:
|
|
if len(new_line) + len(word) < limit:
|
|
new_line += " "+word
|
|
else:
|
|
sub_lines.append(new_line)
|
|
new_line = word
|
|
else:
|
|
sub_lines.append(new_line)
|
|
line = "\n".join(sub_lines)
|
|
|
|
pretty_msg += "\n"+line
|
|
|
|
# Remove last period because Blender adds it.
|
|
if pretty_msg.endswith("."):
|
|
pretty_msg = pretty_msg[:-1]
|
|
|
|
return pretty_msg
|
|
|
|
execute = execute_tooltip_log
|
|
|
|
|
|
registry = [
|
|
VIEW3D_PT_svn_log,
|
|
SVN_UL_log,
|
|
SVN_OT_log_tooltip,
|
|
SVN_OT_log_show_commit_msg
|
|
]
|