[gthumb] [facebook] started work on the facebook album exporter
- From: Paolo Bacchilega <paobac src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gthumb] [facebook] started work on the facebook album exporter
- Date: Fri, 9 Apr 2010 23:27:20 +0000 (UTC)
commit 6fe1e3d8562090bbc43ebd6dcb783f96a60e5ab2
Author: Paolo Bacchilega <paobac src gnome org>
Date: Mon Apr 5 15:47:21 2010 +0200
[facebook] started work on the facebook album exporter
configure.ac | 3 +
extensions/Makefile.am | 1 +
extensions/facebook/Makefile.am | 55 +
extensions/facebook/actions.c | 45 +
extensions/facebook/actions.h | 32 +
extensions/facebook/callbacks.c | 94 ++
extensions/facebook/callbacks.h | 30 +
extensions/facebook/data/Makefile.am | 3 +
extensions/facebook/data/ui/Makefile.am | 12 +
extensions/facebook/data/ui/export-to-facebook.ui | 433 ++++++++
.../facebook/data/ui/facebook-account-chooser.ui | 56 +
.../facebook/data/ui/facebook-account-manager.ui | 103 ++
.../facebook/data/ui/facebook-ask-authorization.ui | 78 ++
.../data/ui/facebook-complete-authorization.ui | 79 ++
.../facebook/data/ui/facebook-export-completed.ui | 70 ++
extensions/facebook/dlg-export-to-facebook.c | 894 +++++++++++++++
extensions/facebook/dlg-export-to-facebook.h | 31 +
.../facebook/facebook-account-chooser-dialog.c | 184 +++
.../facebook/facebook-account-chooser-dialog.h | 61 +
.../facebook/facebook-account-manager-dialog.c | 247 ++++
.../facebook/facebook-account-manager-dialog.h | 59 +
extensions/facebook/facebook-account.c | 180 +++
extensions/facebook/facebook-account.h | 66 ++
extensions/facebook/facebook-album.c | 286 +++++
extensions/facebook/facebook-album.h | 82 ++
extensions/facebook/facebook-connection.c | 560 ++++++++++
extensions/facebook/facebook-connection.h | 107 ++
extensions/facebook/facebook-photo.c | 252 +++++
extensions/facebook/facebook-photo.h | 92 ++
extensions/facebook/facebook-service.c | 1173 ++++++++++++++++++++
extensions/facebook/facebook-service.h | 132 +++
extensions/facebook/facebook-types.h | 51 +
extensions/facebook/facebook-user.c | 247 ++++
extensions/facebook/facebook-user.h | 82 ++
extensions/facebook/facebook.extension.in.in | 12 +
extensions/facebook/main.c | 53 +
36 files changed, 5945 insertions(+), 0 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index e5f51c5..86fb7ba 100644
--- a/configure.ac
+++ b/configure.ac
@@ -446,6 +446,9 @@ extensions/edit_metadata/data/ui/Makefile
extensions/exiv2_tools/Makefile
extensions/exiv2_tools/data/Makefile
extensions/exiv2_tools/data/ui/Makefile
+extensions/facebook/Makefile
+extensions/facebook/data/Makefile
+extensions/facebook/data/ui/Makefile
extensions/file_manager/Makefile
extensions/file_tools/Makefile
extensions/file_tools/data/Makefile
diff --git a/extensions/Makefile.am b/extensions/Makefile.am
index 83a3550..434830d 100644
--- a/extensions/Makefile.am
+++ b/extensions/Makefile.am
@@ -8,6 +8,7 @@ SUBDIRS = \
desktop_background \
edit_metadata \
exiv2_tools \
+ facebook \
file_manager \
file_tools \
file_viewer \
diff --git a/extensions/facebook/Makefile.am b/extensions/facebook/Makefile.am
new file mode 100644
index 0000000..f0ec599
--- /dev/null
+++ b/extensions/facebook/Makefile.am
@@ -0,0 +1,55 @@
+if ENABLE_WEB_SERVICES
+
+SUBDIRS = data
+
+extensiondir = $(pkglibdir)/extensions
+extension_LTLIBRARIES = libfacebook.la
+
+libfacebook_la_SOURCES = \
+ actions.c \
+ actions.h \
+ callbacks.c \
+ callbacks.h \
+ dlg-export-to-facebook.c \
+ dlg-export-to-facebook.h \
+ facebook-account.c \
+ facebook-account.h \
+ facebook-account-chooser-dialog.c \
+ facebook-account-chooser-dialog.h \
+ facebook-account-manager-dialog.c \
+ facebook-account-manager-dialog.h \
+ facebook-connection.c \
+ facebook-connection.h \
+ facebook-photo.c \
+ facebook-photo.h \
+ facebook-photoset.c \
+ facebook-photoset.h \
+ facebook-service.c \
+ facebook-service.h \
+ facebook-types.h \
+ facebook-user.c \
+ facebook-user.h \
+ main.c
+
+libfacebook_la_CFLAGS = $(GTHUMB_CFLAGS) $(LIBSOUP_CFLAGS) -I$(top_srcdir) -I$(top_builddir)/gthumb
+libfacebook_la_LDFLAGS = $(EXTENSION_LIBTOOL_FLAGS)
+libfacebook_la_LIBADD = $(GTHUMB_LIBS) $(LIBSOUP_LIBS)
+libfacebook_la_DEPENDENCIES = $(top_builddir)/gthumb/gthumb$(EXEEXT)
+
+extensioninidir = $(extensiondir)
+extensionini_in_files = facebook.extension.in.in
+extensionini_DATA = $(extensionini_in_files:.extension.in.in=.extension)
+
+%.extension.in: %.extension.in.in $(extension_LTLIBRARIES)
+ $(AM_V_GEN)( sed -e "s|%LIBRARY%|`. ./$(extension_LTLIBRARIES) && echo $$dlname`|" \
+ $< > $@ )
+
+%.extension: %.extension.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*po) ; LC_ALL=C $(INTLTOOL_MERGE) -d -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< $@
+
+EXTRA_DIST = $(extensionini_in_files)
+
+DISTCLEANFILES = $(extensionini_DATA)
+
+endif
+
+-include $(top_srcdir)/git.mk
diff --git a/extensions/facebook/actions.c b/extensions/facebook/actions.c
new file mode 100644
index 0000000..30a73c1
--- /dev/null
+++ b/extensions/facebook/actions.c
@@ -0,0 +1,45 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * GThumb
+ *
+ * Copyright (C) 2010 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 <gthumb.h>
+#include "dlg-export-to-facebook.h"
+
+
+void
+gth_browser_activate_action_export_facebook (GtkAction *action,
+ GthBrowser *browser)
+{
+ GList *items;
+ GList *file_list;
+
+ items = gth_file_selection_get_selected (GTH_FILE_SELECTION (gth_browser_get_file_list_view (browser)));
+ file_list = gth_file_list_get_files (GTH_FILE_LIST (gth_browser_get_file_list (browser)), items);
+ if (file_list == NULL)
+ file_list = gth_file_store_get_visibles (gth_browser_get_file_store (browser));
+ dlg_export_to_facebook (browser, file_list);
+
+ _g_object_list_unref (file_list);
+ _gtk_tree_path_list_free (items);
+}
diff --git a/extensions/facebook/actions.h b/extensions/facebook/actions.h
new file mode 100644
index 0000000..5586ee9
--- /dev/null
+++ b/extensions/facebook/actions.h
@@ -0,0 +1,32 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * GThumb
+ *
+ * Copyright (C) 2010 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 ACTIONS_H
+#define ACTIONS_H
+
+#include <gtk/gtk.h>
+
+#define DEFINE_ACTION(x) void x (GtkAction *action, gpointer data);
+
+DEFINE_ACTION(gth_browser_activate_action_export_facebook)
+
+#endif /* ACTIONS_H */
diff --git a/extensions/facebook/callbacks.c b/extensions/facebook/callbacks.c
new file mode 100644
index 0000000..cc41b5a
--- /dev/null
+++ b/extensions/facebook/callbacks.c
@@ -0,0 +1,94 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * GThumb
+ *
+ * Copyright (C) 2010 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-object.h>
+#include <gthumb.h>
+#include "actions.h"
+
+
+#define BROWSER_DATA_KEY "facebook-browser-data"
+
+
+static const char *ui_info =
+"<ui>"
+" <menubar name='MenuBar'>"
+" <menu name='File' action='FileMenu'>"
+" <menu name='Export' action='ExportMenu'>"
+" <placeholder name='Web_Services'>"
+" <menuitem action='File_Export_Facebook'/>"
+" </placeholder>"
+" </menu>"
+" </menu>"
+" </menubar>"
+"</ui>";
+
+
+static GtkActionEntry action_entries[] = {
+ { "File_Export_Facebook", "facebook",
+ N_("Face_book..."), NULL,
+ N_("Upload photos to Facebook"),
+ G_CALLBACK (gth_browser_activate_action_export_facebook) },
+};
+
+
+typedef struct {
+ GtkActionGroup *action_group;
+} BrowserData;
+
+
+static void
+browser_data_free (BrowserData *data)
+{
+ g_free (data);
+}
+
+
+void
+fl__gth_browser_construct_cb (GthBrowser *browser)
+{
+ BrowserData *data;
+ GError *error = NULL;
+ guint merge_id;
+
+ g_return_if_fail (GTH_IS_BROWSER (browser));
+
+ data = g_new0 (BrowserData, 1);
+
+ data->action_group = gtk_action_group_new ("Facebook Actions");
+ gtk_action_group_set_translation_domain (data->action_group, NULL);
+ gtk_action_group_add_actions (data->action_group,
+ action_entries,
+ G_N_ELEMENTS (action_entries),
+ browser);
+ gtk_ui_manager_insert_action_group (gth_browser_get_ui_manager (browser), data->action_group, 0);
+
+ merge_id = gtk_ui_manager_add_ui_from_string (gth_browser_get_ui_manager (browser), ui_info, -1, &error);
+ if (merge_id == 0) {
+ g_warning ("building ui failed: %s", error->message);
+ g_clear_error (&error);
+ }
+
+ g_object_set_data_full (G_OBJECT (browser), BROWSER_DATA_KEY, data, (GDestroyNotify) browser_data_free);
+}
diff --git a/extensions/facebook/callbacks.h b/extensions/facebook/callbacks.h
new file mode 100644
index 0000000..eda224b
--- /dev/null
+++ b/extensions/facebook/callbacks.h
@@ -0,0 +1,30 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * GThumb
+ *
+ * Copyright (C) 2010 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 CALLBACKS_H
+#define CALLBACKS_H
+
+#include <gthumb.h>
+
+void fb__gth_browser_construct_cb (GthBrowser *browser);
+
+#endif /* CALLBACKS_H */
diff --git a/extensions/facebook/data/Makefile.am b/extensions/facebook/data/Makefile.am
new file mode 100644
index 0000000..c1713cf
--- /dev/null
+++ b/extensions/facebook/data/Makefile.am
@@ -0,0 +1,3 @@
+SUBDIRS = ui
+
+-include $(top_srcdir)/git.mk
diff --git a/extensions/facebook/data/ui/Makefile.am b/extensions/facebook/data/ui/Makefile.am
new file mode 100644
index 0000000..0ebaf9e
--- /dev/null
+++ b/extensions/facebook/data/ui/Makefile.am
@@ -0,0 +1,12 @@
+uidir = $(pkgdatadir)/ui
+ui_DATA = \
+ export-to-flickr.ui \
+ facebook-account-chooser.ui \
+ facebook-account-manager.ui \
+ facebook-ask-authorization.ui \
+ facebook-complete-authorization.ui \
+ facebook-export-completed.ui
+
+EXTRA_DIST = $(ui_DATA)
+
+-include $(top_srcdir)/git.mk
diff --git a/extensions/facebook/data/ui/export-to-facebook.ui b/extensions/facebook/data/ui/export-to-facebook.ui
new file mode 100644
index 0000000..c45d946
--- /dev/null
+++ b/extensions/facebook/data/ui/export-to-facebook.ui
@@ -0,0 +1,433 @@
+<?xml version="1.0"?>
+<interface>
+ <requires lib="gtk+" version="2.16"/>
+ <!-- interface-naming-policy project-wide -->
+ <object class="GtkDialog" id="export_dialog">
+ <property name="border_width">5</property>
+ <property name="title" translatable="yes">Export to Flickr</property>
+ <property name="type_hint">dialog</property>
+ <property name="has_separator">False</property>
+ <child internal-child="vbox">
+ <object class="GtkVBox" id="dialog-vbox7">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkVBox" id="hbox1">
+ <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="images_box">
+ <property name="width_request">460</property>
+ <property name="height_request">220</property>
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">3</property>
+ <child>
+ <object class="GtkLabel" id="images_info_label">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox1">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkTable" id="table2">
+ <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">5</property>
+ <child>
+ <object class="GtkAlignment" id="alignment1">
+ <property name="visible">True</property>
+ <property name="top_padding">6</property>
+ <child>
+ <object class="GtkLabel" id="label3">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="label" translatable="yes">_Account:</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">account_combobox</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox4">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">3</property>
+ <child>
+ <object class="GtkHBox" id="hbox4">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkComboBox" id="account_combobox">
+ <property name="width_request">300</property>
+ <property name="visible">True</property>
+ <property name="model">account_liststore</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext4"/>
+ <attributes>
+ <attribute name="text">1</attribute>
+ </attributes>
+ </child>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="edit_accounts_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="tooltip_text" translatable="yes">Edit accounts</property>
+ <child>
+ <object class="GtkImage" id="image2">
+ <property name="visible">True</property>
+ <property name="stock">gtk-edit</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox3">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label4">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Free space:</property>
+ <attributes>
+ <attribute name="absolute-size" value="10000"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="free_space_label">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.51999998092651367</property>
+ <attributes>
+ <attribute name="absolute-size" value="10000"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Ph_otoset:</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">photoset_comboboxentry</property>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBoxEntry" id="photoset_comboboxentry">
+ <property name="visible">True</property>
+ <property name="model">photoset_liststore</property>
+ <property name="text_column">2</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext2"/>
+ <attributes>
+ <attribute name="text">3</attribute>
+ </attributes>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label5">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">_Privacy:</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">privacy_combobox</property>
+ </object>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="privacy_combobox">
+ <property name="visible">True</property>
+ <property name="model">privacy_liststore</property>
+ <property name="active">0</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext5"/>
+ <attributes>
+ <attribute name="text">1</attribute>
+ </attributes>
+ </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>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="hidden_checkbutton">
+ <property name="label" translatable="yes">Hi_de from public searches</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="right_attach">2</property>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">_Safety:</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">safety_combobox</property>
+ </object>
+ <packing>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="safety_combobox">
+ <property name="visible">True</property>
+ <property name="model">safety_liststore</property>
+ <property name="active">0</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext3"/>
+ <attributes>
+ <attribute name="text">1</attribute>
+ </attributes>
+ </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="expand">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <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_area7">
+ <property name="visible">True</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="close_button">
+ <property name="label">gtk-cancel</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="upload_button">
+ <property name="label" translatable="yes">_Upload</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="image">upload_image</property>
+ <property name="use_underline">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="button1">
+ <property name="label">gtk-help</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>
+ <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">close_button</action-widget>
+ <action-widget response="-5">upload_button</action-widget>
+ <action-widget response="-11">button1</action-widget>
+ </action-widgets>
+ </object>
+ <object class="GtkImage" id="upload_image">
+ <property name="visible">True</property>
+ <property name="stock">gtk-goto-top</property>
+ </object>
+ <object class="GtkListStore" id="account_liststore">
+ <columns>
+ <!-- column-name account -->
+ <column type="GObject"/>
+ <!-- column-name username -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkListStore" id="safety_liststore">
+ <columns>
+ <!-- column-name value -->
+ <column type="gint"/>
+ <!-- column-name name -->
+ <column type="gchararray"/>
+ </columns>
+ <data>
+ <row>
+ <col id="0">0</col>
+ <col id="1" translatable="yes">Safe content</col>
+ </row>
+ <row>
+ <col id="0">1</col>
+ <col id="1" translatable="yes">Moderate content</col>
+ </row>
+ <row>
+ <col id="0">2</col>
+ <col id="1" translatable="yes">Restricted content</col>
+ </row>
+ </data>
+ </object>
+ <object class="GtkListStore" id="privacy_liststore">
+ <columns>
+ <!-- column-name value -->
+ <column type="gint"/>
+ <!-- column-name name -->
+ <column type="gchararray"/>
+ </columns>
+ <data>
+ <row>
+ <col id="0">0</col>
+ <col id="1" translatable="yes">Public photos</col>
+ </row>
+ <row>
+ <col id="0">1</col>
+ <col id="1" translatable="yes">Private photos, visible to family and friends</col>
+ </row>
+ <row>
+ <col id="0">2</col>
+ <col id="1" translatable="yes">Private photos, visible to friends</col>
+ </row>
+ <row>
+ <col id="0">3</col>
+ <col id="1" translatable="yes">Private photos, visible to family</col>
+ </row>
+ <row>
+ <col id="0">4</col>
+ <col id="1" translatable="yes">Private photos</col>
+ </row>
+ </data>
+ </object>
+ <object class="GtkListStore" id="photoset_liststore">
+ <columns>
+ <!-- column-name data -->
+ <column type="GObject"/>
+ <!-- column-name icon -->
+ <column type="gchararray"/>
+ <!-- column-name title -->
+ <column type="gchararray"/>
+ <!-- column-name n_photos -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+</interface>
diff --git a/extensions/facebook/data/ui/facebook-account-chooser.ui b/extensions/facebook/data/ui/facebook-account-chooser.ui
new file mode 100644
index 0000000..54abe69
--- /dev/null
+++ b/extensions/facebook/data/ui/facebook-account-chooser.ui
@@ -0,0 +1,56 @@
+<?xml version="1.0"?>
+<interface>
+ <requires lib="gtk+" version="2.16"/>
+ <!-- interface-naming-policy project-wide -->
+ <object class="GtkVBox" id="account_chooser">
+ <property name="visible">True</property>
+ <property name="border_width">5</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkHBox" id="hbox3">
+ <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">A_ccount:</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="account_combobox">
+ <property name="width_request">300</property>
+ <property name="visible">True</property>
+ <property name="model">account_liststore</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext1"/>
+ <attributes>
+ <attribute name="text">1</attribute>
+ </attributes>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <object class="GtkListStore" id="account_liststore">
+ <columns>
+ <!-- column-name account -->
+ <column type="GObject"/>
+ <!-- column-name name -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+</interface>
diff --git a/extensions/facebook/data/ui/facebook-account-manager.ui b/extensions/facebook/data/ui/facebook-account-manager.ui
new file mode 100644
index 0000000..2106973
--- /dev/null
+++ b/extensions/facebook/data/ui/facebook-account-manager.ui
@@ -0,0 +1,103 @@
+<?xml version="1.0"?>
+<interface>
+ <requires lib="gtk+" version="2.16"/>
+ <!-- interface-naming-policy project-wide -->
+ <object class="GtkListStore" id="accounts_liststore">
+ <columns>
+ <!-- column-name data -->
+ <column type="GObject"/>
+ <!-- column-name username -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkVBox" id="account_manager">
+ <property name="visible">True</property>
+ <property name="border_width">5</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkLabel" id="label3">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">A_ccounts:</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">account_treeview</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox1">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow1">
+ <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="account_treeview">
+ <property name="width_request">300</property>
+ <property name="height_request">150</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="model">accounts_liststore</property>
+ <property name="headers_visible">False</property>
+ <property name="headers_clickable">False</property>
+ <property name="reorderable">True</property>
+ <property name="search_column">1</property>
+ <child>
+ <object class="GtkTreeViewColumn" id="treeviewcolumn1">
+ <child>
+ <object class="GtkCellRendererText" id="account_cellrenderertext"/>
+ <attributes>
+ <attribute name="text">1</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox3">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkButton" id="delete_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <child>
+ <object class="GtkImage" id="image3">
+ <property name="visible">True</property>
+ <property name="stock">gtk-delete</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+</interface>
diff --git a/extensions/facebook/data/ui/facebook-ask-authorization.ui b/extensions/facebook/data/ui/facebook-ask-authorization.ui
new file mode 100644
index 0000000..8cc162b
--- /dev/null
+++ b/extensions/facebook/data/ui/facebook-ask-authorization.ui
@@ -0,0 +1,78 @@
+<?xml version="1.0"?>
+<interface>
+ <requires lib="gtk+" version="2.16"/>
+ <!-- interface-naming-policy project-wide -->
+ <object class="GtkMessageDialog" id="ask_authorization_messagedialog">
+ <property name="border_width">5</property>
+ <property name="title" translatable="yes">Upload to Flickr</property>
+ <property name="resizable">False</property>
+ <property name="modal">True</property>
+ <property name="type_hint">normal</property>
+ <property name="skip_taskbar_hint">True</property>
+ <property name="message_type">other</property>
+ <property name="text" translatable="yes">gthumb requires your authorization to upload the photos to Flickr</property>
+ <property name="secondary_text" translatable="yes">Click 'Authorize' to open your web browser and authorize gthumb to upload photos to Flickr. When you're finished, return to this window to complete the authorization.</property>
+ <property name="image">icon_image</property>
+ <child internal-child="vbox">
+ <object class="GtkVBox" id="dialog-vbox1">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">2</property>
+ <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="cancel_button">
+ <property name="label">gtk-cancel</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="authorize_button">
+ <property name="label" translatable="yes">_Authorize...</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="image">button_image</property>
+ <property name="use_underline">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="-6">cancel_button</action-widget>
+ <action-widget response="-5">authorize_button</action-widget>
+ </action-widgets>
+ </object>
+ <object class="GtkImage" id="button_image">
+ <property name="visible">True</property>
+ <property name="stock">gtk-dialog-authentication</property>
+ </object>
+ <object class="GtkImage" id="icon_image">
+ <property name="visible">True</property>
+ <property name="yalign">0</property>
+ <property name="stock">gtk-dialog-authentication</property>
+ <property name="icon-size">6</property>
+ </object>
+</interface>
diff --git a/extensions/facebook/data/ui/facebook-complete-authorization.ui b/extensions/facebook/data/ui/facebook-complete-authorization.ui
new file mode 100644
index 0000000..4b11130
--- /dev/null
+++ b/extensions/facebook/data/ui/facebook-complete-authorization.ui
@@ -0,0 +1,79 @@
+<?xml version="1.0"?>
+<interface>
+ <requires lib="gtk+" version="2.16"/>
+ <!-- interface-naming-policy project-wide -->
+ <object class="GtkMessageDialog" id="complete_authorization_messagedialog">
+ <property name="border_width">5</property>
+ <property name="title" translatable="yes">Upload to Flickr</property>
+ <property name="resizable">False</property>
+ <property name="modal">True</property>
+ <property name="type_hint">normal</property>
+ <property name="skip_taskbar_hint">True</property>
+ <property name="message_type">other</property>
+ <property name="text" translatable="yes">Return to this window when you have finished the authorization process on Flickr.com</property>
+ <property name="secondary_text" translatable="yes">Once you're done, click the 'Continue' button below.</property>
+ <property name="image">icon_image1</property>
+ <child internal-child="vbox">
+ <object class="GtkVBox" id="dialog-vbox4">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">2</property>
+ <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="cancel_button1">
+ <property name="label">gtk-cancel</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_stock">True</property>
+ <property name="xalign">0.50999999046325684</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="complete_button">
+ <property name="label" translatable="yes">C_ontinue</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="image">button_image1</property>
+ <property name="use_underline">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="-6">cancel_button1</action-widget>
+ <action-widget response="-5">complete_button</action-widget>
+ </action-widgets>
+ </object>
+ <object class="GtkImage" id="icon_image1">
+ <property name="visible">True</property>
+ <property name="yalign">0</property>
+ <property name="stock">gtk-dialog-authentication</property>
+ <property name="icon-size">6</property>
+ </object>
+ <object class="GtkImage" id="button_image1">
+ <property name="visible">True</property>
+ <property name="stock">gtk-dialog-authentication</property>
+ </object>
+</interface>
diff --git a/extensions/facebook/data/ui/facebook-export-completed.ui b/extensions/facebook/data/ui/facebook-export-completed.ui
new file mode 100644
index 0000000..8341986
--- /dev/null
+++ b/extensions/facebook/data/ui/facebook-export-completed.ui
@@ -0,0 +1,70 @@
+<?xml version="1.0"?>
+<interface>
+ <requires lib="gtk+" version="2.16"/>
+ <!-- interface-naming-policy project-wide -->
+ <object class="GtkMessageDialog" id="completed_messagedialog">
+ <property name="border_width">5</property>
+ <property name="title" translatable="yes">Upload to Flickr</property>
+ <property name="resizable">False</property>
+ <property name="modal">True</property>
+ <property name="type_hint">normal</property>
+ <property name="skip_taskbar_hint">True</property>
+ <property name="text" translatable="yes">Files successfully uploaded to the server.</property>
+ <child internal-child="vbox">
+ <object class="GtkVBox" id="dialog-vbox1">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">2</property>
+ <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="button1">
+ <property name="label" translatable="yes">_Open in the Browser</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="image">icon_image</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="GtkButton" id="button2">
+ <property name="label">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">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="1">button1</action-widget>
+ <action-widget response="-7">button2</action-widget>
+ </action-widgets>
+ </object>
+ <object class="GtkImage" id="icon_image">
+ <property name="visible">True</property>
+ <property name="yalign">0</property>
+ <property name="stock">gtk-jump-to</property>
+ </object>
+</interface>
diff --git a/extensions/facebook/dlg-export-to-facebook.c b/extensions/facebook/dlg-export-to-facebook.c
new file mode 100644
index 0000000..9b19533
--- /dev/null
+++ b/extensions/facebook/dlg-export-to-facebook.c
@@ -0,0 +1,894 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * GThumb
+ *
+ * Copyright (C) 2010 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 <gthumb.h>
+#include "dlg-export-to-facebook.h"
+#include "facebook-account-chooser-dialog.h"
+#include "facebook-account-manager-dialog.h"
+#include "facebook-album.h"
+#include "facebook-service.h"
+#include "facebook-user.h"
+
+
+#define GET_WIDGET(x) (_gtk_builder_get_widget (data->builder, (x)))
+#define _OPEN_IN_BROWSER_RESPONSE 1
+
+
+enum {
+ ACCOUNT_DATA_COLUMN,
+ ACCOUNT_NAME_COLUMN
+};
+
+
+enum {
+ ALBUM_DATA_COLUMN,
+ ALBUM_ICON_COLUMN,
+ ALBUM_TITLE_COLUMN,
+ ALBUM_N_PHOTOS_COLUMN
+};
+
+
+typedef struct {
+ GthBrowser *browser;
+ GthFileData *location;
+ GList *file_list;
+ GtkBuilder *builder;
+ GtkWidget *dialog;
+ GtkWidget *progress_dialog;
+ GList *accounts;
+ FacebookAccount *account;
+ FacebookUser *user;
+ GList *albums;
+ FacebookPhotoset *album;
+ FacebookConnection *conn;
+ FacebookService *service;
+ GList *photos_ids;
+ GCancellable *cancellable;
+} DialogData;
+
+
+static void
+export_dialog_destroy_cb (GtkWidget *widget,
+ DialogData *data)
+{
+ if (data->conn != NULL)
+ gth_task_completed (GTH_TASK (data->conn), NULL);
+ _g_object_unref (data->cancellable);
+ _g_object_unref (data->service);
+ _g_object_unref (data->conn);
+ _g_object_list_unref (data->accounts);
+ _g_object_unref (data->account);
+ _g_object_unref (data->user);
+ _g_object_list_unref (data->albums);
+ _g_object_unref (data->album);
+ _g_string_list_free (data->photos_ids);
+ _g_object_unref (data->builder);
+ _g_object_list_unref (data->file_list);
+ _g_object_unref (data->location);
+ g_free (data);
+}
+
+
+static void
+completed_messagedialog_response_cb (GtkDialog *dialog,
+ int response_id,
+ gpointer user_data)
+{
+ DialogData *data = user_data;
+
+ switch (response_id) {
+ case GTK_RESPONSE_DELETE_EVENT:
+ case GTK_RESPONSE_CLOSE:
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+ gtk_widget_destroy (data->dialog);
+ break;
+
+ case _OPEN_IN_BROWSER_RESPONSE:
+ {
+ char *url = NULL;
+ GError *error = NULL;
+
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+
+ if (data->album == NULL) {
+ GString *ids;
+ GList *scan;
+
+ ids = g_string_new ("");
+ for (scan = data->photos_ids; scan; scan = scan->next) {
+ if (scan != data->photos_ids)
+ g_string_append (ids, ",");
+ g_string_append (ids, (char *) scan->data);
+ }
+ url = g_strconcat ("http://www.facebook.com/photos/upload/edit/?ids=", ids->str, NULL);
+
+ g_string_free (ids, TRUE);
+ }
+ else if (data->album->url != NULL)
+ url = g_strdup (data->album->url);
+ else if (data->album->id != NULL)
+ url = g_strconcat ("http://www.facebook.com/photos/", data->user->id, "/sets/", data->album->id, NULL);
+
+ if ((url != NULL) && ! gtk_show_uri (gtk_widget_get_screen (GTK_WIDGET (dialog)), url, 0, &error)) {
+ if (data->conn != NULL)
+ gth_task_dialog (GTH_TASK (data->conn), TRUE);
+ _gtk_error_dialog_from_gerror_run (GTK_WINDOW (data->browser), _("Could not connect to the server"), &error);
+ }
+ gtk_widget_destroy (data->dialog);
+
+ g_free (url);
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+static void
+export_completed_with_success (DialogData *data)
+{
+ GtkBuilder *builder;
+ GtkWidget *dialog;
+
+ gth_task_dialog (GTH_TASK (data->conn), TRUE);
+
+ builder = _gtk_builder_new_from_file ("flicker-export-completed.ui", "flicker");
+ dialog = _gtk_builder_get_widget (builder, "completed_messagedialog");
+ g_object_set_data_full (G_OBJECT (dialog), "builder", builder, g_object_unref);
+ g_signal_connect (dialog,
+ "response",
+ G_CALLBACK (completed_messagedialog_response_cb),
+ data);
+
+ gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (data->browser));
+ gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
+ gtk_window_present (GTK_WINDOW (dialog));
+}
+
+
+static void
+add_photos_to_album_ready_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ DialogData *data = user_data;
+ GError *error = NULL;
+
+ if (! facebook_service_add_photos_to_set_finish (FACEBOOK_SERVICE (source_object), result, &error)) {
+ _gtk_error_dialog_from_gerror_show (GTK_WINDOW (data->browser), _("Could not create the album"), &error);
+ gtk_widget_destroy (data->dialog);
+ return;
+ }
+
+ export_completed_with_success (data);
+}
+
+
+static void
+add_photos_to_album (DialogData *data)
+{
+ facebook_service_add_photos_to_set (data->service,
+ data->album,
+ data->photos_ids,
+ data->cancellable,
+ add_photos_to_album_ready_cb,
+ data);
+}
+
+
+static void
+create_album_ready_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ DialogData *data = user_data;
+ GError *error = NULL;
+ char *primary;
+
+ primary = g_strdup (data->album->primary);
+ g_object_unref (data->album);
+ data->album = facebook_service_create_album_finish (FACEBOOK_SERVICE (source_object), result, &error);
+ if (error != NULL) {
+ _gtk_error_dialog_from_gerror_show (GTK_WINDOW (data->browser), _("Could not create the album"), &error);
+ gtk_widget_destroy (data->dialog);
+ }
+ else {
+ facebook_album_set_primary (data->album, primary);
+ add_photos_to_album (data);
+ }
+
+ g_free (primary);
+}
+
+
+static void
+post_photos_ready_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ DialogData *data = user_data;
+ GError *error = NULL;
+
+ data->photos_ids = facebook_service_post_photos_finish (FACEBOOK_SERVICE (source_object), result, &error);
+ if (error != NULL) {
+ _gtk_error_dialog_from_gerror_show (GTK_WINDOW (data->browser), _("Could not upload the files"), &error);
+ gtk_widget_destroy (data->dialog);
+ return;
+ }
+
+ if (data->album == NULL) {
+ export_completed_with_success (data);
+ return;
+ }
+
+ /* create the album if it doesn't exists */
+
+ if (data->album->id == NULL) {
+ char *first_id;
+
+ first_id = data->photos_ids->data;
+ facebook_album_set_primary (data->album, first_id);
+ facebook_service_create_album (data->service,
+ data->album,
+ data->cancellable,
+ create_album_ready_cb,
+ data);
+ }
+ else
+ add_photos_to_album (data);
+}
+
+
+static int
+find_album_by_title (FacebookPhotoset *album,
+ const char *name)
+{
+ return g_strcmp0 (album->title, name);
+}
+
+
+static void
+export_dialog_response_cb (GtkDialog *dialog,
+ int response_id,
+ gpointer user_data)
+{
+ DialogData *data = user_data;
+
+ switch (response_id) {
+ case GTK_RESPONSE_HELP:
+ show_help_dialog (GTK_WINDOW (data->browser), "export-to-facebook");
+ break;
+
+ case GTK_RESPONSE_DELETE_EVENT:
+ case GTK_RESPONSE_CANCEL:
+ facebook_accounts_save_to_file (data->accounts, data->account);
+ gtk_widget_destroy (data->dialog);
+ break;
+
+ case GTK_RESPONSE_OK:
+ {
+ char *album_title;
+ GList *file_list;
+
+ gtk_widget_hide (data->dialog);
+ gth_task_dialog (GTH_TASK (data->conn), FALSE);
+
+ data->album = NULL;
+ album_title = gtk_combo_box_get_active_text (GTK_COMBO_BOX (GET_WIDGET ("album_comboboxentry")));
+ if ((album_title != NULL) && (g_strcmp0 (album_title, "") != 0)) {
+ GList *link;
+
+ link = g_list_find_custom (data->albums, album_title, (GCompareFunc) find_album_by_title);
+ if (link != NULL)
+ data->album = g_object_ref (link->data);
+
+ if (data->album == NULL) {
+ data->album = facebook_album_new ();
+ facebook_album_set_title (data->album, album_title);
+ }
+ }
+
+ file_list = gth_file_data_list_to_file_list (data->file_list);
+ facebook_service_post_photos (data->service,
+ gtk_combo_box_get_active (GTK_COMBO_BOX (GET_WIDGET ("privacy_combobox"))),
+ gtk_combo_box_get_active (GTK_COMBO_BOX (GET_WIDGET ("safety_combobox"))),
+ gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (GET_WIDGET ("hidden_checkbutton"))),
+ file_list,
+ data->cancellable,
+ post_photos_ready_cb,
+ data);
+
+ _g_object_list_unref (file_list);
+ g_free (album_title);
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+static void
+update_account_list (DialogData *data)
+{
+ GtkTreeIter iter;
+ int current_account;
+ int idx;
+ GList *scan;
+ char *free_space;
+
+ current_account = 0;
+ gtk_list_store_clear (GTK_LIST_STORE (GET_WIDGET ("account_liststore")));
+ for (scan = data->accounts, idx = 0; scan; scan = scan->next, idx++) {
+ FacebookAccount *account = scan->data;
+
+ if ((data->account != NULL) && (g_strcmp0 (data->account->username, account->username) == 0))
+ current_account = idx;
+
+ gtk_list_store_append (GTK_LIST_STORE (GET_WIDGET ("account_liststore")), &iter);
+ gtk_list_store_set (GTK_LIST_STORE (GET_WIDGET ("account_liststore")), &iter,
+ ACCOUNT_DATA_COLUMN, account,
+ ACCOUNT_NAME_COLUMN, account->username,
+ -1);
+ }
+ gtk_combo_box_set_active (GTK_COMBO_BOX (GET_WIDGET ("account_combobox")), current_account);
+
+ free_space = g_format_size_for_display (data->user->max_bandwidth - data->user->used_bandwidth);
+ gtk_label_set_text (GTK_LABEL (GET_WIDGET ("free_space_label")), free_space);
+ g_free (free_space);
+}
+
+
+static void
+album_list_ready_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ DialogData *data = user_data;
+ GError *error = NULL;
+ GList *scan;
+
+ _g_object_list_unref (data->albums);
+ data->albums = facebook_service_list_albums_finish (FACEBOOK_SERVICE (source_object), res, &error);
+ if (error != NULL) {
+ if (data->conn != NULL)
+ gth_task_dialog (GTH_TASK (data->conn), TRUE);
+ _gtk_error_dialog_from_gerror_run (GTK_WINDOW (data->browser), _("Could not connect to the server"), &error);
+ gtk_widget_destroy (data->dialog);
+ return;
+ }
+
+ gtk_list_store_clear (GTK_LIST_STORE (GET_WIDGET ("album_liststore")));
+ for (scan = data->albums; scan; scan = scan->next) {
+ FacebookPhotoset *album = scan->data;
+ char *n_photos;
+ GtkTreeIter iter;
+
+ n_photos = g_strdup_printf ("(%d)", album->n_photos);
+
+ gtk_list_store_append (GTK_LIST_STORE (GET_WIDGET ("album_liststore")), &iter);
+ gtk_list_store_set (GTK_LIST_STORE (GET_WIDGET ("album_liststore")), &iter,
+ ALBUM_DATA_COLUMN, album,
+ ALBUM_ICON_COLUMN, "file-catalog",
+ ALBUM_TITLE_COLUMN, album->title,
+ ALBUM_N_PHOTOS_COLUMN, n_photos,
+ -1);
+
+ g_free (n_photos);
+ }
+
+ gtk_widget_set_sensitive (GET_WIDGET ("upload_button"), TRUE);
+
+ gth_task_dialog (GTH_TASK (data->conn), TRUE);
+
+ gtk_window_set_transient_for (GTK_WINDOW (data->dialog), GTK_WINDOW (data->browser));
+ gtk_window_set_modal (GTK_WINDOW (data->dialog), FALSE);
+ gtk_window_present (GTK_WINDOW (data->dialog));
+}
+
+
+static void
+get_album_list (DialogData *data)
+{
+ facebook_service_list_albums (data->service,
+ NULL,
+ data->cancellable,
+ album_list_ready_cb,
+ data);
+}
+
+
+static void
+upload_status_ready_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ DialogData *data = user_data;
+ GError *error = NULL;
+
+ _g_object_unref (data->user);
+ data->user = facebook_service_get_upload_status_finish (FACEBOOK_SERVICE (source_object), res, &error);
+ if (error != NULL) {
+ if (data->conn != NULL)
+ gth_task_dialog (GTH_TASK (data->conn), TRUE);
+ _gtk_error_dialog_from_gerror_run (GTK_WINDOW (data->browser), _("Could not connect to the server"), &error);
+ gtk_widget_destroy (data->dialog);
+ return;
+ }
+
+ update_account_list (data);
+ get_album_list (data);
+}
+
+
+static void
+connect_to_server (DialogData *data)
+{
+ g_return_if_fail (data->account != NULL);
+
+ facebook_connection_set_auth_token (data->conn, data->account->token);
+ if (data->service == NULL)
+ data->service = facebook_service_new (data->conn);
+ facebook_service_get_upload_status (data->service,
+ data->cancellable,
+ upload_status_ready_cb,
+ data);
+}
+
+
+static void
+connection_token_ready_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ DialogData *data = user_data;
+ GError *error = NULL;
+ GList *link;
+
+ if (! facebook_connection_get_token_finish (FACEBOOK_CONNECTION (source_object), res, &error)) {
+ if (data->conn != NULL)
+ gth_task_dialog (GTH_TASK (data->conn), TRUE);
+ _gtk_error_dialog_from_gerror_run (GTK_WINDOW (data->browser), _("Could not connect to the server"), &error);
+ gtk_widget_destroy (data->dialog);
+ return;
+ }
+
+ _g_object_unref (data->account);
+ data->account = facebook_account_new ();
+ facebook_account_set_username (data->account, facebook_connection_get_username (data->conn));
+ facebook_account_set_token (data->account, facebook_connection_get_auth_token (data->conn));
+
+ link = g_list_find_custom (data->accounts, data->account, (GCompareFunc) facebook_account_cmp);
+ if (link != NULL) {
+ data->accounts = g_list_remove_link (data->accounts, link);
+ _g_object_list_unref (link);
+ }
+ data->accounts = g_list_prepend (data->accounts, g_object_ref (data->account));
+
+ connect_to_server (data);
+}
+
+
+static void
+complete_authorization_messagedialog_response_cb (GtkDialog *dialog,
+ int response_id,
+ gpointer user_data)
+{
+ DialogData *data = user_data;
+
+ switch (response_id) {
+ case GTK_RESPONSE_HELP:
+ show_help_dialog (GTK_WINDOW (dialog), "flicker-complete-authorization");
+ break;
+
+ case GTK_RESPONSE_DELETE_EVENT:
+ case GTK_RESPONSE_CANCEL:
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+ gtk_widget_destroy (data->dialog);
+ break;
+
+ case GTK_RESPONSE_OK:
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+ gth_task_dialog (GTH_TASK (data->conn), FALSE);
+ facebook_connection_get_token (data->conn,
+ data->cancellable,
+ connection_token_ready_cb,
+ data);
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+static void
+complete_authorization (DialogData *data)
+{
+ GtkBuilder *builder;
+ GtkWidget *dialog;
+
+ gth_task_dialog (GTH_TASK (data->conn), TRUE);
+
+ builder = _gtk_builder_new_from_file ("flicker-complete-authorization.ui", "flicker");
+ dialog = _gtk_builder_get_widget (builder, "complete_authorization_messagedialog");
+ g_object_set_data_full (G_OBJECT (dialog), "builder", builder, g_object_unref);
+ g_signal_connect (dialog,
+ "response",
+ G_CALLBACK (complete_authorization_messagedialog_response_cb),
+ data);
+
+ gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (data->browser));
+ gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
+ gtk_window_present (GTK_WINDOW (dialog));
+}
+
+
+static void
+ask_authorization_messagedialog_response_cb (GtkDialog *dialog,
+ int response_id,
+ gpointer user_data)
+{
+ DialogData *data = user_data;
+
+ switch (response_id) {
+ case GTK_RESPONSE_HELP:
+ show_help_dialog (GTK_WINDOW (dialog), "flicker-ask-authorization");
+ break;
+
+ case GTK_RESPONSE_DELETE_EVENT:
+ case GTK_RESPONSE_CANCEL:
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+ gtk_widget_destroy (data->dialog);
+ break;
+
+ case GTK_RESPONSE_OK:
+ {
+ char *url;
+ GError *error = NULL;
+
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+
+ url = facebook_connection_get_login_link (data->conn, FACEBOOK_ACCESS_WRITE);
+ if (gtk_show_uri (gtk_widget_get_screen (GTK_WIDGET (dialog)), url, 0, &error)) {
+ complete_authorization (data);
+ }
+ else {
+ if (data->conn != NULL)
+ gth_task_dialog (GTH_TASK (data->conn), TRUE);
+ _gtk_error_dialog_from_gerror_run (GTK_WINDOW (data->browser), _("Could not connect to the server"), &error);
+ gtk_widget_destroy (data->dialog);
+ }
+
+ g_free (url);
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+static void
+ask_authorization (DialogData *data)
+{
+ GtkBuilder *builder;
+ GtkWidget *dialog;
+
+ gth_task_dialog (GTH_TASK (data->conn), TRUE);
+
+ builder = _gtk_builder_new_from_file ("flicker-ask-authorization.ui", "flicker");
+ dialog = _gtk_builder_get_widget (builder, "ask_authorization_messagedialog");
+ g_object_set_data_full (G_OBJECT (dialog), "builder", builder, g_object_unref);
+ g_signal_connect (dialog,
+ "response",
+ G_CALLBACK (ask_authorization_messagedialog_response_cb),
+ data);
+
+ gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (data->browser));
+ gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
+ gtk_window_present (GTK_WINDOW (dialog));
+}
+
+
+static void
+connection_frob_ready_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ DialogData *data = user_data;
+ GError *error = NULL;
+
+ if (! facebook_connection_get_frob_finish (FACEBOOK_CONNECTION (source_object), res, &error)) {
+ if (data->conn != NULL)
+ gth_task_dialog (GTH_TASK (data->conn), TRUE);
+ _gtk_error_dialog_from_gerror_run (GTK_WINDOW (data->browser), _("Could not connect to the server"), &error);
+ gtk_widget_destroy (data->dialog);
+ return;
+ }
+
+ ask_authorization (data);
+}
+
+
+static void
+start_authorization_process (DialogData *data)
+{
+ facebook_connection_get_frob (data->conn,
+ data->cancellable,
+ connection_frob_ready_cb,
+ data);
+}
+
+
+static void
+account_chooser_dialog_response_cb (GtkDialog *dialog,
+ int response_id,
+ gpointer user_data)
+{
+ DialogData *data = user_data;
+
+ switch (response_id) {
+ case GTK_RESPONSE_DELETE_EVENT:
+ case GTK_RESPONSE_CANCEL:
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+ gtk_widget_destroy (data->dialog);
+ break;
+
+ case GTK_RESPONSE_OK:
+ _g_object_unref (data->account);
+ data->account = facebook_account_chooser_dialog_get_active (FACEBOOK_ACCOUNT_CHOOSER_DIALOG (dialog));
+ if (data->account != NULL) {
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+ connect_to_server (data);
+ }
+
+ break;
+
+ case FACEBOOK_ACCOUNT_CHOOSER_RESPONSE_NEW:
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+ start_authorization_process (data);
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+static void
+auto_select_account (DialogData *data)
+{
+ gtk_widget_hide (data->dialog);
+ gth_task_dialog (GTH_TASK (data->conn), FALSE);
+
+ if (data->accounts != NULL) {
+ if (data->account != NULL) {
+ connect_to_server (data);
+ }
+ else if (data->accounts->next == NULL) {
+ data->account = g_object_ref (data->accounts->data);
+ connect_to_server (data);
+ }
+ else {
+ GtkWidget *dialog;
+
+ gth_task_dialog (GTH_TASK (data->conn), TRUE);
+ dialog = facebook_account_chooser_dialog_new (data->accounts, data->account);
+ g_signal_connect (dialog,
+ "response",
+ G_CALLBACK (account_chooser_dialog_response_cb),
+ data);
+
+ gtk_window_set_title (GTK_WINDOW (dialog), _("Choose Account"));
+ gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (data->browser));
+ gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
+ gtk_window_present (GTK_WINDOW (dialog));
+ }
+ }
+ else
+ start_authorization_process (data);
+}
+
+
+static void
+account_manager_dialog_response_cb (GtkDialog *dialog,
+ int response_id,
+ gpointer user_data)
+{
+ DialogData *data = user_data;
+
+ switch (response_id) {
+ case GTK_RESPONSE_DELETE_EVENT:
+ case GTK_RESPONSE_CANCEL:
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+ break;
+
+ case GTK_RESPONSE_OK:
+ _g_object_list_unref (data->accounts);
+ data->accounts = facebook_account_manager_dialog_get_accounts (FACEBOOK_ACCOUNT_MANAGER_DIALOG (dialog));
+ if (! g_list_find_custom (data->accounts, data->account, (GCompareFunc) facebook_account_cmp)) {
+ _g_object_unref (data->account);
+ data->account = NULL;
+ auto_select_account (data);
+ }
+ else
+ update_account_list (data);
+ facebook_accounts_save_to_file (data->accounts, data->account);
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+static void
+edit_accounts_button_clicked_cb (GtkButton *button,
+ gpointer user_data)
+{
+ DialogData *data = user_data;
+ GtkWidget *dialog;
+
+ dialog = facebook_account_manager_dialog_new (data->accounts);
+ g_signal_connect (dialog,
+ "response",
+ G_CALLBACK (account_manager_dialog_response_cb),
+ data);
+
+ gtk_window_set_title (GTK_WINDOW (dialog), _("Edit Accounts"));
+ gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (data->dialog));
+ gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
+ gtk_window_present (GTK_WINDOW (dialog));
+}
+
+
+static void
+account_combobox_changed_cb (GtkComboBox *widget,
+ gpointer user_data)
+{
+ DialogData *data = user_data;
+ GtkTreeIter iter;
+ FacebookAccount *account;
+
+ if (! gtk_combo_box_get_active_iter (widget, &iter))
+ return;
+
+ gtk_tree_model_get (gtk_combo_box_get_model (widget),
+ &iter,
+ ACCOUNT_DATA_COLUMN, &account,
+ -1);
+
+ if (facebook_account_cmp (account, data->account) != 0) {
+ _g_object_unref (data->account);
+ data->account = account;
+ auto_select_account (data);
+ }
+ else
+ g_object_unref (account);
+}
+
+
+void
+dlg_export_to_facebook (GthBrowser *browser,
+ GList *file_list)
+{
+ DialogData *data;
+ GList *scan;
+ int n_total;
+ goffset total_size;
+ char *total_size_formatted;
+ char *text;
+ GtkWidget *list_view;
+
+ data = g_new0 (DialogData, 1);
+ data->browser = browser;
+ data->location = gth_file_data_dup (gth_browser_get_location_data (browser));
+ data->builder = _gtk_builder_new_from_file ("export-to-facebook.ui", "flicker");
+ data->dialog = _gtk_builder_get_widget (data->builder, "export_dialog");
+ data->cancellable = g_cancellable_new ();
+
+ data->file_list = NULL;
+ n_total = 0;
+ total_size = 0;
+ for (scan = file_list; scan; scan = scan->next) {
+ GthFileData *file_data = scan->data;
+ const char *mime_type;
+
+ mime_type = gth_file_data_get_mime_type (file_data);
+ if (g_content_type_equals (mime_type, "image/bmp")
+ || g_content_type_equals (mime_type, "image/gif")
+ || g_content_type_equals (mime_type, "image/jpeg")
+ || g_content_type_equals (mime_type, "image/png"))
+ {
+ total_size += g_file_info_get_size (file_data->info);
+ n_total++;
+ data->file_list = g_list_prepend (data->file_list, g_object_ref (file_data));
+ }
+ }
+ data->file_list = g_list_reverse (data->file_list);
+
+ if (data->file_list == NULL) {
+ GError *error;
+
+ error = g_error_new_literal (GTH_ERROR, GTH_ERROR_GENERIC, _("No valid file selected."));
+ _gtk_error_dialog_from_gerror_show (GTK_WINDOW (browser), _("Could not export the files"), &error);
+ gtk_widget_destroy (data->dialog);
+
+ return;
+ }
+
+ total_size_formatted = g_format_size_for_display (total_size);
+ text = g_strdup_printf (g_dngettext (NULL, "%d file (%s)", "%d files (%s)", n_total), n_total, total_size_formatted);
+ gtk_label_set_text (GTK_LABEL (GET_WIDGET ("images_info_label")), text);
+ g_free (text);
+ g_free (total_size_formatted);
+
+ /* Set the widget data */
+
+ list_view = gth_file_list_new (GTH_FILE_LIST_TYPE_NO_SELECTION);
+ gth_file_list_set_thumb_size (GTH_FILE_LIST (list_view), 112);
+ gth_file_view_set_spacing (GTH_FILE_VIEW (gth_file_list_get_view (GTH_FILE_LIST (list_view))), 0);
+ gth_file_list_enable_thumbs (GTH_FILE_LIST (list_view), TRUE);
+ gth_file_list_set_caption (GTH_FILE_LIST (list_view), "none");
+ gth_file_list_set_sort_func (GTH_FILE_LIST (list_view), gth_main_get_sort_type ("file::name")->cmp_func, FALSE);
+ gtk_widget_show (list_view);
+ gtk_box_pack_start (GTK_BOX (GET_WIDGET ("images_box")), list_view, TRUE, TRUE, 0);
+ gth_file_list_set_files (GTH_FILE_LIST (list_view), data->file_list);
+
+ gtk_entry_set_text (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (GET_WIDGET ("album_comboboxentry")))), g_file_info_get_edit_name (data->location->info));
+ gtk_widget_set_sensitive (GET_WIDGET ("upload_button"), FALSE);
+
+ /* Set the signals handlers. */
+
+ g_signal_connect (G_OBJECT (data->dialog),
+ "destroy",
+ G_CALLBACK (export_dialog_destroy_cb),
+ data);
+ g_signal_connect (data->dialog,
+ "response",
+ G_CALLBACK (export_dialog_response_cb),
+ data);
+ g_signal_connect (GET_WIDGET ("edit_accounts_button"),
+ "clicked",
+ G_CALLBACK (edit_accounts_button_clicked_cb),
+ data);
+ g_signal_connect (GET_WIDGET ("account_combobox"),
+ "changed",
+ G_CALLBACK (account_combobox_changed_cb),
+ data);
+
+ data->conn = facebook_connection_new ();
+ data->progress_dialog = gth_progress_dialog_new (GTK_WINDOW (data->browser));
+ gth_progress_dialog_add_task (GTH_PROGRESS_DIALOG (data->progress_dialog), GTH_TASK (data->conn));
+
+ data->accounts = facebook_accounts_load_from_file ();
+ data->account = facebook_accounts_find_default (data->accounts);
+ auto_select_account (data);
+}
diff --git a/extensions/facebook/dlg-export-to-facebook.h b/extensions/facebook/dlg-export-to-facebook.h
new file mode 100644
index 0000000..7bc68d1
--- /dev/null
+++ b/extensions/facebook/dlg-export-to-facebook.h
@@ -0,0 +1,31 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * GThumb
+ *
+ * Copyright (C) 2010 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_EXPORT_TO_FACEBOOK_H
+#define DLG_EXPORT_TO_FACEBOOK_H
+
+#include <gthumb.h>
+
+void dlg_export_to_facebook (GthBrowser *browser,
+ GList *file_list);
+
+#endif /* DLG_EXPORT_TO_FACEBOOK_H */
diff --git a/extensions/facebook/facebook-account-chooser-dialog.c b/extensions/facebook/facebook-account-chooser-dialog.c
new file mode 100644
index 0000000..9bdf5ff
--- /dev/null
+++ b/extensions/facebook/facebook-account-chooser-dialog.c
@@ -0,0 +1,184 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * GThumb
+ *
+ * Copyright (C) 2010 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 "facebook-account-chooser-dialog.h"
+
+#define GET_WIDGET(x) (_gtk_builder_get_widget (self->priv->builder, (x)))
+
+
+enum {
+ ACCOUNT_DATA_COLUMN,
+ ACCOUNT_NAME_COLUMN
+};
+
+
+static gpointer parent_class = NULL;
+
+
+struct _FacebookAccountChooserDialogPrivate {
+ GtkBuilder *builder;
+};
+
+
+static void
+facebook_account_chooser_dialog_finalize (GObject *object)
+{
+ FacebookAccountChooserDialog *self;
+
+ self = FACEBOOK_ACCOUNT_CHOOSER_DIALOG (object);
+
+ _g_object_unref (self->priv->builder);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+
+static void
+facebook_account_chooser_dialog_class_init (FacebookAccountChooserDialogClass *klass)
+{
+ GObjectClass *object_class;
+
+ parent_class = g_type_class_peek_parent (klass);
+ g_type_class_add_private (klass, sizeof (FacebookAccountChooserDialogPrivate));
+
+ object_class = (GObjectClass*) klass;
+ object_class->finalize = facebook_account_chooser_dialog_finalize;
+}
+
+
+static void
+facebook_account_chooser_dialog_init (FacebookAccountChooserDialog *self)
+{
+ GtkWidget *content;
+
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, FACEBOOK_TYPE_ACCOUNT_CHOOSER_DIALOG, FacebookAccountChooserDialogPrivate);
+ self->priv->builder = _gtk_builder_new_from_file ("flicker-account-chooser.ui", "flicker");
+
+ gtk_window_set_resizable (GTK_WINDOW (self), FALSE);
+ gtk_dialog_set_has_separator (GTK_DIALOG (self), FALSE);
+ gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (self))), 5);
+ gtk_container_set_border_width (GTK_CONTAINER (self), 5);
+
+ content = _gtk_builder_get_widget (self->priv->builder, "account_chooser");
+ gtk_container_set_border_width (GTK_CONTAINER (content), 5);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (self))), content, TRUE, TRUE, 0);
+
+ gtk_dialog_add_button (GTK_DIALOG (self),
+ GTK_STOCK_NEW,
+ FACEBOOK_ACCOUNT_CHOOSER_RESPONSE_NEW);
+ gtk_dialog_add_button (GTK_DIALOG (self),
+ GTK_STOCK_CANCEL,
+ GTK_RESPONSE_CANCEL);
+ gtk_dialog_add_button (GTK_DIALOG (self),
+ GTK_STOCK_OK,
+ GTK_RESPONSE_OK);
+ gtk_dialog_set_default_response (GTK_DIALOG (self), GTK_RESPONSE_OK);
+}
+
+
+GType
+facebook_account_chooser_dialog_get_type (void)
+{
+ static GType type = 0;
+
+ if (type == 0) {
+ static const GTypeInfo g_define_type_info = {
+ sizeof (FacebookAccountChooserDialogClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) facebook_account_chooser_dialog_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL,
+ sizeof (FacebookAccountChooserDialog),
+ 0,
+ (GInstanceInitFunc) facebook_account_chooser_dialog_init,
+ NULL
+ };
+ type = g_type_register_static (GTK_TYPE_DIALOG,
+ "FacebookAccountChooserDialog",
+ &g_define_type_info,
+ 0);
+ }
+
+ return type;
+}
+
+
+static void
+facebook_account_chooser_dialog_construct (FacebookAccountChooserDialog *self,
+ GList *accounts,
+ FacebookAccount *default_account)
+{
+ GtkTreeIter iter;
+ GList *scan;
+ int active = 0;
+ int idx;
+
+ gtk_list_store_clear (GTK_LIST_STORE (GET_WIDGET ("account_liststore")));
+
+ for (scan = accounts, idx = 0; scan; scan = scan->next, idx++) {
+ FacebookAccount *account = scan->data;
+
+ if ((default_account != NULL) && (g_strcmp0 (account->username, default_account->username) == 0))
+ active = idx;
+
+ gtk_list_store_append (GTK_LIST_STORE (GET_WIDGET ("account_liststore")), &iter);
+ gtk_list_store_set (GTK_LIST_STORE (GET_WIDGET ("account_liststore")), &iter,
+ ACCOUNT_DATA_COLUMN, account,
+ ACCOUNT_NAME_COLUMN, account->username,
+ -1);
+ }
+
+ gtk_combo_box_set_active (GTK_COMBO_BOX (GET_WIDGET ("account_combobox")), active);
+}
+
+
+GtkWidget *
+facebook_account_chooser_dialog_new (GList *accounts,
+ FacebookAccount *default_account)
+{
+ FacebookAccountChooserDialog *self;
+
+ self = g_object_new (FACEBOOK_TYPE_ACCOUNT_CHOOSER_DIALOG, NULL);
+ facebook_account_chooser_dialog_construct (self, accounts, default_account);
+
+ return (GtkWidget *) self;
+}
+
+
+FacebookAccount *
+facebook_account_chooser_dialog_get_active (FacebookAccountChooserDialog *self)
+{
+ GtkTreeIter iter;
+ FacebookAccount *account;
+
+ if (! gtk_combo_box_get_active_iter (GTK_COMBO_BOX (GET_WIDGET ("account_combobox")), &iter))
+ return NULL;
+
+ gtk_tree_model_get (GTK_TREE_MODEL (GET_WIDGET ("account_liststore")), &iter,
+ ACCOUNT_DATA_COLUMN, &account,
+ -1);
+
+ return account;
+}
diff --git a/extensions/facebook/facebook-account-chooser-dialog.h b/extensions/facebook/facebook-account-chooser-dialog.h
new file mode 100644
index 0000000..a71ba35
--- /dev/null
+++ b/extensions/facebook/facebook-account-chooser-dialog.h
@@ -0,0 +1,61 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * GThumb
+ *
+ * Copyright (C) 2010 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 FACEBOOK_ACCOUNT_CHOOSER_DIALOG_H
+#define FACEBOOK_ACCOUNT_CHOOSER_DIALOG_H
+
+#include <gtk/gtk.h>
+#include <gthumb.h>
+#include "facebook-account.h"
+
+G_BEGIN_DECLS
+
+#define FACEBOOK_ACCOUNT_CHOOSER_RESPONSE_NEW 1
+
+#define FACEBOOK_TYPE_ACCOUNT_CHOOSER_DIALOG (facebook_account_chooser_dialog_get_type ())
+#define FACEBOOK_ACCOUNT_CHOOSER_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FACEBOOK_TYPE_ACCOUNT_CHOOSER_DIALOG, FacebookAccountChooserDialog))
+#define FACEBOOK_ACCOUNT_CHOOSER_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FACEBOOK_TYPE_ACCOUNT_CHOOSER_DIALOG, FacebookAccountChooserDialogClass))
+#define FACEBOOK_IS_ACCOUNT_CHOOSER_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FACEBOOK_TYPE_ACCOUNT_CHOOSER_DIALOG))
+#define FACEBOOK_IS_ACCOUNT_CHOOSER_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FACEBOOK_TYPE_ACCOUNT_CHOOSER_DIALOG))
+#define FACEBOOK_ACCOUNT_CHOOSER_DIALOG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), FACEBOOK_TYPE_ACCOUNT_CHOOSER_DIALOG, FacebookAccountChooserDialogClass))
+
+typedef struct _FacebookAccountChooserDialog FacebookAccountChooserDialog;
+typedef struct _FacebookAccountChooserDialogClass FacebookAccountChooserDialogClass;
+typedef struct _FacebookAccountChooserDialogPrivate FacebookAccountChooserDialogPrivate;
+
+struct _FacebookAccountChooserDialog {
+ GtkDialog parent_instance;
+ FacebookAccountChooserDialogPrivate *priv;
+};
+
+struct _FacebookAccountChooserDialogClass {
+ GtkDialogClass parent_class;
+};
+
+GType facebook_account_chooser_dialog_get_type (void);
+GtkWidget * facebook_account_chooser_dialog_new (GList *accounts,
+ FacebookAccount *default_account);
+FacebookAccount * facebook_account_chooser_dialog_get_active (FacebookAccountChooserDialog *self);
+
+G_END_DECLS
+
+#endif /* FACEBOOK_ACCOUNT_CHOOSER_DIALOG_H */
diff --git a/extensions/facebook/facebook-account-manager-dialog.c b/extensions/facebook/facebook-account-manager-dialog.c
new file mode 100644
index 0000000..40845c1
--- /dev/null
+++ b/extensions/facebook/facebook-account-manager-dialog.c
@@ -0,0 +1,247 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * GThumb
+ *
+ * Copyright (C) 2010 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 "facebook-account.h"
+#include "facebook-account-manager-dialog.h"
+
+
+#define GET_WIDGET(x) (_gtk_builder_get_widget (self->priv->builder, (x)))
+
+
+enum {
+ ACCOUNT_DATA_COLUMN,
+ ACCOUNT_NAME_COLUMN,
+};
+
+
+static gpointer parent_class = NULL;
+
+
+struct _FacebookAccountManagerDialogPrivate {
+ GtkBuilder *builder;
+};
+
+
+static void
+facebook_account_manager_dialog_finalize (GObject *object)
+{
+ FacebookAccountManagerDialog *self;
+
+ self = FACEBOOK_ACCOUNT_MANAGER_DIALOG (object);
+
+ _g_object_unref (self->priv->builder);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+
+static void
+facebook_account_manager_dialog_class_init (FacebookAccountManagerDialogClass *klass)
+{
+ GObjectClass *object_class;
+
+ parent_class = g_type_class_peek_parent (klass);
+ g_type_class_add_private (klass, sizeof (FacebookAccountManagerDialogPrivate));
+
+ object_class = (GObjectClass*) klass;
+ object_class->finalize = facebook_account_manager_dialog_finalize;
+}
+
+
+static void
+delete_button_clicked_cb (GtkWidget *button,
+ gpointer user_data)
+{
+ FacebookAccountManagerDialog *self = user_data;
+ GtkTreeModel *tree_model;
+ GtkTreeIter iter;
+
+ if (gtk_tree_selection_get_selected (gtk_tree_view_get_selection (GTK_TREE_VIEW (GET_WIDGET ("account_treeview"))),
+ &tree_model,
+ &iter))
+ {
+ gtk_list_store_remove (GTK_LIST_STORE (tree_model), &iter);
+ }
+}
+
+
+static void
+text_renderer_edited_cb (GtkCellRendererText *renderer,
+ char *path,
+ char *new_text,
+ gpointer user_data)
+{
+ FacebookAccountManagerDialog *self = user_data;
+ GtkTreePath *tree_path;
+ GtkTreeIter iter;
+ FacebookAccount *account;
+
+ tree_path = gtk_tree_path_new_from_string (path);
+ tree_path = gtk_tree_path_new_from_string (path);
+ if (! gtk_tree_model_get_iter (GTK_TREE_MODEL (GET_WIDGET ("accounts_liststore")),
+ &iter,
+ tree_path))
+ {
+ gtk_tree_path_free (tree_path);
+ return;
+ }
+ gtk_tree_path_free (tree_path);
+
+ gtk_tree_model_get (GTK_TREE_MODEL (GET_WIDGET ("accounts_liststore")), &iter,
+ ACCOUNT_DATA_COLUMN, &account,
+ -1);
+ facebook_account_set_username (account, new_text);
+
+ gtk_list_store_set (GTK_LIST_STORE (GET_WIDGET ("accounts_liststore")), &iter,
+ ACCOUNT_DATA_COLUMN, account,
+ ACCOUNT_NAME_COLUMN, new_text,
+ -1);
+
+ g_object_unref (account);
+}
+
+
+static void
+facebook_account_manager_dialog_init (FacebookAccountManagerDialog *self)
+{
+ GtkWidget *content;
+
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, FACEBOOK_TYPE_ACCOUNT_MANAGER_DIALOG, FacebookAccountManagerDialogPrivate);
+ self->priv->builder = _gtk_builder_new_from_file ("flicker-account-manager.ui", "flicker");
+
+ gtk_window_set_resizable (GTK_WINDOW (self), FALSE);
+ gtk_dialog_set_has_separator (GTK_DIALOG (self), FALSE);
+ gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (self))), 5);
+ gtk_container_set_border_width (GTK_CONTAINER (self), 5);
+
+ content = _gtk_builder_get_widget (self->priv->builder, "account_manager");
+ gtk_container_set_border_width (GTK_CONTAINER (content), 5);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (self))), content, TRUE, TRUE, 0);
+
+ gtk_dialog_add_button (GTK_DIALOG (self),
+ GTK_STOCK_CANCEL,
+ GTK_RESPONSE_CANCEL);
+ gtk_dialog_add_button (GTK_DIALOG (self),
+ GTK_STOCK_OK,
+ GTK_RESPONSE_OK);
+
+ g_object_set (GET_WIDGET ("account_cellrenderertext"), "editable", TRUE, NULL);
+ g_signal_connect (GET_WIDGET ("account_cellrenderertext"),
+ "edited",
+ G_CALLBACK (text_renderer_edited_cb),
+ self);
+
+ g_signal_connect (GET_WIDGET ("delete_button"),
+ "clicked",
+ G_CALLBACK (delete_button_clicked_cb),
+ self);
+}
+
+
+GType
+facebook_account_manager_dialog_get_type (void)
+{
+ static GType type = 0;
+
+ if (type == 0) {
+ static const GTypeInfo g_define_type_info = {
+ sizeof (FacebookAccountManagerDialogClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) facebook_account_manager_dialog_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL,
+ sizeof (FacebookAccountManagerDialog),
+ 0,
+ (GInstanceInitFunc) facebook_account_manager_dialog_init,
+ NULL
+ };
+ type = g_type_register_static (GTK_TYPE_DIALOG,
+ "FacebookAccountManagerDialog",
+ &g_define_type_info,
+ 0);
+ }
+
+ return type;
+}
+
+
+static void
+facebook_account_manager_dialog_construct (FacebookAccountManagerDialog *self,
+ GList *accounts)
+{
+ GtkListStore *list_store;
+ GtkTreeIter iter;
+ GList *scan;
+
+ list_store = GTK_LIST_STORE (GET_WIDGET ("accounts_liststore"));
+ gtk_list_store_clear (list_store);
+ for (scan = accounts; scan; scan = scan->next) {
+ FacebookAccount *account = scan->data;
+
+ gtk_list_store_append (list_store, &iter);
+ gtk_list_store_set (list_store, &iter,
+ ACCOUNT_DATA_COLUMN, account,
+ ACCOUNT_NAME_COLUMN, account->username,
+ -1);
+ }
+}
+
+
+GtkWidget *
+facebook_account_manager_dialog_new (GList *accounts)
+{
+ FacebookAccountManagerDialog *self;
+
+ self = g_object_new (FACEBOOK_TYPE_ACCOUNT_MANAGER_DIALOG, NULL);
+ facebook_account_manager_dialog_construct (self, accounts);
+
+ return (GtkWidget *) self;
+}
+
+
+GList *
+facebook_account_manager_dialog_get_accounts (FacebookAccountManagerDialog *self)
+{
+ GList *accounts;
+ GtkTreeModel *tree_model;
+ GtkTreeIter iter;
+
+ tree_model = (GtkTreeModel *) GET_WIDGET ("accounts_liststore");
+ if (! gtk_tree_model_get_iter_first (tree_model, &iter))
+ return NULL;
+
+ accounts = NULL;
+ do {
+ FacebookAccount *account;
+
+ gtk_tree_model_get (tree_model, &iter,
+ ACCOUNT_DATA_COLUMN, &account,
+ -1);
+ accounts = g_list_prepend (accounts, account);
+ }
+ while (gtk_tree_model_iter_next (tree_model, &iter));
+
+ return g_list_reverse (accounts);
+}
diff --git a/extensions/facebook/facebook-account-manager-dialog.h b/extensions/facebook/facebook-account-manager-dialog.h
new file mode 100644
index 0000000..02f56ea
--- /dev/null
+++ b/extensions/facebook/facebook-account-manager-dialog.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * GThumb
+ *
+ * Copyright (C) 2010 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 FACEBOOK_ACCOUNT_MANAGER_DIALOG_H
+#define FACEBOOK_ACCOUNT_MANAGER_DIALOG_H
+
+#include <gtk/gtk.h>
+#include <gthumb.h>
+
+G_BEGIN_DECLS
+
+#define FACEBOOK_ACCOUNT_MANAGER_RESPONSE_NEW 1
+
+#define FACEBOOK_TYPE_ACCOUNT_MANAGER_DIALOG (facebook_account_manager_dialog_get_type ())
+#define FACEBOOK_ACCOUNT_MANAGER_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FACEBOOK_TYPE_ACCOUNT_MANAGER_DIALOG, FacebookAccountManagerDialog))
+#define FACEBOOK_ACCOUNT_MANAGER_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FACEBOOK_TYPE_ACCOUNT_MANAGER_DIALOG, FacebookAccountManagerDialogClass))
+#define FACEBOOK_IS_ACCOUNT_MANAGER_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FACEBOOK_TYPE_ACCOUNT_MANAGER_DIALOG))
+#define FACEBOOK_IS_ACCOUNT_MANAGER_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FACEBOOK_TYPE_ACCOUNT_MANAGER_DIALOG))
+#define FACEBOOK_ACCOUNT_MANAGER_DIALOG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), FACEBOOK_TYPE_ACCOUNT_MANAGER_DIALOG, FacebookAccountManagerDialogClass))
+
+typedef struct _FacebookAccountManagerDialog FacebookAccountManagerDialog;
+typedef struct _FacebookAccountManagerDialogClass FacebookAccountManagerDialogClass;
+typedef struct _FacebookAccountManagerDialogPrivate FacebookAccountManagerDialogPrivate;
+
+struct _FacebookAccountManagerDialog {
+ GtkDialog parent_instance;
+ FacebookAccountManagerDialogPrivate *priv;
+};
+
+struct _FacebookAccountManagerDialogClass {
+ GtkDialogClass parent_class;
+};
+
+GType facebook_account_manager_dialog_get_type (void);
+GtkWidget * facebook_account_manager_dialog_new (GList *accounts);
+GList * facebook_account_manager_dialog_get_accounts (FacebookAccountManagerDialog *dialog);
+
+G_END_DECLS
+
+#endif /* FACEBOOK_ACCOUNT_MANAGER_DIALOG_H */
diff --git a/extensions/facebook/facebook-account.c b/extensions/facebook/facebook-account.c
new file mode 100644
index 0000000..0abf778
--- /dev/null
+++ b/extensions/facebook/facebook-account.c
@@ -0,0 +1,180 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * GThumb
+ *
+ * Copyright (C) 2010 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 <stdlib.h>
+#include <string.h>
+#include <gthumb.h>
+#include "facebook-account.h"
+
+
+static gpointer facebook_account_parent_class = NULL;
+
+
+static void
+facebook_account_finalize (GObject *obj)
+{
+ FacebookAccount *self;
+
+ self = FACEBOOK_ACCOUNT (obj);
+
+ g_free (self->username);
+ g_free (self->token);
+
+ G_OBJECT_CLASS (facebook_account_parent_class)->finalize (obj);
+}
+
+
+static void
+facebook_account_class_init (FacebookAccountClass *klass)
+{
+ facebook_account_parent_class = g_type_class_peek_parent (klass);
+ G_OBJECT_CLASS (klass)->finalize = facebook_account_finalize;
+}
+
+
+static DomElement*
+facebook_account_create_element (DomDomizable *base,
+ DomDocument *doc)
+{
+ FacebookAccount *self;
+ DomElement *element;
+
+ self = FACEBOOK_ACCOUNT (base);
+
+ element = dom_document_create_element (doc, "account", NULL);
+ if (self->username != NULL)
+ dom_element_set_attribute (element, "username", self->username);
+ if (self->token != NULL)
+ dom_element_set_attribute (element, "token", self->token);
+ if (self->is_default)
+ dom_element_set_attribute (element, "default", "1");
+
+ return element;
+}
+
+
+static void
+facebook_account_load_from_element (DomDomizable *base,
+ DomElement *element)
+{
+ FacebookAccount *self;
+
+ self = FACEBOOK_ACCOUNT (base);
+
+ facebook_account_set_username (self, dom_element_get_attribute (element, "username"));
+ facebook_account_set_token (self, dom_element_get_attribute (element, "token"));
+ self->is_default = (g_strcmp0 (dom_element_get_attribute (element, "default"), "1") == 0);
+}
+
+
+static void
+facebook_account_dom_domizable_interface_init (DomDomizableIface *iface)
+{
+ iface->create_element = facebook_account_create_element;
+ iface->load_from_element = facebook_account_load_from_element;
+}
+
+
+static void
+facebook_account_instance_init (FacebookAccount *self)
+{
+}
+
+
+GType
+facebook_account_get_type (void)
+{
+ static GType facebook_account_type_id = 0;
+
+ if (facebook_account_type_id == 0) {
+ static const GTypeInfo g_define_type_info = {
+ sizeof (FacebookAccountClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) facebook_account_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL,
+ sizeof (FacebookAccount),
+ 0,
+ (GInstanceInitFunc) facebook_account_instance_init,
+ NULL
+ };
+ static const GInterfaceInfo dom_domizable_info = {
+ (GInterfaceInitFunc) facebook_account_dom_domizable_interface_init,
+ (GInterfaceFinalizeFunc) NULL,
+ NULL
+ };
+
+ facebook_account_type_id = g_type_register_static (G_TYPE_OBJECT,
+ "FacebookAccount",
+ &g_define_type_info,
+ 0);
+ g_type_add_interface_static (facebook_account_type_id, DOM_TYPE_DOMIZABLE, &dom_domizable_info);
+ }
+
+ return facebook_account_type_id;
+}
+
+
+FacebookAccount *
+facebook_account_new (void)
+{
+ return g_object_new (FACEBOOK_TYPE_ACCOUNT, NULL);
+}
+
+
+void
+facebook_account_set_username (FacebookAccount *self,
+ const char *value)
+{
+ g_free (self->username);
+ self->username = NULL;
+ if (value != NULL)
+ self->username = g_strdup (value);
+}
+
+
+void
+facebook_account_set_token (FacebookAccount *self,
+ const char *value)
+{
+ g_free (self->token);
+ self->token = NULL;
+ if (value != NULL)
+ self->token = g_strdup (value);
+}
+
+
+int
+facebook_account_cmp (FacebookAccount *a,
+ FacebookAccount *b)
+{
+ if ((a == NULL) && (b == NULL))
+ return 0;
+ else if (a == NULL)
+ return 1;
+ else if (b == NULL)
+ return -1;
+ else
+ return g_strcmp0 (a->username, b->username);
+}
diff --git a/extensions/facebook/facebook-account.h b/extensions/facebook/facebook-account.h
new file mode 100644
index 0000000..20e7dcd
--- /dev/null
+++ b/extensions/facebook/facebook-account.h
@@ -0,0 +1,66 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * GThumb
+ *
+ * Copyright (C) 2010 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 FACEBOOK_ACCOUNT_H
+#define FACEBOOK_ACCOUNT_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define FACEBOOK_TYPE_ACCOUNT (facebook_account_get_type ())
+#define FACEBOOK_ACCOUNT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FACEBOOK_TYPE_ACCOUNT, FacebookAccount))
+#define FACEBOOK_ACCOUNT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FACEBOOK_TYPE_ACCOUNT, FacebookAccountClass))
+#define FACEBOOK_IS_ACCOUNT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FACEBOOK_TYPE_ACCOUNT))
+#define FACEBOOK_IS_ACCOUNT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FACEBOOK_TYPE_ACCOUNT))
+#define FACEBOOK_ACCOUNT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), FACEBOOK_TYPE_ACCOUNT, FacebookAccountClass))
+
+typedef struct _FacebookAccount FacebookAccount;
+typedef struct _FacebookAccountClass FacebookAccountClass;
+typedef struct _FacebookAccountPrivate FacebookAccountPrivate;
+
+struct _FacebookAccount {
+ GObject parent_instance;
+ FacebookAccountPrivate *priv;
+
+ char *username;
+ char *token;
+ gboolean is_default;
+};
+
+struct _FacebookAccountClass {
+ GObjectClass parent_class;
+};
+
+GType facebook_account_get_type (void);
+FacebookAccount * facebook_account_new (void);
+void facebook_account_set_username (FacebookAccount *self,
+ const char *value);
+void facebook_account_set_token (FacebookAccount *self,
+ const char *value);
+int facebook_account_cmp (FacebookAccount *a,
+ FacebookAccount *b);
+
+G_END_DECLS
+
+#endif /* FACEBOOK_ACCOUNT_H */
diff --git a/extensions/facebook/facebook-album.c b/extensions/facebook/facebook-album.c
new file mode 100644
index 0000000..0573fe5
--- /dev/null
+++ b/extensions/facebook/facebook-album.c
@@ -0,0 +1,286 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * GThumb
+ *
+ * Copyright (C) 2010 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 <stdlib.h>
+#include <string.h>
+#include <gthumb.h>
+#include "facebook-photoset.h"
+
+
+static gpointer facebook_photoset_parent_class = NULL;
+
+
+static void
+facebook_photoset_finalize (GObject *obj)
+{
+ FacebookPhotoset *self;
+
+ self = FACEBOOK_PHOTOSET (obj);
+
+ g_free (self->id);
+ g_free (self->title);
+ g_free (self->description);
+ g_free (self->primary);
+ g_free (self->secret);
+ g_free (self->server);
+ g_free (self->farm);
+
+ G_OBJECT_CLASS (facebook_photoset_parent_class)->finalize (obj);
+}
+
+
+static void
+facebook_photoset_class_init (FacebookPhotosetClass *klass)
+{
+ facebook_photoset_parent_class = g_type_class_peek_parent (klass);
+ G_OBJECT_CLASS (klass)->finalize = facebook_photoset_finalize;
+}
+
+
+static DomElement*
+facebook_photoset_create_element (DomDomizable *base,
+ DomDocument *doc)
+{
+ FacebookPhotoset *self;
+ DomElement *element;
+ char *value;
+
+ self = FACEBOOK_PHOTOSET (base);
+
+ element = dom_document_create_element (doc, "photoset", NULL);
+ if (self->id != NULL)
+ dom_element_set_attribute (element, "id", self->id);
+ if (self->primary != NULL)
+ dom_element_set_attribute (element, "primary", self->primary);
+ if (self->secret != NULL)
+ dom_element_set_attribute (element, "secret", self->secret);
+ if (self->server != NULL)
+ dom_element_set_attribute (element, "server", self->server);
+ if (self->n_photos >= 0) {
+ value = g_strdup_printf ("%d", self->n_photos);
+ dom_element_set_attribute (element, "photos", value);
+ g_free (value);
+ }
+ if (self->farm != NULL)
+ dom_element_set_attribute (element, "farm", self->farm);
+
+ if (self->title != NULL)
+ dom_element_append_child (element, dom_document_create_element_with_text (doc, self->title, "title", NULL));
+ if (self->description != NULL)
+ dom_element_append_child (element, dom_document_create_element_with_text (doc, self->description, "description", NULL));
+
+ return element;
+}
+
+
+static void
+facebook_photoset_load_from_element (DomDomizable *base,
+ DomElement *element)
+{
+ FacebookPhotoset *self;
+ DomElement *node;
+
+ self = FACEBOOK_PHOTOSET (base);
+
+ facebook_photoset_set_id (self, dom_element_get_attribute (element, "id"));
+ facebook_photoset_set_title (self, NULL);
+ facebook_photoset_set_description (self, NULL);
+ facebook_photoset_set_n_photos (self, dom_element_get_attribute (element, "photos"));
+ facebook_photoset_set_primary (self, dom_element_get_attribute (element, "primary"));
+ facebook_photoset_set_secret (self, dom_element_get_attribute (element, "secret"));
+ facebook_photoset_set_server (self, dom_element_get_attribute (element, "server"));
+ facebook_photoset_set_farm (self, dom_element_get_attribute (element, "farm"));
+ facebook_photoset_set_url (self, dom_element_get_attribute (element, "url"));
+
+ for (node = element->first_child; node; node = node->next_sibling) {
+ if (g_strcmp0 (node->tag_name, "title") == 0) {
+ facebook_photoset_set_title (self, dom_element_get_inner_text (node));
+ }
+ else if (g_strcmp0 (node->tag_name, "description") == 0) {
+ facebook_photoset_set_description (self, dom_element_get_inner_text (node));
+ }
+ }
+}
+
+
+static void
+facebook_photoset_dom_domizable_interface_init (DomDomizableIface *iface)
+{
+ iface->create_element = facebook_photoset_create_element;
+ iface->load_from_element = facebook_photoset_load_from_element;
+}
+
+
+static void
+facebook_photoset_instance_init (FacebookPhotoset *self)
+{
+ self->id = NULL;
+ self->title = NULL;
+ self->description = NULL;
+ self->primary = NULL;
+ self->secret = NULL;
+ self->server = NULL;
+ self->farm = NULL;
+ self->url = NULL;
+}
+
+
+GType
+facebook_photoset_get_type (void)
+{
+ static GType facebook_photoset_type_id = 0;
+
+ if (facebook_photoset_type_id == 0) {
+ static const GTypeInfo g_define_type_info = {
+ sizeof (FacebookPhotosetClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) facebook_photoset_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL,
+ sizeof (FacebookPhotoset),
+ 0,
+ (GInstanceInitFunc) facebook_photoset_instance_init,
+ NULL
+ };
+ static const GInterfaceInfo dom_domizable_info = {
+ (GInterfaceInitFunc) facebook_photoset_dom_domizable_interface_init,
+ (GInterfaceFinalizeFunc) NULL,
+ NULL
+ };
+
+ facebook_photoset_type_id = g_type_register_static (G_TYPE_OBJECT,
+ "FacebookPhotoset",
+ &g_define_type_info,
+ 0);
+ g_type_add_interface_static (facebook_photoset_type_id, DOM_TYPE_DOMIZABLE, &dom_domizable_info);
+ }
+
+ return facebook_photoset_type_id;
+}
+
+
+FacebookPhotoset *
+facebook_photoset_new (void)
+{
+ return g_object_new (FACEBOOK_TYPE_PHOTOSET, NULL);
+}
+
+
+void
+facebook_photoset_set_id (FacebookPhotoset *self,
+ const char *value)
+{
+ g_free (self->id);
+ self->id = NULL;
+ if (value != NULL)
+ self->id = g_strdup (value);
+}
+
+
+void
+facebook_photoset_set_title (FacebookPhotoset *self,
+ const char *value)
+{
+ g_free (self->title);
+ self->title = NULL;
+ if (value != NULL)
+ self->title = g_strdup (value);
+}
+
+
+void
+facebook_photoset_set_description (FacebookPhotoset *self,
+ const char *value)
+{
+ g_free (self->description);
+ self->description = NULL;
+ if (value != NULL)
+ self->description = g_strdup (value);
+}
+
+
+void
+facebook_photoset_set_n_photos (FacebookPhotoset *self,
+ const char *value)
+{
+ if (value != NULL)
+ self->n_photos = atoi (value);
+ else
+ self->n_photos = 0;
+}
+
+
+void
+facebook_photoset_set_primary (FacebookPhotoset *self,
+ const char *value)
+{
+ g_free (self->primary);
+ self->primary = NULL;
+ if (value != NULL)
+ self->primary = g_strdup (value);
+}
+
+
+void
+facebook_photoset_set_secret (FacebookPhotoset *self,
+ const char *value)
+{
+ g_free (self->secret);
+ self->secret = NULL;
+ if (value != NULL)
+ self->secret = g_strdup (value);
+}
+
+
+void
+facebook_photoset_set_server (FacebookPhotoset *self,
+ const char *value)
+{
+ g_free (self->server);
+ self->server = NULL;
+ if (value != NULL)
+ self->server = g_strdup (value);
+}
+
+
+void
+facebook_photoset_set_farm (FacebookPhotoset *self,
+ const char *value)
+{
+ g_free (self->farm);
+ self->farm = NULL;
+ if (value != NULL)
+ self->farm = g_strdup (value);
+}
+
+
+void
+facebook_photoset_set_url (FacebookPhotoset *self,
+ const char *value)
+{
+ g_free (self->url);
+ self->url = NULL;
+ if (value != NULL)
+ self->url = g_strdup (value);
+}
diff --git a/extensions/facebook/facebook-album.h b/extensions/facebook/facebook-album.h
new file mode 100644
index 0000000..7aabea8
--- /dev/null
+++ b/extensions/facebook/facebook-album.h
@@ -0,0 +1,82 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * GThumb
+ *
+ * Copyright (C) 2010 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 FACEBOOK_PHOTOSET_H
+#define FACEBOOK_PHOTOSET_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define FACEBOOK_TYPE_PHOTOSET (facebook_photoset_get_type ())
+#define FACEBOOK_PHOTOSET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FACEBOOK_TYPE_PHOTOSET, FacebookPhotoset))
+#define FACEBOOK_PHOTOSET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FACEBOOK_TYPE_PHOTOSET, FacebookPhotosetClass))
+#define FACEBOOK_IS_PHOTOSET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FACEBOOK_TYPE_PHOTOSET))
+#define FACEBOOK_IS_PHOTOSET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FACEBOOK_TYPE_PHOTOSET))
+#define FACEBOOK_PHOTOSET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), FACEBOOK_TYPE_PHOTOSET, FacebookPhotosetClass))
+
+typedef struct _FacebookPhotoset FacebookPhotoset;
+typedef struct _FacebookPhotosetClass FacebookPhotosetClass;
+
+struct _FacebookPhotoset {
+ GObject parent_instance;
+
+ char *id;
+ char *title;
+ char *description;
+ int n_photos;
+ char *primary;
+ char *secret;
+ char *server;
+ char *farm;
+ char *url;
+};
+
+struct _FacebookPhotosetClass {
+ GObjectClass parent_class;
+};
+
+GType facebook_photoset_get_type (void);
+FacebookPhotoset * facebook_photoset_new (void);
+void facebook_photoset_set_id (FacebookPhotoset *self,
+ const char *value);
+void facebook_photoset_set_title (FacebookPhotoset *self,
+ const char *value);
+void facebook_photoset_set_description (FacebookPhotoset *self,
+ const char *value);
+void facebook_photoset_set_n_photos (FacebookPhotoset *self,
+ const char *value);
+void facebook_photoset_set_primary (FacebookPhotoset *self,
+ const char *value);
+void facebook_photoset_set_secret (FacebookPhotoset *self,
+ const char *value);
+void facebook_photoset_set_server (FacebookPhotoset *self,
+ const char *value);
+void facebook_photoset_set_farm (FacebookPhotoset *self,
+ const char *value);
+void facebook_photoset_set_url (FacebookPhotoset *self,
+ const char *value);
+
+G_END_DECLS
+
+#endif /* FACEBOOK_PHOTOSET_H */
diff --git a/extensions/facebook/facebook-connection.c b/extensions/facebook/facebook-connection.c
new file mode 100644
index 0000000..a5b54b9
--- /dev/null
+++ b/extensions/facebook/facebook-connection.c
@@ -0,0 +1,560 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * GThumb
+ *
+ * Copyright (C) 2010 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.h>
+#include <glib/gi18n.h>
+#include <gthumb.h>
+#include "facebook-connection.h"
+#include "facebook-user.h"
+
+
+#undef DEBUG_FACEBOOK_CONNECTION
+#define GTHUMB_FACEBOOK_API_KEY "8960706ee7f4151e893b11837e9c24ce"
+#define GTHUMB_FACEBOOK_SHARED_SECRET "1ff8d1e45c873423"
+
+
+GQuark
+facebook_connection_error_quark (void)
+{
+ static GQuark quark;
+
+ if (!quark)
+ quark = g_quark_from_static_string ("facebook_connection");
+
+ return quark;
+}
+
+
+/* -- FacebookConnection -- */
+
+
+struct _FacebookConnectionPrivate
+{
+ SoupSession *session;
+ char *frob;
+ char *token;
+ char *username;
+ char *user_id;
+ GCancellable *cancellable;
+ GSimpleAsyncResult *result;
+ GChecksum *checksum;
+};
+
+
+static gpointer parent_class = NULL;
+
+
+static void
+facebook_connection_finalize (GObject *object)
+{
+ FacebookConnection *self;
+
+ self = FACEBOOK_CONNECTION (object);
+
+ g_checksum_free (self->priv->checksum);
+ _g_object_unref (self->priv->result);
+ _g_object_unref (self->priv->cancellable);
+ g_free (self->priv->user_id);
+ g_free (self->priv->username);
+ g_free (self->priv->token);
+ g_free (self->priv->frob);
+ _g_object_unref (self->priv->session);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+
+static void
+facebook_connection_exec (GthTask *base)
+{
+ /* void */
+}
+
+
+static void
+facebook_connection_cancelled (GthTask *base)
+{
+ /* void */
+}
+
+
+static void
+facebook_connection_class_init (FacebookConnectionClass *klass)
+{
+ GObjectClass *object_class;
+ GthTaskClass *task_class;
+
+ parent_class = g_type_class_peek_parent (klass);
+ g_type_class_add_private (klass, sizeof (FacebookConnectionPrivate));
+
+ object_class = (GObjectClass*) klass;
+ object_class->finalize = facebook_connection_finalize;
+
+ task_class = (GthTaskClass*) klass;
+ task_class->exec = facebook_connection_exec;
+ task_class->cancelled = facebook_connection_cancelled;
+}
+
+
+static void
+facebook_connection_init (FacebookConnection *self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, FACEBOOK_TYPE_CONNECTION, FacebookConnectionPrivate);
+ self->priv->session = NULL;
+ self->priv->username = NULL;
+ self->priv->user_id = NULL;
+ self->priv->token = NULL;
+ self->priv->frob = NULL;
+ self->priv->cancellable = NULL;
+ self->priv->result = NULL;
+ self->priv->checksum = g_checksum_new (G_CHECKSUM_MD5);
+}
+
+
+GType
+facebook_connection_get_type (void)
+{
+ static GType type = 0;
+
+ if (! type) {
+ GTypeInfo type_info = {
+ sizeof (FacebookConnectionClass),
+ NULL,
+ NULL,
+ (GClassInitFunc) facebook_connection_class_init,
+ NULL,
+ NULL,
+ sizeof (FacebookConnection),
+ 0,
+ (GInstanceInitFunc) facebook_connection_init
+ };
+
+ type = g_type_register_static (GTH_TYPE_TASK,
+ "FacebookConnection",
+ &type_info,
+ 0);
+ }
+
+ return type;
+}
+
+
+FacebookConnection *
+facebook_connection_new (void)
+{
+ return (FacebookConnection *) g_object_new (FACEBOOK_TYPE_CONNECTION, NULL);
+}
+
+
+void
+facebook_connection_send_message (FacebookConnection *self,
+ SoupMessage *msg,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data,
+ gpointer source_tag,
+ SoupSessionCallback soup_session_cb,
+ gpointer soup_session_cb_data)
+{
+ if (self->priv->session == NULL) {
+ self->priv->session = soup_session_async_new_with_options (
+#ifdef HAVE_LIBSOUP_GNOME
+ SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_PROXY_RESOLVER_GNOME,
+#endif
+ NULL);
+
+#ifdef DEBUG_FACEBOOK_CONNECTION
+ {
+ SoupLogger *logger;
+
+ logger = soup_logger_new (SOUP_LOGGER_LOG_BODY, -1);
+ soup_session_add_feature (self->priv->session, SOUP_SESSION_FEATURE (logger));
+
+ g_object_unref (logger);
+ }
+#endif
+ }
+
+ _g_object_unref (self->priv->cancellable);
+ self->priv->cancellable = _g_object_ref (cancellable);
+
+ _g_object_unref (self->priv->result);
+ self->priv->result = g_simple_async_result_new (G_OBJECT (soup_session_cb_data),
+ callback,
+ user_data,
+ source_tag);
+
+ soup_session_queue_message (self->priv->session,
+ msg,
+ soup_session_cb,
+ soup_session_cb_data);
+}
+
+
+GSimpleAsyncResult *
+facebook_connection_get_result (FacebookConnection *self)
+{
+ return self->priv->result;
+}
+
+
+void
+facebook_connection_reset_result (FacebookConnection *self)
+{
+ _g_object_unref (self->priv->result);
+ self->priv->result = NULL;
+}
+
+
+void
+facebook_connection_add_api_sig (FacebookConnection *self,
+ GHashTable *data_set)
+{
+ GList *keys;
+ GList *scan;
+
+ g_hash_table_insert (data_set, "api_key", GTHUMB_FACEBOOK_API_KEY);
+ if (self->priv->token != NULL)
+ g_hash_table_insert (data_set, "auth_token", self->priv->token);
+
+ g_checksum_reset (self->priv->checksum);
+ g_checksum_update (self->priv->checksum, (guchar *) GTHUMB_FACEBOOK_SHARED_SECRET, -1);
+
+ keys = g_hash_table_get_keys (data_set);
+ keys = g_list_sort (keys, (GCompareFunc) strcmp);
+ for (scan = keys; scan; scan = scan->next) {
+ char *key = scan->data;
+
+ g_checksum_update (self->priv->checksum, (guchar *) key, -1);
+ g_checksum_update (self->priv->checksum, g_hash_table_lookup (data_set, key), -1);
+ }
+ g_hash_table_insert (data_set, "api_sig", (gpointer) g_checksum_get_string (self->priv->checksum));
+
+ g_list_free (keys);
+}
+
+
+static void
+connection_frob_ready_cb (SoupSession *session,
+ SoupMessage *msg,
+ gpointer user_data)
+{
+ FacebookConnection *self = user_data;
+ SoupBuffer *body;
+ DomDocument *doc = NULL;
+ GError *error = NULL;
+
+ g_free (self->priv->frob);
+ self->priv->frob = NULL;
+
+ body = soup_message_body_flatten (msg->response_body);
+ if (facebook_utils_parse_response (body, &doc, &error)) {
+ DomElement *root;
+ DomElement *child;
+
+ root = DOM_ELEMENT (doc)->first_child;
+ for (child = root->first_child; child; child = child->next_sibling)
+ if (g_strcmp0 (child->tag_name, "frob") == 0)
+ self->priv->frob = g_strdup (dom_element_get_inner_text (child));
+
+ if (self->priv->frob == NULL) {
+ error = g_error_new_literal (FACEBOOK_CONNECTION_ERROR, 0, _("Unknown error"));
+ g_simple_async_result_set_from_error (self->priv->result, error);
+ }
+ else
+ g_simple_async_result_set_op_res_gboolean (self->priv->result, TRUE);
+
+ g_object_unref (doc);
+ }
+ else
+ g_simple_async_result_set_from_error (self->priv->result, error);
+
+ g_simple_async_result_complete_in_idle (self->priv->result);
+
+ soup_buffer_free (body);
+}
+
+
+void
+facebook_connection_get_frob (FacebookConnection *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GHashTable *data_set;
+ SoupMessage *msg;
+
+ gth_task_progress (GTH_TASK (self), _("Connecting to the server"), NULL, TRUE, 0.0);
+
+ data_set = g_hash_table_new (g_str_hash, g_str_equal);
+ g_hash_table_insert (data_set, "method", "facebook.auth.getFrob");
+ facebook_connection_add_api_sig (self, data_set);
+ msg = soup_form_request_new_from_hash ("GET", "http://api.facebook.com/services/rest", data_set);
+ facebook_connection_send_message (self,
+ msg,
+ cancellable,
+ callback,
+ user_data,
+ facebook_connection_get_frob,
+ connection_frob_ready_cb,
+ self);
+
+ g_hash_table_destroy (data_set);
+}
+
+
+gboolean
+facebook_connection_get_frob_finish (FacebookConnection *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
+ return FALSE;
+ else
+ return TRUE;
+}
+
+
+static char *
+get_access_type_name (FacebookAccessType access_type)
+{
+ char *name = NULL;
+
+ switch (access_type) {
+ case FACEBOOK_ACCESS_READ:
+ name = "read";
+ break;
+
+ case FACEBOOK_ACCESS_WRITE:
+ name = "write";
+ break;
+
+ case FACEBOOK_ACCESS_DELETE:
+ name = "delete";
+ break;
+ }
+
+ return name;
+}
+
+char *
+facebook_connection_get_login_link (FacebookConnection *self,
+ FacebookAccessType access_type)
+{
+ GHashTable *data_set;
+ GString *link;
+ GList *keys;
+ GList *scan;
+
+ g_return_val_if_fail (self->priv->frob != NULL, NULL);
+
+ data_set = g_hash_table_new (g_str_hash, g_str_equal);
+ g_hash_table_insert (data_set, "frob", self->priv->frob);
+ g_hash_table_insert (data_set, "perms", get_access_type_name (access_type));
+ facebook_connection_add_api_sig (self, data_set);
+
+ link = g_string_new ("http://www.facebook.com/services/auth/?");
+ keys = g_hash_table_get_keys (data_set);
+ for (scan = keys; scan; scan = scan->next) {
+ char *key = scan->data;
+
+ if (scan != keys)
+ g_string_append (link, "&");
+ g_string_append (link, key);
+ g_string_append (link, "=");
+ g_string_append (link, g_hash_table_lookup (data_set, key));
+ }
+
+ g_list_free (keys);
+ g_hash_table_destroy (data_set);
+
+ return g_string_free (link, FALSE);
+}
+
+
+static void
+connection_token_ready_cb (SoupSession *session,
+ SoupMessage *msg,
+ gpointer user_data)
+{
+ FacebookConnection *self = user_data;
+ SoupBuffer *body;
+ DomDocument *doc = NULL;
+ GError *error = NULL;
+
+ body = soup_message_body_flatten (msg->response_body);
+ if (facebook_utils_parse_response (body, &doc, &error)) {
+ DomElement *response;
+ DomElement *auth;
+
+ response = DOM_ELEMENT (doc)->first_child;
+ for (auth = response->first_child; auth; auth = auth->next_sibling) {
+ if (g_strcmp0 (auth->tag_name, "auth") == 0) {
+ DomElement *node;
+
+ for (node = auth->first_child; node; node = node->next_sibling) {
+ if (g_strcmp0 (node->tag_name, "token") == 0) {
+ self->priv->token = g_strdup (dom_element_get_inner_text (node));
+ }
+ else if (g_strcmp0 (node->tag_name, "user") == 0) {
+ self->priv->username = g_strdup (dom_element_get_attribute (node, "username"));
+ self->priv->user_id = g_strdup (dom_element_get_attribute (node, "nsid"));
+ }
+ }
+ }
+ }
+
+ if (self->priv->token == NULL) {
+ error = g_error_new_literal (FACEBOOK_CONNECTION_ERROR, 0, _("Unknown error"));
+ g_simple_async_result_set_from_error (self->priv->result, error);
+ }
+ else
+ g_simple_async_result_set_op_res_gboolean (self->priv->result, TRUE);
+
+ g_object_unref (doc);
+ }
+ else
+ g_simple_async_result_set_from_error (self->priv->result, error);
+
+ g_simple_async_result_complete_in_idle (self->priv->result);
+
+ soup_buffer_free (body);
+}
+
+
+void
+facebook_connection_get_token (FacebookConnection *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GHashTable *data_set;
+ SoupMessage *msg;
+
+ gth_task_progress (GTH_TASK (self), _("Connecting to the server"), NULL, TRUE, 0.0);
+
+ g_free (self->priv->token);
+ self->priv->token = NULL;
+
+ data_set = g_hash_table_new (g_str_hash, g_str_equal);
+ g_hash_table_insert (data_set, "method", "facebook.auth.getToken");
+ g_hash_table_insert (data_set, "frob", self->priv->frob);
+ facebook_connection_add_api_sig (self, data_set);
+ msg = soup_form_request_new_from_hash ("GET", "http://api.facebook.com/services/rest", data_set);
+ facebook_connection_send_message (self,
+ msg,
+ cancellable,
+ callback,
+ user_data,
+ facebook_connection_get_token,
+ connection_token_ready_cb,
+ self);
+
+ g_hash_table_destroy (data_set);
+}
+
+
+gboolean
+facebook_connection_get_token_finish (FacebookConnection *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
+ return FALSE;
+ else
+ return TRUE;
+}
+
+
+void
+facebook_connection_set_auth_token (FacebookConnection *self,
+ const char *value)
+{
+ g_free (self->priv->token);
+ self->priv->token = NULL;
+ if (value != NULL)
+ self->priv->token = g_strdup (value);
+}
+
+
+const char *
+facebook_connection_get_auth_token (FacebookConnection *self)
+{
+ return self->priv->token;
+}
+
+
+const char *
+facebook_connection_get_username (FacebookConnection *self)
+{
+ return self->priv->username;
+}
+
+
+const char *
+facebook_connection_get_user_id (FacebookConnection *self)
+{
+ return self->priv->user_id;
+}
+
+
+/* utilities */
+
+
+gboolean
+facebook_utils_parse_response (SoupBuffer *body,
+ DomDocument **doc_p,
+ GError **error)
+{
+ DomDocument *doc;
+ DomElement *node;
+
+ doc = dom_document_new ();
+ if (! dom_document_load (doc, body->data, body->length, error)) {
+ g_object_unref (doc);
+ return FALSE;
+ }
+
+ for (node = DOM_ELEMENT (doc)->first_child; node; node = node->next_sibling) {
+ if (g_strcmp0 (node->tag_name, "rsp") == 0) {
+ if (g_strcmp0 (dom_element_get_attribute (node, "stat"), "ok") != 0) {
+ DomElement *child;
+
+ for (child = node->first_child; child; child = child->next_sibling) {
+ if (g_strcmp0 (child->tag_name, "err") == 0) {
+ *error = g_error_new_literal (FACEBOOK_CONNECTION_ERROR,
+ atoi (dom_element_get_attribute (child, "code")),
+ dom_element_get_attribute (child, "msg"));
+ }
+ }
+
+ g_object_unref (doc);
+ return FALSE;
+ }
+ }
+ }
+
+ *doc_p = doc;
+
+ return TRUE;
+}
diff --git a/extensions/facebook/facebook-connection.h b/extensions/facebook/facebook-connection.h
new file mode 100644
index 0000000..c2ae844
--- /dev/null
+++ b/extensions/facebook/facebook-connection.h
@@ -0,0 +1,107 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * GThumb
+ *
+ * Copyright (C) 2010 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 FACEBOOK_CONNECTION_H
+#define FACEBOOK_CONNECTION_H
+
+#include <glib-object.h>
+#ifdef HAVE_LIBSOUP_GNOME
+#include <libsoup/soup-gnome.h>
+#else
+#include <libsoup/soup.h>
+#endif /* HAVE_LIBSOUP_GNOME */
+#include <gthumb.h>
+
+typedef enum {
+ FACEBOOK_ACCESS_READ,
+ FACEBOOK_ACCESS_WRITE,
+ FACEBOOK_ACCESS_DELETE
+} FacebookAccessType;
+
+#define FACEBOOK_CONNECTION_ERROR facebook_connection_error_quark ()
+GQuark facebook_connection_error_quark (void);
+
+#define FACEBOOK_TYPE_CONNECTION (facebook_connection_get_type ())
+#define FACEBOOK_CONNECTION(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), FACEBOOK_TYPE_CONNECTION, FacebookConnection))
+#define FACEBOOK_CONNECTION_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), FACEBOOK_TYPE_CONNECTION, FacebookConnectionClass))
+#define FACEBOOK_IS_CONNECTION(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), FACEBOOK_TYPE_CONNECTION))
+#define FACEBOOK_IS_CONNECTION_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), FACEBOOK_TYPE_CONNECTION))
+#define FACEBOOK_CONNECTION_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), FACEBOOK_TYPE_CONNECTION, FacebookConnectionClass))
+
+typedef struct _FacebookConnection FacebookConnection;
+typedef struct _FacebookConnectionPrivate FacebookConnectionPrivate;
+typedef struct _FacebookConnectionClass FacebookConnectionClass;
+
+struct _FacebookConnection
+{
+ GthTask __parent;
+ FacebookConnectionPrivate *priv;
+};
+
+struct _FacebookConnectionClass
+{
+ GthTaskClass __parent_class;
+};
+
+GType facebook_connection_get_type (void) G_GNUC_CONST;
+FacebookConnection * facebook_connection_new (void);
+void facebook_connection_send_message (FacebookConnection *self,
+ SoupMessage *msg,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data,
+ gpointer source_tag,
+ SoupSessionCallback soup_session_cb,
+ gpointer soup_session_cb_data);
+GSimpleAsyncResult * facebook_connection_get_result (FacebookConnection *self);
+void facebook_connection_reset_result (FacebookConnection *self);
+void facebook_connection_add_api_sig (FacebookConnection *self,
+ GHashTable *data_set);
+void facebook_connection_get_frob (FacebookConnection *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean facebook_connection_get_frob_finish (FacebookConnection *self,
+ GAsyncResult *result,
+ GError **error);
+char * facebook_connection_get_login_link (FacebookConnection *self,
+ FacebookAccessType access_type);
+void facebook_connection_get_token (FacebookConnection *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean facebook_connection_get_token_finish (FacebookConnection *self,
+ GAsyncResult *result,
+ GError **error);
+void facebook_connection_set_auth_token (FacebookConnection *self,
+ const char *value);
+const char * facebook_connection_get_auth_token (FacebookConnection *self);
+const char * facebook_connection_get_username (FacebookConnection *self);
+const char * facebook_connection_get_user_id (FacebookConnection *self);
+
+/* utilities */
+
+gboolean facebook_utils_parse_response (SoupBuffer *body,
+ DomDocument **doc_p,
+ GError **error);
+
+#endif /* FACEBOOK_CONNECTION_H */
diff --git a/extensions/facebook/facebook-photo.c b/extensions/facebook/facebook-photo.c
new file mode 100644
index 0000000..4be7f6f
--- /dev/null
+++ b/extensions/facebook/facebook-photo.c
@@ -0,0 +1,252 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * GThumb
+ *
+ * Copyright (C) 2010 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 <stdlib.h>
+#include <string.h>
+#include <gthumb.h>
+#include "facebook-photo.h"
+
+
+static gpointer facebook_photo_parent_class = NULL;
+
+
+static void
+facebook_photo_finalize (GObject *obj)
+{
+ FacebookPhoto *self;
+
+ self = FACEBOOK_PHOTO (obj);
+
+ g_free (self->id);
+ g_free (self->secret);
+ g_free (self->server);
+ g_free (self->title);
+
+ G_OBJECT_CLASS (facebook_photo_parent_class)->finalize (obj);
+}
+
+
+static void
+facebook_photo_class_init (FacebookPhotoClass *klass)
+{
+ facebook_photo_parent_class = g_type_class_peek_parent (klass);
+ G_OBJECT_CLASS (klass)->finalize = facebook_photo_finalize;
+}
+
+
+static DomElement*
+facebook_photo_create_element (DomDomizable *base,
+ DomDocument *doc)
+{
+ FacebookPhoto *self;
+ DomElement *element;
+
+ self = FACEBOOK_PHOTO (base);
+
+ element = dom_document_create_element (doc, "photo", NULL);
+ if (self->id != NULL)
+ dom_element_set_attribute (element, "id", self->id);
+ if (self->secret != NULL)
+ dom_element_set_attribute (element, "secret", self->secret);
+ if (self->server != NULL)
+ dom_element_set_attribute (element, "server", self->server);
+ if (self->title != NULL)
+ dom_element_set_attribute (element, "title", self->title);
+ if (self->is_primary)
+ dom_element_set_attribute (element, "isprimary", "1");
+
+ return element;
+}
+
+
+static void
+facebook_photo_load_from_element (DomDomizable *base,
+ DomElement *element)
+{
+ FacebookPhoto *self;
+
+ if ((element == NULL) || (g_strcmp0 (element->tag_name, "photo") != 0))
+ return;
+
+ self = FACEBOOK_PHOTO (base);
+
+ facebook_photo_set_id (self, dom_element_get_attribute (element, "id"));
+ facebook_photo_set_secret (self, dom_element_get_attribute (element, "secret"));
+ facebook_photo_set_server (self, dom_element_get_attribute (element, "server"));
+ facebook_photo_set_title (self, dom_element_get_attribute (element, "title"));
+ facebook_photo_set_is_primary (self, dom_element_get_attribute (element, "isprimary"));
+ facebook_photo_set_url_sq (self, dom_element_get_attribute (element, "url_sq"));
+ facebook_photo_set_url_t (self, dom_element_get_attribute (element, "url_t"));
+ facebook_photo_set_url_s (self, dom_element_get_attribute (element, "url_s"));
+ facebook_photo_set_url_m (self, dom_element_get_attribute (element, "url_m"));
+ facebook_photo_set_url_o (self, dom_element_get_attribute (element, "url_o"));
+}
+
+
+static void
+facebook_photo_dom_domizable_interface_init (DomDomizableIface *iface)
+{
+ iface->create_element = facebook_photo_create_element;
+ iface->load_from_element = facebook_photo_load_from_element;
+}
+
+
+static void
+facebook_photo_instance_init (FacebookPhoto *self)
+{
+}
+
+
+GType
+facebook_photo_get_type (void)
+{
+ static GType facebook_photo_type_id = 0;
+
+ if (facebook_photo_type_id == 0) {
+ static const GTypeInfo g_define_type_info = {
+ sizeof (FacebookPhotoClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) facebook_photo_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL,
+ sizeof (FacebookPhoto),
+ 0,
+ (GInstanceInitFunc) facebook_photo_instance_init,
+ NULL
+ };
+ static const GInterfaceInfo dom_domizable_info = {
+ (GInterfaceInitFunc) facebook_photo_dom_domizable_interface_init,
+ (GInterfaceFinalizeFunc) NULL,
+ NULL
+ };
+
+ facebook_photo_type_id = g_type_register_static (G_TYPE_OBJECT,
+ "FacebookPhoto",
+ &g_define_type_info,
+ 0);
+ g_type_add_interface_static (facebook_photo_type_id, DOM_TYPE_DOMIZABLE, &dom_domizable_info);
+ }
+
+ return facebook_photo_type_id;
+}
+
+
+FacebookPhoto *
+facebook_photo_new (void)
+{
+ return g_object_new (FACEBOOK_TYPE_PHOTO, NULL);
+}
+
+
+void
+facebook_photo_set_id (FacebookPhoto *self,
+ const char *value)
+{
+ _g_strset (&self->id, value);
+}
+
+
+void
+facebook_photo_set_secret (FacebookPhoto *self,
+ const char *value)
+{
+ _g_strset (&self->secret, value);
+}
+
+
+void
+facebook_photo_set_server (FacebookPhoto *self,
+ const char *value)
+{
+ _g_strset (&self->server, value);
+}
+
+
+void
+facebook_photo_set_title (FacebookPhoto *self,
+ const char *value)
+{
+ _g_strset (&self->title, value);
+}
+
+
+void
+facebook_photo_set_is_primary (FacebookPhoto *self,
+ const char *value)
+{
+ self->is_primary = (g_strcmp0 (value, "1") == 0);
+}
+
+
+void
+facebook_photo_set_url_sq (FacebookPhoto *self,
+ const char *value)
+{
+ _g_strset (&self->url_sq, value);
+}
+
+
+void
+facebook_photo_set_url_t (FacebookPhoto *self,
+ const char *value)
+{
+ _g_strset (&self->url_t, value);
+}
+
+
+void
+facebook_photo_set_url_s (FacebookPhoto *self,
+ const char *value)
+{
+ _g_strset (&self->url_s, value);
+}
+
+
+void
+facebook_photo_set_url_m (FacebookPhoto *self,
+ const char *value)
+{
+ _g_strset (&self->url_m, value);
+}
+
+
+void
+facebook_photo_set_url_o (FacebookPhoto *self,
+ const char *value)
+{
+ _g_strset (&self->url_o, value);
+}
+
+
+void
+facebook_photo_set_original_format (FacebookPhoto *self,
+ const char *value)
+{
+ _g_strset (&self->original_format, value);
+
+ g_free (self->mime_type);
+ self->mime_type = NULL;
+ if (self->original_format != NULL)
+ self->mime_type = g_strconcat ("image/", self->original_format, NULL);
+}
diff --git a/extensions/facebook/facebook-photo.h b/extensions/facebook/facebook-photo.h
new file mode 100644
index 0000000..11c4c87
--- /dev/null
+++ b/extensions/facebook/facebook-photo.h
@@ -0,0 +1,92 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * GThumb
+ *
+ * Copyright (C) 2010 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 FACEBOOK_PHOTO_H
+#define FACEBOOK_PHOTO_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define FACEBOOK_TYPE_PHOTO (facebook_photo_get_type ())
+#define FACEBOOK_PHOTO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FACEBOOK_TYPE_PHOTO, FacebookPhoto))
+#define FACEBOOK_PHOTO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FACEBOOK_TYPE_PHOTO, FacebookPhotoClass))
+#define FACEBOOK_IS_PHOTO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FACEBOOK_TYPE_PHOTO))
+#define FACEBOOK_IS_PHOTO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FACEBOOK_TYPE_PHOTO))
+#define FACEBOOK_PHOTO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), FACEBOOK_TYPE_PHOTO, FacebookPhotoClass))
+
+typedef struct _FacebookPhoto FacebookPhoto;
+typedef struct _FacebookPhotoClass FacebookPhotoClass;
+typedef struct _FacebookPhotoPrivate FacebookPhotoPrivate;
+
+struct _FacebookPhoto {
+ GObject parent_instance;
+ FacebookPhotoPrivate *priv;
+
+ char *id;
+ char *secret;
+ char *server;
+ char *title;
+ gboolean is_primary;
+ char *url_sq;
+ char *url_t;
+ char *url_s;
+ char *url_m;
+ char *url_o;
+ char *original_format;
+ char *mime_type;
+ int position;
+};
+
+struct _FacebookPhotoClass {
+ GObjectClass parent_class;
+};
+
+GType facebook_photo_get_type (void);
+FacebookPhoto * facebook_photo_new (void);
+void facebook_photo_set_id (FacebookPhoto *self,
+ const char *value);
+void facebook_photo_set_secret (FacebookPhoto *self,
+ const char *value);
+void facebook_photo_set_server (FacebookPhoto *self,
+ const char *value);
+void facebook_photo_set_title (FacebookPhoto *self,
+ const char *value);
+void facebook_photo_set_is_primary (FacebookPhoto *self,
+ const char *value);
+void facebook_photo_set_url_sq (FacebookPhoto *self,
+ const char *value);
+void facebook_photo_set_url_t (FacebookPhoto *self,
+ const char *value);
+void facebook_photo_set_url_s (FacebookPhoto *self,
+ const char *value);
+void facebook_photo_set_url_m (FacebookPhoto *self,
+ const char *value);
+void facebook_photo_set_url_o (FacebookPhoto *self,
+ const char *value);
+void facebook_photo_set_original_format (FacebookPhoto *self,
+ const char *value);
+
+G_END_DECLS
+
+#endif /* FACEBOOK_PHOTO_H */
diff --git a/extensions/facebook/facebook-service.c b/extensions/facebook/facebook-service.c
new file mode 100644
index 0000000..6c5d1fc
--- /dev/null
+++ b/extensions/facebook/facebook-service.c
@@ -0,0 +1,1173 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * GThumb
+ *
+ * Copyright (C) 2010 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.h>
+#include <glib/gi18n.h>
+#include <gthumb.h>
+#include "facebook-account.h"
+#include "facebook-connection.h"
+#include "facebook-photo.h"
+#include "facebook-photoset.h"
+#include "facebook-service.h"
+#include "facebook-user.h"
+
+
+typedef struct {
+ FacebookPrivacyType privacy_level;
+ FacebookSafetyType safety_level;
+ gboolean hidden;
+ GList *file_list;
+ GCancellable *cancellable;
+ GAsyncReadyCallback callback;
+ gpointer user_data;
+ GList *current;
+ goffset total_size;
+ goffset uploaded_size;
+ int n_files;
+ int uploaded_files;
+ GList *ids;
+} PostPhotosData;
+
+
+static void
+post_photos_data_free (PostPhotosData *post_photos)
+{
+ if (post_photos == NULL)
+ return;
+ _g_string_list_free (post_photos->ids);
+ _g_object_unref (post_photos->cancellable);
+ _g_object_list_unref (post_photos->file_list);
+ g_free (post_photos);
+}
+
+
+typedef struct {
+ FacebookPhotoset *photoset;
+ GList *photo_ids;
+ GCancellable *cancellable;
+ GAsyncReadyCallback callback;
+ gpointer user_data;
+ int n_files;
+ GList *current;
+ int n_current;
+} AddPhotosData;
+
+
+static void
+add_photos_data_free (AddPhotosData *add_photos)
+{
+ if (add_photos == NULL)
+ return;
+ _g_object_unref (add_photos->photoset);
+ _g_string_list_free (add_photos->photo_ids);
+ _g_object_unref (add_photos->cancellable);
+ g_free (add_photos);
+}
+
+
+struct _FacebookServicePrivate
+{
+ FacebookConnection *conn;
+ FacebookUser *user;
+ PostPhotosData *post_photos;
+ AddPhotosData *add_photos;
+};
+
+
+static gpointer parent_class = NULL;
+
+
+static void
+facebook_service_finalize (GObject *object)
+{
+ FacebookService *self;
+
+ self = FACEBOOK_SERVICE (object);
+
+ _g_object_unref (self->priv->conn);
+ _g_object_unref (self->priv->user);
+ post_photos_data_free (self->priv->post_photos);
+ add_photos_data_free (self->priv->add_photos);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+
+static void
+facebook_service_class_init (FacebookServiceClass *klass)
+{
+ GObjectClass *object_class;
+
+ parent_class = g_type_class_peek_parent (klass);
+ g_type_class_add_private (klass, sizeof (FacebookServicePrivate));
+
+ object_class = (GObjectClass*) klass;
+ object_class->finalize = facebook_service_finalize;
+}
+
+
+static void
+facebook_service_init (FacebookService *self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, FACEBOOK_TYPE_SERVICE, FacebookServicePrivate);
+ self->priv->conn = NULL;
+ self->priv->user = NULL;
+ self->priv->post_photos = NULL;
+}
+
+
+GType
+facebook_service_get_type (void)
+{
+ static GType type = 0;
+
+ if (! type) {
+ GTypeInfo type_info = {
+ sizeof (FacebookServiceClass),
+ NULL,
+ NULL,
+ (GClassInitFunc) facebook_service_class_init,
+ NULL,
+ NULL,
+ sizeof (FacebookService),
+ 0,
+ (GInstanceInitFunc) facebook_service_init
+ };
+
+ type = g_type_register_static (G_TYPE_OBJECT,
+ "FacebookService",
+ &type_info,
+ 0);
+ }
+
+ return type;
+}
+
+
+FacebookService *
+facebook_service_new (FacebookConnection *conn)
+{
+ FacebookService *self;
+
+ self = (FacebookService *) g_object_new (FACEBOOK_TYPE_SERVICE, NULL);
+ self->priv->conn = g_object_ref (conn);
+
+ return self;
+}
+
+
+/* -- facebook_service_get_upload_status -- */
+
+
+static void
+get_upload_status_ready_cb (SoupSession *session,
+ SoupMessage *msg,
+ gpointer user_data)
+{
+ FacebookService *self = user_data;
+ GSimpleAsyncResult *result;
+ SoupBuffer *body;
+ DomDocument *doc = NULL;
+ GError *error = NULL;
+
+ result = facebook_connection_get_result (self->priv->conn);
+
+ if (msg->status_code != 200) {
+ g_simple_async_result_set_error (result,
+ SOUP_HTTP_ERROR,
+ msg->status_code,
+ "%s",
+ soup_status_get_phrase (msg->status_code));
+ g_simple_async_result_complete_in_idle (result);
+ return;
+ }
+
+ body = soup_message_body_flatten (msg->response_body);
+ if (facebook_utils_parse_response (body, &doc, &error)) {
+ DomElement *response;
+ DomElement *node;
+ FacebookUser *user = NULL;
+
+ response = DOM_ELEMENT (doc)->first_child;
+ for (node = response->first_child; node; node = node->next_sibling) {
+ if (g_strcmp0 (node->tag_name, "user") == 0) {
+ user = facebook_user_new ();
+ dom_domizable_load_from_element (DOM_DOMIZABLE (user), node);
+ g_simple_async_result_set_op_res_gpointer (result, user, (GDestroyNotify) g_object_unref);
+ }
+ }
+
+ if (user == NULL) {
+ error = g_error_new_literal (FACEBOOK_CONNECTION_ERROR, 0, _("Unknown error"));
+ g_simple_async_result_set_from_error (result, error);
+ }
+
+ g_object_unref (doc);
+ }
+ else
+ g_simple_async_result_set_from_error (result, error);
+
+ g_simple_async_result_complete_in_idle (result);
+
+ soup_buffer_free (body);
+}
+
+
+void
+facebook_service_get_upload_status (FacebookService *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GHashTable *data_set;
+ SoupMessage *msg;
+
+ gth_task_progress (GTH_TASK (self->priv->conn), _("Connecting to the server"), _("Getting account information"), TRUE, 0.0);
+
+ data_set = g_hash_table_new (g_str_hash, g_str_equal);
+ g_hash_table_insert (data_set, "method", "facebook.people.getUploadStatus");
+ facebook_connection_add_api_sig (self->priv->conn, data_set);
+ msg = soup_form_request_new_from_hash ("GET", "http://api.facebook.com/services/rest", data_set);
+ facebook_connection_send_message (self->priv->conn,
+ msg,
+ cancellable,
+ callback,
+ user_data,
+ facebook_service_get_upload_status,
+ get_upload_status_ready_cb,
+ self);
+
+ g_hash_table_destroy (data_set);
+}
+
+
+FacebookUser *
+facebook_service_get_upload_status_finish (FacebookService *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
+ return NULL;
+ else
+ return g_object_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result)));
+}
+
+
+/* -- facebook_service_list_photosets -- */
+
+
+static void
+list_photosets_ready_cb (SoupSession *session,
+ SoupMessage *msg,
+ gpointer user_data)
+{
+ FacebookService *self = user_data;
+ GSimpleAsyncResult *result;
+ SoupBuffer *body;
+ DomDocument *doc = NULL;
+ GError *error = NULL;
+
+ result = facebook_connection_get_result (self->priv->conn);
+
+ if (msg->status_code != 200) {
+ g_simple_async_result_set_error (result,
+ SOUP_HTTP_ERROR,
+ msg->status_code,
+ "%s",
+ soup_status_get_phrase (msg->status_code));
+ g_simple_async_result_complete_in_idle (result);
+ return;
+ }
+
+ body = soup_message_body_flatten (msg->response_body);
+ if (facebook_utils_parse_response (body, &doc, &error)) {
+ DomElement *response;
+ DomElement *node;
+ GList *photosets = NULL;
+
+ response = DOM_ELEMENT (doc)->first_child;
+ for (node = response->first_child; node; node = node->next_sibling) {
+ if (g_strcmp0 (node->tag_name, "photosets") == 0) {
+ DomElement *child;
+
+ for (child = node->first_child; child; child = child->next_sibling) {
+ if (g_strcmp0 (child->tag_name, "photoset") == 0) {
+ FacebookPhotoset *photoset;
+
+ photoset = facebook_photoset_new ();
+ dom_domizable_load_from_element (DOM_DOMIZABLE (photoset), child);
+ photosets = g_list_prepend (photosets, photoset);
+ }
+ }
+ }
+ }
+
+ photosets = g_list_reverse (photosets);
+ g_simple_async_result_set_op_res_gpointer (result, photosets, (GDestroyNotify) _g_object_list_unref);
+
+ g_object_unref (doc);
+ }
+ else
+ g_simple_async_result_set_from_error (result, error);
+
+ g_simple_async_result_complete_in_idle (result);
+
+ soup_buffer_free (body);
+}
+
+
+void
+facebook_service_list_photosets (FacebookService *self,
+ const char *user_id,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GHashTable *data_set;
+ SoupMessage *msg;
+
+ gth_task_progress (GTH_TASK (self->priv->conn), _("Getting the album list"), NULL, TRUE, 0.0);
+
+ data_set = g_hash_table_new (g_str_hash, g_str_equal);
+ g_hash_table_insert (data_set, "method", "facebook.photosets.getList");
+ if (user_id != NULL)
+ g_hash_table_insert (data_set, "user_id", (char *) user_id);
+ facebook_connection_add_api_sig (self->priv->conn, data_set);
+ msg = soup_form_request_new_from_hash ("GET", "http://api.facebook.com/services/rest", data_set);
+ facebook_connection_send_message (self->priv->conn,
+ msg,
+ cancellable,
+ callback,
+ user_data,
+ facebook_service_list_photosets,
+ list_photosets_ready_cb,
+ self);
+
+ g_hash_table_destroy (data_set);
+}
+
+
+GList *
+facebook_service_list_photosets_finish (FacebookService *service,
+ GAsyncResult *result,
+ GError **error)
+{
+ if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
+ return NULL;
+ else
+ return _g_object_list_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result)));
+}
+
+
+/* -- facebook_service_create_photoset_finish -- */
+
+
+static void
+create_photoset_ready_cb (SoupSession *session,
+ SoupMessage *msg,
+ gpointer user_data)
+{
+ FacebookService *self = user_data;
+ GSimpleAsyncResult *result;
+ SoupBuffer *body;
+ DomDocument *doc = NULL;
+ GError *error = NULL;
+
+ result = facebook_connection_get_result (self->priv->conn);
+
+ if (msg->status_code != 200) {
+ g_simple_async_result_set_error (result,
+ SOUP_HTTP_ERROR,
+ msg->status_code,
+ "%s",
+ soup_status_get_phrase (msg->status_code));
+ g_simple_async_result_complete_in_idle (result);
+ return;
+ }
+
+ body = soup_message_body_flatten (msg->response_body);
+ if (facebook_utils_parse_response (body, &doc, &error)) {
+ DomElement *response;
+ DomElement *node;
+ FacebookPhotoset *photoset = NULL;
+
+ response = DOM_ELEMENT (doc)->first_child;
+ for (node = response->first_child; node; node = node->next_sibling) {
+ if (g_strcmp0 (node->tag_name, "photoset") == 0) {
+ photoset = facebook_photoset_new ();
+ dom_domizable_load_from_element (DOM_DOMIZABLE (photoset), node);
+ g_simple_async_result_set_op_res_gpointer (result, photoset, (GDestroyNotify) g_object_unref);
+ }
+ }
+
+ if (photoset == NULL) {
+ error = g_error_new_literal (FACEBOOK_CONNECTION_ERROR, 0, _("Unknown error"));
+ g_simple_async_result_set_from_error (result, error);
+ }
+
+ g_object_unref (doc);
+ }
+ else
+ g_simple_async_result_set_from_error (result, error);
+
+ g_simple_async_result_complete_in_idle (result);
+
+ soup_buffer_free (body);
+}
+
+
+void
+facebook_service_create_photoset (FacebookService *self,
+ FacebookPhotoset *photoset,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GHashTable *data_set;
+ SoupMessage *msg;
+
+ g_return_if_fail (photoset != NULL);
+ g_return_if_fail (photoset->primary != NULL);
+
+ gth_task_progress (GTH_TASK (self->priv->conn), _("Creating the new album"), NULL, TRUE, 0.0);
+
+ data_set = g_hash_table_new (g_str_hash, g_str_equal);
+ g_hash_table_insert (data_set, "method", "facebook.photosets.create");
+ g_hash_table_insert (data_set, "title", photoset->title);
+ g_hash_table_insert (data_set, "primary_photo_id", photoset->primary);
+ facebook_connection_add_api_sig (self->priv->conn, data_set);
+ msg = soup_form_request_new_from_hash ("GET", "http://api.facebook.com/services/rest", data_set);
+ facebook_connection_send_message (self->priv->conn,
+ msg,
+ cancellable,
+ callback,
+ user_data,
+ facebook_service_create_photoset,
+ create_photoset_ready_cb,
+ self);
+
+ g_hash_table_destroy (data_set);
+}
+
+
+FacebookPhotoset *
+facebook_service_create_photoset_finish (FacebookService *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
+ return NULL;
+ else
+ return g_object_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result)));
+}
+
+
+/* -- facebook_service_add_photos_to_set -- */
+
+
+static void
+add_photos_to_set_done (FacebookService *self,
+ GError *error)
+{
+ GSimpleAsyncResult *result;
+
+ result = facebook_connection_get_result (self->priv->conn);
+ if (result == NULL)
+ result = g_simple_async_result_new (G_OBJECT (self),
+ self->priv->add_photos->callback,
+ self->priv->add_photos->user_data,
+ facebook_service_add_photos_to_set);
+
+ if (error == NULL)
+ g_simple_async_result_set_op_res_gboolean (result, TRUE);
+ else
+ g_simple_async_result_set_from_error (result, error);
+
+ g_simple_async_result_complete_in_idle (result);
+}
+
+
+static void add_current_photo_to_set (FacebookService *self);
+
+
+static void
+add_next_photo_to_set (FacebookService *self)
+{
+ self->priv->add_photos->current = self->priv->add_photos->current->next;
+ self->priv->add_photos->n_current += 1;
+ add_current_photo_to_set (self);
+}
+
+
+static void
+add_current_photo_to_set_ready_cb (SoupSession *session,
+ SoupMessage *msg,
+ gpointer user_data)
+{
+ FacebookService *self = user_data;
+ GSimpleAsyncResult *result;
+ SoupBuffer *body;
+ DomDocument *doc = NULL;
+ GError *error = NULL;
+
+ result = facebook_connection_get_result (self->priv->conn);
+
+ if (msg->status_code != 200) {
+ g_simple_async_result_set_error (result,
+ SOUP_HTTP_ERROR,
+ msg->status_code,
+ "%s",
+ soup_status_get_phrase (msg->status_code));
+ g_simple_async_result_complete_in_idle (result);
+ return;
+ }
+
+ body = soup_message_body_flatten (msg->response_body);
+ if (! facebook_utils_parse_response (body, &doc, &error)) {
+ soup_buffer_free (body);
+ add_photos_to_set_done (self, error);
+ return;
+ }
+
+ g_object_unref (doc);
+ soup_buffer_free (body);
+
+ add_next_photo_to_set (self);
+}
+
+
+static void
+add_current_photo_to_set (FacebookService *self)
+{
+ char *photo_id;
+ GHashTable *data_set;
+ SoupMessage *msg;
+
+ if (self->priv->add_photos->current == NULL) {
+ add_photos_to_set_done (self, NULL);
+ return;
+ }
+
+ gth_task_progress (GTH_TASK (self->priv->conn),
+ _("Creating the new album"),
+ "",
+ FALSE,
+ (double) self->priv->add_photos->n_current / (self->priv->add_photos->n_files + 1));
+
+ photo_id = self->priv->add_photos->current->data;
+ if (g_strcmp0 (photo_id, self->priv->add_photos->photoset->primary) == 0) {
+ add_next_photo_to_set (self);
+ return;
+ }
+
+ data_set = g_hash_table_new (g_str_hash, g_str_equal);
+ g_hash_table_insert (data_set, "method", "facebook.photosets.addPhoto");
+ g_hash_table_insert (data_set, "photoset_id", self->priv->add_photos->photoset->id);
+ g_hash_table_insert (data_set, "photo_id", photo_id);
+ facebook_connection_add_api_sig (self->priv->conn, data_set);
+ msg = soup_form_request_new_from_hash ("POST", "http://api.facebook.com/services/rest", data_set);
+ facebook_connection_send_message (self->priv->conn,
+ msg,
+ self->priv->add_photos->cancellable,
+ self->priv->add_photos->callback,
+ self->priv->add_photos->user_data,
+ facebook_service_add_photos_to_set,
+ add_current_photo_to_set_ready_cb,
+ self);
+
+ g_hash_table_destroy (data_set);
+}
+
+
+void
+facebook_service_add_photos_to_set (FacebookService *self,
+ FacebookPhotoset *photoset,
+ GList *photo_ids,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ gth_task_progress (GTH_TASK (self->priv->conn), _("Creating the new album"), NULL, TRUE, 0.0);
+
+ add_photos_data_free (self->priv->add_photos);
+ self->priv->add_photos = g_new0 (AddPhotosData, 1);
+ self->priv->add_photos->photoset = _g_object_ref (photoset);
+ self->priv->add_photos->photo_ids = _g_string_list_dup (photo_ids);
+ self->priv->add_photos->cancellable = _g_object_ref (cancellable);
+ self->priv->add_photos->callback = callback;
+ self->priv->add_photos->user_data = user_data;
+ self->priv->add_photos->n_files = g_list_length (self->priv->add_photos->photo_ids);
+ self->priv->add_photos->current = self->priv->add_photos->photo_ids;
+ self->priv->add_photos->n_current = 1;
+
+ facebook_connection_reset_result (self->priv->conn);
+ add_current_photo_to_set (self);
+}
+
+
+gboolean
+facebook_service_add_photos_to_set_finish (FacebookService *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
+ return FALSE;
+ else
+ return TRUE;
+}
+
+
+/* -- facebook_service_post_photos -- */
+
+
+static void
+post_photos_done (FacebookService *self,
+ GError *error)
+{
+ GSimpleAsyncResult *result;
+
+ result = facebook_connection_get_result (self->priv->conn);
+ if (error == NULL) {
+ self->priv->post_photos->ids = g_list_reverse (self->priv->post_photos->ids);
+ g_simple_async_result_set_op_res_gpointer (result, self->priv->post_photos->ids, (GDestroyNotify) _g_string_list_free);
+ self->priv->post_photos->ids = NULL;
+ }
+ else
+ g_simple_async_result_set_from_error (result, error);
+
+ g_simple_async_result_complete_in_idle (result);
+}
+
+
+static void facebook_service_post_current_file (FacebookService *self);
+
+
+static void
+post_photo_ready_cb (SoupSession *session,
+ SoupMessage *msg,
+ gpointer user_data)
+{
+ FacebookService *self = user_data;
+ SoupBuffer *body;
+ DomDocument *doc = NULL;
+ GError *error = NULL;
+ GthFileData *file_data;
+
+ if (msg->status_code != 200) {
+ GError *error;
+
+ error = g_error_new (SOUP_HTTP_ERROR, msg->status_code, "%s", soup_status_get_phrase (msg->status_code));
+ post_photos_done (self, error);
+ g_error_free (error);
+
+ return;
+ }
+
+ body = soup_message_body_flatten (msg->response_body);
+ if (facebook_utils_parse_response (body, &doc, &error)) {
+ DomElement *response;
+ DomElement *node;
+
+ /* save the file id */
+
+ response = DOM_ELEMENT (doc)->first_child;
+ for (node = response->first_child; node; node = node->next_sibling) {
+ if (g_strcmp0 (node->tag_name, "photoid") == 0) {
+ const char *id;
+
+ id = dom_element_get_inner_text (node);
+ self->priv->post_photos->ids = g_list_prepend (self->priv->post_photos->ids, g_strdup (id));
+ }
+ }
+
+ g_object_unref (doc);
+ }
+ else {
+ soup_buffer_free (body);
+ post_photos_done (self, error);
+ return;
+ }
+
+ soup_buffer_free (body);
+
+ file_data = self->priv->post_photos->current->data;
+ self->priv->post_photos->uploaded_size += g_file_info_get_size (file_data->info);
+ self->priv->post_photos->current = self->priv->post_photos->current->next;
+ facebook_service_post_current_file (self);
+}
+
+
+static char *
+get_safety_value (FacebookSafetyType safety_level)
+{
+ char *value = NULL;
+
+ switch (safety_level) {
+ case FACEBOOK_SAFETY_SAFE:
+ value = "1";
+ break;
+
+ case FACEBOOK_SAFETY_MODERATE:
+ value = "2";
+ break;
+
+ case FACEBOOK_SAFETY_RESTRICTED:
+ value = "3";
+ break;
+ }
+
+ return value;
+}
+
+
+static void
+post_photo_file_buffer_ready_cb (void **buffer,
+ gsize count,
+ GError *error,
+ gpointer user_data)
+{
+ FacebookService *self = user_data;
+ GthFileData *file_data;
+ SoupMultipart *multipart;
+ char *uri;
+ SoupBuffer *body;
+ SoupMessage *msg;
+
+ if (error != NULL) {
+ post_photos_done (self, error);
+ return;
+ }
+
+ file_data = self->priv->post_photos->current->data;
+ multipart = soup_multipart_new ("multipart/form-data");
+
+ /* the metadata part */
+
+ {
+ GHashTable *data_set;
+ char *title;
+ char *description;
+ char *tags;
+ GObject *metadata;
+ GList *keys;
+ GList *scan;
+
+ data_set = g_hash_table_new (g_str_hash, g_str_equal);
+
+ title = gth_file_data_get_attribute_as_string (file_data, "general::title");
+ if (title != NULL)
+ g_hash_table_insert (data_set, "title", title);
+
+ description = gth_file_data_get_attribute_as_string (file_data, "general::description");
+ if (description != NULL)
+ g_hash_table_insert (data_set, "description", description);
+
+ tags = NULL;
+ metadata = g_file_info_get_attribute_object (file_data->info, "general::tags");
+ if ((metadata != NULL) && GTH_IS_STRING_LIST (metadata))
+ tags = gth_string_list_join (GTH_STRING_LIST (metadata), " ");
+ if (tags != NULL)
+ g_hash_table_insert (data_set, "tags", tags);
+
+ g_hash_table_insert (data_set, "is_public", (self->priv->post_photos->privacy_level == FACEBOOK_PRIVACY_PUBLIC) ? "1" : "0");
+ g_hash_table_insert (data_set, "is_friend", ((self->priv->post_photos->privacy_level == FACEBOOK_PRIVACY_FRIENDS) || (self->priv->post_photos->privacy_level == FACEBOOK_PRIVACY_FRIENDS_FAMILY)) ? "1" : "0");
+ g_hash_table_insert (data_set, "is_family", ((self->priv->post_photos->privacy_level == FACEBOOK_PRIVACY_FAMILY) || (self->priv->post_photos->privacy_level == FACEBOOK_PRIVACY_FRIENDS_FAMILY)) ? "1" : "0");
+ g_hash_table_insert (data_set, "safety_level", get_safety_value (self->priv->post_photos->safety_level));
+ g_hash_table_insert (data_set, "hidden", self->priv->post_photos->hidden ? "2" : "1");
+ facebook_connection_add_api_sig (self->priv->conn, data_set);
+
+ keys = g_hash_table_get_keys (data_set);
+ for (scan = keys; scan; scan = scan->next) {
+ char *key = scan->data;
+ soup_multipart_append_form_string (multipart, key, g_hash_table_lookup (data_set, key));
+ }
+
+ g_free (tags);
+ g_list_free (keys);
+ g_hash_table_unref (data_set);
+ }
+
+ /* the file part */
+
+ uri = g_file_get_uri (file_data->file);
+ body = soup_buffer_new (SOUP_MEMORY_TEMPORARY, *buffer, count);
+ soup_multipart_append_form_file (multipart,
+ "photo",
+ uri,
+ gth_file_data_get_mime_type (file_data),
+ body);
+
+ soup_buffer_free (body);
+ g_free (uri);
+
+ /* send the file */
+
+ {
+ char *details;
+
+ /* Translators: %s is a filename */
+ details = g_strdup_printf (_("Uploading '%s'"), g_file_info_get_display_name (file_data->info));
+ gth_task_progress (GTH_TASK (self->priv->conn),
+ NULL, details,
+ FALSE,
+ (double) (self->priv->post_photos->uploaded_size + (g_file_info_get_size (file_data->info) / 2.0)) / self->priv->post_photos->total_size);
+
+ g_free (details);
+ }
+
+ msg = soup_form_request_new_from_multipart ("http://api.facebook.com/services/upload/", multipart);
+ facebook_connection_send_message (self->priv->conn,
+ msg,
+ self->priv->post_photos->cancellable,
+ self->priv->post_photos->callback,
+ self->priv->post_photos->user_data,
+ facebook_service_post_photos,
+ post_photo_ready_cb,
+ self);
+
+ soup_multipart_free (multipart);
+}
+
+
+static void
+facebook_service_post_current_file (FacebookService *self)
+{
+ GthFileData *file_data;
+
+ if (self->priv->post_photos->current == NULL) {
+ post_photos_done (self, NULL);
+ return;
+ }
+
+ file_data = self->priv->post_photos->current->data;
+ g_load_file_async (file_data->file,
+ G_PRIORITY_DEFAULT,
+ self->priv->post_photos->cancellable,
+ post_photo_file_buffer_ready_cb,
+ self);
+}
+
+
+static void
+post_photos_info_ready_cb (GList *files,
+ GError *error,
+ gpointer user_data)
+{
+ FacebookService *self = user_data;
+ GList *scan;
+
+ if (error != NULL) {
+ post_photos_done (self, error);
+ return;
+ }
+
+ self->priv->post_photos->file_list = _g_object_list_ref (files);
+ for (scan = self->priv->post_photos->file_list; scan; scan = scan->next) {
+ GthFileData *file_data = scan->data;
+
+ self->priv->post_photos->total_size += g_file_info_get_size (file_data->info);
+ self->priv->post_photos->n_files += 1;
+ }
+
+ self->priv->post_photos->current = self->priv->post_photos->file_list;
+ facebook_service_post_current_file (self);
+}
+
+
+void
+facebook_service_post_photos (FacebookService *self,
+ FacebookPrivacyType privacy_level,
+ FacebookSafetyType safety_level,
+ gboolean hidden,
+ GList *file_list, /* GFile list */
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ gth_task_progress (GTH_TASK (self->priv->conn), _("Uploading the files to the server"), NULL, TRUE, 0.0);
+
+ post_photos_data_free (self->priv->post_photos);
+ self->priv->post_photos = g_new0 (PostPhotosData, 1);
+ self->priv->post_photos->privacy_level = privacy_level;
+ self->priv->post_photos->safety_level = safety_level;
+ self->priv->post_photos->hidden = hidden;
+ self->priv->post_photos->cancellable = _g_object_ref (cancellable);
+ self->priv->post_photos->callback = callback;
+ self->priv->post_photos->user_data = user_data;
+ self->priv->post_photos->total_size = 0;
+ self->priv->post_photos->n_files = 0;
+
+ _g_query_all_metadata_async (file_list,
+ FALSE,
+ TRUE,
+ "*",
+ self->priv->post_photos->cancellable,
+ post_photos_info_ready_cb,
+ self);
+}
+
+
+GList *
+facebook_service_post_photos_finish (FacebookService *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
+ return NULL;
+ else
+ return _g_string_list_dup (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result)));
+}
+
+
+/* -- facebook_service_list_photos -- */
+
+
+static void
+list_photos_ready_cb (SoupSession *session,
+ SoupMessage *msg,
+ gpointer user_data)
+{
+ FacebookService *self = user_data;
+ GSimpleAsyncResult *result;
+ SoupBuffer *body;
+ DomDocument *doc = NULL;
+ GError *error = NULL;
+
+ result = facebook_connection_get_result (self->priv->conn);
+
+ if (msg->status_code != 200) {
+ g_simple_async_result_set_error (result,
+ SOUP_HTTP_ERROR,
+ msg->status_code,
+ "%s",
+ soup_status_get_phrase (msg->status_code));
+ g_simple_async_result_complete_in_idle (result);
+ return;
+ }
+
+ body = soup_message_body_flatten (msg->response_body);
+ if (facebook_utils_parse_response (body, &doc, &error)) {
+ DomElement *response;
+ DomElement *node;
+ GList *photos = NULL;
+
+ response = DOM_ELEMENT (doc)->first_child;
+ for (node = response->first_child; node; node = node->next_sibling) {
+ if (g_strcmp0 (node->tag_name, "photoset") == 0) {
+ DomElement *child;
+ int position;
+
+ position = 0;
+ for (child = node->first_child; child; child = child->next_sibling) {
+ if (g_strcmp0 (child->tag_name, "photo") == 0) {
+ FacebookPhoto *photo;
+
+ photo = facebook_photo_new ();
+ dom_domizable_load_from_element (DOM_DOMIZABLE (photo), child);
+ photo->position = position++;
+ photos = g_list_prepend (photos, photo);
+ }
+ }
+ }
+ }
+
+ photos = g_list_reverse (photos);
+ g_simple_async_result_set_op_res_gpointer (result, photos, (GDestroyNotify) _g_object_list_unref);
+
+ g_object_unref (doc);
+ }
+ else
+ g_simple_async_result_set_from_error (result, error);
+
+ g_simple_async_result_complete_in_idle (result);
+
+ soup_buffer_free (body);
+}
+
+
+void
+facebook_service_list_photos (FacebookService *self,
+ FacebookPhotoset *photoset,
+ const char *extras,
+ int per_page,
+ int page,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GHashTable *data_set;
+ char *s;
+ SoupMessage *msg;
+
+ g_return_if_fail (photoset != NULL);
+
+ gth_task_progress (GTH_TASK (self->priv->conn), _("Getting the photo list"), NULL, TRUE, 0.0);
+
+ data_set = g_hash_table_new (g_str_hash, g_str_equal);
+ g_hash_table_insert (data_set, "method", "facebook.photosets.getPhotos");
+ g_hash_table_insert (data_set, "photoset_id", photoset->id);
+ if (extras != NULL)
+ g_hash_table_insert (data_set, "extras", (char *) extras);
+ if (per_page > 0) {
+ s = g_strdup_printf ("%d", per_page);
+ g_hash_table_insert (data_set, "per_page", s);
+ g_free (s);
+ }
+ if (page > 0) {
+ s = g_strdup_printf ("%d", page);
+ g_hash_table_insert (data_set, "page", s);
+ g_free (s);
+ }
+ facebook_connection_add_api_sig (self->priv->conn, data_set);
+ msg = soup_form_request_new_from_hash ("GET", "http://api.facebook.com/services/rest", data_set);
+ facebook_connection_send_message (self->priv->conn,
+ msg,
+ cancellable,
+ callback,
+ user_data,
+ facebook_service_list_photos,
+ list_photos_ready_cb,
+ self);
+
+ g_hash_table_destroy (data_set);
+}
+
+
+GList *
+facebook_service_list_photos_finish (FacebookService *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
+ return NULL;
+ else
+ return _g_object_list_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result)));
+}
+
+
+/* utilities */
+
+
+GList *
+facebook_accounts_load_from_file (void)
+{
+ GList *accounts = NULL;
+ char *filename;
+ char *buffer;
+ gsize len;
+ DomDocument *doc;
+
+ filename = gth_user_dir_get_file (GTH_DIR_CONFIG, GTHUMB_DIR, "accounts", "facebook.xml", NULL);
+ if (! g_file_get_contents (filename, &buffer, &len, NULL)) {
+ g_free (filename);
+ return NULL;
+ }
+
+ doc = dom_document_new ();
+ if (dom_document_load (doc, buffer, len, NULL)) {
+ DomElement *node;
+
+ node = DOM_ELEMENT (doc)->first_child;
+ if ((node != NULL) && (g_strcmp0 (node->tag_name, "accounts") == 0)) {
+ DomElement *child;
+
+ for (child = node->first_child;
+ child != NULL;
+ child = child->next_sibling)
+ {
+ if (strcmp (child->tag_name, "account") == 0) {
+ FacebookAccount *account;
+
+ account = facebook_account_new ();
+ dom_domizable_load_from_element (DOM_DOMIZABLE (account), child);
+
+ accounts = g_list_prepend (accounts, account);
+ }
+ }
+
+ accounts = g_list_reverse (accounts);
+ }
+ }
+
+ g_object_unref (doc);
+ g_free (buffer);
+ g_free (filename);
+
+ return accounts;
+}
+
+
+FacebookAccount *
+facebook_accounts_find_default (GList *accounts)
+{
+ GList *scan;
+
+ for (scan = accounts; scan; scan = scan->next) {
+ FacebookAccount *account = scan->data;
+
+ if (account->is_default)
+ return g_object_ref (account);
+ }
+
+ return NULL;
+}
+
+
+void
+facebook_accounts_save_to_file (GList *accounts,
+ FacebookAccount *default_account)
+{
+ DomDocument *doc;
+ DomElement *root;
+ GList *scan;
+ char *buffer;
+ gsize len;
+ char *filename;
+ GFile *file;
+
+ doc = dom_document_new ();
+ root = dom_document_create_element (doc, "accounts", NULL);
+ dom_element_append_child (DOM_ELEMENT (doc), root);
+ for (scan = accounts; scan; scan = scan->next) {
+ FacebookAccount *account = scan->data;
+ DomElement *node;
+
+ if ((default_account != NULL) && g_strcmp0 (account->username, default_account->username) == 0)
+ account->is_default = TRUE;
+ else
+ account->is_default = FALSE;
+ node = dom_domizable_create_element (DOM_DOMIZABLE (account), doc);
+ dom_element_append_child (root, node);
+ }
+
+ gth_user_dir_make_dir_for_file (GTH_DIR_CONFIG, GTHUMB_DIR, "accounts", "facebook.xml", NULL);
+ filename = gth_user_dir_get_file (GTH_DIR_CONFIG, GTHUMB_DIR, "accounts", "facebook.xml", NULL);
+ file = g_file_new_for_path (filename);
+ buffer = dom_document_dump (doc, &len);
+ g_write_file (file, FALSE, G_FILE_CREATE_PRIVATE | G_FILE_CREATE_REPLACE_DESTINATION, buffer, len, NULL, NULL);
+
+ g_free (buffer);
+ g_object_unref (file);
+ g_free (filename);
+ g_object_unref (doc);
+}
diff --git a/extensions/facebook/facebook-service.h b/extensions/facebook/facebook-service.h
new file mode 100644
index 0000000..88747b3
--- /dev/null
+++ b/extensions/facebook/facebook-service.h
@@ -0,0 +1,132 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * GThumb
+ *
+ * Copyright (C) 2010 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 FACEBOOK_SERVICE_H
+#define FACEBOOK_SERVICE_H
+
+#include <glib-object.h>
+#include "facebook-account.h"
+#include "facebook-connection.h"
+#include "facebook-photoset.h"
+#include "facebook-user.h"
+
+typedef enum {
+ FACEBOOK_PRIVACY_PUBLIC,
+ FACEBOOK_PRIVACY_FRIENDS_FAMILY,
+ FACEBOOK_PRIVACY_FRIENDS,
+ FACEBOOK_PRIVACY_FAMILY,
+ FACEBOOK_PRIVACY_PRIVATE
+} FacebookPrivacyType;
+
+typedef enum {
+ FACEBOOK_SAFETY_SAFE,
+ FACEBOOK_SAFETY_MODERATE,
+ FACEBOOK_SAFETY_RESTRICTED
+} FacebookSafetyType;
+
+#define FACEBOOK_TYPE_SERVICE (facebook_service_get_type ())
+#define FACEBOOK_SERVICE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), FACEBOOK_TYPE_SERVICE, FacebookService))
+#define FACEBOOK_SERVICE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), FACEBOOK_TYPE_SERVICE, FacebookServiceClass))
+#define FACEBOOK_IS_SERVICE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), FACEBOOK_TYPE_SERVICE))
+#define FACEBOOK_IS_SERVICE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), FACEBOOK_TYPE_SERVICE))
+#define FACEBOOK_SERVICE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), FACEBOOK_TYPE_SERVICE, FacebookServiceClass))
+
+typedef struct _FacebookService FacebookService;
+typedef struct _FacebookServicePrivate FacebookServicePrivate;
+typedef struct _FacebookServiceClass FacebookServiceClass;
+
+struct _FacebookService
+{
+ GObject __parent;
+ FacebookServicePrivate *priv;
+};
+
+struct _FacebookServiceClass
+{
+ GObjectClass __parent_class;
+};
+
+GType facebook_service_get_type (void) G_GNUC_CONST;
+FacebookService * facebook_service_new (FacebookConnection *conn);
+void facebook_service_get_upload_status (FacebookService *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+FacebookUser * facebook_service_get_upload_status_finish (FacebookService *self,
+ GAsyncResult *result,
+ GError **error);
+void facebook_service_list_photosets (FacebookService *self,
+ const char *user_id,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+GList * facebook_service_list_photosets_finish (FacebookService *self,
+ GAsyncResult *result,
+ GError **error);
+void facebook_service_create_photoset (FacebookService *self,
+ FacebookPhotoset *photoset,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+FacebookPhotoset * facebook_service_create_photoset_finish (FacebookService *self,
+ GAsyncResult *result,
+ GError **error);
+void facebook_service_add_photos_to_set (FacebookService *self,
+ FacebookPhotoset *photoset,
+ GList *photo_ids,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean facebook_service_add_photos_to_set_finish (FacebookService *self,
+ GAsyncResult *result,
+ GError **error);
+void facebook_service_post_photos (FacebookService *self,
+ FacebookPrivacyType privacy_level,
+ FacebookSafetyType safety_level,
+ gboolean hidden,
+ GList *file_list, /* GFile list */
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+GList * facebook_service_post_photos_finish (FacebookService *self,
+ GAsyncResult *result,
+ GError **error);
+void facebook_service_list_photos (FacebookService *self,
+ FacebookPhotoset *photoset,
+ const char *extras,
+ int per_page,
+ int page,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+GList * facebook_service_list_photos_finish (FacebookService *self,
+ GAsyncResult *result,
+ GError **error);
+
+/* utilities */
+
+GList * facebook_accounts_load_from_file (void);
+FacebookAccount * facebook_accounts_find_default (GList *accounts);
+void facebook_accounts_save_to_file (GList *accounts,
+ FacebookAccount *default_account);
+
+#endif /* FACEBOOK_SERVICE_H */
diff --git a/extensions/facebook/facebook-types.h b/extensions/facebook/facebook-types.h
new file mode 100644
index 0000000..949bdd5
--- /dev/null
+++ b/extensions/facebook/facebook-types.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * GThumb
+ *
+ * Copyright (C) 2010 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 FACEBOOK_TYPES_H
+#define FACEBOOK_TYPES_H
+
+typedef enum {
+ FACEBOOK_SAFETY_LEVEL_SAFE = 1,
+ FACEBOOK_SAFETY_LEVEL_MODERATE = 2,
+ FACEBOOK_SAFETY_LEVEL_RESTRICTED = 3
+} FacebookSafetyLevel;
+
+typedef enum {
+ FACEBOOK_CONTENT_TYPE_PHOTO = 1,
+ FACEBOOK_CONTENT_TYPE_SCREENSHOT = 2,
+ FACEBOOK_CONTENT_TYPE_OTHER = 3
+} FacebookContentType;
+
+typedef enum {
+ FACEBOOK_HIDDEN_PUBLIC = 1,
+ FACEBOOK_HIDDEN_HIDDEN = 2,
+} FacebookHiddenType;
+
+typedef enum {
+ FACEBOOK_SIZE_SMALL_SQUARE = 75,
+ FACEBOOK_SIZE_THUMBNAIL = 100,
+ FACEBOOK_SIZE_SMALL = 240,
+ FACEBOOK_SIZE_MEDIUM = 500,
+ FACEBOOK_SIZE_LARGE = 1024
+} FacebookSize;
+
+#endif /* FACEBOOK_TYPES_H */
diff --git a/extensions/facebook/facebook-user.c b/extensions/facebook/facebook-user.c
new file mode 100644
index 0000000..e5697fd
--- /dev/null
+++ b/extensions/facebook/facebook-user.c
@@ -0,0 +1,247 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * GThumb
+ *
+ * Copyright (C) 2010 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 <stdlib.h>
+#include <string.h>
+#include <gthumb.h>
+#include "facebook-user.h"
+
+
+static gpointer facebook_user_parent_class = NULL;
+
+
+static void
+facebook_user_finalize (GObject *obj)
+{
+ FacebookUser *self;
+
+ self = FACEBOOK_USER (obj);
+
+ g_free (self->id);
+ g_free (self->username);
+
+ G_OBJECT_CLASS (facebook_user_parent_class)->finalize (obj);
+}
+
+
+static void
+facebook_user_class_init (FacebookUserClass *klass)
+{
+ facebook_user_parent_class = g_type_class_peek_parent (klass);
+ G_OBJECT_CLASS (klass)->finalize = facebook_user_finalize;
+}
+
+
+static DomElement*
+facebook_user_create_element (DomDomizable *base,
+ DomDocument *doc)
+{
+ FacebookUser *self;
+ DomElement *element;
+
+ self = FACEBOOK_USER (base);
+
+ element = dom_document_create_element (doc, "user", NULL);
+ if (self->id != NULL)
+ dom_element_set_attribute (element, "id", self->id);
+
+ return element;
+}
+
+
+static void
+facebook_user_load_from_element (DomDomizable *base,
+ DomElement *element)
+{
+ FacebookUser *self;
+ DomElement *node;
+
+ self = FACEBOOK_USER (base);
+
+ facebook_user_set_id (self, dom_element_get_attribute (element, "id"));
+ facebook_user_set_is_pro (self, dom_element_get_attribute (element, "ispro"));
+
+ for (node = element->first_child; node; node = node->next_sibling) {
+ if (g_strcmp0 (node->tag_name, "username") == 0) {
+ facebook_user_set_username (self, dom_element_get_inner_text (node));
+ }
+ else if (g_strcmp0 (node->tag_name, "bandwidth") == 0) {
+ facebook_user_set_max_bandwidth (self, dom_element_get_attribute (node, "maxbytes"));
+ facebook_user_set_used_bandwidth (self, dom_element_get_attribute (node, "usedbytes"));
+ }
+ else if (g_strcmp0 (node->tag_name, "filesize") == 0) {
+ facebook_user_set_max_filesize (self, dom_element_get_attribute (node, "maxbytes"));
+ }
+ else if (g_strcmp0 (node->tag_name, "videosize") == 0) {
+ facebook_user_set_max_videosize (self, dom_element_get_attribute (node, "maxbytes"));
+ }
+ else if (g_strcmp0 (node->tag_name, "sets") == 0) {
+ facebook_user_set_n_sets (self, dom_element_get_attribute (node, "created"));
+ }
+ else if (g_strcmp0 (node->tag_name, "videos") == 0) {
+ facebook_user_set_n_videos (self, dom_element_get_attribute (node, "uploaded"));
+ }
+ }
+}
+
+
+static void
+facebook_user_dom_domizable_interface_init (DomDomizableIface *iface)
+{
+ iface->create_element = facebook_user_create_element;
+ iface->load_from_element = facebook_user_load_from_element;
+}
+
+
+static void
+facebook_user_instance_init (FacebookUser *self)
+{
+ self->id = NULL;
+ self->username = NULL;
+}
+
+
+GType
+facebook_user_get_type (void)
+{
+ static GType facebook_user_type_id = 0;
+
+ if (facebook_user_type_id == 0) {
+ static const GTypeInfo g_define_type_info = {
+ sizeof (FacebookUserClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) facebook_user_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL,
+ sizeof (FacebookUser),
+ 0,
+ (GInstanceInitFunc) facebook_user_instance_init,
+ NULL
+ };
+ static const GInterfaceInfo dom_domizable_info = {
+ (GInterfaceInitFunc) facebook_user_dom_domizable_interface_init,
+ (GInterfaceFinalizeFunc) NULL,
+ NULL
+ };
+
+ facebook_user_type_id = g_type_register_static (G_TYPE_OBJECT,
+ "FacebookUser",
+ &g_define_type_info,
+ 0);
+ g_type_add_interface_static (facebook_user_type_id, DOM_TYPE_DOMIZABLE, &dom_domizable_info);
+ }
+
+ return facebook_user_type_id;
+}
+
+
+FacebookUser *
+facebook_user_new (void)
+{
+ return g_object_new (FACEBOOK_TYPE_USER, NULL);
+}
+
+
+void
+facebook_user_set_id (FacebookUser *self,
+ const char *value)
+{
+ g_free (self->id);
+ self->id = NULL;
+ if (value != NULL)
+ self->id = g_strdup (value);
+}
+
+
+void
+facebook_user_set_is_pro (FacebookUser *self,
+ const char *value)
+{
+ self->is_pro = (g_strcmp0 (value, "1") == 0);
+}
+
+
+void
+facebook_user_set_username(FacebookUser *self,
+ const char *value)
+{
+ g_free (self->username);
+ self->username = NULL;
+ if (value != NULL)
+ self->username = g_strdup (value);
+}
+
+
+void
+facebook_user_set_max_bandwidth (FacebookUser *self,
+ const char *value)
+{
+ self->max_bandwidth = g_ascii_strtoull (value, NULL, 10);
+}
+
+
+void
+facebook_user_set_used_bandwidth (FacebookUser *self,
+ const char *value)
+{
+ self->used_bandwidth = g_ascii_strtoull (value, NULL, 10);
+}
+
+
+void
+facebook_user_set_max_filesize (FacebookUser *self,
+ const char *value)
+{
+ self->max_filesize = g_ascii_strtoull (value, NULL, 10);
+}
+
+
+void
+facebook_user_set_max_videosize (FacebookUser *self,
+ const char *value)
+{
+ self->max_videosize = g_ascii_strtoull (value, NULL, 10);
+}
+
+
+void
+facebook_user_set_n_sets (FacebookUser *self,
+ const char *value)
+{
+ if (value != NULL)
+ self->n_sets = atoi (value);
+ else
+ self->n_sets = 0;
+}
+
+
+void
+facebook_user_set_n_videos (FacebookUser *self,
+ const char *value)
+{
+ if (value != NULL)
+ self->n_videos = atoi (value);
+ else
+ self->n_videos = 0;
+}
diff --git a/extensions/facebook/facebook-user.h b/extensions/facebook/facebook-user.h
new file mode 100644
index 0000000..f4c6b8a
--- /dev/null
+++ b/extensions/facebook/facebook-user.h
@@ -0,0 +1,82 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * GThumb
+ *
+ * Copyright (C) 2010 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 FACEBOOK_USER_H
+#define FACEBOOK_USER_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define FACEBOOK_TYPE_USER (facebook_user_get_type ())
+#define FACEBOOK_USER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FACEBOOK_TYPE_USER, FacebookUser))
+#define FACEBOOK_USER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FACEBOOK_TYPE_USER, FacebookUserClass))
+#define FACEBOOK_IS_USER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FACEBOOK_TYPE_USER))
+#define FACEBOOK_IS_USER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FACEBOOK_TYPE_USER))
+#define FACEBOOK_USER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), FACEBOOK_TYPE_USER, FacebookUserClass))
+
+typedef struct _FacebookUser FacebookUser;
+typedef struct _FacebookUserClass FacebookUserClass;
+
+struct _FacebookUser {
+ GObject parent_instance;
+
+ char *id;
+ gboolean is_pro;
+ char *username;
+ goffset max_bandwidth;
+ goffset used_bandwidth;
+ goffset max_filesize;
+ goffset max_videosize;
+ int n_sets;
+ int n_videos;
+};
+
+struct _FacebookUserClass {
+ GObjectClass parent_class;
+};
+
+GType facebook_user_get_type (void);
+FacebookUser * facebook_user_new (void);
+void facebook_user_set_id (FacebookUser *self,
+ const char *value);
+void facebook_user_set_is_pro (FacebookUser *self,
+ const char *value);
+void facebook_user_set_username (FacebookUser *self,
+ const char *value);
+void facebook_user_set_max_bandwidth (FacebookUser *self,
+ const char *value);
+void facebook_user_set_used_bandwidth (FacebookUser *self,
+ const char *value);
+void facebook_user_set_max_filesize (FacebookUser *self,
+ const char *value);
+void facebook_user_set_max_videosize (FacebookUser *self,
+ const char *value);
+void facebook_user_set_n_sets (FacebookUser *self,
+ const char *value);
+void facebook_user_set_n_videos (FacebookUser *self,
+ const char *value);
+
+G_END_DECLS
+
+#endif /* FACEBOOK_USER_H */
diff --git a/extensions/facebook/facebook.extension.in.in b/extensions/facebook/facebook.extension.in.in
new file mode 100644
index 0000000..3edd9de
--- /dev/null
+++ b/extensions/facebook/facebook.extension.in.in
@@ -0,0 +1,12 @@
+[Extension]
+_Name=Flicker
+_Description=Upload images to Facebook
+Authors=gthumb development team
+Copyright=Copyright © 2010 The Free Software Foundation, Inc.
+Version=1.0
+Icon=facebook
+Category=Exporter
+
+[Loader]
+Type=module
+File=%LIBRARY%
diff --git a/extensions/facebook/main.c b/extensions/facebook/main.c
new file mode 100644
index 0000000..2d25e24
--- /dev/null
+++ b/extensions/facebook/main.c
@@ -0,0 +1,53 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * GThumb
+ *
+ * Copyright (C) 2010 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 <gthumb.h>
+#include "callbacks.h"
+
+
+G_MODULE_EXPORT void
+gthumb_extension_activate (void)
+{
+ gth_hook_add_callback ("gth-browser-construct", 10, G_CALLBACK (fb__gth_browser_construct_cb), NULL);
+}
+
+
+G_MODULE_EXPORT void
+gthumb_extension_deactivate (void)
+{
+}
+
+
+G_MODULE_EXPORT gboolean
+gthumb_extension_is_configurable (void)
+{
+ return FALSE;
+}
+
+
+G_MODULE_EXPORT void
+gthumb_extension_configure (GtkWindow *parent)
+{
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]