Project

General

Profile

Bug #2475 » 0002-gtk4-update-the-unit-selection-dialog-live-with-all-.patch

Andrew McGuinness, 11/10/2026 10:07 PM

View differences:

client/gui-gtk-4.0/unitselect.c
const struct unit *punit,
bool transported, GtkTreeIter *it,
GtkTreeIter *parent);
static void usdlg_tab_update_units(struct unit_select_dialog *pdialog,
enum unit_select_location_mode loc,
enum unit_activity act,
const struct unit *punit,
bool transported, GtkTreeIter *it);
static void usdlg_cmd_ready(GObject *object, gpointer data);
static void usdlg_cmd_sentry(GObject *object, gpointer data);
......
return store;
}
struct retain_set {
int count;
int max;
int *ids;
};
static void retain_set_init(struct retain_set* retain_list, int n) {
retain_list->max = n;
retain_list->count = 0;
if (n > 0) {
retain_list->ids = fc_calloc(sizeof(int), n);
}
}
static void retain_set_free(struct retain_set* retain_list) {
if (retain_list->max > 0) {
free(retain_list->ids);
}
}
static void retain_set_add(struct retain_set* rs, int id) {
fc_assert(rs->count < rs->max);
rs->ids[rs->count++] = id;
}
static bool retain_set_contains(struct retain_set* retain_list, int id) {
int i;
for (i = 0; i < retain_list->count; ++i) {
if (retain_list->ids[i] == id) return true;
}
return false;
}
/*************************************************************************//**
Update on tab of the dialog.
Populate an existing tree containing units. Can be top level on the
first tab, or third level on the others.
it_unit points to the beginning of the current tree
it_unit is only valid if valid_unit is true
parent is the parent of the tree, NULL for the first tab.
Units not of utype_id will be left alone, not updated or removed.
(for first tab, this is called once for each unit type to update all types)
*****************************************************************************/
static void refresh_tree(struct unit_select_dialog *pdialog,
enum unit_select_location_mode loc,
GtkTreeStore* store,
bool valid_unit,
GtkTreeIter* it_unit,
GtkTreeIter* parent,
int utype_id,
struct usdata* data, // note can validly be null!
int act,
struct retain_set* retain_unit_rows)
{
while (valid_unit) {
int v_utype;
int v_unit;
bool retain;
gtk_tree_model_get(GTK_TREE_MODEL(store), it_unit,
3, &v_utype,
4, &v_unit, -1);
if (v_utype == utype_id) {
retain = false;
if (data) {
unit_list_iterate(data->units[act], punit) {
if ((punit->id == v_unit) &&
(!unit_transported(punit))) {
usdlg_tab_update_units(pdialog, loc, act, punit, FALSE, it_unit);
retain = true;
retain_set_add(retain_unit_rows, punit->id);
break;
}
} unit_list_iterate_end;
}
} else {
retain = true; // for now, it's another type
}
if (retain) {
valid_unit = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), it_unit);
} else {
valid_unit = gtk_tree_store_remove(store, it_unit);
}
}
if (data) {
unit_list_iterate(data->units[act], punit) {
GtkTreeIter it_unit_add;
if (!retain_set_contains(retain_unit_rows, punit->id)) {
usdlg_tab_append_units(pdialog, loc, act, punit, FALSE,
&it_unit_add, parent);
}
} unit_list_iterate_end;
}
}
/*************************************************************************//**
Update one tab of the dialog.
*****************************************************************************/
static bool usdlg_tab_update(struct unit_select_dialog *pdialog,
struct usdata_hash *ushash,
......
{
bool show = FALSE;
GtkTreeStore *store;
bool valid_utype;
bool valid_act;
bool valid_unit;
int v_utype;
fc_assert_ret_val(ushash, FALSE);
fc_assert_ret_val(pdialog != NULL, FALSE);
store = pdialog->tabs[loc].store;
/* clear current store. */
gtk_tree_store_clear(GTK_TREE_STORE(store));
/* Iterate over all unit types. */
if (loc == SELLOC_UNITS) {
/* Special case - show all units on this tile in their transports. */
/* Special case - show all units on this tile in their transports.
All units are stored in the hash with activity "IDLE", whatever their actual activity */
int act = ACTIVITY_IDLE;
/* Keep track of which rows in the tree still matter */
int old_unit_count = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(store), NULL);
struct retain_set retain_unit_rows;
retain_set_init(&retain_unit_rows, old_unit_count);
unit_type_iterate(utype) {
struct usdata *data;
usdata_hash_lookup(ushash, utype_index(utype), &data);
/* remove all existing rows from the tree of this type that are not
* part of the new dataset, and update any that are.
*/
GtkTreeIter it_unit;
valid_unit = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &it_unit);
refresh_tree(pdialog,
loc,
store,
valid_unit,
&it_unit,
NULL,
utype_index(utype),
data,
act,
&retain_unit_rows);
} unit_type_iterate_end;
retain_set_free(&retain_unit_rows);
show = TRUE;
} else {
int old_utype_count = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(store), NULL);
struct retain_set retain_utype_rows;
retain_set_init(&retain_utype_rows, old_utype_count);
GtkTreeIter it_utype;
valid_utype = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &it_utype);
while (valid_utype) {
gtk_tree_model_get(GTK_TREE_MODEL(store), &it_utype,
3, &v_utype, -1);
struct usdata *data;
usdata_hash_lookup(ushash, v_utype, &data);
if (!data) {
continue;
}
valid_utype = gtk_tree_store_remove(store, &it_utype);
} else {
/* this unit type is to be populated */
show = TRUE;
retain_set_add(&retain_utype_rows, v_utype);
activity_type_iterate(act) {
if (unit_list_size(data->units[act]) == 0) {
continue;
int old_act_count = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(store), &it_utype);
struct retain_set retain_act_rows;
retain_set_init(&retain_act_rows, old_act_count);
GtkTreeIter it_action;
valid_act = gtk_tree_model_iter_children(GTK_TREE_MODEL(store), &it_action, &it_utype);
while (valid_act) {
int act;
gtk_tree_model_get(GTK_TREE_MODEL(store), &it_action,
6, &act, -1);
if (unit_list_size(data->units[act]) == 0) {
valid_act = gtk_tree_store_remove(store, &it_action);
} else {
/* This activity is to be populated */
retain_set_add(&retain_act_rows, act);
/* Keep track of which rows in the tree still matter */
int old_unit_count = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(store), &it_action);
struct retain_set retain_unit_rows;
retain_set_init(&retain_unit_rows, old_unit_count);
GtkTreeIter it_unit;
valid_unit = gtk_tree_model_iter_children(GTK_TREE_MODEL(store), &it_unit, &it_action);
refresh_tree(pdialog,
loc,
store,
valid_unit,
&it_unit,
&it_action,
v_utype,
data,
act,
&retain_unit_rows);
valid_act = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &it_action);
retain_set_free(&retain_unit_rows);
} /* next activity */
}
unit_list_iterate(data->units[act], punit) {
GtkTreeIter it_unit;
/* at this point we have correctly populated every activity that was already
* existing in the tree for the unit type. We now need to add the activities
* that were not present */
if (data) { /* if there are no units of this type, we are done */
int count = 0;
activity_type_iterate(act) {
if (unit_list_size(data->units[act]) == 0) {
continue;
}
count += unit_list_size(data->units[act]);
if (retain_set_contains(&retain_act_rows, act)) {
/* this activity is already done */
continue;
}
/* Display unit activities. */
usdlg_tab_append_activity(GTK_TREE_STORE(store), loc, data->utype,
act, unit_list_size(data->units[act]),
&it_action, &it_utype);
/* Level 3: Display all units with this activity
* (and transported units in further level(s)). */
unit_list_iterate(data->units[act], punit) {
GtkTreeIter it_unit;
usdlg_tab_append_units(pdialog, loc, act, punit, FALSE,
&it_unit, &it_action);
} unit_list_iterate_end;
/* Expand to the activities. */
GtkTreePath *path;
path
= gtk_tree_model_get_path(GTK_TREE_MODEL(store),
&it_utype);
gtk_tree_view_expand_row(GTK_TREE_VIEW(pdialog->tabs[loc].view), path,
FALSE);
gtk_tree_path_free(path);
show = TRUE;
} activity_type_iterate_end;
/* Update sum of units with this type. */
gtk_tree_store_set(GTK_TREE_STORE(store), &it_utype, 2, count, -1);
}
retain_set_free(&retain_act_rows);
valid_utype = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &it_utype);
} /* next unit type */
}
usdlg_tab_append_units(pdialog, loc, act, punit, FALSE,
&it_unit, NULL);
} unit_list_iterate_end;
/* at this point we have correctly populated every unit type that was already
* existing in the tree We now need to add the unit types
* that were not present */
/* Show this tab. */
show = TRUE;
} activity_type_iterate_end;
} unit_type_iterate_end;
} else {
unit_type_iterate(utype) {
struct usdata *data;
bool first = TRUE;
GtkTreeIter it_utype;
GtkTreePath *path;
int count = 0;
if (retain_set_contains(&retain_utype_rows, utype_index(utype))) {
continue;
}
usdata_hash_lookup(ushash, utype_index(utype), &data);
if (!data) {
continue;
}
activity_type_iterate(act) {
GtkTreeIter it_act;
GtkTreeIter it_utype_add;
if (unit_list_size(data->units[act]) == 0) {
continue;
}
......
/* Level 1: Display unit type. */
if (first) {
usdlg_tab_append_utype(GTK_TREE_STORE(store), loc, data->utype,
&it_utype);
&it_utype_add);
first = FALSE;
}
/* Level 2: Display unit activities. */
usdlg_tab_append_activity(GTK_TREE_STORE(store), loc, data->utype,
act, unit_list_size(data->units[act]),
&it_act, &it_utype);
&it_act, &it_utype_add);
/* Level 3: Display all units with this activity
* (and transported units in further level(s)). */
......
count += unit_list_size(data->units[act]);
/* Update sum of units with this type. */
gtk_tree_store_set(GTK_TREE_STORE(store), &it_utype, 2, count, -1);
gtk_tree_store_set(GTK_TREE_STORE(store), &it_utype_add, 2, count, -1);
/* Expand to the activities. */
path
= gtk_tree_model_get_path(GTK_TREE_MODEL(pdialog->tabs[loc].store),
&it_utype);
&it_utype_add);
gtk_tree_view_expand_row(GTK_TREE_VIEW(pdialog->tabs[loc].view), path,
FALSE);
gtk_tree_path_free(path);
......
/* Show this tab. */
show = TRUE;
} activity_type_iterate_end;
} unit_type_iterate_end;
retain_set_free(&retain_utype_rows);
}
return show;
......
}
/*************************************************************************//**
Append units (recursively).
update units (recursively).
"it" is already valid, ready to be set.
May already have children
*****************************************************************************/
static void usdlg_tab_append_units(struct unit_select_dialog *pdialog,
static void usdlg_tab_update_units(struct unit_select_dialog *pdialog,
enum unit_select_location_mode loc,
enum unit_activity act,
const struct unit *punit,
bool transported, GtkTreeIter *it,
GtkTreeIter *parent)
bool transported, GtkTreeIter *it)
{
const char *text;
GdkPixbuf *pix;
......
store = pdialog->tabs[loc].store;
/* Add this item. */
gtk_tree_store_append(GTK_TREE_STORE(store), it, parent);
/* Unit gfx */
pix = usdlg_get_unit_image(punit);
......
-1);
g_object_unref(pix);
/* Keep track of which rows in this part of the tree still matter */
int old_unit_count = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(store), it);
struct retain_set retain_unit_rows;
retain_set_init(&retain_unit_rows, old_unit_count);
if (old_unit_count > 0) {
GtkTreeIter it_unit;
bool valid_unit = gtk_tree_model_iter_children(GTK_TREE_MODEL(store), &it_unit, it);
int v_utype;
int v_unit;
int v_act;
bool retain;
while (valid_unit) {
gtk_tree_model_get(GTK_TREE_MODEL(store), &it_unit,
3, &v_utype,
4, &v_unit,
6, &v_act, -1);
retain = false;
if (get_transporter_occupancy(punit) > 0) {
unit_list_iterate(unit_transport_cargo(punit), pcargo) {
if (pcargo->id == v_unit) {
usdlg_tab_update_units(pdialog, loc, act, pcargo, FALSE, &it_unit);
retain = true;
retain_set_add(&retain_unit_rows, pcargo->id);
// break;
}
} unit_list_iterate_end;
}
if (retain) {
valid_unit = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &it_unit);
} else {
valid_unit = gtk_tree_store_remove(GTK_TREE_STORE(store), &it_unit);
}
}
}
if (get_transporter_occupancy(punit) > 0) {
unit_list_iterate(unit_transport_cargo(punit), pcargo) {
GtkTreeIter it_cargo;
usdlg_tab_append_units(pdialog, loc, act, pcargo, TRUE, &it_cargo, it);
if ((unit_transport_get(pcargo) == punit) &&
!retain_set_contains(&retain_unit_rows, pcargo->id)) {
usdlg_tab_append_units(pdialog, loc, act, pcargo, TRUE,
&it_cargo, it);
}
} unit_list_iterate_end;
}
retain_set_free(&retain_unit_rows);
if (!transported && unit_is_in_focus(punit)
&& get_num_units_in_focus() == 1) {
......
}
}
/*************************************************************************//**
Append units (recursively).
*****************************************************************************/
static void usdlg_tab_append_units(struct unit_select_dialog *pdialog,
enum unit_select_location_mode loc,
enum unit_activity act,
const struct unit *punit,
bool transported, GtkTreeIter *it,
GtkTreeIter *parent)
{
GtkTreeStore *store;
fc_assert_ret(pdialog != NULL);
fc_assert_ret(punit != NULL);
store = pdialog->tabs[loc].store;
/* Add this item. */
gtk_tree_store_append(GTK_TREE_STORE(store), it, parent);
usdlg_tab_update_units(pdialog, loc, act, punit, transported, it);
}
/*************************************************************************//**
Callback for the ready button.
*****************************************************************************/
client/packhand.c
|| get_focus_unit_on_tile(unit_tile(punit))
|| (moved && get_focus_unit_on_tile(old_tile))) {
update_unit_info_label(get_units_in_focus());
/* Update (an possible active) unit select dialog. */
unit_select_dialog_update();
}
if (repaint_unit) {
refresh_unit_mapcanvas(punit, unit_tile(punit), TRUE, FALSE);
/* Update (an possible active) unit select dialog. */
unit_select_dialog_update();
}
if ((check_focus || get_num_units_in_focus() == 0)
(1-1/2)