mirror of
https://github.com/aclist/dztui.git
synced 2026-08-26 17:57:06 +02:00
feat: add ModManager file
This commit is contained in:
parent
34d8545c94
commit
b8ed0723e1
178
dzgui/managers/mods.py
Normal file
178
dzgui/managers/mods.py
Normal file
@ -0,0 +1,178 @@
|
||||
import logging
|
||||
import shutil
|
||||
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from dzgui.api.mods import (
|
||||
get_delimited_mods,
|
||||
get_local_mod_path,
|
||||
find_stale_mods,
|
||||
_hash,
|
||||
remove_stale_signatures,
|
||||
)
|
||||
from dzgui.const.constants import APP_NAME, APPID_DAYZ, APPID_DAYZ_EXP
|
||||
from dzgui.const.enum import Preferences
|
||||
from dzgui.managers.threading import call_on_thread, StoredFunc, ThreadingManager
|
||||
from dzgui.model.model_factory import ModelFactory
|
||||
from dzgui.util.format import format_mods
|
||||
|
||||
import dzgui.api.pefile as PeFile
|
||||
|
||||
import gi
|
||||
|
||||
gi.require_version("Gtk", "3.0")
|
||||
from gi.repository import Gtk # noqa E402
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from dzgui.controllers.mc import Controller
|
||||
|
||||
logger = logging.getLogger(APP_NAME)
|
||||
|
||||
|
||||
class ModManager:
|
||||
"""
|
||||
Because mods may be dynamically updated on the system,
|
||||
this manager is instantiated each time the Mods page is opened
|
||||
and persists until the next time the page is opened
|
||||
"""
|
||||
def __init__(self, controller: "Controller") -> None:
|
||||
|
||||
self.controller = controller
|
||||
self.emitter = controller.get_emitter()
|
||||
self.prefs = controller.get_prefs()
|
||||
self.path = controller.query_config(Preferences.DEFAULT)
|
||||
self.treeview = self.controller.get_modtreeview()
|
||||
|
||||
self.thread_man = ThreadingManager(controller)
|
||||
self._get_mods()
|
||||
|
||||
@call_on_thread("getting mods")
|
||||
def _get_mods(self) -> list:
|
||||
mods = get_delimited_mods(self.path)
|
||||
if len(mods) < 1:
|
||||
msg = self.format_mod_statusbar()
|
||||
self.emitter.emit("mods_updated", msg, 0)
|
||||
return
|
||||
|
||||
func = StoredFunc(self._on_mods_loaded, mods)
|
||||
self.thread_man.set_cleanup_func(func)
|
||||
|
||||
def _on_mods_loaded(self, mods: list[int]) -> None:
|
||||
model = ModelFactory().make_mod_store()
|
||||
model.extend(mods)
|
||||
self.treeview.set_model(model)
|
||||
msg = self.format_mod_statusbar()
|
||||
mods = len(model)
|
||||
self.emitter.emit("mods_updated", msg, mods)
|
||||
|
||||
@call_on_thread("deleting mods")
|
||||
def delete_mods(self) -> None:
|
||||
sel = self.treeview.get_selection()
|
||||
model, pathlist = sel.get_selected_rows()
|
||||
# NOTE reverse when multiple selection
|
||||
iters = []
|
||||
for path in reversed(pathlist):
|
||||
_iter = self.delete_single_mod(path)
|
||||
iters.append(_iter)
|
||||
|
||||
func = StoredFunc(self._on_mods_deleted, iters)
|
||||
self.thread_man.set_cleanup_func(func)
|
||||
|
||||
def get_mod_from_tree_path(
|
||||
self, tree_path: Gtk.TreePath
|
||||
) -> tuple[str, Gtk.TreeIter]:
|
||||
model = self.treeview.get_model()
|
||||
tree_iter = model.get_iter(tree_path)
|
||||
mod = model.get(tree_iter, 2)[0]
|
||||
return mod, tree_iter
|
||||
|
||||
def delete_single_mod(self, tree_path: Gtk.TreePath) -> None:
|
||||
mod, _iter = self.get_mod_from_tree_path(tree_path)
|
||||
|
||||
steam_path = Path(self.path)
|
||||
mods_path = get_local_mod_path(steam_path)
|
||||
app_path = PeFile.get_nested_app_path(steam_path, APPID_DAYZ)
|
||||
|
||||
md5 = _hash(mod)
|
||||
symlink = app_path / md5
|
||||
symlink.unlink()
|
||||
shutil.rmtree(mods_path / mod)
|
||||
|
||||
# NOTE: second pass to unlink DAYZ_EXP mods
|
||||
# TODO: test this with working APPID_DAYZ_EXP installation
|
||||
try:
|
||||
app_path_exp = PeFile.get_nested_app_path(steam_path, APPID_DAYZ_EXP)
|
||||
symlink = app_path_exp / md5
|
||||
symlink.unlink()
|
||||
except PeFile.AppNotInstalledError:
|
||||
pass
|
||||
return _iter
|
||||
|
||||
def _on_mods_deleted(self, iters: list[Gtk.TreeIter]) -> None:
|
||||
for _iter in iters:
|
||||
self.treeview.get_model().remove(_iter)
|
||||
remove_stale_signatures(self.prefs.paths.config, self.prefs.paths.version)
|
||||
mods = len(self.treeview.get_model())
|
||||
msg = self.format_mod_statusbar()
|
||||
self.emitter.emit("mods_updated", msg, mods)
|
||||
|
||||
def uncolorize_mods(self) -> None:
|
||||
model = self.treeview.get_model()
|
||||
for mod in model:
|
||||
_iter = mod.iter
|
||||
path = model.get_path(_iter)
|
||||
model[path][4] = None
|
||||
path = Gtk.TreePath.new_from_indices([0])
|
||||
self.treeview.set_cursor(path)
|
||||
|
||||
def toggle_mod_selection(self, state: bool) -> None:
|
||||
sel = self.treeview.get_selection()
|
||||
if state:
|
||||
sel.select_all()
|
||||
else:
|
||||
sel.unselect_all()
|
||||
self.treeview.grab_focus()
|
||||
|
||||
def format_mod_statusbar(self) -> str:
|
||||
total_mods, total_size = self.calc_mod_size()
|
||||
msg = format_mods(total_size, total_mods)
|
||||
return msg
|
||||
|
||||
def calc_mod_size(self) -> tuple[int, int]:
|
||||
model = self.treeview.get_model()
|
||||
if model is None:
|
||||
return 0, 0
|
||||
total_mods = len(model)
|
||||
total_size = 0
|
||||
for mod in model:
|
||||
total_size += mod[3]
|
||||
return total_mods, total_size
|
||||
|
||||
def _on_stale_mods_found(self, stale_mods: list) -> None:
|
||||
"""Manipulates attached ListStore in the main event loop"""
|
||||
model = self.treeview.get_model()
|
||||
if model is None:
|
||||
return
|
||||
for mod in model:
|
||||
it = mod.iter
|
||||
path = model.get_path(it)
|
||||
if int(mod[2]) in stale_mods:
|
||||
model[path][4] = True
|
||||
self.emitter.emit("mods_highlighted")
|
||||
|
||||
@call_on_thread("working")
|
||||
def highlight_stale(self) -> None:
|
||||
# TODO: set progress bar for number of mods
|
||||
stale = find_stale_mods(self.prefs.paths.config)
|
||||
func = StoredFunc(self._on_stale_mods_found, stale)
|
||||
self.thread_man.set_cleanup_func(func)
|
||||
|
||||
def select_colorized(self) -> None:
|
||||
model = self.treeview.get_model()
|
||||
sel = self.treeview.get_selection()
|
||||
for mod in model:
|
||||
it = mod.iter
|
||||
path = model.get_path(it)
|
||||
if mod[4] is True:
|
||||
sel.select_path(path)
|
||||
Loading…
Reference in New Issue
Block a user