gimp/app/gui/image-commands.c
Michael Natterer 8d86ec25e0 Move away from creating all item_factories statically in menus_init() but
2003-01-10  Michael Natterer  <mitch@gimp.org>

	Move away from creating all item_factories statically in
	menus_init() but create a new one for each place where one is
	needed:

	* app/widgets/Makefile.am
	* app/widgets/widgets-types.h
	* app/widgets/gimpmenufactory.[ch]: new factory which creates and
	configures the GimpItemFactories it knows about on-the-fly.

	* app/widgets/gimpitemfactory.[ch]: added
	gimp_item_factory_update() which calls the "update_func". Added
	"gboolean update_on_popup" so item_factories can be configured to
	require manual updates (used for the <Image> factory).

	* app/gui/menus.[ch]: create a "global_menu_factory" and register
	all menus we have with it. Added various setup functions which
	do stuff like adding the "Open Recent" menu or reorder plug-in
	menu entries. Removed the debugging stuff...

	* app/gui/Makefile.am
	* app/gui/debug-commands.[ch]: ...and added it here.

	* app/gui/gui.c: create the <Toolbox>, the popup-<Image> and the
	<Paths> factories here because they are still global.

	* app/gui/plug-in-menus.[ch]: changed the "image_factory"
	parameters to "item_factory" and create/update the entries for the
	passed item_factory only. Makes the whole stuff much more
	straightforward.

	* app/plug-in/plug-ins.c: don't call plug_in_make_menu().

	* app/display/gimpdisplay.[ch]
	* app/display/gimpdisplayshell.[ch]: added "menu_factory" and
	"popup_factory" parameters to gimp_display_new() and
	gimp_display_shell_new(). Create the menubar_factory and the
	qmask_factory dynamically. Pass the shell, not a Gimp to the QMask
	callbacks. Changed gimp_display_shell_set_menu_sensitivity() to
	gimp_display_shell_menu_update() and don't call it directly (it's
	a GimpItemFactory update_func now). Call gimp_item_factory_update()
	on the resp. factories instead.

	* app/gui/qmask-commands.c
	* app/display/gimpdisplayshell-callbacks.c
	* app/tools/gimpimagemaptool.c: changed accordingly.

	* app/widgets/gimpbrusheditor.c
	* app/widgets/gimpbrushfactoryview.[ch]
	* app/widgets/gimpbufferview.[ch]
	* app/widgets/gimpcolormapeditor.[ch]
	* app/widgets/gimpcontainereditor.[ch]
	* app/widgets/gimpdataeditor.[ch]
	* app/widgets/gimpdatafactoryview.[ch]
	* app/widgets/gimpdialogfactory.[ch]
	* app/widgets/gimpdock.c
	* app/widgets/gimpdockbook.[ch]
	* app/widgets/gimpdocumentview.[ch]
	* app/widgets/gimpgradienteditor.[ch]
	* app/widgets/gimpimageview.[ch]
	* app/widgets/gimpitemlistview.[ch]
	* app/widgets/gimppaletteeditor.[ch]: pass around lots of
	GimpMenuFactory pointers and menu_identifiers so all views can
	create their item_factories themselves. Unref the factories when
	they are no longer needed because they belong to the views now.

	* app/gui/dialogs-commands.c
	* app/gui/dialogs-constructors.c
	* app/gui/dialogs.c
	* app/gui/brush-select.c
	* app/gui/gradient-select.c
	* app/gui/palette-select.c
	* app/gui/pattern-select.c: changed accordingly.

	* app/gui/file-dialog-utils.[ch] (file_dialog_new): require
	menu_factory and menu_identifier parameters.

	* app/gui/file-open-dialog.[ch]
	* app/gui/file-save-dialog.[ch]: removed file_*_dialog_menu_init()
	(they went to menus.c as setup_funcs). Added file_*_dialog_set_type()
	and moved the <Load> and <Save> factory callbacks to file-commands.c

	* app/gui/file-commands.[ch]: changed accordingly.

	* app/gui/view-commands.c: changed the statusbar, menubar, rulers
	and guides callbacks to do their job only if the setting has
	actually changed. Don't update whole item factories afterwards.
	Instead, just change the state of the items that actually need
	update.

	Unrelated:

	* app/core/gimpchannel.c (gimp_channel_init): set "bounds_known"
	and friends to FALSE since we don't know that the new channel will
	be empty (fixes QMask and probably other stuff).

	* app/gui/image-commands.c
	* app/gui/vectors-commands.c: cleanup.
2003-01-10 17:55:53 +00:00

536 lines
14 KiB
C

/* The GIMP -- an image manipulation program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* 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"
#include <gtk/gtk.h>
#include "libgimpwidgets/gimpwidgets.h"
#include "gui-types.h"
#include "core/gimp.h"
#include "core/gimpcontext.h"
#include "core/gimpimage.h"
#include "core/gimpimage-crop.h"
#include "core/gimpimage-duplicate.h"
#include "core/gimpimage-mask.h"
#include "core/gimpimage-merge.h"
#include "core/gimpimage-resize.h"
#include "core/gimpimage-scale.h"
#include "widgets/gimpviewabledialog.h"
#include "display/gimpdisplay.h"
#include "display/gimpdisplayshell.h"
#include "display/gimpprogress.h"
#include "convert-dialog.h"
#include "image-commands.h"
#include "resize-dialog.h"
#include "undo.h"
#include "libgimp/gimpintl.h"
typedef struct
{
Resize *resize;
GimpDisplay *gdisp;
GimpImage *gimage;
} ImageResize;
#define return_if_no_display(gdisp,data) \
if (GIMP_IS_DISPLAY (data)) \
gdisp = data; \
else if (GIMP_IS_GIMP (data)) \
gdisp = gimp_context_get_display (gimp_get_user_context (GIMP (data))); \
else \
gdisp = NULL; \
if (! gdisp) \
return
#define return_if_no_image(gimage,data) \
if (GIMP_IS_DISPLAY (data)) \
gimage = ((GimpDisplay *) data)->gimage; \
else if (GIMP_IS_GIMP (data)) \
gimage = gimp_context_get_image (gimp_get_user_context (GIMP (data))); \
else \
gimage = NULL; \
if (! gimage) \
return
/* local functions */
static void image_resize_callback (GtkWidget *widget,
gpointer data);
static void image_scale_callback (GtkWidget *widget,
gpointer data);
static void image_scale_warn_callback (GtkWidget *widget,
gboolean do_scale,
gpointer data);
static void image_scale_implement (ImageResize *image_scale);
/* public functions */
void
image_convert_rgb_cmd_callback (GtkWidget *widget,
gpointer data)
{
GimpImage *gimage;
return_if_no_image (gimage, data);
convert_to_rgb (gimage);
}
void
image_convert_grayscale_cmd_callback (GtkWidget *widget,
gpointer data)
{
GimpImage *gimage;
return_if_no_image (gimage, data);
convert_to_grayscale (gimage);
}
void
image_convert_indexed_cmd_callback (GtkWidget *widget,
gpointer data)
{
GimpImage *gimage;
return_if_no_image (gimage, data);
convert_to_indexed (gimage);
}
void
image_resize_cmd_callback (GtkWidget *widget,
gpointer data)
{
GimpDisplay *gdisp;
GimpImage *gimage;
ImageResize *image_resize;
return_if_no_display (gdisp, data);
gimage = gdisp->gimage;
image_resize = g_new0 (ImageResize, 1);
image_resize->gdisp = gdisp;
image_resize->gimage = gimage;
image_resize->resize =
resize_widget_new (GIMP_VIEWABLE (gimage),
ResizeWidget,
gimage->width,
gimage->height,
gimage->xresolution,
gimage->yresolution,
gimage->unit,
GIMP_DISPLAY_SHELL (gdisp->shell)->dot_for_dot,
G_CALLBACK (image_resize_callback),
image_resize);
g_signal_connect_object (gdisp, "disconnect",
G_CALLBACK (gtk_widget_destroy),
image_resize->resize->resize_shell,
G_CONNECT_SWAPPED);
g_object_weak_ref (G_OBJECT (image_resize->resize->resize_shell),
(GWeakNotify) g_free,
image_resize);
gtk_widget_show (image_resize->resize->resize_shell);
}
void
image_scale_cmd_callback (GtkWidget *widget,
gpointer data)
{
GimpDisplay *gdisp;
GimpImage *gimage;
ImageResize *image_scale;
return_if_no_display (gdisp, data);
gimage = gdisp->gimage;
image_scale = g_new0 (ImageResize, 1);
image_scale->gdisp = gdisp;
image_scale->gimage = gimage;
image_scale->resize =
resize_widget_new (GIMP_VIEWABLE (gimage),
ScaleWidget,
gimage->width,
gimage->height,
gimage->xresolution,
gimage->yresolution,
gimage->unit,
GIMP_DISPLAY_SHELL (gdisp->shell)->dot_for_dot,
G_CALLBACK (image_scale_callback),
image_scale);
g_signal_connect_object (gdisp, "disconnect",
G_CALLBACK (gtk_widget_destroy),
image_scale->resize->resize_shell,
G_CONNECT_SWAPPED);
g_object_weak_ref (G_OBJECT (image_scale->resize->resize_shell),
(GWeakNotify) g_free,
image_scale);
gtk_widget_show (image_scale->resize->resize_shell);
}
void
image_crop_cmd_callback (GtkWidget *widget,
gpointer data)
{
GimpDisplay *gdisp;
gint x1, y1, x2, y2;
return_if_no_display (gdisp, data);
if (! gimp_image_mask_bounds (gdisp->gimage, &x1, &y1, &x2, &y2))
{
g_message (_("Cannot crop because the current selection is empty."));
return;
}
gimp_image_crop (gdisp->gimage, x1, y1, x2, y2, FALSE, TRUE);
}
void
image_duplicate_cmd_callback (GtkWidget *widget,
gpointer data)
{
GimpImage *gimage;
GimpImage *new_gimage;
return_if_no_image (gimage, data);
new_gimage = gimp_image_duplicate (gimage);
gimp_create_display (new_gimage->gimp, new_gimage, 0x0101);
g_object_unref (new_gimage);
}
void
image_merge_layers_cmd_callback (GtkWidget *widget,
gpointer data)
{
GimpImage *gimage;
return_if_no_image (gimage, data);
image_layers_merge_query (gimage, TRUE);
}
void
image_flatten_image_cmd_callback (GtkWidget *widget,
gpointer data)
{
GimpImage *gimage;
return_if_no_image (gimage, data);
gimp_image_flatten (gimage);
gimp_image_flush (gimage);
}
/****************************/
/* The layer merge dialog */
/****************************/
typedef struct _LayerMergeOptions LayerMergeOptions;
struct _LayerMergeOptions
{
GtkWidget *query_box;
GimpImage *gimage;
gboolean merge_visible;
GimpMergeType merge_type;
};
static void
image_layers_merge_query_ok_callback (GtkWidget *widget,
gpointer data)
{
LayerMergeOptions *options;
GimpImage *gimage;
options = (LayerMergeOptions *) data;
if (! (gimage = options->gimage))
return;
if (options->merge_visible)
gimp_image_merge_visible_layers (gimage, options->merge_type);
gimp_image_flush (gimage);
gtk_widget_destroy (options->query_box);
}
void
image_layers_merge_query (GimpImage *gimage,
/* if FALSE, anchor active layer */
gboolean merge_visible)
{
LayerMergeOptions *options;
GtkWidget *vbox;
GtkWidget *frame;
/* The new options structure */
options = g_new (LayerMergeOptions, 1);
options->gimage = gimage;
options->merge_visible = merge_visible;
options->merge_type = GIMP_EXPAND_AS_NECESSARY;
/* The dialog */
options->query_box =
gimp_viewable_dialog_new (GIMP_VIEWABLE (gimage),
_("Merge Layers"), "layers_merge_options",
GIMP_STOCK_MERGE_DOWN,
_("Layers Merge Options"),
gimp_standard_help_func,
"dialogs/layers/merge_visible_layers.html",
GTK_STOCK_CANCEL, gtk_widget_destroy,
NULL, 1, NULL, FALSE, TRUE,
GTK_STOCK_OK, image_layers_merge_query_ok_callback,
options, NULL, NULL, TRUE, FALSE,
NULL);
g_object_weak_ref (G_OBJECT (options->query_box),
(GWeakNotify) g_free,
options);
/* The main vbox */
vbox = gtk_vbox_new (FALSE, 2);
gtk_container_set_border_width (GTK_CONTAINER (vbox), 4);
gtk_container_add (GTK_CONTAINER (GTK_DIALOG (options->query_box)->vbox),
vbox);
frame =
gimp_radio_group_new2 (TRUE,
merge_visible ?
_("Final, Merged Layer should be:") :
_("Final, Anchored Layer should be:"),
G_CALLBACK (gimp_radio_button_update),
&options->merge_type,
GINT_TO_POINTER (options->merge_type),
_("Expanded as necessary"),
GINT_TO_POINTER (GIMP_EXPAND_AS_NECESSARY), NULL,
_("Clipped to image"),
GINT_TO_POINTER (GIMP_CLIP_TO_IMAGE), NULL,
_("Clipped to bottom layer"),
GINT_TO_POINTER (GIMP_CLIP_TO_BOTTOM_LAYER), NULL,
NULL);
gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
gtk_widget_show (frame);
gtk_widget_show (vbox);
gtk_widget_show (options->query_box);
}
/* private functions */
static void
image_resize_callback (GtkWidget *widget,
gpointer data)
{
ImageResize *image_resize;
image_resize = (ImageResize *) data;
g_assert (image_resize != NULL);
g_assert (image_resize->gimage != NULL);
gtk_widget_set_sensitive (image_resize->resize->resize_shell, FALSE);
if (image_resize->resize->width > 0 &&
image_resize->resize->height > 0)
{
gimp_image_resize (image_resize->gimage,
image_resize->resize->width,
image_resize->resize->height,
image_resize->resize->offset_x,
image_resize->resize->offset_y);
gimp_image_flush (image_resize->gimage);
}
else
{
g_message (_("Resize Error: Both width and height must be "
"greater than zero."));
}
gtk_widget_destroy (image_resize->resize->resize_shell);
}
static void
image_scale_callback (GtkWidget *widget,
gpointer data)
{
ImageResize *image_scale;
image_scale = (ImageResize *) data;
g_assert (image_scale != NULL);
g_assert (image_scale->gimage != NULL);
gtk_widget_set_sensitive (image_scale->resize->resize_shell, FALSE);
if (gimp_image_check_scaling (image_scale->gimage,
image_scale->resize->width,
image_scale->resize->height))
{
image_scale_implement (image_scale);
gtk_widget_destroy (image_scale->resize->resize_shell);
}
else
{
GtkWidget *dialog;
dialog =
gimp_query_boolean_box (_("Layer Too Small"),
gimp_standard_help_func,
"dialogs/scale_layer_warn.html",
GTK_STOCK_DIALOG_QUESTION,
_("The chosen image size will shrink\n"
"some layers completely away.\n"
"Is this what you want?"),
GTK_STOCK_OK, GTK_STOCK_CANCEL,
G_OBJECT (image_scale->resize->resize_shell),
"destroy",
image_scale_warn_callback,
image_scale);
gtk_widget_show (dialog);
}
}
static void
image_scale_warn_callback (GtkWidget *widget,
gboolean do_scale,
gpointer data)
{
ImageResize *image_scale;
GimpImage *gimage;
image_scale = (ImageResize *) data;
gimage = image_scale->gimage;
if (do_scale) /* User doesn't mind losing layers... */
{
image_scale_implement (image_scale);
gtk_widget_destroy (image_scale->resize->resize_shell);
}
else
{
gtk_widget_set_sensitive (image_scale->resize->resize_shell, TRUE);
}
}
static void
image_scale_implement (ImageResize *image_scale)
{
GimpImage *gimage = NULL;
gboolean display_flush = FALSE; /* this is a bit ugly:
we hijack the flush variable
to check if an undo_group was
already started */
g_assert (image_scale != NULL);
g_assert (image_scale->gimage != NULL);
gimage = image_scale->gimage;
if (image_scale->resize->resolution_x != gimage->xresolution ||
image_scale->resize->resolution_y != gimage->yresolution)
{
undo_push_group_start (gimage, IMAGE_SCALE_UNDO_GROUP);
gimp_image_set_resolution (gimage,
image_scale->resize->resolution_x,
image_scale->resize->resolution_y);
display_flush = TRUE;
}
if (image_scale->resize->unit != gimage->unit)
{
if (! display_flush)
undo_push_group_start (gimage, IMAGE_SCALE_UNDO_GROUP);
gimp_image_set_unit (gimage, image_scale->resize->unit);
display_flush = TRUE;
}
if (image_scale->resize->width != gimage->width ||
image_scale->resize->height != gimage->height)
{
if (image_scale->resize->width > 0 &&
image_scale->resize->height > 0)
{
GimpProgress *progress;
if (! display_flush)
undo_push_group_start (gimage, IMAGE_SCALE_UNDO_GROUP);
progress = gimp_progress_start (image_scale->gdisp,
_("Scaling..."),
TRUE, NULL, NULL);
gimp_image_scale (gimage,
image_scale->resize->width,
image_scale->resize->height,
image_scale->resize->interpolation,
gimp_progress_update_and_flush, progress);
gimp_progress_end (progress);
display_flush = TRUE;
}
else
{
g_message (_("Scale Error: Both width and height must be "
"greater than zero."));
return;
}
}
if (display_flush)
{
undo_push_group_end (gimage);
gimp_image_flush (gimage);
}
}