2005-06-06 Carlos Garnacho Parro <carlosg@gnome.org> * gnomecc-canvas.c: added a11y support
1724 lines
45 KiB
C
1724 lines
45 KiB
C
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
|
/* Copyright (C) 2005 Carlos Garnacho
|
|
*
|
|
* 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 2 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, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
|
|
*
|
|
* Authors: Jody Goldberg <jody@gnome.org>
|
|
* Carlos Garnacho Parro <carlosg@gnome.org>
|
|
*/
|
|
|
|
#include "gnomecc-canvas.h"
|
|
#include "gnomecc-event-box.h"
|
|
#include "gnomecc-rounded-rect.h"
|
|
#include <libgnomecanvas/libgnomecanvas.h>
|
|
#include <libgnome/gnome-desktop-item.h>
|
|
#include <gconf/gconf-client.h>
|
|
#include <gdk/gdkkeysyms.h>
|
|
#include <gtk/gtk.h>
|
|
#include <atk/atk.h>
|
|
|
|
#define GNOMECC_CANVAS_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GNOMECC_TYPE_CANVAS, GnomeccCanvasPrivate))
|
|
|
|
#define PAD 5 /*when scrolling keep a few pixels above or below if possible */
|
|
#define ABOVE_LINE_SPACING 4
|
|
#define UNDER_LINE_SPACING 0
|
|
#define UNDER_TITLE_SPACING 0 /* manually insert 1 blank line of text */
|
|
#define LINE_HEIGHT 1
|
|
#define BORDERS 7
|
|
#define MAX_ITEM_WIDTH 120
|
|
#define ITEMS_SEPARATION 12
|
|
|
|
typedef struct _GnomeccCanvasPrivate GnomeccCanvasPrivate;
|
|
|
|
struct _GnomeccCanvasPrivate {
|
|
GnomeCanvasItem *under_cover;
|
|
double height;
|
|
double width;
|
|
|
|
double min_height;
|
|
double max_width;
|
|
ControlCenterInformation *info;
|
|
ControlCenterEntry *selected;
|
|
|
|
gboolean rtl;
|
|
gint items_per_row;
|
|
|
|
/* calculated sizes
|
|
for the elements */
|
|
gdouble max_item_width;
|
|
gdouble max_item_height;
|
|
gdouble max_icon_height;
|
|
|
|
/* accessibility stuff */
|
|
GHashTable *accessible_children;
|
|
};
|
|
|
|
typedef struct {
|
|
GnomeccCanvas *canvas;
|
|
|
|
GnomeCanvasGroup *group;
|
|
GnomeCanvasItem *text;
|
|
GnomeCanvasItem *pixbuf;
|
|
GnomeCanvasItem *highlight_pixbuf;
|
|
GnomeCanvasItem *cover;
|
|
GnomeCanvasItem *selection;
|
|
double icon_height;
|
|
double icon_width;
|
|
double text_height;
|
|
guint launching : 1;
|
|
guint selected : 1;
|
|
guint highlighted : 1;
|
|
|
|
gint n_category;
|
|
gint n_entry;
|
|
gint index;
|
|
} EntryInfo;
|
|
|
|
typedef struct {
|
|
GnomeCanvasGroup *group;
|
|
GnomeCanvasItem *title;
|
|
GnomeCanvasItem *line;
|
|
} CategoryInfo;
|
|
|
|
enum {
|
|
SELECTION_CHANGED,
|
|
LAST_SIGNAL
|
|
};
|
|
|
|
enum {
|
|
PROP_0,
|
|
PROP_INFO
|
|
};
|
|
|
|
static guint gnomecc_canvas_signals [LAST_SIGNAL] = { 0 };
|
|
|
|
|
|
static void gnomecc_canvas_class_init (GnomeccCanvasClass *class);
|
|
static void gnomecc_canvas_init (GnomeccCanvas *canvas);
|
|
static void gnomecc_canvas_finalize (GObject *object);
|
|
|
|
static void gnomecc_canvas_set_property (GObject *object,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec);
|
|
static void gnomecc_canvas_get_property (GObject *object,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec);
|
|
|
|
static void gnomecc_canvas_draw_background (GnomeCanvas *canvas, GdkDrawable *drawable,
|
|
int x, int y, int width, int height);
|
|
|
|
static void gnomecc_canvas_size_allocate (GtkWidget *canvas,
|
|
GtkAllocation *allocation);
|
|
static void gnomecc_canvas_style_set (GtkWidget *canvas,
|
|
GtkStyle *previous_style);
|
|
static void gnomecc_canvas_realize (GtkWidget *canvas);
|
|
|
|
static AtkObject* gnomecc_canvas_get_accessible (GtkWidget *widget);
|
|
|
|
|
|
|
|
G_DEFINE_TYPE (GnomeccCanvas, gnomecc_canvas, GNOME_TYPE_CANVAS);
|
|
|
|
static void
|
|
gnomecc_canvas_class_init (GnomeccCanvasClass *class)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
|
GnomeCanvasClass *canvas_class = GNOME_CANVAS_CLASS (class);
|
|
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
|
|
|
|
object_class->set_property = gnomecc_canvas_set_property;
|
|
object_class->get_property = gnomecc_canvas_get_property;
|
|
object_class->finalize = gnomecc_canvas_finalize;
|
|
canvas_class->draw_background = gnomecc_canvas_draw_background;
|
|
|
|
widget_class->style_set = gnomecc_canvas_style_set;
|
|
widget_class->size_allocate = gnomecc_canvas_size_allocate;
|
|
widget_class->realize = gnomecc_canvas_realize;
|
|
widget_class->get_accessible = gnomecc_canvas_get_accessible;
|
|
|
|
class->changed = NULL;
|
|
|
|
g_object_class_install_property (object_class,
|
|
PROP_INFO,
|
|
g_param_spec_pointer ("info",
|
|
"information for the canvas",
|
|
"information for the canvas",
|
|
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
|
|
gnomecc_canvas_signals [SELECTION_CHANGED] =
|
|
g_signal_new ("selection-changed",
|
|
G_OBJECT_CLASS_TYPE (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET (GnomeccCanvasClass, changed),
|
|
NULL, NULL,
|
|
g_cclosure_marshal_VOID__STRING,
|
|
G_TYPE_NONE, 1,
|
|
G_TYPE_STRING);
|
|
|
|
g_type_class_add_private (object_class,
|
|
sizeof (GnomeccCanvasPrivate));
|
|
}
|
|
|
|
static void
|
|
gnomecc_canvas_init (GnomeccCanvas *canvas)
|
|
{
|
|
GnomeccCanvasPrivate *priv;
|
|
|
|
g_return_if_fail (GNOMECC_IS_CANVAS (canvas));
|
|
|
|
priv = GNOMECC_CANVAS_GET_PRIVATE (canvas);
|
|
|
|
priv->max_width = 300;
|
|
priv->min_height = 0;
|
|
priv->info = NULL;
|
|
priv->selected = NULL;
|
|
|
|
priv->max_item_width = 0;
|
|
priv->max_item_height = 0;
|
|
priv->items_per_row = 0;
|
|
priv->rtl = (gtk_widget_get_direction (GTK_WIDGET (canvas)) == GTK_TEXT_DIR_RTL);
|
|
|
|
priv->accessible_children = g_hash_table_new (g_int_hash, g_int_equal);
|
|
|
|
gtk_widget_show_all (GTK_WIDGET (canvas));
|
|
}
|
|
|
|
static gboolean
|
|
single_click_activates (void)
|
|
{
|
|
static gboolean needs_init = TRUE;
|
|
static gboolean use_single_click = FALSE;
|
|
if (needs_init) {
|
|
GConfClient *client = gconf_client_get_default ();
|
|
char *policy = gconf_client_get_string (client, "/apps/nautilus/preferences/click_policy", NULL);
|
|
g_object_unref (G_OBJECT (client));
|
|
|
|
if (policy != NULL) {
|
|
use_single_click = (0 == g_ascii_strcasecmp (policy, "single"));
|
|
g_free (policy);
|
|
}
|
|
needs_init = FALSE;
|
|
}
|
|
|
|
return use_single_click;
|
|
}
|
|
|
|
static void
|
|
gnome_canvas_item_show_hide (GnomeCanvasItem *item, gboolean show)
|
|
{
|
|
if (show)
|
|
gnome_canvas_item_show (item);
|
|
else
|
|
gnome_canvas_item_hide (item);
|
|
}
|
|
|
|
static void
|
|
setup_entry (GnomeccCanvas *canvas, ControlCenterEntry *entry)
|
|
{
|
|
GnomeccCanvasPrivate *priv;
|
|
EntryInfo *ei;
|
|
GtkWidget *widget;
|
|
GtkStateType state;
|
|
|
|
if (!entry)
|
|
return;
|
|
|
|
priv = GNOMECC_CANVAS_GET_PRIVATE (canvas);
|
|
widget = GTK_WIDGET (canvas);
|
|
ei = entry->user_data;
|
|
|
|
if (ei->pixbuf) {
|
|
gnome_canvas_item_show_hide (ei->highlight_pixbuf, ei->highlighted);
|
|
gnome_canvas_item_show_hide (ei->pixbuf, !ei->highlighted);
|
|
}
|
|
if (!ei->selected)
|
|
state = GTK_STATE_NORMAL;
|
|
else if (gtk_window_has_toplevel_focus (GTK_WINDOW (gtk_widget_get_toplevel (widget))))
|
|
state = GTK_STATE_SELECTED;
|
|
else
|
|
state = GTK_STATE_ACTIVE;
|
|
gnome_canvas_item_show_hide (ei->selection, ei->selected);
|
|
g_object_set (ei->selection,
|
|
"fill_color_gdk", &widget->style->base [state],
|
|
NULL);
|
|
g_object_set (ei->text,
|
|
"fill_color_gdk", &widget->style->text [state],
|
|
NULL);
|
|
}
|
|
|
|
static gboolean
|
|
cb_entry_info_reset (gpointer data)
|
|
{
|
|
EntryInfo *ei = data;
|
|
ei->launching = FALSE;
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
activate_entry (ControlCenterEntry *entry)
|
|
{
|
|
EntryInfo *ei = entry->user_data;
|
|
|
|
if (!ei->launching) {
|
|
GnomeDesktopItem *desktop_item;
|
|
|
|
ei->launching = TRUE;
|
|
gtk_timeout_add (1000, cb_entry_info_reset, ei);
|
|
|
|
desktop_item = gnome_desktop_item_new_from_file (entry->desktop_entry,
|
|
GNOME_DESKTOP_ITEM_LOAD_ONLY_IF_EXISTS,
|
|
NULL);
|
|
if (desktop_item != NULL) {
|
|
gnome_desktop_item_launch (desktop_item, NULL, 0, NULL);
|
|
gnome_desktop_item_unref (desktop_item);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
select_entry (GnomeccCanvas *canvas, ControlCenterEntry *entry)
|
|
{
|
|
GnomeccCanvasPrivate *priv;
|
|
EntryInfo *ei = NULL;
|
|
GtkAdjustment *pos;
|
|
double affine[6];
|
|
ControlCenterEntry *selected;
|
|
|
|
priv = GNOMECC_CANVAS_GET_PRIVATE (canvas);
|
|
selected = priv->selected;
|
|
|
|
if (selected == entry)
|
|
return;
|
|
|
|
if (selected && selected->user_data)
|
|
((EntryInfo *) selected->user_data)->selected = FALSE;
|
|
setup_entry (canvas, selected);
|
|
|
|
priv->selected = selected = entry;
|
|
|
|
if (selected && selected->user_data)
|
|
((EntryInfo *) selected->user_data)->selected = TRUE;
|
|
setup_entry (canvas, selected);
|
|
|
|
g_signal_emit (canvas, gnomecc_canvas_signals [SELECTION_CHANGED], 0,
|
|
(entry) ? entry->comment : NULL);
|
|
|
|
if (!entry)
|
|
return;
|
|
|
|
ei = entry->user_data;
|
|
gnome_canvas_item_i2c_affine (GNOME_CANVAS_ITEM (ei->group), affine);
|
|
pos = gtk_layout_get_vadjustment (GTK_LAYOUT (ei->cover->canvas));
|
|
|
|
if (affine[5] < pos->value)
|
|
gtk_adjustment_set_value (pos, MAX (affine[5] - PAD, 0));
|
|
else if ((affine[5] + priv->max_item_height) > (pos->value+pos->page_size))
|
|
gtk_adjustment_set_value (pos, MAX (MIN (affine[5] + priv->max_item_height + PAD, pos->upper) - pos->page_size, 0));
|
|
}
|
|
|
|
static gboolean
|
|
cover_event (GnomeCanvasItem *item, GdkEvent *event, ControlCenterEntry *entry)
|
|
{
|
|
EntryInfo *ei = entry->user_data;
|
|
GnomeccCanvas *canvas = ei->canvas;
|
|
|
|
switch (event->type) {
|
|
case GDK_ENTER_NOTIFY:
|
|
ei->highlighted = TRUE;
|
|
setup_entry (canvas, entry); /* highlight even if it is already selected */
|
|
return TRUE;
|
|
case GDK_LEAVE_NOTIFY:
|
|
ei->highlighted = FALSE;
|
|
setup_entry (canvas, entry);
|
|
return TRUE;
|
|
case GDK_BUTTON_PRESS:
|
|
if (single_click_activates ()) {
|
|
activate_entry (entry);
|
|
} else {
|
|
select_entry (canvas, entry);
|
|
}
|
|
return TRUE;
|
|
case GDK_2BUTTON_PRESS:
|
|
activate_entry (entry);
|
|
return TRUE;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
cb_canvas_event (GnomeCanvasItem *item, GdkEvent *event, GnomeccCanvas *canvas)
|
|
{
|
|
GnomeccCanvasPrivate *priv;
|
|
EntryInfo *ei = NULL;
|
|
gint n_category, n_entry;
|
|
gint n_categories, n_entries;
|
|
|
|
priv = GNOMECC_CANVAS_GET_PRIVATE (canvas);
|
|
|
|
if (event->type == GDK_BUTTON_PRESS) {
|
|
select_entry (canvas, NULL);
|
|
return TRUE;
|
|
}
|
|
|
|
if (event->type != GDK_KEY_PRESS)
|
|
return FALSE;
|
|
|
|
if (priv->selected)
|
|
ei = priv->selected->user_data;
|
|
|
|
n_entry = 0;
|
|
n_category = 0;
|
|
n_categories = priv->info->n_categories;
|
|
n_entries = priv->info->categories[ei->n_category]->n_entries;
|
|
|
|
switch (event->key.keyval) {
|
|
case GDK_KP_Right:
|
|
case GDK_Right:
|
|
if (ei) {
|
|
n_entry = (priv->rtl) ? ei->n_entry - 1 : ei->n_entry + 1;
|
|
n_category = ei->n_category;
|
|
}
|
|
break;
|
|
case GDK_KP_Left:
|
|
case GDK_Left:
|
|
if (ei) {
|
|
n_entry = (priv->rtl) ? ei->n_entry + 1 : ei->n_entry - 1;
|
|
n_category = ei->n_category;
|
|
}
|
|
break;
|
|
case GDK_KP_Down:
|
|
case GDK_Down:
|
|
if (ei) {
|
|
n_category = ei->n_category;
|
|
n_entry = ei->n_entry;
|
|
|
|
if (ei->n_entry + priv->items_per_row <
|
|
priv->info->categories[n_category]->n_entries)
|
|
n_entry += priv->items_per_row;
|
|
}
|
|
break;
|
|
case GDK_KP_Up:
|
|
case GDK_Up:
|
|
if (ei) {
|
|
n_category = ei->n_category;
|
|
n_entry = ei->n_entry;
|
|
|
|
if (ei->n_entry - priv->items_per_row >= 0)
|
|
n_entry -= priv->items_per_row;
|
|
}
|
|
break;
|
|
case GDK_Tab:
|
|
case GDK_KP_Tab:
|
|
if (ei) {
|
|
n_entry = 0;
|
|
n_category = ei->n_category + 1;
|
|
|
|
if (n_category > priv->info->n_categories - 1)
|
|
n_category = 0;
|
|
}
|
|
break;
|
|
case GDK_ISO_Left_Tab:
|
|
if (ei) {
|
|
n_entry = 0;
|
|
n_category = ei->n_category - 1;
|
|
|
|
if (n_category < 0)
|
|
n_category = priv->info->n_categories;
|
|
}
|
|
break;
|
|
case GDK_Return:
|
|
case GDK_KP_Enter:
|
|
if (priv->selected)
|
|
activate_entry (priv->selected);
|
|
|
|
return TRUE;
|
|
break;
|
|
case GDK_Escape:
|
|
gtk_main_quit ();
|
|
return TRUE;
|
|
case 'q':
|
|
case 'Q':
|
|
if (event->key.state == GDK_CONTROL_MASK) {
|
|
gtk_main_quit ();
|
|
}
|
|
return TRUE;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
n_category = CLAMP (n_category, 0, priv->info->n_categories - 1);
|
|
n_entry = CLAMP (n_entry, 0, priv->info->categories[n_category]->n_entries - 1);
|
|
|
|
select_entry (canvas, priv->info->categories[n_category]->entries[n_entry]);
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
calculate_item_width (GnomeccCanvas *canvas, EntryInfo *ei)
|
|
{
|
|
GnomeccCanvasPrivate *priv;
|
|
PangoLayout *layout;
|
|
PangoRectangle rectangle;
|
|
|
|
priv = GNOMECC_CANVAS_GET_PRIVATE (canvas);
|
|
layout = GNOME_CANVAS_TEXT (ei->text)->layout;
|
|
|
|
pango_layout_set_wrap (layout, PANGO_WRAP_WORD_CHAR);
|
|
pango_layout_set_width (layout, -1);
|
|
pango_layout_get_pixel_extents (layout, NULL, &rectangle);
|
|
|
|
/* If its too big wrap at the max and regen to find the layout */
|
|
if (rectangle.width > MAX_ITEM_WIDTH) {
|
|
pango_layout_set_width (layout, MAX_ITEM_WIDTH * PANGO_SCALE);
|
|
pango_layout_get_pixel_extents (layout, NULL, &rectangle);
|
|
rectangle.width = MAX_ITEM_WIDTH;
|
|
}
|
|
|
|
ei->text_height = rectangle.height;
|
|
|
|
priv->max_item_width = MAX (priv->max_item_width, rectangle.width);
|
|
}
|
|
|
|
static void
|
|
calculate_item_height (GnomeccCanvas *canvas, EntryInfo *ei)
|
|
{
|
|
GnomeccCanvasPrivate *priv;
|
|
gint item_height;
|
|
|
|
priv = GNOMECC_CANVAS_GET_PRIVATE (canvas);
|
|
|
|
if (ei->pixbuf)
|
|
priv->max_icon_height = MAX (priv->max_icon_height, ei->icon_height);
|
|
|
|
item_height = ei->icon_height + ei->text_height;
|
|
priv->max_item_height = MAX (priv->max_item_height, item_height);
|
|
}
|
|
|
|
static void
|
|
calculate_sizes (GnomeccCanvas *canvas)
|
|
{
|
|
GnomeccCanvasPrivate *priv;
|
|
int i, j;
|
|
|
|
priv = GNOMECC_CANVAS_GET_PRIVATE (canvas);
|
|
priv->max_item_height = 0;
|
|
priv->max_icon_height = 0;
|
|
priv->max_item_width = 0;
|
|
|
|
for (i = 0; i < priv->info->n_categories; i++) {
|
|
for (j = 0; j < priv->info->categories[i]->n_entries; j++) {
|
|
EntryInfo *ei = priv->info->categories[i]->entries[j]->user_data;
|
|
|
|
calculate_item_width (canvas, ei);
|
|
calculate_item_height (canvas, ei);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
gnome_canvas_item_move_absolute (GnomeCanvasItem *item, double dx, double dy)
|
|
{
|
|
double translate[6];
|
|
|
|
g_return_if_fail (item != NULL);
|
|
g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
|
|
|
|
art_affine_translate (translate, dx, dy);
|
|
|
|
gnome_canvas_item_affine_absolute (item, translate);
|
|
}
|
|
|
|
static guchar
|
|
lighten_component (guchar cur_value)
|
|
{
|
|
int new_value = cur_value;
|
|
new_value += 24 + (new_value >> 3);
|
|
if (new_value > 255) {
|
|
new_value = 255;
|
|
}
|
|
return (guchar) new_value;
|
|
}
|
|
|
|
static GdkPixbuf *
|
|
create_spotlight_pixbuf (GdkPixbuf* src)
|
|
{
|
|
GdkPixbuf *dest;
|
|
int i, j;
|
|
int width, height, has_alpha, src_row_stride, dst_row_stride;
|
|
guchar *target_pixels, *original_pixels;
|
|
guchar *pixsrc, *pixdest;
|
|
|
|
g_return_val_if_fail (gdk_pixbuf_get_colorspace (src) == GDK_COLORSPACE_RGB, NULL);
|
|
g_return_val_if_fail ((!gdk_pixbuf_get_has_alpha (src)
|
|
&& gdk_pixbuf_get_n_channels (src) == 3)
|
|
|| (gdk_pixbuf_get_has_alpha (src)
|
|
&& gdk_pixbuf_get_n_channels (src) == 4), NULL);
|
|
g_return_val_if_fail (gdk_pixbuf_get_bits_per_sample (src) == 8, NULL);
|
|
|
|
dest = gdk_pixbuf_copy (src);
|
|
|
|
has_alpha = gdk_pixbuf_get_has_alpha (src);
|
|
width = gdk_pixbuf_get_width (src);
|
|
height = gdk_pixbuf_get_height (src);
|
|
dst_row_stride = gdk_pixbuf_get_rowstride (dest);
|
|
src_row_stride = gdk_pixbuf_get_rowstride (src);
|
|
target_pixels = gdk_pixbuf_get_pixels (dest);
|
|
original_pixels = gdk_pixbuf_get_pixels (src);
|
|
|
|
for (i = 0; i < height; i++) {
|
|
pixdest = target_pixels + i * dst_row_stride;
|
|
pixsrc = original_pixels + i * src_row_stride;
|
|
for (j = 0; j < width; j++) {
|
|
*pixdest++ = lighten_component (*pixsrc++);
|
|
*pixdest++ = lighten_component (*pixsrc++);
|
|
*pixdest++ = lighten_component (*pixsrc++);
|
|
if (has_alpha) {
|
|
*pixdest++ = *pixsrc++;
|
|
}
|
|
}
|
|
}
|
|
|
|
return dest;
|
|
}
|
|
|
|
static void
|
|
build_canvas (GnomeccCanvas *canvas)
|
|
{
|
|
GnomeccCanvasPrivate *priv;
|
|
GnomeCanvas *gcanvas;
|
|
int i, j, index;
|
|
|
|
priv = GNOMECC_CANVAS_GET_PRIVATE (canvas);
|
|
gcanvas = GNOME_CANVAS (canvas);
|
|
index = 0;
|
|
|
|
priv->under_cover = gnome_canvas_item_new (gnome_canvas_root (gcanvas),
|
|
gnomecc_event_box_get_type(),
|
|
NULL);
|
|
|
|
gnome_canvas_item_grab_focus (GNOME_CANVAS_ITEM (gnome_canvas_root (gcanvas)));
|
|
g_signal_connect (gnome_canvas_root (gcanvas), "event",
|
|
G_CALLBACK (cb_canvas_event), canvas);
|
|
|
|
for (i = 0; i < priv->info->n_categories; i++) {
|
|
CategoryInfo *catinfo;
|
|
|
|
if (priv->info->categories[i]->user_data == NULL)
|
|
priv->info->categories[i]->user_data = g_new (CategoryInfo, 1);
|
|
|
|
catinfo = priv->info->categories[i]->user_data;
|
|
catinfo->group = NULL;
|
|
catinfo->title = NULL;
|
|
catinfo->line = NULL;
|
|
|
|
catinfo->group =
|
|
GNOME_CANVAS_GROUP (gnome_canvas_item_new (gnome_canvas_root (gcanvas),
|
|
gnome_canvas_group_get_type (),
|
|
NULL));
|
|
gnome_canvas_item_move_absolute (GNOME_CANVAS_ITEM (catinfo->group), 0, BORDERS);
|
|
|
|
if (i > 0) {
|
|
catinfo->line = gnome_canvas_item_new (catinfo->group,
|
|
gnome_canvas_rect_get_type (),
|
|
"x2", (double) priv->max_width - 2 * BORDERS,
|
|
"y2", (double) LINE_HEIGHT,
|
|
NULL);
|
|
}
|
|
|
|
catinfo->title = NULL;
|
|
if (priv->info->categories[i] && (priv->info->n_categories != 1 || priv->info->categories[0]->real_category)) {
|
|
char *label = g_strdup_printf ("<span weight=\"bold\">%s</span>", priv->info->categories[i]->title);
|
|
catinfo->title = gnome_canvas_item_new (catinfo->group,
|
|
gnome_canvas_text_get_type (),
|
|
"text", priv->info->categories[i]->title,
|
|
"markup", label,
|
|
"anchor", GTK_ANCHOR_NW,
|
|
NULL);
|
|
g_free (label);
|
|
}
|
|
|
|
for (j = 0; j < priv->info->categories[i]->n_entries; j++) {
|
|
EntryInfo *ei;
|
|
|
|
if (priv->info->categories[i]->entries[j]->user_data == NULL)
|
|
priv->info->categories[i]->entries[j]->user_data = g_new0 (EntryInfo, 1);
|
|
|
|
ei = priv->info->categories[i]->entries[j]->user_data;
|
|
|
|
ei->canvas = canvas;
|
|
ei->group = GNOME_CANVAS_GROUP (
|
|
gnome_canvas_item_new (catinfo->group,
|
|
gnome_canvas_group_get_type (),
|
|
NULL));
|
|
ei->selection = gnome_canvas_item_new (
|
|
ei->group,
|
|
GNOMECC_TYPE_ROUNDED_RECT,
|
|
NULL);
|
|
|
|
if (priv->info->categories[i]->entries[j]->title) {
|
|
ei->text = gnome_canvas_item_new (ei->group,
|
|
gnome_canvas_text_get_type (),
|
|
"anchor", GTK_ANCHOR_NW,
|
|
"justification", GTK_JUSTIFY_CENTER,
|
|
"clip", TRUE,
|
|
NULL);
|
|
pango_layout_set_alignment (GNOME_CANVAS_TEXT (ei->text)->layout,
|
|
PANGO_ALIGN_CENTER);
|
|
pango_layout_set_justify (GNOME_CANVAS_TEXT (ei->text)->layout,
|
|
FALSE);
|
|
g_object_set (ei->text,
|
|
"text", priv->info->categories[i]->entries[j]->title,
|
|
NULL);
|
|
} else
|
|
ei->text = NULL;
|
|
|
|
if (priv->info->categories[i]->entries[j]->icon_pixbuf) {
|
|
GdkPixbuf *pixbuf = priv->info->categories[i]->entries[j]->icon_pixbuf;
|
|
GdkPixbuf *highlight_pixbuf =
|
|
create_spotlight_pixbuf (pixbuf);
|
|
ei->icon_height = gdk_pixbuf_get_height (pixbuf);
|
|
ei->icon_width = gdk_pixbuf_get_width (pixbuf);
|
|
ei->pixbuf = gnome_canvas_item_new (ei->group,
|
|
gnome_canvas_pixbuf_get_type (),
|
|
"pixbuf", pixbuf,
|
|
NULL);
|
|
g_object_unref (pixbuf);
|
|
ei->highlight_pixbuf = gnome_canvas_item_new (ei->group,
|
|
gnome_canvas_pixbuf_get_type (),
|
|
"pixbuf", highlight_pixbuf,
|
|
NULL);
|
|
g_object_unref (highlight_pixbuf);
|
|
} else {
|
|
ei->pixbuf = NULL;
|
|
ei->highlight_pixbuf = NULL;
|
|
}
|
|
|
|
ei->cover = gnome_canvas_item_new (ei->group,
|
|
gnomecc_event_box_get_type(),
|
|
NULL);
|
|
calculate_item_width (canvas, ei);
|
|
calculate_item_height (canvas, ei);
|
|
|
|
ei->n_category = i;
|
|
ei->n_entry = j;
|
|
ei->index = index;
|
|
|
|
g_signal_connect (ei->cover, "event",
|
|
G_CALLBACK (cover_event),
|
|
priv->info->categories[i]->entries[j]);
|
|
|
|
setup_entry (canvas, priv->info->categories[i]->entries[j]);
|
|
index++;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
gnomecc_canvas_set_property (GObject *object,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
GnomeccCanvasPrivate *priv;
|
|
|
|
priv = GNOMECC_CANVAS_GET_PRIVATE (object);
|
|
|
|
switch (prop_id) {
|
|
case PROP_INFO:
|
|
priv->info = g_value_get_pointer (value);
|
|
build_canvas (GNOMECC_CANVAS (object));
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gnomecc_canvas_get_property (GObject *object,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
GnomeccCanvasPrivate *priv;
|
|
|
|
priv = GNOMECC_CANVAS_GET_PRIVATE (object);
|
|
|
|
switch (prop_id) {
|
|
case PROP_INFO:
|
|
g_value_set_pointer (value, priv->info);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gnomecc_canvas_finalize (GObject *object)
|
|
{
|
|
if (G_OBJECT_CLASS (gnomecc_canvas_parent_class)->finalize)
|
|
(* G_OBJECT_CLASS (gnomecc_canvas_parent_class)->finalize) (object);
|
|
}
|
|
|
|
static void
|
|
gnomecc_canvas_draw_background (GnomeCanvas *canvas, GdkDrawable *drawable,
|
|
int x, int y, int width, int height)
|
|
{
|
|
/* By default, we use the style base color. */
|
|
gdk_gc_set_foreground (canvas->pixmap_gc,
|
|
>K_WIDGET (canvas)->style->base[GTK_STATE_NORMAL]);
|
|
gdk_draw_rectangle (drawable,
|
|
canvas->pixmap_gc,
|
|
TRUE,
|
|
0, 0,
|
|
width, height);
|
|
}
|
|
|
|
/* A category canvas item contains all the elements
|
|
* for the category, as well as the title and a separator
|
|
*/
|
|
static void
|
|
relayout_category (GnomeccCanvas *canvas, CategoryInfo *catinfo,
|
|
gint vert_pos, gint *category_vert_pos)
|
|
{
|
|
GnomeccCanvasPrivate *priv;
|
|
|
|
priv = GNOMECC_CANVAS_GET_PRIVATE (canvas);
|
|
gnome_canvas_item_move_absolute (GNOME_CANVAS_ITEM (catinfo->group),
|
|
0, vert_pos);
|
|
|
|
if (catinfo->line) {
|
|
gnome_canvas_item_move_absolute (catinfo->line, BORDERS, ABOVE_LINE_SPACING);
|
|
|
|
gnome_canvas_item_set (catinfo->line,
|
|
"x2", (double) priv->max_width - 2 * BORDERS,
|
|
"y2", (double) LINE_HEIGHT,
|
|
NULL);
|
|
}
|
|
|
|
if (catinfo->title) {
|
|
double text_height, text_width;
|
|
|
|
g_object_get (catinfo->title,
|
|
"text_height", &text_height,
|
|
"text_width", &text_width,
|
|
NULL);
|
|
|
|
*category_vert_pos += text_height; /* move it down 1 line */
|
|
gnome_canvas_item_move_absolute (catinfo->title,
|
|
(priv->rtl) ? priv->max_width - BORDERS - text_width : BORDERS,
|
|
*category_vert_pos);
|
|
*category_vert_pos += text_height + text_height/2 + UNDER_TITLE_SPACING;
|
|
}
|
|
}
|
|
|
|
static void
|
|
relayout_item (GnomeccCanvas *canvas, EntryInfo *ei,
|
|
gint category_horiz_pos, gint category_vert_pos)
|
|
{
|
|
GnomeccCanvasPrivate *priv;
|
|
|
|
priv = GNOMECC_CANVAS_GET_PRIVATE (canvas);
|
|
|
|
gnome_canvas_item_move_absolute (GNOME_CANVAS_ITEM (ei->group),
|
|
category_horiz_pos,
|
|
category_vert_pos);
|
|
|
|
gnome_canvas_item_set (ei->selection,
|
|
"x2", (double) priv->max_item_width + 2 * PAD,
|
|
"y2", (double) ei->text_height + 1, /* expand it down slightly */
|
|
NULL);
|
|
|
|
gnome_canvas_item_move_absolute (ei->selection, -PAD, priv->max_icon_height);
|
|
|
|
if (ei->text) {
|
|
/* canvas asks layout for its extent, layout gives real
|
|
* size, not fixed width and drawing gets confused.
|
|
*/
|
|
gnome_canvas_item_set (ei->text,
|
|
"clip_width", (double) priv->max_item_width,
|
|
"clip_height", (double) priv->max_item_height,
|
|
NULL);
|
|
|
|
/* text is centered by pango */
|
|
pango_layout_set_width (GNOME_CANVAS_TEXT (ei->text)->layout,
|
|
(gint) priv->max_item_width * PANGO_SCALE);
|
|
|
|
gnome_canvas_item_move_absolute (ei->text,
|
|
0, priv->max_icon_height);
|
|
}
|
|
|
|
if (ei->pixbuf) {
|
|
/* manually center the icon */
|
|
gnome_canvas_item_move_absolute (ei->pixbuf,
|
|
priv->max_item_width / 2 - ei->icon_width / 2, 0);
|
|
gnome_canvas_item_move_absolute (ei->highlight_pixbuf,
|
|
priv->max_item_width / 2 - ei->icon_width / 2, 0);
|
|
}
|
|
|
|
/* cover the item */
|
|
gnome_canvas_item_set (ei->cover,
|
|
"x2", (double) priv->max_item_width,
|
|
"y2", (double) priv->max_item_height,
|
|
NULL);
|
|
}
|
|
|
|
static void
|
|
relayout_canvas (GnomeccCanvas *canvas)
|
|
{
|
|
gint cat, cat_count, entry, cat_entries;
|
|
gint vert_pos, category_vert_pos, category_horiz_pos;
|
|
gint real_width;
|
|
GnomeccCanvasPrivate *priv;
|
|
ControlCenterCategory *category;
|
|
CategoryInfo *catinfo;
|
|
EntryInfo *ei;
|
|
|
|
priv = GNOMECC_CANVAS_GET_PRIVATE (canvas);
|
|
real_width = priv->max_width - 2 * BORDERS + ITEMS_SEPARATION;
|
|
priv->items_per_row = real_width / ((gint) priv->max_item_width + ITEMS_SEPARATION);
|
|
|
|
cat_count = priv->info->n_categories;
|
|
vert_pos = BORDERS;
|
|
|
|
for (cat = 0; cat < cat_count; cat++) {
|
|
category_vert_pos = 0;
|
|
|
|
category_horiz_pos = (priv->rtl) ? priv->max_width - (gint) priv->max_item_width - BORDERS : BORDERS;
|
|
|
|
category = priv->info->categories [cat];
|
|
catinfo = category->user_data;
|
|
|
|
cat_entries = category->n_entries;
|
|
|
|
relayout_category (canvas, catinfo, vert_pos, &category_vert_pos);
|
|
category_vert_pos += UNDER_LINE_SPACING;
|
|
|
|
for (entry = 0; entry < cat_entries; entry++) {
|
|
/* we don't want the first item to wrap, it would
|
|
be too separated from the section title */
|
|
if ((entry > 0) &&
|
|
((priv->items_per_row == 0) ||
|
|
(priv->items_per_row > 0 && (entry % priv->items_per_row == 0)))) {
|
|
|
|
category_horiz_pos = (priv->rtl) ?
|
|
priv->max_width - (gint) priv->max_item_width - BORDERS : BORDERS;
|
|
category_vert_pos += (gint) priv->max_item_height + ITEMS_SEPARATION;
|
|
}
|
|
|
|
ei = category->entries[entry]->user_data;
|
|
|
|
relayout_item (canvas, ei, category_horiz_pos, category_vert_pos);
|
|
|
|
if (priv->rtl)
|
|
category_horiz_pos -= (gint) priv->max_item_width + ITEMS_SEPARATION;
|
|
else
|
|
category_horiz_pos += (gint) priv->max_item_width + ITEMS_SEPARATION;
|
|
}
|
|
|
|
category_vert_pos += (gint) priv->max_item_height;
|
|
vert_pos += category_vert_pos + ITEMS_SEPARATION;
|
|
}
|
|
|
|
/* substract the last ITEMS_SEPARATION to
|
|
adjust the canvas size a bit more */
|
|
vert_pos -= ITEMS_SEPARATION;
|
|
|
|
priv->height = MAX (vert_pos, priv->min_height);
|
|
priv->width = priv->max_width;
|
|
}
|
|
|
|
static void
|
|
gnomecc_canvas_size_allocate(GtkWidget *canvas, GtkAllocation *allocation)
|
|
{
|
|
GnomeccCanvasPrivate *priv;
|
|
|
|
if (GTK_WIDGET_CLASS (gnomecc_canvas_parent_class)->size_allocate)
|
|
(* GTK_WIDGET_CLASS (gnomecc_canvas_parent_class)->size_allocate) (canvas, allocation);
|
|
|
|
if (allocation->height == 1 || allocation->width == 1)
|
|
return;
|
|
|
|
priv = GNOMECC_CANVAS_GET_PRIVATE (canvas);
|
|
priv->max_width = allocation->width;
|
|
priv->min_height = allocation->height;
|
|
|
|
relayout_canvas (GNOMECC_CANVAS (canvas));
|
|
|
|
gnome_canvas_set_scroll_region (GNOME_CANVAS (canvas), 0, 0, priv->width - 1, priv->height - 1);
|
|
g_object_set (priv->under_cover,
|
|
"x2", priv->width,
|
|
"y2", priv->height,
|
|
NULL);
|
|
}
|
|
|
|
static void
|
|
set_style (GnomeccCanvas *canvas, gboolean font_changed)
|
|
{
|
|
int i, j;
|
|
GnomeccCanvasPrivate *priv;
|
|
GtkWidget *widget = GTK_WIDGET (canvas);
|
|
|
|
if (!GTK_WIDGET_REALIZED (widget))
|
|
return;
|
|
|
|
priv = GNOMECC_CANVAS_GET_PRIVATE (canvas);
|
|
|
|
for (i = 0; i < priv->info->n_categories; i++) {
|
|
CategoryInfo *catinfo = priv->info->categories[i]->user_data;
|
|
|
|
if (catinfo->line) {
|
|
g_object_set (catinfo->line,
|
|
"fill_color_gdk", &widget->style->text_aa[GTK_STATE_NORMAL],
|
|
NULL);
|
|
}
|
|
if (catinfo->title) {
|
|
g_object_set (catinfo->title,
|
|
"fill_color_gdk", &widget->style->text[GTK_STATE_NORMAL],
|
|
NULL);
|
|
|
|
if (font_changed)
|
|
g_object_set (catinfo->title,
|
|
"font", NULL,
|
|
NULL);
|
|
}
|
|
|
|
for (j = 0; j < priv->info->categories[i]->n_entries; j++) {
|
|
ControlCenterEntry *entry = priv->info->categories[i]->entries[j];
|
|
EntryInfo *entryinfo = entry->user_data;
|
|
if (font_changed && entryinfo->text)
|
|
g_object_set (entryinfo->text,
|
|
"font", NULL,
|
|
NULL);
|
|
setup_entry (canvas, entry);
|
|
}
|
|
}
|
|
|
|
if (font_changed) {
|
|
calculate_sizes (canvas);
|
|
relayout_canvas (canvas);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gnomecc_canvas_style_set (GtkWidget *canvas, GtkStyle *previous_style)
|
|
{
|
|
if (!GTK_WIDGET_REALIZED (canvas))
|
|
return;
|
|
|
|
set_style (GNOMECC_CANVAS (canvas), (previous_style &&
|
|
canvas->style &&
|
|
!pango_font_description_equal (canvas->style->font_desc,
|
|
previous_style->font_desc)));
|
|
}
|
|
|
|
static void
|
|
gnomecc_canvas_realize (GtkWidget *canvas)
|
|
{
|
|
if (GTK_WIDGET_CLASS (gnomecc_canvas_parent_class)->realize)
|
|
(* GTK_WIDGET_CLASS (gnomecc_canvas_parent_class)->realize) (canvas);
|
|
|
|
set_style (GNOMECC_CANVAS (canvas), FALSE);
|
|
}
|
|
|
|
GtkWidget*
|
|
gnomecc_canvas_new (ControlCenterInformation *info)
|
|
{
|
|
return g_object_new (GNOMECC_TYPE_CANVAS,
|
|
"info", info,
|
|
NULL);
|
|
}
|
|
|
|
/* Accessibility support */
|
|
static gpointer accessible_parent_class;
|
|
static gpointer accessible_item_parent_class;
|
|
|
|
enum {
|
|
ACTION_ACTIVATE,
|
|
LAST_ACTION
|
|
};
|
|
|
|
typedef struct {
|
|
AtkObject parent;
|
|
|
|
ControlCenterEntry *entry;
|
|
AtkStateSet *state_set;
|
|
|
|
guint action_idle_handler;
|
|
} GnomeccCanvasItemAccessible;
|
|
|
|
typedef struct {
|
|
AtkObjectClass parent_class;
|
|
} GnomeccCanvasItemAccessibleClass;
|
|
|
|
static const gchar *const action_names[] =
|
|
{
|
|
"activate",
|
|
NULL
|
|
};
|
|
|
|
static const gchar *const action_descriptions[] =
|
|
{
|
|
"Activate item",
|
|
NULL
|
|
};
|
|
|
|
static void
|
|
gnomecc_canvas_item_accessible_get_extents (AtkComponent *component,
|
|
gint *x,
|
|
gint *y,
|
|
gint *width,
|
|
gint *height,
|
|
AtkCoordType coord_type)
|
|
{
|
|
GnomeccCanvasItemAccessible *item;
|
|
GnomeccCanvas *canvas;
|
|
GnomeCanvasItem *cover;
|
|
AtkObject *parent_object;
|
|
gint p_x, p_y;
|
|
|
|
item = (GnomeccCanvasItemAccessible *) component;
|
|
|
|
canvas = ((EntryInfo *) item->entry->user_data)->canvas;
|
|
parent_object = gtk_widget_get_accessible (GTK_WIDGET (canvas));
|
|
atk_component_get_position (ATK_COMPONENT (parent_object), &p_x, &p_y, coord_type);
|
|
|
|
/* the cover pretty much represents the item size */
|
|
cover = GNOME_CANVAS_ITEM (((EntryInfo *)item->entry->user_data)->cover);
|
|
|
|
*x = p_x + cover->x1;
|
|
*y = p_y + cover->y1;
|
|
*width = cover->x2 - cover->x1;
|
|
*height = cover->y2 - cover->y1;
|
|
}
|
|
|
|
static void
|
|
atk_component_item_interface_init (AtkComponentIface *iface)
|
|
{
|
|
iface->get_extents = gnomecc_canvas_item_accessible_get_extents;
|
|
}
|
|
|
|
static gboolean
|
|
idle_do_action (gpointer data)
|
|
{
|
|
GnomeccCanvasItemAccessible *item;
|
|
|
|
item = (GnomeccCanvasItemAccessible *) data;
|
|
|
|
item->action_idle_handler = 0;
|
|
activate_entry (item->entry);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
gnomecc_canvas_item_accessible_action_do_action (AtkAction *action,
|
|
gint index)
|
|
{
|
|
GnomeccCanvasItemAccessible *item;
|
|
|
|
if (index < 0 || index >= LAST_ACTION)
|
|
return FALSE;
|
|
|
|
item = (GnomeccCanvasItemAccessible *) action;
|
|
|
|
switch (index) {
|
|
case ACTION_ACTIVATE:
|
|
if (!item->action_idle_handler)
|
|
item->action_idle_handler = g_idle_add (idle_do_action, item);
|
|
break;
|
|
default:
|
|
g_assert_not_reached ();
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gint
|
|
gnomecc_canvas_item_accessible_action_get_n_actions (AtkAction *action)
|
|
{
|
|
return LAST_ACTION;
|
|
}
|
|
|
|
static const gchar *
|
|
gnomecc_canvas_item_accessible_action_get_description (AtkAction *action,
|
|
gint index)
|
|
{
|
|
if (index < 0 || index >= LAST_ACTION)
|
|
return NULL;
|
|
|
|
return action_descriptions[index];
|
|
}
|
|
|
|
static const gchar *
|
|
gnomecc_canvas_item_accessible_action_get_name (AtkAction *action,
|
|
gint index)
|
|
{
|
|
if (index < 0 || index >= LAST_ACTION)
|
|
return NULL;
|
|
|
|
return action_names[index];
|
|
}
|
|
|
|
static void
|
|
atk_action_item_interface_init (AtkActionIface *iface)
|
|
{
|
|
iface->do_action = gnomecc_canvas_item_accessible_action_do_action;
|
|
iface->get_n_actions = gnomecc_canvas_item_accessible_action_get_n_actions;
|
|
iface->get_description = gnomecc_canvas_item_accessible_action_get_description;
|
|
iface->get_name = gnomecc_canvas_item_accessible_action_get_name;
|
|
}
|
|
|
|
static gint
|
|
gnomecc_canvas_item_accessible_get_index_in_parent (AtkObject *object)
|
|
{
|
|
GnomeccCanvasItemAccessible *item;
|
|
|
|
item = (GnomeccCanvasItemAccessible *) object;
|
|
|
|
return ((EntryInfo *) item->entry->user_data)->index;
|
|
}
|
|
|
|
static G_CONST_RETURN gchar*
|
|
gnomecc_canvas_item_accessible_get_name (AtkObject *object)
|
|
{
|
|
GnomeccCanvasItemAccessible *item;
|
|
|
|
if (object->name)
|
|
return object->name;
|
|
else {
|
|
item = (GnomeccCanvasItemAccessible *) object;
|
|
|
|
if (item->entry && item->entry->title)
|
|
return item->entry->title;
|
|
else
|
|
return "Item with no description";
|
|
}
|
|
}
|
|
|
|
static AtkObject*
|
|
gnomecc_canvas_item_accessible_get_parent (AtkObject *object)
|
|
{
|
|
GnomeccCanvasItemAccessible *item;
|
|
GnomeccCanvas *canvas;
|
|
|
|
item = (GnomeccCanvasItemAccessible *) object;
|
|
canvas = ((EntryInfo *) item->entry->user_data)->canvas;
|
|
|
|
return gtk_widget_get_accessible (GTK_WIDGET (canvas));
|
|
}
|
|
|
|
static AtkStateSet*
|
|
gnomecc_canvas_item_accessible_ref_state_set (AtkObject *object)
|
|
{
|
|
GnomeccCanvasItemAccessible *item;
|
|
GnomeccCanvasPrivate *priv;
|
|
GnomeccCanvas *canvas;
|
|
|
|
item = (GnomeccCanvasItemAccessible *) object;
|
|
canvas = ((EntryInfo *) item->entry->user_data)->canvas;
|
|
priv = GNOMECC_CANVAS_GET_PRIVATE (canvas);
|
|
|
|
if (item->entry == priv->selected)
|
|
atk_state_set_add_state (item->state_set, ATK_STATE_FOCUSED);
|
|
else
|
|
atk_state_set_remove_state (item->state_set, ATK_STATE_FOCUSED);
|
|
|
|
return g_object_ref (item->state_set);
|
|
}
|
|
|
|
static void
|
|
gnomecc_canvas_item_accessible_class_init (AtkObjectClass *class)
|
|
{
|
|
GObjectClass *object_class;
|
|
|
|
accessible_item_parent_class = g_type_class_peek_parent (class);
|
|
|
|
object_class = (GObjectClass *)class;
|
|
|
|
class->get_index_in_parent = gnomecc_canvas_item_accessible_get_index_in_parent;
|
|
class->get_name = gnomecc_canvas_item_accessible_get_name;
|
|
class->get_parent = gnomecc_canvas_item_accessible_get_parent;
|
|
class->ref_state_set = gnomecc_canvas_item_accessible_ref_state_set;
|
|
}
|
|
|
|
static void
|
|
gnomecc_canvas_item_accessible_object_init (GnomeccCanvasItemAccessible *item)
|
|
{
|
|
item->state_set = atk_state_set_new ();
|
|
|
|
atk_state_set_add_state (item->state_set, ATK_STATE_ENABLED);
|
|
atk_state_set_add_state (item->state_set, ATK_STATE_FOCUSABLE);
|
|
atk_state_set_add_state (item->state_set, ATK_STATE_SENSITIVE);
|
|
atk_state_set_add_state (item->state_set, ATK_STATE_SELECTABLE);
|
|
atk_state_set_add_state (item->state_set, ATK_STATE_VISIBLE);
|
|
|
|
item->action_idle_handler = 0;
|
|
}
|
|
|
|
static GType
|
|
gnomecc_canvas_item_accessible_get_type (void)
|
|
{
|
|
static GType type = 0;
|
|
|
|
if (type == 0) {
|
|
static const GTypeInfo info = {
|
|
sizeof (GnomeccCanvasItemAccessibleClass),
|
|
(GBaseInitFunc) NULL, /* base init */
|
|
(GBaseFinalizeFunc) NULL, /* base finalize */
|
|
(GClassInitFunc) gnomecc_canvas_item_accessible_class_init, /* class init */
|
|
(GClassFinalizeFunc) NULL, /* class finalize */
|
|
NULL, /* class data */
|
|
sizeof (GnomeccCanvasItemAccessible), /* instance size */
|
|
0, /* nb preallocs */
|
|
(GInstanceInitFunc) gnomecc_canvas_item_accessible_object_init, /* instance init */
|
|
NULL /* value table */
|
|
};
|
|
|
|
static const GInterfaceInfo atk_component_info = {
|
|
(GInterfaceInitFunc) atk_component_item_interface_init,
|
|
(GInterfaceFinalizeFunc) NULL,
|
|
NULL
|
|
};
|
|
|
|
static const GInterfaceInfo atk_action_info = {
|
|
(GInterfaceInitFunc) atk_action_item_interface_init,
|
|
(GInterfaceFinalizeFunc) NULL,
|
|
NULL
|
|
};
|
|
|
|
type = g_type_register_static (ATK_TYPE_OBJECT,
|
|
"GnomeccCanvasItemAccessible", &info, 0);
|
|
|
|
g_type_add_interface_static (type, ATK_TYPE_COMPONENT,
|
|
&atk_component_info);
|
|
g_type_add_interface_static (type, ATK_TYPE_ACTION,
|
|
&atk_action_info);
|
|
}
|
|
|
|
return type;
|
|
}
|
|
|
|
static gint
|
|
gnomecc_canvas_accessible_get_n_children (AtkObject *accessible)
|
|
{
|
|
GnomeccCanvasPrivate *priv;
|
|
GnomeccCanvas *canvas;
|
|
GtkWidget *widget;
|
|
gint i, count;
|
|
|
|
widget = GTK_ACCESSIBLE (accessible)->widget;
|
|
|
|
if (!widget)
|
|
return 0;
|
|
|
|
canvas = GNOMECC_CANVAS (widget);
|
|
priv = GNOMECC_CANVAS_GET_PRIVATE (canvas);
|
|
count = 0;
|
|
|
|
for (i = 0; i < priv->info->n_categories; i++)
|
|
count += priv->info->categories[i]->n_entries;
|
|
|
|
return count;
|
|
}
|
|
|
|
static ControlCenterEntry*
|
|
gnomecc_canvas_accessible_get_entry (GnomeccCanvas *canvas, gint index)
|
|
{
|
|
GnomeccCanvasPrivate *priv;
|
|
gint i, count;
|
|
|
|
priv = GNOMECC_CANVAS_GET_PRIVATE (canvas);
|
|
count = 0;
|
|
|
|
for (i = 0; i < priv->info->n_categories; i++) {
|
|
if (index >= count &&
|
|
index < count + priv->info->categories[i]->n_entries) {
|
|
return priv->info->categories[i]->entries[index - count];
|
|
} else
|
|
count += priv->info->categories [i]->n_entries;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static AtkObject*
|
|
gnomecc_canvas_accessible_ref_child (AtkObject *accessible, gint index)
|
|
{
|
|
GnomeccCanvasItemAccessible *item;
|
|
ControlCenterEntry *entry;
|
|
GnomeccCanvasPrivate *priv;
|
|
GnomeccCanvas *canvas;
|
|
GtkWidget *widget;
|
|
AtkObject *object;
|
|
|
|
widget = GTK_ACCESSIBLE (accessible)->widget;
|
|
|
|
if (!widget)
|
|
return NULL;
|
|
|
|
canvas = GNOMECC_CANVAS (widget);
|
|
priv = GNOMECC_CANVAS_GET_PRIVATE (canvas);
|
|
entry = gnomecc_canvas_accessible_get_entry (canvas, index);
|
|
|
|
if (!entry)
|
|
return NULL;
|
|
|
|
object = g_hash_table_lookup (priv->accessible_children, &index);
|
|
|
|
if (!object) {
|
|
object = g_object_new (gnomecc_canvas_item_accessible_get_type (), NULL);
|
|
item = (GnomeccCanvasItemAccessible *) object;
|
|
|
|
object->role = ATK_ROLE_ICON;
|
|
item->entry = entry;
|
|
|
|
g_hash_table_insert (priv->accessible_children,
|
|
&((EntryInfo *) entry->user_data)->index, object);
|
|
}
|
|
|
|
return g_object_ref (object);
|
|
}
|
|
|
|
static void
|
|
gnomecc_canvas_accessible_initialize (AtkObject *accessible, gpointer data)
|
|
{
|
|
if (ATK_OBJECT_CLASS (accessible_parent_class)->initialize)
|
|
ATK_OBJECT_CLASS (accessible_parent_class)->initialize (accessible, data);
|
|
|
|
accessible->role = ATK_ROLE_LAYERED_PANE;
|
|
}
|
|
|
|
static void
|
|
gnomecc_canvas_accessible_class_init (AtkObjectClass *class)
|
|
{
|
|
GObjectClass *object_class;
|
|
GtkAccessibleClass *accessible_class;
|
|
|
|
accessible_parent_class = g_type_class_peek_parent (class);
|
|
|
|
object_class = (GObjectClass *)class;
|
|
accessible_class = (GtkAccessibleClass *)class;
|
|
|
|
class->get_n_children = gnomecc_canvas_accessible_get_n_children;
|
|
class->ref_child = gnomecc_canvas_accessible_ref_child;
|
|
class->initialize = gnomecc_canvas_accessible_initialize;
|
|
}
|
|
|
|
static gboolean
|
|
gnomecc_canvas_accessible_add_selection (AtkSelection *selection, gint i)
|
|
{
|
|
GnomeccCanvasPrivate *priv;
|
|
GnomeccCanvas *canvas;
|
|
GtkWidget *widget;
|
|
ControlCenterEntry *entry;
|
|
|
|
widget = GTK_ACCESSIBLE (selection)->widget;
|
|
|
|
if (!widget)
|
|
return FALSE;
|
|
|
|
canvas = GNOMECC_CANVAS (widget);
|
|
priv = GNOMECC_CANVAS_GET_PRIVATE (canvas);
|
|
|
|
entry = gnomecc_canvas_accessible_get_entry (canvas, i);
|
|
select_entry (canvas, entry);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gnomecc_canvas_accessible_clear_selection (AtkSelection *selection)
|
|
{
|
|
GnomeccCanvas *canvas;
|
|
GtkWidget *widget;
|
|
|
|
widget = GTK_ACCESSIBLE (selection)->widget;
|
|
|
|
if (!widget)
|
|
return FALSE;
|
|
|
|
canvas = GNOMECC_CANVAS (widget);
|
|
select_entry (canvas, NULL);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static AtkObject*
|
|
gnomecc_canvas_accessible_ref_selection (AtkSelection *selection, gint i)
|
|
{
|
|
GnomeccCanvasPrivate *priv;
|
|
GnomeccCanvas *canvas;
|
|
GtkWidget *widget;
|
|
|
|
widget = GTK_ACCESSIBLE (selection)->widget;
|
|
|
|
if (!widget)
|
|
return NULL;
|
|
|
|
canvas = GNOMECC_CANVAS (widget);
|
|
priv = GNOMECC_CANVAS_GET_PRIVATE (canvas);
|
|
|
|
if (priv->selected)
|
|
return atk_object_ref_accessible_child (gtk_widget_get_accessible (widget),
|
|
((EntryInfo *)priv->selected->user_data)->index);
|
|
return NULL;
|
|
}
|
|
|
|
static gint
|
|
gnomecc_canvas_accessible_get_selection_count (AtkSelection *selection)
|
|
{
|
|
GnomeccCanvasPrivate *priv;
|
|
GnomeccCanvas *canvas;
|
|
GtkWidget *widget;
|
|
|
|
widget = GTK_ACCESSIBLE (selection)->widget;
|
|
|
|
if (!widget)
|
|
return 0;
|
|
|
|
canvas = GNOMECC_CANVAS (widget);
|
|
priv = GNOMECC_CANVAS_GET_PRIVATE (canvas);
|
|
|
|
return (priv->selected) ? 1 : 0;
|
|
}
|
|
|
|
static gboolean
|
|
gnomecc_canvas_accessible_is_child_selected (AtkSelection *selection, gint i)
|
|
{
|
|
GnomeccCanvasPrivate *priv;
|
|
GnomeccCanvas *canvas;
|
|
GtkWidget *widget;
|
|
|
|
widget = GTK_ACCESSIBLE (selection)->widget;
|
|
|
|
if (!widget)
|
|
return FALSE;
|
|
|
|
canvas = GNOMECC_CANVAS (widget);
|
|
priv = GNOMECC_CANVAS_GET_PRIVATE (canvas);
|
|
|
|
return (priv->selected == gnomecc_canvas_accessible_get_entry (canvas, i));
|
|
}
|
|
|
|
static gboolean
|
|
gnomecc_canvas_accessible_remove_selection (AtkSelection *selection, gint i)
|
|
{
|
|
/* There can be only one item selected */
|
|
return gnomecc_canvas_accessible_clear_selection (selection);
|
|
}
|
|
|
|
static gboolean
|
|
gnomecc_canvas_accessible_select_all_selection (AtkSelection *selection)
|
|
{
|
|
/* This can't happen */
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
gnomecc_canvas_accessible_selection_interface_init (AtkSelectionIface *iface)
|
|
{
|
|
iface->add_selection = gnomecc_canvas_accessible_add_selection;
|
|
iface->clear_selection = gnomecc_canvas_accessible_clear_selection;
|
|
iface->ref_selection = gnomecc_canvas_accessible_ref_selection;
|
|
iface->get_selection_count = gnomecc_canvas_accessible_get_selection_count;
|
|
iface->is_child_selected = gnomecc_canvas_accessible_is_child_selected;
|
|
iface->remove_selection = gnomecc_canvas_accessible_remove_selection;
|
|
iface->select_all_selection = gnomecc_canvas_accessible_select_all_selection;
|
|
}
|
|
|
|
static AtkObject*
|
|
gnomecc_canvas_accessible_ref_accessible_at_point (AtkComponent *component,
|
|
gint x,
|
|
gint y,
|
|
AtkCoordType coord_type)
|
|
{
|
|
GtkWidget *widget;
|
|
GnomeccCanvasPrivate *priv;
|
|
GnomeccCanvas *canvas;
|
|
EntryInfo *entry;
|
|
gint x_pos, y_pos, x_w, y_w;
|
|
gint i, j;
|
|
|
|
widget = GTK_ACCESSIBLE (component)->widget;
|
|
|
|
if (widget == NULL)
|
|
return NULL;
|
|
|
|
canvas = GNOMECC_CANVAS (widget);
|
|
priv = GNOMECC_CANVAS_GET_PRIVATE (canvas);
|
|
|
|
atk_component_get_extents (component, &x_pos, &y_pos, NULL, NULL, coord_type);
|
|
x_w = x - x_pos;
|
|
y_w = y - y_pos;
|
|
|
|
for (i = 0; i < priv->info->n_categories; i++) {
|
|
for (j = 0; j < priv->info->categories[i]->n_entries; j++) {
|
|
entry = (EntryInfo *) priv->info->categories[i]->entries[j];
|
|
|
|
if (x_w > entry->cover->x1 &&
|
|
x_w < entry->cover->x2 &&
|
|
y_w > entry->cover->y1 &&
|
|
y_w < entry->cover->y2)
|
|
return gnomecc_canvas_accessible_ref_child (ATK_OBJECT (component), entry->index);
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
atk_component_interface_init (AtkComponentIface *iface)
|
|
{
|
|
iface->ref_accessible_at_point = gnomecc_canvas_accessible_ref_accessible_at_point;
|
|
}
|
|
|
|
static GType
|
|
gnomecc_canvas_accessible_get_type (void)
|
|
{
|
|
static GType type = 0;
|
|
AtkObjectFactory *factory;
|
|
GType derived_type;
|
|
GTypeQuery query;
|
|
GType derived_atk_type;
|
|
|
|
if (type == 0) {
|
|
static GTypeInfo info = {
|
|
0, /* class size */
|
|
(GBaseInitFunc) NULL, /* base init */
|
|
(GBaseFinalizeFunc) NULL, /* base finalize */
|
|
(GClassInitFunc) gnomecc_canvas_accessible_class_init,
|
|
(GClassFinalizeFunc) NULL, /* class finalize */
|
|
NULL, /* class data */
|
|
0, /* instance size */
|
|
0, /* nb preallocs */
|
|
(GInstanceInitFunc) NULL, /* instance init */
|
|
NULL /* value table */
|
|
};
|
|
|
|
static const GInterfaceInfo atk_component_info = {
|
|
(GInterfaceInitFunc) atk_component_interface_init,
|
|
(GInterfaceFinalizeFunc) NULL,
|
|
NULL
|
|
};
|
|
|
|
static const GInterfaceInfo atk_selection_info = {
|
|
(GInterfaceInitFunc) gnomecc_canvas_accessible_selection_interface_init,
|
|
(GInterfaceFinalizeFunc) NULL,
|
|
NULL
|
|
};
|
|
|
|
derived_type = g_type_parent (GNOMECC_TYPE_CANVAS);
|
|
factory = atk_registry_get_factory (atk_get_default_registry (),
|
|
derived_type);
|
|
|
|
derived_atk_type = atk_object_factory_get_accessible_type (factory);
|
|
g_type_query (derived_atk_type, &query);
|
|
|
|
info.class_size = query.class_size;
|
|
info.instance_size = query.instance_size;
|
|
|
|
type = g_type_register_static (derived_atk_type,
|
|
"GnomeccCanvasAccessible",
|
|
&info, 0);
|
|
|
|
g_type_add_interface_static (type,
|
|
ATK_TYPE_COMPONENT,
|
|
&atk_component_info);
|
|
g_type_add_interface_static (type,
|
|
ATK_TYPE_SELECTION,
|
|
&atk_selection_info);
|
|
}
|
|
|
|
return type;
|
|
}
|
|
|
|
static AtkObject*
|
|
gnomecc_canvas_accessible_new (GObject *object)
|
|
{
|
|
AtkObject *accessible;
|
|
|
|
accessible = g_object_new (gnomecc_canvas_accessible_get_type (), NULL);
|
|
atk_object_initialize (accessible, object);
|
|
|
|
return accessible;
|
|
}
|
|
|
|
static AtkObject*
|
|
gnomecc_canvas_accessible_factory_create_accessible (GObject *object)
|
|
{
|
|
return gnomecc_canvas_accessible_new (object);
|
|
}
|
|
|
|
static void
|
|
gnomecc_canvas_accessible_factory_class_init (AtkObjectFactoryClass *class)
|
|
{
|
|
class->create_accessible = gnomecc_canvas_accessible_factory_create_accessible;
|
|
class->get_accessible_type = gnomecc_canvas_accessible_get_type;
|
|
}
|
|
|
|
static GType
|
|
gnomecc_canvas_accessible_factory_get_type (void)
|
|
{
|
|
static GType type = 0;
|
|
|
|
if (type == 0) {
|
|
static const GTypeInfo info = {
|
|
sizeof (AtkObjectFactoryClass),
|
|
NULL, /* base_init */
|
|
NULL, /* base_finalize */
|
|
(GClassInitFunc) gnomecc_canvas_accessible_factory_class_init,
|
|
NULL, /* class_finalize */
|
|
NULL, /* class_data */
|
|
sizeof (AtkObjectFactory),
|
|
0, /* n_preallocs */
|
|
NULL, NULL
|
|
};
|
|
|
|
type = g_type_register_static (ATK_TYPE_OBJECT_FACTORY,
|
|
"GnomeccCanvasAccessibleFactory",
|
|
&info, 0);
|
|
}
|
|
|
|
return type;
|
|
}
|
|
|
|
static AtkObject*
|
|
gnomecc_canvas_get_accessible (GtkWidget *widget)
|
|
{
|
|
static gboolean already_here = FALSE;
|
|
|
|
if (!already_here) {
|
|
AtkObjectFactory *factory;
|
|
AtkRegistry *registry;
|
|
GType derived_type;
|
|
GType derived_atk_type;
|
|
|
|
already_here = TRUE;
|
|
|
|
derived_type = g_type_parent (GNOMECC_TYPE_CANVAS);
|
|
|
|
registry = atk_get_default_registry ();
|
|
factory = atk_registry_get_factory (registry, derived_type);
|
|
|
|
derived_atk_type = atk_object_factory_get_accessible_type (factory);
|
|
|
|
if (g_type_is_a (derived_atk_type, GTK_TYPE_ACCESSIBLE)) {
|
|
atk_registry_set_factory_type (registry,
|
|
GNOMECC_TYPE_CANVAS,
|
|
gnomecc_canvas_accessible_factory_get_type ());
|
|
}
|
|
}
|
|
|
|
return (* GTK_WIDGET_CLASS (gnomecc_canvas_parent_class)->get_accessible) (widget);
|
|
}
|