<URL: http://bugs.freeciv.org/Ticket/Display.html?id=40727 >

This patch improves and extends the unit selection order
(shortcut 'y') to somewhat more useful groupings of units.
Also, a more efficient algorithm is used to filter units
which should be selected.

The "Select same type" menu item in the "Orders" menu is
replaced by a "Select" submenu which contains the items:

  j: "Single"
  k: "Same Type On Tile"
  <shift>k: "All On Tile"
  y: "Same Type On Continent"
  <shift>y: "Same Type Everywhere"

where the prefix is the shortcut key assigned to the menu
item.

Hence the old behavior of 'y' is moved to '<shift>y' (and
in trunk '<shift>y' to 'k').

The shortcut keys were chosen based on availability,
closeness, and ease of use of common operations.

Also, the restriction that only idle units, units without
orders, and units not under ai control are selected is
removed. 


The function request_unit_select_same_type_tile() is
replaced by request_unit_select() which takes a list of
units to base the selection on and parameters controlling
how units should be filtered, according to unit type and
location:

  SELTYPE_SINGLE - Just select the first unit of the list.
  SELTYPE_SAME - The selected units must have the same
    type as any unit in the list.
  SELTYPE_ALL - Do not filter on the unit type.

  SELLOC_TILE - The selected units must be on the same
    tiles as the units in the list.
  SELLOC_CONT - The selected units must be on the same
    continents as the units in the list.
  SELLOC_ALL - Do not filter on location.

Hash tables are used so that the running time of the
selection function is linear in the number of units,
assumming that add_unit_focus() is constant time (it
is not at the moment, since it uses unit_is_in_focus()
which calls unit_list_search() = O(N); I will fix this
in another patch).


-----------------------------------------------------------------------
早く選択しなければならない。
 client/control.c          |   76 ++++++++++++++++++++++++++++++++++++++------
 client/control.h          |   20 +++++++++++-
 client/gui-gtk-2.0/menu.c |   34 ++++++++++++++++++--
 client/gui-xaw/menu.c     |    2 +-
 4 files changed, 116 insertions(+), 16 deletions(-)

diff --git a/client/control.c b/client/control.c
index 280c4ba..82d44f7 100644
--- a/client/control.c
+++ b/client/control.c
@@ -18,6 +18,7 @@
 #include <assert.h>
 
 #include "fcintl.h"
+#include "hash.h"
 #include "log.h"
 #include "mem.h"
 #include "timing.h"
@@ -1210,22 +1211,75 @@ void request_unit_wakeup(struct unit *punit)
 }
 
 /****************************************************************************
-  Select all units of the same type as the given unit.
+  Select all units based on the given list of units and the selection modes.
 ****************************************************************************/
-void request_unit_select_same_type(struct unit_list *punits)
-{
-  if (can_client_change_view()) {
-    unit_list_iterate(punits, punit) {
-      unit_list_iterate(unit_owner(punit)->units, punit2) {
-	if (unit_type(punit2) == unit_type(punit)
-	    && !unit_list_search(punits, punit2)
-	    && punit2->activity == ACTIVITY_IDLE
-	    && !unit_has_orders(punit2)) {
-	  add_unit_focus(punit2);
-	}
+void request_unit_select(struct unit_list *punits,
+                         enum unit_select_type_mode seltype,
+                         enum unit_select_location_mode selloc)
+{
+  const struct player *pplayer;
+  struct unit *punit_first;
+  struct hash_table *tile_table, *type_table, *cont_table;
+
+  if (!can_client_change_view() || !punits
+      || unit_list_size(punits) < 1) {
+    return;
+  }
+
+  punit_first = unit_list_get(punits, 0);
+
+  if (seltype == SELTYPE_SINGLE) {
+    set_unit_focus(punit_first);
+    return;
+  }
+
+  pplayer = unit_owner(punit_first);
+  tile_table = hash_new(hash_fval_keyval, hash_fcmp_keyval);
+  type_table = hash_new(hash_fval_keyval, hash_fcmp_keyval);
+  cont_table = hash_new(hash_fval_int, hash_fcmp_int);
+
+  unit_list_iterate(punits, punit) {
+    if (seltype == SELTYPE_SAME) {
+      hash_insert(type_table, unit_type(punit), NULL);
+    }
+
+    if (selloc == SELLOC_TILE) {
+      hash_insert(tile_table, punit->tile, NULL);
+    } else if (selloc == SELLOC_CONT) {
+      hash_insert(cont_table, &punit->tile->continent, NULL);
+    }
+  } unit_list_iterate_end;
+
+  if (selloc == SELLOC_TILE) {
+    hash_keys_iterate(tile_table, key) {
+      const struct tile *ptile = key;
+      unit_list_iterate(ptile->units, punit) {
+        if (unit_owner(punit) != pplayer) {
+          continue;
+        }
+        if (seltype == SELTYPE_SAME
+            && !hash_key_exists(type_table, unit_type(punit))) {
+          continue;
+        }
+        add_unit_focus(punit);
       } unit_list_iterate_end;
+    } hash_keys_iterate_end;
+  } else {
+    unit_list_iterate(pplayer->units, punit) {
+      if ((seltype == SELTYPE_SAME
+           && !hash_key_exists(type_table, unit_type(punit)))
+          || (selloc == SELLOC_CONT
+              && !hash_key_exists(cont_table, &punit->tile->continent))) {
+        continue;
+      }
+
+      add_unit_focus(punit);
     } unit_list_iterate_end;
   }
+
+  hash_free(tile_table);
+  hash_free(type_table);
+  hash_free(cont_table);
 }
 
 /**************************************************************************
diff --git a/client/control.h b/client/control.h
index 4512d18..213e315 100644
--- a/client/control.h
+++ b/client/control.h
@@ -96,7 +96,25 @@ void request_unit_return(struct unit *punit);
 void request_unit_upgrade(struct unit *punit);
 void request_units_wait(struct unit_list *punits);
 void request_unit_wakeup(struct unit *punit);
-void request_unit_select_same_type(struct unit_list *punits);
+
+enum unit_select_type_mode {
+  SELTYPE_SINGLE = 0,
+  SELTYPE_SAME,
+  SELTYPE_ALL,
+  NUM_SELTYPES
+};
+
+enum unit_select_location_mode {
+  SELLOC_TILE = 0,
+  SELLOC_CONT, /* Continent. */
+  SELLOC_ALL,
+  NUM_SELLOCS
+};
+
+void request_unit_select(struct unit_list *punits,
+                         enum unit_select_type_mode seltype,
+                         enum unit_select_location_mode selloc);
+
 void request_diplomat_action(enum diplomat_actions action, int dipl_id,
 			     int target_id, int value);
 void request_diplomat_answer(enum diplomat_actions action, int dipl_id,
diff --git a/client/gui-gtk-2.0/menu.c b/client/gui-gtk-2.0/menu.c
index ba77872..2dbb15f 100644
--- a/client/gui-gtk-2.0/menu.c
+++ b/client/gui-gtk-2.0/menu.c
@@ -147,6 +147,10 @@ enum MenuID {
   MENU_ORDER_UPGRADE,
   MENU_ORDER_DIPLOMAT_DLG,
   MENU_ORDER_NUKE,
+  MENU_ORDER_SELECT_SINGLE,
+  MENU_ORDER_SELECT_SAME_TYPE_TILE,
+  MENU_ORDER_SELECT_ALL_ON_TILE,
+  MENU_ORDER_SELECT_SAME_TYPE_CONT,
   MENU_ORDER_SELECT_SAME_TYPE,
   MENU_ORDER_WAIT,
   MENU_ORDER_DONE,
@@ -414,8 +418,20 @@ static void orders_menu_callback(gpointer callback_data,
 				 guint callback_action, GtkWidget *widget)
 {
   switch(callback_action) {
+  case MENU_ORDER_SELECT_SINGLE:
+    request_unit_select(get_units_in_focus(), SELTYPE_SINGLE, SELLOC_TILE);
+    break;
+  case MENU_ORDER_SELECT_SAME_TYPE_TILE:
+    request_unit_select(get_units_in_focus(), SELTYPE_SAME, SELLOC_TILE);
+    break;
+  case MENU_ORDER_SELECT_ALL_ON_TILE:
+    request_unit_select(get_units_in_focus(), SELTYPE_ALL, SELLOC_TILE);
+    break;
+  case MENU_ORDER_SELECT_SAME_TYPE_CONT:
+    request_unit_select(get_units_in_focus(), SELTYPE_SAME, SELLOC_CONT);
+    break;
   case MENU_ORDER_SELECT_SAME_TYPE:
-    request_unit_select_same_type(get_units_in_focus());
+    request_unit_select(get_units_in_focus(), SELTYPE_SAME, SELLOC_ALL);
     break;
   case MENU_ORDER_BUILD_CITY:
     unit_list_iterate(get_units_in_focus(), punit) {
@@ -903,8 +919,20 @@ static GtkItemFactoryEntry menu_items[]	=
 	orders_menu_callback,	MENU_ORDER_NUKE						},
   { "/" N_("Orders") "/sep5",				NULL,
 	NULL,			0,					"<Separator>"	},
-  { "/" N_("Orders") "/" N_("Select same type"), "y",
-    orders_menu_callback, MENU_ORDER_SELECT_SAME_TYPE },
+  { "/" N_("Orders") "/" N_("Select"),			NULL,
+	NULL,			0,					"<Branch>"	},
+  { "/" N_("Orders") "/" N_("Select") "/tearoff1",	NULL,
+	NULL,			0,					"<Tearoff>"	},
+  { "/" N_("Orders") "/" N_("Select") "/" N_("Single"), "j",
+        orders_menu_callback,	MENU_ORDER_SELECT_SINGLE				},
+  { "/" N_("Orders") "/" N_("Select") "/" N_("Same Type On Tile"), "k",
+        orders_menu_callback,	MENU_ORDER_SELECT_SAME_TYPE_TILE			},
+  { "/" N_("Orders") "/" N_("Select") "/" N_("All On Tile"), "<shift>k",
+        orders_menu_callback,	MENU_ORDER_SELECT_ALL_ON_TILE				},
+  { "/" N_("Orders") "/" N_("Select") "/" N_("Same Type On Continent"), "y",
+        orders_menu_callback,	MENU_ORDER_SELECT_SAME_TYPE_CONT			},
+  { "/" N_("Orders") "/" N_("Select") "/" N_("Same Type Everywhere"), "<shift>y",
+        orders_menu_callback,	MENU_ORDER_SELECT_SAME_TYPE				},
   { "/" N_("Orders") "/" N_("_Wait"),			"w",
 	orders_menu_callback,	MENU_ORDER_WAIT						},
   { "/" N_("Orders") "/" N_("Done"),			"space",
diff --git a/client/gui-xaw/menu.c b/client/gui-xaw/menu.c
index 8ce86c3..060ef36 100644
--- a/client/gui-xaw/menu.c
+++ b/client/gui-xaw/menu.c
@@ -776,7 +776,7 @@ static void orders_menu_callback(Widget w, XtPointer client_data,
     key_unit_nuke();
     break;
   case MENU_ORDER_SELECT_SAME_TYPE:
-    request_unit_select_same_type(get_units_in_focus());
+    request_unit_select(get_units_in_focus(), SELTYPE_SAME, SELLOC_ALL);
     break;
   case MENU_ORDER_WAIT:
     key_unit_wait();
 client/control.c          |   95 +++++++++++++++++++++++++++++++--------------
 client/control.h          |   21 +++++++++-
 client/gui-gtk-2.0/menu.c |   38 ++++++++++++++----
 client/gui-xaw/menu.c     |    2 +-
 4 files changed, 116 insertions(+), 40 deletions(-)

diff --git a/client/control.c b/client/control.c
index 8a20e96..4fdb521 100644
--- a/client/control.c
+++ b/client/control.c
@@ -19,6 +19,7 @@
 
 /* utility */
 #include "fcintl.h"
+#include "hash.h"
 #include "log.h"
 #include "mem.h"
 #include "timing.h"
@@ -1231,42 +1232,78 @@ void request_unit_wakeup(struct unit *punit)
 }
 
 /****************************************************************************
-  Select all units of the same type as the given unit.
+  Select all units based on the given list of units and the selection modes.
 ****************************************************************************/
-void request_unit_select_same_type(struct unit_list *punits)
-{
-  if (can_client_change_view()) {
-    unit_list_iterate(punits, punit) {
-      unit_list_iterate(unit_owner(punit)->units, punit2) {
-	if (unit_type(punit2) == unit_type(punit)
-	    && !unit_list_search(punits, punit2)
-	    && punit2->activity == ACTIVITY_IDLE
-	    && !unit_has_orders(punit2)) {
-	  add_unit_focus(punit2);
-	}
-      } unit_list_iterate_end;
-    } unit_list_iterate_end;
+void request_unit_select(struct unit_list *punits,
+                         enum unit_select_type_mode seltype,
+                         enum unit_select_location_mode selloc)
+{
+  const struct player *pplayer;
+  const struct tile *ptile;
+  struct unit *punit_first;
+  struct hash_table *tile_table, *type_table, *cont_table;
+
+  if (!can_client_change_view() || !punits
+      || unit_list_size(punits) < 1) {
+    return;
   }
-}
 
-/****************************************************************************
- Select all units of the same type as the given unit that have the same tile
-****************************************************************************/
-void request_unit_select_same_type_tile(struct unit_list *punits)
-{
-  if (can_client_change_view()) {
-    unit_list_iterate(punits, punit) {
-      unit_list_iterate(punit->tile->units, punit2) {
-	if (unit_type(punit2) == unit_type(punit)
-	    && !unit_list_search(punits, punit2)
-	    && punit2->activity == ACTIVITY_IDLE
-	    && !punit2->ai.control
-	    && !unit_has_orders(punit2)) {
-	  add_unit_focus(punit2);
-	}
+  punit_first = unit_list_get(punits, 0);
+
+  if (seltype == SELTYPE_SINGLE) {
+    set_unit_focus(punit_first);
+    return;
+  }
+
+  pplayer = unit_owner(punit_first);
+  tile_table = hash_new(hash_fval_keyval, hash_fcmp_keyval);
+  type_table = hash_new(hash_fval_keyval, hash_fcmp_keyval);
+  cont_table = hash_new(hash_fval_int, hash_fcmp_int);
+
+  unit_list_iterate(punits, punit) {
+    if (seltype == SELTYPE_SAME) {
+      hash_insert(type_table, unit_type(punit), NULL);
+    }
+
+    ptile = unit_tile(punit);
+    if (selloc == SELLOC_TILE) {
+      hash_insert(tile_table, ptile, NULL);
+    } else if (selloc == SELLOC_CONT) {
+      hash_insert(cont_table, &ptile->continent, NULL);
+    }
+  } unit_list_iterate_end;
+
+  if (selloc == SELLOC_TILE) {
+    hash_keys_iterate(tile_table, key) {
+      ptile = key;
+      unit_list_iterate(ptile->units, punit) {
+        if (unit_owner(punit) != pplayer) {
+          continue;
+        }
+        if (seltype == SELTYPE_SAME
+            && !hash_key_exists(type_table, unit_type(punit))) {
+          continue;
+        }
+        add_unit_focus(punit);
       } unit_list_iterate_end;
+    } hash_keys_iterate_end;
+  } else {
+    unit_list_iterate(pplayer->units, punit) {
+      ptile = unit_tile(punit);
+      if ((seltype == SELTYPE_SAME
+           && !hash_key_exists(type_table, unit_type(punit)))
+          || (selloc == SELLOC_CONT
+              && !hash_key_exists(cont_table, &ptile->continent))) {
+        continue;
+      }
+
+      add_unit_focus(punit);
     } unit_list_iterate_end;
   }
+
+  hash_free(tile_table);
+  hash_free(type_table);
+  hash_free(cont_table);
 }
 
 /**************************************************************************
diff --git a/client/control.h b/client/control.h
index ea7f591..762724f 100644
--- a/client/control.h
+++ b/client/control.h
@@ -87,8 +87,25 @@ void request_unit_return(struct unit *punit);
 void request_unit_upgrade(struct unit *punit);
 void request_units_wait(struct unit_list *punits);
 void request_unit_wakeup(struct unit *punit);
-void request_unit_select_same_type(struct unit_list *punits);
-void request_unit_select_same_type_tile(struct unit_list *punits);
+
+enum unit_select_type_mode {
+  SELTYPE_SINGLE = 0,
+  SELTYPE_SAME,
+  SELTYPE_ALL,
+  NUM_SELTYPES
+};
+
+enum unit_select_location_mode {
+  SELLOC_TILE = 0,
+  SELLOC_CONT, /* Continent. */
+  SELLOC_ALL,
+  NUM_SELLOCS
+};
+
+void request_unit_select(struct unit_list *punits,
+                         enum unit_select_type_mode seltype,
+                         enum unit_select_location_mode selloc);
+
 void request_diplomat_action(enum diplomat_actions action, int dipl_id,
 			     int target_id, int value);
 void request_diplomat_answer(enum diplomat_actions action, int dipl_id,
diff --git a/client/gui-gtk-2.0/menu.c b/client/gui-gtk-2.0/menu.c
index f837955..4189d02 100644
--- a/client/gui-gtk-2.0/menu.c
+++ b/client/gui-gtk-2.0/menu.c
@@ -155,8 +155,11 @@ enum MenuID {
   MENU_ORDER_UPGRADE,
   MENU_ORDER_DIPLOMAT_DLG,
   MENU_ORDER_NUKE,
-  MENU_ORDER_SELECT_SAME_TYPE,
+  MENU_ORDER_SELECT_SINGLE,
   MENU_ORDER_SELECT_SAME_TYPE_TILE,
+  MENU_ORDER_SELECT_ALL_ON_TILE,
+  MENU_ORDER_SELECT_SAME_TYPE_CONT,
+  MENU_ORDER_SELECT_SAME_TYPE,
   MENU_ORDER_WAIT,
   MENU_ORDER_DONE,
 
@@ -432,11 +435,20 @@ static void orders_menu_callback(gpointer callback_data,
 				 guint callback_action, GtkWidget *widget)
 {
   switch(callback_action) {
-  case MENU_ORDER_SELECT_SAME_TYPE:
-    request_unit_select_same_type(get_units_in_focus());
+  case MENU_ORDER_SELECT_SINGLE:
+    request_unit_select(get_units_in_focus(), SELTYPE_SINGLE, SELLOC_TILE);
     break;
   case MENU_ORDER_SELECT_SAME_TYPE_TILE:
-    request_unit_select_same_type_tile(get_units_in_focus());
+    request_unit_select(get_units_in_focus(), SELTYPE_SAME, SELLOC_TILE);
+    break;
+  case MENU_ORDER_SELECT_ALL_ON_TILE:
+    request_unit_select(get_units_in_focus(), SELTYPE_ALL, SELLOC_TILE);
+    break;
+  case MENU_ORDER_SELECT_SAME_TYPE_CONT:
+    request_unit_select(get_units_in_focus(), SELTYPE_SAME, SELLOC_CONT);
+    break;
+  case MENU_ORDER_SELECT_SAME_TYPE:
+    request_unit_select(get_units_in_focus(), SELTYPE_SAME, SELLOC_ALL);
     break;
   case MENU_ORDER_BUILD_CITY:
     unit_list_iterate(get_units_in_focus(), punit) {
@@ -960,10 +972,20 @@ static GtkItemFactoryEntry menu_items[]	=
 	orders_menu_callback,	MENU_ORDER_NUKE						},
   { "/" N_("Orders") "/sep5",				NULL,
 	NULL,			0,					"<Separator>"	},
-  { "/" N_("Orders") "/" N_("Select same type"), "y",
-    orders_menu_callback, MENU_ORDER_SELECT_SAME_TYPE },
-  { "/" N_("Orders") "/" N_("Select same type in tile"), "<shift>y",
-    orders_menu_callback, MENU_ORDER_SELECT_SAME_TYPE_TILE },
+  { "/" N_("Orders") "/" N_("Select"),			NULL,
+	NULL,			0,					"<Branch>"	},
+  { "/" N_("Orders") "/" N_("Select") "/tearoff1",	NULL,
+	NULL,			0,					"<Tearoff>"	},
+  { "/" N_("Orders") "/" N_("Select") "/" N_("Single"), "j",
+        orders_menu_callback,	MENU_ORDER_SELECT_SINGLE				},
+  { "/" N_("Orders") "/" N_("Select") "/" N_("Same Type On Tile"), "k",
+        orders_menu_callback,	MENU_ORDER_SELECT_SAME_TYPE_TILE			},
+  { "/" N_("Orders") "/" N_("Select") "/" N_("All On Tile"), "<shift>k",
+        orders_menu_callback,	MENU_ORDER_SELECT_ALL_ON_TILE				},
+  { "/" N_("Orders") "/" N_("Select") "/" N_("Same Type On Continent"), "y",
+        orders_menu_callback,	MENU_ORDER_SELECT_SAME_TYPE_CONT			},
+  { "/" N_("Orders") "/" N_("Select") "/" N_("Same Type Everywhere"), "<shift>y",
+        orders_menu_callback,	MENU_ORDER_SELECT_SAME_TYPE				},
   { "/" N_("Orders") "/" N_("_Wait"),			"w",
 	orders_menu_callback,	MENU_ORDER_WAIT						},
   { "/" N_("Orders") "/" N_("Done"),			"space",
diff --git a/client/gui-xaw/menu.c b/client/gui-xaw/menu.c
index 62ed0d4..f65e3f1 100644
--- a/client/gui-xaw/menu.c
+++ b/client/gui-xaw/menu.c
@@ -785,7 +785,7 @@ static void orders_menu_callback(Widget w, XtPointer client_data,
     key_unit_nuke();
     break;
   case MENU_ORDER_SELECT_SAME_TYPE:
-    request_unit_select_same_type(get_units_in_focus());
+    request_unit_select(get_units_in_focus(), SELTYPE_SAME, SELLOC_ALL);
     break;
   case MENU_ORDER_WAIT:
     key_unit_wait();
_______________________________________________
Freeciv-dev mailing list
Freeciv-dev@gna.org
https://mail.gna.org/listinfo/freeciv-dev

Reply via email to