[goobox] ported to modern libraries, removed use of deprecated functions
- From: Paolo Bacchilega <paobac src gnome org>
- To: svn-commits-list gnome org
- Cc:
- Subject: [goobox] ported to modern libraries, removed use of deprecated functions
- Date: Fri, 13 Nov 2009 22:31:08 +0000 (UTC)
commit 9b623aff84019c6bb227b3e3edabb0807c00d073
Author: Paolo Bacchilega <paobac src gnome org>
Date: Fri Nov 13 23:27:40 2009 +0100
ported to modern libraries, removed use of deprecated functions
huge commit that ports goobox in the modern age.
AUTHORS | 2 +-
ChangeLog | 1357 -----------
MAINTAINERS | 2 +-
Makefile.am | 13 +-
autogen.sh | 4 +-
configure.ac | 199 ++
configure.in | 218 --
copy-n-paste/Makefile.am | 16 +
copy-n-paste/eggdesktopfile.c | 1477 ++++++++++++
copy-n-paste/eggdesktopfile.h | 159 ++
copy-n-paste/eggsmclient-private.h | 53 +
copy-n-paste/eggsmclient-xsmp.c | 1370 +++++++++++
copy-n-paste/eggsmclient.c | 589 +++++
copy-n-paste/eggsmclient.h | 117 +
data/GNOME_Goobox.server.in | 15 -
data/Makefile.am | 19 +-
data/glade/Makefile.am | 13 -
data/glade/cover_chooser.glade | 237 --
data/glade/extract_dialog.glade | 241 --
data/glade/format_dialog.glade | 288 ---
data/glade/preferences.glade | 719 ------
data/glade/properties.glade | 657 ------
data/glade/ripper_dialog.glade | 151 --
data/goobox.applications | 6 -
data/goobox.schemas.in | 13 -
data/{glade => ui}/.cvsignore | 0
data/ui/Makefile.am | 12 +
data/ui/cover-chooser.ui | 187 ++
data/ui/extract.ui | 186 ++
data/ui/format-options.ui | 178 ++
data/ui/preferences.ui | 454 ++++
data/ui/properties.ui | 485 ++++
data/ui/ripper.ui | 140 ++
po/POTFILES.in | 61 +-
po/update-potfiles.sh | 13 +
src/GNOME_Goobox.idl | 24 -
src/Makefile.am | 139 +-
src/actions.c | 86 +-
src/album-info.c | 20 +-
src/bacon-cd-selection.c | 534 -----
src/bacon-cd-selection.h | 57 -
src/cd-drive.c | 1455 ------------
src/cd-drive.h | 89 -
src/dlg-cover-chooser.c | 998 ++++-----
src/dlg-extract.c | 190 +--
src/dlg-preferences.c | 309 +--
src/dlg-properties.c | 199 +--
src/dlg-ripper.c | 394 ++--
src/dlg-ripper.h | 1 -
src/dvd_plus_rw_utils.cpp | 511 -----
src/eggtrayicon.c | 559 -----
src/eggtrayicon.h | 77 -
src/file-utils.c | 1583 -------------
src/file-utils.h | 103 -
src/gconf-utils.c | 162 +-
src/gconf-utils.h | 47 +-
src/gio-utils.c | 458 ++++
src/gio-utils.h | 75 +
src/glib-utils.c | 2192 +++++++++++++++++-
src/glib-utils.h | 250 ++-
src/gnome-desktop-thumbnail.c | 1329 +++++++++++
src/gnome-desktop-thumbnail.h | 114 +
src/gnome-thumbnail-pixbuf-utils.c | 172 ++
src/goo-application.c | 219 --
src/goo-application.h | 54 -
src/goo-cdrom-bsd.c | 290 ---
src/goo-cdrom-bsd.h | 52 -
src/goo-cdrom-linux.c | 290 ---
src/goo-cdrom-linux.h | 52 -
src/goo-cdrom-solaris.c | 291 ---
src/goo-cdrom-solaris.h | 52 -
src/goo-cdrom.c | 389 ----
src/goo-cdrom.h | 106 -
src/goo-marshal.list | 1 +
src/goo-player-info.c | 155 +-
src/goo-player-info.h | 3 +-
src/goo-player.c | 645 +++----
src/goo-player.h | 27 +-
src/goo-stock.c | 2 +-
src/goo-volume-tool-button.c | 207 +--
src/goo-volume-tool-button.h | 2 -
src/goo-window.c | 1354 ++++++------
src/goo-window.h | 102 +-
src/gth-image-list.c | 4439 ------------------------------------
src/gth-image-list.h | 321 ---
src/gth-user-dir.c | 109 +
src/gth-user-dir.h | 45 +
src/gth-window.c | 394 ++++
src/gth-window.h | 89 +
src/gthumb-marshal.list | 14 -
src/gthumb-slide.c | 833 -------
src/gthumb-slide.h | 86 -
src/gtk-file-chooser-preview.c | 140 +-
src/gtk-utils.c | 870 ++++++--
src/gtk-utils.h | 201 +-
src/main.c | 815 +++----
src/main.h | 15 +-
src/preferences.c | 15 +-
src/preferences.h | 1 -
src/track-info.c | 2 +-
src/transport.hxx | 1102 ---------
src/typedefs.h | 7 +
src/ui.h | 3 +-
103 files changed, 14088 insertions(+), 21484 deletions(-)
---
diff --git a/AUTHORS b/AUTHORS
index d2db847..ece6749 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -1 +1 @@
-Paolo Bacchilega <paobac cvs gnome org>
+Paolo Bacchilega <paobac src gnome org>
diff --git a/MAINTAINERS b/MAINTAINERS
index 7f62799..c14f939 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1,3 +1,3 @@
Paolo Bacchilega
-E-mail: paobac svn gnome org
+E-mail: paobac src gnome org
Userid: paobac
diff --git a/Makefile.am b/Makefile.am
index c1f58cd..aed33ff 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,14 +1,5 @@
-## Process this file with automake to produce Makefile.in.
-SUBDIRS = \
- data \
- help \
- src \
- po
-
-
-distcleancheck_listfiles = find . -type f -print | grep -v 'omf\.out'
-
-distuninstallcheck_listfiles = find . -type f -print | grep -v '^\./var/scrollkeeper' | grep -v 'omf' | grep -v 'figures'
+SUBDIRS = copy-n-paste data help src po
+ACLOCAL_AMFLAGS = -I m4
EXTRA_DIST = \
diff --git a/autogen.sh b/autogen.sh
index 617d873..eded612 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -6,9 +6,7 @@ test -z "$srcdir" && srcdir=.
PKG_NAME="Goobox"
-(test -f $srcdir/configure.in \
- && test -f $srcdir/ChangeLog \
- && test -d $srcdir/src) || {
+(test -f $srcdir/configure.ac && test -d $srcdir/src) || {
echo -n "**Error**: Directory "\`$srcdir\'" does not look like the"
echo " top-level $PKG_NAME directory"
exit 1
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..35e5d6a
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,199 @@
+AC_INIT([goobox], [2.1.0], [http://bugzilla.gnome.org/enter_bug.cgi?product=goobox])
+
+GNOME_COMMON_INIT
+
+AC_CONFIG_MACRO_DIR([m4])
+AC_CONFIG_HEADERS([config.h])
+AC_CONFIG_SRCDIR([configure.ac])
+
+AM_INIT_AUTOMAKE([1.9 foreign])
+m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES])
+
+AC_PROG_CXX
+AM_PROG_LIBTOOL
+GNOME_DOC_INIT
+GNOME_COMPILE_WARNINGS([maximum])
+GNOME_MAINTAINER_MODE_DEFINES
+
+GLIB_REQUIRED=2.22
+GTK_REQUIRED=2.18
+GSTREAMER_REQUIRED=0.10.12
+LIBNOTIFY_REQUIRED=0.4.3
+LIBMUSICBRAINZ_REQUIRED=2.1.0
+
+dnl ===========================================================================
+
+PKG_CHECK_MODULES(GTK, [gtk+-2.0 >= $GTK_REQUIRED])
+AC_SUBST([GTK_CFLAGS])
+AC_SUBST([GTK_LIBS])
+
+PKG_CHECK_MODULES(GOO, [
+ glib-2.0 >= $GLIB_REQUIRED
+ gthread-2.0
+ gtk+-2.0 >= $GTK_REQUIRED
+ gstreamer-0.10 >= $GSTREAMER_REQUIRED
+ libbrasero-media
+ libmusicbrainz >= $LIBMUSICBRAINZ_REQUIRED
+ gconf-2.0
+ unique-1.0
+])
+AC_SUBST(GOO_CFLAGS)
+AC_SUBST(GOO_LIBS)
+
+dnl ===========================================================================
+
+AC_PATH_PROG(GLIB_GENMARSHAL, glib-genmarshal)
+AC_PATH_PROG(GLIB_MKENUMS, glib-mkenums)
+
+AC_ARG_ENABLE(run_in_place,
+ AS_HELP_STRING([--enable-run-in-place],[load ui data and extensions from the source tree]),
+ [case "${enableval}" in
+ yes) enable_run_in_place=yes ;;
+ no) enable_run_in_place=no ;;
+ *) AC_MSG_ERROR([bad value ${enableval} for --enable-run-in-place]) ;;
+ esac],
+ [enable_run_in_place=no])
+AM_CONDITIONAL(RUN_IN_PLACE, test "x$enable_run_in_place" != xno)
+
+if test x$enable_run_in_place = xyes; then
+ AC_DEFINE(RUN_IN_PLACE, 1, [load ui data and extensions from the source tree])
+fi
+
+dnl ===========================================================================
+dnl
+dnl libnotify
+dnl
+
+AC_ARG_ENABLE(notification,[AC_HELP_STRING([--enable-notification],[enable current track notification if libnotify is available [default=yes]])],, [enable_notification="yes"])
+AM_CONDITIONAL(ENABLE_NOTIFICATION, test "x$enable_notification" = xyes)
+
+LIBNOTIFY_LIBS=""
+LIBNOTIFY_CFLAGS=""
+if test x"$enable_notification" = xyes; then
+ AC_MSG_CHECKING(for notification support)
+
+ if pkg-config --atleast-version=$LIBNOTIFY_REQUIRED libnotify; then
+ LIBNOTIFY_LIBS=`pkg-config --libs libnotify`
+ LIBNOTIFY_CFLAGS=`pkg-config --cflags libnotify`
+ AC_DEFINE(ENABLE_NOTIFICATION, 1, [Have libnotify])
+ else
+ enable_notification=no
+ fi
+fi
+AC_MSG_RESULT($enable_notification)
+AC_SUBST(LIBNOTIFY_LIBS)
+AC_SUBST(LIBNOTIFY_CFLAGS)
+
+dnl ===========================================================================
+
+AC_ARG_ENABLE(media-keys,[AC_HELP_STRING([--enable-media-keys],[enable use of the keyboard media keys [default=yes]])],, [enable_media_keys="yes"])
+AM_CONDITIONAL(ENABLE_MEDIA_KEYS, test "x$enable_media_keys" = xyes)
+
+DBUS_LIBS=""
+DBUS_CFLAGS=""
+if test x"$enable_media_keys" = xyes; then
+ AC_MSG_CHECKING(for media keys support)
+
+ if pkg-config dbus-glib-1; then
+ DBUS_LIBS=`pkg-config --libs dbus-glib-1`
+ DBUS_CFLAGS=`pkg-config --cflags dbus-glib-1`
+ AC_DEFINE(ENABLE_MEDIA_KEYS, 1, [enable media keys support])
+ else
+ enable_media_keys=no
+ fi
+fi
+AC_MSG_RESULT($enable_media_keys)
+AC_SUBST(DBUS_LIBS)
+AC_SUBST(DBUS_CFLAGS)
+
+dnl ===========================================================================
+
+SYSTEM_LIBS=""
+host=`uname -s`
+case "$host" in
+*FreeBSD*)
+ DEFAULT_DEVICE="/dev/acd0c"
+ AC_DEFINE(HAVE_BSD, 1, [Define to 1 if this is a BSD system])
+ SYSTEM_LIBS="-lcam"
+ ;;
+*NetBSD*)
+ DEFAULT_DEVICE="/dev/rcd0d"
+ AC_DEFINE(HAVE_BSD, 1, [Define to 1 if this is a BSD system])
+ ;;
+*Linux*)
+ DEFAULT_DEVICE="/dev/cdrom"
+ AC_DEFINE(HAVE_LINUX, 1, [Define to 1 if this is a Linux system])
+ ;;
+*SunOS*)
+ DEFAULT_DEVICE="/vol/dev/aliases/cdrom0"
+ AC_DEFINE(HAVE_SOLARIS, 1, [Define to 1 if this is a Solaris system])
+ ;;
+esac
+
+AC_DEFINE_UNQUOTED(DEFAULT_DEVICE, "$DEFAULT_DEVICE", [Default CD device])
+AC_SUBST(SYSTEM_LIBS)
+
+dnl ===========================================================================
+
+IT_PROG_INTLTOOL([0.35.0])
+GETTEXT_PACKAGE=goobox
+AC_SUBST([GETTEXT_PACKAGE])
+AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, "$GETTEXT_PACKAGE", [GetText Package])
+AM_GLIB_GNU_GETTEXT
+
+if test "x${prefix}" = "xNONE"; then
+ AC_DEFINE_UNQUOTED(LOCALEDIR, "${ac_default_prefix}/${DATADIRNAME}/locale", [Locale directory])
+else
+ AC_DEFINE_UNQUOTED(LOCALEDIR, "${prefix}/${DATADIRNAME}/locale", [Locale directory])
+fi
+
+dnl ===========================================================================
+
+AC_ARG_ENABLE(deprecations,
+ [AS_HELP_STRING([--enable-deprecations],[warn about deprecated usages])])
+if test "x$enable_deprecations" = "xyes"; then
+ DISABLE_DEPRECATED="-DG_DISABLE_DEPRECATED -DGDK_DISABLE_DEPRECATED -DGDK_PIXBUF_DISABLE_DEPRECATED -DGTK_DISABLE_DEPRECATED"
+fi
+AC_SUBST(DISABLE_DEPRECATED)
+
+dnl ===========================================================================
+
+AC_ARG_ENABLE(schemas-install,[AC_HELP_STRING([--disable-schemas-install],
+ [Disable installation of the gconf schemas])])
+AM_CONDITIONAL(SCHEMAS_INSTALL, test x$enable_schemas_install != xno)
+
+AC_PATH_PROG(GCONFTOOL, gconftool-2, no)
+if test x"$GCONFTOOL" = xno; then
+ AC_MSG_ERROR([gconftool-2 executable not found in your path - should be installed with GConf])
+fi
+AM_GCONF_SOURCE_2
+
+dnl ===========================================================================
+
+AC_CONFIG_FILES([
+Makefile
+copy-n-paste/Makefile
+data/Makefile
+data/goobox.desktop.in
+data/ui/Makefile
+data/icons/Makefile
+data/icons/48x48/Makefile
+data/icons/48x48/apps/Makefile
+help/Makefile
+src/Makefile
+src/icons/Makefile
+po/Makefile.in
+])
+
+AC_OUTPUT
+
+echo "
+
+Configuration:
+
+ Source code location: ${srcdir}
+ Compiler: ${CC}
+ Run in place: ${enable_run_in_place}
+ Enable notification: ${enable_notification}
+ Enable media keys: ${enable_media_keys}
+"
diff --git a/copy-n-paste/Makefile.am b/copy-n-paste/Makefile.am
new file mode 100644
index 0000000..3846c85
--- /dev/null
+++ b/copy-n-paste/Makefile.am
@@ -0,0 +1,16 @@
+INCLUDES = -DGTK_DISABLE_DEPRECATED \
+ -DGDK_DISABLE_DEPRECATED \
+ -DG_DISABLE_DEPRECATED
+
+noinst_LTLIBRARIES = libeggsmclient.la
+
+libeggsmclient_la_LIBADD = $(GTK_LIBS)
+libeggsmclient_la_CFLAGS = $(GTK_CFLAGS)
+libeggsmclient_la_SOURCES = eggdesktopfile.h \
+ eggdesktopfile.c \
+ eggsmclient.h \
+ eggsmclient.c \
+ eggsmclient-private.h \
+ eggsmclient-xsmp.c
+
+-include $(top_srcdir)/git.mk
diff --git a/copy-n-paste/eggdesktopfile.c b/copy-n-paste/eggdesktopfile.c
new file mode 100644
index 0000000..357e548
--- /dev/null
+++ b/copy-n-paste/eggdesktopfile.c
@@ -0,0 +1,1477 @@
+/* eggdesktopfile.c - Freedesktop.Org Desktop Files
+ * Copyright (C) 2007 Novell, Inc.
+ *
+ * Based on gnome-desktop-item.c
+ * Copyright (C) 1999, 2000 Red Hat Inc.
+ * Copyright (C) 2001 George Lebl
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; see the file COPYING.LIB. 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 "eggdesktopfile.h"
+
+#include <string.h>
+#include <unistd.h>
+
+#include <glib/gi18n.h>
+#include <gdk/gdkx.h>
+#include <gtk/gtk.h>
+
+struct EggDesktopFile {
+ GKeyFile *key_file;
+ char *source;
+
+ char *name, *icon;
+ EggDesktopFileType type;
+ char document_code;
+};
+
+/**
+ * egg_desktop_file_new:
+ * @desktop_file_path: path to a Freedesktop-style Desktop file
+ * @error: error pointer
+ *
+ * Creates a new #EggDesktopFile for @desktop_file.
+ *
+ * Return value: the new #EggDesktopFile, or %NULL on error.
+ **/
+EggDesktopFile *
+egg_desktop_file_new (const char *desktop_file_path, GError **error)
+{
+ GKeyFile *key_file;
+
+ key_file = g_key_file_new ();
+ if (!g_key_file_load_from_file (key_file, desktop_file_path, 0, error))
+ {
+ g_key_file_free (key_file);
+ return NULL;
+ }
+
+ return egg_desktop_file_new_from_key_file (key_file, desktop_file_path,
+ error);
+}
+
+/**
+ * egg_desktop_file_new_from_data_dirs:
+ * @desktop_file_path: relative path to a Freedesktop-style Desktop file
+ * @error: error pointer
+ *
+ * Looks for @desktop_file_path in the paths returned from
+ * g_get_user_data_dir() and g_get_system_data_dirs(), and creates
+ * a new #EggDesktopFile from it.
+ *
+ * Return value: the new #EggDesktopFile, or %NULL on error.
+ **/
+EggDesktopFile *
+egg_desktop_file_new_from_data_dirs (const char *desktop_file_path,
+ GError **error)
+{
+ EggDesktopFile *desktop_file;
+ GKeyFile *key_file;
+ char *full_path;
+
+ key_file = g_key_file_new ();
+ if (!g_key_file_load_from_data_dirs (key_file, desktop_file_path,
+ &full_path, 0, error))
+ {
+ g_key_file_free (key_file);
+ return NULL;
+ }
+
+ desktop_file = egg_desktop_file_new_from_key_file (key_file,
+ full_path,
+ error);
+ g_free (full_path);
+ return desktop_file;
+}
+
+/**
+ * egg_desktop_file_new_from_dirs:
+ * @desktop_file_path: relative path to a Freedesktop-style Desktop file
+ * @search_dirs: NULL-terminated array of directories to search
+ * @error: error pointer
+ *
+ * Looks for @desktop_file_path in the paths returned from
+ * g_get_user_data_dir() and g_get_system_data_dirs(), and creates
+ * a new #EggDesktopFile from it.
+ *
+ * Return value: the new #EggDesktopFile, or %NULL on error.
+ **/
+EggDesktopFile *
+egg_desktop_file_new_from_dirs (const char *desktop_file_path,
+ const char **search_dirs,
+ GError **error)
+{
+ EggDesktopFile *desktop_file;
+ GKeyFile *key_file;
+ char *full_path;
+
+ key_file = g_key_file_new ();
+ if (!g_key_file_load_from_dirs (key_file, desktop_file_path, search_dirs,
+ &full_path, 0, error))
+ {
+ g_key_file_free (key_file);
+ return NULL;
+ }
+
+ desktop_file = egg_desktop_file_new_from_key_file (key_file,
+ full_path,
+ error);
+ g_free (full_path);
+ return desktop_file;
+}
+
+/**
+ * egg_desktop_file_new_from_key_file:
+ * @key_file: a #GKeyFile representing a desktop file
+ * @source: the path or URI that @key_file was loaded from, or %NULL
+ * @error: error pointer
+ *
+ * Creates a new #EggDesktopFile for @key_file. Assumes ownership of
+ * @key_file (on success or failure); you should consider @key_file to
+ * be freed after calling this function.
+ *
+ * Return value: the new #EggDesktopFile, or %NULL on error.
+ **/
+EggDesktopFile *
+egg_desktop_file_new_from_key_file (GKeyFile *key_file,
+ const char *source,
+ GError **error)
+{
+ EggDesktopFile *desktop_file;
+ char *version, *type;
+
+ if (!g_key_file_has_group (key_file, EGG_DESKTOP_FILE_GROUP))
+ {
+ g_set_error (error, EGG_DESKTOP_FILE_ERROR,
+ EGG_DESKTOP_FILE_ERROR_INVALID,
+ _("File is not a valid .desktop file"));
+ g_key_file_free (key_file);
+ return NULL;
+ }
+
+ version = g_key_file_get_value (key_file, EGG_DESKTOP_FILE_GROUP,
+ EGG_DESKTOP_FILE_KEY_VERSION,
+ NULL);
+ if (version)
+ {
+ double version_num;
+ char *end;
+
+ version_num = g_ascii_strtod (version, &end);
+ if (*end)
+ {
+ g_warning ("Invalid Version string '%s' in %s",
+ version, source ? source : "(unknown)");
+ }
+ else if (version_num > 1.0)
+ {
+ g_set_error (error, EGG_DESKTOP_FILE_ERROR,
+ EGG_DESKTOP_FILE_ERROR_INVALID,
+ _("Unrecognized desktop file Version '%s'"), version);
+ g_free (version);
+ g_key_file_free (key_file);
+ return NULL;
+ }
+ g_free (version);
+ }
+
+ desktop_file = g_new0 (EggDesktopFile, 1);
+ desktop_file->key_file = key_file;
+
+ if (g_path_is_absolute (source))
+ desktop_file->source = g_filename_to_uri (source, NULL, NULL);
+ else
+ desktop_file->source = g_strdup (source);
+
+ desktop_file->name = g_key_file_get_string (key_file, EGG_DESKTOP_FILE_GROUP,
+ EGG_DESKTOP_FILE_KEY_NAME, error);
+ if (!desktop_file->name)
+ {
+ egg_desktop_file_free (desktop_file);
+ return NULL;
+ }
+
+ type = g_key_file_get_string (key_file, EGG_DESKTOP_FILE_GROUP,
+ EGG_DESKTOP_FILE_KEY_TYPE, error);
+ if (!type)
+ {
+ egg_desktop_file_free (desktop_file);
+ return NULL;
+ }
+
+ if (!strcmp (type, "Application"))
+ {
+ char *exec, *p;
+
+ desktop_file->type = EGG_DESKTOP_FILE_TYPE_APPLICATION;
+
+ exec = g_key_file_get_string (key_file,
+ EGG_DESKTOP_FILE_GROUP,
+ EGG_DESKTOP_FILE_KEY_EXEC,
+ error);
+ if (!exec)
+ {
+ egg_desktop_file_free (desktop_file);
+ g_free (type);
+ return NULL;
+ }
+
+ /* See if it takes paths or URIs or neither */
+ for (p = exec; *p; p++)
+ {
+ if (*p == '%')
+ {
+ if (p[1] == '\0' || strchr ("FfUu", p[1]))
+ {
+ desktop_file->document_code = p[1];
+ break;
+ }
+ p++;
+ }
+ }
+
+ g_free (exec);
+ }
+ else if (!strcmp (type, "Link"))
+ {
+ char *url;
+
+ desktop_file->type = EGG_DESKTOP_FILE_TYPE_LINK;
+
+ url = g_key_file_get_string (key_file,
+ EGG_DESKTOP_FILE_GROUP,
+ EGG_DESKTOP_FILE_KEY_URL,
+ error);
+ if (!url)
+ {
+ egg_desktop_file_free (desktop_file);
+ g_free (type);
+ return NULL;
+ }
+ g_free (url);
+ }
+ else if (!strcmp (type, "Directory"))
+ desktop_file->type = EGG_DESKTOP_FILE_TYPE_DIRECTORY;
+ else
+ desktop_file->type = EGG_DESKTOP_FILE_TYPE_UNRECOGNIZED;
+
+ g_free (type);
+
+ /* Check the Icon key */
+ desktop_file->icon = g_key_file_get_string (key_file,
+ EGG_DESKTOP_FILE_GROUP,
+ EGG_DESKTOP_FILE_KEY_ICON,
+ NULL);
+ if (desktop_file->icon && !g_path_is_absolute (desktop_file->icon))
+ {
+ char *ext;
+
+ /* Lots of .desktop files still get this wrong */
+ ext = strrchr (desktop_file->icon, '.');
+ if (ext && (!strcmp (ext, ".png") ||
+ !strcmp (ext, ".xpm") ||
+ !strcmp (ext, ".svg")))
+ {
+ g_warning ("Desktop file '%s' has malformed Icon key '%s'"
+ "(should not include extension)",
+ source ? source : "(unknown)",
+ desktop_file->icon);
+ *ext = '\0';
+ }
+ }
+
+ return desktop_file;
+}
+
+/**
+ * egg_desktop_file_free:
+ * @desktop_file: an #EggDesktopFile
+ *
+ * Frees @desktop_file.
+ **/
+void
+egg_desktop_file_free (EggDesktopFile *desktop_file)
+{
+ g_key_file_free (desktop_file->key_file);
+ g_free (desktop_file->source);
+ g_free (desktop_file->name);
+ g_free (desktop_file->icon);
+ g_free (desktop_file);
+}
+
+/**
+ * egg_desktop_file_get_source:
+ * @desktop_file: an #EggDesktopFile
+ *
+ * Gets the URI that @desktop_file was loaded from.
+ *
+ * Return value: @desktop_file's source URI
+ **/
+const char *
+egg_desktop_file_get_source (EggDesktopFile *desktop_file)
+{
+ return desktop_file->source;
+}
+
+/**
+ * egg_desktop_file_get_desktop_file_type:
+ * @desktop_file: an #EggDesktopFile
+ *
+ * Gets the desktop file type of @desktop_file.
+ *
+ * Return value: @desktop_file's type
+ **/
+EggDesktopFileType
+egg_desktop_file_get_desktop_file_type (EggDesktopFile *desktop_file)
+{
+ return desktop_file->type;
+}
+
+/**
+ * egg_desktop_file_get_name:
+ * @desktop_file: an #EggDesktopFile
+ *
+ * Gets the (localized) value of @desktop_file's "Name" key.
+ *
+ * Return value: the application/link name
+ **/
+const char *
+egg_desktop_file_get_name (EggDesktopFile *desktop_file)
+{
+ return desktop_file->name;
+}
+
+/**
+ * egg_desktop_file_get_icon:
+ * @desktop_file: an #EggDesktopFile
+ *
+ * Gets the value of @desktop_file's "Icon" key.
+ *
+ * If the icon string is a full path (that is, if g_path_is_absolute()
+ * returns %TRUE when called on it), it points to a file containing an
+ * unthemed icon. If the icon string is not a full path, it is the
+ * name of a themed icon, which can be looked up with %GtkIconTheme,
+ * or passed directly to a theme-aware widget like %GtkImage or
+ * %GtkCellRendererPixbuf.
+ *
+ * Return value: the icon path or name
+ **/
+const char *
+egg_desktop_file_get_icon (EggDesktopFile *desktop_file)
+{
+ return desktop_file->icon;
+}
+
+gboolean
+egg_desktop_file_has_key (EggDesktopFile *desktop_file,
+ const char *key,
+ GError **error)
+{
+ return g_key_file_has_key (desktop_file->key_file,
+ EGG_DESKTOP_FILE_GROUP, key,
+ error);
+}
+
+char *
+egg_desktop_file_get_string (EggDesktopFile *desktop_file,
+ const char *key,
+ GError **error)
+{
+ return g_key_file_get_string (desktop_file->key_file,
+ EGG_DESKTOP_FILE_GROUP, key,
+ error);
+}
+
+char *
+egg_desktop_file_get_locale_string (EggDesktopFile *desktop_file,
+ const char *key,
+ const char *locale,
+ GError **error)
+{
+ return g_key_file_get_locale_string (desktop_file->key_file,
+ EGG_DESKTOP_FILE_GROUP, key, locale,
+ error);
+}
+
+gboolean
+egg_desktop_file_get_boolean (EggDesktopFile *desktop_file,
+ const char *key,
+ GError **error)
+{
+ return g_key_file_get_boolean (desktop_file->key_file,
+ EGG_DESKTOP_FILE_GROUP, key,
+ error);
+}
+
+double
+egg_desktop_file_get_numeric (EggDesktopFile *desktop_file,
+ const char *key,
+ GError **error)
+{
+ return g_key_file_get_double (desktop_file->key_file,
+ EGG_DESKTOP_FILE_GROUP, key,
+ error);
+}
+
+char **
+egg_desktop_file_get_string_list (EggDesktopFile *desktop_file,
+ const char *key,
+ gsize *length,
+ GError **error)
+{
+ return g_key_file_get_string_list (desktop_file->key_file,
+ EGG_DESKTOP_FILE_GROUP, key, length,
+ error);
+}
+
+char **
+egg_desktop_file_get_locale_string_list (EggDesktopFile *desktop_file,
+ const char *key,
+ const char *locale,
+ gsize *length,
+ GError **error)
+{
+ return g_key_file_get_locale_string_list (desktop_file->key_file,
+ EGG_DESKTOP_FILE_GROUP, key,
+ locale, length,
+ error);
+}
+
+/**
+ * egg_desktop_file_can_launch:
+ * @desktop_file: an #EggDesktopFile
+ * @desktop_environment: the name of the running desktop environment,
+ * or %NULL
+ *
+ * Tests if @desktop_file can/should be launched in the current
+ * environment. If @desktop_environment is non-%NULL, @desktop_file's
+ * "OnlyShowIn" and "NotShowIn" keys are checked to make sure that
+ * this desktop_file is appropriate for the named environment.
+ *
+ * Furthermore, if @desktop_file has type
+ * %EGG_DESKTOP_FILE_TYPE_APPLICATION, its "TryExec" key (if any) is
+ * also checked, to make sure the binary it points to exists.
+ *
+ * egg_desktop_file_can_launch() does NOT check the value of the
+ * "Hidden" key.
+ *
+ * Return value: %TRUE if @desktop_file can be launched
+ **/
+gboolean
+egg_desktop_file_can_launch (EggDesktopFile *desktop_file,
+ const char *desktop_environment)
+{
+ char *try_exec, *found_program;
+ char **only_show_in, **not_show_in;
+ gboolean found;
+ int i;
+
+ if (desktop_file->type != EGG_DESKTOP_FILE_TYPE_APPLICATION &&
+ desktop_file->type != EGG_DESKTOP_FILE_TYPE_LINK)
+ return FALSE;
+
+ if (desktop_environment)
+ {
+ only_show_in = g_key_file_get_string_list (desktop_file->key_file,
+ EGG_DESKTOP_FILE_GROUP,
+ EGG_DESKTOP_FILE_KEY_ONLY_SHOW_IN,
+ NULL, NULL);
+ if (only_show_in)
+ {
+ for (i = 0, found = FALSE; only_show_in[i] && !found; i++)
+ {
+ if (!strcmp (only_show_in[i], desktop_environment))
+ found = TRUE;
+ }
+
+ g_strfreev (only_show_in);
+
+ if (!found)
+ return FALSE;
+ }
+
+ not_show_in = g_key_file_get_string_list (desktop_file->key_file,
+ EGG_DESKTOP_FILE_GROUP,
+ EGG_DESKTOP_FILE_KEY_NOT_SHOW_IN,
+ NULL, NULL);
+ if (not_show_in)
+ {
+ for (i = 0, found = FALSE; not_show_in[i] && !found; i++)
+ {
+ if (!strcmp (not_show_in[i], desktop_environment))
+ found = TRUE;
+ }
+
+ g_strfreev (not_show_in);
+
+ if (found)
+ return FALSE;
+ }
+ }
+
+ if (desktop_file->type == EGG_DESKTOP_FILE_TYPE_APPLICATION)
+ {
+ try_exec = g_key_file_get_string (desktop_file->key_file,
+ EGG_DESKTOP_FILE_GROUP,
+ EGG_DESKTOP_FILE_KEY_TRY_EXEC,
+ NULL);
+ if (try_exec)
+ {
+ found_program = g_find_program_in_path (try_exec);
+ g_free (try_exec);
+
+ if (!found_program)
+ return FALSE;
+ g_free (found_program);
+ }
+ }
+
+ return TRUE;
+}
+
+/**
+ * egg_desktop_file_accepts_documents:
+ * @desktop_file: an #EggDesktopFile
+ *
+ * Tests if @desktop_file represents an application that can accept
+ * documents on the command line.
+ *
+ * Return value: %TRUE or %FALSE
+ **/
+gboolean
+egg_desktop_file_accepts_documents (EggDesktopFile *desktop_file)
+{
+ return desktop_file->document_code != 0;
+}
+
+/**
+ * egg_desktop_file_accepts_multiple:
+ * @desktop_file: an #EggDesktopFile
+ *
+ * Tests if @desktop_file can accept multiple documents at once.
+ *
+ * If this returns %FALSE, you can still pass multiple documents to
+ * egg_desktop_file_launch(), but that will result in multiple copies
+ * of the application being launched. See egg_desktop_file_launch()
+ * for more details.
+ *
+ * Return value: %TRUE or %FALSE
+ **/
+gboolean
+egg_desktop_file_accepts_multiple (EggDesktopFile *desktop_file)
+{
+ return (desktop_file->document_code == 'F' ||
+ desktop_file->document_code == 'U');
+}
+
+/**
+ * egg_desktop_file_accepts_uris:
+ * @desktop_file: an #EggDesktopFile
+ *
+ * Tests if @desktop_file can accept (non-"file:") URIs as documents to
+ * open.
+ *
+ * Return value: %TRUE or %FALSE
+ **/
+gboolean
+egg_desktop_file_accepts_uris (EggDesktopFile *desktop_file)
+{
+ return (desktop_file->document_code == 'U' ||
+ desktop_file->document_code == 'u');
+}
+
+static void
+append_quoted_word (GString *str,
+ const char *s,
+ gboolean in_single_quotes,
+ gboolean in_double_quotes)
+{
+ const char *p;
+
+ if (!in_single_quotes && !in_double_quotes)
+ g_string_append_c (str, '\'');
+ else if (!in_single_quotes && in_double_quotes)
+ g_string_append (str, "\"'");
+
+ if (!strchr (s, '\''))
+ g_string_append (str, s);
+ else
+ {
+ for (p = s; *p != '\0'; p++)
+ {
+ if (*p == '\'')
+ g_string_append (str, "'\\''");
+ else
+ g_string_append_c (str, *p);
+ }
+ }
+
+ if (!in_single_quotes && !in_double_quotes)
+ g_string_append_c (str, '\'');
+ else if (!in_single_quotes && in_double_quotes)
+ g_string_append (str, "'\"");
+}
+
+static void
+do_percent_subst (EggDesktopFile *desktop_file,
+ char code,
+ GString *str,
+ GSList **documents,
+ gboolean in_single_quotes,
+ gboolean in_double_quotes)
+{
+ GSList *d;
+ char *doc;
+
+ switch (code)
+ {
+ case '%':
+ g_string_append_c (str, '%');
+ break;
+
+ case 'F':
+ case 'U':
+ for (d = *documents; d; d = d->next)
+ {
+ doc = d->data;
+ g_string_append (str, " ");
+ append_quoted_word (str, doc, in_single_quotes, in_double_quotes);
+ }
+ *documents = NULL;
+ break;
+
+ case 'f':
+ case 'u':
+ if (*documents)
+ {
+ doc = (*documents)->data;
+ g_string_append (str, " ");
+ append_quoted_word (str, doc, in_single_quotes, in_double_quotes);
+ *documents = (*documents)->next;
+ }
+ break;
+
+ case 'i':
+ if (desktop_file->icon)
+ {
+ g_string_append (str, "--icon ");
+ append_quoted_word (str, desktop_file->icon,
+ in_single_quotes, in_double_quotes);
+ }
+ break;
+
+ case 'c':
+ if (desktop_file->name)
+ {
+ append_quoted_word (str, desktop_file->name,
+ in_single_quotes, in_double_quotes);
+ }
+ break;
+
+ case 'k':
+ if (desktop_file->source)
+ {
+ append_quoted_word (str, desktop_file->source,
+ in_single_quotes, in_double_quotes);
+ }
+ break;
+
+ case 'D':
+ case 'N':
+ case 'd':
+ case 'n':
+ case 'v':
+ case 'm':
+ /* Deprecated; skip */
+ break;
+
+ default:
+ g_warning ("Unrecognized %%-code '%%%c' in Exec", code);
+ break;
+ }
+}
+
+static char *
+parse_exec (EggDesktopFile *desktop_file,
+ GSList **documents,
+ GError **error)
+{
+ char *exec, *p, *command;
+ gboolean escape, single_quot, double_quot;
+ GString *gs;
+
+ exec = g_key_file_get_string (desktop_file->key_file,
+ EGG_DESKTOP_FILE_GROUP,
+ EGG_DESKTOP_FILE_KEY_EXEC,
+ error);
+ if (!exec)
+ return NULL;
+
+ /* Build the command */
+ gs = g_string_new (NULL);
+ escape = single_quot = double_quot = FALSE;
+
+ for (p = exec; *p != '\0'; p++)
+ {
+ if (escape)
+ {
+ escape = FALSE;
+ g_string_append_c (gs, *p);
+ }
+ else if (*p == '\\')
+ {
+ if (!single_quot)
+ escape = TRUE;
+ g_string_append_c (gs, *p);
+ }
+ else if (*p == '\'')
+ {
+ g_string_append_c (gs, *p);
+ if (!single_quot && !double_quot)
+ single_quot = TRUE;
+ else if (single_quot)
+ single_quot = FALSE;
+ }
+ else if (*p == '"')
+ {
+ g_string_append_c (gs, *p);
+ if (!single_quot && !double_quot)
+ double_quot = TRUE;
+ else if (double_quot)
+ double_quot = FALSE;
+ }
+ else if (*p == '%' && p[1])
+ {
+ do_percent_subst (desktop_file, p[1], gs, documents,
+ single_quot, double_quot);
+ p++;
+ }
+ else
+ g_string_append_c (gs, *p);
+ }
+
+ g_free (exec);
+ command = g_string_free (gs, FALSE);
+
+ /* Prepend "xdg-terminal " if needed (FIXME: use gvfs) */
+ if (g_key_file_has_key (desktop_file->key_file,
+ EGG_DESKTOP_FILE_GROUP,
+ EGG_DESKTOP_FILE_KEY_TERMINAL,
+ NULL))
+ {
+ GError *terminal_error = NULL;
+ gboolean use_terminal =
+ g_key_file_get_boolean (desktop_file->key_file,
+ EGG_DESKTOP_FILE_GROUP,
+ EGG_DESKTOP_FILE_KEY_TERMINAL,
+ &terminal_error);
+ if (terminal_error)
+ {
+ g_free (command);
+ g_propagate_error (error, terminal_error);
+ return NULL;
+ }
+
+ if (use_terminal)
+ {
+ gs = g_string_new ("xdg-terminal ");
+ append_quoted_word (gs, command, FALSE, FALSE);
+ g_free (command);
+ command = g_string_free (gs, FALSE);
+ }
+ }
+
+ return command;
+}
+
+static GSList *
+translate_document_list (EggDesktopFile *desktop_file, GSList *documents)
+{
+ gboolean accepts_uris = egg_desktop_file_accepts_uris (desktop_file);
+ GSList *ret, *d;
+
+ for (d = documents, ret = NULL; d; d = d->next)
+ {
+ const char *document = d->data;
+ gboolean is_uri = !g_path_is_absolute (document);
+ char *translated;
+
+ if (accepts_uris)
+ {
+ if (is_uri)
+ translated = g_strdup (document);
+ else
+ translated = g_filename_to_uri (document, NULL, NULL);
+ }
+ else
+ {
+ if (is_uri)
+ translated = g_filename_from_uri (document, NULL, NULL);
+ else
+ translated = g_strdup (document);
+ }
+
+ if (translated)
+ ret = g_slist_prepend (ret, translated);
+ }
+
+ return g_slist_reverse (ret);
+}
+
+static void
+free_document_list (GSList *documents)
+{
+ GSList *d;
+
+ for (d = documents; d; d = d->next)
+ g_free (d->data);
+ g_slist_free (documents);
+}
+
+/**
+ * egg_desktop_file_parse_exec:
+ * @desktop_file: a #EggDesktopFile
+ * @documents: a list of document paths or URIs
+ * @error: error pointer
+ *
+ * Parses @desktop_file's Exec key, inserting @documents into it, and
+ * returns the result.
+ *
+ * If @documents contains non-file: URIs and @desktop_file does not
+ * accept URIs, those URIs will be ignored. Likewise, if @documents
+ * contains more elements than @desktop_file accepts, the extra
+ * documents will be ignored.
+ *
+ * Return value: the parsed Exec string
+ **/
+char *
+egg_desktop_file_parse_exec (EggDesktopFile *desktop_file,
+ GSList *documents,
+ GError **error)
+{
+ GSList *translated, *docs;
+ char *command;
+
+ docs = translated = translate_document_list (desktop_file, documents);
+ command = parse_exec (desktop_file, &docs, error);
+ free_document_list (translated);
+
+ return command;
+}
+
+static gboolean
+parse_link (EggDesktopFile *desktop_file,
+ EggDesktopFile **app_desktop_file,
+ GSList **documents,
+ GError **error)
+{
+ char *url;
+ GKeyFile *key_file;
+
+ url = g_key_file_get_string (desktop_file->key_file,
+ EGG_DESKTOP_FILE_GROUP,
+ EGG_DESKTOP_FILE_KEY_URL,
+ error);
+ if (!url)
+ return FALSE;
+ *documents = g_slist_prepend (NULL, url);
+
+ /* FIXME: use gvfs */
+ key_file = g_key_file_new ();
+ g_key_file_set_string (key_file, EGG_DESKTOP_FILE_GROUP,
+ EGG_DESKTOP_FILE_KEY_NAME,
+ "xdg-open");
+ g_key_file_set_string (key_file, EGG_DESKTOP_FILE_GROUP,
+ EGG_DESKTOP_FILE_KEY_TYPE,
+ "Application");
+ g_key_file_set_string (key_file, EGG_DESKTOP_FILE_GROUP,
+ EGG_DESKTOP_FILE_KEY_EXEC,
+ "xdg-open %u");
+ *app_desktop_file = egg_desktop_file_new_from_key_file (key_file, NULL, NULL);
+ return TRUE;
+}
+
+#if GTK_CHECK_VERSION (2, 12, 0)
+static char *
+start_startup_notification (GdkDisplay *display,
+ EggDesktopFile *desktop_file,
+ const char *argv0,
+ int screen,
+ int workspace,
+ guint32 launch_time)
+{
+ static int sequence = 0;
+ char *startup_id;
+ char *description, *wmclass;
+ char *screen_str, *workspace_str;
+
+ if (g_key_file_has_key (desktop_file->key_file,
+ EGG_DESKTOP_FILE_GROUP,
+ EGG_DESKTOP_FILE_KEY_STARTUP_NOTIFY,
+ NULL))
+ {
+ if (!g_key_file_get_boolean (desktop_file->key_file,
+ EGG_DESKTOP_FILE_GROUP,
+ EGG_DESKTOP_FILE_KEY_STARTUP_NOTIFY,
+ NULL))
+ return NULL;
+ wmclass = NULL;
+ }
+ else
+ {
+ wmclass = g_key_file_get_string (desktop_file->key_file,
+ EGG_DESKTOP_FILE_GROUP,
+ EGG_DESKTOP_FILE_KEY_STARTUP_WM_CLASS,
+ NULL);
+ if (!wmclass)
+ return NULL;
+ }
+
+ if (launch_time == (guint32)-1)
+ launch_time = gdk_x11_display_get_user_time (display);
+ startup_id = g_strdup_printf ("%s-%lu-%s-%s-%d_TIME%lu",
+ g_get_prgname (),
+ (unsigned long)getpid (),
+ g_get_host_name (),
+ argv0,
+ sequence++,
+ (unsigned long)launch_time);
+
+ description = g_strdup_printf (_("Starting %s"), desktop_file->name);
+ screen_str = g_strdup_printf ("%d", screen);
+ workspace_str = workspace == -1 ? NULL : g_strdup_printf ("%d", workspace);
+
+ gdk_x11_display_broadcast_startup_message (display, "new",
+ "ID", startup_id,
+ "NAME", desktop_file->name,
+ "SCREEN", screen_str,
+ "BIN", argv0,
+ "ICON", desktop_file->icon,
+ "DESKTOP", workspace_str,
+ "DESCRIPTION", description,
+ "WMCLASS", wmclass,
+ NULL);
+
+ g_free (description);
+ g_free (wmclass);
+ g_free (screen_str);
+ g_free (workspace_str);
+
+ return startup_id;
+}
+
+static void
+end_startup_notification (GdkDisplay *display,
+ const char *startup_id)
+{
+ gdk_x11_display_broadcast_startup_message (display, "remove",
+ "ID", startup_id,
+ NULL);
+}
+
+#define EGG_DESKTOP_FILE_SN_TIMEOUT_LENGTH (30 /* seconds */)
+
+typedef struct {
+ GdkDisplay *display;
+ char *startup_id;
+} StartupNotificationData;
+
+static gboolean
+startup_notification_timeout (gpointer data)
+{
+ StartupNotificationData *sn_data = data;
+
+ end_startup_notification (sn_data->display, sn_data->startup_id);
+ g_object_unref (sn_data->display);
+ g_free (sn_data->startup_id);
+ g_free (sn_data);
+
+ return FALSE;
+}
+
+static void
+set_startup_notification_timeout (GdkDisplay *display,
+ const char *startup_id)
+{
+ StartupNotificationData *sn_data;
+
+ sn_data = g_new (StartupNotificationData, 1);
+ sn_data->display = g_object_ref (display);
+ sn_data->startup_id = g_strdup (startup_id);
+
+ g_timeout_add_seconds (EGG_DESKTOP_FILE_SN_TIMEOUT_LENGTH,
+ startup_notification_timeout, sn_data);
+}
+#endif /* GTK 2.12 */
+
+static GPtrArray *
+array_putenv (GPtrArray *env, char *variable)
+{
+ guint i, keylen;
+
+ if (!env)
+ {
+ char **envp;
+
+ env = g_ptr_array_new ();
+
+ envp = g_listenv ();
+ for (i = 0; envp[i]; i++)
+ {
+ const char *value;
+
+ value = g_getenv (envp[i]);
+ g_ptr_array_add (env, g_strdup_printf ("%s=%s", envp[i],
+ value ? value : ""));
+ }
+ g_strfreev (envp);
+ }
+
+ keylen = strcspn (variable, "=");
+
+ /* Remove old value of key */
+ for (i = 0; i < env->len; i++)
+ {
+ char *envvar = env->pdata[i];
+
+ if (!strncmp (envvar, variable, keylen) && envvar[keylen] == '=')
+ {
+ g_free (envvar);
+ g_ptr_array_remove_index_fast (env, i);
+ break;
+ }
+ }
+
+ /* Add new value */
+ g_ptr_array_add (env, g_strdup (variable));
+
+ return env;
+}
+
+static gboolean
+egg_desktop_file_launchv (EggDesktopFile *desktop_file,
+ GSList *documents, va_list args,
+ GError **error)
+{
+ EggDesktopFileLaunchOption option;
+ GSList *translated_documents = NULL, *docs = NULL;
+ char *command, **argv;
+ int argc, i, screen_num;
+ gboolean success, current_success;
+ GdkDisplay *display;
+ char *startup_id;
+
+ GPtrArray *env = NULL;
+ char **variables = NULL;
+ GdkScreen *screen = NULL;
+ int workspace = -1;
+ const char *directory = NULL;
+ guint32 launch_time = (guint32)-1;
+ GSpawnFlags flags = G_SPAWN_SEARCH_PATH;
+ GSpawnChildSetupFunc setup_func = NULL;
+ gpointer setup_data = NULL;
+
+ GPid *ret_pid = NULL;
+ int *ret_stdin = NULL, *ret_stdout = NULL, *ret_stderr = NULL;
+ char **ret_startup_id = NULL;
+
+ if (documents && desktop_file->document_code == 0)
+ {
+ g_set_error (error, EGG_DESKTOP_FILE_ERROR,
+ EGG_DESKTOP_FILE_ERROR_NOT_LAUNCHABLE,
+ _("Application does not accept documents on command line"));
+ return FALSE;
+ }
+
+ /* Read the options: technically it's incorrect for the caller to
+ * NULL-terminate the list of options (rather than 0-terminating
+ * it), but NULL-terminating lets us use G_GNUC_NULL_TERMINATED,
+ * it's more consistent with other glib/gtk methods, and it will
+ * work as long as sizeof (int) <= sizeof (NULL), and NULL is
+ * represented as 0. (Which is true everywhere we care about.)
+ */
+ while ((option = va_arg (args, EggDesktopFileLaunchOption)))
+ {
+ switch (option)
+ {
+ case EGG_DESKTOP_FILE_LAUNCH_CLEARENV:
+ if (env)
+ g_ptr_array_free (env, TRUE);
+ env = g_ptr_array_new ();
+ break;
+ case EGG_DESKTOP_FILE_LAUNCH_PUTENV:
+ variables = va_arg (args, char **);
+ for (i = 0; variables[i]; i++)
+ env = array_putenv (env, variables[i]);
+ break;
+
+ case EGG_DESKTOP_FILE_LAUNCH_SCREEN:
+ screen = va_arg (args, GdkScreen *);
+ break;
+ case EGG_DESKTOP_FILE_LAUNCH_WORKSPACE:
+ workspace = va_arg (args, int);
+ break;
+
+ case EGG_DESKTOP_FILE_LAUNCH_DIRECTORY:
+ directory = va_arg (args, const char *);
+ break;
+ case EGG_DESKTOP_FILE_LAUNCH_TIME:
+ launch_time = va_arg (args, guint32);
+ break;
+ case EGG_DESKTOP_FILE_LAUNCH_FLAGS:
+ flags |= va_arg (args, GSpawnFlags);
+ /* Make sure they didn't set any flags that don't make sense. */
+ flags &= ~G_SPAWN_FILE_AND_ARGV_ZERO;
+ break;
+ case EGG_DESKTOP_FILE_LAUNCH_SETUP_FUNC:
+ setup_func = va_arg (args, GSpawnChildSetupFunc);
+ setup_data = va_arg (args, gpointer);
+ break;
+
+ case EGG_DESKTOP_FILE_LAUNCH_RETURN_PID:
+ ret_pid = va_arg (args, GPid *);
+ break;
+ case EGG_DESKTOP_FILE_LAUNCH_RETURN_STDIN_PIPE:
+ ret_stdin = va_arg (args, int *);
+ break;
+ case EGG_DESKTOP_FILE_LAUNCH_RETURN_STDOUT_PIPE:
+ ret_stdout = va_arg (args, int *);
+ break;
+ case EGG_DESKTOP_FILE_LAUNCH_RETURN_STDERR_PIPE:
+ ret_stderr = va_arg (args, int *);
+ break;
+ case EGG_DESKTOP_FILE_LAUNCH_RETURN_STARTUP_ID:
+ ret_startup_id = va_arg (args, char **);
+ break;
+
+ default:
+ g_set_error (error, EGG_DESKTOP_FILE_ERROR,
+ EGG_DESKTOP_FILE_ERROR_UNRECOGNIZED_OPTION,
+ _("Unrecognized launch option: %d"),
+ GPOINTER_TO_INT (option));
+ success = FALSE;
+ goto out;
+ }
+ }
+
+ if (screen)
+ {
+ char *display_name = gdk_screen_make_display_name (screen);
+ char *display_env = g_strdup_printf ("DISPLAY=%s", display_name);
+ env = array_putenv (env, display_env);
+ g_free (display_name);
+ g_free (display_env);
+
+ display = gdk_screen_get_display (screen);
+ }
+ else
+ {
+ display = gdk_display_get_default ();
+ screen = gdk_display_get_default_screen (display);
+ }
+ screen_num = gdk_screen_get_number (screen);
+
+ translated_documents = translate_document_list (desktop_file, documents);
+ docs = translated_documents;
+
+ success = FALSE;
+
+ do
+ {
+ command = parse_exec (desktop_file, &docs, error);
+ if (!command)
+ goto out;
+
+ if (!g_shell_parse_argv (command, &argc, &argv, error))
+ {
+ g_free (command);
+ goto out;
+ }
+ g_free (command);
+
+#if GTK_CHECK_VERSION (2, 12, 0)
+ startup_id = start_startup_notification (display, desktop_file,
+ argv[0], screen_num,
+ workspace, launch_time);
+ if (startup_id)
+ {
+ char *startup_id_env = g_strdup_printf ("DESKTOP_STARTUP_ID=%s",
+ startup_id);
+ env = array_putenv (env, startup_id_env);
+ g_free (startup_id_env);
+ }
+#else
+ startup_id = NULL;
+#endif /* GTK 2.12 */
+
+ if (env != NULL)
+ g_ptr_array_add (env, NULL);
+
+ current_success =
+ g_spawn_async_with_pipes (directory,
+ argv,
+ env ? (char **)(env->pdata) : NULL,
+ flags,
+ setup_func, setup_data,
+ ret_pid,
+ ret_stdin, ret_stdout, ret_stderr,
+ error);
+ g_strfreev (argv);
+
+ if (startup_id)
+ {
+#if GTK_CHECK_VERSION (2, 12, 0)
+ if (current_success)
+ {
+ set_startup_notification_timeout (display, startup_id);
+
+ if (ret_startup_id)
+ *ret_startup_id = startup_id;
+ else
+ g_free (startup_id);
+ }
+ else
+#endif /* GTK 2.12 */
+ g_free (startup_id);
+ }
+ else if (ret_startup_id)
+ *ret_startup_id = NULL;
+
+ if (current_success)
+ {
+ /* If we successfully launch any instances of the app, make
+ * sure we return TRUE and don't set @error.
+ */
+ success = TRUE;
+ error = NULL;
+
+ /* Also, only set the output params on the first one */
+ ret_pid = NULL;
+ ret_stdin = ret_stdout = ret_stderr = NULL;
+ ret_startup_id = NULL;
+ }
+ }
+ while (docs && current_success);
+
+ out:
+ if (env)
+ {
+ g_strfreev ((char **)env->pdata);
+ g_ptr_array_free (env, FALSE);
+ }
+ free_document_list (translated_documents);
+
+ return success;
+}
+
+/**
+ * egg_desktop_file_launch:
+ * @desktop_file: an #EggDesktopFile
+ * @documents: a list of URIs or paths to documents to open
+ * @error: error pointer
+ * @...: additional options
+ *
+ * Launches @desktop_file with the given arguments. Additional options
+ * can be specified as follows:
+ *
+ * %EGG_DESKTOP_FILE_LAUNCH_CLEARENV: (no arguments)
+ * clears the environment in the child process
+ * %EGG_DESKTOP_FILE_LAUNCH_PUTENV: (char **variables)
+ * adds the NAME=VALUE strings in the given %NULL-terminated
+ * array to the child process's environment
+ * %EGG_DESKTOP_FILE_LAUNCH_SCREEN: (GdkScreen *screen)
+ * causes the application to be launched on the given screen
+ * %EGG_DESKTOP_FILE_LAUNCH_WORKSPACE: (int workspace)
+ * causes the application to be launched on the given workspace
+ * %EGG_DESKTOP_FILE_LAUNCH_DIRECTORY: (char *dir)
+ * causes the application to be launched in the given directory
+ * %EGG_DESKTOP_FILE_LAUNCH_TIME: (guint32 launch_time)
+ * sets the "launch time" for the application. If the user
+ * interacts with another window after @launch_time but before
+ * the launched application creates its first window, the window
+ * manager may choose to not give focus to the new application.
+ * Passing 0 for @launch_time will explicitly request that the
+ * application not receive focus.
+ * %EGG_DESKTOP_FILE_LAUNCH_FLAGS (GSpawnFlags flags)
+ * Sets additional #GSpawnFlags to use. See g_spawn_async() for
+ * more details.
+ * %EGG_DESKTOP_FILE_LAUNCH_SETUP_FUNC (GSpawnChildSetupFunc, gpointer)
+ * Sets the child setup callback and the data to pass to it.
+ * (See g_spawn_async() for more details.)
+ *
+ * %EGG_DESKTOP_FILE_LAUNCH_RETURN_PID (GPid **pid)
+ * On a successful launch, sets * pid to the PID of the launched
+ * application.
+ * %EGG_DESKTOP_FILE_LAUNCH_RETURN_STARTUP_ID (char **startup_id)
+ * On a successful launch, sets * startup_id to the Startup
+ * Notification "startup id" of the launched application.
+ * %EGG_DESKTOP_FILE_LAUNCH_RETURN_STDIN_PIPE (int *fd)
+ * On a successful launch, sets * fd to the file descriptor of
+ * a pipe connected to the application's stdin.
+ * %EGG_DESKTOP_FILE_LAUNCH_RETURN_STDOUT_PIPE (int *fd)
+ * On a successful launch, sets * fd to the file descriptor of
+ * a pipe connected to the application's stdout.
+ * %EGG_DESKTOP_FILE_LAUNCH_RETURN_STDERR_PIPE (int *fd)
+ * On a successful launch, sets * fd to the file descriptor of
+ * a pipe connected to the application's stderr.
+ *
+ * The options should be terminated with a single %NULL.
+ *
+ * If @documents contains multiple documents, but
+ * egg_desktop_file_accepts_multiple() returns %FALSE for
+ * @desktop_file, then egg_desktop_file_launch() will actually launch
+ * multiple instances of the application. In that case, the return
+ * value (as well as any values passed via
+ * %EGG_DESKTOP_FILE_LAUNCH_RETURN_PID, etc) will only reflect the
+ * first instance of the application that was launched (but the
+ * %EGG_DESKTOP_FILE_LAUNCH_SETUP_FUNC will be called for each
+ * instance).
+ *
+ * Return value: %TRUE if the application was successfully launched.
+ **/
+gboolean
+egg_desktop_file_launch (EggDesktopFile *desktop_file,
+ GSList *documents, GError **error,
+ ...)
+{
+ va_list args;
+ gboolean success;
+ EggDesktopFile *app_desktop_file;
+
+ switch (desktop_file->type)
+ {
+ case EGG_DESKTOP_FILE_TYPE_APPLICATION:
+ va_start (args, error);
+ success = egg_desktop_file_launchv (desktop_file, documents,
+ args, error);
+ va_end (args);
+ break;
+
+ case EGG_DESKTOP_FILE_TYPE_LINK:
+ if (documents)
+ {
+ g_set_error (error, EGG_DESKTOP_FILE_ERROR,
+ EGG_DESKTOP_FILE_ERROR_NOT_LAUNCHABLE,
+ _("Can't pass document URIs to a 'Type=Link' desktop entry"));
+ return FALSE;
+ }
+
+ if (!parse_link (desktop_file, &app_desktop_file, &documents, error))
+ return FALSE;
+
+ va_start (args, error);
+ success = egg_desktop_file_launchv (app_desktop_file, documents,
+ args, error);
+ va_end (args);
+
+ egg_desktop_file_free (app_desktop_file);
+ free_document_list (documents);
+ break;
+
+ default:
+ g_set_error (error, EGG_DESKTOP_FILE_ERROR,
+ EGG_DESKTOP_FILE_ERROR_NOT_LAUNCHABLE,
+ _("Not a launchable item"));
+ success = FALSE;
+ break;
+ }
+
+ return success;
+}
+
+
+GQuark
+egg_desktop_file_error_quark (void)
+{
+ return g_quark_from_static_string ("egg-desktop_file-error-quark");
+}
+
+
+G_LOCK_DEFINE_STATIC (egg_desktop_file);
+static EggDesktopFile *egg_desktop_file;
+
+/**
+ * egg_set_desktop_file:
+ * @desktop_file_path: path to the application's desktop file
+ *
+ * Creates an #EggDesktopFile for the application from the data at
+ * @desktop_file_path. This will also call g_set_application_name()
+ * with the localized application name from the desktop file, and
+ * gtk_window_set_default_icon_name() or
+ * gtk_window_set_default_icon_from_file() with the application's
+ * icon. Other code may use additional information from the desktop
+ * file.
+ *
+ * Note that for thread safety reasons, this function can only
+ * be called once.
+ **/
+void
+egg_set_desktop_file (const char *desktop_file_path)
+{
+ GError *error = NULL;
+
+ G_LOCK (egg_desktop_file);
+ if (egg_desktop_file)
+ egg_desktop_file_free (egg_desktop_file);
+
+ egg_desktop_file = egg_desktop_file_new (desktop_file_path, &error);
+ if (error)
+ {
+ g_warning ("Could not load desktop file '%s': %s",
+ desktop_file_path, error->message);
+ g_error_free (error);
+ }
+
+ if (egg_desktop_file) {
+ /* Set localized application name and default window icon */
+ if (egg_desktop_file->name)
+ g_set_application_name (egg_desktop_file->name);
+ if (egg_desktop_file->icon)
+ {
+ if (g_path_is_absolute (egg_desktop_file->icon))
+ gtk_window_set_default_icon_from_file (egg_desktop_file->icon, NULL);
+ else
+ gtk_window_set_default_icon_name (egg_desktop_file->icon);
+ }
+ }
+
+ G_UNLOCK (egg_desktop_file);
+}
+
+/**
+ * egg_get_desktop_file:
+ *
+ * Gets the application's #EggDesktopFile, as set by
+ * egg_set_desktop_file().
+ *
+ * Return value: the #EggDesktopFile, or %NULL if it hasn't been set.
+ **/
+EggDesktopFile *
+egg_get_desktop_file (void)
+{
+ EggDesktopFile *retval;
+
+ G_LOCK (egg_desktop_file);
+ retval = egg_desktop_file;
+ G_UNLOCK (egg_desktop_file);
+
+ return retval;
+}
diff --git a/copy-n-paste/eggdesktopfile.h b/copy-n-paste/eggdesktopfile.h
new file mode 100644
index 0000000..f8a3d3e
--- /dev/null
+++ b/copy-n-paste/eggdesktopfile.h
@@ -0,0 +1,159 @@
+/* eggdesktopfile.h - Freedesktop.Org Desktop Files
+ * Copyright (C) 2007 Novell, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place -
+ * Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __EGG_DESKTOP_FILE_H__
+#define __EGG_DESKTOP_FILE_H__
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+typedef struct EggDesktopFile EggDesktopFile;
+
+typedef enum {
+ EGG_DESKTOP_FILE_TYPE_UNRECOGNIZED,
+
+ EGG_DESKTOP_FILE_TYPE_APPLICATION,
+ EGG_DESKTOP_FILE_TYPE_LINK,
+ EGG_DESKTOP_FILE_TYPE_DIRECTORY
+} EggDesktopFileType;
+
+EggDesktopFile *egg_desktop_file_new (const char *desktop_file_path,
+ GError **error);
+
+EggDesktopFile *egg_desktop_file_new_from_data_dirs (const char *desktop_file_path,
+ GError **error);
+EggDesktopFile *egg_desktop_file_new_from_dirs (const char *desktop_file_path,
+ const char **search_dirs,
+ GError **error);
+EggDesktopFile *egg_desktop_file_new_from_key_file (GKeyFile *key_file,
+ const char *source,
+ GError **error);
+
+void egg_desktop_file_free (EggDesktopFile *desktop_file);
+
+const char *egg_desktop_file_get_source (EggDesktopFile *desktop_file);
+
+EggDesktopFileType egg_desktop_file_get_desktop_file_type (EggDesktopFile *desktop_file);
+
+const char *egg_desktop_file_get_name (EggDesktopFile *desktop_file);
+const char *egg_desktop_file_get_icon (EggDesktopFile *desktop_file);
+
+gboolean egg_desktop_file_can_launch (EggDesktopFile *desktop_file,
+ const char *desktop_environment);
+
+gboolean egg_desktop_file_accepts_documents (EggDesktopFile *desktop_file);
+gboolean egg_desktop_file_accepts_multiple (EggDesktopFile *desktop_file);
+gboolean egg_desktop_file_accepts_uris (EggDesktopFile *desktop_file);
+
+char *egg_desktop_file_parse_exec (EggDesktopFile *desktop_file,
+ GSList *documents,
+ GError **error);
+
+gboolean egg_desktop_file_launch (EggDesktopFile *desktop_file,
+ GSList *documents,
+ GError **error,
+ ...) G_GNUC_NULL_TERMINATED;
+
+typedef enum {
+ EGG_DESKTOP_FILE_LAUNCH_CLEARENV = 1,
+ EGG_DESKTOP_FILE_LAUNCH_PUTENV,
+ EGG_DESKTOP_FILE_LAUNCH_SCREEN,
+ EGG_DESKTOP_FILE_LAUNCH_WORKSPACE,
+ EGG_DESKTOP_FILE_LAUNCH_DIRECTORY,
+ EGG_DESKTOP_FILE_LAUNCH_TIME,
+ EGG_DESKTOP_FILE_LAUNCH_FLAGS,
+ EGG_DESKTOP_FILE_LAUNCH_SETUP_FUNC,
+ EGG_DESKTOP_FILE_LAUNCH_RETURN_PID,
+ EGG_DESKTOP_FILE_LAUNCH_RETURN_STDIN_PIPE,
+ EGG_DESKTOP_FILE_LAUNCH_RETURN_STDOUT_PIPE,
+ EGG_DESKTOP_FILE_LAUNCH_RETURN_STDERR_PIPE,
+ EGG_DESKTOP_FILE_LAUNCH_RETURN_STARTUP_ID
+} EggDesktopFileLaunchOption;
+
+/* Standard Keys */
+#define EGG_DESKTOP_FILE_GROUP "Desktop Entry"
+
+#define EGG_DESKTOP_FILE_KEY_TYPE "Type"
+#define EGG_DESKTOP_FILE_KEY_VERSION "Version"
+#define EGG_DESKTOP_FILE_KEY_NAME "Name"
+#define EGG_DESKTOP_FILE_KEY_GENERIC_NAME "GenericName"
+#define EGG_DESKTOP_FILE_KEY_NO_DISPLAY "NoDisplay"
+#define EGG_DESKTOP_FILE_KEY_COMMENT "Comment"
+#define EGG_DESKTOP_FILE_KEY_ICON "Icon"
+#define EGG_DESKTOP_FILE_KEY_HIDDEN "Hidden"
+#define EGG_DESKTOP_FILE_KEY_ONLY_SHOW_IN "OnlyShowIn"
+#define EGG_DESKTOP_FILE_KEY_NOT_SHOW_IN "NotShowIn"
+#define EGG_DESKTOP_FILE_KEY_TRY_EXEC "TryExec"
+#define EGG_DESKTOP_FILE_KEY_EXEC "Exec"
+#define EGG_DESKTOP_FILE_KEY_PATH "Path"
+#define EGG_DESKTOP_FILE_KEY_TERMINAL "Terminal"
+#define EGG_DESKTOP_FILE_KEY_MIME_TYPE "MimeType"
+#define EGG_DESKTOP_FILE_KEY_CATEGORIES "Categories"
+#define EGG_DESKTOP_FILE_KEY_STARTUP_NOTIFY "StartupNotify"
+#define EGG_DESKTOP_FILE_KEY_STARTUP_WM_CLASS "StartupWMClass"
+#define EGG_DESKTOP_FILE_KEY_URL "URL"
+
+/* Accessors */
+gboolean egg_desktop_file_has_key (EggDesktopFile *desktop_file,
+ const char *key,
+ GError **error);
+char *egg_desktop_file_get_string (EggDesktopFile *desktop_file,
+ const char *key,
+ GError **error) G_GNUC_MALLOC;
+char *egg_desktop_file_get_locale_string (EggDesktopFile *desktop_file,
+ const char *key,
+ const char *locale,
+ GError **error) G_GNUC_MALLOC;
+gboolean egg_desktop_file_get_boolean (EggDesktopFile *desktop_file,
+ const char *key,
+ GError **error);
+double egg_desktop_file_get_numeric (EggDesktopFile *desktop_file,
+ const char *key,
+ GError **error);
+char **egg_desktop_file_get_string_list (EggDesktopFile *desktop_file,
+ const char *key,
+ gsize *length,
+ GError **error) G_GNUC_MALLOC;
+char **egg_desktop_file_get_locale_string_list (EggDesktopFile *desktop_file,
+ const char *key,
+ const char *locale,
+ gsize *length,
+ GError **error) G_GNUC_MALLOC;
+
+
+/* Errors */
+#define EGG_DESKTOP_FILE_ERROR egg_desktop_file_error_quark()
+
+GQuark egg_desktop_file_error_quark (void);
+
+typedef enum {
+ EGG_DESKTOP_FILE_ERROR_INVALID,
+ EGG_DESKTOP_FILE_ERROR_NOT_LAUNCHABLE,
+ EGG_DESKTOP_FILE_ERROR_UNRECOGNIZED_OPTION
+} EggDesktopFileError;
+
+/* Global application desktop file */
+void egg_set_desktop_file (const char *desktop_file_path);
+EggDesktopFile *egg_get_desktop_file (void);
+
+
+G_END_DECLS
+
+#endif /* __EGG_DESKTOP_FILE_H__ */
diff --git a/copy-n-paste/eggsmclient-private.h b/copy-n-paste/eggsmclient-private.h
new file mode 100644
index 0000000..ccb10bf
--- /dev/null
+++ b/copy-n-paste/eggsmclient-private.h
@@ -0,0 +1,53 @@
+/* eggsmclient-private.h
+ * Copyright (C) 2007 Novell, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __EGG_SM_CLIENT_PRIVATE_H__
+#define __EGG_SM_CLIENT_PRIVATE_H__
+
+#include <gdkconfig.h>
+#include "eggsmclient.h"
+
+G_BEGIN_DECLS
+
+GKeyFile *egg_sm_client_save_state (EggSMClient *client);
+void egg_sm_client_quit_requested (EggSMClient *client);
+void egg_sm_client_quit_cancelled (EggSMClient *client);
+void egg_sm_client_quit (EggSMClient *client);
+
+#if defined (GDK_WINDOWING_X11)
+# ifdef EGG_SM_CLIENT_BACKEND_XSMP
+GType egg_sm_client_xsmp_get_type (void);
+EggSMClient *egg_sm_client_xsmp_new (void);
+# endif
+# ifdef EGG_SM_CLIENT_BACKEND_DBUS
+GType egg_sm_client_dbus_get_type (void);
+EggSMClient *egg_sm_client_dbus_new (void);
+# endif
+#elif defined (GDK_WINDOWING_WIN32)
+GType egg_sm_client_win32_get_type (void);
+EggSMClient *egg_sm_client_win32_new (void);
+#elif defined (GDK_WINDOWING_QUARTZ)
+GType egg_sm_client_osx_get_type (void);
+EggSMClient *egg_sm_client_osx_new (void);
+#endif
+
+G_END_DECLS
+
+
+#endif /* __EGG_SM_CLIENT_PRIVATE_H__ */
diff --git a/copy-n-paste/eggsmclient-xsmp.c b/copy-n-paste/eggsmclient-xsmp.c
new file mode 100644
index 0000000..1a56156
--- /dev/null
+++ b/copy-n-paste/eggsmclient-xsmp.c
@@ -0,0 +1,1370 @@
+/*
+ * Copyright (C) 2007 Novell, Inc.
+ *
+ * Inspired by various other pieces of code including GsmClient (C)
+ * 2001 Havoc Pennington, GnomeClient (C) 1998 Carsten Schaar, and twm
+ * session code (C) 1998 The Open Group.
+ *
+ * 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 library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "eggsmclient.h"
+#include "eggsmclient-private.h"
+
+#include "eggdesktopfile.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <X11/SM/SMlib.h>
+
+#include <gdk/gdk.h>
+
+#define EGG_TYPE_SM_CLIENT_XSMP (egg_sm_client_xsmp_get_type ())
+#define EGG_SM_CLIENT_XSMP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_SM_CLIENT_XSMP, EggSMClientXSMP))
+#define EGG_SM_CLIENT_XSMP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EGG_TYPE_SM_CLIENT_XSMP, EggSMClientXSMPClass))
+#define EGG_IS_SM_CLIENT_XSMP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_SM_CLIENT_XSMP))
+#define EGG_IS_SM_CLIENT_XSMP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EGG_TYPE_SM_CLIENT_XSMP))
+#define EGG_SM_CLIENT_XSMP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EGG_TYPE_SM_CLIENT_XSMP, EggSMClientXSMPClass))
+
+typedef struct _EggSMClientXSMP EggSMClientXSMP;
+typedef struct _EggSMClientXSMPClass EggSMClientXSMPClass;
+
+/* These mostly correspond to the similarly-named states in section
+ * 9.1 of the XSMP spec. Some of the states there aren't represented
+ * here, because we don't need them. SHUTDOWN_CANCELLED is slightly
+ * different from the spec; we use it when the client is IDLE after a
+ * ShutdownCancelled message, but the application is still interacting
+ * and doesn't know the shutdown has been cancelled yet.
+ */
+typedef enum
+{
+ XSMP_STATE_IDLE,
+ XSMP_STATE_SAVE_YOURSELF,
+ XSMP_STATE_INTERACT_REQUEST,
+ XSMP_STATE_INTERACT,
+ XSMP_STATE_SAVE_YOURSELF_DONE,
+ XSMP_STATE_SHUTDOWN_CANCELLED,
+ XSMP_STATE_CONNECTION_CLOSED
+} EggSMClientXSMPState;
+
+static const char *state_names[] = {
+ "idle",
+ "save-yourself",
+ "interact-request",
+ "interact",
+ "save-yourself-done",
+ "shutdown-cancelled",
+ "connection-closed"
+};
+
+#define EGG_SM_CLIENT_XSMP_STATE(xsmp) (state_names[(xsmp)->state])
+
+struct _EggSMClientXSMP
+{
+ EggSMClient parent;
+
+ SmcConn connection;
+ char *client_id;
+
+ EggSMClientXSMPState state;
+ char **restart_command;
+ gboolean set_restart_command;
+ int restart_style;
+
+ guint idle;
+
+ /* Current SaveYourself state */
+ guint expecting_initial_save_yourself : 1;
+ guint need_save_state : 1;
+ guint need_quit_requested : 1;
+ guint interact_errors : 1;
+ guint shutting_down : 1;
+
+ /* Todo list */
+ guint waiting_to_set_initial_properties : 1;
+ guint waiting_to_emit_quit : 1;
+ guint waiting_to_emit_quit_cancelled : 1;
+ guint waiting_to_save_myself : 1;
+
+};
+
+struct _EggSMClientXSMPClass
+{
+ EggSMClientClass parent_class;
+
+};
+
+static void sm_client_xsmp_startup (EggSMClient *client,
+ const char *client_id);
+static void sm_client_xsmp_set_restart_command (EggSMClient *client,
+ int argc,
+ const char **argv);
+static void sm_client_xsmp_will_quit (EggSMClient *client,
+ gboolean will_quit);
+static gboolean sm_client_xsmp_end_session (EggSMClient *client,
+ EggSMClientEndStyle style,
+ gboolean request_confirmation);
+
+static void xsmp_save_yourself (SmcConn smc_conn,
+ SmPointer client_data,
+ int save_style,
+ Bool shutdown,
+ int interact_style,
+ Bool fast);
+static void xsmp_die (SmcConn smc_conn,
+ SmPointer client_data);
+static void xsmp_save_complete (SmcConn smc_conn,
+ SmPointer client_data);
+static void xsmp_shutdown_cancelled (SmcConn smc_conn,
+ SmPointer client_data);
+static void xsmp_interact (SmcConn smc_conn,
+ SmPointer client_data);
+
+static SmProp *array_prop (const char *name,
+ ...);
+static SmProp *ptrarray_prop (const char *name,
+ GPtrArray *values);
+static SmProp *string_prop (const char *name,
+ const char *value);
+static SmProp *card8_prop (const char *name,
+ unsigned char value);
+
+static void set_properties (EggSMClientXSMP *xsmp, ...);
+static void delete_properties (EggSMClientXSMP *xsmp, ...);
+
+static GPtrArray *generate_command (char **restart_command,
+ const char *client_id,
+ const char *state_file);
+
+static void save_state (EggSMClientXSMP *xsmp);
+static void do_save_yourself (EggSMClientXSMP *xsmp);
+static void update_pending_events (EggSMClientXSMP *xsmp);
+
+static void ice_init (void);
+static gboolean process_ice_messages (IceConn ice_conn);
+static void smc_error_handler (SmcConn smc_conn,
+ Bool swap,
+ int offending_minor_opcode,
+ unsigned long offending_sequence,
+ int error_class,
+ int severity,
+ SmPointer values);
+
+G_DEFINE_TYPE (EggSMClientXSMP, egg_sm_client_xsmp, EGG_TYPE_SM_CLIENT)
+
+static void
+egg_sm_client_xsmp_init (EggSMClientXSMP *xsmp)
+{
+ xsmp->state = XSMP_STATE_CONNECTION_CLOSED;
+ xsmp->connection = NULL;
+ xsmp->restart_style = SmRestartIfRunning;
+}
+
+static void
+egg_sm_client_xsmp_class_init (EggSMClientXSMPClass *klass)
+{
+ EggSMClientClass *sm_client_class = EGG_SM_CLIENT_CLASS (klass);
+
+ sm_client_class->startup = sm_client_xsmp_startup;
+ sm_client_class->set_restart_command = sm_client_xsmp_set_restart_command;
+ sm_client_class->will_quit = sm_client_xsmp_will_quit;
+ sm_client_class->end_session = sm_client_xsmp_end_session;
+}
+
+EggSMClient *
+egg_sm_client_xsmp_new (void)
+{
+ if (!g_getenv ("SESSION_MANAGER"))
+ return NULL;
+
+ return g_object_new (EGG_TYPE_SM_CLIENT_XSMP, NULL);
+}
+
+static gboolean
+sm_client_xsmp_set_initial_properties (gpointer user_data)
+{
+ EggSMClientXSMP *xsmp = user_data;
+ EggDesktopFile *desktop_file;
+ GPtrArray *clone, *restart;
+ char pid_str[64];
+
+ if (xsmp->idle)
+ {
+ g_source_remove (xsmp->idle);
+ xsmp->idle = 0;
+ }
+ xsmp->waiting_to_set_initial_properties = FALSE;
+
+ if (egg_sm_client_get_mode () == EGG_SM_CLIENT_MODE_NO_RESTART)
+ xsmp->restart_style = SmRestartNever;
+
+ /* Parse info out of desktop file */
+ desktop_file = egg_get_desktop_file ();
+ if (desktop_file)
+ {
+ GError *err = NULL;
+ char *cmdline, **argv;
+ int argc;
+
+ if (xsmp->restart_style == SmRestartIfRunning)
+ {
+ if (egg_desktop_file_get_boolean (desktop_file,
+ "X-GNOME-AutoRestart", NULL))
+ xsmp->restart_style = SmRestartImmediately;
+ }
+
+ if (!xsmp->set_restart_command)
+ {
+ cmdline = egg_desktop_file_parse_exec (desktop_file, NULL, &err);
+ if (cmdline && g_shell_parse_argv (cmdline, &argc, &argv, &err))
+ {
+ egg_sm_client_set_restart_command (EGG_SM_CLIENT (xsmp),
+ argc, (const char **)argv);
+ g_strfreev (argv);
+ }
+ else
+ {
+ g_warning ("Could not parse Exec line in desktop file: %s",
+ err->message);
+ g_error_free (err);
+ }
+ g_free (cmdline);
+ }
+ }
+
+ if (!xsmp->set_restart_command)
+ xsmp->restart_command = g_strsplit (g_get_prgname (), " ", -1);
+
+ clone = generate_command (xsmp->restart_command, NULL, NULL);
+ restart = generate_command (xsmp->restart_command, xsmp->client_id, NULL);
+
+ g_debug ("Setting initial properties");
+
+ /* Program, CloneCommand, RestartCommand, and UserID are required.
+ * ProcessID isn't required, but the SM may be able to do something
+ * useful with it.
+ */
+ g_snprintf (pid_str, sizeof (pid_str), "%lu", (gulong) getpid ());
+ set_properties (xsmp,
+ string_prop (SmProgram, g_get_prgname ()),
+ ptrarray_prop (SmCloneCommand, clone),
+ ptrarray_prop (SmRestartCommand, restart),
+ string_prop (SmUserID, g_get_user_name ()),
+ string_prop (SmProcessID, pid_str),
+ card8_prop (SmRestartStyleHint, xsmp->restart_style),
+ NULL);
+ g_ptr_array_free (clone, TRUE);
+ g_ptr_array_free (restart, TRUE);
+
+ if (desktop_file)
+ {
+ set_properties (xsmp,
+ string_prop ("_GSM_DesktopFile", egg_desktop_file_get_source (desktop_file)),
+ NULL);
+ }
+
+ update_pending_events (xsmp);
+ return FALSE;
+}
+
+/* This gets called from two different places: xsmp_die() (when the
+ * server asks us to disconnect) and process_ice_messages() (when the
+ * server disconnects unexpectedly).
+ */
+static void
+sm_client_xsmp_disconnect (EggSMClientXSMP *xsmp)
+{
+ SmcConn connection;
+
+ if (!xsmp->connection)
+ return;
+
+ g_debug ("Disconnecting");
+
+ connection = xsmp->connection;
+ xsmp->connection = NULL;
+ SmcCloseConnection (connection, 0, NULL);
+ xsmp->state = XSMP_STATE_CONNECTION_CLOSED;
+
+ xsmp->waiting_to_save_myself = FALSE;
+ update_pending_events (xsmp);
+}
+
+static void
+sm_client_xsmp_startup (EggSMClient *client,
+ const char *client_id)
+{
+ EggSMClientXSMP *xsmp = (EggSMClientXSMP *)client;
+ SmcCallbacks callbacks;
+ char *ret_client_id;
+ char error_string_ret[256];
+
+ xsmp->client_id = g_strdup (client_id);
+
+ ice_init ();
+ SmcSetErrorHandler (smc_error_handler);
+
+ callbacks.save_yourself.callback = xsmp_save_yourself;
+ callbacks.die.callback = xsmp_die;
+ callbacks.save_complete.callback = xsmp_save_complete;
+ callbacks.shutdown_cancelled.callback = xsmp_shutdown_cancelled;
+
+ callbacks.save_yourself.client_data = xsmp;
+ callbacks.die.client_data = xsmp;
+ callbacks.save_complete.client_data = xsmp;
+ callbacks.shutdown_cancelled.client_data = xsmp;
+
+ client_id = NULL;
+ error_string_ret[0] = '\0';
+ xsmp->connection =
+ SmcOpenConnection (NULL, xsmp, SmProtoMajor, SmProtoMinor,
+ SmcSaveYourselfProcMask | SmcDieProcMask |
+ SmcSaveCompleteProcMask |
+ SmcShutdownCancelledProcMask,
+ &callbacks,
+ xsmp->client_id, &ret_client_id,
+ sizeof (error_string_ret), error_string_ret);
+
+ if (!xsmp->connection)
+ {
+ g_warning ("Failed to connect to the session manager: %s\n",
+ error_string_ret[0] ?
+ error_string_ret : "no error message given");
+ xsmp->state = XSMP_STATE_CONNECTION_CLOSED;
+ return;
+ }
+
+ /* We expect a pointless initial SaveYourself if either (a) we
+ * didn't have an initial client ID, or (b) we DID have an initial
+ * client ID, but the server rejected it and gave us a new one.
+ */
+ if (!xsmp->client_id ||
+ (ret_client_id && strcmp (xsmp->client_id, ret_client_id) != 0))
+ xsmp->expecting_initial_save_yourself = TRUE;
+
+ if (ret_client_id)
+ {
+ g_free (xsmp->client_id);
+ xsmp->client_id = g_strdup (ret_client_id);
+ free (ret_client_id);
+
+ gdk_threads_enter ();
+ gdk_set_sm_client_id (xsmp->client_id);
+ gdk_threads_leave ();
+
+ g_debug ("Got client ID \"%s\"", xsmp->client_id);
+ }
+
+ xsmp->state = XSMP_STATE_IDLE;
+
+ /* Do not set the initial properties until we reach the main loop,
+ * so that the application has a chance to call
+ * egg_set_desktop_file(). (This may also help the session manager
+ * have a better idea of when the application is fully up and
+ * running.)
+ */
+ xsmp->waiting_to_set_initial_properties = TRUE;
+ xsmp->idle = g_idle_add (sm_client_xsmp_set_initial_properties, client);
+}
+
+static void
+sm_client_xsmp_set_restart_command (EggSMClient *client,
+ int argc,
+ const char **argv)
+{
+ EggSMClientXSMP *xsmp = (EggSMClientXSMP *)client;
+ int i;
+
+ g_strfreev (xsmp->restart_command);
+
+ xsmp->restart_command = g_new (char *, argc + 1);
+ for (i = 0; i < argc; i++)
+ xsmp->restart_command[i] = g_strdup (argv[i]);
+ xsmp->restart_command[i] = NULL;
+
+ xsmp->set_restart_command = TRUE;
+}
+
+static void
+sm_client_xsmp_will_quit (EggSMClient *client,
+ gboolean will_quit)
+{
+ EggSMClientXSMP *xsmp = (EggSMClientXSMP *)client;
+
+ if (xsmp->state == XSMP_STATE_CONNECTION_CLOSED)
+ {
+ /* The session manager has already exited! Schedule a quit
+ * signal.
+ */
+ xsmp->waiting_to_emit_quit = TRUE;
+ update_pending_events (xsmp);
+ return;
+ }
+ else if (xsmp->state == XSMP_STATE_SHUTDOWN_CANCELLED)
+ {
+ /* We received a ShutdownCancelled message while the application
+ * was interacting; Schedule a quit_cancelled signal.
+ */
+ xsmp->waiting_to_emit_quit_cancelled = TRUE;
+ update_pending_events (xsmp);
+ return;
+ }
+
+ g_return_if_fail (xsmp->state == XSMP_STATE_INTERACT);
+
+ g_debug ("Sending InteractDone(%s)", will_quit ? "False" : "True");
+ SmcInteractDone (xsmp->connection, !will_quit);
+
+ if (will_quit && xsmp->need_save_state)
+ save_state (xsmp);
+
+ g_debug ("Sending SaveYourselfDone(%s)", will_quit ? "True" : "False");
+ SmcSaveYourselfDone (xsmp->connection, will_quit);
+ xsmp->state = XSMP_STATE_SAVE_YOURSELF_DONE;
+}
+
+static gboolean
+sm_client_xsmp_end_session (EggSMClient *client,
+ EggSMClientEndStyle style,
+ gboolean request_confirmation)
+{
+ EggSMClientXSMP *xsmp = (EggSMClientXSMP *)client;
+ int save_type;
+
+ /* To end the session via XSMP, we have to send a
+ * SaveYourselfRequest. We aren't allowed to do that if anything
+ * else is going on, but we don't want to expose this fact to the
+ * application. So we do our best to patch things up here...
+ *
+ * In the worst case, this method might block for some length of
+ * time in process_ice_messages, but the only time that code path is
+ * honestly likely to get hit is if the application tries to end the
+ * session as the very first thing it does, in which case it
+ * probably won't actually block anyway. It's not worth gunking up
+ * the API to try to deal nicely with the other 0.01% of cases where
+ * this happens.
+ */
+
+ while (xsmp->state != XSMP_STATE_IDLE ||
+ xsmp->expecting_initial_save_yourself)
+ {
+ /* If we're already shutting down, we don't need to do anything. */
+ if (xsmp->shutting_down)
+ return TRUE;
+
+ switch (xsmp->state)
+ {
+ case XSMP_STATE_CONNECTION_CLOSED:
+ return FALSE;
+
+ case XSMP_STATE_SAVE_YOURSELF:
+ /* Trying to log out from the save_state callback? Whatever.
+ * Abort the save_state.
+ */
+ SmcSaveYourselfDone (xsmp->connection, FALSE);
+ xsmp->state = XSMP_STATE_SAVE_YOURSELF_DONE;
+ break;
+
+ case XSMP_STATE_INTERACT_REQUEST:
+ case XSMP_STATE_INTERACT:
+ case XSMP_STATE_SHUTDOWN_CANCELLED:
+ /* Already in a shutdown-related state, just ignore
+ * the new shutdown request...
+ */
+ return TRUE;
+
+ case XSMP_STATE_IDLE:
+ if (xsmp->waiting_to_set_initial_properties)
+ sm_client_xsmp_set_initial_properties (xsmp);
+
+ if (!xsmp->expecting_initial_save_yourself)
+ break;
+ /* else fall through */
+
+ case XSMP_STATE_SAVE_YOURSELF_DONE:
+ /* We need to wait for some response from the server.*/
+ process_ice_messages (SmcGetIceConnection (xsmp->connection));
+ break;
+
+ default:
+ /* Hm... shouldn't happen */
+ return FALSE;
+ }
+ }
+
+ /* xfce4-session will do the wrong thing if we pass SmSaveGlobal and
+ * the user chooses to save the session. But gnome-session will do
+ * the wrong thing if we pass SmSaveBoth and the user chooses NOT to
+ * save the session... Sigh.
+ */
+ if (!strcmp (SmcVendor (xsmp->connection), "xfce4-session"))
+ save_type = SmSaveBoth;
+ else
+ save_type = SmSaveGlobal;
+
+ g_debug ("Sending SaveYourselfRequest(SmSaveGlobal, Shutdown, SmInteractStyleAny, %sFast)", request_confirmation ? "!" : "");
+ SmcRequestSaveYourself (xsmp->connection,
+ save_type,
+ True, /* shutdown */
+ SmInteractStyleAny,
+ !request_confirmation, /* fast */
+ True /* global */);
+ return TRUE;
+}
+
+static gboolean
+idle_do_pending_events (gpointer data)
+{
+ EggSMClientXSMP *xsmp = data;
+ EggSMClient *client = data;
+
+ gdk_threads_enter ();
+
+ xsmp->idle = 0;
+
+ if (xsmp->waiting_to_emit_quit)
+ {
+ xsmp->waiting_to_emit_quit = FALSE;
+ egg_sm_client_quit (client);
+ goto out;
+ }
+
+ if (xsmp->waiting_to_emit_quit_cancelled)
+ {
+ xsmp->waiting_to_emit_quit_cancelled = FALSE;
+ egg_sm_client_quit_cancelled (client);
+ xsmp->state = XSMP_STATE_IDLE;
+ }
+
+ if (xsmp->waiting_to_save_myself)
+ {
+ xsmp->waiting_to_save_myself = FALSE;
+ do_save_yourself (xsmp);
+ }
+
+ out:
+ gdk_threads_leave ();
+ return FALSE;
+}
+
+static void
+update_pending_events (EggSMClientXSMP *xsmp)
+{
+ gboolean want_idle =
+ xsmp->waiting_to_emit_quit ||
+ xsmp->waiting_to_emit_quit_cancelled ||
+ xsmp->waiting_to_save_myself;
+
+ if (want_idle)
+ {
+ if (xsmp->idle == 0)
+ xsmp->idle = g_idle_add (idle_do_pending_events, xsmp);
+ }
+ else
+ {
+ if (xsmp->idle != 0)
+ g_source_remove (xsmp->idle);
+ xsmp->idle = 0;
+ }
+}
+
+static void
+fix_broken_state (EggSMClientXSMP *xsmp, const char *message,
+ gboolean send_interact_done,
+ gboolean send_save_yourself_done)
+{
+ g_warning ("Received XSMP %s message in state %s: client or server error",
+ message, EGG_SM_CLIENT_XSMP_STATE (xsmp));
+
+ /* Forget any pending SaveYourself plans we had */
+ xsmp->waiting_to_save_myself = FALSE;
+ update_pending_events (xsmp);
+
+ if (send_interact_done)
+ SmcInteractDone (xsmp->connection, False);
+ if (send_save_yourself_done)
+ SmcSaveYourselfDone (xsmp->connection, True);
+
+ xsmp->state = send_save_yourself_done ? XSMP_STATE_SAVE_YOURSELF_DONE : XSMP_STATE_IDLE;
+}
+
+/* SM callbacks */
+
+static void
+xsmp_save_yourself (SmcConn smc_conn,
+ SmPointer client_data,
+ int save_type,
+ Bool shutdown,
+ int interact_style,
+ Bool fast)
+{
+ EggSMClientXSMP *xsmp = client_data;
+ gboolean wants_quit_requested;
+
+ g_debug ("Received SaveYourself(%s, %s, %s, %s) in state %s",
+ save_type == SmSaveLocal ? "SmSaveLocal" :
+ save_type == SmSaveGlobal ? "SmSaveGlobal" : "SmSaveBoth",
+ shutdown ? "Shutdown" : "!Shutdown",
+ interact_style == SmInteractStyleAny ? "SmInteractStyleAny" :
+ interact_style == SmInteractStyleErrors ? "SmInteractStyleErrors" :
+ "SmInteractStyleNone", fast ? "Fast" : "!Fast",
+ EGG_SM_CLIENT_XSMP_STATE (xsmp));
+
+ if (xsmp->state != XSMP_STATE_IDLE &&
+ xsmp->state != XSMP_STATE_SHUTDOWN_CANCELLED)
+ {
+ fix_broken_state (xsmp, "SaveYourself", FALSE, TRUE);
+ return;
+ }
+
+ if (xsmp->waiting_to_set_initial_properties)
+ sm_client_xsmp_set_initial_properties (xsmp);
+
+ /* If this is the initial SaveYourself, ignore it; we've already set
+ * properties and there's no reason to actually save state too.
+ */
+ if (xsmp->expecting_initial_save_yourself)
+ {
+ xsmp->expecting_initial_save_yourself = FALSE;
+
+ if (save_type == SmSaveLocal &&
+ interact_style == SmInteractStyleNone &&
+ !shutdown && !fast)
+ {
+ g_debug ("Sending SaveYourselfDone(True) for initial SaveYourself");
+ SmcSaveYourselfDone (xsmp->connection, True);
+ /* As explained in the comment at the end of
+ * do_save_yourself(), SAVE_YOURSELF_DONE is the correct
+ * state here, not IDLE.
+ */
+ xsmp->state = XSMP_STATE_SAVE_YOURSELF_DONE;
+ return;
+ }
+ else
+ g_warning ("First SaveYourself was not the expected one!");
+ }
+
+ /* Even ignoring the "fast" flag completely, there are still 18
+ * different combinations of save_type, shutdown and interact_style.
+ * We interpret them as follows:
+ *
+ * Type Shutdown Interact Interpretation
+ * G F A/E/N do nothing (1)
+ * G T N do nothing (1)*
+ * G T A/E quit_requested (2)
+ * L/B F A/E/N save_state (3)
+ * L/B T N save_state (3)*
+ * L/B T A/E quit_requested, then save_state (4)
+ *
+ * 1. Do nothing, because the SM asked us to do something
+ * uninteresting (save open files, but then don't quit
+ * afterward) or rude (save open files without asking the user
+ * for confirmation).
+ *
+ * 2. Request interaction and then emit ::quit_requested. This
+ * perhaps isn't quite correct for the SmInteractStyleErrors
+ * case, but we don't care.
+ *
+ * 3. Emit ::save_state. The SmSaveBoth SaveYourselfs in these
+ * rows essentially get demoted to SmSaveLocal, because their
+ * Global halves correspond to "do nothing".
+ *
+ * 4. Request interaction, emit ::quit_requested, and then emit
+ * ::save_state after interacting. This is the SmSaveBoth
+ * equivalent of #2, but we also promote SmSaveLocal shutdown
+ * SaveYourselfs to SmSaveBoth here, because we want to give
+ * the user a chance to save open files before quitting.
+ *
+ * (* It would be nice if we could do something useful when the
+ * session manager sends a SaveYourself with shutdown True and
+ * SmInteractStyleNone. But we can't, so we just pretend it didn't
+ * even tell us it was shutting down. The docs for ::quit mention
+ * that it might not always be preceded by ::quit_requested.)
+ */
+
+ /* As an optimization, we don't actually request interaction and
+ * emit ::quit_requested if the application isn't listening to the
+ * signal.
+ */
+ wants_quit_requested = g_signal_has_handler_pending (xsmp, g_signal_lookup ("quit_requested", EGG_TYPE_SM_CLIENT), 0, FALSE);
+
+ xsmp->need_save_state = (save_type != SmSaveGlobal);
+ xsmp->need_quit_requested = (shutdown && wants_quit_requested &&
+ interact_style != SmInteractStyleNone);
+ xsmp->interact_errors = (interact_style == SmInteractStyleErrors);
+
+ xsmp->shutting_down = shutdown;
+
+ do_save_yourself (xsmp);
+}
+
+static void
+do_save_yourself (EggSMClientXSMP *xsmp)
+{
+ if (xsmp->state == XSMP_STATE_SHUTDOWN_CANCELLED)
+ {
+ /* The SM cancelled a previous SaveYourself, but we haven't yet
+ * had a chance to tell the application, so we can't start
+ * processing this SaveYourself yet.
+ */
+ xsmp->waiting_to_save_myself = TRUE;
+ update_pending_events (xsmp);
+ return;
+ }
+
+ if (xsmp->need_quit_requested)
+ {
+ xsmp->state = XSMP_STATE_INTERACT_REQUEST;
+
+ g_debug ("Sending InteractRequest(%s)",
+ xsmp->interact_errors ? "Error" : "Normal");
+ SmcInteractRequest (xsmp->connection,
+ xsmp->interact_errors ? SmDialogError : SmDialogNormal,
+ xsmp_interact,
+ xsmp);
+ return;
+ }
+
+ if (xsmp->need_save_state)
+ {
+ save_state (xsmp);
+
+ /* Though unlikely, the client could have been disconnected
+ * while the application was saving its state.
+ */
+ if (!xsmp->connection)
+ return;
+ }
+
+ g_debug ("Sending SaveYourselfDone(True)");
+ SmcSaveYourselfDone (xsmp->connection, True);
+
+ /* The client state diagram in the XSMP spec says that after a
+ * non-shutdown SaveYourself, we go directly back to "idle". But
+ * everything else in both the XSMP spec and the libSM docs
+ * disagrees.
+ */
+ xsmp->state = XSMP_STATE_SAVE_YOURSELF_DONE;
+}
+
+static void
+save_state (EggSMClientXSMP *xsmp)
+{
+ GKeyFile *state_file;
+ char *state_file_path, *data;
+ EggDesktopFile *desktop_file;
+ GPtrArray *restart;
+ int offset, fd;
+
+ /* We set xsmp->state before emitting save_state, but our caller is
+ * responsible for setting it back afterward.
+ */
+ xsmp->state = XSMP_STATE_SAVE_YOURSELF;
+
+ state_file = egg_sm_client_save_state ((EggSMClient *)xsmp);
+ if (!state_file)
+ {
+ restart = generate_command (xsmp->restart_command, xsmp->client_id, NULL);
+ set_properties (xsmp,
+ ptrarray_prop (SmRestartCommand, restart),
+ NULL);
+ g_ptr_array_free (restart, TRUE);
+ delete_properties (xsmp, SmDiscardCommand, NULL);
+ return;
+ }
+
+ desktop_file = egg_get_desktop_file ();
+ if (desktop_file)
+ {
+ GKeyFile *merged_file;
+ char *desktop_file_path;
+
+ merged_file = g_key_file_new ();
+ desktop_file_path =
+ g_filename_from_uri (egg_desktop_file_get_source (desktop_file),
+ NULL, NULL);
+ if (desktop_file_path &&
+ g_key_file_load_from_file (merged_file, desktop_file_path,
+ G_KEY_FILE_KEEP_COMMENTS |
+ G_KEY_FILE_KEEP_TRANSLATIONS, NULL))
+ {
+ guint g, k, i;
+ char **groups, **keys, *value, *exec;
+
+ groups = g_key_file_get_groups (state_file, NULL);
+ for (g = 0; groups[g]; g++)
+ {
+ keys = g_key_file_get_keys (state_file, groups[g], NULL, NULL);
+ for (k = 0; keys[k]; k++)
+ {
+ value = g_key_file_get_value (state_file, groups[g],
+ keys[k], NULL);
+ if (value)
+ {
+ g_key_file_set_value (merged_file, groups[g],
+ keys[k], value);
+ g_free (value);
+ }
+ }
+ g_strfreev (keys);
+ }
+ g_strfreev (groups);
+
+ g_key_file_free (state_file);
+ state_file = merged_file;
+
+ /* Update Exec key using "--sm-client-state-file %k" */
+ restart = generate_command (xsmp->restart_command,
+ NULL, "%k");
+ for (i = 0; i < restart->len; i++)
+ restart->pdata[i] = g_shell_quote (restart->pdata[i]);
+ g_ptr_array_add (restart, NULL);
+ exec = g_strjoinv (" ", (char **)restart->pdata);
+ g_strfreev ((char **)restart->pdata);
+ g_ptr_array_free (restart, FALSE);
+
+ g_key_file_set_string (state_file, EGG_DESKTOP_FILE_GROUP,
+ EGG_DESKTOP_FILE_KEY_EXEC,
+ exec);
+ g_free (exec);
+ }
+ else
+ desktop_file = NULL;
+
+ g_free (desktop_file_path);
+ }
+
+ /* Now write state_file to disk. (We can't use mktemp(), because
+ * that requires the filename to end with "XXXXXX", and we want
+ * it to end with ".desktop".)
+ */
+
+ data = g_key_file_to_data (state_file, NULL, NULL);
+ g_key_file_free (state_file);
+
+ offset = 0;
+ while (1)
+ {
+ state_file_path = g_strdup_printf ("%s%csession-state%c%s-%ld.%s",
+ g_get_user_config_dir (),
+ G_DIR_SEPARATOR, G_DIR_SEPARATOR,
+ g_get_prgname (),
+ (long)time (NULL) + offset,
+ desktop_file ? "desktop" : "state");
+
+ fd = open (state_file_path, O_WRONLY | O_CREAT | O_EXCL, 0644);
+ if (fd == -1)
+ {
+ if (errno == EEXIST)
+ {
+ offset++;
+ g_free (state_file_path);
+ continue;
+ }
+ else if (errno == ENOTDIR || errno == ENOENT)
+ {
+ char *sep = strrchr (state_file_path, G_DIR_SEPARATOR);
+
+ *sep = '\0';
+ if (g_mkdir_with_parents (state_file_path, 0755) != 0)
+ {
+ g_warning ("Could not create directory '%s'",
+ state_file_path);
+ g_free (state_file_path);
+ state_file_path = NULL;
+ break;
+ }
+
+ continue;
+ }
+
+ g_warning ("Could not create file '%s': %s",
+ state_file_path, g_strerror (errno));
+ g_free (state_file_path);
+ state_file_path = NULL;
+ break;
+ }
+
+ close (fd);
+ g_file_set_contents (state_file_path, data, -1, NULL);
+ break;
+ }
+ g_free (data);
+
+ restart = generate_command (xsmp->restart_command, xsmp->client_id,
+ state_file_path);
+ set_properties (xsmp,
+ ptrarray_prop (SmRestartCommand, restart),
+ NULL);
+ g_ptr_array_free (restart, TRUE);
+
+ if (state_file_path)
+ {
+ set_properties (xsmp,
+ array_prop (SmDiscardCommand,
+ "/bin/rm", "-rf", state_file_path,
+ NULL),
+ NULL);
+ g_free (state_file_path);
+ }
+}
+
+static void
+xsmp_interact (SmcConn smc_conn,
+ SmPointer client_data)
+{
+ EggSMClientXSMP *xsmp = client_data;
+ EggSMClient *client = client_data;
+
+ g_debug ("Received Interact message in state %s",
+ EGG_SM_CLIENT_XSMP_STATE (xsmp));
+
+ if (xsmp->state != XSMP_STATE_INTERACT_REQUEST)
+ {
+ fix_broken_state (xsmp, "Interact", TRUE, TRUE);
+ return;
+ }
+
+ xsmp->state = XSMP_STATE_INTERACT;
+ egg_sm_client_quit_requested (client);
+}
+
+static void
+xsmp_die (SmcConn smc_conn,
+ SmPointer client_data)
+{
+ EggSMClientXSMP *xsmp = client_data;
+ EggSMClient *client = client_data;
+
+ g_debug ("Received Die message in state %s",
+ EGG_SM_CLIENT_XSMP_STATE (xsmp));
+
+ sm_client_xsmp_disconnect (xsmp);
+ egg_sm_client_quit (client);
+}
+
+static void
+xsmp_save_complete (SmcConn smc_conn,
+ SmPointer client_data)
+{
+ EggSMClientXSMP *xsmp = client_data;
+
+ g_debug ("Received SaveComplete message in state %s",
+ EGG_SM_CLIENT_XSMP_STATE (xsmp));
+
+ if (xsmp->state == XSMP_STATE_SAVE_YOURSELF_DONE)
+ xsmp->state = XSMP_STATE_IDLE;
+ else
+ fix_broken_state (xsmp, "SaveComplete", FALSE, FALSE);
+}
+
+static void
+xsmp_shutdown_cancelled (SmcConn smc_conn,
+ SmPointer client_data)
+{
+ EggSMClientXSMP *xsmp = client_data;
+ EggSMClient *client = client_data;
+
+ g_debug ("Received ShutdownCancelled message in state %s",
+ EGG_SM_CLIENT_XSMP_STATE (xsmp));
+
+ xsmp->shutting_down = FALSE;
+
+ if (xsmp->state == XSMP_STATE_SAVE_YOURSELF_DONE)
+ {
+ /* We've finished interacting and now the SM has agreed to
+ * cancel the shutdown.
+ */
+ xsmp->state = XSMP_STATE_IDLE;
+ egg_sm_client_quit_cancelled (client);
+ }
+ else if (xsmp->state == XSMP_STATE_SHUTDOWN_CANCELLED)
+ {
+ /* Hm... ok, so we got a shutdown SaveYourself, which got
+ * cancelled, but the application was still interacting, so we
+ * didn't tell it yet, and then *another* SaveYourself arrived,
+ * which we must still be waiting to tell the app about, except
+ * that now that SaveYourself has been cancelled too! Dizzy yet?
+ */
+ xsmp->waiting_to_save_myself = FALSE;
+ update_pending_events (xsmp);
+ }
+ else
+ {
+ g_debug ("Sending SaveYourselfDone(False)");
+ SmcSaveYourselfDone (xsmp->connection, False);
+
+ if (xsmp->state == XSMP_STATE_INTERACT)
+ {
+ /* The application is currently interacting, so we can't
+ * tell it about the cancellation yet; we will wait until
+ * after it calls egg_sm_client_will_quit().
+ */
+ xsmp->state = XSMP_STATE_SHUTDOWN_CANCELLED;
+ }
+ else
+ {
+ /* The shutdown was cancelled before the application got a
+ * chance to interact.
+ */
+ xsmp->state = XSMP_STATE_IDLE;
+ }
+ }
+}
+
+/* Utilities */
+
+/* Create a restart/clone/Exec command based on @restart_command.
+ * If @client_id is non-%NULL, add "--sm-client-id @client_id".
+ * If @state_file is non-%NULL, add "--sm-client-state-file @state_file".
+ *
+ * None of the input strings are g_strdup()ed; the caller must keep
+ * them around until it is done with the returned GPtrArray, and must
+ * then free the array, but not its contents.
+ */
+static GPtrArray *
+generate_command (char **restart_command, const char *client_id,
+ const char *state_file)
+{
+ GPtrArray *cmd;
+ int i;
+
+ cmd = g_ptr_array_new ();
+ g_ptr_array_add (cmd, restart_command[0]);
+
+ if (client_id)
+ {
+ g_ptr_array_add (cmd, "--sm-client-id");
+ g_ptr_array_add (cmd, (char *)client_id);
+ }
+
+ if (state_file)
+ {
+ g_ptr_array_add (cmd, "--sm-client-state-file");
+ g_ptr_array_add (cmd, (char *)state_file);
+ }
+
+ for (i = 1; restart_command[i]; i++)
+ g_ptr_array_add (cmd, restart_command[i]);
+
+ return cmd;
+}
+
+/* Takes a NULL-terminated list of SmProp * values, created by
+ * array_prop, ptrarray_prop, string_prop, card8_prop, sets them, and
+ * frees them.
+ */
+static void
+set_properties (EggSMClientXSMP *xsmp, ...)
+{
+ GPtrArray *props;
+ SmProp *prop;
+ va_list ap;
+ guint i;
+
+ props = g_ptr_array_new ();
+
+ va_start (ap, xsmp);
+ while ((prop = va_arg (ap, SmProp *)))
+ g_ptr_array_add (props, prop);
+ va_end (ap);
+
+ if (xsmp->connection)
+ {
+ SmcSetProperties (xsmp->connection, props->len,
+ (SmProp **)props->pdata);
+ }
+
+ for (i = 0; i < props->len; i++)
+ {
+ prop = props->pdata[i];
+ g_free (prop->vals);
+ g_free (prop);
+ }
+ g_ptr_array_free (props, TRUE);
+}
+
+/* Takes a NULL-terminated list of property names and deletes them. */
+static void
+delete_properties (EggSMClientXSMP *xsmp, ...)
+{
+ GPtrArray *props;
+ char *prop;
+ va_list ap;
+
+ if (!xsmp->connection)
+ return;
+
+ props = g_ptr_array_new ();
+
+ va_start (ap, xsmp);
+ while ((prop = va_arg (ap, char *)))
+ g_ptr_array_add (props, prop);
+ va_end (ap);
+
+ SmcDeleteProperties (xsmp->connection, props->len,
+ (char **)props->pdata);
+
+ g_ptr_array_free (props, TRUE);
+}
+
+/* Takes an array of strings and creates a LISTofARRAY8 property. The
+ * strings are neither dupped nor freed; they need to remain valid
+ * until you're done with the SmProp.
+ */
+static SmProp *
+array_prop (const char *name, ...)
+{
+ SmProp *prop;
+ SmPropValue pv;
+ GArray *vals;
+ char *value;
+ va_list ap;
+
+ prop = g_new (SmProp, 1);
+ prop->name = (char *)name;
+ prop->type = SmLISTofARRAY8;
+
+ vals = g_array_new (FALSE, FALSE, sizeof (SmPropValue));
+
+ va_start (ap, name);
+ while ((value = va_arg (ap, char *)))
+ {
+ pv.length = strlen (value);
+ pv.value = value;
+ g_array_append_val (vals, pv);
+ }
+
+ prop->num_vals = vals->len;
+ prop->vals = (SmPropValue *)vals->data;
+
+ g_array_free (vals, FALSE);
+
+ return prop;
+}
+
+/* Takes a GPtrArray of strings and creates a LISTofARRAY8 property.
+ * The array contents are neither dupped nor freed; they need to
+ * remain valid until you're done with the SmProp.
+ */
+static SmProp *
+ptrarray_prop (const char *name, GPtrArray *values)
+{
+ SmProp *prop;
+ SmPropValue pv;
+ GArray *vals;
+ guint i;
+
+ prop = g_new (SmProp, 1);
+ prop->name = (char *)name;
+ prop->type = SmLISTofARRAY8;
+
+ vals = g_array_new (FALSE, FALSE, sizeof (SmPropValue));
+
+ for (i = 0; i < values->len; i++)
+ {
+ pv.length = strlen (values->pdata[i]);
+ pv.value = values->pdata[i];
+ g_array_append_val (vals, pv);
+ }
+
+ prop->num_vals = vals->len;
+ prop->vals = (SmPropValue *)vals->data;
+
+ g_array_free (vals, FALSE);
+
+ return prop;
+}
+
+/* Takes a string and creates an ARRAY8 property. The string is
+ * neither dupped nor freed; it needs to remain valid until you're
+ * done with the SmProp.
+ */
+static SmProp *
+string_prop (const char *name, const char *value)
+{
+ SmProp *prop;
+
+ prop = g_new (SmProp, 1);
+ prop->name = (char *)name;
+ prop->type = SmARRAY8;
+
+ prop->num_vals = 1;
+ prop->vals = g_new (SmPropValue, 1);
+
+ prop->vals[0].length = strlen (value);
+ prop->vals[0].value = (char *)value;
+
+ return prop;
+}
+
+/* Takes a char and creates a CARD8 property. */
+static SmProp *
+card8_prop (const char *name, unsigned char value)
+{
+ SmProp *prop;
+ char *card8val;
+
+ /* To avoid having to allocate and free prop->vals[0], we cheat and
+ * make vals a 2-element-long array and then use the second element
+ * to store value.
+ */
+
+ prop = g_new (SmProp, 1);
+ prop->name = (char *)name;
+ prop->type = SmCARD8;
+
+ prop->num_vals = 1;
+ prop->vals = g_new (SmPropValue, 2);
+ card8val = (char *)(&prop->vals[1]);
+ card8val[0] = value;
+
+ prop->vals[0].length = 1;
+ prop->vals[0].value = card8val;
+
+ return prop;
+}
+
+/* ICE code. This makes no effort to play nice with anyone else trying
+ * to use libICE. Fortunately, no one uses libICE for anything other
+ * than SM. (DCOP uses ICE, but it has its own private copy of
+ * libICE.)
+ *
+ * When this moves to gtk, it will need to be cleverer, to avoid
+ * tripping over old apps that use GnomeClient or that use libSM
+ * directly.
+ */
+
+#include <X11/ICE/ICElib.h>
+#include <fcntl.h>
+
+static void ice_error_handler (IceConn ice_conn,
+ Bool swap,
+ int offending_minor_opcode,
+ unsigned long offending_sequence,
+ int error_class,
+ int severity,
+ IcePointer values);
+static void ice_io_error_handler (IceConn ice_conn);
+static void ice_connection_watch (IceConn ice_conn,
+ IcePointer client_data,
+ Bool opening,
+ IcePointer *watch_data);
+
+static void
+ice_init (void)
+{
+ IceSetIOErrorHandler (ice_io_error_handler);
+ IceSetErrorHandler (ice_error_handler);
+ IceAddConnectionWatch (ice_connection_watch, NULL);
+}
+
+static gboolean
+process_ice_messages (IceConn ice_conn)
+{
+ IceProcessMessagesStatus status;
+
+ gdk_threads_enter ();
+ status = IceProcessMessages (ice_conn, NULL, NULL);
+ gdk_threads_leave ();
+
+ switch (status)
+ {
+ case IceProcessMessagesSuccess:
+ return TRUE;
+
+ case IceProcessMessagesIOError:
+ sm_client_xsmp_disconnect (IceGetConnectionContext (ice_conn));
+ return FALSE;
+
+ case IceProcessMessagesConnectionClosed:
+ return FALSE;
+
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static gboolean
+ice_iochannel_watch (GIOChannel *channel,
+ GIOCondition condition,
+ gpointer client_data)
+{
+ return process_ice_messages (client_data);
+}
+
+static void
+ice_connection_watch (IceConn ice_conn,
+ IcePointer client_data,
+ Bool opening,
+ IcePointer *watch_data)
+{
+ guint watch_id;
+
+ if (opening)
+ {
+ GIOChannel *channel;
+ int fd = IceConnectionNumber (ice_conn);
+
+ fcntl (fd, F_SETFD, fcntl (fd, F_GETFD, 0) | FD_CLOEXEC);
+ channel = g_io_channel_unix_new (fd);
+ watch_id = g_io_add_watch (channel, G_IO_IN | G_IO_ERR,
+ ice_iochannel_watch, ice_conn);
+ g_io_channel_unref (channel);
+
+ *watch_data = GUINT_TO_POINTER (watch_id);
+ }
+ else
+ {
+ watch_id = GPOINTER_TO_UINT (*watch_data);
+ g_source_remove (watch_id);
+ }
+}
+
+static void
+ice_error_handler (IceConn ice_conn,
+ Bool swap,
+ int offending_minor_opcode,
+ unsigned long offending_sequence,
+ int error_class,
+ int severity,
+ IcePointer values)
+{
+ /* Do nothing */
+}
+
+static void
+ice_io_error_handler (IceConn ice_conn)
+{
+ /* Do nothing */
+}
+
+static void
+smc_error_handler (SmcConn smc_conn,
+ Bool swap,
+ int offending_minor_opcode,
+ unsigned long offending_sequence,
+ int error_class,
+ int severity,
+ SmPointer values)
+{
+ /* Do nothing */
+}
diff --git a/copy-n-paste/eggsmclient.c b/copy-n-paste/eggsmclient.c
new file mode 100644
index 0000000..efa901d
--- /dev/null
+++ b/copy-n-paste/eggsmclient.c
@@ -0,0 +1,589 @@
+/*
+ * Copyright (C) 2007 Novell, Inc.
+ *
+ * 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 library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <glib/gi18n.h>
+
+#include "eggsmclient.h"
+#include "eggsmclient-private.h"
+
+static void egg_sm_client_debug_handler (const char *log_domain,
+ GLogLevelFlags log_level,
+ const char *message,
+ gpointer user_data);
+
+enum {
+ SAVE_STATE,
+ QUIT_REQUESTED,
+ QUIT_CANCELLED,
+ QUIT,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
+struct _EggSMClientPrivate {
+ GKeyFile *state_file;
+};
+
+#define EGG_SM_CLIENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), EGG_TYPE_SM_CLIENT, EggSMClientPrivate))
+
+G_DEFINE_TYPE (EggSMClient, egg_sm_client, G_TYPE_OBJECT)
+
+static EggSMClient *global_client;
+static EggSMClientMode global_client_mode = EGG_SM_CLIENT_MODE_NORMAL;
+
+static void
+egg_sm_client_init (EggSMClient *client)
+{
+ ;
+}
+
+static void
+egg_sm_client_class_init (EggSMClientClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (EggSMClientPrivate));
+
+ /**
+ * EggSMClient::save_state:
+ * @client: the client
+ * @state_file: a #GKeyFile to save state information into
+ *
+ * Emitted when the session manager has requested that the
+ * application save information about its current state. The
+ * application should save its state into @state_file, and then the
+ * session manager may then restart the application in a future
+ * session and tell it to initialize itself from that state.
+ *
+ * You should not save any data into @state_file's "start group"
+ * (ie, the %NULL group). Instead, applications should save their
+ * data into groups with names that start with the application name,
+ * and libraries that connect to this signal should save their data
+ * into groups with names that start with the library name.
+ *
+ * Alternatively, rather than (or in addition to) using @state_file,
+ * the application can save its state by calling
+ * egg_sm_client_set_restart_command() during the processing of this
+ * signal (eg, to include a list of files to open).
+ **/
+ signals[SAVE_STATE] =
+ g_signal_new ("save_state",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (EggSMClientClass, save_state),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE,
+ 1, G_TYPE_POINTER);
+
+ /**
+ * EggSMClient::quit_requested:
+ * @client: the client
+ *
+ * Emitted when the session manager requests that the application
+ * exit (generally because the user is logging out). The application
+ * should decide whether or not it is willing to quit (perhaps after
+ * asking the user what to do with documents that have unsaved
+ * changes) and then call egg_sm_client_will_quit(), passing %TRUE
+ * or %FALSE to give its answer to the session manager. (It does not
+ * need to give an answer before returning from the signal handler;
+ * it can interact with the user asynchronously and then give its
+ * answer later on.) If the application does not connect to this
+ * signal, then #EggSMClient will automatically return %TRUE on its
+ * behalf.
+ *
+ * The application should not save its session state as part of
+ * handling this signal; if the user has requested that the session
+ * be saved when logging out, then ::save_state will be emitted
+ * separately.
+ *
+ * If the application agrees to quit, it should then wait for either
+ * the ::quit_cancelled or ::quit signals to be emitted.
+ **/
+ signals[QUIT_REQUESTED] =
+ g_signal_new ("quit_requested",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (EggSMClientClass, quit_requested),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+
+ /**
+ * EggSMClient::quit_cancelled:
+ * @client: the client
+ *
+ * Emitted when the session manager decides to cancel a logout after
+ * the application has already agreed to quit. After receiving this
+ * signal, the application can go back to what it was doing before
+ * receiving the ::quit_requested signal.
+ **/
+ signals[QUIT_CANCELLED] =
+ g_signal_new ("quit_cancelled",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (EggSMClientClass, quit_cancelled),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+
+ /**
+ * EggSMClient::quit:
+ * @client: the client
+ *
+ * Emitted when the session manager wants the application to quit
+ * (generally because the user is logging out). The application
+ * should exit as soon as possible after receiving this signal; if
+ * it does not, the session manager may choose to forcibly kill it.
+ *
+ * Normally a GUI application would only be sent a ::quit if it
+ * agreed to quit in response to a ::quit_requested signal. However,
+ * this is not guaranteed; in some situations the session manager
+ * may decide to end the session without giving applications a
+ * chance to object.
+ **/
+ signals[QUIT] =
+ g_signal_new ("quit",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (EggSMClientClass, quit),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+}
+
+static gboolean sm_client_disable = FALSE;
+static char *sm_client_state_file = NULL;
+static char *sm_client_id = NULL;
+static char *sm_config_prefix = NULL;
+
+static gboolean
+sm_client_post_parse_func (GOptionContext *context,
+ GOptionGroup *group,
+ gpointer data,
+ GError **error)
+{
+ EggSMClient *client = egg_sm_client_get ();
+
+ if (sm_client_id == NULL)
+ {
+ const gchar *desktop_autostart_id;
+
+ desktop_autostart_id = g_getenv ("DESKTOP_AUTOSTART_ID");
+
+ if (desktop_autostart_id != NULL)
+ sm_client_id = g_strdup (desktop_autostart_id);
+ }
+
+ /* Unset DESKTOP_AUTOSTART_ID in order to avoid child processes to
+ * use the same client id. */
+ g_unsetenv ("DESKTOP_AUTOSTART_ID");
+
+ if (EGG_SM_CLIENT_GET_CLASS (client)->startup)
+ EGG_SM_CLIENT_GET_CLASS (client)->startup (client, sm_client_id);
+ return TRUE;
+}
+
+/**
+ * egg_sm_client_get_option_group:
+ *
+ * Creates a %GOptionGroup containing the session-management-related
+ * options. You should add this group to the application's
+ * %GOptionContext if you want to use #EggSMClient.
+ *
+ * Return value: the %GOptionGroup
+ **/
+GOptionGroup *
+egg_sm_client_get_option_group (void)
+{
+ const GOptionEntry entries[] = {
+ { "sm-client-disable", 0, 0,
+ G_OPTION_ARG_NONE, &sm_client_disable,
+ N_("Disable connection to session manager"), NULL },
+ { "sm-client-state-file", 0, 0,
+ G_OPTION_ARG_FILENAME, &sm_client_state_file,
+ N_("Specify file containing saved configuration"), N_("FILE") },
+ { "sm-client-id", 0, 0,
+ G_OPTION_ARG_STRING, &sm_client_id,
+ N_("Specify session management ID"), N_("ID") },
+ /* GnomeClient compatibility option */
+ { "sm-disable", 0, G_OPTION_FLAG_HIDDEN,
+ G_OPTION_ARG_NONE, &sm_client_disable,
+ NULL, NULL },
+ /* GnomeClient compatibility option. This is a dummy option that only
+ * exists so that sessions saved by apps with GnomeClient can be restored
+ * later when they've switched to EggSMClient. See bug #575308.
+ */
+ { "sm-config-prefix", 0, G_OPTION_FLAG_HIDDEN,
+ G_OPTION_ARG_STRING, &sm_config_prefix,
+ NULL, NULL },
+ { NULL }
+ };
+ GOptionGroup *group;
+
+ /* Use our own debug handler for the "EggSMClient" domain. */
+ g_log_set_handler (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG,
+ egg_sm_client_debug_handler, NULL);
+
+ group = g_option_group_new ("sm-client",
+ _("Session management options:"),
+ _("Show session management options"),
+ NULL, NULL);
+ g_option_group_add_entries (group, entries);
+ g_option_group_set_parse_hooks (group, NULL, sm_client_post_parse_func);
+
+ return group;
+}
+
+/**
+ * egg_sm_client_set_mode:
+ * @mode: an #EggSMClient mode
+ *
+ * Sets the "mode" of #EggSMClient as follows:
+ *
+ * %EGG_SM_CLIENT_MODE_DISABLED: Session management is completely
+ * disabled. The application will not even connect to the session
+ * manager. (egg_sm_client_get() will still return an #EggSMClient,
+ * but it will just be a dummy object.)
+ *
+ * %EGG_SM_CLIENT_MODE_NO_RESTART: The application will connect to
+ * the session manager (and thus will receive notification when the
+ * user is logging out, etc), but will request to not be
+ * automatically restarted with saved state in future sessions.
+ *
+ * %EGG_SM_CLIENT_MODE_NORMAL: The default. #EggSMCLient will
+ * function normally.
+ *
+ * This must be called before the application's main loop begins.
+ **/
+void
+egg_sm_client_set_mode (EggSMClientMode mode)
+{
+ global_client_mode = mode;
+}
+
+/**
+ * egg_sm_client_get_mode:
+ *
+ * Gets the global #EggSMClientMode. See egg_sm_client_set_mode()
+ * for details.
+ *
+ * Return value: the global #EggSMClientMode
+ **/
+EggSMClientMode
+egg_sm_client_get_mode (void)
+{
+ return global_client_mode;
+}
+
+/**
+ * egg_sm_client_get:
+ *
+ * Returns the master #EggSMClient for the application.
+ *
+ * On platforms that support saved sessions (ie, POSIX/X11), the
+ * application will only request to be restarted by the session
+ * manager if you call egg_set_desktop_file() to set an application
+ * desktop file. In particular, if the desktop file contains the key
+ * "X
+ *
+ * Return value: the master #EggSMClient.
+ **/
+EggSMClient *
+egg_sm_client_get (void)
+{
+ if (!global_client)
+ {
+ if (global_client_mode != EGG_SM_CLIENT_MODE_DISABLED &&
+ !sm_client_disable)
+ {
+#if defined (GDK_WINDOWING_WIN32)
+ global_client = egg_sm_client_win32_new ();
+#elif defined (GDK_WINDOWING_QUARTZ)
+ global_client = egg_sm_client_osx_new ();
+#else
+ /* If both D-Bus and XSMP are compiled in, try XSMP first
+ * (since it supports state saving) and fall back to D-Bus
+ * if XSMP isn't available.
+ */
+# ifdef EGG_SM_CLIENT_BACKEND_XSMP
+ global_client = egg_sm_client_xsmp_new ();
+# endif
+# ifdef EGG_SM_CLIENT_BACKEND_DBUS
+ if (!global_client)
+ global_client = egg_sm_client_dbus_new ();
+# endif
+#endif
+ }
+
+ /* Fallback: create a dummy client, so that callers don't have
+ * to worry about a %NULL return value.
+ */
+ if (!global_client)
+ global_client = g_object_new (EGG_TYPE_SM_CLIENT, NULL);
+ }
+
+ return global_client;
+}
+
+/**
+ * egg_sm_client_is_resumed:
+ * @client: the client
+ *
+ * Checks whether or not the current session has been resumed from
+ * a previous saved session. If so, the application should call
+ * egg_sm_client_get_state_file() and restore its state from the
+ * returned #GKeyFile.
+ *
+ * Return value: %TRUE if the session has been resumed
+ **/
+gboolean
+egg_sm_client_is_resumed (EggSMClient *client)
+{
+ g_return_val_if_fail (client == global_client, FALSE);
+
+ return sm_client_state_file != NULL;
+}
+
+/**
+ * egg_sm_client_get_state_file:
+ * @client: the client
+ *
+ * If the application was resumed by the session manager, this will
+ * return the #GKeyFile containing its state from the previous
+ * session.
+ *
+ * Note that other libraries and #EggSMClient itself may also store
+ * state in the key file, so if you call egg_sm_client_get_groups(),
+ * on it, the return value will likely include groups that you did not
+ * put there yourself. (It is also not guaranteed that the first
+ * group created by the application will still be the "start group"
+ * when it is resumed.)
+ *
+ * Return value: the #GKeyFile containing the application's earlier
+ * state, or %NULL on error. You should not free this key file; it
+ * is owned by @client.
+ **/
+GKeyFile *
+egg_sm_client_get_state_file (EggSMClient *client)
+{
+ EggSMClientPrivate *priv = EGG_SM_CLIENT_GET_PRIVATE (client);
+ char *state_file_path;
+ GError *err = NULL;
+
+ g_return_val_if_fail (client == global_client, NULL);
+
+ if (!sm_client_state_file)
+ return NULL;
+ if (priv->state_file)
+ return priv->state_file;
+
+ if (!strncmp (sm_client_state_file, "file://", 7))
+ state_file_path = g_filename_from_uri (sm_client_state_file, NULL, NULL);
+ else
+ state_file_path = g_strdup (sm_client_state_file);
+
+ priv->state_file = g_key_file_new ();
+ if (!g_key_file_load_from_file (priv->state_file, state_file_path, 0, &err))
+ {
+ g_warning ("Could not load SM state file '%s': %s",
+ sm_client_state_file, err->message);
+ g_clear_error (&err);
+ g_key_file_free (priv->state_file);
+ priv->state_file = NULL;
+ }
+
+ g_free (state_file_path);
+ return priv->state_file;
+}
+
+/**
+ * egg_sm_client_set_restart_command:
+ * @client: the client
+ * @argc: the length of @argv
+ * @argv: argument vector
+ *
+ * Sets the command used to restart @client if it does not have a
+ * .desktop file that can be used to find its restart command.
+ *
+ * This can also be used when handling the ::save_state signal, to
+ * save the current state via an updated command line. (Eg, providing
+ * a list of filenames to open when the application is resumed.)
+ **/
+void
+egg_sm_client_set_restart_command (EggSMClient *client,
+ int argc,
+ const char **argv)
+{
+ g_return_if_fail (EGG_IS_SM_CLIENT (client));
+
+ if (EGG_SM_CLIENT_GET_CLASS (client)->set_restart_command)
+ EGG_SM_CLIENT_GET_CLASS (client)->set_restart_command (client, argc, argv);
+}
+
+/**
+ * egg_sm_client_will_quit:
+ * @client: the client
+ * @will_quit: whether or not the application is willing to quit
+ *
+ * This MUST be called in response to the ::quit_requested signal, to
+ * indicate whether or not the application is willing to quit. The
+ * application may call it either directly from the signal handler, or
+ * at some later point (eg, after asynchronously interacting with the
+ * user).
+ *
+ * If the application does not connect to ::quit_requested,
+ * #EggSMClient will call this method on its behalf (passing %TRUE
+ * for @will_quit).
+ *
+ * After calling this method, the application should wait to receive
+ * either ::quit_cancelled or ::quit.
+ **/
+void
+egg_sm_client_will_quit (EggSMClient *client,
+ gboolean will_quit)
+{
+ g_return_if_fail (EGG_IS_SM_CLIENT (client));
+
+ if (EGG_SM_CLIENT_GET_CLASS (client)->will_quit)
+ EGG_SM_CLIENT_GET_CLASS (client)->will_quit (client, will_quit);
+}
+
+/**
+ * egg_sm_client_end_session:
+ * @style: a hint at how to end the session
+ * @request_confirmation: whether or not the user should get a chance
+ * to confirm the action
+ *
+ * Requests that the session manager end the current session. @style
+ * indicates how the session should be ended, and
+ * @request_confirmation indicates whether or not the user should be
+ * given a chance to confirm the logout/reboot/shutdown. Both of these
+ * flags are merely hints though; the session manager may choose to
+ * ignore them.
+ *
+ * Return value: %TRUE if the request was sent; %FALSE if it could not
+ * be (eg, because it could not connect to the session manager).
+ **/
+gboolean
+egg_sm_client_end_session (EggSMClientEndStyle style,
+ gboolean request_confirmation)
+{
+ EggSMClient *client = egg_sm_client_get ();
+
+ g_return_val_if_fail (EGG_IS_SM_CLIENT (client), FALSE);
+
+ if (EGG_SM_CLIENT_GET_CLASS (client)->end_session)
+ {
+ return EGG_SM_CLIENT_GET_CLASS (client)->end_session (client, style,
+ request_confirmation);
+ }
+ else
+ return FALSE;
+}
+
+/* Signal-emitting callbacks from platform-specific code */
+
+GKeyFile *
+egg_sm_client_save_state (EggSMClient *client)
+{
+ GKeyFile *state_file;
+ char *group;
+
+ g_return_val_if_fail (client == global_client, NULL);
+
+ state_file = g_key_file_new ();
+
+ g_debug ("Emitting save_state");
+ g_signal_emit (client, signals[SAVE_STATE], 0, state_file);
+ g_debug ("Done emitting save_state");
+
+ group = g_key_file_get_start_group (state_file);
+ if (group)
+ {
+ g_free (group);
+ return state_file;
+ }
+ else
+ {
+ g_key_file_free (state_file);
+ return NULL;
+ }
+}
+
+void
+egg_sm_client_quit_requested (EggSMClient *client)
+{
+ g_return_if_fail (client == global_client);
+
+ if (!g_signal_has_handler_pending (client, signals[QUIT_REQUESTED], 0, FALSE))
+ {
+ g_debug ("Not emitting quit_requested because no one is listening");
+ egg_sm_client_will_quit (client, TRUE);
+ return;
+ }
+
+ g_debug ("Emitting quit_requested");
+ g_signal_emit (client, signals[QUIT_REQUESTED], 0);
+ g_debug ("Done emitting quit_requested");
+}
+
+void
+egg_sm_client_quit_cancelled (EggSMClient *client)
+{
+ g_return_if_fail (client == global_client);
+
+ g_debug ("Emitting quit_cancelled");
+ g_signal_emit (client, signals[QUIT_CANCELLED], 0);
+ g_debug ("Done emitting quit_cancelled");
+}
+
+void
+egg_sm_client_quit (EggSMClient *client)
+{
+ g_return_if_fail (client == global_client);
+
+ g_debug ("Emitting quit");
+ g_signal_emit (client, signals[QUIT], 0);
+ g_debug ("Done emitting quit");
+
+ /* FIXME: should we just call gtk_main_quit() here? */
+}
+
+static void
+egg_sm_client_debug_handler (const char *log_domain,
+ GLogLevelFlags log_level,
+ const char *message,
+ gpointer user_data)
+{
+ static int debug = -1;
+
+ if (debug < 0)
+ debug = (g_getenv ("EGG_SM_CLIENT_DEBUG") != NULL);
+
+ if (debug)
+ g_log_default_handler (log_domain, log_level, message, NULL);
+}
diff --git a/copy-n-paste/eggsmclient.h b/copy-n-paste/eggsmclient.h
new file mode 100644
index 0000000..e620b75
--- /dev/null
+++ b/copy-n-paste/eggsmclient.h
@@ -0,0 +1,117 @@
+/* eggsmclient.h
+ * Copyright (C) 2007 Novell, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __EGG_SM_CLIENT_H__
+#define __EGG_SM_CLIENT_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define EGG_TYPE_SM_CLIENT (egg_sm_client_get_type ())
+#define EGG_SM_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_SM_CLIENT, EggSMClient))
+#define EGG_SM_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EGG_TYPE_SM_CLIENT, EggSMClientClass))
+#define EGG_IS_SM_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_SM_CLIENT))
+#define EGG_IS_SM_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EGG_TYPE_SM_CLIENT))
+#define EGG_SM_CLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EGG_TYPE_SM_CLIENT, EggSMClientClass))
+
+typedef struct _EggSMClient EggSMClient;
+typedef struct _EggSMClientClass EggSMClientClass;
+typedef struct _EggSMClientPrivate EggSMClientPrivate;
+
+typedef enum {
+ EGG_SM_CLIENT_END_SESSION_DEFAULT,
+ EGG_SM_CLIENT_LOGOUT,
+ EGG_SM_CLIENT_REBOOT,
+ EGG_SM_CLIENT_SHUTDOWN
+} EggSMClientEndStyle;
+
+typedef enum {
+ EGG_SM_CLIENT_MODE_DISABLED,
+ EGG_SM_CLIENT_MODE_NO_RESTART,
+ EGG_SM_CLIENT_MODE_NORMAL
+} EggSMClientMode;
+
+struct _EggSMClient
+{
+ GObject parent;
+
+};
+
+struct _EggSMClientClass
+{
+ GObjectClass parent_class;
+
+ /* signals */
+ void (*save_state) (EggSMClient *client,
+ GKeyFile *state_file);
+
+ void (*quit_requested) (EggSMClient *client);
+ void (*quit_cancelled) (EggSMClient *client);
+ void (*quit) (EggSMClient *client);
+
+ /* virtual methods */
+ void (*startup) (EggSMClient *client,
+ const char *client_id);
+ void (*set_restart_command) (EggSMClient *client,
+ int argc,
+ const char **argv);
+ void (*will_quit) (EggSMClient *client,
+ gboolean will_quit);
+ gboolean (*end_session) (EggSMClient *client,
+ EggSMClientEndStyle style,
+ gboolean request_confirmation);
+
+ /* Padding for future expansion */
+ void (*_egg_reserved1) (void);
+ void (*_egg_reserved2) (void);
+ void (*_egg_reserved3) (void);
+ void (*_egg_reserved4) (void);
+};
+
+GType egg_sm_client_get_type (void) G_GNUC_CONST;
+
+GOptionGroup *egg_sm_client_get_option_group (void);
+
+/* Initialization */
+void egg_sm_client_set_mode (EggSMClientMode mode);
+EggSMClientMode egg_sm_client_get_mode (void);
+EggSMClient *egg_sm_client_get (void);
+
+/* Resuming a saved session */
+gboolean egg_sm_client_is_resumed (EggSMClient *client);
+GKeyFile *egg_sm_client_get_state_file (EggSMClient *client);
+
+/* Alternate means of saving state */
+void egg_sm_client_set_restart_command (EggSMClient *client,
+ int argc,
+ const char **argv);
+
+/* Handling "quit_requested" signal */
+void egg_sm_client_will_quit (EggSMClient *client,
+ gboolean will_quit);
+
+/* Initiate a logout/reboot/shutdown */
+gboolean egg_sm_client_end_session (EggSMClientEndStyle style,
+ gboolean request_confirmation);
+
+G_END_DECLS
+
+
+#endif /* __EGG_SM_CLIENT_H__ */
diff --git a/data/Makefile.am b/data/Makefile.am
index ebbf010..2613b75 100644
--- a/data/Makefile.am
+++ b/data/Makefile.am
@@ -1,7 +1,4 @@
-SUBDIRS = glade icons
-
-applicationsdir = $(datadir)/application-registry
-applications_DATA = goobox.applications
+SUBDIRS = ui icons
desktop_in_in_files = goobox.desktop.in.in
desktop_in_files = $(desktop_in_in_files:.desktop.in.in=.desktop.in)
@@ -21,28 +18,18 @@ install-data-local:
GCONF_CONFIG_SOURCE=$(GCONF_SCHEMA_CONFIG_SOURCE) $(GCONFTOOL) --makefile-install-rule $(top_builddir)/data/$(schema_DATA)
endif
-bonobodir = $(libdir)/bonobo
-serverdir = $(libdir)/bonobo/servers
-server_in_files = GNOME_Goobox.server.in
-server_DATA = $(server_in_files:.server.in=.server)
-
- INTLTOOL_SERVER_RULE@
-
EXTRA_DIST = \
goobox.schemas \
goobox.schemas.in \
- $(applications_DATA) \
$(desktop_in_in_files) \
$(desktop_in_files) \
$(desktop_DATA) \
- $(icon_DATA) \
- $(server_in_files)
+ $(icon_DATA)
DISTCLEANFILES = \
$(schema_DATA) \
$(desktop_in_files) \
- $(desktop_DATA) \
- $(server_DATA)
+ $(desktop_DATA)
-include $(top_srcdir)/git.mk
diff --git a/data/goobox.schemas.in b/data/goobox.schemas.in
index 8e9c250..049eaab 100644
--- a/data/goobox.schemas.in
+++ b/data/goobox.schemas.in
@@ -256,19 +256,6 @@
</locale>
</schema>
- <schema>
- <key>/schemas/apps/goobox/extract/first_time</key>
- <applyto>/apps/goobox/extract/first_time</applyto>
- <owner>goobox</owner>
- <type>bool</type>
- <default>true</default>
- <locale name="C">
- <short></short>
- <long>
- </long>
- </locale>
- </schema>
-
<!-- RIPPER -->
<schema>
diff --git a/data/glade/.cvsignore b/data/ui/.cvsignore
similarity index 100%
rename from data/glade/.cvsignore
rename to data/ui/.cvsignore
diff --git a/data/ui/Makefile.am b/data/ui/Makefile.am
new file mode 100644
index 0000000..d8399e5
--- /dev/null
+++ b/data/ui/Makefile.am
@@ -0,0 +1,12 @@
+gladedir = $(datadir)/goobox/ui
+glade_DATA = \
+ cover-chooser.ui \
+ extract.ui \
+ format-options.ui \
+ preferences.ui \
+ properties.ui \
+ ripper.ui
+
+EXTRA_DIST = $(glade_DATA)
+
+-include $(top_srcdir)/git.mk
diff --git a/data/ui/cover-chooser.ui b/data/ui/cover-chooser.ui
new file mode 100644
index 0000000..c8cd1bd
--- /dev/null
+++ b/data/ui/cover-chooser.ui
@@ -0,0 +1,187 @@
+<?xml version="1.0"?>
+<interface>
+ <requires lib="gtk+" version="2.16"/>
+ <!-- interface-naming-policy toplevel-contextual -->
+ <object class="GtkDialog" id="cover_chooser_dialog">
+ <property name="visible">True</property>
+ <property name="border_width">5</property>
+ <property name="title" translatable="yes">Choose a CD Cover</property>
+ <property name="window_position">center-on-parent</property>
+ <property name="type_hint">dialog</property>
+ <property name="has_separator">False</property>
+ <child internal-child="vbox">
+ <object class="GtkVBox" id="dialog-vbox1">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkVBox" id="image_list_box">
+ <property name="visible">True</property>
+ <property name="border_width">5</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox1">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkHBox" id="hbox1">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Found images:</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="progress_label">
+ <property name="visible">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="cancel_search_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="relief">none</property>
+ <child>
+ <object class="GtkImage" id="image1">
+ <property name="visible">True</property>
+ <property name="stock">gtk-cancel</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="pack_type">end</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="icon_view_scrolledwindow">
+ <property name="width_request">554</property>
+ <property name="height_request">390</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">automatic</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child internal-child="action_area">
+ <object class="GtkHButtonBox" id="dialog-action_area1">
+ <property name="visible">True</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="help_button">
+ <property name="label">gtk-help</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ <property name="secondary">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="revert_button">
+ <property name="label">gtk-undo</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="ok_button">
+ <property name="label">gtk-apply</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="cancel_button">
+ <property name="label">gtk-close</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-11">help_button</action-widget>
+ <action-widget response="-7">revert_button</action-widget>
+ <action-widget response="-10">ok_button</action-widget>
+ <action-widget response="-7">cancel_button</action-widget>
+ </action-widgets>
+ </object>
+</interface>
diff --git a/data/ui/extract.ui b/data/ui/extract.ui
new file mode 100644
index 0000000..4f5fbee
--- /dev/null
+++ b/data/ui/extract.ui
@@ -0,0 +1,186 @@
+<?xml version="1.0"?>
+<interface>
+ <requires lib="gtk+" version="2.16"/>
+ <!-- interface-naming-policy toplevel-contextual -->
+ <object class="GtkDialog" id="extract_dialog">
+ <property name="border_width">6</property>
+ <property name="title" translatable="yes">Extract Tracks</property>
+ <property name="resizable">False</property>
+ <property name="window_position">center-on-parent</property>
+ <property name="type_hint">dialog</property>
+ <property name="has_separator">False</property>
+ <child internal-child="vbox">
+ <object class="GtkVBox" id="dialog-vbox2">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox3">
+ <property name="visible">True</property>
+ <property name="border_width">5</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox9">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label7">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes"><b>Extract</b></property>
+ <property name="use_markup">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox5">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkLabel" id="label8">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"> </property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox10">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkVBox" id="vbox11">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkRadioButton" id="all_tracks_radiobutton">
+ <property name="label" translatable="yes">_All tracks</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkRadioButton" id="selected_tracks_radiobutton">
+ <property name="label" translatable="yes">_Selected tracks</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">all_tracks_radiobutton</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child internal-child="action_area">
+ <object class="GtkHButtonBox" id="dialog-action_area2">
+ <property name="visible">True</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="cancel_button">
+ <property name="label">gtk-cancel</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="ok_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="receives_default">False</property>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="help_button">
+ <property name="label">gtk-help</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">2</property>
+ <property name="secondary">True</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-6">cancel_button</action-widget>
+ <action-widget response="-5">ok_button</action-widget>
+ <action-widget response="-11">help_button</action-widget>
+ </action-widgets>
+ </object>
+</interface>
diff --git a/data/ui/format-options.ui b/data/ui/format-options.ui
new file mode 100644
index 0000000..9732e71
--- /dev/null
+++ b/data/ui/format-options.ui
@@ -0,0 +1,178 @@
+<?xml version="1.0"?>
+<interface>
+ <requires lib="gtk+" version="2.16"/>
+ <!-- interface-naming-policy toplevel-contextual -->
+ <object class="GtkAdjustment" id="adjustment1">
+ <property name="upper">10</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">1</property>
+ <property name="page_size">1</property>
+ </object>
+ <object class="GtkDialog" id="format_dialog">
+ <property name="title" translatable="yes">Format Properties</property>
+ <property name="resizable">False</property>
+ <property name="window_position">center-on-parent</property>
+ <property name="type_hint">dialog</property>
+ <property name="has_separator">False</property>
+ <child internal-child="vbox">
+ <object class="GtkVBox" id="dialog-vbox4">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox168">
+ <property name="visible">True</property>
+ <property name="border_width">5</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkFrame" id="frame1">
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment1">
+ <property name="visible">True</property>
+ <property name="top_padding">12</property>
+ <property name="left_padding">6</property>
+ <child>
+ <object class="GtkVBox" id="vbox1">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="description_label">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="wrap">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox18">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="quality_label">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox169">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkHScale" id="quality_scale">
+ <property name="width_request">250</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="adjustment">adjustment1</property>
+ <property name="digits">0</property>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox17">
+ <property name="visible">True</property>
+ <property name="spacing">24</property>
+ <child>
+ <object class="GtkLabel" id="smaller_value_label">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="use_markup">True</property>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="bigger_value_label">
+ <property name="visible">True</property>
+ <property name="xalign">1</property>
+ <property name="use_markup">True</property>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="title_label">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child internal-child="action_area">
+ <object class="GtkHButtonBox" id="dialog-action_area4">
+ <property name="visible">True</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="ok_button">
+ <property name="label">gtk-close</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-7">ok_button</action-widget>
+ </action-widgets>
+ </object>
+</interface>
diff --git a/data/ui/preferences.ui b/data/ui/preferences.ui
new file mode 100644
index 0000000..a0922fb
--- /dev/null
+++ b/data/ui/preferences.ui
@@ -0,0 +1,454 @@
+<?xml version="1.0"?>
+<interface>
+ <requires lib="gtk+" version="2.16"/>
+ <!-- interface-naming-policy toplevel-contextual -->
+ <object class="GtkDialog" id="preferences_dialog">
+ <property name="border_width">5</property>
+ <property name="title" translatable="yes">CD Player Preferences</property>
+ <property name="resizable">False</property>
+ <property name="window_position">center-on-parent</property>
+ <property name="type_hint">dialog</property>
+ <property name="has_separator">False</property>
+ <child internal-child="vbox">
+ <object class="GtkVBox" id="dialog-vbox1">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">5</property>
+ <child>
+ <object class="GtkVBox" id="vbox1">
+ <property name="visible">True</property>
+ <property name="border_width">5</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkNotebook" id="notebook">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkVBox" id="vbox167">
+ <property name="visible">True</property>
+ <property name="border_width">12</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkVBox" id="general_vbox">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes"><b>CD Drive</b></property>
+ <property name="use_markup">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkAlignment" id="alignment1">
+ <property name="visible">True</property>
+ <property name="left_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="drive_selector_box">
+ <property name="visible">True</property>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="autoplay_checkbutton">
+ <property name="label" translatable="yes">Automatically play newly inserted discs</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="label37">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">General</property>
+ </object>
+ <packing>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox156">
+ <property name="visible">True</property>
+ <property name="border_width">12</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox157">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label28">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes"><b>Destination folder</b></property>
+ <property name="use_markup">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox15">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkLabel" id="label29">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"> </property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox158">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkFileChooserButton" id="destination_filechooserbutton">
+ <property name="visible">True</property>
+ <property name="action">select-folder</property>
+ <property name="local_only">False</property>
+ <property name="title" translatable="yes">Choose destination folder</property>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox159">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label30">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes"><b>Output format</b></property>
+ <property name="use_markup">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox16">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkLabel" id="label31">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"> </property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox160">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">24</property>
+ <child>
+ <object class="GtkVBox" id="vbox166">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkHBox" id="filetype_combobox_box">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <object class="GtkButton" id="filetype_properties_button">
+ <property name="label">gtk-properties</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="pack_type">end</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkNotebook" id="encoding_notebook">
+ <property name="visible">True</property>
+ <property name="show_tabs">False</property>
+ <property name="show_border">False</property>
+ <child>
+ <object class="GtkVBox" id="vbox146">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">24</property>
+ <child>
+ <object class="GtkLabel" id="ogg_description_label">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes"><small><i>Vorbis is an open source, lossy audio codec with high quality output at a lower file size than MP3.</i></small></property>
+ <property name="use_markup">True</property>
+ <property name="wrap">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="label32">
+ <property name="visible">True</property>
+ </object>
+ <packing>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox149">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">24</property>
+ <child>
+ <object class="GtkLabel" id="flac_description_label">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes"><small><i>Free Lossless Audio Codec (FLAC) is an open source codec that compresses but does not degrade audio quality.</i></small></property>
+ <property name="use_markup">True</property>
+ <property name="wrap">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="label33">
+ <property name="visible">True</property>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox161">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkLabel" id="wave_description_label">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes"><small><i>WAV+PCM is a lossless format that holds uncompressed, raw pulse-code modulated (PCM) audio.</i></small></property>
+ <property name="use_markup">True</property>
+ <property name="wrap">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="label36">
+ <property name="visible">True</property>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="save_playlist_checkbutton">
+ <property name="label" translatable="yes">_Save playlist</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="label27">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Extraction</property>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child internal-child="action_area">
+ <object class="GtkHButtonBox" id="dialog-action_area1">
+ <property name="visible">True</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="help_button">
+ <property name="label">gtk-help</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ <property name="secondary">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="close_button">
+ <property name="label">gtk-close</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-11">help_button</action-widget>
+ <action-widget response="-7">close_button</action-widget>
+ </action-widgets>
+ </object>
+</interface>
diff --git a/data/ui/properties.ui b/data/ui/properties.ui
new file mode 100644
index 0000000..8d674d7
--- /dev/null
+++ b/data/ui/properties.ui
@@ -0,0 +1,485 @@
+<?xml version="1.0"?>
+<interface>
+ <requires lib="gtk+" version="2.16"/>
+ <!-- interface-naming-policy toplevel-contextual -->
+ <object class="GtkAdjustment" id="adjustment1">
+ <property name="upper">9999</property>
+ <property name="value">2000</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
+ <object class="GtkListStore" id="model1">
+ <columns>
+ <!-- column-name gchararray -->
+ <column type="gchararray"/>
+ </columns>
+ <data>
+ <row>
+ <col id="0" translatable="yes">Single artist</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">Various artists</col>
+ </row>
+ </data>
+ </object>
+ <object class="GtkDialog" id="properties_dialog">
+ <property name="visible">True</property>
+ <property name="title" translatable="yes">Properties</property>
+ <property name="window_position">center-on-parent</property>
+ <property name="type_hint">dialog</property>
+ <property name="has_separator">False</property>
+ <child internal-child="vbox">
+ <object class="GtkVBox" id="dialog-vbox1">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">5</property>
+ <child>
+ <object class="GtkVBox" id="vbox1">
+ <property name="visible">True</property>
+ <property name="border_width">5</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox2">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkTable" id="table1">
+ <property name="visible">True</property>
+ <property name="n_rows">5</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">6</property>
+ <property name="row_spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">_Title:</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">title_entry</property>
+ </object>
+ <packing>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label8">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="label" translatable="yes">T_racks:</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">track_treeview</property>
+ </object>
+ <packing>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">_Artist:</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">artist_entry</property>
+ </object>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox8">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkComboBox" id="artist_combobox">
+ <property name="visible">True</property>
+ <property name="model">model1</property>
+ <child>
+ <object class="GtkCellRendererText" id="renderer1"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="artist_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label3">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">_Year:</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox3">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkCheckButton" id="year_checkbutton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="focus_on_click">False</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="year_spinbutton">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="adjustment">adjustment1</property>
+ <property name="climb_rate">1</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox9">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkEntry" id="title_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="search_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="tooltip_text" translatable="yes">Search for the remaining data</property>
+ <property name="relief">none</property>
+ <child>
+ <object class="GtkImage" id="image5">
+ <property name="visible">True</property>
+ <property name="stock">gtk-find</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox1">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkVBox" id="vbox4">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkHBox" id="info_box">
+ <child>
+ <object class="GtkHBox" id="hbox5">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkImage" id="image2">
+ <property name="visible">True</property>
+ <property name="stock">gtk-dialog-warning</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="info_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">No album found.</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="navigation_box">
+ <child>
+ <object class="GtkHBox" id="hbox6">
+ <property name="visible">True</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkButton" id="prev_album_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="relief">none</property>
+ <child>
+ <object class="GtkImage" id="image3">
+ <property name="visible">True</property>
+ <property name="stock">gtk-go-back</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="album_label">
+ <property name="visible">True</property>
+ <property name="label">Album %d of %d</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="next_album_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="relief">none</property>
+ <child>
+ <object class="GtkImage" id="image4">
+ <property name="visible">True</property>
+ <property name="stock">gtk-go-forward</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="pack_type">end</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="fill">False</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow1">
+ <property name="width_request">450</property>
+ <property name="height_request">250</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">automatic</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTreeView" id="track_treeview">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="enable_search">False</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child internal-child="action_area">
+ <object class="GtkHButtonBox" id="dialog-action_area1">
+ <property name="visible">True</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="help_button">
+ <property name="label">gtk-help</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ <property name="secondary">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="undo_button">
+ <property name="label">gtk-undo</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="cancel_button">
+ <property name="label">gtk-cancel</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="ok_button">
+ <property name="label">gtk-ok</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-11">help_button</action-widget>
+ <action-widget response="-6">undo_button</action-widget>
+ <action-widget response="-6">cancel_button</action-widget>
+ <action-widget response="-5">ok_button</action-widget>
+ </action-widgets>
+ </object>
+</interface>
diff --git a/data/ui/ripper.ui b/data/ui/ripper.ui
new file mode 100644
index 0000000..21df062
--- /dev/null
+++ b/data/ui/ripper.ui
@@ -0,0 +1,140 @@
+<?xml version="1.0"?>
+<interface>
+ <requires lib="gtk+" version="2.16"/>
+ <!-- interface-naming-policy toplevel-contextual -->
+ <object class="GtkDialog" id="ripper_dialog">
+ <property name="border_width">6</property>
+ <property name="title" translatable="yes">Extracting Tracks</property>
+ <property name="resizable">False</property>
+ <property name="window_position">center-on-parent</property>
+ <property name="type_hint">dialog</property>
+ <property name="has_separator">False</property>
+ <child internal-child="vbox">
+ <object class="GtkVBox" id="dialog-vbox3">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkHBox" id="hbox1">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkVBox" id="vbox1">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkImage" id="image1">
+ <property name="visible">True</property>
+ <property name="pixel_size">64</property>
+ <property name="icon_name">drive-optical</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="padding">12</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox12">
+ <property name="visible">True</property>
+ <property name="border_width">5</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="title_label">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes"><big><b>Extracting tracks</b></big></property>
+ <property name="use_markup">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="padding">6</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox170">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">2</property>
+ <child>
+ <object class="GtkProgressBar" id="progress_progressbar">
+ <property name="width_request">350</property>
+ <property name="visible">True</property>
+ <property name="pulse_step">0.10000000149</property>
+ <property name="ellipsize">start</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="track_label">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="use_markup">True</property>
+ <property name="ellipsize">end</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child internal-child="action_area">
+ <object class="GtkHButtonBox" id="dialog-action_area3">
+ <property name="visible">True</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="cancel_button">
+ <property name="label">gtk-cancel</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-6">cancel_button</action-widget>
+ </action-widgets>
+ </object>
+</interface>
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 92d9aec..3f95a61 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -1,24 +1,24 @@
-# List of source files containing translatable strings.
-# Please keep this file sorted alphabetically.
+# DO NOT EDIT. This file is automatically generated.
+# List of source files which contain translatable strings.
[encoding: UTF-8]
-data/glade/cover_chooser.glade
-data/glade/extract_dialog.glade
-data/glade/format_dialog.glade
-data/glade/preferences.glade
-data/glade/properties.glade
-data/glade/ripper_dialog.glade
-data/glade/properties.glade
-data/GNOME_Goobox.server.in
+copy-n-paste/eggdesktopfile.c
+copy-n-paste/eggdesktopfile.h
+copy-n-paste/eggsmclient.c
+copy-n-paste/eggsmclient.h
+copy-n-paste/eggsmclient-private.h
+copy-n-paste/eggsmclient-xsmp.c
data/goobox.desktop.in.in
data/goobox.schemas.in
+[type: gettext/glade]data/ui/cover-chooser.ui
+[type: gettext/glade]data/ui/extract.ui
+[type: gettext/glade]data/ui/format-options.ui
+[type: gettext/glade]data/ui/preferences.ui
+[type: gettext/glade]data/ui/properties.ui
+[type: gettext/glade]data/ui/ripper.ui
src/actions.c
src/actions.h
src/album-info.c
src/album-info.h
-src/bacon-cd-selection.c
-src/bacon-cd-selection.h
-src/cd-drive.c
-src/cd-drive.h
src/dlg-cover-chooser.c
src/dlg-cover-chooser.h
src/dlg-extract.c
@@ -29,22 +29,15 @@ src/dlg-properties.c
src/dlg-properties.h
src/dlg-ripper.c
src/dlg-ripper.h
-src/eggtrayicon.c
-src/eggtrayicon.h
-src/file-utils.c
-src/file-utils.h
src/gconf-utils.c
src/gconf-utils.h
+src/gio-utils.c
+src/gio-utils.h
src/glib-utils.c
src/glib-utils.h
-src/goo-application.c
-src/goo-application.h
-src/goo-cdrom-bsd.c
-src/goo-cdrom.c
-src/goo-cdrom.h
-src/goo-cdrom-linux.c
-src/goo-cdrom-linux.h
-src/goo-cdrom-solaris.c
+src/gnome-desktop-thumbnail.c
+src/gnome-desktop-thumbnail.h
+src/gnome-thumbnail-pixbuf-utils.c
src/goo-error.c
src/goo-error.h
src/goo-player.c
@@ -53,19 +46,23 @@ src/goo-player-info.c
src/goo-player-info.h
src/goo-stock.c
src/goo-stock.h
-src/goo-window.c
-src/goo-window.h
src/goo-volume-tool-button.c
src/goo-volume-tool-button.h
-src/gth-image-list.c
-src/gth-image-list.h
-src/gthumb-slide.c
-src/gthumb-slide.h
+src/goo-window.c
+src/goo-window.h
+src/gth-user-dir.c
+src/gth-user-dir.h
+src/gth-window.c
+src/gth-window.h
src/gtk-file-chooser-preview.c
+src/gtk-file-chooser-preview.h
src/gtk-utils.c
src/gtk-utils.h
+src/icons/pixbufs.h
src/main.c
src/main.h
+src/metadata.c
+src/metadata.h
src/preferences.c
src/preferences.h
src/track-info.c
diff --git a/po/update-potfiles.sh b/po/update-potfiles.sh
new file mode 100644
index 0000000..e3ffc5d
--- /dev/null
+++ b/po/update-potfiles.sh
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+echo "# DO NOT EDIT. This file is automatically generated."
+echo "# List of source files which contain translatable strings."
+echo "[encoding: UTF-8]"
+files=`find .. \( -name '*.c' -o -name '*.h' -o -name '*.cpp' -o -name '*.ui' -o -name '*.schemas.in' -o -name '*.desktop.in.in' -o -name '*.extension.in.in' \) -printf "%P\n" | sort`
+for f in $files; do
+ case $f in
+ build/*) ;;
+ *.ui) echo "[type: gettext/glade]$f" ;;
+ *) echo $f
+ esac
+done
diff --git a/src/Makefile.am b/src/Makefile.am
index b9cf997..f5663e1 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,77 +1,33 @@
SUBDIRS=icons
-DIST_SUBDIRS=icons
-
-gladedir = $(datadir)/goobox/glade
bin_PROGRAMS = goobox
-if ENABLE_DEPRECATIONS
-DISABLE_DEPRECATED = -DG_DISABLE_DEPRECATED -DGDK_DISABLE_DEPRECATED -DGDK_PIXBUF_DISABLE_DEPRECATED -DGTK_DISABLE_DEPRECATED -DGNOME_VFS_DISABLE_DEPRECATED -DGNOME_DISABLE_DEPRECATED -DBONOBO_DISABLE_DEPRECATED
+if RUN_IN_PLACE
+uidir = $(abs_top_srcdir)/data/ui
else
-DISABLE_DEPRECATED =
+uidir = $(datadir)/goobox/ui
endif
INCLUDES = \
-I$(top_srcdir) \
-I$(top_builddir) \
+ -I$(top_srcdir)/copy-n-paste/ \
-DGOO_PREFIX=\"$(prefix)\" \
-DGOO_SYSCONFDIR=\"$(sysconfdir)\" \
-DGOO_DATADIR=\"$(datadir)\" \
-DGOO_LIBDIR=\"$(libdir)\" \
- -DGLADEDIR=\""$(gladedir)"\" \
- -DGOO_GLADEDIR=\""$(gladedir)"\" \
+ -DGOO_UIDIR=\""$(uidir)"\" \
$(DISABLE_DEPRECATED) \
+ $(DBUS_CFLAGS) \
$(GOO_CFLAGS) \
- $(LIBNOTIFY_CFLAGS) \
- $(HAL_CFLAGS)
-
-gnome_goobox_idl_sources = \
- GNOME_Goobox-stubs.c \
- GNOME_Goobox-skels.c \
- GNOME_Goobox.h \
- GNOME_Goobox-common.c
-
-$(gnome_goobox_idl_sources): gnome_goobox_idl_stamp
-gnome_goobox_idl_stamp: GNOME_Goobox.idl $(ORBIT_IDL)
- $(ORBIT_IDL) $(IDL_INCLUDES) $<
- touch $@
+ $(LIBNOTIFY_CFLAGS)
BUILT_SOURCES = \
goo-marshal.c \
goo-marshal.h
-GTH_MARSHALLERS = \
- gthumb-marshal.c \
- gthumb-marshal.h
-
-GTH_ENUMS = \
- gthumb-enum-types.h \
- gthumb-enum-types.c
-
-GTH_HEADER_FILES = \
- gth-image-list.h
-
-GTH_SOURCES = \
- $(GTH_HEADER_FILES) \
- $(GTH_MARSHALLERS) \
- $(GTH_ENUMS) \
- gth-image-list.c \
- gthumb-slide.c \
- gthumb-slide.h
-
-BACON_SOURCES = \
- cd-drive.c \
- cd-drive.h \
- bacon-cd-selection.c \
- bacon-cd-selection.h \
- dvd_plus_rw_utils.cpp \
- transport.hxx
-
goobox_SOURCES = \
$(BUILT_SOURCES) \
- $(BACON_SOURCES) \
- $(GTH_SOURCES) \
- $(gnome_goobox_idl_sources) \
actions.c \
actions.h \
album-info.c \
@@ -86,36 +42,31 @@ goobox_SOURCES = \
dlg-properties.h \
dlg-ripper.c \
dlg-ripper.h \
- eggtrayicon.c \
- eggtrayicon.h \
- file-utils.c \
- file-utils.h \
gconf-utils.c \
gconf-utils.h \
+ gio-utils.c \
+ gio-utils.h \
glib-utils.c \
glib-utils.h \
- goo-application.c \
- goo-application.h \
- goo-cdrom.c \
- goo-cdrom.h \
- goo-cdrom-bsd.c \
- goo-cdrom-bsd.h \
- goo-cdrom-linux.c \
- goo-cdrom-linux.h \
- goo-cdrom-solaris.c \
- goo-cdrom-solaris.h \
+ gnome-desktop-thumbnail.c \
+ gnome-desktop-thumbnail.h \
+ gnome-thumbnail-pixbuf-utils.c \
goo-error.c \
goo-error.h \
goo-player.c \
goo-player.h \
goo-player-info.c \
goo-player-info.h \
+ goo-stock.c \
+ goo-stock.h \
goo-volume-tool-button.c \
goo-volume-tool-button.h \
goo-window.c \
goo-window.h \
- goo-stock.c \
- goo-stock.h \
+ gth-user-dir.c \
+ gth-user-dir.h \
+ gth-window.c \
+ gth-window.h \
gtk-file-chooser-preview.c \
gtk-file-chooser-preview.h \
gtk-utils.c \
@@ -138,50 +89,20 @@ goo-marshal.c: goo-marshal.list $(GLIB_GENMARSHAL)
echo "#include \"goo-marshal.h\"" > $@ && \
$(GLIB_GENMARSHAL) $< --body --prefix=goo_marshal >> $@
-gthumb-marshal.h: gthumb-marshal.list $(GLIB_GENMARSHAL)
- $(GLIB_GENMARSHAL) $< --header --prefix=gthumb_marshal > $@
-
-gthumb-marshal.c: gthumb-marshal.list gthumb-marshal.h $(GLIB_GENMARSHAL)
- echo "#include \"gthumb-marshal.h\"" > $@ && \
- $(GLIB_GENMARSHAL) $< --body --prefix=gthumb_marshal >> $@
-
-gthumb-enum-types.h: $(GTH_HEADER_FILES) $(GLIB_MKENUMS)
- $(GLIB_MKENUMS) \
- --fhead "#ifndef GTHUMB_ENUM__TYPES_H\n#define GTHUMB_ENUM_TYPES_H\n\n#include <glib-object.h>\n\nG_BEGIN_DECLS\n" \
- --fprod "/* enumerations from \"@filename \" */\n" \
- --vhead "GType @enum_name _get_type (void);\n#define GTH_TYPE_ ENUMSHORT@ (@enum_name _get_type())\n" \
- --ftail "G_END_DECLS\n\n#endif /* GTHUMB_ENUM_TYPES_H */" \
- $^> xgen-$(@F) \
- && (cmp -s xgen-$(@F) gthumb-enum-types.h || cp xgen-$(@F) gthumb-enum-types.h ) \
- && rm -f xgen-$(@F)
-
-gthumb-enum-types.c: $(GTH_HEADER_FILES) gthumb-enum-types.h
- $(GLIB_MKENUMS) \
- --fhead "#include <glib-object.h>\n" \
- --fprod "\n/* enumerations from \"@filename \" */\n#include \"@filename \"" \
- --vhead "GType\n enum_name@_get_type (void)\n{\n static GType etype = 0;\n if (etype == 0) {\n static const G Type@Value values[] = {" \
- --vprod " { @VALUENAME@, \"@VALUENAME \", \"@valuenick \" }," \
- --vtail " { 0, NULL, NULL }\n };\n etype = g_ type@_register_static (\"@EnumName \", values);\n }\n return etype;\n}\n" \
- $^> xgen-$(@F) \
- && (cmp -s xgen-$(@F) gthumb-enum-types.c || cp xgen-$(@F) gthumb-enum-types.c ) \
- && rm -f xgen-$(@F)
-
-goobox_LDADD = \
- $(GOO_LIBS) \
- $(LIBNOTIFY_LIBS) \
- $(HAL_LIBS) \
+goobox_LDADD = \
+ $(top_builddir)/copy-n-paste/libeggsmclient.la \
+ $(DBUS_LIBS) \
+ $(GOO_LIBS) \
+ $(LIBNOTIFY_LIBS) \
$(SYSTEM_LIBS)
-EXTRA_DIST = \
- goo-marshal.list \
- gthumb-marshal.list \
- GNOME_Goobox.idl
+EXTRA_DIST = \
+ goo-marshal.list \
+ gthumb-marshal.list
+
+CLEANFILES = $(BUILT_SOURCES)
-CLEANFILES = \
- $(BUILT_SOURCES) \
- $(GTH_MARSHALLERS) \
- $(GTH_ENUMS) \
- $(gnome_goobox_idl_sources) \
- gnome_goobox_idl_stamp
+dist-hook:
+ cd $(distdir); rm -f $(CLEANFILES)
-include $(top_srcdir)/git.mk
diff --git a/src/actions.c b/src/actions.c
index 0d3eb84..2a4ba2d 100644
--- a/src/actions.c
+++ b/src/actions.c
@@ -21,32 +21,26 @@
*/
#include <config.h>
-#include <gnome.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
-#include <gnome.h>
-#include <libbonobo.h>
-#include <libgnomevfs/gnome-vfs-utils.h>
#include "actions.h"
-#include "main.h"
+#include "dlg-extract.h"
+#include "dlg-preferences.h"
+#include "dlg-properties.h"
#include "gtk-utils.h"
#include "goo-window.h"
-#include "file-utils.h"
#include "gconf-utils.h"
+#include "main.h"
#include "preferences.h"
-#include "dlg-extract.h"
-#include "dlg-preferences.h"
-#include "dlg-properties.h"
void
activate_action_play (GtkAction *action,
gpointer data)
{
- GooWindow *window = data;
- goo_window_play (window);
+ goo_window_play (GOO_WINDOW (data));
}
@@ -54,8 +48,7 @@ void
activate_action_play_selected (GtkAction *action,
gpointer data)
{
- GooWindow *window = data;
- goo_window_play_selected (window);
+ goo_window_play_selected (GOO_WINDOW (data));
}
@@ -63,8 +56,7 @@ void
activate_action_pause (GtkAction *action,
gpointer data)
{
- GooWindow *window = data;
- goo_window_pause (window);
+ goo_window_pause (GOO_WINDOW (data));
}
@@ -72,8 +64,7 @@ void
activate_action_toggle_play (GtkAction *action,
gpointer data)
{
- GooWindow *window = data;
- goo_window_toggle_play (window);
+ goo_window_toggle_play (GOO_WINDOW (data));
}
@@ -81,8 +72,7 @@ void
activate_action_stop (GtkAction *action,
gpointer data)
{
- GooWindow *window = data;
- goo_window_stop (window);
+ goo_window_stop (GOO_WINDOW (data));
}
@@ -90,8 +80,7 @@ void
activate_action_next (GtkAction *action,
gpointer data)
{
- GooWindow *window = data;
- goo_window_next (window);
+ goo_window_next (GOO_WINDOW (data));
}
@@ -99,8 +88,7 @@ void
activate_action_prev (GtkAction *action,
gpointer data)
{
- GooWindow *window = data;
- goo_window_prev (window);
+ goo_window_prev (GOO_WINDOW (data));
}
@@ -108,8 +96,7 @@ void
activate_action_eject (GtkAction *action,
gpointer data)
{
- GooWindow *window = data;
- goo_window_eject (window);
+ goo_window_eject (GOO_WINDOW (data));
}
@@ -117,41 +104,7 @@ void
activate_action_reload (GtkAction *action,
gpointer data)
{
- GooWindow *window = data;
- goo_window_update (window);
-}
-
-
-
-static void
-show_help (GooWindow *window,
- const char *section)
-{
- GError *err = NULL;
-
- gnome_help_display ("goobox", section, &err);
-
- if (err != NULL) {
- GtkWidget *dialog;
-
- dialog = _gtk_message_dialog_new (GTK_WINDOW (window),
- GTK_DIALOG_DESTROY_WITH_PARENT,
- GTK_STOCK_DIALOG_ERROR,
- _("Could not display help"),
- err->message,
- GTK_STOCK_CLOSE, GTK_RESPONSE_OK,
- NULL);
- gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
- g_signal_connect (G_OBJECT (dialog), "response",
- G_CALLBACK (gtk_widget_destroy),
- NULL);
-
- gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
-
- gtk_widget_show (dialog);
-
- g_error_free (err);
- }
+ goo_window_update (GOO_WINDOW (data));
}
@@ -159,8 +112,7 @@ void
activate_action_manual (GtkAction *action,
gpointer data)
{
- GooWindow *window = data;
- show_help (window, NULL);
+ show_help_dialog (GTK_WINDOW (data), NULL);
}
@@ -168,8 +120,7 @@ void
activate_action_shortcuts (GtkAction *action,
gpointer data)
{
- GooWindow *window = data;
- show_help (window, "goobox-shortcuts");
+ show_help_dialog (GTK_WINDOW (data), "goobox-shortcuts");
}
@@ -275,10 +226,8 @@ external_app_watch_func (GPid pid,
gint status,
gpointer data)
{
- GooWindow *window = data;
-
g_spawn_close_pid (pid);
- goo_window_set_hibernate (window, FALSE);
+ goo_window_set_hibernate (GOO_WINDOW (data), FALSE);
}
@@ -321,7 +270,7 @@ activate_action_copy_disc (GtkAction *action,
char *command;
GError *error = NULL;
- command = g_strconcat ("nautilus-cd-burner --source-device=",
+ command = g_strconcat ("brasero --copy=",
goo_player_get_device (goo_window_get_player (window)),
NULL);
@@ -336,6 +285,7 @@ activate_action_copy_disc (GtkAction *action,
&error);
goo_window_set_hibernate (window, FALSE);
}
+
g_free (command);
}
diff --git a/src/album-info.c b/src/album-info.c
index 2b9be01..a0e7c5d 100644
--- a/src/album-info.c
+++ b/src/album-info.c
@@ -21,11 +21,12 @@
*/
#include <config.h>
-#include <gnome.h>
+#include <glib/gi18n.h>
#include <gst/gst.h>
#include "album-info.h"
#include "glib-utils.h"
-#include "file-utils.h"
+#include "gth-user-dir.h"
+
#define MBI_VARIOUS_ARTIST_ID "89ad4ac3-39f7-470e-963a-56509c546377"
@@ -276,7 +277,8 @@ album_info_copy_metadata (AlbumInfo *to_album,
for (scan_to = to_album->tracks, scan_from = from_album->tracks;
scan_to && scan_from;
- scan_to = scan_to->next, scan_from = scan_from->next) {
+ scan_to = scan_to->next, scan_from = scan_from->next)
+ {
TrackInfo *to_track = scan_to->data;
TrackInfo *from_track = scan_from->data;
@@ -289,18 +291,10 @@ static char *
get_cache_path (AlbumInfo *album,
const char *disc_id)
{
- char *path = NULL;
- char *dir;
-
if (disc_id == NULL)
return NULL;
-
- dir = g_build_filename (g_get_home_dir (), ".gnome2", "goobox.d", "albums", NULL);
- if (ensure_dir_exists (dir, 0700) == GNOME_VFS_OK)
- path = g_build_filename (dir, disc_id, NULL);
- g_free (dir);
-
- return path;
+ else
+ return gth_user_dir_get_file (GTH_DIR_DATA, "goobox", "albums", disc_id, NULL);
}
diff --git a/src/dlg-cover-chooser.c b/src/dlg-cover-chooser.c
index afa6ab1..cebbb2c 100644
--- a/src/dlg-cover-chooser.c
+++ b/src/dlg-cover-chooser.c
@@ -22,338 +22,67 @@
#include <config.h>
-
#include <string.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-
#include <gtk/gtk.h>
-#include <libgnome/libgnome.h>
-#include <libgnomevfs/gnome-vfs-ops.h>
-#include <libgnomevfs/gnome-vfs-async-ops.h>
-#include <libgnomevfs/gnome-vfs-utils.h>
-#include <glade/glade.h>
-#include "typedefs.h"
-#include "main.h"
-#include "file-utils.h"
+#include "gio-utils.h"
#include "glib-utils.h"
#include "gtk-utils.h"
#include "goo-window.h"
#include "goo-stock.h"
-#include "gth-image-list.h"
-#include "preferences.h"
-#include "gconf-utils.h"
-#define GLADE_CHOOSER_FILE "cover_chooser.glade"
+#define BUFFER_SIZE 4096
+#define COVER_BACKUP_FILENAME "original_cover.png"
#define MAX_IMAGES 20
-#define READ_TIMEOUT 20
-#define BUFFER_SIZE 612
-#define IMG_PERM 0600
-#define DIR_PERM 0700
-#define THUMB_SIZE 100
-#define THUMB_BORDER 14
-#define QUERY_RESULT "result.html"
-#define ORIGINAL_COVER "original_cover.png"
-
-typedef struct _DialogData DialogData;
-
-typedef void (*FileSavedFunc) (DialogData *data, const char *filename, gboolean success);
-
-struct _DialogData {
- GooWindow *window;
- char *artist;
- char *album;
- GList *url_list;
- GList *current;
- guint load_id, urls, url;
- char *tmpdir;
- GList *tmpfiles;
- char *cover_backup;
- int max_images;
- gboolean autofetching;
-
- FileSavedFunc file_saved_func;
- char *source;
- char *dest;
-
- GnomeVFSAsyncHandle *vfs_handle;
- GnomeVFSResult vfs_result;
-
- GladeXML *gui;
-
- GtkWidget *dialog;
- GtkWidget *image_list;
- GtkWidget *progress_label;
- GtkWidget *ok_button;
- GtkWidget *revert_button;
- GtkWidget *cc_cancel_search_button;
-};
-
-
-static void
-cancel_search (DialogData *data)
-{
- if (data->vfs_handle != NULL) {
- gnome_vfs_async_cancel (data->vfs_handle);
- data->vfs_handle = NULL;
- }
-
- if (data->load_id != 0) {
- g_source_remove (data->load_id);
- data->load_id = 0;
- }
-}
-
-
-static void
-destroy_cb (GtkWidget *widget,
- DialogData *data)
-{
- cancel_search (data);
-
- if (data->tmpdir != NULL) {
- g_list_foreach (data->tmpfiles, (GFunc) gnome_vfs_unlink, NULL);
- path_list_free (data->tmpfiles);
-
- gnome_vfs_remove_directory (data->tmpdir);
- g_free (data->tmpdir);
- }
-
- g_free (data->dest);
- g_free (data->source);
- g_free (data->album);
- g_free (data->artist);
- path_list_free (data->url_list);
- if (data->gui != NULL)
- g_object_unref (data->gui);
- g_free (data);
-}
-
-
-/* -- copy_file_from_url() -- */
-
-
-static int
-copy_progress_update_cb (GnomeVFSAsyncHandle *handle,
- GnomeVFSXferProgressInfo *info,
- gpointer callback_data)
-{
- DialogData *data = callback_data;
-
- if (info->status != GNOME_VFS_XFER_PROGRESS_STATUS_OK) {
- data->vfs_result = info->status;
- return FALSE;
- }
- if (info->phase == GNOME_VFS_XFER_PHASE_COMPLETED) {
- debug (DEBUG_INFO, "COMPLETED");
-
- if (data->file_saved_func != NULL)
- (*data->file_saved_func) (data, data->dest, (data->vfs_result == GNOME_VFS_OK));
- }
-
- return TRUE;
-}
-
-
-static void
-copy_file_from_url (DialogData *data,
- const char *uri_source,
- const char *uri_dest,
- FileSavedFunc file_saved_func)
-{
- GList *src_uri_list, *dest_uri_list;
- GnomeVFSXferOptions xfer_options;
- GnomeVFSXferErrorMode xfer_error_mode;
- GnomeVFSXferOverwriteMode overwrite_mode;
- GnomeVFSResult result;
-
- data->file_saved_func = file_saved_func;
-
- g_free (data->dest);
- data->dest = g_strdup (uri_dest);
-
- src_uri_list = g_list_prepend (NULL, gnome_vfs_uri_new (uri_source));
- dest_uri_list = g_list_prepend (NULL, gnome_vfs_uri_new (uri_dest));
-
- xfer_options = GNOME_VFS_XFER_DEFAULT;
- xfer_error_mode = GNOME_VFS_XFER_ERROR_MODE_ABORT;
- overwrite_mode = GNOME_VFS_XFER_OVERWRITE_MODE_REPLACE;
-
- data->vfs_result = GNOME_VFS_OK;
-
- result = gnome_vfs_async_xfer (&data->vfs_handle,
- src_uri_list,
- dest_uri_list,
- xfer_options,
- xfer_error_mode,
- overwrite_mode,
- GNOME_VFS_PRIORITY_DEFAULT,
- copy_progress_update_cb,
- data,
- NULL,
- NULL);
-
- g_list_foreach (src_uri_list, (GFunc) gnome_vfs_uri_unref, NULL);
- g_list_free (src_uri_list);
-
- g_list_foreach (dest_uri_list, (GFunc) gnome_vfs_uri_unref, NULL);
- g_list_free (dest_uri_list);
-
- /**/
-
- if (result != GNOME_VFS_OK) {
- if (file_saved_func != NULL)
- (*file_saved_func) (data, uri_dest, FALSE);
- }
-}
+#define GET_WIDGET(x) _gtk_builder_get_widget (data->builder, (x))
-static void load_current_url (DialogData *data);
-
-
-static gboolean
-load_next_url (gpointer callback_data)
-{
- DialogData *data = callback_data;
-
- g_source_remove (data->load_id);
- data->load_id = 0;
-
- data->current = data->current->next;
- load_current_url (data);
-
- return FALSE;
-}
-
-
-static void
-append_image (DialogData *data,
- const char *filename)
-{
- GdkPixbuf *image;
- int pos;
-
- image = gdk_pixbuf_new_from_file_at_size (filename,
- THUMB_SIZE, THUMB_SIZE,
- NULL);
- if (image == NULL)
- return;
-
- pos = gth_image_list_append (GTH_IMAGE_LIST (data->image_list),
- image,
- filename,
- NULL);
- gth_image_list_set_image_data_full (GTH_IMAGE_LIST (data->image_list),
- pos,
- g_strdup (filename),
- g_free);
- g_object_unref (image);
-}
-
-
-static void
-image_saved_cb (DialogData *data,
- const char *filename,
- gboolean success)
-{
- if (success) {
- char *tmpfile = g_strdup (filename);
-
- debug (DEBUG_INFO, "LOAD IMAGE: %s\n", tmpfile);
-
- data->tmpfiles = g_list_prepend (data->tmpfiles, tmpfile);
- append_image (data, tmpfile);
- }
-
- data->load_id = g_idle_add (load_next_url, data);
-}
-
-
-static void
-update_progress_label (DialogData *data)
-{
- char *text;
-
- if (data->url < data->urls)
- text = g_strdup_printf (_("%u, loading image: %u"),
- data->urls,
- data->url + 1);
- else
- text = g_strdup_printf ("%u", data->urls);
- gtk_label_set_text (GTK_LABEL (data->progress_label), text);
- g_free (text);
-}
-
-
-static void
-search_completed (DialogData *data)
-{
- char *text;
-
- gtk_widget_set_sensitive (data->cc_cancel_search_button, FALSE);
-
- text = g_strdup_printf ("%u", data->urls);
- gtk_label_set_text (GTK_LABEL (data->progress_label), text);
- g_free (text);
-}
-
-
-static void
-load_current_url (DialogData *data)
-{
- char *url, *dest;
- char *filename;
-
- update_progress_label (data);
-
- if ((data->current == NULL) || (data->url >= data->max_images)) {
- search_completed (data);
- return;
- }
-
- url = data->current->data;
-
- debug (DEBUG_INFO, "LOAD %s\n", url);
-
- filename = g_strdup_printf ("%d.png", data->url);
- dest = g_build_filename (data->tmpdir, filename, NULL);
- g_free (filename);
-
- copy_file_from_url (data, url, dest, image_saved_cb);
- data->url++;
-}
-
-
-static void
-start_loading_images (DialogData *data)
-{
- gth_image_list_set_no_image_text (GTH_IMAGE_LIST (data->image_list),
- _("Loading images"));
-
- data->current = data->url_list;
- load_current_url (data);
-}
+enum {
+ URL_COLUMN,
+ IMAGE_COLUMN,
+ N_COLUMNS
+};
-static gboolean
-make_file_list_from_search_result (DialogData *data,
- const char *filename)
+typedef struct {
+ GooWindow *window;
+ char *artist;
+ char *album;
+ GtkBuilder *builder;
+ GtkWidget *dialog;
+ GtkWidget *icon_view;
+ GdkPixbuf *cover_backup;
+ GList *file_list;
+ int total_files;
+ GList *current_file;
+ int loaded_files;
+ GCancellable *cancellable;
+ gboolean searching;
+ gboolean destroy;
+} DialogData;
+
+
+static GList *
+make_file_list_from_search_result (void *buffer,
+ gsize count,
+ int max_files)
{
- int fd, n;
- char buf[BUFFER_SIZE];
- int buf_offset = 0;
- GString *partial_url;
- gboolean done = FALSE;
- int urls = 0;
-
- fd = open (filename, O_RDONLY);
- if (fd == 0)
- return FALSE;
-
+ GList *list = NULL;
+ int n_files = 0;
+ gboolean done = FALSE;
+ GInputStream *stream;
+ gssize n;
+ char buf[BUFFER_SIZE];
+ int buf_offset = 0;
+ GString *partial_url;
+
+ stream = g_memory_input_stream_new_from_data (buffer, count, NULL);
partial_url = NULL;
- while ((n = read (fd, buf + buf_offset, BUFFER_SIZE - buf_offset - 1)) > 0) {
+ while ((n = g_input_stream_read (stream,
+ buf + buf_offset,
+ BUFFER_SIZE - buf_offset - 1,
+ NULL,
+ NULL)) > 0)
+ {
const char *prefix = "/images?q=tbn:";
int prefix_len = strlen (prefix);
char *url_start;
@@ -361,20 +90,20 @@ make_file_list_from_search_result (DialogData *data,
buf[buf_offset+n] = 0;
- if (partial_url == NULL)
+ if (partial_url == NULL)
url_start = strstr (buf, prefix);
else
url_start = buf;
while (url_start != NULL) {
char *url_end;
-
+
url_end = strstr (url_start, " ");
-
+
if (url_end == NULL) {
if (partial_url == NULL)
partial_url = g_string_new (url_start);
- else
+ else
g_string_append (partial_url, url_start);
url_start = NULL;
copy_tail = FALSE;
@@ -383,24 +112,23 @@ make_file_list_from_search_result (DialogData *data,
char *url_tail = g_strndup (url_start, url_end - url_start);
char *url;
char *complete_url;
-
+
if (partial_url != NULL) {
g_string_append (partial_url, url_tail);
g_free (url_tail);
url = partial_url->str;
g_string_free (partial_url, FALSE);
partial_url = NULL;
- }
- else
+ }
+ else
url = url_tail;
-
+
complete_url = g_strconcat ("http://images.google.com", url, NULL);
g_free (url);
- data->url_list = g_list_prepend (data->url_list, complete_url);
- urls++;
-
- if (urls >= data->max_images) {
+ list = g_list_prepend (list, complete_url);
+ n_files++;
+ if (n_files >= max_files) {
done = TRUE;
break;
}
@@ -414,219 +142,318 @@ make_file_list_from_search_result (DialogData *data,
if (copy_tail) {
prefix_len = MIN (prefix_len, buf_offset + n);
- strncpy (buf,
- buf + buf_offset + n - prefix_len,
+ strncpy (buf,
+ buf + buf_offset + n - prefix_len,
prefix_len);
buf_offset = prefix_len;
- }
- else
+ }
+ else
buf_offset = 0;
}
if (partial_url != NULL)
g_string_free (partial_url, TRUE);
- close (fd);
- data->tmpfiles = g_list_prepend (data->tmpfiles, g_strdup (filename));
-
- data->url_list = g_list_reverse (data->url_list);
- data->urls = urls;
- data->url = 0;
+ g_object_unref (stream);
- return (data->url_list != NULL);
+ return g_list_reverse (list);
}
-static void
-search_result_saved_cb (DialogData *data,
- const char *filename,
- gboolean success)
-{
- if (! success) {
- _gtk_error_dialog_run (GTK_WINDOW (data->dialog),
- _("Could not search for a cover on Internet"),
- "%s",
- gnome_vfs_result_to_string (data->vfs_result));
- return;
- }
-
- if (! make_file_list_from_search_result (data, filename)) {
- gth_image_list_set_no_image_text (GTH_IMAGE_LIST (data->image_list),
- _("No image found"));
- return;
- }
-
- start_loading_images (data);
-}
-
-
-static char*
-get_query (DialogData *data)
+static char *
+get_query (const char *album,
+ const char *artist)
{
char *s, *e, *q;
- s = g_strdup_printf ("%s %s", data->album, data->artist);
- e = gnome_vfs_escape_string (s);
+ s = g_strdup_printf ("%s %s", album, artist);
+ e = g_uri_escape_string (s, G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, FALSE);
q = g_strconcat ("http://images.google.com/images?q=", e, NULL);
+
g_free (e);
g_free (s);
-
+
return q;
}
-static gboolean
-start_searching (gpointer callback_data)
+/* -- dlg_cover_chooser -- */
+
+
+static void
+destroy_cb (GtkWidget *widget,
+ DialogData *data)
{
- DialogData *data = callback_data;
- char *query;
- char *dest;
+ if (data->searching) {
+ data->destroy = TRUE;
+ g_cancellable_cancel (data->cancellable);
+ return;
+ }
+
+ g_object_unref (data->cancellable);
+ _g_string_list_free (data->file_list);
+ _g_object_unref (data->cover_backup);
+ g_object_unref (data->builder);
+ g_free (data->album);
+ g_free (data->artist);
+ g_free (data);
+}
- g_source_remove (data->load_id);
- data->load_id = 0;
- query = get_query (data);
- dest = g_build_filename (data->tmpdir, QUERY_RESULT, NULL);
- copy_file_from_url (data, query, dest, search_result_saved_cb);
+static void
+search_completed (DialogData *data)
+{
+ char *text;
+
+ data->searching = FALSE;
+ gtk_widget_set_sensitive (GET_WIDGET ("cancel_search_button"), FALSE);
+ text = g_strdup_printf ("%u", data->total_files);
+ gtk_label_set_text (GTK_LABEL (GET_WIDGET ("progress_label")), text);
- g_free (dest);
- g_free (query);
+ g_free (text);
- return FALSE;
+ if (data->destroy)
+ destroy_cb (NULL, data);
}
-/* callbacks */
+static void load_current_file (DialogData *data);
-/* called when the "help" button is clicked. */
static void
-help_cb (GtkWidget *widget,
- DialogData *data)
+search_image_data_ready_cb (void *buffer,
+ gsize count,
+ GError *error,
+ gpointer user_data)
{
- GError *err;
+ DialogData *data = user_data;
- err = NULL;
- gnome_help_display ("goobox", "search_cover_on_internet", &err);
-
- if (err != NULL) {
- GtkWidget *dialog;
-
- dialog = gtk_message_dialog_new (GTK_WINDOW (data->dialog),
- 0,
- GTK_MESSAGE_ERROR,
- GTK_BUTTONS_CLOSE,
- _("Could not display help: %s"),
- err->message);
-
- g_signal_connect (G_OBJECT (dialog), "response",
- G_CALLBACK (gtk_widget_destroy),
- NULL);
-
- gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
-
- gtk_widget_show (dialog);
-
- g_error_free (err);
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+ search_completed (data);
+ return;
}
+
+ if (error == NULL) {
+ GInputStream *stream;
+ GdkPixbuf *image;
+
+ stream = g_memory_input_stream_new_from_data (buffer, count, NULL);
+ image = gdk_pixbuf_new_from_stream (stream, NULL, &error);
+ if (image != NULL) {
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ char *url;
+
+ model = gtk_icon_view_get_model (GTK_ICON_VIEW (data->icon_view));
+ gtk_list_store_append (GTK_LIST_STORE (model), &iter);
+ url = (char *) data->current_file->data;
+ gtk_list_store_set (GTK_LIST_STORE (model), &iter,
+ URL_COLUMN, url,
+ IMAGE_COLUMN, image,
+ -1);
+
+ g_object_unref (image);
+ }
+ }
+
+ data->loaded_files++;
+ data->current_file = data->current_file->next;
+ load_current_file (data);
+}
+
+
+static void
+update_progress_label (DialogData *data)
+{
+ char *text;
+
+ if (data->loaded_files < data->total_files)
+ text = g_strdup_printf (_("%u, loading image: %u"),
+ data->total_files,
+ data->loaded_files + 1);
+ else
+ text = g_strdup_printf ("%u", data->total_files);
+ gtk_label_set_text (GTK_LABEL (GET_WIDGET ("progress_label")), text);
+
+ g_free (text);
}
static void
-revert_cb (GtkWidget *widget,
- DialogData *data)
+load_current_file (DialogData *data)
{
- char *original_cover;
+ char *url;
+ GFile *source;
+
+ update_progress_label (data);
- if (data->cover_backup == NULL)
+ if (data->current_file == NULL) {
+ search_completed (data);
return;
+ }
+
+ url = data->current_file->data;
+
+ debug (DEBUG_INFO, "LOADING %s\n", url);
- original_cover = goo_window_get_cover_filename (data->window);
- file_copy (data->cover_backup, original_cover);
- goo_window_update_cover (data->window);
+ source = g_file_new_for_uri (url);
+ g_load_file_async (source,
+ G_PRIORITY_DEFAULT,
+ data->cancellable,
+ search_image_data_ready_cb,
+ data);
+
+ g_object_unref (source);
}
-/* called when the "ok" button is clicked. */
static void
-ok_cb (GtkWidget *widget,
- DialogData *data)
+start_loading_files (DialogData *data)
{
- GthImageList *list = GTH_IMAGE_LIST (data->image_list);
- GList *selection;
+ gtk_list_store_clear (GTK_LIST_STORE (gtk_icon_view_get_model (GTK_ICON_VIEW (data->icon_view))));
+ data->total_files = g_list_length (data->file_list);
+ data->current_file = data->file_list;
+ data->loaded_files = 0;
+ load_current_file (data);
+}
- selection = gth_image_list_get_selection (list);
- if (selection != NULL) {
- char *src = selection->data;
- debug (DEBUG_INFO, "SET COVER: %s\n", src);
+static void
+search_query_ready_cb (void *buffer,
+ gsize count,
+ GError *error,
+ gpointer user_data)
+{
+ DialogData *data = user_data;
- if (! data->autofetching
- || goo_window_get_current_cd_autofetch (data->window))
- goo_window_set_cover_image (data->window, src);
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+ search_completed (data);
+ return;
+ }
- g_list_free (selection);
+ if (error != NULL) {
+ _gtk_error_dialog_from_gerror_show (GTK_WINDOW (data->dialog),
+ _("Could not search for a cover on Internet"),
+ &error);
+ search_completed (data);
+ return;
}
+
+ data->file_list = make_file_list_from_search_result (buffer, count, MAX_IMAGES);
+ start_loading_files (data);
}
static void
-image_list_selection_changed_cb (GthImageList *list,
- DialogData *data)
+start_searching (DialogData *data)
{
- GList *selection;
+ char *query;
+ GFile *file;
+
+ data->searching = TRUE;
+ g_cancellable_reset (data->cancellable);
+ gtk_widget_set_sensitive (GET_WIDGET ("cancel_search_button"), TRUE);
+
+ query = get_query (data->album, data->artist);
+ file = g_file_new_for_uri (query);
+ g_load_file_async (file,
+ G_PRIORITY_DEFAULT,
+ data->cancellable,
+ search_query_ready_cb,
+ data);
+
+ g_object_unref (file);
+ g_free (query);
+}
- selection = gth_image_list_get_selection (list);
- gtk_widget_set_sensitive (data->ok_button, selection != NULL);
- g_list_free (selection);
+
+static void
+help_button_clicked_cb (GtkWidget *widget,
+ DialogData *data)
+{
+ show_help_dialog (GTK_WINDOW (data->window), "search_cover_on_internet");
}
static void
-image_list_item_activated_cb (GthImageList *list,
- int pos,
- DialogData *data)
+revert_button_clicked_cb (GtkWidget *widget,
+ DialogData *data)
{
- ok_cb (NULL, data);
+ goo_window_set_cover_image_from_pixbuf (data->window, data->cover_backup);
}
static void
-backup_cover_image (DialogData *data)
+ok_button_clicked_cb (GtkWidget *widget,
+ DialogData *data)
{
- GnomeVFSURI *uri;
- char *original_cover;
- char *cover_backup;
+ GList *list;
- original_cover = goo_window_get_cover_filename (data->window);
- if (original_cover == NULL) {
- gtk_widget_set_sensitive (data->revert_button, FALSE);
- return;
- }
+ list = gtk_icon_view_get_selected_items (GTK_ICON_VIEW (data->icon_view));
+ if (list != NULL) {
+ GtkTreePath *path;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
- uri = new_uri_from_path (original_cover);
- if (uri == NULL) {
- gtk_widget_set_sensitive (data->revert_button, FALSE);
- return;
+ path = list->data;
+ model = gtk_icon_view_get_model (GTK_ICON_VIEW (data->icon_view));
+ if (gtk_tree_model_get_iter (model, &iter, path)) {
+ GdkPixbuf *image;
+
+ gtk_tree_model_get (model, &iter, IMAGE_COLUMN, &image, -1);
+ goo_window_set_cover_image_from_pixbuf (data->window, image);
+
+ g_object_unref (image);
+ }
+
+ g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
+ g_list_free (list);
}
- gtk_widget_set_sensitive (data->revert_button, gnome_vfs_uri_exists (uri));
- gnome_vfs_uri_unref (uri);
+}
+
+
+static void
+icon_view_selection_changed_cb (GtkIconView *icon_view,
+ DialogData *data)
+{
+ GList *list;
- cover_backup = g_build_filename (data->tmpdir, ORIGINAL_COVER, NULL);
- file_copy (original_cover, cover_backup);
- g_free (original_cover);
+ list = gtk_icon_view_get_selected_items (GTK_ICON_VIEW (data->icon_view));
+ gtk_widget_set_sensitive (GET_WIDGET ("ok_button"), list != NULL);
- data->tmpfiles = g_list_prepend (data->tmpfiles, cover_backup);
- data->cover_backup = cover_backup;
+ g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
+ g_list_free (list);
}
static void
-cancel_search_button_clicked_cb (GtkWidget *widget,
+icon_view_item_activated_cb (GtkIconView *icon_view,
+ GtkTreePath *path,
+ DialogData *data)
+{
+ ok_button_clicked_cb (NULL, data);
+}
+
+
+static void
+cancel_search_button_clicked_cb (GtkWidget *widget,
DialogData *data)
{
- cancel_search (data);
- search_completed (data);
+ g_cancellable_cancel (data->cancellable);
+}
+
+
+static void
+backup_cover_image (DialogData *data)
+{
+ char *cover_filename;
+
+ cover_filename = goo_window_get_cover_filename (data->window);
+ gtk_widget_set_sensitive (GET_WIDGET ("revert_button"), cover_filename != NULL);
+ if (cover_filename != NULL)
+ data->cover_backup = gdk_pixbuf_new_from_file (cover_filename, NULL);
+
+ g_free (cover_filename);
}
@@ -635,50 +462,49 @@ dlg_cover_chooser (GooWindow *window,
const char *album,
const char *artist)
{
- DialogData *data;
- GtkWidget *scrolled_window;
- GtkWidget *btn_cancel;
- GtkWidget *btn_help;
- GtkWidget *image;
+ DialogData *data;
+ GtkListStore *model;
+ GtkCellRenderer *renderer;
+ GtkWidget *image;
data = g_new0 (DialogData, 1);
data->window = window;
- data->gui = glade_xml_new (GOO_GLADEDIR "/" GLADE_CHOOSER_FILE, NULL, NULL);
- if (!data->gui) {
- g_warning ("Could not find " GLADE_CHOOSER_FILE "\n");
- g_free (data);
- return;
- }
-
+ data->builder = _gtk_builder_new_from_file ("cover-chooser.ui", "");
data->album = g_strdup (album);
data->artist = g_strdup (artist);
- data->max_images = MAX_IMAGES;
+ data->cancellable = g_cancellable_new ();
/* Get the widgets. */
- data->dialog = glade_xml_get_widget (data->gui, "cover_chooser_dialog");
- scrolled_window = glade_xml_get_widget (data->gui, "image_list_scrolledwindow");
- data->progress_label = glade_xml_get_widget (data->gui, "progress_label");
- data->ok_button = glade_xml_get_widget (data->gui, "cc_okbutton");
- data->revert_button = glade_xml_get_widget (data->gui, "cc_revertbutton");
- btn_cancel = glade_xml_get_widget (data->gui, "cc_cancelbutton");
- btn_help = glade_xml_get_widget (data->gui, "cc_helpbutton");
+ data->dialog = GET_WIDGET ("cover_chooser_dialog");
+
+ model = gtk_list_store_new (N_COLUMNS,
+ G_TYPE_STRING,
+ GDK_TYPE_PIXBUF);
+ data->icon_view = gtk_icon_view_new_with_model (GTK_TREE_MODEL (model));
+ g_object_unref (model);
- data->cc_cancel_search_button = glade_xml_get_widget (data->gui, "cc_cancel_search_button");
+ renderer = gtk_cell_renderer_pixbuf_new ();
+ g_object_set (renderer, "follow-state", TRUE, NULL);
+ gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (data->icon_view),
+ renderer,
+ TRUE);
+ gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (data->icon_view),
+ renderer,
+ "pixbuf", IMAGE_COLUMN,
+ NULL);
- data->image_list = gth_image_list_new (THUMB_SIZE + THUMB_BORDER);
- gth_image_list_set_view_mode (GTH_IMAGE_LIST (data->image_list),
- GTH_VIEW_MODE_VOID);
- gth_image_list_set_selection_mode (GTH_IMAGE_LIST (data->image_list),
- GTK_SELECTION_SINGLE);
- gtk_container_add (GTK_CONTAINER (scrolled_window), data->image_list);
+ gtk_widget_show (data->icon_view);
+ gtk_container_add (GTK_CONTAINER (GET_WIDGET ("icon_view_scrolledwindow")), data->icon_view);
/* Set widgets data. */
- gtk_widget_set_sensitive (data->ok_button, FALSE);
+ backup_cover_image (data);
+
+ gtk_widget_set_sensitive (GET_WIDGET ("ok_button"), FALSE);
image = gtk_image_new_from_stock (GOO_STOCK_RESET, GTK_ICON_SIZE_BUTTON);
- g_object_set (data->revert_button,
+ g_object_set (GET_WIDGET ("revert_button"),
"use_stock", TRUE,
"label", GOO_STOCK_RESET,
"image", image,
@@ -690,31 +516,31 @@ dlg_cover_chooser (GooWindow *window,
"destroy",
G_CALLBACK (destroy_cb),
data);
- g_signal_connect_swapped (G_OBJECT (btn_cancel),
+ g_signal_connect_swapped (GET_WIDGET ("cancel_button"),
"clicked",
G_CALLBACK (gtk_widget_destroy),
G_OBJECT (data->dialog));
- g_signal_connect (G_OBJECT (btn_help),
+ g_signal_connect (GET_WIDGET ("help_button"),
"clicked",
- G_CALLBACK (help_cb),
+ G_CALLBACK (help_button_clicked_cb),
data);
- g_signal_connect (G_OBJECT (data->ok_button),
+ g_signal_connect (GET_WIDGET ("ok_button"),
"clicked",
- G_CALLBACK (ok_cb),
+ G_CALLBACK (ok_button_clicked_cb),
data);
- g_signal_connect (G_OBJECT (data->revert_button),
+ g_signal_connect (GET_WIDGET ("revert_button"),
"clicked",
- G_CALLBACK (revert_cb),
+ G_CALLBACK (revert_button_clicked_cb),
data);
- g_signal_connect (G_OBJECT (data->image_list),
- "selection_changed",
- G_CALLBACK (image_list_selection_changed_cb),
+ g_signal_connect (G_OBJECT (data->icon_view),
+ "selection-changed",
+ G_CALLBACK (icon_view_selection_changed_cb),
data);
- g_signal_connect (G_OBJECT (data->image_list),
- "item_activated",
- G_CALLBACK (image_list_item_activated_cb),
+ g_signal_connect (G_OBJECT (data->icon_view),
+ "item-activated",
+ G_CALLBACK (icon_view_item_activated_cb),
data);
- g_signal_connect (G_OBJECT (data->cc_cancel_search_button),
+ g_signal_connect (GET_WIDGET ("cancel_search_button"),
"clicked",
G_CALLBACK (cancel_search_button_clicked_cb),
data);
@@ -723,83 +549,72 @@ dlg_cover_chooser (GooWindow *window,
gtk_window_set_transient_for (GTK_WINDOW (data->dialog), GTK_WINDOW (window));
gtk_window_set_modal (GTK_WINDOW (data->dialog), TRUE);
- gtk_widget_show_all (data->dialog);
+ gtk_widget_show (data->dialog);
- /**/
+ start_searching (data);
+}
- data->tmpdir = g_strdup (get_temp_work_dir ());
- ensure_dir_exists (data->tmpdir, DIR_PERM);
- backup_cover_image (data);
- gth_image_list_set_no_image_text (GTH_IMAGE_LIST (data->image_list),
- _("Searching images..."));
+/* -- auto fetch functions -- */
- data->load_id = g_idle_add (start_searching, data);
-}
+
+typedef struct {
+ GooWindow *window;
+} FetchData;
static void
-auto_fetch__image_saved_cb (DialogData *data,
- const char *filename,
- gboolean success)
+fetch_data_free (FetchData *data)
{
- if (success) {
- char *tmpfile = g_strdup (filename);
-
- debug (DEBUG_INFO, "LOAD IMAGE: %s\n", tmpfile);
- data->tmpfiles = g_list_prepend (data->tmpfiles, tmpfile);
-
- goo_window_set_cover_image (data->window, get_path_from_uri (filename));
- }
-
- destroy_cb (NULL, data);
+ g_free (data);
}
static void
-auto_fetch__search_result_saved_cb (DialogData *data,
- const char *filename,
- gboolean success)
+fetch_image_data_ready_cb (void *buffer,
+ gsize count,
+ GError *error,
+ gpointer user_data)
{
- if (! success || ! make_file_list_from_search_result (data, filename))
- return;
-
- if (data->url_list != NULL) {
- char *filename, *url, *dest;
+ FetchData *data = user_data;
- url = (char*) data->url_list->data;
-
- filename = g_strdup_printf ("%d.png", data->url);
- dest = g_build_filename (data->tmpdir, filename, NULL);
- g_free (filename);
-
- copy_file_from_url (data, url, dest, auto_fetch__image_saved_cb)
-;
- g_free (dest);
- }
- else
- destroy_cb (NULL, data);
+ if (error == NULL)
+ goo_window_set_cover_image_from_data (data->window, buffer, count);
+ fetch_data_free (data);
}
-static gboolean
-auto_fetch_from_name__start_searching (gpointer callback_data)
+static void
+query_ready_cb (void *buffer,
+ gsize count,
+ GError *error,
+ gpointer user_data)
{
- DialogData *data = callback_data;
- char *query;
- char *dest;
+ FetchData *data = user_data;
+ GList *list;
- g_source_remove (data->load_id);
- data->load_id = 0;
+ if (error != NULL) {
+ fetch_data_free (data);
+ return;
+ }
- query = get_query (data);
- dest = g_build_filename (data->tmpdir, QUERY_RESULT, NULL);
- copy_file_from_url (data, query, dest, auto_fetch__search_result_saved_cb);
+ list = make_file_list_from_search_result (buffer, count, 1);
+ if (list != NULL) {
+ GFile *file;
- g_free (dest);
- g_free (query);
+ file = g_file_new_for_uri ((char *) list->data);
+ g_load_file_async (file,
+ G_PRIORITY_DEFAULT,
+ NULL,
+ fetch_image_data_ready_cb,
+ data);
- return FALSE;
+ g_object_unref (file);
+ }
+ else
+ fetch_data_free (data);
+
+ _g_string_list_free (list);
}
@@ -808,36 +623,23 @@ fetch_cover_image_from_name (GooWindow *window,
const char *album,
const char *artist)
{
- DialogData *data;
+ FetchData *data;
+ char *url;
+ GFile *file;
- data = g_new0 (DialogData, 1);
+ data = g_new0 (FetchData, 1);
data->window = window;
- data->album = g_strdup (album);
- data->artist = g_strdup (artist);
- data->max_images = 1;
- data->autofetching = TRUE;
-
- data->tmpdir = get_temp_work_dir ();
- ensure_dir_exists (data->tmpdir, DIR_PERM);
-
- data->load_id = g_idle_add (auto_fetch_from_name__start_searching, data);
-}
-
-
-static gboolean
-auto_fetch_from_asin__start_searching (gpointer callback_data)
-{
- DialogData *data = callback_data;
- char *dest;
- g_source_remove (data->load_id);
- data->load_id = 0;
+ url = get_query (album, artist);
+ file = g_file_new_for_uri (url);
+ g_load_file_async (file,
+ G_PRIORITY_DEFAULT,
+ NULL,
+ query_ready_cb,
+ data);
- dest = g_strconcat ("file://", data->tmpdir, "/1.jpg", NULL);
- copy_file_from_url (data, data->source, dest, auto_fetch__image_saved_cb);
- g_free (dest);
-
- return FALSE;
+ g_object_unref (file);
+ g_free (url);
}
@@ -845,16 +647,20 @@ void
fetch_cover_image_from_asin (GooWindow *window,
const char *asin)
{
- DialogData *data;
+ FetchData *data;
+ char *url;
+ GFile *file;
- data = g_new0 (DialogData, 1);
+ data = g_new0 (FetchData, 1);
data->window = window;
- data->max_images = 1;
- data->autofetching = TRUE;
-
- data->tmpdir = g_strdup (get_temp_work_dir ());
- ensure_dir_exists (data->tmpdir, DIR_PERM);
-
- data->source = g_strdup_printf ("http://images.amazon.com/images/P/%s.01._SCLZZZZZZZ_.jpg", asin);
- data->load_id = g_idle_add (auto_fetch_from_asin__start_searching, data);
+ url = g_strdup_printf ("http://images.amazon.com/images/P/%s.01._SCLZZZZZZZ_.jpg", asin);
+ file = g_file_new_for_uri (url);
+ g_load_file_async (file,
+ G_PRIORITY_DEFAULT,
+ NULL,
+ fetch_image_data_ready_cb,
+ data);
+
+ g_object_unref (file);
+ g_free (url);
}
diff --git a/src/dlg-extract.c b/src/dlg-extract.c
index 9635a76..1ea4f15 100644
--- a/src/dlg-extract.c
+++ b/src/dlg-extract.c
@@ -21,141 +21,81 @@
*/
#include <config.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-
#include <gtk/gtk.h>
-#include <libgnome/libgnome.h>
-#include <libgnomeui/gnome-dialog.h>
-#include <libgnomeui/gnome-dialog-util.h>
-#include <libgnomeui/gnome-propertybox.h>
-#include <libgnomeui/gnome-pixmap.h>
-#include <libgnomevfs/gnome-vfs-utils.h>
-#include <glade/glade.h>
#include <gst/gst.h>
-#include "typedefs.h"
-#include "main.h"
+#include "dlg-extract.h"
+#include "dlg-ripper.h"
#include "gconf-utils.h"
-#include "gtk-utils.h"
-#include "typedefs.h"
+#include "goo-player.h"
#include "goo-stock.h"
-#include "goo-window.h"
-#include "preferences.h"
+#include "gtk-utils.h"
#include "track-info.h"
-#include "goo-player.h"
-#include "dlg-ripper.h"
-#include "file-utils.h"
+#include "typedefs.h"
+
+
+#define GET_WIDGET(x) _gtk_builder_get_widget (data->builder, (x))
-#define GLADE_EXTRACT_FILE "extract_dialog.glade"
typedef struct {
GooWindow *window;
- GooPlayer *player_cd;
+ GooPlayer *player;
GList *tracks;
GList *selected_tracks;
-
- GladeXML *gui;
-
+ GtkBuilder *builder;
GtkWidget *dialog;
- GtkWidget *e_alltrack_radiobutton;
- GtkWidget *e_selected_radiobutton;
} DialogData;
-/* called when the main dialog is closed. */
static void
-destroy_cb (GtkWidget *widget,
- DialogData *data)
+dialog_destroy_cb (GtkWidget *widget,
+ DialogData *data)
{
track_list_free (data->selected_tracks);
track_list_free (data->tracks);
- g_object_unref (data->player_cd);
- g_object_unref (data->gui);
+ g_object_unref (data->player);
+ g_object_unref (data->builder);
g_free (data);
}
-static gboolean
-dlg_extract__start_ripping (gpointer callback_data)
+static void
+ok_button_clicked_cb (GtkWidget *widget,
+ DialogData *data)
{
- GtkWidget *dialog = callback_data;
- DialogData *data;
- GList *tracks_to_rip;
+ GList *tracks_to_rip;
- data = g_object_get_data (G_OBJECT (dialog), "dialog_data");
+ gtk_window_set_modal (GTK_WINDOW (data->dialog), FALSE);
+ gtk_widget_hide (data->dialog);
- if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->e_selected_radiobutton)))
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (GET_WIDGET ("selected_tracks_radiobutton"))))
tracks_to_rip = track_list_dup (data->selected_tracks);
else
- tracks_to_rip = track_list_dup (goo_player_get_album (data->player_cd)->tracks);
+ tracks_to_rip = track_list_dup (goo_player_get_album (data->player)->tracks);
dlg_ripper (data->window, tracks_to_rip);
track_list_free (tracks_to_rip);
gtk_widget_destroy (data->dialog);
-
- return FALSE;
-}
-
-
-/* called when the "ok" button is clicked. */
-static void
-ok_cb (GtkWidget *widget,
- DialogData *data)
-{
- gtk_window_set_modal (GTK_WINDOW (data->dialog), FALSE);
- gtk_widget_hide (data->dialog);
-
- g_idle_add (dlg_extract__start_ripping, data->dialog);
}
-/* called when the "help" button is clicked. */
static void
-help_cb (GtkWidget *widget,
- DialogData *data)
+help_button_clicked_cb (GtkWidget *widget,
+ DialogData *data)
{
- GError *err;
-
- err = NULL;
- gnome_help_display ("goobox", "extract", &err);
-
- if (err != NULL) {
- GtkWidget *dialog;
-
- dialog = gtk_message_dialog_new (GTK_WINDOW (data->dialog),
- GTK_DIALOG_MODAL,
- GTK_MESSAGE_ERROR,
- GTK_BUTTONS_CLOSE,
- _("Could not display help: %s"),
- err->message);
-
- g_signal_connect (G_OBJECT (dialog), "response",
- G_CALLBACK (gtk_widget_destroy),
- NULL);
-
- gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
- gtk_widget_show (dialog);
-
- g_error_free (err);
- }
+ show_help_dialog (GTK_WINDOW (data->window), "extract");
}
-/* create the main dialog. */
void
dlg_extract_ask (GooWindow *window)
{
- GstElement *encoder;
- gboolean ogg_encoder, flac_encoder, wave_encoder;
- DialogData *data;
- GtkWidget *btn_ok;
- GtkWidget *btn_cancel;
- GtkWidget *btn_help;
- int selected;
+ GstElement *encoder;
+ gboolean ogg_encoder;
+ gboolean flac_encoder;
+ gboolean wave_encoder;
+ DialogData *data;
+ int selected;
encoder = gst_element_factory_make (OGG_ENCODER, "encoder");
ogg_encoder = encoder != NULL;
@@ -172,7 +112,7 @@ dlg_extract_ask (GooWindow *window)
if (encoder != NULL)
gst_object_unref (GST_OBJECT (encoder));
- if (!ogg_encoder && !flac_encoder && !wave_encoder) {
+ if (! ogg_encoder && ! flac_encoder && ! wave_encoder) {
GtkWidget *d;
char *msg;
@@ -205,66 +145,45 @@ dlg_extract_ask (GooWindow *window)
data = g_new0 (DialogData, 1);
data->window = window;
- data->gui = glade_xml_new (GOO_GLADEDIR "/" GLADE_EXTRACT_FILE, NULL, NULL);
- if (!data->gui) {
- g_warning ("Could not find " GLADE_EXTRACT_FILE "\n");
- g_free (data);
- return;
- }
-
+ data->builder = _gtk_builder_new_from_file ("extract.ui", "");
data->tracks = goo_window_get_tracks (window, FALSE);
data->selected_tracks = goo_window_get_tracks (window, TRUE);
- data->player_cd = goo_window_get_player (window);
- g_object_ref (data->player_cd);
+ data->player = g_object_ref (goo_window_get_player (window));
eel_gconf_preload_cache ("/apps/goobox/dialogs/extract", GCONF_CLIENT_PRELOAD_ONELEVEL);
/* Get the widgets. */
- data->dialog = glade_xml_get_widget (data->gui, "extract_dialog");
- data->e_alltrack_radiobutton = glade_xml_get_widget (data->gui, "e_alltrack_radiobutton");
- data->e_selected_radiobutton = glade_xml_get_widget (data->gui, "e_selected_radiobutton");
-
- btn_ok = glade_xml_get_widget (data->gui, "e_okbutton");
- btn_cancel = glade_xml_get_widget (data->gui, "e_cancelbutton");
- btn_help = glade_xml_get_widget (data->gui, "e_helpbutton");
-
- gtk_button_set_use_stock (GTK_BUTTON (btn_ok), TRUE);
- gtk_button_set_label (GTK_BUTTON (btn_ok), GOO_STOCK_EXTRACT);
+ data->dialog = GET_WIDGET ("extract_dialog");
+ gtk_button_set_use_stock (GTK_BUTTON (GET_WIDGET ("ok_button")), TRUE);
+ gtk_button_set_label (GTK_BUTTON (GET_WIDGET ("ok_button")), GOO_STOCK_EXTRACT);
/* Set widgets data. */
- g_object_set_data (G_OBJECT (data->dialog), "dialog_data", data);
-
- /* FIXME: use this property or delete it. */
- if (eel_gconf_get_boolean (PREF_EXTRACT_FIRST_TIME, TRUE)) {
- eel_gconf_set_boolean (PREF_EXTRACT_FIRST_TIME, FALSE);
- }
-
selected = g_list_length (data->selected_tracks);
- gtk_widget_set_sensitive (data->e_selected_radiobutton, selected > 0);
+ gtk_widget_set_sensitive (GET_WIDGET ("selected_tracks_radiobutton"), selected > 0);
if (selected <= 1)
- gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (data->e_alltrack_radiobutton), TRUE);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (GET_WIDGET ("all_tracks_radiobutton")), TRUE);
else
- gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (data->e_selected_radiobutton), TRUE);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (GET_WIDGET ("selected_tracks_radiobutton")), TRUE);
/* Set the signals handlers. */
- g_signal_connect (G_OBJECT (data->dialog),
+ g_signal_connect (data->dialog,
"destroy",
- G_CALLBACK (destroy_cb),
+ G_CALLBACK (dialog_destroy_cb),
data);
- g_signal_connect_swapped (G_OBJECT (btn_cancel),
+ g_signal_connect_swapped (GET_WIDGET ("cancel_button"),
"clicked",
G_CALLBACK (gtk_widget_destroy),
G_OBJECT (data->dialog));
- g_signal_connect (G_OBJECT (btn_help),
+ g_signal_connect (GET_WIDGET ("help_button"),
"clicked",
- G_CALLBACK (help_cb),
+ G_CALLBACK (help_button_clicked_cb),
data);
- g_signal_connect (G_OBJECT (btn_ok),
+ g_signal_connect (GET_WIDGET ("ok_button"),
"clicked",
- G_CALLBACK (ok_cb),
+ G_CALLBACK (ok_button_clicked_cb),
data);
/* run dialog. */
@@ -282,6 +201,7 @@ dlg_extract_selected (GooWindow *window)
tracks_to_rip = goo_window_get_tracks (window, TRUE);
dlg_ripper (window, tracks_to_rip);
+
track_list_free (tracks_to_rip);
}
@@ -289,17 +209,13 @@ dlg_extract_selected (GooWindow *window)
void
dlg_extract (GooWindow *window)
{
- /* FIXME
GList *selected_tracks;
- int n_selected_tracks;
selected_tracks = goo_window_get_tracks (window, TRUE);
- n_selected_tracks = g_list_length (selected_tracks);
- track_list_free (selected_tracks);
-
- if (n_selected_tracks <= 1)
+ if (g_list_length (selected_tracks) < 1)
dlg_ripper (window, NULL);
- else*/
-
- dlg_extract_ask (window);
+ else
+ dlg_extract_ask (window);
+
+ track_list_free (selected_tracks);
}
diff --git a/src/dlg-preferences.c b/src/dlg-preferences.c
index d0f58de..d993b7e 100644
--- a/src/dlg-preferences.c
+++ b/src/dlg-preferences.c
@@ -3,7 +3,7 @@
/*
* Goo
*
- * Copyright (C) 2001, 2003, 2004 Free Software Foundation, Inc.
+ * Copyright (C) 2001-2009 Free Software Foundation, Inc.
*
* 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
@@ -21,144 +21,100 @@
*/
#include <config.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-
+#include <brasero/brasero-drive-selection.h>
#include <gtk/gtk.h>
-#include <libgnome/libgnome.h>
-#include <glade/glade.h>
-#include <libgnomevfs/gnome-vfs-utils.h>
#include <gst/gst.h>
-#include "main.h"
#include "gconf-utils.h"
-#include "typedefs.h"
#include "goo-window.h"
+#include "gtk-utils.h"
#include "preferences.h"
-#include "bacon-cd-selection.h"
-#include "goo-stock.h"
-#include "file-utils.h"
+#include "typedefs.h"
+
+#define N_VALUES 10
+#define DEFAULT_OGG_QUALITY 0.5
+#define DEFAULT_FLAC_COMPRESSION 5
+#define GET_WIDGET(x) _gtk_builder_get_widget (data->builder, (x))
-#define GLADE_PREF_FILE "preferences.glade"
-#define GLADE_FORMAT_FILE "format_dialog.glade"
-enum { TEXT_COLUMN, DATA_COLUMN, PRESENT_COLUMN, N_COLUMNS };
+enum {
+ TEXT_COLUMN,
+ DATA_COLUMN,
+ PRESENT_COLUMN,
+ N_COLUMNS
+};
+
-/*static int ogg_rate[] = { 64, 80, 96, 112, 128, 160, 192, 224, 256, 320 };*/
static int flac_compression[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
-#define N_VALUES 10
-#define DEFAULT_OGG_QUALITY 0.5
-#define DEFAULT_FLAC_COMPRESSION 5
typedef struct {
GooWindow *window;
- int ogg_value;
- int flac_value;
-
- GladeXML *gui;
-
+ GtkBuilder *builder;
GtkWidget *dialog;
GtkWidget *drive_selector;
-
- GtkWidget *p_destination_filechooserbutton;
- GtkWidget *p_autoplay_checkbutton;
-
+ GtkWidget *filetype_combobox;
GtkTreeModel *filetype_model;
- GtkWidget *p_filetype_combobox;
- GtkWidget *p_encoding_notebook;
- GtkWidget *p_filetype_properties_button;
- GtkWidget *p_save_playlist_checkbutton;
+ int ogg_value;
+ int flac_value;
} DialogData;
-/* called when the "apply" button is clicked. */
static void
-apply_cb (GtkWidget *widget,
- DialogData *data)
+apply_button_clicked_cb (GtkWidget *widget,
+ DialogData *data)
{
- const char *destination;
- const char *device;
- const char *current_device;
+ const char *destination;
+ BraseroDrive *br_drive;
+ const char *device;
- destination = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (data->p_destination_filechooserbutton));
- eel_gconf_set_path (PREF_EXTRACT_DESTINATION, destination);
+ destination = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (GET_WIDGET ("destination_filechooserbutton")));
+ eel_gconf_set_uri (PREF_EXTRACT_DESTINATION, destination);
- pref_set_file_format (gtk_combo_box_get_active (GTK_COMBO_BOX (data->p_filetype_combobox)));
- eel_gconf_set_boolean (PREF_EXTRACT_SAVE_PLAYLIST, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->p_save_playlist_checkbutton)));
- eel_gconf_set_boolean (PREF_GENERAL_AUTOPLAY, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->p_autoplay_checkbutton)));
+ pref_set_file_format (gtk_combo_box_get_active (GTK_COMBO_BOX (data->filetype_combobox)));
+ eel_gconf_set_boolean (PREF_EXTRACT_SAVE_PLAYLIST, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (GET_WIDGET ("save_playlist_checkbutton"))));
+ eel_gconf_set_boolean (PREF_GENERAL_AUTOPLAY, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (GET_WIDGET ("autoplay_checkbutton"))));
/**/
- device = bacon_cd_selection_get_device (BACON_CD_SELECTION (data->drive_selector));
- if (device == NULL)
+ br_drive = brasero_drive_selection_get_active (BRASERO_DRIVE_SELECTION (data->drive_selector));
+ if (br_drive == NULL)
return;
-
- current_device = goo_player_get_device (goo_window_get_player (data->window));
-
- if ((current_device != NULL) && (strcmp (current_device, device) == 0))
- return;
-
+
+ device = brasero_drive_get_device (br_drive);
eel_gconf_set_string (PREF_GENERAL_DEVICE, device);
goo_window_set_device (data->window, device);
goo_window_update (data->window);
+
+ g_object_unref (br_drive);
}
-/* called when the main dialog is closed. */
static void
-destroy_cb (GtkWidget *widget,
- DialogData *data)
+dialog_destroy_cb (GtkWidget *widget,
+ DialogData *data)
{
- apply_cb (widget, data);
+ apply_button_clicked_cb (widget, data);
data->window->preferences_dialog = NULL;
- g_object_unref (G_OBJECT (data->gui));
+
+ g_object_unref (data->builder);
g_free (data);
}
-/* called when the "close" button is clicked. */
static void
-close_cb (GtkWidget *widget,
- DialogData *data)
+close_button_clicked_cb (GtkWidget *widget,
+ DialogData *data)
{
- apply_cb (widget, data);
+ apply_button_clicked_cb (widget, data);
gtk_widget_destroy (data->dialog);
}
-/* called when the "help" button is clicked. */
static void
-help_cb (GtkWidget *widget,
- DialogData *data)
+help_button_clicked_cb (GtkWidget *widget,
+ DialogData *data)
{
- GError *err;
-
- err = NULL;
- gnome_help_display ("goo", "preferences", &err);
-
- if (err != NULL) {
- GtkWidget *dialog;
-
- dialog = gtk_message_dialog_new (GTK_WINDOW (data->dialog),
- GTK_DIALOG_MODAL,
- GTK_MESSAGE_ERROR,
- GTK_BUTTONS_CLOSE,
- _("Could not display help: %s"),
- err->message);
-
- g_signal_connect (G_OBJECT (dialog), "response",
- G_CALLBACK (gtk_widget_destroy),
- NULL);
-
- gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
-
- gtk_widget_show (dialog);
-
- g_error_free (err);
- }
+ show_help_dialog (GTK_WINDOW (data->window), "preferences");
}
@@ -166,13 +122,10 @@ void dlg_format (DialogData *dialog_data, GooFileFormat format);
static void
-filetype_properties_clicked_cb (GtkWidget *widget,
- DialogData *data)
+filetype_properties_clicked_cb (GtkWidget *widget,
+ DialogData *data)
{
- int format;
-
- format = gtk_combo_box_get_active (GTK_COMBO_BOX (data->p_filetype_combobox));
- dlg_format (data, format);
+ dlg_format (data, gtk_combo_box_get_active (GTK_COMBO_BOX (data->filetype_combobox)));
}
@@ -181,7 +134,7 @@ drive_selector_device_changed_cb (GtkOptionMenu *option_menu,
const char *device_path,
DialogData *data)
{
- apply_cb (NULL, data);
+ apply_button_clicked_cb (NULL, data);
}
@@ -191,9 +144,9 @@ filetype_combobox_changed_cb (GtkComboBox *widget,
{
int format;
- format = gtk_combo_box_get_active (GTK_COMBO_BOX (data->p_filetype_combobox));
- gtk_notebook_set_current_page (GTK_NOTEBOOK (data->p_encoding_notebook), format);
- gtk_widget_set_sensitive (data->p_filetype_properties_button, format != GOO_FILE_FORMAT_WAVE);
+ format = gtk_combo_box_get_active (GTK_COMBO_BOX (data->filetype_combobox));
+ gtk_notebook_set_current_page (GTK_NOTEBOOK (GET_WIDGET ("encoding_notebook")), format);
+ gtk_widget_set_sensitive (GET_WIDGET ("filetype_properties_button"), format != GOO_FILE_FORMAT_WAVE);
}
@@ -202,12 +155,11 @@ set_description_label (DialogData *data,
const char *widget_name,
const char *label_text)
{
- GtkWidget *label;
- char *text;
+ char *text;
- label = glade_xml_get_widget (data->gui, widget_name);
text = g_markup_printf_escaped ("<small><i>%s</i></small>", label_text);
- gtk_label_set_markup (GTK_LABEL (label), text);
+ gtk_label_set_markup (GTK_LABEL (GET_WIDGET (widget_name)), text);
+
g_free (text);
}
@@ -216,12 +168,6 @@ void
dlg_preferences (GooWindow *window)
{
DialogData *data;
- GtkWidget *btn_close;
- GtkWidget *btn_help;
- GtkWidget *box;
- GtkWidget *filetype_combobox_box;
- GtkWidget *p_filetype_properties_button;
- char *device = NULL;
char *destination = NULL;
GooFileFormat file_format;
GstElement *encoder;
@@ -237,49 +183,29 @@ dlg_preferences (GooWindow *window)
data = g_new0 (DialogData, 1);
data->window = window;
- data->gui = glade_xml_new (GOO_GLADEDIR "/" GLADE_PREF_FILE, NULL, NULL);
- if (!data->gui) {
- g_warning ("Could not find " GLADE_PREF_FILE "\n");
- g_free (data);
- return;
- }
+ data->builder = _gtk_builder_new_from_file ("preferences.ui", "");
eel_gconf_preload_cache ("/apps/goobox/general", GCONF_CLIENT_PRELOAD_ONELEVEL);
/* Get the widgets. */
- data->dialog = glade_xml_get_widget (data->gui, "preferences_dialog");
+ data->dialog = GET_WIDGET ("preferences_dialog");
window->preferences_dialog = data->dialog;
- data->p_destination_filechooserbutton = glade_xml_get_widget (data->gui, "p_destination_filechooserbutton");
- data->p_autoplay_checkbutton = glade_xml_get_widget (data->gui, "p_autoplay_checkbutton");
-
- filetype_combobox_box = glade_xml_get_widget (data->gui, "filetype_combobox_box");
- data->p_encoding_notebook = glade_xml_get_widget (data->gui, "p_encoding_notebook");
- data->p_filetype_properties_button = glade_xml_get_widget (data->gui, "p_filetype_properties_button");
- data->p_save_playlist_checkbutton = glade_xml_get_widget (data->gui, "p_save_playlist_checkbutton");
- p_filetype_properties_button = glade_xml_get_widget (data->gui, "p_filetype_properties_button");
-
- box = glade_xml_get_widget (data->gui, "p_drive_selector_box");
- btn_close = glade_xml_get_widget (data->gui, "p_closebutton");
- btn_help = glade_xml_get_widget (data->gui, "p_helpbutton");
-
/* Set widgets data. */
if (preferences_get_use_sound_juicer ()) {
GtkWidget *notebook;
GtkWidget *encoder_page;
- GtkWidget *vbox;
- notebook = glade_xml_get_widget (data->gui, "p_notebook");
+ notebook = GET_WIDGET ("notebook");
gtk_notebook_set_show_border (GTK_NOTEBOOK (notebook), FALSE);
gtk_notebook_set_show_tabs (GTK_NOTEBOOK (notebook), FALSE);
encoder_page = gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), 1);
gtk_widget_hide (encoder_page);
- vbox = glade_xml_get_widget (data->gui, "general_vbox");
- gtk_container_set_border_width (GTK_CONTAINER (vbox), 0);
+ gtk_container_set_border_width (GTK_CONTAINER (GET_WIDGET ("general_vbox")), 0);
}
/* Extraction */
@@ -288,31 +214,26 @@ dlg_preferences (GooWindow *window)
G_TYPE_STRING,
G_TYPE_INT,
G_TYPE_BOOLEAN));
- data->p_filetype_combobox = gtk_combo_box_new_with_model (data->filetype_model);
+ data->filetype_combobox = gtk_combo_box_new_with_model (data->filetype_model);
renderer = gtk_cell_renderer_text_new ();
- gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (data->p_filetype_combobox),
+ gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (data->filetype_combobox),
renderer,
FALSE);
- gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (data->p_filetype_combobox),
+ gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (data->filetype_combobox),
renderer,
"text", TEXT_COLUMN,
"sensitive", PRESENT_COLUMN,
NULL);
- gtk_widget_show (data->p_filetype_combobox);
- gtk_box_pack_start (GTK_BOX (filetype_combobox_box), data->p_filetype_combobox, TRUE, TRUE, 0);
+ gtk_widget_show (data->filetype_combobox);
+ gtk_box_pack_start (GTK_BOX (GET_WIDGET ("filetype_combobox_box")), data->filetype_combobox, TRUE, TRUE, 0);
/**/
- destination = eel_gconf_get_path (PREF_EXTRACT_DESTINATION, "");
- if ((destination == NULL) || (strcmp (destination, "") == 0)) {
- char *tmp;
-
- tmp = xdg_user_dir_lookup ("MUSIC");
- destination = get_uri_from_local_path (tmp);
- g_free (tmp);
- }
- gtk_file_chooser_set_uri (GTK_FILE_CHOOSER (data->p_destination_filechooserbutton), destination);
+ destination = eel_gconf_get_uri (PREF_EXTRACT_DESTINATION, "");
+ if ((destination == NULL) || (strcmp (destination, "") == 0))
+ destination = g_filename_to_uri (g_get_user_special_dir (G_USER_DIRECTORY_MUSIC), NULL, NULL);
+ gtk_file_chooser_set_uri (GTK_FILE_CHOOSER (GET_WIDGET ("destination_filechooserbutton")), destination);
g_free (destination);
encoder = gst_element_factory_make (OGG_ENCODER, "encoder");
@@ -366,7 +287,7 @@ dlg_preferences (GooWindow *window)
file_format = GOO_FILE_FORMAT_WAVE;
}
- gtk_combo_box_set_active (GTK_COMBO_BOX (data->p_filetype_combobox), file_format);
+ gtk_combo_box_set_active (GTK_COMBO_BOX (data->filetype_combobox), file_format);
filetype_combobox_changed_cb (NULL, data);
/**/
@@ -375,46 +296,38 @@ dlg_preferences (GooWindow *window)
set_description_label (data, "flac_description_label", _("Free Lossless Audio Codec (FLAC) is an open source codec that compresses but does not degrade audio quality."));
set_description_label (data, "wave_description_label", _("WAV+PCM is a lossless format that holds uncompressed, raw pulse-code modulated (PCM) audio."));
- gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (data->p_save_playlist_checkbutton), eel_gconf_get_boolean (PREF_EXTRACT_SAVE_PLAYLIST, TRUE));
- gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (data->p_autoplay_checkbutton), eel_gconf_get_boolean (PREF_GENERAL_AUTOPLAY, TRUE));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (GET_WIDGET ("save_playlist_checkbutton")), eel_gconf_get_boolean (PREF_EXTRACT_SAVE_PLAYLIST, TRUE));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (GET_WIDGET ("autoplay_checkbutton")), eel_gconf_get_boolean (PREF_GENERAL_AUTOPLAY, TRUE));
/**/
- data->drive_selector = bacon_cd_selection_new (Drives, goo_player_get_drive (goo_window_get_player (window)));
+ data->drive_selector = brasero_drive_selection_new ();
gtk_widget_show (data->drive_selector);
- gtk_box_pack_start (GTK_BOX (box), data->drive_selector, TRUE, TRUE, 0);
-
- device = eel_gconf_get_string (PREF_GENERAL_DEVICE, bacon_cd_selection_get_default_device (BACON_CD_SELECTION (data->drive_selector)));
- bacon_cd_selection_set_device (BACON_CD_SELECTION (data->drive_selector), device);
- g_free (device);
+ gtk_box_pack_start (GTK_BOX (GET_WIDGET ("drive_selector_box")), data->drive_selector, TRUE, TRUE, 0);
/* Set the signals handlers. */
- g_signal_connect (G_OBJECT (data->dialog),
+ g_signal_connect (data->dialog,
"destroy",
- G_CALLBACK (destroy_cb),
+ G_CALLBACK (dialog_destroy_cb),
data);
-
- g_signal_connect (G_OBJECT (btn_close),
+ g_signal_connect (GET_WIDGET ("close_button"),
"clicked",
- G_CALLBACK (close_cb),
+ G_CALLBACK (close_button_clicked_cb),
data);
- g_signal_connect (G_OBJECT (btn_help),
+ g_signal_connect (GET_WIDGET ("help_button"),
"clicked",
- G_CALLBACK (help_cb),
+ G_CALLBACK (help_button_clicked_cb),
data);
-
- g_signal_connect (G_OBJECT (p_filetype_properties_button),
+ g_signal_connect (GET_WIDGET ("filetype_properties_button"),
"clicked",
G_CALLBACK (filetype_properties_clicked_cb),
data);
-
g_signal_connect (G_OBJECT (data->drive_selector),
- "device_changed",
+ "changed",
G_CALLBACK (drive_selector_device_changed_cb),
data);
-
- g_signal_connect (G_OBJECT (data->p_filetype_combobox),
+ g_signal_connect (data->filetype_combobox,
"changed",
G_CALLBACK (filetype_combobox_changed_cb),
data);
@@ -434,7 +347,7 @@ typedef struct {
GooFileFormat format;
int value;
- GladeXML *gui;
+ GtkBuilder *builder;
GtkWidget *dialog;
GtkWidget *f_quality_label;
GtkWidget *f_quality_scale;
@@ -445,7 +358,7 @@ static void
format_dialog_destroy_cb (GtkWidget *widget,
FormatDialogData *data)
{
- g_object_unref (G_OBJECT (data->gui));
+ g_object_unref (data->builder);
g_free (data);
}
@@ -522,56 +435,34 @@ dlg_format (DialogData *preferences_data,
GooFileFormat format)
{
FormatDialogData *data;
- GtkWidget *btn_ok;
- GtkWidget *f_smaller_value_label;
- GtkWidget *f_bigger_value_label;
- GtkWidget *f_title_label;
- GtkWidget *f_description_label;
- char *text;
+ char *text;
data = g_new0 (FormatDialogData, 1);
data->format = format;
- data->gui = glade_xml_new (GOO_GLADEDIR "/" GLADE_FORMAT_FILE, NULL, NULL);
- if (!data->gui) {
- g_warning ("Could not find " GLADE_FORMAT_FILE "\n");
- g_free (data);
- return;
- }
-
- /* Get the widgets. */
-
- data->dialog = glade_xml_get_widget (data->gui, "format_dialog");
-
- data->f_quality_label = glade_xml_get_widget (data->gui, "f_quality_label");
- data->f_quality_scale = glade_xml_get_widget (data->gui, "f_quality_scale");
- f_smaller_value_label = glade_xml_get_widget (data->gui, "f_smaller_value_label");
- f_bigger_value_label = glade_xml_get_widget (data->gui, "f_bigger_value_label");
- f_title_label = glade_xml_get_widget (data->gui, "f_title_label");
- f_description_label = glade_xml_get_widget (data->gui, "f_description_label");
-
- btn_ok = glade_xml_get_widget (data->gui, "f_okbutton");
+ data->builder = _gtk_builder_new_from_file ("format-options.ui", "");
+ data->dialog = GET_WIDGET ("format_dialog");
/* Set widgets data. */
data->value = get_current_value (format);
- gtk_range_set_value (GTK_RANGE (data->f_quality_scale), scale_value (data->value));
+ gtk_range_set_value (GTK_RANGE (GET_WIDGET ("quality_scale")), scale_value (data->value));
if (format == GOO_FILE_FORMAT_FLAC) {
text = g_strdup_printf ("<small><i>%s</i></small>", _("Faster compression"));
- gtk_label_set_markup (GTK_LABEL (f_smaller_value_label), text);
+ gtk_label_set_markup (GTK_LABEL (GET_WIDGET ("smaller_value_label")), text);
g_free (text);
text = g_strdup_printf ("<small><i>%s</i></small>", _("Higher compression"));
- gtk_label_set_markup (GTK_LABEL (f_bigger_value_label), text);
+ gtk_label_set_markup (GTK_LABEL (GET_WIDGET ("bigger_value_label")), text);
g_free (text);
}
else {
text = g_strdup_printf ("<small><i>%s</i></small>", _("Smaller size"));
- gtk_label_set_markup (GTK_LABEL (f_smaller_value_label), text);
+ gtk_label_set_markup (GTK_LABEL (GET_WIDGET ("smaller_value_label")), text);
g_free (text);
text = g_strdup_printf ("<small><i>%s</i></small>", _("Higher quality"));
- gtk_label_set_markup (GTK_LABEL (f_bigger_value_label), text);
+ gtk_label_set_markup (GTK_LABEL (GET_WIDGET ("bigger_value_label")), text);
g_free (text);
}
@@ -586,7 +477,7 @@ dlg_format (DialogData *preferences_data,
text = g_strdup ("");
break;
}
- gtk_label_set_markup (GTK_LABEL (f_title_label), text);
+ gtk_label_set_markup (GTK_LABEL (GET_WIDGET ("title_label")), text);
g_free (text);
switch (data->format) {
@@ -600,7 +491,7 @@ dlg_format (DialogData *preferences_data,
text = "";
break;
}
- gtk_label_set_text (GTK_LABEL (data->f_quality_label), text);
+ gtk_label_set_text (GTK_LABEL (GET_WIDGET ("quality_label")), text);
switch (data->format) {
case GOO_FILE_FORMAT_OGG:
@@ -616,20 +507,20 @@ dlg_format (DialogData *preferences_data,
text = "";
break;
}
- gtk_label_set_text (GTK_LABEL (f_description_label), text);
+ gtk_label_set_text (GTK_LABEL (GET_WIDGET ("description_label")), text);
/* Set the signals handlers. */
- g_signal_connect (G_OBJECT (data->dialog),
+ g_signal_connect (data->dialog,
"destroy",
G_CALLBACK (format_dialog_destroy_cb),
data);
- g_signal_connect (G_OBJECT (btn_ok),
+ g_signal_connect (GET_WIDGET ("ok_button"),
"clicked",
G_CALLBACK (format_dialog_ok_button_clicked_cb),
data);
- g_signal_connect (G_OBJECT (data->f_quality_scale),
+ g_signal_connect (GET_WIDGET ("quality_scale"),
"value_changed",
G_CALLBACK (format_dialog_scale_value_changed_cb),
data);
diff --git a/src/dlg-properties.c b/src/dlg-properties.c
index 8eb2b67..de56ecd 100644
--- a/src/dlg-properties.c
+++ b/src/dlg-properties.c
@@ -3,7 +3,7 @@
/*
* Goo
*
- * Copyright (C) 2001, 2003, 2004 Free Software Foundation, Inc.
+ * Copyright (C) 2001-2009 Free Software Foundation, Inc.
*
* 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
@@ -21,47 +21,42 @@
*/
#include <config.h>
-
#include <gtk/gtk.h>
-#include <libgnome/libgnome.h>
-#include <glade/glade.h>
-#include "goo-stock.h"
#include "dlg-properties.h"
+#include "goo-stock.h"
+#include "gtk-utils.h"
#include "metadata.h"
-#define GLADE_PREF_FILE "properties.glade"
-enum { NUMBER_COLUMN, TITLE_COLUMN, ARTIST_COLUMN, DATA_COLUMN, N_COLUMNS };
+#define GET_WIDGET(x) _gtk_builder_get_widget (data->builder, (x))
+
+
+enum {
+ NUMBER_COLUMN,
+ TITLE_COLUMN,
+ ARTIST_COLUMN,
+ DATA_COLUMN,
+ N_COLUMNS
+};
+
typedef struct {
GooWindow *window;
- GladeXML *gui;
GtkWidget *dialog;
+ GtkBuilder *builder;
GtkListStore *list_store;
GtkTreeViewColumn *author_column;
- GtkWidget *p_title_entry;
- GtkWidget *p_artist_entry;
- GtkWidget *p_artist_combobox;
- GtkWidget *p_year_spinbutton;
- GtkWidget *p_year_checkbutton;
- GtkWidget *p_info_label;
- GtkWidget *p_info_box;
- GtkWidget *p_navigation_box;
- GtkWidget *p_album_label;
- GtkWidget *p_track_treeview;
-
GList *albums;
int n_albums, current_album;
} DialogData;
-/* called when the main dialog is closed. */
static void
-destroy_cb (GtkWidget *widget,
- DialogData *data)
+dialog_destroy_cb (GtkWidget *widget,
+ DialogData *data)
{
data->window->properties_dialog = NULL;
- g_object_unref (G_OBJECT (data->gui));
+ g_object_unref (G_OBJECT (data->builder));
g_free (data);
}
@@ -74,15 +69,15 @@ set_album_from_data (DialogData *data)
GtkTreeIter iter;
album = album_info_copy (goo_window_get_album (data->window));
- album_info_set_title (album, gtk_entry_get_text (GTK_ENTRY (data->p_title_entry)));
- album_artist = gtk_entry_get_text (GTK_ENTRY (data->p_artist_entry));
+ album_info_set_title (album, gtk_entry_get_text (GTK_ENTRY (GET_WIDGET ("title_entry"))));
+ album_artist = gtk_entry_get_text (GTK_ENTRY (GET_WIDGET ("artist_entry")));
album_info_set_artist (album, album_artist, "");
- album->various_artist = gtk_combo_box_get_active (GTK_COMBO_BOX (data->p_artist_combobox)) == 1;
+ album->various_artist = gtk_combo_box_get_active (GTK_COMBO_BOX (GET_WIDGET ("artist_combobox"))) == 1;
- if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->p_year_checkbutton))) {
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (GET_WIDGET ("year_checkbutton")))) {
GDate *date;
- date = g_date_new_dmy (1, 1, gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (data->p_year_spinbutton)));
+ date = g_date_new_dmy (1, 1, gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (GET_WIDGET ("year_spinbutton"))));
album_info_set_release_date (album, date);
g_date_free (date);
}
@@ -128,8 +123,8 @@ set_album_from_data (DialogData *data)
static void
-ok_cb (GtkWidget *widget,
- DialogData *data)
+ok_button_clicked_cb (GtkWidget *widget,
+ DialogData *data)
{
set_album_from_data (data);
gtk_widget_destroy (data->dialog);
@@ -137,34 +132,10 @@ ok_cb (GtkWidget *widget,
static void
-help_cb (GtkWidget *widget,
- DialogData *data)
+help_button_clicked_cb (GtkWidget *widget,
+ DialogData *data)
{
- GError *err;
-
- err = NULL;
- gnome_help_display ("goo", "properties", &err);
-
- if (err != NULL) {
- GtkWidget *dialog;
-
- dialog = gtk_message_dialog_new (GTK_WINDOW (data->dialog),
- GTK_DIALOG_MODAL,
- GTK_MESSAGE_ERROR,
- GTK_BUTTONS_CLOSE,
- _("Could not display help: %s"),
- err->message);
-
- g_signal_connect (G_OBJECT (dialog), "response",
- G_CALLBACK (gtk_widget_destroy),
- NULL);
-
- gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
-
- gtk_widget_show (dialog);
-
- g_error_free (err);
- }
+ show_help_dialog (GTK_WINDOW (data->window), "properties");
}
@@ -174,17 +145,17 @@ set_data_from_album (DialogData *data,
{
GList *scan;
- gtk_combo_box_set_active (GTK_COMBO_BOX (data->p_artist_combobox), album->various_artist ? 1 : 0);
+ gtk_combo_box_set_active (GTK_COMBO_BOX (GET_WIDGET ("artist_combobox")), album->various_artist ? 1 : 0);
if (album->title != NULL)
- gtk_entry_set_text (GTK_ENTRY (data->p_title_entry), album->title);
+ gtk_entry_set_text (GTK_ENTRY (GET_WIDGET ("title_entry")), album->title);
if (album->artist != NULL)
- gtk_entry_set_text (GTK_ENTRY (data->p_artist_entry), album->artist);
- gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (data->p_year_checkbutton), g_date_valid (album->release_date));
+ gtk_entry_set_text (GTK_ENTRY (GET_WIDGET ("artist_entry")), album->artist);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (GET_WIDGET ("year_checkbutton")), g_date_valid (album->release_date));
if (g_date_valid (album->release_date))
- gtk_spin_button_set_value (GTK_SPIN_BUTTON (data->p_year_spinbutton), g_date_get_year (album->release_date));
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (GET_WIDGET ("year_spinbutton")), g_date_get_year (album->release_date));
else
- gtk_spin_button_set_value (GTK_SPIN_BUTTON (data->p_year_spinbutton), 0);
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (GET_WIDGET ("year_spinbutton")), 0);
gtk_list_store_clear (data->list_store);
for (scan = album->tracks; scan; scan = scan->next) {
@@ -212,11 +183,11 @@ show_album (DialogData *data,
return;
data->current_album = n;
- gtk_widget_hide (data->p_info_box);
- gtk_widget_show (data->p_navigation_box);
+ gtk_widget_hide (GET_WIDGET ("info_box"));
+ gtk_widget_show (GET_WIDGET ("navigation_box"));
s = g_strdup_printf (_("Album %d of %d"), data->current_album + 1, data->n_albums);
- gtk_label_set_text (GTK_LABEL (data->p_album_label), s);
+ gtk_label_set_text (GTK_LABEL (GET_WIDGET ("album_label")), s);
g_free (s);
set_data_from_album (data, g_list_nth (data->albums, n)->data);
@@ -264,7 +235,7 @@ search_cb (GtkWidget *widget,
mb_SetDepth (mb, 4);
/*mb_SetMaxItems(mb, 10);*/
- mb_args[0] = (char*) gtk_entry_get_text (GTK_ENTRY (data->p_title_entry));
+ mb_args[0] = (char*) gtk_entry_get_text (GTK_ENTRY (GET_WIDGET ("title_entry")));
mb_args[1] = NULL;
if (! mb_QueryWithArgs (mb, MBQ_FindAlbumByName, mb_args)) {
char mb_error[1024];
@@ -272,7 +243,7 @@ search_cb (GtkWidget *widget,
mb_GetQueryError (mb, mb_error, sizeof (mb_error));
s = g_strdup_printf (_("Search failed: %s\n"), mb_error);
- gtk_label_set_text (GTK_LABEL (data->p_info_label), s);
+ gtk_label_set_text (GTK_LABEL (GET_WIDGET ("info_label")), s);
g_free (s);
}
else {
@@ -281,9 +252,9 @@ search_cb (GtkWidget *widget,
data->n_albums = g_list_length (data->albums);
if (data->n_albums == 0) {
- gtk_label_set_text (GTK_LABEL (data->p_info_label), _("No album found"));
- gtk_widget_show (data->p_info_box);
- gtk_widget_hide (data->p_navigation_box);
+ gtk_label_set_text (GTK_LABEL (GET_WIDGET ("info_label")), _("No album found"));
+ gtk_widget_show (GET_WIDGET ("info_box"));
+ gtk_widget_hide (GET_WIDGET ("navigation_box"));
}
else
show_album (data, 0);
@@ -431,11 +402,11 @@ artist_combobox_changed_cb (GtkComboBox *widget,
{
gboolean single_artist = gtk_combo_box_get_active (widget) == 0;
gtk_tree_view_column_set_visible (data->author_column, ! single_artist);
- gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (data->p_track_treeview), ! single_artist);
+ gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (GET_WIDGET ("track_treeview")), ! single_artist);
if (single_artist)
- gtk_widget_show (data->p_artist_entry);
+ gtk_widget_show (GET_WIDGET ("artist_entry"));
else
- gtk_widget_hide (data->p_artist_entry);
+ gtk_widget_hide (GET_WIDGET ("artist_entry"));
}
@@ -456,11 +427,11 @@ next_album_button_clicked_cb (GtkButton *button,
static void
-reset_album_button_clicked_cb (GtkButton *button,
- DialogData *data)
+undo_button_clicked_cb (GtkButton *button,
+ DialogData *data)
{
- gtk_widget_hide (data->p_info_box);
- gtk_widget_hide (data->p_navigation_box);
+ gtk_widget_hide (GET_WIDGET ("info_box"));
+ gtk_widget_hide (GET_WIDGET ("navigation_box"));
set_data_from_album (data, goo_window_get_album (data->window));
}
@@ -469,7 +440,7 @@ static void
year_checkbutton_toggled_cb (GtkToggleButton *button,
DialogData *data)
{
- gtk_widget_set_sensitive (data->p_year_spinbutton, gtk_toggle_button_get_active (button));
+ gtk_widget_set_sensitive (GET_WIDGET ("year_spinbutton"), gtk_toggle_button_get_active (button));
}
@@ -477,13 +448,6 @@ void
dlg_properties (GooWindow *window)
{
DialogData *data;
- GtkWidget *btn_cancel;
- GtkWidget *btn_ok;
- GtkWidget *btn_help;
- GtkWidget *btn_search;
- GtkWidget *p_prev_album_button;
- GtkWidget *p_next_album_button;
- GtkWidget *p_reset_album_button;
GtkWidget *image;
if (window->properties_dialog != NULL) {
@@ -493,42 +457,17 @@ dlg_properties (GooWindow *window)
data = g_new0 (DialogData, 1);
data->window = window;
- data->gui = glade_xml_new (GOO_GLADEDIR "/" GLADE_PREF_FILE, NULL, NULL);
- if (!data->gui) {
- g_warning ("Could not find " GLADE_PREF_FILE "\n");
- g_free (data);
- return;
- }
+ data->builder = _gtk_builder_new_from_file ("properties.ui", "");
/* Get the widgets. */
- data->dialog = glade_xml_get_widget (data->gui, "properties_dialog");
+ data->dialog = GET_WIDGET ("properties_dialog");
window->properties_dialog = data->dialog;
- data->p_artist_entry = glade_xml_get_widget (data->gui, "p_artist_entry");
- data->p_artist_combobox = glade_xml_get_widget (data->gui, "p_artist_combobox");
- data->p_title_entry = glade_xml_get_widget (data->gui, "p_title_entry");
- data->p_info_box = glade_xml_get_widget (data->gui, "p_info_box");
- data->p_info_label = glade_xml_get_widget (data->gui, "p_info_label");
- data->p_navigation_box = glade_xml_get_widget (data->gui, "p_navigation_box");
- data->p_album_label = glade_xml_get_widget (data->gui, "p_album_label");
- data->p_year_spinbutton = glade_xml_get_widget (data->gui, "p_year_spinbutton");
- data->p_year_checkbutton = glade_xml_get_widget (data->gui, "p_year_checkbutton");
-
- btn_ok = glade_xml_get_widget (data->gui, "p_okbutton");
- btn_cancel = glade_xml_get_widget (data->gui, "p_cancelbutton");
- btn_help = glade_xml_get_widget (data->gui, "p_helpbutton");
- btn_search = glade_xml_get_widget (data->gui, "p_search_button");
- data->p_track_treeview = glade_xml_get_widget (data->gui, "p_track_treeview");
-
- p_prev_album_button = glade_xml_get_widget (data->gui, "p_prev_album_button");
- p_next_album_button = glade_xml_get_widget (data->gui, "p_next_album_button");
- p_reset_album_button = glade_xml_get_widget (data->gui, "p_undobutton");
-
- /**/
+ /* Set widgets data. */
image = gtk_image_new_from_stock (GOO_STOCK_RESET, GTK_ICON_SIZE_BUTTON);
- g_object_set (p_reset_album_button,
+ g_object_set (GET_WIDGET ("undo_button"),
"use_stock", TRUE,
"label", GOO_STOCK_RESET,
"image", image,
@@ -539,48 +478,48 @@ dlg_properties (GooWindow *window)
G_TYPE_STRING,
G_TYPE_STRING,
G_TYPE_POINTER);
- gtk_tree_view_set_model (GTK_TREE_VIEW (data->p_track_treeview), GTK_TREE_MODEL (data->list_store));
- add_columns (data, GTK_TREE_VIEW (data->p_track_treeview));
+ gtk_tree_view_set_model (GTK_TREE_VIEW (GET_WIDGET ("track_treeview")), GTK_TREE_MODEL (data->list_store));
+ add_columns (data, GTK_TREE_VIEW (GET_WIDGET ("track_treeview")));
/* Set the signals handlers. */
g_signal_connect (G_OBJECT (data->dialog),
"destroy",
- G_CALLBACK (destroy_cb),
+ G_CALLBACK (dialog_destroy_cb),
data);
- g_signal_connect_swapped (G_OBJECT (btn_cancel),
+ g_signal_connect_swapped (GET_WIDGET ("cancel_button"),
"clicked",
G_CALLBACK (gtk_widget_destroy),
data->dialog);
- g_signal_connect (G_OBJECT (btn_ok),
+ g_signal_connect (GET_WIDGET ("ok_button"),
"clicked",
- G_CALLBACK (ok_cb),
+ G_CALLBACK (ok_button_clicked_cb),
data);
- g_signal_connect (G_OBJECT (btn_help),
+ g_signal_connect (GET_WIDGET ("help_button"),
"clicked",
- G_CALLBACK (help_cb),
+ G_CALLBACK (help_button_clicked_cb),
data);
- g_signal_connect (G_OBJECT (btn_search),
+ g_signal_connect (GET_WIDGET ("search_button"),
"clicked",
G_CALLBACK (search_cb),
data);
- g_signal_connect (G_OBJECT (data->p_artist_combobox),
+ g_signal_connect (GET_WIDGET ("artist_combobox"),
"changed",
G_CALLBACK (artist_combobox_changed_cb),
data);
- g_signal_connect (G_OBJECT (p_prev_album_button),
+ g_signal_connect (GET_WIDGET ("prev_album_button"),
"clicked",
G_CALLBACK (prev_album_button_clicked_cb),
data);
- g_signal_connect (G_OBJECT (p_next_album_button),
+ g_signal_connect (GET_WIDGET ("next_album_button"),
"clicked",
G_CALLBACK (next_album_button_clicked_cb),
data);
- g_signal_connect (G_OBJECT (p_reset_album_button),
+ g_signal_connect (GET_WIDGET ("undo_button"),
"clicked",
- G_CALLBACK (reset_album_button_clicked_cb),
+ G_CALLBACK (undo_button_clicked_cb),
data);
- g_signal_connect (G_OBJECT (data->p_year_checkbutton),
+ g_signal_connect (GET_WIDGET ("year_checkbutton"),
"toggled",
G_CALLBACK (year_checkbutton_toggled_cb),
data);
diff --git a/src/dlg-ripper.c b/src/dlg-ripper.c
index d22eeaa..413a9e7 100644
--- a/src/dlg-ripper.c
+++ b/src/dlg-ripper.c
@@ -21,34 +21,17 @@
*/
#include <config.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
#include <math.h>
-
+#include <brasero/brasero-drive.h>
#include <gtk/gtk.h>
-#include <libgnome/libgnome.h>
-#include <libgnomeui/gnome-dialog.h>
-#include <libgnomeui/gnome-dialog-util.h>
-#include <libgnomeui/gnome-propertybox.h>
-#include <libgnomeui/gnome-pixmap.h>
-#include <libgnomevfs/gnome-vfs-ops.h>
-#include <libgnomevfs/gnome-vfs-utils.h>
-#include <glade/glade.h>
#include <gst/gst.h>
-#include "typedefs.h"
-#include "main.h"
#include "gconf-utils.h"
+#include "glib-utils.h"
#include "gtk-utils.h"
-#include "file-utils.h"
-#include "typedefs.h"
-#include "goo-window.h"
+#include "main.h"
#include "preferences.h"
-#include "track-info.h"
-#include "goo-player.h"
-#include "goo-cdrom.h"
+#include "typedefs.h"
+
#define TOC_OFFSET 150
#define GLADE_RIPPER_FILE "ripper_dialog.glade"
@@ -58,10 +41,11 @@
#define DEFAULT_OGG_QUALITY 0.5
#define DEFAULT_FLAC_COMPRESSION 5
#define BUFFER_SIZE 1024
+#define GET_WIDGET(x) _gtk_builder_get_widget (data->builder, (x))
+
typedef struct {
GooWindow *window;
- char *device;
char *destination;
GooFileFormat format;
GList *tracks;
@@ -69,7 +53,7 @@ typedef struct {
GList *current_track;
int current_track_n;
char *ext;
- GooCdrom *cdrom;
+ BraseroDrive *drive;
AlbumInfo *album;
GstElement *pipeline;
@@ -83,35 +67,31 @@ typedef struct {
int total_sectors;
int current_track_sectors;
int prev_tracks_sectors;
- char *current_file;
+ GFile *current_file;
gboolean ripping;
GTimer *timer;
double prev_remaining_time;
- GladeXML *gui;
-
+ GtkBuilder *builder;
GtkWidget *dialog;
- GtkWidget *r_progress_progressbar;
- GtkWidget *r_track_label;
} DialogData;
/* From lame.h */
typedef enum vbr_mode_e {
- vbr_off=0,
- vbr_mt, /* obsolete, same as vbr_mtrh */
- vbr_rh,
- vbr_abr,
- vbr_mtrh,
- vbr_max_indicator, /* Don't use this! It's used for sanity checks. */
- vbr_default=vbr_rh /* change this to change the default VBR mode of LAME */
+ vbr_off=0,
+ vbr_mt, /* obsolete, same as vbr_mtrh */
+ vbr_rh,
+ vbr_abr,
+ vbr_mtrh,
+ vbr_max_indicator, /* Don't use this! It's used for sanity checks. */
+ vbr_default=vbr_rh /* change this to change the default VBR mode of LAME */
} vbr_mode;
-/* called when the main dialog is closed. */
static void
-destroy_cb (GtkWidget *widget,
- DialogData *data)
+dialog_destroy_cb (GtkWidget *widget,
+ DialogData *data)
{
if (data->update_handle != 0) {
g_source_remove (data->update_handle);
@@ -119,7 +99,7 @@ destroy_cb (GtkWidget *widget,
}
if (data->ripping && (data->current_file != NULL))
- gnome_vfs_unlink (data->current_file);
+ g_file_delete (data->current_file, NULL, NULL);
if (data->pipeline != NULL) {
gst_element_set_state (data->pipeline, GST_STATE_NULL);
@@ -129,15 +109,12 @@ destroy_cb (GtkWidget *widget,
if (data->timer != NULL)
g_timer_destroy (data->timer);
- goo_cdrom_unlock_tray (data->cdrom);
- g_object_unref (data->cdrom);
-
- g_free (data->device);
+ brasero_drive_unlock (data->drive);
+ g_object_unref (data->drive);
g_free (data->destination);
- g_free (data->current_file);
+ _g_object_unref (data->current_file);
track_list_free (data->tracks);
-
- g_object_unref (data->gui);
+ g_object_unref (data->builder);
g_free (data);
}
@@ -151,7 +128,7 @@ rip_next_track (gpointer callback_data)
DialogData *data = callback_data;
TrackInfo *track;
- g_free (data->current_file);
+ _g_object_unref (data->current_file);
data->current_file = NULL;
gst_element_set_state (data->encoder, GST_STATE_NULL);
@@ -178,7 +155,7 @@ update_ui (gpointer callback_data)
ripped_tracks = data->current_track_sectors + data->prev_tracks_sectors;
fraction = (double) ripped_tracks / data->total_sectors;
- gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (data->r_progress_progressbar), fraction);
+ gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (GET_WIDGET ("progress_progressbar")), fraction);
/* wait a bit before predicting the remaining time. */
if ((fraction < 10e-3) || (ripped_tracks < 250)) {
@@ -206,7 +183,7 @@ update_ui (gpointer callback_data)
}
msg = g_strdup_printf (_("Extracting track: %d of %d %s"), data->current_track_n, data->n_tracks, (time_left != NULL ? time_left : ""));
- gtk_progress_bar_set_text (GTK_PROGRESS_BAR (data->r_progress_progressbar), msg);
+ gtk_progress_bar_set_text (GTK_PROGRESS_BAR (GET_WIDGET ("progress_progressbar")), msg);
g_free (msg);
g_free (time_left);
@@ -283,10 +260,9 @@ create_pipeline (DialogData *data)
data->pipeline = gst_pipeline_new ("pipeline");
- /*data->source = gst_element_make_from_uri (GST_URI_SRC, "cdda://1", "source");*/
data->source = gst_element_factory_make ("cdparanoiasrc", "source");
g_object_set (G_OBJECT (data->source),
- "device", data->device,
+ "device", brasero_drive_get_device (data->drive),
"read-speed", G_MAXINT,
NULL);
@@ -299,19 +275,16 @@ create_pipeline (DialogData *data)
switch (data->format) {
case GOO_FILE_FORMAT_OGG:
data->ext = "ogg";
-
data->encoder = gst_element_factory_make (OGG_ENCODER, "encoder");
ogg_quality = eel_gconf_get_float (PREF_ENCODER_OGG_QUALITY, DEFAULT_OGG_QUALITY);
g_object_set (data->encoder,
"quality", ogg_quality,
NULL);
-
data->container = gst_element_factory_make ("oggmux", "container");
break;
case GOO_FILE_FORMAT_FLAC:
data->ext = "flac";
-
data->encoder = data->container = gst_element_factory_make (FLAC_ENCODER, "encoder");
flac_compression = eel_gconf_get_integer (PREF_ENCODER_FLAC_COMPRESSION, DEFAULT_FLAC_COMPRESSION);
g_object_set (data->encoder,
@@ -321,12 +294,11 @@ create_pipeline (DialogData *data)
case GOO_FILE_FORMAT_WAVE:
data->ext = "wav";
-
data->encoder = data->container = gst_element_factory_make (WAVE_ENCODER, "encoder");
break;
}
- data->sink = gst_element_factory_make ("gnomevfssink", "sink");
+ data->sink = gst_element_factory_make ("giosink", "sink");
if (data->encoder == data->container) {
gst_bin_add_many (GST_BIN (data->pipeline), data->source, queue,
@@ -362,25 +334,102 @@ create_pipeline (DialogData *data)
}
-static char*
-get_destination_folder (DialogData *data)
+static gboolean
+valid_filename_char (char c)
{
- char *artist_filename;
- char *album_filename;
- char *result;
+ /* "$\'`\"\\!?* ()[]<>&|@#:;" */
+ return strchr ("/\\!?*:;'`\"", c) == NULL;
+}
- artist_filename = tracktitle_to_filename (data->album->artist);
- album_filename = tracktitle_to_filename (data->album->title);
-
- result = g_strconcat (data->destination, G_DIR_SEPARATOR_S,
- artist_filename, G_DIR_SEPARATOR_S,
- album_filename,
- NULL);
+/* Remove special characters from a track title in order to make it a
+ * valid filename. */
+char*
+tracktitle_to_filename (const char *trackname,
+ gboolean escape)
+{
+ char *filename, *f, *f2;
+ const char *t;
+ gboolean add_space;
+
+ if (trackname == NULL)
+ return NULL;
+
+ filename = g_new (char, strlen (trackname) + 1);
+
+ /* Substitute invalid characters with spaces. */
+
+ f = filename;
+ t = trackname;
+ while (*t != 0) {
+ gboolean invalid_char = FALSE;
+
+ while ((*t != 0) && ! valid_filename_char (*t)) {
+ invalid_char = TRUE;
+ t++;
+ }
+
+ if (invalid_char)
+ *f++ = ' ';
+
+ *f = *t;
+
+ if (*t != 0) {
+ f++;
+ t++;
+ }
+ }
+ *f = 0;
+
+ /* Remove double spaces. */
+ add_space = FALSE;
+ for (f = f2 = filename; *f != 0; f++)
+ if (*f != ' ') {
+ if (add_space) {
+ *f2++ = ' ';
+ add_space = FALSE;
+ }
+ *f2 = *f;
+ f2++;
+ } else
+ add_space = TRUE;
+ *f2 = 0;
+
+ if (escape) {
+ char *escaped;
+
+ escaped = g_uri_escape_string (filename, G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, TRUE);
+ g_free (filename);
+ filename = escaped;
+ }
+
+ return filename;
+}
+
+
+static GFile *
+get_destination_folder (DialogData *data)
+{
+ char *artist_filename;
+ char *album_filename;
+ char *uri;
+ GFile *folder;
+
+ artist_filename = tracktitle_to_filename (data->album->artist, TRUE);
+ album_filename = tracktitle_to_filename (data->album->title, TRUE);
+ uri = g_strconcat (data->destination,
+ G_DIR_SEPARATOR_S,
+ artist_filename,
+ G_DIR_SEPARATOR_S,
+ album_filename,
+ NULL);
+ folder = g_file_new_for_uri (uri);
+
+ g_free (uri);
g_free (artist_filename);
g_free (album_filename);
- return result;
+ return folder;
}
@@ -393,25 +442,18 @@ done_dialog_response_cb (GtkDialog *dialog,
gtk_widget_destroy (GTK_WIDGET (dialog));
- if ((button_number == GTK_RESPONSE_OK)
- && eel_gconf_get_boolean (PREF_RIPPER_VIEW_DISTINATION, FALSE)) {
- GError *error = NULL;
- char *folder = get_destination_folder (data);
- char *scheme = NULL;
- char *url = NULL;
+ if ((button_number == GTK_RESPONSE_OK) && eel_gconf_get_boolean (PREF_RIPPER_VIEW_DISTINATION, FALSE)) {
+ GFile *folder;
+ char *uri;
+ GError *error = NULL;
- scheme = gnome_vfs_get_uri_scheme (folder);
- if ((scheme == NULL) || (strcmp (scheme, "") == 0))
- url = gnome_vfs_get_uri_from_local_path (folder);
- else
- url = g_strdup (folder);
-
- if (! gnome_url_show (url, &error))
+ folder = get_destination_folder (data);
+ uri = g_file_get_uri (folder);
+ if (! gtk_show_uri (gtk_widget_get_screen (GTK_WIDGET (dialog)), uri, GDK_CURRENT_TIME, &error))
_gtk_error_dialog_from_gerror_run (GTK_WINDOW (data->window), _("Could not display the destination folder"), &error);
- g_free (scheme);
- g_free (url);
- g_free (folder);
+ g_free (uri);
+ g_object_unref (folder);
}
gtk_widget_destroy (data->dialog);
@@ -439,8 +481,8 @@ get_track_filename (TrackInfo *track,
{
char *filename, *track_filename;
- filename = tracktitle_to_filename (track->title);
- track_filename = g_strdup_printf ("%s.%%20%s.%s", zero_padded (track->number + 1), filename, ext);
+ filename = tracktitle_to_filename (track->title, FALSE);
+ track_filename = g_strdup_printf ("%s. %s.%s", zero_padded (track->number + 1), filename, ext);
g_free (filename);
return track_filename;
@@ -450,46 +492,33 @@ get_track_filename (TrackInfo *track,
static void
save_playlist (DialogData *data)
{
- char *folder;
- char *filename, *album_filename;
- GnomeVFSResult result;
- GnomeVFSHandle *handle;
+ GFile *folder;
+ char *album_filename;
+ char *playlist_filename;
+ GFile *file;
+ GOutputStream *stream;
folder = get_destination_folder (data);
- album_filename = tracktitle_to_filename (data->album->title);
- filename = g_strconcat ("file://", folder, "/", album_filename, ".pls", NULL);
- g_free (album_filename);
-
- gnome_vfs_unlink (filename);
+ album_filename = tracktitle_to_filename (data->album->title, FALSE);
+ playlist_filename = g_strconcat (album_filename, ".pls", NULL);
+ file = g_file_get_child (folder, playlist_filename);
- result = gnome_vfs_create (&handle,
- filename,
- GNOME_VFS_OPEN_WRITE,
- TRUE,
- PLS_PERMISSIONS);
+ g_file_delete (file, NULL, NULL);
- if (result == GNOME_VFS_OK) {
- GList *scan;
+ stream = (GOutputStream *) g_file_create (file, G_FILE_CREATE_NONE, NULL, NULL);
+ if (stream != NULL) {
char buffer[BUFFER_SIZE];
+ GList *scan;
int n = 0;
strcpy (buffer, "[playlist]\n");
- gnome_vfs_write (handle,
- buffer,
- strlen (buffer),
- NULL);
+ g_output_stream_write (stream, buffer, strlen (buffer), NULL, NULL);
sprintf (buffer, "NumberOfEntries=%d\n", data->n_tracks);
- gnome_vfs_write (handle,
- buffer,
- strlen (buffer),
- NULL);
+ g_output_stream_write (stream, buffer, strlen (buffer), NULL, NULL);
strcpy (buffer, "Version=2\n");
- gnome_vfs_write (handle,
- buffer,
- strlen (buffer),
- NULL);
+ g_output_stream_write (stream, buffer, strlen (buffer), NULL, NULL);
for (scan = data->tracks; scan; scan = scan->next) {
TrackInfo *track = scan->data;
@@ -499,47 +528,40 @@ save_playlist (DialogData *data)
n++;
track_filename = get_track_filename (track, data->ext);
- unescaped = gnome_vfs_unescape_string (track_filename, "");
+ unescaped = g_uri_unescape_string (track_filename, "");
sprintf (buffer, "File%d=%s\n", n, unescaped);
- gnome_vfs_write (handle,
- buffer,
- strlen (buffer),
- NULL);
+ g_output_stream_write (stream, buffer, strlen (buffer), NULL, NULL);
g_free (unescaped);
g_free (track_filename);
sprintf (buffer, "Title%d=%s - %s\n", n, data->album->artist, track->title);
- gnome_vfs_write (handle,
- buffer,
- strlen (buffer),
- NULL);
+ g_output_stream_write (stream, buffer, strlen (buffer), NULL, NULL);
sprintf (buffer, "Length%d=%d\n", n, track->min * 60 + track->sec);
- gnome_vfs_write (handle,
- buffer,
- strlen (buffer),
- NULL);
+ g_output_stream_write (stream, buffer, strlen (buffer), NULL, NULL);
}
- gnome_vfs_close (handle);
+ g_object_unref (stream);
}
- g_free (filename);
- g_free (folder);
+ g_object_unref (file);
+ g_free (playlist_filename);
+ g_free (album_filename);
+ g_object_unref (folder);
}
static void
rip_current_track (DialogData *data)
{
- TrackInfo *track;
- char *msg;
- char *filename;
- char *folder;
- GstEvent *event;
- GnomeVFSResult result;
+ TrackInfo *track;
+ char *msg;
+ GFile *folder;
+ GError *error = NULL;
+ char *filename;
+ GstEvent *event;
if (data->current_track == NULL) {
GtkWidget *d;
@@ -570,37 +592,33 @@ rip_current_track (DialogData *data)
track = data->current_track->data;
msg = g_markup_printf_escaped (_("<i>Extracting \"%s\"</i>"), track->title);
- gtk_label_set_markup (GTK_LABEL (data->r_track_label), msg);
+ gtk_label_set_markup (GTK_LABEL (GET_WIDGET ("track_label")), msg);
g_free (msg);
- /* Set the filename */
+ /* Set the destination file */
folder = get_destination_folder (data);
-
- if ((result = ensure_dir_exists (folder, DESTINATION_PERMISSIONS)) != GNOME_VFS_OK) {
- char *utf8_folder;
-
- utf8_folder = g_locale_to_utf8 (folder, -1, 0, 0, 0);
- _gtk_error_dialog_run (GTK_WINDOW (data->window),
- _("Could not extract tracks"),
- _("Could not create folder \"%s\"\n\n%s"),
- utf8_folder,
- gnome_vfs_result_to_string (result));
- g_free (utf8_folder);
- gtk_widget_destroy (data->dialog);
- return;
+ if (! g_file_make_directory_with_parents (folder, NULL, &error)) {
+ if (! g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS)) {
+ _gtk_error_dialog_from_gerror_show (GTK_WINDOW (data->window),
+ _("Could not extract tracks"),
+ &error);
+ gtk_widget_destroy (data->dialog);
+ return;
+ }
}
- g_free (data->current_file);
+ _g_object_unref (data->current_file);
filename = get_track_filename (track, data->ext);
- data->current_file = g_strconcat (folder, G_DIR_SEPARATOR_S, filename, NULL);
+ data->current_file = g_file_get_child (folder, filename);
+
g_free (filename);
- g_free (folder);
+ g_object_unref (folder);
- gnome_vfs_unlink (data->current_file);
+ g_file_delete (data->current_file, NULL, NULL);
gst_element_set_state (data->sink, GST_STATE_NULL);
- g_object_set (G_OBJECT (data->sink), "location", data->current_file, NULL);
+ g_object_set (G_OBJECT (data->sink), "file", data->current_file, NULL);
/* Set track tags. */
@@ -659,15 +677,13 @@ start_ripper (DialogData *data)
GList *scan;
data->ripping = TRUE;
+ brasero_drive_lock (data->drive, _("Extracting disc tracks"), NULL);
- goo_cdrom_lock_tray (data->cdrom);
create_pipeline (data);
-
data->prev_tracks_sectors = 0;
data->total_sectors = 0;
for (scan = data->tracks; scan; scan = scan->next) {
TrackInfo *track = scan->data;
-
data->total_sectors += track->sectors;
}
data->current_track_n = 1;
@@ -678,67 +694,45 @@ start_ripper (DialogData *data)
}
-/* create the main dialog. */
void
dlg_ripper (GooWindow *window,
GList *tracks)
{
- DialogData *data;
- GtkWidget *btn_cancel;
- GooPlayer *player;
+ GooPlayer *player;
+ DialogData *data;
- data = g_new0 (DialogData, 1);
- data->window = window;
- data->gui = glade_xml_new (GOO_GLADEDIR "/" GLADE_RIPPER_FILE, NULL, NULL);
- if (!data->gui) {
- g_warning ("Could not find " GLADE_RIPPER_FILE "\n");
- g_free (data);
- return;
- }
-
goo_window_stop (window);
player = goo_window_get_player (window);
- data->destination = eel_gconf_get_path (PREF_EXTRACT_DESTINATION, "");
- if ((data->destination == NULL) || (strcmp (data->destination, "") == 0)) {
- char *tmp;
-
- tmp = xdg_user_dir_lookup ("MUSIC");
- data->destination = get_uri_from_local_path (tmp);
- g_free (tmp);
- }
- data->device = g_strdup (goo_player_get_device (player));
+ data = g_new0 (DialogData, 1);
+ data->window = window;
+ data->builder = _gtk_builder_new_from_file ("ripper.ui", "");
+ data->dialog = GET_WIDGET ("ripper_dialog");
+ data->destination = eel_gconf_get_uri (PREF_EXTRACT_DESTINATION, "");
+ if ((data->destination == NULL) || (strcmp (data->destination, "") == 0))
+ data->destination = g_filename_to_uri (g_get_user_special_dir (G_USER_DIRECTORY_MUSIC), NULL, NULL);
+ data->drive = g_object_ref (goo_player_get_drive (player));
data->format = pref_get_file_format ();
data->album = goo_player_get_album (player);
- if (tracks == NULL)
- data->tracks = track_list_dup (data->album->tracks);
- else
+ if (tracks != NULL)
data->tracks = track_list_dup (tracks);
+ else
+ data->tracks = track_list_dup (data->album->tracks);
data->n_tracks = g_list_length (data->tracks);
-
data->update_handle = 0;
data->timer = g_timer_new ();
- data->cdrom = goo_cdrom_new (data->device);
-
- /* Get the widgets. */
-
- data->dialog = glade_xml_get_widget (data->gui, "ripper_dialog");
- data->r_progress_progressbar = glade_xml_get_widget (data->gui, "r_progress_progressbar");
- data->r_track_label = glade_xml_get_widget (data->gui, "r_track_label");
- btn_cancel = glade_xml_get_widget (data->gui, "r_cancelbutton");
/* Set widgets data. */
- gtk_label_set_ellipsize (GTK_LABEL (data->r_track_label),
- PANGO_ELLIPSIZE_END);
+ gtk_label_set_ellipsize (GTK_LABEL (GET_WIDGET ("track_label")), PANGO_ELLIPSIZE_END);
/* Set the signals handlers. */
- g_signal_connect (G_OBJECT (data->dialog),
+ g_signal_connect (data->dialog,
"destroy",
- G_CALLBACK (destroy_cb),
+ G_CALLBACK (dialog_destroy_cb),
data);
- g_signal_connect_swapped (G_OBJECT (btn_cancel),
+ g_signal_connect_swapped (GET_WIDGET ("cancel_button"),
"clicked",
G_CALLBACK (gtk_widget_destroy),
data->dialog);
diff --git a/src/dlg-ripper.h b/src/dlg-ripper.h
index e68b716..74b99f0 100644
--- a/src/dlg-ripper.h
+++ b/src/dlg-ripper.h
@@ -23,7 +23,6 @@
#ifndef DLG_RIPPER_H
#define DLG_RIPPER_H
-#include "typedefs.h"
#include "goo-window.h"
void dlg_ripper (GooWindow *window,
diff --git a/src/gconf-utils.c b/src/gconf-utils.c
index 93f9c83..0ec519a 100644
--- a/src/gconf-utils.c
+++ b/src/gconf-utils.c
@@ -1,9 +1,9 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
- * Goo
+ * gThumb
*
- * Copyright (C) 2004 Free Software Foundation, Inc.
+ * Copyright (C) 2001, 2002 The Free Software Foundation, Inc.
*
* 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
@@ -46,7 +46,7 @@
#include <config.h>
#include <string.h>
-#include <gnome.h>
+#include <errno.h>
#include <gconf/gconf-client.h>
#include <gconf/gconf.h>
#include "gconf-utils.h"
@@ -54,6 +54,9 @@
#include "goo-error.h"
#include "glib-utils.h"
+#define HOME_DIR "~"
+
+
static GConfClient *global_gconf_client = NULL;
@@ -106,7 +109,6 @@ eel_gconf_handle_error (GError **error)
"GConf error:\n %s\n"
"All further errors "
"shown only on terminal",
- "%s",
(*error)->message);
}
g_error_free (*error);
@@ -231,6 +233,37 @@ eel_gconf_get_integer (const char *key,
}
+int
+eel_gconf_get_enum (const char *key,
+ GType enum_type,
+ int def_val)
+{
+ GEnumValue *def_enum_val;
+ char *value_nick;
+ GEnumValue *value;
+
+ def_enum_val = _g_enum_type_get_value (enum_type, def_val);
+ value_nick = eel_gconf_get_string (key, def_enum_val->value_nick);
+ value = _g_enum_type_get_value_by_nick (enum_type, value_nick);
+ g_free (value_nick);
+
+ return (value != NULL) ? value->value : 0;
+}
+
+
+void
+eel_gconf_set_enum (const char *key,
+ GType enum_type,
+ int value)
+{
+ GEnumValue *enum_value;
+
+ enum_value = _g_enum_type_get_value (enum_type, value);
+ if (enum_value != NULL)
+ eel_gconf_set_string (key, enum_value->value_nick);
+}
+
+
void
eel_gconf_set_float (const char *key,
float float_value)
@@ -316,15 +349,16 @@ eel_gconf_get_string (const char *key,
val = gconf_client_get_string (client, key, &error);
- if (val != NULL) {
+ /* Return the default value if the key does not exist,
+ or if it is empty. */
+ if (val != NULL && strcmp (val, "")) {
g_return_val_if_fail (error == NULL, result);
g_free (result);
result = g_strdup (val);
- g_free (val);
} else if (error != NULL)
eel_gconf_handle_error (&error);
-
+
return result;
}
@@ -416,7 +450,7 @@ eel_gconf_get_path_list (const char *key)
slist = NULL;
for (scan = str_slist; scan; scan = scan->next) {
char *str = scan->data;
- char *path = _g_substitute (str, '~', g_get_home_dir ());
+ char *path = _g_replace (str, HOME_DIR, g_get_home_dir ());
slist = g_slist_prepend (slist, path);
}
@@ -427,53 +461,6 @@ eel_gconf_get_path_list (const char *key)
}
-static char *
-tilde_compress (const char *path)
-{
- const char *home_dir = g_get_home_dir();
- int home_dir_l = strlen (home_dir);
- int ntilde = 0;
- const char *scan;
- int path_l, result_l;
- char *result, *scan2;
-
- if (path == NULL)
- return NULL;
-
- path_l = strlen (path);
- for (scan = path; scan != NULL; scan++) {
- if (path_l - (scan - path) < home_dir_l)
- break;
- if (strncmp (scan, home_dir, home_dir_l) == 0)
- ntilde++;
- }
-
- if (ntilde == 0)
- return g_strdup (path);
-
- result_l = strlen (path) + ntilde - (ntilde * home_dir_l);
- result = g_new (char, result_l + 1);
-
- for (scan = path, scan2 = result; scan != NULL; scan2++) {
- if (path_l - (scan - path) < home_dir_l) {
- strcpy (scan2, scan);
- scan2 += strlen (scan);
- break;
- }
- if (strncmp (scan, home_dir, home_dir_l) == 0) {
- *scan2 = '~';
- scan += home_dir_l;
- } else {
- *scan2 = *scan;
- scan++;
- }
- }
- *scan2 = 0;
-
- return result;
-}
-
-
void
eel_gconf_set_path_list (const char *key,
const GSList *string_list_value)
@@ -484,7 +471,7 @@ eel_gconf_set_path_list (const char *key,
path_slist = NULL;
for (scan = string_list_value; scan; scan = scan->next) {
char *value = scan->data;
- char *path = tilde_compress (value);
+ char *path = _g_replace (value, g_get_home_dir (), HOME_DIR);
path_slist = g_slist_prepend (path_slist, path);
}
path_slist = g_slist_reverse (path_slist);
@@ -544,26 +531,67 @@ char *
eel_gconf_get_path (const char *key,
const char *def_val)
{
+ char *value;
char *path;
- char *no_tilde_path;
-
- path = eel_gconf_get_string (key, def_val);
- no_tilde_path = _g_substitute (path, '~', g_get_home_dir ());
- g_free (path);
+
+ value = eel_gconf_get_string (key, def_val);
+ path = _g_replace (value, HOME_DIR, g_get_home_dir ());
+ g_free (value);
- return no_tilde_path;
+ return path;
}
void
eel_gconf_set_path (const char *key,
- const char *value)
+ const char *path)
{
- char *tilde_path;
+ char *value;
+
+ value = _g_replace (path, g_get_home_dir (), HOME_DIR);
+ eel_gconf_set_string (key, value);
+ g_free (value);
+}
+
+
+char *
+eel_gconf_get_uri (const char *key,
+ const char *def_val)
+{
+ GRegex *regex;
+ char *s;
+ char *home;
+ char *uri;
+
+ regex = g_regex_new ("~", 0, 0, NULL);
+ s = eel_gconf_get_string (key, def_val);
+ home = g_uri_escape_string (g_get_home_dir (), G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, TRUE);
+ uri = g_regex_replace_literal (regex, s, -1, 0, home, 0, NULL);
+
+ g_free (home);
+ g_free (s);
+ g_regex_unref (regex);
+
+ return uri;
+}
+
+
+void
+eel_gconf_set_uri (const char *key,
+ const char *value)
+{
+ char *home;
+ GRegex *regex;
+ char *s;
+
+ home = g_uri_escape_string (g_get_home_dir (), G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, TRUE);
+ regex = g_regex_new (home, 0, 0, NULL);
+ s = g_regex_replace_literal (regex, value, -1, 0, "~", 0, NULL);
+ eel_gconf_set_string (key, s);
- tilde_path = tilde_compress (value);
- eel_gconf_set_string (key, tilde_path);
- g_free (tilde_path);
+ g_free (s);
+ g_regex_unref (regex);
+ g_free (home);
}
diff --git a/src/gconf-utils.h b/src/gconf-utils.h
index 31ae3ee..853bea1 100644
--- a/src/gconf-utils.h
+++ b/src/gconf-utils.h
@@ -1,9 +1,9 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
- * Goo
+ * gThumb
*
- * Copyright (C) 2004 Free Software Foundation, Inc.
+ * Copyright (C) 2001, 2002 The Free Software Foundation, Inc.
*
* 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
@@ -56,90 +56,67 @@ G_BEGIN_DECLS
#define EEL_GCONF_UNDEFINED_CONNECTION 0
GConfClient *eel_gconf_client_get_global (void);
-
void eel_global_client_free (void);
-
gboolean eel_gconf_handle_error (GError **error);
-
gboolean eel_gconf_get_boolean (const char *key,
gboolean def_val);
-
void eel_gconf_set_boolean (const char *key,
gboolean value);
-
int eel_gconf_get_integer (const char *key,
int def_val);
-
void eel_gconf_set_integer (const char *key,
int value);
-
+int eel_gconf_get_enum (const char *key,
+ GType enum_type,
+ int def_val);
+void eel_gconf_set_enum (const char *key,
+ GType enum_type,
+ int value);
float eel_gconf_get_float (const char *key,
float def_val);
-
void eel_gconf_set_float (const char *key,
float value);
-
char * eel_gconf_get_string (const char *key,
const char *def_val);
-
void eel_gconf_set_string (const char *key,
const char *value);
-
char * eel_gconf_get_path (const char *key,
const char *def_val);
-
void eel_gconf_set_path (const char *key,
const char *value);
-
+char * eel_gconf_get_uri (const char *key,
+ const char *def_val);
+void eel_gconf_set_uri (const char *key,
+ const char *value);
char * eel_gconf_get_locale_string (const char *key,
const char *def_val);
-
void eel_gconf_set_locale_string (const char *key,
const char *value);
-
GSList * eel_gconf_get_string_list (const char *key);
-
void eel_gconf_set_string_list (const char *key,
const GSList *string_list_value);
-
GSList * eel_gconf_get_path_list (const char *key);
-
void eel_gconf_set_path_list (const char *key,
const GSList *string_list_value);
-
GSList * eel_gconf_get_locale_string_list(const char *key);
-
void eel_gconf_set_locale_string_list(const char *key,
const GSList *string_list_value);
-
gboolean eel_gconf_is_default (const char *key);
-
gboolean eel_gconf_monitor_add (const char *directory);
-
gboolean eel_gconf_monitor_remove (const char *directory);
-
void eel_gconf_preload_cache (const char *directory,
GConfClientPreloadType preload_type);
-
void eel_gconf_suggest_sync (void);
-
GConfValue* eel_gconf_get_value (const char *key);
-
GConfValue* eel_gconf_get_default_value (const char *key);
-
gboolean eel_gconf_value_is_equal (const GConfValue *a,
const GConfValue *b);
-
void eel_gconf_value_free (GConfValue *value);
-
guint eel_gconf_notification_add (const char *key,
GConfClientNotifyFunc notification_callback,
gpointer callback_data);
-
void eel_gconf_notification_remove (guint notification_id);
-
GSList * eel_gconf_value_get_string_list (const GConfValue *value);
-
void eel_gconf_value_set_string_list (GConfValue *value,
const GSList *string_list);
diff --git a/src/gio-utils.c b/src/gio-utils.c
new file mode 100644
index 0000000..0399ca5
--- /dev/null
+++ b/src/gio-utils.c
@@ -0,0 +1,458 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * GThumb
+ *
+ * Copyright (C) 2008 Free Software Foundation, Inc.
+ *
+ * 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 Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+#include "glib-utils.h"
+#include "gio-utils.h"
+
+
+#define BUFFER_SIZE 4096
+
+
+gboolean
+g_load_file_in_buffer (GFile *file,
+ void **buffer,
+ gsize *size,
+ GError **error)
+{
+ GFileInputStream *istream;
+ gboolean retval;
+ void *local_buffer;
+ gsize count;
+ gssize n;
+ char tmp_buffer[BUFFER_SIZE];
+
+ istream = g_file_read (file, NULL, error);
+ if (istream == NULL)
+ return FALSE;
+
+ retval = FALSE;
+ local_buffer = NULL;
+ count = 0;
+ for (;;) {
+ n = g_input_stream_read (G_INPUT_STREAM (istream), tmp_buffer, BUFFER_SIZE, NULL, error);
+ if (n < 0) {
+ g_free (local_buffer);
+ retval = FALSE;
+ break;
+ }
+ else if (n == 0) {
+ *buffer = local_buffer;
+ *size = count;
+ retval = TRUE;
+ break;
+ }
+
+ local_buffer = g_realloc (local_buffer, count + n + 1);
+ memcpy (local_buffer + count, tmp_buffer, n);
+ count += n;
+ }
+
+ g_object_unref (istream);
+
+ return retval;
+}
+
+
+typedef struct {
+ int io_priority;
+ GCancellable *cancellable;
+ BufferReadyCallback callback;
+ gpointer user_data;
+ GInputStream *stream;
+ guchar tmp_buffer[BUFFER_SIZE];
+ void *buffer;
+ gsize count;
+} LoadData;
+
+
+static void
+load_data_free (LoadData *load_data)
+{
+ if (load_data->stream != NULL)
+ g_object_unref (load_data->stream);
+ g_free (load_data->buffer);
+ g_free (load_data);
+}
+
+
+static void
+load_file__stream_read_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ LoadData *load_data = user_data;
+ GError *error = NULL;
+ gssize count;
+
+ count = g_input_stream_read_finish (load_data->stream, result, &error);
+ if (count < 0) {
+ load_data->callback (NULL, -1, error, load_data->user_data);
+ load_data_free (load_data);
+ return;
+ }
+ else if (count == 0) {
+ if (load_data->buffer != NULL)
+ ((char *)load_data->buffer)[load_data->count] = 0;
+ load_data->callback (load_data->buffer, load_data->count, NULL, load_data->user_data);
+ load_data_free (load_data);
+ return;
+ }
+
+ load_data->buffer = g_realloc (load_data->buffer, load_data->count + count + 1);
+ memcpy (load_data->buffer + load_data->count, load_data->tmp_buffer, count);
+ load_data->count += count;
+
+ g_input_stream_read_async (load_data->stream,
+ load_data->tmp_buffer,
+ BUFFER_SIZE,
+ load_data->io_priority,
+ load_data->cancellable,
+ load_file__stream_read_cb,
+ load_data);
+}
+
+
+static void
+load_file__file_read_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ LoadData *load_data = user_data;
+ GError *error = NULL;
+
+ load_data->stream = (GInputStream *) g_file_read_finish (G_FILE (source_object), result, &error);
+ if (load_data->stream == NULL) {
+ load_data->callback (NULL, -1, error, load_data->user_data);
+ load_data_free (load_data);
+ return;
+ }
+
+ load_data->count = 0;
+ g_input_stream_read_async (load_data->stream,
+ load_data->tmp_buffer,
+ BUFFER_SIZE,
+ load_data->io_priority,
+ load_data->cancellable,
+ load_file__stream_read_cb,
+ load_data);
+}
+
+
+void
+g_load_file_async (GFile *file,
+ int io_priority,
+ GCancellable *cancellable,
+ BufferReadyCallback callback,
+ gpointer user_data)
+{
+ LoadData *load_data;
+
+ load_data = g_new0 (LoadData, 1);
+ load_data->io_priority = io_priority;
+ load_data->cancellable = cancellable;
+ load_data->callback = callback;
+ load_data->user_data = user_data;
+
+ g_file_read_async (file, io_priority, cancellable, load_file__file_read_cb, load_data);
+}
+
+
+/* -- g_write_file_async -- */
+
+
+typedef struct {
+ int io_priority;
+ GCancellable *cancellable;
+ BufferReadyCallback callback;
+ gpointer user_data;
+ void *buffer;
+ gsize count;
+ gsize written;
+ GError *error;
+} WriteData;
+
+
+static void
+write_data_free (WriteData *write_data)
+{
+ g_free (write_data);
+}
+
+
+static void
+write_file__notify (gpointer user_data)
+{
+ WriteData *write_data = user_data;
+
+ write_data->callback (write_data->buffer, write_data->count, write_data->error, write_data->user_data);
+ write_data_free (write_data);
+}
+
+
+static void
+write_file__stream_flush_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GOutputStream *stream = (GOutputStream *) source_object;
+ WriteData *write_data = user_data;
+ GError *error = NULL;
+
+ g_output_stream_flush_finish (stream, result, &error);
+ write_data->error = error;
+ g_object_unref (stream);
+
+ call_when_idle (write_file__notify, write_data);
+}
+
+
+static void
+write_file__stream_write_ready_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GOutputStream *stream = (GOutputStream *) source_object;
+ WriteData *write_data = user_data;
+ GError *error = NULL;
+ gssize count;
+
+ count = g_output_stream_write_finish (stream, result, &error);
+ write_data->written += count;
+
+ if ((count == 0) || (write_data->written == write_data->count)) {
+ g_output_stream_flush_async (stream,
+ write_data->io_priority,
+ write_data->cancellable,
+ write_file__stream_flush_cb,
+ user_data);
+ return;
+ }
+
+ g_output_stream_write_async (stream,
+ write_data->buffer + write_data->written,
+ write_data->count - write_data->written,
+ write_data->io_priority,
+ write_data->cancellable,
+ write_file__stream_write_ready_cb,
+ write_data);
+}
+
+
+static void
+write_file__replace_ready_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ WriteData *write_data = user_data;
+ GOutputStream *stream;
+ GError *error = NULL;
+
+ stream = (GOutputStream*) g_file_replace_finish ((GFile*) source_object, result, &error);
+ if (stream == NULL) {
+ write_data->callback (write_data->buffer, write_data->count, error, write_data->user_data);
+ write_data_free (write_data);
+ return;
+ }
+
+ write_data->written = 0;
+ g_output_stream_write_async (stream,
+ write_data->buffer,
+ write_data->count,
+ write_data->io_priority,
+ write_data->cancellable,
+ write_file__stream_write_ready_cb,
+ write_data);
+}
+
+
+void
+g_write_file_async (GFile *file,
+ void *buffer,
+ gsize count,
+ int io_priority,
+ GCancellable *cancellable,
+ BufferReadyCallback callback,
+ gpointer user_data)
+{
+ WriteData *write_data;
+
+ write_data = g_new0 (WriteData, 1);
+ write_data->buffer = buffer;
+ write_data->count = count;
+ write_data->io_priority = io_priority;
+ write_data->cancellable = cancellable;
+ write_data->callback = callback;
+ write_data->user_data = user_data;
+
+ g_file_replace_async (file, NULL, FALSE, 0, io_priority, cancellable, write_file__replace_ready_cb, write_data);
+}
+
+
+GFile *
+_g_file_create_unique (GFile *parent,
+ const char *display_name,
+ const char *suffix,
+ GError **error)
+{
+ GFile *file = NULL;
+ GError *local_error = NULL;
+ int n;
+ GFileOutputStream *stream;
+
+ file = g_file_get_child_for_display_name (parent, display_name, &local_error);
+ n = 0;
+ do {
+ char *new_display_name;
+
+ if (file != NULL)
+ g_object_unref (file);
+
+ n++;
+ if (n == 1)
+ new_display_name = g_strdup_printf ("%s%s", display_name, suffix);
+ else
+ new_display_name = g_strdup_printf ("%s %d%s", display_name, n, suffix);
+
+ file = g_file_get_child_for_display_name (parent, new_display_name, &local_error);
+ if (local_error == NULL)
+ stream = g_file_create (file, 0, NULL, &local_error);
+
+ if ((stream == NULL) && g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_EXISTS))
+ g_clear_error (&local_error);
+
+ g_free (new_display_name);
+ }
+ while ((stream == NULL) && (local_error == NULL));
+
+ if (stream == NULL) {
+ g_object_unref (file);
+ file = NULL;
+ }
+ else
+ g_object_unref (stream);
+
+ return file;
+}
+
+
+GFile *
+_g_directory_create_unique (GFile *parent,
+ const char *display_name,
+ const char *suffix,
+ GError **error)
+{
+ GFile *file = NULL;
+ gboolean created = FALSE;
+ GError *local_error = NULL;
+ int n;
+
+ file = g_file_get_child_for_display_name (parent, display_name, &local_error);
+ if (file == NULL) {
+ g_propagate_error (error, local_error);
+ return NULL;
+ }
+
+ n = 0;
+ do {
+ char *new_display_name;
+
+ if (file != NULL)
+ g_object_unref (file);
+
+ n++;
+ if (n == 1)
+ new_display_name = g_strdup_printf ("%s%s", display_name, suffix);
+ else
+ new_display_name = g_strdup_printf ("%s %d%s", display_name, n, suffix);
+
+ file = g_file_get_child_for_display_name (parent, new_display_name, &local_error);
+ if (local_error == NULL)
+ created = g_file_make_directory (file, NULL, &local_error);
+
+ if (! created && g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_EXISTS))
+ g_clear_error (&local_error);
+
+ g_free (new_display_name);
+ }
+ while (! created && (local_error == NULL));
+
+ if (local_error != NULL) {
+ g_object_unref (file);
+ file = NULL;
+ }
+
+ if (local_error != NULL)
+ g_propagate_error (error, local_error);
+
+ return file;
+}
+
+
+/* -- g_write_file -- */
+
+
+gboolean
+g_write_file (GFile *file,
+ gboolean make_backup,
+ GFileCreateFlags flags,
+ void *buffer,
+ gsize count,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean success;
+ GOutputStream *stream;
+
+ stream = (GOutputStream *) g_file_replace (file, NULL, make_backup, flags, cancellable, error);
+ if (stream != NULL)
+ success = g_output_stream_write_all (stream, buffer, count, NULL, cancellable, error);
+ else
+ success = FALSE;
+
+ _g_object_unref (stream);
+
+ return success;
+}
+
+
+gboolean
+_g_directory_make (GFile *file,
+ guint32 unix_mode,
+ GError **error)
+{
+ if (! g_file_make_directory (file, NULL, error)) {
+ if (! (*error)->code == G_IO_ERROR_EXISTS)
+ return FALSE;
+ g_clear_error (error);
+ }
+
+ return g_file_set_attribute_uint32 (file,
+ G_FILE_ATTRIBUTE_UNIX_MODE,
+ unix_mode,
+ G_FILE_QUERY_INFO_NONE,
+ NULL,
+ error);
+}
diff --git a/src/gio-utils.h b/src/gio-utils.h
new file mode 100644
index 0000000..332b1ae
--- /dev/null
+++ b/src/gio-utils.h
@@ -0,0 +1,75 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * GThumb
+ *
+ * Copyright (C) 2008 Free Software Foundation, Inc.
+ *
+ * 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 Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _GIO_UTILS_H
+#define _GIO_UTILS_H
+
+#include <glib.h>
+#include <gio/gio.h>
+#include "typedefs.h"
+
+G_BEGIN_DECLS
+
+/* callback types */
+
+typedef void (*BufferReadyCallback) (void *buffer,
+ gsize count,
+ GError *error,
+ gpointer user_data);
+
+/* -- load/write/create file -- */
+
+gboolean g_load_file_in_buffer (GFile *file,
+ void **buffer,
+ gsize *size,
+ GError **error);
+void g_load_file_async (GFile *file,
+ int io_priority,
+ GCancellable *cancellable,
+ BufferReadyCallback callback,
+ gpointer user_data);
+gboolean g_write_file (GFile *file,
+ gboolean make_backup,
+ GFileCreateFlags flags,
+ void *buffer,
+ gsize count,
+ GCancellable *cancellable,
+ GError **error);
+void g_write_file_async (GFile *file,
+ void *buffer,
+ gsize count,
+ int io_priority,
+ GCancellable *cancellable,
+ BufferReadyCallback callback,
+ gpointer user_data);
+GFile * _g_file_create_unique (GFile *parent,
+ const char *display_name,
+ const char *suffix,
+ GError **error);
+GFile * _g_directory_create_unique (GFile *parent,
+ const char *display_name,
+ const char *suffix,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* _GIO_UTILS_H */
diff --git a/src/glib-utils.c b/src/glib-utils.c
index 965b2b5..dc4e2b1 100644
--- a/src/glib-utils.c
+++ b/src/glib-utils.c
@@ -1,9 +1,9 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
- * Goo
+ * GThumb
*
- * Copyright (C) 2001, 2003, 2004 Free Software Foundation, Inc.
+ * Copyright (C) 2001-2008 Free Software Foundation, Inc.
*
* 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
@@ -20,12 +20,510 @@
* Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
*/
-#include <string.h>
#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
#include <glib.h>
+#include <glib/gi18n.h>
#include <glib/gprintf.h>
+#include <gio/gio.h>
#include "glib-utils.h"
+#define MAX_PATTERNS 128
+
+
+/* gobject utils*/
+
+
+gpointer
+_g_object_ref (gpointer object)
+{
+ if (object != NULL)
+ return g_object_ref (object);
+ else
+ return NULL;
+}
+
+
+void
+_g_object_unref (gpointer object)
+{
+ if (object != NULL)
+ g_object_unref (object);
+}
+
+
+void
+_g_object_clear (gpointer object)
+{
+ gpointer *object_p = (gpointer *) object;
+
+ if ((object_p != NULL) && (*object_p != NULL)) {
+ g_object_unref (*object_p);
+ *object_p = NULL;
+ }
+}
+
+
+GList *
+_g_object_list_ref (GList *list)
+{
+ GList *new_list;
+
+ if (list == NULL)
+ return NULL;
+
+ new_list = g_list_copy (list);
+ g_list_foreach (new_list, (GFunc) g_object_ref, NULL);
+
+ return new_list;
+}
+
+
+void
+_g_object_list_unref (GList *list)
+{
+ g_list_foreach (list, (GFunc) g_object_unref, NULL);
+ g_list_free (list);
+}
+
+
+GType
+g_object_list_get_type (void)
+{
+ static GType type = 0;
+
+ if (type == 0)
+ type = g_boxed_type_register_static ("GObjectList",
+ (GBoxedCopyFunc) _g_object_list_ref,
+ (GBoxedFreeFunc) _g_object_list_unref);
+
+ return type;
+}
+
+
+GEnumValue *
+_g_enum_type_get_value (GType enum_type,
+ int value)
+{
+ GEnumClass *class;
+ GEnumValue *enum_value;
+
+ class = G_ENUM_CLASS (g_type_class_ref (enum_type));
+ enum_value = g_enum_get_value (class, value);
+ g_type_class_unref (class);
+
+ return enum_value;
+}
+
+
+GEnumValue *
+_g_enum_type_get_value_by_nick (GType enum_type,
+ const char *nick)
+{
+ GEnumClass *class;
+ GEnumValue *enum_value;
+
+ class = G_ENUM_CLASS (g_type_class_ref (enum_type));
+ enum_value = g_enum_get_value_by_nick (class, nick);
+ g_type_class_unref (class);
+
+ return enum_value;
+}
+
+
+/* idle callback */
+
+
+IdleCall*
+idle_call_new (DataFunc func,
+ gpointer data)
+{
+ IdleCall *call = g_new0 (IdleCall, 1);
+ call->func = func;
+ call->data = data;
+ return call;
+}
+
+
+void
+idle_call_free (IdleCall *call)
+{
+ g_free (call);
+}
+
+
+static gboolean
+idle_call_exec_cb (gpointer data)
+{
+ IdleCall *call = data;
+ (*call->func) (call->data);
+ return FALSE;
+}
+
+
+guint
+idle_call_exec (IdleCall *call,
+ gboolean use_idle_cb)
+{
+ if (use_idle_cb)
+ return g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
+ idle_call_exec_cb,
+ call,
+ (GDestroyNotify) idle_call_free);
+ else {
+ (*call->func) (call->data);
+ idle_call_free (call);
+ return 0;
+ }
+}
+
+
+guint
+call_when_idle (DataFunc func,
+ gpointer data)
+{
+ return idle_call_exec (idle_call_new (func, data), TRUE);
+}
+
+
+typedef struct {
+ gpointer object;
+ ReadyCallback ready_func;
+ gpointer user_data;
+ GError *error;
+ guint id;
+} ObjectReadyData;
+
+
+static gboolean
+exec_object_ready_func (gpointer user_data)
+{
+ ObjectReadyData *data = user_data;
+
+ g_source_remove (data->id);
+ data->ready_func (G_OBJECT (data->object), data->error, data->user_data);
+ g_free (data);
+
+ return FALSE;
+}
+
+
+void
+object_ready_with_error (gpointer object,
+ ReadyCallback ready_func,
+ gpointer user_data,
+ GError *error)
+{
+ ObjectReadyData *data;
+
+ data = g_new0 (ObjectReadyData, 1);
+
+ data->object = object;
+ data->ready_func = ready_func;
+ data->user_data = user_data;
+ data->error = error;
+ data->id = g_idle_add (exec_object_ready_func, data);
+}
+
+
+typedef struct {
+ ReadyFunc ready_func;
+ gpointer user_data;
+ GError *error;
+ guint id;
+} ReadyData;
+
+
+static gboolean
+exec_ready_func (gpointer user_data)
+{
+ ReadyData *data = user_data;
+
+ g_source_remove (data->id);
+ data->ready_func (data->error, data->user_data);
+ g_free (data);
+
+ return FALSE;
+}
+
+
+void
+ready_with_error (ReadyFunc ready_func,
+ gpointer user_data,
+ GError *error)
+{
+ ReadyData *data;
+
+ data = g_new0 (ReadyData, 1);
+ data->ready_func = ready_func;
+ data->user_data = user_data;
+ data->error = error;
+ data->id = g_idle_add (exec_ready_func, data);
+}
+
+
+/* debug */
+
+
+void
+debug (const char *file,
+ int line,
+ const char *function,
+ const char *format,
+ ...)
+{
+#ifdef DEBUG
+ static gboolean first_time = 0;
+ static gboolean print_debug_info = 0;
+ va_list args;
+ char *str;
+
+ if (! first_time) {
+ first_time = 1;
+ if (g_getenv ("GTHUMB_DEBUG"))
+ print_debug_info = 1;
+ }
+
+ if (! print_debug_info)
+ return;
+
+ g_return_if_fail (format != NULL);
+
+ va_start (args, format);
+ str = g_strdup_vprintf (format, args);
+ va_end (args);
+
+ g_fprintf (stderr, "[%s] %s:%d (%s):\n\t%s\n", g_get_prgname(), file, line, function, str);
+
+ g_free (str);
+#endif
+}
+
+
+void
+performance (const char *file,
+ int line,
+ const char *function,
+ const char *format,
+ ...)
+{
+#ifdef DEBUG
+ va_list args;
+ char *formatted, *str, *filename;
+
+ filename = g_path_get_basename (file);
+
+ va_start (args, format);
+ formatted = g_strdup_vprintf (format, args);
+ va_end (args);
+
+ str = g_strdup_printf ("MARK: %s: (%s:%d) [%s] : %s", g_get_prgname(), filename, line, function, formatted);
+ g_free (formatted);
+
+ access (str, F_OK);
+ g_free (str);
+ g_free (filename);
+#endif
+}
+
+
+/* GTimeVal utils */
+
+
+int
+_g_time_val_cmp (GTimeVal *a,
+ GTimeVal *b)
+{
+ if (a->tv_sec == b->tv_sec) {
+ if (a->tv_usec == b->tv_usec)
+ return 0;
+ else
+ return a->tv_usec > b->tv_usec ? 1 : -1;
+ }
+ else if (a->tv_sec > b->tv_sec)
+ return 1;
+ else
+ return -1;
+}
+
+
+void
+_g_time_val_reset (GTimeVal *time_)
+{
+ time_->tv_sec = 0;
+ time_->tv_usec = 0;
+}
+
+
+gboolean
+_g_time_val_from_exif_date (const char *exif_date,
+ GTimeVal *time_)
+{
+ struct tm tm;
+ long val;
+
+ g_return_val_if_fail (time_ != NULL, FALSE);
+
+ if (exif_date == NULL)
+ return FALSE;
+
+ while (g_ascii_isspace (*exif_date))
+ exif_date++;
+
+ if (*exif_date == '\0')
+ return FALSE;
+
+ if (! g_ascii_isdigit (*exif_date))
+ return FALSE;
+
+ /* YYYY */
+
+ val = g_ascii_strtoull (exif_date, (char **)&exif_date, 10);
+ tm.tm_year = val - 1900;
+
+ if (*exif_date != ':')
+ return FALSE;
+
+ /* MM */
+
+ exif_date++;
+ tm.tm_mon = g_ascii_strtoull (exif_date, (char **)&exif_date, 10) - 1;
+
+ if (*exif_date != ':')
+ return FALSE;
+
+ /* DD */
+
+ exif_date++;
+ tm.tm_mday = g_ascii_strtoull (exif_date, (char **)&exif_date, 10);
+
+ if (*exif_date != ' ')
+ return FALSE;
+
+ /* hh */
+
+ val = g_ascii_strtoull (exif_date, (char **)&exif_date, 10);
+ tm.tm_hour = val;
+
+ if (*exif_date != ':')
+ return FALSE;
+
+ /* mm */
+
+ exif_date++;
+ tm.tm_min = g_ascii_strtoull (exif_date, (char **)&exif_date, 10);
+
+ if (*exif_date != ':')
+ return FALSE;
+
+ /* ss */
+
+ exif_date++;
+ tm.tm_sec = strtoul (exif_date, (char **)&exif_date, 10);
+
+ time_->tv_sec = mktime (&tm);
+ time_->tv_usec = 0;
+
+ /* usec */
+
+ if ((*exif_date == ',') || (*exif_date == '.')) {
+ glong mul = 100000;
+
+ while (g_ascii_isdigit (*++exif_date)) {
+ time_->tv_usec += (*exif_date - '0') * mul;
+ mul /= 10;
+ }
+ }
+
+ while (g_ascii_isspace (*exif_date))
+ exif_date++;
+
+ return *exif_date == '\0';
+}
+
+
+char *
+_g_time_val_to_exif_date (GTimeVal *time_)
+{
+ char *retval;
+ struct tm *tm;
+ time_t secs;
+
+ g_return_val_if_fail (time_->tv_usec >= 0 && time_->tv_usec < G_USEC_PER_SEC, NULL);
+
+ secs = time_->tv_sec;
+ tm = localtime (&secs);
+
+ retval = g_strdup_printf ("%4d:%02d:%02d %02d:%02d:%02d",
+ tm->tm_year + 1900,
+ tm->tm_mon + 1,
+ tm->tm_mday,
+ tm->tm_hour,
+ tm->tm_min,
+ tm->tm_sec);
+
+ return retval;
+}
+
+
+/* Bookmark file utils */
+
+
+void
+_g_bookmark_file_clear (GBookmarkFile *bookmark)
+{
+ char **uris;
+ int i;
+
+ uris = g_bookmark_file_get_uris (bookmark, NULL);
+ for (i = 0; uris[i] != NULL; i++)
+ g_bookmark_file_remove_item (bookmark, uris[i], NULL);
+ g_strfreev (uris);
+}
+
+
+void
+_g_bookmark_file_add_uri (GBookmarkFile *bookmark,
+ const char *uri)
+{
+ g_bookmark_file_set_is_private (bookmark, uri, TRUE);
+ g_bookmark_file_add_application (bookmark, uri, NULL, NULL);
+}
+
+
+void
+_g_bookmark_file_set_uris (GBookmarkFile *bookmark,
+ GList *uri_list)
+{
+ GList *scan;
+
+ _g_bookmark_file_clear (bookmark);
+ for (scan = uri_list; scan; scan = scan->next)
+ _g_bookmark_file_add_uri (bookmark, scan->data);
+}
+
+
+/* String utils */
+
+
+void
+_g_strset (char **s,
+ const char *value)
+{
+ if (*s == value)
+ return;
+
+ if (*s != NULL) {
+ g_free (*s);
+ *s = NULL;
+ }
+
+ if (value != NULL)
+ *s = g_strdup (value);
+}
+
char *
_g_strdup_with_max_size (const char *s,
@@ -86,23 +584,23 @@ _g_get_template_from_text (const char *utf8_template)
chunk_end = chunk_start;
ch = g_utf8_get_char (chunk_end);
- while (reading_sharps
- && (*chunk_end != 0)
+ while (reading_sharps
+ && (*chunk_end != 0)
&& (ch == '#')) {
chunk_end = g_utf8_next_char (chunk_end);
ch = g_utf8_get_char (chunk_end);
chunk_len++;
}
-
+
ch = g_utf8_get_char (chunk_end);
- while (! reading_sharps
- && (*chunk_end != 0)
+ while (! reading_sharps
+ && (*chunk_end != 0)
&& (*chunk_end != '#')) {
chunk_end = g_utf8_next_char (chunk_end);
ch = g_utf8_get_char (chunk_end);
chunk_len++;
}
-
+
chunk = _g_utf8_strndup (chunk_start, chunk_len);
str_list = g_list_prepend (str_list, chunk);
n++;
@@ -150,7 +648,7 @@ _g_get_name_from_template (char **utf8_template,
g_string_append_c (s, '0');
sharps_len--;
}
-
+
g_string_append (s, s_n);
g_free (s_n);
}
@@ -164,38 +662,42 @@ _g_get_name_from_template (char **utf8_template,
char *
-_g_substitute (const char *from,
- const char this,
- const char *with_this)
+_g_replace (const char *str,
+ const char *from_str,
+ const char *to_str)
{
- char *result;
- GString *r;
- const char *s;
+ char **tokens;
+ int i;
+ GString *gstr;
- if ((from == NULL) || (with_this == NULL))
- return g_strdup ("");
+ if (str == NULL)
+ return NULL;
- if (strchr (from, this) == NULL)
- return g_strdup (from);
+ if (from_str == NULL)
+ return g_strdup (str);
- r = g_string_new (NULL);
- for (s = from; *s != 0; s++)
- if (*s == this)
- g_string_append (r, with_this);
- else
- g_string_append_c (r, *s);
+ if (strcmp (str, from_str) == 0)
+ return g_strdup (to_str);
- result = r->str;
- g_string_free (r, FALSE);
+ tokens = g_strsplit (str, from_str, -1);
- return result;
+ gstr = g_string_new (NULL);
+ for (i = 0; tokens[i] != NULL; i++) {
+ g_string_append (gstr, tokens[i]);
+ if ((to_str != NULL) && (tokens[i+1] != NULL))
+ g_string_append (gstr, to_str);
+ }
+
+ g_strfreev (tokens);
+
+ return g_string_free (gstr, FALSE);
}
char *
-_g_substitute_pattern (const char *utf8_text,
- gunichar pattern,
- const char *value)
+_g_replace_pattern (const char *utf8_text,
+ gunichar pattern,
+ const char *value)
{
const char *s;
GString *r;
@@ -213,15 +715,17 @@ _g_substitute_pattern (const char *utf8_text,
if (ch == '%') {
s = g_utf8_next_char (s);
-
+
if (*s == 0) {
g_string_append_unichar (r, ch);
break;
}
ch = g_utf8_get_char (s);
- if (ch == pattern)
- g_string_append (r, value);
+ if (ch == pattern) {
+ if (value)
+ g_string_append (r, value);
+ }
else {
g_string_append (r, "%");
g_string_append_unichar (r, ch);
@@ -239,6 +743,29 @@ _g_substitute_pattern (const char *utf8_text,
char *
+_g_utf8_replace (const char *string,
+ const char *pattern,
+ const char *replacement)
+{
+ GRegex *regex;
+ char *result;
+
+ if (string == NULL)
+ return NULL;
+
+ regex = g_regex_new (pattern, 0, 0, NULL);
+ if (regex == NULL)
+ return NULL;
+
+ result = g_regex_replace_literal (regex, string, -1, 0, replacement, 0, NULL);
+
+ g_regex_unref (regex);
+
+ return result;
+}
+
+
+char *
_g_utf8_strndup (const char *str,
gsize n)
{
@@ -256,42 +783,76 @@ _g_utf8_strndup (const char *str,
}
+static const char *
+_g_utf8_strstr (const char *haystack,
+ const char *needle)
+{
+ const char *s;
+ gsize i;
+ gsize haystack_len = g_utf8_strlen (haystack, -1);
+ gsize needle_len = g_utf8_strlen (needle, -1);
+ int needle_size = strlen (needle);
+
+ s = haystack;
+ for (i = 0; i <= haystack_len - needle_len; i++) {
+ if (strncmp (s, needle, needle_size) == 0)
+ return s;
+ s = g_utf8_next_char(s);
+ }
+
+ return NULL;
+}
+
+
char **
-_g_utf8_strsplit (const char *str,
- const gunichar delimiter)
+_g_utf8_strsplit (const char *string,
+ const char *delimiter,
+ int max_tokens)
{
- GSList *slist = NULL, *scan;
+ GSList *string_list = NULL, *slist;
char **str_array;
- const char *s, *t;
+ const char *s;
guint n = 0;
+ const char *remainder;
- if (str == NULL)
- return g_new0 (char *, 1);
-
- t = s = str;
- do {
- gunichar ch = g_utf8_get_char (t);
- if ((ch == delimiter) || (*t == 0)) {
- if (t != s) {
- n++;
- slist = g_slist_prepend (slist, g_strndup (s, t - s));
- }
+ g_return_val_if_fail (string != NULL, NULL);
+ g_return_val_if_fail (delimiter != NULL, NULL);
+ g_return_val_if_fail (delimiter[0] != '\0', NULL);
- if (*t != 0)
- t = s = g_utf8_next_char (t);
- else
- break;
- } else
- t = g_utf8_next_char (t);
- } while (TRUE);
+ if (max_tokens < 1)
+ max_tokens = G_MAXINT;
+
+ remainder = string;
+ s = _g_utf8_strstr (remainder, delimiter);
+ if (s != NULL) {
+ gsize delimiter_size = strlen (delimiter);
+
+ while (--max_tokens && (s != NULL)) {
+ gsize size = s - remainder;
+ char *new_string;
+
+ new_string = g_new (char, size + 1);
+ strncpy (new_string, remainder, size);
+ new_string[size] = 0;
+
+ string_list = g_slist_prepend (string_list, new_string);
+ n++;
+ remainder = s + delimiter_size;
+ s = _g_utf8_strstr (remainder, delimiter);
+ }
+ }
+ if (*string) {
+ n++;
+ string_list = g_slist_prepend (string_list, g_strdup (remainder));
+ }
str_array = g_new (char*, n + 1);
str_array[n--] = NULL;
- for (scan = slist; scan; scan = scan->next)
- str_array[n--] = scan->data;
+ for (slist = string_list; slist; slist = slist->next)
+ str_array[n--] = slist->data;
- g_slist_free (slist);
+ g_slist_free (string_list);
return str_array;
}
@@ -300,36 +861,9 @@ _g_utf8_strsplit (const char *str,
char *
_g_utf8_strstrip (const char *str)
{
- const char *s;
- const char *t;
-
if (str == NULL)
return NULL;
-
- s = str;
-
- do {
- gunichar ch = g_utf8_get_char (s);
- if (! g_unichar_isspace (ch))
- break;
- s = g_utf8_next_char (s);
- } while (*s != 0);
-
- if (*s == 0)
- return NULL;
-
- /**/
-
- t = s;
-
- do {
- gunichar ch = g_utf8_get_char (t);
- if (g_unichar_isspace (ch))
- break;
- t = g_utf8_next_char (t);
- } while (*t != 0);
-
- return g_strndup (s, t - s);
+ return g_strstrip (g_strdup (str));
}
@@ -337,7 +871,10 @@ gboolean
_g_utf8_all_spaces (const char *utf8_string)
{
gunichar c;
-
+
+ if (utf8_string == NULL)
+ return TRUE;
+
c = g_utf8_get_char (utf8_string);
while (c != 0) {
if (! g_unichar_isspace (c))
@@ -350,35 +887,1472 @@ _g_utf8_all_spaces (const char *utf8_string)
}
+char *
+_g_utf8_remove_extension (const char *str)
+{
+ char *p;
+ char *ext;
+ char *dest;
+
+ if ((str == NULL) || ! g_utf8_validate (str, -1, NULL))
+ return NULL;
+
+ p = (char *) str;
+ ext = g_utf8_strrchr (p, -1, g_utf8_get_char ("."));
+ dest = g_strdup (p);
+ g_utf8_strncpy (dest, p, g_utf8_strlen (p, -1) - g_utf8_strlen (ext, -1));
+
+ return dest;
+}
+
+
+GList *
+_g_list_insert_list_before (GList *list1,
+ GList *sibling,
+ GList *list2)
+{
+ if (!list2)
+ {
+ return list1;
+ }
+ else if (!list1)
+ {
+ return list2;
+ }
+ else if (sibling)
+ {
+ GList *list2_last = g_list_last (list2);
+ if (sibling->prev)
+ {
+ sibling->prev->next = list2;
+ list2->prev = sibling->prev;
+ sibling->prev = list2_last;
+ list2_last->next = sibling;
+ return list1;
+ }
+ else
+ {
+ sibling->prev = list2_last;
+ list2_last->next = sibling;
+ return list2;
+ }
+ }
+ else
+ {
+ return g_list_concat (list1, list2);
+ }
+}
+
+
+GHashTable *static_strings = NULL;
+static GStaticMutex static_strings_mutex = G_STATIC_MUTEX_INIT;
+
+
+const char *
+get_static_string (const char *s)
+{
+ const char *result;
+
+ if (s == NULL)
+ return NULL;
+
+ g_static_mutex_lock (&static_strings_mutex);
+
+ if (static_strings == NULL)
+ static_strings = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+
+ if (! g_hash_table_lookup_extended (static_strings, s, (gpointer) &result, NULL)) {
+ result = g_strdup (s);
+ g_hash_table_insert (static_strings,
+ (gpointer) result,
+ GINT_TO_POINTER (1));
+ }
+
+ g_static_mutex_unlock (&static_strings_mutex);
+
+ return result;
+}
+
+
+char *
+_g_rand_string (int len)
+{
+ static char *alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+ static int letters_only = 52;
+ static int whole_alphabet = 62;
+ char *s;
+ GRand *rand_gen;
+ int i;
+
+ s = g_malloc (sizeof (char) * (len + 1));
+ rand_gen = g_rand_new ();
+ for (i = 0; i < len; i++)
+ s[i] = alphabet[g_rand_int_range (rand_gen, 0, (i == 0) ? letters_only : whole_alphabet)];
+ g_rand_free (rand_gen);
+ s[len] = 0;
+
+ return s;
+}
+
+
+int
+_g_strv_find (char **v,
+ const char *s)
+{
+ int i;
+
+ for (i = 0; v[i] != NULL; i++) {
+ if (strcmp (v[i], s) == 0)
+ return i;
+ }
+
+ return -1;
+}
+
+
+char *
+_g_str_remove_suffix (const char *s,
+ const char *suffix)
+{
+ int s_len;
+ int suffix_len;
+
+ if (s == NULL)
+ return NULL;
+ if (suffix == NULL)
+ return g_strdup (s);
+
+ s_len = strlen (s);
+ suffix_len = strlen (suffix);
+
+ if (suffix_len >= s_len)
+ return g_strdup ("");
+ else
+ return g_strndup (s, s_len - suffix_len);
+}
+
+
+/* Regexp utils */
+
+static char **
+get_patterns_from_pattern (const char *pattern_string)
+{
+ char **patterns;
+ int i;
+
+ if (pattern_string == NULL)
+ return NULL;
+
+ patterns = _g_utf8_strsplit (pattern_string, ";", MAX_PATTERNS);
+ for (i = 0; patterns[i] != NULL; i++) {
+ char *p1, *p2;
+
+ p1 = _g_utf8_strstrip (patterns[i]);
+ p2 = _g_replace (p1, ".", "\\.");
+ patterns[i] = _g_replace (p2, "*", ".*");
+
+ g_free (p2);
+ g_free (p1);
+ }
+
+ return patterns;
+}
+
+
+GRegex **
+get_regexps_from_pattern (const char *pattern_string,
+ GRegexCompileFlags compile_options)
+{
+ char **patterns;
+ GRegex **regexps;
+ int i;
+
+ patterns = get_patterns_from_pattern (pattern_string);
+ if (patterns == NULL)
+ return NULL;
+
+ regexps = g_new0 (GRegex*, g_strv_length (patterns) + 1);
+ for (i = 0; patterns[i] != NULL; i++)
+ regexps[i] = g_regex_new (patterns[i],
+ G_REGEX_OPTIMIZE | compile_options,
+ G_REGEX_MATCH_NOTEMPTY,
+ NULL);
+ g_strfreev (patterns);
+
+ return regexps;
+}
+
+
+gboolean
+string_matches_regexps (GRegex **regexps,
+ const char *string,
+ GRegexMatchFlags match_options)
+{
+ gboolean matched;
+ int i;
+
+ if ((regexps == NULL) || (regexps[0] == NULL))
+ return TRUE;
+
+ if (string == NULL)
+ return FALSE;
+
+ matched = FALSE;
+ for (i = 0; regexps[i] != NULL; i++)
+ if (g_regex_match (regexps[i], string, match_options, NULL)) {
+ matched = TRUE;
+ break;
+ }
+
+ return matched;
+}
+
+
void
-debug (const char *file,
- int line,
- const char *function,
- const char *format, ...)
+free_regexps (GRegex **regexps)
{
-#ifdef DEBUG
- va_list args;
- char *str;
+ int i;
- g_return_if_fail (format != NULL);
-
- va_start (args, format);
- str = g_strdup_vprintf (format, args);
+ if (regexps == NULL)
+ return;
+
+ for (i = 0; regexps[i] != NULL; i++)
+ g_regex_unref (regexps[i]);
+ g_free (regexps);
+}
+
+
+/* URI utils */
+
+
+const char *
+get_home_uri (void)
+{
+ static char *home_uri = NULL;
+
+ if (home_uri == NULL) {
+ const char *path;
+ char *uri;
+
+ path = g_get_home_dir ();
+ uri = g_uri_escape_string (path, G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, TRUE);
+
+ home_uri = g_strconcat ("file://", uri, NULL);
+
+ g_free (uri);
+ }
+
+ return home_uri;
+}
+
+
+const char *
+get_desktop_uri (void)
+{
+ static char *desktop_uri = NULL;
+
+ if (desktop_uri == NULL) {
+ char *path;
+ char *uri;
+
+ path = xdg_user_dir_lookup ("DESKTOP");
+ uri = g_uri_escape_string (path, G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, TRUE);
+
+ desktop_uri = g_strconcat ("file://", uri, NULL);
+
+ g_free (uri);
+ g_free (path);
+ }
+
+ return desktop_uri;
+}
+
+
+char *
+xdg_user_dir_lookup (const char *type)
+{
+ /* This function is used by gthumb to determine the default "PICTURES"
+ directory for imports. The actually directory name is localized.
+ Bug 425365. */
+
+ FILE *file;
+ char *home_dir, *config_home, *config_file;
+ char buffer[512];
+ char *user_dir;
+ char *p, *d;
+ int len;
+ int relative;
+
+ home_dir = getenv ("HOME");
+ if (home_dir == NULL)
+ return strdup ("/tmp");
+
+ config_home = getenv ("XDG_CONFIG_HOME");
+ if (config_home == NULL || config_home[0] == 0) {
+ config_file = malloc (strlen (home_dir) + strlen ("/.config/user-dirs.dirs") + 1);
+ strcpy (config_file, home_dir);
+ strcat (config_file, "/.config/user-dirs.dirs");
+ }
+ else {
+ config_file = malloc (strlen (config_home) + strlen ("/user-dirs.dirs") + 1);
+ strcpy (config_file, config_home);
+ strcat (config_file, "/user-dirs.dirs");
+ }
+
+ file = fopen (config_file, "r");
+ free (config_file);
+ if (file == NULL)
+ goto error;
+
+ user_dir = NULL;
+
+ while (fgets (buffer, sizeof (buffer), file)) {
+ /* Remove newline at end */
+ len = strlen (buffer);
+ if (len > 0 && buffer[len-1] == '\n')
+ buffer[len-1] = 0;
+
+ p = buffer;
+ while (*p == ' ' || *p == '\t')
+ p++;
+
+ if (strncmp (p, "XDG_", 4) != 0)
+ continue;
+ p += 4;
+
+ if (strncmp (p, type, strlen (type)) != 0)
+ continue;
+ p += strlen (type);
+
+ if (strncmp (p, "_DIR", 4) != 0)
+ continue;
+ p += 4;
+
+ while (*p == ' ' || *p == '\t')
+ p++;
+
+ if (*p != '=')
+ continue;
+ p++;
+
+ while (*p == ' ' || *p == '\t')
+ p++;
+
+ if (*p != '"')
+ continue;
+ p++;
+
+ relative = 0;
+ if (strncmp (p, "$HOME/", 6) == 0) {
+ p += 6;
+ relative = 1;
+ } else if (*p != '/')
+ continue;
+
+ if (relative) {
+ user_dir = malloc (strlen (home_dir) + 1 + strlen (p) + 1);
+ strcpy (user_dir, home_dir);
+ strcat (user_dir, "/");
+ } else {
+ user_dir = malloc (strlen (p) + 1);
+ *user_dir = 0;
+ }
+
+ d = user_dir + strlen (user_dir);
+ while (*p && *p != '"') {
+ if ((*p == '\\') && (*(p+1) != 0))
+ p++;
+ *d++ = *p++;
+ }
+ *d = 0;
+ }
+
+ fclose (file);
+
+ if (user_dir)
+ return user_dir;
+
+error:
+ /* Special case desktop for historical compatibility */
+ if (strcmp (type, "DESKTOP") == 0) {
+ user_dir = malloc (strlen (home_dir) + strlen ("/Desktop") + 1);
+ strcpy (user_dir, home_dir);
+ strcat (user_dir, "/Desktop");
+ return user_dir;
+ }
+ else
+ return strdup (home_dir);
+}
+
+
+int
+uricmp (const char *uri1,
+ const char *uri2)
+{
+ if (uri1 == NULL) {
+ if (uri2 == NULL)
+ return 0;
+ else
+ return -1;
+ }
+
+ if (uri2 == NULL) {
+ if (uri1 == NULL)
+ return 0;
+ else
+ return 1;
+ }
+
+ return g_strcmp0 (uri1, uri2);
+}
+
+
+gboolean
+same_uri (const char *uri1,
+ const char *uri2)
+{
+ return uricmp (uri1, uri2) == 0;
+}
+
+
+void
+_g_string_list_free (GList *string_list)
+{
+ if (string_list == NULL)
+ return;
+ g_list_foreach (string_list, (GFunc) g_free, NULL);
+ g_list_free (string_list);
+
+}
+
+
+GList *
+_g_string_list_dup (GList *string_list)
+{
+ GList *new_list = NULL;
+ GList *scan;
+
+ for (scan = string_list; scan; scan = scan->next)
+ new_list = g_list_prepend (new_list, g_strdup (scan->data));
+
+ return g_list_reverse (new_list);
+}
+
+
+GType
+g_string_list_get_type (void)
+{
+ static GType type = 0;
+
+ if (type == 0)
+ type = g_boxed_type_register_static ("GStringList",
+ (GBoxedCopyFunc) _g_string_list_dup,
+ (GBoxedFreeFunc) _g_string_list_free);
+
+ return type;
+}
+
+
+GList *
+get_file_list_from_url_list (char *url_list)
+{
+ GList *list = NULL;
+ int i;
+ char *url_start, *url_end;
+
+ url_start = url_list;
+ while (url_start[0] != '\0') {
+ char *url;
+
+ if (strncmp (url_start, "file:", 5) == 0) {
+ url_start += 5;
+ if ((url_start[0] == '/')
+ && (url_start[1] == '/')) url_start += 2;
+ }
+
+ i = 0;
+ while ((url_start[i] != '\0')
+ && (url_start[i] != '\r')
+ && (url_start[i] != '\n')) i++;
+ url_end = url_start + i;
+
+ url = g_strndup (url_start, url_end - url_start);
+ list = g_list_prepend (list, url);
+
+ url_start = url_end;
+ i = 0;
+ while ((url_start[i] != '\0')
+ && ((url_start[i] == '\r')
+ || (url_start[i] == '\n'))) i++;
+ url_start += i;
+ }
+
+ return g_list_reverse (list);
+}
+
+
+const char *
+_g_uri_get_basename (const char *uri)
+{
+ register char *base;
+ register gssize last_char;
+
+ if (uri == NULL)
+ return NULL;
+
+ if (uri[0] == '\0')
+ return "";
+
+ last_char = strlen (uri) - 1;
+
+ if (uri[last_char] == G_DIR_SEPARATOR)
+ return "";
+
+ base = g_utf8_strrchr (uri, -1, G_DIR_SEPARATOR);
+ if (! base)
+ return uri;
+
+ return base + 1;
+}
+
+
+const char *
+_g_uri_get_file_extension (const char *uri)
+{
+ int len;
+ int p;
+ const char *ptr = uri;
+
+ if (uri == NULL)
+ return NULL;
+
+ len = strlen (uri);
+ if (len <= 1)
+ return NULL;
+
+ p = len - 1;
+ while ((p >= 0) && (ptr[p] != '.'))
+ p--;
+
+ if (p < 0)
+ return NULL;
+
+ return uri + p;
+}
+
+
+static gboolean
+uri_is_filetype (const char *uri,
+ GFileType file_type)
+{
+ gboolean result = FALSE;
+ GFile *file;
+ GFileInfo *info;
+ GError *error = NULL;
+
+ file = g_file_new_for_uri (uri);
+
+ if (! g_file_query_exists (file, NULL)) {
+ g_object_unref (file);
+ return FALSE;
+ }
+
+ info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_TYPE, 0, NULL, &error);
+ if (error == NULL) {
+ result = (g_file_info_get_file_type (info) == file_type);
+ }
+ else {
+ g_warning ("Failed to get file type for uri %s: %s", uri, error->message);
+ g_error_free (error);
+ }
+
+ g_object_unref (info);
+ g_object_unref (file);
+
+ return result;
+}
+
+
+gboolean
+_g_uri_is_file (const char *uri)
+{
+ return uri_is_filetype (uri, G_FILE_TYPE_REGULAR);
+}
+
+
+gboolean
+_g_uri_is_dir (const char *uri)
+{
+ return uri_is_filetype (uri, G_FILE_TYPE_DIRECTORY);
+}
+
+
+gboolean
+_g_uri_parent_of_uri (const char *dirname,
+ const char *filename)
+{
+ int dirname_l, filename_l, separator_position;
+
+ if ((dirname == NULL) || (filename == NULL))
+ return FALSE;
+
+ dirname_l = strlen (dirname);
+ filename_l = strlen (filename);
+
+ if ((dirname_l == filename_l + 1)
+ && (dirname[dirname_l - 1] == '/'))
+ return FALSE;
+
+ if ((filename_l == dirname_l + 1)
+ && (filename[filename_l - 1] == '/'))
+ return FALSE;
+
+ if (dirname[dirname_l - 1] == '/')
+ separator_position = dirname_l - 1;
+ else
+ separator_position = dirname_l;
+
+ return ((filename_l > dirname_l)
+ && (strncmp (dirname, filename, dirname_l) == 0)
+ && (filename[separator_position] == '/'));
+}
+
+
+char *
+_g_uri_get_parent (const char *uri)
+{
+ int p;
+ const char *ptr = uri;
+ char *new_uri;
+
+ if (uri == NULL)
+ return NULL;
+
+ p = strlen (uri) - 1;
+ if (p < 0)
+ return NULL;
+
+ while ((p > 0) && (ptr[p] != '/'))
+ p--;
+ if ((p == 0) && (ptr[p] == '/'))
+ p++;
+ new_uri = g_strndup (uri, (guint)p);
+
+ return new_uri;
+}
+
+
+char *
+_g_uri_remove_extension (const char *uri)
+{
+ int len;
+ int p;
+ char *new_path;
+
+ if (uri == NULL)
+ return NULL;
+
+ len = strlen (uri);
+ if (len == 1)
+ return g_strdup (uri);
+
+ p = len - 1;
+ while ((p > 0) && (uri[p] != '.'))
+ p--;
+ if (p == 0)
+ p = len;
+ new_path = g_strndup (uri, (guint) p);
+
+ return new_path;
+}
+
+
+char *
+_g_build_uri (const char *base, ...)
+{
+ va_list args;
+ const char *child;
+ GString *uri;
+
+ uri = g_string_new (base);
+
+ va_start (args, base);
+ while ((child = va_arg (args, const char *)) != NULL) {
+ if (! g_str_has_suffix (uri->str, "/") && ! g_str_has_prefix (child, "/"))
+ g_string_append (uri, "/");
+ g_string_append (uri, child);
+ }
va_end (args);
- g_fprintf (stderr, "[GOO] %s:%d (%s):\n\t%s\n", file, line, function, str);
+ return g_string_free (uri, FALSE);
+}
- g_free (str);
-#else /* ! DEBUG */
-#endif
+
+/* GIO utils */
+
+
+gboolean
+_g_file_equal (GFile *file1,
+ GFile *file2)
+{
+ if ((file1 == NULL) && (file2 == NULL))
+ return TRUE;
+ if ((file1 == NULL) || (file2 == NULL))
+ return FALSE;
+
+ return g_file_equal (file1, file2);
+}
+
+
+char *
+_g_file_get_display_name (GFile *file)
+{
+ char *name = NULL;
+ GFileInfo *file_info;
+
+ file_info = g_file_query_info (file,
+ G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
+ G_FILE_QUERY_INFO_NONE,
+ NULL,
+ NULL);
+ if (file_info != NULL)
+ name = g_strdup (g_file_info_get_attribute_string (file_info, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME));
+ else
+ name = g_file_get_parse_name (file);
+
+ return name;
+}
+
+
+GFileType
+_g_file_get_standard_type (GFile *file)
+{
+ GFileType result;
+ GFileInfo *info;
+ GError *error = NULL;
+
+ info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_TYPE, 0, NULL, &error);
+ if (error == NULL) {
+ result = g_file_info_get_file_type (info);
+ }
+ else {
+ result = G_FILE_ATTRIBUTE_TYPE_INVALID;
+ g_error_free (error);
+ }
+
+ g_object_unref (info);
+
+ return result;
+}
+
+
+GFile *
+_g_file_get_destination (GFile *source,
+ GFile *source_base,
+ GFile *destination_folder)
+{
+ char *source_uri;
+ const char *source_suffix;
+ char *destination_folder_uri;
+ char *destination_uri;
+ GFile *destination;
+
+ source_uri = g_file_get_uri (source);
+ if (source_base != NULL) {
+ char *source_base_uri;
+
+ source_base_uri = g_file_get_uri (source_base);
+ source_suffix = source_uri + strlen (source_base_uri);
+
+ g_free (source_base_uri);
+ }
+ else
+ source_suffix = _g_uri_get_basename (source_uri);
+
+ destination_folder_uri = g_file_get_uri (destination_folder);
+ destination_uri = g_strconcat (destination_folder_uri, "/", source_suffix, NULL);
+ destination = g_file_new_for_uri (destination_uri);
+
+ g_free (destination_uri);
+ g_free (destination_folder_uri);
+ g_free (source_uri);
+
+ return destination;
+}
+
+
+GFile *
+_g_file_get_child (GFile *file,
+ ...)
+{
+ va_list args;
+ const char *name;
+ GFile *child;
+
+ child = g_object_ref (file);
+
+ va_start (args, file);
+ while ((name = va_arg (args, const char *)) != NULL) {
+ GFile *tmp;
+
+ tmp = g_file_get_child (child, name);
+ g_object_unref (child);
+ child = tmp;
+ }
+ va_end (args);
+
+ return child;
+}
+
+
+GIcon *
+_g_file_get_icon (GFile *file)
+{
+ GIcon *icon = NULL;
+ GFileInfo *file_info;
+
+ file_info = g_file_query_info (file,
+ G_FILE_ATTRIBUTE_STANDARD_ICON,
+ G_FILE_QUERY_INFO_NONE,
+ NULL,
+ NULL);
+ if (file_info != NULL)
+ icon = (GIcon*) g_file_info_get_attribute_object (file_info, G_FILE_ATTRIBUTE_STANDARD_ICON);
+ else
+ icon = g_themed_icon_new ("file");
+
+ return icon;
+}
+
+
+GList *
+_g_file_list_dup (GList *l)
+{
+ GList *r = NULL, *scan;
+ for (scan = l; scan; scan = scan->next)
+ r = g_list_prepend (r, g_file_dup ((GFile*) scan->data));
+ return g_list_reverse (r);
+}
+
+
+void
+_g_file_list_free (GList *l)
+{
+ GList *scan;
+ for (scan = l; scan; scan = scan->next)
+ g_object_unref (scan->data);
+ g_list_free (l);
+}
+
+
+GList *
+_g_file_list_new_from_uri_list (GList *uris)
+{
+ GList *r = NULL, *scan;
+ for (scan = uris; scan; scan = scan->next)
+ r = g_list_prepend (r, g_file_new_for_uri ((char*)scan->data));
+ return g_list_reverse (r);
+}
+
+
+GList *
+_g_file_list_new_from_uriv (char **uris)
+{
+ GList *r = NULL;
+ int i;
+
+ if (uris == NULL)
+ return NULL;
+
+ for (i = 0; uris[i] != NULL; i++)
+ r = g_list_prepend (r, g_file_new_for_uri (uris[i]));
+
+ return g_list_reverse (r);
+}
+
+
+GList *
+_g_file_list_find_file (GList *l,
+ GFile *file)
+{
+ GList *scan;
+
+ for (scan = l; scan; scan = scan->next)
+ if (g_file_equal (file, (GFile *) scan->data))
+ return scan;
+ return NULL;
+}
+
+
+const char*
+_g_file_get_mime_type (GFile *file,
+ gboolean fast_file_type)
+{
+ GFileInfo *info;
+ GError *err = NULL;
+ const char *result = NULL;
+
+ info = g_file_query_info (file,
+ fast_file_type ?
+ G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE :
+ G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
+ 0, NULL, &err);
+ if (info == NULL) {
+ char *uri;
+
+ uri = g_file_get_uri (file);
+ g_warning ("could not get content type for %s: %s", uri, err->message);
+ g_free (uri);
+ g_clear_error (&err);
+ }
+ else {
+ result = get_static_string (g_content_type_get_mime_type (g_file_info_get_content_type (info)));
+ g_object_unref (info);
+ }
+
+ return result;
+}
+
+
+void
+_g_file_get_modification_time (GFile *file,
+ GTimeVal *result)
+{
+ GFileInfo *info;
+ GError *err = NULL;
+
+ result->tv_sec = 0;
+ result->tv_usec = 0;
+
+ info = g_file_query_info (file,
+ G_FILE_ATTRIBUTE_TIME_MODIFIED "," G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC,
+ 0,
+ NULL,
+ &err);
+ if (info != NULL) {
+ g_file_info_get_modification_time (info, result);
+ g_object_unref (info);
+ }
+ else {
+ char *uri;
+
+ uri = g_file_get_uri (file);
+ g_warning ("could not get modification time for %s: %s", uri, err->message);
+ g_free (uri);
+ g_clear_error (&err);
+ }
+}
+
+
+time_t
+_g_file_get_mtime (GFile *file)
+{
+ GTimeVal timeval;
+
+ _g_file_get_modification_time (file, &timeval);
+ return (time_t) timeval.tv_sec;
+}
+
+
+int
+_g_file_cmp_uris (GFile *a,
+ GFile *b)
+{
+ return g_file_equal (a, b) ? 0 : 1;
+}
+
+
+int
+_g_file_cmp_modification_time (GFile *file_a,
+ GFile *file_b)
+{
+ GTimeVal timeval_a;
+ GTimeVal timeval_b;
+
+ _g_file_get_modification_time (file_a, &timeval_a);
+ _g_file_get_modification_time (file_b, &timeval_b);
+
+ return _g_time_val_cmp (&timeval_a, &timeval_b);
+}
+
+
+goffset
+_g_file_get_size (GFile *file)
+{
+ GFileInfo *info;
+ GError *err = NULL;
+ goffset size = 0;
+
+ info = g_file_query_info (file, G_FILE_ATTRIBUTE_FILESYSTEM_SIZE, 0, NULL, &err);
+ if (info != NULL) {
+ size = g_file_info_get_size (info);
+ g_object_unref (info);
+ }
+ else {
+ char *uri;
+
+ uri = g_file_get_uri (file);
+ g_warning ("could not get size for %s: %s", uri, err->message);
+ g_free (uri);
+ g_clear_error (&err);
+ }
+
+ return size;
+}
+
+
+#define MAX_SYMLINKS 32
+
+
+static GFile *
+resolve_symlinks (GFile *file,
+ GError **error,
+ int level)
+{
+ GFile *resolved;
+ char *path;
+ char **names;
+ int i;
+
+ if (level > MAX_SYMLINKS) {
+ char *uri;
+
+ uri = g_file_get_uri (file);
+ *error = g_error_new (G_IO_ERROR, G_IO_ERROR_TOO_MANY_LINKS, "Too many symbolic links for file: %s.", uri);
+ g_free (uri);
+
+ return NULL;
+ }
+
+ path = g_file_get_path (file);
+ if (path == NULL) {
+ char *uri;
+
+ uri = g_file_get_uri (file);
+ *error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "No local pathname for file: %s.", uri);
+ g_free (uri);
+
+ return NULL;
+ }
+
+ resolved = g_file_new_for_path (G_DIR_SEPARATOR_S);
+
+ names = g_strsplit (path, G_DIR_SEPARATOR_S, -1);
+ for (i = 0; names[i] != NULL; i++) {
+ GFile *child;
+ GFileInfo *info;
+ GFile *new_resolved;
+
+ if (strcmp (names[i], "") == 0)
+ continue;
+
+ child = g_file_get_child (resolved, names[i]);
+ info = g_file_query_info (child, G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK "," G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET, 0, NULL, error);
+ if (info == NULL) {
+ g_object_unref (child);
+ g_object_unref (resolved);
+ resolved = NULL;
+ break;
+ }
+
+ /* if names[i] isn't a symbolic link add it to the resolved path and continue */
+
+ if (! g_file_info_get_is_symlink (info)) {
+ g_object_unref (info);
+ g_object_unref (resolved);
+ resolved = child;
+ continue;
+ }
+
+ g_object_unref (child);
+
+ /* names[i] is a symbolic link */
+
+ new_resolved = g_file_resolve_relative_path (resolved, g_file_info_get_symlink_target (info));
+
+ g_object_unref (resolved);
+ g_object_unref (info);
+
+ resolved = resolve_symlinks (new_resolved, error, level + 1);
+
+ g_object_unref (new_resolved);
+ }
+
+ g_strfreev (names);
+ g_free (path);
+
+ return resolved;
+}
+
+
+GFile *
+_g_file_resolve_all_symlinks (GFile *file,
+ GError **error)
+{
+ return resolve_symlinks (file, error, 0);
+}
+
+
+GFile *
+_g_file_append_prefix (GFile *file,
+ const char *prefix)
+{
+ char *uri;
+ char *new_uri;
+ GFile *new_file;
+
+ uri = g_file_get_uri (file);
+ new_uri = g_strconcat (prefix, uri, NULL);
+ new_file = g_file_new_for_uri (new_uri);
+
+ g_free (new_uri);
+ g_free (uri);
+
+ return new_file;
+}
+
+
+GFile *
+_g_file_append_path (GFile *file,
+ const char *path)
+{
+ char *uri;
+ char *escaped;
+ char *new_uri;
+ GFile *new_file;
+
+ if (path == NULL)
+ return g_file_dup (file);
+
+ uri = g_file_get_uri (file);
+ escaped = g_uri_escape_string (path, G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, FALSE);
+ new_uri = _g_build_uri (uri, escaped, NULL);
+ new_file = g_file_new_for_uri (new_uri);
+
+ g_free (new_uri);
+ g_free (escaped);
+ g_free (uri);
+
+ return new_file;
+}
+
+
+static gboolean
+_g_file_attributes_matches_mask (const char *attributes,
+ const char *mask)
+{
+ gboolean matches = FALSE;
+ char **attributes_v;
+ char **mask_v;
+ int i;
+
+ attributes_v = g_strsplit (attributes, ",", -1);
+ mask_v = g_strsplit (mask, ",", -1);
+ for (i = 0; ! matches && (mask_v[i] != NULL); i++) {
+ GFileAttributeMatcher *matcher;
+ int j;
+
+ matcher = g_file_attribute_matcher_new (mask_v[i]);
+ for (j = 0; ! matches && (attributes_v[j] != NULL); j++)
+ matches = g_file_attribute_matcher_matches (matcher, attributes_v[j]);
+
+ g_file_attribute_matcher_unref (matcher);
+ }
+
+ g_strfreev (mask_v);
+ g_strfreev (attributes_v);
+
+ return matches;
+}
+
+
+gboolean
+_g_file_attributes_matches (const char *attributes,
+ const char *mask)
+{
+ return _g_file_attributes_matches_mask (attributes, mask) || _g_file_attributes_matches_mask (mask, attributes);
+}
+
+
+/* -- _g_file_info_swap_attributes -- */
+
+
+typedef struct {
+ GFileAttributeType type;
+ union {
+ char *string;
+ char **stringv;
+ gboolean boolean;
+ guint32 uint32;
+ gint32 int32;
+ guint64 uint64;
+ gint64 int64;
+ gpointer object;
+ } v;
+} _GFileAttributeValue;
+
+
+static void
+_g_file_attribute_value_free (_GFileAttributeValue *attr_value)
+{
+ switch (attr_value->type) {
+ case G_FILE_ATTRIBUTE_TYPE_STRING:
+ case G_FILE_ATTRIBUTE_TYPE_BYTE_STRING:
+ g_free (attr_value->v.string);
+ break;
+/* FIXME: add if glib >= 2.22
+ case G_FILE_ATTRIBUTE_TYPE_STRINGV:
+ g_strfreev (attr_value->v.stringv);
+ break;
+*/
+ case G_FILE_ATTRIBUTE_TYPE_OBJECT:
+ g_object_unref (attr_value->v.object);
+ break;
+ default:
+ break;
+ }
+
+ g_free (attr_value);
+}
+
+
+static _GFileAttributeValue *
+_g_file_info_get_value (GFileInfo *info,
+ const char *attr)
+{
+ _GFileAttributeValue *attr_value;
+ GFileAttributeType type;
+ gpointer value;
+ GFileAttributeStatus status;
+
+ attr_value = g_new (_GFileAttributeValue, 1);
+ attr_value->type = G_FILE_ATTRIBUTE_TYPE_INVALID;
+
+ if (! g_file_info_get_attribute_data (info, attr, &type, &value, &status))
+ return attr_value;
+
+ attr_value->type = type;
+ switch (type) {
+ case G_FILE_ATTRIBUTE_TYPE_INVALID:
+ break;
+ case G_FILE_ATTRIBUTE_TYPE_STRING:
+ case G_FILE_ATTRIBUTE_TYPE_BYTE_STRING:
+ attr_value->v.string = g_strdup ((char *) value);
+ break;
+/* FIXME: add if glib >= 2.22
+ case G_FILE_ATTRIBUTE_TYPE_STRINGV:
+ attr_value->v.stringv = g_strdupv ((char **) value);
+ break;
+*/
+ case G_FILE_ATTRIBUTE_TYPE_BOOLEAN:
+ attr_value->v.boolean = * ((gboolean *) value);
+ break;
+ case G_FILE_ATTRIBUTE_TYPE_UINT32:
+ attr_value->v.uint32 = * ((guint32 *) value);
+ break;
+ case G_FILE_ATTRIBUTE_TYPE_INT32:
+ attr_value->v.int32 = * ((gint32 *) value);
+ break;
+ case G_FILE_ATTRIBUTE_TYPE_UINT64:
+ attr_value->v.uint64 = * ((guint64 *) value);
+ break;
+ case G_FILE_ATTRIBUTE_TYPE_INT64:
+ attr_value->v.int64 = * ((gint64 *) value);
+ break;
+ case G_FILE_ATTRIBUTE_TYPE_OBJECT:
+ attr_value->v.object = g_object_ref ((GObject *) value);
+ break;
+ default:
+ g_warning ("unknown attribute type: %d", type);
+ break;
+ }
+
+ return attr_value;
+}
+
+
+static void
+_g_file_info_set_value (GFileInfo *info,
+ const char *attr,
+ _GFileAttributeValue *attr_value)
+{
+ gpointer value = NULL;
+
+ if (attr_value->type == G_FILE_ATTRIBUTE_TYPE_INVALID)
+ return;
+
+ switch (attr_value->type) {
+ case G_FILE_ATTRIBUTE_TYPE_STRING:
+ case G_FILE_ATTRIBUTE_TYPE_BYTE_STRING:
+ value = attr_value->v.string;
+ break;
+/* FIXME: add if glib >= 2.22
+ case G_FILE_ATTRIBUTE_TYPE_STRINGV:
+ value = attr_value->v.stringv;
+ break;
+*/
+ case G_FILE_ATTRIBUTE_TYPE_BOOLEAN:
+ value = &attr_value->v.boolean;
+ break;
+ case G_FILE_ATTRIBUTE_TYPE_UINT32:
+ value = &attr_value->v.uint32;
+ break;
+ case G_FILE_ATTRIBUTE_TYPE_INT32:
+ value = &attr_value->v.int32;
+ break;
+ case G_FILE_ATTRIBUTE_TYPE_UINT64:
+ value = &attr_value->v.uint64;
+ break;
+ case G_FILE_ATTRIBUTE_TYPE_INT64:
+ value = &attr_value->v.int64;
+ break;
+ case G_FILE_ATTRIBUTE_TYPE_OBJECT:
+ value = attr_value->v.object;
+ break;
+ default:
+ g_warning ("Unknown attribute type: %d", attr_value->type);
+ break;
+ }
+
+ g_file_info_set_attribute (info, attr, attr_value->type, value);
}
void
-set_time_string (char *buffer,
- gint64 time)
+_g_file_info_swap_attributes (GFileInfo *info,
+ const char *attr1,
+ const char *attr2)
+{
+ _GFileAttributeValue *value1;
+ _GFileAttributeValue *value2;
+
+ value1 = _g_file_info_get_value (info, attr1);
+ value2 = _g_file_info_get_value (info, attr2);
+
+ _g_file_info_set_value (info, attr1, value2);
+ _g_file_info_set_value (info, attr2, value1);
+
+ _g_file_attribute_value_free (value1);
+ _g_file_attribute_value_free (value2);
+}
+
+
+gboolean
+_g_mime_type_is_image (const char *mime_type)
+{
+ g_return_val_if_fail (mime_type != NULL, FALSE);
+
+ /* Valid image mime types:
+ 1. All *image* types,
+ 2. application/x-crw
+ This is a RAW photo file, which for some reason
+ uses an "application" prefix instead of "image".
+ */
+
+ return (g_content_type_is_a (mime_type, "image/*")
+ || (strcmp (mime_type, "application/x-crw") == 0));
+}
+
+
+gboolean
+_g_mime_type_is_video (const char *mime_type)
+{
+ g_return_val_if_fail (mime_type != NULL, FALSE);
+
+ return (g_content_type_is_a (mime_type, "video/*")
+ || (strcmp (mime_type, "application/ogg") == 0)
+ || (strcmp (mime_type, "application/vnd.rn-realmedia") == 0));
+}
+
+
+gboolean
+_g_mime_type_is_audio (const char *mime_type)
+{
+ g_return_val_if_fail (mime_type != NULL, FALSE);
+
+ return g_content_type_is_a (mime_type, "audio/*");
+}
+
+
+gboolean
+_g_volume_equal (GVolume *v1,
+ GVolume *v2)
+{
+ char *id1;
+ char *id2;
+ gboolean result;
+
+ id1 = g_volume_get_identifier (v1, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
+ id2 = g_volume_get_identifier (v2, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
+ result = g_strcmp0 (id1, id2) == 0;
+
+ g_free (id1);
+ g_free (id2);
+
+ return result;
+}
+
+
+gboolean
+_g_drive_equal (GDrive *d1,
+ GDrive *d2)
+{
+ char *id1;
+ char *id2;
+ gboolean result;
+
+ id1 = g_drive_get_identifier (d1, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
+ id2 = g_drive_get_identifier (d2, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
+ result = g_strcmp0 (id1, id2) == 0;
+
+ g_free (id1);
+ g_free (id2);
+
+ return result;
+}
+
+
+char *
+_g_make_temp_directory (void)
+{
+ char *template;
+
+ template = g_strconcat (g_get_tmp_dir (), "/goobox-XXXXXX", NULL);
+ g_assert (mkdtemp (template) != NULL);
+
+ return template;
+}
+
+
+/* this is totem_time_to_string renamed, thanks to the authors :) */
+char *
+_g_format_duration_for_display (gint64 msecs)
{
- sprintf (buffer,
- "%" G_GINT64_FORMAT ":%02" G_GINT64_FORMAT " ",
- time / 60, time % 60);
+ int sec, min, hour, _time;
+
+ _time = (int) (msecs / 1000);
+ sec = _time % 60;
+ _time = _time - sec;
+ min = (_time % (60*60)) / 60;
+ _time = _time - (min * 60);
+ hour = _time / (60*60);
+
+ if (hour > 0)
+ {
+ /* hour:minutes:seconds */
+ /* Translators: This is a time format, like "9:05:02" for 9
+ * hours, 5 minutes, and 2 seconds. You may change ":" to
+ * the separator that your locale uses or use "%Id" instead
+ * of "%d" if your locale uses localized digits.
+ */
+ return g_strdup_printf (C_("long time format", "%d:%02d:%02d"), hour, min, sec);
+ }
+
+ /* minutes:seconds */
+ /* Translators: This is a time format, like "5:02" for 5
+ * minutes and 2 seconds. You may change ":" to the
+ * separator that your locale uses or use "%Id" instead of
+ * "%d" if your locale uses localized digits.
+ */
+ return g_strdup_printf (C_("short time format", "%d:%02d"), min, sec);
}
diff --git a/src/glib-utils.h b/src/glib-utils.h
index d1d61e8..7377420 100644
--- a/src/glib-utils.h
+++ b/src/glib-utils.h
@@ -3,7 +3,7 @@
/*
* GThumb
*
- * Copyright (C) 2001, 2003 Free Software Foundation, Inc.
+ * Copyright (C) 2001-2008 Free Software Foundation, Inc.
*
* 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
@@ -23,60 +23,246 @@
#ifndef _GLIB_UTILS_H
#define _GLIB_UTILS_H
+#include <glib.h>
#include <glib-object.h>
+#include <gio/gio.h>
+#include "typedefs.h"
+
+G_BEGIN_DECLS
+
+#define GFILE_NAME_TYPE_ATTRIBUTES "standard::name,standard::type"
+#define GFILE_DISPLAY_ATTRIBUTES "standard::display-name,standard::icon"
+#define GFILE_BASIC_ATTRIBUTES GFILE_DISPLAY_ATTRIBUTES ",standard::name,standard::type"
+
+#define DEFINE_STANDARD_ATTRIBUTES(a) ( \
+ "standard::type," \
+ "standard::is-hidden," \
+ "standard::is-backup," \
+ "standard::name," \
+ "standard::display-name," \
+ "standard::edit-name," \
+ "standard::icon," \
+ "standard::size," \
+ "thumbnail::path" \
+ "time::created," \
+ "time::created-usec," \
+ "time::modified," \
+ "time::modified-usec," \
+ "access::*" \
+ a)
+#define GFILE_STANDARD_ATTRIBUTES (DEFINE_STANDARD_ATTRIBUTES(""))
+#define GFILE_STANDARD_ATTRIBUTES_WITH_FAST_CONTENT_TYPE (DEFINE_STANDARD_ATTRIBUTES(",standard::fast-content-type"))
+#define GFILE_STANDARD_ATTRIBUTES_WITH_CONTENT_TYPE (DEFINE_STANDARD_ATTRIBUTES(",standard::fast-content-type,standard::content-type"))
+
+#define GNOME_COPIED_FILES (gdk_atom_intern_static_string ("x-special/gnome-copied-files"))
+#define IROUND(x) ((int)floor(((double)x) + 0.5))
+#define FLOAT_EQUAL(a,b) (fabs (a - b) < 1e-6)
+#define ID_LENGTH 8
+#define G_TYPE_OBJECT_LIST (g_object_list_get_type ())
+#define G_TYPE_STRING_LIST (g_string_list_get_type ())
+
+/* signals */
#define g_signal_handlers_disconnect_by_data(instance, data) \
g_signal_handlers_disconnect_matched ((instance), G_SIGNAL_MATCH_DATA, \
- 0, 0, NULL, NULL, (data))
+ 0, 0, NULL, NULL, (data))
#define g_signal_handlers_block_by_data(instance, data) \
g_signal_handlers_block_matched ((instance), G_SIGNAL_MATCH_DATA, \
- 0, 0, NULL, NULL, (data))
+ 0, 0, NULL, NULL, (data))
#define g_signal_handlers_unblock_by_data(instance, data) \
g_signal_handlers_unblock_matched ((instance), G_SIGNAL_MATCH_DATA, \
- 0, 0, NULL, NULL, (data))
+ 0, 0, NULL, NULL, (data))
+
+/* gobject utils*/
+
+gpointer _g_object_ref (gpointer object);
+void _g_object_unref (gpointer object);
+void _g_object_clear (gpointer object);
+GList * _g_object_list_ref (GList *list);
+void _g_object_list_unref (GList *list);
+GType g_object_list_get_type (void);
+GEnumValue * _g_enum_type_get_value (GType enum_type,
+ int value);
+GEnumValue * _g_enum_type_get_value_by_nick (GType enum_type,
+ const char *nick);
+
+/* idle callback */
+
+typedef struct {
+ DataFunc func;
+ gpointer data;
+} IdleCall;
+
+
+IdleCall* idle_call_new (DataFunc func,
+ gpointer data);
+void idle_call_free (IdleCall *call);
+guint idle_call_exec (IdleCall *call,
+ gboolean use_idle_cb);
+guint call_when_idle (DataFunc func,
+ gpointer data);
+void object_ready_with_error (gpointer object,
+ ReadyCallback ready_func,
+ gpointer user_data,
+ GError *error);
+void ready_with_error (ReadyFunc ready_func,
+ gpointer user_data,
+ GError *error);
+
+/* debug */
+
+void debug (const char *file,
+ int line,
+ const char *function,
+ const char *format,
+ ...);
+void performance (const char *file,
+ int line,
+ const char *function,
+ const char *format,
+ ...);
+
+#define DEBUG_INFO __FILE__, __LINE__, G_STRFUNC
+/* GTimeVal utils */
-char * _g_strdup_with_max_size (const char *s,
- int max_size);
+int _g_time_val_cmp (GTimeVal *a,
+ GTimeVal *b);
+void _g_time_val_reset (GTimeVal *time_);
+gboolean _g_time_val_from_exif_date (const char *exif_date,
+ GTimeVal *time_);
+char * _g_time_val_to_exif_date (GTimeVal *time_);
-char ** _g_get_template_from_text (const char *template);
+/* Bookmark file utils */
-char * _g_get_name_from_template (char **template,
- int num);
+void _g_bookmark_file_clear (GBookmarkFile *bookmark);
+void _g_bookmark_file_add_uri (GBookmarkFile *bookmark,
+ const char *uri);
+void _g_bookmark_file_set_uris (GBookmarkFile *bookmark,
+ GList *uri_list);
-char * _g_substitute (const char *from,
- const char this,
- const char *with_this);
+/* String utils */
-char * _g_substitute_pattern (const char *utf8_text,
- gunichar pattern,
- const char *value);
+void _g_strset (char **s,
+ const char *value);
+char * _g_strdup_with_max_size (const char *s,
+ int max_size);
+char ** _g_get_template_from_text (const char *s_template);
+char * _g_get_name_from_template (char **s_template,
+ int num);
+char * _g_replace (const char *str,
+ const char *from_str,
+ const char *to_str);
+char * _g_replace_pattern (const char *utf8_text,
+ gunichar pattern,
+ const char *value);
+char * _g_utf8_replace (const char *string,
+ const char *pattern,
+ const char *replacement);
+char * _g_utf8_strndup (const char *str,
+ gsize n);
+char ** _g_utf8_strsplit (const char *string,
+ const char *delimiter,
+ int max_tokens);
+char * _g_utf8_strstrip (const char *str);
+gboolean _g_utf8_all_spaces (const char *utf8_string);
+char * _g_utf8_remove_extension (const char *str);
+GList * _g_list_insert_list_before (GList *list1,
+ GList *sibling,
+ GList *list2);
+const char * get_static_string (const char *s);
+char * _g_rand_string (int len);
+int _g_strv_find (char **v,
+ const char *s);
+char * _g_str_remove_suffix (const char *s,
+ const char *suffix);
-char * _g_utf8_strndup (const char *str,
- gsize n);
+/* Regexp utils */
-char ** _g_utf8_strsplit (const char *str,
- gunichar delimiter);
+GRegex ** get_regexps_from_pattern (const char *pattern_string,
+ GRegexCompileFlags compile_options);
+gboolean string_matches_regexps (GRegex **regexps,
+ const char *string,
+ GRegexMatchFlags match_options);
+void free_regexps (GRegex **regexps);
-char * _g_utf8_strstrip (const char *str);
-gboolean _g_utf8_all_spaces (const char *utf8_string);
+/* URI utils */
-void set_time_string (char *buffer,
- gint64 time);
+const char * get_home_uri (void);
+const char * get_desktop_uri (void);
+char * xdg_user_dir_lookup (const char *type);
+int uricmp (const char *uri1,
+ const char *uri2);
+gboolean same_uri (const char *uri1,
+ const char *uri2);
+void _g_string_list_free (GList *string_list);
+GList * _g_string_list_dup (GList *string_list);
+GType g_string_list_get_type (void);
+GList * get_file_list_from_url_list (char *url_list);
+const char * _g_uri_get_basename (const char *uri);
+const char * _g_uri_get_file_extension (const char *uri);
+gboolean _g_uri_is_file (const char *uri);
+gboolean _g_uri_is_dir (const char *uri);
+gboolean _g_uri_parent_of_uri (const char *dir,
+ const char *file);
+char * _g_uri_get_parent (const char *uri);
+char * _g_uri_remove_extension (const char *uri);
+char * _g_build_uri (const char *base,
+ ...);
+/* GIO utils */
-/**/
+gboolean _g_file_equal (GFile *file1,
+ GFile *file2);
+char * _g_file_get_display_name (GFile *file);
+GFileType _g_file_get_standard_type (GFile *file);
+GFile * _g_file_get_destination (GFile *source,
+ GFile *source_base,
+ GFile *destination_folder);
+GFile * _g_file_get_child (GFile *file,
+ ...);
+GIcon * _g_file_get_icon (GFile *file);
+GList * _g_file_list_dup (GList *l);
+void _g_file_list_free (GList *l);
+GList * _g_file_list_new_from_uri_list (GList *uris);
+GList * _g_file_list_new_from_uriv (char **uris);
+GList * _g_file_list_find_file (GList *l,
+ GFile *file);
+const char* _g_file_get_mime_type (GFile *file,
+ gboolean fast_file_type);
+void _g_file_get_modification_time (GFile *file,
+ GTimeVal *result);
+time_t _g_file_get_mtime (GFile *file);
+int _g_file_cmp_uris (GFile *a,
+ GFile *b);
+int _g_file_cmp_modification_time (GFile *a,
+ GFile *b);
+goffset _g_file_get_size (GFile *info);
+GFile * _g_file_resolve_all_symlinks (GFile *file,
+ GError **error);
+GFile * _g_file_append_prefix (GFile *file,
+ const char *prefix);
+GFile * _g_file_append_path (GFile *file,
+ const char *path);
+gboolean _g_file_attributes_matches (const char *attributes,
+ const char *mask);
+void _g_file_info_swap_attributes (GFileInfo *info,
+ const char *attr1,
+ const char *attr2);
+gboolean _g_mime_type_is_image (const char *mime_type);
+gboolean _g_mime_type_is_video (const char *mime_type);
+gboolean _g_mime_type_is_audio (const char *mime_type);
+gboolean _g_volume_equal (GVolume *v1,
+ GVolume *v2);
+gboolean _g_drive_equal (GDrive *d1,
+ GDrive *d2);
+char * _g_make_temp_directory (void);
-#ifndef __GNUC__
-#define __FUNCTION__ ""
-#endif
+/* Other */
-#define DEBUG_INFO __FILE__, __LINE__, __FUNCTION__
+char * _g_format_duration_for_display (gint64 msecs);
-void debug (const char *file,
- int line,
- const char *function,
- const char *format, ...);
+G_END_DECLS
#endif /* _GLIB_UTILS_H */
diff --git a/src/gnome-desktop-thumbnail.c b/src/gnome-desktop-thumbnail.c
new file mode 100644
index 0000000..7cdbf95
--- /dev/null
+++ b/src/gnome-desktop-thumbnail.c
@@ -0,0 +1,1329 @@
+/*
+ * gnome-thumbnail.c: Utilities for handling thumbnails
+ *
+ * Copyright (C) 2002 Red Hat, Inc.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome 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.
+ *
+ * The Gnome Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with the Gnome Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl redhat com>
+ */
+
+#include <config.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <time.h>
+#include <math.h>
+#include <string.h>
+#include <glib.h>
+#include <stdio.h>
+
+#define GDK_PIXBUF_ENABLE_BACKEND
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#define GNOME_DESKTOP_USE_UNSTABLE_API
+#include "gnome-desktop-thumbnail.h"
+#include <gconf/gconf.h>
+#include <gconf/gconf-client.h>
+#include <glib/gstdio.h>
+
+#define SECONDS_BETWEEN_STATS 10
+
+struct _GnomeDesktopThumbnailFactoryPrivate {
+ char *application;
+ GnomeDesktopThumbnailSize size;
+
+ GMutex *lock;
+
+ GHashTable *scripts_hash;
+ guint thumbnailers_notify;
+ guint reread_scheduled;
+};
+
+static void gnome_desktop_thumbnail_factory_init (GnomeDesktopThumbnailFactory *factory);
+static void gnome_desktop_thumbnail_factory_class_init (GnomeDesktopThumbnailFactoryClass *class);
+
+G_DEFINE_TYPE (GnomeDesktopThumbnailFactory,
+ gnome_desktop_thumbnail_factory,
+ G_TYPE_OBJECT)
+#define parent_class gnome_desktop_thumbnail_factory_parent_class
+
+typedef struct {
+ gint width;
+ gint height;
+ gint input_width;
+ gint input_height;
+ gboolean preserve_aspect_ratio;
+} SizePrepareContext;
+
+#define LOAD_BUFFER_SIZE 4096
+
+static void
+size_prepared_cb (GdkPixbufLoader *loader,
+ int width,
+ int height,
+ gpointer data)
+{
+ SizePrepareContext *info = data;
+
+ g_return_if_fail (width > 0 && height > 0);
+
+ info->input_width = width;
+ info->input_height = height;
+
+ if (width < info->width && height < info->height) return;
+
+ if (info->preserve_aspect_ratio &&
+ (info->width > 0 || info->height > 0)) {
+ if (info->width < 0)
+ {
+ width = width * (double)info->height/(double)height;
+ height = info->height;
+ }
+ else if (info->height < 0)
+ {
+ height = height * (double)info->width/(double)width;
+ width = info->width;
+ }
+ else if ((double)height * (double)info->width >
+ (double)width * (double)info->height) {
+ width = 0.5 + (double)width * (double)info->height / (double)height;
+ height = info->height;
+ } else {
+ height = 0.5 + (double)height * (double)info->width / (double)width;
+ width = info->width;
+ }
+ } else {
+ if (info->width > 0)
+ width = info->width;
+ if (info->height > 0)
+ height = info->height;
+ }
+
+ gdk_pixbuf_loader_set_size (loader, width, height);
+}
+
+static GdkPixbuf *
+_gdk_pixbuf_new_from_uri_at_scale (const char *uri,
+ gint width,
+ gint height,
+ gboolean preserve_aspect_ratio)
+{
+ gboolean result;
+ char buffer[LOAD_BUFFER_SIZE];
+ gsize bytes_read;
+ GdkPixbufLoader *loader;
+ GdkPixbuf *pixbuf;
+ GdkPixbufAnimation *animation;
+ GdkPixbufAnimationIter *iter;
+ gboolean has_frame;
+ SizePrepareContext info;
+ GFile *file;
+ GInputStream *input_stream;
+
+ g_return_val_if_fail (uri != NULL, NULL);
+
+ file = g_file_new_for_uri (uri);
+
+ input_stream = G_INPUT_STREAM (g_file_read (file, NULL, NULL));
+ if (input_stream == NULL) {
+ g_object_unref (file);
+ return NULL;
+ }
+
+ loader = gdk_pixbuf_loader_new ();
+ if (1 <= width || 1 <= height) {
+ info.width = width;
+ info.height = height;
+ info.input_width = info.input_height = 0;
+ info.preserve_aspect_ratio = preserve_aspect_ratio;
+ g_signal_connect (loader, "size-prepared", G_CALLBACK (size_prepared_cb), &info);
+ }
+
+ has_frame = FALSE;
+
+ result = FALSE;
+ while (!has_frame) {
+
+ bytes_read = g_input_stream_read (input_stream,
+ buffer,
+ sizeof (buffer),
+ NULL,
+ NULL);
+ if (bytes_read == -1) {
+ break;
+ }
+ result = TRUE;
+ if (bytes_read == 0) {
+ break;
+ }
+
+ if (!gdk_pixbuf_loader_write (loader,
+ (unsigned char *)buffer,
+ bytes_read,
+ NULL)) {
+ result = FALSE;
+ break;
+ }
+
+ animation = gdk_pixbuf_loader_get_animation (loader);
+ if (animation) {
+ iter = gdk_pixbuf_animation_get_iter (animation, NULL);
+ if (!gdk_pixbuf_animation_iter_on_currently_loading_frame (iter)) {
+ has_frame = TRUE;
+ }
+ g_object_unref (iter);
+ }
+ }
+
+ gdk_pixbuf_loader_close (loader, NULL);
+
+ if (!result) {
+ g_object_unref (G_OBJECT (loader));
+ g_input_stream_close (input_stream, NULL, NULL);
+ g_object_unref (input_stream);
+ g_object_unref (file);
+ return NULL;
+ }
+
+ g_input_stream_close (input_stream, NULL, NULL);
+ g_object_unref (input_stream);
+ g_object_unref (file);
+
+ pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
+ if (pixbuf != NULL) {
+ g_object_ref (G_OBJECT (pixbuf));
+ g_object_set_data (G_OBJECT (pixbuf), "gnome-original-width",
+ GINT_TO_POINTER (info.input_width));
+ g_object_set_data (G_OBJECT (pixbuf), "gnome-original-height",
+ GINT_TO_POINTER (info.input_height));
+ }
+ g_object_unref (G_OBJECT (loader));
+
+ return pixbuf;
+}
+
+static void
+gnome_desktop_thumbnail_factory_finalize (GObject *object)
+{
+ GnomeDesktopThumbnailFactory *factory;
+ GnomeDesktopThumbnailFactoryPrivate *priv;
+ GConfClient *client;
+
+ factory = GNOME_DESKTOP_THUMBNAIL_FACTORY (object);
+
+ priv = factory->priv;
+
+ g_free (priv->application);
+ priv->application = NULL;
+
+ if (priv->reread_scheduled != 0) {
+ g_source_remove (priv->reread_scheduled);
+ priv->reread_scheduled = 0;
+ }
+
+ if (priv->thumbnailers_notify != 0) {
+ client = gconf_client_get_default ();
+ gconf_client_notify_remove (client, priv->thumbnailers_notify);
+ priv->thumbnailers_notify = 0;
+ g_object_unref (client);
+ }
+
+ if (priv->scripts_hash)
+ {
+ g_hash_table_destroy (priv->scripts_hash);
+ priv->scripts_hash = NULL;
+ }
+
+ if (priv->lock)
+ {
+ g_mutex_free (priv->lock);
+ priv->lock = NULL;
+ }
+
+ g_free (priv);
+ factory->priv = NULL;
+
+ if (G_OBJECT_CLASS (parent_class)->finalize)
+ (* G_OBJECT_CLASS (parent_class)->finalize) (object);
+}
+
+/* Must be called on main thread */
+static GHashTable *
+read_scripts (void)
+{
+ GHashTable *scripts_hash;
+ GConfClient *client;
+ GSList *subdirs, *l;
+ char *subdir, *enable, *escape, *commandkey, *command, *mimetype;
+
+ client = gconf_client_get_default ();
+
+ if (gconf_client_get_bool (client,
+ "/desktop/gnome/thumbnailers/disable_all",
+ NULL))
+ {
+ g_object_unref (G_OBJECT (client));
+ return NULL;
+ }
+
+ scripts_hash = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free, g_free);
+
+
+ subdirs = gconf_client_all_dirs (client, "/desktop/gnome/thumbnailers", NULL);
+
+ for (l = subdirs; l != NULL; l = l->next)
+ {
+ subdir = l->data;
+
+ enable = g_strdup_printf ("%s/enable", subdir);
+ if (gconf_client_get_bool (client,
+ enable,
+ NULL))
+ {
+ commandkey = g_strdup_printf ("%s/command", subdir);
+ command = gconf_client_get_string (client, commandkey, NULL);
+ g_free (commandkey);
+
+ if (command != NULL) {
+ mimetype = strrchr (subdir, '/');
+ if (mimetype != NULL)
+ {
+ mimetype++; /* skip past slash */
+
+ /* Convert '@' to slash in mimetype */
+ escape = strchr (mimetype, '@');
+ if (escape != NULL)
+ *escape = '/';
+
+ /* Convert any remaining '@' to '+' in mimetype */
+ while ((escape = strchr (mimetype, '@')) != NULL)
+ *escape = '+';
+
+ g_hash_table_insert (scripts_hash,
+ g_strdup (mimetype), command);
+ }
+ else
+ {
+ g_free (command);
+ }
+ }
+ }
+ g_free (enable);
+
+ g_free (subdir);
+ }
+
+ g_slist_free(subdirs);
+
+ g_object_unref (G_OBJECT (client));
+
+ return scripts_hash;
+}
+
+
+/* Must be called on main thread */
+static void
+gnome_desktop_thumbnail_factory_reread_scripts (GnomeDesktopThumbnailFactory *factory)
+{
+ GnomeDesktopThumbnailFactoryPrivate *priv = factory->priv;
+ GHashTable *scripts_hash;
+
+ scripts_hash = read_scripts ();
+
+ g_mutex_lock (priv->lock);
+
+ if (priv->scripts_hash != NULL)
+ g_hash_table_destroy (priv->scripts_hash);
+
+ priv->scripts_hash = scripts_hash;
+
+ g_mutex_unlock (priv->lock);
+}
+
+static gboolean
+reread_idle_callback (gpointer user_data)
+{
+ GnomeDesktopThumbnailFactory *factory = user_data;
+ GnomeDesktopThumbnailFactoryPrivate *priv = factory->priv;
+
+ gnome_desktop_thumbnail_factory_reread_scripts (factory);
+
+ g_mutex_lock (priv->lock);
+ priv->reread_scheduled = 0;
+ g_mutex_unlock (priv->lock);
+
+ return FALSE;
+}
+
+static void
+schedule_reread (GConfClient* client,
+ guint cnxn_id,
+ GConfEntry *entry,
+ gpointer user_data)
+{
+ GnomeDesktopThumbnailFactory *factory = user_data;
+ GnomeDesktopThumbnailFactoryPrivate *priv = factory->priv;
+
+ g_mutex_lock (priv->lock);
+
+ if (priv->reread_scheduled == 0)
+ {
+ priv->reread_scheduled = g_idle_add (reread_idle_callback,
+ factory);
+ }
+
+ g_mutex_unlock (priv->lock);
+}
+
+
+static void
+gnome_desktop_thumbnail_factory_init (GnomeDesktopThumbnailFactory *factory)
+{
+ GConfClient *client;
+ GnomeDesktopThumbnailFactoryPrivate *priv;
+
+ factory->priv = g_new0 (GnomeDesktopThumbnailFactoryPrivate, 1);
+
+ priv = factory->priv;
+
+ priv->size = GNOME_DESKTOP_THUMBNAIL_SIZE_NORMAL;
+ priv->application = g_strdup ("gnome-thumbnail-factory");
+
+ priv->scripts_hash = NULL;
+
+ priv->lock = g_mutex_new ();
+
+ gnome_desktop_thumbnail_factory_reread_scripts (factory);
+
+ client = gconf_client_get_default ();
+ gconf_client_add_dir (client,
+ "/desktop/gnome",
+ GCONF_CLIENT_PRELOAD_NONE, NULL);
+
+ priv->thumbnailers_notify = gconf_client_notify_add (client, "/desktop/gnome/thumbnailers",
+ schedule_reread, factory, NULL,
+ NULL);
+
+ g_object_unref (G_OBJECT (client));
+}
+
+static void
+gnome_desktop_thumbnail_factory_class_init (GnomeDesktopThumbnailFactoryClass *class)
+{
+ GObjectClass *gobject_class;
+
+ gobject_class = G_OBJECT_CLASS (class);
+
+ gobject_class->finalize = gnome_desktop_thumbnail_factory_finalize;
+}
+
+/**
+ * gnome_desktop_thumbnail_factory_new:
+ * @size: The thumbnail size to use
+ *
+ * Creates a new #GnomeDesktopThumbnailFactory.
+ *
+ * This function must be called on the main thread.
+ *
+ * Return value: a new #GnomeDesktopThumbnailFactory
+ *
+ * Since: 2.2
+ **/
+GnomeDesktopThumbnailFactory *
+gnome_desktop_thumbnail_factory_new (GnomeDesktopThumbnailSize size)
+{
+ GnomeDesktopThumbnailFactory *factory;
+
+ factory = g_object_new (GNOME_DESKTOP_TYPE_THUMBNAIL_FACTORY, NULL);
+
+ factory->priv->size = size;
+
+ return factory;
+}
+
+/**
+ * gnome_desktop_thumbnail_factory_lookup:
+ * @factory: a #GnomeDesktopThumbnailFactory
+ * @uri: the uri of a file
+ * @mtime: the mtime of the file
+ *
+ * Tries to locate an existing thumbnail for the file specified.
+ *
+ * Usage of this function is threadsafe.
+ *
+ * Return value: The absolute path of the thumbnail, or %NULL if none exist.
+ *
+ * Since: 2.2
+ **/
+char *
+gnome_desktop_thumbnail_factory_lookup (GnomeDesktopThumbnailFactory *factory,
+ const char *uri,
+ time_t mtime)
+{
+ GnomeDesktopThumbnailFactoryPrivate *priv = factory->priv;
+ char *path, *file;
+ GChecksum *checksum;
+ guint8 digest[16];
+ gsize digest_len = sizeof (digest);
+ GdkPixbuf *pixbuf;
+ gboolean res;
+
+ g_return_val_if_fail (uri != NULL, NULL);
+
+ res = FALSE;
+
+ checksum = g_checksum_new (G_CHECKSUM_MD5);
+ g_checksum_update (checksum, (const guchar *) uri, strlen (uri));
+
+ g_checksum_get_digest (checksum, digest, &digest_len);
+ g_assert (digest_len == 16);
+
+ file = g_strconcat (g_checksum_get_string (checksum), ".png", NULL);
+
+ path = g_build_filename (g_get_home_dir (),
+ ".thumbnails",
+ (priv->size == GNOME_DESKTOP_THUMBNAIL_SIZE_NORMAL)?"normal":"large",
+ file,
+ NULL);
+ g_free (file);
+
+ pixbuf = gdk_pixbuf_new_from_file (path, NULL);
+ if (pixbuf != NULL)
+ {
+ res = gnome_desktop_thumbnail_is_valid (pixbuf, uri, mtime);
+ g_object_unref (pixbuf);
+ }
+
+ g_checksum_free (checksum);
+
+ if (res)
+ return path;
+
+ g_free (path);
+ return FALSE;
+}
+
+/**
+ * gnome_desktop_thumbnail_factory_has_valid_failed_thumbnail:
+ * @factory: a #GnomeDesktopThumbnailFactory
+ * @uri: the uri of a file
+ * @mtime: the mtime of the file
+ *
+ * Tries to locate an failed thumbnail for the file specified. Writing
+ * and looking for failed thumbnails is important to avoid to try to
+ * thumbnail e.g. broken images several times.
+ *
+ * Usage of this function is threadsafe.
+ *
+ * Return value: TRUE if there is a failed thumbnail for the file.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gnome_desktop_thumbnail_factory_has_valid_failed_thumbnail (GnomeDesktopThumbnailFactory *factory,
+ const char *uri,
+ time_t mtime)
+{
+ char *path, *file;
+ GdkPixbuf *pixbuf;
+ gboolean res;
+ GChecksum *checksum;
+ guint8 digest[16];
+ gsize digest_len = sizeof (digest);
+
+ checksum = g_checksum_new (G_CHECKSUM_MD5);
+ g_checksum_update (checksum, (const guchar *) uri, strlen (uri));
+
+ g_checksum_get_digest (checksum, digest, &digest_len);
+ g_assert (digest_len == 16);
+
+ res = FALSE;
+
+ file = g_strconcat (g_checksum_get_string (checksum), ".png", NULL);
+
+ path = g_build_filename (g_get_home_dir (),
+ ".thumbnails/fail",
+ factory->priv->application,
+ file,
+ NULL);
+ g_free (file);
+
+ pixbuf = gdk_pixbuf_new_from_file (path, NULL);
+ g_free (path);
+
+ if (pixbuf)
+ {
+ res = gnome_desktop_thumbnail_is_valid (pixbuf, uri, mtime);
+ g_object_unref (pixbuf);
+ }
+
+ g_checksum_free (checksum);
+
+ return res;
+}
+
+static gboolean
+mimetype_supported_by_gdk_pixbuf (const char *mime_type)
+{
+ guint i;
+ static GHashTable *formats_hash = NULL;
+
+ if (!formats_hash) {
+ GSList *formats, *list;
+
+ formats_hash = g_hash_table_new (g_str_hash, g_str_equal);
+
+ formats = gdk_pixbuf_get_formats ();
+ list = formats;
+
+ while (list) {
+ GdkPixbufFormat *format = list->data;
+ gchar **mime_types;
+
+ mime_types = gdk_pixbuf_format_get_mime_types (format);
+
+ for (i = 0; mime_types[i] != NULL; i++)
+ g_hash_table_insert (formats_hash,
+ (gpointer) g_strdup (mime_types[i]),
+ GUINT_TO_POINTER (1));
+
+ g_strfreev (mime_types);
+ list = list->next;
+ }
+ g_slist_free (formats);
+ }
+
+ if (g_hash_table_lookup (formats_hash, mime_type))
+ return TRUE;
+
+ return FALSE;
+}
+
+/**
+ * gnome_desktop_thumbnail_factory_can_thumbnail:
+ * @factory: a #GnomeDesktopThumbnailFactory
+ * @uri: the uri of a file
+ * @mime_type: the mime type of the file
+ * @mtime: the mtime of the file
+ *
+ * Returns TRUE if this GnomeIconFactory can (at least try) to thumbnail
+ * this file. Thumbnails or files with failed thumbnails won't be thumbnailed.
+ *
+ * Usage of this function is threadsafe.
+ *
+ * Return value: TRUE if the file can be thumbnailed.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gnome_desktop_thumbnail_factory_can_thumbnail (GnomeDesktopThumbnailFactory *factory,
+ const char *uri,
+ const char *mime_type,
+ time_t mtime)
+{
+ /* Don't thumbnail thumbnails */
+ if (uri &&
+ strncmp (uri, "file:/", 6) == 0 &&
+ strstr (uri, "/.thumbnails/") != NULL)
+ return FALSE;
+
+ if (mime_type != NULL &&
+ (mimetype_supported_by_gdk_pixbuf (mime_type) ||
+ (factory->priv->scripts_hash != NULL &&
+ g_hash_table_lookup (factory->priv->scripts_hash, mime_type))))
+ {
+ return !gnome_desktop_thumbnail_factory_has_valid_failed_thumbnail (factory,
+ uri,
+ mtime);
+ }
+
+ return FALSE;
+}
+
+static char *
+expand_thumbnailing_script (const char *script,
+ const int size,
+ const char *inuri,
+ const char *outfile)
+{
+ GString *str;
+ const char *p, *last;
+ char *localfile, *quoted;
+ gboolean got_in;
+
+ str = g_string_new (NULL);
+
+ got_in = FALSE;
+ last = script;
+ while ((p = strchr (last, '%')) != NULL)
+ {
+ g_string_append_len (str, last, p - last);
+ p++;
+
+ switch (*p) {
+ case 'u':
+ quoted = g_shell_quote (inuri);
+ g_string_append (str, quoted);
+ g_free (quoted);
+ got_in = TRUE;
+ p++;
+ break;
+ case 'i':
+ localfile = g_filename_from_uri (inuri, NULL, NULL);
+ if (localfile)
+ {
+ quoted = g_shell_quote (localfile);
+ g_string_append (str, quoted);
+ got_in = TRUE;
+ g_free (quoted);
+ g_free (localfile);
+ }
+ p++;
+ break;
+ case 'o':
+ quoted = g_shell_quote (outfile);
+ g_string_append (str, quoted);
+ g_free (quoted);
+ p++;
+ break;
+ case 's':
+ g_string_append_printf (str, "%d", size);
+ p++;
+ break;
+ case '%':
+ g_string_append_c (str, '%');
+ p++;
+ break;
+ case 0:
+ default:
+ break;
+ }
+ last = p;
+ }
+ g_string_append (str, last);
+
+ if (got_in)
+ return g_string_free (str, FALSE);
+
+ g_string_free (str, TRUE);
+ return NULL;
+}
+
+/**
+ * gnome_desktop_thumbnail_factory_generate_thumbnail:
+ * @factory: a #GnomeDesktopThumbnailFactory
+ * @uri: the uri of a file
+ * @mime_type: the mime type of the file
+ *
+ * Tries to generate a thumbnail for the specified file. If it succeeds
+ * it returns a pixbuf that can be used as a thumbnail.
+ *
+ * Usage of this function is threadsafe.
+ *
+ * Return value: thumbnail pixbuf if thumbnailing succeeded, %NULL otherwise.
+ *
+ * Since: 2.2
+ **/
+GdkPixbuf *
+gnome_desktop_thumbnail_factory_generate_thumbnail (GnomeDesktopThumbnailFactory *factory,
+ const char *uri,
+ const char *mime_type)
+{
+ GdkPixbuf *pixbuf, *scaled, *tmp_pixbuf;
+ char *script, *expanded_script;
+ int width, height, size;
+ int original_width = 0;
+ int original_height = 0;
+ char dimension[12];
+ double scale;
+ int exit_status;
+ char *tmpname;
+
+ g_return_val_if_fail (uri != NULL, NULL);
+ g_return_val_if_fail (mime_type != NULL, NULL);
+
+ /* Doesn't access any volatile fields in factory, so it's threadsafe */
+
+ size = 128;
+ if (factory->priv->size == GNOME_DESKTOP_THUMBNAIL_SIZE_LARGE)
+ size = 256;
+
+ pixbuf = NULL;
+
+ script = NULL;
+ if (factory->priv->scripts_hash != NULL)
+ script = g_hash_table_lookup (factory->priv->scripts_hash, mime_type);
+
+ if (script)
+ {
+ int fd;
+ GError *error = NULL;
+
+ fd = g_file_open_tmp (".gnome_desktop_thumbnail.XXXXXX", &tmpname, &error);
+
+ if (fd != -1)
+ {
+ close (fd);
+
+ expanded_script = expand_thumbnailing_script (script, size, uri, tmpname);
+ if (expanded_script != NULL &&
+ g_spawn_command_line_sync (expanded_script,
+ NULL, NULL, &exit_status, NULL) &&
+ exit_status == 0)
+ {
+ pixbuf = gdk_pixbuf_new_from_file (tmpname, NULL);
+ }
+
+ g_free (expanded_script);
+ g_unlink(tmpname);
+ }
+ g_free (tmpname);
+ }
+
+ /* Fall back to gdk-pixbuf */
+ if (pixbuf == NULL)
+ {
+ pixbuf = _gdk_pixbuf_new_from_uri_at_scale (uri, size, size, TRUE);
+
+ if (pixbuf != NULL)
+ {
+ original_width = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (pixbuf),
+ "gnome-original-width"));
+ original_height = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (pixbuf),
+ "gnome-original-height"));
+ }
+ }
+
+ if (pixbuf == NULL)
+ return NULL;
+
+ /* The pixbuf loader may attach an "orientation" option to the pixbuf,
+ if the tiff or exif jpeg file had an orientation tag. Rotate/flip
+ the pixbuf as specified by this tag, if present. */
+ tmp_pixbuf = gdk_pixbuf_apply_embedded_orientation (pixbuf);
+ g_object_unref (pixbuf);
+ pixbuf = tmp_pixbuf;
+
+ width = gdk_pixbuf_get_width (pixbuf);
+ height = gdk_pixbuf_get_height (pixbuf);
+
+ if (width > size || height > size)
+ {
+ const gchar *orig_width, *orig_height;
+ scale = (double)size / MAX (width, height);
+
+ scaled = gnome_desktop_thumbnail_scale_down_pixbuf (pixbuf,
+ floor (width * scale + 0.5),
+ floor (height * scale + 0.5));
+
+ orig_width = gdk_pixbuf_get_option (pixbuf, "tEXt::Thumb::Image::Width");
+ orig_height = gdk_pixbuf_get_option (pixbuf, "tEXt::Thumb::Image::Height");
+
+ if (orig_width != NULL) {
+ gdk_pixbuf_set_option (scaled, "tEXt::Thumb::Image::Width", orig_width);
+ }
+ if (orig_height != NULL) {
+ gdk_pixbuf_set_option (scaled, "tEXt::Thumb::Image::Height", orig_height);
+ }
+
+ g_object_unref (pixbuf);
+ pixbuf = scaled;
+ }
+
+ if (original_width > 0) {
+ g_snprintf (dimension, sizeof (dimension), "%i", original_width);
+ gdk_pixbuf_set_option (pixbuf, "tEXt::Thumb::Image::Width", dimension);
+ }
+ if (original_height > 0) {
+ g_snprintf (dimension, sizeof (dimension), "%i", original_height);
+ gdk_pixbuf_set_option (pixbuf, "tEXt::Thumb::Image::Height", dimension);
+ }
+
+ return pixbuf;
+}
+
+
+gboolean
+gnome_desktop_thumbnail_factory_generate_thumbnail_async (GnomeDesktopThumbnailFactory *factory,
+ const char *uri,
+ const char *mime_type,
+ GPid *pid,
+ char **tmpname,
+ GError **error)
+{
+ gboolean retval = FALSE;
+ int size;
+ char *script;
+ int fd;
+ char *expanded_script;
+ int argc;
+ char **argv;
+
+ g_return_val_if_fail (uri != NULL, FALSE);
+ g_return_val_if_fail (mime_type != NULL, FALSE);
+
+ size = 128;
+ if (factory->priv->size == GNOME_DESKTOP_THUMBNAIL_SIZE_LARGE)
+ size = 256;
+
+ script = NULL;
+ if (factory->priv->scripts_hash != NULL)
+ script = g_hash_table_lookup (factory->priv->scripts_hash, mime_type);
+
+ if (script == NULL)
+ return FALSE;
+
+ fd = g_file_open_tmp (".gnome_desktop_thumbnail.XXXXXX", tmpname, error);
+ if (fd == -1)
+ return FALSE;
+ close (fd);
+
+ expanded_script = expand_thumbnailing_script (script, size, uri, *tmpname);
+ if (g_shell_parse_argv (expanded_script, &argc, &argv, error))
+ if (g_spawn_async (NULL, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, pid, error))
+ retval = TRUE;
+
+ g_free (expanded_script);
+
+ return retval;
+}
+
+
+GdkPixbuf *
+gnome_desktop_thumbnail_factory_load_from_tempfile (GnomeDesktopThumbnailFactory *factory,
+ char **tmpname)
+{
+ GdkPixbuf *pixbuf;
+ GdkPixbuf *tmp_pixbuf;
+
+ pixbuf = gdk_pixbuf_new_from_file (*tmpname, NULL);
+ g_unlink (*tmpname);
+ g_free (*tmpname);
+ *tmpname = NULL;
+
+ if (pixbuf == NULL)
+ return NULL;
+
+ tmp_pixbuf = gdk_pixbuf_apply_embedded_orientation (pixbuf);
+ g_object_unref (pixbuf);
+ pixbuf = tmp_pixbuf;
+
+ return pixbuf;
+}
+
+
+static gboolean
+make_thumbnail_dirs (GnomeDesktopThumbnailFactory *factory)
+{
+ char *thumbnail_dir;
+ char *image_dir;
+ gboolean res;
+
+ res = FALSE;
+
+ thumbnail_dir = g_build_filename (g_get_home_dir (),
+ ".thumbnails",
+ NULL);
+ if (!g_file_test (thumbnail_dir, G_FILE_TEST_IS_DIR))
+ {
+ g_mkdir (thumbnail_dir, 0700);
+ res = TRUE;
+ }
+
+ image_dir = g_build_filename (thumbnail_dir,
+ (factory->priv->size == GNOME_DESKTOP_THUMBNAIL_SIZE_NORMAL)?"normal":"large",
+ NULL);
+ if (!g_file_test (image_dir, G_FILE_TEST_IS_DIR))
+ {
+ g_mkdir (image_dir, 0700);
+ res = TRUE;
+ }
+
+ g_free (thumbnail_dir);
+ g_free (image_dir);
+
+ return res;
+}
+
+static gboolean
+make_thumbnail_fail_dirs (GnomeDesktopThumbnailFactory *factory)
+{
+ char *thumbnail_dir;
+ char *fail_dir;
+ char *app_dir;
+ gboolean res;
+
+ res = FALSE;
+
+ thumbnail_dir = g_build_filename (g_get_home_dir (),
+ ".thumbnails",
+ NULL);
+ if (!g_file_test (thumbnail_dir, G_FILE_TEST_IS_DIR))
+ {
+ g_mkdir (thumbnail_dir, 0700);
+ res = TRUE;
+ }
+
+ fail_dir = g_build_filename (thumbnail_dir,
+ "fail",
+ NULL);
+ if (!g_file_test (fail_dir, G_FILE_TEST_IS_DIR))
+ {
+ g_mkdir (fail_dir, 0700);
+ res = TRUE;
+ }
+
+ app_dir = g_build_filename (fail_dir,
+ factory->priv->application,
+ NULL);
+ if (!g_file_test (app_dir, G_FILE_TEST_IS_DIR))
+ {
+ g_mkdir (app_dir, 0700);
+ res = TRUE;
+ }
+
+ g_free (thumbnail_dir);
+ g_free (fail_dir);
+ g_free (app_dir);
+
+ return res;
+}
+
+
+/**
+ * gnome_desktop_thumbnail_factory_save_thumbnail:
+ * @factory: a #GnomeDesktopThumbnailFactory
+ * @thumbnail: the thumbnail as a pixbuf
+ * @uri: the uri of a file
+ * @original_mtime: the modification time of the original file
+ *
+ * Saves @thumbnail at the right place. If the save fails a
+ * failed thumbnail is written.
+ *
+ * Usage of this function is threadsafe.
+ *
+ * Since: 2.2
+ **/
+void
+gnome_desktop_thumbnail_factory_save_thumbnail (GnomeDesktopThumbnailFactory *factory,
+ GdkPixbuf *thumbnail,
+ const char *uri,
+ time_t original_mtime)
+{
+ GnomeDesktopThumbnailFactoryPrivate *priv = factory->priv;
+ char *path, *file, *dir;
+ char *tmp_path;
+ const char *width, *height;
+ int tmp_fd;
+ char mtime_str[21];
+ gboolean saved_ok;
+ GChecksum *checksum;
+ guint8 digest[16];
+ gsize digest_len = sizeof (digest);
+
+ checksum = g_checksum_new (G_CHECKSUM_MD5);
+ g_checksum_update (checksum, (const guchar *) uri, strlen (uri));
+
+ g_checksum_get_digest (checksum, digest, &digest_len);
+ g_assert (digest_len == 16);
+
+ file = g_strconcat (g_checksum_get_string (checksum), ".png", NULL);
+
+ dir = g_build_filename (g_get_home_dir (),
+ ".thumbnails",
+ (priv->size == GNOME_DESKTOP_THUMBNAIL_SIZE_NORMAL)?"normal":"large",
+ NULL);
+
+ path = g_build_filename (dir,
+ file,
+ NULL);
+ g_free (file);
+
+ g_checksum_free (checksum);
+
+ tmp_path = g_strconcat (path, ".XXXXXX", NULL);
+
+ tmp_fd = g_mkstemp (tmp_path);
+ if (tmp_fd == -1 &&
+ make_thumbnail_dirs (factory))
+ {
+ g_free (tmp_path);
+ tmp_path = g_strconcat (path, ".XXXXXX", NULL);
+ tmp_fd = g_mkstemp (tmp_path);
+ }
+
+ if (tmp_fd == -1)
+ {
+ gnome_desktop_thumbnail_factory_create_failed_thumbnail (factory, uri, original_mtime);
+ g_free (dir);
+ g_free (tmp_path);
+ g_free (path);
+ return;
+ }
+ close (tmp_fd);
+
+ g_snprintf (mtime_str, 21, "%ld", original_mtime);
+ width = gdk_pixbuf_get_option (thumbnail, "tEXt::Thumb::Image::Width");
+ height = gdk_pixbuf_get_option (thumbnail, "tEXt::Thumb::Image::Height");
+
+ if (width != NULL && height != NULL)
+ saved_ok = gdk_pixbuf_save (thumbnail,
+ tmp_path,
+ "png", NULL,
+ "tEXt::Thumb::Image::Width", width,
+ "tEXt::Thumb::Image::Height", height,
+ "tEXt::Thumb::URI", uri,
+ "tEXt::Thumb::MTime", mtime_str,
+ "tEXt::Software", "GNOME::ThumbnailFactory",
+ NULL);
+ else
+ saved_ok = gdk_pixbuf_save (thumbnail,
+ tmp_path,
+ "png", NULL,
+ "tEXt::Thumb::URI", uri,
+ "tEXt::Thumb::MTime", mtime_str,
+ "tEXt::Software", "GNOME::ThumbnailFactory",
+ NULL);
+
+
+ if (saved_ok)
+ {
+ g_chmod (tmp_path, 0600);
+ g_rename(tmp_path, path);
+ }
+ else
+ {
+ gnome_desktop_thumbnail_factory_create_failed_thumbnail (factory, uri, original_mtime);
+ }
+
+ g_free (dir);
+ g_free (path);
+ g_free (tmp_path);
+}
+
+/**
+ * gnome_desktop_thumbnail_factory_create_failed_thumbnail:
+ * @factory: a #GnomeDesktopThumbnailFactory
+ * @uri: the uri of a file
+ * @mtime: the modification time of the file
+ *
+ * Creates a failed thumbnail for the file so that we don't try
+ * to re-thumbnail the file later.
+ *
+ * Usage of this function is threadsafe.
+ *
+ * Since: 2.2
+ **/
+void
+gnome_desktop_thumbnail_factory_create_failed_thumbnail (GnomeDesktopThumbnailFactory *factory,
+ const char *uri,
+ time_t mtime)
+{
+ char *path, *file, *dir;
+ char *tmp_path;
+ int tmp_fd;
+ char mtime_str[21];
+ gboolean saved_ok;
+ GdkPixbuf *pixbuf;
+ GChecksum *checksum;
+ guint8 digest[16];
+ gsize digest_len = sizeof (digest);
+
+ checksum = g_checksum_new (G_CHECKSUM_MD5);
+ g_checksum_update (checksum, (const guchar *) uri, strlen (uri));
+
+ g_checksum_get_digest (checksum, digest, &digest_len);
+ g_assert (digest_len == 16);
+
+ file = g_strconcat (g_checksum_get_string (checksum), ".png", NULL);
+
+ dir = g_build_filename (g_get_home_dir (),
+ ".thumbnails/fail",
+ factory->priv->application,
+ NULL);
+
+ path = g_build_filename (dir,
+ file,
+ NULL);
+ g_free (file);
+
+ g_checksum_free (checksum);
+
+ tmp_path = g_strconcat (path, ".XXXXXX", NULL);
+
+ tmp_fd = g_mkstemp (tmp_path);
+ if (tmp_fd == -1 &&
+ make_thumbnail_fail_dirs (factory))
+ {
+ g_free (tmp_path);
+ tmp_path = g_strconcat (path, ".XXXXXX", NULL);
+ tmp_fd = g_mkstemp (tmp_path);
+ }
+
+ if (tmp_fd == -1)
+ {
+ g_free (dir);
+ g_free (tmp_path);
+ g_free (path);
+ return;
+ }
+ close (tmp_fd);
+
+ g_snprintf (mtime_str, 21, "%ld", mtime);
+ pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, 1, 1);
+ saved_ok = gdk_pixbuf_save (pixbuf,
+ tmp_path,
+ "png", NULL,
+ "tEXt::Thumb::URI", uri,
+ "tEXt::Thumb::MTime", mtime_str,
+ "tEXt::Software", "GNOME::ThumbnailFactory",
+ NULL);
+ g_object_unref (pixbuf);
+ if (saved_ok)
+ {
+ g_chmod (tmp_path, 0600);
+ g_rename(tmp_path, path);
+ }
+
+ g_free (dir);
+ g_free (path);
+ g_free (tmp_path);
+}
+
+/**
+ * gnome_desktop_thumbnail_md5:
+ * @uri: an uri
+ *
+ * Calculates the MD5 checksum of the uri. This can be useful
+ * if you want to manually handle thumbnail files.
+ *
+ * Return value: A string with the MD5 digest of the uri string.
+ *
+ * Since: 2.2
+ *
+ * @Deprecated: 2.22: Use #GChecksum instead
+ **/
+char *
+gnome_desktop_thumbnail_md5 (const char *uri)
+{
+ return g_compute_checksum_for_data (G_CHECKSUM_MD5,
+ (const guchar *) uri,
+ strlen (uri));
+}
+
+/**
+ * gnome_desktop_thumbnail_path_for_uri:
+ * @uri: an uri
+ * @size: a thumbnail size
+ *
+ * Returns the filename that a thumbnail of size @size for @uri would have.
+ *
+ * Return value: an absolute filename
+ *
+ * Since: 2.2
+ **/
+char *
+gnome_desktop_thumbnail_path_for_uri (const char *uri,
+ GnomeDesktopThumbnailSize size)
+{
+ char *md5;
+ char *file;
+ char *path;
+
+ md5 = gnome_desktop_thumbnail_md5 (uri);
+ file = g_strconcat (md5, ".png", NULL);
+ g_free (md5);
+
+ path = g_build_filename (g_get_home_dir (),
+ ".thumbnails",
+ (size == GNOME_DESKTOP_THUMBNAIL_SIZE_NORMAL)?"normal":"large",
+ file,
+ NULL);
+
+ g_free (file);
+
+ return path;
+}
+
+/**
+ * gnome_desktop_thumbnail_has_uri:
+ * @pixbuf: an loaded thumbnail pixbuf
+ * @uri: a uri
+ *
+ * Returns whether the thumbnail has the correct uri embedded in the
+ * Thumb::URI option in the png.
+ *
+ * Return value: TRUE if the thumbnail is for @uri
+ *
+ * Since: 2.2
+ **/
+gboolean
+gnome_desktop_thumbnail_has_uri (GdkPixbuf *pixbuf,
+ const char *uri)
+{
+ const char *thumb_uri;
+
+ thumb_uri = gdk_pixbuf_get_option (pixbuf, "tEXt::Thumb::URI");
+ if (!thumb_uri)
+ return FALSE;
+
+ return strcmp (uri, thumb_uri) == 0;
+}
+
+/**
+ * gnome_desktop_thumbnail_is_valid:
+ * @pixbuf: an loaded thumbnail #GdkPixbuf
+ * @uri: a uri
+ * @mtime: the mtime
+ *
+ * Returns whether the thumbnail has the correct uri and mtime embedded in the
+ * png options.
+ *
+ * Return value: TRUE if the thumbnail has the right @uri and @mtime
+ *
+ * Since: 2.2
+ **/
+gboolean
+gnome_desktop_thumbnail_is_valid (GdkPixbuf *pixbuf,
+ const char *uri,
+ time_t mtime)
+{
+ const char *thumb_uri, *thumb_mtime_str;
+ time_t thumb_mtime;
+
+ thumb_uri = gdk_pixbuf_get_option (pixbuf, "tEXt::Thumb::URI");
+ if (!thumb_uri)
+ return FALSE;
+ if (strcmp (uri, thumb_uri) != 0)
+ return FALSE;
+
+ thumb_mtime_str = gdk_pixbuf_get_option (pixbuf, "tEXt::Thumb::MTime");
+ if (!thumb_mtime_str)
+ return FALSE;
+ thumb_mtime = atol (thumb_mtime_str);
+ if (mtime != thumb_mtime)
+ return FALSE;
+
+ return TRUE;
+}
diff --git a/src/gnome-desktop-thumbnail.h b/src/gnome-desktop-thumbnail.h
new file mode 100644
index 0000000..dfdc1af
--- /dev/null
+++ b/src/gnome-desktop-thumbnail.h
@@ -0,0 +1,114 @@
+/*
+ * gnome-thumbnail.h: Utilities for handling thumbnails
+ *
+ * Copyright (C) 2002 Red Hat, Inc.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome 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.
+ *
+ * The Gnome Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with the Gnome Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl redhat com>
+ */
+
+#ifndef GNOME_DESKTOP_THUMBNAIL_H
+#define GNOME_DESKTOP_THUMBNAIL_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include <time.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+G_BEGIN_DECLS
+
+typedef enum {
+ GNOME_DESKTOP_THUMBNAIL_SIZE_NORMAL,
+ GNOME_DESKTOP_THUMBNAIL_SIZE_LARGE
+} GnomeDesktopThumbnailSize;
+
+#define GNOME_DESKTOP_TYPE_THUMBNAIL_FACTORY (gnome_desktop_thumbnail_factory_get_type ())
+#define GNOME_DESKTOP_THUMBNAIL_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GNOME_DESKTOP_TYPE_THUMBNAIL_FACTORY, GnomeDesktopThumbnailFactory))
+#define GNOME_DESKTOP_THUMBNAIL_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GNOME_DESKTOP_TYPE_THUMBNAIL_FACTORY, GnomeDesktopThumbnailFactoryClass))
+#define GNOME_DESKTOP_IS_THUMBNAIL_FACTORY(obj) (G_TYPE_INSTANCE_CHECK_TYPE ((obj), GNOME_DESKTOP_TYPE_THUMBNAIL_FACTORY))
+#define GNOME_DESKTOP_IS_THUMBNAIL_FACTORY_CLASS(klass) (G_TYPE_CLASS_CHECK_CLASS_TYPE ((klass), GNOME_DESKTOP_TYPE_THUMBNAIL_FACTORY))
+
+typedef struct _GnomeDesktopThumbnailFactory GnomeDesktopThumbnailFactory;
+typedef struct _GnomeDesktopThumbnailFactoryClass GnomeDesktopThumbnailFactoryClass;
+typedef struct _GnomeDesktopThumbnailFactoryPrivate GnomeDesktopThumbnailFactoryPrivate;
+
+struct _GnomeDesktopThumbnailFactory {
+ GObject parent;
+
+ GnomeDesktopThumbnailFactoryPrivate *priv;
+};
+
+struct _GnomeDesktopThumbnailFactoryClass {
+ GObjectClass parent;
+};
+
+GType gnome_desktop_thumbnail_factory_get_type (void);
+GnomeDesktopThumbnailFactory *gnome_desktop_thumbnail_factory_new (GnomeDesktopThumbnailSize size);
+
+char * gnome_desktop_thumbnail_factory_lookup (GnomeDesktopThumbnailFactory *factory,
+ const char *uri,
+ time_t mtime);
+
+gboolean gnome_desktop_thumbnail_factory_has_valid_failed_thumbnail (GnomeDesktopThumbnailFactory *factory,
+ const char *uri,
+ time_t mtime);
+gboolean gnome_desktop_thumbnail_factory_can_thumbnail (GnomeDesktopThumbnailFactory *factory,
+ const char *uri,
+ const char *mime_type,
+ time_t mtime);
+GdkPixbuf * gnome_desktop_thumbnail_factory_generate_thumbnail (GnomeDesktopThumbnailFactory *factory,
+ const char *uri,
+ const char *mime_type);
+gboolean gnome_desktop_thumbnail_factory_generate_thumbnail_async (GnomeDesktopThumbnailFactory *factory,
+ const char *uri,
+ const char *mime_type,
+ GPid *pid,
+ char **tmpname,
+ GError **error);
+GdkPixbuf * gnome_desktop_thumbnail_factory_load_from_tempfile (GnomeDesktopThumbnailFactory *factory,
+ char **tmpname);
+void gnome_desktop_thumbnail_factory_save_thumbnail (GnomeDesktopThumbnailFactory *factory,
+ GdkPixbuf *thumbnail,
+ const char *uri,
+ time_t original_mtime);
+void gnome_desktop_thumbnail_factory_create_failed_thumbnail (GnomeDesktopThumbnailFactory *factory,
+ const char *uri,
+ time_t mtime);
+
+
+/* Thumbnailing utils: */
+gboolean gnome_desktop_thumbnail_has_uri (GdkPixbuf *pixbuf,
+ const char *uri);
+gboolean gnome_desktop_thumbnail_is_valid (GdkPixbuf *pixbuf,
+ const char *uri,
+ time_t mtime);
+char * gnome_desktop_thumbnail_md5 (const char *uri);
+char * gnome_desktop_thumbnail_path_for_uri (const char *uri,
+ GnomeDesktopThumbnailSize size);
+
+
+/* Pixbuf utils */
+
+GdkPixbuf *gnome_desktop_thumbnail_scale_down_pixbuf (GdkPixbuf *pixbuf,
+ int dest_width,
+ int dest_height);
+
+G_END_DECLS
+
+#endif /* GNOME_DESKTOP_THUMBNAIL_H */
diff --git a/src/gnome-thumbnail-pixbuf-utils.c b/src/gnome-thumbnail-pixbuf-utils.c
new file mode 100644
index 0000000..833242a
--- /dev/null
+++ b/src/gnome-thumbnail-pixbuf-utils.c
@@ -0,0 +1,172 @@
+/*
+ * gnome-thumbnail-pixbuf-utils.c: Utilities for handling pixbufs when thumbnailing
+ *
+ * Copyright (C) 2002 Red Hat, Inc.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome 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.
+ *
+ * The Gnome Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with the Gnome Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl redhat com>
+ */
+
+#include <config.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+
+#define GNOME_DESKTOP_USE_UNSTABLE_API
+#include "gnome-desktop-thumbnail.h"
+
+#define LOAD_BUFFER_SIZE 65536
+
+/**
+ * gnome_thumbnail_scale_down_pixbuf:
+ * @pixbuf: a #GdkPixbuf
+ * @dest_width: the desired new width
+ * @dest_height: the desired new height
+ *
+ * Scales the pixbuf to the desired size. This function
+ * is a lot faster than gdk-pixbuf when scaling down by
+ * large amounts.
+ *
+ * Return value: a scaled pixbuf
+ *
+ * Since: 2.2
+ **/
+GdkPixbuf *
+gnome_desktop_thumbnail_scale_down_pixbuf (GdkPixbuf *pixbuf,
+ int dest_width,
+ int dest_height)
+{
+ int source_width, source_height;
+ int s_x1, s_y1, s_x2, s_y2;
+ int s_xfrac, s_yfrac;
+ int dx, dx_frac, dy, dy_frac;
+ div_t ddx, ddy;
+ int x, y;
+ int r, g, b, a;
+ int n_pixels;
+ gboolean has_alpha;
+ guchar *dest, *src, *xsrc, *src_pixels;
+ GdkPixbuf *dest_pixbuf;
+ int pixel_stride;
+ int source_rowstride, dest_rowstride;
+
+ if (dest_width == 0 || dest_height == 0) {
+ return NULL;
+ }
+
+ source_width = gdk_pixbuf_get_width (pixbuf);
+ source_height = gdk_pixbuf_get_height (pixbuf);
+
+ g_assert (source_width >= dest_width);
+ g_assert (source_height >= dest_height);
+
+ ddx = div (source_width, dest_width);
+ dx = ddx.quot;
+ dx_frac = ddx.rem;
+
+ ddy = div (source_height, dest_height);
+ dy = ddy.quot;
+ dy_frac = ddy.rem;
+
+ has_alpha = gdk_pixbuf_get_has_alpha (pixbuf);
+ source_rowstride = gdk_pixbuf_get_rowstride (pixbuf);
+ src_pixels = gdk_pixbuf_get_pixels (pixbuf);
+
+ dest_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, has_alpha, 8,
+ dest_width, dest_height);
+ dest = gdk_pixbuf_get_pixels (dest_pixbuf);
+ dest_rowstride = gdk_pixbuf_get_rowstride (dest_pixbuf);
+
+ pixel_stride = (has_alpha)?4:3;
+
+ s_y1 = 0;
+ s_yfrac = -dest_height/2;
+ while (s_y1 < source_height) {
+ s_y2 = s_y1 + dy;
+ s_yfrac += dy_frac;
+ if (s_yfrac > 0) {
+ s_y2++;
+ s_yfrac -= dest_height;
+ }
+
+ s_x1 = 0;
+ s_xfrac = -dest_width/2;
+ while (s_x1 < source_width) {
+ s_x2 = s_x1 + dx;
+ s_xfrac += dx_frac;
+ if (s_xfrac > 0) {
+ s_x2++;
+ s_xfrac -= dest_width;
+ }
+
+ /* Average block of [x1,x2[ x [y1,y2[ and store in dest */
+ r = g = b = a = 0;
+ n_pixels = 0;
+
+ src = src_pixels + s_y1 * source_rowstride + s_x1 * pixel_stride;
+ for (y = s_y1; y < s_y2; y++) {
+ xsrc = src;
+ if (has_alpha) {
+ for (x = 0; x < s_x2-s_x1; x++) {
+ n_pixels++;
+
+ r += xsrc[3] * xsrc[0];
+ g += xsrc[3] * xsrc[1];
+ b += xsrc[3] * xsrc[2];
+ a += xsrc[3];
+ xsrc += 4;
+ }
+ } else {
+ for (x = 0; x < s_x2-s_x1; x++) {
+ n_pixels++;
+ r += *xsrc++;
+ g += *xsrc++;
+ b += *xsrc++;
+ }
+ }
+ src += source_rowstride;
+ }
+
+ if (has_alpha) {
+ if (a != 0) {
+ *dest++ = r / a;
+ *dest++ = g / a;
+ *dest++ = b / a;
+ *dest++ = a / n_pixels;
+ } else {
+ *dest++ = 0;
+ *dest++ = 0;
+ *dest++ = 0;
+ *dest++ = 0;
+ }
+ } else {
+ *dest++ = r / n_pixels;
+ *dest++ = g / n_pixels;
+ *dest++ = b / n_pixels;
+ }
+
+ s_x1 = s_x2;
+ }
+ s_y1 = s_y2;
+ dest += dest_rowstride - dest_width * pixel_stride;
+ }
+
+ return dest_pixbuf;
+}
diff --git a/src/goo-marshal.list b/src/goo-marshal.list
index dd0cd20..d433cc9 100644
--- a/src/goo-marshal.list
+++ b/src/goo-marshal.list
@@ -3,4 +3,5 @@ VOID:INT,POINTER
VOID:VOID
VOID:DOUBLE
VOID:STRING
+VOID:STRING,STRING
VOID:BOOL
diff --git a/src/goo-player-info.c b/src/goo-player-info.c
index f75369c..a7bc6dc 100644
--- a/src/goo-player-info.c
+++ b/src/goo-player-info.c
@@ -22,13 +22,12 @@
#include <config.h>
#include <string.h>
-#include <gnome.h>
-#include <libgnomevfs/gnome-vfs-utils.h>
+#include <glib/gi18n.h>
#include "goo-player-info.h"
#include "goo-marshal.h"
-#include "glib-utils.h"
#include "goo-stock.h"
-#include "file-utils.h"
+#include "goo-window.h"
+#include "glib-utils.h"
#define SPACING 0
#define TITLE1_FORMAT "<span size='large' weight='bold'>%s</span>"
@@ -60,8 +59,8 @@ struct _GooPlayerInfoPrivateData {
GtkWidget *status_image;
GtkWidget *notebook;
GtkTooltips *tips;
- char current_time[64];
- char total_time[64];
+ char *current_time;
+ char *total_time;
char time[64];
gint64 track_length;
gboolean dragging;
@@ -216,7 +215,8 @@ time_scale_value_changed_cb (GtkRange *range,
new_value = gtk_range_get_value (range);
current_time = info->priv->track_length * (new_value / 100.0);
- set_time_string (info->priv->current_time, current_time);
+ g_free (info->priv->current_time);
+ info->priv->current_time = _g_format_duration_for_display (current_time * 1000);
sprintf (info->priv->time, _("%s / %s"), info->priv->total_time, info->priv->current_time);
set_time (info, info->priv->time);
@@ -243,7 +243,8 @@ update_time_label_cb (gpointer data)
}
current_time = priv->track_length * new_value;
- set_time_string (priv->current_time, current_time);
+ g_free (info->priv->current_time);
+ info->priv->current_time = _g_format_duration_for_display (current_time * 1000);
sprintf (priv->time, _("%s / %s"), priv->current_time, priv->total_time);
set_time (info, priv->time);
@@ -294,82 +295,6 @@ cover_button_clicked_cb (GtkWidget *button,
/* -- drag and drop -- */
-static char *
-get_path_from_url (char *url)
-{
- GnomeVFSURI *uri;
- char *escaped;
- char *path;
-
- if (url == NULL)
- return NULL;
-
- uri = gnome_vfs_uri_new (url);
- escaped = gnome_vfs_uri_to_string (uri, GNOME_VFS_URI_HIDE_TOPLEVEL_METHOD);
- path = gnome_vfs_unescape_string (escaped, NULL);
-
- gnome_vfs_uri_unref (uri);
- g_free (escaped);
-
- return path;
-}
-
-
-static GList *
-get_file_list_from_dnd_data (gchar *url_list)
-{
- GList *list = NULL;
- int i;
- char *url_start, *url_end;
- gboolean dnd_valid = FALSE;
-
- i = 0;
- url_start = url_list;
- while (url_list[i] != '\0') {
- char *url;
-
- while ((url_list[i] != '\0')
- && (url_list[i] != '\r')
- && (url_list[i] != '\n')) i++;
-
- url_end = url_list + i;
- if (strncmp (url_start, "file:", 5) == 0) {
- url_start += 5;
- if ((url_start[0] == '/')
- && (url_start[1] == '/')) url_start += 2;
-
- dnd_valid = TRUE;
-
- url = g_strndup (url_start, url_end - url_start);
- list = g_list_prepend (list, get_path_from_url (url));
- g_free (url);
-
- while ((url_list[i] != '\0')
- && ((url_list[i] == '\r')
- || (url_list[i] == '\n'))) i++;
- url_start = url_list + i;
-
- } else {
- while ((url_list[i] != '\0')
- && ((url_list[i] == '\r')
- || (url_list[i] == '\n'))) i++;
- url_start = url_list + i;
- if (url_list[i] == '\0' && !dnd_valid) {
- path_list_free (list);
- return NULL;
- }
- }
- }
-
- if (!dnd_valid) {
- path_list_free (list);
- return NULL;
- }
-
- return g_list_reverse (list);
-}
-
-
void
cover_button_drag_data_received (GtkWidget *widget,
GdkDragContext *context,
@@ -380,9 +305,8 @@ cover_button_drag_data_received (GtkWidget *widget,
guint time,
gpointer extra_data)
{
- GooPlayerInfo *info = extra_data;
- GList *list;
- char *cover_filename;
+ GooPlayerInfo *info = extra_data;
+ char **uris;
if (! ((data->length >= 0) && (data->format == 8))) {
gtk_drag_finish (context, FALSE, FALSE, time);
@@ -391,18 +315,20 @@ cover_button_drag_data_received (GtkWidget *widget,
gtk_drag_finish (context, TRUE, FALSE, time);
- list = get_file_list_from_dnd_data ((char *)data->data);
- if (list == NULL)
- return;
+ uris = gtk_selection_data_get_uris (data);
+ if (uris[0] != NULL) {
+ GFile *file;
+ char *cover_filename;
- cover_filename = list->data;
- if (cover_filename == NULL)
- return;
+ file = g_file_new_for_uri (uris[0]);
+ cover_filename = g_file_get_path (file);
+ goo_window_set_cover_image (info->priv->window, cover_filename);
- goo_window_set_cover_image (info->priv->window, cover_filename);
+ g_free (cover_filename);
+ g_object_unref (file);
+ }
- if (list != NULL)
- path_list_free (list);
+ g_strfreev (uris);
}
@@ -413,6 +339,9 @@ goo_player_info_init (GooPlayerInfo *info)
}
+static void goo_player_info_update_state (GooPlayerInfo *info);
+
+
static void
goo_player_info_construct (GooPlayerInfo *info)
{
@@ -422,8 +351,8 @@ goo_player_info_construct (GooPlayerInfo *info)
priv = info->priv;
priv->dragging = FALSE;
- priv->current_time[0] = '\0';
- priv->total_time[0] = '\0';
+ priv->current_time = NULL;
+ priv->total_time = NULL;
priv->update_id = 0;
GTK_WIDGET_UNSET_FLAGS (info, GTK_CAN_FOCUS);
@@ -578,6 +507,8 @@ goo_player_info_construct (GooPlayerInfo *info)
"button_release_event",
G_CALLBACK (time_scale_button_release_cb),
info);
+
+ goo_player_info_update_state (info);
}
@@ -591,6 +522,8 @@ goo_player_info_finalize (GObject *object)
info = GOO_PLAYER_INFO (object);
if (info->priv != NULL) {
+ g_free (info->priv->current_time);
+ g_free (info->priv->total_time);
if (info->priv->update_id != 0) {
g_source_remove (info->priv->update_id);
info->priv->update_id = 0;
@@ -612,7 +545,8 @@ goo_player_info_set_time (GooPlayerInfo *info,
if (priv->dragging)
return;
- set_time_string (priv->current_time, current_time);
+ g_free (priv->current_time);
+ priv->current_time = _g_format_duration_for_display (current_time * 1000);
sprintf (priv->time, _("%s / %s"), priv->current_time, priv->total_time);
set_time (info, priv->time);
@@ -662,7 +596,8 @@ update_subtitle (GooPlayerInfo *info,
album = goo_window_get_album (info->priv->window);
if ((album->title == NULL) || (album->artist == NULL)) {
- set_time_string (info->priv->total_time, track->length);
+ g_free (info->priv->total_time);
+ info->priv->total_time = _g_format_duration_for_display (track->length * 1000);
set_title2 (info, info->priv->total_time);
}
else {
@@ -675,7 +610,7 @@ update_subtitle (GooPlayerInfo *info,
static void
-goo_player_info_update_state (GooPlayerInfo *info)
+goo_player_info_update_state (GooPlayerInfo *info)
{
GooPlayerInfoPrivateData *priv = info->priv;
GooPlayerState state;
@@ -708,16 +643,17 @@ goo_player_info_update_state (GooPlayerInfo *info)
gtk_label_set_selectable (GTK_LABEL (priv->title2_label), FALSE);
gtk_label_set_selectable (GTK_LABEL (priv->title3_label), FALSE);
- if ((state == GOO_PLAYER_STATE_ERROR)
- || (state == GOO_PLAYER_STATE_NO_DISC)
- || (state == GOO_PLAYER_STATE_DATA_DISC)) {
- GError *error = goo_player_get_error (player);
-
- set_title1 (info, error->message);
+ if ((state == GOO_PLAYER_STATE_ERROR) || (state == GOO_PLAYER_STATE_NO_DISC))
+ {
+ set_title1 (info, _("No disc"));
set_title2 (info, "");
set_title3 (info, "");
- g_error_free (error);
}
+ else if (state == GOO_PLAYER_STATE_DATA_DISC) {
+ set_title1 (info, _("Data disc"));
+ set_title2 (info, "");
+ set_title3 (info, "");
+ }
else {
TrackInfo *track;
@@ -816,7 +752,8 @@ goo_player_info_set_total_time (GooPlayerInfo *info,
{
GooPlayerInfoPrivateData *priv = info->priv;
- set_time_string (priv->total_time, total_time);
+ g_free (priv->total_time);
+ priv->total_time = _g_format_duration_for_display (total_time * 1000);
goo_player_info_update_state (info);
}
diff --git a/src/goo-player-info.h b/src/goo-player-info.h
index 5983bfd..30ef316 100644
--- a/src/goo-player-info.h
+++ b/src/goo-player-info.h
@@ -23,8 +23,7 @@
#ifndef GOO_PLAYER_INFO_H
#define GOO_PLAYER_INFO_H
-#include <glib-object.h>
-#include <gtk/gtkhbox.h>
+#include <gtk/gtk.h>
#include "goo-window.h"
#define GOO_TYPE_PLAYER_INFO (goo_player_info_get_type ())
diff --git a/src/goo-player.c b/src/goo-player.c
index 3ec269c..130ed7d 100644
--- a/src/goo-player.c
+++ b/src/goo-player.c
@@ -23,18 +23,14 @@
#include <config.h>
#include <math.h>
#include <string.h>
-#include <gnome.h>
+#include <glib/gi18n.h>
#include <gst/gst.h>
-#include <libgnomevfs/gnome-vfs-ops.h>
-#include <libgnomevfs/gnome-vfs-utils.h>
#include <musicbrainz/queries.h>
#include <musicbrainz/mb_c.h>
-
#include "goo-player.h"
#include "goo-marshal.h"
-#include "goo-cdrom.h"
#include "glib-utils.h"
-#include "file-utils.h"
+#include "gth-user-dir.h"
#include "main.h"
#include "metadata.h"
@@ -45,19 +41,21 @@
#define PROGRESS_DELAY 400
#define QUEUE_SIZE 16384U /*131072U*/
-struct _GooPlayerPrivateData {
+struct _GooPlayerPrivate {
+ BraseroDrive *drive;
+ gulong medium_added_event;
+ gulong medium_removed_event;
+
GooPlayerState state;
GooPlayerAction action;
- GError *error;
double volume_value;
gboolean is_busy;
+ gboolean audio_cd;
- CDDrive *drive;
- GooCdrom *cdrom;
GstElement *pipeline;
GstElement *source;
GstPad *source_pad;
- GstElement *volume;
+ GstElement *audio_volume;
GstFormat track_format;
GstFormat sector_format;
@@ -91,60 +89,15 @@ static guint goo_player_signals[LAST_SIGNAL] = { 0 };
static void goo_player_finalize (GObject *object);
#define GOO_PLAYER_GET_PRIVATE_DATA(object) \
- (G_TYPE_INSTANCE_GET_PRIVATE ((object), GOO_TYPE_PLAYER, GooPlayerPrivateData))
+ (G_TYPE_INSTANCE_GET_PRIVATE ((object), GOO_TYPE_PLAYER, GooPlayerPrivate))
G_DEFINE_TYPE (GooPlayer, goo_player, G_TYPE_OBJECT)
static void
-remove_state_polling (GooPlayer *player)
-{
- if (player->priv->update_state_id == 0)
- return;
-
- debug (DEBUG_INFO, "REMOVE POLLING\n");
-
- g_source_remove (player->priv->update_state_id);
- player->priv->update_state_id = 0;
-}
-
-
-static gboolean
-update_state_cb (gpointer data)
-{
- GooPlayer *player = data;
-
- g_source_remove (player->priv->update_state_id);
- goo_cdrom_update_state (player->priv->cdrom);
- player->priv->update_state_id = g_timeout_add (POLL_TIMEOUT,
- update_state_cb,
- player);
-
- return FALSE;
-}
-
-
-static void
-add_state_polling (GooPlayer *player)
-{
- if (player->priv->update_state_id != 0)
- return;
-
- debug (DEBUG_INFO, "ADD POLLING\n");
-
- player->priv->update_state_id = g_timeout_add (POLL_TIMEOUT,
- update_state_cb,
- player);
-}
-
-
-static void
destroy_pipeline (GooPlayer *player,
- gboolean poll)
+ gboolean poll)
{
- if (player->priv->cdrom != NULL)
- goo_cdrom_unlock_tray (player->priv->cdrom);
-
if (player->priv->pipeline != NULL) {
gst_element_set_state (player->priv->pipeline, GST_STATE_NULL);
gst_object_unref (GST_OBJECT (player->priv->pipeline));
@@ -158,28 +111,22 @@ destroy_pipeline (GooPlayer *player,
player->priv->source = NULL;
player->priv->source_pad = NULL;
- player->priv->volume = NULL;
-
- if (poll)
- add_state_polling (player);
+ player->priv->audio_volume = NULL;
}
static void
-action_done (GooPlayer *player,
+action_done (GooPlayer *self,
GooPlayerAction action)
{
- g_signal_emit_by_name (G_OBJECT (player), "done", action, NULL);
+ g_signal_emit_by_name (G_OBJECT (self), "done", action, NULL);
}
static gboolean
-player_done_cb (gpointer callback_data)
+player_done_cb (gpointer user_data)
{
- GooPlayer *player = callback_data;
-
- action_done (player, GOO_PLAYER_ACTION_PLAY);
-
+ action_done ((GooPlayer *) user_data, GOO_PLAYER_ACTION_PLAY);
return FALSE;
}
@@ -187,17 +134,17 @@ player_done_cb (gpointer callback_data)
static gboolean
pipeline_bus_message_cb (GstBus *bus,
GstMessage *message,
- gpointer data)
+ gpointer user_data)
{
- GooPlayer *player = (GooPlayer *) data;
+ GooPlayer *self = user_data;
switch (GST_MESSAGE_TYPE (message)) {
case GST_MESSAGE_EOS:
- if (player->priv->update_progress_id != 0) {
- g_source_remove (player->priv->update_progress_id);
- player->priv->update_progress_id = 0;
+ if (self->priv->update_progress_id != 0) {
+ g_source_remove (self->priv->update_progress_id);
+ self->priv->update_progress_id = 0;
}
- g_idle_add (player_done_cb, data);
+ g_idle_add (player_done_cb, user_data);
break;
default:
@@ -209,58 +156,60 @@ pipeline_bus_message_cb (GstBus *bus,
static gboolean
-update_progress_cb (gpointer callback_data)
+update_progress_cb (gpointer user_data)
{
- GooPlayer *player = callback_data;
- gboolean ret;
+ GooPlayer *self = user_data;
gint64 sector = 0;
- if (player->priv->update_progress_id != 0) {
- g_source_remove (player->priv->update_progress_id);
- player->priv->update_progress_id = 0;
+ if (self->priv->update_progress_id != 0) {
+ g_source_remove (self->priv->update_progress_id);
+ self->priv->update_progress_id = 0;
}
- ret = gst_pad_query_position (player->priv->source_pad, &(player->priv->sector_format), §or);
- if (!ret)
+ if (! gst_pad_query_position (self->priv->source_pad,
+ &self->priv->sector_format,
+ §or))
+ {
return FALSE;
+ }
- g_signal_emit_by_name (G_OBJECT (player),
+ g_signal_emit_by_name (G_OBJECT (self),
"progress",
- ((double) sector) / (double) player->priv->current_track->sectors,
+ ((double) sector) / (double) self->priv->current_track->sectors,
NULL);
- player->priv->update_progress_id = g_timeout_add (PROGRESS_DELAY, update_progress_cb, callback_data);
+ self->priv->update_progress_id = g_timeout_add (PROGRESS_DELAY, update_progress_cb, user_data);
return FALSE;
}
static void
-create_pipeline (GooPlayer *player)
+create_pipeline (GooPlayer *self)
{
- const char *device;
GstElement *audioconvert;
GstElement *audioresample;
GstElement *queue;
GstElement *sink;
- destroy_pipeline (player, FALSE);
- remove_state_polling (player);
- goo_cdrom_lock_tray (player->priv->cdrom);
+ destroy_pipeline (self, FALSE);
- player->priv->pipeline = gst_pipeline_new ("pipeline");
+ /*remove_state_polling (self);
+ goo_cdrom_lock_tray (self->priv->cdrom); FIXME */
- /*player->priv->source = gst_element_make_from_uri (GST_URI_SRC, "cdda://1", "source");*/
- player->priv->source = gst_element_factory_make ("cdparanoiasrc", "source");
- device = goo_cdrom_get_device (player->priv->cdrom);
- debug (DEBUG_INFO, "DEVICE: %s\n", device);
- g_object_set (G_OBJECT (player->priv->source),
- "device", device,
+ self->priv->pipeline = gst_pipeline_new ("pipeline");
+
+ /*self->priv->source = gst_element_make_from_uri (GST_URI_SRC, "cdda://1", "source");*/
+ self->priv->source = gst_element_factory_make ("cdparanoiasrc", "source");
+
+ debug (DEBUG_INFO, "DEVICE: %s\n", brasero_drive_get_device (self->priv->drive));
+ g_object_set (G_OBJECT (self->priv->source),
+ "device", brasero_drive_get_device (self->priv->drive),
NULL);
audioconvert = gst_element_factory_make ("audioconvert", "convert");
audioresample = gst_element_factory_make ("audioresample", "resample");
- player->priv->volume = gst_element_factory_make ("volume", "volume");
+ self->priv->audio_volume = gst_element_factory_make ("volume", "volume");
queue = gst_element_factory_make ("queue", "queue");
g_object_set (queue,
@@ -270,20 +219,29 @@ create_pipeline (GooPlayer *player)
sink = gst_element_factory_make ("gconfaudiosink", "sink");
- gst_bin_add_many (GST_BIN (player->priv->pipeline),
- player->priv->source, queue, audioconvert,
- audioresample, player->priv->volume, sink, NULL);
- gst_element_link_many (player->priv->source, queue,
- audioconvert, audioresample,
- player->priv->volume, sink, NULL);
-
- player->priv->track_format = gst_format_get_by_nick ("track");
- player->priv->sector_format = gst_format_get_by_nick ("sector");
- player->priv->source_pad = gst_element_get_pad (player->priv->source, "src");
+ gst_bin_add_many (GST_BIN (self->priv->pipeline),
+ self->priv->source,
+ queue,
+ audioconvert,
+ audioresample,
+ self->priv->audio_volume,
+ sink,
+ NULL);
+ gst_element_link_many (self->priv->source,
+ queue,
+ audioconvert,
+ audioresample,
+ self->priv->audio_volume,
+ sink,
+ NULL);
+
+ self->priv->track_format = gst_format_get_by_nick ("track");
+ self->priv->sector_format = gst_format_get_by_nick ("sector");
+ self->priv->source_pad = gst_element_get_pad (self->priv->source, "src");
- gst_bus_add_watch (gst_pipeline_get_bus (GST_PIPELINE (player->priv->pipeline)),
+ gst_bus_add_watch (gst_pipeline_get_bus (GST_PIPELINE (self->priv->pipeline)),
pipeline_bus_message_cb,
- player);
+ self);
}
@@ -292,20 +250,25 @@ goo_player_empty_list (GooPlayer *player)
{
album_info_unref (player->priv->album);
player->priv->album = album_info_new ();
-
player->priv->current_track = NULL;
player->priv->current_track_n = -1;
}
static void
-goo_player_set_state (GooPlayer *player,
+goo_player_set_state (GooPlayer *self,
GooPlayerState state,
gboolean notify)
{
- player->priv->state = state;
+ /* FIXME
+ if (state == GOO_PLAYER_STATE_PLAYING)
+ brasero_drive_lock (self->priv->drive, _("Playing CD"), NULL);
+ else
+ brasero_drive_unlock (self->priv->drive);*/
+
+ self->priv->state = state;
if (notify)
- g_signal_emit (G_OBJECT (player),
+ g_signal_emit (G_OBJECT (self),
goo_player_signals[STATE_CHANGED],
0,
NULL);
@@ -315,7 +278,12 @@ goo_player_set_state (GooPlayer *player,
static void
goo_player_class_init (GooPlayerClass *class)
{
- GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+ GObjectClass *gobject_class;
+
+ gobject_class = G_OBJECT_CLASS (class);
+ gobject_class->finalize = goo_player_finalize;
+
+ g_type_class_add_private (class, sizeof (GooPlayerPrivate));
goo_player_signals[START] =
g_signal_new ("start",
@@ -323,7 +291,7 @@ goo_player_class_init (GooPlayerClass *class)
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GooPlayerClass, start),
NULL, NULL,
- goo_marshal_VOID__INT,
+ g_cclosure_marshal_VOID__INT,
G_TYPE_NONE,
1,
G_TYPE_INT);
@@ -343,7 +311,7 @@ goo_player_class_init (GooPlayerClass *class)
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GooPlayerClass, progress),
NULL, NULL,
- goo_marshal_VOID__DOUBLE,
+ g_cclosure_marshal_VOID__DOUBLE,
G_TYPE_NONE, 1,
G_TYPE_DOUBLE);
goo_player_signals[MESSAGE] =
@@ -352,7 +320,7 @@ goo_player_class_init (GooPlayerClass *class)
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GooPlayerClass, message),
NULL, NULL,
- goo_marshal_VOID__STRING,
+ g_cclosure_marshal_VOID__STRING,
G_TYPE_NONE, 1,
G_TYPE_STRING);
goo_player_signals[STATE_CHANGED] =
@@ -361,246 +329,122 @@ goo_player_class_init (GooPlayerClass *class)
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GooPlayerClass, state_changed),
NULL, NULL,
- goo_marshal_VOID__VOID,
+ g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
-
- gobject_class->finalize = goo_player_finalize;
-
- g_type_class_add_private (class, sizeof (GooPlayerPrivateData));
}
static void
-goo_player_init (GooPlayer *player)
+goo_player_init (GooPlayer *self)
{
- GooPlayerPrivateData *priv;
-
- player->priv = GOO_PLAYER_GET_PRIVATE_DATA (player);
- priv = player->priv;
-
- priv->state = GOO_PLAYER_STATE_STOPPED;
- priv->action = GOO_PLAYER_ACTION_NONE;
- priv->error = NULL;
- priv->is_busy = FALSE;
-
- priv->yes_or_no = g_mutex_new ();
- priv->check_id = 0;
- priv->exiting = FALSE,
-
- priv->discid = NULL;
- priv->album = album_info_new ();
- priv->current_track_n = -1;
- priv->volume_value = 1.0;
-
- priv->update_progress_id = 0;
+ self->priv = GOO_PLAYER_GET_PRIVATE_DATA (self);
+ self->priv->state = GOO_PLAYER_STATE_LISTING;
+ self->priv->action = GOO_PLAYER_ACTION_NONE;
+ self->priv->is_busy = FALSE;
+ self->priv->yes_or_no = g_mutex_new ();
+ self->priv->check_id = 0;
+ self->priv->exiting = FALSE,
+ self->priv->discid = NULL;
+ self->priv->album = album_info_new ();
+ self->priv->current_track_n = -1;
+ self->priv->volume_value = 1.0;
+ self->priv->update_progress_id = 0;
}
static void
goo_player_finalize (GObject *object)
{
- GooPlayer *player;
+ GooPlayer *self;
g_return_if_fail (object != NULL);
g_return_if_fail (GOO_IS_PLAYER (object));
- player = GOO_PLAYER (object);
+ self = GOO_PLAYER (object);
- g_mutex_lock (player->priv->yes_or_no);
- player->priv->exiting = TRUE;
- g_mutex_unlock (player->priv->yes_or_no);
-
- if (player->priv != NULL) {
- GooPlayerPrivateData *priv = player->priv;
-
- if (priv->check_id != 0) {
- g_source_remove (priv->check_id);
- priv->check_id = 0;
- }
-
- destroy_pipeline (player, FALSE);
+ g_mutex_lock (self->priv->yes_or_no);
+ self->priv->exiting = TRUE;
+ g_mutex_unlock (self->priv->yes_or_no);
- g_mutex_free (priv->yes_or_no);
+ if (self->priv->medium_added_event != 0)
+ g_signal_handler_disconnect (self->priv->drive, self->priv->medium_added_event);
+ if (self->priv->medium_removed_event != 0)
+ g_signal_handler_disconnect (self->priv->drive, self->priv->medium_removed_event);
+ g_object_unref (self->priv->drive);
- remove_state_polling (player);
- destroy_pipeline (player, FALSE);
+ if (self->priv->check_id != 0)
+ g_source_remove (self->priv->check_id);
- g_free (priv->discid);
- album_info_unref (priv->album);
-
- player->priv = NULL;
- }
+ destroy_pipeline (self, FALSE);
+ g_mutex_free (self->priv->yes_or_no);
+ destroy_pipeline (self, FALSE);
+ g_free (self->priv->discid);
+ album_info_unref (self->priv->album);
G_OBJECT_CLASS (goo_player_parent_class)->finalize (object);
}
static void
-goo_player_set_error (GooPlayer *player,
- GError *error)
+drive_medium_added_cb (BraseroDrive *drive,
+ BraseroMedium *medium,
+ gpointer user_data)
{
- if (player->priv->error != NULL)
- g_error_free (player->priv->error);
- player->priv->error = error;
+ goo_player_update ((GooPlayer *) user_data);
}
static void
-cdrom_state_changed_cb (GooCdrom *cdrom,
- GooPlayer *player)
+drive_medium_removed_cb (BraseroDrive *drive,
+ BraseroMedium *medium,
+ gpointer user_data)
{
- GooCdromState cdrom_state;
- char *state;
- char *message = "";
-
- if (goo_player_get_device (player) == NULL)
- return;
-
- cdrom_state = goo_cdrom_get_state (cdrom);
-
- switch (cdrom_state) {
- case GOO_CDROM_STATE_ERROR:
- state = "ERROR";
- break;
- case GOO_CDROM_STATE_UNKNOWN:
- state = "UNKNOWN";
- break;
- case GOO_CDROM_STATE_DRIVE_NOT_READY:
- state = "DRIVE_NOT_READY";
- message = _("Drive not ready");
- break;
- case GOO_CDROM_STATE_TRAY_OPEN:
- state = "TRAY_OPEN";
- message = _("Tray open");
- message = _("No disc");
- break;
- case GOO_CDROM_STATE_NO_DISC:
- state = "NO_DISC";
- message = _("No disc");
- break;
- case GOO_CDROM_STATE_DATA_CD:
- state = "DATA_CD";
- message = _("Data Disc");
- break;
- case GOO_CDROM_STATE_OK:
- state = "OK";
- break;
- default:
- break;
- }
-
- debug (DEBUG_INFO, "STATE CHANGED [%s]\n", state);
-
- if (cdrom_state == GOO_CDROM_STATE_UNKNOWN)
- return;
-
- else if (cdrom_state == GOO_CDROM_STATE_OK) {
- goo_player_set_state (player, GOO_PLAYER_STATE_STOPPED, TRUE);
- goo_player_list (player);
- }
- else if (cdrom_state == GOO_CDROM_STATE_ERROR) {
- GError *error = goo_cdrom_get_error (player->priv->cdrom);
-
- goo_player_set_error (player, error);
- goo_player_set_state (player, GOO_PLAYER_STATE_ERROR, TRUE);
- goo_player_empty_list (player);
- action_done (player, GOO_PLAYER_ACTION_LIST);
- }
- else if (cdrom_state == GOO_CDROM_STATE_NO_DISC) {
- goo_player_set_error (player, g_error_new (GOO_CDROM_ERROR, 0, "%s", message));
- goo_player_set_state (player, GOO_PLAYER_STATE_NO_DISC, TRUE);
- goo_player_empty_list (player);
- action_done (player, GOO_PLAYER_ACTION_LIST);
- }
- else if (cdrom_state == GOO_CDROM_STATE_DATA_CD) {
- goo_player_set_error (player, g_error_new (GOO_CDROM_ERROR, 0, "%s", message));
- goo_player_set_state (player, GOO_PLAYER_STATE_DATA_DISC, TRUE);
- goo_player_empty_list (player);
- action_done (player, GOO_PLAYER_ACTION_LIST);
- }
- else {
- goo_player_set_error (player, g_error_new (GOO_CDROM_ERROR, 0, "%s", message));
- goo_player_set_state (player, GOO_PLAYER_STATE_ERROR, TRUE);
- goo_player_empty_list (player);
- action_done (player, GOO_PLAYER_ACTION_LIST);
- if (cdrom_state == GOO_CDROM_STATE_TRAY_OPEN)
- add_state_polling (GOO_PLAYER (player));
- }
-}
-
+ GooPlayer *self = user_data;
-static void
-create_cdrom (GooPlayer *player)
-{
- if (player->priv->cdrom != NULL)
- return;
-
- player->priv->cdrom = goo_cdrom_new (player->priv->drive->device);
- g_signal_connect (player->priv->cdrom,
- "state_changed",
- G_CALLBACK (cdrom_state_changed_cb),
- GOO_PLAYER (player));
+ goo_player_update (self);
}
GooPlayer *
-goo_player_new (const char *device)
-{
- GooPlayer *player;
-
- player = GOO_PLAYER (g_object_new (GOO_TYPE_PLAYER, NULL));
-
- player->priv->drive = get_drive_from_device (device);
- if (player->priv->drive != NULL)
- create_cdrom (player);
-
- return player;
-}
-
-
-CDDrive *
-goo_player_get_drive (GooPlayer *player)
+goo_player_new (BraseroDrive *drive)
{
- return player->priv->drive;
+ GooPlayer *self;
+
+ g_return_val_if_fail (drive != NULL, NULL);
+
+ self = GOO_PLAYER (g_object_new (GOO_TYPE_PLAYER, NULL));
+ self->priv->drive = g_object_ref (drive);
+ self->priv->medium_added_event =
+ g_signal_connect (self->priv->drive,
+ "medium-added",
+ G_CALLBACK (drive_medium_added_cb),
+ self);
+ self->priv->medium_removed_event =
+ g_signal_connect (self->priv->drive,
+ "medium-removed",
+ G_CALLBACK (drive_medium_removed_cb),
+ self);
+
+ return self;
}
static void
-notify_action_start (GooPlayer *player)
+notify_action_start (GooPlayer *self)
{
- g_signal_emit (G_OBJECT (player),
+ g_signal_emit (G_OBJECT (self),
goo_player_signals[START],
0,
- player->priv->action,
+ self->priv->action,
NULL);
}
-void
-goo_player_update (GooPlayer *player)
-{
- if (player->priv->cdrom == NULL) {
- goo_player_set_error (player, g_error_new (GOO_CDROM_ERROR, 0, "%s", _("Invalid device")));
- goo_player_set_state (player, GOO_PLAYER_STATE_NO_DISC, TRUE);
- goo_player_empty_list (player);
- action_done (player, GOO_PLAYER_ACTION_LIST);
- return;
- }
-
- player->priv->action = GOO_PLAYER_ACTION_UPDATE;
- player->priv->state = GOO_PLAYER_STATE_UPDATING;
- notify_action_start (player);
-
- goo_cdrom_set_state (player->priv->cdrom, GOO_CDROM_STATE_UNKNOWN);
- goo_cdrom_update_state (player->priv->cdrom);
-}
-
-
static void
-goo_player_set_is_busy (GooPlayer *player,
+goo_player_set_is_busy (GooPlayer *self,
gboolean is_busy)
{
- player->priv->is_busy = is_busy;
+ self->priv->is_busy = is_busy;
}
@@ -608,19 +452,19 @@ goo_player_set_is_busy (GooPlayer *player,
void
-goo_player_set_album (GooPlayer *player,
+goo_player_set_album (GooPlayer *self,
AlbumInfo *album)
{
- if (player->priv->album == NULL)
+ if (self->priv->album == NULL)
return;
- album_info_copy_metadata (player->priv->album, album);
- album_info_save_to_cache (player->priv->album, player->priv->discid);
- action_done (player, GOO_PLAYER_ACTION_METADATA);
+ album_info_copy_metadata (self->priv->album, album);
+ album_info_save_to_cache (self->priv->album, self->priv->discid);
+ action_done (self, GOO_PLAYER_ACTION_METADATA);
}
static void
-set_cd_metadata_from_rdf (GooPlayer *player,
+set_cd_metadata_from_rdf (GooPlayer *self,
char *rdf)
{
musicbrainz_t mb;
@@ -640,7 +484,7 @@ set_cd_metadata_from_rdf (GooPlayer *player,
/* FIXME: ask the user which album to use if the query
* returned more than one album. */
- goo_player_set_album (player, first_album);
+ goo_player_set_album (self, first_album);
album_list_free (albums);
}
@@ -649,20 +493,14 @@ set_cd_metadata_from_rdf (GooPlayer *player,
static char *
-get_cached_rdf_path (GooPlayer *player)
+get_cached_rdf_path (GooPlayer *self)
{
- char *path = NULL;
- char *dir;
-
- if (player->priv->discid == NULL)
+ if (self->priv->discid != NULL) {
+ gth_user_dir_make_dir_for_file (GTH_DIR_CACHE, "goobox", self->priv->discid, NULL);
+ return gth_user_dir_get_file (GTH_DIR_CACHE, "goobox", self->priv->discid, NULL);
+ }
+ else
return NULL;
-
- dir = g_build_filename (g_get_home_dir (), ".gnome2", "goobox.d", "cache", NULL);
- if (ensure_dir_exists (dir, 0700) == GNOME_VFS_OK)
- path = g_build_filename (dir, player->priv->discid, NULL);
- g_free (dir);
-
- return path;
}
@@ -684,7 +522,7 @@ save_rdf_to_cache (GooPlayer *player,
g_free (path);
return;
}
-
+
if (! g_file_set_contents (path, rdf, strlen (rdf), &error)) {
debug (DEBUG_INFO, "%s\n", error->message);
g_clear_error (&error);
@@ -695,13 +533,13 @@ save_rdf_to_cache (GooPlayer *player,
static char *
-read_cached_rdf (GooPlayer *player)
+read_cached_rdf (GooPlayer *self)
{
char *path;
char *rdf = NULL;
GError *error = NULL;
- path = get_cached_rdf_path (player);
+ path = get_cached_rdf_path (self);
if (path == NULL)
return NULL;
@@ -742,7 +580,7 @@ check_get_cd_metadata (gpointer data)
return FALSE;
}
- if (!done) {
+ if (! done) {
player->priv->check_id = g_timeout_add (REFRESH_RATE,
check_get_cd_metadata,
player);
@@ -823,7 +661,7 @@ check_get_cd_tracks (gpointer data)
return FALSE;
}
- if (!done) {
+ if (! done) {
player->priv->check_id = g_timeout_add (REFRESH_RATE,
check_get_cd_tracks,
player);
@@ -878,7 +716,7 @@ get_cd_tracks (void *thread_data)
mb = mb_New ();
mb_UseUTF8 (mb, TRUE);
- mb_SetDevice (mb, (char*) goo_player_get_device (player));
+ mb_SetDevice (mb, (char *) goo_player_get_device (player));
if (mb_Query (mb, MBQ_GetCDTOC)) {
char data[256];
int n_tracks, i;
@@ -920,25 +758,52 @@ get_cd_tracks (void *thread_data)
}
+gboolean
+goo_player_is_audio_cd (GooPlayer *self)
+{
+ return self->priv->audio_cd;
+}
+
+
+void
+goo_player_update (GooPlayer *self)
+{
+ BraseroMedium *medium;
+
+ self->priv->audio_cd = FALSE;
+
+ medium = brasero_drive_get_medium (self->priv->drive);
+ if (medium == NULL) {
+ goo_player_stop (self);
+ goo_player_set_state (self, GOO_PLAYER_STATE_NO_DISC, TRUE);
+ goo_player_empty_list (self);
+ action_done (self, GOO_PLAYER_ACTION_LIST);
+ }
+ else if ((BRASERO_MEDIUM_IS (brasero_medium_get_status (medium), BRASERO_MEDIUM_CD | BRASERO_MEDIUM_HAS_AUDIO))) {
+ self->priv->audio_cd = TRUE;
+ goo_player_set_state (self, GOO_PLAYER_STATE_STOPPED, TRUE);
+ goo_player_list (self);
+ }
+ else {
+ goo_player_stop (self);
+ goo_player_set_state (self, GOO_PLAYER_STATE_DATA_DISC, TRUE);
+ goo_player_empty_list (self);
+ action_done (self, GOO_PLAYER_ACTION_LIST);
+ }
+}
+
+
void
goo_player_list (GooPlayer *player)
{
if (goo_player_get_is_busy (player))
return;
-
+
player->priv->action = GOO_PLAYER_ACTION_LIST;
player->priv->state = GOO_PLAYER_STATE_LISTING;
notify_action_start (player);
goo_player_empty_list (player);
-
- if (goo_cdrom_get_state (player->priv->cdrom) != GOO_CDROM_STATE_OK) {
- debug (DEBUG_INFO, "NOT OK\n");
- goo_player_set_state (player, GOO_PLAYER_STATE_ERROR, TRUE);
- action_done (player, GOO_PLAYER_ACTION_LIST);
- return;
- }
-
goo_player_set_is_busy (player, TRUE);
create_pipeline (player);
@@ -996,7 +861,7 @@ goo_player_seek_track (GooPlayer *player,
track_n = CLAMP (n, 0, player->priv->album->n_tracks - 1);
player->priv->current_track_n = track_n;
- debug (DEBUG_INFO, "seek to track %d\n", track_n); /* FIXME */
+ debug (DEBUG_INFO, "seek to track %d\n", track_n);
player->priv->current_track = get_track (player, player->priv->current_track_n);
@@ -1010,7 +875,9 @@ goo_player_seek_track (GooPlayer *player,
track_n,
GST_SEEK_TYPE_SET,
track_n + 1))
+ {
g_warning ("seek failed");
+ }
gst_element_get_state (player->priv->pipeline, NULL, NULL, -1);
action_done (player, GOO_PLAYER_ACTION_SEEK_SONG);
@@ -1053,6 +920,7 @@ gboolean
goo_player_set_device (GooPlayer *player,
const char *device)
{
+ /* FIXME
if (goo_player_get_is_busy (player))
return TRUE;
@@ -1062,24 +930,31 @@ goo_player_set_device (GooPlayer *player,
remove_state_polling (player);
player->priv->drive = get_drive_from_device (device);
- if (player->priv->drive == NULL)
- goo_player_set_state (player, GOO_PLAYER_STATE_ERROR, FALSE);
- else {
+ if (player->priv->drive != NULL) {
create_cdrom (player);
- goo_cdrom_set_device (player->priv->cdrom, player->priv->drive->device);
+ goo_cdrom_set_device (player->priv->cdrom, player->priv->device);
}
+ else
+ goo_player_set_state (player, GOO_PLAYER_STATE_ERROR, FALSE);
return TRUE;
+ */
+
+ return TRUE;
+}
+
+
+BraseroDrive *
+goo_player_get_drive (GooPlayer *self)
+{
+ return self->priv->drive;
}
const char *
-goo_player_get_device (GooPlayer *player)
+goo_player_get_device (GooPlayer *self)
{
- if (player->priv->drive == NULL)
- return NULL;
- else
- return player->priv->drive->device;
+ return brasero_drive_get_device (self->priv->drive);
}
@@ -1102,15 +977,16 @@ goo_player_play (GooPlayer *player)
if (! ((player->priv->pipeline != NULL)
&& ((goo_player_get_state (player) == GOO_PLAYER_STATE_PAUSED)
|| (goo_player_get_state (player) == GOO_PLAYER_STATE_SEEKING))))
+ {
create_pipeline (player);
+ }
- g_object_set (G_OBJECT (player->priv->volume), "volume", goo_player_get_volume (player), NULL);
+ g_object_set (G_OBJECT (player->priv->audio_volume), "volume", goo_player_get_audio_volume (player), NULL);
gst_element_set_state (player->priv->pipeline, GST_STATE_PLAYING);
goo_player_set_state (player, GOO_PLAYER_STATE_PLAYING, TRUE);
player->priv->update_progress_id = g_timeout_add (PROGRESS_DELAY, update_progress_cb, player);
-
}
@@ -1148,40 +1024,43 @@ goo_player_stop (GooPlayer *player)
}
-gboolean
-goo_player_eject (GooPlayer *player)
+
+static void
+eject_ready_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
{
- GooCdromState cdrom_state;
- gboolean result;
+ GooPlayer *self = user_data;
+ GError *error = NULL;
- if (goo_player_get_is_busy (player))
- return FALSE;
+ if (! g_drive_eject_with_operation_finish (G_DRIVE (source_object), result, &error))
+ g_signal_emit_by_name (G_OBJECT (self), "done", GOO_PLAYER_ACTION_EJECT, error);
+ else
+ g_signal_emit_by_name (G_OBJECT (self), "done", GOO_PLAYER_ACTION_EJECT, NULL);
- player->priv->action = GOO_PLAYER_ACTION_EJECT;
- player->priv->state = GOO_PLAYER_STATE_EJECTING;
- notify_action_start (player);
+ goo_player_set_state (self, GOO_PLAYER_STATE_STOPPED, TRUE);
+}
- destroy_pipeline (player, TRUE);
- cdrom_state = goo_cdrom_get_state (player->priv->cdrom);
- result = goo_cdrom_eject (player->priv->cdrom);
+void
+goo_player_eject (GooPlayer *self)
+{
+ GDrive *gdrive;
- if (!result)
- goo_player_set_error (player, goo_cdrom_get_error (player->priv->cdrom));
+ if (self->priv->drive == NULL)
+ return;
- action_done (player, GOO_PLAYER_ACTION_EJECT);
-
- return result;
-}
+ g_signal_emit_by_name (G_OBJECT (self), "start", GOO_PLAYER_ACTION_EJECT);
+ gdrive = brasero_drive_get_gdrive (self->priv->drive);
+ g_drive_eject_with_operation (gdrive,
+ G_MOUNT_UNMOUNT_NONE,
+ NULL,
+ NULL,
+ eject_ready_cb,
+ self);
-GError *
-goo_player_get_error (GooPlayer *player)
-{
- if (player->priv->error != NULL)
- return g_error_copy (player->priv->error);
- else
- return NULL;
+ g_object_unref (gdrive);
}
@@ -1200,22 +1079,22 @@ goo_player_get_state (GooPlayer *player)
double
-goo_player_get_volume (GooPlayer *player)
+goo_player_get_audio_volume (GooPlayer *player)
{
return player->priv->volume_value;
}
void
-goo_player_set_volume (GooPlayer *player,
- double vol)
+goo_player_set_audio_volume (GooPlayer *player,
+ double vol)
{
if (goo_player_get_is_busy (player))
return;
player->priv->volume_value = vol;
- if (player->priv->volume != NULL)
- g_object_set (G_OBJECT (player->priv->volume), "volume", vol, NULL);
+ if (player->priv->audio_volume != NULL)
+ g_object_set (G_OBJECT (player->priv->audio_volume), "volume", vol, NULL);
}
diff --git a/src/goo-player.h b/src/goo-player.h
index f337e23..f6dff79 100644
--- a/src/goo-player.h
+++ b/src/goo-player.h
@@ -3,7 +3,7 @@
/*
* Goo
*
- * Copyright (C) 2004 Free Software Foundation, Inc.
+ * Copyright (C) 2004-2009 Free Software Foundation, Inc.
*
* 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
@@ -24,7 +24,8 @@
#define GOO_PLAYER_H
#include <glib.h>
-#include "cd-drive.h"
+#include <gio/gio.h>
+#include <brasero/brasero-drive.h>
#include "track-info.h"
#include "album-info.h"
@@ -35,9 +36,9 @@
#define GOO_IS_PLAYER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GOO_TYPE_PLAYER))
#define GOO_PLAYER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GOO_TYPE_PLAYER, GooPlayerClass))
-typedef struct _GooPlayer GooPlayer;
-typedef struct _GooPlayerClass GooPlayerClass;
-typedef struct _GooPlayerPrivateData GooPlayerPrivateData;
+typedef struct _GooPlayer GooPlayer;
+typedef struct _GooPlayerClass GooPlayerClass;
+typedef struct _GooPlayerPrivate GooPlayerPrivate;
typedef enum {
GOO_PLAYER_ACTION_NONE,
@@ -68,7 +69,7 @@ typedef enum {
struct _GooPlayer
{
GObject __parent;
- GooPlayerPrivateData *priv;
+ GooPlayerPrivate *priv;
};
struct _GooPlayerClass
@@ -90,8 +91,10 @@ struct _GooPlayerClass
};
GType goo_player_get_type (void);
-GooPlayer * goo_player_new (const char *device);
-CDDrive * goo_player_get_drive (GooPlayer *player);
+GooPlayer * goo_player_new (BraseroDrive *drive);
+BraseroDrive * goo_player_get_drive (GooPlayer *player);
+const char * goo_player_get_device (GooPlayer *player);
+gboolean goo_player_is_audio_cd (GooPlayer *player);
void goo_player_update (GooPlayer *player);
void goo_player_list (GooPlayer *player);
void goo_player_seek_track (GooPlayer *player,
@@ -102,20 +105,18 @@ void goo_player_skip_to (GooPlayer *player,
void goo_player_play (GooPlayer *player);
void goo_player_pause (GooPlayer *player);
void goo_player_stop (GooPlayer *player);
-gboolean goo_player_eject (GooPlayer *player);
-GError * goo_player_get_error (GooPlayer *player);
+void goo_player_eject (GooPlayer *player);
GooPlayerAction goo_player_get_action (GooPlayer *player);
GooPlayerState goo_player_get_state (GooPlayer *player);
gboolean goo_player_set_device (GooPlayer *player,
const char *device);
-const char * goo_player_get_device (GooPlayer *player);
const char * goo_player_get_discid (GooPlayer *player);
void goo_player_set_album (GooPlayer *player,
AlbumInfo *album);
AlbumInfo * goo_player_get_album (GooPlayer *player);
-void goo_player_set_volume (GooPlayer *player,
+void goo_player_set_audio_volume (GooPlayer *player,
double vol);
-double goo_player_get_volume (GooPlayer *player);
+double goo_player_get_audio_volume (GooPlayer *player);
gboolean goo_player_get_is_busy (GooPlayer *player);
#endif /* GOO_PLAYER_H */
diff --git a/src/goo-stock.c b/src/goo-stock.c
index 9a5f599..9f45906 100644
--- a/src/goo-stock.c
+++ b/src/goo-stock.c
@@ -21,8 +21,8 @@
*/
#include <config.h>
+#include <glib/gi18n.h>
#include <gtk/gtk.h>
-#include <gnome.h>
#include "goo-stock.h"
#include "icons/pixbufs.h"
diff --git a/src/goo-volume-tool-button.c b/src/goo-volume-tool-button.c
index e8d2332..2341fd4 100644
--- a/src/goo-volume-tool-button.c
+++ b/src/goo-volume-tool-button.c
@@ -39,16 +39,14 @@
#include <config.h>
#include <math.h>
-#include <gnome.h>
#include <gdk/gdkkeysyms.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
#include "glib-utils.h"
#include "goo-volume-tool-button.h"
#include "goo-marshal.h"
#include "goo-stock.h"
-#define GOO_VOLUME_TOOL_BUTTON_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), GOO_TYPE_VOLUME_TOOL_BUTTON, GooVolumeToolButtonPrivate))
-#define FLOAT_EQUAL(a,b) (fabs ((a) - (b)) < 1e-6)
-
#define SCALE_WIDTH 25
#define SCALE_HEIGHT 100
#define DEFAULT_VOLUME 1.0
@@ -88,8 +86,10 @@ enum {
static guint goo_volume_tool_button_signals[LAST_SIGNAL] = { 0 };
+
G_DEFINE_TYPE (GooVolumeToolButton, goo_volume_tool_button, GTK_TYPE_TOOL_BUTTON)
+
static gboolean
goo_volume_tool_button_set_tooltip (GtkToolItem *tool_item,
GtkTooltips *tooltips,
@@ -110,51 +110,48 @@ goo_volume_tool_button_set_tooltip (GtkToolItem *tool_item,
static void
goo_volume_tool_button_construct_contents (GooVolumeToolButton *button)
{
- GooVolumeToolButtonPrivate *priv;
- GtkWidget *box;
- GtkOrientation orientation;
-
- priv = GOO_VOLUME_TOOL_BUTTON_GET_PRIVATE (button);
+ GtkWidget *box;
+ GtkOrientation orientation;
orientation = gtk_tool_item_get_orientation (GTK_TOOL_ITEM (button));
if (orientation == GTK_ORIENTATION_HORIZONTAL) {
box = gtk_hbox_new (FALSE, 0);
- gtk_arrow_set (GTK_ARROW (priv->arrow), GTK_ARROW_DOWN, GTK_SHADOW_NONE);
+ gtk_arrow_set (GTK_ARROW (button->priv->arrow), GTK_ARROW_DOWN, GTK_SHADOW_NONE);
}
else {
box = gtk_vbox_new (FALSE, 0);
- gtk_arrow_set (GTK_ARROW (priv->arrow), GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
+ gtk_arrow_set (GTK_ARROW (button->priv->arrow), GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
}
- if (priv->button && priv->button->parent) {
- g_object_ref (priv->button);
- gtk_container_remove (GTK_CONTAINER (priv->button->parent),
- priv->button);
- gtk_container_add (GTK_CONTAINER (box), priv->button);
- g_object_unref (priv->button);
+ if (button->priv->button && button->priv->button->parent) {
+ g_object_ref (button->priv->button);
+ gtk_container_remove (GTK_CONTAINER (button->priv->button->parent),
+ button->priv->button);
+ gtk_container_add (GTK_CONTAINER (box), button->priv->button);
+ g_object_unref (button->priv->button);
}
- if (priv->arrow_button && priv->arrow_button->parent) {
- g_object_ref (priv->arrow_button);
- gtk_container_remove (GTK_CONTAINER (priv->arrow_button->parent),
- priv->arrow_button);
- gtk_box_pack_end (GTK_BOX (box), priv->arrow_button,
+ if (button->priv->arrow_button && button->priv->arrow_button->parent) {
+ g_object_ref (button->priv->arrow_button);
+ gtk_container_remove (GTK_CONTAINER (button->priv->arrow_button->parent),
+ button->priv->arrow_button);
+ gtk_box_pack_end (GTK_BOX (box), button->priv->arrow_button,
FALSE, FALSE, 0);
- g_object_unref (priv->arrow_button);
+ g_object_unref (button->priv->arrow_button);
}
- if (priv->box) {
+ if (button->priv->box) {
/* Note: we are not destroying the button and the arrow_button
* here because they were removed from their container above
*/
- gtk_widget_destroy (priv->box);
+ gtk_widget_destroy (button->priv->box);
}
- priv->box = box;
+ button->priv->box = box;
- gtk_container_add (GTK_CONTAINER (button), priv->box);
- gtk_widget_show_all (priv->box);
+ gtk_container_add (GTK_CONTAINER (button), button->priv->box);
+ gtk_widget_show_all (button->priv->box);
gtk_widget_queue_resize (GTK_WIDGET (button));
}
@@ -204,7 +201,7 @@ goo_volume_tool_button_toolbar_reconfigured (GtkToolItem *toolitem)
static void
goo_volume_tool_button_init (GooVolumeToolButton *button)
{
- button->priv = GOO_VOLUME_TOOL_BUTTON_GET_PRIVATE (button);
+ button->priv = G_TYPE_INSTANCE_GET_PRIVATE ((button), GOO_TYPE_VOLUME_TOOL_BUTTON, GooVolumeToolButtonPrivate);
button->priv->mute = FALSE;
button->priv->mute_value = 0.0;
button->priv->value = 0.0;
@@ -253,38 +250,6 @@ goo_volume_tool_button_class_init (GooVolumeToolButtonClass *klass)
g_type_class_add_private (object_class, sizeof (GooVolumeToolButtonPrivate));
}
-static void
-button_state_changed_cb (GtkWidget *widget,
- GtkStateType previous_state,
- GooVolumeToolButton *button)
-{
- GooVolumeToolButtonPrivate *priv;
- GtkWidget *other;
- GtkStateType state = GTK_WIDGET_STATE (widget);
-
- priv = GOO_VOLUME_TOOL_BUTTON_GET_PRIVATE (button);
-
- other = (widget == priv->arrow_button) ? priv->button : priv->arrow_button;
-
- g_signal_handlers_block_by_func (other,
- G_CALLBACK (button_state_changed_cb),
- button);
-
- if (state == GTK_STATE_PRELIGHT) {
- gtk_widget_set_state (other, state);
- }
- else if (state == GTK_STATE_NORMAL) {
- gtk_widget_set_state (other, state);
- }
- else if (state == GTK_STATE_ACTIVE) {
- gtk_widget_set_state (other, GTK_STATE_NORMAL);
- }
-
- g_signal_handlers_unblock_by_func (other,
- G_CALLBACK (button_state_changed_cb),
- button);
-}
-
static gboolean
button_clicked_cb (GtkWidget *widget,
@@ -313,7 +278,6 @@ arrow_button_press_cb (GtkToggleButton *toggle_button,
GdkEventButton *event,
GooVolumeToolButton *button)
{
- GooVolumeToolButtonPrivate *priv;
GtkWidget *widget;
GtkAdjustment *adj;
int x, y, m, dx, dy, sx, sy, ystartoff, mouse_y;
@@ -321,43 +285,41 @@ arrow_button_press_cb (GtkToggleButton *toggle_button,
GdkEventButton *e;
gtk_toggle_button_set_active (toggle_button, TRUE);
-
- priv = GOO_VOLUME_TOOL_BUTTON_GET_PRIVATE (button);
widget = GTK_WIDGET (toggle_button);
- adj = gtk_range_get_adjustment (GTK_RANGE (priv->volume_scale));
+ adj = gtk_range_get_adjustment (GTK_RANGE (button->priv->volume_scale));
- priv->timeout = TRUE;
+ button->priv->timeout = TRUE;
- gtk_window_set_screen (GTK_WINDOW (priv->popup_win), gtk_widget_get_screen (widget));
+ gtk_window_set_screen (GTK_WINDOW (button->priv->popup_win), gtk_widget_get_screen (widget));
gdk_window_get_origin (widget->window, &x, &y);
x += widget->allocation.x;
y += widget->allocation.y;
- gtk_window_move (GTK_WINDOW (priv->popup_win),
+ gtk_window_move (GTK_WINDOW (button->priv->popup_win),
x, y - (SCALE_HEIGHT / 2));
update_volume_label (button);
- gtk_widget_show_all (priv->popup_win);
+ gtk_widget_show_all (button->priv->popup_win);
- gdk_window_get_origin (priv->popup_win->window, &dx, &dy);
- dy += priv->popup_win->allocation.y;
- gdk_window_get_origin (priv->volume_scale->window, &sx, &sy);
- sy += priv->volume_scale->allocation.y;
+ gdk_window_get_origin (button->priv->popup_win->window, &dx, &dy);
+ dy += button->priv->popup_win->allocation.y;
+ gdk_window_get_origin (button->priv->volume_scale->window, &sx, &sy);
+ sy += button->priv->volume_scale->allocation.y;
ystartoff = sy - dy;
mouse_y = event->y;
- v = gtk_range_get_value (GTK_RANGE (priv->volume_scale)) / (adj->upper - adj->lower);
- x += (widget->allocation.width - priv->popup_win->allocation.width) / 2;
+ v = gtk_range_get_value (GTK_RANGE (button->priv->volume_scale)) / (adj->upper - adj->lower);
+ x += (widget->allocation.width - button->priv->popup_win->allocation.width) / 2;
y -= ystartoff;
- y -= GTK_RANGE (priv->volume_scale)->min_slider_size / 2;
- m = priv->volume_scale->allocation.height - GTK_RANGE (priv->volume_scale)->min_slider_size;
+ y -= GTK_RANGE (button->priv->volume_scale)->min_slider_size / 2;
+ m = button->priv->volume_scale->allocation.height - GTK_RANGE (button->priv->volume_scale)->min_slider_size;
y -= m * (1.0 - v);
y += mouse_y;
- gtk_window_move (GTK_WINDOW (priv->popup_win), x, y);
- gdk_window_get_origin (priv->volume_scale->window, &sx, &sy);
+ gtk_window_move (GTK_WINDOW (button->priv->popup_win), x, y);
+ gdk_window_get_origin (button->priv->volume_scale->window, &sx, &sy);
- gdk_pointer_grab (priv->popup_win->window,
+ gdk_pointer_grab (button->priv->popup_win->window,
TRUE,
(GDK_POINTER_MOTION_MASK
| GDK_BUTTON_PRESS_MASK
@@ -365,21 +327,21 @@ arrow_button_press_cb (GtkToggleButton *toggle_button,
NULL,
NULL,
GDK_CURRENT_TIME);
- gdk_keyboard_grab (priv->popup_win->window, TRUE, GDK_CURRENT_TIME);
- gtk_widget_grab_focus (priv->volume_scale);
- gtk_grab_add (priv->popup_win);
+ gdk_keyboard_grab (button->priv->popup_win->window, TRUE, GDK_CURRENT_TIME);
+ gtk_widget_grab_focus (button->priv->volume_scale);
+ gtk_grab_add (button->priv->popup_win);
/* forward event to the slider */
e = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
- e->window = priv->volume_scale->window;
- e->x = priv->volume_scale->allocation.width / 2;
- m = priv->volume_scale->allocation.height - GTK_RANGE (priv->volume_scale)->min_slider_size;
- e->y = ((1.0 - v) * m) + GTK_RANGE (priv->volume_scale)->min_slider_size / 2 + 1;
- gtk_widget_event (priv->volume_scale, (GdkEvent *) e);
+ e->window = button->priv->volume_scale->window;
+ e->x = button->priv->volume_scale->allocation.width / 2;
+ m = button->priv->volume_scale->allocation.height - GTK_RANGE (button->priv->volume_scale)->min_slider_size;
+ e->y = ((1.0 - v) * m) + GTK_RANGE (button->priv->volume_scale)->min_slider_size / 2 + 1;
+ gtk_widget_event (button->priv->volume_scale, (GdkEvent *) e);
e->window = event->window;
gdk_event_free ((GdkEvent *) e);
- priv->pop_time = event->time;
+ button->priv->pop_time = event->time;
return TRUE;
}
@@ -417,16 +379,13 @@ scale_button_release_cb (GtkToggleButton *toggle_button,
GdkEventButton *event,
GooVolumeToolButton *button)
{
- GooVolumeToolButtonPrivate *priv;
- priv = GOO_VOLUME_TOOL_BUTTON_GET_PRIVATE (button);
-
- if (priv->timeout) {
+ if (button->priv->timeout) {
/* if we did a quick click, leave the window open; else, hide it */
- if (event->time > priv->pop_time + CLICK_TIMEOUT) {
+ if (event->time > button->priv->pop_time + CLICK_TIMEOUT) {
ungrab (button);
return FALSE;
}
- priv->timeout = FALSE;
+ button->priv->timeout = FALSE;
}
return FALSE;
@@ -438,10 +397,7 @@ popup_win_event_cb (GtkWidget *widget,
GdkEvent *event,
GooVolumeToolButton *button)
{
- GooVolumeToolButtonPrivate *priv;
- GtkWidget *event_widget;
-
- priv = GOO_VOLUME_TOOL_BUTTON_GET_PRIVATE (button);
+ GtkWidget *event_widget;
switch (event->type) {
case GDK_BUTTON_PRESS:
@@ -455,7 +411,7 @@ popup_win_event_cb (GtkWidget *widget,
else {
int x, y, w, h;
- gdk_window_get_geometry (priv->popup_win->window,
+ gdk_window_get_geometry (button->priv->popup_win->window,
&x, &y, &w, &h, NULL);
if ((event->button.x < 0)
@@ -491,7 +447,6 @@ button_scroll_event_cb (GtkWidget *widget,
GdkEventScroll *event,
GooVolumeToolButton *button)
{
- GooVolumeToolButtonPrivate *priv = GOO_VOLUME_TOOL_BUTTON_GET_PRIVATE (button);
double direction = 1.0;
if (event->direction == GDK_SCROLL_UP)
@@ -502,7 +457,7 @@ button_scroll_event_cb (GtkWidget *widget,
return FALSE;
goo_volume_tool_button_set_volume (button,
- priv->value + (direction * priv->step),
+ button->priv->value + (direction * button->priv->step),
TRUE);
return TRUE;
@@ -680,7 +635,6 @@ up_button_clicked_cb (GtkButton *gtk_button,
static void
goo_volume_button_construct (GooVolumeToolButton *button)
{
- struct _GooVolumeToolButtonPrivate *priv = button->priv;
GtkWidget *box;
GtkWidget *arrow;
GtkWidget *arrow_button;
@@ -690,23 +644,23 @@ goo_volume_button_construct (GooVolumeToolButton *button)
GtkWidget *up_button;
GtkWidget *down_button;
- priv->tips = gtk_tooltips_new ();
- gtk_object_ref (GTK_OBJECT (priv->tips));
- gtk_object_sink (GTK_OBJECT (priv->tips));
+ button->priv->tips = gtk_tooltips_new ();
+ gtk_object_ref (GTK_OBJECT (button->priv->tips));
+ gtk_object_sink (GTK_OBJECT (button->priv->tips));
/* Create the popup window. */
- priv->popup_win = gtk_window_new (GTK_WINDOW_POPUP);
- gtk_window_set_wmclass (GTK_WINDOW (priv->popup_win), "", "goo_volume_button");
+ button->priv->popup_win = gtk_window_new (GTK_WINDOW_POPUP);
+ gtk_window_set_wmclass (GTK_WINDOW (button->priv->popup_win), "", "goo_volume_button");
- g_signal_connect (G_OBJECT (priv->popup_win),
+ g_signal_connect (G_OBJECT (button->priv->popup_win),
"event",
G_CALLBACK (popup_win_event_cb),
button);
out_frame = gtk_frame_new (NULL);
gtk_frame_set_shadow_type (GTK_FRAME (out_frame), GTK_SHADOW_OUT);
- gtk_container_add (GTK_CONTAINER (priv->popup_win), out_frame);
+ gtk_container_add (GTK_CONTAINER (button->priv->popup_win), out_frame);
/**/
@@ -738,20 +692,20 @@ goo_volume_button_construct (GooVolumeToolButton *button)
button);
gtk_box_pack_start (GTK_BOX (volume_vbox), up_button, FALSE, FALSE, 0);
- priv->volume_scale = gtk_vscale_new_with_range (0.0, 1.0, 0.1);
- gtk_range_set_inverted (GTK_RANGE (priv->volume_scale), TRUE);
- gtk_scale_set_draw_value (GTK_SCALE (priv->volume_scale), FALSE);
- /*gtk_range_set_update_policy (GTK_RANGE (priv->volume_scale), GTK_UPDATE_DELAYED);*/
- gtk_range_set_increments (GTK_RANGE (priv->volume_scale), 0.1, 0.1);
- gtk_widget_set_size_request (priv->volume_scale, -1, SCALE_HEIGHT);
+ button->priv->volume_scale = gtk_vscale_new_with_range (0.0, 1.0, 0.1);
+ gtk_range_set_inverted (GTK_RANGE (button->priv->volume_scale), TRUE);
+ gtk_scale_set_draw_value (GTK_SCALE (button->priv->volume_scale), FALSE);
+ /*gtk_range_set_update_policy (GTK_RANGE (button->priv->volume_scale), GTK_UPDATE_DELAYED);*/
+ gtk_range_set_increments (GTK_RANGE (button->priv->volume_scale), 0.1, 0.1);
+ gtk_widget_set_size_request (button->priv->volume_scale, -1, SCALE_HEIGHT);
- gtk_box_pack_start (GTK_BOX (volume_vbox), priv->volume_scale, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (volume_vbox), button->priv->volume_scale, TRUE, TRUE, 0);
- g_signal_connect (priv->volume_scale,
+ g_signal_connect (button->priv->volume_scale,
"value_changed",
G_CALLBACK (volume_scale_value_changed_cb),
button);
- g_signal_connect (priv->volume_scale,
+ g_signal_connect (button->priv->volume_scale,
"button_release_event",
G_CALLBACK (scale_button_release_cb),
button);
@@ -783,8 +737,8 @@ goo_volume_button_construct (GooVolumeToolButton *button)
/**/
gtk_box_pack_start (GTK_BOX (volume_vbox), gtk_hseparator_new (), FALSE, FALSE, 0);
- priv->volume_label = gtk_label_new (NULL);
- gtk_box_pack_start (GTK_BOX (volume_vbox), priv->volume_label, FALSE, FALSE, 3);
+ button->priv->volume_label = gtk_label_new (NULL);
+ gtk_box_pack_start (GTK_BOX (volume_vbox), button->priv->volume_label, FALSE, FALSE, 3);
/* Construct the toolbar button. */
@@ -805,7 +759,7 @@ goo_volume_button_construct (GooVolumeToolButton *button)
gtk_container_add (GTK_CONTAINER (arrow_button), arrow);
gtk_box_pack_end (GTK_BOX (box), arrow_button,
FALSE, FALSE, 0);
- gtk_tooltips_set_tip (priv->tips, arrow_button, _("Change the volume level"), NULL);
+ gtk_tooltips_set_tip (button->priv->tips, arrow_button, _("Change the volume level"), NULL);
gtk_widget_show_all (box);
gtk_widget_hide (button->priv->volume_label);
@@ -818,15 +772,6 @@ goo_volume_button_construct (GooVolumeToolButton *button)
button->priv->box = box;
g_signal_connect (real_button,
- "state_changed",
- G_CALLBACK (button_state_changed_cb),
- button);
- g_signal_connect (arrow_button,
- "state_changed",
- G_CALLBACK (button_state_changed_cb),
- button);
-
- g_signal_connect (real_button,
"clicked",
G_CALLBACK (button_clicked_cb),
button);
diff --git a/src/goo-volume-tool-button.h b/src/goo-volume-tool-button.h
index cf49e22..2172ad8 100644
--- a/src/goo-volume-tool-button.h
+++ b/src/goo-volume-tool-button.h
@@ -64,8 +64,6 @@ typedef struct _GooVolumeToolButtonPrivate GooVolumeToolButtonPrivate;
struct _GooVolumeToolButton {
GtkToolButton parent;
-
- /*< private >*/
GooVolumeToolButtonPrivate *priv;
};
diff --git a/src/goo-window.c b/src/goo-window.c
index 564e465..158a384 100644
--- a/src/goo-window.c
+++ b/src/goo-window.c
@@ -3,7 +3,7 @@
/*
* Goo
*
- * Copyright (C) 2004, 2005 Free Software Foundation, Inc.
+ * Copyright (C) 2004-2009 Free Software Foundation, Inc.
*
* 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
@@ -23,21 +23,23 @@
#include <config.h>
#include <math.h>
#include <string.h>
-#include <gnome.h>
-#include <libbonobo.h>
-#include <libgnomevfs/gnome-vfs-ops.h>
+#include <glib/gi18n.h>
+#include <gdk/gdkkeysyms.h>
+#include <gtk/gtk.h>
#include <gst/gst.h>
-
+#ifdef ENABLE_MEDIA_KEYS
+#include <dbus/dbus-glib.h>
+#endif /* ENABLE_MEDIA_KEYS */
#include "actions.h"
+#include "gio-utils.h"
#include "dlg-cover-chooser.h"
-#include "eggtrayicon.h"
-#include "file-utils.h"
#include "goo-marshal.h"
#include "goo-stock.h"
#include "goo-player.h"
#include "goo-player-info.h"
#include "goo-window.h"
#include "goo-volume-tool-button.h"
+#include "gth-user-dir.h"
#include "gtk-utils.h"
#include "gtk-file-chooser-preview.h"
#include "glib-utils.h"
@@ -46,7 +48,6 @@
#include "preferences.h"
#include "typedefs.h"
#include "ui.h"
-
#include "icons/pixbufs.h"
#define ICON_GTK_SIZE GTK_ICON_SIZE_LARGE_TOOLBAR
@@ -56,7 +57,6 @@
#define DEFAULT_WIN_HEIGHT 400
#define HIDE_TRACK_LIST N_("Hide _tracks")
#define SHOW_TRACK_LIST N_("Show _tracks")
-#define DEFAULT_DEVICE "/dev/cdrom"
#define DEFAULT_VOLUME 100
#define PLAYER_CHECK_RATE 100
#define COVER_SIZE 80
@@ -67,7 +67,7 @@
#define TRAY_TOOLTIP_DELAY 500
#define AUTOPLAY_DELAY 250
-struct _GooWindowPrivateData {
+struct _GooWindowPrivate {
GtkUIManager *ui;
GtkWidget *list_view;
GtkListStore *list_store;
@@ -98,7 +98,7 @@ struct _GooWindowPrivateData {
guint list_info_cid;
guint progress_cid;
- guint first_timeout_handle;
+ guint first_time_event;
guint next_timeout_handle;
gint activity_ref; /* when > 0 some activity
* is present. */
@@ -121,14 +121,19 @@ struct _GooWindowPrivateData {
gboolean hibernate;
gboolean notify_action;
gboolean ejected;
+
+#ifdef ENABLE_MEDIA_KEYS
+ DBusGProxy *media_keys_proxy;
+ gulong focus_in_event;
+#endif /* ENABLE_MEDIA_KEYS */
};
enum {
UPDATE_COVER,
LAST_SIGNAL
};
-static guint goo_window_signals[LAST_SIGNAL] = { 0 };
+static guint goo_window_signals[LAST_SIGNAL] = { 0 };
static int icon_size = 0;
GList *window_list = NULL;
@@ -144,10 +149,11 @@ enum {
};
#define GOO_WINDOW_GET_PRIVATE_DATA(object) \
- (G_TYPE_INSTANCE_GET_PRIVATE ((object), GOO_TYPE_WINDOW, GooWindowPrivateData))
+ (G_TYPE_INSTANCE_GET_PRIVATE ((object), GOO_TYPE_WINDOW, GooWindowPrivate))
+
+G_DEFINE_TYPE (GooWindow, goo_window, GTH_TYPE_WINDOW)
-G_DEFINE_TYPE (GooWindow, goo_window, GNOME_TYPE_APP)
static void
set_active (GooWindow *window,
@@ -176,73 +182,38 @@ set_sensitive (GooWindow *window,
static void
window_update_statusbar_list_info (GooWindow *window)
{
- GooWindowPrivateData *priv = window->priv;
-
if (window == NULL)
return;
- gtk_statusbar_pop (GTK_STATUSBAR (priv->statusbar), priv->list_info_cid);
+ gtk_statusbar_pop (GTK_STATUSBAR (window->priv->statusbar), window->priv->list_info_cid);
- if (priv->album->n_tracks != 0) {
- char time_text[64];
- char *tracks_s = NULL;
- GString *status;
+ if (window->priv->album->n_tracks != 0) {
+ char *time_text;
+ char *tracks_s = NULL;
+ GString *status;
- tracks_s = g_strdup_printf (ngettext ("%d track", "%d tracks", priv->album->n_tracks), priv->album->n_tracks);
- set_time_string (time_text, priv->album->total_length);
+ tracks_s = g_strdup_printf (ngettext ("%d track", "%d tracks", window->priv->album->n_tracks), window->priv->album->n_tracks);
+ time_text = _g_format_duration_for_display (window->priv->album->total_length * 1000);
status = g_string_new (NULL);
g_string_append (status, tracks_s);
g_string_append (status, ", ");
g_string_append (status, time_text);
- gtk_statusbar_push (GTK_STATUSBAR (priv->statusbar),
- priv->list_info_cid,
+ gtk_statusbar_push (GTK_STATUSBAR (window->priv->statusbar),
+ window->priv->list_info_cid,
status->str);
g_string_free (status, TRUE);
+ g_free (time_text);
g_free (tracks_s);
}
}
-/* window_update_list */
-
-
-typedef struct {
- GooWindow *window;
- GList *file_list;
-} UpdateData;
-
-
-static void
-update_data_free (gpointer callback_data)
-{
- UpdateData *data = callback_data;
-
- g_return_if_fail (data != NULL);
-
- window_update_statusbar_list_info (data->window);
-
- if (data->file_list != NULL)
- g_list_free (data->file_list);
- g_free (data);
-}
-
-
-static char*
-get_time_string (gint64 time)
-{
- char buffer[1024];
- set_time_string (buffer, time);
- return g_strdup (buffer);
-}
-
-
static void
window_update_sensitivity (GooWindow *window)
{
- GooWindowPrivateData *priv = window->priv;
int n_selected;
gboolean sel_not_null;
gboolean one_file_selected;
@@ -254,11 +225,11 @@ window_update_sensitivity (GooWindow *window)
gboolean stopped;
gboolean play_all;
- n_selected = _gtk_count_selected (gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->list_view)));
+ n_selected = _gtk_count_selected (gtk_tree_view_get_selection (GTK_TREE_VIEW (window->priv->list_view)));
sel_not_null = n_selected > 0;
one_file_selected = n_selected == 1;
- state = goo_player_get_state (priv->player);
- error = (state == GOO_PLAYER_STATE_ERROR) || priv->hibernate;
+ state = goo_player_get_state (window->priv->player);
+ error = ! goo_player_is_audio_cd (window->priv->player) || window->priv->hibernate;
playing = state == GOO_PLAYER_STATE_PLAYING;
paused = state == GOO_PLAYER_STATE_PAUSED;
stopped = state == GOO_PLAYER_STATE_STOPPED;
@@ -273,8 +244,8 @@ window_update_sensitivity (GooWindow *window)
set_sensitive (window, "Next", audio_cd);
set_sensitive (window, "Prev", audio_cd);
- set_sensitive (window, "Extract", audio_cd && (priv->album->n_tracks > 0));
- set_sensitive (window, "CopyDisc", audio_cd && (priv->album->n_tracks > 0));
+ set_sensitive (window, "Extract", audio_cd && (window->priv->album->n_tracks > 0));
+ set_sensitive (window, "CopyDisc", audio_cd && (window->priv->album->n_tracks > 0));
set_sensitive (window, "Properties", audio_cd);
set_sensitive (window, "PickCoverFromDisk", audio_cd);
set_sensitive (window, "RemoveCover", audio_cd);
@@ -283,20 +254,20 @@ window_update_sensitivity (GooWindow *window)
set_sensitive (window, "Repeat", play_all);
set_sensitive (window, "Shuffle", play_all);
- gtk_widget_set_sensitive (priv->list_view, audio_cd);
+ gtk_widget_set_sensitive (window->priv->list_view, audio_cd);
- set_sensitive (window, "Eject", ! priv->hibernate);
- set_sensitive (window, "EjectToolBar", ! priv->hibernate);
- set_sensitive (window, "Preferences", ! priv->hibernate);
+ set_sensitive (window, "Eject", ! window->priv->hibernate);
+ set_sensitive (window, "EjectToolBar", ! window->priv->hibernate);
+ set_sensitive (window, "Preferences", ! window->priv->hibernate);
}
static GdkPixbuf *
create_void_icon (GooWindow *window)
{
- GtkSettings *settings;
- int width, height, icon_size;
- GdkPixbuf *icon;
+ GtkSettings *settings;
+ int width, height, icon_size;
+ GdkPixbuf *icon;
settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (window)));
@@ -317,8 +288,8 @@ get_iter_from_track_number (GooWindow *window,
int track_number,
GtkTreeIter *iter)
{
- GooWindowPrivateData *priv = window->priv;
- GtkTreeModel *model = GTK_TREE_MODEL (priv->list_store);
+
+ GtkTreeModel *model = GTK_TREE_MODEL (window->priv->list_store);
if (! gtk_tree_model_get_iter_first (model, iter))
return FALSE;
@@ -332,7 +303,8 @@ get_iter_from_track_number (GooWindow *window,
return TRUE;
}
track_info_unref (track);
- } while (gtk_tree_model_iter_next (model, iter));
+ }
+ while (gtk_tree_model_iter_next (model, iter));
return FALSE;
}
@@ -343,7 +315,7 @@ set_track_icon (GooWindow *window,
int track_number,
const char *stock_id)
{
- GooWindowPrivateData *priv = window->priv;
+
GtkTreeIter iter;
GdkPixbuf *icon;
@@ -357,7 +329,7 @@ set_track_icon (GooWindow *window,
NULL);
else
icon = create_void_icon (window);
- gtk_list_store_set (priv->list_store, &iter,
+ gtk_list_store_set (window->priv->list_store, &iter,
COLUMN_ICON, icon,
-1);
g_object_unref (icon);
@@ -373,69 +345,6 @@ set_current_track_icon (GooWindow *window,
}
-static gboolean
-update_list_idle (gpointer callback_data)
-{
- UpdateData *data = callback_data;
- GooWindow *window = data->window;
- GooWindowPrivateData *priv = window->priv;
- GList *file_list;
- GList *scan;
- int i, n = FILES_TO_PROCESS_AT_ONCE;
- GdkPixbuf *icon;
-
- if (priv->update_timeout_handle != 0) {
- g_source_remove (priv->update_timeout_handle);
- priv->update_timeout_handle = 0;
- }
-
- if (data->file_list == NULL) {
- window_update_sensitivity (window);
- update_data_free (data);
- return FALSE;
- }
-
- file_list = data->file_list;
- for (i = 0, scan = file_list; (i < n) && scan->next; i++)
- scan = scan->next;
-
- data->file_list = scan->next;
- scan->next = NULL;
-
- icon = create_void_icon (window);
-
- for (scan = file_list; scan; scan = scan->next) {
- TrackInfo *track = scan->data;
- GtkTreeIter iter;
- char *time_s;
-
- gtk_list_store_prepend (priv->list_store, &iter);
-
- time_s = get_time_string (track->length);
- gtk_list_store_set (priv->list_store, &iter,
- COLUMN_TRACK_INFO, track,
- COLUMN_NUMBER, track->number + 1, /*FIXME*/
- COLUMN_ICON, icon,
- COLUMN_TIME, time_s,
- COLUMN_TITLE, track->title,
- COLUMN_ARTIST, track->artist,
- -1);
- g_free (time_s);
- }
-
- g_object_unref (icon);
-
- if (gtk_events_pending ())
- gtk_main_iteration_do (TRUE);
-
- g_list_free (file_list);
-
- priv->update_timeout_handle = g_idle_add (update_list_idle, data);
-
- return FALSE;
-}
-
-
void
goo_window_update (GooWindow *window)
{
@@ -447,34 +356,51 @@ goo_window_update (GooWindow *window)
static void
goo_window_update_list (GooWindow *window)
{
- GooWindowPrivateData *priv = window->priv;
- UpdateData *udata;
-
- if (GTK_WIDGET_REALIZED (priv->list_view))
- gtk_tree_view_scroll_to_point (GTK_TREE_VIEW (priv->list_view), 0, 0);
+ GdkPixbuf *icon;
+ GList *scan;
+
+ if (GTK_WIDGET_REALIZED (window->priv->list_view))
+ gtk_tree_view_scroll_to_point (GTK_TREE_VIEW (window->priv->list_view), 0, 0);
/**/
- track_info_unref (priv->current_track);
- priv->current_track = NULL;
+ track_info_unref (window->priv->current_track);
+ window->priv->current_track = NULL;
- if (priv->playlist != NULL)
- g_list_free (priv->playlist);
- priv->playlist = NULL;
+ if (window->priv->playlist != NULL)
+ g_list_free (window->priv->playlist);
+ window->priv->playlist = NULL;
/**/
- gtk_expander_set_expanded (GTK_EXPANDER (window->priv->list_expander), (priv->album->tracks != NULL));
+ gtk_expander_set_expanded (GTK_EXPANDER (window->priv->list_expander), (window->priv->album->tracks != NULL));
/**/
- gtk_list_store_clear (priv->list_store);
+ icon = create_void_icon (window);
+ gtk_list_store_clear (window->priv->list_store);
+ for (scan = window->priv->album->tracks; scan; scan = scan->next) {
+ TrackInfo *track = scan->data;
+ GtkTreeIter iter;
+ char *time_s;
+
+ gtk_list_store_prepend (window->priv->list_store, &iter);
- udata = g_new0 (UpdateData, 1);
- udata->window = window;
- if (priv->album->tracks != NULL)
- udata->file_list = g_list_copy (priv->album->tracks);
- update_list_idle (udata);
+ time_s = _g_format_duration_for_display (track->length * 1000);
+ gtk_list_store_set (window->priv->list_store, &iter,
+ COLUMN_TRACK_INFO, track,
+ COLUMN_NUMBER, track->number + 1,
+ COLUMN_ICON, icon,
+ COLUMN_TIME, time_s,
+ COLUMN_TITLE, track->title,
+ COLUMN_ARTIST, track->artist,
+ -1);
+ g_free (time_s);
+ }
+
+ window_update_sensitivity (window);
+
+ g_object_unref (icon);
}
@@ -484,9 +410,8 @@ goo_window_update_list (GooWindow *window)
static void
goo_window_update_titles (GooWindow *window)
{
- GooWindowPrivateData *priv = window->priv;
- GtkTreeModel *model = GTK_TREE_MODEL (priv->list_store);
- GtkTreeIter iter;
+ GtkTreeModel *model = GTK_TREE_MODEL (window->priv->list_store);
+ GtkTreeIter iter;
if (! gtk_tree_model_get_iter_first (model, &iter))
return;
@@ -496,28 +421,28 @@ goo_window_update_titles (GooWindow *window)
TrackInfo *new_track;
gtk_tree_model_get (model, &iter, COLUMN_TRACK_INFO, &track, -1);
- new_track = album_info_get_track (priv->album, track->number);
+ new_track = album_info_get_track (window->priv->album, track->number);
track_info_unref (track);
if (new_track == NULL)
continue;
- gtk_list_store_set (priv->list_store, &iter,
+ gtk_list_store_set (window->priv->list_store, &iter,
COLUMN_TRACK_INFO, new_track,
COLUMN_TITLE, new_track->title,
COLUMN_ARTIST, new_track->artist,
-1);
/* Update the current track info. */
- if ((priv->current_track != NULL)
- && (new_track->number == priv->current_track->number)) {
- track_info_unref (priv->current_track);
+ if ((window->priv->current_track != NULL)
+ && (new_track->number == window->priv->current_track->number)) {
+ track_info_unref (window->priv->current_track);
track_info_ref (new_track);
- priv->current_track = new_track;
+ window->priv->current_track = new_track;
}
track_info_unref (new_track);
-
- } while (gtk_tree_model_iter_next (model, &iter));
+ }
+ while (gtk_tree_model_iter_next (model, &iter));
}
@@ -534,53 +459,63 @@ goo_window_finalize (GObject *object)
eel_gconf_notification_remove (window->priv->cnxn_id[i]);
if (window->priv != NULL) {
- GooWindowPrivateData *priv = window->priv;
+#ifdef ENABLE_MEDIA_KEYS
+ if (window->priv->media_keys_proxy != NULL) {
+ dbus_g_proxy_call (window->priv->media_keys_proxy,
+ "ReleaseMediaPlayerKeys",
+ NULL,
+ G_TYPE_STRING, "goobox",
+ G_TYPE_INVALID,
+ G_TYPE_INVALID);
+ g_object_unref (window->priv->media_keys_proxy);
+ }
+#endif /* ENABLE_MEDIA_KEYS */
/* Save preferences */
- eel_gconf_set_integer (PREF_GENERAL_VOLUME, (int) (goo_player_get_volume (priv->player) * 100.0));
-
+ eel_gconf_set_integer (PREF_GENERAL_VOLUME, (int) (goo_player_get_audio_volume (window->priv->player) * 100.0));
+
/**/
- gtk_object_destroy (GTK_OBJECT (priv->tooltips));
- g_object_unref (priv->list_store);
+ gtk_object_destroy (GTK_OBJECT (window->priv->tooltips));
+ g_object_unref (window->priv->list_store);
- if (priv->tray_tooltip_delay != 0) {
- g_source_remove (priv->tray_tooltip_delay);
- priv->tray_tooltip_delay = 0;
+ if (window->priv->tray_tooltip_delay != 0) {
+ g_source_remove (window->priv->tray_tooltip_delay);
+ window->priv->tray_tooltip_delay = 0;
}
- if (priv->next_timeout_handle != 0) {
- g_source_remove (priv->next_timeout_handle);
- priv->next_timeout_handle = 0;
+ if (window->priv->next_timeout_handle != 0) {
+ g_source_remove (window->priv->next_timeout_handle);
+ window->priv->next_timeout_handle = 0;
}
- if (priv->update_timeout_handle != 0) {
- g_source_remove (priv->update_timeout_handle);
- priv->update_timeout_handle = 0;
+ if (window->priv->update_timeout_handle != 0) {
+ g_source_remove (window->priv->update_timeout_handle);
+ window->priv->update_timeout_handle = 0;
}
- if (priv->playlist != NULL)
- g_list_free (priv->playlist);
+ if (window->priv->playlist != NULL)
+ g_list_free (window->priv->playlist);
- if (priv->file_popup_menu != NULL) {
- gtk_widget_destroy (priv->file_popup_menu);
- priv->file_popup_menu = NULL;
+ if (window->priv->file_popup_menu != NULL) {
+ gtk_widget_destroy (window->priv->file_popup_menu);
+ window->priv->file_popup_menu = NULL;
}
- if (priv->cover_popup_menu != NULL) {
- gtk_widget_destroy (priv->cover_popup_menu);
- priv->cover_popup_menu = NULL;
+ if (window->priv->cover_popup_menu != NULL) {
+ gtk_widget_destroy (window->priv->cover_popup_menu);
+ window->priv->cover_popup_menu = NULL;
}
- g_signal_handlers_disconnect_by_data (priv->player, window);
- g_object_unref (priv->player);
+ g_signal_handlers_disconnect_by_data (window->priv->player, window);
+ g_object_unref (window->priv->player);
- track_info_unref (priv->current_track);
- album_info_unref (priv->album);
+ track_info_unref (window->priv->current_track);
+ album_info_unref (window->priv->album);
- path_list_free (priv->url_list);
- priv->url_list = NULL;
+ _g_string_list_free (window->priv->url_list);
+ window->priv->url_list = NULL;
window->priv = NULL;
}
@@ -797,8 +732,8 @@ static int
get_track_number_from_position (GooWindow *window,
int pos)
{
- GooWindowPrivateData *priv = window->priv;
- GtkTreeModel *model = GTK_TREE_MODEL (priv->list_store);
+
+ GtkTreeModel *model = GTK_TREE_MODEL (window->priv->list_store);
GtkTreeIter iter;
int i = 0;
@@ -814,7 +749,8 @@ get_track_number_from_position (GooWindow *window,
if (i == pos)
return n;
i++;
- } while (gtk_tree_model_iter_next (model, &iter));
+ }
+ while (gtk_tree_model_iter_next (model, &iter));
return -1;
}
@@ -824,8 +760,8 @@ static int
get_position_from_track_number (GooWindow *window,
int track_number)
{
- GooWindowPrivateData *priv = window->priv;
- GtkTreeModel *model = GTK_TREE_MODEL (priv->list_store);
+
+ GtkTreeModel *model = GTK_TREE_MODEL (window->priv->list_store);
GtkTreeIter iter;
int pos = 0;
@@ -852,31 +788,31 @@ create_playlist (GooWindow *window,
gboolean play_all,
gboolean shuffle)
{
- GooWindowPrivateData *priv = window->priv;
+
GList *playlist;
int pos = 0, i;
debug (DEBUG_INFO, "PLAY ALL: %d\n", play_all);
debug (DEBUG_INFO, "SHUFFLE: %d\n", shuffle);
- if (priv->playlist != NULL)
- g_list_free (priv->playlist);
- priv->playlist = NULL;
+ if (window->priv->playlist != NULL)
+ g_list_free (window->priv->playlist);
+ window->priv->playlist = NULL;
if (!play_all)
return;
playlist = NULL;
- if (priv->current_track != NULL)
- pos = get_position_from_track_number (window, priv->current_track->number);
+ if (window->priv->current_track != NULL)
+ pos = get_position_from_track_number (window, window->priv->current_track->number);
- for (i = 0; i < priv->album->n_tracks; i++, pos = (pos + 1) % priv->album->n_tracks) {
+ for (i = 0; i < window->priv->album->n_tracks; i++, pos = (pos + 1) % window->priv->album->n_tracks) {
int track_number;
track_number = get_track_number_from_position (window, pos);
- if ((priv->current_track != NULL)
- && (priv->current_track->number == track_number))
+ if ((window->priv->current_track != NULL)
+ && (window->priv->current_track->number == track_number))
continue;
playlist = g_list_prepend (playlist, GINT_TO_POINTER (track_number));
}
@@ -901,7 +837,7 @@ create_playlist (GooWindow *window,
g_rand_free (grand);
}
- priv->playlist = playlist;
+ window->priv->playlist = playlist;
}
@@ -909,11 +845,9 @@ static void
play_track (GooWindow *window,
int track_number)
{
- GooWindowPrivateData *priv = window->priv;
-
- if (!GTK_WIDGET_VISIBLE (window))
- priv->notify_action = TRUE;
- goo_player_seek_track (priv->player, track_number);
+ if (! GTK_WIDGET_VISIBLE (window))
+ window->priv->notify_action = TRUE;
+ goo_player_seek_track (window->priv->player, track_number);
}
@@ -1045,8 +979,9 @@ first_time_idle (gpointer callback_data)
{
GooWindow *window = callback_data;
- g_source_remove (window->priv->first_timeout_handle);
-
+ g_source_remove (window->priv->first_time_event);
+ window->priv->first_time_event = 0;
+
goo_player_update (window->priv->player);
return FALSE;
@@ -1072,8 +1007,49 @@ goo_window_show (GtkWidget *widget)
set_active (window, "ViewStatusbar", view_foobar);
goo_window_set_statusbar_visibility (window, view_foobar);
- if (window->priv->first_timeout_handle == 0)
- window->priv->first_timeout_handle = g_timeout_add (IDLE_TIMEOUT, first_time_idle, window);
+ if (window->priv->first_time_event == 0)
+ window->priv->first_time_event = g_timeout_add (IDLE_TIMEOUT, first_time_idle, window);
+}
+
+
+static gboolean
+check_state_for_closing_cb (gpointer data)
+{
+ GooWindow *self = data;
+
+ g_source_remove (self->priv->check_id);
+ self->priv->check_id = 0;
+
+ if (! goo_player_get_is_busy (self->priv->player)) {
+ gtk_widget_destroy (GTK_WIDGET (self));
+ return FALSE;
+ }
+
+ self->priv->check_id = g_timeout_add (PLAYER_CHECK_RATE,
+ check_state_for_closing_cb,
+ self);
+
+ return FALSE;
+}
+
+
+static void
+goo_window_real_close (GthWindow *base)
+{
+ GooWindow *self = GOO_WINDOW (base);
+
+ self->priv->exiting = TRUE;
+ if (goo_player_get_is_busy (self->priv->player)) {
+ gtk_widget_set_sensitive (GTK_WIDGET (self), FALSE);
+
+ if (self->priv->check_id != 0)
+ g_source_remove (self->priv->check_id);
+ self->priv->check_id = g_timeout_add (PLAYER_CHECK_RATE,
+ check_state_for_closing_cb,
+ self);
+ }
+ else
+ gtk_widget_destroy (GTK_WIDGET (self));
}
@@ -1082,14 +1058,20 @@ goo_window_class_init (GooWindowClass *class)
{
GObjectClass *gobject_class;
GtkWidgetClass *widget_class;
+ GthWindowClass *window_class;
- widget_class = (GtkWidgetClass*) class;
- gobject_class = (GObjectClass*) class;
+ g_type_class_add_private (class, sizeof (GooWindowPrivate));
+ gobject_class = (GObjectClass*) class;
gobject_class->finalize = goo_window_finalize;
+
+ widget_class = (GtkWidgetClass*) class;
widget_class->unrealize = goo_window_unrealize;
- widget_class->show = goo_window_show;
+ widget_class->show = goo_window_show;
+ window_class = (GthWindowClass *) class;
+ window_class->close = goo_window_real_close;
+
goo_window_signals[UPDATE_COVER] =
g_signal_new ("update-cover",
G_TYPE_FROM_CLASS (class),
@@ -1099,7 +1081,6 @@ goo_window_class_init (GooWindowClass *class)
goo_marshal_VOID__VOID,
G_TYPE_NONE,
0);
- g_type_class_add_private (class, sizeof (GooWindowPrivateData));
}
@@ -1266,7 +1247,7 @@ get_action_name (GooPlayerAction action)
name = "METADATA";
break;
default:
- name = "WHAT?";
+ name = "???";
break;
}
@@ -1283,14 +1264,14 @@ set_action_label_and_icon (GooWindow *window,
const char *action_prefix,
...)
{
- GooWindowPrivateData *priv = window->priv;
+
va_list args;
va_start (args, action_prefix);
while (action_prefix != NULL) {
char *path = g_strconcat (action_prefix, action_name, NULL);
- GtkAction *action = gtk_ui_manager_get_action (priv->ui, path);
+ GtkAction *action = gtk_ui_manager_get_action (window->priv->ui, path);
if (action != NULL)
g_object_set (G_OBJECT (action),
@@ -1314,8 +1295,6 @@ player_start_cb (GooPlayer *player,
GooPlayerAction action,
GooWindow *window)
{
- GooWindowPrivateData *priv = window->priv;
-
debug (DEBUG_INFO, "START [%s]\n", get_action_name (action));
switch (action) {
@@ -1328,32 +1307,32 @@ player_start_cb (GooPlayer *player,
"/MenuBar/CDMenu/",
NULL);
-#ifdef HAVE_LIBNOTIFY
+#ifdef ENABLE_NOTIFICATION
- if (priv->notify_action) {
+ if (window->priv->notify_action) {
GString *info = g_string_new ("");
- if (priv->album->title != NULL) {
- char *e_album = g_markup_escape_text (priv->album->title, -1);
+ if (window->priv->album->title != NULL) {
+ char *e_album = g_markup_escape_text (window->priv->album->title, -1);
g_string_append_printf (info, "<i>%s</i>", e_album);
g_free (e_album);
}
g_string_append (info, "\n");
- if (priv->album->artist != NULL) {
- char *e_artist = g_markup_escape_text (priv->album->artist, -1);
+ if (window->priv->album->artist != NULL) {
+ char *e_artist = g_markup_escape_text (window->priv->album->artist, -1);
g_string_append_printf (info, "<big>%s</big>", e_artist);
g_free (e_artist);
}
system_notify (window,
- priv->current_track->title,
+ window->priv->current_track->title,
info->str);
g_string_free (info, TRUE);
- priv->notify_action = FALSE;
+ window->priv->notify_action = FALSE;
}
-#endif /* HAVE_LIBNOTIFY */
+#endif /* ENABLE_NOTIFICATION */
break;
default:
@@ -1365,15 +1344,14 @@ player_start_cb (GooPlayer *player,
static void
goo_window_select_current_track (GooWindow *window)
{
- GooWindowPrivateData *priv = window->priv;
- GtkTreeSelection *selection;
- GtkTreePath *path;
- int pos;
+ GtkTreeSelection *selection;
+ GtkTreePath *path;
+ int pos;
- selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->list_view));
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (window->priv->list_view));
gtk_tree_selection_unselect_all (selection);
- pos = get_position_from_track_number (window, priv->current_track->number);
+ pos = get_position_from_track_number (window, window->priv->current_track->number);
if (pos == -1)
return;
@@ -1387,11 +1365,10 @@ static gboolean
next_time_idle (gpointer callback_data)
{
GooWindow *window = callback_data;
- GooWindowPrivateData *priv = window->priv;
- if (priv->next_timeout_handle != 0) {
- g_source_remove (priv->next_timeout_handle);
- priv->next_timeout_handle = 0;
+ if (window->priv->next_timeout_handle != 0) {
+ g_source_remove (window->priv->next_timeout_handle);
+ window->priv->next_timeout_handle = 0;
}
play_next_track_in_playlist (window);
@@ -1420,40 +1397,33 @@ goo_window_set_current_track (GooWindow *window,
static void
window_update_title (GooWindow *window)
{
- GooWindowPrivateData *priv = window->priv;
- GooPlayerState state;
- gboolean playing;
- gboolean paused;
- gboolean stopped;
- GString *title;
+ GooPlayerState state;
+ gboolean playing;
+ gboolean paused;
+ gboolean stopped;
+ GString *title;
- state = goo_player_get_state (priv->player);
+ state = goo_player_get_state (window->priv->player);
playing = state == GOO_PLAYER_STATE_PLAYING;
paused = state == GOO_PLAYER_STATE_PAUSED;
stopped = state == GOO_PLAYER_STATE_STOPPED;
title = g_string_new ("");
- if (priv->current_track != NULL) {
- g_string_append (title, priv->current_track->title);
- if (priv->current_track->artist != NULL) {
+ if (window->priv->current_track != NULL) {
+ g_string_append (title, window->priv->current_track->title);
+ if (window->priv->current_track->artist != NULL) {
g_string_append (title, " - ");
- g_string_append (title, priv->current_track->artist);
+ g_string_append (title, window->priv->current_track->artist);
}
- /*g_string_append_printf (title,
+ g_string_append_printf (title,
" [%d/%d]",
- priv->current_track->number + 1,
- priv->album->n_tracks);
+ window->priv->current_track->number + 1,
+ window->priv->album->n_tracks);
if (paused)
g_string_append_printf (title,
" [%s]",
- _("Paused"));*/
- }
- else if (state == GOO_PLAYER_STATE_ERROR) {
- GError *error = goo_player_get_error (priv->player);
-
- g_string_append (title, error->message);
- g_error_free (error);
+ _("Paused"));
}
else if (state == GOO_PLAYER_STATE_NO_DISC) {
g_string_append (title, _("No Disc"));
@@ -1462,20 +1432,20 @@ window_update_title (GooWindow *window)
g_string_append (title, _("Data Disc"));
}
else {
- if ((priv->album->title == NULL) || (priv->album->artist == NULL))
+ if ((window->priv->album->title == NULL) || (window->priv->album->artist == NULL))
g_string_append (title, _("Audio CD"));
else {
- g_string_append (title, priv->album->title);
+ g_string_append (title, window->priv->album->title);
g_string_append (title, " - ");
- g_string_append (title, priv->album->artist);
+ g_string_append (title, window->priv->album->artist);
}
}
gtk_window_set_title (GTK_WINDOW (window), title->str);
- /*if (priv->tray_tips != NULL) {
- gtk_tooltips_set_tip (GTK_TOOLTIPS (priv->tray_tips),
- priv->tray_box,
+ /* FIXME if (window->priv->tray_tips != NULL) {
+ gtk_tooltips_set_tip (GTK_TOOLTIPS (window->priv->tray_tips),
+ window->priv->tray_box,
title->str,
NULL);
}*/
@@ -1489,24 +1459,19 @@ goo_window_get_cover_filename (GooWindow *window)
{
const char *discid;
char *filename;
- char *dir;
+ char *path;
discid = goo_player_get_discid (window->priv->player);
if (discid == NULL)
return NULL;
- dir = g_build_filename (g_get_home_dir (),
- ".gnome2",
- "goobox.d",
- "covers",
- NULL);
- ensure_dir_exists (dir, 0700);
- filename = g_strconcat (dir, G_DIR_SEPARATOR_S,
- discid, ".png",
- NULL);
- g_free (dir);
+ filename = g_strconcat (discid, ".png", NULL);
+ gth_user_dir_make_dir_for_file (GTH_DIR_CONFIG, "goobox", "covers", filename, NULL);
+ path = gth_user_dir_get_file (GTH_DIR_CONFIG, "goobox", "covers", filename, NULL);
+
+ g_free (filename);
- return filename;
+ return path;
}
@@ -1520,61 +1485,29 @@ goo_window_update_cover (GooWindow *window)
}
-static char*
-get_config_filename (void)
-{
- char *config_dirname;
- char *config_filename;
-
- config_dirname = g_build_filename (g_get_home_dir (),
- ".gnome2",
- "goobox.d",
- NULL);
- ensure_dir_exists (config_dirname, 0700);
- config_filename = g_build_filename (config_dirname,
- "config",
- NULL);
- g_free (config_dirname);
-
- return config_filename;
-}
-
-
static void
save_config_file (GKeyFile *kv_file,
char *config_filename)
{
- GnomeVFSURI *uri;
- GnomeVFSHandle *handle;
- GnomeVFSResult result;
- GError *error = NULL;
- char *buffer;
- gsize buffer_size;
-
- uri = new_uri_from_path (config_filename);
- result = gnome_vfs_create_uri (&handle,
- uri,
- GNOME_VFS_OPEN_WRITE,
- FALSE,
- 0600);
- gnome_vfs_uri_unref (uri);
-
- if (result != GNOME_VFS_OK)
- return;
+ GFile *file;
+ char *buffer;
+ gsize buffer_size;
+ file = g_file_new_for_path (config_filename);
buffer = g_key_file_to_data (kv_file,
&buffer_size,
- &error);
- if (error == NULL)
- gnome_vfs_write (handle,
- buffer,
- buffer_size,
- NULL);
- else
- g_error_free (error);
+ NULL);
+ if (buffer != NULL)
+ g_write_file (file,
+ FALSE,
+ G_FILE_CREATE_NONE,
+ buffer,
+ buffer_size,
+ NULL,
+ NULL);
g_free (buffer);
- gnome_vfs_close (handle);
+ g_object_unref (file);
}
@@ -1588,7 +1521,7 @@ goo_window_set_current_cd_autofetch (GooWindow *window,
kv_file = g_key_file_new ();
- config_filename = get_config_filename ();
+ config_filename = gth_user_dir_get_file (GTH_DIR_CONFIG, "goobox", "config", NULL);
g_key_file_load_from_file (kv_file,
config_filename,
G_KEY_FILE_NONE,
@@ -1599,6 +1532,7 @@ goo_window_set_current_cd_autofetch (GooWindow *window,
discid,
autofetch);
+ gth_user_dir_make_dir_for_file (GTH_DIR_CONFIG, "goobox", "config", NULL);
save_config_file (kv_file, config_filename);
g_free (config_filename);
@@ -1609,34 +1543,35 @@ goo_window_set_current_cd_autofetch (GooWindow *window,
gboolean
goo_window_get_current_cd_autofetch (GooWindow *window)
{
- GKeyFile *kv_file;
- char *config_filename;
- const char *discid;
- gboolean value = FALSE;
- GError *error = NULL;
+ GKeyFile *kv_file;
+ char *config_filename;
+ gboolean autofetch = FALSE;
kv_file = g_key_file_new ();
-
- config_filename = get_config_filename ();
- g_key_file_load_from_file (kv_file,
- config_filename,
- G_KEY_FILE_NONE,
- NULL);
- discid = goo_player_get_discid (window->priv->player);
-
- value = g_key_file_get_boolean (kv_file,
- CONFIG_KEY_AUTOFETCH_GROUP,
- discid,
- &error);
- if (error != NULL) {
- value = TRUE;
- g_error_free (error);
+ config_filename = gth_user_dir_get_file (GTH_DIR_CONFIG, "goobox", "config", NULL);
+ if (g_key_file_load_from_file (kv_file,
+ config_filename,
+ G_KEY_FILE_NONE,
+ NULL))
+ {
+ const char *discid;
+ GError *error = NULL;
+
+ discid = goo_player_get_discid (window->priv->player);
+ autofetch = g_key_file_get_boolean (kv_file,
+ CONFIG_KEY_AUTOFETCH_GROUP,
+ discid,
+ &error);
+ if (error != NULL) {
+ autofetch = TRUE;
+ g_error_free (error);
+ }
}
g_free (config_filename);
g_key_file_free (kv_file);
- return value;
+ return autofetch;
}
@@ -1668,6 +1603,7 @@ auto_fetch_cover_image (GooWindow *window)
return;
}
g_free (filename);
+
if (window->priv->album->asin != NULL)
fetch_cover_image_from_asin (window, window->priv->album->asin);
else if ((window->priv->album->title != NULL) && (window->priv->album->artist != NULL))
@@ -1704,8 +1640,6 @@ player_done_cb (GooPlayer *player,
GError *error,
GooWindow *window)
{
- GooWindowPrivateData *priv = window->priv;
-
debug (DEBUG_INFO, "DONE [%s]\n", get_action_name (action));
switch (action) {
@@ -1715,12 +1649,12 @@ player_done_cb (GooPlayer *player,
goo_window_update_cover (window);
window_update_title (window);
set_current_track_icon (window, NULL);
- if (AutoPlay || (priv->ejected && eel_gconf_get_boolean (PREF_GENERAL_AUTOPLAY, TRUE))) {
+ if (AutoPlay || (window->priv->ejected && eel_gconf_get_boolean (PREF_GENERAL_AUTOPLAY, TRUE))) {
AutoPlay = FALSE;
g_timeout_add (AUTOPLAY_DELAY, autoplay_cb, window);
}
if (goo_player_get_state (player) >= GOO_PLAYER_STATE_NO_DISC)
- priv->ejected = FALSE;
+ window->priv->ejected = FALSE;
break;
case GOO_PLAYER_ACTION_METADATA:
@@ -1732,7 +1666,7 @@ player_done_cb (GooPlayer *player,
break;
case GOO_PLAYER_ACTION_SEEK_SONG:
- goo_window_set_current_track (window, goo_player_get_current_track (priv->player));
+ goo_window_set_current_track (window, goo_player_get_current_track (window->priv->player));
goo_window_select_current_track (window);
set_current_track_icon (window, GTK_STOCK_MEDIA_PLAY);
break;
@@ -1749,14 +1683,12 @@ player_done_cb (GooPlayer *player,
NULL);
if (action == GOO_PLAYER_ACTION_PLAY) {
set_current_track_icon (window, GTK_STOCK_MEDIA_PLAY);
- priv->next_timeout_handle = g_idle_add (next_time_idle, window);
+ window->priv->next_timeout_handle = g_idle_add (next_time_idle, window);
}
else if (action == GOO_PLAYER_ACTION_STOP)
set_current_track_icon (window, GTK_STOCK_MEDIA_STOP);
- else if (action == GOO_PLAYER_ACTION_EJECT) {
- priv->ejected = TRUE;
- goo_player_update (priv->player);
- }
+ else if (action == GOO_PLAYER_ACTION_EJECT)
+ window->priv->ejected = TRUE;
break;
case GOO_PLAYER_ACTION_PAUSE:
@@ -1829,10 +1761,9 @@ file_button_press_cb (GtkWidget *widget,
gpointer data)
{
GooWindow *window = data;
- GooWindowPrivateData *priv = window->priv;
GtkTreeSelection *selection;
- selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->list_view));
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (window->priv->list_view));
if (selection == NULL)
return FALSE;
@@ -1843,11 +1774,11 @@ file_button_press_cb (GtkWidget *widget,
GtkTreePath *path;
GtkTreeIter iter;
- if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (priv->list_view),
+ if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (window->priv->list_view),
event->x, event->y,
- &path, NULL, NULL, NULL)) {
-
- if (! gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->list_store), &iter, path)) {
+ &path, NULL, NULL, NULL))
+ {
+ if (! gtk_tree_model_get_iter (GTK_TREE_MODEL (window->priv->list_store), &iter, path)) {
gtk_tree_path_free (path);
return FALSE;
}
@@ -1857,24 +1788,26 @@ file_button_press_cb (GtkWidget *widget,
gtk_tree_selection_unselect_all (selection);
gtk_tree_selection_select_iter (selection, &iter);
}
-
- } else
+ }
+ else
gtk_tree_selection_unselect_all (selection);
- gtk_menu_popup (GTK_MENU (priv->file_popup_menu),
+ gtk_menu_popup (GTK_MENU (window->priv->file_popup_menu),
NULL, NULL, NULL,
window,
event->button,
event->time);
return TRUE;
-
- } else if ((event->type == GDK_BUTTON_PRESS) && (event->button == 1)) {
+ }
+ else if ((event->type == GDK_BUTTON_PRESS) && (event->button == 1)) {
GtkTreePath *path;
- if (! gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (priv->list_view),
+ if (! gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (window->priv->list_view),
event->x, event->y,
&path, NULL, NULL, NULL))
+ {
gtk_tree_selection_unselect_all (selection);
+ }
}
return FALSE;
@@ -1915,39 +1848,36 @@ static void
sort_column_changed_cb (GtkTreeSortable *sortable,
gpointer user_data)
{
- GooWindow *window = user_data;
- GooWindowPrivateData *priv = window->priv;
- GtkSortType order;
- int column_id;
- GooPlayerState state;
+ GooWindow *window = user_data;
+ GtkSortType order;
+ int column_id;
+ GooPlayerState state;
if (! gtk_tree_sortable_get_sort_column_id (sortable,
&column_id,
&order))
+ {
return;
+ }
- priv->sort_method = get_sort_method_from_column (column_id);
- priv->sort_type = order;
-
- state = goo_player_get_state (priv->player);
+ window->priv->sort_method = get_sort_method_from_column (column_id);
+ window->priv->sort_type = order;
/* Recreate the playlist if not already playing. */
- if ((state != GOO_PLAYER_STATE_PLAYING) && (state != GOO_PLAYER_STATE_PAUSED)) {
- gboolean play_all;
- gboolean shuffle;
-
- play_all = eel_gconf_get_boolean (PREF_PLAYLIST_PLAYALL, TRUE);
- shuffle = eel_gconf_get_boolean (PREF_PLAYLIST_SHUFFLE, FALSE);
- create_playlist (window, play_all, shuffle);
- }
+
+ state = goo_player_get_state (window->priv->player);
+ if ((state != GOO_PLAYER_STATE_PLAYING) && (state != GOO_PLAYER_STATE_PAUSED))
+ create_playlist (window,
+ eel_gconf_get_boolean (PREF_PLAYLIST_PLAYALL, TRUE),
+ eel_gconf_get_boolean (PREF_PLAYLIST_SHUFFLE, FALSE));
}
static void
update_ui_from_expander_state (GooWindow *window)
{
- GooWindowPrivateData *priv = window->priv;
- GtkExpander *expander = GTK_EXPANDER (priv->list_expander);
+
+ GtkExpander *expander = GTK_EXPANDER (window->priv->list_expander);
if (gtk_expander_get_expanded (expander)) {
gtk_expander_set_label (expander, _(HIDE_TRACK_LIST));
@@ -1955,16 +1885,16 @@ update_ui_from_expander_state (GooWindow *window)
gtk_window_resize (GTK_WINDOW (window),
eel_gconf_get_integer (PREF_UI_WINDOW_WIDTH, DEFAULT_WIN_WIDTH),
eel_gconf_get_integer (PREF_UI_WINDOW_HEIGHT, DEFAULT_WIN_HEIGHT));
- gtk_statusbar_set_has_resize_grip (GTK_STATUSBAR (priv->statusbar), TRUE);
- /*gtk_widget_show (priv->list_view->parent);*/
+ gtk_statusbar_set_has_resize_grip (GTK_STATUSBAR (window->priv->statusbar), TRUE);
+ /*gtk_widget_show (window->priv->list_view->parent);*/
gtk_window_set_resizable (GTK_WINDOW (window), TRUE);
}
else {
if (GTK_WIDGET_REALIZED (window))
save_window_size (window);
- gtk_statusbar_set_has_resize_grip (GTK_STATUSBAR (priv->statusbar), FALSE);
+ gtk_statusbar_set_has_resize_grip (GTK_STATUSBAR (window->priv->statusbar), FALSE);
gtk_expander_set_label (expander, _(SHOW_TRACK_LIST));
- /*gtk_widget_hide (priv->list_view->parent);*/
+ /*gtk_widget_hide (window->priv->list_view->parent);*/
gtk_window_set_resizable (GTK_WINDOW (window), FALSE);
}
}
@@ -1979,7 +1909,6 @@ list_expander_expanded_cb (GtkExpander *expander,
eel_gconf_set_boolean (PREF_UI_PLAYLIST, gtk_expander_get_expanded (expander));
}
-
static void
player_info_skip_to_cb (GooPlayerInfo *info,
int seconds,
@@ -2059,20 +1988,21 @@ window_key_press_cb (GtkWidget *widget,
}
+#if 0
static void
tray_object_destroyed (GooWindow *window)
{
- GooWindowPrivateData *priv = window->priv;
- gtk_object_unref (GTK_OBJECT (priv->tray_tips));
- priv->tray_tips = NULL;
- if (priv->tray_popup_menu != NULL) {
- gtk_widget_destroy (priv->file_popup_menu);
- priv->file_popup_menu = NULL;
+ gtk_object_unref (GTK_OBJECT (window->priv->tray_tips));
+ window->priv->tray_tips = NULL;
+
+ if (window->priv->tray_popup_menu != NULL) {
+ gtk_widget_destroy (window->priv->file_popup_menu);
+ window->priv->file_popup_menu = NULL;
}
- priv->tray = NULL;
+ window->priv->tray = NULL;
}
@@ -2098,10 +2028,12 @@ tray_icon_pressed (GtkWidget *widget,
if ((event->keyval == GDK_space)
|| (event->keyval == GDK_KP_Space)
|| (event->keyval == GDK_Return)
- || (event->keyval == GDK_KP_Enter)) {
+ || (event->keyval == GDK_KP_Enter))
+ {
goo_window_toggle_visibility (window);
return TRUE;
- } else
+ }
+ else
return FALSE;
}
@@ -2199,13 +2131,14 @@ tray_icon_expose (GtkWidget *widget,
return FALSE;
}
+#endif
static void
volume_button_changed_cb (GooVolumeToolButton *button,
GooWindow *window)
{
- goo_player_set_volume (window->priv->player, goo_volume_tool_button_get_volume (button));
+ goo_player_set_audio_volume (window->priv->player, goo_volume_tool_button_get_volume (button));
}
@@ -2221,23 +2154,122 @@ goo_window_init (GooWindow *window)
}
+#ifdef ENABLE_MEDIA_KEYS
+
+
static void
-goo_window_construct (GooWindow *window,
- const char *device_path)
-{
- GooWindowPrivateData *priv = window->priv;
- GtkWidget *menubar, *toolbar;
- GtkWidget *scrolled_window;
- GtkWidget *vbox;
- GtkWidget *hbox;
- GtkWidget *expander;
- GtkTreeSelection *selection;
- int i;
- GtkActionGroup *actions;
- GtkUIManager *ui;
- GError *error = NULL;
- char *device;
-
+media_player_key_pressed_cb (DBusGProxy *proxy,
+ const char *application,
+ const char *key,
+ GooWindow *window)
+{
+ if (strcmp ("goobox", application) == 0) {
+ if (strcmp ("Play", key) == 0)
+ goo_window_toggle_play (window);
+ else if (strcmp ("Previous", key) == 0)
+ goo_window_prev (window);
+ else if (strcmp ("Next", key) == 0)
+ goo_window_next (window);
+ else if (strcmp ("Stop", key) == 0)
+ goo_window_stop (window);
+ }
+}
+
+
+static gboolean
+window_focus_in_event_cb (GtkWidget *widget,
+ GdkEventFocus *event,
+ gpointer user_data)
+{
+ GooWindow *window = user_data;
+
+ dbus_g_proxy_call (window->priv->media_keys_proxy,
+ "GrabMediaPlayerKeys",
+ NULL,
+ G_TYPE_STRING, "goobox",
+ G_TYPE_UINT, 0,
+ G_TYPE_INVALID,
+ G_TYPE_INVALID);
+
+ return FALSE;
+}
+
+
+static void
+_goo_window_enable_media_keys (GooWindow *window)
+{
+ DBusGConnection *connection;
+
+ connection = dbus_g_bus_get (DBUS_BUS_SESSION, NULL);
+ if (connection == NULL)
+ return;
+
+ window->priv->media_keys_proxy = dbus_g_proxy_new_for_name_owner (connection,
+ "org.gnome.SettingsDaemon",
+ "/org/gnome/SettingsDaemon/MediaKeys",
+ "org.gnome.SettingsDaemon.MediaKeys",
+ NULL);
+ if (window->priv->media_keys_proxy == NULL)
+ window->priv->media_keys_proxy = dbus_g_proxy_new_for_name_owner (connection,
+ "org.gnome.SettingsDaemon",
+ "/org/gnome/SettingsDaemon",
+ "org.gnome.SettingsDaemon",
+ NULL);
+ dbus_g_connection_unref (connection);
+
+ if (window->priv->media_keys_proxy == NULL)
+ return;
+
+ dbus_g_proxy_call (window->priv->media_keys_proxy,
+ "GrabMediaPlayerKeys",
+ NULL,
+ G_TYPE_STRING, "goobox",
+ G_TYPE_UINT, 0,
+ G_TYPE_INVALID,
+ G_TYPE_INVALID);
+
+ dbus_g_object_register_marshaller (goo_marshal_VOID__STRING_STRING,
+ G_TYPE_NONE,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_INVALID);
+ dbus_g_proxy_add_signal (window->priv->media_keys_proxy,
+ "MediaPlayerKeyPressed",
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_INVALID);
+ dbus_g_proxy_connect_signal (window->priv->media_keys_proxy,
+ "MediaPlayerKeyPressed",
+ G_CALLBACK (media_player_key_pressed_cb),
+ window,
+ NULL);
+
+ window->priv->focus_in_event = g_signal_connect (window,
+ "focus-in-event",
+ G_CALLBACK (window_focus_in_event_cb),
+ window);
+}
+
+
+#endif /* ENABLE_MEDIA_KEYS */
+
+
+static void
+goo_window_construct (GooWindow *window,
+ BraseroDrive *drive)
+{
+
+ GtkWidget *menubar, *toolbar;
+ GtkWidget *scrolled_window;
+ GtkWidget *vbox;
+ GtkWidget *hbox;
+ GtkWidget *expander;
+ GtkTreeSelection *selection;
+ int i;
+ GtkActionGroup *actions;
+ GtkUIManager *ui;
+ GError *error = NULL;
+
g_signal_connect (G_OBJECT (window),
"delete_event",
G_CALLBACK (window_delete_event_cb),
@@ -2257,67 +2289,62 @@ goo_window_construct (GooWindow *window,
/* Create the data */
- if (device_path == NULL)
- device = eel_gconf_get_string (PREF_GENERAL_DEVICE, DEFAULT_DEVICE);
- else
- device = g_strdup (device_path);
- priv->player = goo_player_new (device);
- g_free (device);
+ window->priv->player = goo_player_new (drive);
- g_signal_connect (priv->player,
+ g_signal_connect (window->priv->player,
"start",
G_CALLBACK (player_start_cb),
window);
- g_signal_connect (priv->player,
+ g_signal_connect (window->priv->player,
"done",
G_CALLBACK (player_done_cb),
window);
- g_signal_connect (priv->player,
+ g_signal_connect (window->priv->player,
"state_changed",
G_CALLBACK (player_state_changed_cb),
window);
- priv->playlist = NULL;
+ window->priv->playlist = NULL;
/* Create the widgets. */
- priv->tooltips = gtk_tooltips_new ();
+ window->priv->tooltips = gtk_tooltips_new ();
/* * File list. */
- priv->list_store = gtk_list_store_new (NUMBER_OF_COLUMNS,
- GOO_TYPE_TRACK_INFO,
- G_TYPE_INT,
- GDK_TYPE_PIXBUF,
- G_TYPE_STRING,
- G_TYPE_STRING,
- G_TYPE_STRING);
- priv->list_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (priv->list_store));
-
- gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (priv->list_view), TRUE);
- add_columns (window, GTK_TREE_VIEW (priv->list_view));
- gtk_tree_view_set_enable_search (GTK_TREE_VIEW (priv->list_view), TRUE);
- gtk_tree_view_set_search_column (GTK_TREE_VIEW (priv->list_view), COLUMN_TITLE);
-
- gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (priv->list_store),
+ window->priv->list_store = gtk_list_store_new (NUMBER_OF_COLUMNS,
+ GOO_TYPE_TRACK_INFO,
+ G_TYPE_INT,
+ GDK_TYPE_PIXBUF,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_STRING);
+ window->priv->list_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (window->priv->list_store));
+
+ gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (window->priv->list_view), TRUE);
+ add_columns (window, GTK_TREE_VIEW (window->priv->list_view));
+ gtk_tree_view_set_enable_search (GTK_TREE_VIEW (window->priv->list_view), TRUE);
+ gtk_tree_view_set_search_column (GTK_TREE_VIEW (window->priv->list_view), COLUMN_TITLE);
+
+ gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (window->priv->list_store),
COLUMN_TITLE, title_column_sort_func,
NULL, NULL);
- gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (priv->list_store),
+ gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (window->priv->list_store),
COLUMN_ARTIST, artist_column_sort_func,
NULL, NULL);
- gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (priv->list_store),
+ gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (window->priv->list_store),
COLUMN_TIME, time_column_sort_func,
NULL, NULL);
- priv->sort_method = preferences_get_sort_method ();
- priv->sort_type = preferences_get_sort_type ();
+ window->priv->sort_method = preferences_get_sort_method ();
+ window->priv->sort_type = preferences_get_sort_type ();
- gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (priv->list_store), get_column_from_sort_method (priv->sort_method), priv->sort_type);
+ gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (window->priv->list_store), get_column_from_sort_method (window->priv->sort_method), window->priv->sort_type);
- selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->list_view));
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (window->priv->list_view));
gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
- g_signal_connect (G_OBJECT (priv->list_view),
+ g_signal_connect (G_OBJECT (window->priv->list_view),
"row_activated",
G_CALLBACK (row_activated_cb),
window);
@@ -2325,48 +2352,26 @@ goo_window_construct (GooWindow *window,
"changed",
G_CALLBACK (selection_changed_cb),
window);
- g_signal_connect (G_OBJECT (priv->list_view),
+ g_signal_connect (G_OBJECT (window->priv->list_view),
"button_press_event",
G_CALLBACK (file_button_press_cb),
window);
- g_signal_connect (G_OBJECT (priv->list_store),
+ g_signal_connect (G_OBJECT (window->priv->list_store),
"sort_column_changed",
G_CALLBACK (sort_column_changed_cb),
window);
- /*
- egg_tree_multi_drag_add_drag_support (GTK_TREE_VIEW (window->list_view));
-
- gtk_drag_source_set (window->list_view,
- GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
- target_table, n_targets,
- GDK_ACTION_COPY);
-
- g_signal_connect (G_OBJECT (window->list_view),
- "drag_data_get",
- G_CALLBACK (file_list_drag_data_get),
- window);
- g_signal_connect (G_OBJECT (window->list_view),
- "drag_begin",
- G_CALLBACK (file_list_drag_begin),
- window);
- g_signal_connect (G_OBJECT (window->list_view),
- "drag_end",
- G_CALLBACK (file_list_drag_end),
- window);
- */
-
scrolled_window = gtk_scrolled_window_new (NULL, NULL);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
GTK_POLICY_AUTOMATIC,
GTK_POLICY_AUTOMATIC);
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window), GTK_SHADOW_IN);
- gtk_container_add (GTK_CONTAINER (scrolled_window), priv->list_view);
+ gtk_container_add (GTK_CONTAINER (scrolled_window), window->priv->list_view);
/* Build the menu and the toolbar. */
- priv->actions = actions = gtk_action_group_new ("Actions");
+ window->priv->actions = actions = gtk_action_group_new ("Actions");
gtk_action_group_set_translation_domain (actions, NULL);
gtk_action_group_add_actions (actions,
action_entries,
@@ -2377,7 +2382,7 @@ goo_window_construct (GooWindow *window,
n_action_toggle_entries,
window);
- priv->ui = ui = gtk_ui_manager_new ();
+ window->priv->ui = ui = gtk_ui_manager_new ();
g_signal_connect (ui, "connect_proxy",
G_CALLBACK (connect_proxy_cb), window);
@@ -2388,34 +2393,18 @@ goo_window_construct (GooWindow *window,
gtk_window_add_accel_group (GTK_WINDOW (window),
gtk_ui_manager_get_accel_group (ui));
- if (!gtk_ui_manager_add_ui_from_string (ui, ui_info, -1, &error)) {
+ if (! gtk_ui_manager_add_ui_from_string (ui, ui_info, -1, &error)) {
g_message ("building menus failed: %s", error->message);
g_error_free (error);
}
menubar = gtk_ui_manager_get_widget (ui, "/MenuBar");
gtk_widget_show (menubar);
+ gth_window_attach (GTH_WINDOW (window), menubar, GTH_WINDOW_MENUBAR);
- gnome_app_add_docked (GNOME_APP (window),
- menubar,
- "MenuBar",
- (BONOBO_DOCK_ITEM_BEH_NEVER_VERTICAL
- | BONOBO_DOCK_ITEM_BEH_EXCLUSIVE
- | (eel_gconf_get_boolean (PREF_DESKTOP_MENUBAR_DETACHABLE, TRUE) ? BONOBO_DOCK_ITEM_BEH_NORMAL : BONOBO_DOCK_ITEM_BEH_LOCKED)),
- BONOBO_DOCK_TOP,
- 1, 1, 0);
-
- priv->toolbar = toolbar = gtk_ui_manager_get_widget (ui, "/ToolBar");
+ window->priv->toolbar = toolbar = gtk_ui_manager_get_widget (ui, "/ToolBar");
gtk_toolbar_set_show_arrow (GTK_TOOLBAR (toolbar), TRUE);
-
- gnome_app_add_docked (GNOME_APP (window),
- toolbar,
- "ToolBar",
- (BONOBO_DOCK_ITEM_BEH_NEVER_VERTICAL
- | BONOBO_DOCK_ITEM_BEH_EXCLUSIVE
- | (eel_gconf_get_boolean (PREF_DESKTOP_TOOLBAR_DETACHABLE, TRUE) ? BONOBO_DOCK_ITEM_BEH_NORMAL : BONOBO_DOCK_ITEM_BEH_LOCKED)),
- BONOBO_DOCK_TOP,
- 2, 1, 0);
+ gth_window_attach_toolbar (GTH_WINDOW (window), 0, window->priv->toolbar);
{
GtkAction *action;
@@ -2459,8 +2448,8 @@ goo_window_construct (GooWindow *window,
gtk_size_group_add_widget (size_group, pause_button);
}
- priv->file_popup_menu = gtk_ui_manager_get_widget (ui, "/ListPopupMenu");
- priv->cover_popup_menu = gtk_ui_manager_get_widget (ui, "/CoverPopupMenu");
+ window->priv->file_popup_menu = gtk_ui_manager_get_widget (ui, "/ListPopupMenu");
+ window->priv->cover_popup_menu = gtk_ui_manager_get_widget (ui, "/CoverPopupMenu");
/* Add the volume button to the toolbar. */
@@ -2469,31 +2458,31 @@ goo_window_construct (GooWindow *window,
sep = gtk_separator_tool_item_new ();
gtk_widget_show (GTK_WIDGET (sep));
- gtk_toolbar_insert (GTK_TOOLBAR (priv->toolbar),
+ gtk_toolbar_insert (GTK_TOOLBAR (window->priv->toolbar),
GTK_TOOL_ITEM (sep),
VOLUME_BUTTON_POSITION);
}
- priv->volume_button = (GtkWidget*) goo_volume_tool_button_new ();
- g_signal_connect (priv->volume_button,
+ window->priv->volume_button = (GtkWidget*) goo_volume_tool_button_new ();
+ g_signal_connect (window->priv->volume_button,
"changed",
G_CALLBACK (volume_button_changed_cb),
window);
- gtk_widget_show (GTK_WIDGET (priv->volume_button));
- gtk_tool_item_set_is_important (GTK_TOOL_ITEM (priv->volume_button), FALSE); /*FIXME*/
- gtk_toolbar_insert (GTK_TOOLBAR (priv->toolbar),
- GTK_TOOL_ITEM (priv->volume_button),
+ gtk_widget_show (GTK_WIDGET (window->priv->volume_button));
+ gtk_tool_item_set_is_important (GTK_TOOL_ITEM (window->priv->volume_button), FALSE);
+ gtk_toolbar_insert (GTK_TOOLBAR (window->priv->toolbar),
+ GTK_TOOL_ITEM (window->priv->volume_button),
VOLUME_BUTTON_POSITION + 1);
/* Create the statusbar. */
- priv->statusbar = gtk_statusbar_new ();
- priv->help_message_cid = gtk_statusbar_get_context_id (GTK_STATUSBAR (priv->statusbar), "help_message");
- priv->list_info_cid = gtk_statusbar_get_context_id (GTK_STATUSBAR (priv->statusbar), "list_info");
- priv->progress_cid = gtk_statusbar_get_context_id (GTK_STATUSBAR (priv->statusbar), "progress");
- gnome_app_set_statusbar (GNOME_APP (window), priv->statusbar);
-
- gtk_statusbar_set_has_resize_grip (GTK_STATUSBAR (priv->statusbar), TRUE);
+ window->priv->statusbar = gtk_statusbar_new ();
+ gtk_statusbar_set_has_resize_grip (GTK_STATUSBAR (window->priv->statusbar), TRUE);
+ window->priv->help_message_cid = gtk_statusbar_get_context_id (GTK_STATUSBAR (window->priv->statusbar), "help_message");
+ window->priv->list_info_cid = gtk_statusbar_get_context_id (GTK_STATUSBAR (window->priv->statusbar), "list_info");
+ window->priv->progress_cid = gtk_statusbar_get_context_id (GTK_STATUSBAR (window->priv->statusbar), "progress");
+ gth_window_attach (GTH_WINDOW (window), window->priv->statusbar, GTH_WINDOW_STATUSBAR);
+ gtk_statusbar_set_has_resize_grip (GTK_STATUSBAR (window->priv->statusbar), TRUE);
/**/
@@ -2520,8 +2509,8 @@ goo_window_construct (GooWindow *window,
/**/
- priv->list_expander = expander = gtk_expander_new_with_mnemonic (_(HIDE_TRACK_LIST));
- gtk_container_add (GTK_CONTAINER (priv->list_expander), scrolled_window);
+ window->priv->list_expander = expander = gtk_expander_new_with_mnemonic (_(HIDE_TRACK_LIST));
+ gtk_container_add (GTK_CONTAINER (window->priv->list_expander), scrolled_window);
gtk_expander_set_expanded (GTK_EXPANDER (expander), FALSE /*eel_gconf_get_boolean (PREF_UI_PLAYLIST, TRUE)*/);
g_signal_connect (expander,
"notify::expanded",
@@ -2532,10 +2521,10 @@ goo_window_construct (GooWindow *window,
expander,
TRUE, TRUE, 0);
- gnome_app_set_contents (GNOME_APP (window), vbox);
gtk_widget_show_all (vbox);
+ gth_window_attach_content (GTH_WINDOW (window), 0, vbox);
- gtk_widget_grab_focus (priv->list_view);
+ gtk_widget_grab_focus (window->priv->list_view);
window_sync_ui_with_preferences (window);
@@ -2543,17 +2532,19 @@ goo_window_construct (GooWindow *window,
eel_gconf_get_integer (PREF_UI_WINDOW_WIDTH, DEFAULT_WIN_WIDTH),
eel_gconf_get_integer (PREF_UI_WINDOW_HEIGHT, DEFAULT_WIN_HEIGHT));
- goo_volume_tool_button_set_volume (GOO_VOLUME_TOOL_BUTTON (priv->volume_button),
+ goo_volume_tool_button_set_volume (GOO_VOLUME_TOOL_BUTTON (window->priv->volume_button),
eel_gconf_get_integer (PREF_GENERAL_VOLUME, DEFAULT_VOLUME) / 100.0,
TRUE);
- /* Create the tray icon. */
+ /* The tray icon. */
- priv->tray = GTK_WIDGET (egg_tray_icon_new ("Goobox"));
- priv->tray_box = gtk_event_box_new ();
+#if 0
+ window->priv->tray = GTK_WIDGET (egg_tray_icon_new ("Goobox"));
+ window->priv->tray_box = gtk_event_box_new ();
- priv->info_popup = gtk_window_new (GTK_WINDOW_POPUP);
+ window->priv->info_popup = gtk_window_new (GTK_WINDOW_POPUP);
+ /* FIXME
{
GtkWidget *out_frame;
GtkWidget *info;
@@ -2561,7 +2552,7 @@ goo_window_construct (GooWindow *window,
out_frame = gtk_frame_new (NULL);
gtk_frame_set_shadow_type (GTK_FRAME (out_frame), GTK_SHADOW_OUT);
gtk_widget_show (out_frame);
- gtk_container_add (GTK_CONTAINER (priv->info_popup), out_frame);
+ gtk_container_add (GTK_CONTAINER (window->priv->info_popup), out_frame);
info = goo_player_info_new (window, FALSE);
gtk_container_set_border_width (GTK_CONTAINER (info), 0);
@@ -2576,140 +2567,124 @@ goo_window_construct (GooWindow *window,
window);
gtk_container_add (GTK_CONTAINER (out_frame), info);
}
+ */
- g_signal_connect (G_OBJECT (priv->tray_box),
+ g_signal_connect (G_OBJECT (window->priv->tray_box),
"button_press_event",
G_CALLBACK (tray_icon_clicked),
window);
- g_signal_connect (G_OBJECT (priv->tray_box),
+ g_signal_connect (G_OBJECT (window->priv->tray_box),
"key_press_event",
G_CALLBACK (tray_icon_pressed),
window);
- g_signal_connect (G_OBJECT (priv->tray_box),
+ g_signal_connect (G_OBJECT (window->priv->tray_box),
"enter_notify_event",
G_CALLBACK (tray_icon_enter_notify_event_cb),
window);
- g_signal_connect (G_OBJECT (priv->tray_box),
+ g_signal_connect (G_OBJECT (window->priv->tray_box),
"leave_notify_event",
G_CALLBACK (tray_icon_leave_notify_event_cb),
window);
- g_object_set_data_full (G_OBJECT (priv->tray),
+ g_object_set_data_full (G_OBJECT (window->priv->tray),
"tray-action-data",
window,
(GDestroyNotify) tray_object_destroyed);
- gtk_container_add (GTK_CONTAINER (priv->tray), priv->tray_box);
- priv->tray_icon = gtk_image_new_from_icon_name ("goobox", GTK_ICON_SIZE_BUTTON);
+ gtk_container_add (GTK_CONTAINER (window->priv->tray), window->priv->tray_box);
+ window->priv->tray_icon = gtk_image_new_from_icon_name ("goobox", GTK_ICON_SIZE_BUTTON);
- g_signal_connect (G_OBJECT (priv->tray_icon),
+ g_signal_connect (G_OBJECT (window->priv->tray_icon),
"expose_event",
G_CALLBACK (tray_icon_expose),
window);
- GTK_WIDGET_SET_FLAGS (priv->tray_box, GTK_CAN_FOCUS);
- atk_object_set_name (gtk_widget_get_accessible (priv->tray_box), _("CD Player"));
+ GTK_WIDGET_SET_FLAGS (window->priv->tray_box, GTK_CAN_FOCUS);
+ atk_object_set_name (gtk_widget_get_accessible (window->priv->tray_box), _("CD Player"));
- gtk_container_add (GTK_CONTAINER (priv->tray_box), priv->tray_icon);
- priv->tray_tips = gtk_tooltips_new ();
- gtk_object_ref (GTK_OBJECT (priv->tray_tips));
- gtk_object_sink (GTK_OBJECT (priv->tray_tips));
- /* gtk_tooltips_set_tip (GTK_TOOLTIPS (priv->tray_tips),
- priv->tray_box,
+ gtk_container_add (GTK_CONTAINER (window->priv->tray_box), window->priv->tray_icon);
+ window->priv->tray_tips = gtk_tooltips_new ();
+ gtk_object_ref (GTK_OBJECT (window->priv->tray_tips));
+ gtk_object_sink (GTK_OBJECT (window->priv->tray_tips));
+ /* gtk_tooltips_set_tip (GTK_TOOLTIPS (window->priv->tray_tips),
+ window->priv->tray_box,
_("CD Player"),
NULL); */
- priv->tray_popup_menu = gtk_ui_manager_get_widget (ui, "/TrayPopupMenu");
- gnome_popup_menu_attach (priv->tray_popup_menu, priv->tray_box, NULL);
- gtk_widget_show_all (priv->tray);
+ window->priv->tray_popup_menu = gtk_ui_manager_get_widget (ui, "/TrayPopupMenu");
+ gnome_popup_menu_attach (window->priv->tray_popup_menu, window->priv->tray_box, NULL);
+ gtk_widget_show_all (window->priv->tray);
+#endif
/* Add notification callbacks. */
i = 0;
- priv->cnxn_id[i++] = eel_gconf_notification_add (
+ window->priv->cnxn_id[i++] = eel_gconf_notification_add (
PREF_UI_TOOLBAR,
pref_view_toolbar_changed,
window);
- priv->cnxn_id[i++] = eel_gconf_notification_add (
+ window->priv->cnxn_id[i++] = eel_gconf_notification_add (
PREF_UI_STATUSBAR,
pref_view_statusbar_changed,
window);
- priv->cnxn_id[i++] = eel_gconf_notification_add (
+ window->priv->cnxn_id[i++] = eel_gconf_notification_add (
PREF_UI_PLAYLIST,
pref_view_playlist_changed,
window);
- priv->cnxn_id[i++] = eel_gconf_notification_add (
+ window->priv->cnxn_id[i++] = eel_gconf_notification_add (
PREF_PLAYLIST_PLAYALL,
pref_playlist_playall_changed,
window);
- priv->cnxn_id[i++] = eel_gconf_notification_add (
+ window->priv->cnxn_id[i++] = eel_gconf_notification_add (
PREF_PLAYLIST_SHUFFLE,
pref_playlist_shuffle_changed,
window);
- priv->cnxn_id[i++] = eel_gconf_notification_add (
+ window->priv->cnxn_id[i++] = eel_gconf_notification_add (
PREF_PLAYLIST_REPEAT,
pref_playlist_repeat_changed,
window);
-}
-
-GtkWindow *
-goo_window_new (const char *device)
-{
- GooWindow *window;
- window = (GooWindow*) g_object_new (GOO_TYPE_WINDOW, NULL);
- goo_window_construct (window, device);
+ /* Media keys*/
- window_list = g_list_prepend (window_list, window);
-
- return (GtkWindow*) window;
+#ifdef ENABLE_MEDIA_KEYS
+ _goo_window_enable_media_keys (window);
+#endif /* ENABLE_MEDIA_KEYS */
}
-static void
-goo_window_destroy (GooWindow *window)
+GtkWidget *
+goo_window_new (BraseroDrive *drive)
{
- window_list = g_list_remove (window_list, window);
- gtk_widget_destroy (GTK_WIDGET (window));
- if (window_list == NULL)
- bonobo_main_quit ();
-}
+ GooWindow *window;
+ if (drive == NULL) {
+ char *default_device;
-static gboolean
-check_player_state_cb (gpointer data)
-{
- GooWindow *window = data;
+ default_device = eel_gconf_get_string (PREF_GENERAL_DEVICE, NULL);
+ if (default_device != NULL)
+ drive = main_get_drive_for_device (default_device);
+ if (drive == NULL)
+ drive = main_get_most_likely_drive ();
- g_source_remove (window->priv->check_id);
+ g_free (default_device);
+ }
- if (!goo_player_get_is_busy (window->priv->player))
- goo_window_destroy (window);
- else
- window->priv->check_id = g_timeout_add (PLAYER_CHECK_RATE,
- check_player_state_cb,
- window);
+ g_return_val_if_fail (drive != NULL, NULL);
- return FALSE;
+ window = (GooWindow*) g_object_new (GOO_TYPE_WINDOW, NULL);
+ goo_window_construct (window, drive);
+
+ window_list = g_list_prepend (window_list, window);
+
+ return (GtkWidget *) window;
}
void
goo_window_close (GooWindow *window)
{
- window->priv->exiting = TRUE;
- if (! goo_player_get_is_busy (window->priv->player))
- goo_window_destroy (window);
- else {
- gtk_widget_set_sensitive (GTK_WIDGET (window), FALSE);
-
- if (window->priv->check_id != 0)
- g_source_remove (window->priv->check_id);
- window->priv->check_id = g_timeout_add (PLAYER_CHECK_RATE,
- check_player_state_cb,
- window);
- }
+ gth_window_close (GTH_WINDOW (window));
}
@@ -2742,15 +2717,16 @@ goo_window_set_statusbar_visibility (GooWindow *window,
void
goo_window_play (GooWindow *window)
{
- GooWindowPrivateData *priv = window->priv;
-
if (window->priv->hibernate)
return;
- if (goo_player_get_state (priv->player) == GOO_PLAYER_STATE_PLAYING)
+ if (! goo_player_is_audio_cd (window->priv->player))
+ return;
+
+ if (goo_player_get_state (window->priv->player) == GOO_PLAYER_STATE_PLAYING)
return;
- if (goo_player_get_state (priv->player) != GOO_PLAYER_STATE_PAUSED) {
+ if (goo_player_get_state (window->priv->player) != GOO_PLAYER_STATE_PAUSED) {
gboolean play_all;
gboolean shuffle;
@@ -2758,8 +2734,8 @@ goo_window_play (GooWindow *window)
shuffle = eel_gconf_get_boolean (PREF_PLAYLIST_SHUFFLE, FALSE);
create_playlist (window, play_all, shuffle);
- if (priv->current_track != NULL)
- play_track (window, priv->current_track->number);
+ if (window->priv->current_track != NULL)
+ play_track (window, window->priv->current_track->number);
else if (window->priv->playlist != NULL)
play_next_track_in_playlist (window);
else
@@ -2767,7 +2743,7 @@ goo_window_play (GooWindow *window)
}
else {
set_current_track_icon (window, GTK_STOCK_MEDIA_PLAY);
- goo_player_play (priv->player);
+ goo_player_play (window->priv->player);
}
}
@@ -2794,33 +2770,27 @@ goo_window_play_selected (GooWindow *window)
void
goo_window_stop (GooWindow *window)
{
- GooWindowPrivateData *priv = window->priv;
-
if (window->priv->hibernate)
return;
-
- goo_player_stop (priv->player);
+ if (! goo_player_is_audio_cd (window->priv->player))
+ return;
+ goo_player_stop (window->priv->player);
}
void
goo_window_pause (GooWindow *window)
{
- GooWindowPrivateData *priv = window->priv;
-
if (window->priv->hibernate)
return;
-
- goo_player_pause (priv->player);
+ goo_player_pause (window->priv->player);
}
void
goo_window_toggle_play (GooWindow *window)
{
- GooWindowPrivateData *priv = window->priv;
-
- if (goo_player_get_state (priv->player) != GOO_PLAYER_STATE_PLAYING)
+ if (goo_player_get_state (window->priv->player) != GOO_PLAYER_STATE_PLAYING)
goo_window_play (window);
else
goo_window_pause (window);
@@ -2830,21 +2800,22 @@ goo_window_toggle_play (GooWindow *window)
void
goo_window_next (GooWindow *window)
{
- GooWindowPrivateData *priv = window->priv;
-
if (window->priv->album->n_tracks == 0)
return;
- if (priv->current_track != NULL) {
+ if (! goo_player_is_audio_cd (window->priv->player))
+ return;
+
+ if (window->priv->current_track != NULL) {
if (window->priv->playlist == NULL) {
- int current_track = priv->current_track->number;
+ int current_track = window->priv->current_track->number;
int current_pos;
int new_pos, new_track;
goo_window_stop (window);
current_pos = get_position_from_track_number (window, current_track);
- new_pos = MIN (current_pos + 1, priv->album->n_tracks - 1);
+ new_pos = MIN (current_pos + 1, window->priv->album->n_tracks - 1);
new_track = get_track_number_from_position (window, new_pos);
goo_window_set_current_track (window, new_track);
@@ -2866,6 +2837,9 @@ goo_window_prev (GooWindow *window)
{
int new_pos;
+ if (! goo_player_is_audio_cd (window->priv->player))
+ return;
+
if (window->priv->album->n_tracks == 0)
return;
@@ -2877,9 +2851,11 @@ goo_window_prev (GooWindow *window)
current_track = window->priv->current_track->number;
current_pos = get_position_from_track_number (window, current_track);
+ /* FIXME
if (goo_player_info_get_progress (GOO_PLAYER_INFO (window->priv->info)) * window->priv->current_track->length > 4)
new_pos = current_pos;
else
+ */
new_pos = MAX (current_pos - 1, 0);
}
else
@@ -2895,27 +2871,16 @@ goo_window_eject (GooWindow *window)
{
if (window->priv->hibernate)
return;
-
- if (! goo_player_eject (window->priv->player)) {
- GError *e = goo_player_get_error (window->priv->player);
- _gtk_error_dialog_from_gerror_run (GTK_WINDOW (window),
- _("Could not eject the CD"), &e);
- }
+ goo_player_eject (window->priv->player);
}
void
goo_window_set_device (GooWindow *window,
- const char *device_path)
+ const char *device)
{
- if (! goo_player_set_device (window->priv->player, device_path)) {
- GError *e = goo_player_get_error (window->priv->player);
-
- _gtk_error_dialog_from_gerror_run (GTK_WINDOW (window),
- _("Could not read drive"), &e);
- }
-
- if (device_path == NULL)
+ goo_player_set_device (window->priv->player, device);
+ if (device == NULL)
window_update_sensitivity (window);
}
@@ -2929,9 +2894,7 @@ add_selected_track (GtkTreeModel *model,
GList **list = data;
TrackInfo *track;
- gtk_tree_model_get (model, iter,
- COLUMN_TRACK_INFO, &track,
- -1);
+ gtk_tree_model_get (model, iter, COLUMN_TRACK_INFO, &track, -1);
*list = g_list_prepend (*list, track);
}
@@ -2947,19 +2910,18 @@ GList *
goo_window_get_tracks (GooWindow *window,
gboolean selection)
{
- GooWindowPrivateData *priv = window->priv;
GtkTreeSelection *list_selection;
GList *tracks;
- if (priv->album->tracks == NULL)
+ if (window->priv->album->tracks == NULL)
return NULL;
if (! selection)
- return track_list_dup (priv->album->tracks);
+ return track_list_dup (window->priv->album->tracks);
/* return selected track list */
- list_selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->list_view));
+ list_selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (window->priv->list_view));
if (list_selection == NULL)
return NULL;
@@ -2978,34 +2940,23 @@ goo_window_get_player (GooWindow *window)
void
-goo_window_set_cover_image (GooWindow *window,
- const char *filename)
+goo_window_set_cover_image_from_pixbuf (GooWindow *window,
+ GdkPixbuf *image)
{
- GdkPixbuf *image;
GError *error = NULL;
GdkPixbuf *frame;
char *cover_filename;
- if (window->priv->hibernate)
+ if (image == NULL)
return;
- goo_window_set_current_cd_autofetch (window, FALSE);
-
- image = gdk_pixbuf_new_from_file (filename, &error);
- if (image == NULL) {
- _gtk_error_dialog_from_gerror_run (GTK_WINDOW (window),
- _("Could not load image"),
- &error);
- return;
- }
-
frame = gdk_pixbuf_new (gdk_pixbuf_get_colorspace (image),
gdk_pixbuf_get_has_alpha (image),
gdk_pixbuf_get_bits_per_sample (image),
gdk_pixbuf_get_width (image) + 2,
gdk_pixbuf_get_height (image) + 2);
gdk_pixbuf_fill (frame, 0x00000000);
- gdk_pixbuf_copy_area (image,
+ gdk_pixbuf_copy_area (image,
0, 0,
gdk_pixbuf_get_width (image),
gdk_pixbuf_get_height (image),
@@ -3014,16 +2965,70 @@ goo_window_set_cover_image (GooWindow *window,
cover_filename = goo_window_get_cover_filename (window);
debug (DEBUG_INFO, "SAVE IMAGE %s\n", cover_filename);
-
+
if (! gdk_pixbuf_save (frame, cover_filename, "png", &error, NULL))
_gtk_error_dialog_from_gerror_run (GTK_WINDOW (window),
_("Could not save cover image"),
&error);
+ else {
+ goo_window_set_current_cd_autofetch (window, FALSE);
+ goo_window_update_cover (window);
+ }
+
g_free (cover_filename);
g_object_unref (frame);
+}
+
+
+void
+goo_window_set_cover_image (GooWindow *window,
+ const char *filename)
+{
+ GdkPixbuf *image;
+ GError *error = NULL;
+
+ if (window->priv->hibernate)
+ return;
+
+ image = gdk_pixbuf_new_from_file (filename, &error);
+ if (image == NULL) {
+ _gtk_error_dialog_from_gerror_run (GTK_WINDOW (window),
+ _("Could not load image"),
+ &error);
+ return;
+ }
+ goo_window_set_cover_image_from_pixbuf (window, image);
+
g_object_unref (image);
+}
+
+
+void
+goo_window_set_cover_image_from_data (GooWindow *window,
+ void *buffer,
+ gsize count)
+{
+ GInputStream *stream;
+ GdkPixbuf *image;
+ GError *error = NULL;
+
+ if (window->priv->hibernate)
+ return;
- goo_window_update_cover (window);
+ stream = g_memory_input_stream_new_from_data (buffer, count, NULL);
+ image = gdk_pixbuf_new_from_stream (stream, NULL, &error);
+ if (image == NULL) {
+ _gtk_error_dialog_from_gerror_run (GTK_WINDOW (window),
+ _("Could not load image"),
+ &error);
+ g_object_unref (stream);
+ return;
+ }
+
+ goo_window_set_cover_image_from_pixbuf (window, image);
+
+ g_object_unref (image);
+ g_object_unref (stream);
}
@@ -3142,9 +3147,13 @@ goo_window_search_cover_on_internet (GooWindow *window)
debug (DEBUG_INFO, "SEARCH ON INTERNET\n");
if ((window->priv->album->title == NULL) || (window->priv->album->artist == NULL)) {
- _gtk_error_dialog_run (GTK_WINDOW (window),
- _("Could not search for a cover on Internet"),
- _("You have to enter the artist and album names in order to find the album cover."));
+ _gtk_message_dialog_new (GTK_WINDOW (window),
+ GTK_DIALOG_MODAL,
+ GTK_STOCK_DIALOG_ERROR,
+ _("Could not search for a cover on Internet"),
+ _("You have to enter the artist and album names in order to find the album cover."),
+ GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
+ NULL);
return;
}
@@ -3155,7 +3164,8 @@ goo_window_search_cover_on_internet (GooWindow *window)
void
goo_window_remove_cover (GooWindow *window)
{
- char *cover_filename;
+ char *cover_filename;
+ GFile *file;
if (window->priv->hibernate)
return;
@@ -3166,7 +3176,9 @@ goo_window_remove_cover (GooWindow *window)
if (cover_filename == NULL)
return;
- gnome_vfs_unlink (cover_filename);
+ file = g_file_new_for_path (cover_filename);
+ g_file_delete (file, NULL, NULL);
+
g_free (cover_filename);
goo_window_update_cover (window);
@@ -3207,7 +3219,7 @@ goo_window_toggle_visibility (GooWindow *window)
double
-goo_window_get_volume (GooWindow *window)
+goo_window_get_volume (GooWindow *window)
{
GooVolumeToolButton *volume_button;
@@ -3217,8 +3229,8 @@ goo_window_get_volume (GooWindow *window)
void
-goo_window_set_volume (GooWindow *window,
- double value)
+goo_window_set_volume (GooWindow *window,
+ double value)
{
GooVolumeToolButton *volume_button;
@@ -3231,8 +3243,8 @@ goo_window_set_volume (GooWindow *window,
void
-goo_window_set_hibernate (GooWindow *window,
- gboolean hibernate)
+goo_window_set_hibernate (GooWindow *window,
+ gboolean hibernate)
{
window->priv->hibernate = hibernate;
diff --git a/src/goo-window.h b/src/goo-window.h
index f0021af..f45e9c6 100644
--- a/src/goo-window.h
+++ b/src/goo-window.h
@@ -23,8 +23,11 @@
#ifndef GOO_WINDOW_H
#define GOO_WINDOW_H
-#include <libgnomeui/gnome-app.h>
+#include <gtk/gtk.h>
+#include <brasero/brasero-drive.h>
+#include "album-info.h"
#include "goo-player.h"
+#include "gth-window.h"
#define GOO_TYPE_WINDOW (goo_window_get_type ())
#define GOO_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GOO_TYPE_WINDOW, GooWindow))
@@ -33,21 +36,21 @@
#define GOO_IS_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GOO_TYPE_WINDOW))
#define GOO_WINDOW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GOO_TYPE_WINDOW, GooWindowClass))
-typedef struct _GooWindow GooWindow;
-typedef struct _GooWindowClass GooWindowClass;
-typedef struct _GooWindowPrivateData GooWindowPrivateData;
+typedef struct _GooWindow GooWindow;
+typedef struct _GooWindowClass GooWindowClass;
+typedef struct _GooWindowPrivate GooWindowPrivate;
struct _GooWindow
{
- GnomeApp __parent;
- GtkWidget *preferences_dialog;
- GtkWidget *properties_dialog;
- GooWindowPrivateData *priv;
+ GthWindow __parent;
+ GtkWidget *preferences_dialog;
+ GtkWidget *properties_dialog;
+ GooWindowPrivate *priv;
};
struct _GooWindowClass
{
- GnomeAppClass __parent_class;
+ GthWindowClass __parent_class;
/*<signals>*/
@@ -55,43 +58,48 @@ struct _GooWindowClass
};
GType goo_window_get_type (void);
-GtkWindow * goo_window_new (const char *device);
-void goo_window_close (GooWindow *window);
-void goo_window_set_toolbar_visibility (GooWindow *window,
- gboolean visible);
-void goo_window_set_statusbar_visibility (GooWindow *window,
- gboolean visible);
-void goo_window_update (GooWindow *window);
-void goo_window_play (GooWindow *window);
-void goo_window_play_selected (GooWindow *window);
-void goo_window_toggle_play (GooWindow *window);
-void goo_window_stop (GooWindow *window);
-void goo_window_pause (GooWindow *window);
-void goo_window_prev (GooWindow *window);
-void goo_window_next (GooWindow *window);
-void goo_window_eject (GooWindow *window);
-void goo_window_set_device (GooWindow *window,
- const char *device);
-AlbumInfo * goo_window_get_album (GooWindow *window);
-GList * goo_window_get_tracks (GooWindow *window,
- gboolean selection);
-GooPlayer * goo_window_get_player (GooWindow *window);
-void goo_window_update_cover (GooWindow *window);
-void goo_window_set_cover_image (GooWindow *window,
- const char *filename);
-char * goo_window_get_cover_filename (GooWindow *window);
-void goo_window_pick_cover_from_disk (GooWindow *window);
-void goo_window_search_cover_on_internet (GooWindow *window);
-void goo_window_remove_cover (GooWindow *window);
-void goo_window_toggle_visibility (GooWindow *window);
-double goo_window_get_volume (GooWindow *window);
-void goo_window_set_volume (GooWindow *window,
- double value);
-void goo_window_set_hibernate (GooWindow *window,
- gboolean hibernate);
-void goo_window_set_current_cd_autofetch (GooWindow *window,
- gboolean autofetch);
-gboolean goo_window_get_current_cd_autofetch (GooWindow *window);
-GtkWidget * goo_window_get_tray_icon (GooWindow *window);
+GtkWidget * goo_window_new (BraseroDrive *drive);
+void goo_window_close (GooWindow *window);
+void goo_window_set_toolbar_visibility (GooWindow *window,
+ gboolean visible);
+void goo_window_set_statusbar_visibility (GooWindow *window,
+ gboolean visible);
+void goo_window_update (GooWindow *window);
+void goo_window_play (GooWindow *window);
+void goo_window_play_selected (GooWindow *window);
+void goo_window_toggle_play (GooWindow *window);
+void goo_window_stop (GooWindow *window);
+void goo_window_pause (GooWindow *window);
+void goo_window_prev (GooWindow *window);
+void goo_window_next (GooWindow *window);
+void goo_window_eject (GooWindow *window);
+void goo_window_set_device (GooWindow *window,
+ const char *device);
+AlbumInfo * goo_window_get_album (GooWindow *window);
+GList * goo_window_get_tracks (GooWindow *window,
+ gboolean selection);
+GooPlayer * goo_window_get_player (GooWindow *window);
+void goo_window_update_cover (GooWindow *window);
+void goo_window_set_cover_image_from_pixbuf (GooWindow *window,
+ GdkPixbuf *image);
+void goo_window_set_cover_image (GooWindow *window,
+ const char *filename);
+void goo_window_set_cover_image_from_data (GooWindow *window,
+ void *buffer,
+ gsize count);
+char * goo_window_get_cover_filename (GooWindow *window);
+void goo_window_pick_cover_from_disk (GooWindow *window);
+void goo_window_search_cover_on_internet (GooWindow *window);
+void goo_window_remove_cover (GooWindow *window);
+void goo_window_toggle_visibility (GooWindow *window);
+double goo_window_get_volume (GooWindow *window);
+void goo_window_set_volume (GooWindow *window,
+ double value);
+void goo_window_set_hibernate (GooWindow *window,
+ gboolean hibernate);
+void goo_window_set_current_cd_autofetch (GooWindow *window,
+ gboolean autofetch);
+gboolean goo_window_get_current_cd_autofetch (GooWindow *window);
+GtkWidget * goo_window_get_tray_icon (GooWindow *window);
#endif /* GOO_WINDOW_H */
diff --git a/src/gth-user-dir.c b/src/gth-user-dir.c
new file mode 100644
index 0000000..ad1e3d3
--- /dev/null
+++ b/src/gth-user-dir.c
@@ -0,0 +1,109 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * GThumb
+ *
+ * Copyright (C) 2008 Free Software Foundation, Inc.
+ *
+ * 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 Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <glib/gi18n.h>
+#include <glib.h>
+#include "gth-user-dir.h"
+
+
+static char *
+gth_user_dir_get_file_va_list (GthDir dir_type,
+ const char *first_element,
+ va_list var_args)
+{
+ const char *user_dir;
+ char *config_dir;
+ GString *path;
+ const char *element;
+ char *filename;
+
+ switch (dir_type) {
+ case GTH_DIR_CONFIG:
+ user_dir = g_get_user_config_dir ();
+ break;
+ case GTH_DIR_CACHE:
+ user_dir = g_get_user_cache_dir ();
+ break;
+ case GTH_DIR_DATA:
+ user_dir = g_get_user_data_dir ();
+ break;
+ }
+
+ config_dir = g_build_path (G_DIR_SEPARATOR_S,
+ user_dir,
+ NULL);
+ path = g_string_new (config_dir);
+
+ element = first_element;
+ while (element != NULL) {
+ if (element[0] != '\0') {
+ g_string_append (path, G_DIR_SEPARATOR_S);
+ g_string_append (path, element);
+ }
+ element = va_arg (var_args, const char *);
+ }
+
+ filename = path->str;
+
+ g_string_free (path, FALSE);
+ g_free (config_dir);
+
+ return filename;
+}
+
+
+char *
+gth_user_dir_get_file (GthDir dir_type,
+ const char *first_element,
+ ...)
+{
+ va_list var_args;
+ char *filename;
+
+ va_start (var_args, first_element);
+ filename = gth_user_dir_get_file_va_list (dir_type, first_element, var_args);
+ va_end (var_args);
+
+ return filename;
+}
+
+
+void
+gth_user_dir_make_dir_for_file (GthDir dir_type,
+ const char *first_element,
+ ...)
+{
+ va_list var_args;
+ char *config_file;
+ char *config_dir;
+
+ va_start (var_args, first_element);
+ config_file = gth_user_dir_get_file_va_list (dir_type, first_element, var_args);
+ va_end (var_args);
+
+ config_dir = g_path_get_dirname (config_file);
+ g_mkdir_with_parents (config_dir, 0700);
+
+ g_free (config_dir);
+ g_free (config_file);
+}
diff --git a/src/gth-user-dir.h b/src/gth-user-dir.h
new file mode 100644
index 0000000..2308c1b
--- /dev/null
+++ b/src/gth-user-dir.h
@@ -0,0 +1,45 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * GThumb
+ *
+ * Copyright (C) 2008 Free Software Foundation, Inc.
+ *
+ * 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 Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef GTH_USER_DIR_H
+#define GTH_USER_DIR_H
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+typedef enum {
+ GTH_DIR_CONFIG,
+ GTH_DIR_CACHE,
+ GTH_DIR_DATA
+} GthDir;
+
+char * gth_user_dir_get_file (GthDir dir_type,
+ const char *first_element,
+ ...);
+void gth_user_dir_make_dir_for_file (GthDir dir_type,
+ const char *first_element,
+ ...);
+
+G_END_DECLS
+
+#endif /* GTH_USER_DIR_H */
diff --git a/src/gth-window.c b/src/gth-window.c
new file mode 100644
index 0000000..458aac4
--- /dev/null
+++ b/src/gth-window.c
@@ -0,0 +1,394 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * GThumb
+ *
+ * Copyright (C) 2005-2008 Free Software Foundation, Inc.
+ *
+ * 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 Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <gtk/gtk.h>
+#include "gth-window.h"
+#include "gtk-utils.h"
+
+
+#define GTH_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GTH_TYPE_WINDOW, GthWindowPrivate))
+
+enum {
+ GTH_WINDOW_DUMMY_PROPERTY,
+ GTH_WINDOW_N_PAGES
+};
+
+
+static GtkWindowClass *parent_class = NULL;
+static GList *window_list = NULL;
+
+
+struct _GthWindowPrivate {
+ int n_pages;
+ int current_page;
+ GtkWidget *table;
+ GtkWidget *notebook;
+ GtkWidget *menubar;
+ GtkWidget *toolbar;
+ GtkWidget *statusbar;
+ GtkWidget **toolbars;
+ GtkWidget **contents;
+};
+
+
+static void
+gth_window_set_n_pages (GthWindow *self,
+ int n_pages)
+{
+ int i;
+
+ self->priv->n_pages = n_pages;
+
+ self->priv->table = gtk_table_new (4, 1, FALSE);
+ gtk_widget_show (self->priv->table);
+ gtk_container_add (GTK_CONTAINER (self), self->priv->table);
+
+ self->priv->notebook = gtk_notebook_new ();
+ gtk_notebook_set_show_tabs (GTK_NOTEBOOK (self->priv->notebook), FALSE);
+ gtk_notebook_set_show_border (GTK_NOTEBOOK (self->priv->notebook), FALSE);
+ gtk_widget_show (self->priv->notebook);
+ gtk_table_attach (GTK_TABLE (self->priv->table),
+ self->priv->notebook,
+ 0, 1,
+ 2, 3,
+ GTK_EXPAND | GTK_FILL,
+ GTK_EXPAND | GTK_FILL,
+ 0, 0);
+
+ self->priv->toolbars = g_new0 (GtkWidget *, n_pages);
+ self->priv->contents = g_new0 (GtkWidget *, n_pages);
+
+ for (i = 0; i < n_pages; i++) {
+ GtkWidget *page;
+
+ page = gtk_vbox_new (FALSE, 0);
+ gtk_widget_show (page);
+ gtk_notebook_append_page (GTK_NOTEBOOK (self->priv->notebook),
+ page,
+ NULL);
+
+ self->priv->toolbars[i] = gtk_hbox_new (FALSE, 0);
+ gtk_widget_show (self->priv->toolbars[i]);
+ gtk_box_pack_start (GTK_BOX (page), self->priv->toolbars[i], FALSE, FALSE, 0);
+
+ self->priv->contents[i] = gtk_hbox_new (FALSE, 0);
+ gtk_widget_show (self->priv->contents[i]);
+ gtk_box_pack_start (GTK_BOX (page), self->priv->contents[i], TRUE, TRUE, 0);
+ }
+}
+
+
+static void
+gth_window_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GthWindow *self;
+
+ self = GTH_WINDOW (object);
+
+ switch (property_id) {
+ case GTH_WINDOW_N_PAGES:
+ gth_window_set_n_pages (self, g_value_get_int (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+
+static void
+gth_window_finalize (GObject *object)
+{
+ GthWindow *window = GTH_WINDOW (object);
+
+ g_free (window->priv->toolbars);
+ g_free (window->priv->contents);
+
+ window_list = g_list_remove (window_list, window);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+
+ if (window_list == NULL)
+ gtk_main_quit ();
+}
+
+
+static gboolean
+gth_window_delete_event (GtkWidget *widget,
+ GdkEventAny *event)
+{
+ gth_window_close ((GthWindow*) widget);
+ return TRUE;
+}
+
+
+static void
+gth_window_real_close (GthWindow *window)
+{
+ /* virtual */
+}
+
+
+static void
+gth_window_real_set_current_page (GthWindow *window,
+ int page)
+{
+ if (window->priv->current_page == page)
+ return;
+ window->priv->current_page = page;
+ gtk_notebook_set_current_page (GTK_NOTEBOOK (window->priv->notebook), page);
+}
+
+
+static void
+gth_window_class_init (GthWindowClass *klass)
+{
+ GObjectClass *gobject_class;
+ GtkWidgetClass *widget_class;
+
+ parent_class = g_type_class_peek_parent (klass);
+ g_type_class_add_private (klass, sizeof (GthWindowPrivate));
+
+ gobject_class = (GObjectClass*) klass;
+ gobject_class->set_property = gth_window_set_property;
+ gobject_class->finalize = gth_window_finalize;
+
+ widget_class = (GtkWidgetClass*) klass;
+ widget_class->delete_event = gth_window_delete_event;
+
+ klass->close = gth_window_real_close;
+ klass->set_current_page = gth_window_real_set_current_page;
+
+ g_object_class_install_property (G_OBJECT_CLASS (klass),
+ GTH_WINDOW_N_PAGES,
+ g_param_spec_int ("n-pages",
+ "n-pages",
+ "n-pages",
+ 0,
+ G_MAXINT,
+ 1,
+ G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
+}
+
+
+static void
+gth_window_init (GthWindow *window)
+{
+ window->priv = GTH_WINDOW_GET_PRIVATE (window);
+ window->priv->table = NULL;
+ window->priv->contents = NULL;
+ window->priv->n_pages = 0;
+ window->priv->current_page = -1;
+ window->priv->menubar = NULL;
+ window->priv->toolbar = NULL;
+ window->priv->statusbar = NULL;
+
+ window_list = g_list_prepend (window_list, window);
+}
+
+
+GType
+gth_window_get_type (void)
+{
+ static GType type = 0;
+
+ if (! type) {
+ GTypeInfo type_info = {
+ sizeof (GthWindowClass),
+ NULL,
+ NULL,
+ (GClassInitFunc) gth_window_class_init,
+ NULL,
+ NULL,
+ sizeof (GthWindow),
+ 0,
+ (GInstanceInitFunc) gth_window_init
+ };
+
+ type = g_type_register_static (GTK_TYPE_WINDOW,
+ "GthWindow",
+ &type_info,
+ 0);
+ }
+
+ return type;
+}
+
+
+void
+gth_window_close (GthWindow *window)
+{
+ GTH_WINDOW_GET_CLASS (window)->close (window);
+}
+
+
+void
+gth_window_attach (GthWindow *window,
+ GtkWidget *child,
+ GthWindowArea area)
+{
+ int position;
+
+ g_return_if_fail (window != NULL);
+ g_return_if_fail (GTH_IS_WINDOW (window));
+ g_return_if_fail (child != NULL);
+ g_return_if_fail (GTK_IS_WIDGET (child));
+
+ switch (area) {
+ case GTH_WINDOW_MENUBAR:
+ window->priv->menubar = child;
+ position = 0;
+ break;
+ case GTH_WINDOW_TOOLBAR:
+ window->priv->toolbar = child;
+ position = 1;
+ break;
+ case GTH_WINDOW_STATUSBAR:
+ window->priv->statusbar = child;
+ position = 3;
+ break;
+ default:
+ return;
+ }
+
+ gtk_table_attach (GTK_TABLE (window->priv->table),
+ child,
+ 0, 1,
+ position, position + 1,
+ GTK_EXPAND | GTK_FILL,
+ GTK_FILL,
+ 0, 0);
+}
+
+
+void
+gth_window_attach_toolbar (GthWindow *window,
+ int page,
+ GtkWidget *child)
+{
+ g_return_if_fail (window != NULL);
+ g_return_if_fail (GTH_IS_WINDOW (window));
+ g_return_if_fail (page >= 0 && page < window->priv->n_pages);
+ g_return_if_fail (child != NULL);
+ g_return_if_fail (GTK_IS_WIDGET (child));
+
+ _gtk_container_remove_children (GTK_CONTAINER (window->priv->toolbars[page]), NULL, NULL);
+ gtk_container_add (GTK_CONTAINER (window->priv->toolbars[page]), child);
+}
+
+
+void
+gth_window_attach_content (GthWindow *window,
+ int page,
+ GtkWidget *child)
+{
+ g_return_if_fail (window != NULL);
+ g_return_if_fail (GTH_IS_WINDOW (window));
+ g_return_if_fail (page >= 0 && page < window->priv->n_pages);
+ g_return_if_fail (child != NULL);
+ g_return_if_fail (GTK_IS_WIDGET (child));
+
+ _gtk_container_remove_children (GTK_CONTAINER (window->priv->contents[page]), NULL, NULL);
+ gtk_container_add (GTK_CONTAINER (window->priv->contents[page]), child);
+}
+
+
+void
+gth_window_set_current_page (GthWindow *window,
+ int page)
+{
+ GTH_WINDOW_GET_CLASS (window)->set_current_page (window, page);
+}
+
+
+int
+gth_window_get_current_page (GthWindow *window)
+{
+ return window->priv->current_page;
+}
+
+
+static void
+hide_widget (GtkWidget *widget)
+{
+ if (widget != NULL)
+ gtk_widget_hide (widget);
+}
+
+
+static void
+show_widget (GtkWidget *widget)
+{
+ if (widget != NULL)
+ gtk_widget_show (widget);
+}
+
+
+void
+gth_window_show_only_content (GthWindow *window,
+ gboolean only_content)
+{
+ int i;
+
+ if (only_content) {
+ for (i = 0; i < window->priv->n_pages; i++)
+ hide_widget (window->priv->toolbars[i]);
+ hide_widget (window->priv->menubar);
+ hide_widget (window->priv->toolbar);
+ hide_widget (window->priv->statusbar);
+ }
+ else {
+ for (i = 0; i < window->priv->n_pages; i++)
+ show_widget (window->priv->toolbars[i]);
+ show_widget (window->priv->menubar);
+ show_widget (window->priv->toolbar);
+ show_widget (window->priv->statusbar);
+ }
+}
+
+
+int
+gth_window_get_n_windows (void)
+{
+ return g_list_length (window_list);
+}
+
+
+GList *
+gth_window_get_window_list (void)
+{
+ return window_list;
+}
+
+
+GtkWidget *
+gth_window_get_current_window (void)
+{
+ if ((window_list == NULL) || (g_list_length (window_list) > 1))
+ return NULL;
+ else
+ return (GtkWidget *) window_list->data;
+}
diff --git a/src/gth-window.h b/src/gth-window.h
new file mode 100644
index 0000000..24b3839
--- /dev/null
+++ b/src/gth-window.h
@@ -0,0 +1,89 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * GThumb
+ *
+ * Copyright (C) 2005-2008 Free Software Foundation, Inc.
+ *
+ * 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 Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef GTH_WINDOW_H
+#define GTH_WINDOW_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+typedef enum { /*< skip >*/
+ GTH_WINDOW_MENUBAR,
+ GTH_WINDOW_TOOLBAR,
+ GTH_WINDOW_STATUSBAR,
+} GthWindowArea;
+
+#define GTH_TYPE_WINDOW (gth_window_get_type ())
+#define GTH_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTH_TYPE_WINDOW, GthWindow))
+#define GTH_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTH_TYPE_WINDOW, GthWindowClass))
+#define GTH_IS_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTH_TYPE_WINDOW))
+#define GTH_IS_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTH_TYPE_WINDOW))
+#define GTH_WINDOW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GTH_TYPE_WINDOW, GthWindowClass))
+
+typedef struct _GthWindow GthWindow;
+typedef struct _GthWindowClass GthWindowClass;
+typedef struct _GthWindowPrivate GthWindowPrivate;
+
+struct _GthWindow
+{
+ GtkWindow __parent;
+ GthWindowPrivate *priv;
+};
+
+struct _GthWindowClass
+{
+ GtkWindowClass __parent_class;
+
+ /*< virtual functions >*/
+
+ void (*set_current_page) (GthWindow *window,
+ int page);
+ void (*close) (GthWindow *window);
+};
+
+GType gth_window_get_type (void);
+void gth_window_close (GthWindow *window);
+void gth_window_attach (GthWindow *window,
+ GtkWidget *child,
+ GthWindowArea area);
+void gth_window_attach_toolbar (GthWindow *window,
+ int page,
+ GtkWidget *child);
+void gth_window_attach_content (GthWindow *window,
+ int page,
+ GtkWidget *child);
+void gth_window_set_current_page (GthWindow *window,
+ int page);
+int gth_window_get_current_page (GthWindow *window);
+void gth_window_show_only_content (GthWindow *window,
+ gboolean only_content);
+
+/**/
+
+int gth_window_get_n_windows (void);
+GList * gth_window_get_window_list (void);
+GtkWidget * gth_window_get_current_window (void);
+
+G_END_DECLS
+
+#endif /* GTH_WINDOW_H */
diff --git a/src/gtk-file-chooser-preview.c b/src/gtk-file-chooser-preview.c
index a49ea44..7a2e8e9 100644
--- a/src/gtk-file-chooser-preview.c
+++ b/src/gtk-file-chooser-preview.c
@@ -23,32 +23,24 @@
*/
#include <config.h>
-#include <libintl.h>
+#include <glib/gi18n.h>
#include <gtk/gtk.h>
-#include <libgnomeui/gnome-thumbnail.h>
-#include <libgnomevfs/gnome-vfs.h>
-#include <libgnomevfs/gnome-vfs-mime.h>
-#include <libgnomevfs/gnome-vfs-mime-utils.h>
+#include "glib-utils.h"
+#include "gnome-desktop-thumbnail.h"
#include "gtk-file-chooser-preview.h"
-#define _(String) gettext (String)
-
-#ifdef gettext_noop
-# define N_(String) gettext_noop (String)
-#else
-# define N_(String) (String)
-#endif
#define MIN_WIDTH 150
#define PREVIEW_SIZE 120
struct _GtkFileChooserPreviewPrivateData {
- char *uri;
- GnomeThumbnailFactory *thumb_factory;
- GtkWidget *image;
- GtkWidget *image_info;
- GdkWindow *bin_window;
+ char *uri;
+ GnomeDesktopThumbnailFactory
+ *thumb_factory;
+ GtkWidget *image;
+ GtkWidget *image_info;
+ GdkWindow *bin_window;
};
@@ -138,8 +130,8 @@ gtk_file_chooser_preview_init (GtkFileChooserPreview *preview)
preview->priv = g_new0 (GtkFileChooserPreviewPrivateData, 1);
priv = preview->priv;
- priv->thumb_factory = gnome_thumbnail_factory_new (GNOME_THUMBNAIL_SIZE_NORMAL);
- priv->uri = NULL;
+ preview->priv->thumb_factory = gnome_desktop_thumbnail_factory_new (GNOME_DESKTOP_THUMBNAIL_SIZE_NORMAL);
+ preview->priv->uri = NULL;
}
@@ -153,13 +145,8 @@ gtk_file_chooser_preview_finalize (GObject *object)
preview = GTK_FILE_CHOOSER_PREVIEW (object);
if (preview->priv != NULL) {
- GtkFileChooserPreviewPrivateData *priv = preview->priv;
-
- if (priv->thumb_factory != NULL)
- g_object_unref (priv->thumb_factory);
-
- g_free (priv->uri);
- priv->uri = NULL;
+ _g_object_unref (preview->priv->thumb_factory);
+ g_free (preview->priv->uri);
g_free (preview->priv);
preview->priv = NULL;
@@ -181,7 +168,6 @@ set_void_preview (GtkFileChooserPreview *preview)
static void
gtk_file_chooser_preview_construct (GtkFileChooserPreview *preview)
{
- GtkFileChooserPreviewPrivateData *priv = preview->priv;
GtkWidget *event_box;
GtkWidget *vbox;
GtkWidget *button;
@@ -223,15 +209,15 @@ gtk_file_chooser_preview_construct (GtkFileChooserPreview *preview)
gtk_widget_show (vbox2);
gtk_box_pack_start (GTK_BOX (vbox), vbox2, TRUE, FALSE, 0);
- priv->image = gtk_image_new ();
- gtk_widget_show (priv->image);
- gtk_box_pack_start (GTK_BOX (vbox2), priv->image, FALSE, FALSE, 0);
+ preview->priv->image = gtk_image_new ();
+ gtk_widget_show (preview->priv->image);
+ gtk_box_pack_start (GTK_BOX (vbox2), preview->priv->image, FALSE, FALSE, 0);
- priv->image_info = gtk_label_new (NULL);
- gtk_label_set_justify (GTK_LABEL (priv->image_info), GTK_JUSTIFY_CENTER);
- gtk_misc_set_alignment (GTK_MISC (priv->image_info), 0.5, 0.5);
- gtk_widget_hide (priv->image_info);
- gtk_box_pack_start (GTK_BOX (vbox2), priv->image_info, FALSE, FALSE, 0);
+ preview->priv->image_info = gtk_label_new (NULL);
+ gtk_label_set_justify (GTK_LABEL (preview->priv->image_info), GTK_JUSTIFY_CENTER);
+ gtk_misc_set_alignment (GTK_MISC (preview->priv->image_info), 0.5, 0.5);
+ gtk_widget_hide (preview->priv->image_info);
+ gtk_box_pack_start (GTK_BOX (vbox2), preview->priv->image_info, FALSE, FALSE, 0);
set_void_preview (preview);
}
@@ -250,70 +236,46 @@ gtk_file_chooser_preview_new (void)
}
-static GnomeVFSFileInfo*
-vfs_get_file_info (const gchar *uri)
-{
- GnomeVFSFileInfo *info;
- GnomeVFSResult result;
-
- if (uri == NULL)
- return NULL;
-
- info = gnome_vfs_file_info_new ();
- result = gnome_vfs_get_file_info (uri,
- info,
- (GNOME_VFS_FILE_INFO_DEFAULT | GNOME_VFS_FILE_INFO_FOLLOW_LINKS));
-
- if (result != GNOME_VFS_OK) {
- gnome_vfs_file_info_unref (info);
- info = NULL;
- }
-
- return info;
-}
-
-
void
gtk_file_chooser_preview_set_uri (GtkFileChooserPreview *preview,
const char *uri)
{
- GtkFileChooserPreviewPrivateData *priv = preview->priv;
- GnomeVFSFileInfo *info;
- GdkPixbuf *pixbuf;
- char *thumb_uri;
-
- g_free (priv->uri);
- priv->uri = NULL;
-
+ GFile *file;
+ GFileInfo *info;
+ GTimeVal timeval;
+ GdkPixbuf *pixbuf;
+ char *thumb_uri;
+
+ g_free (preview->priv->uri);
+ preview->priv->uri = NULL;
if (uri == NULL) {
set_void_preview (preview);
return;
}
- priv->uri = g_strdup (uri);
- info = vfs_get_file_info (uri);
-
+ preview->priv->uri = g_strdup (uri);
+ file = g_file_new_for_uri (uri);
+ info = g_file_query_info (file, "time::*,standard::content-type,standard::size", G_FILE_QUERY_INFO_NONE, NULL, NULL);
if (info == NULL) {
set_void_preview (preview);
+ g_object_unref (file);
return;
}
- thumb_uri = gnome_thumbnail_factory_lookup (priv->thumb_factory,
- uri,
- info->mtime);
+ g_file_info_get_modification_time (info, &timeval);
+
+ thumb_uri = gnome_desktop_thumbnail_factory_lookup (preview->priv->thumb_factory,
+ uri,
+ timeval.tv_sec);
if (thumb_uri != NULL) {
pixbuf = gdk_pixbuf_new_from_file (thumb_uri, NULL);
-
- } else {
- char *mime_type = gnome_vfs_get_mime_type (uri);
-
- pixbuf = gnome_thumbnail_factory_generate_thumbnail (priv->thumb_factory, uri, mime_type);
+ }
+ else {
+ pixbuf = gnome_desktop_thumbnail_factory_generate_thumbnail (preview->priv->thumb_factory, uri, g_file_info_get_content_type (info));
if (pixbuf != NULL)
- gnome_thumbnail_factory_save_thumbnail (priv->thumb_factory, pixbuf, uri, info->mtime);
+ gnome_desktop_thumbnail_factory_save_thumbnail (preview->priv->thumb_factory, pixbuf, uri, timeval.tv_sec);
else
- gnome_thumbnail_factory_create_failed_thumbnail (priv->thumb_factory, uri, info->mtime);
-
- g_free (mime_type);
+ gnome_desktop_thumbnail_factory_create_failed_thumbnail (preview->priv->thumb_factory, uri, timeval.tv_sec);
}
if (pixbuf != NULL) {
@@ -322,28 +284,28 @@ gtk_file_chooser_preview_set_uri (GtkFileChooserPreview *preview,
w = gdk_pixbuf_get_option (pixbuf, "tEXt::Thumb::Image::Width");
h = gdk_pixbuf_get_option (pixbuf, "tEXt::Thumb::Image::Height");
- size_text = gnome_vfs_format_file_size_for_display (info->size);
+ size_text = g_format_size_for_display (g_file_info_get_size (info));
text = g_strconcat (size_text,
((w == NULL)?NULL:"\n"),
w, " x ", h, " ", _("pixels"),
NULL);
g_free (size_text);
- gtk_label_set_markup (GTK_LABEL (priv->image_info), text);
+ gtk_label_set_markup (GTK_LABEL (preview->priv->image_info), text);
g_free (text);
- gtk_image_set_from_pixbuf (GTK_IMAGE (priv->image), pixbuf);
+ gtk_image_set_from_pixbuf (GTK_IMAGE (preview->priv->image), pixbuf);
g_object_unref (pixbuf);
- gtk_widget_show (priv->image);
- gtk_widget_show (priv->image_info);
+ gtk_widget_show (preview->priv->image);
+ gtk_widget_show (preview->priv->image_info);
gtk_widget_set_sensitive (GTK_BIN (preview)->child, TRUE);
-
- } else
+ }
+ else
set_void_preview (preview);
- gnome_vfs_file_info_unref (info);
+ g_object_unref (info);
}
diff --git a/src/gtk-utils.c b/src/gtk-utils.c
index 63465c0..e10a557 100644
--- a/src/gtk-utils.c
+++ b/src/gtk-utils.c
@@ -1,9 +1,9 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
- * Goo
+ * File-Roller
*
- * Copyright (C) 2001, 2003, 2004 Free Software Foundation, Inc.
+ * Copyright (C) 2001-2008 The Free Software Foundation, Inc.
*
* 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
@@ -21,45 +21,16 @@
*/
#include <config.h>
-#include <libgnome/libgnome.h>
-#include <gtk/gtk.h>
+#include <math.h>
+#include <string.h>
#include "gconf-utils.h"
-#include "file-utils.h"
-
+#include "gtk-utils.h"
#define REQUEST_ENTRY_WIDTH 220
-GtkWidget *
-_gtk_image_new_from_xpm_data (char * xpm_data[])
-{
- GtkWidget *image;
- GdkPixbuf *pixbuf;
-
- pixbuf = gdk_pixbuf_new_from_xpm_data ((const char**) xpm_data);
- image = gtk_image_new_from_pixbuf (pixbuf);
- g_object_unref (G_OBJECT (pixbuf));
-
- return image;
-}
-
-
-GtkWidget *
-_gtk_image_new_from_inline (const guint8 *data)
-{
- GtkWidget *image;
- GdkPixbuf *pixbuf;
-
- pixbuf = gdk_pixbuf_new_from_inline (-1, data, FALSE, NULL);
- image = gtk_image_new_from_pixbuf (pixbuf);
- g_object_unref (G_OBJECT (pixbuf));
-
- return image;
-}
-
-
static GtkWidget *
-create_button (const char *stock_id,
+create_button (const char *stock_id,
const char *text)
{
GtkWidget *button;
@@ -131,8 +102,8 @@ _gtk_message_dialog_new (GtkWindow *parent,
gtk_dialog_set_has_separator (GTK_DIALOG (d), FALSE);
gtk_container_set_border_width (GTK_CONTAINER (d), 6);
- gtk_container_set_border_width (GTK_CONTAINER (GTK_DIALOG (d)->vbox), 6);
- gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (d)->vbox), 8);
+ gtk_container_set_border_width (GTK_CONTAINER (gtk_dialog_get_content_area (GTK_DIALOG (d))), 6);
+ gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (d))), 8);
/* Add label and image */
@@ -144,11 +115,11 @@ _gtk_message_dialog_new (GtkWindow *parent,
escaped_message = g_markup_escape_text (message, -1);
if (secondary_message != NULL) {
char *escaped_secondary_message = g_markup_escape_text (secondary_message, -1);
- markup_text = g_strdup_printf ("<span weight=\"bold\" size=\"larger\">%s</span>\n\n%s",
+ markup_text = g_strdup_printf ("<span weight=\"bold\" size=\"larger\">%s</span>\n\n%s",
escaped_message,
escaped_secondary_message);
g_free (escaped_secondary_message);
- } else
+ } else
markup_text = g_strdup (escaped_message);
gtk_label_set_markup (GTK_LABEL (label), markup_text);
g_free (markup_text);
@@ -156,22 +127,22 @@ _gtk_message_dialog_new (GtkWindow *parent,
gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
gtk_label_set_selectable (GTK_LABEL (label), TRUE);
-
+
hbox = gtk_hbox_new (FALSE, 6);
gtk_container_set_border_width (GTK_CONTAINER (hbox), 6);
-
+
gtk_box_pack_start (GTK_BOX (hbox), image,
FALSE, FALSE, 0);
-
+
gtk_box_pack_start (GTK_BOX (hbox), label,
TRUE, TRUE, 0);
-
- gtk_box_pack_start (GTK_BOX (GTK_DIALOG (d)->vbox),
+
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (d))),
hbox,
FALSE, FALSE, 0);
-
+
gtk_widget_show_all (hbox);
-
+
/* Add buttons */
if (first_button_text == NULL)
@@ -223,15 +194,15 @@ _gtk_request_dialog_run (GtkWindow *parent,
gtk_dialog_set_has_separator (GTK_DIALOG (d), FALSE);
gtk_container_set_border_width (GTK_CONTAINER (d), 6);
- gtk_container_set_border_width (GTK_CONTAINER (GTK_DIALOG (d)->vbox), 6);
- gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (d)->vbox), 12);
+ gtk_container_set_border_width (GTK_CONTAINER (gtk_dialog_get_content_area (GTK_DIALOG (d))), 6);
+ gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (d))), 12);
/* Add label and image */
image = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_DIALOG);
gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0.0);
- label = gtk_label_new (message);
+ label = gtk_label_new (message);
gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
gtk_label_set_selectable (GTK_LABEL (label), FALSE);
gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
@@ -260,37 +231,41 @@ _gtk_request_dialog_run (GtkWindow *parent,
gtk_box_pack_start (GTK_BOX (hbox), vbox,
TRUE, TRUE, 0);
-
- gtk_box_pack_start (GTK_BOX (GTK_DIALOG (d)->vbox),
+
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (d))),
hbox,
FALSE, FALSE, 0);
-
+
gtk_widget_show_all (hbox);
-
+
/* Add buttons */
button = create_button (GTK_STOCK_CANCEL, no_button_text);
- gtk_dialog_add_action_widget (GTK_DIALOG (d),
- button,
+ gtk_dialog_add_action_widget (GTK_DIALOG (d),
+ button,
GTK_RESPONSE_CANCEL);
/**/
button = create_button (GTK_STOCK_OK, yes_button_text);
- gtk_dialog_add_action_widget (GTK_DIALOG (d),
+ gtk_dialog_add_action_widget (GTK_DIALOG (d),
button,
GTK_RESPONSE_YES);
/**/
- gtk_dialog_set_default_response (GTK_DIALOG (d),
+ gtk_dialog_set_default_response (GTK_DIALOG (d),
GTK_RESPONSE_YES);
gtk_widget_grab_focus (entry);
/* Run dialog */
- if (gtk_dialog_run (GTK_DIALOG (d)) == GTK_RESPONSE_YES)
- result = g_strdup (gtk_entry_get_text (GTK_ENTRY (entry)));
+ if ((gtk_dialog_run (GTK_DIALOG (d)) == GTK_RESPONSE_YES) &&
+ (strlen (gtk_entry_get_text (GTK_ENTRY (entry))) > 0) )
+ /* Normalize unicode text to "NFC" form for consistency. */
+ result = g_utf8_normalize (gtk_entry_get_text (GTK_ENTRY (entry)),
+ -1,
+ G_NORMALIZE_NFC);
else
result = NULL;
@@ -319,51 +294,51 @@ _gtk_yesno_dialog_new (GtkWindow *parent,
gtk_dialog_set_has_separator (GTK_DIALOG (d), FALSE);
gtk_container_set_border_width (GTK_CONTAINER (d), 6);
- gtk_container_set_border_width (GTK_CONTAINER (GTK_DIALOG (d)->vbox), 6);
- gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (d)->vbox), 8);
+ gtk_container_set_border_width (GTK_CONTAINER (gtk_dialog_get_content_area (GTK_DIALOG (d))), 6);
+ gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (d))), 8);
/* Add label and image */
image = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_DIALOG);
gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0.0);
- label = gtk_label_new (message);
+ label = gtk_label_new (message);
gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
gtk_label_set_selectable (GTK_LABEL (label), TRUE);
-
+
hbox = gtk_hbox_new (FALSE, 12);
gtk_container_set_border_width (GTK_CONTAINER (hbox), 6);
-
+
gtk_box_pack_start (GTK_BOX (hbox), image,
FALSE, FALSE, 0);
-
+
gtk_box_pack_start (GTK_BOX (hbox), label,
TRUE, TRUE, 0);
-
- gtk_box_pack_start (GTK_BOX (GTK_DIALOG (d)->vbox),
+
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (d))),
hbox,
FALSE, FALSE, 0);
-
+
gtk_widget_show_all (hbox);
/* Add buttons */
button = create_button (GTK_STOCK_CANCEL, no_button_text);
- gtk_dialog_add_action_widget (GTK_DIALOG (d),
- button,
+ gtk_dialog_add_action_widget (GTK_DIALOG (d),
+ button,
GTK_RESPONSE_CANCEL);
/**/
button = create_button (GTK_STOCK_OK, yes_button_text);
- gtk_dialog_add_action_widget (GTK_DIALOG (d),
- button,
+ gtk_dialog_add_action_widget (GTK_DIALOG (d),
+ button,
GTK_RESPONSE_YES);
/**/
gtk_dialog_set_default_response (GTK_DIALOG (d), GTK_RESPONSE_YES);
-
+
return d;
}
@@ -372,7 +347,7 @@ static void
yesno__check_button_toggled_cb (GtkToggleButton *button,
const char *gconf_key)
{
- eel_gconf_set_boolean (gconf_key,
+ eel_gconf_set_boolean (gconf_key,
! gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)));
}
@@ -399,8 +374,8 @@ _gtk_yesno_dialog_with_checkbutton_new (GtkWindow *parent,
gtk_dialog_set_has_separator (GTK_DIALOG (d), FALSE);
gtk_container_set_border_width (GTK_CONTAINER (d), 6);
- gtk_container_set_border_width (GTK_CONTAINER (GTK_DIALOG (d)->vbox), 6);
- gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (d)->vbox), 8);
+ gtk_container_set_border_width (GTK_CONTAINER (gtk_dialog_get_content_area (GTK_DIALOG (d))), 6);
+ gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (d))), 8);
/* Add label and image */
@@ -410,42 +385,42 @@ _gtk_yesno_dialog_with_checkbutton_new (GtkWindow *parent,
label = gtk_label_new (message);
gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
gtk_label_set_selectable (GTK_LABEL (label), TRUE);
-
+
hbox = gtk_hbox_new (FALSE, 12);
gtk_container_set_border_width (GTK_CONTAINER (hbox), 6);
-
+
gtk_box_pack_start (GTK_BOX (hbox), image,
FALSE, FALSE, 0);
-
+
gtk_box_pack_start (GTK_BOX (hbox), label,
TRUE, TRUE, 0);
-
- gtk_box_pack_start (GTK_BOX (GTK_DIALOG (d)->vbox),
+
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (d))),
hbox,
FALSE, FALSE, 0);
/* Add checkbutton */
check_button = gtk_check_button_new_with_mnemonic (check_button_label);
- gtk_box_pack_start (GTK_BOX (GTK_DIALOG (d)->vbox),
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (d))),
check_button,
FALSE, FALSE, 0);
gtk_widget_show (check_button);
-
+
gtk_widget_show_all (hbox);
/* Add buttons */
button = create_button (GTK_STOCK_CANCEL, no_button_text);
- gtk_dialog_add_action_widget (GTK_DIALOG (d),
- button,
+ gtk_dialog_add_action_widget (GTK_DIALOG (d),
+ button,
GTK_RESPONSE_CANCEL);
/**/
button = create_button (GTK_STOCK_OK, yes_button_text);
- gtk_dialog_add_action_widget (GTK_DIALOG (d),
- button,
+ gtk_dialog_add_action_widget (GTK_DIALOG (d),
+ button,
GTK_RESPONSE_YES);
/**/
@@ -453,10 +428,10 @@ _gtk_yesno_dialog_with_checkbutton_new (GtkWindow *parent,
gtk_dialog_set_default_response (GTK_DIALOG (d), GTK_RESPONSE_YES);
g_signal_connect (G_OBJECT (check_button),
- "toggled",
- G_CALLBACK (yesno__check_button_toggled_cb),
+ "toggled",
+ G_CALLBACK (yesno__check_button_toggled_cb),
(gpointer) gconf_key);
-
+
return d;
}
@@ -465,7 +440,7 @@ static void
ok__check_button_toggled_cb (GtkToggleButton *button,
const char *gconf_key)
{
- eel_gconf_set_boolean (gconf_key,
+ eel_gconf_set_boolean (gconf_key,
gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)));
}
@@ -503,23 +478,23 @@ _gtk_ok_dialog_with_checkbutton_new (GtkWindow *parent,
escaped_message = g_markup_escape_text (message, -1);
markup_text = g_strdup_printf ("<span weight=\"bold\" size=\"larger\">%s</span>", escaped_message);
- label = gtk_label_new ("");
+ label = gtk_label_new ("");
gtk_label_set_markup (GTK_LABEL (label), markup_text);
g_free (markup_text);
g_free (escaped_message);
gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
gtk_label_set_selectable (GTK_LABEL (label), TRUE);
-
+
hbox = gtk_hbox_new (FALSE, 12);
gtk_container_set_border_width (GTK_CONTAINER (hbox), 6);
-
+
gtk_box_pack_start (GTK_BOX (hbox), image,
FALSE, FALSE, 0);
-
+
gtk_box_pack_start (GTK_BOX (hbox), label,
TRUE, TRUE, 0);
-
+
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (d)->vbox),
hbox,
FALSE, FALSE, 0);
@@ -534,60 +509,199 @@ _gtk_ok_dialog_with_checkbutton_new (GtkWindow *parent,
gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (check_button),
eel_gconf_get_boolean (gconf_key, FALSE));
-
+
gtk_widget_show_all (hbox);
/* Add buttons */
button = create_button (GTK_STOCK_CANCEL, ok_button_text);
- gtk_dialog_add_action_widget (GTK_DIALOG (d),
- button,
+ gtk_dialog_add_action_widget (GTK_DIALOG (d),
+ button,
GTK_RESPONSE_OK);
-
+
/**/
gtk_dialog_set_default_response (GTK_DIALOG (d), GTK_RESPONSE_YES);
g_signal_connect (G_OBJECT (check_button),
- "toggled",
- G_CALLBACK (ok__check_button_toggled_cb),
+ "toggled",
+ G_CALLBACK (ok__check_button_toggled_cb),
+ (gpointer) gconf_key);
+
+ return d;
+}
+
+
+GtkWidget*
+_gtk_message_dialog_with_checkbutton_new (GtkWindow *parent,
+ GtkDialogFlags flags,
+ const char *stock_id,
+ const char *message,
+ const char *secondary_message,
+ const char *gconf_key,
+ const char *check_button_label,
+ const char *first_button_text,
+ ...)
+{
+ GtkWidget *d;
+ GtkWidget *label;
+ GtkWidget *image;
+ GtkWidget *hbox;
+ GtkWidget *check_button;
+ va_list args;
+ const char *text;
+ int response_id;
+ char *escaped_message, *markup_text;
+
+ g_return_val_if_fail (message != NULL, NULL);
+
+ if (stock_id == NULL)
+ stock_id = GTK_STOCK_DIALOG_INFO;
+
+ d = gtk_dialog_new_with_buttons ("", parent, flags, NULL);
+ gtk_window_set_resizable (GTK_WINDOW (d), FALSE);
+
+ gtk_dialog_set_has_separator (GTK_DIALOG (d), FALSE);
+ gtk_container_set_border_width (GTK_CONTAINER (d), 6);
+ gtk_container_set_border_width (GTK_CONTAINER (gtk_dialog_get_content_area (GTK_DIALOG (d))), 6);
+ gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (d))), 8);
+
+ /* Add label and image */
+
+ image = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_DIALOG);
+ gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0.0);
+
+ label = gtk_label_new ("");
+
+ escaped_message = g_markup_escape_text (message, -1);
+ if (secondary_message != NULL) {
+ char *escaped_secondary_message = g_markup_escape_text (secondary_message, -1);
+ markup_text = g_strdup_printf ("<span weight=\"bold\" size=\"larger\">%s</span>\n\n%s",
+ escaped_message,
+ escaped_secondary_message);
+ g_free (escaped_secondary_message);
+ } else
+ markup_text = g_strdup (escaped_message);
+ gtk_label_set_markup (GTK_LABEL (label), markup_text);
+ g_free (markup_text);
+ g_free (escaped_message);
+
+ gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+ gtk_label_set_selectable (GTK_LABEL (label), TRUE);
+
+ hbox = gtk_hbox_new (FALSE, 6);
+ gtk_container_set_border_width (GTK_CONTAINER (hbox), 6);
+
+ gtk_box_pack_start (GTK_BOX (hbox), image,
+ FALSE, FALSE, 0);
+
+ gtk_box_pack_start (GTK_BOX (hbox), label,
+ TRUE, TRUE, 0);
+
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (d))),
+ hbox,
+ FALSE, FALSE, 0);
+
+ gtk_widget_show_all (hbox);
+
+ /* Add checkbutton */
+
+ check_button = gtk_check_button_new_with_mnemonic (check_button_label);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (d))),
+ check_button,
+ FALSE, FALSE, 0);
+ gtk_widget_show (check_button);
+
+ /* Add buttons */
+
+ if (first_button_text == NULL)
+ return d;
+
+ va_start (args, first_button_text);
+
+ text = first_button_text;
+ response_id = va_arg (args, gint);
+
+ while (text != NULL) {
+ gtk_dialog_add_button (GTK_DIALOG (d), text, response_id);
+
+ text = va_arg (args, gchar*);
+ if (text == NULL)
+ break;
+ response_id = va_arg (args, int);
+ }
+
+ va_end (args);
+
+ gtk_dialog_set_default_response (GTK_DIALOG (d), GTK_RESPONSE_YES);
+
+ g_signal_connect (G_OBJECT (check_button),
+ "toggled",
+ G_CALLBACK (yesno__check_button_toggled_cb),
(gpointer) gconf_key);
-
+
return d;
}
void
-_gtk_error_dialog_from_gerror_run (GtkWindow *parent,
- const char *main_message,
- GError **gerror)
+_gtk_error_dialog_from_gerror_run (GtkWindow *parent,
+ const char *title,
+ GError **gerror)
{
GtkWidget *d;
g_return_if_fail (*gerror != NULL);
- g_return_if_fail ((*gerror)->message != NULL);
-
- if ((*gerror)->message != NULL) {
- d = _gtk_message_dialog_new (parent,
- GTK_DIALOG_MODAL,
- GTK_STOCK_DIALOG_ERROR,
- main_message,
- (*gerror)->message,
- GTK_STOCK_OK, GTK_RESPONSE_CANCEL,
- NULL);
- g_signal_connect (G_OBJECT (d), "response",
- G_CALLBACK (gtk_widget_destroy),
- NULL);
- gtk_widget_show (d);
- }
+ d = _gtk_message_dialog_new (parent,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_STOCK_DIALOG_ERROR,
+ title,
+ (*gerror)->message,
+ GTK_STOCK_OK, GTK_RESPONSE_OK,
+ NULL);
+ gtk_dialog_run (GTK_DIALOG (d));
+
+ gtk_widget_destroy (d);
g_clear_error (gerror);
}
+static void
+error_dialog_response_cb (GtkDialog *dialog,
+ int response,
+ gpointer user_data)
+{
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+}
+
+
+void
+_gtk_error_dialog_from_gerror_show (GtkWindow *parent,
+ const char *title,
+ GError **gerror)
+{
+ GtkWidget *d;
+
+ d = _gtk_message_dialog_new (parent,
+ GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_STOCK_DIALOG_ERROR,
+ title,
+ (gerror != NULL) ? (*gerror)->message : NULL,
+ GTK_STOCK_OK, GTK_RESPONSE_OK,
+ NULL);
+ g_signal_connect (d, "response", G_CALLBACK (error_dialog_response_cb), NULL);
+
+ gtk_window_present (GTK_WINDOW (d));
+
+ if (gerror != NULL)
+ g_clear_error (gerror);
+}
+
+
+
void
_gtk_error_dialog_run (GtkWindow *parent,
- const gchar *main_message,
const gchar *format,
...)
{
@@ -602,8 +716,8 @@ _gtk_error_dialog_run (GtkWindow *parent,
d = _gtk_message_dialog_new (parent,
GTK_DIALOG_MODAL,
GTK_STOCK_DIALOG_ERROR,
- main_message,
message,
+ NULL,
GTK_STOCK_CLOSE, GTK_RESPONSE_CANCEL,
NULL);
g_free (message);
@@ -646,179 +760,489 @@ _gtk_info_dialog_run (GtkWindow *parent,
}
-void
-_gtk_entry_set_locale_text (GtkEntry *entry,
- const char *text)
+static GdkPixbuf *
+get_themed_icon_pixbuf (GThemedIcon *icon,
+ int size,
+ GtkIconTheme *icon_theme)
+{
+ char **icon_names;
+ GtkIconInfo *icon_info;
+ GdkPixbuf *pixbuf;
+ GError *error = NULL;
+
+ g_object_get (icon, "names", &icon_names, NULL);
+
+ icon_info = gtk_icon_theme_choose_icon (icon_theme, (const char **)icon_names, size, 0);
+ if (icon_info == NULL)
+ icon_info = gtk_icon_theme_lookup_icon (icon_theme, "text-x-generic", size, GTK_ICON_LOOKUP_USE_BUILTIN);
+
+ pixbuf = gtk_icon_info_load_icon (icon_info, &error);
+ if (pixbuf == NULL) {
+ g_warning ("could not load icon pixbuf: %s\n", error->message);
+ g_clear_error (&error);
+ }
+
+ gtk_icon_info_free (icon_info);
+ g_strfreev (icon_names);
+
+ return pixbuf;
+}
+
+
+static GdkPixbuf *
+get_file_icon_pixbuf (GFileIcon *icon,
+ int size)
{
- char *utf8_text;
+ GFile *file;
+ char *filename;
+ GdkPixbuf *pixbuf;
- if (text == NULL)
- text = "";
+ file = g_file_icon_get_file (icon);
+ filename = g_file_get_path (file);
+ pixbuf = gdk_pixbuf_new_from_file_at_size (filename, size, -1, NULL);
+ g_free (filename);
+ g_object_unref (file);
- utf8_text = g_locale_to_utf8 (text, -1, NULL, NULL, NULL);
- gtk_entry_set_text (entry, utf8_text);
- g_free (utf8_text);
+ return pixbuf;
}
-char *
-_gtk_entry_get_locale_text (GtkEntry *entry)
+gboolean
+scale_keeping_ratio_min (int *width,
+ int *height,
+ int min_width,
+ int min_height,
+ int max_width,
+ int max_height,
+ gboolean allow_upscaling)
{
- const char *utf8_text;
- char *text;
+ double w = *width;
+ double h = *height;
+ double min_w = min_width;
+ double min_h = min_height;
+ double max_w = max_width;
+ double max_h = max_height;
+ double factor;
+ int new_width, new_height;
+ gboolean modified;
+
+ if ((*width < max_width) && (*height < max_height) && ! allow_upscaling)
+ return FALSE;
+
+ if (((*width < min_width) || (*height < min_height)) && ! allow_upscaling)
+ return FALSE;
+
+ factor = MAX (MIN (max_w / w, max_h / h), MAX (min_w / w, min_h / h));
+ new_width = MAX ((int) floor (w * factor + 0.50), 1);
+ new_height = MAX ((int) floor (h * factor + 0.50), 1);
+
+ modified = (new_width != *width) || (new_height != *height);
- utf8_text = gtk_entry_get_text (entry);
- if (utf8_text == NULL)
+ *width = new_width;
+ *height = new_height;
+
+ return modified;
+}
+
+
+gboolean
+scale_keeping_ratio (int *width,
+ int *height,
+ int max_width,
+ int max_height,
+ gboolean allow_upscaling)
+{
+ return scale_keeping_ratio_min (width,
+ height,
+ 0,
+ 0,
+ max_width,
+ max_height,
+ allow_upscaling);
+}
+
+
+GdkPixbuf *
+_g_icon_get_pixbuf (GIcon *icon,
+ int size,
+ GtkIconTheme *theme)
+{
+ GdkPixbuf *pixbuf;
+ int w, h;
+
+ if (icon == NULL)
return NULL;
- text = g_locale_from_utf8 (utf8_text, -1, NULL, NULL, NULL);
+ if (G_IS_THEMED_ICON (icon))
+ pixbuf = get_themed_icon_pixbuf (G_THEMED_ICON (icon), size, theme);
+ if (G_IS_FILE_ICON (icon))
+ pixbuf = get_file_icon_pixbuf (G_FILE_ICON (icon), size);
+
+ if (pixbuf == NULL)
+ return NULL;
- return text;
+ w = gdk_pixbuf_get_width (pixbuf);
+ h = gdk_pixbuf_get_height (pixbuf);
+ if (scale_keeping_ratio (&w, &h, size, size, FALSE)) {
+ GdkPixbuf *scaled;
+
+ scaled = gdk_pixbuf_scale_simple (pixbuf, w, h, GDK_INTERP_BILINEAR);
+ g_object_unref (pixbuf);
+ pixbuf = scaled;
+ }
+
+ return pixbuf;
+}
+
+
+GdkPixbuf *
+get_mime_type_pixbuf (const char *mime_type,
+ int icon_size,
+ GtkIconTheme *icon_theme)
+{
+ GdkPixbuf *pixbuf = NULL;
+ GIcon *icon;
+
+ if (icon_theme == NULL)
+ icon_theme = gtk_icon_theme_get_default ();
+
+ icon = g_content_type_get_icon (mime_type);
+ pixbuf = _g_icon_get_pixbuf (icon, icon_size, icon_theme);
+ g_object_unref (icon);
+
+ return pixbuf;
+}
+
+
+int
+_gtk_icon_get_pixel_size (GtkWidget *widget,
+ GtkIconSize size)
+{
+ int icon_width, icon_height;
+
+ gtk_icon_size_lookup_for_settings (gtk_widget_get_settings (widget),
+ size,
+ &icon_width, &icon_height);
+ return MAX (icon_width, icon_height);
}
void
-_gtk_label_set_locale_text (GtkLabel *label,
- const char *text)
+show_help_dialog (GtkWindow *parent,
+ const char *section)
{
- char *utf8_text;
+ char *uri;
+ GError *error = NULL;
+
+ uri = g_strconcat ("ghelp:goobox", section ? "?" : NULL, section, NULL);
+ if (! gtk_show_uri (gtk_window_get_screen (parent), uri, GDK_CURRENT_TIME, &error)) {
+ GtkWidget *dialog;
+
+ dialog = _gtk_message_dialog_new (parent,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_STOCK_DIALOG_ERROR,
+ _("Could not display help"),
+ error->message,
+ GTK_STOCK_OK, GTK_RESPONSE_OK,
+ NULL);
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+
+ g_signal_connect (G_OBJECT (dialog), "response",
+ G_CALLBACK (gtk_widget_destroy),
+ NULL);
+
+ gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
- if (text == NULL)
- text = "";
+ gtk_widget_show (dialog);
- utf8_text = g_locale_to_utf8 (text, -1, NULL, NULL, NULL);
- gtk_label_set_text (label, utf8_text);
- g_free (utf8_text);
+ g_clear_error (&error);
+ }
+ g_free (uri);
}
-char *
-_gtk_label_get_locale_text (GtkLabel *label)
+void
+_gtk_container_remove_children (GtkContainer *container,
+ gpointer start_after_this,
+ gpointer stop_before_this)
{
- const char *utf8_text;
- char *text;
+ GList *children;
+ GList *scan;
- utf8_text = gtk_label_get_text (label);
- if (utf8_text == NULL)
- return NULL;
+ children = gtk_container_get_children (container);
+
+ if (start_after_this != NULL) {
+ for (scan = children; scan; scan = scan->next)
+ if (scan->data == start_after_this) {
+ scan = scan->next;
+ break;
+ }
+ }
+ else
+ scan = children;
+
+ for (/* void */; scan && (scan->data != stop_before_this); scan = scan->next)
+ gtk_container_remove (container, scan->data);
+ g_list_free (children);
+}
+
+
+int
+_gtk_container_get_pos (GtkContainer *container,
+ GtkWidget *child)
+{
+ GList *children;
+ gboolean found = FALSE;
+ int idx;
+ GList *scan;
+
+ children = gtk_container_get_children (container);
+ for (idx = 0, scan = children; ! found && scan; idx++, scan = scan->next) {
+ if (child == scan->data) {
+ found = TRUE;
+ break;
+ }
+ }
+ g_list_free (children);
+
+ return ! found ? -1 : idx;
+}
+
+
+guint
+_gtk_container_get_n_children (GtkContainer *container)
+{
+ GList *children;
+ guint len;
+
+ children = gtk_container_get_children (container);
+ len = g_list_length (children);
+ g_list_free (children);
+
+ return len;
+}
+
+
+GtkBuilder *
+_gtk_builder_new_from_file (const char *ui_file,
+ const char *extension)
+{
+ char *filename;
+ GtkBuilder *builder;
+ GError *error = NULL;
+
+ filename = g_build_filename (GOO_UIDIR, ui_file, NULL);
+ builder = gtk_builder_new ();
+ if (! gtk_builder_add_from_file (builder, filename, &error)) {
+ g_warning ("%s\n", error->message);
+ g_clear_error (&error);
+ }
+ g_free (filename);
+
+ return builder;
+}
+
+
+GtkWidget *
+_gtk_builder_get_widget (GtkBuilder *builder,
+ const char *name)
+{
+ return (GtkWidget *) gtk_builder_get_object (builder, name);
+}
- text = g_locale_from_utf8 (utf8_text, -1, NULL, NULL, NULL);
- return text;
+GtkWidget *
+_gtk_combo_box_new_with_texts (const char *first_text,
+ ...)
+{
+ GtkWidget *combo_box;
+ va_list args;
+ const char *text;
+
+ combo_box = gtk_combo_box_new_text ();
+
+ va_start (args, first_text);
+
+ text = first_text;
+ while (text != NULL) {
+ gtk_combo_box_append_text (GTK_COMBO_BOX (combo_box), text);
+ text = va_arg (args, const char *);
+ }
+
+ va_end (args);
+
+ return combo_box;
}
void
-_gtk_entry_set_filename_text (GtkEntry *entry,
- const char *text)
+_gtk_combo_box_append_texts (GtkComboBox *combo_box,
+ const char *first_text,
+ ...)
{
- char *utf8_text;
+ va_list args;
+ const char *text;
- if (text == NULL)
- text = "";
+ va_start (args, first_text);
- utf8_text = g_filename_to_utf8 (text, -1, NULL, NULL, NULL);
- gtk_entry_set_text (entry, utf8_text);
- g_free (utf8_text);
+ text = first_text;
+ while (text != NULL) {
+ gtk_combo_box_append_text (combo_box, text);
+ text = va_arg (args, const char *);
+ }
+
+ va_end (args);
}
-char *
-_gtk_entry_get_filename_text (GtkEntry *entry)
+GtkWidget *
+_gtk_image_new_from_xpm_data (char * xpm_data[])
{
- const char *utf8_text;
- char *text;
+ GdkPixbuf *pixbuf;
+ GtkWidget *image;
- utf8_text = gtk_entry_get_text (entry);
- if (utf8_text == NULL)
- return NULL;
+ pixbuf = gdk_pixbuf_new_from_xpm_data ((const char**) xpm_data);
+ image = gtk_image_new_from_pixbuf (pixbuf);
+ gtk_widget_show (image);
- text = g_filename_from_utf8 (utf8_text, -1, NULL, NULL, NULL);
+ g_object_unref (G_OBJECT (pixbuf));
- return text;
+ return image;
+}
+
+
+GtkWidget *
+_gtk_image_new_from_inline (const guint8 *data)
+{
+ GdkPixbuf *pixbuf;
+ GtkWidget *image;
+
+ pixbuf = gdk_pixbuf_new_from_inline (-1, data, FALSE, NULL);
+ image = gtk_image_new_from_pixbuf (pixbuf);
+ gtk_widget_show (image);
+
+ g_object_unref (G_OBJECT (pixbuf));
+
+ return image;
}
void
-_gtk_label_set_filename_text (GtkLabel *label,
- const char *text)
+_gtk_widget_get_screen_size (GtkWidget *widget,
+ int *width,
+ int *height)
{
- char *utf8_text;
+ GdkScreen *screen;
+ GdkRectangle screen_geom;
- if (text == NULL)
- text = "";
+ screen = gtk_widget_get_screen (widget);
+ gdk_screen_get_monitor_geometry (screen,
+ gdk_screen_get_monitor_at_window (screen, widget->window),
+ &screen_geom);
- utf8_text = g_filename_to_utf8 (text, -1, NULL, NULL, NULL);
- gtk_label_set_text (label, utf8_text);
- g_free (utf8_text);
+ *width = screen_geom.width;
+ *height = screen_geom.height;
}
-char *
-_gtk_label_get_filename_text (GtkLabel *label)
+void
+_gtk_tree_path_list_free (GList *list)
{
- const char *utf8_text;
- char *text;
+ g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
+ g_list_free (list);
+}
- utf8_text = gtk_label_get_text (label);
- if (utf8_text == NULL)
- return NULL;
- text = g_filename_from_utf8 (utf8_text, -1, NULL, NULL, NULL);
+int
+_gtk_paned_get_position2 (GtkPaned *paned)
+{
+ GtkRequisition requisition;
+ int pos;
+ int size;
+
+ if (! GTK_WIDGET_VISIBLE (paned))
+ return 0;
+
+ pos = gtk_paned_get_position (paned);
- return text;
+ gtk_window_get_size (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (paned))), &(requisition.width), &(requisition.height));
+ if (gtk_orientable_get_orientation (GTK_ORIENTABLE (paned)) == GTK_ORIENTATION_HORIZONTAL)
+ size = requisition.width;
+ else
+ size = requisition.height;
+
+ return size - pos;
}
-gboolean
-exec_command (const char *application,
- GList *file_list)
+void
+_gtk_paned_set_position2 (GtkPaned *paned,
+ int pos)
{
- GString *command;
- GList *scan;
- GError *err = NULL;
- gboolean error;
+ GtkWidget *top_level;
+ int size;
+
+ top_level = gtk_widget_get_toplevel (GTK_WIDGET (paned));
+ if (gtk_orientable_get_orientation (GTK_ORIENTABLE (paned)) == GTK_ORIENTATION_HORIZONTAL)
+ size = top_level->allocation.width;
+ else
+ size = top_level->allocation.height;
+
+ if (pos > 0)
+ gtk_paned_set_position (paned, size - pos);
+}
- command = g_string_new ("");
- g_string_append (command, application);
- for (scan = file_list; scan; scan = scan->next) {
- char *filename = scan->data;
- char *e_filename;
- g_string_append_c (command, ' ');
- e_filename = shell_escape (filename);
- g_string_append (command, e_filename);
+void
+_g_launch_command (GtkWidget *parent,
+ const char *command,
+ const char *name,
+ GList *files)
+{
+ GError *error = NULL;
+ GAppInfo *app_info;
+ GdkAppLaunchContext *launch_context;
+
+ app_info = g_app_info_create_from_commandline (command, name, G_APP_INFO_CREATE_SUPPORTS_URIS, &error);
+ if (app_info == NULL) {
+ _gtk_error_dialog_from_gerror_show(GTK_WINDOW (parent), _("Could not launch the application"), &error);
+ return;
+ }
- g_free (e_filename);
- }
+ launch_context = gdk_app_launch_context_new ();
+ gdk_app_launch_context_set_screen (launch_context, gtk_widget_get_screen (parent));
- error = (! g_spawn_command_line_async (command->str, &err) || (err != NULL));
- if (error)
- _gtk_error_dialog_from_gerror_run (NULL, _("Could not execute command"), &err);
- g_string_free (command, TRUE);
+ if (! g_app_info_launch (app_info, files, G_APP_LAUNCH_CONTEXT (launch_context), &error)) {
+ _gtk_error_dialog_from_gerror_show(GTK_WINDOW (parent), _("Could not launch the application"), &error);
+ return;
+ }
- return ! error;
+ g_object_unref (app_info);
}
static void
count_selected (GtkTreeModel *model,
- GtkTreePath *path,
- GtkTreeIter *iter,
- gpointer data)
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer data)
{
- int *n = data;
- *n = *n + 1;
+ int *n = data;
+ *n = *n + 1;
}
int
_gtk_count_selected (GtkTreeSelection *selection)
{
- int n = 0;
+ int n;
+
+ if (selection == NULL)
+ return 0;
- if (selection == NULL)
- return 0;
- gtk_tree_selection_selected_foreach (selection, count_selected, &n);
- return n;
+ n = 0;
+ gtk_tree_selection_selected_foreach (selection, count_selected, &n);
+
+ return n;
}
+
diff --git a/src/gtk-utils.h b/src/gtk-utils.h
index dfd5e31..1d50de7 100644
--- a/src/gtk-utils.h
+++ b/src/gtk-utils.h
@@ -1,9 +1,9 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
- * GThumb
+ * File-Roller
*
- * Copyright (C) 2001 The Free Software Foundation, Inc.
+ * Copyright (C) 2001-2008 Free Software Foundation, Inc.
*
* 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
@@ -20,87 +20,116 @@
* Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
*/
-#ifndef _GTK_UTILS_H
-#define _GTK_UTILS_H
-
-#include <gtk/gtkwidget.h>
-
-GtkWidget * _gtk_image_new_from_xpm_data (char *xpm_data[]);
-GtkWidget * _gtk_image_new_from_inline (const guint8 *data);
-
-GtkWidget* _gtk_message_dialog_new (GtkWindow *parent,
- GtkDialogFlags flags,
- const char *stock_id,
- const char *message,
- const char *secondary_message,
- const char *first_button_text,
- ...);
-
-gchar* _gtk_request_dialog_run (GtkWindow *parent,
- GtkDialogFlags flags,
- const char *message,
- const char *default_value,
- int max_length,
- const char *no_button_text,
- const char *yes_button_text);
-
-GtkWidget* _gtk_yesno_dialog_new (GtkWindow *parent,
- GtkDialogFlags flags,
- const char *message,
- const char *no_button_text,
- const char *yes_button_text);
-
-GtkWidget* _gtk_yesno_dialog_with_checkbutton_new (GtkWindow *parent,
- GtkDialogFlags flags,
- const char *message,
- const char *no_button_text,
- const char *yes_button_text,
- const char *check_button_label,
- const char *gconf_key);
-
-GtkWidget* _gtk_ok_dialog_with_checkbutton_new (GtkWindow *parent,
- GtkDialogFlags flags,
- const char *message,
- const char *ok_button_text,
- const char *check_button_label,
- const char *gconf_key);
-
-void _gtk_error_dialog_from_gerror_run (GtkWindow *parent,
- const char *main_message,
- GError **gerror);
-
-void _gtk_error_dialog_run (GtkWindow *parent,
- const gchar *main_message,
- const gchar *format,
- ...) G_GNUC_PRINTF (3, 4);
-
-void _gtk_info_dialog_run (GtkWindow *parent,
- const gchar *format,
- ...) G_GNUC_PRINTF (2, 3);
-
-void _gtk_entry_set_locale_text (GtkEntry *entry,
- const char *text);
-
-char * _gtk_entry_get_locale_text (GtkEntry *entry);
-
-void _gtk_label_set_locale_text (GtkLabel *label,
- const char *text);
-
-char * _gtk_label_get_locale_text (GtkLabel *label);
-
-void _gtk_entry_set_filename_text (GtkEntry *entry,
- const char *text);
-
-char * _gtk_entry_get_filename_text (GtkEntry *entry);
-
-void _gtk_label_set_filename_text (GtkLabel *label,
- const char *text);
-
-char * _gtk_label_get_filename_text (GtkLabel *label);
-
-gboolean exec_command (const char *application,
- GList *file_list);
-
-int _gtk_count_selected (GtkTreeSelection *selection);
-
-#endif /* _GTK_UTILS_H */
+#ifndef GTK_UTILS_H
+#define GTK_UTILS_H
+
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+GtkWidget* _gtk_message_dialog_new (GtkWindow *parent,
+ GtkDialogFlags flags,
+ const char *stock_id,
+ const char *message,
+ const char *secondary_message,
+ const char *first_button_text,
+ ...);
+GtkWidget*
+_gtk_message_dialog_with_checkbutton_new (GtkWindow *parent,
+ GtkDialogFlags flags,
+ const char *stock_id,
+ const char *message,
+ const char *secondary_message,
+ const char *gconf_key,
+ const char *check_button_label,
+ const char *first_button_text,
+ ...);
+gchar* _gtk_request_dialog_run (GtkWindow *parent,
+ GtkDialogFlags flags,
+ const char *message,
+ const char *default_value,
+ int max_length,
+ const char *no_button_text,
+ const char *yes_button_text);
+GtkWidget* _gtk_yesno_dialog_new (GtkWindow *parent,
+ GtkDialogFlags flags,
+ const char *message,
+ const char *no_button_text,
+ const char *yes_button_text);
+GtkWidget*
+_gtk_yesno_dialog_with_checkbutton_new (GtkWindow *parent,
+ GtkDialogFlags flags,
+ const char *message,
+ const char *no_button_text,
+ const char *yes_button_text,
+ const char *check_button_label,
+ const char *gconf_key);
+GtkWidget*
+_gtk_ok_dialog_with_checkbutton_new (GtkWindow *parent,
+ GtkDialogFlags flags,
+ const char *message,
+ const char *ok_button_text,
+ const char *check_button_label,
+ const char *gconf_key);
+void
+_gtk_error_dialog_from_gerror_run (GtkWindow *parent,
+ const char *title,
+ GError **gerror);
+void
+_gtk_error_dialog_from_gerror_show (GtkWindow *parent,
+ const char *title,
+ GError **gerror);
+void _gtk_error_dialog_run (GtkWindow *parent,
+ const gchar *format,
+ ...) G_GNUC_PRINTF (2, 3);
+void _gtk_info_dialog_run (GtkWindow *parent,
+ const gchar *format,
+ ...) G_GNUC_PRINTF (2, 3);
+GdkPixbuf * _g_icon_get_pixbuf (GIcon *icon,
+ int size,
+ GtkIconTheme *icon_theme);
+GdkPixbuf * get_mime_type_pixbuf (const char *mime_type,
+ int icon_size,
+ GtkIconTheme *icon_theme);
+int _gtk_icon_get_pixel_size (GtkWidget *widget,
+ GtkIconSize size);
+void show_help_dialog (GtkWindow *parent,
+ const char *section);
+void _gtk_container_remove_children
+ (GtkContainer *container,
+ gpointer start_after_this,
+ gpointer stop_before_this);
+int _gtk_container_get_pos (GtkContainer *container,
+ GtkWidget *child);
+guint _gtk_container_get_n_children (GtkContainer *container);
+GtkBuilder *
+ _gtk_builder_new_from_file (const char *filename,
+ const char *extension);
+GtkWidget *
+ _gtk_builder_get_widget (GtkBuilder *builder,
+ const char *name);
+GtkWidget * _gtk_combo_box_new_with_texts (const char *first_text,
+ ...);
+void _gtk_combo_box_append_texts (GtkComboBox *combo_box,
+ const char *first_text,
+ ...);
+GtkWidget * _gtk_image_new_from_xpm_data (char *xpm_data[]);
+GtkWidget * _gtk_image_new_from_inline (const guint8 *data);
+void _gtk_widget_get_screen_size (GtkWidget *widget,
+ int *width,
+ int *height);
+void _gtk_tree_path_list_free (GList *list);
+int _gtk_paned_get_position2 (GtkPaned *paned);
+void _gtk_paned_set_position2 (GtkPaned *paned,
+ int pos);
+void _g_launch_command (GtkWidget *parent,
+ const char *command,
+ const char *name,
+ GList *files);
+int _gtk_count_selected (GtkTreeSelection *selection);
+
+G_END_DECLS
+
+#endif
diff --git a/src/main.c b/src/main.c
index d18296d..573c178 100644
--- a/src/main.c
+++ b/src/main.c
@@ -3,7 +3,7 @@
/*
* Goo
*
- * Copyright (C) 2004 Free Software Foundation, Inc.
+ * Copyright (C) 2004-2009 Free Software Foundation, Inc.
*
* 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
@@ -21,39 +21,44 @@
*/
#include <config.h>
-#include <gnome.h>
-#include <libbonobo.h>
-#include <libgnomeui/gnome-window-icon.h>
-#include <libgnomevfs/gnome-vfs-init.h>
-#include <libgnomevfs/gnome-vfs-utils.h>
-#include <glade/glade.h>
+#include <brasero/brasero-medium-monitor.h>
#include <gst/gst.h>
-#include "file-utils.h"
+#include <glib.h>
+#include <unique/unique.h>
+#include "eggsmclient.h"
#include "goo-stock.h"
#include "gconf-utils.h"
#include "goo-window.h"
#include "typedefs.h"
#include "preferences.h"
#include "main.h"
-#include "goo-application.h"
#include "gtk-utils.h"
#include "glib-utils.h"
-#include "cd-drive.h"
-
-#ifdef HAVE_MMKEYS
-#include <X11/Xlib.h>
-#include <X11/XF86keysym.h>
-#include <gdk/gdkx.h>
-#endif /* HAVE_MMKEYS */
-
-#ifdef HAVE_LIBNOTIFY
+#ifdef ENABLE_NOTIFICATION
#include <libnotify/notify.h>
static NotifyNotification *notification = NULL;
+#endif /* ENABLE_NOTIFICATION */
+
+#define VOLUME_STEP 10 /* FIXME */
+
+enum {
+ COMMAND_UNUSED,
+ COMMAND_PLAY,
+ COMMAND_PLAY_PAUSE,
+ COMMAND_STOP,
+ COMMAND_NEXT_TRACK,
+ COMMAND_PREVIOUS_TRACK,
+ COMMAND_EJECT_DISK,
+ COMMAND_HIDE_SHOW,
+ COMMAND_VOLUME_UP,
+ COMMAND_vOLUME_DOWN,
+ COMMAND_QUIT,
+ COMMAND_PRESENT,
+ COMMAND_SET_DEVICE
+};
-#endif /* HAVE_LIBNOTIFY */
-
-GtkWindow *main_window = NULL;
+GtkWidget *main_window = NULL;
int AutoPlay = FALSE;
int PlayPause = FALSE;
int Stop = FALSE;
@@ -64,20 +69,13 @@ int HideShow = FALSE;
int VolumeUp = FALSE;
int VolumeDown = FALSE;
int Quit = FALSE;
-GList *Drives = NULL;
-
-static void prepare_app (void);
-static void initialize_data (void);
-static void release_data (void);
-static void init_session (char **argv);
-static gboolean session_is_restored (void);
-static gboolean load_session (void);
-static void init_mmkeys (void);
+static void release_data (void);
-static char *default_device = NULL;
-static BonoboObject *goo_application = NULL;
-static GOptionContext *context = NULL;
+static UniqueApp *application = NULL;
+static const char *program_argv0; /* argv[0] from main(); used as the command to restart the program */
+static char *default_device = NULL;
+static gboolean version = FALSE;
static const GOptionEntry options[] = {
{ "device", 'd', 0, G_OPTION_ARG_STRING, &default_device,
@@ -113,238 +111,209 @@ static const GOptionEntry options[] = {
{ "quit", '\0', 0, G_OPTION_ARG_NONE, &Quit,
N_("Quit the application"),
0 },
-
+
+ { "version", 'v', 0, G_OPTION_ARG_NONE, &version,
+ N_("Show version"), NULL },
+
{ NULL }
};
-/* -- Main -- */
+/* session management */
-int main (int argc,
- char **argv)
+static void
+goo_save_state (EggSMClient *client,
+ GKeyFile *state,
+ gpointer user_data)
{
- GnomeProgram *program;
- char *description;
- CORBA_Object factory;
+ const char *argv[2] = { NULL };
- if (! g_thread_supported ())
- g_thread_init (NULL);
+ argv[0] = program_argv0;
+ argv[1] = NULL;
+ egg_sm_client_set_restart_command (client, 1, argv);
- bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
- bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
- textdomain (GETTEXT_PACKAGE);
-
- description = g_strdup_printf ("- %s", _("Play CDs and save the tracks to disk as files"));
- context = g_option_context_new (description);
- g_free (description);
- g_option_context_add_main_entries (context, options, GETTEXT_PACKAGE);
- g_option_context_add_group (context, gst_init_get_option_group ());
- g_option_context_set_ignore_unknown_options (context, TRUE);
-
- program = gnome_program_init ("goobox", VERSION,
- LIBGNOMEUI_MODULE, argc, argv,
- GNOME_PARAM_GOPTION_CONTEXT, context,
- GNOME_PARAM_HUMAN_READABLE_NAME, _("CD Player"),
- GNOME_PARAM_APP_PREFIX, GOO_PREFIX,
- GNOME_PARAM_APP_SYSCONFDIR, GOO_SYSCONFDIR,
- GNOME_PARAM_APP_DATADIR, GOO_DATADIR,
- GNOME_PARAM_APP_LIBDIR, GOO_LIBDIR,
- NULL);
-
- if (! g_thread_supported ()) {
- g_thread_init (NULL);
- gdk_threads_init ();
- }
-
- if (! gnome_vfs_init ())
- g_error ("Cannot initialize the Virtual File System.");
- gnome_authentication_manager_init ();
- glade_gnome_init ();
-
-#ifdef HAVE_LIBNOTIFY
+ g_key_file_set_string (state, "Session", "/device", goo_player_get_device (goo_window_get_player (GOO_WINDOW (main_window))));
+}
- if (! notify_init ("goobox"))
- g_warning ("Cannot initialize notification system.");
-
-#endif /* HAVE_LIBNOTIFY */
- factory = bonobo_activation_activate_from_id ("OAFIID:GNOME_Goobox_Application_Factory",
- Bonobo_ACTIVATION_FLAG_EXISTING_ONLY,
- NULL, NULL);
+static void
+goo_session_manager_init (void)
+{
+ EggSMClient *client = NULL;
- if (factory != NULL) {
- CORBA_Environment env;
- GNOME_Goobox_Application app;
-
- CORBA_exception_init (&env);
+ client = egg_sm_client_get ();
+ g_signal_connect (client, "save-state", G_CALLBACK (goo_save_state), NULL);
+}
- app = bonobo_activation_activate_from_id ("OAFIID:GNOME_Goobox_Application", 0, NULL, &env);
- if (AutoPlay)
- GNOME_Goobox_Application_play (app, &env);
- else if (PlayPause)
- GNOME_Goobox_Application_play_pause (app, &env);
- else if (Stop)
- GNOME_Goobox_Application_stop (app, &env);
- else if (Next)
- GNOME_Goobox_Application_next (app, &env);
- else if (Prev)
- GNOME_Goobox_Application_prev (app, &env);
- else if (Eject)
- GNOME_Goobox_Application_eject (app, &env);
- else if (HideShow)
- GNOME_Goobox_Application_hide_show (app, &env);
- else if (VolumeUp)
- GNOME_Goobox_Application_volume_up (app, &env);
- else if (VolumeDown)
- GNOME_Goobox_Application_volume_down (app, &env);
- else if (Quit)
- GNOME_Goobox_Application_quit (app, &env);
- else
- GNOME_Goobox_Application_present (app, &env);
+static void
+goo_restore_session (EggSMClient *client)
+{
+ GKeyFile *state = NULL;
+ char *device;
+ BraseroDrive *drive;
- bonobo_object_release_unref (app, &env);
- CORBA_exception_free (&env);
+ state = egg_sm_client_get_state_file (client);
- gdk_notify_startup_complete ();
+ device = g_key_file_get_string (state, "Session", "device", NULL);
+ drive = main_get_drive_for_device (device);
+ main_window = goo_window_new (drive);
+ gtk_widget_show (main_window);
- exit (0);
- }
+ g_object_unref (drive);
+ g_free (device);
+}
- goo_stock_init ();
- init_session (argv);
- initialize_data ();
- prepare_app ();
- bonobo_main ();
+static UniqueResponse
+unique_app_message_received_cb (UniqueApp *unique_app,
+ UniqueCommand command,
+ UniqueMessageData *message,
+ guint time_,
+ gpointer user_data)
+{
+ UniqueResponse res;
- release_data ();
+ res = UNIQUE_RESPONSE_OK;
- return 0;
-}
+ switch (command) {
+ case UNIQUE_OPEN:
+ case UNIQUE_NEW:
+ /* FIXME */
+ break;
+ case COMMAND_PLAY:
+ goo_window_play (GOO_WINDOW (main_window));
+ break;
-/* Initialize application data. */
+ case COMMAND_PLAY_PAUSE:
+ goo_window_toggle_play (GOO_WINDOW (main_window));
+ break;
+ case COMMAND_STOP:
+ goo_window_stop (GOO_WINDOW (main_window));
+ break;
-static void
-initialize_data (void)
-{
- g_set_application_name (_("CD Player"));
- gtk_window_set_default_icon_name ("goobox");
+ case COMMAND_NEXT_TRACK:
+ goo_window_next (GOO_WINDOW (main_window));
+ break;
- eel_gconf_monitor_add ("/apps/goobox");
-
- Drives = scan_for_cdroms (FALSE, FALSE);
-}
+ case COMMAND_PREVIOUS_TRACK:
+ goo_window_prev (GOO_WINDOW (main_window));
+ break;
+ case COMMAND_EJECT_DISK:
+ goo_window_eject (GOO_WINDOW (main_window));
+ break;
-static void
-release_data (void)
-{
- if (goo_application != NULL)
- bonobo_object_unref (goo_application);
- eel_global_client_free ();
-
- if (Drives != NULL) {
- g_list_foreach (Drives, (GFunc) cd_drive_free, NULL);
- g_list_free (Drives);
- Drives = NULL;
- }
-}
+ case COMMAND_HIDE_SHOW:
+ goo_window_toggle_visibility (GOO_WINDOW (main_window));
+ break;
+ case COMMAND_VOLUME_UP:
+ {
+ int volume;
-CDDrive *
-get_drive_from_device (const char *device)
-{
- CDDrive *result = NULL;
- char *resolved_device = NULL;
- char *resolved_real_device = NULL;
- const char *real_device = NULL;
- GList *scan;
-
- if (device == NULL)
- return NULL;
-
- if (resolve_all_symlinks (device, &resolved_device) != GNOME_VFS_OK)
- resolved_device = NULL;
+ volume = goo_window_get_volume (GOO_WINDOW (main_window));
+ goo_window_set_volume (GOO_WINDOW (main_window), volume + VOLUME_STEP);
+ }
+ break;
- if (resolved_device == NULL)
- return NULL;
-
- device = get_path_from_uri (resolved_device);
- for (scan = Drives; scan; scan = scan->next) {
- CDDrive *drive = scan->data;
-
- if (drive->device == NULL)
- continue;
- if (resolve_all_symlinks (drive->device, &resolved_real_device) != GNOME_VFS_OK)
- continue;
- real_device = get_path_from_uri (resolved_real_device);
- if (strcmp (real_device, device) == 0) {
- result = drive;
- break;
+ case COMMAND_vOLUME_DOWN:
+ {
+ int volume;
+
+ volume = goo_window_get_volume (GOO_WINDOW (main_window));
+ goo_window_set_volume (GOO_WINDOW (main_window), volume - VOLUME_STEP);
}
- }
-
- g_free (resolved_device);
-
- return result;
-}
+ break;
+ case COMMAND_QUIT:
+ goo_window_close (GOO_WINDOW (main_window));
+ break;
-GtkWindow *
-get_window_from_device (const char *device)
-{
- CDDrive *device_drive;
- GList *scan;
-
- device_drive = get_drive_from_device (device);
- if (device_drive == NULL)
- return NULL;
-
- for (scan = window_list; scan; scan = scan->next) {
- GooWindow *window = scan->data;
+ case COMMAND_PRESENT:
+ if (GTK_WIDGET_VISIBLE (main_window))
+ gtk_window_present (GTK_WINDOW (main_window));
+ else
+ goo_window_toggle_visibility (GOO_WINDOW (main_window));
+ break;
- if (goo_player_get_drive (goo_window_get_player (window)) == device_drive)
- return (GtkWindow *) window;
- }
-
- return NULL;
-}
+ case COMMAND_SET_DEVICE:
+ {
+ char *device;
+ device = unique_message_data_get_text (message);
+ if (*device == '\0')
+ device = NULL;
-/* Create the windows. */
+ if (device != NULL) {
+ /* FIXME
+ GooPlayer *player;
+ CDDrive *current_drive;
+
+ player = goo_window_get_player (GOO_WINDOW (main_window));
+ current_drive = goo_player_get_drive (player);
+
+ if (current_drive == NULL) {
+ main_window = get_window_from_device (device);
+ if (main_window == NULL)
+ main_window = goo_window_new (device);
+ }
+ else
+ goo_window_set_device (GOO_WINDOW (main_window), device);
+ */
+ }
+ }
+ break;
+
+ default:
+ res = UNIQUE_RESPONSE_PASSTHROUGH;
+ break;
+ }
+
+ return res;
+}
static gboolean
-check_plugins (void)
+required_gstreamer_plugins_available (void)
{
- char *required_plugins[] = { "cdparanoiasrc", "gnomevfssink", "audioconvert", "volume" };
+ char *required_plugins[] = { "cdparanoiasrc", "audioconvert", "volume", "giosink" };
int i;
for (i = 0; i < G_N_ELEMENTS (required_plugins); i++) {
GstElement *element;
gboolean present;
-
+
element = gst_element_factory_make (required_plugins[i], NULL);
present = (element != NULL);
- if (element != NULL)
+ if (element != NULL)
gst_object_unref (GST_OBJECT (element));
-
+
if (! present)
return FALSE;
- }
+ }
return TRUE;
}
-static void
-prepare_app (void)
+static void
+release_data (void)
{
- if (! check_plugins ()) {
+ _g_object_unref (application);
+ eel_global_client_free ();
+}
+
+
+static void
+prepare_application (void)
+{
+ EggSMClient *client = NULL;
+
+ if (! required_gstreamer_plugins_available ()) {
GtkWidget *d;
d = _gtk_message_dialog_new (NULL,
0,
@@ -354,289 +323,241 @@ prepare_app (void)
GTK_STOCK_OK, GTK_RESPONSE_OK,
NULL);
g_signal_connect (G_OBJECT (d), "response",
- G_CALLBACK (bonobo_main_quit),
+ G_CALLBACK (gtk_main_quit),
NULL);
gtk_widget_show (d);
return;
}
-
- if (session_is_restored ())
- load_session ();
- else
- main_window = goo_window_new (default_device);
- gtk_widget_show (GTK_WIDGET (main_window));
-
- goo_application = goo_application_new (gdk_screen_get_default ());
-
-#ifdef HAVE_MMKEYS
- init_mmkeys ();
-#endif /* HAVE_MMKEYS */
-}
-
-
-/* SM support */
-
-
-/* The master client we use for SM */
-static GnomeClient *master_client = NULL;
-/* argv[0] from main(); used as the command to restart the program */
-static const char *program_argv0 = NULL;
+ application = unique_app_new_with_commands ("org.gnome.goobox", NULL,
+ "auto-play", COMMAND_PLAY,
+ "play-pause", COMMAND_PLAY_PAUSE,
+ "stop", COMMAND_STOP,
+ "next", COMMAND_NEXT_TRACK,
+ "prev", COMMAND_PREVIOUS_TRACK,
+ "eject", COMMAND_EJECT_DISK,
+ "hide-show", COMMAND_HIDE_SHOW,
+ "volume-up", COMMAND_VOLUME_UP,
+ "volume-down", COMMAND_vOLUME_DOWN,
+ "quit", COMMAND_QUIT,
+ "present", COMMAND_PRESENT,
+ "set-device", COMMAND_SET_DEVICE,
+ NULL);
+
+ if (unique_app_is_running (application)) {
+ if (default_device != NULL) {
+ UniqueMessageData *data;
+
+ data = unique_message_data_new ();
+ unique_message_data_set_text (data, default_device, -1);
+ unique_app_send_message (application, COMMAND_SET_DEVICE, data);
+
+ unique_message_data_free (data);
+ }
+ if (AutoPlay)
+ unique_app_send_message (application, COMMAND_PLAY, NULL);
+ else if (PlayPause)
+ unique_app_send_message (application, COMMAND_PLAY_PAUSE, NULL);
+ else if (Stop)
+ unique_app_send_message (application, COMMAND_STOP, NULL);
+ else if (Next)
+ unique_app_send_message (application, COMMAND_NEXT_TRACK, NULL);
+ else if (Prev)
+ unique_app_send_message (application, COMMAND_PREVIOUS_TRACK, NULL);
+ else if (Eject)
+ unique_app_send_message (application, COMMAND_EJECT_DISK, NULL);
+ else if (HideShow)
+ unique_app_send_message (application, COMMAND_HIDE_SHOW, NULL);
+ else if (VolumeUp)
+ unique_app_send_message (application, COMMAND_VOLUME_UP, NULL);
+ else if (VolumeDown)
+ unique_app_send_message (application, COMMAND_vOLUME_DOWN, NULL);
+ else if (Quit)
+ unique_app_send_message (application, COMMAND_QUIT, NULL);
-static void
-save_session (GnomeClient *client)
-{
- const char *prefix;
+ return;
+ }
- prefix = gnome_client_get_config_prefix (client);
- gnome_config_push_prefix (prefix);
+ if (! unique_app_is_running (application)) {
+ g_set_application_name (_("CD Player"));
+ gtk_window_set_default_icon_name ("goobox");
+ goo_stock_init ();
+ eel_gconf_monitor_add ("/apps/goobox");
+ g_signal_connect (application,
+ "message-received",
+ G_CALLBACK (unique_app_message_received_cb),
+ NULL);
+ }
- gnome_config_set_string ("Session/device", goo_player_get_device (goo_window_get_player (GOO_WINDOW (main_window))));
+ client = egg_sm_client_get ();
+ if (egg_sm_client_is_resumed (client)) {
+ goo_restore_session (client);
+ return;
+ }
- gnome_config_pop_prefix ();
- gnome_config_sync ();
+ gtk_widget_show (goo_window_new (NULL));
}
-/* save_yourself handler for the master client */
-static gboolean
-client_save_yourself_cb (GnomeClient *client,
- gint phase,
- GnomeSaveStyle save_style,
- gboolean shutdown,
- GnomeInteractStyle interact_style,
- gboolean fast,
- gpointer data)
+int main (int argc, char **argv)
{
- const char *prefix;
- char *argv[4] = { NULL };
+ char *description;
+ GOptionContext *context = NULL;
+ GError *error = NULL;
- save_session (client);
-
- prefix = gnome_client_get_config_prefix (client);
-
- /* Tell the session manager how to discard this save */
-
- argv[0] = "rm";
- argv[1] = "-rf";
- argv[2] = gnome_config_get_real_path (prefix);
- argv[3] = NULL;
- gnome_client_set_discard_command (client, 3, argv);
+ program_argv0 = argv[0];
- /* Tell the session manager how to clone or restart this instance */
+ if (! g_thread_supported ()) {
+ g_thread_init (NULL);
+ gdk_threads_init ();
+ }
- argv[0] = (char *) program_argv0;
- argv[1] = NULL; /* "--debug-session"; */
+ bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+ textdomain (GETTEXT_PACKAGE);
- gnome_client_set_clone_command (client, 1, argv);
- gnome_client_set_restart_command (client, 1, argv);
-
- return TRUE;
-}
-
-/* die handler for the master client */
-static void
-client_die_cb (GnomeClient *client, gpointer data)
-{
- if (! client->save_yourself_emitted)
- save_session (client);
- if (goo_application != NULL)
- bonobo_object_unref (goo_application);
- bonobo_main_quit ();
-}
-
-
-static void
-init_session (char **argv)
-{
- if (master_client != NULL)
- return;
-
- program_argv0 = argv[0];
+ description = g_strdup_printf ("- %s", _("Play CDs and save the tracks to disk as files"));
+ context = g_option_context_new (description);
+ g_free (description);
+ g_option_context_set_translation_domain (context, GETTEXT_PACKAGE);
+ g_option_context_add_main_entries (context, options, GETTEXT_PACKAGE);
+ g_option_context_add_group (context, gtk_get_option_group (TRUE));
+ g_option_context_add_group (context, egg_sm_client_get_option_group ());
+ g_option_context_add_group (context, gst_init_get_option_group ());
+ g_option_context_set_ignore_unknown_options (context, TRUE);
+ if (! g_option_context_parse (context, &argc, &argv, &error)) {
+ g_critical ("Failed to parse arguments: %s", error->message);
+ g_error_free (error);
+ g_option_context_free (context);
+ return EXIT_FAILURE;
+ }
+ g_option_context_free (context);
- master_client = gnome_master_client ();
+ if (version) {
+ g_print ("%s %s, Copyright (C) 2004-2009 Free Software Foundation, Inc.\n", PACKAGE_NAME, PACKAGE_VERSION);
+ return 0;
+ }
- g_signal_connect (master_client, "save_yourself",
- G_CALLBACK (client_save_yourself_cb),
- NULL);
+#ifdef ENABLE_NOTIFICATION
+ if (! notify_init ("goobox"))
+ g_warning ("Cannot initialize notification system.");
+#endif /* ENABLE_NOTIFICATION */
+ goo_session_manager_init ();
+ prepare_application ();
+
+ if (! unique_app_is_running (application)) {
+ gdk_threads_enter ();
+ gtk_main ();
+ gdk_threads_leave ();
+ }
- g_signal_connect (master_client, "die",
- G_CALLBACK (client_die_cb),
- NULL);
+ release_data ();
+
+ return 0;
}
-gboolean
-session_is_restored (void)
+/*
+CDDrive *
+get_drive_from_device (const char *device)
{
- gboolean restored;
+ CDDrive *result = NULL;
+ char *resolved_device = NULL;
+ char *resolved_real_device = NULL;
+ const char *real_device = NULL;
+ GList *scan;
- if (! master_client)
- return FALSE;
-
- restored = (gnome_client_get_flags (master_client) & GNOME_CLIENT_RESTORED) != 0;
+ if (device == NULL)
+ return NULL;
+
+ if (resolve_all_symlinks (device, &resolved_device) != GNOME_VFS_OK)
+ resolved_device = NULL;
- return restored;
+ if (resolved_device == NULL)
+ return NULL;
+
+ device = get_path_from_uri (resolved_device);
+ for (scan = Drives; scan; scan = scan->next) {
+ CDDrive *drive = scan->data;
+
+ if (drive->device == NULL)
+ continue;
+ if (resolve_all_symlinks (drive->device, &resolved_real_device) != GNOME_VFS_OK)
+ continue;
+ real_device = get_path_from_uri (resolved_real_device);
+ if (strcmp (real_device, device) == 0) {
+ result = drive;
+ break;
+ }
+ }
+
+ g_free (resolved_device);
+
+ return result;
}
-gboolean
-load_session (void)
+GtkWindow *
+get_window_from_device (const char *device)
{
- char *device;
-
- gnome_config_push_prefix (gnome_client_get_config_prefix (master_client));
-
- device = gnome_config_get_string ("Session/device");
- main_window = goo_window_new (device);
- g_free (device);
-
- gnome_config_pop_prefix ();
-
- return TRUE;
-}
-
-
-/* From rhythmbox/shell/rb-shell-player.c
- *
- * Copyright (C) 2002, 2003 Jorn Baayen <jorn nl linux org>
- * Copyright (C) 2002,2003 Colin Walters <walters debian org>
- *
- * Modified by Paolo Bacchilega for Goobox
- *
- * Copyright (C) 2005 Paolo Bacchilega <paobac cvs gnome org>
- */
-
-#ifdef HAVE_MMKEYS
+ CDDrive *device_drive;
+ GList *scan;
+
+ device_drive = get_drive_from_device (device);
+ if (device_drive == NULL)
+ return NULL;
+
+ for (scan = window_list; scan; scan = scan->next) {
+ GooWindow *window = scan->data;
-static void
-grab_mmkey (int key_code,
- GdkWindow *root)
-{
- gdk_error_trap_push ();
-
- XGrabKey (GDK_DISPLAY (), key_code,
- 0,
- GDK_WINDOW_XID (root), True,
- GrabModeAsync, GrabModeAsync);
- XGrabKey (GDK_DISPLAY (), key_code,
- Mod2Mask,
- GDK_WINDOW_XID (root), True,
- GrabModeAsync, GrabModeAsync);
- XGrabKey (GDK_DISPLAY (), key_code,
- Mod5Mask,
- GDK_WINDOW_XID (root), True,
- GrabModeAsync, GrabModeAsync);
- XGrabKey (GDK_DISPLAY (), key_code,
- LockMask,
- GDK_WINDOW_XID (root), True,
- GrabModeAsync, GrabModeAsync);
- XGrabKey (GDK_DISPLAY (), key_code,
- Mod2Mask | Mod5Mask,
- GDK_WINDOW_XID (root), True,
- GrabModeAsync, GrabModeAsync);
- XGrabKey (GDK_DISPLAY (), key_code,
- Mod2Mask | LockMask,
- GDK_WINDOW_XID (root), True,
- GrabModeAsync, GrabModeAsync);
- XGrabKey (GDK_DISPLAY (), key_code,
- Mod5Mask | LockMask,
- GDK_WINDOW_XID (root), True,
- GrabModeAsync, GrabModeAsync);
- XGrabKey (GDK_DISPLAY (), key_code,
- Mod2Mask | Mod5Mask | LockMask,
- GDK_WINDOW_XID (root), True,
- GrabModeAsync, GrabModeAsync);
+ if (goo_player_get_drive (goo_window_get_player (window)) == device_drive)
+ return (GtkWindow *) window;
+ }
- gdk_flush ();
- if (gdk_error_trap_pop ())
- debug (DEBUG_INFO, "Error grabbing key");
+ return NULL;
}
+*/
-static GdkFilterReturn
-filter_mmkeys (GdkXEvent *xevent,
- GdkEvent *event,
- gpointer data)
+BraseroDrive *
+main_get_most_likely_drive (void)
{
- XEvent *xev;
- XKeyEvent *key;
-
- xev = (XEvent *) xevent;
- if (xev->type != KeyPress)
- return GDK_FILTER_CONTINUE;
+ BraseroDrive *result;
+ BraseroMediumMonitor *monitor;
+ GSList *drivers;
+
+ monitor = brasero_medium_monitor_get_default ();
+ drivers = brasero_medium_monitor_get_drives (monitor, BRASERO_MEDIA_TYPE_AUDIO | BRASERO_MEDIA_TYPE_CD);
+ if (drivers != NULL)
+ result = g_object_ref ((BraseroDrive *) drivers->data);
+ else
+ result = NULL;
- key = (XKeyEvent *) xevent;
+ g_slist_foreach (drivers, (GFunc) g_object_unref, NULL);
+ g_slist_free (drivers);
+ g_object_unref (monitor);
- if (XKeysymToKeycode (GDK_DISPLAY (), XF86XK_AudioPlay) == key->keycode) {
- goo_window_toggle_play (GOO_WINDOW (main_window));
- return GDK_FILTER_REMOVE;
- }
- else if (XKeysymToKeycode (GDK_DISPLAY (), XF86XK_AudioPause) == key->keycode) {
- goo_window_pause (GOO_WINDOW (main_window));
- return GDK_FILTER_REMOVE;
- }
- else if (XKeysymToKeycode (GDK_DISPLAY (), XF86XK_AudioStop) == key->keycode) {
- goo_window_stop (GOO_WINDOW (main_window));
- return GDK_FILTER_REMOVE;
- }
- else if (XKeysymToKeycode (GDK_DISPLAY (), XF86XK_AudioPrev) == key->keycode) {
- goo_window_prev (GOO_WINDOW (main_window));
- return GDK_FILTER_REMOVE;
- }
- else if (XKeysymToKeycode (GDK_DISPLAY (), XF86XK_AudioNext) == key->keycode) {
- goo_window_next (GOO_WINDOW (main_window));
- return GDK_FILTER_REMOVE;
- }
- else if (XKeysymToKeycode (GDK_DISPLAY (), XF86XK_Eject) == key->keycode) {
- goo_window_eject (GOO_WINDOW (main_window));
- return GDK_FILTER_REMOVE;
- }
- else
- return GDK_FILTER_CONTINUE;
+ return result;
}
-static void
-init_mmkeys (void)
+BraseroDrive *
+main_get_drive_for_device (const char *device)
{
- gint keycodes[] = {0, 0, 0, 0, 0, 0};
- GdkDisplay *display;
- GdkScreen *screen;
- GdkWindow *root;
- guint i, j;
-
- keycodes[0] = XKeysymToKeycode (GDK_DISPLAY (), XF86XK_AudioPlay);
- keycodes[1] = XKeysymToKeycode (GDK_DISPLAY (), XF86XK_AudioStop);
- keycodes[2] = XKeysymToKeycode (GDK_DISPLAY (), XF86XK_AudioPrev);
- keycodes[3] = XKeysymToKeycode (GDK_DISPLAY (), XF86XK_AudioNext);
- keycodes[4] = XKeysymToKeycode (GDK_DISPLAY (), XF86XK_AudioPause);
- keycodes[5] = XKeysymToKeycode (GDK_DISPLAY (), XF86XK_Eject);
-
- display = gdk_display_get_default ();
-
- for (i = 0; i < gdk_display_get_n_screens (display); i++) {
- screen = gdk_display_get_screen (display, i);
-
- if (screen != NULL) {
- root = gdk_screen_get_root_window (screen);
-
- for (j = 0; j < G_N_ELEMENTS (keycodes) ; j++) {
- if (keycodes[j] != 0)
- grab_mmkey (keycodes[j], root);
- }
+ BraseroDrive *result = NULL;
+ BraseroMediumMonitor *monitor;
- gdk_window_add_filter (root, filter_mmkeys, NULL);
- }
- }
-}
+ monitor = brasero_medium_monitor_get_default ();
+ result = brasero_medium_monitor_get_drive (monitor, device);
+ g_object_unref (monitor);
-#endif /* HAVE_MMKEYS */
+ return result;
+}
-#ifdef HAVE_LIBNOTIFY
+#ifdef ENABLE_NOTIFICATION
static gboolean
@@ -673,7 +594,7 @@ notify_action_stop_cb (NotifyNotification *notification,
}
-#endif /* HAVE_LIBNOTIFY */
+#endif /* ENABLE_NOTIFICATION */
void
@@ -681,7 +602,7 @@ system_notify (GooWindow *window,
const char *title,
const char *msg)
{
-#ifdef HAVE_LIBNOTIFY
+#ifdef ENABLE_NOTIFICATION
GtkWidget *tray_icon;
GdkScreen *screen = NULL;
int x = -1, y = -1;
@@ -706,7 +627,7 @@ system_notify (GooWindow *window,
}
if (notification == NULL) {
- caps = notify_get_server_caps();
+ caps = notify_get_server_caps ();
if (caps != NULL) {
for (c = caps; c != NULL; c = c->next) {
if (strcmp ((char*)c->data, "actions") == 0) {
@@ -747,5 +668,5 @@ system_notify (GooWindow *window,
notify_notification_show (notification, NULL);
-#endif /* HAVE_LIBNOTIFY */
+#endif /* ENABLE_NOTIFICATION */
}
diff --git a/src/main.h b/src/main.h
index fc25060..ad8d327 100644
--- a/src/main.h
+++ b/src/main.h
@@ -23,19 +23,22 @@
#ifndef MAIN_H
#define MAIN_H
+#include <brasero/brasero-drive.h>
#include "goo-window.h"
-#include "cd-drive.h"
-extern GtkWindow *main_window;
+extern GtkWidget *main_window;
extern GList *window_list;
extern int AutoPlay;
extern int HideShow;
-extern GList *Drives;
-void system_notify (GooWindow *window,
- const char *title,
- const char *msg);
+/*
CDDrive * get_drive_from_device (const char *device);
GtkWindow * get_window_from_device (const char *device);
+*/
+BraseroDrive * main_get_most_likely_drive (void);
+BraseroDrive * main_get_drive_for_device (const char *device);
+void system_notify (GooWindow *window,
+ const char *title,
+ const char *msg);
#endif /* MAIN_H */
diff --git a/src/preferences.c b/src/preferences.c
index b3ba6a6..50b96d8 100644
--- a/src/preferences.c
+++ b/src/preferences.c
@@ -21,12 +21,10 @@
*/
#include <string.h>
-#include <libgnome/libgnome.h>
#include <gconf/gconf-client.h>
#include "typedefs.h"
#include "preferences.h"
#include "main.h"
-#include "file-utils.h"
#include "gconf-utils.h"
#include "goo-window.h"
@@ -251,5 +249,16 @@ preferences_set_sort_type (GtkSortType i_value)
gboolean
preferences_get_use_sound_juicer (void)
{
- return eel_gconf_get_boolean (PREF_GENERAL_USE_SJ, FALSE) && is_program_in_path (SOUND_JUICER_EXE);
+ char *path;
+ gboolean result;
+
+ if (! eel_gconf_get_boolean (PREF_GENERAL_USE_SJ, FALSE))
+ return FALSE;
+
+ path = g_find_program_in_path (SOUND_JUICER_EXE);
+ result = path != NULL;
+
+ g_free (path);
+
+ return result;
}
diff --git a/src/preferences.h b/src/preferences.h
index 389be42..6508bc6 100644
--- a/src/preferences.h
+++ b/src/preferences.h
@@ -49,7 +49,6 @@
#define PREF_EXTRACT_DESTINATION "/apps/goobox/dialogs/extract/destination"
#define PREF_EXTRACT_FILETYPE "/apps/goobox/dialogs/extract/file_type"
#define PREF_EXTRACT_SAVE_PLAYLIST "/apps/goobox/dialogs/extract/save_playlist"
-#define PREF_EXTRACT_FIRST_TIME "/apps/goobox/dialogs/extract/first_time"
#define PREF_RIPPER_VIEW_DISTINATION "/apps/goobox/dialogs/ripper/view_destination"
#define PREF_ENCODER_OGG_QUALITY "/apps/goobox/encoder/ogg_quality"
diff --git a/src/track-info.c b/src/track-info.c
index de83eec..3d1c543 100644
--- a/src/track-info.c
+++ b/src/track-info.c
@@ -21,7 +21,7 @@
*/
#include <config.h>
-#include <gnome.h>
+#include <glib/gi18n.h>
#include <gst/gst.h>
#include "track-info.h"
diff --git a/src/typedefs.h b/src/typedefs.h
index 56a4f5e..9ff7e4c 100644
--- a/src/typedefs.h
+++ b/src/typedefs.h
@@ -41,5 +41,12 @@ typedef enum {
} WindowSortMethod;
+typedef void (*DataFunc) (gpointer user_data);
+typedef void (*ReadyFunc) (GError *error,
+ gpointer user_data);
+typedef void (*ReadyCallback) (GObject *object,
+ GError *error,
+ gpointer user_data);
+
#endif /* TYPEDEFS_H */
diff --git a/src/ui.h b/src/ui.h
index 9c1fd66..4152cc1 100644
--- a/src/ui.h
+++ b/src/ui.h
@@ -25,7 +25,6 @@
#include <config.h>
-#include <gnome.h>
#include "actions.h"
#include "goo-stock.h"
@@ -37,7 +36,7 @@ static GtkActionEntry action_entries[] = {
{ "HelpMenu", NULL, N_("_Help") },
{ "CDCoverMenu", NULL, N_("C_over") },
- { "About", GNOME_STOCK_ABOUT,
+ { "About", GTK_STOCK_ABOUT,
NULL, NULL,
N_("Information about the program"),
G_CALLBACK (activate_action_about) },
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]