/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
* Pix
*
* 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, see .
*/
#include
#define GDK_PIXBUF_ENABLE_BACKEND
#include
#include
#include
#include "dlg-import-from-flickr.h"
#include "flickr-account.h"
#include "flickr-photo.h"
#include "flickr-photoset.h"
#include "flickr-service.h"
#include "flickr-types.h"
#define GET_WIDGET(x) (_gtk_builder_get_widget (data->builder, (x)))
#define FAKE_SIZE 100000
enum {
ACCOUNT_DATA_COLUMN,
ACCOUNT_NAME_COLUMN
};
enum {
PHOTOSET_DATA_COLUMN,
PHOTOSET_ICON_COLUMN,
PHOTOSET_TITLE_COLUMN,
PHOTOSET_N_PHOTOS_COLUMN
};
typedef struct {
FlickrServer *server;
GthBrowser *browser;
GthFileData *location;
GtkBuilder *builder;
GtkWidget *dialog;
GtkWidget *preferences_dialog;
GtkWidget *progress_dialog;
FlickrService *service;
GtkWidget *file_list;
GList *photosets;
FlickrPhotoset *photoset;
GList *photos;
GCancellable *cancellable;
} DialogData;
static void
import_dialog_destroy_cb (GtkWidget *widget,
DialogData *data)
{
if (data->service != NULL)
gth_task_completed (GTH_TASK (data->service), NULL);
_g_object_unref (data->cancellable);
_g_object_unref (data->service);
_g_object_list_unref (data->photosets);
_g_object_unref (data->photoset);
_g_object_list_unref (data->photos);
gtk_widget_destroy (data->progress_dialog);
_g_object_unref (data->builder);
_g_object_unref (data->location);
g_free (data);
}
static GList *
get_files_to_download (DialogData *data)
{
GthFileView *file_view;
GList *selected;
GList *file_list;
file_view = (GthFileView *) gth_file_list_get_view (GTH_FILE_LIST (data->file_list));
selected = gth_file_selection_get_selected (GTH_FILE_SELECTION (file_view));
if (selected != NULL)
file_list = gth_file_list_get_files (GTH_FILE_LIST (data->file_list), selected);
else
file_list = gth_file_store_get_visibles (GTH_FILE_STORE (gth_file_view_get_model (file_view)));
_gtk_tree_path_list_free (selected);
return file_list;
}
static void
import_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:
gth_file_list_cancel (GTH_FILE_LIST (data->file_list), (DataFunc) gtk_widget_destroy, data->dialog);
break;
case GTK_RESPONSE_OK:
{
GtkTreeIter iter;
FlickrPhotoset *photoset;
GList *file_list;
if (! gtk_combo_box_get_active_iter (GTK_COMBO_BOX (GET_WIDGET ("photoset_combobox")), &iter)) {
gtk_dialog_set_response_sensitive (GTK_DIALOG (data->dialog), GTK_RESPONSE_OK, FALSE);
return;
}
gtk_tree_model_get (GTK_TREE_MODEL (GET_WIDGET ("photoset_liststore")), &iter,
PHOTOSET_DATA_COLUMN, &photoset,
-1);
file_list = get_files_to_download (data);
if (file_list != NULL) {
GSettings *settings;
GFile *destination;
char *subfolder_template;
GthTask *task;
settings = g_settings_new (PIX_IMPORTER_SCHEMA);
destination = gth_import_preferences_get_destination ();
subfolder_template = g_settings_get_string (settings, PREF_IMPORTER_SUBFOLDER_TEMPLATE);
task = gth_import_task_new (data->browser,
file_list,
destination,
subfolder_template,
(photoset->title != NULL ? photoset->title : ""),
NULL,
FALSE,
FALSE,
FALSE);
gth_browser_exec_task (data->browser, task, GTH_TASK_FLAGS_DEFAULT);
gtk_widget_destroy (data->dialog);
g_object_unref (task);
g_free (subfolder_template);
_g_object_unref (destination);
g_object_unref (settings);
}
_g_object_list_unref (file_list);
g_object_unref (photoset);
}
break;
default:
break;
}
}
static void
update_account_list (DialogData *data)
{
int current_account_idx;
OAuthAccount *current_account;
int idx;
GList *scan;
GtkTreeIter iter;
gtk_list_store_clear (GTK_LIST_STORE (GET_WIDGET ("account_liststore")));
current_account_idx = 0;
current_account = web_service_get_current_account (WEB_SERVICE (data->service));
for (scan = web_service_get_accounts (WEB_SERVICE (data->service)), idx = 0; scan; scan = scan->next, idx++) {
OAuthAccount *account = scan->data;
if (oauth_account_cmp (current_account, account) == 0)
current_account_idx = 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->name,
-1);
}
gtk_combo_box_set_active (GTK_COMBO_BOX (GET_WIDGET ("account_combobox")), current_account_idx);
}
static void
photoset_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->photosets);
data->photosets = flickr_service_list_photosets_finish (FLICKR_SERVICE (source_object), res, &error);
if (error != NULL) {
if (data->service != NULL)
gth_task_dialog (GTH_TASK (data->service), TRUE, NULL);
_gtk_error_dialog_from_gerror_run (GTK_WINDOW (data->browser), _("Could not connect to the server"), error);
g_clear_error (&error);
gtk_widget_destroy (data->dialog);
return;
}
gtk_list_store_clear (GTK_LIST_STORE (GET_WIDGET ("photoset_liststore")));
for (scan = data->photosets; scan; scan = scan->next) {
FlickrPhotoset *photoset = scan->data;
char *n_photos;
GtkTreeIter iter;
n_photos = g_strdup_printf ("(%d)", photoset->n_photos);
gtk_list_store_append (GTK_LIST_STORE (GET_WIDGET ("photoset_liststore")), &iter);
gtk_list_store_set (GTK_LIST_STORE (GET_WIDGET ("photoset_liststore")), &iter,
PHOTOSET_DATA_COLUMN, photoset,
PHOTOSET_ICON_COLUMN, "file-catalog-symbolic",
PHOTOSET_TITLE_COLUMN, photoset->title,
PHOTOSET_N_PHOTOS_COLUMN, n_photos,
-1);
g_free (n_photos);
}
gth_task_dialog (GTH_TASK (data->service), TRUE, NULL);
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
authentication_ready_cb (WebService *service,
DialogData *data)
{
update_account_list (data);
flickr_service_list_photosets (data->service,
data->cancellable,
photoset_list_ready_cb,
data);
}
static void
authentication_accounts_changed_cb (WebService *service,
DialogData *data)
{
update_account_list (data);
}
static void
edit_accounts_button_clicked_cb (GtkButton *button,
DialogData *data)
{
web_service_edit_accounts (WEB_SERVICE (data->service), GTK_WINDOW (data->dialog));
}
static void
account_combobox_changed_cb (GtkComboBox *widget,
gpointer user_data)
{
DialogData *data = user_data;
GtkTreeIter iter;
OAuthAccount *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 (oauth_account_cmp (account, web_service_get_current_account (WEB_SERVICE (data->service))) != 0)
web_service_connect (WEB_SERVICE (data->service), account);
g_object_unref (account);
}
static void
update_selection_status (DialogData *data)
{
GList *file_list;
int n_selected;
char *text_selected;
file_list = get_files_to_download (data);
n_selected = g_list_length (file_list);
text_selected = g_strdup_printf (g_dngettext (NULL, "%d file", "%d files", n_selected), n_selected);
gtk_label_set_text (GTK_LABEL (GET_WIDGET ("images_info_label")), text_selected);
g_free (text_selected);
_g_object_list_unref (file_list);
}
static void
list_photos_ready_cb (GObject *source_object,
GAsyncResult *result,
gpointer user_data)
{
DialogData *data = user_data;
GError *error = NULL;
GList *list;
GList *scan;
gth_task_dialog (GTH_TASK (data->service), TRUE, NULL);
_g_object_list_unref (data->photos);
data->photos = flickr_service_list_photos_finish (data->service, result, &error);
if (error != NULL) {
if (data->service != NULL)
gth_task_dialog (GTH_TASK (data->service), TRUE, NULL);
_gtk_error_dialog_from_gerror_show (GTK_WINDOW (data->browser), _("Could not get the photo list"), error);
g_clear_error (&error);
gtk_widget_destroy (data->dialog);
return;
}
list = NULL;
for (scan = data->photos; scan; scan = scan->next) {
FlickrPhoto *photo = scan->data;
GthFileData *file_data;
if (photo->url[FLICKR_URL_O] == NULL)
continue;
file_data = gth_file_data_new_for_uri (photo->url[FLICKR_URL_O], (photo->mime_type != NULL) ? photo->mime_type : "image/jpeg");
g_file_info_set_file_type (file_data->info, G_FILE_TYPE_REGULAR);
g_file_info_set_size (file_data->info, FAKE_SIZE); /* set a fake size to make the progress dialog work correctly */
g_file_info_set_attribute_object (file_data->info, "flickr::object", G_OBJECT (photo));
list = g_list_prepend (list, file_data);
}
gth_file_list_set_files (GTH_FILE_LIST (data->file_list), list);
update_selection_status (data);
gtk_dialog_set_response_sensitive (GTK_DIALOG (data->dialog), GTK_RESPONSE_OK, list != NULL);
_g_object_list_unref (list);
}
static void
photoset_combobox_changed_cb (GtkComboBox *widget,
gpointer user_data)
{
DialogData *data = user_data;
GtkTreeIter iter;
if (! gtk_combo_box_get_active_iter (widget, &iter)) {
gth_file_list_clear (GTH_FILE_LIST (data->file_list), _("No album selected"));
return;
}
_g_object_unref (data->photoset);
gtk_tree_model_get (gtk_combo_box_get_model (widget),
&iter,
PHOTOSET_DATA_COLUMN, &data->photoset,
-1);
gth_import_preferences_dialog_set_event (GTH_IMPORT_PREFERENCES_DIALOG (data->preferences_dialog), data->photoset->title);
gth_task_dialog (GTH_TASK (data->service), FALSE, NULL);
flickr_service_list_photos (data->service,
data->photoset,
"original_format, url_sq, url_t, url_s, url_m, url_z, url_b, url_o",
data->cancellable,
list_photos_ready_cb,
data);
}
static GthImage *
flickr_thumbnail_loader (GInputStream *istream,
GthFileData *file_data,
int requested_size,
int *original_width,
int *original_height,
gboolean *loaded_original,
gpointer user_data,
GCancellable *cancellable,
GError **error)
{
GthImage *image = NULL;
GthThumbLoader *thumb_loader = user_data;
FlickrPhoto *photo;
const char *uri = NULL;
photo = (FlickrPhoto *) g_file_info_get_attribute_object (file_data->info, "flickr::object");
requested_size = gth_thumb_loader_get_requested_size (thumb_loader);
if (requested_size == FLICKR_SIZE_SMALL_SQUARE)
uri = photo->url[FLICKR_URL_SQ];
else if (requested_size == FLICKR_SIZE_THUMBNAIL)
uri = photo->url[FLICKR_URL_T];
else if (requested_size == FLICKR_SIZE_SMALL)
uri = photo->url[FLICKR_URL_S];
else if (requested_size == FLICKR_SIZE_MEDIUM)
uri = photo->url[FLICKR_URL_M];
if (uri == NULL)
uri = photo->url[FLICKR_URL_O];
if (uri != NULL) {
GFile *file;
void *buffer;
gsize size;
file = g_file_new_for_uri (uri);
if (_g_file_load_in_buffer (file, &buffer, &size, cancellable, error)) {
GInputStream *stream;
GdkPixbuf *pixbuf;
stream = g_memory_input_stream_new_from_data (buffer, size, g_free);
pixbuf = gdk_pixbuf_new_from_stream (stream, cancellable, error);
if (pixbuf != NULL) {
GdkPixbuf *rotated;
rotated = gdk_pixbuf_apply_embedded_orientation (pixbuf);
g_object_unref (pixbuf);
pixbuf = rotated;
image = gth_image_new_for_pixbuf (pixbuf);
}
g_object_unref (pixbuf);
g_object_unref (stream);
}
g_object_unref (file);
}
else
*error = g_error_new_literal (GTH_ERROR, 0, "cannot generate the thumbnail");
return image;
}
static int
flickr_photo_position_func (GthFileData *a,
GthFileData *b)
{
FlickrPhoto *photo_a;
FlickrPhoto *photo_b;
photo_a = (FlickrPhoto *) g_file_info_get_attribute_object (a->info, "flickr::object");
photo_b = (FlickrPhoto *) g_file_info_get_attribute_object (b->info, "flickr::object");
if (photo_a->position == photo_b->position)
return strcmp (photo_a->title, photo_b->title);
else if (photo_a->position > photo_b->position)
return 1;
else
return -1;
}
static void
file_list_selection_changed_cb (GthFileView *file_view,
gpointer user_data)
{
update_selection_status ((DialogData *) user_data);
}
void
dlg_import_from_flickr (FlickrServer *server,
GthBrowser *browser)
{
DialogData *data;
GthThumbLoader *thumb_loader;
char *title;
data = g_new0 (DialogData, 1);
data->server = server;
data->browser = browser;
data->location = gth_file_data_dup (gth_browser_get_location_data (browser));
data->builder = _gtk_builder_new_from_file ("import-from-flickr.ui", "flicker_utils");
data->cancellable = g_cancellable_new ();
data->dialog = g_object_new (GTK_TYPE_DIALOG,
"transient-for", GTK_WINDOW (browser),
"modal", FALSE,
"destroy-with-parent", FALSE,
"use-header-bar", _gtk_settings_get_dialogs_use_header (),
NULL);
gtk_container_add (GTK_CONTAINER (gtk_dialog_get_content_area (GTK_DIALOG (data->dialog))),
_gtk_builder_get_widget (data->builder, "dialog_content"));
gtk_dialog_add_buttons (GTK_DIALOG (data->dialog),
_GTK_LABEL_CANCEL, GTK_RESPONSE_CANCEL,
_("_Import"), GTK_RESPONSE_OK,
NULL);
_gtk_dialog_add_class_to_response (GTK_DIALOG (data->dialog), GTK_RESPONSE_OK, GTK_STYLE_CLASS_SUGGESTED_ACTION);
{
GtkCellLayout *cell_layout;
GtkCellRenderer *renderer;
cell_layout = GTK_CELL_LAYOUT (GET_WIDGET ("photoset_combobox"));
renderer = gtk_cell_renderer_pixbuf_new ();
gtk_cell_layout_pack_start (cell_layout, renderer, FALSE);
gtk_cell_layout_set_attributes (cell_layout, renderer,
"icon-name", PHOTOSET_ICON_COLUMN,
NULL);
renderer = gtk_cell_renderer_text_new ();
gtk_cell_layout_pack_start (cell_layout, renderer, TRUE);
gtk_cell_layout_set_attributes (cell_layout, renderer,
"text", PHOTOSET_TITLE_COLUMN,
NULL);
renderer = gtk_cell_renderer_text_new ();
gtk_cell_layout_pack_start (cell_layout, renderer, FALSE);
gtk_cell_layout_set_attributes (cell_layout, renderer,
"text", PHOTOSET_N_PHOTOS_COLUMN,
NULL);
}
/* Set the widget data */
data->file_list = gth_file_list_new (gth_grid_view_new (), GTH_FILE_LIST_MODE_NORMAL, FALSE);
thumb_loader = gth_file_list_get_thumb_loader (GTH_FILE_LIST (data->file_list));
gth_thumb_loader_set_use_cache (thumb_loader, FALSE);
gth_thumb_loader_set_loader_func (thumb_loader, flickr_thumbnail_loader);
gth_file_list_set_thumb_size (GTH_FILE_LIST (data->file_list), FLICKR_SIZE_THUMBNAIL);
gth_file_list_enable_thumbs (GTH_FILE_LIST (data->file_list), TRUE);
gth_file_list_set_ignore_hidden (GTH_FILE_LIST (data->file_list), TRUE);
gth_file_list_set_caption (GTH_FILE_LIST (data->file_list), "none");
gth_file_list_set_sort_func (GTH_FILE_LIST (data->file_list), flickr_photo_position_func, FALSE);
gth_file_list_clear (GTH_FILE_LIST (data->file_list), _("No album selected"));
gtk_widget_show (data->file_list);
gtk_box_pack_start (GTK_BOX (GET_WIDGET ("images_box")), data->file_list, TRUE, TRUE, 0);
gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (GET_WIDGET ("photoset_liststore")), PHOTOSET_TITLE_COLUMN, GTK_SORT_ASCENDING);
gtk_dialog_set_response_sensitive (GTK_DIALOG (data->dialog), GTK_RESPONSE_OK, FALSE);
data->preferences_dialog = gth_import_preferences_dialog_new ();
gtk_window_set_transient_for (GTK_WINDOW (data->preferences_dialog), GTK_WINDOW (data->dialog));
gtk_box_pack_start (GTK_BOX (GET_WIDGET ("destination_button_box")),
gth_import_destination_button_new (GTH_IMPORT_PREFERENCES_DIALOG (data->preferences_dialog)),
TRUE,
TRUE,
0);
gtk_widget_show_all (GET_WIDGET ("destination_button_box"));
title = g_strdup_printf (_("Import from %s"), data->server->display_name);
gtk_window_set_title (GTK_WINDOW (data->dialog), title);
g_free (title);
_gtk_window_resize_to_fit_screen_height (data->dialog, 500);
/* Set the signals handlers. */
g_signal_connect (data->dialog,
"destroy",
G_CALLBACK (import_dialog_destroy_cb),
data);
g_signal_connect (data->dialog,
"delete-event",
G_CALLBACK (gtk_true),
NULL);
g_signal_connect (data->dialog,
"response",
G_CALLBACK (import_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);
g_signal_connect (GET_WIDGET ("photoset_combobox"),
"changed",
G_CALLBACK (photoset_combobox_changed_cb),
data);
g_signal_connect (gth_file_list_get_view (GTH_FILE_LIST (data->file_list)),
"file-selection-changed",
G_CALLBACK (file_list_selection_changed_cb),
data);
update_selection_status (data);
gth_import_preferences_dialog_set_event (GTH_IMPORT_PREFERENCES_DIALOG (data->preferences_dialog), "");
data->service = flickr_service_new (server,
data->cancellable,
GTK_WIDGET (data->browser),
data->dialog);
g_signal_connect (data->service,
"account-ready",
G_CALLBACK (authentication_ready_cb),
data);
g_signal_connect (data->service,
"accounts-changed",
G_CALLBACK (authentication_accounts_changed_cb),
data);
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->service), GTH_TASK_FLAGS_DEFAULT);
web_service_autoconnect (WEB_SERVICE (data->service));
}