diff options
Diffstat (limited to 'src/fe-gtk/userlistgui.c')
-rw-r--r-- | src/fe-gtk/userlistgui.c | 718 |
1 files changed, 718 insertions, 0 deletions
diff --git a/src/fe-gtk/userlistgui.c b/src/fe-gtk/userlistgui.c new file mode 100644 index 00000000..f040a6a1 --- /dev/null +++ b/src/fe-gtk/userlistgui.c @@ -0,0 +1,718 @@ +/* X-Chat + * Copyright (C) 1998 Peter Zelezny. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include "fe-gtk.h" + +#include <gtk/gtkbox.h> +#include <gtk/gtklabel.h> +#include <gtk/gtkdnd.h> +#include <gtk/gtkentry.h> +#include <gtk/gtktreeview.h> +#include <gtk/gtktreeselection.h> +#include <gtk/gtkscrolledwindow.h> +#include <gtk/gtkcellrendererpixbuf.h> +#include <gtk/gtkcellrenderertext.h> +#include <gtk/gtkliststore.h> +#include <gdk/gdkkeysyms.h> + +#include "../common/xchat.h" +#include "../common/util.h" +#include "../common/userlist.h" +#include "../common/modes.h" +#include "../common/notify.h" +#include "../common/xchatc.h" +#include "gtkutil.h" +#include "palette.h" +#include "maingui.h" +#include "menu.h" +#include "pixmaps.h" +#include "userlistgui.h" + +#ifdef USE_GTKSPELL +#include <gtk/gtktextview.h> +#endif + + +enum +{ + COL_PIX=0, // GdkPixbuf * + COL_NICK=1, // char * + COL_HOST=2, // char * + COL_USER=3, // struct User * + COL_GDKCOLOR=4 // GdkColor * +}; + + +GdkPixbuf * +get_user_icon (server *serv, struct User *user) +{ + char *pre; + int level; + + if (!user) + return NULL; + + /* these ones are hardcoded */ + switch (user->prefix[0]) + { + case 0: return NULL; + case '@': return pix_op; + case '%': return pix_hop; + case '+': return pix_voice; + } + + /* find out how many levels above Op this user is */ + pre = strchr (serv->nick_prefixes, '@'); + if (pre && pre != serv->nick_prefixes) + { + pre--; + level = 0; + while (1) + { + if (pre[0] == user->prefix[0]) + { + switch (level) + { + case 0: return pix_red; /* 1 level above op */ + case 1: return pix_purple; /* 2 levels above op */ + } + break; /* 3+, no icons */ + } + level++; + if (pre == serv->nick_prefixes) + break; + pre--; + } + } + + return NULL; +} + +void +fe_userlist_numbers (session *sess) +{ + char tbuf[256]; + + if (sess == current_tab || !sess->gui->is_tab) + { + if (sess->total) + { + snprintf (tbuf, sizeof (tbuf), _("%d ops, %d total"), sess->ops, sess->total); + tbuf[sizeof (tbuf) - 1] = 0; + gtk_label_set_text (GTK_LABEL (sess->gui->namelistinfo), tbuf); + } else + { + gtk_label_set_text (GTK_LABEL (sess->gui->namelistinfo), NULL); + } + + if (sess->type == SESS_CHANNEL && prefs.gui_tweaks & 1) + fe_set_title (sess); + } +} + +static void +scroll_to_iter (GtkTreeIter *iter, GtkTreeView *treeview, GtkTreeModel *model) +{ + GtkTreePath *path = gtk_tree_model_get_path (model, iter); + if (path) + { + gtk_tree_view_scroll_to_cell (treeview, path, NULL, TRUE, 0.5, 0.5); + gtk_tree_path_free (path); + } +} + +/* select a row in the userlist by nick-name */ + +void +userlist_select (session *sess, char *name) +{ + GtkTreeIter iter; + GtkTreeView *treeview = GTK_TREE_VIEW (sess->gui->user_tree); + GtkTreeModel *model = gtk_tree_view_get_model (treeview); + GtkTreeSelection *selection = gtk_tree_view_get_selection (treeview); + struct User *row_user; + + if (gtk_tree_model_get_iter_first (model, &iter)) + { + do + { + gtk_tree_model_get (model, &iter, COL_USER, &row_user, -1); + if (sess->server->p_cmp (row_user->nick, name) == 0) + { + if (gtk_tree_selection_iter_is_selected (selection, &iter)) + gtk_tree_selection_unselect_iter (selection, &iter); + else + gtk_tree_selection_select_iter (selection, &iter); + + /* and make sure it's visible */ + scroll_to_iter (&iter, treeview, model); + return; + } + } + while (gtk_tree_model_iter_next (model, &iter)); + } +} + +char ** +userlist_selection_list (GtkWidget *widget, int *num_ret) +{ + GtkTreeIter iter; + GtkTreeView *treeview = (GtkTreeView *) widget; + GtkTreeSelection *selection = gtk_tree_view_get_selection (treeview); + GtkTreeModel *model = gtk_tree_view_get_model (treeview); + struct User *user; + int i, num_sel; + char **nicks; + + *num_ret = 0; + /* first, count the number of selections */ + num_sel = 0; + if (gtk_tree_model_get_iter_first (model, &iter)) + { + do + { + if (gtk_tree_selection_iter_is_selected (selection, &iter)) + num_sel++; + } + while (gtk_tree_model_iter_next (model, &iter)); + } + + if (num_sel < 1) + return NULL; + + nicks = malloc (sizeof (char *) * (num_sel + 1)); + + i = 0; + gtk_tree_model_get_iter_first (model, &iter); + do + { + if (gtk_tree_selection_iter_is_selected (selection, &iter)) + { + gtk_tree_model_get (model, &iter, COL_USER, &user, -1); + nicks[i] = g_strdup (user->nick); + i++; + nicks[i] = NULL; + } + } + while (gtk_tree_model_iter_next (model, &iter)); + + *num_ret = i; + return nicks; +} + +void +fe_userlist_set_selected (struct session *sess) +{ + GtkListStore *store = sess->res->user_model; + GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (sess->gui->user_tree)); + GtkTreeIter iter; + struct User *user; + + /* if it's not front-most tab it doesn't own the GtkTreeView! */ + if (store != (GtkListStore*) gtk_tree_view_get_model (GTK_TREE_VIEW (sess->gui->user_tree))) + return; + + if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter)) + { + do + { + gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, COL_USER, &user, -1); + + if (gtk_tree_selection_iter_is_selected (selection, &iter)) + user->selected = 1; + else + user->selected = 0; + + } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter)); + } +} + +static GtkTreeIter * +find_row (GtkTreeView *treeview, GtkTreeModel *model, struct User *user, + int *selected) +{ + static GtkTreeIter iter; + struct User *row_user; + + *selected = FALSE; + if (gtk_tree_model_get_iter_first (model, &iter)) + { + do + { + gtk_tree_model_get (model, &iter, COL_USER, &row_user, -1); + if (row_user == user) + { + if (gtk_tree_view_get_model (treeview) == model) + { + if (gtk_tree_selection_iter_is_selected (gtk_tree_view_get_selection (treeview), &iter)) + *selected = TRUE; + } + return &iter; + } + } + while (gtk_tree_model_iter_next (model, &iter)); + } + + return NULL; +} + +void +userlist_set_value (GtkWidget *treeview, gfloat val) +{ + gtk_adjustment_set_value ( + gtk_tree_view_get_vadjustment (GTK_TREE_VIEW (treeview)), val); +} + +gfloat +userlist_get_value (GtkWidget *treeview) +{ + return gtk_tree_view_get_vadjustment (GTK_TREE_VIEW (treeview))->value; +} + +int +fe_userlist_remove (session *sess, struct User *user) +{ + GtkTreeIter *iter; +/* GtkAdjustment *adj; + gfloat val, end;*/ + int sel; + + iter = find_row (GTK_TREE_VIEW (sess->gui->user_tree), + sess->res->user_model, user, &sel); + if (!iter) + return 0; + +/* adj = gtk_tree_view_get_vadjustment (GTK_TREE_VIEW (sess->gui->user_tree)); + val = adj->value;*/ + + gtk_list_store_remove (sess->res->user_model, iter); + + /* is it the front-most tab? */ +/* if (gtk_tree_view_get_model (GTK_TREE_VIEW (sess->gui->user_tree)) + == sess->res->user_model) + { + end = adj->upper - adj->lower - adj->page_size; + if (val > end) + val = end; + gtk_adjustment_set_value (adj, val); + }*/ + + return sel; +} + +void +fe_userlist_rehash (session *sess, struct User *user) +{ + GtkTreeIter *iter; + int sel; + int do_away = TRUE; + + iter = find_row (GTK_TREE_VIEW (sess->gui->user_tree), + sess->res->user_model, user, &sel); + if (!iter) + return; + + if (prefs.away_size_max < 1 || !prefs.away_track) + do_away = FALSE; + + gtk_list_store_set (GTK_LIST_STORE (sess->res->user_model), iter, + COL_HOST, user->hostname, + COL_GDKCOLOR, (do_away) + ? (user->away ? &colors[COL_AWAY] : NULL) + : (NULL), + -1); +} + +void +fe_userlist_insert (session *sess, struct User *newuser, int row, int sel) +{ + GtkTreeModel *model = sess->res->user_model; + GdkPixbuf *pix = get_user_icon (sess->server, newuser); + GtkTreeIter iter; + int do_away = TRUE; + char *nick; + + if (prefs.away_size_max < 1 || !prefs.away_track) + do_away = FALSE; + + nick = newuser->nick; + if (prefs.gui_tweaks & 64) + { + nick = malloc (strlen (newuser->nick) + 2); + nick[0] = newuser->prefix[0]; + if (!nick[0] || nick[0] == ' ') + strcpy (nick, newuser->nick); + else + strcpy (nick + 1, newuser->nick); + pix = NULL; + } + + gtk_list_store_insert_with_values (GTK_LIST_STORE (model), &iter, row, + COL_PIX, pix, + COL_NICK, nick, + COL_HOST, newuser->hostname, + COL_USER, newuser, + COL_GDKCOLOR, (do_away) + ? (newuser->away ? &colors[COL_AWAY] : NULL) + : (NULL), + -1); + + if (prefs.gui_tweaks & 64) + free (nick); + + /* is it me? */ + if (newuser->me && sess->gui->nick_box) + { + if (!sess->gui->is_tab || sess == current_tab) + mg_set_access_icon (sess->gui, pix, sess->server->is_away); + } + +#if 0 + if (prefs.hilitenotify && notify_isnotify (sess, newuser->nick)) + { + gtk_clist_set_foreground ((GtkCList *) sess->gui->user_clist, row, + &colors[prefs.nu_color]); + } +#endif + + /* is it the front-most tab? */ + if (gtk_tree_view_get_model (GTK_TREE_VIEW (sess->gui->user_tree)) + == model) + { + if (sel) + gtk_tree_selection_select_iter (gtk_tree_view_get_selection + (GTK_TREE_VIEW (sess->gui->user_tree)), &iter); + } +} + +void +fe_userlist_move (session *sess, struct User *user, int new_row) +{ + fe_userlist_insert (sess, user, new_row, fe_userlist_remove (sess, user)); +} + +void +fe_userlist_clear (session *sess) +{ + gtk_list_store_clear (sess->res->user_model); +} + +static void +userlist_dnd_drop (GtkTreeView *widget, GdkDragContext *context, + gint x, gint y, GtkSelectionData *selection_data, + guint info, guint ttime, gpointer userdata) +{ + struct User *user; + GtkTreePath *path; + GtkTreeModel *model; + GtkTreeIter iter; + + if (!gtk_tree_view_get_path_at_pos (widget, x, y, &path, NULL, NULL, NULL)) + return; + + model = gtk_tree_view_get_model (widget); + if (!gtk_tree_model_get_iter (model, &iter, path)) + return; + gtk_tree_model_get (model, &iter, COL_USER, &user, -1); + + mg_dnd_drop_file (current_sess, user->nick, selection_data->data); +} + +static gboolean +userlist_dnd_motion (GtkTreeView *widget, GdkDragContext *context, gint x, + gint y, guint ttime, gpointer tree) +{ + GtkTreePath *path; + GtkTreeSelection *sel; + + if (!tree) + return FALSE; + + if (gtk_tree_view_get_path_at_pos (widget, x, y, &path, NULL, NULL, NULL)) + { + sel = gtk_tree_view_get_selection (widget); + gtk_tree_selection_unselect_all (sel); + gtk_tree_selection_select_path (sel, path); + } + + return FALSE; +} + +static gboolean +userlist_dnd_leave (GtkTreeView *widget, GdkDragContext *context, guint ttime) +{ + gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (widget)); + return TRUE; +} + +void * +userlist_create_model (void) +{ + return gtk_list_store_new (5, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_POINTER, GDK_TYPE_COLOR); +} + +static void +userlist_add_columns (GtkTreeView * treeview) +{ + GtkCellRenderer *renderer; + + /* icon column */ + renderer = gtk_cell_renderer_pixbuf_new (); + if (prefs.gui_tweaks & 32) + g_object_set (G_OBJECT (renderer), "ypad", 0, NULL); + gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview), + -1, NULL, renderer, + "pixbuf", 0, NULL); + + /* nick column */ + renderer = gtk_cell_renderer_text_new (); + if (prefs.gui_tweaks & 32) + g_object_set (G_OBJECT (renderer), "ypad", 0, NULL); + gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer), 1); + gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview), + -1, NULL, renderer, + "text", 1, "foreground-gdk", 4, NULL); + + if (prefs.showhostname_in_userlist) + { + /* hostname column */ + renderer = gtk_cell_renderer_text_new (); + if (prefs.gui_tweaks & 32) + g_object_set (G_OBJECT (renderer), "ypad", 0, NULL); + gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer), 1); + gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview), + -1, NULL, renderer, + "text", 2, NULL); + } +} + +static gint +userlist_click_cb (GtkWidget *widget, GdkEventButton *event, gpointer userdata) +{ + char **nicks; + int i; + GtkTreeSelection *sel; + GtkTreePath *path; + + if (!event) + return FALSE; + + if (!(event->state & GDK_CONTROL_MASK) && + event->type == GDK_2BUTTON_PRESS && prefs.doubleclickuser[0]) + { + nicks = userlist_selection_list (widget, &i); + if (nicks) + { + nick_command_parse (current_sess, prefs.doubleclickuser, nicks[0], + nicks[0]); + while (i) + { + i--; + g_free (nicks[i]); + } + free (nicks); + } + return TRUE; + } + + if (event->button == 3) + { + /* do we have a multi-selection? */ + nicks = userlist_selection_list (widget, &i); + if (nicks && i > 1) + { + menu_nickmenu (current_sess, event, nicks[0], i); + while (i) + { + i--; + g_free (nicks[i]); + } + free (nicks); + return TRUE; + } + if (nicks) + { + g_free (nicks[0]); + free (nicks); + } + + sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget)); + if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget), + event->x, event->y, &path, 0, 0, 0)) + { + gtk_tree_selection_unselect_all (sel); + gtk_tree_selection_select_path (sel, path); + gtk_tree_path_free (path); + nicks = userlist_selection_list (widget, &i); + if (nicks) + { + menu_nickmenu (current_sess, event, nicks[0], i); + while (i) + { + i--; + g_free (nicks[i]); + } + free (nicks); + } + } else + { + gtk_tree_selection_unselect_all (sel); + } + + return TRUE; + } + + return FALSE; +} + +static gboolean +userlist_key_cb (GtkWidget *wid, GdkEventKey *evt, gpointer userdata) +{ + if (evt->keyval >= GDK_asterisk && evt->keyval <= GDK_z) + { + /* dirty trick to avoid auto-selection */ + SPELL_ENTRY_SET_EDITABLE (current_sess->gui->input_box, FALSE); + gtk_widget_grab_focus (current_sess->gui->input_box); + SPELL_ENTRY_SET_EDITABLE (current_sess->gui->input_box, TRUE); + gtk_widget_event (current_sess->gui->input_box, (GdkEvent *)evt); + return TRUE; + } + + return FALSE; +} + +GtkWidget * +userlist_create (GtkWidget *box) +{ + GtkWidget *sw, *treeview; + static const GtkTargetEntry dnd_dest_targets[] = + { + {"text/uri-list", 0, 1}, + {"XCHAT_CHANVIEW", GTK_TARGET_SAME_APP, 75 } + }; + static const GtkTargetEntry dnd_src_target[] = + { + {"XCHAT_USERLIST", GTK_TARGET_SAME_APP, 75 } + }; + + sw = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), + GTK_SHADOW_ETCHED_IN); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), + prefs.showhostname_in_userlist ? + GTK_POLICY_AUTOMATIC : + GTK_POLICY_NEVER, + GTK_POLICY_AUTOMATIC); + gtk_box_pack_start (GTK_BOX (box), sw, TRUE, TRUE, 0); + gtk_widget_show (sw); + + treeview = gtk_tree_view_new (); + gtk_widget_set_name (treeview, "xchat-userlist"); + gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE); + gtk_tree_selection_set_mode (gtk_tree_view_get_selection + (GTK_TREE_VIEW (treeview)), + GTK_SELECTION_MULTIPLE); + + /* set up drops */ + gtk_drag_dest_set (treeview, GTK_DEST_DEFAULT_ALL, dnd_dest_targets, 2, + GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK); + gtk_drag_source_set (treeview, GDK_BUTTON1_MASK, dnd_src_target, 1, GDK_ACTION_MOVE); + + /* file DND (for DCC) */ + g_signal_connect (G_OBJECT (treeview), "drag_motion", + G_CALLBACK (userlist_dnd_motion), treeview); + g_signal_connect (G_OBJECT (treeview), "drag_leave", + G_CALLBACK (userlist_dnd_leave), 0); + g_signal_connect (G_OBJECT (treeview), "drag_data_received", + G_CALLBACK (userlist_dnd_drop), treeview); + + g_signal_connect (G_OBJECT (treeview), "button_press_event", + G_CALLBACK (userlist_click_cb), 0); + g_signal_connect (G_OBJECT (treeview), "key_press_event", + G_CALLBACK (userlist_key_cb), 0); + + /* tree/chanview DND */ +#ifndef WIN32 /* leaks GDI pool memory, don't enable */ + g_signal_connect (G_OBJECT (treeview), "drag_begin", + G_CALLBACK (mg_drag_begin_cb), NULL); + g_signal_connect (G_OBJECT (treeview), "drag_drop", + G_CALLBACK (mg_drag_drop_cb), NULL); + g_signal_connect (G_OBJECT (treeview), "drag_motion", + G_CALLBACK (mg_drag_motion_cb), NULL); + g_signal_connect (G_OBJECT (treeview), "drag_end", + G_CALLBACK (mg_drag_end_cb), NULL); +#endif + + userlist_add_columns (GTK_TREE_VIEW (treeview)); + + gtk_container_add (GTK_CONTAINER (sw), treeview); + gtk_widget_show (treeview); + + return treeview; +} + +void +userlist_show (session *sess) +{ + gtk_tree_view_set_model (GTK_TREE_VIEW (sess->gui->user_tree), + sess->res->user_model); +} + +void +fe_uselect (session *sess, char *word[], int do_clear, int scroll_to) +{ + int thisname; + char *name; + GtkTreeIter iter; + GtkTreeView *treeview = GTK_TREE_VIEW (sess->gui->user_tree); + GtkTreeModel *model = gtk_tree_view_get_model (treeview); + GtkTreeSelection *selection = gtk_tree_view_get_selection (treeview); + struct User *row_user; + + if (gtk_tree_model_get_iter_first (model, &iter)) + { + if (do_clear) + gtk_tree_selection_unselect_all (selection); + + do + { + if (*word[0]) + { + gtk_tree_model_get (model, &iter, COL_USER, &row_user, -1); + thisname = 0; + while ( *(name = word[thisname++]) ) + { + if (sess->server->p_cmp (row_user->nick, name) == 0) + { + gtk_tree_selection_select_iter (selection, &iter); + if (scroll_to) + scroll_to_iter (&iter, treeview, model); + break; + } + } + } + + } + while (gtk_tree_model_iter_next (model, &iter)); + } +} |