2008-02-29 Cosimo Cecchi <cosimoc@gnome.org>
[nautilus.git] / libnautilus-private / nautilus-icon-canvas-item.c
blobaa3bfe6fe77f7d4d3b65b0b1aabb2f2209afc112
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
3 /* Nautilus - Icon canvas item class for icon container.
5 * Copyright (C) 2000 Eazel, Inc
7 * Author: Andy Hertzfeld <andy@eazel.com>
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
19 * You should have received a copy of the GNU Library General Public
20 * License along with this library; if not, write to the
21 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22 * Boston, MA 02111-1307, USA.
25 #include <config.h>
26 #include <math.h>
27 #include "nautilus-icon-canvas-item.h"
29 #include <glib/gi18n.h>
31 #include "nautilus-file-utilities.h"
32 #include "nautilus-global-preferences.h"
33 #include "nautilus-icon-private.h"
34 #include <eel/eel-art-extensions.h>
35 #include <eel/eel-gdk-extensions.h>
36 #include <eel/eel-gdk-pixbuf-extensions.h>
37 #include <eel/eel-glib-extensions.h>
38 #include <eel/eel-gnome-extensions.h>
39 #include <eel/eel-graphic-effects.h>
40 #include <eel/eel-gtk-macros.h>
41 #include <eel/eel-pango-extensions.h>
42 #include <eel/eel-string.h>
43 #include <eel/eel-accessibility.h>
44 #include <gdk-pixbuf/gdk-pixbuf.h>
45 #include <gtk/gtksignal.h>
46 #include <gdk/gdk.h>
47 #include <librsvg/rsvg.h>
48 #include <glib/gi18n.h>
49 #include <eel/eel-canvas-util.h>
50 #include <atk/atkimage.h>
51 #include <atk/atkcomponent.h>
52 #include <atk/atknoopobject.h>
53 #include <stdio.h>
54 #include <string.h>
56 #define EMBLEM_SPACING 2
58 /* gap between bottom of icon and start of text box */
59 #define LABEL_OFFSET 1
60 #define LABEL_LINE_SPACING 0
62 #define MAX_TEXT_WIDTH_STANDARD 135
63 #define MAX_TEXT_WIDTH_TIGHTER 80
64 #define MAX_TEXT_WIDTH_BESIDE 90
66 /* Private part of the NautilusIconCanvasItem structure. */
67 struct NautilusIconCanvasItemDetails {
68 /* The image, text, font. */
69 double x, y;
70 GdkPixbuf *pixbuf;
71 GdkPixbuf *rendered_pixbuf;
72 GList *emblem_pixbufs;
73 char *editable_text; /* Text that can be modified by a renaming function */
74 char *additional_text; /* Text that cannot be modifed, such as file size, etc. */
75 GdkPoint *attach_points;
76 int n_attach_points;
78 /* Size of the text at current font. */
79 int text_dx;
80 int text_width;
81 int text_height;
83 /* preview state */
84 guint is_active : 1;
86 /* Highlight state. */
87 guint is_highlighted_for_selection : 1;
88 guint is_highlighted_as_keyboard_focus: 1;
89 guint is_highlighted_for_drop : 1;
90 guint show_stretch_handles : 1;
91 guint is_prelit : 1;
93 guint rendered_is_active : 1;
94 guint rendered_is_highlighted_for_selection : 1;
95 guint rendered_is_highlighted_for_drop : 1;
96 guint rendered_is_prelit : 1;
97 guint rendered_is_focused : 1;
99 guint is_renaming : 1;
101 guint bounds_cached : 1;
103 guint is_visible : 1;
105 GdkRectangle embedded_text_rect;
106 char *embedded_text;
108 /* Cached PangoLayouts. Only used if the icon is visible */
109 PangoLayout *editable_text_layout;
110 PangoLayout *additional_text_layout;
111 PangoLayout *embedded_text_layout;
113 /* Cached rectangle in canvas coordinates */
114 EelIRect canvas_rect;
115 EelIRect text_rect;
116 EelIRect emblem_rect;
118 EelIRect bounds_cache;
120 /* Accessibility bits */
121 GailTextUtil *text_util;
124 /* Object argument IDs. */
125 enum {
126 PROP_0,
127 PROP_EDITABLE_TEXT,
128 PROP_ADDITIONAL_TEXT,
129 PROP_HIGHLIGHTED_FOR_SELECTION,
130 PROP_HIGHLIGHTED_AS_KEYBOARD_FOCUS,
131 PROP_HIGHLIGHTED_FOR_DROP
134 typedef enum {
135 RIGHT_SIDE,
136 BOTTOM_SIDE,
137 LEFT_SIDE,
138 TOP_SIDE
139 } RectangleSide;
141 enum {
142 ACTION_OPEN,
143 ACTION_MENU,
144 LAST_ACTION
147 typedef struct {
148 char *action_descriptions[LAST_ACTION];
149 char *image_description;
150 char *description;
151 } NautilusIconCanvasItemAccessiblePrivate;
153 typedef struct {
154 NautilusIconCanvasItem *item;
155 gint action_number;
156 } NautilusIconCanvasItemAccessibleActionContext;
158 typedef struct {
159 NautilusIconCanvasItem *icon_item;
160 EelIRect icon_rect;
161 RectangleSide side;
162 int position;
163 int index;
164 GList *emblem;
165 } EmblemLayout;
167 static int click_policy_auto_value;
169 /* GtkObject */
170 static void nautilus_icon_canvas_item_class_init (NautilusIconCanvasItemClass *class);
171 static void nautilus_icon_canvas_item_init (NautilusIconCanvasItem *item);
173 /* private */
174 static void draw_or_measure_label_text (NautilusIconCanvasItem *item,
175 GdkDrawable *drawable,
176 gboolean create_mask,
177 EelIRect icon_rect);
178 static void draw_label_text (NautilusIconCanvasItem *item,
179 GdkDrawable *drawable,
180 gboolean create_mask,
181 EelIRect icon_rect);
182 static void measure_label_text (NautilusIconCanvasItem *item);
183 static void get_icon_canvas_rectangle (NautilusIconCanvasItem *item,
184 EelIRect *rect);
185 static void emblem_layout_reset (EmblemLayout *layout,
186 NautilusIconCanvasItem *icon_item,
187 EelIRect icon_rect,
188 gboolean is_rtl);
189 static gboolean emblem_layout_next (EmblemLayout *layout,
190 GdkPixbuf **emblem_pixbuf,
191 EelIRect *emblem_rect,
192 gboolean is_rtl);
193 static void draw_pixbuf (GdkPixbuf *pixbuf,
194 GdkDrawable *drawable,
195 int x,
196 int y);
197 static PangoLayout *get_label_layout (PangoLayout **layout,
198 NautilusIconCanvasItem *item,
199 const char *text);
200 static void draw_label_layout (NautilusIconCanvasItem *item,
201 GdkDrawable *drawable,
202 PangoLayout *layout,
203 gboolean highlight,
204 GdkColor *label_color,
205 int x,
206 int y,
207 GdkGC *gc);
208 static gboolean hit_test_stretch_handle (NautilusIconCanvasItem *item,
209 EelIRect canvas_rect,
210 GtkCornerType *corner);
211 static void draw_embedded_text (NautilusIconCanvasItem *icon_item,
212 GdkDrawable *drawable,
213 int x,
214 int y);
216 static GdkPixbuf *nautilus_icon_canvas_lighten_pixbuf (GdkPixbuf* src, guint lighten_value);
219 static NautilusIconCanvasItemClass *parent_class = NULL;
220 static gpointer accessible_parent_class = NULL;
222 static GQuark accessible_private_data_quark = 0;
224 static const char *nautilus_icon_canvas_item_accessible_action_names[] = {
225 "open",
226 "menu",
227 NULL
230 static const char *nautilus_icon_canvas_item_accessible_action_descriptions[] = {
231 "Open item",
232 "Popup context menu",
233 NULL
237 /* Object initialization function for the icon item. */
238 static void
239 nautilus_icon_canvas_item_init (NautilusIconCanvasItem *icon_item)
241 static gboolean setup_auto_enums = FALSE;
243 if (!setup_auto_enums) {
244 eel_preferences_add_auto_enum
245 (NAUTILUS_PREFERENCES_CLICK_POLICY,
246 &click_policy_auto_value);
247 setup_auto_enums = TRUE;
250 icon_item->details = G_TYPE_INSTANCE_GET_PRIVATE ((icon_item), NAUTILUS_TYPE_ICON_CANVAS_ITEM, NautilusIconCanvasItemDetails);
251 nautilus_icon_canvas_item_invalidate_label_size (icon_item);
254 static void
255 nautilus_icon_canvas_item_finalize (GObject *object)
257 NautilusIconCanvasItemDetails *details;
259 g_return_if_fail (NAUTILUS_IS_ICON_CANVAS_ITEM (object));
261 details = NAUTILUS_ICON_CANVAS_ITEM (object)->details;
263 if (details->pixbuf != NULL) {
264 g_object_unref (details->pixbuf);
267 if (details->text_util != NULL) {
268 g_object_unref (details->text_util);
271 eel_gdk_pixbuf_list_free (details->emblem_pixbufs);
272 g_free (details->editable_text);
273 g_free (details->additional_text);
274 g_free (details->attach_points);
276 if (details->rendered_pixbuf != NULL) {
277 g_object_unref (details->rendered_pixbuf);
280 if (details->editable_text_layout != NULL) {
281 g_object_unref (details->editable_text_layout);
284 if (details->additional_text_layout != NULL) {
285 g_object_unref (details->additional_text_layout);
288 if (details->embedded_text_layout != NULL) {
289 g_object_unref (details->embedded_text_layout);
292 g_free (details->embedded_text);
294 EEL_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
297 /* Currently we require pixbufs in this format (for hit testing).
298 * Perhaps gdk-pixbuf will be changed so it can do the hit testing
299 * and we won't have this requirement any more.
301 static gboolean
302 pixbuf_is_acceptable (GdkPixbuf *pixbuf)
304 return gdk_pixbuf_get_colorspace (pixbuf) == GDK_COLORSPACE_RGB
305 && ((!gdk_pixbuf_get_has_alpha (pixbuf)
306 && gdk_pixbuf_get_n_channels (pixbuf) == 3)
307 || (gdk_pixbuf_get_has_alpha (pixbuf)
308 && gdk_pixbuf_get_n_channels (pixbuf) == 4))
309 && gdk_pixbuf_get_bits_per_sample (pixbuf) == 8;
312 static void
313 nautilus_icon_canvas_item_invalidate_bounds_cache (NautilusIconCanvasItem *item)
315 item->details->bounds_cached = FALSE;
318 /* invalidate the text width and height cached in the item details. */
319 void
320 nautilus_icon_canvas_item_invalidate_label_size (NautilusIconCanvasItem *item)
322 nautilus_icon_canvas_item_invalidate_bounds_cache (item);
323 item->details->text_width = -1;
324 item->details->text_height = -1;
325 if (item->details->editable_text_layout != NULL) {
326 g_object_unref (item->details->editable_text_layout);
327 item->details->editable_text_layout = NULL;
329 if (item->details->additional_text_layout != NULL) {
330 g_object_unref (item->details->additional_text_layout);
331 item->details->additional_text_layout = NULL;
335 /* Set property handler for the icon item. */
336 static void
337 nautilus_icon_canvas_item_set_property (GObject *object,
338 guint property_id,
339 const GValue *value,
340 GParamSpec *pspec)
342 NautilusIconCanvasItem *item;
343 NautilusIconCanvasItemDetails *details;
345 item = NAUTILUS_ICON_CANVAS_ITEM (object);
346 details = item->details;
348 switch (property_id) {
350 case PROP_EDITABLE_TEXT:
351 if (eel_strcmp (details->editable_text,
352 g_value_get_string (value)) == 0) {
353 return;
356 g_free (details->editable_text);
357 details->editable_text = g_strdup (g_value_get_string (value));
358 if (details->text_util) {
359 AtkObject *accessible;
361 gail_text_util_text_setup (details->text_util,
362 details->editable_text);
363 accessible = eel_accessibility_get_atk_object (item);
364 g_object_notify (G_OBJECT(accessible), "accessible-name");
367 nautilus_icon_canvas_item_invalidate_label_size (item);
368 break;
370 case PROP_ADDITIONAL_TEXT:
371 if (eel_strcmp (details->additional_text,
372 g_value_get_string (value)) == 0) {
373 return;
376 g_free (details->additional_text);
377 details->additional_text = g_strdup (g_value_get_string (value));
379 nautilus_icon_canvas_item_invalidate_label_size (item);
380 break;
382 case PROP_HIGHLIGHTED_FOR_SELECTION:
383 if (!details->is_highlighted_for_selection == !g_value_get_boolean (value)) {
384 return;
386 details->is_highlighted_for_selection = g_value_get_boolean (value);
387 break;
389 case PROP_HIGHLIGHTED_AS_KEYBOARD_FOCUS:
390 if (!details->is_highlighted_as_keyboard_focus == !g_value_get_boolean (value)) {
391 return;
393 details->is_highlighted_as_keyboard_focus = g_value_get_boolean (value);
395 if (details->is_highlighted_as_keyboard_focus) {
396 AtkObject *atk_object = eel_accessibility_for_object (object);
397 atk_focus_tracker_notify (atk_object);
399 break;
401 case PROP_HIGHLIGHTED_FOR_DROP:
402 if (!details->is_highlighted_for_drop == !g_value_get_boolean (value)) {
403 return;
405 details->is_highlighted_for_drop = g_value_get_boolean (value);
406 break;
408 default:
409 g_warning ("nautilus_icons_view_item_item_set_arg on unknown argument");
410 return;
413 eel_canvas_item_request_update (EEL_CANVAS_ITEM (object));
416 /* Get property handler for the icon item */
417 static void
418 nautilus_icon_canvas_item_get_property (GObject *object,
419 guint property_id,
420 GValue *value,
421 GParamSpec *pspec)
423 NautilusIconCanvasItemDetails *details;
425 details = NAUTILUS_ICON_CANVAS_ITEM (object)->details;
427 switch (property_id) {
429 case PROP_EDITABLE_TEXT:
430 g_value_set_string (value, details->editable_text);
431 break;
433 case PROP_ADDITIONAL_TEXT:
434 g_value_set_string (value, details->additional_text);
435 break;
437 case PROP_HIGHLIGHTED_FOR_SELECTION:
438 g_value_set_boolean (value, details->is_highlighted_for_selection);
439 break;
441 case PROP_HIGHLIGHTED_AS_KEYBOARD_FOCUS:
442 g_value_set_boolean (value, details->is_highlighted_as_keyboard_focus);
443 break;
445 case PROP_HIGHLIGHTED_FOR_DROP:
446 g_value_set_boolean (value, details->is_highlighted_for_drop);
447 break;
449 default:
450 g_warning ("invalid property %d", property_id);
451 break;
455 GdkPixmap *
456 nautilus_icon_canvas_item_get_image (NautilusIconCanvasItem *item,
457 GdkBitmap **mask,
458 GdkColormap *colormap)
460 GdkPixmap *pixmap;
461 EelCanvas *canvas;
462 GdkScreen *screen;
463 GdkGC *gc;
464 int width, height;
465 int item_offset_x, item_offset_y;
466 EelIRect icon_rect;
467 EelIRect emblem_rect;
468 GdkPixbuf *pixbuf;
469 GdkPixbuf *emblem_pixbuf;
470 EmblemLayout emblem_layout;
471 double item_x, item_y;
472 gboolean is_rtl;
473 cairo_t *cr;
475 g_return_val_if_fail (NAUTILUS_IS_ICON_CANVAS_ITEM (item), NULL);
477 canvas = EEL_CANVAS_ITEM (item)->canvas;
478 screen = gdk_colormap_get_screen (colormap);
480 /* Assume we're updated so canvas item data is right */
482 /* Calculate the offset from the top-left corner of the
483 new image to the item position (where the pixmap is placed) */
484 eel_canvas_world_to_window (canvas,
485 item->details->x, item->details->y,
486 &item_x, &item_y);
488 item_offset_x = item_x - EEL_CANVAS_ITEM (item)->x1;
489 item_offset_y = item_y - EEL_CANVAS_ITEM (item)->y1;
491 /* Calculate the width of the item */
492 width = EEL_CANVAS_ITEM (item)->x2 - EEL_CANVAS_ITEM (item)->x1;
493 height = EEL_CANVAS_ITEM (item)->y2 - EEL_CANVAS_ITEM (item)->y1;
495 pixmap = gdk_pixmap_new (gdk_screen_get_root_window (screen),
496 width, height,
497 gdk_colormap_get_visual (colormap)->depth);
498 gdk_drawable_set_colormap (GDK_DRAWABLE (pixmap), colormap);
500 pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
501 TRUE,
502 gdk_pixbuf_get_bits_per_sample (item->details->pixbuf),
503 width, height);
504 gdk_pixbuf_fill (pixbuf, 0x00000000);
506 gdk_pixbuf_composite (item->details->pixbuf, pixbuf,
507 item_offset_x, item_offset_y,
508 gdk_pixbuf_get_width (item->details->pixbuf),
509 gdk_pixbuf_get_height (item->details->pixbuf),
510 item_offset_x, item_offset_y, 1.0, 1.0,
511 GDK_INTERP_BILINEAR, 255);
513 icon_rect.x0 = item_offset_x;
514 icon_rect.y0 = item_offset_y;
515 icon_rect.x1 = item_offset_x + gdk_pixbuf_get_width (item->details->pixbuf);
516 icon_rect.y1 = item_offset_y + gdk_pixbuf_get_height (item->details->pixbuf);
519 is_rtl = nautilus_icon_container_is_layout_rtl (NAUTILUS_ICON_CONTAINER (canvas));
521 emblem_layout_reset (&emblem_layout, item, icon_rect, is_rtl);
522 while (emblem_layout_next (&emblem_layout, &emblem_pixbuf, &emblem_rect, is_rtl)) {
523 gdk_pixbuf_composite (emblem_pixbuf, pixbuf,
524 emblem_rect.x0, emblem_rect.y0,
525 gdk_pixbuf_get_width (emblem_pixbuf),
526 gdk_pixbuf_get_height (emblem_pixbuf),
527 emblem_rect.x0, emblem_rect.y0,
528 1.0, 1.0,
529 GDK_INTERP_BILINEAR, 255);
532 /* clear the pixmap */
533 cr = gdk_cairo_create (pixmap);
534 cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
535 cairo_paint (cr);
536 cairo_destroy (cr);
538 gc = gdk_gc_new (pixmap);
539 gdk_draw_pixbuf (pixmap, gc, pixbuf,
540 0, 0, 0, 0,
541 gdk_pixbuf_get_width (pixbuf), gdk_pixbuf_get_height (pixbuf),
542 GDK_RGB_DITHER_NORMAL,
543 0, 0);
544 g_object_unref (gc);
546 *mask = gdk_pixmap_new (gdk_screen_get_root_window (screen),
547 width, height,
549 gc = gdk_gc_new (*mask);
550 gdk_draw_rectangle (*mask, gc,
551 TRUE,
552 0, 0,
553 width, height);
554 g_object_unref (gc);
556 gdk_pixbuf_render_threshold_alpha (pixbuf, *mask,
557 0, 0, 0, 0,
558 gdk_pixbuf_get_width (pixbuf), gdk_pixbuf_get_height (pixbuf),
559 128);
561 draw_embedded_text (item, GDK_DRAWABLE (pixmap),
562 item_offset_x, item_offset_y);
564 draw_label_text (item, GDK_DRAWABLE (pixmap), FALSE, icon_rect);
565 draw_label_text (item, GDK_DRAWABLE (*mask), TRUE, icon_rect);
567 gdk_pixbuf_unref (pixbuf);
569 return pixmap;
572 void
573 nautilus_icon_canvas_item_set_image (NautilusIconCanvasItem *item,
574 GdkPixbuf *image)
576 NautilusIconCanvasItemDetails *details;
578 g_return_if_fail (NAUTILUS_IS_ICON_CANVAS_ITEM (item));
579 g_return_if_fail (image == NULL || pixbuf_is_acceptable (image));
581 details = item->details;
582 if (details->pixbuf == image) {
583 return;
586 if (image != NULL) {
587 g_object_ref (image);
589 if (details->pixbuf != NULL) {
590 g_object_unref (details->pixbuf);
592 if (details->rendered_pixbuf != NULL) {
593 g_object_unref (details->rendered_pixbuf);
594 details->rendered_pixbuf = NULL;
597 details->pixbuf = image;
599 nautilus_icon_canvas_item_invalidate_bounds_cache (item);
600 eel_canvas_item_request_update (EEL_CANVAS_ITEM (item));
603 void
604 nautilus_icon_canvas_item_set_emblems (NautilusIconCanvasItem *item,
605 GList *emblem_pixbufs)
607 GList *p;
609 g_return_if_fail (NAUTILUS_IS_ICON_CANVAS_ITEM (item));
611 g_assert (item->details->emblem_pixbufs != emblem_pixbufs || emblem_pixbufs == NULL);
613 /* The case where the emblems are identical is fairly common,
614 * so lets take the time to check for it.
616 if (eel_g_list_equal (item->details->emblem_pixbufs, emblem_pixbufs)) {
617 return;
620 /* Check if they are acceptable. */
621 for (p = emblem_pixbufs; p != NULL; p = p->next) {
622 g_return_if_fail (pixbuf_is_acceptable (p->data));
625 /* Take in the new list of emblems. */
626 eel_gdk_pixbuf_list_ref (emblem_pixbufs);
627 eel_gdk_pixbuf_list_free (item->details->emblem_pixbufs);
628 item->details->emblem_pixbufs = g_list_copy (emblem_pixbufs);
630 nautilus_icon_canvas_item_invalidate_bounds_cache (item);
631 eel_canvas_item_request_update (EEL_CANVAS_ITEM (item));
634 void
635 nautilus_icon_canvas_item_set_attach_points (NautilusIconCanvasItem *item,
636 GdkPoint *attach_points,
637 int n_attach_points)
639 g_free (item->details->attach_points);
640 item->details->attach_points = NULL;
641 item->details->n_attach_points = 0;
643 if (attach_points != NULL && n_attach_points != 0) {
644 item->details->attach_points = g_memdup (attach_points, n_attach_points * sizeof (GdkPoint));
645 item->details->n_attach_points = n_attach_points;
648 nautilus_icon_canvas_item_invalidate_bounds_cache (item);
651 void
652 nautilus_icon_canvas_item_set_embedded_text_rect (NautilusIconCanvasItem *item,
653 const GdkRectangle *text_rect)
655 item->details->embedded_text_rect = *text_rect;
657 nautilus_icon_canvas_item_invalidate_bounds_cache (item);
658 eel_canvas_item_request_update (EEL_CANVAS_ITEM (item));
661 void
662 nautilus_icon_canvas_item_set_embedded_text (NautilusIconCanvasItem *item,
663 const char *text)
665 g_free (item->details->embedded_text);
666 item->details->embedded_text = g_strdup (text);
668 if (item->details->embedded_text_layout != NULL) {
669 if (text != NULL) {
670 pango_layout_set_text (item->details->embedded_text_layout, text, -1);
671 } else {
672 pango_layout_set_text (item->details->embedded_text_layout, "", -1);
676 eel_canvas_item_request_update (EEL_CANVAS_ITEM (item));
680 /* Recomputes the bounding box of a icon canvas item.
681 * This is a generic implementation that could be used for any canvas item
682 * class, it has no assumptions about how the item is used.
684 static void
685 recompute_bounding_box (NautilusIconCanvasItem *icon_item,
686 double i2w_dx, double i2w_dy)
688 /* The bounds stored in the item is the same as what get_bounds
689 * returns, except it's in canvas coordinates instead of the item's
690 * parent's coordinates.
693 EelCanvasItem *item;
694 EelDPoint top_left, bottom_right;
696 item = EEL_CANVAS_ITEM (icon_item);
698 eel_canvas_item_get_bounds (item,
699 &top_left.x, &top_left.y,
700 &bottom_right.x, &bottom_right.y);
702 top_left.x += i2w_dx;
703 top_left.y += i2w_dy;
704 bottom_right.x += i2w_dx;
705 bottom_right.y += i2w_dy;
706 eel_canvas_w2c_d (item->canvas,
707 top_left.x, top_left.y,
708 &item->x1, &item->y1);
709 eel_canvas_w2c_d (item->canvas,
710 bottom_right.x, bottom_right.y,
711 &item->x2, &item->y2);
714 static EelIRect
715 compute_text_rectangle (const NautilusIconCanvasItem *item,
716 EelIRect icon_rectangle,
717 gboolean canvas_coords)
719 EelIRect text_rectangle;
720 double pixels_per_unit;
721 double text_width, text_height, text_dx;
723 pixels_per_unit = EEL_CANVAS_ITEM (item)->canvas->pixels_per_unit;
724 if (canvas_coords) {
725 text_width = item->details->text_width;
726 text_height = item->details->text_height;
727 text_dx = item->details->text_dx;
728 } else {
729 text_width = item->details->text_width / pixels_per_unit;
730 text_height = item->details->text_height / pixels_per_unit;
731 text_dx = item->details->text_dx / pixels_per_unit;
734 if (NAUTILUS_ICON_CONTAINER (EEL_CANVAS_ITEM (item)->canvas)->details->label_position == NAUTILUS_ICON_LABEL_POSITION_BESIDE) {
735 if (!nautilus_icon_container_is_layout_rtl (NAUTILUS_ICON_CONTAINER (EEL_CANVAS_ITEM (item)->canvas))) {
736 text_rectangle.x0 = icon_rectangle.x1;
737 text_rectangle.x1 = text_rectangle.x0 + text_dx + text_width;
738 } else {
739 text_rectangle.x1 = icon_rectangle.x0;
740 text_rectangle.x0 = text_rectangle.x1 - text_dx - text_width;
742 text_rectangle.y0 = (icon_rectangle.y0 + icon_rectangle.y1) / 2- (int) text_height / 2;
743 text_rectangle.y1 = text_rectangle.y0 + text_height + LABEL_OFFSET / pixels_per_unit;
744 } else {
745 text_rectangle.x0 = (icon_rectangle.x0 + icon_rectangle.x1) / 2 - (int) text_width / 2;
746 text_rectangle.y0 = icon_rectangle.y1;
747 text_rectangle.x1 = text_rectangle.x0 + text_width;
748 text_rectangle.y1 = text_rectangle.y0 + text_height + LABEL_OFFSET / pixels_per_unit;
751 return text_rectangle;
754 static EelIRect
755 get_current_canvas_bounds (EelCanvasItem *item)
757 EelIRect bounds;
759 g_return_val_if_fail (EEL_IS_CANVAS_ITEM (item), eel_irect_empty);
761 bounds.x0 = item->x1;
762 bounds.y0 = item->y1;
763 bounds.x1 = item->x2;
764 bounds.y1 = item->y2;
766 return bounds;
769 void
770 nautilus_icon_canvas_item_update_bounds (NautilusIconCanvasItem *item,
771 double i2w_dx, double i2w_dy)
773 EelIRect before, after, emblem_rect;
774 EmblemLayout emblem_layout;
775 EelCanvasItem *canvas_item;
776 GdkPixbuf *emblem_pixbuf;
777 gboolean is_rtl;
779 canvas_item = EEL_CANVAS_ITEM (item);
781 /* Compute new bounds. */
782 before = get_current_canvas_bounds (canvas_item);
783 recompute_bounding_box (item, i2w_dx, i2w_dy);
784 after = get_current_canvas_bounds (canvas_item);
786 /* If the bounds didn't change, we are done. */
787 if (eel_irect_equal (before, after)) {
788 return;
791 is_rtl = nautilus_icon_container_is_layout_rtl (NAUTILUS_ICON_CONTAINER (canvas_item->canvas));
793 /* Update canvas and text rect cache */
794 get_icon_canvas_rectangle (item, &item->details->canvas_rect);
795 item->details->text_rect = compute_text_rectangle (item, item->details->canvas_rect, TRUE);
797 /* Update emblem rect cache */
798 item->details->emblem_rect.x0 = 0;
799 item->details->emblem_rect.x1 = 0;
800 item->details->emblem_rect.y0 = 0;
801 item->details->emblem_rect.y1 = 0;
802 emblem_layout_reset (&emblem_layout, item, item->details->canvas_rect, is_rtl);
803 while (emblem_layout_next (&emblem_layout, &emblem_pixbuf, &emblem_rect, is_rtl)) {
804 eel_irect_union (&item->details->emblem_rect, &item->details->emblem_rect, &emblem_rect);
807 /* queue a redraw. */
808 eel_canvas_request_redraw (canvas_item->canvas,
809 before.x0, before.y0,
810 before.x1 + 1, before.y1 + 1);
813 /* Update handler for the icon canvas item. */
814 static void
815 nautilus_icon_canvas_item_update (EelCanvasItem *item,
816 double i2w_dx, double i2w_dy,
817 gint flags)
819 nautilus_icon_canvas_item_update_bounds (NAUTILUS_ICON_CANVAS_ITEM (item), i2w_dx, i2w_dy);
821 eel_canvas_item_request_redraw (EEL_CANVAS_ITEM (item));
823 EEL_CALL_PARENT (EEL_CANVAS_ITEM_CLASS, update,
824 (item, i2w_dx, i2w_dy, flags));
827 /* Rendering */
828 static gboolean
829 in_single_click_mode (void)
831 return click_policy_auto_value == NAUTILUS_CLICK_POLICY_SINGLE;
835 /* Utility routine to create a rectangle with rounded corners.
836 * This could possibly move to Eel as a general purpose routine.
838 static void
839 make_round_rect (cairo_t *cr,
840 double x,
841 double y,
842 double width,
843 double height,
844 double radius)
846 double cx, cy;
848 width -= 2 * radius;
849 height -= 2 * radius;
851 cairo_move_to (cr, x + radius, y);
853 cairo_rel_line_to (cr, width, 0.0);
855 cairo_get_current_point (cr, &cx, &cy);
856 cairo_arc (cr, cx, cy + radius, radius, 3.0 * G_PI_2, 0);
858 cairo_rel_line_to (cr, 0.0, height);
860 cairo_get_current_point (cr, &cx, &cy);
861 cairo_arc (cr, cx - radius, cy, radius, 0, G_PI_2);
863 cairo_rel_line_to (cr, - width, 0.0);
865 cairo_get_current_point (cr, &cx, &cy);
866 cairo_arc (cr, cx, cy - radius, radius, G_PI_2, G_PI);
868 cairo_rel_line_to (cr, 0.0, -height);
870 cairo_get_current_point (cr, &cx, &cy);
871 cairo_arc (cr, cx + radius, cy, radius, G_PI, 3.0 * G_PI_2);
873 cairo_close_path (cr);
876 static void
877 draw_frame (NautilusIconCanvasItem *item,
878 GdkDrawable *drawable,
879 guint color,
880 gboolean create_mask,
881 int x,
882 int y,
883 int width,
884 int height)
886 NautilusIconContainer *container;
887 cairo_t *cr;
889 container = NAUTILUS_ICON_CONTAINER (EEL_CANVAS_ITEM (item)->canvas);
891 /* Get a cairo context */
892 cr = gdk_cairo_create (drawable);
894 /* Set the rounded rect clip region. Magic rounding value taken
895 * from old code.
897 make_round_rect (cr, x, y, width, height, 5);
899 if (create_mask) {
900 /* Dunno how to do this with cairo...
901 * It used to threshold the rendering so that the
902 * bitmask didn't show white where alpha < 0.5
906 cairo_set_source_rgba (cr,
907 EEL_RGBA_COLOR_GET_R (color) / 255.0,
908 EEL_RGBA_COLOR_GET_G (color) / 255.0,
909 EEL_RGBA_COLOR_GET_B (color) / 255.0,
910 EEL_RGBA_COLOR_GET_A (color) / 255.0);
912 /* Paint into drawable now that we have set up the color and opacity */
913 cairo_fill (cr);
915 /* Clean up now that drawing is complete */
916 cairo_destroy (cr);
919 /* Keep these for a bit while we work on performance of draw_or_measure_label_text. */
921 #define PERFORMANCE_TEST_DRAW_DISABLE
922 #define PERFORMANCE_TEST_MEASURE_DISABLE
925 /* This gets the size of the layout from the position of the layout.
926 * This means that if the layout is right aligned we get the full width
927 * of the layout, not just the width of the text snippet on the right side
929 static void
930 layout_get_full_size (PangoLayout *layout,
931 int *width,
932 int *height,
933 int *dx)
935 PangoRectangle logical_rect;
936 int total_width;
938 pango_layout_get_extents (layout, NULL, &logical_rect);
939 *width = (logical_rect.width + PANGO_SCALE / 2) / PANGO_SCALE;
940 total_width = (logical_rect.x + logical_rect.width + PANGO_SCALE / 2) / PANGO_SCALE;
941 *dx = total_width - *width;
942 *height = (logical_rect.height + PANGO_SCALE / 2) / PANGO_SCALE;
946 static void
947 draw_or_measure_label_text (NautilusIconCanvasItem *item,
948 GdkDrawable *drawable,
949 gboolean create_mask,
950 EelIRect icon_rect)
952 NautilusIconCanvasItemDetails *details;
953 NautilusIconContainer *container;
954 gint editable_height, editable_width, editable_dx;
955 gint additional_height, additional_width, additional_dx;
956 EelCanvasItem *canvas_item;
957 PangoLayout *editable_layout;
958 PangoLayout *additional_layout;
959 GdkColor *label_color;
960 int icon_width;
961 gboolean have_editable, have_additional, needs_highlight, needs_frame, prelight_label, is_rtl_label_beside;
962 int max_text_width;
963 int x;
964 GdkGC *gc;
965 EelIRect text_rect;
966 int text_back_padding_x, text_back_padding_y;
968 icon_width = 0;
969 gc = NULL;
971 details = item->details;
972 needs_highlight = details->is_highlighted_for_selection || details->is_highlighted_for_drop;
974 have_editable = details->editable_text != NULL && details->editable_text[0] != '\0';
975 have_additional = details->additional_text != NULL && details->additional_text[0] != '\0';
977 /* No font or no text, then do no work. */
978 if (!have_editable && !have_additional) {
979 details->text_height = 0;
980 details->text_width = 0;
981 return;
984 #if (defined PERFORMANCE_TEST_MEASURE_DISABLE && defined PERFORMANCE_TEST_DRAW_DISABLE)
985 /* don't do any drawing and fake out the width */
986 details->text_width = 80;
987 details->text_height = 20;
988 return;
989 #endif
991 #ifdef PERFORMANCE_TEST_MEASURE_DISABLE
992 if (drawable == NULL) {
993 /* fake out the width */
994 details->text_width = 80;
995 details->text_height = 20;
996 return;
998 #endif
1000 #ifdef PERFORMANCE_TEST_DRAW_DISABLE
1001 if (drawable != NULL) {
1002 return;
1004 #endif
1006 canvas_item = EEL_CANVAS_ITEM (item);
1007 if (drawable != NULL) {
1008 icon_width = details->pixbuf == NULL ? 0 : gdk_pixbuf_get_width (details->pixbuf);
1011 editable_width = 0;
1012 editable_height = 0;
1013 editable_dx = 0;
1014 additional_width = 0;
1015 additional_height = 0;
1016 additional_dx = 0;
1018 max_text_width = floor (nautilus_icon_canvas_item_get_max_text_width (item));
1020 container = NAUTILUS_ICON_CONTAINER (EEL_CANVAS_ITEM (item)->canvas);
1021 editable_layout = NULL;
1022 additional_layout = NULL;
1024 if (have_editable) {
1025 editable_layout = get_label_layout (&details->editable_text_layout, item, details->editable_text);
1026 layout_get_full_size (editable_layout, &editable_width, &editable_height, &editable_dx);
1029 if (have_additional) {
1030 additional_layout = get_label_layout (&details->additional_text_layout, item, details->additional_text);
1031 layout_get_full_size (additional_layout, &additional_width, &additional_height, &additional_dx);
1034 if (editable_width > additional_width) {
1035 details->text_width = editable_width;
1036 details->text_dx = editable_dx;
1037 } else {
1038 details->text_width = additional_width;
1039 details->text_dx = additional_dx;
1042 if (have_additional) {
1043 details->text_height = editable_height + LABEL_LINE_SPACING + additional_height;
1044 } else {
1045 details->text_height = editable_height;
1048 /* add some extra space for highlighting even when we don't highlight so things won't move */
1049 text_back_padding_x = 4;
1050 text_back_padding_y = 1;
1052 details->text_height += text_back_padding_y*2; /* extra slop for nicer highlighting */
1053 details->text_width += text_back_padding_x*2; /* extra to make it look nicer */
1055 /* if measuring, we are done */
1056 if (!drawable) {
1057 if (editable_layout) {
1058 g_object_unref (editable_layout);
1061 if (additional_layout) {
1062 g_object_unref (additional_layout);
1065 return;
1068 text_rect = compute_text_rectangle (item, icon_rect, TRUE);
1070 is_rtl_label_beside = nautilus_icon_container_is_layout_rtl (container) &&
1071 container->details->label_position == NAUTILUS_ICON_LABEL_POSITION_BESIDE;
1073 /* if the icon is highlighted, do some set-up */
1074 if (needs_highlight && !details->is_renaming &&
1075 details->text_width > 0 && details->text_height > 0) {
1076 draw_frame (item,
1077 drawable,
1078 GTK_WIDGET_HAS_FOCUS (GTK_WIDGET (container)) ? container->details->highlight_color_rgba : container->details->active_color_rgba,
1079 create_mask,
1080 is_rtl_label_beside ? text_rect.x0 + item->details->text_dx : text_rect.x0,
1081 text_rect.y0,
1082 is_rtl_label_beside ? text_rect.x1 - text_rect.x0 - item->details->text_dx : text_rect.x1 - text_rect.x0,
1083 text_rect.y1 - text_rect.y0);
1086 if (container->details->label_position == NAUTILUS_ICON_LABEL_POSITION_BESIDE) {
1087 x = text_rect.x0 + 2;
1088 } else {
1089 x = text_rect.x0 + ((text_rect.x1 - text_rect.x0) - max_text_width) / 2;
1092 if (have_editable) {
1093 gtk_widget_style_get (GTK_WIDGET (container),
1094 "frame_text", &needs_frame,
1095 "activate_prelight_icon_label", &prelight_label,
1096 NULL);
1097 if (needs_frame && !needs_highlight && details->text_width > 0 && details->text_height > 0) {
1098 if (!(prelight_label && item->details->is_prelit)) {
1099 draw_frame (item,
1100 drawable,
1101 container->details->normal_color_rgba,
1102 create_mask,
1103 text_rect.x0,
1104 text_rect.y0,
1105 text_rect.x1 - text_rect.x0,
1106 text_rect.y1 - text_rect.y0);
1107 } else {
1108 draw_frame (item,
1109 drawable,
1110 container->details->prelight_color_rgba,
1111 create_mask,
1112 text_rect.x0,
1113 text_rect.y0,
1114 text_rect.x1 - text_rect.x0,
1115 text_rect.y1 - text_rect.y0);
1119 gc = nautilus_icon_container_get_label_color_and_gc
1120 (NAUTILUS_ICON_CONTAINER (canvas_item->canvas),
1121 &label_color, TRUE, needs_highlight,
1122 prelight_label & item->details->is_prelit);
1124 draw_label_layout (item, drawable,
1125 editable_layout, needs_highlight,
1126 label_color,
1128 text_rect.y0 + text_back_padding_y, gc);
1131 if (have_additional) {
1132 gc = nautilus_icon_container_get_label_color_and_gc
1133 (NAUTILUS_ICON_CONTAINER (canvas_item->canvas),
1134 &label_color, FALSE, needs_highlight,
1135 FALSE);
1137 draw_label_layout (item, drawable,
1138 additional_layout, needs_highlight,
1139 label_color,
1141 text_rect.y0 + editable_height + LABEL_LINE_SPACING + text_back_padding_y, gc);
1144 if (!create_mask && item->details->is_highlighted_as_keyboard_focus) {
1145 gtk_paint_focus (GTK_WIDGET (EEL_CANVAS_ITEM (item)->canvas)->style,
1146 drawable,
1147 needs_highlight ? GTK_STATE_SELECTED : GTK_STATE_NORMAL,
1148 NULL,
1149 GTK_WIDGET (EEL_CANVAS_ITEM (item)->canvas),
1150 "icon-container",
1151 text_rect.x0,
1152 text_rect.y0,
1153 text_rect.x1 - text_rect.x0,
1154 text_rect.y1 - text_rect.y0);
1157 if (editable_layout) {
1158 g_object_unref (editable_layout);
1161 if (additional_layout) {
1162 g_object_unref (additional_layout);
1166 static void
1167 measure_label_text (NautilusIconCanvasItem *item)
1169 EelIRect rect = {0, };
1171 /* check to see if the cached values are still valid; if so, there's
1172 * no work necessary
1175 if (item->details->text_width >= 0 && item->details->text_height >= 0) {
1176 return;
1179 draw_or_measure_label_text (item, NULL, FALSE, rect);
1182 static void
1183 draw_label_text (NautilusIconCanvasItem *item, GdkDrawable *drawable,
1184 gboolean create_mask, EelIRect icon_rect)
1186 draw_or_measure_label_text (item, drawable, create_mask, icon_rect);
1189 void
1190 nautilus_icon_canvas_item_set_is_visible (NautilusIconCanvasItem *item,
1191 gboolean visible)
1193 if (item->details->is_visible == visible)
1194 return;
1196 item->details->is_visible = visible;
1198 if (!visible) {
1199 if (item->details->editable_text_layout) {
1200 g_object_unref (item->details->editable_text_layout);
1201 item->details->editable_text_layout = NULL;
1203 if (item->details->additional_text_layout) {
1204 g_object_unref (item->details->additional_text_layout);
1205 item->details->additional_text_layout = NULL;
1207 if (item->details->embedded_text_layout) {
1208 g_object_unref (item->details->embedded_text_layout);
1209 item->details->embedded_text_layout = NULL;
1215 static GdkPixbuf *
1216 get_knob_pixbuf (void)
1218 GdkPixbuf *knob_pixbuf;
1219 char *knob_filename;
1221 knob_pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (),
1222 "stock-nautilus-knob",
1223 8, 0, NULL);
1224 if (!knob_pixbuf) {
1225 knob_filename = nautilus_pixmap_file ("knob.png");
1226 knob_pixbuf = gdk_pixbuf_new_from_file (knob_filename, NULL);
1227 g_free (knob_filename);
1230 return knob_pixbuf;
1233 static void
1234 draw_stretch_handles (NautilusIconCanvasItem *item, GdkDrawable *drawable,
1235 const EelIRect *rect)
1237 GtkWidget *widget;
1238 GdkGC *gc;
1239 GdkPixbuf *knob_pixbuf;
1240 GdkBitmap *stipple;
1241 int knob_width, knob_height;
1243 if (!item->details->show_stretch_handles) {
1244 return;
1247 widget = GTK_WIDGET (EEL_CANVAS_ITEM (item)->canvas);
1249 gc = gdk_gc_new (drawable);
1250 knob_pixbuf = get_knob_pixbuf ();
1251 knob_width = gdk_pixbuf_get_width (knob_pixbuf);
1252 knob_height = gdk_pixbuf_get_height (knob_pixbuf);
1254 stipple = eel_stipple_bitmap_for_screen (
1255 gdk_drawable_get_screen (GDK_DRAWABLE (drawable)));
1257 /* first draw the box */
1258 gdk_gc_set_rgb_fg_color (gc, &widget->style->white);
1259 gdk_draw_rectangle
1260 (drawable, gc, FALSE,
1261 rect->x0,
1262 rect->y0,
1263 rect->x1 - rect->x0 - 1,
1264 rect->y1 - rect->y0 - 1);
1266 gdk_gc_set_rgb_fg_color (gc, &widget->style->black);
1267 gdk_gc_set_stipple (gc, stipple);
1268 gdk_gc_set_fill (gc, GDK_STIPPLED);
1269 gdk_draw_rectangle
1270 (drawable, gc, FALSE,
1271 rect->x0,
1272 rect->y0,
1273 rect->x1 - rect->x0 - 1,
1274 rect->y1 - rect->y0 - 1);
1276 /* draw the stretch handles themselves */
1278 draw_pixbuf (knob_pixbuf, drawable, rect->x0, rect->y0);
1279 draw_pixbuf (knob_pixbuf, drawable, rect->x0, rect->y1 - knob_height);
1280 draw_pixbuf (knob_pixbuf, drawable, rect->x1 - knob_width, rect->y0);
1281 draw_pixbuf (knob_pixbuf, drawable, rect->x1 - knob_width, rect->y1 - knob_height);
1282 g_object_unref (knob_pixbuf);
1284 g_object_unref (gc);
1287 static void
1288 emblem_layout_reset (EmblemLayout *layout, NautilusIconCanvasItem *icon_item, EelIRect icon_rect, gboolean is_rtl)
1290 layout->icon_item = icon_item;
1291 layout->icon_rect = icon_rect;
1292 layout->side = is_rtl ? LEFT_SIDE : RIGHT_SIDE;
1293 layout->position = 0;
1294 layout->index = 0;
1295 layout->emblem = icon_item->details->emblem_pixbufs;
1298 static gboolean
1299 emblem_layout_next (EmblemLayout *layout,
1300 GdkPixbuf **emblem_pixbuf,
1301 EelIRect *emblem_rect,
1302 gboolean is_rtl)
1304 GdkPixbuf *pixbuf;
1305 int width, height, x, y;
1306 GdkPoint *attach_points;
1308 /* Check if we have layed out all of the pixbufs. */
1309 if (layout->emblem == NULL) {
1310 return FALSE;
1313 /* Get the pixbuf. */
1314 pixbuf = layout->emblem->data;
1315 width = gdk_pixbuf_get_width (pixbuf);
1316 height = gdk_pixbuf_get_height (pixbuf);
1319 /* Advance to the next emblem. */
1320 layout->emblem = layout->emblem->next;
1322 attach_points = layout->icon_item->details->attach_points;
1323 if (attach_points != NULL) {
1324 if (layout->index >= layout->icon_item->details->n_attach_points) {
1325 return FALSE;
1328 x = layout->icon_rect.x0 + attach_points[layout->index].x;
1329 y = layout->icon_rect.y0 + attach_points[layout->index].y;
1331 layout->index += 1;
1333 /* Return the rectangle and pixbuf. */
1334 *emblem_pixbuf = pixbuf;
1335 emblem_rect->x0 = x - width / 2;
1336 emblem_rect->y0 = y - height / 2;
1337 emblem_rect->x1 = emblem_rect->x0 + width;
1338 emblem_rect->y1 = emblem_rect->y0 + height;
1340 return TRUE;
1344 for (;;) {
1346 /* Find the side to lay out along. */
1347 switch (layout->side) {
1348 case RIGHT_SIDE:
1349 x = layout->icon_rect.x1;
1350 y = is_rtl ? layout->icon_rect.y1 : layout->icon_rect.y0;
1351 break;
1352 case BOTTOM_SIDE:
1353 x = is_rtl ? layout->icon_rect.x0 : layout->icon_rect.x1;
1354 y = layout->icon_rect.y1;
1355 break;
1356 case LEFT_SIDE:
1357 x = layout->icon_rect.x0;
1358 y = is_rtl ? layout->icon_rect.y0 : layout->icon_rect.y1;
1359 break;
1360 case TOP_SIDE:
1361 x = is_rtl ? layout->icon_rect.x1 : layout->icon_rect.x0;
1362 y = layout->icon_rect.y0;
1363 break;
1364 default:
1365 g_assert_not_reached ();
1366 x = 0;
1367 y = 0;
1368 break;
1370 if (layout->position != 0) {
1371 switch (layout->side) {
1372 case RIGHT_SIDE:
1373 y += (is_rtl ? -1 : 1) * (layout->position + height / 2);
1374 break;
1375 case BOTTOM_SIDE:
1376 x += (is_rtl ? 1 : -1 ) * (layout->position + width / 2);
1377 break;
1378 case LEFT_SIDE:
1379 y += (is_rtl ? 1 : -1) * (layout->position + height / 2);
1380 break;
1381 case TOP_SIDE:
1382 x += (is_rtl ? -1 : 1) * (layout->position + width / 2);
1383 break;
1387 /* Check to see if emblem fits in current side. */
1388 if (x >= layout->icon_rect.x0 && x <= layout->icon_rect.x1
1389 && y >= layout->icon_rect.y0 && y <= layout->icon_rect.y1) {
1391 /* It fits. */
1393 /* Advance along the side. */
1394 switch (layout->side) {
1395 case RIGHT_SIDE:
1396 case LEFT_SIDE:
1397 layout->position += height + EMBLEM_SPACING;
1398 break;
1399 case BOTTOM_SIDE:
1400 case TOP_SIDE:
1401 layout->position += width + EMBLEM_SPACING;
1402 break;
1405 /* Return the rectangle and pixbuf. */
1406 *emblem_pixbuf = pixbuf;
1407 emblem_rect->x0 = x - width / 2;
1408 emblem_rect->y0 = y - height / 2;
1409 emblem_rect->x1 = emblem_rect->x0 + width;
1410 emblem_rect->y1 = emblem_rect->y0 + height;
1412 return TRUE;
1415 /* It doesn't fit, so move to the next side. */
1416 switch (layout->side) {
1417 case RIGHT_SIDE:
1418 layout->side = is_rtl ? TOP_SIDE : BOTTOM_SIDE;
1419 break;
1420 case BOTTOM_SIDE:
1421 layout->side = is_rtl ? RIGHT_SIDE : LEFT_SIDE;
1422 break;
1423 case LEFT_SIDE:
1424 layout->side = is_rtl ? BOTTOM_SIDE : TOP_SIDE;
1425 break;
1426 case TOP_SIDE:
1427 default:
1428 return FALSE;
1430 layout->position = 0;
1434 static void
1435 draw_pixbuf (GdkPixbuf *pixbuf, GdkDrawable *drawable, int x, int y)
1437 /* FIXME bugzilla.gnome.org 40703:
1438 * Dither would be better if we passed dither values.
1440 gdk_draw_pixbuf (drawable, NULL, pixbuf, 0, 0, x, y,
1441 gdk_pixbuf_get_width (pixbuf),
1442 gdk_pixbuf_get_height (pixbuf),
1443 GDK_RGB_DITHER_NORMAL, 0, 0);
1446 /* should be moved to libeel! */
1447 static guchar
1448 nautilus_icon_canvas_lighten_pixbuf_component (guchar cur_value, guint lighten_value) {
1449 int new_value = cur_value;
1450 if (lighten_value > 0) {
1451 new_value += lighten_value + (new_value >> 3);
1452 if (new_value > 255) {
1453 new_value = 255;
1456 return (guchar) new_value;
1459 /* should be moved to libeel! */
1460 static GdkPixbuf *
1461 nautilus_icon_canvas_lighten_pixbuf (GdkPixbuf* src, guint lighten_value) {
1462 GdkPixbuf *dest;
1463 int i, j;
1464 int width, height, has_alpha, src_row_stride, dst_row_stride;
1465 guchar *target_pixels, *original_pixels;
1466 guchar *pixsrc, *pixdest;
1468 g_return_val_if_fail (gdk_pixbuf_get_colorspace (src) == GDK_COLORSPACE_RGB, NULL);
1469 g_return_val_if_fail ((!gdk_pixbuf_get_has_alpha (src)
1470 && gdk_pixbuf_get_n_channels (src) == 3)
1471 || (gdk_pixbuf_get_has_alpha (src)
1472 && gdk_pixbuf_get_n_channels (src) == 4), NULL);
1473 g_return_val_if_fail (gdk_pixbuf_get_bits_per_sample (src) == 8, NULL);
1475 dest = gdk_pixbuf_new (gdk_pixbuf_get_colorspace (src),
1476 gdk_pixbuf_get_has_alpha (src),
1477 gdk_pixbuf_get_bits_per_sample (src),
1478 gdk_pixbuf_get_width (src),
1479 gdk_pixbuf_get_height (src));
1481 has_alpha = gdk_pixbuf_get_has_alpha (src);
1482 width = gdk_pixbuf_get_width (src);
1483 height = gdk_pixbuf_get_height (src);
1484 dst_row_stride = gdk_pixbuf_get_rowstride (dest);
1485 src_row_stride = gdk_pixbuf_get_rowstride (src);
1486 target_pixels = gdk_pixbuf_get_pixels (dest);
1487 original_pixels = gdk_pixbuf_get_pixels (src);
1489 for (i = 0; i < height; i++) {
1490 pixdest = target_pixels + i * dst_row_stride;
1491 pixsrc = original_pixels + i * src_row_stride;
1492 for (j = 0; j < width; j++) {
1493 *pixdest++ = nautilus_icon_canvas_lighten_pixbuf_component (*pixsrc++, lighten_value);
1494 *pixdest++ = nautilus_icon_canvas_lighten_pixbuf_component (*pixsrc++, lighten_value);
1495 *pixdest++ = nautilus_icon_canvas_lighten_pixbuf_component (*pixsrc++, lighten_value);
1496 if (has_alpha) {
1497 *pixdest++ = *pixsrc++;
1501 return dest;
1506 static GdkPixbuf *
1507 render_icon (GdkPixbuf *pixbuf, guint render_mode, guint saturation, guint brightness, guint lighten_value, guint color)
1509 GdkPixbuf *temp_pixbuf, *old_pixbuf;
1511 if (render_mode == 1) {
1512 /* lighten icon */
1513 temp_pixbuf = eel_create_spotlight_pixbuf (pixbuf);
1515 else if (render_mode == 2) {
1516 /* colorize icon */
1517 temp_pixbuf = eel_create_colorized_pixbuf (pixbuf,
1518 EEL_RGBA_COLOR_GET_R (color),
1519 EEL_RGBA_COLOR_GET_G (color),
1520 EEL_RGBA_COLOR_GET_B (color));
1521 } else if (render_mode == 3) {
1522 /* monochromely colorize icon */
1523 old_pixbuf = eel_create_darkened_pixbuf (pixbuf, 0, 255);
1524 temp_pixbuf = eel_create_colorized_pixbuf (old_pixbuf,
1525 EEL_RGBA_COLOR_GET_R (color),
1526 EEL_RGBA_COLOR_GET_G (color),
1527 EEL_RGBA_COLOR_GET_B (color));
1528 g_object_unref (old_pixbuf);
1529 } else {
1530 temp_pixbuf = NULL;
1533 if (saturation < 255 || brightness < 255 || temp_pixbuf == NULL) { // temp_pixbuf == NULL just for safer code (return copy)
1534 old_pixbuf = temp_pixbuf;
1535 temp_pixbuf = eel_create_darkened_pixbuf (temp_pixbuf ? temp_pixbuf : pixbuf, saturation, brightness);
1536 if (old_pixbuf) {
1537 g_object_unref (old_pixbuf);
1541 if (lighten_value > 0) {
1542 old_pixbuf = temp_pixbuf;
1543 temp_pixbuf = nautilus_icon_canvas_lighten_pixbuf (temp_pixbuf ? temp_pixbuf : pixbuf, lighten_value);
1544 if (old_pixbuf) {
1545 g_object_unref (old_pixbuf);
1549 return temp_pixbuf;
1552 /* shared code to highlight or dim the passed-in pixbuf */
1553 static GdkPixbuf *
1554 real_map_pixbuf (NautilusIconCanvasItem *icon_item)
1556 EelCanvas *canvas;
1557 char *audio_filename;
1558 NautilusIconContainer *container;
1559 GdkPixbuf *temp_pixbuf, *old_pixbuf, *audio_pixbuf;
1560 double zoom;
1561 guint render_mode, saturation, brightness, lighten;
1563 temp_pixbuf = icon_item->details->pixbuf;
1564 canvas = EEL_CANVAS_ITEM(icon_item)->canvas;
1565 container = NAUTILUS_ICON_CONTAINER (canvas);
1567 g_object_ref (temp_pixbuf);
1569 if (icon_item->details->is_prelit) {
1570 old_pixbuf = temp_pixbuf;
1572 gtk_widget_style_get (GTK_WIDGET (container),
1573 "prelight_icon_render_mode", &render_mode,
1574 "prelight_icon_saturation", &saturation,
1575 "prelight_icon_brightness", &brightness,
1576 "prelight_icon_lighten", &lighten,
1577 NULL);
1579 if (render_mode > 0 || saturation < 255 || brightness < 255) {
1580 temp_pixbuf = render_icon (temp_pixbuf,
1581 render_mode,
1582 saturation,
1583 brightness,
1584 lighten,
1585 container->details->prelight_icon_color_rgba);
1586 g_object_unref (old_pixbuf);
1591 /* FIXME bugzilla.gnome.org 42471: This hard-wired image is inappropriate to
1592 * this level of code, which shouldn't know that the
1593 * preview is audio, nor should it have an icon
1594 * hard-wired in.
1597 /* if the icon is currently being previewed, superimpose an image to indicate that */
1598 /* audio is the only kind of previewing right now, so this code isn't as general as it could be */
1599 if (icon_item->details->is_active) {
1600 zoom = (double) gdk_pixbuf_get_width (temp_pixbuf) / NAUTILUS_ICON_SIZE_STANDARD;
1601 /* Load the audio symbol. */
1602 audio_filename = nautilus_pixmap_file ("audio.svg");
1603 if (audio_filename != NULL) {
1604 audio_pixbuf = rsvg_pixbuf_from_file_at_zoom_with_max (audio_filename, zoom, zoom,
1605 NAUTILUS_ICON_MAXIMUM_SIZE,
1606 NAUTILUS_ICON_MAXIMUM_SIZE,
1607 NULL);
1608 } else {
1609 audio_pixbuf = NULL;
1612 /* Composite it onto the icon. */
1613 if (audio_pixbuf != NULL) {
1614 gdk_pixbuf_composite
1615 (audio_pixbuf,
1616 temp_pixbuf,
1617 0, 0,
1618 gdk_pixbuf_get_width (temp_pixbuf),
1619 gdk_pixbuf_get_height(temp_pixbuf),
1620 0, 0,
1621 1.0, 1.0,
1622 GDK_INTERP_BILINEAR, 0xFF);
1624 g_object_unref (audio_pixbuf);
1627 g_free (audio_filename);
1631 if (icon_item->details->is_highlighted_for_selection
1632 || icon_item->details->is_highlighted_for_drop) {
1633 guint color;
1635 old_pixbuf = temp_pixbuf;
1637 color = GTK_WIDGET_HAS_FOCUS (GTK_WIDGET (canvas)) ? NAUTILUS_ICON_CONTAINER (canvas)->details->highlight_color_rgba : NAUTILUS_ICON_CONTAINER (canvas)->details->active_color_rgba;
1639 temp_pixbuf = eel_create_colorized_pixbuf (temp_pixbuf,
1640 EEL_RGBA_COLOR_GET_R (color),
1641 EEL_RGBA_COLOR_GET_G (color),
1642 EEL_RGBA_COLOR_GET_B (color));
1644 g_object_unref (old_pixbuf);
1647 if (!icon_item->details->is_active
1648 && !icon_item->details->is_prelit
1649 && !icon_item->details->is_highlighted_for_selection
1650 && !icon_item->details->is_highlighted_for_drop) {
1651 old_pixbuf = temp_pixbuf;
1653 gtk_widget_style_get (GTK_WIDGET (container),
1654 "normal_icon_render_mode", &render_mode,
1655 "normal_icon_saturation", &saturation,
1656 "normal_icon_brightness", &brightness,
1657 "normal_icon_lighten", &lighten,
1658 NULL);
1659 if (render_mode > 0 || saturation < 255 || brightness < 255) {
1660 /* if theme requests colorization */
1661 temp_pixbuf = render_icon (temp_pixbuf,
1662 render_mode,
1663 saturation,
1664 brightness,
1665 lighten,
1666 container->details->normal_icon_color_rgba);
1667 g_object_unref (old_pixbuf);
1671 return temp_pixbuf;
1674 static GdkPixbuf *
1675 map_pixbuf (NautilusIconCanvasItem *icon_item)
1677 if (!(icon_item->details->rendered_pixbuf != NULL
1678 && icon_item->details->rendered_is_active == icon_item->details->is_active
1679 && icon_item->details->rendered_is_prelit == icon_item->details->is_prelit
1680 && icon_item->details->rendered_is_highlighted_for_selection == icon_item->details->is_highlighted_for_selection
1681 && icon_item->details->rendered_is_highlighted_for_drop == icon_item->details->is_highlighted_for_drop
1682 && (icon_item->details->is_highlighted_for_selection && icon_item->details->rendered_is_focused == GTK_WIDGET_HAS_FOCUS (EEL_CANVAS_ITEM (icon_item)->canvas)))) {
1683 if (icon_item->details->rendered_pixbuf != NULL) {
1684 g_object_unref (icon_item->details->rendered_pixbuf);
1686 icon_item->details->rendered_pixbuf = real_map_pixbuf (icon_item);
1687 icon_item->details->rendered_is_active = icon_item->details->is_active;
1688 icon_item->details->rendered_is_prelit = icon_item->details->is_prelit;
1689 icon_item->details->rendered_is_highlighted_for_selection = icon_item->details->is_highlighted_for_selection;
1690 icon_item->details->rendered_is_highlighted_for_drop = icon_item->details->is_highlighted_for_drop;
1691 icon_item->details->rendered_is_focused = GTK_WIDGET_HAS_FOCUS (EEL_CANVAS_ITEM (icon_item)->canvas);
1694 g_object_ref (icon_item->details->rendered_pixbuf);
1696 return icon_item->details->rendered_pixbuf;
1699 static void
1700 draw_embedded_text (NautilusIconCanvasItem *item,
1701 GdkDrawable *drawable,
1702 int x, int y)
1704 GdkGC *gc;
1705 GdkRectangle clip_rect;
1706 PangoLayout *layout;
1707 PangoContext *context;
1708 PangoFontDescription *desc;
1710 if (item->details->embedded_text == NULL ||
1711 item->details->embedded_text_rect.width == 0 ||
1712 item->details->embedded_text_rect.height == 0) {
1713 return;
1716 if (item->details->embedded_text_layout != NULL) {
1717 layout = g_object_ref (item->details->embedded_text_layout);
1718 } else {
1719 context = gtk_widget_get_pango_context (GTK_WIDGET (EEL_CANVAS_ITEM (item)->canvas));
1720 layout = pango_layout_new (context);
1721 pango_layout_set_text (layout, item->details->embedded_text, -1);
1723 desc = pango_font_description_from_string ("monospace 6");
1724 pango_layout_set_font_description (layout, desc);
1725 pango_font_description_free (desc);
1727 if (item->details->is_visible) {
1728 item->details->embedded_text_layout = g_object_ref (layout);
1732 gc = gdk_gc_new (drawable);
1734 clip_rect.x = x + item->details->embedded_text_rect.x;
1735 clip_rect.y = y + item->details->embedded_text_rect.y;
1736 clip_rect.width = item->details->embedded_text_rect.width;
1737 clip_rect.height = item->details->embedded_text_rect.height;
1739 gdk_gc_set_clip_rectangle (gc, &clip_rect);
1741 gdk_draw_layout (drawable, gc,
1742 x + item->details->embedded_text_rect.x,
1743 y + item->details->embedded_text_rect.y,
1744 layout);
1746 g_object_unref (gc);
1747 g_object_unref (layout);
1750 /* Draw the icon item for non-anti-aliased mode. */
1751 static void
1752 nautilus_icon_canvas_item_draw (EelCanvasItem *item, GdkDrawable *drawable,
1753 GdkEventExpose *expose)
1755 NautilusIconCanvasItem *icon_item;
1756 NautilusIconCanvasItemDetails *details;
1757 EelIRect icon_rect, emblem_rect;
1758 EmblemLayout emblem_layout;
1759 GdkPixbuf *emblem_pixbuf, *temp_pixbuf;
1760 GdkRectangle draw_rect, pixbuf_rect;
1761 gboolean is_rtl;
1763 icon_item = NAUTILUS_ICON_CANVAS_ITEM (item);
1764 details = icon_item->details;
1766 /* Draw the pixbuf. */
1767 if (details->pixbuf == NULL) {
1768 return;
1771 icon_rect = icon_item->details->canvas_rect;
1773 /* if the pre-lit or selection flag is set, make a pre-lit or darkened pixbuf and draw that instead */
1774 /* and colorize normal pixbuf if rc wants that */
1775 temp_pixbuf = map_pixbuf (icon_item);
1776 pixbuf_rect.x = icon_rect.x0;
1777 pixbuf_rect.y = icon_rect.y0;
1778 pixbuf_rect.width = gdk_pixbuf_get_width (temp_pixbuf);
1779 pixbuf_rect.height = gdk_pixbuf_get_height (temp_pixbuf);
1780 if (gdk_rectangle_intersect (&(expose->area), &pixbuf_rect, &draw_rect)) {
1781 gdk_draw_pixbuf (drawable,
1782 NULL,
1783 temp_pixbuf,
1784 draw_rect.x - pixbuf_rect.x,
1785 draw_rect.y - pixbuf_rect.y,
1786 draw_rect.x,
1787 draw_rect.y,
1788 draw_rect.width,
1789 draw_rect.height,
1790 GDK_RGB_DITHER_NORMAL,
1791 0,0);
1793 g_object_unref (temp_pixbuf);
1795 draw_embedded_text (icon_item, drawable, icon_rect.x0, icon_rect.y0);
1797 is_rtl = nautilus_icon_container_is_layout_rtl (NAUTILUS_ICON_CONTAINER (item->canvas));
1799 /* Draw the emblem pixbufs. */
1800 emblem_layout_reset (&emblem_layout, icon_item, icon_rect, is_rtl);
1801 while (emblem_layout_next (&emblem_layout, &emblem_pixbuf, &emblem_rect, is_rtl)) {
1802 draw_pixbuf (emblem_pixbuf, drawable, emblem_rect.x0, emblem_rect.y0);
1805 /* Draw stretching handles (if necessary). */
1806 draw_stretch_handles (icon_item, drawable, &icon_rect);
1808 /* Draw the label text. */
1809 draw_label_text (icon_item, drawable, FALSE, icon_rect);
1812 #define ZERO_WIDTH_SPACE "\xE2\x80\x8B"
1814 #define ZERO_OR_THREE_DIGITS(p) \
1815 (!g_ascii_isdigit (*p) || \
1816 (g_ascii_isdigit (*(p+1)) && \
1817 g_ascii_isdigit (*(p+2))))
1819 static PangoLayout *
1820 create_label_layout (NautilusIconCanvasItem *item,
1821 const char *text)
1823 PangoLayout *layout;
1824 PangoContext *context;
1825 PangoFontDescription *desc;
1826 NautilusIconContainer *container;
1827 EelCanvasItem *canvas_item;
1828 GString *str;
1829 char *zeroified_text;
1830 const char *p;
1832 canvas_item = EEL_CANVAS_ITEM (item);
1834 container = NAUTILUS_ICON_CONTAINER (canvas_item->canvas);
1835 context = gtk_widget_get_pango_context (GTK_WIDGET (canvas_item->canvas));
1836 layout = pango_layout_new (context);
1838 zeroified_text = NULL;
1840 if (text != NULL) {
1841 str = g_string_new (NULL);
1843 for (p = text; *p != '\0'; p++) {
1844 str = g_string_append_c (str, *p);
1846 if (*p == '_' || *p == '-' || (*p == '.' && ZERO_OR_THREE_DIGITS (p+1))) {
1847 /* Ensure that we allow to break after '_' or '.' characters,
1848 * if they are not likely to be part of a version information, to
1849 * not break wrapping of foobar-0.0.1.
1850 * Wrap before IPs and long numbers, though. */
1851 str = g_string_append (str, ZERO_WIDTH_SPACE);
1855 zeroified_text = g_string_free (str, FALSE);
1858 pango_layout_set_text (layout, zeroified_text, -1);
1859 pango_layout_set_width (layout, floor (nautilus_icon_canvas_item_get_max_text_width (item)) * PANGO_SCALE);
1861 pango_layout_set_auto_dir (layout, FALSE);
1863 if (container->details->label_position == NAUTILUS_ICON_LABEL_POSITION_BESIDE) {
1864 if (!nautilus_icon_container_is_layout_rtl (container)) {
1865 pango_layout_set_alignment (layout, PANGO_ALIGN_LEFT);
1866 } else {
1867 pango_layout_set_alignment (layout, PANGO_ALIGN_RIGHT);
1869 } else {
1870 pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER);
1873 pango_layout_set_spacing (layout, LABEL_LINE_SPACING);
1874 pango_layout_set_wrap (layout, PANGO_WRAP_WORD_CHAR);
1876 /* Create a font description */
1877 if (container->details->font) {
1878 desc = pango_font_description_from_string (container->details->font);
1879 } else {
1880 desc = pango_font_description_copy (pango_context_get_font_description (context));
1881 pango_font_description_set_size (desc,
1882 pango_font_description_get_size (desc) +
1883 container->details->font_size_table [container->details->zoom_level]);
1885 pango_layout_set_font_description (layout, desc);
1886 pango_font_description_free (desc);
1887 g_free (zeroified_text);
1889 return layout;
1892 static PangoLayout *
1893 get_label_layout (PangoLayout **layout_cache,
1894 NautilusIconCanvasItem *item,
1895 const char *text)
1897 PangoLayout *layout;
1899 if (*layout_cache != NULL) {
1900 return g_object_ref (*layout_cache);
1903 layout = create_label_layout (item, text);
1905 if (item->details->is_visible) {
1906 *layout_cache = g_object_ref (layout);
1909 return layout;
1912 static void
1913 draw_label_layout (NautilusIconCanvasItem *item,
1914 GdkDrawable *drawable,
1915 PangoLayout *layout,
1916 gboolean highlight,
1917 GdkColor *label_color,
1918 int x,
1919 int y,
1920 GdkGC *gc)
1922 if (drawable == NULL) {
1923 return;
1926 if (item->details->is_renaming) {
1927 return;
1930 if (!highlight && (NAUTILUS_ICON_CONTAINER (EEL_CANVAS_ITEM (item)->canvas)->details->use_drop_shadows)) {
1931 /* draw a drop shadow */
1932 eel_gdk_draw_layout_with_drop_shadow (drawable, gc,
1933 label_color,
1934 &GTK_WIDGET (EEL_CANVAS_ITEM (item)->canvas)->style->black,
1935 x, y,
1936 layout);
1937 } else {
1938 gdk_draw_layout (drawable, gc,
1939 x, y,
1940 layout);
1944 /* handle events */
1946 static int
1947 nautilus_icon_canvas_item_event (EelCanvasItem *item, GdkEvent *event)
1949 NautilusIconCanvasItem *icon_item;
1950 GdkCursor *cursor;
1952 icon_item = NAUTILUS_ICON_CANVAS_ITEM (item);
1954 switch (event->type) {
1955 case GDK_ENTER_NOTIFY:
1956 if (!icon_item->details->is_prelit) {
1957 icon_item->details->is_prelit = TRUE;
1958 eel_canvas_item_request_update (item);
1959 /* show a hand cursor */
1960 if (in_single_click_mode ()) {
1961 cursor = gdk_cursor_new_for_display (gdk_display_get_default(),
1962 GDK_HAND2);
1963 gdk_window_set_cursor (((GdkEventAny *)event)->window, cursor);
1964 gdk_cursor_unref (cursor);
1967 /* FIXME bugzilla.gnome.org 42473:
1968 * We should emit our own signal here,
1969 * not one from the container; it could hook
1970 * up to that signal and emit one of its
1971 * own. Doing it this way hard-codes what
1972 * "user_data" is. Also, the two signals
1973 * should be separate. The "unpreview" signal
1974 * does not have a return value.
1976 icon_item->details->is_active = nautilus_icon_container_emit_preview_signal
1977 (NAUTILUS_ICON_CONTAINER (item->canvas),
1978 NAUTILUS_ICON_CANVAS_ITEM (item)->user_data,
1979 TRUE);
1981 return TRUE;
1983 case GDK_LEAVE_NOTIFY:
1984 if (icon_item->details->is_prelit
1985 || icon_item->details->is_highlighted_for_drop) {
1986 /* When leaving, turn of the prelight state and the
1987 * higlighted for drop. The latter gets turned on
1988 * by the drag&drop motion callback.
1990 /* FIXME bugzilla.gnome.org 42473:
1991 * We should emit our own signal here,
1992 * not one from the containe; it could hook up
1993 * to that signal and emit one of its
1994 * ownr. Doing it this way hard-codes what
1995 * "user_data" is. Also, the two signals
1996 * should be separate. The "unpreview" signal
1997 * does not have a return value.
1999 nautilus_icon_container_emit_preview_signal
2000 (NAUTILUS_ICON_CONTAINER (item->canvas),
2001 NAUTILUS_ICON_CANVAS_ITEM (item)->user_data,
2002 FALSE);
2003 icon_item->details->is_prelit = FALSE;
2004 icon_item->details->is_active = 0;
2005 icon_item->details->is_highlighted_for_drop = FALSE;
2006 eel_canvas_item_request_update (item);
2008 /* show default cursor */
2009 gdk_window_set_cursor (((GdkEventAny *)event)->window, NULL);
2011 return TRUE;
2013 default:
2014 /* Don't eat up other events; icon container might use them. */
2015 return FALSE;
2019 static gboolean
2020 hit_test_pixbuf (GdkPixbuf *pixbuf, EelIRect pixbuf_location, EelIRect probe_rect)
2022 EelIRect relative_rect, pixbuf_rect;
2023 int x, y;
2024 guint8 *pixel;
2026 /* You can get here without a pixbuf in some strange cases. */
2027 if (pixbuf == NULL) {
2028 return FALSE;
2031 /* Check to see if it's within the rectangle at all. */
2032 relative_rect.x0 = probe_rect.x0 - pixbuf_location.x0;
2033 relative_rect.y0 = probe_rect.y0 - pixbuf_location.y0;
2034 relative_rect.x1 = probe_rect.x1 - pixbuf_location.x0;
2035 relative_rect.y1 = probe_rect.y1 - pixbuf_location.y0;
2036 pixbuf_rect.x0 = 0;
2037 pixbuf_rect.y0 = 0;
2038 pixbuf_rect.x1 = gdk_pixbuf_get_width (pixbuf);
2039 pixbuf_rect.y1 = gdk_pixbuf_get_height (pixbuf);
2040 eel_irect_intersect (&relative_rect, &relative_rect, &pixbuf_rect);
2041 if (eel_irect_is_empty (&relative_rect)) {
2042 return FALSE;
2045 /* If there's no alpha channel, it's opaque and we have a hit. */
2046 if (!gdk_pixbuf_get_has_alpha (pixbuf)) {
2047 return TRUE;
2049 g_assert (gdk_pixbuf_get_n_channels (pixbuf) == 4);
2051 /* Check the alpha channel of the pixel to see if we have a hit. */
2052 for (x = relative_rect.x0; x < relative_rect.x1; x++) {
2053 for (y = relative_rect.y0; y < relative_rect.y1; y++) {
2054 pixel = gdk_pixbuf_get_pixels (pixbuf)
2055 + y * gdk_pixbuf_get_rowstride (pixbuf)
2056 + x * 4;
2057 if (pixel[3] > 1) {
2058 return TRUE;
2062 return FALSE;
2065 static gboolean
2066 hit_test (NautilusIconCanvasItem *icon_item, EelIRect canvas_rect)
2068 NautilusIconCanvasItemDetails *details;
2069 EelIRect emblem_rect;
2070 EmblemLayout emblem_layout;
2071 GdkPixbuf *emblem_pixbuf;
2072 gboolean is_rtl;
2074 details = icon_item->details;
2076 /* Quick check to see if the rect hits the icon, text or emblems at all. */
2077 if (!eel_irect_hits_irect (icon_item->details->canvas_rect, canvas_rect)
2078 && (!eel_irect_hits_irect (details->text_rect, canvas_rect))
2079 && (!eel_irect_hits_irect (details->emblem_rect, canvas_rect))) {
2080 return FALSE;
2083 /* Check for hits in the stretch handles. */
2084 if (hit_test_stretch_handle (icon_item, canvas_rect, NULL)) {
2085 return TRUE;
2088 /* Check for hit in the icon. */
2089 if (eel_irect_hits_irect (icon_item->details->canvas_rect, canvas_rect)) {
2090 return TRUE;
2093 /* Check for hit in the text. */
2094 if (eel_irect_hits_irect (details->text_rect, canvas_rect)
2095 && !icon_item->details->is_renaming) {
2096 return TRUE;
2099 is_rtl = nautilus_icon_container_is_layout_rtl (NAUTILUS_ICON_CONTAINER (EEL_CANVAS_ITEM (icon_item)->canvas));
2101 /* Check for hit in the emblem pixbufs. */
2102 emblem_layout_reset (&emblem_layout, icon_item, icon_item->details->canvas_rect, is_rtl);
2103 while (emblem_layout_next (&emblem_layout, &emblem_pixbuf, &emblem_rect, is_rtl)) {
2104 if (hit_test_pixbuf (emblem_pixbuf, emblem_rect, canvas_rect)) {
2105 return TRUE;
2109 return FALSE;
2112 /* Point handler for the icon canvas item. */
2113 static double
2114 nautilus_icon_canvas_item_point (EelCanvasItem *item, double x, double y, int cx, int cy,
2115 EelCanvasItem **actual_item)
2117 EelIRect canvas_rect;
2119 *actual_item = item;
2120 canvas_rect.x0 = cx;
2121 canvas_rect.y0 = cy;
2122 canvas_rect.x1 = cx + 1;
2123 canvas_rect.y1 = cy + 1;
2124 if (hit_test (NAUTILUS_ICON_CANVAS_ITEM (item), canvas_rect)) {
2125 return 0.0;
2126 } else {
2127 /* This value means not hit.
2128 * It's kind of arbitrary. Can we do better?
2130 return item->canvas->pixels_per_unit * 2 + 10;
2134 static void
2135 nautilus_icon_canvas_item_translate (EelCanvasItem *item, double dx, double dy)
2137 NautilusIconCanvasItem *icon_item;
2138 NautilusIconCanvasItemDetails *details;
2140 icon_item = NAUTILUS_ICON_CANVAS_ITEM (item);
2141 details = icon_item->details;
2143 details->x += dx;
2144 details->y += dy;
2147 /* Bounds handler for the icon canvas item. */
2148 static void
2149 nautilus_icon_canvas_item_bounds (EelCanvasItem *item,
2150 double *x1, double *y1, double *x2, double *y2)
2152 NautilusIconCanvasItem *icon_item;
2153 NautilusIconCanvasItemDetails *details;
2154 EelIRect icon_rect, text_rect, total_rect, emblem_rect;
2155 double pixels_per_unit;
2156 EmblemLayout emblem_layout;
2157 GdkPixbuf *emblem_pixbuf;
2158 gboolean is_rtl;
2160 g_assert (x1 != NULL);
2161 g_assert (y1 != NULL);
2162 g_assert (x2 != NULL);
2163 g_assert (y2 != NULL);
2165 icon_item = NAUTILUS_ICON_CANVAS_ITEM (item);
2166 details = icon_item->details;
2168 if (details->bounds_cached) {
2169 total_rect = details->bounds_cache;
2170 } else {
2171 measure_label_text (icon_item);
2173 pixels_per_unit = item->canvas->pixels_per_unit;
2175 /* Compute icon rectangle. */
2176 icon_rect.x0 = 0;
2177 icon_rect.y0 = 0;
2178 if (details->pixbuf == NULL) {
2179 icon_rect.x1 = icon_rect.x0;
2180 icon_rect.y1 = icon_rect.y0;
2181 } else {
2182 icon_rect.x1 = icon_rect.x0 + gdk_pixbuf_get_width (details->pixbuf) / pixels_per_unit;
2183 icon_rect.y1 = icon_rect.y0 + gdk_pixbuf_get_height (details->pixbuf) / pixels_per_unit;
2186 /* Compute text rectangle. */
2187 text_rect = compute_text_rectangle (icon_item, icon_rect, FALSE);
2189 is_rtl = nautilus_icon_container_is_layout_rtl (NAUTILUS_ICON_CONTAINER (item->canvas));
2191 /* Compute total rectangle, adding in emblem rectangles. */
2192 eel_irect_union (&total_rect, &icon_rect, &text_rect);
2193 emblem_layout_reset (&emblem_layout, icon_item, icon_rect, is_rtl);
2194 while (emblem_layout_next (&emblem_layout, &emblem_pixbuf, &emblem_rect, is_rtl)) {
2195 emblem_rect.x0 = floor (emblem_rect.x0 / pixels_per_unit);
2196 emblem_rect.y0 = floor (emblem_rect.y0 / pixels_per_unit);
2197 emblem_rect.x1 = ceil (emblem_rect.x1 / pixels_per_unit);
2198 emblem_rect.y1 = ceil (emblem_rect.y1 / pixels_per_unit);
2200 eel_irect_union (&total_rect, &total_rect, &emblem_rect);
2203 details->bounds_cache = total_rect;
2204 details->bounds_cached = TRUE;
2207 /* Return the result. */
2208 *x1 = (int)details->x + total_rect.x0;
2209 *y1 = (int)details->y + total_rect.y0;
2210 *x2 = (int)details->x + total_rect.x1 + 1;
2211 *y2 = (int)details->y + total_rect.y1 + 1;
2214 /* Get the rectangle of the icon only, in world coordinates. */
2215 EelDRect
2216 nautilus_icon_canvas_item_get_icon_rectangle (const NautilusIconCanvasItem *item)
2218 EelDRect rectangle;
2219 double pixels_per_unit;
2220 GdkPixbuf *pixbuf;
2222 g_return_val_if_fail (NAUTILUS_IS_ICON_CANVAS_ITEM (item), eel_drect_empty);
2224 rectangle.x0 = item->details->x;
2225 rectangle.y0 = item->details->y;
2227 pixbuf = item->details->pixbuf;
2229 pixels_per_unit = EEL_CANVAS_ITEM (item)->canvas->pixels_per_unit;
2230 rectangle.x1 = rectangle.x0 + (pixbuf == NULL ? 0 : gdk_pixbuf_get_width (pixbuf)) / pixels_per_unit;
2231 rectangle.y1 = rectangle.y0 + (pixbuf == NULL ? 0 : gdk_pixbuf_get_height (pixbuf)) / pixels_per_unit;
2233 eel_canvas_item_i2w (EEL_CANVAS_ITEM (item),
2234 &rectangle.x0,
2235 &rectangle.y0);
2236 eel_canvas_item_i2w (EEL_CANVAS_ITEM (item),
2237 &rectangle.x1,
2238 &rectangle.y1);
2240 return rectangle;
2243 EelDRect
2244 nautilus_icon_canvas_item_get_text_rectangle (NautilusIconCanvasItem *item)
2246 /* FIXME */
2247 EelIRect icon_rectangle;
2248 EelIRect text_rectangle;
2249 EelDRect ret;
2250 double pixels_per_unit;
2251 GdkPixbuf *pixbuf;
2253 g_return_val_if_fail (NAUTILUS_IS_ICON_CANVAS_ITEM (item), eel_drect_empty);
2255 icon_rectangle.x0 = item->details->x;
2256 icon_rectangle.y0 = item->details->y;
2258 pixbuf = item->details->pixbuf;
2260 pixels_per_unit = EEL_CANVAS_ITEM (item)->canvas->pixels_per_unit;
2261 icon_rectangle.x1 = icon_rectangle.x0 + (pixbuf == NULL ? 0 : gdk_pixbuf_get_width (pixbuf)) / pixels_per_unit;
2262 icon_rectangle.y1 = icon_rectangle.y0 + (pixbuf == NULL ? 0 : gdk_pixbuf_get_height (pixbuf)) / pixels_per_unit;
2264 measure_label_text (item);
2265 text_rectangle = compute_text_rectangle (item, icon_rectangle, FALSE);
2267 ret.x0 = text_rectangle.x0;
2268 ret.y0 = text_rectangle.y0;
2269 ret.x1 = text_rectangle.x1;
2270 ret.y1 = text_rectangle.y1;
2272 eel_canvas_item_i2w (EEL_CANVAS_ITEM (item),
2273 &ret.x0,
2274 &ret.y0);
2275 eel_canvas_item_i2w (EEL_CANVAS_ITEM (item),
2276 &ret.x1,
2277 &ret.y1);
2279 return ret;
2283 /* Get the rectangle of the icon only, in canvas coordinates. */
2284 static void
2285 get_icon_canvas_rectangle (NautilusIconCanvasItem *item,
2286 EelIRect *rect)
2288 GdkPixbuf *pixbuf;
2290 g_return_if_fail (NAUTILUS_IS_ICON_CANVAS_ITEM (item));
2291 g_return_if_fail (rect != NULL);
2293 eel_canvas_w2c (EEL_CANVAS_ITEM (item)->canvas,
2294 item->details->x,
2295 item->details->y,
2296 &rect->x0,
2297 &rect->y0);
2299 pixbuf = item->details->pixbuf;
2301 rect->x1 = rect->x0 + (pixbuf == NULL ? 0 : gdk_pixbuf_get_width (pixbuf));
2302 rect->y1 = rect->y0 + (pixbuf == NULL ? 0 : gdk_pixbuf_get_height (pixbuf));
2305 void
2306 nautilus_icon_canvas_item_set_show_stretch_handles (NautilusIconCanvasItem *item,
2307 gboolean show_stretch_handles)
2309 g_return_if_fail (NAUTILUS_IS_ICON_CANVAS_ITEM (item));
2310 g_return_if_fail (show_stretch_handles == FALSE || show_stretch_handles == TRUE);
2312 if (!item->details->show_stretch_handles == !show_stretch_handles) {
2313 return;
2316 item->details->show_stretch_handles = show_stretch_handles;
2317 eel_canvas_item_request_update (EEL_CANVAS_ITEM (item));
2320 /* Check if one of the stretch handles was hit. */
2321 static gboolean
2322 hit_test_stretch_handle (NautilusIconCanvasItem *item,
2323 EelIRect probe_canvas_rect,
2324 GtkCornerType *corner)
2326 EelIRect icon_rect;
2327 GdkPixbuf *knob_pixbuf;
2328 int knob_width, knob_height;
2329 int hit_corner;
2331 g_return_val_if_fail (NAUTILUS_IS_ICON_CANVAS_ITEM (item), FALSE);
2333 /* Make sure there are handles to hit. */
2334 if (!item->details->show_stretch_handles) {
2335 return FALSE;
2338 /* Quick check to see if the rect hits the icon at all. */
2339 icon_rect = item->details->canvas_rect;
2340 if (!eel_irect_hits_irect (probe_canvas_rect, icon_rect)) {
2341 return FALSE;
2344 knob_pixbuf = get_knob_pixbuf ();
2345 knob_width = gdk_pixbuf_get_width (knob_pixbuf);
2346 knob_height = gdk_pixbuf_get_height (knob_pixbuf);
2347 g_object_unref (knob_pixbuf);
2349 /* Check for hits in the stretch handles. */
2350 hit_corner = -1;
2351 if (probe_canvas_rect.x0 < icon_rect.x0 + knob_width) {
2352 if (probe_canvas_rect.y0 < icon_rect.y0 + knob_height)
2353 hit_corner = GTK_CORNER_TOP_LEFT;
2354 else if (probe_canvas_rect.y1 >= icon_rect.y1 - knob_height)
2355 hit_corner = GTK_CORNER_BOTTOM_LEFT;
2357 else if (probe_canvas_rect.x1 >= icon_rect.x1 - knob_width) {
2358 if (probe_canvas_rect.y0 < icon_rect.y0 + knob_height)
2359 hit_corner = GTK_CORNER_TOP_RIGHT;
2360 else if (probe_canvas_rect.y1 >= icon_rect.y1 - knob_height)
2361 hit_corner = GTK_CORNER_BOTTOM_RIGHT;
2363 if (corner)
2364 *corner = hit_corner;
2366 return hit_corner != -1;
2369 gboolean
2370 nautilus_icon_canvas_item_hit_test_stretch_handles (NautilusIconCanvasItem *item,
2371 EelDPoint world_point,
2372 GtkCornerType *corner)
2374 EelIRect canvas_rect;
2376 g_return_val_if_fail (NAUTILUS_IS_ICON_CANVAS_ITEM (item), FALSE);
2378 eel_canvas_w2c (EEL_CANVAS_ITEM (item)->canvas,
2379 world_point.x,
2380 world_point.y,
2381 &canvas_rect.x0,
2382 &canvas_rect.y0);
2383 canvas_rect.x1 = canvas_rect.x0 + 1;
2384 canvas_rect.y1 = canvas_rect.y0 + 1;
2385 return hit_test_stretch_handle (item, canvas_rect, corner);
2388 /* nautilus_icon_canvas_item_hit_test_rectangle
2390 * Check and see if there is an intersection between the item and the
2391 * canvas rect.
2393 gboolean
2394 nautilus_icon_canvas_item_hit_test_rectangle (NautilusIconCanvasItem *item, EelIRect canvas_rect)
2396 g_return_val_if_fail (NAUTILUS_IS_ICON_CANVAS_ITEM (item), FALSE);
2398 return hit_test (item, canvas_rect);
2401 const char *
2402 nautilus_icon_canvas_item_get_editable_text (NautilusIconCanvasItem *icon_item)
2404 g_return_val_if_fail (NAUTILUS_IS_ICON_CANVAS_ITEM (icon_item), NULL);
2406 return icon_item->details->editable_text;
2409 void
2410 nautilus_icon_canvas_item_set_renaming (NautilusIconCanvasItem *item, gboolean state)
2412 g_return_if_fail (NAUTILUS_IS_ICON_CANVAS_ITEM (item));
2413 g_return_if_fail (state == FALSE || state == TRUE);
2415 if (!item->details->is_renaming == !state) {
2416 return;
2419 item->details->is_renaming = state;
2420 eel_canvas_item_request_update (EEL_CANVAS_ITEM (item));
2423 double
2424 nautilus_icon_canvas_item_get_max_text_width (NautilusIconCanvasItem *item)
2426 EelCanvasItem *canvas_item;
2428 canvas_item = EEL_CANVAS_ITEM (item);
2429 if (nautilus_icon_container_is_tighter_layout (NAUTILUS_ICON_CONTAINER (canvas_item->canvas))) {
2430 return MAX_TEXT_WIDTH_TIGHTER * canvas_item->canvas->pixels_per_unit;
2431 } else {
2433 if (NAUTILUS_ICON_CONTAINER (canvas_item->canvas)->details->label_position == NAUTILUS_ICON_LABEL_POSITION_BESIDE) {
2434 return MAX_TEXT_WIDTH_BESIDE * canvas_item->canvas->pixels_per_unit;
2435 } else {
2436 return MAX_TEXT_WIDTH_STANDARD * canvas_item->canvas->pixels_per_unit;
2444 /* NautilusIconCanvasItemAccessible */
2446 static NautilusIconCanvasItemAccessiblePrivate *
2447 accessible_get_priv (AtkObject *accessible)
2449 NautilusIconCanvasItemAccessiblePrivate *priv;
2451 priv = g_object_get_qdata (G_OBJECT (accessible),
2452 accessible_private_data_quark);
2454 return priv;
2457 /* AtkAction interface */
2459 static gboolean
2460 nautilus_icon_canvas_item_accessible_idle_do_action (gpointer data)
2462 NautilusIconCanvasItem *item;
2463 NautilusIconCanvasItemAccessibleActionContext *ctx;
2464 NautilusIcon *icon;
2465 NautilusIconContainer *container;
2466 GList* selection;
2467 GList file_list;
2468 GdkEventButton button_event = { 0 };
2469 gint action_number;
2471 container = NAUTILUS_ICON_CONTAINER (data);
2472 container->details->a11y_item_action_idle_handler = 0;
2473 while (!g_queue_is_empty (container->details->a11y_item_action_queue)) {
2474 ctx = g_queue_pop_head (container->details->a11y_item_action_queue);
2475 action_number = ctx->action_number;
2476 item = ctx->item;
2477 g_free (ctx);
2478 icon = item->user_data;
2480 switch (action_number) {
2481 case ACTION_OPEN:
2482 file_list.data = icon->data;
2483 file_list.next = NULL;
2484 file_list.prev = NULL;
2485 g_signal_emit_by_name (container, "activate", &file_list);
2486 break;
2487 case ACTION_MENU:
2488 selection = nautilus_icon_container_get_selection (container);
2489 if (selection == NULL ||
2490 g_list_length (selection) != 1 ||
2491 selection->data != icon->data) {
2492 g_list_free (selection);
2493 return FALSE;
2495 g_list_free (selection);
2496 g_signal_emit_by_name (container, "context_click_selection", &button_event);
2497 break;
2498 default :
2499 g_assert_not_reached ();
2500 break;
2503 return FALSE;
2506 static gboolean
2507 nautilus_icon_canvas_item_accessible_do_action (AtkAction *accessible, int i)
2509 NautilusIconCanvasItem *item;
2510 NautilusIconCanvasItemAccessibleActionContext *ctx;
2511 NautilusIcon *icon;
2512 NautilusIconContainer *container;
2514 g_return_val_if_fail (i < LAST_ACTION, FALSE);
2516 item = eel_accessibility_get_gobject (ATK_OBJECT (accessible));
2517 if (!item) {
2518 return FALSE;
2520 icon = item->user_data;
2521 container = NAUTILUS_ICON_CONTAINER (EEL_CANVAS_ITEM (item)->canvas);
2522 switch (i) {
2523 case ACTION_OPEN:
2524 case ACTION_MENU:
2525 if (container->details->a11y_item_action_queue == NULL) {
2526 container->details->a11y_item_action_queue = g_queue_new ();
2528 ctx = g_new (NautilusIconCanvasItemAccessibleActionContext, 1);
2529 ctx->action_number = i;
2530 ctx->item = item;
2531 g_queue_push_head (container->details->a11y_item_action_queue, ctx);
2532 if (container->details->a11y_item_action_idle_handler == 0) {
2533 container->details->a11y_item_action_idle_handler = g_idle_add (nautilus_icon_canvas_item_accessible_idle_do_action, container);
2535 break;
2536 default :
2537 g_warning ("Invalid action passed to NautilusIconCanvasItemAccessible::do_action");
2538 return FALSE;
2541 return TRUE;
2544 static int
2545 nautilus_icon_canvas_item_accessible_get_n_actions (AtkAction *accessible)
2547 return LAST_ACTION;
2550 static const char *
2551 nautilus_icon_canvas_item_accessible_action_get_description (AtkAction *accessible,
2552 int i)
2554 NautilusIconCanvasItemAccessiblePrivate *priv;
2556 g_return_val_if_fail (i < LAST_ACTION, NULL);
2558 priv = accessible_get_priv (ATK_OBJECT (accessible));
2559 if (priv->action_descriptions[i]) {
2560 return priv->action_descriptions[i];
2561 } else {
2562 return nautilus_icon_canvas_item_accessible_action_descriptions[i];
2566 static const char *
2567 nautilus_icon_canvas_item_accessible_action_get_name (AtkAction *accessible, int i)
2569 g_return_val_if_fail (i < LAST_ACTION, NULL);
2571 return nautilus_icon_canvas_item_accessible_action_names[i];
2574 static const char *
2575 nautilus_icon_canvas_item_accessible_action_get_keybinding (AtkAction *accessible,
2576 int i)
2578 g_return_val_if_fail (i < LAST_ACTION, NULL);
2580 return NULL;
2583 static gboolean
2584 nautilus_icon_canvas_item_accessible_action_set_description (AtkAction *accessible,
2585 int i,
2586 const char *description)
2588 NautilusIconCanvasItemAccessiblePrivate *priv;
2590 g_return_val_if_fail (i < LAST_ACTION, FALSE);
2592 priv = accessible_get_priv (ATK_OBJECT (accessible));
2594 if (priv->action_descriptions[i]) {
2595 g_free (priv->action_descriptions[i]);
2597 priv->action_descriptions[i] = g_strdup (description);
2599 return TRUE;
2602 static void
2603 nautilus_icon_canvas_item_accessible_action_interface_init (AtkActionIface *iface)
2605 iface->do_action = nautilus_icon_canvas_item_accessible_do_action;
2606 iface->get_n_actions = nautilus_icon_canvas_item_accessible_get_n_actions;
2607 iface->get_description = nautilus_icon_canvas_item_accessible_action_get_description;
2608 iface->get_keybinding = nautilus_icon_canvas_item_accessible_action_get_keybinding;
2609 iface->get_name = nautilus_icon_canvas_item_accessible_action_get_name;
2610 iface->set_description = nautilus_icon_canvas_item_accessible_action_set_description;
2613 static G_CONST_RETURN gchar *
2614 nautilus_icon_canvas_item_accessible_get_name (AtkObject *accessible)
2616 NautilusIconCanvasItem *item;
2618 if (accessible->name) {
2619 return accessible->name;
2622 item = eel_accessibility_get_gobject (accessible);
2623 if (!item) {
2624 return NULL;
2626 return item->details->editable_text;
2629 static G_CONST_RETURN gchar*
2630 nautilus_icon_canvas_item_accessible_get_description (AtkObject *accessible)
2632 NautilusIconCanvasItem *item;
2634 item = eel_accessibility_get_gobject (accessible);
2635 if (!item) {
2636 return NULL;
2639 return item->details->additional_text;
2642 static AtkObject *
2643 nautilus_icon_canvas_item_accessible_get_parent (AtkObject *accessible)
2645 NautilusIconCanvasItem *item;
2647 item = eel_accessibility_get_gobject (accessible);
2648 if (!item) {
2649 return NULL;
2652 return gtk_widget_get_accessible (GTK_WIDGET (EEL_CANVAS_ITEM (item)->canvas));
2655 static int
2656 nautilus_icon_canvas_item_accessible_get_index_in_parent (AtkObject *accessible)
2658 NautilusIconCanvasItem *item;
2659 NautilusIconContainer *container;
2660 GList *l;
2661 NautilusIcon *icon;
2662 int i;
2664 item = eel_accessibility_get_gobject (accessible);
2665 if (!item) {
2666 return -1;
2669 container = NAUTILUS_ICON_CONTAINER (EEL_CANVAS_ITEM (item)->canvas);
2671 l = container->details->icons;
2672 i = 0;
2673 while (l) {
2674 icon = l->data;
2676 if (icon->item == item) {
2677 return i;
2680 i++;
2681 l = l->next;
2684 return -1;
2687 static AtkStateSet*
2688 nautilus_icon_canvas_item_accessible_ref_state_set (AtkObject *accessible)
2690 AtkStateSet *state_set;
2691 NautilusIconCanvasItem *item;
2692 NautilusIconContainer *container;
2693 NautilusIcon *icon;
2694 GList *l;
2695 gboolean one_item_selected;
2697 state_set = ATK_OBJECT_CLASS (accessible_parent_class)->ref_state_set (accessible);
2699 item = eel_accessibility_get_gobject (accessible);
2700 if (!item) {
2701 atk_state_set_add_state (state_set, ATK_STATE_DEFUNCT);
2702 return state_set;
2704 container = NAUTILUS_ICON_CONTAINER (EEL_CANVAS_ITEM (item)->canvas);
2705 if (item->details->is_highlighted_as_keyboard_focus) {
2706 atk_state_set_add_state (state_set, ATK_STATE_FOCUSED);
2707 } else if (!container->details->keyboard_focus) {
2709 one_item_selected = FALSE;
2710 l = container->details->icons;
2711 while (l) {
2712 icon = l->data;
2714 if (icon->item == item) {
2715 if (icon->is_selected) {
2716 one_item_selected = TRUE;
2717 } else {
2718 break;
2720 } else if (icon->is_selected) {
2721 one_item_selected = FALSE;
2722 break;
2725 l = l->next;
2728 if (one_item_selected) {
2729 atk_state_set_add_state (state_set, ATK_STATE_FOCUSED);
2733 return state_set;
2736 static void
2737 nautilus_icon_canvas_item_accessible_initialize (AtkObject *accessible,
2738 gpointer data)
2740 NautilusIconCanvasItemAccessiblePrivate *priv;
2742 if (ATK_OBJECT_CLASS (accessible_parent_class)->initialize) {
2743 ATK_OBJECT_CLASS (accessible_parent_class)->initialize (accessible, data);
2746 priv = g_new0 (NautilusIconCanvasItemAccessiblePrivate, 1);
2747 g_object_set_qdata (G_OBJECT (accessible),
2748 accessible_private_data_quark,
2749 priv);
2752 static void
2753 nautilus_icon_canvas_item_accessible_finalize (GObject *object)
2755 NautilusIconCanvasItemAccessiblePrivate *priv;
2756 int i;
2758 priv = accessible_get_priv (ATK_OBJECT (object));
2760 for (i = 0; i < LAST_ACTION; i++) {
2761 g_free (priv->action_descriptions[i]);
2763 g_free (priv->image_description);
2764 g_free (priv->description);
2766 g_free (priv);
2768 G_OBJECT_CLASS (accessible_parent_class)->finalize (object);
2771 static void
2772 nautilus_icon_canvas_item_accessible_class_init (AtkObjectClass *klass)
2774 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
2776 accessible_parent_class = g_type_class_peek_parent (klass);
2778 gobject_class->finalize = nautilus_icon_canvas_item_accessible_finalize;
2780 klass->get_name = nautilus_icon_canvas_item_accessible_get_name;
2781 klass->get_description = nautilus_icon_canvas_item_accessible_get_description;
2782 klass->get_parent = nautilus_icon_canvas_item_accessible_get_parent;
2783 klass->get_index_in_parent = nautilus_icon_canvas_item_accessible_get_index_in_parent;
2784 klass->ref_state_set = nautilus_icon_canvas_item_accessible_ref_state_set;
2785 klass->initialize = nautilus_icon_canvas_item_accessible_initialize;
2786 accessible_private_data_quark = g_quark_from_static_string ("icon-canvas-item-accessible-private-data");
2790 static G_CONST_RETURN gchar *
2791 nautilus_icon_canvas_item_accessible_get_image_description
2792 (AtkImage *image)
2794 NautilusIconCanvasItemAccessiblePrivate *priv;
2795 NautilusIconCanvasItem *item;
2796 NautilusIcon *icon;
2797 NautilusIconContainer *container;
2798 char *description;
2800 priv = accessible_get_priv (ATK_OBJECT (image));
2801 if (priv->image_description) {
2802 return priv->image_description;
2803 } else {
2804 item = eel_accessibility_get_gobject (ATK_OBJECT (image));
2805 if (item == NULL) {
2806 return NULL;
2808 icon = item->user_data;
2809 container = NAUTILUS_ICON_CONTAINER (EEL_CANVAS_ITEM (item)->canvas);
2810 description = nautilus_icon_container_get_icon_description (container, icon->data);
2811 g_free (priv->description);
2812 priv->description = description;
2813 return priv->description;
2817 static void
2818 nautilus_icon_canvas_item_accessible_get_image_size
2819 (AtkImage *image,
2820 gint *width,
2821 gint *height)
2823 NautilusIconCanvasItem *item;
2825 item = eel_accessibility_get_gobject (ATK_OBJECT (image));
2827 if (!item || !item->details->pixbuf) {
2828 *width = *height = 0;
2829 } else {
2830 *width = gdk_pixbuf_get_width (item->details->pixbuf);
2831 *height = gdk_pixbuf_get_height (item->details->pixbuf);
2835 static void
2836 nautilus_icon_canvas_item_accessible_get_image_position
2837 (AtkImage *image,
2838 gint *x,
2839 gint *y,
2840 AtkCoordType coord_type)
2842 NautilusIconCanvasItem *item;
2843 gint x_offset, y_offset, itmp;
2845 item = eel_accessibility_get_gobject (ATK_OBJECT (image));
2846 if (!item) {
2847 return;
2849 if (!item->details->canvas_rect.x0 && !item->details->canvas_rect.x1) {
2850 return;
2851 } else {
2852 x_offset = 0;
2853 y_offset = 0;
2854 if (item->details->text_width) {
2855 itmp = item->details->canvas_rect.x0 -
2856 item->details->text_rect.x0;
2857 if (itmp > x_offset) {
2858 x_offset = itmp;
2860 itmp = item->details->canvas_rect.y0 -
2861 item->details->text_rect.y0;
2862 if (itmp > y_offset) {
2863 y_offset = itmp;
2866 if (item->details->emblem_pixbufs) {
2867 itmp = item->details->canvas_rect.x0 -
2868 item->details->emblem_rect.x0;
2869 if (itmp > x_offset) {
2870 x_offset = itmp;
2872 itmp = item->details->canvas_rect.y0 -
2873 item->details->emblem_rect.y0;
2874 if (itmp > y_offset) {
2875 y_offset = itmp;
2879 atk_component_get_position (ATK_COMPONENT (image), x, y, coord_type);
2880 *x += x_offset;
2881 *y += y_offset;
2884 static gboolean
2885 nautilus_icon_canvas_item_accessible_set_image_description
2886 (AtkImage *image,
2887 const gchar *description)
2889 NautilusIconCanvasItemAccessiblePrivate *priv;
2891 priv = accessible_get_priv (ATK_OBJECT (image));
2893 g_free (priv->image_description);
2894 priv->image_description = g_strdup (description);
2896 return TRUE;
2899 static void
2900 nautilus_icon_canvas_item_accessible_image_interface_init (AtkImageIface *iface)
2902 iface->get_image_description = nautilus_icon_canvas_item_accessible_get_image_description;
2903 iface->set_image_description = nautilus_icon_canvas_item_accessible_set_image_description;
2904 iface->get_image_size = nautilus_icon_canvas_item_accessible_get_image_size;
2905 iface->get_image_position = nautilus_icon_canvas_item_accessible_get_image_position;
2908 static gint
2909 nautilus_icon_canvas_item_accessible_get_offset_at_point (AtkText *text,
2910 gint x,
2911 gint y,
2912 AtkCoordType coords)
2914 gint real_x, real_y, real_width, real_height;
2915 NautilusIconCanvasItem *item;
2916 gint editable_height;
2917 gint offset = 0;
2918 gint index;
2919 PangoLayout *layout, *editable_layout, *additional_layout;
2920 PangoRectangle rect0;
2921 char *icon_text;
2922 gboolean have_editable;
2923 gboolean have_additional;
2924 gint text_offset;
2926 atk_component_get_extents (ATK_COMPONENT (text), &real_x, &real_y,
2927 &real_width, &real_height, coords);
2929 x -= real_x;
2930 y -= real_y;
2932 item = eel_accessibility_get_gobject (ATK_OBJECT (text));
2934 if (item->details->pixbuf) {
2935 y -= gdk_pixbuf_get_height (item->details->pixbuf);
2937 have_editable = item->details->editable_text != NULL &&
2938 item->details->editable_text[0] != '\0';
2939 have_additional = item->details->additional_text != NULL &&item->details->additional_text[0] != '\0';
2941 editable_layout = NULL;
2942 additional_layout = NULL;
2943 if (have_editable) {
2944 editable_layout = get_label_layout (&item->details->editable_text_layout, item, item->details->editable_text);
2945 pango_layout_get_pixel_size (editable_layout, NULL, &editable_height);
2946 if (y >= editable_height &&
2947 have_additional) {
2948 additional_layout = get_label_layout (&item->details->additional_text_layout, item, item->details->additional_text);
2949 layout = additional_layout;
2950 icon_text = item->details->additional_text;
2951 y -= editable_height + LABEL_LINE_SPACING;
2952 } else {
2953 layout = editable_layout;
2954 icon_text = item->details->editable_text;
2956 } else if (have_additional) {
2957 additional_layout = get_label_layout (&item->details->additional_text_layout, item, item->details->additional_text);
2958 layout = additional_layout;
2959 icon_text = item->details->additional_text;
2960 } else {
2961 return 0;
2964 text_offset = 0;
2965 if (have_editable) {
2966 pango_layout_index_to_pos (editable_layout, 0, &rect0);
2967 text_offset = PANGO_PIXELS (rect0.x);
2969 if (have_additional) {
2970 gint itmp;
2972 pango_layout_index_to_pos (additional_layout, 0, &rect0);
2973 itmp = PANGO_PIXELS (rect0.x);
2974 if (itmp < text_offset) {
2975 text_offset = itmp;
2978 pango_layout_index_to_pos (layout, 0, &rect0);
2979 x += text_offset;
2980 if (!pango_layout_xy_to_index (layout,
2981 x * PANGO_SCALE,
2982 y * PANGO_SCALE,
2983 &index, NULL)) {
2984 if (x < 0 || y < 0) {
2985 index = 0;
2986 } else {
2987 index = -1;
2990 if (index == -1) {
2991 offset = g_utf8_strlen (icon_text, -1);
2992 } else {
2993 offset = g_utf8_pointer_to_offset (icon_text, icon_text + index);
2995 if (layout == additional_layout) {
2996 offset += g_utf8_strlen (item->details->editable_text, -1);
2999 if (editable_layout != NULL) {
3000 g_object_unref (editable_layout);
3003 if (additional_layout != NULL) {
3004 g_object_unref (additional_layout);
3007 return offset;
3010 static void
3011 nautilus_icon_canvas_item_accessible_get_character_extents (AtkText *text,
3012 gint offset,
3013 gint *x,
3014 gint *y,
3015 gint *width,
3016 gint *height,
3017 AtkCoordType coords)
3019 gint pos_x, pos_y;
3020 gint len, byte_offset;
3021 gint editable_height;
3022 gchar *icon_text;
3023 NautilusIconCanvasItem *item;
3024 PangoLayout *layout, *editable_layout, *additional_layout;
3025 PangoRectangle rect;
3026 PangoRectangle rect0;
3027 gboolean have_editable;
3028 gint text_offset;
3030 atk_component_get_position (ATK_COMPONENT (text), &pos_x, &pos_y, coords);
3031 item = eel_accessibility_get_gobject (ATK_OBJECT (text));
3033 if (item->details->pixbuf) {
3034 pos_y += gdk_pixbuf_get_height (item->details->pixbuf);
3037 have_editable = item->details->editable_text != NULL &&
3038 item->details->editable_text[0] != '\0';
3039 if (have_editable) {
3040 len = g_utf8_strlen (item->details->editable_text, -1);
3041 } else {
3042 len = 0;
3045 editable_layout = get_label_layout (&item->details->editable_text_layout, item, item->details->editable_text);
3046 additional_layout = get_label_layout (&item->details->additional_text_layout, item, item->details->additional_text);
3048 if (offset < len) {
3049 icon_text = item->details->editable_text;
3050 layout = editable_layout;
3051 } else {
3052 offset -= len;
3053 icon_text = item->details->additional_text;
3054 layout = additional_layout;
3055 pos_y += LABEL_LINE_SPACING;
3056 if (have_editable) {
3057 pango_layout_get_pixel_size (editable_layout, NULL, &editable_height);
3058 pos_y += editable_height;
3061 byte_offset = g_utf8_offset_to_pointer (icon_text, offset) - icon_text;
3062 pango_layout_index_to_pos (layout, byte_offset, &rect);
3063 text_offset = 0;
3064 if (have_editable) {
3065 pango_layout_index_to_pos (editable_layout, 0, &rect0);
3066 text_offset = PANGO_PIXELS (rect0.x);
3068 if (item->details->additional_text != NULL &&
3069 item->details->additional_text[0] != '\0') {
3070 gint itmp;
3072 pango_layout_index_to_pos (additional_layout, 0, &rect0);
3073 itmp = PANGO_PIXELS (rect0.x);
3074 if (itmp < text_offset) {
3075 text_offset = itmp;
3079 g_object_unref (editable_layout);
3080 g_object_unref (additional_layout);
3082 *x = pos_x + PANGO_PIXELS (rect.x) - text_offset;
3083 *y = pos_y + PANGO_PIXELS (rect.y);
3084 *width = PANGO_PIXELS (rect.width);
3085 *height = PANGO_PIXELS (rect.height);
3088 static void
3089 nautilus_icon_canvas_item_accessible_text_interface_init (AtkTextIface *iface)
3091 iface->get_text = eel_accessibility_text_get_text;
3092 iface->get_character_at_offset = eel_accessibility_text_get_character_at_offset;
3093 iface->get_text_before_offset = eel_accessibility_text_get_text_before_offset;
3094 iface->get_text_at_offset = eel_accessibility_text_get_text_at_offset;
3095 iface->get_text_after_offset = eel_accessibility_text_get_text_after_offset;
3096 iface->get_character_count = eel_accessibility_text_get_character_count;
3097 iface->get_character_extents = nautilus_icon_canvas_item_accessible_get_character_extents;
3098 iface->get_offset_at_point = nautilus_icon_canvas_item_accessible_get_offset_at_point;
3101 static GType
3102 nautilus_icon_canvas_item_accessible_get_type (void)
3104 static GType type = 0;
3106 if (!type) {
3107 const GInterfaceInfo atk_image_info = {
3108 (GInterfaceInitFunc)
3109 nautilus_icon_canvas_item_accessible_image_interface_init,
3110 (GInterfaceFinalizeFunc) NULL,
3111 NULL
3114 const GInterfaceInfo atk_text_info = {
3115 (GInterfaceInitFunc)
3116 nautilus_icon_canvas_item_accessible_text_interface_init,
3117 (GInterfaceFinalizeFunc) NULL,
3118 NULL
3121 const GInterfaceInfo atk_action_info = {
3122 (GInterfaceInitFunc)
3123 nautilus_icon_canvas_item_accessible_action_interface_init,
3124 (GInterfaceFinalizeFunc) NULL,
3125 NULL
3128 type = eel_accessibility_create_derived_type (
3129 "NautilusIconCanvasItemAccessibility",
3130 EEL_TYPE_CANVAS_ITEM,
3131 nautilus_icon_canvas_item_accessible_class_init);
3133 if (type != G_TYPE_INVALID) {
3134 g_type_add_interface_static (
3135 type, ATK_TYPE_IMAGE, &atk_image_info);
3137 g_type_add_interface_static (
3138 type, ATK_TYPE_TEXT, &atk_text_info);
3140 g_type_add_interface_static (
3141 type, ATK_TYPE_ACTION, &atk_action_info);
3146 return type;
3149 static AtkObject *
3150 nautilus_icon_canvas_item_accessible_create (GObject *for_object)
3152 GType type;
3153 AtkObject *accessible;
3154 NautilusIconCanvasItem *item;
3155 GString *item_text;
3157 item = NAUTILUS_ICON_CANVAS_ITEM (for_object);
3158 g_return_val_if_fail (item != NULL, NULL);
3160 type = nautilus_icon_canvas_item_accessible_get_type ();
3162 if (type == G_TYPE_INVALID) {
3163 return atk_no_op_object_new (for_object);
3166 item_text = g_string_new (NULL);
3167 if (item->details->editable_text) {
3168 g_string_append (item_text, item->details->editable_text);
3170 if (item->details->additional_text) {
3171 g_string_append (item_text, item->details->additional_text);
3173 item->details->text_util = gail_text_util_new ();
3174 gail_text_util_text_setup (item->details->text_util,
3175 item_text->str);
3176 g_string_free (item_text, TRUE);
3178 accessible = g_object_new (type, NULL);
3179 accessible = eel_accessibility_set_atk_object_return
3180 (for_object, accessible);
3181 atk_object_set_role (accessible, ATK_ROLE_ICON);
3182 return accessible;
3185 EEL_ACCESSIBLE_FACTORY (nautilus_icon_canvas_item_accessible_get_type (),
3186 "NautilusIconCanvasItemAccessibilityFactory",
3187 nautilus_icon_canvas_item_accessible,
3188 nautilus_icon_canvas_item_accessible_create)
3191 static GailTextUtil *
3192 nautilus_icon_canvas_item_get_text (GObject *text)
3194 return NAUTILUS_ICON_CANVAS_ITEM (text)->details->text_util;
3197 static void
3198 nautilus_icon_canvas_item_text_interface_init (EelAccessibleTextIface *iface)
3200 iface->get_text = nautilus_icon_canvas_item_get_text;
3203 /* Class initialization function for the icon canvas item. */
3204 static void
3205 nautilus_icon_canvas_item_class_init (NautilusIconCanvasItemClass *class)
3207 GObjectClass *object_class;
3208 EelCanvasItemClass *item_class;
3210 parent_class = g_type_class_peek_parent (class);
3212 object_class = G_OBJECT_CLASS (class);
3213 item_class = EEL_CANVAS_ITEM_CLASS (class);
3215 object_class->finalize = nautilus_icon_canvas_item_finalize;
3216 object_class->set_property = nautilus_icon_canvas_item_set_property;
3217 object_class->get_property = nautilus_icon_canvas_item_get_property;
3219 g_object_class_install_property (
3220 object_class,
3221 PROP_EDITABLE_TEXT,
3222 g_param_spec_string ("editable_text",
3223 "editable text",
3224 "the editable label",
3225 "", G_PARAM_READWRITE));
3227 g_object_class_install_property (
3228 object_class,
3229 PROP_ADDITIONAL_TEXT,
3230 g_param_spec_string ("additional_text",
3231 "additional text",
3232 "some more text",
3233 "", G_PARAM_READWRITE));
3235 g_object_class_install_property (
3236 object_class,
3237 PROP_HIGHLIGHTED_FOR_SELECTION,
3238 g_param_spec_boolean ("highlighted_for_selection",
3239 "highlighted for selection",
3240 "whether we are highlighted for a selection",
3241 FALSE, G_PARAM_READWRITE));
3243 g_object_class_install_property (
3244 object_class,
3245 PROP_HIGHLIGHTED_AS_KEYBOARD_FOCUS,
3246 g_param_spec_boolean ("highlighted_as_keyboard_focus",
3247 "highlighted as keyboard focus",
3248 "whether we are highlighted to render keyboard focus",
3249 FALSE, G_PARAM_READWRITE));
3252 g_object_class_install_property (
3253 object_class,
3254 PROP_HIGHLIGHTED_FOR_DROP,
3255 g_param_spec_boolean ("highlighted_for_drop",
3256 "highlighted for drop",
3257 "whether we are highlighted for a D&D drop",
3258 FALSE, G_PARAM_READWRITE));
3260 item_class->update = nautilus_icon_canvas_item_update;
3261 item_class->draw = nautilus_icon_canvas_item_draw;
3262 item_class->point = nautilus_icon_canvas_item_point;
3263 item_class->translate = nautilus_icon_canvas_item_translate;
3264 item_class->bounds = nautilus_icon_canvas_item_bounds;
3265 item_class->event = nautilus_icon_canvas_item_event;
3267 EEL_OBJECT_SET_FACTORY (NAUTILUS_TYPE_ICON_CANVAS_ITEM,
3268 nautilus_icon_canvas_item_accessible);
3270 g_type_class_add_private (class, sizeof (NautilusIconCanvasItemDetails));
3273 GType
3274 nautilus_icon_canvas_item_get_type (void)
3276 static GType type = 0;
3278 if (!type) {
3279 const GTypeInfo info = {
3280 sizeof (NautilusIconCanvasItemClass),
3281 NULL, /* base_init */
3282 NULL, /* base_finalize */
3283 (GClassInitFunc) nautilus_icon_canvas_item_class_init,
3284 NULL, /* class_finalize */
3285 NULL, /* class_data */
3286 sizeof (NautilusIconCanvasItem),
3287 0, /* n_preallocs */
3288 (GInstanceInitFunc) nautilus_icon_canvas_item_init,
3290 const GInterfaceInfo eel_text_info = {
3291 (GInterfaceInitFunc)
3292 nautilus_icon_canvas_item_text_interface_init,
3293 (GInterfaceFinalizeFunc) NULL,
3294 NULL
3297 type = g_type_register_static
3298 (EEL_TYPE_CANVAS_ITEM, "NautilusIconCanvasItem", &info, 0);
3300 g_type_add_interface_static
3301 (type, EEL_TYPE_ACCESSIBLE_TEXT, &eel_text_info);
3304 return type;