Initial checkin of Pika from heckimp
This commit is contained in:
259
app/widgets/meson.build
Normal file
259
app/widgets/meson.build
Normal 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,
|
||||
],
|
||||
)
|
||||
209
app/widgets/pikaaccellabel.c
Normal file
209
app/widgets/pikaaccellabel.c
Normal 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;
|
||||
}
|
||||
62
app/widgets/pikaaccellabel.h
Normal file
62
app/widgets/pikaaccellabel.h
Normal 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__ */
|
||||
510
app/widgets/pikaaction-history.c
Normal file
510
app/widgets/pikaaction-history.c
Normal 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));
|
||||
}
|
||||
50
app/widgets/pikaaction-history.h
Normal file
50
app/widgets/pikaaction-history.h
Normal 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
1410
app/widgets/pikaaction.c
Normal file
File diff suppressed because it is too large
Load Diff
163
app/widgets/pikaaction.h
Normal file
163
app/widgets/pikaaction.h
Normal 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__ */
|
||||
147
app/widgets/pikaactioneditor.c
Normal file
147
app/widgets/pikaactioneditor.c
Normal 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);
|
||||
}
|
||||
|
||||
59
app/widgets/pikaactioneditor.h
Normal file
59
app/widgets/pikaactioneditor.h
Normal 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__ */
|
||||
249
app/widgets/pikaactionfactory.c
Normal file
249
app/widgets/pikaactionfactory.c
Normal 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);
|
||||
}
|
||||
89
app/widgets/pikaactionfactory.h
Normal file
89
app/widgets/pikaactionfactory.h
Normal 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__ */
|
||||
1224
app/widgets/pikaactiongroup.c
Normal file
1224
app/widgets/pikaactiongroup.c
Normal file
File diff suppressed because it is too large
Load Diff
263
app/widgets/pikaactiongroup.h
Normal file
263
app/widgets/pikaactiongroup.h
Normal 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__ */
|
||||
424
app/widgets/pikaactionimpl.c
Normal file
424
app/widgets/pikaactionimpl.c
Normal 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);
|
||||
}
|
||||
67
app/widgets/pikaactionimpl.h
Normal file
67
app/widgets/pikaactionimpl.h
Normal 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__ */
|
||||
831
app/widgets/pikaactionview.c
Normal file
831
app/widgets/pikaactionview.c
Normal 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 });
|
||||
}
|
||||
79
app/widgets/pikaactionview.h
Normal file
79
app/widgets/pikaactionview.h
Normal 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__ */
|
||||
413
app/widgets/pikablobeditor.c
Normal file
413
app/widgets/pikablobeditor.c
Normal 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);
|
||||
}
|
||||
64
app/widgets/pikablobeditor.h
Normal file
64
app/widgets/pikablobeditor.h
Normal 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__ */
|
||||
461
app/widgets/pikabrusheditor.c
Normal file
461
app/widgets/pikabrusheditor.c
Normal 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);
|
||||
}
|
||||
}
|
||||
68
app/widgets/pikabrusheditor.h
Normal file
68
app/widgets/pikabrusheditor.h
Normal 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__ */
|
||||
278
app/widgets/pikabrushfactoryview.c
Normal file
278
app/widgets/pikabrushfactoryview.c
Normal 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);
|
||||
}
|
||||
73
app/widgets/pikabrushfactoryview.h
Normal file
73
app/widgets/pikabrushfactoryview.h
Normal 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__ */
|
||||
348
app/widgets/pikabrushselect.c
Normal file
348
app/widgets/pikabrushselect.c
Normal 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);
|
||||
}
|
||||
}
|
||||
66
app/widgets/pikabrushselect.h
Normal file
66
app/widgets/pikabrushselect.h
Normal 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__ */
|
||||
358
app/widgets/pikabuffersourcebox.c
Normal file
358
app/widgets/pikabuffersourcebox.c
Normal 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;
|
||||
}
|
||||
62
app/widgets/pikabuffersourcebox.h
Normal file
62
app/widgets/pikabuffersourcebox.h
Normal 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__ */
|
||||
312
app/widgets/pikabufferview.c
Normal file
312
app/widgets/pikabufferview.c
Normal 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);
|
||||
}
|
||||
72
app/widgets/pikabufferview.h
Normal file
72
app/widgets/pikabufferview.h
Normal 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__ */
|
||||
1016
app/widgets/pikacairo-mascot.c
Normal file
1016
app/widgets/pikacairo-mascot.c
Normal file
File diff suppressed because it is too large
Load Diff
49
app/widgets/pikacairo-mascot.h
Normal file
49
app/widgets/pikacairo-mascot.h
Normal 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__ */
|
||||
146
app/widgets/pikacellrendererbutton.c
Normal file
146
app/widgets/pikacellrendererbutton.c
Normal 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);
|
||||
}
|
||||
63
app/widgets/pikacellrendererbutton.h
Normal file
63
app/widgets/pikacellrendererbutton.h
Normal 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__ */
|
||||
239
app/widgets/pikacellrendererdashes.c
Normal file
239
app/widgets/pikacellrendererdashes.c
Normal 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);
|
||||
}
|
||||
57
app/widgets/pikacellrendererdashes.h
Normal file
57
app/widgets/pikacellrendererdashes.h
Normal 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__ */
|
||||
414
app/widgets/pikacellrendererviewable.c
Normal file
414
app/widgets/pikacellrendererviewable.c
Normal 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);
|
||||
}
|
||||
69
app/widgets/pikacellrendererviewable.h
Normal file
69
app/widgets/pikacellrendererviewable.h
Normal 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__ */
|
||||
376
app/widgets/pikachanneltreeview.c
Normal file
376
app/widgets/pikachanneltreeview.c
Normal 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);
|
||||
}
|
||||
59
app/widgets/pikachanneltreeview.h
Normal file
59
app/widgets/pikachanneltreeview.h
Normal 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
602
app/widgets/pikacircle.c
Normal 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
70
app/widgets/pikacircle.h
Normal 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
1297
app/widgets/pikaclipboard.c
Normal file
File diff suppressed because it is too large
Load Diff
54
app/widgets/pikaclipboard.h
Normal file
54
app/widgets/pikaclipboard.h
Normal 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
341
app/widgets/pikacolorbar.c
Normal 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));
|
||||
}
|
||||
64
app/widgets/pikacolorbar.h
Normal file
64
app/widgets/pikacolorbar.h
Normal 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__ */
|
||||
813
app/widgets/pikacolordialog.c
Normal file
813
app/widgets/pikacolordialog.c
Normal 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);
|
||||
}
|
||||
}
|
||||
90
app/widgets/pikacolordialog.h
Normal file
90
app/widgets/pikacolordialog.h
Normal 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__ */
|
||||
832
app/widgets/pikacolordisplayeditor.c
Normal file
832
app/widgets/pikacolordisplayeditor.c
Normal 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);
|
||||
}
|
||||
83
app/widgets/pikacolordisplayeditor.h
Normal file
83
app/widgets/pikacolordisplayeditor.h
Normal 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__ */
|
||||
736
app/widgets/pikacoloreditor.c
Normal file
736
app/widgets/pikacoloreditor.c
Normal 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);
|
||||
}
|
||||
}
|
||||
66
app/widgets/pikacoloreditor.h
Normal file
66
app/widgets/pikacoloreditor.h
Normal 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
1242
app/widgets/pikacolorframe.c
Normal file
File diff suppressed because it is too large
Load Diff
124
app/widgets/pikacolorframe.h
Normal file
124
app/widgets/pikacolorframe.h
Normal 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__ */
|
||||
590
app/widgets/pikacolorhistory.c
Normal file
590
app/widgets/pikacolorhistory.c
Normal 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);
|
||||
}
|
||||
}
|
||||
68
app/widgets/pikacolorhistory.h
Normal file
68
app/widgets/pikacolorhistory.h
Normal 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__ */
|
||||
|
||||
413
app/widgets/pikacolormapeditor.c
Normal file
413
app/widgets/pikacolormapeditor.c
Normal 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);
|
||||
}
|
||||
68
app/widgets/pikacolormapeditor.h
Normal file
68
app/widgets/pikacolormapeditor.h
Normal 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__ */
|
||||
812
app/widgets/pikacolormapselection.c
Normal file
812
app/widgets/pikacolormapselection.c
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
88
app/widgets/pikacolormapselection.h
Normal file
88
app/widgets/pikacolormapselection.h
Normal 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__ */
|
||||
|
||||
321
app/widgets/pikacolorpanel.c
Normal file
321
app/widgets/pikacolorpanel.c
Normal 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;
|
||||
}
|
||||
}
|
||||
69
app/widgets/pikacolorpanel.h
Normal file
69
app/widgets/pikacolorpanel.h
Normal 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__ */
|
||||
183
app/widgets/pikacolorselectorpalette.c
Normal file
183
app/widgets/pikacolorselectorpalette.c
Normal 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);
|
||||
}
|
||||
}
|
||||
57
app/widgets/pikacolorselectorpalette.h
Normal file
57
app/widgets/pikacolorselectorpalette.h
Normal 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__ */
|
||||
179
app/widgets/pikacombotagentry.c
Normal file
179
app/widgets/pikacombotagentry.c
Normal 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);
|
||||
}
|
||||
59
app/widgets/pikacombotagentry.h
Normal file
59
app/widgets/pikacombotagentry.h
Normal 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__ */
|
||||
634
app/widgets/pikacomponenteditor.c
Normal file
634
app/widgets/pikacomponenteditor.c
Normal 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;
|
||||
}
|
||||
73
app/widgets/pikacomponenteditor.h
Normal file
73
app/widgets/pikacomponenteditor.h
Normal 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__ */
|
||||
216
app/widgets/pikacompressioncombobox.c
Normal file
216
app/widgets/pikacompressioncombobox.c
Normal 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;
|
||||
}
|
||||
59
app/widgets/pikacompressioncombobox.h
Normal file
59
app/widgets/pikacompressioncombobox.h
Normal 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__ */
|
||||
224
app/widgets/pikacontainerbox.c
Normal file
224
app/widgets/pikacontainerbox.c
Normal 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;
|
||||
}
|
||||
62
app/widgets/pikacontainerbox.h
Normal file
62
app/widgets/pikacontainerbox.h
Normal 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__ */
|
||||
490
app/widgets/pikacontainercombobox.c
Normal file
490
app/widgets/pikacontainercombobox.c
Normal 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);
|
||||
}
|
||||
}
|
||||
61
app/widgets/pikacontainercombobox.h
Normal file
61
app/widgets/pikacontainercombobox.h
Normal 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__ */
|
||||
570
app/widgets/pikacontainereditor.c
Normal file
570
app/widgets/pikacontainereditor.c
Normal 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);
|
||||
}
|
||||
}
|
||||
71
app/widgets/pikacontainereditor.h
Normal file
71
app/widgets/pikacontainereditor.h
Normal 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__ */
|
||||
444
app/widgets/pikacontainerentry.c
Normal file
444
app/widgets/pikacontainerentry.c
Normal 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);
|
||||
}
|
||||
60
app/widgets/pikacontainerentry.h
Normal file
60
app/widgets/pikacontainerentry.h
Normal 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__ */
|
||||
898
app/widgets/pikacontainericonview.c
Normal file
898
app/widgets/pikacontainericonview.c
Normal 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;
|
||||
}
|
||||
74
app/widgets/pikacontainericonview.h
Normal file
74
app/widgets/pikacontainericonview.h
Normal 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__ */
|
||||
443
app/widgets/pikacontainerpopup.c
Normal file
443
app/widgets/pikacontainerpopup.c
Normal 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");
|
||||
}
|
||||
92
app/widgets/pikacontainerpopup.h
Normal file
92
app/widgets/pikacontainerpopup.h
Normal 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__ */
|
||||
648
app/widgets/pikacontainertreestore.c
Normal file
648
app/widgets/pikacontainertreestore.c
Normal 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);
|
||||
}
|
||||
}
|
||||
104
app/widgets/pikacontainertreestore.h
Normal file
104
app/widgets/pikacontainertreestore.h
Normal 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__ */
|
||||
915
app/widgets/pikacontainertreeview-dnd.c
Normal file
915
app/widgets/pikacontainertreeview-dnd.c
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
75
app/widgets/pikacontainertreeview-dnd.h
Normal file
75
app/widgets/pikacontainertreeview-dnd.h
Normal 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__ */
|
||||
57
app/widgets/pikacontainertreeview-private.h
Normal file
57
app/widgets/pikacontainertreeview-private.h
Normal 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__ */
|
||||
2039
app/widgets/pikacontainertreeview.c
Normal file
2039
app/widgets/pikacontainertreeview.c
Normal file
File diff suppressed because it is too large
Load Diff
146
app/widgets/pikacontainertreeview.h
Normal file
146
app/widgets/pikacontainertreeview.h
Normal 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__ */
|
||||
96
app/widgets/pikacontainerview-utils.c
Normal file
96
app/widgets/pikacontainerview-utils.c
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
34
app/widgets/pikacontainerview-utils.h
Normal file
34
app/widgets/pikacontainerview-utils.h
Normal 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__ */
|
||||
1425
app/widgets/pikacontainerview.c
Normal file
1425
app/widgets/pikacontainerview.c
Normal file
File diff suppressed because it is too large
Load Diff
168
app/widgets/pikacontainerview.h
Normal file
168
app/widgets/pikacontainerview.h
Normal 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__ */
|
||||
887
app/widgets/pikacontrollereditor.c
Normal file
887
app/widgets/pikacontrollereditor.c
Normal 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;
|
||||
}
|
||||
68
app/widgets/pikacontrollereditor.h
Normal file
68
app/widgets/pikacontrollereditor.h
Normal 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__ */
|
||||
537
app/widgets/pikacontrollerinfo.c
Normal file
537
app/widgets/pikacontrollerinfo.c
Normal 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;
|
||||
}
|
||||
86
app/widgets/pikacontrollerinfo.h
Normal file
86
app/widgets/pikacontrollerinfo.h
Normal 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__ */
|
||||
297
app/widgets/pikacontrollerkeyboard.c
Normal file
297
app/widgets/pikacontrollerkeyboard.c
Normal 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;
|
||||
}
|
||||
60
app/widgets/pikacontrollerkeyboard.h
Normal file
60
app/widgets/pikacontrollerkeyboard.h
Normal 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__ */
|
||||
707
app/widgets/pikacontrollerlist.c
Normal file
707
app/widgets/pikacontrollerlist.c
Normal 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);
|
||||
}
|
||||
72
app/widgets/pikacontrollerlist.h
Normal file
72
app/widgets/pikacontrollerlist.h
Normal 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
Reference in New Issue
Block a user