diff --git a/dzgui/controllers/mc.py b/dzgui/controllers/mc.py index 3b2326a..710effa 100644 --- a/dzgui/controllers/mc.py +++ b/dzgui/controllers/mc.py @@ -172,30 +172,30 @@ class Controller(GObject.GObject): def unblock_signals(self) -> None: self.block_signals(False) - @deprecated("Currently unused") - def block_signals(self, state: bool = True) -> None: - self.suppress_signal( - self.mediator.filters, - self.mediator.filters.maps_combo, - "_on_map_changed", - state, - ) - self.suppress_signal( - self.mediator.menu, - self.mediator.menu.selected_row, - "_on_tree_selection_changed", - state, - ) - self.suppress_signal( - self.mediator.menu, self.mediator.menu, "_on_keypress", state - ) - for check in self.mediator.filters.checks: - self.suppress_signal( - self.mediator.filters, - check, - "_on_check_toggled", - state, - ) + # @deprecated("Currently unused") + # def block_signals(self, state: bool = True) -> None: + # self.suppress_signal( + # self.mediator.filters, + # self.mediator.filters.maps_combo, + # "_on_map_changed", + # state, + # ) + # self.suppress_signal( + # self.mediator.menu, + # self.mediator.menu.selected_row, + # "_on_tree_selection_changed", + # state, + # ) + # self.suppress_signal( + # self.mediator.menu, self.mediator.menu, "_on_keypress", state + # ) + # for check in self.mediator.filters.checks: + # self.suppress_signal( + # self.mediator.filters, + # check, + # "_on_check_toggled", + # state, + # ) def suppress_signal( self, owner: Gtk.Widget, widget: Gtk.Widget, func_name: str, state: bool @@ -554,54 +554,6 @@ class Controller(GObject.GObject): client = self.query_config(Preferences.CLIENT) open_user_workshop(uid, client) - # def copy_log(self, paths: list[Gtk.TreePath]) -> str: - # if len(paths) < 1: - # return "" - # final = [] - # for path in paths: - # record = self.log_store[path] - # r = [el for el in record] - # concat = strings.delimiter.join(r) - # final.append(concat) - # text = "\n".join(final) - # return text - - # def get_col_value_by_path_index(self, path: Gtk.TreePath, index: int) -> Any: - # treeview = self.get_active_treeview() - # model = treeview.get_model() - # if model is None: - # return None - # value = model[path][index] - # return value - - # def copy_name(self, path: Gtk.TreePath) -> None: - # name = self.get_col_value_by_path_index(path, 0) - # if name is None: - # return - # self.copy_clipboard(name) - - # def copy_ip(self, path: Gtk.TreePath) -> None: - # treeview = self.get_active_treeview() - # record = treeview.get_record() - # self.copy_clipboard(f"{record.ip}:{record.qport}") - - # def copy_clipboard(self, text: str) -> None: - # self.clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD) - # self.clipboard.set_text(text, -1) - - # def open_mod_page(self, path: Gtk.TreePath) -> None: - # mod, it = self.get_mod_from_tree_path(path) - # cmd = self.query_config(Preferences.CLIENT) - # open_workshop_page(mod, cmd) - - # def get_mod_from_tree_path( - # self, tree_path: Gtk.TreePath - # ) -> tuple[str, Gtk.TreeIter]: - # model = self.get_mod_store() - # tree_iter = model.get_iter(tree_path) - # mod = model.get(tree_iter, 2)[0] - # return mod, tree_iter - def delete_single_mod_cleanup(self, _iter: Gtk.TreeIter) -> None: self.get_mod_store().remove(_iter) remove_stale_signatures(self.prefs.paths.config, self.prefs.paths.version) @@ -668,64 +620,8 @@ class Controller(GObject.GObject): self.emitter.emit("fav_server_changed", name, simple_ip) def menu_action(self, action: ContextMenu, tree: Gtk.TreeView) -> None: - # TODO: pass prefs into contextman context_man = ContextMenuManager(tree, self) context_man.process(action) - # context_man.process(action) - # match action: - # # NOTE: manipulates server stores and caches - # # add to saved servers model verbatim and sort in place - # # update tab with ! - # # update config file with IP - # # TODO: needs threading - # case ContextMenu.ADD_SERVER: - # pass - - # # spawn edit dialog and update cache, notes file - # case ContextMenu.ADD_NOTE: - # pass - - # case ContextMenu.COPY_CLIPBOARD: - # self.copy_ip(path) - - # case ContextMenu.COPY_NAME: - # self.copy_name(path) - - # case ContextMenu.DELETE_MOD: - # self.delete_single_mod(path) - # # TODO: connect to emitter automatically - # # Gtk.TreeModel, row-inserted/row-deleted - # # updates statusbar - # # FIXME: signal should instead be emitted off of treeview when rows added/inserted - # # self.update_mod_statusbar() - - # case ContextMenu.REFRESH_PLAYERS: - # # get record - # # call a2s on thread - # pass - - # # update history model, update tab label, pop off of queue, write new list into file - # # see dq.py - # case ContextMenu.REMOVE_HISTORY: - # pass - - # # reverse of ADD_SERVER - # case ContextMenu.REMOVE_SERVER: - # pass - - # case ContextMenu.SET_FAV: - # self.set_fav() - - # # NOTE: spawns dedicated dialogs - # # TODO: needs threading - # case ContextMenu.SHOW_DETAILS: - # pass - # # TODO: needs threading - # case ContextMenu.SHOW_MODS: - # pass - - # case ContextMenu.OPEN_WORKSHOP: - # self.open_mod_page(path) def toggle_mod_selection(self, state: bool) -> None: sel = self.mediator.modtreeview.get_selection() diff --git a/dzgui/views/mixins/context_mixin.py b/dzgui/views/mixins/context_mixin.py index 01b4e61..85fae0a 100644 --- a/dzgui/views/mixins/context_mixin.py +++ b/dzgui/views/mixins/context_mixin.py @@ -18,9 +18,8 @@ class ContextMixin(TreeView): widget: Gtk.Widget, event: Gdk.EventButton | Gdk.EventKey, ) -> None: - # FIXME: double click causes issue - # split into present by click and present by key + # FIXME: start debug log focused if self.is_selection_empty(): return False @@ -42,8 +41,7 @@ class ContextMixin(TreeView): self.context_menu.connect("key-press-event", self._on_key) for row in group.value: - item = Gtk.MenuItem(label=row.dict["label"]) - item.connect("activate", self._on_menu_click, row) + item = self._process_dynamic_row(row) self.context_menu.append(item) self.context_menu.show_all() @@ -58,6 +56,22 @@ class ContextMixin(TreeView): self.context_menu.select_first(False) + def _process_dynamic_row(self, row: ContextMenu) -> None: + if row == ContextMenu.ADD_SERVER and self.is_in_favs(): + row = ContextMenu.REMOVE_SERVER + + item = Gtk.MenuItem(label=row.dict["label"]) + item.connect("activate", self._on_menu_click, row) + + if row == ContextMenu.SHOW_MODS: + item.set_sensitive(self.is_modded()) + + return item + # TODO: notes cache + # if row == ContextMenu.ADD_NOTE: + # if self.get_record_string() in notes_cache: + # item.set_label(strings.edit_note) + def _process_button_event(self, event: Gdk.EventButton) -> None: try: pathinfo = self.get_path_at_pos(int(event.x), int(event.y)) @@ -71,24 +85,7 @@ class ContextMixin(TreeView): pass def _on_menu_click(self, widget: Gtk.MenuItem, enum: ContextMenu) -> None: - """ - Local mods page allows multi selection, so ensure that only focused row - is selected. Used by ContextMenu.OPEN_WORKSHOP and ContextMenu.DELETE_MOD. - - ModTreeView supports multi-delete, but context menu enforces - single deletion on the focused row. - - Debug log page allows multi selection and copy of rows, so special handling - is used per below. - """ - if enum == ContextMenu.COPY_LOG_CLIPBOARD: - model, records = self.get_selection().get_selected_rows() - clipboard = self.controller.copy_log(records) - self.controller.copy_clipboard(clipboard) - else: - #path = self.get_focused_row_path() - #self.controller.menu_action(enum, path) - self.controller.menu_action(enum, self) + self.controller.menu_action(enum, self) def _on_key(self, menu: Gtk.Menu, event: Gdk.EventKey) -> bool | None: if not is_navkey(event.keyval): diff --git a/dzgui/views/trees/tree_log.py b/dzgui/views/trees/tree_log.py index 535ae97..b5ec90a 100644 --- a/dzgui/views/trees/tree_log.py +++ b/dzgui/views/trees/tree_log.py @@ -8,6 +8,7 @@ from dzgui.views.trees.tree_base import TreeView from dzgui.views.mixins.context_mixin import ContextMixin import gi + gi.require_version("Gtk", "3.0") from gi.repository import Gtk, GLib, Gdk, GObject, Pango # noqa E402 @@ -16,6 +17,7 @@ if TYPE_CHECKING: logger = logging.getLogger(__name__) + class LogTreeView(ContextMixin, TreeView): def __init__(self, controller: "Controller") -> None: super().__init__(controller, menu=ContextMenuGroup.LOG) @@ -33,42 +35,16 @@ class LogTreeView(ContextMixin, TreeView): for i, column_title in enumerate(strings.log_cols): renderer = Gtk.CellRendererText() - column = Gtk.TreeViewColumn( - column_title, renderer, text=i - ) + column = Gtk.TreeViewColumn(column_title, renderer, text=i) column.set_sizing(Gtk.TreeViewColumnSizing.FIXED) column.set_resizable(True) column.set_sort_column_id(i) self.append_column(column) - self.connect("button-press-event", self._on_log_button_press) - self.connect("button-release-event", self._on_log_button_release) self.connect("key-press-event", self._on_log_keypress) + self.connect("button-press-event", self._on_log_keypress) self.s = self.get_selection().get_selected_rows() def _on_log_keypress(self, widget: Gtk.Widget, event: Gdk.EventKey) -> None: self.present_menu(widget, event) - - def _on_log_button_press(self, - widget: Gtk.Widget, - event: Gdk.EventButton - ) -> bool: - - """ - Prevents context menu from deselecting rows - """ - if event.button == 3: - self.s = self.get_selection().get_selected_rows() - return True - return False - - def _on_log_button_release(self, - widget: Gtk.Widget, - event: Gdk.EventButton - ) -> None: - if event.button == 3: - self.present_menu(widget, event) - self.get_selection().unselect_all() - for row in self.s[1]: - self.get_selection().select_path(row) diff --git a/dzgui/views/trees/tree_servers.py b/dzgui/views/trees/tree_servers.py index 0522e67..972a94f 100644 --- a/dzgui/views/trees/tree_servers.py +++ b/dzgui/views/trees/tree_servers.py @@ -1,11 +1,12 @@ import logging import threading + +from queue import Queue from typing import Any, Optional, Self from warnings import deprecated - from dzgui.views.mixins.context_mixin import ContextMixin -from dzgui.const.enum import ContextMenuGroup, ServerTab +from dzgui.const.enum import ContextMenu, ContextMenuGroup, ServerTab from dzgui.api.servers import Record from dzgui.api.servers import ping as Ping from dzgui.model.map_model import MapManager @@ -60,12 +61,10 @@ class ServerTreeView(ContextMixin, TreeView): self.handler_id: int self.query_func_jobs = 1 - self.seen_cache = [] - self.current_proc = None - from queue import Queue + self.queue = Queue() - #self.queue = multiprocessing.Queue() + # self.queue = multiprocessing.Queue() prefs = self.controller.get_prefs() columns = prefs.paths.columns @@ -132,42 +131,6 @@ class ServerTreeView(ContextMixin, TreeView): self.thread = None - def _get_ping( - self, - column: Gtk.TreeViewColumn, - cell: Gtk.CellRendererText, - model: Gtk.TreeModel, - it: Gtk.TreeIter, - data: Any, - ): - def ping_server(model, _iter, ip: str, qport: int, ping: int): - # res = Ping(0, model[_iter]) - res = Ping(0, ip, qport, ping) - ping = res.ping - print(res) - GLib.idle_add(lambda: model.set(_iter, ping_column, ping)) - - addr_column = 7 - qport_column = 8 - ping_column = 9 - # TODO: unpack model before entering thread - # test 40k servers with unpacked model - addr = model.get_value(it, addr_column).split(":")[0] - qport = model.get_value(it, qport_column) - ping = model.get_value(it, ping_column) - ip = f"{addr}:{qport}" - - if ip in self.seen_cache: - return - self.seen_cache.append(ip) - - thread = threading.Thread( - daemon=True, - target=ping_server, - args=(model, it, ip, qport, ping), - ) - thread.start() - def start_timeout(self) -> None: self.queue_id = GLib.timeout_add(QUEUE_CHECK_DELAY, self._check_result_queue) @@ -238,15 +201,7 @@ class ServerTreeView(ContextMixin, TreeView): # NOTE: get final width after drag action completes GLib.idle_add(self.controller.propagate_column_width, col) - #def terminate_process(self) -> None: - # if self.thread and self.thread.is_alive(): - # print("thread still exists") - # if self.current_proc and self.current_proc.is_alive(): - # self.current_proc.terminate() - def start_distcalc(self, emitter: Optional["Emitter"] = None): - #self.terminate_process() - #self.queue_id = GLib.timeout_add(QUEUE_CHECK_DELAY, self._check_result_queue) self.emitter.emit("distcalc_started") record = self.get_record() if record is None: @@ -261,11 +216,6 @@ class ServerTreeView(ContextMixin, TreeView): self.controller.set_statusbar_dist(haversine, self.get_enum()) return - #def t(ip, enum, queue) -> None: - # if queue.empty(): - # queue.put(["LONG DISTANCE", 0]) - - enum = self.get_enum() self.thread = threading.Thread( daemon=True, @@ -273,16 +223,13 @@ class ServerTreeView(ContextMixin, TreeView): args=(record.ip, enum, self.queue, self.controller), ) self.thread.start() - #self.current_proc = CalcDist( - # record.ip, self.get_enum(), self.queue, self.controller - #) - #self.current_proc.start() def _check_result_queue(self) -> Literal[True]: latest_result = None while not self.queue.empty(): latest_result = self.queue.get() + # FIXME: cache is being checked at two intervals cache = self.controller.get_dist_cache() if latest_result: @@ -299,9 +246,6 @@ class ServerTreeView(ContextMixin, TreeView): ) -> bool | None: if event.state is Gdk.ModifierType.CONTROL_MASK: match event.keyval: - case Gdk.KEY_r: - # TODO: unimplemented, threading - self.refresh_player_count() case Gdk.KEY_f: self.emitter.emit("request_keyword_focus") case Gdk.KEY_m: @@ -312,8 +256,10 @@ class ServerTreeView(ContextMixin, TreeView): if self.enum is ServerTab.LAN: self.emitter.emit("request_lan_entry_focus") case Gdk.KEY_c: - record = self.get_record() - self.controller.copy_ip(record) + self.controller.menu_action(ContextMenu.COPY_SERVER_IP, self) + case Gdk.KEY_r: + # TODO: unimplemented, needs threading + self.controller.menu_action(ContextMenu.REFRESH_PLAYERS, self) else: match event.keyval: case Gdk.KEY_l | Gdk.KEY_Right: @@ -321,30 +267,23 @@ class ServerTreeView(ContextMixin, TreeView): case _: self.emitter.emit("check_button_pressed", event.keyval) - # mod_context_items = [ContextMenu.OPEN_WORKSHOP, ContextMenu.DELETE_MOD] - # TODO: dynamic menu entries - # for row in items: - # if row == ContextMenu.ADD_SERVER: - # if self.is_in_favs(): - # row = ContextMenu.REMOVE_SERVER - # item = Gtk.MenuItem(label=row.dict["label"]) - # item.type = row - # item.action = row.dict["action"] - # self.menu.append(item) - # if row == ContextMenu.SHOW_MODS: - # if not self.has_mods(): - # item.set_sensitive(False) - # if row == ContextMenu.ADD_NOTE: - # if self.get_record_string() in notes_cache: - # item.set_label(strings.edit_note) + # TODO: unimplemented + # def is_modded(self) -> bool: + # select = self.get_selection() + # sels = select.get_selected_rows() + # (model, pathlist) = sels + # path = pathlist[0] + # tree_iter = model.get_iter(path) + # mods = model.get_value(tree_iter, 11) + # return mods - # if event.type is Gdk.EventType.KEY_PRESS and event.keyval is Gdk.KEY_l: - # if self.is_selection_empty(): - # return - # self.menu.popup_at_widget(widget, Gdk.Gravity.CENTER, Gdk.Gravity.WEST) - # else: - # self.menu.popup_at_pointer(event) - # self.menu.select_first(False) + # TODO: unimplemented + # def is_in_favs(self) -> bool: + # record = self.get_record_string() + # proc = call_out("is_in_favs", record) + # if proc.returncode == 0: + # return True + # return False def _parent_row_activated( self, tree: TreeView, path: Gtk.TreePath, column: Gtk.TreeViewColumn @@ -368,7 +307,7 @@ class ServerTreeView(ContextMixin, TreeView): return f"{addr}:{qport}" def get_record(self) -> Record | None: - # TODO: use tree_base.get_selected_row() + # TODO: clean up, use tree_base.get_selected_row() select = self.get_selection() sels = select.get_selected_rows() (model, pathlist) = sels @@ -392,6 +331,40 @@ class ServerTreeView(ContextMixin, TreeView): def set_loaded(self, status: bool) -> None: self.loaded = status + # @deprecated("currently unused") + # def _get_ping( + # self, + # column: Gtk.TreeViewColumn, + # cell: Gtk.CellRendererText, + # model: Gtk.TreeModel, + # it: Gtk.TreeIter, + # data: Any, + # ): + # def ping_server(model, _iter, ip: str, qport: int, ping: int): + # res = Ping(0, ip, qport, ping) + # ping = res.ping + # GLib.idle_add(lambda: model.set(_iter, ping_column, ping)) + + # addr_column = 7 + # qport_column = 8 + # ping_column = 9 + + # addr = model.get_value(it, addr_column).split(":")[0] + # qport = model.get_value(it, qport_column) + # ping = model.get_value(it, ping_column) + # ip = f"{addr}:{qport}" + + # if ip in self.seen_cache: + # return + # self.seen_cache.append(ip) + + # thread = threading.Thread( + # daemon=True, + # target=ping_server, + # args=(model, it, ip, qport, ping), + # ) + # thread.start() + @deprecated("Currently unused") def _lazy_load( self,