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

> [book - Thu Sep 18 21:31:39 2008]:
> 
> Here is my port of the allies-only button warclient feature for
> S2_1 and later. It has some improvements over the warclient
> version, namely that for local games (i.e. games on a forked
> server) the feature is hidden, and much improved code comments.

Updated to recent S2_1 and trunk, and somewhat improved.


-----------------------------------------------------------------------
何ヶ月も厳しい訓練して、今足の指で箸を割ることができる。
 client/gui-gtk-2.0/chatline.c |   59 +++++++++++++++++++++++++++++-
 client/gui-gtk-2.0/gui_main.c |   82 ++++++++++++++++++++++++++++++++++++++++-
 client/gui-gtk-2.0/gui_main.h |    2 +
 client/gui-gtk-2.0/pages.c    |    1 +
 4 files changed, 141 insertions(+), 3 deletions(-)

diff --git a/client/gui-gtk-2.0/chatline.c b/client/gui-gtk-2.0/chatline.c
index e802940..89855bc 100644
--- a/client/gui-gtk-2.0/chatline.c
+++ b/client/gui-gtk-2.0/chatline.c
@@ -27,6 +27,7 @@
 #include "packets.h"
 #include "support.h"
 
+#include "client_main.h"
 #include "climisc.h"
 #include "gui_main.h"
 #include "gui_stuff.h"
@@ -37,6 +38,55 @@
 struct genlist *history_list;
 int history_pos;
 
+/**************************************************************************
+  Helper function to determine if a given client input line is intended as
+  a "plain" public message. Note that messages prefixed with : are a
+  special case (explicit public messages), and will return FALSE.
+**************************************************************************/
+static bool is_plain_public_message(const char *s)
+{
+  /* FIXME: These prefix definitions are duplicated in the server. */
+  const char ALLIES_CHAT_PREFIX = '.';
+  const char SERVER_COMMAND_PREFIX = '/';
+  const char MESSAGE_PREFIX = ':';
+
+  const char *p;
+
+  /* If it is a server command or an explicit ally
+   * message, then it is not a public message. */
+  if (s[0] == SERVER_COMMAND_PREFIX || s[0] == ALLIES_CHAT_PREFIX) {
+    return FALSE;
+  }
+
+  /* It might be a private message of the form
+   *   'player name with spaces':the message
+   * or with ". So skip past the player name part. */
+  if (s[0] == '\'' || s[0] == '"') {
+    p = strchr(s + 1, s[0]);
+  } else {
+    p = s;
+  }
+
+  /* Now we just need to check that it is not a private
+   * message. If we encounter a space then the preceeding
+   * text could not have been a user/player name (the
+   * quote check above eliminated names with spaces) so
+   * it must be a public message. Otherwise if we encounter
+   * the message prefix : then the text parsed up until now
+   * was a player/user name and the line is intended as
+   * a private message (or explicit public message if the
+   * first character is :). */
+  while (p != NULL && *p != '\0') {
+    if (my_isspace(*p)) {
+      return TRUE;
+    } else if (*p == MESSAGE_PREFIX) {
+      return FALSE;
+    }
+    p++;
+  }
+  return TRUE;
+}
+
 
 /**************************************************************************
 ...
@@ -48,7 +98,14 @@ void inputline_return(GtkEntry *w, gpointer data)
   theinput = gtk_entry_get_text(w);
   
   if (*theinput) {
-    send_chat(theinput);
+    if (client_state() == C_S_RUNNING && allied_chat_only
+        && is_plain_public_message(theinput)) {
+      char buf[MAX_LEN_MSG];
+      my_snprintf(buf, sizeof(buf), ". %s", theinput);
+      send_chat(buf);
+    } else {
+      send_chat(theinput);
+    }
 
     if (genlist_size(history_list) >= MAX_CHATLINE_HISTORY) {
       void *data;
diff --git a/client/gui-gtk-2.0/gui_main.c b/client/gui-gtk-2.0/gui_main.c
index 653f6fb..2413a7e 100644
--- a/client/gui-gtk-2.0/gui_main.c
+++ b/client/gui-gtk-2.0/gui_main.c
@@ -57,6 +57,7 @@
 #include "clinet.h"
 #include "colors.h"
 #include "connectdlg.h"
+#include "connectdlg_common.h"
 #include "control.h"
 #include "cma_fe.h"
 #include "dialogs.h"
@@ -102,6 +103,7 @@ bool split_bottom_notebook = FALSE;
 bool new_messages_go_to_top = FALSE;
 bool show_message_window_buttons = TRUE;
 bool metaserver_tab_first = FALSE;
+bool allied_chat_only = FALSE;
 
 GtkWidget *toplevel;
 GdkWindow *root_window;
@@ -172,6 +174,7 @@ char font_city_names[512] = "Sans Bold 10";
 char font_city_productions[512] = "Serif 10";
 
 static void split_bottom_notebook_callback(struct client_option *op);
+static void allied_chat_only_callback(struct client_option *op);
 
 client_option gui_options[] = {
   /* This option is the same as the one in gui-gtk */
@@ -242,6 +245,19 @@ client_option gui_options[] = {
                      "be the first notebook tab in the network page. This "
                      "option requires a restart in order to take effect."),
                   COC_NETWORK),
+  GEN_BOOL_OPTION_CB(allied_chat_only,
+                     N_("Plain chat messages are sent to allies only"),
+                     N_("If this option is enabled, then plain messages "
+                        "typed into the chat entry while the game is "
+                        "running will only be sent to your allies. "
+                        "Otherwise plain messages will be sent as "
+                        "public chat messages. To send a public chat "
+                        "message with this option enabled, prefix the "
+                        "message with a single colon ':'. This option "
+                        "can also be set using a toggle button beside "
+                        "the chat entry (only visible in multiplayer "
+                        "games)."),
+                     COC_NETWORK, allied_chat_only_callback),
   GEN_FONT_OPTION(font_city_label,
   		  city_label,
 		  N_("City Label"),
@@ -319,6 +335,7 @@ static int unit_ids[MAX_NUM_UNITS_BELOW];  /* ids of the units icons in
 GtkTextView *main_message_area;
 GtkTextBuffer *message_buffer;
 static GtkWidget *inputline;
+static GtkWidget *allied_chat_toggle_button;
 
 static enum Display_color_type display_color_type;  /* practically unused */
 static gint timer_id;                               /*       ditto        */
@@ -348,6 +365,8 @@ static gboolean select_unit_pixmap_callback(GtkWidget *w, GdkEvent *ev,
 					    gpointer data);
 static gboolean quit_dialog_callback(void);
 
+static void allied_chat_button_toggled(GtkToggleButton *button,
+                                       gpointer user_data);
 
 /****************************************************************************
   Called by the tileset code to set the font size that should be used to
@@ -1084,6 +1103,7 @@ static void setup_widgets(void)
 {
   GtkWidget *box, *ebox, *hbox, *sbox, *align, *label;
   GtkWidget *frame, *table, *table2, *paned, *hpaned, *sw, *text;
+  GtkWidget *button;
   int i;
   char buf[256];
   struct sprite *sprite;
@@ -1457,13 +1477,24 @@ static void setup_widgets(void)
   chat_welcome_message();
 
   /* the chat line */
-  inputline = gtk_entry_new();
-  gtk_box_pack_start(GTK_BOX(vbox), inputline, FALSE, FALSE, 3);
+  hbox = gtk_hbox_new(FALSE, 4);
+  gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 3);
 
+  inputline = gtk_entry_new();
   g_signal_connect(inputline, "activate",
 		   G_CALLBACK(inputline_return), NULL);
   g_signal_connect(inputline, "key_press_event",
                    G_CALLBACK(inputline_handler), NULL);
+  gtk_box_pack_start(GTK_BOX(hbox), inputline, TRUE, TRUE, 0);
+
+  button = gtk_toggle_button_new_with_label(_("Allies Only"));
+  gtk_button_set_focus_on_click(GTK_BUTTON(button), FALSE);
+  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button),
+                               allied_chat_only);
+  g_signal_connect(button, "toggled",
+                   G_CALLBACK(allied_chat_button_toggled), NULL);
+  gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 2);
+  allied_chat_toggle_button = button;
 
   /* Other things to take care of */
 
@@ -2235,3 +2266,50 @@ static void split_bottom_notebook_callback(struct client_option *op)
   }
   popup_meswin_dialog(FALSE);
 }
+
+/****************************************************************************
+  Option callback for the 'allied_chat_only' option. This updates the state
+  of the associated toggle button.
+****************************************************************************/
+static void allied_chat_only_callback(struct client_option *op)
+{
+  GtkWidget *button;
+
+  button = allied_chat_toggle_button;
+  g_return_if_fail(button != NULL);
+  g_return_if_fail(GTK_IS_TOGGLE_BUTTON(button));
+
+  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button),
+                               *op->p_bool_value);
+}
+
+/**************************************************************************
+  Set the chatline buttons to reflect the state of the game and current
+  client options. This function should be called on game start.
+**************************************************************************/
+void refresh_chat_buttons(void)
+{
+  GtkWidget *button;
+
+  button = allied_chat_toggle_button;
+  g_return_if_fail(button != NULL);
+  g_return_if_fail(GTK_IS_TOGGLE_BUTTON(button));
+
+  /* Hide the "Allies Only" button for local games. */
+  if (is_server_running()) {
+    gtk_widget_hide(button);
+  } else {
+    gtk_widget_show(button);
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button),
+                                 allied_chat_only);
+  }
+}
+
+/**************************************************************************
+  Handle a toggle of the "Allies Only" chat button.
+**************************************************************************/
+static void allied_chat_button_toggled(GtkToggleButton *button,
+                                       gpointer user_data)
+{
+  allied_chat_only = gtk_toggle_button_get_active(button);
+}
diff --git a/client/gui-gtk-2.0/gui_main.h b/client/gui-gtk-2.0/gui_main.h
index 939bc27..c986338 100644
--- a/client/gui-gtk-2.0/gui_main.h
+++ b/client/gui-gtk-2.0/gui_main.h
@@ -31,6 +31,7 @@ extern bool split_bottom_notebook;
 extern bool new_messages_go_to_top;
 extern bool show_message_window_buttons;
 extern bool metaserver_tab_first;
+extern bool allied_chat_only;
 
 extern GdkGC *          civ_gc;
 extern GdkGC *          mask_fg_gc;
@@ -107,6 +108,7 @@ gboolean inputline_handler(GtkWidget *w, GdkEventKey *ev);
 
 void reset_unit_table(void);
 void popup_quit_dialog(void);
+void refresh_chat_buttons(void);
 
 /* There simply is no proper header to place this define. Creating one
  * just for this seems overkill. */
diff --git a/client/gui-gtk-2.0/pages.c b/client/gui-gtk-2.0/pages.c
index 2259c2e..8e389cb 100644
--- a/client/gui-gtk-2.0/pages.c
+++ b/client/gui-gtk-2.0/pages.c
@@ -2309,6 +2309,7 @@ void set_client_page(enum client_pages page)
     break;
   case PAGE_GAME:
     chatline_scroll_to_bottom();
+    refresh_chat_buttons();
     break;
   case PAGE_NETWORK:
     update_network_lists();
 client/gui-gtk-2.0/chatline.c |   59 ++++++++++++++++++++++++++++-
 client/gui-gtk-2.0/gui_main.c |   84 +++++++++++++++++++++++++++++++++++++++-
 client/gui-gtk-2.0/gui_main.h |    2 +
 client/gui-gtk-2.0/pages.c    |    1 +
 4 files changed, 142 insertions(+), 4 deletions(-)

diff --git a/client/gui-gtk-2.0/chatline.c b/client/gui-gtk-2.0/chatline.c
index 7e02a61..0c2821b 100644
--- a/client/gui-gtk-2.0/chatline.c
+++ b/client/gui-gtk-2.0/chatline.c
@@ -27,6 +27,7 @@
 #include "packets.h"
 #include "support.h"
 
+#include "civclient.h"
 #include "climisc.h"
 #include "clinet.h"
 #include "gui_main.h"
@@ -38,6 +39,55 @@
 struct genlist *history_list;
 int history_pos;
 
+/**************************************************************************
+  Helper function to determine if a given client input line is intended as
+  a "plain" public message. Note that messages prefixed with : are a
+  special case (explicit public messages), and will return FALSE.
+**************************************************************************/
+static bool is_plain_public_message(const char *s)
+{
+  /* FIXME: These prefix definitions are duplicated in the server. */
+  const char ALLIES_CHAT_PREFIX = '.';
+  const char SERVER_COMMAND_PREFIX = '/';
+  const char MESSAGE_PREFIX = ':';
+
+  const char *p;
+
+  /* If it is a server command or an explicit ally
+   * message, then it is not a public message. */
+  if (s[0] == SERVER_COMMAND_PREFIX || s[0] == ALLIES_CHAT_PREFIX) {
+    return FALSE;
+  }
+
+  /* It might be a private message of the form
+   *   'player name with spaces':the message
+   * or with ". So skip past the player name part. */
+  if (s[0] == '\'' || s[0] == '"') {
+    p = strchr(s + 1, s[0]);
+  } else {
+    p = s;
+  }
+
+  /* Now we just need to check that it is not a private
+   * message. If we encounter a space then the preceeding
+   * text could not have been a user/player name (the
+   * quote check above eliminated names with spaces) so
+   * it must be a public message. Otherwise if we encounter
+   * the message prefix : then the text parsed up until now
+   * was a player/user name and the line is intended as
+   * a private message (or explicit public message if the
+   * first character is :). */
+  while (p != NULL && *p != '\0') {
+    if (my_isspace(*p)) {
+      return TRUE;
+    } else if (*p == MESSAGE_PREFIX) {
+      return FALSE;
+    }
+    p++;
+  }
+  return TRUE;
+}
+
 
 /**************************************************************************
 ...
@@ -49,7 +99,14 @@ void inputline_return(GtkEntry *w, gpointer data)
   theinput = gtk_entry_get_text(w);
   
   if (*theinput) {
-    send_chat(theinput);
+    if (client_state() == C_S_RUNNING && allied_chat_only
+        && is_plain_public_message(theinput)) {
+      char buf[MAX_LEN_MSG];
+      my_snprintf(buf, sizeof(buf), ". %s", theinput);
+      send_chat(buf);
+    } else {
+      send_chat(theinput);
+    }
 
     if (genlist_size(history_list) >= MAX_CHATLINE_HISTORY) {
       void *data;
diff --git a/client/gui-gtk-2.0/gui_main.c b/client/gui-gtk-2.0/gui_main.c
index 33d13fe..f010564 100644
--- a/client/gui-gtk-2.0/gui_main.c
+++ b/client/gui-gtk-2.0/gui_main.c
@@ -55,6 +55,7 @@
 #include "clinet.h"
 #include "colors.h"
 #include "connectdlg.h"
+#include "connectdlg_common.h"
 #include "control.h"
 #include "cma_fe.h"
 #include "dialogs.h"
@@ -99,6 +100,7 @@ bool split_bottom_notebook = FALSE;
 bool new_messages_go_to_top = FALSE;
 bool show_message_window_buttons = TRUE;
 bool metaserver_tab_first = FALSE;
+bool allied_chat_only = FALSE;
 
 GtkWidget *toplevel;
 GdkWindow *root_window;
@@ -153,6 +155,7 @@ const char * const gui_character_encoding = "UTF-8";
 const bool gui_use_transliteration = FALSE;
 
 static void split_bottom_notebook_callback(struct client_option *op);
+static void allied_chat_only_callback(struct client_option *op);
 
 client_option gui_options[] = {
   /* This option is the same as the one in gui-gtk */
@@ -222,7 +225,20 @@ client_option gui_options[] = {
                   N_("If this option is enabled, the metaserver tab will "
                      "be the first notebook tab in the network page. This "
                      "option requires a restart in order to take effect."),
-                  COC_NETWORK)
+                  COC_NETWORK),
+  GEN_BOOL_OPTION_CB(allied_chat_only,
+                     N_("Plain chat messages are sent to allies only"),
+                     N_("If this option is enabled, then plain messages "
+                        "typed into the chat entry while the game is "
+                        "running will only be sent to your allies. "
+                        "Otherwise plain messages will be sent as "
+                        "public chat messages. To send a public chat "
+                        "message with this option enabled, prefix the "
+                        "message with a single colon ':'. This option "
+                        "can also be set using a toggle button beside "
+                        "the chat entry (only visible in multiplayer "
+                        "games)."),
+                     COC_NETWORK, allied_chat_only_callback)
 };
 const int num_gui_options = ARRAY_SIZE(gui_options);
 
@@ -240,6 +256,7 @@ static int unit_ids[MAX_NUM_UNITS_BELOW];  /* ids of the units icons in
 GtkTextView *main_message_area;
 GtkTextBuffer *message_buffer;
 static GtkWidget *inputline;
+static GtkWidget *allied_chat_toggle_button;
 
 static enum Display_color_type display_color_type;  /* practically unused */
 static gint timer_id;                               /*       ditto        */
@@ -268,6 +285,8 @@ static gboolean select_unit_pixmap_callback(GtkWidget *w, GdkEvent *ev,
 					    gpointer data);
 static gboolean quit_dialog_callback(void);
 
+static void allied_chat_button_toggled(GtkToggleButton *button,
+                                       gpointer user_data);
 
 /****************************************************************************
   Called by the tileset code to set the font size that should be used to
@@ -978,6 +997,7 @@ static void setup_widgets(void)
 {
   GtkWidget *box, *ebox, *hbox, *sbox, *align, *label;
   GtkWidget *frame, *table, *table2, *paned, *hpaned, *sw, *text;
+  GtkWidget *button;
   int i;
   char buf[256];
   struct sprite *sprite;
@@ -1333,13 +1353,24 @@ static void setup_widgets(void)
       " menu.\nNow.. Go give'em hell!") );
 
   /* the chat line */
-  inputline = gtk_entry_new();
-  gtk_box_pack_start(GTK_BOX(vbox), inputline, FALSE, FALSE, 3);
+  hbox = gtk_hbox_new(FALSE, 4);
+  gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 3);
 
+  inputline = gtk_entry_new();
   g_signal_connect(inputline, "activate",
 		   G_CALLBACK(inputline_return), NULL);
   g_signal_connect(inputline, "key_press_event",
                    G_CALLBACK(inputline_handler), NULL);
+  gtk_box_pack_start(GTK_BOX(hbox), inputline, TRUE, TRUE, 0);
+
+  button = gtk_toggle_button_new_with_label(_("Allies Only"));
+  gtk_button_set_focus_on_click(GTK_BUTTON(button), FALSE);
+  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button),
+                               allied_chat_only);
+  g_signal_connect(button, "toggled",
+                   G_CALLBACK(allied_chat_button_toggled), NULL);
+  gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 2);
+  allied_chat_toggle_button = button;
 
   /* Other things to take care of */
 
@@ -2075,3 +2106,50 @@ static void split_bottom_notebook_callback(struct client_option *op)
   }
   popup_meswin_dialog(FALSE);
 }
+
+/****************************************************************************
+  Option callback for the 'allied_chat_only' option. This updates the state
+  of the associated toggle button.
+****************************************************************************/
+static void allied_chat_only_callback(struct client_option *op)
+{
+  GtkWidget *button;
+
+  button = allied_chat_toggle_button;
+  g_return_if_fail(button != NULL);
+  g_return_if_fail(GTK_IS_TOGGLE_BUTTON(button));
+
+  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button),
+                               *op->p_bool_value);
+}
+
+/**************************************************************************
+  Set the chatline buttons to reflect the state of the game and current
+  client options. This function should be called on game start.
+**************************************************************************/
+void refresh_chat_buttons(void)
+{
+  GtkWidget *button;
+
+  button = allied_chat_toggle_button;
+  g_return_if_fail(button != NULL);
+  g_return_if_fail(GTK_IS_TOGGLE_BUTTON(button));
+
+  /* Hide the "Allies Only" button for local games. */
+  if (is_server_running()) {
+    gtk_widget_hide(button);
+  } else {
+    gtk_widget_show(button);
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button),
+                                 allied_chat_only);
+  }
+}
+
+/**************************************************************************
+  Handle a toggle of the "Allies Only" chat button.
+**************************************************************************/
+static void allied_chat_button_toggled(GtkToggleButton *button,
+                                       gpointer user_data)
+{
+  allied_chat_only = gtk_toggle_button_get_active(button);
+}
diff --git a/client/gui-gtk-2.0/gui_main.h b/client/gui-gtk-2.0/gui_main.h
index bc03bc7..7bc7071 100644
--- a/client/gui-gtk-2.0/gui_main.h
+++ b/client/gui-gtk-2.0/gui_main.h
@@ -31,6 +31,7 @@ extern bool split_bottom_notebook;
 extern bool new_messages_go_to_top;
 extern bool show_message_window_buttons;
 extern bool metaserver_tab_first;
+extern bool allied_chat_only;
 
 extern GdkGC *          civ_gc;
 extern GdkGC *          mask_fg_gc;
@@ -89,6 +90,7 @@ gboolean inputline_handler(GtkWidget *w, GdkEventKey *ev);
 
 void reset_unit_table(void);
 void popup_quit_dialog(void);
+void refresh_chat_buttons(void);
 
 /* There simply is no proper header to place this define. Creating one
  * just for this seems overkill. */
diff --git a/client/gui-gtk-2.0/pages.c b/client/gui-gtk-2.0/pages.c
index 19eae83..c047bce 100644
--- a/client/gui-gtk-2.0/pages.c
+++ b/client/gui-gtk-2.0/pages.c
@@ -2316,6 +2316,7 @@ void set_client_page(enum client_pages page)
     break;
   case PAGE_GAME:
     chatline_scroll_to_bottom();
+    refresh_chat_buttons();
     break;
   case PAGE_NETWORK:
     update_network_lists();
_______________________________________________
Freeciv-dev mailing list
Freeciv-dev@gna.org
https://mail.gna.org/listinfo/freeciv-dev

Reply via email to