diff options
author | Stefano Karapetsas <[email protected]> | 2011-12-14 10:13:54 +0100 |
---|---|---|
committer | Stefano Karapetsas <[email protected]> | 2011-12-14 10:13:54 +0100 |
commit | ef0467789bfc8406b57ba553e4d59f4d6c3f9be8 (patch) | |
tree | 09d541636a16cb38448fe6183289ebdc3080c1bf /mate-dictionary/src | |
download | mate-utils-ef0467789bfc8406b57ba553e4d59f4d6c3f9be8.tar.bz2 mate-utils-ef0467789bfc8406b57ba553e4d59f4d6c3f9be8.tar.xz |
Moved from Mate-Extra repository
Diffstat (limited to 'mate-dictionary/src')
22 files changed, 7788 insertions, 0 deletions
diff --git a/mate-dictionary/src/Makefile.am b/mate-dictionary/src/Makefile.am new file mode 100644 index 00000000..ddde3d26 --- /dev/null +++ b/mate-dictionary/src/Makefile.am @@ -0,0 +1,82 @@ +NULL = + +INCLUDES = \ + -DPREFIX=\""$(prefix)"\" \ + -DSYSCONFDIR=\""$(sysconfdir)"\" \ + -DLIBDIR=\""$(libdir)"\" \ + -DDATADIR=\""$(datadir)"\" \ + -DUIDATADIR=\""$(datadir)/mate-2.0/ui"\" \ + -DPKGDATADIR=\""$(datadir)/mate-dictionary"\" \ + -DMATELOCALEDIR=\""$(prefix)/$(DATADIRNAME)/locale"\" \ + $(NULL) + +bin_PROGRAMS = mate-dictionary + +if BUILD_GDICT_APPLET +libexec_PROGRAMS = mate-dictionary-applet +endif + +mate_dictionary_SOURCES = \ + gdict-about.c \ + gdict-about.h \ + gdict-app.c \ + gdict-app.h \ + gdict-common.c \ + gdict-common.h \ + gdict-pref-dialog.c \ + gdict-pref-dialog.h \ + gdict-print.c \ + gdict-print.h \ + gdict-sidebar.c \ + gdict-sidebar.h \ + gdict-source-dialog.c \ + gdict-source-dialog.h \ + gdict-window.c \ + gdict-window.h \ + main.c \ + $(NULL) +mate_dictionary_CFLAGS = \ + -I$(top_builddir)/mate-dictionary \ + -I$(top_srcdir)/mate-dictionary \ + $(MATE_UTILS_CFLAGS) \ + $(NULL) +mate_dictionary_LDADD = \ + -lm \ + $(top_builddir)/mate-dictionary/libgdict/libmatedict.la \ + $(MATE_UTILS_LIBS) \ + $(NULL) + +if BUILD_GDICT_APPLET + +mate_dictionary_applet_SOURCES = \ + gdict-about.c \ + gdict-about.h \ + gdict-aligned-window.c \ + gdict-aligned-window.h \ + gdict-applet.c \ + gdict-applet.h \ + gdict-common.c \ + gdict-common.h \ + gdict-pref-dialog.c \ + gdict-pref-dialog.h \ + gdict-print.c \ + gdict-print.h \ + gdict-sidebar.c \ + gdict-sidebar.h \ + gdict-source-dialog.c \ + gdict-source-dialog.h \ + $(NULL) +mate_dictionary_applet_CFLAGS = \ + -I$(top_builddir)/mate-dictionary \ + -I$(top_srcdir)/mate-dictionary \ + $(MATE_UTILS_CFLAGS) \ + $(APPLET_CFLAGS) \ + $(NULL) +mate_dictionary_applet_LDADD = \ + -lm \ + $(top_builddir)/mate-dictionary/libgdict/libmatedict.la \ + $(MATE_UTILS_LIBS) \ + $(APPLET_LIBS) \ + $(NULL) + +endif # BUILD_GDICT_APPLET diff --git a/mate-dictionary/src/gdict-about.c b/mate-dictionary/src/gdict-about.c new file mode 100644 index 00000000..dc74b560 --- /dev/null +++ b/mate-dictionary/src/gdict-about.c @@ -0,0 +1,90 @@ +/* gdict-about.c - GtkAboutDialog wrapper + * + * This file is part of MATE Dictionary + * + * Copyright (C) 2005 Emmanuele Bassi + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <glib/gi18n.h> +#include <gdk-pixbuf/gdk-pixbuf.h> + +#include "gdict-about.h" + +void +gdict_show_about_dialog (GtkWidget *parent) +{ + const gchar *authors[] = { + "Mike Hughes <[email protected]>", + "Spiros Papadimitriou <[email protected]>", + "Bradford Hovinen <[email protected]>", + "Vincent Noel <[email protected]>", + "Emmanuele Bassi <[email protected]>", + NULL + }; + + const gchar *documenters[] = { + "Sun MATE Documentation Team <[email protected]>", + "John Fleck <[email protected]>", + "Emmanuele Bassi <[email protected]>", + NULL + }; + + const gchar *translator_credits = _("translator-credits"); + const gchar *copyright = "Copyright \xc2\xa9 2005-2006 Emmanuele Bassi"; + const gchar *comments = _("Look up words in dictionaries"); + + const gchar *license = + "This program is free software; you can redistribute it and/or " + "modify it under the terms of the GNU General Public License as " + "published by the Free Software Foundation; either version 2 of " + "the License, or (at your option) any later version.\n" + "\n" + "This program is distributed in the hope that it will be useful, " + "but WITHOUT ANY WARRANTY; without even the implied warranty of " + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU " + "General Public License for more details.\n" + "\n" + "You should have received a copy of the GNU General Public License " + "along with this program; if not, write to the Free Software " + "Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA " + "02111-1307, USA.\n"; + + g_return_if_fail (GTK_IS_WIDGET (parent)); + + gtk_show_about_dialog (GTK_IS_WINDOW (parent) ? GTK_WINDOW (parent) : NULL, + "name", _("Dictionary"), + "version", VERSION, + "copyright", copyright, + "comments", comments, + "authors", authors, + "documenters", documenters, + "translator-credits", translator_credits, + "logo-icon-name", "accessories-dictionary", + "license", license, + "wrap-license", TRUE, + "screen", gtk_widget_get_screen (parent), + NULL); +} diff --git a/mate-dictionary/src/gdict-about.h b/mate-dictionary/src/gdict-about.h new file mode 100644 index 00000000..2e6467cf --- /dev/null +++ b/mate-dictionary/src/gdict-about.h @@ -0,0 +1,30 @@ +/* gdict-about.h - GtkAboutDialog wrapper + * + * This file is part of MATE Dictionary + * + * Copyright (C) 2005 Emmanuele Bassi + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef __GDICT_ABOUT_H__ +#define __GDICT_ABOUT_H__ + +#include <gtk/gtk.h> + +void gdict_show_about_dialog (GtkWidget *parent); + +#endif /* __GDICT_ABOUT_H__ */ diff --git a/mate-dictionary/src/gdict-aligned-window.c b/mate-dictionary/src/gdict-aligned-window.c new file mode 100644 index 00000000..c7ddf83e --- /dev/null +++ b/mate-dictionary/src/gdict-aligned-window.c @@ -0,0 +1,348 @@ +/* gdict-aligned-window.c - Popup window aligned to a widget + * + * Copyright (c) 2005-2006 Emmanuele Bassi <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Ported from Seth Nickell's Python class: + * Copyright (c) 2003 Seth Nickell + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <gtk/gtk.h> + +#include "gdict-aligned-window.h" + +#define GDICT_ALIGNED_WINDOW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GDICT_TYPE_ALIGNED_WINDOW, GdictAlignedWindowPrivate)) + +struct _GdictAlignedWindowPrivate +{ + GtkWidget *align_widget; + + guint motion_id; +}; + +enum +{ + PROP_0, + + PROP_ALIGN_WIDGET +}; + +static void gdict_aligned_window_finalize (GObject *object); +static void gdict_aligned_window_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); +static void gdict_aligned_window_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); + +static void gdict_aligned_window_realize (GtkWidget *widget); +static void gdict_aligned_window_show (GtkWidget *widget); + +static gboolean gdict_aligned_window_motion_notify_cb (GtkWidget *widget, + GdkEventMotion *event, + GdictAlignedWindow *aligned_window); + + +G_DEFINE_TYPE (GdictAlignedWindow, gdict_aligned_window, GTK_TYPE_WINDOW); + + + +static void +gdict_aligned_window_class_init (GdictAlignedWindowClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + gobject_class->set_property = gdict_aligned_window_set_property; + gobject_class->get_property = gdict_aligned_window_get_property; + gobject_class->finalize = gdict_aligned_window_finalize; + + widget_class->realize = gdict_aligned_window_realize; + widget_class->show = gdict_aligned_window_show; + + g_object_class_install_property (gobject_class, PROP_ALIGN_WIDGET, + g_param_spec_object ("align-widget", + "Align Widget", + "The widget the window should align to", + GTK_TYPE_WIDGET, + G_PARAM_READWRITE)); + + g_type_class_add_private (klass, sizeof (GdictAlignedWindowPrivate)); +} + +static void +gdict_aligned_window_init (GdictAlignedWindow *aligned_window) +{ + GdictAlignedWindowPrivate *priv = GDICT_ALIGNED_WINDOW_GET_PRIVATE (aligned_window); + GtkWindow *window = GTK_WINDOW (aligned_window); + + aligned_window->priv = priv; + + priv->align_widget = NULL; + priv->motion_id = 0; + + /* set window properties */ +#if 0 + gtk_window_set_modal (window, TRUE); +#endif + gtk_window_set_decorated (window, FALSE); + gtk_window_set_type_hint (window, GDK_WINDOW_TYPE_HINT_DOCK); +} + +static void +gdict_aligned_window_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GdictAlignedWindow *aligned_window = GDICT_ALIGNED_WINDOW (object); + + switch (prop_id) + { + case PROP_ALIGN_WIDGET: + g_value_set_object (value, aligned_window->priv->align_widget); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gdict_aligned_window_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GdictAlignedWindow *aligned_window = GDICT_ALIGNED_WINDOW (object); + + switch (prop_id) + { + case PROP_ALIGN_WIDGET: + gdict_aligned_window_set_widget (aligned_window, + g_value_get_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gdict_aligned_window_position (GdictAlignedWindow *window) +{ + GdictAlignedWindowPrivate *priv; + GtkWidget *align_widget; + gint our_width, our_height; + gint entry_x, entry_y, entry_width, entry_height; + gint x, y; + GdkGravity gravity = GDK_GRAVITY_NORTH_WEST; + GdkWindow *gdk_window; + + g_assert (GDICT_IS_ALIGNED_WINDOW (window)); + priv = window->priv; + + if (!priv->align_widget) + return; + + align_widget = priv->align_widget; + gdk_window = gtk_widget_get_window (align_widget); + + gdk_flush (); + + gdk_window_get_geometry (gtk_widget_get_window (GTK_WIDGET (window)), + NULL, + NULL, + &our_width, + &our_height, + NULL); + + /* stick, skip taskbar and pager */ + gtk_window_stick (GTK_WINDOW (window)); + gtk_window_set_skip_taskbar_hint (GTK_WINDOW (window), TRUE); + gtk_window_set_skip_pager_hint (GTK_WINDOW (window), TRUE); + + /* make sure the align_widget is realized before we do anything */ + gtk_widget_realize (align_widget); + + /* get the positional and dimensional attributes of the align widget */ + gdk_window_get_origin (gdk_window, + &entry_x, + &entry_y); + gdk_window_get_geometry (gdk_window, + NULL, + NULL, + &entry_width, + &entry_height, + NULL); + + if (entry_x + our_width < gdk_screen_width ()) + x = entry_x + 1; + else + { + x = entry_x + entry_width - our_width - 1; + + gravity = GDK_GRAVITY_NORTH_EAST; + } + + if (entry_y + entry_height + our_height < gdk_screen_height ()) + y = entry_y + entry_height - 1; + else + { + y = entry_y - our_height + 1; + + if (gravity == GDK_GRAVITY_NORTH_EAST) + gravity = GDK_GRAVITY_SOUTH_EAST; + else + gravity = GDK_GRAVITY_SOUTH_WEST; + } + + gtk_window_set_gravity (GTK_WINDOW (window), gravity); + gtk_window_move (GTK_WINDOW (window), x, y); +} + +static void +gdict_aligned_window_realize (GtkWidget *widget) +{ + GTK_WIDGET_CLASS (gdict_aligned_window_parent_class)->realize (widget); + + gdict_aligned_window_position (GDICT_ALIGNED_WINDOW (widget)); +} + +static void +gdict_aligned_window_show (GtkWidget *widget) +{ + gdict_aligned_window_position (GDICT_ALIGNED_WINDOW (widget)); + + GTK_WIDGET_CLASS (gdict_aligned_window_parent_class)->show (widget); +} + +static void +gdict_aligned_window_finalize (GObject *object) +{ + G_OBJECT_CLASS (gdict_aligned_window_parent_class)->finalize (object); +} + +static gboolean +gdict_aligned_window_motion_notify_cb (GtkWidget *widget, + GdkEventMotion *event, + GdictAlignedWindow *aligned_window) +{ + GtkAllocation alloc; + GdkRectangle rect; + + gtk_widget_get_allocation (GTK_WIDGET (aligned_window), &alloc); + + rect.x = 0; + rect.y = 0; + rect.width = alloc.width; + rect.height = alloc.height; + + gdk_window_invalidate_rect (gtk_widget_get_window (GTK_WIDGET (aligned_window)), + &rect, + FALSE); + + return FALSE; +} + + +/** + * gdict_aligned_window_new: + * @align_widget: a #GtkWidget to which the window should align + * + * Creates a new window, aligned to a previously created widget. + * + * Return value: a new #GdictAlignedWindow + */ +GtkWidget * +gdict_aligned_window_new (GtkWidget *align_widget) +{ + return g_object_new (GDICT_TYPE_ALIGNED_WINDOW, + "align-widget", align_widget, + NULL); +} + +/** + * gdict_aligned_window_set_widget: + * @aligned_window: a #GdictAlignedWindow + * @align_widget: the #GtkWidget @aligned_window should align to + * + * Sets @align_widget as the #GtkWidget to which @aligned_window should + * align. + * + * Note that @align_widget must have a #GdkWindow in order to + * #GdictAlignedWindow to work. + */ +void +gdict_aligned_window_set_widget (GdictAlignedWindow *aligned_window, + GtkWidget *align_widget) +{ + GdictAlignedWindowPrivate *priv; + + g_return_if_fail (GDICT_IS_ALIGNED_WINDOW (aligned_window)); + g_return_if_fail (GTK_IS_WIDGET (align_widget)); + +#if 0 + if (GTK_WIDGET_NO_WINDOW (align_widget)) + { + g_warning ("Attempting to set a widget of class '%s' as the " + "align widget, but widgets of this class does not " + "have a GdkWindow.", + g_type_name (G_OBJECT_TYPE (align_widget))); + + return; + } +#endif + + priv = GDICT_ALIGNED_WINDOW_GET_PRIVATE (aligned_window); + + if (priv->align_widget) + { + g_signal_handler_disconnect (priv->align_widget, priv->motion_id); + priv->align_widget = NULL; + } + + priv->align_widget = align_widget; + priv->motion_id = g_signal_connect (priv->align_widget, "motion-notify-event", + G_CALLBACK (gdict_aligned_window_motion_notify_cb), + aligned_window); +} + +/** + * gdict_aligned_window_get_widget: + * @aligned_window: a #GdictAlignedWindow + * + * Retrieves the #GtkWidget to which @aligned_window is aligned to. + * + * Return value: the align widget. + */ +GtkWidget * +gdict_aligned_window_get_widget (GdictAlignedWindow *aligned_window) +{ + g_return_val_if_fail (GDICT_IS_ALIGNED_WINDOW (aligned_window), NULL); + + return aligned_window->priv->align_widget; +} diff --git a/mate-dictionary/src/gdict-aligned-window.h b/mate-dictionary/src/gdict-aligned-window.h new file mode 100644 index 00000000..8647f272 --- /dev/null +++ b/mate-dictionary/src/gdict-aligned-window.h @@ -0,0 +1,69 @@ +/* gdict-aligned-widget.h - Popup window aligned to a widget + * + * Copyright (c) 2005 Emmanuele Bassi <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Ported from Seth Nickell's Python class: + * Copyright (c) 2003 Seth Nickell + */ + +#ifndef __GDICT_ALIGNED_WINDOW_H__ +#define __GDICT_ALIGNED_WINDOW_H__ + +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +#define GDICT_TYPE_ALIGNED_WINDOW (gdict_aligned_window_get_type ()) +#define GDICT_ALIGNED_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDICT_TYPE_ALIGNED_WINDOW, GdictAlignedWindow)) +#define GDICT_IS_ALIGNED_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDICT_TYPE_ALIGNED_WINDOW)) +#define GDICT_ALIGNED_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDICT_TYPE_ALIGNED_WINDOW, GdictAlignedWindowClass)) +#define GDICT_IS_ALIGNED_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDICT_TYPE_ALIGNED_WINDOW)) +#define GDICT_ALIGNED_WINDOW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDICT_TYPE_ALIGNED_WINDOW, GdictAlignedWindowClass)) + +typedef struct _GdictAlignedWindow GdictAlignedWindow; +typedef struct _GdictAlignedWindowClass GdictAlignedWindowClass; +typedef struct _GdictAlignedWindowPrivate GdictAlignedWindowPrivate; + +struct _GdictAlignedWindow +{ + /*< private >*/ + GtkWindow parent_instance; + + GdictAlignedWindowPrivate *priv; +}; + +struct _GdictAlignedWindowClass +{ + /*< private >*/ + GtkWindowClass parent_class; + + void (*_gdict_reserved1) (void); + void (*_gdict_reserved2) (void); + void (*_gdict_reserved3) (void); + void (*_gdict_reserved4) (void); +}; + +GType gdict_aligned_window_get_type (void) G_GNUC_CONST; + +GtkWidget *gdict_aligned_window_new (GtkWidget *align_widget); +void gdict_aligned_window_set_widget (GdictAlignedWindow *aligned_window, + GtkWidget *align_widget); +GtkWidget *gdict_aligned_window_get_widget (GdictAlignedWindow *aligned_window); + +G_END_DECLS + +#endif diff --git a/mate-dictionary/src/gdict-app.c b/mate-dictionary/src/gdict-app.c new file mode 100644 index 00000000..eab6bbd8 --- /dev/null +++ b/mate-dictionary/src/gdict-app.c @@ -0,0 +1,475 @@ +/* gdict-app.c - main application class + * + * This file is part of MATE Dictionary + * + * Copyright (C) 2005 Emmanuele Bassi + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> +#include <sys/stat.h> + +#include <gtk/gtk.h> +#include <glib.h> +#include <glib/gi18n.h> + +#include "gdict-common.h" +#include "gdict-pref-dialog.h" +#include "gdict-app.h" + +static GdictApp *singleton = NULL; + + +struct _GdictAppClass +{ + GObjectClass parent_class; +}; + + + +G_DEFINE_TYPE (GdictApp, gdict_app, G_TYPE_OBJECT); + + +static void +gdict_app_finalize (GObject *object) +{ + GdictApp *app = GDICT_APP (object); + + if (app->loader) + g_object_unref (app->loader); + + app->current_window = NULL; + + g_slist_foreach (app->windows, + (GFunc) gtk_widget_destroy, + NULL); + g_slist_free (app->windows); + + g_slist_foreach (app->lookup_words, (GFunc) g_free, NULL); + g_slist_free (app->lookup_words); + + g_slist_foreach (app->match_words, (GFunc) g_free, NULL); + g_slist_free (app->match_words); + + g_free (app->database); + g_free (app->source_name); + + G_OBJECT_CLASS (gdict_app_parent_class)->finalize (object); +} + +static void +gdict_app_class_init (GdictAppClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->finalize = gdict_app_finalize; +} + +static void +gdict_app_init (GdictApp *app) +{ + app->windows = NULL; + app->current_window = NULL; +} + +static void +gdict_window_destroy_cb (GtkWidget *widget, + gpointer user_data) +{ + GdictWindow *window = GDICT_WINDOW (widget); + GdictApp *app = GDICT_APP (user_data); + + g_assert (GDICT_IS_APP (app)); + + app->windows = g_slist_remove (app->windows, window); + + if (window == app->current_window) + app->current_window = app->windows ? app->windows->data : NULL; + + if (app->windows == NULL) + gtk_main_quit (); +} + +static void +gdict_window_created_cb (GdictWindow *parent, + GdictWindow *new_window, + gpointer user_data) +{ + GdictApp *app = GDICT_APP (user_data); + + /* this might seem convoluted - but it's necessary, since I don't want + * GdictWindow to know about the GdictApp singleton. every time a new + * window is created by a GdictWindow, it will register its "child window" + * here; the lifetime handlers will check every child window created and + * destroyed, and will add/remove it to the windows list accordingly + */ + g_signal_connect (new_window, "created", + G_CALLBACK (gdict_window_created_cb), app); + g_signal_connect (new_window, "destroy", + G_CALLBACK (gdict_window_destroy_cb), app); + + if (gtk_window_get_group (GTK_WINDOW (parent))) + gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (parent)), + GTK_WINDOW (new_window)); + + app->windows = g_slist_prepend (app->windows, new_window); + app->current_window = new_window; +} + +static void +gdict_create_window (GdictApp *app) +{ + GSList *l; + + if (!singleton->lookup_words && !singleton->match_words) + { + GtkWidget *window; + + window = gdict_window_new (GDICT_WINDOW_ACTION_CLEAR, + singleton->loader, + singleton->source_name, + NULL); + g_signal_connect (window, "created", + G_CALLBACK (gdict_window_created_cb), app); + g_signal_connect (window, "destroy", + G_CALLBACK (gdict_window_destroy_cb), app); + + app->windows = g_slist_prepend (app->windows, window); + app->current_window = GDICT_WINDOW (window); + + gtk_widget_show (window); + + return; + } + + for (l = singleton->lookup_words; l != NULL; l = l->next) + { + gchar *word = l->data; + GtkWidget *window; + + window = gdict_window_new (GDICT_WINDOW_ACTION_LOOKUP, + singleton->loader, + singleton->source_name, + word); + + g_signal_connect (window, "created", + G_CALLBACK (gdict_window_created_cb), app); + g_signal_connect (window, "destroy", + G_CALLBACK (gdict_window_destroy_cb), app); + + app->windows = g_slist_prepend (app->windows, window); + app->current_window = GDICT_WINDOW (window); + + gtk_widget_show (window); + } + + for (l = singleton->match_words; l != NULL; l = l->next) + { + gchar *word = l->data; + GtkWidget *window; + + window = gdict_window_new (GDICT_WINDOW_ACTION_MATCH, + singleton->loader, + singleton->source_name, + word); + + g_signal_connect (window, "created", + G_CALLBACK (gdict_window_created_cb), app); + g_signal_connect (window, "destroy", + G_CALLBACK (gdict_window_destroy_cb), app); + + app->windows = g_slist_prepend (app->windows, window); + app->current_window = GDICT_WINDOW (window); + + gtk_widget_show (window); + } +} + +static void +definition_found_cb (GdictContext *context, + GdictDefinition *definition, + gpointer user_data) +{ + /* Translators: the first is the word found, the second is the + * database name and the last is the definition's text; please + * keep the new lines. */ + g_print (_("Definition for '%s'\n" + " From '%s':\n" + "\n" + "%s\n"), + gdict_definition_get_word (definition), + gdict_definition_get_database (definition), + gdict_definition_get_text (definition)); +} + +static void +error_cb (GdictContext *context, + const GError *error, + gpointer user_data) +{ + g_print (_("Error: %s\n"), error->message); + + gtk_main_quit (); +} + +static void +lookup_end_cb (GdictContext *context, + gpointer user_data) +{ + GdictApp *app = GDICT_APP (user_data); + + app->remaining_words -= 1; + + if (app->remaining_words == 0) + gtk_main_quit (); +} + +static void +gdict_look_up_word_and_quit (GdictApp *app) +{ + GdictSource *source; + GdictContext *context; + GSList *l; + + if ((!app->lookup_words) || (!app->match_words)) + { + g_print (_("See mate-dictionary --help for usage\n")); + + gdict_cleanup (); + exit (1); + } + + if (app->source_name) + source = gdict_source_loader_get_source (app->loader, app->source_name); + else + source = gdict_source_loader_get_source (app->loader, GDICT_DEFAULT_SOURCE_NAME); + + if (!source) + { + g_warning (_("Unable to find a suitable dictionary source")); + + gdict_cleanup (); + exit (1); + } + + /* we'll just use this one context, so we can destroy it along with + * the source that contains it + */ + context = gdict_source_peek_context (source); + g_assert (GDICT_IS_CONTEXT (context)); + + g_signal_connect (context, "definition-found", + G_CALLBACK (definition_found_cb), app); + g_signal_connect (context, "error", + G_CALLBACK (error_cb), app); + g_signal_connect (context, "lookup-end", + G_CALLBACK (lookup_end_cb), app); + + app->remaining_words = 0; + for (l = app->lookup_words; l != NULL; l = l->next) + { + gchar *word = l->data; + GError *err = NULL; + + app->remaining_words += 1; + + gdict_context_define_word (context, + app->database, + word, + &err); + + if (err) + { + g_warning (_("Error while looking up the definition of \"%s\":\n%s"), + word, + err->message); + + g_error_free (err); + } + } + + gtk_main (); + + g_object_unref (source); + + gdict_cleanup (); + exit (0); +} + +void +gdict_init (int *argc, char ***argv) +{ + GError *mateconf_error, *err = NULL; + GOptionContext *context; + gchar *loader_path; + gchar **lookup_words = NULL; + gchar **match_words = NULL; + gchar *database = NULL; + gchar *strategy = NULL; + gchar *source_name = NULL; + gboolean no_window = FALSE; + gboolean list_sources = FALSE; + + const GOptionEntry gdict_app_goptions[] = + { + { "look-up", 0, 0, G_OPTION_ARG_STRING_ARRAY, &lookup_words, + N_("Words to look up"), N_("word") }, + { "match", 0, 0, G_OPTION_ARG_STRING_ARRAY, &match_words, + N_("Words to match"), N_("word") }, + { "source", 's', 0, G_OPTION_ARG_STRING, &source_name, + N_("Dictionary source to use"), N_("source") }, + { "list-sources", 'l', 0, G_OPTION_ARG_NONE, &list_sources, + N_("Show available dictionary sources"), NULL }, + { "no-window", 'n', 0, G_OPTION_ARG_NONE, &no_window, + N_("Print result to the console"), NULL }, + { "database", 'D', 0, G_OPTION_ARG_STRING, &database, + N_("Database to use"), N_("db") }, + { "strategy", 'S', 0, G_OPTION_ARG_STRING, &strategy, + N_("Strategy to use"), N_("strat") }, + { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &lookup_words, + N_("Words to look up"), N_("word") }, + { NULL }, + }; + + /* we must have GLib's type system up and running in order to create the + * singleton object for mate-dictionary; thus, we can't rely on + * mate_program_init() calling g_type_init() for us. + */ + g_type_init (); + + g_assert (singleton == NULL); + + singleton = GDICT_APP (g_object_new (GDICT_TYPE_APP, NULL)); + g_assert (GDICT_IS_APP (singleton)); + + /* create the new option context */ + context = g_option_context_new (_(" - Look up words in dictionaries")); + + g_option_context_set_translation_domain (context, GETTEXT_PACKAGE); + g_option_context_add_main_entries (context, gdict_app_goptions, GETTEXT_PACKAGE); + g_option_context_add_group (context, gdict_get_option_group ()); + g_option_context_add_group (context, gtk_get_option_group (TRUE)); + + g_option_context_parse (context, argc, argv, &err); + if (err) + { + g_critical ("Failed to parse argument: %s", err->message); + g_error_free (err); + g_option_context_free (context); + gdict_cleanup (); + + exit (1); + } + + g_set_application_name (_("Dictionary")); + gtk_window_set_default_icon_name ("accessories-dictionary"); + + if (!gdict_create_data_dir ()) + { + gdict_cleanup (); + + exit (1); + } + + mateconf_error = NULL; + singleton->mateconf_client = mateconf_client_get_default (); + mateconf_client_add_dir (singleton->mateconf_client, + GDICT_MATECONF_DIR, + MATECONF_CLIENT_PRELOAD_ONELEVEL, + &mateconf_error); + if (mateconf_error) + { + g_warning ("Unable to access MateConf: %s\n", mateconf_error->message); + + g_error_free (mateconf_error); + g_object_unref (singleton->mateconf_client); + } + + /* add user's path for fetching dictionary sources */ + singleton->loader = gdict_source_loader_new (); + loader_path = gdict_get_data_dir (); + gdict_source_loader_add_search_path (singleton->loader, loader_path); + g_free (loader_path); + + if (lookup_words) + { + gsize i; + gsize length = g_strv_length (lookup_words); + + for (i = 0; i < length; i++) + singleton->lookup_words = g_slist_prepend (singleton->lookup_words, + g_strdup (lookup_words[i])); + } + + if (match_words) + { + gsize i; + gsize length = g_strv_length (match_words); + + for (i = 0; i < length; i++) + singleton->match_words = g_slist_prepend (singleton->match_words, + g_strdup (match_words[i])); + } + + if (database) + singleton->database = g_strdup (database); + + if (source_name) + singleton->source_name = g_strdup (source_name); + + if (no_window) + singleton->no_window = TRUE; + + if (list_sources) + singleton->list_sources = TRUE; +} + +void +gdict_main (void) +{ + if (!singleton) + { + g_warning ("You must initialize GdictApp using gdict_init()\n"); + return; + } + + if (!singleton->no_window) + gdict_create_window (singleton); + else + gdict_look_up_word_and_quit (singleton); + + gtk_main (); +} + +void +gdict_cleanup (void) +{ + if (!singleton) + { + g_warning ("You must initialize GdictApp using gdict_init()\n"); + return; + } + + g_object_unref (singleton); +} diff --git a/mate-dictionary/src/gdict-app.h b/mate-dictionary/src/gdict-app.h new file mode 100644 index 00000000..c66f8958 --- /dev/null +++ b/mate-dictionary/src/gdict-app.h @@ -0,0 +1,72 @@ +/* gdict-app.h - main application class + * + * This file is part of MATE Dictionary + * + * Copyright (C) 2005 Emmanuele Bassi + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef __GDICT_APP_H__ +#define __GDICT_APP_H__ + +#include <gtk/gtk.h> +#include <mateconf/mateconf-client.h> +#include <libgdict/gdict.h> + +#include "gdict-window.h" + +G_BEGIN_DECLS + +#define GDICT_TYPE_APP (gdict_app_get_type ()) +#define GDICT_APP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDICT_TYPE_APP, GdictApp)) +#define GDICT_IS_APP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDICT_TYPE_APP)) + +typedef struct _GdictApp GdictApp; +typedef struct _GdictAppClass GdictAppClass; + + +struct _GdictApp +{ + GObject parent_instance; + + MateConfClient *mateconf_client; + + GSList *lookup_words; + GSList *match_words; + gint remaining_words; + + gchar *database; + gchar *source_name; + gboolean no_window; + gboolean list_sources; + + GdictSourceLoader *loader; + + GdictWindow *current_window; + GSList *windows; +}; + + +GType gdict_app_get_type (void) G_GNUC_CONST; + +void gdict_init (int *argc, char ***argv); +void gdict_main (void); +void gdict_cleanup (void); + +G_END_DECLS + +#endif /* __GDICT_APP_H__ */ diff --git a/mate-dictionary/src/gdict-applet.c b/mate-dictionary/src/gdict-applet.c new file mode 100644 index 00000000..442381a6 --- /dev/null +++ b/mate-dictionary/src/gdict-applet.c @@ -0,0 +1,1333 @@ +/* gdict-applet.c - MATE Dictionary Applet + * + * Copyright (c) 2005 Emmanuele Bassi <[email protected]> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <glib/gi18n.h> +#include <gdk/gdkkeysyms.h> +#include <gdk-pixbuf/gdk-pixbuf.h> +#include <gtk/gtk.h> +#include <mateconf/mateconf-client.h> + +#include "gdict-applet.h" +#include "gdict-about.h" +#include "gdict-pref-dialog.h" +#include "gdict-print.h" +#include "gdict-common.h" +#include "gdict-aligned-window.h" + +#define GDICT_APPLET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDICT_TYPE_APPLET, GdictAppletClass)) +#define GDICT_APPLET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDICT_TYPE_APPLET, GdictAppletClass)) + +struct _GdictAppletClass +{ + MatePanelAppletClass parent_class; +}; + +#define GDICT_APPLET_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GDICT_TYPE_APPLET, GdictAppletPrivate)) + +struct _GdictAppletPrivate +{ + guint size; + GtkOrientation orient; + + MateConfClient *mateconf_client; + guint notify_id; + guint font_notify_id; + + gchar *database; + gchar *strategy; + gchar *source_name; + gchar *print_font; + gchar *defbox_font; + + gchar *word; + GdictContext *context; + guint lookup_start_id; + guint lookup_end_id; + guint error_id; + + GdictSourceLoader *loader; + + GtkWidget *box; + GtkWidget *toggle; + GtkWidget *image; + GtkWidget *entry; + GtkWidget *window; + GtkWidget *frame; + GtkWidget *defbox; + + guint idle_draw_id; + + GdkPixbuf *icon; + + gint window_width; + gint window_height; + + guint is_window_showing : 1; +}; + +#define WINDOW_MIN_WIDTH 300 +#define WINDOW_MIN_HEIGHT 200 +#define WINDOW_NUM_COLUMNS 47 +#define WINDOW_NUM_ROWS 20 + +G_DEFINE_TYPE (GdictApplet, gdict_applet, PANEL_TYPE_APPLET); + + +static const GtkTargetEntry drop_types[] = +{ + { "text/plain", 0, 0 }, + { "TEXT", 0, 0 }, + { "STRING", 0, 0 }, + { "UTF8_STRING", 0, 0 }, +}; +static const guint n_drop_types = G_N_ELEMENTS (drop_types); + + +static void +set_atk_name_description (GtkWidget *widget, + const char *name, + const char *description) +{ + AtkObject *aobj; + + aobj = gtk_widget_get_accessible (widget); + if (!GTK_IS_ACCESSIBLE (aobj)) + return; + + atk_object_set_name (aobj, name); + atk_object_set_description (aobj, description); +} + +static void +set_window_default_size (GdictApplet *applet) +{ + GdictAppletPrivate *priv = applet->priv; + GtkWidget *widget, *defbox; + gint width, height; + gint font_size; + GdkScreen *screen; + gint monitor_num; + GtkRequisition req; + GdkRectangle monitor; + + if (!priv->window) + return; + + widget = priv->window; + defbox = priv->defbox; + + /* Size based on the font size */ + font_size = pango_font_description_get_size (gtk_widget_get_style (defbox)->font_desc); + font_size = PANGO_PIXELS (font_size); + + width = font_size * WINDOW_NUM_COLUMNS; + height = font_size * WINDOW_NUM_ROWS; + + /* Use at least the requisition size of the window... */ + gtk_widget_size_request (widget, &req); + width = MAX (width, req.width); + height = MAX (height, req.height); + + /* ... but make it no larger than half the monitor size */ + screen = gtk_widget_get_screen (widget); + monitor_num = gdk_screen_get_monitor_at_window (screen, + gtk_widget_get_window (widget)); + + gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor); + + width = MIN (width, monitor.width / 2); + height = MIN (height, monitor.height / 2); + + /* Set size */ + gtk_widget_set_size_request (priv->frame, width, height); +} + +static void +clear_cb (GtkWidget *widget, + GdictApplet *applet) +{ + GdictAppletPrivate *priv = applet->priv; + + gtk_entry_set_text (GTK_ENTRY (priv->entry), ""); + + if (!priv->defbox) + return; + + gdict_defbox_clear (GDICT_DEFBOX (priv->defbox)); +} + +static void +print_cb (GtkWidget *widget, + GdictApplet *applet) +{ + GdictAppletPrivate *priv = applet->priv; + + if (!priv->defbox) + return; + + gdict_show_print_dialog (GTK_WINDOW (priv->window), + GDICT_DEFBOX (priv->defbox)); +} + +static void +save_cb (GtkWidget *widget, + GdictApplet *applet) +{ + GdictAppletPrivate *priv = applet->priv; + GtkWidget *dialog; + + if (!priv->defbox) + return; + + dialog = gtk_file_chooser_dialog_new (_("Save a Copy"), + GTK_WINDOW (priv->window), + GTK_FILE_CHOOSER_ACTION_SAVE, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, + NULL); + gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog), TRUE); + + /* default to user's $HOME */ + gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), g_get_home_dir ()); + gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog), _("Untitled document")); + + if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) + { + gchar *filename; + gchar *text; + gsize len; + GError *write_error = NULL; + + filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)); + + text = gdict_defbox_get_text (GDICT_DEFBOX (priv->defbox), &len); + + g_file_set_contents (filename, + text, + len, + &write_error); + if (write_error) + { + gchar *message; + + message = g_strdup_printf (_("Error while writing to '%s'"), filename); + + gdict_show_error_dialog (GTK_WINDOW (priv->window), + message, + write_error->message); + + g_error_free (write_error); + g_free (message); + } + + g_free (text); + g_free (filename); + } + + gtk_widget_destroy (dialog); +} + +static void +gdict_applet_set_menu_items_sensitive (GdictApplet *applet, + gboolean is_sensitive) +{ + MateComponentUIComponent *popup_component; + + popup_component = mate_panel_applet_get_popup_component (MATE_PANEL_APPLET (applet)); + if (!MATECOMPONENT_IS_UI_COMPONENT (popup_component)) + return; + + matecomponent_ui_component_set_prop (popup_component, + "/commands/Clear", + "sensitive", (is_sensitive ? "1" : "0"), + NULL); + matecomponent_ui_component_set_prop (popup_component, + "/commands/Print", + "sensitive", (is_sensitive ? "1" : "0"), + NULL); + matecomponent_ui_component_set_prop (popup_component, + "/commands/Save", + "sensitive", (is_sensitive ? "1" : "0"), + NULL); +} + +static gboolean +window_key_press_event_cb (GtkWidget *widget, + GdkEventKey *event, + gpointer user_data) +{ + GdictApplet *applet = GDICT_APPLET (user_data); + + if (event->keyval == GDK_Escape) + { + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (applet->priv->toggle), FALSE); + gtk_toggle_button_toggled (GTK_TOGGLE_BUTTON (applet->priv->toggle)); + + return TRUE; + } + else if ((event->keyval == GDK_l) && + (event->state & GDK_CONTROL_MASK)) + { + gtk_widget_grab_focus (applet->priv->entry); + + return TRUE; + } + + return FALSE; +} + +static void +window_show_cb (GtkWidget *window, + GdictApplet *applet) +{ + set_window_default_size (applet); +} + +static void +gdict_applet_build_window (GdictApplet *applet) +{ + GdictAppletPrivate *priv = applet->priv; + GtkWidget *window; + GtkWidget *frame; + GtkWidget *vbox; + GtkWidget *bbox; + GtkWidget *button; + + if (!priv->entry) + { + g_warning ("No entry widget defined"); + + return; + } + + window = gdict_aligned_window_new (priv->toggle); + g_signal_connect (window, "key-press-event", + G_CALLBACK (window_key_press_event_cb), + applet); + g_signal_connect (window, "show", + G_CALLBACK (window_show_cb), + applet); + + frame = gtk_frame_new (NULL); + gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN); + gtk_container_add (GTK_CONTAINER (window), frame); + gtk_widget_show (frame); + priv->frame = frame; + + vbox = gtk_vbox_new (FALSE, 12); + gtk_container_set_border_width (GTK_CONTAINER (vbox), 6); + gtk_container_add (GTK_CONTAINER (frame), vbox); + gtk_widget_show (vbox); + + priv->defbox = gdict_defbox_new (); + if (priv->context) + gdict_defbox_set_context (GDICT_DEFBOX (priv->defbox), priv->context); + + gtk_box_pack_start (GTK_BOX (vbox), priv->defbox, TRUE, TRUE, 0); + gtk_widget_show (priv->defbox); + gtk_widget_set_can_focus (priv->defbox, TRUE); + gtk_widget_set_can_default (priv->defbox, TRUE); + + bbox = gtk_hbutton_box_new (); + gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), GTK_BUTTONBOX_END); + gtk_box_set_spacing (GTK_BOX (bbox), 6); + gtk_box_pack_end (GTK_BOX (vbox), bbox, FALSE, FALSE, 0); + gtk_widget_show (bbox); + + button = gtk_button_new_from_stock (GTK_STOCK_CLEAR); + gtk_widget_set_tooltip_text (button, _("Clear the definitions found")); + set_atk_name_description (button, + _("Clear definition"), + _("Clear the text of the definition")); + + g_signal_connect (button, "clicked", G_CALLBACK (clear_cb), applet); + gtk_box_pack_start (GTK_BOX (bbox), button, FALSE, FALSE, 0); + gtk_widget_show (button); + + button = gtk_button_new_from_stock (GTK_STOCK_PRINT); + gtk_widget_set_tooltip_text (button, _("Print the definitions found")); + set_atk_name_description (button, + _("Print definition"), + _("Print the text of the definition")); + + g_signal_connect (button, "clicked", G_CALLBACK (print_cb), applet); + gtk_box_pack_start (GTK_BOX (bbox), button, FALSE, FALSE, 0); + gtk_widget_show (button); + + button = gtk_button_new_from_stock (GTK_STOCK_SAVE); + gtk_widget_set_tooltip_text (button, _("Save the definitions found")); + set_atk_name_description (button, + _("Save definition"), + _("Save the text of the definition to a file")); + + g_signal_connect (button, "clicked", G_CALLBACK (save_cb), applet); + gtk_box_pack_start (GTK_BOX (bbox), button, FALSE, FALSE, 0); + gtk_widget_show (button); + + gtk_window_set_default (GTK_WINDOW (window), priv->defbox); + + priv->window = window; + priv->is_window_showing = FALSE; +} + +static gboolean +gdict_applet_icon_toggled_cb (GtkWidget *widget, + GdictApplet *applet) +{ + GdictAppletPrivate *priv = applet->priv; + + if (!priv->window) + gdict_applet_build_window (applet); + + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget))) + { + gtk_window_set_screen (GTK_WINDOW (priv->window), + gtk_widget_get_screen (GTK_WIDGET (applet))); + gtk_window_present (GTK_WINDOW (priv->window)); + gtk_widget_grab_focus (priv->defbox); + + priv->is_window_showing = TRUE; + } + else + { + /* force hiding the find pane */ + gdict_defbox_set_show_find (GDICT_DEFBOX (priv->defbox), FALSE); + + gtk_widget_grab_focus (priv->entry); + gtk_widget_hide (priv->window); + + priv->is_window_showing = FALSE; + } + + return FALSE; +} + +static void +gdict_applet_entry_activate_cb (GtkWidget *widget, + GdictApplet *applet) +{ + GdictAppletPrivate *priv = applet->priv; + gchar *text; + + text = gtk_editable_get_chars (GTK_EDITABLE (widget), 0, -1); + if (!text) + return; + + g_free (priv->word); + priv->word = text; + + if (!priv->window) + gdict_applet_build_window (applet); + + gdict_defbox_lookup (GDICT_DEFBOX (priv->defbox), priv->word); +} + +static gboolean +gdict_applet_entry_key_press_cb (GtkWidget *widget, + GdkEventKey *event, + gpointer user_data) +{ + GdictAppletPrivate *priv = GDICT_APPLET (user_data)->priv; + + if (event->keyval == GDK_Escape) + { + if (priv->is_window_showing) + { + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->toggle), FALSE); + gtk_toggle_button_toggled (GTK_TOGGLE_BUTTON (priv->toggle)); + + return TRUE; + } + } + else if (event->keyval == GDK_Tab) + { + if (priv->is_window_showing) + gtk_widget_grab_focus (priv->defbox); + } + + return FALSE; +} + +static gboolean +gdict_applet_icon_button_press_event_cb (GtkWidget *widget, + GdkEventButton *event, + GdictApplet *applet) +{ + GdictAppletPrivate *priv = applet->priv; + + /* we don't want to block the applet's popup menu unless the + * user is toggling the button + */ + if (event->button != 1) + g_signal_stop_emission_by_name (priv->toggle, "button-press-event"); + + return FALSE; +} + +static gboolean +gdict_applet_entry_button_press_event_cb (GtkWidget *widget, + GdkEventButton *event, + GdictApplet *applet) +{ + mate_panel_applet_request_focus (MATE_PANEL_APPLET (applet), event->time); + + return FALSE; +} + +static gboolean +gdict_applet_draw (GdictApplet *applet) +{ + GdictAppletPrivate *priv = applet->priv; + GtkWidget *box; + GtkWidget *hbox; + gchar *text = NULL; + + if (priv->entry) + text = gtk_editable_get_chars (GTK_EDITABLE (priv->entry), 0, -1); + + if (priv->box) + gtk_widget_destroy (priv->box); + + switch (priv->orient) + { + case GTK_ORIENTATION_VERTICAL: + box = gtk_vbox_new (FALSE, 0); + break; + case GTK_ORIENTATION_HORIZONTAL: + box = gtk_hbox_new (FALSE, 0); + break; + default: + g_assert_not_reached (); + break; + } + + gtk_container_add (GTK_CONTAINER (applet), box); + gtk_widget_show (box); + + /* toggle button */ + priv->toggle = gtk_toggle_button_new (); + gtk_widget_set_tooltip_text (priv->toggle, _("Click to view the dictionary window")); + set_atk_name_description (priv->toggle, + _("Toggle dictionary window"), + _("Show or hide the definition window")); + + gtk_button_set_relief (GTK_BUTTON (priv->toggle), + GTK_RELIEF_NONE); + g_signal_connect (priv->toggle, "toggled", + G_CALLBACK (gdict_applet_icon_toggled_cb), + applet); + g_signal_connect (priv->toggle, "button-press-event", + G_CALLBACK (gdict_applet_icon_button_press_event_cb), + applet); + gtk_box_pack_start (GTK_BOX (box), priv->toggle, FALSE, FALSE, 0); + gtk_widget_show (priv->toggle); + + hbox = gtk_hbox_new (FALSE, 0); + gtk_container_set_border_width (GTK_CONTAINER (hbox), 0); + gtk_container_add (GTK_CONTAINER (priv->toggle), hbox); + gtk_widget_show (hbox); + + if (priv->icon) + { + GdkPixbuf *scaled; + + priv->image = gtk_image_new (); + gtk_image_set_pixel_size (GTK_IMAGE (priv->image), priv->size - 10); + + scaled = gdk_pixbuf_scale_simple (priv->icon, + priv->size - 5, + priv->size - 5, + GDK_INTERP_BILINEAR); + + gtk_image_set_from_pixbuf (GTK_IMAGE (priv->image), scaled); + g_object_unref (scaled); + + gtk_box_pack_start (GTK_BOX (hbox), priv->image, FALSE, FALSE, 0); + + gtk_widget_show (priv->image); + } + else + { + priv->image = gtk_image_new (); + + gtk_image_set_pixel_size (GTK_IMAGE (priv->image), priv->size - 10); + gtk_image_set_from_stock (GTK_IMAGE (priv->image), + GTK_STOCK_MISSING_IMAGE, + -1); + + gtk_box_pack_start (GTK_BOX (hbox), priv->image, FALSE, FALSE, 0); + gtk_widget_show (priv->image); + } + + /* entry */ + priv->entry = gtk_entry_new (); + gtk_widget_set_tooltip_text (priv->entry, _("Type the word you want to look up")); + set_atk_name_description (priv->entry, + _("Dictionary entry"), + _("Look up words in dictionaries")); + + gtk_editable_set_editable (GTK_EDITABLE (priv->entry), TRUE); + gtk_entry_set_width_chars (GTK_ENTRY (priv->entry), 12); + g_signal_connect (priv->entry, "activate", + G_CALLBACK (gdict_applet_entry_activate_cb), + applet); + g_signal_connect (priv->entry, "button-press-event", + G_CALLBACK (gdict_applet_entry_button_press_event_cb), + applet); + g_signal_connect (priv->entry, "key-press-event", + G_CALLBACK (gdict_applet_entry_key_press_cb), + applet); + gtk_box_pack_end (GTK_BOX (box), priv->entry, FALSE, FALSE, 0); + gtk_widget_show (priv->entry); + + if (text) + { + gtk_entry_set_text (GTK_ENTRY (priv->entry), text); + + g_free (text); + } + + priv->box = box; + +#if 0 + gtk_widget_grab_focus (priv->entry); +#endif + + gtk_widget_show_all (GTK_WIDGET (applet)); + + return FALSE; +} + +static void +gdict_applet_queue_draw (GdictApplet *applet) +{ + if (!applet->priv->idle_draw_id) + applet->priv->idle_draw_id = g_idle_add ((GSourceFunc) gdict_applet_draw, + applet); +} + +static void +gdict_applet_lookup_start_cb (GdictContext *context, + GdictApplet *applet) +{ + GdictAppletPrivate *priv = applet->priv; + + if (!priv->window) + gdict_applet_build_window (applet); + + if (!priv->is_window_showing) + { + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->toggle), TRUE); + + gtk_window_present (GTK_WINDOW (priv->window)); + gtk_widget_grab_focus (priv->defbox); + + priv->is_window_showing = TRUE; + } + + gdict_applet_set_menu_items_sensitive (applet, FALSE); +} + +static void +gdict_applet_lookup_end_cb (GdictContext *context, + GdictApplet *applet) +{ + gdict_applet_set_menu_items_sensitive (applet, TRUE); + + gtk_window_present (GTK_WINDOW (applet->priv->window)); +} + +static void +gdict_applet_error_cb (GdictContext *context, + const GError *error, + GdictApplet *applet) +{ + /* disable menu items */ + gdict_applet_set_menu_items_sensitive (applet, FALSE); +} + +static void +gdict_applet_cmd_lookup (MateComponentUIComponent *component, + GdictApplet *applet, + const gchar *cname) +{ + GdictAppletPrivate *priv = applet->priv; + gchar *text = NULL;; + + text = gtk_editable_get_chars (GTK_EDITABLE (priv->entry), 0, -1); + if (!text) + return; + + g_free (priv->word); + priv->word = text; + + if (!priv->window) + gdict_applet_build_window (applet); + + gdict_defbox_lookup (GDICT_DEFBOX (priv->defbox), priv->word); +} + +static void +gdict_applet_cmd_clear (MateComponentUIComponent *component, + GdictApplet *applet, + const gchar *cname) +{ + clear_cb (NULL, applet); +} + +static void +gdict_applet_cmd_print (MateComponentUIComponent *component, + GdictApplet *applet, + const gchar *cname) +{ + print_cb (NULL, applet); +} + +static void +gdict_applet_cmd_preferences (MateComponentUIComponent *component, + GdictApplet *applet, + const gchar *cname) +{ + gdict_show_pref_dialog (GTK_WIDGET (applet), + _("Dictionary Preferences"), + applet->priv->loader); +} + +static void +gdict_applet_cmd_about (MateComponentUIComponent *component, + GdictApplet *applet, + const gchar *cname) +{ + gdict_show_about_dialog (GTK_WIDGET (applet)); +} + +static void +gdict_applet_cmd_help (MateComponentUIComponent *component, + GdictApplet *applet, + const gchar *cname) +{ + GError *err = NULL; + + gtk_show_uri (gtk_widget_get_screen (GTK_WIDGET (applet)), + "ghelp:mate-dictionary#mate-dictionary-applet", + gtk_get_current_event_time (), &err); + + if (err) + { + gdict_show_error_dialog (NULL, + _("There was an error while displaying help"), + err->message); + g_error_free (err); + } +} + +static void +gdict_applet_change_background (MatePanelApplet *applet, + MatePanelAppletBackgroundType type, + GdkColor *color, + GdkPixmap *pixmap) +{ + if (MATE_PANEL_APPLET_CLASS (gdict_applet_parent_class)->change_background) + MATE_PANEL_APPLET_CLASS (gdict_applet_parent_class)->change_background (applet, + type, + color, + pixmap); +} + +static void +gdict_applet_change_orient (MatePanelApplet *applet, + MatePanelAppletOrient orient) +{ + GdictAppletPrivate *priv = GDICT_APPLET (applet)->priv; + guint new_size; + GtkAllocation allocation; + + gtk_widget_get_allocation (GTK_WIDGET (applet), &allocation); + switch (orient) + { + case MATE_PANEL_APPLET_ORIENT_LEFT: + case MATE_PANEL_APPLET_ORIENT_RIGHT: + priv->orient = GTK_ORIENTATION_VERTICAL; + new_size = allocation.width; + break; + case MATE_PANEL_APPLET_ORIENT_UP: + case MATE_PANEL_APPLET_ORIENT_DOWN: + priv->orient = GTK_ORIENTATION_HORIZONTAL; + new_size = allocation.height; + break; + } + + if (new_size != priv->size) + priv->size = new_size; + + gdict_applet_queue_draw (GDICT_APPLET (applet)); + + if (MATE_PANEL_APPLET_CLASS (gdict_applet_parent_class)->change_orient) + MATE_PANEL_APPLET_CLASS (gdict_applet_parent_class)->change_orient (applet, + orient); +} + +static void +gdict_applet_size_allocate (GtkWidget *widget, + GdkRectangle *allocation) +{ + GdictApplet *applet = GDICT_APPLET (widget); + GdictAppletPrivate *priv = applet->priv; + guint new_size; + + if (priv->orient == GTK_ORIENTATION_HORIZONTAL) + new_size = allocation->height; + else + new_size = allocation->width; + + if (priv->size != new_size) + { + priv->size = new_size; + + gtk_image_set_pixel_size (GTK_IMAGE (priv->image), priv->size - 10); + + /* re-scale the icon, if it was found */ + if (priv->icon) + { + GdkPixbuf *scaled; + + scaled = gdk_pixbuf_scale_simple (priv->icon, + priv->size - 5, + priv->size - 5, + GDK_INTERP_BILINEAR); + + gtk_image_set_from_pixbuf (GTK_IMAGE (priv->image), scaled); + g_object_unref (scaled); + } + } + + if (GTK_WIDGET_CLASS (gdict_applet_parent_class)->size_allocate) + GTK_WIDGET_CLASS (gdict_applet_parent_class)->size_allocate (widget, + allocation); +} + +static void +gdict_applet_style_set (GtkWidget *widget, + GtkStyle *old_style) +{ + if (GTK_WIDGET_CLASS (gdict_applet_parent_class)->style_set) + GTK_WIDGET_CLASS (gdict_applet_parent_class)->style_set (widget, + old_style); +#if 0 + set_window_default_size (GDICT_APPLET (widget)); +#endif +} + +static void +gdict_applet_set_database (GdictApplet *applet, + const gchar *database) +{ + GdictAppletPrivate *priv = applet->priv; + + g_free (priv->database); + + if (database) + priv->database = g_strdup (database); + else + priv->database = gdict_mateconf_get_string_with_default (priv->mateconf_client, + GDICT_MATECONF_DATABASE_KEY, + GDICT_DEFAULT_DATABASE); + if (priv->defbox) + gdict_defbox_set_database (GDICT_DEFBOX (priv->defbox), + priv->database); +} + +static void +gdict_applet_set_strategy (GdictApplet *applet, + const gchar *strategy) +{ + GdictAppletPrivate *priv = applet->priv; + + g_free (priv->strategy); + + if (strategy) + priv->strategy = g_strdup (strategy); + else + priv->strategy = gdict_mateconf_get_string_with_default (priv->mateconf_client, + GDICT_MATECONF_STRATEGY_KEY, + GDICT_DEFAULT_STRATEGY); +} + +static GdictContext * +get_context_from_loader (GdictApplet *applet) +{ + GdictAppletPrivate *priv = applet->priv; + GdictSource *source; + GdictContext *retval; + + if (!priv->source_name) + priv->source_name = g_strdup (GDICT_DEFAULT_SOURCE_NAME); + + source = gdict_source_loader_get_source (priv->loader, + priv->source_name); + if (!source) + { + gchar *detail; + + detail = g_strdup_printf (_("No dictionary source available with name '%s'"), + priv->source_name); + + gdict_show_error_dialog (NULL, + _("Unable to find dictionary source"), + NULL); + + g_free (detail); + + return NULL; + } + + gdict_applet_set_database (applet, gdict_source_get_database (source)); + gdict_applet_set_strategy (applet, gdict_source_get_strategy (source)); + + retval = gdict_source_get_context (source); + if (!retval) + { + gchar *detail; + + detail = g_strdup_printf (_("No context available for source '%s'"), + gdict_source_get_description (source)); + + gdict_show_error_dialog (NULL, + _("Unable to create a context"), + detail); + + g_free (detail); + g_object_unref (source); + + return NULL; + } + + g_object_unref (source); + + return retval; +} + +static void +gdict_applet_set_print_font (GdictApplet *applet, + const gchar *print_font) +{ + GdictAppletPrivate *priv = applet->priv; + + g_free (priv->print_font); + + if (print_font) + priv->print_font = g_strdup (print_font); + else + priv->print_font = gdict_mateconf_get_string_with_default (priv->mateconf_client, + GDICT_MATECONF_PRINT_FONT_KEY, + GDICT_DEFAULT_PRINT_FONT); +} + +static void +gdict_applet_set_defbox_font (GdictApplet *applet, + const gchar *defbox_font) +{ + GdictAppletPrivate *priv = applet->priv; + + g_free (priv->defbox_font); + + if (defbox_font) + priv->defbox_font = g_strdup (defbox_font); + else + priv->defbox_font = gdict_mateconf_get_string_with_default (priv->mateconf_client, + DOCUMENT_FONT_KEY, + GDICT_DEFAULT_DEFBOX_FONT); + + if (priv->defbox) + gdict_defbox_set_font_name (GDICT_DEFBOX (priv->defbox), + priv->defbox_font); +} + +static void +gdict_applet_set_context (GdictApplet *applet, + GdictContext *context) +{ + GdictAppletPrivate *priv = applet->priv; + + if (priv->context) + { + g_signal_handler_disconnect (priv->context, priv->lookup_start_id); + g_signal_handler_disconnect (priv->context, priv->lookup_end_id); + g_signal_handler_disconnect (priv->context, priv->error_id); + + priv->lookup_start_id = 0; + priv->lookup_end_id = 0; + priv->error_id = 0; + + g_object_unref (priv->context); + priv->context = NULL; + } + + if (priv->defbox) + gdict_defbox_set_context (GDICT_DEFBOX (priv->defbox), context); + + if (!context) + return; + + /* attach our callbacks */ + priv->lookup_start_id = g_signal_connect (context, "lookup-start", + G_CALLBACK (gdict_applet_lookup_start_cb), + applet); + priv->lookup_end_id = g_signal_connect (context, "lookup-end", + G_CALLBACK (gdict_applet_lookup_end_cb), + applet); + priv->error_id = g_signal_connect (context, "error", + G_CALLBACK (gdict_applet_error_cb), + applet); + + priv->context = context; +} + +static void +gdict_applet_set_source_name (GdictApplet *applet, + const gchar *source_name) +{ + GdictAppletPrivate *priv = applet->priv; + GdictContext *context; + + g_free (priv->source_name); + + if (source_name) + priv->source_name = g_strdup (source_name); + else + priv->source_name = gdict_mateconf_get_string_with_default (priv->mateconf_client, + GDICT_MATECONF_SOURCE_KEY, + GDICT_DEFAULT_SOURCE_NAME); + + context = get_context_from_loader (applet); + gdict_applet_set_context (applet, context); +} + +static void +gdict_applet_mateconf_notify_cb (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + gpointer user_data) +{ + GdictApplet *applet = GDICT_APPLET (user_data); + + if (strcmp (entry->key, GDICT_MATECONF_PRINT_FONT_KEY) == 0) + { + if (entry->value && (entry->value->type == MATECONF_VALUE_STRING)) + gdict_applet_set_print_font (applet, mateconf_value_get_string (entry->value)); + else + gdict_applet_set_print_font (applet, GDICT_DEFAULT_PRINT_FONT); + } + else if (strcmp (entry->key, GDICT_MATECONF_SOURCE_KEY) == 0) + { + if (entry->value && (entry->value->type == MATECONF_VALUE_STRING)) + gdict_applet_set_source_name (applet, mateconf_value_get_string (entry->value)); + else + gdict_applet_set_source_name (applet, GDICT_DEFAULT_SOURCE_NAME); + } + else if (strcmp (entry->key, GDICT_MATECONF_DATABASE_KEY) == 0) + { + if (entry->value && (entry->value->type == MATECONF_VALUE_STRING)) + gdict_applet_set_database (applet, mateconf_value_get_string (entry->value)); + else + gdict_applet_set_database (applet, GDICT_DEFAULT_DATABASE); + } + else if (strcmp (entry->key, GDICT_MATECONF_STRATEGY_KEY) == 0) + { + if (entry->value && (entry->value->type == MATECONF_VALUE_STRING)) + gdict_applet_set_strategy (applet, mateconf_value_get_string (entry->value)); + else + gdict_applet_set_strategy (applet, GDICT_DEFAULT_STRATEGY); + } + else if (strcmp (entry->key, DOCUMENT_FONT_KEY) == 0) + { + if (entry->value && (entry->value->type == MATECONF_VALUE_STRING)) + gdict_applet_set_defbox_font (applet, mateconf_value_get_string (entry->value)); + else + gdict_applet_set_defbox_font (applet, GDICT_DEFAULT_DEFBOX_FONT); + } +} + +static void +gdict_applet_finalize (GObject *object) +{ + GdictApplet *applet = GDICT_APPLET (object); + GdictAppletPrivate *priv = applet->priv; + + if (priv->idle_draw_id) + g_source_remove (priv->idle_draw_id); + + if (priv->notify_id) + mateconf_client_notify_remove (priv->mateconf_client, priv->notify_id); + + if (priv->font_notify_id) + mateconf_client_notify_remove (priv->mateconf_client, priv->font_notify_id); + + if (priv->mateconf_client) + g_object_unref (priv->mateconf_client); + + if (priv->context) + { + if (priv->lookup_start_id) + { + g_signal_handler_disconnect (priv->context, priv->lookup_start_id); + g_signal_handler_disconnect (priv->context, priv->lookup_end_id); + g_signal_handler_disconnect (priv->context, priv->error_id); + } + + g_object_unref (priv->context); + } + + if (priv->loader) + g_object_unref (priv->loader); + + if (priv->icon) + g_object_unref (priv->icon); + + g_free (priv->source_name); + g_free (priv->print_font); + g_free (priv->defbox_font); + g_free (priv->word); + + G_OBJECT_CLASS (gdict_applet_parent_class)->finalize (object); +} + +static void +gdict_applet_class_init (GdictAppletClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + MatePanelAppletClass *applet_class = MATE_PANEL_APPLET_CLASS (klass); + + gobject_class->finalize = gdict_applet_finalize; + + widget_class->size_allocate = gdict_applet_size_allocate; + widget_class->style_set = gdict_applet_style_set; + + applet_class->change_background = gdict_applet_change_background; + applet_class->change_orient = gdict_applet_change_orient; + + g_type_class_add_private (gobject_class, sizeof (GdictAppletPrivate)); +} + +static void +gdict_applet_init (GdictApplet *applet) +{ + GdictAppletPrivate *priv; + gchar *data_dir; + GError *mateconf_error; + + priv = GDICT_APPLET_GET_PRIVATE (applet); + applet->priv = priv; + + if (!priv->loader) + priv->loader = gdict_source_loader_new (); + + /* add our data dir inside $HOME to the loader's search paths */ + data_dir = gdict_get_data_dir (); + gdict_source_loader_add_search_path (priv->loader, data_dir); + g_free (data_dir); + + gtk_window_set_default_icon_name ("accessories-dictionary"); + + mate_panel_applet_set_flags (MATE_PANEL_APPLET (applet), + MATE_PANEL_APPLET_EXPAND_MINOR); + + /* get the default mateconf client */ + if (!priv->mateconf_client) + priv->mateconf_client = mateconf_client_get_default (); + + mateconf_error = NULL; + mateconf_client_add_dir (priv->mateconf_client, + GDICT_MATECONF_DIR, + MATECONF_CLIENT_PRELOAD_ONELEVEL, + &mateconf_error); + if (mateconf_error) + { + gdict_show_gerror_dialog (NULL, + _("Unable to connect to MateConf"), + mateconf_error); + mateconf_error = NULL; + } + + priv->notify_id = mateconf_client_notify_add (priv->mateconf_client, + GDICT_MATECONF_DIR, + gdict_applet_mateconf_notify_cb, + applet, NULL, + &mateconf_error); + if (mateconf_error) + { + gdict_show_gerror_dialog (NULL, + _("Unable to get notification for preferences"), + mateconf_error); + + mateconf_error = NULL; + } + + priv->font_notify_id = mateconf_client_notify_add (priv->mateconf_client, + DOCUMENT_FONT_KEY, + gdict_applet_mateconf_notify_cb, + applet, NULL, + &mateconf_error); + if (mateconf_error) + { + gdict_show_gerror_dialog (NULL, + _("Unable to get notification for the document font"), + mateconf_error); + + mateconf_error = NULL; + } + +#ifndef GDICT_APPLET_STAND_ALONE + mate_panel_applet_set_background_widget (MATE_PANEL_APPLET (applet), + GTK_WIDGET (applet)); + + priv->size = mate_panel_applet_get_size (MATE_PANEL_APPLET (applet)); + + switch (mate_panel_applet_get_orient (MATE_PANEL_APPLET (applet))) + { + case MATE_PANEL_APPLET_ORIENT_LEFT: + case MATE_PANEL_APPLET_ORIENT_RIGHT: + priv->orient = GTK_ORIENTATION_VERTICAL; + break; + case MATE_PANEL_APPLET_ORIENT_UP: + case MATE_PANEL_APPLET_ORIENT_DOWN: + priv->orient = GTK_ORIENTATION_HORIZONTAL; + break; + } +#else + priv->size = 24; + priv->orient = GTK_ORIENTATION_HORIZONTAL; + g_message ("(in %s) applet { size = %d, orient = %s }", + G_STRFUNC, + priv->size, + (priv->orient == GTK_ORIENTATION_HORIZONTAL ? "H" : "V")); +#endif /* !GDICT_APPLET_STAND_ALONE */ + + priv->icon = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (), + "accessories-dictionary", + 48, + 0, + NULL); + + /* force first draw */ + gdict_applet_draw (applet); + + /* force retrieval of the configuration from MateConf */ + gdict_applet_set_source_name (applet, NULL); + gdict_applet_set_defbox_font (applet, NULL); + gdict_applet_set_print_font (applet, NULL); +} + +#ifndef GDICT_APPLET_STAND_ALONE +static const MateComponentUIVerb gdict_applet_menu_verbs[] = +{ + MATECOMPONENT_UI_UNSAFE_VERB ("LookUp", gdict_applet_cmd_lookup), + + MATECOMPONENT_UI_UNSAFE_VERB ("Clear", gdict_applet_cmd_clear), + MATECOMPONENT_UI_UNSAFE_VERB ("Print", gdict_applet_cmd_print), + + MATECOMPONENT_UI_UNSAFE_VERB ("Preferences", gdict_applet_cmd_preferences), + MATECOMPONENT_UI_UNSAFE_VERB ("Help", gdict_applet_cmd_help), + MATECOMPONENT_UI_UNSAFE_VERB ("About", gdict_applet_cmd_about), + + MATECOMPONENT_UI_VERB_END +}; + +static gboolean +gdict_applet_factory (MatePanelApplet *applet, + const gchar *iid, + gpointer data) +{ + gboolean retval = FALSE; + + if (((!strcmp (iid, "OAFIID:MATE_DictionaryApplet")) || + (!strcmp (iid, "OAFIID:MATE_GDictApplet"))) && + gdict_create_data_dir ()) + { + /* Set up the menu */ + mate_panel_applet_setup_menu_from_file (applet, UIDATADIR, + "MATE_DictionaryApplet.xml", + NULL, + gdict_applet_menu_verbs, + applet); + + gtk_widget_show (GTK_WIDGET (applet)); + + /* set the menu items insensitive */ + gdict_applet_set_menu_items_sensitive (GDICT_APPLET (applet), FALSE); + + retval = TRUE; + } + + return retval; +} + +/* this defines the main () for the applet */ +MATE_PANEL_APPLET_MATECOMPONENT_FACTORY ("OAFIID:MATE_DictionaryApplet_Factory", + GDICT_TYPE_APPLET, + "mate-dictionary-applet", + VERSION, + gdict_applet_factory, + NULL); + +#else /* GDICT_APPLET_STAND_ALONE */ + +int +main (int argc, char *argv[]) +{ + GtkWidget *window; + GtkWidget *applet; + + /* gettext stuff */ + bindtextdomain (GETTEXT_PACKAGE, MATELOCALEDIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); + + gtk_init (&argc, &argv); + + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + g_signal_connect (window, "destroy", G_CALLBACK (gtk_main_quit), NULL); + + applet = GTK_WIDGET (g_object_new (GDICT_TYPE_APPLET, NULL)); + g_message ("(in %s) typeof(applet) = '%s'", + G_STRFUNC, + g_type_name (G_OBJECT_TYPE (applet))); + + gdict_applet_queue_draw (GDICT_APPLET (applet)); + + gtk_container_set_border_width (GTK_CONTAINER (window), 12); + gtk_container_add (GTK_CONTAINER (window), applet); + + gtk_widget_show_all (window); + + gtk_main (); + + return 0; +} + +#endif /* !GDICT_APPLET_STAND_ALONE */ diff --git a/mate-dictionary/src/gdict-applet.h b/mate-dictionary/src/gdict-applet.h new file mode 100644 index 00000000..48f61c24 --- /dev/null +++ b/mate-dictionary/src/gdict-applet.h @@ -0,0 +1,48 @@ +/* gdict-applet.h - MATE Dictionary Applet + * + * Copyright (c) 2005 Emmanuele Bassi <[email protected]> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __GDICT_APPLET_H__ +#define __GDICT_APPLET_H__ + +#include <gtk/gtk.h> +#include <mate-panel-applet.h> +#include <libgdict/gdict.h> + +G_BEGIN_DECLS + +#define GDICT_TYPE_APPLET (gdict_applet_get_type ()) +#define GDICT_APPLET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDICT_TYPE_APPLET, GdictApplet)) +#define GDICT_IS_APPLET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDICT_TYPE_APPLET)) + +typedef struct _GdictApplet GdictApplet; +typedef struct _GdictAppletClass GdictAppletClass; +typedef struct _GdictAppletPrivate GdictAppletPrivate; + +struct _GdictApplet +{ + MatePanelApplet parent_instance; + + GdictAppletPrivate *priv; +}; + +GType gdict_applet_get_type (void); + +G_END_DECLS + +#endif /* __GDICT_APPLET_H__ */ diff --git a/mate-dictionary/src/gdict-common.c b/mate-dictionary/src/gdict-common.c new file mode 100644 index 00000000..8f106c18 --- /dev/null +++ b/mate-dictionary/src/gdict-common.c @@ -0,0 +1,201 @@ +/* gdict-common.h - shared code between application and applet + * + * This file is part of MATE Dictionary + * + * Copyright (C) 2005 Emmanuele Bassi + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <sys/types.h> +#include <errno.h> + +#include <glib.h> +#include <glib/gi18n.h> +#include <glib/gstdio.h> + +#include <gtk/gtk.h> + +#include "gdict-common.h" + +gchar * +gdict_get_data_dir (void) +{ + gchar *retval; + + retval = g_build_filename (g_get_home_dir (), + ".mate2", + "mate-dictionary", + NULL); + + return retval; +} + +/* create the data directory inside $HOME, if it doesn't exist yet */ +gboolean +gdict_create_data_dir (void) +{ + gchar *data_dir_name; + + data_dir_name = gdict_get_data_dir (); + if (g_mkdir (data_dir_name, 0700) == -1) + { + /* this is weird, but sometimes there's a "mate-dictionary" file + * inside $HOME/.mate2; see bug #329126. + */ + if ((errno == EEXIST) && + (g_file_test (data_dir_name, G_FILE_TEST_IS_REGULAR))) + { + gchar *backup = g_strdup_printf ("%s.pre-2-14", data_dir_name); + + if (g_rename (data_dir_name, backup) == -1) + { + GtkWidget *error_dialog; + + error_dialog = gtk_message_dialog_new (NULL, + GTK_DIALOG_MODAL, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + _("Unable to rename file '%s' to '%s': %s"), + data_dir_name, + backup, + g_strerror (errno)); + + gtk_dialog_run (GTK_DIALOG (error_dialog)); + + gtk_widget_destroy (error_dialog); + g_free (backup); + g_free (data_dir_name); + + return FALSE; + } + + g_free (backup); + + if (g_mkdir (data_dir_name, 0700) == -1) + { + GtkWidget *error_dialog; + + error_dialog = gtk_message_dialog_new (NULL, + GTK_DIALOG_MODAL, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + _("Unable to create the data directory '%s': %s"), + data_dir_name, + g_strerror (errno)); + + gtk_dialog_run (GTK_DIALOG (error_dialog)); + + gtk_widget_destroy (error_dialog); + g_free (data_dir_name); + + return FALSE; + } + + goto success; + } + + if (errno != EEXIST) + { + GtkWidget *error_dialog; + + error_dialog = gtk_message_dialog_new (NULL, + GTK_DIALOG_MODAL, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + _("Unable to create the data directory '%s': %s"), + data_dir_name, + g_strerror (errno)); + + gtk_dialog_run (GTK_DIALOG (error_dialog)); + + gtk_widget_destroy (error_dialog); + g_free (data_dir_name); + + return FALSE; + } + } + +success: + g_free (data_dir_name); + + return TRUE; +} + +/* shows an error dialog making it transient for @parent */ +void +gdict_show_error_dialog (GtkWindow *parent, + const gchar *message, + const gchar *detail) +{ + GtkWidget *dialog; + + g_return_if_fail ((parent == NULL) || (GTK_IS_WINDOW (parent))); + g_return_if_fail (message != NULL); + + dialog = gtk_message_dialog_new (parent, + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + "%s", message); + gtk_window_set_title (GTK_WINDOW (dialog), ""); + + if (detail) + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + "%s", detail); + + if (parent && gtk_window_get_group (parent)) + gtk_window_group_add_window (gtk_window_get_group (parent), GTK_WINDOW (dialog)); + + gtk_dialog_run (GTK_DIALOG (dialog)); + + gtk_widget_destroy (dialog); +} + +void +gdict_show_gerror_dialog (GtkWindow *parent, + const gchar *message, + GError *error) +{ + g_return_if_fail ((parent == NULL) || (GTK_IS_WINDOW (parent))); + g_return_if_fail (message != NULL); + g_return_if_fail (error != NULL); + + gdict_show_error_dialog (parent, message, error->message); + + g_error_free (error); + error = NULL; +} + +gchar * +gdict_mateconf_get_string_with_default (MateConfClient *client, + const gchar *key, + const gchar *def) +{ + gchar *val; + + val = mateconf_client_get_string (client, key, NULL); + return val ? val : g_strdup (def); +} + diff --git a/mate-dictionary/src/gdict-common.h b/mate-dictionary/src/gdict-common.h new file mode 100644 index 00000000..f8bc903d --- /dev/null +++ b/mate-dictionary/src/gdict-common.h @@ -0,0 +1,48 @@ +/* gdict-common.h - shared code between application and applet + * + * This file is part of MATE Dictionary + * + * Copyright (C) 2005 Emmanuele Bassi + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef __GDICT_COMMON_H__ +#define __GDICT_COMMON_H__ + +#include <gtk/gtk.h> +#include <mateconf/mateconf-client.h> + +G_BEGIN_DECLS + +gboolean gdict_create_data_dir (void); +gchar * gdict_get_data_dir (void) G_GNUC_MALLOC; + +void gdict_show_error_dialog (GtkWindow *parent, + const gchar *message, + const gchar *detail); +void gdict_show_gerror_dialog (GtkWindow *parent, + const gchar *message, + GError *error); + +gchar * gdict_mateconf_get_string_with_default (MateConfClient *client, + const gchar *key, + const gchar *def); + + +G_END_DECLS + +#endif /* __GDICT_COMMON_H__ */ diff --git a/mate-dictionary/src/gdict-pref-dialog.c b/mate-dictionary/src/gdict-pref-dialog.c new file mode 100644 index 00000000..24b61980 --- /dev/null +++ b/mate-dictionary/src/gdict-pref-dialog.c @@ -0,0 +1,744 @@ +/* gdict-pref-dialog.c - preferences dialog + * + * This file is part of MATE Dictionary + * + * Copyright (C) 2005 Emmanuele Bassi + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <sys/types.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#include <glib/gi18n.h> +#include <mateconf/mateconf-client.h> + +#include "gdict-source-dialog.h" +#include "gdict-pref-dialog.h" +#include "gdict-common.h" + +#define GDICT_PREFERENCES_UI PKGDATADIR "/mate-dictionary-preferences.ui" + +#define DEFAULT_MIN_WIDTH 220 +#define DEFAULT_MIN_HEIGHT 330 + +/******************* + * GdictPrefDialog * + *******************/ + +static GtkWidget *global_dialog = NULL; + +enum +{ + SOURCES_ACTIVE_COLUMN = 0, + SOURCES_NAME_COLUMN, + SOURCES_DESCRIPTION_COLUMN, + + SOURCES_N_COLUMNS +}; + +struct _GdictPrefDialog +{ + GtkDialog parent_instance; + + GtkBuilder *builder; + + MateConfClient *mateconf_client; + guint notify_id; + + gchar *active_source; + GdictSourceLoader *loader; + GtkListStore *sources_list; + + /* direct pointers to widgets */ + GtkWidget *notebook; + + GtkWidget *sources_view; + GtkWidget *sources_add; + GtkWidget *sources_remove; + + gchar *print_font; + GtkWidget *font_button; + + GtkWidget *help_button; + GtkWidget *close_button; +}; + +struct _GdictPrefDialogClass +{ + GtkDialogClass parent_class; +}; + +enum +{ + PROP_0, + + PROP_SOURCE_LOADER +}; + + +G_DEFINE_TYPE (GdictPrefDialog, gdict_pref_dialog, GTK_TYPE_DIALOG); + + +static gboolean +select_active_source_name (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data) +{ + GdictPrefDialog *dialog = GDICT_PREF_DIALOG (data); + gboolean is_active; + + gtk_tree_model_get (model, iter, + SOURCES_ACTIVE_COLUMN, &is_active, + -1); + if (is_active) + { + GtkTreeSelection *selection; + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->sources_view)); + + gtk_tree_selection_select_iter (selection, iter); + + return TRUE; + } + + return FALSE; +} + +static void +update_sources_view (GdictPrefDialog *dialog) +{ + const GSList *sources, *l; + + gtk_tree_view_set_model (GTK_TREE_VIEW (dialog->sources_view), NULL); + + gtk_list_store_clear (dialog->sources_list); + + /* force update of the sources list */ + gdict_source_loader_update (dialog->loader); + + sources = gdict_source_loader_get_sources (dialog->loader); + for (l = sources; l != NULL; l = l->next) + { + GdictSource *source = GDICT_SOURCE (l->data); + GtkTreeIter iter; + const gchar *name, *description; + gboolean is_active = FALSE; + + name = gdict_source_get_name (source); + description = gdict_source_get_description (source); + if (!description) + description = name; + + if (strcmp (name, dialog->active_source) == 0) + is_active = TRUE; + + gtk_list_store_append (dialog->sources_list, &iter); + gtk_list_store_set (dialog->sources_list, &iter, + SOURCES_ACTIVE_COLUMN, is_active, + SOURCES_NAME_COLUMN, name, + SOURCES_DESCRIPTION_COLUMN, description, + -1); + } + + gtk_tree_view_set_model (GTK_TREE_VIEW (dialog->sources_view), + GTK_TREE_MODEL (dialog->sources_list)); + + /* select the currently active source name */ + gtk_tree_model_foreach (GTK_TREE_MODEL (dialog->sources_list), + select_active_source_name, + dialog); +} + +static void +source_renderer_toggled_cb (GtkCellRendererToggle *renderer, + const gchar *path, + GdictPrefDialog *dialog) +{ + GtkTreePath *treepath; + GtkTreeIter iter; + gboolean res; + gboolean is_active; + gchar *name; + + treepath = gtk_tree_path_new_from_string (path); + res = gtk_tree_model_get_iter (GTK_TREE_MODEL (dialog->sources_list), + &iter, + treepath); + if (!res) + { + gtk_tree_path_free (treepath); + + return; + } + + gtk_tree_model_get (GTK_TREE_MODEL (dialog->sources_list), &iter, + SOURCES_NAME_COLUMN, &name, + SOURCES_ACTIVE_COLUMN, &is_active, + -1); + if (!is_active && name != NULL) + { + g_free (dialog->active_source); + dialog->active_source = g_strdup (name); + + mateconf_client_set_string (dialog->mateconf_client, + GDICT_MATECONF_SOURCE_KEY, + dialog->active_source, + NULL); + + update_sources_view (dialog); + + g_free (name); + } + + gtk_tree_path_free (treepath); +} + +static void +sources_view_row_activated_cb (GtkTreeView *tree_view, + GtkTreePath *tree_path, + GtkTreeViewColumn *tree_iter, + GdictPrefDialog *dialog) +{ + GtkWidget *edit_dialog; + gchar *source_name; + GtkTreeModel *model; + GtkTreeIter iter; + + model = gtk_tree_view_get_model (tree_view); + if (!model) + return; + + if (!gtk_tree_model_get_iter (model, &iter, tree_path)) + return; + + gtk_tree_model_get (model, &iter, SOURCES_NAME_COLUMN, &source_name, -1); + if (!source_name) + return; + + edit_dialog = gdict_source_dialog_new (GTK_WINDOW (dialog), + _("Edit Dictionary Source"), + GDICT_SOURCE_DIALOG_EDIT, + dialog->loader, + source_name); + gtk_dialog_run (GTK_DIALOG (edit_dialog)); + + gtk_widget_destroy (edit_dialog); + g_free (source_name); + + update_sources_view (dialog); +} + +static void +build_sources_view (GdictPrefDialog *dialog) +{ + GtkTreeViewColumn *column; + GtkCellRenderer *renderer; + + if (dialog->sources_list) + return; + + dialog->sources_list = gtk_list_store_new (SOURCES_N_COLUMNS, + G_TYPE_BOOLEAN, /* active */ + G_TYPE_STRING, /* name */ + G_TYPE_STRING /* description */); + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (dialog->sources_list), + SOURCES_DESCRIPTION_COLUMN, + GTK_SORT_ASCENDING); + + renderer = gtk_cell_renderer_toggle_new (); + gtk_cell_renderer_toggle_set_radio (GTK_CELL_RENDERER_TOGGLE (renderer), TRUE); + g_signal_connect (renderer, "toggled", + G_CALLBACK (source_renderer_toggled_cb), + dialog); + + column = gtk_tree_view_column_new_with_attributes ("active", + renderer, + "active", SOURCES_ACTIVE_COLUMN, + NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW (dialog->sources_view), column); + + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes ("description", + renderer, + "text", SOURCES_DESCRIPTION_COLUMN, + NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW (dialog->sources_view), column); + + gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (dialog->sources_view), FALSE); + gtk_tree_view_set_model (GTK_TREE_VIEW (dialog->sources_view), + GTK_TREE_MODEL (dialog->sources_list)); + + g_signal_connect (dialog->sources_view, "row-activated", + G_CALLBACK (sources_view_row_activated_cb), + dialog); +} + +static void +source_add_clicked_cb (GtkWidget *widget, + GdictPrefDialog *dialog) +{ + GtkWidget *add_dialog; + + add_dialog = gdict_source_dialog_new (GTK_WINDOW (dialog), + _("Add Dictionary Source"), + GDICT_SOURCE_DIALOG_CREATE, + dialog->loader, + NULL); + + gtk_dialog_run (GTK_DIALOG (add_dialog)); + + gtk_widget_destroy (add_dialog); + + update_sources_view (dialog); +} + +static void +source_remove_clicked_cb (GtkWidget *widget, + GdictPrefDialog *dialog) +{ + GtkTreeSelection *selection; + GtkTreeModel *model; + GtkTreeIter iter; + gboolean is_selected; + gchar *name, *description; + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->sources_view)); + if (!selection) + return; + + is_selected = gtk_tree_selection_get_selected (selection, &model, &iter); + if (!is_selected) + return; + + gtk_tree_model_get (model, &iter, + SOURCES_NAME_COLUMN, &name, + SOURCES_DESCRIPTION_COLUMN, &description, + -1); + if (!name) + return; + else + { + GtkWidget *confirm_dialog; + gint response; + + confirm_dialog = gtk_message_dialog_new (GTK_WINDOW (dialog), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_WARNING, + GTK_BUTTONS_NONE, + _("Remove \"%s\"?"), description); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (confirm_dialog), + _("This will permanently remove the " + "dictionary source from the list.")); + + gtk_dialog_add_button (GTK_DIALOG (confirm_dialog), + GTK_STOCK_CANCEL, + GTK_RESPONSE_CANCEL); + gtk_dialog_add_button (GTK_DIALOG (confirm_dialog), + GTK_STOCK_REMOVE, + GTK_RESPONSE_OK); + + gtk_window_set_title (GTK_WINDOW (confirm_dialog), ""); + + response = gtk_dialog_run (GTK_DIALOG (confirm_dialog)); + if (response == GTK_RESPONSE_CANCEL) + { + gtk_widget_destroy (confirm_dialog); + + goto out; + } + + gtk_widget_destroy (confirm_dialog); + } + + if (gdict_source_loader_remove_source (dialog->loader, name)) + gtk_list_store_remove (GTK_LIST_STORE (model), &iter); + else + { + GtkWidget *error_dialog; + gchar *message; + + message = g_strdup_printf (_("Unable to remove source '%s'"), + description); + + error_dialog = gtk_message_dialog_new (GTK_WINDOW (dialog), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + "%s", message); + gtk_window_set_title (GTK_WINDOW (error_dialog), ""); + + gtk_dialog_run (GTK_DIALOG (error_dialog)); + + gtk_widget_destroy (error_dialog); + } + +out: + g_free (name); + g_free (description); + + update_sources_view (dialog); +} + +static void +set_source_loader (GdictPrefDialog *dialog, + GdictSourceLoader *loader) +{ + if (!dialog->sources_list) + return; + + if (dialog->loader) + g_object_unref (dialog->loader); + + dialog->loader = g_object_ref (loader); + + update_sources_view (dialog); +} + +static void +font_button_font_set_cb (GtkWidget *font_button, + GdictPrefDialog *dialog) +{ + const gchar *font; + + font = gtk_font_button_get_font_name (GTK_FONT_BUTTON (font_button)); + if (!font || font[0] == '\0') + return; + + if (dialog->print_font && (strcmp (dialog->print_font, font) == 0)) + return; + + g_free (dialog->print_font); + dialog->print_font = g_strdup (font); + + mateconf_client_set_string (dialog->mateconf_client, + GDICT_MATECONF_PRINT_FONT_KEY, + dialog->print_font, + NULL); +} + +static void +gdict_pref_dialog_mateconf_notify_cb (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + gpointer user_data) +{ + GdictPrefDialog *dialog = GDICT_PREF_DIALOG (user_data); + + if (strcmp (entry->key, GDICT_MATECONF_SOURCE_KEY) == 0) + { + if (entry->value && entry->value->type == MATECONF_VALUE_STRING) + { + g_free (dialog->active_source); + dialog->active_source = g_strdup (mateconf_value_get_string (entry->value)); + } + else + { + g_free (dialog->active_source); + dialog->active_source = g_strdup (GDICT_DEFAULT_SOURCE_NAME); + } + + update_sources_view (dialog); + } + else if (strcmp (entry->key, GDICT_MATECONF_PRINT_FONT_KEY) == 0) + { + if (entry->value && entry->value->type == MATECONF_VALUE_STRING) + { + g_free (dialog->print_font); + dialog->print_font = g_strdup (mateconf_value_get_string (entry->value)); + } + else + { + g_free (dialog->print_font); + dialog->print_font = g_strdup (GDICT_DEFAULT_PRINT_FONT); + } + + gtk_font_button_set_font_name (GTK_FONT_BUTTON (dialog->font_button), + dialog->print_font); + } +} + +static void +response_cb (GtkDialog *dialog, + gint response_id, + gpointer user_data) +{ + GError *err = NULL; + + switch (response_id) + { + case GTK_RESPONSE_HELP: + gtk_show_uri (gtk_widget_get_screen (GTK_WIDGET (dialog)), + "ghelp:mate-dictionary#mate-dictionary-preferences", + gtk_get_current_event_time (), &err); + if (err) + { + GtkWidget *error_dialog; + gchar *message; + + message = g_strdup_printf (_("There was an error while displaying help")); + error_dialog = gtk_message_dialog_new (GTK_WINDOW (dialog), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + "%s", message); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (error_dialog), + "%s", err->message); + gtk_window_set_title (GTK_WINDOW (error_dialog), ""); + + gtk_dialog_run (GTK_DIALOG (error_dialog)); + + gtk_widget_destroy (error_dialog); + g_error_free (err); + } + + /* we don't want the dialog to close itself */ + g_signal_stop_emission_by_name (dialog, "response"); + break; + case GTK_RESPONSE_ACCEPT: + default: + gtk_widget_hide (GTK_WIDGET (dialog)); + break; + } +} + +static void +gdict_pref_dialog_finalize (GObject *object) +{ + GdictPrefDialog *dialog = GDICT_PREF_DIALOG (object); + + if (dialog->notify_id); + mateconf_client_notify_remove (dialog->mateconf_client, dialog->notify_id); + + if (dialog->mateconf_client) + g_object_unref (dialog->mateconf_client); + + if (dialog->builder) + g_object_unref (dialog->builder); + + if (dialog->active_source) + g_free (dialog->active_source); + + if (dialog->loader) + g_object_unref (dialog->loader); + + G_OBJECT_CLASS (gdict_pref_dialog_parent_class)->finalize (object); +} + +static void +gdict_pref_dialog_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GdictPrefDialog *dialog = GDICT_PREF_DIALOG (object); + + switch (prop_id) + { + case PROP_SOURCE_LOADER: + set_source_loader (dialog, g_value_get_object (value)); + break; + default: + break; + } +} + +static void +gdict_pref_dialog_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GdictPrefDialog *dialog = GDICT_PREF_DIALOG (object); + + switch (prop_id) + { + case PROP_SOURCE_LOADER: + g_value_set_object (value, dialog->loader); + break; + default: + break; + } +} + +static void +gdict_pref_dialog_class_init (GdictPrefDialogClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->set_property = gdict_pref_dialog_set_property; + gobject_class->get_property = gdict_pref_dialog_get_property; + gobject_class->finalize = gdict_pref_dialog_finalize; + + g_object_class_install_property (gobject_class, + PROP_SOURCE_LOADER, + g_param_spec_object ("source-loader", + "Source Loader", + "The GdictSourceLoader used by the application", + GDICT_TYPE_SOURCE_LOADER, + (G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY))); +} + +static void +gdict_pref_dialog_init (GdictPrefDialog *dialog) +{ + gchar *font; + GError *error = NULL; + + gtk_window_set_default_size (GTK_WINDOW (dialog), + DEFAULT_MIN_WIDTH, + DEFAULT_MIN_HEIGHT); + + gtk_container_set_border_width (GTK_CONTAINER (dialog), 5); + gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), 2); + gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE); + + /* add buttons */ + gtk_dialog_add_button (GTK_DIALOG (dialog), + "gtk-help", + GTK_RESPONSE_HELP); + gtk_dialog_add_button (GTK_DIALOG (dialog), + "gtk-close", + GTK_RESPONSE_ACCEPT); + + dialog->mateconf_client = mateconf_client_get_default (); + mateconf_client_add_dir (dialog->mateconf_client, + GDICT_MATECONF_DIR, + MATECONF_CLIENT_PRELOAD_ONELEVEL, + NULL); + dialog->notify_id = mateconf_client_notify_add (dialog->mateconf_client, + GDICT_MATECONF_DIR, + gdict_pref_dialog_mateconf_notify_cb, + dialog, + NULL, + NULL); + + /* get the UI from the GtkBuilder file */ + dialog->builder = gtk_builder_new (); + gtk_builder_add_from_file (dialog->builder, GDICT_PREFERENCES_UI, &error); + + if (error) { + g_critical ("Unable to load the preferences user interface: %s", error->message); + g_error_free (error); + g_assert_not_reached (); + } + + /* the main widget */ + gtk_container_add (GTK_CONTAINER (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), + GTK_WIDGET (gtk_builder_get_object (dialog->builder, "preferences_root"))); + + /* keep all the interesting widgets around */ + dialog->notebook = GTK_WIDGET (gtk_builder_get_object (dialog->builder, "preferences_notebook")); + + dialog->sources_view = GTK_WIDGET (gtk_builder_get_object (dialog->builder, "sources_treeview")); + build_sources_view (dialog); + + dialog->active_source = gdict_mateconf_get_string_with_default (dialog->mateconf_client, + GDICT_MATECONF_SOURCE_KEY, + GDICT_DEFAULT_SOURCE_NAME); + + dialog->sources_add = GTK_WIDGET (gtk_builder_get_object (dialog->builder, "add_button")); + gtk_widget_set_tooltip_text (dialog->sources_add, + _("Add a new dictionary source")); + g_signal_connect (dialog->sources_add, "clicked", + G_CALLBACK (source_add_clicked_cb), dialog); + + dialog->sources_remove = GTK_WIDGET (gtk_builder_get_object (dialog->builder, "remove_button")); + gtk_widget_set_tooltip_text (dialog->sources_remove, + _("Remove the currently selected dictionary source")); + g_signal_connect (dialog->sources_remove, "clicked", + G_CALLBACK (source_remove_clicked_cb), dialog); + + font = mateconf_client_get_string (dialog->mateconf_client, + GDICT_MATECONF_PRINT_FONT_KEY, + NULL); + if (!font) + font = g_strdup (GDICT_DEFAULT_PRINT_FONT); + + dialog->font_button = GTK_WIDGET (gtk_builder_get_object (dialog->builder, "print_font_button")); + gtk_font_button_set_font_name (GTK_FONT_BUTTON (dialog->font_button), font); + gtk_widget_set_tooltip_text (dialog->font_button, + _("Set the font used for printing the definitions")); + g_signal_connect (dialog->font_button, "font-set", + G_CALLBACK (font_button_font_set_cb), dialog); + g_free (font); + + gtk_widget_show_all (dialog->notebook); + + /* we want to intercept the response signal before any other + * callbacks might be attached by the users of the + * GdictPrefDialog widget. + */ + g_signal_connect (dialog, "response", + G_CALLBACK (response_cb), + NULL); +} + +void +gdict_show_pref_dialog (GtkWidget *parent, + const gchar *title, + GdictSourceLoader *loader) +{ + GtkWidget *dialog; + + g_return_if_fail (GTK_IS_WIDGET (parent)); + g_return_if_fail (GDICT_IS_SOURCE_LOADER (loader)); + + if (parent) + dialog = g_object_get_data (G_OBJECT (parent), "gdict-pref-dialog"); + else + dialog = global_dialog; + + if (!dialog) + { + dialog = g_object_new (GDICT_TYPE_PREF_DIALOG, + "source-loader", loader, + "title", title, + NULL); + + g_object_ref_sink (dialog); + + g_signal_connect (dialog, "delete-event", + G_CALLBACK (gtk_widget_hide_on_delete), + NULL); + + if (parent && GTK_IS_WINDOW (parent)) + { + gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (parent)); + gtk_window_set_destroy_with_parent (GTK_WINDOW (dialog), TRUE); + g_object_set_data_full (G_OBJECT (parent), "gdict-pref-dialog", + dialog, + g_object_unref); + } + else + global_dialog = dialog; + } + + gtk_window_set_screen (GTK_WINDOW (dialog), + gtk_widget_get_screen (parent)); + gtk_window_present (GTK_WINDOW (dialog)); +} diff --git a/mate-dictionary/src/gdict-pref-dialog.h b/mate-dictionary/src/gdict-pref-dialog.h new file mode 100644 index 00000000..7cae7e57 --- /dev/null +++ b/mate-dictionary/src/gdict-pref-dialog.h @@ -0,0 +1,65 @@ +/* gdict-pref-dialog.h - preferences dialog + * + * This file is part of MATE Dictionary + * + * Copyright (C) 2005 Emmanuele Bassi + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef __GDICT_PREF_DIALOG_H__ +#define __GDICT_PREF_DIALOG_H__ + +#include <gtk/gtk.h> +#include <libgdict/gdict.h> + +G_BEGIN_DECLS + +#define GDICT_TYPE_PREF_DIALOG (gdict_pref_dialog_get_type ()) +#define GDICT_PREF_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDICT_TYPE_PREF_DIALOG, GdictPrefDialog)) +#define GDICT_IS_PREF_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDICT_TYPE_PREF_DIALOG)) + +#define GDICT_DEFAULT_DEFBOX_FONT "Sans 10" +#define GDICT_DEFAULT_PRINT_FONT "Serif 10" +#define GDICT_DEFAULT_SOURCE_NAME "Default" + +#define GDICT_MATECONF_DIR "/apps/mate-dictionary" +#define GDICT_MATECONF_DATABASE_KEY GDICT_MATECONF_DIR "/database" +#define GDICT_MATECONF_STRATEGY_KEY GDICT_MATECONF_DIR "/strategy" +#define GDICT_MATECONF_PRINT_FONT_KEY GDICT_MATECONF_DIR "/print-font" +#define GDICT_MATECONF_SOURCE_KEY GDICT_MATECONF_DIR "/source-name" +#define GDICT_MATECONF_WINDOW_WIDTH_KEY GDICT_MATECONF_DIR "/default-window-width" +#define GDICT_MATECONF_WINDOW_HEIGHT_KEY GDICT_MATECONF_DIR "/default-window-height" +#define GDICT_MATECONF_WINDOW_IS_MAXIMIZED_KEY GDICT_MATECONF_DIR "/window-is-maximized" +#define GDICT_MATECONF_SIDEBAR_VISIBLE_KEY GDICT_MATECONF_DIR "/sidebar-visible" +#define GDICT_MATECONF_SIDEBAR_PAGE_KEY GDICT_MATECONF_DIR "/sidebar-page" +#define GDICT_MATECONF_SIDEBAR_WIDTH_KEY GDICT_MATECONF_DIR "/sidebar-width" +#define GDICT_MATECONF_STATUSBAR_VISIBLE_KEY GDICT_MATECONF_DIR "/statusbar-visible" + +#define DOCUMENT_FONT_KEY "/desktop/mate/interface/document_font_name" + +typedef struct _GdictPrefDialog GdictPrefDialog; +typedef struct _GdictPrefDialogClass GdictPrefDialogClass; + +GType gdict_pref_dialog_get_type (void) G_GNUC_CONST; + +void gdict_show_pref_dialog (GtkWidget *parent, + const gchar *title, + GdictSourceLoader *loader); + +G_END_DECLS + +#endif /* __GDICT_PREF_DIALOG_H__ */ diff --git a/mate-dictionary/src/gdict-print.c b/mate-dictionary/src/gdict-print.c new file mode 100644 index 00000000..855dbab6 --- /dev/null +++ b/mate-dictionary/src/gdict-print.c @@ -0,0 +1,322 @@ +/* gdict-print.c - print-related helper functions + * + * This file is part of MATE Dictionary + * + * Copyright (C) 2005 Emmanuele Bassi + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <string.h> +#include <math.h> + +#include <gtk/gtk.h> +#include <glib/gi18n.h> + +#include <mateconf/mateconf-client.h> + +#include <libgdict/gdict.h> + +#include "gdict-pref-dialog.h" +#include "gdict-print.h" + +#define HEADER_HEIGHT(lines) ((lines) * 72 / 25.4) +#define HEADER_GAP(lines) ((lines) * 72 / 25.4) + +typedef struct _GdictPrintData +{ + GdictDefbox *defbox; + gchar *word; + + PangoFontDescription *font_desc; + gdouble font_size; + + gchar **lines; + + gint n_lines; + gint lines_per_page; + gint n_pages; +} GdictPrintData; + +static void +begin_print (GtkPrintOperation *operation, + GtkPrintContext *context, + gpointer user_data) +{ + GdictPrintData *data = user_data; + gchar *contents; + gdouble height; + + height = gtk_print_context_get_height (context) + - HEADER_HEIGHT (10) + - HEADER_GAP (3); + + contents = gdict_defbox_get_text (data->defbox, NULL); + + data->lines = g_strsplit (contents, "\n", 0); + data->n_lines = g_strv_length (data->lines); + data->lines_per_page = floor (height / data->font_size); + + data->n_pages = (data->n_lines - 1) / data->lines_per_page + 1; + + gtk_print_operation_set_n_pages (operation, data->n_pages); + + g_free (contents); +} + +static void +draw_page (GtkPrintOperation *operation, + GtkPrintContext *context, + gint page_number, + gpointer user_data) +{ + GdictPrintData *data = user_data; + cairo_t *cr; + PangoLayout *layout; + gint text_width, text_height; + gdouble width; + gint line, i; + PangoFontDescription *desc; + gchar *page_str; + + cr = gtk_print_context_get_cairo_context (context); + width = gtk_print_context_get_width (context); + + cairo_rectangle (cr, 0, 0, width, HEADER_HEIGHT (10)); + + cairo_set_source_rgb (cr, 0.8, 0.8, 0.8); + cairo_fill_preserve (cr); + + cairo_set_source_rgb (cr, 0, 0, 0); + cairo_set_line_width (cr, 1); + cairo_stroke (cr); + + /* header */ + layout = gtk_print_context_create_pango_layout (context); + + desc = pango_font_description_from_string ("sans 14"); + pango_layout_set_font_description (layout, desc); + pango_font_description_free (desc); + + pango_layout_set_text (layout, data->word, -1); + pango_layout_get_pixel_size (layout, &text_width, &text_height); + + if (text_width > width) + { + pango_layout_set_width (layout, width); + pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_START); + pango_layout_get_pixel_size (layout, &text_width, &text_height); + } + + cairo_move_to (cr, (width - text_width) / 2, + (HEADER_HEIGHT (10) - text_height) / 2); + pango_cairo_show_layout (cr, layout); + + page_str = g_strdup_printf ("%d/%d", page_number + 1, data->n_pages); + pango_layout_set_text (layout, page_str, -1); + g_free (page_str); + + pango_layout_set_width (layout, -1); + pango_layout_get_pixel_size (layout, &text_width, &text_height); + cairo_move_to (cr, width - text_width - 4, + (HEADER_HEIGHT (10) - text_height) / 2); + pango_cairo_show_layout (cr, layout); + + g_object_unref (layout); + + /* text */ + layout = gtk_print_context_create_pango_layout (context); + pango_font_description_set_size (data->font_desc, + data->font_size * PANGO_SCALE); + pango_layout_set_font_description (layout, data->font_desc); + + cairo_move_to (cr, 0, HEADER_HEIGHT (10) + HEADER_GAP (3)); + line = page_number * data->lines_per_page; + for (i = 0; i < data->lines_per_page && line < data->n_lines; i++) + { + pango_layout_set_text (layout, data->lines[line], -1); + + pango_cairo_show_layout (cr, layout); + cairo_rel_move_to (cr, 0, data->font_size); + line++; + } + + g_object_unref (layout); +} + +static void +end_print (GtkPrintOperation *operation, + GtkPrintContext *context, + gpointer user_data) +{ + GdictPrintData *data = user_data; + + pango_font_description_free (data->font_desc); + g_free (data->word); + g_strfreev (data->lines); + g_free (data); +} + +static gchar * +get_print_font (void) +{ + MateConfClient *client; + gchar *print_font; + + client = mateconf_client_get_default (); + print_font = mateconf_client_get_string (client, + GDICT_MATECONF_PRINT_FONT_KEY, + NULL); + if (!print_font) + print_font = g_strdup (GDICT_DEFAULT_PRINT_FONT); + + g_object_unref (client); + + return print_font; +} + +void +gdict_show_print_preview (GtkWindow *parent, + GdictDefbox *defbox) +{ + GtkPrintOperation *operation; + GdictPrintData *data; + gchar *print_font; + gchar *word; + GError *error; + + g_return_if_fail (parent == NULL || GTK_IS_WINDOW (parent)); + g_return_if_fail (GDICT_IS_DEFBOX (defbox)); + + g_object_get (defbox, "word", &word, NULL); + if (!word) + { + g_warning ("Preview should be disabled."); + return; + } + + data = g_new0 (GdictPrintData, 1); + data->defbox = defbox; + data->word = word; + + operation = gtk_print_operation_new (); + print_font = get_print_font (); + data->font_desc = pango_font_description_from_string (print_font); + data->font_size = pango_font_description_get_size (data->font_desc) + / PANGO_SCALE; + g_free (print_font); + + g_signal_connect (operation, "begin-print", + G_CALLBACK (begin_print), data); + g_signal_connect (operation, "draw-page", + G_CALLBACK (draw_page), data); + g_signal_connect (operation, "end-print", + G_CALLBACK (end_print), data); + + error = NULL; + gtk_print_operation_run (operation, + GTK_PRINT_OPERATION_ACTION_PREVIEW, + parent, + &error); + g_object_unref (operation); + + if (error) + { + GtkWidget *dialog; + + dialog = gtk_message_dialog_new (parent, + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + _("Unable to display the preview: %s"), + error->message); + g_error_free (error); + + g_signal_connect (dialog, "response", + G_CALLBACK (gtk_widget_destroy), NULL); + + gtk_widget_show (dialog); + } +} + +void +gdict_show_print_dialog (GtkWindow *parent, + GdictDefbox *defbox) +{ + GtkPrintOperation *operation; + GdictPrintData *data; + gchar *print_font; + gchar *word; + GError *error; + + g_return_if_fail (parent == NULL || GTK_IS_WINDOW (parent)); + g_return_if_fail (GDICT_IS_DEFBOX (defbox)); + + g_object_get (defbox, "word", &word, NULL); + if (!word) + { + g_warning ("Print should be disabled."); + return; + } + + data = g_new0 (GdictPrintData, 1); + data->defbox = defbox; + data->word = word; + + operation = gtk_print_operation_new (); + print_font = get_print_font (); + data->font_desc = pango_font_description_from_string (print_font); + data->font_size = pango_font_description_get_size (data->font_desc) + / PANGO_SCALE; + g_free (print_font); + + g_signal_connect (operation, "begin-print", + G_CALLBACK (begin_print), data); + g_signal_connect (operation, "draw-page", + G_CALLBACK (draw_page), data); + g_signal_connect (operation, "end-print", + G_CALLBACK (end_print), data); + + error = NULL; + gtk_print_operation_run (operation, + GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG, + parent, + &error); + g_object_unref (operation); + + if (error) + { + GtkWidget *dialog; + + dialog = gtk_message_dialog_new (parent, + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + _("Unable to display the preview: %s"), + error->message); + g_error_free (error); + + g_signal_connect (dialog, "response", + G_CALLBACK (gtk_widget_destroy), NULL); + + gtk_widget_show (dialog); + } +} diff --git a/mate-dictionary/src/gdict-print.h b/mate-dictionary/src/gdict-print.h new file mode 100644 index 00000000..5c0fe25f --- /dev/null +++ b/mate-dictionary/src/gdict-print.h @@ -0,0 +1,38 @@ +/* gdict-print.h - print-related helper functions + * + * This file is part of MATE Dictionary + * + * Copyright (C) 2005 Emmanuele Bassi + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef __GDICT_PRINT_H__ +#define __GDICT_PRINT_H__ + +#include <gtk/gtk.h> +#include <libgdict/gdict-defbox.h> + +G_BEGIN_DECLS + +void gdict_show_print_preview (GtkWindow *parent, + GdictDefbox *defbox); +void gdict_show_print_dialog (GtkWindow *parent, + GdictDefbox *defbox); + +G_END_DECLS + +#endif /* __GDICT_PRINT_H__ */ diff --git a/mate-dictionary/src/gdict-sidebar.c b/mate-dictionary/src/gdict-sidebar.c new file mode 100644 index 00000000..c8f51be0 --- /dev/null +++ b/mate-dictionary/src/gdict-sidebar.c @@ -0,0 +1,568 @@ +/* gdict-sidebar.c - sidebar widget + * + * Copyright (C) 2006 Emmanuele Bassi <[email protected]> + * + * This program 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * + * Based on the equivalent widget from Evince + * by Jonathan Blandford, + * Copyright (C) 2004 Red Hat, Inc. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> + +#include <gdk/gdkkeysyms.h> +#include <gtk/gtk.h> +#include <glib/gi18n.h> + +#include "gdict-sidebar.h" + +typedef struct +{ + guint index; + + gchar *id; + gchar *name; + + GtkWidget *child; + GtkWidget *menu_item; +} SidebarPage; + +#define GDICT_SIDEBAR_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GDICT_TYPE_SIDEBAR, GdictSidebarPrivate)) + +struct _GdictSidebarPrivate +{ + GHashTable *pages_by_id; + GSList *pages; + + GtkWidget *hbox; + GtkWidget *notebook; + GtkWidget *menu; + GtkWidget *close_button; + GtkWidget *label; + GtkWidget *select_button; +}; + +enum +{ + PAGE_CHANGED, + CLOSED, + + LAST_SIGNAL +}; + +static guint sidebar_signals[LAST_SIGNAL] = { 0 }; +static GQuark sidebar_page_id_quark = 0; + +G_DEFINE_TYPE (GdictSidebar, gdict_sidebar, GTK_TYPE_VBOX); + +SidebarPage * +sidebar_page_new (const gchar *id, + const gchar *name, + GtkWidget *widget) +{ + SidebarPage *page; + + page = g_slice_new (SidebarPage); + + page->id = g_strdup (id); + page->name = g_strdup (name); + page->child = widget; + page->index = -1; + page->menu_item = NULL; + + return page; +} + +void +sidebar_page_free (SidebarPage *page) +{ + if (G_LIKELY (page)) + { + g_free (page->name); + g_free (page->id); + + g_slice_free (SidebarPage, page); + } +} + +static void +gdict_sidebar_finalize (GObject *object) +{ + GdictSidebar *sidebar = GDICT_SIDEBAR (object); + GdictSidebarPrivate *priv = sidebar->priv; + + if (priv->pages_by_id) + g_hash_table_destroy (priv->pages_by_id); + + if (priv->pages) + { + g_slist_foreach (priv->pages, (GFunc) sidebar_page_free, NULL); + g_slist_free (priv->pages); + } + + G_OBJECT_CLASS (gdict_sidebar_parent_class)->finalize (object); +} + +static void +gdict_sidebar_dispose (GObject *object) +{ + GdictSidebar *sidebar = GDICT_SIDEBAR (object); + + if (sidebar->priv->menu) + { + gtk_menu_detach (GTK_MENU (sidebar->priv->menu)); + sidebar->priv->menu = NULL; + } + + G_OBJECT_CLASS (gdict_sidebar_parent_class)->dispose (object); +} + +static void +gdict_sidebar_menu_position_function (GtkMenu *menu, + gint *x, + gint *y, + gboolean *push_in, + gpointer user_data) +{ + GtkWidget *widget; + GtkAllocation allocation; + + g_assert (GTK_IS_BUTTON (user_data)); + + widget = GTK_WIDGET (user_data); + + gdk_window_get_origin (gtk_widget_get_window (widget), x, y); + + gtk_widget_get_allocation (widget, &allocation); + *x += allocation.x; + *y += allocation.y + allocation.height; + + *push_in = FALSE; +} + +static gboolean +gdict_sidebar_select_button_press_cb (GtkWidget *widget, + GdkEventButton *event, + gpointer user_data) +{ + GdictSidebar *sidebar = GDICT_SIDEBAR (user_data); + GtkAllocation allocation; + + if (event->button == 1) + { + GtkRequisition req; + gint width; + + gtk_widget_get_allocation (widget, &allocation); + width = allocation.width; + gtk_widget_set_size_request (sidebar->priv->menu, -1, -1); + gtk_widget_size_request (sidebar->priv->menu, &req); + gtk_widget_set_size_request (sidebar->priv->menu, + MAX (width, req.width), -1); + gtk_widget_grab_focus (widget); + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), TRUE); + gtk_menu_popup (GTK_MENU (sidebar->priv->menu), + NULL, NULL, + gdict_sidebar_menu_position_function, widget, + event->button, event->time); + + return TRUE; + } + + return FALSE; +} + +static gboolean +gdict_sidebar_select_key_press_cb (GtkWidget *widget, + GdkEventKey *event, + gpointer user_data) +{ + GdictSidebar *sidebar = GDICT_SIDEBAR (user_data); + + if (event->keyval == GDK_space || + event->keyval == GDK_KP_Space || + event->keyval == GDK_Return || + event->keyval == GDK_KP_Enter) + { + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), TRUE); + gtk_menu_popup (GTK_MENU (sidebar->priv->menu), + NULL, NULL, + gdict_sidebar_menu_position_function, widget, + 1, event->time); + + return TRUE; + } + + return FALSE; +} + +static void +gdict_sidebar_close_clicked_cb (GtkWidget *widget, + gpointer user_data) +{ + GdictSidebar *sidebar = GDICT_SIDEBAR (user_data); + + g_signal_emit (sidebar, sidebar_signals[CLOSED], 0); +} + +static void +gdict_sidebar_menu_deactivate_cb (GtkWidget *widget, + gpointer user_data) +{ + GdictSidebar *sidebar = GDICT_SIDEBAR (user_data); + GdictSidebarPrivate *priv = sidebar->priv; + GtkToggleButton *select_button = GTK_TOGGLE_BUTTON (priv->select_button); + + gtk_toggle_button_set_active (select_button, FALSE); +} + +static void +gdict_sidebar_menu_detach_cb (GtkWidget *widget, + GtkMenu *menu) +{ + GdictSidebar *sidebar = GDICT_SIDEBAR (widget); + + sidebar->priv->menu = NULL; +} + +static void +gdict_sidebar_menu_item_activate (GtkWidget *widget, + gpointer user_data) +{ + GdictSidebar *sidebar = GDICT_SIDEBAR (user_data); + GdictSidebarPrivate *priv = sidebar->priv; + GtkWidget *menu_item; + const gchar *id; + SidebarPage *page; + gint current_index; + + menu_item = gtk_menu_get_active (GTK_MENU (priv->menu)); + id = g_object_get_qdata (G_OBJECT (menu_item), sidebar_page_id_quark); + g_assert (id != NULL); + + page = g_hash_table_lookup (priv->pages_by_id, id); + g_assert (page != NULL); + + current_index = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook)); + if (current_index == page->index) + return; + + gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), + page->index); + gtk_label_set_text (GTK_LABEL (priv->label), page->name); + + g_signal_emit (sidebar, sidebar_signals[PAGE_CHANGED], 0); +} + +static void +gdict_sidebar_class_init (GdictSidebarClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (gobject_class, sizeof (GdictSidebarPrivate)); + + sidebar_page_id_quark = g_quark_from_static_string ("gdict-sidebar-page-id"); + + gobject_class->finalize = gdict_sidebar_finalize; + gobject_class->dispose = gdict_sidebar_dispose; + + sidebar_signals[PAGE_CHANGED] = + g_signal_new ("page-changed", + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GdictSidebarClass, page_changed), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + sidebar_signals[CLOSED] = + g_signal_new ("closed", + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GdictSidebarClass, closed), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); +} + +static void +gdict_sidebar_init (GdictSidebar *sidebar) +{ + GdictSidebarPrivate *priv; + GtkWidget *hbox; + GtkWidget *select_hbox; + GtkWidget *select_button; + GtkWidget *close_button; + GtkWidget *arrow; + + sidebar->priv = priv = GDICT_SIDEBAR_GET_PRIVATE (sidebar); + + /* we store all the pages inside the list, but we keep + * a pointer inside the hash table for faster look up + * times; what's inside the table will be destroyed with + * the list, so there's no need to supply the destroy + * functions for keys and values. + */ + priv->pages = NULL; + priv->pages_by_id = g_hash_table_new (g_str_hash, g_str_equal); + + /* top option menu */ + hbox = gtk_hbox_new (FALSE, 0); + gtk_box_pack_start (GTK_BOX (sidebar), hbox, FALSE, FALSE, 0); + gtk_widget_show (hbox); + priv->hbox = hbox; + + select_button = gtk_toggle_button_new (); + gtk_button_set_relief (GTK_BUTTON (select_button), GTK_RELIEF_NONE); + g_signal_connect (select_button, "button-press-event", + G_CALLBACK (gdict_sidebar_select_button_press_cb), + sidebar); + g_signal_connect (select_button, "key-press-event", + G_CALLBACK (gdict_sidebar_select_key_press_cb), + sidebar); + priv->select_button = select_button; + + select_hbox = gtk_hbox_new (FALSE, 0); + + priv->label = gtk_label_new (NULL); + gtk_misc_set_alignment (GTK_MISC (priv->label), 0.0, 0.5); + gtk_box_pack_start (GTK_BOX (select_hbox), priv->label, FALSE, FALSE, 0); + gtk_widget_show (priv->label); + + arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE); + gtk_box_pack_end (GTK_BOX (select_hbox), arrow, FALSE, FALSE, 0); + gtk_widget_show (arrow); + + gtk_container_add (GTK_CONTAINER (select_button), select_hbox); + gtk_widget_show (select_hbox); + + gtk_box_pack_start (GTK_BOX (hbox), select_button, TRUE, TRUE, 0); + gtk_widget_show (select_button); + + close_button = gtk_button_new (); + gtk_button_set_relief (GTK_BUTTON (close_button), GTK_RELIEF_NONE); + gtk_button_set_image (GTK_BUTTON (close_button), + gtk_image_new_from_stock (GTK_STOCK_CLOSE, + GTK_ICON_SIZE_SMALL_TOOLBAR)); + g_signal_connect (close_button, "clicked", + G_CALLBACK (gdict_sidebar_close_clicked_cb), + sidebar); + gtk_box_pack_end (GTK_BOX (hbox), close_button, FALSE, FALSE, 0); + gtk_widget_show (close_button); + priv->close_button = close_button; + + sidebar->priv->menu = gtk_menu_new (); + g_signal_connect (sidebar->priv->menu, "deactivate", + G_CALLBACK (gdict_sidebar_menu_deactivate_cb), + sidebar); + gtk_menu_attach_to_widget (GTK_MENU (sidebar->priv->menu), + GTK_WIDGET (sidebar), + gdict_sidebar_menu_detach_cb); + gtk_widget_show (sidebar->priv->menu); + + sidebar->priv->notebook = gtk_notebook_new (); + gtk_notebook_set_show_border (GTK_NOTEBOOK (sidebar->priv->notebook), FALSE); + gtk_notebook_set_show_tabs (GTK_NOTEBOOK (sidebar->priv->notebook), FALSE); + gtk_box_pack_start (GTK_BOX (sidebar), sidebar->priv->notebook, TRUE, TRUE, 6); + gtk_widget_show (sidebar->priv->notebook); +} + +/* + * Public API + */ + +GtkWidget * +gdict_sidebar_new (void) +{ + return g_object_new (GDICT_TYPE_SIDEBAR, NULL); +} + +void +gdict_sidebar_add_page (GdictSidebar *sidebar, + const gchar *page_id, + const gchar *page_name, + GtkWidget *page_widget) +{ + GdictSidebarPrivate *priv; + SidebarPage *page; + GtkWidget *menu_item; + + g_return_if_fail (GDICT_IS_SIDEBAR (sidebar)); + g_return_if_fail (page_id != NULL); + g_return_if_fail (page_name != NULL); + g_return_if_fail (GTK_IS_WIDGET (page_widget)); + + priv = sidebar->priv; + + if (g_hash_table_lookup (priv->pages_by_id, page_id)) + { + g_warning ("Attempting to add a page to the sidebar with " + "id `%s', but there already is a page with the " + "same id. Aborting...", + page_id); + return; + } + + /* add the page inside the page list */ + page = sidebar_page_new (page_id, page_name, page_widget); + + priv->pages = g_slist_append (priv->pages, page); + g_hash_table_insert (priv->pages_by_id, page->id, page); + + page->index = gtk_notebook_append_page (GTK_NOTEBOOK (priv->notebook), + page_widget, + NULL); + + /* add the menu item for the page */ + menu_item = gtk_image_menu_item_new_with_label (page_name); + g_object_set_qdata_full (G_OBJECT (menu_item), + sidebar_page_id_quark, + g_strdup (page_id), + (GDestroyNotify) g_free); + g_signal_connect (menu_item, "activate", + G_CALLBACK (gdict_sidebar_menu_item_activate), + sidebar); + gtk_menu_shell_append (GTK_MENU_SHELL (priv->menu), menu_item); + gtk_widget_show (menu_item); + page->menu_item = menu_item; + + gtk_menu_shell_select_item (GTK_MENU_SHELL (priv->menu), menu_item); + gtk_label_set_text (GTK_LABEL (priv->label), page_name); + gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), page->index); +} + +void +gdict_sidebar_remove_page (GdictSidebar *sidebar, + const gchar *page_id) +{ + GdictSidebarPrivate *priv; + SidebarPage *page; + GList *children, *l; + + g_return_if_fail (GDICT_IS_SIDEBAR (sidebar)); + g_return_if_fail (page_id != NULL); + + priv = sidebar->priv; + + if ((page = g_hash_table_lookup (priv->pages_by_id, page_id)) == NULL) + { + g_warning ("Attempting to remove a page from the sidebar with " + "id `%s', but there is no page with this id. Aborting...", + page_id); + return; + } + + children = gtk_container_get_children (GTK_CONTAINER (priv->menu)); + for (l = children; l != NULL; l = l->next) + { + GtkWidget *menu_item = l->data; + + if (menu_item == page->menu_item) + { + gtk_container_remove (GTK_CONTAINER (priv->menu), menu_item); + break; + } + } + g_list_free (children); + + gtk_notebook_remove_page (GTK_NOTEBOOK (priv->notebook), page->index); + + g_hash_table_remove (priv->pages_by_id, page->id); + priv->pages = g_slist_remove (priv->pages, page); + + sidebar_page_free (page); + + /* select the first page, if present */ + page = priv->pages->data; + if (page) + { + gtk_menu_shell_select_item (GTK_MENU_SHELL (priv->menu), page->menu_item); + gtk_label_set_text (GTK_LABEL (priv->label), page->name); + gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), page->index); + } + else + gtk_widget_hide (GTK_WIDGET (sidebar)); +} + +void +gdict_sidebar_view_page (GdictSidebar *sidebar, + const gchar *page_id) +{ + GdictSidebarPrivate *priv; + SidebarPage *page; + + g_return_if_fail (GDICT_IS_SIDEBAR (sidebar)); + g_return_if_fail (page_id != NULL); + + priv = sidebar->priv; + page = g_hash_table_lookup (priv->pages_by_id, page_id); + if (!page) + return; + + gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), page->index); + gtk_label_set_text (GTK_LABEL (priv->label), page->name); + gtk_menu_shell_select_item (GTK_MENU_SHELL (priv->menu), page->menu_item); +} + +const gchar * +gdict_sidebar_current_page (GdictSidebar *sidebar) +{ + GdictSidebarPrivate *priv; + gint index; + SidebarPage *page; + + g_return_val_if_fail (GDICT_IS_SIDEBAR (sidebar), NULL); + + priv = sidebar->priv; + + index = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook)); + page = g_slist_nth_data (priv->pages, index); + g_assert (page != NULL); + + return page->id; +} + +gchar ** +gdict_sidebar_list_pages (GdictSidebar *sidebar, + gsize *length) +{ + GdictSidebarPrivate *priv; + gchar **retval; + gint i; + GSList *l; + + g_return_val_if_fail (GDICT_IS_SIDEBAR (sidebar), NULL); + + priv = sidebar->priv; + + retval = g_new (gchar*, g_slist_length (priv->pages) + 1); + for (l = priv->pages, i = 0; l; l = l->next, i++) + retval[i++] = g_strdup (l->data); + + retval[i] = NULL; + + if (length) + *length = i; + + return retval; +} diff --git a/mate-dictionary/src/gdict-sidebar.h b/mate-dictionary/src/gdict-sidebar.h new file mode 100644 index 00000000..82fa5c4c --- /dev/null +++ b/mate-dictionary/src/gdict-sidebar.h @@ -0,0 +1,75 @@ +/* gdict-sidebar.h - sidebar widget + * + * Copyright (C) 2006 Emmanuele Bassi <[email protected]> + * + * This program 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * + * Based on the equivalent widget from Evince + * by Jonathan Blandford, + * Copyright (C) 2004 Red Hat, Inc. + */ + +#ifndef __GDICT_SIDEBAR_H__ +#define __GDICT_SIDEBAR_H__ + +#include <gtk/gtk.h> + +#define GDICT_TYPE_SIDEBAR (gdict_sidebar_get_type ()) +#define GDICT_SIDEBAR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDICT_TYPE_SIDEBAR, GdictSidebar)) +#define GDICT_IS_SIDEBAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDICT_TYPE_SIDEBAR)) +#define GDICT_SIDEBAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDICT_TYPE_SIDEBAR, GdictSidebarClass)) +#define GDICT_IS_SIDEBAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDICT_TYPE_SIDEBAR)) +#define GDICT_SIDEBAR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDICT_TYPE_SIDEBAR, GdictSidebarClass)) + +typedef struct _GdictSidebar GdictSidebar; +typedef struct _GdictSidebarPrivate GdictSidebarPrivate; +typedef struct _GdictSidebarClass GdictSidebarClass; + +struct _GdictSidebar +{ + GtkVBox parent_instance; + + GdictSidebarPrivate *priv; +}; + +struct _GdictSidebarClass +{ + GtkVBoxClass parent_class; + + void (*page_changed) (GdictSidebar *sidebar); + void (*closed) (GdictSidebar *sidebar); + + void (*_gdict_padding_1) (void); + void (*_gdict_padding_2) (void); + void (*_gdict_padding_3) (void); + void (*_gdict_padding_4) (void); +}; + +GType gdict_sidebar_get_type (void) G_GNUC_CONST; + +GtkWidget * gdict_sidebar_new (void); +void gdict_sidebar_add_page (GdictSidebar *sidebar, + const gchar *page_id, + const gchar *page_name, + GtkWidget *page_widget); +void gdict_sidebar_remove_page (GdictSidebar *sidebar, + const gchar *page_id); +void gdict_sidebar_view_page (GdictSidebar *sidebar, + const gchar *page_id); +const gchar *gdict_sidebar_current_page (GdictSidebar *sidebar); +gchar ** gdict_sidebar_list_pages (GdictSidebar *sidebar, + gsize *length) G_GNUC_MALLOC; + +#endif /* __GDICT_SIDEBAR_H__ */ diff --git a/mate-dictionary/src/gdict-source-dialog.c b/mate-dictionary/src/gdict-source-dialog.c new file mode 100644 index 00000000..2a3014ab --- /dev/null +++ b/mate-dictionary/src/gdict-source-dialog.c @@ -0,0 +1,759 @@ +/* gdict-source-dialog.c - source dialog + * + * This file is part of MATE Dictionary + * + * Copyright (C) 2005 Emmanuele Bassi + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <sys/types.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#include <glib/gi18n.h> +#include <mateconf/mateconf-client.h> + +#include "gdict-source-dialog.h" +#include "gdict-common.h" + +#define GDICT_SOURCE_UI PKGDATADIR "/mate-dictionary-source.ui" + +/********************* + * GdictSourceDialog * + *********************/ + +struct _GdictSourceDialog +{ + GtkDialog parent_instance; + + GtkBuilder *builder; + + MateConfClient *mateconf_client; + guint notify_id; + + GdictSourceLoader *loader; + GdictSource *source; + gchar *source_name; + GdictContext *context; + + GdictSourceDialogAction action; + + GdictSourceTransport transport; + + GtkWidget *add_button; + GtkWidget *close_button; + GtkWidget *cancel_button; + GtkWidget *help_button; + + GtkWidget *db_chooser; + GtkWidget *strat_chooser; + + GtkWidget *transport_combo; +}; + +struct _GdictSourceDialogClass +{ + GtkDialogClass parent_class; +}; + +enum +{ + PROP_0, + + PROP_SOURCE_LOADER, + PROP_SOURCE_NAME, + PROP_ACTION +}; + +G_DEFINE_TYPE (GdictSourceDialog, gdict_source_dialog, GTK_TYPE_DIALOG); + +static void +set_source_loader (GdictSourceDialog *dialog, + GdictSourceLoader *loader) +{ + if (dialog->loader) + g_object_unref (dialog->loader); + + dialog->loader = g_object_ref (loader); +} + +static void +transport_combo_changed_cb (GtkWidget *widget, + gpointer user_data) +{ + GdictSourceDialog *dialog = GDICT_SOURCE_DIALOG (user_data); + gint transport; + + transport = gtk_combo_box_get_active (GTK_COMBO_BOX (widget)); + if (transport == dialog->transport) + return; + + if (transport == GDICT_SOURCE_TRANSPORT_DICTD) + { + gtk_widget_show (GTK_WIDGET (gtk_builder_get_object (dialog->builder, "hostname_label"))); + gtk_widget_show (GTK_WIDGET (gtk_builder_get_object (dialog->builder, "hostname_entry"))); + gtk_widget_show (GTK_WIDGET (gtk_builder_get_object (dialog->builder, "port_label"))); + gtk_widget_show (GTK_WIDGET (gtk_builder_get_object (dialog->builder, "port_entry"))); + + if (dialog->action == GDICT_SOURCE_DIALOG_CREATE) + { + gtk_widget_set_sensitive (dialog->add_button, TRUE); + + dialog->transport = GDICT_SOURCE_TRANSPORT_DICTD; + } + } + else + { + gtk_widget_hide (GTK_WIDGET (gtk_builder_get_object (dialog->builder, "hostname_label"))); + gtk_widget_hide (GTK_WIDGET (gtk_builder_get_object (dialog->builder, "hostname_entry"))); + gtk_widget_hide (GTK_WIDGET (gtk_builder_get_object (dialog->builder, "port_label"))); + gtk_widget_hide (GTK_WIDGET (gtk_builder_get_object (dialog->builder, "port_entry"))); + + if (dialog->action == GDICT_SOURCE_DIALOG_CREATE) + { + gtk_widget_set_sensitive (dialog->add_button, FALSE); + + dialog->transport = GDICT_SOURCE_TRANSPORT_INVALID; + } + } +} + +static gchar * +get_text_from_entry (GdictSourceDialog *dialog, + const gchar *entry_name) +{ + GtkWidget *entry; + gchar *retval; + + entry = GTK_WIDGET (gtk_builder_get_object (dialog->builder, entry_name)); + if (!entry) + return NULL; + + retval = gtk_editable_get_chars (GTK_EDITABLE (entry), 0, -1); + + return retval; +} + +static void +set_text_to_entry (GdictSourceDialog *dialog, + const gchar *entry_name, + const gchar *text) +{ + GtkWidget *entry; + + entry = GTK_WIDGET (gtk_builder_get_object (dialog->builder, entry_name)); + if (!entry) + return; + + gtk_entry_set_text (GTK_ENTRY (entry), text); +} + +static void +set_transport_settings (GdictSourceDialog *dialog) +{ + switch (dialog->transport) + { + case GDICT_SOURCE_TRANSPORT_DICTD: + { + GdictClientContext *context; + const gchar *hostname; + gchar *port_str; + guint port; + + context = GDICT_CLIENT_CONTEXT (dialog->context); + hostname = gdict_client_context_get_hostname (context); + port = gdict_client_context_get_port (context); + port_str = g_strdup_printf ("%d", port); + + set_text_to_entry (dialog, "hostname_entry", hostname); + set_text_to_entry (dialog, "port_entry", port_str); + + gtk_widget_show (GTK_WIDGET (gtk_builder_get_object (dialog->builder, "hostname_label"))); + gtk_widget_show (GTK_WIDGET (gtk_builder_get_object (dialog->builder, "hostname_entry"))); + gtk_widget_show (GTK_WIDGET (gtk_builder_get_object (dialog->builder, "port_label"))); + gtk_widget_show (GTK_WIDGET (gtk_builder_get_object (dialog->builder, "port_entry"))); + + g_free (port_str); + } + break; + case GDICT_SOURCE_TRANSPORT_INVALID: + default: + break; + } +} + +static void +update_dialog_ui (GdictSourceDialog *dialog) +{ + GdictSource *source; + + /* TODO - add code to update the contents of the dialog depending + * on the action; if we are in _CREATE, no action is needed + */ + switch (dialog->action) + { + case GDICT_SOURCE_DIALOG_VIEW: + case GDICT_SOURCE_DIALOG_EDIT: + if (!dialog->source_name) + { + g_warning ("Attempting to retrieve source, but no " + "source name has been defined. Aborting..."); + return; + } + + source = gdict_source_loader_get_source (dialog->loader, + dialog->source_name); + if (!source) + { + g_warning ("Attempting to retrieve source, but no " + "source named `%s' was found. Aborting...", + dialog->source_name); + return; + } + + g_object_ref (source); + + dialog->source = source; + set_text_to_entry (dialog, "description_entry", + gdict_source_get_description (source)); + + dialog->transport = gdict_source_get_transport (source); + gtk_combo_box_set_active (GTK_COMBO_BOX (dialog->transport_combo), + (gint) dialog->transport); + + /* set the context for the database and strategy choosers */ + dialog->context = gdict_source_get_context (source); + if (!dialog->context) + { + g_warning ("Attempting to retrieve the context, but " + "none was found for source `%s'.", + dialog->source_name); + return; + } + + set_transport_settings (dialog); + + gdict_database_chooser_set_context (GDICT_DATABASE_CHOOSER (dialog->db_chooser), + dialog->context); + gdict_database_chooser_refresh (GDICT_DATABASE_CHOOSER (dialog->db_chooser)); + gdict_strategy_chooser_set_context (GDICT_STRATEGY_CHOOSER (dialog->strat_chooser), + dialog->context); + gdict_strategy_chooser_refresh (GDICT_STRATEGY_CHOOSER (dialog->strat_chooser)); + break; + case GDICT_SOURCE_DIALOG_CREATE: + /* DICTD transport is default */ + gtk_combo_box_set_active (GTK_COMBO_BOX (dialog->transport_combo), 0); + g_signal_emit_by_name (dialog->transport_combo, "changed"); + break; + default: + g_assert_not_reached (); + break; + } +} + +static void +build_new_source (GdictSourceDialog *dialog) +{ + GdictSource *source; + gchar *name, *text; + GdictSourceTransport transport; + gchar *host, *port; + gchar *data; + gsize length; + GError *error; + gchar *filename; + GdictDatabaseChooser *db_chooser; + GdictStrategyChooser *strat_chooser; + + source = gdict_source_new (); + + /* use the timestamp and the pid to get a unique name */ + name = g_strdup_printf ("source-%lu-%u", + (gulong) time (NULL), + (guint) getpid ()); + gdict_source_set_name (source, name); + g_free (name); + + text = get_text_from_entry (dialog, "description_entry"); + gdict_source_set_description (source, text); + g_free (text); + + db_chooser = GDICT_DATABASE_CHOOSER (dialog->db_chooser); + text = gdict_database_chooser_get_current_database (db_chooser); + gdict_source_set_database (source, text); + g_free (text); + + strat_chooser = GDICT_STRATEGY_CHOOSER (dialog->strat_chooser); + text = gdict_strategy_chooser_get_current_strategy (strat_chooser); + gdict_source_set_strategy (source, text); + g_free (text); + + /* get the selected transport id */ + transport = dialog->transport; + switch (transport) + { + case GDICT_SOURCE_TRANSPORT_DICTD: + host = get_text_from_entry (dialog, "hostname_entry"); + port = get_text_from_entry (dialog, "port_entry"); + + gdict_source_set_transport (source, GDICT_SOURCE_TRANSPORT_DICTD, + "hostname", host, + "port", atoi (port), + NULL); + + g_free (host); + g_free (port); + break; + case GDICT_SOURCE_TRANSPORT_INVALID: + default: + g_warning ("Invalid transport"); + return; + } + + error = NULL; + data = gdict_source_to_data (source, &length, &error); + if (error) + { + gdict_show_gerror_dialog (GTK_WINDOW (dialog), + _("Unable to create a source file"), + error); + + g_object_unref (source); + return; + } + + name = g_strdup_printf ("%s.desktop", gdict_source_get_name (source)); + filename = g_build_filename (g_get_home_dir (), + ".mate2", + "mate-dictionary", + name, + NULL); + g_free (name); + + g_file_set_contents (filename, data, length, &error); + if (error) + gdict_show_gerror_dialog (GTK_WINDOW (dialog), + _("Unable to save source file"), + error); + + g_free (filename); + g_free (data); + g_object_unref (source); +} + +static void +save_source (GdictSourceDialog *dialog) +{ + GdictSource *source; + GdictDatabaseChooser *db_chooser; + GdictStrategyChooser *strat_chooser; + gchar *name, *text; + GdictSourceTransport transport; + gchar *host, *port; + gchar *data; + gsize length; + GError *error; + gchar *filename; + + source = gdict_source_loader_get_source (dialog->loader, + dialog->source_name); + if (!source) + { + g_warning ("Attempting to save source `%s', but no " + "source for that name was found.", + dialog->source_name); + + return; + } + + text = get_text_from_entry (dialog, "description_entry"); + gdict_source_set_description (source, text); + g_free (text); + + db_chooser = GDICT_DATABASE_CHOOSER (dialog->db_chooser); + text = gdict_database_chooser_get_current_database (db_chooser); + gdict_source_set_database (source, text); + g_free (text); + + strat_chooser = GDICT_STRATEGY_CHOOSER (dialog->strat_chooser); + text = gdict_strategy_chooser_get_current_strategy (strat_chooser); + gdict_source_set_strategy (source, text); + g_free (text); + + + /* get the selected transport id */ + transport = dialog->transport; + switch (transport) + { + case GDICT_SOURCE_TRANSPORT_DICTD: + host = get_text_from_entry (dialog, "hostname_entry"); + port = get_text_from_entry (dialog, "port_entry"); + + gdict_source_set_transport (source, GDICT_SOURCE_TRANSPORT_DICTD, + "hostname", host, + "port", atoi (port), + NULL); + + g_free (host); + g_free (port); + break; + case GDICT_SOURCE_TRANSPORT_INVALID: + default: + g_warning ("Invalid transport"); + return; + } + + error = NULL; + data = gdict_source_to_data (source, &length, &error); + if (error) + { + gdict_show_gerror_dialog (GTK_WINDOW (dialog), + _("Unable to create a source file"), + error); + + g_object_unref (source); + return; + } + + name = g_strdup_printf ("%s.desktop", gdict_source_get_name (source)); + filename = g_build_filename (g_get_home_dir (), + ".mate2", + "mate-dictionary", + name, + NULL); + g_free (name); + + g_file_set_contents (filename, data, length, &error); + if (error) + gdict_show_gerror_dialog (GTK_WINDOW (dialog), + _("Unable to save source file"), + error); + + g_free (filename); + g_free (data); + g_object_unref (source); +} + +static void +gdict_source_dialog_response_cb (GtkDialog *dialog, + gint response_id, + gpointer user_data) +{ + GError *err = NULL; + + switch (response_id) + { + case GTK_RESPONSE_ACCEPT: + build_new_source (GDICT_SOURCE_DIALOG (dialog)); + break; + case GTK_RESPONSE_HELP: + gtk_show_uri (gtk_widget_get_screen (GTK_WIDGET (dialog)), + "ghelp:mate-dictionary#mate-dictionary-add-source", + gtk_get_current_event_time (), &err); + if (err) + { + gdict_show_gerror_dialog (GTK_WINDOW (dialog), + _("There was an error while displaying help"), + err); + g_error_free (err); + } + + /* we don't want the dialog to close itself */ + g_signal_stop_emission_by_name (dialog, "response"); + break; + case GTK_RESPONSE_CLOSE: + save_source (GDICT_SOURCE_DIALOG (dialog)); + break; + case GTK_RESPONSE_CANCEL: + break; + default: + break; + } +} + +static void +gdict_source_dialog_finalize (GObject *object) +{ + GdictSourceDialog *dialog = GDICT_SOURCE_DIALOG (object); + + if (dialog->mateconf_client) + g_object_unref (dialog->mateconf_client); + + if (dialog->builder) + g_object_unref (dialog->builder); + + if (dialog->source_name) + g_free (dialog->source_name); + + if (dialog->source) + g_object_unref (dialog->source); + + if (dialog->loader) + g_object_unref (dialog->loader); + + G_OBJECT_CLASS (gdict_source_dialog_parent_class)->finalize (object); +} + +static void +gdict_source_dialog_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GdictSourceDialog *dialog = GDICT_SOURCE_DIALOG (object); + + switch (prop_id) + { + case PROP_SOURCE_LOADER: + set_source_loader (dialog, g_value_get_object (value)); + break; + case PROP_SOURCE_NAME: + g_free (dialog->source_name); + dialog->source_name = g_strdup (g_value_get_string (value)); + break; + case PROP_ACTION: + dialog->action = (GdictSourceDialogAction) g_value_get_int (value); + break; + default: + break; + } +} + +static void +gdict_source_dialog_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GdictSourceDialog *dialog = GDICT_SOURCE_DIALOG (object); + + switch (prop_id) + { + case PROP_SOURCE_LOADER: + g_value_set_object (value, dialog->loader); + break; + case PROP_SOURCE_NAME: + g_value_set_string (value, dialog->source_name); + break; + case PROP_ACTION: + g_value_set_int (value, dialog->action); + break; + default: + break; + } +} + +static GObject * +gdict_source_dialog_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_params) +{ + GObject *object; + GdictSourceDialog *dialog; + GtkWidget *vbox; + GError *error = NULL; + + object = G_OBJECT_CLASS (gdict_source_dialog_parent_class)->constructor (type, + n_construct_properties, + construct_params); + dialog = GDICT_SOURCE_DIALOG (object); + + gtk_container_set_border_width (GTK_CONTAINER (dialog), 5); + gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), 2); + + gtk_widget_push_composite_child (); + + /* get the UI from the GtkBuilder file */ + dialog->builder = gtk_builder_new (); + gtk_builder_add_from_file (dialog->builder, GDICT_SOURCE_UI, &error); + + if (error) { + g_critical ("Unable to load the user interface definition file: %s", + error->message); + g_error_free (error); + g_assert_not_reached (); + } + + /* the main widget */ + gtk_container_add (GTK_CONTAINER (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), + GTK_WIDGET (gtk_builder_get_object (dialog->builder, "source_root"))); + + /* the transport combo changes the UI by changing the visible widgets + * bound to the transport's own options. + */ + dialog->transport_combo = GTK_WIDGET (gtk_builder_get_object (dialog->builder, "transport_combo")); + g_signal_connect (dialog->transport_combo, "changed", + G_CALLBACK (transport_combo_changed_cb), + dialog); + + /* the help button is always visible */ + dialog->help_button = gtk_dialog_add_button (GTK_DIALOG (dialog), + GTK_STOCK_HELP, + GTK_RESPONSE_HELP); + + vbox = GTK_WIDGET (gtk_builder_get_object (dialog->builder, "db-vbox")); + dialog->db_chooser = gdict_database_chooser_new (); + gtk_box_pack_start (GTK_BOX (vbox), dialog->db_chooser, TRUE, TRUE, 0); + gtk_widget_show (dialog->db_chooser); + + vbox = GTK_WIDGET (gtk_builder_get_object (dialog->builder, "strat-vbox")); + dialog->strat_chooser = gdict_strategy_chooser_new (); + gtk_box_pack_start (GTK_BOX (vbox), dialog->strat_chooser, TRUE, TRUE, 0); + gtk_widget_show (dialog->strat_chooser); + + /* the UI changes depending on the action that the source dialog + * should perform + */ + switch (dialog->action) + { + case GDICT_SOURCE_DIALOG_VIEW: + /* disable every editable widget */ + gtk_editable_set_editable (GTK_EDITABLE (gtk_builder_get_object (dialog->builder, "name_entry")), FALSE); + gtk_editable_set_editable (GTK_EDITABLE (gtk_builder_get_object (dialog->builder, "description_entry")), FALSE); + gtk_editable_set_editable (GTK_EDITABLE (gtk_builder_get_object (dialog->builder, "hostname_entry")), FALSE); + gtk_editable_set_editable (GTK_EDITABLE (gtk_builder_get_object (dialog->builder, "port_entry")), FALSE); + + gtk_widget_set_sensitive (dialog->transport_combo, FALSE); + + /* we just allow closing the dialog */ + dialog->close_button = gtk_dialog_add_button (GTK_DIALOG (dialog), + GTK_STOCK_CLOSE, + GTK_RESPONSE_CLOSE); + break; + case GDICT_SOURCE_DIALOG_CREATE: + dialog->cancel_button = gtk_dialog_add_button (GTK_DIALOG (dialog), + GTK_STOCK_CANCEL, + GTK_RESPONSE_CANCEL); + dialog->add_button = gtk_dialog_add_button (GTK_DIALOG (dialog), + GTK_STOCK_ADD, + GTK_RESPONSE_ACCEPT); + /* the "add" button sensitivity is controlled by the transport_combo + * since it's the only setting that makes a source usable. + */ + gtk_widget_set_sensitive (dialog->add_button, FALSE); + break; + case GDICT_SOURCE_DIALOG_EDIT: + dialog->cancel_button = gtk_dialog_add_button (GTK_DIALOG (dialog), + GTK_STOCK_CANCEL, + GTK_RESPONSE_CANCEL); + dialog->close_button = gtk_dialog_add_button (GTK_DIALOG (dialog), + GTK_STOCK_CLOSE, + GTK_RESPONSE_CLOSE); + break; + default: + g_assert_not_reached (); + break; + } + + /* this will take care of updating the contents of the dialog + * based on the action + */ + update_dialog_ui (dialog); + + gtk_widget_pop_composite_child (); + + return object; +} + +static void +gdict_source_dialog_class_init (GdictSourceDialogClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->constructor = gdict_source_dialog_constructor; + gobject_class->set_property = gdict_source_dialog_set_property; + gobject_class->get_property = gdict_source_dialog_get_property; + gobject_class->finalize = gdict_source_dialog_finalize; + + g_object_class_install_property (gobject_class, + PROP_SOURCE_LOADER, + g_param_spec_object ("source-loader", + "Source Loader", + "The GdictSourceLoader used by the application", + GDICT_TYPE_SOURCE_LOADER, + (G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY))); + g_object_class_install_property (gobject_class, + PROP_SOURCE_NAME, + g_param_spec_string ("source-name", + "Source Name", + "The source name", + NULL, + (G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT))); + g_object_class_install_property (gobject_class, + PROP_ACTION, + g_param_spec_int ("action", + "Action", + "The action the source dialog should perform", + -1, + GDICT_SOURCE_DIALOG_EDIT, + GDICT_SOURCE_DIALOG_VIEW, + (G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY))); +} + +static void +gdict_source_dialog_init (GdictSourceDialog *dialog) +{ + gtk_widget_set_size_request (GTK_WIDGET (dialog), 400, 300); + gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE); + + gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE); + + dialog->transport = GDICT_SOURCE_TRANSPORT_INVALID; + + g_signal_connect (dialog, "response", + G_CALLBACK (gdict_source_dialog_response_cb), + NULL); +} + +GtkWidget * +gdict_source_dialog_new (GtkWindow *parent, + const gchar *title, + GdictSourceDialogAction action, + GdictSourceLoader *loader, + const gchar *source_name) +{ + GtkWidget *retval; + + g_return_val_if_fail ((parent == NULL || GTK_IS_WINDOW (parent)), NULL); + g_return_val_if_fail (GDICT_IS_SOURCE_LOADER (loader), NULL); + + retval = g_object_new (GDICT_TYPE_SOURCE_DIALOG, + "source-loader", loader, + "source-name", source_name, + "action", action, + "title", title, + NULL); + + if (parent) + { + gtk_window_set_transient_for (GTK_WINDOW (retval), parent); + gtk_window_set_destroy_with_parent (GTK_WINDOW (retval), TRUE); + gtk_window_set_screen (GTK_WINDOW (retval), + gtk_widget_get_screen (GTK_WIDGET (parent))); + } + + return retval; +} diff --git a/mate-dictionary/src/gdict-source-dialog.h b/mate-dictionary/src/gdict-source-dialog.h new file mode 100644 index 00000000..ecddd8ff --- /dev/null +++ b/mate-dictionary/src/gdict-source-dialog.h @@ -0,0 +1,54 @@ +/* gdict-source-dialog.h - source dialog + * + * This file is part of MATE Dictionary + * + * Copyright (C) 2005 Emmanuele Bassi + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef __GDICT_SOURCE_DIALOG_H__ +#define __GDICT_SOURCE_DIALOG_H__ + +#include <gtk/gtk.h> +#include <libgdict/gdict.h> + +G_BEGIN_DECLS + +#define GDICT_TYPE_SOURCE_DIALOG (gdict_source_dialog_get_type ()) +#define GDICT_SOURCE_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDICT_TYPE_SOURCE_DIALOG, GdictSourceDialog)) +#define GDICT_IS_SOURCE_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDICT_TYPE_SOURCE_DIALOG)) + +typedef enum +{ + GDICT_SOURCE_DIALOG_VIEW, + GDICT_SOURCE_DIALOG_CREATE, + GDICT_SOURCE_DIALOG_EDIT +} GdictSourceDialogAction; + +typedef struct _GdictSourceDialog GdictSourceDialog; +typedef struct _GdictSourceDialogClass GdictSourceDialogClass; + +GType gdict_source_dialog_get_type (void) G_GNUC_CONST; +GtkWidget *gdict_source_dialog_new (GtkWindow *parent, + const gchar *title, + GdictSourceDialogAction action, + GdictSourceLoader *loader, + const gchar *source_name); + +G_END_DECLS + +#endif /* __GDICT_SOURCE_DIALOG_H__ */ diff --git a/mate-dictionary/src/gdict-window.c b/mate-dictionary/src/gdict-window.c new file mode 100644 index 00000000..1be0256c --- /dev/null +++ b/mate-dictionary/src/gdict-window.c @@ -0,0 +1,2214 @@ +/* gdict-window.c - main application window + * + * This file is part of MATE Dictionary + * + * Copyright (C) 2005 Emmanuele Bassi + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> + +#include <gtk/gtk.h> +#include <glib.h> +#include <glib/gi18n.h> + +#include <libgdict/gdict.h> + +#include "gdict-sidebar.h" +#include "gdict-print.h" +#include "gdict-pref-dialog.h" +#include "gdict-about.h" +#include "gdict-window.h" +#include "gdict-common.h" + +#define GDICT_WINDOW_COLUMNS 56 +#define GDICT_WINDOW_ROWS 33 + +#define GDICT_WINDOW_MIN_WIDTH 400 +#define GDICT_WINDOW_MIN_HEIGHT 330 + +/* sidebar pages logical ids */ +#define GDICT_SIDEBAR_SPELLER_PAGE "speller" +#define GDICT_SIDEBAR_DATABASES_PAGE "db-chooser" +#define GDICT_SIDEBAR_STRATEGIES_PAGE "strat-chooser" +#define GDICT_SIDEBAR_SOURCES_PAGE "source-chooser" + +enum +{ + COMPLETION_TEXT_COLUMN, + + COMPLETION_N_COLUMNS +}; + +enum +{ + PROP_0, + + PROP_ACTION, + PROP_SOURCE_LOADER, + PROP_SOURCE_NAME, + PROP_PRINT_FONT, + PROP_DEFBOX_FONT, + PROP_WORD, + PROP_WINDOW_ID +}; + +enum +{ + CREATED, + + LAST_SIGNAL +}; + +static guint gdict_window_signals[LAST_SIGNAL] = { 0 }; + +static const GtkTargetEntry drop_types[] = +{ + { "text/plain", 0, 0 }, + { "TEXT", 0, 0 }, + { "STRING", 0, 0 }, + { "UTF8_STRING", 0, 0 }, +}; +static const guint n_drop_types = G_N_ELEMENTS (drop_types); + + + +G_DEFINE_TYPE (GdictWindow, gdict_window, GTK_TYPE_WINDOW); + + +static void +gdict_window_finalize (GObject *gobject) +{ + GdictWindow *window = GDICT_WINDOW (gobject); + + g_free (window->source_name); + g_free (window->print_font); + g_free (window->defbox_font); + g_free (window->word); + g_free (window->database); + g_free (window->strategy); + + G_OBJECT_CLASS (gdict_window_parent_class)->finalize (gobject); +} + +static void +gdict_window_dispose (GObject *gobject) +{ + GdictWindow *window = GDICT_WINDOW (gobject); + + if (window->notify_id) + { + mateconf_client_notify_remove (window->mateconf_client, window->notify_id); + window->notify_id = 0; + } + + if (window->font_notify_id) + { + mateconf_client_notify_remove (window->mateconf_client, + window->font_notify_id); + window->font_notify_id = 0; + } + + if (window->mateconf_client) + { + g_object_unref (window->mateconf_client); + window->mateconf_client = NULL; + } + + if (window->context) + { + if (window->lookup_start_id) + { + g_signal_handler_disconnect (window->context, + window->lookup_start_id); + g_signal_handler_disconnect (window->context, + window->definition_id); + g_signal_handler_disconnect (window->context, + window->lookup_end_id); + g_signal_handler_disconnect (window->context, + window->error_id); + + window->lookup_start_id = 0; + window->definition_id = 0; + window->lookup_end_id = 0; + window->error_id = 0; + } + + g_object_unref (window->context); + window->context = NULL; + } + + if (window->loader) + { + g_object_unref (window->loader); + window->loader = NULL; + } + + if (window->ui_manager) + { + g_object_unref (window->ui_manager); + window->ui_manager = NULL; + } + + if (window->action_group) + { + g_object_unref (window->action_group); + window->action_group = NULL; + } + + if (window->completion) + { + g_object_unref (window->completion); + window->completion = NULL; + } + + if (window->completion_model) + { + g_object_unref (window->completion_model); + window->completion_model = NULL; + } + + if (window->busy_cursor) + { + gdk_cursor_unref (window->busy_cursor); + window->busy_cursor = NULL; + } + + G_OBJECT_CLASS (gdict_window_parent_class)->dispose (gobject); +} + +static const gchar *toggle_state[] = { + "/MainMenu/FileMenu/SaveAsMenu", + "/MainMenu/FileMenu/FilePreviewMenu", + "/MainMenu/FileMenu/FilePrintMenu", + "/MainMenu/GoMenu", +}; + +static gint n_toggle_state = G_N_ELEMENTS (toggle_state); + +static void +gdict_window_ensure_menu_state (GdictWindow *window) +{ + gint i; + gboolean is_sensitive; + + g_assert (GDICT_IS_WINDOW (window)); + + if (!window->ui_manager) + return; + + is_sensitive = !!(window->word != NULL); + for (i = 0; i < n_toggle_state; i++) + { + GtkWidget *item; + + item = gtk_ui_manager_get_widget (window->ui_manager, toggle_state[i]); + if (!item) + continue; + + gtk_widget_set_sensitive (item, is_sensitive); + } +} + +static void +gdict_window_set_sidebar_visible (GdictWindow *window, + gboolean is_visible) +{ + g_assert (GDICT_IS_WINDOW (window)); + + if (is_visible != window->sidebar_visible) + { + GtkAction *action; + + if (is_visible) + gtk_widget_show (window->sidebar_frame); + else + gtk_widget_hide (window->sidebar_frame); + + action = gtk_action_group_get_action (window->action_group, + "ViewSidebar"); + gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), is_visible); + + window->sidebar_visible = is_visible; + } +} + +static void +gdict_window_set_statusbar_visible (GdictWindow *window, + gboolean is_visible) +{ + g_assert (GDICT_IS_WINDOW (window)); + + if (is_visible != window->statusbar_visible) + { + GtkAction *action; + + if (is_visible) + gtk_widget_show (window->status); + else + gtk_widget_hide (window->status); + + action = gtk_action_group_get_action (window->action_group, + "ViewStatusbar"); + gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), is_visible); + + window->statusbar_visible = is_visible; + } +} + +static void +gdict_window_definition_cb (GdictContext *context, + GdictDefinition *definition, + GdictWindow *window) +{ + gint total, n; + gdouble fraction; + + g_assert (GDICT_IS_WINDOW (window)); + + total = gdict_definition_get_total (definition); + n = window->current_definition + 1; + + fraction = CLAMP (((gdouble) n / (gdouble) total), 0.0, 1.0); + + gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (window->progress), + fraction); + while (gtk_events_pending ()) + gtk_main_iteration (); + + window->current_definition = n; +} + +static void +gdict_window_lookup_start_cb (GdictContext *context, + GdictWindow *window) +{ + gchar *message; + + if (!window->word) + return; + + if (!window->busy_cursor) + window->busy_cursor = gdk_cursor_new (GDK_WATCH); + + message = g_strdup_printf (_("Searching for '%s'..."), window->word); + + if (window->status && window->statusbar_visible) + gtk_statusbar_push (GTK_STATUSBAR (window->status), 0, message); + + if (window->progress) + gtk_widget_show (window->progress); + + window->max_definition = -1; + window->last_definition = 0; + window->current_definition = 0; + + gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET (window)), window->busy_cursor); + + g_free (message); +} + +static void +gdict_window_lookup_end_cb (GdictContext *context, + GdictWindow *window) +{ + gchar *message; + gint count; + GtkTreeIter iter; + GdictSource *source; + GdictContext *speller_context; + + count = window->current_definition; + + window->max_definition = count - 1; + + if (count == 0) + message = g_strdup (_("No definitions found")); + else + message = g_strdup_printf (ngettext("A definition found", + "%d definitions found", + count), + count); + + if (window->status && window->statusbar_visible) + gtk_statusbar_push (GTK_STATUSBAR (window->status), 0, message); + + if (window->progress) + gtk_widget_hide (window->progress); + + /* we clone the context, so that the signals that it + * fires do not get caught by the signal handlers we + * use for getting the definitions. + */ + source = gdict_source_loader_get_source (window->loader, window->source_name); + speller_context = gdict_source_get_context (source); + gdict_speller_set_context (GDICT_SPELLER (window->speller), speller_context); + g_object_unref (speller_context); + g_object_unref (source); + + /* search for similar words; if we have a no-match we already started + * looking in the error signal handler + */ + if (count != 0 && window->word) + { + gdict_speller_set_strategy (GDICT_SPELLER (window->speller), window->strategy); + gdict_speller_match (GDICT_SPELLER (window->speller), window->word); + gtk_list_store_append (window->completion_model, &iter); + gtk_list_store_set (window->completion_model, &iter, + COMPLETION_TEXT_COLUMN, window->word, + -1); + } + + gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET (window)), NULL); + g_free (message); + + if (count == 0) + { + g_free (window->word); + window->word = NULL; + } + + gdict_window_ensure_menu_state (window); +} + +static void +gdict_window_error_cb (GdictContext *context, + const GError *error, + GdictWindow *window) +{ + gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET (window)), NULL); + + if (window->status && window->statusbar_visible) + gtk_statusbar_push (GTK_STATUSBAR (window->status), 0, + _("No definitions found")); + + gtk_widget_hide (window->progress); + + /* launch the speller only on NO_MATCH */ + if (error->code == GDICT_CONTEXT_ERROR_NO_MATCH) + { + GdictSource *source; + GdictContext *context; + + gdict_window_set_sidebar_visible (window, TRUE); + gdict_sidebar_view_page (GDICT_SIDEBAR (window->sidebar), + GDICT_SIDEBAR_SPELLER_PAGE); + + /* we clone the context, so that the signals that it + * fires do not get caught by the signal handlers we + * use for getting the definitions. + */ + source = gdict_source_loader_get_source (window->loader, + window->source_name); + context = gdict_source_get_context (source); + + gdict_speller_set_context (GDICT_SPELLER (window->speller), + context); + g_object_unref (context); + g_object_unref (source); + + gdict_speller_set_strategy (GDICT_SPELLER (window->speller), + window->strategy); + + gdict_speller_match (GDICT_SPELLER (window->speller), + window->word); + } + + /* unset the word and update the UI */ + g_free (window->word); + window->word = NULL; + + gdict_window_ensure_menu_state (window); +} + +static void +gdict_window_set_database (GdictWindow *window, + const gchar *database) +{ + g_free (window->database); + + if (database) + window->database = g_strdup (database); + else + window->database = gdict_mateconf_get_string_with_default (window->mateconf_client, + GDICT_MATECONF_DATABASE_KEY, + GDICT_DEFAULT_DATABASE); + + if (window->defbox) + gdict_defbox_set_database (GDICT_DEFBOX (window->defbox), + window->database); + + if (window->db_chooser) + gdict_database_chooser_set_current_database (GDICT_DATABASE_CHOOSER (window->db_chooser), + window->database); +} + +static void +gdict_window_set_strategy (GdictWindow *window, + const gchar *strategy) +{ + g_free (window->strategy); + + if (strategy && strategy[0] != '\0') + window->strategy = g_strdup (strategy); + else + window->strategy = gdict_mateconf_get_string_with_default (window->mateconf_client, + GDICT_MATECONF_STRATEGY_KEY, + GDICT_DEFAULT_STRATEGY); + + if (window->speller) + gdict_speller_set_strategy (GDICT_SPELLER (window->speller), + window->strategy); + + if (window->strat_chooser) + gdict_strategy_chooser_set_current_strategy (GDICT_STRATEGY_CHOOSER (window->strat_chooser), + window->strategy); +} + +static GdictContext * +get_context_from_loader (GdictWindow *window) +{ + GdictSource *source; + GdictContext *retval; + + if (!window->source_name) + window->source_name = g_strdup (GDICT_DEFAULT_SOURCE_NAME); + + source = gdict_source_loader_get_source (window->loader, + window->source_name); + if (!source && + strcmp (window->source_name, GDICT_DEFAULT_SOURCE_NAME) != 0) + { + g_free (window->source_name); + window->source_name = g_strdup (GDICT_DEFAULT_SOURCE_NAME); + + source = gdict_source_loader_get_source (window->loader, + window->source_name); + } + + if (!source) + { + gchar *detail; + + detail = g_strdup_printf (_("No dictionary source available with name '%s'"), + window->source_name); + + gdict_show_error_dialog (GTK_WINDOW (window), + _("Unable to find dictionary source"), + detail); + + g_free (detail); + + return NULL; + } + + gdict_window_set_database (window, gdict_source_get_database (source)); + gdict_window_set_strategy (window, gdict_source_get_strategy (source)); + + retval = gdict_source_get_context (source); + if (!retval) + { + gchar *detail; + + detail = g_strdup_printf (_("No context available for source '%s'"), + gdict_source_get_description (source)); + + gdict_show_error_dialog (GTK_WINDOW (window), + _("Unable to create a context"), + detail); + + g_free (detail); + g_object_unref (source); + + return NULL; + } + + g_object_unref (source); + + return retval; +} + +static void +gdict_window_set_defbox_font (GdictWindow *window, + const gchar *defbox_font) +{ + g_free (window->defbox_font); + + if (defbox_font) + window->defbox_font = g_strdup (defbox_font); + else + window->defbox_font = gdict_mateconf_get_string_with_default (window->mateconf_client, + DOCUMENT_FONT_KEY, + GDICT_DEFAULT_DEFBOX_FONT); + + if (window->defbox) + gdict_defbox_set_font_name (GDICT_DEFBOX (window->defbox), + window->defbox_font); +} + +static void +gdict_window_set_print_font (GdictWindow *window, + const gchar *print_font) +{ + g_free (window->print_font); + + if (print_font) + window->print_font = g_strdup (print_font); + else + window->print_font = gdict_mateconf_get_string_with_default (window->mateconf_client, + GDICT_MATECONF_PRINT_FONT_KEY, + GDICT_DEFAULT_PRINT_FONT); +} + +static void +gdict_window_set_word (GdictWindow *window, + const gchar *word, + const gchar *database) +{ + gchar *title; + + g_free (window->word); + window->word = NULL; + + if (word && word[0] != '\0') + window->word = g_strdup (word); + else + return; + + if (!database || database[0] == '\0') + database = window->database; + + if (window->word) + title = g_strdup_printf (_("%s - Dictionary"), window->word); + else + title = g_strdup (_("Dictionary")); + + gtk_window_set_title (GTK_WINDOW (window), title); + g_free (title); + + if (window->defbox) + { + gdict_defbox_set_database (GDICT_DEFBOX (window->defbox), database); + gdict_defbox_lookup (GDICT_DEFBOX (window->defbox), word); + } +} + +static void +gdict_window_set_context (GdictWindow *window, + GdictContext *context) +{ + if (window->context) + { + g_signal_handler_disconnect (window->context, window->definition_id); + g_signal_handler_disconnect (window->context, window->lookup_start_id); + g_signal_handler_disconnect (window->context, window->lookup_end_id); + g_signal_handler_disconnect (window->context, window->error_id); + + window->definition_id = 0; + window->lookup_start_id = 0; + window->lookup_end_id = 0; + window->error_id = 0; + + g_object_unref (window->context); + window->context = NULL; + } + + if (window->defbox) + gdict_defbox_set_context (GDICT_DEFBOX (window->defbox), context); + + if (window->db_chooser) + gdict_database_chooser_set_context (GDICT_DATABASE_CHOOSER (window->db_chooser), context); + + if (window->strat_chooser) + gdict_strategy_chooser_set_context (GDICT_STRATEGY_CHOOSER (window->strat_chooser), context); + + if (!context) + return; + + /* attach our callbacks */ + window->definition_id = g_signal_connect (context, "definition-found", + G_CALLBACK (gdict_window_definition_cb), + window); + window->lookup_start_id = g_signal_connect (context, "lookup-start", + G_CALLBACK (gdict_window_lookup_start_cb), + window); + window->lookup_end_id = g_signal_connect (context, "lookup-end", + G_CALLBACK (gdict_window_lookup_end_cb), + window); + window->error_id = g_signal_connect (context, "error", + G_CALLBACK (gdict_window_error_cb), + window); + + window->context = context; +} + +static void +gdict_window_set_source_name (GdictWindow *window, + const gchar *source_name) +{ + GdictContext *context; + + if (window->source_name && source_name && + strcmp (window->source_name, source_name) == 0) + return; + + g_free (window->source_name); + + if (source_name) + window->source_name = g_strdup (source_name); + else + window->source_name = gdict_mateconf_get_string_with_default (window->mateconf_client, + GDICT_MATECONF_SOURCE_KEY, + GDICT_DEFAULT_SOURCE_NAME); + + context = get_context_from_loader (window); + gdict_window_set_context (window, context); + + if (window->source_chooser) + gdict_source_chooser_set_current_source (GDICT_SOURCE_CHOOSER (window->source_chooser), + window->source_name); + + g_object_notify (G_OBJECT (window), "source-name"); +} + +static void +gdict_window_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GdictWindow *window = GDICT_WINDOW (object); + + switch (prop_id) + { + case PROP_ACTION: + window->action = g_value_get_enum (value); + break; + case PROP_SOURCE_LOADER: + if (window->loader) + g_object_unref (window->loader); + window->loader = g_value_get_object (value); + g_object_ref (window->loader); + break; + case PROP_SOURCE_NAME: + gdict_window_set_source_name (window, g_value_get_string (value)); + break; + case PROP_WORD: + gdict_window_set_word (window, g_value_get_string (value), NULL); + break; + case PROP_PRINT_FONT: + gdict_window_set_print_font (window, g_value_get_string (value)); + break; + case PROP_DEFBOX_FONT: + gdict_window_set_defbox_font (window, g_value_get_string (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gdict_window_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GdictWindow *window = GDICT_WINDOW (object); + + switch (prop_id) + { + case PROP_ACTION: + g_value_set_enum (value, window->action); + break; + case PROP_SOURCE_LOADER: + g_value_set_object (value, window->loader); + break; + case PROP_SOURCE_NAME: + g_value_set_string (value, window->source_name); + break; + case PROP_WORD: + g_value_set_string (value, window->word); + break; + case PROP_PRINT_FONT: + g_value_set_string (value, window->print_font); + break; + case PROP_DEFBOX_FONT: + g_value_set_string (value, window->defbox_font); + break; + case PROP_WINDOW_ID: + g_value_set_uint (value, window->window_id); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gdict_window_cmd_file_new (GtkAction *action, + GdictWindow *window) +{ + GtkWidget *new_window; + gchar *word = NULL; + + /* store the default size of the window and its state, so that + * it's picked up by the newly created window + */ + mateconf_client_set_int (window->mateconf_client, + GDICT_MATECONF_WINDOW_WIDTH_KEY, + window->default_width, + NULL); + mateconf_client_set_int (window->mateconf_client, + GDICT_MATECONF_WINDOW_HEIGHT_KEY, + window->default_height, + NULL); + mateconf_client_set_bool (window->mateconf_client, + GDICT_MATECONF_WINDOW_IS_MAXIMIZED_KEY, + window->is_maximized, + NULL); + mateconf_client_set_bool (window->mateconf_client, + GDICT_MATECONF_SIDEBAR_VISIBLE_KEY, + window->sidebar_visible, + NULL); + mateconf_client_set_int (window->mateconf_client, + GDICT_MATECONF_SIDEBAR_WIDTH_KEY, + window->sidebar_width, + NULL); + mateconf_client_set_string (window->mateconf_client, + GDICT_MATECONF_SIDEBAR_PAGE_KEY, + gdict_sidebar_current_page (GDICT_SIDEBAR (window->sidebar)), + NULL); + mateconf_client_set_bool (window->mateconf_client, + GDICT_MATECONF_STATUSBAR_VISIBLE_KEY, + window->statusbar_visible, + NULL); + + word = gdict_defbox_get_selected_word (GDICT_DEFBOX (window->defbox)); + if (word) + { + new_window = gdict_window_new (GDICT_WINDOW_ACTION_LOOKUP, + window->loader, + NULL, word); + g_free (word); + } + else + new_window = gdict_window_new (GDICT_WINDOW_ACTION_CLEAR, + window->loader, + NULL, NULL); + + gtk_widget_show (new_window); + + g_signal_emit (window, gdict_window_signals[CREATED], 0, new_window); +} + +static void +gdict_window_cmd_save_as (GtkAction *action, + GdictWindow *window) +{ + GtkWidget *dialog; + + g_assert (GDICT_IS_WINDOW (window)); + + dialog = gtk_file_chooser_dialog_new (_("Save a Copy"), + GTK_WINDOW (window), + GTK_FILE_CHOOSER_ACTION_SAVE, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, + NULL); + gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog), TRUE); + + /* default to user's home */ + gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), g_get_home_dir ()); + gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog), _("Untitled document")); + + if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) + { + gchar *filename; + gchar *text; + gsize len; + GError *write_error = NULL; + + filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)); + + text = gdict_defbox_get_text (GDICT_DEFBOX (window->defbox), &len); + + g_file_set_contents (filename, + text, + len, + &write_error); + if (write_error) + { + gchar *message; + + message = g_strdup_printf (_("Error while writing to '%s'"), filename); + + gdict_show_gerror_dialog (GTK_WINDOW (window), + message, + write_error); + + g_free (message); + } + + g_free (text); + g_free (filename); + } + + gtk_widget_destroy (dialog); +} + +static void +gdict_window_cmd_file_preview (GtkAction *action, + GdictWindow *window) +{ + g_assert (GDICT_IS_WINDOW (window)); + + gdict_show_print_preview (GTK_WINDOW (window), + GDICT_DEFBOX (window->defbox)); +} + +static void +gdict_window_cmd_file_print (GtkAction *action, + GdictWindow *window) +{ + g_assert (GDICT_IS_WINDOW (window)); + + gdict_show_print_dialog (GTK_WINDOW (window), + GDICT_DEFBOX (window->defbox)); +} + +static void +gdict_window_cmd_file_close_window (GtkAction *action, + GdictWindow *window) +{ + g_assert (GDICT_IS_WINDOW (window)); + + /* store the default size of the window and its state */ + mateconf_client_set_int (window->mateconf_client, + GDICT_MATECONF_WINDOW_WIDTH_KEY, + window->default_width, + NULL); + mateconf_client_set_int (window->mateconf_client, + GDICT_MATECONF_WINDOW_HEIGHT_KEY, + window->default_height, + NULL); + mateconf_client_set_bool (window->mateconf_client, + GDICT_MATECONF_WINDOW_IS_MAXIMIZED_KEY, + window->is_maximized, + NULL); + mateconf_client_set_bool (window->mateconf_client, + GDICT_MATECONF_SIDEBAR_VISIBLE_KEY, + window->sidebar_visible, + NULL); + mateconf_client_set_int (window->mateconf_client, + GDICT_MATECONF_SIDEBAR_WIDTH_KEY, + window->sidebar_width, + NULL); + mateconf_client_set_string (window->mateconf_client, + GDICT_MATECONF_SIDEBAR_PAGE_KEY, + gdict_sidebar_current_page (GDICT_SIDEBAR (window->sidebar)), + NULL); + mateconf_client_set_bool (window->mateconf_client, + GDICT_MATECONF_STATUSBAR_VISIBLE_KEY, + window->statusbar_visible, + NULL); + + /* if this was called from the uimanager, destroy the widget; + * otherwise, if it was called from the delete_event, the widget + * will destroy itself. + */ + if (action) + gtk_widget_destroy (GTK_WIDGET (window)); +} + +static void +gdict_window_cmd_edit_copy (GtkAction *action, + GdictWindow *window) +{ + g_assert (GDICT_IS_WINDOW (window)); + + gdict_defbox_copy_to_clipboard (GDICT_DEFBOX (window->defbox), + gtk_clipboard_get (GDK_SELECTION_CLIPBOARD)); +} + +static void +gdict_window_cmd_edit_select_all (GtkAction *action, + GdictWindow *window) +{ + g_assert (GDICT_IS_WINDOW (window)); + + gdict_defbox_select_all (GDICT_DEFBOX (window->defbox)); +} + +static void +gdict_window_cmd_edit_find (GtkAction *action, + GdictWindow *window) +{ + g_assert (GDICT_IS_WINDOW (window)); + + gdict_defbox_set_show_find (GDICT_DEFBOX (window->defbox), TRUE); +} + +static void +gdict_window_cmd_edit_find_next (GtkAction *action, + GdictWindow *window) +{ + g_assert (GDICT_IS_WINDOW (window)); + + gdict_defbox_find_next (GDICT_DEFBOX (window->defbox)); +} + +static void +gdict_window_cmd_edit_find_previous (GtkAction *action, + GdictWindow *window) +{ + g_assert (GDICT_IS_WINDOW (window)); + + gdict_defbox_find_previous (GDICT_DEFBOX (window->defbox)); +} + +static void +gdict_window_cmd_edit_preferences (GtkAction *action, + GdictWindow *window) +{ + g_assert (GDICT_IS_WINDOW (window)); + + gdict_show_pref_dialog (GTK_WIDGET (window), + _("Dictionary Preferences"), + window->loader); +} + +static void +gdict_window_cmd_view_sidebar (GtkAction *action, + GdictWindow *window) +{ + g_assert (GDICT_IS_WINDOW (window)); + + if (window->sidebar_visible) + gdict_window_set_sidebar_visible (window, FALSE); + else + gdict_window_set_sidebar_visible (window, TRUE); +} + +static void +gdict_window_cmd_view_statusbar (GtkAction *action, + GdictWindow *window) +{ + g_assert (GDICT_IS_WINDOW (window)); + + if (window->statusbar_visible) + gdict_window_set_statusbar_visible (window, FALSE); + else + gdict_window_set_statusbar_visible (window, TRUE); +} + +static void +gdict_window_cmd_view_speller (GtkAction *action, + GdictWindow *window) +{ + g_assert (GDICT_IS_WINDOW (window)); + + gdict_sidebar_view_page (GDICT_SIDEBAR (window->sidebar), + GDICT_SIDEBAR_SPELLER_PAGE); + gdict_window_set_sidebar_visible (window, TRUE); +} + +static void +gdict_window_cmd_view_databases (GtkAction *action, + GdictWindow *window) +{ + g_assert (GDICT_IS_WINDOW (window)); + + gdict_sidebar_view_page (GDICT_SIDEBAR (window->sidebar), + GDICT_SIDEBAR_DATABASES_PAGE); + gdict_window_set_sidebar_visible (window, TRUE); +} + +static void +gdict_window_cmd_view_strategies (GtkAction *action, + GdictWindow *window) +{ + g_assert (GDICT_IS_WINDOW (window)); + + gdict_sidebar_view_page (GDICT_SIDEBAR (window->sidebar), + GDICT_SIDEBAR_STRATEGIES_PAGE); + gdict_window_set_sidebar_visible (window, TRUE); +} + +static void +gdict_window_cmd_view_sources (GtkAction *action, + GdictWindow *window) +{ + g_assert (GDICT_IS_WINDOW (window)); + + gdict_sidebar_view_page (GDICT_SIDEBAR (window->sidebar), + GDICT_SIDEBAR_SOURCES_PAGE); + gdict_window_set_sidebar_visible (window, TRUE); +} + +static void +gdict_window_cmd_go_first_def (GtkAction *action, + GdictWindow *window) +{ + g_assert (GDICT_IS_WINDOW (window)); + + window->last_definition = 0; + gdict_defbox_jump_to_definition (GDICT_DEFBOX (window->defbox), + window->last_definition); +} + +static void +gdict_window_cmd_go_previous_def (GtkAction *action, + GdictWindow *window) +{ + g_assert (GDICT_IS_WINDOW (window)); + + if (window->last_definition == 0) + return; + + window->last_definition -= 1; + gdict_defbox_jump_to_definition (GDICT_DEFBOX (window->defbox), + window->last_definition); +} + +static void +gdict_window_cmd_go_next_def (GtkAction *action, + GdictWindow *window) +{ + g_assert (GDICT_IS_WINDOW (window)); + + if (window->max_definition == -1) + window->max_definition = gdict_defbox_count_definitions (GDICT_DEFBOX (window->defbox)) - 1; + + if (window->last_definition == window->max_definition) + return; + + window->last_definition += 1; + gdict_defbox_jump_to_definition (GDICT_DEFBOX (window->defbox), + window->last_definition); +} + +static void +gdict_window_cmd_go_last_def (GtkAction *action, + GdictWindow *window) +{ + g_assert (GDICT_IS_WINDOW (window)); + + if (window->max_definition == -1) + window->last_definition = gdict_defbox_count_definitions (GDICT_DEFBOX (window->defbox)) - 1; + + window->last_definition = window->max_definition; + gdict_defbox_jump_to_definition (GDICT_DEFBOX (window->defbox), + window->last_definition); +} + +static void +gdict_window_cmd_help_contents (GtkAction *action, + GdictWindow *window) +{ + GError *err = NULL; + + g_return_if_fail (GDICT_IS_WINDOW (window)); + + gtk_show_uri (gtk_widget_get_screen (GTK_WIDGET (window)), + "ghelp:mate-dictionary", + gtk_get_current_event_time (), &err); + if (err) + { + gdict_show_gerror_dialog (GTK_WINDOW (window), + _("There was an error while displaying help"), + err); + g_error_free (err); + } +} + +static void +gdict_window_cmd_help_about (GtkAction *action, + GdictWindow *window) +{ + g_assert (GDICT_IS_WINDOW (window)); + + gdict_show_about_dialog (GTK_WIDGET (window)); +} + +static void +gdict_window_cmd_lookup (GtkAction *action, + GdictWindow *window) +{ + g_assert (GDICT_IS_WINDOW (window)); + + gtk_widget_grab_focus (window->entry); +} + +static void +gdict_window_cmd_escape (GtkAction *action, + GdictWindow *window) +{ + g_assert (GDICT_IS_WINDOW (window)); + + gdict_defbox_set_show_find (GDICT_DEFBOX (window->defbox), FALSE); +} + +static const GtkActionEntry entries[] = +{ + { "File", NULL, N_("_File") }, + { "Edit", NULL, N_("_Edit") }, + { "View", NULL, N_("_View") }, + { "Go", NULL, N_("_Go") }, + { "Help", NULL, N_("_Help") }, + + /* File menu */ + { "FileNew", GTK_STOCK_NEW, N_("_New"), "<control>N", + N_("New look up"), G_CALLBACK (gdict_window_cmd_file_new) }, + { "FileSaveAs", GTK_STOCK_SAVE_AS, N_("_Save a Copy..."), NULL, NULL, + G_CALLBACK (gdict_window_cmd_save_as) }, + { "FilePreview", NULL, N_("P_review..."), "<control><shift>P", + N_("Preview this document"), G_CALLBACK (gdict_window_cmd_file_preview) }, + { "FilePrint", GTK_STOCK_PRINT, N_("_Print..."), "<control>P", + N_("Print this document"), G_CALLBACK (gdict_window_cmd_file_print) }, + { "FileCloseWindow", GTK_STOCK_CLOSE, NULL, "<control>W", NULL, + G_CALLBACK (gdict_window_cmd_file_close_window) }, + + /* Edit menu */ + { "EditCopy", GTK_STOCK_COPY, NULL, "<control>C", NULL, + G_CALLBACK (gdict_window_cmd_edit_copy) }, + { "EditSelectAll", NULL, N_("Select _All"), "<control>A", NULL, + G_CALLBACK (gdict_window_cmd_edit_select_all) }, + { "EditFind", GTK_STOCK_FIND, NULL, "<control>F", + N_("Find a word or phrase in the document"), + G_CALLBACK (gdict_window_cmd_edit_find) }, + { "EditFindNext", NULL, N_("Find Ne_xt"), "<control>G", NULL, + G_CALLBACK (gdict_window_cmd_edit_find_next) }, + { "EditFindPrevious", NULL, N_("Find Pre_vious"), "<control><shift>G", NULL, + G_CALLBACK (gdict_window_cmd_edit_find_previous) }, + { "EditPreferences", GTK_STOCK_PREFERENCES, N_("_Preferences"), NULL, NULL, + G_CALLBACK (gdict_window_cmd_edit_preferences) }, + + /* Go menu */ + { "GoPreviousDef", GTK_STOCK_GO_BACK, N_("_Previous Definition"), "<control>Page_Up", + N_("Go to the previous definition"), G_CALLBACK (gdict_window_cmd_go_previous_def) }, + { "GoNextDef", GTK_STOCK_GO_FORWARD, N_("_Next Definition"), "<control>Page_Down", + N_("Go to the next definition"), G_CALLBACK (gdict_window_cmd_go_next_def) }, + { "GoFirstDef", GTK_STOCK_GOTO_FIRST, N_("_First Definition"), "<control>Home", + N_("Go to the first definition"), G_CALLBACK (gdict_window_cmd_go_first_def) }, + { "GoLastDef", GTK_STOCK_GOTO_LAST, N_("_Last Definition"), "<control>End", + N_("Go to the last definition"), G_CALLBACK (gdict_window_cmd_go_last_def) }, + + /* View menu */ + { "ViewSpeller", NULL, N_("Similar _Words"), "<control>T", NULL, + G_CALLBACK (gdict_window_cmd_view_speller), }, + { "ViewSource", NULL, N_("Dictionary Sources"), "<control>D", NULL, + G_CALLBACK (gdict_window_cmd_view_sources), }, + { "ViewDB", NULL, N_("Available _Databases"), "<control>B", NULL, + G_CALLBACK (gdict_window_cmd_view_databases), }, + { "ViewStrat", NULL, N_("Available St_rategies"), "<control>R", NULL, + G_CALLBACK (gdict_window_cmd_view_strategies), }, + + /* Help menu */ + { "HelpContents", GTK_STOCK_HELP, N_("_Contents"), "F1", NULL, + G_CALLBACK (gdict_window_cmd_help_contents) }, + { "HelpAbout", GTK_STOCK_ABOUT, N_("_About"), NULL, NULL, + G_CALLBACK (gdict_window_cmd_help_about) }, + + /* Accelerators */ + { "Lookup", NULL, "", "<control>L", NULL, G_CALLBACK (gdict_window_cmd_lookup) }, + { "Escape", NULL, "", "Escape", "", G_CALLBACK (gdict_window_cmd_escape) }, + { "Slash", GTK_STOCK_FIND, NULL, "slash", NULL, G_CALLBACK (gdict_window_cmd_edit_find) }, +}; + +static const GtkToggleActionEntry toggle_entries[] = { + /* View menu */ + { "ViewSidebar", NULL, N_("_Sidebar"), "F9", NULL, + G_CALLBACK (gdict_window_cmd_view_sidebar), FALSE }, + { "ViewStatusbar", NULL, N_("S_tatusbar"), NULL, NULL, + G_CALLBACK (gdict_window_cmd_view_statusbar), FALSE }, +}; + +static gboolean +gdict_window_delete_event_cb (GtkWidget *widget, + GdkEvent *event, + gpointer user_data) +{ + gdict_window_cmd_file_close_window (NULL, GDICT_WINDOW (widget)); + + return FALSE; +} + +static gboolean +gdict_window_state_event_cb (GtkWidget *widget, + GdkEventWindowState *event, + gpointer user_data) +{ + GdictWindow *window = GDICT_WINDOW (widget); + + if (event->new_window_state & GDK_WINDOW_STATE_MAXIMIZED) + window->is_maximized = TRUE; + else + window->is_maximized = FALSE; + + return FALSE; +} + +static void +gdict_window_mateconf_notify_cb (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + gpointer user_data) +{ + GdictWindow *window; + + window = GDICT_WINDOW (user_data); + + if (strcmp (entry->key, GDICT_MATECONF_PRINT_FONT_KEY) == 0) + { + if (entry->value && (entry->value->type == MATECONF_VALUE_STRING)) + gdict_window_set_print_font (window, mateconf_value_get_string (entry->value)); + else + gdict_window_set_print_font (window, GDICT_DEFAULT_PRINT_FONT); + } + else if (strcmp (entry->key, GDICT_MATECONF_SOURCE_KEY) == 0) + { + if (entry->value && (entry->value->type == MATECONF_VALUE_STRING)) + gdict_window_set_source_name (window, mateconf_value_get_string (entry->value)); + else + gdict_window_set_source_name (window, GDICT_DEFAULT_SOURCE_NAME); + } + else if (strcmp (entry->key, GDICT_MATECONF_DATABASE_KEY) == 0) + { + if (entry->value && (entry->value->type == MATECONF_VALUE_STRING)) + gdict_window_set_database (window, mateconf_value_get_string (entry->value)); + else + gdict_window_set_database (window, GDICT_DEFAULT_DATABASE); + } + else if (strcmp (entry->key, DOCUMENT_FONT_KEY) == 0) + { + if (entry->value && (entry->value->type == MATECONF_VALUE_STRING)) + gdict_window_set_defbox_font (window, mateconf_value_get_string (entry->value)); + else + gdict_window_set_defbox_font (window, GDICT_DEFAULT_DEFBOX_FONT); + } + else if (strcmp (entry->key, GDICT_MATECONF_SIDEBAR_VISIBLE_KEY) == 0) + { + if (entry->value && (entry->value->type == MATECONF_VALUE_BOOL)) + gdict_window_set_sidebar_visible (window, + mateconf_value_get_bool (entry->value)); + else + gdict_window_set_sidebar_visible (window, FALSE); + } + else if (strcmp (entry->key, GDICT_MATECONF_STATUSBAR_VISIBLE_KEY) == 0) + { + if (entry->value && (entry->value->type == MATECONF_VALUE_BOOL)) + gdict_window_set_statusbar_visible (window, + mateconf_value_get_bool (entry->value)); + else + gdict_window_set_statusbar_visible (window, FALSE); + } +} + +static void +lookup_word (GdictWindow *window, + gpointer dummy) +{ + const gchar *entry_text; + gchar *word; + + g_assert (GDICT_IS_WINDOW (window)); + + if (!window->context) + return; + + entry_text = gtk_entry_get_text (GTK_ENTRY (window->entry)); + if (!entry_text || *entry_text == '\0') + return; + + word = g_strdup (entry_text); + gdict_window_set_word (window, g_strstrip (word), NULL); + + g_free (word); +} + +static void +source_activated_cb (GdictSourceChooser *chooser, + const gchar *source_name, + GdictSource *source, + GdictWindow *window) +{ + g_signal_handlers_block_by_func (chooser, source_activated_cb, window); + gdict_window_set_source_name (window, source_name); + g_signal_handlers_unblock_by_func (chooser, source_activated_cb, window); + + if (window->status && window->statusbar_visible) + { + gchar *message; + + message = g_strdup_printf (_("Dictionary source `%s' selected"), + gdict_source_get_description (source)); + gtk_statusbar_push (GTK_STATUSBAR (window->status), 0, message); + g_free (message); + } +} + +static void +strategy_activated_cb (GdictStrategyChooser *chooser, + const gchar *strat_name, + const gchar *strat_desc, + GdictWindow *window) +{ + g_signal_handlers_block_by_func (chooser, strategy_activated_cb, window); + gdict_window_set_strategy (window, strat_name); + g_signal_handlers_unblock_by_func (chooser, strategy_activated_cb, window); + + if (window->status && window->statusbar_visible) + { + gchar *message; + + message = g_strdup_printf (_("Strategy `%s' selected"), strat_desc); + gtk_statusbar_push (GTK_STATUSBAR (window->status), 0, message); + g_free (message); + } +} + +static void +database_activated_cb (GdictDatabaseChooser *chooser, + const gchar *db_name, + const gchar *db_desc, + GdictWindow *window) +{ + g_signal_handlers_block_by_func (chooser, database_activated_cb, window); + gdict_window_set_database (window, db_name); + g_signal_handlers_unblock_by_func (chooser, database_activated_cb, window); + + if (window->status && window->statusbar_visible) + { + gchar *message; + + message = g_strdup_printf (_("Database `%s' selected"), db_desc); + gtk_statusbar_push (GTK_STATUSBAR (window->status), 0, message); + g_free (message); + } +} + +static void +speller_word_activated_cb (GdictSpeller *speller, + const gchar *word, + const gchar *db_name, + GdictWindow *window) +{ + gtk_entry_set_text (GTK_ENTRY (window->entry), word); + + gdict_window_set_word (window, word, db_name); + + if (window->status && window->statusbar_visible) + { + gchar *message; + + message = g_strdup_printf (_("Word `%s' selected"), word); + gtk_statusbar_push (GTK_STATUSBAR (window->status), 0, message); + g_free (message); + } +} + +static void +sidebar_page_changed_cb (GdictSidebar *sidebar, + GdictWindow *window) +{ + const gchar *page_id; + const gchar *message; + + page_id = gdict_sidebar_current_page (sidebar); + + switch (page_id[0]) + { + case 's': + { + switch (page_id[1]) + { + case 'p': /* speller */ + message = _("Double-click on the word to look up"); + if (window->word) + gdict_speller_match (GDICT_SPELLER (window->speller), + window->word); + break; + case 't': /* strat-chooser */ + message = _("Double-click on the matching strategy to use"); + + gdict_strategy_chooser_refresh (GDICT_STRATEGY_CHOOSER (window->strat_chooser)); + break; + case 'o': /* source-chooser */ + message = _("Double-click on the source to use"); + gdict_source_chooser_refresh (GDICT_SOURCE_CHOOSER (window->source_chooser)); + break; + default: + message = NULL; + } + } + break; + case 'd': /* db-chooser */ + message = _("Double-click on the database to use"); + + gdict_database_chooser_refresh (GDICT_DATABASE_CHOOSER (window->db_chooser)); + break; + default: + message = NULL; + break; + } + + if (message && window->status && window->statusbar_visible) + gtk_statusbar_push (GTK_STATUSBAR (window->status), 0, message); +} + +static void +sidebar_closed_cb (GdictSidebar *sidebar, + GdictWindow *window) +{ + gdict_window_set_sidebar_visible (window, FALSE); +} + +static void +gdict_window_link_clicked (GdictDefbox *defbox, + const gchar *link_text, + GdictWindow *window) +{ + GtkWidget *new_window; + + /* store the default size of the window and its state, so that + * it's picked up by the newly created window + */ + mateconf_client_set_int (window->mateconf_client, + GDICT_MATECONF_WINDOW_WIDTH_KEY, + window->default_width, + NULL); + mateconf_client_set_int (window->mateconf_client, + GDICT_MATECONF_WINDOW_HEIGHT_KEY, + window->default_height, + NULL); + mateconf_client_set_bool (window->mateconf_client, + GDICT_MATECONF_WINDOW_IS_MAXIMIZED_KEY, + window->is_maximized, + NULL); + mateconf_client_set_bool (window->mateconf_client, + GDICT_MATECONF_SIDEBAR_VISIBLE_KEY, + window->sidebar_visible, + NULL); + mateconf_client_set_int (window->mateconf_client, + GDICT_MATECONF_SIDEBAR_WIDTH_KEY, + window->sidebar_width, + NULL); + mateconf_client_set_string (window->mateconf_client, + GDICT_MATECONF_SIDEBAR_PAGE_KEY, + gdict_sidebar_current_page (GDICT_SIDEBAR (window->sidebar)), + NULL); + mateconf_client_set_bool (window->mateconf_client, + GDICT_MATECONF_STATUSBAR_VISIBLE_KEY, + window->statusbar_visible, + NULL); + + new_window = gdict_window_new (GDICT_WINDOW_ACTION_LOOKUP, + window->loader, + NULL, link_text); + gtk_widget_show (new_window); + + g_signal_emit (window, gdict_window_signals[CREATED], 0, new_window); +} + +static void +gdict_window_drag_data_received_cb (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + GtkSelectionData *data, + guint info, + guint time_, + gpointer user_data) +{ + GdictWindow *window = GDICT_WINDOW (user_data); + gchar *text; + + text = (gchar *) gtk_selection_data_get_text (data); + if (text) + { + gtk_entry_set_text (GTK_ENTRY (window->entry), text); + + gdict_window_set_word (window, text, NULL); + g_free (text); + + gtk_drag_finish (context, TRUE, FALSE, time_); + } + else + gtk_drag_finish (context, FALSE, FALSE, time_); +} + +static void +gdict_window_size_allocate (GtkWidget *widget, + GtkAllocation *allocation) +{ + GdictWindow *window = GDICT_WINDOW (widget); + + if (!window->is_maximized) + { + window->default_width = allocation->width; + window->default_height = allocation->height; + } + + if (GTK_WIDGET_CLASS (gdict_window_parent_class)->size_allocate) + GTK_WIDGET_CLASS (gdict_window_parent_class)->size_allocate (widget, + allocation); +} + +static void +set_window_default_size (GdictWindow *window) +{ + GtkWidget *widget; + gboolean is_maximized; + gint width, height; + gint font_size; + GdkScreen *screen; + gint monitor_num; + GtkRequisition req; + GdkRectangle monitor; + + g_assert (GDICT_IS_WINDOW (window)); + + widget = GTK_WIDGET (window); + + /* make sure that the widget is realized */ + gtk_widget_realize (widget); + + /* recover the state from MateConf */ + width = mateconf_client_get_int (window->mateconf_client, + GDICT_MATECONF_WINDOW_WIDTH_KEY, + NULL); + height = mateconf_client_get_int (window->mateconf_client, + GDICT_MATECONF_WINDOW_HEIGHT_KEY, + NULL); + is_maximized = mateconf_client_get_bool (window->mateconf_client, + GDICT_MATECONF_WINDOW_IS_MAXIMIZED_KEY, + NULL); + + /* XXX - the user wants mate-dictionary to resize itself, so + * we compute the minimum safe geometry needed for displaying + * the text returned by a dictionary server, which is based + * on the font size and the ANSI terminal. this is dumb, + * I know, but dictionary servers return pre-formatted text + * and we can't reformat it ourselves. + */ + if (width == -1 || height == -1) + { + /* Size based on the font size */ + GtkWidget *defbox = window->defbox; + + font_size = pango_font_description_get_size (gtk_widget_get_style (defbox)->font_desc); + font_size = PANGO_PIXELS (font_size); + + width = font_size * GDICT_WINDOW_COLUMNS; + height = font_size * GDICT_WINDOW_ROWS; + + /* Use at least the requisition size of the window... */ + gtk_widget_size_request (widget, &req); + width = MAX (width, req.width); + height = MAX (height, req.height); + + /* ... but make it no larger than the monitor */ + screen = gtk_widget_get_screen (widget); + monitor_num = gdk_screen_get_monitor_at_window (screen, gtk_widget_get_window (widget)); + + gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor); + width = MIN (width, monitor.width * 3 / 4); + height = MIN (height, monitor.height * 3 / 4); + } + + /* Set default size */ + gtk_window_set_default_size (GTK_WINDOW (widget), + width, + height); + + if (is_maximized) + gtk_window_maximize (GTK_WINDOW (widget)); +} + +static void +gdict_window_style_set (GtkWidget *widget, + GtkStyle *old_style) +{ + + if (GTK_WIDGET_CLASS (gdict_window_parent_class)->style_set) + GTK_WIDGET_CLASS (gdict_window_parent_class)->style_set (widget, old_style); + + set_window_default_size (GDICT_WINDOW (widget)); +} + +static void +gdict_window_handle_notify_position_cb (GtkWidget *widget, + GParamSpec *pspec, + gpointer user_data) +{ + GdictWindow *window = GDICT_WINDOW (user_data); + gint window_width, pos; + GtkAllocation allocation; + + pos = gtk_paned_get_position (GTK_PANED (widget)); + gtk_widget_get_allocation (GTK_WIDGET (window), &allocation); + window_width = allocation.width; + + window->sidebar_width = window_width - pos; +} + +static GObject * +gdict_window_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_params) +{ + GObject *object; + GdictWindow *window; + gint width, height, sidebar_width; + gboolean is_maximized; + GtkWidget *hbox; + GtkWidget *handle; + GtkWidget *frame1, *frame2; + GtkWidget *vbox; + GtkWidget *button; + GtkActionGroup *action_group; + GtkAccelGroup *accel_group; + PangoFontDescription *font_desc; + gchar *font_name, *sidebar_page; + GError *error; + gboolean sidebar_visible; + gboolean statusbar_visible; + GtkAllocation allocation; + + object = G_OBJECT_CLASS (gdict_window_parent_class)->constructor (type, + n_construct_properties, + construct_params); + window = GDICT_WINDOW (object); + + gtk_widget_push_composite_child (); + + window->main_box = gtk_vbox_new (FALSE, 0); + gtk_container_add (GTK_CONTAINER (window), window->main_box); + gtk_widget_show (window->main_box); + + /* build menus */ + action_group = gtk_action_group_new ("MenuActions"); + window->action_group = action_group; + gtk_action_group_set_translation_domain (action_group, NULL); + gtk_action_group_add_actions (action_group, entries, + G_N_ELEMENTS (entries), + window); + gtk_action_group_add_toggle_actions (action_group, toggle_entries, + G_N_ELEMENTS (toggle_entries), + window); + + window->ui_manager = gtk_ui_manager_new (); + gtk_ui_manager_insert_action_group (window->ui_manager, action_group, 0); + + accel_group = gtk_ui_manager_get_accel_group (window->ui_manager); + gtk_window_add_accel_group (GTK_WINDOW (window), accel_group); + + error = NULL; + if (!gtk_ui_manager_add_ui_from_file (window->ui_manager, + PKGDATADIR "/mate-dictionary-ui.xml", + &error)) + { + g_warning ("Building menus failed: %s", error->message); + g_error_free (error); + } + else + { + window->menubar = gtk_ui_manager_get_widget (window->ui_manager, "/MainMenu"); + + gtk_box_pack_start (GTK_BOX (window->main_box), window->menubar, FALSE, FALSE, 0); + gtk_widget_show (window->menubar); + + gdict_window_ensure_menu_state (window); + } + + vbox = gtk_vbox_new (FALSE, 6); + gtk_container_set_border_width (GTK_CONTAINER (vbox), 6); + gtk_container_add (GTK_CONTAINER (window->main_box), vbox); + gtk_widget_show (vbox); + + hbox = gtk_hbox_new (FALSE, 12); + gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); + gtk_widget_show (hbox); + + button = gtk_button_new_with_mnemonic (_("Look _up:")); + g_signal_connect_swapped (button, "clicked", + G_CALLBACK (lookup_word), + window); + gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE); + gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0); + gtk_widget_show (button); + + window->completion_model = gtk_list_store_new (COMPLETION_N_COLUMNS, + G_TYPE_STRING); + + window->completion = gtk_entry_completion_new (); + gtk_entry_completion_set_popup_completion (window->completion, TRUE); + gtk_entry_completion_set_model (window->completion, + GTK_TREE_MODEL (window->completion_model)); + gtk_entry_completion_set_text_column (window->completion, + COMPLETION_TEXT_COLUMN); + + window->entry = gtk_entry_new (); + if (window->word) + gtk_entry_set_text (GTK_ENTRY (window->entry), window->word); + + gtk_entry_set_completion (GTK_ENTRY (window->entry), + window->completion); + g_signal_connect_swapped (window->entry, "activate", + G_CALLBACK (lookup_word), + window); + gtk_box_pack_start (GTK_BOX (hbox), window->entry, TRUE, TRUE, 0); + gtk_widget_show (window->entry); + + handle = gtk_hpaned_new (); + gtk_box_pack_start (GTK_BOX (vbox), handle, TRUE, TRUE, 0); + gtk_widget_show (handle); + + frame1 = gtk_vbox_new (FALSE, 0); + frame2 = gtk_vbox_new (FALSE, 0); + + window->defbox = gdict_defbox_new (); + if (window->context) + gdict_defbox_set_context (GDICT_DEFBOX (window->defbox), window->context); + + g_signal_connect (window->defbox, "link-clicked", + G_CALLBACK (gdict_window_link_clicked), + window); + + gtk_drag_dest_set (window->defbox, + GTK_DEST_DEFAULT_ALL, + drop_types, n_drop_types, + GDK_ACTION_COPY); + g_signal_connect (window->defbox, "drag-data-received", + G_CALLBACK (gdict_window_drag_data_received_cb), + window); + gtk_container_add (GTK_CONTAINER (frame1), window->defbox); + gtk_widget_show (window->defbox); + + /* Sidebar */ + window->sidebar = gdict_sidebar_new (); + g_signal_connect (window->sidebar, "page-changed", + G_CALLBACK (sidebar_page_changed_cb), + window); + g_signal_connect (window->sidebar, "closed", + G_CALLBACK (sidebar_closed_cb), + window); + + /* Speller */ + window->speller = gdict_speller_new (); + if (window->context) + gdict_speller_set_context (GDICT_SPELLER (window->speller), + window->context); + g_signal_connect (window->speller, "word-activated", + G_CALLBACK (speller_word_activated_cb), + window); + gdict_sidebar_add_page (GDICT_SIDEBAR (window->sidebar), + GDICT_SIDEBAR_SPELLER_PAGE, + _("Similar words"), + window->speller); + gtk_widget_show (window->speller); + + /* Database chooser */ + if (window->context) + gdict_database_chooser_set_context (GDICT_DATABASE_CHOOSER (window->db_chooser), + window->context); + g_signal_connect (window->db_chooser, "database-activated", + G_CALLBACK (database_activated_cb), + window); + gdict_sidebar_add_page (GDICT_SIDEBAR (window->sidebar), + GDICT_SIDEBAR_DATABASES_PAGE, + _("Available dictionaries"), + window->db_chooser); + gtk_widget_show (window->db_chooser); + + /* Strategy chooser */ + if (window->context) + gdict_strategy_chooser_set_context (GDICT_STRATEGY_CHOOSER (window->strat_chooser), + window->context); + g_signal_connect (window->strat_chooser, "strategy-activated", + G_CALLBACK (strategy_activated_cb), + window); + gdict_sidebar_add_page (GDICT_SIDEBAR (window->sidebar), + GDICT_SIDEBAR_STRATEGIES_PAGE, + _("Available strategies"), + window->strat_chooser); + gtk_widget_show (window->strat_chooser); + + /* Source chooser */ + window->source_chooser = gdict_source_chooser_new_with_loader (window->loader); + g_signal_connect (window->source_chooser, "source-activated", + G_CALLBACK (source_activated_cb), + window); + gdict_sidebar_add_page (GDICT_SIDEBAR (window->sidebar), + GDICT_SIDEBAR_SOURCES_PAGE, + _("Dictionary sources"), + window->source_chooser); + gtk_widget_show (window->source_chooser); + + gtk_container_add (GTK_CONTAINER (frame2), window->sidebar); + gtk_widget_show (window->sidebar); + + gtk_paned_pack1 (GTK_PANED (handle), frame1, TRUE, FALSE); + gtk_paned_pack2 (GTK_PANED (handle), frame2, FALSE, TRUE); + + window->defbox_frame = frame1; + window->sidebar_frame = frame2; + + gtk_widget_show (window->defbox_frame); + + window->status = gtk_statusbar_new (); + gtk_statusbar_set_has_resize_grip (GTK_STATUSBAR (window->status), TRUE); + gtk_box_pack_end (GTK_BOX (window->main_box), window->status, FALSE, FALSE, 0); + statusbar_visible = mateconf_client_get_bool (window->mateconf_client, + GDICT_MATECONF_STATUSBAR_VISIBLE_KEY, + NULL); + gdict_window_set_statusbar_visible (window, statusbar_visible); + + window->progress = gtk_progress_bar_new (); + gtk_box_pack_end (GTK_BOX (window->status), window->progress, FALSE, FALSE, 0); + + /* retrieve the font size from mateconf */ + font_name = gdict_mateconf_get_string_with_default (window->mateconf_client, + DOCUMENT_FONT_KEY, + GDICT_DEFAULT_DEFBOX_FONT); + + gdict_window_set_defbox_font (window, font_name); + font_desc = pango_font_description_from_string (font_name); + g_free (font_name); + + sidebar_visible = mateconf_client_get_bool (window->mateconf_client, + GDICT_MATECONF_SIDEBAR_VISIBLE_KEY, + NULL); + gdict_window_set_sidebar_visible (window, sidebar_visible); + + /* retrieve the window state from mateconf */ + is_maximized = mateconf_client_get_bool (window->mateconf_client, + GDICT_MATECONF_WINDOW_IS_MAXIMIZED_KEY, + NULL); + + width = mateconf_client_get_int (window->mateconf_client, + GDICT_MATECONF_WINDOW_WIDTH_KEY, + NULL); + height = mateconf_client_get_int (window->mateconf_client, + GDICT_MATECONF_WINDOW_HEIGHT_KEY, + NULL); + sidebar_width = mateconf_client_get_int (window->mateconf_client, + GDICT_MATECONF_SIDEBAR_WIDTH_KEY, + NULL); + sidebar_page = mateconf_client_get_string (window->mateconf_client, + GDICT_MATECONF_SIDEBAR_PAGE_KEY, + NULL); + + /* if the (width, height) tuple is not defined, use the font to + * calculate the right window geometry + */ + if (width == -1 || height == -1) + { + gint font_size; + + font_size = pango_font_description_get_size (font_desc); + font_size = PANGO_PIXELS (font_size); + + width = MAX (GDICT_WINDOW_COLUMNS * font_size, GDICT_WINDOW_MIN_WIDTH); + height = MAX (GDICT_WINDOW_ROWS * font_size, GDICT_WINDOW_MIN_HEIGHT); + } + else + { + window->default_width = width; + window->default_height = height; + } + + pango_font_description_free (font_desc); + + window->is_maximized = is_maximized; + + gtk_window_set_title (GTK_WINDOW (window), _("Dictionary")); + gtk_window_set_default_size (GTK_WINDOW (window), + width, + height); + if (is_maximized) + gtk_window_maximize (GTK_WINDOW (window)); + + gtk_widget_get_allocation (GTK_WIDGET (window), &allocation); + gtk_paned_set_position (GTK_PANED (handle), allocation.width - sidebar_width); + if (sidebar_page) + { + gdict_sidebar_view_page (GDICT_SIDEBAR (window->sidebar), sidebar_page); + g_free (sidebar_page); + } + + g_signal_connect (window, "delete-event", + G_CALLBACK (gdict_window_delete_event_cb), + NULL); + g_signal_connect (window, "window-state-event", + G_CALLBACK (gdict_window_state_event_cb), + NULL); + g_signal_connect (handle, "notify::position", + G_CALLBACK (gdict_window_handle_notify_position_cb), + window); + + gtk_widget_grab_focus (window->entry); + + gtk_widget_pop_composite_child (); + + return object; +} + +static void +gdict_window_class_init (GdictWindowClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + gobject_class->finalize = gdict_window_finalize; + gobject_class->dispose = gdict_window_dispose; + gobject_class->set_property = gdict_window_set_property; + gobject_class->get_property = gdict_window_get_property; + gobject_class->constructor = gdict_window_constructor; + + widget_class->style_set = gdict_window_style_set; + widget_class->size_allocate = gdict_window_size_allocate; + + g_object_class_install_property (gobject_class, + PROP_ACTION, + g_param_spec_enum ("action", + "Action", + "The default action performed by the window", + GDICT_TYPE_WINDOW_ACTION, + GDICT_WINDOW_ACTION_CLEAR, + (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY))); + g_object_class_install_property (gobject_class, + PROP_SOURCE_LOADER, + g_param_spec_object ("source-loader", + "Source Loader", + "The GdictSourceLoader to be used to load dictionary sources", + GDICT_TYPE_SOURCE_LOADER, + (G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY))); + g_object_class_install_property (gobject_class, + PROP_SOURCE_NAME, + g_param_spec_string ("source-name", + "Source Name", + "The name of the GdictSource to be used", + GDICT_DEFAULT_SOURCE_NAME, + (G_PARAM_READABLE | G_PARAM_WRITABLE))); + g_object_class_install_property (gobject_class, + PROP_PRINT_FONT, + g_param_spec_string ("print-font", + "Print Font", + "The font name to be used when printing", + GDICT_DEFAULT_PRINT_FONT, + (G_PARAM_READABLE | G_PARAM_WRITABLE))); + g_object_class_install_property (gobject_class, + PROP_DEFBOX_FONT, + g_param_spec_string ("defbox-font", + "Defbox Font", + "The font name to be used by the defbox widget", + GDICT_DEFAULT_DEFBOX_FONT, + (G_PARAM_READABLE | G_PARAM_WRITABLE))); + g_object_class_install_property (gobject_class, + PROP_WORD, + g_param_spec_string ("word", + "Word", + "The word to search", + NULL, + (G_PARAM_READABLE | G_PARAM_WRITABLE))); + g_object_class_install_property (gobject_class, + PROP_WINDOW_ID, + g_param_spec_uint ("window-id", + "Window ID", + "The unique identifier for this window", + 0, + G_MAXUINT, + 0, + G_PARAM_READABLE)); + + gdict_window_signals[CREATED] = + g_signal_new ("created", + G_OBJECT_CLASS_TYPE (gobject_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GdictWindowClass, created), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + GDICT_TYPE_WINDOW); +} + +static void +gdict_window_init (GdictWindow *window) +{ + GError *mateconf_error; + + window->action = GDICT_WINDOW_ACTION_CLEAR; + + window->loader = NULL; + window->context = NULL; + + window->mateconf_client = mateconf_client_get_default (); + + mateconf_error = NULL; + mateconf_client_add_dir (window->mateconf_client, + GDICT_MATECONF_DIR, + MATECONF_CLIENT_PRELOAD_NONE, + &mateconf_error); + if (mateconf_error) + { + gdict_show_gerror_dialog (NULL, + _("Unable to connect to MateConf"), + mateconf_error); + } + + window->notify_id = mateconf_client_notify_add (window->mateconf_client, + GDICT_MATECONF_DIR, + gdict_window_mateconf_notify_cb, + window, + NULL, + &mateconf_error); + if (mateconf_error) + { + gdict_show_gerror_dialog (NULL, + _("Unable to get notification for preferences"), + mateconf_error); + } + + window->font_notify_id = mateconf_client_notify_add (window->mateconf_client, + DOCUMENT_FONT_KEY, + gdict_window_mateconf_notify_cb, + window, + NULL, + &mateconf_error); + if (mateconf_error) + { + gdict_show_gerror_dialog (NULL, + _("Unable to get notification for the document font"), + mateconf_error); + } + + window->word = NULL; + window->source_name = NULL; + window->print_font = NULL; + window->defbox_font = NULL; + + window->database = NULL; + window->strategy = NULL; + + window->default_width = -1; + window->default_height = -1; + window->is_maximized = FALSE; + + window->window_id = (gulong) time (NULL); + + /* we need to create the chooser widgets for the sidebar before + * we set the construction properties + */ + window->db_chooser = gdict_database_chooser_new (); + window->strat_chooser = gdict_strategy_chooser_new (); +} + +GtkWidget * +gdict_window_new (GdictWindowAction action, + GdictSourceLoader *loader, + const gchar *source_name, + const gchar *word) +{ + GtkWidget *retval; + GdictWindow *window; + + g_return_val_if_fail (GDICT_IS_SOURCE_LOADER (loader), NULL); + + retval = g_object_new (GDICT_TYPE_WINDOW, + "action", action, + "source-loader", loader, + "source-name", source_name, + NULL); + + window = GDICT_WINDOW (retval); + + if (word && word[0] != '\0') + { + switch (action) + { + case GDICT_WINDOW_ACTION_LOOKUP: + gtk_entry_set_text (GTK_ENTRY (window->entry), word); + gdict_window_set_word (window, word, NULL); + break; + case GDICT_WINDOW_ACTION_MATCH: + { + GdictSource *source; + GdictContext *context; + + gtk_entry_set_text (GTK_ENTRY (window->entry), word); + + gdict_window_set_sidebar_visible (window, TRUE); + gdict_sidebar_view_page (GDICT_SIDEBAR (window->sidebar), + GDICT_SIDEBAR_SPELLER_PAGE); + + /* we clone the context, so that the signals that it + * fires do not get caught by the signal handlers we + * use for getting the definitions. + */ + source = gdict_source_loader_get_source (window->loader, + window->source_name); + context = gdict_source_get_context (source); + + gdict_speller_set_context (GDICT_SPELLER (window->speller), context); + + g_object_unref (context); + g_object_unref (source); + + gdict_speller_set_strategy (GDICT_SPELLER (window->speller), + window->strategy); + + gdict_speller_match (GDICT_SPELLER (window->speller), word); + } + case GDICT_WINDOW_ACTION_CLEAR: + gdict_defbox_clear (GDICT_DEFBOX (window->defbox)); + break; + default: + g_assert_not_reached (); + break; + } + } + + return retval; +} + +/* GdictWindowAction */ +static const GEnumValue _gdict_window_action_values[] = { + { GDICT_WINDOW_ACTION_LOOKUP, "GDICT_WINDOW_ACTION_LOOKUP", "lookup" }, + { GDICT_WINDOW_ACTION_MATCH, "GDICT_WINDOW_ACTION_MATCH", "match" }, + { GDICT_WINDOW_ACTION_CLEAR, "GDICT_WINDOW_ACTION_CLEAR", "clear" }, + { 0, NULL, NULL } +}; + +GType +gdict_window_action_get_type (void) +{ + static GType our_type = 0; + + if (!our_type) + our_type = g_enum_register_static ("GdictWindowAction", _gdict_window_action_values); + + return our_type; +} diff --git a/mate-dictionary/src/gdict-window.h b/mate-dictionary/src/gdict-window.h new file mode 100644 index 00000000..36455cba --- /dev/null +++ b/mate-dictionary/src/gdict-window.h @@ -0,0 +1,129 @@ +/* gdict-window.h - main application window + * + * This file is part of MATE Dictionary + * + * Copyright (C) 2005 Emmanuele Bassi + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef __GDICT_WINDOW_H__ +#define __GDICT_WINDOW_H__ + +#include <gdk-pixbuf/gdk-pixbuf.h> +#include <gtk/gtk.h> +#include <mateconf/mateconf-client.h> +#include <libgdict/gdict.h> + +G_BEGIN_DECLS + +#define GDICT_TYPE_WINDOW (gdict_window_get_type ()) +#define GDICT_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDICT_TYPE_WINDOW, GdictWindow)) +#define GDICT_IS_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDICT_TYPE_WINDOW)) + +typedef enum { + GDICT_WINDOW_ACTION_LOOKUP, + GDICT_WINDOW_ACTION_MATCH, + GDICT_WINDOW_ACTION_CLEAR +} GdictWindowAction; + +#define GDICT_TYPE_WINDOW_ACTION (gdict_window_action_get_type ()) +GType gdict_window_action_get_type (void) G_GNUC_CONST; + +typedef struct _GdictWindow GdictWindow; +typedef struct _GdictWindowClass GdictWindowClass; + +struct _GdictWindow +{ + GtkWindow parent_instance; + + GtkWidget *main_box; + GtkWidget *menubar; + GtkWidget *entry; + + /* sidebar widgets */ + GtkWidget *speller; + GtkWidget *db_chooser; + GtkWidget *strat_chooser; + GtkWidget *source_chooser; + + GtkWidget *sidebar; + GtkWidget *sidebar_frame; + + GtkWidget *defbox; + GtkWidget *defbox_frame; + + GtkWidget *status; + GtkWidget *progress; + + GtkUIManager *ui_manager; + GtkActionGroup *action_group; + + GtkEntryCompletion *completion; + GtkListStore *completion_model; + + GdictWindowAction action; + + gchar *word; + gint max_definition; + gint last_definition; + gint current_definition; + + gchar *source_name; + GdictSourceLoader *loader; + GdictContext *context; + guint definition_id; + guint lookup_start_id; + guint lookup_end_id; + guint error_id; + + gchar *database; + gchar *strategy; + gchar *print_font; + gchar *defbox_font; + + MateConfClient *mateconf_client; + guint notify_id; + guint font_notify_id; + + GdkCursor *busy_cursor; + + gint default_width; + gint default_height; + gint sidebar_width; + + guint is_maximized : 1; + guint sidebar_visible : 1; + guint statusbar_visible : 1; + + gulong window_id; +}; + +struct _GdictWindowClass +{ + GtkWindowClass parent_class; + + void (*created) (GdictWindow *parent_window, + GdictWindow *new_window); +}; + +GType gdict_window_get_type (void) G_GNUC_CONST; +GtkWidget *gdict_window_new (GdictWindowAction action, + GdictSourceLoader *loader, + const gchar *source_name, + const gchar *word); + +#endif /* __GDICT_WINDOW_H__ */ diff --git a/mate-dictionary/src/main.c b/mate-dictionary/src/main.c new file mode 100644 index 00000000..f6dcc94d --- /dev/null +++ b/mate-dictionary/src/main.c @@ -0,0 +1,24 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <libintl.h> + +#include "gdict-app.h" + +int main (int argc, char *argv[]) +{ + bindtextdomain (GETTEXT_PACKAGE, MATELOCALEDIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); + + gdict_init (&argc, &argv); + + gdict_main (); + + gdict_cleanup (); + + return EXIT_SUCCESS; +} |