1
0
Fork 0
mirror of https://github.com/aclist/dztui.git synced 2024-12-28 21:32:36 +01:00

Merge pull request #173 from aclist/release/5.6.0-beta.10

Release/5.6.0-beta.10
This commit is contained in:
aclist 2024-12-05 08:25:19 +09:00 committed by GitHub
commit 3adac2a211
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 107 additions and 39 deletions

View file

@ -1,5 +1,13 @@
# Changelog # Changelog
## [5.6.0-beta.10] 2024-12-04
### Fixed
- Untoggle highlight button when repopulating mod list
- Resolve remote IP when saving records for game servers with multiple hosts
- Update statusbar when removing servers from list/repopulating
### Added:
- "Select stale" button to bulk select mods marked as obsolete
## [5.6.0-beta.9] 2024-12-03 ## [5.6.0-beta.9] 2024-12-03
### Fixed ### Fixed
- Normalize user locale when parsing floats - Normalize user locale when parsing floats

View file

@ -1,7 +1,7 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -o pipefail set -o pipefail
version=5.6.0-beta.9 version=5.6.0-beta.10
#CONSTANTS #CONSTANTS
aid=221100 aid=221100
@ -569,10 +569,10 @@ fetch_helpers_by_sum(){
[[ -f "$config_file" ]] && source "$config_file" [[ -f "$config_file" ]] && source "$config_file"
declare -A sums declare -A sums
sums=( sums=(
["ui.py"]="1c6e5b996eccd891a3e56930e28246da" ["ui.py"]="3b90cd522e52131e7ae396671e1c1ad2"
["query_v2.py"]="55d339ba02512ac69de288eb3be41067" ["query_v2.py"]="55d339ba02512ac69de288eb3be41067"
["vdf2json.py"]="2f49f6f5d3af919bebaab2e9c220f397" ["vdf2json.py"]="2f49f6f5d3af919bebaab2e9c220f397"
["funcs"]="d98d8626a1d61b2d5947b53155a14928" ["funcs"]="62f6b3fb2dcb56a78b7642c0f0aa7abe"
["lan"]="c62e84ddd1457b71a85ad21da662b9af" ["lan"]="c62e84ddd1457b71a85ad21da662b9af"
) )
local author="aclist" local author="aclist"

View file

@ -343,7 +343,7 @@ list_mods(){
local base_dir local base_dir
local size local size
if [[ -z $(installed_mods) ]] || [[ -z $(find $workshop_dir -maxdepth 2 -name "*.cpp" | grep .cpp) ]]; then if [[ -z $(installed_mods) ]] || [[ -z $(find $workshop_dir -maxdepth 2 -name "*.cpp" | grep .cpp) ]]; then
echo "No mods currently installed or incorrect path set." printf "No mods currently installed or incorrect path set."
logger WARN "Found no locally installed mods" logger WARN "Found no locally installed mods"
return 1 return 1
else else
@ -930,7 +930,7 @@ remove_from_favs(){
break break
fi fi
done done
if [[ ${#ip_list} -gt 0 ]]; then if [[ ${#ip_list[@]} -gt 0 ]]; then
readarray -t ip_list < <(printf "%s\n" "${ip_list[@]}") readarray -t ip_list < <(printf "%s\n" "${ip_list[@]}")
fi fi
update_config update_config
@ -938,6 +938,7 @@ remove_from_favs(){
local cache="$(< "$_cache_my_servers")" local cache="$(< "$_cache_my_servers")"
<<< "$cache" grep -v -P "$r$" > $_cache_my_servers <<< "$cache" grep -v -P "$r$" > $_cache_my_servers
logger INFO "Removed the record $record from saved servers" logger INFO "Removed the record $record from saved servers"
echo "Removed $record from saved servers"
return 90 return 90
} }
update_favs_from_table(){ update_favs_from_table(){
@ -1019,7 +1020,7 @@ open_workshop_page(){
shift shift
local id="$1" local id="$1"
local workshop_uri="steam://url/CommunityFilePage/$id" local workshop_uri="steam://url/CommunityFilePage/$id"
$steam_cmd "$workshop_uri" $id $steam_cmd "$workshop_uri" $id &
} }
open_link(){ open_link(){
shift shift

View file

@ -123,6 +123,11 @@ class RowType(EnumWithAttrs):
"label": None, "label": None,
"tooltip": None, "tooltip": None,
} }
RESOLVE_IP = {
"label": "Resolve IP",
"tooltip": None,
"wait_msg": "Resolving remote IP"
}
HIGHLIGHT = { HIGHLIGHT = {
"label": "Highlight stale", "label": "Highlight stale",
"tooltip": None, "tooltip": None,
@ -156,7 +161,7 @@ class RowType(EnumWithAttrs):
} }
RECENT_SERVERS = { RECENT_SERVERS = {
"label": "Recent servers", "label": "Recent servers",
"tooltip": "Shows the last to servers you connected to (includes attempts)", "tooltip": "Shows the last 10 servers you connected to (includes attempts)",
} }
CONN_BY_IP = { CONN_BY_IP = {
"label": "Connect by IP", "label": "Connect by IP",
@ -539,19 +544,14 @@ def parse_mod_rows(data):
def parse_server_rows(data): def parse_server_rows(data):
sum = 0
lines = data.stdout.splitlines() lines = data.stdout.splitlines()
reader = csv.reader(lines, delimiter=delimiter) reader = csv.reader(lines, delimiter=delimiter)
hits = len(lines)
try: try:
rows = [[row[0], row[1], row[2], row[3], int(row[4]), int(row[5]), int(row[6]), row[7], int(row[8])] for row in reader if row] rows = [[row[0], row[1], row[2], row[3], int(row[4]), int(row[5]), int(row[6]), row[7], int(row[8])] for row in reader if row]
except IndexError: except IndexError:
return 1 return 1
for row in rows: for row in rows:
server_store.append(row) server_store.append(row)
players = int(row[4])
sum += players
return [sum, hits]
def query_config(widget, key=""): def query_config(widget, key=""):
@ -642,16 +642,35 @@ def process_shell_return_code(transient_parent, msg, code, original_input):
spawn_dialog(transient_parent, msg, Popup.NOTIFY) spawn_dialog(transient_parent, msg, Popup.NOTIFY)
return return
case 95: case 95:
# reload mods list # successful mod deletion
spawn_dialog(transient_parent, msg, Popup.NOTIFY) spawn_dialog(transient_parent, msg, Popup.NOTIFY)
treeview = transient_parent.grid.scrollable_treelist.treeview treeview = transient_parent.grid.scrollable_treelist.treeview
grid = treeview.get_parent().get_parent()
(model, pathlist) = treeview.get_selection().get_selected_rows()
for p in reversed(pathlist):
it = model.get_iter(p)
model.remove(it)
total_size = 0
total_mods = len(model)
for row in model:
total_size += row[3]
size = locale.format_string('%.3f', total_size, grouping=True)
pretty = pluralize("mods", total_mods)
grid.update_statusbar(f"Found {total_mods:n} {pretty} taking up {size} MiB")
# untoggle selection for visibility of other stale rows
treeview.toggle_selection(False)
case 96:
# unsuccessful mod deletion
spawn_dialog(transient_parent, msg, Popup.NOTIFY)
# re-block this signal before redrawing table contents # re-block this signal before redrawing table contents
treeview = transient_parent.grid.scrollable_treelist.treeview
toggle_signal(treeview, treeview, '_on_keypress', False) toggle_signal(treeview, treeview, '_on_keypress', False)
treeview.update_quad_column(RowType.LIST_MODS) treeview.update_quad_column(RowType.LIST_MODS)
case 99: case 99:
# highlight stale mods # highlight stale mods
panel = transient_parent.grid.sel_panel panel = transient_parent.grid.sel_panel
panel.colorize_cells(True) panel.colorize_cells(True)
panel.toggle_select_stale_button(True)
case 100: case 100:
# final handoff before launch # final handoff before launch
final_conf = spawn_dialog(transient_parent, msg, Popup.CONFIRM) final_conf = spawn_dialog(transient_parent, msg, Popup.CONFIRM)
@ -701,6 +720,11 @@ def process_tree_option(input, treeview):
msg = out[-1] msg = out[-1]
process_shell_return_code(transient_parent, msg, rc, input) process_shell_return_code(transient_parent, msg, rc, input)
if command == RowType.RESOLVE_IP:
record = "%s:%s" %(treeview.get_column_at_index(7), treeview.get_column_at_index(8))
wait_msg = command.dict["wait_msg"]
call_on_thread(True, cmd_string, wait_msg, record)
return
# help pages # help pages
if context == WindowContext.TABLE_MODS and command == RowType.HIGHLIGHT: if context == WindowContext.TABLE_MODS and command == RowType.HIGHLIGHT:
wait_msg = command.dict["wait_msg"] wait_msg = command.dict["wait_msg"]
@ -1090,6 +1114,7 @@ class TreeView(Gtk.TreeView):
def _on_menu_click(self, menu_item): def _on_menu_click(self, menu_item):
#TODO: context menus use old stringwise parsing #TODO: context menus use old stringwise parsing
# use enumerated contexts
parent = self.get_outer_window() parent = self.get_outer_window()
context = self.get_first_col() context = self.get_first_col()
value = self.get_column_at_index(0) value = self.get_column_at_index(0)
@ -1099,12 +1124,10 @@ class TreeView(Gtk.TreeView):
match context_menu_label: match context_menu_label:
case "Add to my servers" | "Remove from my servers": case "Add to my servers" | "Remove from my servers":
record = "%s:%s" %(self.get_column_at_index(7), self.get_column_at_index(8)) record = "%s:%s" %(self.get_column_at_index(7), self.get_column_at_index(8))
proc = call_out(parent, context_menu_label, record) process_tree_option([self.view, RowType.RESOLVE_IP], self)
if context == "Name (My saved servers)": if context == "Name (My saved servers)":
iter = self.get_current_iter() iter = self.get_current_iter()
server_store.remove(iter) server_store.remove(iter)
msg = proc.stdout
res = spawn_dialog(parent, msg, Popup.NOTIFY)
case "Remove from history": case "Remove from history":
record = "%s:%s" %(self.get_column_at_index(7), self.get_column_at_index(8)) record = "%s:%s" %(self.get_column_at_index(7), self.get_column_at_index(8))
call_out(parent, context_menu_label, record) call_out(parent, context_menu_label, record)
@ -1128,18 +1151,20 @@ class TreeView(Gtk.TreeView):
success_msg = "Successfully deleted the mod '%s'." %(value) success_msg = "Successfully deleted the mod '%s'." %(value)
fail_msg = "An error occurred during deletion. Aborting." fail_msg = "An error occurred during deletion. Aborting."
res = spawn_dialog(parent, conf_msg, Popup.CONFIRM) res = spawn_dialog(parent, conf_msg, Popup.CONFIRM)
if res == 0: if res != 0:
mods = [] return
symlink = self.get_column_at_index(1) mods = []
dir = self.get_column_at_index(2) symlink = self.get_column_at_index(1)
concat = symlink + " " + dir + "\n" dir = self.get_column_at_index(2)
mods.append(concat) concat = symlink + " " + dir + "\n"
with open(mods_temp_file, "w") as outfile: mods.append(concat)
outfile.writelines(mods) with open(mods_temp_file, "w") as outfile:
process_tree_option([self.view, RowType.DELETE_SELECTED], self) outfile.writelines(mods)
process_tree_option([self.view, RowType.DELETE_SELECTED], self)
case "Open in Steam Workshop": case "Open in Steam Workshop":
record = self.get_column_at_index(2) record = self.get_column_at_index(2)
call_out(parent, "open_workshop_page", record) base_cmd = "open_workshop_page"
subprocess.Popen(['/usr/bin/env', 'bash', funcs, base_cmd, record])
def toggle_selection(self, bool): def toggle_selection(self, bool):
l = len(mod_store) l = len(mod_store)
@ -1147,12 +1172,10 @@ class TreeView(Gtk.TreeView):
case True: case True:
for i in range (0, l): for i in range (0, l):
path = Gtk.TreePath(i) path = Gtk.TreePath(i)
it = mod_store.get_iter(path)
self.get_selection().select_path(path) self.get_selection().select_path(path)
case False: case False:
for i in range (0, l): for i in range (0, l):
path = Gtk.TreePath(i) path = Gtk.TreePath(i)
it = mod_store.get_iter(path)
self.get_selection().unselect_path(path) self.get_selection().unselect_path(path)
def _on_button_release(self, widget, event): def _on_button_release(self, widget, event):
@ -1259,8 +1282,11 @@ class TreeView(Gtk.TreeView):
if self.view == WindowContext.TABLE_API or self.view == WindowContext.TABLE_SERVER: if self.view == WindowContext.TABLE_API or self.view == WindowContext.TABLE_SERVER:
addr = self.get_column_at_index(7) addr = self.get_column_at_index(7)
if addr is None: if addr is None:
server_tooltip[0] = format_tooltip()
grid.update_statusbar(server_tooltip[0])
return return
if addr in cache: if addr in cache:
server_tooltip[0] = format_tooltip()
dist = format_distance(cache[addr][0]) dist = format_distance(cache[addr][0])
ping = format_ping(cache[addr][1]) ping = format_ping(cache[addr][1])
@ -1388,7 +1414,7 @@ class TreeView(Gtk.TreeView):
self.grab_focus() self.grab_focus()
for column in self.get_columns(): for column in self.get_columns():
column.connect("notify::width", self._on_col_width_changed) column.connect("notify::width", self._on_col_width_changed)
if hits == 0: if len(server_store) == 0:
call_out(self, "start_cooldown", "", "") call_out(self, "start_cooldown", "", "")
api_warn_msg = """\ api_warn_msg = """\
No servers returned. Possible network issue or API key on cooldown? No servers returned. Possible network issue or API key on cooldown?
@ -1403,10 +1429,8 @@ class TreeView(Gtk.TreeView):
data = call_out(self, "dump_servers", mode, *filters) data = call_out(self, "dump_servers", mode, *filters)
toggle_signal(self, self.selected_row, '_on_tree_selection_changed', False) toggle_signal(self, self.selected_row, '_on_tree_selection_changed', False)
row_metadata = parse_server_rows(data) parse_server_rows(data)
sum = row_metadata[0] server_tooltip[0] = format_tooltip()
hits = row_metadata[1]
server_tooltip[0] = format_tooltip(sum, hits)
grid.update_statusbar(server_tooltip[0]) grid.update_statusbar(server_tooltip[0])
map_data = call_out(self, "get_unique_maps", mode) map_data = call_out(self, "get_unique_maps", mode)
@ -1426,6 +1450,7 @@ class TreeView(Gtk.TreeView):
right_panel.set_filter_visibility(False) right_panel.set_filter_visibility(False)
else: else:
grid.sel_panel.set_visible(True) grid.sel_panel.set_visible(True)
grid.sel_panel.initialize()
self.set_model(mod_store) self.set_model(mod_store)
self.grab_focus() self.grab_focus()
@ -1721,6 +1746,8 @@ class TreeView(Gtk.TreeView):
cooldown = call_out(self, "test_cooldown", "", "") cooldown = call_out(self, "test_cooldown", "", "")
if cooldown.returncode == 1: if cooldown.returncode == 1:
spawn_dialog(outer, cooldown.stdout, Popup.NOTIFY) spawn_dialog(outer, cooldown.stdout, Popup.NOTIFY)
# reset context to main menu if navigation was blocked
self.view = WindowContext.MAIN_MENU
return 1 return 1
for check in checks: for check in checks:
toggle_signal(filters_vbox, check, '_on_check_toggle', False) toggle_signal(filters_vbox, check, '_on_check_toggle', False)
@ -1786,7 +1813,11 @@ def format_metadata(row_sel):
return prefix return prefix
def format_tooltip(players, hits): def format_tooltip():
hits = len(server_store)
players = 0
for row in server_store:
players+= row[4]
hits_pretty = pluralize("matches", hits) hits_pretty = pluralize("matches", hits)
players_pretty = pluralize("players", players) players_pretty = pluralize("players", players)
tooltip = f"Found {hits:n} {hits_pretty} with {players:n} {players_pretty}" tooltip = f"Found {hits:n} {hits_pretty} with {players:n} {players_pretty}"
@ -1796,10 +1827,8 @@ def format_tooltip(players, hits):
def filter_servers(transient_parent, filters_vbox, treeview, context): def filter_servers(transient_parent, filters_vbox, treeview, context):
def filter(dialog): def filter(dialog):
def clear_and_destroy(): def clear_and_destroy():
row_metadata = parse_server_rows(data) parse_server_rows(data)
sum = row_metadata[0] server_tooltip[0] = format_tooltip()
hits = row_metadata[1]
server_tooltip[0] = format_tooltip(sum, hits)
transient_parent.grid.update_statusbar(server_tooltip[0]) transient_parent.grid.update_statusbar(server_tooltip[0])
toggle_signal(treeview, treeview.selected_row, '_on_tree_selection_changed', True) toggle_signal(treeview, treeview.selected_row, '_on_tree_selection_changed', True)
@ -2275,6 +2304,7 @@ class Grid(Gtk.Grid):
self.scrollable_treelist.treeview.terminate_process() self.scrollable_treelist.treeview.terminate_process()
def _on_calclat_started(self, treeview): def _on_calclat_started(self, treeview):
server_tooltip[0] = format_tooltip()
server_tooltip[1] = server_tooltip[0] + "| Distance: calculating..." server_tooltip[1] = server_tooltip[0] + "| Distance: calculating..."
self.update_statusbar(server_tooltip[1]) self.update_statusbar(server_tooltip[1])
@ -2363,6 +2393,19 @@ class ModSelectionPanel(Gtk.Box):
button.connect("clicked", self._on_button_clicked) button.connect("clicked", self._on_button_clicked)
self.pack_start(button, False, True, 0) self.pack_start(button, False, True, 0)
def initialize(self):
l = len(self.get_children())
last = self.get_children()[l-1]
last_label = last.get_label()
for i in self.get_children():
match i.get_label():
case "Select stale":
i.destroy()
case "Unhighlight stale":
i.set_label("Highlight stale")
def _on_button_clicked(self, button): def _on_button_clicked(self, button):
self.active_button = button self.active_button = button
label = button.get_label() label = button.get_label()
@ -2384,6 +2427,22 @@ class ModSelectionPanel(Gtk.Box):
process_tree_option([treeview.view, RowType.HIGHLIGHT], treeview) process_tree_option([treeview.view, RowType.HIGHLIGHT], treeview)
case "Unhighlight stale": case "Unhighlight stale":
self.colorize_cells(False) self.colorize_cells(False)
self._remove_last_button()
case "Select stale":
for i in range (0, len(mod_store)):
if mod_store[i][4] == "#FF0000":
path = Gtk.TreePath(i)
treeview.get_selection().select_path(path)
def toggle_select_stale_button(self, bool):
if bool is True:
button = Gtk.Button(label="Select stale")
button.set_margin_start(10)
button.set_margin_end(10)
button.connect("clicked", self._on_button_clicked)
self.pack_start(button, False, True, 0)
self.show_all()
def colorize_cells(self, bool): def colorize_cells(self, bool):
def _colorize(path, color): def _colorize(path, color):
@ -2417,7 +2476,6 @@ class ModSelectionPanel(Gtk.Box):
def _iterate_mod_deletion(self, model, pathlist, ct): def _iterate_mod_deletion(self, model, pathlist, ct):
# hedge against large number of arguments
widgets = relative_widget(self) widgets = relative_widget(self)
parent = widgets["outer"] parent = widgets["outer"]
treeview = widgets["treeview"] treeview = widgets["treeview"]
@ -2438,6 +2496,7 @@ class ModSelectionPanel(Gtk.Box):
path = model.get_value(it, 2) path = model.get_value(it, 2)
concat = symlink + " " + path + "\n" concat = symlink + " " + path + "\n"
mods.append(concat) mods.append(concat)
# hedge against large number of arguments passed to shell
with open(mods_temp_file, "w") as outfile: with open(mods_temp_file, "w") as outfile:
outfile.writelines(mods) outfile.writelines(mods)
process_tree_option([treeview.view, RowType.DELETE_SELECTED], treeview) process_tree_option([treeview.view, RowType.DELETE_SELECTED], treeview)