/* 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 #include #include #include "fe-gtk.h" #ifdef WIN32 #include #include #else #include #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_XLIB #include #endif GdkPixmap *channelwin_pix; #ifdef USE_XLIB static void redraw_trans_xtexts (void) { GSList *list = sess_list; session *sess; int done_main = FALSE; while (list) { sess = list->data; if (GTK_XTEXT (sess->gui->xtext)->transparent) { if (!sess->gui->is_tab || !done_main) gtk_xtext_refresh (GTK_XTEXT (sess->gui->xtext), 1); if (sess->gui->is_tab) done_main = TRUE; } list = list->next; } } static GdkFilterReturn root_event_cb (GdkXEvent *xev, GdkEventProperty *event, gpointer data) { static Atom at = None; XEvent *xevent = (XEvent *)xev; if (xevent->type == PropertyNotify) { if (at == None) at = XInternAtom (xevent->xproperty.display, "_XROOTPMAP_ID", True); if (at == xevent->xproperty.atom) redraw_trans_xtexts (); } return GDK_FILTER_CONTINUE; } #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, 0, 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}, {NULL} }; 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); } 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 (DISPLAY_NAME " " PACKAGE_VERSION "\n"); gtk_init (&argc, &argv); create_msg_dialog ("Version Information", buffer); g_free (buffer); return 0; } if (arg_show_autoload) { buffer = g_strdup_printf ("%s" G_DIR_SEPARATOR_S "addons\n", get_xdir ()); gtk_init (&argc, &argv); create_msg_dialog ("Plugin/Script Auto-load Directory", buffer); g_free (buffer); return 0; } if (arg_show_config) { buffer = g_strdup_printf ("%s" G_DIR_SEPARATOR_S "\n", get_xdir ()); gtk_init (&argc, &argv); create_msg_dialog ("User Config Directory", buffer); 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 USE_XLIB gdk_window_set_events (gdk_get_default_root_window (), GDK_PROPERTY_CHANGE_MASK); gdk_window_add_filter (gdk_get_default_root_window (), (GdkFilterFunc)root_event_cb, NULL); #endif 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) { if (!prefs.hex_gui_focus_omitalerts || !fe_gui_info (sess, 0) == 1) { #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 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 (sess->gui->lagometer->parent, 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 (sess->gui->throttlemeter->parent, 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; if (dcc->file) gtkutil_file_req (message, dcc_saveas_cb, ud, dcc->file, NULL, FRF_WRITE|FRF_NOASKOVERWRITE); } int fe_gui_info (session *sess, int info_type) { switch (info_type) { case 0: /* window status */ #if GTK_CHECK_VERSION(2,20,0) if (!gtk_widget_get_visible (GTK_WIDGET (sess->gui->window))) #else if (!GTK_WIDGET_VISIBLE (GTK_WIDGET (sess->gui->window))) #endif 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 #if GTK_CHECK_VERSION(2,24,8) return gdk_win32_window_get_impl_hwnd (sess->gui->window->window); #else return GDK_WINDOW_HWND (sess->gui->window->window); #endif #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) { /* the http:// part's missing, prepend it, otherwise it won't always work */ if (strchr (url, ':') == NULL && url_check_word (url) != WORD_PATH) { 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); }