remove all remaining WIN32 code
[rofl0r-ixchat.git] / src / fe-gtk / userlistgui.c
blobf88c9d5ded010676f736f29aa67c94982629c842
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 "gtkutil.h"
44 #include "palette.h"
45 #include "maingui.h"
46 #include "menu.h"
47 #include "pixmaps.h"
48 #include "userlistgui.h"
50 #ifdef USE_GTKSPELL
51 #include <gtk/gtktextview.h>
52 #endif
55 enum
57 COL_PIX=0, // GdkPixbuf *
58 COL_NICK=1, // char *
59 COL_HOST=2, // char *
60 COL_USER=3, // struct User *
61 COL_GDKCOLOR=4 // GdkColor *
65 GdkPixbuf *
66 get_user_icon (server *serv, struct User *user)
68 char *pre;
69 int level;
71 if (!user)
72 return NULL;
74 /* these ones are hardcoded */
75 switch (user->prefix[0])
77 case 0: return NULL;
78 case '@': return pix_op;
79 case '%': return pix_hop;
80 case '+': return pix_voice;
83 /* find out how many levels above Op this user is */
84 pre = strchr (serv->nick_prefixes, '@');
85 if (pre && pre != serv->nick_prefixes)
87 pre--;
88 level = 0;
89 while (1)
91 if (pre[0] == user->prefix[0])
93 switch (level)
95 case 0: return pix_red; /* 1 level above op */
96 case 1: return pix_purple; /* 2 levels above op */
98 break; /* 3+, no icons */
100 level++;
101 if (pre == serv->nick_prefixes)
102 break;
103 pre--;
107 return NULL;
110 void
111 fe_userlist_numbers (session *sess)
113 char tbuf[256];
115 if (sess == current_tab || !sess->gui->is_tab)
117 if (sess->total)
119 snprintf (tbuf, sizeof (tbuf), _("%d ops, %d total"), sess->ops, sess->total);
120 tbuf[sizeof (tbuf) - 1] = 0;
121 gtk_label_set_text (GTK_LABEL (sess->gui->namelistinfo), tbuf);
122 } else
124 gtk_label_set_text (GTK_LABEL (sess->gui->namelistinfo), NULL);
127 if (sess->type == SESS_CHANNEL && prefs.gui_tweaks & 1)
128 fe_set_title (sess);
132 static void
133 scroll_to_iter (GtkTreeIter *iter, GtkTreeView *treeview, GtkTreeModel *model)
135 GtkTreePath *path = gtk_tree_model_get_path (model, iter);
136 if (path)
138 gtk_tree_view_scroll_to_cell (treeview, path, NULL, TRUE, 0.5, 0.5);
139 gtk_tree_path_free (path);
143 /* select a row in the userlist by nick-name */
145 void
146 userlist_select (session *sess, char *name)
148 GtkTreeIter iter;
149 GtkTreeView *treeview = GTK_TREE_VIEW (sess->gui->user_tree);
150 GtkTreeModel *model = gtk_tree_view_get_model (treeview);
151 GtkTreeSelection *selection = gtk_tree_view_get_selection (treeview);
152 struct User *row_user;
154 if (gtk_tree_model_get_iter_first (model, &iter))
158 gtk_tree_model_get (model, &iter, COL_USER, &row_user, -1);
159 if (sess->server->p_cmp (row_user->nick, name) == 0)
161 if (gtk_tree_selection_iter_is_selected (selection, &iter))
162 gtk_tree_selection_unselect_iter (selection, &iter);
163 else
164 gtk_tree_selection_select_iter (selection, &iter);
166 /* and make sure it's visible */
167 scroll_to_iter (&iter, treeview, model);
168 return;
171 while (gtk_tree_model_iter_next (model, &iter));
175 char **
176 userlist_selection_list (GtkWidget *widget, int *num_ret)
178 GtkTreeIter iter;
179 GtkTreeView *treeview = (GtkTreeView *) widget;
180 GtkTreeSelection *selection = gtk_tree_view_get_selection (treeview);
181 GtkTreeModel *model = gtk_tree_view_get_model (treeview);
182 struct User *user;
183 int i, num_sel;
184 char **nicks;
186 *num_ret = 0;
187 /* first, count the number of selections */
188 num_sel = 0;
189 if (gtk_tree_model_get_iter_first (model, &iter))
193 if (gtk_tree_selection_iter_is_selected (selection, &iter))
194 num_sel++;
196 while (gtk_tree_model_iter_next (model, &iter));
199 if (num_sel < 1)
200 return NULL;
202 nicks = malloc (sizeof (char *) * (num_sel + 1));
204 i = 0;
205 gtk_tree_model_get_iter_first (model, &iter);
208 if (gtk_tree_selection_iter_is_selected (selection, &iter))
210 gtk_tree_model_get (model, &iter, COL_USER, &user, -1);
211 nicks[i] = g_strdup (user->nick);
212 i++;
213 nicks[i] = NULL;
216 while (gtk_tree_model_iter_next (model, &iter));
218 *num_ret = i;
219 return nicks;
222 void
223 fe_userlist_set_selected (struct session *sess)
225 GtkListStore *store = sess->res->user_model;
226 GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (sess->gui->user_tree));
227 GtkTreeIter iter;
228 struct User *user;
230 /* if it's not front-most tab it doesn't own the GtkTreeView! */
231 if (store != (GtkListStore*) gtk_tree_view_get_model (GTK_TREE_VIEW (sess->gui->user_tree)))
232 return;
234 if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter))
238 gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, COL_USER, &user, -1);
240 if (gtk_tree_selection_iter_is_selected (selection, &iter))
241 user->selected = 1;
242 else
243 user->selected = 0;
245 } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter));
249 static GtkTreeIter *
250 find_row (GtkTreeView *treeview, GtkTreeModel *model, struct User *user,
251 int *selected)
253 static GtkTreeIter iter;
254 struct User *row_user;
256 *selected = FALSE;
257 if (gtk_tree_model_get_iter_first (model, &iter))
261 gtk_tree_model_get (model, &iter, COL_USER, &row_user, -1);
262 if (row_user == user)
264 if (gtk_tree_view_get_model (treeview) == model)
266 if (gtk_tree_selection_iter_is_selected (gtk_tree_view_get_selection (treeview), &iter))
267 *selected = TRUE;
269 return &iter;
272 while (gtk_tree_model_iter_next (model, &iter));
275 return NULL;
278 void
279 userlist_set_value (GtkWidget *treeview, gfloat val)
281 gtk_adjustment_set_value (
282 gtk_tree_view_get_vadjustment (GTK_TREE_VIEW (treeview)), val);
285 gfloat
286 userlist_get_value (GtkWidget *treeview)
288 return gtk_tree_view_get_vadjustment (GTK_TREE_VIEW (treeview))->value;
292 fe_userlist_remove (session *sess, struct User *user)
294 GtkTreeIter *iter;
295 /* GtkAdjustment *adj;
296 gfloat val, end;*/
297 int sel;
299 iter = find_row (GTK_TREE_VIEW (sess->gui->user_tree),
300 sess->res->user_model, user, &sel);
301 if (!iter)
302 return 0;
304 /* adj = gtk_tree_view_get_vadjustment (GTK_TREE_VIEW (sess->gui->user_tree));
305 val = adj->value;*/
307 gtk_list_store_remove (sess->res->user_model, iter);
309 /* is it the front-most tab? */
310 /* if (gtk_tree_view_get_model (GTK_TREE_VIEW (sess->gui->user_tree))
311 == sess->res->user_model)
313 end = adj->upper - adj->lower - adj->page_size;
314 if (val > end)
315 val = end;
316 gtk_adjustment_set_value (adj, val);
319 return sel;
322 void
323 fe_userlist_rehash (session *sess, struct User *user)
325 GtkTreeIter *iter;
326 int sel;
327 int do_away = TRUE;
329 iter = find_row (GTK_TREE_VIEW (sess->gui->user_tree),
330 sess->res->user_model, user, &sel);
331 if (!iter)
332 return;
334 if (prefs.away_size_max < 1 || !prefs.away_track)
335 do_away = FALSE;
337 gtk_list_store_set (GTK_LIST_STORE (sess->res->user_model), iter,
338 COL_HOST, user->hostname,
339 COL_GDKCOLOR, (do_away)
340 ? (user->away ? &colors[COL_AWAY] : NULL)
341 : (NULL),
342 -1);
345 void
346 fe_userlist_insert (session *sess, struct User *newuser, int row, int sel)
348 GtkTreeModel *model = sess->res->user_model;
349 GdkPixbuf *pix = get_user_icon (sess->server, newuser);
350 GtkTreeIter iter;
351 int do_away = TRUE;
352 char *nick;
354 if (prefs.away_size_max < 1 || !prefs.away_track)
355 do_away = FALSE;
357 nick = newuser->nick;
358 if (prefs.gui_tweaks & 64)
360 nick = malloc (strlen (newuser->nick) + 2);
361 nick[0] = newuser->prefix[0];
362 if (!nick[0] || nick[0] == ' ')
363 strcpy (nick, newuser->nick);
364 else
365 strcpy (nick + 1, newuser->nick);
366 pix = NULL;
369 gtk_list_store_insert_with_values (GTK_LIST_STORE (model), &iter, row,
370 COL_PIX, pix,
371 COL_NICK, nick,
372 COL_HOST, newuser->hostname,
373 COL_USER, newuser,
374 COL_GDKCOLOR, (do_away)
375 ? (newuser->away ? &colors[COL_AWAY] : NULL)
376 : (NULL),
377 -1);
379 if (prefs.gui_tweaks & 64)
380 free (nick);
382 /* is it me? */
383 if (newuser->me && sess->gui->nick_box)
385 if (!sess->gui->is_tab || sess == current_tab)
386 mg_set_access_icon (sess->gui, pix, sess->server->is_away);
389 #if 0
390 if (prefs.hilitenotify && notify_isnotify (sess, newuser->nick))
392 gtk_clist_set_foreground ((GtkCList *) sess->gui->user_clist, row,
393 &colors[prefs.nu_color]);
395 #endif
397 /* is it the front-most tab? */
398 if (gtk_tree_view_get_model (GTK_TREE_VIEW (sess->gui->user_tree))
399 == model)
401 if (sel)
402 gtk_tree_selection_select_iter (gtk_tree_view_get_selection
403 (GTK_TREE_VIEW (sess->gui->user_tree)), &iter);
407 void
408 fe_userlist_move (session *sess, struct User *user, int new_row)
410 fe_userlist_insert (sess, user, new_row, fe_userlist_remove (sess, user));
413 void
414 fe_userlist_clear (session *sess)
416 gtk_list_store_clear (sess->res->user_model);
419 static void
420 userlist_dnd_drop (GtkTreeView *widget, GdkDragContext *context,
421 gint x, gint y, GtkSelectionData *selection_data,
422 guint info, guint ttime, gpointer userdata)
424 struct User *user;
425 GtkTreePath *path;
426 GtkTreeModel *model;
427 GtkTreeIter iter;
429 if (!gtk_tree_view_get_path_at_pos (widget, x, y, &path, NULL, NULL, NULL))
430 return;
432 model = gtk_tree_view_get_model (widget);
433 if (!gtk_tree_model_get_iter (model, &iter, path))
434 return;
435 gtk_tree_model_get (model, &iter, COL_USER, &user, -1);
437 mg_dnd_drop_file (current_sess, user->nick, selection_data->data);
440 static gboolean
441 userlist_dnd_motion (GtkTreeView *widget, GdkDragContext *context, gint x,
442 gint y, guint ttime, gpointer tree)
444 GtkTreePath *path;
445 GtkTreeSelection *sel;
447 if (!tree)
448 return FALSE;
450 if (gtk_tree_view_get_path_at_pos (widget, x, y, &path, NULL, NULL, NULL))
452 sel = gtk_tree_view_get_selection (widget);
453 gtk_tree_selection_unselect_all (sel);
454 gtk_tree_selection_select_path (sel, path);
457 return FALSE;
460 static gboolean
461 userlist_dnd_leave (GtkTreeView *widget, GdkDragContext *context, guint ttime)
463 gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (widget));
464 return TRUE;
467 void *
468 userlist_create_model (void)
470 return gtk_list_store_new (5, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING,
471 G_TYPE_POINTER, GDK_TYPE_COLOR);
474 static void
475 userlist_add_columns (GtkTreeView * treeview)
477 GtkCellRenderer *renderer;
479 /* icon column */
480 renderer = gtk_cell_renderer_pixbuf_new ();
481 if (prefs.gui_tweaks & 32)
482 g_object_set (G_OBJECT (renderer), "ypad", 0, NULL);
483 gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
484 -1, NULL, renderer,
485 "pixbuf", 0, NULL);
487 /* nick column */
488 renderer = gtk_cell_renderer_text_new ();
489 if (prefs.gui_tweaks & 32)
490 g_object_set (G_OBJECT (renderer), "ypad", 0, NULL);
491 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer), 1);
492 gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
493 -1, NULL, renderer,
494 "text", 1, "foreground-gdk", 4, NULL);
496 if (prefs.showhostname_in_userlist)
498 /* hostname column */
499 renderer = gtk_cell_renderer_text_new ();
500 if (prefs.gui_tweaks & 32)
501 g_object_set (G_OBJECT (renderer), "ypad", 0, NULL);
502 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer), 1);
503 gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
504 -1, NULL, renderer,
505 "text", 2, NULL);
509 static gint
510 userlist_click_cb (GtkWidget *widget, GdkEventButton *event, gpointer userdata)
512 char **nicks;
513 int i;
514 GtkTreeSelection *sel;
515 GtkTreePath *path;
517 if (!event)
518 return FALSE;
520 if (!(event->state & GDK_CONTROL_MASK) &&
521 event->type == GDK_2BUTTON_PRESS && prefs.doubleclickuser[0])
523 nicks = userlist_selection_list (widget, &i);
524 if (nicks)
526 nick_command_parse (current_sess, prefs.doubleclickuser, nicks[0],
527 nicks[0]);
528 while (i)
530 i--;
531 g_free (nicks[i]);
533 free (nicks);
535 return TRUE;
538 if (event->button == 3)
540 /* do we have a multi-selection? */
541 nicks = userlist_selection_list (widget, &i);
542 if (nicks && i > 1)
544 menu_nickmenu (current_sess, event, nicks[0], i);
545 while (i)
547 i--;
548 g_free (nicks[i]);
550 free (nicks);
551 return TRUE;
553 if (nicks)
555 g_free (nicks[0]);
556 free (nicks);
559 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
560 if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget),
561 event->x, event->y, &path, 0, 0, 0))
563 gtk_tree_selection_unselect_all (sel);
564 gtk_tree_selection_select_path (sel, path);
565 gtk_tree_path_free (path);
566 nicks = userlist_selection_list (widget, &i);
567 if (nicks)
569 menu_nickmenu (current_sess, event, nicks[0], i);
570 while (i)
572 i--;
573 g_free (nicks[i]);
575 free (nicks);
577 } else
579 gtk_tree_selection_unselect_all (sel);
582 return TRUE;
585 return FALSE;
588 static gboolean
589 userlist_key_cb (GtkWidget *wid, GdkEventKey *evt, gpointer userdata)
591 if (evt->keyval >= GDK_asterisk && evt->keyval <= GDK_z)
593 /* dirty trick to avoid auto-selection */
594 SPELL_ENTRY_SET_EDITABLE (current_sess->gui->input_box, FALSE);
595 gtk_widget_grab_focus (current_sess->gui->input_box);
596 SPELL_ENTRY_SET_EDITABLE (current_sess->gui->input_box, TRUE);
597 gtk_widget_event (current_sess->gui->input_box, (GdkEvent *)evt);
598 return TRUE;
601 return FALSE;
604 GtkWidget *
605 userlist_create (GtkWidget *box)
607 GtkWidget *sw, *treeview;
608 static const GtkTargetEntry dnd_dest_targets[] =
610 {"text/uri-list", 0, 1},
611 {"XCHAT_CHANVIEW", GTK_TARGET_SAME_APP, 75 }
613 static const GtkTargetEntry dnd_src_target[] =
615 {"XCHAT_USERLIST", GTK_TARGET_SAME_APP, 75 }
618 sw = gtk_scrolled_window_new (NULL, NULL);
619 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw),
620 GTK_SHADOW_ETCHED_IN);
621 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
622 prefs.showhostname_in_userlist ?
623 GTK_POLICY_AUTOMATIC :
624 GTK_POLICY_NEVER,
625 GTK_POLICY_AUTOMATIC);
626 gtk_box_pack_start (GTK_BOX (box), sw, TRUE, TRUE, 0);
627 gtk_widget_show (sw);
629 treeview = gtk_tree_view_new ();
630 gtk_widget_set_name (treeview, "xchat-userlist");
631 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
632 gtk_tree_selection_set_mode (gtk_tree_view_get_selection
633 (GTK_TREE_VIEW (treeview)),
634 GTK_SELECTION_MULTIPLE);
636 /* set up drops */
637 gtk_drag_dest_set (treeview, GTK_DEST_DEFAULT_ALL, dnd_dest_targets, 2,
638 GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK);
639 gtk_drag_source_set (treeview, GDK_BUTTON1_MASK, dnd_src_target, 1, GDK_ACTION_MOVE);
641 /* file DND (for DCC) */
642 g_signal_connect (G_OBJECT (treeview), "drag_motion",
643 G_CALLBACK (userlist_dnd_motion), treeview);
644 g_signal_connect (G_OBJECT (treeview), "drag_leave",
645 G_CALLBACK (userlist_dnd_leave), 0);
646 g_signal_connect (G_OBJECT (treeview), "drag_data_received",
647 G_CALLBACK (userlist_dnd_drop), treeview);
649 g_signal_connect (G_OBJECT (treeview), "button_press_event",
650 G_CALLBACK (userlist_click_cb), 0);
651 g_signal_connect (G_OBJECT (treeview), "key_press_event",
652 G_CALLBACK (userlist_key_cb), 0);
654 /* tree/chanview DND */
655 g_signal_connect (G_OBJECT (treeview), "drag_begin",
656 G_CALLBACK (mg_drag_begin_cb), NULL);
657 g_signal_connect (G_OBJECT (treeview), "drag_drop",
658 G_CALLBACK (mg_drag_drop_cb), NULL);
659 g_signal_connect (G_OBJECT (treeview), "drag_motion",
660 G_CALLBACK (mg_drag_motion_cb), NULL);
661 g_signal_connect (G_OBJECT (treeview), "drag_end",
662 G_CALLBACK (mg_drag_end_cb), NULL);
664 userlist_add_columns (GTK_TREE_VIEW (treeview));
666 gtk_container_add (GTK_CONTAINER (sw), treeview);
667 gtk_widget_show (treeview);
669 return treeview;
672 void
673 userlist_show (session *sess)
675 gtk_tree_view_set_model (GTK_TREE_VIEW (sess->gui->user_tree),
676 sess->res->user_model);
679 void
680 fe_uselect (session *sess, char *word[], int do_clear, int scroll_to)
682 int thisname;
683 char *name;
684 GtkTreeIter iter;
685 GtkTreeView *treeview = GTK_TREE_VIEW (sess->gui->user_tree);
686 GtkTreeModel *model = gtk_tree_view_get_model (treeview);
687 GtkTreeSelection *selection = gtk_tree_view_get_selection (treeview);
688 struct User *row_user;
690 if (gtk_tree_model_get_iter_first (model, &iter))
692 if (do_clear)
693 gtk_tree_selection_unselect_all (selection);
697 if (*word[0])
699 gtk_tree_model_get (model, &iter, COL_USER, &row_user, -1);
700 thisname = 0;
701 while ( *(name = word[thisname++]) )
703 if (sess->server->p_cmp (row_user->nick, name) == 0)
705 gtk_tree_selection_select_iter (selection, &iter);
706 if (scroll_to)
707 scroll_to_iter (&iter, treeview, model);
708 break;
714 while (gtk_tree_model_iter_next (model, &iter));