file-open.c 17.9 KB
Newer Older
1
/* GIMP - The GNU Image Manipulation Program
Michael Natterer's avatar
Michael Natterer committed
2 3 4
 * Copyright (C) 1995, 1996, 1997 Spencer Kimball and Peter Mattis
 * Copyright (C) 1997 Josh MacDonald
 *
5 6
 * file-open.c
 *
Michael Natterer's avatar
Michael Natterer committed
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 * 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 Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include "config.h"

Michael Natterer's avatar
Michael Natterer committed
24
#include <errno.h>
Michael Natterer's avatar
Michael Natterer committed
25 26
#include <stdlib.h>
#include <string.h>
27

Michael Natterer's avatar
Michael Natterer committed
28 29 30
#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
31

Michael Natterer's avatar
Michael Natterer committed
32 33 34 35
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

36
#include <glib-object.h>
37
#include <glib/gstdio.h>
38

39 40 41 42 43
#ifdef G_OS_WIN32
#include <io.h>
#define R_OK 4
#endif

Michael Natterer's avatar
Michael Natterer committed
44
#include "core/core-types.h"
Michael Natterer's avatar
Michael Natterer committed
45

46 47
#include "config/gimpcoreconfig.h"

Michael Natterer's avatar
Michael Natterer committed
48
#include "core/gimp.h"
49
#include "core/gimpcontext.h"
50
#include "core/gimpdocumentlist.h"
51
#include "core/gimpimage.h"
52
#include "core/gimpimage-merge.h"
53
#include "core/gimpimage-undo.h"
54
#include "core/gimpimagefile.h"
55
#include "core/gimplayer.h"
56
#include "core/gimpparamspecs.h"
57
#include "core/gimpprogress.h"
58

59
#include "pdb/gimppdb.h"
Michael Natterer's avatar
Michael Natterer committed
60

61
#include "plug-in/gimppluginmanager.h"
62
#include "plug-in/gimppluginprocedure.h"
63 64
#include "plug-in/plug-in-icc-profile.h"

65

Michael Natterer's avatar
Michael Natterer committed
66 67
#include "file-open.h"
#include "file-utils.h"
68
#include "gimprecentlist.h"
Michael Natterer's avatar
Michael Natterer committed
69

70
#include "gimp-intl.h"
Michael Natterer's avatar
Michael Natterer committed
71 72


73 74
static void  file_open_sanitize_image       (GimpImage    *image,
                                             gboolean      as_new);
75 76 77 78
static void  file_open_handle_color_profile (GimpImage    *image,
                                             GimpContext  *context,
                                             GimpProgress *progress,
                                             GimpRunMode   run_mode);
79 80


Michael Natterer's avatar
Michael Natterer committed
81 82
/*  public functions  */

83
GimpImage *
84 85 86
file_open_image (Gimp                *gimp,
                 GimpContext         *context,
                 GimpProgress        *progress,
87 88
                 const gchar         *uri,
                 const gchar         *entered_filename,
89
                 gboolean             as_new,
90 91 92
                 GimpPlugInProcedure *file_proc,
                 GimpRunMode          run_mode,
                 GimpPDBStatusType   *status,
93 94
                 const gchar        **mime_type,
                 GError             **error)
Michael Natterer's avatar
Michael Natterer committed
95
{
96 97 98
  GValueArray *return_vals;
  gchar       *filename;
  GimpImage   *image = NULL;
Michael Natterer's avatar
Michael Natterer committed
99

Michael Natterer's avatar
Michael Natterer committed
100
  g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL);
101
  g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
102
  g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), NULL);
Michael Natterer's avatar
Michael Natterer committed
103
  g_return_val_if_fail (status != NULL, NULL);
104
  g_return_val_if_fail (error == NULL || *error == NULL, NULL);
Michael Natterer's avatar
Michael Natterer committed
105

106
  *status = GIMP_PDB_EXECUTION_ERROR;
Michael Natterer's avatar
Michael Natterer committed
107

108
  if (! file_proc)
109 110
    file_proc = file_utils_find_proc (gimp->plug_in_manager->load_procs,
                                      uri, error);
Michael Natterer's avatar
Michael Natterer committed
111

112
  if (! file_proc)
113
    return NULL;
Michael Natterer's avatar
Michael Natterer committed
114

115
  filename = file_utils_filename_from_uri (uri);
116 117

  if (filename)
Michael Natterer's avatar
Michael Natterer committed
118
    {
119
      /* check if we are opening a file */
120
      if (g_file_test (filename, G_FILE_TEST_EXISTS))
121
        {
122
          if (! g_file_test (filename, G_FILE_TEST_IS_REGULAR))
123
            {
124
              g_free (filename);
125 126
              g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
                           _("Not a regular file"));
127 128 129
              return NULL;
            }

130
          if (g_access (filename, R_OK) != 0)
131
            {
132
              g_free (filename);
133 134
              g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_ACCES,
                           g_strerror (errno));
135 136 137
              return NULL;
            }
        }
Michael Natterer's avatar
Michael Natterer committed
138
    }
139 140 141 142
  else
    {
      filename = g_strdup (uri);
    }
Michael Natterer's avatar
Michael Natterer committed
143

144 145 146 147 148 149 150 151
  return_vals =
    gimp_pdb_execute_procedure_by_name (gimp->pdb,
                                        context, progress,
                                        GIMP_OBJECT (file_proc)->name,
                                        GIMP_TYPE_INT32, run_mode,
                                        G_TYPE_STRING,   filename,
                                        G_TYPE_STRING,   entered_filename,
                                        G_TYPE_NONE);
Michael Natterer's avatar
Michael Natterer committed
152

153
  g_free (filename);
154

155
  *status = g_value_get_enum (&return_vals->values[0]);
Michael Natterer's avatar
Michael Natterer committed
156

157
  if (*status == GIMP_PDB_SUCCESS)
Michael Natterer's avatar
Michael Natterer committed
158
    {
159
      image = gimp_value_get_image (&return_vals->values[1], gimp);
160

161
      if (image)
162
        {
163
          file_open_sanitize_image (image, as_new);
164

165 166
          if (mime_type)
            *mime_type = file_proc->mime_type;
167 168 169
        }
      else
        {
170
          g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
171 172 173
                       _("%s plug-in returned SUCCESS but did not "
                         "return an image"),
                       gimp_plug_in_procedure_get_label (file_proc));
174 175 176 177 178
          *status = GIMP_PDB_EXECUTION_ERROR;
        }
    }
  else if (*status != GIMP_PDB_CANCEL)
    {
179
      g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
180 181
                   _("%s plug-In could not open image"),
                   gimp_plug_in_procedure_get_label (file_proc));
Michael Natterer's avatar
Michael Natterer committed
182 183
    }

184
  g_value_array_free (return_vals);
185

186 187 188
  if (image)
    file_open_handle_color_profile (image, context, progress, run_mode);

189
  return image;
Michael Natterer's avatar
Michael Natterer committed
190 191
}

192 193 194 195 196 197 198 199 200 201 202
/*  Attempts to load a thumbnail by using a registered thumbnail loader.  */
GimpImage *
file_open_thumbnail (Gimp          *gimp,
                     GimpContext   *context,
                     GimpProgress  *progress,
                     const gchar   *uri,
                     gint           size,
                     const gchar  **mime_type,
                     gint          *image_width,
                     gint          *image_height)
{
203 204
  GimpPlugInProcedure *file_proc;
  GimpProcedure       *procedure;
205 206 207 208 209 210 211 212 213 214 215

  g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL);
  g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
  g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), NULL);
  g_return_val_if_fail (mime_type != NULL, NULL);
  g_return_val_if_fail (image_width != NULL, NULL);
  g_return_val_if_fail (image_height != NULL, NULL);

  *image_width  = 0;
  *image_height = 0;

216 217
  file_proc = file_utils_find_proc (gimp->plug_in_manager->load_procs,
                                    uri, NULL);
218 219 220 221

  if (! file_proc || ! file_proc->thumb_loader)
    return NULL;

222
  procedure = gimp_pdb_lookup_procedure (gimp->pdb, file_proc->thumb_loader);
223

224
  if (procedure && procedure->num_args >= 2 && procedure->num_values >= 1)
225 226
    {
      GimpPDBStatusType  status;
227
      GValueArray       *return_vals;
228
      gchar             *filename;
229
      GimpImage         *image = NULL;
230

231
      filename = file_utils_filename_from_uri (uri);
232

233 234
      if (! filename)
        filename = g_strdup (uri);
235

236 237 238 239 240 241 242
      return_vals =
        gimp_pdb_execute_procedure_by_name (gimp->pdb,
                                            context, progress,
                                            GIMP_OBJECT (procedure)->name,
                                            G_TYPE_STRING,   filename,
                                            GIMP_TYPE_INT32, size,
                                            G_TYPE_NONE);
243

244
      g_free (filename);
245

246
      status = g_value_get_enum (&return_vals->values[0]);
247

248
      if (status == GIMP_PDB_SUCCESS)
249
        {
250
          image = gimp_value_get_image (&return_vals->values[1], gimp);
251

252
          if (return_vals->n_values >= 3)
253
            {
254 255
              *image_width  = MAX (0, g_value_get_int (&return_vals->values[2]));
              *image_height = MAX (0, g_value_get_int (&return_vals->values[3]));
256
            }
257

258 259
          if (image)
            {
260
              file_open_sanitize_image (image, FALSE);
261

262
              *mime_type = file_proc->mime_type;
263

264
#ifdef GIMP_UNSTABLE
265 266
              g_printerr ("opened thumbnail at %d x %d\n",
                          image->width, image->height);
267
#endif
268
            }
269
        }
270

271
      g_value_array_free (return_vals);
272 273

      return image;
274 275 276 277 278
    }

  return NULL;
}

279 280
GimpImage *
file_open_with_display (Gimp               *gimp,
281
                        GimpContext        *context,
282
                        GimpProgress       *progress,
283
                        const gchar        *uri,
284
                        gboolean            as_new,
285 286
                        GimpPDBStatusType  *status,
                        GError            **error)
Michael Natterer's avatar
Michael Natterer committed
287
{
288
  return file_open_with_proc_and_display (gimp, context, progress,
289 290
                                          uri, uri, as_new, NULL,
                                          status, error);
Michael Natterer's avatar
Michael Natterer committed
291 292
}

293
GimpImage *
294 295 296 297 298
file_open_with_proc_and_display (Gimp                *gimp,
                                 GimpContext         *context,
                                 GimpProgress        *progress,
                                 const gchar         *uri,
                                 const gchar         *entered_filename,
299
                                 gboolean             as_new,
300 301 302
                                 GimpPlugInProcedure *file_proc,
                                 GimpPDBStatusType   *status,
                                 GError             **error)
Michael Natterer's avatar
Michael Natterer committed
303
{
304
  GimpImage   *image;
305
  const gchar *mime_type = NULL;
306 307

  g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL);
308
  g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
309
  g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), NULL);
310 311
  g_return_val_if_fail (status != NULL, NULL);

312
  image = file_open_image (gimp, context, progress,
313 314
                           uri,
                           entered_filename,
315
                           as_new,
316 317 318 319 320
                           file_proc,
                           GIMP_RUN_INTERACTIVE,
                           status,
                           &mime_type,
                           error);
321

322
  if (image)
Michael Natterer's avatar
Michael Natterer committed
323
    {
324
      gimp_create_display (image->gimp, image, GIMP_UNIT_PIXEL, 1.0);
Michael Natterer's avatar
Michael Natterer committed
325

326
      if (! as_new)
327
        {
328 329 330 331 332 333 334 335 336
          GimpDocumentList *documents = GIMP_DOCUMENT_LIST (gimp->documents);
          GimpImagefile    *imagefile;

          imagefile = gimp_document_list_add_uri (documents, uri, mime_type);

          /*  can only create a thumbnail if the passed uri and the
           *  resulting image's uri match.
           */
          if (strcmp (uri, gimp_image_get_uri (image)) == 0)
337
            {
338 339 340 341 342
              /*  no need to save a thumbnail if there's a good one already  */
              if (! gimp_imagefile_check_thumbnail (imagefile))
                {
                  gimp_imagefile_save_thumbnail (imagefile, mime_type, image);
                }
343
            }
344

345 346 347
          if (gimp->config->save_document_history)
            gimp_recent_list_add_uri (uri, mime_type);
        }
348

349
      /*  the display owns the image now  */
350
      g_object_unref (image);
Michael Natterer's avatar
Michael Natterer committed
351 352
    }

353
  return image;
Michael Natterer's avatar
Michael Natterer committed
354
}
355

356 357 358 359 360 361 362 363 364 365 366
GList *
file_open_layers (Gimp                *gimp,
                  GimpContext         *context,
                  GimpProgress        *progress,
                  GimpImage           *dest_image,
                  gboolean             merge_visible,
                  const gchar         *uri,
                  GimpRunMode          run_mode,
                  GimpPlugInProcedure *file_proc,
                  GimpPDBStatusType   *status,
                  GError             **error)
367
{
368
  GimpImage   *new_image;
369
  GList       *layers    = NULL;
370
  const gchar *mime_type = NULL;
371 372 373

  g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL);
  g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
374
  g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), NULL);
375 376 377 378 379
  g_return_val_if_fail (GIMP_IS_IMAGE (dest_image), NULL);
  g_return_val_if_fail (uri != NULL, NULL);
  g_return_val_if_fail (status != NULL, NULL);
  g_return_val_if_fail (error == NULL || *error == NULL, NULL);

380
  new_image = file_open_image (gimp, context, progress,
381
                               uri, uri, FALSE,
382 383
                               file_proc,
                               run_mode,
384
                               status, &mime_type, error);
385 386 387

  if (new_image)
    {
388 389
      GList *list;
      gint   n_visible = 0;
390 391 392

      gimp_image_undo_disable (new_image);

393 394 395
      for (list = GIMP_LIST (new_image->layers)->list;
           list;
           list = g_list_next (list))
396
        {
397 398 399
          if (! merge_visible)
            layers = g_list_prepend (layers, list->data);

400 401 402 403
          if (gimp_item_get_visible (list->data))
            {
              n_visible++;

404 405
              if (! layers)
                layers = g_list_prepend (layers, list->data);
406
            }
407 408
        }

409
      if (merge_visible && n_visible > 1)
410
        {
411 412 413 414 415 416 417 418 419
          GimpLayer *layer;

          g_list_free (layers);

          layer = gimp_image_merge_visible_layers (new_image, context,
                                                   GIMP_CLIP_TO_IMAGE, FALSE);

          layers = g_list_prepend (NULL, layer);
        }
420

421 422
      if (layers)
        {
423 424
          gchar *basename = file_utils_uri_display_basename (uri);

425
          for (list = layers; list; list = g_list_next (list))
426
            {
427 428
              GimpLayer *layer = list->data;
              GimpItem  *item;
429

430 431 432
              item = gimp_item_convert (GIMP_ITEM (layer), dest_image,
                                        G_TYPE_FROM_INSTANCE (layer),
                                        TRUE);
433

434 435 436 437 438 439 440 441 442 443 444
              if (layers->next == NULL)
                {
                  gimp_object_set_name (GIMP_OBJECT (item), basename);
                }
              else
                {
                  gchar *name = g_strdup_printf ("%s - %s", basename,
                                                 gimp_object_get_name (GIMP_OBJECT (layer)));

                  gimp_object_take_name (GIMP_OBJECT (item), name);
                }
445

446
              list->data = item;
447
            }
448

449 450
          g_free (basename);

451 452 453 454 455
          gimp_document_list_add_uri (GIMP_DOCUMENT_LIST (gimp->documents),
                                      uri, mime_type);

          if (gimp->config->save_document_history)
            gimp_recent_list_add_uri (uri, mime_type);
456
        }
457 458 459
      else
        {
          g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
460
                       _("Image doesn't contain any layers"));
461 462
          *status = GIMP_PDB_EXECUTION_ERROR;
       }
463 464 465 466

      g_object_unref (new_image);
    }

467
  return g_list_reverse (layers);
468
}
469 470


471 472 473
/*  This function is called for filenames passed on the command-line
 *  or from the D-Bus service.
 */
474 475
gboolean
file_open_from_command_line (Gimp        *gimp,
476 477
                             const gchar *filename,
                             gboolean     as_new)
478
{
479 480 481
  GError   *error   = NULL;
  gchar    *uri;
  gboolean  success = FALSE;
482

483 484
  g_return_val_if_fail (GIMP_IS_GIMP (gimp), FALSE);
  g_return_val_if_fail (filename != NULL, FALSE);
485

486 487 488 489
  /* we accept URI or filename */
  uri = file_utils_any_to_uri (gimp, filename, &error);

  if (uri)
490
    {
491 492
      GimpImage         *image;
      GimpPDBStatusType  status;
493

494 495 496
      image = file_open_with_display (gimp,
                                      gimp_get_user_context (gimp),
                                      NULL,
497
                                      uri, as_new,
498
                                      &status, &error);
499

500
      if (image)
501
        {
502
          success = TRUE;
503
        }
504
      else if (status != GIMP_PDB_CANCEL)
505
        {
506 507 508
          gchar *filename = file_utils_uri_to_utf8_filename (uri);

          g_message (_("Opening '%s' failed: %s"), filename, error->message);
509
          g_clear_error (&error);
510 511

          g_free (filename);
512
        }
513 514

      g_free (uri);
515
    }
516 517 518 519 520 521 522 523
  else
    {
      g_printerr ("conversion filename -> uri failed: %s\n",
                  error->message);
      g_clear_error (&error);
    }

  return success;
524 525 526
}


527 528 529
/*  private functions  */

static void
530 531
file_open_sanitize_image (GimpImage *image,
                          gboolean   as_new)
532
{
533 534 535
  if (as_new)
    gimp_object_set_name (GIMP_OBJECT (image), NULL);

536
  /* clear all undo steps */
537
  gimp_image_undo_free (image);
538 539

  /* make sure that undo is enabled */
540 541
  while (image->undo_freeze_count)
    gimp_image_undo_thaw (image);
542 543

  /* set the image to clean  */
544
  gimp_image_clean_all (image);
545

546 547 548
  gimp_image_invalidate_layer_previews (image);
  gimp_image_invalidate_channel_previews (image);
  gimp_viewable_invalidate_preview (GIMP_VIEWABLE (image));
549
}
550 551 552 553 554 555 556 557 558

static void
file_open_profile_apply_rgb (GimpImage    *image,
                             GimpContext  *context,
                             GimpProgress *progress,
                             GimpRunMode   run_mode)
{
  GError *error = NULL;

559 560
  if (gimp_image_base_type (image) != GIMP_GRAY &&
      ! plug_in_icc_profile_apply_rgb (image, context, progress,
561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594
                                       run_mode, &error))
    {
      gimp_message (image->gimp, G_OBJECT (progress),
                    GIMP_MESSAGE_WARNING, error->message);
      g_error_free (error);
    }
}

static void
file_open_handle_color_profile (GimpImage    *image,
                                GimpContext  *context,
                                GimpProgress *progress,
                                GimpRunMode   run_mode)
{
  if (gimp_image_parasite_find (image, "icc-profile"))
    {
      switch (image->gimp->config->color_profile_policy)
        {
        case GIMP_COLOR_PROFILE_POLICY_ASK:
          if (run_mode == GIMP_RUN_INTERACTIVE)
            file_open_profile_apply_rgb (image, context, progress,
                                         GIMP_RUN_INTERACTIVE);
          break;

        case GIMP_COLOR_PROFILE_POLICY_KEEP:
          break;

        case GIMP_COLOR_PROFILE_POLICY_CONVERT:
          file_open_profile_apply_rgb (image, context, progress,
                                       GIMP_RUN_NONINTERACTIVE);
          break;
        }
    }
}