Logo Search packages:      
Sourcecode: eog version File versions

eog-image.c

#include <config.h>
#include <string.h>
#include <unistd.h>
#include <glib/gthread.h>
#include <glib/gqueue.h>
#include <gtk/gtkmain.h>
#include <libgnome/gnome-macros.h>
#include <libgnome/gnome-i18n.h>
#include <libgnomeui/gnome-thumbnail.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <libgnomevfs/gnome-vfs.h>
#if HAVE_EXIF
#include <libexif/exif-data.h>
#include <libexif/exif-utils.h>
#include <libexif/exif-loader.h>
#endif

#include "libeog-marshal.h"
#include "eog-image.h"
#include "eog-image-private.h"
#include "eog-pixbuf-util.h"
#include "eog-image-cache.h"
#include "eog-metadata-reader.h"
#include "eog-image-save-info.h"
#include "eog-util.h"
#if HAVE_JPEG
#include "eog-image-jpeg.h"
#endif

static GThread     *thread                     = NULL;
static gboolean     thread_running             = FALSE;
static GQueue      *jobs_waiting               = NULL;
static GQueue      *jobs_done                  = NULL;
static gint         dispatch_callbacks_id      = -1;
static GStaticMutex jobs_mutex                 = G_STATIC_MUTEX_INIT;


enum {
      SIGNAL_LOADING_UPDATE,
      SIGNAL_LOADING_SIZE_PREPARED,
      SIGNAL_LOADING_FINISHED,
      SIGNAL_LOADING_FAILED,
      SIGNAL_LOADING_CANCELLED,
      SIGNAL_PROGRESS,
      SIGNAL_IMAGE_CHANGED,
      SIGNAL_THUMBNAIL_FINISHED,
      SIGNAL_THUMBNAIL_FAILED,
      SIGNAL_THUMBNAIL_CANCELLED,
      SIGNAL_LAST
};

static gint eog_image_signals [SIGNAL_LAST];

#define NO_DEBUG
#define DEBUG_ASYNC 0
#define THUMB_DEBUG 0
#define OBJECT_WATCH 0

#if OBJECT_WATCH
static int n_active_images = 0;
#endif

#define CHECK_LOAD_TIMEOUT 5

/* Chunk size for reading image data */
#define READ_BUFFER_SIZE 65536

/*============================================

  static thumbnail loader for all image objects

  ------------------------------------------*/

static gint
dispatch_image_finished (gpointer data)
{
      EogImage *image;
 
#if DEBUG_ASYNC
      g_print ("*** dispatch callback called ***");
#endif

      image = NULL;

      g_static_mutex_lock (&jobs_mutex);
      if (!g_queue_is_empty (jobs_done)) {
            image = EOG_IMAGE (g_queue_pop_head (jobs_done));
      }
      else {
            g_queue_free (jobs_done);
            jobs_done = NULL;
            dispatch_callbacks_id = -1;
      }
      g_static_mutex_unlock (&jobs_mutex);      

      if (image == NULL) {
#if DEBUG_ASYNC
            g_print (" --- shutdown\n");
#endif
            return FALSE;
      }
            
      if (image->priv->thumbnail != NULL) {
            g_signal_emit (G_OBJECT (image), eog_image_signals [SIGNAL_THUMBNAIL_FINISHED], 0);
      }
      else {
            g_signal_emit (G_OBJECT (image), eog_image_signals [SIGNAL_THUMBNAIL_FAILED], 0);
      }
      g_object_unref (image);

#if DEBUG_ASYNC
      g_print ("\n");
#endif
      
      return TRUE;
}

static gpointer
create_thumbnails (gpointer data)
{
      EogImage *image;
      EogImagePrivate *priv;
      char *uri_str = NULL;
      char *path = NULL;
      gboolean finished = FALSE;
      gboolean create_thumb = FALSE;
      GnomeVFSFileInfo *info;
      GnomeVFSResult result;
      GnomeThumbnailFactory *factory;

#if DEBUG_ASYNC
      g_print ("*** Start thread ***\n");
#endif      

      while (!finished) {
              create_thumb = FALSE;

            /* get next image to process */
            g_static_mutex_lock (&jobs_mutex);

            image = EOG_IMAGE (g_queue_pop_head (jobs_waiting));
            g_assert (image != NULL);

            g_static_mutex_unlock (&jobs_mutex);

            /* thumbnail loading/creation  */
            priv = image->priv;

            if (priv->thumbnail != NULL) {
                  g_object_unref (priv->thumbnail);
                  priv->thumbnail = NULL;
            }

            uri_str = gnome_vfs_uri_to_string (priv->uri, GNOME_VFS_URI_HIDE_NONE);
            info = gnome_vfs_file_info_new ();
            result = gnome_vfs_get_file_info_uri (priv->uri, info, 
                                          GNOME_VFS_FILE_INFO_DEFAULT |
                                          GNOME_VFS_FILE_INFO_GET_MIME_TYPE);

            if (result == GNOME_VFS_OK &&
                (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_MTIME) != 0 &&
                (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE) != 0) 
            {
                  path = gnome_thumbnail_path_for_uri (uri_str, GNOME_THUMBNAIL_SIZE_NORMAL);

                  if (g_file_test (path, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR)) {
                        priv->thumbnail = gdk_pixbuf_new_from_file (path, NULL);

                        if (!gnome_thumbnail_is_valid (priv->thumbnail, uri_str, info->mtime)) {
                              g_object_unref (priv->thumbnail);
                              priv->thumbnail = NULL;
                              create_thumb = TRUE;
#if THUMB_DEBUG
                              g_print ("uri: %s, thumbnail is invalid\n", uri_str);
#endif
                        }
                  }
                  else {
#if THUMB_DEBUG
                        g_print ("uri: %s, has no thumbnail file\n", uri_str);
#endif
                        create_thumb = TRUE;
                  }
            }
            else {
#if THUMB_DEBUG
                  g_print ("uri: %s vfs errror: %s\n", uri_str, gnome_vfs_result_to_string (result));
#endif
            }

            if (create_thumb) {

                  g_assert (path != NULL);
                  g_assert (info != NULL);
                  g_assert ((info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_MTIME) != 0);
                  g_assert ((info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE) != 0);
                  g_assert (priv->thumbnail == NULL);
            
#if THUMB_DEBUG
                  g_print ("create thumbnail for uri: %s\n -> mtime: %i\n -> mime_type; %s\n -> thumbpath: %s\n", 
                         uri_str, info->mtime, info->mime_type, path);
#endif
      
                  factory = gnome_thumbnail_factory_new (GNOME_THUMBNAIL_SIZE_NORMAL);
      
                  if (!gnome_thumbnail_factory_has_valid_failed_thumbnail (factory, uri_str, info->mtime) &&
                      gnome_thumbnail_factory_can_thumbnail (factory, uri_str, info->mime_type, info->mtime)) 
                  {
                        priv->thumbnail = gnome_thumbnail_factory_generate_thumbnail (factory, uri_str, info->mime_type);
                        
                        if (priv->thumbnail != NULL) {
                              gnome_thumbnail_factory_save_thumbnail (factory, priv->thumbnail, uri_str, info->mtime);
                        }
                  }
                  
                  g_object_unref (factory);
                  
            }
            
            gnome_vfs_file_info_unref (info);
            g_free (uri_str);
            g_free (path);
            

            /* check for thread shutdown */
            g_static_mutex_lock (&jobs_mutex);

            if (jobs_done == NULL) {
                  jobs_done = g_queue_new ();
            }
            g_queue_push_tail (jobs_done, image);
            
            if (dispatch_callbacks_id == -1) {
                  dispatch_callbacks_id = g_idle_add (dispatch_image_finished, NULL);
            }

            if (g_queue_is_empty (jobs_waiting)) {
                  g_queue_free (jobs_waiting);
                  jobs_waiting = NULL;
                  thread_running = FALSE;
                  finished = TRUE;
            }
                  
            g_static_mutex_unlock (&jobs_mutex);
      }

#if DEBUG_ASYNC
      g_print ("*** Finish thread ***\n");
#endif      


      return NULL;
}

static void
add_image_to_queue (EogImage *image)
{
      if (!g_thread_supported ()) {
            g_thread_init (NULL);
      }

      g_static_mutex_lock (&jobs_mutex);

      if (jobs_waiting == NULL) {
            jobs_waiting = g_queue_new ();
      }

      g_object_ref (image);
      g_queue_push_tail (jobs_waiting, image);

      if (!thread_running) {
            thread = g_thread_create (create_thumbnails, NULL, TRUE, NULL);
            thread_running = TRUE;
      }

      g_static_mutex_unlock (&jobs_mutex);
}


/*======================================

   EogImage implementation 

   ------------------------------------*/

GNOME_CLASS_BOILERPLATE (EogImage,
                   eog_image,
                   GObject,
                   G_TYPE_OBJECT);

static void
eog_image_dispose (GObject *object)
{
      EogImagePrivate *priv;
      GList *it;

      priv = EOG_IMAGE (object)->priv;

      eog_image_free_mem (EOG_IMAGE (object));

      if (priv->uri) {
            gnome_vfs_uri_unref (priv->uri);
            priv->uri = NULL;
      }

      if (priv->caption) {
            g_free (priv->caption);
            priv->caption = NULL;
      }

      if (priv->caption_key) {
            g_free (priv->caption_key);
            priv->caption_key = NULL;
      }

      if (priv->file_type) {
            g_free (priv->file_type);
            priv->file_type = NULL;
      }
      
      if (priv->status_mutex) {
            g_mutex_free (priv->status_mutex);
            priv->status_mutex = NULL;
      }

      if (priv->trans) {
            g_object_unref (priv->trans);
            priv->trans = NULL;
      }

      if (priv->undo_stack) {
            for (it = priv->undo_stack; it != NULL; it = it->next){
                  g_object_unref (G_OBJECT (it->data));
            }

            g_list_free (priv->undo_stack);
            priv->undo_stack = NULL;
      }
}

static void
eog_image_finalize (GObject *object)
{
      EogImagePrivate *priv;

      priv = EOG_IMAGE (object)->priv;

      g_free (priv);

#if OBJECT_WATCH
      n_active_images--;
      if (n_active_images == 0) {
            g_message ("All image objects destroyed.");
      }
      else {
            g_message ("active image objects: %i", n_active_images);
      }
#endif
}

static void
eog_image_class_init (EogImageClass *klass)
{
      GObjectClass *object_class = (GObjectClass*) klass;

      object_class->dispose = eog_image_dispose;
      object_class->finalize = eog_image_finalize;

      eog_image_signals [SIGNAL_LOADING_UPDATE] = 
            g_signal_new ("loading_update",
                        G_TYPE_OBJECT,
                        G_SIGNAL_RUN_LAST,
                        G_STRUCT_OFFSET (EogImageClass, loading_update),
                        NULL, NULL,
                        libeog_marshal_VOID__INT_INT_INT_INT,
                        G_TYPE_NONE, 4,
                        G_TYPE_INT,
                        G_TYPE_INT,
                        G_TYPE_INT,
                        G_TYPE_INT);
      eog_image_signals [SIGNAL_LOADING_SIZE_PREPARED] = 
            g_signal_new ("loading_size_prepared",
                        G_TYPE_OBJECT,
                        G_SIGNAL_RUN_LAST,
                        G_STRUCT_OFFSET (EogImageClass, loading_size_prepared),
                        NULL, NULL,
                        libeog_marshal_VOID__INT_INT,
                        G_TYPE_NONE, 2,
                        G_TYPE_INT,
                        G_TYPE_INT);
      eog_image_signals [SIGNAL_LOADING_FINISHED] = 
            g_signal_new ("loading_finished",
                        G_TYPE_OBJECT,
                        G_SIGNAL_RUN_LAST,
                        G_STRUCT_OFFSET (EogImageClass, loading_finished),
                        NULL, NULL,
                        g_cclosure_marshal_VOID__VOID,
                        G_TYPE_NONE, 0);                   
      eog_image_signals [SIGNAL_LOADING_FAILED] = 
            g_signal_new ("loading_failed",
                        G_TYPE_OBJECT,
                        G_SIGNAL_RUN_LAST,
                        G_STRUCT_OFFSET (EogImageClass, loading_failed),
                        NULL, NULL,
                        libeog_marshal_VOID__POINTER,
                        G_TYPE_NONE, 1,
                        G_TYPE_POINTER);
      eog_image_signals [SIGNAL_LOADING_CANCELLED] = 
            g_signal_new ("loading_cancelled",
                        G_TYPE_OBJECT,
                        G_SIGNAL_RUN_LAST,
                        G_STRUCT_OFFSET (EogImageClass, loading_cancelled),
                        NULL, NULL,
                        g_cclosure_marshal_VOID__VOID,
                        G_TYPE_NONE, 0);
      eog_image_signals [SIGNAL_PROGRESS] = 
            g_signal_new ("progress",
                        G_TYPE_OBJECT,
                        G_SIGNAL_RUN_LAST,
                        G_STRUCT_OFFSET (EogImageClass, progress),
                        NULL, NULL,
                        g_cclosure_marshal_VOID__FLOAT,
                        G_TYPE_NONE, 1,
                        G_TYPE_FLOAT);
      eog_image_signals [SIGNAL_IMAGE_CHANGED] = 
            g_signal_new ("image_changed",
                        G_TYPE_OBJECT,
                        G_SIGNAL_RUN_LAST,
                        G_STRUCT_OFFSET (EogImageClass, image_changed),
                        NULL, NULL,
                        g_cclosure_marshal_VOID__VOID,
                        G_TYPE_NONE, 0);
      eog_image_signals [SIGNAL_THUMBNAIL_FINISHED] = 
            g_signal_new ("thumbnail_finished",
                        G_TYPE_OBJECT,
                        G_SIGNAL_RUN_LAST,
                        G_STRUCT_OFFSET (EogImageClass, thumbnail_finished),
                        NULL, NULL,
                        g_cclosure_marshal_VOID__VOID,
                        G_TYPE_NONE, 0);                   
      eog_image_signals [SIGNAL_THUMBNAIL_FAILED] = 
            g_signal_new ("thumbnail_failed",
                        G_TYPE_OBJECT,
                        G_SIGNAL_RUN_LAST,
                        G_STRUCT_OFFSET (EogImageClass, thumbnail_failed),
                        NULL, NULL,
                        g_cclosure_marshal_VOID__VOID,
                        G_TYPE_NONE, 0);
      eog_image_signals [SIGNAL_THUMBNAIL_CANCELLED] = 
            g_signal_new ("thumbnail_cancelled",
                        G_TYPE_OBJECT,
                        G_SIGNAL_RUN_LAST,
                        G_STRUCT_OFFSET (EogImageClass, thumbnail_cancelled),
                        NULL, NULL,
                        g_cclosure_marshal_VOID__VOID,
                        G_TYPE_NONE, 0);
}

static void
eog_image_instance_init (EogImage *img)
{
      EogImagePrivate *priv;

      priv = g_new0 (EogImagePrivate, 1);

      priv->uri = NULL;
      priv->image = NULL;
      priv->thumbnail = NULL;
      priv->width = priv->height = -1;
      priv->modified = FALSE;
      priv->status_mutex = g_mutex_new ();
      priv->load_finished = NULL;
#if HAVE_EXIF
      priv->exif = NULL;
#endif

      img->priv = priv;
}

EogImage* 
eog_image_new_uri (GnomeVFSURI *uri)
{
      EogImage *img;
      EogImagePrivate *priv;
      
      img = EOG_IMAGE (g_object_new (EOG_TYPE_IMAGE, NULL));
      priv = img->priv;

      priv->uri = gnome_vfs_uri_ref (uri);
      priv->mode = EOG_IMAGE_LOAD_DEFAULT;
      priv->status = EOG_IMAGE_STATUS_UNKNOWN;
      priv->modified = FALSE;
      priv->load_thread = NULL;

      priv->undo_stack = NULL;
      priv->trans = NULL;

#if OBJECT_WATCH
      n_active_images++;
      g_message ("active image objects: %i", n_active_images);
#endif
      
      return img;
}

EogImage* 
eog_image_new (const char *txt_uri)
{
      GnomeVFSURI *uri;
      EogImage *image;

      uri = gnome_vfs_uri_new (txt_uri);
      image = eog_image_new_uri (uri);
      gnome_vfs_uri_unref (uri);

      return image;
}

GQuark
eog_image_error_quark (void)
{
      static GQuark q = 0;
      if (q == 0)
            q = g_quark_from_static_string ("eog-image-error-quark");
      
      return q;
}

#if HAVE_EXIF
static void
update_exif_data (EogImage *image)
{
      EogImagePrivate *priv;
      ExifEntry *entry;
      ExifByteOrder bo;

      g_return_if_fail (EOG_IS_IMAGE (image));

      priv = image->priv;

#ifdef DEBUG
      g_message ("update exif data");
#endif
      if (priv->exif == NULL) return;

      /* FIXME: Must we update more properties here? */
      bo = exif_data_get_byte_order (priv->exif);
      
      entry = exif_content_get_entry (priv->exif->ifd [EXIF_IFD_EXIF], EXIF_TAG_PIXEL_X_DIMENSION);
      if (entry != NULL) {
            exif_set_long (entry->data, bo, priv->width);
      }
      
      entry = exif_content_get_entry (priv->exif->ifd [EXIF_IFD_EXIF], EXIF_TAG_PIXEL_Y_DIMENSION);
      if (entry != NULL) {
            exif_set_long (entry->data, bo, priv->height);
      }
}

      
#endif 

static gboolean
load_emit_signal_progress (gpointer data)
{
      EogImage *img;

      img = EOG_IMAGE (data);

      g_signal_emit (G_OBJECT (img), eog_image_signals [SIGNAL_PROGRESS], 0, img->priv->progress);

      return FALSE;
}


static void
transform_progress_hook_async (EogTransform *trans, float progress, gpointer hook_data)
{
      EogImage *img;
      EogImagePrivate *priv;

      img = EOG_IMAGE(hook_data);
      priv = img->priv;

      if ((progress < priv->progress) || (progress - priv->progress) > 0.1) {
            g_mutex_lock (priv->status_mutex);
            priv->progress = progress;
            g_mutex_unlock (priv->status_mutex);            

            g_idle_add (load_emit_signal_progress, img);
      }
}


static gboolean
load_emit_signal_done (gpointer data)
{
      EogImage *img;
      EogImagePrivate *priv;

      img = EOG_IMAGE (data);
      priv = img->priv;

      switch (img->priv->status) {
      case EOG_IMAGE_STATUS_FAILED:
      case EOG_IMAGE_STATUS_UNKNOWN:
            if (priv->image != NULL) {
                  g_object_unref (priv->image);
                  priv->image = NULL;
            }
#if HAVE_EXIF
            if (priv->exif != NULL) {
                  exif_data_unref (priv->exif);
                  priv->exif = NULL;
            }
#endif
            priv->progress = 0.0;

            if (img->priv->status == EOG_IMAGE_STATUS_FAILED) {
                  g_signal_emit (G_OBJECT (img), eog_image_signals [SIGNAL_LOADING_FAILED], 0, priv->error_message);
            }
            else {
                  g_signal_emit (G_OBJECT (img), eog_image_signals [SIGNAL_LOADING_CANCELLED], 0);
            }
            
            break;

      case EOG_IMAGE_STATUS_LOADED:
            g_assert (priv->file_type != NULL);

            priv->progress = 1.0;
            g_signal_emit (G_OBJECT (img), eog_image_signals [SIGNAL_LOADING_FINISHED], 0);
            break;

      default:
            g_assert_not_reached ();
            break;
      }
      g_signal_emit (G_OBJECT (img), eog_image_signals [SIGNAL_PROGRESS], 0, img->priv->progress);

      /* release reference we had for loading process */
      g_object_unref (img);

      return FALSE;
}

static gboolean
load_emit_signal_size_prepared (gpointer data)
{

      EogImage *img;
      
      img = EOG_IMAGE (data);

      g_signal_emit (G_OBJECT (img), eog_image_signals [SIGNAL_LOADING_SIZE_PREPARED], 0, img->priv->width, img->priv->height);

      return FALSE;
      }

static void
load_size_prepared (GdkPixbufLoader *loader, gint width, gint height, gpointer data)
{
      EogImage *img;

      g_return_if_fail (EOG_IS_IMAGE (data));

      img = EOG_IMAGE (data);

      g_mutex_lock (img->priv->status_mutex);
      img->priv->width = width;
      img->priv->height = height;
      g_mutex_unlock (img->priv->status_mutex);

      g_idle_add (load_emit_signal_size_prepared, img);
}

static EogMetadataReader*
check_for_metadata_img_format (EogImage *img, guchar *buffer, int bytes_read)
{
      EogMetadataReader *md_reader = NULL;

#ifdef DEBUG      
      g_print ("check img format for jpeg: %x%x - length: %i\n", buffer[0], buffer[1], bytes_read);
#endif
      
      if (bytes_read >= 2) {
            /* SOI (start of image) marker for JPEGs is 0xFFD8 */
            if ((buffer[0] == 0xFF) && (buffer[1] == 0xD8)) {           
                  md_reader = eog_metadata_reader_new (EOG_METADATA_JPEG);
            }
      }
      
      return md_reader;
}

/* this function runs in it's own thread */
static gpointer
real_image_load (gpointer data)
{
      EogImage *img;
      EogImagePrivate *priv;
      GdkPixbufLoader *loader;
      GnomeVFSFileInfo *info;
      GnomeVFSResult result;
      GnomeVFSHandle *handle;
      guchar *buffer;
      GnomeVFSFileSize bytes_read;
      GnomeVFSFileSize bytes_read_total;
      gboolean failed;
      GError *error = NULL;
      gboolean first_run = TRUE;
      EogMetadataReader *md_reader = NULL;

      img = EOG_IMAGE (data);
      priv = img->priv;

#ifdef DEBUG
      g_print ("real image load %s\n", gnome_vfs_uri_to_string (priv->uri, GNOME_VFS_URI_HIDE_NONE));
#endif

      g_assert (priv->image == NULL);

      if (priv->file_type != NULL) {
            g_free (priv->file_type);
            priv->file_type = NULL;
      }

      result = gnome_vfs_open_uri (&handle, priv->uri, GNOME_VFS_OPEN_READ);
      if (result != GNOME_VFS_OK) {
            g_mutex_lock (priv->status_mutex);
            priv->status = EOG_IMAGE_STATUS_FAILED;
            priv->error_message = (char*) gnome_vfs_result_to_string (result);
            g_mutex_unlock (priv->status_mutex);

            g_idle_add (load_emit_signal_done, img);

            return NULL;
      }

      /* determine file size */
      /* FIXME: we should reuse the values gained in eog_image_load here */
      info = gnome_vfs_file_info_new ();
      result = gnome_vfs_get_file_info_uri (priv->uri,
                                    info,
                                    GNOME_VFS_FILE_INFO_DEFAULT);
      if ((result != GNOME_VFS_OK) || (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE) == 0) {
            g_mutex_lock (priv->status_mutex);
            priv->error_message = (char*) gnome_vfs_result_to_string (result);
            priv->status = EOG_IMAGE_STATUS_FAILED;
            g_mutex_unlock (priv->status_mutex);

            gnome_vfs_file_info_unref (info);

            g_idle_add (load_emit_signal_done, img);

            return NULL;
      }
      bytes_read_total = 0;
      priv->bytes = info->size;
      
      buffer = g_new0 (guchar, READ_BUFFER_SIZE);
      loader = gdk_pixbuf_loader_new ();
      failed = FALSE;

      g_signal_connect_object (G_OBJECT (loader), "size-prepared", (GCallback) load_size_prepared, img, 0);

      while (!priv->cancel_loading) {
              GnomeVFSFileSize last_progress_bytes = 0;
            
            result = gnome_vfs_read (handle, buffer, READ_BUFFER_SIZE, &bytes_read);
            if (result == GNOME_VFS_ERROR_EOF || bytes_read == 0) {
                  break;
            }
            else if (result != GNOME_VFS_OK) {
                  failed = TRUE;
                  break;
            }
            
            if (!gdk_pixbuf_loader_write (loader, buffer, bytes_read, &error)) {
                  failed = TRUE;
                  break;
            }

            bytes_read_total += bytes_read;
            if ((bytes_read_total - last_progress_bytes) > 250000) { /* update progress each ~250kb */
                  priv->progress = (float) bytes_read_total / (float) info->size;
                  last_progress_bytes = bytes_read_total;
                  g_idle_add (load_emit_signal_progress, img);
            }
            
            /* check if we support reading metadata for that image format (only JPG atm) */
            if (first_run) {
                  md_reader = check_for_metadata_img_format (img, buffer, bytes_read);
                  first_run = FALSE;
            }
            
            if (md_reader != NULL) {
                  eog_metadata_reader_consume (md_reader, buffer, bytes_read);
            }
      }

      gdk_pixbuf_loader_close (loader, NULL);   
      
      g_free (buffer);
      gnome_vfs_close (handle);
      gnome_vfs_file_info_unref (info);
      
      if (failed || (bytes_read_total == 0)) {
            g_mutex_lock (priv->status_mutex);
            if (error != NULL) {
                  priv->error_message = g_strdup (error->message);
            }
            else if (bytes_read_total == 0) {
                  priv->error_message = g_strdup (_("empty file"));
            }
            else {
                  priv->error_message = NULL;
            }
            priv->status = EOG_IMAGE_STATUS_FAILED;
            g_mutex_unlock (priv->status_mutex);
      }
      else if (priv->cancel_loading) {
            g_mutex_lock (priv->status_mutex);
            priv->cancel_loading = FALSE;
            priv->status = EOG_IMAGE_STATUS_UNKNOWN;
            g_mutex_unlock (priv->status_mutex);
      }
      else {
            GdkPixbuf *image;
            GdkPixbuf *transformed = NULL;
            EogTransform *trans;
            GdkPixbufFormat *format;

            g_mutex_lock (priv->status_mutex);
            image = priv->image;
            trans = priv->trans;
            g_mutex_unlock (priv->status_mutex);
            
            if (image == NULL) {
                  image = gdk_pixbuf_loader_get_pixbuf (loader);
                  g_object_ref (image);
            }
            g_assert (image != NULL);
            
            if (trans != NULL) {
                  transformed = eog_transform_apply (trans, image, transform_progress_hook_async, img);

                  g_object_unref (image);
                  image = transformed;
            }

            g_mutex_lock (priv->status_mutex);
            priv->progress = 1.0;
            priv->image = image;
            priv->width = gdk_pixbuf_get_width (priv->image);
            priv->height = gdk_pixbuf_get_height (priv->image);
            format = gdk_pixbuf_loader_get_format (loader);
            if (format != NULL) {
                  priv->file_type = g_strdup (gdk_pixbuf_format_get_name (format));
            }
                  
            if (md_reader != NULL) {
#if HAVE_EXIF     
                  priv->exif = eog_metadata_reader_get_exif_data (md_reader);
                  priv->exif_chunk = NULL;
                  priv->exif_chunk_len = 0;
                  update_exif_data (img);
#else
                  eog_metadata_reader_get_exif_chunk (md_reader, &priv->exif_chunk, &priv->exif_chunk_len);
#endif
            }
            eog_image_cache_add (img);

            priv->status = EOG_IMAGE_STATUS_LOADED;
            g_mutex_unlock (priv->status_mutex);
            
      }

      g_idle_add (load_emit_signal_done, img);

      if (error != NULL) {
            g_error_free (error);
      }
      g_object_unref (loader);
      if (md_reader != NULL) {
            g_object_unref (md_reader);
            md_reader = NULL;
      }     

      g_mutex_lock (priv->status_mutex);
      priv->load_thread = NULL;

      if (priv->load_finished != NULL) {
            g_cond_broadcast (priv->load_finished);
      }
      g_mutex_unlock (priv->status_mutex);

      return NULL;
}

void
eog_image_load (EogImage *img, EogImageLoadMode mode)
{
      EogImagePrivate *priv;

      g_return_if_fail (EOG_IS_IMAGE (img));

      priv = EOG_IMAGE (img)->priv;

      if (priv->status == EOG_IMAGE_STATUS_LOADED) {
            g_assert (priv->image != NULL);
            eog_image_cache_reload (img);
            g_signal_emit (G_OBJECT (img), eog_image_signals [SIGNAL_LOADING_FINISHED], 0);
      }
      else if (priv->status == EOG_IMAGE_STATUS_FAILED) {
            g_signal_emit (G_OBJECT (img), eog_image_signals [SIGNAL_LOADING_FAILED], 0, "");         
      }
      else if (priv->status == EOG_IMAGE_STATUS_UNKNOWN) { 
            g_assert (priv->image == NULL);
            
            /* make sure the object isn't destroyed while we try to load it */
            g_object_ref (img);

            /* initialize data fields for progressive loading */
            priv->error_message = NULL;
            priv->cancel_loading = FALSE;
            priv->mode = EOG_IMAGE_LOAD_COMPLETE;
            
#if 0
            priv->mode = mode;

            if (priv->mode == EOG_IMAGE_LOAD_DEFAULT) {
                  /* determine if the image should be loaded progressively or not */
                  if (gnome_vfs_uri_is_local (priv->uri)) {
                        GnomeVFSFileInfo *info;
                        GnomeVFSResult result;
                        info = gnome_vfs_file_info_new ();
                        
                        result = gnome_vfs_get_file_info_uri (priv->uri,
                                                      info,
                                                      GNOME_VFS_FILE_INFO_DEFAULT);

                        if (result != GNOME_VFS_OK) {
                              g_signal_emit (G_OBJECT (img), eog_image_signals [SIGNAL_LOADING_FAILED], 
                                           0, gnome_vfs_result_to_string (result));
                              g_print ("VFS Error: %s\n", gnome_vfs_result_to_string (result));
                              return;
                        }

                        priv->mode = EOG_IMAGE_LOAD_PROGRESSIVE;
                        if (((info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE) != 0) && 
                            (info->size < 1000000))
                        {
                              priv->mode = EOG_IMAGE_LOAD_COMPLETE;
                        }

                        gnome_vfs_file_info_unref (info);
                  }
                  else {
                        priv->mode = EOG_IMAGE_LOAD_PROGRESSIVE;
                        
                  }
            }
#endif

            /* start the thread machinery */
            priv->status      = EOG_IMAGE_STATUS_LOADING;
            priv->load_thread = g_thread_create (real_image_load, img, TRUE, NULL);
      }
}

gboolean
eog_image_load_sync (EogImage *img, EogImageLoadMode mode)
{
      EogImagePrivate *priv;
      gboolean success = FALSE; 

      g_return_val_if_fail (EOG_IS_IMAGE (img), FALSE);

      priv = img->priv;

      if (priv->status == EOG_IMAGE_STATUS_LOADED) {
            g_assert (priv->image != NULL);
            eog_image_cache_reload (img);
            success = TRUE;
      }
      else if (priv->status == EOG_IMAGE_STATUS_FAILED) {
            success = FALSE;
      }
      else if (priv->status == EOG_IMAGE_STATUS_UNKNOWN) { 

            g_mutex_lock (priv->status_mutex);

            priv->load_finished = g_cond_new ();
            
            eog_image_load (img, mode);
            g_cond_wait (priv->load_finished, priv->status_mutex);
            
            g_cond_free (priv->load_finished);
            priv->load_finished = NULL;

            success = (priv->status == EOG_IMAGE_STATUS_LOADED);
      
            g_mutex_unlock (priv->status_mutex);
      }


      if (success) {
            g_assert (priv->file_type != NULL);
      }

      return success;
}

gboolean 
eog_image_load_thumbnail (EogImage *img)
{
      EogImagePrivate *priv;

      g_return_val_if_fail (EOG_IS_IMAGE (img), FALSE);

      priv = img->priv;

      if (priv->thumbnail == NULL)
      {
            add_image_to_queue (img);
      }
      
      return (priv->thumbnail != NULL);
}

gboolean 
eog_image_is_animation (EogImage *img)
{
      return FALSE;
}

GdkPixbuf* 
eog_image_get_pixbuf (EogImage *img)
{
      GdkPixbuf *image = NULL;

      g_return_val_if_fail (EOG_IS_IMAGE (img), NULL);

      g_mutex_lock (img->priv->status_mutex);
      image = img->priv->image;
      g_mutex_unlock (img->priv->status_mutex);

      if (image != NULL) {
            g_object_ref (image);
      }
      
      return image;
}

GdkPixbuf* 
eog_image_get_pixbuf_thumbnail (EogImage *img)
{
      g_return_val_if_fail (EOG_IS_IMAGE (img), NULL);

      if (img->priv->thumbnail != 0) {
            g_object_ref (img->priv->thumbnail);
            return img->priv->thumbnail;
      }

      return NULL;
}

void 
eog_image_get_size (EogImage *img, int *width, int *height)
{
      EogImagePrivate *priv;

      g_return_if_fail (EOG_IS_IMAGE (img));

      priv = img->priv;

      *width = priv->width; 
      *height = priv->height;
}

static gboolean
check_progress_sync (gpointer data)
{
      g_signal_emit (G_OBJECT (data), eog_image_signals [SIGNAL_PROGRESS], 0, EOG_IMAGE (data)->priv->progress);
      
      return TRUE;
}

static void
transform_progress_hook_sync (EogTransform *trans, float progress, gpointer hook_data)
{
      EogImagePrivate *priv;

      priv = EOG_IMAGE (hook_data)->priv;

      if (progress != priv->progress) {
            
            priv->progress = progress;

            if (gtk_events_pending ()) {
                  gtk_main_iteration ();
            }
      }
}

static void
image_transform (EogImage *img, EogTransform *trans, gboolean is_undo)
{
      EogImagePrivate *priv;
      GdkPixbuf *transformed;
      gboolean modified = FALSE;

      g_return_if_fail (EOG_IS_IMAGE (img));
      g_return_if_fail (EOG_IS_TRANSFORM (trans));

      priv = img->priv;

      if (priv->image != NULL) {
            transformed = eog_transform_apply (trans, priv->image, transform_progress_hook_sync, img);
            
            g_object_unref (priv->image);
            priv->image = transformed;
            priv->width = gdk_pixbuf_get_width (transformed);
            priv->height = gdk_pixbuf_get_height (transformed);
       
            modified = TRUE;
      }

      if (priv->thumbnail != NULL) {
            transformed = eog_transform_apply (trans, priv->thumbnail, transform_progress_hook_sync, img);

            g_object_unref (priv->thumbnail);
            priv->thumbnail = transformed;
       
            modified = TRUE;
      }

      if (modified) {
            priv->modified = TRUE;
#if HAVE_EXIF
            update_exif_data (img);
#endif 
            g_signal_emit (G_OBJECT (img), eog_image_signals [SIGNAL_IMAGE_CHANGED], 0);
      }

      if (priv->trans == NULL) {
            g_object_ref (trans);
            priv->trans = trans;
      }
      else {
            EogTransform *composition;

            composition = eog_transform_compose (priv->trans, trans);

            g_object_unref (priv->trans);
            priv->trans = composition;
      }
      
      if (!is_undo) {
            g_object_ref (trans);
            priv->undo_stack = g_list_prepend (priv->undo_stack, trans);
      }
}


void                
eog_image_transform (EogImage *img, EogTransform *trans)
{
      gint signal_id;

      signal_id = g_timeout_add (CHECK_LOAD_TIMEOUT, check_progress_sync, img);

      image_transform (img, trans, FALSE);

      g_source_remove (signal_id);

      g_signal_emit (G_OBJECT (img), eog_image_signals [SIGNAL_PROGRESS], 0, 1.0);
}

void 
eog_image_undo (EogImage *img)
{
      EogImagePrivate *priv;
      EogTransform *trans;
      EogTransform *inverse;

      g_return_if_fail (EOG_IS_IMAGE (img));

      priv = img->priv;

      if (priv->undo_stack != NULL) {
            trans = EOG_TRANSFORM (priv->undo_stack->data);

            inverse = eog_transform_reverse (trans);

            image_transform (img, inverse, TRUE);

            priv->undo_stack = g_list_delete_link (priv->undo_stack, priv->undo_stack);
            g_object_unref (trans);
            g_object_unref (inverse);

            if (eog_transform_is_identity (priv->trans)) {
                  g_object_unref (priv->trans);
                  priv->trans = NULL;
            }
      }

      priv->modified = (priv->undo_stack != NULL);
}

static char*
tmp_file_get_path (void)
{
      char *tmp_file;
      int fd;

      tmp_file = g_build_filename (g_get_tmp_dir (), "eog-save-XXXXXX", NULL);
      fd = g_mkstemp (tmp_file);
      if (fd == -1) {
            /* error case */
            g_free (tmp_file);
            tmp_file = NULL;
      }
      else {
            close (fd);
      }

      return tmp_file;
}

static gboolean
tmp_file_move_to_uri (const char* tmpfile, const GnomeVFSURI *uri, gboolean overwrite, GError **error)
{
      GnomeVFSResult result;
      GnomeVFSURI *source_uri;
      GnomeVFSFileInfo *info;
      GnomeVFSXferOverwriteMode overwrt_mode = GNOME_VFS_XFER_OVERWRITE_MODE_ABORT;

      if (!overwrite && gnome_vfs_uri_exists ((GnomeVFSURI*) uri)) 
      {
            /* explicit check if uri exists, seems that gnome_vfs_xfer_uri, doesn't
             *  work as expected 
             */
            g_set_error (error, EOG_IMAGE_ERROR,
                       EOG_IMAGE_ERROR_FILE_EXISTS,
                       _("File exists"));
            return FALSE;
      }

      info = gnome_vfs_file_info_new ();
      result = gnome_vfs_get_file_info_uri ((GnomeVFSURI*) uri, info, GNOME_VFS_FILE_INFO_DEFAULT);
      if (result != GNOME_VFS_OK) {
            /* we don't propagate the error here, because if we get a 
             * fatal error, the xfer_uri will fail too and then
             * handled.
             */
            gnome_vfs_file_info_unref (info);
            info = NULL;
      }

      if (overwrite == TRUE) {
            overwrt_mode = GNOME_VFS_XFER_OVERWRITE_MODE_REPLACE;
      }
      source_uri = gnome_vfs_uri_new (tmpfile);

      result = gnome_vfs_xfer_uri (source_uri,
                             uri, 
                             GNOME_VFS_XFER_DELETE_ITEMS,           /* delete source file */
                             GNOME_VFS_XFER_ERROR_MODE_ABORT,       /* abort on all errors */
                             overwrt_mode,
                             NULL,                                  /* no progress callback */
                             NULL);

      gnome_vfs_uri_unref (source_uri);

      if (result == GNOME_VFS_ERROR_FILE_EXISTS) {
            g_set_error (error, EOG_IMAGE_ERROR,
                       EOG_IMAGE_ERROR_FILE_EXISTS,
                       gnome_vfs_result_to_string (result));
      }
      else if (result != GNOME_VFS_OK) {
            g_set_error (error, EOG_IMAGE_ERROR,
                       EOG_IMAGE_ERROR_VFS, 
                       gnome_vfs_result_to_string (result));
      }
      else if (info != NULL) {
            /* reset file permissions/owner to the original ones */
            GnomeVFSSetFileInfoMask mask = 
                  GNOME_VFS_SET_FILE_INFO_PERMISSIONS | GNOME_VFS_SET_FILE_INFO_OWNER;
            gnome_vfs_set_file_info_uri ((GnomeVFSURI*) uri, info, mask);
      }

      if (info != NULL) {
            gnome_vfs_file_info_unref (info);
      }

      return (result == GNOME_VFS_OK);
}

static gboolean
tmp_file_delete (char *tmpfile)
{
      if (tmpfile == NULL) return FALSE;

      if (g_file_test (tmpfile, G_FILE_TEST_EXISTS)) {
            int result;

            result = unlink (tmpfile);
            if (result == -1) {
                  g_warning ("Couldn't delete temporary file: %s\n", tmpfile);
                  return FALSE;
            }
      }

      return TRUE;
}

static void 
eog_image_reset_modifications (EogImage *image)
{
      EogImagePrivate *priv;
      GList *it = NULL;

      g_return_if_fail (EOG_IS_IMAGE (image));

      priv = image->priv;

      /* free the undo stack */
      for (it = priv->undo_stack; it != NULL; it = it->next) {
            g_object_unref (G_OBJECT (it->data));
      }
      g_list_free (priv->undo_stack);
      priv->undo_stack = NULL;

      /* free accumulated transform object */
      if (priv->trans != NULL) {
            g_object_unref (priv->trans);
            priv->trans = NULL;
      }
      priv->modified = FALSE;
}

static void
eog_image_link_with_target (EogImage *image, EogImageSaveInfo *target)
{
      EogImagePrivate *priv;

      g_return_if_fail (EOG_IS_IMAGE (image));
      g_return_if_fail (EOG_IS_IMAGE_SAVE_INFO (target));

      priv = image->priv;

      /* update file location */
      if (priv->uri != NULL) {
            gnome_vfs_uri_unref (priv->uri);
      }
      priv->uri = gnome_vfs_uri_ref (target->uri);

      /* Clear caption and caption key, these will be 
       * updated on next eog_image_get_caption call.
       */
      if (priv->caption != NULL) {
            g_free (priv->caption);
            priv->caption = NULL;
      }
      if (priv->caption_key != NULL) {
            g_free (priv->caption_key);
            priv->caption_key = NULL;
      }

      /* update file format */
      if (priv->file_type != NULL) {
            g_free (priv->file_type);
      }
      priv->file_type = g_strdup (target->format);
}

gboolean
eog_image_save_by_info (EogImage *img, EogImageSaveInfo *source, GError **error) 
{
      EogImagePrivate *priv;
      gboolean success = FALSE;
      char *tmpfile;

      g_return_val_if_fail (EOG_IS_IMAGE (img), FALSE);
      g_return_val_if_fail (EOG_IS_IMAGE_SAVE_INFO (source), FALSE);

      priv = img->priv;

      /* see if we need any saving at all */
      if (source->exists && !source->modified) {
            return TRUE;
      }

      /* fail if there is no image to save */
      if (priv->image == NULL) {
            g_set_error (error, EOG_IMAGE_ERROR,
                       EOG_IMAGE_ERROR_NOT_LOADED,
                       _("No image loaded."));
            return FALSE;
      }

      /* generate temporary file name */
      tmpfile = tmp_file_get_path ();
      if (tmpfile == NULL) {
            g_set_error (error, EOG_IMAGE_ERROR,
                       EOG_IMAGE_ERROR_TMP_FILE_FAILED,
                       _("Temporary file creation failed."));
            return FALSE;
      }
      
      /* determine kind of saving */
      if ((g_ascii_strcasecmp (source->format, EOG_FILE_FORMAT_JPEG) == 0) && 
          source->exists && source->modified) 
      {
            success = eog_image_jpeg_save_file (img, tmpfile, source, NULL, error);
      }

      if (!success && (*error == NULL)) {
            success = gdk_pixbuf_save (priv->image, tmpfile, source->format, error, NULL);
      }

      if (success) {
            /* try to move result file to target uri */
            success = tmp_file_move_to_uri (tmpfile, priv->uri, TRUE /*overwrite*/, error);
      }

      if (success) {
            eog_image_reset_modifications (img);
      }

      tmp_file_delete (tmpfile);

      g_free (tmpfile);

      return success;
}

static gboolean
eog_image_copy_file (EogImageSaveInfo *source, EogImageSaveInfo *target, GError **error)
{
      GnomeVFSResult    result;
      GnomeVFSFileInfo *info;
      GnomeVFSXferOverwriteMode overwrt_mode = GNOME_VFS_XFER_OVERWRITE_MODE_ABORT;

      g_return_val_if_fail (EOG_IS_IMAGE_SAVE_INFO (source), FALSE);
      g_return_val_if_fail (EOG_IS_IMAGE_SAVE_INFO (target), FALSE);

      if (target->overwrite != TRUE && 
          gnome_vfs_uri_exists (target->uri)) 
      {
            /* explicit check if uri exists, seems that gnome_vfs_xfer_uri, doesn't
             *  work as expected 
             */
            g_set_error (error, EOG_IMAGE_ERROR,
                       EOG_IMAGE_ERROR_FILE_EXISTS,
                       _("File exists"));
            return FALSE;
      }
      else if (target->overwrite == TRUE) {
            overwrt_mode = GNOME_VFS_XFER_OVERWRITE_MODE_REPLACE;
      }

      info = gnome_vfs_file_info_new ();
      result = gnome_vfs_get_file_info_uri ((GnomeVFSURI*) target->uri, info, GNOME_VFS_FILE_INFO_DEFAULT);
      if (result != GNOME_VFS_OK) {
            /* we don't propagate the error here, because if we get a 
             * fatal error, the xfer_uri will fail too and then
             * handled.
             */
            gnome_vfs_file_info_unref (info);
            info = NULL;
      }

      result = gnome_vfs_xfer_uri (source->uri,
                             target->uri, 
                             GNOME_VFS_XFER_DEFAULT,            /* copy the data */
                             GNOME_VFS_XFER_ERROR_MODE_ABORT,   /* abort on all errors */
                             overwrt_mode,
                             NULL,                              /* no progress callback */
                             NULL);

      if (result == GNOME_VFS_ERROR_FILE_EXISTS) {
            g_set_error (error, EOG_IMAGE_ERROR,
                       EOG_IMAGE_ERROR_FILE_EXISTS,
                       gnome_vfs_result_to_string (result));
      }
      else if (result != GNOME_VFS_OK) {
            g_set_error (error, EOG_IMAGE_ERROR,
                       EOG_IMAGE_ERROR_VFS, 
                       gnome_vfs_result_to_string (result));
      }
      else if (info != NULL) {
            /* reset file permissions/owner to the original ones */
            GnomeVFSSetFileInfoMask mask = 
                  GNOME_VFS_SET_FILE_INFO_PERMISSIONS | GNOME_VFS_SET_FILE_INFO_OWNER;
            gnome_vfs_set_file_info_uri (target->uri, info, mask);
      }

      if (info != NULL) {
            gnome_vfs_file_info_unref (info);
      }

      return (result == GNOME_VFS_OK);
}

gboolean
eog_image_save_as_by_info (EogImage *img, EogImageSaveInfo *source, EogImageSaveInfo *target, GError **error)
{
      EogImagePrivate *priv;
      gboolean success = FALSE;
      char *tmpfile;
      gboolean direct_copy = FALSE;

      g_return_val_if_fail (EOG_IS_IMAGE (img), FALSE);
      g_return_val_if_fail (EOG_IS_IMAGE_SAVE_INFO (source), FALSE);
      g_return_val_if_fail (EOG_IS_IMAGE_SAVE_INFO (target), FALSE);

      priv = img->priv;

      /* fail if there is no image to save */
      if (priv->image == NULL) {
            g_set_error (error, EOG_IMAGE_ERROR,
                       EOG_IMAGE_ERROR_NOT_LOADED,
                       _("No image loaded."));
            return FALSE;
      }

      /* generate temporary file name */
      tmpfile = tmp_file_get_path ();
      if (tmpfile == NULL) {
            g_set_error (error, EOG_IMAGE_ERROR,
                       EOG_IMAGE_ERROR_TMP_FILE_FAILED,
                       _("Temporary file creation failed."));
            return FALSE;
      }
      
      /* determine kind of saving */
      if (g_ascii_strcasecmp (source->format, target->format) == 0 && !source->modified) {
            success = eog_image_copy_file (source, target, error);
            direct_copy = success;
      }
      else if ((g_ascii_strcasecmp (source->format, EOG_FILE_FORMAT_JPEG) == 0 && source->exists) ||
             (g_ascii_strcasecmp (target->format, EOG_FILE_FORMAT_JPEG) == 0))
      {
            success = eog_image_jpeg_save_file (img, tmpfile, source, target, error);
      }

      if (!success && (*error == NULL)) {
            success = gdk_pixbuf_save (priv->image, tmpfile, target->format, error, NULL);
      }

      if (success && !direct_copy) { /* not required if we alredy copied the file directly */
            /* try to move result file to target uri */
            success = tmp_file_move_to_uri (tmpfile, target->uri, target->overwrite, error);
      }

      if (success) {
            /* update image information to new uri */
            eog_image_reset_modifications (img);
            eog_image_link_with_target (img, target);
      }

      tmp_file_delete (tmpfile);
      g_free (tmpfile);

      return success;
}

/* only for compability reasons, use eog_image_save[_as]_by_info. */
gboolean
eog_image_save (EogImage *img, GnomeVFSURI *uri, GdkPixbufFormat *format, GError **error)
{
      EogImageSaveInfo *source = NULL;
      EogImageSaveInfo *target = NULL;
      gboolean success = FALSE;

      g_return_val_if_fail (EOG_IS_IMAGE (img), FALSE);

      source = eog_image_save_info_from_image (img);

      if (!gnome_vfs_uri_equal (img->priv->uri, uri)) {
            char *txt_uri = gnome_vfs_uri_to_string (uri, GNOME_VFS_URI_HIDE_NONE);
            target = eog_image_save_info_from_uri (txt_uri, format);
            g_free (txt_uri);
      }

      if (source != NULL && target != NULL) {
            success = eog_image_save_as_by_info (img, source, target, error);
      }
      else if (source != NULL) {
            success = eog_image_save_by_info (img, source, error);
      }

      return success;
}


/*
 * This function is extracted from 
 * File: nautilus/libnautilus-private/nautilus-file.c
 * Revision: 1.309
 * Author: Darin Adler <darin@bentspoon.com>
 */
static gboolean
have_broken_filenames (void)
{
      static gboolean initialized = FALSE;
      static gboolean broken;
      
      if (initialized) {
            return broken;
      }
      
      broken = g_getenv ("G_BROKEN_FILENAMES") != NULL;
  
      initialized = TRUE;
  
      return broken;
}

/* 
 * This function is inspired by
 * nautilus/libnautilus-private/nautilus-file.c:nautilus_file_get_display_name_nocopy
 * Revision: 1.309
 * Author: Darin Adler <darin@bentspoon.com>
 */
gchar*               
eog_image_get_caption (EogImage *img)
{
      EogImagePrivate *priv;
      char *name;
      char *utf8_name;
      gboolean validated = FALSE;
      gboolean broken_filenames;

      g_return_val_if_fail (EOG_IS_IMAGE (img), NULL);

      priv = img->priv;

      if (priv->uri == NULL) return NULL;

      if (priv->caption != NULL) 
            /* Use cached caption string */
            return priv->caption;

      name = gnome_vfs_uri_extract_short_name (priv->uri);
      
      if (name != NULL && gnome_vfs_uri_is_local (priv->uri)) {
            /* Support the G_BROKEN_FILENAMES feature of
             * glib by using g_filename_to_utf8 to convert
             * local filenames to UTF-8. Also do the same
             * thing with any local filename that does not
             * validate as good UTF-8.
             */
            broken_filenames = have_broken_filenames ();
            if (broken_filenames || !g_utf8_validate (name, -1, NULL)) {
                  utf8_name = g_locale_to_utf8 (name, -1, NULL, NULL, NULL);
                  if (utf8_name != NULL) {
                        g_free (name);
                        name = utf8_name;
                        /* Guaranteed to be correct utf8 here */
                        validated = TRUE;
                  }
            } 
            else if (!broken_filenames) {
                  /* name was valid, no need to re-validate */
                  validated = TRUE;
            }
      }
      
      if (!validated && !g_utf8_validate (name, -1, NULL)) {
            if (name == NULL) {
                  name = g_strdup ("[Invalid Unicode]");
            }
            else {
                  utf8_name = eog_util_make_valid_utf8 (name);
                  g_free (name);
                  name = utf8_name;
            }
      }

      priv->caption = name;

      if (priv->caption == NULL) {
            char *short_str;

            short_str = gnome_vfs_uri_extract_short_name (priv->uri);
            if (g_utf8_validate (short_str, -1, NULL)) {
                  priv->caption = g_strdup (short_str);
            }
            else {
                  priv->caption = g_filename_to_utf8 (short_str, -1, NULL, NULL, NULL);
            }
            g_free (short_str);
      }
      
      return priv->caption;
}

void 
eog_image_free_mem_private (EogImage *image)
{
      EogImagePrivate *priv;
      
      priv = image->priv;

      if (priv->status == EOG_IMAGE_STATUS_LOADING) {
            eog_image_cancel_load (image);
      }
      else {
            if (priv->image != NULL) {
                  gdk_pixbuf_unref (priv->image);
                  priv->image = NULL;
            }
            
#if HAVE_EXIF
            if (priv->exif != NULL) {
                  exif_data_unref (priv->exif);
                  priv->exif = NULL;
            }
#endif
            
            if (priv->exif_chunk != NULL) {
                  g_free (priv->exif_chunk);
                  priv->exif_chunk = NULL;
            }
            priv->exif_chunk_len = 0;

            priv->status = EOG_IMAGE_STATUS_UNKNOWN;
      }
}

void
eog_image_free_mem (EogImage *image)
{
      g_return_if_fail (EOG_IS_IMAGE (image));

      if (image->priv->image != NULL) {
            eog_image_cache_remove (image);
            eog_image_free_mem_private (image);
      }
}


const gchar*        
eog_image_get_collate_key (EogImage *img)
{
      EogImagePrivate *priv;

      g_return_val_if_fail (EOG_IS_IMAGE (img), NULL);
      
      priv = img->priv;

      if (priv->caption_key == NULL) {
            char *caption;

            caption = eog_image_get_caption (img);
            priv->caption_key = g_utf8_collate_key (caption, -1);
      }

      return priv->caption_key;
}

void
eog_image_cancel_load (EogImage *img)
{
      EogImagePrivate *priv;

      g_return_if_fail (EOG_IS_IMAGE (img));
      
      priv = img->priv;

      g_mutex_lock (priv->status_mutex);
      if (priv->status == EOG_IMAGE_STATUS_LOADING) {
            priv->cancel_loading = TRUE;
      }
      g_mutex_unlock (priv->status_mutex);
}

gboolean 
eog_image_has_metadata (EogImage *img)
{
      EogImagePrivate *priv;
      gboolean has_metadata;

      g_return_val_if_fail (EOG_IS_IMAGE (img), FALSE);

      priv = img->priv;

      has_metadata = ((priv->exif_chunk != NULL) || (priv->iptc_chunk != NULL));

#if HAVE_EXIF
      has_metadata |= (priv->exif != NULL);
#endif      

      return has_metadata;
}

gpointer
eog_image_get_exif_information (EogImage *img)
{
      EogImagePrivate *priv;
      gpointer data = NULL;
      
      g_return_val_if_fail (EOG_IS_IMAGE (img), NULL);
      
      priv = img->priv;

#if HAVE_EXIF
      g_mutex_lock (priv->status_mutex);
      exif_data_ref (priv->exif);
      data = priv->exif;
      g_mutex_unlock (priv->status_mutex);
#endif

      return data;
}

gboolean            
eog_image_is_loaded (EogImage *img)
{
      EogImagePrivate *priv;
      gboolean result;

      g_return_val_if_fail (EOG_IS_IMAGE (img), FALSE);

      priv = img->priv;

      g_mutex_lock (priv->status_mutex);
      result = (priv->status == EOG_IMAGE_STATUS_LOADED);
      g_mutex_unlock (priv->status_mutex);
      
      return result;
}

GnomeVFSURI*
eog_image_get_uri (EogImage *img)
{
      g_return_val_if_fail (EOG_IS_IMAGE (img), NULL);
      
      return gnome_vfs_uri_ref (img->priv->uri);
}

gboolean
eog_image_is_modified (EogImage *img)
{
      g_return_val_if_fail (EOG_IS_IMAGE (img), FALSE);
      
      return img->priv->modified;
}

GnomeVFSFileSize
eog_image_get_bytes (EogImage *img)
{
      g_return_val_if_fail (EOG_IS_IMAGE (img), 0);
      
      return img->priv->bytes;
}

void
eog_image_modified (EogImage *img)
{
      g_return_if_fail (EOG_IS_IMAGE (img));

      g_signal_emit (G_OBJECT (img), eog_image_signals [SIGNAL_IMAGE_CHANGED], 0);
}

gchar*
eog_image_get_uri_for_display (EogImage *img)
{
      EogImagePrivate *priv;
      gchar *uri_str = NULL;
      gchar *str = NULL;

      g_return_val_if_fail (EOG_IS_IMAGE (img), NULL);
      
      priv = img->priv;

      if (priv->uri != NULL) {
            uri_str = gnome_vfs_uri_to_string (priv->uri, GNOME_VFS_URI_HIDE_NONE);
            if (uri_str != NULL) {
                  str = gnome_vfs_format_uri_for_display (uri_str);
                  g_free (uri_str);
            }
      }

      return str;
}

Generated by  Doxygen 1.6.0   Back to index