/* -*- 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 * Carlos Garnacho Parro */ #include "gnomecc-canvas.h" #include "gnomecc-event-box.h" #include "gnomecc-rounded-rect.h" #include #include #include #include #include #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; }; 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; } 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); 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; 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); 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; priv = GNOMECC_CANVAS_GET_PRIVATE (canvas); gcanvas = GNOME_CANVAS (canvas); 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 ("%s", 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; 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]); } } } 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); }