Bug #2475 » 0002-gtk4-update-the-unit-selection-dialog-live-with-all-.patch
| 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)
|
||