# coding: utf-8 import errno import logging import os from PyQt5.QtCore import (QObject, pyqtSignal) from mhw_armor_edit.editor.models import FilePluginRegistry log = logging.getLogger() class WorkspaceFile(QObject): modifiedChanged = pyqtSignal(bool) reloaded = pyqtSignal() def __init__(self, directory, rel_path, data=None, parent=None): super().__init__(parent) self.directory = directory self.rel_path = rel_path self.abs_path, _ = directory.get_child_path(self.rel_path) self.data = data self.relations = {} self.attrs = {} def set_attrs(self, attrs): self.attrs.update(attrs) def get_attr(self, key): return self.attrs.get(key) def add_relation(self, key, ws_file): self.relations[key] = ws_file self.relations[key].modified_cb = self.handle_modified def get_relation_data(self, key): rel = self.relations.get(key) if rel is None: return None return rel.data def set_data(self, data): self.data = data self.data.modified_cb = self.handle_modified self.reloaded.emit() def handle_modified(self, modified): self.modifiedChanged.emit(modified) def set_directory(self, directory): self.directory = directory self.abs_path, _ = directory.get_child_path(self.rel_path) for rel in self.relations.values(): rel.set_directory(directory) def get_files_modified(self): files = [self, ] files.extend( rel for rel in self.relations.values() if rel.data.modified) return files def save(self): self.directory.ensure_dirs(self.rel_path) with open(self.abs_path, "wb") as fp: self.data.save(fp) def __repr__(self): return f"<WorkspaceFile {self.abs_path}>" def __hash__(self): return hash(self.directory) ^ hash(self.abs_path) class Directory(QObject): changed = pyqtSignal(str) def __init__(self, name, file_icon, path, parent=None): super().__init__(parent) self.name = name self.file_icon = file_icon self.path = path def __repr__(self): return f"<Directory {self.name}: {self.path}>" def set_path(self, path): self.path = path self.changed.emit(path) @property def is_valid(self): return self.path is not None and os.path.exists(self.path) def get_child_path(self, rel_path): path = os.path.join(self.path, rel_path) exists = os.path.exists(path) return path, exists def get_child_rel_path(self, abs_path): return os.path.relpath(abs_path, self.path) def ensure_dirs(self, rel_path): abs_path, _ = self.get_child_path(rel_path) dir_path = os.path.dirname(abs_path) try: os.makedirs(dir_path) except OSError as e: if e.errno != errno.EEXIST: raise class Workspace(QObject): fileOpened = pyqtSignal(str, str) fileActivated = pyqtSignal(str, str) fileClosed = pyqtSignal(str, str) fileLoadError = pyqtSignal(str, str, str) def __init__(self, directories, parent=None): super().__init__(parent) self.directories = directories self.files = dict() def open_file(self, directory, abs_path): abs_path = os.path.normpath(abs_path) rel_path = directory.get_child_rel_path(abs_path) if abs_path in self.files: self.fileActivated.emit(abs_path, rel_path) else: try: ws_file = WorkspaceFile(directory, rel_path, parent=self) FilePluginRegistry.load_model(ws_file) FilePluginRegistry.load_relations(ws_file, self.directories) self.files[abs_path] = ws_file self.fileOpened.emit(abs_path, rel_path) except Exception as e: log.exception("error loading path: %s", abs_path) self.fileLoadError.emit(abs_path, rel_path, str(e)) def open_file_any_dir(self, rel_path): for directory in self.directories: abs_path, exists = directory.get_child_path(rel_path) if exists: return self.open_file(directory, abs_path) def close_file(self, ws_file): try: self.files.pop(ws_file.abs_path) self.fileClosed.emit(ws_file.abs_path, ws_file.rel_path) except (ValueError, KeyError): log.exception("error while closing file %s", ws_file)