/* 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->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)
breakpre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long *//* X-Chat
* Copyright (C) 1998 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 <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "fe-gtk.h"
#ifdef WIN32
#include <gdk/gdkwin32.h>
#include <windows.h>
#else
#include <unistd.h>
#endif
#include "../common/hexchat.h"
#include "../common/fe.h"
#include "../common/util.h"
#include "../common/text.h"
#include "../common/cfgfiles.h"
#include "../common/hexchatc.h"
#include "../common/plugin.h"
#include "../common/server.h"
#include "../common/url.h"
#include "gtkutil.h"
#include "maingui.h"
#include "pixmaps.h"
#include "chanlist.h"
#include "joind.h"
#include "xtext.h"
#include "palette.h"
#include "menu.h"
#include "notifygui.h"
#include "textgui.h"
#include "fkeys.h"
#include "plugin-tray.h"
#include "urlgrab.h"
#include "setup.h"
#ifdef USE_LIBCANBERRA
#include <canberra.h>
#endif
GdkPixmap *channelwin_pix;
#ifdef USE_LIBCANBERRA
static ca_context *ca_con;
#endif
#ifdef HAVE_GTK_MAC
GtkosxApplication *osx_app;
#endif
/* === command-line parameter parsing : requires glib 2.6 === */
static char *arg_cfgdir = NULL;
static gint arg_show_autoload = 0;
static gint arg_show_config = 0;
static gint arg_show_version = 0;
static gint arg_minimize = 0;
static const GOptionEntry gopt_entries[] =
{
{"no-auto", 'a', 0, G_OPTION_ARG_NONE, &arg_dont_autoconnect, N_("Don't auto connect to servers"), NULL},
{"cfgdir", 'd', 0, G_OPTION_ARG_STRING, &arg_cfgdir, N_("Use a different config directory"), "PATH"},
{"no-plugins", 'n', 0, G_OPTION_ARG_NONE, &arg_skip_plugins, N_("Don't auto load any plugins"), NULL},
{"plugindir", 'p', 0, G_OPTION_ARG_NONE, &arg_show_autoload, N_("Show plugin/script auto-load directory"), NULL},
{"configdir", 'u', 0, G_OPTION_ARG_NONE, &arg_show_config, N_("Show user config directory"), NULL},
{"url", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &arg_url, N_("Open an irc://server:port/channel?key URL"), "URL"},
{"command", 'c', 0, G_OPTION_ARG_STRING, &arg_command, N_("Execute command:"), "COMMAND"},
#ifdef USE_DBUS
{"existing", 'e', 0, G_OPTION_ARG_NONE, &arg_existing, N_("Open URL or execute command in an existing HexChat"), NULL},
#endif
{"minimize", 0, 0, G_OPTION_ARG_INT, &arg_minimize, N_("Begin minimized. Level 0=Normal 1=Iconified 2=Tray"), N_("level")},
{"version", 'v', 0, G_OPTION_ARG_NONE, &arg_show_version, N_("Show version information"), NULL},
{G_OPTION_REMAINING, '\0', 0, G_OPTION_ARG_STRING_ARRAY, &arg_urls, N_("Open an irc://server:port/channel?key URL"), "URL"},
{NULL}
};
#ifdef WIN32
static void
create_msg_dialog (gchar *title, gchar *message)
{
GtkWidget *dialog;
dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, "%s", message);
gtk_window_set_title (GTK_WINDOW (dialog), title);
/* On Win32 we automatically have the icon. If we try to load it explicitly, it will look ugly for some reason. */
#ifndef WIN32
pixmaps_init ();
gtk_window_set_icon (GTK_WINDOW (dialog), pix_hexchat);
#endif
gtk_dialog_run (GTK_DIALOG (dialog));
gtk_widget_destroy (dialog);
}
#endif
int
fe_args (int argc, char *argv[])
{
GError *error = NULL;
GOptionContext *context;
char *buffer;
#ifdef ENABLE_NLS
bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
textdomain (GETTEXT_PACKAGE);
#endif
context = g_option_context_new (NULL);
#ifdef WIN32
g_option_context_set_help_enabled (context, FALSE); /* disable stdout help as stdout is unavailable for subsystem:windows */
#endif
g_option_context_add_main_entries (context, gopt_entries, GETTEXT_PACKAGE);
g_option_context_add_group (context, gtk_get_option_group (FALSE));
g_option_context_parse (context, &argc, &argv, &error);
#ifdef WIN32
if (error) /* workaround for argv not being available when using subsystem:windows */
{
if (error->message) /* the error message contains argv so search for patterns in that */
{
if (strstr (error->message, "--help-all") != NULL)
{
buffer = g_strdup_printf (g_option_context_get_help (context, FALSE, NULL));
gtk_init (&argc, &argv);
create_msg_dialog ("Long Help", buffer);
g_free (buffer);
return 0;
}
else if (strstr (error->message, "--help") != NULL || strstr (error->message, "-?") != NULL)
{
buffer = g_strdup_printf (g_option_context_get_help (context, TRUE, NULL));
gtk_init (&argc, &argv);
create_msg_dialog ("Help", buffer);
g_free (buffer);
return 0;
}
else
{
buffer = g_strdup_printf ("%s\n", error->message);
gtk_init (&argc, &argv);
create_msg_dialog ("Error", buffer);
g_free (buffer);
return 1;
}
}
}
#else
if (error)
{
if (error->message)
printf ("%s\n", error->message);
return 1;
}
#endif
g_option_context_free (context);
if (arg_show_version)
{
buffer = g_strdup_printf ("%s %s", PACKAGE_NAME, PACKAGE_VERSION);
#ifdef WIN32
gtk_init (&argc, &argv);
create_msg_dialog ("Version Information", buffer);
#else
puts (buffer);
#endif
g_free (buffer);
return 0;
}
if (arg_show_autoload)
{
buffer = g_strdup_printf ("%s%caddons%c", get_xdir(), G_DIR_SEPARATOR, G_DIR_SEPARATOR);
#ifdef WIN32
gtk_init (&argc, &argv);
create_msg_dialog ("Plugin/Script Auto-load Directory", buffer);
#else
puts (buffer);
#endif
g_free (buffer);
return 0;
}
if (arg_show_config)
{
buffer = g_strdup_printf ("%s%c", get_xdir(), G_DIR_SEPARATOR);
#ifdef WIN32
gtk_init (&argc, &argv);
create_msg_dialog ("User Config Directory", buffer);
#else
puts (buffer);
#endif
g_free (buffer);
return 0;
}
#ifdef WIN32
/* this is mainly for irc:// URL handling. When windows calls us from */
/* I.E, it doesn't give an option of "Start in" directory, like short */
/* cuts can. So we have to set the current dir manually, to the path */
/* of the exe. */
{
char *tmp = strdup (argv[0]);
char *sl;
sl = strrchr (tmp, G_DIR_SEPARATOR);
if (sl)
{
*sl = 0;
chdir (tmp);
}
free (tmp);
}
#endif
gtk_init (&argc, &argv);
#ifdef HAVE_GTK_MAC
osx_app = g_object_new(GTKOSX_TYPE_APPLICATION, NULL);
#endif
return -1;
}
const char cursor_color_rc[] =
"style \"xc-ib-st\""
"{"
"GtkEntry::cursor-color=\"#%02x%02x%02x\""
"}"
"widget \"*.hexchat-inputbox\" style : application \"xc-ib-st\"";
GtkStyle *
create_input_style (GtkStyle *style)
{
char buf[256];
static int done_rc = FALSE;
pango_font_description_free (style->font_desc);
style->font_desc = pango_font_description_from_string (prefs.hex_text_font);
/* fall back */
if (pango_font_description_get_size (style->font_desc) == 0)
{
snprintf (buf, sizeof (buf), _("Failed to open font:\n\n%s"), prefs.hex_text_font);
fe_message (buf, FE_MSG_ERROR);
pango_font_description_free (style->font_desc);
style->font_desc = pango_font_description_from_string ("sans 11");
}
if (prefs.hex_gui_input_style && !done_rc)
{
done_rc = TRUE;
sprintf (buf, cursor_color_rc, (colors[COL_FG].red >> 8),
(colors[COL_FG].green >> 8), (colors[COL_FG].blue >> 8));
gtk_rc_parse_string (buf);
}
style->bg[GTK_STATE_NORMAL] = colors[COL_FG];
style->base[GTK_STATE_NORMAL] = colors[COL_BG];
style->text[GTK_STATE_NORMAL] = colors[COL_FG];
return style;
}
void
fe_init (void)
{
palette_load ();
key_init ();
pixmaps_init ();
#ifdef HAVE_GTK_MAC
gtkosx_application_set_dock_icon_pixbuf (osx_app, pix_hexchat);
#endif
channelwin_pix = pixmap_load_from_file (prefs.hex_text_background);
input_style = create_input_style (gtk_style_new ());
}
void
fe_main (void)
{
#ifdef HAVE_GTK_MAC
gtkosx_application_ready(osx_app);
#endif
gtk_main ();
/* sleep for 2 seconds so any QUIT messages are not lost. The */
/* GUI is closed at this point, so the user doesn't even know! */
if (prefs.wait_on_exit)
sleep (2);
}
void
fe_cleanup (void)
{
/* it's saved when pressing OK in setup.c */
/*palette_save ();*/
}
void
fe_exit (void)
{
gtk_main_quit ();
}
int
fe_timeout_add (int interval, void *callback, void *userdata)
{
return g_timeout_add (interval, (GSourceFunc) callback, userdata);
}
void
fe_timeout_remove (int tag)
{
g_source_remove (tag);
}
#ifdef WIN32
static void
log_handler (const gchar *log_domain,
GLogLevelFlags log_level,
const gchar *message,
gpointer unused_data)
{
session *sess;
/* if (getenv ("HEXCHAT_WARNING_IGNORE")) this gets ignored sometimes, so simply just disable all warnings */
return;
sess = find_dialog (serv_list->data, "(warnings)");
if (!sess)
sess = new_ircwindow (serv_list->data, "(warnings)", SESS_DIALOG, 0);
PrintTextf (sess, "%s\t%s\n", log_domain, message);
if (getenv ("HEXCHAT_WARNING_ABORT"))
abort ();
}
#endif
/* install tray stuff */
static int
fe_idle (gpointer data)
{
session *sess = sess_list->data;
plugin_add (sess, NULL, NULL, tray_plugin_init, tray_plugin_deinit, NULL, FALSE);
if (arg_minimize == 1)
gtk_window_iconify (GTK_WINDOW (sess->gui->window));
else if (arg_minimize == 2)
tray_toggle_visibility (FALSE);
return 0;
}
void
fe_new_window (session *sess, int focus)
{
int tab = FALSE;
if (sess->type == SESS_DIALOG)
{
if (prefs.hex_gui_tab_dialogs)
tab = TRUE;
} else
{
if (prefs.hex_gui_tab_chans)
tab = TRUE;
}
mg_changui_new (sess, NULL, tab, focus);
#ifdef WIN32
g_log_set_handler ("GLib", G_LOG_LEVEL_CRITICAL|G_LOG_LEVEL_WARNING, (GLogFunc)log_handler, 0);
g_log_set_handler ("GLib-GObject", G_LOG_LEVEL_CRITICAL|G_LOG_LEVEL_WARNING, (GLogFunc)log_handler, 0);
g_log_set_handler ("Gdk", G_LOG_LEVEL_CRITICAL|G_LOG_LEVEL_WARNING, (GLogFunc)log_handler, 0);
g_log_set_handler ("Gtk", G_LOG_LEVEL_CRITICAL|G_LOG_LEVEL_WARNING, (GLogFunc)log_handler, 0);
#endif
if (!sess_list->next)
g_idle_add (fe_idle, NULL);
}
void
fe_new_server (struct server *serv)
{
serv->gui = malloc (sizeof (struct server_gui));
memset (serv->gui, 0, sizeof (struct server_gui));
}
void
fe_message (char *msg, int flags)
{
GtkWidget *dialog;
int type = GTK_MESSAGE_WARNING;
if (flags & FE_MSG_ERROR)
type = GTK_MESSAGE_ERROR;
if (flags & FE_MSG_INFO)
type = GTK_MESSAGE_INFO;
dialog = gtk_message_dialog_new (GTK_WINDOW (parent_window), 0, type,
GTK_BUTTONS_OK, "%s", msg);
if (flags & FE_MSG_MARKUP)
gtk_message_dialog_set_markup (GTK_MESSAGE_DIALOG (dialog), msg);
g_signal_connect (G_OBJECT (dialog), "response",
G_CALLBACK (gtk_widget_destroy), 0);
gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
gtk_widget_show (dialog);
if (flags & FE_MSG_WAIT)
gtk_dialog_run (GTK_DIALOG (dialog));
}
void
fe_idle_add (void *func, void *data)
{
g_idle_add (func, data);
}
void
fe_input_remove (int tag)
{
g_source_remove (tag);
}
int
fe_input_add (int sok, int flags, void *func, void *data)
{
int tag, type = 0;
GIOChannel *channel;
#ifdef WIN32
if (flags & FIA_FD)
channel = g_io_channel_win32_new_fd (sok);
else
channel = g_io_channel_win32_new_socket (sok);
#else
channel = g_io_channel_unix_new (sok);
#endif
if (flags & FIA_READ)
type |= G_IO_IN | G_IO_HUP | G_IO_ERR;
if (flags & FIA_WRITE)
type |= G_IO_OUT | G_IO_ERR;
if (flags & FIA_EX)
type |= G_IO_PRI;
tag = g_io_add_watch (channel, type, (GIOFunc) func, data);
g_io_channel_unref (channel);
return tag;
}
void
fe_set_topic (session *sess, char *topic, char *stripped_topic)
{
if (!sess->gui->is_tab || sess == current_tab)
{
if (prefs.hex_text_stripcolor_topic)
{
gtk_entry_set_text (GTK_ENTRY (sess->gui->topic_entry), stripped_topic);
}
else
{
gtk_entry_set_text (GTK_ENTRY (sess->gui->topic_entry), topic);
}
mg_set_topic_tip (sess);
}
else
{
if (sess->res->topic_text)
{
free (sess->res->topic_text);
}
if (prefs.hex_text_stripcolor_topic)
{
sess->res->topic_text = strdup (stripped_topic);
}
else
{
sess->res->topic_text = strdup (topic);
}
}
}
void
fe_set_hilight (struct session *sess)
{
if (sess->gui->is_tab)
fe_set_tab_color (sess, 3); /* set tab to blue */
if (prefs.hex_input_flash_hilight && (!prefs.hex_away_omit_alerts || !sess->server->is_away))
fe_flash_window (sess); /* taskbar flash */
}
static void
fe_update_mode_entry (session *sess, GtkWidget *entry, char **text, char *new_text)
{
if (!sess->gui->is_tab || sess == current_tab)
{
if (sess->gui->flag_wid[0]) /* channel mode buttons enabled? */
gtk_entry_set_text (GTK_ENTRY (entry), new_text);
} else
{
if (sess->gui->is_tab)
{
if (*text)
free (*text);
*text = strdup (new_text);
}
}
}
void
fe_update_channel_key (struct session *sess)
{
fe_update_mode_entry (sess, sess->gui->key_entry,
&sess->res->key_text, sess->channelkey);
fe_set_title (sess);
}
void
fe_update_channel_limit (struct session *sess)
{
char tmp[16];
sprintf (tmp, "%d", sess->limit);
fe_update_mode_entry (sess, sess->gui->limit_entry,
&sess->res->limit_text, tmp);
fe_set_title (sess);
}
int
fe_is_chanwindow (struct server *serv)
{
if (!serv->gui->chanlist_window)
return 0;
return 1;
}
void
fe_notify_update (char *name)
{
if (!name)
notify_gui_update ();
}
void
fe_text_clear (struct session *sess, int lines)
{
gtk_xtext_clear (sess->res->buffer, lines);
}
void
fe_close_window (struct session *sess)
{
if (sess->gui->is_tab)
mg_tab_close (sess);
else
gtk_widget_destroy (sess->gui->window);
}
void
fe_progressbar_start (session *sess)
{
if (!sess->gui->is_tab || current_tab == sess)
/* if it's the focused tab, create it for real! */
mg_progressbar_create (sess->gui);
else
/* otherwise just remember to create on when it gets focused */
sess->res->c_graph = TRUE;
}
void
fe_progressbar_end (server *serv)
{
GSList *list = sess_list;
session *sess;
while (list) /* check all windows that use this server and *
* remove the connecting graph, if it has one. */
{
sess = list->data;
if (sess->server == serv)
{
if (sess->gui->bar)
mg_progressbar_destroy (sess->gui);
sess->res->c_graph = FALSE;
}
list = list->next;
}
}
void
fe_print_text (struct session *sess, char *text, time_t stamp,
gboolean no_activity)
{
PrintTextRaw (sess->res->buffer, (unsigned char *)text, prefs.hex_text_indent, stamp);
if (!no_activity && !sess->new_data && sess != current_tab &&
sess->gui->is_tab && !sess->nick_said)
{
sess->new_data = TRUE;
lastact_update (sess);
if (sess->msg_said)
fe_set_tab_color (sess, 2);
else
fe_set_tab_color (sess, 1);
}
}
void
fe_beep (session *sess)
{
#ifdef WIN32
if (!PlaySound ("Notification.IM", NULL, SND_ALIAS|SND_ASYNC))
{
/* This is really just a fallback attempt, may or may not work on new Windows releases, especially on x64.
* You should set up the "Instant Message Notification" system sound instead, supported on Vista and up.
*/
Beep (1000, 50);
}
#else
#ifdef USE_LIBCANBERRA
if (ca_con == NULL)
{
ca_context_create (&ca_con);
ca_context_change_props (ca_con,
CA_PROP_APPLICATION_ID, "hexchat",
CA_PROP_APPLICATION_NAME, DISPLAY_NAME,
CA_PROP_APPLICATION_ICON_NAME, "hexchat", NULL);
}
if (ca_context_play (ca_con, 0, CA_PROP_EVENT_ID, "message-new-instant", NULL) != 0)
#endif
gdk_beep ();
#endif
}
void
fe_lastlog (session *sess, session *lastlog_sess, char *sstr, gtk_xtext_search_flags flags)
{
GError *err = NULL;
xtext_buffer *buf, *lbuf;
buf = sess->res->buffer;
if (gtk_xtext_is_empty (buf))
{
PrintText (lastlog_sess, _("Search buffer is empty.\n"));
return;
}
lbuf = lastlog_sess->res->buffer;
if (flags & regexp)
{
GRegexCompileFlags gcf = (flags & case_match)? 0: G_REGEX_CASELESS;
lbuf->search_re = g_regex_new (sstr, gcf, 0, &err);
if (err)
{
PrintText (lastlog_sess, _(err->message));
g_error_free (err);
return;
}
}
else
{
if (flags & case_match)
{
lbuf->search_nee = g_strdup (sstr);
}
else
{
lbuf->search_nee = g_utf8_casefold (sstr, strlen (sstr));
}
lbuf->search_lnee = strlen (lbuf->search_nee);
}
lbuf->search_flags = flags;
lbuf->search_text = strdup (sstr);
gtk_xtext_lastlog (lbuf, buf);
}
void
fe_set_lag (server *serv, int lag)
{
GSList *list = sess_list;
session *sess;
gdouble per;
char lagtext[64];
char lagtip[128];
unsigned long nowtim;
if (lag == -1)
{
if (!serv->lag_sent)
return;
nowtim = make_ping_time ();
lag = (nowtim - serv->lag_sent) / 100000;
}
/* if there is no pong for >30s report the lag as +30s */
if (lag > 300 && serv->lag_sent)
lag=300;
per = (double)((double)lag / (double)10);
if (per > 1.0)
per = 1.0;
snprintf (lagtext, sizeof (lagtext) - 1, "%s%d.%ds",
serv->lag_sent ? "+" : "", lag / 10, lag % 10);
snprintf (lagtip, sizeof (lagtip) - 1, "Lag: %s%d.%d seconds",
serv->lag_sent ? "+" : "", lag / 10, lag % 10);
while (list)
{
sess = list->data;
if (sess->server == serv)
{
if (sess->res->lag_tip)
free (sess->res->lag_tip);
sess->res->lag_tip = strdup (lagtip);
if (!sess->gui->is_tab || current_tab == sess)
{
if (sess->gui->lagometer)
{
gtk_progress_bar_set_fraction ((GtkProgressBar *) sess->gui->lagometer, per);
gtk_widget_set_tooltip_text (gtk_widget_get_parent (sess->gui->lagometer), lagtip);
}
if (sess->gui->laginfo)
gtk_label_set_text ((GtkLabel *) sess->gui->laginfo, lagtext);
} else
{
sess->res->lag_value = per;
if (sess->res->lag_text)
free (sess->res->lag_text);
sess->res->lag_text = strdup (lagtext);
}
}
list = list->next;
}
}
void
fe_set_throttle (server *serv)
{
GSList *list = sess_list;
struct session *sess;
float per;
char tbuf[96];
char tip[160];
per = (float) serv->sendq_len / 1024.0;
if (per > 1.0)
per = 1.0;
while (list)
{
sess = list->data;
if (sess->server == serv)
{
snprintf (tbuf, sizeof (tbuf) - 1, _("%d bytes"), serv->sendq_len);
snprintf (tip, sizeof (tip) - 1, _("Network send queue: %d bytes"), serv->sendq_len);
if (sess->res->queue_tip)
free (sess->res->queue_tip);
sess->res->queue_tip = strdup (tip);
if (!sess->gui->is_tab || current_tab == sess)
{
if (sess->gui->throttlemeter)
{
gtk_progress_bar_set_fraction ((GtkProgressBar *) sess->gui->throttlemeter, per);
gtk_widget_set_tooltip_text (gtk_widget_get_parent (sess->gui->throttlemeter), tip);
}
if (sess->gui->throttleinfo)
gtk_label_set_text ((GtkLabel *) sess->gui->throttleinfo, tbuf);
} else
{
sess->res->queue_value = per;
if (sess->res->queue_text)
free (sess->res->queue_text);
sess->res->queue_text = strdup (tbuf);
}
}
list = list->next;
}
}
void
fe_ctrl_gui (session *sess, fe_gui_action action, int arg)
{
switch (action)
{
case FE_GUI_HIDE:
gtk_widget_hide (sess->gui->window); break;
case FE_GUI_SHOW:
gtk_widget_show (sess->gui->window);
gtk_window_present (GTK_WINDOW (sess->gui->window));
break;
case FE_GUI_FOCUS:
mg_bring_tofront_sess (sess); break;
case FE_GUI_FLASH:
fe_flash_window (sess); break;
case FE_GUI_COLOR:
fe_set_tab_color (sess, arg); break;
case FE_GUI_ICONIFY:
gtk_window_iconify (GTK_WINDOW (sess->gui->window)); break;
case FE_GUI_MENU:
menu_bar_toggle (); /* toggle menubar on/off */
break;
case FE_GUI_ATTACH:
mg_detach (sess, arg); /* arg: 0=toggle 1=detach 2=attach */
break;
case FE_GUI_APPLY:
setup_apply_real (TRUE, TRUE, TRUE);
}
}
static void
dcc_saveas_cb (struct DCC *dcc, char *file)
{
if (is_dcc (dcc))
{
if (dcc->dccstat == STAT_QUEUED)
{
if (file)
dcc_get_with_destfile (dcc, file);
else if (dcc->resume_sent == 0)
dcc_abort (dcc->serv->front_session, dcc);
}
}
}
void
fe_confirm (const char *message, void (*yesproc)(void *), void (*noproc)(void *), void *ud)
{
/* warning, assuming fe_confirm is used by DCC only! */
struct DCC *dcc = ud;
char *filepath;
if (dcc->file)
{
filepath = g_build_filename (prefs.hex_dcc_dir, dcc->file, NULL);
gtkutil_file_req (message, dcc_saveas_cb, ud, filepath, NULL,
FRF_WRITE|FRF_NOASKOVERWRITE|FRF_FILTERISINITIAL);
g_free (filepath);
}
}
int
fe_gui_info (session *sess, int info_type)
{
switch (info_type)
{
case 0: /* window status */
if (!gtk_widget_get_visible (GTK_WIDGET (sess->gui->window)))
{
return 2; /* hidden (iconified or systray) */
}
if (gtk_window_is_active (GTK_WINDOW (sess->gui->window)))
{
return 1; /* active/focused */
}
return 0; /* normal (no keyboard focus or behind a window) */
}
return -1;
}
void *
fe_gui_info_ptr (session *sess, int info_type)
{
switch (info_type)
{
case 0: /* native window pointer (for plugins) */
#ifdef WIN32
return gdk_win32_window_get_impl_hwnd (gtk_widget_get_window (sess->gui->window));
#else
return sess->gui->window;
#endif
break;
case 1: /* GtkWindow * (for plugins) */
return sess->gui->window;
}
return NULL;
}
char *
fe_get_inputbox_contents (session *sess)
{
/* not the current tab */
if (sess->res->input_text)
return sess->res->input_text;
/* current focused tab */
return SPELL_ENTRY_GET_TEXT (sess->gui->input_box);
}
int
fe_get_inputbox_cursor (session *sess)
{
/* not the current tab (we don't remember the cursor pos) */
if (sess->res->input_text)
return 0;
/* current focused tab */
return SPELL_ENTRY_GET_POS (sess->gui->input_box);
}
void
fe_set_inputbox_cursor (session *sess, int delta, int pos)
{
if (!sess->gui->is_tab || sess == current_tab)
{
if (delta)
pos += SPELL_ENTRY_GET_POS (sess->gui->input_box);
SPELL_ENTRY_SET_POS (sess->gui->input_box, pos);
} else
{
/* we don't support changing non-front tabs yet */
}
}
void
fe_set_inputbox_contents (session *sess, char *text)
{
if (!sess->gui->is_tab || sess == current_tab)
{
SPELL_ENTRY_SET_TEXT (sess->gui->input_box, text);
} else
{
if (sess->res->input_text)
free (sess->res->input_text);
sess->res->input_text = strdup (text);
}
}
static void
fe_open_url_inner (const char *url)
{
#ifdef WIN32
ShellExecute (0, "open", url, NULL, NULL, SW_SHOWNORMAL);
#elif defined __APPLE__
/* on Mac you can just 'open http://foo.bar/' */
gchar open[512];
g_snprintf (open, sizeof(open), "%s %s", g_find_program_in_path ("open"), url, NULL);
hexchat_exec (open);
#else
gtk_show_uri (NULL, url, GDK_CURRENT_TIME, NULL);
#endif
}
static void
fe_open_url_locale (const char *url)
{
int url_type = url_check_word (url);
char *uri;
/* gvfs likes file:// */
if (url_type == WORD_PATH)
{
#ifndef WIN32
uri = g_strconcat ("file://", url, NULL);
fe_open_url_inner (uri);
g_free (uri);
#else
fe_open_url_inner (url);
#endif
}
/* IPv6 addr. Add http:// */
else if (url_type == WORD_HOST6)
{
/* IPv6 addrs in urls should be enclosed in [ ] */
if (*url != '[')
uri = g_strdup_printf ("http://[%s]", url);
else
uri = g_strdup_printf ("http://%s", url);
fe_open_url_inner (uri);
g_free (uri);
}
/* the http:// part's missing, prepend it, otherwise it won't always work */
else if (strchr (url, ':') == NULL)
{
url = g_strdup_printf ("http://%s", url);
fe_open_url_inner (url);
g_free ((char *)url);
}
/* we have a sane URL, send it to the browser untouched */
else
{
fe_open_url_inner (url);
}
}
void
fe_open_url (const char *url)
{
char *loc;
if (prefs.utf8_locale)
{
fe_open_url_locale (url);
return;
}
/* the OS expects it in "locale" encoding. This makes it work on
unix systems that use ISO-8859-x and Win32. */
loc = g_locale_from_utf8 (url, -1, 0, 0, 0);
if (loc)
{
fe_open_url_locale (loc);
g_free (loc);
}
}
void
fe_server_event (server *serv, int type, int arg)
{
GSList *list = sess_list;
session *sess;
while (list)
{
sess = list->data;
if (sess->server == serv && (current_tab == sess || !sess->gui->is_tab))
{
session_gui *gui = sess->gui;
switch (type)
{
case FE_SE_CONNECTING: /* connecting in progress */
case FE_SE_RECONDELAY: /* reconnect delay begun */
/* enable Disconnect item */
gtk_widget_set_sensitive (gui->menu_item[MENU_ID_DISCONNECT], 1);
break;
case FE_SE_CONNECT:
/* enable Disconnect and Away menu items */
gtk_widget_set_sensitive (gui->menu_item[MENU_ID_AWAY], 1);
gtk_widget_set_sensitive (gui->menu_item[MENU_ID_DISCONNECT], 1);
break;
case FE_SE_LOGGEDIN: /* end of MOTD */
gtk_widget_set_sensitive (gui->menu_item[MENU_ID_JOIN], 1);
/* if number of auto-join channels is zero, open joind */
if (arg == 0)
joind_open (serv);
break;
case FE_SE_DISCONNECT:
/* disable Disconnect and Away menu items */
gtk_widget_set_sensitive (gui->menu_item[MENU_ID_AWAY], 0);
gtk_widget_set_sensitive (gui->menu_item[MENU_ID_DISCONNECT], 0);
gtk_widget_set_sensitive (gui->menu_item[MENU_ID_JOIN], 0);
/* close the join-dialog, if one exists */
joind_close (serv);
}
}
list = list->next;
}
}
void
fe_get_file (const char *title, char *initial,
void (*callback) (void *userdata, char *file), void *userdata,
int flags)
{
/* OK: Call callback once per file, then once more with file=NULL. */
/* CANCEL: Call callback once with file=NULL. */
gtkutil_file_req (title, callback, userdata, initial, NULL, flags | FRF_FILTERISINITIAL);
}
void
fe_open_chan_list (server *serv, char *filter, int do_refresh)
{
chanlist_opengui (serv, do_refresh);
}
(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 (FALSE);
}
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);
}
/* 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, 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, _("<none>"));
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;
}