/* X-Chat * Copyright (C) 1998-2005 Peter Zelezny. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #include #include #include #include #include #include "../common/hexchat.h" #include "../common/fe.h" #include "../common/server.h" #include "../common/hexchatc.h" #include "../common/outbound.h" #include "../common/inbound.h" #include "../common/plugin.h" #include "../common/modes.h" #include "../common/url.h" #include "../common/util.h" #include "../common/text.h" #include "../common/chanopt.h" #include "../common/cfgfiles.h" #include "fe-gtk.h" #include "banlist.h" #include "gtkutil.h" #include "joind.h" #include "palette.h" #include "maingui.h" #include "menu.h" #include "fkeys.h" #include "userlistgui.h" #include "chanview.h" #include "pixmaps.h" #include "plugin-tray.h" #include "xtext.h" #include "sexy-spell-entry.h" #define GUI_SPACING (3) #define GUI_BORDER (0) enum { POS_INVALID = 0, POS_TOPLEFT = 1, POS_BOTTOMLEFT = 2, POS_TOPRIGHT = 3, POS_BOTTOMRIGHT = 4, POS_TOP = 5, /* for tabs only */ POS_BOTTOM = 6, POS_HIDDEN = 7 }; /* two different types of tabs */ #define TAG_IRC 0 /* server, channel, dialog */ #define TAG_UTIL 1 /* dcc, notify, chanlist */ static void mg_create_entry (session *sess, GtkWidget *box); static void mg_create_search (session *sess, GtkWidget *box); static void mg_link_irctab (session *sess, int focus); static session_gui static_mg_gui; static session_gui *mg_gui = NULL; /* the shared irc tab */ static int ignore_chanmode = FALSE; static const char chan_flags[] = { 'c', 'n', 't', 'i', 'm', 'l', 'k' }; static chan *active_tab = NULL; /* active tab */ GtkWidget *parent_window = NULL; /* the master window */ GtkStyle *input_style; static PangoAttrList *away_list; static PangoAttrList *newdata_list; static PangoAttrList *nickseen_list; static PangoAttrList *newmsg_list; static PangoAttrList *plain_list = NULL; static PangoAttrList * mg_attr_list_create (GdkColor *col, int size) { PangoAttribute *attr; PangoAttrList *list; list = pango_attr_list_new (); if (col) { attr = pango_attr_foreground_new (col->red, col->green, col->blue); attr->start_index = 0; attr->end_index = 0xffff; pango_attr_list_insert (list, attr); } if (size > 0) { attr = pango_attr_scale_new (size == 1 ? PANGO_SCALE_SMALL : PANGO_SCALE_X_SMALL); attr->start_index = 0; attr->end_index = 0xffff; pango_attr_list_insert (list, attr); } return list; } static void mg_create_tab_colors (void) { if (plain_list) { pango_attr_list_unref (plain_list); pango_attr_list_unref (newmsg_list); pango_attr_list_unref (newdata_list); pango_attr_list_unref (nickseen_list); pango_attr_list_unref (away_list); } plain_list = mg_attr_list_create (NULL, prefs.hex_gui_tab_small); newdata_list = mg_attr_list_create (&colors[COL_NEW_DATA], prefs.hex_gui_tab_small); nickseen_list = mg_attr_list_create (&colors[COL_HILIGHT], prefs.hex_gui_tab_small); newmsg_list = mg_attr_list_create (&colors[COL_NEW_MSG], prefs.hex_gui_tab_small); away_list = mg_attr_list_create (&colors[COL_AWAY], FALSE); } static void set_window_urgency (GtkWidget *win, gboolean set) { gtk_window_set_urgency_hint (GTK_WINDOW (win), set); } static void flash_window (GtkWidget *win) { #ifdef HAVE_GTK_MAC gtkosx_application_attention_request (osx_app, INFO_REQUEST); #endif set_window_urgency (win, TRUE); } static void unflash_window (GtkWidget *win) { set_window_urgency (win, FALSE); } /* flash the taskbar button */ void fe_flash_window (session *sess) { if (fe_gui_info (sess, 0) != 1) /* only do it if not focused */ flash_window (sess->gui->window); } /* set a tab plain, red, light-red, or blue */ void fe_set_tab_color (struct session *sess, int col) { struct session *server_sess = sess->server->server_session; if (sess->res->tab && sess->gui->is_tab && (col == 0 || sess != current_tab)) { switch (col) { case 0: /* no particular color (theme default) */ sess->tab_state = TAB_STATE_NONE; chan_set_color (sess->res->tab, plain_list); break; case 1: /* new data has been displayed (dark red) */ sess->tab_state = TAB_STATE_NEW_DATA; chan_set_color (sess->res->tab, newdata_list); if (chan_is_collapsed (sess->res->tab) && !((server_sess->tab_state & TAB_STATE_NEW_MSG) || (server_sess->tab_state & TAB_STATE_NEW_HILIGHT)) && !(server_sess == current_tab)) { server_sess->tab_state = TAB_STATE_NEW_DATA; chan_set_color (chan_get_parent (sess->res->tab), newdata_list); } break; case 2: /* new message arrived in channel (light red) */ sess->tab_state = TAB_STATE_NEW_MSG; chan_set_color (sess->res->tab, newmsg_list); if (chan_is_collapsed (sess->res->tab) && !(server_sess->tab_state & TAB_STATE_NEW_HILIGHT) && !(server_sess == current_tab)) { server_sess->tab_state = TAB_STATE_NEW_MSG; chan_set_color (chan_get_parent (sess->res->tab), newmsg_list); } break; case 3: /* your nick has been seen (blue) */ sess->tab_state = TAB_STATE_NEW_HILIGHT; chan_set_color (sess->res->tab, nickseen_list); if (chan_is_collapsed (sess->res->tab) && !(server_sess == current_tab)) { server_sess->tab_state = TAB_STATE_NEW_MSG; chan_set_color (chan_get_parent (sess->res->tab), nickseen_list); } break; } lastact_update (sess); sess->last_tab_state = sess->tab_state; /* For plugins handling future prints */ } } static void mg_set_myself_away (session_gui *gui, gboolean away) { gtk_label_set_attributes (GTK_LABEL (gtk_bin_get_child (GTK_BIN (gui->nick_label))), away ? away_list : NULL); } /* change the little icon to the left of your nickname */ void mg_set_access_icon (session_gui *gui, GdkPixbuf *pix, gboolean away) { if (gui->op_xpm) { if (pix == gtk_image_get_pixbuf (GTK_IMAGE (gui->op_xpm))) /* no change? */ { mg_set_myself_away (gui, away); return; } gtk_widget_destroy (gui->op_xpm); gui->op_xpm = NULL; } if (pix && prefs.hex_gui_input_icon) { gui->op_xpm = gtk_image_new_from_pixbuf (pix); gtk_box_pack_start (GTK_BOX (gui->nick_box), gui->op_xpm, 0, 0, 0); gtk_widget_show (gui->op_xpm); } mg_set_myself_away (gui, away); } static gboolean mg_inputbox_focus (GtkWidget *widget, GdkEventFocus *event, session_gui *gui) { GSList *list; session *sess; if (gui->is_tab) return FALSE; list = sess_list; while (list) { sess = list->data; if (sess->gui == gui) { current_sess = sess; if (!sess->server->server_session) sess->server->server_session = sess; break; } list = list->next; } return FALSE; } void mg_inputbox_cb (GtkWidget *igad, session_gui *gui) { char *cmd; static int ignore = FALSE; GSList *list; session *sess = NULL; if (ignore) return; cmd = SPELL_ENTRY_GET_TEXT (igad); if (cmd[0] == 0) return; cmd = g_strdup (cmd); /* avoid recursive loop */ ignore = TRUE; SPELL_ENTRY_SET_TEXT (igad, ""); ignore = FALSE; /* where did this event come from? */ if (gui->is_tab) { sess = current_tab; } else { list = sess_list; while (list) { sess = list->data; if (sess->gui == gui) break; list = list->next; } if (!list) sess = NULL; } if (sess) handle_multiline (sess, cmd, TRUE, FALSE); g_free (cmd); } static gboolean mg_spellcheck_cb (SexySpellEntry *entry, gchar *word, gpointer data) { /* This can cause freezes on long words, nicks arn't very long anyway. */ if (strlen (word) > 20) return TRUE; /* Ignore anything we think is a valid url */ if (url_check_word (word) != 0) return FALSE; return TRUE; } #if 0 static gboolean has_key (char *modes) { if (!modes) return FALSE; /* this is a crude check, but "-k" can't exist, so it works. */ while (*modes) { if (*modes == 'k') return TRUE; if (*modes == ' ') return FALSE; modes++; } return FALSE; } #endif void fe_set_title (session *sess) { char tbuf[512]; int type; if (sess->gui->is_tab && sess != current_tab) return; type = sess->type; if (sess->server->connected == FALSE && sess->type != SESS_DIALOG) goto def; switch (type) { case SESS_DIALOG: g_snprintf (tbuf, sizeof (tbuf), DISPLAY_NAME": %s %s @ %s", _("Dialog with"), sess->channel, server_get_network (sess->server, TRUE)); break; case SESS_SERVER: g_snprintf (tbuf, sizeof (tbuf), DISPLAY_NAME": %s @ %s", sess->server->nick, server_get_network (sess->server, TRUE)); break; case SESS_CHANNEL: /* don't display keys in the titlebar */ if (prefs.hex_gui_win_modes) { g_snprintf (tbuf, sizeof (tbuf), DISPLAY_NAME": %s @ %s / %s (%s)", sess->server->nick, server_get_network (sess->server, TRUE), sess->channel, sess->current_modes ? sess->current_modes : ""); } else { g_snprintf (tbuf, sizeof (tbuf), DISPLAY_NAME": %s @ %s / %s", sess->server->nick, server_get_network (sess->server, TRUE), sess->channel); } if (prefs.hex_gui_win_ucount) { g_snprintf (tbuf + strlen (tbuf), 9, " (%d)", sess->total); } break; case SESS_NOTICES: case SESS_SNOTICES: g_snprintf (tbuf, sizeof (tbuf), DISPLAY_NAME": %s @ %s (notices)", sess->server->nick, server_get_network (sess->server, TRUE)); break; default: def: g_snprintf (tbuf, sizeof (tbuf), DISPLAY_NAME); gtk_window_set_title (GTK_WINDOW (sess->gui->window), tbuf); return; } gtk_window_set_title (GTK_WINDOW (sess->gui->window), tbuf); } static gboolean mg_windowstate_cb (GtkWindow *wid, GdkEventWindowState *event, gpointer userdata) { if ((event->changed_mask & GDK_WINDOW_STATE_ICONIFIED) && (event->new_window_state & GDK_WINDOW_STATE_ICONIFIED) && prefs.hex_gui_tray_minimize && prefs.hex_gui_tray && !unity_mode ()) { tray_toggle_visibility (TRUE); gtk_window_deiconify (wid); } prefs.hex_gui_win_state = 0; if (event->new_window_state & GDK_WINDOW_STATE_MAXIMIZED) prefs.hex_gui_win_state = 1; prefs.hex_gui_win_fullscreen = 0; if (event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN) prefs.hex_gui_win_fullscreen = 1; menu_set_fullscreen (current_sess->gui, prefs.hex_gui_win_fullscreen); return FALSE; } static gboolean mg_configure_cb (GtkWidget *wid, GdkEventConfigure *event, session *sess) { if (sess == NULL) /* for the main_window */ { if (mg_gui) { if (prefs.hex_gui_win_save && !prefs.hex_gui_win_state && !prefs.hex_gui_win_fullscreen) { sess = current_sess; gtk_window_get_position (GTK_WINDOW (wid), &prefs.hex_gui_win_left, &prefs.hex_gui_win_top); gtk_window_get_size (GTK_WINDOW (wid), &prefs.hex_gui_win_width, &prefs.hex_gui_win_height); } } } if (sess) { if (sess->type == SESS_DIALOG && prefs.hex_gui_win_save) { gtk_window_get_position (GTK_WINDOW (wid), &prefs.hex_gui_dialog_left, &prefs.hex_gui_dialog_top); gtk_window_get_size (GTK_WINDOW (wid), &prefs.hex_gui_dialog_width, &prefs.hex_gui_dialog_height); } } return FALSE; } /* move to a non-irc tab */ static void mg_show_generic_tab (GtkWidget *box) { int num; GtkWidget *f = NULL; if (current_sess && gtk_widget_has_focus (current_sess->gui->input_box)) f = current_sess->gui->input_box; num = gtk_notebook_page_num (GTK_NOTEBOOK (mg_gui->note_book), box); gtk_notebook_set_current_page (GTK_NOTEBOOK (mg_gui->note_book), num); gtk_tree_view_set_model (GTK_TREE_VIEW (mg_gui->user_tree), NULL); gtk_window_set_title (GTK_WINDOW (mg_gui->window), g_object_get_data (G_OBJECT (box), "title")); gtk_widget_set_sensitive (mg_gui->menu, FALSE); if (f) gtk_widget_grab_focus (f); } /* a channel has been focused */ static void mg_focus (session *sess) { if (sess->gui->is_tab) current_tab = sess; current_sess = sess; /* dirty trick to avoid auto-selection */ SPELL_ENTRY_SET_EDITABLE (sess->gui->input_box, FALSE); gtk_widget_grab_focus (sess->gui->input_box); SPELL_ENTRY_SET_EDITABLE (sess->gui->input_box, TRUE); sess->server->front_session = sess; if (sess->server->server_session != NULL) { if (sess->server->server_session->type != SESS_SERVER) sess->server->server_session = sess; } else { sess->server->server_session = sess; } /* when called via mg_changui_new, is_tab might be true, but sess->res->tab is still NULL. */ if (sess->res->tab) fe_set_tab_color (sess, 0); } static int mg_progressbar_update (GtkWidget *bar) { static int type = 0; static gdouble pos = 0; pos += 0.05; if (pos >= 0.99) { if (type == 0) { type = 1; gtk_progress_bar_set_orientation ((GtkProgressBar *) bar, GTK_PROGRESS_RIGHT_TO_LEFT); } else { type = 0; gtk_progress_bar_set_orientation ((GtkProgressBar *) bar, GTK_PROGRESS_LEFT_TO_RIGHT); } pos = 0.05; } gtk_progress_bar_set_fraction ((GtkProgressBar *) bar, pos); return 1; } void mg_progressbar_create (session_gui *gui) { gui->bar = gtk_progress_bar_new (); gtk_box_pack_start (GTK_BOX (gui->nick_box), gui->bar, 0, 0, 0); gtk_widget_show (gui->bar); gui->bartag = fe_timeout_add (50, mg_progressbar_update, gui->bar); } void mg_progressbar_destroy (session_gui *gui) { fe_timeout_remove (gui->bartag); gtk_widget_destroy (gui->bar); gui->bar = 0; gui->bartag = 0; } /* switching tabs away from this one, so remember some info about it! */ static void mg_unpopulate (session *sess) { restore_gui *res; session_gui *gui; int i; gui = sess->gui; res = sess->res; res->input_text = g_strdup (SPELL_ENTRY_GET_TEXT (gui->input_box)); res->topic_text = g_strdup (gtk_entry_get_text (GTK_ENTRY (gui->topic_entry))); res->limit_text = g_strdup (gtk_entry_get_text (GTK_ENTRY (gui->limit_entry))); res->key_text = g_strdup (gtk_entry_get_text (GTK_ENTRY (gui->key_entry))); if (gui->laginfo) res->lag_text = g_strdup (gtk_label_get_text (GTK_LABEL (gui->laginfo))); if (gui->throttleinfo) res->queue_text = g_strdup (gtk_label_get_text (GTK_LABEL (gui->throttleinfo))); for (i = 0; i < NUM_FLAG_WIDS - 1; i++) res->flag_wid_state[i] = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (gui->flag_wid[i])); res->old_ul_value = userlist_get_value (gui->user_tree); if (gui->lagometer) res->lag_value = gtk_progress_bar_get_fraction ( GTK_PROGRESS_BAR (gui->lagometer)); if (gui->throttlemeter) res->queue_value = gtk_progress_bar_get_fraction ( GTK_PROGRESS_BAR (gui->throttlemeter)); if (gui->bar) { res->c_graph = TRUE; /* still have a graph, just not visible now */ mg_progressbar_destroy (gui); } } static void mg_restore_label (GtkWidget *label, char **text) { if (!label) return; if (*text) { gtk_label_set_text (GTK_LABEL (label), *text); g_free (*text); *text = NULL; } else { gtk_label_set_text (GTK_LABEL (label), ""); } } static void mg_restore_entry (GtkWidget *entry, char **text) { if (*text) { gtk_entry_set_text (GTK_ENTRY (entry), *text); g_free (*text); *text = NULL; } else { gtk_entry_set_text (GTK_ENTRY (entry), ""); } gtk_editable_set_position (GTK_EDITABLE (entry), -1); } static void mg_restore_speller (GtkWidget *entry, char **text) { if (*text) { SPELL_ENTRY_SET_TEXT (entry, *text); g_free (*text); *text = NULL; } else { SPELL_ENTRY_SET_TEXT (entry, ""); } SPELL_ENTRY_SET_POS (entry, -1); } void mg_set_topic_tip (session *sess) { char *text; switch (sess->type) { case SESS_CHANNEL: if (sess->topic) { text = g_strdup_printf (_("Topic for %s is: %s"), sess->channel, sess->topic); gtk_widget_set_tooltip_text (sess->gui->topic_entry, text); g_free (text); } else gtk_widget_set_tooltip_text (sess->gui->topic_entry, _("No topic is set")); break; default: if (gtk_entry_get_text (GTK_ENTRY (sess->gui->topic_entry)) && gtk_entry_get_text (GTK_ENTRY (sess->gui->topic_entry))[0]) gtk_widget_set_tooltip_text (sess->gui->topic_entry, (char *)gtk_entry_get_text (GTK_ENTRY (sess->gui->topic_entry))); else gtk_widget_set_tooltip_text (sess->gui->topic_entry, NULL); } } static void mg_hide_empty_pane (GtkPaned *pane) { if ((gtk_paned_get_child1 (pane) == NULL || !gtk_widget_get_visible (gtk_paned_get_child1 (pane))) && (gtk_paned_get_child2 (pane) == NULL || !gtk_widget_get_visible (gtk_paned_get_child2 (pane)))) { gtk_widget_hide (GTK_WIDGET (pane)); return; } gtk_widget_show (GTK_WIDGET (pane)); } static void mg_hide_empty_boxes (session_gui *gui) { /* hide empty vpanes - so the handle is not shown */ mg_hide_empty_pane ((GtkPaned*)gui->vpane_right); mg_hide_empty_pane ((GtkPaned*)gui->vpane_left); } static void mg_userlist_showhide (session *sess, int show) { session_gui *gui = sess->gui; int handle_size; int right_size; GtkAllocation allocation; right_size = MAX (prefs.hex_gui_pane_right_size, prefs.hex_gui_pane_right_size_min); if (show) { gtk_widget_show (gui->user_box); gui->ul_hidden = 0; gtk_widget_get_allocation (gui->hpane_right, &allocation); gtk_widget_style_get (GTK_WIDGET (gui->hpane_right), "handle-size", &handle_size, NULL); gtk_paned_set_position (GTK_PANED (gui->hpane_right), allocation.width - (right_size + handle_size)); } else { gtk_widget_hide (gui->user_box); gui->ul_hidden = 1; } mg_hide_empty_boxes (gui); } static gboolean mg_is_userlist_and_tree_combined (void) { if (prefs.hex_gui_tab_pos == POS_TOPLEFT && prefs.hex_gui_ulist_pos == POS_BOTTOMLEFT) return TRUE; if (prefs.hex_gui_tab_pos == POS_BOTTOMLEFT && prefs.hex_gui_ulist_pos == POS_TOPLEFT) return TRUE; if (prefs.hex_gui_tab_pos == POS_TOPRIGHT && prefs.hex_gui_ulist_pos == POS_BOTTOMRIGHT) return TRUE; if (prefs.hex_gui_tab_pos == POS_BOTTOMRIGHT && prefs.hex_gui_ulist_pos == POS_TOPRIGHT) return TRUE; return FALSE; } /* decide if the userlist should be shown or hidden for this tab */ void mg_decide_userlist (session *sess, gboolean switch_to_current) { /* when called from menu.c we need this */ if (sess->gui == mg_gui && switch_to_current) sess = current_tab; if (prefs.hex_gui_ulist_hide) { mg_userlist_showhide (sess, FALSE); return; } switch (sess->type) { case SESS_SERVER: case SESS_DIALOG: case SESS_NOTICES: case SESS_SNOTICES: if (mg_is_userlist_and_tree_combined ()) mg_userlist_showhide (sess, TRUE); /* show */ else mg_userlist_showhide (sess, FALSE); /* hide */ break; default: mg_userlist_showhide (sess, TRUE); /* show */ } } static int ul_tag = 0; static gboolean mg_populate_userlist (session *sess) { if (!sess) sess = current_tab; if (is_session (sess)) { if (sess->type == SESS_DIALOG) mg_set_access_icon (sess->gui, NULL, sess->server->is_away); else mg_set_access_icon (sess->gui, get_user_icon (sess->server, sess->me), sess->server->is_away); userlist_show (sess); userlist_set_value (sess->gui->user_tree, sess->res->old_ul_value); } ul_tag = 0; return 0; } /* fill the irc tab with a new channel */ static void mg_populate (session *sess) { session_gui *gui = sess->gui; restore_gui *res = sess->res; int i, render = TRUE; guint16 vis = gui->ul_hidden; GtkAllocation allocation; switch (sess->type) { case SESS_DIALOG: /* show the dialog buttons */ gtk_widget_show (gui->dialogbutton_box); /* hide the chan-mode buttons */ gtk_widget_hide (gui->topicbutton_box); /* hide the userlist */ mg_decide_userlist (sess, FALSE); /* shouldn't edit the topic */ gtk_editable_set_editable (GTK_EDITABLE (gui->topic_entry), FALSE); /* might be hidden from server tab */ if (prefs.hex_gui_topicbar) gtk_widget_show (gui->topic_bar); break; case SESS_SERVER: if (prefs.hex_gui_mode_buttons) gtk_widget_show (gui->topicbutton_box); /* hide the dialog buttons */ gtk_widget_hide (gui->dialogbutton_box); /* hide the userlist */ mg_decide_userlist (sess, FALSE); /* servers don't have topics */ gtk_widget_hide (gui->topic_bar); break; default: /* hide the dialog buttons */ gtk_widget_hide (gui->dialogbutton_box); if (prefs.hex_gui_mode_buttons) gtk_widget_show (gui->topicbutton_box); /* show the userlist */ mg_decide_userlist (sess, FALSE); /* let the topic be editted */ gtk_editable_set_editable (GTK_EDITABLE (gui->topic_entry), TRUE); if (prefs.hex_gui_topicbar) gtk_widget_show (gui->topic_bar); } /* move to THE irc tab */ if (gui->is_tab) gtk_notebook_set_current_page (GTK_NOTEBOOK (gui->note_book), 0); /* xtext size change? Then don't render, wait for the expose caused by showing/hidding the userlist */ gtk_widget_get_allocation (gui->user_box, &allocation); if (vis != gui->ul_hidden && allocation.width > 1) render = FALSE; gtk_xtext_buffer_show (GTK_XTEXT (gui->xtext), res->buffer, render); if (gui->is_tab) gtk_widget_set_sensitive (gui->menu, TRUE); /* restore all the GtkEntry's */ mg_restore_entry (gui->topic_entry, &res->topic_text); mg_restore_speller (gui->input_box, &res->input_text); mg_restore_entry (gui->key_entry, &res->key_text); mg_restore_entry (gui->limit_entry, &res->limit_text); mg_restore_label (gui->laginfo, &res->lag_text); mg_restore_label (gui->throttleinfo, &res->queue_text); mg_focus (sess); fe_set_title (sess); /* this one flickers, so only change if necessary */ if (strcmp (sess->server->nick, gtk_button_get_label (GTK_BUTTON (gui->nick_label))) != 0) gtk_button_set_label (GTK_BUTTON (gui->nick_label), sess->server->nick); /* this is slow, so make it a timeout event */ if (!gui->is_tab) { mg_populate_userlist (sess); } else { if (ul_tag == 0) ul_tag = g_idle_add ((GSourceFunc)mg_populate_userlist, NULL); } fe_userlist_numbers (sess); /* restore all the channel mode buttons */ ignore_chanmode = TRUE; for (i = 0; i < NUM_FLAG_WIDS - 1; i++) { /* Hide if mode not supported */ if (sess->server && strchr (sess->server->chanmodes, chan_flags[i]) == NULL) gtk_widget_hide (sess->gui->flag_wid[i]); else gtk_widget
/* HexChat
 * Copyright (C) 1998-2010 Peter Zelezny.
 * Copyright (C) 2009-2013 Berke Viktor.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

#include "proto-irc.h"

#ifndef HEXCHAT_INBOUND_H
#define HEXCHAT_INBOUND_H

void inbound_next_nick (session *sess, char *nick, int error,
								const message_tags_data *tags_data);
void inbound_uback (server *serv, const message_tags_data *tags_data);
void inbound_uaway (server *serv, const message_tags_data *tags_data);
void inbound_account (server *serv, char *nick, char *account,
							 const message_tags_data *tags_data);
void inbound_part (server *serv, char *chan, char *user, char *ip, char *reason,
						 const message_tags_data *tags_data);
void inbound_upart (server *serv, char *chan, char *ip, char *reason,
						  const message_tags_data *tags_data);
void inbound_ukick (server *serv, char *chan, char *kicker, char *reason,
						  const message_tags_data *tags_data);
void inbound_kick (server *serv, char *chan, char *user, char *kicker,
						 char *reason, const message_tags_data *tags_data);
void inbound_notice (server *serv, char *to, char *nick, char *msg, char *ip,
							int id, const message_tags_data *tags_data);
void inbound_quit (server *serv, char *nick, char *ip, char *reason,
						 const message_tags_data *tags_data);
void inbound_topicnew (server *serv, char *nick, char *chan, char *topic,
							  const message_tags_data *tags_data);
void inbound_join (server *serv, char *chan, char *user, char *ip, 
						 char *account, char *realname, 
						 const message_tags_data *tags_data);
void inbound_ujoin (server *serv, char *chan, char *nick, char *ip,
						  const message_tags_data *tags_data);
void inbound_topictime (server *serv, char *chan, char *nick, time_t stamp,
								const message_tags_data *tags_data);
void inbound_topic (server *serv, char *chan, char *topic_text,
						  const message_tags_data *tags_data);
void inbound_user_info_start (session *sess, char *nick,
										const message_tags_data *tags_data);
void inbound_user_info (session *sess, char *chan, char *user, char *host,
								char *servname, char *nick, char *realname, char *account,
								unsigned int away, const message_tags_data *tags_data);
void inbound_foundip (session *sess, char *ip, 
							 const message_tags_data *tags_data);
int inbound_banlist (session *sess, time_t stamp, char *chan, char *mask, 
							char *banner, int is_exemption,
							const message_tags_data *tags_data);
void inbound_ping_reply (session *sess, char *timestring, char *from,
								 const message_tags_data *tags_data);
void inbound_nameslist (server *serv, char *chan, char *names,
								const message_tags_data *tags_data);
int inbound_nameslist_end (server *serv, char *chan,
									const message_tags_data *tags_data);
void inbound_away (server *serv, char *nick, char *msg,
						 const message_tags_data *tags_data);
void inbound_away_notify (server *serv, char *nick, char *reason,
								  const message_tags_data *tags_data);
void inbound_login_start (session *sess, char *nick, char *servname,
								  const message_tags_data *tags_data);
void inbound_login_end (session *sess, char *text,
								const message_tags_data *tags_data);
void inbound_chanmsg (server *serv, session *sess, char *chan, char *from,
							 char *text, char fromme, int id, 
							 const message_tags_data *tags_data);
void clear_channel (session *sess);
void set_topic (session *sess, char *topic, char *stripped_topic);
void inbound_privmsg (server *serv, char *from, char *ip, char *text, int id, 
							 const message_tags_data *tags_data);
void inbound_action (session *sess, char *chan, char *from, char *ip,
							char *text, int fromme, int id,
							const message_tags_data *tags_data);
void inbound_newnick (server *serv, char *nick, char *newnick, int quiet,
							 const message_tags_data *tags_data);
void inbound_identified (server *serv);
void inbound_cap_ack (server *serv, char *nick, char *extensions,
							 const message_tags_data *tags_data);
void inbound_cap_ls (server *serv, char *nick, char *extensions,
							const message_tags_data *tags_data);
void inbound_cap_nak (server *serv, const message_tags_data *tags_data);
void inbound_cap_list (server *serv, char *nick, char *extensions,
							  const message_tags_data *tags_data);
void inbound_sasl_authenticate (server *serv, char *data);
int inbound_sasl_error (server *serv);
void inbound_sasl_supportedmechs (server *serv, char *list);
void do_dns (session *sess, char *nick, char *host,
				 const message_tags_data *tags_data);
gboolean alert_match_word (char *word, char *masks);
gboolean alert_match_text (char *text, char *masks);

#endif
buf[256]; if (!cancel) { g_snprintf (buf, sizeof (buf), "nick %s", text); handle_command (current_sess, buf, FALSE); } } static void mg_nickclick_cb (GtkWidget *button, gpointer userdata) { fe_get_str (_("Enter new nickname:"), current_sess->server->nick, mg_change_nick, (void *) 1); } /* make sure chanview and userlist positions are sane */ static void mg_sanitize_positions (int *cv, int *ul) { if (prefs.hex_gui_tab_layout == 2) { /* treeview can't be on TOP or BOTTOM */ if (*cv == POS_TOP || *cv == POS_BOTTOM) *cv = POS_TOPLEFT; } /* userlist can't be on TOP or BOTTOM */ if (*ul == POS_TOP || *ul == POS_BOTTOM) *ul = POS_TOPRIGHT; /* can't have both in the same place */ if (*cv == *ul) { *cv = POS_TOPRIGHT; if (*ul == POS_TOPRIGHT) *cv = POS_BOTTOMRIGHT; } } static void mg_place_userlist_and_chanview_real (session_gui *gui, GtkWidget *userlist, GtkWidget *chanview) { int unref_userlist = FALSE; int unref_chanview = FALSE; /* first, remove userlist/treeview from their containers */ if (userlist && gtk_widget_get_parent (userlist)) { g_object_ref (userlist); gtk_container_remove (GTK_CONTAINER (gtk_widget_get_parent (userlist)), userlist); unref_userlist = TRUE; } if (chanview && gtk_widget_get_parent (chanview)) { g_object_ref (chanview); gtk_container_remove (GTK_CONTAINER (gtk_widget_get_parent (chanview)), chanview); unref_chanview = TRUE; } if (chanview) { /* incase the previous pos was POS_HIDDEN */ gtk_widget_show (chanview); gtk_table_set_row_spacing (GTK_TABLE (gui->main_table), 1, 0); gtk_table_set_row_spacing (GTK_TABLE (gui->main_table), 2, 2); /* then place them back in their new positions */ switch (prefs.hex_gui_tab_pos) { case POS_TOPLEFT: gtk_paned_pack1 (GTK_PANED (gui->vpane_left), chanview, FALSE, TRUE); break; case POS_BOTTOMLEFT: gtk_paned_pack2 (GTK_PANED (gui->vpane_left), chanview, FALSE, TRUE); break; case POS_TOPRIGHT: gtk_paned_pack1 (GTK_PANED (gui->vpane_right), chanview, FALSE, TRUE); break; case POS_BOTTOMRIGHT: gtk_paned_pack2 (GTK_PANED (gui->vpane_right), chanview, FALSE, TRUE); break; case POS_TOP: gtk_table_set_row_spacing (GTK_TABLE (gui->main_table), 1, GUI_SPACING-1); gtk_table_attach (GTK_TABLE (gui->main_table), chanview, 1, 2, 1, 2, GTK_FILL, GTK_FILL, 0, 0); break; case POS_HIDDEN: gtk_widget_hide (chanview); /* always attach it to something to avoid ref_count=0 */ if (prefs.hex_gui_ulist_pos == POS_TOP) gtk_table_attach (GTK_TABLE (gui->main_table), chanview, 1, 2, 3, 4, GTK_FILL, GTK_FILL, 0, 0); else gtk_table_attach (GTK_TABLE (gui->main_table), chanview, 1, 2, 1, 2, GTK_FILL, GTK_FILL, 0, 0); break; default:/* POS_BOTTOM */ gtk_table_set_row_spacing (GTK_TABLE (gui->main_table), 2, 3); gtk_table_attach (GTK_TABLE (gui->main_table), chanview, 1, 2, 3, 4, GTK_FILL, GTK_FILL, 0, 0); } } if (userlist) { switch (prefs.hex_gui_ulist_pos) { case POS_TOPLEFT: gtk_paned_pack1 (GTK_PANED (gui->vpane_left), userlist, FALSE, TRUE); break; case POS_BOTTOMLEFT: gtk_paned_pack2 (GTK_PANED (gui->vpane_left), userlist, FALSE, TRUE); break; case POS_BOTTOMRIGHT: gtk_paned_pack2 (GTK_PANED (gui->vpane_right), userlist, FALSE, TRUE); break; /*case POS_HIDDEN: break;*/ /* Hide using the VIEW menu instead */ default:/* POS_TOPRIGHT */ gtk_paned_pack1 (GTK_PANED (gui->vpane_right), userlist, FALSE, TRUE); } } if (mg_is_userlist_and_tree_combined () && prefs.hex_gui_pane_divider_position != 0) { gtk_paned_set_position (GTK_PANED (gui->vpane_left), prefs.hex_gui_pane_divider_position); gtk_paned_set_position (GTK_PANED (gui->vpane_right), prefs.hex_gui_pane_divider_position); } if (unref_chanview) g_object_unref (chanview); if (unref_userlist) g_object_unref (userlist); mg_hide_empty_boxes (gui); } static void mg_place_userlist_and_chanview (session_gui *gui) { GtkOrientation orientation; GtkWidget *chanviewbox = NULL; int pos; mg_sanitize_positions (&prefs.hex_gui_tab_pos, &prefs.hex_gui_ulist_pos); if (gui->chanview) { pos = prefs.hex_gui_tab_pos; orientation = chanview_get_orientation (gui->chanview); if ((pos == POS_BOTTOM || pos == POS_TOP) && orientation == GTK_ORIENTATION_VERTICAL) chanview_set_orientation (gui->chanview, FALSE); else if ((pos == POS_TOPLEFT || pos == POS_BOTTOMLEFT || pos == POS_TOPRIGHT || pos == POS_BOTTOMRIGHT) && orientation == GTK_ORIENTATION_HORIZONTAL) chanview_set_orientation (gui->chanview, TRUE); chanviewbox = chanview_get_box (gui->chanview); } mg_place_userlist_and_chanview_real (gui, gui->user_box, chanviewbox); } void mg_change_layout (int type) { if (mg_gui) { /* put tabs at the bottom */ if (type == 0 && prefs.hex_gui_tab_pos != POS_BOTTOM && prefs.hex_gui_tab_pos != POS_TOP) prefs.hex_gui_tab_pos = POS_BOTTOM; mg_place_userlist_and_chanview (mg_gui); chanview_set_impl (mg_gui->chanview, type); } } static void mg_inputbox_rightclick (GtkEntry *entry, GtkWidget *menu) { mg_create_color_menu (menu, NULL); } /* Search bar adapted from Conspire's by William Pitcock */ #define SEARCH_CHANGE 1 #define SEARCH_NEXT 2 #define SEARCH_PREVIOUS 3 #define SEARCH_REFRESH 4 static void search_handle_event(int search_type, session *sess) { textentry *last; const gchar *text = NULL; gtk_xtext_search_flags flags; GError *err = NULL; gboolean backwards = FALSE; /* When just typing show most recent first */ if (search_type == SEARCH_PREVIOUS || search_type == SEARCH_CHANGE) backwards = TRUE; flags = ((prefs.hex_text_search_case_match == 1? case_match: 0) | (backwards? backward: 0) | (prefs.hex_text_search_highlight_all == 1? highlight: 0) | (prefs.hex_text_search_follow == 1? follow: 0) | (prefs.hex_text_search_regexp == 1? regexp: 0)); if (search_type != SEARCH_REFRESH) text = gtk_entry_get_text (GTK_ENTRY(sess->gui->shentry)); last = gtk_xtext_search (GTK_XTEXT (sess->gui->xtext), text, flags, &err); if (err) { gtk_entry_set_icon_from_stock (GTK_ENTRY (sess->gui->shentry), GTK_ENTRY_ICON_SECONDARY, GTK_STOCK_DIALOG_ERROR); gtk_entry_set_icon_tooltip_text (GTK_ENTRY (sess->gui->shentry), GTK_ENTRY_ICON_SECONDARY, _(err->message)); g_error_free (err); } else if (!last) { if (text && text[0] == 0) /* empty string, no error */ { gtk_entry_set_icon_from_stock (GTK_ENTRY (sess->gui->shentry), GTK_ENTRY_ICON_SECONDARY, NULL); } else { /* Either end of search or not found, try again to wrap if only end */ last = gtk_xtext_search (GTK_XTEXT (sess->gui->xtext), text, flags, &err); if (!last) /* Not found error */ { gtk_entry_set_icon_from_stock (GTK_ENTRY (sess->gui->shentry), GTK_ENTRY_ICON_SECONDARY, GTK_STOCK_DIALOG_ERROR); gtk_entry_set_icon_tooltip_text (GTK_ENTRY (sess->gui->shentry), GTK_ENTRY_ICON_SECONDARY, _("No results found.")); } } } else { gtk_entry_set_icon_from_stock (GTK_ENTRY (sess->gui->shentry), GTK_ENTRY_ICON_SECONDARY, NULL); } } static void search_handle_change(GtkWidget *wid, session *sess) { search_handle_event(SEARCH_CHANGE, sess); } static void search_handle_refresh(GtkWidget *wid, session *sess) { search_handle_event(SEARCH_REFRESH, sess); } void mg_search_handle_previous(GtkWidget *wid, session *sess) { search_handle_event(SEARCH_PREVIOUS, sess); } void mg_search_handle_next(GtkWidget *wid, session *sess) { search_handle_event(SEARCH_NEXT, sess); } static void search_set_option (GtkToggleButton *but, guint *pref) { *pref = gtk_toggle_button_get_active(but); save_config(); } void mg_search_toggle(session *sess) { if (gtk_widget_get_visible(sess->gui->shbox)) { gtk_widget_hide(sess->gui->shbox); gtk_widget_grab_focus(sess->gui->input_box); gtk_entry_set_text(GTK_ENTRY(sess->gui->shentry), ""); } else { /* Reset search state */ gtk_entry_set_icon_from_stock (GTK_ENTRY (sess->gui->shentry), GTK_ENTRY_ICON_SECONDARY, NULL); /* Show and focus */ gtk_widget_show(sess->gui->shbox); gtk_widget_grab_focus(sess->gui->shentry); } } static gboolean search_handle_esc (GtkWidget *win, GdkEventKey *key, session *sess) { if (key->keyval == GDK_KEY_Escape) mg_search_toggle(sess); return FALSE; } static void mg_create_search(session *sess, GtkWidget *box) { GtkWidget *entry, *label, *next, *previous, *highlight, *matchcase, *regex, *close; session_gui *gui = sess->gui; gui->shbox = gtk_hbox_new(FALSE, 5); gtk_box_pack_start(GTK_BOX(box), gui->shbox, FALSE, FALSE, 0); close = gtk_button_new (); gtk_button_set_image (GTK_BUTTON (close), gtk_image_new_from_stock (GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU)); gtk_button_set_relief(GTK_BUTTON(close), GTK_RELIEF_NONE); gtk_widget_set_can_focus (close, FALSE); gtk_box_pack_start(GTK_BOX(gui->shbox), close, FALSE, FALSE, 0); g_signal_connect_swapped(G_OBJECT(close), "clicked", G_CALLBACK(mg_search_toggle), sess); label = gtk_label_new(_("Find:")); gtk_box_pack_start(GTK_BOX(gui->shbox), label, FALSE, FALSE, 0); gui->shentry = entry = gtk_entry_new(); gtk_box_pack_start(GTK_BOX(gui->shbox), entry, FALSE, FALSE, 0); gtk_widget_set_size_request (gui->shentry, 180, -1); gui->search_changed_signal = g_signal_connect(G_OBJECT(entry), "changed", G_CALLBACK(search_handle_change), sess); g_signal_connect (G_OBJECT (entry), "key_press_event", G_CALLBACK (search_handle_esc), sess); g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(mg_search_handle_next), sess); gtk_entry_set_icon_activatable (GTK_ENTRY (entry), GTK_ENTRY_ICON_SECONDARY, FALSE); gtk_entry_set_icon_tooltip_text (GTK_ENTRY (sess->gui->shentry), GTK_ENTRY_ICON_SECONDARY, _("Search hit end or not found.")); previous = gtk_button_new (); gtk_button_set_image (GTK_BUTTON (previous), gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU)); gtk_button_set_relief(GTK_BUTTON(previous), GTK_RELIEF_NONE); gtk_widget_set_can_focus (previous, FALSE); gtk_box_pack_start(GTK_BOX(gui->shbox), previous, FALSE, FALSE, 0); g_signal_connect(G_OBJECT(previous), "clicked", G_CALLBACK(mg_search_handle_previous), sess); next = gtk_button_new (); gtk_button_set_image (GTK_BUTTON (next), gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU)); gtk_button_set_relief(GTK_BUTTON(next), GTK_RELIEF_NONE); gtk_widget_set_can_focus (next, FALSE); gtk_box_pack_start(GTK_BOX(gui->shbox), next, FALSE, FALSE, 0); g_signal_connect(G_OBJECT(next), "clicked", G_CALLBACK(mg_search_handle_next), sess); highlight = gtk_check_button_new_with_mnemonic (_("_Highlight all")); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(highlight), prefs.hex_text_search_highlight_all); gtk_widget_set_can_focus (highlight, FALSE); g_signal_connect (G_OBJECT (highlight), "toggled", G_CALLBACK (search_set_option), &prefs.hex_text_search_highlight_all); g_signal_connect (G_OBJECT (highlight), "toggled", G_CALLBACK (search_handle_refresh), sess); gtk_box_pack_start(GTK_BOX(gui->shbox), highlight, FALSE, FALSE, 0); gtk_widget_set_tooltip_text (highlight, _("Highlight all occurrences, and underline the current occurrence.")); matchcase = gtk_check_button_new_with_mnemonic (_("Mat_ch case")); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(matchcase), prefs.hex_text_search_case_match); gtk_widget_set_can_focus (matchcase, FALSE); g_signal_connect (G_OBJECT (matchcase), "toggled", G_CALLBACK (search_set_option), &prefs.hex_text_search_case_match); gtk_box_pack_start(GTK_BOX(gui->shbox), matchcase, FALSE, FALSE, 0); gtk_widget_set_tooltip_text (matchcase, _("Perform a case-sensitive search.")); regex = gtk_check_button_new_with_mnemonic (_("_Regex")); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(regex), prefs.hex_text_search_regexp); gtk_widget_set_can_focus (regex, FALSE); g_signal_connect (G_OBJECT (regex), "toggled", G_CALLBACK (search_set_option), &prefs.hex_text_search_regexp); gtk_box_pack_start(GTK_BOX(gui->shbox), regex, FALSE, FALSE, 0); gtk_widget_set_tooltip_text (regex, _("Regard search string as a regular expression.")); } static void mg_create_entry (session *sess, GtkWidget *box) { GtkWidget *hbox, *but, *entry; session_gui *gui = sess->gui; hbox = gtk_hbox_new (FALSE, 0); gtk_box_pack_start (GTK_BOX (box), hbox, 0, 0, 0); gui->nick_box = gtk_hbox_new (FALSE, 0); gtk_box_pack_start (GTK_BOX (hbox), gui->nick_box, 0, 0, 0); gui->nick_label = but = gtk_button_new_with_label (sess->server->nick); gtk_button_set_relief (GTK_BUTTON (but), GTK_RELIEF_NONE); gtk_widget_set_can_focus (but, FALSE); gtk_box_pack_end (GTK_BOX (gui->nick_box), but, 0, 0, 0); g_signal_connect (G_OBJECT (but), "clicked", G_CALLBACK (mg_nickclick_cb), NULL); gui->input_box = entry = sexy_spell_entry_new (); sexy_spell_entry_set_checked ((SexySpellEntry *)entry, prefs.hex_gui_input_spell); sexy_spell_entry_set_parse_attributes ((SexySpellEntry *)entry, prefs.hex_gui_input_attr); gtk_entry_set_max_length (GTK_ENTRY (gui->input_box), 0); g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (mg_inputbox_cb), gui); gtk_container_add (GTK_CONTAINER (hbox), entry); gtk_widget_set_name (entry, "hexchat-inputbox"); g_signal_connect (G_OBJECT (entry), "key_press_event", G_CALLBACK (key_handle_key_press), NULL); g_signal_connect (G_OBJECT (entry), "focus_in_event", G_CALLBACK (mg_inputbox_focus), gui); g_signal_connect (G_OBJECT (entry), "populate_popup", G_CALLBACK (mg_inputbox_rightclick), NULL); g_signal_connect (G_OBJECT (entry), "word-check", G_CALLBACK (mg_spellcheck_cb), NULL); gtk_widget_grab_focus (entry); if (prefs.hex_gui_input_style) mg_apply_entry_style (entry); } static void mg_switch_tab_cb (chanview *cv, chan *ch, int tag, gpointer ud) { chan *old; session *sess = ud; old = active_tab; active_tab = ch; if (tag == TAG_IRC) { if (active_tab != old) { if (old && current_tab) mg_unpopulate (current_tab); mg_populate (sess); } } else if (old != active_tab) { /* userdata for non-irc tabs is actually the GtkBox */ mg_show_generic_tab (ud); if (!mg_is_userlist_and_tree_combined ()) mg_userlist_showhide (current_sess, FALSE); /* hide */ } } /* compare two tabs (for tab sorting function) */ static int mg_tabs_compare (session *a, session *b) { /* server tabs always go first */ if (a->type == SESS_SERVER) return -1; /* then channels */ if (a->type == SESS_CHANNEL && b->type != SESS_CHANNEL) return -1; if (a->type != SESS_CHANNEL && b->type == SESS_CHANNEL) return 1; return g_ascii_strcasecmp (a->channel, b->channel); } static void mg_create_tabs (session_gui *gui) { gboolean use_icons = FALSE; /* if any one of these PNGs exist, the chanview will create * the extra column for icons. */ if (prefs.hex_gui_tab_icons && (pix_tree_channel || pix_tree_dialog || pix_tree_server || pix_tree_util)) { use_icons = TRUE; } gui->chanview = chanview_new (prefs.hex_gui_tab_layout, prefs.hex_gui_tab_trunc, prefs.hex_gui_tab_sort, use_icons, prefs.hex_gui_ulist_style ? input_style : NULL); chanview_set_callbacks (gui->chanview, mg_switch_tab_cb, mg_xbutton_cb, mg_tab_contextmenu_cb, (void *)mg_tabs_compare); mg_place_userlist_and_chanview (gui); } static gboolean mg_tabwin_focus_cb (GtkWindow * win, GdkEventFocus *event, gpointer userdata) { current_sess = current_tab; if (current_sess) { gtk_xtext_check_marker_visibility (GTK_XTEXT (current_sess->gui->xtext)); plugin_emit_dummy_print (current_sess, "Focus Window"); } unflash_window (GTK_WIDGET (win)); return FALSE; } static gboolean mg_topwin_focus_cb (GtkWindow * win, GdkEventFocus *event, session *sess) { current_sess = sess; if (!sess->server->server_session) sess->server->server_session = sess; gtk_xtext_check_marker_visibility(GTK_XTEXT (current_sess->gui->xtext)); unflash_window (GTK_WIDGET (win)); plugin_emit_dummy_print (sess, "Focus Window"); return FALSE; } static void mg_create_menu (session_gui *gui, GtkWidget *table, int away_state) { GtkAccelGroup *accel_group; accel_group = gtk_accel_group_new (); gtk_window_add_accel_group (GTK_WINDOW (gtk_widget_get_toplevel (table)), accel_group); g_object_unref (accel_group); gui->menu = menu_create_main (accel_group, TRUE, away_state, !gui->is_tab, gui->menu_item); gtk_table_attach (GTK_TABLE (table), gui->menu, 0, 3, 0, 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0); } static void mg_create_irctab (session *sess, GtkWidget *table) { GtkWidget *vbox; session_gui *gui = sess->gui; vbox = gtk_vbox_new (FALSE, 0); gtk_table_attach (GTK_TABLE (table), vbox, 1, 2, 2, 3, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); mg_create_center (sess, gui, vbox); } static void mg_create_topwindow (session *sess) { GtkWidget *win; GtkWidget *table; if (sess->type == SESS_DIALOG) win = gtkutil_window_new ("HexChat", NULL, prefs.hex_gui_dialog_width, prefs.hex_gui_dialog_height, 0); else win = gtkutil_window_new ("HexChat", NULL, prefs.hex_gui_win_width, prefs.hex_gui_win_height, 0); sess->gui->window = win; gtk_container_set_border_width (GTK_CONTAINER (win), GUI_BORDER); gtk_window_set_opacity (GTK_WINDOW (win), (prefs.hex_gui_transparency / 255.)); g_signal_connect (G_OBJECT (win), "focus_in_event", G_CALLBACK (mg_topwin_focus_cb), sess); g_signal_connect (G_OBJECT (win), "destroy", G_CALLBACK (mg_topdestroy_cb), sess); g_signal_connect (G_OBJECT (win), "configure_event", G_CALLBACK (mg_configure_cb), sess); palette_alloc (win); table = gtk_table_new (4, 3, FALSE); /* spacing under the menubar */ gtk_table_set_row_spacing (GTK_TABLE (table), 0, GUI_SPACING); /* left and right borders */ gtk_table_set_col_spacing (GTK_TABLE (table), 0, 1); gtk_table_set_col_spacing (GTK_TABLE (table), 1, 1); gtk_container_add (GTK_CONTAINER (win), table); mg_create_irctab (sess, table); mg_create_menu (sess->gui, table, sess->server->is_away); if (sess->res->buffer == NULL) { sess->res->buffer = gtk_xtext_buffer_new (GTK_XTEXT (sess->gui->xtext)); gtk_xtext_buffer_show (GTK_XTEXT (sess->gui->xtext), sess->res->buffer, TRUE); gtk_xtext_set_time_stamp (sess->res->buffer, prefs.hex_stamp_text); sess->res->user_model = userlist_create_model (sess); } userlist_show (sess); gtk_widget_show_all (table); if (prefs.hex_gui_hide_menu) gtk_widget_hide (sess->gui->menu); /* Will be shown when needed */ gtk_widget_hide (sess->gui->topic_bar); if (!prefs.hex_gui_ulist_buttons) gtk_widget_hide (sess->gui->button_box); if (!prefs.hex_gui_input_nick) gtk_widget_hide (sess->gui->nick_box); gtk_widget_hide(sess->gui->shbox); mg_decide_userlist (sess, FALSE); if (sess->type == SESS_DIALOG) { /* hide the chan-mode buttons */ gtk_widget_hide (sess->gui->topicbutton_box); } else { gtk_widget_hide (sess->gui->dialogbutton_box); if (!prefs.hex_gui_mode_buttons) gtk_widget_hide (sess->gui->topicbutton_box); } mg_place_userlist_and_chanview (sess->gui); gtk_widget_show (win); } static gboolean mg_tabwindow_de_cb (GtkWidget *widget, GdkEvent *event, gpointer user_data) { GSList *list; session *sess; if (prefs.hex_gui_tray_close && !unity_mode () && tray_toggle_visibility (FALSE)) return TRUE; /* check for remaining toplevel windows */ list = sess_list; while (list) { sess = list->data; if (!sess->gui->is_tab) return FALSE; list = list->next; } mg_open_quit_dialog (TRUE); return TRUE; } static void mg_create_tabwindow (session *sess) { GtkWidget *win; GtkWidget *table; win = gtkutil_window_new ("HexChat", NULL, prefs.hex_gui_win_width, prefs.hex_gui_win_height, 0); sess->gui->window = win; gtk_window_move (GTK_WINDOW (win), prefs.hex_gui_win_left, prefs.hex_gui_win_top); if (prefs.hex_gui_win_state) gtk_window_maximize (GTK_WINDOW (win)); if (prefs.hex_gui_win_fullscreen) gtk_window_fullscreen (GTK_WINDOW (win)); gtk_window_set_opacity (GTK_WINDOW (win), (prefs.hex_gui_transparency / 255.)); gtk_container_set_border_width (GTK_CONTAINER (win), GUI_BORDER); g_signal_connect (G_OBJECT (win), "delete_event", G_CALLBACK (mg_tabwindow_de_cb), 0); g_signal_connect (G_OBJECT (win), "destroy", G_CALLBACK (mg_tabwindow_kill_cb), 0); g_signal_connect (G_OBJECT (win), "focus_in_event", G_CALLBACK (mg_tabwin_focus_cb), NULL); g_signal_connect (G_OBJECT (win), "configure_event", G_CALLBACK (mg_configure_cb), NULL); g_signal_connect (G_OBJECT (win), "window_state_event", G_CALLBACK (mg_windowstate_cb), NULL); palette_alloc (win); sess->gui->main_table = table = gtk_table_new (4, 3, FALSE); /* spacing under the menubar */ gtk_table_set_row_spacing (GTK_TABLE (table), 0, GUI_SPACING); /* left and right borders */ gtk_table_set_col_spacing (GTK_TABLE (table), 0, 1); gtk_table_set_col_spacing (GTK_TABLE (table), 1, 1); gtk_container_add (GTK_CONTAINER (win), table); mg_create_irctab (sess, table); mg_create_tabs (sess->gui); mg_create_menu (sess->gui, table, sess->server->is_away); mg_focus (sess); gtk_widget_show_all (table); if (prefs.hex_gui_hide_menu) gtk_widget_hide (sess->gui->menu); mg_decide_userlist (sess, FALSE); /* Will be shown when needed */ gtk_widget_hide (sess->gui->topic_bar); if (!prefs.hex_gui_mode_buttons) gtk_widget_hide (sess->gui->topicbutton_box); if (!prefs.hex_gui_ulist_buttons) gtk_widget_hide (sess->gui->button_box); if (!prefs.hex_gui_input_nick) gtk_widget_hide (sess->gui->nick_box); gtk_widget_hide (sess->gui->shbox); mg_place_userlist_and_chanview (sess->gui); gtk_widget_show (win); } void mg_apply_setup (void) { GSList *list = sess_list; session *sess; int done_main = FALSE; mg_create_tab_colors (); while (list) { sess = list->data; gtk_xtext_set_time_stamp (sess->res->buffer, prefs.hex_stamp_text); ((xtext_buffer *)sess->res->buffer)->needs_recalc = TRUE; if (!sess->gui->is_tab || !done_main) mg_place_userlist_and_chanview (sess->gui); if (sess->gui->is_tab) done_main = TRUE; list = list->next; } } static chan * mg_add_generic_tab (char *name, char *title, void *family, GtkWidget *box) { chan *ch; gtk_notebook_append_page (GTK_NOTEBOOK (mg_gui->note_book), box, NULL); gtk_widget_show (box); ch = chanview_add (mg_gui->chanview, name, NULL, box, TRUE, TAG_UTIL, pix_tree_util); chan_set_color (ch, plain_list); g_object_set_data_full (G_OBJECT (box), "title", g_strdup (title), g_free); g_object_set_data (G_OBJECT (box), "ch", ch); if (prefs.hex_gui_tab_newtofront) chan_focus (ch); return ch; } void fe_buttons_update (session *sess) { session_gui *gui = sess->gui; gtk_widget_destroy (gui->button_box); gui->button_box = mg_create_userlistbuttons (gui->button_box_parent); if (prefs.hex_gui_ulist_buttons) gtk_widget_show (sess->gui->button_box); else gtk_widget_hide (sess->gui->button_box); } void fe_clear_channel (session *sess) { char tbuf[CHANLEN+6]; session_gui *gui = sess->gui; if (sess->gui->is_tab) { if (sess->waitchannel[0]) { if (prefs.hex_gui_tab_trunc > 2 && g_utf8_strlen (sess->waitchannel, -1) > prefs.hex_gui_tab_trunc) { /* truncate long channel names */ tbuf[0] = '('; strcpy (tbuf + 1, sess->waitchannel); g_utf8_offset_to_pointer(tbuf, prefs.hex_gui_tab_trunc)[0] = 0; strcat (tbuf, "..)"); } else { sprintf (tbuf, "(%s)", sess->waitchannel); } } else strcpy (tbuf, _("")); chan_rename (sess->res->tab, tbuf, prefs.hex_gui_tab_trunc); } if (!sess->gui->is_tab || sess == current_tab) { gtk_entry_set_text (GTK_ENTRY (gui->topic_entry), ""); if (gui->op_xpm) { gtk_widget_destroy (gui->op_xpm); gui->op_xpm = 0; } } else { if (sess->res->topic_text) { g_free (sess->res->topic_text); sess->res->topic_text = NULL; } } } void fe_set_nonchannel (session *sess, int state) { } void fe_dlgbuttons_update (session *sess) { GtkWidget *box; session_gui *gui = sess->gui; gtk_widget_destroy (gui->dialogbutton_box); gui->dialogbutton_box = box = gtk_hbox_new (0, 0); gtk_box_pack_start (GTK_BOX (gui->topic_bar), box, 0, 0, 0); gtk_box_reorder_child (GTK_BOX (gui->topic_bar), box, 3); mg_create_dialogbuttons (box); gtk_widget_show_all (box); if (current_tab && current_tab->type != SESS_DIALOG) gtk_widget_hide (current_tab->gui->dialogbutton_box); } void fe_update_mode_buttons (session *sess, char mode, char sign) { int state, i; if (sign == '+') state = TRUE; else state = FALSE; for (i = 0; i < NUM_FLAG_WIDS - 1; i++) { if (chan_flags[i] == mode) { if (!sess->gui->is_tab || sess == current_tab) { ignore_chanmode = TRUE; if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (sess->gui->flag_wid[i])) != state) gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (sess->gui->flag_wid[i]), state); ignore_chanmode = FALSE; } else { sess->res->flag_wid_state[i] = state; } return; } } } void fe_set_nick (server *serv, char *newnick) { GSList *list = sess_list; session *sess; while (list) { sess = list->data; if (sess->server == serv) { if (current_tab == sess || !sess->gui->is_tab) gtk_button_set_label (GTK_BUTTON (sess->gui->nick_label), newnick); } list = list->next; } } void fe_set_away (server *serv) { GSList *list = sess_list; session *sess; while (list) { sess = list->data; if (sess->server == serv) { if (!sess->gui->is_tab || sess == current_tab) { menu_set_away (sess->gui, serv->is_away); /* gray out my nickname */ mg_set_myself_away (sess->gui, serv->is_away); } } list = list->next; } } void fe_set_channel (session *sess) { if (sess->res->tab != NULL) chan_rename (sess->res->tab, sess->channel, prefs.hex_gui_tab_trunc); } void mg_changui_new (session *sess, restore_gui *res, int tab, int focus) { int first_run = FALSE; session_gui *gui; if (res == NULL) { res = g_new0 (restore_gui, 1); } sess->res = res; if (sess->server->front_session == NULL) { sess->server->front_session = sess; } if (!tab) { gui = g_new0 (session_gui, 1); gui->is_tab = FALSE; sess->gui = gui; mg_create_topwindow (sess); fe_set_title (sess); return; } if (mg_gui == NULL) { first_run = TRUE; gui = &static_mg_gui; memset (gui, 0, sizeof (session_gui)); gui->is_tab = TRUE; sess->gui = gui; mg_create_tabwindow (sess); mg_gui = gui; parent_window = gui->window; } else { sess->gui = gui = mg_gui; gui->is_tab = TRUE; } mg_add_chan (sess); if (first_run || (prefs.hex_gui_tab_newtofront == FOCUS_NEW_ONLY_ASKED && focus) || prefs.hex_gui_tab_newtofront == FOCUS_NEW_ALL ) chan_focus (res->tab); } 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) { GtkWidget *vbox, *win; if (prefs.hex_gui_tab_pos == POS_HIDDEN && prefs.hex_gui_tab_utils) prefs.hex_gui_tab_utils = 0; if (force_toplevel || !prefs.hex_gui_tab_utils) { win = gtkutil_window_new (title, name, width, height, 2); vbox = gtk_vbox_new (0, 0); *vbox_ret = vbox; gtk_container_add (GTK_CONTAINER (win), vbox); gtk_widget_show (vbox); if (close_callback) g_signal_connect (G_OBJECT (win), "destroy", G_CALLBACK (close_callback), userdata); return win; } vbox = gtk_vbox_new (0, 2); g_object_set_data (G_OBJECT (vbox), "w", GINT_TO_POINTER (width)); g_object_set_data (G_OBJECT (vbox), "h", GINT_TO_POINTER (height)); gtk_container_set_border_width (GTK_CONTAINER (vbox), 3); *vbox_ret = vbox; if (close_callback) g_signal_connect (G_OBJECT (vbox), "destroy", G_CALLBACK (close_callback), userdata); mg_add_generic_tab (name, title, family, vbox); /* if (link_buttons) { hbox = gtk_hbox_new (FALSE, 0); gtk_box_pack_start (GTK_BOX (vbox), hbox, 0, 0, 0); mg_create_link_buttons (hbox, ch); gtk_widget_show (hbox); }*/ return vbox; } void mg_move_tab (session *sess, int delta) { if (sess->gui->is_tab) chan_move (sess->res->tab, delta); } void mg_move_tab_family (session *sess, int delta) { if (sess->gui->is_tab) chan_move_family (sess->res->tab, delta); } void mg_set_title (GtkWidget *vbox, char *title) /* for non-irc tab/window only */ { char *old; old = g_object_get_data (G_OBJECT (vbox), "title"); if (old) { g_object_set_data_full (G_OBJECT (vbox), "title", g_strdup (title), g_free); } else { gtk_window_set_title (GTK_WINDOW (vbox), title); } } void fe_server_callback (server *serv) { joind_close (serv); if (serv->gui->chanlist_window) mg_close_gen (NULL, serv->gui->chanlist_window); if (serv->gui->rawlog_window) mg_close_gen (NULL, serv->gui->rawlog_window); g_free (serv->gui); } /* called when a session is being killed */ void fe_session_callback (session *sess) { gtk_xtext_buffer_free (sess->res->buffer); g_object_unref (G_OBJECT (sess->res->user_model)); if (sess->res->banlist && sess->res->banlist->window) mg_close_gen (NULL, sess->res->banlist->window); g_free (sess->res->input_text); g_free (sess->res->topic_text); g_free (sess->res->limit_text); g_free (sess->res->key_text); g_free (sess->res->queue_text); g_free (sess->res->queue_tip); g_free (sess->res->lag_text); g_free (sess->res->lag_tip); if (sess->gui->bartag) fe_timeout_remove (sess->gui->bartag); if (sess->gui != &static_mg_gui) g_free (sess->gui); g_free (sess->res); } /* ===== DRAG AND DROP STUFF ===== */ static gboolean is_child_of (GtkWidget *widget, GtkWidget *parent) { while (widget) { if (gtk_widget_get_parent (widget) == parent) return TRUE; widget = gtk_widget_get_parent (widget); } return FALSE; } static void mg_handle_drop (GtkWidget *widget, int y, int *pos, int *other_pos) { int height; session_gui *gui = current_sess->gui; height = gdk_window_get_height (gtk_widget_get_window (widget)); if (y < height / 2) { if (is_child_of (widget, gui->vpane_left)) *pos = 1; /* top left */ else *pos = 3; /* top right */ } else { if (is_child_of (widget, gui->vpane_left)) *pos = 2; /* bottom left */ else *pos = 4; /* bottom right */ } /* both in the same pos? must move one */ if (*pos == *other_pos) { switch (*other_pos) { case 1: *other_pos = 2; break; case 2: *other_pos = 1; break; case 3: *other_pos = 4; break; case 4: *other_pos = 3; break; } } mg_place_userlist_and_chanview (gui); } static gboolean mg_is_gui_target (GdkDragContext *context) { char *target_name; if (!context || !gdk_drag_context_list_targets (context) || !gdk_drag_context_list_targets (context)->data) return FALSE; target_name = gdk_atom_name (gdk_drag_context_list_targets (context)->data); if (target_name) { /* if it's not HEXCHAT_CHANVIEW or HEXCHAT_USERLIST */ /* we should ignore it. */ if (target_name[0] != 'H') { g_free (target_name); return FALSE; } g_free (target_name); } return TRUE; } /* this begin callback just creates an nice of the source */ gboolean mg_drag_begin_cb (GtkWidget *widget, GdkDragContext *context, gpointer userdata) { int width, height; GdkColormap *cmap; GdkPixbuf *pix, *pix2; /* ignore file drops */ if (!mg_is_gui_target (context)) return FALSE; cmap = gtk_widget_get_colormap (widget); width = gdk_window_get_width (gtk_widget_get_window (widget)); height = gdk_window_get_height (gtk_widget_get_window (widget)); pix = gdk_pixbuf_get_from_drawable (NULL, gtk_widget_get_window (widget), cmap, 0, 0, 0, 0, width, height); pix2 = gdk_pixbuf_scale_simple (pix, width * 4 / 5, height / 2, GDK_INTERP_HYPER); g_object_unref (pix); gtk_drag_set_icon_pixbuf (context, pix2, 0, 0); g_object_set_data (G_OBJECT (widget), "ico", pix2); return TRUE; } void mg_drag_end_cb (GtkWidget *widget, GdkDragContext *context, gpointer userdata) { /* ignore file drops */ if (!mg_is_gui_target (context)) return; g_object_unref (g_object_get_data (G_OBJECT (widget), "ico")); } /* drop complete */ gboolean mg_drag_drop_cb (GtkWidget *widget, GdkDragContext *context, int x, int y, guint time, gpointer user_data) { /* ignore file drops */ if (!mg_is_gui_target (context)) return FALSE; switch (gdk_drag_context_get_selected_action (context)) { case GDK_ACTION_MOVE: /* from userlist */ mg_handle_drop (widget, y, &prefs.hex_gui_ulist_pos, &prefs.hex_gui_tab_pos); break; case GDK_ACTION_COPY: /* from tree - we use GDK_ACTION_COPY for the tree */ mg_handle_drop (widget, y, &prefs.hex_gui_tab_pos, &prefs.hex_gui_ulist_pos); break; default: return FALSE; } return TRUE; } /* draw highlight rectangle in the destination */ gboolean mg_drag_motion_cb (GtkWidget *widget, GdkDragContext *context, int x, int y, guint time, gpointer scbar) { GdkGC *gc; GdkColor col; GdkGCValues val; int half, width, height; int ox, oy; GdkDrawable *draw; GtkAllocation allocation; /* ignore file drops */ if (!mg_is_gui_target (context)) return FALSE; if (scbar) /* scrollbar */ { gtk_widget_get_allocation (widget, &allocation); ox = allocation.x; oy = allocation.y; width = allocation.width; height = allocation.height; draw = gtk_widget_get_window (widget); } else { ox = oy = 0; width = gdk_window_get_width (gtk_widget_get_window (widget)); height = gdk_window_get_height (gtk_widget_get_window (widget)); draw = gtk_widget_get_window (widget); } val.subwindow_mode = GDK_INCLUDE_INFERIORS; val.graphics_exposures = 0; val.function = GDK_XOR; gc = gdk_gc_new_with_values (gtk_widget_get_window (widget), &val, GDK_GC_EXPOSURES | GDK_GC_SUBWINDOW | GDK_GC_FUNCTION); col.red = rand() % 0xffff; col.green = rand() % 0xffff; col.blue = rand() % 0xffff; gdk_colormap_alloc_color (gtk_widget_get_colormap (widget), &col, FALSE, TRUE); gdk_gc_set_foreground (gc, &col); half = height / 2; #if 0 /* are both tree/userlist on the same side? */ paned = (GtkPaned *)widget->parent->parent; if (paned->child1 != NULL && paned->child2 != NULL) { gdk_draw_rectangle (draw, gc, 0, 1, 2, width - 3, height - 4); gdk_draw_rectangle (draw, gc, 0, 0, 1, width - 1, height - 2); g_object_unref (gc); return TRUE; } #endif if (y < half) { gdk_draw_rectangle (draw, gc, FALSE, 1 + ox, 2 + oy, width - 3, half - 4); gdk_draw_rectangle (draw, gc, FALSE, 0 + ox, 1 + oy, width - 1, half - 2); gtk_widget_queue_draw_area (widget, ox, half + oy, width, height - half); } else { gdk_draw_rectangle (draw, gc, FALSE, 0 + ox, half + 1 + oy, width - 1, half - 2); gdk_draw_rectangle (draw, gc, FALSE, 1 + ox, half + 2 + oy, width - 3, half - 4); gtk_widget_queue_draw_area (widget, ox, oy, width, half); } g_object_unref (gc); return TRUE; }