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