fix implicit declarations
[rofl0r-ixchat.git] / src / fe-gtk / userlistgui.c
blobcb2c84dc4beed5007d3c88b8c752cf8ae2dbc266
1 /* X-Chat
2 * Copyright (C) 1998 Peter Zelezny.
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19 #include <stdio.h>
20 #include <string.h>
21 #include <stdlib.h>
23 #include "fe-gtk.h"
25 #include <gtk/gtkbox.h>
26 #include <gtk/gtklabel.h>
27 #include <gtk/gtkdnd.h>
28 #include <gtk/gtkentry.h>
29 #include <gtk/gtktreeview.h>
30 #include <gtk/gtktreeselection.h>
31 #include <gtk/gtkscrolledwindow.h>
32 #include <gtk/gtkcellrendererpixbuf.h>
33 #include <gtk/gtkcellrenderertext.h>
34 #include <gtk/gtkliststore.h>
35 #include <gdk/gdkkeysyms.h>
37 #include "../common/xchat.h"
38 #include "../common/util.h"
39 #include "../common/userlist.h"
40 #include "../common/modes.h"
41 #include "../common/notify.h"
42 #include "../common/xchatc.h"
43 #include "../common/fe.h"
44 #include "gtkutil.h"
45 #include "palette.h"
46 #include "maingui.h"
47 #include "menu.h"
48 #include "pixmaps.h"
49 #include "userlistgui.h"
51 #ifdef USE_GTKSPELL
52 #include <gtk/gtktextview.h>
53 #endif
56 enum
58 COL_PIX=0, // GdkPixbuf *
59 COL_NICK=1, // char *
60 COL_HOST=2, // char *
61 COL_USER=3, // struct User *
62 COL_GDKCOLOR=4 // GdkColor *
66 GdkPixbuf *
67 get_user_icon (server *serv, struct User *user)
69 char *pre;
70 int level;
72 if (!user)
73 return NULL;
75 /* these ones are hardcoded */
76 switch (user->prefix[0])
78 case 0: return NULL;
79 case '@': return pix_op;
80 case '%': return pix_hop;
81 case '+': return pix_voice;
84 /* find out how many levels above Op this user is */
85 pre = strchr (serv->nick_prefixes, '@');
86 if (pre && pre != serv->nick_prefixes)
88 pre--;
89 level = 0;
90 while (1)
92 if (pre[0] == user->prefix[0])
94 switch (level)
96 case 0: return pix_red; /* 1 level above op */
97 case 1: return pix_purple; /* 2 levels above op */
99 break; /* 3+, no icons */
101 level++;
102 if (pre == serv->nick_prefixes)
103 break;
104 pre--;
108 return NULL;
111 void
112 fe_userlist_numbers (session *sess)
114 char tbuf[256];
116 if (sess == current_tab || !sess->gui->is_tab)
118 if (sess->total)
120 snprintf (tbuf, sizeof (tbuf), _("%d ops, %d total"), sess->ops, sess->total);
121 tbuf[sizeof (tbuf) - 1] = 0;
122 gtk_label_set_text (GTK_LABEL (sess->gui->namelistinfo), tbuf);
123 } else
125 gtk_label_set_text (GTK_LABEL (sess->gui->namelistinfo), NULL);
128 if (sess->type == SESS_CHANNEL && prefs.gui_tweaks & 1)
129 fe_set_title (sess);
133 static void
134 scroll_to_iter (GtkTreeIter *iter, GtkTreeView *treeview, GtkTreeModel *model)
136 GtkTreePath *path = gtk_tree_model_get_path (model, iter);
137 if (path)
139 gtk_tree_view_scroll_to_cell (treeview, path, NULL, TRUE, 0.5, 0.5);
140 gtk_tree_path_free (path);
144 /* select a row in the userlist by nick-name */
146 void
147 userlist_select (session *sess, char *name)
149 GtkTreeIter iter;
150 GtkTreeView *treeview = GTK_TREE_VIEW (sess->gui->user_tree);
151 GtkTreeModel *model = gtk_tree_view_get_model (treeview);
152 GtkTreeSelection *selection = gtk_tree_view_get_selection (treeview);
153 struct User *row_user;
155 if (gtk_tree_model_get_iter_first (model, &iter))
159 gtk_tree_model_get (model, &iter, COL_USER, &row_user, -1);
160 if (sess->server->p_cmp (row_user->nick, name) == 0)
162 if (gtk_tree_selection_iter_is_selected (selection, &iter))
163 gtk_tree_selection_unselect_iter (selection, &iter);
164 else
165 gtk_tree_selection_select_iter (selection, &iter);
167 /* and make sure it's visible */
168 scroll_to_iter (&iter, treeview, model);
169 return;
172 while (gtk_tree_model_iter_next (model, &iter));
176 char **
177 userlist_selection_list (GtkWidget *widget, int *num_ret)
179 GtkTreeIter iter;
180 GtkTreeView *treeview = (GtkTreeView *) widget;
181 GtkTreeSelection *selection = gtk_tree_view_get_selection (treeview);
182 GtkTreeModel *model = gtk_tree_view_get_model (treeview);
183 struct User *user;
184 int i, num_sel;
185 char **nicks;
187 *num_ret = 0;
188 /* first, count the number of selections */
189 num_sel = 0;
190 if (gtk_tree_model_get_iter_first (model, &iter))
194 if (gtk_tree_selection_iter_is_selected (selection, &iter))
195 num_sel++;
197 while (gtk_tree_model_iter_next (model, &iter));
200 if (num_sel < 1)
201 return NULL;
203 nicks = malloc (sizeof (char *) * (num_sel + 1));
205 i = 0;
206 gtk_tree_model_get_iter_first (model, &iter);
209 if (gtk_tree_selection_iter_is_selected (selection, &iter))
211 gtk_tree_model_get (model, &iter, COL_USER, &user, -1);
212 nicks[i] = g_strdup (user->nick);
213 i++;
214 nicks[i] = NULL;
217 while (gtk_tree_model_iter_next (model, &iter));
219 *num_ret = i;
220 return nicks;
223 void
224 fe_userlist_set_selected (struct session *sess)
226 GtkListStore *store = sess->res->user_model;
227 GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (sess->gui->user_tree));
228 GtkTreeIter iter;
229 struct User *user;
231 /* if it's not front-most tab it doesn't own the GtkTreeView! */
232 if (store != (GtkListStore*) gtk_tree_view_get_model (GTK_TREE_VIEW (sess->gui->user_tree)))
233 return;
235 if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter))
239 gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, COL_USER, &user, -1);
241 if (gtk_tree_selection_iter_is_selected (selection, &iter))
242 user->selected = 1;
243 else
244 user->selected = 0;
246 } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter));
250 static GtkTreeIter *
251 find_row (GtkTreeView *treeview, GtkTreeModel *model, struct User *user,
252 int *selected)
254 static GtkTreeIter iter;
255 struct User *row_user;
257 *selected = FALSE;
258 if (gtk_tree_model_get_iter_first (model, &iter))
262 gtk_tree_model_get (model, &iter, COL_USER, &row_user, -1);
263 if (row_user == user)
265 if (gtk_tree_view_get_model (treeview) == model)
267 if (gtk_tree_selection_iter_is_selected (gtk_tree_view_get_selection (treeview), &iter))
268 *selected = TRUE;
270 return &iter;
273 while (gtk_tree_model_iter_next (model, &iter));
276 return NULL;
279 void
280 userlist_set_value (GtkWidget *treeview, gfloat val)
282 gtk_adjustment_set_value (
283 gtk_tree_view_get_vadjustment (GTK_TREE_VIEW (treeview)), val);
286 gfloat
287 userlist_get_value (GtkWidget *treeview)
289 return gtk_tree_view_get_vadjustment (GTK_TREE_VIEW (treeview))->value;
293 fe_userlist_remove (session *sess, struct User *user)
295 GtkTreeIter *iter;
296 /* GtkAdjustment *adj;
297 gfloat val, end;*/
298 int sel;
300 iter = find_row (GTK_TREE_VIEW (sess->gui->user_tree),
301 sess->res->user_model, user, &sel);
302 if (!iter)
303 return 0;
305 /* adj = gtk_tree_view_get_vadjustment (GTK_TREE_VIEW (sess->gui->user_tree));
306 val = adj->value;*/
308 gtk_list_store_remove (sess->res->user_model, iter);
310 /* is it the front-most tab? */
311 /* if (gtk_tree_view_get_model (GTK_TREE_VIEW (sess->gui->user_tree))
312 == sess->res->user_model)
314 end = adj->upper - adj->lower - adj->page_size;
315 if (val > end)
316 val = end;
317 gtk_adjustment_set_value (adj, val);
320 return sel;
323 void
324 fe_userlist_rehash (session *sess, struct User *user)
326 GtkTreeIter *iter;
327 int sel;
328 int do_away = TRUE;
330 iter = find_row (GTK_TREE_VIEW (sess->gui->user_tree),
331 sess->res->user_model, user, &sel);
332 if (!iter)
333 return;
335 if (prefs.away_size_max < 1 || !prefs.away_track)
336 do_away = FALSE;
338 gtk_list_store_set (GTK_LIST_STORE (sess->res->user_model), iter,
339 COL_HOST, user->hostname,
340 COL_GDKCOLOR, (do_away)
341 ? (user->away ? &colors[COL_AWAY] : NULL)
342 : (NULL),
343 -1);
346 void
347 fe_userlist_insert (session *sess, struct User *newuser, int row, int sel)
349 GtkTreeModel *model = sess->res->user_model;
350 GdkPixbuf *pix = get_user_icon (sess->server, newuser);
351 GtkTreeIter iter;
352 int do_away = TRUE;
353 char *nick;
355 if (prefs.away_size_max < 1 || !prefs.away_track)
356 do_away = FALSE;
358 nick = newuser->nick;
359 if (prefs.gui_tweaks & 64)
361 nick = malloc (strlen (newuser->nick) + 2);
362 nick[0] = newuser->prefix[0];
363 if (!nick[0] || nick[0] == ' ')
364 strcpy (nick, newuser->nick);
365 else
366 strcpy (nick + 1, newuser->nick);
367 pix = NULL;
370 gtk_list_store_insert_with_values (GTK_LIST_STORE (model), &iter, row,
371 COL_PIX, pix,
372 COL_NICK, nick,
373 COL_HOST, newuser->hostname,
374 COL_USER, newuser,
375 COL_GDKCOLOR, (do_away)
376 ? (newuser->away ? &colors[COL_AWAY] : NULL)
377 : (NULL),
378 -1);
380 if (prefs.gui_tweaks & 64)
381 free (nick);
383 /* is it me? */
384 if (newuser->me && sess->gui->nick_box)
386 if (!sess->gui->is_tab || sess == current_tab)
387 mg_set_access_icon (sess->gui, pix, sess->server->is_away);
390 #if 0
391 if (prefs.hilitenotify && notify_isnotify (sess, newuser->nick))
393 gtk_clist_set_foreground ((GtkCList *) sess->gui->user_clist, row,
394 &colors[prefs.nu_color]);
396 #endif
398 /* is it the front-most tab? */
399 if (gtk_tree_view_get_model (GTK_TREE_VIEW (sess->gui->user_tree))
400 == model)
402 if (sel)
403 gtk_tree_selection_select_iter (gtk_tree_view_get_selection
404 (GTK_TREE_VIEW (sess->gui->user_tree)), &iter);
408 void
409 fe_userlist_move (session *sess, struct User *user, int new_row)
411 fe_userlist_insert (sess, user, new_row, fe_userlist_remove (sess, user));
414 void
415 fe_userlist_clear (session *sess)
417 gtk_list_store_clear (sess->res->user_model);
420 static void
421 userlist_dnd_drop (GtkTreeView *widget, GdkDragContext *context,
422 gint x, gint y, GtkSelectionData *selection_data,
423 guint info, guint ttime, gpointer userdata)
425 struct User *user;
426 GtkTreePath *path;
427 GtkTreeModel *model;
428 GtkTreeIter iter;
430 if (!gtk_tree_view_get_path_at_pos (widget, x, y, &path, NULL, NULL, NULL))
431 return;
433 model = gtk_tree_view_get_model (widget);
434 if (!gtk_tree_model_get_iter (model, &iter, path))
435 return;
436 gtk_tree_model_get (model, &iter, COL_USER, &user, -1);
438 mg_dnd_drop_file (current_sess, user->nick, selection_data->data);
441 static gboolean
442 userlist_dnd_motion (GtkTreeView *widget, GdkDragContext *context, gint x,
443 gint y, guint ttime, gpointer tree)
445 GtkTreePath *path;
446 GtkTreeSelection *sel;
448 if (!tree)
449 return FALSE;
451 if (gtk_tree_view_get_path_at_pos (widget, x, y, &path, NULL, NULL, NULL))
453 sel = gtk_tree_view_get_selection (widget);
454 gtk_tree_selection_unselect_all (sel);
455 gtk_tree_selection_select_path (sel, path);
458 return FALSE;
461 static gboolean
462 userlist_dnd_leave (GtkTreeView *widget, GdkDragContext *context, guint ttime)
464 gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (widget));
465 return TRUE;
468 void *
469 userlist_create_model (void)
471 return gtk_list_store_new (5, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING,
472 G_TYPE_POINTER, GDK_TYPE_COLOR);
475 static void
476 userlist_add_columns (GtkTreeView * treeview)
478 GtkCellRenderer *renderer;
480 /* icon column */
481 renderer = gtk_cell_renderer_pixbuf_new ();
482 if (prefs.gui_tweaks & 32)
483 g_object_set (G_OBJECT (renderer), "ypad", 0, NULL);
484 gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
485 -1, NULL, renderer,
486 "pixbuf", 0, NULL);
488 /* nick column */
489 renderer = gtk_cell_renderer_text_new ();
490 if (prefs.gui_tweaks & 32)
491 g_object_set (G_OBJECT (renderer), "ypad", 0, NULL);
492 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer), 1);
493 gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
494 -1, NULL, renderer,
495 "text", 1, "foreground-gdk", 4, NULL);
497 if (prefs.showhostname_in_userlist)
499 /* hostname column */
500 renderer = gtk_cell_renderer_text_new ();
501 if (prefs.gui_tweaks & 32)
502 g_object_set (G_OBJECT (renderer), "ypad", 0, NULL);
503 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer), 1);
504 gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
505 -1, NULL, renderer,
506 "text", 2, NULL);
510 static gint
511 userlist_click_cb (GtkWidget *widget, GdkEventButton *event, gpointer userdata)
513 char **nicks;
514 int i;
515 GtkTreeSelection *sel;
516 GtkTreePath *path;
518 if (!event)
519 return FALSE;
521 if (!(event->state & GDK_CONTROL_MASK) &&
522 event->type == GDK_2BUTTON_PRESS && prefs.doubleclickuser[0])
524 nicks = userlist_selection_list (widget, &i);
525 if (nicks)
527 nick_command_parse (current_sess, prefs.doubleclickuser, nicks[0],
528 nicks[0]);
529 while (i)
531 i--;
532 g_free (nicks[i]);
534 free (nicks);
536 return TRUE;
539 if (event->button == 3)
541 /* do we have a multi-selection? */
542 nicks = userlist_selection_list (widget, &i);
543 if (nicks && i > 1)
545 menu_nickmenu (current_sess, event, nicks[0], i);
546 while (i)
548 i--;
549 g_free (nicks[i]);
551 free (nicks);
552 return TRUE;
554 if (nicks)
556 g_free (nicks[0]);
557 free (nicks);
560 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
561 if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget),
562 event->x, event->y, &path, 0, 0, 0))
564 gtk_tree_selection_unselect_all (sel);
565 gtk_tree_selection_select_path (sel, path);
566 gtk_tree_path_free (path);
567 nicks = userlist_selection_list (widget, &i);
568 if (nicks)
570 menu_nickmenu (current_sess, event, nicks[0], i);
571 while (i)
573 i--;
574 g_free (nicks[i]);
576 free (nicks);
578 } else
580 gtk_tree_selection_unselect_all (sel);
583 return TRUE;
586 return FALSE;
589 static gboolean
590 userlist_key_cb (GtkWidget *wid, GdkEventKey *evt, gpointer userdata)
592 if (evt->keyval >= GDK_asterisk && evt->keyval <= GDK_z)
594 /* dirty trick to avoid auto-selection */
595 SPELL_ENTRY_SET_EDITABLE (current_sess->gui->input_box, FALSE);
596 gtk_widget_grab_focus (current_sess->gui->input_box);
597 SPELL_ENTRY_SET_EDITABLE (current_sess->gui->input_box, TRUE);
598 gtk_widget_event (current_sess->gui->input_box, (GdkEvent *)evt);
599 return TRUE;
602 return FALSE;
605 GtkWidget *
606 userlist_create (GtkWidget *box)
608 GtkWidget *sw, *treeview;
609 static const GtkTargetEntry dnd_dest_targets[] =
611 {"text/uri-list", 0, 1},
612 {"XCHAT_CHANVIEW", GTK_TARGET_SAME_APP, 75 }
614 static const GtkTargetEntry dnd_src_target[] =
616 {"XCHAT_USERLIST", GTK_TARGET_SAME_APP, 75 }
619 sw = gtk_scrolled_window_new (NULL, NULL);
620 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw),
621 GTK_SHADOW_ETCHED_IN);
622 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
623 prefs.showhostname_in_userlist ?
624 GTK_POLICY_AUTOMATIC :
625 GTK_POLICY_NEVER,
626 GTK_POLICY_AUTOMATIC);
627 gtk_box_pack_start (GTK_BOX (box), sw, TRUE, TRUE, 0);
628 gtk_widget_show (sw);
630 treeview = gtk_tree_view_new ();
631 gtk_widget_set_name (treeview, "xchat-userlist");
632 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
633 gtk_tree_selection_set_mode (gtk_tree_view_get_selection
634 (GTK_TREE_VIEW (treeview)),
635 GTK_SELECTION_MULTIPLE);
637 /* set up drops */
638 gtk_drag_dest_set (treeview, GTK_DEST_DEFAULT_ALL, dnd_dest_targets, 2,
639 GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK);
640 gtk_drag_source_set (treeview, GDK_BUTTON1_MASK, dnd_src_target, 1, GDK_ACTION_MOVE);
642 /* file DND (for DCC) */
643 g_signal_connect (G_OBJECT (treeview), "drag_motion",
644 G_CALLBACK (userlist_dnd_motion), treeview);
645 g_signal_connect (G_OBJECT (treeview), "drag_leave",
646 G_CALLBACK (userlist_dnd_leave), 0);
647 g_signal_connect (G_OBJECT (treeview), "drag_data_received",
648 G_CALLBACK (userlist_dnd_drop), treeview);
650 g_signal_connect (G_OBJECT (treeview), "button_press_event",
651 G_CALLBACK (userlist_click_cb), 0);
652 g_signal_connect (G_OBJECT (treeview), "key_press_event",
653 G_CALLBACK (userlist_key_cb), 0);
655 /* tree/chanview DND */
656 g_signal_connect (G_OBJECT (treeview), "drag_begin",
657 G_CALLBACK (mg_drag_begin_cb), NULL);
658 g_signal_connect (G_OBJECT (treeview), "drag_drop",
659 G_CALLBACK (mg_drag_drop_cb), NULL);
660 g_signal_connect (G_OBJECT (treeview), "drag_motion",
661 G_CALLBACK (mg_drag_motion_cb), NULL);
662 g_signal_connect (G_OBJECT (treeview), "drag_end",
663 G_CALLBACK (mg_drag_end_cb), NULL);
665 userlist_add_columns (GTK_TREE_VIEW (treeview));
667 gtk_container_add (GTK_CONTAINER (sw), treeview);
668 gtk_widget_show (treeview);
670 return treeview;
673 void
674 userlist_show (session *sess)
676 gtk_tree_view_set_model (GTK_TREE_VIEW (sess->gui->user_tree),
677 sess->res->user_model);
680 void
681 fe_uselect (session *sess, char *word[], int do_clear, int scroll_to)
683 int thisname;
684 char *name;
685 GtkTreeIter iter;
686 GtkTreeView *treeview = GTK_TREE_VIEW (sess->gui->user_tree);
687 GtkTreeModel *model = gtk_tree_view_get_model (treeview);
688 GtkTreeSelection *selection = gtk_tree_view_get_selection (treeview);
689 struct User *row_user;
691 if (gtk_tree_model_get_iter_first (model, &iter))
693 if (do_clear)
694 gtk_tree_selection_unselect_all (selection);
698 if (*word[0])
700 gtk_tree_model_get (model, &iter, COL_USER, &row_user, -1);
701 thisname = 0;
702 while ( *(name = word[thisname++]) )
704 if (sess->server->p_cmp (row_user->nick, name) == 0)
706 gtk_tree_selection_select_iter (selection, &iter);
707 if (scroll_to)
708 scroll_to_iter (&iter, treeview, model);
709 break;
715 while (gtk_tree_model_iter_next (model, &iter));