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
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"
48 #include "userlistgui.h"
51 #include <gtk/gtktextview.h>
57 COL_PIX
=0, // GdkPixbuf *
60 COL_USER
=3, // struct User *
61 COL_GDKCOLOR
=4 // GdkColor *
66 get_user_icon (server
*serv
, struct User
*user
)
74 /* these ones are hardcoded */
75 switch (user
->prefix
[0])
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
)
91 if (pre
[0] == user
->prefix
[0])
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 */
101 if (pre
== serv
->nick_prefixes
)
111 fe_userlist_numbers (session
*sess
)
115 if (sess
== current_tab
|| !sess
->gui
->is_tab
)
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
);
124 gtk_label_set_text (GTK_LABEL (sess
->gui
->namelistinfo
), NULL
);
127 if (sess
->type
== SESS_CHANNEL
&& prefs
.gui_tweaks
& 1)
133 scroll_to_iter (GtkTreeIter
*iter
, GtkTreeView
*treeview
, GtkTreeModel
*model
)
135 GtkTreePath
*path
= gtk_tree_model_get_path (model
, iter
);
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 */
146 userlist_select (session
*sess
, char *name
)
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
);
164 gtk_tree_selection_select_iter (selection
, &iter
);
166 /* and make sure it's visible */
167 scroll_to_iter (&iter
, treeview
, model
);
171 while (gtk_tree_model_iter_next (model
, &iter
));
176 userlist_selection_list (GtkWidget
*widget
, int *num_ret
)
179 GtkTreeView
*treeview
= (GtkTreeView
*) widget
;
180 GtkTreeSelection
*selection
= gtk_tree_view_get_selection (treeview
);
181 GtkTreeModel
*model
= gtk_tree_view_get_model (treeview
);
187 /* first, count the number of selections */
189 if (gtk_tree_model_get_iter_first (model
, &iter
))
193 if (gtk_tree_selection_iter_is_selected (selection
, &iter
))
196 while (gtk_tree_model_iter_next (model
, &iter
));
202 nicks
= malloc (sizeof (char *) * (num_sel
+ 1));
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
);
216 while (gtk_tree_model_iter_next (model
, &iter
));
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
));
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
)))
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
))
245 } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (store
), &iter
));
250 find_row (GtkTreeView
*treeview
, GtkTreeModel
*model
, struct User
*user
,
253 static GtkTreeIter iter
;
254 struct User
*row_user
;
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
))
272 while (gtk_tree_model_iter_next (model
, &iter
));
279 userlist_set_value (GtkWidget
*treeview
, gfloat val
)
281 gtk_adjustment_set_value (
282 gtk_tree_view_get_vadjustment (GTK_TREE_VIEW (treeview
)), val
);
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
)
295 /* GtkAdjustment *adj;
299 iter
= find_row (GTK_TREE_VIEW (sess
->gui
->user_tree
),
300 sess
->res
->user_model
, user
, &sel
);
304 /* adj = gtk_tree_view_get_vadjustment (GTK_TREE_VIEW (sess->gui->user_tree));
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;
316 gtk_adjustment_set_value (adj, val);
323 fe_userlist_rehash (session
*sess
, struct User
*user
)
329 iter
= find_row (GTK_TREE_VIEW (sess
->gui
->user_tree
),
330 sess
->res
->user_model
, user
, &sel
);
334 if (prefs
.away_size_max
< 1 || !prefs
.away_track
)
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
)
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
);
354 if (prefs
.away_size_max
< 1 || !prefs
.away_track
)
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
);
365 strcpy (nick
+ 1, newuser
->nick
);
369 gtk_list_store_insert_with_values (GTK_LIST_STORE (model
), &iter
, row
,
372 COL_HOST
, newuser
->hostname
,
374 COL_GDKCOLOR
, (do_away
)
375 ? (newuser
->away
? &colors
[COL_AWAY
] : NULL
)
379 if (prefs
.gui_tweaks
& 64)
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
);
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
]);
397 /* is it the front-most tab? */
398 if (gtk_tree_view_get_model (GTK_TREE_VIEW (sess
->gui
->user_tree
))
402 gtk_tree_selection_select_iter (gtk_tree_view_get_selection
403 (GTK_TREE_VIEW (sess
->gui
->user_tree
)), &iter
);
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
));
414 fe_userlist_clear (session
*sess
)
416 gtk_list_store_clear (sess
->res
->user_model
);
420 userlist_dnd_drop (GtkTreeView
*widget
, GdkDragContext
*context
,
421 gint x
, gint y
, GtkSelectionData
*selection_data
,
422 guint info
, guint ttime
, gpointer userdata
)
429 if (!gtk_tree_view_get_path_at_pos (widget
, x
, y
, &path
, NULL
, NULL
, NULL
))
432 model
= gtk_tree_view_get_model (widget
);
433 if (!gtk_tree_model_get_iter (model
, &iter
, path
))
435 gtk_tree_model_get (model
, &iter
, COL_USER
, &user
, -1);
437 mg_dnd_drop_file (current_sess
, user
->nick
, selection_data
->data
);
441 userlist_dnd_motion (GtkTreeView
*widget
, GdkDragContext
*context
, gint x
,
442 gint y
, guint ttime
, gpointer tree
)
445 GtkTreeSelection
*sel
;
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
);
461 userlist_dnd_leave (GtkTreeView
*widget
, GdkDragContext
*context
, guint ttime
)
463 gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (widget
));
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
);
475 userlist_add_columns (GtkTreeView
* treeview
)
477 GtkCellRenderer
*renderer
;
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
),
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
),
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
),
510 userlist_click_cb (GtkWidget
*widget
, GdkEventButton
*event
, gpointer userdata
)
514 GtkTreeSelection
*sel
;
520 if (!(event
->state
& GDK_CONTROL_MASK
) &&
521 event
->type
== GDK_2BUTTON_PRESS
&& prefs
.doubleclickuser
[0])
523 nicks
= userlist_selection_list (widget
, &i
);
526 nick_command_parse (current_sess
, prefs
.doubleclickuser
, nicks
[0],
538 if (event
->button
== 3)
540 /* do we have a multi-selection? */
541 nicks
= userlist_selection_list (widget
, &i
);
544 menu_nickmenu (current_sess
, event
, nicks
[0], i
);
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
);
569 menu_nickmenu (current_sess
, event
, nicks
[0], i
);
579 gtk_tree_selection_unselect_all (sel
);
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
);
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
:
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
);
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
);
673 userlist_show (session
*sess
)
675 gtk_tree_view_set_model (GTK_TREE_VIEW (sess
->gui
->user_tree
),
676 sess
->res
->user_model
);
680 fe_uselect (session
*sess
, char *word
[], int do_clear
, int scroll_to
)
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
))
693 gtk_tree_selection_unselect_all (selection
);
699 gtk_tree_model_get (model
, &iter
, COL_USER
, &row_user
, -1);
701 while ( *(name
= word
[thisname
++]) )
703 if (sess
->server
->p_cmp (row_user
->nick
, name
) == 0)
705 gtk_tree_selection_select_iter (selection
, &iter
);
707 scroll_to_iter (&iter
, treeview
, model
);
714 while (gtk_tree_model_iter_next (model
, &iter
));