app: add menu items and a dialog for GimpItem::fill()

which are essentially a copy of the stroking GUI. We now can fill the
exact shape outlined by stroking selections and paths. Suggestions for
the menu item labels are welcome.
This commit is contained in:
Michael Natterer
2016-03-15 20:52:22 +01:00
parent c09a724bda
commit 49ffbbbbc0
12 changed files with 437 additions and 1 deletions

View File

@ -113,6 +113,18 @@ static const GimpActionEntry select_actions[] =
G_CALLBACK (select_save_cmd_callback), G_CALLBACK (select_save_cmd_callback),
GIMP_HELP_SELECTION_TO_CHANNEL }, GIMP_HELP_SELECTION_TO_CHANNEL },
{ "select-fill", GIMP_STOCK_TOOL_BUCKET_FILL,
NC_("select-action", "_Fill Selection Outline..."), NULL,
NC_("select-action", "Fill the selection outline"),
G_CALLBACK (select_fill_cmd_callback),
GIMP_HELP_SELECTION_FILL },
{ "select-fill-last-values", GIMP_STOCK_TOOL_BUCKET_FILL,
NC_("select-action", "_Fill Selection Outline"), NULL,
NC_("select-action", "Fill the selection outline with last used values"),
G_CALLBACK (select_fill_last_vals_cmd_callback),
GIMP_HELP_SELECTION_FILL },
{ "select-stroke", GIMP_STOCK_SELECTION_STROKE, { "select-stroke", GIMP_STOCK_SELECTION_STROKE,
NC_("select-action", "_Stroke Selection..."), NULL, NC_("select-action", "_Stroke Selection..."), NULL,
NC_("select-action", "Paint along the selection outline"), NC_("select-action", "Paint along the selection outline"),
@ -178,6 +190,8 @@ select_actions_update (GimpActionGroup *group,
SET_SENSITIVE ("select-flood", drawable && sel); SET_SENSITIVE ("select-flood", drawable && sel);
SET_SENSITIVE ("select-save", drawable && !fs); SET_SENSITIVE ("select-save", drawable && !fs);
SET_SENSITIVE ("select-fill", writable && !children && sel);
SET_SENSITIVE ("select-fill-last-values", writable && !children && sel);
SET_SENSITIVE ("select-stroke", writable && !children && sel); SET_SENSITIVE ("select-stroke", writable && !children && sel);
SET_SENSITIVE ("select-stroke-last-values", writable && !children && sel); SET_SENSITIVE ("select-stroke-last-values", writable && !children && sel);

View File

@ -39,6 +39,7 @@
#include "display/gimpdisplay.h" #include "display/gimpdisplay.h"
#include "display/gimpdisplayshell.h" #include "display/gimpdisplayshell.h"
#include "dialogs/fill-dialog.h"
#include "dialogs/stroke-dialog.h" #include "dialogs/stroke-dialog.h"
#include "actions.h" #include "actions.h"
@ -338,6 +339,82 @@ select_save_cmd_callback (GtkAction *action,
"gimp-channel-list"); "gimp-channel-list");
} }
void
select_fill_cmd_callback (GtkAction *action,
gpointer data)
{
GimpImage *image;
GimpDrawable *drawable;
GtkWidget *widget;
GtkWidget *dialog;
return_if_no_image (image, data);
return_if_no_widget (widget, data);
drawable = gimp_image_get_active_drawable (image);
if (! drawable)
{
gimp_message_literal (image->gimp,
G_OBJECT (widget), GIMP_MESSAGE_WARNING,
_("There is no active layer or channel to fill."));
return;
}
dialog = fill_dialog_new (GIMP_ITEM (gimp_image_get_mask (image)),
action_data_get_context (data),
_("Fill Selection Outline"),
GIMP_STOCK_TOOL_BUCKET_FILL,
GIMP_HELP_SELECTION_FILL,
widget);
gtk_widget_show (dialog);
}
void
select_fill_last_vals_cmd_callback (GtkAction *action,
gpointer data)
{
GimpImage *image;
GimpDrawable *drawable;
GtkWidget *widget;
GimpFillOptions *options;
GError *error = NULL;
return_if_no_image (image, data);
return_if_no_widget (widget, data);
drawable = gimp_image_get_active_drawable (image);
if (! drawable)
{
gimp_message_literal (image->gimp,
G_OBJECT (widget), GIMP_MESSAGE_WARNING,
_("There is no active layer or channel to fill."));
return;
}
options = g_object_get_data (G_OBJECT (image->gimp), "saved-fill-options");
if (options)
g_object_ref (options);
else
options = gimp_fill_options_new (image->gimp,
action_data_get_context (data), TRUE);
if (! gimp_item_fill (GIMP_ITEM (gimp_image_get_mask (image)),
drawable, options, TRUE, NULL, &error))
{
gimp_message_literal (image->gimp,
G_OBJECT (widget), GIMP_MESSAGE_WARNING,
error->message);
g_clear_error (&error);
}
else
{
gimp_image_flush (image);
}
g_object_unref (options);
}
void void
select_stroke_cmd_callback (GtkAction *action, select_stroke_cmd_callback (GtkAction *action,
gpointer data) gpointer data)

View File

@ -41,6 +41,11 @@ void select_flood_cmd_callback (GtkAction *action,
gpointer data); gpointer data);
void select_save_cmd_callback (GtkAction *action, void select_save_cmd_callback (GtkAction *action,
gpointer data); gpointer data);
void select_fill_cmd_callback (GtkAction *action,
gpointer data);
void select_fill_last_vals_cmd_callback (GtkAction *action,
gpointer data);
void select_stroke_cmd_callback (GtkAction *action, void select_stroke_cmd_callback (GtkAction *action,
gpointer data); gpointer data);
void select_stroke_last_vals_cmd_callback (GtkAction *action, void select_stroke_last_vals_cmd_callback (GtkAction *action,

View File

@ -108,6 +108,18 @@ static const GimpActionEntry vectors_actions[] =
G_CALLBACK (vectors_lower_to_bottom_cmd_callback), G_CALLBACK (vectors_lower_to_bottom_cmd_callback),
GIMP_HELP_PATH_LOWER_TO_BOTTOM }, GIMP_HELP_PATH_LOWER_TO_BOTTOM },
{ "vectors-fill", GIMP_STOCK_TOOL_BUCKET_FILL,
NC_("vectors-action", "Fill Path..."), NULL,
NC_("vectors-action", "Fill the path"),
G_CALLBACK (vectors_fill_cmd_callback),
GIMP_HELP_PATH_FILL },
{ "vectors-fill-last-values", GIMP_STOCK_TOOL_BUCKET_FILL,
NC_("vectors-action", "Fill Path"), NULL,
NC_("vectors-action", "Fill the path with last values"),
G_CALLBACK (vectors_fill_last_vals_cmd_callback),
GIMP_HELP_PATH_FILL },
{ "vectors-stroke", GIMP_STOCK_PATH_STROKE, { "vectors-stroke", GIMP_STOCK_PATH_STROKE,
NC_("vectors-action", "Stro_ke Path..."), NULL, NC_("vectors-action", "Stro_ke Path..."), NULL,
NC_("vectors-action", "Paint along the path"), NC_("vectors-action", "Paint along the path"),
@ -345,6 +357,12 @@ vectors_actions_update (GimpActionGroup *group,
SET_SENSITIVE ("vectors-selection-to-vectors", image && !mask_empty); SET_SENSITIVE ("vectors-selection-to-vectors", image && !mask_empty);
SET_SENSITIVE ("vectors-selection-to-vectors-short", image && !mask_empty); SET_SENSITIVE ("vectors-selection-to-vectors-short", image && !mask_empty);
SET_SENSITIVE ("vectors-selection-to-vectors-advanced", image && !mask_empty); SET_SENSITIVE ("vectors-selection-to-vectors-advanced", image && !mask_empty);
SET_SENSITIVE ("vectors-fill", vectors &&
dr_writable &&
!dr_children);
SET_SENSITIVE ("vectors-fill-last-values", vectors &&
dr_writable &&
!dr_children);
SET_SENSITIVE ("vectors-stroke", vectors && SET_SENSITIVE ("vectors-stroke", vectors &&
dr_writable && dr_writable &&
!dr_children); !dr_children);

View File

@ -56,6 +56,7 @@
#include "tools/gimpvectortool.h" #include "tools/gimpvectortool.h"
#include "tools/tool_manager.h" #include "tools/tool_manager.h"
#include "dialogs/fill-dialog.h"
#include "dialogs/stroke-dialog.h" #include "dialogs/stroke-dialog.h"
#include "dialogs/vectors-export-dialog.h" #include "dialogs/vectors-export-dialog.h"
#include "dialogs/vectors-import-dialog.h" #include "dialogs/vectors-import-dialog.h"
@ -368,6 +369,83 @@ vectors_selection_to_vectors_cmd_callback (GtkAction *action,
} }
} }
void
vectors_fill_cmd_callback (GtkAction *action,
gpointer data)
{
GimpImage *image;
GimpVectors *vectors;
GimpDrawable *drawable;
GtkWidget *widget;
GtkWidget *dialog;
return_if_no_vectors (image, vectors, data);
return_if_no_widget (widget, data);
drawable = gimp_image_get_active_drawable (image);
if (! drawable)
{
gimp_message_literal (image->gimp,
G_OBJECT (widget), GIMP_MESSAGE_WARNING,
_("There is no active layer or channel to fill."));
return;
}
dialog = fill_dialog_new (GIMP_ITEM (vectors),
action_data_get_context (data),
_("Fill Path"),
GIMP_STOCK_TOOL_BUCKET_FILL,
GIMP_HELP_PATH_FILL,
widget);
gtk_widget_show (dialog);
}
void
vectors_fill_last_vals_cmd_callback (GtkAction *action,
gpointer data)
{
GimpImage *image;
GimpVectors *vectors;
GimpDrawable *drawable;
GtkWidget *widget;
GimpFillOptions *options;
GError *error = NULL;
return_if_no_vectors (image, vectors, data);
return_if_no_widget (widget, data);
drawable = gimp_image_get_active_drawable (image);
if (! drawable)
{
gimp_message_literal (image->gimp,
G_OBJECT (widget), GIMP_MESSAGE_WARNING,
_("There is no active layer or channel to fill."));
return;
}
options = g_object_get_data (G_OBJECT (image->gimp), "saved-fill-options");
if (options)
g_object_ref (options);
else
options = gimp_fill_options_new (image->gimp,
action_data_get_context (data), TRUE);
if (! gimp_item_fill (GIMP_ITEM (vectors),
drawable, options, TRUE, NULL, &error))
{
gimp_message_literal (image->gimp, G_OBJECT (widget),
GIMP_MESSAGE_WARNING, error->message);
g_clear_error (&error);
}
else
{
gimp_image_flush (image);
}
g_object_unref (options);
}
void void
vectors_stroke_cmd_callback (GtkAction *action, vectors_stroke_cmd_callback (GtkAction *action,
gpointer data) gpointer data)
@ -424,7 +502,6 @@ vectors_stroke_last_vals_cmd_callback (GtkAction *action,
return; return;
} }
options = g_object_get_data (G_OBJECT (image->gimp), "saved-stroke-options"); options = g_object_get_data (G_OBJECT (image->gimp), "saved-stroke-options");
if (options) if (options)

View File

@ -49,6 +49,11 @@ void vectors_to_selection_cmd_callback (GtkAction *action,
void vectors_selection_to_vectors_cmd_callback (GtkAction *action, void vectors_selection_to_vectors_cmd_callback (GtkAction *action,
gint value, gint value,
gpointer data); gpointer data);
void vectors_fill_cmd_callback (GtkAction *action,
gpointer data);
void vectors_fill_last_vals_cmd_callback (GtkAction *action,
gpointer data);
void vectors_stroke_cmd_callback (GtkAction *action, void vectors_stroke_cmd_callback (GtkAction *action,
gpointer data); gpointer data);
void vectors_stroke_last_vals_cmd_callback (GtkAction *action, void vectors_stroke_last_vals_cmd_callback (GtkAction *action,

View File

@ -44,6 +44,8 @@ libappdialogs_a_sources = \
file-open-location-dialog.h \ file-open-location-dialog.h \
file-save-dialog.c \ file-save-dialog.c \
file-save-dialog.h \ file-save-dialog.h \
fill-dialog.c \
fill-dialog.h \
grid-dialog.h \ grid-dialog.h \
grid-dialog.c \ grid-dialog.c \
image-merge-layers-dialog.c \ image-merge-layers-dialog.c \

200
app/dialogs/fill-dialog.c Normal file
View File

@ -0,0 +1,200 @@
/* GIMP - The GNU Image Manipulation Program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* fill-dialog.c
* Copyright (C) 2016 Michael Natterer <mitch@gimp.org>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gegl.h>
#include <gtk/gtk.h>
#include "libgimpconfig/gimpconfig.h"
#include "libgimpwidgets/gimpwidgets.h"
#include "dialogs-types.h"
#include "core/gimp.h"
#include "core/gimpdrawable.h"
#include "core/gimpimage.h"
#include "core/gimpfilloptions.h"
#include "widgets/gimpfilleditor.h"
#include "widgets/gimpviewabledialog.h"
#include "fill-dialog.h"
#include "gimp-intl.h"
#define RESPONSE_RESET 1
/* local functions */
static void fill_dialog_response (GtkWidget *widget,
gint response_id,
GtkWidget *dialog);
/* public function */
GtkWidget *
fill_dialog_new (GimpItem *item,
GimpContext *context,
const gchar *title,
const gchar *icon_name,
const gchar *help_id,
GtkWidget *parent)
{
GimpFillOptions *options;
GimpFillOptions *saved_options;
GtkWidget *dialog;
GtkWidget *main_vbox;
GtkWidget *fill_editor;
g_return_val_if_fail (GIMP_IS_ITEM (item), NULL);
g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
g_return_val_if_fail (icon_name != NULL, NULL);
g_return_val_if_fail (help_id != NULL, NULL);
g_return_val_if_fail (parent == NULL || GTK_IS_WIDGET (parent), NULL);
options = gimp_fill_options_new (context->gimp, context, TRUE);
saved_options = g_object_get_data (G_OBJECT (context->gimp),
"saved-fill-options");
if (saved_options)
gimp_config_sync (G_OBJECT (saved_options), G_OBJECT (options), 0);
dialog = gimp_viewable_dialog_new (GIMP_VIEWABLE (item), context,
title, "gimp-fill-options",
icon_name,
_("Choose Fill Style"),
parent,
gimp_standard_help_func,
help_id,
GIMP_STOCK_RESET, RESPONSE_RESET,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
_("_Fill"), GTK_RESPONSE_OK,
NULL);
gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
RESPONSE_RESET,
GTK_RESPONSE_OK,
GTK_RESPONSE_CANCEL,
-1);
gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
g_signal_connect (dialog, "response",
G_CALLBACK (fill_dialog_response),
dialog);
g_object_set_data (G_OBJECT (dialog), "gimp-item", item);
g_object_set_data_full (G_OBJECT (dialog), "gimp-fill-options", options,
(GDestroyNotify) g_object_unref);
main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
main_vbox, TRUE, TRUE, 0);
gtk_widget_show (main_vbox);
fill_editor = gimp_fill_editor_new (options, FALSE);
gtk_box_pack_start (GTK_BOX (main_vbox), fill_editor, FALSE, FALSE, 0);
gtk_widget_show (fill_editor);
return dialog;
}
/* private functions */
static void
fill_dialog_response (GtkWidget *widget,
gint response_id,
GtkWidget *dialog)
{
GimpFillOptions *options;
GimpItem *item;
GimpImage *image;
GimpContext *context;
item = g_object_get_data (G_OBJECT (dialog), "gimp-item");
options = g_object_get_data (G_OBJECT (dialog), "gimp-fill-options");
image = gimp_item_get_image (item);
context = GIMP_VIEWABLE_DIALOG (dialog)->context;
switch (response_id)
{
case RESPONSE_RESET:
gimp_config_reset (GIMP_CONFIG (options));
break;
case GTK_RESPONSE_OK:
{
GimpDrawable *drawable = gimp_image_get_active_drawable (image);
GimpFillOptions *saved_options;
GError *error = NULL;
if (! drawable)
{
gimp_message_literal (context->gimp, G_OBJECT (widget),
GIMP_MESSAGE_WARNING,
_("There is no active layer or channel "
"to fill."));
return;
}
saved_options = g_object_get_data (G_OBJECT (context->gimp),
"saved-fill-options");
if (saved_options)
g_object_ref (saved_options);
else
saved_options = gimp_fill_options_new (context->gimp, context, TRUE);
gimp_config_sync (G_OBJECT (options), G_OBJECT (saved_options), 0);
g_object_set_data_full (G_OBJECT (context->gimp), "saved-fill-options",
saved_options,
(GDestroyNotify) g_object_unref);
if (! gimp_item_fill (item, drawable, options, TRUE, NULL, &error))
{
gimp_message_literal (context->gimp,
G_OBJECT (widget),
GIMP_MESSAGE_WARNING,
error ? error->message : "NULL");
g_clear_error (&error);
return;
}
gimp_image_flush (image);
}
/* fallthrough */
default:
gtk_widget_destroy (dialog);
break;
}
}

33
app/dialogs/fill-dialog.h Normal file
View File

@ -0,0 +1,33 @@
/* GIMP - The GNU Image Manipulation Program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* fill-dialog.h
* Copyright (C) 2016 Michael Natterer <mitch@gimp.org>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef __FILL_DIALOG_H__
#define __FILL_DIALOG_H__
GtkWidget * fill_dialog_new (GimpItem *item,
GimpContext *context,
const gchar *title,
const gchar *icon_name,
const gchar *help_id,
GtkWidget *parent);
#endif /* __FILL_DIALOG_H__ */

View File

@ -75,6 +75,7 @@
#define GIMP_HELP_SELECTION_GROW "gimp-selection-grow" #define GIMP_HELP_SELECTION_GROW "gimp-selection-grow"
#define GIMP_HELP_SELECTION_BORDER "gimp-selection-border" #define GIMP_HELP_SELECTION_BORDER "gimp-selection-border"
#define GIMP_HELP_SELECTION_FLOOD "gimp-selection-flood" #define GIMP_HELP_SELECTION_FLOOD "gimp-selection-flood"
#define GIMP_HELP_SELECTION_FILL "gimp-selection-fill"
#define GIMP_HELP_SELECTION_STROKE "gimp-selection-stroke" #define GIMP_HELP_SELECTION_STROKE "gimp-selection-stroke"
#define GIMP_HELP_SELECTION_TO_CHANNEL "gimp-selection-to-channel" #define GIMP_HELP_SELECTION_TO_CHANNEL "gimp-selection-to-channel"
#define GIMP_HELP_SELECTION_TO_PATH "gimp-selection-to-path" #define GIMP_HELP_SELECTION_TO_PATH "gimp-selection-to-path"
@ -258,6 +259,7 @@
#define GIMP_HELP_PATH_SELECTION_ADD "gimp-path-selection-add" #define GIMP_HELP_PATH_SELECTION_ADD "gimp-path-selection-add"
#define GIMP_HELP_PATH_SELECTION_SUBTRACT "gimp-path-selection-subtract" #define GIMP_HELP_PATH_SELECTION_SUBTRACT "gimp-path-selection-subtract"
#define GIMP_HELP_PATH_SELECTION_INTERSECT "gimp-path-selection-intersect" #define GIMP_HELP_PATH_SELECTION_INTERSECT "gimp-path-selection-intersect"
#define GIMP_HELP_PATH_FILL "gimp-path-fill"
#define GIMP_HELP_PATH_STROKE "gimp-path-stroke" #define GIMP_HELP_PATH_STROKE "gimp-path-stroke"
#define GIMP_HELP_PATH_COPY "gimp-path-copy" #define GIMP_HELP_PATH_COPY "gimp-path-copy"
#define GIMP_HELP_PATH_PASTE "gimp-path-paste" #define GIMP_HELP_PATH_PASTE "gimp-path-paste"

View File

@ -213,6 +213,8 @@
<menuitem action="edit-fill-fg" /> <menuitem action="edit-fill-fg" />
<menuitem action="edit-fill-bg" /> <menuitem action="edit-fill-bg" />
<menuitem action="edit-fill-pattern" /> <menuitem action="edit-fill-pattern" />
<menuitem action="select-fill" />
<menuitem action="vectors-fill" />
</placeholder> </placeholder>
<placeholder name="Stroke"> <placeholder name="Stroke">
<menuitem action="select-stroke" /> <menuitem action="select-stroke" />

View File

@ -200,6 +200,7 @@ app/dialogs/fade-dialog.c
app/dialogs/file-open-dialog.c app/dialogs/file-open-dialog.c
app/dialogs/file-open-location-dialog.c app/dialogs/file-open-location-dialog.c
app/dialogs/file-save-dialog.c app/dialogs/file-save-dialog.c
app/dialogs/fill-dialog.c
app/dialogs/grid-dialog.c app/dialogs/grid-dialog.c
app/dialogs/image-merge-layers-dialog.c app/dialogs/image-merge-layers-dialog.c
app/dialogs/image-new-dialog.c app/dialogs/image-new-dialog.c