diff options
Diffstat (limited to 'src/widgets/sp-color-selector.cpp')
-rw-r--r-- | src/widgets/sp-color-selector.cpp | 334 |
1 files changed, 334 insertions, 0 deletions
diff --git a/src/widgets/sp-color-selector.cpp b/src/widgets/sp-color-selector.cpp new file mode 100644 index 0000000..c1c6975 --- /dev/null +++ b/src/widgets/sp-color-selector.cpp @@ -0,0 +1,334 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** @file + * TODO: insert short description here + *//* + * Authors: + * see git history + * bulia byak <buliabyak@users.sf.net> + * Jon A. Cruz <jon@joncruz.org> + * + * Copyright (C) 2018 Authors + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include <cmath> +#include <gtk/gtk.h> +#include <glibmm/i18n.h> +#include "sp-color-selector.h" + +enum { + GRABBED, + DRAGGED, + RELEASED, + CHANGED, + LAST_SIGNAL +}; + +#define noDUMP_CHANGE_INFO +#define FOO_NAME(x) g_type_name( G_TYPE_FROM_INSTANCE(x) ) + +static void sp_color_selector_dispose(GObject *object); + +static void sp_color_selector_show_all( GtkWidget *widget ); +static void sp_color_selector_hide( GtkWidget *widget ); + +static guint csel_signals[LAST_SIGNAL] = {0}; + +double ColorSelector::_epsilon = 1e-4; + +G_DEFINE_TYPE(SPColorSelector, sp_color_selector, GTK_TYPE_BOX); + +void sp_color_selector_class_init( SPColorSelectorClass *klass ) +{ + static const gchar* nameset[] = {N_("Unnamed"), nullptr}; + GObjectClass *object_class = G_OBJECT_CLASS(klass); + GtkWidgetClass *widget_class; + widget_class = GTK_WIDGET_CLASS(klass); + + csel_signals[GRABBED] = g_signal_new( "grabbed", + G_TYPE_FROM_CLASS(object_class), + (GSignalFlags)(G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE), + G_STRUCT_OFFSET(SPColorSelectorClass, grabbed), + nullptr, nullptr, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0 ); + csel_signals[DRAGGED] = g_signal_new( "dragged", + G_TYPE_FROM_CLASS(object_class), + (GSignalFlags)(G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE), + G_STRUCT_OFFSET(SPColorSelectorClass, dragged), + nullptr, nullptr, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0 ); + csel_signals[RELEASED] = g_signal_new( "released", + G_TYPE_FROM_CLASS(object_class), + (GSignalFlags)(G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE), + G_STRUCT_OFFSET(SPColorSelectorClass, released), + nullptr, nullptr, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0 ); + csel_signals[CHANGED] = g_signal_new( "changed", + G_TYPE_FROM_CLASS(object_class), + (GSignalFlags)(G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE), + G_STRUCT_OFFSET(SPColorSelectorClass, changed), + nullptr, nullptr, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0 ); + + klass->name = nameset; + klass->submode_count = 1; + + object_class->dispose = sp_color_selector_dispose; + + widget_class->show_all = sp_color_selector_show_all; + widget_class->hide = sp_color_selector_hide; + +} + +void sp_color_selector_init( SPColorSelector *csel ) +{ + gtk_orientable_set_orientation(GTK_ORIENTABLE(csel), GTK_ORIENTATION_VERTICAL); + + if ( csel->base ) + { + csel->base->init(); + } +/* g_signal_connect(G_OBJECT(csel->rgbae), "changed", G_CALLBACK(sp_color_selector_rgba_entry_changed), csel); */ +} + +void sp_color_selector_dispose(GObject *object) +{ + SPColorSelector *csel = SP_COLOR_SELECTOR( object ); + if ( csel->base ) + { + delete csel->base; + csel->base = nullptr; + } + + if ((G_OBJECT_CLASS(sp_color_selector_parent_class))->dispose ) { + (G_OBJECT_CLASS(sp_color_selector_parent_class))->dispose(object); + } +} + +void sp_color_selector_show_all( GtkWidget *widget ) +{ + gtk_widget_show( widget ); +} + +void sp_color_selector_hide(GtkWidget *widget) +{ + gtk_widget_hide( widget ); +} + +GtkWidget *sp_color_selector_new( GType selector_type ) +{ + g_return_val_if_fail( g_type_is_a( selector_type, SP_TYPE_COLOR_SELECTOR ), NULL ); + + SPColorSelector *csel = SP_COLOR_SELECTOR( g_object_new( selector_type, nullptr ) ); + + return GTK_WIDGET( csel ); +} + +void ColorSelector::setSubmode( guint /*submode*/ ) +{ +} + +guint ColorSelector::getSubmode() const +{ + guint mode = 0; + return mode; +} + +ColorSelector::ColorSelector( SPColorSelector* csel ) + : _csel(csel), + _color( 0 ), + _alpha(1.0), + _held(FALSE), + virgin(true) +{ + g_return_if_fail( SP_IS_COLOR_SELECTOR(_csel) ); +} + +ColorSelector::~ColorSelector() += default; + +void ColorSelector::init() +{ + _csel->base = new ColorSelector( _csel ); +} + +void ColorSelector::setColor( const SPColor& color ) +{ + setColorAlpha( color, _alpha ); +} + +SPColor ColorSelector::getColor() const +{ + return _color; +} + +void ColorSelector::setAlpha( gfloat alpha ) +{ + g_return_if_fail( ( 0.0 <= alpha ) && ( alpha <= 1.0 ) ); + setColorAlpha( _color, alpha ); +} + +gfloat ColorSelector::getAlpha() const +{ + return _alpha; +} + +/** +Called from the outside to set the color; optionally emits signal (only when called from +downstream, e.g. the RGBA value field, but not from the rest of the program) +*/ +void ColorSelector::setColorAlpha( const SPColor& color, gfloat alpha, bool emit ) +{ +#ifdef DUMP_CHANGE_INFO + g_message("ColorSelector::setColorAlpha( this=%p, %f, %f, %f, %s, %f, %s) in %s", this, color.v.c[0], color.v.c[1], color.v.c[2], (color.icc?color.icc->colorProfile.c_str():"<null>"), alpha, (emit?"YES":"no"), FOO_NAME(_csel)); +#endif + g_return_if_fail( _csel != nullptr ); + g_return_if_fail( ( 0.0 <= alpha ) && ( alpha <= 1.0 ) ); + +#ifdef DUMP_CHANGE_INFO + g_message("---- ColorSelector::setColorAlpha virgin:%s !close:%s alpha is:%s in %s", + (virgin?"YES":"no"), + (!color.isClose( _color, _epsilon )?"YES":"no"), + ((fabs((_alpha) - (alpha)) >= _epsilon )?"YES":"no"), + FOO_NAME(_csel) + ); +#endif + + if ( virgin || !color.isClose( _color, _epsilon ) || + (fabs((_alpha) - (alpha)) >= _epsilon )) { + + virgin = false; + + _color = color; + _alpha = alpha; + _colorChanged(); + + if (emit) { + g_signal_emit(G_OBJECT(_csel), csel_signals[CHANGED], 0); + } +#ifdef DUMP_CHANGE_INFO + } else { + g_message("++++ ColorSelector::setColorAlpha color:%08x ==> _color:%08X isClose:%s in %s", color.toRGBA32(alpha), _color.toRGBA32(_alpha), + (color.isClose( _color, _epsilon )?"YES":"no"), FOO_NAME(_csel)); +#endif + } +} + +void ColorSelector::_grabbed() +{ + _held = TRUE; +#ifdef DUMP_CHANGE_INFO + g_message("%s:%d: About to signal %s in %s", __FILE__, __LINE__, + "GRABBED", + FOO_NAME(_csel)); +#endif + g_signal_emit(G_OBJECT(_csel), csel_signals[GRABBED], 0); +} + +void ColorSelector::_released() +{ + _held = false; +#ifdef DUMP_CHANGE_INFO + g_message("%s:%d: About to signal %s in %s", __FILE__, __LINE__, + "RELEASED", + FOO_NAME(_csel)); +#endif + g_signal_emit(G_OBJECT(_csel), csel_signals[RELEASED], 0); + g_signal_emit(G_OBJECT(_csel), csel_signals[CHANGED], 0); +} + +// Called from subclasses to update color and broadcast if needed +void ColorSelector::_updateInternals( const SPColor& color, gfloat alpha, gboolean held ) +{ + g_return_if_fail( ( 0.0 <= alpha ) && ( alpha <= 1.0 ) ); + gboolean colorDifferent = ( !color.isClose( _color, _epsilon ) + || ( fabs((_alpha) - (alpha)) >= _epsilon ) ); + + gboolean grabbed = held && !_held; + gboolean released = !held && _held; + + // Store these before emitting any signals + _held = held; + if ( colorDifferent ) + { + _color = color; + _alpha = alpha; + } + + if ( grabbed ) + { +#ifdef DUMP_CHANGE_INFO + g_message("%s:%d: About to signal %s to color %08x::%s in %s", __FILE__, __LINE__, + "GRABBED", + color.toRGBA32( alpha ), (color.icc?color.icc->colorProfile.c_str():"<null>"), FOO_NAME(_csel)); +#endif + g_signal_emit(G_OBJECT(_csel), csel_signals[GRABBED], 0); + } + else if ( released ) + { +#ifdef DUMP_CHANGE_INFO + g_message("%s:%d: About to signal %s to color %08x::%s in %s", __FILE__, __LINE__, + "RELEASED", + color.toRGBA32( alpha ), (color.icc?color.icc->colorProfile.c_str():"<null>"), FOO_NAME(_csel)); +#endif + g_signal_emit(G_OBJECT(_csel), csel_signals[RELEASED], 0); + } + + if ( colorDifferent || released ) + { +#ifdef DUMP_CHANGE_INFO + g_message("%s:%d: About to signal %s to color %08x::%s in %s", __FILE__, __LINE__, + (_held ? "CHANGED" : "DRAGGED" ), + color.toRGBA32( alpha ), (color.icc?color.icc->colorProfile.c_str():"<null>"), FOO_NAME(_csel)); +#endif + g_signal_emit(G_OBJECT(_csel), csel_signals[_held ? DRAGGED : CHANGED], 0); + } +} + +/** + * Called once the color actually changes. Allows subclasses to react to changes. + */ +void ColorSelector::_colorChanged() +{ +} + +void ColorSelector::getColorAlpha( SPColor &color, gfloat &alpha ) const +{ + gint i = 0; + + color = _color; + alpha = _alpha; + + // Try to catch uninitialized value usage + if ( color.v.c[0] ) + { + i++; + } + if ( color.v.c[1] ) + { + i++; + } + if ( color.v.c[2] ) + { + i++; + } + if ( alpha ) + { + i++; + } +} + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : |