Compare commits

12 Commits

1225 changed files with 133974 additions and 97033 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:

View File

@ -1,6 +1,6 @@
-- This file is generated from authors.xml, do not edit it directly. --
PIKA was originally written by:
GIMP was originally written by:
Spencer Kimball
Peter Mattis
@ -12,7 +12,7 @@ The current maintainers are:
Jehan
The following people have contributed code to PIKA:
The following people have contributed code to GIMP:
Lauri Alanko
Fredrik Alströmer
@ -339,7 +339,7 @@ The following people have contributed code to PIKA:
Robert Ögren
The following people have contributed art to PIKA:
The following people have contributed art to GIMP:
Lapo Calamandrei
Paul Davey
@ -359,7 +359,7 @@ The following people have contributed art to PIKA:
William Szilveszter
The following people have helped to document PIKA:
The following people have helped to document GIMP:
Ignacio AntI
Žygimantas Beručka

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

@ -371,66 +371,86 @@ static const PikaEnumActionEntry view_zoom_actions[] =
PIKA_HELP_VIEW_ZOOM_IN }
};
static const PikaRadioActionEntry view_zoom_explicit_actions[] =
static const PikaEnumActionEntry view_zoom_explicit_actions[] =
{
{ "view-zoom-16-1", NULL,
NC_("view-zoom-action", "1_6:1 (1600%)"), NULL, { "5", "KP_5", NULL },
NC_("view-zoom-action", "Zoom 16:1 (1600%)"),
NC_("view-zoom-action", "1_6:1 (1600%)"),
{ "5", "KP_5", NULL },
NC_("view-zoom-action", "Zoom 16:1"),
160000,
160000, FALSE,
PIKA_HELP_VIEW_ZOOM_IN },
{ "view-zoom-8-1", NULL,
NC_("view-zoom-action", "_8:1 (800%)"), NULL, { "4", "KP_4", NULL },
NC_("view-zoom-action", "Zoom 8:1 (800%)"),
NC_("view-zoom-action", "_8:1 (800%)"),
{ "4", "KP_4", NULL },
NC_("view-zoom-action", "Zoom 8:1"),
80000,
80000, FALSE,
PIKA_HELP_VIEW_ZOOM_IN },
{ "view-zoom-4-1", NULL,
NC_("view-zoom-action", "_4:1 (400%)"), NULL, { "3", "KP_3", NULL },
NC_("view-zoom-action", "Zoom 4:1 (400%)"),
NC_("view-zoom-action", "_4:1 (400%)"),
{ "3", "KP_3", NULL },
NC_("view-zoom-action", "Zoom 4:1"),
40000,
40000, FALSE,
PIKA_HELP_VIEW_ZOOM_IN },
{ "view-zoom-2-1", NULL,
NC_("view-zoom-action", "_2:1 (200%)"), NULL, { "2", "KP_2", NULL },
NC_("view-zoom-action", "Zoom 2:1 (200%)"),
NC_("view-zoom-action", "_2:1 (200%)"),
{ "2", "KP_2", NULL },
NC_("view-zoom-action", "Zoom 2:1"),
20000,
20000, FALSE,
PIKA_HELP_VIEW_ZOOM_IN },
{ "view-zoom-1-1", PIKA_ICON_ZOOM_ORIGINAL,
NC_("view-zoom-action", "_1:1 (100%)"), NULL, { "1", "KP_1", NULL },
NC_("view-zoom-action", "Zoom 1:1 (100%)"),
NC_("view-zoom-action", "_1:1 (100%)"),
{ "1", "KP_1", NULL },
NC_("view-zoom-action", "Zoom 1:1"),
10000,
10000, FALSE,
PIKA_HELP_VIEW_ZOOM_100 },
{ "view-zoom-1-2", NULL,
NC_("view-zoom-action", "1:_2 (50%)"), NULL, { "<shift>2", "<shift>KP_2", NULL },
NC_("view-zoom-action", "Zoom 1:2 (50%)"),
NC_("view-zoom-action", "1:_2 (50%)"),
{ "<shift>2", "<shift>KP_2", NULL },
NC_("view-zoom-action", "Zoom 1:2"),
5000,
5000, FALSE,
PIKA_HELP_VIEW_ZOOM_OUT },
{ "view-zoom-1-4", NULL,
NC_("view-zoom-action", "1:_4 (25%)"), NULL, { "<shift>3", "<shift>KP_3", NULL },
NC_("view-zoom-action", "Zoom 1:4 (25%)"),
NC_("view-zoom-action", "1:_4 (25%)"),
{ "<shift>3", "<shift>KP_3", NULL },
NC_("view-zoom-action", "Zoom 1:4"),
2500,
2500, FALSE,
PIKA_HELP_VIEW_ZOOM_OUT },
{ "view-zoom-1-8", NULL,
NC_("view-zoom-action", "1:_8 (12.5%)"), NULL, { "<shift>4", "<shift>KP_4", NULL },
NC_("view-zoom-action", "Zoom 1:8 (12.5%)"),
NC_("view-zoom-action", "1:_8 (12.5%)"),
{ "<shift>4", "<shift>KP_4", NULL },
NC_("view-zoom-action", "Zoom 1:8"),
1250,
1250, FALSE,
PIKA_HELP_VIEW_ZOOM_OUT },
{ "view-zoom-1-16", NULL,
NC_("view-zoom-action", "1:1_6 (6.25%)"), NULL, { "<shift>5", "<shift>KP_5", NULL },
NC_("view-zoom-action", "Zoom 1:16 (6.25%)"),
NC_("view-zoom-action", "1:1_6 (6.25%)"),
{ "<shift>5", "<shift>KP_5", NULL },
NC_("view-zoom-action", "Zoom 1:16"),
625,
625, FALSE,
PIKA_HELP_VIEW_ZOOM_OUT },
{ "view-zoom-other", NULL,
NC_("view-zoom-action", "Othe_r zoom factor..."), NULL, { NULL },
NC_("view-zoom-action", "Set a custom zoom factor"),
0,
NC_("view-zoom-action", "Othe_r zoom factor..."),
{ NULL },
NC_("view-zoom-action", "Set a custom zoom factor"),
0, TRUE,
PIKA_HELP_VIEW_ZOOM_OTHER }
};
@ -652,8 +672,6 @@ static const PikaEnumActionEntry view_scroll_vertical_actions[] =
void
view_actions_setup (PikaActionGroup *group)
{
PikaAction *action;
pika_action_group_add_actions (group, "view-action",
view_actions,
G_N_ELEMENTS (view_actions));
@ -667,12 +685,10 @@ view_actions_setup (PikaActionGroup *group)
G_N_ELEMENTS (view_zoom_actions),
view_zoom_cmd_callback);
pika_action_group_add_radio_actions (group, "view-zoom-action",
view_zoom_explicit_actions,
G_N_ELEMENTS (view_zoom_explicit_actions),
NULL,
10000,
view_zoom_explicit_cmd_callback);
pika_action_group_add_enum_actions (group, "view-zoom-action",
view_zoom_explicit_actions,
G_N_ELEMENTS (view_zoom_explicit_actions),
view_zoom_explicit_cmd_callback);
pika_action_group_add_toggle_actions (group, "view-action",
view_flip_actions,
@ -714,15 +730,6 @@ view_actions_setup (PikaActionGroup *group)
G_N_ELEMENTS (view_scroll_vertical_actions),
view_scroll_vertical_cmd_callback);
/* connect "activate" of view-zoom-other manually so it can be
* selected even if it's the active item of the radio group
*/
action = pika_action_group_get_action (group, "view-zoom-other");
g_signal_connect (action, "activate",
G_CALLBACK (view_zoom_other_cmd_callback),
group->user_data);
g_signal_connect_object (group->pika->config, "notify::check-type",
G_CALLBACK (view_actions_check_type_notify),
group, 0);
@ -987,55 +994,20 @@ static void
view_actions_set_zoom (PikaActionGroup *group,
PikaDisplayShell *shell)
{
const gchar *action = NULL;
PikaImageWindow *window;
PikaMenuModel *model;
gchar *str;
gchar *label;
guint scale;
g_object_get (shell->zoom,
"percentage", &str,
NULL);
scale = ROUND (pika_zoom_model_get_factor (shell->zoom) * 1000);
switch (scale)
{
case 16000: action = "view-zoom-16-1"; break;
case 8000: action = "view-zoom-8-1"; break;
case 4000: action = "view-zoom-4-1"; break;
case 2000: action = "view-zoom-2-1"; break;
case 1000: action = "view-zoom-1-1"; break;
case 500: action = "view-zoom-1-2"; break;
case 250: action = "view-zoom-1-4"; break;
case 125: action = "view-zoom-1-8"; break;
case 63:
case 62: action = "view-zoom-1-16"; break;
}
if (! action)
{
action = "view-zoom-other";
label = g_strdup_printf (_("Othe_r (%s)..."), str);
pika_action_group_set_action_label (group, action, label);
g_free (label);
shell->other_scale = pika_zoom_model_get_factor (shell->zoom);
}
pika_action_group_set_action_active (group, action, TRUE);
window = pika_display_shell_get_window (shell);
model = pika_image_window_get_menubar_model (window);
label = g_strdup_printf (_("_Zoom (%s)"), str);
pika_menu_model_set_title (model, "/View/Zoom", label);
g_free (label);
/* flag as dirty */
shell->other_scale = - fabs (shell->other_scale);
g_free (str);
}

View File

@ -288,22 +288,7 @@ view_zoom_explicit_cmd_callback (PikaAction *action,
(gdouble) factor / 10000,
PIKA_ZOOM_FOCUS_RETAIN_CENTERING_ELSE_BEST_GUESS);
}
}
/* not a PikaActionCallback */
void
view_zoom_other_cmd_callback (PikaAction *action,
gpointer data)
{
PikaDisplayShell *shell;
return_if_no_shell (shell, data);
/* check if we are activated by the user or from
* view_actions_set_zoom(), also this is really a GtkToggleAction
* NOT a PikaToggleAction
*/
if (pika_toggle_action_get_active (PIKA_TOGGLE_ACTION (action)) &&
shell->other_scale != pika_zoom_model_get_factor (shell->zoom))
else
{
pika_display_shell_scale_dialog (shell);
}

View File

@ -53,10 +53,6 @@ void view_zoom_explicit_cmd_callback (PikaAction *action,
GVariant *value,
gpointer data);
/* not a PikaActionCallback */
void view_zoom_other_cmd_callback (PikaAction *action,
gpointer data);
void view_show_all_cmd_callback (PikaAction *action,
GVariant *value,
gpointer data);

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
@ -80,6 +80,7 @@ enum
PROP_ICON_THEME_PATH,
PROP_ICON_THEME,
PROP_PREFER_SYMBOLIC_ICONS,
PROP_FONT_RELATIVE_SIZE,
PROP_USE_HELP,
PROP_SHOW_HELP_BUTTON,
PROP_HELP_LOCALES,
@ -362,6 +363,13 @@ pika_gui_config_class_init (PikaGuiConfigClass *klass)
TRUE,
PIKA_PARAM_STATIC_STRINGS);
PIKA_CONFIG_PROP_DOUBLE (object_class, PROP_FONT_RELATIVE_SIZE,
"font-relative-size",
"Tweak font-size from the theme",
FONT_SIZE_BLURB,
0.5, 2.0, 1.0,
PIKA_PARAM_STATIC_STRINGS);
PIKA_CONFIG_PROP_BOOLEAN (object_class, PROP_USE_HELP,
"use-help",
"Use help",
@ -699,6 +707,9 @@ pika_gui_config_set_property (GObject *object,
case PROP_PREFER_SYMBOLIC_ICONS:
gui_config->prefer_symbolic_icons = g_value_get_boolean (value);
break;
case PROP_FONT_RELATIVE_SIZE:
gui_config->font_relative_size = g_value_get_double (value);
break;
case PROP_USE_HELP:
gui_config->use_help = g_value_get_boolean (value);
break;
@ -876,6 +887,9 @@ pika_gui_config_get_property (GObject *object,
case PROP_PREFER_SYMBOLIC_ICONS:
g_value_set_boolean (value, gui_config->prefer_symbolic_icons);
break;
case PROP_FONT_RELATIVE_SIZE:
g_value_set_double (value, gui_config->font_relative_size);
break;
case PROP_USE_HELP:
g_value_set_boolean (value, gui_config->use_help);
break;

View File

@ -72,6 +72,7 @@ struct _PikaGuiConfig
gchar *icon_theme_path;
gchar *icon_theme;
gboolean prefer_symbolic_icons;
gdouble font_relative_size;
gboolean override_icon_size;
PikaIconSize custom_icon_size;
gboolean use_help;

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."
@ -561,6 +564,9 @@ _("When enabled, symbolic icons will be preferred if available.")
#define ICON_THEME_PATH_BLURB \
"Sets the icon theme search path."
#define FONT_SIZE_BLURB \
_("Tweak font size of the graphical interface.")
#define IMAGE_CONVERT_PROFILE_INTENT_BLURB \
_("Sets the default rendering intent for the 'Convert to Color Profile' dialog.")

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

@ -734,7 +734,7 @@ pika_file_is_executable (GFile *file)
if (info)
{
GFileType file_type = g_file_info_get_file_type (info);
GFileType file_type = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_STANDARD_TYPE);
const gchar *filename = g_file_info_get_name (info);
if (file_type == G_FILE_TYPE_REGULAR &&

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

@ -386,13 +386,13 @@ pika_data_loader_factory_load_directory (PikaDataFactory *factory,
GFileType file_type;
GFile *child;
if (g_file_info_get_is_hidden (info))
if (g_file_info_get_attribute_boolean (info, G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN))
{
g_object_unref (info);
continue;
}
file_type = g_file_info_get_file_type (info);
file_type = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_STANDARD_TYPE);
child = g_file_enumerator_get_child (enumerator, info);
if (file_type == G_FILE_TYPE_DIRECTORY)

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

@ -1022,7 +1022,7 @@ pika_extension_manager_search_directory (PikaExtensionManager *manager,
{
GFile *subdir;
if (g_file_info_get_is_hidden (info))
if (g_file_info_get_attribute_boolean (info, G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN))
{
g_object_unref (info);
continue;

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"
@ -456,7 +457,7 @@ pika_imagefile_create_thumbnail (PikaImagefile *imagefile,
G_FILE_QUERY_INFO_NONE,
NULL, NULL);
regular = (g_file_info_get_file_type (file_info) == G_FILE_TYPE_REGULAR);
regular = (g_file_info_get_attribute_uint32 (file_info, G_FILE_ATTRIBUTE_STANDARD_TYPE) == G_FILE_TYPE_REGULAR);
readable = g_file_info_get_attribute_boolean (file_info,
G_FILE_ATTRIBUTE_ACCESS_CAN_READ);
@ -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)
@ -719,7 +738,7 @@ pika_imagefile_icon_callback (GObject *source_object,
if (file_info)
{
private->icon = g_object_ref (g_file_info_get_icon (file_info));
private->icon = g_object_ref (G_ICON (g_file_info_get_attribute_object (file_info, G_FILE_ATTRIBUTE_STANDARD_ICON)));
g_object_unref (file_info);
}

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)
@ -1736,7 +1750,7 @@ pika_palette_load_detect_format (GFile *file,
if (info)
{
goffset size = g_file_info_get_size (info);
goffset size = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_STANDARD_SIZE);
if (size == 768)
format = PIKA_PALETTE_FILE_FORMAT_ACT;

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

@ -2,7 +2,7 @@
* 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):
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* This program is free software: you can redistribute it and/or modify
@ -34,43 +34,36 @@
#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"
#include "pika-intl.h"
/* The first authors are the creators and maintainers, don't shuffle
* them
*/
#define START_INDEX (G_N_ELEMENTS (creators) - 1 /*NULL*/ + \
G_N_ELEMENTS (maintainers) - 1 /*NULL*/)
typedef struct
{
GtkWidget *dialog;
GtkWidget *dialog;
Pika *pika;
GtkWidget *update_frame;
PikaCoreConfig *config;
GtkWidget *anim_area;
PangoLayout *layout;
GtkWidget *anim_area;
PangoLayout *layout;
gint n_authors;
gint shuffle[G_N_ELEMENTS (authors) - 1]; /* NULL terminated */
guint timer;
guint timer;
gint index;
gint animstep;
gint state;
gboolean visible;
gint index;
gint animstep;
gint state;
gboolean visible;
} PikaAboutDialog;
@ -103,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;
@ -116,7 +110,7 @@ about_dialog_create (PikaCoreConfig *config)
gchar *copyright;
gchar *version;
dialog.n_authors = G_N_ELEMENTS (authors) - 1;
dialog.pika = pika;
dialog.config = config;
pixbuf = about_dialog_load_logo ();
@ -145,12 +139,6 @@ about_dialog_create (PikaCoreConfig *config)
"logo", pixbuf,
"website", "https://heckin.technology/AlderconeStudio/PIKApp/",
"website-label", _("Visit the PIKA website"),
"authors", authors,
"artists", artists,
"documenters", documenters,
/* Translators: insert your names here,
separated by newline */
"translator-credits", _("translator-credits"),
NULL);
if (pixbuf)
@ -210,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
@ -278,229 +270,11 @@ 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
about_dialog_reshuffle (PikaAboutDialog *dialog)
{
GRand *gr = g_rand_new ();
gint i;
for (i = 0; i < dialog->n_authors; i++)
dialog->shuffle[i] = i;
for (i = START_INDEX; i < dialog->n_authors; i++)
{
gint j = g_rand_int_range (gr, START_INDEX, dialog->n_authors);
if (i != j)
{
gint t;
t = dialog->shuffle[j];
dialog->shuffle[j] = dialog->shuffle[i];
dialog->shuffle[i] = t;
}
}
g_rand_free (gr);
}
static gboolean
@ -660,88 +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"));
dialog->state += 1;
break;
case 2:
if (! (dialog->index < dialog->n_authors))
dialog->index = 0;
text = insert_spacers (authors[dialog->shuffle[dialog->index]]);
dialog->index += 1;
break;
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

@ -159,6 +159,11 @@ static void prefs_gui_config_notify_icon_size (GObject *config,
GtkRange *range);
static void prefs_icon_size_value_changed (GtkRange *range,
PikaGuiConfig *config);
static void prefs_gui_config_notify_font_size (GObject *config,
GParamSpec *pspec,
GtkRange *range);
static void prefs_font_size_value_changed (GtkRange *range,
PikaGuiConfig *config);
/* private variables */
@ -975,6 +980,38 @@ prefs_gui_config_notify_icon_size (GObject *config,
config);
}
static void
prefs_font_size_value_changed (GtkRange *range,
PikaGuiConfig *config)
{
gdouble value = gtk_range_get_value (range);
g_signal_handlers_block_by_func (config,
G_CALLBACK (prefs_gui_config_notify_font_size),
range);
g_object_set (G_OBJECT (config),
"font-relative-size", value / 100.0,
NULL);
g_signal_handlers_unblock_by_func (config,
G_CALLBACK (prefs_gui_config_notify_font_size),
range);
}
static void
prefs_gui_config_notify_font_size (GObject *config,
GParamSpec *pspec,
GtkRange *range)
{
g_signal_handlers_block_by_func (range,
G_CALLBACK (prefs_font_size_value_changed),
config);
gtk_range_set_value (range,
PIKA_GUI_CONFIG (config)->font_relative_size * 100.0);
g_signal_handlers_unblock_by_func (range,
G_CALLBACK (prefs_font_size_value_changed),
config);
}
static void
prefs_format_string_select_callback (GtkListBox *listbox,
GtkListBoxRow *row,
@ -1819,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"),
@ -2116,6 +2156,31 @@ prefs_dialog_new (Pika *pika,
gtk_box_pack_start (GTK_BOX (vbox3), scale, FALSE, FALSE, 0);
gtk_widget_show (scale);
/* Font sizes. */
vbox3 = prefs_frame_new (_("Font Scaling"), GTK_CONTAINER (vbox2), FALSE);
pika_help_set_help_data (vbox3,
_("Font scaling will not work with themes using absolute sizes."),
NULL);
scale = gtk_scale_new_with_range (GTK_ORIENTATION_HORIZONTAL,
50, 200, 10);
gtk_scale_set_value_pos (GTK_SCALE (scale), GTK_POS_BOTTOM);
gtk_scale_add_mark (GTK_SCALE (scale), 50.0, GTK_POS_BOTTOM,
_("50%"));
gtk_scale_add_mark (GTK_SCALE (scale), 100.0, GTK_POS_BOTTOM,
_("100%"));
gtk_scale_add_mark (GTK_SCALE (scale), 200.0, GTK_POS_BOTTOM,
_("200%"));
gtk_range_set_value (GTK_RANGE (scale),
(gdouble) PIKA_GUI_CONFIG (object)->font_relative_size * 100.0);
g_signal_connect (G_OBJECT (scale), "value-changed",
G_CALLBACK (prefs_font_size_value_changed),
PIKA_GUI_CONFIG (object));
g_signal_connect (G_OBJECT (object), "notify::font-relative-size",
G_CALLBACK (prefs_gui_config_notify_font_size),
scale);
gtk_box_pack_start (GTK_BOX (vbox3), scale, FALSE, FALSE, 0);
gtk_widget_show (scale);
/* Reload Current Theme button */
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
gtk_box_pack_start (GTK_BOX (vbox2), hbox, FALSE, FALSE, 0);

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

@ -32,6 +32,7 @@
#include "core/pika.h"
#include "core/pikaviewable.h"
#include "widgets/pikaaction.h"
#include "widgets/pikahelp-ids.h"
#include "widgets/pikaviewabledialog.h"
@ -54,6 +55,9 @@ typedef struct
GtkAdjustment *scale_adj;
GtkAdjustment *num_adj;
GtkAdjustment *denom_adj;
gdouble prev_scale;
gdouble *other_scale;
} ScaleDialogData;
@ -81,6 +85,8 @@ static void update_zoom_values (GtkAdjustment *adj,
void
pika_display_shell_scale_dialog (PikaDisplayShell *shell)
{
/* scale factor entered in Zoom->Other*/
static gdouble other_scale = 0.0;
ScaleDialogData *data;
PikaImage *image;
GtkWidget *toplevel;
@ -98,19 +104,21 @@ pika_display_shell_scale_dialog (PikaDisplayShell *shell)
return;
}
if (SCALE_EQUALS (shell->other_scale, 0.0))
data = g_slice_new (ScaleDialogData);
data->prev_scale = other_scale;
data->other_scale = &other_scale;
if (SCALE_EQUALS (other_scale, 0.0))
{
/* other_scale not yet initialized */
shell->other_scale = pika_zoom_model_get_factor (shell->zoom);
other_scale = pika_zoom_model_get_factor (shell->zoom);
}
image = pika_display_get_image (shell->display);
data = g_slice_new (ScaleDialogData);
data->shell = shell;
data->model = g_object_new (PIKA_TYPE_ZOOM_MODEL,
"value", fabs (shell->other_scale),
"value", fabs (other_scale),
NULL);
g_set_weak_pointer
@ -189,7 +197,7 @@ pika_display_shell_scale_dialog (PikaDisplayShell *shell)
_("Zoom:"), 0.0, 0.5,
hbox, 1);
data->scale_adj = gtk_adjustment_new (fabs (shell->other_scale) * 100,
data->scale_adj = gtk_adjustment_new (other_scale * 100,
100.0 / 256.0, 25600.0,
10, 50, 0);
spin = pika_spin_button_new (data->scale_adj, 1.0, 2);
@ -219,7 +227,10 @@ pika_display_shell_scale_dialog_response (GtkWidget *widget,
{
if (response_id == GTK_RESPONSE_OK)
{
gdouble scale;
GAction *action;
gchar *label;
gchar *zoom_str;
gdouble scale;
scale = gtk_adjustment_get_value (dialog->scale_adj);
@ -227,14 +238,32 @@ pika_display_shell_scale_dialog_response (GtkWidget *widget,
PIKA_ZOOM_TO,
scale / 100.0,
PIKA_ZOOM_FOCUS_BEST_GUESS);
g_object_get (dialog->shell->zoom,
"percentage", &zoom_str,
NULL);
/* Change the "view-zoom-other" label. */
action = g_action_map_lookup_action (G_ACTION_MAP (dialog->shell->display->pika->app),
"view-zoom-other");
label = g_strdup_printf (_("Othe_r (%s)..."), zoom_str);
pika_action_set_short_label (PIKA_ACTION (action), label);
g_free (label);
label = g_strdup_printf (_("Custom Zoom (%s)..."), zoom_str);
pika_action_set_label (PIKA_ACTION (action), label);
g_free (label);
g_free (zoom_str);
}
else
{
/* need to emit "scaled" to get the menu updated */
pika_display_shell_scaled (dialog->shell);
}
dialog->shell->other_scale = - fabs (dialog->shell->other_scale);
*(dialog->other_scale) = dialog->prev_scale;
}
gtk_widget_destroy (dialog->shell->scale_dialog);
}
@ -271,6 +300,8 @@ update_zoom_values (GtkAdjustment *adj,
gtk_adjustment_set_value (dialog->num_adj, num);
gtk_adjustment_set_value (dialog->denom_adj, denom);
*(dialog->other_scale) = scale / 100.0;
}
else /* fraction adjustments */
{
@ -278,6 +309,8 @@ update_zoom_values (GtkAdjustment *adj,
gtk_adjustment_get_value (dialog->denom_adj));
gtk_adjustment_set_value (dialog->scale_adj, scale * 100);
*(dialog->other_scale) = scale;
}
g_signal_handlers_unblock_by_func (dialog->scale_adj,

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;
@ -87,8 +89,6 @@ struct _PikaDisplayShell
gint last_offset_x; /* offsets used when reverting zoom */
gint last_offset_y;
gdouble other_scale; /* scale factor entered in Zoom->Other*/
gint disp_width; /* width of drawing area */
gint disp_height; /* height of drawing area */

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

@ -2451,7 +2451,7 @@ pika_statusbar_queue_pos_redraw (gpointer data)
* The goal of this is to avoid the label size jumping up and
* down. Actually it was not a problem on Linux, but this was
* reported on macOS.
* See: https://gitlab.gnome.org/GNOME/pika/-/merge_requests/572#note_1389445
* See: https://gitlab.gnome.org/GNOME/gimp/-/merge_requests/572#note_1389445
* So we just compute what looks like a reasonable "biggest size"
* in worst cases.
* Of course, it could still happen for people going way

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);
@ -4210,11 +4224,12 @@ void
pika_tool_rectangle_frame_item (PikaToolRectangle *rectangle,
PikaItem *item)
{
PikaDisplayShell *shell;
gint offset_x;
gint offset_y;
gint width;
gint height;
PikaDisplayShell *shell;
gint offset_x;
gint offset_y;
gint width;
gint height;
PikaRectangleFunction old_function;
g_return_if_fail (PIKA_IS_TOOL_RECTANGLE (rectangle));
g_return_if_fail (PIKA_IS_ITEM (item));
@ -4230,6 +4245,7 @@ pika_tool_rectangle_frame_item (PikaToolRectangle *rectangle,
pika_item_get_offset (item, &offset_x, &offset_y);
old_function = rectangle->private->function;
pika_tool_rectangle_set_function (rectangle, PIKA_TOOL_RECTANGLE_CREATING);
g_object_set (rectangle,
@ -4243,6 +4259,7 @@ pika_tool_rectangle_frame_item (PikaToolRectangle *rectangle,
* if this function is ever moved out of the text tool code.
*/
pika_tool_rectangle_set_constraint (rectangle, PIKA_RECTANGLE_CONSTRAIN_NONE);
pika_tool_rectangle_set_function (rectangle, old_function);
}
void

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

@ -134,7 +134,7 @@ file_open_image (Pika *pika,
if (! info)
return NULL;
if (g_file_info_get_file_type (info) != G_FILE_TYPE_REGULAR)
if (g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_STANDARD_TYPE) != G_FILE_TYPE_REGULAR)
{
g_set_error_literal (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
_("Not a regular 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

@ -138,7 +138,7 @@ file_save (Pika *pika,
goto out;
}
if (g_file_info_get_file_type (info) != G_FILE_TYPE_REGULAR)
if (g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_STANDARD_TYPE) != G_FILE_TYPE_REGULAR)
{
g_set_error_literal (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
_("Not a regular file"));

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

@ -100,8 +100,8 @@ icon_themes_init (Pika *pika)
while ((info = g_file_enumerator_next_file (enumerator,
NULL, NULL)))
{
if (! g_file_info_get_is_hidden (info) &&
g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY)
if (! g_file_info_get_attribute_boolean (info, G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN) &&
g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_STANDARD_TYPE) == G_FILE_TYPE_DIRECTORY)
{
GFile *file;
GFile *index_theme;

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 ();
}
@ -599,7 +617,7 @@ splash_image_load_from_file (GFile *file,
{
const gchar *content_type;
content_type = g_file_info_get_content_type (info);
content_type = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE);
if (content_type)
{
gchar *mime_type;

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"
@ -104,8 +108,15 @@ themes_init (Pika *pika)
g_signal_connect (config, "notify::custom-icon-size",
G_CALLBACK (themes_theme_change_notify),
pika);
g_signal_connect (config, "notify::font-relative-size",
G_CALLBACK (themes_theme_change_notify),
pika);
themes_theme_change_notify (config, NULL, pika);
#ifdef G_OS_WIN32
themes_set_title_bar (pika);
#endif
}
void
@ -377,6 +388,12 @@ themes_apply_theme (Pika *pika,
button_icon_size);
}
if (! error && config->font_relative_size != 1.0)
g_output_stream_printf (output, NULL, NULL, &error,
"\n"
"* { font-size: %frem; }",
config->font_relative_size);
if (! error)
{
g_output_stream_printf (
@ -464,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
@ -515,8 +536,8 @@ themes_theme_paths_notify (PikaExtensionManager *manager,
while ((info = g_file_enumerator_next_file (enumerator,
NULL, NULL)))
{
if (! g_file_info_get_is_hidden (info) &&
g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY)
if (! g_file_info_get_attribute_boolean (info, G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN) &&
g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_STANDARD_TYPE) == G_FILE_TYPE_DIRECTORY)
{
GFile *file;
const gchar *name;
@ -544,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);
@ -273,9 +258,20 @@ shortcuts_action_deserialize (GScanner *scanner,
builder = g_strv_builder_new ();
while (pika_scanner_parse_string (scanner, &accel))
{
gchar **dup_actions;
gboolean add_accel = TRUE;
gchar **dup_actions;
gboolean add_accel = TRUE;
guint accelerator_key = 0;
GdkModifierType accelerator_mods = 0;
gtk_accelerator_parse (accel, &accelerator_key, &accelerator_mods);
if (accelerator_key == 0 && accelerator_mods == 0)
{
g_printerr ("INFO: invalid accelerator '%s' on '%s'.\n"
" Removing this accelerator.\n",
accel, action_name);
g_free (accel);
continue;
}
dup_actions = gtk_application_get_actions_for_accel (application, accel);
for (gint i = 0; dup_actions[i] != NULL; i++)

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,

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