diff options
Diffstat (limited to 'src/fe-gtk')
32 files changed, 2221 insertions, 360 deletions
diff --git a/src/fe-gtk/Makefile.am b/src/fe-gtk/Makefile.am index 7db38096..da36ed57 100644 --- a/src/fe-gtk/Makefile.am +++ b/src/fe-gtk/Makefile.am @@ -10,8 +10,8 @@ EXTRA_DIST = \ about.h ascii.h banlist.h chanlist.h chanview.h chanview-tabs.c \ chanview-tree.c custom-list.h editlist.h fe-gtk.h fkeys.h gtkutil.h joind.h \ maingui.h menu.h mmx_cmod.S mmx_cmod.h notifygui.h palette.h pixmaps.h \ - plugin-tray.h plugingui.c plugingui.h rawlog.h search.h sexy-spell-entry.h \ - textgui.h urlgrab.h userlistgui.h xtext.h + plugin-tray.h plugingui.c plugingui.h rawlog.h search.h sexy-iso-codes.h \ + sexy-spell-entry.h sexy-marshal.h textgui.h urlgrab.h userlistgui.h xtext.h if USE_MMX mmx_cmod_S = mmx_cmod.S @@ -22,11 +22,12 @@ plugingui_c = plugingui.c endif if USE_LIBSEXY -sexy_spell_entry_c = sexy-spell-entry.c +sexy_spell = \ + sexy-iso-codes.c sexy-marshal.c sexy-spell-entry.c endif xchat_SOURCES = about.c ascii.c banlist.c chanlist.c chanview.c custom-list.c \ dccgui.c editlist.c fe-gtk.c fkeys.c gtkutil.c ignoregui.c joind.c menu.c \ maingui.c $(mmx_cmod_S) notifygui.c palette.c pixmaps.c plugin-tray.c $(plugingui_c) \ - rawlog.c search.c servlistgui.c setup.c $(sexy_spell_entry_c) textgui.c \ + rawlog.c search.c servlistgui.c setup.c $(sexy_spell) textgui.c \ urlgrab.c userlistgui.c xtext.c diff --git a/src/fe-gtk/about.c b/src/fe-gtk/about.c index 60700aec..dc1cbf08 100644 --- a/src/fe-gtk/about.c +++ b/src/fe-gtk/about.c @@ -39,6 +39,7 @@ #include "../common/xchat.h" #include "../common/util.h" +#include "../common/xchatc.h" #include "palette.h" #include "pixmaps.h" #include "gtkutil.h" @@ -69,7 +70,7 @@ menu_about (GtkWidget * wid, gpointer sess) "(C) 1998-2005 Peter Zelezny", author, buf, 0)); } -#else +#endif static GtkWidget *about = 0; @@ -95,7 +96,7 @@ menu_about (GtkWidget * wid, gpointer sess) } about = gtk_dialog_new (); - gtk_window_set_position (GTK_WINDOW (about), GTK_WIN_POS_CENTER); + gtk_window_set_position (GTK_WINDOW (about), GTK_WIN_POS_CENTER_ON_PARENT); gtk_window_set_resizable (GTK_WINDOW (about), FALSE); gtk_window_set_title (GTK_WINDOW (about), _("About "DISPLAY_NAME)); if (parent_window) @@ -113,36 +114,47 @@ menu_about (GtkWidget * wid, gpointer sess) gtk_container_add (GTK_CONTAINER (vbox), label); g_get_charset (&locale); (snprintf) (buf, sizeof (buf), - "<span size=\"x-large\"><b>"DISPLAY_NAME" "PACKAGE_VERSION"</b></span>\n\n" - "%s\n\n" + "<span size=\"x-large\"><b>"DISPLAY_NAME" "PACKAGE_VERSION"</b></span>\n" #ifdef WIN32 - /* leave this message to avoid time wasting bug reports! */ - "This version is unofficial and comes with no support.\n\n" -#endif - "%s\n" + "%s%s%s" + "\n<b>XChat Release</b>: "XCHAT_RELEASE"\n\n" + "<b>OS</b>: %s\n" "<b>Charset</b>: %s " -#ifdef WIN32 "<b>GTK+</b>: %i.%i.%i\n" + "<b>Compiled</b>: "__DATE__"\n" + "<b>Portable Mode</b>: %s\n" + "<b>Build Type</b>: x%d\n\n" + "<small>This version is unofficial and comes with no support.\n" + "\302\251 1998-2010 Peter \305\275elezn\303\275 <zed@xchat.org>" + /* "\n<a href=\"http://code.google.com/p/xchat-wdk/\">http://code.google.com/p/xchat-wdk/</a>" this is broken in gtk ATM */ + "</small>", + (strcmp (prefs.gui_license, "") ? "<span size=\"x-large\">" : ""), + (strcmp (prefs.gui_license, "") ? prefs.gui_license : ""), + (strcmp (prefs.gui_license, "") ? "</span>\n" : ""), + get_cpu_str (), + locale, + gtk_major_version, + gtk_minor_version, + gtk_micro_version, + (portable_mode () ? "Yes" : "No"), + get_cpu_arch () #else + "\n%s\n\n" + "%s\n" + "<b>Charset</b>: %s " "<b>Renderer</b>: %s\n" -#endif "<b>Compiled</b>: "__DATE__"\n\n" "<small>\302\251 1998-2010 Peter \305\275elezn\303\275 <zed@xchat.org></small>", - _("A multiplatform IRC Client"), - get_cpu_str(), - locale, -#ifdef WIN32 - gtk_major_version, - gtk_minor_version, - gtk_micro_version -#else + _("A multiplatform IRC Client"), + get_cpu_str (), + locale, #ifdef USE_XFT - "Xft" + "Xft" #else - "Pango" + "Pango" #endif #endif - ); + ); gtk_label_set_markup (GTK_LABEL (label), buf); gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_CENTER); @@ -158,4 +170,3 @@ menu_about (GtkWidget * wid, gpointer sess) gtk_widget_show_all (about); } -#endif diff --git a/src/fe-gtk/banlist.c b/src/fe-gtk/banlist.c index afaa7eb4..fecb62a5 100644 --- a/src/fe-gtk/banlist.c +++ b/src/fe-gtk/banlist.c @@ -19,10 +19,13 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <unistd.h> #include <fcntl.h> #include <time.h> +#ifndef WIN32 +#include <unistd.h> +#endif + #include "fe-gtk.h" #include <gtk/gtkhbox.h> @@ -134,11 +137,8 @@ banlist_do_refresh (struct session *sess) gtk_list_store_clear (store); handle_command (sess, "ban", FALSE); -#ifdef WIN32 - if (0) -#else + if (supports_exempt (sess->server)) -#endif { snprintf (tbuf, sizeof (tbuf), "quote mode %s +e", sess->channel); handle_command (sess, tbuf, FALSE); diff --git a/src/fe-gtk/chanlist.c b/src/fe-gtk/chanlist.c index 2f65b518..4dca935b 100644 --- a/src/fe-gtk/chanlist.c +++ b/src/fe-gtk/chanlist.c @@ -19,10 +19,13 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <unistd.h> #include <fcntl.h> #include <time.h> +#ifndef WIN32 +#include <unistd.h> +#endif + #include "fe-gtk.h" #include <gtk/gtkalignment.h> diff --git a/src/fe-gtk/chanview-tabs.c b/src/fe-gtk/chanview-tabs.c index 8e3da8e6..f99e61f1 100644 --- a/src/fe-gtk/chanview-tabs.c +++ b/src/fe-gtk/chanview-tabs.c @@ -144,8 +144,8 @@ tab_scroll_left_up_clicked (GtkWidget *widget, chanview *cv) for (i = adj->value; ((i > new_value) && (tab_left_is_moving)); i -= 0.1) { gtk_adjustment_set_value (adj, i); - while (g_main_pending ()) - g_main_iteration (TRUE); + while (g_main_context_pending (NULL)) + g_main_context_iteration (NULL, TRUE); } gtk_adjustment_set_value (adj, new_value); @@ -191,8 +191,8 @@ tab_scroll_right_down_clicked (GtkWidget *widget, chanview *cv) for (i = adj->value; ((i < new_value) && (tab_right_is_moving)); i += 0.1) { gtk_adjustment_set_value (adj, i); - while (g_main_pending ()) - g_main_iteration (TRUE); + while (g_main_context_pending (NULL)) + g_main_context_iteration (NULL, TRUE); } gtk_adjustment_set_value (adj, new_value); diff --git a/src/fe-gtk/editlist.c b/src/fe-gtk/editlist.c index 5af67e32..d374d064 100644 --- a/src/fe-gtk/editlist.c +++ b/src/fe-gtk/editlist.c @@ -19,10 +19,14 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <unistd.h> #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> + +#ifndef WIN32 +#include <unistd.h> +#endif + #include "fe-gtk.h" #include <gtk/gtkstock.h> diff --git a/src/fe-gtk/fe-gtk.c b/src/fe-gtk/fe-gtk.c index 5efcaeec..88a1138c 100644 --- a/src/fe-gtk/fe-gtk.c +++ b/src/fe-gtk/fe-gtk.c @@ -19,7 +19,6 @@ #include <stdio.h> #include <string.h> #include <stdlib.h> -#include <unistd.h> #include "fe-gtk.h" @@ -32,6 +31,12 @@ #include <gtk/gtkmessagedialog.h> #include <gtk/gtkversion.h> +#ifdef WIN32 +#include <gdk/gdkwin32.h> +#else +#include <unistd.h> +#endif + #include "../common/xchat.h" #include "../common/fe.h" #include "../common/util.h" @@ -39,6 +44,7 @@ #include "../common/cfgfiles.h" #include "../common/xchatc.h" #include "../common/plugin.h" +#include "../common/server.h" #include "gtkutil.h" #include "maingui.h" #include "pixmaps.h" @@ -136,11 +142,26 @@ static const GOptionEntry gopt_entries[] = {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, message); + gtk_window_set_title (GTK_WINDOW (dialog), title); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); +} +#endif + int fe_args (int argc, char *argv[]) { GError *error = NULL; GOptionContext *context; +#ifdef WIN32 + char *buffer[2048]; +#endif #ifdef ENABLE_NLS bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR); @@ -149,22 +170,67 @@ fe_args (int argc, char *argv[]) #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) + { + if (snprintf (buffer, 2048, g_option_context_get_help (context, FALSE, NULL))) + { + gtk_init (&argc, &argv); + create_msg_dialog ("Long Help", buffer); + } + return 0; + } else if (strstr (error->message, "--help") != NULL || strstr (error->message, "-?") != NULL) + { + if (snprintf (buffer, 2048, g_option_context_get_help (context, TRUE, NULL))) + { + gtk_init (&argc, &argv); + create_msg_dialog ("Help", buffer); + } + return 0; + } else + { + if (snprintf (buffer, 2048, "%s\n", error->message)) + { + gtk_init (&argc, &argv); + create_msg_dialog ("Error", 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) { +#ifdef WIN32 + if (snprintf (buffer, 2048, DISPLAY_NAME" "PACKAGE_VERSION"\n")) + { + gtk_init (&argc, &argv); + create_msg_dialog ("Version Information", buffer); + } +#else printf (PACKAGE_TARNAME" "PACKAGE_VERSION"\n"); +#endif return 0; } @@ -177,7 +243,18 @@ fe_args (int argc, char *argv[]) if (sl) { *sl = 0; - printf ("%s\\plugins\n", exe); + if (snprintf (buffer, 2048, "%s\\plugins\n", exe)) + { + gtk_init (&argc, &argv); + create_msg_dialog ("Plugin Auto-load Directory", buffer); + } + } else + { + if (snprintf (buffer, 2048, ".\\plugins\n")) + { + gtk_init (&argc, &argv); + create_msg_dialog ("Plugin Auto-load Directory", buffer); + } } #else printf ("%s\n", XCHATLIBDIR"/plugins"); @@ -187,7 +264,15 @@ fe_args (int argc, char *argv[]) if (arg_show_config) { +#ifdef WIN32 + if (snprintf (buffer, 2048, "%s\n", get_xdir_fs ())) + { + gtk_init (&argc, &argv); + create_msg_dialog ("User Config Directory", buffer); + } +#else printf ("%s\n", get_xdir_fs ()); +#endif return 0; } @@ -330,7 +415,7 @@ log_handler (const gchar *log_domain, { session *sess; - if (getenv ("XCHAT_WARNING_IGNORE")) + /* if (getenv ("XCHAT_WARNING_IGNORE")) this gets ignored sometimes, so simply just disable all warnings */ return; sess = find_dialog (serv_list->data, "(warnings)"); @@ -845,7 +930,11 @@ fe_gui_info_ptr (session *sess, int 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 diff --git a/src/fe-gtk/fe-gtk.h b/src/fe-gtk/fe-gtk.h index 12516259..8fffb3cc 100644 --- a/src/fe-gtk/fe-gtk.h +++ b/src/fe-gtk/fe-gtk.h @@ -1,13 +1,6 @@ #include "../../config.h" -#ifdef WIN32 -/* If you're compiling this for Windows, your release is un-official - * and not condoned. Please don't use the XChat name. Make up your - * own name! */ -#define DISPLAY_NAME "XChat-Unofficial" -#else -#define DISPLAY_NAME "XChat" -#endif +#define DISPLAY_NAME "XChat-WDK" #ifndef WIN32 #include <sys/types.h> diff --git a/src/fe-gtk/fkeys.c b/src/fe-gtk/fkeys.c index 014b5cc3..b9690731 100644 --- a/src/fe-gtk/fkeys.c +++ b/src/fe-gtk/fkeys.c @@ -20,11 +20,14 @@ #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> -#include <unistd.h> #include <string.h> #include <fcntl.h> #include <ctype.h> +#ifndef WIN32 +#include <unistd.h> +#endif + #include "fe-gtk.h" #include <gtk/gtklabel.h> @@ -315,7 +318,7 @@ key_handle_key_press (GtkWidget *wid, GdkEventKey *evt, session *sess) key_action_tab_clean (); break; -#if defined(USE_GTKSPELL) && !defined(WIN32) +#if defined(USE_GTKSPELL)/* && !defined(WIN32) */ /* gtktextview has no 'activate' event, so we trap ENTER here */ case GDK_Return: case GDK_KP_Enter: diff --git a/src/fe-gtk/gtkutil.c b/src/fe-gtk/gtkutil.c index 63ab491b..fe4e1737 100644 --- a/src/fe-gtk/gtkutil.c +++ b/src/fe-gtk/gtkutil.c @@ -22,8 +22,8 @@ #include <stdarg.h> #include <sys/types.h> #include <sys/stat.h> -#include <unistd.h> #include <fcntl.h> + #include "fe-gtk.h" #include <gtk/gtkbutton.h> @@ -52,6 +52,13 @@ #include "gtkutil.h" #include "pixmaps.h" +#ifdef WIN32 +#include "../common/fe.h" +#include "../common/thread.h" +#else +#include <unistd.h> +#endif + /* gtkutil.c, just some gtk wrappers */ extern void path_part (char *file, char *path, int pathlen); @@ -63,6 +70,13 @@ struct file_req void *userdata; filereqcallback callback; int flags; /* FRF_* flags */ + +#ifdef WIN32 + int multiple; + thread *th; + char *title; /* native locale */ + char *filter; +#endif }; static char last_dir[256] = ""; @@ -164,6 +178,190 @@ gtkutil_file_req_response (GtkWidget *dialog, gint res, struct file_req *freq) } } +#ifdef WIN32 +static int +win32_openfile (char *file_buf, int file_buf_len, char *title_text, char *filter, + int multiple) +{ + OPENFILENAME o; + + memset (&o, 0, sizeof (o)); + + o.lStructSize = sizeof (o); + o.lpstrFilter = filter; + o.lpstrFile = file_buf; + o.nMaxFile = file_buf_len; + o.lpstrTitle = title_text; + o.Flags = 0x02000000 | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | + OFN_NOCHANGEDIR | OFN_EXPLORER | OFN_LONGNAMES | OFN_NONETWORKBUTTON; + if (multiple) + { + o.Flags |= OFN_ALLOWMULTISELECT; + } + + return GetOpenFileName (&o); +} + +static int +win32_savefile (char *file_buf, int file_buf_len, char *title_text, char *filter, + int multiple) +{ + /* we need the filter to get the default filename. it is from fe-gtk.c (fe_confirm); + * but that filter is actually the whole filename, so apply an empty filter and all good. + * in win32_thread2 we copy the filter ( = the filename) after the last dir into our + * LPTSTR file buffer to make it actually work. the docs for this amazingly retard api: + * + * http://msdn.microsoft.com/en-us/library/ms646839%28VS.85%29.aspx + */ + + OPENFILENAME o; + + memset (&o, 0, sizeof (o)); + + o.lStructSize = sizeof (o); + o.lpstrFilter = "All files\0*.*\0\0"; + o.lpstrFile = file_buf; + o.nMaxFile = file_buf_len; + o.lpstrTitle = title_text; + o.Flags = 0x02000000 | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | + OFN_NOCHANGEDIR | OFN_EXPLORER | OFN_LONGNAMES | OFN_NONETWORKBUTTON; + if (multiple) + { + o.Flags |= OFN_ALLOWMULTISELECT; + } + + return GetSaveFileName (&o); +} + +static void * +win32_thread (struct file_req *freq) +{ + char buf[1024 + 32]; + char file[1024]; + + memset (file, 0, sizeof (file)); + safe_strcpy (file, last_dir, sizeof (file)); + + if (win32_openfile (file, sizeof (file), freq->title, freq->filter, freq->multiple)) + { + if (freq->multiple) + { + char *f = file; + + if (f[strlen (f) + 1] == 0) /* only selected one file */ + { + snprintf (buf, sizeof (buf), "1\n%s\n", file); + write (freq->th->pipe_fd[1], buf, strlen (buf)); + } else + { + f += strlen (f) + 1; /* skip first, it's only the dir */ + while (f[0]) + { + snprintf (buf, sizeof (buf), "1\n%s\\%s\n", /*dir!*/file, f); + write (freq->th->pipe_fd[1], buf, strlen (buf)); + f += strlen (f) + 1; + } + } + + } else + { + snprintf (buf, sizeof (buf), "1\n%s\n", file); + write (freq->th->pipe_fd[1], buf, strlen (buf)); + } + } + + write (freq->th->pipe_fd[1], "0\n", 2); + Sleep (2000); + + return NULL; +} + +static void * +win32_thread2 (struct file_req *freq) +{ + char buf[1024 + 32]; + char file[1024]; + + memset (file, 0, sizeof (file)); + safe_strcpy (file, last_dir, sizeof (file)); + safe_strcpy (file, freq->filter, sizeof (file)); + + if (win32_savefile (file, sizeof (file), freq->title, NULL, freq->multiple)) + { + if (freq->multiple) + { + char *f = file; + + if (f[strlen (f) + 1] == 0) /* only selected one file */ + { + snprintf (buf, sizeof (buf), "1\n%s\n", file); + write (freq->th->pipe_fd[1], buf, strlen (buf)); + } else + { + f += strlen (f) + 1; /* skip first, it's only the dir */ + while (f[0]) + { + snprintf (buf, sizeof (buf), "1\n%s\\%s\n", /*dir!*/file, f); + write (freq->th->pipe_fd[1], buf, strlen (buf)); + f += strlen (f) + 1; + } + } + + } else + { + snprintf (buf, sizeof (buf), "1\n%s\n", file); + write (freq->th->pipe_fd[1], buf, strlen (buf)); + } + } + + write (freq->th->pipe_fd[1], "0\n", 2); + Sleep (2000); + + return NULL; +} + +static gboolean +win32_close_pipe (int fd) +{ + close (fd); + return 0; +} + +static gboolean +win32_read_thread (GIOChannel *source, GIOCondition cond, struct file_req *freq) +{ + char buf[512]; + char *file; + + waitline2 (source, buf, sizeof buf); + + switch (buf[0]) + { + case '0': /* filedialog has closed */ + freq->callback (freq->userdata, NULL); + break; + + case '1': /* got a filename! */ + waitline2 (source, buf, sizeof buf); + file = g_filename_to_utf8 (buf, -1, 0, 0, 0); + freq->callback (freq->userdata, file); + g_free (file); + return TRUE; + } + + /* it doesn't work to close them here, because of the weird + way giowin32 works. We must _return_ before closing them */ + g_timeout_add(3000, (GSourceFunc)win32_close_pipe, freq->th->pipe_fd[0]); + g_timeout_add(2000, (GSourceFunc)win32_close_pipe, freq->th->pipe_fd[1]); + + g_free (freq->title); + free (freq->th); + free (freq); + + return FALSE; +} +#endif + void gtkutil_file_req (const char *title, void *callback, void *userdata, char *filter, int flags) @@ -172,6 +370,58 @@ gtkutil_file_req (const char *title, void *callback, void *userdata, char *filte GtkWidget *dialog; extern char *get_xdir_fs (void); +#ifdef WIN32 + if (!(flags & FRF_WRITE)) + { + freq = malloc (sizeof (struct file_req)); + freq->th = thread_new (); + freq->flags = 0; + freq->multiple = (flags & FRF_MULTIPLE); + freq->callback = callback; + freq->userdata = userdata; + freq->title = g_locale_from_utf8 (title, -1, 0, 0, 0); + if (!filter) + { + freq->filter = "All files\0*.*\0" + "Executables\0*.exe\0" + "ZIP files\0*.zip\0\0"; + } + else + { + freq->filter = filter; + } + + thread_start (freq->th, win32_thread, freq); + fe_input_add (freq->th->pipe_fd[0], FIA_FD|FIA_READ, win32_read_thread, freq); + + return; + + } + + else { + freq = malloc (sizeof (struct file_req)); + freq->th = thread_new (); + freq->flags = 0; + freq->multiple = (flags & FRF_MULTIPLE); + freq->callback = callback; + freq->userdata = userdata; + freq->title = g_locale_from_utf8 (title, -1, 0, 0, 0); + if (!filter) + { + freq->filter = "All files\0*.*\0\0"; + } + else + { + freq->filter = filter; + } + + thread_start (freq->th, win32_thread2, freq); + fe_input_add (freq->th->pipe_fd[0], FIA_FD|FIA_READ, win32_read_thread, freq); + + return; + } +#endif + if (flags & FRF_WRITE) { dialog = gtk_file_chooser_dialog_new (title, NULL, diff --git a/src/fe-gtk/joind.c b/src/fe-gtk/joind.c index ee5c56d1..e645f0bc 100644 --- a/src/fe-gtk/joind.c +++ b/src/fe-gtk/joind.c @@ -9,10 +9,13 @@ #include <sys/types.h> #include <sys/stat.h> -#include <unistd.h> #include <string.h> #include <stdio.h> +#ifndef WIN32 +#include <unistd.h> +#endif + #include <gtk/gtkbbox.h> #include <gtk/gtkbutton.h> #include <gtk/gtkdialog.h> diff --git a/src/fe-gtk/maingui.c b/src/fe-gtk/maingui.c index 8e6873a2..0b7f7c43 100644 --- a/src/fe-gtk/maingui.c +++ b/src/fe-gtk/maingui.c @@ -53,6 +53,8 @@ #include "../common/plugin.h" #include "../common/modes.h" #include "../common/url.h" +#include "../common/util.h" + #include "fe-gtk.h" #include "banlist.h" #include "gtkutil.h" @@ -214,60 +216,10 @@ mg_create_tab_colors (void) away_list = mg_attr_list_create (&colors[COL_AWAY], FALSE); } -#ifdef WIN32 -#define WINVER 0x0501 /* needed for vc6? */ -#include <windows.h> -#include <gdk/gdkwin32.h> - -/* Flash the taskbar button on Windows when there's a highlight event. */ - -static void -flash_window (GtkWidget *win) -{ - FLASHWINFO fi; - static HMODULE user = NULL; - static BOOL (*flash) (PFLASHWINFO) = NULL; - - if (!user) - { - user = GetModuleHandleA ("USER32"); - if (!user) - return; /* this should never fail */ - } - - if (!flash) - { - flash = (void *)GetProcAddress (user, "FlashWindowEx"); - if (!flash) - return; /* this fails on NT4.0 and Win95 */ - } - - fi.cbSize = sizeof (fi); - fi.hwnd = GDK_WINDOW_HWND (win->window); - fi.dwFlags = FLASHW_ALL | FLASHW_TIMERNOFG; - fi.uCount = 0; - fi.dwTimeout = 500; - flash (&fi); - /*FlashWindowEx (&fi);*/ -} -#else - -#ifdef USE_XLIB -#include <gdk/gdkx.h> - static void set_window_urgency (GtkWidget *win, gboolean set) { - XWMHints *hints; - - hints = XGetWMHints(GDK_WINDOW_XDISPLAY(win->window), GDK_WINDOW_XWINDOW(win->window)); - if (set) - hints->flags |= XUrgencyHint; - else - hints->flags &= ~XUrgencyHint; - XSetWMHints(GDK_WINDOW_XDISPLAY(win->window), - GDK_WINDOW_XWINDOW(win->window), hints); - XFree(hints); + gtk_window_set_urgency_hint (GTK_WINDOW (win), set); } static void @@ -281,18 +233,14 @@ unflash_window (GtkWidget *win) { set_window_urgency (win, FALSE); } -#endif -#endif /* flash the taskbar button */ void fe_flash_window (session *sess) { -#if defined(WIN32) || defined(USE_XLIB) if (fe_gui_info (sess, 0) != 1) /* only do it if not focused */ flash_window (sess->gui->window); -#endif } /* set a tab plain, red, light-red, or blue */ @@ -530,10 +478,22 @@ fe_set_title (session *sess) break; default: def: - gtk_window_set_title (GTK_WINDOW (sess->gui->window), DISPLAY_NAME); + snprintf (tbuf, sizeof (tbuf), DISPLAY_NAME); + if (strcmp (prefs.gui_license, "")) /* zero means gui_license is empty */ + { + strcat (tbuf, " - "); + strcat (tbuf, prefs.gui_license); + } + gtk_window_set_title (GTK_WINDOW (sess->gui->window), tbuf); return; } + if (strcmp (prefs.gui_license, "")) /* zero means gui_license is empty */ + { + strcat (tbuf, " - "); + strcat (tbuf, prefs.gui_license); + } + gtk_window_set_title (GTK_WINDOW (sess->gui->window), tbuf); } @@ -840,6 +800,9 @@ mg_userlist_showhide (session *sess, int show) { session_gui *gui = sess->gui; int handle_size; + int right_size; + + right_size = MAX (prefs.gui_pane_right_size, prefs.gui_pane_right_size_min); if (show) { @@ -847,7 +810,7 @@ mg_userlist_showhide (session *sess, int show) gui->ul_hidden = 0; gtk_widget_style_get (GTK_WIDGET (gui->hpane_right), "handle-size", &handle_size, NULL); - gtk_paned_set_position (GTK_PANED (gui->hpane_right), GTK_WIDGET (gui->hpane_right)->allocation.width - (prefs.gui_pane_right_size + handle_size)); + gtk_paned_set_position (GTK_PANED (gui->hpane_right), GTK_WIDGET (gui->hpane_right)->allocation.width - (right_size + handle_size)); } else { @@ -1335,7 +1298,7 @@ mg_open_quit_dialog (gboolean minimize_button) gtk_button_box_set_layout (GTK_BUTTON_BOX (dialog_action_area1), GTK_BUTTONBOX_END); - if (minimize_button) + if (minimize_button && !xtray_mode ()) { button = gtk_button_new_with_mnemonic (_("_Minimize to Tray")); gtk_widget_show (button); @@ -1633,7 +1596,12 @@ mg_create_alertmenu (session *sess, GtkWidget *menu) submenu = menu_quick_sub (_("_Extra Alerts"), menu, NULL, XCMENU_MNEMONIC, -1); mg_perchan_menu_item (_("Beep on _Message"), submenu, &sess->alert_beep, prefs.input_beep_chans); - mg_perchan_menu_item (_("Blink Tray _Icon"), submenu, &sess->alert_tray, prefs.input_tray_chans); + + if (!xtray_mode ()) /*disable this context menu item when xtray is loaded */ + { + mg_perchan_menu_item (_("Blink Tray _Icon"), submenu, &sess->alert_tray, prefs.input_tray_chans); + } + mg_perchan_menu_item (_("Blink Task _Bar"), submenu, &sess->alert_taskbar, prefs.input_flash_chans); } @@ -2883,7 +2851,7 @@ mg_create_entry (session *sess, GtkWidget *box) #else gui->input_box = entry = gtk_entry_new (); #endif - gtk_entry_set_max_length (GTK_ENTRY (gui->input_box), 2048); + 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); @@ -2973,11 +2941,7 @@ mg_tabwin_focus_cb (GtkWindow * win, GdkEventFocus *event, gpointer userdata) gtk_xtext_check_marker_visibility (GTK_XTEXT (current_sess->gui->xtext)); plugin_emit_dummy_print (current_sess, "Focus Window"); } -#ifndef WIN32 -#ifdef USE_XLIB unflash_window (GTK_WIDGET (win)); -#endif -#endif return FALSE; } @@ -2988,11 +2952,7 @@ mg_topwin_focus_cb (GtkWindow * win, GdkEventFocus *event, session *sess) if (!sess->server->server_session) sess->server->server_session = sess; gtk_xtext_check_marker_visibility(GTK_XTEXT (current_sess->gui->xtext)); -#ifndef WIN32 -#ifdef USE_XLIB unflash_window (GTK_WIDGET (win)); -#endif -#endif plugin_emit_dummy_print (sess, "Focus Window"); return FALSE; } diff --git a/src/fe-gtk/makefile.mak b/src/fe-gtk/makefile.mak new file mode 100644 index 00000000..27922405 --- /dev/null +++ b/src/fe-gtk/makefile.mak @@ -0,0 +1,62 @@ +include "..\makeinc.mak" + +FEGTK_OBJECTS = \ +about.obj \ +ascii.obj \ +banlist.obj \ +chanlist.obj \ +chanview.obj \ +custom-list.obj \ +dccgui.obj \ +editlist.obj \ +fe-gtk.obj \ +fkeys.obj \ +gtkutil.obj \ +ignoregui.obj \ +joind.obj \ +maingui.obj \ +menu.obj \ +notifygui.obj \ +palette.obj \ +pixmaps.obj \ +plugingui.obj \ +plugin-tray.obj \ +rawlog.obj \ +search.obj \ +servlistgui.obj \ +setup.obj \ +sexy-iso-codes.obj \ +sexy-marshal.obj \ +sexy-spell-entry.obj \ +textgui.obj \ +urlgrab.obj \ +userlistgui.obj \ +xtext.obj + +!ifdef X64 +MACHINE_FLAG = /MACHINE:X64 +!else +MACHINE_FLAG = /MACHINE:X86 +!endif + +COMLIB = ..\common\xchatcommon.lib +PROG = xchat.exe + +all: $(PROG) + +.c.obj:: + $(CC) $(CFLAGS) -I..\..\plugins $(GLIB) $(GTK) $< + +$(PROG): $(FEGTK_OBJECTS) $(COMLIB) xchat-icon.obj + $(LINK) /out:$(PROG) /entry:mainCRTStartup $(LDFLAGS) $(LIBS) $(FEGTK_OBJECTS) $(COMLIB) xchat-icon.obj + +xchat.res: xchat.rc ../../xchat.ico + rc /nologo /r xchat.rc + +xchat-icon.obj: xchat.res + cvtres /nologo $(MACHINE_FLAG) /OUT:xchat-icon.obj xchat.res + +clean: + @del *.obj + @del $(PROG) + @del xchat.res diff --git a/src/fe-gtk/menu.c b/src/fe-gtk/menu.c index d04be222..4760ba93 100644 --- a/src/fe-gtk/menu.c +++ b/src/fe-gtk/menu.c @@ -20,10 +20,11 @@ #include <stdlib.h> #include <fcntl.h> #include <string.h> -#include <unistd.h> #ifdef WIN32 #include <windows.h> +#else +#include <unistd.h> #endif #include "fe-gtk.h" @@ -1122,6 +1123,23 @@ usermenu_update (void) } } +#if 0 +static void +menu_saveconf (void) +{ + session *sess = current_sess; + + if (save_config ()) + { + PrintText (sess, "Settings have been saved successfully.\n"); + } + else + { + PrintText (sess, "Error saving settings.\n"); + } +} +#endif + static void menu_newserver_window (GtkWidget * wid, gpointer none) { @@ -1198,12 +1216,53 @@ menu_search () } static void +menu_search_next () +{ + GtkXText *xtext = GTK_XTEXT (current_sess->gui->xtext); + xtext_buffer *buf = xtext->buffer; + + if (!gtk_xtext_search (xtext, buf->search_text, + (buf->search_flags & (case_match | follow | regexp)), NULL)) + { + fe_message (_("Search hit end, not found."), FE_MSG_ERROR); + } +} + +static void +menu_search_prev () +{ + GtkXText *xtext = GTK_XTEXT (current_sess->gui->xtext); + xtext_buffer *buf = xtext->buffer; + + if (!gtk_xtext_search(xtext, buf->search_text, + (buf->search_flags & (case_match | follow | regexp) | backward), NULL)) + { + fe_message (_("Search hit end, not found."), FE_MSG_ERROR); + } +} + +static void +menu_search_reset () +{ + GtkXText *xtext = GTK_XTEXT (current_sess->gui->xtext); + xtext_buffer *buf = xtext->buffer; + + gtk_xtext_search (xtext, "", 0, NULL); +} + +static void menu_resetmarker (GtkWidget * wid, gpointer none) { gtk_xtext_reset_marker_pos (GTK_XTEXT (current_sess->gui->xtext)); } static void +menu_copy_selection (GtkWidget * wid, gpointer none) +{ + gtk_xtext_copy_selection (GTK_XTEXT (current_sess->gui->xtext)); +} + +static void menu_flushbuffer (GtkWidget * wid, gpointer none) { fe_text_clear (current_sess, 0); @@ -1580,7 +1639,7 @@ static struct mymenu mymenu[] = { #endif {0, 0, 0, M_SEP, 0, 0, 0}, /* 11 */ #define DETACH_OFFSET (12) - {0, menu_detach, GTK_STOCK_REDO, M_MENUSTOCK, 0, 0, 1, GDK_I}, /* 12 */ + {0, menu_detach, GTK_STOCK_REDO, M_MENUSTOCK, 0, 0, 1, GDK_i}, /* 12 */ #define CLOSE_OFFSET (13) {0, menu_close, GTK_STOCK_CLOSE, M_MENUSTOCK, 0, 0, 1, GDK_w}, {0, 0, 0, M_SEP, 0, 0, 0}, @@ -1633,6 +1692,10 @@ static struct mymenu mymenu[] = { {N_("Userlist Popup..."), menu_ulpopup, 0, M_MENUITEM, 0, 0, 1}, {0, 0, 0, M_END, 0, 0, 0}, /* 53 */ +#if 0 + {N_("Save Settings to Disk"), menu_saveconf, GTK_STOCK_SAVE, M_MENUSTOCK, 0, 0, 1}, /* don't use this, a /set auto-save approach will be added instead */ +#endif + {N_("_Window"), 0, 0, M_NEWMENU, 0, 0, 1}, {N_("Ban List..."), menu_banlist, 0, M_MENUITEM, 0, 0, 1}, {N_("Character Chart..."), ascii_open, 0, M_MENUITEM, 0, 0, 1}, @@ -1645,12 +1708,19 @@ static struct mymenu mymenu[] = { {N_("URL Grabber..."), url_opengui, 0, M_MENUITEM, 0, 0, 1}, {0, 0, 0, M_SEP, 0, 0, 0}, {N_("Reset Marker Line"), menu_resetmarker, 0, M_MENUITEM, 0, 0, 1, GDK_m}, + {N_("_Copy Selection"), menu_copy_selection, 0, M_MENUITEM, 0, 0, 1, GDK_C}, {N_("C_lear Text"), menu_flushbuffer, GTK_STOCK_CLEAR, M_MENUSTOCK, 0, 0, 1, GDK_l}, -#define SEARCH_OFFSET 67 - {N_("Search Text..."), menu_search, GTK_STOCK_FIND, M_MENUSTOCK, 0, 0, 1, GDK_f}, {N_("Save Text..."), menu_savebuffer, GTK_STOCK_SAVE, M_MENUSTOCK, 0, 0, 1}, +#define SEARCH_OFFSET 70 + {N_("Search"), 0, GTK_STOCK_JUSTIFY_LEFT, M_MENUSUB, 0, 0, 1}, + {N_("Search Text..."), menu_search, GTK_STOCK_FIND, M_MENUSTOCK, 0, 0, 1, GDK_f}, + {N_("Reset Search"), menu_search_reset, GTK_STOCK_FIND, M_MENUSTOCK, 0, 0, 1, GDK_F}, + {N_("Search Next" ), menu_search_next, GTK_STOCK_FIND, M_MENUSTOCK, 0, 0, 1, GDK_g}, + {N_("Search Previous" ), menu_search_prev, GTK_STOCK_FIND, M_MENUSTOCK, 0, 0, 1, GDK_G}, + {0, 0, 0, M_END, 0, 0, 0}, + + {N_("_Help"), 0, 0, M_NEWMENU, 0, 0, 1}, /* 74 */ - {N_("_Help"), 0, 0, M_NEWMENU, 0, 0, 1}, /* 69 */ {N_("_Contents"), menu_docs, GTK_STOCK_HELP, M_MENUSTOCK, 0, 0, 1, GDK_F1}, #if 0 {N_("Check for updates"), menu_update, 0, M_MENUITEM, 0, 1}, @@ -2188,7 +2258,9 @@ normalitem: mymenu[i].key, mymenu[i].key == GDK_F1 ? 0 : mymenu[i].key == GDK_w ? close_mask : - GDK_CONTROL_MASK, + (g_ascii_isupper (mymenu[i].key)) ? + GDK_SHIFT_MASK | GDK_CONTROL_MASK : + GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE); if (mymenu[i].callback) g_signal_connect (G_OBJECT (item), "activate", diff --git a/src/fe-gtk/palette.c b/src/fe-gtk/palette.c index ebae92ff..104700c2 100644 --- a/src/fe-gtk/palette.c +++ b/src/fe-gtk/palette.c @@ -18,11 +18,14 @@ #include <stdio.h> #include <stdlib.h> -#include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> +#ifndef WIN32 +#include <unistd.h> +#endif + #include "fe-gtk.h" #include "palette.h" @@ -78,8 +81,9 @@ GdkColor colors[] = { {0, 0x0000, 0x0000, 0xffff}, /* 38 tab Nick Mentioned (blue) */ {0, 0xffff, 0x0000, 0x0000}, /* 39 tab New Message (red) */ {0, 0x9595, 0x9595, 0x9595}, /* 40 away user (grey) */ + {0, 0xffff, 0x0000, 0x0000}, /* 41 spell checker color (red) */ }; -#define MAX_COL 40 +#define MAX_COL 41 void diff --git a/src/fe-gtk/palette.h b/src/fe-gtk/palette.h index c97693bb..627c6963 100644 --- a/src/fe-gtk/palette.h +++ b/src/fe-gtk/palette.h @@ -9,6 +9,7 @@ extern GdkColor colors[]; #define COL_HILIGHT 38 #define COL_NEW_MSG 39 #define COL_AWAY 40 +#define COL_SPELL 41 void palette_alloc (GtkWidget * widget); void palette_load (void); diff --git a/src/fe-gtk/plugin-tray.c b/src/fe-gtk/plugin-tray.c index e46d9570..b9f60f8d 100644 --- a/src/fe-gtk/plugin-tray.c +++ b/src/fe-gtk/plugin-tray.c @@ -1,7 +1,6 @@ /* Copyright (C) 2006-2007 Peter Zelezny. */ #include <string.h> -#include <unistd.h> #include "../common/xchat-plugin.h" #include "../common/xchat.h" #include "../common/xchatc.h" @@ -15,6 +14,10 @@ #include "menu.h" #include <gtk/gtk.h> +#ifndef WIN32 +#include <unistd.h> +#endif + #ifdef USE_LIBNOTIFY #include <libnotify/notify.h> #ifndef NOTIFY_CHECK_VERSION @@ -217,10 +220,10 @@ tray_stop_flash (void) nets = tray_count_networks (); chans = tray_count_channels (); if (nets) - tray_set_tipf (_("XChat: Connected to %u networks and %u channels"), + tray_set_tipf (_("XChat-WDK: Connected to %u networks and %u channels"), nets, chans); else - tray_set_tipf ("XChat: %s", _("Not connected.")); + tray_set_tipf ("XChat-WDK: %s", _("Not connected.")); } if (custom_icon1) @@ -370,7 +373,7 @@ tray_toggle_visibility (gboolean force_hide) /* ph may have an invalid context now */ xchat_set_context (ph, xchat_find_context (ph, NULL, NULL)); - win = (GtkWindow *)xchat_get_info (ph, "win_ptr"); + win = xchat_get_info (ph, "gtkwin_ptr"); tray_stop_flash (); tray_reset_counts (); @@ -505,11 +508,12 @@ tray_menu_cb (GtkWidget *widget, guint button, guint time, gpointer userdata) /*gtk_menu_set_screen (GTK_MENU (menu), gtk_widget_get_screen (widget));*/ if (tray_get_window_status () == WS_HIDDEN) - tray_make_item (menu, _("_Restore"), tray_menu_restore_cb, NULL); + tray_make_item (menu, _("_Restore Window"), tray_menu_restore_cb, NULL); else - tray_make_item (menu, _("_Hide"), tray_menu_restore_cb, NULL); + tray_make_item (menu, _("_Hide Window"), tray_menu_restore_cb, NULL); tray_make_item (menu, NULL, tray_menu_quit_cb, NULL); +#ifndef WIN32 /* somehow this is broken on win32 */ submenu = mg_submenu (menu, _("_Blink on")); blink_item (&prefs.input_tray_chans, submenu, _("Channel Message")); blink_item (&prefs.input_tray_priv, submenu, _("Private Message")); @@ -526,6 +530,8 @@ tray_menu_cb (GtkWidget *widget, guint button, guint time, gpointer userdata) gtk_widget_set_sensitive (item, FALSE); tray_make_item (menu, NULL, tray_menu_quit_cb, NULL); +#endif + mg_create_icon_item (_("_Quit"), GTK_STOCK_QUIT, menu, tray_menu_quit_cb, NULL); menu_add_plugin_items (menu, "\x5$TRAY", NULL); @@ -551,8 +557,12 @@ tray_init (void) sticon = gtk_status_icon_new_from_pixbuf (ICON_NORMAL); if (!sticon) return; + +#ifndef WIN32 g_signal_connect (G_OBJECT (sticon), "popup-menu", G_CALLBACK (tray_menu_cb), sticon); +#endif + g_signal_connect (G_OBJECT (sticon), "activate", G_CALLBACK (tray_menu_restore_cb), NULL); } @@ -570,15 +580,15 @@ tray_hilight_cb (char *word[], void *userdata) /* FIXME: hides any previous private messages */ tray_hilight_count++; if (tray_hilight_count == 1) - tray_set_tipf (_("XChat: Highlighted message from: %s (%s)"), + tray_set_tipf (_("XChat-WDK: Highlighted message from: %s (%s)"), word[1], xchat_get_info (ph, "channel")); else - tray_set_tipf (_("XChat: %u highlighted messages, latest from: %s (%s)"), + tray_set_tipf (_("XChat-WDK: %u highlighted messages, latest from: %s (%s)"), tray_hilight_count, word[1], xchat_get_info (ph, "channel")); } if (prefs.input_balloon_hilight) - tray_set_balloonf (word[2], _("XChat: Highlighted message from: %s (%s)"), + tray_set_balloonf (word[2], _("XChat-WDK: Highlighted message from: %s (%s)"), word[1], xchat_get_info (ph, "channel")); return XCHAT_EAT_NONE; @@ -596,14 +606,14 @@ tray_message_cb (char *word[], void *userdata) tray_pub_count++; if (tray_pub_count == 1) - tray_set_tipf (_("XChat: New public message from: %s (%s)"), + tray_set_tipf (_("XChat-WDK: New public message from: %s (%s)"), word[1], xchat_get_info (ph, "channel")); else - tray_set_tipf (_("XChat: %u new public messages."), tray_pub_count); + tray_set_tipf (_("XChat-WDK: %u new public messages."), tray_pub_count); } if (prefs.input_balloon_chans) - tray_set_balloonf (word[2], _("XChat: New public message from: %s (%s)"), + tray_set_balloonf (word[2], _("XChat-WDK: New public message from: %s (%s)"), word[1], xchat_get_info (ph, "channel")); return XCHAT_EAT_NONE; @@ -625,14 +635,14 @@ tray_priv (char *from, char *text) tray_priv_count++; if (tray_priv_count == 1) - tray_set_tipf (_("XChat: Private message from: %s (%s)"), + tray_set_tipf (_("XChat-WDK: Private message from: %s (%s)"), from, network); else - tray_set_tipf (_("XChat: %u private messages, latest from: %s (%s)"), + tray_set_tipf (_("XChat-WDK: %u private messages, latest from: %s (%s)"), tray_priv_count, from, network); if (prefs.input_balloon_priv) - tray_set_balloonf (text, _("XChat: Private message from: %s (%s)"), + tray_set_balloonf (text, _("XChat-WDK: Private message from: %s (%s)"), from, network); } @@ -678,15 +688,15 @@ tray_dcc_cb (char *word[], void *userdata) tray_file_count++; if (tray_file_count == 1) - tray_set_tipf (_("XChat: File offer from: %s (%s)"), + tray_set_tipf (_("XChat-WDK: File offer from: %s (%s)"), word[1], network); else - tray_set_tipf (_("XChat: %u file offers, latest from: %s (%s)"), + tray_set_tipf (_("XChat-WDK: %u file offers, latest from: %s (%s)"), tray_file_count, word[1], network); } if (prefs.input_balloon_priv) - tray_set_balloonf ("", _("XChat: File offer from: %s (%s)"), + tray_set_balloonf ("", _("XChat-WDK: File offer from: %s (%s)"), word[1], network); return XCHAT_EAT_NONE; @@ -722,7 +732,7 @@ tray_apply_setup (void) } else { - if (prefs.gui_tray) + if (prefs.gui_tray && !xtray_mode ()) tray_init (); } } @@ -754,7 +764,7 @@ tray_plugin_init (xchat_plugin *plugin_handle, char **plugin_name, xchat_hook_print (ph, "Focus Window", -1, tray_focus_cb, NULL); - if (prefs.gui_tray) + if (prefs.gui_tray && !xtray_mode ()) tray_init (); return 1; /* return 1 for success */ diff --git a/src/fe-gtk/plugingui.c b/src/fe-gtk/plugingui.c index de59e649..71d2f02e 100644 --- a/src/fe-gtk/plugingui.c +++ b/src/fe-gtk/plugingui.c @@ -146,8 +146,13 @@ plugingui_load_cb (session *sess, char *file) void plugingui_load (void) { - gtkutil_file_req (_("Select a Plugin or Script to load"), plugingui_load_cb, - current_sess, NULL, FRF_ADDFOLDER); + gtkutil_file_req (_("Select a Plugin or Script to load"), plugingui_load_cb, current_sess, +#ifdef WIN32 + "Plugins and Scripts\0*.dll;*.lua;*.pl;*.py;*.tcl\0" + "All files\0*.*\0\0", 0); +#else + NULL, FRF_ADDFOLDER); +#endif } static void diff --git a/src/fe-gtk/rawlog.c b/src/fe-gtk/rawlog.c index 56ca0510..593468c1 100644 --- a/src/fe-gtk/rawlog.c +++ b/src/fe-gtk/rawlog.c @@ -19,9 +19,12 @@ #include <stdio.h> #include <string.h> #include <fcntl.h> -#include <unistd.h> #include <stdlib.h> +#ifndef WIN32 +#include <unistd.h> +#endif + #include "fe-gtk.h" #include <gtk/gtkbutton.h> diff --git a/src/fe-gtk/search.c b/src/fe-gtk/search.c index d62e79c7..30ef266f 100644 --- a/src/fe-gtk/search.c +++ b/src/fe-gtk/search.c @@ -42,15 +42,20 @@ #include "xtext.h" #include "maingui.h" - -static textentry *last; /* our last search pos */ -static int case_match = 0; -static int search_backward = 0; - +GtkWidget *searchwin; static void search_search (session * sess, const gchar *text) { + gtk_xtext_search_flags flags; + textentry *last; + GError *err = NULL; + + flags = ((prefs.text_search_case_match == 1? case_match: 0) | + (prefs.text_search_backward == 1? backward: 0) | + (prefs.text_search_highlight_all == 1? highlight: 0) | + (prefs.text_search_follow == 1? follow: 0) | + (prefs.text_search_regexp == 1? regexp: 0)); if (!is_session (sess)) { fe_message (_("The window you opened this Search " @@ -58,10 +63,20 @@ search_search (session * sess, const gchar *text) return; } - last = gtk_xtext_search (GTK_XTEXT (sess->gui->xtext), text, - last, case_match, search_backward); - if (!last) + last = gtk_xtext_search (GTK_XTEXT (sess->gui->xtext), text, flags, &err); + if (text == NULL || text[0] == 0) + { + return; + } + if (err) + { + fe_message (_(err->message), FE_MSG_ERROR); + g_error_free (err); + } + else if (!last) + { fe_message (_("Search hit end, not found."), FE_MSG_ERROR); + } } static void @@ -79,6 +94,23 @@ static void search_close_cb (GtkWidget * button, GtkWidget * win) { gtk_widget_destroy (win); + searchwin = NULL; +} + +static void +search_reset_cb (GtkWidget * button, session * sess) +{ + search_search (sess, ""); + if (searchwin) + { + search_close_cb (button, searchwin); + } +} + +static void +search_cleanup_cb (GtkWidget * button, GtkWidget * win) +{ + searchwin = NULL; } static void @@ -98,13 +130,26 @@ search_key_cb (GtkWidget * window, GdkEventKey * key, gpointer userdata) static void search_caseign_cb (GtkToggleButton * but, session * sess) { - case_match = (but->active)? 1: 0; + prefs.text_search_case_match = (but->active)? 1: 0; } static void search_dirbwd_cb (GtkToggleButton * but, session * sess) { - search_backward = (but->active)? 1: 0; + prefs.text_search_backward = (but->active)? 1: 0; +} + +static void +search_regexp_cb (GtkToggleButton * but, session * sess) +{ + prefs.text_search_regexp = (but->active)? 1: 0; +} + +static void +search_highlight_cb (GtkToggleButton * but, session * sess) +{ + prefs.text_search_highlight_all = (but->active)? 1: 0; + search_search (sess, NULL); } void @@ -112,18 +157,21 @@ search_open (session * sess) { GtkWidget *win, *hbox, *vbox, *entry, *wid; - last = NULL; + if (searchwin) + { + gtk_widget_destroy (searchwin); + searchwin = NULL; + } win = mg_create_generic_tab ("search", _("XChat: Search"), TRUE, FALSE, - NULL, NULL, 0, 0, &vbox, 0); + search_cleanup_cb, NULL, 0, 0, &vbox, 0); gtk_container_set_border_width (GTK_CONTAINER (win), 12); gtk_box_set_spacing (GTK_BOX (vbox), 4); + /* First line: _____________________ _Find */ hbox = gtk_hbox_new (0, 10); gtk_container_add (GTK_CONTAINER (vbox), hbox); gtk_widget_show (hbox); - gtkutil_label_new (_("Find:"), hbox); - entry = gtk_entry_new (); g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (search_entry_cb), sess); @@ -131,29 +179,61 @@ search_open (session * sess) gtk_widget_show (entry); gtk_widget_grab_focus (entry); + wid = gtk_hbutton_box_new (); + gtk_container_add (GTK_CONTAINER (hbox), wid); + gtk_widget_show (wid); + wid = gtkutil_button (wid, GTK_STOCK_FIND, 0, search_find_cb, sess, + _("_Find")); + g_object_set_data (G_OBJECT (wid), "e", entry); + + /* Second line: X Match case */ wid = gtk_check_button_new_with_mnemonic (_("_Match case")); - GTK_TOGGLE_BUTTON (wid)->active = case_match; + GTK_TOGGLE_BUTTON (wid)->active = prefs.text_search_case_match; g_signal_connect (G_OBJECT (wid), "toggled", G_CALLBACK (search_caseign_cb), sess); gtk_container_add (GTK_CONTAINER (vbox), wid); + add_tip (wid, "Perform a case-sensitive search."); gtk_widget_show (wid); + /* Third line: X Search backwards */ wid = gtk_check_button_new_with_mnemonic (_("Search _backwards")); - GTK_TOGGLE_BUTTON (wid)->active = search_backward; + GTK_TOGGLE_BUTTON (wid)->active = prefs.text_search_backward; g_signal_connect (G_OBJECT (wid), "toggled", G_CALLBACK (search_dirbwd_cb), sess); gtk_container_add (GTK_CONTAINER (vbox), wid); + add_tip (wid, "Search from the newest text line to the oldest."); + gtk_widget_show (wid); + + /* Fourth line: X Highlight all */ + wid = gtk_check_button_new_with_mnemonic (_("_Highlight all")); + GTK_TOGGLE_BUTTON (wid)->active = prefs.text_search_highlight_all; + g_signal_connect (G_OBJECT (wid), "toggled", G_CALLBACK (search_highlight_cb), sess); + gtk_container_add (GTK_CONTAINER (vbox), wid); + add_tip (wid, "Highlight all occurrences, and underline the current occurrence."); gtk_widget_show (wid); + /* Fifth line: X Regular expression */ + wid = gtk_check_button_new_with_mnemonic (_("R_egular expression")); + GTK_TOGGLE_BUTTON (wid)->active = prefs.text_search_regexp; + g_signal_connect (G_OBJECT (wid), "toggled", G_CALLBACK (search_regexp_cb), sess); + gtk_container_add (GTK_CONTAINER (vbox), wid); + add_tip (wid, "Regard search string as a regular expression."); + gtk_widget_show (wid); + + /* Sixth line: _Close Close and _Reset */ hbox = gtk_hbutton_box_new (); gtk_box_pack_start (GTK_BOX (vbox), hbox, 0, 0, 4); gtk_widget_show (hbox); - gtkutil_button (hbox, GTK_STOCK_CLOSE, 0, search_close_cb, win, + wid = gtkutil_button (hbox, GTK_STOCK_CLOSE, 0, search_close_cb, win, _("_Close")); - wid = gtkutil_button (hbox, GTK_STOCK_FIND, 0, search_find_cb, sess, - _("_Find")); - g_object_set_data (G_OBJECT (wid), "e", entry); + add_tip (wid, "Close this box, but continue searching new lines."); + wid = gtkutil_button (hbox, "gtk-reset", 0, search_reset_cb, sess, + _("Close and _Reset")); + add_tip (wid, "Close this box, reset highlighted search items, and stop searching new lines."); - g_signal_connect (G_OBJECT (win), "key-press-event", G_CALLBACK (search_key_cb), win); + /* Add recognition of the ESC key to close the box */ + g_signal_connect (G_OBJECT (win), "key_press_event", G_CALLBACK (search_key_cb), win); + /* That's all, folks */ + searchwin = win; gtk_widget_show (win); } diff --git a/src/fe-gtk/servlistgui.c b/src/fe-gtk/servlistgui.c index 2ac0e6c9..96feb5f4 100644 --- a/src/fe-gtk/servlistgui.c +++ b/src/fe-gtk/servlistgui.c @@ -61,7 +61,7 @@ static GtkWidget *entry_nick1; static GtkWidget *entry_nick2; static GtkWidget *entry_nick3; static GtkWidget *entry_guser; -static GtkWidget *entry_greal; +/* static GtkWidget *entry_greal; */ /* edit area */ static GtkWidget *edit_win; @@ -649,8 +649,8 @@ servlist_savegui (void) if (GTK_ENTRY (entry_guser)->text[0] == 0) return 1; - if (GTK_ENTRY (entry_greal)->text[0] == 0) - return 1; + /* if (GTK_ENTRY (entry_greal)->text[0] == 0) + return 1; */ strcpy (prefs.nick1, GTK_ENTRY (entry_nick1)->text); strcpy (prefs.nick2, GTK_ENTRY (entry_nick2)->text); @@ -659,7 +659,7 @@ servlist_savegui (void) sp = strchr (prefs.username, ' '); if (sp) sp[0] = 0; /* spaces will break the login */ - strcpy (prefs.realname, GTK_ENTRY (entry_greal)->text); + /* strcpy (prefs.realname, GTK_ENTRY (entry_greal)->text); */ servlist_save (); return 0; @@ -1624,12 +1624,12 @@ servlist_open_networks (void) GtkWidget *label4; GtkWidget *label5; GtkWidget *label6; - GtkWidget *label7; + /* GtkWidget *label7; */ GtkWidget *entry1; GtkWidget *entry2; GtkWidget *entry3; GtkWidget *entry4; - GtkWidget *entry5; + /* GtkWidget *entry5; */ GtkWidget *vbox2; GtkWidget *label1; GtkWidget *table4; @@ -1703,12 +1703,12 @@ servlist_open_networks (void) (GtkAttachOptions) (0), 0, 0); gtk_misc_set_alignment (GTK_MISC (label6), 0, 0.5); - label7 = gtk_label_new_with_mnemonic (_("Rea_l name:")); + /* label7 = gtk_label_new_with_mnemonic (_("Rea_l name:")); gtk_widget_show (label7); gtk_table_attach (GTK_TABLE (table1), label7, 0, 1, 4, 5, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0); - gtk_misc_set_alignment (GTK_MISC (label7), 0, 0.5); + gtk_misc_set_alignment (GTK_MISC (label7), 0, 0.5);*/ entry_nick1 = entry1 = gtk_entry_new (); gtk_entry_set_text (GTK_ENTRY (entry1), prefs.nick1); @@ -1738,12 +1738,12 @@ servlist_open_networks (void) (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), (GtkAttachOptions) (0), 0, 0); - entry_greal = entry5 = gtk_entry_new (); + /* entry_greal = entry5 = gtk_entry_new (); gtk_entry_set_text (GTK_ENTRY (entry5), prefs.realname); gtk_widget_show (entry5); gtk_table_attach (GTK_TABLE (table1), entry5, 1, 2, 4, 5, (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), - (GtkAttachOptions) (0), 0, 0); + (GtkAttachOptions) (0), 0, 0); */ vbox2 = gtk_vbox_new (FALSE, 0); gtk_widget_show (vbox2); @@ -1882,7 +1882,7 @@ servlist_open_networks (void) gtk_label_set_mnemonic_widget (GTK_LABEL (label3), entry1); gtk_label_set_mnemonic_widget (GTK_LABEL (label6), entry4); - gtk_label_set_mnemonic_widget (GTK_LABEL (label7), entry5); + /* gtk_label_set_mnemonic_widget (GTK_LABEL (label7), entry5); */ gtk_widget_grab_focus (networks_tree); gtk_widget_grab_default (button_close); diff --git a/src/fe-gtk/setup.c b/src/fe-gtk/setup.c index 517e0944..cec06b25 100644 --- a/src/fe-gtk/setup.c +++ b/src/fe-gtk/setup.c @@ -102,7 +102,12 @@ typedef struct static const setting textbox_settings[] = { {ST_HEADER, N_("Text Box Appearance"),0,0,0}, +#ifdef WIN32 + {ST_EFONT, N_("Main font:"), P_OFFSETNL(font_main), 0, 0, sizeof prefs.font_main}, + {ST_ENTRY, N_("Alternative fonts:"), P_OFFSETNL(font_alternative), "Separate multiple entries with commas without spaces before or after.", 0, sizeof prefs.font_alternative}, +#else {ST_EFONT, N_("Font:"), P_OFFSETNL(font_normal), 0, 0, sizeof prefs.font_normal}, +#endif {ST_EFILE, N_("Background image:"), P_OFFSETNL(background), 0, 0, sizeof prefs.background}, {ST_NUMBER, N_("Scrollback lines:"), P_OFFINTNL(max_lines),0,0,100000}, {ST_TOGGLE, N_("Colored nick names"), P_OFFINTNL(colorednicks), @@ -120,7 +125,24 @@ static const setting textbox_settings[] = {ST_HEADER, N_("Time Stamps"),0,0,0}, {ST_TOGGLE, N_("Enable time stamps"), P_OFFINTNL(timestamp),0,0,2}, {ST_ENTRY, N_("Time stamp format:"), P_OFFSETNL(stamp_format), - N_("See strftime manpage for details."),0,sizeof prefs.stamp_format}, +#ifdef WIN32 + N_("See the strftime MSDN article for details."),0,sizeof prefs.stamp_format}, +#else + N_("See the strftime manpage for details."),0,sizeof prefs.stamp_format}, +#endif + + {ST_HEADER, N_("Auto-Copy Behavior"),0,0,0}, + {ST_TOGGLE, N_("Automatically copy selected text"), P_OFFINTNL(autocopy_text), + N_("Copy selected text to clipboard when left mouse button is released. " + "Otherwise, CONTROL-SHIFT-C will copy the " + "selected text to the clipboard."), 0, 0}, + {ST_TOGGLE, N_("Automatically include time stamps"), P_OFFINTNL(autocopy_stamp), + N_("Automatically include time stamps in copied lines of text. Otherwise, " + "include time stamps if the SHIFT key is held down while selecting."), 0, 0}, + {ST_TOGGLE, N_("Automatically include color information"), P_OFFINTNL(autocopy_color), + N_("Automatically include color information in copied lines of text. " + "Otherwise, include color information if the CONTROL key is held down " + "while selecting."), 0, 0}, {ST_END, 0, 0, 0, 0, 0} }; @@ -138,6 +160,12 @@ static const setting inputbox_settings[] = {ST_TOGGLE, N_("Use the Text box font and colors"), P_OFFINTNL(style_inputbox),0,0,0}, #if defined(USE_GTKSPELL) || defined(USE_LIBSEXY) {ST_TOGGLE, N_("Spell checking"), P_OFFINTNL(gui_input_spell),0,0,0}, + {ST_ENTRY, N_("Dictionaries to use:"), P_OFFSETNL(spell_langs),0,0,sizeof prefs.spell_langs}, +#ifdef WIN32 + {ST_LABEL, N_("Use language codes (as in \"share\\myspell\\dicts\").\nSeparate multiple entries with commas.")}, +#else + {ST_LABEL, N_("Use language codes. Separate multiple entries with commas.")}, +#endif #endif {ST_HEADER, N_("Nick Completion"),0,0,0}, @@ -348,6 +376,24 @@ static const setting alert_settings[] = {ST_END, 0, 0, 0, 0, 0} }; +static const setting alert_settings_xtray[] = +{ + {ST_HEADER, N_("Alerts"),0,0,0}, + + {ST_ALERTHEAD}, + {ST_3OGGLE, N_("Blink task bar on:"), 0, 0, (void *)taskbarlist, 0}, + {ST_3OGGLE, N_("Make a beep sound on:"), 0, 0, (void *)beeplist, 0}, + + {ST_HEADER, N_("Highlighted Messages"),0,0,0}, + {ST_LABEL, N_("Highlighted messages are ones where your nickname is mentioned, but also:"), 0, 0, 0, 1}, + + {ST_ENTRY, N_("Extra words to highlight:"), P_OFFSETNL(irc_extra_hilight), 0, 0, sizeof prefs.irc_extra_hilight}, + {ST_ENTRY, N_("Nick names not to highlight:"), P_OFFSETNL(irc_no_hilight), 0, 0, sizeof prefs.irc_no_hilight}, + {ST_ENTRY, N_("Nick names to always highlight:"), P_OFFSETNL(irc_nick_hilight), 0, 0, sizeof prefs.irc_nick_hilight}, + {ST_LABEL, N_("Separate multiple words with commas.\nWildcards are accepted.")}, + {ST_END, 0, 0, 0, 0, 0} +}; + static const setting general_settings[] = { {ST_HEADER, N_("Default Messages"),0,0,0}, @@ -363,7 +409,6 @@ static const setting general_settings[] = {ST_END, 0, 0, 0, 0, 0} }; -#if 0 static const setting advanced_settings[] = { {ST_HEADER, N_("Advanced Settings"),0,0,0}, @@ -371,6 +416,25 @@ static const setting advanced_settings[] = {ST_TOGGLE, N_("Display MODEs in raw form"), P_OFFINTNL(raw_modes), 0, 0, 0}, {ST_TOGGLE, N_("Whois on notify"), P_OFFINTNL(whois_on_notifyonline), N_("Sends a /WHOIS when a user comes online in your notify list"), 0, 0}, {ST_TOGGLE, N_("Hide join and part messages"), P_OFFINTNL(confmode), N_("Hide channel join/part messages by default"), 0, 0}, + {ST_ENTRY, N_("License Text:"), P_OFFSETNL(gui_license), 0, 0, sizeof prefs.gui_license}, + {ST_HEADER, N_("Auto Open DCC Windows"),0,0,0}, + {ST_TOGGLE, N_("Send window"), P_OFFINTNL(autoopendccsendwindow), 0, 0, 0}, + {ST_TOGGLE, N_("Receive window"), P_OFFINTNL(autoopendccrecvwindow), 0, 0, 0}, + {ST_TOGGLE, N_("Chat window"), P_OFFINTNL(autoopendccchatwindow), 0, 0, 0}, + + {ST_END, 0, 0, 0, 0, 0} +}; + +#ifdef WIN32 +static const setting advanced_settings_oneinstance[] = +{ + {ST_HEADER, N_("Advanced Settings"),0,0,0}, + {ST_NUMBER, N_("Auto reconnect delay:"), P_OFFINTNL(recon_delay), 0, 0, 9999}, + {ST_TOGGLE, N_("Display MODEs in raw form"), P_OFFINTNL(raw_modes), 0, 0, 0}, + {ST_TOGGLE, N_("Whois on notify"), P_OFFINTNL(whois_on_notifyonline), N_("Sends a /WHOIS when a user comes online in your notify list"), 0, 0}, + {ST_TOGGLE, N_("Hide join and part messages"), P_OFFINTNL(confmode), N_("Hide channel join/part messages by default"), 0, 0}, + {ST_TOGGLE, N_("Allow only one instance of XChat to run"), P_OFFINTNL(gui_one_instance), 0, 0, 0}, + {ST_ENTRY, N_("License Text:"), P_OFFSETNL(gui_license), 0, 0, sizeof prefs.gui_license}, {ST_HEADER, N_("Auto Open DCC Windows"),0,0,0}, {ST_TOGGLE, N_("Send window"), P_OFFINTNL(autoopendccsendwindow), 0, 0, 0}, {ST_TOGGLE, N_("Receive window"), P_OFFINTNL(autoopendccrecvwindow), 0, 0, 0}, @@ -391,7 +455,11 @@ static const setting logging_settings[] = {ST_HEADER, N_("Time Stamps"),0,0,0}, {ST_TOGGLE, N_("Insert timestamps in logs"), P_OFFINTNL(timestamp_logs), 0, 0, 2}, {ST_ENTRY, N_("Log timestamp format:"), P_OFFSETNL(timestamp_log_format), 0, 0, sizeof prefs.timestamp_log_format}, - {ST_LABEL, N_("See strftime manpage for details.")}, +#ifdef WIN32 + {ST_LABEL, N_("See the strftime MSDN article for details.")}, +#else + {ST_LABEL, N_("See the strftime manpage for details.")}, +#endif {ST_END, 0, 0, 0, 0, 0} }; @@ -1328,6 +1396,9 @@ setup_create_color_page (void) setup_create_other_color (_("New message:"), COL_NEW_MSG, 10, tab); setup_create_other_colorR (_("Away user:"), COL_AWAY, 10, tab); setup_create_other_color (_("Highlight:"), COL_HILIGHT, 11, tab); +#if defined(USE_GTKSPELL) || defined(USE_LIBSEXY) + setup_create_other_colorR (_("Spell checker:"), COL_SPELL, 11, tab); +#endif return box; } @@ -1711,7 +1782,7 @@ static const char *const cata[] = N_("General"), N_("Logging"), N_("Sound"), -/* N_("Advanced"),*/ + N_("Advanced"), NULL, N_("Network"), N_("Network setup"), @@ -1732,10 +1803,33 @@ setup_create_pages (GtkWidget *box) setup_add_page (cata[3], book, setup_create_page (userlist_settings)); setup_add_page (cata[4], book, setup_create_page (tabs_settings)); setup_add_page (cata[5], book, setup_create_color_page ()); - setup_add_page (cata[8], book, setup_create_page (alert_settings)); + + if (xtray_mode ()) + { + setup_add_page (cata[8], book, setup_create_page (alert_settings_xtray)); + } + else + { + setup_add_page (cata[8], book, setup_create_page (alert_settings)); + } + setup_add_page (cata[9], book, setup_create_page (general_settings)); setup_add_page (cata[10], book, setup_create_page (logging_settings)); setup_add_page (cata[11], book, setup_create_sound_page ()); + +#ifdef WIN32 + if (portable_mode ()) + { + setup_add_page (cata[12], book, setup_create_page (advanced_settings)); + } + else + { + setup_add_page (cata[12], book, setup_create_page (advanced_settings_oneinstance)); + } +#else + setup_add_page (cata[12], book, setup_create_page (advanced_settings)); +#endif + setup_add_page (cata[14], book, setup_create_page (network_settings)); setup_add_page (cata[15], book, setup_create_page (filexfer_settings)); @@ -1971,6 +2065,12 @@ setup_apply_real (int new_pix, int do_ulist, int do_layout) static void setup_apply (struct xchatprefs *pr) { +#ifdef WIN32 + PangoFontDescription *old_desc; + PangoFontDescription *new_desc; + char buffer[4 * FONTNAMELEN + 1]; + time_t rawtime; +#endif int new_pix = FALSE; int noapply = FALSE; int do_ulist = FALSE; @@ -2014,6 +2114,30 @@ setup_apply (struct xchatprefs *pr) memcpy (&prefs, pr, sizeof (prefs)); +#ifdef WIN32 + /* merge font_main and font_alternative into font_normal */ + old_desc = pango_font_description_from_string (prefs.font_main); + sprintf (buffer, "%s,%s", pango_font_description_get_family (old_desc), prefs.font_alternative); + new_desc = pango_font_description_from_string (buffer); + pango_font_description_set_weight (new_desc, pango_font_description_get_weight (old_desc)); + pango_font_description_set_style (new_desc, pango_font_description_get_style (old_desc)); + pango_font_description_set_size (new_desc, pango_font_description_get_size (old_desc)); + sprintf (prefs.font_normal, "%s", pango_font_description_to_string (new_desc)); + + /* FIXME this is not required after pango_font_description_from_string() + g_free (old_desc); + g_free (new_desc); + */ + + /* workaround for strftime differences between POSIX and MSVC */ + time (&rawtime); + + if (!strftime (buffer, sizeof (buffer), prefs.stamp_format, localtime (&rawtime)) || !strftime (buffer, sizeof (buffer), prefs.timestamp_log_format, localtime (&rawtime))) + { + fe_message (_("Invalid time stamp format! See the strftime MSDN article for details."), FE_MSG_ERROR); + } +#endif + setup_apply_real (new_pix, do_ulist, do_layout); if (noapply) diff --git a/src/fe-gtk/sexy-iso-codes.c b/src/fe-gtk/sexy-iso-codes.c new file mode 100644 index 00000000..4b637c9f --- /dev/null +++ b/src/fe-gtk/sexy-iso-codes.c @@ -0,0 +1,305 @@ +/* + * Copyright (C) 2005 Nathan Fredrickson + * Borrowed from Galeon, renamed, and simplified to only use iso-codes with no + * fallback method. + * + * Copyright (C) 2004 Christian Persch + * Copyright (C) 2004 Crispin Flowerday + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "../../config.h" + +#include "sexy-iso-codes.h" + +#include <glib/gi18n.h> + +#include <string.h> + +#include <libxml/xmlreader.h> + +static GHashTable *iso_639_table = NULL; +static GHashTable *iso_3166_table = NULL; + +#define ISO_639_DOMAIN "iso_639" +#define ISO_3166_DOMAIN "iso_3166" + +#ifdef HAVE_ISO_CODES + +#define ISOCODESLOCALEDIR "/share/locale" + +static void +read_iso_639_entry (xmlTextReaderPtr reader, + GHashTable *table) +{ + xmlChar *code, *name; + + code = xmlTextReaderGetAttribute (reader, (const xmlChar *) "iso_639_1_code"); + name = xmlTextReaderGetAttribute (reader, (const xmlChar *) "name"); + + /* Get iso-639-2 code */ + if (code == NULL || code[0] == '\0') + { + xmlFree (code); + /* FIXME: use the 2T or 2B code? */ + code = xmlTextReaderGetAttribute (reader, (const xmlChar *) "iso_639_2T_code"); + } + + if (code != NULL && code[0] != '\0' && name != NULL && name[0] != '\0') + { + g_hash_table_insert (table, code, name); + } + else + { + xmlFree (code); + xmlFree (name); + } +} + +static void +read_iso_3166_entry (xmlTextReaderPtr reader, + GHashTable *table) +{ + xmlChar *code, *name; + + code = xmlTextReaderGetAttribute (reader, (const xmlChar *) "alpha_2_code"); + name = xmlTextReaderGetAttribute (reader, (const xmlChar *) "name"); + + if (code != NULL && code[0] != '\0' && name != NULL && name[0] != '\0') + { + char *lcode; + + lcode = g_ascii_strdown ((char *) code, -1); + xmlFree (code); + + g_hash_table_insert (table, lcode, name); + } + else + { + xmlFree (code); + xmlFree (name); + } + +} + +typedef enum +{ + STATE_START, + STATE_STOP, + STATE_ENTRIES, +} ParserState; + +static gboolean +load_iso_entries (int iso, + GFunc read_entry_func, + gpointer user_data) +{ + xmlTextReaderPtr reader; + ParserState state = STATE_START; + xmlChar iso_entries[32], iso_entry[32]; + char *filename; + int ret = -1; + +#ifdef WIN32 + filename = g_strdup_printf (".\\share\\xml\\iso-codes\\iso_%d.xml", iso); +#else + filename = g_strdup_printf ("/usr/share/xml/iso-codes/iso_%d.xml", iso); +#endif + reader = xmlNewTextReaderFilename (filename); + if (reader == NULL) goto out; + + xmlStrPrintf (iso_entries, sizeof (iso_entries), + (xmlChar *)"iso_%d_entries", iso); + xmlStrPrintf (iso_entry, sizeof (iso_entry), + (xmlChar *)"iso_%d_entry", iso); + + ret = xmlTextReaderRead (reader); + + while (ret == 1) + { + const xmlChar *tag; + xmlReaderTypes type; + + tag = xmlTextReaderConstName (reader); + type = xmlTextReaderNodeType (reader); + + if (state == STATE_ENTRIES && + type == XML_READER_TYPE_ELEMENT && + xmlStrEqual (tag, iso_entry)) + { + read_entry_func (reader, user_data); + } + else if (state == STATE_START && + type == XML_READER_TYPE_ELEMENT && + xmlStrEqual (tag, iso_entries)) + { + state = STATE_ENTRIES; + } + else if (state == STATE_ENTRIES && + type == XML_READER_TYPE_END_ELEMENT && + xmlStrEqual (tag, iso_entries)) + { + state = STATE_STOP; + } + else if (type == XML_READER_TYPE_SIGNIFICANT_WHITESPACE || + type == XML_READER_TYPE_WHITESPACE || + type == XML_READER_TYPE_TEXT || + type == XML_READER_TYPE_COMMENT) + { + /* eat it */ + } + else + { + /* ignore it */ + } + + ret = xmlTextReaderRead (reader); + } + + xmlFreeTextReader (reader); + +out: + if (ret < 0 || state != STATE_STOP) + { + /* This is not critical, we will fallback to our own code */ + g_free (filename); + return FALSE; + } + + g_free (filename); + + return TRUE; +} + +#endif /* HAVE_ISO_CODES */ + + +static void +ensure_iso_codes_initialised (void) +{ + static gboolean initialised = FALSE; + + if (initialised == TRUE) + { + return; + } + initialised = TRUE; + +#if defined (ENABLE_NLS) && defined (HAVE_ISO_CODES) + bindtextdomain (ISO_639_DOMAIN, ISOCODESLOCALEDIR); + bind_textdomain_codeset (ISO_639_DOMAIN, "UTF-8"); + + bindtextdomain(ISO_3166_DOMAIN, ISOCODESLOCALEDIR); + bind_textdomain_codeset (ISO_3166_DOMAIN, "UTF-8"); +#endif + + iso_639_table = g_hash_table_new_full (g_str_hash, g_str_equal, + (GDestroyNotify) xmlFree, + (GDestroyNotify) xmlFree); + + iso_3166_table = g_hash_table_new_full (g_str_hash, g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) xmlFree); + +#ifdef HAVE_ISO_CODES + load_iso_entries (639, (GFunc) read_iso_639_entry, iso_639_table); + load_iso_entries (3166, (GFunc) read_iso_3166_entry, iso_3166_table); +#endif +} + + +static char * +get_iso_name_for_lang_code (const char *code) +{ + char **str; + char *name = NULL; + const char *langname, *localename; + int len; + + str = g_strsplit (code, "_", -1); + + /* count the entries */ + for (len = 0; str[len]; len++ ) /* empty */; + + g_return_val_if_fail (len != 0, NULL); + + langname = (const char *) g_hash_table_lookup (iso_639_table, str[0]); + + if (len == 1 && langname != NULL) + { + name = g_strdup (dgettext (ISO_639_DOMAIN, langname)); + } + else if (len == 2 && langname != NULL) + { + localename = (const char *) g_hash_table_lookup (iso_3166_table, str[1]); + + if (localename != NULL) + { + /* translators: the first %s is the language name, and the + * second %s is the locale name. Example: + * "French (France) + * + * Also: The text before the "|" is context to help you decide on + * the correct translation. You MUST OMIT it in the translated string. + */ + name = g_strdup_printf (Q_("language|%s (%s)"), + dgettext (ISO_639_DOMAIN, langname), + dgettext (ISO_3166_DOMAIN, localename)); + } + else + { + name = g_strdup_printf (Q_("language|%s (%s)"), + dgettext (ISO_639_DOMAIN, langname), str[1]); + } + } + + g_strfreev (str); + + return name; +} + +/** + * gtkspell_iso_codes_lookup_name_for_code: + * @code: A language code, e.g. en_CA + * + * Looks up a name to display to the user for a language code, + * this might use the iso-codes package if support was compiled + * in, and it is available + * + * Returns: the UTF-8 string to display to the user, or NULL if + * a name for the code could not be found + */ +char * +gtkspell_iso_codes_lookup_name_for_code (const char *code) +{ + char * lcode; + char * ret; + + g_return_val_if_fail (code != NULL, NULL); + + ensure_iso_codes_initialised (); + + lcode = g_ascii_strdown (code, -1); + + ret = get_iso_name_for_lang_code (lcode); + + g_free (lcode); + + return ret; +} + diff --git a/src/fe-gtk/sexy-iso-codes.h b/src/fe-gtk/sexy-iso-codes.h new file mode 100644 index 00000000..e5f37162 --- /dev/null +++ b/src/fe-gtk/sexy-iso-codes.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2005 Nathan Fredrickson + * Borrowed from Galeon, renamed, and simplified to only use iso-codes with no + * fallback method. + * + * Copyright (C) 2004 Christian Persch + * Copyright (C) 2004 Crispin Flowerday + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#ifndef GTKSPELL_ISO_CODES_H +#define GTKSPELL_ISO_CODES_H + +#include <glib.h> + +G_BEGIN_DECLS + +char * gtkspell_iso_codes_lookup_name_for_code (const char *code); + +G_END_DECLS + +#endif diff --git a/src/fe-gtk/sexy-marshal.c b/src/fe-gtk/sexy-marshal.c new file mode 100644 index 00000000..10a629f2 --- /dev/null +++ b/src/fe-gtk/sexy-marshal.c @@ -0,0 +1,129 @@ + +#include <glib-object.h> + + +#ifdef G_ENABLE_DEBUG +#define g_marshal_value_peek_boolean(v) g_value_get_boolean (v) +#define g_marshal_value_peek_char(v) g_value_get_char (v) +#define g_marshal_value_peek_uchar(v) g_value_get_uchar (v) +#define g_marshal_value_peek_int(v) g_value_get_int (v) +#define g_marshal_value_peek_uint(v) g_value_get_uint (v) +#define g_marshal_value_peek_long(v) g_value_get_long (v) +#define g_marshal_value_peek_ulong(v) g_value_get_ulong (v) +#define g_marshal_value_peek_int64(v) g_value_get_int64 (v) +#define g_marshal_value_peek_uint64(v) g_value_get_uint64 (v) +#define g_marshal_value_peek_enum(v) g_value_get_enum (v) +#define g_marshal_value_peek_flags(v) g_value_get_flags (v) +#define g_marshal_value_peek_float(v) g_value_get_float (v) +#define g_marshal_value_peek_double(v) g_value_get_double (v) +#define g_marshal_value_peek_string(v) (char*) g_value_get_string (v) +#define g_marshal_value_peek_param(v) g_value_get_param (v) +#define g_marshal_value_peek_boxed(v) g_value_get_boxed (v) +#define g_marshal_value_peek_pointer(v) g_value_get_pointer (v) +#define g_marshal_value_peek_object(v) g_value_get_object (v) +#else /* !G_ENABLE_DEBUG */ +/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API. + * Do not access GValues directly in your code. Instead, use the + * g_value_get_*() functions + */ +#define g_marshal_value_peek_boolean(v) (v)->data[0].v_int +#define g_marshal_value_peek_char(v) (v)->data[0].v_int +#define g_marshal_value_peek_uchar(v) (v)->data[0].v_uint +#define g_marshal_value_peek_int(v) (v)->data[0].v_int +#define g_marshal_value_peek_uint(v) (v)->data[0].v_uint +#define g_marshal_value_peek_long(v) (v)->data[0].v_long +#define g_marshal_value_peek_ulong(v) (v)->data[0].v_ulong +#define g_marshal_value_peek_int64(v) (v)->data[0].v_int64 +#define g_marshal_value_peek_uint64(v) (v)->data[0].v_uint64 +#define g_marshal_value_peek_enum(v) (v)->data[0].v_long +#define g_marshal_value_peek_flags(v) (v)->data[0].v_ulong +#define g_marshal_value_peek_float(v) (v)->data[0].v_float +#define g_marshal_value_peek_double(v) (v)->data[0].v_double +#define g_marshal_value_peek_string(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_param(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_boxed(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_pointer(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_object(v) (v)->data[0].v_pointer +#endif /* !G_ENABLE_DEBUG */ + + +/* BOOLEAN:STRING (./marshal.list:1) */ +void +sexy_marshal_BOOLEAN__STRING (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data) +{ + typedef gboolean (*GMarshalFunc_BOOLEAN__STRING) (gpointer data1, + gpointer arg_1, + gpointer data2); + register GMarshalFunc_BOOLEAN__STRING callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + gboolean v_return; + + g_return_if_fail (return_value != NULL); + g_return_if_fail (n_param_values == 2); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_BOOLEAN__STRING) (marshal_data ? marshal_data : cc->callback); + + v_return = callback (data1, + g_marshal_value_peek_string (param_values + 1), + data2); + + g_value_set_boolean (return_value, v_return); +} + +/* OBJECT:OBJECT,OBJECT (./marshal.list:2) */ +void +sexy_marshal_OBJECT__OBJECT_OBJECT (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data) +{ + typedef GObject* (*GMarshalFunc_OBJECT__OBJECT_OBJECT) (gpointer data1, + gpointer arg_1, + gpointer arg_2, + gpointer data2); + register GMarshalFunc_OBJECT__OBJECT_OBJECT callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + GObject* v_return; + + g_return_if_fail (return_value != NULL); + g_return_if_fail (n_param_values == 3); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_OBJECT__OBJECT_OBJECT) (marshal_data ? marshal_data : cc->callback); + + v_return = callback (data1, + g_marshal_value_peek_object (param_values + 1), + g_marshal_value_peek_object (param_values + 2), + data2); + + g_value_take_object (return_value, v_return); +} + diff --git a/src/fe-gtk/sexy-marshal.h b/src/fe-gtk/sexy-marshal.h new file mode 100644 index 00000000..f41eccbe --- /dev/null +++ b/src/fe-gtk/sexy-marshal.h @@ -0,0 +1,28 @@ + +#ifndef __sexy_marshal_MARSHAL_H__ +#define __sexy_marshal_MARSHAL_H__ + +#include <glib-object.h> + +G_BEGIN_DECLS + +/* BOOLEAN:STRING (./marshal.list:1) */ +extern void sexy_marshal_BOOLEAN__STRING (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* OBJECT:OBJECT,OBJECT (./marshal.list:2) */ +extern void sexy_marshal_OBJECT__OBJECT_OBJECT (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +G_END_DECLS + +#endif /* __sexy_marshal_MARSHAL_H__ */ + diff --git a/src/fe-gtk/sexy-spell-entry.c b/src/fe-gtk/sexy-spell-entry.c index d67ffe2d..9483f04b 100644 --- a/src/fe-gtk/sexy-spell-entry.c +++ b/src/fe-gtk/sexy-spell-entry.c @@ -26,10 +26,19 @@ #include <gtk/gtk.h> #include "sexy-spell-entry.h" #include <string.h> +#include <fcntl.h> #include <glib/gi18n.h> #include <sys/types.h> -/*#include "gtkspell-iso-codes.h" -#include "sexy-marshal.h"*/ +#include <sys/stat.h> +#include "sexy-iso-codes.h" +#include "sexy-marshal.h" + +#ifdef WIN32 +#include "typedef.h" +#endif + +#include "../common/cfgfiles.h" +#include "../common/xchatc.h" /* * Bunch of poop to make enchant into a runtime dependency rather than a @@ -134,12 +143,19 @@ initialize_enchant () GModule *enchant; gpointer funcptr; +#ifdef WIN32 + enchant = g_module_open("libenchant.dll", 0); +#else enchant = g_module_open("libenchant", 0); +#endif if (enchant == NULL) { +#ifndef WIN32 enchant = g_module_open("libenchant.so.1", 0); - if (enchant == NULL) - return; + if (enchant == NULL) + return; +#endif + return; } have_enchant = TRUE; @@ -207,14 +223,14 @@ sexy_spell_entry_class_init(SexySpellEntryClass *klass) * Returns: %FALSE to indicate that the word should be marked as * correct. */ -/* signals[WORD_CHECK] = g_signal_new("word_check", + signals[WORD_CHECK] = g_signal_new("word_check", G_TYPE_FROM_CLASS(object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(SexySpellEntryClass, word_check), (GSignalAccumulator) spell_accumulator, NULL, sexy_marshal_BOOLEAN__STRING, G_TYPE_BOOLEAN, - 1, G_TYPE_STRING);*/ + 1, G_TYPE_STRING); } static void @@ -260,8 +276,42 @@ gtk_entry_find_position (GtkEntry *entry, gint x) static void insert_underline(SexySpellEntry *entry, guint start, guint end) { - PangoAttribute *ucolor = pango_attr_underline_color_new (65535, 0, 0); - PangoAttribute *unline = pango_attr_underline_new (PANGO_UNDERLINE_ERROR); + int fh, l; + int red, green, blue; + struct stat st; + char *cfg; + PangoAttribute *ucolor; + PangoAttribute *unline; + + fh = xchat_open_file ("colors.conf", O_RDONLY, 0, 0); + + if (fh != -1) + { + fstat (fh, &st); + cfg = malloc (st.st_size + 1); + + if (cfg) + { + cfg[0] = '\0'; + l = read (fh, cfg, st.st_size); + if (l >= 0) + { + cfg[l] = '\0'; + } + + cfg_get_color (cfg, "color_265", &red, &green, &blue); + free (cfg); + } + + close (fh); + } else + { + red = 65535; + green = blue = 0; + } + + ucolor = pango_attr_underline_color_new (red, green, blue); + unline = pango_attr_underline_new (PANGO_UNDERLINE_ERROR); ucolor->start_index = start; unline->start_index = start; @@ -457,10 +507,6 @@ build_spelling_menu(SexySpellEntry *entry, const gchar *word) if (entry->priv->dict_list == NULL) return topmenu; -#if 1 - dict = (struct EnchantDict *) entry->priv->dict_list->data; - build_suggestion_menu(entry, topmenu, dict, word); -#else /* Suggestions */ if (g_slist_length(entry->priv->dict_list) == 1) { dict = (struct EnchantDict *) entry->priv->dict_list->data; @@ -489,7 +535,6 @@ build_spelling_menu(SexySpellEntry *entry, const gchar *word) build_suggestion_menu(entry, menu, dict, word); } } -#endif /* Separator */ mi = gtk_separator_menu_item_new (); @@ -503,11 +548,6 @@ build_spelling_menu(SexySpellEntry *entry, const gchar *word) gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mi), gtk_image_new_from_stock(GTK_STOCK_ADD, GTK_ICON_SIZE_MENU)); -#if 1 - dict = (struct EnchantDict *) entry->priv->dict_list->data; - g_object_set_data(G_OBJECT(mi), "enchant-dict", dict); - g_signal_connect(G_OBJECT(mi), "activate", G_CALLBACK(add_to_dictionary), entry); -#else if (g_slist_length(entry->priv->dict_list) == 1) { dict = (struct EnchantDict *) entry->priv->dict_list->data; g_object_set_data(G_OBJECT(mi), "enchant-dict", dict); @@ -539,7 +579,6 @@ build_spelling_menu(SexySpellEntry *entry, const gchar *word) gtk_menu_shell_append(GTK_MENU_SHELL(menu), submi); } } -#endif gtk_widget_show_all(mi); gtk_menu_shell_append(GTK_MENU_SHELL(topmenu), mi); @@ -721,11 +760,7 @@ word_misspelled(SexySpellEntry *entry, int start, int end) g_strlcpy(word, text + start, end - start + 1); -#if 0 g_signal_emit(entry, signals[WORD_CHECK], 0, word, &ret); -#else - ret = default_word_check (entry, word); -#endif g_free(word); return ret; @@ -921,10 +956,10 @@ void sexy_spell_entry_activate_default_languages(SexySpellEntry *entry) { #if GLIB_CHECK_VERSION (2, 6, 0) - const gchar* const *langs; + /*const gchar* const *langs; int i; - gchar *lastprefix = NULL; - GSList *enchant_langs; + gchar *lastprefix = NULL;*/ + GSList *enchant_langs, *i; if (!have_enchant) return; @@ -933,14 +968,14 @@ sexy_spell_entry_activate_default_languages(SexySpellEntry *entry) entry->priv->broker = enchant_broker_init(); - langs = g_get_language_names (); + /*langs = g_get_language_names (); if (langs == NULL) - return; + return;*/ enchant_langs = sexy_spell_entry_get_languages(entry); - for (i = 0; langs[i]; i++) { + /*for (i = 0; langs[i]; i++) { if ((g_strncasecmp(langs[i], "C", 1) != 0) && (strlen(langs[i]) >= 2) && enchant_has_lang(langs[i], enchant_langs)) { @@ -952,10 +987,19 @@ sexy_spell_entry_activate_default_languages(SexySpellEntry *entry) } } if (lastprefix != NULL) - g_free(lastprefix); + g_free(lastprefix);*/ + + for (i = enchant_langs; i; i = g_slist_next (i)) + { + if (strstr (prefs.spell_langs, i->data) != NULL) + { + sexy_spell_entry_activate_language_internal (entry, i->data, NULL); + } + } g_slist_foreach(enchant_langs, (GFunc) g_free, NULL); g_slist_free(enchant_langs); + g_slist_free (i); /* If we don't have any languages activated, use "en" */ if (entry->priv->dict_list == NULL) @@ -1083,8 +1127,8 @@ gchar * sexy_spell_entry_get_language_name(const SexySpellEntry *entry, const gchar *lang) { - /*if (have_enchant) - return gtkspell_iso_codes_lookup_name_for_code(lang);*/ + if (have_enchant) + return gtkspell_iso_codes_lookup_name_for_code(lang); return NULL; } diff --git a/src/fe-gtk/typedef.h b/src/fe-gtk/typedef.h new file mode 100644 index 00000000..b20612ea --- /dev/null +++ b/src/fe-gtk/typedef.h @@ -0,0 +1,11 @@ +#ifndef SSIZE_T_DEFINED +#ifdef ssize_t +#undef ssize_t +#endif +#ifdef _WIN64 +typedef __int64 ssize_t; +#else +typedef _W64 int ssize_t; +#endif +#define SSIZE_T_DEFINED +#endif diff --git a/src/fe-gtk/xchat.exe.manifest b/src/fe-gtk/xchat.exe.manifest new file mode 100644 index 00000000..13685d98 --- /dev/null +++ b/src/fe-gtk/xchat.exe.manifest @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> + <assemblyIdentity + name="XChat" + processorArchitecture="*" + version="1.0.0.0" + type="win32" + /> + <description>XChat IRC client</description> + <dependency> + <dependentAssembly> + <assemblyIdentity + type="win32" + name="Microsoft.Windows.Common-Controls" + version="6.0.0.0" + processorArchitecture="*" + publicKeyToken="6595b64144ccf1df" + language="*" + /> + </dependentAssembly> + </dependency> +</assembly> diff --git a/src/fe-gtk/xchat.rc b/src/fe-gtk/xchat.rc new file mode 100644 index 00000000..28676b83 --- /dev/null +++ b/src/fe-gtk/xchat.rc @@ -0,0 +1,28 @@ +#include <winver.h> +#include "../../config.h" +#include "../../resource.h" + +1 24 "xchat.exe.manifest" + +XC_ICON ICON "../../xchat.ico" + +VS_VERSION_INFO VERSIONINFO + FILEVERSION COMMA_VERSION + PRODUCTVERSION COMMA_VERSION + BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904B0" + BEGIN + + VALUE "FileDescription", "XChat-WDK IRC Client" + VALUE "ProductName", "XChat-WDK" + VALUE "ProductVersion", PACKAGE_VERSION + VALUE "FileVersion", PACKAGE_VERSION + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0409, 0x04B0 + END + END diff --git a/src/fe-gtk/xtext.c b/src/fe-gtk/xtext.c index fa9803c7..d474f148 100644 --- a/src/fe-gtk/xtext.c +++ b/src/fe-gtk/xtext.c @@ -42,7 +42,6 @@ #include <ctype.h> #include <stdlib.h> #include <time.h> -#include <unistd.h> #include <gtk/gtkmain.h> #include <gtk/gtksignal.h> #include <gtk/gtkselection.h> @@ -66,6 +65,11 @@ #include "mmx_cmod.h" #endif +#include "../common/xchat.h" +#include "../common/fe.h" +#include "../common/util.h" +#include "../common/xchatc.h" +#include "fe-gtk.h" #include "xtext.h" #define charlen(str) g_utf8_skip[*(guchar *)(str)] @@ -73,6 +77,8 @@ #ifdef WIN32 #include <windows.h> #include <gdk/gdkwin32.h> +#else +#include <unistd.h> #endif /* is delimiter */ @@ -89,6 +95,20 @@ static GtkWidgetClass *parent_class = NULL; +/* + * offsets_t is used for retaining search information. + * It is stored in the 'data' member of a GList, + * as chained from ent->marks. It saves starting and + * ending+1 offset of a found occurrence. + */ +typedef union offsets_u { + struct offsets_s { + guint16 start; + guint16 end; + } o; + guint32 u; +} offsets_t; + struct textentry { struct textentry *next; @@ -108,6 +128,7 @@ struct textentry guchar tag; guchar pad1; guchar pad2; /* 32-bit align : 44 bytes total */ + GList *marks; /* List of found strings */ }; enum @@ -146,11 +167,25 @@ static void gtk_xtext_recalc_widths (xtext_buffer *buf, int); static void gtk_xtext_fix_indent (xtext_buffer *buf); static int gtk_xtext_find_subline (GtkXText *xtext, textentry *ent, int line); static char *gtk_xtext_conv_color (unsigned char *text, int len, int *newlen); +/* For use by gtk_xtext_strip_color() and its callers -- */ +typedef union offlen_u { + struct offlen_s { + guint16 off; + guint16 len; + } o; + guint32 u; +} offlen_t; static unsigned char * gtk_xtext_strip_color (unsigned char *text, int len, unsigned char *outbuf, - int *newlen, int *mb_ret, int strip_hidden); + int *newlen, int *mb_ret, GSList **slp, int strip_hidden); static gboolean gtk_xtext_check_ent_visibility (GtkXText * xtext, textentry *find_ent, int add); static int gtk_xtext_render_page_timeout (GtkXText * xtext); +static int gtk_xtext_search_offset (xtext_buffer *buf, textentry *ent, unsigned int off); +static void gtk_xtext_search_textentry (xtext_buffer *, textentry *, int); +static void gtk_xtext_search_textentry_del (xtext_buffer *, textentry *); +static void gtk_xtext_search_textentry_fini (gpointer, gpointer); +static void gtk_xtext_search_fini (xtext_buffer *); +static gboolean gtk_xtext_search_init (xtext_buffer *buf, const gchar *text, gtk_xtext_search_flags flags, GError **perr); /* some utility functions first */ @@ -767,9 +802,8 @@ gtk_xtext_adjustment_set (xtext_buffer *buf, int fire_signal) if (adj->upper == 0) adj->upper = 1; - adj->page_size = - (GTK_WIDGET (buf->xtext)->allocation.height - - buf->xtext->font->descent) / buf->xtext->fontsize; + adj->page_size = GTK_WIDGET (buf->xtext)->allocation.height / + buf->xtext->fontsize; adj->page_increment = adj->page_size; if (adj->value > adj->upper - adj->page_size) @@ -998,7 +1032,11 @@ gtk_xtext_realize (GtkWidget * widget) gdk_window_set_user_data (widget->window, widget); +#if GTK_CHECK_VERSION(2,24,0) + xtext->depth = gdk_window_get_visual (widget->window)->depth; +#else xtext->depth = gdk_drawable_get_visual (widget->window)->depth; +#endif val.subwindow_mode = GDK_INCLUDE_INFERIORS; val.graphics_exposures = 0; @@ -1093,6 +1131,7 @@ gtk_xtext_size_allocate (GtkWidget * widget, GtkAllocation * allocation) xtext->avoid_trans = FALSE; + allocation->height = allocation->height / xtext->fontsize * xtext->fontsize; widget->allocation = *allocation; if (GTK_WIDGET_REALIZED (widget)) { @@ -1122,18 +1161,6 @@ gtk_xtext_size_allocate (GtkWidget * widget, GtkAllocation * allocation) } } -static void -gtk_xtext_selection_clear_full (xtext_buffer *buf) -{ - textentry *ent = buf->text_first; - while (ent) - { - ent->mark_start = -1; - ent->mark_end = -1; - ent = ent->next; - } -} - static int gtk_xtext_selection_clear (xtext_buffer *buf) { @@ -1717,13 +1744,6 @@ gtk_xtext_selection_draw (GtkXText * xtext, GdkEventMotion * event, gboolean ren offset_end = tmp; } - /* has the selection changed? Dont render unless necessary */ - if (xtext->buffer->last_ent_start == ent_start && - xtext->buffer->last_ent_end == ent_end && - xtext->buffer->last_offset_start == offset_start && - xtext->buffer->last_offset_end == offset_end) - return; - /* set all the old mark_ fields to -1 */ gtk_xtext_selection_clear (xtext->buffer); @@ -1753,10 +1773,24 @@ gtk_xtext_selection_draw (GtkXText * xtext, GdkEventMotion * event, gboolean ren gtk_xtext_selection_render (xtext, ent_start, offset_start, ent_end, offset_end); } +static int +gtk_xtext_timeout_ms (GtkXText *xtext, int pixes) +{ + int apixes = abs(pixes); + + if (apixes < 6) return 100; + if (apixes < 12) return 50; + if (apixes < 20) return 20; + return 10; +} + static gint gtk_xtext_scrolldown_timeout (GtkXText * xtext) { int p_y, win_height; + xtext_buffer *buf = xtext->buffer; + GtkAdjustment *adj = xtext->adj; + textentry *ent; gdk_window_get_pointer (GTK_WIDGET (xtext)->window, 0, &p_y, 0); gdk_drawable_get_size (GTK_WIDGET (xtext)->window, 0, &win_height); @@ -1764,13 +1798,30 @@ gtk_xtext_scrolldown_timeout (GtkXText * xtext) if (p_y > win_height && xtext->adj->value < (xtext->adj->upper - xtext->adj->page_size)) { - xtext->adj->value++; - gtk_adjustment_changed (xtext->adj); - gtk_xtext_render_page (xtext); - return 1; + xtext->adj->value += buf->pagetop_ent->lines_taken; + ent = buf->last_ent_end->next; + if (ent) + { + gtk_adjustment_value_changed (xtext->adj); + } + else + { + buf->scrollbar_down = TRUE; + } + xtext->scroll_tag = g_timeout_add (gtk_xtext_timeout_ms (xtext, p_y - win_height), + (GSourceFunc) + gtk_xtext_scrolldown_timeout, + xtext); + xtext->select_start_y -= (adj->value - xtext->select_start_adj) * xtext->fontsize; + xtext->select_start_adj = adj->value; + gtk_xtext_selection_draw (xtext, NULL, TRUE); + gtk_xtext_render_ents (xtext, ent, buf->last_ent_end); + } + else + { + xtext->scroll_tag = 0; } - xtext->scroll_tag = 0; return 0; } @@ -1778,18 +1829,35 @@ static gint gtk_xtext_scrollup_timeout (GtkXText * xtext) { int p_y; + xtext_buffer *buf = xtext->buffer; + GtkAdjustment *adj = xtext->adj; + textentry *ent; gdk_window_get_pointer (GTK_WIDGET (xtext)->window, 0, &p_y, 0); - if (p_y < 0 && xtext->adj->value > 0.0) + if (p_y < 0 && adj->value >= 0) { - xtext->adj->value--; - gtk_adjustment_changed (xtext->adj); - gtk_xtext_render_page (xtext); - return 1; + buf->scrollbar_down = FALSE; + ent = buf->last_ent_start->prev; + if (ent) + { + adj->value -= ent->lines_taken; + gtk_adjustment_value_changed (adj); + } + xtext->select_start_y -= (adj->value - xtext->select_start_adj) * xtext->fontsize; + xtext->select_start_adj = adj->value; + gtk_xtext_selection_draw (xtext, NULL, TRUE); + gtk_xtext_render_ents (xtext, ent, buf->last_ent_end); + xtext->scroll_tag = g_timeout_add (gtk_xtext_timeout_ms (xtext, p_y), + (GSourceFunc) + gtk_xtext_scrollup_timeout, + xtext); + } + else + { + xtext->scroll_tag = 0; } - xtext->scroll_tag = 0; return 0; } @@ -1799,35 +1867,32 @@ gtk_xtext_selection_update (GtkXText * xtext, GdkEventMotion * event, int p_y, g int win_height; int moved; + if (xtext->scroll_tag) + { + return; + } + gdk_drawable_get_size (GTK_WIDGET (xtext)->window, 0, &win_height); /* selecting past top of window, scroll up! */ if (p_y < 0 && xtext->adj->value >= 0) { - if (!xtext->scroll_tag) - xtext->scroll_tag = g_timeout_add (100, - (GSourceFunc) - gtk_xtext_scrollup_timeout, - xtext); - return; + gtk_xtext_scrollup_timeout (xtext); } /* selecting past bottom of window, scroll down! */ - if (p_y > win_height && + else if (p_y > win_height && xtext->adj->value < (xtext->adj->upper - xtext->adj->page_size)) { - if (!xtext->scroll_tag) - xtext->scroll_tag = g_timeout_add (100, - (GSourceFunc) - gtk_xtext_scrolldown_timeout, - xtext); - return; + gtk_xtext_scrolldown_timeout (xtext); + } + else + { + moved = (int)xtext->adj->value - xtext->select_start_adj; + xtext->select_start_y -= (moved * xtext->fontsize); + xtext->select_start_adj = xtext->adj->value; + gtk_xtext_selection_draw (xtext, event, render); } - - moved = (int)xtext->adj->value - xtext->select_start_adj; - xtext->select_start_y -= (moved * xtext->fontsize); - xtext->select_start_adj = xtext->adj->value; - gtk_xtext_selection_draw (xtext, event, render); } static char * @@ -1883,7 +1948,7 @@ gtk_xtext_get_word (GtkXText * xtext, int x, int y, textentry ** ret_ent, if (ret_len) *ret_len = str - word; - return gtk_xtext_strip_color (word, len, xtext->scratch_buffer, NULL, NULL, FALSE); + return gtk_xtext_strip_color (word, len, xtext->scratch_buffer, NULL, NULL, NULL, FALSE); } #ifdef MOTION_MONITOR @@ -1941,7 +2006,7 @@ gtk_xtext_check_mark_stamp (GtkXText *xtext, GdkModifierType mask) { gboolean redraw = FALSE; - if ((mask & GDK_SHIFT_MASK)) + if (mask & GDK_SHIFT_MASK || prefs.autocopy_stamp) { if (!xtext->mark_stamp) { @@ -2106,7 +2171,16 @@ gtk_xtext_set_clip_owner (GtkWidget * xtext, GdkEventButton * event) free (str); } - gtk_selection_owner_set (xtext, GDK_SELECTION_PRIMARY, event->time); + if (event) + { + gtk_selection_owner_set (xtext, GDK_SELECTION_PRIMARY, event->time); + } +} + +void +gtk_xtext_copy_selection (GtkXText *xtext) +{ + gtk_xtext_set_clip_owner (xtext, NULL); } static void @@ -2174,6 +2248,11 @@ gtk_xtext_button_release (GtkWidget * widget, GdkEventButton * event) if (event->button == 1) { xtext->button_down = FALSE; + if (xtext->scroll_tag) + { + g_source_remove (xtext->scroll_tag); + xtext->scroll_tag = 0; + } gtk_grab_remove (widget); /*gdk_pointer_ungrab (0);*/ @@ -2182,9 +2261,12 @@ gtk_xtext_button_release (GtkWidget * widget, GdkEventButton * event) if (xtext->buffer->last_ent_start) { xtext->color_paste = FALSE; - if (event->state & GDK_CONTROL_MASK) + if (event->state & GDK_CONTROL_MASK || prefs.autocopy_color) xtext->color_paste = TRUE; - gtk_xtext_set_clip_owner (GTK_WIDGET (xtext), event); + if (prefs.autocopy_text) + { + gtk_xtext_set_clip_owner (GTK_WIDGET (xtext), event); + } } if (xtext->select_start_x == event->x && @@ -2249,7 +2331,10 @@ gtk_xtext_button_press (GtkWidget * widget, GdkEventButton * event) ent->mark_end = offset + len; gtk_xtext_selection_render (xtext, ent, offset, ent, offset + len); xtext->word_or_line_select = TRUE; - gtk_xtext_set_clip_owner (GTK_WIDGET (xtext), event); + if (prefs.autocopy_text) + { + gtk_xtext_set_clip_owner (GTK_WIDGET (xtext), event); + } } return FALSE; @@ -2265,7 +2350,10 @@ gtk_xtext_button_press (GtkWidget * widget, GdkEventButton * event) ent->mark_end = ent->str_len; gtk_xtext_selection_render (xtext, ent, 0, ent, ent->str_len); xtext->word_or_line_select = TRUE; - gtk_xtext_set_clip_owner (GTK_WIDGET (xtext), event); + if (prefs.autocopy_text) + { + gtk_xtext_set_clip_owner (GTK_WIDGET (xtext), event); + } } return FALSE; @@ -2391,7 +2479,7 @@ gtk_xtext_selection_get_text (GtkXText *xtext, int *len_ret) len = strlen (txt); } else { - stripped = gtk_xtext_strip_color (txt, strlen (txt), NULL, &len, 0, FALSE); + stripped = gtk_xtext_strip_color (txt, strlen (txt), NULL, &len, NULL, NULL, FALSE); free (txt); } @@ -2431,12 +2519,14 @@ gtk_xtext_selection_get (GtkWidget * widget, #if (GTK_MAJOR_VERSION == 2) && (GTK_MINOR_VERSION == 0) gdk_string_to_compound_text ( + stripped, &encoding, &format, &new_text, + &new_length); #else gdk_string_to_compound_text_for_display ( gdk_drawable_get_display (widget->window), -#endif stripped, &encoding, &format, &new_text, &new_length); +#endif gtk_selection_data_set (selection_data_ptr, encoding, format, new_text, new_length); gdk_free_compound_text (new_text); @@ -2551,19 +2641,26 @@ gtk_xtext_get_type (void) static unsigned char * gtk_xtext_strip_color (unsigned char *text, int len, unsigned char *outbuf, - int *newlen, int *mb_ret, int strip_hidden) + int *newlen, int *mb_ret, GSList **slp, int strip_hidden) { int i = 0; int rcol = 0, bgcol = 0; int hidden = FALSE; unsigned char *new_str; int mb = FALSE; + GSList *sl = NULL; + unsigned char *text0 = text; + int off1, len1; + offlen_t data; if (outbuf == NULL) new_str = malloc (len + 2); else new_str = outbuf; + off1 = 0; + len1 = 0; + data.u = 0; while (len > 0) { if (*text >= 128) @@ -2597,12 +2694,32 @@ gtk_xtext_strip_color (unsigned char *text, int len, unsigned char *outbuf, break; default: if (!(hidden && strip_hidden)) + { + if (text - text0 - off1 != len1) + { + if (len1) + { + data.o.off = off1; + data.o.len = len1; + sl = g_slist_append (sl, GUINT_TO_POINTER (data.u)); + len1 = 0; + } + off1 = text - text0; + } + len1++; new_str[i++] = *text; + } } } text++; len--; } + if (len1) + { + data.o.off = off1; + data.o.len = len1; + sl = g_slist_append (sl, GUINT_TO_POINTER (data.u)); + } new_str[i] = 0; @@ -2612,9 +2729,15 @@ gtk_xtext_strip_color (unsigned char *text, int len, unsigned char *outbuf, if (mb_ret != NULL) *mb_ret = mb; + if (slp) + *slp = sl; + else + g_slist_free (sl); + return new_str; } + /* GeEkMaN: converts mIRC control codes to literal control codes */ static char * @@ -2718,7 +2841,7 @@ gtk_xtext_text_width (GtkXText *xtext, unsigned char *text, int len, int new_len, mb; new_buf = gtk_xtext_strip_color (text, len, xtext->scratch_buffer, - &new_len, &mb, !xtext->ignore_hidden); + &new_len, &mb, NULL, !xtext->ignore_hidden); if (mb_ret) *mb_ret = mb; @@ -2884,6 +3007,63 @@ gtk_xtext_reset (GtkXText * xtext, int mark, int attribs) xtext->nc = 0; } +/* + * gtk_xtext_search_offset (buf, ent, off) -- + * Look for arg offset in arg textentry + * Return one or more flags: + * GTK_MATCH_MID if we are in a match + * GTK_MATCH_START if we're at the first byte of it + * GTK_MATCH_END if we're at the first byte past it + * GTK_MATCH_CUR if it is the current match + */ +#define GTK_MATCH_START 1 +#define GTK_MATCH_MID 2 +#define GTK_MATCH_END 4 +#define GTK_MATCH_CUR 8 +static int +gtk_xtext_search_offset (xtext_buffer *buf, textentry *ent, unsigned int off) +{ + GList *gl; + offsets_t o; + int flags = 0; + + for (gl = g_list_first (ent->marks); gl; gl = g_list_next (gl)) + { + o.u = GPOINTER_TO_UINT (gl->data); + if (off < o.o.start || off > o.o.end) + continue; + flags = GTK_MATCH_MID; + if (off == o.o.start) + flags |= GTK_MATCH_START; + if (off == o.o.end) + { + gl = g_list_next (gl); + if (gl) + { + o.u = GPOINTER_TO_UINT (gl->data); + if (off == o.o.start) /* If subseq match is adjacent */ + { + flags |= (gl == buf->curmark)? GTK_MATCH_CUR: 0; + } + else /* If subseq match is not adjacent */ + { + flags |= GTK_MATCH_END; + } + } + else /* If there is no subseq match */ + { + flags |= GTK_MATCH_END; + } + } + else if (gl == buf->curmark) /* If not yet at the end of this match */ + { + flags |= GTK_MATCH_CUR; + } + break; + } + return flags; +} + /* render a single line, which WONT wrap, and parse mIRC colors */ static int @@ -2898,6 +3078,9 @@ gtk_xtext_render_str (GtkXText * xtext, int y, textentry * ent, int offset; int mark = FALSE; int ret = 1; + int k; + int srch_underline = FALSE; + int srch_mark = FALSE; xtext->in_hilight = FALSE; @@ -3049,6 +3232,50 @@ gtk_xtext_render_str (GtkXText * xtext, int y, textentry * ent, } } + if (!left_only && !mark && + (k = gtk_xtext_search_offset (xtext->buffer, ent, offset + i))) + { + x += gtk_xtext_render_flush (xtext, x, y, pstr, j, gc, ent->mb); + pstr += j; + j = 0; + if (!(xtext->buffer->search_flags & highlight)) + { + if (k & GTK_MATCH_CUR) + { + xtext_set_bg (xtext, gc, XTEXT_MARK_BG); + xtext_set_fg (xtext, gc, XTEXT_MARK_FG); + xtext->backcolor = TRUE; + srch_mark = TRUE; + } else + { + xtext_set_bg (xtext, gc, xtext->col_back); + xtext_set_fg (xtext, gc, xtext->col_fore); + xtext->backcolor = (xtext->col_back != XTEXT_BG)? TRUE: FALSE; + srch_mark = FALSE; + } + } + else + { + xtext->underline = (k & GTK_MATCH_CUR)? TRUE: FALSE; + if (k & (GTK_MATCH_START | GTK_MATCH_MID)) + { + xtext_set_bg (xtext, gc, XTEXT_MARK_BG); + xtext_set_fg (xtext, gc, XTEXT_MARK_FG); + xtext->backcolor = TRUE; + srch_mark = TRUE; + } + if (k & GTK_MATCH_END) + { + xtext_set_bg (xtext, gc, xtext->col_back); + xtext_set_fg (xtext, gc, xtext->col_fore); + xtext->backcolor = (xtext->col_back != XTEXT_BG)? TRUE: FALSE; + srch_mark = FALSE; + xtext->underline = FALSE; + } + srch_underline = xtext->underline; + } + } + switch (str[i]) { case '\n': @@ -3198,6 +3425,11 @@ gtk_xtext_render_str (GtkXText * xtext, int y, textentry * ent, xtext_set_bg (xtext, gc, XTEXT_MARK_BG); xtext_set_fg (xtext, gc, XTEXT_MARK_FG); xtext->backcolor = TRUE; + if (srch_underline) + { + xtext->underline = FALSE; + srch_underline = FALSE; + } mark = TRUE; } @@ -3220,7 +3452,7 @@ gtk_xtext_render_str (GtkXText * xtext, int y, textentry * ent, if (j) x += gtk_xtext_render_flush (xtext, x, y, pstr, j, gc, ent->mb); - if (mark) + if (mark || srch_mark) { xtext_set_bg (xtext, gc, xtext->col_back); xtext_set_fg (xtext, gc, xtext->col_fore); @@ -3836,7 +4068,7 @@ gtk_xtext_load_trans (GtkXText * xtext) PaintDesktop (hdc); ReleaseDC (hwnd, hdc); - gdk_window_get_size (GTK_WIDGET (xtext)->window, &width, &height); + gdk_drawable_get_size (GTK_WIDGET (xtext)->window, &width, &height); img = gdk_image_get (GTK_WIDGET (xtext)->window, 0, 0, width+128, height); xtext->pixmap = win32_tint (xtext, img, img->width, img->height); @@ -4388,7 +4620,7 @@ gtk_xtext_save (GtkXText * xtext, int fh) while (ent) { buf = gtk_xtext_strip_color (ent->str, ent->str_len, NULL, - &newlen, NULL, FALSE); + &newlen, NULL, NULL, FALSE); write (fh, buf, newlen); write (fh, "\n", 1); free (buf); @@ -4775,6 +5007,11 @@ gtk_xtext_kill_ent (xtext_buffer *buffer, textentry *ent) if (buffer->marker_pos == ent) buffer->marker_pos = NULL; + if (ent->marks) + { + gtk_xtext_search_textentry_del (buffer, ent); + } + free (ent); return visible; } @@ -4839,7 +5076,23 @@ gtk_xtext_remove_bottom (xtext_buffer *buffer) else buffer->text_first = NULL; - gtk_xtext_kill_ent (buffer, ent); + if (gtk_xtext_kill_ent (buffer, ent)) + { + if (!buffer->xtext->add_io_tag) + { + /* remove scrolling events */ + if (buffer->xtext->io_tag) + { + g_source_remove (buffer->xtext->io_tag); + buffer->xtext->io_tag = 0; + } + buffer->xtext->force_render = TRUE; + buffer->xtext->add_io_tag = g_timeout_add (REFRESH_TIMEOUT * 2, + (GSourceFunc) + gtk_xtext_render_page_timeout, + buffer->xtext); + } + } } /* If lines=0 => clear all */ @@ -4874,6 +5127,8 @@ gtk_xtext_clear (xtext_buffer *buf, int lines) else { /* delete all */ + if (buf->search_found) + gtk_xtext_search_fini (buf); if (buf->xtext->auto_indent) buf->indent = MARGIN; buf->scrollbar_down = TRUE; @@ -4905,21 +5160,37 @@ static gboolean gtk_xtext_check_ent_visibility (GtkXText * xtext, textentry *find_ent, int add) { textentry *ent; - int lines_max; - int line = 0; + int lines; + xtext_buffer *buf = xtext->buffer; int width; int height; - gdk_drawable_get_size (GTK_WIDGET (xtext)->window, &width, &height); + if (find_ent == NULL) + { + return FALSE; + } - lines_max = ((height + xtext->pixel_offset) / xtext->fontsize) + add; - ent = xtext->buffer->pagetop_ent; + gdk_drawable_get_size (GTK_WIDGET (xtext)->window, &width, &height); - while (ent && line < lines_max) + ent = buf->pagetop_ent; + /* If top line not completely displayed return FALSE */ + if (ent == find_ent && buf->pagetop_subline > 0) { - if (find_ent == ent) + return FALSE; + } + /* Loop through line positions looking for find_ent */ + lines = ((height + xtext->pixel_offset) / xtext->fontsize) + buf->pagetop_subline + add; + while (ent) + { + lines -= ent->lines_taken; + if (lines <= 0) + { + return FALSE; + } + if (ent == find_ent) + { return TRUE; - line += ent->lines_taken; + } ent = ent->next; } @@ -4933,90 +5204,369 @@ gtk_xtext_check_marker_visibility (GtkXText * xtext) xtext->buffer->marker_seen = TRUE; } -textentry * -gtk_xtext_search (GtkXText * xtext, const gchar *text, textentry *start, gboolean case_match, gboolean backward) +static void +gtk_xtext_unstrip_color (gint start, gint end, GSList *slp, GList **gl, gint maxo) { - textentry *ent, *fent; - int line; - gchar *str, *nee, *hay; /* needle in haystack */ - - gtk_xtext_selection_clear_full (xtext->buffer); - xtext->buffer->last_ent_start = NULL; - xtext->buffer->last_ent_end = NULL; + gint off1, off2, curlen; + GSList *cursl; + offsets_t marks; - /* set up text comparand for Case Match or Ignore */ - if (case_match) - nee = g_strdup (text); - else - nee = g_utf8_casefold (text, strlen (text)); + off1 = 0; + curlen = 0; + cursl = slp; + while (cursl) + { + offlen_t ol; + ol.u = GPOINTER_TO_UINT(cursl->data); + if (start < ol.o.len) + { + off1 = ol.o.off + start; + break; + } + curlen += ol.o.len; + start -= ol.o.len; + end -= ol.o.len; + cursl = g_slist_next (cursl); + } - /* Validate that start gives a currently valid ent pointer */ - ent = xtext->buffer->text_first; - while (ent) + off2 = off1; + while (cursl) { - if (ent == start) + offlen_t ol; + ol.u = GPOINTER_TO_UINT(cursl->data); + if (end < ol.o.len) + { + off2 = ol.o.off + end; break; - ent = ent->next; + } + curlen += ol.o.len; + end -= ol.o.len; + cursl = g_slist_next (cursl); + } + if (!cursl) + { + off2 = maxo; } - if (!ent) - start = NULL; - /* Choose first ent to look at */ - if (start) - ent = backward? start->prev: start->next; - else - ent = backward? xtext->buffer->text_last: xtext->buffer->text_first; + marks.o.start = off1; + marks.o.end = off2; + *gl = g_list_append (*gl, GUINT_TO_POINTER (marks.u)); +} - /* Search from there to one end or the other until found */ - while (ent) +/* Search a single textentry for occurrence(s) of search arg string */ +static void +gtk_xtext_search_textentry (xtext_buffer *buf, textentry *ent, int pre) +{ + gchar *str; /* text string to be searched */ + GList *gl = NULL; + GSList *slp; + gint lstr; + + if (buf->search_text == NULL) { - /* If Case Ignore, fold before & free after calling strstr */ - if (case_match) - hay = g_strdup (ent->str); - else - hay = g_utf8_casefold (ent->str, strlen (ent->str)); - /* Try to find the needle in this haystack */ - str = g_strstr_len (hay, strlen (hay), nee); + return; + } + + str = gtk_xtext_strip_color (ent->str, ent->str_len, buf->xtext->scratch_buffer, + &lstr, NULL, &slp, !buf->xtext->ignore_hidden); + + /* Regular-expression matching --- */ + if (buf->search_flags & regexp) + { + GMatchInfo *gmi; + gint start, end; + + if (buf->search_re == NULL) + { + return; + } + g_regex_match (buf->search_re, str, 0, &gmi); + while (g_match_info_matches (gmi)) + { + g_match_info_fetch_pos (gmi, 0, &start, &end); + gtk_xtext_unstrip_color (start, end, slp, &gl, ent->str_len); + g_match_info_next (gmi, NULL); + } + g_match_info_free (gmi); + + /* Non-regular-expression matching --- */ + } else { + gchar *hay, *pos; + gint lhay, off, len; + gint match = buf->search_flags & case_match; + + hay = match? g_strdup (str): g_utf8_casefold (str, lstr); + lhay = strlen (hay); + off = 0; + + for (pos = hay, len = lhay; len; + off += buf->search_lnee, pos = hay + off, len = lhay - off) + { + str = g_strstr_len (pos, len, buf->search_nee); + if (str == NULL) + { + break; + } + off = str - hay; + gtk_xtext_unstrip_color (off, off + buf->search_lnee, + slp, &gl, ent->str_len); + } + g_free (hay); - if (str) - break; - ent = backward? ent->prev: ent->next; } - fent = ent; - /* Save distance to start, end of found string */ - if (ent) + /* Common processing --- */ + g_slist_free (slp); + ent->marks = gl; + if (gl) + { + buf->search_found = (pre? g_list_prepend: g_list_append) (buf->search_found, ent); + if (pre == FALSE && buf->hintsearch == NULL) + { + buf->hintsearch = ent; + } + } + return; +} + +/* Free all search information for a textentry */ +static void +gtk_xtext_search_textentry_del (xtext_buffer *buf, textentry *ent) +{ + g_list_free (ent->marks); + ent->marks = NULL; + if (buf->cursearch && buf->cursearch->data == ent) + { + buf->cursearch = NULL; + buf->curmark = NULL; + } + if (buf->pagetop_ent == ent) + { + buf->pagetop_ent = NULL; + } + if (buf->hintsearch == ent) + { + buf->hintsearch = NULL; + } + buf->search_found = g_list_remove (buf->search_found, ent); +} + +/* Used only by glist_foreach */ +static void +gtk_xtext_search_textentry_fini (gpointer entp, gpointer dummy) +{ + textentry *ent = entp; + + g_list_free (ent->marks); + ent->marks = NULL; +} + +/* Free all search information for all textentrys and the xtext_buffer */ +static void +gtk_xtext_search_fini (xtext_buffer *buf) +{ + g_list_foreach (buf->search_found, gtk_xtext_search_textentry_fini, 0); + g_list_free (buf->search_found); + buf->search_found = NULL; + g_free (buf->search_text); + buf->search_text = NULL; + g_free (buf->search_nee); + buf->search_nee = NULL; + buf->search_flags = 0; + buf->cursearch = NULL; + buf->curmark = NULL; + if (buf->search_re) + { + g_regex_unref (buf->search_re); + buf->search_re = NULL; + } +} + +/* Returns TRUE if the base search information exists and is still okay to use */ +static gboolean +gtk_xtext_search_init (xtext_buffer *buf, const gchar *text, gtk_xtext_search_flags flags, GError **perr) +{ + /* Of the five flags, backward and highlight_all do not need a new search */ + if (buf->search_found && + strcmp (buf->search_text, text) == 0 && + (buf->search_flags & case_match) == (flags & case_match) && + (buf->search_flags & follow) == (flags & follow) && + (buf->search_flags & regexp) == (flags & regexp)) + { + return TRUE; + } + buf->hintsearch = buf->cursearch? buf->cursearch->data: NULL; + gtk_xtext_search_fini (buf); + buf->search_text = g_strdup (text); + if (flags & regexp) + { + buf->search_re = g_regex_new (text, (flags & case_match)? 0: G_REGEX_CASELESS, 0, perr); + if (perr && *perr) + { + return FALSE; + } + } + else + { + if (flags & case_match) + { + buf->search_nee = g_strdup (text); + } + else + { + buf->search_nee = g_utf8_casefold (text, strlen (text)); + } + buf->search_lnee = strlen (buf->search_nee); + } + buf->search_flags = flags; + buf->cursearch = NULL; + buf->curmark = NULL; + return FALSE; +} + +#define BACKWARD (flags & backward) +#define FIRSTLAST(lp) (BACKWARD? g_list_last(lp): g_list_first(lp)) +#define NEXTPREVIOUS(lp) (BACKWARD? g_list_previous(lp): g_list_next(lp)) +textentry * +gtk_xtext_search (GtkXText * xtext, const gchar *text, gtk_xtext_search_flags flags, GError **perr) +{ + textentry *ent = NULL; + xtext_buffer *buf = xtext->buffer; + GList *gl; + + if (buf->text_first == NULL) { - ent->mark_start = str - hay; - ent->mark_end = ent->mark_start + strlen (nee); + return NULL; + } - /* is the match visible? Might need to scroll */ - if (!gtk_xtext_check_ent_visibility (xtext, ent, 0)) + /* If the text arg is NULL, one of these has been toggled: highlight follow */ + if (text == NULL) /* Here on highlight or follow toggle */ + { + gint oldfollow = buf->search_flags & follow; + gint newfollow = flags & follow; + + /* If "Follow" has just been checked, search possible new textentries --- */ + if (newfollow && (newfollow != oldfollow)) { - ent = xtext->buffer->text_first; - line = 0; - while (ent) + gl = g_list_last (buf->search_found); + ent = gl? gl->data: buf->text_first; + for (; ent; ent = ent->next) { - line += ent->lines_taken; - ent = ent->next; - if (ent == fent) - break; + gtk_xtext_search_textentry (buf, ent, FALSE); + } + } + buf->search_flags = flags; + ent = buf->pagetop_ent; + } + + /* if the text arg is "", the reset button has been clicked or Control-Shift-F has been hit */ + else if (text[0] == 0) /* Let a null string do a reset. */ + { + gtk_xtext_search_fini (buf); + } + + /* If the text arg is neither NULL nor "", it's the search string */ + else + { + if (gtk_xtext_search_init (buf, text, flags, perr) == FALSE) /* If a new search: */ + { + if (perr && *perr) + { + return NULL; + } + for (ent = buf->text_first; ent; ent = ent->next) + { + gtk_xtext_search_textentry (buf, ent, TRUE); + } + buf->search_found = g_list_reverse (buf->search_found); + } + + /* Now base search results are in place. */ + + if (buf->search_found) + { + /* If we're in the midst of moving among found items */ + if (buf->cursearch) + { + ent = buf->cursearch->data; + buf->curmark = NEXTPREVIOUS (buf->curmark); + if (buf->curmark == NULL) + { + /* We've returned all the matches for this textentry. */ + buf->cursearch = NEXTPREVIOUS (buf->cursearch); + if (buf->cursearch) + { + ent = buf->cursearch->data; + buf->curmark = FIRSTLAST (ent->marks); + } + else /* We've returned all the matches for all textentries */ + { + ent = NULL; + } + } + } +#if 0 + /* If user changed the search, let's look starting where he was */ + else if (buf->hintsearch) + { + for (ent = buf->hintsearch; ent; ent = BACKWARD? ent->prev: ent->next) + if (ent->marks) + break; + if (ent == NULL) + for (ent = buf->hintsearch; ent; ent = BACKWARD? ent->next: ent->prev) + if (ent->marks) + break; + if (ent) + { + buf->cursearch = g_list_find (buf->search_found, ent); + buf->curmark = FIRSTLAST (ent->marks); + } + } +#endif + /* This is a fresh search */ + else + { + buf->cursearch = FIRSTLAST (buf->search_found); + ent = buf->cursearch->data; + buf->curmark = FIRSTLAST (ent->marks); } - while (line > xtext->adj->upper - xtext->adj->page_size) - line--; - if (backward) - line -= xtext->adj->page_size - ent->lines_taken; - xtext->adj->value = line; - xtext->buffer->scrollbar_down = FALSE; - gtk_adjustment_changed (xtext->adj); } } + buf->hintsearch = ent; + + if (!gtk_xtext_check_ent_visibility (xtext, ent, 1)) + { + GtkAdjustment *adj = xtext->adj; + float value; + + buf->pagetop_ent = NULL; + for (value = 0, ent = buf->text_first; + ent && ent != buf->hintsearch; ent = ent->next) + { + value += ent->lines_taken; + } + if (value > adj->upper - adj->page_size) + { + value = adj->upper - adj->page_size; + } + else if ((flags & backward) && ent) + { + value -= adj->page_size - ent->lines_taken; + if (value < 0) + { + value = 0; + } + } + gtk_adjustment_set_value (adj, value); + } - g_free (nee); gtk_widget_queue_draw (GTK_WIDGET (xtext)); - return fent; + return buf->hintsearch; } +#undef BACKWARD +#undef FIRSTLAST +#undef NEXTPREVIOUS static int gtk_xtext_render_page_timeout (GtkXText * xtext) @@ -5079,6 +5629,7 @@ gtk_xtext_append_entry (xtext_buffer *buf, textentry * ent, time_t stamp) ent->mark_start = -1; ent->mark_end = -1; ent->next = NULL; + ent->marks = NULL; if (ent->indent < MARGIN) ent->indent = MARGIN; /* 2 pixels is the left margin */ @@ -5097,10 +5648,11 @@ gtk_xtext_append_entry (xtext_buffer *buf, textentry * ent, time_t stamp) if (buf->reset_marker_pos || ((buf->marker_pos == NULL || buf->marker_seen) && (buf->xtext->buffer != buf || #if GTK_CHECK_VERSION(2,4,0) - !gtk_window_has_toplevel_focus (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (buf->xtext))))))) + !gtk_window_has_toplevel_focus (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (buf->xtext)))) #else - !(GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (buf->xtext))))->has_focus))) + !(GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (buf->xtext))))->has_focus #endif + ))) { buf->marker_pos = ent; dontscroll (buf); /* force scrolling off */ @@ -5140,6 +5692,10 @@ gtk_xtext_append_entry (xtext_buffer *buf, textentry * ent, time_t stamp) if (buf->old_value < 0) buf->old_value = 0; } + if (buf->search_flags & follow) + { + gtk_xtext_search_textentry (buf, ent, FALSE); + } } /* the main two public functions */ @@ -5466,6 +6022,11 @@ gtk_xtext_buffer_free (xtext_buffer *buf) if (buf->xtext->selection_buffer == buf) buf->xtext->selection_buffer = NULL; + if (buf->search_found) + { + gtk_xtext_search_fini (buf); + } + ent = buf->text_first; while (ent) { diff --git a/src/fe-gtk/xtext.h b/src/fe-gtk/xtext.h index a37ddc32..6c126346 100644 --- a/src/fe-gtk/xtext.h +++ b/src/fe-gtk/xtext.h @@ -43,6 +43,13 @@ typedef struct _GtkXText GtkXText; typedef struct _GtkXTextClass GtkXTextClass; typedef struct textentry textentry; +typedef enum gtk_xtext_search_flags_e { + case_match = 1, + backward = 2, + highlight = 4, + follow = 8, + regexp = 16 +} gtk_xtext_search_flags; typedef struct { GtkXText *xtext; /* attached to this widget */ @@ -77,6 +84,16 @@ typedef struct { unsigned int grid_dirty:1; unsigned int marker_seen:1; unsigned int reset_marker_pos:1; + + GList *search_found; /* list of textentries where search found strings */ + gchar *search_text; /* desired text to search for */ + gchar *search_nee; /* prepared needle to look in haystack for */ + gint search_lnee; /* its length */ + gtk_xtext_search_flags search_flags; /* match, bwd, highlight */ + GList *cursearch; /* GList whose 'data' pts to current textentry */ + GList *curmark; /* current item in ent->marks */ + GRegex *search_re; /* Compiled regular expression */ + textentry *hintsearch; /* textentry found for last search */ } xtext_buffer; struct _GtkXText @@ -247,7 +264,7 @@ void gtk_xtext_clear (xtext_buffer *buf, int lines); void gtk_xtext_save (GtkXText * xtext, int fh); void gtk_xtext_refresh (GtkXText * xtext, int do_trans); int gtk_xtext_lastlog (xtext_buffer *out, xtext_buffer *search_area, int (*cmp_func) (char *, void *userdata), void *userdata); -textentry *gtk_xtext_search (GtkXText * xtext, const gchar *text, textentry *start, gboolean case_match, gboolean backward); +textentry *gtk_xtext_search (GtkXText * xtext, const gchar *text, gtk_xtext_search_flags flags, GError **err); void gtk_xtext_reset_marker_pos (GtkXText *xtext); void gtk_xtext_check_marker_visibility(GtkXText *xtext); @@ -270,6 +287,7 @@ void gtk_xtext_set_wordwrap (GtkXText *xtext, gboolean word_wrap); xtext_buffer *gtk_xtext_buffer_new (GtkXText *xtext); void gtk_xtext_buffer_free (xtext_buffer *buf); void gtk_xtext_buffer_show (GtkXText *xtext, xtext_buffer *buf, int render); +void gtk_xtext_copy_selection (GtkXText *xtext); GType gtk_xtext_get_type (void); #endif |