Skip to content

Port to GTK4#260

Open
jmallach wants to merge 104 commits into
XQF:masterfrom
jmallach:gtk4
Open

Port to GTK4#260
jmallach wants to merge 104 commits into
XQF:masterfrom
jmallach:gtk4

Conversation

@jmallach

Copy link
Copy Markdown
Contributor

This is a big PR, mostly an experiment with Claude, to take the GTK3 port where it was left and move it to GTK4.

The file docs/gtk4-migration-plan.md has details on what happened during the process. I tried to make each change self contained on its own commit so reviewing would be somewhat easier, but I know it's a huge PR to review, so take your time, or feel free to reject if this is just not wanted. I thought that pouring more time on GTK3 was pointless, as that version is also deprecated at this point.

jmallach and others added 30 commits May 11, 2026 10:40
Switch CMakeLists.txt from the dual GTK_TARGET=2/3 build to a single
gtk4 target.  Add src/gtk4-compat.h, a thin compatibility header that
stubs out the removed GTK2/3 widgets (GtkCList, GtkCTree, GtkHBox,
GtkVBox, GtkTable, GtkAlignment, GtkItemFactory, etc.) so the rest of
the codebase can be ported incrementally without the whole tree breaking
at once.

Update the UI file (src/xqf.ui, replaces xqf-gtk2.ui/xqf-gtk3.ui),
dialogs (event-key → GtkEventControllerKey, gtk_dialog_run → nested
GMainLoop), pixmap loading (drop direct GdkWindow access), and
miscellaneous deprecated-API call sites throughout src/.

Add docs/gtk4-migration-plan.md describing the full migration roadmap
and tools/docker/ with a build container for reproducible GTK 4 builds.

Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Add GObject wrapper types XqfServerItem and XqfPlayerItem
(src/xqf-{server,player}-item.{c,h}) to wrap struct server * and
struct player * as GListStore items.  XqfPlayerItem also holds a
server_ref on the owning server to keep the player list alive.

Add src/xqf-lists.{c,h} which creates the GtkColumnView + GListStore
+ GtkSortListModel + GtkMultiSelection/GtkSingleSelection stack for
both the server and player panels.  Per-column GtkCustomSorters
delegate to the existing compare_servers()/compare_players() logic.
Initial sort is set via gtk_column_view_sort_by_column() (GTK 4.10+):
Ping/ascending for servers, Frags/descending for players.

Rewrite src/srv-list.c to drive GListStore instead of GtkCList rows.
Key mechanisms:
- server_store_rebind(): splice a fresh XqfServerItem in place to
  force GtkColumnView to rebind the factory for updated rows.
- server_list_sync_selection(): read GtkBitset from GtkMultiSelection
  to update cur_server.
- server_list_build_filtered(): save/restore selection around a full
  store rebuild using server_store_find() on the sort model.

Update src/xqf.c to call create_server_column_view() /
create_player_column_view() instead of create_cwidget(), and switch
all selection/row queries to the new GListStore-based helpers.

Rename legacy GtkCList-era identifiers throughout:
- Global widgets: server_clist → server_view, player_clist → player_view
- All server_clist_* functions → server_list_*
- All player_clist_* functions → player_list_*
- struct clist_def → struct list_def, struct clist_column → struct list_column
- enum cwidget_type/CWIDGET_CLIST/CWIDGET_CTREE → enum cview_type/CVIEW_LIST/CVIEW_TREE
- Callback/compare functions renamed to drop the clist infix

Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
The server-info panel was the last remaining GtkCTree consumer.
Replace it with a plain GtkTreeView backed by a GtkTreeStore with
three columns: RULE (string), VALUE (string), INFO_PTR (pointer to
the info[] pair used for sorting top-level rows).

- srvinf_treeview_new(): creates store + treeview, sets custom sort
  funcs so child flag-rows stay in insertion order while top-level
  rows are sorted by compare_srvinfo().
- srvinf_treeview_set_server(): populates the store; show_extended_flags
  and show_split_value now take GtkTreeIter* instead of GtkCTreeNode*.
- srvinf_copy_selected_values(): replaces the gtk_ctree_node_get_text
  loop; reads VALUE column from GtkTreeSelection.
- Drop create_ctree_widget(), list_sort_column(), column_header_set_title(),
  srvinf_list_def, srvinf_columns[], srvinf_list_compare_func(), and
  server_info_event_cb() — all now dead code.

Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
The sources panel (left pane, game groups → masters) was the last
remaining GtkCTree consumer. Replace it with a GtkTreeView backed
by a three-column GtkTreeStore: MASTER (G_TYPE_POINTER), PIXBUF
(GDK_TYPE_PIXBUF), NAME (G_TYPE_STRING).

Key changes:
- source_store (GtkTreeStore) + source_find_master() helper replace
  gtk_ctree_find_by_row_data() throughout.
- source_treeview_enable_master_group() uses gtk_tree_store_insert_before()
  to preserve the correct game-type order.
- source_treeview_add_master() expands the parent after inserting the
  child so new masters are immediately visible.
- fill_source_treeview() restores expand/collapse state from config
  after building the tree.
- ui_done() saves collapse state via gtk_tree_view_row_expanded().
- source_selection_changed() reads GtkTreeSelection instead of
  iterating the old GtkCList selection list.
- Selection signal: GtkTreeSelection::changed replaces the GtkCTree
  tree_select_row / tree_unselect_row pair.
- source_treeview_has_master() replaces the gtk_ctree_find_by_row_data
  existence check in source.c::refresh_source_list().
- All functions renamed source_ctree_* → source_treeview_* to match
  the new widget type.
- source_ctree_event_callback stub removed (was no-op).

Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
- callbacks.c: use GListModel API (server_store) for server count and
  cur_server for selection test instead of always-NULL gtk_clist compat stubs
- xqf-ui.c: save_view_geometry uses gtk_column_view_column_get_fixed_width
  instead of gtk_clist_get_column_width (which returned 0)
- xqf.c: remove no-op gtk_clist_freeze/sort/thaw around server refresh;
  GtkSortListModel sorts automatically
- psearch.c: rewrite psearch_next_player/find_player using GListModel API
  (player_selection, server_selection, XqfPlayerItem, XqfServerItem)
- skin.c: replace gtk_clist_get_row_height stub (always 18) with literal 16

Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
- pref.c: arguments_list (custom game args) migrated to GtkListStore
  with 2 text columns; cursor-changed replaces select_row
- flt-player.c: pattern_list migrated to GtkListStore with 3 pixbuf
  columns (group icons) + mode text + pattern text; GtkGestureClick
  for group toggle; row swap via data exchange instead of clist_swap_rows
- filter.c: country_filter_list, country_left_list, country_right_list
  all migrated to GtkListStore (id/pixbuf/name columns); GtkTreeSelection
  changed signal replaces select_row/unselect_row; gtk_clist_get_row_data
  country ID storage replaced by proper COUNTRY_COL_ID column reads
- xqf.c: remove dead server_list_compare_func and player_list_compare_func

All GtkCList/GtkCTree usage outside gtk4-compat.h is now eliminated.

Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Bulk replace all gtk_vbox_new(homogeneous, spacing) and
gtk_hbox_new(homogeneous, spacing) calls with the GTK4 form
gtk_box_new(GTK_ORIENTATION_VERTICAL/HORIZONTAL, spacing) across
all source files (152 occurrences). The homogeneous flag is dropped
as none of the callers used TRUE.

Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace all gtk_table_new/gtk_table_set_*_spacing/gtk_table_attach*
with the GTK4 equivalents: gtk_grid_new, gtk_grid_set_row/column_spacing,
and gtk_grid_attach. Per-column/row spacing overrides are dropped (no
GtkGrid equivalent); the visual difference is negligible. Local variable
names are updated from "table" to "grid" throughout. Also replace
gtk_misc_set_alignment on labels with gtk_label_set_xalign.

Files changed: addmaster.c, flt-player.c, filter.c, pref.c,
srv-prop.c, statistics.c.

Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
The type_hint property was removed from GtkWindow in GTK4 and causes
a fatal error when GtkBuilder loads the UI file. Remove it from
GtkAboutDialog and the preferences dialog.

Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Both objects used internal-child="vbox" and internal-child="action_area"
which were removed in GTK4. Neither object is referenced by the code via
gtk_builder_get_object() — about_dialog() uses gtk_show_about_dialog()
directly and preferences are built entirely in pref.c. Remove both dead
objects to fix the fatal GtkBuilder load error.

Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Tasks complete:
- 1: GtkCList → GtkColumnView (xqf-lists.c, 415 lines, full impl)
- 5: gdk_window decorations removed (no remaining calls)
- 6: #ifdef GUI_GTK2/3 blocks removed (none in source)
- 8: CMakeLists.txt GTK4-only (single gtk4 target)

Only task 10 (gtk_dialog_run → async) remains in Phase 1.
set_widgets_sensitivity() now calls set_action_enabled() which toggles
GSimpleAction enabled state instead of manipulating removed GtkMenuItem
widgets. pref.c updates show-hostnames/show-default-port via the action
group state rather than gtk_check_menu_item_set_active().

Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
srv-info.c replaces GtkCTree stubs with a real GtkTreeView+GtkTreeStore
implementation for the server rules/cvars panel. xqf-ui.c/h updated for
GTK4 widget API (gtk_widget_show → gtk_widget_set_visible, etc.).

Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
xqf.ui rewritten for GTK4: GtkBox layout replaces deprecated containers,
GtkPopoverMenuBar + GMenuModel replaces removed GtkMenuBar/GtkMenu,
paned/scroll structure updated to GTK4 child-setter API. Old
xqf-gtk2.ui and xqf-gtk3.ui deleted — xqf.ui is now the sole UI file.

Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
GtkWindow does not implement GActionMap in GTK4; replace with a
GSimpleActionGroup inserted via gtk_widget_insert_action_group().
Add win_action_group() for cross-TU access. Replace gtk_main() app
loop with an explicit GMainLoop. Fix main window close-request:
use dedicated main_window_close_cb returning FALSE so GTK4 proceeds
with normal window destruction and the process exits cleanly.

Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
…MainLoop

gtk_main()/gtk_main_quit() were removed in GTK4. All 10 dialog call
sites now use dialog_run_modal(window) — a nested GMainLoop that
auto-quits when the window is destroyed. utils.c external-program loop
gets its own GMainLoop via a new loop field on the connection struct,
removing the do_quit/gtk_main_quit pattern. gtk_main/gtk_main_quit
removed from gtk4-compat.h.

Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
All 11 Phase 1 tasks marked done. Task 10 note clarifies that no
gtk_dialog_run() calls existed; the real work was replacing gtk_main()
compat shims with dialog_run_modal() and an explicit app GMainLoop.

Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Wire up the gestures and controllers that GtkCList provided for free:
- Double-click server row → connect (GtkGestureClick, button 1)
- Enter/KP_Enter → connect; Space → refresh selected (GtkEventControllerKey)
- Right-click server list → GtkPopoverMenu with Connect, Observe, Record
  Demo, Refresh Selected, Properties, Add to Favorites, DNS Lookup, RCON
- Right-click player list → GtkPopoverMenu with Add to Red/Green/Blue filter

All menu items resolve to existing "win.*" actions in the GSimpleActionGroup.
Removed the dead player_view_event_cb stub that predated GtkColumnView.

Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove g_object_set_property("tab-hborder") from pref.c, statistics.c,
  filter.c: property was removed in GTK4; each dialog with a GtkNotebook
  was emitting a GTK-CRITICAL on open
- Fix combo_get_entry() in srv-prop.c: use gtk_combo_box_get_child()
  instead of gtk_bin_get_child()/gtk_widget_get_first_child(), which
  returned the internal GtkBox rather than the entry in GTK4 — this was
  silently dropping typed text in Add Server / Add Master dialogs
- Move expand-row logic out of fill_source_treeview() into
  source_treeview_restore_expand_state(), called after source_treeview
  global is set; previously all game groups were left collapsed at startup

Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Document the Docker-based build workflow (xqf-dev image, cmake --build +
cmake --install, WITH_QSTAT=quakestat already in CMakeCache). Also add a
note on the one-commit-per-fix discipline for easier review.

Includes a "Current Known Bugs" section capturing the two known startup
issues as of 2026-05-11 (18× GDK_TYPE_PIXBUF CRITICALs, Favorites-only
source pane) — to be updated once fixes are verified and committed.

Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
GtkCellRendererPixbuf's "pixbuf" property causes type-compatibility
CRITICALs on GTK 4.22 because the model column type (GDK_TYPE_PIXBUF)
no longer matches what the property binding expects.

Switch all GtkTreeStore/GtkListStore pixbuf columns to GDK_TYPE_TEXTURE,
bind them with the "texture" attribute, and add GdkTexture* to struct
pixmap (created from the GdkPixbuf via gdk_texture_new_for_pixbuf).

Affects: pixmaps.h, loadpixmap.c, pixmaps.c, xqf-ui.c/h, pref.c,
filter.c, flt-player.c.

Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
gtk_column_view_column_new() takes (transfer full) for its factory
argument — it calls g_clear_object(&factory) internally. Our subsequent
g_object_unref(factory) freed it a second time, leaving every column
with a dangling factory pointer. This caused 18x startup CRITICALs in
gtk_column_view_cell_widget_new when items were first added to the
server list (g_value_type_compatible src_type=0, invalid object type).

Drop the redundant g_object_unref(factory) in both server and player
column setup loops.

Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove the 18× CRITICAL bug entry; both root causes are now fixed:
- GtkCellRendererPixbuf "texture"/GDK_TYPE_TEXTURE (commit d529ab8)
- GtkListItemFactory double-unref in column view setup (commit 9c1c2fa)

Update "Current Known Bugs" date to 2026-05-17.

Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
All APIs XQF uses are available since GTK 4.0 (no Since: annotations on
any of them). Set the pkg_check_modules floor to 4.0 so distributors
targeting older GTK4 stacks can try it without an artificial barrier.

Add a "Deprecated API in active use" section to the migration plan
listing GtkCellRenderer*/GtkTreeView (deprecated 4.10) and
gdk_texture_new_for_pixbuf (deprecated 4.20), along with which source
files are affected and how each resolves as migration progresses.

Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
GtkCheckButton no longer inherits from GtkToggleButton in GTK4, and
GtkRadioButton is entirely removed.  Replace all affected call sites:

- gtk_toggle_button_{get,set}_active → gtk_check_button_{get,set}_active
- GTK_TOGGLE_BUTTON(x) → GTK_CHECK_BUTTON(x)  (for GtkCheckButton widgets)
- gtk_radio_button_new_with_label / gtk_radio_button_get_group →
  gtk_check_button_new_with_label + gtk_check_button_set_group
- CMakeLists.txt: fix pkg_check_modules version syntax (gtk4>=4.0)

Files changed: addmaster.c, dialogs.c, filter.c, flt-player.c, pref.c,
psearch.c, scripts.c, srv-prop.c, CMakeLists.txt.

xqf.c filter_buttons[] are genuine GtkToggleButton and are left unchanged.

Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
GtkCheckButton in GTK4 no longer inherits from GtkButton and does not
emit 'clicked'.  Use 'toggled' for the radio-style mode buttons in the
player filter pattern editor.

Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
The "None" filter skip (0→1) was unconditional, so opening the filter
dialog with an empty filter list triggered debug(0) "invalid filter nr 1"
on every open.  Guard the bump with server_filters->len > 0 so it only
fires when at least one filter actually exists.

Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Three issues in pref.c:
- gtk_widget_set_size_request(..., -2): invalid height; change to -1
- g_signal_connect("focus_out_event"): removed in GTK4; replace with
  GtkEventControllerFocus + "leave" signal (g_signal_connect_swapped so
  dir_entry_activate_callback receives the GtkEntry as its widget arg)
- g_signal_connect("event") on color buttons: the callback was already
  a no-op stub; remove the four dead connections

Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
When the prefs dialog is destroyed, GtkTreeView emits cursor-changed /
selection-changed while its GtkTreeSelection is already being finalized,
causing 60+ CRITICALs of the form:
  gtk_tree_selection_get_selected: assertion 'GTK_IS_TREE_SELECTION' failed

Fix:
- custom_args_list_select_row_callback: return early if the tree view is
  in destruction (gtk_widget_in_destruction)
- game_selection_changed_callback: guard with GTK_IS_TREE_SELECTION check
  since the selection pointer passed by GTK may be stale during teardown

Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
gtk_widget_in_destruction only checks the specific widget, not ancestors,
so the previous guard in custom_args_list_select_row_callback was
ineffective.  Replace all teardown guards with GTK_IS_TREE_SELECTION()
checks, which correctly detect a finalized selection regardless of which
widget triggered the destruction chain.

Fixed callbacks:
- pref.c: custom_args_list_select_row_callback (cursor-changed on per-game tree)
- scripts.c: script_selection_changed_callback (changed on scripts tree)
- flt-player.c: pattern_list_selection_changed (changed on pattern list)

Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
jmallach added 29 commits May 19, 2026 19:08
Remove gtk4-compat.h shim calls from the five small dialog files
(addmaster, addserver, redial, psearch, statistics):
- gtk_box_pack_start/end → gtk_box_append
- gtk_entry_get/set_text → gtk_editable_get/set_text
- gtk_hseparator_new → gtk_separator_new(HORIZONTAL)
- gtk_hbutton_box_new + gtk_button_box_set_layout → gtk_box_new
- gtk_widget_destroy(window) → gtk_window_destroy
- gtk_widget_set_can_default / gtk_widget_grab_default → removed
- gtk_misc_set_alignment → gtk_label_set_xalign (statistics.c)

Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove gtk4-compat.h shim calls from dialogs, rcon, srv-prop and game:
- gtk_box_pack_start/end → gtk_box_append
- gtk_entry_get/set_text → gtk_editable_get/set_text
- gtk_hseparator_new → gtk_separator_new(HORIZONTAL)
- gtk_frame_set_shadow_type → removed
- gtk_widget_destroy(window) → gtk_window_destroy
- gtk_widget_set_can_default / gtk_widget_grab_default → removed

Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
- gtk_box_pack_start/end → gtk_box_append
- gtk_entry_get/set_text → gtk_editable_get/set_text
- gtk_widget_destroy(window) → gtk_window_destroy
- gtk_frame_set_shadow_type → removed
- gtk_scrolled_window_add_with_viewport → gtk_scrolled_window_set_child

Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace all deprecated shim calls with native GTK4 API:
gtk_box_pack_start/end → gtk_box_append, gtk_entry_get/set_text →
gtk_editable_get/set_text (both GTK_ENTRY and combo_get_entry variants),
gtk_widget_destroy → gtk_window_destroy, gtk_frame_set_shadow_type /
gtk_widget_set_can_default / gtk_widget_grab_default → removed,
gtk_misc_set_alignment → gtk_label_set_xalign,
gtk_scrolled_window_add_with_viewport → gtk_scrolled_window_set_child.

Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace all deprecated shim calls with native GTK4 API:
gtk_box_pack_start/end → gtk_box_append, gtk_entry_set_text →
gtk_editable_set_text, gtk_hbutton_box_new/gtk_vbutton_box_new →
gtk_box_new, gtk_button_box_set_layout / gtk_frame_set_shadow_type /
gtk_widget_set_can_default / gtk_widget_grab_default → removed,
gtk_scrolled_window_add_with_viewport → gtk_scrolled_window_set_child,
gtk_widget_destroy → gtk_window_destroy in signal connects.

Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Fix remaining shim calls missed in earlier batches:
- addmaster.c: gtk_entry_set_text(combo_get_entry) → gtk_editable_set_text
- srv-prop.c: one gtk_box_pack_start → gtk_box_append
- xqf.c: gtk_widget_destroy (×3) → gtk_window_destroy, gtk_entry_get_text → gtk_editable_get_text

Remove now-unused shim definitions from gtk4-compat.h:
gtk_box_pack_start/end, gtk_widget_destroy, gtk_widget_set_can_default,
gtk_widget_grab_default, gtk_widget_show_all, gtk_entry_get/set_text,
gtk_hseparator_new, gtk_misc_set_alignment, GTK_SHADOW_* enums,
gtk_frame_set_shadow_type, gtk_scrolled_window_add_with_viewport,
GtkButtonBox/gtk_button_box_set_layout/gtk_vbutton_box_new/gtk_hbutton_box_new.

Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace all surviving shim call sites with native GTK4 API:
- gtk_scrolled_window_new(NULL,NULL) → gtk_scrolled_window_new() (12 sites)
- gtk_window_new(GTK_WINDOW_TOPLEVEL) → gtk_window_new() in redial.c
- gtk_paned_get_child1 → gtk_paned_get_start_child in xqf-ui.c (3 sites)
- GTK_SELECTION_EXTENDED → GTK_SELECTION_MULTIPLE in xqf-ui.c (2 sites)
- gtk_text_view_get_vadjustment → gtk_scrollable_get_vadjustment in rcon.c (2 sites)
- gtk_misc_set_padding/GTK_BIN/gtk_bin_get_child in pref.c → inline margin calls

Remove now-unused shims from gtk4-compat.h: gtk_box_pack_start/end (already done),
gtk_scrolled_window_new(h,v), gtk_window_new(type), gtk_paned_get_child1/2,
GTK_SELECTION_EXTENDED, gtk_init(argc,argv).

Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove all shim definitions whose call sites have been inlined:
GtkContainer/gtk_container_add, GTK_MISC, gtk_misc_set_padding,
GtkBin/GTK_BIN/gtk_bin_get_child, gtk_text_view_get_vadjustment/hadjustment,
GdkEventButton, GTK_SELECTION_EXTENDED, gtk_paned_get_child1/2,
gtk_window_new(type), gtk_scrolled_window_new(h,v), gtk_init(argc,argv).

Also remove two dead never-connected signal handler stubs in filter.c
(country_mouse_click_left/right_list) that were the only GdkEventButton users.

gtk4-compat.h now contains only gtk_file_chooser_get/set_filename helpers,
which wrap the deprecated-but-functional gtk_file_chooser_get/set_file — tied
to GtkFileChooserDialog which itself needs replacement in a later pass.

Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace GtkTreeStore + GtkTreeView (used as a flat single-column list)
with GtkStringList + GtkSingleSelection + GtkListView using a
GtkSignalListItemFactory. The script index now maps directly to the
list position, eliminating the SCRIPTSLIST_ATTR_INDEX column.

Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace GtkTreeStore + GtkTreeView (used as flat icon+text list) with
GListStore<GObject> + GtkSingleSelection + GtkListView. Game type now
maps directly to list position, eliminating GAMESLIST_ATTR_* columns
and the genprefs[].tree_path field. Selection model uses notify::selected
instead of GtkTreeSelection::changed.

Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
…ColumnView

Replace GtkListStore + GtkTreeView (2-column game/args list) with
GListStore<GObject> + GtkSingleSelection + GtkColumnView. Each list
item stores "game" and "args" strings via g_object_set_data_full.
Callbacks updated to use GListStore API (g_list_store_append/remove)
instead of GtkListStore API. Selection uses notify::selected signal
instead of cursor-changed.

Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
…istView

Replace three GtkListStore + GtkTreeView country list widgets (filter
display, popup left/right selection lists) with GListStore<GObject> +
GtkListView backed by GtkNoSelection/GtkSingleSelection. Items store
code/texture/name via g_object_set_data_full. All list manipulation
(append, clear, remove by position) uses GListStore API. Selection
callbacks use notify::selected instead of GtkTreeSelection::changed.

Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
…mnView

Replace GtkListStore+GtkTreeView with GListStore+GtkColumnView+GtkSingleSelection
for the player filter pattern list.

- make_pattern_item() creates GObject items with grp0/1/2 (GdkTexture*),
  mode, and pattern string data
- pattern_list_update_row() uses g_list_store_splice() to replace items
- pattern_list_insert_row() uses g_list_store_insert/append()
- Group icon columns use per-cell GtkGestureClick; on_grp_cell_click()
  reads gtk_list_item_get_position() to call pattern_set_groups()
- Selection via GtkSingleSelection notify::selected signal
- Move up/down uses gtk_column_view_scroll_to() + set_selected()
- Remove PLT_COL_* enum, show_pattern_error() (dead code), aligned_image()

Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Phase 5 (deprecated tree widgets) is complete for all list-style widgets:
filter.c, flt-player.c, scripts.c, pref.c all migrated to GListStore +
GtkColumnView/GtkListView. srv-info.c and xqf-ui.c intentionally kept
as GtkTreeView (genuine hierarchical trees).

Phase 6 (compat shims) is complete except gtk_file_chooser_get/set_filename
which requires a separate GtkFileDialog migration pass.

gtk4-compat.h is now 44 lines.

Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
scroll_to was added in GTK 4.12; guard it so the build works on 4.10/4.11.
Selection still works on older versions, item may just not scroll into view.

Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
The 4.10 floor was set in anticipation of GtkFileDialog (still pending).
Current code builds on 4.0+; the one 4.12 API (gtk_column_view_scroll_to)
is already guarded with GTK_CHECK_VERSION.

Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Already implemented: gtk_application_new("io.github.xqf"), xqf_activate,
g_application_run, gtk_application_add_window.

Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Rewrites src/config.c (~860 lines) to use GLib GKeyFile throughout
while preserving the identical public API from config.h.  The custom
hand-rolled INI parser is removed entirely.

Key changes:
- Single GKeyFile *main_kf replaces the old per-section hash table
- Script metadata loaded via GKeyFile after stripping the "# " prefix
  from the ### BEGIN/END XQF INFO block
- config_sync() uses g_key_file_save_to_file() (atomic write)
- Iterator structs hold a strv + index instead of custom linked-list state
- Internal path-parsing function renamed cfg_parse_path() to avoid the
  name collision with resolve_path() declared in utils.h

Existing config files are format-compatible: GKeyFile accepts keys
containing '/' and spaces, and uses the same \n/\r/\\ escaping as the
old parser.

Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
…tk4-compat.h

Migrate all file-open dialogs to GtkFileDialog (GTK ≥ 4.10) with a
GtkFileChooserDialog fallback for GTK < 4.10, wrapped in
G_GNUC_{BEGIN,END}_IGNORE_DEPRECATIONS.

Changes:
- New file_dialog_cb_t typedef: (const char *path, gpointer data)
- file_dialog() and file_dialog_textentry() changed from GtkWidget*-
  returning (response-callback style) to void, taking file_dialog_cb_t
- All four response callbacks converted to the new signature
  (dialogs.c, pref.c ×2, scripts.c)
- sound_file_btn_clicked() in pref.c migrated to GtkFileDialog with
  GTK_CHECK_VERSION(4, 10, 0) guard; fallback uses GtkFileChooserDialog
  with native gtk_file_chooser_get_file() instead of the removed _get_filename()
- gtk4-compat.h deleted (provided gtk_file_chooser_get/set_filename shims
  that are now superseded)
- #include "gtk4-compat.h" removed from xqf.h, pixmaps.h, utils.c

Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Phase 2 converted all 101 XPM files to PNG and moved them to
pixmaps/default/. The src/xpm/ directory was left in the tree as a
safety net; it is no longer referenced by any code or build rule.

Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
- Mark Phase 6 fully complete (gtk4-compat.h deleted)
- Mark Phase 7 fully complete (config.c rewritten with GKeyFile)
- Add Phase 2 task 6: src/xpm/ deletion (done)
- Update Current State Summary to reflect completed work
- Add Phase 8: GtkComboBox → GtkDropDown (9 files, no version guard needed)
- Add Phase 9: gtk_image_*_from_pixbuf + gtk_widget_get_allocation (5+2 files)
- Update deprecated-API table with replacement version and phase reference
- Remove stale "src/xpm/ deletion" entry from Deferred section

Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Non-editable combo boxes → GtkDropDown + GtkStringList (GTK 4.0+):
- filter.c: filter_option_menu now uses GtkDropDown + GtkStringList;
  set_filter_menu() uses gtk_string_list_splice(); "changed" signal
  replaced by "notify::selected"
- pref.c: weapon-switch, backpack-switch, noskins menus replaced with
  gtk_drop_down_new_from_strings(); callbacks updated for notify::selected

Editable combo boxes → plain GtkEntry (history dropdown dropped):
- addserver.c, addmaster.c, psearch.c, rcon.c: gtk_combo_box_text_new_with_entry()
  replaced with gtk_entry_new(); combo_get_entry() / combo_set_vals() calls
  remain valid via updated helpers in srv-prop.c
- pref.c: qw_skin_combo, q2_skin_combo, cfg_combo, proto_entry → GtkEntry
- srv-prop.c: customcfg_combo → GtkEntry; combo_get_entry() simplified to
  return GTK_ENTRY(widget); combo_set_vals() simplified to set text only

Custom model with icon+text → GListStore + GtkSignalListItemFactory:
- xqf-ui.c: create_server_type_menu() rewritten using GListStore of GObject
  items (with g_object_set_data keys "server-type", "icon", "name") and a
  GtkSignalListItemFactory that builds GtkBox(GtkImage + GtkLabel) per row
- xqf-ui.h: SERVERTYPE_ATTR_* enum removed (no longer needed)
- statistics.c: "All Games" item prepended using g_list_store_insert +
  g_object_set_data instead of GtkListStore + GtkTreeIter

Also fix stray src/xpm/noflag.xpm include in country-filter.c left over
from Phase 2 (xpm deletion): replaced with find_pixmap_directory("noflag.png")
+ gdk_pixbuf_new_from_file().

Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace gtk_image_new/set_from_pixbuf with gtk_image_new/set_from_paintable
using GdkTexture. Country flag loading now also creates pix->texture so all
call sites can use the paintable API uniformly.

Replace gtk_widget_get_allocation with gtk_widget_get_width/height in the
three geometry-save callbacks (main window, rcon, statistics).

Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
…ource tree

Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace libgtk2.0-dev with libgtk-4-dev
- Replace intltool with gettext (CMakeLists uses find_package(Gettext))
- Add libxml2-dev (required by gamesxml2c, was missing from dep list)
- Drop -Werror (GTK4 deprecated APIs trigger -Wdeprecated-declarations)
- Update actions/checkout from v2 to v4

Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
- loadpixmap.c: fix excess initializer in struct pixmap (struct now has
  2 members after GTK2 fields were removed; 3-element { 0,0,0 } → { 0,0 })
- xqf-ui.h: remove dangling unclosed block comment left when the Glade
  widget-lookup function was dropped; was causing -Wcomment on every TU
  that includes this header
- pref.c: escape /* in "audio/* filter" comment to suppress -Wcomment
- xqf.c: remove unused `hbox` and `i` declarations/assignments in
  populate_main_window (dead code left after GTK4 refactor)

Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
srv-info.c, xqf-ui.c and xqf.c intentionally retain GtkTreeView,
GtkTreeStore and GtkTreeSelection for the source-tree and server-info
panels until those panels are ported to GtkListView/GtkColumnView in a
later migration phase.  Scoping the suppression to the xqf target means
the rcon standalone binary, which does not link GTK, keeps full warning
coverage.

Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
@lavenderdotpet

lavenderdotpet commented Jul 15, 2026

Copy link
Copy Markdown

ffs dont use ai

this shit is not good for open source projects
with it being trained on so many dif licences of projects

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants