ffe4eaf00d
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).
593 lines
18 KiB
C
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);
|
|
}
|