Compare commits

..

8 Commits

Author SHA1 Message Date
4b829a4b3c Fix about dialog 2023-12-07 16:00:26 -08:00
af7dc8395d Update READE 2023-12-07 16:00:19 -08:00
8ac3f8a548 Updated URLs everywhere. Maybe fix about-dialog 2023-12-07 15:36:47 -08:00
63b2780499 Fix about dialog again. Update readme with more complete deps 2023-12-07 13:07:58 -08:00
d472f6348d Update upstream 2023-12-02 11:03:24 -08:00
84ea557696 Fix build advice in README 2023-10-30 16:04:02 -07:00
852cbfc1fb Updated new files from upstream 2023-10-30 15:56:43 -07:00
3bbdd873ef Updated with upstream update 2023-10-30 15:55:30 -07:00
766 changed files with 112445 additions and 79933 deletions

View File

@ -18,7 +18,8 @@
# - PIKA_CI_CROSSROAD_WIN64: trigger the crossroad/meson build for Win 64-bit.
# - PIKA_CI_MSYS2_WIN32: trigger the native MSYS2 build for Win 32-bit.
# - PIKA_CI_MSYS2_WIN64: trigger the native MSYS2 build for Win 64-bit.
# - PIKA_CI_WIN_INSTALLER: trigger both native MSYS2 builds then creates Windows installer.
# - PIKA_CI_MSYS2_WIN_AARCH64: trigger the native MSYS2 build for Windows/Aarch64.
# - PIKA_CI_WIN_INSTALLER: trigger all native MSYS2 builds then creates Windows installer.
# - PIKA_CI_SOURCES: trigger the meson/gcc build and the source tarball job.
# - PIKA_CI_CPPCHECK: trigger cppcheck static analysis.
# - PIKA_CI_FLATPAK: trigger the nightly flatpak build and publishing.
@ -375,7 +376,7 @@ pika-meson-raster-icons:
- ninja -C _build
- ninja -C _build test
## WINDOWS 64-bit CI (native MSYS2) ##
## WINDOWS x86_64 CI (native MSYS2) ##
deps-win64-native:
rules:
@ -462,7 +463,7 @@ packaging-win64-native:
- done-dll.list
needs: ["pika-win64-native"]
## WINDOWS 32-bit CI (native MSYS2) ##
## WINDOWS x86 CI (native MSYS2) ##
deps-win32-native:
rules:
@ -548,6 +549,96 @@ packaging-win32-native:
- done-dll.list
needs: ["pika-win32-native"]
## WINDOWS Aarch64 CI (native MSYS2) ##
deps-win-aarch64-native:
rules:
# On releases.
- if: '$CI_COMMIT_TAG != null'
# Custom builds though web GUI, API or schedules.
- if: '$PIKA_CI_MSYS2_WIN_AARCH64 != null'
- if: '$PIKA_CI_WIN_INSTALLER != null'
# Merge requests with appropriate label.
- if: '$CI_MERGE_REQUEST_LABELS =~ /.*5. Windows Installer.*/'
stage: dependencies
variables:
MSYSTEM: "CLANGARM64"
CHERE_INVOKING: "yes"
tags:
- windows-aarch64
script:
- C:\msys64\usr\bin\pacman --noconfirm -Syyuu
- C:\msys64\usr\bin\bash -lc "bash -x ./build/windows/gitlab-ci/build-deps-msys2.sh"
artifacts:
name: "${CI_JOB_NAME}-${CI_COMMIT_REF_SLUG}"
when: always
expire_in: 2 hours
paths:
- _install-arm64
needs: []
pika-win-aarch64-native:
rules:
# On releases.
- if: '$CI_COMMIT_TAG != null'
# Custom builds though web GUI, API or schedules.
- if: '$PIKA_CI_MSYS2_WIN_AARCH64 != null'
- if: '$PIKA_CI_WIN_INSTALLER != null'
# Merge requests with appropriate label.
- if: '$CI_MERGE_REQUEST_LABELS =~ /.*5. Windows Installer.*/'
stage: pika
variables:
MSYSTEM: "CLANGARM64"
CHERE_INVOKING: "yes"
tags:
- windows-aarch64
script:
# Temporary patch until we use the latest LLVM.
- git apply ./build/windows/patches/0001-clang-rc-files-fix.patch
- git apply ./build/windows/patches/0004-clang-windres.patch
- C:\msys64\usr\bin\pacman --noconfirm -Syyuu
- C:\msys64\usr\bin\bash -lc "bash -x ./build/windows/gitlab-ci/build-pika-msys2.sh"
artifacts:
name: "${CI_JOB_NAME}-${CI_COMMIT_REF_SLUG}"
when: always
expire_in: 1 day
paths:
- _install-arm64
- _build-arm64/build/windows/installer/
- _build-arm64/meson-*/
cache:
paths:
- _ccache/
needs: ["deps-win-aarch64-native"]
packaging-win-aarch64-native:
rules:
# On releases.
- if: '$CI_COMMIT_TAG != null'
# Custom builds though web GUI, API or schedules.
- if: '$PIKA_CI_WIN_INSTALLER != null'
# Merge requests with appropriate label.
- if: '$CI_MERGE_REQUEST_LABELS =~ /.*5. Windows Installer.*/'
stage: packaging
variables:
MSYSTEM: "CLANGARM64"
CHERE_INVOKING: "yes"
tags:
- windows-aarch64
script:
- C:\msys64\usr\bin\pacman --noconfirm -Syyuu
- C:\msys64\usr\bin\bash -lc "bash -x ./build/windows/gitlab-ci/package-pika-msys2.sh"
- cd pika-arm64
- C:\msys64\usr\bin\bash -lc "bash -x ../build/windows/gitlab-ci/split-debug-msys2.sh"
artifacts:
name: "${CI_JOB_NAME}-${CI_COMMIT_REF_SLUG}"
when: always
expire_in: 1 day
paths:
- pika-arm64
- done-dll.list
needs: ["pika-win-aarch64-native"]
## WINDOWS 64-bit CI (cross-build crossroad) ##
deps-win64:
@ -822,6 +913,7 @@ win-installer-nightly:
dependencies:
- packaging-win64-native
- packaging-win32-native
- packaging-win-aarch64-native
# This is needed for the BMP image generation for the installer.
# See commit e1203e9f76f.
- pika-meson-debian
@ -835,7 +927,7 @@ win-installer-nightly:
script:
- C:\msys64\usr\bin\pacman --noconfirm -Syyuu
- C:\msys64\usr\bin\bash -lc "bash -x ./build/windows/gitlab-ci/installer-pika-msys2.sh > installer.log 2>&1"
needs: ["packaging-win32-native", "packaging-win64-native", "pika-meson-debian"]
needs: ["packaging-win32-native", "packaging-win64-native", "packaging-win-aarch64-native", "pika-meson-debian"]
sources-meson:
rules:

8
README
View File

@ -39,13 +39,7 @@ similar.
Partial package list on Debian/Ubuntu/etc:
appstream-util bison build-essential cmake desktop-file-utils flex gjs libaa1-dev libappstream-glib-dev libarchive-dev
libatk1.0-dev libcairo2-dev libcfitsio-dev libexiv2-dev libfontconfig-dev libfreetype-dev libgdk-pixbuf-2.0-dev
libgexiv2-dev libglib2.0-dev libgs-dev libgtk-3-dev libgudev-1.0-dev libharfbuzz-dev libheif-dev libjpeg-dev
libjson-glib-dev liblcms2-dev libmng-dev libmypaint-dev libopenexr-dev libopenjp2-7-dev libpango1.0-dev libpng-dev
libpoppler-dev libpoppler-glib-dev librsvg2-dev libtiff-dev libunwind-dev libwebp-dev libwmf-dev libx11-dev
libxcursor-dev libxext-dev libxfixes-dev libxmu-dev libxpm-dev libzstd-dev luajit meson mypaint-brushes ninja-build
pappler-data python3-gi-dev xmllint xsltproc
appstream-util bison build-essential cmake desktop-file-utils flex gjs libaa1-dev libappstream-glib-dev libarchive-dev libatk1.0-dev libcairo2-dev libcfitsio-dev libexiv2-dev libfontconfig-dev libfreetype-dev libgdk-pixbuf-2.0-dev libgexiv2-dev libglib2.0-dev libgs-dev libgtk-3-dev libgudev-1.0-dev libharfbuzz-dev libheif-dev libjpeg-dev libjson-glib-dev liblcms2-dev libmng-dev libmypaint-dev libopenexr-dev libopenjp2-7-dev libpango1.0-dev libpng-dev libpoppler-dev libpoppler-glib-dev librsvg2-dev libtiff-dev libunwind-dev libwebp-dev libwmf-dev libx11-dev libxcursor-dev libxext-dev libxfixes-dev libxmu-dev libxpm-dev libzstd-dev luajit meson mypaint-brushes ninja-build poppler-data python3-gi libxml2-utils xsltproc gobject-introspection libgirepository1.0-dev libbz2-dev gettext
Seems like a lot! But many of these are for format support. This is for the maximal setup we've tested.

View File

@ -34,6 +34,7 @@
#include "core/pikaimage-colormap.h"
#include "widgets/pikaactiongroup.h"
#include "widgets/pikacolormapeditor.h"
#include "widgets/pikahelp-ids.h"
#include "actions.h"
@ -49,6 +50,12 @@ static const PikaActionEntry colormap_actions[] =
NC_("colormap-action", "_Edit Color..."), NULL, { NULL },
NC_("colormap-action", "Edit this color"),
colormap_edit_color_cmd_callback,
PIKA_HELP_INDEXED_PALETTE_EDIT },
{ "colormap-delete-color", PIKA_ICON_EDIT_DELETE,
NC_("colormap-action", "_Delete Color..."), NULL, { NULL },
NC_("colormap-action", "Delete this color"),
colormap_delete_color_cmd_callback,
PIKA_HELP_INDEXED_PALETTE_EDIT }
};
@ -116,13 +123,14 @@ void
colormap_actions_update (PikaActionGroup *group,
gpointer data)
{
PikaImage *image = action_data_get_image (data);
PikaContext *context = action_data_get_context (data);
gboolean indexed = FALSE;
gboolean drawable_indexed = FALSE;
gint num_colors = 0;
PikaRGB fg;
PikaRGB bg;
PikaColormapEditor *editor = PIKA_COLORMAP_EDITOR (data);
PikaImage *image = action_data_get_image (data);
PikaContext *context = action_data_get_context (data);
gboolean indexed = FALSE;
gboolean drawable_indexed = FALSE;
gint num_colors = 0;
PikaRGB fg;
PikaRGB bg;
if (image)
{
@ -154,6 +162,9 @@ colormap_actions_update (PikaActionGroup *group,
SET_SENSITIVE ("colormap-edit-color",
indexed && num_colors > 0);
SET_SENSITIVE ("colormap-delete-color",
indexed && num_colors > 0 &&
pika_colormap_editor_is_color_deletable (editor));
SET_SENSITIVE ("colormap-add-color-from-fg",
indexed && num_colors < 256);

View File

@ -50,6 +50,16 @@ colormap_edit_color_cmd_callback (PikaAction *action,
pika_colormap_editor_edit_color (editor);
}
void
colormap_delete_color_cmd_callback (PikaAction *action,
GVariant *value,
gpointer data)
{
PikaColormapEditor *editor = PIKA_COLORMAP_EDITOR (data);
pika_colormap_editor_delete_color (editor);
}
void
colormap_add_color_cmd_callback (PikaAction *action,
GVariant *value,

View File

@ -26,6 +26,9 @@
void colormap_edit_color_cmd_callback (PikaAction *action,
GVariant *value,
gpointer data);
void colormap_delete_color_cmd_callback (PikaAction *action,
GVariant *value,
gpointer data);
void colormap_add_color_cmd_callback (PikaAction *action,
GVariant *value,
gpointer data);

View File

@ -184,19 +184,31 @@ static const PikaEnumActionEntry edit_paste_actions[] =
PIKA_HELP_EDIT_PASTE_IN_PLACE },
{ "edit-paste-into", PIKA_ICON_EDIT_PASTE_INTO,
NC_("edit-action", "Paste _Into Selection"), NULL, { NULL },
NC_("edit-action", "Paste as Floating Data _Into Selection"), NULL, { NULL },
NC_("edit-action",
"Paste the content of the clipboard into the current selection"),
PIKA_PASTE_TYPE_FLOATING_INTO, FALSE,
PIKA_HELP_EDIT_PASTE_INTO },
{ "edit-paste-into-in-place", PIKA_ICON_EDIT_PASTE_INTO,
NC_("edit-action", "Paste Int_o Selection In Place"), NULL, { NULL },
NC_("edit-action", "Paste as Floating Data Int_o Selection In Place"), NULL, { NULL },
NC_("edit-action",
"Paste the content of the clipboard into the current selection "
"at its original position"),
PIKA_PASTE_TYPE_FLOATING_INTO_IN_PLACE, FALSE,
PIKA_HELP_EDIT_PASTE_INTO_IN_PLACE }
PIKA_HELP_EDIT_PASTE_INTO_IN_PLACE },
{ "edit-paste-float", PIKA_ICON_EDIT_PASTE,
NC_("edit-action", "Paste as _Floating Data"), NULL, { NULL },
NC_("edit-action", "Paste the content of the clipboard as Floating Data"),
PIKA_PASTE_TYPE_FLOATING, FALSE,
PIKA_HELP_EDIT_PASTE },
{ "edit-paste-float-in-place", PIKA_ICON_EDIT_PASTE,
NC_("edit-action", "Paste as Floa_ting Data In Place"), NULL, { NULL },
NC_("edit-action", "Paste the content of the clipboard as Floating Data at its original position"),
PIKA_PASTE_TYPE_FLOATING_IN_PLACE, FALSE,
PIKA_HELP_EDIT_PASTE }
};
static const PikaEnumActionEntry edit_fill_actions[] =

View File

@ -972,7 +972,7 @@ layers_actions_update (PikaActionGroup *group,
SET_SENSITIVE ("layers-new", image);
SET_SENSITIVE ("layers-new-last-values", image);
SET_SENSITIVE ("layers-new-from-visible", image);
SET_SENSITIVE ("layers-new-group", image && !indexed);
SET_SENSITIVE ("layers-new-group", image && !indexed && !fs);
SET_SENSITIVE ("layers-duplicate", n_selected_layers > 0 && !fs && !ac);
SET_SENSITIVE ("layers-delete", n_selected_layers > 0 && !ac);

View File

@ -446,7 +446,7 @@ pika_gegl_procedure_new (Pika *pika,
FALSE,
PIKA_PARAM_READWRITE));
pika_procedure_add_argument (procedure,
g_param_spec_int ("n-drawables",
g_param_spec_int ("num-drawables",
"N drawables",
"The number of drawables",
0, G_MAXINT32, 0,

View File

@ -62,10 +62,16 @@ static const PikaActionEntry select_actions[] =
select_invert_cmd_callback,
PIKA_HELP_SELECTION_INVERT },
{ "select-float", PIKA_ICON_LAYER_FLOATING_SELECTION,
NC_("select-action", "_Float"), NULL, { "<primary><shift>L", NULL },
NC_("select-action", "Create a floating selection"),
select_float_cmd_callback,
{ "select-cut-float", PIKA_ICON_LAYER_FLOATING_SELECTION,
NC_("select-action", "Cu_t and Float"), NULL, { "<primary><shift>L", NULL },
NC_("select-action", "Cut the selection directly into a floating selection"),
select_cut_float_cmd_callback,
PIKA_HELP_SELECTION_FLOAT },
{ "select-copy-float", PIKA_ICON_LAYER_FLOATING_SELECTION,
NC_("select-action", "_Copy and Float"), NULL, { NULL },
NC_("select-action", "Copy the selection directly into a floating selection"),
select_copy_float_cmd_callback,
PIKA_HELP_SELECTION_FLOAT },
{ "select-feather", NULL,
@ -186,9 +192,13 @@ select_actions_update (PikaActionGroup *group,
SET_SENSITIVE ("select-all", image && ! sel_all);
SET_SENSITIVE ("select-none", image && sel);
SET_SENSITIVE ("select-invert", image);
SET_SENSITIVE ("select-float", g_list_length (drawables) == 1 && sel &&
! pika_item_is_content_locked (drawables->data, NULL) &&
! pika_viewable_get_children (drawables->data));
SET_SENSITIVE ("select-cut-float", g_list_length (drawables) == 1 && sel &&
! pika_item_is_content_locked (drawables->data, NULL) &&
! pika_viewable_get_children (drawables->data));
SET_SENSITIVE ("select-copy-float", g_list_length (drawables) == 1 && sel &&
! pika_item_is_content_locked (drawables->data, NULL) &&
! pika_viewable_get_children (drawables->data));
SET_SENSITIVE ("select-feather", image && sel);
SET_SENSITIVE ("select-sharpen", image && sel);

View File

@ -71,6 +71,10 @@ static void select_shrink_callback (GtkWidget *widget,
gdouble size,
PikaUnit unit,
gpointer data);
static void select_float (PikaAction *action,
GVariant *value,
gboolean cut,
gpointer data);
/* public functions */
@ -112,33 +116,19 @@ select_invert_cmd_callback (PikaAction *action,
}
void
select_float_cmd_callback (PikaAction *action,
GVariant *value,
gpointer data)
select_cut_float_cmd_callback (PikaAction *action,
GVariant *value,
gpointer data)
{
PikaImage *image;
GtkWidget *widget;
GList *drawables;
GError *error = NULL;
return_if_no_image (image, data);
return_if_no_widget (widget, data);
select_float (action, value, TRUE, data);
}
drawables = pika_image_get_selected_drawables (image);
if (pika_selection_float (PIKA_SELECTION (pika_image_get_mask (image)),
drawables,
action_data_get_context (data),
TRUE, 0, 0, &error))
{
pika_image_flush (image);
}
else
{
pika_message_literal (image->pika,
G_OBJECT (widget), PIKA_MESSAGE_WARNING,
error->message);
g_clear_error (&error);
}
g_list_free (drawables);
void
select_copy_float_cmd_callback (PikaAction *action,
GVariant *value,
gpointer data)
{
select_float (action, value, FALSE, data);
}
void
@ -696,3 +686,34 @@ select_shrink_callback (GtkWidget *widget,
TRUE);
pika_image_flush (image);
}
static void
select_float (PikaAction *action,
GVariant *value,
gboolean cut,
gpointer data)
{
PikaImage *image;
GtkWidget *widget;
GList *drawables;
GError *error = NULL;
return_if_no_image (image, data);
return_if_no_widget (widget, data);
drawables = pika_image_get_selected_drawables (image);
if (pika_selection_float (PIKA_SELECTION (pika_image_get_mask (image)),
drawables,
action_data_get_context (data),
cut, 0, 0, &error))
{
pika_image_flush (image);
}
else
{
pika_message_literal (image->pika,
G_OBJECT (widget), PIKA_MESSAGE_WARNING,
error->message);
g_clear_error (&error);
}
g_list_free (drawables);
}

View File

@ -32,7 +32,10 @@ void select_none_cmd_callback (PikaAction *action,
void select_invert_cmd_callback (PikaAction *action,
GVariant *value,
gpointer data);
void select_float_cmd_callback (PikaAction *action,
void select_cut_float_cmd_callback (PikaAction *action,
GVariant *value,
gpointer data);
void select_copy_float_cmd_callback (PikaAction *action,
GVariant *value,
gpointer data);
void select_feather_cmd_callback (PikaAction *action,

View File

@ -98,6 +98,7 @@ enum
PROP_GLOBAL_PALETTE,
PROP_GLOBAL_GRADIENT,
PROP_GLOBAL_FONT,
PROP_GLOBAL_EXPAND,
PROP_DEFAULT_IMAGE,
PROP_DEFAULT_GRID,
PROP_UNDO_LEVELS,
@ -540,6 +541,13 @@ pika_core_config_class_init (PikaCoreConfigClass *klass)
TRUE,
PIKA_PARAM_STATIC_STRINGS);
PIKA_CONFIG_PROP_BOOLEAN (object_class, PROP_GLOBAL_EXPAND,
"global-expand",
"Global expand",
GLOBAL_EXPAND_BLURB,
TRUE,
PIKA_PARAM_STATIC_STRINGS);
PIKA_CONFIG_PROP_OBJECT (object_class, PROP_DEFAULT_IMAGE,
"default-image",
"Default image",
@ -1061,6 +1069,9 @@ pika_core_config_set_property (GObject *object,
case PROP_GLOBAL_FONT:
core_config->global_font = g_value_get_boolean (value);
break;
case PROP_GLOBAL_EXPAND:
core_config->global_expand = g_value_get_boolean (value);
break;
case PROP_DEFAULT_IMAGE:
if (g_value_get_object (value))
pika_config_sync (g_value_get_object (value) ,
@ -1337,6 +1348,9 @@ pika_core_config_get_property (GObject *object,
case PROP_GLOBAL_FONT:
g_value_set_boolean (value, core_config->global_font);
break;
case PROP_GLOBAL_EXPAND:
g_value_set_boolean (value, core_config->global_expand);
break;
case PROP_DEFAULT_IMAGE:
g_value_set_object (value, core_config->default_image);
break;

View File

@ -82,6 +82,7 @@ struct _PikaCoreConfig
gboolean global_palette;
gboolean global_gradient;
gboolean global_font;
gboolean global_expand;
PikaTemplate *default_image;
PikaGrid *default_grid;
gint levels_of_undo;

View File

@ -44,7 +44,7 @@
#endif
#define DEFAULT_USER_MANUAL_ONLINE_URI \
"https://docs.pika.org/" PIKA_APP_VERSION_STRING
"https://heckin.technology/AlderconeStudio/PIKApp/wiki/" PIKA_APP_VERSION_STRING
enum

View File

@ -50,7 +50,7 @@ _("Timestamp of the last update check.")
"Defines the color management behavior."
#define COLOR_PROFILE_POLICY_BLURB \
_("How to handle embedded color profiles when opening a file.")
_("What to do when opening a file with an embedded ICC color profile.")
#define COLOR_PROFILE_PATH_BLURB \
_("Sets the default folder path for all color profile file dialogs.")
@ -181,6 +181,9 @@ _("When enabled, the selected pattern will be used for all tools.")
#define GLOBAL_PALETTE_BLURB \
"When enabled, the selected palette will be used for all tools."
#define GLOBAL_EXPAND_BLURB \
"When enabled, the selected auto expand layer settings will be used for all tools."
#define GRADIENT_PATH_BLURB \
"Sets the gradient search path."

View File

@ -1195,6 +1195,7 @@ pika_undo_type_get_type (void)
{ PIKA_UNDO_GROUP_IMAGE_VECTORS_MERGE, "PIKA_UNDO_GROUP_IMAGE_VECTORS_MERGE", "group-image-vectors-merge" },
{ PIKA_UNDO_GROUP_IMAGE_QUICK_MASK, "PIKA_UNDO_GROUP_IMAGE_QUICK_MASK", "group-image-quick-mask" },
{ PIKA_UNDO_GROUP_IMAGE_GRID, "PIKA_UNDO_GROUP_IMAGE_GRID", "group-image-grid" },
{ PIKA_UNDO_GROUP_IMAGE_COLORMAP_REMAP, "PIKA_UNDO_GROUP_IMAGE_COLORMAP_REMAP", "group-image-colormap-remap" },
{ PIKA_UNDO_GROUP_GUIDE, "PIKA_UNDO_GROUP_GUIDE", "group-guide" },
{ PIKA_UNDO_GROUP_SAMPLE_POINT, "PIKA_UNDO_GROUP_SAMPLE_POINT", "group-sample-point" },
{ PIKA_UNDO_GROUP_DRAWABLE, "PIKA_UNDO_GROUP_DRAWABLE", "group-drawable" },
@ -1303,6 +1304,7 @@ pika_undo_type_get_type (void)
{ PIKA_UNDO_GROUP_IMAGE_VECTORS_MERGE, NC_("undo-type", "Merge paths"), NULL },
{ PIKA_UNDO_GROUP_IMAGE_QUICK_MASK, NC_("undo-type", "Quick Mask"), NULL },
{ PIKA_UNDO_GROUP_IMAGE_GRID, NC_("undo-type", "Grid"), NULL },
{ PIKA_UNDO_GROUP_IMAGE_COLORMAP_REMAP, NC_("undo-type", "Colormap remapping"), NULL },
{ PIKA_UNDO_GROUP_GUIDE, NC_("undo-type", "Guide"), NULL },
{ PIKA_UNDO_GROUP_SAMPLE_POINT, NC_("undo-type", "Sample Point"), NULL },
{ PIKA_UNDO_GROUP_DRAWABLE, NC_("undo-type", "Layer/Channel"), NULL },

View File

@ -548,6 +548,7 @@ typedef enum /*< pdb-skip >*/
PIKA_UNDO_GROUP_IMAGE_VECTORS_MERGE, /*< desc="Merge paths" >*/
PIKA_UNDO_GROUP_IMAGE_QUICK_MASK, /*< desc="Quick Mask" >*/
PIKA_UNDO_GROUP_IMAGE_GRID, /*< desc="Grid" >*/
PIKA_UNDO_GROUP_IMAGE_COLORMAP_REMAP, /*< desc="Colormap remapping" >*/
PIKA_UNDO_GROUP_GUIDE, /*< desc="Guide" >*/
PIKA_UNDO_GROUP_SAMPLE_POINT, /*< desc="Sample Point" >*/
PIKA_UNDO_GROUP_DRAWABLE, /*< desc="Layer/Channel" >*/
@ -714,6 +715,7 @@ typedef enum /*< pdb-skip, skip >*/
PIKA_CONTEXT_PROP_BUFFER = 18,
PIKA_CONTEXT_PROP_IMAGEFILE = 19,
PIKA_CONTEXT_PROP_TEMPLATE = 20,
PIKA_CONTEXT_PROP_EXPAND = 21,
PIKA_CONTEXT_PROP_LAST = PIKA_CONTEXT_PROP_TEMPLATE
} PikaContextPropType;
@ -740,6 +742,7 @@ typedef enum /*< pdb-skip, skip >*/
PIKA_CONTEXT_PROP_MASK_BUFFER = 1 << 18,
PIKA_CONTEXT_PROP_MASK_IMAGEFILE = 1 << 19,
PIKA_CONTEXT_PROP_MASK_TEMPLATE = 1 << 20,
PIKA_CONTEXT_PROP_MASK_EXPAND = 1 << 21,
/* aliases */
PIKA_CONTEXT_PROP_MASK_PAINT = (PIKA_CONTEXT_PROP_MASK_FOREGROUND |
@ -749,7 +752,8 @@ typedef enum /*< pdb-skip, skip >*/
PIKA_CONTEXT_PROP_MASK_BRUSH |
PIKA_CONTEXT_PROP_MASK_DYNAMICS |
PIKA_CONTEXT_PROP_MASK_PATTERN |
PIKA_CONTEXT_PROP_MASK_GRADIENT),
PIKA_CONTEXT_PROP_MASK_GRADIENT |
PIKA_CONTEXT_PROP_MASK_EXPAND),
PIKA_CONTEXT_PROP_MASK_ALL = (PIKA_CONTEXT_PROP_MASK_IMAGE |
PIKA_CONTEXT_PROP_MASK_DISPLAY |

View File

@ -33,8 +33,10 @@
#include "pikacontainer.h"
#include "pikacontext.h"
#include "pikadisplay.h"
#include "pikadrawable.h"
#include "pikaimage.h"
#include "pikaprogress.h"
#include "pikaresource.h"
#include "pikawaitable.h"
#include "about.h"
@ -325,17 +327,17 @@ pika_get_empty_display (Pika *pika)
return NULL;
}
guint32
GBytes *
pika_get_display_window_id (Pika *pika,
PikaDisplay *display)
{
g_return_val_if_fail (PIKA_IS_PIKA (pika), -1);
g_return_val_if_fail (PIKA_IS_DISPLAY (display), -1);
g_return_val_if_fail (PIKA_IS_PIKA (pika), NULL);
g_return_val_if_fail (PIKA_IS_DISPLAY (display), NULL);
if (pika->gui.display_get_window_id)
return pika->gui.display_get_window_id (display);
return -1;
return NULL;
}
PikaDisplay *
@ -407,10 +409,11 @@ gboolean
pika_pdb_dialog_new (Pika *pika,
PikaContext *context,
PikaProgress *progress,
PikaContainer *container,
GType contents_type,
GBytes *parent_handle,
const gchar *title,
const gchar *callback_name,
const gchar *object_name,
PikaObject *object,
...)
{
gboolean retval = FALSE;
@ -418,7 +421,10 @@ pika_pdb_dialog_new (Pika *pika,
g_return_val_if_fail (PIKA_IS_PIKA (pika), FALSE);
g_return_val_if_fail (PIKA_IS_CONTEXT (context), FALSE);
g_return_val_if_fail (progress == NULL || PIKA_IS_PROGRESS (progress), FALSE);
g_return_val_if_fail (PIKA_IS_CONTAINER (container), FALSE);
g_return_val_if_fail (g_type_is_a (contents_type, PIKA_TYPE_RESOURCE) ||
g_type_is_a (contents_type, PIKA_TYPE_DRAWABLE), FALSE);
g_return_val_if_fail (object == NULL ||
g_type_is_a (G_TYPE_FROM_INSTANCE (object), contents_type), FALSE);
g_return_val_if_fail (title != NULL, FALSE);
g_return_val_if_fail (callback_name != NULL, FALSE);
@ -426,12 +432,11 @@ pika_pdb_dialog_new (Pika *pika,
{
va_list args;
va_start (args, object_name);
va_start (args, object);
retval = pika->gui.pdb_dialog_new (pika, context, progress,
container, title,
callback_name, object_name,
args);
contents_type, parent_handle, title,
callback_name, object, args);
va_end (args);
}
@ -440,27 +445,28 @@ pika_pdb_dialog_new (Pika *pika,
}
gboolean
pika_pdb_dialog_set (Pika *pika,
PikaContainer *container,
const gchar *callback_name,
const gchar *object_name,
pika_pdb_dialog_set (Pika *pika,
GType contents_type,
const gchar *callback_name,
PikaObject *object,
...)
{
gboolean retval = FALSE;
g_return_val_if_fail (PIKA_IS_PIKA (pika), FALSE);
g_return_val_if_fail (PIKA_IS_CONTAINER (container), FALSE);
g_return_val_if_fail (g_type_is_a (contents_type, PIKA_TYPE_RESOURCE) ||
contents_type == PIKA_TYPE_DRAWABLE, FALSE);
g_return_val_if_fail (callback_name != NULL, FALSE);
g_return_val_if_fail (object_name != NULL, FALSE);
g_return_val_if_fail (object == NULL || g_type_is_a (G_TYPE_FROM_INSTANCE (object), contents_type), FALSE);
if (pika->gui.pdb_dialog_set)
{
va_list args;
va_start (args, object_name);
va_start (args, object);
retval = pika->gui.pdb_dialog_set (pika, container, callback_name,
object_name, args);
retval = pika->gui.pdb_dialog_set (pika, contents_type, callback_name,
object, args);
va_end (args);
}
@ -470,15 +476,16 @@ pika_pdb_dialog_set (Pika *pika,
gboolean
pika_pdb_dialog_close (Pika *pika,
PikaContainer *container,
GType contents_type,
const gchar *callback_name)
{
g_return_val_if_fail (PIKA_IS_PIKA (pika), FALSE);
g_return_val_if_fail (PIKA_IS_CONTAINER (container), FALSE);
g_return_val_if_fail (g_type_is_a (contents_type, PIKA_TYPE_RESOURCE) ||
contents_type == PIKA_TYPE_DRAWABLE, FALSE);
g_return_val_if_fail (callback_name != NULL, FALSE);
if (pika->gui.pdb_dialog_close)
return pika->gui.pdb_dialog_close (pika, container, callback_name);
return pika->gui.pdb_dialog_close (pika, contents_type, callback_name);
return FALSE;
}

View File

@ -58,7 +58,7 @@ struct _PikaGui
PikaObject * (* get_window_strategy) (Pika *pika);
PikaDisplay * (* get_empty_display) (Pika *pika);
guint32 (* display_get_window_id) (PikaDisplay *display);
GBytes * (* display_get_window_id) (PikaDisplay *display);
PikaDisplay * (* display_create) (Pika *pika,
PikaImage *image,
PikaUnit unit,
@ -77,18 +77,19 @@ struct _PikaGui
gboolean (* pdb_dialog_new) (Pika *pika,
PikaContext *context,
PikaProgress *progress,
PikaContainer *container,
GType contents_type,
GBytes *parent_handle,
const gchar *title,
const gchar *callback_name,
const gchar *object_name,
PikaObject *object,
va_list args);
gboolean (* pdb_dialog_set) (Pika *pika,
PikaContainer *container,
GType contents_type,
const gchar *callback_name,
const gchar *object_name,
PikaObject *object,
va_list args);
gboolean (* pdb_dialog_close) (Pika *pika,
PikaContainer *container,
GType contents_type,
const gchar *callback_name);
gboolean (* recent_list_add_file) (Pika *pika,
GFile *file,
@ -126,7 +127,7 @@ PikaDisplay * pika_get_display_by_id (Pika *pika,
gint ID);
gint pika_get_display_id (Pika *pika,
PikaDisplay *display);
guint32 pika_get_display_window_id (Pika *pika,
GBytes * pika_get_display_window_id (Pika *pika,
PikaDisplay *display);
PikaDisplay * pika_create_display (Pika *pika,
PikaImage *image,
@ -175,19 +176,21 @@ GFile * pika_get_icon_theme_dir (Pika *pika);
gboolean pika_pdb_dialog_new (Pika *pika,
PikaContext *context,
PikaProgress *progress,
PikaContainer *container,
GType contents_type,
GBytes *parent_handle,
const gchar *title,
const gchar *callback_name,
const gchar *object_name,
PikaObject *object,
...) G_GNUC_NULL_TERMINATED;
gboolean pika_pdb_dialog_set (Pika *pika,
PikaContainer *container,
GType contents_type,
const gchar *callback_name,
const gchar *object_name,
PikaObject *object,
...) G_GNUC_NULL_TERMINATED;
gboolean pika_pdb_dialog_close (Pika *pika,
PikaContainer *container,
GType contents_type,
const gchar *callback_name);
gboolean pika_recent_list_add_file (Pika *pika,
GFile *file,
const gchar *mime_type);

View File

@ -557,6 +557,9 @@ user_update_menurc_over20 (const GMatchInfo *matched_value,
else if (g_strcmp0 (action_match, "view-rotate-reset") == 0 &&
install->old_major == 2)
new_action_name = g_strdup ("view-reset");
/* select-float became select-cut-float in 3.0 (select-copy-float added). */
else if (g_strcmp0 (action_match, "select-float") == 0)
new_action_name = g_strdup ("select-cut-float");
if (new_action_name == NULL)
new_action_name = g_strdup (action_match);

View File

@ -286,7 +286,8 @@ pika_brush_get_new_preview (PikaViewable *viewable,
guchar *mask;
guchar *buf;
gint x, y;
gboolean scaled = FALSE;
gboolean free_mask = FALSE;
gdouble scale = 1.0;
mask_width = pika_temp_buf_get_width (mask_buf);
mask_height = pika_temp_buf_get_height (mask_buf);
@ -295,45 +296,47 @@ pika_brush_get_new_preview (PikaViewable *viewable,
{
gdouble ratio_x = (gdouble) width / (gdouble) mask_width;
gdouble ratio_y = (gdouble) height / (gdouble) mask_height;
gdouble scale = MIN (ratio_x, ratio_y);
if (scale != 1.0)
scale = MIN (ratio_x, ratio_y);
}
if (PIKA_IS_BRUSH_GENERATED (brush) || scale != 1.0)
{
pika_brush_begin_use (brush);
if (PIKA_IS_BRUSH_GENERATED (brush))
{
pika_brush_begin_use (brush);
PikaBrushGenerated *gen_brush = PIKA_BRUSH_GENERATED (brush);
if (PIKA_IS_BRUSH_GENERATED (brush))
{
PikaBrushGenerated *gen_brush = PIKA_BRUSH_GENERATED (brush);
mask_buf = pika_brush_transform_mask (brush, scale,
(pika_brush_generated_get_aspect_ratio (gen_brush) - 1.0) * 20.0 / 19.0,
pika_brush_generated_get_angle (gen_brush) / -360.0,
FALSE,
pika_brush_generated_get_hardness (gen_brush));
}
else
{
mask_buf = pika_brush_transform_mask (brush, scale, 0.0, 0.0, FALSE, 1.0);
}
mask_buf = pika_brush_transform_mask (brush, scale,
(pika_brush_generated_get_aspect_ratio (gen_brush) - 1.0) * 20.0 / 19.0,
pika_brush_generated_get_angle (gen_brush) / 360.0,
FALSE,
pika_brush_generated_get_hardness (gen_brush));
}
else
mask_buf = pika_brush_transform_mask (brush, scale,
if (! mask_buf)
{
mask_buf = pika_temp_buf_new (1, 1, babl_format ("Y u8"));
pika_temp_buf_data_clear ((PikaTempBuf *) mask_buf);
}
else
{
pika_temp_buf_ref ((PikaTempBuf *) mask_buf);
}
if (pixmap_buf)
pixmap_buf = pika_brush_transform_pixmap (brush, scale,
0.0, 0.0, FALSE, 1.0);
if (! mask_buf)
{
mask_buf = pika_temp_buf_new (1, 1, babl_format ("Y u8"));
pika_temp_buf_data_clear ((PikaTempBuf *) mask_buf);
}
else
{
pika_temp_buf_ref ((PikaTempBuf *) mask_buf);
}
mask_width = pika_temp_buf_get_width (mask_buf);
mask_height = pika_temp_buf_get_height (mask_buf);
if (pixmap_buf)
pixmap_buf = pika_brush_transform_pixmap (brush, scale,
0.0, 0.0, FALSE, 1.0);
mask_width = pika_temp_buf_get_width (mask_buf);
mask_height = pika_temp_buf_get_height (mask_buf);
scaled = TRUE;
}
free_mask = TRUE;
}
return_buf = pika_temp_buf_new (mask_width, mask_height,
@ -381,7 +384,7 @@ pika_brush_get_new_preview (PikaViewable *viewable,
pika_temp_buf_unlock (mask_buf, mask_data);
if (scaled)
if (free_mask)
{
pika_temp_buf_unref ((PikaTempBuf *) mask_buf);

View File

@ -747,7 +747,7 @@ pika_channel_resize (PikaItem *item,
gint offset_x,
gint offset_y)
{
PIKA_ITEM_CLASS (parent_class)->resize (item, context, PIKA_FILL_TRANSPARENT,
PIKA_ITEM_CLASS (parent_class)->resize (item, context, fill_type,
new_width, new_height,
offset_x, offset_y);

View File

@ -202,6 +202,7 @@ pika_container_class_init (PikaContainerClass *klass)
klass->search = NULL;
klass->get_unique_names = NULL;
klass->get_child_by_name = NULL;
klass->get_children_by_name = NULL;
klass->get_child_by_index = NULL;
klass->get_child_index = NULL;
@ -885,6 +886,34 @@ pika_container_get_unique_names (PikaContainer *container)
return FALSE;
}
GList *
pika_container_get_children_by_name (PikaContainer *container,
const gchar *name)
{
g_return_val_if_fail (PIKA_IS_CONTAINER (container), NULL);
if (!name)
return NULL;
if (PIKA_CONTAINER_GET_CLASS (container)->get_children_by_name != NULL &&
! pika_container_get_unique_names (container))
{
return PIKA_CONTAINER_GET_CLASS (container)->get_children_by_name (container,
name);
}
else
{
PikaObject *child;
child = PIKA_CONTAINER_GET_CLASS (container)->get_child_by_name (container, name);
if (child != NULL)
return g_list_prepend (NULL, child);
else
return NULL;
}
}
PikaObject *
pika_container_get_child_by_name (PikaContainer *container,
const gchar *name)

View File

@ -79,6 +79,8 @@ struct _PikaContainerClass
gboolean (* get_unique_names) (PikaContainer *container);
PikaObject * (* get_child_by_name) (PikaContainer *container,
const gchar *name);
GList * (* get_children_by_name) (PikaContainer *container,
const gchar *name);
PikaObject * (* get_child_by_index) (PikaContainer *container,
gint index);
gint (* get_child_index) (PikaContainer *container,
@ -121,6 +123,8 @@ PikaObject * pika_container_search (PikaContainer *contain
gboolean pika_container_get_unique_names (PikaContainer *container);
GList * pika_container_get_children_by_name (PikaContainer *container,
const gchar *name);
PikaObject * pika_container_get_child_by_name (PikaContainer *container,
const gchar *name);
PikaObject * pika_container_get_child_by_index (PikaContainer *container,

View File

@ -34,6 +34,7 @@
#include "pika-memsize.h"
#include "pikadata.h"
#include "pikaidtable.h"
#include "pikaimage.h"
#include "pikatag.h"
#include "pikatagged.h"
@ -51,6 +52,7 @@ enum
PROP_0,
PROP_ID,
PROP_FILE,
PROP_IMAGE,
PROP_WRITABLE,
PROP_DELETABLE,
PROP_MIME_TYPE
@ -59,8 +61,10 @@ enum
struct _PikaDataPrivate
{
gint ID;
GFile *file;
gint ID;
GFile *file;
PikaImage *image;
GQuark mime_type;
guint writable : 1;
guint deletable : 1;
@ -172,6 +176,11 @@ pika_data_class_init (PikaDataClass *klass)
G_TYPE_FILE,
PIKA_PARAM_READWRITE));
g_object_class_install_property (object_class, PROP_IMAGE,
g_param_spec_object ("image", NULL, NULL,
PIKA_TYPE_IMAGE,
PIKA_PARAM_READWRITE));
g_object_class_install_property (object_class, PROP_WRITABLE,
g_param_spec_boolean ("writable", NULL, NULL,
FALSE,
@ -268,6 +277,12 @@ pika_data_set_property (GObject *object,
private->deletable);
break;
case PROP_IMAGE:
pika_data_set_image (data,
g_value_get_object (value),
private->writable,
private->deletable);
break;
case PROP_WRITABLE:
private->writable = g_value_get_boolean (value);
break;
@ -307,6 +322,10 @@ pika_data_get_property (GObject *object,
g_value_set_object (value, private->file);
break;
case PROP_IMAGE:
g_value_set_object (value, private->image);
break;
case PROP_WRITABLE:
g_value_set_boolean (value, private->writable);
break;
@ -462,7 +481,7 @@ pika_data_get_identifier (PikaTagged *tagged)
gchar *identifier = NULL;
gchar *collection = NULL;
g_return_val_if_fail (private->internal || private->file != NULL, NULL);
g_return_val_if_fail (private->internal || private->file != NULL || private->image != NULL, NULL);
collection = pika_data_get_collection (data);
/* The identifier is guaranteed to be unique because we use 2 directory
@ -503,7 +522,7 @@ pika_data_get_collection (PikaData *data)
PikaDataPrivate *private = PIKA_DATA_GET_PRIVATE (data);
gchar *collection = NULL;
g_return_val_if_fail (private->internal || private->file != NULL, NULL);
g_return_val_if_fail (private->internal || private->file != NULL || private->image != NULL, NULL);
if (private->file)
{
@ -551,6 +570,10 @@ pika_data_get_collection (PikaData *data)
g_free (path);
}
else if (private->image)
{
collection = g_strdup_printf ("[image-id-%d]", pika_image_get_id (private->image));
}
else if (private->internal)
{
collection = g_strdup (private->collection);
@ -603,7 +626,7 @@ pika_data_save (PikaData *data,
g_return_val_if_fail (private->writable == TRUE, FALSE);
if (private->internal)
if (private->internal || private->image != NULL)
{
private->dirty = FALSE;
return TRUE;
@ -862,6 +885,8 @@ pika_data_set_file (PikaData *data,
if (private->internal)
return;
g_return_if_fail (private->image == NULL);
g_set_object (&private->file, file);
private->writable = FALSE;
@ -937,6 +962,53 @@ pika_data_get_file (PikaData *data)
return private->file;
}
/**
* pika_data_set_image:
* @data: A #PikaData object
* @image: Image to assign to @data.
* @writable: %TRUE if we want to be able to write to this file.
* @deletable: %TRUE if we want to be able to delete this file.
*
* This function assigns an image to @data. This can only be done if no file has
* been assigned (a non-internal data can be attached either to a file or to an
* image).
**/
void
pika_data_set_image (PikaData *data,
PikaImage *image,
gboolean writable,
gboolean deletable)
{
PikaDataPrivate *private;
g_return_if_fail (PIKA_IS_DATA (data));
g_return_if_fail (PIKA_IS_IMAGE (image));
private = PIKA_DATA_GET_PRIVATE (data);
if (private->internal)
return;
g_return_if_fail (private->file == NULL);
g_set_object (&private->image, image);
private->writable = writable ? TRUE : FALSE;
private->deletable = deletable ? TRUE : FALSE;
}
PikaImage *
pika_data_get_image (PikaData *data)
{
PikaDataPrivate *private;
g_return_val_if_fail (PIKA_IS_DATA (data), NULL);
private = PIKA_DATA_GET_PRIVATE (data);
return private->image;
}
/**
* pika_data_create_filename:
* @data: a #Pikadata object.

View File

@ -102,6 +102,11 @@ void pika_data_set_file (PikaData *data,
gboolean writable,
gboolean deletable);
GFile * pika_data_get_file (PikaData *data);
void pika_data_set_image (PikaData *data,
PikaImage *image,
gboolean writable,
gboolean deletable);
PikaImage * pika_data_get_image (PikaData *data);
void pika_data_create_filename (PikaData *data,
GFile *dest_dir);

View File

@ -403,6 +403,9 @@ pika_data_factory_real_data_save (PikaDataFactory *factory)
PikaData *data = list->data;
GError *error = NULL;
if (pika_data_get_image (data))
continue;
if (! pika_data_get_file (data))
pika_data_create_filename (data, writable_dir);
@ -460,6 +463,7 @@ pika_data_factory_real_data_duplicate (PikaDataFactory *factory,
gint copy_len;
gint number;
gchar *new_name;
GError *error = NULL;
ext = strrchr (name, '#');
copy_len = strlen (_("copy"));
@ -479,8 +483,12 @@ pika_data_factory_real_data_duplicate (PikaDataFactory *factory,
pika_object_take_name (PIKA_OBJECT (new_data), new_name);
if (! pika_data_factory_data_save_single (factory, new_data, &error))
g_critical ("%s: data saving failed: %s", G_STRFUNC, error->message);
pika_container_add (priv->container, PIKA_OBJECT (new_data));
g_object_unref (new_data);
g_clear_error (&error);
}
return new_data;
@ -674,8 +682,14 @@ pika_data_factory_data_new (PikaDataFactory *factory,
if (data)
{
GError *error = NULL;
if (! pika_data_factory_data_save_single (factory, data, &error))
g_critical ("%s: data saving failed: %s", G_STRFUNC, error->message);
pika_container_add (priv->container, PIKA_OBJECT (data));
g_object_unref (data);
g_clear_error (&error);
return data;
}
@ -750,7 +764,7 @@ pika_data_factory_data_save_single (PikaDataFactory *factory,
g_return_val_if_fail (PIKA_IS_DATA (data), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
if (! pika_data_is_dirty (data))
if (! pika_data_is_dirty (data) || pika_data_get_image (data))
return TRUE;
if (! pika_data_get_file (data))

View File

@ -45,6 +45,8 @@ struct _PikaDrawablePrivate
GeglBuffer *paint_buffer;
cairo_region_t *paint_copy_region;
cairo_region_t *paint_update_region;
gboolean push_resize_undo;
};
#endif /* __PIKA_DRAWABLE_PRIVATE_H__ */

View File

@ -632,7 +632,10 @@ pika_drawable_resize (PikaItem *item,
copy_y - new_offset_y, 0, 0));
}
pika_drawable_set_buffer_full (drawable, pika_item_is_attached (item), NULL,
pika_drawable_set_buffer_full (drawable,
pika_item_is_attached (item) &&
drawable->private->push_resize_undo,
NULL,
new_buffer,
GEGL_RECTANGLE (new_offset_x, new_offset_y,
0, 0),
@ -910,6 +913,10 @@ pika_drawable_real_set_buffer (PikaDrawable *drawable,
}
g_set_object (&drawable->private->buffer, buffer);
if (pika_drawable_is_painting (drawable))
g_set_object (&drawable->private->paint_buffer, buffer);
g_clear_object (&drawable->private->format_profile);
if (drawable->private->buffer_source_node)
@ -1076,6 +1083,8 @@ pika_drawable_new (GType type,
pika_drawable_set_buffer (drawable, FALSE, NULL, buffer);
g_object_unref (buffer);
pika_drawable_enable_resize_undo (drawable);
return drawable;
}
@ -1676,6 +1685,22 @@ pika_drawable_push_undo (PikaDrawable *drawable,
x, y, width, height);
}
void
pika_drawable_disable_resize_undo (PikaDrawable *drawable)
{
g_return_if_fail (PIKA_IS_DRAWABLE (drawable));
drawable->private->push_resize_undo = FALSE;
}
void
pika_drawable_enable_resize_undo (PikaDrawable *drawable)
{
g_return_if_fail (PIKA_IS_DRAWABLE (drawable));
drawable->private->push_resize_undo = TRUE;
}
const Babl *
pika_drawable_get_space (PikaDrawable *drawable)
{

View File

@ -209,6 +209,9 @@ void pika_drawable_push_undo (PikaDrawable *drawable,
gint width,
gint height);
void pika_drawable_disable_resize_undo (PikaDrawable *drawable);
void pika_drawable_enable_resize_undo (PikaDrawable *drawable);
const Babl * pika_drawable_get_space (PikaDrawable *drawable);
const Babl * pika_drawable_get_format (PikaDrawable *drawable);
const Babl * pika_drawable_get_format_with_alpha(PikaDrawable *drawable);

View File

@ -31,25 +31,43 @@
#include "core-types.h"
#include "config/pikageglconfig.h"
#include "gegl/pika-babl.h"
#include "gegl/pika-gegl-loops.h"
#include "pika.h"
#include "pikacontainer.h"
#include "pikadatafactory.h"
#include "pikadrawable.h"
#include "pikaimage.h"
#include "pikaimage-colormap.h"
#include "pikaimage-private.h"
#include "pikaimage-undo.h"
#include "pikaimage-undo-push.h"
#include "pikapalette.h"
#include "pika-intl.h"
typedef struct
{
GeglBuffer *buffer;
const Babl *format;
/* Shared by jobs. */
gboolean *found;
GRWLock *lock;
} IndexUsedJobData;
/* local function prototype */
static void pika_image_colormap_set_palette_entry (PikaImage *image,
const PikaRGB *color,
gint index);
static void pika_image_colormap_set_palette_entry (PikaImage *image,
const PikaRGB *color,
gint index);
static void pika_image_colormap_thread_is_index_used (IndexUsedJobData *data,
gint index);
/* public functions */
@ -80,7 +98,7 @@ pika_image_colormap_init (PikaImage *image)
pika_palette_set_columns (private->palette, 16);
pika_data_make_internal (PIKA_DATA (private->palette), palette_id);
pika_data_set_image (PIKA_DATA (private->palette), image, TRUE, FALSE);
palettes = pika_data_factory_get_container (image->pika->palette_factory);
@ -319,9 +337,8 @@ pika_image_set_colormap (PikaImage *image,
pika_image_colormap_set_palette_entry (image, &color, i);
}
pika_data_thaw (PIKA_DATA (private->palette));
pika_image_colormap_changed (image, -1);
pika_data_thaw (PIKA_DATA (private->palette));
}
void
@ -347,6 +364,45 @@ pika_image_unset_colormap (PikaImage *image,
pika_image_colormap_changed (image, -1);
}
gboolean
pika_image_colormap_is_index_used (PikaImage *image,
gint color_index)
{
GList *layers;
GList *iter;
GThreadPool *pool;
GRWLock lock;
gboolean found = FALSE;
gint num_processors;
g_rw_lock_init (&lock);
num_processors = PIKA_GEGL_CONFIG (image->pika->config)->num_processors;
layers = pika_image_get_layer_list (image);
pool = g_thread_pool_new_full ((GFunc) pika_image_colormap_thread_is_index_used,
GINT_TO_POINTER (color_index),
(GDestroyNotify) g_free,
num_processors, TRUE, NULL);
for (iter = layers; iter; iter = g_list_next (iter))
{
IndexUsedJobData *job_data;
job_data = g_malloc (sizeof (IndexUsedJobData ));
job_data->buffer = pika_drawable_get_buffer (iter->data);
job_data->format = pika_drawable_get_format_without_alpha (iter->data);
job_data->found = &found;
job_data->lock = &lock;
g_thread_pool_push (pool, job_data, NULL);
}
g_thread_pool_free (pool, FALSE, TRUE);
g_rw_lock_clear (&lock);
g_list_free (layers);
return found;
}
void
pika_image_get_colormap_entry (PikaImage *image,
gint color_index,
@ -420,6 +476,57 @@ pika_image_add_colormap_entry (PikaImage *image,
pika_image_colormap_changed (image, -1);
}
gboolean
pika_image_delete_colormap_entry (PikaImage *image,
gint color_index,
gboolean push_undo)
{
g_return_val_if_fail (PIKA_IS_IMAGE (image), FALSE);
if (! pika_image_colormap_is_index_used (image, color_index))
{
PikaImagePrivate *private;
PikaPaletteEntry *entry;
GList *layers;
GList *iter;
if (push_undo)
{
pika_image_undo_group_start (image, PIKA_UNDO_GROUP_IMAGE_COLORMAP_REMAP,
C_("undo-type", "Delete Colormap entry"));
pika_image_undo_push_image_colormap (image, NULL);
}
private = PIKA_IMAGE_GET_PRIVATE (image);
layers = pika_image_get_layer_list (image);
for (iter = layers; iter; iter = g_list_next (iter))
{
if (push_undo)
pika_image_undo_push_drawable_mod (image, NULL, iter->data, TRUE);
pika_gegl_shift_index (pika_drawable_get_buffer (iter->data), NULL,
pika_drawable_get_format (iter->data),
color_index, -1);
}
entry = pika_palette_get_entry (private->palette, color_index);
pika_palette_delete_entry (private->palette, entry);
g_list_free (layers);
if (push_undo)
pika_image_undo_group_end (image);
pika_image_colormap_changed (image, -1);
return TRUE;
}
return FALSE;
}
/* private functions */
@ -442,3 +549,22 @@ pika_image_colormap_set_palette_entry (PikaImage *image,
pika_palette_set_entry (private->palette, index, name, color);
}
static void
pika_image_colormap_thread_is_index_used (IndexUsedJobData *data,
gint index)
{
g_rw_lock_reader_lock (data->lock);
if (*data->found)
{
g_rw_lock_reader_unlock (data->lock);
return;
}
g_rw_lock_reader_unlock (data->lock);
if (pika_gegl_is_index_used (data->buffer, NULL, data->format, index))
{
g_rw_lock_writer_lock (data->lock);
*data->found = TRUE;
g_rw_lock_writer_unlock (data->lock);
}
}

View File

@ -49,6 +49,9 @@ void pika_image_set_colormap (PikaImage *image,
void pika_image_unset_colormap (PikaImage *image,
gboolean push_undo);
gboolean pika_image_colormap_is_index_used (PikaImage *image,
gint color_index);
void pika_image_get_colormap_entry (PikaImage *image,
gint color_index,
PikaRGB *color);
@ -59,6 +62,9 @@ void pika_image_set_colormap_entry (PikaImage *image,
void pika_image_add_colormap_entry (PikaImage *image,
const PikaRGB *color);
gboolean pika_image_delete_colormap_entry (PikaImage *image,
gint color_index,
gboolean push_undo);
#endif /* __PIKA_IMAGE_COLORMAP_H__ */

View File

@ -24,17 +24,22 @@
#include <cairo.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <gegl.h>
#include <gexiv2/gexiv2.h>
#include "libpikabase/pikabase.h"
#include "libpikacolor/pikacolor.h"
#include "core-types.h"
#include "pika.h"
#include "pikaimage.h"
#include "pikaimage-color-profile.h"
#include "pikaimage-metadata.h"
#include "pikaimage-private.h"
#include "pikaimage-rotate.h"
#include "pikaimage-undo.h"
#include "pikaimage-undo-push.h"
#include "pikalayer-new.h"
/* public functions */
@ -186,3 +191,110 @@ pika_image_metadata_update_colorspace (PikaImage *image)
pika_metadata_set_colorspace (metadata, space);
}
}
/**
* pika_image_metadata_load_thumbnail:
* @pika: The #Pika object.
* @file: A #GFile image.
* @full_image_width: the width of the full image (not the thumbnail).
* @full_image_height: the height of the full image (not the thumbnail).
* @error: Return location for error message
*
* Retrieves a thumbnail from metadata in @file if present.
* It does not need to actually load the full image, only the metadata through
* GExiv2, which makes it fast.
*
* Returns: (transfer none) (nullable): a #PikaImage of the @file thumbnail.
*/
PikaImage *
pika_image_metadata_load_thumbnail (Pika *pika,
GFile *file,
gint *full_image_width,
gint *full_image_height,
const Babl **format,
GError **error)
{
PikaMetadata *metadata;
GInputStream *input_stream;
GdkPixbuf *pixbuf;
guint8 *thumbnail_buffer;
gint thumbnail_size;
PikaImage *image = NULL;
g_return_val_if_fail (G_IS_FILE (file), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
metadata = pika_metadata_load_from_file (file, error);
if (! metadata)
return NULL;
if (! gexiv2_metadata_get_exif_thumbnail (GEXIV2_METADATA (metadata),
&thumbnail_buffer,
&thumbnail_size))
{
g_object_unref (metadata);
return NULL;
}
input_stream = g_memory_input_stream_new_from_data (thumbnail_buffer,
thumbnail_size,
(GDestroyNotify) g_free);
pixbuf = gdk_pixbuf_new_from_stream (input_stream, NULL, error);
g_object_unref (input_stream);
if (pixbuf)
{
PikaLayer *layer;
image = pika_image_new (pika,
gdk_pixbuf_get_width (pixbuf),
gdk_pixbuf_get_height (pixbuf),
PIKA_RGB, PIKA_PRECISION_U8_NON_LINEAR);
pika_image_undo_disable (image);
/* XXX This is possibly wrong, because an image of a given color space may
* have a thumbnail stored in a different colorspace. This is even more
* true with PIKA which always exports RGB thumbnails (see code in
* pika_image_metadata_save_filter()), even for say grayscale images.
* Nevertheless other software may store thumbnail using the same
* colorspace.
*/
*format = pika_pixbuf_get_format (pixbuf);
layer = pika_layer_new_from_pixbuf (pixbuf, image,
pika_image_get_layer_format (image, FALSE),
/* No need to localize; this image is short-lived. */
"Background",
PIKA_OPACITY_OPAQUE,
pika_image_get_default_new_layer_mode (image));
g_object_unref (pixbuf);
pika_image_add_layer (image, layer, NULL, 0, FALSE);
pika_image_apply_metadata_orientation (image, pika_get_user_context (pika), metadata, NULL);
}
/* This is the unoriented dimensions. Should we switch when there is a 90 or
* 270 degree rotation? We don't actually know if the metadata orientation is
* correct.
*/
*full_image_width = gexiv2_metadata_get_pixel_width (GEXIV2_METADATA (metadata));
*full_image_height = gexiv2_metadata_get_pixel_height (GEXIV2_METADATA (metadata));
if (*full_image_width < 1 || *full_image_height < 1)
{
/* Dimensions stored in metadata might be less accurate, yet it's still
* informational.
*/
*full_image_width = gexiv2_metadata_try_get_metadata_pixel_width (GEXIV2_METADATA (metadata), NULL);
*full_image_height = gexiv2_metadata_try_get_metadata_pixel_width (GEXIV2_METADATA (metadata), NULL);
}
if (*full_image_width < 1 || *full_image_height < 1)
{
*full_image_width = 0;
*full_image_height = 0;
}
g_object_unref (metadata);
return image;
}

View File

@ -33,5 +33,12 @@ void pika_image_metadata_update_bits_per_sample (PikaImage *image);
void pika_image_metadata_update_resolution (PikaImage *image);
void pika_image_metadata_update_colorspace (PikaImage *image);
PikaImage * pika_image_metadata_load_thumbnail (Pika *pika,
GFile *file,
gint *full_image_width,
gint *full_image_height,
const Babl **format,
GError **error);
#endif /* __PIKA_IMAGE_METADATA_H__ */

View File

@ -282,6 +282,17 @@ pika_image_import_rotation_metadata (PikaImage *image,
}
}
void
pika_image_apply_metadata_orientation (PikaImage *image,
PikaContext *context,
PikaMetadata *metadata,
PikaProgress *progress)
{
pika_image_metadata_rotate (image, context,
gexiv2_metadata_try_get_orientation (GEXIV2_METADATA (metadata), NULL),
progress);
}
/* Private Functions */

View File

@ -23,14 +23,20 @@
#define __PIKA_IMAGE_ROTATE_H__
void pika_image_rotate (PikaImage *image,
PikaContext *context,
PikaRotationType rotate_type,
PikaProgress *progress);
void pika_image_rotate (PikaImage *image,
PikaContext *context,
PikaRotationType rotate_type,
PikaProgress *progress);
void pika_image_import_rotation_metadata (PikaImage *image,
PikaContext *context,
PikaProgress *progress,
gboolean interactive);
void pika_image_apply_metadata_orientation (PikaImage *image,
PikaContext *context,
PikaMetadata *metadata,
PikaProgress *progress);
void pika_image_import_rotation_metadata (PikaImage *image,
PikaContext *context,
PikaProgress *progress,
gboolean interactive);
#endif /* __PIKA_IMAGE_ROTATE_H__ */

View File

@ -625,6 +625,9 @@ pika_image_undo_dirty_from_type (PikaUndoType undo_type)
case PIKA_UNDO_GROUP_IMAGE_CONVERT:
return PIKA_DIRTY_IMAGE | PIKA_DIRTY_DRAWABLE;
case PIKA_UNDO_GROUP_IMAGE_COLORMAP_REMAP:
return PIKA_DIRTY_IMAGE | PIKA_DIRTY_DRAWABLE;
case PIKA_UNDO_GROUP_IMAGE_LAYERS_MERGE:
return PIKA_DIRTY_IMAGE_STRUCTURE | PIKA_DIRTY_DRAWABLE;

View File

@ -45,6 +45,7 @@
#include "pikacontainer.h"
#include "pikacontext.h"
#include "pikaimage.h"
#include "pikaimage-metadata.h"
#include "pikaimagefile.h"
#include "pikapickable.h"
#include "pikaprogress.h"
@ -486,19 +487,37 @@ pika_imagefile_create_thumbnail (PikaImagefile *imagefile,
if (error && *error)
{
g_printerr ("Info: Thumbnail load procedure failed: %s\n"
" Falling back to file load procedure.\n",
" Falling back to metadata or file load.\n",
(*error)->message);
g_clear_error (error);
}
image = file_open_image (private->pika, context, progress,
private->file,
FALSE, NULL, PIKA_RUN_NONINTERACTIVE,
&status, &mime_type, error);
image = pika_image_metadata_load_thumbnail (private->pika, private->file, &width, &height, &format, error);
if (image)
pika_thumbnail_set_info_from_image (private->thumbnail,
mime_type, image);
{
pika_thumbnail_set_info (private->thumbnail,
mime_type, width, height,
format, 0);
}
else
{
if (error && *error)
{
g_printerr ("Info: metadata load failed: %s\n"
" Falling back to file load procedure.\n",
(*error)->message);
g_clear_error (error);
}
image = file_open_image (private->pika, context, progress,
private->file,
FALSE, NULL, PIKA_RUN_NONINTERACTIVE,
&status, &mime_type, error);
if (image)
pika_thumbnail_set_info_from_image (private->thumbnail,
mime_type, image);
}
}
if (image)

View File

@ -76,6 +76,8 @@ static PikaObject * pika_list_search (PikaContainer *conta
PikaContainerSearchFunc func,
gpointer user_data);
static gboolean pika_list_get_unique_names (PikaContainer *container);
static GList * pika_list_get_children_by_name (PikaContainer *container,
const gchar *name);
static PikaObject * pika_list_get_child_by_name (PikaContainer *container,
const gchar *name);
static PikaObject * pika_list_get_child_by_index (PikaContainer *container,
@ -101,23 +103,24 @@ pika_list_class_init (PikaListClass *klass)
PikaObjectClass *pika_object_class = PIKA_OBJECT_CLASS (klass);
PikaContainerClass *container_class = PIKA_CONTAINER_CLASS (klass);
object_class->finalize = pika_list_finalize;
object_class->set_property = pika_list_set_property;
object_class->get_property = pika_list_get_property;
object_class->finalize = pika_list_finalize;
object_class->set_property = pika_list_set_property;
object_class->get_property = pika_list_get_property;
pika_object_class->get_memsize = pika_list_get_memsize;
pika_object_class->get_memsize = pika_list_get_memsize;
container_class->add = pika_list_add;
container_class->remove = pika_list_remove;
container_class->reorder = pika_list_reorder;
container_class->clear = pika_list_clear;
container_class->have = pika_list_have;
container_class->foreach = pika_list_foreach;
container_class->search = pika_list_search;
container_class->get_unique_names = pika_list_get_unique_names;
container_class->get_child_by_name = pika_list_get_child_by_name;
container_class->get_child_by_index = pika_list_get_child_by_index;
container_class->get_child_index = pika_list_get_child_index;
container_class->add = pika_list_add;
container_class->remove = pika_list_remove;
container_class->reorder = pika_list_reorder;
container_class->clear = pika_list_clear;
container_class->have = pika_list_have;
container_class->foreach = pika_list_foreach;
container_class->search = pika_list_search;
container_class->get_unique_names = pika_list_get_unique_names;
container_class->get_children_by_name = pika_list_get_children_by_name;
container_class->get_child_by_name = pika_list_get_child_by_name;
container_class->get_child_by_index = pika_list_get_child_by_index;
container_class->get_child_index = pika_list_get_child_index;
g_object_class_install_property (object_class, PROP_UNIQUE_NAMES,
g_param_spec_boolean ("unique-names",
@ -369,6 +372,29 @@ pika_list_get_unique_names (PikaContainer *container)
return list->unique_names;
}
static GList *
pika_list_get_children_by_name (PikaContainer *container,
const gchar *name)
{
PikaList *list = PIKA_LIST (container);
GList *children = NULL;
GList *iter;
for (iter = list->queue->head; iter; iter = g_list_next (iter))
{
PikaObject *object = iter->data;
if (! strcmp (pika_object_get_name (object), name))
{
children = g_list_prepend (children, object);
if (list->unique_names)
return children;
}
}
return children;
}
static PikaObject *
pika_list_get_child_by_name (PikaContainer *container,
const gchar *name)

View File

@ -1083,9 +1083,6 @@ pika_palette_load_ase (PikaContext *context,
}
g_free (palette_name);
/* Header blocks are considered a "color" so we offset the count here */
num_cols -= 1;
for (i = 0; i < num_cols; i++)
{
gchar color_space[4];
@ -1108,6 +1105,23 @@ pika_palette_load_ase (PikaContext *context,
}
skip_first = FALSE;
/* Skip group marker padding */
group = GINT16_FROM_BE (group);
if (group < 0)
{
gchar marker[4];
if (! g_input_stream_read_all (input, &marker, sizeof (marker),
&bytes_read, NULL, error))
{
g_printerr ("Invalid ASE group marker: %s.",
pika_file_get_utf8_name (file));
break;
}
num_cols--;
continue;
}
color_name = pika_palette_load_ase_block_name (input, file_size, error);
if (! color_name)
break;
@ -1148,7 +1162,7 @@ pika_palette_load_ase (PikaContext *context,
for (gint j = 0; j < components; j++)
{
gint tmp;
gint32 tmp;
if (! g_input_stream_read_all (input, &tmp, sizeof (tmp),
&bytes_read, NULL, error))
@ -1162,7 +1176,7 @@ pika_palette_load_ase (PikaContext *context,
/* Convert 4 bytes to a 32bit float value */
tmp = GINT32_FROM_BE (tmp);
pixels[j] = *(gfloat *) &tmp;
memcpy (&pixels[j], &tmp, 4);
}
if (! valid_color)

View File

@ -33,6 +33,8 @@
#include "core-types.h"
#include "pika-memsize.h"
#include "pikaimage.h"
#include "pikaimage-undo-push.h"
#include "pikapalette.h"
#include "pikapalette-load.h"
#include "pikapalette-save.h"
@ -44,6 +46,12 @@
#define RGB_EPSILON 1e-6
enum
{
ENTRY_CHANGED,
LAST_SIGNAL
};
/* local function prototypes */
@ -75,6 +83,8 @@ static gchar * pika_palette_get_description (PikaViewable *viewa
static const gchar * pika_palette_get_extension (PikaData *data);
static void pika_palette_copy (PikaData *data,
PikaData *src_data);
static void pika_palette_real_entry_changed (PikaPalette *palette,
gint index);
static void pika_palette_entry_free (PikaPaletteEntry *entry);
static gint64 pika_palette_entry_get_memsize (PikaPaletteEntry *entry,
@ -88,6 +98,7 @@ G_DEFINE_TYPE_WITH_CODE (PikaPalette, pika_palette, PIKA_TYPE_DATA,
#define parent_class pika_palette_parent_class
static guint signals[LAST_SIGNAL] = { 0 };
static void
pika_palette_class_init (PikaPaletteClass *klass)
@ -97,6 +108,14 @@ pika_palette_class_init (PikaPaletteClass *klass)
PikaViewableClass *viewable_class = PIKA_VIEWABLE_CLASS (klass);
PikaDataClass *data_class = PIKA_DATA_CLASS (klass);
signals[ENTRY_CHANGED] = g_signal_new ("entry-changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (PikaPaletteClass, entry_changed),
NULL, NULL, NULL,
G_TYPE_NONE, 1,
G_TYPE_INT);
object_class->finalize = pika_palette_finalize;
pika_object_class->get_memsize = pika_palette_get_memsize;
@ -110,6 +129,8 @@ pika_palette_class_init (PikaPaletteClass *klass)
data_class->save = pika_palette_save;
data_class->get_extension = pika_palette_get_extension;
data_class->copy = pika_palette_copy;
klass->entry_changed = pika_palette_real_entry_changed;
}
static void
@ -344,6 +365,16 @@ pika_palette_copy (PikaData *data,
pika_data_thaw (data);
}
static void
pika_palette_real_entry_changed (PikaPalette *palette,
gint index)
{
PikaImage *image = pika_data_get_image (PIKA_DATA (palette));
if (image != NULL)
pika_image_colormap_changed (image, index);
}
static gchar *
pika_palette_get_checksum (PikaTagged *tagged)
{
@ -406,11 +437,17 @@ pika_palette_move_entry (PikaPalette *palette,
if (g_list_find (palette->colors, entry))
{
gint old_position = g_list_index (palette->colors, entry);
palette->colors = g_list_remove (palette->colors,
entry);
palette->colors = g_list_insert (palette->colors,
entry, position);
if (! pika_data_is_frozen (PIKA_DATA (palette)))
for (gint i = MIN (position, old_position); i <= MAX (position, old_position); i++)
g_signal_emit (palette, signals[ENTRY_CHANGED], 0, i);
pika_data_dirty (PIKA_DATA (palette));
}
}
@ -442,6 +479,10 @@ pika_palette_add_entry (PikaPalette *palette,
palette->n_colors += 1;
if (! pika_data_is_frozen (PIKA_DATA (palette)))
for (gint i = position; i < palette->n_colors; i++)
g_signal_emit (palette, signals[ENTRY_CHANGED], 0, i);
pika_data_dirty (PIKA_DATA (palette));
return entry;
@ -456,12 +497,18 @@ pika_palette_delete_entry (PikaPalette *palette,
if (g_list_find (palette->colors, entry))
{
gint old_position = g_list_index (palette->colors, entry);
pika_palette_entry_free (entry);
palette->colors = g_list_remove (palette->colors, entry);
palette->n_colors--;
if (! pika_data_is_frozen (PIKA_DATA (palette)))
for (gint i = old_position; i < palette->n_colors; i++)
g_signal_emit (palette, signals[ENTRY_CHANGED], 0, i);
pika_data_dirty (PIKA_DATA (palette));
}
}
@ -489,6 +536,9 @@ pika_palette_set_entry (PikaPalette *palette,
entry->name = g_strdup (name);
if (! pika_data_is_frozen (PIKA_DATA (palette)))
g_signal_emit (palette, signals[ENTRY_CHANGED], 0, position);
pika_data_dirty (PIKA_DATA (palette));
return TRUE;
@ -497,7 +547,8 @@ pika_palette_set_entry (PikaPalette *palette,
gboolean
pika_palette_set_entry_color (PikaPalette *palette,
gint position,
const PikaRGB *color)
const PikaRGB *color,
gboolean push_undo_if_image)
{
PikaPaletteEntry *entry;
@ -509,8 +560,15 @@ pika_palette_set_entry_color (PikaPalette *palette,
if (! entry)
return FALSE;
if (push_undo_if_image && pika_data_get_image (PIKA_DATA (palette)))
pika_image_undo_push_image_colormap (pika_data_get_image (PIKA_DATA (palette)),
C_("undo-type", "Change Colormap entry"));
entry->color = *color;
if (! pika_data_is_frozen (PIKA_DATA (palette)))
g_signal_emit (palette, signals[ENTRY_CHANGED], 0, position);
pika_data_dirty (PIKA_DATA (palette));
return TRUE;
@ -535,6 +593,9 @@ pika_palette_set_entry_name (PikaPalette *palette,
entry->name = g_strdup (name);
if (! pika_data_is_frozen (PIKA_DATA (palette)))
g_signal_emit (palette, signals[ENTRY_CHANGED], 0, position);
pika_data_dirty (PIKA_DATA (palette));
return TRUE;

View File

@ -56,6 +56,9 @@ struct _PikaPalette
struct _PikaPaletteClass
{
PikaDataClass parent_class;
void (* entry_changed) (PikaPalette *palette,
gint index);
};
@ -85,7 +88,8 @@ gboolean pika_palette_set_entry (PikaPalette *palette,
const PikaRGB *color);
gboolean pika_palette_set_entry_color (PikaPalette *palette,
gint position,
const PikaRGB *color);
const PikaRGB *color,
gboolean push_undo_if_image);
gboolean pika_palette_set_entry_name (PikaPalette *palette,
gint position,
const gchar *name);

View File

@ -217,7 +217,7 @@ pika_palette_mru_add (PikaPaletteMru *mru,
/* Even though they are nearly the same color, let's make them
* exactly equal.
*/
pika_palette_set_entry_color (palette, 0, color);
pika_palette_set_entry_color (palette, 0, color, FALSE);
return;
}

View File

@ -78,7 +78,7 @@ static void pika_pdb_progress_progress_set_value (PikaProgress *progress
gdouble percentage);
static gdouble pika_pdb_progress_progress_get_value (PikaProgress *progress);
static void pika_pdb_progress_progress_pulse (PikaProgress *progress);
static guint32 pika_pdb_progress_progress_get_window_id (PikaProgress *progress);
static GBytes * pika_pdb_progress_progress_get_window_id (PikaProgress *progress);
static GObjectClass *parent_class = NULL;
@ -241,14 +241,13 @@ pika_pdb_progress_set_property (GObject *object,
}
}
static gdouble
pika_pdb_progress_run_callback (PikaPdbProgress *progress,
PikaProgressCommand command,
const gchar *text,
gdouble value)
static void
pika_pdb_progress_run_callback (PikaPdbProgress *progress,
PikaProgressCommand command,
const gchar *text,
gdouble value,
GBytes **handle)
{
gdouble retval = 0;
if (progress->callback_name && ! progress->callback_busy)
{
PikaValueArray *return_vals;
@ -274,17 +273,16 @@ pika_pdb_progress_run_callback (PikaPdbProgress *progress,
g_type_name (G_TYPE_FROM_INSTANCE (progress)));
}
else if (pika_value_array_length (return_vals) >= 2 &&
G_VALUE_HOLDS_DOUBLE (pika_value_array_index (return_vals, 1)))
handle != NULL &&
G_VALUE_HOLDS_BOXED (pika_value_array_index (return_vals, 1)))
{
retval = g_value_get_double (pika_value_array_index (return_vals, 1));
*handle = g_value_dup_boxed (pika_value_array_index (return_vals, 1));
}
pika_value_array_unref (return_vals);
progress->callback_busy = FALSE;
}
return retval;
}
static PikaProgress *
@ -298,7 +296,7 @@ pika_pdb_progress_progress_start (PikaProgress *progress,
{
pika_pdb_progress_run_callback (pdb_progress,
PIKA_PROGRESS_COMMAND_START,
message, 0.0);
message, 0.0, NULL);
pdb_progress->active = TRUE;
pdb_progress->value = 0.0;
@ -318,7 +316,7 @@ pika_pdb_progress_progress_end (PikaProgress *progress)
{
pika_pdb_progress_run_callback (pdb_progress,
PIKA_PROGRESS_COMMAND_END,
NULL, 0.0);
NULL, 0.0, NULL);
pdb_progress->active = FALSE;
pdb_progress->value = 0.0;
@ -342,7 +340,7 @@ pika_pdb_progress_progress_set_text (PikaProgress *progress,
if (pdb_progress->active)
pika_pdb_progress_run_callback (pdb_progress,
PIKA_PROGRESS_COMMAND_SET_TEXT,
message, 0.0);
message, 0.0, NULL);
}
static void
@ -355,7 +353,7 @@ pika_pdb_progress_progress_set_value (PikaProgress *progress,
{
pika_pdb_progress_run_callback (pdb_progress,
PIKA_PROGRESS_COMMAND_SET_VALUE,
NULL, percentage);
NULL, percentage, NULL);
pdb_progress->value = percentage;
}
}
@ -377,18 +375,19 @@ pika_pdb_progress_progress_pulse (PikaProgress *progress)
if (pdb_progress->active)
pika_pdb_progress_run_callback (pdb_progress,
PIKA_PROGRESS_COMMAND_PULSE,
NULL, 0.0);
NULL, 0.0, NULL);
}
static guint32
static GBytes *
pika_pdb_progress_progress_get_window_id (PikaProgress *progress)
{
PikaPdbProgress *pdb_progress = PIKA_PDB_PROGRESS (progress);
GBytes *handle = NULL;
return (guint32)
pika_pdb_progress_run_callback (pdb_progress,
PIKA_PROGRESS_COMMAND_GET_WINDOW,
NULL, 0.0);
pika_pdb_progress_run_callback (pdb_progress,
PIKA_PROGRESS_COMMAND_GET_WINDOW,
NULL, 0.0, &handle);
return handle;
}
PikaPdbProgress *

View File

@ -209,7 +209,7 @@ pika_progress_pulse (PikaProgress *progress)
progress_iface->pulse (progress);
}
guint32
GBytes *
pika_progress_get_window_id (PikaProgress *progress)
{
PikaProgressInterface *progress_iface;
@ -221,7 +221,7 @@ pika_progress_get_window_id (PikaProgress *progress)
if (progress_iface->get_window_id)
return progress_iface->get_window_id (progress);
return 0;
return NULL;
}
gboolean

View File

@ -48,7 +48,7 @@ struct _PikaProgressInterface
gdouble (* get_value) (PikaProgress *progress);
void (* pulse) (PikaProgress *progress);
guint32 (* get_window_id) (PikaProgress *progress);
GBytes * (* get_window_id) (PikaProgress *progress);
gboolean (* message) (PikaProgress *progress,
Pika *pika,
@ -78,7 +78,7 @@ void pika_progress_set_value (PikaProgress *progress,
gdouble pika_progress_get_value (PikaProgress *progress);
void pika_progress_pulse (PikaProgress *progress);
guint32 pika_progress_get_window_id (PikaProgress *progress);
GBytes * pika_progress_get_window_id (PikaProgress *progress);
gboolean pika_progress_message (PikaProgress *progress,
Pika *pika,

View File

@ -59,7 +59,7 @@ static void pika_sub_progress_set_value (PikaProgress *prog
gdouble percentage);
static gdouble pika_sub_progress_get_value (PikaProgress *progress);
static void pika_sub_progress_pulse (PikaProgress *progress);
static guint32 pika_sub_progress_get_window_id (PikaProgress *progress);
static GBytes * pika_sub_progress_get_window_id (PikaProgress *progress);
static gboolean pika_sub_progress_message (PikaProgress *progress,
Pika *pika,
PikaMessageSeverity severity,
@ -228,7 +228,7 @@ pika_sub_progress_pulse (PikaProgress *progress)
pika_progress_pulse (sub->progress);
}
static guint32
static GBytes *
pika_sub_progress_get_window_id (PikaProgress *progress)
{
PikaSubProgress *sub = PIKA_SUB_PROGRESS (progress);
@ -236,7 +236,7 @@ pika_sub_progress_get_window_id (PikaProgress *progress)
if (sub->progress)
return pika_progress_get_window_id (sub->progress);
return 0;
return NULL;
}
static gboolean

View File

@ -34,11 +34,12 @@
#include "config/pikacoreconfig.h"
#include "widgets/pikawidgets-utils.h"
#include "about.h"
#include "git-version.h"
#include "about-dialog.h"
#include "authors.h"
#include "pika-update.h"
#include "pika-version.h"
@ -47,20 +48,22 @@
typedef struct
{
GtkWidget *dialog;
GtkWidget *dialog;
Pika *pika;
GtkWidget *update_frame;
PikaCoreConfig *config;
GtkWidget *anim_area;
PangoLayout *layout;
GtkWidget *anim_area;
PangoLayout *layout;
guint timer;
guint timer;
gint index;
gint animstep;
gint state;
gboolean visible;
gint index;
gint animstep;
gint state;
gboolean visible;
} PikaAboutDialog;
@ -93,7 +96,8 @@ static void about_dialog_download_clicked
const gchar *link);
GtkWidget *
about_dialog_create (PikaCoreConfig *config)
about_dialog_create (Pika *pika,
PikaCoreConfig *config)
{
static PikaAboutDialog dialog;
@ -106,6 +110,7 @@ about_dialog_create (PikaCoreConfig *config)
gchar *copyright;
gchar *version;
dialog.pika = pika;
dialog.config = config;
pixbuf = about_dialog_load_logo ();
@ -134,9 +139,6 @@ about_dialog_create (PikaCoreConfig *config)
"logo", pixbuf,
"website", "https://heckin.technology/AlderconeStudio/PIKApp/",
"website-label", _("Visit the PIKA website"),
/* Translators: insert your names here,
separated by newline */
"translator-credits", _("translator-credits"),
NULL);
if (pixbuf)
@ -196,6 +198,10 @@ about_dialog_map (GtkWidget *widget,
dialog->timer = g_timeout_add (800, about_dialog_timer, dialog);
}
#ifdef G_OS_WIN32
pika_window_set_title_bar_theme (dialog->pika, widget, FALSE);
#endif
}
static void
@ -264,203 +270,6 @@ static void
about_dialog_add_update (PikaAboutDialog *dialog,
PikaCoreConfig *config)
{
GtkWidget *container;
GList *children;
GtkWidget *vbox;
GtkWidget *frame;
GtkWidget *box;
GtkWidget *box2;
GtkWidget *label;
GtkWidget *button;
GtkWidget *button_image;
GtkWidget *button_label;
GDateTime *datetime;
gchar *date;
gchar *text;
if (dialog->update_frame)
{
gtk_widget_destroy (dialog->update_frame);
dialog->update_frame = NULL;
}
/* Get the dialog vbox. */
container = gtk_dialog_get_content_area (GTK_DIALOG (dialog->dialog));
children = gtk_container_get_children (GTK_CONTAINER (container));
g_return_if_fail (GTK_IS_BOX (children->data));
vbox = children->data;
g_list_free (children);
/* The update frame. */
frame = gtk_frame_new (NULL);
gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 2);
box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
gtk_container_add (GTK_CONTAINER (frame), box);
/* Button in the frame. */
button = gtk_button_new ();
gtk_box_pack_start (GTK_BOX (box), button, FALSE, FALSE, 0);
gtk_widget_show (button);
box2 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_container_add (GTK_CONTAINER (button), box2);
gtk_widget_show (box2);
button_image = gtk_image_new_from_icon_name (NULL, GTK_ICON_SIZE_DIALOG);
gtk_box_pack_start (GTK_BOX (box2), button_image, FALSE, FALSE, 0);
gtk_widget_show (button_image);
button_label = gtk_label_new (NULL);
gtk_box_pack_start (GTK_BOX (box2), button_label, FALSE, FALSE, 0);
gtk_container_child_set (GTK_CONTAINER (box2), button_label, "expand", TRUE, NULL);
gtk_widget_show (button_label);
if (config->last_known_release != NULL)
{
/* There is a newer version. */
const gchar *download_url = NULL;
gchar *comment = NULL;
/* We want the frame to stand out. */
label = gtk_label_new (NULL);
text = g_strdup_printf ("<tt><b><big>%s</big></b></tt>",
_("Update available!"));
gtk_label_set_markup (GTK_LABEL (label), text);
g_free (text);
gtk_widget_show (label);
gtk_frame_set_label_widget (GTK_FRAME (frame), label);
gtk_frame_set_label_align (GTK_FRAME (frame), 0.5, 0.5);
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_OUT);
gtk_box_reorder_child (GTK_BOX (vbox), frame, 3);
/* Button is an update link. */
gtk_image_set_from_icon_name (GTK_IMAGE (button_image),
"software-update-available",
GTK_ICON_SIZE_DIALOG);
#ifdef PIKA_UNSTABLE
download_url = "https://heckin.technology/AlderconeStudio/PIKApp/downloads/devel/";
#else
download_url = "https://heckin.technology/AlderconeStudio/PIKApp/downloads/";
#endif
g_signal_connect (button, "clicked",
(GCallback) about_dialog_download_clicked,
(gpointer) download_url);
/* The preferred localized date representation without the time. */
datetime = g_date_time_new_from_unix_local (config->last_release_timestamp);
date = g_date_time_format (datetime, "%x");
g_date_time_unref (datetime);
if (config->last_revision > 0)
{
/* This is actually a new revision of current version. */
text = g_strdup_printf (_("Download PIKA %s revision %d (released on %s)\n"),
config->last_known_release,
config->last_revision,
date);
/* Finally an optional release comment. */
if (config->last_release_comment)
{
/* Translators: <> tags are Pango markup. Please keep these
* markups in your translation. */
comment = g_strdup_printf (_("<u>Release comment</u>: <i>%s</i>"), config->last_release_comment);
}
}
else
{
text = g_strdup_printf (_("Download PIKA %s (released on %s)\n"),
config->last_known_release, date);
}
gtk_label_set_text (GTK_LABEL (button_label), text);
g_free (text);
g_free (date);
if (comment)
{
label = gtk_label_new (NULL);
gtk_label_set_max_width_chars (GTK_LABEL (label), 80);
gtk_label_set_markup (GTK_LABEL (label), comment);
gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
g_free (comment);
gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0);
gtk_widget_show (label);
}
}
else
{
/* Button is a "Check for updates" action. */
gtk_image_set_from_icon_name (GTK_IMAGE (button_image),
"view-refresh",
GTK_ICON_SIZE_MENU);
gtk_label_set_text (GTK_LABEL (button_label), _("Check for updates"));
g_signal_connect_swapped (button, "clicked",
(GCallback) pika_update_check, config);
}
gtk_box_reorder_child (GTK_BOX (vbox), frame, 4);
/* Last check date box. */
box2 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
if (config->last_known_release != NULL)
gtk_widget_set_margin_top (box2, 20);
gtk_container_add (GTK_CONTAINER (box), box2);
gtk_widget_show (box2);
/* Show a small "Check for updates" button only if the big one has
* been replaced by a download button.
*/
if (config->last_known_release != NULL)
{
button = gtk_button_new_from_icon_name ("view-refresh", GTK_ICON_SIZE_MENU);
gtk_widget_set_tooltip_text (button, _("Check for updates"));
gtk_box_pack_start (GTK_BOX (box2), button, FALSE, FALSE, 0);
g_signal_connect_swapped (button, "clicked",
(GCallback) pika_update_check, config);
gtk_widget_show (button);
}
if (config->check_update_timestamp > 0)
{
gchar *subtext;
gchar *time;
datetime = g_date_time_new_from_unix_local (config->check_update_timestamp);
date = g_date_time_format (datetime, "%x");
time = g_date_time_format (datetime, "%X");
/* Translators: first string is the date in the locale's date
* representation (e.g., 12/31/99), second is the time in the
* locale's time representation (e.g., 23:13:48).
*/
subtext = g_strdup_printf (_("Last checked on %s at %s"), date, time);
g_date_time_unref (datetime);
g_free (date);
g_free (time);
text = g_strdup_printf ("<i>%s</i>", subtext);
label = gtk_label_new (NULL);
gtk_label_set_markup (GTK_LABEL (label), text);
gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_CENTER);
gtk_box_pack_start (GTK_BOX (box2), label, FALSE, FALSE, 0);
gtk_container_child_set (GTK_CONTAINER (box2), label, "expand", TRUE, NULL);
gtk_widget_show (label);
g_free (text);
g_free (subtext);
}
gtk_widget_show (box);
gtk_widget_show (frame);
g_set_weak_pointer (&dialog->update_frame, frame);
/* Reconstruct the dialog when release info changes. */
g_signal_connect (config, "notify::last-known-release",
(GCallback) about_dialog_last_release_changed,
dialog);
}
static void
@ -625,82 +434,6 @@ decorate_text (PikaAboutDialog *dialog,
static gboolean
about_dialog_timer (gpointer data)
{
PikaAboutDialog *dialog = data;
gint timeout = 0;
if (dialog->animstep == 0)
{
gchar *text = NULL;
dialog->visible = TRUE;
switch (dialog->state)
{
case 0:
dialog->timer = g_timeout_add (30, about_dialog_timer, dialog);
dialog->state += 1;
return FALSE;
case 1:
text = insert_spacers (_("PIKA is brought to you by Aldercone Studio"));
break;
case 2:
return FALSE;
default:
g_return_val_if_reached (TRUE);
break;
}
g_return_val_if_fail (text != NULL, TRUE);
pango_layout_set_text (dialog->layout, text, -1);
pango_layout_set_attributes (dialog->layout, NULL);
g_free (text);
}
if (dialog->animstep < 16)
{
decorate_text (dialog, 2, ((gfloat) dialog->animstep) / 15.0);
}
else if (dialog->animstep == 16)
{
timeout = 800;
}
else if (dialog->animstep == 17)
{
timeout = 30;
}
else if (dialog->animstep < 33)
{
decorate_text (dialog, 1,
1.0 - ((gfloat) (dialog->animstep - 17)) / 15.0);
}
else if (dialog->animstep == 33)
{
dialog->visible = FALSE;
timeout = 300;
}
else
{
dialog->visible = FALSE;
dialog->animstep = -1;
timeout = 30;
}
dialog->animstep++;
gtk_widget_queue_draw (dialog->anim_area);
if (timeout > 0)
{
dialog->timer = g_timeout_add (timeout, about_dialog_timer, dialog);
return FALSE;
}
/* else keep the current timeout */
return TRUE;
}

View File

@ -23,7 +23,8 @@
#define __ABOUT_DIALOG_H__
GtkWidget * about_dialog_create (PikaCoreConfig *config);
GtkWidget * about_dialog_create (Pika *pika,
PikaCoreConfig *config);
#endif /* __ABOUT_DIALOG_H__ */

View File

@ -221,7 +221,7 @@ dialogs_about_get (PikaDialogFactory *factory,
PikaUIManager *ui_manager,
gint view_size)
{
return about_dialog_create (context->pika->edit_config);
return about_dialog_create (context->pika, context->pika->edit_config);
}
GtkWidget *

View File

@ -1856,6 +1856,9 @@ prefs_dialog_new (Pika *pika,
prefs_check_button_add_with_icon (object, "global-gradient",
_("_Gradient"), PIKA_ICON_GRADIENT,
GTK_BOX (vbox2), size_group);
prefs_check_button_add_with_icon (object, "global-expand",
_("E_xpand Layers"), PIKA_ICON_TOOL_SCALE,
GTK_BOX (vbox2), size_group);
/* Move Tool */
vbox2 = prefs_frame_new (_("Move Tool"),

View File

@ -151,6 +151,13 @@ quit_close_all_dialog_new (Pika *pika,
GClosure *closure;
gint rows;
gint view_size;
GdkRectangle geometry;
GdkMonitor *monitor;
gint max_rows;
gint scale_factor;
const gfloat rows_per_height = 32 / 1440.0f;
const gint greatest_max_rows = 36;
const gint least_max_rows = 6;
g_return_val_if_fail (PIKA_IS_PIKA (pika), NULL);
@ -206,8 +213,28 @@ quit_close_all_dialog_new (Pika *pika,
private->box = PIKA_MESSAGE_DIALOG (private->dialog)->box;
monitor = pika_widget_get_monitor (private->dialog);
scale_factor = gdk_monitor_get_scale_factor (monitor);
gdk_monitor_get_geometry (monitor, &geometry);
if (scale_factor > 1)
{
#ifdef GDK_WINDOWING_WIN32
max_rows = (geometry.height * scale_factor * rows_per_height)
/ (scale_factor + 1);
#else
max_rows = (geometry.height * rows_per_height) / (scale_factor + 1);
#endif
}
else
{
max_rows = geometry.height * rows_per_height;
}
max_rows = CLAMP (max_rows, least_max_rows, greatest_max_rows);
view_size = pika->config->layer_preview_size;
rows = CLAMP (pika_container_get_n_children (private->images), 3, 6);
rows = CLAMP (pika_container_get_n_children (private->images), 3, max_rows);
view = pika_container_tree_view_new (private->images, private->context,
view_size, 1);

View File

@ -191,7 +191,7 @@ tips_dialog_create (Pika *pika)
gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
gtk_widget_show (hbox);
more_button = gtk_link_button_new_with_label ("https://docs.pika.org/",
more_button = gtk_link_button_new_with_label ("https://heckin.technology/AlderconeStudio/PIKApp/wiki/",
/* a link to the related section in the user manual */
_("Learn more"));
gtk_widget_show (more_button);
@ -251,7 +251,7 @@ tips_dialog_set_tip (PikaTip *tip)
/* set the URI to unset the "visited" state */
gtk_link_button_set_uri (GTK_LINK_BUTTON (more_button),
"https://docs.pika.org/");
"https://heckin.technology/AlderconeStudio/PIKApp/wiki/");
gtk_widget_set_sensitive (more_button, tip->help_id != NULL);
}

View File

@ -191,12 +191,12 @@ welcome_dialog_create (Pika *pika)
/* "graduation cap" emoticone in UTF-8. */
"\xf0\x9f\x8e\x93",
_("Tutorials"),
"https://heckin.technology/AlderconeStudio/PIKApp/tutorials/");
"https://heckin.technology/AlderconeStudio/PIKApp/");
welcome_add_link (GTK_GRID (grid), 0, &row,
/* "open book" emoticone in UTF-8. */
"\xf0\x9f\x93\x96",
_("Documentation"),
"https://docs.pika.org/");
"https://heckin.technology/AlderconeStudio/PIKApp/wiki/");
/* XXX: should we add API docs for plug-in developers once it's
* properly set up? */
@ -229,12 +229,12 @@ welcome_dialog_create (Pika *pika)
/* "keyboard" emoticone in UTF-8. */
"\xe2\x8c\xa8",
_("Contributing"),
"https://heckin.technology/AlderconeStudio/PIKApp/develop/");
"https://heckin.technology/AlderconeStudio/PIKApp/");
welcome_add_link (GTK_GRID (grid), 1, &row,
/* "love letter" emoticone in UTF-8. */
"\xf0\x9f\x92\x8c",
_("Donating"),
"https://heckin.technology/AlderconeStudio/PIKApp/donating/");
"https://mastodon.art/@aldercone/");
/*****************/
/* Release Notes */

View File

@ -46,6 +46,11 @@ static void pika_display_bounds_changed_handler (PikaImage *image,
gint old_x,
gint old_y,
PikaDisplay *display);
static void pika_display_flush_handler (PikaImage *image,
gboolean invalidate_preview,
PikaDisplay *display);
static gboolean
pika_display_flush_handler_idle (gpointer user_data);
/* public functions */
@ -122,3 +127,23 @@ pika_display_bounds_changed_handler (PikaImage *image,
{
pika_display_update_bounding_box (display);
}
static void
pika_display_flush_handler (PikaImage *image,
gboolean invalidate_preview,
PikaDisplay *display)
{
g_idle_add_full (G_PRIORITY_LOW,
(GSourceFunc) pika_display_flush_handler_idle,
g_object_ref (display), g_object_unref);
}
static gboolean
pika_display_flush_handler_idle (gpointer user_data)
{
PikaDisplay *display = user_data;
pika_display_flush (display);
return G_SOURCE_REMOVE;
}

View File

@ -110,7 +110,7 @@ static void pika_display_progress_set_value (PikaProgress *progre
gdouble percentage);
static gdouble pika_display_progress_get_value (PikaProgress *progress);
static void pika_display_progress_pulse (PikaProgress *progress);
static guint32 pika_display_progress_get_window_id (PikaProgress *progress);
static GBytes * pika_display_progress_get_window_id (PikaProgress *progress);
static gboolean pika_display_progress_message (PikaProgress *progress,
Pika *pika,
PikaMessageSeverity severity,
@ -323,7 +323,7 @@ pika_display_progress_pulse (PikaProgress *progress)
pika_progress_pulse (PIKA_PROGRESS (display->priv->shell));
}
static guint32
static GBytes *
pika_display_progress_get_window_id (PikaProgress *progress)
{
PikaDisplayImpl *display = PIKA_DISPLAY_IMPL (progress);
@ -331,7 +331,7 @@ pika_display_progress_get_window_id (PikaProgress *progress)
if (display->priv->shell)
return pika_progress_get_window_id (PIKA_PROGRESS (display->priv->shell));
return 0;
return NULL;
}
static gboolean

View File

@ -103,15 +103,16 @@ pika_display_shell_progress_pulse (PikaProgress *progress)
pika_progress_pulse (PIKA_PROGRESS (statusbar));
}
static guint32
static GBytes *
pika_display_shell_progress_get_window_id (PikaProgress *progress)
{
GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (progress));
PikaDisplayShell *shell = PIKA_DISPLAY_SHELL (progress);
GBytes *handle = NULL;
if (GTK_IS_WINDOW (toplevel))
return pika_window_get_native_id (GTK_WINDOW (toplevel));
if (shell->window_handle)
handle = g_bytes_ref (shell->window_handle);
return 0;
return handle;
}
static gboolean

View File

@ -27,6 +27,10 @@
#include <gegl.h>
#include <gtk/gtk.h>
#ifdef GDK_WINDOWING_WAYLAND
#include <gdk/gdkwayland.h>
#endif
#include "libpikabase/pikabase.h"
#include "libpikamath/pikamath.h"
#include "libpikacolor/pikacolor.h"
@ -587,6 +591,8 @@ pika_display_shell_constructed (GObject *object)
G_CALLBACK (pika_display_shell_canvas_grab_notify),
shell);
pika_widget_set_native_handle (GTK_WIDGET (shell), &shell->window_handle);
g_signal_connect (shell->canvas, "realize",
G_CALLBACK (pika_display_shell_canvas_realize),
shell);
@ -851,6 +857,17 @@ pika_display_shell_dispose (GObject *object)
shell->display = NULL;
if (shell->window_handle != NULL)
{
#ifdef GDK_WINDOWING_WAYLAND
if (GDK_IS_WAYLAND_DISPLAY (gdk_display_get_default ()) &&
/* The GdkWindow is likely already destroyed. */
gtk_widget_get_window (GTK_WIDGET (shell)) != NULL)
gdk_wayland_window_unexport_handle (gtk_widget_get_window (GTK_WIDGET (shell)));
#endif
g_clear_pointer (&shell->window_handle, g_bytes_unref);
}
G_OBJECT_CLASS (parent_class)->dispose (object);
}

View File

@ -55,6 +55,8 @@ struct _PikaDisplayShell
PikaDisplay *display;
GBytes *window_handle;
PikaUIManager *popup_manager;
GdkMonitor *initial_monitor;

View File

@ -1174,9 +1174,6 @@ pika_image_window_set_aux_info (PikaSessionManaged *session_managed,
else if (StartupInfo.wShowWindow == SW_SHOWMINIMIZED ||
StartupInfo.wShowWindow == SW_SHOWMINNOACTIVE ||
StartupInfo.wShowWindow == SW_MINIMIZE)
/* XXX Iconification does not seem to work. I see the
* window being iconified and immediately re-raised.
* I leave this piece of code for later improvement. */
gtk_window_iconify (GTK_WINDOW (session_managed));
else
/* Another show property not relevant to min/max.

View File

@ -2026,8 +2026,10 @@ static void
pika_tool_rectangle_update_options (PikaToolRectangle *rectangle)
{
PikaToolRectanglePrivate *private = rectangle->private;
gdouble x1, y1;
gdouble x2, y2;
gdouble x1 = 0;
gdouble y1 = 0;
gdouble x2 = 0;
gdouble y2 = 0;
pika_tool_rectangle_get_public_rect (rectangle, &x1, &y1, &x2, &y2);
@ -2162,7 +2164,10 @@ static void
pika_tool_rectangle_update_status (PikaToolRectangle *rectangle)
{
PikaToolRectanglePrivate *private = rectangle->private;
gdouble x1, y1, x2, y2;
gdouble x1 = 0;
gdouble y1 = 0;
gdouble x2 = 0;
gdouble y2 = 0;
pika_tool_rectangle_get_public_rect (rectangle, &x1, &y1, &x2, &y2);
@ -2436,7 +2441,10 @@ pika_tool_rectangle_coord_on_handle (PikaToolRectangle *rectangle,
{
PikaToolRectanglePrivate *private = rectangle->private;
PikaDisplayShell *shell;
gdouble x1, y1, x2, y2;
gdouble x1 = 0;
gdouble y1 = 0;
gdouble x2 = 0;
gdouble y2 = 0;
gdouble rect_w, rect_h;
gdouble handle_x = 0;
gdouble handle_y = 0;
@ -2838,7 +2846,10 @@ pika_tool_rectangle_setup_snap_offsets (PikaToolRectangle *rectangle,
{
PikaToolWidget *widget = PIKA_TOOL_WIDGET (rectangle);
PikaToolRectanglePrivate *private = rectangle->private;
gdouble x1, y1, x2, y2;
gdouble x1 = 0;
gdouble y1 = 0;
gdouble x2 = 0;
gdouble y2 = 0;
gdouble coord_x, coord_y;
pika_tool_rectangle_get_public_rect (rectangle, &x1, &y1, &x2, &y2);
@ -4185,7 +4196,10 @@ pika_tool_rectangle_point_in_rectangle (PikaToolRectangle *rectangle,
gdouble x,
gdouble y)
{
gdouble x1, y1, x2, y2;
gdouble x1 = 0;
gdouble y1 = 0;
gdouble x2 = 0;
gdouble y2 = 0;
g_return_val_if_fail (PIKA_IS_TOOL_RECTANGLE (rectangle), FALSE);

View File

@ -35,11 +35,15 @@
#include "core/pikabrush.h"
#include "core/pikabrush-load.h"
#include "core/pikabrush-private.h"
#include "core/pikacontainer.h"
#include "core/pikadrawable.h"
#include "core/pikaimage.h"
#include "core/pikalayer-new.h"
#include "core/pikaimage-merge.h"
#include "core/pikaimage-new.h"
#include "core/pikaimage-resize.h"
#include "core/pikalayer-new.h"
#include "core/pikaparamspecs.h"
#include "core/pikapickable.h"
#include "core/pikatempbuf.h"
#include "pdb/pikaprocedure.h"
@ -51,12 +55,14 @@
/* local function prototypes */
static PikaImage * file_gbr_brush_to_image (Pika *pika,
PikaBrush *brush);
static PikaBrush * file_gbr_image_to_brush (PikaImage *image,
PikaDrawable *drawable,
const gchar *name,
gdouble spacing);
static PikaImage * file_gbr_brush_to_image (Pika *pika,
PikaBrush *brush);
static PikaBrush * file_gbr_image_to_brush (PikaImage *image,
PikaContext *context,
gint n_drawables,
PikaDrawable **drawables,
const gchar *name,
gdouble spacing);
/* public functions */
@ -119,24 +125,26 @@ file_gbr_save_invoker (PikaProcedure *procedure,
const PikaValueArray *args,
GError **error)
{
PikaValueArray *return_vals;
PikaImage *image;
PikaDrawable *drawable;
PikaBrush *brush;
const gchar *name;
GFile *file;
gint spacing;
gboolean success;
PikaValueArray *return_vals;
PikaImage *image;
PikaDrawable **drawables;
gint n_drawables;
PikaBrush *brush;
const gchar *name;
GFile *file;
gint spacing;
gboolean success;
pika_set_busy (pika);
image = g_value_get_object (pika_value_array_index (args, 1));
drawable = g_value_get_object (pika_value_array_index (args, 2));
file = g_value_get_object (pika_value_array_index (args, 3));
spacing = g_value_get_int (pika_value_array_index (args, 4));
name = g_value_get_string (pika_value_array_index (args, 5));
image = g_value_get_object (pika_value_array_index (args, 1));
n_drawables = g_value_get_int (pika_value_array_index (args, 2));
drawables = (PikaDrawable **) pika_value_get_object_array (pika_value_array_index (args, 3));
file = g_value_get_object (pika_value_array_index (args, 4));
spacing = g_value_get_int (pika_value_array_index (args, 5));
name = g_value_get_string (pika_value_array_index (args, 6));
brush = file_gbr_image_to_brush (image, drawable, name, spacing);
brush = file_gbr_image_to_brush (image, context, n_drawables, drawables, name, spacing);
pika_data_set_file (PIKA_DATA (brush), file, TRUE, TRUE);
@ -417,15 +425,49 @@ file_gbr_brush_to_image (Pika *pika,
}
static PikaBrush *
file_gbr_image_to_brush (PikaImage *image,
PikaDrawable *drawable,
const gchar *name,
gdouble spacing)
file_gbr_image_to_brush (PikaImage *image,
PikaContext *context,
gint n_drawables,
PikaDrawable **drawables,
const gchar *name,
gdouble spacing)
{
gint width = pika_item_get_width (PIKA_ITEM (drawable));
gint height = pika_item_get_height (PIKA_ITEM (drawable));
PikaBrush *brush;
PikaImage *subimage = NULL;
PikaDrawable *drawable;
gint width;
gint height;
return file_gbr_drawable_to_brush (drawable,
GEGL_RECTANGLE (0, 0, width, height),
name, spacing);
g_return_val_if_fail (n_drawables > 0, NULL);
g_return_val_if_fail (drawables != NULL, NULL);
if (n_drawables > 1)
{
GList *drawable_list = NULL;
for (gint i = 0; i < n_drawables; i++)
drawable_list = g_list_prepend (drawable_list, drawables[i]);
subimage = pika_image_new_from_drawables (image->pika, drawable_list, FALSE, FALSE);
g_list_free (drawable_list);
pika_container_remove (image->pika->images, PIKA_OBJECT (subimage));
pika_image_resize_to_layers (subimage, context,
NULL, NULL, NULL, NULL, NULL);
drawable = PIKA_DRAWABLE (pika_image_merge_visible_layers (subimage, context, PIKA_CLIP_TO_IMAGE,
FALSE, TRUE, NULL));
pika_pickable_flush (PIKA_PICKABLE (subimage));
}
else
{
drawable = drawables[0];
}
width = pika_item_get_width (PIKA_ITEM (drawable));
height = pika_item_get_height (PIKA_ITEM (drawable));
brush = file_gbr_drawable_to_brush (drawable,
GEGL_RECTANGLE (0, 0, width, height),
name, spacing);
g_clear_object (&subimage);
return brush;
}

View File

@ -260,6 +260,7 @@ file_pat_image_to_pattern (PikaImage *image,
gint height;
g_return_val_if_fail (n_drawables > 0, NULL);
g_return_val_if_fail (drawables != NULL, NULL);
if (n_drawables > 1)
{

View File

@ -87,7 +87,7 @@ file_data_init (Pika *pika)
"1995-2019");
pika_procedure_add_argument (procedure,
pika_param_spec_enum ("dummy-param",
pika_param_spec_enum ("run-mode",
"Dummy Param",
"Dummy parameter",
PIKA_TYPE_RUN_MODE,
@ -145,7 +145,7 @@ file_data_init (Pika *pika)
"1995-2019");
pika_procedure_add_argument (procedure,
pika_param_spec_enum ("dummy-param",
pika_param_spec_enum ("run-mode",
"Dummy Param",
"Dummy parameter",
PIKA_TYPE_RUN_MODE,
@ -158,12 +158,17 @@ file_data_init (Pika *pika)
FALSE,
PIKA_PARAM_READWRITE));
pika_procedure_add_argument (procedure,
pika_param_spec_drawable ("drawable",
"Drawable",
"Active drawable "
"of input image",
FALSE,
PIKA_PARAM_READWRITE));
g_param_spec_int ("num-drawables",
"Num drawables",
"Number of drawables",
1, G_MAXINT, 1,
PIKA_PARAM_READWRITE));
pika_procedure_add_argument (procedure,
pika_param_spec_object_array ("drawables",
"Drawables",
"Selected drawables",
PIKA_TYPE_DRAWABLE,
PIKA_PARAM_READWRITE | PIKA_PARAM_NO_VALIDATE));
pika_procedure_add_argument (procedure,
g_param_spec_object ("file",
"File",
@ -219,7 +224,7 @@ file_data_init (Pika *pika)
"1999-2019");
pika_procedure_add_argument (procedure,
pika_param_spec_enum ("dummy-param",
pika_param_spec_enum ("run-mode",
"Dummy Param",
"Dummy parameter",
PIKA_TYPE_RUN_MODE,
@ -277,7 +282,7 @@ file_data_init (Pika *pika)
"1999-2019");
pika_procedure_add_argument (procedure,
pika_param_spec_enum ("dummy-param",
pika_param_spec_enum ("run-mode",
"Dummy Param",
"Dummy parameter",
PIKA_TYPE_RUN_MODE,
@ -363,7 +368,7 @@ file_data_init (Pika *pika)
"1997-2019");
pika_procedure_add_argument (procedure,
pika_param_spec_enum ("dummy-param",
pika_param_spec_enum ("run-mode",
"Dummy Param",
"Dummy parameter",
PIKA_TYPE_RUN_MODE,
@ -420,7 +425,7 @@ file_data_init (Pika *pika)
"1995-2019");
pika_procedure_add_argument (procedure,
pika_param_spec_enum ("dummy-param",
pika_param_spec_enum ("run-mode",
"Dummy Param",
"Dummy parameter",
PIKA_TYPE_RUN_MODE,
@ -433,7 +438,7 @@ file_data_init (Pika *pika)
FALSE,
PIKA_PARAM_READWRITE));
pika_procedure_add_argument (procedure,
g_param_spec_int ("n-drawables",
g_param_spec_int ("num-drawables",
"Num drawables",
"Number of drawables",
1, G_MAXINT, 1,
@ -491,7 +496,7 @@ file_data_init (Pika *pika)
"Jehan", "Jehan", "2019");
pika_procedure_add_argument (procedure,
pika_param_spec_enum ("dummy-param",
pika_param_spec_enum ("run-mode",
"Dummy Param",
"Dummy parameter",
PIKA_TYPE_RUN_MODE,

View File

@ -374,7 +374,7 @@ file_open_thumbnail (Pika *pika,
{
image = g_value_get_object (pika_value_array_index (return_vals, 1));
if (pika_value_array_length (return_vals) >= 3 &&
if (pika_value_array_length (return_vals) >= 4 &&
G_VALUE_HOLDS_INT (pika_value_array_index (return_vals, 2)) &&
G_VALUE_HOLDS_INT (pika_value_array_index (return_vals, 3)))
{
@ -385,11 +385,11 @@ file_open_thumbnail (Pika *pika,
MAX (0, g_value_get_int (pika_value_array_index (return_vals, 3)));
if (pika_value_array_length (return_vals) >= 5 &&
G_VALUE_HOLDS_INT (pika_value_array_index (return_vals, 4)))
G_VALUE_HOLDS_ENUM (pika_value_array_index (return_vals, 4)))
{
gint value = g_value_get_int (pika_value_array_index (return_vals, 4));
PikaImageType itype = g_value_get_enum (pika_value_array_index (return_vals, 4));
switch (value)
switch (itype)
{
case PIKA_RGB_IMAGE:
*format = pika_babl_format (PIKA_RGB,
@ -424,7 +424,7 @@ file_open_thumbnail (Pika *pika,
babl_new_palette ("-pika-indexed-format-dummy",
&rgb, &rgba);
if (value == PIKA_INDEXED_IMAGE)
if (itype == PIKA_INDEXED_IMAGE)
*format = rgb;
else
*format = rgba;

View File

@ -918,6 +918,98 @@ pika_gegl_index_to_mask (GeglBuffer *indexed_buffer,
});
}
gboolean
pika_gegl_is_index_used (GeglBuffer *indexed_buffer,
const GeglRectangle *indexed_rect,
const Babl *indexed_format,
gint index)
{
GeglBufferIterator *iter;
gboolean found = FALSE;
if (! indexed_rect)
indexed_rect = gegl_buffer_get_extent (indexed_buffer);
iter = gegl_buffer_iterator_new (indexed_buffer, indexed_rect, 0,
indexed_format,
GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 1);
/* I initially had an implementation using gegl_parallel_distribute_area()
* which turned out to be much slower than the simpler iteration on the whole
* buffer at once. I think the cost of threading and using GRWLock is just far
* too high for such very basic value check.
* See gegl_parallel_distribute_area() implementation in commit dbaa8b6a1c.
*/
while (gegl_buffer_iterator_next (iter))
{
const guchar *indexed = (const guchar *) iter->items[0].data;
gint count = iter->length;
while (count--)
{
if (*indexed == index)
{
/*
* Position of one item using this color index:
gint x = iter->items[0].roi.x + (iter->length - count - 1) % iter->items[0].roi.width;
gint y = iter->items[0].roi.y + (gint) ((iter->length - count - 1) / iter->items[0].roi.width);
*/
found = TRUE;
break;
}
indexed++;
}
if (found)
{
gegl_buffer_iterator_stop (iter);
break;
}
}
return found;
}
void
pika_gegl_shift_index (GeglBuffer *indexed_buffer,
const GeglRectangle *indexed_rect,
const Babl *indexed_format,
gint from_index,
gint shift)
{
gboolean indexed_format_has_alpha;
if (! indexed_rect)
indexed_rect = gegl_buffer_get_extent (indexed_buffer);
indexed_format_has_alpha = babl_format_has_alpha (indexed_format);
gegl_parallel_distribute_area (
indexed_rect, PIXELS_PER_THREAD,
[=] (const GeglRectangle *indexed_area)
{
GeglBufferIterator *iter;
iter = gegl_buffer_iterator_new (indexed_buffer, indexed_area, 0,
indexed_format,
GEGL_ACCESS_READWRITE, GEGL_ABYSS_NONE, 1);
while (gegl_buffer_iterator_next (iter))
{
guchar *indexed = (guchar *) iter->items[0].data;
gint count = iter->length;
while (count--)
{
if (*indexed >= from_index)
*indexed += shift;
indexed += (indexed_format_has_alpha ? 2 : 1);
}
}
});
}
static void
pika_gegl_convert_color_profile_progress (PikaProgress *progress,
gdouble value)

View File

@ -91,6 +91,15 @@ void pika_gegl_index_to_mask (GeglBuffer *indexed_buffer
GeglBuffer *mask_buffer,
const GeglRectangle *mask_rect,
gint index);
gboolean pika_gegl_is_index_used (GeglBuffer *indexed_buffer,
const GeglRectangle *indexed_rect,
const Babl *indexed_format,
gint index);
void pika_gegl_shift_index (GeglBuffer *indexed_buffer,
const GeglRectangle *indexed_rect,
const Babl *indexed_format,
gint from_index,
gint shift);
void pika_gegl_convert_color_profile (GeglBuffer *src_buffer,
const GeglRectangle *src_rect,

View File

@ -26,13 +26,19 @@
#include <string.h>
#include <cairo.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <gegl.h>
#include <gegl-plugin.h>
#include "libpikacolor/pikacolor.h"
#include "pika-gegl-types.h"
#include "core/pikapattern.h"
#include "core/pikaprogress.h"
#include "pika-babl.h"
#include "pika-gegl-loops.h"
#include "pika-gegl-utils.h"
@ -337,6 +343,100 @@ pika_gegl_buffer_dup (GeglBuffer *buffer)
return new_buffer;
}
GeglBuffer *
pika_gegl_buffer_resize (GeglBuffer *buffer,
gint new_width,
gint new_height,
gint offset_x,
gint offset_y,
PikaRGB *color,
PikaPattern *pattern,
gint pattern_offset_x,
gint pattern_offset_y)
{
GeglBuffer *new_buffer;
gboolean intersect;
GeglRectangle copy_rect;
const GeglRectangle *extent;
const Babl *format;
g_return_val_if_fail (GEGL_IS_BUFFER (buffer), NULL);
extent = gegl_buffer_get_extent (buffer);
format = gegl_buffer_get_format (buffer);
new_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, new_width, new_height),
format);
intersect = gegl_rectangle_intersect (&copy_rect,
GEGL_RECTANGLE (0, 0, extent->width,
extent->height),
GEGL_RECTANGLE (offset_x, offset_y,
new_width, new_height));
if (! intersect ||
copy_rect.width != new_width ||
copy_rect.height != new_height)
{
/* Clear the new buffer if needed and color/pattern is given */
if (pattern)
{
GeglBuffer *src_buffer;
GeglBuffer *dest_buffer;
PikaColorProfile *src_profile;
PikaColorProfile *dest_profile;
src_buffer = pika_pattern_create_buffer (pattern);
src_profile = pika_babl_format_get_color_profile (
gegl_buffer_get_format (src_buffer));
dest_profile = pika_babl_format_get_color_profile (
gegl_buffer_get_format (new_buffer));
if (pika_color_transform_can_gegl_copy (src_profile, dest_profile))
{
dest_buffer = g_object_ref (src_buffer);
}
else
{
dest_buffer = gegl_buffer_new (gegl_buffer_get_extent (src_buffer),
gegl_buffer_get_format (new_buffer));
pika_gegl_convert_color_profile (src_buffer, NULL, src_profile,
dest_buffer, NULL, dest_profile,
PIKA_COLOR_RENDERING_INTENT_PERCEPTUAL,
TRUE, NULL);
}
g_object_unref (src_profile);
g_object_unref (dest_profile);
gegl_buffer_set_pattern (new_buffer, NULL, dest_buffer,
pattern_offset_x, pattern_offset_y);
g_object_unref (src_buffer);
g_object_unref (dest_buffer);
}
else if (color)
{
GeglColor *gegl_color;
gegl_color = pika_gegl_color_new (color, gegl_buffer_get_format (buffer));
gegl_buffer_set_color (new_buffer, NULL, gegl_color);
g_object_unref (gegl_color);
}
}
if (intersect && copy_rect.width && copy_rect.height)
{
/* Copy the pixels in the intersection */
pika_gegl_buffer_copy (buffer, &copy_rect, GEGL_ABYSS_NONE, new_buffer,
GEGL_RECTANGLE (copy_rect.x - offset_x,
copy_rect.y - offset_y, 0, 0));
}
return new_buffer;
}
gboolean
pika_gegl_buffer_set_extent (GeglBuffer *buffer,
const GeglRectangle *extent)

View File

@ -59,6 +59,15 @@ gboolean pika_gegl_param_spec_has_key (GParamSpec *pspe
const gchar *value);
GeglBuffer * pika_gegl_buffer_dup (GeglBuffer *buffer);
GeglBuffer * pika_gegl_buffer_resize (GeglBuffer *buffer,
gint new_width,
gint new_height,
gint offset_x,
gint offset_y,
PikaRGB *color,
PikaPattern *pattern,
gint pattern_offset_x,
gint pattern_offset_y);
gboolean pika_gegl_buffer_set_extent (GeglBuffer *buffer,
const GeglRectangle *extent);

View File

@ -287,10 +287,7 @@ progress_error_dialog (PikaProgress *progress)
}
else
{
guint32 window_id = pika_progress_get_window_id (progress);
if (window_id)
pika_window_set_transient_for (GTK_WINDOW (dialog), window_id);
pika_window_set_transient_for (GTK_WINDOW (dialog), progress);
}
}

View File

@ -62,6 +62,8 @@
#include "core/pikacancelable.h"
#include "core/pikacontainer.h"
#include "core/pikacontext.h"
#include "core/pikadatafactory.h"
#include "core/pikadrawable.h"
#include "core/pikagradient.h"
#include "core/pikaimage.h"
#include "core/pikaimagefile.h"
@ -89,6 +91,7 @@
#include "widgets/pikamenufactory.h"
#include "widgets/pikapaletteselect.h"
#include "widgets/pikapatternselect.h"
#include "widgets/pikapickableselect.h"
#include "widgets/pikaprogressdialog.h"
#include "widgets/pikauimanager.h"
#include "widgets/pikawidgets-utils.h"
@ -136,7 +139,7 @@ static GFile * gui_get_theme_dir (Pika *pika);
static GFile * gui_get_icon_theme_dir (Pika *pika);
static PikaObject * gui_get_window_strategy (Pika *pika);
static PikaDisplay * gui_get_empty_display (Pika *pika);
static guint32 gui_display_get_window_id (PikaDisplay *display);
static GBytes * gui_display_get_window_id (PikaDisplay *display);
static PikaDisplay * gui_display_create (Pika *pika,
PikaImage *image,
PikaUnit unit,
@ -156,18 +159,19 @@ static void gui_free_progress (Pika *pika,
static gboolean gui_pdb_dialog_new (Pika *pika,
PikaContext *context,
PikaProgress *progress,
PikaContainer *container,
GType object_type,
GBytes *parent_handle,
const gchar *title,
const gchar *callback_name,
const gchar *object_name,
PikaObject *object,
va_list args);
static gboolean gui_pdb_dialog_set (Pika *pika,
PikaContainer *container,
GType contents_type,
const gchar *callback_name,
const gchar *object_name,
PikaObject *object,
va_list args);
static gboolean gui_pdb_dialog_close (Pika *pika,
PikaContainer *container,
GType contents_type,
const gchar *callback_name);
static gboolean gui_recent_list_add_file (Pika *pika,
GFile *file,
@ -378,7 +382,7 @@ gui_get_empty_display (Pika *pika)
return display;
}
static guint32
static GBytes *
gui_display_get_window_id (PikaDisplay *display)
{
PikaDisplay *disp = PIKA_DISPLAY (display);
@ -386,13 +390,11 @@ gui_display_get_window_id (PikaDisplay *display)
if (shell)
{
GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (shell));
if (GTK_IS_WINDOW (toplevel))
return pika_window_get_native_id (GTK_WINDOW (toplevel));
if (shell)
return g_bytes_ref (shell->window_handle);
}
return 0;
return NULL;
}
static PikaDisplay *
@ -516,16 +518,16 @@ gui_wait (Pika *pika,
args = pika_procedure_get_arguments (procedure);
pika_value_array_truncate (args, 5);
g_value_set_enum (pika_value_array_index (args, 0),
PIKA_RUN_INTERACTIVE);
g_value_set_int (pika_value_array_index (args, 1),
output_pipe[0]);
g_value_set_int (pika_value_array_index (args, 2),
input_pipe[1]);
g_value_set_string (pika_value_array_index (args, 3),
message);
g_value_set_int (pika_value_array_index (args, 4),
PIKA_IS_CANCELABLE (waitable));
g_value_set_enum (pika_value_array_index (args, 0),
PIKA_RUN_INTERACTIVE);
g_value_set_int (pika_value_array_index (args, 1),
output_pipe[0]);
g_value_set_int (pika_value_array_index (args, 2),
input_pipe[1]);
g_value_set_string (pika_value_array_index (args, 3),
message);
g_value_set_boolean (pika_value_array_index (args, 4),
PIKA_IS_CANCELABLE (waitable));
pika_procedure_execute_async (procedure, pika,
pika_get_user_context (pika),
@ -616,65 +618,74 @@ static gboolean
gui_pdb_dialog_new (Pika *pika,
PikaContext *context,
PikaProgress *progress,
PikaContainer *container,
GType contents_type,
GBytes *parent_handle,
const gchar *title,
const gchar *callback_name,
const gchar *object_name,
PikaObject *object,
va_list args)
{
GType dialog_type = G_TYPE_NONE;
const gchar *dialog_role = NULL;
const gchar *help_id = NULL;
if (pika_container_get_children_type (container) == PIKA_TYPE_BRUSH)
if (contents_type == PIKA_TYPE_BRUSH)
{
dialog_type = PIKA_TYPE_BRUSH_SELECT;
dialog_role = "pika-brush-selection";
help_id = PIKA_HELP_BRUSH_DIALOG;
}
else if (pika_container_get_children_type (container) == PIKA_TYPE_FONT)
else if (contents_type == PIKA_TYPE_FONT)
{
dialog_type = PIKA_TYPE_FONT_SELECT;
dialog_role = "pika-font-selection";
help_id = PIKA_HELP_FONT_DIALOG;
}
else if (pika_container_get_children_type (container) == PIKA_TYPE_GRADIENT)
else if (contents_type == PIKA_TYPE_GRADIENT)
{
dialog_type = PIKA_TYPE_GRADIENT_SELECT;
dialog_role = "pika-gradient-selection";
help_id = PIKA_HELP_GRADIENT_DIALOG;
}
else if (pika_container_get_children_type (container) == PIKA_TYPE_PALETTE)
else if (contents_type == PIKA_TYPE_PALETTE)
{
dialog_type = PIKA_TYPE_PALETTE_SELECT;
dialog_role = "pika-palette-selection";
help_id = PIKA_HELP_PALETTE_DIALOG;
}
else if (pika_container_get_children_type (container) == PIKA_TYPE_PATTERN)
else if (contents_type == PIKA_TYPE_PATTERN)
{
dialog_type = PIKA_TYPE_PATTERN_SELECT;
dialog_role = "pika-pattern-selection";
help_id = PIKA_HELP_PATTERN_DIALOG;
}
else if (g_type_is_a (contents_type, PIKA_TYPE_DRAWABLE))
{
dialog_type = PIKA_TYPE_PICKABLE_SELECT;
dialog_role = "pika-pickable-selection";
}
else
{
g_return_val_if_reached (FALSE);
}
if (dialog_type != G_TYPE_NONE)
{
PikaObject *object = NULL;
if (! object && ! g_type_is_a (contents_type, PIKA_TYPE_DRAWABLE))
object = pika_context_get_by_type (context, contents_type);
if (object_name && strlen (object_name))
object = pika_container_get_child_by_name (container, object_name);
if (! object)
object = pika_context_get_by_type (context,
pika_container_get_children_type (container));
if (object)
if (object || g_type_is_a (contents_type, PIKA_TYPE_DRAWABLE))
{
gint n_properties = 0;
gchar **names = NULL;
GValue *values = NULL;
GtkWidget *dialog;
GtkWidget *view;
gboolean use_header_bar;
g_object_get (gtk_settings_get_default (),
"gtk-dialogs-use-header", &use_header_bar,
NULL);
names = pika_properties_append (dialog_type,
&n_properties, names, &values,
@ -684,10 +695,11 @@ gui_pdb_dialog_new (Pika *pika,
"help-id", help_id,
"pdb", pika->pdb,
"context", context,
"select-type", pika_container_get_children_type (container),
"select-type", contents_type,
"initial-object", object,
"callback-name", callback_name,
"menu-factory", menus_get_global_menu_factory (pika),
"use-header-bar", use_header_bar,
NULL);
names = pika_properties_append_valist (dialog_type,
@ -707,27 +719,25 @@ gui_pdb_dialog_new (Pika *pika,
pika_docked_set_show_button_bar (PIKA_DOCKED (view), FALSE);
if (progress)
{
guint32 window_id = pika_progress_get_window_id (progress);
if (window_id)
pika_window_set_transient_for (GTK_WINDOW (dialog), window_id);
}
pika_window_set_transient_for (GTK_WINDOW (dialog), progress);
gtk_widget_show (dialog);
/* workaround for bug #360106 */
{
GSource *source = g_timeout_source_new (100);
GClosure *closure;
{
GSource *source = g_timeout_source_new (100);
GClosure *closure;
closure = g_cclosure_new_object (G_CALLBACK (gui_pdb_dialog_present),
G_OBJECT (dialog));
closure = g_cclosure_new_object (G_CALLBACK (gui_pdb_dialog_present),
G_OBJECT (dialog));
g_source_set_closure (source, closure);
g_source_attach (source, NULL);
g_source_unref (source);
}
g_source_set_closure (source, closure);
g_source_attach (source, NULL);
g_source_unref (source);
}
if (parent_handle != NULL)
pika_window_set_transient_for_handle (GTK_WINDOW (dialog), parent_handle);
return TRUE;
}
@ -738,73 +748,96 @@ gui_pdb_dialog_new (Pika *pika,
static gboolean
gui_pdb_dialog_set (Pika *pika,
PikaContainer *container,
GType contents_type,
const gchar *callback_name,
const gchar *object_name,
PikaObject *object,
va_list args)
{
PikaPdbDialogClass *klass = NULL;
PikaPdbDialogClass *klass = NULL;
PikaContainer *container = NULL;
PikaPdbDialog *dialog;
if (pika_container_get_children_type (container) == PIKA_TYPE_BRUSH)
klass = g_type_class_peek (PIKA_TYPE_BRUSH_SELECT);
else if (pika_container_get_children_type (container) == PIKA_TYPE_FONT)
klass = g_type_class_peek (PIKA_TYPE_FONT_SELECT);
else if (pika_container_get_children_type (container) == PIKA_TYPE_GRADIENT)
klass = g_type_class_peek (PIKA_TYPE_GRADIENT_SELECT);
else if (pika_container_get_children_type (container) == PIKA_TYPE_PALETTE)
klass = g_type_class_peek (PIKA_TYPE_PALETTE_SELECT);
else if (pika_container_get_children_type (container) == PIKA_TYPE_PATTERN)
klass = g_type_class_peek (PIKA_TYPE_PATTERN_SELECT);
if (klass)
if (contents_type == PIKA_TYPE_BRUSH)
{
PikaPdbDialog *dialog;
klass = g_type_class_peek (PIKA_TYPE_BRUSH_SELECT);
container = pika_data_factory_get_container (pika->brush_factory);
}
else if (contents_type == PIKA_TYPE_FONT)
{
klass = g_type_class_peek (PIKA_TYPE_FONT_SELECT);
container = pika_data_factory_get_container (pika->font_factory);
}
else if (contents_type == PIKA_TYPE_GRADIENT)
{
klass = g_type_class_peek (PIKA_TYPE_GRADIENT_SELECT);
container = pika_data_factory_get_container (pika->gradient_factory);
}
else if (contents_type == PIKA_TYPE_PALETTE)
{
klass = g_type_class_peek (PIKA_TYPE_PALETTE_SELECT);
container = pika_data_factory_get_container (pika->palette_factory);
}
else if (contents_type == PIKA_TYPE_PATTERN)
{
klass = g_type_class_peek (PIKA_TYPE_PATTERN_SELECT);
container = pika_data_factory_get_container (pika->pattern_factory);
}
else if (contents_type == PIKA_TYPE_DRAWABLE)
{
klass = g_type_class_peek (PIKA_TYPE_PICKABLE_SELECT);
}
dialog = pika_pdb_dialog_get_by_callback (klass, callback_name);
g_return_val_if_fail (klass != NULL, FALSE);
if (dialog && dialog->select_type == pika_container_get_children_type (container))
dialog = pika_pdb_dialog_get_by_callback (klass, callback_name);
if (dialog != NULL &&
dialog->select_type == contents_type &&
(container == NULL || pika_container_get_child_index (container, object) != -1))
{
const gchar *prop_name = va_arg (args, const gchar *);
if (g_type_is_a (contents_type, PIKA_TYPE_RESOURCE))
{
PikaObject *object;
object = pika_container_get_child_by_name (container, object_name);
if (object)
{
const gchar *prop_name = va_arg (args, const gchar *);
pika_context_set_by_type (dialog->context, dialog->select_type,
object);
if (prop_name)
g_object_set_valist (G_OBJECT (dialog), prop_name, args);
gtk_window_present (GTK_WINDOW (dialog));
return TRUE;
}
g_return_val_if_fail (container != NULL, FALSE);
pika_context_set_by_type (dialog->context, dialog->select_type, object);
}
else
{
g_return_val_if_fail (klass->set_object != NULL, FALSE);
klass->set_object (dialog, object);
}
if (prop_name)
g_object_set_valist (G_OBJECT (dialog), prop_name, args);
gtk_window_present (GTK_WINDOW (dialog));
return TRUE;
}
return FALSE;
}
static gboolean
gui_pdb_dialog_close (Pika *pika,
PikaContainer *container,
const gchar *callback_name)
gui_pdb_dialog_close (Pika *pika,
GType contents_type,
const gchar *callback_name)
{
PikaPdbDialogClass *klass = NULL;
if (pika_container_get_children_type (container) == PIKA_TYPE_BRUSH)
if (contents_type == PIKA_TYPE_BRUSH)
klass = g_type_class_peek (PIKA_TYPE_BRUSH_SELECT);
else if (pika_container_get_children_type (container) == PIKA_TYPE_FONT)
else if (contents_type == PIKA_TYPE_FONT)
klass = g_type_class_peek (PIKA_TYPE_FONT_SELECT);
else if (pika_container_get_children_type (container) == PIKA_TYPE_GRADIENT)
else if (contents_type == PIKA_TYPE_GRADIENT)
klass = g_type_class_peek (PIKA_TYPE_GRADIENT_SELECT);
else if (pika_container_get_children_type (container) == PIKA_TYPE_PALETTE)
else if (contents_type == PIKA_TYPE_PALETTE)
klass = g_type_class_peek (PIKA_TYPE_PALETTE_SELECT);
else if (pika_container_get_children_type (container) == PIKA_TYPE_PATTERN)
else if (contents_type == PIKA_TYPE_PATTERN)
klass = g_type_class_peek (PIKA_TYPE_PATTERN_SELECT);
else if (contents_type == PIKA_TYPE_DRAWABLE)
klass = g_type_class_peek (PIKA_TYPE_PICKABLE_SELECT);
if (klass)
{
@ -812,7 +845,7 @@ gui_pdb_dialog_close (Pika *pika,
dialog = pika_pdb_dialog_get_by_callback (klass, callback_name);
if (dialog && dialog->select_type == pika_container_get_children_type (container))
if (dialog && dialog->select_type == contents_type)
{
gtk_widget_destroy (GTK_WIDGET (dialog));
return TRUE;

View File

@ -90,6 +90,12 @@
#include "splash.h"
#include "themes.h"
#ifdef G_OS_WIN32
#include <windef.h>
#include <winbase.h>
#include <windows.h>
#endif
#ifdef GDK_WINDOWING_QUARTZ
#import <AppKit/AppKit.h>
@ -543,6 +549,11 @@ gui_restore_after_callback (Pika *pika,
PikaGuiConfig *gui_config = PIKA_GUI_CONFIG (pika->config);
PikaUIManager *image_ui_manager;
PikaDisplay *display;
#ifdef G_OS_WIN32
STARTUPINFO StartupInfo;
GetStartupInfo (&StartupInfo);
#endif
if (pika->be_verbose)
g_print ("INIT: %s\n", G_STRFUNC);
@ -610,11 +621,23 @@ gui_restore_after_callback (Pika *pika,
shell = pika_display_get_shell (display);
#ifdef G_OS_WIN32
themes_set_title_bar (pika);
#endif
if (gui_config->restore_session)
session_restore (pika, initial_monitor);
toplevel = gtk_widget_get_toplevel (GTK_WIDGET (shell));
#ifdef G_OS_WIN32
/* Prevents window from reappearing on start-up if the user
* requested it to be minimized via window hints
*/
if (StartupInfo.wShowWindow != SW_SHOWMINIMIZED &&
StartupInfo.wShowWindow != SW_SHOWMINNOACTIVE &&
StartupInfo.wShowWindow != SW_MINIMIZE)
#endif
/* move keyboard focus to the display */
gtk_window_present (GTK_WINDOW (toplevel));
}
@ -925,12 +948,26 @@ gui_check_unique_accelerators (Pika *pika)
gchar **disabled_accels;
gint len;
gint remove;
gboolean print_warning = TRUE;
action = g_action_map_lookup_action (G_ACTION_MAP (pika->app),
actions[i]);
/* Just keep the first one (no reason other than we have
* to choose), unless it's a secondary shortcut, and the
* second is a primary shortcut.
*/
if (l == 0 && j != 0)
if ((l == 0 && j != 0) ||
/* If the first action is one of "view-zoom-1-*" and
* the shortcut default, we assume it's because of our
* trick to transform `Shift+num` shortcuts based on
* layout and we happen to be on a layout where it
* clashes with other shortcuts. In this case, we
* drop the duplicate shortcut on the zoom action. See
* special code in
* pika_action_group_add_action_with_accel()
*/
(g_str_has_prefix (actions[i], "view-zoom-1-") &&
pika_action_is_default_accel (PIKA_ACTION (action), accels[j])))
{
disabled_action = actions[i];
disabled_accels = accels;
@ -942,6 +979,20 @@ gui_check_unique_accelerators (Pika *pika)
disabled_accels = accels2;
remove = l;
}
action = g_action_map_lookup_action (G_ACTION_MAP (pika->app),
disabled_action);
if (g_str_has_prefix (disabled_action, "view-zoom-1-") &&
pika_action_is_default_accel (PIKA_ACTION (action), disabled_accels[remove]))
/* We drop the shortcut **silently** because it will be
* a case where we have 2 default accelerators clashing
* (because of the conversion code) while not being a
* real bug. Clashes with custom accelerators are
* handled by shortcuts_action_deserialize().
*/
print_warning = FALSE;
/* Remove only the duplicate shortcut but keep others. */
len = g_strv_length (disabled_accels);
g_free (disabled_accels[remove]);
@ -949,12 +1000,11 @@ gui_check_unique_accelerators (Pika *pika)
&disabled_accels[remove + 1],
sizeof (char *) * (len - remove));
g_printerr ("Actions \"%s\" and \"%s\" use the same accelerator.\n"
" Disabling the accelerator on \"%s\".\n",
actions[i], actions[k], disabled_action);
if (print_warning)
g_printerr ("Actions \"%s\" and \"%s\" use the same accelerator.\n"
" Disabling the accelerator on \"%s\".\n",
actions[i], actions[k], disabled_action);
action = g_action_map_lookup_action (G_ACTION_MAP (pika->app),
disabled_action);
pika_action_set_accels (PIKA_ACTION (action),
(const gchar **) disabled_accels);
}

View File

@ -29,6 +29,12 @@
#include <gdk/gdkwayland.h>
#endif
#ifdef G_OS_WIN32
#include <windef.h>
#include <winbase.h>
#include <windows.h>
#endif
#include "libpikabase/pikabase.h"
#include "libpikamath/pikamath.h"
#include "libpikacolor/pikacolor.h"
@ -125,6 +131,11 @@ splash_create (Pika *pika,
GdkRectangle workarea;
gint max_width;
gint max_height;
#ifdef G_OS_WIN32
STARTUPINFO StartupInfo;
GetStartupInfo (&StartupInfo);
#endif
g_return_if_fail (splash == NULL);
g_return_if_fail (GDK_IS_MONITOR (monitor));
@ -255,6 +266,13 @@ splash_create (Pika *pika,
gtk_widget_show (splash->window);
#ifdef G_OS_WIN32
if (StartupInfo.wShowWindow == SW_SHOWMINIMIZED ||
StartupInfo.wShowWindow == SW_SHOWMINNOACTIVE ||
StartupInfo.wShowWindow == SW_MINIMIZE)
gtk_window_iconify (GTK_WINDOW (splash->window));
#endif
if (FALSE)
splash->timer = g_timer_new ();
}

View File

@ -35,6 +35,10 @@
#include "core/pika.h"
#include "display/pikaimagewindow.h"
#include "widgets/pikawidgets-utils.h"
#include "themes.h"
#include "pika-intl.h"
@ -109,6 +113,10 @@ themes_init (Pika *pika)
pika);
themes_theme_change_notify (config, NULL, pika);
#ifdef G_OS_WIN32
themes_set_title_bar (pika);
#endif
}
void
@ -473,6 +481,10 @@ themes_theme_change_notify (PikaGuiConfig *config,
g_object_unref (theme_css);
gtk_style_context_reset_widgets (gdk_screen_get_default ());
#ifdef G_OS_WIN32
themes_set_title_bar (pika);
#endif
}
static void
@ -553,3 +565,22 @@ themes_theme_paths_notify (PikaExtensionManager *manager,
g_list_free_full (path, (GDestroyNotify) g_object_unref);
}
}
void
themes_set_title_bar (Pika *pika)
{
#ifdef G_OS_WIN32
GList *windows = pika_get_image_windows (pika);
GList *iter;
for (iter = windows; iter; iter = g_list_next (iter))
{
GtkWidget *window = GTK_WIDGET (windows->data);
pika_window_set_title_bar_theme (pika, window, TRUE);
}
if (windows)
g_list_free (windows);
#endif
}

View File

@ -34,5 +34,6 @@ GFile * themes_get_theme_file (Pika *pika,
const gchar *first_component,
...) G_GNUC_NULL_TERMINATED;
void themes_set_title_bar (Pika *pika);
#endif /* __THEMES_H__ */

View File

@ -28,7 +28,6 @@
#include <gtk/gtk.h>
#include "libpikabase/pikabase.h"
#include "libpikabase/pikaprotocol.h"
#include "libpikaconfig/pikaconfig.h"
#include "menus-types.h"
@ -56,8 +55,7 @@ static GTokenType shortcuts_action_deserialize (GScanner *scanner,
enum
{
PROTOCOL_VERSION = 1,
FILE_VERSION,
FILE_VERSION = 1,
ACTION,
};
@ -68,8 +66,7 @@ shortcuts_rc_parse (GtkApplication *application,
GError **error)
{
GScanner *scanner;
gint protocol_version = PIKA_PROTOCOL_VERSION;
gint file_version = SHORTCUTS_RC_FILE_VERSION;
gint file_version = SHORTCUTS_RC_FILE_VERSION;
GTokenType token;
g_return_val_if_fail (GTK_IS_APPLICATION (application), FALSE);
@ -81,9 +78,6 @@ shortcuts_rc_parse (GtkApplication *application,
if (! scanner)
return FALSE;
g_scanner_scope_add_symbol (scanner, 0,
"protocol-version",
GINT_TO_POINTER (PROTOCOL_VERSION));
g_scanner_scope_add_symbol (scanner, 0,
"file-version",
GINT_TO_POINTER (FILE_VERSION));
@ -92,9 +86,9 @@ shortcuts_rc_parse (GtkApplication *application,
token = G_TOKEN_LEFT_PAREN;
while (protocol_version == PIKA_PROTOCOL_VERSION &&
file_version == SHORTCUTS_RC_FILE_VERSION &&
g_scanner_peek_next_token (scanner) == token)
while (g_scanner_peek_next_token (scanner) == token ||
(token == G_TOKEN_SYMBOL &&
g_scanner_peek_next_token (scanner) == G_TOKEN_IDENTIFIER))
{
token = g_scanner_get_next_token (scanner);
@ -107,12 +101,6 @@ shortcuts_rc_parse (GtkApplication *application,
case G_TOKEN_SYMBOL:
switch (GPOINTER_TO_INT (scanner->value.v_symbol))
{
case PROTOCOL_VERSION:
token = G_TOKEN_INT;
if (pika_scanner_parse_int (scanner, &protocol_version))
token = G_TOKEN_RIGHT_PAREN;
break;
case FILE_VERSION:
token = G_TOKEN_INT;
if (pika_scanner_parse_int (scanner, &file_version))
@ -127,7 +115,17 @@ shortcuts_rc_parse (GtkApplication *application,
default:
break;
}
break;
break;
case G_TOKEN_IDENTIFIER:
g_printerr ("%s: ignoring unknown symbol '%s'.\n", G_STRFUNC, scanner->value.v_string);
while ((token = g_scanner_get_next_token (scanner)) != G_TOKEN_EOF)
{
if (token == G_TOKEN_RIGHT_PAREN)
break;
}
token = G_TOKEN_LEFT_PAREN;
break;
case G_TOKEN_RIGHT_PAREN:
token = G_TOKEN_LEFT_PAREN;
@ -138,25 +136,16 @@ shortcuts_rc_parse (GtkApplication *application,
}
}
if (protocol_version != PIKA_PROTOCOL_VERSION ||
file_version != SHORTCUTS_RC_FILE_VERSION ||
token != G_TOKEN_LEFT_PAREN)
if (file_version != SHORTCUTS_RC_FILE_VERSION)
{
if (protocol_version != PIKA_PROTOCOL_VERSION)
{
g_set_error (error,
PIKA_CONFIG_ERROR, PIKA_CONFIG_ERROR_VERSION,
_("Skipping '%s': wrong PIKA protocol version."),
pika_file_get_utf8_name (file));
}
else if (file_version != SHORTCUTS_RC_FILE_VERSION)
{
g_set_error (error,
PIKA_CONFIG_ERROR, PIKA_CONFIG_ERROR_VERSION,
_("Skipping '%s': wrong shortcutsrc file format version."),
pika_file_get_utf8_name (file));
}
else if (token != G_TOKEN_ERROR)
g_printerr (_("Wrong shortcutsrc (%s) file format version: %d (expected: %d). "
"We tried to load shortcuts as well as possible.\n"),
pika_file_get_utf8_name (file),
file_version, SHORTCUTS_RC_FILE_VERSION);
}
if (token != G_TOKEN_LEFT_PAREN)
{
if (token != G_TOKEN_ERROR)
{
g_scanner_get_next_token (scanner);
g_scanner_unexp_token (scanner, token, NULL, NULL, NULL,
@ -194,10 +183,6 @@ shortcuts_rc_write (GtkApplication *application,
actions = g_action_group_list_actions (G_ACTION_GROUP (application));
pika_config_writer_open (writer, "protocol-version");
pika_config_writer_printf (writer, "%d", PIKA_PROTOCOL_VERSION);
pika_config_writer_close (writer);
pika_config_writer_open (writer, "file-version");
pika_config_writer_printf (writer, "%d", SHORTCUTS_RC_FILE_VERSION);
pika_config_writer_close (writer);

View File

@ -85,5 +85,12 @@ typedef enum /*< skip, pdb-skip >*/
PIKA_PAINT_STATE_FINISH /* Cleanup and/or reset PaintFunc operation */
} PikaPaintState;
/* State of lock blinking */
typedef enum /*< skip, pdb-skip >*/
{
PIKA_PAINT_LOCK_NOT_BLINKED,
PIKA_PAINT_LOCK_BLINK_PENDING,
PIKA_PAINT_LOCK_BLINKED,
} PikaPaintLockBlinkState;
#endif /* __PAINT_ENUMS_H__ */

View File

@ -819,6 +819,8 @@ pika_brush_core_get_paint_buffer (PikaPaintCore *paint_core,
gint x1, y1, x2, y2;
gint drawable_width, drawable_height;
gint brush_width, brush_height;
gint offset_change_x, offset_change_y;
PikaCoords new_coords;
pika_brush_transform_size (core->brush,
core->scale, core->aspect_ratio,
@ -835,11 +837,31 @@ pika_brush_core_get_paint_buffer (PikaPaintCore *paint_core,
x = (gint) floor (coords->x) - (brush_width / 2);
y = (gint) floor (coords->y) - (brush_height / 2);
x1 = x - 1;
y1 = y - 1;
x2 = x + brush_width + 1;
y2 = y + brush_height + 1;
pika_paint_core_expand_drawable (paint_core, drawable, paint_options,
x1, x2, y1, y2,
&offset_change_x, &offset_change_y);
if (offset_change_x || offset_change_y)
{
x += offset_change_x;
y += offset_change_y;
new_coords = *coords;
new_coords.x = coords->x + offset_change_x;
new_coords.y = coords->y + offset_change_y;
pika_symmetry_set_origin (paint_core->sym, drawable, &new_coords);
}
drawable_width = pika_item_get_width (PIKA_ITEM (drawable));
drawable_height = pika_item_get_height (PIKA_ITEM (drawable));
x1 = CLAMP (x - 1, 0, drawable_width);
y1 = CLAMP (y - 1, 0, drawable_height);
x1 = CLAMP (x - 1, 0, drawable_width);
y1 = CLAMP (y - 1, 0, drawable_height);
x2 = CLAMP (x + brush_width + 1, 0, drawable_width);
y2 = CLAMP (y + brush_height + 1, 0, drawable_height);

View File

@ -470,6 +470,21 @@ pika_blob_duplicate (PikaBlob *b)
return g_memdup2 (b, sizeof (PikaBlob) + sizeof (PikaBlobSpan) * (b->height - 1));
}
void
pika_blob_move (PikaBlob *b,
gint x,
gint y)
{
gint i = 0;
b->y += y;
for (i = 0; i < b->height; i++)
{
b->data[i].left += x;
b->data[i].right += x;
}
}
#if 0
void
pika_blob_dump (PikaBlob *b)

View File

@ -86,6 +86,9 @@ void pika_blob_bounds (PikaBlob *b,
PikaBlob * pika_blob_convex_union (PikaBlob *b1,
PikaBlob *b2);
PikaBlob * pika_blob_duplicate (PikaBlob *b);
void pika_blob_move (PikaBlob *b,
gint x,
gint y);
#endif /* __PIKA_INK_BLOB_H__ */

View File

@ -242,17 +242,49 @@ pika_ink_get_paint_buffer (PikaPaintCore *paint_core,
gint *paint_width,
gint *paint_height)
{
PikaInk *ink = PIKA_INK (paint_core);
gint x, y;
gint width, height;
gint dwidth, dheight;
gint x1, y1, x2, y2;
PikaInk *ink = PIKA_INK (paint_core);
gint x, y;
gint width, height;
gint dwidth, dheight;
gint x1, y1, x2, y2;
gint offset_change_x, offset_change_y;
PikaCoords new_coords;
GList *iter;
pika_blob_bounds (ink->cur_blob, &x, &y, &width, &height);
x1 = x / SUBSAMPLE - 1;
y1 = y / SUBSAMPLE - 1;
x2 = (x + width) / SUBSAMPLE + 2;
y2 = (y + height) / SUBSAMPLE + 2;
pika_paint_core_expand_drawable (paint_core, drawable, paint_options,
x1, x2, y1, y2,
&offset_change_x, &offset_change_y);
dwidth = pika_item_get_width (PIKA_ITEM (drawable));
dheight = pika_item_get_height (PIKA_ITEM (drawable));
if (offset_change_x || offset_change_y)
{
x += SUBSAMPLE * offset_change_x;
y += SUBSAMPLE * offset_change_y;
new_coords = *coords;
new_coords.x = coords->x + offset_change_x;
new_coords.y = coords->y + offset_change_y;
pika_symmetry_set_origin (paint_core->sym, drawable, &new_coords);
for (iter = ink->blobs_to_render; iter; iter = g_list_next (iter))
pika_blob_move (iter->data,
SUBSAMPLE * offset_change_x,
SUBSAMPLE * offset_change_y);
for (iter = ink->last_blobs; iter; iter = g_list_next (iter))
pika_blob_move (iter->data,
SUBSAMPLE * offset_change_x,
SUBSAMPLE * offset_change_y);
}
x1 = CLAMP (x / SUBSAMPLE - 1, 0, dwidth);
y1 = CLAMP (y / SUBSAMPLE - 1, 0, dheight);
x2 = CLAMP ((x + width) / SUBSAMPLE + 2, 0, dwidth);
@ -319,7 +351,6 @@ pika_ink_motion (PikaPaintCore *paint_core,
PikaInk *ink = PIKA_INK (paint_core);
PikaInkOptions *options = PIKA_INK_OPTIONS (paint_options);
PikaContext *context = PIKA_CONTEXT (paint_options);
GList *blob_unions = NULL;
GList *blobs_to_render = NULL;
GeglBuffer *paint_buffer;
gint paint_buffer_x;
@ -338,6 +369,7 @@ pika_ink_motion (PikaPaintCore *paint_core,
coords.x -= off_x;
coords.y -= off_y;
pika_symmetry_set_origin (sym, drawable, &coords);
paint_core->sym = sym;
n_strokes = pika_symmetry_get_size (sym);
@ -377,7 +409,8 @@ pika_ink_motion (PikaPaintCore *paint_core,
last_blob);
ink->start_blobs = g_list_prepend (ink->start_blobs,
pika_blob_duplicate (last_blob));
blobs_to_render = g_list_prepend (blobs_to_render, last_blob);
blobs_to_render = g_list_prepend (blobs_to_render,
pika_blob_duplicate (last_blob));
}
ink->start_blobs = g_list_reverse (ink->start_blobs);
ink->last_blobs = g_list_reverse (ink->last_blobs);
@ -411,7 +444,6 @@ pika_ink_motion (PikaPaintCore *paint_core,
g_list_nth (ink->last_blobs, i)->data = blob;
blobs_to_render = g_list_prepend (blobs_to_render, blob_union);
blob_unions = g_list_prepend (blob_unions, blob_union);
}
blobs_to_render = g_list_reverse (blobs_to_render);
}
@ -422,6 +454,7 @@ pika_ink_motion (PikaPaintCore *paint_core,
pika_pickable_srgb_to_image_color (PIKA_PICKABLE (drawable),
&foreground, &foreground);
color = pika_gegl_color_new (&foreground, pika_drawable_get_space (drawable));
ink->blobs_to_render = blobs_to_render;
for (i = 0; i < n_strokes; i++)
{
@ -467,8 +500,7 @@ pika_ink_motion (PikaPaintCore *paint_core,
g_object_unref (color);
g_list_free_full (blob_unions, g_free);
g_list_free (blobs_to_render);
g_list_free_full (blobs_to_render, g_free);
}
static PikaBlob *

View File

@ -41,10 +41,11 @@ struct _PikaInk
{
PikaPaintCore parent_instance;
GList *start_blobs; /* starting blobs per stroke (for undo) */
GList *start_blobs; /* starting blobs per stroke (for undo) */
PikaBlob *cur_blob; /* current blob */
GList *last_blobs; /* blobs for last stroke positions */
PikaBlob *cur_blob; /* current blob */
GList *last_blobs; /* blobs for last stroke positions */
GList *blobs_to_render;
};
struct _PikaInkClass

View File

@ -259,7 +259,7 @@ pika_mybrush_core_motion (PikaPaintCore *paint_core,
{
PikaMybrushCore *mybrush = PIKA_MYBRUSH_CORE (paint_core);
MyPaintRectangle rect;
PikaCoords coords;
PikaCoords origin;
GList *iter;
gdouble dt = 0.0;
gint off_x, off_y;
@ -269,10 +269,10 @@ pika_mybrush_core_motion (PikaPaintCore *paint_core,
pika_item_get_offset (PIKA_ITEM (drawable), &off_x, &off_y);
n_strokes = pika_symmetry_get_size (sym);
coords = *(pika_symmetry_get_origin (sym));
coords.x -= off_x;
coords.y -= off_y;
pika_symmetry_set_origin (sym, drawable, &coords);
origin = *(pika_symmetry_get_origin (sym));
origin.x -= off_x;
origin.y -= off_y;
pika_symmetry_set_origin (sym, drawable, &origin);
/* The number of strokes may change during a motion, depending on
* the type of symmetry. When that happens, reset the brushes.
@ -291,9 +291,8 @@ pika_mybrush_core_motion (PikaPaintCore *paint_core,
iter;
iter = g_list_next (iter), i++)
{
MyPaintBrush *brush = iter->data;
coords = *(pika_symmetry_get_coords (sym, i));
MyPaintBrush *brush = iter->data;
PikaCoords coords = *(pika_symmetry_get_coords (sym, i));
mypaint_brush_stroke_to (brush,
(MyPaintSurface *) mybrush->private->surface,
@ -326,6 +325,42 @@ pika_mybrush_core_motion (PikaPaintCore *paint_core,
MyPaintBrush *brush = iter->data;
PikaCoords coords = *(pika_symmetry_get_coords (sym, i));
gdouble pressure = coords.pressure;
gboolean expanded;
gfloat radius = 100;
gint x1, x2, y1, y2;
gint offset_change_x, offset_change_y;
gint off_x_surf, off_y_surf;
gint off_x, off_y;
x1 = coords.x - radius;
y1 = coords.y - radius;
x2 = coords.x + radius;
y2 = coords.y + radius;
expanded = pika_paint_core_expand_drawable (paint_core, drawable, paint_options,
x1, x2, y1, y2,
&offset_change_x, &offset_change_y);
pika_item_get_offset (PIKA_ITEM (drawable), &off_x, &off_y);
if (expanded)
pika_mypaint_surface_set_buffer (mybrush->private->surface, pika_drawable_get_buffer (drawable),
off_x, off_y);
pika_mypaint_surface_get_offset (mybrush->private->surface, &off_x_surf, &off_y_surf);
coords.x -= off_x_surf;
coords.y -= off_y_surf;
if (offset_change_x || offset_change_y)
{
pika_mypaint_surface_set_offset (mybrush->private->surface,
off_x_surf + offset_change_x,
off_y_surf + offset_change_y);
origin = *(pika_symmetry_get_origin (sym));
origin.x += offset_change_x;
origin.y += offset_change_y;
pika_symmetry_set_origin (sym, drawable, &origin);
}
mypaint_brush_stroke_to (brush,
(MyPaintSurface *) mybrush->private->surface,

View File

@ -38,13 +38,15 @@
struct _PikaMybrushSurface
{
MyPaintSurface surface;
GeglBuffer *buffer;
GeglBuffer *paint_mask;
gint paint_mask_x;
gint paint_mask_y;
GeglRectangle dirty;
PikaComponentMask component_mask;
MyPaintSurface surface;
GeglBuffer *buffer;
GeglBuffer *paint_mask;
gint paint_mask_x;
gint paint_mask_y;
gint off_x;
gint off_y;
GeglRectangle dirty;
PikaComponentMask component_mask;
PikaMybrushOptions *options;
};
@ -382,6 +384,8 @@ pika_mypaint_surface_draw_dab (MyPaintSurface *base_surface,
colorize = opaque * colorize;
/* FIXME: This should use the real matrix values to trim aspect_ratio dabs */
x += surface->off_x;
y += surface->off_y;
dabRect = calculate_dab_roi (x, y, radius);
gegl_rectangle_intersect (&dabRect, &dabRect, gegl_buffer_get_extent (surface->buffer));
@ -561,5 +565,40 @@ pika_mypaint_surface_new (GeglBuffer *buffer,
surface->paint_mask_y = paint_mask_y;
surface->dirty = *GEGL_RECTANGLE (0, 0, 0, 0);
surface->off_x = 0;
surface->off_y = 0;
return surface;
}
void
pika_mypaint_surface_set_buffer (PikaMybrushSurface *surface,
GeglBuffer *buffer,
gint paint_mask_x,
gint paint_mask_y)
{
g_object_unref (surface->buffer);
surface->buffer = g_object_ref (buffer);
surface->paint_mask_x = paint_mask_x;
surface->paint_mask_y = paint_mask_y;
}
void
pika_mypaint_surface_set_offset (PikaMybrushSurface *surface,
gint off_x,
gint off_y)
{
surface->off_x = off_x;
surface->off_y = off_y;
}
void
pika_mypaint_surface_get_offset (PikaMybrushSurface *surface,
gint *off_x,
gint *off_y)
{
*off_x = surface->off_x;
*off_y = surface->off_y;
}

View File

@ -33,5 +33,18 @@ pika_mypaint_surface_new (GeglBuffer *buffer,
gint paint_mask_y,
PikaMybrushOptions *options);
void
pika_mypaint_surface_set_buffer (PikaMybrushSurface *surface,
GeglBuffer *buffer,
gint paint_mask_x,
gint paint_mask_y);
void
pika_mypaint_surface_set_offset (PikaMybrushSurface *surface,
gint off_x,
gint off_y);
void
pika_mypaint_surface_get_offset (PikaMybrushSurface *surface,
gint *off_x,
gint *off_y);
#endif /* __PIKA_MYBRUSH_SURFACE_H__ */

View File

@ -193,7 +193,8 @@ pika_paintbrush_real_get_paint_params (PikaPaintbrush *paintbrush,
*paint_mode = pika_context_get_paint_mode (context);
if (pika_paint_options_get_gradient_color (paint_options, image,
if (pika_paint_options_are_dynamics_enabled (paint_options) &&
pika_paint_options_get_gradient_color (paint_options, image,
grad_point,
paint_core->pixel_dist,
paint_color))
@ -252,6 +253,7 @@ _pika_paintbrush_motion (PikaPaintCore *paint_core,
coords.x -= off_x;
coords.y -= off_y;
pika_symmetry_set_origin (sym, drawable, &coords);
paint_core->sym = sym;
/* Some settings are based on the original stroke. */
opacity *= pika_dynamics_get_linear_value (dynamics,
@ -313,6 +315,8 @@ _pika_paintbrush_motion (PikaPaintCore *paint_core,
&paint_buffer_y,
&paint_width,
&paint_height);
coords = *(pika_symmetry_get_coords (sym, i));
if (! paint_buffer)
continue;

View File

@ -24,10 +24,12 @@
#include <string.h>
#include <cairo.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <gegl.h>
#include "libpikabase/pikabase.h"
#include "libpikacolor/pikacolor.h"
#include "libpikamath/pikamath.h"
#include "paint-types.h"
@ -47,6 +49,9 @@
#include "core/pikaimage-guides.h"
#include "core/pikaimage-symmetry.h"
#include "core/pikaimage-undo.h"
#include "core/pikaimage-undo-push.h"
#include "core/pikalayer.h"
#include "core/pikalayermask.h"
#include "core/pikapickable.h"
#include "core/pikaprojection.h"
#include "core/pikasymmetry.h"
@ -158,7 +163,10 @@ static void
pika_paint_core_init (PikaPaintCore *core)
{
core->ID = global_core_ID++;
core->undo_buffers = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref);
core->undo_buffers = g_hash_table_new_full (g_direct_hash, g_direct_equal,
NULL, g_object_unref);
core->original_bounds = g_hash_table_new_full (g_direct_hash, g_direct_equal,
NULL, g_free);
}
static void
@ -170,6 +178,7 @@ pika_paint_core_finalize (GObject *object)
g_clear_pointer (&core->undo_desc, g_free);
g_hash_table_unref (core->undo_buffers);
g_hash_table_unref (core->original_bounds);
if (core->applicators)
g_hash_table_unref (core->applicators);
@ -357,10 +366,11 @@ pika_paint_core_start (PikaPaintCore *core,
const PikaCoords *coords,
GError **error)
{
PikaImage *image;
PikaChannel *mask;
gint max_width = 0;
gint max_height = 0;
PikaImage *image;
PikaChannel *mask;
gint max_width = 0;
gint max_height = 0;
GeglRectangle *rect;
g_return_val_if_fail (PIKA_IS_PAINT_CORE (core), FALSE);
g_return_val_if_fail (g_list_length (drawables) > 0, FALSE);
@ -418,8 +428,16 @@ pika_paint_core_start (PikaPaintCore *core,
for (GList *iter = drawables; iter; iter = iter->next)
{
/* Allocate the undo structures */
rect = g_new (GeglRectangle, 1);
rect->width = pika_item_get_width (PIKA_ITEM (iter->data));
rect->height = pika_item_get_height (PIKA_ITEM (iter->data));
pika_item_get_offset (PIKA_ITEM (iter->data), &rect->x, &rect->y);
g_hash_table_insert (core->original_bounds, iter->data,
rect);
g_hash_table_insert (core->undo_buffers, iter->data,
pika_gegl_buffer_dup (pika_drawable_get_buffer (iter->data)));
max_width = MAX (max_width, pika_item_get_width (iter->data));
max_height = MAX (max_height, pika_item_get_height (iter->data));
}
@ -484,6 +502,9 @@ pika_paint_core_start (PikaPaintCore *core,
}
}
/* initialize the lock_blink_state */
core->lock_blink_state = PIKA_PAINT_LOCK_NOT_BLINKED;
/* Freeze the drawable preview so that it isn't constantly updated. */
for (GList *iter = drawables; iter; iter = iter->next)
pika_viewable_preview_freeze (PIKA_VIEWABLE (iter->data));
@ -532,7 +553,9 @@ pika_paint_core_finish (PikaPaintCore *core,
{
GeglBuffer *undo_buffer;
GeglBuffer *buffer;
GeglBuffer *drawable_buffer;
GeglRectangle rect;
GeglRectangle old_rect;
if (! g_hash_table_steal_extended (core->undo_buffers, iter->data,
NULL, (gpointer*) &undo_buffer))
@ -542,16 +565,6 @@ pika_paint_core_finish (PikaPaintCore *core,
continue;
}
pika_rectangle_intersect (core->x1, core->y1,
core->x2 - core->x1, core->y2 - core->y1,
0, 0,
pika_item_get_width (PIKA_ITEM (iter->data)),
pika_item_get_height (PIKA_ITEM (iter->data)),
&rect.x, &rect.y, &rect.width, &rect.height);
gegl_rectangle_align_to_buffer (&rect, &rect, undo_buffer,
GEGL_RECTANGLE_ALIGNMENT_SUPERSET);
if (! undo_group_started)
{
pika_image_undo_group_start (image, PIKA_UNDO_GROUP_PAINT,
@ -559,19 +572,115 @@ pika_paint_core_finish (PikaPaintCore *core,
undo_group_started = TRUE;
}
PIKA_PAINT_CORE_GET_CLASS (core)->push_undo (core, image, NULL);
/* get new and old bounds of drawable */
old_rect = *(GeglRectangle*) g_hash_table_lookup (core->original_bounds, iter->data);
buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, rect.width, rect.height),
pika_drawable_get_format (iter->data));
pika_item_get_offset (PIKA_ITEM (iter->data), &rect.x, &rect.y);
rect.width = pika_item_get_width (PIKA_ITEM (iter->data));
rect.height = pika_item_get_height (PIKA_ITEM (iter->data));
pika_gegl_buffer_copy (undo_buffer,
&rect,
GEGL_ABYSS_NONE,
buffer,
GEGL_RECTANGLE (0, 0, 0, 0));
/* Making copy of entire buffer consumes more memory, so do that only when buffer has resized */
if (rect.x == old_rect.x &&
rect.y == old_rect.y &&
rect.width == old_rect.width &&
rect.height == old_rect.height)
{
pika_rectangle_intersect (core->x1, core->y1, core->x2 - core->x1,
core->y2 - core->y1, 0, 0,
pika_item_get_width (PIKA_ITEM (iter->data)),
pika_item_get_height (PIKA_ITEM (iter->data)),
&rect.x, &rect.y, &rect.width, &rect.height);
pika_drawable_push_undo (iter->data, NULL,
buffer, rect.x, rect.y, rect.width, rect.height);
gegl_rectangle_align_to_buffer (&rect, &rect, undo_buffer,
GEGL_RECTANGLE_ALIGNMENT_SUPERSET);
PIKA_PAINT_CORE_GET_CLASS (core)->push_undo (core, image, NULL);
buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, rect.width, rect.height),
pika_drawable_get_format (iter->data));
pika_gegl_buffer_copy (undo_buffer,
&rect,
GEGL_ABYSS_NONE,
buffer,
GEGL_RECTANGLE (0, 0, 0, 0));
pika_drawable_push_undo (iter->data, NULL,
buffer, rect.x, rect.y, rect.width, rect.height);
}
else
{
/* drawable is expanded only if drawable is layer or layer mask*/
g_return_if_fail (PIKA_IS_LAYER (iter->data) || PIKA_IS_LAYER_MASK (iter->data));
/* create a copy of original buffer from undo data */
buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0,
old_rect.width,
old_rect.height),
pika_drawable_get_format (iter->data));
pika_gegl_buffer_copy (undo_buffer,
GEGL_RECTANGLE (old_rect.x - rect.x,
old_rect.y - rect.y,
old_rect.width,
old_rect.height),
GEGL_ABYSS_NONE,
buffer,
GEGL_RECTANGLE (0, 0, 0, 0));
/* make a backup copy of drawable to restore */
drawable_buffer = g_object_ref (pika_drawable_get_buffer (PIKA_DRAWABLE (iter->data)));
if (PIKA_IS_LAYER_MASK (drawables->data) || PIKA_LAYER (drawables->data)->mask)
{
GeglBuffer *other_new;
GeglBuffer *other_old;
PikaDrawable *other_drawable;
if (PIKA_IS_LAYER_MASK (drawables->data))
other_drawable = PIKA_DRAWABLE ((PIKA_LAYER_MASK (drawables->data))->layer);
else
other_drawable = PIKA_DRAWABLE (PIKA_LAYER (drawables->data)->mask);
/* create a copy of original buffer by taking the required area */
other_old = gegl_buffer_new (GEGL_RECTANGLE (0, 0,
old_rect.width,
old_rect.height),
pika_drawable_get_format (other_drawable));
pika_gegl_buffer_copy (pika_drawable_get_buffer (other_drawable),
GEGL_RECTANGLE (old_rect.x - rect.x,
old_rect.y - rect.y,
old_rect.width,
old_rect.height),
GEGL_ABYSS_NONE,
other_old,
GEGL_RECTANGLE (0, 0, 0, 0));
/* make a backup copy of drawable to restore */
other_new = g_object_ref (pika_drawable_get_buffer (other_drawable));
pika_drawable_set_buffer_full (other_drawable, FALSE, NULL,
other_old, &old_rect,
FALSE);
pika_drawable_set_buffer_full (other_drawable, TRUE, NULL,
other_new, &rect,
FALSE);
g_object_unref (other_new);
g_object_unref (other_old);
}
/* Restore drawable to state before painting started */
pika_drawable_set_buffer_full (iter->data, FALSE, NULL,
buffer, &old_rect,
FALSE);
/* Change the drawable again but push undo this time */
pika_drawable_set_buffer_full (iter->data, TRUE, NULL,
drawable_buffer, &rect,
FALSE);
g_object_unref (drawable_buffer);
}
g_object_unref (buffer);
g_object_unref (undo_buffer);
@ -614,7 +723,8 @@ pika_paint_core_cancel (PikaPaintCore *core,
&x, &y, &width, &height))
{
GeglBuffer *undo_buffer;
GeglRectangle rect;
GeglRectangle new_rect;
GeglRectangle old_rect;
if (! g_hash_table_steal_extended (core->undo_buffers, iter->data,
NULL, (gpointer*) &undo_buffer))
@ -624,21 +734,108 @@ pika_paint_core_cancel (PikaPaintCore *core,
continue;
}
gegl_rectangle_align_to_buffer (&rect,
GEGL_RECTANGLE (x, y, width, height),
pika_drawable_get_buffer (iter->data),
GEGL_RECTANGLE_ALIGNMENT_SUPERSET);
old_rect = *(GeglRectangle*) g_hash_table_lookup (core->original_bounds, iter->data);
pika_item_get_offset (PIKA_ITEM (iter->data), &new_rect.x, &new_rect.y);
new_rect.width = pika_item_get_width (PIKA_ITEM (iter->data));
new_rect.height = pika_item_get_height (PIKA_ITEM (iter->data));
if (new_rect.x == old_rect.x &&
new_rect.y == old_rect.y &&
new_rect.width == old_rect.width &&
new_rect.height == old_rect.height)
{
GeglRectangle rect;
gegl_rectangle_align_to_buffer (&rect,
GEGL_RECTANGLE (x, y, width, height),
pika_drawable_get_buffer (iter->data),
GEGL_RECTANGLE_ALIGNMENT_SUPERSET);
pika_gegl_buffer_copy (undo_buffer,
&rect,
GEGL_ABYSS_NONE,
pika_drawable_get_buffer (iter->data),
&rect);
pika_drawable_update (iter->data, x, y, width, height);
}
else
{
GeglBuffer *buffer;
GeglRectangle bbox;
/* drawable is expanded only if drawable is layer or layer mask,
* so drawable cannot be anything else */
g_return_if_fail (PIKA_IS_LAYER (iter->data) || PIKA_IS_LAYER_MASK (iter->data));
/* When canceling painting with drawable expansion, ensure that
* the drawable display outside the reverted size is not shown. We
* cannot use pika_drawable_update() because it won't work while
* painting. Directly emit the "update" signal.
* */
bbox = pika_drawable_get_bounding_box (iter->data);
g_signal_emit_by_name (iter->data, "update", bbox.x, bbox.y, bbox.width, bbox.height);
/* create a copy of original buffer from undo data */
buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0,
old_rect.width,
old_rect.height),
pika_drawable_get_format (iter->data));
pika_gegl_buffer_copy (undo_buffer,
GEGL_RECTANGLE (old_rect.x - new_rect.x,
old_rect.y - new_rect.y,
old_rect.width,
old_rect.height),
GEGL_ABYSS_NONE,
buffer,
GEGL_RECTANGLE (0, 0, 0, 0));
if (PIKA_IS_LAYER_MASK (drawables->data) || PIKA_LAYER (drawables->data)->mask)
{
GeglBuffer *other_old;
PikaDrawable *other_drawable;
if (PIKA_IS_LAYER_MASK (drawables->data))
other_drawable = PIKA_DRAWABLE ((PIKA_LAYER_MASK (drawables->data))->layer);
else
other_drawable = PIKA_DRAWABLE (PIKA_LAYER (drawables->data)->mask);
/* create a copy of original buffer by taking the required area */
other_old = gegl_buffer_new (GEGL_RECTANGLE (0, 0,
old_rect.width,
old_rect.height),
pika_drawable_get_format (other_drawable));
pika_gegl_buffer_copy (pika_drawable_get_buffer (other_drawable),
GEGL_RECTANGLE (old_rect.x - new_rect.x,
old_rect.y - new_rect.y,
old_rect.width,
old_rect.height),
GEGL_ABYSS_NONE,
other_old,
GEGL_RECTANGLE (0, 0, 0, 0));
pika_drawable_set_buffer_full (other_drawable, FALSE, NULL,
other_old, &old_rect,
FALSE);
g_object_unref (other_old);
}
/* Restore drawable to state before painting started */
pika_drawable_set_buffer_full (iter->data, FALSE, NULL,
buffer, &old_rect,
FALSE);
pika_drawable_update (iter->data, 0, 0, -1, -1);
g_object_unref (buffer);
}
pika_gegl_buffer_copy (undo_buffer,
&rect,
GEGL_ABYSS_NONE,
pika_drawable_get_buffer (iter->data),
&rect);
g_object_unref (undo_buffer);
}
pika_drawable_update (iter->data, x, y, width, height);
pika_viewable_preview_thaw (PIKA_VIEWABLE (iter->data));
}
@ -651,6 +848,7 @@ pika_paint_core_cleanup (PikaPaintCore *core)
g_return_if_fail (PIKA_IS_PAINT_CORE (core));
g_hash_table_remove_all (core->undo_buffers);
g_hash_table_remove_all (core->original_bounds);
g_clear_object (&core->saved_proj_buffer);
g_clear_object (&core->canvas_buffer);
@ -692,6 +890,227 @@ pika_paint_core_get_show_all (PikaPaintCore *core)
return core->show_all;
}
gboolean
pika_paint_core_expand_drawable (PikaPaintCore *core,
PikaDrawable *drawable,
PikaPaintOptions *options,
gint x1,
gint x2,
gint y1,
gint y2,
gint *new_off_x,
gint *new_off_y)
{
gint drawable_width, drawable_height;
gint drawable_offset_x, drawable_offset_y;
gint image_width, image_height;
gint new_width, new_height;
gint expand_amount;
gboolean show_all;
gboolean outside_image;
PikaImage *image = pika_item_get_image (PIKA_ITEM (drawable));
PikaLayer *layer;
drawable_width = pika_item_get_width (PIKA_ITEM (drawable));
drawable_height = pika_item_get_height (PIKA_ITEM (drawable));
pika_item_get_offset (PIKA_ITEM (drawable), &drawable_offset_x, &drawable_offset_y);
new_width = drawable_width;
new_height = drawable_height;
*new_off_x = 0;
*new_off_y = 0;
image_width = pika_image_get_width (image);
image_height = pika_image_get_height (image);
expand_amount = options->expand_amount;
show_all = pika_paint_core_get_show_all (core);
outside_image = x2 < -drawable_offset_x || x1 > image_width - drawable_offset_x ||
y2 < -drawable_offset_y || y1 > image_height - drawable_offset_y;
/* Don't expand if drawable is anything other
* than layer or layer mask */
if (PIKA_IS_LAYER_MASK (drawable))
layer = (PIKA_LAYER_MASK (drawable))->layer;
else if (PIKA_IS_LAYER (drawable))
layer = PIKA_LAYER (drawable);
else
return FALSE;
if (!pika_paint_core_get_show_all (core) && outside_image)
return FALSE;
if (!options->expand_use)
return FALSE;
if (x1 < 0)
{
if (show_all)
{
new_width += expand_amount - x1;
*new_off_x += expand_amount - x1;
}
else if (drawable_offset_x > 0)
{
new_width += expand_amount - x1;
*new_off_x += expand_amount - x1;
if (*new_off_x > drawable_offset_x)
{
new_width -= *new_off_x - drawable_offset_x;
*new_off_x = drawable_offset_x;
}
}
}
if (y1 < 0)
{
if (show_all)
{
new_height += expand_amount - y1;
*new_off_y += expand_amount - y1;
}
else if (drawable_offset_y > 0)
{
new_height += expand_amount - y1;
*new_off_y += expand_amount - y1;
if (*new_off_y > drawable_offset_y)
{
new_height -= *new_off_y - drawable_offset_y;
*new_off_y = drawable_offset_y;
}
}
}
if (x2 > drawable_width)
{
if (show_all)
{
new_width += x2 - drawable_width + expand_amount;
}
else if (drawable_width + drawable_offset_x < image_width)
{
new_width += x2 - drawable_width + expand_amount;
if (new_width + drawable_offset_x - *new_off_x > image_width)
new_width = image_width + *new_off_x - drawable_offset_x;
}
}
if (y2 > drawable_height)
{
if (show_all)
{
new_height += y2 - drawable_height + expand_amount;
}
else if (drawable_height + drawable_offset_y < image_height)
{
new_height += y2 - drawable_height + expand_amount;
if (new_height + drawable_offset_y - *new_off_y > image_height)
new_height = image_height + *new_off_y - drawable_offset_y;
}
}
if (new_width != drawable_width || *new_off_x ||
new_height != drawable_height || *new_off_y)
{
PikaRGB color;
PikaPattern *pattern;
PikaContext *context = PIKA_CONTEXT (options);
PikaFillType fill_type = options->expand_fill_type;
gboolean context_has_image;
PikaFillType mask_fill_type;
GeglBuffer *undo_buffer;
GeglBuffer *new_buffer;
if (pika_item_get_lock_position (PIKA_ITEM (layer)))
{
if (core->lock_blink_state == PIKA_PAINT_LOCK_NOT_BLINKED)
core->lock_blink_state = PIKA_PAINT_LOCK_BLINK_PENDING;
/* Since we are not expanding, set new offset to zero */
*new_off_x = 0;
*new_off_y = 0;
return FALSE;
}
mask_fill_type = options->expand_mask_fill_type == PIKA_ADD_MASK_BLACK ?
PIKA_FILL_TRANSPARENT :
PIKA_FILL_WHITE;
/* The image field of context is null but
* is required for Filling with Middle Gray */
context_has_image = context->image != NULL;
if (!context_has_image)
context->image = image;
/* ensure that every expansion is pushed to undo stack */
if (core->x2 == core->x1)
core->x2++;
if (core->y2 == core->y1)
core->y2++;
g_object_freeze_notify (G_OBJECT (layer));
pika_drawable_disable_resize_undo (PIKA_DRAWABLE (layer));
PIKA_LAYER_GET_CLASS (layer)->resize (layer, context, fill_type,
new_width, new_height,
*new_off_x, *new_off_y);
pika_drawable_enable_resize_undo (PIKA_DRAWABLE (layer));
if (layer->mask)
{
g_object_freeze_notify (G_OBJECT (layer->mask));
pika_drawable_disable_resize_undo (PIKA_DRAWABLE (layer->mask));
PIKA_ITEM_GET_CLASS (layer->mask)->resize (PIKA_ITEM (layer->mask), context,
mask_fill_type, new_width, new_height,
*new_off_x, *new_off_y);
pika_drawable_enable_resize_undo (PIKA_DRAWABLE (layer->mask));
g_object_thaw_notify (G_OBJECT (layer->mask));
}
g_object_thaw_notify (G_OBJECT (layer));
pika_image_flush (image);
if (PIKA_IS_LAYER_MASK (drawable))
{
fill_type = mask_fill_type;
}
else if (fill_type == PIKA_FILL_TRANSPARENT &&
! pika_drawable_has_alpha (drawable))
{
fill_type = PIKA_FILL_BACKGROUND;
}
new_buffer = pika_gegl_buffer_resize (core->canvas_buffer, new_width, new_height,
-(*new_off_x), -(*new_off_y), NULL, NULL, 0, 0);
g_object_unref (core->canvas_buffer);
core->canvas_buffer = new_buffer;
pika_get_fill_params (context, fill_type, &color, &pattern, NULL);
pika_pickable_srgb_to_image_color (PIKA_PICKABLE (drawable),
&color, &color);
if (! pika_drawable_has_alpha (drawable))
pika_rgb_set_alpha (&color, 1.0);
undo_buffer = g_hash_table_lookup (core->undo_buffers, drawable);
g_object_ref (undo_buffer);
new_buffer = pika_gegl_buffer_resize (undo_buffer, new_width, new_height,
-(*new_off_x), -(*new_off_y), &color,
pattern, 0, 0);
g_hash_table_insert (core->undo_buffers, drawable, new_buffer);
g_object_unref (undo_buffer);
/* Restore context to its original state */
if (!context_has_image)
context->image = NULL;
return TRUE;
}
return FALSE;
}
void
pika_paint_core_set_current_coords (PikaPaintCore *core,
const PikaCoords *coords)

View File

@ -69,12 +69,17 @@ struct _PikaPaintCore
GeglBuffer *paint_buffer; /* the buffer to paint pixels to */
gint paint_buffer_x;
gint paint_buffer_y;
GHashTable *original_bounds; /* the original bounds of drawables */
GeglBuffer *mask_buffer; /* the target drawable's mask */
GHashTable *applicators;
GArray *stroke_buffer;
PikaSymmetry *sym;
PikaPaintLockBlinkState
lock_blink_state;
};
struct _PikaPaintCoreClass
@ -156,6 +161,16 @@ void pika_paint_core_set_show_all (PikaPaintCore *core,
gboolean show_all);
gboolean pika_paint_core_get_show_all (PikaPaintCore *core);
gboolean pika_paint_core_expand_drawable (PikaPaintCore *paint_core,
PikaDrawable *drawable,
PikaPaintOptions *paint_options,
gint x1,
gint x2,
gint y1,
gint y2,
gint *x,
gint *y);
void pika_paint_core_set_current_coords (PikaPaintCore *core,
const PikaCoords *coords);
void pika_paint_core_get_current_coords (PikaPaintCore *core,

View File

@ -83,6 +83,11 @@
#define DEFAULT_SMOOTHING_QUALITY 20
#define DEFAULT_SMOOTHING_FACTOR 50
#define DEFAULT_EXPAND_USE FALSE
#define DEFAULT_EXPAND_AMOUNT 100.0
#define DEFAULT_EXPAND_FILL_TYPE PIKA_FILL_TRANSPARENT
#define DEFAULT_EXPAND_MASK_FILL_TYPE PIKA_ADD_MASK_WHITE
enum
{
PROP_0,
@ -134,7 +139,12 @@ enum
PROP_USE_SMOOTHING,
PROP_SMOOTHING_QUALITY,
PROP_SMOOTHING_FACTOR
PROP_SMOOTHING_FACTOR,
PROP_EXPAND_USE,
PROP_EXPAND_AMOUNT,
PROP_EXPAND_FILL_TYPE,
PROP_EXPAND_MASK_FILL_TYPE
};
@ -308,6 +318,36 @@ pika_paint_options_class_init (PikaPaintOptionsClass *klass)
DEFAULT_USE_JITTER,
PIKA_PARAM_STATIC_STRINGS);
PIKA_CONFIG_PROP_BOOLEAN (object_class, PROP_EXPAND_USE,
"expand-use",
_("Expand Layers"),
_("Expand active layer as you paint"),
DEFAULT_EXPAND_USE,
PIKA_PARAM_STATIC_STRINGS);
PIKA_CONFIG_PROP_DOUBLE (object_class, PROP_EXPAND_AMOUNT,
"expand-amount",
_("Amount"),
_("Amount of expansion"),
1.0, 1000.0, DEFAULT_EXPAND_AMOUNT,
PIKA_PARAM_STATIC_STRINGS);
PIKA_CONFIG_PROP_ENUM (object_class, PROP_EXPAND_FILL_TYPE,
"expand-fill-type",
_("Fill With"),
_("Fill layer with"),
PIKA_TYPE_FILL_TYPE,
DEFAULT_EXPAND_FILL_TYPE,
PIKA_PARAM_STATIC_STRINGS);
PIKA_CONFIG_PROP_ENUM (object_class, PROP_EXPAND_MASK_FILL_TYPE,
"expand-mask-fill-type",
_("Fill Mask With"),
_("Fill layer mask with"),
PIKA_TYPE_ADD_MASK_TYPE,
DEFAULT_EXPAND_MASK_FILL_TYPE,
PIKA_PARAM_STATIC_STRINGS);
PIKA_CONFIG_PROP_BOOLEAN (object_class, PROP_DYNAMICS_ENABLED,
"dynamics-enabled",
_("Enable dynamics"),
@ -642,6 +682,19 @@ pika_paint_options_set_property (GObject *object,
smoothing_options->smoothing_factor = g_value_get_double (value);
break;
case PROP_EXPAND_USE:
options->expand_use = g_value_get_boolean (value);
break;
case PROP_EXPAND_AMOUNT:
options->expand_amount = g_value_get_double (value);
break;
case PROP_EXPAND_FILL_TYPE:
options->expand_fill_type = g_value_get_enum (value);
break;
case PROP_EXPAND_MASK_FILL_TYPE:
options->expand_mask_fill_type = g_value_get_enum (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
@ -788,6 +841,19 @@ pika_paint_options_get_property (GObject *object,
g_value_set_double (value, smoothing_options->smoothing_factor);
break;
case PROP_EXPAND_USE:
g_value_set_boolean (value, options->expand_use);
break;
case PROP_EXPAND_AMOUNT:
g_value_set_double (value, options->expand_amount);
break;
case PROP_EXPAND_FILL_TYPE:
g_value_set_enum (value, options->expand_fill_type);
break;
case PROP_EXPAND_MASK_FILL_TYPE:
g_value_set_enum (value, options->expand_mask_fill_type);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
@ -1221,9 +1287,18 @@ static const gchar *gradient_props[] =
"gradient-repeat"
};
static const gchar *expand_props[] =
{
"expand-use",
"expand-amount",
"expand-fill-type",
"expand-mask-fill-type",
};
static const gint max_n_props = (G_N_ELEMENTS (brush_props) +
G_N_ELEMENTS (dynamics_props) +
G_N_ELEMENTS (gradient_props));
G_N_ELEMENTS (gradient_props) +
G_N_ELEMENTS (expand_props));
gboolean
pika_paint_options_is_prop (const gchar *prop_name,
@ -1254,6 +1329,13 @@ pika_paint_options_is_prop (const gchar *prop_name,
return TRUE;
}
if (prop_mask & PIKA_CONTEXT_PROP_MASK_EXPAND)
{
for (i = 0; i < G_N_ELEMENTS (expand_props); i++)
if (! strcmp (prop_name, expand_props[i]))
return TRUE;
}
return FALSE;
}
@ -1288,6 +1370,12 @@ pika_paint_options_copy_props (PikaPaintOptions *src,
names[n_props++] = gradient_props[i];
}
if (prop_mask & PIKA_CONTEXT_PROP_MASK_EXPAND)
{
for (i = 0; i < G_N_ELEMENTS (expand_props); i++)
names[n_props++] = expand_props[i];
}
if (n_props > 0)
{
g_object_getv (G_OBJECT (src), n_props, names, values);

View File

@ -109,6 +109,11 @@ struct _PikaPaintOptions
gboolean hard;
gboolean expand_use;
gdouble expand_amount;
PikaFillType expand_fill_type;
PikaAddMaskType expand_mask_fill_type;
PikaJitterOptions *jitter_options;
gboolean dynamics_enabled;

View File

@ -406,6 +406,7 @@ pika_smudge_motion (PikaPaintCore *paint_core,
coords.x -= off_x;
coords.y -= off_y;
pika_symmetry_set_origin (sym, drawable, &coords);
paint_core->sym = sym;
opacity = pika_dynamics_get_linear_value (dynamics,
PIKA_DYNAMICS_OUTPUT_OPACITY,

View File

@ -347,6 +347,7 @@ pika_source_core_motion (PikaSourceCore *source_core,
coords.x -= off_x;
coords.y -= off_y;
pika_symmetry_set_origin (sym, drawable, &coords);
paint_core->sym = sym;
/* Some settings are based on the original stroke. */
opacity = pika_dynamics_get_linear_value (dynamics,

View File

@ -34,6 +34,8 @@
#include "pdb-types.h"
#include "core/pika.h"
#include "core/pikabrush.h"
#include "core/pikacontainer.h"
#include "core/pikadatafactory.h"
#include "core/pikaparamspecs.h"
@ -53,20 +55,24 @@ brushes_popup_invoker (PikaProcedure *procedure,
gboolean success = TRUE;
const gchar *brush_callback;
const gchar *popup_title;
const gchar *initial_brush_name;
PikaBrush *initial_brush;
GBytes *parent_window;
brush_callback = g_value_get_string (pika_value_array_index (args, 0));
popup_title = g_value_get_string (pika_value_array_index (args, 1));
initial_brush_name = g_value_get_string (pika_value_array_index (args, 2));
initial_brush = g_value_get_object (pika_value_array_index (args, 2));
parent_window = g_value_get_boxed (pika_value_array_index (args, 3));
if (success)
{
if (pika->no_interface ||
PikaContainer *container = pika_data_factory_get_container (pika->brush_factory);
if (pika->no_interface ||
! pika_pdb_lookup_procedure (pika->pdb, brush_callback) ||
! pika_pdb_dialog_new (pika, context, progress,
pika_data_factory_get_container (pika->brush_factory),
popup_title, brush_callback, initial_brush_name,
NULL))
pika_container_get_children_type (container),
parent_window, popup_title, brush_callback,
PIKA_OBJECT (initial_brush), NULL))
success = FALSE;
}
@ -89,9 +95,12 @@ brushes_close_popup_invoker (PikaProcedure *procedure,
if (success)
{
PikaContainer *container = pika_data_factory_get_container (pika->brush_factory);
if (pika->no_interface ||
! pika_pdb_lookup_procedure (pika->pdb, brush_callback) ||
! pika_pdb_dialog_close (pika, pika_data_factory_get_container (pika->brush_factory),
! pika_pdb_dialog_close (pika,
pika_container_get_children_type (container),
brush_callback))
success = FALSE;
}
@ -110,18 +119,20 @@ brushes_set_popup_invoker (PikaProcedure *procedure,
{
gboolean success = TRUE;
const gchar *brush_callback;
const gchar *brush_name;
PikaBrush *brush;
brush_callback = g_value_get_string (pika_value_array_index (args, 0));
brush_name = g_value_get_string (pika_value_array_index (args, 1));
brush = g_value_get_object (pika_value_array_index (args, 1));
if (success)
{
PikaContainer *container = pika_data_factory_get_container (pika->brush_factory);
if (pika->no_interface ||
! pika_pdb_lookup_procedure (pika->pdb, brush_callback) ||
! pika_pdb_dialog_set (pika, pika_data_factory_get_container (pika->brush_factory),
brush_callback, brush_name,
NULL))
! pika_pdb_dialog_set (pika,
pika_container_get_children_type (container),
brush_callback, PIKA_OBJECT (brush), NULL))
success = FALSE;
}
@ -163,12 +174,17 @@ register_brush_select_procs (PikaPDB *pdb)
NULL,
PIKA_PARAM_READWRITE));
pika_procedure_add_argument (procedure,
pika_param_spec_string ("initial-brush-name",
"initial brush name",
"The name of the brush to set as the initial choice",
FALSE, TRUE, FALSE,
NULL,
PIKA_PARAM_READWRITE));
pika_param_spec_brush ("initial-brush",
"initial brush",
"The brush to set as the initial choice",
FALSE,
PIKA_PARAM_READWRITE));
pika_procedure_add_argument (procedure,
g_param_spec_boxed ("parent-window",
"parent window",
"An optional parent window handle for the popup to be set transient to",
G_TYPE_BYTES,
PIKA_PARAM_READWRITE));
pika_pdb_register_procedure (pdb, procedure);
g_object_unref (procedure);
@ -218,12 +234,11 @@ register_brush_select_procs (PikaPDB *pdb)
NULL,
PIKA_PARAM_READWRITE));
pika_procedure_add_argument (procedure,
pika_param_spec_string ("brush-name",
"brush name",
"The name of the brush to set as selected",
FALSE, FALSE, FALSE,
NULL,
PIKA_PARAM_READWRITE));
pika_param_spec_brush ("brush",
"brush",
"The brush to set as selected",
FALSE,
PIKA_PARAM_READWRITE));
pika_pdb_register_procedure (pdb, procedure);
g_object_unref (procedure);
}

View File

@ -148,20 +148,20 @@ display_get_window_handle_invoker (PikaProcedure *procedure,
gboolean success = TRUE;
PikaValueArray *return_vals;
PikaDisplay *display;
gint window = 0;
GBytes *handle = NULL;
display = g_value_get_object (pika_value_array_index (args, 0));
if (success)
{
window = (gint32) pika_get_display_window_id (pika, display);
handle = pika_get_display_window_id (pika, display);
}
return_vals = pika_procedure_get_return_values (procedure, success,
error ? *error : NULL);
if (success)
g_value_set_int (pika_value_array_index (return_vals, 1), window);
g_value_take_boxed (pika_value_array_index (return_vals, 1), handle);
return return_vals;
}
@ -330,7 +330,8 @@ register_display_procs (PikaPDB *pdb)
"pika-display-get-window-handle");
pika_procedure_set_static_help (procedure,
"Get a handle to the native window for an image display.",
"This procedure returns a handle to the native window for a given image display. For example in the X backend of GDK, a native window handle is an Xlib XID. A value of 0 is returned for an invalid display or if this function is unimplemented for the windowing system that is being used.",
"This procedure returns a handle to the native window for a given image display.\n"
"It can be different types of data depending on the platform you are running on. For example in the X backend of GDK, a native window handle is an Xlib XID whereas on Wayland, it is a string handle. A value of NULL is returned for an invalid display or if this function is unimplemented for the windowing system that is being used.",
NULL);
pika_procedure_set_static_attribution (procedure,
"Sven Neumann <sven@gimp.org>",
@ -343,11 +344,11 @@ register_display_procs (PikaPDB *pdb)
FALSE,
PIKA_PARAM_READWRITE));
pika_procedure_add_return_value (procedure,
g_param_spec_int ("window",
"window",
"The native window handle or 0",
G_MININT32, G_MAXINT32, 0,
PIKA_PARAM_READWRITE));
g_param_spec_boxed ("handle",
"handle",
"The native window handle or NULL",
G_TYPE_BYTES,
PIKA_PARAM_READWRITE));
pika_pdb_register_procedure (pdb, procedure);
g_object_unref (procedure);

View File

@ -0,0 +1,243 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995-2003 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 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/>.
*/
/* NOTE: This file is auto-generated by pdbgen.pl. */
#include "config.h"
#include "stamp-pdbgen.h"
#include <gegl.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include "libpikabase/pikabase.h"
#include "pdb-types.h"
#include "core/pika.h"
#include "core/pikadatafactory.h"
#include "core/pikadrawable.h"
#include "core/pikaparamspecs.h"
#include "pikapdb.h"
#include "pikaprocedure.h"
#include "internal-procs.h"
static PikaValueArray *
drawables_popup_invoker (PikaProcedure *procedure,
Pika *pika,
PikaContext *context,
PikaProgress *progress,
const PikaValueArray *args,
GError **error)
{
gboolean success = TRUE;
const gchar *callback;
const gchar *popup_title;
const gchar *drawable_type;
PikaDrawable *initial_drawable;
GBytes *parent_window;
callback = g_value_get_string (pika_value_array_index (args, 0));
popup_title = g_value_get_string (pika_value_array_index (args, 1));
drawable_type = g_value_get_string (pika_value_array_index (args, 2));
initial_drawable = g_value_get_object (pika_value_array_index (args, 3));
parent_window = g_value_get_boxed (pika_value_array_index (args, 4));
if (success)
{
if (pika->no_interface ||
! pika_pdb_lookup_procedure (pika->pdb, callback) ||
! pika_pdb_dialog_new (pika, context, progress,
g_type_from_name (drawable_type),
parent_window, popup_title, callback,
PIKA_OBJECT (initial_drawable),
NULL))
success = FALSE;
}
return pika_procedure_get_return_values (procedure, success,
error ? *error : NULL);
}
static PikaValueArray *
drawables_close_popup_invoker (PikaProcedure *procedure,
Pika *pika,
PikaContext *context,
PikaProgress *progress,
const PikaValueArray *args,
GError **error)
{
gboolean success = TRUE;
const gchar *callback;
callback = g_value_get_string (pika_value_array_index (args, 0));
if (success)
{
if (pika->no_interface ||
! pika_pdb_lookup_procedure (pika->pdb, callback) ||
! pika_pdb_dialog_close (pika, PIKA_TYPE_DRAWABLE, callback))
success = FALSE;
}
return pika_procedure_get_return_values (procedure, success,
error ? *error : NULL);
}
static PikaValueArray *
drawables_set_popup_invoker (PikaProcedure *procedure,
Pika *pika,
PikaContext *context,
PikaProgress *progress,
const PikaValueArray *args,
GError **error)
{
gboolean success = TRUE;
const gchar *callback;
PikaDrawable *drawable;
callback = g_value_get_string (pika_value_array_index (args, 0));
drawable = g_value_get_object (pika_value_array_index (args, 1));
if (success)
{
if (pika->no_interface ||
! pika_pdb_lookup_procedure (pika->pdb, callback) ||
! pika_pdb_dialog_set (pika, PIKA_TYPE_DRAWABLE, callback, PIKA_OBJECT (drawable), NULL))
success = FALSE;
}
return pika_procedure_get_return_values (procedure, success,
error ? *error : NULL);
}
void
register_drawable_select_procs (PikaPDB *pdb)
{
PikaProcedure *procedure;
/*
* pika-drawables-popup
*/
procedure = pika_procedure_new (drawables_popup_invoker);
pika_object_set_static_name (PIKA_OBJECT (procedure),
"pika-drawables-popup");
pika_procedure_set_static_help (procedure,
"Invokes the drawable selection dialog.",
"Opens a dialog letting a user choose an drawable.",
NULL);
pika_procedure_set_static_attribution (procedure,
"Jehan",
"Jehan",
"2023");
pika_procedure_add_argument (procedure,
pika_param_spec_string ("callback",
"callback",
"The callback PDB proc to call when user chooses an drawable",
FALSE, FALSE, TRUE,
NULL,
PIKA_PARAM_READWRITE));
pika_procedure_add_argument (procedure,
pika_param_spec_string ("popup-title",
"popup title",
"Title of the drawable selection dialog",
FALSE, FALSE, FALSE,
NULL,
PIKA_PARAM_READWRITE));
pika_procedure_add_argument (procedure,
pika_param_spec_string ("drawable-type",
"drawable type",
"The name of the PIKA_TYPE_DRAWABLE subtype",
FALSE, FALSE, TRUE,
NULL,
PIKA_PARAM_READWRITE));
pika_procedure_add_argument (procedure,
pika_param_spec_drawable ("initial-drawable",
"initial drawable",
"The drawable to set as the initial choice",
FALSE,
PIKA_PARAM_READWRITE | PIKA_PARAM_NO_VALIDATE));
pika_procedure_add_argument (procedure,
g_param_spec_boxed ("parent-window",
"parent window",
"An optional parent window handle for the popup to be set transient to",
G_TYPE_BYTES,
PIKA_PARAM_READWRITE));
pika_pdb_register_procedure (pdb, procedure);
g_object_unref (procedure);
/*
* pika-drawables-close-popup
*/
procedure = pika_procedure_new (drawables_close_popup_invoker);
pika_object_set_static_name (PIKA_OBJECT (procedure),
"pika-drawables-close-popup");
pika_procedure_set_static_help (procedure,
"Close the drawable selection dialog.",
"Closes an open drawable selection dialog.",
NULL);
pika_procedure_set_static_attribution (procedure,
"Jehan",
"Jehan",
"2023");
pika_procedure_add_argument (procedure,
pika_param_spec_string ("callback",
"callback",
"The name of the callback registered for this pop-up",
FALSE, FALSE, TRUE,
NULL,
PIKA_PARAM_READWRITE));
pika_pdb_register_procedure (pdb, procedure);
g_object_unref (procedure);
/*
* pika-drawables-set-popup
*/
procedure = pika_procedure_new (drawables_set_popup_invoker);
pika_object_set_static_name (PIKA_OBJECT (procedure),
"pika-drawables-set-popup");
pika_procedure_set_static_help (procedure,
"Sets the selected drawable in a drawable selection dialog.",
"Sets the selected drawable in a drawable selection dialog.",
NULL);
pika_procedure_set_static_attribution (procedure,
"Jehan",
"Jehan",
"2023");
pika_procedure_add_argument (procedure,
pika_param_spec_string ("callback",
"callback",
"The name of the callback registered for this pop-up",
FALSE, FALSE, TRUE,
NULL,
PIKA_PARAM_READWRITE));
pika_procedure_add_argument (procedure,
pika_param_spec_drawable ("drawable",
"drawable",
"The drawable to set as selected",
FALSE,
PIKA_PARAM_READWRITE | PIKA_PARAM_NO_VALIDATE));
pika_pdb_register_procedure (pdb, procedure);
g_object_unref (procedure);
}

View File

@ -75,6 +75,57 @@ font_get_by_name_invoker (PikaProcedure *procedure,
return return_vals;
}
static PikaValueArray *
fonts_get_by_name_invoker (PikaProcedure *procedure,
Pika *pika,
PikaContext *context,
PikaProgress *progress,
const PikaValueArray *args,
GError **error)
{
gboolean success = TRUE;
PikaValueArray *return_vals;
const gchar *name;
gint num_fonts = 0;
PikaFont **fonts = NULL;
name = g_value_get_string (pika_value_array_index (args, 0));
if (success)
{
GList *list;
list = pika_pdb_get_resources (pika, PIKA_TYPE_FONT, name, PIKA_PDB_DATA_ACCESS_READ, error);
if (list == NULL)
success = FALSE;
num_fonts = g_list_length (list);
if (num_fonts > 0)
{
gint i = 0;
fonts = g_new (PikaFont *, num_fonts);
for (GList *iter = list; i < num_fonts; i++, iter = g_list_next (iter))
fonts[i] = g_object_ref (iter->data);
}
g_list_free (list);
}
return_vals = pika_procedure_get_return_values (procedure, success,
error ? *error : NULL);
if (success)
{
g_value_set_int (pika_value_array_index (return_vals, 1), num_fonts);
pika_value_take_object_array (pika_value_array_index (return_vals, 2), PIKA_TYPE_FONT, (GObject **) fonts, num_fonts);
}
return return_vals;
}
void
register_font_procs (PikaPDB *pdb)
{
@ -87,8 +138,9 @@ register_font_procs (PikaPDB *pdb)
pika_object_set_static_name (PIKA_OBJECT (procedure),
"pika-font-get-by-name");
pika_procedure_set_static_help (procedure,
"Returns the font with the given name.",
"Returns the font with the given name.",
"Returns a font with the given name.",
"If several fonts are named identically, the one which is returned by this function should be considered random. This can be used when you know you won't have multiple fonts of this name or that you don't want to choose (non-interactive scripts, etc.).\n"
"If you need more control, you should use 'pika-fonts-get-by-name' instead.",
NULL);
pika_procedure_set_static_attribution (procedure,
"Michael Natterer <mitch@gimp.org>",
@ -109,4 +161,40 @@ register_font_procs (PikaPDB *pdb)
PIKA_PARAM_READWRITE));
pika_pdb_register_procedure (pdb, procedure);
g_object_unref (procedure);
/*
* pika-fonts-get-by-name
*/
procedure = pika_procedure_new (fonts_get_by_name_invoker);
pika_object_set_static_name (PIKA_OBJECT (procedure),
"pika-fonts-get-by-name");
pika_procedure_set_static_help (procedure,
"Returns the fonts with the given name.",
"Returns the fonts with the given name. There may be more than one.",
NULL);
pika_procedure_set_static_attribution (procedure,
"Jehan",
"Jehan",
"2023");
pika_procedure_add_argument (procedure,
pika_param_spec_string ("name",
"name",
"The name of the font",
FALSE, FALSE, TRUE,
NULL,
PIKA_PARAM_READWRITE));
pika_procedure_add_return_value (procedure,
g_param_spec_int ("num-fonts",
"num fonts",
"The number of fonts with the given name",
0, G_MAXINT32, 0,
PIKA_PARAM_READWRITE));
pika_procedure_add_return_value (procedure,
pika_param_spec_object_array ("fonts",
"fonts",
"The fonts with the given name",
PIKA_TYPE_FONT,
PIKA_PARAM_READWRITE));
pika_pdb_register_procedure (pdb, procedure);
g_object_unref (procedure);
}

View File

@ -34,8 +34,10 @@
#include "pdb-types.h"
#include "core/pika.h"
#include "core/pikacontainer.h"
#include "core/pikadatafactory.h"
#include "core/pikaparamspecs.h"
#include "text/pikafont.h"
#include "pikapdb.h"
#include "pikaprocedure.h"
@ -53,21 +55,25 @@ fonts_popup_invoker (PikaProcedure *procedure,
gboolean success = TRUE;
const gchar *font_callback;
const gchar *popup_title;
const gchar *initial_font_name;
PikaFont *initial_font;
GBytes *parent_window;
font_callback = g_value_get_string (pika_value_array_index (args, 0));
popup_title = g_value_get_string (pika_value_array_index (args, 1));
initial_font_name = g_value_get_string (pika_value_array_index (args, 2));
initial_font = g_value_get_object (pika_value_array_index (args, 2));
parent_window = g_value_get_boxed (pika_value_array_index (args, 3));
if (success)
{
PikaContainer *container = pika_data_factory_get_container (pika->font_factory);
if (pika->no_interface ||
! pika_pdb_lookup_procedure (pika->pdb, font_callback) ||
! pika_data_factory_data_wait (pika->font_factory) ||
! pika_pdb_dialog_new (pika, context, progress,
pika_data_factory_get_container (pika->font_factory),
popup_title, font_callback, initial_font_name,
NULL))
pika_container_get_children_type (container),
parent_window, popup_title, font_callback,
PIKA_OBJECT (initial_font), NULL))
success = FALSE;
}
@ -90,10 +96,12 @@ fonts_close_popup_invoker (PikaProcedure *procedure,
if (success)
{
PikaContainer *container = pika_data_factory_get_container (pika->font_factory);
if (pika->no_interface ||
! pika_pdb_lookup_procedure (pika->pdb, font_callback) ||
! pika_pdb_dialog_close (pika,
pika_data_factory_get_container (pika->font_factory),
pika_container_get_children_type (container),
font_callback))
success = FALSE;
}
@ -112,20 +120,21 @@ fonts_set_popup_invoker (PikaProcedure *procedure,
{
gboolean success = TRUE;
const gchar *font_callback;
const gchar *font_name;
PikaFont *font;
font_callback = g_value_get_string (pika_value_array_index (args, 0));
font_name = g_value_get_string (pika_value_array_index (args, 1));
font = g_value_get_object (pika_value_array_index (args, 1));
if (success)
{
PikaContainer *container = pika_data_factory_get_container (pika->font_factory);
if (pika->no_interface ||
! pika_pdb_lookup_procedure (pika->pdb, font_callback) ||
! pika_data_factory_data_wait (pika->font_factory) ||
! pika_pdb_dialog_set (pika,
pika_data_factory_get_container (pika->font_factory),
font_callback, font_name,
NULL))
pika_container_get_children_type (container),
font_callback, PIKA_OBJECT (font), NULL))
success = FALSE;
}
@ -167,12 +176,17 @@ register_font_select_procs (PikaPDB *pdb)
NULL,
PIKA_PARAM_READWRITE));
pika_procedure_add_argument (procedure,
pika_param_spec_string ("initial-font-name",
"initial font name",
"The name of the initial font choice.",
FALSE, TRUE, FALSE,
NULL,
PIKA_PARAM_READWRITE));
pika_param_spec_font ("initial-font",
"initial font",
"The name of the initial font choice.",
FALSE,
PIKA_PARAM_READWRITE));
pika_procedure_add_argument (procedure,
g_param_spec_boxed ("parent-window",
"parent window",
"An optional parent window handle for the popup to be set transient to",
G_TYPE_BYTES,
PIKA_PARAM_READWRITE));
pika_pdb_register_procedure (pdb, procedure);
g_object_unref (procedure);
@ -222,12 +236,11 @@ register_font_select_procs (PikaPDB *pdb)
NULL,
PIKA_PARAM_READWRITE));
pika_procedure_add_argument (procedure,
pika_param_spec_string ("font-name",
"font name",
"The name of the font to set as selected",
FALSE, FALSE, FALSE,
NULL,
PIKA_PARAM_READWRITE));
pika_param_spec_font ("font",
"font",
"The font to set as selected",
FALSE,
PIKA_PARAM_READWRITE));
pika_pdb_register_procedure (pdb, procedure);
g_object_unref (procedure);
}

View File

@ -34,6 +34,7 @@
#include "pdb-types.h"
#include "core/pika.h"
#include "core/pikacontainer.h"
#include "core/pikadatafactory.h"
#include "core/pikagradient.h"
#include "core/pikaparamspecs.h"
@ -54,14 +55,18 @@ gradients_popup_invoker (PikaProcedure *procedure,
gboolean success = TRUE;
const gchar *gradient_callback;
const gchar *popup_title;
const gchar *initial_gradient_name;
PikaGradient *initial_gradient;
GBytes *parent_window;
gradient_callback = g_value_get_string (pika_value_array_index (args, 0));
popup_title = g_value_get_string (pika_value_array_index (args, 1));
initial_gradient_name = g_value_get_string (pika_value_array_index (args, 2));
initial_gradient = g_value_get_object (pika_value_array_index (args, 2));
parent_window = g_value_get_boxed (pika_value_array_index (args, 3));
if (success)
{
PikaContainer *container = pika_data_factory_get_container (pika->gradient_factory);
/* Formerly, this procedure had another parameter:
* the sample size of the gradient's data passed in the changed callback.
* Now the sample size is determined by core, and in the future,
@ -71,9 +76,9 @@ gradients_popup_invoker (PikaProcedure *procedure,
if (pika->no_interface ||
! pika_pdb_lookup_procedure (pika->pdb, gradient_callback) ||
! pika_pdb_dialog_new (pika, context, progress,
pika_data_factory_get_container (pika->gradient_factory),
popup_title, gradient_callback, initial_gradient_name,
NULL))
pika_container_get_children_type (container),
parent_window, popup_title, gradient_callback,
PIKA_OBJECT (initial_gradient), NULL))
success = FALSE;
}
@ -96,9 +101,12 @@ gradients_close_popup_invoker (PikaProcedure *procedure,
if (success)
{
PikaContainer *container = pika_data_factory_get_container (pika->gradient_factory);
if (pika->no_interface ||
! pika_pdb_lookup_procedure (pika->pdb, gradient_callback) ||
! pika_pdb_dialog_close (pika, pika_data_factory_get_container (pika->gradient_factory),
! pika_pdb_dialog_close (pika,
pika_container_get_children_type (container),
gradient_callback))
success = FALSE;
}
@ -117,18 +125,20 @@ gradients_set_popup_invoker (PikaProcedure *procedure,
{
gboolean success = TRUE;
const gchar *gradient_callback;
const gchar *gradient_name;
PikaGradient *gradient;
gradient_callback = g_value_get_string (pika_value_array_index (args, 0));
gradient_name = g_value_get_string (pika_value_array_index (args, 1));
gradient = g_value_get_object (pika_value_array_index (args, 1));
if (success)
{
PikaContainer *container = pika_data_factory_get_container (pika->gradient_factory);
if (pika->no_interface ||
! pika_pdb_lookup_procedure (pika->pdb, gradient_callback) ||
! pika_pdb_dialog_set (pika, pika_data_factory_get_container (pika->gradient_factory),
gradient_callback, gradient_name,
NULL))
! pika_pdb_dialog_set (pika,
pika_container_get_children_type (container),
gradient_callback, PIKA_OBJECT (gradient), NULL))
success = FALSE;
}
@ -170,12 +180,17 @@ register_gradient_select_procs (PikaPDB *pdb)
NULL,
PIKA_PARAM_READWRITE));
pika_procedure_add_argument (procedure,
pika_param_spec_string ("initial-gradient-name",
"initial gradient name",
"The name of the initial gradient choice",
FALSE, TRUE, FALSE,
NULL,
PIKA_PARAM_READWRITE));
pika_param_spec_gradient ("initial-gradient",
"initial gradient",
"The initial gradient choice",
FALSE,
PIKA_PARAM_READWRITE));
pika_procedure_add_argument (procedure,
g_param_spec_boxed ("parent-window",
"parent window",
"An optional parent window handle for the popup to be set transient to",
G_TYPE_BYTES,
PIKA_PARAM_READWRITE));
pika_pdb_register_procedure (pdb, procedure);
g_object_unref (procedure);
@ -225,12 +240,11 @@ register_gradient_select_procs (PikaPDB *pdb)
NULL,
PIKA_PARAM_READWRITE));
pika_procedure_add_argument (procedure,
pika_param_spec_string ("gradient-name",
"gradient name",
"The name of the gradient to set as selected",
FALSE, FALSE, FALSE,
NULL,
PIKA_PARAM_READWRITE));
pika_param_spec_gradient ("gradient",
"gradient",
"The gradient to set as selected",
FALSE,
PIKA_PARAM_READWRITE));
pika_pdb_register_procedure (pdb, procedure);
g_object_unref (procedure);
}

View File

@ -55,6 +55,7 @@
#include "core/pikaimage.h"
#include "core/pikaitem.h"
#include "core/pikalayer.h"
#include "core/pikapalette.h"
#include "core/pikaparamspecs.h"
#include "core/pikapickable.h"
#include "core/pikaprogress.h"
@ -1575,6 +1576,35 @@ image_set_colormap_invoker (PikaProcedure *procedure,
error ? *error : NULL);
}
static PikaValueArray *
image_get_palette_invoker (PikaProcedure *procedure,
Pika *pika,
PikaContext *context,
PikaProgress *progress,
const PikaValueArray *args,
GError **error)
{
gboolean success = TRUE;
PikaValueArray *return_vals;
PikaImage *image;
PikaPalette *colormap = NULL;
image = g_value_get_object (pika_value_array_index (args, 0));
if (success)
{
colormap = pika_image_get_colormap_palette (image);
}
return_vals = pika_procedure_get_return_values (procedure, success,
error ? *error : NULL);
if (success)
g_value_set_object (pika_value_array_index (return_vals, 1), colormap);
return return_vals;
}
static PikaValueArray *
image_get_metadata_invoker (PikaProcedure *procedure,
Pika *pika,
@ -4356,6 +4386,35 @@ register_image_procs (PikaPDB *pdb)
pika_pdb_register_procedure (pdb, procedure);
g_object_unref (procedure);
/*
* pika-image-get-palette
*/
procedure = pika_procedure_new (image_get_palette_invoker);
pika_object_set_static_name (PIKA_OBJECT (procedure),
"pika-image-get-palette");
pika_procedure_set_static_help (procedure,
"Returns the image's colormap",
"This procedure returns the image's colormap as a PikaPalette. If the image is not in Indexed color mode, %NULL is returned.",
NULL);
pika_procedure_set_static_attribution (procedure,
"Jehan",
"Jehan",
"2023");
pika_procedure_add_argument (procedure,
pika_param_spec_image ("image",
"image",
"The image",
FALSE,
PIKA_PARAM_READWRITE));
pika_procedure_add_return_value (procedure,
pika_param_spec_palette ("colormap",
"colormap",
"The image's colormap.",
FALSE,
PIKA_PARAM_READWRITE));
pika_pdb_register_procedure (pdb, procedure);
g_object_unref (procedure);
/*
* pika-image-get-metadata
*/

View File

@ -34,7 +34,7 @@
#include "internal-procs.h"
/* 775 procedures registered total */
/* 779 procedures registered total */
void
internal_procs_init (PikaPDB *pdb)
@ -52,6 +52,7 @@ internal_procs_init (PikaPDB *pdb)
register_drawable_procs (pdb);
register_drawable_color_procs (pdb);
register_drawable_edit_procs (pdb);
register_drawable_select_procs (pdb);
register_dynamics_procs (pdb);
register_edit_procs (pdb);
register_file_procs (pdb);

Some files were not shown because too many files have changed in this diff Show More