hexchat/src/fe-gtk/maingui.c

3903 lines
99 KiB
C

/* 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 <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <gdk/gdkkeysyms.h>
#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->gui->is_tab && (col == 0 || sess != current_tab))
{
switch (col)
{
case 0: /* no particular color (theme default) */
sess->new_data = FALSE;
sess->msg_said = FALSE;
sess->nick_said = FALSE;
chan_set_color (sess->res->tab, plain_list);
break;
case 1: /* new data has been displayed (dark red) */
sess->new_data = TRUE;
sess->msg_said = FALSE;
sess->nick_said = FALSE;
chan_set_color (sess->res->tab, newdata_list);
if (chan_is_collapsed (sess->res->tab)
&& !(server_sess->msg_said || server_sess->nick_said)
&& !(server_sess == current_tab))
{
server_sess->new_data = TRUE;
server_sess->msg_said = FALSE;
server_sess->nick_said = FALSE;
chan_set_color (chan_get_parent (sess->res->tab), newdata_list);
}
break;
case 2: /* new message arrived in channel (light red) */
sess->new_data = FALSE;
sess->msg_said = TRUE;
sess->nick_said = FALSE;
chan_set_color (sess->res->tab, newmsg_list);
if (chan_is_collapsed (sess->res->tab)
&& !server_sess->nick_said
&& !(server_sess == current_tab))
{
server_sess->new_data = FALSE;
server_sess->msg_said = TRUE;
server_sess->nick_said = FALSE;
chan_set_color (chan_get_parent (sess->res->tab), newmsg_list);
}
break;
case 3: /* your nick has been seen (blue) */
sess->new_data = FALSE;
sess->msg_said = FALSE;
sess->nick_said = TRUE;
chan_set_color (sess->res->tab, nickseen_list);
if (chan_is_collapsed (sess->res->tab) && !(server_sess == current_tab))
{
server_sess->new_data = FALSE;
server_sess->msg_said = FALSE;
server_sess->nick_said = TRUE;
chan_set_color (chan_get_parent (sess->res->tab), nickseen_list);
}
break;
}
lastact_update (sess);
}
}
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;
}
if (sess->new_data || sess->nick_said || sess->msg_said)
{
sess->nick_said = FALSE;
sess->msg_said = FALSE;
sess->new_data = FALSE;
lastact_update (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 void
mg_userlist_toggle_cb (GtkWidget *button, gpointer userdata)
{
prefs.hex_gui_ulist_hide = !prefs.hex_gui_ulist_hide;
mg_decide_userlist (current_sess, FALSE);
gtk_widget_grab_focus (current_sess->gui->input_box);
}
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_show (sess->gui->flag_wid[i]);
/* Update state */
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gui->flag_wid[i]),
res->flag_wid_state[i]);
}
ignore_chanmode = FALSE;
if (gui->lagometer)
{
gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (gui->lagometer),
res->lag_value);
if (res->lag_tip)
gtk_widget_set_tooltip_text (gtk_widget_get_parent (sess->gui->lagometer), res->lag_tip);
}
if (gui->throttlemeter)
{
gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (gui->throttlemeter),
res->queue_value);
if (res->queue_tip)
gtk_widget_set_tooltip_text (gtk_widget_get_parent (sess->gui->throttlemeter), res->queue_tip);
}
/* did this tab have a connecting graph? restore it.. */
if (res->c_graph)
{
res->c_graph = FALSE;
mg_progressbar_create (gui);
}
/* menu items */
menu_set_away (gui, sess->server->is_away);
gtk_widget_set_sensitive (gui->menu_item[MENU_ID_AWAY], sess->server->connected);
gtk_widget_set_sensitive (gui->menu_item[MENU_ID_JOIN], sess->server->end_of_motd);
gtk_widget_set_sensitive (gui->menu_item[MENU_ID_DISCONNECT],
sess->server->connected || sess->server->recondelay_tag);
mg_set_topic_tip (sess);
plugin_emit_dummy_print (sess, "Focus Tab");
}
void
mg_bring_tofront_sess (session *sess) /* IRC tab or window */
{
if (sess->gui->is_tab)
chan_focus (sess->res->tab);
else
gtk_window_present (GTK_WINDOW (sess->gui->window));
}
void
mg_bring_tofront (GtkWidget *vbox) /* non-IRC tab or window */
{
chan *ch;
ch = g_object_get_data (G_OBJECT (vbox), "ch");
if (ch)
chan_focus (ch);
else
gtk_window_present (GTK_WINDOW (gtk_widget_get_toplevel (vbox)));
}
void
mg_switch_page (int relative, int num)
{
if (mg_gui)
chanview_move_focus (mg_gui->chanview, relative, num);
}
/* a toplevel IRC window was destroyed */
static void
mg_topdestroy_cb (GtkWidget *win, session *sess)
{
/* printf("enter mg_topdestroy. sess %p was destroyed\n", sess);*/
/* kill the text buffer */
gtk_xtext_buffer_free (sess->res->buffer);
/* kill the user list */
g_object_unref (G_OBJECT (sess->res->user_model));
session_free (sess); /* tell hexchat.c about it */
}
/* cleanup an IRC tab */
static void
mg_ircdestroy (session *sess)
{
GSList *list;
/* kill the text buffer */
gtk_xtext_buffer_free (sess->res->buffer);
/* kill the user list */
g_object_unref (G_OBJECT (sess->res->user_model));
session_free (sess); /* tell hexchat.c about it */
if (mg_gui == NULL)
{
/* puts("-> mg_gui is already NULL");*/
return;
}
list = sess_list;
while (list)
{
sess = list->data;
if (sess->gui->is_tab)
{
/* puts("-> some tabs still remain");*/
return;
}
list = list->next;
}
/* puts("-> no tabs left, killing main tabwindow");*/
gtk_widget_destroy (mg_gui->window);
active_tab = NULL;
mg_gui = NULL;
parent_window = NULL;
}
static void
mg_tab_close_cb (GtkWidget *dialog, gint arg1, session *sess)
{
GSList *list, *next;
gtk_widget_destroy (dialog);
if (arg1 == GTK_RESPONSE_OK && is_session (sess))
{
/* force it NOT to send individual PARTs */
sess->server->sent_quit = TRUE;
for (list = sess_list; list;)
{
next = list->next;
if (((session *)list->data)->server == sess->server &&
((session *)list->data) != sess)
fe_close_window ((session *)list->data);
list = next;
}
/* just send one QUIT - better for BNCs */
sess->server->sent_quit = FALSE;
fe_close_window (sess);
}
}
void
mg_tab_close (session *sess)
{
GtkWidget *dialog;
GSList *list;
int i;
if (chan_remove (sess->res->tab, FALSE))
mg_ircdestroy (sess);
else
{
for (i = 0, list = sess_list; list; list = list->next)
{
session *s = (session*)list->data;
if (s->server == sess->server && (s->type == SESS_CHANNEL || s->type == SESS_DIALOG))
i++;
}
dialog = gtk_message_dialog_new (GTK_WINDOW (parent_window), 0,
GTK_MESSAGE_WARNING, GTK_BUTTONS_OK_CANCEL,
_("This server still has %d channels or dialogs associated with it. "
"Close them all?"), i);
g_signal_connect (G_OBJECT (dialog), "response",
G_CALLBACK (mg_tab_close_cb), sess);
if (prefs.hex_gui_tab_layout)
{
gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
}
else
{
gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER_ON_PARENT);
}
gtk_widget_show (dialog);
}
}
static void
mg_menu_destroy (GtkWidget *menu, gpointer userdata)
{
gtk_widget_destroy (menu);
g_object_unref (menu);
}
void
mg_create_icon_item (char *label, char *stock, GtkWidget *menu,
void *callback, void *userdata)
{
GtkWidget *item;
item = create_icon_menu (label, stock, TRUE);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (callback),
userdata);
gtk_widget_show (item);
}
static int
mg_count_networks (void)
{
int cons = 0;
GSList *list;
for (list = serv_list; list; list = list->next)
{
if (((server *)list->data)->connected)
cons++;
}
return cons;
}
static int
mg_count_dccs (void)
{
GSList *list;
struct DCC *dcc;
int dccs = 0;
list = dcc_list;
while (list)
{
dcc = list->data;
if ((dcc->type == TYPE_SEND || dcc->type == TYPE_RECV) &&
dcc->dccstat == STAT_ACTIVE)
dccs++;
list = list->next;
}
return dccs;
}
void
mg_open_quit_dialog (gboolean minimize_button)
{
static GtkWidget *dialog = NULL;
GtkWidget *dialog_vbox1;
GtkWidget *table1;
GtkWidget *image;
GtkWidget *checkbutton1;
GtkWidget *label;
GtkWidget *dialog_action_area1;
GtkWidget *button;
char *text, *connecttext;
int cons;
int dccs;
if (dialog)
{
gtk_window_present (GTK_WINDOW (dialog));
return;
}
dccs = mg_count_dccs ();
cons = mg_count_networks ();
if (dccs + cons == 0 || !prefs.hex_gui_quit_dialog)
{
hexchat_exit ();
return;
}
dialog = gtk_dialog_new ();
gtk_container_set_border_width (GTK_CONTAINER (dialog), 6);
gtk_window_set_title (GTK_WINDOW (dialog), _("Quit HexChat?"));
gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (parent_window));
gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
dialog_vbox1 = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
gtk_widget_show (dialog_vbox1);
table1 = gtk_table_new (2, 2, FALSE);
gtk_widget_show (table1);
gtk_box_pack_start (GTK_BOX (dialog_vbox1), table1, TRUE, TRUE, 0);
gtk_container_set_border_width (GTK_CONTAINER (table1), 6);
gtk_table_set_row_spacings (GTK_TABLE (table1), 12);
gtk_table_set_col_spacings (GTK_TABLE (table1), 12);
image = gtk_image_new_from_stock ("gtk-dialog-warning", GTK_ICON_SIZE_DIALOG);
gtk_widget_show (image);
gtk_table_attach (GTK_TABLE (table1), image, 0, 1, 0, 1,
(GtkAttachOptions) (GTK_FILL),
(GtkAttachOptions) (GTK_FILL), 0, 0);
checkbutton1 = gtk_check_button_new_with_mnemonic (_("Don't ask next time."));
gtk_widget_show (checkbutton1);
gtk_table_attach (GTK_TABLE (table1), checkbutton1, 0, 2, 1, 2,
(GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
(GtkAttachOptions) (0), 0, 4);
connecttext = g_strdup_printf (_("You are connected to %i IRC networks."), cons);
text = g_strdup_printf ("<span weight=\"bold\" size=\"larger\">%s</span>\n\n%s\n%s",
_("Are you sure you want to quit?"),
cons ? connecttext : "",
dccs ? _("Some file transfers are still active.") : "");
g_free (connecttext);
label = gtk_label_new (text);
g_free (text);
gtk_widget_show (label);
gtk_table_attach (GTK_TABLE (table1), label, 1, 2, 0, 1,
(GtkAttachOptions) (GTK_EXPAND | GTK_SHRINK | GTK_FILL),
(GtkAttachOptions) (GTK_EXPAND | GTK_SHRINK), 0, 0);
gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
dialog_action_area1 = gtk_dialog_get_action_area (GTK_DIALOG (dialog));
gtk_widget_show (dialog_action_area1);
gtk_button_box_set_layout (GTK_BUTTON_BOX (dialog_action_area1),
GTK_BUTTONBOX_END);
if (minimize_button && !unity_mode ())
{
button = gtk_button_new_with_mnemonic (_("_Minimize to Tray"));
gtk_widget_show (button);
gtk_dialog_add_action_widget (GTK_DIALOG (dialog), button, 1);
}
button = gtk_button_new_from_stock ("gtk-cancel");
gtk_widget_show (button);
gtk_dialog_add_action_widget (GTK_DIALOG (dialog), button,
GTK_RESPONSE_CANCEL);
gtk_widget_grab_focus (button);
button = gtk_button_new_from_stock ("gtk-quit");
gtk_widget_show (button);
gtk_dialog_add_action_widget (GTK_DIALOG (dialog), button, 0);
gtk_widget_show (dialog);
switch (gtk_dialog_run (GTK_DIALOG (dialog)))
{
case 0:
if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (checkbutton1)))
prefs.hex_gui_quit_dialog = 0;
hexchat_exit ();
break;
case 1: /* minimize to tray */
if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (checkbutton1)))
{
prefs.hex_gui_tray_close = 1;
/*prefs.hex_gui_quit_dialog = 0;*/
}
/* force tray icon ON, if not already */
if (!prefs.hex_gui_tray)
{
prefs.hex_gui_tray = 1;
tray_apply_setup ();
}
tray_toggle_visibility (TRUE);
break;
}
gtk_widget_destroy (dialog);
dialog = NULL;
}
void
mg_close_sess (session *sess)
{
if (sess_list->next == NULL)
{
mg_open_quit_dialog (FALSE);
return;
}
fe_close_window (sess);
}
static int
mg_chan_remove (chan *ch)
{
/* remove the tab from chanview */
chan_remove (ch, TRUE);
/* any tabs left? */
if (chanview_get_size (mg_gui->chanview) < 1)
{
/* if not, destroy the main tab window */
gtk_widget_destroy (mg_gui->window);
current_tab = NULL;
active_tab = NULL;
mg_gui = NULL;
parent_window = NULL;
return TRUE;
}
return FALSE;
}
/* destroy non-irc tab/window */
static void
mg_close_gen (chan *ch, GtkWidget *box)
{
char *title = g_object_get_data (G_OBJECT (box), "title");
g_free (title);
if (!ch)
ch = g_object_get_data (G_OBJECT (box), "ch");
if (ch)
{
/* remove from notebook */
gtk_widget_destroy (box);
/* remove the tab from chanview */
mg_chan_remove (ch);
} else
{
gtk_widget_destroy (gtk_widget_get_toplevel (box));
}
}
/* the "X" close button has been pressed (tab-view) */
static void
mg_xbutton_cb (chanview *cv, chan *ch, int tag, gpointer userdata)
{
if (tag == TAG_IRC) /* irc tab */
mg_close_sess (userdata);
else /* non-irc utility tab */
mg_close_gen (ch, userdata);
}
static void
mg_link_gentab (chan *ch, GtkWidget *box)
{
int num;
GtkWidget *win;
g_object_ref (box);
num = gtk_notebook_page_num (GTK_NOTEBOOK (mg_gui->note_book), box);
gtk_notebook_remove_page (GTK_NOTEBOOK (mg_gui->note_book), num);
mg_chan_remove (ch);
win = gtkutil_window_new (g_object_get_data (G_OBJECT (box), "title"), "",
GPOINTER_TO_INT (g_object_get_data (G_OBJECT (box), "w")),
GPOINTER_TO_INT (g_object_get_data (G_OBJECT (box), "h")),
2);
/* so it doesn't try to chan_remove (there's no tab anymore) */
g_object_steal_data (G_OBJECT (box), "ch");
gtk_container_set_border_width (GTK_CONTAINER (box), 0);
gtk_container_add (GTK_CONTAINER (win), box);
gtk_widget_show (win);
g_object_unref (box);
}
static void
mg_detach_tab_cb (GtkWidget *item, chan *ch)
{
if (chan_get_tag (ch) == TAG_IRC) /* IRC tab */
{
/* userdata is session * */
mg_link_irctab (chan_get_userdata (ch), 1);
return;
}
/* userdata is GtkWidget * */
mg_link_gentab (ch, chan_get_userdata (ch)); /* non-IRC tab */
}
static void
mg_destroy_tab_cb (GtkWidget *item, chan *ch)
{
/* treat it just like the X button press */
mg_xbutton_cb (mg_gui->chanview, ch, chan_get_tag (ch), chan_get_userdata (ch));
}
static void
mg_color_insert (GtkWidget *item, gpointer userdata)
{
char buf[32];
char *text;
int num = GPOINTER_TO_INT (userdata);
if (num > 99)
{
switch (num)
{
case 100:
text = "\002"; break;
case 101:
text = "\037"; break;
case 102:
text = "\035"; break;
default:
text = "\017"; break;
}
key_action_insert (current_sess->gui->input_box, 0, text, 0, 0);
} else
{
sprintf (buf, "\003%02d", num);
key_action_insert (current_sess->gui->input_box, 0, buf, 0, 0);
}
}
static void
mg_markup_item (GtkWidget *menu, char *text, int arg)
{
GtkWidget *item;
item = gtk_menu_item_new_with_label ("");
gtk_label_set_markup (GTK_LABEL (gtk_bin_get_child (GTK_BIN (item))), text);
g_signal_connect (G_OBJECT (item), "activate",
G_CALLBACK (mg_color_insert), GINT_TO_POINTER (arg));
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
gtk_widget_show (item);
}
GtkWidget *
mg_submenu (GtkWidget *menu, char *text)
{
GtkWidget *submenu, *item;
item = gtk_menu_item_new_with_mnemonic (text);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
gtk_widget_show (item);
submenu = gtk_menu_new ();
gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu);
gtk_widget_show (submenu);
return submenu;
}
static void
mg_create_color_menu (GtkWidget *menu, session *sess)
{
GtkWidget *submenu;
GtkWidget *subsubmenu;
char buf[256];
int i;
submenu = mg_submenu (menu, _("Insert Attribute or Color Code"));
mg_markup_item (submenu, _("<b>Bold</b>"), 100);
mg_markup_item (submenu, _("<u>Underline</u>"), 101);
mg_markup_item (submenu, _("<i>Italic</i>"), 102);
mg_markup_item (submenu, _("Normal"), 103);
subsubmenu = mg_submenu (submenu, _("Colors 0-7"));
for (i = 0; i < 8; i++)
{
sprintf (buf, "<tt><sup>%02d</sup> <span background=\"#%02x%02x%02x\">"
" </span></tt>",
i, colors[i].red >> 8, colors[i].green >> 8, colors[i].blue >> 8);
mg_markup_item (subsubmenu, buf, i);
}
subsubmenu = mg_submenu (submenu, _("Colors 8-15"));
for (i = 8; i < 16; i++)
{
sprintf (buf, "<tt><sup>%02d</sup> <span background=\"#%02x%02x%02x\">"
" </span></tt>",
i, colors[i].red >> 8, colors[i].green >> 8, colors[i].blue >> 8);
mg_markup_item (subsubmenu, buf, i);
}
}
static void
mg_set_guint8 (GtkCheckMenuItem *item, guint8 *setting)
{
session *sess = current_sess;
guint8 logging = sess->text_logging;
*setting = SET_OFF;
if (gtk_check_menu_item_get_active (item))
*setting = SET_ON;
/* has the logging setting changed? */
if (logging != sess->text_logging)
log_open_or_close (sess);
chanopt_save (sess);
chanopt_save_all ();
}
static void
mg_perchan_menu_item (char *label, GtkWidget *menu, guint8 *setting, guint global)
{
guint8 initial_value = *setting;
/* if it's using global value, use that as initial state */
if (initial_value == SET_DEFAULT)
initial_value = global;
menu_toggle_item (label, menu, mg_set_guint8, setting, initial_value);
}
static void
mg_create_perchannelmenu (session *sess, GtkWidget *menu)
{
GtkWidget *submenu;
submenu = menu_quick_sub (_("_Settings"), menu, NULL, XCMENU_MNEMONIC, -1);
mg_perchan_menu_item (_("_Log to Disk"), submenu, &sess->text_logging, prefs.hex_irc_logging);
mg_perchan_menu_item (_("_Reload Scrollback"), submenu, &sess->text_scrollback, prefs.hex_text_replay);
if (sess->type == SESS_CHANNEL)
{
mg_perchan_menu_item (_("Strip _Colors"), submenu, &sess->text_strip, prefs.hex_text_stripcolor_msg);
mg_perchan_menu_item (_("_Hide Join/Part Messages"), submenu, &sess->text_hidejoinpart, prefs.hex_irc_conf_mode);
}
}
static void
mg_create_alertmenu (session *sess, GtkWidget *menu)
{
GtkWidget *submenu;
submenu = menu_quick_sub (_("_Extra Alerts"), menu, NULL, XCMENU_MNEMONIC, -1);
mg_perchan_menu_item (_("Beep on _Message"), submenu, &sess->alert_beep, prefs.hex_input_beep_chans);
mg_perchan_menu_item (_("Blink Tray _Icon"), submenu, &sess->alert_tray, prefs.hex_input_tray_chans);
mg_perchan_menu_item (_("Blink Task _Bar"), submenu, &sess->alert_taskbar, prefs.hex_input_flash_chans);
}
static void
mg_create_tabmenu (session *sess, GdkEventButton *event, chan *ch)
{
GtkWidget *menu, *item;
char buf[256];
menu = gtk_menu_new ();
if (sess)
{
char *name = g_markup_escape_text (sess->channel[0] ? sess->channel : _("<none>"), -1);
g_snprintf (buf, sizeof (buf), "<span foreground=\"#3344cc\"><b>%s</b></span>", name);
g_free (name);
item = gtk_menu_item_new_with_label ("");
gtk_label_set_markup (GTK_LABEL (gtk_bin_get_child (GTK_BIN (item))), buf);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
gtk_widget_show (item);
/* separator */
menu_quick_item (0, 0, menu, XCMENU_SHADED, 0, 0);
/* per-channel alerts */
mg_create_alertmenu (sess, menu);
/* per-channel settings */
mg_create_perchannelmenu (sess, menu);
/* separator */
menu_quick_item (0, 0, menu, XCMENU_SHADED, 0, 0);
if (sess->type == SESS_CHANNEL)
menu_addfavoritemenu (sess->server, menu, sess->channel, TRUE);
else if (sess->type == SESS_SERVER)
menu_addconnectmenu (sess->server, menu);
}
mg_create_icon_item (_("_Detach"), GTK_STOCK_REDO, menu,
mg_detach_tab_cb, ch);
mg_create_icon_item (_("_Close"), GTK_STOCK_CLOSE, menu,
mg_destroy_tab_cb, ch);
if (sess && tabmenu_list)
menu_create (menu, tabmenu_list, sess->channel, FALSE);
if (sess)
menu_add_plugin_items (menu, "\x4$TAB", sess->channel);
if (event->window)
gtk_menu_set_screen (GTK_MENU (menu), gdk_window_get_screen (event->window));
g_object_ref (menu);
g_object_ref_sink (menu);
g_object_unref (menu);
g_signal_connect (G_OBJECT (menu), "selection-done",
G_CALLBACK (mg_menu_destroy), NULL);
gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, 0, event->time);
}
static gboolean
mg_tab_contextmenu_cb (chanview *cv, chan *ch, int tag, gpointer ud, GdkEventButton *event)
{
/* middle-click or shift-click to close a tab */
if (((prefs.hex_gui_tab_middleclose && event->button == 2) || (event->button == 1 && event->state & STATE_SHIFT))
&& event->type == GDK_BUTTON_PRESS)
{
mg_xbutton_cb (cv, ch, tag, ud);
return TRUE;
}
if (event->button != 3)
return FALSE;
if (tag == TAG_IRC)
mg_create_tabmenu (ud, event, ch);
else
mg_create_tabmenu (NULL, event, ch);
return TRUE;
}
void
mg_dnd_drop_file (session *sess, char *target, char *uri)
{
char *p, *data, *next, *fname;
p = data = g_strdup (uri);
while (*p)
{
next = strchr (p, '\r');
if (g_ascii_strncasecmp ("file:", p, 5) == 0)
{
if (next)
*next = 0;
fname = g_filename_from_uri (p, NULL, NULL);
if (fname)
{
/* dcc_send() expects utf-8 */
p = g_filename_from_utf8 (fname, -1, 0, 0, 0);
if (p)
{
dcc_send (sess, target, p, prefs.hex_dcc_max_send_cps, 0);
g_free (p);
}
g_free (fname);
}
}
if (!next)
break;
p = next + 1;
if (*p == '\n')
p++;
}
g_free (data);
}
static void
mg_dialog_dnd_drop (GtkWidget * widget, GdkDragContext * context, gint x,
gint y, GtkSelectionData * selection_data, guint info,
guint32 time, gpointer ud)
{
if (current_sess->type == SESS_DIALOG)
/* sess->channel is really the nickname of dialogs */
mg_dnd_drop_file (current_sess, current_sess->channel, (char *)gtk_selection_data_get_data (selection_data));
}
/* add a tabbed channel */
static void
mg_add_chan (session *sess)
{
GdkPixbuf *icon;
char *name = _("<none>");
if (sess->channel[0])
name = sess->channel;
switch (sess->type)
{
case SESS_CHANNEL:
icon = pix_tree_channel;
break;
case SESS_SERVER:
icon = pix_tree_server;
break;
default:
icon = pix_tree_dialog;
}
sess->res->tab = chanview_add (sess->gui->chanview, name, sess->server, sess,
sess->type == SESS_SERVER ? FALSE : TRUE,
TAG_IRC, icon);
if (plain_list == NULL)
mg_create_tab_colors ();
chan_set_color (sess->res->tab, plain_list);
if (sess->res->buffer == NULL)
{
sess->res->buffer = gtk_xtext_buffer_new (GTK_XTEXT (sess->gui->xtext));
gtk_xtext_set_time_stamp (sess->res->buffer, prefs.hex_stamp_text);
sess->res->user_model = userlist_create_model (sess);
}
}
static void
mg_userlist_button (GtkWidget * box, char *label, char *cmd,
int a, int b, int c, int d)
{
GtkWidget *wid = gtk_button_new_with_label (label);
g_signal_connect (G_OBJECT (wid), "clicked",
G_CALLBACK (userlist_button_cb), cmd);
gtk_table_attach_defaults (GTK_TABLE (box), wid, a, b, c, d);
show_and_unfocus (wid);
}
static GtkWidget *
mg_create_userlistbuttons (GtkWidget *box)
{
struct popup *pop;
GSList *list = button_list;
int a = 0, b = 0;
GtkWidget *tab;
tab = gtk_table_new (5, 2, FALSE);
gtk_box_pack_end (GTK_BOX (box), tab, FALSE, FALSE, 0);
while (list)
{
pop = list->data;
if (pop->cmd[0])
{
mg_userlist_button (tab, pop->name, pop->cmd, a, a + 1, b, b + 1);
a++;
if (a == 2)
{
a = 0;
b++;
}
}
list = list->next;
}
return tab;
}
static void
mg_topic_cb (GtkWidget *entry, gpointer userdata)
{
session *sess = current_sess;
char *text;
if (sess->channel[0] && sess->server->connected && sess->type == SESS_CHANNEL)
{
text = (char *)gtk_entry_get_text (GTK_ENTRY (entry));
if (text[0] == 0)
text = NULL;
sess->server->p_topic (sess->server, sess->channel, text);
} else
gtk_entry_set_text (GTK_ENTRY (entry), "");
/* restore focus to the input widget, where the next input will most
likely be */
gtk_widget_grab_focus (sess->gui->input_box);
}
static void
mg_tabwindow_kill_cb (GtkWidget *win, gpointer userdata)
{
GSList *list, *next;
session *sess;
/* puts("enter mg_tabwindow_kill_cb");*/
hexchat_is_quitting = TRUE;
/* see if there's any non-tab windows left */
list = sess_list;
while (list)
{
sess = list->data;
next = list->next;
if (!sess->gui->is_tab)
{
hexchat_is_quitting = FALSE;
/* puts("-> will not exit, some toplevel windows left");*/
} else
{
mg_ircdestroy (sess);
}
list = next;
}
current_tab = NULL;
active_tab = NULL;
mg_gui = NULL;
parent_window = NULL;
}
static GtkWidget *
mg_changui_destroy (session *sess)
{
GtkWidget *ret = NULL;
if (sess->gui->is_tab)
{
/* avoid calling the "destroy" callback */
g_signal_handlers_disconnect_by_func (G_OBJECT (sess->gui->window),
mg_tabwindow_kill_cb, 0);
/* remove the tab from the chanview */
if (!mg_chan_remove (sess->res->tab))
/* if the window still exists, restore the signal handler */
g_signal_connect (G_OBJECT (sess->gui->window), "destroy",
G_CALLBACK (mg_tabwindow_kill_cb), 0);
} else
{
/* avoid calling the "destroy" callback */
g_signal_handlers_disconnect_by_func (G_OBJECT (sess->gui->window),
mg_topdestroy_cb, sess);
/*gtk_widget_destroy (sess->gui->window);*/
/* don't destroy until the new one is created. Not sure why, but */
/* it fixes: Gdk-CRITICAL **: gdk_colormap_get_screen: */
/* assertion `GDK_IS_COLORMAP (cmap)' failed */
ret = sess->gui->window;
g_free (sess->gui);
sess->gui = NULL;
}
return ret;
}
static void
mg_link_irctab (session *sess, int focus)
{
GtkWidget *win;
if (sess->gui->is_tab)
{
win = mg_changui_destroy (sess);
mg_changui_new (sess, sess->res, 0, focus);
mg_populate (sess);
hexchat_is_quitting = FALSE;
if (win)
gtk_widget_destroy (win);
return;
}
mg_unpopulate (sess);
win = mg_changui_destroy (sess);
mg_changui_new (sess, sess->res, 1, focus);
/* the buffer is now attached to a different widget */
((xtext_buffer *)sess->res->buffer)->xtext = (GtkXText *)sess->gui->xtext;
if (win)
gtk_widget_destroy (win);
}
void
mg_detach (session *sess, int mode)
{
switch (mode)
{
/* detach only */
case 1:
if (sess->gui->is_tab)
mg_link_irctab (sess, 1);
break;
/* attach only */
case 2:
if (!sess->gui->is_tab)
mg_link_irctab (sess, 1);
break;
/* toggle */
default:
mg_link_irctab (sess, 1);
}
}
static int
check_is_number (char *t)
{
while (*t)
{
if (*t < '0' || *t > '9')
return FALSE;
t++;
}
return TRUE;
}
static void
mg_change_flag (GtkWidget * wid, session *sess, char flag)
{
server *serv = sess->server;
char mode[3];
mode[1] = flag;
mode[2] = '\0';
if (serv->connected && sess->channel[0])
{
if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (wid)))
mode[0] = '+';
else
mode[0] = '-';
serv->p_mode (serv, sess->channel, mode);
serv->p_join_info (serv, sess->channel);
sess->ignore_mode = TRUE;
sess->ignore_date = TRUE;
}
}
static void
flagl_hit (GtkWidget * wid, struct session *sess)
{
char modes[512];
const char *limit_str;
server *serv = sess->server;
if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (wid)))
{
if (serv->connected && sess->channel[0])
{
limit_str = gtk_entry_get_text (GTK_ENTRY (sess->gui->limit_entry));
if (check_is_number ((char *)limit_str) == FALSE)
{
fe_message (_("User limit must be a number!\n"), FE_MSG_ERROR);
gtk_entry_set_text (GTK_ENTRY (sess->gui->limit_entry), "");
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (wid), FALSE);
return;
}
g_snprintf (modes, sizeof (modes), "+l %d", atoi (limit_str));
serv->p_mode (serv, sess->channel, modes);
serv->p_join_info (serv, sess->channel);
}
} else
mg_change_flag (wid, sess, 'l');
}
static void
flagk_hit (GtkWidget * wid, struct session *sess)
{
char modes[512];
server *serv = sess->server;
if (serv->connected && sess->channel[0])
{
g_snprintf (modes, sizeof (modes), "-k %s",
gtk_entry_get_text (GTK_ENTRY (sess->gui->key_entry)));
if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (wid)))
modes[0] = '+';
serv->p_mode (serv, sess->channel, modes);
}
}
static void
mg_flagbutton_cb (GtkWidget *but, char *flag)
{
session *sess;
char mode;
if (ignore_chanmode)
return;
sess = current_sess;
mode = tolower ((unsigned char) flag[0]);
switch (mode)
{
case 'l':
flagl_hit (but, sess);
break;
case 'k':
flagk_hit (but, sess);
break;
case 'b':
ignore_chanmode = TRUE;
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (sess->gui->flag_b), FALSE);
ignore_chanmode = FALSE;
banlist_opengui (sess);
break;
default:
mg_change_flag (but, sess, mode);
}
}
static GtkWidget *
mg_create_flagbutton (char *tip, GtkWidget *box, char *face)
{
GtkWidget *btn, *lbl;
char label_markup[16];
g_snprintf (label_markup, sizeof(label_markup), "<tt>%s</tt>", face);
lbl = gtk_label_new (NULL);
gtk_label_set_markup (GTK_LABEL(lbl), label_markup);
btn = gtk_toggle_button_new ();
gtk_widget_set_size_request (btn, -1, 0);
gtk_widget_set_tooltip_text (btn, tip);
gtk_container_add (GTK_CONTAINER(btn), lbl);
gtk_box_pack_start (GTK_BOX (box), btn, 0, 0, 0);
g_signal_connect (G_OBJECT (btn), "toggled",
G_CALLBACK (mg_flagbutton_cb), face);
show_and_unfocus (btn);
return btn;
}
static void
mg_key_entry_cb (GtkWidget * igad, gpointer userdata)
{
char modes[512];
session *sess = current_sess;
server *serv = sess->server;
if (serv->connected && sess->channel[0])
{
g_snprintf (modes, sizeof (modes), "+k %s",
gtk_entry_get_text (GTK_ENTRY (igad)));
serv->p_mode (serv, sess->channel, modes);
serv->p_join_info (serv, sess->channel);
}
}
static void
mg_limit_entry_cb (GtkWidget * igad, gpointer userdata)
{
char modes[512];
session *sess = current_sess;
server *serv = sess->server;
if (serv->connected && sess->channel[0])
{
if (check_is_number ((char *)gtk_entry_get_text (GTK_ENTRY (igad))) == FALSE)
{
gtk_entry_set_text (GTK_ENTRY (igad), "");
fe_message (_("User limit must be a number!\n"), FE_MSG_ERROR);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (sess->gui->flag_l), FALSE);
return;
}
g_snprintf (modes, sizeof(modes), "+l %d",
atoi (gtk_entry_get_text (GTK_ENTRY (igad))));
serv->p_mode (serv, sess->channel, modes);
serv->p_join_info (serv, sess->channel);
}
}
static void
mg_apply_entry_style (GtkWidget *entry)
{
gtk_widget_modify_base (entry, GTK_STATE_NORMAL, &colors[COL_BG]);
gtk_widget_modify_text (entry, GTK_STATE_NORMAL, &colors[COL_FG]);
gtk_widget_modify_font (entry, input_style->font_desc);
}
static void
mg_create_chanmodebuttons (session_gui *gui, GtkWidget *box)
{
gui->flag_c = mg_create_flagbutton (_("Filter Colors"), box, "c");
gui->flag_n = mg_create_flagbutton (_("No outside messages"), box, "n");
gui->flag_t = mg_create_flagbutton (_("Topic Protection"), box, "t");
gui->flag_i = mg_create_flagbutton (_("Invite Only"), box, "i");
gui->flag_m = mg_create_flagbutton (_("Moderated"), box, "m");
gui->flag_b = mg_create_flagbutton (_("Ban List"), box, "b");
gui->flag_k = mg_create_flagbutton (_("Keyword"), box, "k");
gui->key_entry = gtk_entry_new ();
gtk_widget_set_name (gui->key_entry, "hexchat-inputbox");
gtk_entry_set_max_length (GTK_ENTRY (gui->key_entry), 23);
gtk_widget_set_size_request (gui->key_entry, 115, -1);
gtk_box_pack_start (GTK_BOX (box), gui->key_entry, 0, 0, 0);
g_signal_connect (G_OBJECT (gui->key_entry), "activate",
G_CALLBACK (mg_key_entry_cb), NULL);
if (prefs.hex_gui_input_style)
mg_apply_entry_style (gui->key_entry);
gui->flag_l = mg_create_flagbutton (_("User Limit"), box, "l");
gui->limit_entry = gtk_entry_new ();
gtk_widget_set_name (gui->limit_entry, "hexchat-inputbox");
gtk_entry_set_max_length (GTK_ENTRY (gui->limit_entry), 10);
gtk_widget_set_size_request (gui->limit_entry, 30, -1);
gtk_box_pack_start (GTK_BOX (box), gui->limit_entry, 0, 0, 0);
g_signal_connect (G_OBJECT (gui->limit_entry), "activate",
G_CALLBACK (mg_limit_entry_cb), NULL);
if (prefs.hex_gui_input_style)
mg_apply_entry_style (gui->limit_entry);
}
/*static void
mg_create_link_buttons (GtkWidget *box, gpointer userdata)
{
gtkutil_button (box, GTK_STOCK_CLOSE, _("Close this tab/window"),
mg_x_click_cb, userdata, 0);
if (!userdata)
gtkutil_button (box, GTK_STOCK_REDO, _("Attach/Detach this tab"),
mg_link_cb, userdata, 0);
}*/
static void
mg_dialog_button_cb (GtkWidget *wid, char *cmd)
{
/* the longest cmd is 12, and the longest nickname is 64 */
char buf[128];
char *host = "";
char *topic;
if (!current_sess)
return;
topic = (char *)(gtk_entry_get_text (GTK_ENTRY (current_sess->gui->topic_entry)));
topic = strrchr (topic, '@');
if (topic)
host = topic + 1;
auto_insert (buf, sizeof (buf), cmd, 0, 0, "", "", "",
server_get_network (current_sess->server, TRUE), host, "",
current_sess->channel, "");
handle_command (current_sess, buf, TRUE);
/* dirty trick to avoid auto-selection */
SPELL_ENTRY_SET_EDITABLE (current_sess->gui->input_box, FALSE);
gtk_widget_grab_focus (current_sess->gui->input_box);
SPELL_ENTRY_SET_EDITABLE (current_sess->gui->input_box, TRUE);
}
static void
mg_dialog_button (GtkWidget *box, char *name, char *cmd)
{
GtkWidget *wid;
wid = gtk_button_new_with_label (name);
gtk_box_pack_start (GTK_BOX (box), wid, FALSE, FALSE, 0);
g_signal_connect (G_OBJECT (wid), "clicked",
G_CALLBACK (mg_dialog_button_cb), cmd);
gtk_widget_set_size_request (wid, -1, 0);
}
static void
mg_create_dialogbuttons (GtkWidget *box)
{
struct popup *pop;
GSList *list = dlgbutton_list;
while (list)
{
pop = list->data;
if (pop->cmd[0])
mg_dialog_button (box, pop->name, pop->cmd);
list = list->next;
}
}
static void
mg_create_topicbar (session *sess, GtkWidget *box)
{
GtkWidget *hbox, *topic, *bbox;
session_gui *gui = sess->gui;
gui->topic_bar = hbox = gtk_hbox_new (FALSE, 0);
gtk_box_pack_start (GTK_BOX (box), hbox, 0, 0, 0);
if (!gui->is_tab)
sess->res->tab = NULL;
gui->topic_entry = topic = sexy_spell_entry_new ();
gtk_widget_set_name (topic, "hexchat-inputbox");
sexy_spell_entry_set_checked (SEXY_SPELL_ENTRY (topic), FALSE);
gtk_container_add (GTK_CONTAINER (hbox), topic);
g_signal_connect (G_OBJECT (topic), "activate",
G_CALLBACK (mg_topic_cb), 0);
if (prefs.hex_gui_input_style)
mg_apply_entry_style (topic);
gui->topicbutton_box = bbox = gtk_hbox_new (FALSE, 0);
gtk_box_pack_start (GTK_BOX (hbox), bbox, 0, 0, 0);
mg_create_chanmodebuttons (gui, bbox);
gui->dialogbutton_box = bbox = gtk_hbox_new (FALSE, 0);
gtk_box_pack_start (GTK_BOX (hbox), bbox, 0, 0, 0);
mg_create_dialogbuttons (bbox);
if (!prefs.hex_gui_ulist_resizable)
gtkutil_button (hbox, GTK_STOCK_GOTO_LAST, _("Show/Hide userlist"),
mg_userlist_toggle_cb, 0, 0);
}
/* check if a word is clickable */
static int
mg_word_check (GtkWidget * xtext, char *word)
{
session *sess = current_sess;
int ret;
ret = url_check_word (word);
if (ret == 0 && sess->type == SESS_DIALOG)
return WORD_DIALOG;
return ret;
}
/* mouse click inside text area */
static void
mg_word_clicked (GtkWidget *xtext, char *word, GdkEventButton *even)
{
session *sess = current_sess;
int word_type = 0, start, end;
char *tmp;
if (word)
{
word_type = mg_word_check (xtext, word);
url_last (&start, &end);
}
if (even->button == 1) /* left button */
{
if (word == NULL)
{
mg_focus (sess);
return;
}
if ((even->state & 13) == prefs.hex_gui_url_mod)
{
switch (word_type)
{
case WORD_URL:
case WORD_HOST6:
case WORD_HOST:
word[end] = 0;
fe_open_url (word + start);
}
}
return;
}
if (even->button == 2)
{
if (sess->type == SESS_DIALOG)
menu_middlemenu (sess, even);
else if (even->type == GDK_2BUTTON_PRESS)
userlist_select (sess, word);
return;
}
if (word == NULL)
return;
switch (word_type)
{
case 0:
case WORD_PATH:
menu_middlemenu (sess, even);
break;
case WORD_URL:
case WORD_HOST6:
case WORD_HOST:
word[end] = 0;
word += start;
menu_urlmenu (even, word);
break;
case WORD_NICK:
word[end] = 0;
word += start;
menu_nickmenu (sess, even, word, FALSE);
break;
case WORD_CHANNEL:
word[end] = 0;
word += start;
menu_chanmenu (sess, even, word);
break;
case WORD_EMAIL:
word[end] = 0;
word += start;
tmp = g_strdup_printf ("mailto:%s", word + (ispunct (*word) ? 1 : 0));
menu_urlmenu (even, tmp);
g_free (tmp);
break;
case WORD_DIALOG:
menu_nickmenu (sess, even, sess->channel, FALSE);
break;
}
}
void
mg_update_xtext (GtkWidget *wid)
{
GtkXText *xtext = GTK_XTEXT (wid);
gtk_xtext_set_palette (xtext, colors);
gtk_xtext_set_max_lines (xtext, prefs.hex_text_max_lines);
gtk_xtext_set_background (xtext, channelwin_pix);
gtk_xtext_set_wordwrap (xtext, prefs.hex_text_wordwrap);
gtk_xtext_set_show_marker (xtext, prefs.hex_text_show_marker);
gtk_xtext_set_show_separator (xtext, prefs.hex_text_indent ? prefs.hex_text_show_sep : 0);
gtk_xtext_set_indent (xtext, prefs.hex_text_indent);
if (!gtk_xtext_set_font (xtext, prefs.hex_text_font))
{
fe_message ("Failed to open any font. I'm out of here!", FE_MSG_WAIT | FE_MSG_ERROR);
exit (1);
}
gtk_xtext_refresh (xtext);
}
static void
mg_create_textarea (session *sess, GtkWidget *box)
{
GtkWidget *inbox, *vbox, *frame;
GtkXText *xtext;
session_gui *gui = sess->gui;
static const GtkTargetEntry dnd_targets[] =
{
{"text/uri-list", 0, 1}
};
static const GtkTargetEntry dnd_dest_targets[] =
{
{"HEXCHAT_CHANVIEW", GTK_TARGET_SAME_APP, 75 },
{"HEXCHAT_USERLIST", GTK_TARGET_SAME_APP, 75 }
};
vbox = gtk_vbox_new (FALSE, 0);
gtk_container_add (GTK_CONTAINER (box), vbox);
inbox = gtk_hbox_new (FALSE, 2);
gtk_container_add (GTK_CONTAINER (vbox), inbox);
frame = gtk_frame_new (NULL);
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
gtk_container_add (GTK_CONTAINER (inbox), frame);
gui->xtext = gtk_xtext_new (colors, TRUE);
xtext = GTK_XTEXT (gui->xtext);
gtk_xtext_set_max_indent (xtext, prefs.hex_text_max_indent);
gtk_xtext_set_thin_separator (xtext, prefs.hex_text_thin_sep);
gtk_xtext_set_urlcheck_function (xtext, mg_word_check);
gtk_xtext_set_max_lines (xtext, prefs.hex_text_max_lines);
gtk_container_add (GTK_CONTAINER (frame), GTK_WIDGET (xtext));
mg_update_xtext (GTK_WIDGET (xtext));
g_signal_connect (G_OBJECT (xtext), "word_click",
G_CALLBACK (mg_word_clicked), NULL);
gui->vscrollbar = gtk_vscrollbar_new (GTK_XTEXT (xtext)->adj);
gtk_box_pack_start (GTK_BOX (inbox), gui->vscrollbar, FALSE, TRUE, 0);
gtk_drag_dest_set (gui->vscrollbar, 5, dnd_dest_targets, 2,
GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK);
g_signal_connect (G_OBJECT (gui->vscrollbar), "drag_begin",
G_CALLBACK (mg_drag_begin_cb), NULL);
g_signal_connect (G_OBJECT (gui->vscrollbar), "drag_drop",
G_CALLBACK (mg_drag_drop_cb), NULL);
g_signal_connect (G_OBJECT (gui->vscrollbar), "drag_motion",
G_CALLBACK (mg_drag_motion_cb), gui->vscrollbar);
g_signal_connect (G_OBJECT (gui->vscrollbar), "drag_end",
G_CALLBACK (mg_drag_end_cb), NULL);
gtk_drag_dest_set (gui->xtext, GTK_DEST_DEFAULT_ALL, dnd_targets, 1,
GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK);
g_signal_connect (G_OBJECT (gui->xtext), "drag_data_received",
G_CALLBACK (mg_dialog_dnd_drop), NULL);
}
static GtkWidget *
mg_create_infoframe (GtkWidget *box)
{
GtkWidget *frame, *label, *hbox;
frame = gtk_frame_new (0);
gtk_frame_set_shadow_type ((GtkFrame*)frame, GTK_SHADOW_OUT);
gtk_container_add (GTK_CONTAINER (box), frame);
hbox = gtk_hbox_new (0, 0);
gtk_container_add (GTK_CONTAINER (frame), hbox);
label = gtk_label_new (NULL);
gtk_container_add (GTK_CONTAINER (hbox), label);
return label;
}
static void
mg_create_meters (session_gui *gui, GtkWidget *parent_box)
{
GtkWidget *infbox, *wid, *box;
gui->meter_box = infbox = box = gtk_vbox_new (0, 1);
gtk_box_pack_start (GTK_BOX (parent_box), box, 0, 0, 0);
if ((prefs.hex_gui_lagometer & 2) || (prefs.hex_gui_throttlemeter & 2))
{
infbox = gtk_hbox_new (0, 0);
gtk_box_pack_start (GTK_BOX (box), infbox, 0, 0, 0);
}
if (prefs.hex_gui_lagometer & 1)
{
gui->lagometer = wid = gtk_progress_bar_new ();
#ifdef WIN32
gtk_widget_set_size_request (wid, 1, 10);
#else
gtk_widget_set_size_request (wid, 1, 8);
#endif
wid = gtk_event_box_new ();
gtk_container_add (GTK_CONTAINER (wid), gui->lagometer);
gtk_box_pack_start (GTK_BOX (box), wid, 0, 0, 0);
}
if (prefs.hex_gui_lagometer & 2)
{
gui->laginfo = wid = mg_create_infoframe (infbox);
gtk_label_set_text ((GtkLabel *) wid, "Lag");
}
if (prefs.hex_gui_throttlemeter & 1)
{
gui->throttlemeter = wid = gtk_progress_bar_new ();
#ifdef WIN32
gtk_widget_set_size_request (wid, 1, 10);
#else
gtk_widget_set_size_request (wid, 1, 8);
#endif
wid = gtk_event_box_new ();
gtk_container_add (GTK_CONTAINER (wid), gui->throttlemeter);
gtk_box_pack_start (GTK_BOX (box), wid, 0, 0, 0);
}
if (prefs.hex_gui_throttlemeter & 2)
{
gui->throttleinfo = wid = mg_create_infoframe (infbox);
gtk_label_set_text ((GtkLabel *) wid, "Throttle");
}
}
void
mg_update_meters (session_gui *gui)
{
gtk_widget_destroy (gui->meter_box);
gui->lagometer = NULL;
gui->laginfo = NULL;
gui->throttlemeter = NULL;
gui->throttleinfo = NULL;
mg_create_meters (gui, gui->button_box_parent);
gtk_widget_show_all (gui->meter_box);
}
static void
mg_create_userlist (session_gui *gui, GtkWidget *box)
{
GtkWidget *frame, *ulist, *vbox;
vbox = gtk_vbox_new (0, 1);
gtk_container_add (GTK_CONTAINER (box), vbox);
frame = gtk_frame_new (NULL);
if (prefs.hex_gui_ulist_count)
gtk_box_pack_start (GTK_BOX (vbox), frame, 0, 0, GUI_SPACING);
gui->namelistinfo = gtk_label_new (NULL);
gtk_container_add (GTK_CONTAINER (frame), gui->namelistinfo);
gui->user_tree = ulist = userlist_create (vbox);
if (prefs.hex_gui_ulist_style)
{
gtk_widget_set_style (ulist, input_style);
gtk_widget_modify_base (ulist, GTK_STATE_NORMAL, &colors[COL_BG]);
}
mg_create_meters (gui, vbox);
gui->button_box_parent = vbox;
gui->button_box = mg_create_userlistbuttons (vbox);
}
static void
mg_vpane_cb (GtkPaned *pane, GParamSpec *param, session_gui *gui)
{
prefs.hex_gui_pane_divider_position = gtk_paned_get_position (pane);
}
static void
mg_leftpane_cb (GtkPaned *pane, GParamSpec *param, session_gui *gui)
{
prefs.hex_gui_pane_left_size = gtk_paned_get_position (pane);
}
static void
mg_rightpane_cb (GtkPaned *pane, GParamSpec *param, session_gui *gui)
{
int handle_size;
GtkAllocation allocation;
gtk_widget_style_get (GTK_WIDGET (pane), "handle-size", &handle_size, NULL);
/* record the position from the RIGHT side */
gtk_widget_get_allocation (GTK_WIDGET(pane), &allocation);
prefs.hex_gui_pane_right_size = allocation.width - gtk_paned_get_position (pane) - handle_size;
}
static gboolean
mg_add_pane_signals (session_gui *gui)
{
g_signal_connect (G_OBJECT (gui->hpane_right), "notify::position",
G_CALLBACK (mg_rightpane_cb), gui);
g_signal_connect (G_OBJECT (gui->hpane_left), "notify::position",
G_CALLBACK (mg_leftpane_cb), gui);
g_signal_connect (G_OBJECT (gui->vpane_left), "notify::position",
G_CALLBACK (mg_vpane_cb), gui);
g_signal_connect (G_OBJECT (gui->vpane_right), "notify::position",
G_CALLBACK (mg_vpane_cb), gui);
return FALSE;
}
static void
mg_create_center (session *sess, session_gui *gui, GtkWidget *box)
{
GtkWidget *vbox, *hbox, *book;
/* sep between top and bottom of left side */
gui->vpane_left = gtk_vpaned_new ();
/* sep between top and bottom of right side */
gui->vpane_right = gtk_vpaned_new ();
/* sep between left and xtext */
gui->hpane_left = gtk_hpaned_new ();
gtk_paned_set_position (GTK_PANED (gui->hpane_left), prefs.hex_gui_pane_left_size);
/* sep between xtext and right side */
gui->hpane_right = gtk_hpaned_new ();
if (prefs.hex_gui_win_swap)
{
gtk_paned_pack2 (GTK_PANED (gui->hpane_left), gui->vpane_left, FALSE, TRUE);
gtk_paned_pack1 (GTK_PANED (gui->hpane_left), gui->hpane_right, TRUE, TRUE);
}
else
{
gtk_paned_pack1 (GTK_PANED (gui->hpane_left), gui->vpane_left, FALSE, TRUE);
gtk_paned_pack2 (GTK_PANED (gui->hpane_left), gui->hpane_right, TRUE, TRUE);
}
gtk_paned_pack2 (GTK_PANED (gui->hpane_right), gui->vpane_right, FALSE, TRUE);
gtk_container_add (GTK_CONTAINER (box), gui->hpane_left);
gui->note_book = book = gtk_notebook_new ();
gtk_notebook_set_show_tabs (GTK_NOTEBOOK (book), FALSE);
gtk_notebook_set_show_border (GTK_NOTEBOOK (book), FALSE);
gtk_paned_pack1 (GTK_PANED (gui->hpane_right), book, TRUE, TRUE);
hbox = gtk_hbox_new (FALSE, 0);
gtk_paned_pack1 (GTK_PANED (gui->vpane_right), hbox, FALSE, TRUE);
mg_create_userlist (gui, hbox);
gui->user_box = hbox;
vbox = gtk_vbox_new (FALSE, 3);
gtk_notebook_append_page (GTK_NOTEBOOK (book), vbox, NULL);
mg_create_topicbar (sess, vbox);
if (prefs.hex_gui_search_pos)
{
mg_create_search (sess, vbox);
mg_create_textarea (sess, vbox);
}
else
{
mg_create_textarea (sess, vbox);
mg_create_search (sess, vbox);
}
mg_create_entry (sess, vbox);
mg_add_pane_signals (gui);
}
static void
mg_change_nick (int cancel, char *text, gpointer userdata)
{
char 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, T