1 /* X-Chat
2  * Copyright (C) 1998-2005 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 <stdlib.h>
20 #include <string.h>
21 #include <stdio.h>
22 #include <ctype.h>
23 
24 #include <gdk/gdkkeysyms.h>
25 
26 #include "../common/hexchat.h"
27 #include "../common/fe.h"
28 #include "../common/server.h"
29 #include "../common/hexchatc.h"
30 #include "../common/outbound.h"
31 #include "../common/inbound.h"
32 #include "../common/plugin.h"
33 #include "../common/modes.h"
34 #include "../common/url.h"
35 #include "../common/util.h"
36 #include "../common/text.h"
37 #include "../common/chanopt.h"
38 #include "../common/cfgfiles.h"
39 
40 #include "fe-gtk.h"
41 #include "banlist.h"
42 #include "gtkutil.h"
43 #include "joind.h"
44 #include "palette.h"
45 #include "maingui.h"
46 #include "menu.h"
47 #include "fkeys.h"
48 #include "userlistgui.h"
49 #include "chanview.h"
50 #include "pixmaps.h"
51 #include "plugin-tray.h"
52 #include "xtext.h"
53 #include "sexy-spell-entry.h"
54 #include "gtkutil.h"
55 
56 #ifdef G_OS_WIN32
57 #include <windows.h>
58 #endif
59 
60 #define GUI_SPACING (3)
61 #define GUI_BORDER (0)
62 
63 enum
64 {
65 	POS_INVALID = 0,
66 	POS_TOPLEFT = 1,
67 	POS_BOTTOMLEFT = 2,
68 	POS_TOPRIGHT = 3,
69 	POS_BOTTOMRIGHT = 4,
70 	POS_TOP = 5,	/* for tabs only */
71 	POS_BOTTOM = 6,
72 	POS_HIDDEN = 7
73 };
74 
75 /* two different types of tabs */
76 #define TAG_IRC 0		/* server, channel, dialog */
77 #define TAG_UTIL 1	/* dcc, notify, chanlist */
78 
79 static void mg_create_entry (session *sess, GtkWidget *box);
80 static void mg_create_search (session *sess, GtkWidget *box);
81 static void mg_link_irctab (session *sess, int focus);
82 
83 static session_gui static_mg_gui;
84 static session_gui *mg_gui = NULL;	/* the shared irc tab */
85 static int ignore_chanmode = FALSE;
86 static const char chan_flags[] = { 'c', 'n', 't', 'i', 'm', 'l', 'k' };
87 
88 static chan *active_tab = NULL;	/* active tab */
89 GtkWidget *parent_window = NULL;			/* the master window */
90 
91 GtkStyle *input_style;
92 
93 static PangoAttrList *away_list;
94 static PangoAttrList *newdata_list;
95 static PangoAttrList *nickseen_list;
96 static PangoAttrList *newmsg_list;
97 static PangoAttrList *plain_list = NULL;
98 
99 static PangoAttrList *
mg_attr_list_create(GdkColor * col,int size)100 mg_attr_list_create (GdkColor *col, int size)
101 {
102 	PangoAttribute *attr;
103 	PangoAttrList *list;
104 
105 	list = pango_attr_list_new ();
106 
107 	if (col)
108 	{
109 		attr = pango_attr_foreground_new (col->red, col->green, col->blue);
110 		attr->start_index = 0;
111 		attr->end_index = 0xffff;
112 		pango_attr_list_insert (list, attr);
113 	}
114 
115 	if (size > 0)
116 	{
117 		attr = pango_attr_scale_new (size == 1 ? PANGO_SCALE_SMALL : PANGO_SCALE_X_SMALL);
118 		attr->start_index = 0;
119 		attr->end_index = 0xffff;
120 		pango_attr_list_insert (list, attr);
121 	}
122 
123 	return list;
124 }
125 
126 static void
mg_create_tab_colors(void)127 mg_create_tab_colors (void)
128 {
129 	if (plain_list)
130 	{
131 		pango_attr_list_unref (plain_list);
132 		pango_attr_list_unref (newmsg_list);
133 		pango_attr_list_unref (newdata_list);
134 		pango_attr_list_unref (nickseen_list);
135 		pango_attr_list_unref (away_list);
136 	}
137 
138 	plain_list = mg_attr_list_create (NULL, prefs.hex_gui_tab_small);
139 	newdata_list = mg_attr_list_create (&colors[COL_NEW_DATA], prefs.hex_gui_tab_small);
140 	nickseen_list = mg_attr_list_create (&colors[COL_HILIGHT], prefs.hex_gui_tab_small);
141 	newmsg_list = mg_attr_list_create (&colors[COL_NEW_MSG], prefs.hex_gui_tab_small);
142 	away_list = mg_attr_list_create (&colors[COL_AWAY], FALSE);
143 }
144 
145 static void
set_window_urgency(GtkWidget * win,gboolean set)146 set_window_urgency (GtkWidget *win, gboolean set)
147 {
148 	gtk_window_set_urgency_hint (GTK_WINDOW (win), set);
149 }
150 
151 static void
flash_window(GtkWidget * win)152 flash_window (GtkWidget *win)
153 {
154 #ifdef HAVE_GTK_MAC
155 	gtkosx_application_attention_request (osx_app, INFO_REQUEST);
156 #endif
157 	set_window_urgency (win, TRUE);
158 }
159 
160 static void
unflash_window(GtkWidget * win)161 unflash_window (GtkWidget *win)
162 {
163 	set_window_urgency (win, FALSE);
164 }
165 
166 /* flash the taskbar button */
167 
168 void
fe_flash_window(session * sess)169 fe_flash_window (session *sess)
170 {
171 	if (fe_gui_info (sess, 0) != 1)	/* only do it if not focused */
172 		flash_window (sess->gui->window);
173 }
174 
175 /* set a tab plain, red, light-red, or blue */
176 
177 void
fe_set_tab_color(struct session * sess,int col)178 fe_set_tab_color (struct session *sess, int col)
179 {
180 	struct session *server_sess = sess->server->server_session;
181 	if (sess->res->tab && sess->gui->is_tab && (col == 0 || sess != current_tab))
182 	{
183 		switch (col)
184 		{
185 		case 0:	/* no particular color (theme default) */
186 			sess->tab_state = TAB_STATE_NONE;
187 			chan_set_color (sess->res->tab, plain_list);
188 			break;
189 		case 1:	/* new data has been displayed (dark red) */
190 			sess->tab_state = TAB_STATE_NEW_DATA;
191 			chan_set_color (sess->res->tab, newdata_list);
192 
193 			if (chan_is_collapsed (sess->res->tab)
194 				&& !((server_sess->tab_state & TAB_STATE_NEW_MSG)
195 					 || (server_sess->tab_state & TAB_STATE_NEW_HILIGHT))
196 				&& !(server_sess == current_tab))
197 			{
198 				server_sess->tab_state = TAB_STATE_NEW_DATA;
199 				chan_set_color (chan_get_parent (sess->res->tab), newdata_list);
200 			}
201 
202 			break;
203 		case 2:	/* new message arrived in channel (light red) */
204 			sess->tab_state = TAB_STATE_NEW_MSG;
205 			chan_set_color (sess->res->tab, newmsg_list);
206 
207 			if (chan_is_collapsed (sess->res->tab)
208 				&& !(server_sess->tab_state & TAB_STATE_NEW_HILIGHT)
209 				&& !(server_sess == current_tab))
210 			{
211 				server_sess->tab_state = TAB_STATE_NEW_MSG;
212 				chan_set_color (chan_get_parent (sess->res->tab), newmsg_list);
213 			}
214 
215 			break;
216 		case 3:	/* your nick has been seen (blue) */
217 			sess->tab_state = TAB_STATE_NEW_HILIGHT;
218 			chan_set_color (sess->res->tab, nickseen_list);
219 
220 			if (chan_is_collapsed (sess->res->tab) && !(server_sess == current_tab))
221 			{
222 				server_sess->tab_state = TAB_STATE_NEW_MSG;
223 				chan_set_color (chan_get_parent (sess->res->tab), nickseen_list);
224 			}
225 
226 			break;
227 		}
228 		lastact_update (sess);
229 		sess->last_tab_state = sess->tab_state; /* For plugins handling future prints */
230 	}
231 }
232 
233 static void
mg_set_myself_away(session_gui * gui,gboolean away)234 mg_set_myself_away (session_gui *gui, gboolean away)
235 {
236 	gtk_label_set_attributes (GTK_LABEL (gtk_bin_get_child (GTK_BIN (gui->nick_label))),
237 									  away ? away_list : NULL);
238 }
239 
240 /* change the little icon to the left of your nickname */
241 
242 void
mg_set_access_icon(session_gui * gui,GdkPixbuf * pix,gboolean away)243 mg_set_access_icon (session_gui *gui, GdkPixbuf *pix, gboolean away)
244 {
245 	if (gui->op_xpm)
246 	{
247 		if (pix == gtk_image_get_pixbuf (GTK_IMAGE (gui->op_xpm))) /* no change? */
248 		{
249 			mg_set_myself_away (gui, away);
250 			return;
251 		}
252 
253 		gtk_widget_destroy (gui->op_xpm);
254 		gui->op_xpm = NULL;
255 	}
256 
257 	if (pix && prefs.hex_gui_input_icon)
258 	{
259 		gui->op_xpm = gtk_image_new_from_pixbuf (pix);
260 		gtk_box_pack_start (GTK_BOX (gui->nick_box), gui->op_xpm, 0, 0, 0);
261 		gtk_widget_show (gui->op_xpm);
262 	}
263 
264 	mg_set_myself_away (gui, away);
265 }
266 
267 static gboolean
mg_inputbox_focus(GtkWidget * widget,GdkEventFocus * event,session_gui * gui)268 mg_inputbox_focus (GtkWidget *widget, GdkEventFocus *event, session_gui *gui)
269 {
270 	GSList *list;
271 	session *sess;
272 
273 	if (gui->is_tab)
274 		return FALSE;
275 
276 	list = sess_list;
277 	while (list)
278 	{
279 		sess = list->data;
280 		if (sess->gui == gui)
281 		{
282 			current_sess = sess;
283 			if (!sess->server->server_session)
284 				sess->server->server_session = sess;
285 			break;
286 		}
287 		list = list->next;
288 	}
289 
290 	return FALSE;
291 }
292 
293 void
mg_inputbox_cb(GtkWidget * igad,session_gui * gui)294 mg_inputbox_cb (GtkWidget *igad, session_gui *gui)
295 {
296 	char *cmd;
297 	static int ignore = FALSE;
298 	GSList *list;
299 	session *sess = NULL;
300 
301 	if (ignore)
302 		return;
303 
304 	cmd = SPELL_ENTRY_GET_TEXT (igad);
305 	if (cmd[0] == 0)
306 		return;
307 
308 	cmd = g_strdup (cmd);
309 
310 	/* avoid recursive loop */
311 	ignore = TRUE;
312 	SPELL_ENTRY_SET_TEXT (igad, "");
313 	ignore = FALSE;
314 
315 	/* where did this event come from? */
316 	if (gui->is_tab)
317 	{
318 		sess = current_tab;
319 	} else
320 	{
321 		list = sess_list;
322 		while (list)
323 		{
324 			sess = list->data;
325 			if (sess->gui == gui)
326 				break;
327 			list = list->next;
328 		}
329 		if (!list)
330 			sess = NULL;
331 	}
332 
333 	if (sess)
334 		handle_multiline (sess, cmd, TRUE, FALSE);
335 
336 	g_free (cmd);
337 }
338 
339 static gboolean
mg_spellcheck_cb(SexySpellEntry * entry,gchar * word,gpointer data)340 mg_spellcheck_cb (SexySpellEntry *entry, gchar *word, gpointer data)
341 {
342 	/* This can cause freezes on long words, nicks arn't very long anyway. */
343 	if (strlen (word) > 20)
344 		return TRUE;
345 
346 	/* Ignore anything we think is a valid url */
347 	if (url_check_word (word) != 0)
348 		return FALSE;
349 
350 	return TRUE;
351 }
352 
353 #if 0
354 static gboolean
355 has_key (char *modes)
356 {
357 	if (!modes)
358 		return FALSE;
359 	/* this is a crude check, but "-k" can't exist, so it works. */
360 	while (*modes)
361 	{
362 		if (*modes == 'k')
363 			return TRUE;
364 		if (*modes == ' ')
365 			return FALSE;
366 		modes++;
367 	}
368 	return FALSE;
369 }
370 #endif
371 
372 void
fe_set_title(session * sess)373 fe_set_title (session *sess)
374 {
375 	char tbuf[512];
376 	int type;
377 
378 	if (sess->gui->is_tab && sess != current_tab)
379 		return;
380 
381 	type = sess->type;
382 
383 	if (sess->server->connected == FALSE && sess->type != SESS_DIALOG)
384 		goto def;
385 
386 	switch (type)
387 	{
388 	case SESS_DIALOG:
389 		g_snprintf (tbuf, sizeof (tbuf), "%s %s @ %s - %s",
390 					 _("Dialog with"), sess->channel, server_get_network (sess->server, TRUE),
391 					 _(DISPLAY_NAME));
392 		break;
393 	case SESS_SERVER:
394 		g_snprintf (tbuf, sizeof (tbuf), "%s @ %s - %s",
395 					 sess->server->nick, server_get_network (sess->server, TRUE),
396 					 _(DISPLAY_NAME));
397 		break;
398 	case SESS_CHANNEL:
399 		/* don't display keys in the titlebar */
400 		if (prefs.hex_gui_win_modes)
401 		{
402 			g_snprintf (tbuf, sizeof (tbuf),
403 						 "%s @ %s / %s (%s) - %s",
404 						 sess->server->nick, server_get_network (sess->server, TRUE),
405 						 sess->channel, sess->current_modes ? sess->current_modes : "",
406 						 _(DISPLAY_NAME));
407 		}
408 		else
409 		{
410 			g_snprintf (tbuf, sizeof (tbuf),
411 						 "%s @ %s / %s - %s",
412 						 sess->server->nick, server_get_network (sess->server, TRUE),
413 						 sess->channel, _(DISPLAY_NAME));
414 		}
415 		if (prefs.hex_gui_win_ucount)
416 		{
417 			g_snprintf (tbuf + strlen (tbuf), 9, " (%d)", sess->total);
418 		}
419 		break;
420 	case SESS_NOTICES:
421 	case SESS_SNOTICES:
422 		g_snprintf (tbuf, sizeof (tbuf), "%s @ %s (notices) - %s",
423 					 sess->server->nick, server_get_network (sess->server, TRUE),
424 					 _(DISPLAY_NAME));
425 		break;
426 	default:
427 	def:
428 		g_snprintf (tbuf, sizeof (tbuf), _(DISPLAY_NAME));
429 		gtk_window_set_title (GTK_WINDOW (sess->gui->window), tbuf);
430 		return;
431 	}
432 
433 	gtk_window_set_title (GTK_WINDOW (sess->gui->window), tbuf);
434 }
435 
436 static gboolean
mg_windowstate_cb(GtkWindow * wid,GdkEventWindowState * event,gpointer userdata)437 mg_windowstate_cb (GtkWindow *wid, GdkEventWindowState *event, gpointer userdata)
438 {
439 	if ((event->changed_mask & GDK_WINDOW_STATE_ICONIFIED) &&
440 		 (event->new_window_state & GDK_WINDOW_STATE_ICONIFIED) &&
441 		 prefs.hex_gui_tray_minimize && prefs.hex_gui_tray &&
442 		 gtkutil_tray_icon_supported (wid))
443 	{
444 		tray_toggle_visibility (TRUE);
445 		gtk_window_deiconify (wid);
446 	}
447 
448 	prefs.hex_gui_win_state = 0;
449 	if (event->new_window_state & GDK_WINDOW_STATE_MAXIMIZED)
450 		prefs.hex_gui_win_state = 1;
451 
452 	prefs.hex_gui_win_fullscreen = 0;
453 	if (event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN)
454 		prefs.hex_gui_win_fullscreen = 1;
455 
456 	menu_set_fullscreen (current_sess->gui, prefs.hex_gui_win_fullscreen);
457 
458 	return FALSE;
459 }
460 
461 static gboolean
mg_configure_cb(GtkWidget * wid,GdkEventConfigure * event,session * sess)462 mg_configure_cb (GtkWidget *wid, GdkEventConfigure *event, session *sess)
463 {
464 	if (sess == NULL)			/* for the main_window */
465 	{
466 		if (mg_gui)
467 		{
468 			if (prefs.hex_gui_win_save && !prefs.hex_gui_win_state && !prefs.hex_gui_win_fullscreen)
469 			{
470 				sess = current_sess;
471 				gtk_window_get_position (GTK_WINDOW (wid), &prefs.hex_gui_win_left,
472 												 &prefs.hex_gui_win_top);
473 				gtk_window_get_size (GTK_WINDOW (wid), &prefs.hex_gui_win_width,
474 											&prefs.hex_gui_win_height);
475 			}
476 		}
477 	}
478 
479 	if (sess)
480 	{
481 		if (sess->type == SESS_DIALOG && prefs.hex_gui_win_save)
482 		{
483 			gtk_window_get_position (GTK_WINDOW (wid), &prefs.hex_gui_dialog_left,
484 											 &prefs.hex_gui_dialog_top);
485 			gtk_window_get_size (GTK_WINDOW (wid), &prefs.hex_gui_dialog_width,
486 										&prefs.hex_gui_dialog_height);
487 		}
488 	}
489 
490 	return FALSE;
491 }
492 
493 /* move to a non-irc tab */
494 
495 static void
mg_show_generic_tab(GtkWidget * box)496 mg_show_generic_tab (GtkWidget *box)
497 {
498 	int num;
499 	GtkWidget *f = NULL;
500 
501 	if (current_sess && gtk_widget_has_focus (current_sess->gui->input_box))
502 		f = current_sess->gui->input_box;
503 
504 	num = gtk_notebook_page_num (GTK_NOTEBOOK (mg_gui->note_book), box);
505 	gtk_notebook_set_current_page (GTK_NOTEBOOK (mg_gui->note_book), num);
506 	gtk_tree_view_set_model (GTK_TREE_VIEW (mg_gui->user_tree), NULL);
507 	gtk_window_set_title (GTK_WINDOW (mg_gui->window),
508 								 g_object_get_data (G_OBJECT (box), "title"));
509 	gtk_widget_set_sensitive (mg_gui->menu, FALSE);
510 
511 	if (f)
512 		gtk_widget_grab_focus (f);
513 }
514 
515 /* a channel has been focused */
516 
517 static void
mg_focus(session * sess)518 mg_focus (session *sess)
519 {
520 	if (sess->gui->is_tab)
521 		current_tab = sess;
522 	current_sess = sess;
523 
524 	/* dirty trick to avoid auto-selection */
525 	SPELL_ENTRY_SET_EDITABLE (sess->gui->input_box, FALSE);
526 	gtk_widget_grab_focus (sess->gui->input_box);
527 	SPELL_ENTRY_SET_EDITABLE (sess->gui->input_box, TRUE);
528 
529 	sess->server->front_session = sess;
530 
531 	if (sess->server->server_session != NULL)
532 	{
533 		if (sess->server->server_session->type != SESS_SERVER)
534 			sess->server->server_session = sess;
535 	} else
536 	{
537 		sess->server->server_session = sess;
538 	}
539 
540 	/* when called via mg_changui_new, is_tab might be true, but
541 		sess->res->tab is still NULL. */
542 	if (sess->res->tab)
543 		fe_set_tab_color (sess, 0);
544 }
545 
546 static int
mg_progressbar_update(GtkWidget * bar)547 mg_progressbar_update (GtkWidget *bar)
548 {
549 	static int type = 0;
550 	static gdouble pos = 0;
551 
552 	pos += 0.05;
553 	if (pos >= 0.99)
554 	{
555 		if (type == 0)
556 		{
557 			type = 1;
558 			gtk_progress_bar_set_orientation ((GtkProgressBar *) bar,
559 														 GTK_PROGRESS_RIGHT_TO_LEFT);
560 		} else
561 		{
562 			type = 0;
563 			gtk_progress_bar_set_orientation ((GtkProgressBar *) bar,
564 														 GTK_PROGRESS_LEFT_TO_RIGHT);
565 		}
566 		pos = 0.05;
567 	}
568 	gtk_progress_bar_set_fraction ((GtkProgressBar *) bar, pos);
569 	return 1;
570 }
571 
572 void
mg_progressbar_create(session_gui * gui)573 mg_progressbar_create (session_gui *gui)
574 {
575 	gui->bar = gtk_progress_bar_new ();
576 	gtk_box_pack_start (GTK_BOX (gui->nick_box), gui->bar, 0, 0, 0);
577 	gtk_widget_show (gui->bar);
578 	gui->bartag = fe_timeout_add (50, mg_progressbar_update, gui->bar);
579 }
580 
581 void
mg_progressbar_destroy(session_gui * gui)582 mg_progressbar_destroy (session_gui *gui)
583 {
584 	fe_timeout_remove (gui->bartag);
585 	gtk_widget_destroy (gui->bar);
586 	gui->bar = 0;
587 	gui->bartag = 0;
588 }
589 
590 /* switching tabs away from this one, so remember some info about it! */
591 
592 static void
mg_unpopulate(session * sess)593 mg_unpopulate (session *sess)
594 {
595 	restore_gui *res;
596 	session_gui *gui;
597 	int i;
598 
599 	gui = sess->gui;
600 	res = sess->res;
601 
602 	res->input_text = g_strdup (SPELL_ENTRY_GET_TEXT (gui->input_box));
603 	res->topic_text = g_strdup (gtk_entry_get_text (GTK_ENTRY (gui->topic_entry)));
604 	res->limit_text = g_strdup (gtk_entry_get_text (GTK_ENTRY (gui->limit_entry)));
605 	res->key_text = g_strdup (gtk_entry_get_text (GTK_ENTRY (gui->key_entry)));
606 	if (gui->laginfo)
607 		res->lag_text = g_strdup (gtk_label_get_text (GTK_LABEL (gui->laginfo)));
608 	if (gui->throttleinfo)
609 		res->queue_text = g_strdup (gtk_label_get_text (GTK_LABEL (gui->throttleinfo)));
610 
611 	for (i = 0; i < NUM_FLAG_WIDS - 1; i++)
612 		res->flag_wid_state[i] = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (gui->flag_wid[i]));
613 
614 	res->old_ul_value = userlist_get_value (gui->user_tree);
615 	if (gui->lagometer)
616 		res->lag_value = gtk_progress_bar_get_fraction (
617 													GTK_PROGRESS_BAR (gui->lagometer));
618 	if (gui->throttlemeter)
619 		res->queue_value = gtk_progress_bar_get_fraction (
620 													GTK_PROGRESS_BAR (gui->throttlemeter));
621 
622 	if (gui->bar)
623 	{
624 		res->c_graph = TRUE;	/* still have a graph, just not visible now */
625 		mg_progressbar_destroy (gui);
626 	}
627 }
628 
629 static void
mg_restore_label(GtkWidget * label,char ** text)630 mg_restore_label (GtkWidget *label, char **text)
631 {
632 	if (!label)
633 		return;
634 
635 	if (*text)
636 	{
637 		gtk_label_set_text (GTK_LABEL (label), *text);
638 		g_free (*text);
639 		*text = NULL;
640 	} else
641 	{
642 		gtk_label_set_text (GTK_LABEL (label), "");
643 	}
644 }
645 
646 static void
mg_restore_entry(GtkWidget * entry,char ** text)647 mg_restore_entry (GtkWidget *entry, char **text)
648 {
649 	if (*text)
650 	{
651 		gtk_entry_set_text (GTK_ENTRY (entry), *text);
652 		g_free (*text);
653 		*text = NULL;
654 	} else
655 	{
656 		gtk_entry_set_text (GTK_ENTRY (entry), "");
657 	}
658 	gtk_editable_set_position (GTK_EDITABLE (entry), -1);
659 }
660 
661 static void
mg_restore_speller(GtkWidget * entry,char ** text)662 mg_restore_speller (GtkWidget *entry, char **text)
663 {
664 	if (*text)
665 	{
666 		SPELL_ENTRY_SET_TEXT (entry, *text);
667 		g_free (*text);
668 		*text = NULL;
669 	} else
670 	{
671 		SPELL_ENTRY_SET_TEXT (entry, "");
672 	}
673 	SPELL_ENTRY_SET_POS (entry, -1);
674 }
675 
676 void
mg_set_topic_tip(session * sess)677 mg_set_topic_tip (session *sess)
678 {
679 	char *text;
680 
681 	switch (sess->type)
682 	{
683 	case SESS_CHANNEL:
684 		if (sess->topic)
685 		{
686 			text = g_strdup_printf (_("Topic for %s is: %s"), sess->channel,
687 						 sess->topic);
688 			gtk_widget_set_tooltip_text (sess->gui->topic_entry, text);
689 			g_free (text);
690 		} else
691 			gtk_widget_set_tooltip_text (sess->gui->topic_entry, _("No topic is set"));
692 		break;
693 	default:
694 		if (gtk_entry_get_text (GTK_ENTRY (sess->gui->topic_entry)) &&
695 			 gtk_entry_get_text (GTK_ENTRY (sess->gui->topic_entry))[0])
696 			gtk_widget_set_tooltip_text (sess->gui->topic_entry, (char *)gtk_entry_get_text (GTK_ENTRY (sess->gui->topic_entry)));
697 		else
698 			gtk_widget_set_tooltip_text (sess->gui->topic_entry, NULL);
699 	}
700 }
701 
702 static void
mg_hide_empty_pane(GtkPaned * pane)703 mg_hide_empty_pane (GtkPaned *pane)
704 {
705 	if ((gtk_paned_get_child1 (pane) == NULL || !gtk_widget_get_visible (gtk_paned_get_child1 (pane))) &&
706 		(gtk_paned_get_child2 (pane) == NULL || !gtk_widget_get_visible (gtk_paned_get_child2 (pane))))
707 	{
708 		gtk_widget_hide (GTK_WIDGET (pane));
709 		return;
710 	}
711 
712 	gtk_widget_show (GTK_WIDGET (pane));
713 }
714 
715 static void
mg_hide_empty_boxes(session_gui * gui)716 mg_hide_empty_boxes (session_gui *gui)
717 {
718 	/* hide empty vpanes - so the handle is not shown */
719 	mg_hide_empty_pane ((GtkPaned*)gui->vpane_right);
720 	mg_hide_empty_pane ((GtkPaned*)gui->vpane_left);
721 }
722 
723 static void
mg_userlist_showhide(session * sess,int show)724 mg_userlist_showhide (session *sess, int show)
725 {
726 	session_gui *gui = sess->gui;
727 	int handle_size;
728 	int right_size;
729 	GtkAllocation allocation;
730 
731 	right_size = MAX (prefs.hex_gui_pane_right_size, prefs.hex_gui_pane_right_size_min);
732 
733 	if (show)
734 	{
735 		gtk_widget_show (gui->user_box);
736 		gui->ul_hidden = 0;
737 
738 		gtk_widget_get_allocation (gui->hpane_right, &allocation);
739 		gtk_widget_style_get (GTK_WIDGET (gui->hpane_right), "handle-size", &handle_size, NULL);
740 		gtk_paned_set_position (GTK_PANED (gui->hpane_right), allocation.width - (right_size + handle_size));
741 	}
742 	else
743 	{
744 		gtk_widget_hide (gui->user_box);
745 		gui->ul_hidden = 1;
746 	}
747 
748 	mg_hide_empty_boxes (gui);
749 }
750 
751 static gboolean
mg_is_userlist_and_tree_combined(void)752 mg_is_userlist_and_tree_combined (void)
753 {
754 	if (prefs.hex_gui_tab_pos == POS_TOPLEFT && prefs.hex_gui_ulist_pos == POS_BOTTOMLEFT)
755 		return TRUE;
756 	if (prefs.hex_gui_tab_pos == POS_BOTTOMLEFT && prefs.hex_gui_ulist_pos == POS_TOPLEFT)
757 		return TRUE;
758 
759 	if (prefs.hex_gui_tab_pos == POS_TOPRIGHT && prefs.hex_gui_ulist_pos == POS_BOTTOMRIGHT)
760 		return TRUE;
761 	if (prefs.hex_gui_tab_pos == POS_BOTTOMRIGHT && prefs.hex_gui_ulist_pos == POS_TOPRIGHT)
762 		return TRUE;
763 
764 	return FALSE;
765 }
766 
767 /* decide if the userlist should be shown or hidden for this tab */
768 
769 void
mg_decide_userlist(session * sess,gboolean switch_to_current)770 mg_decide_userlist (session *sess, gboolean switch_to_current)
771 {
772 	/* when called from menu.c we need this */
773 	if (sess->gui == mg_gui && switch_to_current)
774 		sess = current_tab;
775 
776 	if (prefs.hex_gui_ulist_hide)
777 	{
778 		mg_userlist_showhide (sess, FALSE);
779 		return;
780 	}
781 
782 	switch (sess->type)
783 	{
784 	case SESS_SERVER:
785 	case SESS_DIALOG:
786 	case SESS_NOTICES:
787 	case SESS_SNOTICES:
788 		if (mg_is_userlist_and_tree_combined ())
789 			mg_userlist_showhide (sess, TRUE);	/* show */
790 		else
791 			mg_userlist_showhide (sess, FALSE);	/* hide */
792 		break;
793 	default:
794 		mg_userlist_showhide (sess, TRUE);	/* show */
795 	}
796 }
797 
798 static int ul_tag = 0;
799 
800 static gboolean
mg_populate_userlist(session * sess)801 mg_populate_userlist (session *sess)
802 {
803 	if (!sess)
804 		sess = current_tab;
805 
806 	if (is_session (sess))
807 	{
808 		if (sess->type == SESS_DIALOG)
809 			mg_set_access_icon (sess->gui, NULL, sess->server->is_away);
810 		else
811 			mg_set_access_icon (sess->gui, get_user_icon (sess->server, sess->me), sess->server->is_away);
812 		userlist_show (sess);
813 		userlist_set_value (sess->gui->user_tree, sess->res->old_ul_value);
814 	}
815 
816 	ul_tag = 0;
817 	return 0;
818 }
819 
820 /* fill the irc tab with a new channel */
821 
822 static void
mg_populate(session * sess)823 mg_populate (session *sess)
824 {
825 	session_gui *gui = sess->gui;
826 	restore_gui *res = sess->res;
827 	int i, render = TRUE;
828 	guint16 vis = gui->ul_hidden;
829 	GtkAllocation allocation;
830 
831 	switch (sess->type)
832 	{
833 	case SESS_DIALOG:
834 		/* show the dialog buttons */
835 		gtk_widget_show (gui->dialogbutton_box);
836 		/* hide the chan-mode buttons */
837 		gtk_widget_hide (gui->topicbutton_box);
838 		/* hide the userlist */
839 		mg_decide_userlist (sess, FALSE);
840 		/* shouldn't edit the topic */
841 		gtk_editable_set_editable (GTK_EDITABLE (gui->topic_entry), FALSE);
842 		/* might be hidden from server tab */
843 		if (prefs.hex_gui_topicbar)
844 			gtk_widget_show (gui->topic_bar);
845 		break;
846 	case SESS_SERVER:
847 		if (prefs.hex_gui_mode_buttons)
848 			gtk_widget_show (gui->topicbutton_box);
849 		/* hide the dialog buttons */
850 		gtk_widget_hide (gui->dialogbutton_box);
851 		/* hide the userlist */
852 		mg_decide_userlist (sess, FALSE);
853 		/* servers don't have topics */
854 		gtk_widget_hide (gui->topic_bar);
855 		break;
856 	default:
857 		/* hide the dialog buttons */
858 		gtk_widget_hide (gui->dialogbutton_box);
859 		if (prefs.hex_gui_mode_buttons)
860 			gtk_widget_show (gui->topicbutton_box);
861 		/* show the userlist */
862 		mg_decide_userlist (sess, FALSE);
863 		/* let the topic be editted */
864 		gtk_editable_set_editable (GTK_EDITABLE (gui->topic_entry), TRUE);
865 		if (prefs.hex_gui_topicbar)
866 			gtk_widget_show (gui->topic_bar);
867 	}
868 
869 	/* move to THE irc tab */
870 	if (gui->is_tab)
871 		gtk_notebook_set_current_page (GTK_NOTEBOOK (gui->note_book), 0);
872 
873 	/* xtext size change? Then don't render, wait for the expose caused
874       by showing/hidding the userlist */
875 	gtk_widget_get_allocation (gui->user_box, &allocation);
876 	if (vis != gui->ul_hidden && allocation.width > 1)
877 		render = FALSE;
878 
879 	gtk_xtext_buffer_show (GTK_XTEXT (gui->xtext), res->buffer, render);
880 
881 	if (gui->is_tab)
882 		gtk_widget_set_sensitive (gui->menu, TRUE);
883 
884 	/* restore all the GtkEntry's */
885 	mg_restore_entry (gui->topic_entry, &res->topic_text);
886 	mg_restore_speller (gui->input_box, &res->input_text);
887 	mg_restore_entry (gui->key_entry, &res->key_text);
888 	mg_restore_entry (gui->limit_entry, &res->limit_text);
889 	mg_restore_label (gui->laginfo, &res->lag_text);
890 	mg_restore_label (gui->throttleinfo, &res->queue_text);
891 
892 	mg_focus (sess);
893 	fe_set_title (sess);
894 
895 	/* this one flickers, so only change if necessary */
896 	if (strcmp (sess->server->nick, gtk_button_get_label (GTK_BUTTON (gui->nick_label))) != 0)
897 		gtk_button_set_label (GTK_BUTTON (gui->nick_label), sess->server->nick);
898 
899 	/* this is slow, so make it a timeout event */
900 	if (!gui->is_tab)
901 	{
902 		mg_populate_userlist (sess);
903 	} else
904 	{
905 		if (ul_tag == 0)
906 			ul_tag = g_idle_add ((GSourceFunc)mg_populate_userlist, NULL);
907 	}
908 
909 	fe_userlist_numbers (sess);
910 
911 	/* restore all the channel mode buttons */
912 	ignore_chanmode = TRUE;
913 	for (i = 0; i < NUM_FLAG_WIDS - 1; i++)
914 	{
915 		/* Hide if mode not supported */
916 		if (sess->server && strchr (sess->server->chanmodes, chan_flags[i]) == NULL)
917 			gtk_widget_hide (sess->gui->flag_wid[i]);
918 		else
919 			gtk_widget_show (sess->gui->flag_wid[i]);
920 
921 		/* Update state */
922 		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gui->flag_wid[i]),
923 									res->flag_wid_state[i]);
924 	}
925 	ignore_chanmode = FALSE;
926 
927 	if (gui->lagometer)
928 	{
929 		gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (gui->lagometer),
930 												 res->lag_value);
931 		if (res->lag_tip)
932 			gtk_widget_set_tooltip_text (gtk_widget_get_parent (sess->gui->lagometer), res->lag_tip);
933 	}
934 	if (gui->throttlemeter)
935 	{
936 		gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (gui->throttlemeter),
937 												 res->queue_value);
938 		if (res->queue_tip)
939 			gtk_widget_set_tooltip_text (gtk_widget_get_parent (sess->gui->throttlemeter), res->queue_tip);
940 	}
941 
942 	/* did this tab have a connecting graph? restore it.. */
943 	if (res->c_graph)
944 	{
945 		res->c_graph = FALSE;
946 		mg_progressbar_create (gui);
947 	}
948 
949 	/* menu items */
950 	menu_set_away (gui, sess->server->is_away);
951 	gtk_widget_set_sensitive (gui->menu_item[MENU_ID_AWAY], sess->server->connected);
952 	gtk_widget_set_sensitive (gui->menu_item[MENU_ID_JOIN], sess->server->end_of_motd);
953 	gtk_widget_set_sensitive (gui->menu_item[MENU_ID_DISCONNECT],
954 									  sess->server->connected || sess->server->recondelay_tag);
955 
956 	mg_set_topic_tip (sess);
957 
958 	plugin_emit_dummy_print (sess, "Focus Tab");
959 }
960 
961 void
mg_bring_tofront_sess(session * sess)962 mg_bring_tofront_sess (session *sess)	/* IRC tab or window */
963 {
964 	if (sess->gui->is_tab)
965 		chan_focus (sess->res->tab);
966 	else
967 		gtk_window_present (GTK_WINDOW (sess->gui->window));
968 }
969 
970 void
mg_bring_tofront(GtkWidget * vbox)971 mg_bring_tofront (GtkWidget *vbox)	/* non-IRC tab or window */
972 {
973 	chan *ch;
974 
975 	ch = g_object_get_data (G_OBJECT (vbox), "ch");
976 	if (ch)
977 		chan_focus (ch);
978 	else
979 		gtk_window_present (GTK_WINDOW (gtk_widget_get_toplevel (vbox)));
980 }
981 
982 void
mg_switch_page(int relative,int num)983 mg_switch_page (int relative, int num)
984 {
985 	if (mg_gui)
986 		chanview_move_focus (mg_gui->chanview, relative, num);
987 }
988 
989 /* a toplevel IRC window was destroyed */
990 
991 static void
mg_topdestroy_cb(GtkWidget * win,session * sess)992 mg_topdestroy_cb (GtkWidget *win, session *sess)
993 {
994 /*	printf("enter mg_topdestroy. sess %p was destroyed\n", sess);*/
995 	session_free (sess);	/* tell hexchat.c about it */
996 }
997 
998 /* cleanup an IRC tab */
999 
1000 static void
mg_ircdestroy(session * sess)1001 mg_ircdestroy (session *sess)
1002 {
1003 	GSList *list;
1004 
1005 	session_free (sess);	/* tell hexchat.c about it */
1006 
1007 	if (mg_gui == NULL)
1008 	{
1009 /*		puts("-> mg_gui is already NULL");*/
1010 		return;
1011 	}
1012 
1013 	list = sess_list;
1014 	while (list)
1015 	{
1016 		sess = list->data;
1017 		if (sess->gui->is_tab)
1018 		{
1019 /*			puts("-> some tabs still remain");*/
1020 			return;
1021 		}
1022 		list = list->next;
1023 	}
1024 
1025 /*	puts("-> no tabs left, killing main tabwindow");*/
1026 	gtk_widget_destroy (mg_gui->window);
1027 	active_tab = NULL;
1028 	mg_gui = NULL;
1029 	parent_window = NULL;
1030 }
1031 
1032 static void
mg_tab_close_cb(GtkWidget * dialog,gint arg1,session * sess)1033 mg_tab_close_cb (GtkWidget *dialog, gint arg1, session *sess)
1034 {
1035 	GSList *list, *next;
1036 
1037 	gtk_widget_destroy (dialog);
1038 	if (arg1 == GTK_RESPONSE_OK && is_session (sess))
1039 	{
1040 		/* force it NOT to send individual PARTs */
1041 		sess->server->sent_quit = TRUE;
1042 
1043 		for (list = sess_list; list;)
1044 		{
1045 			next = list->next;
1046 			if (((session *)list->data)->server == sess->server &&
1047 				 ((session *)list->data) != sess)
1048 				fe_close_window ((session *)list->data);
1049 			list = next;
1050 		}
1051 
1052 		/* just send one QUIT - better for BNCs */
1053 		sess->server->sent_quit = FALSE;
1054 		fe_close_window (sess);
1055 	}
1056 }
1057 
1058 void
mg_tab_close(session * sess)1059 mg_tab_close (session *sess)
1060 {
1061 	GtkWidget *dialog;
1062 	GSList *list;
1063 	int i;
1064 
1065 	if (chan_remove (sess->res->tab, FALSE))
1066 	{
1067 		sess->res->tab = NULL;
1068 		mg_ircdestroy (sess);
1069 	}
1070 	else
1071 	{
1072 		for (i = 0, list = sess_list; list; list = list->next)
1073 		{
1074 			session *s = (session*)list->data;
1075 			if (s->server == sess->server && (s->type == SESS_CHANNEL || s->type == SESS_DIALOG))
1076 				i++;
1077 		}
1078 		dialog = gtk_message_dialog_new (GTK_WINDOW (parent_window), 0,
1079 						GTK_MESSAGE_WARNING, GTK_BUTTONS_OK_CANCEL,
1080 						_("This server still has %d channels or dialogs associated with it. "
1081 						  "Close them all?"), i);
1082 		g_signal_connect (G_OBJECT (dialog), "response",
1083 								G_CALLBACK (mg_tab_close_cb), sess);
1084 		if (prefs.hex_gui_tab_layout)
1085 		{
1086 			gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
1087 		}
1088 		else
1089 		{
1090 			gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER_ON_PARENT);
1091 		}
1092 		gtk_widget_show (dialog);
1093 	}
1094 }
1095 
1096 static void
mg_menu_destroy(GtkWidget * menu,gpointer userdata)1097 mg_menu_destroy (GtkWidget *menu, gpointer userdata)
1098 {
1099 	gtk_widget_destroy (menu);
1100 	g_object_unref (menu);
1101 }
1102 
1103 void
mg_create_icon_item(char * label,char * stock,GtkWidget * menu,void * callback,void * userdata)1104 mg_create_icon_item (char *label, char *stock, GtkWidget *menu,
1105 							void *callback, void *userdata)
1106 {
1107 	GtkWidget *item;
1108 
1109 	item = create_icon_menu (label, stock, TRUE);
1110 	gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1111 	g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (callback),
1112 							userdata);
1113 	gtk_widget_show (item);
1114 }
1115 
1116 static int
mg_count_networks(void)1117 mg_count_networks (void)
1118 {
1119 	int cons = 0;
1120 	GSList *list;
1121 
1122 	for (list = serv_list; list; list = list->next)
1123 	{
1124 		if (((server *)list->data)->connected)
1125 			cons++;
1126 	}
1127 	return cons;
1128 }
1129 
1130 static int
mg_count_dccs(void)1131 mg_count_dccs (void)
1132 {
1133 	GSList *list;
1134 	struct DCC *dcc;
1135 	int dccs = 0;
1136 
1137 	list = dcc_list;
1138 	while (list)
1139 	{
1140 		dcc = list->data;
1141 		if ((dcc->type == TYPE_SEND || dcc->type == TYPE_RECV) &&
1142 			 dcc->dccstat == STAT_ACTIVE)
1143 			dccs++;
1144 		list = list->next;
1145 	}
1146 
1147 	return dccs;
1148 }
1149 
1150 void
mg_open_quit_dialog(gboolean minimize_button)1151 mg_open_quit_dialog (gboolean minimize_button)
1152 {
1153 	static GtkWidget *dialog = NULL;
1154 	GtkWidget *dialog_vbox1;
1155 	GtkWidget *table1;
1156 	GtkWidget *image;
1157 	GtkWidget *checkbutton1;
1158 	GtkWidget *label;
1159 	GtkWidget *dialog_action_area1;
1160 	GtkWidget *button;
1161 	char *text, *connecttext;
1162 	int cons;
1163 	int dccs;
1164 
1165 	if (dialog)
1166 	{
1167 		gtk_window_present (GTK_WINDOW (dialog));
1168 		return;
1169 	}
1170 
1171 	dccs = mg_count_dccs ();
1172 	cons = mg_count_networks ();
1173 	if (dccs + cons == 0 || !prefs.hex_gui_quit_dialog)
1174 	{
1175 		hexchat_exit ();
1176 		return;
1177 	}
1178 
1179 	dialog = gtk_dialog_new ();
1180 	gtk_container_set_border_width (GTK_CONTAINER (dialog), 6);
1181 	gtk_window_set_title (GTK_WINDOW (dialog), _("Quit HexChat?"));
1182 	gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (parent_window));
1183 	gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
1184 
1185 	dialog_vbox1 = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
1186 	gtk_widget_show (dialog_vbox1);
1187 
1188 	table1 = gtk_table_new (2, 2, FALSE);
1189 	gtk_widget_show (table1);
1190 	gtk_box_pack_start (GTK_BOX (dialog_vbox1), table1, TRUE, TRUE, 0);
1191 	gtk_container_set_border_width (GTK_CONTAINER (table1), 6);
1192 	gtk_table_set_row_spacings (GTK_TABLE (table1), 12);
1193 	gtk_table_set_col_spacings (GTK_TABLE (table1), 12);
1194 
1195 	image = gtk_image_new_from_stock ("gtk-dialog-warning", GTK_ICON_SIZE_DIALOG);
1196 	gtk_widget_show (image);
1197 	gtk_table_attach (GTK_TABLE (table1), image, 0, 1, 0, 1,
1198 							(GtkAttachOptions) (GTK_FILL),
1199 							(GtkAttachOptions) (GTK_FILL), 0, 0);
1200 
1201 	checkbutton1 = gtk_check_button_new_with_mnemonic (_("Don't ask next time."));
1202 	gtk_widget_show (checkbutton1);
1203 	gtk_table_attach (GTK_TABLE (table1), checkbutton1, 0, 2, 1, 2,
1204 							(GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
1205 							(GtkAttachOptions) (0), 0, 4);
1206 
1207 	connecttext = g_strdup_printf (_("You are connected to %i IRC networks."), cons);
1208 	text = g_strdup_printf ("<span weight=\"bold\" size=\"larger\">%s</span>\n\n%s\n%s",
1209 								_("Are you sure you want to quit?"),
1210 								cons ? connecttext : "",
1211 								dccs ? _("Some file transfers are still active.") : "");
1212 	g_free (connecttext);
1213 	label = gtk_label_new (text);
1214 	g_free (text);
1215 	gtk_widget_show (label);
1216 	gtk_table_attach (GTK_TABLE (table1), label, 1, 2, 0, 1,
1217 							(GtkAttachOptions) (GTK_EXPAND | GTK_SHRINK | GTK_FILL),
1218 							(GtkAttachOptions) (GTK_EXPAND | GTK_SHRINK), 0, 0);
1219 	gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
1220 	gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
1221 
1222 	dialog_action_area1 = gtk_dialog_get_action_area (GTK_DIALOG (dialog));
1223 	gtk_widget_show (dialog_action_area1);
1224 	gtk_button_box_set_layout (GTK_BUTTON_BOX (dialog_action_area1),
1225 										GTK_BUTTONBOX_END);
1226 
1227 	if (minimize_button && gtkutil_tray_icon_supported (GTK_WINDOW(dialog)))
1228 	{
1229 		button = gtk_button_new_with_mnemonic (_("_Minimize to Tray"));
1230 		gtk_widget_show (button);
1231 		gtk_dialog_add_action_widget (GTK_DIALOG (dialog), button, 1);
1232 	}
1233 
1234 	button = gtk_button_new_from_stock ("gtk-cancel");
1235 	gtk_widget_show (button);
1236 	gtk_dialog_add_action_widget (GTK_DIALOG (dialog), button,
1237 											GTK_RESPONSE_CANCEL);
1238 	gtk_widget_grab_focus (button);
1239 
1240 	button = gtk_button_new_from_stock ("gtk-quit");
1241 	gtk_widget_show (button);
1242 	gtk_dialog_add_action_widget (GTK_DIALOG (dialog), button, 0);
1243 
1244 	gtk_widget_show (dialog);
1245 
1246 	switch (gtk_dialog_run (GTK_DIALOG (dialog)))
1247 	{
1248 	case 0:
1249 		if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (checkbutton1)))
1250 			prefs.hex_gui_quit_dialog = 0;
1251 		hexchat_exit ();
1252 		break;
1253 	case 1: /* minimize to tray */
1254 		if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (checkbutton1)))
1255 		{
1256 			prefs.hex_gui_tray_close = 1;
1257 			/*prefs.hex_gui_quit_dialog = 0;*/
1258 		}
1259 		/* force tray icon ON, if not already */
1260 		if (!prefs.hex_gui_tray)
1261 		{
1262 			prefs.hex_gui_tray = 1;
1263 			tray_apply_setup ();
1264 		}
1265 		tray_toggle_visibility (TRUE);
1266 		break;
1267 	}
1268 
1269 	gtk_widget_destroy (dialog);
1270 	dialog = NULL;
1271 }
1272 
1273 void
mg_close_sess(session * sess)1274 mg_close_sess (session *sess)
1275 {
1276 	if (sess_list->next == NULL)
1277 	{
1278 		mg_open_quit_dialog (FALSE);
1279 		return;
1280 	}
1281 
1282 	fe_close_window (sess);
1283 }
1284 
1285 static int
mg_chan_remove(chan * ch)1286 mg_chan_remove (chan *ch)
1287 {
1288 	/* remove the tab from chanview */
1289 	chan_remove (ch, TRUE);
1290 	/* any tabs left? */
1291 	if (chanview_get_size (mg_gui->chanview) < 1)
1292 	{
1293 		/* if not, destroy the main tab window */
1294 		gtk_widget_destroy (mg_gui->window);
1295 		current_tab = NULL;
1296 		active_tab = NULL;
1297 		mg_gui = NULL;
1298 		parent_window = NULL;
1299 		return TRUE;
1300 	}
1301 	return FALSE;
1302 }
1303 
1304 /* destroy non-irc tab/window */
1305 
1306 static void
mg_close_gen(chan * ch,GtkWidget * box)1307 mg_close_gen (chan *ch, GtkWidget *box)
1308 {
1309 	if (!ch)
1310 		ch = g_object_get_data (G_OBJECT (box), "ch");
1311 	if (ch)
1312 	{
1313 		/* remove from notebook */
1314 		gtk_widget_destroy (box);
1315 		/* remove the tab from chanview */
1316 		mg_chan_remove (ch);
1317 	} else
1318 	{
1319 		gtk_widget_destroy (gtk_widget_get_toplevel (box));
1320 	}
1321 }
1322 
1323 /* the "X" close button has been pressed (tab-view) */
1324 
1325 static void
mg_xbutton_cb(chanview * cv,chan * ch,int tag,gpointer userdata)1326 mg_xbutton_cb (chanview *cv, chan *ch, int tag, gpointer userdata)
1327 {
1328 	if (tag == TAG_IRC)	/* irc tab */
1329 		mg_close_sess (userdata);
1330 	else						/* non-irc utility tab */
1331 		mg_close_gen (ch, userdata);
1332 }
1333 
1334 static void
mg_link_gentab(chan * ch,GtkWidget * box)1335 mg_link_gentab (chan *ch, GtkWidget *box)
1336 {
1337 	int num;
1338 	GtkWidget *win;
1339 
1340 	g_object_ref (box);
1341 
1342 	num = gtk_notebook_page_num (GTK_NOTEBOOK (mg_gui->note_book), box);
1343 	gtk_notebook_remove_page (GTK_NOTEBOOK (mg_gui->note_book), num);
1344 	mg_chan_remove (ch);
1345 
1346 	win = gtkutil_window_new (g_object_get_data (G_OBJECT (box), "title"), "",
1347 									  GPOINTER_TO_INT (g_object_get_data (G_OBJECT (box), "w")),
1348 									  GPOINTER_TO_INT (g_object_get_data (G_OBJECT (box), "h")),
1349 									  2);
1350 	/* so it doesn't try to chan_remove (there's no tab anymore) */
1351 	g_object_steal_data (G_OBJECT (box), "ch");
1352 	gtk_container_set_border_width (GTK_CONTAINER (box), 0);
1353 	gtk_container_add (GTK_CONTAINER (win), box);
1354 	gtk_widget_show (win);
1355 
1356 	g_object_unref (box);
1357 }
1358 
1359 static void
mg_detach_tab_cb(GtkWidget * item,chan * ch)1360 mg_detach_tab_cb (GtkWidget *item, chan *ch)
1361 {
1362 	if (chan_get_tag (ch) == TAG_IRC)	/* IRC tab */
1363 	{
1364 		/* userdata is session * */
1365 		mg_link_irctab (chan_get_userdata (ch), 1);
1366 		return;
1367 	}
1368 
1369 	/* userdata is GtkWidget * */
1370 	mg_link_gentab (ch, chan_get_userdata (ch));	/* non-IRC tab */
1371 }
1372 
1373 static void
mg_destroy_tab_cb(GtkWidget * item,chan * ch)1374 mg_destroy_tab_cb (GtkWidget *item, chan *ch)
1375 {
1376 	/* treat it just like the X button press */
1377 	mg_xbutton_cb (mg_gui->chanview, ch, chan_get_tag (ch), chan_get_userdata (ch));
1378 }
1379 
1380 static void
mg_color_insert(GtkWidget * item,gpointer userdata)1381 mg_color_insert (GtkWidget *item, gpointer userdata)
1382 {
1383 	char buf[32];
1384 	char *text;
1385 	int num = GPOINTER_TO_INT (userdata);
1386 
1387 	if (num > 99)
1388 	{
1389 		switch (num)
1390 		{
1391 		case 100:
1392 			text = "\002"; break;
1393 		case 101:
1394 			text = "\037"; break;
1395 		case 102:
1396 			text = "\035"; break;
1397 		case 103:
1398 			text = "\036"; break;
1399 		default:
1400 			text = "\017"; break;
1401 		}
1402 		key_action_insert (current_sess->gui->input_box, 0, text, 0, 0);
1403 	} else
1404 	{
1405 		sprintf (buf, "\003%02d", num);
1406 		key_action_insert (current_sess->gui->input_box, 0, buf, 0, 0);
1407 	}
1408 }
1409 
1410 static void
mg_markup_item(GtkWidget * menu,char * text,int arg)1411 mg_markup_item (GtkWidget *menu, char *text, int arg)
1412 {
1413 	GtkWidget *item;
1414 
1415 	item = gtk_menu_item_new_with_label ("");
1416 	gtk_label_set_markup (GTK_LABEL (gtk_bin_get_child (GTK_BIN (item))), text);
1417 	g_signal_connect (G_OBJECT (item), "activate",
1418 							G_CALLBACK (mg_color_insert), GINT_TO_POINTER (arg));
1419 	gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1420 	gtk_widget_show (item);
1421 }
1422 
1423 GtkWidget *
mg_submenu(GtkWidget * menu,char * text)1424 mg_submenu (GtkWidget *menu, char *text)
1425 {
1426 	GtkWidget *submenu, *item;
1427 
1428 	item = gtk_menu_item_new_with_mnemonic (text);
1429 	gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1430 	gtk_widget_show (item);
1431 
1432 	submenu = gtk_menu_new ();
1433 	gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu);
1434 	gtk_widget_show (submenu);
1435 
1436 	return submenu;
1437 }
1438 
1439 static void
mg_create_color_menu(GtkWidget * menu,session * sess)1440 mg_create_color_menu (GtkWidget *menu, session *sess)
1441 {
1442 	GtkWidget *submenu;
1443 	GtkWidget *subsubmenu;
1444 	char buf[256];
1445 	int i;
1446 
1447 	submenu = mg_submenu (menu, _("Insert Attribute or Color Code"));
1448 
1449 	mg_markup_item (submenu, _("<b>Bold</b>"), 100);
1450 	mg_markup_item (submenu, _("<u>Underline</u>"), 101);
1451 	mg_markup_item (submenu, _("<i>Italic</i>"), 102);
1452 	mg_markup_item (submenu, _("<s>Strikethrough</s>"), 103);
1453 	mg_markup_item (submenu, _("Normal"), 999);
1454 
1455 	subsubmenu = mg_submenu (submenu, _("Colors 0-7"));
1456 
1457 	for (i = 0; i < 8; i++)
1458 	{
1459 		sprintf (buf, "<tt><sup>%02d</sup> <span background=\"#%02x%02x%02x\">"
1460 					"   </span></tt>",
1461 				i, colors[i].red >> 8, colors[i].green >> 8, colors[i].blue >> 8);
1462 		mg_markup_item (subsubmenu, buf, i);
1463 	}
1464 
1465 	subsubmenu = mg_submenu (submenu, _("Colors 8-15"));
1466 
1467 	for (i = 8; i < 16; i++)
1468 	{
1469 		sprintf (buf, "<tt><sup>%02d</sup> <span background=\"#%02x%02x%02x\">"
1470 					"   </span></tt>",
1471 				i, colors[i].red >> 8, colors[i].green >> 8, colors[i].blue >> 8);
1472 		mg_markup_item (subsubmenu, buf, i);
1473 	}
1474 }
1475 
1476 static void
mg_set_guint8(GtkCheckMenuItem * item,guint8 * setting)1477 mg_set_guint8 (GtkCheckMenuItem *item, guint8 *setting)
1478 {
1479 	session *sess = current_sess;
1480 	guint8 logging = sess->text_logging;
1481 
1482 	*setting = SET_OFF;
1483 	if (gtk_check_menu_item_get_active (item))
1484 		*setting = SET_ON;
1485 
1486 	/* has the logging setting changed? */
1487 	if (logging != sess->text_logging)
1488 		log_open_or_close (sess);
1489 
1490 	chanopt_save (sess);
1491 	chanopt_save_all (FALSE);
1492 }
1493 
1494 static void
mg_perchan_menu_item(char * label,GtkWidget * menu,guint8 * setting,guint global)1495 mg_perchan_menu_item (char *label, GtkWidget *menu, guint8 *setting, guint global)
1496 {
1497 	guint8 initial_value = *setting;
1498 
1499 	/* if it's using global value, use that as initial state */
1500 	if (initial_value == SET_DEFAULT)
1501 		initial_value = global;
1502 
1503 	menu_toggle_item (label, menu, mg_set_guint8, setting, initial_value);
1504 }
1505 
1506 static void
mg_create_perchannelmenu(session * sess,GtkWidget * menu)1507 mg_create_perchannelmenu (session *sess, GtkWidget *menu)
1508 {
1509 	GtkWidget *submenu;
1510 
1511 	submenu = menu_quick_sub (_("_Settings"), menu, NULL, XCMENU_MNEMONIC, -1);
1512 
1513 	mg_perchan_menu_item (_("_Log to Disk"), submenu, &sess->text_logging, prefs.hex_irc_logging);
1514 	mg_perchan_menu_item (_("_Reload Scrollback"), submenu, &sess->text_scrollback, prefs.hex_text_replay);
1515 	if (sess->type == SESS_CHANNEL)
1516 	{
1517 		mg_perchan_menu_item (_("Strip _Colors"), submenu, &sess->text_strip, prefs.hex_text_stripcolor_msg);
1518 		mg_perchan_menu_item (_("_Hide Join/Part Messages"), submenu, &sess->text_hidejoinpart, prefs.hex_irc_conf_mode);
1519 	}
1520 }
1521 
1522 static void
mg_create_alertmenu(session * sess,GtkWidget * menu)1523 mg_create_alertmenu (session *sess, GtkWidget *menu)
1524 {
1525 	GtkWidget *submenu;
1526 	int hex_balloon, hex_beep, hex_tray, hex_flash;
1527 
1528 
1529 	switch (sess->type) {
1530 		case SESS_DIALOG:
1531 			hex_balloon = prefs.hex_input_balloon_priv;
1532 			hex_beep = prefs.hex_input_beep_priv;
1533 			hex_tray = prefs.hex_input_tray_priv;
1534 			hex_flash = prefs.hex_input_flash_priv;
1535 			break;
1536 		default:
1537 			hex_balloon = prefs.hex_input_balloon_chans;
1538 			hex_beep = prefs.hex_input_beep_chans;
1539 			hex_tray = prefs.hex_input_tray_chans;
1540 			hex_flash = prefs.hex_input_flash_chans;
1541 	}
1542 
1543 	submenu = menu_quick_sub(_("_Extra Alerts"), menu, NULL, XCMENU_MNEMONIC, -1);
1544 
1545 	mg_perchan_menu_item(_("Show Notifications"), submenu, &sess->alert_balloon, hex_balloon);
1546 
1547 	mg_perchan_menu_item(_("Beep on _Message"), submenu, &sess->alert_beep, hex_beep);
1548 
1549 	mg_perchan_menu_item(_("Blink Tray _Icon"), submenu, &sess->alert_tray, hex_tray);
1550 
1551 	mg_perchan_menu_item(_("Blink Task _Bar"), submenu, &sess->alert_taskbar, hex_flash);
1552 }
1553 
1554 static void
mg_create_tabmenu(session * sess,GdkEventButton * event,chan * ch)1555 mg_create_tabmenu (session *sess, GdkEventButton *event, chan *ch)
1556 {
1557 	GtkWidget *menu, *item;
1558 	char buf[256];
1559 
1560 	menu = gtk_menu_new ();
1561 
1562 	if (sess)
1563 	{
1564 		char *name = g_markup_escape_text (sess->channel[0] ? sess->channel : _("<none>"), -1);
1565 		g_snprintf (buf, sizeof (buf), "<span foreground=\"#3344cc\"><b>%s</b></span>", name);
1566 		g_free (name);
1567 
1568 		item = gtk_menu_item_new_with_label ("");
1569 		gtk_label_set_markup (GTK_LABEL (gtk_bin_get_child (GTK_BIN (item))), buf);
1570 		gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1571 		gtk_widget_show (item);
1572 
1573 		/* separator */
1574 		menu_quick_item (0, 0, menu, XCMENU_SHADED, 0, 0);
1575 
1576 		/* per-channel alerts */
1577 		mg_create_alertmenu (sess, menu);
1578 
1579 		/* per-channel settings */
1580 		mg_create_perchannelmenu (sess, menu);
1581 
1582 		/* separator */
1583 		menu_quick_item (0, 0, menu, XCMENU_SHADED, 0, 0);
1584 
1585 		if (sess->type == SESS_CHANNEL)
1586 			menu_addfavoritemenu (sess->server, menu, sess->channel, TRUE);
1587 		else if (sess->type == SESS_SERVER)
1588 			menu_addconnectmenu (sess->server, menu);
1589 	}
1590 
1591 	mg_create_icon_item (_("_Detach"), GTK_STOCK_REDO, menu,
1592 								mg_detach_tab_cb, ch);
1593 	mg_create_icon_item (_("_Close"), GTK_STOCK_CLOSE, menu,
1594 								mg_destroy_tab_cb, ch);
1595 	if (sess && tabmenu_list)
1596 		menu_create (menu, tabmenu_list, sess->channel, FALSE);
1597 	if (sess)
1598 		menu_add_plugin_items (menu, "\x4$TAB", sess->channel);
1599 
1600 	if (event->window)
1601 		gtk_menu_set_screen (GTK_MENU (menu), gdk_window_get_screen (event->window));
1602 	g_object_ref (menu);
1603 	g_object_ref_sink (menu);
1604 	g_object_unref (menu);
1605 	g_signal_connect (G_OBJECT (menu), "selection-done",
1606 							G_CALLBACK (mg_menu_destroy), NULL);
1607 	gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, 0, event->time);
1608 }
1609 
1610 static gboolean
mg_tab_contextmenu_cb(chanview * cv,chan * ch,int tag,gpointer ud,GdkEventButton * event)1611 mg_tab_contextmenu_cb (chanview *cv, chan *ch, int tag, gpointer ud, GdkEventButton *event)
1612 {
1613 	/* middle-click to close a tab */
1614 	if (((prefs.hex_gui_tab_middleclose && event->button == 2))
1615 		&& event->type == GDK_BUTTON_PRESS)
1616 	{
1617 		mg_xbutton_cb (cv, ch, tag, ud);
1618 		return TRUE;
1619 	}
1620 
1621 	if (event->button != 3)
1622 		return FALSE;
1623 
1624 	if (tag == TAG_IRC)
1625 		mg_create_tabmenu (ud, event, ch);
1626 	else
1627 		mg_create_tabmenu (NULL, event, ch);
1628 
1629 	return TRUE;
1630 }
1631 
1632 void
mg_dnd_drop_file(session * sess,char * target,char * uri)1633 mg_dnd_drop_file (session *sess, char *target, char *uri)
1634 {
1635 	char *p, *data, *next, *fname;
1636 
1637 	p = data = g_strdup (uri);
1638 	while (*p)
1639 	{
1640 		next = strchr (p, '\r');
1641 		if (g_ascii_strncasecmp ("file:", p, 5) == 0)
1642 		{
1643 			if (next)
1644 				*next = 0;
1645 			fname = g_filename_from_uri (p, NULL, NULL);
1646 			if (fname)
1647 			{
1648 				/* dcc_send() expects utf-8 */
1649 				p = g_filename_from_utf8 (fname, -1, 0, 0, 0);
1650 				if (p)
1651 				{
1652 					dcc_send (sess, target, p, prefs.hex_dcc_max_send_cps, 0);
1653 					g_free (p);
1654 				}
1655 				g_free (fname);
1656 			}
1657 		}
1658 		if (!next)
1659 			break;
1660 		p = next + 1;
1661 		if (*p == '\n')
1662 			p++;
1663 	}
1664 	g_free (data);
1665 
1666 }
1667 
1668 static void
mg_dialog_dnd_drop(GtkWidget * widget,GdkDragContext * context,gint x,gint y,GtkSelectionData * selection_data,guint info,guint32 time,gpointer ud)1669 mg_dialog_dnd_drop (GtkWidget * widget, GdkDragContext * context, gint x,
1670 						  gint y, GtkSelectionData * selection_data, guint info,
1671 						  guint32 time, gpointer ud)
1672 {
1673 	if (current_sess->type == SESS_DIALOG)
1674 		/* sess->channel is really the nickname of dialogs */
1675 		mg_dnd_drop_file (current_sess, current_sess->channel, (char *)gtk_selection_data_get_data (selection_data));
1676 }
1677 
1678 /* add a tabbed channel */
1679 
1680 static void
mg_add_chan(session * sess)1681 mg_add_chan (session *sess)
1682 {
1683 	GdkPixbuf *icon;
1684 	char *name = _("<none>");
1685 
1686 	if (sess->channel[0])
1687 		name = sess->channel;
1688 
1689 	switch (sess->type)
1690 	{
1691 	case SESS_CHANNEL:
1692 		icon = pix_tree_channel;
1693 		break;
1694 	case SESS_SERVER:
1695 		icon = pix_tree_server;
1696 		break;
1697 	default:
1698 		icon = pix_tree_dialog;
1699 	}
1700 
1701 	sess->res->tab = chanview_add (sess->gui->chanview, name, sess->server, sess,
1702 											 sess->type == SESS_SERVER ? FALSE : TRUE,
1703 											 TAG_IRC, icon);
1704 	if (plain_list == NULL)
1705 		mg_create_tab_colors ();
1706 
1707 	chan_set_color (sess->res->tab, plain_list);
1708 
1709 	if (sess->res->buffer == NULL)
1710 	{
1711 		sess->res->buffer = gtk_xtext_buffer_new (GTK_XTEXT (sess->gui->xtext));
1712 		gtk_xtext_set_time_stamp (sess->res->buffer, prefs.hex_stamp_text);
1713 		sess->res->user_model = userlist_create_model (sess);
1714 	}
1715 }
1716 
1717 static void
mg_userlist_button(GtkWidget * box,char * label,char * cmd,int a,int b,int c,int d)1718 mg_userlist_button (GtkWidget * box, char *label, char *cmd,
1719 						  int a, int b, int c, int d)
1720 {
1721 	GtkWidget *wid = gtk_button_new_with_label (label);
1722 	g_signal_connect (G_OBJECT (wid), "clicked",
1723 							G_CALLBACK (userlist_button_cb), cmd);
1724 	gtk_table_attach_defaults (GTK_TABLE (box), wid, a, b, c, d);
1725 	show_and_unfocus (wid);
1726 }
1727 
1728 static GtkWidget *
mg_create_userlistbuttons(GtkWidget * box)1729 mg_create_userlistbuttons (GtkWidget *box)
1730 {
1731 	struct popup *pop;
1732 	GSList *list = button_list;
1733 	int a = 0, b = 0;
1734 	GtkWidget *tab;
1735 
1736 	tab = gtk_table_new (5, 2, FALSE);
1737 	gtk_box_pack_end (GTK_BOX (box), tab, FALSE, FALSE, 0);
1738 
1739 	while (list)
1740 	{
1741 		pop = list->data;
1742 		if (pop->cmd[0])
1743 		{
1744 			mg_userlist_button (tab, pop->name, pop->cmd, a, a + 1, b, b + 1);
1745 			a++;
1746 			if (a == 2)
1747 			{
1748 				a = 0;
1749 				b++;
1750 			}
1751 		}
1752 		list = list->next;
1753 	}
1754 
1755 	return tab;
1756 }
1757 
1758 static void
mg_topic_cb(GtkWidget * entry,gpointer userdata)1759 mg_topic_cb (GtkWidget *entry, gpointer userdata)
1760 {
1761 	session *sess = current_sess;
1762 	char *text;
1763 
1764 	if (sess->channel[0] && sess->server->connected && sess->type == SESS_CHANNEL)
1765 	{
1766 		text = (char *)gtk_entry_get_text (GTK_ENTRY (entry));
1767 		if (text[0] == 0)
1768 			text = NULL;
1769 		sess->server->p_topic (sess->server, sess->channel, text);
1770 	} else
1771 		gtk_entry_set_text (GTK_ENTRY (entry), "");
1772 	/* restore focus to the input widget, where the next input will most
1773 likely be */
1774 	gtk_widget_grab_focus (sess->gui->input_box);
1775 }
1776 
1777 static void
mg_tabwindow_kill_cb(GtkWidget * win,gpointer userdata)1778 mg_tabwindow_kill_cb (GtkWidget *win, gpointer userdata)
1779 {
1780 	GSList *list, *next;
1781 	session *sess;
1782 
1783 /*	puts("enter mg_tabwindow_kill_cb");*/
1784 	hexchat_is_quitting = TRUE;
1785 
1786 	/* see if there's any non-tab windows left */
1787 	list = sess_list;
1788 	while (list)
1789 	{
1790 		sess = list->data;
1791 		next = list->next;
1792 		if (!sess->gui->is_tab)
1793 		{
1794 			hexchat_is_quitting = FALSE;
1795 /*			puts("-> will not exit, some toplevel windows left");*/
1796 		} else
1797 		{
1798 			mg_ircdestroy (sess);
1799 		}
1800 		list = next;
1801 	}
1802 
1803 	current_tab = NULL;
1804 	active_tab = NULL;
1805 	mg_gui = NULL;
1806 	parent_window = NULL;
1807 }
1808 
1809 static GtkWidget *
mg_changui_destroy(session * sess)1810 mg_changui_destroy (session *sess)
1811 {
1812 	GtkWidget *ret = NULL;
1813 
1814 	if (sess->gui->is_tab)
1815 	{
1816 		/* avoid calling the "destroy" callback */
1817 		g_signal_handlers_disconnect_by_func (G_OBJECT (sess->gui->window),
1818 														  mg_tabwindow_kill_cb, 0);
1819 		/* remove the tab from the chanview */
1820 		if (!mg_chan_remove (sess->res->tab))
1821 			/* if the window still exists, restore the signal handler */
1822 			g_signal_connect (G_OBJECT (sess->gui->window), "destroy",
1823 									G_CALLBACK (mg_tabwindow_kill_cb), 0);
1824 	} else
1825 	{
1826 		/* avoid calling the "destroy" callback */
1827 		g_signal_handlers_disconnect_by_func (G_OBJECT (sess->gui->window),
1828 														  mg_topdestroy_cb, sess);
1829 		/*gtk_widget_destroy (sess->gui->window);*/
1830 		/* don't destroy until the new one is created. Not sure why, but */
1831 		/* it fixes: Gdk-CRITICAL **: gdk_colormap_get_screen: */
1832 		/*           assertion `GDK_IS_COLORMAP (cmap)' failed */
1833 		ret = sess->gui->window;
1834 		g_free (sess->gui);
1835 		sess->gui = NULL;
1836 	}
1837 	return ret;
1838 }
1839 
1840 static void
mg_link_irctab(session * sess,int focus)1841 mg_link_irctab (session *sess, int focus)
1842 {
1843 	GtkWidget *win;
1844 
1845 	if (sess->gui->is_tab)
1846 	{
1847 		win = mg_changui_destroy (sess);
1848 		mg_changui_new (sess, sess->res, 0, focus);
1849 		mg_populate (sess);
1850 		hexchat_is_quitting = FALSE;
1851 		if (win)
1852 			gtk_widget_destroy (win);
1853 		return;
1854 	}
1855 
1856 	mg_unpopulate (sess);
1857 	win = mg_changui_destroy (sess);
1858 	mg_changui_new (sess, sess->res, 1, focus);
1859 	/* the buffer is now attached to a different widget */
1860 	((xtext_buffer *)sess->res->buffer)->xtext = (GtkXText *)sess->gui->xtext;
1861 	if (win)
1862 		gtk_widget_destroy (win);
1863 }
1864 
1865 void
mg_detach(session * sess,int mode)1866 mg_detach (session *sess, int mode)
1867 {
1868 	switch (mode)
1869 	{
1870 	/* detach only */
1871 	case 1:
1872 		if (sess->gui->is_tab)
1873 			mg_link_irctab (sess, 1);
1874 		break;
1875 	/* attach only */
1876 	case 2:
1877 		if (!sess->gui->is_tab)
1878 			mg_link_irctab (sess, 1);
1879 		break;
1880 	/* toggle */
1881 	default:
1882 		mg_link_irctab (sess, 1);
1883 	}
1884 }
1885 
1886 static int
check_is_number(char * t)1887 check_is_number (char *t)
1888 {
1889 	while (*t)
1890 	{
1891 		if (*t < '0' || *t > '9')
1892 			return FALSE;
1893 		t++;
1894 	}
1895 	return TRUE;
1896 }
1897 
1898 static void
mg_change_flag(GtkWidget * wid,session * sess,char flag)1899 mg_change_flag (GtkWidget * wid, session *sess, char flag)
1900 {
1901 	server *serv = sess->server;
1902 	char mode[3];
1903 
1904 	mode[1] = flag;
1905 	mode[2] = '\0';
1906 	if (serv->connected && sess->channel[0])
1907 	{
1908 		if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (wid)))
1909 			mode[0] = '+';
1910 		else
1911 			mode[0] = '-';
1912 		serv->p_mode (serv, sess->channel, mode);
1913 		serv->p_join_info (serv, sess->channel);
1914 		sess->ignore_mode = TRUE;
1915 		sess->ignore_date = TRUE;
1916 	}
1917 }
1918 
1919 static void
flagl_hit(GtkWidget * wid,struct session * sess)1920 flagl_hit (GtkWidget * wid, struct session *sess)
1921 {
1922 	char modes[512];
1923 	const char *limit_str;
1924 	server *serv = sess->server;
1925 
1926 	if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (wid)))
1927 	{
1928 		if (serv->connected && sess->channel[0])
1929 		{
1930 			limit_str = gtk_entry_get_text (GTK_ENTRY (sess->gui->limit_entry));
1931 			if (check_is_number ((char *)limit_str) == FALSE)
1932 			{
1933 				fe_message (_("User limit must be a number!\n"), FE_MSG_ERROR);
1934 				gtk_entry_set_text (GTK_ENTRY (sess->gui->limit_entry), "");
1935 				gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (wid), FALSE);
1936 				return;
1937 			}
1938 			g_snprintf (modes, sizeof (modes), "+l %d", atoi (limit_str));
1939 			serv->p_mode (serv, sess->channel, modes);
1940 			serv->p_join_info (serv, sess->channel);
1941 		}
1942 	} else
1943 		mg_change_flag (wid, sess, 'l');
1944 }
1945 
1946 static void
flagk_hit(GtkWidget * wid,struct session * sess)1947 flagk_hit (GtkWidget * wid, struct session *sess)
1948 {
1949 	char modes[512];
1950 	server *serv = sess->server;
1951 
1952 	if (serv->connected && sess->channel[0])
1953 	{
1954 		g_snprintf (modes, sizeof (modes), "-k %s",
1955 			  gtk_entry_get_text (GTK_ENTRY (sess->gui->key_entry)));
1956 
1957 		if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (wid)))
1958 			modes[0] = '+';
1959 
1960 		serv->p_mode (serv, sess->channel, modes);
1961 	}
1962 }
1963 
1964 static void
mg_flagbutton_cb(GtkWidget * but,char * flag)1965 mg_flagbutton_cb (GtkWidget *but, char *flag)
1966 {
1967 	session *sess;
1968 	char mode;
1969 
1970 	if (ignore_chanmode)
1971 		return;
1972 
1973 	sess = current_sess;
1974 	mode = tolower ((unsigned char) flag[0]);
1975 
1976 	switch (mode)
1977 	{
1978 	case 'l':
1979 		flagl_hit (but, sess);
1980 		break;
1981 	case 'k':
1982 		flagk_hit (but, sess);
1983 		break;
1984 	case 'b':
1985 		ignore_chanmode = TRUE;
1986 		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (sess->gui->flag_b), FALSE);
1987 		ignore_chanmode = FALSE;
1988 		banlist_opengui (sess);
1989 		break;
1990 	default:
1991 		mg_change_flag (but, sess, mode);
1992 	}
1993 }
1994 
1995 static GtkWidget *
mg_create_flagbutton(char * tip,GtkWidget * box,char * face)1996 mg_create_flagbutton (char *tip, GtkWidget *box, char *face)
1997 {
1998 	GtkWidget *btn, *lbl;
1999 	char label_markup[16];
2000 
2001 	g_snprintf (label_markup, sizeof(label_markup), "<tt>%s</tt>", face);
2002 	lbl = gtk_label_new (NULL);
2003 	gtk_label_set_markup (GTK_LABEL(lbl), label_markup);
2004 
2005 	btn = gtk_toggle_button_new ();
2006 	gtk_widget_set_size_request (btn, -1, 0);
2007 	gtk_widget_set_tooltip_text (btn, tip);
2008 	gtk_container_add (GTK_CONTAINER(btn), lbl);
2009 
2010 	gtk_box_pack_start (GTK_BOX (box), btn, 0, 0, 0);
2011 	g_signal_connect (G_OBJECT (btn), "toggled",
2012 							G_CALLBACK (mg_flagbutton_cb), face);
2013 	show_and_unfocus (btn);
2014 
2015 	return btn;
2016 }
2017 
2018 static void
mg_key_entry_cb(GtkWidget * igad,gpointer userdata)2019 mg_key_entry_cb (GtkWidget * igad, gpointer userdata)
2020 {
2021 	char modes[512];
2022 	session *sess = current_sess;
2023 	server *serv = sess->server;
2024 
2025 	if (serv->connected && sess->channel[0])
2026 	{
2027 		g_snprintf (modes, sizeof (modes), "+k %s",
2028 				gtk_entry_get_text (GTK_ENTRY (igad)));
2029 		serv->p_mode (serv, sess->channel, modes);
2030 		serv->p_join_info (serv, sess->channel);
2031 	}
2032 }
2033 
2034 static void
mg_limit_entry_cb(GtkWidget * igad,gpointer userdata)2035 mg_limit_entry_cb (GtkWidget * igad, gpointer userdata)
2036 {
2037 	char modes[512];
2038 	session *sess = current_sess;
2039 	server *serv = sess->server;
2040 
2041 	if (serv->connected && sess->channel[0])
2042 	{
2043 		if (check_is_number ((char *)gtk_entry_get_text (GTK_ENTRY (igad))) == FALSE)
2044 		{
2045 			gtk_entry_set_text (GTK_ENTRY (igad), "");
2046 			fe_message (_("User limit must be a number!\n"), FE_MSG_ERROR);
2047 			gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (sess->gui->flag_l), FALSE);
2048 			return;
2049 		}
2050 		g_snprintf (modes, sizeof(modes), "+l %d",
2051 				atoi (gtk_entry_get_text (GTK_ENTRY (igad))));
2052 		serv->p_mode (serv, sess->channel, modes);
2053 		serv->p_join_info (serv, sess->channel);
2054 	}
2055 }
2056 
2057 static void
mg_apply_entry_style(GtkWidget * entry)2058 mg_apply_entry_style (GtkWidget *entry)
2059 {
2060 	gtk_widget_modify_base (entry, GTK_STATE_NORMAL, &colors[COL_BG]);
2061 	gtk_widget_modify_text (entry, GTK_STATE_NORMAL, &colors[COL_FG]);
2062 	gtk_widget_modify_font (entry, input_style->font_desc);
2063 }
2064 
2065 static void
mg_create_chanmodebuttons(session_gui * gui,GtkWidget * box)2066 mg_create_chanmodebuttons (session_gui *gui, GtkWidget *box)
2067 {
2068 	gui->flag_c = mg_create_flagbutton (_("Filter Colors"), box, "c");
2069 	gui->flag_n = mg_create_flagbutton (_("No outside messages"), box, "n");
2070 	gui->flag_t = mg_create_flagbutton (_("Topic Protection"), box, "t");
2071 	gui->flag_i = mg_create_flagbutton (_("Invite Only"), box, "i");
2072 	gui->flag_m = mg_create_flagbutton (_("Moderated"), box, "m");
2073 	gui->flag_b = mg_create_flagbutton (_("Ban List"), box, "b");
2074 
2075 	gui->flag_k = mg_create_flagbutton (_("Keyword"), box, "k");
2076 	gui->key_entry = gtk_entry_new ();
2077 	gtk_widget_set_name (gui->key_entry, "hexchat-inputbox");
2078 	gtk_entry_set_max_length (GTK_ENTRY (gui->key_entry), 23);
2079 	gtk_widget_set_size_request (gui->key_entry, 115, -1);
2080 	gtk_box_pack_start (GTK_BOX (box), gui->key_entry, 0, 0, 0);
2081 	g_signal_connect (G_OBJECT (gui->key_entry), "activate",
2082 							G_CALLBACK (mg_key_entry_cb), NULL);
2083 
2084 	if (prefs.hex_gui_input_style)
2085 		mg_apply_entry_style (gui->key_entry);
2086 
2087 	gui->flag_l = mg_create_flagbutton (_("User Limit"), box, "l");
2088 	gui->limit_entry = gtk_entry_new ();
2089 	gtk_widget_set_name (gui->limit_entry, "hexchat-inputbox");
2090 	gtk_entry_set_max_length (GTK_ENTRY (gui->limit_entry), 10);
2091 	gtk_widget_set_size_request (gui->limit_entry, 30, -1);
2092 	gtk_box_pack_start (GTK_BOX (box), gui->limit_entry, 0, 0, 0);
2093 	g_signal_connect (G_OBJECT (gui->limit_entry), "activate",
2094 							G_CALLBACK (mg_limit_entry_cb), NULL);
2095 
2096 	if (prefs.hex_gui_input_style)
2097 		mg_apply_entry_style (gui->limit_entry);
2098 }
2099 
2100 /*static void
2101 mg_create_link_buttons (GtkWidget *box, gpointer userdata)
2102 {
2103 	gtkutil_button (box, GTK_STOCK_CLOSE, _("Close this tab/window"),
2104 						 mg_x_click_cb, userdata, 0);
2105 
2106 	if (!userdata)
2107 	gtkutil_button (box, GTK_STOCK_REDO, _("Attach/Detach this tab"),
2108 						 mg_link_cb, userdata, 0);
2109 }*/
2110 
2111 static void
mg_dialog_button_cb(GtkWidget * wid,char * cmd)2112 mg_dialog_button_cb (GtkWidget *wid, char *cmd)
2113 {
2114 	/* the longest cmd is 12, and the longest nickname is 64 */
2115 	char buf[128];
2116 	char *host = "";
2117 	char *topic;
2118 
2119 	if (!current_sess)
2120 		return;
2121 
2122 	topic = (char *)(gtk_entry_get_text (GTK_ENTRY (current_sess->gui->topic_entry)));
2123 	topic = strrchr (topic, '@');
2124 	if (topic)
2125 		host = topic + 1;
2126 
2127 	auto_insert (buf, sizeof (buf), cmd, 0, 0, "", "", "",
2128 					 server_get_network (current_sess->server, TRUE), host, "",
2129 					 current_sess->channel, "");
2130 
2131 	handle_command (current_sess, buf, TRUE);
2132 
2133 	/* dirty trick to avoid auto-selection */
2134 	SPELL_ENTRY_SET_EDITABLE (current_sess->gui->input_box, FALSE);
2135 	gtk_widget_grab_focus (current_sess->gui->input_box);
2136 	SPELL_ENTRY_SET_EDITABLE (current_sess->gui->input_box, TRUE);
2137 }
2138 
2139 static void
mg_dialog_button(GtkWidget * box,char * name,char * cmd)2140 mg_dialog_button (GtkWidget *box, char *name, char *cmd)
2141 {
2142 	GtkWidget *wid;
2143 
2144 	wid = gtk_button_new_with_label (name);
2145 	gtk_box_pack_start (GTK_BOX (box), wid, FALSE, FALSE, 0);
2146 	g_signal_connect (G_OBJECT (wid), "clicked",
2147 							G_CALLBACK (mg_dialog_button_cb), cmd);
2148 	gtk_widget_set_size_request (wid, -1, 0);
2149 }
2150 
2151 static void
mg_create_dialogbuttons(GtkWidget * box)2152 mg_create_dialogbuttons (GtkWidget *box)
2153 {
2154 	struct popup *pop;
2155 	GSList *list = dlgbutton_list;
2156 
2157 	while (list)
2158 	{
2159 		pop = list->data;
2160 		if (pop->cmd[0])
2161 			mg_dialog_button (box, pop->name, pop->cmd);
2162 		list = list->next;
2163 	}
2164 }
2165 
2166 static void
mg_create_topicbar(session * sess,GtkWidget * box)2167 mg_create_topicbar (session *sess, GtkWidget *box)
2168 {
2169 	GtkWidget *hbox, *topic, *bbox;
2170 	session_gui *gui = sess->gui;
2171 
2172 	gui->topic_bar = hbox = gtk_hbox_new (FALSE, 0);
2173 	gtk_box_pack_start (GTK_BOX (box), hbox, 0, 0, 0);
2174 
2175 	if (!gui->is_tab)
2176 		sess->res->tab = NULL;
2177 
2178 	gui->topic_entry = topic = sexy_spell_entry_new ();
2179 	gtk_widget_set_name (topic, "hexchat-inputbox");
2180 	sexy_spell_entry_set_checked (SEXY_SPELL_ENTRY (topic), FALSE);
2181 	gtk_container_add (GTK_CONTAINER (hbox), topic);
2182 	g_signal_connect (G_OBJECT (topic), "activate",
2183 							G_CALLBACK (mg_topic_cb), 0);
2184 
2185 	if (prefs.hex_gui_input_style)
2186 		mg_apply_entry_style (topic);
2187 
2188 	gui->topicbutton_box = bbox = gtk_hbox_new (FALSE, 0);
2189 	gtk_box_pack_start (GTK_BOX (hbox), bbox, 0, 0, 0);
2190 	mg_create_chanmodebuttons (gui, bbox);
2191 
2192 	gui->dialogbutton_box = bbox = gtk_hbox_new (FALSE, 0);
2193 	gtk_box_pack_start (GTK_BOX (hbox), bbox, 0, 0, 0);
2194 	mg_create_dialogbuttons (bbox);
2195 }
2196 
2197 /* check if a word is clickable */
2198 
2199 static int
mg_word_check(GtkWidget * xtext,char * word)2200 mg_word_check (GtkWidget * xtext, char *word)
2201 {
2202 	session *sess = current_sess;
2203 	int ret;
2204 
2205 	ret = url_check_word (word);
2206 	if (ret == 0 && sess->type == SESS_DIALOG)
2207 		return WORD_DIALOG;
2208 
2209 	return ret;
2210 }
2211 
2212 /* mouse click inside text area */
2213 
2214 static void
mg_word_clicked(GtkWidget * xtext,char * word,GdkEventButton * even)2215 mg_word_clicked (GtkWidget *xtext, char *word, GdkEventButton *even)
2216 {
2217 	session *sess = current_sess;
2218 	int word_type = 0, start, end;
2219 	char *tmp;
2220 
2221 	if (word)
2222 	{
2223 		word_type = mg_word_check (xtext, word);
2224 		url_last (&start, &end);
2225 	}
2226 
2227 	if (even->button == 1)			/* left button */
2228 	{
2229 		if (word == NULL)
2230 		{
2231 			mg_focus (sess);
2232 			return;
2233 		}
2234 
2235 		if ((even->state & 13) == prefs.hex_gui_url_mod)
2236 		{
2237 			switch (word_type)
2238 			{
2239 			case WORD_URL:
2240 			case WORD_HOST6:
2241 			case WORD_HOST:
2242 				word[end] = 0;
2243 				fe_open_url (word + start);
2244 			}
2245 		}
2246 		return;
2247 	}
2248 
2249 	if (even->button == 2)
2250 	{
2251 		if (sess->type == SESS_DIALOG)
2252 			menu_middlemenu (sess, even);
2253 		else if (even->type == GDK_2BUTTON_PRESS)
2254 			userlist_select (sess, word);
2255 		return;
2256 	}
2257 	if (word == NULL)
2258 		return;
2259 
2260 	switch (word_type)
2261 	{
2262 	case 0:
2263 	case WORD_PATH:
2264 		menu_middlemenu (sess, even);
2265 		break;
2266 	case WORD_URL:
2267 	case WORD_HOST6:
2268 	case WORD_HOST:
2269 		word[end] = 0;
2270 		word += start;
2271 		menu_urlmenu (even, word);
2272 		break;
2273 	case WORD_NICK:
2274 		word[end] = 0;
2275 		word += start;
2276 		menu_nickmenu (sess, even, word, FALSE);
2277 		break;
2278 	case WORD_CHANNEL:
2279 		word[end] = 0;
2280 		word += start;
2281 		menu_chanmenu (sess, even, word);
2282 		break;
2283 	case WORD_EMAIL:
2284 		word[end] = 0;
2285 		word += start;
2286 		tmp = g_strdup_printf ("mailto:%s", word + (ispunct (*word) ? 1 : 0));
2287 		menu_urlmenu (even, tmp);
2288 		g_free (tmp);
2289 		break;
2290 	case WORD_DIALOG:
2291 		menu_nickmenu (sess, even, sess->channel, FALSE);
2292 		break;
2293 	}
2294 }
2295 
2296 void
mg_update_xtext(GtkWidget * wid)2297 mg_update_xtext (GtkWidget *wid)
2298 {
2299 	GtkXText *xtext = GTK_XTEXT (wid);
2300 
2301 	gtk_xtext_set_palette (xtext, colors);
2302 	gtk_xtext_set_max_lines (xtext, prefs.hex_text_max_lines);
2303 	gtk_xtext_set_background (xtext, channelwin_pix);
2304 	gtk_xtext_set_wordwrap (xtext, prefs.hex_text_wordwrap);
2305 	gtk_xtext_set_show_marker (xtext, prefs.hex_text_show_marker);
2306 	gtk_xtext_set_show_separator (xtext, prefs.hex_text_indent ? prefs.hex_text_show_sep : 0);
2307 	gtk_xtext_set_indent (xtext, prefs.hex_text_indent);
2308 	if (!gtk_xtext_set_font (xtext, prefs.hex_text_font))
2309 	{
2310 		fe_message ("Failed to open any font. I'm out of here!", FE_MSG_WAIT | FE_MSG_ERROR);
2311 		exit (1);
2312 	}
2313 
2314 	gtk_xtext_refresh (xtext);
2315 }
2316 
2317 static void
mg_create_textarea(session * sess,GtkWidget * box)2318 mg_create_textarea (session *sess, GtkWidget *box)
2319 {
2320 	GtkWidget *inbox, *vbox, *frame;
2321 	GtkXText *xtext;
2322 	session_gui *gui = sess->gui;
2323 	static const GtkTargetEntry dnd_targets[] =
2324 	{
2325 		{"text/uri-list", 0, 1}
2326 	};
2327 	static const GtkTargetEntry dnd_dest_targets[] =
2328 	{
2329 		{"HEXCHAT_CHANVIEW", GTK_TARGET_SAME_APP, 75 },
2330 		{"HEXCHAT_USERLIST", GTK_TARGET_SAME_APP, 75 }
2331 	};
2332 
2333 	vbox = gtk_vbox_new (FALSE, 0);
2334 	gtk_container_add (GTK_CONTAINER (box), vbox);
2335 	inbox = gtk_hbox_new (FALSE, 2);
2336 	gtk_container_add (GTK_CONTAINER (vbox), inbox);
2337 
2338 	frame = gtk_frame_new (NULL);
2339 	gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
2340 	gtk_container_add (GTK_CONTAINER (inbox), frame);
2341 
2342 	gui->xtext = gtk_xtext_new (colors, TRUE);
2343 	xtext = GTK_XTEXT (gui->xtext);
2344 	gtk_xtext_set_max_indent (xtext, prefs.hex_text_max_indent);
2345 	gtk_xtext_set_thin_separator (xtext, prefs.hex_text_thin_sep);
2346 	gtk_xtext_set_urlcheck_function (xtext, mg_word_check);
2347 	gtk_xtext_set_max_lines (xtext, prefs.hex_text_max_lines);
2348 	gtk_container_add (GTK_CONTAINER (frame), GTK_WIDGET (xtext));
2349 
2350 	mg_update_xtext (GTK_WIDGET (xtext));
2351 
2352 	g_signal_connect (G_OBJECT (xtext), "word_click",
2353 							G_CALLBACK (mg_word_clicked), NULL);
2354 
2355 	gui->vscrollbar = gtk_vscrollbar_new (GTK_XTEXT (xtext)->adj);
2356 	gtk_box_pack_start (GTK_BOX (inbox), gui->vscrollbar, FALSE, TRUE, 0);
2357 
2358 	gtk_drag_dest_set (gui->vscrollbar, 5, dnd_dest_targets, 2,
2359 							 GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK);
2360 	g_signal_connect (G_OBJECT (gui->vscrollbar), "drag_begin",
2361 							G_CALLBACK (mg_drag_begin_cb), NULL);
2362 	g_signal_connect (G_OBJECT (gui->vscrollbar), "drag_drop",
2363 							G_CALLBACK (mg_drag_drop_cb), NULL);
2364 	g_signal_connect (G_OBJECT (gui->vscrollbar), "drag_motion",
2365 							G_CALLBACK (mg_drag_motion_cb), gui->vscrollbar);
2366 	g_signal_connect (G_OBJECT (gui->vscrollbar), "drag_end",
2367 							G_CALLBACK (mg_drag_end_cb), NULL);
2368 
2369 	gtk_drag_dest_set (gui->xtext, GTK_DEST_DEFAULT_ALL, dnd_targets, 1,
2370 							 GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK);
2371 	g_signal_connect (G_OBJECT (gui->xtext), "drag_data_received",
2372 							G_CALLBACK (mg_dialog_dnd_drop), NULL);
2373 }
2374 
2375 static GtkWidget *
mg_create_infoframe(GtkWidget * box)2376 mg_create_infoframe (GtkWidget *box)
2377 {
2378 	GtkWidget *frame, *label, *hbox;
2379 
2380 	frame = gtk_frame_new (0);
2381 	gtk_frame_set_shadow_type ((GtkFrame*)frame, GTK_SHADOW_OUT);
2382 	gtk_container_add (GTK_CONTAINER (box), frame);
2383 
2384 	hbox = gtk_hbox_new (0, 0);
2385 	gtk_container_add (GTK_CONTAINER (frame), hbox);
2386 
2387 	label = gtk_label_new (NULL);
2388 	gtk_container_add (GTK_CONTAINER (hbox), label);
2389 
2390 	return label;
2391 }
2392 
2393 static void
mg_create_meters(session_gui * gui,GtkWidget * parent_box)2394 mg_create_meters (session_gui *gui, GtkWidget *parent_box)
2395 {
2396 	GtkWidget *infbox, *wid, *box;
2397 
2398 	gui->meter_box = infbox = box = gtk_vbox_new (0, 1);
2399 	gtk_box_pack_start (GTK_BOX (parent_box), box, 0, 0, 0);
2400 
2401 	if ((prefs.hex_gui_lagometer & 2) || (prefs.hex_gui_throttlemeter & 2))
2402 	{
2403 		infbox = gtk_hbox_new (0, 0);
2404 		gtk_box_pack_start (GTK_BOX (box), infbox, 0, 0, 0);
2405 	}
2406 
2407 	if (prefs.hex_gui_lagometer & 1)
2408 	{
2409 		gui->lagometer = wid = gtk_progress_bar_new ();
2410 #ifdef WIN32
2411 		gtk_widget_set_size_request (wid, 1, 10);
2412 #else
2413 		gtk_widget_set_size_request (wid, 1, 8);
2414 #endif
2415 
2416 		wid = gtk_event_box_new ();
2417 		gtk_container_add (GTK_CONTAINER (wid), gui->lagometer);
2418 		gtk_box_pack_start (GTK_BOX (box), wid, 0, 0, 0);
2419 	}
2420 	if (prefs.hex_gui_lagometer & 2)
2421 	{
2422 		gui->laginfo = wid = mg_create_infoframe (infbox);
2423 		gtk_label_set_text ((GtkLabel *) wid, "Lag");
2424 	}
2425 
2426 	if (prefs.hex_gui_throttlemeter & 1)
2427 	{
2428 		gui->throttlemeter = wid = gtk_progress_bar_new ();
2429 #ifdef WIN32
2430 		gtk_widget_set_size_request (wid, 1, 10);
2431 #else
2432 		gtk_widget_set_size_request (wid, 1, 8);
2433 #endif
2434 
2435 		wid = gtk_event_box_new ();
2436 		gtk_container_add (GTK_CONTAINER (wid), gui->throttlemeter);
2437 		gtk_box_pack_start (GTK_BOX (box), wid, 0, 0, 0);
2438 	}
2439 	if (prefs.hex_gui_throttlemeter & 2)
2440 	{
2441 		gui->throttleinfo = wid = mg_create_infoframe (infbox);
2442 		gtk_label_set_text ((GtkLabel *) wid, "Throttle");
2443 	}
2444 }
2445 
2446 void
mg_update_meters(session_gui * gui)2447 mg_update_meters (session_gui *gui)
2448 {
2449 	gtk_widget_destroy (gui->meter_box);
2450 	gui->lagometer = NULL;
2451 	gui->laginfo = NULL;
2452 	gui->throttlemeter = NULL;
2453 	gui->throttleinfo = NULL;
2454 
2455 	mg_create_meters (gui, gui->button_box_parent);
2456 	gtk_widget_show_all (gui->meter_box);
2457 }
2458 
2459 static void
mg_create_userlist(session_gui * gui,GtkWidget * box)2460 mg_create_userlist (session_gui *gui, GtkWidget *box)
2461 {
2462 	GtkWidget *frame, *ulist, *vbox;
2463 
2464 	vbox = gtk_vbox_new (0, 1);
2465 	gtk_container_add (GTK_CONTAINER (box), vbox);
2466 
2467 	frame = gtk_frame_new (NULL);
2468 	if (prefs.hex_gui_ulist_count)
2469 		gtk_box_pack_start (GTK_BOX (vbox), frame, 0, 0, GUI_SPACING);
2470 
2471 	gui->namelistinfo = gtk_label_new (NULL);
2472 	gtk_container_add (GTK_CONTAINER (frame), gui->namelistinfo);
2473 
2474 	gui->user_tree = ulist = userlist_create (vbox);
2475 
2476 	if (prefs.hex_gui_ulist_style)
2477 	{
2478 		gtk_widget_set_style (ulist, input_style);
2479 		gtk_widget_modify_base (ulist, GTK_STATE_NORMAL, &colors[COL_BG]);
2480 	}
2481 
2482 	mg_create_meters (gui, vbox);
2483 
2484 	gui->button_box_parent = vbox;
2485 	gui->button_box = mg_create_userlistbuttons (vbox);
2486 }
2487 
2488 static void
mg_vpane_cb(GtkPaned * pane,GParamSpec * param,session_gui * gui)2489 mg_vpane_cb (GtkPaned *pane, GParamSpec *param, session_gui *gui)
2490 {
2491 	prefs.hex_gui_pane_divider_position = gtk_paned_get_position (pane);
2492 }
2493 
2494 static void
mg_leftpane_cb(GtkPaned * pane,GParamSpec * param,session_gui * gui)2495 mg_leftpane_cb (GtkPaned *pane, GParamSpec *param, session_gui *gui)
2496 {
2497 	prefs.hex_gui_pane_left_size = gtk_paned_get_position (pane);
2498 }
2499 
2500 static void
mg_rightpane_cb(GtkPaned * pane,GParamSpec * param,session_gui * gui)2501 mg_rightpane_cb (GtkPaned *pane, GParamSpec *param, session_gui *gui)
2502 {
2503 	int handle_size;
2504 	GtkAllocation allocation;
2505 
2506 	gtk_widget_style_get (GTK_WIDGET (pane), "handle-size", &handle_size, NULL);
2507 	/* record the position from the RIGHT side */
2508 	gtk_widget_get_allocation (GTK_WIDGET(pane), &allocation);
2509 	prefs.hex_gui_pane_right_size = allocation.width - gtk_paned_get_position (pane) - handle_size;
2510 }
2511 
2512 static gboolean
mg_add_pane_signals(session_gui * gui)2513 mg_add_pane_signals (session_gui *gui)
2514 {
2515 	g_signal_connect (G_OBJECT (gui->hpane_right), "notify::position",
2516 							G_CALLBACK (mg_rightpane_cb), gui);
2517 	g_signal_connect (G_OBJECT (gui->hpane_left), "notify::position",
2518 							G_CALLBACK (mg_leftpane_cb), gui);
2519 	g_signal_connect (G_OBJECT (gui->vpane_left), "notify::position",
2520 							G_CALLBACK (mg_vpane_cb), gui);
2521 	g_signal_connect (G_OBJECT (gui->vpane_right), "notify::position",
2522 							G_CALLBACK (mg_vpane_cb), gui);
2523 	return FALSE;
2524 }
2525 
2526 static void
mg_create_center(session * sess,session_gui * gui,GtkWidget * box)2527 mg_create_center (session *sess, session_gui *gui, GtkWidget *box)
2528 {
2529 	GtkWidget *vbox, *hbox, *book;
2530 
2531 	/* sep between top and bottom of left side */
2532 	gui->vpane_left = gtk_vpaned_new ();
2533 
2534 	/* sep between top and bottom of right side */
2535 	gui->vpane_right = gtk_vpaned_new ();
2536 
2537 	/* sep between left and xtext */
2538 	gui->hpane_left = gtk_hpaned_new ();
2539 	gtk_paned_set_position (GTK_PANED (gui->hpane_left), prefs.hex_gui_pane_left_size);
2540 
2541 	/* sep between xtext and right side */
2542 	gui->hpane_right = gtk_hpaned_new ();
2543 
2544 	if (prefs.hex_gui_win_swap)
2545 	{
2546 		gtk_paned_pack2 (GTK_PANED (gui->hpane_left), gui->vpane_left, FALSE, TRUE);
2547 		gtk_paned_pack1 (GTK_PANED (gui->hpane_left), gui->hpane_right, TRUE, TRUE);
2548 	}
2549 	else
2550 	{
2551 		gtk_paned_pack1 (GTK_PANED (gui->hpane_left), gui->vpane_left, FALSE, TRUE);
2552 		gtk_paned_pack2 (GTK_PANED (gui->hpane_left), gui->hpane_right, TRUE, TRUE);
2553 	}
2554 	gtk_paned_pack2 (GTK_PANED (gui->hpane_right), gui->vpane_right, FALSE, TRUE);
2555 
2556 	gtk_container_add (GTK_CONTAINER (box), gui->hpane_left);
2557 
2558 	gui->note_book = book = gtk_notebook_new ();
2559 	gtk_notebook_set_show_tabs (GTK_NOTEBOOK (book), FALSE);
2560 	gtk_notebook_set_show_border (GTK_NOTEBOOK (book), FALSE);
2561 	gtk_paned_pack1 (GTK_PANED (gui->hpane_right), book, TRUE, TRUE);
2562 
2563 	hbox = gtk_hbox_new (FALSE, 0);
2564 	gtk_paned_pack1 (GTK_PANED (gui->vpane_right), hbox, FALSE, TRUE);
2565 	mg_create_userlist (gui, hbox);
2566 
2567 	gui->user_box = hbox;
2568 
2569 	vbox = gtk_vbox_new (FALSE, 3);
2570 	gtk_notebook_append_page (GTK_NOTEBOOK (book), vbox, NULL);
2571 	mg_create_topicbar (sess, vbox);
2572 
2573 	if (prefs.hex_gui_search_pos)
2574 	{
2575 		mg_create_search (sess, vbox);
2576 		mg_create_textarea (sess, vbox);
2577 	}
2578 	else
2579 	{
2580 		mg_create_textarea (sess, vbox);
2581 		mg_create_search (sess, vbox);
2582 	}
2583 
2584 	mg_create_entry (sess, vbox);
2585 
2586 	mg_add_pane_signals (gui);
2587 }
2588 
2589 static void
mg_change_nick(int cancel,char * text,gpointer userdata)2590 mg_change_nick (int cancel, char *text, gpointer userdata)
2591 {
2592 	char buf[256];
2593 
2594 	if (!cancel)
2595 	{
2596 		g_snprintf (buf, sizeof (buf), "nick %s", text);
2597 		handle_command (current_sess, buf, FALSE);
2598 	}
2599 }
2600 
2601 static void
mg_nickclick_cb(GtkWidget * button,gpointer userdata)2602 mg_nickclick_cb (GtkWidget *button, gpointer userdata)
2603 {
2604 	fe_get_str (_("Enter new nickname:"), current_sess->server->nick,
2605 					mg_change_nick, (void *) 1);
2606 }
2607 
2608 /* make sure chanview and userlist positions are sane */
2609 
2610 static void
mg_sanitize_positions(int * cv,int * ul)2611 mg_sanitize_positions (int *cv, int *ul)
2612 {
2613 	if (prefs.hex_gui_tab_layout == 2)
2614 	{
2615 		/* treeview can't be on TOP or BOTTOM */
2616 		if (*cv == POS_TOP || *cv == POS_BOTTOM)
2617 			*cv = POS_TOPLEFT;
2618 	}
2619 
2620 	/* userlist can't be on TOP or BOTTOM */
2621 	if (*ul == POS_TOP || *ul == POS_BOTTOM)
2622 		*ul = POS_TOPRIGHT;
2623 
2624 	/* can't have both in the same place */
2625 	if (*cv == *ul)
2626 	{
2627 		*cv = POS_TOPRIGHT;
2628 		if (*ul == POS_TOPRIGHT)
2629 			*cv = POS_BOTTOMRIGHT;
2630 	}
2631 }
2632 
2633 static void
mg_place_userlist_and_chanview_real(session_gui * gui,GtkWidget * userlist,GtkWidget * chanview)2634 mg_place_userlist_and_chanview_real (session_gui *gui, GtkWidget *userlist, GtkWidget *chanview)
2635 {
2636 	int unref_userlist = FALSE;
2637 	int unref_chanview = FALSE;
2638 
2639 	/* first, remove userlist/treeview from their containers */
2640 	if (userlist && gtk_widget_get_parent (userlist))
2641 	{
2642 		g_object_ref (userlist);
2643 		gtk_container_remove (GTK_CONTAINER (gtk_widget_get_parent (userlist)), userlist);
2644 		unref_userlist = TRUE;
2645 	}
2646 
2647 	if (chanview && gtk_widget_get_parent (chanview))
2648 	{
2649 		g_object_ref (chanview);
2650 		gtk_container_remove (GTK_CONTAINER (gtk_widget_get_parent (chanview)), chanview);
2651 		unref_chanview = TRUE;
2652 	}
2653 
2654 	if (chanview)
2655 	{
2656 		/* incase the previous pos was POS_HIDDEN */
2657 		gtk_widget_show (chanview);
2658 
2659 		gtk_table_set_row_spacing (GTK_TABLE (gui->main_table), 1, 0);
2660 		gtk_table_set_row_spacing (GTK_TABLE (gui->main_table), 2, 2);
2661 
2662 		/* then place them back in their new positions */
2663 		switch (prefs.hex_gui_tab_pos)
2664 		{
2665 		case POS_TOPLEFT:
2666 			gtk_paned_pack1 (GTK_PANED (gui->vpane_left), chanview, FALSE, TRUE);
2667 			break;
2668 		case POS_BOTTOMLEFT:
2669 			gtk_paned_pack2 (GTK_PANED (gui->vpane_left), chanview, FALSE, TRUE);
2670 			break;
2671 		case POS_TOPRIGHT:
2672 			gtk_paned_pack1 (GTK_PANED (gui->vpane_right), chanview, FALSE, TRUE);
2673 			break;
2674 		case POS_BOTTOMRIGHT:
2675 			gtk_paned_pack2 (GTK_PANED (gui->vpane_right), chanview, FALSE, TRUE);
2676 			break;
2677 		case POS_TOP:
2678 			gtk_table_set_row_spacing (GTK_TABLE (gui->main_table), 1, GUI_SPACING-1);
2679 			gtk_table_attach (GTK_TABLE (gui->main_table), chanview,
2680 									1, 2, 1, 2, GTK_FILL, GTK_FILL, 0, 0);
2681 			break;
2682 		case POS_HIDDEN:
2683 			gtk_widget_hide (chanview);
2684 			/* always attach it to something to avoid ref_count=0 */
2685 			if (prefs.hex_gui_ulist_pos == POS_TOP)
2686 				gtk_table_attach (GTK_TABLE (gui->main_table), chanview,
2687 										1, 2, 3, 4, GTK_FILL, GTK_FILL, 0, 0);
2688 
2689 			else
2690 				gtk_table_attach (GTK_TABLE (gui->main_table), chanview,
2691 										1, 2, 1, 2, GTK_FILL, GTK_FILL, 0, 0);
2692 			break;
2693 		default:/* POS_BOTTOM */
2694 			gtk_table_set_row_spacing (GTK_TABLE (gui->main_table), 2, 3);
2695 			gtk_table_attach (GTK_TABLE (gui->main_table), chanview,
2696 									1, 2, 3, 4, GTK_FILL, GTK_FILL, 0, 0);
2697 		}
2698 	}
2699 
2700 	if (userlist)
2701 	{
2702 		switch (prefs.hex_gui_ulist_pos)
2703 		{
2704 		case POS_TOPLEFT:
2705 			gtk_paned_pack1 (GTK_PANED (gui->vpane_left), userlist, FALSE, TRUE);
2706 			break;
2707 		case POS_BOTTOMLEFT:
2708 			gtk_paned_pack2 (GTK_PANED (gui->vpane_left), userlist, FALSE, TRUE);
2709 			break;
2710 		case POS_BOTTOMRIGHT:
2711 			gtk_paned_pack2 (GTK_PANED (gui->vpane_right), userlist, FALSE, TRUE);
2712 			break;
2713 		/*case POS_HIDDEN:
2714 			break;*/	/* Hide using the VIEW menu instead */
2715 		default:/* POS_TOPRIGHT */
2716 			gtk_paned_pack1 (GTK_PANED (gui->vpane_right), userlist, FALSE, TRUE);
2717 		}
2718 	}
2719 
2720 	if (mg_is_userlist_and_tree_combined () && prefs.hex_gui_pane_divider_position != 0)
2721 	{
2722 		gtk_paned_set_position (GTK_PANED (gui->vpane_left), prefs.hex_gui_pane_divider_position);
2723 		gtk_paned_set_position (GTK_PANED (gui->vpane_right), prefs.hex_gui_pane_divider_position);
2724 	}
2725 
2726 	if (unref_chanview)
2727 		g_object_unref (chanview);
2728 	if (unref_userlist)
2729 		g_object_unref (userlist);
2730 
2731 	mg_hide_empty_boxes (gui);
2732 }
2733 
2734 static void
mg_place_userlist_and_chanview(session_gui * gui)2735 mg_place_userlist_and_chanview (session_gui *gui)
2736 {
2737 	GtkOrientation orientation;
2738 	GtkWidget *chanviewbox = NULL;
2739 	int pos;
2740 
2741 	mg_sanitize_positions (&prefs.hex_gui_tab_pos, &prefs.hex_gui_ulist_pos);
2742 
2743 	if (gui->chanview)
2744 	{
2745 		pos = prefs.hex_gui_tab_pos;
2746 
2747 		orientation = chanview_get_orientation (gui->chanview);
2748 		if ((pos == POS_BOTTOM || pos == POS_TOP) && orientation == GTK_ORIENTATION_VERTICAL)
2749 			chanview_set_orientation (gui->chanview, FALSE);
2750 		else if ((pos == POS_TOPLEFT || pos == POS_BOTTOMLEFT || pos == POS_TOPRIGHT || pos == POS_BOTTOMRIGHT) && orientation == GTK_ORIENTATION_HORIZONTAL)
2751 			chanview_set_orientation (gui->chanview, TRUE);
2752 		chanviewbox = chanview_get_box (gui->chanview);
2753 	}
2754 
2755 	mg_place_userlist_and_chanview_real (gui, gui->user_box, chanviewbox);
2756 }
2757 
2758 void
mg_change_layout(int type)2759 mg_change_layout (int type)
2760 {
2761 	if (mg_gui)
2762 	{
2763 		/* put tabs at the bottom */
2764 		if (type == 0 && prefs.hex_gui_tab_pos != POS_BOTTOM && prefs.hex_gui_tab_pos != POS_TOP)
2765 			prefs.hex_gui_tab_pos = POS_BOTTOM;
2766 
2767 		mg_place_userlist_and_chanview (mg_gui);
2768 		chanview_set_impl (mg_gui->chanview, type);
2769 	}
2770 }
2771 
2772 static void
mg_inputbox_rightclick(GtkEntry * entry,GtkWidget * menu)2773 mg_inputbox_rightclick (GtkEntry *entry, GtkWidget *menu)
2774 {
2775 	mg_create_color_menu (menu, NULL);
2776 }
2777 
2778 /* Search bar adapted from Conspire's by William Pitcock */
2779 
2780 #define SEARCH_CHANGE		1
2781 #define SEARCH_NEXT			2
2782 #define SEARCH_PREVIOUS		3
2783 #define SEARCH_REFRESH		4
2784 
2785 static void
search_handle_event(int search_type,session * sess)2786 search_handle_event(int search_type, session *sess)
2787 {
2788 	textentry *last;
2789 	const gchar *text = NULL;
2790 	gtk_xtext_search_flags flags;
2791 	GError *err = NULL;
2792 	gboolean backwards = FALSE;
2793 
2794 	/* When just typing show most recent first */
2795 	if (search_type == SEARCH_PREVIOUS || search_type == SEARCH_CHANGE)
2796 		backwards = TRUE;
2797 
2798 	flags = ((prefs.hex_text_search_case_match == 1? case_match: 0) |
2799 				(backwards? backward: 0) |
2800 				(prefs.hex_text_search_highlight_all == 1? highlight: 0) |
2801 				(prefs.hex_text_search_follow == 1? follow: 0) |
2802 				(prefs.hex_text_search_regexp == 1? regexp: 0));
2803 
2804 	if (search_type != SEARCH_REFRESH)
2805 		text = gtk_entry_get_text (GTK_ENTRY(sess->gui->shentry));
2806 	last = gtk_xtext_search (GTK_XTEXT (sess->gui->xtext), text, flags, &err);
2807 
2808 	if (err)
2809 	{
2810 		gtk_entry_set_icon_from_stock (GTK_ENTRY (sess->gui->shentry), GTK_ENTRY_ICON_SECONDARY, GTK_STOCK_DIALOG_ERROR);
2811 		gtk_entry_set_icon_tooltip_text (GTK_ENTRY (sess->gui->shentry), GTK_ENTRY_ICON_SECONDARY, _(err->message));
2812 		g_error_free (err);
2813 	}
2814 	else if (!last)
2815 	{
2816 		if (text && text[0] == 0) /* empty string, no error */
2817 		{
2818 			gtk_entry_set_icon_from_stock (GTK_ENTRY (sess->gui->shentry), GTK_ENTRY_ICON_SECONDARY, NULL);
2819 		}
2820 		else
2821 		{
2822 			/* Either end of search or not found, try again to wrap if only end */
2823 			last = gtk_xtext_search (GTK_XTEXT (sess->gui->xtext), text, flags, &err);
2824 			if (!last) /* Not found error */
2825 			{
2826 				gtk_entry_set_icon_from_stock (GTK_ENTRY (sess->gui->shentry), GTK_ENTRY_ICON_SECONDARY, GTK_STOCK_DIALOG_ERROR);
2827 				gtk_entry_set_icon_tooltip_text (GTK_ENTRY (sess->gui->shentry), GTK_ENTRY_ICON_SECONDARY, _("No results found."));
2828 			}
2829 		}
2830 	}
2831 	else
2832 	{
2833 		gtk_entry_set_icon_from_stock (GTK_ENTRY (sess->gui->shentry), GTK_ENTRY_ICON_SECONDARY, NULL);
2834 	}
2835 }
2836 
2837 static void
search_handle_change(GtkWidget * wid,session * sess)2838 search_handle_change(GtkWidget *wid, session *sess)
2839 {
2840 	search_handle_event(SEARCH_CHANGE, sess);
2841 }
2842 
2843 static void
search_handle_refresh(GtkWidget * wid,session * sess)2844 search_handle_refresh(GtkWidget *wid, session *sess)
2845 {
2846 	search_handle_event(SEARCH_REFRESH, sess);
2847 }
2848 
2849 void
mg_search_handle_previous(GtkWidget * wid,session * sess)2850 mg_search_handle_previous(GtkWidget *wid, session *sess)
2851 {
2852 	search_handle_event(SEARCH_PREVIOUS, sess);
2853 }
2854 
2855 void
mg_search_handle_next(GtkWidget * wid,session * sess)2856 mg_search_handle_next(GtkWidget *wid, session *sess)
2857 {
2858 	search_handle_event(SEARCH_NEXT, sess);
2859 }
2860 
2861 static void
search_set_option(GtkToggleButton * but,guint * pref)2862 search_set_option (GtkToggleButton *but, guint *pref)
2863 {
2864 	*pref = gtk_toggle_button_get_active(but);
2865 	save_config();
2866 }
2867 
2868 void
mg_search_toggle(session * sess)2869 mg_search_toggle(session *sess)
2870 {
2871 	if (gtk_widget_get_visible(sess->gui->shbox))
2872 	{
2873 		gtk_widget_hide(sess->gui->shbox);
2874 		gtk_widget_grab_focus(sess->gui->input_box);
2875 		gtk_entry_set_text(GTK_ENTRY(sess->gui->shentry), "");
2876 	}
2877 	else
2878 	{
2879 		/* Reset search state */
2880 		gtk_entry_set_icon_from_stock (GTK_ENTRY (sess->gui->shentry), GTK_ENTRY_ICON_SECONDARY, NULL);
2881 
2882 		/* Show and focus */
2883 		gtk_widget_show(sess->gui->shbox);
2884 		gtk_widget_grab_focus(sess->gui->shentry);
2885 	}
2886 }
2887 
2888 static gboolean
search_handle_esc(GtkWidget * win,GdkEventKey * key,session * sess)2889 search_handle_esc (GtkWidget *win, GdkEventKey *key, session *sess)
2890 {
2891 	if (key->keyval == GDK_KEY_Escape)
2892 		mg_search_toggle(sess);
2893 
2894 	return FALSE;
2895 }
2896 
2897 static void
mg_create_search(session * sess,GtkWidget * box)2898 mg_create_search(session *sess, GtkWidget *box)
2899 {
2900 	GtkWidget *entry, *label, *next, *previous, *highlight, *matchcase, *regex, *close;
2901 	session_gui *gui = sess->gui;
2902 
2903 	gui->shbox = gtk_hbox_new(FALSE, 5);
2904 	gtk_box_pack_start(GTK_BOX(box), gui->shbox, FALSE, FALSE, 0);
2905 
2906 	close = gtk_button_new ();
2907 	gtk_button_set_image (GTK_BUTTON (close), gtk_image_new_from_stock (GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU));
2908 	gtk_button_set_relief(GTK_BUTTON(close), GTK_RELIEF_NONE);
2909 	gtk_widget_set_can_focus (close, FALSE);
2910 	gtk_box_pack_start(GTK_BOX(gui->shbox), close, FALSE, FALSE, 0);
2911 	g_signal_connect_swapped(G_OBJECT(close), "clicked", G_CALLBACK(mg_search_toggle), sess);
2912 
2913 	label = gtk_label_new(_("Find:"));
2914 	gtk_box_pack_start(GTK_BOX(gui->shbox), label, FALSE, FALSE, 0);
2915 
2916 	gui->shentry = entry = gtk_entry_new();
2917 	gtk_box_pack_start(GTK_BOX(gui->shbox), entry, FALSE, FALSE, 0);
2918 	gtk_widget_set_size_request (gui->shentry, 180, -1);
2919 	gui->search_changed_signal = g_signal_connect(G_OBJECT(entry), "changed", G_CALLBACK(search_handle_change), sess);
2920 	g_signal_connect (G_OBJECT (entry), "key_press_event", G_CALLBACK (search_handle_esc), sess);
2921 	g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(mg_search_handle_next), sess);
2922 	gtk_entry_set_icon_activatable (GTK_ENTRY (entry), GTK_ENTRY_ICON_SECONDARY, FALSE);
2923 	gtk_entry_set_icon_tooltip_text (GTK_ENTRY (sess->gui->shentry), GTK_ENTRY_ICON_SECONDARY, _("Search hit end or not found."));
2924 
2925 	previous = gtk_button_new ();
2926 	gtk_button_set_image (GTK_BUTTON (previous), gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU));
2927 	gtk_button_set_relief(GTK_BUTTON(previous), GTK_RELIEF_NONE);
2928 	gtk_widget_set_can_focus (previous, FALSE);
2929 	gtk_box_pack_start(GTK_BOX(gui->shbox), previous, FALSE, FALSE, 0);
2930 	g_signal_connect(G_OBJECT(previous), "clicked", G_CALLBACK(mg_search_handle_previous), sess);
2931 
2932 	next = gtk_button_new ();
2933 	gtk_button_set_image (GTK_BUTTON (next), gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU));
2934 	gtk_button_set_relief(GTK_BUTTON(next), GTK_RELIEF_NONE);
2935 	gtk_widget_set_can_focus (next, FALSE);
2936 	gtk_box_pack_start(GTK_BOX(gui->shbox), next, FALSE, FALSE, 0);
2937 	g_signal_connect(G_OBJECT(next), "clicked", G_CALLBACK(mg_search_handle_next), sess);
2938 
2939 	highlight = gtk_check_button_new_with_mnemonic (_("_Highlight all"));
2940 	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(highlight), prefs.hex_text_search_highlight_all);
2941 	gtk_widget_set_can_focus (highlight, FALSE);
2942 	g_signal_connect (G_OBJECT (highlight), "toggled", G_CALLBACK (search_set_option), &prefs.hex_text_search_highlight_all);
2943 	g_signal_connect (G_OBJECT (highlight), "toggled", G_CALLBACK (search_handle_refresh), sess);
2944 	gtk_box_pack_start(GTK_BOX(gui->shbox), highlight, FALSE, FALSE, 0);
2945 	gtk_widget_set_tooltip_text (highlight, _("Highlight all occurrences, and underline the current occurrence."));
2946 
2947 	matchcase = gtk_check_button_new_with_mnemonic (_("Mat_ch case"));
2948 	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(matchcase), prefs.hex_text_search_case_match);
2949 	gtk_widget_set_can_focus (matchcase, FALSE);
2950 	g_signal_connect (G_OBJECT (matchcase), "toggled", G_CALLBACK (search_set_option), &prefs.hex_text_search_case_match);
2951 	gtk_box_pack_start(GTK_BOX(gui->shbox), matchcase, FALSE, FALSE, 0);
2952 	gtk_widget_set_tooltip_text (matchcase, _("Perform a case-sensitive search."));
2953 
2954 	regex = gtk_check_button_new_with_mnemonic (_("_Regex"));
2955 	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(regex), prefs.hex_text_search_regexp);
2956 	gtk_widget_set_can_focus (regex, FALSE);
2957 	g_signal_connect (G_OBJECT (regex), "toggled", G_CALLBACK (search_set_option), &prefs.hex_text_search_regexp);
2958 	gtk_box_pack_start(GTK_BOX(gui->shbox), regex, FALSE, FALSE, 0);
2959 	gtk_widget_set_tooltip_text (regex, _("Regard search string as a regular expression."));
2960 }
2961 
2962 static void
mg_create_entry(session * sess,GtkWidget * box)2963 mg_create_entry (session *sess, GtkWidget *box)
2964 {
2965 	GtkWidget *hbox, *but, *entry;
2966 	session_gui *gui = sess->gui;
2967 
2968 	hbox = gtk_hbox_new (FALSE, 0);
2969 	gtk_box_pack_start (GTK_BOX (box), hbox, 0, 0, 0);
2970 
2971 	gui->nick_box = gtk_hbox_new (FALSE, 0);
2972 	gtk_box_pack_start (GTK_BOX (hbox), gui->nick_box, 0, 0, 0);
2973 
2974 	gui->nick_label = but = gtk_button_new_with_label (sess->server->nick);
2975 	gtk_button_set_relief (GTK_BUTTON (but), GTK_RELIEF_NONE);
2976 	gtk_widget_set_can_focus (but, FALSE);
2977 	gtk_box_pack_end (GTK_BOX (gui->nick_box), but, 0, 0, 0);
2978 	g_signal_connect (G_OBJECT (but), "clicked",
2979 							G_CALLBACK (mg_nickclick_cb), NULL);
2980 
2981 	gui->input_box = entry = sexy_spell_entry_new ();
2982 	sexy_spell_entry_set_checked ((SexySpellEntry *)entry, prefs.hex_gui_input_spell);
2983 	sexy_spell_entry_set_parse_attributes ((SexySpellEntry *)entry, prefs.hex_gui_input_attr);
2984 
2985 	gtk_entry_set_max_length (GTK_ENTRY (gui->input_box), 0);
2986 	g_signal_connect (G_OBJECT (entry), "activate",
2987 							G_CALLBACK (mg_inputbox_cb), gui);
2988 	gtk_container_add (GTK_CONTAINER (hbox), entry);
2989 
2990 	gtk_widget_set_name (entry, "hexchat-inputbox");
2991 	g_signal_connect (G_OBJECT (entry), "key_press_event",
2992 							G_CALLBACK (key_handle_key_press), NULL);
2993 	g_signal_connect (G_OBJECT (entry), "focus_in_event",
2994 							G_CALLBACK (mg_inputbox_focus), gui);
2995 	g_signal_connect (G_OBJECT (entry), "populate_popup",
2996 							G_CALLBACK (mg_inputbox_rightclick), NULL);
2997 	g_signal_connect (G_OBJECT (entry), "word-check",
2998 							G_CALLBACK (mg_spellcheck_cb), NULL);
2999 	gtk_widget_grab_focus (entry);
3000 
3001 	if (prefs.hex_gui_input_style)
3002 		mg_apply_entry_style (entry);
3003 }
3004 
3005 static void
mg_switch_tab_cb(chanview * cv,chan * ch,int tag,gpointer ud)3006 mg_switch_tab_cb (chanview *cv, chan *ch, int tag, gpointer ud)
3007 {
3008 	chan *old;
3009 	session *sess = ud;
3010 
3011 	old = active_tab;
3012 	active_tab = ch;
3013 
3014 	if (tag == TAG_IRC)
3015 	{
3016 		if (active_tab != old)
3017 		{
3018 			if (old && current_tab)
3019 				mg_unpopulate (current_tab);
3020 			mg_populate (sess);
3021 		}
3022 	} else if (old != active_tab)
3023 	{
3024 		/* userdata for non-irc tabs is actually the GtkBox */
3025 		mg_show_generic_tab (ud);
3026 		if (!mg_is_userlist_and_tree_combined ())
3027 			mg_userlist_showhide (current_sess, FALSE);	/* hide */
3028 	}
3029 }
3030 
3031 /* compare two tabs (for tab sorting function) */
3032 
3033 static int
mg_tabs_compare(session * a,session * b)3034 mg_tabs_compare (session *a, session *b)
3035 {
3036 	/* server tabs always go first */
3037 	if (a->type == SESS_SERVER)
3038 		return -1;
3039 
3040 	/* then channels */
3041 	if (a->type == SESS_CHANNEL && b->type != SESS_CHANNEL)
3042 		return -1;
3043 	if (a->type != SESS_CHANNEL && b->type == SESS_CHANNEL)
3044 		return 1;
3045 
3046 	return g_ascii_strcasecmp (a->channel, b->channel);
3047 }
3048 
3049 static void
mg_create_tabs(session_gui * gui)3050 mg_create_tabs (session_gui *gui)
3051 {
3052 	gboolean use_icons = FALSE;
3053 
3054 	/* if any one of these PNGs exist, the chanview will create
3055 	 * the extra column for icons. */
3056 	if (prefs.hex_gui_tab_icons && (pix_tree_channel || pix_tree_dialog || pix_tree_server || pix_tree_util))
3057 	{
3058 		use_icons = TRUE;
3059 	}
3060 
3061 	gui->chanview = chanview_new (prefs.hex_gui_tab_layout, prefs.hex_gui_tab_trunc,
3062 											prefs.hex_gui_tab_sort, use_icons,
3063 											prefs.hex_gui_ulist_style ? input_style : NULL);
3064 	chanview_set_callbacks (gui->chanview, mg_switch_tab_cb, mg_xbutton_cb,
3065 									mg_tab_contextmenu_cb, (void *)mg_tabs_compare);
3066 	mg_place_userlist_and_chanview (gui);
3067 }
3068 
3069 static gboolean
mg_tabwin_focus_cb(GtkWindow * win,GdkEventFocus * event,gpointer userdata)3070 mg_tabwin_focus_cb (GtkWindow * win, GdkEventFocus *event, gpointer userdata)
3071 {
3072 	current_sess = current_tab;
3073 	if (current_sess)
3074 	{
3075 		gtk_xtext_check_marker_visibility (GTK_XTEXT (current_sess->gui->xtext));
3076 		plugin_emit_dummy_print (current_sess, "Focus Window");
3077 	}
3078 	unflash_window (GTK_WIDGET (win));
3079 	return FALSE;
3080 }
3081 
3082 static gboolean
mg_topwin_focus_cb(GtkWindow * win,GdkEventFocus * event,session * sess)3083 mg_topwin_focus_cb (GtkWindow * win, GdkEventFocus *event, session *sess)
3084 {
3085 	current_sess = sess;
3086 	if (!sess->server->server_session)
3087 		sess->server->server_session = sess;
3088 	gtk_xtext_check_marker_visibility(GTK_XTEXT (current_sess->gui->xtext));
3089 	unflash_window (GTK_WIDGET (win));
3090 	plugin_emit_dummy_print (sess, "Focus Window");
3091 	return FALSE;
3092 }
3093 
3094 static void
mg_create_menu(session_gui * gui,GtkWidget * table,int away_state)3095 mg_create_menu (session_gui *gui, GtkWidget *table, int away_state)
3096 {
3097 	GtkAccelGroup *accel_group;
3098 
3099 	accel_group = gtk_accel_group_new ();
3100 	gtk_window_add_accel_group (GTK_WINDOW (gtk_widget_get_toplevel (table)),
3101 										 accel_group);
3102 	g_object_unref (accel_group);
3103 
3104 	gui->menu = menu_create_main (accel_group, TRUE, away_state, !gui->is_tab,
3105 											gui->menu_item);
3106 	gtk_table_attach (GTK_TABLE (table), gui->menu, 0, 3, 0, 1,
3107 						   GTK_EXPAND | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
3108 }
3109 
3110 static void
mg_create_irctab(session * sess,GtkWidget * table)3111 mg_create_irctab (session *sess, GtkWidget *table)
3112 {
3113 	GtkWidget *vbox;
3114 	session_gui *gui = sess->gui;
3115 
3116 	vbox = gtk_vbox_new (FALSE, 0);
3117 	gtk_table_attach (GTK_TABLE (table), vbox, 1, 2, 2, 3,
3118 						   GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
3119 	mg_create_center (sess, gui, vbox);
3120 }
3121 
3122 static void
mg_create_topwindow(session * sess)3123 mg_create_topwindow (session *sess)
3124 {
3125 	GtkWidget *win;
3126 	GtkWidget *table;
3127 
3128 	if (sess->type == SESS_DIALOG)
3129 		win = gtkutil_window_new ("HexChat", NULL,
3130 										  prefs.hex_gui_dialog_width, prefs.hex_gui_dialog_height, 0);
3131 	else
3132 		win = gtkutil_window_new ("HexChat", NULL,
3133 										  prefs.hex_gui_win_width,
3134 										  prefs.hex_gui_win_height, 0);
3135 	sess->gui->window = win;
3136 	gtk_container_set_border_width (GTK_CONTAINER (win), GUI_BORDER);
3137 	gtk_window_set_opacity (GTK_WINDOW (win), (prefs.hex_gui_transparency / 255.));
3138 
3139 	g_signal_connect (G_OBJECT (win), "focus_in_event",
3140 							G_CALLBACK (mg_topwin_focus_cb), sess);
3141 	g_signal_connect (G_OBJECT (win), "destroy",
3142 							G_CALLBACK (mg_topdestroy_cb), sess);
3143 	g_signal_connect (G_OBJECT (win), "configure_event",
3144 							G_CALLBACK (mg_configure_cb), sess);
3145 
3146 	palette_alloc (win);
3147 
3148 	table = gtk_table_new (4, 3, FALSE);
3149 	/* spacing under the menubar */
3150 	gtk_table_set_row_spacing (GTK_TABLE (table), 0, GUI_SPACING);
3151 	/* left and right borders */
3152 	gtk_table_set_col_spacing (GTK_TABLE (table), 0, 1);
3153 	gtk_table_set_col_spacing (GTK_TABLE (table), 1, 1);
3154 	gtk_container_add (GTK_CONTAINER (win), table);
3155 
3156 	mg_create_irctab (sess, table);
3157 	mg_create_menu (sess->gui, table, sess->server->is_away);
3158 
3159 	if (sess->res->buffer == NULL)
3160 	{
3161 		sess->res->buffer = gtk_xtext_buffer_new (GTK_XTEXT (sess->gui->xtext));
3162 		gtk_xtext_buffer_show (GTK_XTEXT (sess->gui->xtext), sess->res->buffer, TRUE);
3163 		gtk_xtext_set_time_stamp (sess->res->buffer, prefs.hex_stamp_text);
3164 		sess->res->user_model = userlist_create_model (sess);
3165 	}
3166 
3167 	userlist_show (sess);
3168 
3169 	gtk_widget_show_all (table);
3170 
3171 	if (prefs.hex_gui_hide_menu)
3172 		gtk_widget_hide (sess->gui->menu);
3173 
3174 	/* Will be shown when needed */
3175 	gtk_widget_hide (sess->gui->topic_bar);
3176 
3177 	if (!prefs.hex_gui_ulist_buttons)
3178 		gtk_widget_hide (sess->gui->button_box);
3179 
3180 	if (!prefs.hex_gui_input_nick)
3181 		gtk_widget_hide (sess->gui->nick_box);
3182 
3183 	gtk_widget_hide(sess->gui->shbox);
3184 
3185 	mg_decide_userlist (sess, FALSE);
3186 
3187 	if (sess->type == SESS_DIALOG)
3188 	{
3189 		/* hide the chan-mode buttons */
3190 		gtk_widget_hide (sess->gui->topicbutton_box);
3191 	} else
3192 	{
3193 		gtk_widget_hide (sess->gui->dialogbutton_box);
3194 
3195 		if (!prefs.hex_gui_mode_buttons)
3196 			gtk_widget_hide (sess->gui->topicbutton_box);
3197 	}
3198 
3199 	mg_place_userlist_and_chanview (sess->gui);
3200 
3201 	gtk_widget_show (win);
3202 }
3203 
3204 static gboolean
mg_tabwindow_de_cb(GtkWidget * widget,GdkEvent * event,gpointer user_data)3205 mg_tabwindow_de_cb (GtkWidget *widget, GdkEvent *event, gpointer user_data)
3206 {
3207 	GSList *list;
3208 	session *sess;
3209 	GtkWindow *win = GTK_WINDOW(gtk_widget_get_toplevel (widget));
3210 
3211 	if (prefs.hex_gui_tray_close && gtkutil_tray_icon_supported (win) && tray_toggle_visibility (FALSE))
3212 		return TRUE;
3213 
3214 	/* check for remaining toplevel windows */
3215 	list = sess_list;
3216 	while (list)
3217 	{
3218 		sess = list->data;
3219 		if (!sess->gui->is_tab)
3220 			return FALSE;
3221 		list = list->next;
3222 	}
3223 
3224 	mg_open_quit_dialog (TRUE);
3225 	return TRUE;
3226 }
3227 
3228 #ifdef G_OS_WIN32
3229 static GdkFilterReturn
mg_time_change(GdkXEvent * xevent,GdkEvent * event,gpointer data)3230 mg_time_change (GdkXEvent *xevent, GdkEvent *event, gpointer data)
3231 {
3232 	MSG *msg = (MSG*)xevent;
3233 
3234 	if (msg->message == WM_TIMECHANGE)
3235 	{
3236 		_tzset();
3237 	}
3238 
3239 	return GDK_FILTER_CONTINUE;
3240 }
3241 #endif
3242 
3243 static void
mg_create_tabwindow(session * sess)3244 mg_create_tabwindow (session *sess)
3245 {
3246 	GtkWidget *win;
3247 	GtkWidget *table;
3248 #ifdef G_OS_WIN32
3249 	GdkWindow *parent_win;
3250 #endif
3251 
3252 	win = gtkutil_window_new ("HexChat", NULL, prefs.hex_gui_win_width,
3253 									  prefs.hex_gui_win_height, 0);
3254 	sess->gui->window = win;
3255 	gtk_window_move (GTK_WINDOW (win), prefs.hex_gui_win_left,
3256 						  prefs.hex_gui_win_top);
3257 	if (prefs.hex_gui_win_state)
3258 		gtk_window_maximize (GTK_WINDOW (win));
3259 	if (prefs.hex_gui_win_fullscreen)
3260 		gtk_window_fullscreen (GTK_WINDOW (win));
3261 	gtk_window_set_opacity (GTK_WINDOW (win), (prefs.hex_gui_transparency / 255.));
3262 	gtk_container_set_border_width (GTK_CONTAINER (win), GUI_BORDER);
3263 
3264 	g_signal_connect (G_OBJECT (win), "delete_event",
3265 						   G_CALLBACK (mg_tabwindow_de_cb), 0);
3266 	g_signal_connect (G_OBJECT (win), "destroy",
3267 						   G_CALLBACK (mg_tabwindow_kill_cb), 0);
3268 	g_signal_connect (G_OBJECT (win), "focus_in_event",
3269 							G_CALLBACK (mg_tabwin_focus_cb), NULL);
3270 	g_signal_connect (G_OBJECT (win), "configure_event",
3271 							G_CALLBACK (mg_configure_cb), NULL);
3272 	g_signal_connect (G_OBJECT (win), "window_state_event",
3273 							G_CALLBACK (mg_windowstate_cb), NULL);
3274 
3275 	palette_alloc (win);
3276 
3277 	sess->gui->main_table = table = gtk_table_new (4, 3, FALSE);
3278 	/* spacing under the menubar */
3279 	gtk_table_set_row_spacing (GTK_TABLE (table), 0, GUI_SPACING);
3280 	/* left and right borders */
3281 	gtk_table_set_col_spacing (GTK_TABLE (table), 0, 1);
3282 	gtk_table_set_col_spacing (GTK_TABLE (table), 1, 1);
3283 	gtk_container_add (GTK_CONTAINER (win), table);
3284 
3285 	mg_create_irctab (sess, table);
3286 	mg_create_tabs (sess->gui);
3287 	mg_create_menu (sess->gui, table, sess->server->is_away);
3288 
3289 	mg_focus (sess);
3290 
3291 	gtk_widget_show_all (table);
3292 
3293 	if (prefs.hex_gui_hide_menu)
3294 		gtk_widget_hide (sess->gui->menu);
3295 
3296 	mg_decide_userlist (sess, FALSE);
3297 
3298 	/* Will be shown when needed */
3299 	gtk_widget_hide (sess->gui->topic_bar);
3300 
3301 	if (!prefs.hex_gui_mode_buttons)
3302 		gtk_widget_hide (sess->gui->topicbutton_box);
3303 
3304 	if (!prefs.hex_gui_ulist_buttons)
3305 		gtk_widget_hide (sess->gui->button_box);
3306 
3307 	if (!prefs.hex_gui_input_nick)
3308 		gtk_widget_hide (sess->gui->nick_box);
3309 
3310 	gtk_widget_hide (sess->gui->shbox);
3311 
3312 	mg_place_userlist_and_chanview (sess->gui);
3313 
3314 	gtk_widget_show (win);
3315 
3316 #ifdef G_OS_WIN32
3317 	parent_win = gtk_widget_get_window (win);
3318 	gdk_window_add_filter (parent_win, mg_time_change, NULL);
3319 #endif
3320 }
3321 
3322 void
mg_apply_setup(void)3323 mg_apply_setup (void)
3324 {
3325 	GSList *list = sess_list;
3326 	session *sess;
3327 	int done_main = FALSE;
3328 
3329 	mg_create_tab_colors ();
3330 
3331 	while (list)
3332 	{
3333 		sess = list->data;
3334 		gtk_xtext_set_time_stamp (sess->res->buffer, prefs.hex_stamp_text);
3335 		((xtext_buffer *)sess->res->buffer)->needs_recalc = TRUE;
3336 		if (!sess->gui->is_tab || !done_main)
3337 			mg_place_userlist_and_chanview (sess->gui);
3338 		if (sess->gui->is_tab)
3339 			done_main = TRUE;
3340 		list = list->next;
3341 	}
3342 }
3343 
3344 static chan *
mg_add_generic_tab(char * name,char * title,void * family,GtkWidget * box)3345 mg_add_generic_tab (char *name, char *title, void *family, GtkWidget *box)
3346 {
3347 	chan *ch;
3348 
3349 	gtk_notebook_append_page (GTK_NOTEBOOK (mg_gui->note_book), box, NULL);
3350 	gtk_widget_show (box);
3351 
3352 	ch = chanview_add (mg_gui->chanview, name, NULL, box, TRUE, TAG_UTIL, pix_tree_util);
3353 	chan_set_color (ch, plain_list);
3354 
3355 	g_object_set_data_full (G_OBJECT (box), "title", g_strdup (title), g_free);
3356 	g_object_set_data (G_OBJECT (box), "ch", ch);
3357 
3358 	if (prefs.hex_gui_tab_newtofront)
3359 		chan_focus (ch);
3360 
3361 	return ch;
3362 }
3363 
3364 void
fe_buttons_update(session * sess)3365 fe_buttons_update (session *sess)
3366 {
3367 	session_gui *gui = sess->gui;
3368 
3369 	gtk_widget_destroy (gui->button_box);
3370 	gui->button_box = mg_create_userlistbuttons (gui->button_box_parent);
3371 
3372 	if (prefs.hex_gui_ulist_buttons)
3373 		gtk_widget_show (sess->gui->button_box);
3374 	else
3375 		gtk_widget_hide (sess->gui->button_box);
3376 }
3377 
3378 void
fe_clear_channel(session * sess)3379 fe_clear_channel (session *sess)
3380 {
3381 	char tbuf[CHANLEN+6];
3382 	session_gui *gui = sess->gui;
3383 
3384 	if (sess->gui->is_tab)
3385 	{
3386 		if (sess->waitchannel[0])
3387 		{
3388 			if (prefs.hex_gui_tab_trunc > 2 && g_utf8_strlen (sess->waitchannel, -1) > prefs.hex_gui_tab_trunc)
3389 			{
3390 				/* truncate long channel names */
3391 				tbuf[0] = '(';
3392 				strcpy (tbuf + 1, sess->waitchannel);
3393 				g_utf8_offset_to_pointer(tbuf, prefs.hex_gui_tab_trunc)[0] = 0;
3394 				strcat (tbuf, "..)");
3395 			} else
3396 			{
3397 				sprintf (tbuf, "(%s)", sess->waitchannel);
3398 			}
3399 		}
3400 		else
3401 			strcpy (tbuf, _("<none>"));
3402 		chan_rename (sess->res->tab, tbuf, prefs.hex_gui_tab_trunc);
3403 	}
3404 
3405 	if (!sess->gui->is_tab || sess == current_tab)
3406 	{
3407 		gtk_entry_set_text (GTK_ENTRY (gui->topic_entry), "");
3408 
3409 		if (gui->op_xpm)
3410 		{
3411 			gtk_widget_destroy (gui->op_xpm);
3412 			gui->op_xpm = 0;
3413 		}
3414 	} else
3415 	{
3416 		if (sess->res->topic_text)
3417 		{
3418 			g_free (sess->res->topic_text);
3419 			sess->res->topic_text = NULL;
3420 		}
3421 	}
3422 }
3423 
3424 void
fe_set_nonchannel(session * sess,int state)3425 fe_set_nonchannel (session *sess, int state)
3426 {
3427 }
3428 
3429 void
fe_dlgbuttons_update(session * sess)3430 fe_dlgbuttons_update (session *sess)
3431 {
3432 	GtkWidget *box;
3433 	session_gui *gui = sess->gui;
3434 
3435 	gtk_widget_destroy (gui->dialogbutton_box);
3436 
3437 	gui->dialogbutton_box = box = gtk_hbox_new (0, 0);
3438 	gtk_box_pack_start (GTK_BOX (gui->topic_bar), box, 0, 0, 0);
3439 	gtk_box_reorder_child (GTK_BOX (gui->topic_bar), box, 3);
3440 	mg_create_dialogbuttons (box);
3441 
3442 	gtk_widget_show_all (box);
3443 
3444 	if (current_tab && current_tab->type != SESS_DIALOG)
3445 		gtk_widget_hide (current_tab->gui->dialogbutton_box);
3446 }
3447 
3448 void
fe_update_mode_buttons(session * sess,char mode,char sign)3449 fe_update_mode_buttons (session *sess, char mode, char sign)
3450 {
3451 	int state, i;
3452 
3453 	if (sign == '+')
3454 		state = TRUE;
3455 	else
3456 		state = FALSE;
3457 
3458 	for (i = 0; i < NUM_FLAG_WIDS - 1; i++)
3459 	{
3460 		if (chan_flags[i] == mode)
3461 		{
3462 			if (!sess->gui->is_tab || sess == current_tab)
3463 			{
3464 				ignore_chanmode = TRUE;
3465 				if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (sess->gui->flag_wid[i])) != state)
3466 					gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (sess->gui->flag_wid[i]), state);
3467 				ignore_chanmode = FALSE;
3468 			} else
3469 			{
3470 				sess->res->flag_wid_state[i] = state;
3471 			}
3472 			return;
3473 		}
3474 	}
3475 }
3476 
3477 void
fe_set_nick(server * serv,char * newnick)3478 fe_set_nick (server *serv, char *newnick)
3479 {
3480 	GSList *list = sess_list;
3481 	session *sess;
3482 
3483 	while (list)
3484 	{
3485 		sess = list->data;
3486 		if (sess->server == serv)
3487 		{
3488 			if (current_tab == sess || !sess->gui->is_tab)
3489 				gtk_button_set_label (GTK_BUTTON (sess->gui->nick_label), newnick);
3490 		}
3491 		list = list->next;
3492 	}
3493 }
3494 
3495 void
fe_set_away(server * serv)3496 fe_set_away (server *serv)
3497 {
3498 	GSList *list = sess_list;
3499 	session *sess;
3500 
3501 	while (list)
3502 	{
3503 		sess = list->data;
3504 		if (sess->server == serv)
3505 		{
3506 			if (!sess->gui->is_tab || sess == current_tab)
3507 			{
3508 				menu_set_away (sess->gui, serv->is_away);
3509 				/* gray out my nickname */
3510 				mg_set_myself_away (sess->gui, serv->is_away);
3511 			}
3512 		}
3513 		list = list->next;
3514 	}
3515 }
3516 
3517 void
fe_set_channel(session * sess)3518 fe_set_channel (session *sess)
3519 {
3520 	if (sess->res->tab != NULL)
3521 		chan_rename (sess->res->tab, sess->channel, prefs.hex_gui_tab_trunc);
3522 }
3523 
3524 void
mg_changui_new(session * sess,restore_gui * res,int tab,int focus)3525 mg_changui_new (session *sess, restore_gui *res, int tab, int focus)
3526 {
3527 	int first_run = FALSE;
3528 	session_gui *gui;
3529 
3530 	if (res == NULL)
3531 	{
3532 		res = g_new0 (restore_gui, 1);
3533 	}
3534 
3535 	sess->res = res;
3536 
3537 	if (sess->server->front_session == NULL)
3538 	{
3539 		sess->server->front_session = sess;
3540 	}
3541 
3542 	if (!tab)
3543 	{
3544 		gui = g_new0 (session_gui, 1);
3545 		gui->is_tab = FALSE;
3546 		sess->gui = gui;
3547 		mg_create_topwindow (sess);
3548 		fe_set_title (sess);
3549 		return;
3550 	}
3551 
3552 	if (mg_gui == NULL)
3553 	{
3554 		first_run = TRUE;
3555 		gui = &static_mg_gui;
3556 		memset (gui, 0, sizeof (session_gui));
3557 		gui->is_tab = TRUE;
3558 		sess->gui = gui;
3559 		mg_create_tabwindow (sess);
3560 		mg_gui = gui;
3561 		parent_window = gui->window;
3562 	} else
3563 	{
3564 		sess->gui = gui = mg_gui;
3565 		gui->is_tab = TRUE;
3566 	}
3567 
3568 	mg_add_chan (sess);
3569 
3570 	if (first_run || (prefs.hex_gui_tab_newtofront == FOCUS_NEW_ONLY_ASKED && focus)
3571 			|| prefs.hex_gui_tab_newtofront == FOCUS_NEW_ALL )
3572 		chan_focus (res->tab);
3573 }
3574 
3575 GtkWidget *
mg_create_generic_tab(char * name,char * title,int force_toplevel,int link_buttons,void * close_callback,void * userdata,int width,int height,GtkWidget ** vbox_ret,void * family)3576 mg_create_generic_tab (char *name, char *title, int force_toplevel,
3577 							  int link_buttons,
3578 							  void *close_callback, void *userdata,
3579 							  int width, int height, GtkWidget **vbox_ret,
3580 							  void *family)
3581 {
3582 	GtkWidget *vbox, *win;
3583 
3584 	if (prefs.hex_gui_tab_pos == POS_HIDDEN && prefs.hex_gui_tab_utils)
3585 		prefs.hex_gui_tab_utils = 0;
3586 
3587 	if (force_toplevel || !prefs.hex_gui_tab_utils)
3588 	{
3589 		win = gtkutil_window_new (title, name, width, height, 2);
3590 		vbox = gtk_vbox_new (0, 0);
3591 		*vbox_ret = vbox;
3592 		gtk_container_add (GTK_CONTAINER (win), vbox);
3593 		gtk_widget_show (vbox);
3594 		if (close_callback)
3595 			g_signal_connect (G_OBJECT (win), "destroy",
3596 									G_CALLBACK (close_callback), userdata);
3597 		return win;
3598 	}
3599 
3600 	vbox = gtk_vbox_new (0, 2);
3601 	g_object_set_data (G_OBJECT (vbox), "w", GINT_TO_POINTER (width));
3602 	g_object_set_data (G_OBJECT (vbox), "h", GINT_TO_POINTER (height));
3603 	gtk_container_set_border_width (GTK_CONTAINER (vbox), 3);
3604 	*vbox_ret = vbox;
3605 
3606 	if (close_callback)
3607 		g_signal_connect (G_OBJECT (vbox), "destroy",
3608 								G_CALLBACK (close_callback), userdata);
3609 
3610 	mg_add_generic_tab (name, title, family, vbox);
3611 
3612 /*	if (link_buttons)
3613 	{
3614 		hbox = gtk_hbox_new (FALSE, 0);
3615 		gtk_box_pack_start (GTK_BOX (vbox), hbox, 0, 0, 0);
3616 		mg_create_link_buttons (hbox, ch);
3617 		gtk_widget_show (hbox);
3618 	}*/
3619 
3620 	return vbox;
3621 }
3622 
3623 void
mg_move_tab(session * sess,int delta)3624 mg_move_tab (session *sess, int delta)
3625 {
3626 	if (sess->gui->is_tab)
3627 		chan_move (sess->res->tab, delta);
3628 }
3629 
3630 void
mg_move_tab_family(session * sess,int delta)3631 mg_move_tab_family (session *sess, int delta)
3632 {
3633 	if (sess->gui->is_tab)
3634 		chan_move_family (sess->res->tab, delta);
3635 }
3636 
3637 void
mg_set_title(GtkWidget * vbox,char * title)3638 mg_set_title (GtkWidget *vbox, char *title) /* for non-irc tab/window only */
3639 {
3640 	char *old;
3641 
3642 	old = g_object_get_data (G_OBJECT (vbox), "title");
3643 	if (old)
3644 	{
3645 		g_object_set_data_full (G_OBJECT (vbox), "title", g_strdup (title), g_free);
3646 	} else
3647 	{
3648 		gtk_window_set_title (GTK_WINDOW (vbox), title);
3649 	}
3650 }
3651 
3652 void
fe_server_callback(server * serv)3653 fe_server_callback (server *serv)
3654 {
3655 	joind_close (serv);
3656 
3657 	if (serv->gui->chanlist_window)
3658 		mg_close_gen (NULL, serv->gui->chanlist_window);
3659 
3660 	if (serv->gui->rawlog_window)
3661 		mg_close_gen (NULL, serv->gui->rawlog_window);
3662 
3663 	g_free (serv->gui);
3664 }
3665 
3666 /* called when a session is being killed */
3667 
3668 void
fe_session_callback(session * sess)3669 fe_session_callback (session *sess)
3670 {
3671 	gtk_xtext_buffer_free (sess->res->buffer);
3672 	g_object_unref (G_OBJECT (sess->res->user_model));
3673 
3674 	if (sess->res->banlist && sess->res->banlist->window)
3675 		mg_close_gen (NULL, sess->res->banlist->window);
3676 
3677 	g_free (sess->res->input_text);
3678 	g_free (sess->res->topic_text);
3679 	g_free (sess->res->limit_text);
3680 	g_free (sess->res->key_text);
3681 	g_free (sess->res->queue_text);
3682 	g_free (sess->res->queue_tip);
3683 	g_free (sess->res->lag_text);
3684 	g_free (sess->res->lag_tip);
3685 
3686 	if (sess->gui->bartag)
3687 		fe_timeout_remove (sess->gui->bartag);
3688 
3689 	if (sess->gui != &static_mg_gui)
3690 		g_free (sess->gui);
3691 	g_free (sess->res);
3692 }
3693 
3694 /* ===== DRAG AND DROP STUFF ===== */
3695 
3696 static gboolean
is_child_of(GtkWidget * widget,GtkWidget * parent)3697 is_child_of (GtkWidget *widget, GtkWidget *parent)
3698 {
3699 	while (widget)
3700 	{
3701 		if (gtk_widget_get_parent (widget) == parent)
3702 			return TRUE;
3703 		widget = gtk_widget_get_parent (widget);
3704 	}
3705 	return FALSE;
3706 }
3707 
3708 static void
mg_handle_drop(GtkWidget * widget,int y,int * pos,int * other_pos)3709 mg_handle_drop (GtkWidget *widget, int y, int *pos, int *other_pos)
3710 {
3711 	int height;
3712 	session_gui *gui = current_sess->gui;
3713 
3714 	height = gdk_window_get_height (gtk_widget_get_window (widget));
3715 
3716 	if (y < height / 2)
3717 	{
3718 		if (is_child_of (widget, gui->vpane_left))
3719 			*pos = 1;	/* top left */
3720 		else
3721 			*pos = 3;	/* top right */
3722 	}
3723 	else
3724 	{
3725 		if (is_child_of (widget, gui->vpane_left))
3726 			*pos = 2;	/* bottom left */
3727 		else
3728 			*pos = 4;	/* bottom right */
3729 	}
3730 
3731 	/* both in the same pos? must move one */
3732 	if (*pos == *other_pos)
3733 	{
3734 		switch (*other_pos)
3735 		{
3736 		case 1:
3737 			*other_pos = 2;
3738 			break;
3739 		case 2:
3740 			*other_pos = 1;
3741 			break;
3742 		case 3:
3743 			*other_pos = 4;
3744 			break;
3745 		case 4:
3746 			*other_pos = 3;
3747 			break;
3748 		}
3749 	}
3750 
3751 	mg_place_userlist_and_chanview (gui);
3752 }
3753 
3754 static gboolean
mg_is_gui_target(GdkDragContext * context)3755 mg_is_gui_target (GdkDragContext *context)
3756 {
3757 	char *target_name;
3758 
3759 	if (!context || !gdk_drag_context_list_targets (context) || !gdk_drag_context_list_targets (context)->data)
3760 		return FALSE;
3761 
3762 	target_name = gdk_atom_name (gdk_drag_context_list_targets (context)->data);
3763 	if (target_name)
3764 	{
3765 		/* if it's not HEXCHAT_CHANVIEW or HEXCHAT_USERLIST */
3766 		/* we should ignore it. */
3767 		if (target_name[0] != 'H')
3768 		{
3769 			g_free (target_name);
3770 			return FALSE;
3771 		}
3772 		g_free (target_name);
3773 	}
3774 
3775 	return TRUE;
3776 }
3777 
3778 /* this begin callback just creates an nice of the source */
3779 
3780 gboolean
mg_drag_begin_cb(GtkWidget * widget,GdkDragContext * context,gpointer userdata)3781 mg_drag_begin_cb (GtkWidget *widget, GdkDragContext *context, gpointer userdata)
3782 {
3783 	int width, height;
3784 	GdkColormap *cmap;
3785 	GdkPixbuf *pix, *pix2;
3786 
3787 	/* ignore file drops */
3788 	if (!mg_is_gui_target (context))
3789 		return FALSE;
3790 
3791 	cmap = gtk_widget_get_colormap (widget);
3792 	width = gdk_window_get_width (gtk_widget_get_window (widget));
3793 	height = gdk_window_get_height (gtk_widget_get_window (widget));
3794 
3795 	pix = gdk_pixbuf_get_from_drawable (NULL, gtk_widget_get_window (widget), cmap, 0, 0, 0, 0, width, height);
3796 	pix2 = gdk_pixbuf_scale_simple (pix, width * 4 / 5, height / 2, GDK_INTERP_HYPER);
3797 	g_object_unref (pix);
3798 
3799 	gtk_drag_set_icon_pixbuf (context, pix2, 0, 0);
3800 	g_object_set_data (G_OBJECT (widget), "ico", pix2);
3801 
3802 	return TRUE;
3803 }
3804 
3805 void
mg_drag_end_cb(GtkWidget * widget,GdkDragContext * context,gpointer userdata)3806 mg_drag_end_cb (GtkWidget *widget, GdkDragContext *context, gpointer userdata)
3807 {
3808 	/* ignore file drops */
3809 	if (!mg_is_gui_target (context))
3810 		return;
3811 
3812 	g_object_unref (g_object_get_data (G_OBJECT (widget), "ico"));
3813 }
3814 
3815 /* drop complete */
3816 
3817 gboolean
mg_drag_drop_cb(GtkWidget * widget,GdkDragContext * context,int x,int y,guint time,gpointer user_data)3818 mg_drag_drop_cb (GtkWidget *widget, GdkDragContext *context, int x, int y, guint time, gpointer user_data)
3819 {
3820 	/* ignore file drops */
3821 	if (!mg_is_gui_target (context))
3822 		return FALSE;
3823 
3824 	switch (gdk_drag_context_get_selected_action (context))
3825 	{
3826 	case GDK_ACTION_MOVE:
3827 		/* from userlist */
3828 		mg_handle_drop (widget, y, &prefs.hex_gui_ulist_pos, &prefs.hex_gui_tab_pos);
3829 		break;
3830 	case GDK_ACTION_COPY:
3831 		/* from tree - we use GDK_ACTION_COPY for the tree */
3832 		mg_handle_drop (widget, y, &prefs.hex_gui_tab_pos, &prefs.hex_gui_ulist_pos);
3833 		break;
3834 	default:
3835 		return FALSE;
3836 	}
3837 
3838 	return TRUE;
3839 }
3840 
3841 /* draw highlight rectangle in the destination */
3842 
3843 gboolean
mg_drag_motion_cb(GtkWidget * widget,GdkDragContext * context,int x,int y,guint time,gpointer scbar)3844 mg_drag_motion_cb (GtkWidget *widget, GdkDragContext *context, int x, int y, guint time, gpointer scbar)
3845 {
3846 	GdkGC *gc;
3847 	GdkColor col;
3848 	GdkGCValues val;
3849 	int half, width, height;
3850 	int ox, oy;
3851 	GdkDrawable *draw;
3852 	GtkAllocation allocation;
3853 
3854 	/* ignore file drops */
3855 	if (!mg_is_gui_target (context))
3856 		return FALSE;
3857 
3858 	if (scbar)	/* scrollbar */
3859 	{
3860 		gtk_widget_get_allocation (widget, &allocation);
3861 		ox = allocation.x;
3862 		oy = allocation.y;
3863 		width = allocation.width;
3864 		height = allocation.height;
3865 		draw = gtk_widget_get_window (widget);
3866 	}
3867 	else
3868 	{
3869 		ox = oy = 0;
3870 		width = gdk_window_get_width (gtk_widget_get_window (widget));
3871 		height = gdk_window_get_height (gtk_widget_get_window (widget));
3872 		draw = gtk_widget_get_window (widget);
3873 	}
3874 
3875 	val.subwindow_mode = GDK_INCLUDE_INFERIORS;
3876 	val.graphics_exposures = 0;
3877 	val.function = GDK_XOR;
3878 
3879 	gc = gdk_gc_new_with_values (gtk_widget_get_window (widget), &val, GDK_GC_EXPOSURES | GDK_GC_SUBWINDOW | GDK_GC_FUNCTION);
3880 	col.red = rand() % 0xffff;
3881 	col.green = rand() % 0xffff;
3882 	col.blue = rand() % 0xffff;
3883 	gdk_colormap_alloc_color (gtk_widget_get_colormap (widget), &col, FALSE, TRUE);
3884 	gdk_gc_set_foreground (gc, &col);
3885 
3886 	half = height / 2;
3887 
3888 #if 0
3889 	/* are both tree/userlist on the same side? */
3890 	paned = (GtkPaned *)widget->parent->parent;
3891 	if (paned->child1 != NULL && paned->child2 != NULL)
3892 	{
3893 		gdk_draw_rectangle (draw, gc, 0, 1, 2, width - 3, height - 4);
3894 		gdk_draw_rectangle (draw, gc, 0, 0, 1, width - 1, height - 2);
3895 		g_object_unref (gc);
3896 		return TRUE;
3897 	}
3898 #endif
3899 
3900 	if (y < half)
3901 	{
3902 		gdk_draw_rectangle (draw, gc, FALSE, 1 + ox, 2 + oy, width - 3, half - 4);
3903 		gdk_draw_rectangle (draw, gc, FALSE, 0 + ox, 1 + oy, width - 1, half - 2);
3904 		gtk_widget_queue_draw_area (widget, ox, half + oy, width, height - half);
3905 	}
3906 	else
3907 	{
3908 		gdk_draw_rectangle (draw, gc, FALSE, 0 + ox, half + 1 + oy, width - 1, half - 2);
3909 		gdk_draw_rectangle (draw, gc, FALSE, 1 + ox, half + 2 + oy, width - 3, half - 4);
3910 		gtk_widget_queue_draw_area (widget, ox, oy, width, half);
3911 	}
3912 
3913 	g_object_unref (gc);
3914 
3915 	return TRUE;
3916 }
3917