gimp/app/actions/image-commands.c
Michael Natterer c49df22eef Action code review and pre-release consistency cleanup:
2004-10-18  Michael Natterer  <mitch@gimp.org>

	Action code review and pre-release consistency cleanup:

	* app/actions/*-actions.c: added some missing and resolved
	conflicting mnemonics, added missing help IDs. Cleaned up the
	*_actions_update() functions.

	* app/actions/channels-actions.c
	* app/actions/layers-actions.c
	* app/actions/vectors-actions.c (*_actions_update): simplified
	the code that figures the prev and next channel,layer,vectors.

	* app/actions/qmask-actions.c: use the same accelerator for
	"qmask-active" and "qmask-toggle". Fixed action sensitivity.

	* app/actions/channels-commands.c
	* app/actions/dockable-commands.c
	* app/actions/documents-commands.c
	* app/actions/gradients-commands.c
	* app/actions/layers-commands.c
	* app/actions/palettes-commands.c
	* app/actions/image-commands.c
	* app/actions/select-commands.c
	* app/actions/vectors-commands.c: folded tons of private utility
	functions into their only callers (they used to be public and
	called from outside before the switch to action based menus).
	Renamed functions and variables saying "query" or "qbox" to
	"dialog". Moved static functions to the end of the files. Misc
	minor cleanups.

	* app/actions/drawable-actions.c
	* app/actions/drawable-commands.c: made the "drawable-visible" and
	"drawable-linked" actions affect the layer if the active drawable
	is a layer mask.

	* app/actions/select-commands.c: added action to stroke with the
	last values used in an attempt to address bug #135746 but #if 0'ed
	it because the approach is too ugly.

	* app/tools/gimpiscissorstool.c: changed mnemonic from I to S.

	* menus/image-menu-xml.in: added more stuff to the (commented out)
	"context" menu.
2004-10-18 11:29:58 +00:00

692 lines
20 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 "libgimpbase/gimpbase.h"
#include "libgimpwidgets/gimpwidgets.h"
#include "actions-types.h"
#include "config/gimpguiconfig.h"
#include "core/core-enums.h"
#include "core/gimp.h"
#include "core/gimpchannel.h"
#include "core/gimpcontext.h"
#include "core/gimpimage.h"
#include "core/gimpimage-convert.h"
#include "core/gimpimage-crop.h"
#include "core/gimpimage-duplicate.h"
#include "core/gimpimage-flip.h"
#include "core/gimpimage-merge.h"
#include "core/gimpimage-resize.h"
#include "core/gimpimage-rotate.h"
#include "core/gimpimage-scale.h"
#include "core/gimpimage-undo.h"
#include "core/gimpprogress.h"
#include "widgets/gimpdialogfactory.h"
#include "widgets/gimpdock.h"
#include "widgets/gimphelp-ids.h"
#include "widgets/gimpmessagebox.h"
#include "widgets/gimpmessagedialog.h"
#include "widgets/gimpviewabledialog.h"
#include "display/gimpdisplay.h"
#include "display/gimpdisplayshell.h"
#include "dialogs/convert-dialog.h"
#include "dialogs/dialogs.h"
#include "dialogs/grid-dialog.h"
#include "dialogs/image-new-dialog.h"
#include "dialogs/resize-dialog.h"
#include "actions.h"
#include "image-commands.h"
#include "gimp-intl.h"
typedef struct _ImageResizeOptions ImageResizeOptions;
struct _ImageResizeOptions
{
ResizeDialog *dialog;
GimpContext *context;
GimpDisplay *gdisp;
GimpImage *gimage;
};
typedef struct _LayerMergeOptions LayerMergeOptions;
struct _LayerMergeOptions
{
GimpContext *context;
GimpImage *gimage;
GimpMergeType merge_type;
};
/* local function prototypes */
static void image_resize_callback (GtkWidget *widget,
gpointer data);
static void image_scale_callback (GtkWidget *widget,
gpointer data);
static void image_scale_confirm_large (ImageResizeOptions *options,
gint64 new_memsize,
gint64 max_memsize);
static void image_scale_confirm_small (ImageResizeOptions *options);
static void image_scale_confirm_response (GtkWidget *widget,
gint response_id,
ImageResizeOptions *options);
static void image_scale_implement (ImageResizeOptions *options);
static void image_merge_layers_response (GtkWidget *dialog,
gint response_id,
LayerMergeOptions *options);
/* public functions */
void
image_new_cmd_callback (GtkAction *action,
gpointer data)
{
GtkWidget *widget;
GtkWidget *dialog;
return_if_no_widget (widget, data);
dialog = gimp_dialog_factory_dialog_new (global_dialog_factory,
gtk_widget_get_screen (widget),
"gimp-image-new-dialog", -1, FALSE);
if (dialog)
{
image_new_dialog_set (dialog, NULL, NULL);
gtk_window_present (GTK_WINDOW (dialog));
}
}
void
image_new_from_image_cmd_callback (GtkAction *action,
gpointer data)
{
GtkWidget *widget;
GtkWidget *dialog;
return_if_no_widget (widget, data);
dialog = gimp_dialog_factory_dialog_new (global_dialog_factory,
gtk_widget_get_screen (widget),
"gimp-image-new-dialog", -1, FALSE);
if (dialog)
{
GimpImage *gimage = action_data_get_image (data);
if (gimage)
image_new_dialog_set (dialog, gimage, NULL);
gtk_window_present (GTK_WINDOW (dialog));
}
}
void
image_convert_cmd_callback (GtkAction *action,
gint value,
gpointer data)
{
GimpImage *gimage;
GtkWidget *widget;
GimpDisplay *gdisp;
return_if_no_image (gimage, data);
return_if_no_widget (widget, data);
return_if_no_display (gdisp, data);
switch ((GimpImageBaseType) value)
{
case GIMP_RGB:
case GIMP_GRAY:
gimp_image_convert (gimage, (GimpImageBaseType) value,
0, 0, FALSE, FALSE, 0, NULL, NULL);
gimp_image_flush (gimage);
break;
case GIMP_INDEXED:
gtk_widget_show (convert_dialog_new (gimage, widget,
GIMP_PROGRESS (gdisp)));
break;
}
}
void
image_resize_cmd_callback (GtkAction *action,
gpointer data)
{
ImageResizeOptions *options;
GimpDisplay *gdisp;
GimpImage *gimage;
return_if_no_display (gdisp, data);
gimage = gdisp->gimage;
options = g_new0 (ImageResizeOptions, 1);
options->context = action_data_get_context (data);
options->gdisp = gdisp;
options->gimage = gimage;
options->dialog =
resize_dialog_new (GIMP_VIEWABLE (gimage), gdisp->shell,
RESIZE_DIALOG,
gimage->width,
gimage->height,
gimage->xresolution,
gimage->yresolution,
GIMP_DISPLAY_SHELL (gdisp->shell)->unit,
G_CALLBACK (image_resize_callback),
options);
g_signal_connect_object (gdisp, "disconnect",
G_CALLBACK (gtk_widget_destroy),
options->dialog->shell,
G_CONNECT_SWAPPED);
g_object_weak_ref (G_OBJECT (options->dialog->shell),
(GWeakNotify) g_free,
options);
gtk_widget_show (options->dialog->shell);
}
void
image_resize_to_layers_cmd_callback (GtkAction *action,
gpointer data)
{
GimpDisplay *gdisp;
GimpProgress *progress;
return_if_no_display (gdisp, data);
progress = gimp_progress_start (GIMP_PROGRESS (gdisp),
_("Resizing..."), FALSE);
gimp_image_resize_to_layers (gdisp->gimage,
action_data_get_context (data),
progress);
if (progress)
gimp_progress_end (progress);
gimp_image_flush (gdisp->gimage);
}
void
image_scale_cmd_callback (GtkAction *action,
gpointer data)
{
ImageResizeOptions *options;
GimpDisplay *gdisp;
GimpImage *gimage;
return_if_no_display (gdisp, data);
gimage = gdisp->gimage;
options = g_new0 (ImageResizeOptions, 1);
options->context = action_data_get_context (data);
options->gdisp = gdisp;
options->gimage = gimage;
options->dialog =
resize_dialog_new (GIMP_VIEWABLE (gimage), gdisp->shell,
SCALE_DIALOG,
gimage->width,
gimage->height,
gimage->xresolution,
gimage->yresolution,
GIMP_DISPLAY_SHELL (gdisp->shell)->unit,
G_CALLBACK (image_scale_callback),
options);
g_signal_connect_object (gdisp, "disconnect",
G_CALLBACK (gtk_widget_destroy),
options->dialog->shell,
G_CONNECT_SWAPPED);
g_object_weak_ref (G_OBJECT (options->dialog->shell),
(GWeakNotify) g_free,
options);
gtk_widget_show (options->dialog->shell);
}
void
image_flip_cmd_callback (GtkAction *action,
gint value,
gpointer data)
{
GimpDisplay *gdisp;
GimpProgress *progress;
return_if_no_display (gdisp, data);
progress = gimp_progress_start (GIMP_PROGRESS (gdisp),
_("Flipping..."), FALSE);
gimp_image_flip (gdisp->gimage, action_data_get_context (data),
(GimpOrientationType) value, progress);
if (progress)
gimp_progress_end (progress);
gimp_image_flush (gdisp->gimage);
}
void
image_rotate_cmd_callback (GtkAction *action,
gint value,
gpointer data)
{
GimpDisplay *gdisp;
GimpProgress *progress;
return_if_no_display (gdisp, data);
progress = gimp_progress_start (GIMP_PROGRESS (gdisp),
_("Rotating..."), FALSE);
gimp_image_rotate (gdisp->gimage, action_data_get_context (data),
(GimpRotationType) value, progress);
if (progress)
gimp_progress_end (progress);
gimp_image_flush (gdisp->gimage);
}
void
image_crop_cmd_callback (GtkAction *action,
gpointer data)
{
GimpImage *gimage;
gint x1, y1, x2, y2;
return_if_no_image (gimage, data);
if (! gimp_channel_bounds (gimp_image_get_mask (gimage),
&x1, &y1, &x2, &y2))
{
g_message (_("Cannot crop because the current selection is empty."));
return;
}
gimp_image_crop (gimage, action_data_get_context (data),
x1, y1, x2, y2, FALSE, TRUE);
gimp_image_flush (gimage);
}
void
image_duplicate_cmd_callback (GtkAction *action,
gpointer data)
{
GimpDisplay *gdisp;
GimpDisplayShell *shell;
GimpImage *new_gimage;
return_if_no_display (gdisp, data);
shell = GIMP_DISPLAY_SHELL (gdisp->shell);
new_gimage = gimp_image_duplicate (gdisp->gimage);
gimp_create_display (new_gimage->gimp,
new_gimage,
shell->unit, shell->scale);
g_object_unref (new_gimage);
}
void
image_merge_layers_cmd_callback (GtkAction *action,
gpointer data)
{
LayerMergeOptions *options;
GimpImage *gimage;
GtkWidget *dialog;
GtkWidget *widget;
GtkWidget *frame;
return_if_no_image (gimage, data);
return_if_no_widget (widget, data);
options = g_new0 (LayerMergeOptions, 1);
options->context = action_data_get_context (data);
options->gimage = gimage;
options->merge_type = GIMP_EXPAND_AS_NECESSARY;
dialog = gimp_viewable_dialog_new (GIMP_VIEWABLE (gimage),
_("Merge Layers"),
"gimp-image-merge-layers",
GIMP_STOCK_MERGE_DOWN,
_("Layers Merge Options"),
widget,
gimp_standard_help_func,
GIMP_HELP_IMAGE_MERGE_LAYERS,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_OK, GTK_RESPONSE_OK,
NULL);
g_signal_connect (dialog, "response",
G_CALLBACK (image_merge_layers_response),
options);
frame = gimp_int_radio_group_new (TRUE, _("Final, Merged Layer should be:"),
G_CALLBACK (gimp_radio_button_update),
&options->merge_type, options->merge_type,
_("Expanded as necessary"),
GIMP_EXPAND_AS_NECESSARY, NULL,
_("Clipped to image"),
GIMP_CLIP_TO_IMAGE, NULL,
_("Clipped to bottom layer"),
GIMP_CLIP_TO_BOTTOM_LAYER, NULL,
NULL);
gtk_container_set_border_width (GTK_CONTAINER (frame), 12);
gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), frame);
gtk_widget_show (frame);
gtk_widget_show (dialog);
}
void
image_flatten_image_cmd_callback (GtkAction *action,
gpointer data)
{
GimpImage *gimage;
return_if_no_image (gimage, data);
gimp_image_flatten (gimage, action_data_get_context (data));
gimp_image_flush (gimage);
}
void
image_configure_grid_cmd_callback (GtkAction *action,
gpointer data)
{
GimpDisplay *gdisp;
GimpDisplayShell *shell;
GimpImage *gimage;
return_if_no_display (gdisp, data);
shell = GIMP_DISPLAY_SHELL (gdisp->shell);
gimage = gdisp->gimage;
if (! shell->grid_dialog)
{
shell->grid_dialog = grid_dialog_new (gdisp->gimage, gdisp->shell);
gtk_window_set_transient_for (GTK_WINDOW (shell->grid_dialog),
GTK_WINDOW (gdisp->shell));
gtk_window_set_destroy_with_parent (GTK_WINDOW (shell->grid_dialog),
TRUE);
g_object_add_weak_pointer (G_OBJECT (shell->grid_dialog),
(gpointer *) &shell->grid_dialog);
}
gtk_window_present (GTK_WINDOW (shell->grid_dialog));
}
/* private functions */
static void
image_resize_callback (GtkWidget *widget,
gpointer data)
{
ImageResizeOptions *options = data;
gtk_widget_set_sensitive (options->dialog->shell, FALSE);
if (options->dialog->width > 0 &&
options->dialog->height > 0)
{
GimpProgress *progress;
progress = gimp_progress_start (GIMP_PROGRESS (options->gdisp),
_("Resizing..."), FALSE);
gimp_image_resize (options->gimage,
options->context,
options->dialog->width,
options->dialog->height,
options->dialog->offset_x,
options->dialog->offset_y,
progress);
if (progress)
gimp_progress_end (progress);
gimp_image_flush (options->gimage);
}
else
{
g_message (_("Resize Error: Both width and height must be "
"greater than zero."));
}
gtk_widget_destroy (options->dialog->shell);
}
static void
image_scale_callback (GtkWidget *widget,
gpointer data)
{
ImageResizeOptions *options = data;
GimpImageScaleCheckType scale_check;
gint64 max_memsize;
gint64 new_memsize;
gtk_widget_set_sensitive (options->dialog->shell, FALSE);
max_memsize =
GIMP_GUI_CONFIG (options->gimage->gimp->config)->max_new_image_size;
scale_check = gimp_image_scale_check (options->gimage,
options->dialog->width,
options->dialog->height,
max_memsize,
&new_memsize);
switch (scale_check)
{
case GIMP_IMAGE_SCALE_TOO_BIG:
image_scale_confirm_large (options, new_memsize, max_memsize);
break;
case GIMP_IMAGE_SCALE_TOO_SMALL:
image_scale_confirm_small (options);
break;
case GIMP_IMAGE_SCALE_OK:
/* If all is well, return directly after scaling image. */
image_scale_implement (options);
gtk_widget_destroy (options->dialog->shell);
break;
}
}
static GtkWidget *
image_scale_confirm_dialog (ImageResizeOptions *options)
{
GtkWidget *dialog;
dialog = gimp_message_dialog_new (_("Confirm Scaling"),
GIMP_STOCK_WARNING,
options->dialog->shell,
GTK_DIALOG_DESTROY_WITH_PARENT,
gimp_standard_help_func,
GIMP_HELP_IMAGE_SCALE_WARNING,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GIMP_STOCK_SCALE, GTK_RESPONSE_OK,
NULL);
g_signal_connect (dialog, "response",
G_CALLBACK (image_scale_confirm_response),
options);
return dialog;
}
static void
image_scale_confirm_large (ImageResizeOptions *options,
gint64 new_memsize,
gint64 max_memsize)
{
GtkWidget *dialog = image_scale_confirm_dialog (options);
gchar *size;
size = gimp_memsize_to_string (new_memsize);
gimp_message_box_set_primary_text (GIMP_MESSAGE_DIALOG (dialog)->box,
_("You are trying to create an image "
"with a size of %s."), size);
g_free (size);
size = gimp_memsize_to_string (max_memsize);
gimp_message_box_set_text (GIMP_MESSAGE_DIALOG (dialog)->box,
_("Scaling the image to the choosen size will "
"make it use more memory than what is "
"configured as \"Maximum Image Size\" in the "
"Preferences dialog (currently %s)."), size);
g_free (size);
gtk_widget_show (dialog);
}
static void
image_scale_confirm_small (ImageResizeOptions *options)
{
GtkWidget *dialog = image_scale_confirm_dialog (options);
gimp_message_box_set_primary_text (GIMP_MESSAGE_DIALOG (dialog)->box,
_("Scaling the image to the choosen size "
"will shrink some layers completely "
"away."));
gimp_message_box_set_text (GIMP_MESSAGE_DIALOG (dialog)->box,
_("Is this what you want to do?"));
gtk_widget_show (dialog);
}
static void
image_scale_confirm_response (GtkWidget *dialog,
gint response_id,
ImageResizeOptions *options)
{
gtk_widget_destroy (dialog);
if (response_id == GTK_RESPONSE_OK)
{
image_scale_implement (options);
gtk_widget_destroy (options->dialog->shell);
}
else
{
gtk_widget_set_sensitive (options->dialog->shell, TRUE);
}
}
static void
image_scale_implement (ImageResizeOptions *options)
{
GimpImage *gimage = options->gimage;
if (options->dialog->resolution_x == gimage->xresolution &&
options->dialog->resolution_y == gimage->yresolution &&
options->dialog->unit == gimage->resolution_unit &&
options->dialog->width == gimage->width &&
options->dialog->height == gimage->height)
return;
gimp_image_undo_group_start (gimage, GIMP_UNDO_GROUP_IMAGE_SCALE,
_("Scale Image"));
gimp_image_set_resolution (gimage,
options->dialog->resolution_x,
options->dialog->resolution_y);
gimp_image_set_unit (gimage, options->dialog->unit);
if (options->dialog->width != gimage->width ||
options->dialog->height != gimage->height)
{
if (options->dialog->width > 0 &&
options->dialog->height > 0)
{
GimpProgress *progress;
progress = gimp_progress_start (GIMP_PROGRESS (options->gdisp),
_("Scaling..."), FALSE);
gimp_image_scale (gimage,
options->dialog->width,
options->dialog->height,
options->dialog->interpolation,
progress);
if (progress)
gimp_progress_end (progress);
}
else
{
g_message (_("Scale Error: Both width and height must be "
"greater than zero."));
return;
}
}
gimp_image_undo_group_end (gimage);
gimp_image_flush (gimage);
}
static void
image_merge_layers_response (GtkWidget *dialog,
gint response_id,
LayerMergeOptions *options)
{
gtk_widget_destroy (dialog);
if (response_id == GTK_RESPONSE_OK)
{
gimp_image_merge_visible_layers (options->gimage,
options->context,
options->merge_type);
gimp_image_flush (options->gimage);
}
g_free (options);
}