plug-ins: port file-gif-save to begin_export() and end_export()

and build its GUI using propwidgets. Remove a lot of code that
is now handled by GimpProcedureConfig, and remove the ui file.
This commit is contained in:
Michael Natterer 2019-10-28 12:12:09 +01:00
parent 0da5f0ae7e
commit a5cf3e9506
5 changed files with 231 additions and 677 deletions

View File

@ -38,19 +38,12 @@
#define SAVE_PROC "file-gif-save" #define SAVE_PROC "file-gif-save"
#define PLUG_IN_BINARY "file-gif-save" #define PLUG_IN_BINARY "file-gif-save"
#define PLUG_IN_ROLE "gimp-file-gif-save"
/* uncomment the line below for a little debugging info */ /* uncomment the line below for a little debugging info */
/* #define GIFDEBUG yesplease */ /* #define GIFDEBUG yesplease */
enum
{
DISPOSE_STORE_VALUE_COLUMN,
DISPOSE_STORE_LABEL_COLUMN
};
enum enum
{ {
DISPOSE_UNSPECIFIED, DISPOSE_UNSPECIFIED,
@ -58,18 +51,6 @@ enum
DISPOSE_REPLACE DISPOSE_REPLACE
}; };
typedef struct
{
gint interlace;
gint save_comment;
gint loop;
gint default_delay;
gint default_dispose;
gboolean use_default_delay;
gboolean use_default_dispose;
gboolean as_animation;
} GIFSaveVals;
typedef struct _Gif Gif; typedef struct _Gif Gif;
typedef struct _GifClass GifClass; typedef struct _GifClass GifClass;
@ -88,13 +69,13 @@ struct _GifClass
#define GIF_TYPE (gif_get_type ()) #define GIF_TYPE (gif_get_type ())
#define GIF (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIF_TYPE, Gif)) #define GIF (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIF_TYPE, Gif))
GType gif_get_type (void) G_GNUC_CONST; GType gif_get_type (void) G_GNUC_CONST;
static GList * gif_query_procedures (GimpPlugIn *plug_in); static GList * gif_query_procedures (GimpPlugIn *plug_in);
static GimpProcedure * gif_create_procedure (GimpPlugIn *plug_in, static GimpProcedure * gif_create_procedure (GimpPlugIn *plug_in,
const gchar *name); const gchar *name);
static GimpValueArray * gif_save (GimpProcedure *procedure, static GimpValueArray * gif_save (GimpProcedure *procedure,
GimpRunMode run_mode, GimpRunMode run_mode,
GimpImage *image, GimpImage *image,
GimpDrawable *drawable, GimpDrawable *drawable,
@ -102,21 +83,22 @@ static GimpValueArray * gif_save (GimpProcedure *procedure,
const GimpValueArray *args, const GimpValueArray *args,
gpointer run_data); gpointer run_data);
static gboolean save_image (GFile *file, static gboolean save_image (GFile *file,
GimpImage *image, GimpImage *image,
GimpDrawable *drawable, GimpDrawable *drawable,
GimpImage *orig_image, GimpImage *orig_image,
GObject *config,
GError **error); GError **error);
static GimpPDBStatusType sanity_check (GFile *file, static GimpPDBStatusType sanity_check (GFile *file,
GimpImage **image, GimpImage **image,
GimpRunMode run_mode, GimpRunMode run_mode,
GError **error); GError **error);
static gboolean bad_bounds_dialog (void); static gboolean bad_bounds_dialog (void);
static gboolean save_dialog (GimpImage *image);
static void comment_entry_callback (GtkTextBuffer *buffer);
static gboolean save_dialog (GimpImage *image,
GimpProcedure *procedure,
GObject *config);
G_DEFINE_TYPE (Gif, gif, GIMP_TYPE_PLUG_IN) G_DEFINE_TYPE (Gif, gif, GIMP_TYPE_PLUG_IN)
@ -124,21 +106,7 @@ G_DEFINE_TYPE (Gif, gif, GIMP_TYPE_PLUG_IN)
GIMP_MAIN (GIF_TYPE) GIMP_MAIN (GIF_TYPE)
static gboolean comment_was_edited = FALSE; static gint Interlace; /* For compression code */
static gchar *globalcomment = NULL;
static gint Interlace; /* For compression code */
static GIFSaveVals gsvals =
{
FALSE, /* interlace */
TRUE, /* save comment */
TRUE, /* loop infinitely */
100, /* default_delay between frames (100ms) */
0, /* default_dispose = "don't care" */
FALSE, /* don't always use default_delay */
FALSE, /* don't always use default_dispose */
FALSE /* as_animation */
};
static void static void
@ -250,6 +218,18 @@ gif_create_procedure (GimpPlugIn *plug_in,
"(animated gif) Use specified disposal for all frames", "(animated gif) Use specified disposal for all frames",
FALSE, FALSE,
G_PARAM_READWRITE); G_PARAM_READWRITE);
GIMP_PROC_AUX_ARG_BOOLEAN (procedure, "save-comment",
"Save comment",
_("Save the image comment in the GIF file"),
gimp_export_comment (),
G_PARAM_READWRITE);
GIMP_PROC_AUX_ARG_STRING (procedure, "comment",
"Comment",
_("Image comment"),
gimp_get_default_comment (),
G_PARAM_READWRITE);
} }
return procedure; return procedure;
@ -264,15 +244,19 @@ gif_save (GimpProcedure *procedure,
const GimpValueArray *args, const GimpValueArray *args,
gpointer run_data) gpointer run_data)
{ {
GimpPDBStatusType status = GIMP_PDB_SUCCESS; GimpProcedureConfig *config;
GimpExportReturn export = GIMP_EXPORT_CANCEL; GimpPDBStatusType status = GIMP_PDB_SUCCESS;
GimpImage *orig_image; GimpExportReturn export = GIMP_EXPORT_CANCEL;
GimpImage *sanitized_image = NULL; GimpImage *orig_image;
GError *error = NULL; GimpImage *sanitized_image = NULL;
GError *error = NULL;
INIT_I18N (); INIT_I18N ();
gegl_init (NULL, NULL); gegl_init (NULL, NULL);
config = gimp_procedure_create_config (procedure);
gimp_procedure_config_begin_export (config, image, run_mode, args, NULL);
orig_image = image; orig_image = image;
if (run_mode == GIMP_RUN_INTERACTIVE || if (run_mode == GIMP_RUN_INTERACTIVE ||
@ -289,12 +273,9 @@ gif_save (GimpProcedure *procedure,
*/ */
sanitized_image = image; sanitized_image = image;
switch (run_mode) if (run_mode == GIMP_RUN_INTERACTIVE)
{ {
case GIMP_RUN_INTERACTIVE: if (! save_dialog (image, procedure, G_OBJECT (config)))
gimp_get_data (SAVE_PROC, &gsvals);
if (! save_dialog (image))
{ {
gimp_image_delete (sanitized_image); gimp_image_delete (sanitized_image);
@ -302,25 +283,6 @@ gif_save (GimpProcedure *procedure,
GIMP_PDB_CANCEL, GIMP_PDB_CANCEL,
NULL); NULL);
} }
break;
case GIMP_RUN_NONINTERACTIVE:
gsvals.interlace = GIMP_VALUES_GET_BOOLEAN (args, 0);
gsvals.save_comment = TRUE;
gsvals.loop = GIMP_VALUES_GET_BOOLEAN (args, 1);
gsvals.default_delay = GIMP_VALUES_GET_INT (args, 2);
gsvals.default_dispose = GIMP_VALUES_GET_INT (args, 3);
gsvals.as_animation = GIMP_VALUES_GET_BOOLEAN (args, 4);
gsvals.use_default_delay = GIMP_VALUES_GET_BOOLEAN (args, 5);
gsvals.use_default_dispose = GIMP_VALUES_GET_BOOLEAN (args, 6);
break;
case GIMP_RUN_WITH_LAST_VALS:
gimp_get_data (SAVE_PROC, &gsvals);
break;
default:
break;
} }
} }
@ -332,11 +294,18 @@ gif_save (GimpProcedure *procedure,
case GIMP_RUN_INTERACTIVE: case GIMP_RUN_INTERACTIVE:
case GIMP_RUN_WITH_LAST_VALS: case GIMP_RUN_WITH_LAST_VALS:
{ {
GimpExportCapabilities capabilities = (GIMP_EXPORT_CAN_HANDLE_INDEXED | GimpExportCapabilities capabilities;
GIMP_EXPORT_CAN_HANDLE_GRAY | gboolean as_animation;
GIMP_EXPORT_CAN_HANDLE_ALPHA);
if (gsvals.as_animation) capabilities = (GIMP_EXPORT_CAN_HANDLE_INDEXED |
GIMP_EXPORT_CAN_HANDLE_GRAY |
GIMP_EXPORT_CAN_HANDLE_ALPHA);
g_object_get (config,
"as-animation", &as_animation,
NULL);
if (as_animation)
capabilities |= GIMP_EXPORT_CAN_HANDLE_LAYERS; capabilities |= GIMP_EXPORT_CAN_HANDLE_LAYERS;
export = gimp_export_image (&image, &drawable, "GIF", export = gimp_export_image (&image, &drawable, "GIF",
@ -358,14 +327,8 @@ gif_save (GimpProcedure *procedure,
break; break;
} }
/* Write the image to file */ if (! save_image (file, image, drawable, orig_image, G_OBJECT (config),
if (save_image (file, image, drawable, orig_image, &error))
&error))
{
/* Store psvals data */
gimp_set_data (SAVE_PROC, &gsvals, sizeof (GIFSaveVals));
}
else
{ {
status = GIMP_PDB_EXECUTION_ERROR; status = GIMP_PDB_EXECUTION_ERROR;
} }
@ -373,6 +336,9 @@ gif_save (GimpProcedure *procedure,
gimp_image_delete (sanitized_image); gimp_image_delete (sanitized_image);
} }
gimp_procedure_config_end_export (config, image, file, status);
g_object_unref (config);
if (export == GIMP_EXPORT_EXPORT) if (export == GIMP_EXPORT_EXPORT)
gimp_image_delete (image); gimp_image_delete (image);
@ -638,7 +604,8 @@ parse_ms_tag (const gchar *str)
static gint static gint
parse_disposal_tag (const gchar *str) parse_disposal_tag (const gchar *str,
gint default_dispose)
{ {
gint offset = 0; gint offset = 0;
gint length; gint length;
@ -656,7 +623,7 @@ parse_disposal_tag (const gchar *str)
offset++; offset++;
} }
return gsvals.default_dispose; return default_dispose;
} }
@ -734,6 +701,7 @@ save_image (GFile *file,
GimpImage *image, GimpImage *image,
GimpDrawable *drawable, GimpDrawable *drawable,
GimpImage *orig_image, GimpImage *orig_image,
GObject *config,
GError **error) GError **error)
{ {
GeglBuffer *buffer; GeglBuffer *buffer;
@ -768,23 +736,32 @@ save_image (GFile *file,
guchar bgindex = 0; guchar bgindex = 0;
guint best_error = 0xFFFFFFFF; guint best_error = 0xFFFFFFFF;
/* Save the comment back to the ImageID, if appropriate */ gboolean config_interlace;
if (globalcomment != NULL && comment_was_edited) gboolean config_loop;
{ gint config_default_delay;
GimpParasite *parasite; gint config_default_dispose;
gboolean config_use_default_delay;
gboolean config_use_default_dispose;
gboolean config_as_animation;
gboolean config_save_comment;
gchar *config_comment;
parasite = gimp_parasite_new ("gimp-comment", g_object_get (config,
GIMP_PARASITE_PERSISTENT, "interlace", &config_interlace,
strlen (globalcomment) + 1, "loop", &config_loop,
(gpointer) globalcomment); "default-delay", &config_default_delay,
gimp_image_attach_parasite (orig_image, parasite); "default-dispose", &config_default_dispose,
gimp_parasite_free (parasite); "force-delay", &config_use_default_delay,
} "force-dispose", &config_use_default_dispose,
"as-animation", &config_as_animation,
"save-comment", &config_save_comment,
"comment", &config_comment,
NULL);
/* The GIF spec says 7bit ASCII for the comment block. */ /* The GIF spec says 7bit ASCII for the comment block. */
if (gsvals.save_comment && globalcomment) if (config_save_comment && config_comment && strlen (config_comment))
{ {
const gchar *c = globalcomment; const gchar *c = config_comment;
gint len; gint len;
for (len = strlen (c); len; c++, len--) for (len = strlen (c); len; c++, len--)
@ -794,13 +771,19 @@ save_image (GFile *file,
g_message (_("The GIF format only supports comments in " g_message (_("The GIF format only supports comments in "
"7bit ASCII encoding. No comment is saved.")); "7bit ASCII encoding. No comment is saved."));
g_free (globalcomment); g_free (config_comment);
globalcomment = NULL; config_comment = NULL;
config_save_comment = FALSE;
break; break;
} }
} }
} }
else
{
config_save_comment = FALSE;
}
/* get a list of layers for this image */ /* get a list of layers for this image */
layers = gimp_image_list_layers (image); layers = gimp_image_list_layers (image);
@ -815,7 +798,7 @@ save_image (GFile *file,
if (nlayers > 1) if (nlayers > 1)
is_gif89 = TRUE; is_gif89 = TRUE;
if (gsvals.save_comment) if (config_save_comment)
is_gif89 = TRUE; is_gif89 = TRUE;
switch (drawable_type) switch (drawable_type)
@ -935,7 +918,7 @@ save_image (GFile *file,
cols = gimp_image_width (image); cols = gimp_image_width (image);
rows = gimp_image_height (image); rows = gimp_image_height (image);
Interlace = gsvals.interlace; Interlace = config_interlace;
if (! gif_encode_header (output, is_gif89, cols, rows, bgindex, if (! gif_encode_header (output, is_gif89, cols, rows, bgindex,
BitsPerPixel, Red, Green, Blue, get_pixel, BitsPerPixel, Red, Green, Blue, get_pixel,
error)) error))
@ -945,14 +928,14 @@ save_image (GFile *file,
/* If the image has multiple layers it'll be made into an animated /* If the image has multiple layers it'll be made into an animated
* GIF, so write out the infinite-looping extension * GIF, so write out the infinite-looping extension
*/ */
if ((nlayers > 1) && (gsvals.loop)) if ((nlayers > 1) && (config_loop))
if (! gif_encode_loop_ext (output, 0, error)) if (! gif_encode_loop_ext (output, 0, error))
return FALSE; return FALSE;
/* Write comment extension - mustn't be written before the looping ext. */ /* Write comment extension - mustn't be written before the looping ext. */
if (gsvals.save_comment && globalcomment) if (config_save_comment && config_comment && strlen (config_comment))
{ {
if (! gif_encode_comment_ext (output, globalcomment, error)) if (! gif_encode_comment_ext (output, config_comment, error))
return FALSE; return FALSE;
} }
@ -1033,28 +1016,29 @@ save_image (GFile *file,
if (is_gif89) if (is_gif89)
{ {
if (i > 0 && ! gsvals.use_default_dispose) if (i > 0 && ! config_use_default_dispose)
{ {
layer_name = gimp_item_get_name (list->prev->data); layer_name = gimp_item_get_name (list->next->data);
Disposal = parse_disposal_tag (layer_name); Disposal = parse_disposal_tag (layer_name,
config_default_dispose);
g_free (layer_name); g_free (layer_name);
} }
else else
{ {
Disposal = gsvals.default_dispose; Disposal = config_default_dispose;
} }
layer_name = gimp_item_get_name (GIMP_ITEM (drawable)); layer_name = gimp_item_get_name (GIMP_ITEM (drawable));
Delay89 = parse_ms_tag (layer_name); Delay89 = parse_ms_tag (layer_name);
g_free (layer_name); g_free (layer_name);
if (Delay89 < 0 || gsvals.use_default_delay) if (Delay89 < 0 || config_use_default_delay)
Delay89 = (gsvals.default_delay + 5) / 10; Delay89 = (config_default_delay + 5) / 10;
else else
Delay89 = (Delay89 + 5) / 10; Delay89 = (Delay89 + 5) / 10;
/* don't allow a CPU-sucking completely 0-delay looping anim */ /* don't allow a CPU-sucking completely 0-delay looping anim */
if ((nlayers > 1) && gsvals.loop && (Delay89 == 0)) if ((nlayers > 1) && config_loop && (Delay89 == 0))
{ {
static gboolean onceonly = FALSE; static gboolean onceonly = FALSE;
@ -1079,7 +1063,7 @@ save_image (GFile *file,
} }
if (! gif_encode_image_data (output, cols, rows, if (! gif_encode_image_data (output, cols, rows,
(rows > 4) ? gsvals.interlace : 0, (rows > 4) ? config_interlace : 0,
useBPP, useBPP,
get_pixel, get_pixel,
offset_x, offset_y, offset_x, offset_y,
@ -1141,225 +1125,164 @@ bad_bounds_dialog (void)
return crop; return crop;
} }
static GtkWidget *
file_gif_toggle_button_init (GtkBuilder *builder,
const gchar *name,
gboolean initial_value,
gboolean *value_pointer)
{
GtkWidget *toggle = NULL;
toggle = GTK_WIDGET (gtk_builder_get_object (builder, name));
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), initial_value);
g_signal_connect (toggle, "toggled",
G_CALLBACK (gimp_toggle_button_update),
value_pointer);
return toggle;
}
static GtkWidget *
file_gif_spin_button_int_init (GtkBuilder *builder,
const gchar *name,
int initial_value,
int *value_pointer)
{
GtkWidget *spin_button = NULL;
GtkAdjustment *adjustment = NULL;
spin_button = GTK_WIDGET (gtk_builder_get_object (builder, name));
adjustment = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (spin_button));
gtk_adjustment_set_value (adjustment, initial_value);
g_signal_connect (adjustment, "value-changed",
G_CALLBACK (gimp_int_adjustment_update),
value_pointer);
return spin_button;
}
static void
file_gif_combo_box_int_update_value (GtkComboBox *combo,
gint *value)
{
GtkTreeIter iter;
if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo), &iter))
{
gtk_tree_model_get (gtk_combo_box_get_model (GTK_COMBO_BOX (combo)),
&iter,
DISPOSE_STORE_VALUE_COLUMN, value,
-1);
}
}
static GtkWidget *
file_gif_combo_box_int_init (GtkBuilder *builder,
const gchar *name,
int initial_value,
int *value_pointer,
const gchar *first_label,
gint first_value,
...)
{
GtkWidget *combo = NULL;
GtkListStore *store = NULL;
const gchar *label = NULL;
gint value = 0;
GtkTreeIter iter = { 0, };
va_list values;
combo = GTK_WIDGET (gtk_builder_get_object (builder, name));
store = GTK_LIST_STORE (gtk_combo_box_get_model (GTK_COMBO_BOX (combo)));
/* Populate */
va_start (values, first_value);
for (label = first_label, value = first_value;
label;
label = va_arg (values, const gchar *), value = va_arg (values, gint))
{
gtk_list_store_append (store, &iter);
gtk_list_store_set (store, &iter,
DISPOSE_STORE_VALUE_COLUMN, value,
DISPOSE_STORE_LABEL_COLUMN, label,
-1);
}
va_end (values);
/* Set initial value */
gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (store),
&iter,
NULL,
initial_value);
gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo), &iter);
/* Arrange update of value */
g_signal_connect (combo, "changed",
G_CALLBACK (file_gif_combo_box_int_update_value),
value_pointer);
return combo;
}
static gboolean static gboolean
save_dialog (GimpImage *image) save_dialog (GimpImage *image,
GimpProcedure *procedure,
GObject *config)
{ {
GtkBuilder *builder = NULL;
gchar *ui_file = NULL;
GError *error = NULL;
GtkWidget *dialog; GtkWidget *dialog;
GtkWidget *main_vbox;
GtkWidget *scrolled_win;
GtkWidget *text_view; GtkWidget *text_view;
GtkTextBuffer *text_buffer; GtkTextBuffer *text_buffer;
GtkWidget *toggle; GtkWidget *toggle;
GtkWidget *frame; GtkWidget *frame;
GimpParasite *GIF2_CMNT; gint32 n_layers;
gint32 nlayers; gboolean animation_supported;
gboolean animation_supported = FALSE;
gboolean run; gboolean run;
g_free (gimp_image_get_layers (image, &nlayers)); g_free (gimp_image_get_layers (image, &n_layers));
animation_supported = nlayers > 1; animation_supported = n_layers > 1;
dialog = gimp_export_dialog_new (_("GIF"), PLUG_IN_BINARY, SAVE_PROC); dialog = gimp_procedure_dialog_new (procedure,
GIMP_PROCEDURE_CONFIG (config),
_("Export Image as GIF"));
/* GtkBuilder init */ main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
builder = gtk_builder_new (); gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
ui_file = g_build_filename (gimp_data_directory (), gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
"ui/plug-ins/plug-in-file-gif.ui", main_vbox, TRUE, TRUE, 0);
NULL); gtk_widget_show (main_vbox);
if (! gtk_builder_add_from_file (builder, ui_file, &error))
g_printerr (_("Error loading UI file '%s':\n%s"),
ui_file, error ? error->message : "???");
g_free (ui_file);
/* Main vbox */ /* Interlace toggle */
gtk_box_pack_start (GTK_BOX (gimp_export_dialog_get_content_area (dialog)), toggle = gimp_prop_check_button_new (config, "interlace",
GTK_WIDGET (gtk_builder_get_object (builder, "main-vbox")), _("_Interlace"));
TRUE, TRUE, 0); gtk_box_pack_start (GTK_BOX (main_vbox), toggle, FALSE, FALSE, 0);
/* regular gif parameter settings */ /* Comment toggle and frame */
file_gif_toggle_button_init (builder, "interlace", frame = gimp_frame_new (NULL);
gsvals.interlace, &gsvals.interlace); gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
file_gif_toggle_button_init (builder, "save-comment", gtk_widget_show (frame);
gsvals.save_comment, &gsvals.save_comment);
file_gif_toggle_button_init (builder, "as-animation",
gsvals.as_animation, &gsvals.as_animation);
text_view = GTK_WIDGET (gtk_builder_get_object (builder, "comment")); toggle = gimp_prop_check_button_new (config, "save-comment",
text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view)); _("Save c_omment"));
gtk_frame_set_label_widget (GTK_FRAME (frame), toggle);
if (globalcomment) scrolled_win = gtk_scrolled_window_new (NULL, NULL);
g_free (globalcomment); gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_win),
GTK_SHADOW_IN);
gtk_container_add (GTK_CONTAINER (frame), scrolled_win);
gtk_widget_show (scrolled_win);
GIF2_CMNT = gimp_image_get_parasite (image, "gimp-comment"); text_view = gtk_text_view_new ();
if (GIF2_CMNT) gtk_container_add (GTK_CONTAINER (scrolled_win), text_view);
gtk_widget_show (text_view);
g_object_bind_property (config, "save-comment",
text_view, "sensitive",
G_BINDING_SYNC_CREATE);
#define MAX_COMMENT 240
text_buffer = gimp_prop_text_buffer_new (config, "comment", MAX_COMMENT);
gtk_text_view_set_buffer (GTK_TEXT_VIEW (text_view), text_buffer);
g_object_unref (text_buffer);
if (animation_supported)
{ {
globalcomment = g_strndup (gimp_parasite_data (GIF2_CMNT), GtkWidget *grid;
gimp_parasite_data_size (GIF2_CMNT)); GtkWidget *hbox;
gimp_parasite_free (GIF2_CMNT); GtkWidget *spin;
GtkWidget *label;
GtkListStore *store;
GtkWidget *combo;
/* Animation toggle and frame */
frame = gimp_frame_new (NULL);
gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
gtk_widget_show (frame);
toggle = gimp_prop_check_button_new (config, "as-animation",
_("As _animation"));
gtk_frame_set_label_widget (GTK_FRAME (frame), toggle);
grid = gtk_grid_new ();
gtk_grid_set_column_spacing (GTK_GRID (grid), 6);
gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
gtk_container_add (GTK_CONTAINER (frame), grid);
gtk_widget_show (grid);
g_object_bind_property (config, "as-animation",
grid, "sensitive",
G_BINDING_SYNC_CREATE);
/* Loop forever toggle */
toggle = gimp_prop_check_button_new (config, "loop",
_("_Loop forever"));
gtk_grid_attach (GTK_GRID (grid), toggle, 0, 0, 2, 1);
/* Default delay spin */
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
gimp_grid_attach_aligned (GTK_GRID (grid), 0, 1,
_("_Delay between frames where unspecified:"),
0.0, 0.5,
hbox, 1);
spin = gimp_prop_spin_button_new (config, "default-delay",
1, 10, 0);
gtk_box_pack_start (GTK_BOX (hbox), spin, FALSE, FALSE, 0);
label = gtk_label_new (_("milliseconds"));
gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
gtk_widget_show (label);
store = gimp_int_store_new (_("I don't care"),
DISPOSE_UNSPECIFIED,
_("Cumulative layers (combine)"),
DISPOSE_COMBINE,
_("One frame per layer (replace)"),
DISPOSE_REPLACE,
NULL);
combo = gimp_prop_int_combo_box_new (config, "default-dispose",
GIMP_INT_STORE (store));
g_object_unref (store);
gimp_grid_attach_aligned (GTK_GRID (grid), 0, 2,
_("_Frame disposal where unspecified"),
0.0, 0.5,
combo, 1);
/* The "Always use default values" toggles */
toggle = gimp_prop_check_button_new (config, "force-delay",
_("_Use delay entered above "
"for all frames"));
gtk_grid_attach (GTK_GRID (grid), toggle, 0, 3, 2, 1);
toggle = gimp_prop_check_button_new (config, "force-dispose",
_("U_se disposal entered above "
"for all frames"));
gtk_grid_attach (GTK_GRID (grid), toggle, 0, 4, 2, 1);
} }
else else
{ {
globalcomment = gimp_get_default_comment (); GtkWidget *hint;
frame = gimp_frame_new (_("Animated GIF"));
gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
gtk_widget_show (frame);
hint = gimp_hint_box_new (_("You can only export as animation when the "
"image has more than one layer.\n"
"The image you are trying to export only "
"has one layer."));
gtk_container_add (GTK_CONTAINER (frame), hint);
gtk_widget_show (hint);
} }
if (globalcomment)
gtk_text_buffer_set_text (text_buffer, globalcomment, -1);
g_signal_connect (text_buffer, "changed",
G_CALLBACK (comment_entry_callback),
NULL);
/* additional animated gif parameter settings */
file_gif_toggle_button_init (builder, "loop-forever",
gsvals.loop, &gsvals.loop);
/* default_delay entry field */
file_gif_spin_button_int_init (builder, "delay-spin",
gsvals.default_delay, &gsvals.default_delay);
/* Disposal selector */
file_gif_combo_box_int_init (builder, "dispose-combo",
gsvals.default_dispose, &gsvals.default_dispose,
_("I don't care"),
DISPOSE_UNSPECIFIED,
_("Cumulative layers (combine)"),
DISPOSE_COMBINE,
_("One frame per layer (replace)"),
DISPOSE_REPLACE,
NULL);
/* The "Always use default values" toggles */
file_gif_toggle_button_init (builder, "use-default-delay",
gsvals.use_default_delay,
&gsvals.use_default_delay);
file_gif_toggle_button_init (builder, "use-default-dispose",
gsvals.use_default_dispose,
&gsvals.use_default_dispose);
frame = GTK_WIDGET (gtk_builder_get_object (builder, "animation-frame"));
toggle = GTK_WIDGET (gtk_builder_get_object (builder, "as-animation"));
if (! animation_supported)
{
gimp_help_set_help_data (toggle,
_("You can only export as animation when the "
"image has more than one layer. The image "
"you are trying to export only has one "
"layer."),
NULL);
/* Make sure the checkbox is not checked from session data. */
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), FALSE);
}
gtk_widget_set_sensitive (toggle, animation_supported);
g_object_bind_property (toggle, "active",
frame, "sensitive",
G_BINDING_SYNC_CREATE);
gtk_widget_show (dialog); gtk_widget_show (dialog);
run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK); run = gimp_procedure_dialog_run (GIMP_PROCEDURE_DIALOG (dialog));
gtk_widget_destroy (dialog); gtk_widget_destroy (dialog);
@ -2540,41 +2463,3 @@ char_flush (GOutputStream *output,
return TRUE; return TRUE;
} }
/* Save interface functions */
static void
comment_entry_callback (GtkTextBuffer *buffer)
{
GtkTextIter start_iter;
GtkTextIter end_iter;
gchar *text;
gtk_text_buffer_get_bounds (buffer, &start_iter, &end_iter);
text = gtk_text_buffer_get_text (buffer, &start_iter, &end_iter, FALSE);
#define MAX_COMMENT 240
if (strlen (text) > MAX_COMMENT)
{
/* translators: the %d is *always* 240 here */
g_message (_("The default comment is limited to %d characters."),
MAX_COMMENT);
gtk_text_buffer_get_iter_at_offset (buffer, &start_iter, MAX_COMMENT - 1);
gtk_text_buffer_get_end_iter (buffer, &end_iter);
/* this calls us recursivaly, but in the else branch
*/
gtk_text_buffer_delete (buffer, &start_iter, &end_iter);
}
else
{
g_free (globalcomment);
globalcomment = g_strdup (text);
comment_was_edited = TRUE;
}
g_free (text);
}

View File

@ -1,7 +1,6 @@
uidatadir = $(gimpdatadir)/ui/plug-ins uidatadir = $(gimpdatadir)/ui/plug-ins
uidata_DATA = \ uidata_DATA = \
plug-in-file-gif.ui \
plug-in-file-tiff.ui \ plug-in-file-tiff.ui \
plug-in-metadata-editor.ui \ plug-in-metadata-editor.ui \
plug-in-metadata-editor-calendar.ui \ plug-in-metadata-editor-calendar.ui \

View File

@ -1,5 +1,4 @@
plugin_ui = [ plugin_ui = [
'plug-in-file-gif.ui',
'plug-in-file-tiff.ui', 'plug-in-file-tiff.ui',
'plug-in-metadata-editor-calendar.ui', 'plug-in-metadata-editor-calendar.ui',
'plug-in-metadata-editor.ui', 'plug-in-metadata-editor.ui',

View File

@ -1,328 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.1 -->
<interface>
<requires lib="gtk+" version="3.0"/>
<object class="GtkAdjustment" id="delay">
<property name="lower">10</property>
<property name="upper">65000</property>
<property name="value">100</property>
<property name="step_increment">10</property>
<property name="page_increment">100</property>
</object>
<object class="GtkListStore" id="dispose-store">
<columns>
<!-- column-name dispose-mode -->
<column type="gint"/>
<!-- column-name dispose-mode-label -->
<column type="gchararray"/>
</columns>
</object>
<object class="GtkBox" id="main-vbox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="border_width">12</property>
<property name="orientation">vertical</property>
<property name="spacing">12</property>
<child>
<object class="GtkFrame" id="gif-options-frame">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label_xalign">0</property>
<property name="shadow_type">none</property>
<child>
<object class="GtkBox" id="gif-options-frame-vbox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">12</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkCheckButton" id="interlace">
<property name="label" translatable="yes">I_nterlace</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkBox" id="comment-hbox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkBox" id="vbox1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkCheckButton" id="save-comment">
<property name="label" translatable="yes">_GIF comment:</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkScrolledWindow" id="comment-scrolled-window">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkTextView" id="comment">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="pixels_below_lines">1</property>
<property name="wrap_mode">word</property>
</object>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="as-animation">
<property name="label" translatable="yes">As _animation</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
</child>
<child type="label">
<object class="GtkLabel" id="frame">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">GIF Options</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkFrame" id="animation-frame">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label_xalign">0</property>
<property name="shadow_type">none</property>
<child>
<object class="GtkBox" id="animation-options-vbox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">12</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkCheckButton" id="loop-forever">
<property name="label" translatable="yes">_Loop forever</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkBox" id="delay-hbox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkLabel" id="delay-label-begin">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">GIF supports hundredths of a second precision.</property>
<property name="label" translatable="yes">_Delay between frames where unspecified:</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">delay-spin</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GimpSpinButton" id="delay-spin">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="tooltip_text" translatable="yes">GIF supports hundredths of a second precision.</property>
<property name="invisible_char">●</property>
<property name="text" translatable="yes">10</property>
<property name="shadow_type">none</property>
<property name="adjustment">delay</property>
<property name="climb_rate">1</property>
<property name="snap_to_ticks">True</property>
<property name="value">10</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="delay-label-end">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">milliseconds</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkBox" id="frame-disposal-hbox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkLabel" id="disposal-label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">_Frame disposal where unspecified:</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">dispose-combo</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkComboBox" id="dispose-combo">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="model">dispose-store</property>
<child>
<object class="GtkCellRendererText" id="text-renderer"/>
<attributes>
<attribute name="text">1</attribute>
</attributes>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="use-default-delay">
<property name="label" translatable="yes">_Use delay entered above for all frames</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="use-default-dispose">
<property name="label" translatable="yes">U_se disposal entered above for all frames</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">4</property>
</packing>
</child>
</object>
</child>
<child type="label">
<object class="GtkLabel" id="animated-options-frame-label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Animated GIF Options</property>
<property name="yalign">0.49000000953674316</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</object>
</interface>

View File

@ -143,7 +143,6 @@ plug-ins/gimpressionist/sizemap.c
plug-ins/gimpressionist/utils.c plug-ins/gimpressionist/utils.c
plug-ins/goat-exercises/goat-exercise-c.c plug-ins/goat-exercises/goat-exercise-c.c
plug-ins/goat-exercises/goat-exercise-py3.py plug-ins/goat-exercises/goat-exercise-py3.py
[type: gettext/glade]plug-ins/ui/plug-in-file-gif.ui
[type: gettext/glade]plug-ins/ui/plug-in-file-tiff.ui [type: gettext/glade]plug-ins/ui/plug-in-file-tiff.ui
[type: gettext/glade]plug-ins/ui/plug-in-metadata-editor.ui [type: gettext/glade]plug-ins/ui/plug-in-metadata-editor.ui
[type: gettext/glade]plug-ins/ui/plug-in-metadata-viewer.ui [type: gettext/glade]plug-ins/ui/plug-in-metadata-viewer.ui