
Xtext's transparency barely worked on windows, didn't work on any modern linux wm and used fake transparency. This uses gtk's built in window opacity that works on more systems and is real transparency. Text area only transparency may return with a transition to cairo, if it works on Windows.
1097 lines
25 KiB
C
1097 lines
25 KiB
C
/* 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 "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
|
|
|
|
/* === 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"},
|
|
#ifndef WIN32 /* uses DBUS */
|
|
{"command", 'c', 0, G_OPTION_ARG_STRING, &arg_command, N_("Execute command:"), "COMMAND"},
|
|
{"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);
|
|
|
|
return -1;
|
|
}
|
|
|
|
const char cursor_color_rc[] =
|
|
"style \"xc-ib-st\""
|
|
"{"
|
|
#ifdef USE_GTKSPELL
|
|
"GtkTextView::cursor-color=\"#%02x%02x%02x\""
|
|
#else
|
|
"GtkEntry::cursor-color=\"#%02x%02x%02x\""
|
|
#endif
|
|
"}"
|
|
"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 ();
|
|
|
|
channelwin_pix = pixmap_load_from_file (prefs.hex_text_background);
|
|
input_style = create_input_style (gtk_style_new ());
|
|
}
|
|
|
|
void
|
|
fe_main (void)
|
|
{
|
|
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)
|
|
{
|
|
PrintTextRaw (sess->res->buffer, (unsigned char *)text, prefs.hex_text_indent, stamp);
|
|
|
|
if (!sess->new_data && sess != current_tab &&
|
|
sess->gui->is_tab && !sess->nick_said && stamp == 0)
|
|
{
|
|
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);
|
|
add_tip (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);
|
|
add_tip (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);
|
|
}
|