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"
43 #include "../common/fe.h"
49 #include "userlistgui.h"
52 #include <gtk/gtktextview.h>
58 COL_PIX
=0, // GdkPixbuf *
61 COL_USER
=3, // struct User *
62 COL_GDKCOLOR
=4 // GdkColor *
67 get_user_icon (server
*serv
, struct User
*user
)
75 /* these ones are hardcoded */
76 switch (user
->prefix
[0])
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
)
92 if (pre
[0] == user
->prefix
[0])
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 */
102 if (pre
== serv
->nick_prefixes
)
112 fe_userlist_numbers (session
*sess
)
116 if (sess
== current_tab
|| !sess
->gui
->is_tab
)
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
);
125 gtk_label_set_text (GTK_LABEL (sess
->gui
->namelistinfo
), NULL
);
128 if (sess
->type
== SESS_CHANNEL
&& prefs
.gui_tweaks
& 1)
134 scroll_to_iter (GtkTreeIter
*iter
, GtkTreeView
*treeview
, GtkTreeModel
*model
)
136 GtkTreePath
*path
= gtk_tree_model_get_path (model
, iter
);
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 */
147 userlist_select (session
*sess
, char *name
)
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
);
165 gtk_tree_selection_select_iter (selection
, &iter
);
167 /* and make sure it's visible */
168 scroll_to_iter (&iter
, treeview
, model
);
172 while (gtk_tree_model_iter_next (model
, &iter
));
177 userlist_selection_list (GtkWidget
*widget
, int *num_ret
)
180 GtkTreeView
*treeview
= (GtkTreeView
*) widget
;
181 GtkTreeSelection
*selection
= gtk_tree_view_get_selection (treeview
);
182 GtkTreeModel
*model
= gtk_tree_view_get_model (treeview
);
188 /* first, count the number of selections */
190 if (gtk_tree_model_get_iter_first (model
, &iter
))
194 if (gtk_tree_selection_iter_is_selected (selection
, &iter
))
197 while (gtk_tree_model_iter_next (model
, &iter
));
203 nicks
= malloc (sizeof (char *) * (num_sel
+ 1));
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
);
217 while (gtk_tree_model_iter_next (model
, &iter
));
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
));
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
)))
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
))
246 } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (store
), &iter
));
251 find_row (GtkTreeView
*treeview
, GtkTreeModel
*model
, struct User
*user
,
254 static GtkTreeIter iter
;
255 struct User
*row_user
;
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
))
273 while (gtk_tree_model_iter_next (model
, &iter
));
280 userlist_set_value (GtkWidget
*treeview
, gfloat val
)
282 gtk_adjustment_set_value (
283 gtk_tree_view_get_vadjustment (GTK_TREE_VIEW (treeview
)), val
);
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
)
296 /* GtkAdjustment *adj;
300 iter
= find_row (GTK_TREE_VIEW (sess
->gui
->user_tree
),
301 sess
->res
->user_model
, user
, &sel
);
305 /* adj = gtk_tree_view_get_vadjustment (GTK_TREE_VIEW (sess->gui->user_tree));
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;
317 gtk_adjustment_set_value (adj, val);
324 fe_userlist_rehash (session
*sess
, struct User
*user
)
330 iter
= find_row (GTK_TREE_VIEW (sess
->gui
->user_tree
),
331 sess
->res
->user_model
, user
, &sel
);
335 if (prefs
.away_size_max
< 1 || !prefs
.away_track
)
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
)
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
);
355 if (prefs
.away_size_max
< 1 || !prefs
.away_track
)
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
);
366 strcpy (nick
+ 1, newuser
->nick
);
370 gtk_list_store_insert_with_values (GTK_LIST_STORE (model
), &iter
, row
,
373 COL_HOST
, newuser
->hostname
,
375 COL_GDKCOLOR
, (do_away
)
376 ? (newuser
->away
? &colors
[COL_AWAY
] : NULL
)
380 if (prefs
.gui_tweaks
& 64)
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
);
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
]);
398 /* is it the front-most tab? */
399 if (gtk_tree_view_get_model (GTK_TREE_VIEW (sess
->gui
->user_tree
))
403 gtk_tree_selection_select_iter (gtk_tree_view_get_selection
404 (GTK_TREE_VIEW (sess
->gui
->user_tree
)), &iter
);
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
));
415 fe_userlist_clear (session
*sess
)
417 gtk_list_store_clear (sess
->res
->user_model
);
421 userlist_dnd_drop (GtkTreeView
*widget
, GdkDragContext
*context
,
422 gint x
, gint y
, GtkSelectionData
*selection_data
,
423 guint info
, guint ttime
, gpointer userdata
)
430 if (!gtk_tree_view_get_path_at_pos (widget
, x
, y
, &path
, NULL
, NULL
, NULL
))
433 model
= gtk_tree_view_get_model (widget
);
434 if (!gtk_tree_model_get_iter (model
, &iter
, path
))
436 gtk_tree_model_get (model
, &iter
, COL_USER
, &user
, -1);
438 mg_dnd_drop_file (current_sess
, user
->nick
, selection_data
->data
);
442 userlist_dnd_motion (GtkTreeView
*widget
, GdkDragContext
*context
, gint x
,
443 gint y
, guint ttime
, gpointer tree
)
446 GtkTreeSelection
*sel
;
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
);
462 userlist_dnd_leave (GtkTreeView
*widget
, GdkDragContext
*context
, guint ttime
)
464 gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (widget
));
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
);
476 userlist_add_columns (GtkTreeView
* treeview
)
478 GtkCellRenderer
*renderer
;
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
),
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
),
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
),
511 userlist_click_cb (GtkWidget
*widget
, GdkEventButton
*event
, gpointer userdata
)
515 GtkTreeSelection
*sel
;
521 if (!(event
->state
& GDK_CONTROL_MASK
) &&
522 event
->type
== GDK_2BUTTON_PRESS
&& prefs
.doubleclickuser
[0])
524 nicks
= userlist_selection_list (widget
, &i
);
527 nick_command_parse (current_sess
, prefs
.doubleclickuser
, nicks
[0],
539 if (event
->button
== 3)
541 /* do we have a multi-selection? */
542 nicks
= userlist_selection_list (widget
, &i
);
545 menu_nickmenu (current_sess
, event
, nicks
[0], i
);
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
);
570 menu_nickmenu (current_sess
, event
, nicks
[0], i
);
580 gtk_tree_selection_unselect_all (sel
);
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
);
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
:
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
);
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
);
674 userlist_show (session
*sess
)
676 gtk_tree_view_set_model (GTK_TREE_VIEW (sess
->gui
->user_tree
),
677 sess
->res
->user_model
);
681 fe_uselect (session
*sess
, char *word
[], int do_clear
, int scroll_to
)
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
))
694 gtk_tree_selection_unselect_all (selection
);
700 gtk_tree_model_get (model
, &iter
, COL_USER
, &row_user
, -1);
702 while ( *(name
= word
[thisname
++]) )
704 if (sess
->server
->p_cmp (row_user
->nick
, name
) == 0)
706 gtk_tree_selection_select_iter (selection
, &iter
);
708 scroll_to_iter (&iter
, treeview
, model
);
715 while (gtk_tree_model_iter_next (model
, &iter
));