From 107837f5445d02a2ea3eac3fc1dda08fe7a6e721 Mon Sep 17 00:00:00 2001 From: Daniel Carbonell Fraj Date: Fri, 14 Feb 2003 19:05:43 +0000 Subject: [PATCH] added the channel list widget --- ChangeLog | 31 ++++++++ src/client.c | 32 +++++--- src/gtetrinet.h | 4 +- src/partyline.c | 198 ++++++++++++++++++++++++++++++++++++++++++++---- src/partyline.h | 7 +- src/tetrinet.c | 64 +++++++++++++++- src/tetrinet.h | 3 + 7 files changed, 307 insertions(+), 32 deletions(-) diff --git a/ChangeLog b/ChangeLog index 39f609a..b7f7921 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,34 @@ +2003-02-14 Dani Carbonell + + * src/client.c: a few fixes in error handling. + + * src/gtetrinet.h: fixed a couple of function declarations. + + * src/partyline.c: added the channel_list widget. + (partyline_page_new): added the new widget. + (partyline_add_channel): new function that will add a channel to the + list of available channels. + (copy_item): function that will copy a item of the work_model to the + actual model of the tree_view. + (stop_list): new function that will be called then all the markers + are sended back to us. + (partyline_update_list): function to be called if you want to update + the channel list. + (partyline_more_channel_lines): new function that will be called if + there are more channels waiting to be listed. + (partyline_clear_list_channel): new function that will clear the + list channel. + + * src/partyline.h: added the new function declarations. + + * src/tetrinet.c: install the channel list refresh signal, and parse + the incoming messages when appropiate. + (tetrinet_inmessage): install the signal when connecting. Refresh + the channel list when the user completes the connection protocol. + Parse the system messages if appropiate. + + * src/tetrinet.h: added new variable declaration. + 2003-02-11 Dani Carbonell * src/gtetrinet.c (main): fixed a bug when quitting GTetrinet when diff --git a/src/client.c b/src/client.c index 996bdaa..f8bb672 100644 --- a/src/client.c +++ b/src/client.c @@ -397,17 +397,21 @@ io_channel_cb (GIOChannel *source, GIOCondition condition) case G_IO_IN : { if (client_readmsg (&buf) < 0) - g_warning ("client_readmsg returned -1\n"); - - if (strlen (buf)) client_inmessage (buf); - - if (strncmp ("noconnecting", buf, 12) == 0) { - connected = 1; /* so we can disconnect :) */ + g_warning ("client_readmsg failed, aborting connection\n"); client_disconnect (); } - - g_free (buf); + else + { + if (strlen (buf)) client_inmessage (buf); + + if (strncmp ("noconnecting", buf, 12) == 0) + { + connected = 1; /* so we can disconnect :) */ + client_disconnect (); + } + g_free (buf); + } }; break; default : break; } @@ -443,13 +447,19 @@ int client_readmsg (gchar **str) switch (g_io_channel_read_chars (io_channel, &buf[i], 1, &bytes, &error)) { case G_IO_STATUS_EOF : - g_warning ("End of file."); break; + g_warning ("End of file."); + return -1; + break; case G_IO_STATUS_AGAIN : - g_warning ("Resource temporarily unavailable."); break; + g_warning ("Resource temporarily unavailable."); + return -1; + break; case G_IO_STATUS_ERROR : - g_warning ("Error"); break; + g_warning ("Error"); + return -1; + break; case G_IO_STATUS_NORMAL : if (error != NULL) diff --git a/src/gtetrinet.h b/src/gtetrinet.h index 581a8f1..32897c4 100644 --- a/src/gtetrinet.h +++ b/src/gtetrinet.h @@ -15,5 +15,5 @@ extern gint keyrelease (GtkWidget *widget, GdkEventKey *key); extern void move_current_page_to_window (void); extern void show_fields_page (void); extern void show_partyline_page (void); -void unblock_keyboard_signal (void); -gint get_current_notebook_page (void); +extern void unblock_keyboard_signal (void); +extern gint get_current_notebook_page (void); diff --git a/src/partyline.c b/src/partyline.c index b059ff1..fb41762 100644 --- a/src/partyline.c +++ b/src/partyline.c @@ -35,11 +35,15 @@ /* widgets that we have to do stuff with */ static GtkWidget *playerlist, *textbox, *entrybox, - *namelabel, *teamlabel, *infolabel, *textboxscroll, *playerlist_scroll; + *namelabel, *teamlabel, *infolabel, *textboxscroll, + *playerlist_scroll, *playerlist_vpaned, *channel_box, + *playerlist_channel_scroll; /* some more widgets for layout */ static GtkWidget *table, *leftbox, *rightbox; +static GtkListStore *work_model, *playerlist_channels; + /* stuff for pline history */ #define PLHSIZE 64 char plhistory[PLHSIZE][256]; @@ -56,6 +60,9 @@ GtkWidget *partyline_page_new (void) GtkCellRenderer *renderer = gtk_cell_renderer_text_new (); GList *focus_list = NULL; + playerlist_channels = gtk_list_store_new (5, G_TYPE_INT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); + work_model = gtk_list_store_new (5, G_TYPE_INT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); + /* left box */ leftbox = gtk_vbox_new (FALSE, 4); /* chat thingy */ @@ -64,14 +71,36 @@ GtkWidget *partyline_page_new (void) gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW(textbox), GTK_WRAP_WORD); gtk_text_view_set_editable (GTK_TEXT_VIEW (textbox), FALSE); gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (textbox), FALSE); - gtk_widget_show (textbox); textboxscroll = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(textboxscroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS); gtk_container_add (GTK_CONTAINER(textboxscroll), textbox); - gtk_widget_show(textboxscroll); - gtk_box_pack_start (GTK_BOX(leftbox), textboxscroll, TRUE, TRUE, 0); + + /* channel list */ + channel_box = GTK_WIDGET (gtk_tree_view_new_with_model (GTK_TREE_MODEL (playerlist_channels))); + gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (channel_box), -1, _("Name"), renderer, + "text", 1, NULL); + gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (channel_box), -1, _("Players"), renderer, + "text", 2, NULL); + gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (channel_box), -1, _("State"), renderer, + "text", 3, NULL); + gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (channel_box), -1, _("Description"), renderer, + "text", 4, NULL); + playerlist_channel_scroll = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW (playerlist_channel_scroll), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + gtk_container_add (GTK_CONTAINER(playerlist_channel_scroll), channel_box); + gtk_widget_set_size_request (playerlist_channel_scroll, -1, 100); + + /* vpaned widget */ + playerlist_vpaned = gtk_vpaned_new (); + + gtk_paned_add1 (GTK_PANED (playerlist_vpaned), playerlist_channel_scroll); + gtk_paned_add2 (GTK_PANED (playerlist_vpaned), textboxscroll); + gtk_box_pack_start (GTK_BOX(leftbox), playerlist_vpaned, TRUE, TRUE, 0); + /* entry box */ entrybox = gtk_entry_new (); gtk_entry_set_max_length (GTK_ENTRY (entrybox), 200); @@ -79,9 +108,8 @@ GtkWidget *partyline_page_new (void) GTK_SIGNAL_FUNC(textentry), NULL); g_signal_connect (G_OBJECT(entrybox), "key-press-event", GTK_SIGNAL_FUNC(entrykey), NULL); - gtk_widget_show (entrybox); gtk_box_pack_start (GTK_BOX(leftbox), entrybox, FALSE, FALSE, 0); - gtk_widget_show (leftbox); + gtk_widget_show_all (leftbox); /* player list with scrollbar */ playerlist = GTK_WIDGET (gtk_tree_view_new_with_model (GTK_TREE_MODEL (playerlist_model))); @@ -91,44 +119,37 @@ GtkWidget *partyline_page_new (void) "text", 1, NULL); gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (playerlist), -1, _("Team"), renderer, "text", 2, NULL); - gtk_widget_show (playerlist); playerlist_scroll = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW (playerlist_scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_NEVER); gtk_container_add (GTK_CONTAINER(playerlist_scroll), playerlist); gtk_widget_set_size_request (playerlist_scroll, 150, 200); - gtk_widget_show (playerlist_scroll); + gtk_widget_show_all (playerlist_scroll); /* right box */ box = gtk_vbox_new (FALSE, 2); widget = leftlabel_new (_("Your name:")); - gtk_widget_show (widget); gtk_box_pack_start (GTK_BOX(box), widget, FALSE, FALSE, 0); namelabel = gtk_label_new (""); gtk_widget_show (namelabel); gtk_box_pack_start (GTK_BOX(box), namelabel, FALSE, FALSE, 0); widget = gtk_vseparator_new (); /* invisible... just needed some space */ - gtk_widget_show (widget); gtk_box_pack_start (GTK_BOX(box), widget, FALSE, FALSE, 2); widget = leftlabel_new (_("Your team:")); - gtk_widget_show (widget); gtk_box_pack_start (GTK_BOX(box), widget, FALSE, FALSE, 0); teamlabel = gtk_label_new (""); - gtk_widget_show (teamlabel); gtk_box_pack_start (GTK_BOX(box), teamlabel, FALSE, FALSE, 0); infolabel = gtk_label_new (""); gtk_label_set_line_wrap (GTK_LABEL(teamlabel), TRUE); - gtk_widget_show (infolabel); gtk_box_pack_start (GTK_BOX(box), infolabel, TRUE, FALSE, 0); gtk_container_set_border_width (GTK_CONTAINER(box), 4); - gtk_widget_show (box); rightbox = gtk_frame_new (NULL); gtk_frame_set_shadow_type (GTK_FRAME(rightbox), GTK_SHADOW_IN); gtk_container_add (GTK_CONTAINER(rightbox), box); - gtk_widget_show (rightbox); + gtk_widget_show_all (rightbox); /* stuff all the boxes into the table */ table = gtk_table_new (2, 2, FALSE); @@ -332,3 +353,150 @@ static gint entrykey (GtkWidget *widget, GdkEventKey *key) return FALSE; } } + +void partyline_add_channel (gchar *line) +{ + GScanner *scan; + gint num, actual, max; + gchar *name, *players, *state, final[1024], *desc, *utf8; + GtkTreeIter iter; + + scan = g_scanner_new (NULL); + g_scanner_input_text (scan, line, strlen (line)); + + /* we'll use single line comments to parse the channel name */ + scan->config->cpair_comment_single = "#["; + scan->config->skip_comment_single = FALSE; + + while ((g_scanner_get_next_token (scan) != G_TOKEN_INT) && !g_scanner_eof (scan)); + num = scan->value.v_int; + + while ((g_scanner_get_next_token (scan) != G_TOKEN_COMMENT_SINGLE) && !g_scanner_eof (scan)); + utf8 = g_locale_to_utf8 (scan->value.v_comment, -1, NULL, NULL, NULL); + name = g_strconcat ("#", utf8, NULL); + + while ((g_scanner_get_next_token (scan) != G_TOKEN_IDENTIFIER) && !g_scanner_eof (scan)); + players = g_strdup (scan->value.v_identifier); + + if (strncmp (players, "FULL", 4)) + { + while ((g_scanner_get_next_token (scan) != G_TOKEN_INT) && !g_scanner_eof (scan)); + actual = scan->value.v_int; + + while ((g_scanner_get_next_token (scan) != G_TOKEN_INT) && !g_scanner_eof (scan)); + max = scan->value.v_int; + + g_snprintf (final, 1024, "%d/%d %s", actual, max, players); + } + else + g_snprintf (final, 1024, "6/6 %s", players); + + g_scanner_get_next_token (scan); /* dump the ')' */ + + if (g_scanner_get_next_token (scan) == G_TOKEN_LEFT_CURLY) + { + g_scanner_get_next_token (scan); + state = g_strdup (scan->value.v_identifier); + } + else + state = g_strdup ("IDLE"); + + while ((g_scanner_get_next_token (scan) != G_TOKEN_RIGHT_PAREN) && !g_scanner_eof (scan)); + if (line[scan->position] != 0) + desc = g_strstrip (g_locale_to_utf8 (&line[scan->position], -1, NULL, NULL, NULL)); + else + desc = g_strdup (""); + + + gtk_list_store_append (work_model, &iter); + gtk_list_store_set (work_model, &iter, + 0, num, + 1, name, + 2, final, + 3, state, + 4, desc, + -1); + + g_scanner_destroy (scan); + g_free (name); + g_free (state); + g_free (players); + g_free (desc); + g_free (utf8); +} + +gboolean copy_item (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter) +{ + gint num; + gchar *name, *players, *state, *desc; + GtkTreeIter iter2; + + path = path; + + gtk_tree_model_get (model, iter, + 0, &num, + 1, &name, + 2, &players, + 3, &state, + 4, &desc, -1); + + gtk_list_store_append (playerlist_channels, &iter2); + gtk_list_store_set (playerlist_channels, &iter2, + 0, num, + 1, name, + 2, players, + 3, state, + 4, desc, + -1); + + g_free (players); + g_free (name); + g_free (state); + g_free (desc); + + return FALSE; +} + +void stop_list (void) +{ + list_issued = FALSE; + + /* update the channel list widget, with some sort of "double buffering" */ + gtk_tree_view_set_model (GTK_TREE_VIEW (channel_box), GTK_TREE_MODEL (work_model)); + gtk_list_store_clear (playerlist_channels); + gtk_tree_model_foreach (GTK_TREE_MODEL (work_model), (GtkTreeModelForeachFunc) copy_item, NULL); + gtk_tree_view_set_model (GTK_TREE_VIEW (channel_box), GTK_TREE_MODEL (playerlist_channels)); +} + +gboolean partyline_update_channel_list (void) +{ + gchar cad[1024]; + + list_issued++; + gtk_list_store_clear (work_model); + tetrinet_playerline ("/list"); + + /* send the mark */ + g_snprintf (cad, 1024, "/msg %d --- MARK ---", playernum); + tetrinet_playerline (cad); + + return TRUE; +} + +void partyline_more_channel_lines (void) +{ + gchar cad[1024]; + + list_issued ++; + tetrinet_playerline ("/list+"); + g_snprintf (cad, 1024, "/msg %d --- MARK ---", playernum); + tetrinet_playerline (cad); +} + +void partyline_clear_list_channel (void) +{ + gtk_list_store_clear (playerlist_channels); + gtk_list_store_clear (work_model); +} diff --git a/src/partyline.h b/src/partyline.h index 1f14ef3..6e41fff 100644 --- a/src/partyline.h +++ b/src/partyline.h @@ -7,4 +7,9 @@ extern void partyline_text (char *text); extern void partyline_fmt (const char *text, ...) G_GNUC_PRINTF (1, 2); extern void partyline_playerlist (int *numbers, char **names, char **teams, int n, char **specs, int sn); extern void partyline_entryfocus (void); -void partyline_switch_entryfocus (void); +extern void partyline_add_channel (gchar *line); +extern gboolean partyline_update_channel_list (void); +extern void partyline_more_channel_lines (void); +extern void partyline_switch_entryfocus (void); +extern void partyline_clear_list_channel (void); +extern void stop_list (void); diff --git a/src/tetrinet.c b/src/tetrinet.c index cd46e5d..23a02c5 100644 --- a/src/tetrinet.c +++ b/src/tetrinet.c @@ -68,6 +68,11 @@ int spectatorcount = 0; char specialblocks[256]; int specialblocknum = 0; +/* + * this will have the number of /list commands sended and waiting for answer + */ +gint list_issued; + FIELD fields[7]; int moderator; /* are we the moderator ? TRUE : FALSE */ @@ -133,6 +138,8 @@ struct sb sbinfo[] = { {0, 0, 0} }; +static guint up_chan_list_source; + static void tetrinet_updatelevels (void); static void tetrinet_setspeciallabel (int sb); static void tetrinet_dospecial (int from, int to, int type); @@ -175,7 +182,8 @@ void tetrinet_inmessage (enum inmsg_type msgtype, char *data) /* process the message */ switch (msgtype) { case IN_CONNECT: - /* do nothing - setup is done with the playernum message */ + list_issued = 0; + up_chan_list_source = g_timeout_add (30000, (GSourceFunc) partyline_update_channel_list, NULL); break; case IN_DISCONNECT: if (!connected) { @@ -195,11 +203,13 @@ void tetrinet_inmessage (enum inmsg_type msgtype, char *data) playernames[i][0] = teamnames[i][0] = 0; fieldslabelupdate (); } + g_source_remove (up_chan_list_source); winlist_clear (); fields_attdefclear (); fields_gmsgclear (); partyline_fmt (_("%c%c*** Disconnected from server"), TETRI_TB_C_DARK_GREEN, TETRI_TB_BOLD); + partyline_clear_list_channel (); break; case IN_CONNECTERROR: connecterror: @@ -265,6 +275,7 @@ void tetrinet_inmessage (enum inmsg_type msgtype, char *data) checkmoderatorstatus (); partylineupdate_join (nick); partylineupdate_team (nick, team); + partyline_update_channel_list (); } } /* show partyline on successful connect */ @@ -391,13 +402,60 @@ void tetrinet_inmessage (enum inmsg_type msgtype, char *data) tetrix = TRUE; } else if (tetrix) { + if (list_issued) + { + gchar *line = nocolor (token); + + if (*line == '(') + { + partyline_add_channel (line); + break; + } + + if (!strncmp ("List", line, 4)) + { + partyline_more_channel_lines (); + break; + } + + if (!strncmp ("TetriNET", line, 8)) + break; + + if (!strncmp ("You", line, 3)) + { + /* we will use the error message as list stopper */ + list_issued--; + if (list_issued <= 0) + stop_list(); + break; + } + + //if (line != NULL) g_free (line); + } + g_snprintf (buf, sizeof(buf), "*** %s", token); partyline_text (buf); break; } - else plinemsg ("Server", token); + else + plinemsg ("Server", token); + } + else + { + if (pnum == playernum) + { + gchar *line = nocolor (token); + if (!strncmp (line, "(msg) --- MARK ---", 18)) + { + list_issued--; + if (list_issued <= 0) + stop_list(); + } + //g_free (line); + } + else + plinemsg (playernames[pnum], token); } - else plinemsg (playernames[pnum], token); } break; case IN_PLINEACT: diff --git a/src/tetrinet.h b/src/tetrinet.h index 991fbe1..5faa5fa 100644 --- a/src/tetrinet.h +++ b/src/tetrinet.h @@ -1,3 +1,5 @@ +#include + #define FIELDWIDTH 12 #define FIELDHEIGHT 22 @@ -13,6 +15,7 @@ extern int gmsgstate; extern char specialblocks[256]; extern int specialblocknum; +extern gint list_issued; extern void tetrinet_inmessage (enum inmsg_type msgtype, char *data); extern void tetrinet_playerline (const char *text); -- 2.50.1