1 /* X-Chat
2  * Copyright (C) 1998 Peter Zelezny.
3  *
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.
8  *
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.
13  *
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18 
19 #include <stdio.h>
20 #include <string.h>
21 #include <stdlib.h>
22 
23 #include "fe-gtk.h"
24 
25 #include <gdk/gdkkeysyms.h>
26 
27 #include "../common/hexchat.h"
28 #include "../common/util.h"
29 #include "../common/userlist.h"
30 #include "../common/modes.h"
31 #include "../common/text.h"
32 #include "../common/notify.h"
33 #include "../common/hexchatc.h"
34 #include "../common/fe.h"
35 #include "gtkutil.h"
36 #include "palette.h"
37 #include "maingui.h"
38 #include "menu.h"
39 #include "pixmaps.h"
40 #include "userlistgui.h"
41 #include "fkeys.h"
42 
43 enum
44 {
45 	COL_PIX=0,		/* GdkPixbuf * */
46 	COL_NICK=1,		/* char * */
47 	COL_HOST=2,		/* char * */
48 	COL_USER=3,		/* struct User * */
49 	COL_GDKCOLOR=4	/* GdkColor * */
50 };
51 
52 
53 GdkPixbuf *
get_user_icon(server * serv,struct User * user)54 get_user_icon (server *serv, struct User *user)
55 {
56 	char *pre;
57 	int level;
58 
59 	if (!user)
60 		return NULL;
61 
62 	/* these ones are hardcoded */
63 	switch (user->prefix[0])
64 	{
65 		case 0: return NULL;
66 		case '+': return pix_ulist_voice;
67 		case '%': return pix_ulist_halfop;
68 		case '@': return pix_ulist_op;
69 	}
70 
71 	/* find out how many levels above Op this user is */
72 	pre = strchr (serv->nick_prefixes, '@');
73 	if (pre && pre != serv->nick_prefixes)
74 	{
75 		pre--;
76 		level = 0;
77 		while (1)
78 		{
79 			if (pre[0] == user->prefix[0])
80 			{
81 				switch (level)
82 				{
83 					case 0: return pix_ulist_owner;		/* 1 level above op */
84 					case 1: return pix_ulist_founder;	/* 2 levels above op */
85 					case 2: return pix_ulist_netop;		/* 3 levels above op */
86 				}
87 				break;	/* 4+, no icons */
88 			}
89 			level++;
90 			if (pre == serv->nick_prefixes)
91 				break;
92 			pre--;
93 		}
94 	}
95 
96 	return NULL;
97 }
98 
99 void
fe_userlist_numbers(session * sess)100 fe_userlist_numbers (session *sess)
101 {
102 	char tbuf[256];
103 
104 	if (sess == current_tab || !sess->gui->is_tab)
105 	{
106 		if (sess->total)
107 		{
108 			g_snprintf (tbuf, sizeof (tbuf), _("%d ops, %d total"), sess->ops, sess->total);
109 			tbuf[sizeof (tbuf) - 1] = 0;
110 			gtk_label_set_text (GTK_LABEL (sess->gui->namelistinfo), tbuf);
111 		} else
112 		{
113 			gtk_label_set_text (GTK_LABEL (sess->gui->namelistinfo), NULL);
114 		}
115 
116 		if (sess->type == SESS_CHANNEL && prefs.hex_gui_win_ucount)
117 			fe_set_title (sess);
118 	}
119 }
120 
121 static void
scroll_to_iter(GtkTreeIter * iter,GtkTreeView * treeview,GtkTreeModel * model)122 scroll_to_iter (GtkTreeIter *iter, GtkTreeView *treeview, GtkTreeModel *model)
123 {
124 	GtkTreePath *path = gtk_tree_model_get_path (model, iter);
125 	if (path)
126 	{
127 		gtk_tree_view_scroll_to_cell (treeview, path, NULL, TRUE, 0.5, 0.5);
128 		gtk_tree_path_free (path);
129 	}
130 }
131 
132 /* select a row in the userlist by nick-name */
133 
134 void
userlist_select(session * sess,char * name)135 userlist_select (session *sess, char *name)
136 {
137 	GtkTreeIter iter;
138 	GtkTreeView *treeview = GTK_TREE_VIEW (sess->gui->user_tree);
139 	GtkTreeModel *model = gtk_tree_view_get_model (treeview);
140 	GtkTreeSelection *selection = gtk_tree_view_get_selection (treeview);
141 	struct User *row_user;
142 
143 	if (gtk_tree_model_get_iter_first (model, &iter))
144 	{
145 		do
146 		{
147 			gtk_tree_model_get (model, &iter, COL_USER, &row_user, -1);
148 			if (sess->server->p_cmp (row_user->nick, name) == 0)
149 			{
150 				if (gtk_tree_selection_iter_is_selected (selection, &iter))
151 					gtk_tree_selection_unselect_iter (selection, &iter);
152 				else
153 					gtk_tree_selection_select_iter (selection, &iter);
154 
155 				/* and make sure it's visible */
156 				scroll_to_iter (&iter, treeview, model);
157 				return;
158 			}
159 		}
160 		while (gtk_tree_model_iter_next (model, &iter));
161 	}
162 }
163 
164 char **
userlist_selection_list(GtkWidget * widget,int * num_ret)165 userlist_selection_list (GtkWidget *widget, int *num_ret)
166 {
167 	GtkTreeIter iter;
168 	GtkTreeView *treeview = (GtkTreeView *) widget;
169 	GtkTreeSelection *selection = gtk_tree_view_get_selection (treeview);
170 	GtkTreeModel *model = gtk_tree_view_get_model (treeview);
171 	struct User *user;
172 	int i, num_sel;
173 	char **nicks;
174 
175 	*num_ret = 0;
176 	/* first, count the number of selections */
177 	num_sel = 0;
178 	if (gtk_tree_model_get_iter_first (model, &iter))
179 	{
180 		do
181 		{
182 			if (gtk_tree_selection_iter_is_selected (selection, &iter))
183 				num_sel++;
184 		}
185 		while (gtk_tree_model_iter_next (model, &iter));
186 	}
187 
188 	if (num_sel < 1)
189 		return NULL;
190 
191 	nicks = g_new (char *, num_sel + 1);
192 
193 	i = 0;
194 	gtk_tree_model_get_iter_first (model, &iter);
195 	do
196 	{
197 		if (gtk_tree_selection_iter_is_selected (selection, &iter))
198 		{
199 			gtk_tree_model_get (model, &iter, COL_USER, &user, -1);
200 			nicks[i] = g_strdup (user->nick);
201 			i++;
202 			nicks[i] = NULL;
203 		}
204 	}
205 	while (gtk_tree_model_iter_next (model, &iter));
206 
207 	*num_ret = i;
208 	return nicks;
209 }
210 
211 void
fe_userlist_set_selected(struct session * sess)212 fe_userlist_set_selected (struct session *sess)
213 {
214 	GtkListStore *store = sess->res->user_model;
215 	GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (sess->gui->user_tree));
216 	GtkTreeIter iter;
217 	struct User *user;
218 
219 	/* if it's not front-most tab it doesn't own the GtkTreeView! */
220 	if (store != (GtkListStore*) gtk_tree_view_get_model (GTK_TREE_VIEW (sess->gui->user_tree)))
221 		return;
222 
223 	if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter))
224 	{
225 		do
226 		{
227 			gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, COL_USER, &user, -1);
228 
229 			if (gtk_tree_selection_iter_is_selected (selection, &iter))
230 				user->selected = 1;
231 			else
232 				user->selected = 0;
233 
234 		} while (gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter));
235 	}
236 }
237 
238 static GtkTreeIter *
find_row(GtkTreeView * treeview,GtkTreeModel * model,struct User * user,int * selected)239 find_row (GtkTreeView *treeview, GtkTreeModel *model, struct User *user,
240 			 int *selected)
241 {
242 	static GtkTreeIter iter;
243 	struct User *row_user;
244 
245 	*selected = FALSE;
246 	if (gtk_tree_model_get_iter_first (model, &iter))
247 	{
248 		do
249 		{
250 			gtk_tree_model_get (model, &iter, COL_USER, &row_user, -1);
251 			if (row_user == user)
252 			{
253 				if (gtk_tree_view_get_model (treeview) == model)
254 				{
255 					if (gtk_tree_selection_iter_is_selected (gtk_tree_view_get_selection (treeview), &iter))
256 						*selected = TRUE;
257 				}
258 				return &iter;
259 			}
260 		}
261 		while (gtk_tree_model_iter_next (model, &iter));
262 	}
263 
264 	return NULL;
265 }
266 
267 void
userlist_set_value(GtkWidget * treeview,gfloat val)268 userlist_set_value (GtkWidget *treeview, gfloat val)
269 {
270 	gtk_adjustment_set_value (
271 			gtk_tree_view_get_vadjustment (GTK_TREE_VIEW (treeview)), val);
272 }
273 
274 gfloat
userlist_get_value(GtkWidget * treeview)275 userlist_get_value (GtkWidget *treeview)
276 {
277 	return gtk_adjustment_get_value (gtk_tree_view_get_vadjustment (GTK_TREE_VIEW (treeview)));
278 }
279 
280 int
fe_userlist_remove(session * sess,struct User * user)281 fe_userlist_remove (session *sess, struct User *user)
282 {
283 	GtkTreeIter *iter;
284 /*	GtkAdjustment *adj;
285 	gfloat val, end;*/
286 	int sel;
287 
288 	iter = find_row (GTK_TREE_VIEW (sess->gui->user_tree),
289 						  GTK_TREE_MODEL(sess->res->user_model), user, &sel);
290 	if (!iter)
291 		return 0;
292 
293 /*	adj = gtk_tree_view_get_vadjustment (GTK_TREE_VIEW (sess->gui->user_tree));
294 	val = adj->value;*/
295 
296 	gtk_list_store_remove (sess->res->user_model, iter);
297 
298 	/* is it the front-most tab? */
299 /*	if (gtk_tree_view_get_model (GTK_TREE_VIEW (sess->gui->user_tree))
300 		 == sess->res->user_model)
301 	{
302 		end = adj->upper - adj->lower - adj->page_size;
303 		if (val > end)
304 			val = end;
305 		gtk_adjustment_set_value (adj, val);
306 	}*/
307 
308 	return sel;
309 }
310 
311 void
fe_userlist_rehash(session * sess,struct User * user)312 fe_userlist_rehash (session *sess, struct User *user)
313 {
314 	GtkTreeIter *iter;
315 	int sel;
316 	int nick_color = 0;
317 
318 	iter = find_row (GTK_TREE_VIEW (sess->gui->user_tree),
319 						  GTK_TREE_MODEL(sess->res->user_model), user, &sel);
320 	if (!iter)
321 		return;
322 
323 	if (prefs.hex_away_track && user->away)
324 		nick_color = COL_AWAY;
325 	else if (prefs.hex_gui_ulist_color)
326 		nick_color = text_color_of(user->nick);
327 
328 	gtk_list_store_set (GTK_LIST_STORE (sess->res->user_model), iter,
329 							  COL_HOST, user->hostname,
330 							  COL_GDKCOLOR, nick_color ? &colors[nick_color] : NULL,
331 							  -1);
332 }
333 
334 void
fe_userlist_insert(session * sess,struct User * newuser,gboolean sel)335 fe_userlist_insert (session *sess, struct User *newuser, gboolean sel)
336 {
337 	GtkTreeModel *model = GTK_TREE_MODEL(sess->res->user_model);
338 	GdkPixbuf *pix = get_user_icon (sess->server, newuser);
339 	GtkTreeIter iter;
340 	char *nick;
341 	int nick_color = 0;
342 
343 	if (prefs.hex_away_track && newuser->away)
344 		nick_color = COL_AWAY;
345 	else if (prefs.hex_gui_ulist_color)
346 		nick_color = text_color_of(newuser->nick);
347 
348 	nick = newuser->nick;
349 	if (!prefs.hex_gui_ulist_icons)
350 	{
351 		nick = g_malloc (strlen (newuser->nick) + 2);
352 		nick[0] = newuser->prefix[0];
353 		if (nick[0] == '\0' || nick[0] == ' ')
354 			strcpy (nick, newuser->nick);
355 		else
356 			strcpy (nick + 1, newuser->nick);
357 		pix = NULL;
358 	}
359 
360 	gtk_list_store_insert_with_values (GTK_LIST_STORE (model), &iter, 0,
361 									COL_PIX, pix,
362 									COL_NICK, nick,
363 									COL_HOST, newuser->hostname,
364 									COL_USER, newuser,
365 									COL_GDKCOLOR, nick_color ? &colors[nick_color] : NULL,
366 								  -1);
367 
368 	if (!prefs.hex_gui_ulist_icons)
369 	{
370 		g_free (nick);
371 	}
372 
373 	/* is it me? */
374 	if (newuser->me && sess->gui->nick_box)
375 	{
376 		if (!sess->gui->is_tab || sess == current_tab)
377 			mg_set_access_icon (sess->gui, pix, sess->server->is_away);
378 	}
379 
380 	/* is it the front-most tab? */
381 	if (gtk_tree_view_get_model (GTK_TREE_VIEW (sess->gui->user_tree))
382 		 == model)
383 	{
384 		if (sel)
385 			gtk_tree_selection_select_iter (gtk_tree_view_get_selection
386 										(GTK_TREE_VIEW (sess->gui->user_tree)), &iter);
387 	}
388 }
389 
390 void
fe_userlist_clear(session * sess)391 fe_userlist_clear (session *sess)
392 {
393 	gtk_list_store_clear (sess->res->user_model);
394 }
395 
396 static void
userlist_dnd_drop(GtkTreeView * widget,GdkDragContext * context,gint x,gint y,GtkSelectionData * selection_data,guint info,guint ttime,gpointer userdata)397 userlist_dnd_drop (GtkTreeView *widget, GdkDragContext *context,
398 						 gint x, gint y, GtkSelectionData *selection_data,
399 						 guint info, guint ttime, gpointer userdata)
400 {
401 	struct User *user;
402 	gchar *data;
403 	GtkTreePath *path;
404 	GtkTreeModel *model;
405 	GtkTreeIter iter;
406 
407 	if (!gtk_tree_view_get_path_at_pos (widget, x, y, &path, NULL, NULL, NULL))
408 		return;
409 
410 	model = gtk_tree_view_get_model (widget);
411 	if (!gtk_tree_model_get_iter (model, &iter, path))
412 		return;
413 	gtk_tree_model_get (model, &iter, COL_USER, &user, -1);
414 
415 	data = (char *)gtk_selection_data_get_data (selection_data);
416 
417 	if (data)
418 		mg_dnd_drop_file (current_sess, user->nick, data);
419 }
420 
421 static gboolean
userlist_dnd_motion(GtkTreeView * widget,GdkDragContext * context,gint x,gint y,guint ttime,gpointer tree)422 userlist_dnd_motion (GtkTreeView *widget, GdkDragContext *context, gint x,
423 							gint y, guint ttime, gpointer tree)
424 {
425 	GtkTreePath *path;
426 	GtkTreeSelection *sel;
427 
428 	if (!tree)
429 		return FALSE;
430 
431 	if (gtk_tree_view_get_path_at_pos (widget, x, y, &path, NULL, NULL, NULL))
432 	{
433 		sel = gtk_tree_view_get_selection (widget);
434 		gtk_tree_selection_unselect_all (sel);
435 		gtk_tree_selection_select_path (sel, path);
436 	}
437 
438 	return FALSE;
439 }
440 
441 static gboolean
userlist_dnd_leave(GtkTreeView * widget,GdkDragContext * context,guint ttime)442 userlist_dnd_leave (GtkTreeView *widget, GdkDragContext *context, guint ttime)
443 {
444 	gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (widget));
445 	return TRUE;
446 }
447 
448 static int
userlist_alpha_cmp(GtkTreeModel * model,GtkTreeIter * iter_a,GtkTreeIter * iter_b,gpointer userdata)449 userlist_alpha_cmp (GtkTreeModel *model, GtkTreeIter *iter_a, GtkTreeIter *iter_b, gpointer userdata)
450 {
451 	struct User *user_a, *user_b;
452 
453 	gtk_tree_model_get (model, iter_a, COL_USER, &user_a, -1);
454 	gtk_tree_model_get (model, iter_b, COL_USER, &user_b, -1);
455 
456 	return nick_cmp_alpha (user_a, user_b, ((session*)userdata)->server);
457 }
458 
459 static int
userlist_ops_cmp(GtkTreeModel * model,GtkTreeIter * iter_a,GtkTreeIter * iter_b,gpointer userdata)460 userlist_ops_cmp (GtkTreeModel *model, GtkTreeIter *iter_a, GtkTreeIter *iter_b, gpointer userdata)
461 {
462 	struct User *user_a, *user_b;
463 
464 	gtk_tree_model_get (model, iter_a, COL_USER, &user_a, -1);
465 	gtk_tree_model_get (model, iter_b, COL_USER, &user_b, -1);
466 
467 	return nick_cmp_az_ops (((session*)userdata)->server, user_a, user_b);
468 }
469 
470 GtkListStore *
userlist_create_model(session * sess)471 userlist_create_model (session *sess)
472 {
473 	GtkListStore *store;
474 	GtkTreeIterCompareFunc cmp_func;
475 	GtkSortType sort_type;
476 
477 	store = gtk_list_store_new (5, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING,
478 										G_TYPE_POINTER, GDK_TYPE_COLOR);
479 
480 	switch (prefs.hex_gui_ulist_sort)
481 	{
482 	case 0:
483 		cmp_func = userlist_ops_cmp;
484 		sort_type = GTK_SORT_ASCENDING;
485 		break;
486 	case 1:
487 		cmp_func = userlist_alpha_cmp;
488 		sort_type = GTK_SORT_ASCENDING;
489 		break;
490 	case 2:
491 		cmp_func = userlist_ops_cmp;
492 		sort_type = GTK_SORT_DESCENDING;
493 		break;
494 	case 3:
495 		cmp_func = userlist_alpha_cmp;
496 		sort_type = GTK_SORT_DESCENDING;
497 		break;
498 	default:
499 		/* No sorting */
500 		gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE(store), NULL, NULL, NULL);
501 		return store;
502 	}
503 
504 	gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE(store), cmp_func, sess, NULL);
505 	gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(store),
506 						GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, sort_type);
507 
508 	return store;
509 }
510 
511 static void
userlist_add_columns(GtkTreeView * treeview)512 userlist_add_columns (GtkTreeView * treeview)
513 {
514 	GtkCellRenderer *renderer;
515 
516 	/* icon column */
517 	renderer = gtk_cell_renderer_pixbuf_new ();
518 	if (prefs.hex_gui_compact)
519 		g_object_set (G_OBJECT (renderer), "ypad", 0, NULL);
520 	gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
521 																-1, NULL, renderer,
522 																"pixbuf", 0, NULL);
523 
524 	/* nick column */
525 	renderer = gtk_cell_renderer_text_new ();
526 	if (prefs.hex_gui_compact)
527 		g_object_set (G_OBJECT (renderer), "ypad", 0, NULL);
528 	gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer), 1);
529 	gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
530 																-1, NULL, renderer,
531 													"text", 1, "foreground-gdk", 4, NULL);
532 
533 	if (prefs.hex_gui_ulist_show_hosts)
534 	{
535 		/* hostname column */
536 		renderer = gtk_cell_renderer_text_new ();
537 		if (prefs.hex_gui_compact)
538 			g_object_set (G_OBJECT (renderer), "ypad", 0, NULL);
539 		gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer), 1);
540 		gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
541 																	-1, NULL, renderer,
542 																	"text", 2, NULL);
543 	}
544 }
545 
546 static gint
userlist_click_cb(GtkWidget * widget,GdkEventButton * event,gpointer userdata)547 userlist_click_cb (GtkWidget *widget, GdkEventButton *event, gpointer userdata)
548 {
549 	char **nicks;
550 	int i;
551 	GtkTreeSelection *sel;
552 	GtkTreePath *path;
553 
554 	if (!event)
555 		return FALSE;
556 
557 	if (!(event->state & STATE_CTRL) &&
558 		event->type == GDK_2BUTTON_PRESS && prefs.hex_gui_ulist_doubleclick[0])
559 	{
560 		nicks = userlist_selection_list (widget, &i);
561 		if (nicks)
562 		{
563 			nick_command_parse (current_sess, prefs.hex_gui_ulist_doubleclick, nicks[0],
564 									  nicks[0]);
565 			while (i)
566 			{
567 				i--;
568 				g_free (nicks[i]);
569 			}
570 			g_free (nicks);
571 		}
572 		return TRUE;
573 	}
574 
575 	if (event->button == 3)
576 	{
577 		/* do we have a multi-selection? */
578 		nicks = userlist_selection_list (widget, &i);
579 		if (nicks && i > 1)
580 		{
581 			menu_nickmenu (current_sess, event, nicks[0], i);
582 			while (i)
583 			{
584 				i--;
585 				g_free (nicks[i]);
586 			}
587 			g_free (nicks);
588 			return TRUE;
589 		}
590 		if (nicks)
591 		{
592 			g_free (nicks[0]);
593 			g_free (nicks);
594 		}
595 
596 		sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
597 		if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget),
598 			 event->x, event->y, &path, 0, 0, 0))
599 		{
600 			gtk_tree_selection_unselect_all (sel);
601 			gtk_tree_selection_select_path (sel, path);
602 			gtk_tree_path_free (path);
603 			nicks = userlist_selection_list (widget, &i);
604 			if (nicks)
605 			{
606 				menu_nickmenu (current_sess, event, nicks[0], i);
607 				while (i)
608 				{
609 					i--;
610 					g_free (nicks[i]);
611 				}
612 				g_free (nicks);
613 			}
614 		} else
615 		{
616 			gtk_tree_selection_unselect_all (sel);
617 		}
618 
619 		return TRUE;
620 	}
621 
622 	return FALSE;
623 }
624 
625 static gboolean
userlist_key_cb(GtkWidget * wid,GdkEventKey * evt,gpointer userdata)626 userlist_key_cb (GtkWidget *wid, GdkEventKey *evt, gpointer userdata)
627 {
628 	if (evt->keyval >= GDK_KEY_asterisk && evt->keyval <= GDK_KEY_z)
629 	{
630 		/* dirty trick to avoid auto-selection */
631 		SPELL_ENTRY_SET_EDITABLE (current_sess->gui->input_box, FALSE);
632 		gtk_widget_grab_focus (current_sess->gui->input_box);
633 		SPELL_ENTRY_SET_EDITABLE (current_sess->gui->input_box, TRUE);
634 		gtk_widget_event (current_sess->gui->input_box, (GdkEvent *)evt);
635 		return TRUE;
636 	}
637 
638 	return FALSE;
639 }
640 
641 GtkWidget *
userlist_create(GtkWidget * box)642 userlist_create (GtkWidget *box)
643 {
644 	GtkWidget *sw, *treeview;
645 	static const GtkTargetEntry dnd_dest_targets[] =
646 	{
647 		{"text/uri-list", 0, 1},
648 		{"HEXCHAT_CHANVIEW", GTK_TARGET_SAME_APP, 75 }
649 	};
650 	static const GtkTargetEntry dnd_src_target[] =
651 	{
652 		{"HEXCHAT_USERLIST", GTK_TARGET_SAME_APP, 75 }
653 	};
654 
655 	sw = gtk_scrolled_window_new (NULL, NULL);
656 	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw),
657 													 GTK_SHADOW_ETCHED_IN);
658 	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
659 											  prefs.hex_gui_ulist_show_hosts ?
660 												GTK_POLICY_AUTOMATIC :
661 												GTK_POLICY_NEVER,
662 											  GTK_POLICY_AUTOMATIC);
663 	gtk_box_pack_start (GTK_BOX (box), sw, TRUE, TRUE, 0);
664 	gtk_widget_show (sw);
665 
666 	treeview = gtk_tree_view_new ();
667 	gtk_widget_set_name (treeview, "hexchat-userlist");
668 	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
669 	gtk_tree_selection_set_mode (gtk_tree_view_get_selection
670 										  (GTK_TREE_VIEW (treeview)),
671 										  GTK_SELECTION_MULTIPLE);
672 
673 	/* set up drops */
674 	gtk_drag_dest_set (treeview, GTK_DEST_DEFAULT_ALL, dnd_dest_targets, 2,
675 							 GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK);
676 	gtk_drag_source_set (treeview, GDK_BUTTON1_MASK, dnd_src_target, 1, GDK_ACTION_MOVE);
677 
678 	/* file DND (for DCC) */
679 	g_signal_connect (G_OBJECT (treeview), "drag_motion",
680 							G_CALLBACK (userlist_dnd_motion), treeview);
681 	g_signal_connect (G_OBJECT (treeview), "drag_leave",
682 							G_CALLBACK (userlist_dnd_leave), 0);
683 	g_signal_connect (G_OBJECT (treeview), "drag_data_received",
684 							G_CALLBACK (userlist_dnd_drop), treeview);
685 
686 	g_signal_connect (G_OBJECT (treeview), "button_press_event",
687 							G_CALLBACK (userlist_click_cb), 0);
688 	g_signal_connect (G_OBJECT (treeview), "key_press_event",
689 							G_CALLBACK (userlist_key_cb), 0);
690 
691 	/* tree/chanview DND */
692 	g_signal_connect (G_OBJECT (treeview), "drag_begin",
693 							G_CALLBACK (mg_drag_begin_cb), NULL);
694 	g_signal_connect (G_OBJECT (treeview), "drag_drop",
695 							G_CALLBACK (mg_drag_drop_cb), NULL);
696 	g_signal_connect (G_OBJECT (treeview), "drag_motion",
697 							G_CALLBACK (mg_drag_motion_cb), NULL);
698 	g_signal_connect (G_OBJECT (treeview), "drag_end",
699 							G_CALLBACK (mg_drag_end_cb), NULL);
700 
701 	userlist_add_columns (GTK_TREE_VIEW (treeview));
702 
703 	gtk_container_add (GTK_CONTAINER (sw), treeview);
704 	gtk_widget_show (treeview);
705 
706 	return treeview;
707 }
708 
709 void
userlist_show(session * sess)710 userlist_show (session *sess)
711 {
712 	gtk_tree_view_set_model (GTK_TREE_VIEW (sess->gui->user_tree),
713 									 GTK_TREE_MODEL(sess->res->user_model));
714 }
715 
716 void
fe_uselect(session * sess,char * word[],int do_clear,int scroll_to)717 fe_uselect (session *sess, char *word[], int do_clear, int scroll_to)
718 {
719 	int thisname;
720 	char *name;
721 	GtkTreeIter iter;
722 	GtkTreeView *treeview = GTK_TREE_VIEW (sess->gui->user_tree);
723 	GtkTreeModel *model = gtk_tree_view_get_model (treeview);
724 	GtkTreeSelection *selection = gtk_tree_view_get_selection (treeview);
725 	struct User *row_user;
726 
727 	if (gtk_tree_model_get_iter_first (model, &iter))
728 	{
729 		if (do_clear)
730 			gtk_tree_selection_unselect_all (selection);
731 
732 		do
733 		{
734 			if (*word[0])
735 			{
736 				gtk_tree_model_get (model, &iter, COL_USER, &row_user, -1);
737 				thisname = 0;
738 				while ( *(name = word[thisname++]) )
739 				{
740 					if (sess->server->p_cmp (row_user->nick, name) == 0)
741 					{
742 						gtk_tree_selection_select_iter (selection, &iter);
743 						if (scroll_to)
744 							scroll_to_iter (&iter, treeview, model);
745 						break;
746 					}
747 				}
748 			}
749 
750 		}
751 		while (gtk_tree_model_iter_next (model, &iter));
752 	}
753 }
754