#include #include "gm-mcp-package.h" #include "gm-mcp-userlist-view.h" #include "gm-iuserlist.h" #include "widgets/gm-world-view.h" #include "gm-pixbuf.h" #include "gm-debug.h" #define GM_USERLIST_ICON_SIZE 22 enum { GM_USERLIST_ICON, GM_USERLIST_NAME, GM_USERLIST_ID, GM_USERLIST_SORT, GM_USERLIST_N_COLUMNS }; typedef struct _GmMcpUserlistView { GmWorldView *view; GmMcpPackage *package; GtkTreeModel *model; GtkListStore *store; GtkTreeView *tree_view; GtkScrolledWindow *scrolled_window; GtkWidget *popup_menu; gboolean initializing; } GmMcpUserlistView; static void on_gm_mcp_userlist_view_weak_notify(gpointer data, GObject *obj); static void on_gm_mcp_userlist_view_player_added(GmMcpPackage *package, gint id, gchar const *icon, gchar const *sort, GmMcpUserlistView *view); static void on_gm_mcp_userlist_view_player_removed(GmMcpPackage *package, gint id, GmMcpUserlistView *view); static void on_gm_mcp_userlist_view_name_changed(GmMcpPackage *package, gint id, gchar const *sort, GmMcpUserlistView *view); static void on_gm_mcp_userlist_view_rank_changed(GmMcpPackage *package, gint id, gchar const *icon, gchar const *sort, GmMcpUserlistView *view); static void on_gm_mcp_userlist_view_state_changed(GmMcpPackage *package, gint id, gchar const *icon, gchar const *sort, GmMcpUserlistView *view); static gboolean on_gm_mcp_userlist_view_popup_menu(GtkWidget *widget, GmMcpUserlistView *view); static gboolean on_gm_mcp_userlist_view_button_press(GtkWidget *widget, GdkEventButton *event, GmMcpUserlistView *view); GtkTreeModel * gm_mcp_userlist_view_model_create(GmMcpUserlistView *view) { GtkListStore *store = gtk_list_store_new(GM_USERLIST_N_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING); GtkTreeModel *model = gtk_tree_model_sort_new_with_model(GTK_TREE_MODEL( store)); view->store = store; view->model = model; return model; } void on_gm_mcp_userlist_view_style_set(GtkWidget *widget, GtkStyle *prev, GmMcpUserlistView *view) { pango_font_description_set_size(widget->style->font_desc, 8 * PANGO_SCALE); } GtkWidget * gm_mcp_userlist_view_create_userlist(GmMcpUserlistView *view) { GtkWidget *scrolled_window = gtk_scrolled_window_new(NULL, NULL); GtkTreeModel *model = gm_mcp_userlist_view_model_create(view); GtkWidget *tree_view = gtk_tree_view_new_with_model(model); GtkCellRenderer *renderer; GtkTreeViewColumn *column; on_gm_mcp_userlist_view_style_set(tree_view, NULL, view); g_signal_connect(tree_view, "style-set", G_CALLBACK(on_gm_mcp_userlist_view_style_set), view); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled_window), GTK_SHADOW_IN); gtk_widget_set_size_request(scrolled_window, 150, -1); gtk_widget_set_size_request(tree_view, 150, -1); gtk_container_add(GTK_CONTAINER(scrolled_window), tree_view); renderer = gtk_cell_renderer_pixbuf_new(); column = gtk_tree_view_column_new_with_attributes(_("I"), renderer, "pixbuf", GM_USERLIST_ICON, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column); gtk_tree_view_column_set_min_width(column, 30); renderer = gtk_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes(_("Name"), renderer, "markup", GM_USERLIST_NAME, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column); gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(model), GM_USERLIST_SORT, GTK_SORT_ASCENDING); GTK_WIDGET_UNSET_FLAGS(GTK_WIDGET(tree_view), GTK_CAN_FOCUS); gtk_widget_modify_base(GTK_WIDGET(tree_view), GTK_STATE_ACTIVE, &(GTK_WIDGET(tree_view)->style->base[GTK_STATE_SELECTED])); gtk_widget_modify_text(GTK_WIDGET(tree_view), GTK_STATE_ACTIVE, &(GTK_WIDGET(tree_view)->style->text[GTK_STATE_SELECTED])); gtk_tree_view_columns_autosize(GTK_TREE_VIEW(tree_view)); gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tree_view), FALSE); view->tree_view = GTK_TREE_VIEW(tree_view); view->scrolled_window = GTK_SCROLLED_WINDOW(scrolled_window); return scrolled_window; } void gm_mcp_userlist_view_new(GmMcpPackage *package, GObject *parent) { GmMcpUserlistView *view; GtkPaned *paned; GtkWidget *vbox, *child; if (!GM_IS_WORLD_VIEW(parent)) { return; } view = g_new0(GmMcpUserlistView, 1); view->view = GM_WORLD_VIEW(parent); view->package = package; paned = GTK_PANED(gm_world_view_hpaned(view->view)); vbox = gtk_paned_get_child2(paned); if (vbox == NULL) { vbox = gtk_vbox_new(FALSE, 6); gtk_paned_pack2(paned, vbox, FALSE, TRUE); } child = gm_mcp_userlist_view_create_userlist(view); gtk_box_pack_end(GTK_BOX(vbox), child, TRUE, TRUE, 0); gtk_widget_show_all(vbox); g_signal_connect(view->tree_view, "popup-menu", G_CALLBACK(on_gm_mcp_userlist_view_popup_menu), view); g_signal_connect(view->tree_view, "button-press-event", G_CALLBACK(on_gm_mcp_userlist_view_button_press), view); g_signal_connect(package, "player_added", G_CALLBACK(on_gm_mcp_userlist_view_player_added), view); g_signal_connect(package, "player_removed", G_CALLBACK(on_gm_mcp_userlist_view_player_removed), view); g_signal_connect(package, "name_changed", G_CALLBACK(on_gm_mcp_userlist_view_name_changed), view); g_signal_connect(package, "state_changed", G_CALLBACK(on_gm_mcp_userlist_view_state_changed), view); g_signal_connect(package, "rank_changed", G_CALLBACK(on_gm_mcp_userlist_view_rank_changed), view); g_object_weak_ref(G_OBJECT(package), on_gm_mcp_userlist_view_weak_notify, view); } /* Callbacks */ static void on_gm_mcp_userlist_view_weak_notify(gpointer data, GObject *obj) { GmMcpUserlistView *view = (GmMcpUserlistView *)(data); GtkWidget *vbox; GList *children; if (GM_IS_WORLD_VIEW(view->view)) { gtk_widget_destroy(GTK_WIDGET(view->scrolled_window)); vbox = gtk_paned_get_child2(GTK_PANED(gm_world_view_hpaned( view->view))); if (vbox != NULL) { children = gtk_container_get_children(GTK_CONTAINER(vbox)); if (children == NULL) { gtk_widget_destroy(vbox); } g_list_free(children); } } g_free(data); } static gboolean gm_mcp_userlist_view_find(GmMcpUserlistView *view, gint id, GtkTreeIter *iter) { GtkTreeModel *model = GTK_TREE_MODEL(view->store); gint iterid; if (gtk_tree_model_get_iter_first(model, iter)) { do { gtk_tree_model_get(model, iter, GM_USERLIST_ID, &iterid, -1); if (iterid == id) { return TRUE; } } while (gtk_tree_model_iter_next(model, iter)); } return FALSE; } static gchar * gm_mcp_userlist_view_get_name(GmMcpUserlistView *view, GmMcpPackage *package, gint id) { gchar const *name; gchar *status; gchar *escape; gchar *result; gchar *color; GdkColor col; name = gm_iuserlist_get_name(GM_IUSERLIST(package), id); if (!gm_iuserlist_supports_status(GM_IUSERLIST(package))) { return g_strdup(name); } else { status = gm_iuserlist_get_status(GM_IUSERLIST(package), id); if (status) { col = GTK_WIDGET(view->tree_view)->style->text[GTK_STATE_INSENSITIVE]; color = g_strdup_printf("#%04X%04X%04X", col.red, col.green, col.blue); escape = g_markup_escape_text(status, -1); result = g_strconcat(name, "\n", escape, "", NULL); g_free(color); g_free(status); g_free(escape); } else { result = g_strdup(name); } return result; } } static void on_gm_mcp_userlist_view_player_added(GmMcpPackage *package, gint id, gchar const *icon, gchar const *sort, GmMcpUserlistView *view) { GtkTreeIter iter; gchar *name; if (gm_mcp_userlist_view_find(view, id, &iter)) { gm_debug_msg(DEBUG_MCP, "GmMcpUserlistView.OnPlayerAdded: player %d " "is already in the list, can't be added twice!", id); return; } name = gm_mcp_userlist_view_get_name(view, package, id); gtk_list_store_append(view->store, &iter); gtk_list_store_set(view->store, &iter, GM_USERLIST_ID, id, GM_USERLIST_ICON, gm_pixbuf_get_at_size(icon, GM_USERLIST_ICON_SIZE, GM_USERLIST_ICON_SIZE), GM_USERLIST_NAME, name, GM_USERLIST_SORT, sort, -1); g_free(name); } static void on_gm_mcp_userlist_view_player_removed(GmMcpPackage *package, gint id, GmMcpUserlistView *view) { GtkTreeIter iter; if (!gm_mcp_userlist_view_find(view, id, &iter)) { gm_debug_msg(DEBUG_MCP, "GmMcpUserlistView.OnPlayerRemoved: player %d " "is not in the list, can't be removed!", id); return; } gtk_list_store_remove(view->store, &iter); } static void on_gm_mcp_userlist_view_name_changed(GmMcpPackage *package, gint id, gchar const *sort, GmMcpUserlistView *view) { GtkTreeIter iter; gchar *name; if (!gm_mcp_userlist_view_find(view, id, &iter)) { gm_debug_msg(DEBUG_MCP, "GmMcpUserlistView.OnNameChanged: player %d " "is not in the list!", id); return; } name = gm_mcp_userlist_view_get_name(view, package, id); gtk_list_store_set(view->store, &iter, GM_USERLIST_NAME, name, -1); g_free(name); if (sort) { gtk_list_store_set(view->store, &iter, GM_USERLIST_SORT, sort, -1); } } static void on_gm_mcp_userlist_view_rank_changed(GmMcpPackage *package, gint id, gchar const *icon, gchar const *sort, GmMcpUserlistView *view) { GtkTreeIter iter; if (!gm_mcp_userlist_view_find(view, id, &iter)) { gm_debug_msg(DEBUG_MCP, "GmMcpUserlistView.OnRankChanged: player %d " "is not in the list!", id); return; } gtk_list_store_set(view->store, &iter, GM_USERLIST_ICON, gm_pixbuf_get_at_size(icon, GM_USERLIST_ICON_SIZE, GM_USERLIST_ICON_SIZE), -1); if (sort) { gtk_list_store_set(view->store, &iter, GM_USERLIST_SORT, sort, -1); } } static void on_gm_mcp_userlist_view_state_changed(GmMcpPackage *package, gint id, gchar const *icon, gchar const *sort, GmMcpUserlistView *view) { GtkTreeIter iter; gchar *name; if (!gm_mcp_userlist_view_find(view, id, &iter)) { gm_debug_msg(DEBUG_MCP, "GmMcpUserlistView.OnStateChanged: player %d " "is not in the list!", id); return; } name = gm_mcp_userlist_view_get_name(view, package, id); gtk_list_store_set(view->store, &iter, GM_USERLIST_ICON, gm_pixbuf_get_at_size(icon, GM_USERLIST_ICON_SIZE, GM_USERLIST_ICON_SIZE), GM_USERLIST_NAME, name, -1); g_free(name); if (sort) { gtk_list_store_set(view->store, &iter, GM_USERLIST_SORT, sort, -1); } } static void popup_menu_detach(GtkWidget *attach_widget, GtkMenu *menu) { GmMcpUserlistView *view = (GmMcpUserlistView *)g_object_get_data( G_OBJECT(menu), "McpUserlistView"); view->popup_menu = NULL; } static void on_gm_mcp_userlist_view_menuitem_activate(GtkMenuItem *item, GmMcpUserlistView *view) { gchar *action = (gchar *)g_object_get_data(G_OBJECT(item), "UserlistAction"); if (action) { gm_world_sendln(GM_MCP_SESSION_WORLD(GM_MCP_PACKAGE_SESSION( view->package)), action); } } static gboolean gm_mcp_userlist_view_do_popup(GmMcpUserlistView *view, GdkEventButton *event) { GList *menu; GList *item; GmKeyValuePair *pair; GtkWidget *menuitem; GtkTreeSelection *selection = gtk_tree_view_get_selection(view->tree_view); gint id; GtkTreeModel *model; GtkTreeIter iter; GtkTreePath *path; if (!event) { if (!gtk_tree_selection_get_selected(selection, &model, &iter)) { return FALSE; } gtk_tree_model_get(model, &iter, GM_USERLIST_ID, &id, -1); } else { if (!gtk_tree_view_get_path_at_pos(view->tree_view, event->x, event->y, &path, NULL, NULL, NULL)) { return FALSE; } else { gtk_tree_model_get_iter(view->model, &iter, path); gtk_tree_path_free(path); gtk_tree_model_get(view->model, &iter, GM_USERLIST_ID, &id, -1); } } menu = gm_iuserlist_get_menu(GM_IUSERLIST(view->package), id); if (view->popup_menu) { gtk_widget_destroy(view->popup_menu); } if (!menu) { return FALSE; } view->popup_menu = gtk_menu_new(); g_object_set_data(G_OBJECT(view->popup_menu), "McpUserlistView", view); gtk_menu_attach_to_widget(GTK_MENU(view->popup_menu), GTK_WIDGET(view->tree_view), popup_menu_detach); for (item = menu; item; item = item->next) { pair = (GmKeyValuePair *)(item->data); if (pair->key == NULL) { menuitem = gtk_separator_menu_item_new(); g_free(pair->value); } else { menuitem = gtk_menu_item_new_with_mnemonic(pair->key); g_object_set_data_full(G_OBJECT(menuitem), "UserlistAction", pair->value, g_free); g_signal_connect(menuitem, "activate", G_CALLBACK(on_gm_mcp_userlist_view_menuitem_activate), view); } gtk_widget_show(menuitem); gtk_menu_shell_append(GTK_MENU_SHELL(view->popup_menu), menuitem); g_free(pair->key); g_free(pair); } g_list_free(menu); if (!event) { gtk_menu_popup(GTK_MENU(view->popup_menu), NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time()); } else { gtk_menu_popup(GTK_MENU(view->popup_menu), NULL, NULL, NULL, NULL, event->button, event->time); } return TRUE; } static gboolean on_gm_mcp_userlist_view_popup_menu(GtkWidget *widget, GmMcpUserlistView *view) { return gm_mcp_userlist_view_do_popup(view, NULL); } static gboolean on_gm_mcp_userlist_view_button_press(GtkWidget *widget, GdkEventButton *event, GmMcpUserlistView *view) { if (event->button == 3 && event->type == GDK_BUTTON_PRESS) { gm_mcp_userlist_view_do_popup(view, event); } return FALSE; }