forked from AuroraMiddleware/gtk
a546ae32d7
Those property features don't seem to be in use anywhere. They are redundant since the docs cover the same information and more. They also created unnecessary translation work. Closes #4904
490 lines
16 KiB
C
490 lines
16 KiB
C
/* GTK - The GIMP Toolkit
|
|
*
|
|
* Copyright (C) 2009 Matthias Clasen <mclasen@redhat.com>
|
|
* Copyright (C) 2008 Richard Hughes <richard@hughsie.com>
|
|
* Copyright (C) 2009 Bastien Nocera <hadess@hadess.net>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library 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
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/*
|
|
* Modified by the GTK+ Team and others 2007. See the AUTHORS
|
|
* file for a list of people on the GTK+ Team. See the ChangeLog
|
|
* files for a list of changes. These files are distributed with
|
|
* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "gtkcellrendererspinner.h"
|
|
#include "gtkiconhelperprivate.h"
|
|
#include "gtkintl.h"
|
|
#include "gtksettings.h"
|
|
#include "gtksnapshot.h"
|
|
#include "gtktypebuiltins.h"
|
|
#include "gtkstylecontextprivate.h"
|
|
#include "gtkcssnumbervalueprivate.h"
|
|
|
|
#include <math.h>
|
|
|
|
/**
|
|
* GtkCellRendererSpinner:
|
|
*
|
|
* Renders a spinning animation in a cell
|
|
*
|
|
* `GtkCellRendererSpinner` renders a spinning animation in a cell, very
|
|
* similar to `GtkSpinner`. It can often be used as an alternative
|
|
* to a `GtkCellRendererProgress` for displaying indefinite activity,
|
|
* instead of actual progress.
|
|
*
|
|
* To start the animation in a cell, set the `GtkCellRendererSpinner:active`
|
|
* property to %TRUE and increment the `GtkCellRendererSpinner:pulse` property
|
|
* at regular intervals. The usual way to set the cell renderer properties
|
|
* for each cell is to bind them to columns in your tree model using e.g.
|
|
* gtk_tree_view_column_add_attribute().
|
|
*/
|
|
|
|
|
|
enum {
|
|
PROP_0,
|
|
PROP_ACTIVE,
|
|
PROP_PULSE,
|
|
PROP_SIZE
|
|
};
|
|
|
|
typedef struct _GtkCellRendererSpinnerClass GtkCellRendererSpinnerClass;
|
|
typedef struct _GtkCellRendererSpinnerPrivate GtkCellRendererSpinnerPrivate;
|
|
|
|
struct _GtkCellRendererSpinner
|
|
{
|
|
GtkCellRenderer parent;
|
|
};
|
|
|
|
struct _GtkCellRendererSpinnerClass
|
|
{
|
|
GtkCellRendererClass parent_class;
|
|
|
|
/* Padding for future expansion */
|
|
void (*_gtk_reserved1) (void);
|
|
void (*_gtk_reserved2) (void);
|
|
void (*_gtk_reserved3) (void);
|
|
void (*_gtk_reserved4) (void);
|
|
};
|
|
|
|
struct _GtkCellRendererSpinnerPrivate
|
|
{
|
|
gboolean active;
|
|
guint pulse;
|
|
GtkIconSize icon_size;
|
|
int size;
|
|
};
|
|
|
|
|
|
static void gtk_cell_renderer_spinner_get_property (GObject *object,
|
|
guint param_id,
|
|
GValue *value,
|
|
GParamSpec *pspec);
|
|
static void gtk_cell_renderer_spinner_set_property (GObject *object,
|
|
guint param_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec);
|
|
static void gtk_cell_renderer_spinner_get_size (GtkCellRendererSpinner *self,
|
|
GtkWidget *widget,
|
|
const GdkRectangle *cell_area,
|
|
int *x_offset,
|
|
int *y_offset,
|
|
int *width,
|
|
int *height);
|
|
static void gtk_cell_renderer_spinner_snapshot (GtkCellRenderer *cell,
|
|
GtkSnapshot *snapshot,
|
|
GtkWidget *widget,
|
|
const GdkRectangle *background_area,
|
|
const GdkRectangle *cell_area,
|
|
GtkCellRendererState flags);
|
|
|
|
G_DEFINE_TYPE_WITH_PRIVATE (GtkCellRendererSpinner, gtk_cell_renderer_spinner, GTK_TYPE_CELL_RENDERER)
|
|
|
|
static GtkSizeRequestMode
|
|
gtk_cell_renderer_spinner_get_request_mode (GtkCellRenderer *cell)
|
|
{
|
|
return GTK_SIZE_REQUEST_CONSTANT_SIZE;
|
|
}
|
|
|
|
static void
|
|
gtk_cell_renderer_spinner_get_preferred_width (GtkCellRenderer *cell,
|
|
GtkWidget *widget,
|
|
int *minimum,
|
|
int *natural)
|
|
{
|
|
int size = 0;
|
|
|
|
gtk_cell_renderer_spinner_get_size (GTK_CELL_RENDERER_SPINNER (cell), widget,
|
|
NULL,
|
|
NULL, NULL, &size, NULL);
|
|
|
|
if (minimum != NULL)
|
|
*minimum = size;
|
|
if (natural != NULL)
|
|
*natural = size;
|
|
}
|
|
|
|
static void
|
|
gtk_cell_renderer_spinner_get_preferred_height (GtkCellRenderer *cell,
|
|
GtkWidget *widget,
|
|
int *minimum,
|
|
int *natural)
|
|
{
|
|
int size = 0;
|
|
|
|
gtk_cell_renderer_spinner_get_size (GTK_CELL_RENDERER_SPINNER (cell), widget,
|
|
NULL,
|
|
NULL, NULL, NULL, &size);
|
|
|
|
if (minimum != NULL)
|
|
*minimum = size;
|
|
if (natural != NULL)
|
|
*natural = size;
|
|
}
|
|
|
|
static void
|
|
gtk_cell_renderer_spinner_class_init (GtkCellRendererSpinnerClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (klass);
|
|
|
|
object_class->get_property = gtk_cell_renderer_spinner_get_property;
|
|
object_class->set_property = gtk_cell_renderer_spinner_set_property;
|
|
|
|
cell_class->get_request_mode = gtk_cell_renderer_spinner_get_request_mode;
|
|
cell_class->get_preferred_width = gtk_cell_renderer_spinner_get_preferred_width;
|
|
cell_class->get_preferred_height = gtk_cell_renderer_spinner_get_preferred_height;
|
|
cell_class->snapshot = gtk_cell_renderer_spinner_snapshot;
|
|
|
|
/* GtkCellRendererSpinner:active:
|
|
*
|
|
* Whether the spinner is active (ie. shown) in the cell
|
|
*/
|
|
g_object_class_install_property (object_class,
|
|
PROP_ACTIVE,
|
|
g_param_spec_boolean ("active", NULL, NULL,
|
|
FALSE,
|
|
G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
|
|
|
|
/**
|
|
* GtkCellRendererSpinner:pulse:
|
|
*
|
|
* Pulse of the spinner. Increment this value to draw the next frame of the
|
|
* spinner animation. Usually, you would update this value in a timeout.
|
|
*
|
|
* By default, the `GtkSpinner` widget draws one full cycle of the animation,
|
|
* consisting of 12 frames, in 750 milliseconds.
|
|
*/
|
|
g_object_class_install_property (object_class,
|
|
PROP_PULSE,
|
|
g_param_spec_uint ("pulse", NULL, NULL,
|
|
0, G_MAXUINT, 0,
|
|
G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
|
|
|
|
/**
|
|
* GtkCellRendererSpinner:size:
|
|
*
|
|
* The `GtkIconSize` value that specifies the size of the rendered spinner.
|
|
*/
|
|
g_object_class_install_property (object_class,
|
|
PROP_SIZE,
|
|
g_param_spec_enum ("size", NULL, NULL,
|
|
GTK_TYPE_ICON_SIZE, GTK_ICON_SIZE_INHERIT,
|
|
G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
|
|
|
|
}
|
|
|
|
static void
|
|
gtk_cell_renderer_spinner_init (GtkCellRendererSpinner *cell)
|
|
{
|
|
GtkCellRendererSpinnerPrivate *priv = gtk_cell_renderer_spinner_get_instance_private (cell);
|
|
priv->pulse = 0;
|
|
priv->icon_size = GTK_ICON_SIZE_INHERIT;
|
|
}
|
|
|
|
/**
|
|
* gtk_cell_renderer_spinner_new:
|
|
*
|
|
* Returns a new cell renderer which will show a spinner to indicate
|
|
* activity.
|
|
*
|
|
* Returns: a new `GtkCellRenderer`
|
|
*/
|
|
GtkCellRenderer *
|
|
gtk_cell_renderer_spinner_new (void)
|
|
{
|
|
return g_object_new (GTK_TYPE_CELL_RENDERER_SPINNER, NULL);
|
|
}
|
|
|
|
static void
|
|
gtk_cell_renderer_spinner_update_size (GtkCellRendererSpinner *cell,
|
|
GtkWidget *widget)
|
|
{
|
|
GtkCellRendererSpinnerPrivate *priv = gtk_cell_renderer_spinner_get_instance_private (cell);
|
|
GtkStyleContext *context;
|
|
GtkCssNode *node;
|
|
GtkCssStyle *style;
|
|
|
|
context = gtk_widget_get_style_context (widget);
|
|
gtk_style_context_save (context);
|
|
|
|
gtk_style_context_add_class (context, "spinner");
|
|
node = gtk_style_context_get_node (context);
|
|
gtk_icon_size_set_style_classes (node, priv->icon_size);
|
|
style = gtk_css_node_get_style (node);
|
|
priv->size = _gtk_css_number_value_get (style->icon->icon_size, 100);
|
|
|
|
gtk_style_context_restore (context);
|
|
}
|
|
|
|
static void
|
|
gtk_cell_renderer_spinner_get_property (GObject *object,
|
|
guint param_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
GtkCellRendererSpinner *cell = GTK_CELL_RENDERER_SPINNER (object);
|
|
GtkCellRendererSpinnerPrivate *priv = gtk_cell_renderer_spinner_get_instance_private (cell);
|
|
|
|
switch (param_id)
|
|
{
|
|
case PROP_ACTIVE:
|
|
g_value_set_boolean (value, priv->active);
|
|
break;
|
|
case PROP_PULSE:
|
|
g_value_set_uint (value, priv->pulse);
|
|
break;
|
|
case PROP_SIZE:
|
|
g_value_set_enum (value, priv->icon_size);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gtk_cell_renderer_spinner_set_property (GObject *object,
|
|
guint param_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
GtkCellRendererSpinner *cell = GTK_CELL_RENDERER_SPINNER (object);
|
|
GtkCellRendererSpinnerPrivate *priv = gtk_cell_renderer_spinner_get_instance_private (cell);
|
|
|
|
switch (param_id)
|
|
{
|
|
case PROP_ACTIVE:
|
|
if (priv->active != g_value_get_boolean (value))
|
|
{
|
|
priv->active = g_value_get_boolean (value);
|
|
g_object_notify (object, "active");
|
|
}
|
|
break;
|
|
case PROP_PULSE:
|
|
if (priv->pulse != g_value_get_uint (value))
|
|
{
|
|
priv->pulse = g_value_get_uint (value);
|
|
g_object_notify (object, "pulse");
|
|
}
|
|
break;
|
|
case PROP_SIZE:
|
|
if (priv->icon_size != g_value_get_enum (value))
|
|
{
|
|
priv->icon_size = g_value_get_enum (value);
|
|
g_object_notify (object, "size");
|
|
}
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gtk_cell_renderer_spinner_get_size (GtkCellRendererSpinner *self,
|
|
GtkWidget *widget,
|
|
const GdkRectangle *cell_area,
|
|
int *x_offset,
|
|
int *y_offset,
|
|
int *width,
|
|
int *height)
|
|
{
|
|
GtkCellRendererSpinnerPrivate *priv = gtk_cell_renderer_spinner_get_instance_private (self);
|
|
double align;
|
|
int w, h;
|
|
int xpad, ypad;
|
|
float xalign, yalign;
|
|
gboolean rtl;
|
|
|
|
rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
|
|
|
|
gtk_cell_renderer_spinner_update_size (self, widget);
|
|
|
|
g_object_get (self,
|
|
"xpad", &xpad,
|
|
"ypad", &ypad,
|
|
"xalign", &xalign,
|
|
"yalign", &yalign,
|
|
NULL);
|
|
|
|
w = h = priv->size;
|
|
|
|
if (cell_area)
|
|
{
|
|
if (x_offset)
|
|
{
|
|
align = rtl ? 1.0 - xalign : xalign;
|
|
*x_offset = align * (cell_area->width - w);
|
|
*x_offset = MAX (*x_offset, 0);
|
|
}
|
|
if (y_offset)
|
|
{
|
|
align = rtl ? 1.0 - yalign : yalign;
|
|
*y_offset = align * (cell_area->height - h);
|
|
*y_offset = MAX (*y_offset, 0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (x_offset)
|
|
*x_offset = 0;
|
|
if (y_offset)
|
|
*y_offset = 0;
|
|
}
|
|
|
|
if (width)
|
|
*width = w;
|
|
if (height)
|
|
*height = h;
|
|
}
|
|
|
|
static void
|
|
gtk_paint_spinner (GtkStyleContext *context,
|
|
cairo_t *cr,
|
|
guint step,
|
|
int x,
|
|
int y,
|
|
int width,
|
|
int height)
|
|
{
|
|
GdkRGBA color;
|
|
guint num_steps;
|
|
double dx, dy;
|
|
double radius;
|
|
double half;
|
|
int i;
|
|
guint real_step;
|
|
|
|
num_steps = 12;
|
|
real_step = step % num_steps;
|
|
|
|
/* set a clip region for the expose event */
|
|
cairo_rectangle (cr, x, y, width, height);
|
|
cairo_clip (cr);
|
|
|
|
cairo_translate (cr, x, y);
|
|
|
|
/* draw clip region */
|
|
cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
|
|
|
|
gtk_style_context_get_color (context, &color);
|
|
dx = width / 2;
|
|
dy = height / 2;
|
|
radius = MIN (width / 2, height / 2);
|
|
half = num_steps / 2;
|
|
|
|
for (i = 0; i < num_steps; i++)
|
|
{
|
|
int inset = 0.7 * radius;
|
|
|
|
/* transparency is a function of time and initial value */
|
|
double t = (double) ((i + num_steps - real_step)
|
|
% num_steps) / num_steps;
|
|
|
|
cairo_save (cr);
|
|
|
|
cairo_set_source_rgba (cr,
|
|
color.red / 65535.,
|
|
color.green / 65535.,
|
|
color.blue / 65535.,
|
|
color.alpha * t);
|
|
|
|
cairo_set_line_width (cr, 2.0);
|
|
cairo_move_to (cr,
|
|
dx + (radius - inset) * cos (i * G_PI / half),
|
|
dy + (radius - inset) * sin (i * G_PI / half));
|
|
cairo_line_to (cr,
|
|
dx + radius * cos (i * G_PI / half),
|
|
dy + radius * sin (i * G_PI / half));
|
|
cairo_stroke (cr);
|
|
|
|
cairo_restore (cr);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gtk_cell_renderer_spinner_snapshot (GtkCellRenderer *cell,
|
|
GtkSnapshot *snapshot,
|
|
GtkWidget *widget,
|
|
const GdkRectangle *background_area,
|
|
const GdkRectangle *cell_area,
|
|
GtkCellRendererState flags)
|
|
{
|
|
GtkCellRendererSpinner *self = GTK_CELL_RENDERER_SPINNER (cell);
|
|
GtkCellRendererSpinnerPrivate *priv = gtk_cell_renderer_spinner_get_instance_private (self);
|
|
GdkRectangle pix_rect;
|
|
GdkRectangle draw_rect;
|
|
int xpad, ypad;
|
|
cairo_t *cr;
|
|
|
|
if (!priv->active)
|
|
return;
|
|
|
|
gtk_cell_renderer_spinner_get_size (self, widget, cell_area,
|
|
&pix_rect.x,
|
|
&pix_rect.y,
|
|
&pix_rect.width,
|
|
&pix_rect.height);
|
|
|
|
g_object_get (self,
|
|
"xpad", &xpad,
|
|
"ypad", &ypad,
|
|
NULL);
|
|
|
|
pix_rect.x += cell_area->x + xpad;
|
|
pix_rect.y += cell_area->y + ypad;
|
|
pix_rect.width -= xpad * 2;
|
|
pix_rect.height -= ypad * 2;
|
|
|
|
if (!gdk_rectangle_intersect (cell_area, &pix_rect, &draw_rect))
|
|
return;
|
|
|
|
cr = gtk_snapshot_append_cairo (snapshot,
|
|
&GRAPHENE_RECT_INIT (
|
|
cell_area->x, cell_area->y,
|
|
cell_area->width, cell_area->height
|
|
));
|
|
|
|
gtk_paint_spinner (gtk_widget_get_style_context (widget),
|
|
cr,
|
|
priv->pulse,
|
|
draw_rect.x, draw_rect.y,
|
|
draw_rect.width, draw_rect.height);
|
|
|
|
cairo_destroy (cr);
|
|
}
|