[gthumb/ext: 25/79] Added the ability to activate/deactivate extensions



commit 539153c3bbc92f2aee96081b318ad87debd8f07d
Author: Paolo Bacchilega <paobac src gnome org>
Date:   Sat Jun 27 23:45:35 2009 +0200

    Added the ability to activate/deactivate extensions

 data/gthumb.schemas.in                             |   14 +
 data/ui/Makefile.am                                |    1 +
 data/ui/extensions.ui                              |  127 ++++++
 data/ui/preferences.ui                             |    4 +-
 extensions/catalogs/catalogs.extension.in.in       |    7 +-
 extensions/comments/comments.extension.in.in       |    6 +-
 extensions/exiv2/exiv2.extension.in.in             |    6 +-
 .../file_manager/file_manager.extension.in.in      |    8 +-
 extensions/file_tools/file_tools.extension.in.in   |    8 +-
 extensions/file_viewer/file_viewer.extension.in.in |    6 +-
 .../image_viewer/image_viewer.extension.in.in      |    9 +-
 extensions/search/search.extension.in.in           |    6 +-
 gthumb/Makefile.am                                 |    3 +
 gthumb/dlg-extensions.c                            |  441 ++++++++++++++++++++
 gthumb/dlg-extensions.h                            |   30 ++
 gthumb/dlg-personalize-filters.c                   |   26 +-
 gthumb/gth-browser-actions-callbacks.c             |    9 +
 gthumb/gth-browser-actions-callbacks.h             |    1 +
 gthumb/gth-browser-actions-entries.h               |    5 +
 gthumb/gth-browser-ui.h                            |    1 +
 gthumb/gth-extensions.c                            |  175 +++++++--
 gthumb/gth-extensions.h                            |   75 ++--
 gthumb/gth-main.c                                  |   81 ++++-
 gthumb/gth-preferences.h                           |    1 +
 gthumb/gthumb-error.h                              |    5 +
 gthumb/main.c                                      |   26 +-
 gthumb/main.h                                      |   32 ++
 po/POTFILES.in                                     |    1 +
 28 files changed, 994 insertions(+), 120 deletions(-)
---
diff --git a/data/gthumb.schemas.in b/data/gthumb.schemas.in
index 0acd2bc..e846034 100644
--- a/data/gthumb.schemas.in
+++ b/data/gthumb.schemas.in
@@ -95,6 +95,20 @@
 	</locale>
       </schema>
 
+      <schema>
+	<key>/schemas/apps/gthumb/general/active_extensions</key>
+	<applyto>/apps/gthumb/general/active_extensions</applyto>
+	<owner>gthumb</owner>
+	<type>list</type>
+	<list_type>string</list_type>
+	<default>[]</default>
+	<locale name="C">
+	  <short></short>
+	  <long>
+	  </long>
+	</locale>
+      </schema>
+
       <!-- Browser -->
 
       <schema>
diff --git a/data/ui/Makefile.am b/data/ui/Makefile.am
index 08a6c66..f57c614 100644
--- a/data/ui/Makefile.am
+++ b/data/ui/Makefile.am
@@ -1,6 +1,7 @@
 uidir = $(datadir)/gthumb/ui
 ui_DATA = 			\
 	bookmarks.ui		\
+	extensions.ui		\
 	filter-editor.ui	\
 	personalize-filters.ui	\
 	preferences.ui		\
diff --git a/data/ui/extensions.ui b/data/ui/extensions.ui
new file mode 100644
index 0000000..fba62e7
--- /dev/null
+++ b/data/ui/extensions.ui
@@ -0,0 +1,127 @@
+<?xml version="1.0"?>
+<interface>
+  <requires lib="gtk+" version="2.16"/>
+  <!-- interface-naming-policy project-wide -->
+  <object class="GtkDialog" id="extensions_dialog">
+    <property name="border_width">5</property>
+    <property name="title" translatable="yes">gthumb Extensions</property>
+    <property name="type_hint">normal</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">8</property>
+        <child>
+          <object class="GtkHBox" id="hbox1">
+            <property name="visible">True</property>
+            <property name="border_width">6</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="GtkLabel" id="extensions_label">
+                    <property name="visible">True</property>
+                    <property name="xalign">0</property>
+                    <property name="label" translatable="yes">_Available extensions:</property>
+                    <property name="use_underline">True</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkScrolledWindow" id="extensions_scrolledwindow">
+                    <property name="width_request">300</property>
+                    <property name="height_request">350</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>
+                      <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="about_button">
+                <property name="label" translatable="yes">gtk-about</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</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="preferences_button">
+                <property name="label" translatable="yes">gtk-preferences</property>
+                <property name="visible">True</property>
+                <property name="sensitive">False</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</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="close_button">
+                <property name="label" translatable="yes">gtk-close</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</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>
+          </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="0">about_button</action-widget>
+      <action-widget response="0">preferences_button</action-widget>
+      <action-widget response="0">close_button</action-widget>
+    </action-widgets>
+  </object>
+</interface>
diff --git a/data/ui/preferences.ui b/data/ui/preferences.ui
index e48e8d7..df7e7be 100644
--- a/data/ui/preferences.ui
+++ b/data/ui/preferences.ui
@@ -4,7 +4,7 @@
   <!-- interface-naming-policy toplevel-contextual -->
   <object class="GtkDialog" id="preferences_dialog">
     <property name="border_width">6</property>
-    <property name="title" translatable="yes">gThumb Preferences</property>
+    <property name="title" translatable="yes">gthumb Preferences</property>
     <property name="resizable">False</property>
     <property name="type_hint">dialog</property>
     <property name="has_separator">False</property>
@@ -181,8 +181,8 @@
                                 <child>
                                   <object class="GtkFileChooserButton" id="startup_dir_filechooserbutton">
                                     <property name="visible">True</property>
-                                    <property name="action">select-folder</property>
                                     <property name="local_only">False</property>
+                                    <property name="action">select-folder</property>
                                     <property name="title" translatable="yes">Choose startup folder</property>
                                   </object>
                                   <packing>
diff --git a/extensions/catalogs/catalogs.extension.in.in b/extensions/catalogs/catalogs.extension.in.in
index 2874d45..0c60d69 100644
--- a/extensions/catalogs/catalogs.extension.in.in
+++ b/extensions/catalogs/catalogs.extension.in.in
@@ -1,9 +1,10 @@
 [Extension]
 _Name=Catalogs
 _Description=Allow to create file collections.
-Authors=Paolo Bacchilega <paobac src gnome org>
-Copyright=Copyright © 2008-2009 Paolo Bacchilega
-Version=1
+_Authors=Paolo Bacchilega <paobac src gnome org>
+Copyright=Copyright © 2008-2009 The Free Software Foundation, Inc.
+Version=1.0
+URL=http://live.gnome.org/gthumb
 
 [Loader]
 Type=module
diff --git a/extensions/comments/comments.extension.in.in b/extensions/comments/comments.extension.in.in
index 297016c..9b9c248 100644
--- a/extensions/comments/comments.extension.in.in
+++ b/extensions/comments/comments.extension.in.in
@@ -1,9 +1,9 @@
 [Extension]
 _Name=Comments and tags
 _Description=Allow to add comments and tags to files.
-Authors=Paolo Bacchilega <paobac src gnome org>
-Copyright=Copyright © 2009 Paolo Bacchilega
-Version=1
+_Authors=gthumb development team
+Copyright=Copyright © 2009 The Free Software Foundation, Inc.
+Version=1.0
 
 [Loader]
 Type=module
diff --git a/extensions/exiv2/exiv2.extension.in.in b/extensions/exiv2/exiv2.extension.in.in
index 9454099..37c2bd0 100644
--- a/extensions/exiv2/exiv2.extension.in.in
+++ b/extensions/exiv2/exiv2.extension.in.in
@@ -1,9 +1,9 @@
 [Extension]
 _Name=Image metadata
 _Description=Read and write exif/iptc and xmp metadata.
-Authors=Paolo Bacchilega <paobac src gnome org>
-Copyright=Copyright © 2009 Paolo Bacchilega
-Version=1
+_Authors=gthumb development team
+Copyright=Copyright © 2009 The Free Software Foundation, Inc.
+Version=1.0
 
 [Loader]
 Type=module
diff --git a/extensions/file_manager/file_manager.extension.in.in b/extensions/file_manager/file_manager.extension.in.in
index d0f62bb..7f6c746 100644
--- a/extensions/file_manager/file_manager.extension.in.in
+++ b/extensions/file_manager/file_manager.extension.in.in
@@ -1,9 +1,9 @@
 [Extension]
-_Name=File Manager
+_Name=File manager
 _Description=File manager operations.
-Authors=Paolo Bacchilega <paobac src gnome org>
-Copyright=Copyright © 2009 Paolo Bacchilega
-Version=1
+_Authors=gthumb development team
+Copyright=Copyright © 2009 The Free Software Foundation, Inc.
+Version=1.0
 
 [Loader]
 Type=module
diff --git a/extensions/file_tools/file_tools.extension.in.in b/extensions/file_tools/file_tools.extension.in.in
index a93016f..62848a1 100644
--- a/extensions/file_tools/file_tools.extension.in.in
+++ b/extensions/file_tools/file_tools.extension.in.in
@@ -1,9 +1,9 @@
 [Extension]
-_Name=Image Tools
+_Name=Image tools
 _Description=Basic tools to modify images.
-Authors=Paolo Bacchilega <paobac src gnome org>
-Copyright=Copyright © 2009 Paolo Bacchilega
-Version=1
+_Authors=gthumb development team
+Copyright=Copyright © 2009 The Free Software Foundation, Inc.
+Version=1.0
 
 [Loader]
 Type=module
diff --git a/extensions/file_viewer/file_viewer.extension.in.in b/extensions/file_viewer/file_viewer.extension.in.in
index 00123f5..9a557a4 100644
--- a/extensions/file_viewer/file_viewer.extension.in.in
+++ b/extensions/file_viewer/file_viewer.extension.in.in
@@ -1,9 +1,5 @@
 [Extension]
-_Name=Image Viewer
-_Description=Allows to view images.
-Authors=Paolo Bacchilega <paobac src gnome org>
-Copyright=Copyright © 2009 Paolo Bacchilega
-Version=1
+Mandatory=true
 
 [Loader]
 Type=module
diff --git a/extensions/image_viewer/image_viewer.extension.in.in b/extensions/image_viewer/image_viewer.extension.in.in
index 00123f5..0a3d3ac 100644
--- a/extensions/image_viewer/image_viewer.extension.in.in
+++ b/extensions/image_viewer/image_viewer.extension.in.in
@@ -1,10 +1,11 @@
 [Extension]
-_Name=Image Viewer
+_Name=Image viewer
 _Description=Allows to view images.
-Authors=Paolo Bacchilega <paobac src gnome org>
-Copyright=Copyright © 2009 Paolo Bacchilega
-Version=1
+_Authors=gthumb development team
+Copyright=Copyright © 2009 The Free Software Foundation, Inc.
+Version=1.0
 
 [Loader]
+Requires=file_viewer
 Type=module
 File=%LIBRARY%
diff --git a/extensions/search/search.extension.in.in b/extensions/search/search.extension.in.in
index 37fb172..b4dbb70 100644
--- a/extensions/search/search.extension.in.in
+++ b/extensions/search/search.extension.in.in
@@ -1,9 +1,9 @@
 [Extension]
 _Name=Search
 _Description=Allows to search for files.
-Authors=Paolo Bacchilega <paobac src gnome org>
-Copyright=Copyright © 2008-2009 Paolo Bacchilega
-Version=1
+_Authors=gthumb development team
+Copyright=Copyright © 2008-2009 The Free Software Foundation, Inc.
+Version=1.0
 
 [Loader]
 Type=module
diff --git a/gthumb/Makefile.am b/gthumb/Makefile.am
index 8308dc8..adbe63a 100644
--- a/gthumb/Makefile.am
+++ b/gthumb/Makefile.am
@@ -92,6 +92,7 @@ PUBLIC_HEADER_FILES = 					\
 	gth-window.h					\
 	gthumb-error.h					\
 	gtk-utils.h					\
+	main.h						\
 	pixbuf-io.h					\
 	pixbuf-utils.h					\
 	typedefs.h					\
@@ -101,6 +102,7 @@ PUBLIC_HEADER_FILES = 					\
 PRIVATE_HEADER_FILES = 					\
 	dlg-bookmarks.h					\
 	dlg-edit-metadata.h				\
+	dlg-extensions.h				\
 	gth-browser-actions-callbacks.h			\
 	gth-browser-actions-entries.h			\
 	gth-browser-ui.h				\
@@ -120,6 +122,7 @@ gthumb_SOURCES = 					\
 	$(PRIVATE_HEADER_FILES)				\
 	dlg-bookmarks.c					\
 	dlg-edit-metadata.c				\
+	dlg-extensions.c				\
 	dlg-personalize-filters.c			\
 	dlg-preferences.c				\
 	dlg-sort-order.c				\
diff --git a/gthumb/dlg-extensions.c b/gthumb/dlg-extensions.c
new file mode 100644
index 0000000..d3eaca2
--- /dev/null
+++ b/gthumb/dlg-extensions.c
@@ -0,0 +1,441 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ *  GThumb
+ *
+ *  Copyright (C) 2009 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
+ *  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 "gconf-utils.h"
+#include "glib-utils.h"
+#include "gth-browser.h"
+#include "gth-main.h"
+#include "gth-preferences.h"
+#include "gtk-utils.h"
+#include "main.h"
+
+
+#define GET_WIDGET(name) _gtk_builder_get_widget (data->builder, (name))
+#define DEFAULT_ICON "application-x-executable"
+
+
+typedef struct {
+	GthBrowser   *browser;
+	GtkBuilder   *builder;
+	GtkWidget    *dialog;
+	GtkWidget    *list_view;
+	GtkListStore *list_store;
+	GSList       *active_extensions;
+} DialogData;
+
+
+static gboolean
+list_equal (GSList *list1,
+	    GSList *list2)
+{
+	GSList *sscan1;
+
+	if (g_slist_length (list1) != g_slist_length (list2))
+		return FALSE;
+
+	for (sscan1 = list1; sscan1; sscan1 = sscan1->next) {
+		char   *name1 = sscan1->data;
+		GSList *sscan2;
+
+		for (sscan2 = list2; sscan2; sscan2 = sscan2->next) {
+			char *name2 = sscan2->data;
+
+			if (strcmp (name1, name2) == 0)
+				break;
+		}
+
+		if (sscan2 == NULL)
+			return FALSE;
+	}
+
+	return TRUE;
+}
+
+
+/* called when the main dialog is closed. */
+static void
+destroy_cb (GtkWidget  *widget,
+	    DialogData *data)
+{
+	GSList              *active_extensions;
+	GthExtensionManager *manager;
+	GList               *names;
+	GList               *scan;
+
+	active_extensions = NULL;
+	manager = gth_main_get_default_extension_manager ();
+	names = gth_extension_manager_get_extensions (manager);
+	for (scan = names; scan; scan = scan->next) {
+		char                    *name = scan->data;
+		GthExtensionDescription *description;
+
+		description = gth_extension_manager_get_description (manager, name);
+		if (gth_extension_description_is_active (description))
+			active_extensions = g_slist_prepend (active_extensions, g_strdup (name));
+	}
+	active_extensions = g_slist_reverse (active_extensions);
+	eel_gconf_set_string_list (PREF_ACTIVE_EXTENSIONS, active_extensions);
+
+	if (! list_equal (active_extensions, data->active_extensions)) {
+		GtkWidget *dialog;
+		int        response;
+
+		dialog = _gtk_message_dialog_new (GTK_WINDOW (data->browser),
+						  GTK_DIALOG_MODAL,
+						  GTK_STOCK_DIALOG_WARNING,
+						  _("Restart required"),
+						  _("You need to restart gthumb for these changes to take effect"),
+						  _("_Continue"), GTK_RESPONSE_CANCEL,
+						  _("_Restart"), GTK_RESPONSE_OK,
+						  NULL);
+		response = gtk_dialog_run (GTK_DIALOG (dialog));
+		gtk_widget_destroy (dialog);
+
+		if (response == GTK_RESPONSE_OK)
+			gth_restart ();
+	}
+
+	gth_browser_set_dialog (data->browser, "extensions", NULL);
+
+	g_slist_foreach (active_extensions, (GFunc) g_free, NULL);
+	g_slist_free (active_extensions);
+
+	g_slist_foreach (data->active_extensions, (GFunc) g_free, NULL);
+	g_slist_free (data->active_extensions);
+	g_object_unref (data->builder);
+	g_free (data);
+}
+
+
+static void
+extension_description_data_func_cb (GtkTreeViewColumn *tree_column,
+				    GtkCellRenderer   *cell,
+				    GtkTreeModel      *tree_model,
+				    GtkTreeIter       *iter,
+				    gpointer           user_data)
+{
+	GthExtensionDescription *description;
+	char                    *text;
+
+	gtk_tree_model_get (tree_model, iter, 0, &description, -1);
+
+	text = g_markup_printf_escaped ("<b>%s</b>\n%s", description->name, description->description);
+	g_object_set (G_OBJECT (cell), "markup", text, NULL);
+	g_object_set (G_OBJECT (cell), "sensitive", gth_extension_description_is_active (description), NULL);
+
+	g_free (text);
+	g_object_unref (description);
+}
+
+
+static void
+extension_icon_data_func_cb (GtkTreeViewColumn *tree_column,
+		 	     GtkCellRenderer   *cell,
+			     GtkTreeModel      *tree_model,
+			     GtkTreeIter       *iter,
+			     gpointer           user_data)
+{
+	GthExtensionDescription *description;
+
+	gtk_tree_model_get (tree_model, iter, 0, &description, -1);
+	if (description->icon_name != NULL)
+		g_object_set (G_OBJECT (cell), "icon-name", description->icon_name, NULL);
+	else
+		g_object_set (G_OBJECT (cell), "icon-name", DEFAULT_ICON, NULL);
+	g_object_set (G_OBJECT (cell), "sensitive", gth_extension_description_is_active (description), NULL);
+
+	g_object_unref (description);
+}
+
+
+static void
+extension_active_data_func_cb (GtkTreeViewColumn *tree_column,
+		 	       GtkCellRenderer   *cell,
+			       GtkTreeModel      *tree_model,
+			       GtkTreeIter       *iter,
+			       gpointer           user_data)
+{
+	GthExtensionDescription *description;
+
+	gtk_tree_model_get (tree_model, iter, 0, &description, -1);
+	g_object_set (G_OBJECT (cell), "active", gth_extension_description_is_active (description), NULL);
+
+	g_object_unref (description);
+}
+
+
+static void
+cell_renderer_toggle_toggled_cb (GtkCellRendererToggle *cell_renderer,
+				 char                  *path,
+                                 gpointer               user_data)
+{
+	DialogData  *data = user_data;
+	GtkTreePath *tree_path;
+	GtkTreeIter  iter;
+
+	tree_path = gtk_tree_path_new_from_string (path);
+	if (tree_path == NULL)
+		return;
+
+	if (gtk_tree_model_get_iter (GTK_TREE_MODEL (data->list_store), &iter, tree_path)) {
+		GthExtensionDescription *description;
+		GError                  *error = NULL;
+
+		gtk_tree_model_get (GTK_TREE_MODEL (data->list_store), &iter, 0, &description, -1);
+		if (! gth_extension_description_is_active (description)) {
+			if (! gth_extension_manager_activate (gth_main_get_default_extension_manager (), description->id, &error))
+				_gtk_error_dialog_from_gerror_run (GTK_WINDOW (data->dialog), _("Could not activate the extension"), &error);
+			else
+				gtk_list_store_set (data->list_store, &iter, 0, description,-1);
+		}
+		else {
+			if (! gth_extension_manager_deactivate (gth_main_get_default_extension_manager (), description->id, &error))
+				_gtk_error_dialog_from_gerror_run (GTK_WINDOW (data->dialog), _("Could not deactivate the extension"), &error);
+			else
+				gtk_list_store_set (data->list_store, &iter, 0, description,-1);
+		}
+
+		g_object_unref (description);
+	}
+
+	gtk_tree_path_free (tree_path);
+}
+
+
+static void
+add_columns (GtkTreeView *treeview,
+	     DialogData  *data)
+{
+	GtkCellRenderer   *renderer;
+	GtkTreeViewColumn *column;
+
+	/* the name column. */
+
+	column = gtk_tree_view_column_new ();
+	gtk_tree_view_column_set_title (column, _("Extension"));
+
+	renderer = gtk_cell_renderer_pixbuf_new ();
+	gtk_tree_view_column_pack_start (column, renderer, FALSE);
+	g_object_set (renderer, "stock-size", GTK_ICON_SIZE_BUTTON, NULL);
+	gtk_tree_view_column_set_cell_data_func (column, renderer, extension_icon_data_func_cb, data, NULL);
+
+	renderer = gtk_cell_renderer_text_new ();
+        gtk_tree_view_column_pack_start (column, renderer, TRUE);
+        g_object_set (renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
+        gtk_tree_view_column_set_cell_data_func (column, renderer, extension_description_data_func_cb, data, NULL);
+
+        gtk_tree_view_column_set_expand (column, TRUE);
+        gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
+
+	/* the checkbox column */
+
+	column = gtk_tree_view_column_new ();
+	gtk_tree_view_column_set_title (column, _("Use"));
+
+	renderer = gtk_cell_renderer_toggle_new ();
+	g_signal_connect (renderer,
+			  "toggled",
+			  G_CALLBACK (cell_renderer_toggle_toggled_cb),
+			  data);
+
+	gtk_tree_view_column_pack_start (column, renderer, TRUE);
+	gtk_tree_view_column_set_cell_data_func (column, renderer, extension_active_data_func_cb, data, NULL);
+
+	gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
+}
+
+
+static int
+extension_compare_func (GtkTreeModel *tree_model,
+			GtkTreeIter  *iter_a,
+			GtkTreeIter  *iter_b,
+			gpointer      user_data)
+{
+	GthExtensionDescription *description_a;
+	GthExtensionDescription *description_b;
+	int                      result;
+
+	gtk_tree_model_get (tree_model, iter_a, 0, &description_a, -1);
+	gtk_tree_model_get (tree_model, iter_b, 0, &description_b, -1);
+
+	result = strcmp (description_a->name, description_b->name);
+
+	g_object_unref (description_a);
+	g_object_unref (description_b);
+
+	return result;
+}
+
+
+static void
+list_view_selection_changed_cb (GtkTreeSelection *treeselection,
+                                gpointer          user_data)
+{
+	DialogData              *data = user_data;
+	GtkTreeModel            *model;
+	GtkTreeIter              iter;
+	GthExtensionDescription *description;
+	GthExtension            *extension;
+
+	model = GTK_TREE_MODEL (data->list_store);
+	if (! gtk_tree_selection_get_selected (treeselection, &model, &iter))
+		return;
+
+	gtk_tree_model_get (model, &iter, 0, &description, -1);
+
+	extension = gth_extension_description_get_extension (description);
+	gtk_widget_set_sensitive (GET_WIDGET ("preferences_button"), (extension != NULL) && gth_extension_is_configurable (extension));
+
+	g_object_unref (description);
+}
+
+
+static void
+about_button_clicked_cb (GtkButton *button,
+			 gpointer   user_data)
+{
+	DialogData              *data = user_data;
+	GtkTreeModel            *model;
+	GtkTreeIter              iter;
+	GthExtensionDescription *description;
+	GtkWidget               *dialog;
+
+	model = GTK_TREE_MODEL (data->list_store);
+	if (! gtk_tree_selection_get_selected (gtk_tree_view_get_selection (GTK_TREE_VIEW (data->list_view)), &model, &iter))
+		return;
+
+	gtk_tree_model_get (model, &iter, 0, &description, -1);
+
+	dialog = gtk_about_dialog_new ();
+	if (description->name != NULL)
+		g_object_set (dialog, "program-name", description->name, NULL);
+	if (description->description != NULL)
+		g_object_set (dialog, "comments", description->description, NULL);
+	if (description->version != NULL)
+		g_object_set (dialog, "version", description->version, NULL);
+	if (description->authors != NULL)
+		g_object_set (dialog, "authors", description->authors, NULL);
+	if (description->copyright != NULL)
+		g_object_set (dialog, "copyright", description->copyright, NULL);
+	if (description->icon_name != NULL)
+		g_object_set (dialog, "logo-icon-name", description->icon_name, NULL);
+	else
+		g_object_set (dialog, "logo-icon-name", DEFAULT_ICON, NULL);
+	if (description->url != NULL)
+		g_object_set (dialog, "website", description->url, NULL);
+
+	gtk_dialog_run (GTK_DIALOG (dialog));
+	gtk_widget_destroy (dialog);
+
+	g_object_unref (description);
+}
+
+
+void
+dlg_extensions (GthBrowser *browser)
+{
+	DialogData          *data;
+	GthExtensionManager *manager;
+	GList               *extensions;
+	GList               *scan;
+	GtkTreePath         *first;
+
+	if (gth_browser_get_dialog (browser, "extensions") != NULL) {
+		gtk_window_present (GTK_WINDOW (gth_browser_get_dialog (browser, "extensions")));
+		return;
+	}
+
+	data = g_new0 (DialogData, 1);
+	data->browser = browser;
+	data->builder = _gtk_builder_new_from_file ("extensions.ui", NULL);
+	data->active_extensions = eel_gconf_get_string_list (PREF_ACTIVE_EXTENSIONS);
+
+	/* Get the widgets. */
+
+	data->dialog = _gtk_builder_get_widget (data->builder, "extensions_dialog");
+	gth_browser_set_dialog (browser, "extensions", data->dialog);
+	g_object_set_data (G_OBJECT (data->dialog), "dialog_data", data);
+
+	/* Set widgets data. */
+
+	data->list_store = gtk_list_store_new (1, G_TYPE_OBJECT);
+	data->list_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (data->list_store));
+	g_object_unref (data->list_store);
+        gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (data->list_view), TRUE);
+
+        add_columns (GTK_TREE_VIEW (data->list_view), data);
+	gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (data->list_store), 0, extension_compare_func, NULL, NULL);
+        gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (data->list_store), 0, GTK_SORT_ASCENDING);
+
+	gtk_widget_show (data->list_view);
+	gtk_container_add (GTK_CONTAINER (GET_WIDGET ("extensions_scrolledwindow")), data->list_view);
+
+	gtk_label_set_mnemonic_widget (GTK_LABEL (GET_WIDGET ("extensions_label")), data->list_view);
+	gtk_label_set_use_underline (GTK_LABEL (GET_WIDGET ("extensions_label")), TRUE);
+
+	manager = gth_main_get_default_extension_manager ();
+	extensions = gth_extension_manager_get_extensions (manager);
+	for (scan = extensions; scan; scan = scan->next) {
+		const char              *name = scan->data;
+		GthExtensionDescription *description;
+		GtkTreeIter              iter;
+
+		description = gth_extension_manager_get_description (manager, name);
+		if ((description == NULL) || description->mandatory)
+			continue;
+
+		gtk_list_store_append (data->list_store, &iter);
+		gtk_list_store_set (data->list_store, &iter, 0, description, -1);
+	}
+	g_list_free (extensions);
+
+	/* Set the signals handlers. */
+
+	g_signal_connect (G_OBJECT (data->dialog),
+			  "destroy",
+			  G_CALLBACK (destroy_cb),
+			  data);
+	g_signal_connect_swapped (GET_WIDGET ("close_button"),
+				  "clicked",
+				  G_CALLBACK (gtk_widget_destroy),
+				  G_OBJECT (data->dialog));
+	g_signal_connect (GET_WIDGET ("about_button"),
+			  "clicked",
+			  G_CALLBACK (about_button_clicked_cb),
+			  data);
+	g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW (data->list_view)),
+			  "changed",
+			  G_CALLBACK (list_view_selection_changed_cb),
+			  data);
+	first = gtk_tree_path_new_first ();
+	gtk_tree_selection_select_path (gtk_tree_view_get_selection (GTK_TREE_VIEW (data->list_view)), first);
+	gtk_tree_path_free (first);
+
+	/* run dialog. */
+
+	gtk_window_set_transient_for (GTK_WINDOW (data->dialog),
+				      GTK_WINDOW (browser));
+	gtk_window_set_modal (GTK_WINDOW (data->dialog), TRUE);
+	gtk_widget_show (data->dialog);
+}
diff --git a/gthumb/dlg-extensions.h b/gthumb/dlg-extensions.h
new file mode 100644
index 0000000..cc533e1
--- /dev/null
+++ b/gthumb/dlg-extensions.h
@@ -0,0 +1,30 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ *  GThumb
+ *
+ *  Copyright (C) 2009 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
+ *  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 DLG_EXTENSIONS_H
+#define DLG_EXTENSIONS_H
+
+#include "gth-browser.h"
+
+void dlg_extensions (GthBrowser *browser);
+
+#endif /* DLG_EXTENSIONS_H */
diff --git a/gthumb/dlg-personalize-filters.c b/gthumb/dlg-personalize-filters.c
index 08db11d..1c549dd 100644
--- a/gthumb/dlg-personalize-filters.c
+++ b/gthumb/dlg-personalize-filters.c
@@ -457,7 +457,7 @@ update_sensitivity (DialogData *data)
 			g_object_unref (test);
 		}
 	}
-	
+
 	gtk_widget_set_sensitive (GET_WIDGET ("edit_button"), is_filter);
 	gtk_widget_set_sensitive (GET_WIDGET ("delete_button"), is_filter);
 }
@@ -477,7 +477,7 @@ general_filter_changed_cb (GtkComboBox *widget,
 {
 	DialogData *data = user_data;
 	int         idx;
-	
+
 	idx = gtk_combo_box_get_active (widget);
 	eel_gconf_set_string (PREF_GENERAL_FILTER, g_list_nth (data->general_tests, idx)->data);
 }
@@ -491,7 +491,7 @@ dlg_personalize_filters (GthBrowser *browser)
 	char       *general_filter;
 	int         i, active_filter;
 	int         i_general;
-	
+
 	if (gth_browser_get_dialog (browser, "personalize_filters") != NULL) {
 		gtk_window_present (GTK_WINDOW (gth_browser_get_dialog (browser, "personalize_filters")));
 		return;
@@ -517,22 +517,22 @@ dlg_personalize_filters (GthBrowser *browser)
 	for (i = 0, i_general = -1, scan = tests; scan; scan = scan->next, i++) {
 		const char *registered_test_id = scan->data;
 		GthTest    *test;
-		
-		if (strncmp (registered_test_id, "file::type::", 12) != 0) 
+
+		if (strncmp (registered_test_id, "file::type::", 12) != 0)
 			continue;
-		
+
 		i_general += 1;
-		
+
 		if (strcmp (registered_test_id, general_filter) == 0)
 			active_filter = i_general;
 
 		test = gth_main_get_test (registered_test_id);
-		data->general_tests = g_list_prepend (data->general_tests, g_strdup (gth_test_get_id (test)));		
+		data->general_tests = g_list_prepend (data->general_tests, g_strdup (gth_test_get_id (test)));
 		gtk_combo_box_append_text (GTK_COMBO_BOX (data->general_filter_combobox), gth_test_get_display_name (test));
 		g_object_unref (test);
 	}
 	data->general_tests = g_list_reverse (data->general_tests);
-	
+
 	gtk_combo_box_set_active (GTK_COMBO_BOX (data->general_filter_combobox), active_filter);
 	gtk_widget_show (data->general_filter_combobox);
 	gtk_container_add (GTK_CONTAINER (GET_WIDGET ("general_filter_box")), data->general_filter_combobox);
@@ -542,9 +542,9 @@ dlg_personalize_filters (GthBrowser *browser)
 
 	g_free (general_filter);
 	_g_string_list_free (tests);
-	
+
 	/**/
-	
+
 	data->list_store = gtk_list_store_new (NUM_COLUMNS,
 					       G_TYPE_OBJECT,
 					       G_TYPE_STRING,
@@ -565,7 +565,7 @@ dlg_personalize_filters (GthBrowser *browser)
 
 	update_filter_list (data);
 	update_sensitivity (data);
-	
+
 	/* Set the signals handlers. */
 
 	g_signal_connect (G_OBJECT (data->dialog),
@@ -604,7 +604,7 @@ dlg_personalize_filters (GthBrowser *browser)
 			  "changed",
 			  G_CALLBACK (general_filter_changed_cb),
 			  data);
-			  
+
 	data->filters_changed_id = g_signal_connect (gth_main_get_default_monitor (),
 				                     "filters-changed",
 				                     G_CALLBACK (filters_changed_cb),
diff --git a/gthumb/gth-browser-actions-callbacks.c b/gthumb/gth-browser-actions-callbacks.c
index b7edd02..8778750 100644
--- a/gthumb/gth-browser-actions-callbacks.c
+++ b/gthumb/gth-browser-actions-callbacks.c
@@ -23,6 +23,7 @@
 #include <config.h>
 #include <glib/gi18n.h>
 #include "dlg-bookmarks.h"
+#include "dlg-extensions.h"
 #include "dlg-edit-metadata.h"
 #include "dlg-personalize-filters.h"
 #include "dlg-preferences.h"
@@ -132,6 +133,14 @@ gth_browser_activate_action_edit_preferences (GtkAction  *action,
 
 
 void
+gth_browser_activate_action_edit_extensions (GtkAction  *action,
+					     GthBrowser *browser)
+{
+	dlg_extensions (browser);
+}
+
+
+void
 gth_browser_activate_action_go_back (GtkAction  *action,
 				     GthBrowser *browser)
 {
diff --git a/gthumb/gth-browser-actions-callbacks.h b/gthumb/gth-browser-actions-callbacks.h
index 7626888..f0aef4c 100644
--- a/gthumb/gth-browser-actions-callbacks.h
+++ b/gthumb/gth-browser-actions-callbacks.h
@@ -32,6 +32,7 @@ DEFINE_ACTION(gth_browser_activate_action_bookmarks_edit)
 DEFINE_ACTION(gth_browser_activate_action_browser_mode)
 DEFINE_ACTION(gth_browser_activate_action_edit_metadata)
 DEFINE_ACTION(gth_browser_activate_action_edit_preferences)
+DEFINE_ACTION(gth_browser_activate_action_edit_extensions)
 DEFINE_ACTION(gth_browser_activate_action_edit_select_all)
 DEFINE_ACTION(gth_browser_activate_action_file_open_with)
 DEFINE_ACTION(gth_browser_activate_action_file_new_window)
diff --git a/gthumb/gth-browser-actions-entries.h b/gthumb/gth-browser-actions-entries.h
index d48e261..bf4e1cb 100644
--- a/gthumb/gth-browser-actions-entries.h
+++ b/gthumb/gth-browser-actions-entries.h
@@ -75,6 +75,11 @@ static GtkActionEntry gth_browser_action_entries[] = {
 	  N_("Edit various preferences"),
 	  G_CALLBACK (gth_browser_activate_action_edit_preferences) },
 
+	{ "Edit_Extensions", NULL,
+	  N_("Extensions"), NULL,
+	  N_("Activate and configure the application extensions"),
+	  G_CALLBACK (gth_browser_activate_action_edit_extensions) },
+
 	{ "Edit_SelectAll", GTK_STOCK_SELECT_ALL,
 	  NULL, NULL,
 	  NULL,
diff --git a/gthumb/gth-browser-ui.h b/gthumb/gth-browser-ui.h
index 9d88c0f..e8fb5a3 100644
--- a/gthumb/gth-browser-ui.h
+++ b/gthumb/gth-browser-ui.h
@@ -54,6 +54,7 @@ static const char *fixed_ui_info =
 "      <separator/>"
 "      <placeholder name='Edit_Actions'/>"
 "      <separator/>"
+"      <menuitem action='Edit_Extensions'/>"
 "      <menuitem action='Edit_Preferences'/>"
 "    </menu>"
 "    <menu name='View' action='ViewMenu'>"
diff --git a/gthumb/gth-extensions.c b/gthumb/gth-extensions.c
index ace257a..69a664e 100644
--- a/gthumb/gth-extensions.c
+++ b/gthumb/gth-extensions.c
@@ -21,8 +21,11 @@
  */
 
 #include <config.h>
+#include <glib/gi18n.h>
 #include "glib-utils.h"
 #include "gth-extensions.h"
+#include "gthumb-error.h"
+
 
 #define EXTENSION_SUFFIX ".extension"
 
@@ -48,19 +51,21 @@ gth_extension_base_close (GthExtension *self)
 }
 
 
-static void
-gth_extension_base_activate (GthExtension *self)
+static gboolean
+gth_extension_base_activate (GthExtension  *self,
+			     GError       **error)
 {
-	g_return_if_fail (GTH_IS_EXTENSION (self));
 	self->active = TRUE;
+	return TRUE;
 }
 
 
-static void
-gth_extension_base_deactivate (GthExtension *self)
+static gboolean
+gth_extension_base_deactivate (GthExtension  *self,
+			       GError       **error)
 {
-	g_return_if_fail (GTH_IS_EXTENSION (self));
 	self->active = FALSE;
+	return TRUE;
 }
 
 
@@ -148,17 +153,19 @@ gth_extension_is_active (GthExtension *self)
 }
 
 
-void
-gth_extension_activate (GthExtension *self)
+gboolean
+gth_extension_activate (GthExtension  *self,
+	                GError       **error)
 {
-	GTH_EXTENSION_GET_CLASS (self)->activate (self);
+	return GTH_EXTENSION_GET_CLASS (self)->activate (self, error);
 }
 
 
-void
-gth_extension_deactivate (GthExtension *self)
+gboolean
+gth_extension_deactivate (GthExtension  *self,
+	                  GError       **error)
 {
-	GTH_EXTENSION_GET_CLASS (self)->deactivate (self);
+	return GTH_EXTENSION_GET_CLASS (self)->deactivate (self, error);
 }
 
 
@@ -193,7 +200,7 @@ static gboolean
 gth_extension_module_real_open (GthExtension *base)
 {
 	GthExtensionModule *self;
-	char *file_name;
+	char               *file_name;
 
 	self = GTH_EXTENSION_MODULE (base);
 
@@ -244,49 +251,63 @@ get_module_function_name (GthExtensionModule *self,
 }
 
 
-static void
-gth_extension_module_exec_generic_func (GthExtensionModule *self,
-					const char         *name)
+static gboolean
+gth_extension_module_exec_generic_func (GthExtensionModule   *self,
+					const char           *name,
+					GError              **error)
 {
 	char *function_name;
 	void (*func) (void);
 
-	g_return_if_fail (GTH_IS_EXTENSION_MODULE (self));
-	g_return_if_fail (name != NULL);
-
 	function_name = get_module_function_name (self, name);
 	if (g_module_symbol (self->priv->module, function_name, (gpointer *)&func))
 		func();
 	else
-		g_warning ("could not exec module function: %s\n", g_module_error ());
+		*error = g_error_new_literal (GTHUMB_ERROR, GTH_ERROR_GENERIC, g_module_error ());
 
 	g_free (function_name);
+
+	return (*error == NULL);
 }
 
 
-static void
-gth_extension_module_real_activate (GthExtension *base)
+static gboolean
+gth_extension_module_real_activate (GthExtension  *base,
+				    GError       **error)
 {
 	GthExtensionModule *self;
+	gboolean            success;
 
 	self = GTH_EXTENSION_MODULE (base);
 
 	if (base->active)
-		return;
+		return TRUE;
+
+	success = gth_extension_module_exec_generic_func (self, "activate", error);
+	if (success)
+		base->active = TRUE;
 
-	gth_extension_module_exec_generic_func (self, "activate");
-	base->active = TRUE;
+	return success;
 }
 
 
-static void
-gth_extension_module_real_deactivate (GthExtension *base)
+static gboolean
+gth_extension_module_real_deactivate (GthExtension  *base,
+				      GError       **error)
 {
 	GthExtensionModule *self;
+	gboolean            success;
 
 	self = GTH_EXTENSION_MODULE (base);
 
-	gth_extension_module_exec_generic_func (self, "deactivate");
+	if (! base->active)
+		return TRUE;
+
+	success = gth_extension_module_exec_generic_func (self, "deactivate", error);
+	if (success)
+		base->active = FALSE;
+
+	return success;
 }
 
 
@@ -413,7 +434,6 @@ gth_extension_module_new (const char *module_name)
 
 struct _GthExtensionDescriptionPrivate {
 	gboolean      opened;
-	gboolean      activated;
 	GthExtension *extension;
 };
 
@@ -428,11 +448,13 @@ gth_extension_description_finalize (GObject *obj)
 
 	self = GTH_EXTENSION_DESCRIPTION (obj);
 
+	g_free (self->id);
 	g_free (self->name);
 	g_free (self->description);
 	g_strfreev (self->authors);
 	g_free (self->copyright);
 	g_free (self->version);
+	g_free (self->icon_name);
 	g_free (self->url);
 	g_free (self->loader_type);
 	g_free (self->loader_file);
@@ -458,7 +480,6 @@ gth_extension_description_instance_init (GthExtensionDescription *self)
 {
 	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GTH_TYPE_EXTENSION_DESCRIPTION, GthExtensionDescriptionPrivate);
 	self->priv->opened = FALSE;
-	self->priv->activated = FALSE;
 	self->priv->extension = NULL;
 }
 
@@ -492,22 +513,28 @@ gth_extension_description_load_from_file (GthExtensionDescription *desc,
 {
 	GKeyFile *key_file;
 	char     *file_path;
+	char     *basename;
 
 	key_file = g_key_file_new ();
 	file_path = g_file_get_path (file);
 	if (! g_key_file_load_from_file (key_file, file_path, G_KEY_FILE_NONE, NULL))
 		return FALSE;
 
+	basename = g_file_get_basename (file);
+	desc->id = _g_uri_remove_extension (basename);
 	desc->name = g_key_file_get_string (key_file, "Extension", "Name", NULL);
 	desc->description = g_key_file_get_string (key_file, "Extension", "Description", NULL);
 	desc->version = g_key_file_get_string (key_file, "Extension", "Version", NULL);
 	desc->authors = g_key_file_get_string_list (key_file, "Extension", "Authors", NULL, NULL);
 	desc->copyright = g_key_file_get_string (key_file, "Extension", "Copyright", NULL);
+	desc->icon_name = g_key_file_get_string (key_file, "Extension", "Icon", NULL);
 	desc->url = g_key_file_get_string (key_file, "Extension", "URL", NULL);
+	desc->mandatory = g_key_file_get_boolean (key_file, "Extension", "Mandatory", NULL);
 	desc->loader_type = g_key_file_get_string (key_file, "Loader", "Type", NULL);
 	desc->loader_file = g_key_file_get_string (key_file, "Loader", "File", NULL);
 	desc->loader_requires = g_key_file_get_string_list (key_file, "Loader", "Requires", NULL, NULL);
 
+	g_free (basename);
 	g_free (file_path);
 	g_key_file_free (key_file);
 
@@ -530,6 +557,20 @@ gth_extension_description_new (GFile *file)
 }
 
 
+gboolean
+gth_extension_description_is_active (GthExtensionDescription *desc)
+{
+	return (desc->priv->extension != NULL) && desc->priv->extension->active;
+}
+
+
+GthExtension *
+gth_extension_description_get_extension (GthExtensionDescription *desc)
+{
+	return desc->priv->extension;
+}
+
+
 /* -- gth_extension_manager --  */
 
 
@@ -679,8 +720,9 @@ gth_extension_manager_open (GthExtensionManager *manager,
 
 
 gboolean
-gth_extension_manager_activate (GthExtensionManager *manager,
-				const char          *extension_name)
+gth_extension_manager_activate (GthExtensionManager  *manager,
+				const char           *extension_name,
+				GError              **error)
 {
 	GthExtensionDescription *description;
 
@@ -692,12 +734,75 @@ gth_extension_manager_activate (GthExtensionManager *manager,
 		int i;
 
 		for (i = 0; description->loader_requires[i] != NULL; i++)
-			if (! gth_extension_manager_activate (manager, description->loader_requires[i]))
+			if (! gth_extension_manager_activate (manager, description->loader_requires[i], error))
 				return FALSE;
 	}
-	gth_extension_activate (description->priv->extension);
 
-	return TRUE;
+	return gth_extension_activate (description->priv->extension, error);
+}
+
+
+static GList *
+get_extension_dependencies (GthExtensionManager     *manager,
+			    GthExtensionDescription *description)
+{
+	GList *dependencies = NULL;
+	GList *names;
+	GList *scan;
+
+	names = g_hash_table_get_keys (manager->priv->extensions);
+	for (scan = names; scan; scan = scan->next) {
+		const char              *extension_name = scan->data;
+		GthExtensionDescription *other_description;
+
+		other_description = g_hash_table_lookup (manager->priv->extensions, extension_name);
+		if (other_description->loader_requires != NULL) {
+			int i;
+
+			for (i = 0; other_description->loader_requires[i] != NULL; i++)
+				if (strcmp (description->id, other_description->loader_requires[i]) == 0)
+					dependencies = g_list_prepend (dependencies, other_description);
+		}
+	}
+
+	g_list_free (names);
+
+	return g_list_reverse (dependencies);
+}
+
+
+gboolean
+gth_extension_manager_deactivate (GthExtensionManager  *manager,
+				  const char           *extension_name,
+				  GError              **error)
+{
+	GthExtensionDescription *description;
+	GList                   *required_by;
+	GList                   *scan;
+
+	if (! gth_extension_manager_open (manager, extension_name))
+		return TRUE;
+
+	description = g_hash_table_lookup (manager->priv->extensions, extension_name);
+	if (! gth_extension_description_is_active (description))
+		return TRUE;
+
+	required_by = get_extension_dependencies (manager, description);
+	for (scan = required_by; scan; scan = scan->next) {
+		GthExtensionDescription *child_description = scan->data;
+
+		if (gth_extension_description_is_active (child_description)) {
+			*error = g_error_new (GTHUMB_ERROR, GTH_ERROR_EXTENSION_DEPENDENCY, _("The extension '%s' is required by the extension '%s'"), description->name, child_description->name);
+			break;
+		}
+	}
+
+	g_list_free (required_by);
+
+	if (*error == NULL)
+		return gth_extension_deactivate (description->priv->extension, error);
+	else
+		return FALSE;
 }
 
 
diff --git a/gthumb/gth-extensions.h b/gthumb/gth-extensions.h
index 16f178e..6839014 100644
--- a/gthumb/gth-extensions.h
+++ b/gthumb/gth-extensions.h
@@ -84,13 +84,15 @@ struct _GthExtension {
 
 struct _GthExtensionClass {
 	GObjectClass parent_class;
-	gboolean (*open)            (GthExtension *self);
-	void     (*close)           (GthExtension *self);
-	void     (*activate)        (GthExtension *self);
-	void     (*deactivate)      (GthExtension *self);
-	gboolean (*is_configurable) (GthExtension *self);
-	void     (*configure)       (GthExtension *self,
-				     GtkWindow    *parent);
+	gboolean   (*open)            (GthExtension  *self);
+	void       (*close)           (GthExtension  *self);
+	gboolean   (*activate)        (GthExtension  *self,
+				       GError       **error);
+	gboolean   (*deactivate)      (GthExtension  *self,
+				       GError       **error);
+	gboolean   (*is_configurable) (GthExtension  *self);
+	void       (*configure)       (GthExtension  *self,
+				       GtkWindow     *parent);
 };
 
 struct _GthExtensionModule {
@@ -104,15 +106,18 @@ struct _GthExtensionModuleClass {
 
 struct _GthExtensionDescription {
 	GObject parent_instance;
-	char  *name;
-	char  *description;
-	char **authors;
-	char  *copyright;
-	char  *version;
-	char  *url;
-	char  *loader_type;
-	char  *loader_file;
-	char **loader_requires;
+	char      *id;
+	char      *name;
+	char      *description;
+	char     **authors;
+	char      *copyright;
+	char      *version;
+	char      *icon_name;
+	char      *url;
+	char      *loader_type;
+	char      *loader_file;
+	char     **loader_requires;
+	gboolean   mandatory;
 	GthExtensionDescriptionPrivate *priv;
 };
 
@@ -130,30 +135,38 @@ struct _GthExtensionManagerClass {
 };
 
 GType                      gth_extension_get_type                  (void);
-gboolean                   gth_extension_open                      (GthExtension *self);
-void                       gth_extension_close                     (GthExtension *self);
-gboolean                   gth_extension_is_active                 (GthExtension *self);
-void                       gth_extension_activate                  (GthExtension *self);
-void                       gth_extension_deactivate                (GthExtension *self);
-gboolean                   gth_extension_is_configurable           (GthExtension *self);
-void                       gth_extension_configure                 (GthExtension *self,
-					        	   	    GtkWindow    *parent);
+gboolean                   gth_extension_open                      (GthExtension  *self);
+void                       gth_extension_close                     (GthExtension  *self);
+gboolean                   gth_extension_is_active                 (GthExtension  *self);
+gboolean                   gth_extension_activate                  (GthExtension  *self,
+							            GError       **error);
+gboolean                   gth_extension_deactivate                (GthExtension  *self,
+							            GError       **error);
+gboolean                   gth_extension_is_configurable           (GthExtension  *self);
+void                       gth_extension_configure                 (GthExtension  *self,
+					        	   	    GtkWindow     *parent);
 
 GType                      gth_extension_module_get_type           (void);
 GthExtension *             gth_extension_module_new                (const char   *module_name);
 
 GType                      gth_extension_description_get_type      (void);
 GthExtensionDescription *  gth_extension_description_new           (GFile        *file);
+gboolean                   gth_extension_description_is_active     (GthExtensionDescription *desc);
+GthExtension *             gth_extension_description_get_extension (GthExtensionDescription *desc);
 
 GType                      gth_extension_manager_get_type          (void);
 GthExtensionManager *      gth_extension_manager_new               (void);
-gboolean                   gth_extension_manager_open              (GthExtensionManager *manager,
-								    const char          *extension_name);
-gboolean                   gth_extension_manager_activate          (GthExtensionManager *manager,
-								    const char          *extension_name);
-GList *                    gth_extension_manager_get_extensions    (GthExtensionManager *manager);
-GthExtensionDescription *  gth_extension_manager_get_description   (GthExtensionManager *manager,
-								    const char          *extension_name);
+gboolean                   gth_extension_manager_open              (GthExtensionManager  *manager,
+								    const char           *extension_name);
+gboolean                   gth_extension_manager_activate          (GthExtensionManager  *manager,
+								    const char           *extension_name,
+								    GError              **error);
+gboolean                   gth_extension_manager_deactivate        (GthExtensionManager  *manager,
+								    const char           *extension_name,
+								    GError              **error);
+GList *                    gth_extension_manager_get_extensions    (GthExtensionManager  *manager);
+GthExtensionDescription *  gth_extension_manager_get_description   (GthExtensionManager  *manager,
+								    const char           *extension_name);
 
 G_END_DECLS
 
diff --git a/gthumb/gth-main.c b/gthumb/gth-main.c
index ee780a6..4993022 100644
--- a/gthumb/gth-main.c
+++ b/gthumb/gth-main.c
@@ -34,6 +34,7 @@
 #include "gth-metadata-provider.h"
 #include "gth-user-dir.h"
 #include "gth-preferences.h"
+#include "gtk-utils.h"
 #include "pixbuf-io.h"
 #include "typedefs.h"
 
@@ -200,12 +201,58 @@ gth_main_get_type (void)
 }
 
 
+static void
+about_dialog_activate_link_cb (GtkAboutDialog *about,
+			       const char     *link_,
+			       gpointer        data)
+{
+	GError *error = NULL;
+
+	if (! gtk_show_uri (gtk_widget_get_screen (GTK_WIDGET (about)), link_, GDK_CURRENT_TIME, &error)) {
+		char *title;
+
+		title = g_strdup_printf (_("Unable to show '%s'"), link_);
+		_gtk_error_dialog_from_gerror_run (GTK_WINDOW (about), title, &error);
+
+		g_free (title);
+	}
+}
+
+
+static void
+about_dialog_activate_email_cb (GtkAboutDialog *about,
+			        const char     *link_,
+			        gpointer        data)
+{
+	GError *error = NULL;
+	char   *url;
+
+	url = g_strconcat ("mailto:";, link_, NULL);
+	if (! gtk_show_uri (gtk_widget_get_screen (GTK_WIDGET (about)), url, GDK_CURRENT_TIME, &error)) {
+		char *title;
+
+		title = g_strdup_printf (_("Unable to open '%s'"), link_);
+		_gtk_error_dialog_from_gerror_run (GTK_WINDOW (about), title, &error);
+
+		g_free (title);
+	}
+
+	g_free (url);
+}
+
+
 void
 gth_main_initialize (void)
 {
 	if (Main != NULL)
 		return;
 	Main = (GthMain*) g_object_new (GTH_TYPE_MAIN, NULL);
+
+	g_set_application_name (_("gthumb"));
+	gtk_window_set_default_icon_name ("gthumb");
+	gtk_icon_theme_append_search_path (gtk_icon_theme_get_default (), GTHUMB_PKGDATADIR G_DIR_SEPARATOR_S "icons");
+	gtk_about_dialog_set_url_hook (about_dialog_activate_link_cb, NULL, NULL);
+	gtk_about_dialog_set_email_hook (about_dialog_activate_email_cb, NULL, NULL);
 }
 
 
@@ -992,14 +1039,40 @@ gth_main_get_default_extension_manager (void)
 void
 gth_main_activate_extensions (void)
 {
-	const char *default_extensions[] = { "catalogs", "comments", "exiv2", "file_manager", "file_tools", "file_viewer", "image_viewer", "search", NULL };
+	const char *mandatory_extensions[] = { "file_viewer", NULL };
+	const char *default_extensions[] = { "catalogs", "comments", "exiv2", "file_manager", "file_tools", "image_viewer", "search", NULL };
 	int         i;
+	GSList     *active_extensions;
+	GSList     *scan;
 
 	if (Main->priv->extension_manager == NULL)
 		Main->priv->extension_manager = gth_extension_manager_new ();
 
-	/* FIXME: read the extensions list from a gconf key */
+	for (i = 0; mandatory_extensions[i] != NULL; i++) {
+		GError *error = NULL;
+
+		if (! gth_extension_manager_activate (Main->priv->extension_manager, mandatory_extensions[i], &error)) {
+			g_warning ("Could not load the '%s' extension: %s", mandatory_extensions[i], error->message);
+			g_clear_error (&error);
+		}
+	}
+
+	active_extensions = eel_gconf_get_string_list (PREF_ACTIVE_EXTENSIONS);
+	if (active_extensions == NULL)
+		for (i = 0; default_extensions[i] != NULL; i++)
+			active_extensions = g_slist_prepend (active_extensions, g_strdup (default_extensions[i]));
+	active_extensions = g_slist_reverse (active_extensions);
+
+	for (scan = active_extensions; scan; scan = scan->next) {
+		char   *name = scan->data;
+		GError *error = NULL;
+
+		if (! gth_extension_manager_activate (Main->priv->extension_manager, name, &error)) {
+			g_warning ("Could not load the '%s' extension: %s", name, error->message);
+			g_clear_error (&error);
+		}
+	}
 
-	for (i = 0; default_extensions[i] != NULL; i++)
-		gth_extension_manager_activate (Main->priv->extension_manager, default_extensions[i]);
+	g_slist_foreach (active_extensions, (GFunc) g_free, NULL);
+	g_slist_free (active_extensions);
 }
diff --git a/gthumb/gth-preferences.h b/gthumb/gth-preferences.h
index 74dc1d3..ebee44b 100644
--- a/gthumb/gth-preferences.h
+++ b/gthumb/gth-preferences.h
@@ -38,6 +38,7 @@ G_BEGIN_DECLS
 #define PREF_EDITORS                "/apps/gthumb/general/editors"
 #define PREF_MIGRATE_DIRECTORIES    "/apps/gthumb/general/migrate_directories"
 #define PREF_MIGRATE_COMMENT_SYSTEM "/apps/gthumb/general/migrate_comment_system"
+#define PREF_ACTIVE_EXTENSIONS      "/apps/gthumb/general/active_extensions"
 
 #define PREF_GENERAL_FILTER         "/apps/gthumb/browser/general_filter"
 #define PREF_SHOW_HIDDEN_FILES      "/apps/gthumb/browser/show_hidden_files"
diff --git a/gthumb/gthumb-error.h b/gthumb/gthumb-error.h
index ce404cd..67c84c2 100644
--- a/gthumb/gthumb-error.h
+++ b/gthumb/gthumb-error.h
@@ -27,6 +27,11 @@
 
 G_BEGIN_DECLS
 
+typedef enum {
+	GTH_ERROR_GENERIC,
+	GTH_ERROR_EXTENSION_DEPENDENCY
+} GthErrorCode;
+
 #define GTHUMB_ERROR gthumb_error_quark ()
 GQuark gthumb_error_quark (void);
 
diff --git a/gthumb/main.c b/gthumb/main.c
index ee45576..f9aea63 100644
--- a/gthumb/main.c
+++ b/gthumb/main.c
@@ -25,6 +25,7 @@
 #include <gtk/gtk.h>
 #include <unique/unique.h>
 #include "eggsmclient.h"
+#include "glib-utils.h"
 #include "gth-browser.h"
 #include "gth-file-data.h"
 #include "gth-file-source-vfs.h"
@@ -47,6 +48,7 @@ gboolean ImportPhotos = FALSE;
 static UniqueApp   *gthumb_app;
 static char       **remaining_args;
 static const char  *program_argv0; /* argv[0] from main(); used as the command to restart the program */
+static gboolean     restart = FALSE;
 
 
 static const GOptionEntry options[] = {
@@ -310,12 +312,6 @@ main (int argc, char *argv[])
 	}
 	g_option_context_free (context);
 
-	/* application properties */
-
-	g_set_application_name (_("gthumb"));
-	gtk_window_set_default_icon_name ("gthumb");
-	gtk_icon_theme_append_search_path (gtk_icon_theme_get_default (), GTHUMB_PKGDATADIR G_DIR_SEPARATOR_S "icons");
-
 	/* other initializations */
 
 	gth_session_manager_init ();
@@ -341,5 +337,23 @@ main (int argc, char *argv[])
 	gth_main_release ();
 	gth_pref_release ();
 
+	if (restart)
+		g_spawn_command_line_async (program_argv0, NULL);
+
 	return 0;
 }
+
+
+void
+gth_restart (void)
+{
+	GList *windows;
+	GList *scan;
+
+	windows = g_list_copy (gth_window_get_window_list ());
+	for (scan = windows; scan; scan = scan->next)
+		gth_window_close (GTH_WINDOW (scan->data));
+	g_list_free (windows);
+
+	restart = TRUE;
+}
diff --git a/gthumb/main.h b/gthumb/main.h
new file mode 100644
index 0000000..a4b2489
--- /dev/null
+++ b/gthumb/main.h
@@ -0,0 +1,32 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ *  GThumb
+ *
+ *  Copyright (C) 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
+ *  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 MAIN_H
+#define MAIN_H
+
+G_BEGIN_DECLS
+
+void gth_restart (void);
+
+G_END_DECLS
+
+#endif /* MAIN_H */
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 8776e4f..8e6705d 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -5,6 +5,7 @@ copy-n-paste/eggsmclient.c
 data/gthumb.desktop.in.in
 data/gthumb.schemas.in
 [type: gettext/glade]data/ui/bookmarks.ui
+[type: gettext/glade]data/ui/extensions.ui
 [type: gettext/glade]data/ui/filter-editor.ui
 [type: gettext/glade]data/ui/personalize-filters.ui
 [type: gettext/glade]data/ui/preferences.ui



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]