139 lines
6.8 KiB
Python
139 lines
6.8 KiB
Python
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
|
|
import bpy
|
|
from bpy.types import Panel, UIList
|
|
from . import properties
|
|
|
|
|
|
class DLM_UL_library_list(UIList):
|
|
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
|
|
if self.layout_type in {"DEFAULT", "COMPACT"}:
|
|
layout.scale_x = 0.4
|
|
layout.label(text=item.name)
|
|
layout.scale_x = 0.3
|
|
if item.is_missing:
|
|
layout.label(text="MISSING", icon="ERROR")
|
|
elif item.is_indirect:
|
|
layout.label(text="INDIRECT", icon="INFO")
|
|
else:
|
|
layout.label(text="OK", icon="FILE_BLEND")
|
|
layout.scale_x = 0.3
|
|
path_text = item.filepath or ""
|
|
if path_text.startswith("\\\\"):
|
|
parts = path_text.split("\\")
|
|
short_path = f"\\\\{parts[2]}\\" if len(parts) >= 3 else "\\\\ (network)"
|
|
elif len(path_text) >= 2 and path_text[1] == ":":
|
|
short_path = f"{path_text[:2]}\\"
|
|
elif path_text.startswith("//"):
|
|
short_path = "// (relative)"
|
|
else:
|
|
short_path = path_text[:15] + "..." if len(path_text) > 15 else path_text
|
|
layout.label(text=short_path, icon="FILE_FOLDER")
|
|
elif self.layout_type == "GRID":
|
|
layout.alignment = "CENTER"
|
|
layout.label(text="", icon="FILE_BLEND")
|
|
|
|
|
|
def _get_short_path(filepath):
|
|
if not filepath:
|
|
return "Unknown"
|
|
if filepath.startswith("//"):
|
|
return "// (relative)"
|
|
if filepath.startswith("\\\\"):
|
|
parts = filepath.split("\\")
|
|
return f"\\\\{parts[2]}\\" if len(parts) >= 3 else "\\\\ (network)"
|
|
if len(filepath) >= 2 and filepath[1] == ":":
|
|
return f"{filepath[:2]}\\"
|
|
if filepath.startswith("/"):
|
|
parts = filepath.split("/")
|
|
return f"/{parts[1]}/" if len(parts) >= 2 else "/ (root)"
|
|
return "Unknown"
|
|
|
|
|
|
class DLM_PT_main_panel(Panel):
|
|
bl_space_type = "VIEW_3D"
|
|
bl_region_type = "UI"
|
|
bl_category = "Dynamic Link Manager"
|
|
bl_label = "Dynamic Link Manager"
|
|
|
|
def draw_header(self, context):
|
|
layout = self.layout
|
|
layout.operator("preferences.addon_show", text="", icon="PREFERENCES").module = __package__.rsplit(".", 1)[0]
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
props = context.scene.dynamic_link_manager
|
|
|
|
# Path management
|
|
row = layout.row()
|
|
row.operator("dlm.make_paths_relative", text="Make Paths Relative", icon="FILE_PARENT")
|
|
row.operator("dlm.make_paths_absolute", text="Make Paths Absolute", icon="FILE_FOLDER")
|
|
|
|
# CharMig section (placeholder; full UI in implementation step 5)
|
|
box = layout.box()
|
|
box.label(text="Character Migrator")
|
|
row = box.row()
|
|
row.prop(props, "migrator_mode", text="Automatic pair discovery")
|
|
row = box.row()
|
|
row.prop(props, "original_character", text="Original")
|
|
row.operator("dlm.picker_original_character", text="", icon="EYEDROPPER")
|
|
row = box.row()
|
|
row.prop(props, "replacement_character", text="Replacement")
|
|
row.operator("dlm.picker_replacement_character", text="", icon="EYEDROPPER")
|
|
row = box.row()
|
|
row.operator("dlm.run_character_migration", text="Run migration", icon="ARMATURE_DATA")
|
|
|
|
# Linked Libraries (own dropdown)
|
|
box = layout.box()
|
|
box.label(text="Linked Libraries Analysis")
|
|
row = box.row()
|
|
row.operator("dlm.scan_linked_assets", text="Scan Linked Assets", icon="FILE_REFRESH")
|
|
missing_count = sum(1 for lib in props.linked_libraries if lib.is_missing)
|
|
if missing_count > 0:
|
|
row.label(text=f"({props.linked_assets_count} libraries, {missing_count} missing)", icon="ERROR")
|
|
else:
|
|
row.label(text=f"({props.linked_assets_count} libraries)")
|
|
if props.linked_assets_count > 0:
|
|
row = box.row(align=True)
|
|
icon = "DISCLOSURE_TRI_DOWN" if props.linked_libraries_expanded else "DISCLOSURE_TRI_RIGHT"
|
|
row.prop(props, "linked_libraries_expanded", text="", icon=icon, icon_only=True)
|
|
row.label(text="Linked Libraries:")
|
|
row.label(text=f"({props.linked_assets_count} libraries)")
|
|
if props.linked_libraries_expanded:
|
|
row = box.row()
|
|
row.template_list("DLM_UL_library_list", "", props, "linked_libraries", props, "linked_libraries_index")
|
|
row = box.row()
|
|
row.operator("dlm.reload_libraries", text="Reload Libraries", icon="FILE_REFRESH")
|
|
prefs = context.preferences.addons.get(__package__.rsplit(".", 1)[0])
|
|
if prefs and prefs.preferences.search_paths:
|
|
for i, path_item in enumerate(prefs.preferences.search_paths):
|
|
row = box.row()
|
|
row.prop(path_item, "path", text=f"Search path {i+1}")
|
|
row.operator("dlm.browse_search_path", text="", icon="FILE_FOLDER").index = i
|
|
row.operator("dlm.remove_search_path", text="", icon="REMOVE").index = i
|
|
row = box.row()
|
|
row.alignment = "RIGHT"
|
|
row.operator("dlm.add_search_path", text="Add search path", icon="ADD")
|
|
if missing_count > 0:
|
|
row = box.row()
|
|
row.operator("dlm.find_libraries_in_folders", text="Find libraries in these folders", icon="VIEWZOOM")
|
|
if props.linked_libraries and 0 <= props.linked_libraries_index < len(props.linked_libraries):
|
|
selected_lib = props.linked_libraries[props.linked_libraries_index]
|
|
info_box = box.box()
|
|
if selected_lib.is_missing:
|
|
info_box.alert = True
|
|
info_box.label(text=f"Selected: {selected_lib.name}")
|
|
if selected_lib.is_missing:
|
|
info_box.label(text="Status: MISSING", icon="ERROR")
|
|
elif selected_lib.is_indirect:
|
|
info_box.label(text="Status: INDIRECT", icon="INFO")
|
|
else:
|
|
info_box.label(text="Status: OK", icon="FILE_BLEND")
|
|
info_box.label(text=f"Path: {selected_lib.filepath}", icon="FILE_FOLDER")
|
|
info_box.operator("dlm.open_linked_file", text="Open Blend", icon="FILE_BLEND").filepath = selected_lib.filepath
|
|
op = info_box.operator("dlm.relocate_single_library", text="Relocate Library", icon="FILE_FOLDER")
|
|
op.target_filepath = selected_lib.filepath
|