gnome-shell/src/st/st-adjustment.c
Florian Müllner ffe4eaf00d st: Use G_DECLARE_*_TYPE
Cut down on boilerplate by using the (no longer that) new helper
macros. We don't care about breaking ABI in private libraries, so
use G_DECLARE_FINAL_TYPE even where the class struct used to be
exposed in the header, except for types we inherit from ourselves
(obviously) or where the class exposes any vfuncs (where changes
could affect inheritance in extensions).
2015-10-15 22:02:35 +02:00

593 lines
18 KiB
C

/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* st-adjustment.c: Adjustment object
*
* Copyright 2008 OpenedHand
* Copyright 2009 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
* more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* SECTION:st-adjustment
* @short_description: A GObject representing an adjustable bounded value
*
* The #StAdjustment object represents a range of values bounded between a
* minimum and maximum, together with step and page increments and a page size.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <glib-object.h>
#include <clutter/clutter.h>
#include <math.h>
#include "st-adjustment.h"
#include "st-private.h"
typedef struct _StAdjustmentPrivate StAdjustmentPrivate;
struct _StAdjustmentPrivate
{
/* Do not sanity-check values while constructing,
* not all properties may be set yet. */
guint is_constructing : 1;
gdouble lower;
gdouble upper;
gdouble value;
gdouble step_increment;
gdouble page_increment;
gdouble page_size;
};
G_DEFINE_TYPE_WITH_PRIVATE (StAdjustment, st_adjustment, G_TYPE_OBJECT)
enum
{
PROP_0,
PROP_LOWER,
PROP_UPPER,
PROP_VALUE,
PROP_STEP_INC,
PROP_PAGE_INC,
PROP_PAGE_SIZE,
};
enum
{
CHANGED,
LAST_SIGNAL
};
static guint signals[LAST_SIGNAL] = { 0, };
static gboolean st_adjustment_set_lower (StAdjustment *adjustment,
gdouble lower);
static gboolean st_adjustment_set_upper (StAdjustment *adjustment,
gdouble upper);
static gboolean st_adjustment_set_step_increment (StAdjustment *adjustment,
gdouble step);
static gboolean st_adjustment_set_page_increment (StAdjustment *adjustment,
gdouble page);
static gboolean st_adjustment_set_page_size (StAdjustment *adjustment,
gdouble size);
static void
st_adjustment_constructed (GObject *object)
{
GObjectClass *g_class;
StAdjustment *self = ST_ADJUSTMENT (object);
StAdjustmentPrivate *priv = st_adjustment_get_instance_private (self);
g_class = G_OBJECT_CLASS (st_adjustment_parent_class);
/* The docs say we're suppose to chain up, but would crash without
* some extra care. */
if (g_class && g_class->constructed &&
g_class->constructed != st_adjustment_constructed)
{
g_class->constructed (object);
}
priv->is_constructing = FALSE;
st_adjustment_clamp_page (self, priv->lower, priv->upper);
}
static void
st_adjustment_get_property (GObject *gobject,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
StAdjustmentPrivate *priv = st_adjustment_get_instance_private (ST_ADJUSTMENT (gobject));
switch (prop_id)
{
case PROP_LOWER:
g_value_set_double (value, priv->lower);
break;
case PROP_UPPER:
g_value_set_double (value, priv->upper);
break;
case PROP_VALUE:
g_value_set_double (value, priv->value);
break;
case PROP_STEP_INC:
g_value_set_double (value, priv->step_increment);
break;
case PROP_PAGE_INC:
g_value_set_double (value, priv->page_increment);
break;
case PROP_PAGE_SIZE:
g_value_set_double (value, priv->page_size);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
}
}
static void
st_adjustment_set_property (GObject *gobject,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
StAdjustment *adj = ST_ADJUSTMENT (gobject);
switch (prop_id)
{
case PROP_LOWER:
st_adjustment_set_lower (adj, g_value_get_double (value));
break;
case PROP_UPPER:
st_adjustment_set_upper (adj, g_value_get_double (value));
break;
case PROP_VALUE:
st_adjustment_set_value (adj, g_value_get_double (value));
break;
case PROP_STEP_INC:
st_adjustment_set_step_increment (adj, g_value_get_double (value));
break;
case PROP_PAGE_INC:
st_adjustment_set_page_increment (adj, g_value_get_double (value));
break;
case PROP_PAGE_SIZE:
st_adjustment_set_page_size (adj, g_value_get_double (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
}
}
static void
st_adjustment_class_init (StAdjustmentClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->constructed = st_adjustment_constructed;
object_class->get_property = st_adjustment_get_property;
object_class->set_property = st_adjustment_set_property;
g_object_class_install_property (object_class,
PROP_LOWER,
g_param_spec_double ("lower",
"Lower",
"Lower bound",
-G_MAXDOUBLE,
G_MAXDOUBLE,
0.0,
ST_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
g_object_class_install_property (object_class,
PROP_UPPER,
g_param_spec_double ("upper",
"Upper",
"Upper bound",
-G_MAXDOUBLE,
G_MAXDOUBLE,
0.0,
ST_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
g_object_class_install_property (object_class,
PROP_VALUE,
g_param_spec_double ("value",
"Value",
"Current value",
-G_MAXDOUBLE,
G_MAXDOUBLE,
0.0,
ST_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
g_object_class_install_property (object_class,
PROP_STEP_INC,
g_param_spec_double ("step-increment",
"Step Increment",
"Step increment",
0.0,
G_MAXDOUBLE,
0.0,
ST_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
g_object_class_install_property (object_class,
PROP_PAGE_INC,
g_param_spec_double ("page-increment",
"Page Increment",
"Page increment",
0.0,
G_MAXDOUBLE,
0.0,
ST_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
g_object_class_install_property (object_class,
PROP_PAGE_SIZE,
g_param_spec_double ("page-size",
"Page Size",
"Page size",
0.0,
G_MAXDOUBLE,
0.0,
ST_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
/**
* StAdjustment::changed:
* @self: the #StAdjustment
*
* Emitted when any of the adjustment values have changed
*/
signals[CHANGED] =
g_signal_new ("changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (StAdjustmentClass, changed),
NULL, NULL, NULL,
G_TYPE_NONE, 0);
}
static void
st_adjustment_init (StAdjustment *self)
{
StAdjustmentPrivate *priv = st_adjustment_get_instance_private (self);
priv->is_constructing = TRUE;
}
StAdjustment *
st_adjustment_new (gdouble value,
gdouble lower,
gdouble upper,
gdouble step_increment,
gdouble page_increment,
gdouble page_size)
{
return g_object_new (ST_TYPE_ADJUSTMENT,
"value", value,
"lower", lower,
"upper", upper,
"step-increment", step_increment,
"page-increment", page_increment,
"page-size", page_size,
NULL);
}
gdouble
st_adjustment_get_value (StAdjustment *adjustment)
{
g_return_val_if_fail (ST_IS_ADJUSTMENT (adjustment), 0);
return ((StAdjustmentPrivate *)st_adjustment_get_instance_private (adjustment))->value;
}
void
st_adjustment_set_value (StAdjustment *adjustment,
gdouble value)
{
StAdjustmentPrivate *priv;
g_return_if_fail (ST_IS_ADJUSTMENT (adjustment));
priv = st_adjustment_get_instance_private (adjustment);
/* Defer clamp until after construction. */
if (!priv->is_constructing)
{
value = CLAMP (value,
priv->lower,
MAX (priv->lower, priv->upper - priv->page_size));
}
if (priv->value != value)
{
priv->value = value;
g_object_notify (G_OBJECT (adjustment), "value");
}
}
void
st_adjustment_clamp_page (StAdjustment *adjustment,
gdouble lower,
gdouble upper)
{
StAdjustmentPrivate *priv;
gboolean changed;
g_return_if_fail (ST_IS_ADJUSTMENT (adjustment));
priv = st_adjustment_get_instance_private (adjustment);
lower = CLAMP (lower, priv->lower, priv->upper - priv->page_size);
upper = CLAMP (upper, priv->lower + priv->page_size, priv->upper);
changed = FALSE;
if (priv->value + priv->page_size > upper)
{
priv->value = upper - priv->page_size;
changed = TRUE;
}
if (priv->value < lower)
{
priv->value = lower;
changed = TRUE;
}
if (changed)
g_object_notify (G_OBJECT (adjustment), "value");
}
static gboolean
st_adjustment_set_lower (StAdjustment *adjustment,
gdouble lower)
{
StAdjustmentPrivate *priv = st_adjustment_get_instance_private (adjustment);
if (priv->lower != lower)
{
priv->lower = lower;
g_signal_emit (adjustment, signals[CHANGED], 0);
g_object_notify (G_OBJECT (adjustment), "lower");
/* Defer clamp until after construction. */
if (!priv->is_constructing)
st_adjustment_clamp_page (adjustment, priv->lower, priv->upper);
return TRUE;
}
return FALSE;
}
static gboolean
st_adjustment_set_upper (StAdjustment *adjustment,
gdouble upper)
{
StAdjustmentPrivate *priv = st_adjustment_get_instance_private (adjustment);
if (priv->upper != upper)
{
priv->upper = upper;
g_signal_emit (adjustment, signals[CHANGED], 0);
g_object_notify (G_OBJECT (adjustment), "upper");
/* Defer clamp until after construction. */
if (!priv->is_constructing)
st_adjustment_clamp_page (adjustment, priv->lower, priv->upper);
return TRUE;
}
return FALSE;
}
static gboolean
st_adjustment_set_step_increment (StAdjustment *adjustment,
gdouble step)
{
StAdjustmentPrivate *priv = st_adjustment_get_instance_private (adjustment);
if (priv->step_increment != step)
{
priv->step_increment = step;
g_signal_emit (adjustment, signals[CHANGED], 0);
g_object_notify (G_OBJECT (adjustment), "step-increment");
return TRUE;
}
return FALSE;
}
static gboolean
st_adjustment_set_page_increment (StAdjustment *adjustment,
gdouble page)
{
StAdjustmentPrivate *priv = st_adjustment_get_instance_private (adjustment);
if (priv->page_increment != page)
{
priv->page_increment = page;
g_signal_emit (adjustment, signals[CHANGED], 0);
g_object_notify (G_OBJECT (adjustment), "page-increment");
return TRUE;
}
return FALSE;
}
static gboolean
st_adjustment_set_page_size (StAdjustment *adjustment,
gdouble size)
{
StAdjustmentPrivate *priv = st_adjustment_get_instance_private (adjustment);
if (priv->page_size != size)
{
priv->page_size = size;
g_signal_emit (adjustment, signals[CHANGED], 0);
g_object_notify (G_OBJECT (adjustment), "page_size");
/* Well explicitely clamp after construction. */
if (!priv->is_constructing)
st_adjustment_clamp_page (adjustment, priv->lower, priv->upper);
return TRUE;
}
return FALSE;
}
void
st_adjustment_set_values (StAdjustment *adjustment,
gdouble value,
gdouble lower,
gdouble upper,
gdouble step_increment,
gdouble page_increment,
gdouble page_size)
{
StAdjustmentPrivate *priv;
gboolean emit_changed = FALSE;
g_return_if_fail (ST_IS_ADJUSTMENT (adjustment));
g_return_if_fail (page_size >= 0 && page_size <= G_MAXDOUBLE);
g_return_if_fail (step_increment >= 0 && step_increment <= G_MAXDOUBLE);
g_return_if_fail (page_increment >= 0 && page_increment <= G_MAXDOUBLE);
priv = st_adjustment_get_instance_private (adjustment);
emit_changed = FALSE;
g_object_freeze_notify (G_OBJECT (adjustment));
emit_changed |= st_adjustment_set_lower (adjustment, lower);
emit_changed |= st_adjustment_set_upper (adjustment, upper);
emit_changed |= st_adjustment_set_step_increment (adjustment, step_increment);
emit_changed |= st_adjustment_set_page_increment (adjustment, page_increment);
emit_changed |= st_adjustment_set_page_size (adjustment, page_size);
if (value != priv->value)
{
st_adjustment_set_value (adjustment, value);
emit_changed = TRUE;
}
if (emit_changed)
g_signal_emit (G_OBJECT (adjustment), signals[CHANGED], 0);
g_object_thaw_notify (G_OBJECT (adjustment));
}
/**
* st_adjustment_get_values:
* @adjustment: an #StAdjustment
* @value: (out): the current value
* @lower: (out): the lower bound
* @upper: (out): the upper bound
* @step_increment: (out): the step increment
* @page_increment: (out): the page increment
* @page_size: (out): the page size
*
* Gets all of @adjustment's values at once.
*/
void
st_adjustment_get_values (StAdjustment *adjustment,
gdouble *value,
gdouble *lower,
gdouble *upper,
gdouble *step_increment,
gdouble *page_increment,
gdouble *page_size)
{
StAdjustmentPrivate *priv;
g_return_if_fail (ST_IS_ADJUSTMENT (adjustment));
priv = st_adjustment_get_instance_private (adjustment);
if (lower)
*lower = priv->lower;
if (upper)
*upper = priv->upper;
if (value)
*value = st_adjustment_get_value (adjustment);
if (step_increment)
*step_increment = priv->step_increment;
if (page_increment)
*page_increment = priv->page_increment;
if (page_size)
*page_size = priv->page_size;
}
/**
* st_adjustment_adjust_for_scroll_event:
* @adjustment: An #StAdjustment
* @delta: A delta, retrieved directly from clutter_event_get_scroll_delta()
* or similar.
*
* Adjusts the adjustment using delta values from a scroll event.
* You should use this instead of using st_adjustment_set_value()
* as this method will tweak the values directly using the same
* math as GTK+, to ensure that scrolling is consistent across
* the environment.
*/
void
st_adjustment_adjust_for_scroll_event (StAdjustment *adjustment,
gdouble delta)
{
StAdjustmentPrivate *priv;
gdouble new_value, scroll_unit;
g_return_if_fail (ST_IS_ADJUSTMENT (adjustment));
priv = st_adjustment_get_instance_private (adjustment);
scroll_unit = pow (priv->page_size, 2.0 / 3.0);
new_value = priv->value + delta * scroll_unit;
st_adjustment_set_value (adjustment, new_value);
}