from ..config import ADDON_MODULE_NAME from typing import Optional, List, Tuple import bpy def get_addon_preferences() -> 'WBS_AP_Preferences': """ Gets current Blender addon preferences for this addon in any context. :return: Addon preferences. """ return bpy.context.preferences.addons[ADDON_MODULE_NAME].preferences def get_project_preferences() -> Optional['WBS_PG_ProjectPreferences']: """ Gets current project preferences in any context. :return: Project preferences or None. """ addon_preferences = get_addon_preferences() if not len(addon_preferences.projects): raise UserWarning("No active project. Check WBS settings.") return addon_preferences.projects[addon_preferences.active_project_index] class WBS_UL_Projects(bpy.types.UIList): """ UI List displaying currently saved projects. """ def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): split = layout.split(factor=0.3) split.prop(item, "name", text="", emboss=False, translate=False , icon='RADIOBUT_ON' if index == data.active_project_index else 'RADIOBUT_OFF') def invoke(self, context, event): ... class WBS_PG_ExportMethodSetting(bpy.types.PropertyGroup): """ Property group for individual export method settings. """ name: bpy.props.StringProperty( name="Export Setting Name", description="Name of the export folder setting" ) value: bpy.props.StringProperty( name="Export Directory Path", description="Value of the export folder setting", subtype='DIR_PATH' ) class WBS_PG_ProjectPreferences(bpy.types.PropertyGroup): """ Property group holding data for project preferences set. """ wow_path: bpy.props.StringProperty( name="WoW Client Path", subtype='DIR_PATH' ) wmv_path: bpy.props.StringProperty( name="WoW Model Viewer Log Path", subtype='FILE_PATH' ) wow_export_path: bpy.props.StringProperty( name="WoW Export Runtimelog Path", subtype='FILE_PATH' ) noggit_red_path: bpy.props.StringProperty( name="Noggit Red log Path", subtype='FILE_PATH' ) time_import_method: bpy.props.EnumProperty( name="Time Conversion Import Method", items=[ ('Convert', "Convert to 30 FPS", "(WBS Method)"), ('Keep Original', "Keep_Original", "Don't convert timestamps"), ], default='Convert', description="Choose the preferred method for timestamp import." ) import_method: bpy.props.EnumProperty( name="Import Method", items=[ ('WMV', "WMV", "Use WoW Model Viewer"), ('WowExport', "WowExport", "Use WoW Export"), ('NoggitRed', "NoggitRed", "Use Noggit Red"), ], default='WMV', description="Choose the preferred method of import for WoW files." ) export_method_settings: bpy.props.CollectionProperty( type=WBS_PG_ExportMethodSetting, name="Export Folder Settings", description="Settings for the export methods" ) export_dir_path: bpy.props.StringProperty( name="Export Directory Path", description="A directory for WBS to export M2/WBS, could be ascension Data client, or a patch folder inside it.", subtype="DIR_PATH" ) active_export_method_index: bpy.props.IntProperty(default=0) def update_export_dir_path(self, context): method = self.export_method_settings[int(self.export_method_enum)] self.active_export_method_index = int(self.export_method_enum) self.export_dir_path = method.value def export_method_items(self, context) -> List[Tuple[str, str, str]]: return [(str(i), method.name, "") for i, method in enumerate(self.export_method_settings)] export_method_enum: bpy.props.EnumProperty( name="Export Method", description="Select Export Method", items=export_method_items, update=update_export_dir_path ) cache_dir_path: bpy.props.StringProperty( name="Cache Directory Path", description="Any folder that can be used to store textures and other temporary files.", subtype="DIR_PATH" ) project_dir_path: bpy.props.StringProperty( name="Project Directory Path", description="A directory Blender saves WoW files to and treats it as top-priority patch.", subtype="DIR_PATH" ) merge_vertices: bpy.props.BoolProperty( name="Merge Vertices Algorythm when quick saving M2's", description="Use the merge vertices algorythm when quick saving m2's with topbar button", default=True ) verbosity_level: bpy.props.EnumProperty( name="Log Verbosity", description="Controls how detailed output messages should be", items=[ ('1', "ERROR", "Only show critical errors"), ('2', "WARNING", "Show warnings and errors"), ('3', "INFO", "Show general info, warnings, and errors"), ('4', "DEBUG", "Show all messages including debug output"), ], default='3', options=set() ) scene_type: bpy.props.EnumProperty( name="Default Scene Type", description="Controls which scene type should be the default", items=[ ('M2', "M2", "Default to M2"), ('WMO', "WMO", "Default to WMO"), ], default='WMO', options=set() ) class WBS_OT_ProjectListActions(bpy.types.Operator): """ Moves items up and down the list of projects, adds or removes. """ bl_idname = "wbs.project_list_action" bl_label = "List Actions" bl_description = "Move items up and down, add and remove" bl_options = {'REGISTER', 'INTERNAL'} action: bpy.props.EnumProperty( items=( ('UP', "Up", ""), ('DOWN', "Down", ""), ('REMOVE', "Remove", ""), ('ADD', "Add", ""), ('DUPLICATE', "Duplicate", ""))) @classmethod def description(cls, context, properties): match properties.action: case 'UP': return "Move project up the list" case 'DOWN': return "Move project down the list" case 'ADD': return "Add new project" case 'DUPLICATE': return "Duplicate current project" case 'REMOVE': return "Remove project from the list" case _: raise NotImplementedError() def duplicate_project(self, source, target): target.name = f"{source.name}_copy" target.verbosity_level = source.verbosity_level target.scene_type = source.scene_type target.wow_path = source.wow_path target.wmv_path = source.wmv_path target.wow_export_path = source.wow_export_path target.noggit_red_path = source.noggit_red_path target.time_import_method = source.time_import_method target.import_method = source.import_method target.cache_dir_path = source.cache_dir_path target.project_dir_path = source.project_dir_path target.export_dir_path = source.export_dir_path target.merge_vertices = source.merge_vertices for setting in source.export_method_settings: new_setting = target.export_method_settings.add() new_setting.name = setting.name new_setting.value = setting.value def invoke(self, context, event): addon_prefs = get_addon_preferences() idx = addon_prefs.active_project_index match self.action: case 'DOWN': if idx < len(addon_prefs.projects) - 1: addon_prefs.projects.move(idx, idx + 1) addon_prefs.active_project_index += 1 case 'UP': if idx >= 1: addon_prefs.projects.move(idx, idx - 1) addon_prefs.active_project_index -= 1 case 'REMOVE': if len(addon_prefs.projects): addon_prefs.active_project_index -= 1 addon_prefs.projects.remove(idx) case 'ADD': item = addon_prefs.projects.add() item.name = 'New project' addon_prefs.active_project_index = len(addon_prefs.projects) - 1 case 'DUPLICATE': if len(addon_prefs.projects): source = addon_prefs.projects[idx] target = addon_prefs.projects.add() self.duplicate_project(source, target) addon_prefs.active_project_index = len(addon_prefs.projects) - 1 return {"FINISHED"} class WBS_UL_Export(bpy.types.UIList): """ UI List displaying currently saved export methods. """ def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): split = layout.split(factor=0.3) split.prop(item, "name", text="", emboss=False, translate=False , icon='RADIOBUT_ON' if index == data.active_export_method_index else 'RADIOBUT_OFF') def invoke(self, context, event): ... class WBS_OT_ExportMethodActions(bpy.types.Operator): """ Adds or removes export method settings. """ bl_idname = "wbs.export_method_action" bl_label = "Export Method Actions" bl_description = "Add or remove export method settings" bl_options = {'REGISTER', 'INTERNAL'} action: bpy.props.EnumProperty( items=( ('UP', "Up", ""), ('DOWN', "Down", ""), ('ADD', "Add", ""), ('REMOVE', "Remove", ""))) @classmethod def description(cls, context, properties): match properties.action: case 'UP': return "Move project up the list" case 'DOWN': return "Move project down the list" case 'ADD': return "Add new export method setting" case 'REMOVE': return "Remove selected export method setting" case _: raise NotImplementedError() def invoke(self, context, event): proj_prefs = get_project_preferences() idx = proj_prefs.active_export_method_index match self.action: case 'DOWN': if idx < len(proj_prefs.export_method_settings) - 1: proj_prefs.export_method_settings.move(idx, idx + 1) proj_prefs.active_export_method_index += 1 case 'UP': if idx >= 1: proj_prefs.export_method_settings.move(idx, idx - 1) proj_prefs.active_export_method_index -= 1 case 'ADD': item = proj_prefs.export_method_settings.add() item.name = 'New setting' item.value = '' proj_prefs.active_export_method_index = len(proj_prefs.export_method_settings) - 1 case 'REMOVE': if len(proj_prefs.export_method_settings): proj_prefs.export_method_settings.remove(idx) proj_prefs.active_export_method_index -= 1 return {"FINISHED"} class WBS_AP_Preferences(bpy.types.AddonPreferences): """ Stores global preferences for the addon. """ bl_idname = 'io_scene_wmo' projects: bpy.props.CollectionProperty( type=WBS_PG_ProjectPreferences, name='Projects', description='Project presets.', options=set() ) active_project_index: bpy.props.IntProperty(default=0) def draw(self, context: bpy.types.Context): layout = self.layout row = layout.row() row.template_list('WBS_UL_Projects', '', self, 'projects', self, 'active_project_index', rows=2) col = row.column(align=True) col.operator("wbs.project_list_action", text="Create project").action = 'ADD' col.operator("wbs.project_list_action", text="Duplicate project").action = 'DUPLICATE' col.operator("wbs.project_list_action", text="Remove project").action = 'REMOVE' col.separator() col.operator("wbs.project_list_action", icon='TRIA_UP', text="").action = 'UP' col.operator("wbs.project_list_action", icon='TRIA_DOWN', text="").action = 'DOWN' col.separator() if proj_prefs := get_project_preferences(): col = layout.column(align=True) col.label(text='Project settings:', icon='SETTINGS') box = col.box() box.prop(proj_prefs, 'wow_path') box.prop(proj_prefs, 'time_import_method') box.prop(proj_prefs, 'import_method') if proj_prefs.import_method == 'WMV': box.prop(proj_prefs, 'wmv_path') elif proj_prefs.import_method == 'WowExport': box.prop(proj_prefs, 'wow_export_path') elif proj_prefs.import_method == 'NoggitRed': box.prop(proj_prefs, 'noggit_red_path') box.prop(proj_prefs, 'cache_dir_path') box.prop(proj_prefs, 'project_dir_path') box.prop(proj_prefs, 'export_method_enum', text='Export Directory Path') box.prop(proj_prefs, "verbosity_level") box.prop(proj_prefs, "scene_type") col = layout.column(align=True) col.label(text='Export Folder Settings:', icon='SETTINGS') row = col.row() row.template_list('WBS_UL_Export', '', proj_prefs, 'export_method_settings', proj_prefs, 'active_export_method_index') sub_col = row.column(align=True) sub_col.operator("wbs.export_method_action", text="", icon='ADD').action = 'ADD' sub_col.operator("wbs.export_method_action", text="", icon='REMOVE').action = 'REMOVE' sub_col.operator("wbs.export_method_action", icon='TRIA_UP', text="").action = 'UP' sub_col.operator("wbs.export_method_action", icon='TRIA_DOWN', text="").action = 'DOWN' if proj_prefs.export_method_settings: setting = proj_prefs.export_method_settings[proj_prefs.active_export_method_index] box = col.box() box.prop(setting, 'name') box.prop(setting, 'value') col.separator() col = layout.column(align=True) col.label(text='M2 Quick Save Settings:', icon='SETTINGS') box = col.box() box.prop(proj_prefs, 'merge_vertices', text='Merge Vertices')