2019-07-03 23:54:39 +02:00
|
|
|
/* GIMP - The GNU Image Manipulation Program
|
|
|
|
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
|
|
|
*
|
|
|
|
* GimpLink
|
|
|
|
* Copyright (C) 2019 Jehan
|
|
|
|
*
|
|
|
|
* 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 3 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 <https://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include <cairo.h>
|
|
|
|
#include <gegl.h>
|
|
|
|
#include <gdk-pixbuf/gdk-pixbuf.h>
|
|
|
|
|
|
|
|
#include "libgimpbase/gimpbase.h"
|
|
|
|
|
|
|
|
#include "core-types.h"
|
|
|
|
|
|
|
|
#include "gimp.h"
|
|
|
|
#include "gimpimage.h"
|
|
|
|
#include "gimplink.h"
|
|
|
|
#include "gimpmarshal.h"
|
|
|
|
#include "gimppickable.h"
|
|
|
|
#include "gimpprojection.h"
|
|
|
|
|
|
|
|
#include "file/file-open.h"
|
|
|
|
|
|
|
|
#include "gimp-intl.h"
|
|
|
|
|
|
|
|
|
|
|
|
enum
|
|
|
|
{
|
|
|
|
PROP_0,
|
|
|
|
PROP_GIMP,
|
2025-08-04 20:22:45 +02:00
|
|
|
PROP_FILE,
|
2025-08-07 16:53:22 +02:00
|
|
|
PROP_ABSOLUTE_PATH,
|
|
|
|
N_PROPS
|
2019-07-03 23:54:39 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
enum
|
|
|
|
{
|
|
|
|
CHANGED,
|
|
|
|
LAST_SIGNAL
|
|
|
|
};
|
|
|
|
|
|
|
|
struct _GimpLinkPrivate
|
|
|
|
{
|
2025-08-05 12:19:27 +02:00
|
|
|
Gimp *gimp;
|
|
|
|
GFile *file;
|
|
|
|
GFileMonitor *monitor;
|
|
|
|
gboolean absolute_path;
|
|
|
|
|
2025-08-07 16:53:22 +02:00
|
|
|
GeglBuffer *buffer;
|
2025-08-05 12:19:27 +02:00
|
|
|
gboolean broken;
|
|
|
|
GError *error;
|
|
|
|
guint idle_changed_source;
|
|
|
|
|
|
|
|
gboolean is_vector;
|
|
|
|
gint width;
|
|
|
|
gint height;
|
2025-08-28 23:06:11 +02:00
|
|
|
gboolean keep_ratio;
|
2025-08-05 12:19:27 +02:00
|
|
|
GimpImageBaseType base_type;
|
|
|
|
GimpPrecision precision;
|
|
|
|
GimpPlugInProcedure *load_proc;
|
2025-09-24 16:08:47 +02:00
|
|
|
const gchar *mime_type;
|
2019-07-03 23:54:39 +02:00
|
|
|
};
|
|
|
|
|
2025-08-04 20:22:45 +02:00
|
|
|
static void gimp_link_finalize (GObject *object);
|
|
|
|
static void gimp_link_get_property (GObject *object,
|
|
|
|
guint property_id,
|
|
|
|
GValue *value,
|
|
|
|
GParamSpec *pspec);
|
|
|
|
static void gimp_link_set_property (GObject *object,
|
|
|
|
guint property_id,
|
|
|
|
const GValue *value,
|
|
|
|
GParamSpec *pspec);
|
|
|
|
|
|
|
|
static void gimp_link_file_changed (GFileMonitor *monitor,
|
|
|
|
GFile *file,
|
|
|
|
GFile *other_file,
|
|
|
|
GFileMonitorEvent event_type,
|
|
|
|
GimpLink *link);
|
|
|
|
static gboolean gimp_link_emit_changed (gpointer data);
|
|
|
|
|
2025-08-07 16:53:22 +02:00
|
|
|
static void gimp_link_update_buffer (GimpLink *link,
|
|
|
|
GimpProgress *progress,
|
|
|
|
GError **error);
|
2025-08-09 23:26:13 +02:00
|
|
|
static void gimp_link_start_monitoring (GimpLink *link);
|
2025-08-04 20:22:45 +02:00
|
|
|
static gchar * gimp_link_get_relative_path (GimpLink *link,
|
|
|
|
GFile *parent,
|
|
|
|
gint n_back);
|
|
|
|
|
2019-07-03 23:54:39 +02:00
|
|
|
|
2025-08-07 16:53:22 +02:00
|
|
|
|
2019-07-03 23:54:39 +02:00
|
|
|
G_DEFINE_TYPE_WITH_PRIVATE (GimpLink, gimp_link, GIMP_TYPE_OBJECT)
|
|
|
|
|
|
|
|
#define parent_class gimp_link_parent_class
|
|
|
|
|
2025-08-07 16:53:22 +02:00
|
|
|
static guint link_signals[LAST_SIGNAL] = { 0 };
|
|
|
|
|
|
|
|
static GParamSpec *link_props[N_PROPS] = { NULL, };
|
2019-07-03 23:54:39 +02:00
|
|
|
|
|
|
|
static void
|
|
|
|
gimp_link_class_init (GimpLinkClass *klass)
|
|
|
|
{
|
|
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
|
|
|
|
link_signals[CHANGED] =
|
|
|
|
g_signal_new ("changed",
|
|
|
|
G_TYPE_FROM_CLASS (klass),
|
|
|
|
G_SIGNAL_RUN_FIRST,
|
|
|
|
G_STRUCT_OFFSET (GimpLinkClass, changed),
|
|
|
|
NULL, NULL, NULL,
|
|
|
|
G_TYPE_NONE, 0);
|
|
|
|
|
|
|
|
object_class->finalize = gimp_link_finalize;
|
|
|
|
object_class->get_property = gimp_link_get_property;
|
|
|
|
object_class->set_property = gimp_link_set_property;
|
|
|
|
|
2025-08-07 16:53:22 +02:00
|
|
|
link_props[PROP_GIMP] = g_param_spec_object ("gimp", NULL, NULL,
|
2019-07-03 23:54:39 +02:00
|
|
|
GIMP_TYPE_GIMP,
|
|
|
|
GIMP_PARAM_READWRITE |
|
2025-08-07 16:53:22 +02:00
|
|
|
G_PARAM_CONSTRUCT_ONLY);
|
|
|
|
link_props[PROP_FILE] = g_param_spec_object ("file", NULL, NULL,
|
2019-07-03 23:54:39 +02:00
|
|
|
G_TYPE_FILE,
|
2025-08-07 16:53:22 +02:00
|
|
|
GIMP_PARAM_READWRITE |
|
|
|
|
G_PARAM_EXPLICIT_NOTIFY);
|
|
|
|
link_props[PROP_ABSOLUTE_PATH] = g_param_spec_boolean ("absolute-path", NULL, NULL,
|
|
|
|
FALSE,
|
|
|
|
GIMP_PARAM_READWRITE);
|
|
|
|
|
|
|
|
g_object_class_install_properties (object_class, N_PROPS, link_props);
|
2019-07-03 23:54:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gimp_link_init (GimpLink *link)
|
|
|
|
{
|
2025-08-05 12:19:27 +02:00
|
|
|
link->p = gimp_link_get_instance_private (link);
|
|
|
|
link->p->gimp = NULL;
|
|
|
|
link->p->file = NULL;
|
|
|
|
link->p->monitor = NULL;
|
2025-08-07 16:53:22 +02:00
|
|
|
link->p->buffer = NULL;
|
2025-08-05 12:19:27 +02:00
|
|
|
link->p->broken = TRUE;
|
|
|
|
link->p->error = NULL;
|
|
|
|
link->p->width = 0;
|
|
|
|
link->p->height = 0;
|
|
|
|
link->p->base_type = GIMP_RGB;
|
|
|
|
link->p->precision = GIMP_PRECISION_U8_PERCEPTUAL;
|
|
|
|
link->p->load_proc = NULL;
|
2020-07-20 17:13:39 +02:00
|
|
|
|
|
|
|
link->p->idle_changed_source = 0;
|
2019-07-03 23:54:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gimp_link_finalize (GObject *object)
|
|
|
|
{
|
|
|
|
GimpLink *link = GIMP_LINK (object);
|
|
|
|
|
|
|
|
g_clear_object (&link->p->file);
|
|
|
|
g_clear_object (&link->p->monitor);
|
2025-08-07 16:53:22 +02:00
|
|
|
g_clear_object (&link->p->buffer);
|
|
|
|
g_clear_error (&link->p->error);
|
2019-07-03 23:54:39 +02:00
|
|
|
|
|
|
|
G_OBJECT_CLASS (parent_class)->finalize (object);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gimp_link_get_property (GObject *object,
|
|
|
|
guint property_id,
|
|
|
|
GValue *value,
|
|
|
|
GParamSpec *pspec)
|
|
|
|
{
|
|
|
|
GimpLink *link = GIMP_LINK (object);
|
|
|
|
|
|
|
|
switch (property_id)
|
|
|
|
{
|
|
|
|
case PROP_GIMP:
|
|
|
|
g_value_set_object (value, link->p->gimp);
|
|
|
|
break;
|
|
|
|
case PROP_FILE:
|
|
|
|
g_value_set_object (value, link->p->file);
|
|
|
|
break;
|
2025-08-04 20:22:45 +02:00
|
|
|
case PROP_ABSOLUTE_PATH:
|
|
|
|
g_value_set_boolean (value, link->p->absolute_path);
|
|
|
|
break;
|
2019-07-03 23:54:39 +02:00
|
|
|
|
|
|
|
default:
|
|
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gimp_link_set_property (GObject *object,
|
|
|
|
guint property_id,
|
|
|
|
const GValue *value,
|
|
|
|
GParamSpec *pspec)
|
|
|
|
{
|
|
|
|
GimpLink *link = GIMP_LINK (object);
|
|
|
|
|
|
|
|
switch (property_id)
|
|
|
|
{
|
|
|
|
case PROP_GIMP:
|
|
|
|
link->p->gimp = g_value_get_object (value);
|
|
|
|
break;
|
|
|
|
case PROP_FILE:
|
2025-08-28 23:06:11 +02:00
|
|
|
gimp_link_set_file (link, g_value_get_object (value), 0, 0, FALSE, NULL, NULL);
|
2019-07-03 23:54:39 +02:00
|
|
|
break;
|
2025-08-04 20:22:45 +02:00
|
|
|
case PROP_ABSOLUTE_PATH:
|
|
|
|
link->p->absolute_path = g_value_get_boolean (value);
|
|
|
|
break;
|
2019-07-03 23:54:39 +02:00
|
|
|
|
|
|
|
default:
|
|
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gimp_link_file_changed (GFileMonitor *monitor,
|
|
|
|
GFile *file,
|
|
|
|
GFile *other_file,
|
|
|
|
GFileMonitorEvent event_type,
|
|
|
|
GimpLink *link)
|
|
|
|
{
|
|
|
|
switch (event_type)
|
|
|
|
{
|
|
|
|
case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
|
2020-07-20 17:13:39 +02:00
|
|
|
if (link->p->idle_changed_source == 0)
|
|
|
|
link->p->idle_changed_source = g_idle_add_full (G_PRIORITY_LOW,
|
|
|
|
gimp_link_emit_changed,
|
|
|
|
link, NULL);
|
|
|
|
break;
|
2019-07-03 23:54:39 +02:00
|
|
|
case G_FILE_MONITOR_EVENT_CREATED:
|
|
|
|
g_signal_emit (link, link_signals[CHANGED], 0);
|
|
|
|
break;
|
|
|
|
case G_FILE_MONITOR_EVENT_DELETED:
|
|
|
|
link->p->broken = TRUE;
|
2025-08-05 12:19:27 +02:00
|
|
|
g_clear_error (&link->p->error);
|
|
|
|
g_set_error_literal (&link->p->error,
|
|
|
|
G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
_("The file got deleted"));
|
2019-07-03 23:54:39 +02:00
|
|
|
break;
|
2020-07-20 17:13:39 +02:00
|
|
|
|
2019-07-03 23:54:39 +02:00
|
|
|
default:
|
|
|
|
/* No need to signal for changes where nothing can be done anyway.
|
|
|
|
* In particular a file deletion, the link is broken, yet we don't
|
|
|
|
* want to re-render.
|
|
|
|
* Don't emit either on G_FILE_MONITOR_EVENT_CHANGED because too
|
|
|
|
* many such events may be emitted for a single file writing.
|
|
|
|
*/
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-07-20 17:13:39 +02:00
|
|
|
static gboolean
|
|
|
|
gimp_link_emit_changed (gpointer data)
|
|
|
|
{
|
|
|
|
GimpLink *link = GIMP_LINK (data);
|
|
|
|
|
2025-08-07 16:53:22 +02:00
|
|
|
gimp_link_update_buffer (link, NULL, NULL);
|
|
|
|
|
2020-07-20 17:13:39 +02:00
|
|
|
g_signal_emit (link, link_signals[CHANGED], 0);
|
|
|
|
link->p->idle_changed_source = 0;
|
|
|
|
|
|
|
|
return G_SOURCE_REMOVE;
|
|
|
|
}
|
|
|
|
|
2025-08-07 16:53:22 +02:00
|
|
|
static void
|
|
|
|
gimp_link_update_buffer (GimpLink *link,
|
|
|
|
GimpProgress *progress,
|
|
|
|
GError **error)
|
|
|
|
{
|
|
|
|
GeglBuffer *buffer = NULL;
|
|
|
|
GError *real_error = NULL;
|
|
|
|
|
|
|
|
g_return_if_fail (GIMP_IS_LINK (link));
|
|
|
|
g_return_if_fail (error == NULL || *error == NULL);
|
|
|
|
|
|
|
|
link->p->is_vector = FALSE;
|
|
|
|
g_clear_error (&link->p->error);
|
|
|
|
|
|
|
|
if (link->p->file)
|
|
|
|
{
|
|
|
|
GimpImage *image;
|
|
|
|
GimpPDBStatusType status;
|
|
|
|
|
2025-09-24 16:08:47 +02:00
|
|
|
link->p->mime_type = NULL;
|
2025-08-07 16:53:22 +02:00
|
|
|
image = file_open_image (link->p->gimp,
|
|
|
|
gimp_get_user_context (link->p->gimp),
|
|
|
|
progress,
|
|
|
|
link->p->file,
|
|
|
|
link->p->width, link->p->height,
|
2025-08-28 23:06:11 +02:00
|
|
|
link->p->keep_ratio,
|
2025-08-07 16:53:22 +02:00
|
|
|
FALSE, NULL,
|
|
|
|
/* XXX We might want interactive opening
|
|
|
|
* for a first opening (when done through
|
|
|
|
* GUI), but not for every re-render.
|
|
|
|
*/
|
|
|
|
GIMP_RUN_NONINTERACTIVE,
|
|
|
|
&link->p->is_vector,
|
2025-09-24 16:08:47 +02:00
|
|
|
&status, &link->p->mime_type,
|
|
|
|
&real_error);
|
2025-08-07 16:53:22 +02:00
|
|
|
|
|
|
|
if (image && status == GIMP_PDB_SUCCESS)
|
|
|
|
{
|
|
|
|
/* If we don't flush the projection first, the buffer may be empty.
|
|
|
|
* I do wonder if the flushing and updating of the link could
|
|
|
|
* not be multi-threaded with gimp_projection_flush() instead,
|
|
|
|
* then notifying the update through signals. For very heavy
|
|
|
|
* images, would it be a better UX? XXX
|
|
|
|
*/
|
|
|
|
gimp_projection_flush_now (gimp_image_get_projection (image), TRUE);
|
|
|
|
buffer = gimp_pickable_get_buffer (GIMP_PICKABLE (image));
|
|
|
|
g_object_ref (buffer);
|
|
|
|
|
|
|
|
link->p->base_type = gimp_image_get_base_type (image);
|
|
|
|
link->p->precision = gimp_image_get_precision (image);
|
|
|
|
link->p->width = gimp_image_get_width (image);
|
|
|
|
link->p->height = gimp_image_get_height (image);
|
|
|
|
link->p->load_proc = gimp_image_get_load_proc (image);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Only keep the buffer, free the rest. */
|
|
|
|
g_clear_object (&image);
|
|
|
|
}
|
|
|
|
|
|
|
|
link->p->broken = (buffer == NULL);
|
|
|
|
if (link->p->broken)
|
|
|
|
{
|
|
|
|
if (real_error)
|
|
|
|
link->p->error = g_error_copy (real_error);
|
|
|
|
else
|
|
|
|
g_set_error_literal (&link->p->error,
|
|
|
|
G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
_("No file was set"));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (error)
|
|
|
|
*error = real_error;
|
|
|
|
else
|
|
|
|
g_clear_error (&real_error);
|
|
|
|
|
|
|
|
if (buffer)
|
|
|
|
{
|
|
|
|
/* Keep the old buffer if the link is broken (outdated image is
|
|
|
|
* better than none).
|
|
|
|
*/
|
|
|
|
g_clear_object (&link->p->buffer);
|
|
|
|
link->p->buffer = buffer;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-08-09 23:26:13 +02:00
|
|
|
static void
|
|
|
|
gimp_link_start_monitoring (GimpLink *link)
|
|
|
|
{
|
|
|
|
link->p->monitor = g_file_monitor_file (link->p->file,
|
|
|
|
G_FILE_MONITOR_WATCH_HARD_LINKS,
|
|
|
|
NULL, NULL);
|
|
|
|
g_signal_connect (link->p->monitor, "changed",
|
|
|
|
G_CALLBACK (gimp_link_file_changed),
|
|
|
|
link);
|
|
|
|
}
|
|
|
|
|
2025-08-04 20:22:45 +02:00
|
|
|
/**
|
|
|
|
* gimp_link_get_relative_path:
|
|
|
|
* @link: the image this link is associated with.
|
|
|
|
* @parent: (transfer full): a #GFile object.
|
|
|
|
* @n_back: set it to 0 when calling it initially.
|
|
|
|
*
|
|
|
|
* This is a variant of g_file_get_relative_path() which will work even
|
|
|
|
* when the @link file is not a child of @parent. In this case, the
|
|
|
|
* relative link will use "../" as many times as necessary until a
|
|
|
|
* common parent folder is found.
|
|
|
|
*
|
|
|
|
* In case no parent is found (it may happen for instance on Windows,
|
|
|
|
* with various file system roots), an absolute path is returned
|
|
|
|
* instead, ensuring that we never return %NULL.
|
|
|
|
*
|
|
|
|
* Note that this function takes ownership of @parent and will take care
|
|
|
|
* of freeing it. This allows for tail recursion.
|
|
|
|
*
|
|
|
|
* Returns: a path from @link relatively to @parent, falling back
|
|
|
|
* to an absolute path if a relative path cannot be constructed.
|
|
|
|
**/
|
|
|
|
static gchar *
|
|
|
|
gimp_link_get_relative_path (GimpLink *link,
|
|
|
|
GFile *parent,
|
|
|
|
gint n_back)
|
|
|
|
{
|
|
|
|
gchar *relative_path;
|
|
|
|
|
|
|
|
g_return_val_if_fail (GIMP_IS_LINK (link), NULL);
|
|
|
|
g_return_val_if_fail (parent != NULL && n_back >= 0, NULL);
|
|
|
|
|
|
|
|
relative_path = g_file_get_relative_path (parent, link->p->file);
|
|
|
|
|
|
|
|
if (relative_path == NULL)
|
|
|
|
{
|
|
|
|
GFile *grand_parent = g_file_get_parent (parent);
|
|
|
|
|
|
|
|
g_object_unref (parent);
|
|
|
|
|
|
|
|
if (grand_parent == NULL)
|
|
|
|
/* This may happen e.g. on Windows where there are several roots
|
|
|
|
* so it is not always possible to make a relative path.
|
|
|
|
*/
|
|
|
|
return g_file_get_path (link->p->file);
|
|
|
|
else
|
|
|
|
return gimp_link_get_relative_path (link, grand_parent, n_back + 1);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
g_object_unref (parent);
|
|
|
|
|
|
|
|
if (n_back > 0)
|
|
|
|
{
|
|
|
|
GStrvBuilder *builder;
|
|
|
|
gchar **array;
|
|
|
|
gchar *dots;
|
|
|
|
gchar *relpath;
|
|
|
|
|
|
|
|
builder = g_strv_builder_new ();
|
|
|
|
for (gint i = 0; i < n_back; i++)
|
|
|
|
g_strv_builder_add (builder, "..");
|
|
|
|
|
|
|
|
array = g_strv_builder_end (builder);
|
|
|
|
dots = g_strjoinv (G_DIR_SEPARATOR_S, array);
|
|
|
|
relpath = g_build_filename (dots, relative_path, NULL);
|
|
|
|
|
|
|
|
g_free (relative_path);
|
|
|
|
g_free (dots);
|
|
|
|
g_strfreev (array);
|
|
|
|
g_strv_builder_unref (builder);
|
|
|
|
|
|
|
|
relative_path = relpath;
|
|
|
|
}
|
|
|
|
|
|
|
|
return relative_path;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-07-03 23:54:39 +02:00
|
|
|
/* public functions */
|
|
|
|
|
|
|
|
/**
|
|
|
|
* gimp_link_new:
|
|
|
|
* @gimp: #Gimp object.
|
|
|
|
* @file: a #GFile object.
|
|
|
|
*
|
2025-08-04 20:22:45 +02:00
|
|
|
* Creates a new link object. By default, all link objects are created
|
|
|
|
* as being relative to the path of the image they will be associated
|
|
|
|
* with.
|
2019-07-03 23:54:39 +02:00
|
|
|
*
|
|
|
|
* Return value: a new #GimpLink or %NULL in case of a problem
|
|
|
|
**/
|
|
|
|
GimpLink *
|
2025-08-07 16:53:22 +02:00
|
|
|
gimp_link_new (Gimp *gimp,
|
|
|
|
GFile *file,
|
2025-08-28 23:06:11 +02:00
|
|
|
gint vector_width,
|
|
|
|
gint vector_height,
|
|
|
|
gboolean keep_ratio,
|
2025-08-07 16:53:22 +02:00
|
|
|
GimpProgress *progress,
|
|
|
|
GError **error)
|
2019-07-03 23:54:39 +02:00
|
|
|
{
|
2025-08-07 16:53:22 +02:00
|
|
|
GimpLink *link;
|
2019-07-03 23:54:39 +02:00
|
|
|
|
|
|
|
g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL);
|
|
|
|
g_return_val_if_fail (G_IS_FILE (file), NULL);
|
|
|
|
|
|
|
|
link = g_object_new (GIMP_TYPE_LINK,
|
2025-08-04 20:22:45 +02:00
|
|
|
"gimp", gimp,
|
|
|
|
"absolute-path", FALSE,
|
2019-07-03 23:54:39 +02:00
|
|
|
NULL);
|
|
|
|
|
2025-08-28 23:06:11 +02:00
|
|
|
gimp_link_set_file (link, file, vector_width, vector_height, keep_ratio, progress, error);
|
2025-08-07 16:53:22 +02:00
|
|
|
|
2019-07-03 23:54:39 +02:00
|
|
|
return GIMP_LINK (link);
|
|
|
|
}
|
|
|
|
|
2025-08-07 16:53:22 +02:00
|
|
|
GimpLink *
|
|
|
|
gimp_link_duplicate (GimpLink *link)
|
|
|
|
{
|
|
|
|
GimpLink *new_link;
|
|
|
|
|
|
|
|
g_return_val_if_fail (GIMP_IS_LINK (link), NULL);
|
|
|
|
|
|
|
|
new_link = g_object_new (GIMP_TYPE_LINK,
|
|
|
|
"gimp", link->p->gimp,
|
|
|
|
"absolute-path", gimp_link_get_absolute_path (link),
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
/* Copy things manually as we do not need to trigger a load. */
|
|
|
|
new_link->p->file = link->p->file ? g_object_ref (link->p->file) : NULL;
|
|
|
|
|
|
|
|
new_link->p->buffer = link->p->buffer ? gegl_buffer_dup (link->p->buffer) : NULL;
|
|
|
|
new_link->p->broken = link->p->broken;
|
|
|
|
new_link->p->error = link->p->error ? g_error_copy (link->p->error) : NULL;
|
|
|
|
|
|
|
|
new_link->p->is_vector = link->p->is_vector;
|
|
|
|
new_link->p->width = link->p->width;
|
|
|
|
new_link->p->height = link->p->height;
|
|
|
|
new_link->p->base_type = link->p->base_type;
|
|
|
|
new_link->p->precision = link->p->precision;
|
|
|
|
new_link->p->load_proc = link->p->load_proc;
|
|
|
|
|
|
|
|
if (new_link->p->file)
|
|
|
|
{
|
|
|
|
gchar *basename;
|
|
|
|
|
|
|
|
basename = g_file_get_basename (new_link->p->file);
|
|
|
|
gimp_object_set_name_safe (GIMP_OBJECT (new_link), basename);
|
|
|
|
g_free (basename);
|
2025-08-09 23:26:13 +02:00
|
|
|
|
|
|
|
if (gimp_link_is_monitored (link))
|
|
|
|
gimp_link_start_monitoring (new_link);
|
2025-08-07 16:53:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return new_link;
|
|
|
|
}
|
|
|
|
|
2025-08-04 20:22:45 +02:00
|
|
|
/*
|
|
|
|
* gimp_link_get_file:
|
|
|
|
* @link: the #GimpLink object.
|
|
|
|
* @xcf_file: optional XCF file from which @path will be relative to.
|
|
|
|
* @path: optional returned path of the returned file.
|
|
|
|
*
|
|
|
|
* If @path is non-%NULL, it will be set to the file system path for the
|
|
|
|
* returned %GFile, either as an absolute or relative path, depending on
|
|
|
|
* how @link was set.
|
|
|
|
* Note that it is possible for @path to be absolute even when it is set
|
|
|
|
* to be a relative path, in cases where no relative path can be
|
|
|
|
* constructed from @xcf_file.
|
|
|
|
*
|
|
|
|
* Returns: the %GFile which %link is syncing too.
|
|
|
|
*/
|
2019-07-03 23:54:39 +02:00
|
|
|
GFile *
|
2025-08-04 20:22:45 +02:00
|
|
|
gimp_link_get_file (GimpLink *link,
|
|
|
|
GFile *xcf_file,
|
|
|
|
gchar **path)
|
2019-07-03 23:54:39 +02:00
|
|
|
{
|
|
|
|
g_return_val_if_fail (GIMP_IS_LINK (link), NULL);
|
2025-08-04 20:22:45 +02:00
|
|
|
g_return_val_if_fail ((path == NULL && xcf_file == NULL) ||
|
|
|
|
(*path == NULL && xcf_file != NULL &&
|
|
|
|
g_file_has_parent (xcf_file, NULL)), NULL);
|
|
|
|
|
|
|
|
if (path != NULL)
|
|
|
|
{
|
|
|
|
if (link->p->absolute_path)
|
|
|
|
*path = g_file_get_path (link->p->file);
|
|
|
|
else
|
|
|
|
*path = gimp_link_get_relative_path (link,
|
|
|
|
g_file_get_parent (xcf_file),
|
|
|
|
0);
|
|
|
|
}
|
2019-07-03 23:54:39 +02:00
|
|
|
|
|
|
|
return link->p->file;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2025-08-07 16:53:22 +02:00
|
|
|
gimp_link_set_file (GimpLink *link,
|
|
|
|
GFile *file,
|
2025-08-28 23:06:11 +02:00
|
|
|
gint vector_width,
|
|
|
|
gint vector_height,
|
|
|
|
gboolean keep_ratio,
|
2025-08-07 16:53:22 +02:00
|
|
|
GimpProgress *progress,
|
|
|
|
GError **error)
|
2019-07-03 23:54:39 +02:00
|
|
|
{
|
2025-08-04 20:22:45 +02:00
|
|
|
g_return_if_fail (GIMP_IS_LINK (link));
|
2019-07-03 23:54:39 +02:00
|
|
|
g_return_if_fail (G_IS_FILE (file) || file == NULL);
|
2025-08-07 16:53:22 +02:00
|
|
|
g_return_if_fail (error == NULL || *error == NULL);
|
2019-07-03 23:54:39 +02:00
|
|
|
|
2025-08-07 16:53:22 +02:00
|
|
|
if (file == link->p->file ||
|
|
|
|
(file && link->p->file && g_file_equal (file, link->p->file)))
|
2025-08-28 23:06:11 +02:00
|
|
|
{
|
|
|
|
if (link->p->width != vector_width ||
|
|
|
|
link->p->height != vector_height ||
|
|
|
|
link->p->keep_ratio != keep_ratio)
|
|
|
|
gimp_link_set_size (link, vector_width, vector_height, keep_ratio);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
link->p->width = vector_width;
|
|
|
|
link->p->height = vector_height;
|
|
|
|
link->p->keep_ratio = keep_ratio;
|
2019-07-03 23:54:39 +02:00
|
|
|
|
2025-08-07 16:53:22 +02:00
|
|
|
g_clear_object (&link->p->monitor);
|
|
|
|
|
|
|
|
g_set_object (&link->p->file, file);
|
|
|
|
|
|
|
|
gimp_link_update_buffer (link, progress, error);
|
|
|
|
|
|
|
|
if (link->p->file)
|
|
|
|
{
|
|
|
|
gchar *basename;
|
|
|
|
|
|
|
|
basename = g_file_get_basename (link->p->file);
|
|
|
|
gimp_object_set_name_safe (GIMP_OBJECT (link), basename);
|
|
|
|
g_free (basename);
|
2025-08-09 23:26:13 +02:00
|
|
|
|
|
|
|
gimp_link_start_monitoring (link);
|
2025-08-07 16:53:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
g_object_notify_by_pspec (G_OBJECT (link), link_props[PROP_FILE]);
|
2019-07-03 23:54:39 +02:00
|
|
|
}
|
|
|
|
|
2025-08-04 20:22:45 +02:00
|
|
|
gboolean
|
|
|
|
gimp_link_get_absolute_path (GimpLink *link)
|
|
|
|
{
|
|
|
|
g_return_val_if_fail (GIMP_IS_LINK (link), FALSE);
|
|
|
|
|
|
|
|
return link->p->absolute_path;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
gimp_link_set_absolute_path (GimpLink *link,
|
|
|
|
gboolean absolute_path)
|
|
|
|
{
|
|
|
|
g_return_if_fail (GIMP_IS_LINK (link));
|
|
|
|
|
|
|
|
link->p->absolute_path = absolute_path;
|
|
|
|
}
|
|
|
|
|
2025-09-24 16:08:47 +02:00
|
|
|
const gchar *
|
|
|
|
gimp_link_get_mime_type (GimpLink *link)
|
|
|
|
{
|
|
|
|
g_return_val_if_fail (GIMP_IS_LINK (link), NULL);
|
|
|
|
|
|
|
|
return link->p->mime_type;
|
|
|
|
}
|
|
|
|
|
2025-08-09 23:26:13 +02:00
|
|
|
void
|
|
|
|
gimp_link_freeze (GimpLink *link)
|
|
|
|
{
|
|
|
|
g_return_if_fail (GIMP_IS_LINK (link));
|
|
|
|
g_return_if_fail (link->p->monitor != NULL);
|
|
|
|
|
|
|
|
g_clear_object (&link->p->monitor);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
gimp_link_thaw (GimpLink *link)
|
|
|
|
{
|
|
|
|
g_return_if_fail (GIMP_IS_LINK (link));
|
|
|
|
g_return_if_fail (G_IS_FILE (link->p->file) && link->p->monitor == NULL);
|
|
|
|
|
|
|
|
gimp_link_update_buffer (link, NULL, NULL);
|
|
|
|
gimp_link_start_monitoring (link);
|
|
|
|
}
|
|
|
|
|
|
|
|
gboolean
|
|
|
|
gimp_link_is_monitored (GimpLink *link)
|
|
|
|
{
|
|
|
|
g_return_val_if_fail (GIMP_IS_LINK (link), FALSE);
|
|
|
|
|
|
|
|
return (link->p->monitor != NULL);
|
|
|
|
}
|
|
|
|
|
2019-07-03 23:54:39 +02:00
|
|
|
gboolean
|
2025-08-07 16:53:22 +02:00
|
|
|
gimp_link_is_broken (GimpLink *link)
|
2019-07-03 23:54:39 +02:00
|
|
|
{
|
2025-08-07 16:53:22 +02:00
|
|
|
g_return_val_if_fail (GIMP_IS_LINK (link), TRUE);
|
2025-08-05 12:19:27 +02:00
|
|
|
|
2019-07-03 23:54:39 +02:00
|
|
|
return link->p->broken;
|
|
|
|
}
|
|
|
|
|
2020-07-29 19:17:04 +02:00
|
|
|
void
|
|
|
|
gimp_link_set_size (GimpLink *link,
|
|
|
|
gint width,
|
2025-08-28 23:06:11 +02:00
|
|
|
gint height,
|
|
|
|
gboolean keep_ratio)
|
2020-07-29 19:17:04 +02:00
|
|
|
{
|
2025-08-07 16:53:22 +02:00
|
|
|
g_return_if_fail (GIMP_IS_LINK (link));
|
|
|
|
|
2025-08-28 23:06:11 +02:00
|
|
|
link->p->width = width;
|
|
|
|
link->p->height = height;
|
|
|
|
link->p->keep_ratio = keep_ratio;
|
2025-08-09 23:26:13 +02:00
|
|
|
|
|
|
|
if (link->p->monitor && link->p->is_vector)
|
|
|
|
gimp_link_update_buffer (link, NULL, NULL);
|
2020-07-29 19:17:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
gimp_link_get_size (GimpLink *link,
|
|
|
|
gint *width,
|
|
|
|
gint *height)
|
|
|
|
{
|
2025-08-07 16:53:22 +02:00
|
|
|
g_return_if_fail (GIMP_IS_LINK (link));
|
|
|
|
|
2020-07-29 19:17:04 +02:00
|
|
|
*width = link->p->width;
|
|
|
|
*height = link->p->height;
|
|
|
|
}
|
|
|
|
|
2025-08-05 12:19:27 +02:00
|
|
|
GimpImageBaseType
|
|
|
|
gimp_link_get_base_type (GimpLink *link)
|
|
|
|
{
|
|
|
|
g_return_val_if_fail (GIMP_IS_LINK (link) && ! link->p->broken, GIMP_RGB);
|
|
|
|
|
|
|
|
return link->p->base_type;
|
|
|
|
}
|
|
|
|
|
|
|
|
GimpPrecision
|
|
|
|
gimp_link_get_precision (GimpLink *link)
|
|
|
|
{
|
|
|
|
g_return_val_if_fail (GIMP_IS_LINK (link) && ! link->p->broken, GIMP_PRECISION_U8_PERCEPTUAL);
|
|
|
|
|
|
|
|
return link->p->precision;
|
|
|
|
}
|
|
|
|
|
|
|
|
GimpPlugInProcedure *
|
|
|
|
gimp_link_get_load_proc (GimpLink *link)
|
|
|
|
{
|
|
|
|
g_return_val_if_fail (GIMP_IS_LINK (link) && ! link->p->broken, NULL);
|
|
|
|
|
|
|
|
return link->p->load_proc;
|
|
|
|
}
|
|
|
|
|
2020-07-29 19:17:04 +02:00
|
|
|
gboolean
|
|
|
|
gimp_link_is_vector (GimpLink *link)
|
|
|
|
{
|
2025-08-07 16:53:22 +02:00
|
|
|
g_return_val_if_fail (GIMP_IS_LINK (link), FALSE);
|
|
|
|
|
2020-07-29 19:17:04 +02:00
|
|
|
return link->p->is_vector;
|
|
|
|
}
|
|
|
|
|
2019-07-03 23:54:39 +02:00
|
|
|
GeglBuffer *
|
2025-08-07 16:53:22 +02:00
|
|
|
gimp_link_get_buffer (GimpLink *link)
|
2019-07-03 23:54:39 +02:00
|
|
|
{
|
2025-08-07 16:53:22 +02:00
|
|
|
g_return_val_if_fail (GIMP_IS_LINK (link), NULL);
|
2019-07-03 23:54:39 +02:00
|
|
|
|
2025-08-07 16:53:22 +02:00
|
|
|
return link->p->buffer;
|
2019-07-03 23:54:39 +02:00
|
|
|
}
|