Initial checkin of Pika from heckimp

This commit is contained in:
2023-09-25 15:35:21 -07:00
commit 891e999216
6761 changed files with 5240685 additions and 0 deletions

259
app/widgets/meson.build Normal file
View File

@ -0,0 +1,259 @@
stamp_widgets_enums = custom_target('stamp-widgets-enums.h',
input : [
files(
'widgets-enums.h'
),
],
output: [ 'stamp-widgets-enums.h', ],
command: [
mkenums_wrap, perl,
meson.project_source_root(), meson.current_source_dir(),
meson.current_build_dir(),
'widgets-',
'#include <gtk/gtk.h>\n' +
'#include "libpikabase/pikabase.h"\n',
'#include "pika-intl.h"'
],
build_by_default: true
)
libappwidgets_sources = [
'pikaaccellabel.c',
'pikaaction-history.c',
'pikaaction.c',
'pikaactioneditor.c',
'pikaactionfactory.c',
'pikaactiongroup.c',
'pikaactionimpl.c',
'pikaactionview.c',
'pikablobeditor.c',
'pikabrusheditor.c',
'pikabrushfactoryview.c',
'pikabrushselect.c',
'pikabuffersourcebox.c',
'pikabufferview.c',
'pikacairo-mascot.c',
'pikacellrendererbutton.c',
'pikacellrendererdashes.c',
'pikacellrendererviewable.c',
'pikachanneltreeview.c',
'pikacircle.c',
'pikaclipboard.c',
'pikacolorbar.c',
'pikacolordialog.c',
'pikacolordisplayeditor.c',
'pikacoloreditor.c',
'pikacolorframe.c',
'pikacolorhistory.c',
'pikacolormapeditor.c',
'pikacolormapselection.c',
'pikacolorpanel.c',
'pikacolorselectorpalette.c',
'pikacombotagentry.c',
'pikacomponenteditor.c',
'pikacompressioncombobox.c',
'pikacontainerbox.c',
'pikacontainercombobox.c',
'pikacontainereditor.c',
'pikacontainerentry.c',
'pikacontainericonview.c',
'pikacontainerpopup.c',
'pikacontainertreestore.c',
'pikacontainertreeview-dnd.c',
'pikacontainertreeview.c',
'pikacontainerview-utils.c',
'pikacontainerview.c',
'pikacontrollereditor.c',
'pikacontrollerinfo.c',
'pikacontrollerkeyboard.c',
'pikacontrollerlist.c',
'pikacontrollers.c',
'pikacontrollerwheel.c',
'pikacriticaldialog.c',
'pikacursor.c',
'pikacurveview.c',
'pikadashboard.c',
'pikadasheditor.c',
'pikadataeditor.c',
'pikadatafactoryview.c',
'pikadeviceeditor.c',
'pikadeviceinfo-coords.c',
'pikadeviceinfo.c',
'pikadeviceinfoeditor.c',
'pikadevicemanager.c',
'pikadevices.c',
'pikadevicestatus.c',
'pikadial.c',
'pikadialogfactory.c',
'pikadnd-xds.c',
'pikadnd.c',
'pikadock.c',
'pikadockable.c',
'pikadockbook.c',
'pikadockcolumns.c',
'pikadockcontainer.c',
'pikadocked.c',
'pikadockwindow.c',
'pikadocumentview.c',
'pikadoubleaction.c',
'pikadrawabletreeview.c',
'pikadynamicseditor.c',
'pikadynamicsfactoryview.c',
'pikadynamicsoutputeditor.c',
'pikaeditor.c',
'pikaenumaction.c',
'pikaerrorconsole.c',
'pikaerrordialog.c',
'pikaexportdialog.c',
'pikaextensionlist.c',
'pikaextensiondetails.c',
'pikafgbgeditor.c',
'pikafgbgview.c',
'pikafiledialog.c',
'pikafileprocview.c',
'pikafilleditor.c',
'pikafontfactoryview.c',
'pikafontselect.c',
'pikagradienteditor.c',
'pikagradientselect.c',
'pikagrideditor.c',
'pikahandlebar.c',
'pikahelp.c',
'pikahistogrambox.c',
'pikahistogrameditor.c',
'pikahistogramview.c',
'pikaiconpicker.c',
'pikaimagecommenteditor.c',
'pikaimageeditor.c',
'pikaimageparasiteview.c',
'pikaimageprofileview.c',
'pikaimagepropview.c',
'pikaimageview.c',
'pikaitemtreeview.c',
'pikalanguagecombobox.c',
'pikalanguageentry.c',
'pikalanguagestore-parser.c',
'pikalanguagestore.c',
'pikalayermodebox.c',
'pikalayermodecombobox.c',
'pikalayertreeview.c',
'pikamenu.c',
'pikamenubar.c',
'pikamenushell.c',
'pikamenudock.c',
'pikamenufactory.c',
'pikamenumodel.c',
'pikamessagebox.c',
'pikamessagedialog.c',
'pikameter.c',
'pikamodifierseditor.c',
'pikanavigationview.c',
'pikaopendialog.c',
'pikaoverlaybox.c',
'pikaoverlaychild.c',
'pikaoverlaydialog.c',
'pikaoverlayframe.c',
'pikapaletteeditor.c',
'pikapaletteselect.c',
'pikapaletteview.c',
'pikapanedbox.c',
'pikapatternfactoryview.c',
'pikapatternselect.c',
'pikapdbdialog.c',
'pikapickablebutton.c',
'pikapickablepopup.c',
'pikapivotselector.c',
'pikapixbuf.c',
'pikapluginview.c',
'pikapolar.c',
'pikapopup.c',
'pikaprefsbox.c',
'pikaprocedureaction.c',
'pikaprogressbox.c',
'pikaprogressdialog.c',
'pikapropwidgets.c',
'pikaradioaction.c',
'pikarender.c',
'pikasamplepointeditor.c',
'pikasavedialog.c',
'pikasearchpopup.c',
'pikaselectiondata.c',
'pikaselectioneditor.c',
'pikasessioninfo-aux.c',
'pikasessioninfo-book.c',
'pikasessioninfo-dock.c',
'pikasessioninfo-dockable.c',
'pikasessioninfo.c',
'pikasessionmanaged.c',
'pikasettingsbox.c',
'pikasettingseditor.c',
'pikashortcutbutton.c',
'pikasizebox.c',
'pikastringaction.c',
'pikastrokeeditor.c',
'pikasymmetryeditor.c',
'pikatagentry.c',
'pikatagpopup.c',
'pikatemplateeditor.c',
'pikatemplateview.c',
'pikatextbuffer-serialize.c',
'pikatextbuffer.c',
'pikatexteditor.c',
'pikatextproxy.c',
'pikatextstyleeditor.c',
'pikatexttag.c',
'pikathumbbox.c',
'pikatoggleaction.c',
'pikatoolbar.c',
'pikatoolbox-color-area.c',
'pikatoolbox-dnd.c',
'pikatoolbox-image-area.c',
'pikatoolbox-indicator-area.c',
'pikatoolbox.c',
'pikatoolbutton.c',
'pikatooleditor.c',
'pikatooloptionseditor.c',
'pikatoolpalette.c',
'pikatoolpreseteditor.c',
'pikatoolpresetfactoryview.c',
'pikatranslationstore.c',
'pikauimanager.c',
'pikaundoeditor.c',
'pikavectorstreeview.c',
'pikaview-popup.c',
'pikaview.c',
'pikaviewablebox.c',
'pikaviewablebutton.c',
'pikaviewabledialog.c',
'pikaviewrenderer-frame.c',
'pikaviewrenderer-utils.c',
'pikaviewrenderer.c',
'pikaviewrendererbrush.c',
'pikaviewrendererbuffer.c',
'pikaviewrendererdrawable.c',
'pikaviewrenderergradient.c',
'pikaviewrendererimage.c',
'pikaviewrendererimagefile.c',
'pikaviewrendererlayer.c',
'pikaviewrendererpalette.c',
'pikaviewrenderervectors.c',
'pikawidgets-constructors.c',
'pikawidgets-utils.c',
'pikawindow.c',
'pikawindowstrategy.c',
'widgets-enums.c',
stamp_widgets_enums,
appcoremarshal[1],
]
libappwidgets = static_library('appwidgets',
libappwidgets_sources,
include_directories: [ rootInclude, rootAppInclude, ],
c_args: '-DG_LOG_DOMAIN="Pika-Widgets"',
dependencies: [
gegl, gtk3,
],
)

View File

@ -0,0 +1,209 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikaaccellabel.c
* Copyright (C) 2020 Ell
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gegl.h>
#include <gtk/gtk.h>
#include "libpikabase/pikabase.h"
#include "widgets-types.h"
#include "pikaaction.h"
#include "pikaaccellabel.h"
enum
{
PROP_0,
PROP_ACTION
};
struct _PikaAccelLabelPrivate
{
PikaAction *action;
};
/* local function prototypes */
static void pika_accel_label_dispose (GObject *object);
static void pika_accel_label_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
static void pika_accel_label_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
static void pika_accel_label_update (PikaAccelLabel *accel_label);
G_DEFINE_TYPE_WITH_PRIVATE (PikaAccelLabel, pika_accel_label, GTK_TYPE_LABEL)
#define parent_class pika_accel_label_parent_class
/* private functions */
static void
pika_accel_label_class_init (PikaAccelLabelClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->dispose = pika_accel_label_dispose;
object_class->get_property = pika_accel_label_get_property;
object_class->set_property = pika_accel_label_set_property;
g_object_class_install_property (object_class, PROP_ACTION,
g_param_spec_object ("action",
NULL, NULL,
PIKA_TYPE_ACTION,
PIKA_PARAM_READWRITE));
}
static void
pika_accel_label_init (PikaAccelLabel *accel_label)
{
accel_label->priv = pika_accel_label_get_instance_private (accel_label);
}
static void
pika_accel_label_dispose (GObject *object)
{
PikaAccelLabel *accel_label = PIKA_ACCEL_LABEL (object);
pika_accel_label_set_action (accel_label, NULL);
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
pika_accel_label_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
PikaAccelLabel *accel_label = PIKA_ACCEL_LABEL (object);
switch (property_id)
{
case PROP_ACTION:
pika_accel_label_set_action (accel_label, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
pika_accel_label_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
PikaAccelLabel *accel_label = PIKA_ACCEL_LABEL (object);
switch (property_id)
{
case PROP_ACTION:
g_value_set_object (value, accel_label->priv->action);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
pika_accel_label_update (PikaAccelLabel *accel_label)
{
gchar **accels;
gtk_label_set_label (GTK_LABEL (accel_label), NULL);
if (! accel_label->priv->action)
return;
accels = pika_action_get_display_accels (accel_label->priv->action);
if (accels && accels[0])
gtk_label_set_label (GTK_LABEL (accel_label), accels[0]);
g_strfreev (accels);
}
/* public functions */
GtkWidget *
pika_accel_label_new (PikaAction *action)
{
g_return_val_if_fail (action == NULL || PIKA_IS_ACTION (action), NULL);
return g_object_new (PIKA_TYPE_ACCEL_LABEL,
"action", action,
NULL);
}
void
pika_accel_label_set_action (PikaAccelLabel *accel_label,
PikaAction *action)
{
g_return_if_fail (PIKA_IS_ACCEL_LABEL (accel_label));
g_return_if_fail (action == NULL || PIKA_IS_ACTION (action));
if (action != accel_label->priv->action)
{
if (accel_label->priv->action)
g_signal_handlers_disconnect_by_func (accel_label->priv->action,
pika_accel_label_update,
accel_label);
g_set_object (&accel_label->priv->action, action);
if (accel_label->priv->action)
g_signal_connect_swapped (action, "accels-changed",
G_CALLBACK (pika_accel_label_update),
accel_label);
pika_accel_label_update (accel_label);
g_object_notify (G_OBJECT (accel_label), "action");
}
}
PikaAction *
pika_accel_label_get_action (PikaAccelLabel *accel_label)
{
g_return_val_if_fail (PIKA_IS_ACCEL_LABEL (accel_label), NULL);
return accel_label->priv->action;
}

View File

@ -0,0 +1,62 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikaaccellabel.h
* Copyright (C) 2020 Ell
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __PIKA_ACCEL_LABEL_H__
#define __PIKA_ACCEL_LABEL_H__
#define PIKA_TYPE_ACCEL_LABEL (pika_accel_label_get_type ())
#define PIKA_ACCEL_LABEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIKA_TYPE_ACCEL_LABEL, PikaAccelLabel))
#define PIKA_ACCEL_LABEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIKA_TYPE_ACCEL_LABEL, PikaAccelLabelClass))
#define PIKA_IS_ACCEL_LABEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, PIKA_TYPE_ACCEL_LABEL))
#define PIKA_IS_ACCEL_LABEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIKA_TYPE_ACCEL_LABEL))
#define PIKA_ACCEL_LABEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIKA_TYPE_ACCEL_LABEL, PikaAccelLabelClass))
typedef struct _PikaAccelLabelPrivate PikaAccelLabelPrivate;
typedef struct _PikaAccelLabelClass PikaAccelLabelClass;
struct _PikaAccelLabel
{
GtkLabel parent_instance;
PikaAccelLabelPrivate *priv;
};
struct _PikaAccelLabelClass
{
GtkLabelClass parent_class;
};
GType pika_accel_label_get_type (void) G_GNUC_CONST;
GtkWidget * pika_accel_label_new (PikaAction *action);
void pika_accel_label_set_action (PikaAccelLabel *accel_label,
PikaAction *action);
PikaAction * pika_accel_label_get_action (PikaAccelLabel *accel_label);
#endif /* __PIKA_ACCEL_LABEL_H__ */

View File

@ -0,0 +1,510 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikaaction-history.c
* Copyright (C) 2013 Jehan <jehan at girinstud.io>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <string.h>
#include <gtk/gtk.h>
#include "libpikabase/pikabase.h"
#include "libpikaconfig/pikaconfig.h"
#include "libpikamath/pikamath.h"
#include "widgets-types.h"
#include "config/pikaguiconfig.h"
#include "core/pika.h"
#include "pikaaction.h"
#include "pikaaction-history.h"
#define PIKA_ACTION_HISTORY_FILENAME "action-history"
/* History items are stored in a queue, sorted by frequency (number of times
* the action was activated), from most frequent to least frequent. Each item,
* in addition to the corresponding action name and its index in the queue,
* stores a "delta": the difference in frequency between it, and the next item
* in the queue; note that the frequency itself is not stored anywhere.
*
* To keep items from remaining at the top of the queue for too long, the delta
* is capped above, such the the maximal delta of the first item is MAX_DELTA,
* and the maximal delta of each subsequent item is the maximal delta of the
* previous item, times MAX_DELTA_FALLOFF.
*
* When an action is activated, its frequency grows by 1, meaning that the
* delta of the corresponding item is incremented (if below the maximum), and
* the delta of the previous item is decremented (if above 0). If the delta of
* the previous item is already 0, then, before the above, the current and
* previous items swap frequencies, and the current item is moved up the queue
* until the preceding item's frequency is greater than 0 (or until it reaches
* the front of the queue).
*/
#define MAX_DELTA 5
#define MAX_DELTA_FALLOFF 0.95
enum
{
HISTORY_ITEM = 1
};
typedef struct
{
gchar *action_name;
gint index;
gint delta;
} PikaActionHistoryItem;
static struct
{
Pika *pika;
GQueue *items;
GHashTable *links;
} history;
static PikaActionHistoryItem * pika_action_history_item_new (const gchar *action_name,
gint index,
gint delta);
static void pika_action_history_item_free (PikaActionHistoryItem *item);
static gint pika_action_history_item_max_delta (gint index);
/* public functions */
void
pika_action_history_init (Pika *pika)
{
PikaGuiConfig *config;
GFile *file;
GScanner *scanner;
GTokenType token;
gint delta = 0;
g_return_if_fail (PIKA_IS_PIKA (pika));
config = PIKA_GUI_CONFIG (pika->config);
if (history.pika != NULL)
{
g_warning ("%s: must be run only once.", G_STRFUNC);
return;
}
history.pika = pika;
history.items = g_queue_new ();
history.links = g_hash_table_new (g_str_hash, g_str_equal);
file = pika_directory_file (PIKA_ACTION_HISTORY_FILENAME, NULL);
if (pika->be_verbose)
g_print ("Parsing '%s'\n", pika_file_get_utf8_name (file));
scanner = pika_scanner_new_file (file, NULL);
g_object_unref (file);
if (! scanner)
return;
g_scanner_scope_add_symbol (scanner, 0, "history-item",
GINT_TO_POINTER (HISTORY_ITEM));
token = G_TOKEN_LEFT_PAREN;
while (g_scanner_peek_next_token (scanner) == token)
{
token = g_scanner_get_next_token (scanner);
switch (token)
{
case G_TOKEN_LEFT_PAREN:
token = G_TOKEN_SYMBOL;
break;
case G_TOKEN_SYMBOL:
if (scanner->value.v_symbol == GINT_TO_POINTER (HISTORY_ITEM))
{
gchar *action_name;
token = G_TOKEN_STRING;
if (g_scanner_peek_next_token (scanner) != token)
break;
if (! pika_scanner_parse_string (scanner, &action_name))
break;
token = G_TOKEN_INT;
if (g_scanner_peek_next_token (scanner) != token ||
! pika_scanner_parse_int (scanner, &delta))
{
g_free (action_name);
break;
}
if (! pika_action_history_is_excluded_action (action_name) &&
! g_hash_table_contains (history.links, action_name))
{
PikaActionHistoryItem *item;
item = pika_action_history_item_new (
action_name,
g_queue_get_length (history.items),
delta);
g_queue_push_tail (history.items, item);
g_hash_table_insert (history.links,
item->action_name,
g_queue_peek_tail_link (history.items));
}
g_free (action_name);
}
token = G_TOKEN_RIGHT_PAREN;
break;
case G_TOKEN_RIGHT_PAREN:
token = G_TOKEN_LEFT_PAREN;
if (g_queue_get_length (history.items) >= config->action_history_size)
goto done;
break;
default: /* do nothing */
break;
}
}
done:
pika_scanner_unref (scanner);
}
void
pika_action_history_exit (Pika *pika)
{
PikaGuiConfig *config;
PikaActionHistoryItem *item;
GList *actions;
GFile *file;
PikaConfigWriter *writer;
gint i;
g_return_if_fail (PIKA_IS_PIKA (pika));
config = PIKA_GUI_CONFIG (pika->config);
file = pika_directory_file (PIKA_ACTION_HISTORY_FILENAME, NULL);
if (pika->be_verbose)
g_print ("Writing '%s'\n", pika_file_get_utf8_name (file));
writer = pika_config_writer_new_from_file (file, TRUE, "PIKA action-history",
NULL);
g_object_unref (file);
for (actions = history.items->head, i = 0;
actions && i < config->action_history_size;
actions = g_list_next (actions), i++)
{
item = actions->data;
pika_config_writer_open (writer, "history-item");
pika_config_writer_string (writer, item->action_name);
pika_config_writer_printf (writer, "%d", item->delta);
pika_config_writer_close (writer);
}
pika_config_writer_finish (writer, "end of action-history", NULL);
pika_action_history_clear (pika);
g_clear_pointer (&history.links, g_hash_table_unref);
g_clear_pointer (&history.items, g_queue_free);
history.pika = NULL;
}
void
pika_action_history_clear (Pika *pika)
{
PikaActionHistoryItem *item;
g_return_if_fail (PIKA_IS_PIKA (pika));
g_hash_table_remove_all (history.links);
while ((item = g_queue_pop_head (history.items)))
pika_action_history_item_free (item);
}
/**
* pika_action_history_search:
* @pika:
* @match_func:
* @keyword:
*
* Search all history #PikaAction which match @keyword with function
* @match_func(action, keyword).
* It will also return inactive actions, but will discard non-visible
* actions.
*
* returns: a #GList of #PikaAction, which must be freed with
* g_list_free_full (result, (GDestroyNotify) g_object_unref)
*/
GList *
pika_action_history_search (Pika *pika,
PikaActionMatchFunc match_func,
const gchar *keyword)
{
PikaGuiConfig *config;
GList *actions;
GList *result = NULL;
gint i;
g_return_val_if_fail (PIKA_IS_PIKA (pika), NULL);
g_return_val_if_fail (match_func != NULL, NULL);
config = PIKA_GUI_CONFIG (pika->config);
for (actions = history.items->head, i = 0;
actions && i < config->action_history_size;
actions = g_list_next (actions), i++)
{
PikaActionHistoryItem *item = actions->data;
GAction *action;
action = g_action_map_lookup_action (G_ACTION_MAP (pika->app), item->action_name);
if (action == NULL)
continue;
g_return_val_if_fail (PIKA_IS_ACTION (action), NULL);
if (! pika_action_is_visible (PIKA_ACTION (action)))
continue;
if (match_func (PIKA_ACTION (action), keyword, NULL, pika))
result = g_list_prepend (result, g_object_ref (action));
}
return g_list_reverse (result);
}
/* pika_action_history_is_blacklisted_action:
*
* Returns whether an action should be excluded from both
* history and search results.
*/
gboolean
pika_action_history_is_blacklisted_action (const gchar *action_name)
{
if (pika_action_is_gui_blacklisted (action_name))
return TRUE;
return (g_str_has_suffix (action_name, "-set") ||
g_str_has_prefix (action_name, "context-") ||
g_str_has_prefix (action_name, "filters-recent-") ||
g_strcmp0 (action_name, "dialogs-action-search") == 0);
}
/* pika_action_history_is_excluded_action:
*
* Returns whether an action should be excluded from history.
*
* Some actions should not be logged in the history, but should
* otherwise appear in the search results, since they correspond
* to different functions at different times, or since their
* label may interfere with more relevant, but less frequent,
* actions.
*/
gboolean
pika_action_history_is_excluded_action (const gchar *action_name)
{
if (pika_action_history_is_blacklisted_action (action_name))
return TRUE;
return (g_strcmp0 (action_name, "edit-undo") == 0 ||
g_strcmp0 (action_name, "edit-strong-undo") == 0 ||
g_strcmp0 (action_name, "edit-redo") == 0 ||
g_strcmp0 (action_name, "edit-strong-redo") == 0 ||
g_strcmp0 (action_name, "filters-repeat") == 0 ||
g_strcmp0 (action_name, "filters-reshow") == 0);
}
/* Called whenever a PikaAction is activated.
* It allows us to log all used actions.
*/
void
pika_action_history_action_activated (PikaAction *action)
{
PikaGuiConfig *config;
const gchar *action_name;
GList *link;
PikaActionHistoryItem *item;
/* Silently return when called at the wrong time, like when the
* activated action was "quit" and the history is already gone.
*/
if (! history.pika)
return;
config = PIKA_GUI_CONFIG (history.pika->config);
if (config->action_history_size == 0)
return;
action_name = pika_action_get_name (action);
/* Some specific actions are of no log interest. */
if (pika_action_history_is_excluded_action (action_name))
return;
g_return_if_fail (action_name != NULL);
/* Remove excessive items. */
while (g_queue_get_length (history.items) > config->action_history_size)
{
item = g_queue_pop_tail (history.items);
g_hash_table_remove (history.links, item->action_name);
pika_action_history_item_free (item);
}
/* Look up the action in the history. */
link = g_hash_table_lookup (history.links, action_name);
/* If the action is not in the history, insert it
* at the back of the history queue, possibly
* replacing the last item.
*/
if (! link)
{
if (g_queue_get_length (history.items) == config->action_history_size)
{
item = g_queue_pop_tail (history.items);
g_hash_table_remove (history.links, item->action_name);
pika_action_history_item_free (item);
}
item = pika_action_history_item_new (
action_name,
g_queue_get_length (history.items),
0);
g_queue_push_tail (history.items, item);
link = g_queue_peek_tail_link (history.items);
g_hash_table_insert (history.links, item->action_name, link);
}
else
{
item = link->data;
}
/* Update the history, according to the logic described
* in the comment at the beginning of the file.
*/
if (item->index > 0)
{
GList *prev_link = g_list_previous (link);
PikaActionHistoryItem *prev_item = prev_link->data;
if (prev_item->delta == 0)
{
for (; prev_link; prev_link = g_list_previous (prev_link))
{
prev_item = prev_link->data;
if (prev_item->delta > 0)
break;
prev_item->index++;
item->index--;
prev_item->delta = item->delta;
item->delta = 0;
}
g_queue_unlink (history.items, link);
if (prev_link)
{
link->prev = prev_link;
link->next = prev_link->next;
link->prev->next = link;
link->next->prev = link;
history.items->length++;
}
else
{
g_queue_push_head_link (history.items, link);
}
}
if (item->index > 0)
prev_item->delta--;
}
if (item->delta < pika_action_history_item_max_delta (item->index))
item->delta++;
}
/* private functions */
static PikaActionHistoryItem *
pika_action_history_item_new (const gchar *action_name,
gint index,
gint delta)
{
PikaActionHistoryItem *item = g_slice_new (PikaActionHistoryItem);
item->action_name = g_strdup (action_name);
item->index = index;
item->delta = CLAMP (delta, 0, pika_action_history_item_max_delta (index));
return item;
}
static void
pika_action_history_item_free (PikaActionHistoryItem *item)
{
g_free (item->action_name);
g_slice_free (PikaActionHistoryItem, item);
}
static gint
pika_action_history_item_max_delta (gint index)
{
return floor (MAX_DELTA * exp (log (MAX_DELTA_FALLOFF) * index));
}

View File

@ -0,0 +1,50 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikaaction-history.h
* Copyright (C) 2013 Jehan <jehan at girinstud.io>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __PIKA_ACTION_HISTORY_H__
#define __PIKA_ACTION_HISTORY_H__
typedef gboolean (* PikaActionMatchFunc) (PikaAction *action,
const gchar *keyword,
gint *section,
Pika *pika);
void pika_action_history_init (Pika *pika);
void pika_action_history_exit (Pika *pika);
void pika_action_history_clear (Pika *pika);
GList * pika_action_history_search (Pika *pika,
PikaActionMatchFunc match_func,
const gchar *keyword);
gboolean pika_action_history_is_blacklisted_action (const gchar *action_name);
gboolean pika_action_history_is_excluded_action (const gchar *action_name);
void pika_action_history_action_activated (PikaAction *action);
#endif /* __PIKA_ACTION_HISTORY_H__ */

1410
app/widgets/pikaaction.c Normal file

File diff suppressed because it is too large Load Diff

163
app/widgets/pikaaction.h Normal file
View File

@ -0,0 +1,163 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikaaction.h
* Copyright (C) 2004-2019 Michael Natterer <mitch@gimp.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __PIKA_ACTION_H__
#define __PIKA_ACTION_H__
G_BEGIN_DECLS
#include "core/pikaobject.h"
#define PIKA_TYPE_ACTION (pika_action_get_type ())
G_DECLARE_INTERFACE (PikaAction, pika_action, PIKA, ACTION, PikaObject)
enum
{
PIKA_ACTION_PROP_0,
PIKA_ACTION_PROP_CONTEXT,
PIKA_ACTION_PROP_SENSITIVE,
PIKA_ACTION_PROP_VISIBLE,
PIKA_ACTION_PROP_LABEL,
PIKA_ACTION_PROP_SHORT_LABEL,
PIKA_ACTION_PROP_TOOLTIP,
PIKA_ACTION_PROP_ICON_NAME,
PIKA_ACTION_PROP_ICON,
PIKA_ACTION_PROP_COLOR,
PIKA_ACTION_PROP_VIEWABLE,
PIKA_ACTION_PROP_ELLIPSIZE,
PIKA_ACTION_PROP_MAX_WIDTH_CHARS,
PIKA_ACTION_PROP_LAST = PIKA_ACTION_PROP_MAX_WIDTH_CHARS,
};
struct _PikaActionInterface
{
GTypeInterface base_interface;
/* Signals */
void (* activate) (PikaAction *action,
GVariant *value);
void (* change_state) (PikaAction *action,
GVariant *value);
void (* accels_changed) (PikaAction *action,
const gchar **accels);
};
void pika_action_init (PikaAction *action);
void pika_action_emit_activate (PikaAction *action,
GVariant *value);
void pika_action_emit_change_state (PikaAction *action,
GVariant *value);
const gchar * pika_action_get_name (PikaAction *action);
PikaActionGroup * pika_action_get_group (PikaAction *action);
void pika_action_set_label (PikaAction *action,
const gchar *label);
const gchar * pika_action_get_label (PikaAction *action);
void pika_action_set_short_label (PikaAction *action,
const gchar *label);
const gchar * pika_action_get_short_label (PikaAction *action);
void pika_action_set_tooltip (PikaAction *action,
const gchar *tooltip);
const gchar * pika_action_get_tooltip (PikaAction *action);
void pika_action_set_icon_name (PikaAction *action,
const gchar *icon_name);
const gchar * pika_action_get_icon_name (PikaAction *action);
void pika_action_set_gicon (PikaAction *action,
GIcon *icon);
GIcon * pika_action_get_gicon (PikaAction *action);
void pika_action_set_help_id (PikaAction *action,
const gchar *help_id);
const gchar * pika_action_get_help_id (PikaAction *action);
void pika_action_set_visible (PikaAction *action,
gboolean visible);
gboolean pika_action_get_visible (PikaAction *action);
gboolean pika_action_is_visible (PikaAction *action);
void pika_action_set_sensitive (PikaAction *action,
gboolean sensitive,
const gchar *reason);
gboolean pika_action_get_sensitive (PikaAction *action,
const gchar **reason);
gboolean pika_action_is_sensitive (PikaAction *action,
const gchar **reason);
void pika_action_set_accels (PikaAction *action,
const gchar **accels);
const gchar ** pika_action_get_default_accels (PikaAction *action);
const gchar ** pika_action_get_accels (PikaAction *action);
gchar ** pika_action_get_display_accels (PikaAction *action);
gboolean pika_action_use_default_accels (PikaAction *action);
const gchar * pika_action_get_menu_path (PikaAction *action);
void pika_action_activate (PikaAction *action);
gint pika_action_name_compare (PikaAction *action1,
PikaAction *action2);
gboolean pika_action_is_gui_blacklisted (const gchar *action_name);
PikaContext * pika_action_get_context (PikaAction *action);
PikaViewable * pika_action_get_viewable (PikaAction *action);
/* Protected functions. */
void pika_action_install_properties (GObjectClass *klass);
void pika_action_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
void pika_action_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
void pika_action_set_proxy (PikaAction *action,
GtkWidget *proxy);
/* Friend functions. */
void pika_action_set_group (PikaAction *action,
PikaActionGroup *group);
void pika_action_set_default_accels (PikaAction *action,
const gchar **accels);
void pika_action_set_menu_path (PikaAction *action,
const gchar *menu_path);
G_END_DECLS
#endif /* __PIKA_ACTION_H__ */

View File

@ -0,0 +1,147 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikaactioneditor.c
* Copyright (C) 2008 Michael Natterer <mitch@gimp.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gegl.h>
#include <gtk/gtk.h>
#include "libpikawidgets/pikawidgets.h"
#include "widgets-types.h"
#include "core/pika.h"
#include "pikaactioneditor.h"
#include "pikaactionview.h"
#include "pika-intl.h"
/* local function prototypes */
static void pika_action_editor_filter_clear (GtkEntry *entry);
static void pika_action_editor_filter_changed (GtkEntry *entry,
PikaActionEditor *editor);
G_DEFINE_TYPE (PikaActionEditor, pika_action_editor, GTK_TYPE_BOX)
#define parent_class pika_action_editor_parent_class
static void
pika_action_editor_class_init (PikaActionEditorClass *klass)
{
}
static void
pika_action_editor_init (PikaActionEditor *editor)
{
GtkWidget *hbox;
GtkWidget *label;
GtkWidget *entry;
gtk_orientable_set_orientation (GTK_ORIENTABLE (editor),
GTK_ORIENTATION_VERTICAL);
gtk_box_set_spacing (GTK_BOX (editor), 12);
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
gtk_box_pack_start (GTK_BOX (editor), hbox, FALSE, FALSE, 0);
gtk_widget_show (hbox);
label = gtk_label_new_with_mnemonic (_("_Search:"));
gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
gtk_widget_show (label);
entry = gtk_entry_new ();
gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
gtk_widget_show (entry);
gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry);
gtk_entry_set_icon_from_icon_name (GTK_ENTRY (entry),
GTK_ENTRY_ICON_SECONDARY, "edit-clear");
gtk_entry_set_icon_activatable (GTK_ENTRY (entry),
GTK_ENTRY_ICON_SECONDARY, TRUE);
gtk_entry_set_icon_sensitive (GTK_ENTRY (entry),
GTK_ENTRY_ICON_SECONDARY, FALSE);
g_signal_connect (entry, "icon-press",
G_CALLBACK (pika_action_editor_filter_clear),
NULL);
g_signal_connect (entry, "changed",
G_CALLBACK (pika_action_editor_filter_changed),
editor);
}
GtkWidget *
pika_action_editor_new (Pika *pika,
const gchar *select_action,
gboolean show_shortcuts)
{
PikaActionEditor *editor;
GtkWidget *scrolled_window;
g_return_val_if_fail (PIKA_IS_PIKA (pika), NULL);
editor = g_object_new (PIKA_TYPE_ACTION_EDITOR, NULL);
scrolled_window = gtk_scrolled_window_new (NULL, NULL);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window),
GTK_SHADOW_IN);
gtk_box_pack_start (GTK_BOX (editor), scrolled_window, TRUE, TRUE, 0);
gtk_widget_show (scrolled_window);
editor->view = pika_action_view_new (pika, select_action, show_shortcuts);
gtk_widget_set_size_request (editor->view, 300, 400);
gtk_container_add (GTK_CONTAINER (scrolled_window), editor->view);
gtk_widget_show (editor->view);
return GTK_WIDGET (editor);
}
/* private functions */
static void
pika_action_editor_filter_clear (GtkEntry *entry)
{
gtk_entry_set_text (entry, "");
}
static void
pika_action_editor_filter_changed (GtkEntry *entry,
PikaActionEditor *editor)
{
pika_action_view_set_filter (PIKA_ACTION_VIEW (editor->view),
gtk_entry_get_text (entry));
gtk_entry_set_icon_sensitive (entry,
GTK_ENTRY_ICON_SECONDARY,
gtk_entry_get_text_length (entry) > 0);
}

View File

@ -0,0 +1,59 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikaactioneditor.h
* Copyright (C) 2008 Michael Natterer <mitch@gimp.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __PIKA_ACTION_EDITOR_H__
#define __PIKA_ACTION_EDITOR_H__
#define PIKA_TYPE_ACTION_EDITOR (pika_action_editor_get_type ())
#define PIKA_ACTION_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIKA_TYPE_ACTION_EDITOR, PikaActionEditor))
#define PIKA_ACTION_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIKA_TYPE_ACTION_EDITOR, PikaActionEditorClass))
#define PIKA_IS_ACTION_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIKA_TYPE_ACTION_EDITOR))
#define PIKA_IS_ACTION_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIKA_TYPE_ACTION_EDITOR))
#define PIKA_ACTION_EDITOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIKA_TYPE_ACTION_EDITOR, PikaActionEditorClass))
typedef struct _PikaActionEditorClass PikaActionEditorClass;
struct _PikaActionEditor
{
GtkBox parent_instance;
GtkWidget *view;
};
struct _PikaActionEditorClass
{
GtkBoxClass parent_class;
};
GType pika_action_editor_get_type (void) G_GNUC_CONST;
GtkWidget * pika_action_editor_new (Pika *pika,
const gchar *select_action,
gboolean show_shortcuts);
#endif /* __PIKA_ACTION_EDITOR_H__ */

View File

@ -0,0 +1,249 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikaactionfactory.c
* Copyright (C) 2004 Michael Natterer <mitch@gimp.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <string.h>
#include <gegl.h>
#include <gtk/gtk.h>
#include "libpikawidgets/pikawidgets.h"
#include "widgets-types.h"
#include "core/pika.h"
#include "pikaaction.h"
#include "pikaactionfactory.h"
#include "pikaactiongroup.h"
static void pika_action_factory_finalize (GObject *object);
static void pika_action_factory_action_removed (PikaActionGroup *group,
gchar *action_name,
PikaActionFactory *factory);
G_DEFINE_TYPE (PikaActionFactory, pika_action_factory, PIKA_TYPE_OBJECT)
#define parent_class pika_action_factory_parent_class
static void
pika_action_factory_class_init (PikaActionFactoryClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = pika_action_factory_finalize;
}
static void
pika_action_factory_init (PikaActionFactory *factory)
{
factory->pika = NULL;
factory->registered_groups = NULL;
}
static void
pika_action_factory_finalize (GObject *object)
{
PikaActionFactory *factory = PIKA_ACTION_FACTORY (object);
GList *list;
for (list = factory->registered_groups; list; list = g_list_next (list))
{
PikaActionFactoryEntry *entry = list->data;
g_free (entry->identifier);
g_free (entry->label);
g_free (entry->icon_name);
g_hash_table_unref (entry->groups);
g_slice_free (PikaActionFactoryEntry, entry);
}
g_list_free (factory->registered_groups);
factory->registered_groups = NULL;
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
pika_action_factory_action_removed (PikaActionGroup *group,
gchar *action_name,
PikaActionFactory *factory)
{
GList *list;
for (list = factory->registered_groups; list; list = g_list_next (list))
{
PikaActionFactoryEntry *entry = list->data;
GList *groups;
GList *iter;
groups = g_hash_table_get_values (entry->groups);
for (iter = groups; iter; iter = iter->next)
{
if (group != iter->data)
{
if (g_action_group_has_action (G_ACTION_GROUP (iter->data), action_name))
break;
}
}
g_list_free (groups);
if (iter != NULL)
break;
}
if (list == NULL)
g_action_map_remove_action (G_ACTION_MAP (factory->pika->app), action_name);
}
/* Public functions */
PikaActionFactory *
pika_action_factory_new (Pika *pika)
{
PikaActionFactory *factory;
g_return_val_if_fail (PIKA_IS_PIKA (pika), NULL);
factory = g_object_new (PIKA_TYPE_ACTION_FACTORY, NULL);
factory->pika = pika;
return factory;
}
void
pika_action_factory_group_register (PikaActionFactory *factory,
const gchar *identifier,
const gchar *label,
const gchar *icon_name,
PikaActionGroupSetupFunc setup_func,
PikaActionGroupUpdateFunc update_func)
{
PikaActionFactoryEntry *entry;
g_return_if_fail (PIKA_IS_ACTION_FACTORY (factory));
g_return_if_fail (identifier != NULL);
g_return_if_fail (label != NULL);
g_return_if_fail (setup_func != NULL);
g_return_if_fail (update_func != NULL);
entry = g_slice_new0 (PikaActionFactoryEntry);
entry->identifier = g_strdup (identifier);
entry->label = g_strdup (label);
entry->icon_name = g_strdup (icon_name);
entry->setup_func = setup_func;
entry->update_func = update_func;
entry->groups = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref);
factory->registered_groups = g_list_prepend (factory->registered_groups,
entry);
}
PikaActionGroup *
pika_action_factory_get_group (PikaActionFactory *factory,
const gchar *identifier,
gpointer user_data)
{
GList *list;
g_return_val_if_fail (PIKA_IS_ACTION_FACTORY (factory), NULL);
g_return_val_if_fail (identifier != NULL, NULL);
for (list = factory->registered_groups; list; list = g_list_next (list))
{
PikaActionFactoryEntry *entry = list->data;
if (! strcmp (entry->identifier, identifier))
{
PikaActionGroup *group;
group = g_hash_table_lookup (entry->groups, user_data);
if (group == NULL)
{
group = pika_action_group_new (factory->pika,
entry->identifier,
entry->label,
entry->icon_name,
user_data,
entry->update_func);
if (entry->setup_func)
entry->setup_func (group);
g_hash_table_insert (entry->groups, user_data, group);
g_signal_connect_object (group, "action-removed",
G_CALLBACK (pika_action_factory_action_removed),
factory, G_CONNECT_AFTER);
}
return group;
}
}
g_warning ("%s: no entry registered for \"%s\"",
G_STRFUNC, identifier);
return NULL;
}
void
pika_action_factory_delete_group (PikaActionFactory *factory,
const gchar *identifier,
gpointer user_data)
{
GList *list;
g_return_if_fail (PIKA_IS_ACTION_FACTORY (factory));
g_return_if_fail (identifier != NULL);
for (list = factory->registered_groups; list; list = g_list_next (list))
{
PikaActionFactoryEntry *entry = list->data;
if (g_strcmp0 (entry->identifier, identifier) == 0)
{
if (! g_hash_table_remove (entry->groups, user_data))
g_warning ("%s: no PikaActionGroup for (id \"%s\", data %p)",
G_STRFUNC, identifier, user_data);
return;
}
}
g_warning ("%s: no entry registered for \"%s\"",
G_STRFUNC, identifier);
}

View File

@ -0,0 +1,89 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikaactionfactory.h
* Copyright (C) 2004 Michael Natterer <mitch@gimp.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __PIKA_ACTION_FACTORY_H__
#define __PIKA_ACTION_FACTORY_H__
#include "core/pikaobject.h"
typedef struct _PikaActionFactoryEntry PikaActionFactoryEntry;
struct _PikaActionFactoryEntry
{
gchar *identifier;
gchar *label;
gchar *icon_name;
PikaActionGroupSetupFunc setup_func;
PikaActionGroupUpdateFunc update_func;
GHashTable *groups;
};
#define PIKA_TYPE_ACTION_FACTORY (pika_action_factory_get_type ())
#define PIKA_ACTION_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIKA_TYPE_ACTION_FACTORY, PikaActionFactory))
#define PIKA_ACTION_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIKA_TYPE_ACTION_FACTORY, PikaActionFactoryClass))
#define PIKA_IS_ACTION_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIKA_TYPE_ACTION_FACTORY))
#define PIKA_IS_ACTION_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIKA_TYPE_ACTION_FACTORY))
#define PIKA_ACTION_FACTORY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIKA_TYPE_ACTION_FACTORY, PikaActionFactoryClass))
typedef struct _PikaActionFactoryClass PikaActionFactoryClass;
struct _PikaActionFactory
{
PikaObject parent_instance;
Pika *pika;
GList *registered_groups;
};
struct _PikaActionFactoryClass
{
PikaObjectClass parent_class;
};
GType pika_action_factory_get_type (void) G_GNUC_CONST;
PikaActionFactory * pika_action_factory_new (Pika *pika);
void pika_action_factory_group_register (PikaActionFactory *factory,
const gchar *identifier,
const gchar *label,
const gchar *icon_name,
PikaActionGroupSetupFunc setup_func,
PikaActionGroupUpdateFunc update_func);
PikaActionGroup * pika_action_factory_get_group (PikaActionFactory *factory,
const gchar *identifier,
gpointer user_data);
void pika_action_factory_delete_group (PikaActionFactory *factory,
const gchar *identifier,
gpointer user_data);
#endif /* __PIKA_ACTION_FACTORY_H__ */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,263 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikaactiongroup.h
* Copyright (C) 2004 Michael Natterer <mitch@gimp.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __PIKA_ACTION_GROUP_H__
#define __PIKA_ACTION_GROUP_H__
#include "core/pikaobject.h"
#define PIKA_TYPE_ACTION_GROUP (pika_action_group_get_type ())
#define PIKA_ACTION_GROUP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIKA_TYPE_ACTION_GROUP, PikaActionGroup))
#define PIKA_ACTION_GROUP_CLASS(vtable) (G_TYPE_CHECK_CLASS_CAST ((vtable), PIKA_TYPE_ACTION_GROUP, PikaActionGroupClass))
#define PIKA_IS_ACTION_GROUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIKA_TYPE_ACTION_GROUP))
#define PIKA_IS_ACTION_GROUP_CLASS(vtable) (G_TYPE_CHECK_CLASS_TYPE ((vtable), PIKA_TYPE_ACTION_GROUP))
#define PIKA_ACTION_GROUP_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), PIKA_TYPE_ACTION_GROUP, PikaActionGroupClass))
typedef struct _PikaActionGroupClass PikaActionGroupClass;
struct _PikaActionGroup
{
PikaObject parent_instance;
Pika *pika;
gchar *label;
gchar *icon_name;
gpointer user_data;
PikaActionGroupUpdateFunc update_func;
GList *actions;
};
struct _PikaActionGroupClass
{
PikaObjectClass parent_class;
GHashTable *groups;
};
typedef void (* PikaActionCallback) (PikaAction *action,
GVariant *value,
gpointer data);
struct _PikaActionEntry
{
const gchar *name;
const gchar *icon_name;
const gchar *label;
const gchar *short_label;
const gchar *accelerator[4];
const gchar *tooltip;
PikaActionCallback callback;
const gchar *help_id;
};
struct _PikaToggleActionEntry
{
const gchar *name;
const gchar *icon_name;
const gchar *label;
const gchar *short_label;
const gchar *accelerator[4];
const gchar *tooltip;
PikaActionCallback callback;
gboolean is_active;
const gchar *help_id;
};
struct _PikaRadioActionEntry
{
const gchar *name;
const gchar *icon_name;
const gchar *label;
const gchar *short_label;
const gchar *accelerator[4];
const gchar *tooltip;
gint value;
const gchar *help_id;
};
struct _PikaEnumActionEntry
{
const gchar *name;
const gchar *icon_name;
const gchar *label;
const gchar *short_label;
const gchar *accelerator[4];
const gchar *tooltip;
gint value;
gboolean value_variable;
const gchar *help_id;
};
struct _PikaStringActionEntry
{
const gchar *name;
const gchar *icon_name;
const gchar *label;
const gchar *short_label;
const gchar *accelerator[4];
const gchar *tooltip;
const gchar *value;
const gchar *help_id;
};
struct _PikaDoubleActionEntry
{
const gchar *name;
const gchar *icon_name;
const gchar *label;
const gchar *short_label;
const gchar *accelerator[4];
const gchar *tooltip;
const gdouble value;
const gchar *help_id;
};
struct _PikaProcedureActionEntry
{
const gchar *name;
const gchar *icon_name;
const gchar *label;
const gchar *accelerator;
const gchar *tooltip;
PikaProcedure *procedure;
const gchar *help_id;
};
GType pika_action_group_get_type (void) G_GNUC_CONST;
PikaActionGroup *pika_action_group_new (Pika *pika,
const gchar *name,
const gchar *label,
const gchar *icon_name,
gpointer user_data,
PikaActionGroupUpdateFunc update_func);
GList *pika_action_groups_from_name (const gchar *name);
const gchar * pika_action_group_get_name (PikaActionGroup *group);
void pika_action_group_add_action (PikaActionGroup *action_group,
PikaAction *action);
void pika_action_group_add_action_with_accel (PikaActionGroup *action_group,
PikaAction *action,
const gchar * const*accelerators);
void pika_action_group_remove_action (PikaActionGroup *action_group,
PikaAction *action);
PikaAction * pika_action_group_get_action (PikaActionGroup *group,
const gchar *action_name);
GList * pika_action_group_list_actions (PikaActionGroup *group);
void pika_action_group_update (PikaActionGroup *group,
gpointer update_data);
void pika_action_group_add_actions (PikaActionGroup *group,
const gchar *msg_context,
const PikaActionEntry *entries,
guint n_entries);
void pika_action_group_add_toggle_actions (PikaActionGroup *group,
const gchar *msg_context,
const PikaToggleActionEntry *entries,
guint n_entries);
GSList *pika_action_group_add_radio_actions (PikaActionGroup *group,
const gchar *msg_context,
const PikaRadioActionEntry *entries,
guint n_entries,
GSList *radio_group,
gint value,
PikaActionCallback callback);
void pika_action_group_add_enum_actions (PikaActionGroup *group,
const gchar *msg_context,
const PikaEnumActionEntry *entries,
guint n_entries,
PikaActionCallback callback);
void pika_action_group_add_string_actions (PikaActionGroup *group,
const gchar *msg_context,
const PikaStringActionEntry *entries,
guint n_entries,
PikaActionCallback callback);
void pika_action_group_add_double_actions (PikaActionGroup *group,
const gchar *msg_context,
const PikaDoubleActionEntry *entries,
guint n_entries,
PikaActionCallback callback);
void pika_action_group_add_procedure_actions(PikaActionGroup *group,
const PikaProcedureActionEntry *entries,
guint n_entries,
PikaActionCallback callback);
void pika_action_group_remove_action_and_accel (PikaActionGroup *group,
PikaAction *action);
void pika_action_group_set_action_visible (PikaActionGroup *group,
const gchar *action_name,
gboolean visible);
void pika_action_group_set_action_sensitive (PikaActionGroup *group,
const gchar *action_name,
gboolean sensitive,
const gchar *reason);
void pika_action_group_set_action_active (PikaActionGroup *group,
const gchar *action_name,
gboolean active);
void pika_action_group_set_action_label (PikaActionGroup *group,
const gchar *action_name,
const gchar *label);
void pika_action_group_set_action_pixbuf (PikaActionGroup *group,
const gchar *action_name,
GdkPixbuf *pixbuf);
void pika_action_group_set_action_tooltip (PikaActionGroup *group,
const gchar *action_name,
const gchar *tooltip);
const gchar * pika_action_group_get_action_tooltip (PikaActionGroup *group,
const gchar *action_name);
void pika_action_group_set_action_color (PikaActionGroup *group,
const gchar *action_name,
const PikaRGB *color,
gboolean set_label);
void pika_action_group_set_action_viewable (PikaActionGroup *group,
const gchar *action_name,
PikaViewable *viewable);
void pika_action_group_set_action_hide_empty (PikaActionGroup *group,
const gchar *action_name,
gboolean hide_empty);
void pika_action_group_set_action_always_show_image (PikaActionGroup *group,
const gchar *action_name,
gboolean always_show_image);
#endif /* __PIKA_ACTION_GROUP_H__ */

View File

@ -0,0 +1,424 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikaaction.c
* Copyright (C) 2004-2019 Michael Natterer <mitch@gimp.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gegl.h>
#include <gtk/gtk.h>
#include "libpikabase/pikabase.h"
#include "widgets-types.h"
#include "pikaaction.h"
#include "pikaactionimpl.h"
#include "pikaaction-history.h"
enum
{
PROP_0,
PROP_ENABLED = PIKA_ACTION_PROP_LAST + 1,
PROP_PARAMETER_TYPE,
PROP_STATE_TYPE,
PROP_STATE
};
enum
{
CHANGE_STATE,
LAST_SIGNAL
};
struct _PikaActionImplPrivate
{
GVariantType *parameter_type;
GVariant *state;
GVariant *state_hint;
gboolean state_set_already;
};
static void pika_action_g_action_iface_init (GActionInterface *iface);
static void pika_action_impl_finalize (GObject *object);
static void pika_action_impl_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec);
static void pika_action_impl_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec);
/* XXX Implementations for our PikaAction are widely inspired by GSimpleAction
* implementations.
*/
static void pika_action_impl_activate (GAction *action,
GVariant *parameter);
static void pika_action_impl_change_state (GAction *action,
GVariant *value);
static gboolean pika_action_impl_get_enabled (GAction *action);
static const GVariantType *
pika_action_impl_get_parameter_type (GAction *action);
static const GVariantType *
pika_action_impl_get_state_type (GAction *action);
static GVariant *
pika_action_impl_get_state (GAction *action);
static GVariant *
pika_action_impl_get_state_hint (GAction *action);
static void pika_action_impl_set_state (PikaAction *pika_action,
GVariant *value);
G_DEFINE_TYPE_WITH_CODE (PikaActionImpl, pika_action_impl, PIKA_TYPE_OBJECT,
G_ADD_PRIVATE (PikaActionImpl)
G_IMPLEMENT_INTERFACE (G_TYPE_ACTION, pika_action_g_action_iface_init)
G_IMPLEMENT_INTERFACE (PIKA_TYPE_ACTION, NULL))
#define parent_class pika_action_impl_parent_class
static guint pika_action_impl_signals[LAST_SIGNAL] = { 0 };
static void
pika_action_impl_class_init (PikaActionImplClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
pika_action_impl_signals[CHANGE_STATE] =
g_signal_new ("change-state",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST | G_SIGNAL_MUST_COLLECT,
G_STRUCT_OFFSET (PikaActionImplClass, change_state),
NULL, NULL, NULL,
G_TYPE_NONE, 1,
G_TYPE_VARIANT);
object_class->finalize = pika_action_impl_finalize;
object_class->set_property = pika_action_impl_set_property;
object_class->get_property = pika_action_impl_get_property;
pika_action_install_properties (object_class);
/**
* PikaAction:enabled:
*
* If @action is currently enabled.
*
* If the action is disabled then calls to g_action_activate() and
* g_action_change_state() have no effect.
**/
g_object_class_install_property (object_class, PROP_ENABLED,
g_param_spec_boolean ("enabled",
"Enabled",
"If the action can be activated",
TRUE,
PIKA_PARAM_READWRITE));
/**
* PikaAction:parameter-type:
*
* The type of the parameter that must be given when activating the
* action.
**/
g_object_class_install_property (object_class, PROP_PARAMETER_TYPE,
g_param_spec_boxed ("parameter-type",
"Parameter Type",
"The type of GVariant passed to activate()",
G_TYPE_VARIANT_TYPE,
PIKA_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY));
/**
* PikaAction:state-type:
*
* The #GVariantType of the state that the action has, or %NULL if the
* action is stateless.
**/
g_object_class_install_property (object_class, PROP_STATE_TYPE,
g_param_spec_boxed ("state-type",
"State Type",
"The type of the state kept by the action",
G_TYPE_VARIANT_TYPE,
PIKA_PARAM_READABLE));
/**
* PikaAction:state:
*
* The state of the action, or %NULL if the action is stateless.
**/
g_object_class_install_property (object_class, PROP_STATE,
g_param_spec_variant ("state",
"State",
"The state the action is in",
G_VARIANT_TYPE_ANY,
NULL,
PIKA_PARAM_READWRITE | G_PARAM_CONSTRUCT));
}
static void
pika_action_g_action_iface_init (GActionInterface *iface)
{
iface->activate = pika_action_impl_activate;
iface->change_state = pika_action_impl_change_state;
iface->get_enabled = pika_action_impl_get_enabled;
iface->get_name = (const gchar* (*) (GAction*)) pika_action_get_name;
iface->get_parameter_type = pika_action_impl_get_parameter_type;
iface->get_state_type = pika_action_impl_get_state_type;
iface->get_state = pika_action_impl_get_state;
iface->get_state_hint = pika_action_impl_get_state_hint;
}
static void
pika_action_impl_init (PikaActionImpl *impl)
{
impl->priv = pika_action_impl_get_instance_private (impl);
impl->priv->state_set_already = FALSE;
pika_action_init (PIKA_ACTION (impl));
}
static void
pika_action_impl_finalize (GObject *object)
{
PikaActionImpl *impl = PIKA_ACTION_IMPL (object);
if (impl->priv->parameter_type)
g_variant_type_free (impl->priv->parameter_type);
if (impl->priv->state)
g_variant_unref (impl->priv->state);
if (impl->priv->state_hint)
g_variant_unref (impl->priv->state_hint);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
pika_action_impl_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
PikaActionImpl *impl = PIKA_ACTION_IMPL (object);
switch (prop_id)
{
case PROP_ENABLED:
g_value_set_boolean (value, pika_action_impl_get_enabled (G_ACTION (impl)));
break;
case PROP_PARAMETER_TYPE:
g_value_set_boxed (value, pika_action_impl_get_parameter_type (G_ACTION (impl)));
break;
case PROP_STATE_TYPE:
g_value_set_boxed (value, pika_action_impl_get_state_type (G_ACTION (impl)));
break;
case PROP_STATE:
g_value_take_variant (value, pika_action_impl_get_state (G_ACTION (impl)));
break;
default:
pika_action_get_property (object, prop_id, value, pspec);
break;
}
}
static void
pika_action_impl_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
PikaActionImpl *impl = PIKA_ACTION_IMPL (object);
switch (prop_id)
{
case PROP_ENABLED:
pika_action_set_sensitive (PIKA_ACTION (impl),
g_value_get_boolean (value), NULL);
break;
case PROP_PARAMETER_TYPE:
impl->priv->parameter_type = g_value_dup_boxed (value);
break;
case PROP_STATE:
/* The first time we see this (during construct) we should just
* take the state as it was handed to us.
*
* After that, we should make sure we go through the same checks
* as the C API.
*/
if (!impl->priv->state_set_already)
{
impl->priv->state = g_value_dup_variant (value);
impl->priv->state_set_already = TRUE;
}
else
{
pika_action_impl_set_state (PIKA_ACTION (impl), g_value_get_variant (value));
}
break;
default:
pika_action_set_property (object, prop_id, value, pspec);
break;
}
}
static void
pika_action_impl_activate (GAction *action,
GVariant *parameter)
{
g_object_add_weak_pointer (G_OBJECT (action), (gpointer) &action);
pika_action_emit_activate (PIKA_ACTION (action), parameter);
if (action != NULL)
{
/* Some actions are self-destructive, such as the "windows-recent-*"
* actions which don't exist after being run. Don't log these.
*/
g_object_remove_weak_pointer (G_OBJECT (action), (gpointer) &action);
pika_action_history_action_activated (PIKA_ACTION (action));
}
}
static void
pika_action_impl_change_state (GAction *action,
GVariant *value)
{
/* If the user connected a signal handler then they are responsible
* for handling state changes.
*/
if (g_signal_has_handler_pending (action, pika_action_impl_signals[CHANGE_STATE], 0, TRUE))
g_signal_emit (action, pika_action_impl_signals[CHANGE_STATE], 0, value);
else
/* If not, then the default behavior is to just set the state. */
pika_action_impl_set_state (PIKA_ACTION (action), value);
}
static gboolean
pika_action_impl_get_enabled (GAction *action)
{
return pika_action_is_sensitive (PIKA_ACTION (action), NULL);
}
static const GVariantType *
pika_action_impl_get_parameter_type (GAction *action)
{
PikaActionImpl *impl = PIKA_ACTION_IMPL (action);
return impl->priv->parameter_type;
}
static const GVariantType *
pika_action_impl_get_state_type (GAction *action)
{
PikaActionImpl *impl = PIKA_ACTION_IMPL (action);
if (impl->priv->state != NULL)
return g_variant_get_type (impl->priv->state);
else
return NULL;
}
static GVariant *
pika_action_impl_get_state (GAction *action)
{
PikaActionImpl *impl = PIKA_ACTION_IMPL (action);
return impl->priv->state ? g_variant_ref (impl->priv->state) : NULL;
}
static GVariant *
pika_action_impl_get_state_hint (GAction *action)
{
PikaActionImpl *impl = PIKA_ACTION_IMPL (action);
if (impl->priv->state_hint != NULL)
return g_variant_ref (impl->priv->state_hint);
else
return NULL;
}
/* public functions */
PikaAction *
pika_action_impl_new (const gchar *name,
const gchar *label,
const gchar *short_label,
const gchar *tooltip,
const gchar *icon_name,
const gchar *help_id,
PikaContext *context)
{
PikaAction *action;
action = g_object_new (PIKA_TYPE_ACTION_IMPL,
"name", name,
"label", label,
"short-label", short_label,
"tooltip", tooltip,
"icon-name", icon_name,
"context", context,
NULL);
pika_action_set_help_id (action, help_id);
return action;
}
/* private functions */
static void
pika_action_impl_set_state (PikaAction *pika_action,
GVariant *value)
{
PikaActionImpl *impl;
const GVariantType *state_type;
g_return_if_fail (PIKA_IS_ACTION (pika_action));
g_return_if_fail (value != NULL);
impl = PIKA_ACTION_IMPL (pika_action);
state_type = impl->priv->state ? g_variant_get_type (impl->priv->state) : NULL;
g_return_if_fail (state_type != NULL);
g_return_if_fail (g_variant_is_of_type (value, state_type));
g_variant_ref_sink (value);
if (! impl->priv->state || ! g_variant_equal (impl->priv->state, value))
{
if (impl->priv->state)
g_variant_unref (impl->priv->state);
impl->priv->state = g_variant_ref (value);
g_object_notify (G_OBJECT (impl), "state");
}
g_variant_unref (value);
}

View File

@ -0,0 +1,67 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikaactionimpl.h
* Copyright (C) 2004-2019 Michael Natterer <mitch@gimp.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __PIKA_ACTION_IMPL_H__
#define __PIKA_ACTION_IMPL_H__
#include "core/pikaobject.h"
#define PIKA_TYPE_ACTION_IMPL (pika_action_impl_get_type ())
#define PIKA_ACTION_IMPL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIKA_TYPE_ACTION_IMPL, PikaActionImpl))
#define PIKA_ACTION_IMPL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIKA_TYPE_ACTION_IMPL, PikaActionImplClass))
#define PIKA_IS_ACTION_IMPL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIKA_TYPE_ACTION_IMPL))
#define PIKA_IS_ACTION_IMPL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), PIKA_TYPE_ACTION_IMPL))
#define PIKA_ACTION_IMPL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PIKA_TYPE_ACTION_IMPL, PikaActionImplClass))
typedef struct _PikaActionImplClass PikaActionImplClass;
typedef struct _PikaActionImplPrivate PikaActionImplPrivate;
struct _PikaActionImpl
{
PikaObject parent_instance;
PikaActionImplPrivate *priv;
};
struct _PikaActionImplClass
{
PikaObjectClass parent_class;
/* signals */
void (* change_state) (PikaActionImpl *impl,
GVariant *value);
};
GType pika_action_impl_get_type (void) G_GNUC_CONST;
PikaAction * pika_action_impl_new (const gchar *name,
const gchar *label,
const gchar *short_label,
const gchar *tooltip,
const gchar *icon_name,
const gchar *help_id,
PikaContext *context);
#endif /* __PIKA_ACTION_IMPL_H__ */

View File

@ -0,0 +1,831 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikaactionview.c
* Copyright (C) 2004-2005 Michael Natterer <mitch@gimp.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <string.h>
#include <gegl.h>
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
#include "libpikabase/pikabase.h"
#include "libpikawidgets/pikawidgets.h"
#include "widgets-types.h"
#include "core/pika.h"
#include "pikaaction.h"
#include "pikaactiongroup.h"
#include "pikaactionview.h"
#include "pikamessagebox.h"
#include "pikamessagedialog.h"
#include "pika-intl.h"
/* local function prototypes */
static void pika_action_view_finalize (GObject *object);
static void pika_action_view_select_path (PikaActionView *view,
GtkTreePath *path);
static void pika_action_view_accels_changed (PikaAction *action,
gchar **accels,
PikaActionView *view);
static void pika_action_view_accel_edited (GtkCellRendererAccel *accel,
const char *path_string,
guint accel_key,
GdkModifierType accel_mask,
guint hardware_keycode,
PikaActionView *view);
static void pika_action_view_accel_cleared (GtkCellRendererAccel *accel,
const char *path_string,
PikaActionView *view);
G_DEFINE_TYPE (PikaActionView, pika_action_view, GTK_TYPE_TREE_VIEW)
#define parent_class pika_action_view_parent_class
static void
pika_action_view_class_init (PikaActionViewClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = pika_action_view_finalize;
}
static void
pika_action_view_init (PikaActionView *view)
{
}
static void
pika_action_view_finalize (GObject *object)
{
PikaActionView *view = PIKA_ACTION_VIEW (object);
if (view->pika)
{
if (view->show_shortcuts)
{
gchar **actions;
actions = g_action_group_list_actions (G_ACTION_GROUP (view->pika->app));
for (gint i = 0; actions[i] != NULL; i++)
{
GAction *action;
if (pika_action_is_gui_blacklisted (actions[i]))
continue;
action = g_action_map_lookup_action (G_ACTION_MAP (view->pika->app), actions[i]);
g_signal_handlers_disconnect_by_func (action,
pika_action_view_accels_changed,
view);
}
g_strfreev (actions);
}
g_clear_object (&view->pika);
}
g_clear_pointer (&view->filter, g_free);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static gint
pika_action_view_name_compare (const void *name1,
const void *name2)
{
return strcmp (*(gchar **) name1, *(gchar **) name2);
}
GtkWidget *
pika_action_view_new (Pika *pika,
const gchar *select_action,
gboolean show_shortcuts)
{
gchar **actions;
gchar *group_name = NULL;
GtkTreeView *view;
GtkTreeViewColumn *column;
GtkCellRenderer *cell;
GtkTreeStore *store;
GtkTreeModel *filter;
GtkTreePath *select_path = NULL;
g_return_val_if_fail (PIKA_IS_PIKA (pika), NULL);
store = gtk_tree_store_new (PIKA_ACTION_VIEW_N_COLUMNS,
G_TYPE_BOOLEAN, /* COLUMN_VISIBLE */
PIKA_TYPE_ACTION, /* COLUMN_ACTION */
G_TYPE_STRING, /* COLUMN_ICON_NAME */
G_TYPE_STRING, /* COLUMN_LABEL */
G_TYPE_STRING, /* COLUMN_LABEL_CASEFOLD */
G_TYPE_STRING, /* COLUMN_NAME */
G_TYPE_UINT, /* COLUMN_ACCEL_KEY */
GDK_TYPE_MODIFIER_TYPE); /* COLUMN_ACCEL_MASK */
actions = g_action_group_list_actions (G_ACTION_GROUP (pika->app));
qsort (actions, g_strv_length (actions), sizeof (gchar *), pika_action_view_name_compare);
for (gint i = 0; actions[i] != NULL; i++)
{
gchar **split_name;
GtkTreeIter group_iter;
GAction *action;
const gchar *icon_name;
gchar *label;
gchar *label_casefold;
guint accel_key = 0;
GdkModifierType accel_mask = 0;
GtkTreeIter action_iter;
if (pika_action_is_gui_blacklisted (actions[i]))
continue;
split_name = g_strsplit (actions[i], "-", 2);
if (group_name == NULL || g_strcmp0 (group_name, split_name[0]) != 0)
{
/* Since we sorted alphabetically and we use the first part of the
* action name as group name, we ensure that we create each group only
* once.
*/
g_free (group_name);
group_name = g_strdup (split_name[0]);
gtk_tree_store_append (store, &group_iter, NULL);
gtk_tree_store_set (store, &group_iter,
/* TODO: get back PikaActionGroup info? */
/*PIKA_ACTION_VIEW_COLUMN_ICON_NAME, group->icon_name,*/
/*PIKA_ACTION_VIEW_COLUMN_LABEL, group->label,*/
PIKA_ACTION_VIEW_COLUMN_LABEL, group_name,
-1);
}
g_strfreev (split_name);
action = g_action_map_lookup_action (G_ACTION_MAP (pika->app), actions[i]);
g_return_val_if_fail (PIKA_IS_ACTION (action), NULL);
icon_name = pika_action_get_icon_name (PIKA_ACTION (action));
label = pika_strip_uline (pika_action_get_label (PIKA_ACTION (action)));
if (! (label && strlen (label)))
{
g_free (label);
label = g_strdup (actions[i]);
}
label_casefold = g_utf8_casefold (label, -1);
if (show_shortcuts)
{
const gchar **accels = NULL;
accels = pika_action_get_accels (PIKA_ACTION (action));
/* TODO GAction: support multiple accelerators! */
if (accels && accels[0])
gtk_accelerator_parse (accels[0], &accel_key, &accel_mask);
}
gtk_tree_store_append (store, &action_iter, &group_iter);
gtk_tree_store_set (store, &action_iter,
PIKA_ACTION_VIEW_COLUMN_VISIBLE, TRUE,
PIKA_ACTION_VIEW_COLUMN_ACTION, action,
PIKA_ACTION_VIEW_COLUMN_ICON_NAME, icon_name,
PIKA_ACTION_VIEW_COLUMN_LABEL, label,
PIKA_ACTION_VIEW_COLUMN_LABEL_CASEFOLD, label_casefold,
PIKA_ACTION_VIEW_COLUMN_NAME, actions[i],
PIKA_ACTION_VIEW_COLUMN_ACCEL_KEY, accel_key,
PIKA_ACTION_VIEW_COLUMN_ACCEL_MASK, accel_mask,
-1);
g_free (label);
g_free (label_casefold);
if (select_action && ! strcmp (select_action, actions[i]))
select_path = gtk_tree_model_get_path (GTK_TREE_MODEL (store),
&action_iter);
}
g_free (group_name);
filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (store), NULL);
g_object_unref (store);
view = g_object_new (PIKA_TYPE_ACTION_VIEW,
"model", filter,
"rules-hint", TRUE,
NULL);
g_object_unref (filter);
gtk_tree_model_filter_set_visible_column (GTK_TREE_MODEL_FILTER (filter),
PIKA_ACTION_VIEW_COLUMN_VISIBLE);
PIKA_ACTION_VIEW (view)->pika = g_object_ref (pika);
PIKA_ACTION_VIEW (view)->show_shortcuts = show_shortcuts;
gtk_tree_view_set_search_column (GTK_TREE_VIEW (view),
PIKA_ACTION_VIEW_COLUMN_LABEL);
column = gtk_tree_view_column_new ();
gtk_tree_view_column_set_title (column, _("Action"));
cell = gtk_cell_renderer_pixbuf_new ();
gtk_tree_view_column_pack_start (column, cell, FALSE);
gtk_tree_view_column_set_attributes (column, cell,
"icon-name",
PIKA_ACTION_VIEW_COLUMN_ICON_NAME,
NULL);
cell = gtk_cell_renderer_text_new ();
gtk_tree_view_column_pack_start (column, cell, TRUE);
gtk_tree_view_column_set_attributes (column, cell,
"text",
PIKA_ACTION_VIEW_COLUMN_LABEL,
NULL);
gtk_tree_view_append_column (view, column);
if (show_shortcuts)
{
for (gint i = 0; actions[i] != NULL; i++)
{
GAction *action;
if (pika_action_is_gui_blacklisted (actions[i]))
continue;
action = g_action_map_lookup_action (G_ACTION_MAP (pika->app), actions[i]);
g_signal_connect (action, "accels-changed",
G_CALLBACK (pika_action_view_accels_changed),
view);
}
column = gtk_tree_view_column_new ();
gtk_tree_view_column_set_title (column, _("Shortcut"));
cell = gtk_cell_renderer_accel_new ();
g_object_set (cell,
"mode", GTK_CELL_RENDERER_MODE_EDITABLE,
"editable", TRUE,
NULL);
gtk_tree_view_column_pack_start (column, cell, TRUE);
gtk_tree_view_column_set_attributes (column, cell,
"accel-key",
PIKA_ACTION_VIEW_COLUMN_ACCEL_KEY,
"accel-mods",
PIKA_ACTION_VIEW_COLUMN_ACCEL_MASK,
NULL);
g_signal_connect (cell, "accel-edited",
G_CALLBACK (pika_action_view_accel_edited),
view);
g_signal_connect (cell, "accel-cleared",
G_CALLBACK (pika_action_view_accel_cleared),
view);
gtk_tree_view_append_column (view, column);
}
g_strfreev (actions);
column = gtk_tree_view_column_new ();
gtk_tree_view_column_set_title (column, _("Name"));
cell = gtk_cell_renderer_text_new ();
gtk_tree_view_column_pack_start (column, cell, TRUE);
gtk_tree_view_column_set_attributes (column, cell,
"text",
PIKA_ACTION_VIEW_COLUMN_NAME,
NULL);
gtk_tree_view_append_column (view, column);
if (select_path)
{
pika_action_view_select_path (PIKA_ACTION_VIEW (view), select_path);
gtk_tree_path_free (select_path);
}
return GTK_WIDGET (view);
}
void
pika_action_view_set_filter (PikaActionView *view,
const gchar *filter)
{
GtkTreeSelection *sel;
GtkTreeModel *filtered_model;
GtkTreeModel *model;
GtkTreeIter iter;
gboolean iter_valid;
GtkTreeRowReference *selected_row = NULL;
g_return_if_fail (PIKA_IS_ACTION_VIEW (view));
filtered_model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filtered_model));
if (filter && ! strlen (filter))
filter = NULL;
g_clear_pointer (&view->filter, g_free);
if (filter)
view->filter = g_utf8_casefold (filter, -1);
sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
if (gtk_tree_selection_get_selected (sel, NULL, &iter))
{
GtkTreePath *path = gtk_tree_model_get_path (filtered_model, &iter);
selected_row = gtk_tree_row_reference_new (filtered_model, path);
}
for (iter_valid = gtk_tree_model_get_iter_first (model, &iter);
iter_valid;
iter_valid = gtk_tree_model_iter_next (model, &iter))
{
GtkTreeIter child_iter;
gboolean child_valid;
gint n_children = 0;
for (child_valid = gtk_tree_model_iter_children (model, &child_iter,
&iter);
child_valid;
child_valid = gtk_tree_model_iter_next (model, &child_iter))
{
gboolean visible = TRUE;
if (view->filter)
{
gchar *label;
gchar *name;
gtk_tree_model_get (model, &child_iter,
PIKA_ACTION_VIEW_COLUMN_LABEL_CASEFOLD, &label,
PIKA_ACTION_VIEW_COLUMN_NAME, &name,
-1);
visible = label && name && (strstr (label, view->filter) != NULL ||
strstr (name, view->filter) != NULL);
g_free (label);
g_free (name);
}
gtk_tree_store_set (GTK_TREE_STORE (model), &child_iter,
PIKA_ACTION_VIEW_COLUMN_VISIBLE, visible,
-1);
if (visible)
n_children++;
}
gtk_tree_store_set (GTK_TREE_STORE (model), &iter,
PIKA_ACTION_VIEW_COLUMN_VISIBLE, n_children > 0,
-1);
}
if (view->filter)
gtk_tree_view_expand_all (GTK_TREE_VIEW (view));
else
gtk_tree_view_collapse_all (GTK_TREE_VIEW (view));
gtk_tree_view_columns_autosize (GTK_TREE_VIEW (view));
if (selected_row)
{
if (gtk_tree_row_reference_valid (selected_row))
{
GtkTreePath *path = gtk_tree_row_reference_get_path (selected_row);
pika_action_view_select_path (view, path);
gtk_tree_path_free (path);
}
gtk_tree_row_reference_free (selected_row);
}
}
/* private functions */
static void
pika_action_view_select_path (PikaActionView *view,
GtkTreePath *path)
{
GtkTreeView *tv = GTK_TREE_VIEW (view);
GtkTreePath *expand;
expand = gtk_tree_path_copy (path);
gtk_tree_path_up (expand);
gtk_tree_view_expand_row (tv, expand, FALSE);
gtk_tree_path_free (expand);
gtk_tree_view_set_cursor (tv, path, NULL, FALSE);
gtk_tree_view_scroll_to_cell (tv, path, NULL, TRUE, 0.5, 0.0);
}
static void
pika_action_view_accels_changed (PikaAction *action,
gchar **accels,
PikaActionView *view)
{
GtkTreeModel *model, *tmpmodel;
GtkTreeIter iter;
gboolean iter_valid;
gchar *pathstr = NULL;
model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
if (! model)
return;
model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (model));
if (! model)
return;
if (gtk_tree_selection_get_selected (gtk_tree_view_get_selection (GTK_TREE_VIEW (view)),
&tmpmodel, &iter))
{
GtkTreePath *path = gtk_tree_model_get_path (tmpmodel, &iter);
pathstr = gtk_tree_path_to_string(path);
}
for (iter_valid = gtk_tree_model_get_iter_first (model, &iter);
iter_valid;
iter_valid = gtk_tree_model_iter_next (model, &iter))
{
GtkTreeIter child_iter;
gboolean child_valid;
for (child_valid = gtk_tree_model_iter_children (model, &child_iter,
&iter);
child_valid;
child_valid = gtk_tree_model_iter_next (model, &child_iter))
{
PikaAction *it_action;
gtk_tree_model_get (model, &child_iter,
PIKA_ACTION_VIEW_COLUMN_ACTION, &it_action,
-1);
if (it_action)
g_object_unref (it_action);
if (it_action == action)
{
const gchar **accels;
guint accel_key = 0;
GdkModifierType accel_mask = 0;
accels = pika_action_get_accels (action);
if (accels && accels[0])
gtk_accelerator_parse (accels[0], &accel_key, &accel_mask);
gtk_tree_store_set (GTK_TREE_STORE (model), &child_iter,
PIKA_ACTION_VIEW_COLUMN_ACCEL_KEY, accel_key,
PIKA_ACTION_VIEW_COLUMN_ACCEL_MASK, accel_mask,
-1);
gtk_tree_store_set (GTK_TREE_STORE (model), &iter,
PIKA_ACTION_VIEW_COLUMN_VISIBLE, TRUE,
-1);
if (pathstr)
{
GtkTreePath *path = gtk_tree_path_new_from_string (pathstr);
pika_action_view_select_path (view, path);
gtk_tree_path_free (path);
}
g_free (pathstr);
return;
}
}
}
g_free (pathstr);
}
typedef struct
{
Pika *pika;
PikaAction *action;
guint accel_key;
GdkModifierType accel_mask;
} ConfirmData;
static void
pika_action_view_conflict_response (GtkWidget *dialog,
gint response_id,
ConfirmData *confirm_data)
{
gtk_widget_destroy (dialog);
if (response_id == GTK_RESPONSE_OK)
{
gchar **dup_actions;
gchar *accel;
accel = gtk_accelerator_name (confirm_data->accel_key,
confirm_data->accel_mask);
dup_actions = gtk_application_get_actions_for_accel (GTK_APPLICATION (confirm_data->pika->app),
accel);
for (gint i = 0; dup_actions[i] != NULL; i++)
{
GAction *conflict_action;
gint start;
gchar *left_paren_ptr = strchr (dup_actions[i], '(');
if (left_paren_ptr)
*left_paren_ptr = '\0'; /* ignore target part of detailed name */
start = g_str_has_prefix (dup_actions[i], "app.") ? 4 : 0;
conflict_action = g_action_map_lookup_action (G_ACTION_MAP (confirm_data->pika->app),
dup_actions[i] + start);
g_return_if_fail (PIKA_IS_ACTION (conflict_action));
pika_action_set_accels (PIKA_ACTION (conflict_action), (const char*[]) { NULL });
}
g_strfreev (dup_actions);
pika_action_set_accels (confirm_data->action, (const char*[]) { accel, NULL });
}
g_slice_free (ConfirmData, confirm_data);
}
static void
pika_action_view_conflict_confirm (PikaActionView *view,
PikaAction *action,
PikaAction *edit_action,
guint accel_key,
GdkModifierType accel_mask)
{
PikaActionGroup *group;
gchar *label;
gchar *accel_string;
ConfirmData *confirm_data;
GtkWidget *dialog;
PikaMessageBox *box;
group = pika_action_get_group (action);
label = pika_strip_uline (pika_action_get_label (action));
accel_string = gtk_accelerator_get_label (accel_key, accel_mask);
confirm_data = g_slice_new (ConfirmData);
confirm_data->pika = view->pika;
confirm_data->action = edit_action;
confirm_data->accel_key = accel_key;
confirm_data->accel_mask = accel_mask;
dialog =
pika_message_dialog_new (_("Conflicting Shortcuts"),
PIKA_ICON_DIALOG_WARNING,
gtk_widget_get_toplevel (GTK_WIDGET (view)), 0,
pika_standard_help_func, NULL,
_("_Cancel"), GTK_RESPONSE_CANCEL,
_("_Reassign Shortcut"), GTK_RESPONSE_OK,
NULL);
pika_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
GTK_RESPONSE_OK,
GTK_RESPONSE_CANCEL,
-1);
g_signal_connect (dialog, "response",
G_CALLBACK (pika_action_view_conflict_response),
confirm_data);
box = PIKA_MESSAGE_DIALOG (dialog)->box;
pika_message_box_set_primary_text (box,
_("Shortcut \"%s\" is already taken "
"by \"%s\" from the \"%s\" group."),
accel_string, label, group->label);
pika_message_box_set_text (box,
_("Reassigning the shortcut will cause it "
"to be removed from \"%s\"."),
label);
g_free (label);
g_free (accel_string);
gtk_widget_show (dialog);
}
static void
pika_action_view_get_accel_action (PikaActionView *view,
const gchar *path_string,
PikaAction **action_return,
guint *action_accel_key,
GdkModifierType *action_accel_mask)
{
GtkTreeModel *model;
GtkTreePath *path;
GtkTreeIter iter;
model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
if (! model)
return;
path = gtk_tree_path_new_from_string (path_string);
if (gtk_tree_model_get_iter (model, &iter, path))
{
PikaAction *action;
gtk_tree_model_get (model, &iter,
PIKA_ACTION_VIEW_COLUMN_ACTION, &action,
PIKA_ACTION_VIEW_COLUMN_ACCEL_KEY, action_accel_key,
PIKA_ACTION_VIEW_COLUMN_ACCEL_MASK, action_accel_mask,
-1);
if (action)
{
g_object_unref (action);
*action_return = action;
}
}
gtk_tree_path_free (path);
return;
}
static void
pika_action_view_accel_edited (GtkCellRendererAccel *accel,
const char *path_string,
guint accel_key,
GdkModifierType accel_mask,
guint hardware_keycode,
PikaActionView *view)
{
PikaAction *action;
guint action_accel_key;
GdkModifierType action_accel_mask;
pika_action_view_get_accel_action (view, path_string,
&action,
&action_accel_key,
&action_accel_mask);
if (! action)
return;
if (accel_key == action_accel_key &&
accel_mask == action_accel_mask)
return;
if (! accel_key ||
/* Don't allow arrow keys, they are all swallowed by the canvas
* and cannot be invoked anyway, the same applies to space.
*/
accel_key == GDK_KEY_Left ||
accel_key == GDK_KEY_Right ||
accel_key == GDK_KEY_Up ||
accel_key == GDK_KEY_Down ||
accel_key == GDK_KEY_space ||
accel_key == GDK_KEY_KP_Space)
{
pika_message_literal (view->pika,
G_OBJECT (view), PIKA_MESSAGE_ERROR,
_("Invalid shortcut."));
}
else if (accel_key == GDK_KEY_F1 ||
action_accel_key == GDK_KEY_F1)
{
pika_message_literal (view->pika,
G_OBJECT (view), PIKA_MESSAGE_ERROR,
_("F1 cannot be remapped."));
}
else if (accel_key >= GDK_KEY_0 &&
accel_key <= GDK_KEY_9 &&
accel_mask == GDK_MOD1_MASK)
{
pika_message (view->pika,
G_OBJECT (view), PIKA_MESSAGE_ERROR,
_("Alt+%d is used to switch to display %d and "
"cannot be remapped."),
accel_key - GDK_KEY_0,
accel_key == GDK_KEY_0 ? 10 : accel_key - GDK_KEY_0);
}
else
{
gchar **dup_actions;
gchar *accel = gtk_accelerator_name (accel_key, accel_mask);
dup_actions = gtk_application_get_actions_for_accel (GTK_APPLICATION (view->pika->app),
accel);
if (dup_actions != NULL && dup_actions[0] != NULL)
{
PikaAction *conflict_action = NULL;
gchar *left_paren_ptr0 = strchr (dup_actions[0], '(');
if (left_paren_ptr0)
*left_paren_ptr0 = '\0'; /* ignore target part of detailed name */
for (gint i = 0; dup_actions[i] != NULL; i++)
{
gint start;
gchar *left_paren_ptr1 = strchr (dup_actions[i], '(');
if (left_paren_ptr1)
*left_paren_ptr1 = '\0'; /* ignore target part of detailed name */
start = g_str_has_prefix (dup_actions[i], "app.") ? 4 : 0;
conflict_action = PIKA_ACTION (g_action_map_lookup_action (G_ACTION_MAP (view->pika->app),
dup_actions[i] + start));
if (! conflict_action)
continue;
if (conflict_action != action)
break;
conflict_action = NULL;
}
if (conflict_action)
pika_action_view_conflict_confirm (view, conflict_action, action,
accel_key, accel_mask);
else
pika_message_literal (view->pika,
G_OBJECT (view), PIKA_MESSAGE_ERROR,
_("Changing shortcut failed."));
}
else
{
pika_action_set_accels (action, (const char*[]) { accel, NULL });
}
g_free (accel);
g_strfreev (dup_actions);
}
}
static void
pika_action_view_accel_cleared (GtkCellRendererAccel *accel,
const char *path_string,
PikaActionView *view)
{
PikaAction *action;
guint action_accel_key;
GdkModifierType action_accel_mask;
pika_action_view_get_accel_action (view, path_string,
&action,
&action_accel_key,
&action_accel_mask);
if (! action)
return;
if (action_accel_key == GDK_KEY_F1)
{
pika_message_literal (view->pika,
G_OBJECT (view), PIKA_MESSAGE_ERROR,
_("F1 cannot be remapped."));
return;
}
pika_action_set_accels (action, (const char*[]) { NULL });
}

View File

@ -0,0 +1,79 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikaactionview.h
* Copyright (C) 2004-2005 Michael Natterer <mitch@gimp.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __PIKA_ACTION_VIEW_H__
#define __PIKA_ACTION_VIEW_H__
enum
{
PIKA_ACTION_VIEW_COLUMN_VISIBLE,
PIKA_ACTION_VIEW_COLUMN_ACTION,
PIKA_ACTION_VIEW_COLUMN_ICON_NAME,
PIKA_ACTION_VIEW_COLUMN_LABEL,
PIKA_ACTION_VIEW_COLUMN_LABEL_CASEFOLD,
PIKA_ACTION_VIEW_COLUMN_NAME,
PIKA_ACTION_VIEW_COLUMN_ACCEL_KEY,
PIKA_ACTION_VIEW_COLUMN_ACCEL_MASK,
PIKA_ACTION_VIEW_N_COLUMNS
};
#define PIKA_TYPE_ACTION_VIEW (pika_action_view_get_type ())
#define PIKA_ACTION_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIKA_TYPE_ACTION_VIEW, PikaActionView))
#define PIKA_ACTION_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIKA_TYPE_ACTION_VIEW, PikaActionViewClass))
#define PIKA_IS_ACTION_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIKA_TYPE_ACTION_VIEW))
#define PIKA_IS_ACTION_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIKA_TYPE_ACTION_VIEW))
#define PIKA_ACTION_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIKA_TYPE_ACTION_VIEW, PikaActionViewClass))
typedef struct _PikaActionViewClass PikaActionViewClass;
struct _PikaActionView
{
GtkTreeView parent_instance;
Pika *pika;
gboolean show_shortcuts;
gchar *filter;
};
struct _PikaActionViewClass
{
GtkTreeViewClass parent_class;
};
GType pika_action_view_get_type (void) G_GNUC_CONST;
GtkWidget * pika_action_view_new (Pika *pika,
const gchar *select_action,
gboolean show_shortcuts);
void pika_action_view_set_filter (PikaActionView *view,
const gchar *filter);
#endif /* __PIKA_ACTION_VIEW_H__ */

View File

@ -0,0 +1,413 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gtk/gtk.h>
#include "widgets-types.h"
#include "libpikabase/pikabase.h"
#include "libpikamath/pikamath.h"
#include "paint/pikaink-blob.h"
#include "pikablobeditor.h"
enum
{
PROP_0,
PROP_TYPE,
PROP_ASPECT,
PROP_ANGLE
};
static void pika_blob_editor_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
static void pika_blob_editor_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
static gboolean pika_blob_editor_draw (GtkWidget *widget,
cairo_t *cr);
static gboolean pika_blob_editor_button_press (GtkWidget *widget,
GdkEventButton *event);
static gboolean pika_blob_editor_button_release (GtkWidget *widget,
GdkEventButton *event);
static gboolean pika_blob_editor_motion_notify (GtkWidget *widget,
GdkEventMotion *event);
static void pika_blob_editor_get_handle (PikaBlobEditor *editor,
GdkRectangle *rect);
static void pika_blob_editor_draw_blob (PikaBlobEditor *editor,
cairo_t *cr,
gdouble xc,
gdouble yc,
gdouble radius);
G_DEFINE_TYPE (PikaBlobEditor, pika_blob_editor, GTK_TYPE_DRAWING_AREA)
#define parent_class pika_blob_editor_parent_class
static void
pika_blob_editor_class_init (PikaBlobEditorClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
object_class->set_property = pika_blob_editor_set_property;
object_class->get_property = pika_blob_editor_get_property;
widget_class->draw = pika_blob_editor_draw;
widget_class->button_press_event = pika_blob_editor_button_press;
widget_class->button_release_event = pika_blob_editor_button_release;
widget_class->motion_notify_event = pika_blob_editor_motion_notify;
g_object_class_install_property (object_class, PROP_TYPE,
g_param_spec_enum ("blob-type",
NULL, NULL,
PIKA_TYPE_INK_BLOB_TYPE,
PIKA_INK_BLOB_TYPE_CIRCLE,
PIKA_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
g_object_class_install_property (object_class, PROP_ASPECT,
g_param_spec_double ("blob-aspect",
NULL, NULL,
1.0, 10.0, 1.0,
PIKA_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
g_object_class_install_property (object_class, PROP_ANGLE,
g_param_spec_double ("blob-angle",
NULL, NULL,
-G_PI, G_PI, 0.0,
PIKA_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
}
static void
pika_blob_editor_init (PikaBlobEditor *editor)
{
editor->active = FALSE;
gtk_widget_add_events (GTK_WIDGET (editor),
GDK_BUTTON_PRESS_MASK |
GDK_BUTTON_RELEASE_MASK |
GDK_POINTER_MOTION_MASK |
GDK_EXPOSURE_MASK);
}
GtkWidget *
pika_blob_editor_new (PikaInkBlobType type,
gdouble aspect,
gdouble angle)
{
return g_object_new (PIKA_TYPE_BLOB_EDITOR,
"blob-type", type,
"blob-aspect", aspect,
"blob-angle", angle,
NULL);
}
static void
pika_blob_editor_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
PikaBlobEditor *editor = PIKA_BLOB_EDITOR (object);
switch (property_id)
{
case PROP_TYPE:
editor->type = g_value_get_enum (value);
break;
case PROP_ASPECT:
editor->aspect = g_value_get_double (value);
break;
case PROP_ANGLE:
editor->angle = g_value_get_double (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
gtk_widget_queue_draw (GTK_WIDGET (editor));
}
static void
pika_blob_editor_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
PikaBlobEditor *editor = PIKA_BLOB_EDITOR (object);
switch (property_id)
{
case PROP_TYPE:
g_value_set_enum (value, editor->type);
break;
case PROP_ASPECT:
g_value_set_double (value, editor->aspect);
break;
case PROP_ANGLE:
g_value_set_double (value, editor->angle);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static gboolean
pika_blob_editor_draw (GtkWidget *widget,
cairo_t *cr)
{
PikaBlobEditor *editor = PIKA_BLOB_EDITOR (widget);
GtkStyleContext *style = gtk_widget_get_style_context (widget);
GtkAllocation allocation;
GdkRectangle rect;
gint r0;
gtk_widget_get_allocation (widget, &allocation);
r0 = MIN (allocation.width, allocation.height) / 2;
if (r0 < 2)
return TRUE;
pika_blob_editor_draw_blob (editor, cr,
allocation.width / 2.0,
allocation.height / 2.0,
0.9 * r0);
pika_blob_editor_get_handle (editor, &rect);
gtk_style_context_save (style);
gtk_style_context_add_class (style, GTK_STYLE_CLASS_BUTTON);
gtk_style_context_set_state (style,
editor->in_handle ?
GTK_STATE_FLAG_PRELIGHT : 0);
gtk_render_background (style, cr, rect.x, rect.y, rect.width, rect.height);
gtk_render_frame (style, cr, rect.x, rect.y, rect.width, rect.height);
gtk_style_context_restore (style);
return TRUE;
}
static gboolean
pika_blob_editor_button_press (GtkWidget *widget,
GdkEventButton *event)
{
PikaBlobEditor *editor = PIKA_BLOB_EDITOR (widget);
if (editor->in_handle)
editor->active = TRUE;
return TRUE;
}
static gboolean
pika_blob_editor_button_release (GtkWidget *widget,
GdkEventButton *event)
{
PikaBlobEditor *editor = PIKA_BLOB_EDITOR (widget);
editor->active = FALSE;
return TRUE;
}
static gboolean
pika_blob_editor_motion_notify (GtkWidget *widget,
GdkEventMotion *event)
{
PikaBlobEditor *editor = PIKA_BLOB_EDITOR (widget);
if (editor->active)
{
GtkAllocation allocation;
gint x;
gint y;
gint rsquare;
gtk_widget_get_allocation (widget, &allocation);
x = event->x - allocation.width / 2;
y = event->y - allocation.height / 2;
rsquare = SQR (x) + SQR (y);
if (rsquare > 0)
{
gint r0;
gdouble angle;
gdouble aspect;
r0 = MIN (allocation.width, allocation.height) / 2;
angle = atan2 (y, x);
aspect = 10.0 * sqrt ((gdouble) rsquare / (r0 * r0)) / 0.85;
aspect = CLAMP (aspect, 1.0, 10.0);
g_object_set (editor,
"blob-angle", angle,
"blob-aspect", aspect,
NULL);
}
}
else
{
GdkRectangle rect;
gboolean in_handle;
pika_blob_editor_get_handle (editor, &rect);
if ((event->x >= rect.x) && (event->x - rect.x < rect.width) &&
(event->y >= rect.y) && (event->y - rect.y < rect.height))
{
in_handle = TRUE;
}
else
{
in_handle = FALSE;
}
if (in_handle != editor->in_handle)
{
editor->in_handle = in_handle;
gtk_widget_queue_draw (widget);
}
}
return TRUE;
}
static void
pika_blob_editor_get_handle (PikaBlobEditor *editor,
GdkRectangle *rect)
{
GtkWidget *widget = GTK_WIDGET (editor);
GtkAllocation allocation;
gint x, y;
gint r;
gtk_widget_get_allocation (widget, &allocation);
r = MIN (allocation.width, allocation.height) / 2;
x = (allocation.width / 2 +
0.85 * r *editor->aspect / 10.0 * cos (editor->angle));
y = (allocation.height / 2 +
0.85 * r * editor->aspect / 10.0 * sin (editor->angle));
rect->x = x - 5;
rect->y = y - 5;
rect->width = 10;
rect->height = 10;
}
static void
pika_blob_editor_draw_blob (PikaBlobEditor *editor,
cairo_t *cr,
gdouble xc,
gdouble yc,
gdouble radius)
{
GtkWidget *widget = GTK_WIDGET (editor);
GtkStyleContext *style = gtk_widget_get_style_context (widget);
PikaBlob *blob;
PikaBlobFunc function = pika_blob_ellipse;
GdkRGBA color;
gint i;
switch (editor->type)
{
case PIKA_INK_BLOB_TYPE_CIRCLE:
function = pika_blob_ellipse;
break;
case PIKA_INK_BLOB_TYPE_SQUARE:
function = pika_blob_square;
break;
case PIKA_INK_BLOB_TYPE_DIAMOND:
function = pika_blob_diamond;
break;
}
/* to get a nice antialiased outline, render the blob at double size */
radius *= 2.0;
blob = function (2.0 * xc, 2.0 * yc,
radius * cos (editor->angle),
radius * sin (editor->angle),
(- (radius / editor->aspect) * sin (editor->angle)),
( (radius / editor->aspect) * cos (editor->angle)));
for (i = 0; i < blob->height; i++)
if (blob->data[i].left <= blob->data[i].right)
{
cairo_move_to (cr, blob->data[i].left / 2.0, (blob->y + i) / 2.0);
break;
}
for (i = i + 1; i < blob->height; i++)
{
if (blob->data[i].left > blob->data[i].right)
break;
cairo_line_to (cr, blob->data[i].left / 2.0, (blob->y + i) / 2.0);
}
for (i = i - 1; i >= 0; i--)
{
if (blob->data[i].left > blob->data[i].right)
break;
cairo_line_to (cr, blob->data[i].right / 2.0, (blob->y + i) / 2.0);
}
cairo_close_path (cr);
g_free (blob);
gtk_style_context_get_color (style, gtk_widget_get_state_flags (widget),
&color);
gdk_cairo_set_source_rgba (cr, &color);
cairo_fill (cr);
}

View File

@ -0,0 +1,64 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikablobeditor.h
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __PIKA_BLOB_EDITOR_H__
#define __PIKA_BLOB_EDITOR_H__
#define PIKA_TYPE_BLOB_EDITOR (pika_blob_editor_get_type ())
#define PIKA_BLOB_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIKA_TYPE_BLOB_EDITOR, PikaBlobEditor))
#define PIKA_BLOB_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIKA_TYPE_BLOB_EDITOR, PikaBlobEditorClass))
#define PIKA_IS_BLOB_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIKA_TYPE_BLOB_EDITOR))
#define PIKA_IS_BLOB_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIKA_TYPE_BLOB_EDITOR))
#define PIKA_BLOB_EDITOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIKA_TYPE_BLOB_EDITOR, PikaBlobEditorClass))
typedef struct _PikaBlobEditorClass PikaBlobEditorClass;
struct _PikaBlobEditor
{
GtkDrawingArea parent_instance;
PikaInkBlobType type;
gdouble aspect;
gdouble angle;
/*< private >*/
gboolean in_handle;
gboolean active;
};
struct _PikaBlobEditorClass
{
GtkDrawingAreaClass parent_class;
};
GType pika_blob_editor_get_type (void) G_GNUC_CONST;
GtkWidget * pika_blob_editor_new (PikaInkBlobType type,
gdouble aspect,
gdouble angle);
#endif /* __PIKA_BLOB_EDITOR_H__ */

View File

@ -0,0 +1,461 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikabrusheditor.c
* Copyright 1998 Jay Cox <jaycox@earthlink.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <string.h>
#include <gegl.h>
#include <gtk/gtk.h>
#include "libpikamath/pikamath.h"
#include "libpikawidgets/pikawidgets.h"
#include "widgets-types.h"
#include "core/pika.h"
#include "core/pikabrushgenerated.h"
#include "core/pikacontext.h"
#include "pikabrusheditor.h"
#include "pikadocked.h"
#include "pikaview.h"
#include "pikaviewrenderer.h"
#include "pika-intl.h"
#define BRUSH_VIEW_SIZE 96
/* local function prototypes */
static void pika_brush_editor_docked_iface_init (PikaDockedInterface *face);
static void pika_brush_editor_constructed (GObject *object);
static void pika_brush_editor_set_data (PikaDataEditor *editor,
PikaData *data);
static void pika_brush_editor_set_context (PikaDocked *docked,
PikaContext *context);
static void pika_brush_editor_update_brush (GtkAdjustment *adjustment,
PikaBrushEditor *editor);
static void pika_brush_editor_update_shape (GtkWidget *widget,
PikaBrushEditor *editor);
static void pika_brush_editor_notify_brush (PikaBrushGenerated *brush,
GParamSpec *pspec,
PikaBrushEditor *editor);
G_DEFINE_TYPE_WITH_CODE (PikaBrushEditor, pika_brush_editor,
PIKA_TYPE_DATA_EDITOR,
G_IMPLEMENT_INTERFACE (PIKA_TYPE_DOCKED,
pika_brush_editor_docked_iface_init))
#define parent_class pika_brush_editor_parent_class
static PikaDockedInterface *parent_docked_iface = NULL;
static void
pika_brush_editor_class_init (PikaBrushEditorClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
PikaDataEditorClass *editor_class = PIKA_DATA_EDITOR_CLASS (klass);
object_class->constructed = pika_brush_editor_constructed;
editor_class->set_data = pika_brush_editor_set_data;
editor_class->title = _("Brush Editor");
}
static void
pika_brush_editor_docked_iface_init (PikaDockedInterface *iface)
{
parent_docked_iface = g_type_interface_peek_parent (iface);
if (! parent_docked_iface)
parent_docked_iface = g_type_default_interface_peek (PIKA_TYPE_DOCKED);
iface->set_context = pika_brush_editor_set_context;
}
static void
pika_brush_editor_init (PikaBrushEditor *editor)
{
PikaDataEditor *data_editor = PIKA_DATA_EDITOR (editor);
GtkWidget *frame;
GtkWidget *hbox;
GtkWidget *label;
GtkWidget *box;
GtkWidget *scale;
frame = gtk_frame_new (NULL);
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
gtk_box_pack_start (GTK_BOX (editor), frame, TRUE, TRUE, 0);
gtk_widget_show (frame);
data_editor->view = pika_view_new_full_by_types (NULL,
PIKA_TYPE_VIEW,
PIKA_TYPE_BRUSH,
BRUSH_VIEW_SIZE,
BRUSH_VIEW_SIZE, 0,
FALSE, FALSE, TRUE);
gtk_widget_set_size_request (data_editor->view, -1, BRUSH_VIEW_SIZE);
pika_view_set_expand (PIKA_VIEW (data_editor->view), TRUE);
gtk_container_add (GTK_CONTAINER (frame), data_editor->view);
gtk_widget_show (data_editor->view);
editor->shape_group = NULL;
editor->options_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
gtk_box_pack_start (GTK_BOX (editor), editor->options_box, FALSE, FALSE, 0);
gtk_widget_show (editor->options_box);
/* Stock Box for the brush shape */
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2);
gtk_box_pack_start (GTK_BOX (editor->options_box), hbox, FALSE, FALSE, 0);
gtk_widget_show (hbox);
label = gtk_label_new (_("Shape:"));
gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
gtk_widget_show (label);
box = pika_enum_icon_box_new (PIKA_TYPE_BRUSH_GENERATED_SHAPE,
"pika-shape",
GTK_ICON_SIZE_MENU,
G_CALLBACK (pika_brush_editor_update_shape),
editor, NULL,
&editor->shape_group);
gtk_box_pack_start (GTK_BOX (hbox), box, FALSE, FALSE, 0);
gtk_widget_show (box);
/* brush radius scale */
editor->radius_data = gtk_adjustment_new (0.0, 0.1, 1000.0, 1.0, 10.0, 0.0);
scale = pika_spin_scale_new (editor->radius_data, _("Radius"), 1);
gtk_box_pack_start (GTK_BOX (editor->options_box), scale, FALSE, FALSE, 0);
gtk_widget_show (scale);
g_signal_connect (editor->radius_data, "value-changed",
G_CALLBACK (pika_brush_editor_update_brush),
editor);
/* number of spikes */
editor->spikes_data = gtk_adjustment_new (2.0, 2.0, 20.0, 1.0, 1.0, 0.0);
scale = pika_spin_scale_new (editor->spikes_data, _("Spikes"), 0);
gtk_box_pack_start (GTK_BOX (editor->options_box), scale, FALSE, FALSE, 0);
gtk_widget_show (scale);
g_signal_connect (editor->spikes_data, "value-changed",
G_CALLBACK (pika_brush_editor_update_brush),
editor);
/* brush hardness scale */
editor->hardness_data = gtk_adjustment_new (0.0, 0.0, 1.0, 0.01, 0.1, 0.0);
scale = pika_spin_scale_new (editor->hardness_data, _("Hardness"), 2);
gtk_box_pack_start (GTK_BOX (editor->options_box), scale, FALSE, FALSE, 0);
gtk_widget_show (scale);
g_signal_connect (editor->hardness_data, "value-changed",
G_CALLBACK (pika_brush_editor_update_brush),
editor);
/* brush aspect ratio scale */
editor->aspect_ratio_data = gtk_adjustment_new (0.0, 1.0, 20.0, 0.1, 1.0, 0.0);
scale = pika_spin_scale_new (editor->aspect_ratio_data, _("Aspect ratio"), 1);
gtk_box_pack_start (GTK_BOX (editor->options_box), scale, FALSE, FALSE, 0);
gtk_widget_show (scale);
g_signal_connect (editor->aspect_ratio_data,"value-changed",
G_CALLBACK (pika_brush_editor_update_brush),
editor);
/* brush angle scale */
editor->angle_data = gtk_adjustment_new (0.0, 0.0, 180.0, 0.1, 1.0, 0.0);
scale = pika_spin_scale_new (editor->angle_data, _("Angle"), 1);
gtk_box_pack_start (GTK_BOX (editor->options_box), scale, FALSE, FALSE, 0);
gtk_widget_show (scale);
g_signal_connect (editor->angle_data, "value-changed",
G_CALLBACK (pika_brush_editor_update_brush),
editor);
/* brush spacing */
editor->spacing_data = gtk_adjustment_new (0.0, 1.0, 5000.0, 1.0, 10.0, 0.0);
scale = pika_spin_scale_new (editor->spacing_data, _("Spacing"), 1);
pika_spin_scale_set_scale_limits (PIKA_SPIN_SCALE (scale), 1.0, 200.0);
gtk_box_pack_start (GTK_BOX (editor->options_box), scale, FALSE, FALSE, 0);
gtk_widget_show (scale);
pika_help_set_help_data (scale, _("Percentage of width of brush"), NULL);
g_signal_connect (editor->spacing_data, "value-changed",
G_CALLBACK (pika_brush_editor_update_brush),
editor);
}
static void
pika_brush_editor_constructed (GObject *object)
{
G_OBJECT_CLASS (parent_class)->constructed (object);
pika_docked_set_show_button_bar (PIKA_DOCKED (object), FALSE);
}
static void
pika_brush_editor_set_data (PikaDataEditor *editor,
PikaData *data)
{
PikaBrushEditor *brush_editor = PIKA_BRUSH_EDITOR (editor);
PikaBrushGeneratedShape shape = PIKA_BRUSH_GENERATED_CIRCLE;
gdouble radius = 0.0;
gint spikes = 2;
gdouble hardness = 0.0;
gdouble ratio = 0.0;
gdouble angle = 0.0;
gdouble spacing = 0.0;
if (editor->data)
g_signal_handlers_disconnect_by_func (editor->data,
pika_brush_editor_notify_brush,
editor);
PIKA_DATA_EDITOR_CLASS (parent_class)->set_data (editor, data);
if (editor->data)
g_signal_connect (editor->data, "notify",
G_CALLBACK (pika_brush_editor_notify_brush),
editor);
pika_view_set_viewable (PIKA_VIEW (editor->view), PIKA_VIEWABLE (data));
if (editor->data)
{
spacing = pika_brush_get_spacing (PIKA_BRUSH (editor->data));
if (PIKA_IS_BRUSH_GENERATED (editor->data))
{
PikaBrushGenerated *brush = PIKA_BRUSH_GENERATED (editor->data);
shape = pika_brush_generated_get_shape (brush);
radius = pika_brush_generated_get_radius (brush);
spikes = pika_brush_generated_get_spikes (brush);
hardness = pika_brush_generated_get_hardness (brush);
ratio = pika_brush_generated_get_aspect_ratio (brush);
angle = pika_brush_generated_get_angle (brush);
}
}
gtk_widget_set_sensitive (brush_editor->options_box,
editor->data_editable);
pika_int_radio_group_set_active (GTK_RADIO_BUTTON (brush_editor->shape_group),
shape);
gtk_adjustment_set_value (brush_editor->radius_data, radius);
gtk_adjustment_set_value (brush_editor->spikes_data, spikes);
gtk_adjustment_set_value (brush_editor->hardness_data, hardness);
gtk_adjustment_set_value (brush_editor->aspect_ratio_data, ratio);
gtk_adjustment_set_value (brush_editor->angle_data, angle);
gtk_adjustment_set_value (brush_editor->spacing_data, spacing);
}
static void
pika_brush_editor_set_context (PikaDocked *docked,
PikaContext *context)
{
PikaDataEditor *data_editor = PIKA_DATA_EDITOR (docked);
parent_docked_iface->set_context (docked, context);
pika_view_renderer_set_context (PIKA_VIEW (data_editor->view)->renderer,
context);
}
/* public functions */
GtkWidget *
pika_brush_editor_new (PikaContext *context,
PikaMenuFactory *menu_factory)
{
g_return_val_if_fail (PIKA_IS_CONTEXT (context), NULL);
return g_object_new (PIKA_TYPE_BRUSH_EDITOR,
"menu-factory", menu_factory,
"menu-identifier", "<BrushEditor>",
"ui-path", "/brush-editor-popup",
"data-factory", context->pika->brush_factory,
"context", context,
"data", pika_context_get_brush (context),
NULL);
}
/* private functions */
static void
pika_brush_editor_update_brush (GtkAdjustment *adjustment,
PikaBrushEditor *editor)
{
PikaBrushGenerated *brush;
gdouble value;
if (! PIKA_IS_BRUSH_GENERATED (PIKA_DATA_EDITOR (editor)->data))
return;
brush = PIKA_BRUSH_GENERATED (PIKA_DATA_EDITOR (editor)->data);
g_signal_handlers_block_by_func (brush,
pika_brush_editor_notify_brush,
editor);
value = gtk_adjustment_get_value (adjustment);
if (adjustment == editor->radius_data)
{
if (value != pika_brush_generated_get_radius (brush))
pika_brush_generated_set_radius (brush, value);
}
else if (adjustment == editor->spikes_data)
{
if (ROUND (value) != pika_brush_generated_get_spikes (brush))
pika_brush_generated_set_spikes (brush, ROUND (value));
}
else if (adjustment == editor->hardness_data)
{
if (value != pika_brush_generated_get_hardness (brush))
pika_brush_generated_set_hardness (brush, value);
}
else if (adjustment == editor->aspect_ratio_data)
{
if (value != pika_brush_generated_get_aspect_ratio (brush))
pika_brush_generated_set_aspect_ratio (brush, value);
}
else if (adjustment == editor->angle_data)
{
if (value != pika_brush_generated_get_angle (brush))
pika_brush_generated_set_angle (brush, value);
}
else if (adjustment == editor->spacing_data)
{
if (value != pika_brush_get_spacing (PIKA_BRUSH (brush)))
pika_brush_set_spacing (PIKA_BRUSH (brush), value);
}
g_signal_handlers_unblock_by_func (brush,
pika_brush_editor_notify_brush,
editor);
}
static void
pika_brush_editor_update_shape (GtkWidget *widget,
PikaBrushEditor *editor)
{
PikaBrushGenerated *brush;
if (! PIKA_IS_BRUSH_GENERATED (PIKA_DATA_EDITOR (editor)->data))
return;
brush = PIKA_BRUSH_GENERATED (PIKA_DATA_EDITOR (editor)->data);
if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
{
PikaBrushGeneratedShape shape;
shape = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget),
"pika-item-data"));
if (pika_brush_generated_get_shape (brush) != shape)
pika_brush_generated_set_shape (brush, shape);
}
}
static void
pika_brush_editor_notify_brush (PikaBrushGenerated *brush,
GParamSpec *pspec,
PikaBrushEditor *editor)
{
GtkAdjustment *adj = NULL;
gdouble value = 0.0;
if (! strcmp (pspec->name, "shape"))
{
g_signal_handlers_block_by_func (editor->shape_group,
pika_brush_editor_update_shape,
editor);
pika_int_radio_group_set_active (GTK_RADIO_BUTTON (editor->shape_group),
brush->shape);
g_signal_handlers_unblock_by_func (editor->shape_group,
pika_brush_editor_update_shape,
editor);
}
else if (! strcmp (pspec->name, "radius"))
{
adj = editor->radius_data;
value = pika_brush_generated_get_radius (brush);
}
else if (! strcmp (pspec->name, "spikes"))
{
adj = editor->spikes_data;
value = pika_brush_generated_get_spikes (brush);
}
else if (! strcmp (pspec->name, "hardness"))
{
adj = editor->hardness_data;
value = pika_brush_generated_get_hardness (brush);
}
else if (! strcmp (pspec->name, "angle"))
{
adj = editor->angle_data;
value = pika_brush_generated_get_angle (brush);
}
else if (! strcmp (pspec->name, "aspect-ratio"))
{
adj = editor->aspect_ratio_data;
value = pika_brush_generated_get_aspect_ratio (brush);
}
else if (! strcmp (pspec->name, "spacing"))
{
adj = editor->spacing_data;
value = pika_brush_get_spacing (PIKA_BRUSH (brush));
}
if (adj)
{
g_signal_handlers_block_by_func (adj,
pika_brush_editor_update_brush,
editor);
gtk_adjustment_set_value (adj, value);
g_signal_handlers_unblock_by_func (adj,
pika_brush_editor_update_brush,
editor);
}
}

View File

@ -0,0 +1,68 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikabrusheditor.h
* Copyright 1998 Jay Cox <jaycox@earthlink.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __PIKA_BRUSH_EDITOR_H__
#define __PIKA_BRUSH_EDITOR_H__
#include "pikadataeditor.h"
#define PIKA_TYPE_BRUSH_EDITOR (pika_brush_editor_get_type ())
#define PIKA_BRUSH_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIKA_TYPE_BRUSH_EDITOR, PikaBrushEditor))
#define PIKA_BRUSH_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIKA_TYPE_BRUSH_EDITOR, PikaBrushEditorClass))
#define PIKA_IS_BRUSH_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIKA_TYPE_BRUSH_EDITOR))
#define PIKA_IS_BRUSH_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIKA_TYPE_BRUSH_EDITOR))
#define PIKA_BRUSH_EDITOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIKA_TYPE_BRUSH_EDITOR, PikaBrushEditorClass))
typedef struct _PikaBrushEditorClass PikaBrushEditorClass;
struct _PikaBrushEditor
{
PikaDataEditor parent_instance;
GtkWidget *shape_group;
GtkWidget *options_box;
GtkAdjustment *radius_data;
GtkAdjustment *spikes_data;
GtkAdjustment *hardness_data;
GtkAdjustment *angle_data;
GtkAdjustment *aspect_ratio_data;
GtkAdjustment *spacing_data;
};
struct _PikaBrushEditorClass
{
PikaDataEditorClass parent_class;
};
GType pika_brush_editor_get_type (void) G_GNUC_CONST;
GtkWidget * pika_brush_editor_new (PikaContext *context,
PikaMenuFactory *menu_factory);
#endif /* __PIKA_BRUSH_EDITOR_H__ */

View File

@ -0,0 +1,278 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikabrushfactoryview.c
* Copyright (C) 2001 Michael Natterer <mitch@gimp.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gegl.h>
#include <gtk/gtk.h>
#include "libpikawidgets/pikawidgets.h"
#include "widgets-types.h"
#include "core/pikacontainer.h"
#include "core/pikacontext.h"
#include "core/pikabrush.h"
#include "core/pikabrushgenerated.h"
#include "core/pikadatafactory.h"
#include "pikabrushfactoryview.h"
#include "pikacontainerview.h"
#include "pikaeditor.h"
#include "pikamenufactory.h"
#include "pikaviewrenderer.h"
#include "pika-intl.h"
enum
{
SPACING_CHANGED,
LAST_SIGNAL
};
static void pika_brush_factory_view_dispose (GObject *object);
static void pika_brush_factory_view_select_item (PikaContainerEditor *editor,
PikaViewable *viewable);
static void pika_brush_factory_view_spacing_changed (PikaBrush *brush,
PikaBrushFactoryView *view);
static void pika_brush_factory_view_spacing_update (GtkAdjustment *adjustment,
PikaBrushFactoryView *view);
G_DEFINE_TYPE (PikaBrushFactoryView, pika_brush_factory_view,
PIKA_TYPE_DATA_FACTORY_VIEW)
#define parent_class pika_brush_factory_view_parent_class
static guint pika_brush_factory_view_signals[LAST_SIGNAL] = { 0 };
static void
pika_brush_factory_view_class_init (PikaBrushFactoryViewClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
PikaContainerEditorClass *editor_class = PIKA_CONTAINER_EDITOR_CLASS (klass);
object_class->dispose = pika_brush_factory_view_dispose;
editor_class->select_item = pika_brush_factory_view_select_item;
/**
* PikaBrushFactoryView::spacing-changed:
* @view: the #PikaBrushFactoryView.
*
* Emitted when the spacing changed.
*/
pika_brush_factory_view_signals[SPACING_CHANGED] =
g_signal_new ("spacing-changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (PikaBrushFactoryViewClass, spacing_changed),
NULL, NULL, NULL,
G_TYPE_NONE, 0);
}
static void
pika_brush_factory_view_init (PikaBrushFactoryView *view)
{
view->spacing_adjustment = gtk_adjustment_new (0.0, 1.0, 5000.0,
1.0, 10.0, 0.0);
view->spacing_scale = pika_spin_scale_new (view->spacing_adjustment,
_("Spacing"), 1);
pika_spin_scale_set_scale_limits (PIKA_SPIN_SCALE (view->spacing_scale),
1.0, 200.0);
pika_help_set_help_data (view->spacing_scale,
_("Percentage of width of brush"),
NULL);
g_signal_connect (view->spacing_adjustment, "value-changed",
G_CALLBACK (pika_brush_factory_view_spacing_update),
view);
}
static void
pika_brush_factory_view_dispose (GObject *object)
{
PikaBrushFactoryView *view = PIKA_BRUSH_FACTORY_VIEW (object);
PikaContainerEditor *editor = PIKA_CONTAINER_EDITOR (object);
if (view->spacing_changed_handler_id)
{
PikaDataFactory *factory;
PikaContainer *container;
factory = pika_data_factory_view_get_data_factory (PIKA_DATA_FACTORY_VIEW (editor));
container = pika_data_factory_get_container (factory);
pika_container_remove_handler (container,
view->spacing_changed_handler_id);
view->spacing_changed_handler_id = 0;
}
G_OBJECT_CLASS (parent_class)->dispose (object);
}
GtkWidget *
pika_brush_factory_view_new (PikaViewType view_type,
PikaDataFactory *factory,
PikaContext *context,
gboolean change_brush_spacing,
gint view_size,
gint view_border_width,
PikaMenuFactory *menu_factory)
{
PikaBrushFactoryView *factory_view;
PikaContainerEditor *editor;
g_return_val_if_fail (PIKA_IS_DATA_FACTORY (factory), NULL);
g_return_val_if_fail (PIKA_IS_CONTEXT (context), NULL);
g_return_val_if_fail (view_size > 0 &&
view_size <= PIKA_VIEWABLE_MAX_PREVIEW_SIZE, NULL);
g_return_val_if_fail (view_border_width >= 0 &&
view_border_width <= PIKA_VIEW_MAX_BORDER_WIDTH,
NULL);
g_return_val_if_fail (menu_factory == NULL ||
PIKA_IS_MENU_FACTORY (menu_factory), NULL);
factory_view = g_object_new (PIKA_TYPE_BRUSH_FACTORY_VIEW,
"view-type", view_type,
"data-factory", factory,
"context", context,
"view-size", view_size,
"view-border-width", view_border_width,
"menu-factory", menu_factory,
"menu-identifier", "<Brushes>",
"ui-path", "/brushes-popup",
"action-group", "brushes",
NULL);
factory_view->change_brush_spacing = change_brush_spacing;
editor = PIKA_CONTAINER_EDITOR (factory_view);
pika_editor_add_action_button (PIKA_EDITOR (editor->view),
"brushes", "brushes-open-as-image",
NULL);
gtk_box_pack_end (GTK_BOX (editor->view), factory_view->spacing_scale,
FALSE, FALSE, 0);
gtk_widget_show (factory_view->spacing_scale);
factory_view->spacing_changed_handler_id =
pika_container_add_handler (pika_data_factory_get_container (factory), "spacing-changed",
G_CALLBACK (pika_brush_factory_view_spacing_changed),
factory_view);
return GTK_WIDGET (factory_view);
}
static void
pika_brush_factory_view_select_item (PikaContainerEditor *editor,
PikaViewable *viewable)
{
PikaBrushFactoryView *view = PIKA_BRUSH_FACTORY_VIEW (editor);
PikaContainer *container;
gboolean spacing_sensitive = FALSE;
if (PIKA_CONTAINER_EDITOR_CLASS (parent_class)->select_item)
PIKA_CONTAINER_EDITOR_CLASS (parent_class)->select_item (editor, viewable);
container = pika_container_view_get_container (editor->view);
if (viewable && pika_container_have (container, PIKA_OBJECT (viewable)))
{
PikaBrush *brush = PIKA_BRUSH (viewable);
spacing_sensitive = TRUE;
g_signal_handlers_block_by_func (view->spacing_adjustment,
pika_brush_factory_view_spacing_update,
view);
gtk_adjustment_set_value (view->spacing_adjustment,
pika_brush_get_spacing (brush));
g_signal_handlers_unblock_by_func (view->spacing_adjustment,
pika_brush_factory_view_spacing_update,
view);
}
gtk_widget_set_sensitive (view->spacing_scale, spacing_sensitive);
}
static void
pika_brush_factory_view_spacing_changed (PikaBrush *brush,
PikaBrushFactoryView *view)
{
PikaContainerEditor *editor = PIKA_CONTAINER_EDITOR (view);
PikaContext *context;
context = pika_container_view_get_context (editor->view);
if (brush == pika_context_get_brush (context))
{
g_signal_handlers_block_by_func (view->spacing_adjustment,
pika_brush_factory_view_spacing_update,
view);
gtk_adjustment_set_value (view->spacing_adjustment,
pika_brush_get_spacing (brush));
g_signal_handlers_unblock_by_func (view->spacing_adjustment,
pika_brush_factory_view_spacing_update,
view);
}
}
static void
pika_brush_factory_view_spacing_update (GtkAdjustment *adjustment,
PikaBrushFactoryView *view)
{
PikaContainerEditor *editor = PIKA_CONTAINER_EDITOR (view);
PikaContext *context;
PikaBrush *brush;
context = pika_container_view_get_context (editor->view);
brush = pika_context_get_brush (context);
if (brush && view->change_brush_spacing)
{
g_signal_handlers_block_by_func (brush,
pika_brush_factory_view_spacing_changed,
view);
pika_brush_set_spacing (brush, gtk_adjustment_get_value (adjustment));
g_signal_handlers_unblock_by_func (brush,
pika_brush_factory_view_spacing_changed,
view);
}
g_signal_emit (view, pika_brush_factory_view_signals[SPACING_CHANGED], 0);
}

View File

@ -0,0 +1,73 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikabrushfactoryview.h
* Copyright (C) 2001 Michael Natterer <mitch@gimp.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __PIKA_BRUSH_FACTORY_VIEW_H__
#define __PIKA_BRUSH_FACTORY_VIEW_H__
#include "pikadatafactoryview.h"
#define PIKA_TYPE_BRUSH_FACTORY_VIEW (pika_brush_factory_view_get_type ())
#define PIKA_BRUSH_FACTORY_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIKA_TYPE_BRUSH_FACTORY_VIEW, PikaBrushFactoryView))
#define PIKA_BRUSH_FACTORY_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIKA_TYPE_BRUSH_FACTORY_VIEW, PikaBrushFactoryViewClass))
#define PIKA_IS_BRUSH_FACTORY_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIKA_TYPE_BRUSH_FACTORY_VIEW))
#define PIKA_IS_BRUSH_FACTORY_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIKA_TYPE_BRUSH_FACTORY_VIEW))
#define PIKA_BRUSH_FACTORY_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIKA_TYPE_BRUSH_FACTORY_VIEW, PikaBrushFactoryViewClass))
typedef struct _PikaBrushFactoryViewClass PikaBrushFactoryViewClass;
struct _PikaBrushFactoryView
{
PikaDataFactoryView parent_instance;
GtkWidget *spacing_scale;
GtkAdjustment *spacing_adjustment;
gboolean change_brush_spacing;
GQuark spacing_changed_handler_id;
};
struct _PikaBrushFactoryViewClass
{
PikaDataFactoryViewClass parent_class;
/* Signals */
void (* spacing_changed) (PikaBrushFactoryView *view);
};
GType pika_brush_factory_view_get_type (void) G_GNUC_CONST;
GtkWidget * pika_brush_factory_view_new (PikaViewType view_type,
PikaDataFactory *factory,
PikaContext *context,
gboolean change_brush_spacing,
gint view_size,
gint view_border_width,
PikaMenuFactory *menu_factory);
#endif /* __PIKA_BRUSH_FACTORY_VIEW_H__ */

View File

@ -0,0 +1,348 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikabrushselect.c
* Copyright (C) 2004 Michael Natterer <mitch@gimp.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gegl.h>
#include <gtk/gtk.h>
#include "libpikabase/pikabase.h"
#include "libpikawidgets/pikawidgets.h"
#include "widgets-types.h"
#include "gegl/pika-babl-compat.h"
#include "core/pika.h"
#include "core/pikacontext.h"
#include "core/pikabrush.h"
#include "core/pikaparamspecs.h"
#include "core/pikatempbuf.h"
#include "pdb/pikapdb.h"
#include "pikabrushfactoryview.h"
#include "pikabrushselect.h"
#include "pikacontainerbox.h"
#include "pikalayermodebox.h"
#include "pika-intl.h"
enum
{
PROP_0,
PROP_OPACITY,
PROP_PAINT_MODE,
PROP_SPACING
};
static void pika_brush_select_constructed (GObject *object);
static void pika_brush_select_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
static PikaValueArray * pika_brush_select_run_callback (PikaPdbDialog *dialog,
PikaObject *object,
gboolean closing,
GError **error);
static void pika_brush_select_opacity_changed (PikaContext *context,
gdouble opacity,
PikaBrushSelect *select);
static void pika_brush_select_mode_changed (PikaContext *context,
PikaLayerMode paint_mode,
PikaBrushSelect *select);
static void pika_brush_select_opacity_update (GtkAdjustment *adj,
PikaBrushSelect *select);
static void pika_brush_select_spacing_update (PikaBrushFactoryView *view,
PikaBrushSelect *select);
G_DEFINE_TYPE (PikaBrushSelect, pika_brush_select, PIKA_TYPE_PDB_DIALOG)
#define parent_class pika_brush_select_parent_class
static void
pika_brush_select_class_init (PikaBrushSelectClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
PikaPdbDialogClass *pdb_class = PIKA_PDB_DIALOG_CLASS (klass);
object_class->constructed = pika_brush_select_constructed;
object_class->set_property = pika_brush_select_set_property;
pdb_class->run_callback = pika_brush_select_run_callback;
g_object_class_install_property (object_class, PROP_OPACITY,
g_param_spec_double ("opacity", NULL, NULL,
PIKA_OPACITY_TRANSPARENT,
PIKA_OPACITY_OPAQUE,
PIKA_OPACITY_OPAQUE,
PIKA_PARAM_WRITABLE |
G_PARAM_CONSTRUCT));
g_object_class_install_property (object_class, PROP_PAINT_MODE,
g_param_spec_enum ("paint-mode", NULL, NULL,
PIKA_TYPE_LAYER_MODE,
PIKA_LAYER_MODE_NORMAL,
PIKA_PARAM_WRITABLE |
G_PARAM_CONSTRUCT));
g_object_class_install_property (object_class, PROP_SPACING,
g_param_spec_int ("spacing", NULL, NULL,
-G_MAXINT, 1000, -1,
PIKA_PARAM_WRITABLE |
G_PARAM_CONSTRUCT));
}
static void
pika_brush_select_init (PikaBrushSelect *select)
{
}
static void
pika_brush_select_constructed (GObject *object)
{
PikaPdbDialog *dialog = PIKA_PDB_DIALOG (object);
PikaBrushSelect *select = PIKA_BRUSH_SELECT (object);
GtkWidget *content_area;
GtkWidget *vbox;
GtkWidget *scale;
GtkWidget *hbox;
GtkWidget *label;
GtkAdjustment *spacing_adj;
G_OBJECT_CLASS (parent_class)->constructed (object);
pika_context_set_opacity (dialog->context, select->initial_opacity);
pika_context_set_paint_mode (dialog->context, select->initial_mode);
g_signal_connect (dialog->context, "opacity-changed",
G_CALLBACK (pika_brush_select_opacity_changed),
dialog);
g_signal_connect (dialog->context, "paint-mode-changed",
G_CALLBACK (pika_brush_select_mode_changed),
dialog);
dialog->view =
pika_brush_factory_view_new (PIKA_VIEW_TYPE_GRID,
dialog->context->pika->brush_factory,
dialog->context,
FALSE,
PIKA_VIEW_SIZE_MEDIUM, 1,
dialog->menu_factory);
pika_container_box_set_size_request (PIKA_CONTAINER_BOX (PIKA_CONTAINER_EDITOR (dialog->view)->view),
5 * (PIKA_VIEW_SIZE_MEDIUM + 2),
5 * (PIKA_VIEW_SIZE_MEDIUM + 2));
gtk_container_set_border_width (GTK_CONTAINER (dialog->view), 12);
content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
gtk_box_pack_start (GTK_BOX (content_area), dialog->view, TRUE, TRUE, 0);
gtk_widget_show (dialog->view);
vbox = GTK_WIDGET (PIKA_CONTAINER_EDITOR (dialog->view)->view);
/* Create the opacity scale widget */
select->opacity_data =
gtk_adjustment_new (pika_context_get_opacity (dialog->context) * 100.0,
0.0, 100.0,
1.0, 10.0, 0.0);
scale = pika_spin_scale_new (select->opacity_data,
_("Opacity"), 1);
pika_spin_scale_set_constrain_drag (PIKA_SPIN_SCALE (scale), TRUE);
gtk_box_pack_end (GTK_BOX (vbox), scale, FALSE, FALSE, 0);
gtk_widget_show (scale);
g_signal_connect (select->opacity_data, "value-changed",
G_CALLBACK (pika_brush_select_opacity_update),
select);
/* Create the paint mode option menu */
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
gtk_box_pack_end (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
gtk_widget_show (hbox);
label = gtk_label_new (_("Mode:"));
gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
gtk_widget_show (label);
select->layer_mode_box = pika_layer_mode_box_new (PIKA_LAYER_MODE_CONTEXT_PAINT);
gtk_box_pack_start (GTK_BOX (hbox), select->layer_mode_box, TRUE, TRUE, 0);
gtk_widget_show (select->layer_mode_box);
g_object_bind_property (G_OBJECT (dialog->context), "paint-mode",
G_OBJECT (select->layer_mode_box), "layer-mode",
G_BINDING_BIDIRECTIONAL |
G_BINDING_SYNC_CREATE);
spacing_adj = PIKA_BRUSH_FACTORY_VIEW (dialog->view)->spacing_adjustment;
/* Use passed spacing instead of brushes default */
if (select->spacing >= 0)
gtk_adjustment_set_value (spacing_adj, select->spacing);
g_signal_connect (dialog->view, "spacing-changed",
G_CALLBACK (pika_brush_select_spacing_update),
select);
}
static void
pika_brush_select_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
PikaPdbDialog *dialog = PIKA_PDB_DIALOG (object);
PikaBrushSelect *select = PIKA_BRUSH_SELECT (object);
switch (property_id)
{
case PROP_OPACITY:
if (dialog->view)
pika_context_set_opacity (dialog->context, g_value_get_double (value));
else
select->initial_opacity = g_value_get_double (value);
break;
case PROP_PAINT_MODE:
if (dialog->view)
pika_context_set_paint_mode (dialog->context, g_value_get_enum (value));
else
select->initial_mode = g_value_get_enum (value);
break;
case PROP_SPACING:
if (dialog->view)
{
if (g_value_get_int (value) >= 0)
gtk_adjustment_set_value (PIKA_BRUSH_FACTORY_VIEW (dialog->view)->spacing_adjustment,
g_value_get_int (value));
}
else
{
select->spacing = g_value_get_int (value);
}
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static PikaValueArray *
pika_brush_select_run_callback (PikaPdbDialog *dialog,
PikaObject *object,
gboolean closing,
GError **error)
{
PikaBrush *brush = PIKA_BRUSH (object);
PikaTempBuf *mask = pika_brush_get_mask (brush);
const Babl *format;
gpointer data;
GBytes *bytes;
PikaValueArray *return_vals;
format = pika_babl_compat_u8_mask_format (pika_temp_buf_get_format (mask));
data = pika_temp_buf_lock (mask, format, GEGL_ACCESS_READ);
bytes = g_bytes_new_static (data,
pika_temp_buf_get_width (mask) *
pika_temp_buf_get_height (mask) *
babl_format_get_bytes_per_pixel (format));
return_vals =
pika_pdb_execute_procedure_by_name (dialog->pdb,
dialog->caller_context,
NULL, error,
dialog->callback_name,
G_TYPE_STRING, pika_object_get_name (object),
G_TYPE_DOUBLE, pika_context_get_opacity (dialog->context) * 100.0,
G_TYPE_INT, PIKA_BRUSH_SELECT (dialog)->spacing,
PIKA_TYPE_LAYER_MODE, pika_context_get_paint_mode (dialog->context),
G_TYPE_INT, pika_brush_get_width (brush),
G_TYPE_INT, pika_brush_get_height (brush),
G_TYPE_BYTES, bytes,
G_TYPE_BOOLEAN, closing,
G_TYPE_NONE);
g_bytes_unref (bytes);
pika_temp_buf_unlock (mask, data);
return return_vals;
}
static void
pika_brush_select_opacity_changed (PikaContext *context,
gdouble opacity,
PikaBrushSelect *select)
{
g_signal_handlers_block_by_func (select->opacity_data,
pika_brush_select_opacity_update,
select);
gtk_adjustment_set_value (select->opacity_data, opacity * 100.0);
g_signal_handlers_unblock_by_func (select->opacity_data,
pika_brush_select_opacity_update,
select);
pika_pdb_dialog_run_callback ((PikaPdbDialog **) &select, FALSE);
}
static void
pika_brush_select_mode_changed (PikaContext *context,
PikaLayerMode paint_mode,
PikaBrushSelect *select)
{
pika_pdb_dialog_run_callback ((PikaPdbDialog **) &select, FALSE);
}
static void
pika_brush_select_opacity_update (GtkAdjustment *adjustment,
PikaBrushSelect *select)
{
pika_context_set_opacity (PIKA_PDB_DIALOG (select)->context,
gtk_adjustment_get_value (adjustment) / 100.0);
}
static void
pika_brush_select_spacing_update (PikaBrushFactoryView *view,
PikaBrushSelect *select)
{
gdouble value = gtk_adjustment_get_value (view->spacing_adjustment);
if (select->spacing != value)
{
select->spacing = value;
pika_pdb_dialog_run_callback ((PikaPdbDialog **) &select, FALSE);
}
}

View File

@ -0,0 +1,66 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikabrushselect.h
* Copyright (C) 2004 Michael Natterer <mitch@gimp.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __PIKA_BRUSH_SELECT_H__
#define __PIKA_BRUSH_SELECT_H__
#include "pikapdbdialog.h"
G_BEGIN_DECLS
#define PIKA_TYPE_BRUSH_SELECT (pika_brush_select_get_type ())
#define PIKA_BRUSH_SELECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIKA_TYPE_BRUSH_SELECT, PikaBrushSelect))
#define PIKA_BRUSH_SELECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIKA_TYPE_BRUSH_SELECT, PikaBrushSelectClass))
#define PIKA_IS_BRUSH_SELECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIKA_TYPE_BRUSH_SELECT))
#define PIKA_IS_BRUSH_SELECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIKA_TYPE_BRUSH_SELECT))
#define PIKA_BRUSH_SELECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIKA_TYPE_BRUSH_SELECT, PikaBrushSelectClass))
typedef struct _PikaBrushSelectClass PikaBrushSelectClass;
struct _PikaBrushSelect
{
PikaPdbDialog parent_instance;
gdouble initial_opacity;
PikaLayerMode initial_mode;
gint spacing;
GtkAdjustment *opacity_data;
GtkWidget *layer_mode_box;
};
struct _PikaBrushSelectClass
{
PikaPdbDialogClass parent_class;
};
GType pika_brush_select_get_type (void) G_GNUC_CONST;
G_END_DECLS
#endif /* __PIKA_BRUSH_SELECT_H__ */

View File

@ -0,0 +1,358 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikabuffersourcebox.c
* Copyright (C) 2015 Michael Natterer <mitch@gimp.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <string.h>
#include <gegl.h>
#include <gtk/gtk.h>
#include "libpikabase/pikabase.h"
#include "libpikawidgets/pikawidgets.h"
#include "widgets-types.h"
#include "gegl/pika-gegl-utils.h"
#include "core/pikacontext.h"
#include "core/pikapickable.h"
#include "pikabuffersourcebox.h"
#include "pikapickablebutton.h"
#include "pika-intl.h"
enum
{
PROP_0,
PROP_CONTEXT,
PROP_SOURCE_NODE,
PROP_NAME,
PROP_PICKABLE,
PROP_ENABLED
};
struct _PikaBufferSourceBoxPrivate
{
PikaContext *context;
GeglNode *source_node;
gchar *name;
PikaPickable *pickable;
gboolean enabled;
GtkWidget *toggle;
GtkWidget *button;
GtkWidget *label;
};
static void pika_buffer_source_box_constructed (GObject *object);
static void pika_buffer_source_box_finalize (GObject *object);
static void pika_buffer_source_box_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
static void pika_buffer_source_box_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
static void pika_buffer_source_box_notify_pickable (PikaPickableButton *button,
const GParamSpec *pspec,
PikaBufferSourceBox *box);
static void pika_buffer_source_box_enable_toggled (GtkToggleButton *button,
PikaBufferSourceBox *box);
G_DEFINE_TYPE_WITH_PRIVATE (PikaBufferSourceBox, pika_buffer_source_box,
GTK_TYPE_BOX)
#define parent_class pika_buffer_source_box_parent_class
static void
pika_buffer_source_box_class_init (PikaBufferSourceBoxClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->constructed = pika_buffer_source_box_constructed;
object_class->finalize = pika_buffer_source_box_finalize;
object_class->set_property = pika_buffer_source_box_set_property;
object_class->get_property = pika_buffer_source_box_get_property;
g_object_class_install_property (object_class, PROP_CONTEXT,
g_param_spec_object ("context", NULL, NULL,
PIKA_TYPE_CONTEXT,
PIKA_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property (object_class, PROP_SOURCE_NODE,
g_param_spec_object ("source-node", NULL, NULL,
GEGL_TYPE_NODE,
PIKA_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property (object_class, PROP_NAME,
g_param_spec_string ("name", NULL, NULL,
NULL,
PIKA_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property (object_class, PROP_PICKABLE,
g_param_spec_object ("pickable", NULL, NULL,
PIKA_TYPE_PICKABLE,
PIKA_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
g_object_class_install_property (object_class, PROP_ENABLED,
g_param_spec_boolean ("enabled", NULL, NULL,
TRUE,
PIKA_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
}
static void
pika_buffer_source_box_init (PikaBufferSourceBox *box)
{
box->priv = pika_buffer_source_box_get_instance_private (box);
gtk_orientable_set_orientation (GTK_ORIENTABLE (box),
GTK_ORIENTATION_HORIZONTAL);
gtk_box_set_spacing (GTK_BOX (box), 2);
}
static void
pika_buffer_source_box_constructed (GObject *object)
{
PikaBufferSourceBox *box = PIKA_BUFFER_SOURCE_BOX (object);
box->priv->toggle = gtk_check_button_new_with_mnemonic (box->priv->name);
gtk_widget_set_valign (box->priv->toggle, GTK_ALIGN_CENTER);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (box->priv->toggle),
box->priv->enabled);
gtk_box_pack_start (GTK_BOX (box), box->priv->toggle, FALSE, FALSE, 0);
gtk_widget_show (box->priv->toggle);
g_signal_connect_object (box->priv->toggle, "toggled",
G_CALLBACK (pika_buffer_source_box_enable_toggled),
box, 0);
box->priv->button = pika_pickable_button_new (box->priv->context,
PIKA_VIEW_SIZE_LARGE, 1);
pika_pickable_button_set_pickable (PIKA_PICKABLE_BUTTON (box->priv->button),
box->priv->pickable);
gtk_box_pack_start (GTK_BOX (box), box->priv->button, FALSE, FALSE, 0);
gtk_widget_show (box->priv->button);
box->priv->label = gtk_label_new (_("(none)"));
gtk_label_set_xalign (GTK_LABEL (box->priv->label), 0.0);
gtk_label_set_ellipsize (GTK_LABEL (box->priv->label), PANGO_ELLIPSIZE_END);
gtk_box_pack_start (GTK_BOX (box), box->priv->label, TRUE, TRUE, 0);
gtk_widget_show (box->priv->label);
g_signal_connect_object (box->priv->button, "notify::pickable",
G_CALLBACK (pika_buffer_source_box_notify_pickable),
box, 0);
G_OBJECT_CLASS (parent_class)->constructed (object);
}
static void
pika_buffer_source_box_finalize (GObject *object)
{
PikaBufferSourceBox *box = PIKA_BUFFER_SOURCE_BOX (object);
g_clear_object (&box->priv->context);
g_clear_object (&box->priv->source_node);
g_clear_pointer (&box->priv->name, g_free);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
pika_buffer_source_box_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
PikaBufferSourceBox *box = PIKA_BUFFER_SOURCE_BOX (object);
switch (property_id)
{
case PROP_CONTEXT:
box->priv->context = g_value_dup_object (value);
break;
case PROP_SOURCE_NODE:
box->priv->source_node = g_value_dup_object (value);
break;
case PROP_NAME:
box->priv->name = g_value_dup_string (value);
break;
case PROP_PICKABLE:
box->priv->pickable = g_value_get_object (value);
if (box->priv->button)
pika_pickable_button_set_pickable (PIKA_PICKABLE_BUTTON (box->priv->button),
box->priv->pickable);
break;
case PROP_ENABLED:
box->priv->enabled = g_value_get_boolean (value);
if (box->priv->toggle)
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (box->priv->toggle),
box->priv->enabled);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
pika_buffer_source_box_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
PikaBufferSourceBox *box = PIKA_BUFFER_SOURCE_BOX (object);
switch (property_id)
{
case PROP_CONTEXT:
g_value_set_object (value, box->priv->context);
break;
case PROP_SOURCE_NODE:
g_value_set_object (value, box->priv->source_node);
break;
case PROP_NAME:
g_value_set_string (value, box->priv->name);
break;
case PROP_PICKABLE:
g_value_set_object (value, box->priv->pickable);
break;
case PROP_ENABLED:
g_value_set_boolean (value, box->priv->enabled);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
pika_buffer_source_box_update_node (PikaBufferSourceBox *box)
{
GeglBuffer *buffer = NULL;
if (box->priv->pickable)
{
gchar *desc;
if (box->priv->enabled)
{
pika_pickable_flush (box->priv->pickable);
/* dup the buffer, since the original may be modified while applying
* the operation. see issue #1283.
*/
buffer = pika_gegl_buffer_dup (
pika_pickable_get_buffer (box->priv->pickable));
}
desc = pika_viewable_get_description (PIKA_VIEWABLE (box->priv->pickable),
NULL);
gtk_label_set_text (GTK_LABEL (box->priv->label), desc);
g_free (desc);
}
else
{
gtk_label_set_text (GTK_LABEL (box->priv->label), _("(none)"));
}
gegl_node_set (box->priv->source_node,
"buffer", buffer,
NULL);
g_clear_object (&buffer);
}
static void
pika_buffer_source_box_notify_pickable (PikaPickableButton *button,
const GParamSpec *pspec,
PikaBufferSourceBox *box)
{
box->priv->pickable = pika_pickable_button_get_pickable (button);
pika_buffer_source_box_update_node (box);
g_object_notify (G_OBJECT (box), "pickable");
}
static void
pika_buffer_source_box_enable_toggled (GtkToggleButton *button,
PikaBufferSourceBox *box)
{
box->priv->enabled = gtk_toggle_button_get_active (button);
pika_buffer_source_box_update_node (box);
g_object_notify (G_OBJECT (box), "enabled");
}
/* public functions */
GtkWidget *
pika_buffer_source_box_new (PikaContext *context,
GeglNode *source_node,
const gchar *name)
{
g_return_val_if_fail (PIKA_IS_CONTEXT (context), NULL);
g_return_val_if_fail (GEGL_IS_NODE (source_node), NULL);
g_return_val_if_fail (name != NULL, NULL);
return g_object_new (PIKA_TYPE_BUFFER_SOURCE_BOX,
"context", context,
"source-node", source_node,
"name", name,
NULL);
}
GtkWidget *
pika_buffer_source_box_get_toggle (PikaBufferSourceBox *box)
{
g_return_val_if_fail (PIKA_IS_BUFFER_SOURCE_BOX (box), NULL);
return box->priv->toggle;
}

View File

@ -0,0 +1,62 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikabuffersourcebox.h
* Copyright (C) 2015 Michael Natterer <mitch@gimp.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __PIKA_BUFFER_SOURCE_BOX_H__
#define __PIKA_BUFFER_SOURCE_BOX_H__
#define PIKA_TYPE_BUFFER_SOURCE_BOX (pika_buffer_source_box_get_type ())
#define PIKA_BUFFER_SOURCE_BOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIKA_TYPE_BUFFER_SOURCE_BOX, PikaBufferSourceBox))
#define PIKA_BUFFER_SOURCE_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIKA_TYPE_BUFFER_SOURCE_BOX, PikaBufferSourceBoxClass))
#define PIKA_IS_BUFFER_SOURCE_BOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIKA_TYPE_BUFFER_SOURCE_BOX))
#define PIKA_IS_BUFFER_SOURCE_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIKA_TYPE_BUFFER_SOURCE_BOX))
#define PIKA_BUFFER_SOURCE_BOX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIKA_TYPE_BUFFER_SOURCE_BOX, PikaBufferSourceBoxClass))
typedef struct _PikaBufferSourceBoxPrivate PikaBufferSourceBoxPrivate;
typedef struct _PikaBufferSourceBoxClass PikaBufferSourceBoxClass;
struct _PikaBufferSourceBox
{
GtkBox parent_instance;
PikaBufferSourceBoxPrivate *priv;
};
struct _PikaBufferSourceBoxClass
{
GtkBoxClass parent_class;
};
GType pika_buffer_source_box_get_type (void) G_GNUC_CONST;
GtkWidget * pika_buffer_source_box_new (PikaContext *context,
GeglNode *source_node,
const gchar *name);
GtkWidget * pika_buffer_source_box_get_toggle (PikaBufferSourceBox *box);
#endif /* __PIKA_BUFFER_SOURCE_BOX_H__ */

View File

@ -0,0 +1,312 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikabufferview.c
* Copyright (C) 2001-2004 Michael Natterer <mitch@gimp.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gegl.h>
#include <gtk/gtk.h>
#include "libpikawidgets/pikawidgets.h"
#include "widgets-types.h"
#include "core/pika.h"
#include "core/pikabuffer.h"
#include "core/pikacontainer.h"
#include "core/pikacontext.h"
#include "core/pikaimage.h"
#include "pikacontainertreeview.h"
#include "pikacontainerview.h"
#include "pikabufferview.h"
#include "pikadnd.h"
#include "pikadocked.h"
#include "pikaeditor.h"
#include "pikahelp-ids.h"
#include "pikaview.h"
#include "pikaviewrendererbuffer.h"
#include "pikauimanager.h"
#include "pikawidgets-utils.h"
#include "pika-intl.h"
static void pika_buffer_view_docked_iface_init (PikaDockedInterface *iface);
static void pika_buffer_view_set_context (PikaDocked *docked,
PikaContext *context);
static void pika_buffer_view_activate_item (PikaContainerEditor *editor,
PikaViewable *viewable);
static void pika_buffer_view_clipboard_changed (Pika *pika,
PikaBufferView *buffer_view);
static void pika_buffer_view_view_notify (PikaContainerView *view,
GParamSpec *pspec,
PikaBufferView *buffer_view);
G_DEFINE_TYPE_WITH_CODE (PikaBufferView, pika_buffer_view,
PIKA_TYPE_CONTAINER_EDITOR,
G_IMPLEMENT_INTERFACE (PIKA_TYPE_DOCKED,
pika_buffer_view_docked_iface_init))
#define parent_class pika_buffer_view_parent_class
static PikaDockedInterface *parent_docked_iface = NULL;
static void
pika_buffer_view_class_init (PikaBufferViewClass *klass)
{
PikaContainerEditorClass *editor_class = PIKA_CONTAINER_EDITOR_CLASS (klass);
editor_class->activate_item = pika_buffer_view_activate_item;
}
static void
pika_buffer_view_docked_iface_init (PikaDockedInterface *iface)
{
parent_docked_iface = g_type_interface_peek_parent (iface);
if (! parent_docked_iface)
parent_docked_iface = g_type_default_interface_peek (PIKA_TYPE_DOCKED);
iface->set_context = pika_buffer_view_set_context;
}
static void
pika_buffer_view_init (PikaBufferView *view)
{
}
static void
pika_buffer_view_set_context (PikaDocked *docked,
PikaContext *context)
{
PikaBufferView *view = PIKA_BUFFER_VIEW (docked);
parent_docked_iface->set_context (docked, context);
pika_view_renderer_set_context (PIKA_VIEW (view->clipboard_view)->renderer,
context);
}
/* public functions */
GtkWidget *
pika_buffer_view_new (PikaViewType view_type,
PikaContainer *container,
PikaContext *context,
gint view_size,
gint view_border_width,
PikaMenuFactory *menu_factory)
{
PikaBufferView *buffer_view;
PikaContainerEditor *editor;
GtkWidget *frame;
GtkWidget *hbox;
g_return_val_if_fail (PIKA_IS_CONTAINER (container), NULL);
g_return_val_if_fail (PIKA_IS_CONTEXT (context), NULL);
g_return_val_if_fail (view_size > 0 &&
view_size <= PIKA_VIEWABLE_MAX_PREVIEW_SIZE, FALSE);
g_return_val_if_fail (view_border_width >= 0 &&
view_border_width <= PIKA_VIEW_MAX_BORDER_WIDTH,
FALSE);
buffer_view = g_object_new (PIKA_TYPE_BUFFER_VIEW,
"view-type", view_type,
"container", container,
"context", context,
"view-size", view_size,
"view-border-width", view_border_width,
"menu-factory", menu_factory,
"menu-identifier", "<Buffers>",
"ui-path", "/buffers-popup",
NULL);
editor = PIKA_CONTAINER_EDITOR (buffer_view);
if (PIKA_IS_CONTAINER_TREE_VIEW (editor->view))
{
PikaContainerTreeView *tree_view;
tree_view = PIKA_CONTAINER_TREE_VIEW (editor->view);
pika_container_tree_view_connect_name_edited (tree_view,
G_CALLBACK (pika_container_tree_view_name_edited),
tree_view);
}
frame = gtk_frame_new (NULL);
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
gtk_box_pack_start (GTK_BOX (editor), frame, FALSE, FALSE, 0);
gtk_box_reorder_child (GTK_BOX (editor), frame, 0);
gtk_widget_show (frame);
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2);
gtk_container_set_border_width (GTK_CONTAINER (hbox), 2);
gtk_container_add (GTK_CONTAINER (frame), hbox);
gtk_widget_show (hbox);
/* FIXME: enable preview of a clipboard image, not just buffer */
buffer_view->clipboard_view =
pika_view_new_full_by_types (NULL,
PIKA_TYPE_VIEW,
PIKA_TYPE_BUFFER,
view_size, view_size, view_border_width,
FALSE, FALSE, TRUE);
gtk_box_pack_start (GTK_BOX (hbox), buffer_view->clipboard_view,
FALSE, FALSE, 0);
gtk_widget_show (buffer_view->clipboard_view);
g_signal_connect_object (editor->view, "notify::view-size",
G_CALLBACK (pika_buffer_view_view_notify),
buffer_view, 0);
g_signal_connect_object (editor->view, "notify::view-border-width",
G_CALLBACK (pika_buffer_view_view_notify),
buffer_view, 0);
buffer_view->clipboard_label = gtk_label_new (_("(None)"));
gtk_box_pack_start (GTK_BOX (hbox), buffer_view->clipboard_label,
FALSE, FALSE, 0);
gtk_widget_show (buffer_view->clipboard_label);
g_signal_connect_object (context->pika, "clipboard-changed",
G_CALLBACK (pika_buffer_view_clipboard_changed),
G_OBJECT (buffer_view), 0);
pika_buffer_view_clipboard_changed (context->pika, buffer_view);
buffer_view->paste_button =
pika_editor_add_action_button (PIKA_EDITOR (editor->view), "buffers",
"buffers-paste",
"buffers-paste-in-place",
pika_get_extend_selection_mask (),
NULL);
buffer_view->paste_into_button =
pika_editor_add_action_button (PIKA_EDITOR (editor->view), "buffers",
"buffers-paste-into",
"buffers-paste-into-in-place",
pika_get_extend_selection_mask (),
NULL);
buffer_view->paste_as_new_layer_button =
pika_editor_add_action_button (PIKA_EDITOR (editor->view), "buffers",
"buffers-paste-as-new-layer",
"buffers-paste-as-new-layer-in-place",
pika_get_extend_selection_mask (),
NULL);
buffer_view->paste_as_new_image_button =
pika_editor_add_action_button (PIKA_EDITOR (editor->view), "buffers",
"buffers-paste-as-new-image", NULL);
buffer_view->delete_button =
pika_editor_add_action_button (PIKA_EDITOR (editor->view), "buffers",
"buffers-delete", NULL);
pika_container_view_enable_dnd (editor->view,
GTK_BUTTON (buffer_view->paste_button),
PIKA_TYPE_BUFFER);
pika_container_view_enable_dnd (editor->view,
GTK_BUTTON (buffer_view->paste_into_button),
PIKA_TYPE_BUFFER);
pika_container_view_enable_dnd (editor->view,
GTK_BUTTON (buffer_view->paste_as_new_layer_button),
PIKA_TYPE_BUFFER);
pika_container_view_enable_dnd (editor->view,
GTK_BUTTON (buffer_view->paste_as_new_image_button),
PIKA_TYPE_BUFFER);
pika_container_view_enable_dnd (editor->view,
GTK_BUTTON (buffer_view->delete_button),
PIKA_TYPE_BUFFER);
pika_ui_manager_update (pika_editor_get_ui_manager (PIKA_EDITOR (editor->view)),
editor);
return GTK_WIDGET (buffer_view);
}
/* private functions */
static void
pika_buffer_view_activate_item (PikaContainerEditor *editor,
PikaViewable *viewable)
{
PikaBufferView *view = PIKA_BUFFER_VIEW (editor);
PikaContainer *container;
if (PIKA_CONTAINER_EDITOR_CLASS (parent_class)->activate_item)
PIKA_CONTAINER_EDITOR_CLASS (parent_class)->activate_item (editor, viewable);
container = pika_container_view_get_container (editor->view);
if (viewable && pika_container_have (container, PIKA_OBJECT (viewable)))
{
gtk_button_clicked (GTK_BUTTON (view->paste_button));
}
}
static void
pika_buffer_view_clipboard_changed (Pika *pika,
PikaBufferView *buffer_view)
{
PikaBuffer *buffer = pika_get_clipboard_buffer (pika);
pika_view_set_viewable (PIKA_VIEW (buffer_view->clipboard_view),
PIKA_VIEWABLE (buffer));
if (buffer)
{
gchar *desc = pika_viewable_get_description (PIKA_VIEWABLE (buffer),
NULL);
gtk_label_set_text (GTK_LABEL (buffer_view->clipboard_label), desc);
g_free (desc);
}
else
{
gtk_label_set_text (GTK_LABEL (buffer_view->clipboard_label), _("(None)"));
}
}
static void
pika_buffer_view_view_notify (PikaContainerView *container_view,
GParamSpec *pspec,
PikaBufferView *buffer_view)
{
PikaView *view = PIKA_VIEW (buffer_view->clipboard_view);
gint view_size;
gint view_border_width;
view_size = pika_container_view_get_view_size (container_view,
&view_border_width);
pika_view_renderer_set_size_full (view->renderer,
view_size, view_size, view_border_width);
}

View File

@ -0,0 +1,72 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikabufferview.h
* Copyright (C) 2001 Michael Natterer <mitch@gimp.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __PIKA_BUFFER_VIEW_H__
#define __PIKA_BUFFER_VIEW_H__
#include "pikacontainereditor.h"
#define PIKA_TYPE_BUFFER_VIEW (pika_buffer_view_get_type ())
#define PIKA_BUFFER_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIKA_TYPE_BUFFER_VIEW, PikaBufferView))
#define PIKA_BUFFER_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIKA_TYPE_BUFFER_VIEW, PikaBufferViewClass))
#define PIKA_IS_BUFFER_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIKA_TYPE_BUFFER_VIEW))
#define PIKA_IS_BUFFER_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIKA_TYPE_BUFFER_VIEW))
#define PIKA_BUFFER_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIKA_TYPE_BUFFER_VIEW, PikaBufferViewClass))
typedef struct _PikaBufferViewClass PikaBufferViewClass;
struct _PikaBufferView
{
PikaContainerEditor parent_instance;
GtkWidget *clipboard_view;
GtkWidget *clipboard_label;
GtkWidget *paste_button;
GtkWidget *paste_into_button;
GtkWidget *paste_as_new_layer_button;
GtkWidget *paste_as_new_image_button;
GtkWidget *delete_button;
};
struct _PikaBufferViewClass
{
PikaContainerEditorClass parent_class;
};
GType pika_buffer_view_get_type (void) G_GNUC_CONST;
GtkWidget * pika_buffer_view_new (PikaViewType view_type,
PikaContainer *container,
PikaContext *context,
gint view_size,
gint view_border_width,
PikaMenuFactory *menu_factory);
#endif /* __PIKA_BUFFER_VIEW_H__ */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,49 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* Wilber Cairo rendering
* Copyright (C) 2008 Sven Neumann <sven@gimp.org>
*
* Some code here is based on code from librsvg that was originally
* written by Raph Levien <raph@artofcode.com> for Gill.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __PIKA_CAIRO_MASCOT_H__
#define __PIKA_CAIRO_MASCOT_H__
void pika_cairo_mascot_toggle_pointer_eyes (void);
void pika_cairo_draw_toolbox_mascot (GtkWidget *widget,
cairo_t *cr);
void pika_cairo_draw_drop_mascot (GtkWidget *widget,
cairo_t *cr,
gboolean blink);
void pika_cairo_mascot (cairo_t *cr,
gdouble x,
gdouble y);
void pika_cairo_mascot_get_size (cairo_t *cr,
gdouble *width,
gdouble *height);
#endif /* __PIKA_CAIRO_MASCOT_H__ */

View File

@ -0,0 +1,146 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikacellrendererbutton.c
* Copyright (C) 2016 Michael Natterer <mitch@gimp.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <gtk/gtk.h>
#include "widgets-types.h"
#include "core/pikamarshal.h"
#include "pikacellrendererbutton.h"
enum
{
CLICKED,
LAST_SIGNAL
};
static gboolean pika_cell_renderer_button_activate (GtkCellRenderer *cell,
GdkEvent *event,
GtkWidget *widget,
const gchar *path,
const GdkRectangle *background_area,
const GdkRectangle *cell_area,
GtkCellRendererState flags);
G_DEFINE_TYPE (PikaCellRendererButton, pika_cell_renderer_button,
GTK_TYPE_CELL_RENDERER_PIXBUF)
#define parent_class pika_cell_renderer_button_parent_class
static guint button_cell_signals[LAST_SIGNAL] = { 0 };
static void
pika_cell_renderer_button_class_init (PikaCellRendererButtonClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (klass);
/**
* PikaCellRendererButton::clicked:
* @cell:
* @path:
* @state:
*
* Called on a button cell when it is clicked.
**/
button_cell_signals[CLICKED] =
g_signal_new ("clicked",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (PikaCellRendererButtonClass, clicked),
NULL, NULL,
pika_marshal_VOID__STRING_FLAGS,
G_TYPE_NONE, 2,
G_TYPE_STRING,
GDK_TYPE_MODIFIER_TYPE);
cell_class->activate = pika_cell_renderer_button_activate;
klass->clicked = NULL;
}
static void
pika_cell_renderer_button_init (PikaCellRendererButton *cell_button)
{
g_object_set (cell_button,
"mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE,
"xpad", 2,
"ypad", 2,
"stock-size", GTK_ICON_SIZE_BUTTON,
NULL);
}
static gboolean
pika_cell_renderer_button_activate (GtkCellRenderer *cell,
GdkEvent *event,
GtkWidget *widget,
const gchar *path,
const GdkRectangle *background_area,
const GdkRectangle *cell_area,
GtkCellRendererState flags)
{
PikaCellRendererButton *cell_button = PIKA_CELL_RENDERER_BUTTON (cell);
GdkModifierType state = 0;
if (event && ((GdkEventAny *) event)->type == GDK_BUTTON_PRESS)
state = ((GdkEventButton *) event)->state;
if (! event ||
(((GdkEventAny *) event)->type == GDK_BUTTON_PRESS &&
((GdkEventButton *) event)->button == 1))
{
pika_cell_renderer_button_clicked (cell_button, path, state);
return TRUE;
}
return FALSE;
}
/* public functions */
GtkCellRenderer *
pika_cell_renderer_button_new (void)
{
return g_object_new (PIKA_TYPE_CELL_RENDERER_BUTTON, NULL);
}
void
pika_cell_renderer_button_clicked (PikaCellRendererButton *cell,
const gchar *path,
GdkModifierType state)
{
g_return_if_fail (PIKA_IS_CELL_RENDERER_BUTTON (cell));
g_return_if_fail (path != NULL);
g_signal_emit (cell, button_cell_signals[CLICKED], 0, path, state);
}

View File

@ -0,0 +1,63 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikacellrendererbutton.h
* Copyright (C) 2016 Michael Natterer <mitch@gimp.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __PIKA_CELL_RENDERER_BUTTON_H__
#define __PIKA_CELL_RENDERER_BUTTON_H__
#define PIKA_TYPE_CELL_RENDERER_BUTTON (pika_cell_renderer_button_get_type ())
#define PIKA_CELL_RENDERER_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIKA_TYPE_CELL_RENDERER_BUTTON, PikaCellRendererButton))
#define PIKA_CELL_RENDERER_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIKA_TYPE_CELL_RENDERER_BUTTON, PikaCellRendererButtonClass))
#define PIKA_IS_CELL_RENDERER_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIKA_TYPE_CELL_RENDERER_BUTTON))
#define PIKA_IS_CELL_RENDERER_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIKA_TYPE_CELL_RENDERER_BUTTON))
#define PIKA_CELL_RENDERER_BUTTON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIKA_TYPE_CELL_RENDERER_BUTTON, PikaCellRendererButtonClass))
typedef struct _PikaCellRendererButtonClass PikaCellRendererButtonClass;
struct _PikaCellRendererButton
{
GtkCellRendererPixbuf parent_instance;
};
struct _PikaCellRendererButtonClass
{
GtkCellRendererPixbufClass parent_class;
void (* clicked) (PikaCellRendererButton *cell,
const gchar *path,
GdkModifierType state);
};
GType pika_cell_renderer_button_get_type (void) G_GNUC_CONST;
GtkCellRenderer * pika_cell_renderer_button_new (void);
void pika_cell_renderer_button_clicked (PikaCellRendererButton *cell,
const gchar *path,
GdkModifierType state);
#endif /* __PIKA_CELL_RENDERER_BUTTON_H__ */

View File

@ -0,0 +1,239 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikacellrendererdashes.c
* Copyright (C) 2005 Sven Neumann <sven@gimp.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <gtk/gtk.h>
#include "widgets-types.h"
#include "libpikabase/pikabase.h"
#include "core/pikadashpattern.h"
#include "pikacellrendererdashes.h"
#define DASHES_WIDTH 96
#define DASHES_HEIGHT 4
#define N_SEGMENTS 24
#define BLOCK_WIDTH (DASHES_WIDTH / (2 * N_SEGMENTS))
enum
{
PROP_0,
PROP_PATTERN
};
static void pika_cell_renderer_dashes_finalize (GObject *object);
static void pika_cell_renderer_dashes_get_property (GObject *object,
guint param_id,
GValue *value,
GParamSpec *pspec);
static void pika_cell_renderer_dashes_set_property (GObject *object,
guint param_id,
const GValue *value,
GParamSpec *pspec);
static void pika_cell_renderer_dashes_get_size (GtkCellRenderer *cell,
GtkWidget *widget,
const GdkRectangle *rectangle,
gint *x_offset,
gint *y_offset,
gint *width,
gint *height);
static void pika_cell_renderer_dashes_render (GtkCellRenderer *cell,
cairo_t *cr,
GtkWidget *widget,
const GdkRectangle *background_area,
const GdkRectangle *cell_area,
GtkCellRendererState flags);
G_DEFINE_TYPE (PikaCellRendererDashes, pika_cell_renderer_dashes,
GTK_TYPE_CELL_RENDERER)
#define parent_class pika_cell_renderer_dashes_parent_class
static void
pika_cell_renderer_dashes_class_init (PikaCellRendererDashesClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (klass);
object_class->finalize = pika_cell_renderer_dashes_finalize;
object_class->get_property = pika_cell_renderer_dashes_get_property;
object_class->set_property = pika_cell_renderer_dashes_set_property;
cell_class->get_size = pika_cell_renderer_dashes_get_size;
cell_class->render = pika_cell_renderer_dashes_render;
g_object_class_install_property (object_class, PROP_PATTERN,
g_param_spec_boxed ("pattern", NULL, NULL,
PIKA_TYPE_DASH_PATTERN,
PIKA_PARAM_WRITABLE));
}
static void
pika_cell_renderer_dashes_init (PikaCellRendererDashes *dashes)
{
dashes->segments = g_new0 (gboolean, N_SEGMENTS);
}
static void
pika_cell_renderer_dashes_finalize (GObject *object)
{
PikaCellRendererDashes *dashes = PIKA_CELL_RENDERER_DASHES (object);
g_free (dashes->segments);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
pika_cell_renderer_dashes_get_property (GObject *object,
guint param_id,
GValue *value,
GParamSpec *pspec)
{
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
}
static void
pika_cell_renderer_dashes_set_property (GObject *object,
guint param_id,
const GValue *value,
GParamSpec *pspec)
{
PikaCellRendererDashes *dashes = PIKA_CELL_RENDERER_DASHES (object);
switch (param_id)
{
case PROP_PATTERN:
pika_dash_pattern_fill_segments (g_value_get_boxed (value),
dashes->segments, N_SEGMENTS);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
break;
}
}
static void
pika_cell_renderer_dashes_get_size (GtkCellRenderer *cell,
GtkWidget *widget,
const GdkRectangle *cell_area,
gint *x_offset,
gint *y_offset,
gint *width,
gint *height)
{
gfloat xalign, yalign;
gint xpad, ypad;
gtk_cell_renderer_get_alignment (cell, &xalign, &yalign);
gtk_cell_renderer_get_padding (cell, &xpad, &ypad);
if (cell_area)
{
if (x_offset)
{
gdouble align;
align = ((gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) ?
1.0 - xalign : xalign);
*x_offset = align * (cell_area->width - DASHES_WIDTH);
*x_offset = MAX (*x_offset, 0) + xpad;
}
if (y_offset)
{
*y_offset = yalign * (cell_area->height - DASHES_HEIGHT);
*y_offset = MAX (*y_offset, 0) + ypad;
}
}
else
{
if (x_offset)
*x_offset = 0;
if (y_offset)
*y_offset = 0;
}
*width = DASHES_WIDTH + 2 * xpad;
*height = DASHES_HEIGHT + 2 * ypad;
}
static void
pika_cell_renderer_dashes_render (GtkCellRenderer *cell,
cairo_t *cr,
GtkWidget *widget,
const GdkRectangle *background_area,
const GdkRectangle *cell_area,
GtkCellRendererState flags)
{
PikaCellRendererDashes *dashes = PIKA_CELL_RENDERER_DASHES (cell);
GtkStyleContext *style = gtk_widget_get_style_context (widget);
GtkStateFlags state;
GdkRGBA color;
gint xpad, ypad;
gint width;
gint x, y;
gtk_cell_renderer_get_padding (cell, &xpad, &ypad);
state = gtk_cell_renderer_get_state (cell, widget, flags);
y = cell_area->y + (cell_area->height - DASHES_HEIGHT) / 2;
width = cell_area->width - 2 * xpad;
for (x = 0; x < width + BLOCK_WIDTH; x += BLOCK_WIDTH)
{
guint index = ((guint) x / BLOCK_WIDTH) % N_SEGMENTS;
if (dashes->segments[index])
{
cairo_rectangle (cr,
cell_area->x + xpad + x, y,
MIN (BLOCK_WIDTH, width - x), DASHES_HEIGHT);
}
}
gtk_style_context_get_color (style, state, &color);
gdk_cairo_set_source_rgba (cr, &color);
cairo_fill (cr);
}
GtkCellRenderer *
pika_cell_renderer_dashes_new (void)
{
return g_object_new (PIKA_TYPE_CELL_RENDERER_DASHES, NULL);
}

View File

@ -0,0 +1,57 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikacellrendererdashes.h
* Copyright (C) 2005 Sven Neumann <sven@gimp.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __PIKA_CELL_RENDERER_DASHES_H__
#define __PIKA_CELL_RENDERER_DASHES_H__
#define PIKA_TYPE_CELL_RENDERER_DASHES (pika_cell_renderer_dashes_get_type ())
#define PIKA_CELL_RENDERER_DASHES(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIKA_TYPE_CELL_RENDERER_DASHES, PikaCellRendererDashes))
#define PIKA_CELL_RENDERER_DASHES_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIKA_TYPE_CELL_RENDERER_DASHES, PikaCellRendererDashesClass))
#define PIKA_IS_CELL_RENDERER_DASHES(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIKA_TYPE_CELL_RENDERER_DASHES))
#define PIKA_IS_CELL_RENDERER_DASHES_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIKA_TYPE_CELL_RENDERER_DASHES))
#define PIKA_CELL_RENDERER_DASHES_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIKA_TYPE_CELL_RENDERER_DASHES, PikaCellRendererDashesClass))
typedef struct _PikaCellRendererDashesClass PikaCellRendererDashesClass;
struct _PikaCellRendererDashes
{
GtkCellRenderer parent_instance;
gboolean *segments;
};
struct _PikaCellRendererDashesClass
{
GtkCellRendererClass parent_class;
};
GType pika_cell_renderer_dashes_get_type (void) G_GNUC_CONST;
GtkCellRenderer * pika_cell_renderer_dashes_new (void);
#endif /* __PIKA_CELL_RENDERER_DASHES_H__ */

View File

@ -0,0 +1,414 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikacellrendererviewable.c
* Copyright (C) 2003 Michael Natterer <mitch@gimp.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <gegl.h>
#include <gtk/gtk.h>
#include "widgets-types.h"
#include "libpikabase/pikabase.h"
#include "core/pikamarshal.h"
#include "core/pikaviewable.h"
#include "pikacellrendererviewable.h"
#include "pikaview-popup.h"
#include "pikaviewrenderer.h"
enum
{
PRE_CLICKED,
CLICKED,
LAST_SIGNAL
};
enum
{
PROP_0,
PROP_RENDERER
};
static void pika_cell_renderer_viewable_finalize (GObject *object);
static void pika_cell_renderer_viewable_get_property (GObject *object,
guint param_id,
GValue *value,
GParamSpec *pspec);
static void pika_cell_renderer_viewable_set_property (GObject *object,
guint param_id,
const GValue *value,
GParamSpec *pspec);
static void pika_cell_renderer_viewable_get_size (GtkCellRenderer *cell,
GtkWidget *widget,
const GdkRectangle *rectangle,
gint *x_offset,
gint *y_offset,
gint *width,
gint *height);
static void pika_cell_renderer_viewable_render (GtkCellRenderer *cell,
cairo_t *cr,
GtkWidget *widget,
const GdkRectangle *background_area,
const GdkRectangle *cell_area,
GtkCellRendererState flags);
static gboolean pika_cell_renderer_viewable_activate (GtkCellRenderer *cell,
GdkEvent *event,
GtkWidget *widget,
const gchar *path,
const GdkRectangle *background_area,
const GdkRectangle *cell_area,
GtkCellRendererState flags);
G_DEFINE_TYPE (PikaCellRendererViewable, pika_cell_renderer_viewable,
GTK_TYPE_CELL_RENDERER)
#define parent_class pika_cell_renderer_viewable_parent_class
static guint viewable_cell_signals[LAST_SIGNAL] = { 0 };
static void
pika_cell_renderer_viewable_class_init (PikaCellRendererViewableClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (klass);
/**
* PikaCellRendererViewable::pre-clicked:
* @cell:
* @path:
* @state:
*
* Called early on a viewable cell when it is clicked, typically
* before selection code is invoked for example.
*
* Returns: %TRUE if the signal handled the event and event
* propagation should stop, for example preventing a
* selection from happening, %FALSE to continue as normal
**/
viewable_cell_signals[PRE_CLICKED] =
g_signal_new ("pre-clicked",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (PikaCellRendererViewableClass, pre_clicked),
g_signal_accumulator_true_handled, NULL,
pika_marshal_BOOLEAN__STRING_FLAGS,
G_TYPE_BOOLEAN, 2,
G_TYPE_STRING,
GDK_TYPE_MODIFIER_TYPE);
/**
* PikaCellRendererViewable::clicked:
* @cell:
* @path:
* @state:
*
* Called late on a viewable cell when it is clicked, typically
* after selection code has been invoked for example.
**/
viewable_cell_signals[CLICKED] =
g_signal_new ("clicked",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (PikaCellRendererViewableClass, clicked),
NULL, NULL,
pika_marshal_VOID__STRING_FLAGS,
G_TYPE_NONE, 2,
G_TYPE_STRING,
GDK_TYPE_MODIFIER_TYPE);
object_class->finalize = pika_cell_renderer_viewable_finalize;
object_class->get_property = pika_cell_renderer_viewable_get_property;
object_class->set_property = pika_cell_renderer_viewable_set_property;
cell_class->get_size = pika_cell_renderer_viewable_get_size;
cell_class->render = pika_cell_renderer_viewable_render;
cell_class->activate = pika_cell_renderer_viewable_activate;
klass->clicked = NULL;
g_object_class_install_property (object_class, PROP_RENDERER,
g_param_spec_object ("renderer",
NULL, NULL,
PIKA_TYPE_VIEW_RENDERER,
PIKA_PARAM_READWRITE));
}
static void
pika_cell_renderer_viewable_init (PikaCellRendererViewable *cellviewable)
{
g_object_set (cellviewable,
"mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE,
NULL);
}
static void
pika_cell_renderer_viewable_finalize (GObject *object)
{
PikaCellRendererViewable *cell = PIKA_CELL_RENDERER_VIEWABLE (object);
g_clear_object (&cell->renderer);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
pika_cell_renderer_viewable_get_property (GObject *object,
guint param_id,
GValue *value,
GParamSpec *pspec)
{
PikaCellRendererViewable *cell = PIKA_CELL_RENDERER_VIEWABLE (object);
switch (param_id)
{
case PROP_RENDERER:
g_value_set_object (value, cell->renderer);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
break;
}
}
static void
pika_cell_renderer_viewable_set_property (GObject *object,
guint param_id,
const GValue *value,
GParamSpec *pspec)
{
PikaCellRendererViewable *cell = PIKA_CELL_RENDERER_VIEWABLE (object);
switch (param_id)
{
case PROP_RENDERER:
{
PikaViewRenderer *renderer = g_value_dup_object (value);
if (cell->renderer)
g_object_unref (cell->renderer);
cell->renderer = renderer;
}
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
break;
}
}
static void
pika_cell_renderer_viewable_get_size (GtkCellRenderer *cell,
GtkWidget *widget,
const GdkRectangle *cell_area,
gint *x_offset,
gint *y_offset,
gint *width,
gint *height)
{
PikaCellRendererViewable *cellviewable;
gfloat xalign, yalign;
gint xpad, ypad;
gint view_width = 0;
gint view_height = 0;
gint calc_width;
gint calc_height;
gtk_cell_renderer_get_alignment (cell, &xalign, &yalign);
gtk_cell_renderer_get_padding (cell, &xpad, &ypad);
cellviewable = PIKA_CELL_RENDERER_VIEWABLE (cell);
if (cellviewable->renderer)
{
view_width = (cellviewable->renderer->width +
2 * cellviewable->renderer->border_width);
view_height = (cellviewable->renderer->height +
2 * cellviewable->renderer->border_width);
}
calc_width = (gint) xpad * 2 + view_width;
calc_height = (gint) ypad * 2 + view_height;
if (x_offset) *x_offset = 0;
if (y_offset) *y_offset = 0;
if (cell_area && view_width > 0 && view_height > 0)
{
if (x_offset)
{
*x_offset = (((gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) ?
1.0 - xalign : xalign) *
(cell_area->width - calc_width - 2 * xpad));
*x_offset = (MAX (*x_offset, 0) + xpad);
}
if (y_offset)
{
*y_offset = (yalign * (cell_area->height - calc_height - 2 * ypad));
*y_offset = (MAX (*y_offset, 0) + ypad);
}
}
if (width) *width = calc_width;
if (height) *height = calc_height;
}
static void
pika_cell_renderer_viewable_render (GtkCellRenderer *cell,
cairo_t *cr,
GtkWidget *widget,
const GdkRectangle *background_area,
const GdkRectangle *cell_area,
GtkCellRendererState flags)
{
PikaCellRendererViewable *cellviewable;
cellviewable = PIKA_CELL_RENDERER_VIEWABLE (cell);
if (cellviewable->renderer)
{
if (! (flags & GTK_CELL_RENDERER_SELECTED))
{
/* this is an ugly hack. The cell state should be passed to
* the view renderer, so that it can adjust its border.
* (or something like this) */
if (cellviewable->renderer->border_type == PIKA_VIEW_BORDER_WHITE)
pika_view_renderer_set_border_type (cellviewable->renderer,
PIKA_VIEW_BORDER_BLACK);
pika_view_renderer_remove_idle (cellviewable->renderer);
}
cairo_translate (cr, cell_area->x, cell_area->y);
pika_view_renderer_draw (cellviewable->renderer, widget, cr,
cell_area->width,
cell_area->height);
}
}
static gboolean
pika_cell_renderer_viewable_activate (GtkCellRenderer *cell,
GdkEvent *event,
GtkWidget *widget,
const gchar *path,
const GdkRectangle *background_area,
const GdkRectangle *cell_area,
GtkCellRendererState flags)
{
PikaCellRendererViewable *cellviewable;
cellviewable = PIKA_CELL_RENDERER_VIEWABLE (cell);
if (cellviewable->renderer)
{
GdkModifierType state = 0;
if (event && ((GdkEventAny *) event)->type == GDK_BUTTON_PRESS)
state = ((GdkEventButton *) event)->state;
if (! event ||
(((GdkEventAny *) event)->type == GDK_BUTTON_PRESS &&
((GdkEventButton *) event)->button == 1))
{
pika_cell_renderer_viewable_clicked (cellviewable, path, state);
return TRUE;
}
}
return FALSE;
}
GtkCellRenderer *
pika_cell_renderer_viewable_new (void)
{
return g_object_new (PIKA_TYPE_CELL_RENDERER_VIEWABLE, NULL);
}
gboolean
pika_cell_renderer_viewable_pre_clicked (PikaCellRendererViewable *cell,
const gchar *path,
GdkModifierType state)
{
gboolean handled = FALSE;
g_return_val_if_fail (PIKA_IS_CELL_RENDERER_VIEWABLE (cell), FALSE);
g_return_val_if_fail (path != NULL, FALSE);
g_signal_emit (cell,
viewable_cell_signals[PRE_CLICKED],
0 /*detail*/,
path, state,
&handled);
return handled;
}
void
pika_cell_renderer_viewable_clicked (PikaCellRendererViewable *cell,
const gchar *path,
GdkModifierType state)
{
g_return_if_fail (PIKA_IS_CELL_RENDERER_VIEWABLE (cell));
g_return_if_fail (path != NULL);
if (cell->renderer)
{
GdkEvent *event = gtk_get_current_event ();
if (event)
{
GdkEventButton *bevent = (GdkEventButton *) event;
GdkModifierType modifiers = gtk_accelerator_get_default_mod_mask ();
if (bevent->type == GDK_BUTTON_PRESS &&
(bevent->state & modifiers) == 0 &&
(bevent->button == 1 || bevent->button == 2))
{
pika_view_popup_show (gtk_get_event_widget (event),
bevent,
cell->renderer->context,
cell->renderer->viewable,
cell->renderer->width,
cell->renderer->height,
cell->renderer->dot_for_dot);
}
gdk_event_free (event);
}
}
/* emit the signal last so no callback effects can set
* cell->renderer to NULL.
*/
g_signal_emit (cell, viewable_cell_signals[CLICKED], 0, path, state);
}

View File

@ -0,0 +1,69 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikacellrendererviewable.h
* Copyright (C) 2003 Michael Natterer <mitch@gimp.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __PIKA_CELL_RENDERER_VIEWABLE_H__
#define __PIKA_CELL_RENDERER_VIEWABLE_H__
#define PIKA_TYPE_CELL_RENDERER_VIEWABLE (pika_cell_renderer_viewable_get_type ())
#define PIKA_CELL_RENDERER_VIEWABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIKA_TYPE_CELL_RENDERER_VIEWABLE, PikaCellRendererViewable))
#define PIKA_CELL_RENDERER_VIEWABLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIKA_TYPE_CELL_RENDERER_VIEWABLE, PikaCellRendererViewableClass))
#define PIKA_IS_CELL_RENDERER_VIEWABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIKA_TYPE_CELL_RENDERER_VIEWABLE))
#define PIKA_IS_CELL_RENDERER_VIEWABLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIKA_TYPE_CELL_RENDERER_VIEWABLE))
#define PIKA_CELL_RENDERER_VIEWABLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIKA_TYPE_CELL_RENDERER_VIEWABLE, PikaCellRendererViewableClass))
typedef struct _PikaCellRendererViewableClass PikaCellRendererViewableClass;
struct _PikaCellRendererViewable
{
GtkCellRenderer parent_instance;
PikaViewRenderer *renderer;
};
struct _PikaCellRendererViewableClass
{
GtkCellRendererClass parent_class;
gboolean (* pre_clicked) (PikaCellRendererViewable *cell,
const gchar *path,
GdkModifierType state);
void (* clicked) (PikaCellRendererViewable *cell,
const gchar *path,
GdkModifierType state);
};
GType pika_cell_renderer_viewable_get_type (void) G_GNUC_CONST;
GtkCellRenderer * pika_cell_renderer_viewable_new (void);
gboolean pika_cell_renderer_viewable_pre_clicked (PikaCellRendererViewable *cell,
const gchar *path,
GdkModifierType state);
void pika_cell_renderer_viewable_clicked (PikaCellRendererViewable *cell,
const gchar *path,
GdkModifierType state);
#endif /* __PIKA_CELL_RENDERER_VIEWABLE_H__ */

View File

@ -0,0 +1,376 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikachanneltreeview.c
* Copyright (C) 2001-2009 Michael Natterer <mitch@gimp.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gegl.h>
#include <gtk/gtk.h>
#include "libpikabase/pikabase.h"
#include "libpikacolor/pikacolor.h"
#include "libpikawidgets/pikawidgets.h"
#include "widgets-types.h"
#include "core/pikachannel.h"
#include "core/pikacontainer.h"
#include "core/pikaimage.h"
#include "core/pikaimage-undo.h"
#include "core/pikalayer.h"
#include "core/pikalayermask.h"
#include "pikaactiongroup.h"
#include "pikachanneltreeview.h"
#include "pikacomponenteditor.h"
#include "pikacontainerview.h"
#include "pikadnd.h"
#include "pikadocked.h"
#include "pikahelp-ids.h"
#include "pikauimanager.h"
#include "pikawidgets-utils.h"
#include "pika-intl.h"
struct _PikaChannelTreeViewPrivate
{
GtkWidget *component_editor;
GtkWidget *toselection_button;
};
static void pika_channel_tree_view_view_iface_init (PikaContainerViewInterface *iface);
static void pika_channel_tree_view_constructed (GObject *object);
static void pika_channel_tree_view_drop_viewables (PikaContainerTreeView *view,
GList *src_viewables,
PikaViewable *dest_viewable,
GtkTreeViewDropPosition drop_pos);
static void pika_channel_tree_view_drop_component (PikaContainerTreeView *tree_view,
PikaImage *image,
PikaChannelType component,
PikaViewable *dest_viewable,
GtkTreeViewDropPosition drop_pos);
static void pika_channel_tree_view_set_image (PikaItemTreeView *item_view,
PikaImage *image);
static PikaItem * pika_channel_tree_view_item_new (PikaImage *image);
static void pika_channel_tree_view_set_context (PikaContainerView *view,
PikaContext *context);
static void pika_channel_tree_view_set_view_size (PikaContainerView *view);
G_DEFINE_TYPE_WITH_CODE (PikaChannelTreeView, pika_channel_tree_view,
PIKA_TYPE_DRAWABLE_TREE_VIEW,
G_ADD_PRIVATE (PikaChannelTreeView)
G_IMPLEMENT_INTERFACE (PIKA_TYPE_CONTAINER_VIEW,
pika_channel_tree_view_view_iface_init))
#define parent_class pika_channel_tree_view_parent_class
static PikaContainerViewInterface *parent_view_iface = NULL;
static void
pika_channel_tree_view_class_init (PikaChannelTreeViewClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
PikaContainerTreeViewClass *view_class = PIKA_CONTAINER_TREE_VIEW_CLASS (klass);
PikaItemTreeViewClass *iv_class = PIKA_ITEM_TREE_VIEW_CLASS (klass);
object_class->constructed = pika_channel_tree_view_constructed;
view_class->drop_viewables = pika_channel_tree_view_drop_viewables;
view_class->drop_component = pika_channel_tree_view_drop_component;
iv_class->set_image = pika_channel_tree_view_set_image;
iv_class->item_type = PIKA_TYPE_CHANNEL;
iv_class->signal_name = "selected-channels-changed";
iv_class->get_container = pika_image_get_channels;
iv_class->get_selected_items = (PikaGetItemsFunc) pika_image_get_selected_channels;
iv_class->set_selected_items = (PikaSetItemsFunc) pika_image_set_selected_channels;
iv_class->add_item = (PikaAddItemFunc) pika_image_add_channel;
iv_class->remove_item = (PikaRemoveItemFunc) pika_image_remove_channel;
iv_class->new_item = pika_channel_tree_view_item_new;
iv_class->action_group = "channels";
iv_class->activate_action = "channels-edit-attributes";
iv_class->new_action = "channels-new";
iv_class->new_default_action = "channels-new-last-values";
iv_class->raise_action = "channels-raise";
iv_class->raise_top_action = "channels-raise-to-top";
iv_class->lower_action = "channels-lower";
iv_class->lower_bottom_action = "channels-lower-to-bottom";
iv_class->duplicate_action = "channels-duplicate";
iv_class->delete_action = "channels-delete";
iv_class->lock_content_help_id = PIKA_HELP_CHANNEL_LOCK_PIXELS;
iv_class->lock_position_help_id = PIKA_HELP_CHANNEL_LOCK_POSITION;
}
static void
pika_channel_tree_view_view_iface_init (PikaContainerViewInterface *view_iface)
{
parent_view_iface = g_type_interface_peek_parent (view_iface);
view_iface->set_context = pika_channel_tree_view_set_context;
view_iface->set_view_size = pika_channel_tree_view_set_view_size;
}
static void
pika_channel_tree_view_init (PikaChannelTreeView *view)
{
view->priv = pika_channel_tree_view_get_instance_private (view);
view->priv->component_editor = NULL;
view->priv->toselection_button = NULL;
}
static void
pika_channel_tree_view_constructed (GObject *object)
{
PikaChannelTreeView *view = PIKA_CHANNEL_TREE_VIEW (object);
PikaContainerTreeView *tree_view = PIKA_CONTAINER_TREE_VIEW (object);
GdkModifierType extend_mask;
GdkModifierType modify_mask;
G_OBJECT_CLASS (parent_class)->constructed (object);
extend_mask = gtk_widget_get_modifier_mask (GTK_WIDGET (object),
GDK_MODIFIER_INTENT_EXTEND_SELECTION);
modify_mask = gtk_widget_get_modifier_mask (GTK_WIDGET (object),
GDK_MODIFIER_INTENT_MODIFY_SELECTION);
pika_dnd_viewable_dest_add (GTK_WIDGET (tree_view->view), PIKA_TYPE_LAYER,
NULL, tree_view);
pika_dnd_viewable_dest_add (GTK_WIDGET (tree_view->view), PIKA_TYPE_LAYER_MASK,
NULL, tree_view);
pika_dnd_component_dest_add (GTK_WIDGET (tree_view->view),
NULL, tree_view);
view->priv->toselection_button =
pika_editor_add_action_button (PIKA_EDITOR (view), "channels",
"channels-selection-replace",
"channels-selection-add",
extend_mask,
"channels-selection-subtract",
modify_mask,
"channels-selection-intersect",
extend_mask | modify_mask,
NULL);
pika_container_view_enable_dnd (PIKA_CONTAINER_VIEW (view),
GTK_BUTTON (view->priv->toselection_button),
PIKA_TYPE_CHANNEL);
gtk_box_reorder_child (pika_editor_get_button_box (PIKA_EDITOR (view)),
view->priv->toselection_button, 4);
}
/* PikaContainerTreeView methods */
static void
pika_channel_tree_view_drop_viewables (PikaContainerTreeView *tree_view,
GList *src_viewables,
PikaViewable *dest_viewable,
GtkTreeViewDropPosition drop_pos)
{
PikaItemTreeView *item_view = PIKA_ITEM_TREE_VIEW (tree_view);
PikaImage *image = pika_item_tree_view_get_image (item_view);
PikaItemTreeViewClass *item_view_class;
GList *iter;
item_view_class = PIKA_ITEM_TREE_VIEW_GET_CLASS (item_view);
for (iter = src_viewables; iter; iter = iter->next)
{
PikaViewable *src_viewable = iter->data;
if (PIKA_IS_DRAWABLE (src_viewable) &&
(image != pika_item_get_image (PIKA_ITEM (src_viewable)) ||
G_TYPE_FROM_INSTANCE (src_viewable) != item_view_class->item_type))
{
PikaItem *new_item;
PikaItem *parent;
gint index;
index = pika_item_tree_view_get_drop_index (item_view, dest_viewable,
drop_pos,
(PikaViewable **) &parent);
new_item = pika_item_convert (PIKA_ITEM (src_viewable),
pika_item_tree_view_get_image (item_view),
item_view_class->item_type);
item_view_class->add_item (image, new_item, parent, index, TRUE);
pika_image_flush (image);
return;
}
}
PIKA_CONTAINER_TREE_VIEW_CLASS (parent_class)->drop_viewables (tree_view,
src_viewables,
dest_viewable,
drop_pos);
}
static void
pika_channel_tree_view_drop_component (PikaContainerTreeView *tree_view,
PikaImage *src_image,
PikaChannelType component,
PikaViewable *dest_viewable,
GtkTreeViewDropPosition drop_pos)
{
PikaItemTreeView *item_view = PIKA_ITEM_TREE_VIEW (tree_view);
PikaImage *image = pika_item_tree_view_get_image (item_view);
PikaItem *new_item;
PikaChannel *parent;
gint index;
const gchar *desc;
gchar *name;
index = pika_item_tree_view_get_drop_index (item_view, dest_viewable,
drop_pos,
(PikaViewable **) &parent);
pika_enum_get_value (PIKA_TYPE_CHANNEL_TYPE, component,
NULL, NULL, &desc, NULL);
name = g_strdup_printf (_("%s Channel Copy"), desc);
new_item = PIKA_ITEM (pika_channel_new_from_component (src_image, component,
name, NULL));
/* copied components are invisible by default so subsequent copies
* of components don't affect each other
*/
pika_item_set_visible (new_item, FALSE, FALSE);
g_free (name);
if (src_image != image)
PIKA_ITEM_GET_CLASS (new_item)->convert (new_item, image,
PIKA_TYPE_CHANNEL);
pika_image_add_channel (image, PIKA_CHANNEL (new_item), parent, index, TRUE);
pika_image_flush (image);
}
/* PikaItemTreeView methods */
static void
pika_channel_tree_view_set_image (PikaItemTreeView *item_view,
PikaImage *image)
{
PikaChannelTreeView *channel_view = PIKA_CHANNEL_TREE_VIEW (item_view);
if (! channel_view->priv->component_editor)
{
PikaContainerView *view = PIKA_CONTAINER_VIEW (item_view);
gint view_size;
view_size = pika_container_view_get_view_size (view, NULL);
channel_view->priv->component_editor =
pika_component_editor_new (view_size,
pika_editor_get_menu_factory (PIKA_EDITOR (item_view)));
pika_docked_set_context (PIKA_DOCKED (channel_view->priv->component_editor),
pika_container_view_get_context (view));
gtk_box_pack_start (GTK_BOX (item_view), channel_view->priv->component_editor,
FALSE, FALSE, 0);
gtk_box_reorder_child (GTK_BOX (item_view),
channel_view->priv->component_editor, 0);
}
if (! image)
gtk_widget_hide (channel_view->priv->component_editor);
pika_image_editor_set_image (PIKA_IMAGE_EDITOR (channel_view->priv->component_editor),
image);
PIKA_ITEM_TREE_VIEW_CLASS (parent_class)->set_image (item_view, image);
if (pika_item_tree_view_get_image (item_view))
gtk_widget_show (channel_view->priv->component_editor);
}
static PikaItem *
pika_channel_tree_view_item_new (PikaImage *image)
{
PikaChannel *new_channel;
PikaRGB color;
pika_rgba_set (&color, 0.0, 0.0, 0.0, 0.5);
pika_image_undo_group_start (image, PIKA_UNDO_GROUP_EDIT_PASTE,
_("New Channel"));
new_channel = pika_channel_new (image,
pika_image_get_width (image),
pika_image_get_height (image),
_("Channel"), &color);
pika_image_add_channel (image, new_channel,
PIKA_IMAGE_ACTIVE_PARENT, -1, TRUE);
pika_image_undo_group_end (image);
return PIKA_ITEM (new_channel);
}
/* PikaContainerView methods */
static void
pika_channel_tree_view_set_context (PikaContainerView *view,
PikaContext *context)
{
PikaChannelTreeView *channel_view = PIKA_CHANNEL_TREE_VIEW (view);
parent_view_iface->set_context (view, context);
if (channel_view->priv->component_editor)
pika_docked_set_context (PIKA_DOCKED (channel_view->priv->component_editor),
context);
}
static void
pika_channel_tree_view_set_view_size (PikaContainerView *view)
{
PikaChannelTreeView *channel_view = PIKA_CHANNEL_TREE_VIEW (view);
gint view_size;
parent_view_iface->set_view_size (view);
view_size = pika_container_view_get_view_size (view, NULL);
if (channel_view->priv->component_editor)
pika_component_editor_set_view_size (PIKA_COMPONENT_EDITOR (channel_view->priv->component_editor),
view_size);
}

View File

@ -0,0 +1,59 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikachanneltreeview.h
* Copyright (C) 2001-2003 Michael Natterer <mitch@gimp.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __PIKA_CHANNEL_TREE_VIEW_H__
#define __PIKA_CHANNEL_TREE_VIEW_H__
#include "pikadrawabletreeview.h"
#define PIKA_TYPE_CHANNEL_TREE_VIEW (pika_channel_tree_view_get_type ())
#define PIKA_CHANNEL_TREE_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIKA_TYPE_CHANNEL_TREE_VIEW, PikaChannelTreeView))
#define PIKA_CHANNEL_TREE_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIKA_TYPE_CHANNEL_TREE_VIEW, PikaChannelTreeViewClass))
#define PIKA_IS_CHANNEL_TREE_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIKA_TYPE_CHANNEL_TREE_VIEW))
#define PIKA_IS_CHANNEL_TREE_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIKA_TYPE_CHANNEL_TREE_VIEW))
#define PIKA_CHANNEL_TREE_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIKA_TYPE_CHANNEL_TREE_VIEW, PikaChannelTreeViewClass))
typedef struct _PikaChannelTreeViewClass PikaChannelTreeViewClass;
typedef struct _PikaChannelTreeViewPrivate PikaChannelTreeViewPrivate;
struct _PikaChannelTreeView
{
PikaDrawableTreeView parent_instance;
PikaChannelTreeViewPrivate *priv;
};
struct _PikaChannelTreeViewClass
{
PikaDrawableTreeViewClass parent_class;
};
GType pika_channel_tree_view_get_type (void) G_GNUC_CONST;
#endif /* __PIKA_CHANNEL_TREE_VIEW_H__ */

602
app/widgets/pikacircle.c Normal file
View File

@ -0,0 +1,602 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikacircle.c
* Copyright (C) 2014 Michael Natterer <mitch@gimp.org>
*
* Based on code from the color-rotate plug-in
* Copyright (C) 1997-1999 Sven Anders (anderss@fmi.uni-passau.de)
* Based on code from Pavel Grinfeld (pavel@ml.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gegl.h>
#include <gtk/gtk.h>
#include "libpikabase/pikabase.h"
#include "libpikamath/pikamath.h"
#include "libpikacolor/pikacolor.h"
#include "libpikawidgets/pikawidgets.h"
#include "widgets-types.h"
#include "pikacircle.h"
enum
{
PROP_0,
PROP_SIZE,
PROP_BORDER_WIDTH,
PROP_BACKGROUND
};
struct _PikaCirclePrivate
{
gint size;
gint border_width;
PikaCircleBackground background;
GdkWindow *event_window;
cairo_surface_t *surface;
gboolean has_grab;
gboolean in_widget;
};
static void pika_circle_dispose (GObject *object);
static void pika_circle_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
static void pika_circle_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
static void pika_circle_realize (GtkWidget *widget);
static void pika_circle_unrealize (GtkWidget *widget);
static void pika_circle_map (GtkWidget *widget);
static void pika_circle_unmap (GtkWidget *widget);
static void pika_circle_get_preferred_width (GtkWidget *widget,
gint *minimum_width,
gint *natural_width);
static void pika_circle_get_preferred_height (GtkWidget *widget,
gint *minimum_height,
gint *natural_height);
static void pika_circle_size_allocate (GtkWidget *widget,
GtkAllocation *allocation);
static gboolean pika_circle_draw (GtkWidget *widget,
cairo_t *cr);
static gboolean pika_circle_button_press_event (GtkWidget *widget,
GdkEventButton *bevent);
static gboolean pika_circle_button_release_event (GtkWidget *widget,
GdkEventButton *bevent);
static gboolean pika_circle_enter_notify_event (GtkWidget *widget,
GdkEventCrossing *event);
static gboolean pika_circle_leave_notify_event (GtkWidget *widget,
GdkEventCrossing *event);
static void pika_circle_real_reset_target (PikaCircle *circle);
static void pika_circle_background_hsv (gdouble angle,
gdouble distance,
guchar *rgb);
static void pika_circle_draw_background (PikaCircle *circle,
cairo_t *cr,
gint size,
PikaCircleBackground background);
G_DEFINE_TYPE_WITH_PRIVATE (PikaCircle, pika_circle, GTK_TYPE_WIDGET)
#define parent_class pika_circle_parent_class
static void
pika_circle_class_init (PikaCircleClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
object_class->dispose = pika_circle_dispose;
object_class->get_property = pika_circle_get_property;
object_class->set_property = pika_circle_set_property;
widget_class->realize = pika_circle_realize;
widget_class->unrealize = pika_circle_unrealize;
widget_class->map = pika_circle_map;
widget_class->unmap = pika_circle_unmap;
widget_class->get_preferred_width = pika_circle_get_preferred_width;
widget_class->get_preferred_height = pika_circle_get_preferred_height;
widget_class->size_allocate = pika_circle_size_allocate;
widget_class->draw = pika_circle_draw;
widget_class->button_press_event = pika_circle_button_press_event;
widget_class->button_release_event = pika_circle_button_release_event;
widget_class->enter_notify_event = pika_circle_enter_notify_event;
widget_class->leave_notify_event = pika_circle_leave_notify_event;
klass->reset_target = pika_circle_real_reset_target;
g_object_class_install_property (object_class, PROP_SIZE,
g_param_spec_int ("size",
NULL, NULL,
32, 1024, 96,
PIKA_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
g_object_class_install_property (object_class, PROP_BORDER_WIDTH,
g_param_spec_int ("border-width",
NULL, NULL,
0, 64, 0,
PIKA_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
g_object_class_install_property (object_class, PROP_BACKGROUND,
g_param_spec_enum ("background",
NULL, NULL,
PIKA_TYPE_CIRCLE_BACKGROUND,
PIKA_CIRCLE_BACKGROUND_HSV,
PIKA_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
}
static void
pika_circle_init (PikaCircle *circle)
{
circle->priv = pika_circle_get_instance_private (circle);
gtk_widget_set_has_window (GTK_WIDGET (circle), FALSE);
gtk_widget_add_events (GTK_WIDGET (circle),
GDK_POINTER_MOTION_MASK |
GDK_BUTTON_PRESS_MASK |
GDK_BUTTON_RELEASE_MASK |
GDK_BUTTON1_MOTION_MASK |
GDK_ENTER_NOTIFY_MASK |
GDK_LEAVE_NOTIFY_MASK);
}
static void
pika_circle_dispose (GObject *object)
{
PikaCircle *circle = PIKA_CIRCLE (object);
g_clear_pointer (&circle->priv->surface, cairo_surface_destroy);
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
pika_circle_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
PikaCircle *circle = PIKA_CIRCLE (object);
switch (property_id)
{
case PROP_SIZE:
circle->priv->size = g_value_get_int (value);
gtk_widget_queue_resize (GTK_WIDGET (circle));
break;
case PROP_BORDER_WIDTH:
circle->priv->border_width = g_value_get_int (value);
gtk_widget_queue_resize (GTK_WIDGET (circle));
break;
case PROP_BACKGROUND:
circle->priv->background = g_value_get_enum (value);
g_clear_pointer (&circle->priv->surface, cairo_surface_destroy);
gtk_widget_queue_draw (GTK_WIDGET (circle));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
pika_circle_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
PikaCircle *circle = PIKA_CIRCLE (object);
switch (property_id)
{
case PROP_SIZE:
g_value_set_int (value, circle->priv->size);
break;
case PROP_BORDER_WIDTH:
g_value_set_int (value, circle->priv->border_width);
break;
case PROP_BACKGROUND:
g_value_set_enum (value, circle->priv->background);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
pika_circle_realize (GtkWidget *widget)
{
PikaCircle *circle = PIKA_CIRCLE (widget);
GtkAllocation allocation;
GdkWindowAttr attributes;
gint attributes_mask;
GTK_WIDGET_CLASS (parent_class)->realize (widget);
gtk_widget_get_allocation (widget, &allocation);
attributes.window_type = GDK_WINDOW_CHILD;
attributes.x = allocation.x;
attributes.y = allocation.y;
attributes.width = allocation.width;
attributes.height = allocation.height;
attributes.wclass = GDK_INPUT_ONLY;
attributes.event_mask = gtk_widget_get_events (widget);
attributes_mask = GDK_WA_X | GDK_WA_Y;
circle->priv->event_window = gdk_window_new (gtk_widget_get_window (widget),
&attributes, attributes_mask);
gdk_window_set_user_data (circle->priv->event_window, circle);
}
static void
pika_circle_unrealize (GtkWidget *widget)
{
PikaCircle *circle = PIKA_CIRCLE (widget);
if (circle->priv->event_window)
{
gdk_window_set_user_data (circle->priv->event_window, NULL);
gdk_window_destroy (circle->priv->event_window);
circle->priv->event_window = NULL;
}
GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
}
static void
pika_circle_map (GtkWidget *widget)
{
PikaCircle *circle = PIKA_CIRCLE (widget);
GTK_WIDGET_CLASS (parent_class)->map (widget);
if (circle->priv->event_window)
gdk_window_show (circle->priv->event_window);
}
static void
pika_circle_unmap (GtkWidget *widget)
{
PikaCircle *circle = PIKA_CIRCLE (widget);
if (circle->priv->has_grab)
{
gtk_grab_remove (widget);
circle->priv->has_grab = FALSE;
}
if (circle->priv->event_window)
gdk_window_hide (circle->priv->event_window);
GTK_WIDGET_CLASS (parent_class)->unmap (widget);
}
static void
pika_circle_get_preferred_width (GtkWidget *widget,
gint *minimum_width,
gint *natural_width)
{
PikaCircle *circle = PIKA_CIRCLE (widget);
*minimum_width = *natural_width = 2 * circle->priv->border_width + circle->priv->size;
}
static void
pika_circle_get_preferred_height (GtkWidget *widget,
gint *minimum_height,
gint *natural_height)
{
PikaCircle *circle = PIKA_CIRCLE (widget);
*minimum_height = *natural_height = 2 * circle->priv->border_width + circle->priv->size;
}
static void
pika_circle_size_allocate (GtkWidget *widget,
GtkAllocation *allocation)
{
PikaCircle *circle = PIKA_CIRCLE (widget);
GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, allocation);
if (gtk_widget_get_realized (widget))
gdk_window_move_resize (circle->priv->event_window,
allocation->x,
allocation->y,
allocation->width,
allocation->height);
g_clear_pointer (&circle->priv->surface, cairo_surface_destroy);
}
static gboolean
pika_circle_draw (GtkWidget *widget,
cairo_t *cr)
{
PikaCircle *circle = PIKA_CIRCLE (widget);
GtkAllocation allocation;
gint size = circle->priv->size;
gtk_widget_get_allocation (widget, &allocation);
cairo_save (cr);
cairo_translate (cr,
(allocation.width - size) / 2,
(allocation.height - size) / 2);
pika_circle_draw_background (circle, cr, size, circle->priv->background);
cairo_restore (cr);
return FALSE;
}
static gboolean
pika_circle_button_press_event (GtkWidget *widget,
GdkEventButton *bevent)
{
PikaCircle *circle = PIKA_CIRCLE (widget);
if (bevent->type == GDK_BUTTON_PRESS &&
bevent->button == 1)
{
gtk_grab_add (widget);
circle->priv->has_grab = TRUE;
}
return FALSE;
}
static gboolean
pika_circle_button_release_event (GtkWidget *widget,
GdkEventButton *bevent)
{
PikaCircle *circle = PIKA_CIRCLE (widget);
if (bevent->button == 1)
{
gtk_grab_remove (widget);
circle->priv->has_grab = FALSE;
if (! circle->priv->in_widget)
PIKA_CIRCLE_GET_CLASS (circle)->reset_target (circle);
}
return FALSE;
}
static gboolean
pika_circle_enter_notify_event (GtkWidget *widget,
GdkEventCrossing *event)
{
PikaCircle *circle = PIKA_CIRCLE (widget);
circle->priv->in_widget = TRUE;
return FALSE;
}
static gboolean
pika_circle_leave_notify_event (GtkWidget *widget,
GdkEventCrossing *event)
{
PikaCircle *circle = PIKA_CIRCLE (widget);
circle->priv->in_widget = FALSE;
if (! circle->priv->has_grab)
PIKA_CIRCLE_GET_CLASS (circle)->reset_target (circle);
return FALSE;
}
static void
pika_circle_real_reset_target (PikaCircle *circle)
{
}
/* public functions */
GtkWidget *
pika_circle_new (void)
{
return g_object_new (PIKA_TYPE_CIRCLE, NULL);
}
/* protected functions */
static gdouble
get_angle_and_distance (gdouble center_x,
gdouble center_y,
gdouble radius,
gdouble x,
gdouble y,
gdouble *distance)
{
gdouble angle = atan2 (center_y - y,
x - center_x);
if (angle < 0)
angle += 2 * G_PI;
if (distance)
*distance = sqrt ((SQR (x - center_x) +
SQR (y - center_y)) / SQR (radius));
return angle;
}
gboolean
_pika_circle_has_grab (PikaCircle *circle)
{
g_return_val_if_fail (PIKA_IS_CIRCLE (circle), FALSE);
return circle->priv->has_grab;
}
gdouble
_pika_circle_get_angle_and_distance (PikaCircle *circle,
gdouble event_x,
gdouble event_y,
gdouble *distance)
{
GtkAllocation allocation;
gdouble center_x;
gdouble center_y;
g_return_val_if_fail (PIKA_IS_CIRCLE (circle), 0.0);
gtk_widget_get_allocation (GTK_WIDGET (circle), &allocation);
center_x = allocation.width / 2.0;
center_y = allocation.height / 2.0;
return get_angle_and_distance (center_x, center_y, circle->priv->size / 2.0,
event_x, event_y,
distance);
}
/* private functions */
static void
pika_circle_background_hsv (gdouble angle,
gdouble distance,
guchar *rgb)
{
PikaHSV hsv;
PikaRGB color;
pika_hsv_set (&hsv,
angle / (2.0 * G_PI),
distance,
1 - sqrt (distance) / 4 /* it just looks nicer this way */);
pika_hsv_to_rgb (&hsv, &color);
pika_rgb_get_uchar (&color, rgb, rgb + 1, rgb + 2);
}
static void
pika_circle_draw_background (PikaCircle *circle,
cairo_t *cr,
gint size,
PikaCircleBackground background)
{
cairo_save (cr);
if (background == PIKA_CIRCLE_BACKGROUND_PLAIN)
{
cairo_arc (cr, size / 2.0, size / 2.0, size / 2.0 - 1.5, 0.0, 2 * G_PI);
cairo_set_line_width (cr, 3.0);
cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.6);
cairo_stroke_preserve (cr);
cairo_set_line_width (cr, 1.0);
cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.8);
cairo_stroke (cr);
}
else
{
if (! circle->priv->surface)
{
guchar *data;
gint stride;
gint x, y;
circle->priv->surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
size, size);
data = cairo_image_surface_get_data (circle->priv->surface);
stride = cairo_image_surface_get_stride (circle->priv->surface);
for (y = 0; y < size; y++)
{
for (x = 0; x < size; x++)
{
gdouble angle;
gdouble distance;
guchar rgb[3] = { 0, };
angle = get_angle_and_distance (size / 2.0, size / 2.0,
size / 2.0,
x, y,
&distance);
switch (background)
{
case PIKA_CIRCLE_BACKGROUND_HSV:
pika_circle_background_hsv (angle, distance, rgb);
break;
default:
break;
}
PIKA_CAIRO_ARGB32_SET_PIXEL (data + y * stride + x * 4,
rgb[0], rgb[1], rgb[2], 255);
}
}
cairo_surface_mark_dirty (circle->priv->surface);
}
cairo_set_source_surface (cr, circle->priv->surface, 0.0, 0.0);
cairo_arc (cr, size / 2.0, size / 2.0, size / 2.0, 0.0, 2 * G_PI);
cairo_clip (cr);
cairo_paint (cr);
}
cairo_restore (cr);
}

70
app/widgets/pikacircle.h Normal file
View File

@ -0,0 +1,70 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikacircle.h
* Copyright (C) 2014 Michael Natterer <mitch@gimp.org>
*
* Based on code from the color-rotate plug-in
* Copyright (C) 1997-1999 Sven Anders (anderss@fmi.uni-passau.de)
* Based on code from Pavel Grinfeld (pavel@ml.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __PIKA_CIRCLE_H__
#define __PIKA_CIRCLE_H__
#define PIKA_TYPE_CIRCLE (pika_circle_get_type ())
#define PIKA_CIRCLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIKA_TYPE_CIRCLE, PikaCircle))
#define PIKA_CIRCLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIKA_TYPE_CIRCLE, PikaCircleClass))
#define PIKA_IS_CIRCLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, PIKA_TYPE_CIRCLE))
#define PIKA_IS_CIRCLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIKA_TYPE_CIRCLE))
#define PIKA_CIRCLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIKA_TYPE_CIRCLE, PikaCircleClass))
typedef struct _PikaCirclePrivate PikaCirclePrivate;
typedef struct _PikaCircleClass PikaCircleClass;
struct _PikaCircle
{
GtkWidget parent_instance;
PikaCirclePrivate *priv;
};
struct _PikaCircleClass
{
GtkWidgetClass parent_class;
void (* reset_target) (PikaCircle *circle);
};
GType pika_circle_get_type (void) G_GNUC_CONST;
GtkWidget * pika_circle_new (void);
gboolean _pika_circle_has_grab (PikaCircle *circle);
gdouble _pika_circle_get_angle_and_distance (PikaCircle *circle,
gdouble event_x,
gdouble event_y,
gdouble *distance);
#endif /* __PIKA_CIRCLE_H__ */

1297
app/widgets/pikaclipboard.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,54 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __PIKA_CLIPBOARD_H__
#define __PIKA_CLIPBOARD_H__
void pika_clipboard_init (Pika *pika);
void pika_clipboard_exit (Pika *pika);
gboolean pika_clipboard_has_image (Pika *pika);
gboolean pika_clipboard_has_buffer (Pika *pika);
gboolean pika_clipboard_has_svg (Pika *pika);
gboolean pika_clipboard_has_curve (Pika *pika);
PikaObject * pika_clipboard_get_object (Pika *pika);
PikaImage * pika_clipboard_get_image (Pika *pika);
PikaBuffer * pika_clipboard_get_buffer (Pika *pika);
gchar * pika_clipboard_get_svg (Pika *pika,
gsize *svg_length);
PikaCurve * pika_clipboard_get_curve (Pika *pika);
void pika_clipboard_set_image (Pika *pika,
PikaImage *image);
void pika_clipboard_set_buffer (Pika *pika,
PikaBuffer *buffer);
void pika_clipboard_set_svg (Pika *pika,
const gchar *svg);
void pika_clipboard_set_text (Pika *pika,
const gchar *text);
void pika_clipboard_set_curve (Pika *pika,
PikaCurve *curve);
#endif /* __PIKA_CLIPBOARD_H__ */

341
app/widgets/pikacolorbar.c Normal file
View File

@ -0,0 +1,341 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <string.h>
#include <gegl.h>
#include <gtk/gtk.h>
#include "libpikabase/pikabase.h"
#include "libpikawidgets/pikawidgets.h"
#include "libpikamath/pikamath.h"
#include "libpikacolor/pikacolor.h"
#include "libpikaconfig/pikaconfig.h"
#include "widgets-types.h"
#include "pikacolorbar.h"
enum
{
PROP_0,
PROP_ORIENTATION,
PROP_COLOR,
PROP_CHANNEL
};
/* local function prototypes */
static void pika_color_bar_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
static void pika_color_bar_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
static gboolean pika_color_bar_draw (GtkWidget *widget,
cairo_t *cr);
G_DEFINE_TYPE (PikaColorBar, pika_color_bar, GTK_TYPE_EVENT_BOX)
#define parent_class pika_color_bar_parent_class
static void
pika_color_bar_class_init (PikaColorBarClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
PikaRGB white = { 1.0, 1.0, 1.0, 1.0 };
object_class->set_property = pika_color_bar_set_property;
object_class->get_property = pika_color_bar_get_property;
widget_class->draw = pika_color_bar_draw;
g_object_class_install_property (object_class, PROP_ORIENTATION,
g_param_spec_enum ("orientation",
NULL, NULL,
GTK_TYPE_ORIENTATION,
GTK_ORIENTATION_HORIZONTAL,
PIKA_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property (object_class, PROP_COLOR,
pika_param_spec_rgb ("color",
NULL, NULL,
FALSE, &white,
PIKA_PARAM_WRITABLE |
G_PARAM_CONSTRUCT));
g_object_class_install_property (object_class, PROP_CHANNEL,
g_param_spec_enum ("histogram-channel",
NULL, NULL,
PIKA_TYPE_HISTOGRAM_CHANNEL,
PIKA_HISTOGRAM_VALUE,
PIKA_PARAM_WRITABLE));
}
static void
pika_color_bar_init (PikaColorBar *bar)
{
gtk_event_box_set_visible_window (GTK_EVENT_BOX (bar), FALSE);
bar->orientation = GTK_ORIENTATION_HORIZONTAL;
}
static void
pika_color_bar_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
PikaColorBar *bar = PIKA_COLOR_BAR (object);
switch (property_id)
{
case PROP_ORIENTATION:
bar->orientation = g_value_get_enum (value);
break;
case PROP_COLOR:
pika_color_bar_set_color (bar, g_value_get_boxed (value));
break;
case PROP_CHANNEL:
pika_color_bar_set_channel (bar, g_value_get_enum (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
pika_color_bar_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
PikaColorBar *bar = PIKA_COLOR_BAR (object);
switch (property_id)
{
case PROP_ORIENTATION:
g_value_set_enum (value, bar->orientation);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static gboolean
pika_color_bar_draw (GtkWidget *widget,
cairo_t *cr)
{
PikaColorBar *bar = PIKA_COLOR_BAR (widget);
GtkAllocation allocation;
cairo_surface_t *surface;
cairo_pattern_t *pattern;
guchar *src;
guchar *dest;
gint x, y;
gint width, height;
gint i;
gtk_widget_get_allocation (widget, &allocation);
x = y = gtk_container_get_border_width (GTK_CONTAINER (bar));
width = allocation.width - 2 * x;
height = allocation.height - 2 * y;
if (width < 1 || height < 1)
return TRUE;
cairo_translate (cr, x, y);
cairo_rectangle (cr, 0, 0, width, height);
cairo_clip (cr);
surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, 256, 1);
for (i = 0, src = bar->buf, dest = cairo_image_surface_get_data (surface);
i < 256;
i++, src += 3, dest += 4)
{
PIKA_CAIRO_RGB24_SET_PIXEL(dest, src[0], src[1], src[2]);
}
cairo_surface_mark_dirty (surface);
pattern = cairo_pattern_create_for_surface (surface);
cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REFLECT);
cairo_surface_destroy (surface);
if (bar->orientation == GTK_ORIENTATION_HORIZONTAL)
{
cairo_scale (cr, (gdouble) width / 256.0, 1.0);
}
else
{
cairo_translate (cr, 0, height);
cairo_scale (cr, 1.0, (gdouble) height / 256.0);
cairo_rotate (cr, - G_PI / 2);
}
cairo_set_source (cr, pattern);
cairo_pattern_destroy (pattern);
cairo_paint (cr);
return TRUE;
}
/* public functions */
/**
* pika_color_bar_new:
* @orientation: whether the bar should be oriented horizontally or
* vertically
*
* Creates a new #PikaColorBar widget.
*
* Returns: The new #PikaColorBar widget.
**/
GtkWidget *
pika_color_bar_new (GtkOrientation orientation)
{
return g_object_new (PIKA_TYPE_COLOR_BAR,
"orientation", orientation,
NULL);
}
/**
* pika_color_bar_set_color:
* @bar: a #PikaColorBar widget
* @color: a #PikaRGB color
*
* Makes the @bar display a gradient from black (on the left or the
* bottom), to the given @color (on the right or at the top).
**/
void
pika_color_bar_set_color (PikaColorBar *bar,
const PikaRGB *color)
{
guchar *buf;
gint i;
g_return_if_fail (PIKA_IS_COLOR_BAR (bar));
g_return_if_fail (color != NULL);
for (i = 0, buf = bar->buf; i < 256; i++, buf += 3)
{
buf[0] = ROUND (color->r * (gdouble) i);
buf[1] = ROUND (color->g * (gdouble) i);
buf[2] = ROUND (color->b * (gdouble) i);
}
gtk_widget_queue_draw (GTK_WIDGET (bar));
}
/**
* pika_color_bar_set_channel:
* @bar: a #PikaColorBar widget
* @channel: a #PikaHistogramChannel
*
* Convenience function that calls pika_color_bar_set_color() with the
* color that matches the @channel.
**/
void
pika_color_bar_set_channel (PikaColorBar *bar,
PikaHistogramChannel channel)
{
PikaRGB color = { 1.0, 1.0, 1.0, 1.0 };
g_return_if_fail (PIKA_IS_COLOR_BAR (bar));
switch (channel)
{
case PIKA_HISTOGRAM_VALUE:
case PIKA_HISTOGRAM_LUMINANCE:
case PIKA_HISTOGRAM_ALPHA:
case PIKA_HISTOGRAM_RGB:
pika_rgb_set (&color, 1.0, 1.0, 1.0);
break;
case PIKA_HISTOGRAM_RED:
pika_rgb_set (&color, 1.0, 0.0, 0.0);
break;
case PIKA_HISTOGRAM_GREEN:
pika_rgb_set (&color, 0.0, 1.0, 0.0);
break;
case PIKA_HISTOGRAM_BLUE:
pika_rgb_set (&color, 0.0, 0.0, 1.0);
break;
}
pika_color_bar_set_color (bar, &color);
}
/**
* pika_color_bar_set_buffers:
* @bar: a #PikaColorBar widget
* @red: an array of 256 values
* @green: an array of 256 values
* @blue: an array of 256 values
*
* This function gives full control over the colors displayed by the
* @bar widget. The 3 arrays can for example be taken from a #Levels
* or a #Curves struct.
**/
void
pika_color_bar_set_buffers (PikaColorBar *bar,
const guchar *red,
const guchar *green,
const guchar *blue)
{
guchar *buf;
gint i;
g_return_if_fail (PIKA_IS_COLOR_BAR (bar));
g_return_if_fail (red != NULL);
g_return_if_fail (green != NULL);
g_return_if_fail (blue != NULL);
for (i = 0, buf = bar->buf; i < 256; i++, buf += 3)
{
buf[0] = red[i];
buf[1] = green[i];
buf[2] = blue[i];
}
gtk_widget_queue_draw (GTK_WIDGET (bar));
}

View File

@ -0,0 +1,64 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __PIKA_COLOR_BAR_H__
#define __PIKA_COLOR_BAR_H__
#define PIKA_TYPE_COLOR_BAR (pika_color_bar_get_type ())
#define PIKA_COLOR_BAR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIKA_TYPE_COLOR_BAR, PikaColorBar))
#define PIKA_COLOR_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIKA_TYPE_COLOR_BAR, PikaColorBarClass))
#define PIKA_IS_COLOR_BAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIKA_TYPE_COLOR_BAR))
#define PIKA_IS_COLOR_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIKA_TYPE_COLOR_BAR))
#define PIKA_COLOR_BAR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIKA_TYPE_COLOR_BAR, PikaColorBarClass))
typedef struct _PikaColorBarClass PikaColorBarClass;
struct _PikaColorBar
{
GtkEventBox parent_class;
GtkOrientation orientation;
guchar buf[3 * 256];
};
struct _PikaColorBarClass
{
GtkEventBoxClass parent_class;
};
GType pika_color_bar_get_type (void) G_GNUC_CONST;
GtkWidget * pika_color_bar_new (GtkOrientation orientation);
void pika_color_bar_set_color (PikaColorBar *bar,
const PikaRGB *color);
void pika_color_bar_set_channel (PikaColorBar *bar,
PikaHistogramChannel channel);
void pika_color_bar_set_buffers (PikaColorBar *bar,
const guchar *red,
const guchar *green,
const guchar *blue);
#endif /* __PIKA_COLOR_BAR_H__ */

View File

@ -0,0 +1,813 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* color_dialog module (C) 1998 Austin Donnelly <austin@greenend.org.uk>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gegl.h>
#include <gtk/gtk.h>
#include "libpikabase/pikabase.h"
#include "libpikacolor/pikacolor.h"
#include "libpikawidgets/pikawidgets.h"
#include "widgets-types.h"
#include "config/pikacoreconfig.h"
#include "core/pika.h"
#include "core/pika-palettes.h"
#include "core/pikacontext.h"
#include "core/pikaimage.h"
#include "core/pikaimage-color-profile.h"
#include "core/pikaimage-colormap.h"
#include "core/pikamarshal.h"
#include "core/pikapalettemru.h"
#include "core/pikaprojection.h"
#include "pikaactiongroup.h"
#include "pikacolordialog.h"
#include "pikacolorhistory.h"
#include "pikacolormapselection.h"
#include "pikadialogfactory.h"
#include "pikahelp-ids.h"
#include "pikawidgets-constructors.h"
#include "pikawidgets-utils.h"
#include "pika-intl.h"
#define RESPONSE_RESET 1
#define COLOR_AREA_SIZE 20
enum
{
UPDATE,
LAST_SIGNAL
};
enum
{
PROP_0,
PROP_USER_CONTEXT_AWARE
};
static void pika_color_dialog_constructed (GObject *object);
static void pika_color_dialog_finalize (GObject *object);
static void pika_color_dialog_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
static void pika_color_dialog_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
static void pika_color_dialog_response (GtkDialog *dialog,
gint response_id);
static void pika_color_dialog_help_func (const gchar *help_id,
gpointer help_data);
static void pika_color_dialog_colormap_clicked (PikaColorDialog *dialog,
PikaPaletteEntry *entry,
GdkModifierType state);
static void
pika_color_dialog_colormap_edit_activate (PikaColorDialog *dialog);
static void
pika_color_dialog_colormap_add_activate (PikaColorDialog *dialog);
static void pika_color_dialog_color_changed (PikaColorSelection *selection,
PikaColorDialog *dialog);
static void pika_color_history_add_clicked (GtkWidget *widget,
PikaColorDialog *dialog);
static void pika_color_dialog_history_selected (PikaColorHistory *history,
const PikaRGB *rgb,
PikaColorDialog *dialog);
static void pika_color_dialog_image_changed (PikaContext *context,
PikaImage *image,
PikaColorDialog *dialog);
static void pika_color_dialog_update_simulation(PikaImage *image,
PikaColorDialog *dialog);
static void pika_color_dialog_update (PikaColorDialog *dialog);
static void pika_color_dialog_show (PikaColorDialog *dialog);
static void pika_color_dialog_hide (PikaColorDialog *dialog);
G_DEFINE_TYPE (PikaColorDialog, pika_color_dialog, PIKA_TYPE_VIEWABLE_DIALOG)
#define parent_class pika_color_dialog_parent_class
static guint color_dialog_signals[LAST_SIGNAL] = { 0, };
static void
pika_color_dialog_class_init (PikaColorDialogClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkDialogClass *dialog_class = GTK_DIALOG_CLASS (klass);
object_class->constructed = pika_color_dialog_constructed;
object_class->finalize = pika_color_dialog_finalize;
object_class->set_property = pika_color_dialog_set_property;
object_class->get_property = pika_color_dialog_get_property;
dialog_class->response = pika_color_dialog_response;
color_dialog_signals[UPDATE] =
g_signal_new ("update",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (PikaColorDialogClass, update),
NULL, NULL,
pika_marshal_VOID__BOXED_ENUM,
G_TYPE_NONE, 2,
PIKA_TYPE_RGB,
PIKA_TYPE_COLOR_DIALOG_STATE);
g_object_class_install_property (object_class, PROP_USER_CONTEXT_AWARE,
g_param_spec_boolean ("user-context-aware",
NULL, NULL, FALSE,
PIKA_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY));
}
static void
pika_color_dialog_init (PikaColorDialog *dialog)
{
dialog->stack = gtk_stack_new ();
gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
dialog->stack, TRUE, TRUE, 0);
gtk_widget_show (dialog->stack);
}
static void
pika_color_dialog_constructed (GObject *object)
{
PikaColorDialog *dialog = PIKA_COLOR_DIALOG (object);
PikaViewableDialog *viewable_dialog = PIKA_VIEWABLE_DIALOG (object);
GtkWidget *hbox;
GtkWidget *history;
GtkWidget *button;
GtkWidget *image;
G_OBJECT_CLASS (parent_class)->constructed (object);
/** Tab: colormap selection. **/
dialog->colormap_selection = pika_colormap_selection_new (viewable_dialog->context->pika->user_context);
gtk_stack_add_named (GTK_STACK (dialog->stack),
dialog->colormap_selection, "colormap");
g_signal_connect_swapped (dialog->colormap_selection, "color-clicked",
G_CALLBACK (pika_color_dialog_colormap_clicked),
dialog);
g_signal_connect_swapped (dialog->colormap_selection, "color-activated",
G_CALLBACK (pika_color_dialog_colormap_edit_activate),
dialog);
gtk_widget_show (dialog->colormap_selection);
/* Edit color button */
button = pika_button_new ();
gtk_button_set_relief (GTK_BUTTON (button), FALSE);
image = gtk_image_new_from_icon_name (PIKA_ICON_EDIT, GTK_ICON_SIZE_LARGE_TOOLBAR);
gtk_container_add (GTK_CONTAINER (button), image);
gtk_widget_show (image);
/* XXX: I use the same icon, tooltip and help id as in
* colormap-actions.c. I wanted to just load these strings from
* the action, but the group is only registered when the Colormap
* dockable is opened.
*/
pika_help_set_help_data_with_markup (button,
NC_("colormap-action", "Edit this color"),
PIKA_HELP_INDEXED_PALETTE_EDIT);
g_signal_connect_swapped (button, "clicked",
G_CALLBACK (pika_color_dialog_colormap_edit_activate),
dialog);
gtk_widget_show (button);
gtk_box_pack_start (GTK_BOX (PIKA_COLORMAP_SELECTION (dialog->colormap_selection)->right_vbox), button,
FALSE, FALSE, 0);
/* Add Color button */
button = pika_button_new ();
gtk_button_set_relief (GTK_BUTTON (button), FALSE);
image = gtk_image_new_from_icon_name (PIKA_ICON_LIST_ADD, GTK_ICON_SIZE_LARGE_TOOLBAR);
gtk_container_add (GTK_CONTAINER (button), image);
gtk_widget_show (image);
pika_help_set_help_data_with_markup (button,
NC_("colormap-action", "Add current foreground color"),
PIKA_HELP_INDEXED_PALETTE_ADD);
g_signal_connect_swapped (button, "clicked",
G_CALLBACK (pika_color_dialog_colormap_add_activate),
dialog);
gtk_widget_show (button);
gtk_box_pack_start (GTK_BOX (PIKA_COLORMAP_SELECTION (dialog->colormap_selection)->right_vbox), button,
FALSE, FALSE, 0);
/** Tab: color selection. **/
dialog->selection = pika_color_selection_new ();
gtk_container_set_border_width (GTK_CONTAINER (dialog->selection), 12);
gtk_stack_add_named (GTK_STACK (dialog->stack), dialog->selection, "color");
gtk_widget_show (dialog->selection);
g_signal_connect (dialog->selection, "color-changed",
G_CALLBACK (pika_color_dialog_color_changed),
dialog);
/* Color history box. */
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
gtk_box_pack_end (GTK_BOX (pika_color_selection_get_right_vbox (PIKA_COLOR_SELECTION (dialog->selection))),
hbox, FALSE, FALSE, 0);
gtk_widget_show (hbox);
/* Button for adding to color history. */
button = pika_icon_button_new (PIKA_ICON_LIST_ADD, NULL);
gtk_box_pack_start (GTK_BOX (hbox), GTK_WIDGET (button), FALSE, FALSE, 0);
pika_help_set_help_data (button,
_("Add the current color to the color history"),
NULL);
gtk_widget_show (button);
g_signal_connect (button, "clicked",
G_CALLBACK (pika_color_history_add_clicked),
dialog);
/* Color history table. */
history = pika_color_history_new (viewable_dialog->context, 12);
gtk_box_pack_start (GTK_BOX (hbox), GTK_WIDGET (history), TRUE, TRUE, 0);
gtk_widget_show (GTK_WIDGET (history));
g_signal_connect (history, "color-selected",
G_CALLBACK (pika_color_dialog_history_selected),
dialog);
g_signal_connect (dialog, "show",
G_CALLBACK (pika_color_dialog_show),
NULL);
g_signal_connect (dialog, "hide",
G_CALLBACK (pika_color_dialog_hide),
NULL);
pika_color_dialog_show (dialog);
}
static void
pika_color_dialog_finalize (GObject *object)
{
PikaColorDialog *dialog = PIKA_COLOR_DIALOG (object);
PikaViewableDialog *viewable_dialog = PIKA_VIEWABLE_DIALOG (dialog);
if (dialog->user_context_aware && viewable_dialog->context)
{
PikaContext *user_context = viewable_dialog->context->pika->user_context;
g_signal_handlers_disconnect_by_func (user_context,
G_CALLBACK (pika_color_dialog_image_changed),
dialog);
}
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
pika_color_dialog_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
PikaColorDialog *dialog = PIKA_COLOR_DIALOG (object);
switch (property_id)
{
case PROP_USER_CONTEXT_AWARE:
dialog->user_context_aware = g_value_get_boolean (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
pika_color_dialog_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
PikaColorDialog *dialog = PIKA_COLOR_DIALOG (object);
switch (property_id)
{
case PROP_USER_CONTEXT_AWARE:
g_value_set_boolean (value, dialog->user_context_aware);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
pika_color_dialog_response (GtkDialog *gtk_dialog,
gint response_id)
{
PikaColorDialog *dialog = PIKA_COLOR_DIALOG (gtk_dialog);
PikaViewableDialog *viewable_dialog = PIKA_VIEWABLE_DIALOG (dialog);
PikaImage *image = NULL;
PikaColormapSelection *colormap_selection;
gint col_index;
PikaRGB color;
colormap_selection = PIKA_COLORMAP_SELECTION (dialog->colormap_selection);
col_index = pika_colormap_selection_get_index (colormap_selection, NULL);
if (dialog->colormap_editing && viewable_dialog->context)
{
PikaContext *user_context = viewable_dialog->context->pika->user_context;
image = pika_context_get_image (user_context);
}
switch (response_id)
{
case RESPONSE_RESET:
pika_color_selection_reset (PIKA_COLOR_SELECTION (dialog->selection));
break;
case GTK_RESPONSE_OK:
pika_color_selection_get_color (PIKA_COLOR_SELECTION (dialog->selection),
&color);
if (dialog->colormap_editing && image)
{
PikaRGB old_color;
dialog->colormap_editing = FALSE;
/* Restore old color for undo */
pika_color_selection_get_old_color (PIKA_COLOR_SELECTION (dialog->selection), &old_color);
pika_image_set_colormap_entry (image, col_index, &old_color, FALSE);
pika_image_set_colormap_entry (image, col_index, &color, TRUE);
pika_image_flush (image);
gtk_stack_set_visible_child_name (GTK_STACK (dialog->stack), "colormap");
g_signal_emit (dialog, color_dialog_signals[UPDATE], 0,
&color, PIKA_COLOR_DIALOG_UPDATE);
}
else
{
g_signal_emit (dialog, color_dialog_signals[UPDATE], 0,
&color, PIKA_COLOR_DIALOG_OK);
}
break;
default:
pika_color_selection_get_old_color (PIKA_COLOR_SELECTION (dialog->selection),
&color);
if (dialog->colormap_editing && image)
{
dialog->colormap_editing = FALSE;
pika_image_set_colormap_entry (image, col_index, &color, FALSE);
pika_projection_flush (pika_image_get_projection (image));
gtk_stack_set_visible_child_name (GTK_STACK (dialog->stack), "colormap");
g_signal_emit (dialog, color_dialog_signals[UPDATE], 0,
&color, PIKA_COLOR_DIALOG_UPDATE);
}
else
{
g_signal_emit (dialog, color_dialog_signals[UPDATE], 0,
&color, PIKA_COLOR_DIALOG_CANCEL);
}
break;
}
}
/* public functions */
GtkWidget *
pika_color_dialog_new (PikaViewable *viewable,
PikaContext *context,
gboolean user_context_aware,
const gchar *title,
const gchar *icon_name,
const gchar *desc,
GtkWidget *parent,
PikaDialogFactory *dialog_factory,
const gchar *dialog_identifier,
const PikaRGB *color,
gboolean wants_updates,
gboolean show_alpha)
{
PikaColorDialog *dialog;
const gchar *role;
gboolean use_header_bar;
g_return_val_if_fail (viewable == NULL || PIKA_IS_VIEWABLE (viewable), NULL);
g_return_val_if_fail (PIKA_IS_CONTEXT (context), NULL);
g_return_val_if_fail (GTK_IS_WIDGET (parent), NULL);
g_return_val_if_fail (dialog_factory == NULL ||
PIKA_IS_DIALOG_FACTORY (dialog_factory), NULL);
g_return_val_if_fail (dialog_factory == NULL || dialog_identifier != NULL,
NULL);
g_return_val_if_fail (color != NULL, NULL);
role = dialog_identifier ? dialog_identifier : "pika-color-selector";
g_object_get (gtk_settings_get_default (),
"gtk-dialogs-use-header", &use_header_bar,
NULL);
dialog = g_object_new (PIKA_TYPE_COLOR_DIALOG,
"title", title,
"role", role,
"help-func", pika_color_dialog_help_func,
"help-id", PIKA_HELP_COLOR_DIALOG,
"icon-name", icon_name,
"description", desc,
"context", context,
"user-context-aware", user_context_aware,
"parent", gtk_widget_get_toplevel (parent),
"use-header-bar", use_header_bar,
NULL);
pika_dialog_add_buttons (PIKA_DIALOG (dialog),
_("_Reset"), RESPONSE_RESET,
_("_Cancel"), GTK_RESPONSE_CANCEL,
_("_OK"), GTK_RESPONSE_OK,
NULL);
pika_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
RESPONSE_RESET,
GTK_RESPONSE_OK,
GTK_RESPONSE_CANCEL,
-1);
if (viewable)
{
pika_viewable_dialog_set_viewables (PIKA_VIEWABLE_DIALOG (dialog),
g_list_prepend (NULL, viewable), context);
}
else
{
GtkWidget *parent;
parent = gtk_widget_get_parent (PIKA_VIEWABLE_DIALOG (dialog)->icon);
parent = gtk_widget_get_parent (parent);
gtk_widget_hide (parent);
}
dialog->wants_updates = wants_updates;
if (dialog_factory)
{
pika_dialog_factory_add_foreign (dialog_factory, dialog_identifier,
GTK_WIDGET (dialog),
pika_widget_get_monitor (parent));
}
pika_color_selection_set_show_alpha (PIKA_COLOR_SELECTION (dialog->selection),
show_alpha);
g_object_set_data (G_OBJECT (context->pika->config->color_management),
"pika-context", context);
pika_color_selection_set_config (PIKA_COLOR_SELECTION (dialog->selection),
context->pika->config->color_management);
g_object_set_data (G_OBJECT (context->pika->config->color_management),
"pika-context", NULL);
pika_color_selection_set_color (PIKA_COLOR_SELECTION (dialog->selection),
color);
pika_color_selection_set_old_color (PIKA_COLOR_SELECTION (dialog->selection),
color);
return GTK_WIDGET (dialog);
}
void
pika_color_dialog_set_color (PikaColorDialog *dialog,
const PikaRGB *color)
{
g_return_if_fail (PIKA_IS_COLOR_DIALOG (dialog));
g_return_if_fail (color != NULL);
g_signal_handlers_block_by_func (dialog->selection,
pika_color_dialog_color_changed,
dialog);
pika_color_selection_set_color (PIKA_COLOR_SELECTION (dialog->selection),
color);
pika_color_selection_set_old_color (PIKA_COLOR_SELECTION (dialog->selection),
color);
g_signal_handlers_unblock_by_func (dialog->selection,
pika_color_dialog_color_changed,
dialog);
}
void
pika_color_dialog_get_color (PikaColorDialog *dialog,
PikaRGB *color)
{
g_return_if_fail (PIKA_IS_COLOR_DIALOG (dialog));
g_return_if_fail (color != NULL);
pika_color_selection_get_color (PIKA_COLOR_SELECTION (dialog->selection),
color);
}
/* private functions */
static void
pika_color_dialog_help_func (const gchar *help_id,
gpointer help_data)
{
PikaColorDialog *dialog = PIKA_COLOR_DIALOG (help_data);
PikaColorNotebook *notebook;
PikaColorSelector *current;
notebook =
PIKA_COLOR_NOTEBOOK (pika_color_selection_get_notebook (PIKA_COLOR_SELECTION (dialog->selection)));
current = pika_color_notebook_get_current_selector (notebook);
help_id = PIKA_COLOR_SELECTOR_GET_CLASS (current)->help_id;
pika_standard_help_func (help_id, NULL);
}
static void
pika_color_dialog_colormap_clicked (PikaColorDialog *dialog,
PikaPaletteEntry *entry,
GdkModifierType state)
{
pika_color_dialog_set_color (dialog, &entry->color);
if (dialog->wants_updates)
{
g_signal_emit (dialog, color_dialog_signals[UPDATE], 0,
&entry->color, PIKA_COLOR_DIALOG_UPDATE);
}
}
static void
pika_color_dialog_colormap_edit_activate (PikaColorDialog *dialog)
{
PikaColormapSelection *colormap_selection;
PikaRGB color;
gint col_index;
dialog->colormap_editing = TRUE;
colormap_selection = PIKA_COLORMAP_SELECTION (dialog->colormap_selection);
col_index = pika_colormap_selection_get_index (colormap_selection, NULL);
pika_image_get_colormap_entry (dialog->active_image, col_index, &color);
pika_color_dialog_set_color (dialog, &color);
gtk_stack_set_visible_child_name (GTK_STACK (dialog->stack), "color");
}
static void
pika_color_dialog_colormap_add_activate (PikaColorDialog *dialog)
{
PikaViewableDialog *viewable_dialog = PIKA_VIEWABLE_DIALOG (dialog);
if (dialog->active_image &&
pika_image_get_colormap_size (dialog->active_image) < 256 &&
viewable_dialog->context)
{
PikaContext *user_context = viewable_dialog->context->pika->user_context;
PikaRGB color;
pika_context_get_foreground (user_context, &color);
pika_image_add_colormap_entry (dialog->active_image, &color);
pika_image_flush (dialog->active_image);
}
}
static void
pika_color_dialog_color_changed (PikaColorSelection *selection,
PikaColorDialog *dialog)
{
PikaViewableDialog *viewable_dialog = PIKA_VIEWABLE_DIALOG (dialog);
PikaRGB color;
pika_color_selection_get_color (selection, &color);
if (dialog->colormap_editing && viewable_dialog->context)
{
PikaContext *user_context = viewable_dialog->context->pika->user_context;
PikaImage *image;
image = pika_context_get_image (user_context);
if (image)
{
PikaColormapSelection *colormap_selection;
gboolean push_undo = FALSE;
gint col_index;
colormap_selection = PIKA_COLORMAP_SELECTION (dialog->colormap_selection);
col_index = pika_colormap_selection_get_index (colormap_selection, NULL);
if (push_undo)
{
PikaRGB old_color;
pika_color_selection_get_old_color (PIKA_COLOR_SELECTION (dialog->selection), &old_color);
/* Restore old color for undo */
pika_image_set_colormap_entry (image, col_index, &old_color,
FALSE);
}
pika_image_set_colormap_entry (image, col_index, &color,
push_undo);
if (push_undo)
pika_image_flush (image);
else
pika_projection_flush (pika_image_get_projection (image));
}
}
if (dialog->wants_updates)
{
g_signal_emit (dialog, color_dialog_signals[UPDATE], 0,
&color, PIKA_COLOR_DIALOG_UPDATE);
}
}
/* History-adding button callback */
static void
pika_color_history_add_clicked (GtkWidget *widget,
PikaColorDialog *dialog)
{
PikaViewableDialog *viewable_dialog = PIKA_VIEWABLE_DIALOG (dialog);
PikaPalette *history;
PikaRGB color;
history = pika_palettes_get_color_history (viewable_dialog->context->pika);
pika_color_selection_get_color (PIKA_COLOR_SELECTION (dialog->selection),
&color);
pika_palette_mru_add (PIKA_PALETTE_MRU (history), &color);
}
/* Color history callback */
static void
pika_color_dialog_history_selected (PikaColorHistory *history,
const PikaRGB *rgb,
PikaColorDialog *dialog)
{
pika_color_selection_set_color (PIKA_COLOR_SELECTION (dialog->selection),
rgb);
}
/* Context-related callbacks */
static void
pika_color_dialog_image_changed (PikaContext *context,
PikaImage *image,
PikaColorDialog *dialog)
{
if (dialog->active_image != image)
{
if (dialog->active_image)
{
g_signal_handlers_disconnect_by_func (dialog->active_image,
G_CALLBACK (pika_color_dialog_update),
dialog);
g_signal_handlers_disconnect_by_func (dialog->active_image,
pika_color_dialog_update_simulation,
dialog);
}
g_set_weak_pointer (&dialog->active_image, image);
if (image)
{
g_signal_connect_swapped (image, "notify::base-type",
G_CALLBACK (pika_color_dialog_update),
dialog);
g_signal_connect (image, "simulation-profile-changed",
G_CALLBACK (pika_color_dialog_update_simulation),
dialog);
g_signal_connect (image, "simulation-intent-changed",
G_CALLBACK (pika_color_dialog_update_simulation),
dialog);
g_signal_connect (image, "simulation-bpc-changed",
G_CALLBACK (pika_color_dialog_update_simulation),
dialog);
pika_color_dialog_update_simulation (image, dialog);
}
pika_color_dialog_update (dialog);
}
}
static void
pika_color_dialog_update_simulation (PikaImage *image,
PikaColorDialog *dialog)
{
g_return_if_fail (PIKA_IS_COLOR_DIALOG (dialog));
if (image && PIKA_IS_COLOR_DIALOG (dialog))
{
pika_color_selection_set_simulation (PIKA_COLOR_SELECTION (dialog->selection),
pika_image_get_simulation_profile (image),
pika_image_get_simulation_intent (image),
pika_image_get_simulation_bpc (image));
}
}
static void
pika_color_dialog_update (PikaColorDialog *dialog)
{
if (dialog->active_image &&
pika_image_get_base_type (dialog->active_image) == PIKA_INDEXED)
gtk_stack_set_visible_child_name (GTK_STACK (dialog->stack), "colormap");
else
gtk_stack_set_visible_child_name (GTK_STACK (dialog->stack), "color");
}
static void
pika_color_dialog_show (PikaColorDialog *dialog)
{
PikaViewableDialog *viewable_dialog = PIKA_VIEWABLE_DIALOG (dialog);
dialog->colormap_editing = FALSE;
if (dialog->user_context_aware && viewable_dialog->context)
{
PikaContext *user_context = viewable_dialog->context->pika->user_context;
PikaImage *image = pika_context_get_image (user_context);
g_signal_connect (user_context, "image-changed",
G_CALLBACK (pika_color_dialog_image_changed),
dialog);
pika_color_dialog_image_changed (viewable_dialog->context,
image, dialog);
pika_color_dialog_update (dialog);
}
else
{
gtk_stack_set_visible_child_name (GTK_STACK (dialog->stack), "color");
}
}
static void
pika_color_dialog_hide (PikaColorDialog *dialog)
{
PikaViewableDialog *viewable_dialog = PIKA_VIEWABLE_DIALOG (dialog);
if (dialog->user_context_aware && viewable_dialog->context)
{
PikaContext *user_context = viewable_dialog->context->pika->user_context;
g_signal_handlers_disconnect_by_func (user_context,
G_CALLBACK (pika_color_dialog_image_changed),
dialog);
}
}

View File

@ -0,0 +1,90 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikacolordialog.h
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __PIKA_COLOR_DIALOG_H__
#define __PIKA_COLOR_DIALOG_H__
#include "pikaviewabledialog.h"
#define PIKA_COLOR_DIALOG_HISTORY_SIZE 12
#define PIKA_TYPE_COLOR_DIALOG (pika_color_dialog_get_type ())
#define PIKA_COLOR_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIKA_TYPE_COLOR_DIALOG, PikaColorDialog))
#define PIKA_COLOR_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIKA_TYPE_COLOR_DIALOG, PikaColorDialogClass))
#define PIKA_IS_COLOR_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIKA_TYPE_COLOR_DIALOG))
#define PIKA_IS_COLOR_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIKA_TYPE_COLOR_DIALOG))
#define PIKA_COLOR_DIALOG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIKA_TYPE_COLOR_DIALOG, PikaColorDialogClass))
typedef struct _PikaColorDialogClass PikaColorDialogClass;
struct _PikaColorDialog
{
PikaViewableDialog parent_instance;
gboolean wants_updates;
gboolean user_context_aware;
GtkWidget *stack;
GtkWidget *selection;
GtkWidget *colormap_selection;
PikaImage *active_image;
gboolean colormap_editing;
};
struct _PikaColorDialogClass
{
PikaViewableDialogClass parent_class;
void (* update) (PikaColorDialog *dialog,
const PikaRGB *color,
PikaColorDialogState state);
};
GType pika_color_dialog_get_type (void) G_GNUC_CONST;
GtkWidget * pika_color_dialog_new (PikaViewable *viewable,
PikaContext *context,
gboolean context_aware,
const gchar *title,
const gchar *icon_name,
const gchar *desc,
GtkWidget *parent,
PikaDialogFactory *dialog_factory,
const gchar *dialog_identifier,
const PikaRGB *color,
gboolean wants_update,
gboolean show_alpha);
void pika_color_dialog_set_color (PikaColorDialog *dialog,
const PikaRGB *color);
void pika_color_dialog_get_color (PikaColorDialog *dialog,
PikaRGB *color);
#endif /* __PIKA_COLOR_DIALOG_H__ */

View File

@ -0,0 +1,832 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikacolordisplayeditor.c
* Copyright (C) 2003 Michael Natterer <mitch@gimp.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gegl.h>
#include <gtk/gtk.h>
#include "libpikacolor/pikacolor.h"
#include "libpikaconfig/pikaconfig.h"
#include "libpikawidgets/pikawidgets.h"
#include "widgets-types.h"
#include "propgui/propgui-types.h"
#include "core/pika.h"
#include "propgui/pikapropgui.h"
#include "pikacolordisplayeditor.h"
#include "pikaeditor.h"
#include "pika-intl.h"
#define LIST_WIDTH 200
#define LIST_HEIGHT 100
enum
{
SRC_COLUMN_NAME,
SRC_COLUMN_ICON,
SRC_COLUMN_TYPE,
N_SRC_COLUMNS
};
enum
{
DEST_COLUMN_ENABLED,
DEST_COLUMN_NAME,
DEST_COLUMN_ICON,
DEST_COLUMN_FILTER,
N_DEST_COLUMNS
};
static void pika_color_display_editor_dispose (GObject *object);
static void pika_color_display_editor_add_clicked (GtkWidget *widget,
PikaColorDisplayEditor *editor);
static void pika_color_display_editor_remove_clicked (GtkWidget *widget,
PikaColorDisplayEditor *editor);
static void pika_color_display_editor_up_clicked (GtkWidget *widget,
PikaColorDisplayEditor *editor);
static void pika_color_display_editor_down_clicked (GtkWidget *widget,
PikaColorDisplayEditor *editor);
static void pika_color_display_editor_reset_clicked (GtkWidget *widget,
PikaColorDisplayEditor *editor);
static void pika_color_display_editor_src_changed (GtkTreeSelection *sel,
PikaColorDisplayEditor *editor);
static void pika_color_display_editor_dest_changed (GtkTreeSelection *sel,
PikaColorDisplayEditor *editor);
static void pika_color_display_editor_added (PikaColorDisplayStack *stack,
PikaColorDisplay *display,
gint position,
PikaColorDisplayEditor *editor);
static void pika_color_display_editor_removed (PikaColorDisplayStack *stack,
PikaColorDisplay *display,
PikaColorDisplayEditor *editor);
static void pika_color_display_editor_reordered (PikaColorDisplayStack *stack,
PikaColorDisplay *display,
gint position,
PikaColorDisplayEditor *editor);
static void pika_color_display_editor_enabled (PikaColorDisplay *display,
GParamSpec *pspec,
PikaColorDisplayEditor *editor);
static void pika_color_display_editor_enable_toggled (GtkCellRendererToggle *toggle,
const gchar *path,
PikaColorDisplayEditor *editor);
static void pika_color_display_editor_update_buttons (PikaColorDisplayEditor *editor);
G_DEFINE_TYPE (PikaColorDisplayEditor, pika_color_display_editor, GTK_TYPE_BOX)
#define parent_class pika_color_display_editor_parent_class
static void
pika_color_display_editor_class_init (PikaColorDisplayEditorClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->dispose = pika_color_display_editor_dispose;
}
static void
pika_color_display_editor_init (PikaColorDisplayEditor *editor)
{
GtkWidget *paned;
GtkWidget *hbox;
GtkWidget *ed;
GtkWidget *scrolled_win;
GtkWidget *tv;
GtkWidget *vbox;
GtkWidget *image;
GtkTreeViewColumn *column;
GtkCellRenderer *rend;
gtk_orientable_set_orientation (GTK_ORIENTABLE (editor),
GTK_ORIENTATION_VERTICAL);
paned = gtk_paned_new (GTK_ORIENTATION_VERTICAL);
gtk_paned_set_wide_handle (GTK_PANED (paned), TRUE);
gtk_box_pack_start (GTK_BOX (editor), paned, TRUE, TRUE, 0);
gtk_widget_show (paned);
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
gtk_paned_pack1 (GTK_PANED (paned), hbox, FALSE, FALSE);
gtk_widget_show (hbox);
scrolled_win = gtk_scrolled_window_new (NULL, NULL);
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_win),
GTK_SHADOW_IN);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
GTK_POLICY_AUTOMATIC,
GTK_POLICY_AUTOMATIC);
gtk_box_pack_start (GTK_BOX (hbox), scrolled_win, TRUE, TRUE, 0);
gtk_widget_show (scrolled_win);
editor->src = gtk_list_store_new (N_SRC_COLUMNS,
G_TYPE_STRING,
G_TYPE_STRING,
G_TYPE_GTYPE);
gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (editor->src),
SRC_COLUMN_NAME, GTK_SORT_ASCENDING);
tv = gtk_tree_view_new_with_model (GTK_TREE_MODEL (editor->src));
g_object_unref (editor->src);
gtk_widget_set_size_request (tv, LIST_WIDTH, LIST_HEIGHT);
gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW (tv), FALSE);
column = gtk_tree_view_column_new ();
gtk_tree_view_column_set_title (column, _("Available Filters"));
gtk_tree_view_append_column (GTK_TREE_VIEW (tv), column);
rend = gtk_cell_renderer_pixbuf_new ();
gtk_tree_view_column_pack_start (column, rend, FALSE);
gtk_tree_view_column_set_attributes (column, rend,
"icon-name", SRC_COLUMN_ICON,
NULL);
rend = gtk_cell_renderer_text_new ();
gtk_tree_view_column_pack_start (column, rend, TRUE);
gtk_tree_view_column_set_attributes (column, rend,
"text", SRC_COLUMN_NAME,
NULL);
gtk_container_add (GTK_CONTAINER (scrolled_win), tv);
gtk_widget_show (tv);
editor->src_sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (tv));
g_signal_connect (editor->src_sel, "changed",
G_CALLBACK (pika_color_display_editor_src_changed),
editor);
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
gtk_box_set_homogeneous (GTK_BOX (vbox), TRUE);
gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
gtk_widget_show (vbox);
editor->add_button = gtk_button_new ();
gtk_box_pack_start (GTK_BOX (vbox), editor->add_button, TRUE, FALSE, 0);
gtk_widget_set_sensitive (editor->add_button, FALSE);
gtk_widget_show (editor->add_button);
image = gtk_image_new_from_icon_name (PIKA_ICON_GO_NEXT,
GTK_ICON_SIZE_BUTTON);
gtk_container_add (GTK_CONTAINER (editor->add_button), image);
gtk_widget_show (image);
g_signal_connect (editor->add_button, "clicked",
G_CALLBACK (pika_color_display_editor_add_clicked),
editor);
editor->remove_button = gtk_button_new ();
gtk_box_pack_start (GTK_BOX (vbox), editor->remove_button, TRUE, FALSE, 0);
gtk_widget_set_sensitive (editor->remove_button, FALSE);
gtk_widget_show (editor->remove_button);
image = gtk_image_new_from_icon_name (PIKA_ICON_GO_PREVIOUS,
GTK_ICON_SIZE_BUTTON);
gtk_container_add (GTK_CONTAINER (editor->remove_button), image);
gtk_widget_show (image);
g_signal_connect (editor->remove_button, "clicked",
G_CALLBACK (pika_color_display_editor_remove_clicked),
editor);
ed = pika_editor_new ();
gtk_box_pack_start (GTK_BOX (hbox), ed, TRUE, TRUE, 0);
gtk_widget_show (ed);
editor->up_button =
pika_editor_add_button (PIKA_EDITOR (ed),
PIKA_ICON_GO_UP,
_("Move the selected filter up"),
NULL,
G_CALLBACK (pika_color_display_editor_up_clicked),
NULL,
G_OBJECT (editor));
editor->down_button =
pika_editor_add_button (PIKA_EDITOR (ed),
PIKA_ICON_GO_DOWN,
_("Move the selected filter down"),
NULL,
G_CALLBACK (pika_color_display_editor_down_clicked),
NULL,
G_OBJECT (editor));
gtk_widget_set_sensitive (editor->up_button, FALSE);
gtk_widget_set_sensitive (editor->down_button, FALSE);
scrolled_win = gtk_scrolled_window_new (NULL, NULL);
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_win),
GTK_SHADOW_IN);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
GTK_POLICY_AUTOMATIC,
GTK_POLICY_AUTOMATIC);
gtk_box_pack_start (GTK_BOX (ed), scrolled_win, TRUE, TRUE, 0);
gtk_widget_show (scrolled_win);
editor->dest = gtk_list_store_new (N_DEST_COLUMNS,
G_TYPE_BOOLEAN,
G_TYPE_STRING,
G_TYPE_STRING,
PIKA_TYPE_COLOR_DISPLAY);
tv = gtk_tree_view_new_with_model (GTK_TREE_MODEL (editor->dest));
g_object_unref (editor->dest);
gtk_widget_set_size_request (tv, LIST_WIDTH, LIST_HEIGHT);
gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW (tv), FALSE);
rend = gtk_cell_renderer_toggle_new ();
g_signal_connect (rend, "toggled",
G_CALLBACK (pika_color_display_editor_enable_toggled),
editor);
column = gtk_tree_view_column_new_with_attributes (NULL, rend,
"active",
DEST_COLUMN_ENABLED,
NULL);
gtk_tree_view_insert_column (GTK_TREE_VIEW (tv), column, 0);
image = gtk_image_new_from_icon_name (PIKA_ICON_VISIBLE,
GTK_ICON_SIZE_MENU);
gtk_tree_view_column_set_widget (column, image);
gtk_widget_show (image);
column = gtk_tree_view_column_new ();
gtk_tree_view_column_set_title (column, _("Active Filters"));
gtk_tree_view_append_column (GTK_TREE_VIEW (tv), column);
rend = gtk_cell_renderer_pixbuf_new ();
gtk_tree_view_column_pack_start (column, rend, FALSE);
gtk_tree_view_column_set_attributes (column, rend,
"icon-name", DEST_COLUMN_ICON,
NULL);
rend = gtk_cell_renderer_text_new ();
gtk_tree_view_column_pack_start (column, rend, TRUE);
gtk_tree_view_column_set_attributes (column, rend,
"text", DEST_COLUMN_NAME,
NULL);
gtk_container_add (GTK_CONTAINER (scrolled_win), tv);
gtk_widget_show (tv);
editor->dest_sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (tv));
g_signal_connect (editor->dest_sel, "changed",
G_CALLBACK (pika_color_display_editor_dest_changed),
editor);
/* the config frame */
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
gtk_paned_pack2 (GTK_PANED (paned), vbox, TRUE, FALSE);
gtk_widget_show (vbox);
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
gtk_widget_show (hbox);
editor->config_frame = pika_frame_new (NULL);
gtk_box_pack_start (GTK_BOX (vbox), editor->config_frame, TRUE, TRUE, 0);
gtk_widget_show (editor->config_frame);
editor->config_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
gtk_container_add (GTK_CONTAINER (editor->config_frame), editor->config_box);
gtk_widget_show (editor->config_box);
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_box_pack_end (GTK_BOX (editor->config_box), hbox, FALSE, FALSE, 0);
gtk_widget_show (hbox);
editor->reset_button = gtk_button_new_with_mnemonic (_("_Reset"));
gtk_box_pack_end (GTK_BOX (hbox), editor->reset_button, FALSE, FALSE, 0);
gtk_widget_show (editor->reset_button);
pika_help_set_help_data (editor->reset_button,
_("Reset the selected filter to default values"),
NULL);
g_signal_connect (editor->reset_button, "clicked",
G_CALLBACK (pika_color_display_editor_reset_clicked),
editor);
pika_color_display_editor_dest_changed (editor->dest_sel, editor);
}
static void
pika_color_display_editor_dispose (GObject *object)
{
PikaColorDisplayEditor *editor = PIKA_COLOR_DISPLAY_EDITOR (object);
g_clear_weak_pointer (&editor->selected);
g_clear_object (&editor->stack);
g_clear_object (&editor->config);
g_clear_object (&editor->managed);
G_OBJECT_CLASS (parent_class)->dispose (object);
}
GtkWidget *
pika_color_display_editor_new (Pika *pika,
PikaColorDisplayStack *stack,
PikaColorConfig *config,
PikaColorManaged *managed)
{
PikaColorDisplayEditor *editor;
GType *display_types;
guint n_display_types;
gint i;
GList *list;
g_return_val_if_fail (PIKA_IS_PIKA (pika), NULL);
g_return_val_if_fail (PIKA_IS_COLOR_DISPLAY_STACK (stack), NULL);
g_return_val_if_fail (PIKA_IS_COLOR_CONFIG (config), NULL);
g_return_val_if_fail (PIKA_IS_COLOR_MANAGED (managed), NULL);
editor = g_object_new (PIKA_TYPE_COLOR_DISPLAY_EDITOR, NULL);
editor->pika = pika;
editor->stack = g_object_ref (stack);
editor->config = g_object_ref (config);
editor->managed = g_object_ref (managed);
display_types = g_type_children (PIKA_TYPE_COLOR_DISPLAY, &n_display_types);
for (i = 0; i < n_display_types; i++)
{
PikaColorDisplayClass *display_class;
GtkTreeIter iter;
display_class = g_type_class_ref (display_types[i]);
gtk_list_store_append (editor->src, &iter);
gtk_list_store_set (editor->src, &iter,
SRC_COLUMN_ICON, display_class->icon_name,
SRC_COLUMN_NAME, display_class->name,
SRC_COLUMN_TYPE, display_types[i],
-1);
g_type_class_unref (display_class);
}
g_free (display_types);
for (list = pika_color_display_stack_get_filters (stack);
list;
list = g_list_next (list))
{
PikaColorDisplay *display = list->data;
GtkTreeIter iter;
gboolean enabled;
const gchar *name;
const gchar *icon_name;
enabled = pika_color_display_get_enabled (display);
name = PIKA_COLOR_DISPLAY_GET_CLASS (display)->name;
icon_name = PIKA_COLOR_DISPLAY_GET_CLASS (display)->icon_name;
gtk_list_store_append (editor->dest, &iter);
gtk_list_store_set (editor->dest, &iter,
DEST_COLUMN_ENABLED, enabled,
DEST_COLUMN_ICON, icon_name,
DEST_COLUMN_NAME, name,
DEST_COLUMN_FILTER, display,
-1);
g_signal_connect_object (display, "notify::enabled",
G_CALLBACK (pika_color_display_editor_enabled),
G_OBJECT (editor), 0);
}
g_signal_connect_object (stack, "added",
G_CALLBACK (pika_color_display_editor_added),
G_OBJECT (editor), 0);
g_signal_connect_object (stack, "removed",
G_CALLBACK (pika_color_display_editor_removed),
G_OBJECT (editor), 0);
g_signal_connect_object (stack, "reordered",
G_CALLBACK (pika_color_display_editor_reordered),
G_OBJECT (editor), 0);
return GTK_WIDGET (editor);
}
static void
pika_color_display_editor_add_clicked (GtkWidget *widget,
PikaColorDisplayEditor *editor)
{
GtkTreeModel *model;
GtkTreeIter iter;
if (gtk_tree_selection_get_selected (editor->src_sel, &model, &iter))
{
PikaColorDisplay *display;
GType type;
gtk_tree_model_get (model, &iter, SRC_COLUMN_TYPE, &type, -1);
display = g_object_new (type,
"color-config", editor->config,
"color-managed", editor->managed,
NULL);
if (display)
{
pika_color_display_stack_add (editor->stack, display);
g_object_unref (display);
}
}
}
static void
pika_color_display_editor_remove_clicked (GtkWidget *widget,
PikaColorDisplayEditor *editor)
{
if (editor->selected)
pika_color_display_stack_remove (editor->stack, editor->selected);
}
static void
pika_color_display_editor_up_clicked (GtkWidget *widget,
PikaColorDisplayEditor *editor)
{
if (editor->selected)
pika_color_display_stack_reorder_up (editor->stack, editor->selected);
}
static void
pika_color_display_editor_down_clicked (GtkWidget *widget,
PikaColorDisplayEditor *editor)
{
if (editor->selected)
pika_color_display_stack_reorder_down (editor->stack, editor->selected);
}
static void
pika_color_display_editor_reset_clicked (GtkWidget *widget,
PikaColorDisplayEditor *editor)
{
if (editor->selected)
pika_color_display_configure_reset (editor->selected);
}
static void
pika_color_display_editor_src_changed (GtkTreeSelection *sel,
PikaColorDisplayEditor *editor)
{
GtkTreeModel *model;
GtkTreeIter iter;
gchar *tip = NULL;
const gchar *name = NULL;
if (gtk_tree_selection_get_selected (sel, &model, &iter))
{
GValue val = G_VALUE_INIT;
gtk_tree_model_get_value (model, &iter, SRC_COLUMN_NAME, &val);
name = g_value_get_string (&val);
tip = g_strdup_printf (_("Add '%s' to the list of active filters"), name);
g_value_unset (&val);
}
gtk_widget_set_sensitive (editor->add_button, name != NULL);
pika_help_set_help_data (editor->add_button, tip, NULL);
g_free (tip);
}
static void
pika_color_display_editor_dest_changed (GtkTreeSelection *sel,
PikaColorDisplayEditor *editor)
{
GtkTreeModel *model;
GtkTreeIter iter;
PikaColorDisplay *display = NULL;
gchar *tip = NULL;
g_clear_weak_pointer (&editor->selected);
if (gtk_tree_selection_get_selected (sel, &model, &iter))
{
GValue val = G_VALUE_INIT;
gtk_tree_model_get_value (model, &iter, DEST_COLUMN_FILTER, &val);
display = g_value_get_object (&val);
g_value_unset (&val);
tip = g_strdup_printf (_("Remove '%s' from the list of active filters"),
PIKA_COLOR_DISPLAY_GET_CLASS (display)->name);
}
pika_help_set_help_data (editor->remove_button, tip, NULL);
g_free (tip);
gtk_widget_set_sensitive (editor->remove_button, display != NULL);
gtk_widget_set_sensitive (editor->reset_button, display != NULL);
if (editor->config_widget)
gtk_container_remove (GTK_CONTAINER (editor->config_box),
editor->config_widget);
if (display)
{
g_set_weak_pointer (&editor->selected, display);
editor->config_widget = pika_color_display_configure (display);
if (! editor->config_widget)
{
editor->config_widget =
pika_prop_gui_new (G_OBJECT (display),
G_TYPE_FROM_INSTANCE (display), 0,
NULL,
pika_get_user_context (editor->pika),
NULL, NULL, NULL);
}
gtk_frame_set_label (GTK_FRAME (editor->config_frame),
PIKA_COLOR_DISPLAY_GET_CLASS (display)->name);
}
else
{
editor->config_widget = NULL;
gtk_frame_set_label (GTK_FRAME (editor->config_frame),
_("No filter selected"));
}
if (editor->config_widget)
{
gtk_box_pack_start (GTK_BOX (editor->config_box), editor->config_widget,
FALSE, FALSE, 0);
gtk_widget_show (editor->config_widget);
g_object_add_weak_pointer (G_OBJECT (editor->config_widget),
(gpointer) &editor->config_widget);
}
pika_color_display_editor_update_buttons (editor);
}
static void
pika_color_display_editor_added (PikaColorDisplayStack *stack,
PikaColorDisplay *display,
gint position,
PikaColorDisplayEditor *editor)
{
GtkTreeIter iter;
gboolean enabled;
const gchar *name;
const gchar *icon_name;
enabled = pika_color_display_get_enabled (display);
name = PIKA_COLOR_DISPLAY_GET_CLASS (display)->name;
icon_name = PIKA_COLOR_DISPLAY_GET_CLASS (display)->icon_name;
gtk_list_store_insert (editor->dest, &iter, position);
gtk_list_store_set (editor->dest, &iter,
DEST_COLUMN_ENABLED, enabled,
DEST_COLUMN_ICON, icon_name,
DEST_COLUMN_NAME, name,
DEST_COLUMN_FILTER, display,
-1);
g_signal_connect_object (display, "notify::enabled",
G_CALLBACK (pika_color_display_editor_enabled),
G_OBJECT (editor), 0);
pika_color_display_editor_update_buttons (editor);
}
static void
pika_color_display_editor_removed (PikaColorDisplayStack *stack,
PikaColorDisplay *display,
PikaColorDisplayEditor *editor)
{
GtkTreeIter iter;
gboolean iter_valid;
for (iter_valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (editor->dest),
&iter);
iter_valid;
iter_valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (editor->dest),
&iter))
{
PikaColorDisplay *display2;
gtk_tree_model_get (GTK_TREE_MODEL (editor->dest), &iter,
DEST_COLUMN_FILTER, &display2,
-1);
g_object_unref (display2);
if (display == display2)
{
g_signal_handlers_disconnect_by_func (display,
pika_color_display_editor_enabled,
editor);
gtk_list_store_remove (editor->dest, &iter);
pika_color_display_editor_update_buttons (editor);
break;
}
}
}
static void
pika_color_display_editor_reordered (PikaColorDisplayStack *stack,
PikaColorDisplay *display,
gint position,
PikaColorDisplayEditor *editor)
{
GtkTreeIter iter;
gboolean iter_valid;
for (iter_valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (editor->dest),
&iter);
iter_valid;
iter_valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (editor->dest),
&iter))
{
PikaColorDisplay *display2;
gtk_tree_model_get (GTK_TREE_MODEL (editor->dest), &iter,
DEST_COLUMN_FILTER, &display2,
-1);
g_object_unref (display2);
if (display == display2)
{
GList *filters = pika_color_display_stack_get_filters (stack);
GtkTreePath *path;
gint old_position;
path = gtk_tree_model_get_path (GTK_TREE_MODEL (editor->dest), &iter);
old_position = gtk_tree_path_get_indices (path)[0];
gtk_tree_path_free (path);
if (position == old_position)
return;
if (position == -1 || position == g_list_length (filters) - 1)
{
gtk_list_store_move_before (editor->dest, &iter, NULL);
}
else if (position == 0)
{
gtk_list_store_move_after (editor->dest, &iter, NULL);
}
else
{
GtkTreeIter place_iter;
path = gtk_tree_path_new_from_indices (position, -1);
gtk_tree_model_get_iter (GTK_TREE_MODEL (editor->dest),
&place_iter, path);
gtk_tree_path_free (path);
if (position > old_position)
gtk_list_store_move_after (editor->dest, &iter, &place_iter);
else
gtk_list_store_move_before (editor->dest, &iter, &place_iter);
}
pika_color_display_editor_update_buttons (editor);
return;
}
}
}
static void
pika_color_display_editor_enabled (PikaColorDisplay *display,
GParamSpec *pspec,
PikaColorDisplayEditor *editor)
{
GtkTreeIter iter;
gboolean iter_valid;
for (iter_valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (editor->dest),
&iter);
iter_valid;
iter_valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (editor->dest),
&iter))
{
PikaColorDisplay *display2;
gtk_tree_model_get (GTK_TREE_MODEL (editor->dest), &iter,
DEST_COLUMN_FILTER, &display2,
-1);
g_object_unref (display2);
if (display == display2)
{
gboolean enabled = pika_color_display_get_enabled (display);
gtk_list_store_set (editor->dest, &iter,
DEST_COLUMN_ENABLED, enabled,
-1);
break;
}
}
}
static void
pika_color_display_editor_enable_toggled (GtkCellRendererToggle *toggle,
const gchar *path_str,
PikaColorDisplayEditor *editor)
{
GtkTreePath *path = gtk_tree_path_new_from_string (path_str);
GtkTreeIter iter;
if (gtk_tree_model_get_iter (GTK_TREE_MODEL (editor->dest), &iter, path))
{
PikaColorDisplay *display;
gboolean enabled;
gtk_tree_model_get (GTK_TREE_MODEL (editor->dest), &iter,
DEST_COLUMN_FILTER, &display,
DEST_COLUMN_ENABLED, &enabled,
-1);
pika_color_display_set_enabled (display, ! enabled);
g_object_unref (display);
}
gtk_tree_path_free (path);
}
static void
pika_color_display_editor_update_buttons (PikaColorDisplayEditor *editor)
{
GtkTreeModel *model;
GtkTreeIter iter;
gboolean up_sensitive = FALSE;
gboolean down_sensitive = FALSE;
if (gtk_tree_selection_get_selected (editor->dest_sel, &model, &iter))
{
GList *filters = pika_color_display_stack_get_filters (editor->stack);
GtkTreePath *path = gtk_tree_model_get_path (model, &iter);
gint *indices = gtk_tree_path_get_indices (path);
up_sensitive = indices[0] > 0;
down_sensitive = indices[0] < (g_list_length (filters) - 1);
gtk_tree_path_free (path);
}
gtk_widget_set_sensitive (editor->up_button, up_sensitive);
gtk_widget_set_sensitive (editor->down_button, down_sensitive);
}

View File

@ -0,0 +1,83 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikacolordisplayeditor.h
* Copyright (C) 2003 Michael Natterer <mitch@gimp.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __PIKA_COLOR_DISPLAY_EDITOR_H__
#define __PIKA_COLOR_DISPLAY_EDITOR_H__
#define PIKA_TYPE_COLOR_DISPLAY_EDITOR (pika_color_display_editor_get_type ())
#define PIKA_COLOR_DISPLAY_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIKA_TYPE_COLOR_DISPLAY_EDITOR, PikaColorDisplayEditor))
#define PIKA_COLOR_DISPLAY_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIKA_TYPE_COLOR_DISPLAY_EDITOR, PikaColorDisplayEditorClass))
#define PIKA_IS_COLOR_DISPLAY_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIKA_TYPE_COLOR_DISPLAY_EDITOR))
#define PIKA_IS_COLOR_DISPLAY_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIKA_TYPE_COLOR_DISPLAY_EDITOR))
#define PIKA_COLOR_DISPLAY_EDITOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIKA_TYPE_COLOR_DISPLAY_EDITOR, PikaColorDisplayEditorClass))
typedef struct _PikaColorDisplayEditorClass PikaColorDisplayEditorClass;
struct _PikaColorDisplayEditor
{
GtkBox parent_instance;
Pika *pika;
PikaColorDisplayStack *stack;
PikaColorConfig *config;
PikaColorManaged *managed;
GtkListStore *src;
GtkListStore *dest;
GtkTreeSelection *src_sel;
GtkTreeSelection *dest_sel;
PikaColorDisplay *selected;
GtkWidget *add_button;
GtkWidget *remove_button;
GtkWidget *up_button;
GtkWidget *down_button;
GtkWidget *config_frame;
GtkWidget *config_box;
GtkWidget *config_widget;
GtkWidget *reset_button;
};
struct _PikaColorDisplayEditorClass
{
GtkBoxClass parent_class;
};
GType pika_color_display_editor_get_type (void) G_GNUC_CONST;
GtkWidget * pika_color_display_editor_new (Pika *pika,
PikaColorDisplayStack *stack,
PikaColorConfig *config,
PikaColorManaged *managed);
#endif /* __PIKA_COLOR_DISPLAY_EDITOR_H__ */

View File

@ -0,0 +1,736 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikacoloreditor.c
* Copyright (C) 2002 Michael Natterer <mitch@gimp.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <string.h>
#include <gegl.h>
#include <gtk/gtk.h>
#include "libpikabase/pikabase.h"
#include "libpikacolor/pikacolor.h"
#include "libpikawidgets/pikawidgets.h"
#include "widgets-types.h"
#include "config/pikacoreconfig.h"
#include "core/pika.h"
#include "core/pikacontext.h"
#include "pikacoloreditor.h"
#include "pikacolorhistory.h"
#include "pikadocked.h"
#include "pikafgbgeditor.h"
#include "pikafgbgview.h"
#include "pikasessioninfo-aux.h"
#include "pika-intl.h"
enum
{
PROP_0,
PROP_CONTEXT
};
static void pika_color_editor_docked_iface_init (PikaDockedInterface *iface);
static void pika_color_editor_constructed (GObject *object);
static void pika_color_editor_dispose (GObject *object);
static void pika_color_editor_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
static void pika_color_editor_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
static void pika_color_editor_style_updated (GtkWidget *widget);
static void pika_color_editor_set_aux_info (PikaDocked *docked,
GList *aux_info);
static GList *pika_color_editor_get_aux_info (PikaDocked *docked);
static GtkWidget *pika_color_editor_get_preview (PikaDocked *docked,
PikaContext *context,
GtkIconSize size);
static void pika_color_editor_set_context (PikaDocked *docked,
PikaContext *context);
static void pika_color_editor_fg_changed (PikaContext *context,
const PikaRGB *rgb,
PikaColorEditor *editor);
static void pika_color_editor_bg_changed (PikaContext *context,
const PikaRGB *rgb,
PikaColorEditor *editor);
static void pika_color_editor_color_changed (PikaColorSelector *selector,
const PikaRGB *rgb,
const PikaHSV *hsv,
PikaColorEditor *editor);
static void pika_color_editor_tab_toggled (GtkWidget *widget,
PikaColorEditor *editor);
static void pika_color_editor_fg_bg_notify (GtkWidget *widget,
GParamSpec *pspec,
PikaColorEditor *editor);
static void pika_color_editor_color_picked (GtkWidget *widget,
const PikaRGB *rgb,
PikaColorEditor *editor);
static void pika_color_editor_entry_changed (PikaColorHexEntry *entry,
PikaColorEditor *editor);
static void pika_color_editor_history_selected (PikaColorHistory *history,
const PikaRGB *rgb,
PikaColorEditor *editor);
G_DEFINE_TYPE_WITH_CODE (PikaColorEditor, pika_color_editor, PIKA_TYPE_EDITOR,
G_IMPLEMENT_INTERFACE (PIKA_TYPE_DOCKED,
pika_color_editor_docked_iface_init))
#define parent_class pika_color_editor_parent_class
static PikaDockedInterface *parent_docked_iface = NULL;
static void
pika_color_editor_class_init (PikaColorEditorClass* klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
object_class->constructed = pika_color_editor_constructed;
object_class->dispose = pika_color_editor_dispose;
object_class->set_property = pika_color_editor_set_property;
object_class->get_property = pika_color_editor_get_property;
widget_class->style_updated = pika_color_editor_style_updated;
g_object_class_install_property (object_class, PROP_CONTEXT,
g_param_spec_object ("context",
NULL, NULL,
PIKA_TYPE_CONTEXT,
G_PARAM_CONSTRUCT |
PIKA_PARAM_READWRITE));
}
static void
pika_color_editor_docked_iface_init (PikaDockedInterface *iface)
{
parent_docked_iface = g_type_interface_peek_parent (iface);
if (! parent_docked_iface)
parent_docked_iface = g_type_default_interface_peek (PIKA_TYPE_DOCKED);
iface->get_preview = pika_color_editor_get_preview;
iface->set_aux_info = pika_color_editor_set_aux_info;
iface->get_aux_info = pika_color_editor_get_aux_info;
iface->set_context = pika_color_editor_set_context;
}
static void
pika_color_editor_init (PikaColorEditor *editor)
{
GtkWidget *notebook;
GtkWidget *hbox;
GtkWidget *button;
gint content_spacing;
gint button_spacing;
GtkIconSize button_icon_size;
PikaRGB rgb;
PikaHSV hsv;
GList *list;
GSList *group;
gint icon_width = 40;
gint icon_height = 38;
editor->context = NULL;
editor->edit_bg = FALSE;
pika_rgba_set (&rgb, 0.0, 0.0, 0.0, 1.0);
pika_rgb_to_hsv (&rgb, &hsv);
gtk_widget_style_get (GTK_WIDGET (editor),
"content-spacing", &content_spacing,
"button-spacing", &button_spacing,
"button-icon-size", &button_icon_size,
NULL);
editor->hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, button_spacing);
gtk_box_set_homogeneous (GTK_BOX (editor->hbox), TRUE);
gtk_box_pack_start (GTK_BOX (editor), editor->hbox, FALSE, FALSE, 0);
gtk_widget_show (editor->hbox);
editor->notebook = pika_color_selector_new (PIKA_TYPE_COLOR_NOTEBOOK,
&rgb, &hsv,
PIKA_COLOR_SELECTOR_RED);
pika_color_selector_set_show_alpha (PIKA_COLOR_SELECTOR (editor->notebook),
FALSE);
gtk_box_pack_start (GTK_BOX (editor), editor->notebook,
TRUE, TRUE, content_spacing);
gtk_widget_show (editor->notebook);
g_signal_connect (editor->notebook, "color-changed",
G_CALLBACK (pika_color_editor_color_changed),
editor);
notebook = pika_color_notebook_get_notebook (PIKA_COLOR_NOTEBOOK (editor->notebook));
gtk_notebook_set_show_tabs (GTK_NOTEBOOK (notebook), FALSE);
gtk_notebook_set_show_border (GTK_NOTEBOOK (notebook), FALSE);
pika_color_notebook_set_has_page (PIKA_COLOR_NOTEBOOK (editor->notebook),
PIKA_TYPE_COLOR_SCALES, TRUE);
group = NULL;
for (list = pika_color_notebook_get_selectors (PIKA_COLOR_NOTEBOOK (editor->notebook));
list;
list = g_list_next (list))
{
PikaColorSelector *selector;
PikaColorSelectorClass *selector_class;
GtkWidget *button;
GtkWidget *image;
selector = PIKA_COLOR_SELECTOR (list->data);
selector_class = PIKA_COLOR_SELECTOR_GET_CLASS (selector);
button = gtk_radio_button_new (group);
group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (button));
gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (button), FALSE);
gtk_box_pack_start (GTK_BOX (editor->hbox), button, TRUE, TRUE, 0);
gtk_widget_show (button);
image = gtk_image_new_from_icon_name (selector_class->icon_name,
GTK_ICON_SIZE_MENU);
gtk_container_add (GTK_CONTAINER (button), image);
gtk_widget_show (image);
pika_help_set_help_data (button,
selector_class->name, selector_class->help_id);
g_object_set_data (G_OBJECT (button), "selector", selector);
g_object_set_data (G_OBJECT (selector), "button", button);
g_signal_connect (button, "toggled",
G_CALLBACK (pika_color_editor_tab_toggled),
editor);
}
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
gtk_box_set_homogeneous (GTK_BOX (hbox), FALSE);
gtk_box_pack_start (GTK_BOX (editor), hbox, FALSE, FALSE, 0);
gtk_widget_show (hbox);
/* FG/BG editor */
editor->fg_bg = pika_fg_bg_editor_new (NULL);
gtk_icon_size_lookup (button_icon_size, &icon_width, &icon_height);
gtk_widget_set_size_request (editor->fg_bg,
(gint) (icon_width * 1.75),
(gint) (icon_height * 1.75));
gtk_box_pack_start (GTK_BOX (hbox), editor->fg_bg, FALSE, FALSE, 0);
gtk_widget_show (editor->fg_bg);
g_signal_connect (editor->fg_bg, "notify::active-color",
G_CALLBACK (pika_color_editor_fg_bg_notify),
editor);
/* The color picker */
button = pika_pick_button_new ();
gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
gtk_widget_show (button);
g_signal_connect (button, "color-picked",
G_CALLBACK (pika_color_editor_color_picked),
editor);
/* The hex triplet entry */
editor->hex_entry = pika_color_hex_entry_new ();
gtk_box_pack_end (GTK_BOX (hbox), editor->hex_entry, TRUE, TRUE, 0);
gtk_widget_show (editor->hex_entry);
g_signal_connect (editor->hex_entry, "color-changed",
G_CALLBACK (pika_color_editor_entry_changed),
editor);
}
static void
pika_color_editor_constructed (GObject *object)
{
PikaColorEditor *editor = PIKA_COLOR_EDITOR (object);
GtkWidget *history;
G_OBJECT_CLASS (parent_class)->constructed (object);
/* The color history */
history = pika_color_history_new (editor->context, 12);
gtk_box_pack_end (GTK_BOX (editor), history, FALSE, FALSE, 0);
gtk_widget_show (history);
g_signal_connect (history, "color-selected",
G_CALLBACK (pika_color_editor_history_selected),
editor);
}
static void
pika_color_editor_dispose (GObject *object)
{
PikaColorEditor *editor = PIKA_COLOR_EDITOR (object);
if (editor->context)
pika_docked_set_context (PIKA_DOCKED (editor), NULL);
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
pika_color_editor_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
switch (property_id)
{
case PROP_CONTEXT:
pika_docked_set_context (PIKA_DOCKED (object),
g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
pika_color_editor_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
PikaColorEditor *editor = PIKA_COLOR_EDITOR (object);
switch (property_id)
{
case PROP_CONTEXT:
g_value_set_object (value, editor->context);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static GtkWidget *
pika_color_editor_get_preview (PikaDocked *docked,
PikaContext *context,
GtkIconSize size)
{
GtkWidget *preview;
gint width;
gint height;
preview = pika_fg_bg_view_new (context);
if (gtk_icon_size_lookup (size, &width, &height))
gtk_widget_set_size_request (preview, width, height);
return preview;
}
#define AUX_INFO_CURRENT_PAGE "current-page"
static void
pika_color_editor_set_aux_info (PikaDocked *docked,
GList *aux_info)
{
PikaColorEditor *editor = PIKA_COLOR_EDITOR (docked);
GtkWidget *notebook;
GList *list;
notebook = pika_color_notebook_get_notebook (PIKA_COLOR_NOTEBOOK (editor->notebook));
parent_docked_iface->set_aux_info (docked, aux_info);
for (list = aux_info; list; list = g_list_next (list))
{
PikaSessionInfoAux *aux = list->data;
if (! strcmp (aux->name, AUX_INFO_CURRENT_PAGE))
{
GList *children;
GList *child;
children = gtk_container_get_children (GTK_CONTAINER (notebook));
for (child = children; child; child = g_list_next (child))
{
if (! strcmp (G_OBJECT_TYPE_NAME (child->data), aux->value))
{
GtkWidget *button;
button = g_object_get_data (G_OBJECT (child->data), "button");
if (button)
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
TRUE);
break;
}
}
g_list_free (children);
}
}
}
static GList *
pika_color_editor_get_aux_info (PikaDocked *docked)
{
PikaColorEditor *editor = PIKA_COLOR_EDITOR (docked);
PikaColorNotebook *notebook = PIKA_COLOR_NOTEBOOK (editor->notebook);
PikaColorSelector *current;
GList *aux_info;
aux_info = parent_docked_iface->get_aux_info (docked);
current = pika_color_notebook_get_current_selector (notebook);
if (current)
{
PikaSessionInfoAux *aux;
aux = pika_session_info_aux_new (AUX_INFO_CURRENT_PAGE,
G_OBJECT_TYPE_NAME (current));
aux_info = g_list_append (aux_info, aux);
}
return aux_info;
}
static void
pika_color_editor_set_context (PikaDocked *docked,
PikaContext *context)
{
PikaColorEditor *editor = PIKA_COLOR_EDITOR (docked);
if (context == editor->context)
return;
if (editor->context)
{
g_signal_handlers_disconnect_by_func (editor->context,
pika_color_editor_fg_changed,
editor);
g_signal_handlers_disconnect_by_func (editor->context,
pika_color_editor_bg_changed,
editor);
g_signal_handlers_disconnect_by_func (editor->context->pika->config,
G_CALLBACK (pika_color_editor_style_updated),
editor);
g_object_unref (editor->context);
}
editor->context = context;
if (editor->context)
{
PikaRGB rgb;
g_object_ref (editor->context);
g_signal_connect (editor->context, "foreground-changed",
G_CALLBACK (pika_color_editor_fg_changed),
editor);
g_signal_connect (editor->context, "background-changed",
G_CALLBACK (pika_color_editor_bg_changed),
editor);
g_signal_connect_object (editor->context->pika->config,
"notify::theme",
G_CALLBACK (pika_color_editor_style_updated),
editor, G_CONNECT_AFTER | G_CONNECT_SWAPPED);
g_signal_connect_object (context->pika->config,
"notify::override-theme-icon-size",
G_CALLBACK (pika_color_editor_style_updated),
editor, G_CONNECT_AFTER | G_CONNECT_SWAPPED);
g_signal_connect_object (context->pika->config,
"notify::custom-icon-size",
G_CALLBACK (pika_color_editor_style_updated),
editor, G_CONNECT_AFTER | G_CONNECT_SWAPPED);
if (editor->edit_bg)
{
pika_context_get_background (editor->context, &rgb);
pika_color_editor_bg_changed (editor->context, &rgb, editor);
}
else
{
pika_context_get_foreground (editor->context, &rgb);
pika_color_editor_fg_changed (editor->context, &rgb, editor);
}
g_object_set_data (G_OBJECT (context->pika->config->color_management),
"pika-context", editor->context);
pika_color_selector_set_config (PIKA_COLOR_SELECTOR (editor->notebook),
context->pika->config->color_management);
g_object_set_data (G_OBJECT (context->pika->config->color_management),
"pika-context", NULL);
}
pika_fg_bg_editor_set_context (PIKA_FG_BG_EDITOR (editor->fg_bg), context);
}
GtkWidget *
pika_color_editor_new (PikaContext *context)
{
return g_object_new (PIKA_TYPE_COLOR_EDITOR,
"context", context,
NULL);
}
static void
pika_color_editor_style_updated (GtkWidget *widget)
{
PikaColorEditor *editor = PIKA_COLOR_EDITOR (widget);
GtkIconSize button_icon_size;
gint icon_width = 40;
gint icon_height = 38;
GTK_WIDGET_CLASS (parent_class)->style_updated (widget);
if (editor->hbox)
pika_editor_set_box_style (PIKA_EDITOR (editor), GTK_BOX (editor->hbox));
gtk_widget_style_get (GTK_WIDGET (editor),
"button-icon-size", &button_icon_size,
NULL);
gtk_icon_size_lookup (button_icon_size, &icon_width, &icon_height);
gtk_widget_set_size_request (editor->fg_bg,
(gint) (icon_width * 1.75),
(gint) (icon_height * 1.75));
}
static void
pika_color_editor_set_color (PikaColorEditor *editor,
const PikaRGB *rgb)
{
PikaHSV hsv;
pika_rgb_to_hsv (rgb, &hsv);
g_signal_handlers_block_by_func (editor->notebook,
pika_color_editor_color_changed,
editor);
pika_color_selector_set_color (PIKA_COLOR_SELECTOR (editor->notebook),
rgb, &hsv);
g_signal_handlers_unblock_by_func (editor->notebook,
pika_color_editor_color_changed,
editor);
g_signal_handlers_block_by_func (editor->hex_entry,
pika_color_editor_entry_changed,
editor);
pika_color_hex_entry_set_color (PIKA_COLOR_HEX_ENTRY (editor->hex_entry),
rgb);
g_signal_handlers_unblock_by_func (editor->hex_entry,
pika_color_editor_entry_changed,
editor);
}
static void
pika_color_editor_fg_changed (PikaContext *context,
const PikaRGB *rgb,
PikaColorEditor *editor)
{
if (! editor->edit_bg)
pika_color_editor_set_color (editor, rgb);
}
static void
pika_color_editor_bg_changed (PikaContext *context,
const PikaRGB *rgb,
PikaColorEditor *editor)
{
if (editor->edit_bg)
pika_color_editor_set_color (editor, rgb);
}
static void
pika_color_editor_color_changed (PikaColorSelector *selector,
const PikaRGB *rgb,
const PikaHSV *hsv,
PikaColorEditor *editor)
{
if (editor->context)
{
if (editor->edit_bg)
{
g_signal_handlers_block_by_func (editor->context,
pika_color_editor_bg_changed,
editor);
pika_context_set_background (editor->context, rgb);
g_signal_handlers_unblock_by_func (editor->context,
pika_color_editor_bg_changed,
editor);
}
else
{
g_signal_handlers_block_by_func (editor->context,
pika_color_editor_fg_changed,
editor);
pika_context_set_foreground (editor->context, rgb);
g_signal_handlers_unblock_by_func (editor->context,
pika_color_editor_fg_changed,
editor);
}
}
g_signal_handlers_block_by_func (editor->hex_entry,
pika_color_editor_entry_changed,
editor);
pika_color_hex_entry_set_color (PIKA_COLOR_HEX_ENTRY (editor->hex_entry),
rgb);
g_signal_handlers_unblock_by_func (editor->hex_entry,
pika_color_editor_entry_changed,
editor);
}
static void
pika_color_editor_tab_toggled (GtkWidget *widget,
PikaColorEditor *editor)
{
if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
{
GtkWidget *selector;
selector = g_object_get_data (G_OBJECT (widget), "selector");
if (selector)
{
GtkWidget *notebook;
gint page_num;
notebook = pika_color_notebook_get_notebook (PIKA_COLOR_NOTEBOOK (editor->notebook));
page_num = gtk_notebook_page_num (GTK_NOTEBOOK (notebook), selector);
if (page_num >= 0)
gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), page_num);
}
}
}
static void
pika_color_editor_fg_bg_notify (GtkWidget *widget,
GParamSpec *pspec,
PikaColorEditor *editor)
{
gboolean edit_bg;
edit_bg = (PIKA_FG_BG_EDITOR (widget)->active_color ==
PIKA_ACTIVE_COLOR_BACKGROUND);
if (edit_bg != editor->edit_bg)
{
editor->edit_bg = edit_bg;
if (editor->context)
{
PikaRGB rgb;
if (edit_bg)
{
pika_context_get_background (editor->context, &rgb);
pika_color_editor_bg_changed (editor->context, &rgb, editor);
}
else
{
pika_context_get_foreground (editor->context, &rgb);
pika_color_editor_fg_changed (editor->context, &rgb, editor);
}
}
}
}
static void
pika_color_editor_color_picked (GtkWidget *widget,
const PikaRGB *rgb,
PikaColorEditor *editor)
{
if (editor->context)
{
if (editor->edit_bg)
pika_context_set_background (editor->context, rgb);
else
pika_context_set_foreground (editor->context, rgb);
}
}
static void
pika_color_editor_entry_changed (PikaColorHexEntry *entry,
PikaColorEditor *editor)
{
PikaRGB rgb;
pika_color_hex_entry_get_color (entry, &rgb);
if (editor->context)
{
if (editor->edit_bg)
pika_context_set_background (editor->context, &rgb);
else
pika_context_set_foreground (editor->context, &rgb);
}
}
static void
pika_color_editor_history_selected (PikaColorHistory *history,
const PikaRGB *rgb,
PikaColorEditor *editor)
{
if (editor->context)
{
if (editor->edit_bg)
pika_context_set_background (editor->context, rgb);
else
pika_context_set_foreground (editor->context, rgb);
}
}

View File

@ -0,0 +1,66 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikacoloreditor.h
* Copyright (C) 2002 Michael Natterer <mitch@gimp.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __PIKA_COLOR_EDITOR_H__
#define __PIKA_COLOR_EDITOR_H__
#include "pikaeditor.h"
#define PIKA_TYPE_COLOR_EDITOR (pika_color_editor_get_type ())
#define PIKA_COLOR_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIKA_TYPE_COLOR_EDITOR, PikaColorEditor))
#define PIKA_COLOR_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIKA_TYPE_COLOR_EDITOR, PikaColorEditorClass))
#define PIKA_IS_COLOR_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIKA_TYPE_COLOR_EDITOR))
#define PIKA_IS_COLOR_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIKA_TYPE_COLOR_EDITOR))
#define PIKA_COLOR_EDITOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIKA_TYPE_COLOR_EDITOR, PikaColorEditorClass))
typedef struct _PikaColorEditorClass PikaColorEditorClass;
struct _PikaColorEditor
{
PikaEditor parent_instance;
PikaContext *context;
gboolean edit_bg;
GtkWidget *hbox;
GtkWidget *notebook;
GtkWidget *fg_bg;
GtkWidget *hex_entry;
};
struct _PikaColorEditorClass
{
PikaEditorClass parent_class;
};
GType pika_color_editor_get_type (void) G_GNUC_CONST;
GtkWidget * pika_color_editor_new (PikaContext *context);
#endif /* __PIKA_COLOR_EDITOR_H__ */

1242
app/widgets/pikacolorframe.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,124 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __PIKA_COLOR_FRAME_H__
#define __PIKA_COLOR_FRAME_H__
#define PIKA_COLOR_FRAME_ROWS 6
#define PIKA_TYPE_COLOR_FRAME (pika_color_frame_get_type ())
#define PIKA_COLOR_FRAME(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIKA_TYPE_COLOR_FRAME, PikaColorFrame))
#define PIKA_COLOR_FRAME_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIKA_TYPE_COLOR_FRAME, PikaColorFrameClass))
#define PIKA_IS_COLOR_FRAME(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIKA_TYPE_COLOR_FRAME))
#define PIKA_IS_COLOR_FRAME_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIKA_TYPE_COLOR_FRAME))
#define PIKA_COLOR_FRAME_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIKA_TYPE_COLOR_FRAME, PikaColorFrameClass))
typedef struct _PikaColorFrameClass PikaColorFrameClass;
struct _PikaColorFrame
{
PikaFrame parent_instance;
Pika *pika;
gboolean sample_valid;
gboolean sample_average;
const Babl *sample_format;
gdouble pixel[4];
PikaRGB color;
gint x;
gint y;
PikaColorPickMode pick_mode;
PangoEllipsizeMode ellipsize;
gboolean has_number;
gint number;
gboolean has_color_area;
gboolean has_coords;
GtkWidget *combo;
GtkWidget *color_area;
GtkWidget *coords_box_x;
GtkWidget *coords_box_y;
GtkWidget *coords_label_x;
GtkWidget *coords_label_y;
GtkWidget *name_labels[PIKA_COLOR_FRAME_ROWS];
GtkWidget *value_labels[PIKA_COLOR_FRAME_ROWS];
PangoLayout *number_layout;
PikaImage *image;
PikaColorConfig *config;
PikaColorProfile *view_profile;
PikaColorRenderingIntent
simulation_intent;
};
struct _PikaColorFrameClass
{
PikaFrameClass parent_class;
};
GType pika_color_frame_get_type (void) G_GNUC_CONST;
GtkWidget * pika_color_frame_new (Pika *pika);
void pika_color_frame_set_mode (PikaColorFrame *frame,
PikaColorPickMode mode);
void pika_color_frame_set_ellipsize (PikaColorFrame *frame,
PangoEllipsizeMode ellipsize);
void pika_color_frame_set_has_number (PikaColorFrame *frame,
gboolean has_number);
void pika_color_frame_set_number (PikaColorFrame *frame,
gint number);
void pika_color_frame_set_has_color_area (PikaColorFrame *frame,
gboolean has_color_area);
void pika_color_frame_set_has_coords (PikaColorFrame *frame,
gboolean has_coords);
void pika_color_frame_set_color (PikaColorFrame *frame,
gboolean sample_average,
const Babl *format,
gpointer pixel,
const PikaRGB *color,
gint x,
gint y);
void pika_color_frame_set_invalid (PikaColorFrame *frame);
void pika_color_frame_set_color_config (PikaColorFrame *frame,
PikaColorConfig *config);
void pika_color_frame_set_image (PikaColorFrame *frame,
PikaImage *image);
#endif /* __PIKA_COLOR_FRAME_H__ */

View File

@ -0,0 +1,590 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikacolorhistory.c
* Copyright (C) 2015 Jehan <jehan@girinstud.io>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gegl.h>
#include <gtk/gtk.h>
#include "libpikabase/pikabase.h"
#include "libpikawidgets/pikawidgets.h"
#include "widgets-types.h"
#include "config/pikacoreconfig.h"
#include "core/pika.h"
#include "core/pika-palettes.h"
#include "core/pikacontext.h"
#include "core/pikaimage.h"
#include "app/core/pikaimage-colormap.h"
#include "core/pikapalettemru.h"
#include "pikacolorhistory.h"
#include "pika-intl.h"
enum
{
COLOR_SELECTED,
LAST_SIGNAL
};
enum
{
PROP_0,
PROP_CONTEXT,
PROP_HISTORY_SIZE,
};
#define DEFAULT_HISTORY_SIZE 12
#define COLOR_AREA_SIZE 20
#define CHANNEL_EPSILON 1e-3
/* GObject methods */
static void pika_color_history_constructed (GObject *object);
static void pika_color_history_finalize (GObject *object);
static void pika_color_history_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
static void pika_color_history_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
/* GtkWidget methods */
static GtkSizeRequestMode pika_color_history_get_request_mode (GtkWidget *widget);
static void pika_color_history_get_preferred_width_for_height (GtkWidget *widget,
gint height,
gint *minimum_width,
gint *natural_width);
static void pika_color_history_get_preferred_height_for_width (GtkWidget *widget,
gint width,
gint *minimum_height,
gint *natural_height);
static void pika_color_history_size_allocate (GtkWidget *widget,
GdkRectangle *allocation);
/* Signal handlers */
static void pika_color_history_color_clicked (GtkWidget *widget,
PikaColorHistory *history);
static void pika_color_history_palette_dirty (PikaColorHistory *history);
static void pika_color_history_color_changed (GtkWidget *widget,
gpointer data);
static void pika_color_history_image_changed (PikaContext *context,
PikaImage *image,
PikaColorHistory *history);
/* Utils */
static void pika_color_history_reorganize (PikaColorHistory *history);
G_DEFINE_TYPE (PikaColorHistory, pika_color_history, GTK_TYPE_GRID)
#define parent_class pika_color_history_parent_class
static guint history_signals[LAST_SIGNAL] = { 0 };
static void
pika_color_history_class_init (PikaColorHistoryClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
object_class->constructed = pika_color_history_constructed;
object_class->set_property = pika_color_history_set_property;
object_class->get_property = pika_color_history_get_property;
object_class->finalize = pika_color_history_finalize;
widget_class->get_request_mode = pika_color_history_get_request_mode;
widget_class->get_preferred_width_for_height = pika_color_history_get_preferred_width_for_height;
widget_class->get_preferred_height_for_width = pika_color_history_get_preferred_height_for_width;
widget_class->size_allocate = pika_color_history_size_allocate;
history_signals[COLOR_SELECTED] =
g_signal_new ("color-selected",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (PikaColorHistoryClass, color_selected),
NULL, NULL, NULL,
G_TYPE_NONE, 1,
G_TYPE_POINTER);
g_object_class_install_property (object_class, PROP_CONTEXT,
g_param_spec_object ("context", NULL, NULL,
PIKA_TYPE_CONTEXT,
PIKA_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
g_object_class_install_property (object_class, PROP_HISTORY_SIZE,
g_param_spec_int ("history-size",
NULL, NULL,
2, G_MAXINT,
DEFAULT_HISTORY_SIZE,
PIKA_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
gtk_widget_class_set_css_name (GTK_WIDGET_CLASS (klass), "PikaColorHistory");
klass->color_selected = NULL;
}
static void
pika_color_history_init (PikaColorHistory *history)
{
history->color_areas = NULL;
history->buttons = NULL;
history->n_rows = 1;
gtk_grid_set_row_spacing (GTK_GRID (history), 2);
gtk_grid_set_column_spacing (GTK_GRID (history), 2);
}
static void
pika_color_history_constructed (GObject *object)
{
PikaColorHistory *history = PIKA_COLOR_HISTORY (object);
PikaPalette *palette;
G_OBJECT_CLASS (parent_class)->constructed (object);
palette = pika_palettes_get_color_history (history->context->pika);
g_signal_connect_object (palette, "dirty",
G_CALLBACK (pika_color_history_palette_dirty),
G_OBJECT (history), G_CONNECT_SWAPPED);
}
static void
pika_color_history_finalize (GObject *object)
{
PikaColorHistory *history = PIKA_COLOR_HISTORY (object);
if (history->context)
g_signal_handlers_disconnect_by_func (history->context,
pika_color_history_image_changed,
history);
if (history->active_image)
g_signal_handlers_disconnect_by_func (history->active_image,
G_CALLBACK (pika_color_history_palette_dirty),
history);
g_clear_pointer (&history->color_areas, g_free);
g_clear_pointer (&history->buttons, g_free);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
pika_color_history_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
PikaColorHistory *history = PIKA_COLOR_HISTORY (object);
switch (property_id)
{
case PROP_CONTEXT:
if (history->context)
g_signal_handlers_disconnect_by_func (history->context,
pika_color_history_image_changed,
history);
if (history->active_image)
{
g_signal_handlers_disconnect_by_func (history->active_image,
G_CALLBACK (pika_color_history_palette_dirty),
history);
history->active_image = NULL;
}
history->context = g_value_get_object (value);
if (history->context)
{
g_signal_connect (history->context, "image-changed",
G_CALLBACK (pika_color_history_image_changed),
history);
history->active_image = pika_context_get_image (history->context);
if (history->active_image)
{
g_signal_connect_swapped (history->active_image, "notify::base-type",
G_CALLBACK (pika_color_history_palette_dirty),
history);
g_signal_connect_swapped (history->active_image, "colormap-changed",
G_CALLBACK (pika_color_history_palette_dirty),
history);
}
}
break;
case PROP_HISTORY_SIZE:
{
GtkWidget *button;
GtkWidget *color_area;
gint i;
history->history_size = g_value_get_int (value);
/* Destroy previous color buttons. */
gtk_container_foreach (GTK_CONTAINER (history),
(GtkCallback) gtk_widget_destroy, NULL);
history->buttons = g_realloc_n (history->buttons,
history->history_size,
sizeof (GtkWidget*));
history->color_areas = g_realloc_n (history->color_areas,
history->history_size,
sizeof (GtkWidget*));
for (i = 0; i < history->history_size; i++)
{
PikaRGB black = { 0.0, 0.0, 0.0, 1.0 };
gint row, column;
column = i % (history->history_size / history->n_rows);
row = i / (history->history_size / history->n_rows);
button = gtk_button_new ();
gtk_widget_set_size_request (button, COLOR_AREA_SIZE, COLOR_AREA_SIZE);
gtk_grid_attach (GTK_GRID (history), button, column, row, 1, 1);
gtk_widget_show (button);
color_area = pika_color_area_new (&black, PIKA_COLOR_AREA_SMALL_CHECKS,
GDK_BUTTON2_MASK);
pika_color_area_set_color_config (PIKA_COLOR_AREA (color_area),
history->context->pika->config->color_management);
gtk_container_add (GTK_CONTAINER (button), color_area);
gtk_widget_show (color_area);
g_signal_connect (button, "clicked",
G_CALLBACK (pika_color_history_color_clicked),
history);
g_signal_connect (color_area, "color-changed",
G_CALLBACK (pika_color_history_color_changed),
GINT_TO_POINTER (i));
history->buttons[i] = button;
history->color_areas[i] = color_area;
}
pika_color_history_palette_dirty (history);
}
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
pika_color_history_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
PikaColorHistory *history = PIKA_COLOR_HISTORY (object);
switch (property_id)
{
case PROP_CONTEXT:
g_value_set_object (value, history->context);
break;
case PROP_HISTORY_SIZE:
g_value_set_int (value, history->history_size);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static GtkSizeRequestMode
pika_color_history_get_request_mode (GtkWidget *widget)
{
return GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
}
static void
pika_color_history_get_preferred_width_for_height (GtkWidget *widget,
gint height,
gint *minimum_width,
gint *natural_width)
{
PikaColorHistory *history = PIKA_COLOR_HISTORY (widget);
GtkWidget *button;
gint button_width = COLOR_AREA_SIZE;
button = gtk_grid_get_child_at (GTK_GRID (widget), 0, 0);
if (button)
button_width = MAX (gtk_widget_get_allocated_width (button),
COLOR_AREA_SIZE);
/* This is a bit of a trick. Though the height actually depends on the
* width, I actually just request for about half the expected width if
* we were to align all color buttons on one line. This way, it is
* possible to resize the widget smaller than it currently is, hence
* allowing reorganization of icons.
*/
*minimum_width = button_width * (1 + history->history_size / 2);
*natural_width = *minimum_width;
}
static void
pika_color_history_get_preferred_height_for_width (GtkWidget *widget,
gint width,
gint *minimum_height,
gint *natural_height)
{
PikaColorHistory *history = PIKA_COLOR_HISTORY (widget);
GtkWidget *button;
gint button_width = COLOR_AREA_SIZE;
gint button_height = COLOR_AREA_SIZE;
gint height;
button = gtk_grid_get_child_at (GTK_GRID (widget), 0, 0);
if (button)
{
button_width = MAX (gtk_widget_get_allocated_width (button),
COLOR_AREA_SIZE);
button_height = MAX (gtk_widget_get_allocated_height (button),
COLOR_AREA_SIZE);
}
if (width > button_width * history->history_size + 2 * (history->history_size - 1))
height = button_height;
else
height = 2 * button_height + 2;
*minimum_height = height;
*natural_height = height;
}
static void
pika_color_history_size_allocate (GtkWidget *widget,
GdkRectangle *allocation)
{
PikaColorHistory *history = PIKA_COLOR_HISTORY (widget);
GtkWidget *button;
gint button_width = COLOR_AREA_SIZE;
gint button_height = COLOR_AREA_SIZE;
gint height;
gint n_rows;
button = gtk_grid_get_child_at (GTK_GRID (widget), 0, 0);
if (button)
{
button_width = MAX (gtk_widget_get_allocated_width (button),
COLOR_AREA_SIZE);
button_height = MAX (gtk_widget_get_allocated_height (button),
COLOR_AREA_SIZE);
}
if (allocation->width > button_width * history->history_size +
2 * (history->history_size - 1))
{
n_rows = 1;
height = button_height;
}
else
{
n_rows = 2;
height = 2 * button_height + 2;
}
if (n_rows != history->n_rows)
{
history->n_rows = n_rows;
pika_color_history_reorganize (history);
}
allocation->height = height;
GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, allocation);
}
/* Public Functions */
GtkWidget *
pika_color_history_new (PikaContext *context,
gint history_size)
{
PikaColorHistory *history;
g_return_val_if_fail (PIKA_IS_CONTEXT (context), NULL);
history = g_object_new (PIKA_TYPE_COLOR_HISTORY,
"context", context,
"history-size", history_size,
NULL);
return GTK_WIDGET (history);
}
/* Color history callback. */
static void
pika_color_history_color_clicked (GtkWidget *widget,
PikaColorHistory *history)
{
PikaColorArea *color_area;
PikaRGB color;
color_area = PIKA_COLOR_AREA (gtk_bin_get_child (GTK_BIN (widget)));
pika_color_area_get_color (color_area, &color);
g_signal_emit (history, history_signals[COLOR_SELECTED], 0,
&color);
}
/* Color history palette callback. */
static void
pika_color_history_palette_dirty (PikaColorHistory *history)
{
PikaPalette *palette;
PikaPalette *colormap_palette = NULL;
PikaImageBaseType base_type = PIKA_RGB;
gint i;
palette = pika_palettes_get_color_history (history->context->pika);
if (history->active_image)
{
base_type = pika_image_get_base_type (history->active_image);
if (base_type == PIKA_INDEXED)
colormap_palette = pika_image_get_colormap_palette (history->active_image);
}
for (i = 0; i < history->history_size; i++)
{
PikaPaletteEntry *entry = pika_palette_get_entry (palette, i);
PikaRGB black = { 0.0, 0.0, 0.0, 1.0 };
PikaRGB color = entry ? entry->color : black;
gboolean oog = FALSE;
g_signal_handlers_block_by_func (history->color_areas[i],
pika_color_history_color_changed,
GINT_TO_POINTER (i));
pika_color_area_set_color (PIKA_COLOR_AREA (history->color_areas[i]),
&color);
if (/* Common out-of-gamut case */
(color.r < 0.0 || color.r > 1.0 ||
color.g < 0.0 || color.g > 1.0 ||
color.b < 0.0 || color.b > 1.0) ||
/* Indexed images */
(colormap_palette && ! pika_palette_find_entry (colormap_palette, &color, NULL)) ||
/* Grayscale images */
(base_type == PIKA_GRAY &&
(ABS (color.r - color.g) > CHANNEL_EPSILON ||
ABS (color.r - color.b) > CHANNEL_EPSILON ||
ABS (color.g - color.b) > CHANNEL_EPSILON)))
oog = TRUE;
pika_color_area_set_out_of_gamut (PIKA_COLOR_AREA (history->color_areas[i]), oog);
g_signal_handlers_unblock_by_func (history->color_areas[i],
pika_color_history_color_changed,
GINT_TO_POINTER (i));
}
}
/* Color area callbacks. */
static void
pika_color_history_color_changed (GtkWidget *widget,
gpointer data)
{
PikaColorHistory *history;
PikaPalette *palette;
PikaRGB color;
history = PIKA_COLOR_HISTORY (gtk_widget_get_ancestor (widget,
PIKA_TYPE_COLOR_HISTORY));
palette = pika_palettes_get_color_history (history->context->pika);
pika_color_area_get_color (PIKA_COLOR_AREA (widget), &color);
pika_palette_set_entry_color (palette, GPOINTER_TO_INT (data), &color);
}
static void
pika_color_history_image_changed (PikaContext *context,
PikaImage *image,
PikaColorHistory *history)
{
/* Update active image. */
if (history->active_image)
g_signal_handlers_disconnect_by_func (history->active_image,
G_CALLBACK (pika_color_history_palette_dirty),
history);
history->active_image = image;
if (image)
{
g_signal_connect_swapped (image, "notify::base-type",
G_CALLBACK (pika_color_history_palette_dirty),
history);
g_signal_connect_swapped (image, "colormap-changed",
G_CALLBACK (pika_color_history_palette_dirty),
history);
}
/* Update the palette. */
pika_color_history_palette_dirty (history);
}
static void
pika_color_history_reorganize (PikaColorHistory *history)
{
GtkWidget *button;
gint i;
g_return_if_fail (history->buttons[0] && GTK_IS_BUTTON (history->buttons[0]));
for (i = 0; i < history->history_size; i++)
{
gint row, column;
column = i % (history->history_size / history->n_rows);
row = i / (history->history_size / history->n_rows);
button = history->buttons[i];
g_object_ref (button);
gtk_container_remove (GTK_CONTAINER (history), button);
gtk_grid_attach (GTK_GRID (history), button, column, row, 1, 1);
}
}

View File

@ -0,0 +1,68 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikacolorhistory.h
* Copyright (C) 2015 Jehan <jehan@girinstud.io>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __PIKA_COLOR_HISTORY_H__
#define __PIKA_COLOR_HISTORY_H__
#define PIKA_TYPE_COLOR_HISTORY (pika_color_history_get_type ())
#define PIKA_COLOR_HISTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIKA_TYPE_COLOR_HISTORY, PikaColorHistory))
#define PIKA_COLOR_HISTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIKA_TYPE_COLOR_HISTORY, PikaColorHistoryClass))
#define PIKA_IS_COLOR_HISTORY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIKA_TYPE_COLOR_HISTORY))
#define PIKA_IS_COLOR_HISTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIKA_TYPE_COLOR_HISTORY))
#define PIKA_COLOR_HISTORY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIKA_TYPE_COLOR_HISTORY, PikaColorHistoryClass))
typedef struct _PikaColorHistoryClass PikaColorHistoryClass;
struct _PikaColorHistory
{
GtkGrid parent_instance;
PikaContext *context;
PikaImage *active_image;
GtkWidget **color_areas;
GtkWidget **buttons;
gint history_size;
gint n_rows;
};
struct _PikaColorHistoryClass
{
GtkGridClass parent_class;
/* signals */
void (* color_selected) (PikaColorHistory *history,
const PikaRGB *rgb);
};
GType pika_color_history_get_type (void) G_GNUC_CONST;
GtkWidget * pika_color_history_new (PikaContext *context,
gint history_size);
#endif /* __PIKA_COLOR_HISTORY_H__ */

View File

@ -0,0 +1,413 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gegl.h>
#include <gtk/gtk.h>
#include "libpikacolor/pikacolor.h"
#include "libpikawidgets/pikawidgets.h"
#include "widgets-types.h"
#include "core/pika.h"
#include "core/pikacontext.h"
#include "core/pikaimage.h"
#include "core/pikaimage-colormap.h"
#include "core/pikapalette.h"
#include "core/pikaprojection.h"
#include "pikacolordialog.h"
#include "pikacolormapeditor.h"
#include "pikacolormapselection.h"
#include "pikadialogfactory.h"
#include "pikadocked.h"
#include "pikamenufactory.h"
#include "pikawidgets-utils.h"
#include "pika-intl.h"
static void pika_colormap_editor_docked_iface_init (PikaDockedInterface *face);
static void pika_colormap_editor_constructed (GObject *object);
static void pika_colormap_editor_dispose (GObject *object);
static void pika_colormap_editor_unmap (GtkWidget *widget);
static void pika_colormap_editor_set_context (PikaDocked *docked,
PikaContext *context);
static void pika_colormap_editor_color_update (PikaColorDialog *dialog,
const PikaRGB *color,
PikaColorDialogState state,
PikaColormapEditor *editor);
static gboolean pika_colormap_editor_entry_button_press (GtkWidget *widget,
GdkEvent *event,
gpointer user_data);
static gboolean pika_colormap_editor_entry_popup (GtkWidget *widget,
gpointer user_data);
static void pika_colormap_editor_color_clicked (PikaColormapEditor *editor,
PikaPaletteEntry *entry,
GdkModifierType state);
G_DEFINE_TYPE_WITH_CODE (PikaColormapEditor, pika_colormap_editor,
PIKA_TYPE_IMAGE_EDITOR,
G_IMPLEMENT_INTERFACE (PIKA_TYPE_DOCKED,
pika_colormap_editor_docked_iface_init))
#define parent_class pika_colormap_editor_parent_class
static PikaDockedInterface *parent_docked_iface = NULL;
static void
pika_colormap_editor_class_init (PikaColormapEditorClass* klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
object_class->constructed = pika_colormap_editor_constructed;
object_class->dispose = pika_colormap_editor_dispose;
widget_class->unmap = pika_colormap_editor_unmap;
}
static void
pika_colormap_editor_docked_iface_init (PikaDockedInterface *iface)
{
parent_docked_iface = g_type_interface_peek_parent (iface);
if (! parent_docked_iface)
parent_docked_iface = g_type_default_interface_peek (PIKA_TYPE_DOCKED);
iface->set_context = pika_colormap_editor_set_context;
}
static void
pika_colormap_editor_init (PikaColormapEditor *editor)
{
}
static void
pika_colormap_editor_constructed (GObject *object)
{
PikaColormapEditor *editor = PIKA_COLORMAP_EDITOR (object);
GdkModifierType extend_mask;
GdkModifierType modify_mask;
G_OBJECT_CLASS (parent_class)->constructed (object);
/* Editor buttons. */
extend_mask = gtk_widget_get_modifier_mask (GTK_WIDGET (object),
GDK_MODIFIER_INTENT_EXTEND_SELECTION);
modify_mask = gtk_widget_get_modifier_mask (GTK_WIDGET (object),
GDK_MODIFIER_INTENT_MODIFY_SELECTION);
pika_editor_add_action_button (PIKA_EDITOR (editor), "colormap",
"colormap-edit-color",
NULL);
pika_editor_add_action_button (PIKA_EDITOR (editor), "colormap",
"colormap-add-color-from-fg",
"colormap-add-color-from-bg",
pika_get_toggle_behavior_mask (),
NULL);
pika_editor_add_action_button (PIKA_EDITOR (editor), "colormap",
"colormap-selection-replace",
"colormap-selection-add",
extend_mask,
"colormap-selection-subtract",
modify_mask,
"colormap-selection-intersect",
extend_mask | modify_mask,
NULL);
}
static void
pika_colormap_editor_dispose (GObject *object)
{
PikaColormapEditor *editor = PIKA_COLORMAP_EDITOR (object);
g_clear_pointer (&editor->color_dialog, gtk_widget_destroy);
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
pika_colormap_editor_unmap (GtkWidget *widget)
{
PikaColormapEditor *editor = PIKA_COLORMAP_EDITOR (widget);
if (editor->color_dialog)
gtk_widget_hide (editor->color_dialog);
GTK_WIDGET_CLASS (parent_class)->unmap (widget);
}
static void
pika_colormap_editor_set_context (PikaDocked *docked,
PikaContext *context)
{
PikaColormapEditor *editor = PIKA_COLORMAP_EDITOR (docked);
parent_docked_iface->set_context (docked, context);
if (editor->selection)
gtk_widget_destroy (editor->selection);
editor->selection = NULL;
/* Main selection widget. */
if (context)
{
editor->selection = pika_colormap_selection_new (context);
gtk_box_pack_start (GTK_BOX (editor), editor->selection, TRUE, TRUE, 0);
gtk_widget_show (editor->selection);
g_signal_connect_swapped (editor->selection, "color-clicked",
G_CALLBACK (pika_colormap_editor_color_clicked),
editor);
g_signal_connect_swapped (editor->selection, "color-activated",
G_CALLBACK (pika_colormap_editor_edit_color),
editor);
g_signal_connect (editor->selection, "button-press-event",
G_CALLBACK (pika_colormap_editor_entry_button_press),
editor);
g_signal_connect (editor->selection, "popup-menu",
G_CALLBACK (pika_colormap_editor_entry_popup),
editor);
}
}
/* public functions */
GtkWidget *
pika_colormap_editor_new (PikaMenuFactory *menu_factory)
{
g_return_val_if_fail (PIKA_IS_MENU_FACTORY (menu_factory), NULL);
return g_object_new (PIKA_TYPE_COLORMAP_EDITOR,
"menu-factory", menu_factory,
"menu-identifier", "<Colormap>",
"ui-path", "/colormap-popup",
NULL);
}
void
pika_colormap_editor_edit_color (PikaColormapEditor *editor)
{
PikaImage *image;
PikaRGB color;
gchar *desc;
gint index;
g_return_if_fail (PIKA_IS_COLORMAP_EDITOR (editor));
image = PIKA_IMAGE_EDITOR (editor)->image;
index = pika_colormap_selection_get_index (PIKA_COLORMAP_SELECTION (editor->selection),
NULL);
if (index == -1)
/* No colormap. */
return;
pika_image_get_colormap_entry (image, index, &color);
desc = g_strdup_printf (_("Edit colormap entry #%d"), index);
if (! editor->color_dialog)
{
editor->color_dialog =
pika_color_dialog_new (PIKA_VIEWABLE (image),
PIKA_IMAGE_EDITOR (editor)->context,
FALSE,
_("Edit Colormap Entry"),
PIKA_ICON_COLORMAP,
desc,
GTK_WIDGET (editor),
pika_dialog_factory_get_singleton (),
"pika-colormap-editor-color-dialog",
(const PikaRGB *) &color,
TRUE, FALSE);
g_signal_connect (editor->color_dialog, "destroy",
G_CALLBACK (gtk_widget_destroyed),
&editor->color_dialog);
g_signal_connect (editor->color_dialog, "update",
G_CALLBACK (pika_colormap_editor_color_update),
editor);
}
else
{
pika_viewable_dialog_set_viewables (PIKA_VIEWABLE_DIALOG (editor->color_dialog),
g_list_prepend (NULL, image),
PIKA_IMAGE_EDITOR (editor)->context);
g_object_set (editor->color_dialog, "description", desc, NULL);
pika_color_dialog_set_color (PIKA_COLOR_DIALOG (editor->color_dialog),
&color);
if (! gtk_widget_get_visible (editor->color_dialog))
pika_dialog_factory_position_dialog (pika_dialog_factory_get_singleton (),
"pika-colormap-editor-color-dialog",
editor->color_dialog,
pika_widget_get_monitor (GTK_WIDGET (editor)));
}
g_free (desc);
gtk_window_present (GTK_WINDOW (editor->color_dialog));
}
gint
pika_colormap_editor_get_index (PikaColormapEditor *editor,
const PikaRGB *search)
{
g_return_val_if_fail (PIKA_IS_COLORMAP_EDITOR (editor), 0);
return pika_colormap_selection_get_index (PIKA_COLORMAP_SELECTION (editor->selection), search);
}
gboolean
pika_colormap_editor_set_index (PikaColormapEditor *editor,
gint index,
PikaRGB *color)
{
g_return_val_if_fail (PIKA_IS_COLORMAP_EDITOR (editor), FALSE);
return pika_colormap_selection_set_index (PIKA_COLORMAP_SELECTION (editor->selection), index, color);
}
gint
pika_colormap_editor_max_index (PikaColormapEditor *editor)
{
g_return_val_if_fail (PIKA_IS_COLORMAP_EDITOR (editor), -1);
return pika_colormap_selection_max_index (PIKA_COLORMAP_SELECTION (editor->selection));
}
static void
pika_colormap_editor_color_update (PikaColorDialog *dialog,
const PikaRGB *color,
PikaColorDialogState state,
PikaColormapEditor *editor)
{
PikaImageEditor *image_editor = PIKA_IMAGE_EDITOR (editor);
PikaImage *image = image_editor->image;
gboolean push_undo = FALSE;
switch (state)
{
case PIKA_COLOR_DIALOG_OK:
push_undo = TRUE;
if (state & pika_get_toggle_behavior_mask ())
pika_context_set_background (image_editor->context, color);
else
pika_context_set_foreground (image_editor->context, color);
/* Fall through */
case PIKA_COLOR_DIALOG_CANCEL:
gtk_widget_hide (editor->color_dialog);
break;
case PIKA_COLOR_DIALOG_UPDATE:
break;
}
if (image)
{
gint col_index;
col_index = pika_colormap_selection_get_index (PIKA_COLORMAP_SELECTION (editor->selection),
NULL);
if (push_undo)
{
PikaRGB old_color;
pika_color_selection_get_old_color (
PIKA_COLOR_SELECTION (dialog->selection), &old_color);
/* Restore old color for undo */
pika_image_set_colormap_entry (image, col_index, &old_color,
FALSE);
}
pika_image_set_colormap_entry (image, col_index, color,
push_undo);
if (push_undo)
pika_image_flush (image);
else
pika_projection_flush (pika_image_get_projection (image));
}
}
static gboolean
pika_colormap_editor_entry_button_press (GtkWidget *widget,
GdkEvent *event,
gpointer user_data)
{
if (gdk_event_triggers_context_menu (event))
{
pika_editor_popup_menu_at_pointer (PIKA_EDITOR (user_data), event);
return GDK_EVENT_STOP;
}
return GDK_EVENT_PROPAGATE;
}
static gboolean
pika_colormap_editor_entry_popup (GtkWidget *widget,
gpointer user_data)
{
PikaColormapEditor *editor = PIKA_COLORMAP_EDITOR (user_data);
PikaColormapSelection *selection = PIKA_COLORMAP_SELECTION (widget);
PikaPaletteEntry *selected;
GdkRectangle rect;
selected = pika_colormap_selection_get_selected_entry (selection);
if (!selected)
return GDK_EVENT_PROPAGATE;
pika_colormap_selection_get_entry_rect (selection, selected, &rect);
return pika_editor_popup_menu_at_rect (PIKA_EDITOR (editor),
gtk_widget_get_window (widget),
&rect, GDK_GRAVITY_CENTER, GDK_GRAVITY_NORTH_WEST,
NULL);
}
static void
pika_colormap_editor_color_clicked (PikaColormapEditor *editor,
PikaPaletteEntry *entry,
GdkModifierType state)
{
PikaImageEditor *image_editor = PIKA_IMAGE_EDITOR (editor);
if (state & pika_get_toggle_behavior_mask ())
pika_context_set_background (image_editor->context, &entry->color);
else
pika_context_set_foreground (image_editor->context, &entry->color);
}

View File

@ -0,0 +1,68 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __PIKA_COLORMAP_EDITOR_H__
#define __PIKA_COLORMAP_EDITOR_H__
#include "pikaimageeditor.h"
#define PIKA_TYPE_COLORMAP_EDITOR (pika_colormap_editor_get_type ())
#define PIKA_COLORMAP_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIKA_TYPE_COLORMAP_EDITOR, PikaColormapEditor))
#define PIKA_COLORMAP_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIKA_TYPE_COLORMAP_EDITOR, PikaColormapEditorClass))
#define PIKA_IS_COLORMAP_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIKA_TYPE_COLORMAP_EDITOR))
#define PIKA_IS_COLORMAP_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIKA_TYPE_COLORMAP_EDITOR))
#define PIKA_COLORMAP_EDITOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIKA_TYPE_COLORMAP_EDITOR, PikaColormapEditorClass))
typedef struct _PikaColormapEditorClass PikaColormapEditorClass;
struct _PikaColormapEditor
{
PikaImageEditor parent_instance;
GtkWidget *selection;
GtkWidget *color_dialog;
};
struct _PikaColormapEditorClass
{
PikaImageEditorClass parent_class;
};
GType pika_colormap_editor_get_type (void) G_GNUC_CONST;
GtkWidget * pika_colormap_editor_new (PikaMenuFactory *menu_factory);
void pika_colormap_editor_edit_color (PikaColormapEditor *editor);
gint pika_colormap_editor_get_index (PikaColormapEditor *editor,
const PikaRGB *search);
gboolean pika_colormap_editor_set_index (PikaColormapEditor *editor,
gint index,
PikaRGB *color);
gint pika_colormap_editor_max_index (PikaColormapEditor *editor);
#endif /* __PIKA_COLORMAP_EDITOR_H__ */

View File

@ -0,0 +1,812 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gegl.h>
#include <gtk/gtk.h>
#include "libpikabase/pikabase.h"
#include "libpikacolor/pikacolor.h"
#include "libpikamath/pikamath.h"
#include "libpikawidgets/pikawidgets.h"
#include "widgets-types.h"
#include "core/pika.h"
#include "core/pikacontext.h"
#include "core/pikacontainer.h"
#include "core/pikaimage.h"
#include "core/pikaimage-colormap.h"
#include "core/pikamarshal.h"
#include "core/pikapalette.h"
#include "core/pikaprojection.h"
#include "pikacolordialog.h"
#include "pikacolormapselection.h"
#include "pikadialogfactory.h"
#include "pikadnd.h"
#include "pikadocked.h"
#include "pikamenufactory.h"
#include "pikapaletteview.h"
#include "pikauimanager.h"
#include "pikaviewrendererpalette.h"
#include "pikawidgets-utils.h"
#include "pika-intl.h"
enum
{
PROP_0,
PROP_CONTEXT
};
enum
{
COLOR_CLICKED,
COLOR_ACTIVATED,
LAST_SIGNAL
};
#define BORDER 6
#define RGB_EPSILON 1e-6
#define HAVE_COLORMAP(image) \
(image != NULL && \
pika_image_get_base_type (image) == PIKA_INDEXED && \
pika_image_get_colormap_palette (image) != NULL)
static void pika_colormap_selection_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
static void pika_colormap_selection_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
static void pika_colormap_selection_dispose (GObject *object);
static void pika_colormap_selection_finalize (GObject *object);
static void pika_colormap_selection_unmap (GtkWidget *widget);
static PangoLayout *
pika_colormap_selection_create_layout (GtkWidget *widget);
static void pika_colormap_selection_update_entries (PikaColormapSelection *selection);
static gboolean
pika_colormap_selection_preview_draw (GtkWidget *widget,
cairo_t *cr,
PikaColormapSelection *selection);
static void pika_colormap_selection_entry_clicked (PikaPaletteView *view,
PikaPaletteEntry *entry,
GdkModifierType state,
PikaColormapSelection *selection);
static void pika_colormap_selection_entry_selected (PikaPaletteView *view,
PikaPaletteEntry *entry,
PikaColormapSelection *selection);
static void pika_colormap_selection_entry_activated (PikaPaletteView *view,
PikaPaletteEntry *entry,
PikaColormapSelection *selection);
static void pika_colormap_selection_color_dropped (PikaPaletteView *view,
PikaPaletteEntry *entry,
const PikaRGB *color,
PikaColormapSelection *selection);
static void pika_colormap_adjustment_changed (GtkAdjustment *adjustment,
PikaColormapSelection *selection);
static void pika_colormap_hex_entry_changed (PikaColorHexEntry *entry,
PikaColormapSelection *selection);
static void pika_colormap_selection_set_context (PikaColormapSelection *selection,
PikaContext *context);
static void pika_colormap_selection_image_changed (PikaColormapSelection *selection,
PikaImage *image);
static void pika_colormap_selection_set_palette (PikaColormapSelection *selection);
G_DEFINE_TYPE (PikaColormapSelection, pika_colormap_selection, GTK_TYPE_BOX)
#define parent_class pika_colormap_selection_parent_class
static guint signals[LAST_SIGNAL] = { 0 };
static void
pika_colormap_selection_class_init (PikaColormapSelectionClass* klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
signals[COLOR_CLICKED] =
g_signal_new ("color-clicked",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (PikaColormapSelectionClass, color_clicked),
NULL, NULL,
pika_marshal_VOID__POINTER_ENUM,
G_TYPE_NONE, 2,
G_TYPE_POINTER,
GDK_TYPE_MODIFIER_TYPE);
signals[COLOR_ACTIVATED] =
g_signal_new ("color-activated",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (PikaColormapSelectionClass, color_activated),
NULL, NULL, NULL,
G_TYPE_NONE, 1,
G_TYPE_POINTER);
object_class->set_property = pika_colormap_selection_set_property;
object_class->get_property = pika_colormap_selection_get_property;
object_class->dispose = pika_colormap_selection_dispose;
object_class->finalize = pika_colormap_selection_finalize;
widget_class->unmap = pika_colormap_selection_unmap;
g_object_class_install_property (object_class, PROP_CONTEXT,
g_param_spec_object ("context",
NULL, NULL,
PIKA_TYPE_CONTEXT,
PIKA_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
}
static void
pika_colormap_selection_init (PikaColormapSelection *selection)
{
GtkWidget *frame;
GtkWidget *grid;
gtk_box_set_homogeneous (GTK_BOX (selection), FALSE);
/* Main colormap frame. */
frame = gtk_frame_new (NULL);
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
gtk_box_pack_start (GTK_BOX (selection), frame, TRUE, TRUE, 0);
gtk_widget_show (frame);
selection->view = pika_view_new_full_by_types (NULL,
PIKA_TYPE_PALETTE_VIEW,
PIKA_TYPE_PALETTE,
1, 1, 0,
FALSE, TRUE, FALSE);
pika_view_set_expand (PIKA_VIEW (selection->view), TRUE);
gtk_container_add (GTK_CONTAINER (frame), selection->view);
gtk_widget_show (selection->view);
g_signal_connect (selection->view, "draw",
G_CALLBACK (pika_colormap_selection_preview_draw),
selection);
g_signal_connect (selection->view, "entry-clicked",
G_CALLBACK (pika_colormap_selection_entry_clicked),
selection);
g_signal_connect (selection->view, "entry-selected",
G_CALLBACK (pika_colormap_selection_entry_selected),
selection);
g_signal_connect (selection->view, "entry-activated",
G_CALLBACK (pika_colormap_selection_entry_activated),
selection);
g_signal_connect (selection->view, "color-dropped",
G_CALLBACK (pika_colormap_selection_color_dropped),
selection);
/* Bottom horizontal box for additional widgets. */
selection->right_vbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
gtk_box_set_homogeneous (GTK_BOX (selection->right_vbox), TRUE);
gtk_box_pack_end (GTK_BOX (selection), selection->right_vbox,
FALSE, FALSE, 0);
gtk_widget_show (selection->right_vbox);
/* Some helpful hints */
grid = gtk_grid_new ();
gtk_grid_set_column_spacing (GTK_GRID (grid), 4);
gtk_grid_set_row_spacing (GTK_GRID (grid), 2);
gtk_box_pack_end (GTK_BOX (selection), grid, FALSE, FALSE, 0);
gtk_widget_show (grid);
selection->index_adjustment = (GtkAdjustment *)
gtk_adjustment_new (0, 0, 0, 1, 10, 0);
selection->index_spinbutton = pika_spin_button_new (selection->index_adjustment,
1.0, 0);
gtk_widget_set_halign (selection->index_spinbutton, GTK_ALIGN_START);
gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (selection->index_spinbutton),
TRUE);
pika_grid_attach_aligned (GTK_GRID (grid), 0, 0,
_("Color index:"), 0.0, 0.5,
selection->index_spinbutton, 1);
g_signal_connect (selection->index_adjustment, "value-changed",
G_CALLBACK (pika_colormap_adjustment_changed),
selection);
selection->color_entry = pika_color_hex_entry_new ();
gtk_widget_set_halign (selection->color_entry, GTK_ALIGN_START);
pika_grid_attach_aligned (GTK_GRID (grid), 0, 1,
_("HTML notation:"), 0.0, 0.5,
selection->color_entry, 1);
g_signal_connect (selection->color_entry, "color-changed",
G_CALLBACK (pika_colormap_hex_entry_changed),
selection);
}
static void
pika_colormap_selection_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
PikaColormapSelection *selection = PIKA_COLORMAP_SELECTION (object);
switch (property_id)
{
case PROP_CONTEXT:
pika_colormap_selection_set_context (selection, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
pika_colormap_selection_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
PikaColormapSelection *selection = PIKA_COLORMAP_SELECTION (object);
switch (property_id)
{
case PROP_CONTEXT:
g_value_set_object (value, selection->context);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
pika_colormap_selection_dispose (GObject *object)
{
PikaColormapSelection *selection = PIKA_COLORMAP_SELECTION (object);
g_clear_pointer (&selection->color_dialog, gtk_widget_destroy);
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
pika_colormap_selection_finalize (GObject *object)
{
PikaColormapSelection *selection = PIKA_COLORMAP_SELECTION (object);
if (selection->context)
{
g_signal_handlers_disconnect_by_func (selection->context,
gtk_widget_queue_draw,
selection);
g_signal_handlers_disconnect_by_func (selection->context,
G_CALLBACK (pika_colormap_selection_image_changed),
selection);
}
if (selection->active_image)
{
g_signal_handlers_disconnect_by_func (selection->active_image,
G_CALLBACK (gtk_widget_queue_draw),
selection);
g_signal_handlers_disconnect_by_func (selection->active_image,
G_CALLBACK (pika_colormap_selection_set_palette),
selection);
}
if (selection->active_palette)
{
g_signal_handlers_disconnect_by_func (selection->active_palette,
G_CALLBACK (gtk_widget_queue_draw),
selection);
}
g_clear_object (&selection->layout);
g_clear_object (&selection->context);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
pika_colormap_selection_unmap (GtkWidget *widget)
{
PikaColormapSelection *selection = PIKA_COLORMAP_SELECTION (widget);
if (selection->color_dialog)
gtk_widget_hide (selection->color_dialog);
GTK_WIDGET_CLASS (parent_class)->unmap (widget);
}
/* public functions */
GtkWidget *
pika_colormap_selection_new (PikaContext *context)
{
g_return_val_if_fail (PIKA_IS_CONTEXT (context), NULL);
return g_object_new (PIKA_TYPE_COLORMAP_SELECTION,
"context", context,
"orientation", GTK_ORIENTATION_VERTICAL,
NULL);
}
gint
pika_colormap_selection_get_index (PikaColormapSelection *selection,
const PikaRGB *search)
{
PikaImage *image;
gint index;
g_return_val_if_fail (PIKA_IS_COLORMAP_SELECTION (selection), 0);
image = pika_context_get_image (selection->context);
if (! HAVE_COLORMAP (image))
return -1;
index = selection->col_index;
if (search)
{
PikaRGB temp;
pika_image_get_colormap_entry (image, index, &temp);
if (pika_rgb_distance (&temp, search) > RGB_EPSILON)
{
gint n_colors = pika_image_get_colormap_size (image);
gint i;
for (i = 0; i < n_colors; i++)
{
pika_image_get_colormap_entry (image, i, &temp);
if (pika_rgb_distance (&temp, search) < RGB_EPSILON)
{
index = i;
break;
}
}
}
}
return index;
}
gboolean
pika_colormap_selection_set_index (PikaColormapSelection *selection,
gint index,
PikaRGB *color)
{
PikaImage *image;
gint size;
g_return_val_if_fail (PIKA_IS_COLORMAP_SELECTION (selection), FALSE);
image = pika_context_get_image (selection->context);
if (! HAVE_COLORMAP (image))
return FALSE;
size = pika_image_get_colormap_size (image);
if (size < 1)
return FALSE;
index = CLAMP (index, 0, size - 1);
if (index != selection->col_index)
{
PikaPalette *palette = pika_image_get_colormap_palette (image);
selection->col_index = index;
pika_palette_view_select_entry (PIKA_PALETTE_VIEW (selection->view),
pika_palette_get_entry (palette, index));
pika_colormap_selection_update_entries (selection);
}
if (color)
pika_image_get_colormap_entry (image, index, color);
return TRUE;
}
gint
pika_colormap_selection_max_index (PikaColormapSelection *selection)
{
PikaImage *image;
g_return_val_if_fail (PIKA_IS_COLORMAP_SELECTION (selection), -1);
image = pika_context_get_image (selection->context);
if (! HAVE_COLORMAP (image))
return -1;
return MAX (0, pika_image_get_colormap_size (image) - 1);
}
PikaPaletteEntry *
pika_colormap_selection_get_selected_entry (PikaColormapSelection *selection)
{
g_return_val_if_fail (PIKA_IS_COLORMAP_SELECTION (selection), NULL);
return pika_palette_view_get_selected_entry (PIKA_PALETTE_VIEW (selection->view));
}
void
pika_colormap_selection_get_entry_rect (PikaColormapSelection *selection,
PikaPaletteEntry *entry,
GdkRectangle *rect)
{
GtkAllocation allocation;
g_return_if_fail (PIKA_IS_COLORMAP_SELECTION (selection));
g_return_if_fail (entry);
g_return_if_fail (rect);
pika_palette_view_get_entry_rect (PIKA_PALETTE_VIEW (selection->view),
entry, rect);
gtk_widget_get_allocation (GTK_WIDGET (selection), &allocation);
/* rect->x += allocation.x; */
/* rect->y += allocation.y; */
}
/* private functions */
static PangoLayout *
pika_colormap_selection_create_layout (GtkWidget *widget)
{
PangoLayout *layout;
PangoAttrList *attrs;
PangoAttribute *attr;
layout = gtk_widget_create_pango_layout (widget,
_("Only indexed images have "
"a colormap."));
pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER);
attrs = pango_attr_list_new ();
attr = pango_attr_style_new (PANGO_STYLE_ITALIC);
attr->start_index = 0;
attr->end_index = -1;
pango_attr_list_insert (attrs, attr);
pango_layout_set_attributes (layout, attrs);
pango_attr_list_unref (attrs);
return layout;
}
static gboolean
pika_colormap_selection_preview_draw (GtkWidget *widget,
cairo_t *cr,
PikaColormapSelection *selection)
{
GtkStyleContext *style = gtk_widget_get_style_context (widget);
PikaImage *image;
GtkAllocation allocation;
GdkRGBA color;
gint width, height;
gint y;
image = pika_context_get_image (selection->context);
if (image == NULL || pika_image_get_base_type (image) == PIKA_INDEXED)
return FALSE;
gtk_style_context_get_color (style, gtk_widget_get_state_flags (widget),
&color);
gdk_cairo_set_source_rgba (cr, &color);
gtk_widget_get_allocation (widget, &allocation);
if (! selection->layout)
selection->layout = pika_colormap_selection_create_layout (selection->view);
pango_layout_set_width (selection->layout,
PANGO_SCALE * (allocation.width - 2 * BORDER));
pango_layout_get_pixel_size (selection->layout, &width, &height);
y = (allocation.height - height) / 2;
cairo_move_to (cr, BORDER, MAX (y, 0));
pango_cairo_show_layout (cr, selection->layout);
return TRUE;
}
static void
pika_colormap_selection_update_entries (PikaColormapSelection *selection)
{
PikaImage *image = pika_context_get_image (selection->context);
if (! HAVE_COLORMAP (image) ||
! pika_image_get_colormap_size (image))
{
gtk_widget_set_sensitive (selection->index_spinbutton, FALSE);
gtk_widget_set_sensitive (selection->color_entry, FALSE);
gtk_adjustment_set_value (selection->index_adjustment, 0);
gtk_entry_set_text (GTK_ENTRY (selection->color_entry), "");
}
else
{
PikaRGB color;
guchar r, g, b;
gchar *string;
gtk_adjustment_set_value (selection->index_adjustment,
selection->col_index);
pika_image_get_colormap_entry (image, selection->col_index, &color);
pika_rgb_get_uchar (&color, &r, &g, &b);
string = g_strdup_printf ("%02x%02x%02x", r, g, b);
gtk_entry_set_text (GTK_ENTRY (selection->color_entry), string);
g_free (string);
gtk_widget_set_sensitive (selection->index_spinbutton, TRUE);
gtk_widget_set_sensitive (selection->color_entry, TRUE);
}
}
static void
pika_colormap_selection_entry_clicked (PikaPaletteView *view,
PikaPaletteEntry *entry,
GdkModifierType state,
PikaColormapSelection *selection)
{
PikaPalette *palette;
gint index;
palette = pika_image_get_colormap_palette (selection->active_image);
index = pika_palette_get_entry_position (palette, entry);
pika_colormap_selection_set_index (selection, index, NULL);
g_signal_emit (selection, signals[COLOR_CLICKED], 0, entry, state);
}
static void
pika_colormap_selection_entry_selected (PikaPaletteView *view,
PikaPaletteEntry *entry,
PikaColormapSelection *selection)
{
PikaPalette *palette;
gint index = 0;
palette = pika_image_get_colormap_palette (selection->active_image);
if (entry)
index = pika_palette_get_entry_position (palette, entry);
pika_colormap_selection_set_index (selection, index, NULL);
}
static void
pika_colormap_selection_entry_activated (PikaPaletteView *view,
PikaPaletteEntry *entry,
PikaColormapSelection *selection)
{
PikaPalette *palette;
gint index;
palette = pika_image_get_colormap_palette (selection->active_image);
index = pika_palette_get_entry_position (palette, entry);
pika_colormap_selection_set_index (selection, index, NULL);
g_signal_emit (selection, signals[COLOR_ACTIVATED], 0, entry);
}
static void
pika_colormap_selection_color_dropped (PikaPaletteView *view,
PikaPaletteEntry *entry,
const PikaRGB *color,
PikaColormapSelection *selection)
{
}
static void
pika_colormap_adjustment_changed (GtkAdjustment *adjustment,
PikaColormapSelection *selection)
{
PikaImage *image = pika_context_get_image (selection->context);
if (HAVE_COLORMAP (image))
{
gint index = ROUND (gtk_adjustment_get_value (adjustment));
pika_colormap_selection_set_index (selection, index, NULL);
pika_colormap_selection_update_entries (selection);
}
}
static void
pika_colormap_hex_entry_changed (PikaColorHexEntry *entry,
PikaColormapSelection *selection)
{
PikaImage *image = pika_context_get_image (selection->context);
if (image)
{
PikaRGB color;
pika_color_hex_entry_get_color (entry, &color);
pika_image_set_colormap_entry (image, selection->col_index, &color, TRUE);
pika_image_flush (image);
}
}
static void
pika_colormap_selection_set_context (PikaColormapSelection *selection,
PikaContext *context)
{
g_return_if_fail (PIKA_IS_COLORMAP_SELECTION (selection));
g_return_if_fail (context == NULL || PIKA_IS_CONTEXT (context));
if (context != selection->context)
{
if (selection->context)
{
g_signal_handlers_disconnect_by_func (selection->context,
gtk_widget_queue_draw,
selection);
g_signal_handlers_disconnect_by_func (selection->context,
G_CALLBACK (pika_colormap_selection_image_changed),
selection);
g_object_unref (selection->context);
}
selection->context = context;
if (context)
{
g_object_ref (context);
g_signal_connect_swapped (context, "foreground-changed",
G_CALLBACK (gtk_widget_queue_draw),
selection);
g_signal_connect_swapped (context, "background-changed",
G_CALLBACK (gtk_widget_queue_draw),
selection);
g_signal_connect_swapped (context, "image-changed",
G_CALLBACK (pika_colormap_selection_image_changed),
selection);
pika_colormap_selection_image_changed (selection, pika_context_get_image (context));
}
pika_view_renderer_set_context (PIKA_VIEW (selection->view)->renderer,
context);
g_object_notify (G_OBJECT (selection), "context");
}
}
static void
pika_colormap_selection_image_changed (PikaColormapSelection *selection,
PikaImage *image)
{
if (selection->active_image)
{
g_signal_handlers_disconnect_by_func (selection->active_image,
G_CALLBACK (gtk_widget_queue_draw),
selection);
g_signal_handlers_disconnect_by_func (selection->active_image,
G_CALLBACK (pika_colormap_selection_set_palette),
selection);
if (pika_image_get_base_type (selection->active_image) == PIKA_INDEXED)
{
PikaPalette *palette;
palette = pika_image_get_colormap_palette (selection->active_image);
g_signal_handlers_disconnect_by_func (palette,
G_CALLBACK (gtk_widget_queue_draw),
selection);
}
if (selection->color_dialog)
gtk_widget_hide (selection->color_dialog);
if (! HAVE_COLORMAP (image))
{
gtk_adjustment_set_upper (selection->index_adjustment, 0);
if (gtk_widget_get_mapped (GTK_WIDGET (selection)))
{
if (selection->active_palette)
g_signal_handlers_disconnect_by_func (selection->active_palette,
G_CALLBACK (gtk_widget_queue_draw),
selection);
pika_view_set_viewable (PIKA_VIEW (selection->view), NULL);
gtk_adjustment_set_upper (selection->index_adjustment, 0);
selection->active_palette = NULL;
}
}
}
g_set_weak_pointer (&selection->active_image, image);
if (image)
{
g_signal_connect_swapped (image, "colormap-changed",
G_CALLBACK (gtk_widget_queue_draw),
selection);
g_signal_connect_swapped (image, "mode-changed",
G_CALLBACK (pika_colormap_selection_set_palette),
selection);
}
pika_colormap_selection_set_palette (selection);
gtk_widget_queue_draw (GTK_WIDGET (selection));
}
static void
pika_colormap_selection_set_palette (PikaColormapSelection *selection)
{
PikaPalette *palette = NULL;
if (selection->active_image)
palette = pika_image_get_colormap_palette (selection->active_image);
if (palette != selection->active_palette)
{
if (selection->active_palette)
{
g_signal_handlers_disconnect_by_func (selection->active_palette,
G_CALLBACK (gtk_widget_queue_draw),
selection);
pika_view_set_viewable (PIKA_VIEW (selection->view), NULL);
gtk_adjustment_set_upper (selection->index_adjustment, 0);
}
g_set_weak_pointer (&selection->active_palette, palette);
if (palette)
{
g_signal_connect_swapped (palette, "dirty",
G_CALLBACK (gtk_widget_queue_draw),
selection);
pika_view_set_viewable (PIKA_VIEW (selection->view),
PIKA_VIEWABLE (palette));
gtk_adjustment_set_upper (selection->index_adjustment,
pika_image_get_colormap_size (selection->active_image) - 1);
}
}
}

View File

@ -0,0 +1,88 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __PIKA_COLORMAP_SELECTION_H__
#define __PIKA_COLORMAP_SELECTION_H__
#define PIKA_TYPE_COLORMAP_SELECTION (pika_colormap_selection_get_type ())
#define PIKA_COLORMAP_SELECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIKA_TYPE_COLORMAP_SELECTION, PikaColormapSelection))
#define PIKA_COLORMAP_SELECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIKA_TYPE_COLORMAP_SELECTION, PikaColormapSelectionClass))
#define PIKA_IS_COLORMAP_SELECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIKA_TYPE_COLORMAP_SELECTION))
#define PIKA_IS_COLORMAP_SELECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIKA_TYPE_COLORMAP_SELECTION))
#define PIKA_COLORMAP_SELECTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIKA_TYPE_COLORMAP_SELECTION, PikaColormapSelectionClass))
typedef struct _PikaColormapSelectionClass PikaColormapSelectionClass;
struct _PikaColormapSelection
{
GtkBox parent_instance;
PikaContext *context;
PikaImage *active_image;
PikaPalette *active_palette;
GtkWidget *view;
gint col_index;
PangoLayout *layout;
GtkAdjustment *index_adjustment;
GtkWidget *index_spinbutton;
GtkWidget *color_entry;
GtkWidget *color_dialog;
GtkWidget *right_vbox;
};
struct _PikaColormapSelectionClass
{
GtkBoxClass parent_class;
void (* color_clicked) (PikaColormapSelection *selection,
PikaPaletteEntry *entry,
GdkModifierType state);
void (* color_activated) (PikaColormapSelection *selection,
PikaPaletteEntry *entry);
};
GType pika_colormap_selection_get_type (void) G_GNUC_CONST;
GtkWidget * pika_colormap_selection_new (PikaContext *context);
gint pika_colormap_selection_get_index (PikaColormapSelection *selection,
const PikaRGB *search);
gboolean pika_colormap_selection_set_index (PikaColormapSelection *selection,
gint index,
PikaRGB *color);
gint pika_colormap_selection_max_index (PikaColormapSelection *selection);
PikaPaletteEntry * pika_colormap_selection_get_selected_entry (PikaColormapSelection *selection);
void pika_colormap_selection_get_entry_rect (PikaColormapSelection *selection,
PikaPaletteEntry *entry,
GdkRectangle *rect);
#endif /* __PIKA_COLORMAP_SELECTION_H__ */

View File

@ -0,0 +1,321 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gegl.h>
#include <gtk/gtk.h>
#include "libpikacolor/pikacolor.h"
#include "libpikawidgets/pikawidgets.h"
#include "widgets-types.h"
#include "config/pikacoreconfig.h"
#include "core/pika.h"
#include "core/pikacontext.h"
#include "pikaaction.h"
#include "pikaactiongroup.h"
#include "pikaactionimpl.h"
#include "pikacolordialog.h"
#include "pikacolorpanel.h"
#define RGBA_EPSILON 1e-6
enum
{
RESPONSE,
LAST_SIGNAL
};
/* local function prototypes */
static void pika_color_panel_dispose (GObject *object);
static gboolean pika_color_panel_button_press (GtkWidget *widget,
GdkEventButton *bevent);
static void pika_color_panel_clicked (GtkButton *button);
static void pika_color_panel_color_changed (PikaColorButton *button);
static GType pika_color_panel_get_action_type (PikaColorButton *button);
static void pika_color_panel_dialog_update (PikaColorDialog *dialog,
const PikaRGB *color,
PikaColorDialogState state,
PikaColorPanel *panel);
G_DEFINE_TYPE (PikaColorPanel, pika_color_panel, PIKA_TYPE_COLOR_BUTTON)
#define parent_class pika_color_panel_parent_class
static guint color_panel_signals[LAST_SIGNAL] = { 0, };
static void
pika_color_panel_class_init (PikaColorPanelClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
GtkButtonClass *button_class = GTK_BUTTON_CLASS (klass);
PikaColorButtonClass *color_button_class = PIKA_COLOR_BUTTON_CLASS (klass);
object_class->dispose = pika_color_panel_dispose;
widget_class->button_press_event = pika_color_panel_button_press;
button_class->clicked = pika_color_panel_clicked;
color_button_class->color_changed = pika_color_panel_color_changed;
color_button_class->get_action_type = pika_color_panel_get_action_type;
color_panel_signals[RESPONSE] =
g_signal_new ("response",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (PikaColorPanelClass, response),
NULL, NULL, NULL,
G_TYPE_NONE, 1,
PIKA_TYPE_COLOR_DIALOG_STATE);
}
static void
pika_color_panel_init (PikaColorPanel *panel)
{
panel->context = NULL;
panel->color_dialog = NULL;
}
static void
pika_color_panel_dispose (GObject *object)
{
PikaColorPanel *panel = PIKA_COLOR_PANEL (object);
if (panel->color_dialog)
{
gtk_widget_destroy (panel->color_dialog);
panel->color_dialog = NULL;
}
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static gboolean
pika_color_panel_button_press (GtkWidget *widget,
GdkEventButton *bevent)
{
if (gdk_event_triggers_context_menu ((GdkEvent *) bevent))
{
PikaColorButton *color_button;
PikaColorPanel *color_panel;
GSimpleActionGroup *group;
PikaAction *action;
PikaRGB color;
color_button = PIKA_COLOR_BUTTON (widget);
color_panel = PIKA_COLOR_PANEL (widget);
group = pika_color_button_get_action_group (color_button);
action = PIKA_ACTION (g_action_map_lookup_action (G_ACTION_MAP (group), "use-foreground"));
pika_action_set_visible (action, color_panel->context != NULL);
action = PIKA_ACTION (g_action_map_lookup_action (G_ACTION_MAP (group), "use-background"));
pika_action_set_visible (action, color_panel->context != NULL);
if (color_panel->context)
{
action = PIKA_ACTION (g_action_map_lookup_action (G_ACTION_MAP (group), "use-foreground"));
pika_context_get_foreground (color_panel->context, &color);
g_object_set (action, "color", &color, NULL);
action = PIKA_ACTION (g_action_map_lookup_action (G_ACTION_MAP (group), "use-background"));
pika_context_get_background (color_panel->context, &color);
g_object_set (action, "color", &color, NULL);
}
action = PIKA_ACTION (g_action_map_lookup_action (G_ACTION_MAP (group), "use-black"));
pika_rgba_set (&color, 0.0, 0.0, 0.0, PIKA_OPACITY_OPAQUE);
g_object_set (action, "color", &color, NULL);
action = PIKA_ACTION (g_action_map_lookup_action (G_ACTION_MAP (group), "use-white"));
pika_rgba_set (&color, 1.0, 1.0, 1.0, PIKA_OPACITY_OPAQUE);
g_object_set (action, "color", &color, NULL);
}
if (GTK_WIDGET_CLASS (parent_class)->button_press_event)
return GTK_WIDGET_CLASS (parent_class)->button_press_event (widget, bevent);
return FALSE;
}
static void
pika_color_panel_clicked (GtkButton *button)
{
PikaColorPanel *panel = PIKA_COLOR_PANEL (button);
PikaRGB color;
pika_color_button_get_color (PIKA_COLOR_BUTTON (button), &color);
if (! panel->color_dialog)
{
PikaColorButton *color_button = PIKA_COLOR_BUTTON (button);
panel->color_dialog =
pika_color_dialog_new (NULL, panel->context, TRUE,
pika_color_button_get_title (color_button),
NULL, NULL,
GTK_WIDGET (button),
NULL, NULL,
&color,
pika_color_button_get_update (color_button),
pika_color_button_has_alpha (color_button));
g_signal_connect (panel->color_dialog, "destroy",
G_CALLBACK (gtk_widget_destroyed),
&panel->color_dialog);
g_signal_connect (panel->color_dialog, "update",
G_CALLBACK (pika_color_panel_dialog_update),
panel);
}
else
{
pika_color_dialog_set_color (PIKA_COLOR_DIALOG (panel->color_dialog),
&color);
}
gtk_window_present (GTK_WINDOW (panel->color_dialog));
}
static GType
pika_color_panel_get_action_type (PikaColorButton *button)
{
return PIKA_TYPE_ACTION_IMPL;
}
/* public functions */
GtkWidget *
pika_color_panel_new (const gchar *title,
const PikaRGB *color,
PikaColorAreaType type,
gint width,
gint height)
{
g_return_val_if_fail (title != NULL, NULL);
g_return_val_if_fail (color != NULL, NULL);
g_return_val_if_fail (width > 0, NULL);
g_return_val_if_fail (height > 0, NULL);
return g_object_new (PIKA_TYPE_COLOR_PANEL,
"title", title,
"type", type,
"color", color,
"area-width", width,
"area-height", height,
NULL);
}
static void
pika_color_panel_color_changed (PikaColorButton *button)
{
PikaColorPanel *panel = PIKA_COLOR_PANEL (button);
PikaRGB color;
if (panel->color_dialog)
{
PikaRGB dialog_color;
pika_color_button_get_color (PIKA_COLOR_BUTTON (button), &color);
pika_color_dialog_get_color (PIKA_COLOR_DIALOG (panel->color_dialog),
&dialog_color);
if (pika_rgba_distance (&color, &dialog_color) > RGBA_EPSILON ||
color.a != dialog_color.a)
{
pika_color_dialog_set_color (PIKA_COLOR_DIALOG (panel->color_dialog),
&color);
}
}
}
void
pika_color_panel_set_context (PikaColorPanel *panel,
PikaContext *context)
{
g_return_if_fail (PIKA_IS_COLOR_PANEL (panel));
g_return_if_fail (context == NULL || PIKA_IS_CONTEXT (context));
panel->context = context;
if (context)
pika_color_button_set_color_config (PIKA_COLOR_BUTTON (panel),
context->pika->config->color_management);
}
void
pika_color_panel_dialog_response (PikaColorPanel *panel,
PikaColorDialogState state)
{
g_return_if_fail (PIKA_IS_COLOR_PANEL (panel));
g_return_if_fail (state == PIKA_COLOR_DIALOG_OK ||
state == PIKA_COLOR_DIALOG_CANCEL);
if (panel->color_dialog && gtk_widget_get_visible (panel->color_dialog))
pika_color_panel_dialog_update (NULL, NULL, state, panel);
}
/* private functions */
static void
pika_color_panel_dialog_update (PikaColorDialog *dialog,
const PikaRGB *color,
PikaColorDialogState state,
PikaColorPanel *panel)
{
switch (state)
{
case PIKA_COLOR_DIALOG_UPDATE:
if (pika_color_button_get_update (PIKA_COLOR_BUTTON (panel)))
pika_color_button_set_color (PIKA_COLOR_BUTTON (panel), color);
break;
case PIKA_COLOR_DIALOG_OK:
case PIKA_COLOR_DIALOG_CANCEL:
/* PikaColorDialog returns the appropriate color (new one or
* original one if process cancelled.
*/
pika_color_button_set_color (PIKA_COLOR_BUTTON (panel), color);
gtk_widget_hide (panel->color_dialog);
g_signal_emit (panel, color_panel_signals[RESPONSE], 0,
state);
break;
}
}

View File

@ -0,0 +1,69 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __PIKA_COLOR_PANEL_H__
#define __PIKA_COLOR_PANEL_H__
#define PIKA_TYPE_COLOR_PANEL (pika_color_panel_get_type ())
#define PIKA_COLOR_PANEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIKA_TYPE_COLOR_PANEL, PikaColorPanel))
#define PIKA_COLOR_PANEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIKA_TYPE_COLOR_PANEL, PikaColorPanelClass))
#define PIKA_IS_COLOR_PANEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIKA_TYPE_COLOR_PANEL))
#define PIKA_IS_COLOR_PANEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIKA_TYPE_COLOR_PANEL))
#define PIKA_COLOR_PANEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIKA_TYPE_COLOR_PANEL, PikaColorPanelClass))
typedef struct _PikaColorPanelClass PikaColorPanelClass;
struct _PikaColorPanel
{
PikaColorButton parent_instance;
PikaContext *context;
GtkWidget *color_dialog;
};
struct _PikaColorPanelClass
{
PikaColorButtonClass parent_class;
/* signals */
void (* response) (PikaColorPanel *panel,
PikaColorDialogState state);
};
GType pika_color_panel_get_type (void) G_GNUC_CONST;
GtkWidget * pika_color_panel_new (const gchar *title,
const PikaRGB *color,
PikaColorAreaType type,
gint width,
gint height);
void pika_color_panel_set_context (PikaColorPanel *panel,
PikaContext *context);
void pika_color_panel_dialog_response (PikaColorPanel *panel,
PikaColorDialogState state);
#endif /* __PIKA_COLOR_PANEL_H__ */

View File

@ -0,0 +1,183 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* pikacolorselectorpalette.c
* Copyright (C) 2006 Michael Natterer <mitch@gimp.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gegl.h>
#include <gtk/gtk.h>
#include "libpikacolor/pikacolor.h"
#include "libpikawidgets/pikawidgets.h"
#include "widgets-types.h"
#include "core/pikacontext.h"
#include "core/pikapalette.h"
#include "pikacolorselectorpalette.h"
#include "pikahelp-ids.h"
#include "pikapaletteview.h"
#include "pikaviewrendererpalette.h"
#include "pika-intl.h"
static void pika_color_selector_palette_set_color (PikaColorSelector *selector,
const PikaRGB *rgb,
const PikaHSV *hsv);
static void pika_color_selector_palette_set_config (PikaColorSelector *selector,
PikaColorConfig *config);
G_DEFINE_TYPE (PikaColorSelectorPalette, pika_color_selector_palette,
PIKA_TYPE_COLOR_SELECTOR)
#define parent_class pika_color_selector_palette_parent_class
static void
pika_color_selector_palette_class_init (PikaColorSelectorPaletteClass *klass)
{
PikaColorSelectorClass *selector_class = PIKA_COLOR_SELECTOR_CLASS (klass);
selector_class->name = _("Palette");
selector_class->help_id = PIKA_HELP_COLORSELECTOR_PALETTE;
selector_class->icon_name = PIKA_ICON_PALETTE;
selector_class->set_color = pika_color_selector_palette_set_color;
selector_class->set_config = pika_color_selector_palette_set_config;
gtk_widget_class_set_css_name (GTK_WIDGET_CLASS (klass),
"PikaColorSelectorPalette");
}
static void
pika_color_selector_palette_init (PikaColorSelectorPalette *select)
{
}
static void
pika_color_selector_palette_set_color (PikaColorSelector *selector,
const PikaRGB *rgb,
const PikaHSV *hsv)
{
PikaColorSelectorPalette *select = PIKA_COLOR_SELECTOR_PALETTE (selector);
if (select->context)
{
PikaPalette *palette = pika_context_get_palette (select->context);
if (palette && pika_palette_get_n_colors (palette) > 0)
{
PikaPaletteEntry *entry;
entry = pika_palette_find_entry (palette, rgb,
PIKA_PALETTE_VIEW (select->view)->selected);
if (entry)
pika_palette_view_select_entry (PIKA_PALETTE_VIEW (select->view),
entry);
}
}
}
static void
pika_color_selector_palette_palette_changed (PikaContext *context,
PikaPalette *palette,
PikaColorSelectorPalette *select)
{
pika_view_set_viewable (PIKA_VIEW (select->view), PIKA_VIEWABLE (palette));
}
static void
pika_color_selector_palette_entry_clicked (PikaPaletteView *view,
PikaPaletteEntry *entry,
GdkModifierType state,
PikaColorSelector *selector)
{
selector->rgb = entry->color;
pika_rgb_to_hsv (&selector->rgb, &selector->hsv);
pika_color_selector_emit_color_changed (selector);
}
static void
pika_color_selector_palette_set_config (PikaColorSelector *selector,
PikaColorConfig *config)
{
PikaColorSelectorPalette *select = PIKA_COLOR_SELECTOR_PALETTE (selector);
if (select->context)
{
g_signal_handlers_disconnect_by_func (select->context,
pika_color_selector_palette_palette_changed,
select);
pika_view_renderer_set_context (PIKA_VIEW (select->view)->renderer,
NULL);
g_clear_object (&select->context);
}
if (config)
select->context = g_object_get_data (G_OBJECT (config), "pika-context");
if (select->context)
{
g_object_ref (select->context);
if (! select->view)
{
select->view = pika_view_new_full_by_types (select->context,
PIKA_TYPE_PALETTE_VIEW,
PIKA_TYPE_PALETTE,
100, 100, 0,
FALSE, TRUE, FALSE);
pika_view_set_expand (PIKA_VIEW (select->view), TRUE);
pika_view_renderer_palette_set_cell_size
(PIKA_VIEW_RENDERER_PALETTE (PIKA_VIEW (select->view)->renderer),
-1);
pika_view_renderer_palette_set_draw_grid
(PIKA_VIEW_RENDERER_PALETTE (PIKA_VIEW (select->view)->renderer),
TRUE);
gtk_box_pack_start (GTK_BOX (select), select->view, TRUE, TRUE, 0);
gtk_widget_show (select->view);
g_signal_connect (select->view, "entry-clicked",
G_CALLBACK (pika_color_selector_palette_entry_clicked),
select);
}
else
{
pika_view_renderer_set_context (PIKA_VIEW (select->view)->renderer,
select->context);
}
g_signal_connect_object (select->context, "palette-changed",
G_CALLBACK (pika_color_selector_palette_palette_changed),
select, 0);
pika_color_selector_palette_palette_changed (select->context,
pika_context_get_palette (select->context),
select);
}
}

View File

@ -0,0 +1,57 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* pikacolorselectorpalette.h
* Copyright (C) 2006 Michael Natterer <mitch@gimp.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __PIKA_COLOR_SELECTOR_PALETTE_H__
#define __PIKA_COLOR_SELECTOR_PALETTE_H__
#define PIKA_TYPE_COLOR_SELECTOR_PALETTE (pika_color_selector_palette_get_type ())
#define PIKA_COLOR_SELECTOR_PALETTE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIKA_TYPE_COLOR_SELECTOR_PALETTE, PikaColorSelectorPalette))
#define PIKA_IS_COLOR_SELECTOR_PALETTE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIKA_TYPE_COLOR_SELECTOR_PALETTE))
#define PIKA_COLOR_SELECTOR_PALETTE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIKA_TYPE_COLOR_SELECTOR_PALETTE, PikaColorSelectorPaletteClass))
#define PIKA_IS_COLOR_SELECTOR_PALETTE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIKA_TYPE_COLOR_SELECTOR_PALETTE))
#define PIKA_COLOR_SELECTOR_PALETTE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIKA_TYPE_COLOR_SELECTOR_PALETTE, PikaColorSelectorPaletteClass))
typedef struct _PikaColorSelectorPalette PikaColorSelectorPalette;
typedef struct _PikaColorSelectorPaletteClass PikaColorSelectorPaletteClass;
struct _PikaColorSelectorPalette
{
PikaColorSelector parent_instance;
PikaContext *context;
GtkWidget *view;
};
struct _PikaColorSelectorPaletteClass
{
PikaColorSelectorClass parent_class;
};
GType pika_color_selector_palette_get_type (void) G_GNUC_CONST;
#endif /* __PIKA_COLOR_SELECTOR_PALETTE_H__ */

View File

@ -0,0 +1,179 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikacombotagentry.c
* Copyright (C) 2008 Aurimas Juška <aurisj@svn.gnome.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <string.h>
#include <gegl.h>
#include <gtk/gtk.h>
#include "libpikawidgets/pikawidgets.h"
#include "widgets-types.h"
#include "core/pikacontainer.h"
#include "core/pikacontext.h"
#include "core/pikatag.h"
#include "core/pikatagged.h"
#include "core/pikataggedcontainer.h"
#include "core/pikaviewable.h"
#include "pikatagentry.h"
#include "pikatagpopup.h"
#include "pikacombotagentry.h"
static void pika_combo_tag_entry_constructed (GObject *object);
static void pika_combo_tag_entry_icon_press (GtkWidget *widget,
GtkEntryIconPosition icon_pos,
GdkEvent *event,
gpointer user_data);
static void pika_combo_tag_entry_popup_destroy (GtkWidget *widget,
PikaComboTagEntry *entry);
static void pika_combo_tag_entry_tag_count_changed (PikaTaggedContainer *container,
gint tag_count,
PikaComboTagEntry *entry);
G_DEFINE_TYPE (PikaComboTagEntry, pika_combo_tag_entry, PIKA_TYPE_TAG_ENTRY);
#define parent_class pika_combo_tag_entry_parent_class
static void
pika_combo_tag_entry_class_init (PikaComboTagEntryClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->constructed = pika_combo_tag_entry_constructed;
}
static void
pika_combo_tag_entry_init (PikaComboTagEntry *entry)
{
entry->popup = NULL;
gtk_widget_add_events (GTK_WIDGET (entry),
GDK_BUTTON_PRESS_MASK |
GDK_POINTER_MOTION_MASK);
gtk_entry_set_icon_from_icon_name (GTK_ENTRY (entry),
GTK_ENTRY_ICON_SECONDARY,
PIKA_ICON_GO_DOWN);
g_signal_connect (entry, "icon-press",
G_CALLBACK (pika_combo_tag_entry_icon_press),
NULL);
}
static void
pika_combo_tag_entry_constructed (GObject *object)
{
PikaComboTagEntry *entry = PIKA_COMBO_TAG_ENTRY (object);
G_OBJECT_CLASS (parent_class)->constructed (object);
g_signal_connect_object (PIKA_TAG_ENTRY (entry)->container,
"tag-count-changed",
G_CALLBACK (pika_combo_tag_entry_tag_count_changed),
entry, 0);
}
/**
* pika_combo_tag_entry_new:
* @container: a tagged container to be used.
* @mode: tag entry mode to work in.
*
* Creates a new #PikaComboTagEntry widget which extends #PikaTagEntry by
* adding ability to pick tags using popup window (similar to combo box).
*
* Returns: a new #PikaComboTagEntry widget.
**/
GtkWidget *
pika_combo_tag_entry_new (PikaTaggedContainer *container,
PikaTagEntryMode mode)
{
g_return_val_if_fail (PIKA_IS_TAGGED_CONTAINER (container), NULL);
return g_object_new (PIKA_TYPE_COMBO_TAG_ENTRY,
"container", container,
"mode", mode,
NULL);
}
static void
pika_combo_tag_entry_icon_press (GtkWidget *widget,
GtkEntryIconPosition icon_pos,
GdkEvent *event,
gpointer user_data)
{
PikaComboTagEntry *entry = PIKA_COMBO_TAG_ENTRY (widget);
if (! entry->popup)
{
PikaTaggedContainer *container = PIKA_TAG_ENTRY (entry)->container;
gint tag_count;
tag_count = pika_tagged_container_get_tag_count (container);
if (tag_count > 0 && ! PIKA_TAG_ENTRY (entry)->has_invalid_tags)
{
entry->popup = pika_tag_popup_new (entry);
g_signal_connect (entry->popup, "destroy",
G_CALLBACK (pika_combo_tag_entry_popup_destroy),
entry);
pika_tag_popup_show (PIKA_TAG_POPUP (entry->popup), event);
}
}
else
{
gtk_widget_destroy (entry->popup);
}
}
static void
pika_combo_tag_entry_popup_destroy (GtkWidget *widget,
PikaComboTagEntry *entry)
{
entry->popup = NULL;
gtk_widget_grab_focus (GTK_WIDGET (entry));
}
static void
pika_combo_tag_entry_tag_count_changed (PikaTaggedContainer *container,
gint tag_count,
PikaComboTagEntry *entry)
{
gboolean sensitive;
sensitive = tag_count > 0 && ! PIKA_TAG_ENTRY (entry)->has_invalid_tags;
gtk_entry_set_icon_sensitive (GTK_ENTRY (entry),
GTK_ENTRY_ICON_SECONDARY,
sensitive);
}

View File

@ -0,0 +1,59 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikacombotagentry.h
* Copyright (C) 2008 Aurimas Juška <aurisj@svn.gnome.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __PIKA_COMBO_TAG_ENTRY_H__
#define __PIKA_COMBO_TAG_ENTRY_H__
#include "pikatagentry.h"
#define PIKA_TYPE_COMBO_TAG_ENTRY (pika_combo_tag_entry_get_type ())
#define PIKA_COMBO_TAG_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIKA_TYPE_COMBO_TAG_ENTRY, PikaComboTagEntry))
#define PIKA_COMBO_TAG_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIKA_TYPE_COMBO_TAG_ENTRY, PikaComboTagEntryClass))
#define PIKA_IS_COMBO_TAG_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIKA_TYPE_COMBO_TAG_ENTRY))
#define PIKA_IS_COMBO_TAG_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIKA_TYPE_COMBO_TAG_ENTRY))
#define PIKA_COMBO_TAG_ENTRY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIKA_TYPE_COMBO_TAG_ENTRY, PikaComboTagEntryClass))
typedef struct _PikaComboTagEntryClass PikaComboTagEntryClass;
struct _PikaComboTagEntry
{
PikaTagEntry parent_instance;
GtkWidget *popup;
};
struct _PikaComboTagEntryClass
{
PikaTagEntryClass parent_class;
};
GType pika_combo_tag_entry_get_type (void) G_GNUC_CONST;
GtkWidget * pika_combo_tag_entry_new (PikaTaggedContainer *container,
PikaTagEntryMode mode);
#endif /* __PIKA_COMBO_TAG_ENTRY_H__ */

View File

@ -0,0 +1,634 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikacomponenteditor.c
* Copyright (C) 2003-2005 Michael Natterer <mitch@gimp.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gegl.h>
#include <gtk/gtk.h>
#include "libpikabase/pikabase.h"
#include "libpikawidgets/pikawidgets.h"
#include "widgets-types.h"
#include "core/pikachannel.h"
#include "core/pikaimage.h"
#include "pikacellrendererviewable.h"
#include "pikacomponenteditor.h"
#include "pikadnd.h"
#include "pikadocked.h"
#include "pikamenufactory.h"
#include "pikaviewrendererimage.h"
#include "pikawidgets-utils.h"
#include "pika-intl.h"
enum
{
COLUMN_CHANNEL,
COLUMN_VISIBLE,
COLUMN_RENDERER,
COLUMN_NAME,
N_COLUMNS
};
static void pika_component_editor_docked_iface_init (PikaDockedInterface *iface);
static void pika_component_editor_set_context (PikaDocked *docked,
PikaContext *context);
static void pika_component_editor_set_image (PikaImageEditor *editor,
PikaImage *image);
static void pika_component_editor_create_components (PikaComponentEditor *editor);
static void pika_component_editor_clear_components (PikaComponentEditor *editor);
static void pika_component_editor_clicked (GtkCellRendererToggle *cellrenderertoggle,
gchar *path,
GdkModifierType state,
PikaComponentEditor *editor);
static gboolean pika_component_editor_select (GtkTreeSelection *selection,
GtkTreeModel *model,
GtkTreePath *path,
gboolean path_currently_selected,
gpointer data);
static gboolean pika_component_editor_button_press (GtkWidget *widget,
GdkEventButton *bevent,
PikaComponentEditor *editor);
static void pika_component_editor_renderer_update (PikaViewRenderer *renderer,
PikaComponentEditor *editor);
static void pika_component_editor_mode_changed (PikaImage *image,
PikaComponentEditor *editor);
static void pika_component_editor_alpha_changed (PikaImage *image,
PikaComponentEditor *editor);
static void pika_component_editor_visibility_changed(PikaImage *image,
PikaChannelType channel,
PikaComponentEditor *editor);
static void pika_component_editor_active_changed (PikaImage *image,
PikaChannelType channel,
PikaComponentEditor *editor);
static PikaImage * pika_component_editor_drag_component (GtkWidget *widget,
PikaContext **context,
PikaChannelType *channel,
gpointer data);
G_DEFINE_TYPE_WITH_CODE (PikaComponentEditor, pika_component_editor,
PIKA_TYPE_IMAGE_EDITOR,
G_IMPLEMENT_INTERFACE (PIKA_TYPE_DOCKED,
pika_component_editor_docked_iface_init))
#define parent_class pika_component_editor_parent_class
static PikaDockedInterface *parent_docked_iface = NULL;
static void
pika_component_editor_class_init (PikaComponentEditorClass *klass)
{
PikaImageEditorClass *image_editor_class = PIKA_IMAGE_EDITOR_CLASS (klass);
image_editor_class->set_image = pika_component_editor_set_image;
}
static void
pika_component_editor_init (PikaComponentEditor *editor)
{
GtkWidget *frame;
GtkListStore *list;
frame = gtk_frame_new (NULL);
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
gtk_box_pack_start (GTK_BOX (editor), frame, FALSE, FALSE, 0);
gtk_widget_show (frame);
list = gtk_list_store_new (N_COLUMNS,
G_TYPE_INT,
G_TYPE_BOOLEAN,
PIKA_TYPE_VIEW_RENDERER,
G_TYPE_STRING);
editor->model = GTK_TREE_MODEL (list);
editor->view = GTK_TREE_VIEW (gtk_tree_view_new_with_model (editor->model));
g_object_unref (list);
gtk_tree_view_set_headers_visible (editor->view, FALSE);
editor->eye_column = gtk_tree_view_column_new ();
gtk_tree_view_append_column (editor->view, editor->eye_column);
editor->eye_cell = pika_cell_renderer_toggle_new (PIKA_ICON_VISIBLE);
gtk_tree_view_column_pack_start (editor->eye_column, editor->eye_cell,
FALSE);
gtk_tree_view_column_set_attributes (editor->eye_column, editor->eye_cell,
"active", COLUMN_VISIBLE,
NULL);
g_signal_connect (editor->eye_cell, "clicked",
G_CALLBACK (pika_component_editor_clicked),
editor);
editor->renderer_cell = pika_cell_renderer_viewable_new ();
gtk_tree_view_insert_column_with_attributes (editor->view,
-1, NULL,
editor->renderer_cell,
"renderer", COLUMN_RENDERER,
NULL);
gtk_tree_view_insert_column_with_attributes (editor->view,
-1, NULL,
gtk_cell_renderer_text_new (),
"text", COLUMN_NAME,
NULL);
gtk_container_add (GTK_CONTAINER (frame), GTK_WIDGET (editor->view));
gtk_widget_show (GTK_WIDGET (editor->view));
g_signal_connect (editor->view, "button-press-event",
G_CALLBACK (pika_component_editor_button_press),
editor);
editor->selection = gtk_tree_view_get_selection (editor->view);
gtk_tree_selection_set_mode (editor->selection, GTK_SELECTION_MULTIPLE);
gtk_tree_selection_set_select_function (editor->selection,
pika_component_editor_select,
editor, NULL);
pika_dnd_component_source_add (GTK_WIDGET (editor->view),
pika_component_editor_drag_component,
editor);
}
static void
pika_component_editor_docked_iface_init (PikaDockedInterface *iface)
{
parent_docked_iface = g_type_interface_peek_parent (iface);
if (! parent_docked_iface)
parent_docked_iface = g_type_default_interface_peek (PIKA_TYPE_DOCKED);
iface->set_context = pika_component_editor_set_context;
}
static void
pika_component_editor_set_context (PikaDocked *docked,
PikaContext *context)
{
PikaComponentEditor *editor = PIKA_COMPONENT_EDITOR (docked);
GtkTreeIter iter;
gboolean iter_valid;
parent_docked_iface->set_context (docked, context);
for (iter_valid = gtk_tree_model_get_iter_first (editor->model, &iter);
iter_valid;
iter_valid = gtk_tree_model_iter_next (editor->model, &iter))
{
PikaViewRenderer *renderer;
gtk_tree_model_get (editor->model, &iter,
COLUMN_RENDERER, &renderer,
-1);
pika_view_renderer_set_context (renderer, context);
g_object_unref (renderer);
}
}
static void
pika_component_editor_set_image (PikaImageEditor *editor,
PikaImage *image)
{
PikaComponentEditor *component_editor = PIKA_COMPONENT_EDITOR (editor);
if (editor->image)
{
pika_component_editor_clear_components (component_editor);
g_signal_handlers_disconnect_by_func (editor->image,
pika_component_editor_mode_changed,
component_editor);
g_signal_handlers_disconnect_by_func (editor->image,
pika_component_editor_alpha_changed,
component_editor);
g_signal_handlers_disconnect_by_func (editor->image,
pika_component_editor_visibility_changed,
component_editor);
g_signal_handlers_disconnect_by_func (editor->image,
pika_component_editor_active_changed,
component_editor);
}
PIKA_IMAGE_EDITOR_CLASS (parent_class)->set_image (editor, image);
if (editor->image)
{
pika_component_editor_create_components (component_editor);
g_signal_connect (editor->image, "mode-changed",
G_CALLBACK (pika_component_editor_mode_changed),
component_editor);
g_signal_connect (editor->image, "alpha-changed",
G_CALLBACK (pika_component_editor_alpha_changed),
component_editor);
g_signal_connect (editor->image, "component-visibility-changed",
G_CALLBACK (pika_component_editor_visibility_changed),
component_editor);
g_signal_connect (editor->image, "component-active-changed",
G_CALLBACK (pika_component_editor_active_changed),
component_editor);
}
}
GtkWidget *
pika_component_editor_new (gint view_size,
PikaMenuFactory *menu_factory)
{
PikaComponentEditor *editor;
g_return_val_if_fail (view_size > 0 &&
view_size <= PIKA_VIEWABLE_MAX_PREVIEW_SIZE, NULL);
g_return_val_if_fail (PIKA_IS_MENU_FACTORY (menu_factory), NULL);
editor = g_object_new (PIKA_TYPE_COMPONENT_EDITOR,
"menu-factory", menu_factory,
"menu-identifier", "<Channels>",
"ui-path", "/channels-popup",
NULL);
pika_component_editor_set_view_size (editor, view_size);
return GTK_WIDGET (editor);
}
void
pika_component_editor_set_view_size (PikaComponentEditor *editor,
gint view_size)
{
GtkWidget *tree_widget;
GtkStyleContext *tree_style;
GtkBorder border;
GtkTreeIter iter;
gboolean iter_valid;
gint icon_size;
g_return_if_fail (PIKA_IS_COMPONENT_EDITOR (editor));
g_return_if_fail (view_size > 0 &&
view_size <= PIKA_VIEWABLE_MAX_PREVIEW_SIZE);
tree_widget = GTK_WIDGET (editor->view);
tree_style = gtk_widget_get_style_context (tree_widget);
gtk_style_context_save (tree_style);
gtk_style_context_add_class (tree_style, GTK_STYLE_CLASS_BUTTON);
gtk_style_context_get_border (tree_style, 0, &border);
gtk_style_context_restore (tree_style);
g_object_get (editor->eye_cell, "icon-size", &icon_size, NULL);
icon_size = MIN (icon_size, MAX (view_size - (border.left + border.right),
view_size - (border.top + border.bottom)));
g_object_set (editor->eye_cell, "icon-size", icon_size, NULL);
for (iter_valid = gtk_tree_model_get_iter_first (editor->model, &iter);
iter_valid;
iter_valid = gtk_tree_model_iter_next (editor->model, &iter))
{
PikaViewRenderer *renderer;
gtk_tree_model_get (editor->model, &iter,
COLUMN_RENDERER, &renderer,
-1);
pika_view_renderer_set_size (renderer, view_size, 1);
g_object_unref (renderer);
}
editor->view_size = view_size;
gtk_tree_view_columns_autosize (editor->view);
}
static void
pika_component_editor_create_components (PikaComponentEditor *editor)
{
PikaImage *image = PIKA_IMAGE_EDITOR (editor)->image;
gint n_components = 0;
PikaChannelType components[MAX_CHANNELS];
GEnumClass *enum_class;
gint i;
switch (pika_image_get_base_type (image))
{
case PIKA_RGB:
n_components = 3;
components[0] = PIKA_CHANNEL_RED;
components[1] = PIKA_CHANNEL_GREEN;
components[2] = PIKA_CHANNEL_BLUE;
break;
case PIKA_GRAY:
n_components = 1;
components[0] = PIKA_CHANNEL_GRAY;
break;
case PIKA_INDEXED:
n_components = 1;
components[0] = PIKA_CHANNEL_INDEXED;
break;
}
if (pika_image_has_alpha (image))
components[n_components++] = PIKA_CHANNEL_ALPHA;
enum_class = g_type_class_ref (PIKA_TYPE_CHANNEL_TYPE);
for (i = 0; i < n_components; i++)
{
PikaViewRenderer *renderer;
GtkTreeIter iter;
GEnumValue *enum_value;
const gchar *desc;
gboolean visible;
visible = pika_image_get_component_visible (image, components[i]);
renderer = pika_view_renderer_new (PIKA_IMAGE_EDITOR (editor)->context,
G_TYPE_FROM_INSTANCE (image),
editor->view_size, 1, FALSE);
pika_view_renderer_set_viewable (renderer, PIKA_VIEWABLE (image));
pika_view_renderer_remove_idle (renderer);
PIKA_VIEW_RENDERER_IMAGE (renderer)->channel = components[i];
g_signal_connect (renderer, "update",
G_CALLBACK (pika_component_editor_renderer_update),
editor);
enum_value = g_enum_get_value (enum_class, components[i]);
desc = pika_enum_value_get_desc (enum_class, enum_value);
gtk_list_store_append (GTK_LIST_STORE (editor->model), &iter);
gtk_list_store_set (GTK_LIST_STORE (editor->model), &iter,
COLUMN_CHANNEL, components[i],
COLUMN_VISIBLE, visible,
COLUMN_RENDERER, renderer,
COLUMN_NAME, desc,
-1);
g_object_unref (renderer);
if (pika_image_get_component_active (image, components[i]))
gtk_tree_selection_select_iter (editor->selection, &iter);
}
g_type_class_unref (enum_class);
}
static void
pika_component_editor_clear_components (PikaComponentEditor *editor)
{
gtk_list_store_clear (GTK_LIST_STORE (editor->model));
/* Clear the renderer so that it don't reference the viewable.
* See bug #149906.
*/
g_object_set (editor->renderer_cell, "renderer", NULL, NULL);
}
static void
pika_component_editor_clicked (GtkCellRendererToggle *cellrenderertoggle,
gchar *path_str,
GdkModifierType state,
PikaComponentEditor *editor)
{
GtkTreePath *path;
GtkTreeIter iter;
path = gtk_tree_path_new_from_string (path_str);
if (gtk_tree_model_get_iter (editor->model, &iter, path))
{
PikaImage *image = PIKA_IMAGE_EDITOR (editor)->image;
PikaChannelType channel;
gboolean active;
gtk_tree_model_get (editor->model, &iter,
COLUMN_CHANNEL, &channel,
-1);
g_object_get (cellrenderertoggle,
"active", &active,
NULL);
pika_image_set_component_visible (image, channel, !active);
pika_image_flush (image);
}
gtk_tree_path_free (path);
}
static gboolean
pika_component_editor_select (GtkTreeSelection *selection,
GtkTreeModel *model,
GtkTreePath *path,
gboolean path_currently_selected,
gpointer data)
{
PikaComponentEditor *editor = PIKA_COMPONENT_EDITOR (data);
GtkTreeIter iter;
PikaChannelType channel;
gboolean active;
gtk_tree_model_get_iter (editor->model, &iter, path);
gtk_tree_model_get (editor->model, &iter,
COLUMN_CHANNEL, &channel,
-1);
active = pika_image_get_component_active (PIKA_IMAGE_EDITOR (editor)->image,
channel);
return active != path_currently_selected;
}
static gboolean
pika_component_editor_button_press (GtkWidget *widget,
GdkEventButton *bevent,
PikaComponentEditor *editor)
{
GtkTreeViewColumn *column;
GtkTreePath *path;
editor->clicked_component = -1;
if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget),
bevent->x,
bevent->y,
&path, &column, NULL, NULL))
{
GtkTreeIter iter;
PikaChannelType channel;
gboolean active;
active = gtk_tree_selection_path_is_selected (editor->selection, path);
gtk_tree_model_get_iter (editor->model, &iter, path);
gtk_tree_path_free (path);
gtk_tree_model_get (editor->model, &iter,
COLUMN_CHANNEL, &channel,
-1);
editor->clicked_component = channel;
if (gdk_event_triggers_context_menu ((GdkEvent *) bevent))
{
pika_editor_popup_menu_at_pointer (PIKA_EDITOR (editor), (GdkEvent *) bevent);
}
else if (bevent->type == GDK_BUTTON_PRESS && bevent->button == 1 &&
column != editor->eye_column)
{
PikaImage *image = PIKA_IMAGE_EDITOR (editor)->image;
pika_image_set_component_active (image, channel, ! active);
pika_image_flush (image);
}
}
return FALSE;
}
static gboolean
pika_component_editor_get_iter (PikaComponentEditor *editor,
PikaChannelType channel,
GtkTreeIter *iter)
{
gint index;
index = pika_image_get_component_index (PIKA_IMAGE_EDITOR (editor)->image,
channel);
if (index != -1)
return gtk_tree_model_iter_nth_child (editor->model, iter, NULL, index);
return FALSE;
}
static void
pika_component_editor_renderer_update (PikaViewRenderer *renderer,
PikaComponentEditor *editor)
{
PikaChannelType channel = PIKA_VIEW_RENDERER_IMAGE (renderer)->channel;
GtkTreeIter iter;
if (pika_component_editor_get_iter (editor, channel, &iter))
{
GtkTreePath *path;
path = gtk_tree_model_get_path (editor->model, &iter);
gtk_tree_model_row_changed (editor->model, path, &iter);
gtk_tree_path_free (path);
}
}
static void
pika_component_editor_mode_changed (PikaImage *image,
PikaComponentEditor *editor)
{
pika_component_editor_clear_components (editor);
pika_component_editor_create_components (editor);
}
static void
pika_component_editor_alpha_changed (PikaImage *image,
PikaComponentEditor *editor)
{
pika_component_editor_clear_components (editor);
pika_component_editor_create_components (editor);
}
static void
pika_component_editor_visibility_changed (PikaImage *image,
PikaChannelType channel,
PikaComponentEditor *editor)
{
GtkTreeIter iter;
if (pika_component_editor_get_iter (editor, channel, &iter))
{
gboolean visible = pika_image_get_component_visible (image, channel);
gtk_list_store_set (GTK_LIST_STORE (editor->model), &iter,
COLUMN_VISIBLE, visible,
-1);
}
}
static void
pika_component_editor_active_changed (PikaImage *image,
PikaChannelType channel,
PikaComponentEditor *editor)
{
GtkTreeIter iter;
if (pika_component_editor_get_iter (editor, channel, &iter))
{
gboolean active = pika_image_get_component_active (image, channel);
if (gtk_tree_selection_iter_is_selected (editor->selection, &iter) !=
active)
{
if (active)
gtk_tree_selection_select_iter (editor->selection, &iter);
else
gtk_tree_selection_unselect_iter (editor->selection, &iter);
}
}
}
static PikaImage *
pika_component_editor_drag_component (GtkWidget *widget,
PikaContext **context,
PikaChannelType *channel,
gpointer data)
{
PikaComponentEditor *editor = PIKA_COMPONENT_EDITOR (data);
if (PIKA_IMAGE_EDITOR (editor)->image &&
editor->clicked_component != -1)
{
if (channel)
*channel = editor->clicked_component;
if (context)
*context = PIKA_IMAGE_EDITOR (editor)->context;
return PIKA_IMAGE_EDITOR (editor)->image;
}
return NULL;
}

View File

@ -0,0 +1,73 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikacomponenteditor.h
* Copyright (C) 2003 Michael Natterer <mitch@gimp.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __PIKA_COMPONENT_EDITOR_H__
#define __PIKA_COMPONENT_EDITOR_H__
#include "pikaimageeditor.h"
#define PIKA_TYPE_COMPONENT_EDITOR (pika_component_editor_get_type ())
#define PIKA_COMPONENT_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIKA_TYPE_COMPONENT_EDITOR, PikaComponentEditor))
#define PIKA_COMPONENT_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIKA_TYPE_COMPONENT_EDITOR, PikaComponentEditorClass))
#define PIKA_IS_COMPONENT_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIKA_TYPE_COMPONENT_EDITOR))
#define PIKA_IS_COMPONENT_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIKA_TYPE_COMPONENT_EDITOR))
#define PIKA_COMPONENT_EDITOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIKA_TYPE_COMPONENT_EDITOR, PikaComponentEditorClass))
typedef struct _PikaComponentEditorClass PikaComponentEditorClass;
struct _PikaComponentEditor
{
PikaImageEditor parent_instance;
gint view_size;
GtkTreeModel *model;
GtkTreeView *view;
GtkTreeSelection *selection;
GtkTreeViewColumn *eye_column;
GtkCellRenderer *eye_cell;
GtkCellRenderer *renderer_cell;
PikaChannelType clicked_component;
};
struct _PikaComponentEditorClass
{
PikaImageEditorClass parent_class;
};
GType pika_component_editor_get_type (void) G_GNUC_CONST;
GtkWidget * pika_component_editor_new (gint view_size,
PikaMenuFactory *menu_factory);
void pika_component_editor_set_view_size (PikaComponentEditor *editor,
gint view_size);
#endif /* __PIKA_COMPONENT_EDITOR_H__ */

View File

@ -0,0 +1,216 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikacompressioncombobox.c
* Copyright (C) 2004, 2008 Sven Neumann <sven@gimp.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "stdlib.h"
#include <gegl.h>
#include <gtk/gtk.h>
#include "libpikawidgets/pikawidgets.h"
#include "widgets-types.h"
#include "pikacompressioncombobox.h"
#include "pika-intl.h"
enum
{
COLUMN_ID,
COLUMN_LABEL,
N_COLUMNS
};
/* local function prototypes */
static void pika_compression_combo_box_constructed (GObject *object);
static gboolean pika_compression_combo_box_separator_func (GtkTreeModel *model,
GtkTreeIter *iter,
gpointer data);
G_DEFINE_TYPE (PikaCompressionComboBox, pika_compression_combo_box,
PIKA_TYPE_STRING_COMBO_BOX)
#define parent_class pika_compression_combo_box_parent_class
/* private functions */
static void
pika_compression_combo_box_class_init (PikaCompressionComboBoxClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->constructed = pika_compression_combo_box_constructed;
}
static void
pika_compression_combo_box_init (PikaCompressionComboBox *combo_box)
{
}
static void
pika_compression_combo_box_constructed (GObject *object)
{
PikaCompressionComboBox *combo_box = PIKA_COMPRESSION_COMBO_BOX (object);
GtkCellLayout *layout;
GtkCellRenderer *cell;
GtkListStore *store;
GtkTreeIter iter;
G_OBJECT_CLASS (parent_class)->constructed (object);
store = gtk_list_store_new (N_COLUMNS,
G_TYPE_STRING, /* ID */
G_TYPE_STRING); /* LABEL */
gtk_combo_box_set_model (GTK_COMBO_BOX (combo_box), GTK_TREE_MODEL (store));
g_object_unref (store);
gtk_combo_box_set_row_separator_func (
GTK_COMBO_BOX (combo_box),
pika_compression_combo_box_separator_func,
NULL,
NULL);
gtk_list_store_append (store, &iter);
gtk_list_store_set (store, &iter,
COLUMN_ID, "none",
COLUMN_LABEL, C_("compression", "None"),
-1);
gtk_list_store_append (store, &iter);
gtk_list_store_set (store, &iter,
COLUMN_ID, NULL,
COLUMN_LABEL, NULL,
-1);
gtk_list_store_append (store, &iter);
gtk_list_store_set (store, &iter,
COLUMN_ID, "fast",
COLUMN_LABEL, C_("compression", "Best performance"),
-1);
gtk_list_store_append (store, &iter);
gtk_list_store_set (store, &iter,
COLUMN_ID, "balanced",
COLUMN_LABEL, C_("compression", "Balanced"),
-1);
gtk_list_store_append (store, &iter);
gtk_list_store_set (store, &iter,
COLUMN_ID, "best",
COLUMN_LABEL, C_("compression", "Best compression"),
-1);
gtk_combo_box_set_entry_text_column (GTK_COMBO_BOX (combo_box),
COLUMN_LABEL);
layout = GTK_CELL_LAYOUT (combo_box);
cell = gtk_cell_renderer_text_new ();
gtk_cell_layout_clear (layout);
gtk_cell_layout_pack_start (layout, cell, TRUE);
gtk_cell_layout_set_attributes (layout, cell,
"text", COLUMN_LABEL,
NULL);
}
static gboolean
pika_compression_combo_box_separator_func (GtkTreeModel *model,
GtkTreeIter *iter,
gpointer data)
{
gchar *value;
gboolean result;
gtk_tree_model_get (model, iter, COLUMN_ID, &value, -1);
result = ! value;
g_free (value);
return result;
}
/* public functions */
GtkWidget *
pika_compression_combo_box_new (void)
{
return g_object_new (PIKA_TYPE_COMPRESSION_COMBO_BOX,
"has-entry", TRUE,
"id-column", COLUMN_ID,
"label-column", COLUMN_LABEL,
NULL);
}
void
pika_compression_combo_box_set_compression (PikaCompressionComboBox *combo_box,
const gchar *compression)
{
g_return_if_fail (PIKA_IS_COMPRESSION_COMBO_BOX (combo_box));
g_return_if_fail (compression != NULL);
if (! pika_string_combo_box_set_active (PIKA_STRING_COMBO_BOX (combo_box),
compression))
{
GtkWidget *entry;
entry = gtk_bin_get_child (GTK_BIN (combo_box));
gtk_combo_box_set_active (GTK_COMBO_BOX (combo_box), -1);
gtk_entry_set_text (GTK_ENTRY (entry), compression);
}
}
gchar *
pika_compression_combo_box_get_compression (PikaCompressionComboBox *combo_box)
{
gchar *result;
g_return_val_if_fail (PIKA_IS_COMPRESSION_COMBO_BOX (combo_box), NULL);
result = pika_string_combo_box_get_active (PIKA_STRING_COMBO_BOX (combo_box));
if (! result)
{
GtkWidget *entry;
entry = gtk_bin_get_child (GTK_BIN (combo_box));
result = g_strdup (gtk_entry_get_text (GTK_ENTRY (entry)));
}
return result;
}

View File

@ -0,0 +1,59 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikacompressioncombobox.h
* Copyright (C) 2019 Ell
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __PIKA_COMPRESSION_COMBO_BOX_H__
#define __PIKA_COMPRESSION_COMBO_BOX_H__
#define PIKA_TYPE_COMPRESSION_COMBO_BOX (pika_compression_combo_box_get_type ())
#define PIKA_COMPRESSION_COMBO_BOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIKA_TYPE_COMPRESSION_COMBO_BOX, PikaCompressionComboBox))
#define PIKA_COMPRESSION_COMBO_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIKA_TYPE_COMPRESSION_COMBO_BOX, PikaCompressionComboBoxClass))
#define PIKA_IS_COMPRESSION_COMBO_BOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIKA_TYPE_COMPRESSION_COMBO_BOX))
#define PIKA_IS_COMPRESSION_COMBO_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIKA_TYPE_COMPRESSION_COMBO_BOX))
#define PIKA_COMPRESSION_COMBO_BOX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIKA_TYPE_COMPRESSION_COMBO_BOX, PikaCompressionComboBoxClass))
typedef struct _PikaCompressionComboBoxClass PikaCompressionComboBoxClass;
struct _PikaCompressionComboBox
{
PikaStringComboBox parent_instance;
};
struct _PikaCompressionComboBoxClass
{
PikaStringComboBoxClass parent_instance;
};
GType pika_compression_combo_box_get_type (void) G_GNUC_CONST;
GtkWidget * pika_compression_combo_box_new (void);
void pika_compression_combo_box_set_compression (PikaCompressionComboBox *combo_box,
const gchar *compression);
gchar * pika_compression_combo_box_get_compression (PikaCompressionComboBox *combo_box);
#endif /* __PIKA_COMPRESSION_COMBO_BOX_H__ */

View File

@ -0,0 +1,224 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikacontainerbox.c
* Copyright (C) 2004 Michael Natterer <mitch@gimp.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <string.h>
#include <gegl.h>
#include <gtk/gtk.h>
#include "libpikawidgets/pikawidgets.h"
#include "widgets-types.h"
#include "core/pikacontainer.h"
#include "core/pikacontext.h"
#include "pikacontainerbox.h"
#include "pikacontainerview.h"
#include "pikadnd.h"
#include "pikadocked.h"
#include "pikapropwidgets.h"
#include "pikaview.h"
#include "pikaviewrenderer.h"
static void pika_container_box_view_iface_init (PikaContainerViewInterface *iface);
static void pika_container_box_docked_iface_init (PikaDockedInterface *iface);
static void pika_container_box_constructed (GObject *object);
static GtkWidget * pika_container_box_get_preview (PikaDocked *docked,
PikaContext *context,
GtkIconSize size);
static void pika_container_box_set_context (PikaDocked *docked,
PikaContext *context);
G_DEFINE_TYPE_WITH_CODE (PikaContainerBox, pika_container_box,
PIKA_TYPE_EDITOR,
G_IMPLEMENT_INTERFACE (PIKA_TYPE_CONTAINER_VIEW,
pika_container_box_view_iface_init)
G_IMPLEMENT_INTERFACE (PIKA_TYPE_DOCKED,
pika_container_box_docked_iface_init))
#define parent_class pika_container_box_parent_class
static void
pika_container_box_class_init (PikaContainerBoxClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->constructed = pika_container_box_constructed;
object_class->set_property = pika_container_view_set_property;
object_class->get_property = pika_container_view_get_property;
pika_container_view_install_properties (object_class);
}
static void
pika_container_box_init (PikaContainerBox *box)
{
GtkWidget *sb;
box->scrolled_win = gtk_scrolled_window_new (NULL, NULL);
gtk_box_pack_start (GTK_BOX (box), box->scrolled_win, TRUE, TRUE, 0);
gtk_widget_show (box->scrolled_win);
sb = gtk_scrolled_window_get_vscrollbar (GTK_SCROLLED_WINDOW (box->scrolled_win));
gtk_widget_set_can_focus (sb, FALSE);
}
static void
pika_container_box_view_iface_init (PikaContainerViewInterface *iface)
{
}
static void
pika_container_box_docked_iface_init (PikaDockedInterface *iface)
{
iface->get_preview = pika_container_box_get_preview;
iface->set_context = pika_container_box_set_context;
}
static void
pika_container_box_constructed (GObject *object)
{
PikaContainerBox *box = PIKA_CONTAINER_BOX (object);
/* This is evil: the hash table of "insert_data" is created on
* demand when PikaContainerView API is used, using a
* value_free_func that is set in the interface_init functions of
* its implementors. Therefore, no PikaContainerView API must be
* called from any init() function, because the interface_init()
* function of a subclass that sets the right value_free_func might
* not have been called yet, leaving the insert_data hash table
* without memory management.
*
* Call PikaContainerView API from GObject::constructed() instead,
* which runs after everything is set up correctly.
*/
pika_container_view_set_dnd_widget (PIKA_CONTAINER_VIEW (box),
box->scrolled_win);
G_OBJECT_CLASS (parent_class)->constructed (object);
}
void
pika_container_box_set_size_request (PikaContainerBox *box,
gint width,
gint height)
{
PikaContainerView *view;
GtkScrolledWindowClass *sw_class;
GtkStyleContext *sw_style;
GtkWidget *sb;
GtkRequisition req;
GtkBorder border;
gint view_size;
gint scrollbar_width;
gint border_x;
gint border_y;
g_return_if_fail (PIKA_IS_CONTAINER_BOX (box));
view = PIKA_CONTAINER_VIEW (box);
view_size = pika_container_view_get_view_size (view, NULL);
g_return_if_fail (width <= 0 || width >= view_size);
g_return_if_fail (height <= 0 || height >= view_size);
sw_class = GTK_SCROLLED_WINDOW_GET_CLASS (box->scrolled_win);
if (sw_class->scrollbar_spacing >= 0)
scrollbar_width = sw_class->scrollbar_spacing;
else
gtk_widget_style_get (GTK_WIDGET (box->scrolled_win),
"scrollbar-spacing", &scrollbar_width,
NULL);
sb = gtk_scrolled_window_get_vscrollbar (GTK_SCROLLED_WINDOW (box->scrolled_win));
gtk_widget_get_preferred_size (sb, &req, NULL);
scrollbar_width += req.width;
border_x = border_y = gtk_container_get_border_width (GTK_CONTAINER (box));
sw_style = gtk_widget_get_style_context (box->scrolled_win);
gtk_style_context_get_border (sw_style, gtk_widget_get_state_flags (box->scrolled_win), &border);
border_x += border.left + border.right + scrollbar_width;
border_y += border.top + border.bottom;
gtk_widget_set_size_request (box->scrolled_win,
width > 0 ? width + border_x : -1,
height > 0 ? height + border_y : -1);
}
static void
pika_container_box_set_context (PikaDocked *docked,
PikaContext *context)
{
pika_container_view_set_context (PIKA_CONTAINER_VIEW (docked), context);
}
static GtkWidget *
pika_container_box_get_preview (PikaDocked *docked,
PikaContext *context,
GtkIconSize size)
{
PikaContainerView *view = PIKA_CONTAINER_VIEW (docked);
PikaContainer *container;
GtkWidget *preview;
gint width;
gint height;
gint border_width = 1;
const gchar *prop_name;
container = pika_container_view_get_container (view);
g_return_val_if_fail (container != NULL, NULL);
gtk_icon_size_lookup (size, &width, &height);
prop_name = pika_context_type_to_prop_name (pika_container_get_children_type (container));
preview = pika_prop_view_new (G_OBJECT (context), prop_name,
context, height);
PIKA_VIEW (preview)->renderer->size = -1;
pika_container_view_get_view_size (view, &border_width);
border_width = MIN (1, border_width);
pika_view_renderer_set_size_full (PIKA_VIEW (preview)->renderer,
width, height, border_width);
return preview;
}

View File

@ -0,0 +1,62 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikacontainerbox.h
* Copyright (C) 2004 Michael Natterer <mitch@gimp.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __PIKA_CONTAINER_BOX_H__
#define __PIKA_CONTAINER_BOX_H__
#include "pikaeditor.h"
#define PIKA_TYPE_CONTAINER_BOX (pika_container_box_get_type ())
#define PIKA_CONTAINER_BOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIKA_TYPE_CONTAINER_BOX, PikaContainerBox))
#define PIKA_CONTAINER_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIKA_TYPE_CONTAINER_BOX, PikaContainerBoxClass))
#define PIKA_IS_CONTAINER_BOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIKA_TYPE_CONTAINER_BOX))
#define PIKA_IS_CONTAINER_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIKA_TYPE_CONTAINER_BOX))
#define PIKA_CONTAINER_BOX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIKA_TYPE_CONTAINER_BOX, PikaContainerBoxClass))
typedef struct _PikaContainerBoxClass PikaContainerBoxClass;
struct _PikaContainerBox
{
PikaEditor parent_instance;
GtkWidget *scrolled_win;
};
struct _PikaContainerBoxClass
{
PikaEditorClass parent_class;
};
GType pika_container_box_get_type (void) G_GNUC_CONST;
void pika_container_box_set_size_request (PikaContainerBox *box,
gint width,
gint height);
#endif /* __PIKA_CONTAINER_BOX_H__ */

View File

@ -0,0 +1,490 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikacontainercombobox.c
* Copyright (C) 2003 Michael Natterer <mitch@gimp.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <string.h>
#include <gegl.h>
#include <gtk/gtk.h>
#include "libpikabase/pikabase.h"
#include "widgets-types.h"
#include "core/pikacontainer.h"
#include "core/pikacontext.h"
#include "core/pikaviewable.h"
#include "pikacellrendererviewable.h"
#include "pikacontainercombobox.h"
#include "pikacontainertreestore.h"
#include "pikacontainerview.h"
#include "pikaviewrenderer.h"
enum
{
PROP_0,
PROP_ELLIPSIZE = PIKA_CONTAINER_VIEW_PROP_LAST + 1
};
static void pika_container_combo_box_view_iface_init (PikaContainerViewInterface *iface);
static void pika_container_combo_box_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
static void pika_container_combo_box_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
static void pika_container_combo_box_set_context (PikaContainerView *view,
PikaContext *context);
static gpointer pika_container_combo_box_insert_item (PikaContainerView *view,
PikaViewable *viewable,
gpointer parent_insert_data,
gint index);
static void pika_container_combo_box_remove_item (PikaContainerView *view,
PikaViewable *viewable,
gpointer insert_data);
static void pika_container_combo_box_reorder_item (PikaContainerView *view,
PikaViewable *viewable,
gint new_index,
gpointer insert_data);
static void pika_container_combo_box_rename_item (PikaContainerView *view,
PikaViewable *viewable,
gpointer insert_data);
static gboolean pika_container_combo_box_select_items(PikaContainerView *view,
GList *viewables,
GList *paths);
static void pika_container_combo_box_clear_items (PikaContainerView *view);
static void pika_container_combo_box_set_view_size (PikaContainerView *view);
static gint pika_container_combo_box_get_selected (PikaContainerView *view,
GList **items,
GList **items_data);
static void pika_container_combo_box_changed (GtkComboBox *combo_box,
PikaContainerView *view);
G_DEFINE_TYPE_WITH_CODE (PikaContainerComboBox, pika_container_combo_box,
GTK_TYPE_COMBO_BOX,
G_IMPLEMENT_INTERFACE (PIKA_TYPE_CONTAINER_VIEW,
pika_container_combo_box_view_iface_init))
#define parent_class pika_container_combo_box_parent_class
static PikaContainerViewInterface *parent_view_iface = NULL;
static void
pika_container_combo_box_class_init (PikaContainerComboBoxClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->set_property = pika_container_combo_box_set_property;
object_class->get_property = pika_container_combo_box_get_property;
pika_container_view_install_properties (object_class);
g_object_class_install_property (object_class,
PROP_ELLIPSIZE,
g_param_spec_enum ("ellipsize", NULL, NULL,
PANGO_TYPE_ELLIPSIZE_MODE,
PANGO_ELLIPSIZE_MIDDLE,
PIKA_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
}
static void
pika_container_combo_box_view_iface_init (PikaContainerViewInterface *iface)
{
parent_view_iface = g_type_interface_peek_parent (iface);
if (! parent_view_iface)
parent_view_iface = g_type_default_interface_peek (PIKA_TYPE_CONTAINER_VIEW);
iface->set_context = pika_container_combo_box_set_context;
iface->insert_item = pika_container_combo_box_insert_item;
iface->remove_item = pika_container_combo_box_remove_item;
iface->reorder_item = pika_container_combo_box_reorder_item;
iface->rename_item = pika_container_combo_box_rename_item;
iface->select_items = pika_container_combo_box_select_items;
iface->clear_items = pika_container_combo_box_clear_items;
iface->set_view_size = pika_container_combo_box_set_view_size;
iface->get_selected = pika_container_combo_box_get_selected;
iface->insert_data_free = (GDestroyNotify) gtk_tree_iter_free;
}
static void
pika_container_combo_box_init (PikaContainerComboBox *combo)
{
GtkTreeModel *model;
GtkCellLayout *layout;
GtkCellRenderer *cell;
GType types[PIKA_CONTAINER_TREE_STORE_N_COLUMNS];
gint n_types = 0;
pika_container_tree_store_columns_init (types, &n_types);
model = pika_container_tree_store_new (PIKA_CONTAINER_VIEW (combo),
n_types, types);
gtk_combo_box_set_model (GTK_COMBO_BOX (combo), model);
g_object_unref (model);
layout = GTK_CELL_LAYOUT (combo);
cell = pika_cell_renderer_viewable_new ();
gtk_cell_layout_pack_start (layout, cell, FALSE);
gtk_cell_layout_set_attributes (layout, cell,
"renderer",
PIKA_CONTAINER_TREE_STORE_COLUMN_RENDERER,
"sensitive",
PIKA_CONTAINER_TREE_STORE_COLUMN_NAME_SENSITIVE,
NULL);
pika_container_tree_store_add_renderer_cell (PIKA_CONTAINER_TREE_STORE (model),
cell, -1);
combo->viewable_renderer = cell;
cell = gtk_cell_renderer_text_new ();
gtk_cell_layout_pack_start (layout, cell, TRUE);
gtk_cell_layout_set_attributes (layout, cell,
"text",
PIKA_CONTAINER_TREE_STORE_COLUMN_NAME,
"sensitive",
PIKA_CONTAINER_TREE_STORE_COLUMN_NAME_SENSITIVE,
NULL);
combo->text_renderer = cell;
g_signal_connect (combo, "changed",
G_CALLBACK (pika_container_combo_box_changed),
combo);
gtk_widget_set_sensitive (GTK_WIDGET (combo), FALSE);
}
static void
pika_container_combo_box_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
PikaContainerComboBox *combo = PIKA_CONTAINER_COMBO_BOX (object);
switch (property_id)
{
case PROP_ELLIPSIZE:
g_object_set_property (G_OBJECT (combo->text_renderer),
pspec->name, value);
break;
default:
pika_container_view_set_property (object, property_id, value, pspec);
break;
}
}
static void
pika_container_combo_box_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
PikaContainerComboBox *combo = PIKA_CONTAINER_COMBO_BOX (object);
switch (property_id)
{
case PROP_ELLIPSIZE:
g_object_get_property (G_OBJECT (combo->text_renderer),
pspec->name, value);
break;
default:
pika_container_view_get_property (object, property_id, value, pspec);
break;
}
}
GtkWidget *
pika_container_combo_box_new (PikaContainer *container,
PikaContext *context,
gint view_size,
gint view_border_width)
{
GtkWidget *combo_box;
PikaContainerView *view;
g_return_val_if_fail (container == NULL || PIKA_IS_CONTAINER (container),
NULL);
g_return_val_if_fail (context == NULL || PIKA_IS_CONTEXT (context), NULL);
combo_box = g_object_new (PIKA_TYPE_CONTAINER_COMBO_BOX, NULL);
view = PIKA_CONTAINER_VIEW (combo_box);
pika_container_view_set_view_size (view, view_size, view_border_width);
if (container)
pika_container_view_set_container (view, container);
if (context)
pika_container_view_set_context (view, context);
return combo_box;
}
/* PikaContainerView methods */
static void
pika_container_combo_box_set_context (PikaContainerView *view,
PikaContext *context)
{
GtkTreeModel *model = gtk_combo_box_get_model (GTK_COMBO_BOX (view));
parent_view_iface->set_context (view, context);
if (model)
pika_container_tree_store_set_context (PIKA_CONTAINER_TREE_STORE (model),
context);
}
static gpointer
pika_container_combo_box_insert_item (PikaContainerView *view,
PikaViewable *viewable,
gpointer parent_insert_data,
gint index)
{
GtkTreeModel *model = gtk_combo_box_get_model (GTK_COMBO_BOX (view));
if (model)
{
GtkTreeIter *iter;
iter = pika_container_tree_store_insert_item (PIKA_CONTAINER_TREE_STORE (model),
viewable,
parent_insert_data,
index);
if (gtk_tree_model_iter_n_children (model, NULL) == 1)
{
/* PikaContainerViews don't select items by default */
gtk_combo_box_set_active (GTK_COMBO_BOX (view), -1);
gtk_widget_set_sensitive (GTK_WIDGET (view), TRUE);
}
return iter;
}
return NULL;
}
static void
pika_container_combo_box_remove_item (PikaContainerView *view,
PikaViewable *viewable,
gpointer insert_data)
{
GtkTreeModel *model = gtk_combo_box_get_model (GTK_COMBO_BOX (view));
if (model)
{
GtkTreeIter *iter = insert_data;
pika_container_tree_store_remove_item (PIKA_CONTAINER_TREE_STORE (model),
viewable,
iter);
if (iter && gtk_tree_model_iter_n_children (model, NULL) == 0)
{
gtk_widget_set_sensitive (GTK_WIDGET (view), FALSE);
}
}
}
static void
pika_container_combo_box_reorder_item (PikaContainerView *view,
PikaViewable *viewable,
gint new_index,
gpointer insert_data)
{
GtkTreeModel *model = gtk_combo_box_get_model (GTK_COMBO_BOX (view));
if (model)
pika_container_tree_store_reorder_item (PIKA_CONTAINER_TREE_STORE (model),
viewable,
new_index,
insert_data);
}
static void
pika_container_combo_box_rename_item (PikaContainerView *view,
PikaViewable *viewable,
gpointer insert_data)
{
GtkTreeModel *model = gtk_combo_box_get_model (GTK_COMBO_BOX (view));
if (model)
pika_container_tree_store_rename_item (PIKA_CONTAINER_TREE_STORE (model),
viewable,
insert_data);
}
static gboolean
pika_container_combo_box_select_items (PikaContainerView *view,
GList *viewables,
GList *paths)
{
GtkComboBox *combo_box = GTK_COMBO_BOX (view);
g_return_val_if_fail (PIKA_IS_CONTAINER_VIEW (view), FALSE);
/* Only zero or one items may selected in a PikaContainerComboBox. */
g_return_val_if_fail (g_list_length (viewables) < 2, FALSE);
if (gtk_combo_box_get_model (GTK_COMBO_BOX (view)))
{
g_signal_handlers_block_by_func (combo_box,
pika_container_combo_box_changed,
view);
if (viewables)
{
GtkTreeModel *model = gtk_combo_box_get_model (GTK_COMBO_BOX (view));
GtkTreeIter iter;
gboolean iter_valid;
for (iter_valid = gtk_tree_model_get_iter_first (model, &iter);
iter_valid;
iter_valid = gtk_tree_model_iter_next (model, &iter))
{
PikaViewRenderer *renderer;
gtk_tree_model_get (model, &iter,
PIKA_CONTAINER_TREE_STORE_COLUMN_RENDERER, &renderer,
-1);
if (renderer->viewable == viewables->data)
{
gtk_combo_box_set_active_iter (combo_box, &iter);
g_object_unref (renderer);
break;
}
g_object_unref (renderer);
}
}
else
{
gtk_combo_box_set_active (combo_box, -1);
}
g_signal_handlers_unblock_by_func (combo_box,
pika_container_combo_box_changed,
view);
}
return TRUE;
}
static void
pika_container_combo_box_clear_items (PikaContainerView *view)
{
GtkTreeModel *model = gtk_combo_box_get_model (GTK_COMBO_BOX (view));
if (model)
pika_container_tree_store_clear_items (PIKA_CONTAINER_TREE_STORE (model));
gtk_widget_set_sensitive (GTK_WIDGET (view), FALSE);
parent_view_iface->clear_items (view);
}
static void
pika_container_combo_box_set_view_size (PikaContainerView *view)
{
GtkTreeModel *model = gtk_combo_box_get_model (GTK_COMBO_BOX (view));
if (model)
pika_container_tree_store_set_view_size (PIKA_CONTAINER_TREE_STORE (model));
}
static gint
pika_container_combo_box_get_selected (PikaContainerView *view,
GList **items,
GList **items_data)
{
GtkComboBox *combo_box = GTK_COMBO_BOX (view);
PikaViewRenderer *renderer = NULL;
GtkTreeIter iter;
gint selected = 0;
if (gtk_combo_box_get_active_iter (combo_box, &iter))
gtk_tree_model_get (gtk_combo_box_get_model (combo_box), &iter,
PIKA_CONTAINER_TREE_STORE_COLUMN_RENDERER, &renderer,
-1);
if (items)
{
if (renderer != NULL && renderer->viewable != NULL)
{
*items = g_list_prepend (NULL, renderer->viewable);
selected = 1;
}
else
{
*items = NULL;
}
}
g_clear_object (&renderer);
return selected;
}
static void
pika_container_combo_box_changed (GtkComboBox *combo,
PikaContainerView *view)
{
GtkTreeIter iter;
if (gtk_combo_box_get_active_iter (combo, &iter))
{
PikaViewRenderer *renderer;
gtk_tree_model_get (gtk_combo_box_get_model (combo), &iter,
PIKA_CONTAINER_TREE_STORE_COLUMN_RENDERER, &renderer,
-1);
pika_container_view_item_selected (view, renderer->viewable);
g_object_unref (renderer);
}
}

View File

@ -0,0 +1,61 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikacontainercombobox.h
* Copyright (C) 2004 Sven Neumann <sven@gimp.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __PIKA_CONTAINER_COMBO_BOX_H__
#define __PIKA_CONTAINER_COMBO_BOX_H__
#define PIKA_TYPE_CONTAINER_COMBO_BOX (pika_container_combo_box_get_type ())
#define PIKA_CONTAINER_COMBO_BOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIKA_TYPE_CONTAINER_COMBO_BOX, PikaContainerComboBox))
#define PIKA_CONTAINER_COMBO_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIKA_TYPE_CONTAINER_COMBO_BOX, PikaContainerComboBoxClass))
#define PIKA_IS_CONTAINER_COMBO_BOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIKA_TYPE_CONTAINER_COMBO_BOX))
#define PIKA_IS_CONTAINER_COMBO_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIKA_TYPE_CONTAINER_COMBO_BOX))
#define PIKA_CONTAINER_COMBO_BOX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIKA_TYPE_CONTAINER_COMBO_BOX, PikaContainerComboBoxClass))
typedef struct _PikaContainerComboBoxClass PikaContainerComboBoxClass;
struct _PikaContainerComboBox
{
GtkComboBox parent_instance;
GtkCellRenderer *text_renderer;
GtkCellRenderer *viewable_renderer;
};
struct _PikaContainerComboBoxClass
{
GtkComboBoxClass parent_class;
};
GType pika_container_combo_box_get_type (void) G_GNUC_CONST;
GtkWidget * pika_container_combo_box_new (PikaContainer *container,
PikaContext *context,
gint view_size,
gint view_border_width);
#endif /* __PIKA_CONTAINER_COMBO_BOX_H__ */

View File

@ -0,0 +1,570 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikacontainereditor.c
* Copyright (C) 2001-2011 Michael Natterer <mitch@gimp.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gegl.h>
#include <gtk/gtk.h>
#include "libpikabase/pikabase.h"
#include "libpikawidgets/pikawidgets.h"
#include "widgets-types.h"
#include "core/pikaasyncset.h"
#include "core/pikacontext.h"
#include "core/pikalist.h"
#include "core/pikaviewable.h"
#include "pikacontainereditor.h"
#include "pikacontainericonview.h"
#include "pikacontainertreeview.h"
#include "pikacontainerview.h"
#include "pikadocked.h"
#include "pikamenufactory.h"
#include "pikaviewrenderer.h"
#include "pikauimanager.h"
enum
{
PROP_0,
PROP_VIEW_TYPE,
PROP_CONTAINER,
PROP_CONTEXT,
PROP_VIEW_SIZE,
PROP_VIEW_BORDER_WIDTH,
PROP_MENU_FACTORY,
PROP_MENU_IDENTIFIER,
PROP_UI_PATH
};
struct _PikaContainerEditorPrivate
{
PikaViewType view_type;
PikaContainer *container;
PikaContext *context;
gint view_size;
gint view_border_width;
PikaMenuFactory *menu_factory;
gchar *menu_identifier;
gchar *ui_path;
GtkWidget *busy_box;
GBinding *async_set_binding;
};
static void pika_container_editor_docked_iface_init (PikaDockedInterface *iface);
static void pika_container_editor_constructed (GObject *object);
static void pika_container_editor_dispose (GObject *object);
static void pika_container_editor_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
static void pika_container_editor_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
static gboolean pika_container_editor_select_items (PikaContainerView *view,
GList *items,
GList *paths,
PikaContainerEditor *editor);
static void pika_container_editor_activate_item (GtkWidget *widget,
PikaViewable *viewable,
gpointer insert_data,
PikaContainerEditor *editor);
static GtkWidget * pika_container_editor_get_preview (PikaDocked *docked,
PikaContext *context,
GtkIconSize size);
static void pika_container_editor_set_context (PikaDocked *docked,
PikaContext *context);
static PikaUIManager * pika_container_editor_get_menu(PikaDocked *docked,
const gchar **ui_path,
gpointer *popup_data);
static gboolean pika_container_editor_has_button_bar (PikaDocked *docked);
static void pika_container_editor_set_show_button_bar (PikaDocked *docked,
gboolean show);
static gboolean pika_container_editor_get_show_button_bar (PikaDocked *docked);
G_DEFINE_TYPE_WITH_CODE (PikaContainerEditor, pika_container_editor,
GTK_TYPE_BOX,
G_ADD_PRIVATE (PikaContainerEditor)
G_IMPLEMENT_INTERFACE (PIKA_TYPE_DOCKED,
pika_container_editor_docked_iface_init))
#define parent_class pika_container_editor_parent_class
static void
pika_container_editor_class_init (PikaContainerEditorClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->constructed = pika_container_editor_constructed;
object_class->dispose = pika_container_editor_dispose;
object_class->set_property = pika_container_editor_set_property;
object_class->get_property = pika_container_editor_get_property;
klass->select_item = NULL;
klass->activate_item = NULL;
g_object_class_install_property (object_class, PROP_VIEW_TYPE,
g_param_spec_enum ("view-type",
NULL, NULL,
PIKA_TYPE_VIEW_TYPE,
PIKA_VIEW_TYPE_LIST,
PIKA_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property (object_class, PROP_CONTAINER,
g_param_spec_object ("container",
NULL, NULL,
PIKA_TYPE_CONTAINER,
PIKA_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property (object_class, PROP_CONTEXT,
g_param_spec_object ("context",
NULL, NULL,
PIKA_TYPE_CONTEXT,
PIKA_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property (object_class, PROP_VIEW_SIZE,
g_param_spec_int ("view-size",
NULL, NULL,
1, PIKA_VIEWABLE_MAX_PREVIEW_SIZE,
PIKA_VIEW_SIZE_MEDIUM,
PIKA_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
g_object_class_install_property (object_class, PROP_VIEW_BORDER_WIDTH,
g_param_spec_int ("view-border-width",
NULL, NULL,
0,
PIKA_VIEW_MAX_BORDER_WIDTH,
1,
PIKA_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
g_object_class_install_property (object_class, PROP_MENU_FACTORY,
g_param_spec_object ("menu-factory",
NULL, NULL,
PIKA_TYPE_MENU_FACTORY,
PIKA_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property (object_class, PROP_MENU_IDENTIFIER,
g_param_spec_string ("menu-identifier",
NULL, NULL,
NULL,
PIKA_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property (object_class, PROP_UI_PATH,
g_param_spec_string ("ui-path",
NULL, NULL,
NULL,
PIKA_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY));
}
static void
pika_container_editor_docked_iface_init (PikaDockedInterface *iface)
{
iface->get_preview = pika_container_editor_get_preview;
iface->set_context = pika_container_editor_set_context;
iface->get_menu = pika_container_editor_get_menu;
iface->has_button_bar = pika_container_editor_has_button_bar;
iface->set_show_button_bar = pika_container_editor_set_show_button_bar;
iface->get_show_button_bar = pika_container_editor_get_show_button_bar;
}
static void
pika_container_editor_init (PikaContainerEditor *editor)
{
gtk_orientable_set_orientation (GTK_ORIENTABLE (editor),
GTK_ORIENTATION_VERTICAL);
editor->priv = pika_container_editor_get_instance_private (editor);
}
static void
pika_container_editor_constructed (GObject *object)
{
PikaContainerEditor *editor = PIKA_CONTAINER_EDITOR (object);
G_OBJECT_CLASS (parent_class)->constructed (object);
pika_assert (PIKA_IS_CONTAINER (editor->priv->container));
pika_assert (PIKA_IS_CONTEXT (editor->priv->context));
switch (editor->priv->view_type)
{
case PIKA_VIEW_TYPE_GRID:
editor->view =
PIKA_CONTAINER_VIEW (pika_container_icon_view_new (editor->priv->container,
editor->priv->context,
editor->priv->view_size,
editor->priv->view_border_width));
break;
case PIKA_VIEW_TYPE_LIST:
editor->view =
PIKA_CONTAINER_VIEW (pika_container_tree_view_new (editor->priv->container,
editor->priv->context,
editor->priv->view_size,
editor->priv->view_border_width));
break;
default:
pika_assert_not_reached ();
}
if (PIKA_IS_LIST (editor->priv->container))
pika_container_view_set_reorderable (PIKA_CONTAINER_VIEW (editor->view),
! pika_list_get_sort_func (PIKA_LIST (editor->priv->container)));
if (editor->priv->menu_factory &&
editor->priv->menu_identifier &&
editor->priv->ui_path)
{
pika_editor_create_menu (PIKA_EDITOR (editor->view),
editor->priv->menu_factory,
editor->priv->menu_identifier,
editor->priv->ui_path,
editor);
}
gtk_box_pack_start (GTK_BOX (editor), GTK_WIDGET (editor->view),
TRUE, TRUE, 0);
gtk_widget_show (GTK_WIDGET (editor->view));
editor->priv->busy_box = pika_busy_box_new (NULL);
gtk_box_pack_start (GTK_BOX (editor), editor->priv->busy_box, TRUE, TRUE, 0);
g_object_bind_property (editor->priv->busy_box, "visible",
editor->view, "visible",
G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN);
/* Connect "select-items" with G_CONNECT_AFTER because it's a
* RUN_LAST signal and the default handler selecting the row must
* run before signal connections. See bug #784176.
*/
g_signal_connect_object (editor->view, "select-items",
G_CALLBACK (pika_container_editor_select_items),
editor, G_CONNECT_AFTER);
g_signal_connect_object (editor->view, "activate-item",
G_CALLBACK (pika_container_editor_activate_item),
editor, 0);
/* g_signal_connect_object (editor->view, "context-item", XXX maybe listen to popup-menu? */
/* G_CALLBACK (pika_container_editor_context_item), */
/* editor, 0); */
{
GList *objects = NULL;
PikaObject *object = pika_context_get_by_type (editor->priv->context,
pika_container_get_children_type (editor->priv->container));
if (object)
objects = g_list_prepend (objects, object);
pika_container_editor_select_items (editor->view, objects, NULL, editor);
g_list_free (objects);
}
}
static void
pika_container_editor_dispose (GObject *object)
{
PikaContainerEditor *editor = PIKA_CONTAINER_EDITOR (object);
pika_container_editor_bind_to_async_set (editor, NULL, NULL);
g_clear_object (&editor->priv->container);
g_clear_object (&editor->priv->context);
g_clear_object (&editor->priv->menu_factory);
g_clear_pointer (&editor->priv->menu_identifier, g_free);
g_clear_pointer (&editor->priv->ui_path, g_free);
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
pika_container_editor_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
PikaContainerEditor *editor = PIKA_CONTAINER_EDITOR (object);
switch (property_id)
{
case PROP_VIEW_TYPE:
editor->priv->view_type = g_value_get_enum (value);
break;
case PROP_CONTAINER:
editor->priv->container = g_value_dup_object (value);
break;
case PROP_CONTEXT:
editor->priv->context = g_value_dup_object (value);
break;
case PROP_VIEW_SIZE:
editor->priv->view_size = g_value_get_int (value);
break;
case PROP_VIEW_BORDER_WIDTH:
editor->priv->view_border_width = g_value_get_int (value);
break;
case PROP_MENU_FACTORY:
editor->priv->menu_factory = g_value_dup_object (value);
break;
case PROP_MENU_IDENTIFIER:
editor->priv->menu_identifier = g_value_dup_string (value);
break;
case PROP_UI_PATH:
editor->priv->ui_path = g_value_dup_string (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
pika_container_editor_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
PikaContainerEditor *editor = PIKA_CONTAINER_EDITOR (object);
switch (property_id)
{
case PROP_VIEW_TYPE:
g_value_set_enum (value, editor->priv->view_type);
break;
case PROP_CONTAINER:
g_value_set_object (value, editor->priv->container);
break;
case PROP_CONTEXT:
g_value_set_object (value, editor->priv->context);
break;
case PROP_VIEW_SIZE:
g_value_set_int (value, editor->priv->view_size);
break;
case PROP_VIEW_BORDER_WIDTH:
g_value_set_int (value, editor->priv->view_border_width);
break;
case PROP_MENU_FACTORY:
g_value_set_object (value, editor->priv->menu_factory);
break;
case PROP_MENU_IDENTIFIER:
g_value_set_string (value, editor->priv->menu_identifier);
break;
case PROP_UI_PATH:
g_value_set_string (value, editor->priv->ui_path);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
GtkSelectionMode
pika_container_editor_get_selection_mode (PikaContainerEditor *editor)
{
return pika_container_view_get_selection_mode (PIKA_CONTAINER_VIEW (editor->view));
}
void
pika_container_editor_set_selection_mode (PikaContainerEditor *editor,
GtkSelectionMode mode)
{
pika_container_view_set_selection_mode (PIKA_CONTAINER_VIEW (editor->view),
mode);
}
/* private functions */
static gboolean
pika_container_editor_select_items (PikaContainerView *view,
GList *items,
GList *paths,
PikaContainerEditor *editor)
{
PikaContainerEditorClass *klass = PIKA_CONTAINER_EDITOR_GET_CLASS (editor);
PikaViewable *viewable = NULL;
/* XXX Right now a PikaContainerEditor only supports 1 item selected
* at once. Let's see later if we want to allow more.
*/
/*g_return_val_if_fail (g_list_length (items) < 2, FALSE);*/
if (items)
viewable = items->data;
if (klass->select_item)
klass->select_item (editor, viewable);
if (editor->priv->container)
{
const gchar *signal_name;
GType children_type;
children_type = pika_container_get_children_type (editor->priv->container);
signal_name = pika_context_type_to_signal_name (children_type);
if (signal_name)
pika_context_set_by_type (editor->priv->context, children_type,
PIKA_OBJECT (viewable));
}
if (pika_editor_get_ui_manager (PIKA_EDITOR (editor->view)))
pika_ui_manager_update (pika_editor_get_ui_manager (PIKA_EDITOR (editor->view)),
pika_editor_get_popup_data (PIKA_EDITOR (editor->view)));
return TRUE;
}
static void
pika_container_editor_activate_item (GtkWidget *widget,
PikaViewable *viewable,
gpointer insert_data,
PikaContainerEditor *editor)
{
PikaContainerEditorClass *klass = PIKA_CONTAINER_EDITOR_GET_CLASS (editor);
if (klass->activate_item)
klass->activate_item (editor, viewable);
}
static GtkWidget *
pika_container_editor_get_preview (PikaDocked *docked,
PikaContext *context,
GtkIconSize size)
{
PikaContainerEditor *editor = PIKA_CONTAINER_EDITOR (docked);
return pika_docked_get_preview (PIKA_DOCKED (editor->view),
context, size);
}
static void
pika_container_editor_set_context (PikaDocked *docked,
PikaContext *context)
{
PikaContainerEditor *editor = PIKA_CONTAINER_EDITOR (docked);
pika_docked_set_context (PIKA_DOCKED (editor->view), context);
}
static PikaUIManager *
pika_container_editor_get_menu (PikaDocked *docked,
const gchar **ui_path,
gpointer *popup_data)
{
PikaContainerEditor *editor = PIKA_CONTAINER_EDITOR (docked);
return pika_docked_get_menu (PIKA_DOCKED (editor->view), ui_path, popup_data);
}
static gboolean
pika_container_editor_has_button_bar (PikaDocked *docked)
{
PikaContainerEditor *editor = PIKA_CONTAINER_EDITOR (docked);
return pika_docked_has_button_bar (PIKA_DOCKED (editor->view));
}
static void
pika_container_editor_set_show_button_bar (PikaDocked *docked,
gboolean show)
{
PikaContainerEditor *editor = PIKA_CONTAINER_EDITOR (docked);
pika_docked_set_show_button_bar (PIKA_DOCKED (editor->view), show);
}
static gboolean
pika_container_editor_get_show_button_bar (PikaDocked *docked)
{
PikaContainerEditor *editor = PIKA_CONTAINER_EDITOR (docked);
return pika_docked_get_show_button_bar (PIKA_DOCKED (editor->view));
}
void
pika_container_editor_bind_to_async_set (PikaContainerEditor *editor,
PikaAsyncSet *async_set,
const gchar *message)
{
g_return_if_fail (PIKA_IS_CONTAINER_EDITOR (editor));
g_return_if_fail (async_set == NULL || PIKA_IS_ASYNC_SET (async_set));
g_return_if_fail (async_set == NULL || message != NULL);
if (! async_set && ! editor->priv->async_set_binding)
return;
g_clear_object (&editor->priv->async_set_binding);
if (async_set)
{
pika_busy_box_set_message (PIKA_BUSY_BOX (editor->priv->busy_box),
message);
editor->priv->async_set_binding = g_object_bind_property (
async_set, "empty",
editor->priv->busy_box, "visible",
G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN);
}
else
{
gtk_widget_hide (editor->priv->busy_box);
}
}

View File

@ -0,0 +1,71 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikacontainereditor.h
* Copyright (C) 2001-2011 Michael Natterer <mitch@gimp.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __PIKA_CONTAINER_EDITOR_H__
#define __PIKA_CONTAINER_EDITOR_H__
#define PIKA_TYPE_CONTAINER_EDITOR (pika_container_editor_get_type ())
#define PIKA_CONTAINER_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIKA_TYPE_CONTAINER_EDITOR, PikaContainerEditor))
#define PIKA_CONTAINER_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIKA_TYPE_CONTAINER_EDITOR, PikaContainerEditorClass))
#define PIKA_IS_CONTAINER_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIKA_TYPE_CONTAINER_EDITOR))
#define PIKA_IS_CONTAINER_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIKA_TYPE_CONTAINER_EDITOR))
#define PIKA_CONTAINER_EDITOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIKA_TYPE_CONTAINER_EDITOR, PikaContainerEditorClass))
typedef struct _PikaContainerEditorPrivate PikaContainerEditorPrivate;
typedef struct _PikaContainerEditorClass PikaContainerEditorClass;
struct _PikaContainerEditor
{
GtkBox parent_instance;
PikaContainerView *view;
PikaContainerEditorPrivate *priv;
};
struct _PikaContainerEditorClass
{
GtkBoxClass parent_class;
void (* select_item) (PikaContainerEditor *editor,
PikaViewable *object);
void (* activate_item) (PikaContainerEditor *editor,
PikaViewable *object);
};
GType pika_container_editor_get_type (void) G_GNUC_CONST;
GtkSelectionMode pika_container_editor_get_selection_mode (PikaContainerEditor *editor);
void pika_container_editor_set_selection_mode (PikaContainerEditor *editor,
GtkSelectionMode mode);
void pika_container_editor_bind_to_async_set (PikaContainerEditor *editor,
PikaAsyncSet *async_set,
const gchar *message);
#endif /* __PIKA_CONTAINER_EDITOR_H__ */

View File

@ -0,0 +1,444 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikacontainerentry.c
* Copyright (C) 2003 Michael Natterer <mitch@gimp.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <string.h>
#include <gegl.h>
#include <gtk/gtk.h>
#include "libpikawidgets/pikawidgets.h"
#include "widgets-types.h"
#include "core/pikacontainer.h"
#include "core/pikacontext.h"
#include "core/pikaviewable.h"
#include "pikacellrendererviewable.h"
#include "pikacontainerentry.h"
#include "pikacontainertreestore.h"
#include "pikacontainerview.h"
#include "pikaviewrenderer.h"
static void pika_container_entry_view_iface_init (PikaContainerViewInterface *iface);
static void pika_container_entry_finalize (GObject *object);
static void pika_container_entry_set_context (PikaContainerView *view,
PikaContext *context);
static gpointer pika_container_entry_insert_item (PikaContainerView *view,
PikaViewable *viewable,
gpointer parent_insert_data,
gint index);
static void pika_container_entry_remove_item (PikaContainerView *view,
PikaViewable *viewable,
gpointer insert_data);
static void pika_container_entry_reorder_item (PikaContainerView *view,
PikaViewable *viewable,
gint new_index,
gpointer insert_data);
static void pika_container_entry_rename_item (PikaContainerView *view,
PikaViewable *viewable,
gpointer insert_data);
static gboolean pika_container_entry_select_items(PikaContainerView *view,
GList *items,
GList *paths);
static void pika_container_entry_clear_items (PikaContainerView *view);
static void pika_container_entry_set_view_size (PikaContainerView *view);
static gint pika_container_entry_get_selected (PikaContainerView *view,
GList **items,
GList **items_data);
static void pika_container_entry_changed (GtkEntry *entry,
PikaContainerView *view);
static void pika_container_entry_match_selected (GtkEntryCompletion *widget,
GtkTreeModel *model,
GtkTreeIter *iter,
PikaContainerView *view);
G_DEFINE_TYPE_WITH_CODE (PikaContainerEntry, pika_container_entry,
GTK_TYPE_ENTRY,
G_IMPLEMENT_INTERFACE (PIKA_TYPE_CONTAINER_VIEW,
pika_container_entry_view_iface_init))
#define parent_class pika_container_entry_parent_class
static PikaContainerViewInterface *parent_view_iface = NULL;
static void
pika_container_entry_class_init (PikaContainerEntryClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->set_property = pika_container_view_set_property;
object_class->get_property = pika_container_view_get_property;
object_class->finalize = pika_container_entry_finalize;
pika_container_view_install_properties (object_class);
}
static void
pika_container_entry_view_iface_init (PikaContainerViewInterface *iface)
{
parent_view_iface = g_type_interface_peek_parent (iface);
if (! parent_view_iface)
parent_view_iface = g_type_default_interface_peek (PIKA_TYPE_CONTAINER_VIEW);
iface->set_context = pika_container_entry_set_context;
iface->insert_item = pika_container_entry_insert_item;
iface->remove_item = pika_container_entry_remove_item;
iface->reorder_item = pika_container_entry_reorder_item;
iface->rename_item = pika_container_entry_rename_item;
iface->select_items = pika_container_entry_select_items;
iface->clear_items = pika_container_entry_clear_items;
iface->set_view_size = pika_container_entry_set_view_size;
iface->get_selected = pika_container_entry_get_selected;
iface->insert_data_free = (GDestroyNotify) gtk_tree_iter_free;
}
static void
pika_container_entry_init (PikaContainerEntry *entry)
{
GtkEntryCompletion *completion;
GtkTreeModel *model;
GtkCellRenderer *cell;
GType types[PIKA_CONTAINER_TREE_STORE_N_COLUMNS];
gint n_types = 0;
entry->viewable = NULL;
completion = g_object_new (GTK_TYPE_ENTRY_COMPLETION,
"inline-completion", FALSE,
"popup-single-match", TRUE,
"popup-set-width", FALSE,
NULL);
pika_container_tree_store_columns_init (types, &n_types);
model = pika_container_tree_store_new (PIKA_CONTAINER_VIEW (entry),
n_types, types);
pika_container_tree_store_set_use_name (PIKA_CONTAINER_TREE_STORE (model),
TRUE);
gtk_entry_completion_set_model (completion, model);
g_object_unref (model);
gtk_entry_set_completion (GTK_ENTRY (entry), completion);
g_signal_connect (completion, "match-selected",
G_CALLBACK (pika_container_entry_match_selected),
entry);
g_object_unref (completion);
/* FIXME: This can be done better with GTK+ 2.6. */
cell = pika_cell_renderer_viewable_new ();
gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (completion), cell, FALSE);
gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (completion), cell,
"renderer",
PIKA_CONTAINER_TREE_STORE_COLUMN_RENDERER,
NULL);
pika_container_tree_store_add_renderer_cell (PIKA_CONTAINER_TREE_STORE (model),
cell, -1);
gtk_entry_completion_set_text_column (completion,
PIKA_CONTAINER_TREE_STORE_COLUMN_NAME);
g_signal_connect (entry, "changed",
G_CALLBACK (pika_container_entry_changed),
entry);
}
GtkWidget *
pika_container_entry_new (PikaContainer *container,
PikaContext *context,
gint view_size,
gint view_border_width)
{
GtkWidget *entry;
PikaContainerView *view;
g_return_val_if_fail (container == NULL || PIKA_IS_CONTAINER (container),
NULL);
g_return_val_if_fail (context == NULL || PIKA_IS_CONTEXT (context), NULL);
entry = g_object_new (PIKA_TYPE_CONTAINER_ENTRY, NULL);
view = PIKA_CONTAINER_VIEW (entry);
pika_container_view_set_view_size (view, view_size, view_border_width);
if (container)
pika_container_view_set_container (view, container);
if (context)
pika_container_view_set_context (view, context);
return entry;
}
/* PikaContainerView methods */
static GtkTreeModel *
pika_container_entry_get_model (PikaContainerView *view)
{
GtkEntryCompletion *completion;
completion = gtk_entry_get_completion (GTK_ENTRY (view));
if (completion)
return gtk_entry_completion_get_model (completion);
return NULL;
}
static void
pika_container_entry_finalize (GObject *object)
{
PikaContainerEntry *entry = PIKA_CONTAINER_ENTRY (object);
g_clear_weak_pointer (&entry->viewable);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
pika_container_entry_set_context (PikaContainerView *view,
PikaContext *context)
{
GtkTreeModel *model = pika_container_entry_get_model (view);
parent_view_iface->set_context (view, context);
if (model)
pika_container_tree_store_set_context (PIKA_CONTAINER_TREE_STORE (model),
context);
}
static gpointer
pika_container_entry_insert_item (PikaContainerView *view,
PikaViewable *viewable,
gpointer parent_insert_data,
gint index)
{
GtkTreeModel *model = pika_container_entry_get_model (view);
return pika_container_tree_store_insert_item (PIKA_CONTAINER_TREE_STORE (model),
viewable,
parent_insert_data,
index);
}
static void
pika_container_entry_remove_item (PikaContainerView *view,
PikaViewable *viewable,
gpointer insert_data)
{
GtkTreeModel *model = pika_container_entry_get_model (view);
pika_container_tree_store_remove_item (PIKA_CONTAINER_TREE_STORE (model),
viewable,
insert_data);
}
static void
pika_container_entry_reorder_item (PikaContainerView *view,
PikaViewable *viewable,
gint new_index,
gpointer insert_data)
{
GtkTreeModel *model = pika_container_entry_get_model (view);
pika_container_tree_store_reorder_item (PIKA_CONTAINER_TREE_STORE (model),
viewable,
new_index,
insert_data);
}
static void
pika_container_entry_rename_item (PikaContainerView *view,
PikaViewable *viewable,
gpointer insert_data)
{
PikaContainerEntry *container_entry = PIKA_CONTAINER_ENTRY (view);
GtkEntry *entry = GTK_ENTRY (view);
GtkTreeModel *model = pika_container_entry_get_model (view);
if (viewable == container_entry->viewable)
{
g_signal_handlers_block_by_func (entry,
pika_container_entry_changed,
view);
gtk_entry_set_text (entry, pika_object_get_name (viewable));
g_signal_handlers_unblock_by_func (entry,
pika_container_entry_changed,
view);
}
pika_container_tree_store_rename_item (PIKA_CONTAINER_TREE_STORE (model),
viewable,
insert_data);
}
static gboolean
pika_container_entry_select_items (PikaContainerView *view,
GList *viewables,
GList *paths)
{
PikaContainerEntry *container_entry = PIKA_CONTAINER_ENTRY (view);
GtkEntry *entry = GTK_ENTRY (view);
PikaViewable *viewable = NULL;
/* XXX Only support 1 selected viewable for now. */
if (viewables)
viewable = viewables->data;
g_signal_handlers_block_by_func (entry,
pika_container_entry_changed,
view);
g_set_weak_pointer (&container_entry->viewable, viewable);
if (viewable)
{
gtk_entry_set_icon_from_icon_name (entry,
GTK_ENTRY_ICON_SECONDARY,
NULL);
}
else
{
/* The selected item does not exist. */
gtk_entry_set_icon_from_icon_name (entry,
GTK_ENTRY_ICON_SECONDARY,
PIKA_ICON_MASCOT_EEK);
}
gtk_entry_set_text (entry, viewable? pika_object_get_name (viewable) : "");
g_signal_handlers_unblock_by_func (entry,
pika_container_entry_changed,
view);
return TRUE;
}
static void
pika_container_entry_clear_items (PikaContainerView *view)
{
GtkTreeModel *model = pika_container_entry_get_model (view);
/* model is NULL in dispose() */
if (model)
pika_container_tree_store_clear_items (PIKA_CONTAINER_TREE_STORE (model));
parent_view_iface->clear_items (view);
}
static void
pika_container_entry_set_view_size (PikaContainerView *view)
{
GtkTreeModel *model = pika_container_entry_get_model (view);
pika_container_tree_store_set_view_size (PIKA_CONTAINER_TREE_STORE (model));
}
static gint
pika_container_entry_get_selected (PikaContainerView *view,
GList **items,
GList **items_data)
{
PikaContainerEntry *container_entry = PIKA_CONTAINER_ENTRY (view);
if (items)
{
if (container_entry->viewable != NULL)
*items = g_list_prepend (NULL, container_entry->viewable);
else
*items = NULL;
}
return container_entry->viewable == NULL ? 0 : 1;
}
static void
pika_container_entry_changed (GtkEntry *entry,
PikaContainerView *view)
{
PikaContainerEntry *container_entry = PIKA_CONTAINER_ENTRY (entry);
PikaContainer *container = pika_container_view_get_container (view);
PikaObject *object;
const gchar *text;
if (! container)
return;
text = gtk_entry_get_text (entry);
object = pika_container_get_child_by_name (container, text);
g_set_weak_pointer (&container_entry->viewable, PIKA_VIEWABLE (object));
if (container_entry->viewable)
{
pika_container_view_item_selected (view, container_entry->viewable);
gtk_entry_set_icon_from_icon_name (entry,
GTK_ENTRY_ICON_SECONDARY,
NULL);
}
else
{
/* While editing the entry, show EEK for non-existent item. */
gtk_entry_set_icon_from_icon_name (entry,
GTK_ENTRY_ICON_SECONDARY,
PIKA_ICON_MASCOT_EEK);
}
}
static void
pika_container_entry_match_selected (GtkEntryCompletion *widget,
GtkTreeModel *model,
GtkTreeIter *iter,
PikaContainerView *view)
{
PikaViewRenderer *renderer;
gtk_tree_model_get (model, iter,
PIKA_CONTAINER_TREE_STORE_COLUMN_RENDERER, &renderer,
-1);
pika_container_view_item_selected (view, renderer->viewable);
g_object_unref (renderer);
}

View File

@ -0,0 +1,60 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikacontainerentry.h
* Copyright (C) 2004 Sven Neumann <sven@gimp.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __PIKA_CONTAINER_ENTRY_H__
#define __PIKA_CONTAINER_ENTRY_H__
#define PIKA_TYPE_CONTAINER_ENTRY (pika_container_entry_get_type ())
#define PIKA_CONTAINER_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIKA_TYPE_CONTAINER_ENTRY, PikaContainerEntry))
#define PIKA_CONTAINER_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIKA_TYPE_CONTAINER_ENTRY, PikaContainerEntryClass))
#define PIKA_IS_CONTAINER_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIKA_TYPE_CONTAINER_ENTRY))
#define PIKA_IS_CONTAINER_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIKA_TYPE_CONTAINER_ENTRY))
#define PIKA_CONTAINER_ENTRY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIKA_TYPE_CONTAINER_ENTRY, PikaContainerEntryClass))
typedef struct _PikaContainerEntryClass PikaContainerEntryClass;
struct _PikaContainerEntry
{
GtkEntry parent_instance;
PikaViewable *viewable;
};
struct _PikaContainerEntryClass
{
GtkEntryClass parent_class;
};
GType pika_container_entry_get_type (void) G_GNUC_CONST;
GtkWidget * pika_container_entry_new (PikaContainer *container,
PikaContext *context,
gint view_size,
gint view_border_width);
#endif /* __PIKA_CONTAINER_ENTRY_H__ */

View File

@ -0,0 +1,898 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikacontainericonview.c
* Copyright (C) 2010 Michael Natterer <mitch@gimp.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <string.h>
#include <gegl.h>
#include <gtk/gtk.h>
#include "libpikawidgets/pikawidgets.h"
#include "widgets-types.h"
#include "core/pikacontainer.h"
#include "core/pikacontext.h"
#include "core/pikaviewable.h"
#include "pikacellrendererviewable.h"
#include "pikacontainertreestore.h"
#include "pikacontainericonview.h"
#include "pikacontainerview.h"
#include "pikadnd.h"
#include "pikaviewrenderer.h"
#include "pikawidgets-utils.h"
struct _PikaContainerIconViewPrivate
{
PikaViewRenderer *dnd_renderer;
};
static void pika_container_icon_view_view_iface_init (PikaContainerViewInterface *iface);
static void pika_container_icon_view_constructed (GObject *object);
static void pika_container_icon_view_finalize (GObject *object);
static void pika_container_icon_view_unmap (GtkWidget *widget);
static gboolean pika_container_icon_view_popup_menu (GtkWidget *widget);
static void pika_container_icon_view_set_container (PikaContainerView *view,
PikaContainer *container);
static void pika_container_icon_view_set_context (PikaContainerView *view,
PikaContext *context);
static void pika_container_icon_view_set_selection_mode(PikaContainerView *view,
GtkSelectionMode mode);
static gpointer pika_container_icon_view_insert_item (PikaContainerView *view,
PikaViewable *viewable,
gpointer parent_insert_data,
gint index);
static void pika_container_icon_view_remove_item (PikaContainerView *view,
PikaViewable *viewable,
gpointer insert_data);
static void pika_container_icon_view_reorder_item (PikaContainerView *view,
PikaViewable *viewable,
gint new_index,
gpointer insert_data);
static void pika_container_icon_view_rename_item (PikaContainerView *view,
PikaViewable *viewable,
gpointer insert_data);
static gboolean pika_container_icon_view_select_items (PikaContainerView *view,
GList *items,
GList *paths);
static void pika_container_icon_view_clear_items (PikaContainerView *view);
static void pika_container_icon_view_set_view_size (PikaContainerView *view);
static void pika_container_icon_view_selection_changed (GtkIconView *view,
PikaContainerIconView *icon_view);
static void pika_container_icon_view_item_activated (GtkIconView *view,
GtkTreePath *path,
PikaContainerIconView *icon_view);
static gboolean pika_container_icon_view_button_press (GtkWidget *widget,
GdkEventButton *bevent,
PikaContainerIconView *icon_view);
static gboolean pika_container_icon_view_tooltip (GtkWidget *widget,
gint x,
gint y,
gboolean keyboard_tip,
GtkTooltip *tooltip,
PikaContainerIconView *icon_view);
static PikaViewable * pika_container_icon_view_drag_viewable (GtkWidget *widget,
PikaContext **context,
gpointer data);
static GdkPixbuf * pika_container_icon_view_drag_pixbuf (GtkWidget *widget,
gpointer data);
static gboolean pika_container_icon_view_get_selected_single (PikaContainerIconView *icon_view,
GtkTreeIter *iter);
static gint pika_container_icon_view_get_selected (PikaContainerView *view,
GList **items,
GList **paths);
G_DEFINE_TYPE_WITH_CODE (PikaContainerIconView, pika_container_icon_view,
PIKA_TYPE_CONTAINER_BOX,
G_ADD_PRIVATE (PikaContainerIconView)
G_IMPLEMENT_INTERFACE (PIKA_TYPE_CONTAINER_VIEW,
pika_container_icon_view_view_iface_init))
#define parent_class pika_container_icon_view_parent_class
static PikaContainerViewInterface *parent_view_iface = NULL;
static void
pika_container_icon_view_class_init (PikaContainerIconViewClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
object_class->constructed = pika_container_icon_view_constructed;
object_class->finalize = pika_container_icon_view_finalize;
widget_class->unmap = pika_container_icon_view_unmap;
widget_class->popup_menu = pika_container_icon_view_popup_menu;
}
static void
pika_container_icon_view_view_iface_init (PikaContainerViewInterface *iface)
{
parent_view_iface = g_type_interface_peek_parent (iface);
if (! parent_view_iface)
parent_view_iface = g_type_default_interface_peek (PIKA_TYPE_CONTAINER_VIEW);
iface->set_container = pika_container_icon_view_set_container;
iface->set_context = pika_container_icon_view_set_context;
iface->set_selection_mode = pika_container_icon_view_set_selection_mode;
iface->insert_item = pika_container_icon_view_insert_item;
iface->remove_item = pika_container_icon_view_remove_item;
iface->reorder_item = pika_container_icon_view_reorder_item;
iface->rename_item = pika_container_icon_view_rename_item;
iface->select_items = pika_container_icon_view_select_items;
iface->clear_items = pika_container_icon_view_clear_items;
iface->set_view_size = pika_container_icon_view_set_view_size;
iface->get_selected = pika_container_icon_view_get_selected;
iface->insert_data_free = (GDestroyNotify) gtk_tree_iter_free;
}
static void
pika_container_icon_view_init (PikaContainerIconView *icon_view)
{
PikaContainerBox *box = PIKA_CONTAINER_BOX (icon_view);
icon_view->priv = pika_container_icon_view_get_instance_private (icon_view);
pika_container_tree_store_columns_init (icon_view->model_columns,
&icon_view->n_model_columns);
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (box->scrolled_win),
GTK_SHADOW_IN);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (box->scrolled_win),
GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
}
static void
pika_container_icon_view_constructed (GObject *object)
{
PikaContainerIconView *icon_view = PIKA_CONTAINER_ICON_VIEW (object);
PikaContainerView *view = PIKA_CONTAINER_VIEW (object);
PikaContainerBox *box = PIKA_CONTAINER_BOX (object);
G_OBJECT_CLASS (parent_class)->constructed (object);
icon_view->model = pika_container_tree_store_new (view,
icon_view->n_model_columns,
icon_view->model_columns);
icon_view->view = g_object_new (GTK_TYPE_ICON_VIEW,
"model", icon_view->model,
"row-spacing", 0,
"column-spacing", 0,
"margin", 0,
"item-padding", 1,
"has-tooltip", TRUE,
NULL);
g_object_unref (icon_view->model);
gtk_container_add (GTK_CONTAINER (box->scrolled_win),
GTK_WIDGET (icon_view->view));
gtk_widget_show (GTK_WIDGET (icon_view->view));
pika_container_view_set_dnd_widget (view, GTK_WIDGET (icon_view->view));
icon_view->renderer_cell = pika_cell_renderer_viewable_new ();
gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (icon_view->view),
icon_view->renderer_cell,
FALSE);
gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (icon_view->view),
icon_view->renderer_cell,
"renderer", PIKA_CONTAINER_TREE_STORE_COLUMN_RENDERER,
NULL);
pika_container_tree_store_add_renderer_cell (PIKA_CONTAINER_TREE_STORE (icon_view->model),
icon_view->renderer_cell, -1);
g_signal_connect (icon_view->view, "selection-changed",
G_CALLBACK (pika_container_icon_view_selection_changed),
icon_view);
g_signal_connect (icon_view->view, "item-activated",
G_CALLBACK (pika_container_icon_view_item_activated),
icon_view);
g_signal_connect (icon_view->view, "query-tooltip",
G_CALLBACK (pika_container_icon_view_tooltip),
icon_view);
}
static void
pika_container_icon_view_finalize (GObject *object)
{
//PikaContainerIconView *icon_view = PIKA_CONTAINER_ICON_VIEW (object);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
pika_container_icon_view_unmap (GtkWidget *widget)
{
//PikaContainerIconView *icon_view = PIKA_CONTAINER_ICON_VIEW (widget);
GTK_WIDGET_CLASS (parent_class)->unmap (widget);
}
static gboolean
pika_container_icon_view_popup_menu (GtkWidget *widget)
{
PikaContainerIconView *icon_view = PIKA_CONTAINER_ICON_VIEW (widget);
GtkTreeIter iter;
GtkTreePath *path;
GdkRectangle rect;
if (!pika_container_icon_view_get_selected_single (icon_view, &iter))
return FALSE;
path = gtk_tree_model_get_path (icon_view->model, &iter);
gtk_icon_view_get_cell_rect (icon_view->view, path, NULL, &rect);
gtk_tree_path_free (path);
return pika_editor_popup_menu_at_rect (PIKA_EDITOR (widget),
gtk_widget_get_window (GTK_WIDGET (icon_view->view)),
&rect, GDK_GRAVITY_CENTER, GDK_GRAVITY_NORTH_WEST,
NULL);
}
GtkWidget *
pika_container_icon_view_new (PikaContainer *container,
PikaContext *context,
gint view_size,
gint view_border_width)
{
PikaContainerIconView *icon_view;
PikaContainerView *view;
g_return_val_if_fail (container == NULL || PIKA_IS_CONTAINER (container),
NULL);
g_return_val_if_fail (context == NULL || PIKA_IS_CONTEXT (context), NULL);
g_return_val_if_fail (view_size > 0 &&
view_size <= PIKA_VIEWABLE_MAX_PREVIEW_SIZE, NULL);
g_return_val_if_fail (view_border_width >= 0 &&
view_border_width <= PIKA_VIEW_MAX_BORDER_WIDTH,
NULL);
icon_view = g_object_new (PIKA_TYPE_CONTAINER_ICON_VIEW, NULL);
view = PIKA_CONTAINER_VIEW (icon_view);
pika_container_view_set_view_size (view, view_size, 0 /* ignore border */);
if (container)
pika_container_view_set_container (view, container);
if (context)
pika_container_view_set_context (view, context);
return GTK_WIDGET (icon_view);
}
/* PikaContainerView methods */
static void
pika_container_icon_view_set_container (PikaContainerView *view,
PikaContainer *container)
{
PikaContainerIconView *icon_view = PIKA_CONTAINER_ICON_VIEW (view);
PikaContainer *old_container;
old_container = pika_container_view_get_container (view);
if (old_container)
{
if (! container)
{
if (pika_dnd_viewable_source_remove (GTK_WIDGET (icon_view->view),
pika_container_get_children_type (old_container)))
{
if (PIKA_VIEWABLE_CLASS (g_type_class_peek (pika_container_get_children_type (old_container)))->get_size)
pika_dnd_pixbuf_source_remove (GTK_WIDGET (icon_view->view));
gtk_drag_source_unset (GTK_WIDGET (icon_view->view));
}
g_signal_handlers_disconnect_by_func (icon_view->view,
pika_container_icon_view_button_press,
icon_view);
}
}
else if (container)
{
if (pika_dnd_drag_source_set_by_type (GTK_WIDGET (icon_view->view),
GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
pika_container_get_children_type (container),
GDK_ACTION_COPY))
{
pika_dnd_viewable_source_add (GTK_WIDGET (icon_view->view),
pika_container_get_children_type (container),
pika_container_icon_view_drag_viewable,
icon_view);
if (PIKA_VIEWABLE_CLASS (g_type_class_peek (pika_container_get_children_type (container)))->get_size)
pika_dnd_pixbuf_source_add (GTK_WIDGET (icon_view->view),
pika_container_icon_view_drag_pixbuf,
icon_view);
}
g_signal_connect (icon_view->view, "button-press-event",
G_CALLBACK (pika_container_icon_view_button_press),
icon_view);
}
parent_view_iface->set_container (view, container);
}
static void
pika_container_icon_view_set_context (PikaContainerView *view,
PikaContext *context)
{
PikaContainerIconView *icon_view = PIKA_CONTAINER_ICON_VIEW (view);
parent_view_iface->set_context (view, context);
if (icon_view->model)
pika_container_tree_store_set_context (PIKA_CONTAINER_TREE_STORE (icon_view->model),
context);
}
static void
pika_container_icon_view_set_selection_mode (PikaContainerView *view,
GtkSelectionMode mode)
{
PikaContainerIconView *icon_view = PIKA_CONTAINER_ICON_VIEW (view);
gtk_icon_view_set_selection_mode (icon_view->view, mode);
parent_view_iface->set_selection_mode (view, mode);
}
static gpointer
pika_container_icon_view_insert_item (PikaContainerView *view,
PikaViewable *viewable,
gpointer parent_insert_data,
gint index)
{
PikaContainerIconView *icon_view = PIKA_CONTAINER_ICON_VIEW (view);
GtkTreeIter *iter;
iter = pika_container_tree_store_insert_item (PIKA_CONTAINER_TREE_STORE (icon_view->model),
viewable,
parent_insert_data,
index);
if (parent_insert_data)
{
#if 0
GtkTreePath *path = gtk_tree_model_get_path (icon_view->model, iter);
gtk_icon_view_expand_to_path (icon_view->view, path);
gtk_tree_path_free (path);
#endif
}
return iter;
}
static void
pika_container_icon_view_remove_item (PikaContainerView *view,
PikaViewable *viewable,
gpointer insert_data)
{
PikaContainerIconView *icon_view = PIKA_CONTAINER_ICON_VIEW (view);
pika_container_tree_store_remove_item (PIKA_CONTAINER_TREE_STORE (icon_view->model),
viewable,
insert_data);
}
static void
pika_container_icon_view_reorder_item (PikaContainerView *view,
PikaViewable *viewable,
gint new_index,
gpointer insert_data)
{
PikaContainerIconView *icon_view = PIKA_CONTAINER_ICON_VIEW (view);
GtkTreeIter *iter = (GtkTreeIter *) insert_data;
gboolean selected = FALSE;
if (iter)
{
GtkTreeIter selected_iter;
selected = pika_container_icon_view_get_selected_single (icon_view,
&selected_iter);
if (selected)
{
PikaViewRenderer *renderer;
gtk_tree_model_get (icon_view->model, &selected_iter,
PIKA_CONTAINER_TREE_STORE_COLUMN_RENDERER, &renderer,
-1);
if (renderer->viewable != viewable)
selected = FALSE;
g_object_unref (renderer);
}
}
pika_container_tree_store_reorder_item (PIKA_CONTAINER_TREE_STORE (icon_view->model),
viewable,
new_index,
iter);
if (selected)
pika_container_view_select_item (view, viewable);
}
static void
pika_container_icon_view_rename_item (PikaContainerView *view,
PikaViewable *viewable,
gpointer insert_data)
{
PikaContainerIconView *icon_view = PIKA_CONTAINER_ICON_VIEW (view);
GtkTreeIter *iter = (GtkTreeIter *) insert_data;
pika_container_tree_store_rename_item (PIKA_CONTAINER_TREE_STORE (icon_view->model),
viewable,
iter);
}
static gboolean
pika_container_icon_view_select_items (PikaContainerView *view,
GList *viewables,
GList *paths)
{
PikaContainerIconView *icon_view = PIKA_CONTAINER_ICON_VIEW (view);
GList *list;
if (viewables)
{
gboolean free_paths = FALSE;
if (g_list_length (paths) != g_list_length (viewables))
{
free_paths = TRUE;
paths = NULL;
for (list = viewables; list; list = list->next)
{
GtkTreeIter *iter;
GtkTreePath *path;
iter = pika_container_view_lookup (PIKA_CONTAINER_VIEW (view),
list->data);
if (! iter)
/* This may happen when the PikaContainerIconView only
* shows a subpart of the whole icons. We don't select
* what is not shown.
*/
continue;
path = gtk_tree_model_get_path (icon_view->model, iter);
paths = g_list_prepend (paths, path);
}
paths = g_list_reverse (paths);
}
g_signal_handlers_block_by_func (icon_view->view,
pika_container_icon_view_selection_changed,
icon_view);
gtk_icon_view_unselect_all (icon_view->view);
for (list = paths; list; list = list->next)
{
gtk_icon_view_select_path (icon_view->view, list->data);
}
if (list)
{
gtk_icon_view_set_cursor (icon_view->view, list->data, NULL, FALSE);
gtk_icon_view_scroll_to_path (icon_view->view, list->data, FALSE, 0.0, 0.0);
}
g_signal_handlers_unblock_by_func (icon_view->view,
pika_container_icon_view_selection_changed,
icon_view);
if (free_paths)
g_list_free_full (paths, (GDestroyNotify) gtk_tree_path_free);
}
else
{
gtk_icon_view_unselect_all (icon_view->view);
}
return TRUE;
}
static void
pika_container_icon_view_clear_items (PikaContainerView *view)
{
PikaContainerIconView *icon_view = PIKA_CONTAINER_ICON_VIEW (view);
pika_container_tree_store_clear_items (PIKA_CONTAINER_TREE_STORE (icon_view->model));
parent_view_iface->clear_items (view);
}
static void
pika_container_icon_view_set_view_size (PikaContainerView *view)
{
PikaContainerIconView *icon_view = PIKA_CONTAINER_ICON_VIEW (view);
if (icon_view->model)
pika_container_tree_store_set_view_size (PIKA_CONTAINER_TREE_STORE (icon_view->model));
if (icon_view->view)
{
gtk_icon_view_set_columns (icon_view->view, -1);
gtk_icon_view_set_item_width (icon_view->view, -1);
/* ugly workaround to force the icon view to invalidate all its
* cached icon sizes
*/
gtk_icon_view_set_item_orientation (icon_view->view,
GTK_ORIENTATION_VERTICAL);
gtk_icon_view_set_item_orientation (icon_view->view,
GTK_ORIENTATION_HORIZONTAL);
}
}
/* callbacks */
static void
pika_container_icon_view_selection_changed (GtkIconView *gtk_icon_view,
PikaContainerIconView *icon_view)
{
PikaContainerView *view = PIKA_CONTAINER_VIEW (icon_view);
GList *items = NULL;
GList *paths;
GList *list;
paths = gtk_icon_view_get_selected_items (icon_view->view);
for (list = paths; list; list = list->next)
{
GtkTreeIter iter;
PikaViewRenderer *renderer;
gtk_tree_model_get_iter (GTK_TREE_MODEL (icon_view->model), &iter,
(GtkTreePath *) list->data);
gtk_tree_model_get (icon_view->model, &iter,
PIKA_CONTAINER_TREE_STORE_COLUMN_RENDERER, &renderer,
-1);
if (renderer->viewable)
items = g_list_prepend (items, renderer->viewable);
g_object_unref (renderer);
}
pika_container_view_multi_selected (view, items, paths);
g_list_free_full (paths, (GDestroyNotify) gtk_tree_path_free);
g_list_free (items);
}
static void
pika_container_icon_view_item_activated (GtkIconView *view,
GtkTreePath *path,
PikaContainerIconView *icon_view)
{
GtkTreeIter iter;
PikaViewRenderer *renderer;
gtk_tree_model_get_iter (icon_view->model, &iter, path);
gtk_tree_model_get (icon_view->model, &iter,
PIKA_CONTAINER_TREE_STORE_COLUMN_RENDERER, &renderer,
-1);
pika_container_view_item_activated (PIKA_CONTAINER_VIEW (icon_view),
renderer->viewable);
g_object_unref (renderer);
}
static gboolean
pika_container_icon_view_button_press (GtkWidget *widget,
GdkEventButton *bevent,
PikaContainerIconView *icon_view)
{
PikaContainerView *container_view = PIKA_CONTAINER_VIEW (icon_view);
GtkTreePath *path;
icon_view->priv->dnd_renderer = NULL;
path = gtk_icon_view_get_path_at_pos (GTK_ICON_VIEW (widget),
bevent->x, bevent->y);
if (path)
{
PikaViewRenderer *renderer;
GtkTreeIter iter;
gtk_tree_model_get_iter (icon_view->model, &iter, path);
gtk_tree_model_get (icon_view->model, &iter,
PIKA_CONTAINER_TREE_STORE_COLUMN_RENDERER, &renderer,
-1);
icon_view->priv->dnd_renderer = renderer;
if (gdk_event_triggers_context_menu ((GdkEvent *) bevent))
{
/* If the clicked item is not selected, it becomes the new
* selection. Otherwise, we use the current selection. This
* allows to not break multiple selection when right-clicking.
*/
if (! pika_container_view_is_item_selected (container_view, renderer->viewable))
pika_container_view_item_selected (container_view, renderer->viewable);
/* Show the context menu. */
if (pika_container_view_get_container (container_view))
pika_editor_popup_menu_at_pointer (PIKA_EDITOR (icon_view), (GdkEvent *) bevent);
}
g_object_unref (renderer);
gtk_tree_path_free (path);
}
else
{
if (gdk_event_triggers_context_menu ((GdkEvent *) bevent))
{
pika_editor_popup_menu_at_pointer (PIKA_EDITOR (icon_view), (GdkEvent *) bevent);
}
return TRUE;
}
return FALSE;
}
static gboolean
pika_container_icon_view_tooltip (GtkWidget *widget,
gint x,
gint y,
gboolean keyboard_tip,
GtkTooltip *tooltip,
PikaContainerIconView *icon_view)
{
PikaViewRenderer *renderer;
GtkTreeIter iter;
GtkTreePath *path;
gboolean show_tip = FALSE;
if (! gtk_icon_view_get_tooltip_context (GTK_ICON_VIEW (widget), &x, &y,
keyboard_tip,
NULL, &path, &iter))
return FALSE;
gtk_tree_model_get (icon_view->model, &iter,
PIKA_CONTAINER_TREE_STORE_COLUMN_RENDERER, &renderer,
-1);
if (renderer)
{
gchar *desc;
gchar *tip;
desc = pika_viewable_get_description (renderer->viewable, &tip);
if (tip)
{
gtk_tooltip_set_text (tooltip, tip);
gtk_icon_view_set_tooltip_cell (GTK_ICON_VIEW (widget), tooltip, path,
icon_view->renderer_cell);
show_tip = TRUE;
g_free (tip);
}
g_free (desc);
g_object_unref (renderer);
}
gtk_tree_path_free (path);
return show_tip;
}
static PikaViewable *
pika_container_icon_view_drag_viewable (GtkWidget *widget,
PikaContext **context,
gpointer data)
{
PikaContainerIconView *icon_view = PIKA_CONTAINER_ICON_VIEW (data);
if (context)
*context = pika_container_view_get_context (PIKA_CONTAINER_VIEW (data));
if (icon_view->priv->dnd_renderer)
return icon_view->priv->dnd_renderer->viewable;
return NULL;
}
static GdkPixbuf *
pika_container_icon_view_drag_pixbuf (GtkWidget *widget,
gpointer data)
{
PikaContainerIconView *icon_view = PIKA_CONTAINER_ICON_VIEW (data);
PikaViewRenderer *renderer = icon_view->priv->dnd_renderer;
gint width;
gint height;
if (renderer && pika_viewable_get_size (renderer->viewable, &width, &height))
return pika_viewable_get_new_pixbuf (renderer->viewable,
renderer->context,
width, height);
return NULL;
}
static gboolean
pika_container_icon_view_get_selected_single (PikaContainerIconView *icon_view,
GtkTreeIter *iter)
{
GList *selected_items;
gboolean retval;
selected_items = gtk_icon_view_get_selected_items (icon_view->view);
if (g_list_length (selected_items) == 1)
{
gtk_tree_model_get_iter (GTK_TREE_MODEL (icon_view->model), iter,
(GtkTreePath *) selected_items->data);
retval = TRUE;
}
else
{
retval = FALSE;
}
g_list_free_full (selected_items, (GDestroyNotify) gtk_tree_path_free);
return retval;
}
static gint
pika_container_icon_view_get_selected (PikaContainerView *view,
GList **items,
GList **paths)
{
PikaContainerIconView *icon_view = PIKA_CONTAINER_ICON_VIEW (view);
GList *selected_paths;
gint selected_count;
PikaContainer *container;
container = pika_container_view_get_container (view);
if (container)
{
const gchar *signal_name;
PikaContext *context;
GType children_type;
context = pika_container_view_get_context (view);
children_type = pika_container_get_children_type (container);
signal_name = pika_context_type_to_signal_name (children_type);
/* As a special case, for containers tied to a context object, we
* look up this object as being selected.
* */
if (signal_name && context)
{
PikaObject *object;
object = pika_context_get_by_type (context, children_type);
selected_count = object ? 1 : 0;
if (items)
{
if (object)
*items = g_list_prepend (NULL, object);
else
*items = NULL;
}
if (paths)
*paths = NULL;
return selected_count;
}
}
selected_paths = gtk_icon_view_get_selected_items (icon_view->view);
selected_count = g_list_length (selected_paths);
if (items)
{
GList *removed_paths = NULL;
GList *list;
*items = NULL;
for (list = selected_paths;
list;
list = g_list_next (list))
{
GtkTreeIter iter;
PikaViewRenderer *renderer;
gtk_tree_model_get_iter (GTK_TREE_MODEL (icon_view->model), &iter,
(GtkTreePath *) list->data);
gtk_tree_model_get (icon_view->model, &iter,
PIKA_CONTAINER_TREE_STORE_COLUMN_RENDERER, &renderer,
-1);
if (renderer->viewable)
*items = g_list_prepend (*items, renderer->viewable);
else
/* Remove from the selected_paths list but at the end, in order not
* to break the for loop.
*/
removed_paths = g_list_prepend (removed_paths, list);
g_object_unref (renderer);
}
*items = g_list_reverse (*items);
for (list = removed_paths; list; list = list->next)
{
GList *remove_list = list->data;
selected_paths = g_list_remove_link (selected_paths, remove_list);
gtk_tree_path_free (remove_list->data);
}
g_list_free_full (removed_paths, (GDestroyNotify) g_list_free);
}
if (paths)
*paths = selected_paths;
else
g_list_free_full (selected_paths, (GDestroyNotify) gtk_tree_path_free);
return selected_count;
}

View File

@ -0,0 +1,74 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikacontainericonview.h
* Copyright (C) 2010 Michael Natterer <mitch@gimp.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __PIKA_CONTAINER_ICON_VIEW_H__
#define __PIKA_CONTAINER_ICON_VIEW_H__
#include "pikacontainerbox.h"
#define PIKA_TYPE_CONTAINER_ICON_VIEW (pika_container_icon_view_get_type ())
#define PIKA_CONTAINER_ICON_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIKA_TYPE_CONTAINER_ICON_VIEW, PikaContainerIconView))
#define PIKA_CONTAINER_ICON_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIKA_TYPE_CONTAINER_ICON_VIEW, PikaContainerIconViewClass))
#define PIKA_IS_CONTAINER_ICON_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIKA_TYPE_CONTAINER_ICON_VIEW))
#define PIKA_IS_CONTAINER_ICON_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIKA_TYPE_CONTAINER_ICON_VIEW))
#define PIKA_CONTAINER_ICON_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIKA_TYPE_CONTAINER_ICON_VIEW, PikaContainerIconViewClass))
typedef struct _PikaContainerIconViewClass PikaContainerIconViewClass;
typedef struct _PikaContainerIconViewPrivate PikaContainerIconViewPrivate;
struct _PikaContainerIconView
{
PikaContainerBox parent_instance;
GtkTreeModel *model;
gint n_model_columns;
GType model_columns[16];
GtkIconView *view;
GtkCellRenderer *renderer_cell;
Pika *dnd_pika; /* eek */
PikaContainerIconViewPrivate *priv;
};
struct _PikaContainerIconViewClass
{
PikaContainerBoxClass parent_class;
};
GType pika_container_icon_view_get_type (void) G_GNUC_CONST;
GtkWidget * pika_container_icon_view_new (PikaContainer *container,
PikaContext *context,
gint view_size,
gint view_border_width);
#endif /* __PIKA_CONTAINER_ICON_VIEW_H__ */

View File

@ -0,0 +1,443 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikacontainerpopup.c
* Copyright (C) 2003-2014 Michael Natterer <mitch@gimp.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gegl.h>
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
#include "libpikawidgets/pikawidgets.h"
#include "widgets-types.h"
#include "core/pika.h"
#include "core/pikacontext.h"
#include "core/pikacontainer.h"
#include "core/pikaviewable.h"
#include "pikacontainerbox.h"
#include "pikacontainereditor.h"
#include "pikacontainerpopup.h"
#include "pikacontainertreeview.h"
#include "pikacontainerview.h"
#include "pikadialogfactory.h"
#include "pikaviewrenderer.h"
#include "pikawidgets-utils.h"
#include "pikawindowstrategy.h"
#include "pika-intl.h"
static void pika_container_popup_finalize (GObject *object);
static void pika_container_popup_confirm (PikaPopup *popup);
static void pika_container_popup_create_view (PikaContainerPopup *popup);
static void pika_container_popup_smaller_clicked (GtkWidget *button,
PikaContainerPopup *popup);
static void pika_container_popup_larger_clicked (GtkWidget *button,
PikaContainerPopup *popup);
static void pika_container_popup_view_type_toggled(GtkWidget *button,
PikaContainerPopup *popup);
static void pika_container_popup_dialog_clicked (GtkWidget *button,
PikaContainerPopup *popup);
static void pika_container_popup_context_changed (PikaContext *context,
PikaViewable *viewable,
PikaContainerPopup *popup);
G_DEFINE_TYPE (PikaContainerPopup, pika_container_popup, PIKA_TYPE_POPUP)
#define parent_class pika_container_popup_parent_class
static void
pika_container_popup_class_init (PikaContainerPopupClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
PikaPopupClass *popup_class = PIKA_POPUP_CLASS (klass);
object_class->finalize = pika_container_popup_finalize;
popup_class->confirm = pika_container_popup_confirm;
}
static void
pika_container_popup_init (PikaContainerPopup *popup)
{
popup->view_type = PIKA_VIEW_TYPE_LIST;
popup->default_view_size = PIKA_VIEW_SIZE_SMALL;
popup->view_size = PIKA_VIEW_SIZE_SMALL;
popup->view_border_width = 1;
popup->frame = gtk_frame_new (NULL);
gtk_frame_set_shadow_type (GTK_FRAME (popup->frame), GTK_SHADOW_OUT);
gtk_container_add (GTK_CONTAINER (popup), popup->frame);
gtk_widget_show (popup->frame);
}
static void
pika_container_popup_finalize (GObject *object)
{
PikaContainerPopup *popup = PIKA_CONTAINER_POPUP (object);
g_clear_object (&popup->context);
g_clear_pointer (&popup->dialog_identifier, g_free);
g_clear_pointer (&popup->dialog_icon_name, g_free);
g_clear_pointer (&popup->dialog_tooltip, g_free);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
pika_container_popup_confirm (PikaPopup *popup)
{
PikaContainerPopup *c_popup = PIKA_CONTAINER_POPUP (popup);
PikaObject *object;
object = pika_context_get_by_type (c_popup->context,
pika_container_get_children_type (c_popup->container));
pika_context_set_by_type (c_popup->orig_context,
pika_container_get_children_type (c_popup->container),
object);
PIKA_POPUP_CLASS (parent_class)->confirm (popup);
}
GtkWidget *
pika_container_popup_new (PikaContainer *container,
PikaContext *context,
PikaViewType view_type,
gint default_view_size,
gint view_size,
gint view_border_width,
PikaDialogFactory *dialog_factory,
const gchar *dialog_identifier,
const gchar *dialog_icon_name,
const gchar *dialog_tooltip)
{
PikaContainerPopup *popup;
g_return_val_if_fail (PIKA_IS_CONTAINER (container), NULL);
g_return_val_if_fail (PIKA_IS_CONTEXT (context), NULL);
g_return_val_if_fail (default_view_size > 0 &&
default_view_size <= PIKA_VIEWABLE_MAX_POPUP_SIZE,
NULL);
g_return_val_if_fail (view_size > 0 &&
view_size <= PIKA_VIEWABLE_MAX_POPUP_SIZE, NULL);
g_return_val_if_fail (view_border_width >= 0 &&
view_border_width <= PIKA_VIEW_MAX_BORDER_WIDTH,
NULL);
g_return_val_if_fail (dialog_factory == NULL ||
PIKA_IS_DIALOG_FACTORY (dialog_factory), NULL);
if (dialog_factory)
{
g_return_val_if_fail (dialog_identifier != NULL, NULL);
g_return_val_if_fail (dialog_icon_name != NULL, NULL);
g_return_val_if_fail (dialog_tooltip != NULL, NULL);
}
popup = g_object_new (PIKA_TYPE_CONTAINER_POPUP,
"type", GTK_WINDOW_POPUP,
NULL);
gtk_window_set_resizable (GTK_WINDOW (popup), FALSE);
popup->container = container;
popup->orig_context = context;
popup->context = pika_context_new (context->pika, "popup", context);
popup->view_type = view_type;
popup->default_view_size = default_view_size;
popup->view_size = view_size;
popup->view_border_width = view_border_width;
g_signal_connect (popup->context,
pika_context_type_to_signal_name (pika_container_get_children_type (container)),
G_CALLBACK (pika_container_popup_context_changed),
popup);
if (dialog_factory)
{
popup->dialog_factory = dialog_factory;
popup->dialog_identifier = g_strdup (dialog_identifier);
popup->dialog_icon_name = g_strdup (dialog_icon_name);
popup->dialog_tooltip = g_strdup (dialog_tooltip);
}
pika_container_popup_create_view (popup);
return GTK_WIDGET (popup);
}
PikaViewType
pika_container_popup_get_view_type (PikaContainerPopup *popup)
{
g_return_val_if_fail (PIKA_IS_CONTAINER_POPUP (popup), PIKA_VIEW_TYPE_LIST);
return popup->view_type;
}
void
pika_container_popup_set_view_type (PikaContainerPopup *popup,
PikaViewType view_type)
{
g_return_if_fail (PIKA_IS_CONTAINER_POPUP (popup));
if (view_type != popup->view_type)
{
popup->view_type = view_type;
gtk_widget_destroy (GTK_WIDGET (popup->editor));
pika_container_popup_create_view (popup);
}
}
gint
pika_container_popup_get_view_size (PikaContainerPopup *popup)
{
g_return_val_if_fail (PIKA_IS_CONTAINER_POPUP (popup), PIKA_VIEW_SIZE_SMALL);
return popup->view_size;
}
void
pika_container_popup_set_view_size (PikaContainerPopup *popup,
gint view_size)
{
GtkWidget *scrolled_win;
GtkWidget *viewport;
GtkAllocation allocation;
g_return_if_fail (PIKA_IS_CONTAINER_POPUP (popup));
scrolled_win = PIKA_CONTAINER_BOX (popup->editor->view)->scrolled_win;
viewport = gtk_bin_get_child (GTK_BIN (scrolled_win));
gtk_widget_get_allocation (viewport, &allocation);
view_size = CLAMP (view_size, PIKA_VIEW_SIZE_TINY,
MIN (PIKA_VIEW_SIZE_GIGANTIC,
allocation.width - 2 * popup->view_border_width));
if (view_size != popup->view_size)
{
popup->view_size = view_size;
pika_container_view_set_view_size (popup->editor->view,
popup->view_size,
popup->view_border_width);
}
}
/* private functions */
static void
pika_container_popup_create_view (PikaContainerPopup *popup)
{
PikaEditor *editor;
GtkWidget *button;
const gchar *signal_name;
GType children_type;
gint rows;
gint columns;
popup->editor = g_object_new (PIKA_TYPE_CONTAINER_EDITOR,
"view-type", popup->view_type,
"container", popup->container,
"context", popup->context,
"view-size", popup->view_size,
"view-border-width", popup->view_border_width,
NULL);
pika_container_view_set_reorderable (PIKA_CONTAINER_VIEW (popup->editor->view),
FALSE);
if (popup->view_type == PIKA_VIEW_TYPE_LIST)
{
GtkWidget *search_entry;
search_entry = gtk_entry_new ();
gtk_box_pack_end (GTK_BOX (popup->editor->view), search_entry,
FALSE, FALSE, 0);
gtk_tree_view_set_search_entry (GTK_TREE_VIEW (PIKA_CONTAINER_TREE_VIEW (PIKA_CONTAINER_VIEW (popup->editor->view))->view),
GTK_ENTRY (search_entry));
gtk_widget_show (search_entry);
}
/* lame workaround for bug #761998 */
if (popup->default_view_size >= PIKA_VIEW_SIZE_LARGE)
{
rows = 6;
columns = 6;
}
else
{
rows = 10;
columns = 6;
}
pika_container_box_set_size_request (PIKA_CONTAINER_BOX (popup->editor->view),
columns * (popup->default_view_size +
2 * popup->view_border_width),
rows * (popup->default_view_size +
2 * popup->view_border_width));
if (PIKA_IS_EDITOR (popup->editor->view))
pika_editor_set_show_name (PIKA_EDITOR (popup->editor->view), FALSE);
gtk_container_add (GTK_CONTAINER (popup->frame), GTK_WIDGET (popup->editor));
gtk_widget_show (GTK_WIDGET (popup->editor));
editor = PIKA_EDITOR (popup->editor->view);
pika_editor_add_button (editor, "zoom-out",
_("Smaller Previews"), NULL,
G_CALLBACK (pika_container_popup_smaller_clicked),
NULL,
G_OBJECT (popup));
pika_editor_add_button (editor, "zoom-in",
_("Larger Previews"), NULL,
G_CALLBACK (pika_container_popup_larger_clicked),
NULL,
G_OBJECT (popup));
button = pika_editor_add_icon_box (editor, PIKA_TYPE_VIEW_TYPE, "pika",
G_CALLBACK (pika_container_popup_view_type_toggled),
popup);
pika_int_radio_group_set_active (GTK_RADIO_BUTTON (button), popup->view_type);
if (popup->dialog_factory)
pika_editor_add_button (editor, popup->dialog_icon_name,
popup->dialog_tooltip, NULL,
G_CALLBACK (pika_container_popup_dialog_clicked),
NULL,
G_OBJECT (popup));
gtk_widget_grab_focus (GTK_WIDGET (popup->editor));
/* Special-casing the object types managed by the context to make sure
* the right items are selected when opening the popup.
*/
children_type = pika_container_get_children_type (popup->container);
signal_name = pika_context_type_to_signal_name (children_type);
if (signal_name)
{
PikaObject *object;
GList *items = NULL;
object = pika_context_get_by_type (popup->orig_context, children_type);
if (object)
items = g_list_prepend (NULL, object);
pika_container_view_select_items (popup->editor->view, items);
g_list_free (items);
}
}
static void
pika_container_popup_smaller_clicked (GtkWidget *button,
PikaContainerPopup *popup)
{
gint view_size;
view_size = pika_container_view_get_view_size (popup->editor->view, NULL);
pika_container_popup_set_view_size (popup, view_size * 0.8);
}
static void
pika_container_popup_larger_clicked (GtkWidget *button,
PikaContainerPopup *popup)
{
gint view_size;
view_size = pika_container_view_get_view_size (popup->editor->view, NULL);
pika_container_popup_set_view_size (popup, view_size * 1.2);
}
static void
pika_container_popup_view_type_toggled (GtkWidget *button,
PikaContainerPopup *popup)
{
if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
{
PikaViewType view_type;
view_type = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (button),
"pika-item-data"));
pika_container_popup_set_view_type (popup, view_type);
}
}
static void
pika_container_popup_dialog_clicked (GtkWidget *button,
PikaContainerPopup *popup)
{
pika_window_strategy_show_dockable_dialog (PIKA_WINDOW_STRATEGY (pika_get_window_strategy (popup->context->pika)),
popup->context->pika,
popup->dialog_factory,
pika_widget_get_monitor (button),
popup->dialog_identifier);
g_signal_emit_by_name (popup, "cancel");
}
static void
pika_container_popup_context_changed (PikaContext *context,
PikaViewable *viewable,
PikaContainerPopup *popup)
{
GdkEvent *current_event;
GtkWidget *current_widget = GTK_WIDGET (popup);
gboolean confirm = FALSE;
current_event = gtk_get_current_event ();
if (current_event && gtk_widget_get_window (current_widget))
{
GdkWindow *event_window = gdk_window_get_effective_toplevel (((GdkEventAny *) current_event)->window);
GdkWindow *popup_window = gdk_window_get_effective_toplevel (gtk_widget_get_window (current_widget));
/* We need to differentiate a context change as a consequence of
* an event on another widget.
*/
if ((((GdkEventAny *) current_event)->type == GDK_BUTTON_PRESS ||
((GdkEventAny *) current_event)->type == GDK_BUTTON_RELEASE) &&
event_window == popup_window)
confirm = TRUE;
gdk_event_free (current_event);
}
if (confirm)
g_signal_emit_by_name (popup, "confirm");
}

View File

@ -0,0 +1,92 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikacontainerpopup.h
* Copyright (C) 2003-2014 Michael Natterer <mitch@gimp.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __PIKA_CONTAINER_POPUP_H__
#define __PIKA_CONTAINER_POPUP_H__
#include "pikapopup.h"
#define PIKA_TYPE_CONTAINER_POPUP (pika_container_popup_get_type ())
#define PIKA_CONTAINER_POPUP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIKA_TYPE_CONTAINER_POPUP, PikaContainerPopup))
#define PIKA_CONTAINER_POPUP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIKA_TYPE_CONTAINER_POPUP, PikaContainerPopupClass))
#define PIKA_IS_CONTAINER_POPUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIKA_TYPE_CONTAINER_POPUP))
#define PIKA_IS_CONTAINER_POPUP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIKA_TYPE_CONTAINER_POPUP))
#define PIKA_CONTAINER_POPUP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIKA_TYPE_CONTAINER_POPUP, PikaContainerPopupClass))
typedef struct _PikaContainerPopupClass PikaContainerPopupClass;
struct _PikaContainerPopup
{
PikaPopup parent_instance;
PikaContainer *container;
PikaContext *orig_context;
PikaContext *context;
PikaViewType view_type;
gint default_view_size;
gint view_size;
gint view_border_width;
GtkWidget *frame;
PikaContainerEditor *editor;
PikaDialogFactory *dialog_factory;
gchar *dialog_identifier;
gchar *dialog_icon_name;
gchar *dialog_tooltip;
};
struct _PikaContainerPopupClass
{
PikaPopupClass parent_instance;
};
GType pika_container_popup_get_type (void) G_GNUC_CONST;
GtkWidget * pika_container_popup_new (PikaContainer *container,
PikaContext *context,
PikaViewType view_type,
gint default_view_size,
gint view_size,
gint view_border_width,
PikaDialogFactory *dialog_factory,
const gchar *dialog_identifier,
const gchar *dialog_icon_name,
const gchar *dialog_tooltip);
PikaViewType pika_container_popup_get_view_type (PikaContainerPopup *popup);
void pika_container_popup_set_view_type (PikaContainerPopup *popup,
PikaViewType view_type);
gint pika_container_popup_get_view_size (PikaContainerPopup *popup);
void pika_container_popup_set_view_size (PikaContainerPopup *popup,
gint view_size);
#endif /* __PIKA_CONTAINER_POPUP_H__ */

View File

@ -0,0 +1,648 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikacontainertreestore.c
* Copyright (C) 2010 Michael Natterer <mitch@gimp.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <string.h>
#include <gegl.h>
#include <gtk/gtk.h>
#include "widgets-types.h"
#include "libpikabase/pikabase.h"
#include "core/pikacontainer.h"
#include "core/pikaviewable.h"
#include "pikacellrendererviewable.h"
#include "pikacontainertreestore.h"
#include "pikacontainerview.h"
#include "pikaviewrenderer.h"
enum
{
PROP_0,
PROP_CONTAINER_VIEW,
PROP_USE_NAME
};
typedef struct _PikaContainerTreeStorePrivate PikaContainerTreeStorePrivate;
struct _PikaContainerTreeStorePrivate
{
PikaContainerView *container_view;
GList *renderer_cells;
GList *renderer_columns;
gboolean use_name;
};
#define GET_PRIVATE(store) \
((PikaContainerTreeStorePrivate *) pika_container_tree_store_get_instance_private ((PikaContainerTreeStore *) (store)))
static void pika_container_tree_store_constructed (GObject *object);
static void pika_container_tree_store_finalize (GObject *object);
static void pika_container_tree_store_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
static void pika_container_tree_store_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
static void pika_container_tree_store_set (PikaContainerTreeStore *store,
GtkTreeIter *iter,
PikaViewable *viewable);
static void pika_container_tree_store_renderer_update (PikaViewRenderer *renderer,
PikaContainerTreeStore *store);
G_DEFINE_TYPE_WITH_PRIVATE (PikaContainerTreeStore, pika_container_tree_store,
GTK_TYPE_TREE_STORE)
#define parent_class pika_container_tree_store_parent_class
static void
pika_container_tree_store_class_init (PikaContainerTreeStoreClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->constructed = pika_container_tree_store_constructed;
object_class->finalize = pika_container_tree_store_finalize;
object_class->set_property = pika_container_tree_store_set_property;
object_class->get_property = pika_container_tree_store_get_property;
g_object_class_install_property (object_class, PROP_CONTAINER_VIEW,
g_param_spec_object ("container-view",
NULL, NULL,
PIKA_TYPE_CONTAINER_VIEW,
PIKA_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property (object_class, PROP_USE_NAME,
g_param_spec_boolean ("use-name",
NULL, NULL,
FALSE,
PIKA_PARAM_READWRITE));
}
static void
pika_container_tree_store_init (PikaContainerTreeStore *store)
{
}
static void
pika_container_tree_store_constructed (GObject *object)
{
G_OBJECT_CLASS (parent_class)->constructed (object);
}
static void
pika_container_tree_store_finalize (GObject *object)
{
PikaContainerTreeStorePrivate *private = GET_PRIVATE (object);
g_list_free (private->renderer_cells);
g_list_free (private->renderer_columns);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
pika_container_tree_store_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
PikaContainerTreeStorePrivate *private = GET_PRIVATE (object);
switch (property_id)
{
case PROP_CONTAINER_VIEW:
private->container_view = g_value_get_object (value); /* don't ref */
break;
case PROP_USE_NAME:
private->use_name = g_value_get_boolean (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
pika_container_tree_store_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
PikaContainerTreeStorePrivate *private = GET_PRIVATE (object);
switch (property_id)
{
case PROP_CONTAINER_VIEW:
g_value_set_object (value, private->container_view);
break;
case PROP_USE_NAME:
g_value_set_boolean (value, private->use_name);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
/* public functions */
GtkTreeModel *
pika_container_tree_store_new (PikaContainerView *container_view,
gint n_columns,
GType *types)
{
PikaContainerTreeStore *store;
g_return_val_if_fail (PIKA_IS_CONTAINER_VIEW (container_view), NULL);
g_return_val_if_fail (n_columns >= PIKA_CONTAINER_TREE_STORE_N_COLUMNS, NULL);
g_return_val_if_fail (types != NULL, NULL);
store = g_object_new (PIKA_TYPE_CONTAINER_TREE_STORE,
"container-view", container_view,
NULL);
gtk_tree_store_set_column_types (GTK_TREE_STORE (store), n_columns, types);
return GTK_TREE_MODEL (store);
}
void
pika_container_tree_store_add_renderer_cell (PikaContainerTreeStore *store,
GtkCellRenderer *cell,
gint column_number)
{
PikaContainerTreeStorePrivate *private;
g_return_if_fail (PIKA_IS_CONTAINER_TREE_STORE (store));
g_return_if_fail (PIKA_IS_CELL_RENDERER_VIEWABLE (cell));
private = GET_PRIVATE (store);
private->renderer_cells = g_list_prepend (private->renderer_cells, cell);
if (column_number >= 0)
private->renderer_columns = g_list_append (private->renderer_columns,
GINT_TO_POINTER (column_number));
}
PikaViewRenderer *
pika_container_tree_store_get_renderer (PikaContainerTreeStore *store,
GtkTreeIter *iter)
{
PikaContainerTreeStorePrivate *private;
PikaViewRenderer *renderer = NULL;
GList *c;
g_return_val_if_fail (PIKA_IS_CONTAINER_TREE_STORE (store), NULL);
private = GET_PRIVATE (store);
for (c = private->renderer_columns; c; c = c->next)
{
gtk_tree_model_get (GTK_TREE_MODEL (store), iter,
GPOINTER_TO_INT (c->data), &renderer,
-1);
if (renderer)
break;
}
g_return_val_if_fail (renderer != NULL, NULL);
return renderer;
}
void
pika_container_tree_store_set_use_name (PikaContainerTreeStore *store,
gboolean use_name)
{
PikaContainerTreeStorePrivate *private;
g_return_if_fail (PIKA_IS_CONTAINER_TREE_STORE (store));
private = GET_PRIVATE (store);
if (private->use_name != use_name)
{
private->use_name = use_name ? TRUE : FALSE;
g_object_notify (G_OBJECT (store), "use-name");
}
}
gboolean
pika_container_tree_store_get_use_name (PikaContainerTreeStore *store)
{
g_return_val_if_fail (PIKA_IS_CONTAINER_TREE_STORE (store), FALSE);
return GET_PRIVATE (store)->use_name;
}
static gboolean
pika_container_tree_store_set_context_foreach (GtkTreeModel *model,
GtkTreePath *path,
GtkTreeIter *iter,
gpointer data)
{
PikaContext *context = data;
PikaViewRenderer *renderer;
gtk_tree_model_get (model, iter,
PIKA_CONTAINER_TREE_STORE_COLUMN_RENDERER, &renderer,
-1);
pika_view_renderer_set_context (renderer, context);
g_object_unref (renderer);
return FALSE;
}
void
pika_container_tree_store_set_context (PikaContainerTreeStore *store,
PikaContext *context)
{
g_return_if_fail (PIKA_IS_CONTAINER_TREE_STORE (store));
gtk_tree_model_foreach (GTK_TREE_MODEL (store),
pika_container_tree_store_set_context_foreach,
context);
}
GtkTreeIter *
pika_container_tree_store_insert_item (PikaContainerTreeStore *store,
PikaViewable *viewable,
GtkTreeIter *parent,
gint index)
{
GtkTreeIter iter;
g_return_val_if_fail (PIKA_IS_CONTAINER_TREE_STORE (store), NULL);
if (index == -1)
gtk_tree_store_append (GTK_TREE_STORE (store), &iter, parent);
else
gtk_tree_store_insert (GTK_TREE_STORE (store), &iter, parent, index);
pika_container_tree_store_set (store, &iter, viewable);
return gtk_tree_iter_copy (&iter);
}
void
pika_container_tree_store_remove_item (PikaContainerTreeStore *store,
PikaViewable *viewable,
GtkTreeIter *iter)
{
if (iter)
{
GtkTreeModel *model = GTK_TREE_MODEL (store);
GtkTreePath *path;
/* emit a "row-changed" signal for 'iter', so that editing of
* corresponding tree-view rows is canceled. otherwise, if we remove the
* item while a corresponding row is being edited, bad things happen (see
* bug #792991).
*/
path = gtk_tree_model_get_path (model, iter);
gtk_tree_model_row_changed (model, path, iter);
gtk_tree_path_free (path);
gtk_tree_store_remove (GTK_TREE_STORE (store), iter);
/* If the store is empty after this remove, clear out renderers
* from all cells so they don't keep refing the viewables
* (see bug #149906).
*/
if (! gtk_tree_model_iter_n_children (model, NULL))
{
PikaContainerTreeStorePrivate *private = GET_PRIVATE (store);
GList *list;
for (list = private->renderer_cells; list; list = list->next)
g_object_set (list->data, "renderer", NULL, NULL);
}
}
}
void
pika_container_tree_store_reorder_item (PikaContainerTreeStore *store,
PikaViewable *viewable,
gint new_index,
GtkTreeIter *iter)
{
PikaContainerTreeStorePrivate *private;
PikaViewable *parent;
PikaContainer *container;
g_return_if_fail (PIKA_IS_CONTAINER_TREE_STORE (store));
private = GET_PRIVATE (store);
if (! iter)
return;
parent = pika_viewable_get_parent (viewable);
if (parent)
container = pika_viewable_get_children (parent);
else
container = pika_container_view_get_container (private->container_view);
if (new_index == -1 ||
new_index == pika_container_get_n_children (container) - 1)
{
gtk_tree_store_move_before (GTK_TREE_STORE (store), iter, NULL);
}
else if (new_index == 0)
{
gtk_tree_store_move_after (GTK_TREE_STORE (store), iter, NULL);
}
else
{
GtkTreePath *path;
GtkTreeIter place_iter;
gint depth;
gint *indices;
gint old_index;
path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), iter);
indices = gtk_tree_path_get_indices (path);
depth = gtk_tree_path_get_depth (path);
old_index = indices[depth - 1];
if (new_index != old_index)
{
indices[depth - 1] = new_index;
gtk_tree_model_get_iter (GTK_TREE_MODEL (store), &place_iter, path);
if (new_index > old_index)
gtk_tree_store_move_after (GTK_TREE_STORE (store),
iter, &place_iter);
else
gtk_tree_store_move_before (GTK_TREE_STORE (store),
iter, &place_iter);
}
gtk_tree_path_free (path);
}
}
gboolean
pika_container_tree_store_rename_item (PikaContainerTreeStore *store,
PikaViewable *viewable,
GtkTreeIter *iter)
{
gboolean new_name_shorter = FALSE;
g_return_val_if_fail (PIKA_IS_CONTAINER_TREE_STORE (store), FALSE);
if (iter)
{
PikaContainerTreeStorePrivate *private = GET_PRIVATE (store);
gchar *name;
gchar *old_name;
if (private->use_name)
name = (gchar *) pika_object_get_name (viewable);
else
name = pika_viewable_get_description (viewable, NULL);
gtk_tree_model_get (GTK_TREE_MODEL (store), iter,
PIKA_CONTAINER_TREE_STORE_COLUMN_NAME, &old_name,
-1);
gtk_tree_store_set (GTK_TREE_STORE (store), iter,
PIKA_CONTAINER_TREE_STORE_COLUMN_NAME, name,
-1);
if (name && old_name && strlen (name) < strlen (old_name))
new_name_shorter = TRUE;
if (! private->use_name)
g_free (name);
g_free (old_name);
}
return new_name_shorter;
}
void
pika_container_tree_store_clear_items (PikaContainerTreeStore *store)
{
g_return_if_fail (PIKA_IS_CONTAINER_TREE_STORE (store));
gtk_tree_store_clear (GTK_TREE_STORE (store));
/* If the store is empty after this remove, clear out renderers
* from all cells so they don't keep refing the viewables
* (see bug #149906).
*/
if (! gtk_tree_model_iter_n_children (GTK_TREE_MODEL (store), NULL))
{
PikaContainerTreeStorePrivate *private = GET_PRIVATE (store);
GList *list;
for (list = private->renderer_cells; list; list = list->next)
g_object_set (list->data, "renderer", NULL, NULL);
}
}
typedef struct
{
gint view_size;
gint border_width;
} SetSizeForeachData;
static gboolean
pika_container_tree_store_set_view_size_foreach (GtkTreeModel *model,
GtkTreePath *path,
GtkTreeIter *iter,
gpointer data)
{
SetSizeForeachData *size_data = data;
PikaViewRenderer *renderer;
gtk_tree_model_get (model, iter,
PIKA_CONTAINER_TREE_STORE_COLUMN_RENDERER, &renderer,
-1);
pika_view_renderer_set_size (renderer,
size_data->view_size,
size_data->border_width);
g_object_unref (renderer);
return FALSE;
}
void
pika_container_tree_store_set_view_size (PikaContainerTreeStore *store)
{
PikaContainerTreeStorePrivate *private;
SetSizeForeachData size_data;
g_return_if_fail (PIKA_IS_CONTAINER_TREE_STORE (store));
private = GET_PRIVATE (store);
size_data.view_size =
pika_container_view_get_view_size (private->container_view,
&size_data.border_width);
gtk_tree_model_foreach (GTK_TREE_MODEL (store),
pika_container_tree_store_set_view_size_foreach,
&size_data);
}
/* private functions */
void
pika_container_tree_store_columns_init (GType *types,
gint *n_types)
{
g_return_if_fail (types != NULL);
g_return_if_fail (n_types != NULL);
g_return_if_fail (*n_types == 0);
pika_assert (PIKA_CONTAINER_TREE_STORE_COLUMN_RENDERER ==
pika_container_tree_store_columns_add (types, n_types,
PIKA_TYPE_VIEW_RENDERER));
pika_assert (PIKA_CONTAINER_TREE_STORE_COLUMN_NAME ==
pika_container_tree_store_columns_add (types, n_types,
G_TYPE_STRING));
pika_assert (PIKA_CONTAINER_TREE_STORE_COLUMN_NAME_ATTRIBUTES ==
pika_container_tree_store_columns_add (types, n_types,
PANGO_TYPE_ATTR_LIST));
pika_assert (PIKA_CONTAINER_TREE_STORE_COLUMN_NAME_SENSITIVE ==
pika_container_tree_store_columns_add (types, n_types,
G_TYPE_BOOLEAN));
pika_assert (PIKA_CONTAINER_TREE_STORE_COLUMN_USER_DATA ==
pika_container_tree_store_columns_add (types, n_types,
G_TYPE_POINTER));
}
gint
pika_container_tree_store_columns_add (GType *types,
gint *n_types,
GType type)
{
g_return_val_if_fail (types != NULL, 0);
g_return_val_if_fail (n_types != NULL, 0);
g_return_val_if_fail (*n_types >= 0, 0);
types[*n_types] = type;
(*n_types)++;
return *n_types - 1;
}
static void
pika_container_tree_store_set (PikaContainerTreeStore *store,
GtkTreeIter *iter,
PikaViewable *viewable)
{
PikaContainerTreeStorePrivate *private = GET_PRIVATE (store);
PikaContext *context;
PikaViewRenderer *renderer;
gchar *name;
gint view_size;
gint border_width;
context = pika_container_view_get_context (private->container_view);
view_size = pika_container_view_get_view_size (private->container_view,
&border_width);
renderer = pika_view_renderer_new (context,
G_TYPE_FROM_INSTANCE (viewable),
view_size, border_width,
FALSE);
pika_view_renderer_set_viewable (renderer, viewable);
pika_view_renderer_remove_idle (renderer);
g_signal_connect (renderer, "update",
G_CALLBACK (pika_container_tree_store_renderer_update),
store);
if (private->use_name)
name = (gchar *) pika_object_get_name (viewable);
else
name = pika_viewable_get_description (viewable, NULL);
gtk_tree_store_set (GTK_TREE_STORE (store), iter,
PIKA_CONTAINER_TREE_STORE_COLUMN_RENDERER, renderer,
PIKA_CONTAINER_TREE_STORE_COLUMN_NAME, name,
PIKA_CONTAINER_TREE_STORE_COLUMN_NAME_SENSITIVE, TRUE,
-1);
if (! private->use_name)
g_free (name);
g_object_unref (renderer);
}
static void
pika_container_tree_store_renderer_update (PikaViewRenderer *renderer,
PikaContainerTreeStore *store)
{
PikaContainerTreeStorePrivate *private = GET_PRIVATE (store);
GtkTreeIter *iter;
iter = pika_container_view_lookup (private->container_view,
renderer->viewable);
if (iter)
{
GtkTreePath *path;
path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), iter);
gtk_tree_model_row_changed (GTK_TREE_MODEL (store), path, iter);
gtk_tree_path_free (path);
}
}

View File

@ -0,0 +1,104 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikacontainertreestore.h
* Copyright (C) 2010 Michael Natterer <mitch@gimp.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __PIKA_CONTAINER_TREE_STORE_H__
#define __PIKA_CONTAINER_TREE_STORE_H__
enum
{
PIKA_CONTAINER_TREE_STORE_COLUMN_RENDERER,
PIKA_CONTAINER_TREE_STORE_COLUMN_NAME,
PIKA_CONTAINER_TREE_STORE_COLUMN_NAME_ATTRIBUTES,
PIKA_CONTAINER_TREE_STORE_COLUMN_NAME_SENSITIVE,
PIKA_CONTAINER_TREE_STORE_COLUMN_USER_DATA,
PIKA_CONTAINER_TREE_STORE_N_COLUMNS
};
#define PIKA_TYPE_CONTAINER_TREE_STORE (pika_container_tree_store_get_type ())
#define PIKA_CONTAINER_TREE_STORE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIKA_TYPE_CONTAINER_TREE_STORE, PikaContainerTreeStore))
#define PIKA_CONTAINER_TREE_STORE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIKA_TYPE_CONTAINER_TREE_STORE, PikaContainerTreeStoreClass))
#define PIKA_IS_CONTAINER_TREE_STORE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIKA_TYPE_CONTAINER_TREE_STORE))
#define PIKA_IS_CONTAINER_TREE_STORE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIKA_TYPE_CONTAINER_TREE_STORE))
#define PIKA_CONTAINER_TREE_STORE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIKA_TYPE_CONTAINER_TREE_STORE, PikaContainerTreeStoreClass))
typedef struct _PikaContainerTreeStoreClass PikaContainerTreeStoreClass;
struct _PikaContainerTreeStore
{
GtkTreeStore parent_instance;
};
struct _PikaContainerTreeStoreClass
{
GtkTreeStoreClass parent_class;
};
GType pika_container_tree_store_get_type (void) G_GNUC_CONST;
void pika_container_tree_store_columns_init (GType *types,
gint *n_types);
gint pika_container_tree_store_columns_add (GType *types,
gint *n_types,
GType type);
GtkTreeModel * pika_container_tree_store_new (PikaContainerView *container_view,
gint n_columns,
GType *types);
void pika_container_tree_store_add_renderer_cell (PikaContainerTreeStore *store,
GtkCellRenderer *cell,
gint column_number);
PikaViewRenderer *
pika_container_tree_store_get_renderer (PikaContainerTreeStore *store,
GtkTreeIter *iter);
void pika_container_tree_store_set_use_name (PikaContainerTreeStore *store,
gboolean use_name);
gboolean pika_container_tree_store_get_use_name (PikaContainerTreeStore *store);
void pika_container_tree_store_set_context (PikaContainerTreeStore *store,
PikaContext *context);
GtkTreeIter * pika_container_tree_store_insert_item (PikaContainerTreeStore *store,
PikaViewable *viewable,
GtkTreeIter *parent,
gint index);
void pika_container_tree_store_remove_item (PikaContainerTreeStore *store,
PikaViewable *viewable,
GtkTreeIter *iter);
void pika_container_tree_store_reorder_item (PikaContainerTreeStore *store,
PikaViewable *viewable,
gint new_index,
GtkTreeIter *iter);
gboolean pika_container_tree_store_rename_item (PikaContainerTreeStore *store,
PikaViewable *viewable,
GtkTreeIter *iter);
void pika_container_tree_store_clear_items (PikaContainerTreeStore *store);
void pika_container_tree_store_set_view_size (PikaContainerTreeStore *store);
#endif /* __PIKA_CONTAINER_TREE_STORE_H__ */

View File

@ -0,0 +1,915 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikacontainertreeview-dnd.c
* Copyright (C) 2003-2009 Michael Natterer <mitch@gimp.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gegl.h>
#include <gtk/gtk.h>
#include "widgets-types.h"
#include "core/pikacontainer.h"
#include "core/pikaviewable.h"
#include "pikacontainertreestore.h"
#include "pikacontainertreeview.h"
#include "pikacontainertreeview-dnd.h"
#include "pikacontainertreeview-private.h"
#include "pikacontainerview.h"
#include "pikadnd.h"
#include "pikaviewrenderer.h"
#include "pikaselectiondata.h"
static gint
pika_container_tree_view_viewable_sort (PikaViewable *v1,
PikaViewable *v2,
PikaContainerTreeView *tree_view)
{
PikaContainerView *view = PIKA_CONTAINER_VIEW (tree_view);
PikaViewable *parent1;
PikaViewable *parent2;
PikaContainer *container1 = NULL;
PikaContainer *container2 = NULL;
PikaContainer *container = pika_container_view_get_container (view);
gint index1 = -1;
gint index2 = -1;
gint depth1;
gint depth2;
parent1 = pika_viewable_get_parent (v1);
parent2 = pika_viewable_get_parent (v2);
if (parent1)
container1 = pika_viewable_get_children (parent1);
else if (pika_container_have (container, PIKA_OBJECT (v1)))
container1 = container;
if (parent2)
container2 = pika_viewable_get_children (parent2);
else if (pika_container_have (container, PIKA_OBJECT (v2)))
container2 = container;
g_return_val_if_fail (container1 && container2, 0);
if (container1 == container2)
{
index1 = pika_container_get_child_index (container1, PIKA_OBJECT (v1));
index2 = pika_container_get_child_index (container2, PIKA_OBJECT (v2));
return index1 < index2 ? -1 : (index1 > index2 ? 1 : 0);
}
depth1 = pika_viewable_get_depth (v1);
depth2 = pika_viewable_get_depth (v2);
if (depth1 == depth2)
{
return pika_container_tree_view_viewable_sort (parent1, parent2, tree_view);
}
else if (depth1 > depth2)
{
depth1 = pika_viewable_get_depth (parent1);
while (depth1 > depth2)
{
parent1 = pika_viewable_get_parent (parent1);
depth1 = pika_viewable_get_depth (parent1);
}
return pika_container_tree_view_viewable_sort (parent1, v2, tree_view);
}
else /* if (depth1 < depth2) */
{
depth2 = pika_viewable_get_depth (parent2);
while (depth1 < depth2)
{
parent2 = pika_viewable_get_parent (parent2);
depth2 = pika_viewable_get_depth (parent2);
}
return pika_container_tree_view_viewable_sort (v1, parent2, tree_view);
}
}
/**
* pika_container_tree_view_drop_status:
* @tree_view:
* @context:
* @x:
* @y:
* @time:
* @return_path: the #GtkTreePath of the drop position if the drop is
* possible.
* @return_atom:
* @return_src_type: the type of drag'n drop.
* @return_src: allocated #GList of #PikaViewable being dragged.
* @return_dest: the #PikaViewable you are dropping on.
* @return_pos: the drop position (before, after or into @return_dest).
*
* Check whether the current drag can be dropped into @tree_view at
* position (@x, @y). If so, the various return value information will
* be optionally filled.
* Note: if @return_src is not %NULL, hence is filled, it must be freed
* with g_list_free().
*
* Returns: %TRUE is the drop is possible, %FALSE otherwise.
*/
static gboolean
pika_container_tree_view_drop_status (PikaContainerTreeView *tree_view,
GdkDragContext *context,
gint x,
gint y,
guint time,
GtkTreePath **return_path,
GdkAtom *return_atom,
PikaDndType *return_src_type,
GList **return_src,
PikaViewable **return_dest,
GtkTreeViewDropPosition *return_pos)
{
GList *src_viewables = NULL;
PikaViewable *dest_viewable = NULL;
GtkTreePath *drop_path = NULL;
GtkTargetList *target_list;
GdkAtom target_atom;
PikaDndType src_type;
GtkTreeViewDropPosition drop_pos = GTK_TREE_VIEW_DROP_BEFORE;
GdkDragAction drag_action = 0;
if (! pika_container_view_get_container (PIKA_CONTAINER_VIEW (tree_view)) ||
! pika_container_view_get_reorderable (PIKA_CONTAINER_VIEW (tree_view)))
goto drop_impossible;
target_list = gtk_drag_dest_get_target_list (GTK_WIDGET (tree_view->view));
target_atom = gtk_drag_dest_find_target (GTK_WIDGET (tree_view->view),
context, target_list);
if (! gtk_target_list_find (target_list, target_atom, &src_type))
goto drop_impossible;
switch (src_type)
{
case PIKA_DND_TYPE_URI_LIST:
case PIKA_DND_TYPE_TEXT_PLAIN:
case PIKA_DND_TYPE_NETSCAPE_URL:
case PIKA_DND_TYPE_COLOR:
case PIKA_DND_TYPE_SVG:
case PIKA_DND_TYPE_SVG_XML:
case PIKA_DND_TYPE_COMPONENT:
case PIKA_DND_TYPE_PIXBUF:
break;
case PIKA_DND_TYPE_XDS:
case PIKA_DND_TYPE_IMAGE:
case PIKA_DND_TYPE_LAYER:
case PIKA_DND_TYPE_CHANNEL:
case PIKA_DND_TYPE_LAYER_MASK:
case PIKA_DND_TYPE_VECTORS:
case PIKA_DND_TYPE_BRUSH:
case PIKA_DND_TYPE_PATTERN:
case PIKA_DND_TYPE_GRADIENT:
case PIKA_DND_TYPE_PALETTE:
case PIKA_DND_TYPE_FONT:
case PIKA_DND_TYPE_BUFFER:
case PIKA_DND_TYPE_IMAGEFILE:
case PIKA_DND_TYPE_TEMPLATE:
case PIKA_DND_TYPE_TOOL_ITEM:
case PIKA_DND_TYPE_NOTEBOOK_TAB:
/* Various PikaViewable drag data. */
{
GtkWidget *src_widget = gtk_drag_get_source_widget (context);
PikaViewable *src_viewable = NULL;
if (! src_widget)
goto drop_impossible;
src_viewable = pika_dnd_get_drag_viewable (src_widget);
if (! PIKA_IS_VIEWABLE (src_viewable))
goto drop_impossible;
src_viewables = g_list_prepend (src_viewables, src_viewable);
}
break;
case PIKA_DND_TYPE_CHANNEL_LIST:
case PIKA_DND_TYPE_LAYER_LIST:
case PIKA_DND_TYPE_VECTORS_LIST:
/* Various PikaViewable list (GList) drag data. */
{
GtkWidget *src_widget = gtk_drag_get_source_widget (context);
GList *iter;
if (! src_widget)
goto drop_impossible;
src_viewables = pika_dnd_get_drag_list (src_widget);
if (! src_viewables)
goto drop_impossible;
for (iter = src_viewables; iter; iter = iter->next)
if (! PIKA_IS_VIEWABLE (iter->data))
{
g_warning ("%s: contents of the viewable list has the wrong type '%s'.",
G_STRFUNC, G_OBJECT_TYPE_NAME (iter->data));
goto drop_impossible;
}
}
break;
default:
goto drop_impossible;
break;
}
gtk_tree_view_convert_widget_to_bin_window_coords (tree_view->view, x, y, &x, &y);
if (gtk_tree_view_get_path_at_pos (tree_view->view, x, y,
&drop_path, NULL, NULL, NULL))
{
PikaViewRenderer *renderer;
GtkTreeIter iter;
GdkRectangle cell_area;
gtk_tree_model_get_iter (tree_view->model, &iter, drop_path);
renderer = pika_container_tree_store_get_renderer (PIKA_CONTAINER_TREE_STORE (tree_view->model),
&iter);
dest_viewable = renderer->viewable;
g_object_unref (renderer);
gtk_tree_view_get_cell_area (tree_view->view, drop_path, NULL, &cell_area);
if (pika_viewable_get_children (dest_viewable))
{
if (gtk_tree_view_row_expanded (tree_view->view, drop_path))
{
if (y >= (cell_area.y + cell_area.height / 2))
drop_pos = GTK_TREE_VIEW_DROP_INTO_OR_AFTER;
else
drop_pos = GTK_TREE_VIEW_DROP_BEFORE;
}
else
{
if (y >= (cell_area.y + 2 * (cell_area.height / 3)))
drop_pos = GTK_TREE_VIEW_DROP_AFTER;
else if (y <= (cell_area.y + cell_area.height / 3))
drop_pos = GTK_TREE_VIEW_DROP_BEFORE;
else
drop_pos = GTK_TREE_VIEW_DROP_INTO_OR_AFTER;
}
}
else
{
if (y >= (cell_area.y + cell_area.height / 2))
drop_pos = GTK_TREE_VIEW_DROP_AFTER;
else
drop_pos = GTK_TREE_VIEW_DROP_BEFORE;
}
}
else
{
GtkTreeIter iter;
gint n_children;
n_children = gtk_tree_model_iter_n_children (tree_view->model, NULL);
if (n_children > 0 &&
gtk_tree_model_iter_nth_child (tree_view->model, &iter,
NULL, n_children - 1))
{
PikaViewRenderer *renderer;
renderer = pika_container_tree_store_get_renderer (PIKA_CONTAINER_TREE_STORE (tree_view->model),
&iter);
drop_path = gtk_tree_model_get_path (tree_view->model, &iter);
dest_viewable = renderer->viewable;
drop_pos = GTK_TREE_VIEW_DROP_AFTER;
g_object_unref (renderer);
}
}
if (dest_viewable || tree_view->priv->dnd_drop_to_empty)
{
if (PIKA_CONTAINER_TREE_VIEW_GET_CLASS (tree_view)->drop_possible (tree_view,
src_type,
src_viewables,
dest_viewable,
drop_path,
drop_pos,
&drop_pos,
&drag_action))
{
gdk_drag_status (context, drag_action, time);
if (return_path)
*return_path = drop_path;
else
gtk_tree_path_free (drop_path);
if (return_atom)
*return_atom = target_atom;
if (return_src)
{
src_viewables = g_list_sort_with_data (src_viewables,
(GCompareDataFunc) pika_container_tree_view_viewable_sort,
tree_view);
*return_src = src_viewables;
}
else
{
g_list_free (src_viewables);
}
if (return_dest)
*return_dest = dest_viewable;
if (return_pos)
*return_pos = drop_pos;
return TRUE;
}
}
drop_impossible:
g_list_free (src_viewables);
gtk_tree_path_free (drop_path);
gdk_drag_status (context, 0, time);
return FALSE;
}
#define SCROLL_DISTANCE 30
#define SCROLL_STEP 10
#define SCROLL_INTERVAL 5
/* #define SCROLL_DEBUG 1 */
static gboolean
pika_container_tree_view_scroll_timeout (gpointer data)
{
PikaContainerTreeView *tree_view = PIKA_CONTAINER_TREE_VIEW (data);
GtkAdjustment *adj;
gdouble new_value;
adj = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (tree_view->view));
#ifdef SCROLL_DEBUG
g_print ("scroll_timeout: scrolling by %d\n", SCROLL_STEP);
#endif
if (tree_view->priv->scroll_dir == GDK_SCROLL_UP)
new_value = gtk_adjustment_get_value (adj) - SCROLL_STEP;
else
new_value = gtk_adjustment_get_value (adj) + SCROLL_STEP;
new_value = CLAMP (new_value,
gtk_adjustment_get_lower (adj),
gtk_adjustment_get_upper (adj) -
gtk_adjustment_get_page_size (adj));
gtk_adjustment_set_value (adj, new_value);
if (tree_view->priv->scroll_timeout_id)
{
g_source_remove (tree_view->priv->scroll_timeout_id);
tree_view->priv->scroll_timeout_id =
g_timeout_add (tree_view->priv->scroll_timeout_interval,
pika_container_tree_view_scroll_timeout,
tree_view);
}
return FALSE;
}
void
pika_container_tree_view_drag_failed (GtkWidget *widget,
GdkDragContext *context,
GtkDragResult result,
PikaContainerTreeView *tree_view)
{
if (tree_view->priv->scroll_timeout_id)
{
g_source_remove (tree_view->priv->scroll_timeout_id);
tree_view->priv->scroll_timeout_id = 0;
}
gtk_tree_view_set_drag_dest_row (tree_view->view, NULL, 0);
}
void
pika_container_tree_view_drag_leave (GtkWidget *widget,
GdkDragContext *context,
guint time,
PikaContainerTreeView *tree_view)
{
if (tree_view->priv->scroll_timeout_id)
{
g_source_remove (tree_view->priv->scroll_timeout_id);
tree_view->priv->scroll_timeout_id = 0;
}
gtk_tree_view_set_drag_dest_row (tree_view->view, NULL, 0);
}
gboolean
pika_container_tree_view_drag_motion (GtkWidget *widget,
GdkDragContext *context,
gint x,
gint y,
guint time,
PikaContainerTreeView *tree_view)
{
GtkAllocation allocation;
GtkTreePath *drop_path;
GtkTreeViewDropPosition drop_pos;
gtk_widget_get_allocation (widget, &allocation);
if (y < SCROLL_DISTANCE || y > (allocation.height - SCROLL_DISTANCE))
{
gint distance;
if (y < SCROLL_DISTANCE)
{
tree_view->priv->scroll_dir = GDK_SCROLL_UP;
distance = MIN (-y, -1);
}
else
{
tree_view->priv->scroll_dir = GDK_SCROLL_DOWN;
distance = MAX (allocation.height - y, 1);
}
tree_view->priv->scroll_timeout_interval = SCROLL_INTERVAL * ABS (distance);
#ifdef SCROLL_DEBUG
g_print ("drag_motion: scroll_distance = %d scroll_interval = %d\n",
distance, tree_view->priv->scroll_timeout_interval);
#endif
if (! tree_view->priv->scroll_timeout_id)
tree_view->priv->scroll_timeout_id =
g_timeout_add (tree_view->priv->scroll_timeout_interval,
pika_container_tree_view_scroll_timeout,
tree_view);
}
else if (tree_view->priv->scroll_timeout_id)
{
g_source_remove (tree_view->priv->scroll_timeout_id);
tree_view->priv->scroll_timeout_id = 0;
}
if (pika_container_tree_view_drop_status (tree_view,
context, x, y, time,
&drop_path, NULL, NULL, NULL, NULL,
&drop_pos))
{
gtk_tree_view_set_drag_dest_row (tree_view->view, drop_path, drop_pos);
gtk_tree_path_free (drop_path);
}
else
{
gtk_tree_view_set_drag_dest_row (tree_view->view, NULL, 0);
}
/* always return TRUE so drag_leave() is called */
return TRUE;
}
gboolean
pika_container_tree_view_drag_drop (GtkWidget *widget,
GdkDragContext *context,
gint x,
gint y,
guint time,
PikaContainerTreeView *tree_view)
{
PikaDndType src_type;
GList *src_viewables;
PikaViewable *dest_viewable;
GdkAtom target;
GtkTreeViewDropPosition drop_pos;
if (tree_view->priv->scroll_timeout_id)
{
g_source_remove (tree_view->priv->scroll_timeout_id);
tree_view->priv->scroll_timeout_id = 0;
}
if (pika_container_tree_view_drop_status (tree_view,
context, x, y, time,
NULL, &target, &src_type,
&src_viewables,
&dest_viewable, &drop_pos))
{
PikaContainerTreeViewClass *tree_view_class;
tree_view_class = PIKA_CONTAINER_TREE_VIEW_GET_CLASS (tree_view);
if (src_viewables)
{
gboolean success = TRUE;
/* XXX: Make PikaContainerTreeViewClass::drop_viewable()
* return success?
*/
tree_view_class->drop_viewables (tree_view, src_viewables,
dest_viewable, drop_pos);
gtk_drag_finish (context, success, FALSE, time);
g_list_free (src_viewables);
}
else
{
/* Necessary for instance for dragging color components onto
* item dialogs.
*/
gtk_drag_get_data (widget, context, target, time);
}
return TRUE;
}
return FALSE;
}
void
pika_container_tree_view_drag_data_received (GtkWidget *widget,
GdkDragContext *context,
gint x,
gint y,
GtkSelectionData *selection_data,
guint info,
guint time,
PikaContainerTreeView *tree_view)
{
PikaViewable *dest_viewable;
GtkTreeViewDropPosition drop_pos;
gboolean success = FALSE;
if (pika_container_tree_view_drop_status (tree_view,
context, x, y, time,
NULL, NULL, NULL, NULL,
&dest_viewable, &drop_pos))
{
PikaContainerTreeViewClass *tree_view_class;
tree_view_class = PIKA_CONTAINER_TREE_VIEW_GET_CLASS (tree_view);
switch (info)
{
case PIKA_DND_TYPE_URI_LIST:
case PIKA_DND_TYPE_TEXT_PLAIN:
case PIKA_DND_TYPE_NETSCAPE_URL:
if (tree_view_class->drop_uri_list)
{
GList *uri_list;
uri_list = pika_selection_data_get_uri_list (selection_data);
if (uri_list)
{
tree_view_class->drop_uri_list (tree_view, uri_list,
dest_viewable, drop_pos);
g_list_free_full (uri_list, (GDestroyNotify) g_free);
success = TRUE;
}
}
break;
case PIKA_DND_TYPE_COLOR:
if (tree_view_class->drop_color)
{
PikaRGB color;
if (pika_selection_data_get_color (selection_data, &color))
{
tree_view_class->drop_color (tree_view, &color,
dest_viewable, drop_pos);
success = TRUE;
}
}
break;
case PIKA_DND_TYPE_SVG:
case PIKA_DND_TYPE_SVG_XML:
if (tree_view_class->drop_svg)
{
const guchar *stream;
gsize stream_length;
stream = pika_selection_data_get_stream (selection_data,
&stream_length);
if (stream)
{
tree_view_class->drop_svg (tree_view,
(const gchar *) stream,
stream_length,
dest_viewable, drop_pos);
success = TRUE;
}
}
break;
case PIKA_DND_TYPE_COMPONENT:
if (tree_view_class->drop_component)
{
PikaImage *image = NULL;
PikaChannelType component;
if (tree_view->dnd_pika)
image = pika_selection_data_get_component (selection_data,
tree_view->dnd_pika,
&component);
if (image)
{
tree_view_class->drop_component (tree_view,
image, component,
dest_viewable, drop_pos);
success = TRUE;
}
}
break;
case PIKA_DND_TYPE_PIXBUF:
if (tree_view_class->drop_pixbuf)
{
GdkPixbuf *pixbuf;
pixbuf = gtk_selection_data_get_pixbuf (selection_data);
if (pixbuf)
{
tree_view_class->drop_pixbuf (tree_view,
pixbuf,
dest_viewable, drop_pos);
g_object_unref (pixbuf);
success = TRUE;
}
}
break;
default:
break;
}
}
gtk_drag_finish (context, success, FALSE, time);
}
gboolean
pika_container_tree_view_real_drop_possible (PikaContainerTreeView *tree_view,
PikaDndType src_type,
GList *src_viewables,
PikaViewable *dest_viewable,
GtkTreePath *drop_path,
GtkTreeViewDropPosition drop_pos,
GtkTreeViewDropPosition *return_drop_pos,
GdkDragAction *return_drag_action)
{
PikaContainerView *view = PIKA_CONTAINER_VIEW (tree_view);
PikaContainer *container = pika_container_view_get_container (view);
PikaContainer *src_container = NULL;
PikaContainer *dest_container = NULL;
GList *iter;
gint src_index = -1;
gint dest_index = -1;
if (dest_viewable)
{
PikaViewable *parent;
/* dropping on the lower third of a group item drops into that group */
if (drop_pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER &&
pika_viewable_get_children (dest_viewable))
{
parent = dest_viewable;
}
else
{
parent = pika_viewable_get_parent (dest_viewable);
}
if (parent)
dest_container = pika_viewable_get_children (parent);
else if (pika_container_have (container, PIKA_OBJECT (dest_viewable)))
dest_container = container;
if (parent == dest_viewable)
dest_index = 0;
else
dest_index = pika_container_get_child_index (dest_container,
PIKA_OBJECT (dest_viewable));
}
if (return_drag_action)
{
if (! src_viewables)
*return_drag_action = GDK_ACTION_COPY;
else
*return_drag_action = GDK_ACTION_MOVE;
}
for (iter = src_viewables; iter; iter = iter->next)
{
PikaViewable *src_viewable = iter->data;
PikaViewable *parent;
parent = pika_viewable_get_parent (src_viewable);
if (parent)
src_container = pika_viewable_get_children (parent);
else if (pika_container_have (container, PIKA_OBJECT (src_viewable)))
src_container = container;
if (src_container)
src_index = pika_container_get_child_index (src_container,
PIKA_OBJECT (src_viewable));
if (g_type_is_a (G_TYPE_FROM_INSTANCE (src_viewable),
pika_container_get_children_type (container)))
{
/* The drop won't change a thing. This is not a fatal drop
* failure, unless there is only one source viewable.
* See also the XXX below.
*/
if (src_viewable == dest_viewable && g_list_length (src_viewables) == 1)
return FALSE;
if (src_index == -1 || dest_index == -1)
return FALSE;
/* don't allow dropping a parent node onto one of its descendants
*/
if (pika_viewable_is_ancestor (src_viewable, dest_viewable))
return FALSE;
}
/* XXX only check these for list of 1 viewable for now.
* Actually this drop failure would also happen for more than 1
* viewable if all the sources are from the same src_container
* with successive indexes.
*/
if (src_container == dest_container && g_list_length (src_viewables) == 1)
{
if (drop_pos == GTK_TREE_VIEW_DROP_BEFORE)
{
if (dest_index == (src_index + 1))
return FALSE;
}
else if (drop_pos == GTK_TREE_VIEW_DROP_AFTER)
{
if (dest_index == (src_index - 1))
return FALSE;
}
}
if (return_drag_action)
{
if (! g_type_is_a (G_TYPE_FROM_INSTANCE (src_viewable),
pika_container_get_children_type (container)))
*return_drag_action = GDK_ACTION_COPY;
}
}
if (return_drop_pos)
*return_drop_pos = drop_pos;
return TRUE;
}
void
pika_container_tree_view_real_drop_viewables (PikaContainerTreeView *tree_view,
GList *src_viewables,
PikaViewable *dest_viewable,
GtkTreeViewDropPosition drop_pos)
{
PikaContainerView *view = PIKA_CONTAINER_VIEW (tree_view);
PikaContainer *src_container;
PikaContainer *dest_container;
GList *iter;
gint dest_index = 0;
g_return_if_fail (g_list_length (src_viewables) > 0);
src_viewables = g_list_reverse (src_viewables);
for (iter = src_viewables; iter; iter = iter->next)
{
PikaViewable *src_viewable = iter->data;
if ((drop_pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
drop_pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE) &&
pika_viewable_get_children (dest_viewable))
{
dest_container = pika_viewable_get_children (dest_viewable);
dest_viewable = NULL;
drop_pos = GTK_TREE_VIEW_DROP_BEFORE;
}
else if (pika_viewable_get_parent (dest_viewable))
{
dest_container = pika_viewable_get_children (pika_viewable_get_parent (dest_viewable));
}
else
{
dest_container = pika_container_view_get_container (view);
}
if (dest_viewable)
{
dest_index = pika_container_get_child_index (dest_container,
PIKA_OBJECT (dest_viewable));
}
if (pika_viewable_get_parent (src_viewable))
{
src_container = pika_viewable_get_children (
pika_viewable_get_parent (src_viewable));
}
else
{
src_container = pika_container_view_get_container (view);
}
if (src_container == dest_container)
{
gint src_index;
src_index = pika_container_get_child_index (src_container,
PIKA_OBJECT (src_viewable));
switch (drop_pos)
{
case GTK_TREE_VIEW_DROP_AFTER:
case GTK_TREE_VIEW_DROP_INTO_OR_AFTER:
if (src_index > dest_index)
dest_index++;
break;
case GTK_TREE_VIEW_DROP_BEFORE:
case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE:
if (src_index < dest_index)
dest_index--;
break;
}
pika_container_reorder (src_container,
PIKA_OBJECT (src_viewable), dest_index);
}
else
{
switch (drop_pos)
{
case GTK_TREE_VIEW_DROP_AFTER:
case GTK_TREE_VIEW_DROP_INTO_OR_AFTER:
dest_index++;
break;
case GTK_TREE_VIEW_DROP_BEFORE:
case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE:
break;
}
g_object_ref (src_viewable);
pika_container_remove (src_container, PIKA_OBJECT (src_viewable));
pika_container_insert (dest_container, PIKA_OBJECT (src_viewable),
dest_index);
g_object_unref (src_viewable);
}
}
}

View File

@ -0,0 +1,75 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikacontainertreeview-dnd.h
* Copyright (C) 2003-2009 Michael Natterer <mitch@gimp.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __PIKA_CONTAINER_TREE_VIEW_DND_H__
#define __PIKA_CONTAINER_TREE_VIEW_DND_H__
void pika_container_tree_view_drag_failed (GtkWidget *widget,
GdkDragContext *context,
GtkDragResult result,
PikaContainerTreeView *tree_view);
void pika_container_tree_view_drag_leave (GtkWidget *widget,
GdkDragContext *context,
guint time,
PikaContainerTreeView *view);
gboolean pika_container_tree_view_drag_motion (GtkWidget *widget,
GdkDragContext *context,
gint x,
gint y,
guint time,
PikaContainerTreeView *view);
gboolean pika_container_tree_view_drag_drop (GtkWidget *widget,
GdkDragContext *context,
gint x,
gint y,
guint time,
PikaContainerTreeView *view);
void pika_container_tree_view_drag_data_received
(GtkWidget *widget,
GdkDragContext *context,
gint x,
gint y,
GtkSelectionData *selection_data,
guint info,
guint time,
PikaContainerTreeView *view);
gboolean
pika_container_tree_view_real_drop_possible (PikaContainerTreeView *tree_view,
PikaDndType src_type,
GList *src_viewables,
PikaViewable *dest_viewable,
GtkTreePath *drop_path,
GtkTreeViewDropPosition drop_pos,
GtkTreeViewDropPosition *return_drop_pos,
GdkDragAction *return_drag_action);
void
pika_container_tree_view_real_drop_viewables (PikaContainerTreeView *tree_view,
GList *src_viewables,
PikaViewable *dest_viewable,
GtkTreeViewDropPosition drop_pos);
#endif /* __PIKA_CONTAINER_TREE_VIEW_DND_H__ */

View File

@ -0,0 +1,57 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikacontainertreeview-private.h
* Copyright (C) 2003-2004 Michael Natterer <mitch@gimp.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __PIKA_CONTAINER_TREE_VIEW_PRIVATE_H__
#define __PIKA_CONTAINER_TREE_VIEW_PRIVATE_H__
struct _PikaContainerTreeViewPrivate
{
GtkTreeSelection *selection;
GtkCellRenderer *name_cell;
GtkWidget *multi_selection_label;
GList *editable_cells;
gchar *editing_path;
PikaViewRenderer *dnd_renderer;
GList *toggle_cells;
GList *renderer_cells;
guint scroll_timeout_id;
guint scroll_timeout_interval;
GdkScrollDirection scroll_dir;
gboolean dnd_drop_to_empty;
gdouble zoom_accumulated_scroll_delta;
GtkGesture *zoom_gesture;
gdouble zoom_gesture_last_set_value;
};
#endif /* __PIKA_CONTAINER_TREE_VIEW_PRIVATE_H__ */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,146 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikacontainertreeview.h
* Copyright (C) 2003-2004 Michael Natterer <mitch@gimp.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __PIKA_CONTAINER_TREE_VIEW_H__
#define __PIKA_CONTAINER_TREE_VIEW_H__
#include "pikacontainerbox.h"
#define PIKA_TYPE_CONTAINER_TREE_VIEW (pika_container_tree_view_get_type ())
#define PIKA_CONTAINER_TREE_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIKA_TYPE_CONTAINER_TREE_VIEW, PikaContainerTreeView))
#define PIKA_CONTAINER_TREE_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIKA_TYPE_CONTAINER_TREE_VIEW, PikaContainerTreeViewClass))
#define PIKA_IS_CONTAINER_TREE_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIKA_TYPE_CONTAINER_TREE_VIEW))
#define PIKA_IS_CONTAINER_TREE_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIKA_TYPE_CONTAINER_TREE_VIEW))
#define PIKA_CONTAINER_TREE_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIKA_TYPE_CONTAINER_TREE_VIEW, PikaContainerTreeViewClass))
typedef struct _PikaContainerTreeViewClass PikaContainerTreeViewClass;
typedef struct _PikaContainerTreeViewPrivate PikaContainerTreeViewPrivate;
struct _PikaContainerTreeView
{
PikaContainerBox parent_instance;
GtkTreeModel *model;
gint n_model_columns;
GType model_columns[16];
GtkTreeView *view;
GtkTreeViewColumn *main_column;
GtkCellRenderer *renderer_cell;
Pika *dnd_pika; /* eek */
PikaContainerTreeViewPrivate *priv;
};
struct _PikaContainerTreeViewClass
{
PikaContainerBoxClass parent_class;
/* signals */
void (* edit_name) (PikaContainerTreeView *tree_view);
/* virtual functions */
gboolean (* drop_possible) (PikaContainerTreeView *tree_view,
PikaDndType src_type,
GList *src_viewables,
PikaViewable *dest_viewable,
GtkTreePath *drop_path,
GtkTreeViewDropPosition drop_pos,
GtkTreeViewDropPosition *return_drop_pos,
GdkDragAction *return_drag_action);
void (* drop_viewables) (PikaContainerTreeView *tree_view,
GList *src_viewables,
PikaViewable *dest_viewable,
GtkTreeViewDropPosition drop_pos);
void (* drop_color) (PikaContainerTreeView *tree_view,
const PikaRGB *src_color,
PikaViewable *dest_viewable,
GtkTreeViewDropPosition drop_pos);
void (* drop_uri_list) (PikaContainerTreeView *tree_view,
GList *uri_list,
PikaViewable *dest_viewable,
GtkTreeViewDropPosition drop_pos);
void (* drop_svg) (PikaContainerTreeView *tree_view,
const gchar *svg_data,
gsize svg_data_length,
PikaViewable *dest_viewable,
GtkTreeViewDropPosition drop_pos);
void (* drop_component) (PikaContainerTreeView *tree_view,
PikaImage *image,
PikaChannelType component,
PikaViewable *dest_viewable,
GtkTreeViewDropPosition drop_pos);
void (* drop_pixbuf) (PikaContainerTreeView *tree_view,
GdkPixbuf *pixbuf,
PikaViewable *dest_viewable,
GtkTreeViewDropPosition drop_pos);
};
GType pika_container_tree_view_get_type (void) G_GNUC_CONST;
GtkWidget * pika_container_tree_view_new (PikaContainer *container,
PikaContext *context,
gint view_size,
gint view_border_width);
GtkCellRenderer *
pika_container_tree_view_get_name_cell
(PikaContainerTreeView *tree_view);
void pika_container_tree_view_set_main_column_title
(PikaContainerTreeView *tree_view,
const gchar *title);
void pika_container_tree_view_add_toggle_cell
(PikaContainerTreeView *tree_view,
GtkCellRenderer *cell);
void pika_container_tree_view_add_renderer_cell
(PikaContainerTreeView *tree_view,
GtkCellRenderer *cell,
gint column_number);
void pika_container_tree_view_set_dnd_drop_to_empty
(PikaContainerTreeView *tree_view,
gboolean dnd_drop_to_emtpy);
void pika_container_tree_view_connect_name_edited
(PikaContainerTreeView *tree_view,
GCallback callback,
gpointer data);
gboolean pika_container_tree_view_name_edited
(GtkCellRendererText *cell,
const gchar *path_str,
const gchar *new_name,
PikaContainerTreeView *tree_view);
#endif /* __PIKA_CONTAINER_TREE_VIEW_H__ */

View File

@ -0,0 +1,96 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gegl.h>
#include <gtk/gtk.h>
#include "widgets-types.h"
#include "core/pikacontainer.h"
#include "core/pikacontext.h"
#include "pikacontainereditor.h"
#include "pikacontainerview.h"
#include "pikacontainerview-utils.h"
#include "pikadockable.h"
/* public functions */
PikaContainerView *
pika_container_view_get_by_dockable (PikaDockable *dockable)
{
GtkWidget *child;
g_return_val_if_fail (PIKA_IS_DOCKABLE (dockable), NULL);
child = gtk_bin_get_child (GTK_BIN (dockable));
if (child)
{
if (PIKA_IS_CONTAINER_EDITOR (child))
{
return PIKA_CONTAINER_EDITOR (child)->view;
}
else if (PIKA_IS_CONTAINER_VIEW (child))
{
return PIKA_CONTAINER_VIEW (child);
}
}
return NULL;
}
void
pika_container_view_remove_active (PikaContainerView *view)
{
PikaContext *context;
PikaContainer *container;
g_return_if_fail (PIKA_IS_CONTAINER_VIEW (view));
context = pika_container_view_get_context (view);
container = pika_container_view_get_container (view);
if (context && container)
{
GType children_type;
PikaObject *active;
children_type = pika_container_get_children_type (container);
active = pika_context_get_by_type (context, children_type);
if (active)
{
PikaObject *new;
new = pika_container_get_neighbor_of (container, active);
if (new)
pika_context_set_by_type (context, children_type, new);
pika_container_remove (container, active);
}
}
}

View File

@ -0,0 +1,34 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikacontainerview-utils.h
* Copyright (C) 2001 Michael Natterer <mitch@gimp.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __PIKA_CONTAINER_VIEW_UTILS_H__
#define __PIKA_CONTAINER_VIEW_UTILS_H__
PikaContainerView * pika_container_view_get_by_dockable (PikaDockable *dockable);
void pika_container_view_remove_active (PikaContainerView *view);
#endif /* __PIKA_CONTAINER_VIEW_UTILS_H__ */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,168 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikacontainerview.h
* Copyright (C) 2001-2010 Michael Natterer <mitch@gimp.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __PIKA_CONTAINER_VIEW_H__
#define __PIKA_CONTAINER_VIEW_H__
typedef enum
{
PIKA_CONTAINER_VIEW_PROP_0,
PIKA_CONTAINER_VIEW_PROP_CONTAINER,
PIKA_CONTAINER_VIEW_PROP_CONTEXT,
PIKA_CONTAINER_VIEW_PROP_SELECTION_MODE,
PIKA_CONTAINER_VIEW_PROP_REORDERABLE,
PIKA_CONTAINER_VIEW_PROP_VIEW_SIZE,
PIKA_CONTAINER_VIEW_PROP_VIEW_BORDER_WIDTH,
PIKA_CONTAINER_VIEW_PROP_LAST = PIKA_CONTAINER_VIEW_PROP_VIEW_BORDER_WIDTH
} PikaContainerViewProp;
#define PIKA_TYPE_CONTAINER_VIEW (pika_container_view_get_type ())
G_DECLARE_INTERFACE (PikaContainerView, pika_container_view, PIKA, CONTAINER_VIEW, GtkWidget)
struct _PikaContainerViewInterface
{
GTypeInterface base_iface;
/* signals */
gboolean (* select_items) (PikaContainerView *view,
GList *items,
GList *paths);
void (* activate_item) (PikaContainerView *view,
PikaViewable *object,
gpointer insert_data);
/* virtual functions */
void (* set_container) (PikaContainerView *view,
PikaContainer *container);
void (* set_context) (PikaContainerView *view,
PikaContext *context);
void (* set_selection_mode) (PikaContainerView *view,
GtkSelectionMode mode);
gpointer (* insert_item) (PikaContainerView *view,
PikaViewable *object,
gpointer parent_insert_data,
gint index);
void (* insert_items_after) (PikaContainerView *view);
void (* remove_item) (PikaContainerView *view,
PikaViewable *object,
gpointer insert_data);
void (* reorder_item) (PikaContainerView *view,
PikaViewable *object,
gint new_index,
gpointer insert_data);
void (* rename_item) (PikaContainerView *view,
PikaViewable *object,
gpointer insert_data);
void (* expand_item) (PikaContainerView *view,
PikaViewable *object,
gpointer insert_data);
void (* clear_items) (PikaContainerView *view);
void (* set_view_size) (PikaContainerView *view);
gint (* get_selected) (PikaContainerView *view,
GList **items,
GList **insert_data);
/* the destroy notifier for private->hash_table's values */
GDestroyNotify insert_data_free;
gboolean model_is_tree;
};
PikaContainer * pika_container_view_get_container (PikaContainerView *view);
void pika_container_view_set_container (PikaContainerView *view,
PikaContainer *container);
PikaContext * pika_container_view_get_context (PikaContainerView *view);
void pika_container_view_set_context (PikaContainerView *view,
PikaContext *context);
GtkSelectionMode pika_container_view_get_selection_mode (PikaContainerView *view);
void pika_container_view_set_selection_mode (PikaContainerView *view,
GtkSelectionMode mode);
gint pika_container_view_get_view_size (PikaContainerView *view,
gint *view_border_width);
void pika_container_view_set_view_size (PikaContainerView *view,
gint view_size,
gint view_border_width);
gboolean pika_container_view_get_reorderable (PikaContainerView *view);
void pika_container_view_set_reorderable (PikaContainerView *view,
gboolean reorderable);
GtkWidget * pika_container_view_get_dnd_widget (PikaContainerView *view);
void pika_container_view_set_dnd_widget (PikaContainerView *view,
GtkWidget *dnd_widget);
void pika_container_view_enable_dnd (PikaContainerView *editor,
GtkButton *button,
GType children_type);
gboolean pika_container_view_select_items (PikaContainerView *view,
GList *viewables);
gboolean pika_container_view_select_item (PikaContainerView *view,
PikaViewable *viewable);
void pika_container_view_activate_item (PikaContainerView *view,
PikaViewable *viewable);
gint pika_container_view_get_selected (PikaContainerView *view,
GList **items,
GList **items_data);
gboolean pika_container_view_is_item_selected (PikaContainerView *view,
PikaViewable *viewable);
/* protected */
gpointer pika_container_view_lookup (PikaContainerView *view,
PikaViewable *viewable);
gboolean pika_container_view_contains (PikaContainerView *view,
GList *viewables);
gboolean pika_container_view_item_selected (PikaContainerView *view,
PikaViewable *item);
gboolean pika_container_view_multi_selected (PikaContainerView *view,
GList *items,
GList *paths);
void pika_container_view_item_activated (PikaContainerView *view,
PikaViewable *item);
/* convenience functions */
void pika_container_view_install_properties (GObjectClass *klass);
void pika_container_view_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
void pika_container_view_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
#endif /* __PIKA_CONTAINER_VIEW_H__ */

View File

@ -0,0 +1,887 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995-1999 Spencer Kimball and Peter Mattis
*
* pikacontrollereditor.c
* Copyright (C) 2004-2008 Michael Natterer <mitch@gimp.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <string.h>
#include <gegl.h>
#include <gtk/gtk.h>
#include "libpikabase/pikabase.h"
#include "libpikawidgets/pikawidgets.h"
#define PIKA_ENABLE_CONTROLLER_UNDER_CONSTRUCTION
#include "libpikawidgets/pikacontroller.h"
#include "widgets-types.h"
#include "core/pikacontext.h"
#include "pikaaction.h"
#include "pikaactioneditor.h"
#include "pikaactionview.h"
#include "pikacontrollereditor.h"
#include "pikacontrollerinfo.h"
#include "pikadialogfactory.h"
#include "pikahelp-ids.h"
#include "pikauimanager.h"
#include "pikaviewabledialog.h"
#include "pikawidgets-utils.h"
#include "pika-intl.h"
enum
{
PROP_0,
PROP_CONTROLLER_INFO,
PROP_CONTEXT
};
enum
{
COLUMN_EVENT,
COLUMN_BLURB,
COLUMN_ICON_NAME,
COLUMN_ACTION,
N_COLUMNS
};
static void pika_controller_editor_constructed (GObject *object);
static void pika_controller_editor_finalize (GObject *object);
static void pika_controller_editor_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
static void pika_controller_editor_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
static void pika_controller_editor_unmap (GtkWidget *widget);
static void pika_controller_editor_sel_changed (GtkTreeSelection *sel,
PikaControllerEditor *editor);
static void pika_controller_editor_row_activated (GtkTreeView *tv,
GtkTreePath *path,
GtkTreeViewColumn *column,
PikaControllerEditor *editor);
static void pika_controller_editor_grab_toggled (GtkWidget *button,
PikaControllerEditor *editor);
static void pika_controller_editor_edit_clicked (GtkWidget *button,
PikaControllerEditor *editor);
static void pika_controller_editor_delete_clicked (GtkWidget *button,
PikaControllerEditor *editor);
static void pika_controller_editor_edit_activated (GtkTreeView *tv,
GtkTreePath *path,
GtkTreeViewColumn *column,
PikaControllerEditor *editor);
static void pika_controller_editor_edit_response (GtkWidget *dialog,
gint response_id,
PikaControllerEditor *editor);
static GtkWidget * pika_controller_string_view_new (PikaController *controller,
GParamSpec *pspec);
static GtkWidget * pika_controller_int_view_new (PikaController *controller,
GParamSpec *pspec);
G_DEFINE_TYPE (PikaControllerEditor, pika_controller_editor, GTK_TYPE_BOX)
#define parent_class pika_controller_editor_parent_class
static void
pika_controller_editor_class_init (PikaControllerEditorClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
object_class->constructed = pika_controller_editor_constructed;
object_class->finalize = pika_controller_editor_finalize;
object_class->set_property = pika_controller_editor_set_property;
object_class->get_property = pika_controller_editor_get_property;
widget_class->unmap = pika_controller_editor_unmap;
g_object_class_install_property (object_class, PROP_CONTROLLER_INFO,
g_param_spec_object ("controller-info",
NULL, NULL,
PIKA_TYPE_CONTROLLER_INFO,
PIKA_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property (object_class, PROP_CONTEXT,
g_param_spec_object ("context",
NULL, NULL,
PIKA_TYPE_CONTEXT,
PIKA_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY));
}
static void
pika_controller_editor_init (PikaControllerEditor *editor)
{
gtk_orientable_set_orientation (GTK_ORIENTABLE (editor),
GTK_ORIENTATION_VERTICAL);
gtk_box_set_spacing (GTK_BOX (editor), 12);
editor->info = NULL;
}
static void
pika_controller_editor_constructed (GObject *object)
{
PikaControllerEditor *editor = PIKA_CONTROLLER_EDITOR (object);
PikaControllerInfo *info;
PikaController *controller;
PikaControllerClass *controller_class;
PikaUIManager *ui_manager;
GtkListStore *store;
GtkWidget *frame;
GtkWidget *vbox;
GtkWidget *grid;
GtkWidget *hbox;
GtkWidget *button;
GtkWidget *tv;
GtkWidget *sw;
GtkWidget *entry;
GtkTreeViewColumn *column;
GtkCellRenderer *cell;
GParamSpec **property_specs;
guint n_property_specs;
gint n_events;
gint row;
gint i;
G_OBJECT_CLASS (parent_class)->constructed (object);
pika_assert (PIKA_IS_CONTROLLER_INFO (editor->info));
info = editor->info;
controller = info->controller;
controller_class = PIKA_CONTROLLER_GET_CLASS (controller);
frame = pika_frame_new (_("General"));
gtk_box_pack_start (GTK_BOX (editor), frame, FALSE, FALSE, 0);
gtk_widget_show (frame);
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 4);
gtk_container_add (GTK_CONTAINER (frame), vbox);
gtk_widget_show (vbox);
entry = pika_prop_entry_new (G_OBJECT (info), "name", -1);
gtk_box_pack_start (GTK_BOX (vbox), entry, FALSE, FALSE, 0);
button = pika_prop_check_button_new (G_OBJECT (info), "debug-events",
_("_Dump events from this controller"));
gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
button = pika_prop_check_button_new (G_OBJECT (info), "enabled",
_("_Enable this controller"));
gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
frame = pika_frame_new (controller_class->name);
gtk_box_pack_start (GTK_BOX (editor), frame, TRUE, TRUE, 0);
gtk_widget_show (frame);
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 4);
gtk_container_add (GTK_CONTAINER (frame), vbox);
gtk_widget_show (vbox);
grid = gtk_grid_new ();
gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
gtk_grid_set_column_spacing (GTK_GRID (grid), 6);
gtk_box_pack_start (GTK_BOX (vbox), grid, FALSE, FALSE, 0);
gtk_widget_show (grid);
row = 0;
pika_grid_attach_aligned (GTK_GRID (grid), 0, row++,
_("Name:"), 0.0, 0.5,
pika_prop_label_new (G_OBJECT (controller),
"name"),
1);
pika_grid_attach_aligned (GTK_GRID (grid), 0, row++,
_("State:"), 0.0, 0.5,
pika_prop_label_new (G_OBJECT (controller),
"state"),
1);
property_specs =
g_object_class_list_properties (G_OBJECT_CLASS (controller_class),
&n_property_specs);
for (i = 0; i < n_property_specs; i++)
{
GParamSpec *pspec = property_specs[i];
GtkWidget *widget;
if (pspec->owner_type == PIKA_TYPE_CONTROLLER)
continue;
if (G_IS_PARAM_SPEC_STRING (pspec))
{
widget = pika_controller_string_view_new (controller, pspec);
pika_grid_attach_aligned (GTK_GRID (grid), 0, row++,
g_param_spec_get_nick (pspec),
0.0, 0.5,
widget,
1);
}
else if (G_IS_PARAM_SPEC_INT (pspec))
{
widget = pika_controller_int_view_new (controller, pspec);
gtk_widget_set_halign (widget, GTK_ALIGN_START);
pika_grid_attach_aligned (GTK_GRID (grid), 0, row++,
g_param_spec_get_nick (pspec),
0.0, 0.5,
widget,
1);
}
}
g_free (property_specs);
store = gtk_list_store_new (N_COLUMNS,
G_TYPE_STRING,
G_TYPE_STRING,
G_TYPE_STRING,
G_TYPE_STRING);
tv = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
g_object_unref (store);
sw = gtk_scrolled_window_new (NULL, NULL);
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw),
GTK_SHADOW_IN);
gtk_widget_set_size_request (sw, 400, 300);
gtk_container_add (GTK_CONTAINER (sw), tv);
gtk_widget_show (tv);
gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE, 0);
gtk_widget_show (sw);
g_signal_connect (tv, "row-activated",
G_CALLBACK (pika_controller_editor_row_activated),
editor);
editor->sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (tv));
g_signal_connect (editor->sel, "changed",
G_CALLBACK (pika_controller_editor_sel_changed),
editor);
ui_manager = pika_ui_managers_from_name ("<Image>")->data;
n_events = pika_controller_get_n_events (controller);
for (i = 0; i < n_events; i++)
{
GtkTreeIter iter;
const gchar *event_name;
const gchar *event_blurb;
const gchar *event_action;
const gchar *icon_name = NULL;
event_name = pika_controller_get_event_name (controller, i);
event_blurb = pika_controller_get_event_blurb (controller, i);
event_action = g_hash_table_lookup (info->mapping, event_name);
if (event_action)
{
PikaAction *action;
action = pika_ui_manager_find_action (ui_manager, NULL, event_action);
if (action)
icon_name = pika_action_get_icon_name (action);
}
gtk_list_store_append (store, &iter);
gtk_list_store_set (store, &iter,
COLUMN_EVENT, event_name,
COLUMN_BLURB, event_blurb,
COLUMN_ICON_NAME, icon_name,
COLUMN_ACTION, event_action,
-1);
}
gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (tv), 0,
_("Event"),
gtk_cell_renderer_text_new (),
"text", COLUMN_BLURB,
NULL);
column = gtk_tree_view_column_new ();
gtk_tree_view_column_set_title (column, _("Action"));
gtk_tree_view_append_column (GTK_TREE_VIEW (tv), column);
cell = gtk_cell_renderer_pixbuf_new ();
gtk_tree_view_column_pack_start (column, cell, FALSE);
gtk_tree_view_column_set_attributes (column, cell,
"icon-name", COLUMN_ICON_NAME,
NULL);
cell = gtk_cell_renderer_text_new ();
gtk_tree_view_column_pack_start (column, cell, TRUE);
gtk_tree_view_column_set_attributes (column, cell,
"text", COLUMN_ACTION,
NULL);
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
gtk_box_pack_end (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
gtk_widget_show (hbox);
editor->grab_button = gtk_toggle_button_new_with_mnemonic (_("_Grab event"));
gtk_box_pack_start (GTK_BOX (hbox), editor->grab_button, TRUE, TRUE, 0);
gtk_widget_show (editor->grab_button);
g_signal_connect (editor->grab_button, "toggled",
G_CALLBACK (pika_controller_editor_grab_toggled),
editor);
pika_help_set_help_data (editor->grab_button,
_("Select the next event arriving from "
"the controller"),
NULL);
editor->edit_button = gtk_button_new_with_mnemonic (_("_Edit event"));
gtk_box_pack_start (GTK_BOX (hbox), editor->edit_button, TRUE, TRUE, 0);
gtk_widget_show (editor->edit_button);
g_signal_connect (editor->edit_button, "clicked",
G_CALLBACK (pika_controller_editor_edit_clicked),
editor);
editor->delete_button = gtk_button_new_with_mnemonic (_("_Clear event"));
gtk_box_pack_start (GTK_BOX (hbox), editor->delete_button, TRUE, TRUE, 0);
gtk_widget_show (editor->delete_button);
g_signal_connect (editor->delete_button, "clicked",
G_CALLBACK (pika_controller_editor_delete_clicked),
editor);
gtk_widget_set_sensitive (editor->edit_button, FALSE);
gtk_widget_set_sensitive (editor->delete_button, FALSE);
}
static void
pika_controller_editor_finalize (GObject *object)
{
PikaControllerEditor *editor = PIKA_CONTROLLER_EDITOR (object);
if (editor->info)
{
pika_controller_info_set_event_snooper (editor->info, NULL, NULL);
g_clear_object (&editor->info);
}
g_clear_object (&editor->context);
if (editor->edit_dialog)
gtk_widget_destroy (editor->edit_dialog);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
pika_controller_editor_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
PikaControllerEditor *editor = PIKA_CONTROLLER_EDITOR (object);
switch (property_id)
{
case PROP_CONTROLLER_INFO:
editor->info = g_value_dup_object (value);
break;
case PROP_CONTEXT:
editor->context = g_value_dup_object (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
pika_controller_editor_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
PikaControllerEditor *editor = PIKA_CONTROLLER_EDITOR (object);
switch (property_id)
{
case PROP_CONTROLLER_INFO:
g_value_set_object (value, editor->info);
break;
case PROP_CONTEXT:
g_value_set_object (value, editor->context);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
pika_controller_editor_unmap (GtkWidget *widget)
{
PikaControllerEditor *editor = PIKA_CONTROLLER_EDITOR (widget);
if (editor->edit_dialog)
gtk_dialog_response (GTK_DIALOG (editor->edit_dialog),
GTK_RESPONSE_CANCEL);
GTK_WIDGET_CLASS (parent_class)->unmap (widget);
}
/* public functions */
GtkWidget *
pika_controller_editor_new (PikaControllerInfo *info,
PikaContext *context)
{
g_return_val_if_fail (PIKA_IS_CONTROLLER_INFO (info), NULL);
g_return_val_if_fail (PIKA_IS_CONTEXT (context), NULL);
return g_object_new (PIKA_TYPE_CONTROLLER_EDITOR,
"controller-info", info,
"context", context,
NULL);
}
/* private functions */
static void
pika_controller_editor_sel_changed (GtkTreeSelection *sel,
PikaControllerEditor *editor)
{
GtkTreeModel *model;
GtkTreeIter iter;
gchar *edit_help = NULL;
gchar *delete_help = NULL;
gboolean edit_sensitive = FALSE;
gboolean delete_sensitive = FALSE;
if (gtk_tree_selection_get_selected (sel, &model, &iter))
{
gchar *event = NULL;
gchar *action = NULL;
gtk_tree_model_get (model, &iter,
COLUMN_BLURB, &event,
COLUMN_ACTION, &action,
-1);
if (action)
{
g_free (action);
delete_sensitive = TRUE;
if (event)
delete_help =
g_strdup_printf (_("Remove the action assigned to '%s'"), event);
}
edit_sensitive = TRUE;
if (event)
edit_help = g_strdup_printf (_("Assign an action to '%s'"), event);
g_free (event);
}
pika_help_set_help_data (editor->edit_button, edit_help, NULL);
gtk_widget_set_sensitive (editor->edit_button, edit_sensitive);
g_free (edit_help);
pika_help_set_help_data (editor->delete_button, delete_help, NULL);
gtk_widget_set_sensitive (editor->delete_button, delete_sensitive);
g_free (delete_help);
pika_controller_info_set_event_snooper (editor->info, NULL, NULL);
}
static void
pika_controller_editor_row_activated (GtkTreeView *tv,
GtkTreePath *path,
GtkTreeViewColumn *column,
PikaControllerEditor *editor)
{
if (gtk_widget_is_sensitive (editor->edit_button))
gtk_button_clicked (GTK_BUTTON (editor->edit_button));
}
static gboolean
pika_controller_editor_snooper (PikaControllerInfo *info,
PikaController *controller,
const PikaControllerEvent *event,
gpointer user_data)
{
PikaControllerEditor *editor = PIKA_CONTROLLER_EDITOR (user_data);
GtkTreeModel *model;
GtkTreeIter iter;
gboolean iter_valid;
const gchar *event_name;
gtk_tree_selection_get_selected (editor->sel, &model, &iter);
event_name = pika_controller_get_event_name (info->controller,
event->any.event_id);
for (iter_valid = gtk_tree_model_get_iter_first (model, &iter);
iter_valid;
iter_valid = gtk_tree_model_iter_next (model, &iter))
{
gchar *list_name;
gtk_tree_model_get (model, &iter,
COLUMN_EVENT, &list_name,
-1);
if (! strcmp (list_name, event_name))
{
GtkTreeView *view;
GtkTreePath *path;
view = gtk_tree_selection_get_tree_view (editor->sel);
gtk_tree_selection_select_iter (editor->sel, &iter);
path = gtk_tree_model_get_path (model, &iter);
gtk_tree_view_scroll_to_cell (view, path, NULL, FALSE, 0.0, 0.0);
gtk_tree_view_set_cursor (view, path, NULL, FALSE);
gtk_tree_path_free (path);
gtk_widget_grab_focus (GTK_WIDGET (view));
g_free (list_name);
break;
}
g_free (list_name);
}
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (editor->grab_button), FALSE);
return TRUE;
}
static void
pika_controller_editor_grab_toggled (GtkWidget *button,
PikaControllerEditor *editor)
{
if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
{
pika_controller_info_set_event_snooper (editor->info,
pika_controller_editor_snooper,
editor);
}
else
{
pika_controller_info_set_event_snooper (editor->info, NULL, NULL);
}
}
static void
pika_controller_editor_edit_clicked (GtkWidget *button,
PikaControllerEditor *editor)
{
GtkTreeModel *model;
GtkTreeIter iter;
gchar *event_name = NULL;
gchar *event_blurb = NULL;
gchar *action_name = NULL;
pika_controller_info_set_event_snooper (editor->info, NULL, NULL);
if (gtk_tree_selection_get_selected (editor->sel, &model, &iter))
gtk_tree_model_get (model, &iter,
COLUMN_EVENT, &event_name,
COLUMN_BLURB, &event_blurb,
COLUMN_ACTION, &action_name,
-1);
if (event_name)
{
GtkWidget *view;
gchar *title;
title = g_strdup_printf (_("Select Action for Event '%s'"),
event_blurb);
g_set_weak_pointer
(&editor->edit_dialog,
pika_viewable_dialog_new (g_list_prepend (NULL, editor->info), editor->context,
_("Select Controller Event Action"),
"pika-controller-action-dialog",
PIKA_ICON_EDIT,
title,
gtk_widget_get_toplevel (GTK_WIDGET (editor)),
pika_standard_help_func,
PIKA_HELP_PREFS_INPUT_CONTROLLERS,
_("_Cancel"), GTK_RESPONSE_CANCEL,
_("_OK"), GTK_RESPONSE_OK,
NULL));
g_free (title);
pika_dialog_set_alternative_button_order (GTK_DIALOG (editor->edit_dialog),
GTK_RESPONSE_OK,
GTK_RESPONSE_CANCEL,
-1);
pika_dialog_factory_add_foreign (pika_dialog_factory_get_singleton (),
"pika-controller-action-dialog",
editor->edit_dialog,
pika_widget_get_monitor (button));
g_signal_connect (editor->edit_dialog, "response",
G_CALLBACK (pika_controller_editor_edit_response),
editor);
view = pika_action_editor_new (editor->context->pika,
action_name, FALSE);
gtk_container_set_border_width (GTK_CONTAINER (view), 12);
gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (editor->edit_dialog))),
view, TRUE, TRUE, 0);
gtk_widget_show (view);
g_signal_connect (PIKA_ACTION_EDITOR (view)->view, "row-activated",
G_CALLBACK (pika_controller_editor_edit_activated),
editor);
g_set_weak_pointer
(&editor->edit_sel,
gtk_tree_view_get_selection (GTK_TREE_VIEW (PIKA_ACTION_EDITOR (view)->view)));
gtk_widget_set_sensitive (GTK_WIDGET (editor), FALSE);
gtk_widget_show (editor->edit_dialog);
g_free (event_name);
g_free (event_blurb);
g_free (action_name);
}
}
static void
pika_controller_editor_delete_clicked (GtkWidget *button,
PikaControllerEditor *editor)
{
GtkTreeModel *model;
GtkTreeIter iter;
gchar *event_name = NULL;
pika_controller_info_set_event_snooper (editor->info, NULL, NULL);
if (gtk_tree_selection_get_selected (editor->sel, &model, &iter))
gtk_tree_model_get (model, &iter,
COLUMN_EVENT, &event_name,
-1);
if (event_name)
{
g_hash_table_remove (editor->info->mapping, event_name);
g_free (event_name);
gtk_list_store_set (GTK_LIST_STORE (model), &iter,
COLUMN_ICON_NAME, NULL,
COLUMN_ACTION, NULL,
-1);
}
}
static void
pika_controller_editor_edit_activated (GtkTreeView *tv,
GtkTreePath *path,
GtkTreeViewColumn *column,
PikaControllerEditor *editor)
{
gtk_dialog_response (GTK_DIALOG (editor->edit_dialog), GTK_RESPONSE_OK);
}
static void
pika_controller_editor_edit_response (GtkWidget *dialog,
gint response_id,
PikaControllerEditor *editor)
{
gtk_widget_set_sensitive (GTK_WIDGET (editor), TRUE);
if (response_id == GTK_RESPONSE_OK)
{
GtkTreeModel *model;
GtkTreeIter iter;
gchar *event_name = NULL;
gchar *icon_name = NULL;
gchar *action_name = NULL;
if (gtk_tree_selection_get_selected (editor->edit_sel, &model, &iter))
gtk_tree_model_get (model, &iter,
PIKA_ACTION_VIEW_COLUMN_ICON_NAME, &icon_name,
PIKA_ACTION_VIEW_COLUMN_NAME, &action_name,
-1);
if (gtk_tree_selection_get_selected (editor->sel, &model, &iter))
gtk_tree_model_get (model, &iter,
COLUMN_EVENT, &event_name,
-1);
if (event_name && action_name)
{
g_hash_table_insert (editor->info->mapping,
g_strdup (event_name),
g_strdup (action_name));
gtk_list_store_set (GTK_LIST_STORE (model), &iter,
COLUMN_ICON_NAME, icon_name,
COLUMN_ACTION, action_name,
-1);
}
g_free (event_name);
g_free (icon_name);
g_free (action_name);
pika_controller_editor_sel_changed (editor->sel, editor);
}
gtk_widget_destroy (dialog);
}
static GtkWidget *
pika_controller_string_view_new (PikaController *controller,
GParamSpec *pspec)
{
GtkWidget *widget = NULL;
g_return_val_if_fail (G_IS_PARAM_SPEC_STRING (pspec), NULL);
if (pspec->flags & G_PARAM_WRITABLE)
{
GtkTreeModel *model = NULL;
gchar *model_name = g_strdup_printf ("%s-values", pspec->name);
GParamSpec *model_spec;
model_spec = g_object_class_find_property (G_OBJECT_GET_CLASS (controller),
model_name);
if (G_IS_PARAM_SPEC_OBJECT (model_spec) &&
g_type_is_a (model_spec->value_type, GTK_TYPE_LIST_STORE))
{
g_object_get (controller,
model_name, &model,
NULL);
}
g_free (model_name);
if (model)
{
widget = pika_prop_string_combo_box_new (G_OBJECT (controller),
pspec->name, model, 0, 1);
g_object_unref (model);
}
else
{
widget = pika_prop_entry_new (G_OBJECT (controller), pspec->name, -1);
}
}
else
{
widget = pika_prop_label_new (G_OBJECT (controller), pspec->name);
}
return widget;
}
static GtkWidget *
pika_controller_int_view_new (PikaController *controller,
GParamSpec *pspec)
{
GtkWidget *widget = NULL;
g_return_val_if_fail (G_IS_PARAM_SPEC_INT (pspec), NULL);
if (pspec->flags & G_PARAM_WRITABLE)
{
PikaIntStore *model = NULL;
gchar *model_name = g_strdup_printf ("%s-values", pspec->name);
GParamSpec *model_spec;
model_spec = g_object_class_find_property (G_OBJECT_GET_CLASS (controller),
model_name);
if (G_IS_PARAM_SPEC_OBJECT (model_spec) &&
g_type_is_a (model_spec->value_type, PIKA_TYPE_INT_STORE))
{
g_object_get (controller,
model_name, &model,
NULL);
}
g_free (model_name);
if (model)
{
widget = pika_prop_int_combo_box_new (G_OBJECT (controller),
pspec->name, model);
g_object_unref (model);
}
else
{
widget = pika_prop_spin_button_new (G_OBJECT (controller),
pspec->name, 1, 8, 0);
}
}
else
{
widget = pika_prop_label_new (G_OBJECT (controller), pspec->name);
}
return widget;
}

View File

@ -0,0 +1,68 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikacontrollereditor.h
* Copyright (C) 2004 Michael Natterer <mitch@gimp.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __PIKA_CONTROLLER_EDITOR_H__
#define __PIKA_CONTROLLER_EDITOR_H__
#define PIKA_TYPE_CONTROLLER_EDITOR (pika_controller_editor_get_type ())
#define PIKA_CONTROLLER_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIKA_TYPE_CONTROLLER_EDITOR, PikaControllerEditor))
#define PIKA_CONTROLLER_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIKA_TYPE_CONTROLLER_EDITOR, PikaControllerEditorClass))
#define PIKA_IS_CONTROLLER_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIKA_TYPE_CONTROLLER_EDITOR))
#define PIKA_IS_CONTROLLER_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIKA_TYPE_CONTROLLER_EDITOR))
#define PIKA_CONTROLLER_EDITOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIKA_TYPE_CONTROLLER_EDITOR, PikaControllerEditorClass))
typedef struct _PikaControllerEditorClass PikaControllerEditorClass;
struct _PikaControllerEditor
{
GtkBox parent_instance;
PikaControllerInfo *info;
PikaContext *context;
GtkTreeSelection *sel;
GtkWidget *grab_button;
GtkWidget *edit_button;
GtkWidget *delete_button;
GtkWidget *edit_dialog;
GtkTreeSelection *edit_sel;
};
struct _PikaControllerEditorClass
{
GtkBoxClass parent_class;
};
GType pika_controller_editor_get_type (void) G_GNUC_CONST;
GtkWidget * pika_controller_editor_new (PikaControllerInfo *info,
PikaContext *context);
#endif /* __PIKA_CONTROLLER_EDITOR_H__ */

View File

@ -0,0 +1,537 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* pikacontrollerinfo.c
* Copyright (C) 2004-2005 Michael Natterer <mitch@gimp.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <string.h>
#include <gegl.h>
#include <gtk/gtk.h>
#include "libpikabase/pikabase.h"
#include "libpikaconfig/pikaconfig.h"
#include "libpikawidgets/pikawidgets.h"
#define PIKA_ENABLE_CONTROLLER_UNDER_CONSTRUCTION
#include "libpikawidgets/pikacontroller.h"
#include "widgets-types.h"
#include "core/pikamarshal.h"
#include "pikacontrollerinfo.h"
#include "pika-intl.h"
enum
{
PROP_0,
PROP_ENABLED,
PROP_DEBUG_EVENTS,
PROP_CONTROLLER,
PROP_MAPPING
};
enum
{
EVENT_MAPPED,
LAST_SIGNAL
};
static void pika_controller_info_config_iface_init (PikaConfigInterface *iface);
static void pika_controller_info_finalize (GObject *object);
static void pika_controller_info_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
static void pika_controller_info_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
static gboolean pika_controller_info_serialize_property (PikaConfig *config,
guint property_id,
const GValue *value,
GParamSpec *pspec,
PikaConfigWriter *writer);
static gboolean pika_controller_info_deserialize_property (PikaConfig *config,
guint property_id,
GValue *value,
GParamSpec *pspec,
GScanner *scanner,
GTokenType *expected);
static gboolean pika_controller_info_event (PikaController *controller,
const PikaControllerEvent *event,
PikaControllerInfo *info);
G_DEFINE_TYPE_WITH_CODE (PikaControllerInfo, pika_controller_info,
PIKA_TYPE_VIEWABLE,
G_IMPLEMENT_INTERFACE (PIKA_TYPE_CONFIG,
pika_controller_info_config_iface_init))
#define parent_class pika_controller_info_parent_class
static guint info_signals[LAST_SIGNAL] = { 0 };
static void
pika_controller_info_class_init (PikaControllerInfoClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
PikaViewableClass *viewable_class = PIKA_VIEWABLE_CLASS (klass);
object_class->finalize = pika_controller_info_finalize;
object_class->set_property = pika_controller_info_set_property;
object_class->get_property = pika_controller_info_get_property;
viewable_class->default_icon_name = PIKA_ICON_CONTROLLER;
PIKA_CONFIG_PROP_BOOLEAN (object_class, PROP_ENABLED,
"enabled",
_("Enabled"),
NULL,
TRUE,
PIKA_PARAM_STATIC_STRINGS);
PIKA_CONFIG_PROP_BOOLEAN (object_class, PROP_DEBUG_EVENTS,
"debug-events",
_("Debug events"),
NULL,
FALSE,
PIKA_PARAM_STATIC_STRINGS);
PIKA_CONFIG_PROP_OBJECT (object_class, PROP_CONTROLLER,
"controller",
"Controller",
NULL,
PIKA_TYPE_CONTROLLER,
PIKA_PARAM_STATIC_STRINGS);
PIKA_CONFIG_PROP_BOXED (object_class, PROP_MAPPING,
"mapping",
"Mapping",
NULL,
G_TYPE_HASH_TABLE,
PIKA_PARAM_STATIC_STRINGS);
info_signals[EVENT_MAPPED] =
g_signal_new ("event-mapped",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (PikaControllerInfoClass, event_mapped),
g_signal_accumulator_true_handled, NULL,
pika_marshal_BOOLEAN__OBJECT_POINTER_STRING,
G_TYPE_BOOLEAN, 3,
G_TYPE_OBJECT,
G_TYPE_POINTER,
G_TYPE_STRING);
}
static void
pika_controller_info_init (PikaControllerInfo *info)
{
info->controller = NULL;
info->mapping = g_hash_table_new_full (g_str_hash,
g_str_equal,
(GDestroyNotify) g_free,
(GDestroyNotify) g_free);
}
static void
pika_controller_info_config_iface_init (PikaConfigInterface *iface)
{
iface->serialize_property = pika_controller_info_serialize_property;
iface->deserialize_property = pika_controller_info_deserialize_property;
}
static void
pika_controller_info_finalize (GObject *object)
{
PikaControllerInfo *info = PIKA_CONTROLLER_INFO (object);
g_clear_object (&info->controller);
g_clear_pointer (&info->mapping, g_hash_table_unref);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
pika_controller_info_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
PikaControllerInfo *info = PIKA_CONTROLLER_INFO (object);
switch (property_id)
{
case PROP_ENABLED:
info->enabled = g_value_get_boolean (value);
break;
case PROP_DEBUG_EVENTS:
info->debug_events = g_value_get_boolean (value);
break;
case PROP_CONTROLLER:
if (info->controller)
{
g_signal_handlers_disconnect_by_func (info->controller,
pika_controller_info_event,
info);
g_object_unref (info->controller);
}
info->controller = g_value_dup_object (value);
if (info->controller)
{
PikaControllerClass *controller_class;
g_signal_connect_object (info->controller, "event",
G_CALLBACK (pika_controller_info_event),
G_OBJECT (info),
0);
controller_class = PIKA_CONTROLLER_GET_CLASS (info->controller);
pika_viewable_set_icon_name (PIKA_VIEWABLE (info),
controller_class->icon_name);
}
break;
case PROP_MAPPING:
if (info->mapping)
g_hash_table_unref (info->mapping);
info->mapping = g_value_dup_boxed (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
pika_controller_info_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
PikaControllerInfo *info = PIKA_CONTROLLER_INFO (object);
switch (property_id)
{
case PROP_ENABLED:
g_value_set_boolean (value, info->enabled);
break;
case PROP_DEBUG_EVENTS:
g_value_set_boolean (value, info->debug_events);
break;
case PROP_CONTROLLER:
g_value_set_object (value, info->controller);
break;
case PROP_MAPPING:
g_value_set_boxed (value, info->mapping);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
pika_controller_info_serialize_mapping (gpointer key,
gpointer value,
gpointer data)
{
const gchar *event_name = key;
const gchar *action_name = value;
PikaConfigWriter *writer = data;
pika_config_writer_open (writer, "map");
pika_config_writer_string (writer, event_name);
pika_config_writer_string (writer, action_name);
pika_config_writer_close (writer);
}
static gboolean
pika_controller_info_serialize_property (PikaConfig *config,
guint property_id,
const GValue *value,
GParamSpec *pspec,
PikaConfigWriter *writer)
{
GHashTable *mapping;
if (property_id != PROP_MAPPING)
return FALSE;
mapping = g_value_get_boxed (value);
if (mapping)
{
pika_config_writer_open (writer, pspec->name);
g_hash_table_foreach (mapping,
(GHFunc) pika_controller_info_serialize_mapping,
writer);
pika_config_writer_close (writer);
}
return TRUE;
}
static gboolean
pika_controller_info_deserialize_property (PikaConfig *config,
guint property_id,
GValue *value,
GParamSpec *pspec,
GScanner *scanner,
GTokenType *expected)
{
GHashTable *mapping;
GTokenType token;
if (property_id != PROP_MAPPING)
return FALSE;
mapping = g_hash_table_new_full (g_str_hash,
g_str_equal,
(GDestroyNotify) g_free,
(GDestroyNotify) g_free);
token = G_TOKEN_LEFT_PAREN;
while (g_scanner_peek_next_token (scanner) == token)
{
token = g_scanner_get_next_token (scanner);
switch (token)
{
case G_TOKEN_LEFT_PAREN:
token = G_TOKEN_IDENTIFIER;
break;
case G_TOKEN_IDENTIFIER:
if (! strcmp (scanner->value.v_identifier, "map"))
{
gchar *event_name;
gchar *action_name;
token = G_TOKEN_STRING;
if (! pika_scanner_parse_string (scanner, &event_name))
goto error;
token = G_TOKEN_STRING;
if (! pika_scanner_parse_string (scanner, &action_name))
goto error;
g_hash_table_insert (mapping, event_name, action_name);
}
token = G_TOKEN_RIGHT_PAREN;
break;
case G_TOKEN_RIGHT_PAREN:
token = G_TOKEN_LEFT_PAREN;
break;
default:
break;
}
}
if (token == G_TOKEN_LEFT_PAREN)
{
token = G_TOKEN_RIGHT_PAREN;
if (g_scanner_peek_next_token (scanner) == token)
{
g_value_take_boxed (value, mapping);
}
else
{
goto error;
}
}
else
{
error:
g_hash_table_unref (mapping);
*expected = token;
}
return TRUE;
}
/* public functions */
PikaControllerInfo *
pika_controller_info_new (GType type)
{
PikaControllerClass *controller_class;
PikaController *controller;
PikaControllerInfo *info;
g_return_val_if_fail (g_type_is_a (type, PIKA_TYPE_CONTROLLER), NULL);
controller_class = g_type_class_ref (type);
controller = pika_controller_new (type);
info = g_object_new (PIKA_TYPE_CONTROLLER_INFO,
"name", controller_class->name,
"controller", controller,
NULL);
g_object_unref (controller);
g_type_class_unref (controller_class);
return info;
}
void
pika_controller_info_set_enabled (PikaControllerInfo *info,
gboolean enabled)
{
g_return_if_fail (PIKA_IS_CONTROLLER_INFO (info));
if (enabled != info->enabled)
g_object_set (info, "enabled", enabled, NULL);
}
gboolean
pika_controller_info_get_enabled (PikaControllerInfo *info)
{
g_return_val_if_fail (PIKA_IS_CONTROLLER_INFO (info), FALSE);
return info->enabled;
}
void
pika_controller_info_set_event_snooper (PikaControllerInfo *info,
PikaControllerEventSnooper snooper,
gpointer snooper_data)
{
g_return_if_fail (PIKA_IS_CONTROLLER_INFO (info));
info->snooper = snooper;
info->snooper_data = snooper_data;
}
/* private functions */
static gboolean
pika_controller_info_event (PikaController *controller,
const PikaControllerEvent *event,
PikaControllerInfo *info)
{
const gchar *event_name;
const gchar *event_blurb;
const gchar *action_name = NULL;
event_name = pika_controller_get_event_name (controller,
event->any.event_id);
event_blurb = pika_controller_get_event_blurb (controller,
event->any.event_id);
if (info->debug_events)
{
g_print ("Received '%s' (class '%s')\n"
" controller event '%s (%s)'\n",
controller->name, PIKA_CONTROLLER_GET_CLASS (controller)->name,
event_name, event_blurb);
switch (event->any.type)
{
case PIKA_CONTROLLER_EVENT_TRIGGER:
g_print (" (trigger event)\n");
break;
case PIKA_CONTROLLER_EVENT_VALUE:
if (G_VALUE_HOLDS_DOUBLE (&event->value.value))
g_print (" (value event, value = %f)\n",
g_value_get_double (&event->value.value));
else
g_print (" (value event, unhandled type '%s')\n",
g_type_name (event->value.value.g_type));
break;
}
}
if (info->snooper)
{
if (info->snooper (info, controller, event, info->snooper_data))
{
if (info->debug_events)
g_print (" intercepted by event snooper\n\n");
return TRUE;
}
}
if (! info->enabled)
{
if (info->debug_events)
g_print (" ignoring because controller is disabled\n\n");
return FALSE;
}
if (info->mapping)
action_name = g_hash_table_lookup (info->mapping, event_name);
if (action_name)
{
gboolean retval = FALSE;
if (info->debug_events)
g_print (" maps to action '%s'\n", action_name);
g_signal_emit (info, info_signals[EVENT_MAPPED], 0,
controller, event, action_name, &retval);
if (info->debug_events)
{
if (retval)
g_print (" action was found\n\n");
else
g_print (" action NOT found\n\n");
}
return retval;
}
else
{
if (info->debug_events)
g_print (" doesn't map to action\n\n");
}
return FALSE;
}

View File

@ -0,0 +1,86 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* pikacontrollerinfo.h
* Copyright (C) 2004-2005 Michael Natterer <mitch@gimp.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __PIKA_CONTROLLER_INFO_H__
#define __PIKA_CONTROLLER_INFO_H__
#include "core/pikaviewable.h"
typedef gboolean (* PikaControllerEventSnooper) (PikaControllerInfo *info,
PikaController *controller,
const PikaControllerEvent *event,
gpointer user_data);
#define PIKA_TYPE_CONTROLLER_INFO (pika_controller_info_get_type ())
#define PIKA_CONTROLLER_INFO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIKA_TYPE_CONTROLLER_INFO, PikaControllerInfo))
#define PIKA_CONTROLLER_INFO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIKA_TYPE_CONTROLLER_INFO, PikaControllerInfoClass))
#define PIKA_IS_CONTROLLER_INFO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIKA_TYPE_CONTROLLER_INFO))
#define PIKA_IS_CONTROLLER_INFO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIKA_TYPE_CONTROLLER_INFO))
#define PIKA_CONTROLLER_INFO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIKA_TYPE_CONTROLLER_INFO, PikaControllerInfoClass))
typedef struct _PikaControllerInfoClass PikaControllerInfoClass;
struct _PikaControllerInfo
{
PikaViewable parent_instance;
gboolean enabled;
gboolean debug_events;
PikaController *controller;
GHashTable *mapping;
PikaControllerEventSnooper snooper;
gpointer snooper_data;
};
struct _PikaControllerInfoClass
{
PikaViewableClass parent_class;
gboolean (* event_mapped) (PikaControllerInfo *info,
PikaController *controller,
const PikaControllerEvent *event,
const gchar *action_name);
};
GType pika_controller_info_get_type (void) G_GNUC_CONST;
PikaControllerInfo * pika_controller_info_new (GType type);
void pika_controller_info_set_enabled (PikaControllerInfo *info,
gboolean enabled);
gboolean pika_controller_info_get_enabled (PikaControllerInfo *info);
void pika_controller_info_set_event_snooper (PikaControllerInfo *info,
PikaControllerEventSnooper snooper,
gpointer snooper_data);
#endif /* __PIKA_CONTROLLER_INFO_H__ */

View File

@ -0,0 +1,297 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* pikacontrollerkeyboard.c
* Copyright (C) 2004-2015 Michael Natterer <mitch@gimp.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gegl.h>
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
#include "libpikawidgets/pikawidgets.h"
#include "widgets-types.h"
#include "pikacontrollerkeyboard.h"
#include "pikahelp-ids.h"
#include "pikawidgets-utils.h"
#include "pika-intl.h"
typedef struct _KeyboardEvent KeyboardEvent;
struct _KeyboardEvent
{
const guint keyval;
const gchar *modifier_string;
GdkModifierType modifiers;
const gchar *name;
const gchar *blurb;
};
static void pika_controller_keyboard_constructed (GObject *object);
static gint pika_controller_keyboard_get_n_events (PikaController *controller);
static const gchar * pika_controller_keyboard_get_event_name (PikaController *controller,
gint event_id);
static const gchar * pika_controller_keyboard_get_event_blurb (PikaController *controller,
gint event_id);
G_DEFINE_TYPE (PikaControllerKeyboard, pika_controller_keyboard,
PIKA_TYPE_CONTROLLER)
#define parent_class pika_controller_keyboard_parent_class
static KeyboardEvent keyboard_events[] =
{
{ GDK_KEY_Up, NULL, 0,
"cursor-up",
N_("Cursor Up") },
{ GDK_KEY_Up, "<Shift>", 0,
"cursor-up-shift",
N_("Cursor Up") },
{ GDK_KEY_Up, "<Primary>", 0,
"cursor-up-primary",
N_("Cursor Up") },
{ GDK_KEY_Up, "<Alt>", 0,
"cursor-up-alt",
N_("Cursor Up") },
{ GDK_KEY_Up, "<Shift><Primary>", 0,
"cursor-up-shift-primary",
N_("Cursor Up") },
{ GDK_KEY_Up, "<Shift><Alt>", 0,
"cursor-up-shift-alt",
N_("Cursor Up") },
{ GDK_KEY_Up, "<Primary><Alt>", 0,
"cursor-up-primary-alt",
N_("Cursor Up") },
{ GDK_KEY_Up, "<Shift><Primary><Alt>", 0,
"cursor-up-shift-primary-alt",
N_("Cursor Up") },
{ GDK_KEY_Down, NULL, 0,
"cursor-down",
N_("Cursor Down") },
{ GDK_KEY_Down, "<Shift>", 0,
"cursor-down-shift",
N_("Cursor Down") },
{ GDK_KEY_Down, "<Primary>", 0,
"cursor-down-primary",
N_("Cursor Down") },
{ GDK_KEY_Down, "<Alt>", 0,
"cursor-down-alt",
N_("Cursor Down") },
{ GDK_KEY_Down, "<Shift><Primary>", 0,
"cursor-down-shift-primary",
N_("Cursor Down") },
{ GDK_KEY_Down, "<Shift><Alt>", 0,
"cursor-down-shift-alt",
N_("Cursor Down") },
{ GDK_KEY_Down, "<Primary><Alt>", 0,
"cursor-down-primary-alt",
N_("Cursor Down") },
{ GDK_KEY_Down, "<Shift><Primary><Alt>", 0,
"cursor-down-shift-primary-alt",
N_("Cursor Down") },
{ GDK_KEY_Left, NULL, 0,
"cursor-left",
N_("Cursor Left") },
{ GDK_KEY_Left, "<Shift>", 0,
"cursor-left-shift",
N_("Cursor Left") },
{ GDK_KEY_Left, "<Primary>", 0,
"cursor-left-primary",
N_("Cursor Left") },
{ GDK_KEY_Left, "<Alt>", 0,
"cursor-left-alt",
N_("Cursor Left") },
{ GDK_KEY_Left, "<Shift><Primary>", 0,
"cursor-left-shift-primary",
N_("Cursor Left") },
{ GDK_KEY_Left, "<Shift><Alt>", 0,
"cursor-left-shift-alt",
N_("Cursor Left") },
{ GDK_KEY_Left, "<Primary><Alt>", 0,
"cursor-left-primary-alt",
N_("Cursor Left") },
{ GDK_KEY_Left, "<Shift><Primary><Alt>", 0,
"cursor-left-shift-primary-alt",
N_("Cursor Left") },
{ GDK_KEY_Right, NULL, 0,
"cursor-right",
N_("Cursor Right") },
{ GDK_KEY_Right, "<Shift>", 0,
"cursor-right-shift",
N_("Cursor Right") },
{ GDK_KEY_Right, "<Primary>", 0,
"cursor-right-primary",
N_("Cursor Right") },
{ GDK_KEY_Right, "<Alt>", 0,
"cursor-right-alt",
N_("Cursor Right") },
{ GDK_KEY_Right, "<Shift><Primary>", 0,
"cursor-right-shift-primary",
N_("Cursor Right") },
{ GDK_KEY_Right, "<Shift><Alt>", 0,
"cursor-right-shift-alt",
N_("Cursor Right") },
{ GDK_KEY_Right, "<Primary><Alt>", 0,
"cursor-right-primary-alt",
N_("Cursor Right") },
{ GDK_KEY_Right, "<Shift><Primary><Alt>", 0,
"cursor-right-shift-primary-alt",
N_("Cursor Right") }
};
static void
pika_controller_keyboard_class_init (PikaControllerKeyboardClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
PikaControllerClass *controller_class = PIKA_CONTROLLER_CLASS (klass);
object_class->constructed = pika_controller_keyboard_constructed;
controller_class->name = _("Keyboard");
controller_class->help_id = PIKA_HELP_CONTROLLER_KEYBOARD;
controller_class->icon_name = PIKA_ICON_CONTROLLER_KEYBOARD;
controller_class->get_n_events = pika_controller_keyboard_get_n_events;
controller_class->get_event_name = pika_controller_keyboard_get_event_name;
controller_class->get_event_blurb = pika_controller_keyboard_get_event_blurb;
}
static void
pika_controller_keyboard_init (PikaControllerKeyboard *keyboard)
{
static gboolean event_names_initialized = FALSE;
if (! event_names_initialized)
{
GdkKeymap *keymap = gdk_keymap_get_for_display (gdk_display_get_default ());
gint i;
for (i = 0; i < G_N_ELEMENTS (keyboard_events); i++)
{
KeyboardEvent *kevent = &keyboard_events[i];
if (kevent->modifier_string)
{
gtk_accelerator_parse (kevent->modifier_string, NULL,
&kevent->modifiers);
gdk_keymap_map_virtual_modifiers (keymap, &kevent->modifiers);
}
if (kevent->modifiers != 0)
{
kevent->blurb =
g_strdup_printf ("%s (%s)", gettext (kevent->blurb),
pika_get_mod_string (kevent->modifiers));
}
else
{
kevent->blurb = gettext (kevent->blurb);
}
}
event_names_initialized = TRUE;
}
}
static void
pika_controller_keyboard_constructed (GObject *object)
{
G_OBJECT_CLASS (parent_class)->constructed (object);
g_object_set (object,
"name", _("Keyboard Events"),
"state", _("Ready"),
NULL);
}
static gint
pika_controller_keyboard_get_n_events (PikaController *controller)
{
return G_N_ELEMENTS (keyboard_events);
}
static const gchar *
pika_controller_keyboard_get_event_name (PikaController *controller,
gint event_id)
{
if (event_id < 0 || event_id >= G_N_ELEMENTS (keyboard_events))
return NULL;
return keyboard_events[event_id].name;
}
static const gchar *
pika_controller_keyboard_get_event_blurb (PikaController *controller,
gint event_id)
{
if (event_id < 0 || event_id >= G_N_ELEMENTS (keyboard_events))
return NULL;
return keyboard_events[event_id].blurb;
}
gboolean
pika_controller_keyboard_key_press (PikaControllerKeyboard *keyboard,
const GdkEventKey *kevent)
{
gint i;
g_return_val_if_fail (PIKA_IS_CONTROLLER_KEYBOARD (keyboard), FALSE);
g_return_val_if_fail (kevent != NULL, FALSE);
/* start with the last event because the last ones in the
* up,down,left,right groups have the most keyboard modifiers
*/
for (i = G_N_ELEMENTS (keyboard_events) - 1; i >= 0; i--)
{
if (keyboard_events[i].keyval == kevent->keyval &&
(keyboard_events[i].modifiers & kevent->state) ==
keyboard_events[i].modifiers)
{
PikaControllerEvent controller_event;
PikaControllerEventTrigger *trigger;
trigger = (PikaControllerEventTrigger *) &controller_event;
trigger->type = PIKA_CONTROLLER_EVENT_TRIGGER;
trigger->source = PIKA_CONTROLLER (keyboard);
trigger->event_id = i;
return pika_controller_event (PIKA_CONTROLLER (keyboard),
&controller_event);
}
}
return FALSE;
}

View File

@ -0,0 +1,60 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* pikacontrollerkeyboard.h
* Copyright (C) 2004 Michael Natterer <mitch@gimp.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __PIKA_CONTROLLER_KEYBOARD_H__
#define __PIKA_CONTROLLER_KEYBOARD_H__
#define PIKA_ENABLE_CONTROLLER_UNDER_CONSTRUCTION
#include "libpikawidgets/pikacontroller.h"
#define PIKA_TYPE_CONTROLLER_KEYBOARD (pika_controller_keyboard_get_type ())
#define PIKA_CONTROLLER_KEYBOARD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIKA_TYPE_CONTROLLER_KEYBOARD, PikaControllerKeyboard))
#define PIKA_CONTROLLER_KEYBOARD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIKA_TYPE_CONTROLLER_KEYBOARD, PikaControllerKeyboardClass))
#define PIKA_IS_CONTROLLER_KEYBOARD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIKA_TYPE_CONTROLLER_KEYBOARD))
#define PIKA_IS_CONTROLLER_KEYBOARD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIKA_TYPE_CONTROLLER_KEYBOARD))
#define PIKA_CONTROLLER_KEYBOARD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIKA_TYPE_CONTROLLER_KEYBOARD, PikaControllerKeyboardClass))
typedef struct _PikaControllerKeyboardClass PikaControllerKeyboardClass;
struct _PikaControllerKeyboard
{
PikaController parent_instance;
};
struct _PikaControllerKeyboardClass
{
PikaControllerClass parent_class;
};
GType pika_controller_keyboard_get_type (void) G_GNUC_CONST;
gboolean pika_controller_keyboard_key_press (PikaControllerKeyboard *keyboard,
const GdkEventKey *kevent);
#endif /* __PIKA_CONTROLLER_KEYBOARD_H__ */

View File

@ -0,0 +1,707 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995-1999 Spencer Kimball and Peter Mattis
*
* pikacontrollerlist.c
* Copyright (C) 2005 Michael Natterer <mitch@gimp.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <string.h>
#include <gegl.h>
#include <gtk/gtk.h>
#include "libpikabase/pikabase.h"
#include "libpikawidgets/pikawidgets.h"
#define PIKA_ENABLE_CONTROLLER_UNDER_CONSTRUCTION
#include "libpikawidgets/pikacontroller.h"
#include "widgets-types.h"
#include "core/pika.h"
#include "core/pikacontainer.h"
#include "pikacontainertreeview.h"
#include "pikacontainerview.h"
#include "pikacontrollereditor.h"
#include "pikacontrollerlist.h"
#include "pikacontrollerinfo.h"
#include "pikacontrollerkeyboard.h"
#include "pikacontrollerwheel.h"
#include "pikacontrollers.h"
#include "pikadialogfactory.h"
#include "pikahelp-ids.h"
#include "pikamessagebox.h"
#include "pikamessagedialog.h"
#include "pikapropwidgets.h"
#include "pikauimanager.h"
#include "pikawidgets-utils.h"
#include "pika-intl.h"
enum
{
PROP_0,
PROP_PIKA
};
enum
{
COLUMN_ICON,
COLUMN_NAME,
COLUMN_TYPE,
N_COLUMNS
};
static void pika_controller_list_constructed (GObject *object);
static void pika_controller_list_finalize (GObject *object);
static void pika_controller_list_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
static void pika_controller_list_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
static void pika_controller_list_src_sel_changed (GtkTreeSelection *sel,
PikaControllerList *list);
static void pika_controller_list_row_activated (GtkTreeView *tv,
GtkTreePath *path,
GtkTreeViewColumn *column,
PikaControllerList *list);
static gboolean pika_controller_list_select_items(PikaContainerView *view,
GList *viewables,
GList *paths,
PikaControllerList *list);
static void pika_controller_list_activate_item (PikaContainerView *view,
PikaViewable *viewable,
gpointer insert_data,
PikaControllerList *list);
static void pika_controller_list_add_clicked (GtkWidget *button,
PikaControllerList *list);
static void pika_controller_list_remove_clicked (GtkWidget *button,
PikaControllerList *list);
static void pika_controller_list_edit_clicked (GtkWidget *button,
PikaControllerList *list);
static void pika_controller_list_edit_destroy (GtkWidget *widget,
PikaControllerInfo *info);
static void pika_controller_list_up_clicked (GtkWidget *button,
PikaControllerList *list);
static void pika_controller_list_down_clicked (GtkWidget *button,
PikaControllerList *list);
G_DEFINE_TYPE (PikaControllerList, pika_controller_list, GTK_TYPE_BOX)
#define parent_class pika_controller_list_parent_class
static void
pika_controller_list_class_init (PikaControllerListClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->constructed = pika_controller_list_constructed;
object_class->finalize = pika_controller_list_finalize;
object_class->set_property = pika_controller_list_set_property;
object_class->get_property = pika_controller_list_get_property;
g_object_class_install_property (object_class, PROP_PIKA,
g_param_spec_object ("pika",
NULL, NULL,
PIKA_TYPE_PIKA,
PIKA_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY));
}
static void
pika_controller_list_init (PikaControllerList *list)
{
GtkWidget *hbox;
GtkWidget *sw;
GtkWidget *tv;
GtkTreeViewColumn *column;
GtkCellRenderer *cell;
GtkWidget *vbox;
GtkWidget *image;
GtkIconSize icon_size;
gint icon_width;
gint icon_height;
GType *controller_types;
guint n_controller_types;
gint i;
gtk_orientable_set_orientation (GTK_ORIENTABLE (list),
GTK_ORIENTATION_VERTICAL);
list->pika = NULL;
list->hbox = hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
gtk_box_pack_start (GTK_BOX (list), hbox, TRUE, TRUE, 0);
gtk_widget_show (hbox);
sw = gtk_scrolled_window_new (NULL, NULL);
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw),
GTK_SHADOW_IN);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
GTK_POLICY_AUTOMATIC,
GTK_POLICY_AUTOMATIC);
gtk_box_pack_start (GTK_BOX (hbox), sw, TRUE, TRUE, 0);
gtk_widget_show (sw);
list->src = gtk_list_store_new (N_COLUMNS,
G_TYPE_STRING,
G_TYPE_STRING,
G_TYPE_GTYPE);
tv = gtk_tree_view_new_with_model (GTK_TREE_MODEL (list->src));
g_object_unref (list->src);
gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW (tv), FALSE);
column = gtk_tree_view_column_new ();
gtk_tree_view_column_set_title (column, _("Available Controllers"));
gtk_tree_view_append_column (GTK_TREE_VIEW (tv), column);
cell = gtk_cell_renderer_pixbuf_new ();
gtk_tree_view_column_pack_start (column, cell, FALSE);
gtk_tree_view_column_set_attributes (column, cell,
"icon-name", COLUMN_ICON,
NULL);
g_object_get (cell, "stock-size", &icon_size, NULL);
cell = gtk_cell_renderer_text_new ();
gtk_tree_view_column_pack_start (column, cell, TRUE);
gtk_tree_view_column_set_attributes (column, cell,
"text", COLUMN_NAME,
NULL);
gtk_container_add (GTK_CONTAINER (sw), tv);
gtk_widget_show (tv);
g_signal_connect_object (tv, "row-activated",
G_CALLBACK (pika_controller_list_row_activated),
G_OBJECT (list), 0);
list->src_sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (tv));
gtk_tree_selection_set_mode (list->src_sel, GTK_SELECTION_BROWSE);
g_signal_connect_object (list->src_sel, "changed",
G_CALLBACK (pika_controller_list_src_sel_changed),
G_OBJECT (list), 0);
controller_types = g_type_children (PIKA_TYPE_CONTROLLER,
&n_controller_types);
for (i = 0; i < n_controller_types; i++)
{
PikaControllerClass *controller_class;
GtkTreeIter iter;
controller_class = g_type_class_ref (controller_types[i]);
gtk_list_store_append (list->src, &iter);
gtk_list_store_set (list->src, &iter,
COLUMN_ICON, controller_class->icon_name,
COLUMN_NAME, controller_class->name,
COLUMN_TYPE, controller_types[i],
-1);
g_type_class_unref (controller_class);
}
g_free (controller_types);
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
gtk_box_set_homogeneous (GTK_BOX (vbox), TRUE);
gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
gtk_widget_show (vbox);
g_set_weak_pointer (&list->add_button, gtk_button_new ());
gtk_box_pack_start (GTK_BOX (vbox), list->add_button, TRUE, FALSE, 0);
gtk_widget_set_sensitive (list->add_button, FALSE);
gtk_widget_show (list->add_button);
image = gtk_image_new_from_icon_name (PIKA_ICON_GO_NEXT,
GTK_ICON_SIZE_BUTTON);
gtk_container_add (GTK_CONTAINER (list->add_button), image);
gtk_widget_show (image);
g_signal_connect (list->add_button, "clicked",
G_CALLBACK (pika_controller_list_add_clicked),
list);
g_set_weak_pointer (&list->remove_button, gtk_button_new ());
gtk_box_pack_start (GTK_BOX (vbox), list->remove_button, TRUE, FALSE, 0);
gtk_widget_set_sensitive (list->remove_button, FALSE);
gtk_widget_show (list->remove_button);
image = gtk_image_new_from_icon_name (PIKA_ICON_GO_PREVIOUS,
GTK_ICON_SIZE_BUTTON);
gtk_container_add (GTK_CONTAINER (list->remove_button), image);
gtk_widget_show (image);
g_signal_connect (list->remove_button, "clicked",
G_CALLBACK (pika_controller_list_remove_clicked),
list);
gtk_icon_size_lookup (icon_size, &icon_width, &icon_height);
list->dest = pika_container_tree_view_new (NULL, NULL, icon_height, 0);
pika_container_tree_view_set_main_column_title (PIKA_CONTAINER_TREE_VIEW (list->dest),
_("Active Controllers"));
gtk_tree_view_set_headers_visible (PIKA_CONTAINER_TREE_VIEW (list->dest)->view,
TRUE);
gtk_box_pack_start (GTK_BOX (list->hbox), list->dest, TRUE, TRUE, 0);
gtk_widget_show (list->dest);
g_signal_connect_object (list->dest, "select-items",
G_CALLBACK (pika_controller_list_select_items),
G_OBJECT (list), 0);
g_signal_connect_object (list->dest, "activate-item",
G_CALLBACK (pika_controller_list_activate_item),
G_OBJECT (list), 0);
list->edit_button =
pika_editor_add_button (PIKA_EDITOR (list->dest),
PIKA_ICON_DOCUMENT_PROPERTIES,
_("Configure the selected controller"),
NULL,
G_CALLBACK (pika_controller_list_edit_clicked),
NULL,
G_OBJECT (list));
list->up_button =
pika_editor_add_button (PIKA_EDITOR (list->dest),
PIKA_ICON_GO_UP,
_("Move the selected controller up"),
NULL,
G_CALLBACK (pika_controller_list_up_clicked),
NULL,
G_OBJECT (list));
list->down_button =
pika_editor_add_button (PIKA_EDITOR (list->dest),
PIKA_ICON_GO_DOWN,
_("Move the selected controller down"),
NULL,
G_CALLBACK (pika_controller_list_down_clicked),
NULL,
G_OBJECT (list));
gtk_widget_set_sensitive (list->edit_button, FALSE);
gtk_widget_set_sensitive (list->up_button, FALSE);
gtk_widget_set_sensitive (list->down_button, FALSE);
}
static void
pika_controller_list_constructed (GObject *object)
{
PikaControllerList *list = PIKA_CONTROLLER_LIST (object);
G_OBJECT_CLASS (parent_class)->constructed (object);
pika_assert (PIKA_IS_PIKA (list->pika));
pika_container_view_set_container (PIKA_CONTAINER_VIEW (list->dest),
pika_controllers_get_list (list->pika));
pika_container_view_set_context (PIKA_CONTAINER_VIEW (list->dest),
pika_get_user_context (list->pika));
}
static void
pika_controller_list_finalize (GObject *object)
{
PikaControllerList *list = PIKA_CONTROLLER_LIST (object);
g_clear_object (&list->pika);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
pika_controller_list_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
PikaControllerList *list = PIKA_CONTROLLER_LIST (object);
switch (property_id)
{
case PROP_PIKA:
list->pika = g_value_dup_object (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
pika_controller_list_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
PikaControllerList *list = PIKA_CONTROLLER_LIST (object);
switch (property_id)
{
case PROP_PIKA:
g_value_set_object (value, list->pika);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
/* public functions */
GtkWidget *
pika_controller_list_new (Pika *pika)
{
g_return_val_if_fail (PIKA_IS_PIKA (pika), NULL);
return g_object_new (PIKA_TYPE_CONTROLLER_LIST,
"pika", pika,
NULL);
}
/* private functions */
static void
pika_controller_list_src_sel_changed (GtkTreeSelection *sel,
PikaControllerList *list)
{
GtkTreeModel *model;
GtkTreeIter iter;
gchar *tip = NULL;
if (gtk_tree_selection_get_selected (sel, &model, &iter))
{
gchar *name;
gtk_tree_model_get (model, &iter,
COLUMN_NAME, &name,
COLUMN_TYPE, &list->src_gtype,
-1);
if (list->add_button)
{
tip =
g_strdup_printf (_("Add '%s' to the list of active controllers"),
name);
gtk_widget_set_sensitive (list->add_button, TRUE);
}
g_free (name);
}
else
{
if (list->add_button)
gtk_widget_set_sensitive (list->add_button, FALSE);
}
if (list->add_button)
{
pika_help_set_help_data (list->add_button, tip, NULL);
g_free (tip);
}
}
static void
pika_controller_list_row_activated (GtkTreeView *tv,
GtkTreePath *path,
GtkTreeViewColumn *column,
PikaControllerList *list)
{
if (gtk_widget_is_sensitive (list->add_button))
gtk_button_clicked (GTK_BUTTON (list->add_button));
}
static gboolean
pika_controller_list_select_items (PikaContainerView *view,
GList *viewables,
GList *paths,
PikaControllerList *list)
{
gboolean selected;
g_return_val_if_fail (g_list_length (viewables) < 2, FALSE);
list->dest_info = viewables ? PIKA_CONTROLLER_INFO (viewables->data) : NULL;
selected = PIKA_IS_CONTROLLER_INFO (list->dest_info);
if (list->remove_button)
{
PikaObject *object = PIKA_OBJECT (list->dest_info);
gchar *tip = NULL;
gtk_widget_set_sensitive (list->remove_button, selected);
if (selected)
tip =
g_strdup_printf (_("Remove '%s' from the list of active controllers"),
pika_object_get_name (object));
pika_help_set_help_data (list->remove_button, tip, NULL);
g_free (tip);
}
gtk_widget_set_sensitive (list->edit_button, selected);
gtk_widget_set_sensitive (list->up_button, selected);
gtk_widget_set_sensitive (list->down_button, selected);
return TRUE;
}
static void
pika_controller_list_activate_item (PikaContainerView *view,
PikaViewable *viewable,
gpointer insert_data,
PikaControllerList *list)
{
if (gtk_widget_is_sensitive (list->edit_button))
gtk_button_clicked (GTK_BUTTON (list->edit_button));
}
static void
pika_controller_list_add_clicked (GtkWidget *button,
PikaControllerList *list)
{
PikaControllerInfo *info;
PikaContainer *container;
if (list->src_gtype == PIKA_TYPE_CONTROLLER_KEYBOARD &&
pika_controllers_get_keyboard (list->pika) != NULL)
{
pika_message_literal (list->pika,
G_OBJECT (button), PIKA_MESSAGE_WARNING,
_("There can only be one active keyboard "
"controller.\n\n"
"You already have a keyboard controller in "
"your list of active controllers."));
return;
}
else if (list->src_gtype == PIKA_TYPE_CONTROLLER_WHEEL &&
pika_controllers_get_wheel (list->pika) != NULL)
{
pika_message_literal (list->pika,
G_OBJECT (button), PIKA_MESSAGE_WARNING,
_("There can only be one active wheel "
"controller.\n\n"
"You already have a wheel controller in "
"your list of active controllers."));
return;
}
info = pika_controller_info_new (list->src_gtype);
container = pika_controllers_get_list (list->pika);
pika_container_add (container, PIKA_OBJECT (info));
g_object_unref (info);
pika_container_view_select_item (PIKA_CONTAINER_VIEW (list->dest),
PIKA_VIEWABLE (info));
pika_controller_list_edit_clicked (list->edit_button, list);
}
static void
pika_controller_list_remove_clicked (GtkWidget *button,
PikaControllerList *list)
{
GtkWidget *dialog;
const gchar *name;
#define RESPONSE_DISABLE 1
dialog = pika_message_dialog_new (_("Remove Controller?"),
PIKA_ICON_DIALOG_WARNING,
GTK_WIDGET (list), GTK_DIALOG_MODAL,
NULL, NULL,
_("_Disable Controller"), RESPONSE_DISABLE,
_("_Cancel"), GTK_RESPONSE_CANCEL,
_("_Remove Controller"), GTK_RESPONSE_OK,
NULL);
pika_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
GTK_RESPONSE_OK,
GTK_RESPONSE_CANCEL,
RESPONSE_DISABLE,
-1);
name = pika_object_get_name (list->dest_info);
pika_message_box_set_primary_text (PIKA_MESSAGE_DIALOG (dialog)->box,
_("Remove Controller '%s'?"), name);
pika_message_box_set_text (PIKA_MESSAGE_DIALOG (dialog)->box,
"%s",
_("Removing this controller from the list of "
"active controllers will permanently delete "
"all event mappings you have configured.\n\n"
"Selecting \"Disable Controller\" will disable "
"the controller without removing it."));
switch (pika_dialog_run (PIKA_DIALOG (dialog)))
{
case RESPONSE_DISABLE:
pika_controller_info_set_enabled (list->dest_info, FALSE);
break;
case GTK_RESPONSE_OK:
{
GtkWidget *editor_dialog;
PikaContainer *container;
editor_dialog = g_object_get_data (G_OBJECT (list->dest_info),
"pika-controller-editor-dialog");
if (editor_dialog)
gtk_dialog_response (GTK_DIALOG (editor_dialog),
GTK_RESPONSE_DELETE_EVENT);
container = pika_controllers_get_list (list->pika);
pika_container_remove (container, PIKA_OBJECT (list->dest_info));
}
break;
default:
break;
}
gtk_widget_destroy (dialog);
}
static void
pika_controller_list_edit_clicked (GtkWidget *button,
PikaControllerList *list)
{
GtkWidget *dialog;
GtkWidget *editor;
dialog = g_object_get_data (G_OBJECT (list->dest_info),
"pika-controller-editor-dialog");
if (dialog)
{
gtk_window_present (GTK_WINDOW (dialog));
return;
}
dialog = pika_dialog_new (_("Configure Input Controller"),
"pika-controller-editor-dialog",
gtk_widget_get_toplevel (GTK_WIDGET (list)),
GTK_DIALOG_DESTROY_WITH_PARENT,
pika_standard_help_func,
PIKA_HELP_PREFS_INPUT_CONTROLLERS,
_("_Close"), GTK_RESPONSE_CLOSE,
NULL);
pika_dialog_factory_add_foreign (pika_dialog_factory_get_singleton (),
"pika-controller-editor-dialog",
dialog,
pika_widget_get_monitor (button));
g_signal_connect (dialog, "response",
G_CALLBACK (gtk_widget_destroy),
NULL);
editor = pika_controller_editor_new (list->dest_info,
pika_get_user_context (list->pika));
gtk_container_set_border_width (GTK_CONTAINER (editor), 12);
gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
editor, TRUE, TRUE, 0);
gtk_widget_show (editor);
g_object_set_data (G_OBJECT (list->dest_info), "pika-controller-editor-dialog",
dialog);
g_signal_connect_object (dialog, "destroy",
G_CALLBACK (pika_controller_list_edit_destroy),
G_OBJECT (list->dest_info), 0);
g_signal_connect_object (list, "destroy",
G_CALLBACK (gtk_widget_destroy),
G_OBJECT (dialog),
G_CONNECT_SWAPPED);
g_signal_connect_object (list, "unmap",
G_CALLBACK (gtk_widget_destroy),
G_OBJECT (dialog),
G_CONNECT_SWAPPED);
gtk_widget_show (dialog);
}
static void
pika_controller_list_edit_destroy (GtkWidget *widget,
PikaControllerInfo *info)
{
g_object_set_data (G_OBJECT (info), "pika-controller-editor-dialog", NULL);
}
static void
pika_controller_list_up_clicked (GtkWidget *button,
PikaControllerList *list)
{
PikaContainer *container;
gint index;
container = pika_controllers_get_list (list->pika);
index = pika_container_get_child_index (container,
PIKA_OBJECT (list->dest_info));
if (index > 0)
pika_container_reorder (container, PIKA_OBJECT (list->dest_info),
index - 1);
}
static void
pika_controller_list_down_clicked (GtkWidget *button,
PikaControllerList *list)
{
PikaContainer *container;
gint index;
container = pika_controllers_get_list (list->pika);
index = pika_container_get_child_index (container,
PIKA_OBJECT (list->dest_info));
if (index < pika_container_get_n_children (container) - 1)
pika_container_reorder (container, PIKA_OBJECT (list->dest_info),
index + 1);
}

View File

@ -0,0 +1,72 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikacontrollerlist.h
* Copyright (C) 2005 Michael Natterer <mitch@gimp.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __PIKA_CONTROLLER_LIST_H__
#define __PIKA_CONTROLLER_LIST_H__
#define PIKA_TYPE_CONTROLLER_LIST (pika_controller_list_get_type ())
#define PIKA_CONTROLLER_LIST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIKA_TYPE_CONTROLLER_LIST, PikaControllerList))
#define PIKA_CONTROLLER_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIKA_TYPE_CONTROLLER_LIST, PikaControllerListClass))
#define PIKA_IS_CONTROLLER_LIST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIKA_TYPE_CONTROLLER_LIST))
#define PIKA_IS_CONTROLLER_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIKA_TYPE_CONTROLLER_LIST))
#define PIKA_CONTROLLER_LIST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIKA_TYPE_CONTROLLER_LIST, PikaControllerListClass))
typedef struct _PikaControllerListClass PikaControllerListClass;
struct _PikaControllerList
{
GtkBox parent_instance;
Pika *pika;
GtkWidget *hbox;
GtkListStore *src;
GtkTreeSelection *src_sel;
GType src_gtype;
GtkWidget *dest;
PikaControllerInfo *dest_info;
GtkWidget *add_button;
GtkWidget *remove_button;
GtkWidget *edit_button;
GtkWidget *up_button;
GtkWidget *down_button;
};
struct _PikaControllerListClass
{
GtkBoxClass parent_class;
};
GType pika_controller_list_get_type (void) G_GNUC_CONST;
GtkWidget * pika_controller_list_new (Pika *pika);
#endif /* __PIKA_CONTROLLER_LIST_H__ */

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