No issues found
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2
3 /* Nautilus - Canvas item class for canvas container.
4 *
5 * Copyright (C) 2000 Eazel, Inc
6 *
7 * Author: Andy Hertzfeld <andy@eazel.com>
8 *
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.
13 *
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.
18 *
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.
23 */
24
25 #include <config.h>
26 #include <math.h>
27 #include "nautilus-canvas-item.h"
28
29 #include <glib/gi18n.h>
30
31 #include "nautilus-file-utilities.h"
32 #include "nautilus-global-preferences.h"
33 #include "nautilus-canvas-private.h"
34 #include <eel/eel-art-extensions.h>
35 #include <eel/eel-gdk-extensions.h>
36 #include <eel/eel-glib-extensions.h>
37 #include <eel/eel-graphic-effects.h>
38 #include <eel/eel-string.h>
39 #include <eel/eel-accessibility.h>
40 #include <gdk-pixbuf/gdk-pixbuf.h>
41 #include <gtk/gtk.h>
42 #include <gdk/gdk.h>
43 #include <glib/gi18n.h>
44 #include <atk/atkimage.h>
45 #include <atk/atkcomponent.h>
46 #include <atk/atknoopobject.h>
47 #include <stdio.h>
48 #include <string.h>
49
50 /* gap between bottom of icon and start of text box */
51 #define LABEL_OFFSET 1
52 #define LABEL_LINE_SPACING 0
53
54 #define MAX_TEXT_WIDTH_STANDARD 135
55
56 /* special text height handling
57 * each item has three text height variables:
58 * + text_height: actual height of the displayed (i.e. on-screen) PangoLayout.
59 * + text_height_for_layout: height used in canvas grid layout algorithms.
60 * “sane amount” of text.
61 * “sane amount“ as of
62 * + hard-coded to three lines in text-below-icon mode.
63 *
64 * This layout height is used by grid layout algorithms, even
65 * though the actually displayed and/or requested text size may be larger
66 * and overlap adjacent icons, if an icon is selected.
67 *
68 * + text_height_for_entire_text: height needed to display the entire PangoLayout,
69 * if it wasn't ellipsized.
70 */
71
72 /* Private part of the NautilusCanvasItem structure. */
73 struct NautilusCanvasItemDetails {
74 /* The image, text, font. */
75 double x, y;
76 GdkPixbuf *pixbuf;
77 GdkPixbuf *rendered_pixbuf;
78 char *editable_text; /* Text that can be modified by a renaming function */
79 char *additional_text; /* Text that cannot be modifed, such as file size, etc. */
80 GdkPoint *attach_points;
81 int n_attach_points;
82
83 /* Size of the text at current font. */
84 int text_dx;
85 int text_width;
86
87 /* actual size required for rendering the text to display */
88 int text_height;
89 /* actual size that would be required for rendering the entire text if it wasn't ellipsized */
90 int text_height_for_entire_text;
91 /* actual size needed for rendering a “sane amount” of text */
92 int text_height_for_layout;
93
94 int editable_text_height;
95
96 /* whether the entire text must always be visible. In that case,
97 * text_height_for_layout will always be equal to text_height.
98 * Used for the last line of a line-wise icon layout. */
99 guint entire_text : 1;
100
101 /* Highlight state. */
102 guint is_highlighted_for_selection : 1;
103 guint is_highlighted_as_keyboard_focus: 1;
104 guint is_highlighted_for_drop : 1;
105 guint is_highlighted_for_clipboard : 1;
106 guint show_stretch_handles : 1;
107 guint is_prelit : 1;
108
109 guint rendered_is_highlighted_for_selection : 1;
110 guint rendered_is_highlighted_for_drop : 1;
111 guint rendered_is_highlighted_for_clipboard : 1;
112 guint rendered_is_prelit : 1;
113 guint rendered_is_focused : 1;
114
115 guint is_renaming : 1;
116
117 guint bounds_cached : 1;
118
119 guint is_visible : 1;
120
121 GdkRectangle embedded_text_rect;
122 char *embedded_text;
123
124 /* Cached PangoLayouts. Only used if the icon is visible */
125 PangoLayout *editable_text_layout;
126 PangoLayout *additional_text_layout;
127 PangoLayout *embedded_text_layout;
128
129 /* Cached rectangle in canvas coordinates */
130 EelIRect icon_rect;
131 EelIRect text_rect;
132
133 EelIRect bounds_cache;
134 EelIRect bounds_cache_for_layout;
135 EelIRect bounds_cache_for_entire_item;
136
137 GdkWindow *cursor_window;
138
139 /* Accessibility bits */
140 GailTextUtil *text_util;
141 };
142
143 /* Object argument IDs. */
144 enum {
145 PROP_0,
146 PROP_EDITABLE_TEXT,
147 PROP_ADDITIONAL_TEXT,
148 PROP_HIGHLIGHTED_FOR_SELECTION,
149 PROP_HIGHLIGHTED_AS_KEYBOARD_FOCUS,
150 PROP_HIGHLIGHTED_FOR_DROP,
151 PROP_HIGHLIGHTED_FOR_CLIPBOARD
152 };
153
154 typedef enum {
155 RIGHT_SIDE,
156 BOTTOM_SIDE,
157 LEFT_SIDE,
158 TOP_SIDE
159 } RectangleSide;
160
161 static void nautilus_canvas_item_text_interface_init (EelAccessibleTextIface *iface);
162 static GType nautilus_canvas_item_accessible_factory_get_type (void);
163
164 G_DEFINE_TYPE_WITH_CODE (NautilusCanvasItem, nautilus_canvas_item, EEL_TYPE_CANVAS_ITEM,
165 G_IMPLEMENT_INTERFACE (EEL_TYPE_ACCESSIBLE_TEXT,
166 nautilus_canvas_item_text_interface_init));
167
168 /* private */
169 static void draw_label_text (NautilusCanvasItem *item,
170 cairo_t *cr,
171 EelIRect icon_rect);
172 static void measure_label_text (NautilusCanvasItem *item);
173 static void get_icon_rectangle (NautilusCanvasItem *item,
174 EelIRect *rect);
175 static void draw_pixbuf (GdkPixbuf *pixbuf,
176 cairo_t *cr,
177 int x,
178 int y);
179 static PangoLayout *get_label_layout (PangoLayout **layout,
180 NautilusCanvasItem *item,
181 const char *text);
182 static gboolean hit_test_stretch_handle (NautilusCanvasItem *item,
183 EelIRect icon_rect,
184 GtkCornerType *corner);
185 static void draw_embedded_text (NautilusCanvasItem *canvas_item,
186 cairo_t *cr,
187 int x,
188 int y);
189
190 static void nautilus_canvas_item_ensure_bounds_up_to_date (NautilusCanvasItem *canvas_item);
191
192 /* Object initialization function for the canvas item. */
193 static void
194 nautilus_canvas_item_init (NautilusCanvasItem *canvas_item)
195 {
196 canvas_item->details = G_TYPE_INSTANCE_GET_PRIVATE ((canvas_item), NAUTILUS_TYPE_CANVAS_ITEM, NautilusCanvasItemDetails);
197 nautilus_canvas_item_invalidate_label_size (canvas_item);
198 }
199
200 static void
201 nautilus_canvas_item_finalize (GObject *object)
202 {
203 NautilusCanvasItemDetails *details;
204
205 g_assert (NAUTILUS_IS_CANVAS_ITEM (object));
206
207 details = NAUTILUS_CANVAS_ITEM (object)->details;
208
209 if (details->cursor_window != NULL) {
210 gdk_window_set_cursor (details->cursor_window, NULL);
211 g_object_unref (details->cursor_window);
212 }
213
214 if (details->pixbuf != NULL) {
215 g_object_unref (details->pixbuf);
216 }
217
218 if (details->text_util != NULL) {
219 g_object_unref (details->text_util);
220 }
221
222 g_free (details->editable_text);
223 g_free (details->additional_text);
224 g_free (details->attach_points);
225
226 if (details->rendered_pixbuf != NULL) {
227 g_object_unref (details->rendered_pixbuf);
228 }
229
230 if (details->editable_text_layout != NULL) {
231 g_object_unref (details->editable_text_layout);
232 }
233
234 if (details->additional_text_layout != NULL) {
235 g_object_unref (details->additional_text_layout);
236 }
237
238 if (details->embedded_text_layout != NULL) {
239 g_object_unref (details->embedded_text_layout);
240 }
241
242 g_free (details->embedded_text);
243
244 G_OBJECT_CLASS (nautilus_canvas_item_parent_class)->finalize (object);
245 }
246
247 /* Currently we require pixbufs in this format (for hit testing).
248 * Perhaps gdk-pixbuf will be changed so it can do the hit testing
249 * and we won't have this requirement any more.
250 */
251 static gboolean
252 pixbuf_is_acceptable (GdkPixbuf *pixbuf)
253 {
254 return gdk_pixbuf_get_colorspace (pixbuf) == GDK_COLORSPACE_RGB
255 && ((!gdk_pixbuf_get_has_alpha (pixbuf)
256 && gdk_pixbuf_get_n_channels (pixbuf) == 3)
257 || (gdk_pixbuf_get_has_alpha (pixbuf)
258 && gdk_pixbuf_get_n_channels (pixbuf) == 4))
259 && gdk_pixbuf_get_bits_per_sample (pixbuf) == 8;
260 }
261
262 static void
263 nautilus_canvas_item_invalidate_bounds_cache (NautilusCanvasItem *item)
264 {
265 item->details->bounds_cached = FALSE;
266 }
267
268 /* invalidate the text width and height cached in the item details. */
269 void
270 nautilus_canvas_item_invalidate_label_size (NautilusCanvasItem *item)
271 {
272 if (item->details->editable_text_layout != NULL) {
273 pango_layout_context_changed (item->details->editable_text_layout);
274 }
275 if (item->details->additional_text_layout != NULL) {
276 pango_layout_context_changed (item->details->additional_text_layout);
277 }
278 if (item->details->embedded_text_layout != NULL) {
279 pango_layout_context_changed (item->details->embedded_text_layout);
280 }
281 nautilus_canvas_item_invalidate_bounds_cache (item);
282 item->details->text_width = -1;
283 item->details->text_height = -1;
284 item->details->text_height_for_layout = -1;
285 item->details->text_height_for_entire_text = -1;
286 item->details->editable_text_height = -1;
287 }
288
289 /* Set property handler for the canvas item. */
290 static void
291 nautilus_canvas_item_set_property (GObject *object,
292 guint property_id,
293 const GValue *value,
294 GParamSpec *pspec)
295 {
296 NautilusCanvasItem *item;
297 NautilusCanvasItemDetails *details;
298 AtkObject *accessible;
299
300 item = NAUTILUS_CANVAS_ITEM (object);
301 accessible = atk_gobject_accessible_for_object (G_OBJECT (item));
302 details = item->details;
303
304 switch (property_id) {
305
306 case PROP_EDITABLE_TEXT:
307 if (g_strcmp0 (details->editable_text,
308 g_value_get_string (value)) == 0) {
309 return;
310 }
311
312 g_free (details->editable_text);
313 details->editable_text = g_strdup (g_value_get_string (value));
314 if (details->text_util) {
315 gail_text_util_text_setup (details->text_util,
316 details->editable_text);
317 g_object_notify (G_OBJECT(accessible), "accessible-name");
318 }
319
320 nautilus_canvas_item_invalidate_label_size (item);
321 if (details->editable_text_layout) {
322 g_object_unref (details->editable_text_layout);
323 details->editable_text_layout = NULL;
324 }
325 break;
326
327 case PROP_ADDITIONAL_TEXT:
328 if (g_strcmp0 (details->additional_text,
329 g_value_get_string (value)) == 0) {
330 return;
331 }
332
333 g_free (details->additional_text);
334 details->additional_text = g_strdup (g_value_get_string (value));
335
336 nautilus_canvas_item_invalidate_label_size (item);
337 if (details->additional_text_layout) {
338 g_object_unref (details->additional_text_layout);
339 details->additional_text_layout = NULL;
340 }
341 break;
342
343 case PROP_HIGHLIGHTED_FOR_SELECTION:
344 if (!details->is_highlighted_for_selection == !g_value_get_boolean (value)) {
345 return;
346 }
347 details->is_highlighted_for_selection = g_value_get_boolean (value);
348 nautilus_canvas_item_invalidate_label_size (item);
349
350 atk_object_notify_state_change (accessible, ATK_STATE_SELECTED,
351 details->is_highlighted_for_selection);
352
353 break;
354
355 case PROP_HIGHLIGHTED_AS_KEYBOARD_FOCUS:
356 if (!details->is_highlighted_as_keyboard_focus == !g_value_get_boolean (value)) {
357 return;
358 }
359 details->is_highlighted_as_keyboard_focus = g_value_get_boolean (value);
360
361 if (details->is_highlighted_as_keyboard_focus) {
362 atk_focus_tracker_notify (accessible);
363 }
364 break;
365
366 case PROP_HIGHLIGHTED_FOR_DROP:
367 if (!details->is_highlighted_for_drop == !g_value_get_boolean (value)) {
368 return;
369 }
370 details->is_highlighted_for_drop = g_value_get_boolean (value);
371 break;
372
373 case PROP_HIGHLIGHTED_FOR_CLIPBOARD:
374 if (!details->is_highlighted_for_clipboard == !g_value_get_boolean (value)) {
375 return;
376 }
377 details->is_highlighted_for_clipboard = g_value_get_boolean (value);
378 break;
379
380 default:
381 g_warning ("nautilus_canvas_item_set_property on unknown argument");
382 return;
383 }
384
385 eel_canvas_item_request_update (EEL_CANVAS_ITEM (object));
386 }
387
388 /* Get property handler for the canvas item */
389 static void
390 nautilus_canvas_item_get_property (GObject *object,
391 guint property_id,
392 GValue *value,
393 GParamSpec *pspec)
394 {
395 NautilusCanvasItemDetails *details;
396
397 details = NAUTILUS_CANVAS_ITEM (object)->details;
398
399 switch (property_id) {
400
401 case PROP_EDITABLE_TEXT:
402 g_value_set_string (value, details->editable_text);
403 break;
404
405 case PROP_ADDITIONAL_TEXT:
406 g_value_set_string (value, details->additional_text);
407 break;
408
409 case PROP_HIGHLIGHTED_FOR_SELECTION:
410 g_value_set_boolean (value, details->is_highlighted_for_selection);
411 break;
412
413 case PROP_HIGHLIGHTED_AS_KEYBOARD_FOCUS:
414 g_value_set_boolean (value, details->is_highlighted_as_keyboard_focus);
415 break;
416
417 case PROP_HIGHLIGHTED_FOR_DROP:
418 g_value_set_boolean (value, details->is_highlighted_for_drop);
419 break;
420
421 case PROP_HIGHLIGHTED_FOR_CLIPBOARD:
422 g_value_set_boolean (value, details->is_highlighted_for_clipboard);
423 break;
424
425 default:
426 g_warning ("invalid property %d", property_id);
427 break;
428 }
429 }
430
431 cairo_surface_t *
432 nautilus_canvas_item_get_drag_surface (NautilusCanvasItem *item)
433 {
434 cairo_surface_t *surface;
435 EelCanvas *canvas;
436 GdkScreen *screen;
437 int width, height;
438 int item_offset_x, item_offset_y;
439 EelIRect icon_rect;
440 double item_x, item_y;
441 cairo_t *cr;
442 GtkStyleContext *context;
443
444 g_return_val_if_fail (NAUTILUS_IS_CANVAS_ITEM (item), NULL);
445
446 canvas = EEL_CANVAS_ITEM (item)->canvas;
447 screen = gtk_widget_get_screen (GTK_WIDGET (canvas));
448 context = gtk_widget_get_style_context (GTK_WIDGET (canvas));
449
450 gtk_style_context_save (context);
451 gtk_style_context_add_class (context, "nautilus-canvas-item");
452
453 /* Assume we're updated so canvas item data is right */
454
455 /* Calculate the offset from the top-left corner of the
456 new image to the item position (where the pixmap is placed) */
457 eel_canvas_world_to_window (canvas,
458 item->details->x, item->details->y,
459 &item_x, &item_y);
460
461 item_offset_x = item_x - EEL_CANVAS_ITEM (item)->x1;
462 item_offset_y = item_y - EEL_CANVAS_ITEM (item)->y1;
463
464 /* Calculate the width of the item */
465 width = EEL_CANVAS_ITEM (item)->x2 - EEL_CANVAS_ITEM (item)->x1;
466 height = EEL_CANVAS_ITEM (item)->y2 - EEL_CANVAS_ITEM (item)->y1;
467
468 surface = gdk_window_create_similar_surface (gdk_screen_get_root_window (screen),
469 CAIRO_CONTENT_COLOR_ALPHA,
470 width, height);
471
472 cr = cairo_create (surface);
473
474 gtk_render_icon (context, cr, item->details->pixbuf,
475 item_offset_x, item_offset_y);
476
477 icon_rect.x0 = item_offset_x;
478 icon_rect.y0 = item_offset_y;
479 icon_rect.x1 = item_offset_x + gdk_pixbuf_get_width (item->details->pixbuf);
480 icon_rect.y1 = item_offset_y + gdk_pixbuf_get_height (item->details->pixbuf);
481
482 draw_embedded_text (item, cr,
483 item_offset_x, item_offset_y);
484 draw_label_text (item, cr, icon_rect);
485 cairo_destroy (cr);
486
487 gtk_style_context_restore (context);
488
489 return surface;
490 }
491
492 void
493 nautilus_canvas_item_set_image (NautilusCanvasItem *item,
494 GdkPixbuf *image)
495 {
496 NautilusCanvasItemDetails *details;
497
498 g_return_if_fail (NAUTILUS_IS_CANVAS_ITEM (item));
499 g_return_if_fail (image == NULL || pixbuf_is_acceptable (image));
500
501 details = item->details;
502 if (details->pixbuf == image) {
503 return;
504 }
505
506 if (image != NULL) {
507 g_object_ref (image);
508 }
509 if (details->pixbuf != NULL) {
510 g_object_unref (details->pixbuf);
511 }
512 if (details->rendered_pixbuf != NULL) {
513 g_object_unref (details->rendered_pixbuf);
514 details->rendered_pixbuf = NULL;
515 }
516
517 details->pixbuf = image;
518
519 nautilus_canvas_item_invalidate_bounds_cache (item);
520 eel_canvas_item_request_update (EEL_CANVAS_ITEM (item));
521 }
522
523 void
524 nautilus_canvas_item_set_attach_points (NautilusCanvasItem *item,
525 GdkPoint *attach_points,
526 int n_attach_points)
527 {
528 g_free (item->details->attach_points);
529 item->details->attach_points = NULL;
530 item->details->n_attach_points = 0;
531
532 if (attach_points != NULL && n_attach_points != 0) {
533 item->details->attach_points = g_memdup (attach_points, n_attach_points * sizeof (GdkPoint));
534 item->details->n_attach_points = n_attach_points;
535 }
536
537 nautilus_canvas_item_invalidate_bounds_cache (item);
538 }
539
540 void
541 nautilus_canvas_item_set_embedded_text_rect (NautilusCanvasItem *item,
542 const GdkRectangle *text_rect)
543 {
544 item->details->embedded_text_rect = *text_rect;
545
546 nautilus_canvas_item_invalidate_bounds_cache (item);
547 eel_canvas_item_request_update (EEL_CANVAS_ITEM (item));
548 }
549
550 void
551 nautilus_canvas_item_set_embedded_text (NautilusCanvasItem *item,
552 const char *text)
553 {
554 g_free (item->details->embedded_text);
555 item->details->embedded_text = g_strdup (text);
556
557 if (item->details->embedded_text_layout != NULL) {
558 if (text != NULL) {
559 pango_layout_set_text (item->details->embedded_text_layout, text, -1);
560 } else {
561 pango_layout_set_text (item->details->embedded_text_layout, "", -1);
562 }
563 }
564
565 eel_canvas_item_request_update (EEL_CANVAS_ITEM (item));
566 }
567
568
569 /* Recomputes the bounding box of a canvas item.
570 * This is a generic implementation that could be used for any canvas item
571 * class, it has no assumptions about how the item is used.
572 */
573 static void
574 recompute_bounding_box (NautilusCanvasItem *canvas_item,
575 double i2w_dx, double i2w_dy)
576 {
577 /* The bounds stored in the item is the same as what get_bounds
578 * returns, except it's in canvas coordinates instead of the item's
579 * parent's coordinates.
580 */
581
582 EelCanvasItem *item;
583 EelDRect bounds_rect;
584
585 item = EEL_CANVAS_ITEM (canvas_item);
586
587 eel_canvas_item_get_bounds (item,
588 &bounds_rect.x0, &bounds_rect.y0,
589 &bounds_rect.x1, &bounds_rect.y1);
590
591 bounds_rect.x0 += i2w_dx;
592 bounds_rect.y0 += i2w_dy;
593 bounds_rect.x1 += i2w_dx;
594 bounds_rect.y1 += i2w_dy;
595 eel_canvas_w2c_d (item->canvas,
596 bounds_rect.x0, bounds_rect.y0,
597 &item->x1, &item->y1);
598 eel_canvas_w2c_d (item->canvas,
599 bounds_rect.x1, bounds_rect.y1,
600 &item->x2, &item->y2);
601 }
602
603 static EelIRect
604 compute_text_rectangle (const NautilusCanvasItem *item,
605 EelIRect icon_rectangle,
606 gboolean canvas_coords,
607 NautilusCanvasItemBoundsUsage usage)
608 {
609 EelIRect text_rectangle;
610 double pixels_per_unit;
611 double text_width, text_height, text_height_for_layout, text_height_for_entire_text, real_text_height;
612
613 pixels_per_unit = EEL_CANVAS_ITEM (item)->canvas->pixels_per_unit;
614 if (canvas_coords) {
615 text_width = item->details->text_width;
616 text_height = item->details->text_height;
617 text_height_for_layout = item->details->text_height_for_layout;
618 text_height_for_entire_text = item->details->text_height_for_entire_text;
619 } else {
620 text_width = item->details->text_width / pixels_per_unit;
621 text_height = item->details->text_height / pixels_per_unit;
622 text_height_for_layout = item->details->text_height_for_layout / pixels_per_unit;
623 text_height_for_entire_text = item->details->text_height_for_entire_text / pixels_per_unit;
624 }
625
626 text_rectangle.x0 = (icon_rectangle.x0 + icon_rectangle.x1) / 2 - (int) text_width / 2;
627 text_rectangle.y0 = icon_rectangle.y1;
628 text_rectangle.x1 = text_rectangle.x0 + text_width;
629
630 if (usage == BOUNDS_USAGE_FOR_LAYOUT) {
631 real_text_height = text_height_for_layout;
632 } else if (usage == BOUNDS_USAGE_FOR_ENTIRE_ITEM) {
633 real_text_height = text_height_for_entire_text;
634 } else if (usage == BOUNDS_USAGE_FOR_DISPLAY) {
635 real_text_height = text_height;
636 } else {
637 g_assert_not_reached ();
638 }
639
640 text_rectangle.y1 = text_rectangle.y0 + real_text_height + LABEL_OFFSET / pixels_per_unit;
641
642 return text_rectangle;
643 }
644
645 static EelIRect
646 get_current_canvas_bounds (EelCanvasItem *item)
647 {
648 EelIRect bounds;
649
650 g_assert (EEL_IS_CANVAS_ITEM (item));
651
652 bounds.x0 = item->x1;
653 bounds.y0 = item->y1;
654 bounds.x1 = item->x2;
655 bounds.y1 = item->y2;
656
657 return bounds;
658 }
659
660 void
661 nautilus_canvas_item_update_bounds (NautilusCanvasItem *item,
662 double i2w_dx, double i2w_dy)
663 {
664 EelIRect before, after;
665 EelCanvasItem *canvas_item;
666
667 canvas_item = EEL_CANVAS_ITEM (item);
668
669 /* Compute new bounds. */
670 before = get_current_canvas_bounds (canvas_item);
671 recompute_bounding_box (item, i2w_dx, i2w_dy);
672 after = get_current_canvas_bounds (canvas_item);
673
674 /* If the bounds didn't change, we are done. */
675 if (eel_irect_equal (before, after)) {
676 return;
677 }
678
679 /* Update canvas and text rect cache */
680 get_icon_rectangle (item, &item->details->icon_rect);
681 item->details->text_rect = compute_text_rectangle (item, item->details->icon_rect,
682 TRUE, BOUNDS_USAGE_FOR_DISPLAY);
683
684 /* queue a redraw. */
685 eel_canvas_request_redraw (canvas_item->canvas,
686 before.x0, before.y0,
687 before.x1 + 1, before.y1 + 1);
688 }
689
690 /* Update handler for the canvas canvas item. */
691 static void
692 nautilus_canvas_item_update (EelCanvasItem *item,
693 double i2w_dx, double i2w_dy,
694 gint flags)
695 {
696 nautilus_canvas_item_update_bounds (NAUTILUS_CANVAS_ITEM (item), i2w_dx, i2w_dy);
697
698 eel_canvas_item_request_redraw (EEL_CANVAS_ITEM (item));
699
700 EEL_CANVAS_ITEM_CLASS (nautilus_canvas_item_parent_class)->update (item, i2w_dx, i2w_dy, flags);
701 }
702
703 /* Rendering */
704 static gboolean
705 in_single_click_mode (void)
706 {
707 int click_policy;
708
709 click_policy = g_settings_get_enum (nautilus_preferences,
710 NAUTILUS_PREFERENCES_CLICK_POLICY);
711
712 return click_policy == NAUTILUS_CLICK_POLICY_SINGLE;
713 }
714
715
716 /* Keep these for a bit while we work on performance of draw_or_measure_label_text. */
717 /*
718 #define PERFORMANCE_TEST_DRAW_DISABLE
719 #define PERFORMANCE_TEST_MEASURE_DISABLE
720 */
721
722 /* This gets the size of the layout from the position of the layout.
723 * This means that if the layout is right aligned we get the full width
724 * of the layout, not just the width of the text snippet on the right side
725 */
726 static void
727 layout_get_full_size (PangoLayout *layout,
728 int *width,
729 int *height,
730 int *dx)
731 {
732 PangoRectangle logical_rect;
733 int the_width, total_width;
734
735 pango_layout_get_extents (layout, NULL, &logical_rect);
736 the_width = (logical_rect.width + PANGO_SCALE / 2) / PANGO_SCALE;
737 total_width = (logical_rect.x + logical_rect.width + PANGO_SCALE / 2) / PANGO_SCALE;
738
739 if (width != NULL) {
740 *width = the_width;
741 }
742
743 if (height != NULL) {
744 *height = (logical_rect.height + PANGO_SCALE / 2) / PANGO_SCALE;
745 }
746
747 if (dx != NULL) {
748 *dx = total_width - the_width;
749 }
750 }
751
752 static void
753 layout_get_size_for_layout (PangoLayout *layout,
754 int max_layout_line_count,
755 int height_for_entire_text,
756 int *height_for_layout)
757 {
758 PangoLayoutIter *iter;
759 PangoRectangle logical_rect;
760 int i;
761
762 /* only use the first max_layout_line_count lines for the gridded auto layout */
763 if (pango_layout_get_line_count (layout) <= max_layout_line_count) {
764 *height_for_layout = height_for_entire_text;
765 } else {
766 *height_for_layout = 0;
767 iter = pango_layout_get_iter (layout);
768 for (i = 0; i < max_layout_line_count; i++) {
769 pango_layout_iter_get_line_extents (iter, NULL, &logical_rect);
770 *height_for_layout += (logical_rect.height + PANGO_SCALE / 2) / PANGO_SCALE;
771
772 if (!pango_layout_iter_next_line (iter)) {
773 break;
774 }
775
776 *height_for_layout += pango_layout_get_spacing (layout);
777 }
778 pango_layout_iter_free (iter);
779 }
780 }
781
782 #define TEXT_BACK_PADDING_X 4
783 #define TEXT_BACK_PADDING_Y 1
784
785 static void
786 prepare_pango_layout_width (NautilusCanvasItem *item,
787 PangoLayout *layout)
788 {
789 if (nautilus_canvas_item_get_max_text_width (item) < 0) {
790 pango_layout_set_width (layout, -1);
791 } else {
792 pango_layout_set_width (layout, floor (nautilus_canvas_item_get_max_text_width (item)) * PANGO_SCALE);
793 pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_END);
794 }
795 }
796
797 static void
798 prepare_pango_layout_for_measure_entire_text (NautilusCanvasItem *item,
799 PangoLayout *layout)
800 {
801 prepare_pango_layout_width (item, layout);
802 pango_layout_set_height (layout, G_MININT);
803 }
804
805 static void
806 prepare_pango_layout_for_draw (NautilusCanvasItem *item,
807 PangoLayout *layout)
808 {
809 NautilusCanvasItemDetails *details;
810 NautilusCanvasContainer *container;
811 gboolean needs_highlight;
812
813 prepare_pango_layout_width (item, layout);
814
815 container = NAUTILUS_CANVAS_CONTAINER (EEL_CANVAS_ITEM (item)->canvas);
816 details = item->details;
817
818 needs_highlight = details->is_highlighted_for_selection || details->is_highlighted_for_drop;
819
820 if (needs_highlight ||
821 details->is_prelit ||
822 details->is_highlighted_as_keyboard_focus ||
823 details->entire_text) {
824 /* VOODOO-TODO, cf. compute_text_rectangle() */
825 pango_layout_set_height (layout, G_MININT);
826 } else {
827 /* TODO? we might save some resources, when the re-layout is not neccessary in case
828 * the layout height already fits into max. layout lines. But pango should figure this
829 * out itself (which it doesn't ATM).
830 */
831 pango_layout_set_height (layout,
832 nautilus_canvas_container_get_max_layout_lines_for_pango (container));
833 }
834 }
835
836 static void
837 measure_label_text (NautilusCanvasItem *item)
838 {
839 NautilusCanvasItemDetails *details;
840 NautilusCanvasContainer *container;
841 gint editable_height, editable_height_for_layout, editable_height_for_entire_text, editable_width, editable_dx;
842 gint additional_height, additional_width, additional_dx;
843 PangoLayout *editable_layout;
844 PangoLayout *additional_layout;
845 gboolean have_editable, have_additional;
846
847 /* check to see if the cached values are still valid; if so, there's
848 * no work necessary
849 */
850
851 if (item->details->text_width >= 0 && item->details->text_height >= 0) {
852 return;
853 }
854
855 details = item->details;
856
857 have_editable = details->editable_text != NULL && details->editable_text[0] != '\0';
858 have_additional = details->additional_text != NULL && details->additional_text[0] != '\0';
859
860 /* No font or no text, then do no work. */
861 if (!have_editable && !have_additional) {
862 details->text_height = 0;
863 details->text_height_for_layout = 0;
864 details->text_height_for_entire_text = 0;
865 details->text_width = 0;
866 return;
867 }
868
869 #ifdef PERFORMANCE_TEST_MEASURE_DISABLE
870 /* fake out the width */
871 details->text_width = 80;
872 details->text_height = 20;
873 details->text_height_for_layout = 20;
874 details->text_height_for_entire_text = 20;
875 return;
876 #endif
877
878 editable_width = 0;
879 editable_height = 0;
880 editable_height_for_layout = 0;
881 editable_height_for_entire_text = 0;
882 editable_dx = 0;
883 additional_width = 0;
884 additional_height = 0;
885 additional_dx = 0;
886
887 container = NAUTILUS_CANVAS_CONTAINER (EEL_CANVAS_ITEM (item)->canvas);
888 editable_layout = NULL;
889 additional_layout = NULL;
890
891 if (have_editable) {
892 /* first, measure required text height: editable_height_for_entire_text
893 * then, measure text height applicable for layout: editable_height_for_layout
894 * next, measure actually displayed height: editable_height
895 */
896 editable_layout = get_label_layout (&details->editable_text_layout, item, details->editable_text);
897
898 prepare_pango_layout_for_measure_entire_text (item, editable_layout);
899 layout_get_full_size (editable_layout,
900 NULL,
901 &editable_height_for_entire_text,
902 NULL);
903 layout_get_size_for_layout (editable_layout,
904 nautilus_canvas_container_get_max_layout_lines (container),
905 editable_height_for_entire_text,
906 &editable_height_for_layout);
907
908 prepare_pango_layout_for_draw (item, editable_layout);
909 layout_get_full_size (editable_layout,
910 &editable_width,
911 &editable_height,
912 &editable_dx);
913 }
914
915 if (have_additional) {
916 additional_layout = get_label_layout (&details->additional_text_layout, item, details->additional_text);
917 prepare_pango_layout_for_draw (item, additional_layout);
918 layout_get_full_size (additional_layout,
919 &additional_width, &additional_height, &additional_dx);
920 }
921
922 details->editable_text_height = editable_height;
923
924 if (editable_width > additional_width) {
925 details->text_width = editable_width;
926 details->text_dx = editable_dx;
927 } else {
928 details->text_width = additional_width;
929 details->text_dx = additional_dx;
930 }
931
932 if (have_additional) {
933 details->text_height = editable_height + LABEL_LINE_SPACING + additional_height;
934 details->text_height_for_layout = editable_height_for_layout + LABEL_LINE_SPACING + additional_height;
935 details->text_height_for_entire_text = editable_height_for_entire_text + LABEL_LINE_SPACING + additional_height;
936 } else {
937 details->text_height = editable_height;
938 details->text_height_for_layout = editable_height_for_layout;
939 details->text_height_for_entire_text = editable_height_for_entire_text;
940 }
941
942 /* add some extra space for highlighting even when we don't highlight so things won't move */
943
944 /* extra slop for nicer highlighting */
945 details->text_height += TEXT_BACK_PADDING_Y*2;
946 details->text_height_for_layout += TEXT_BACK_PADDING_Y*2;
947 details->text_height_for_entire_text += TEXT_BACK_PADDING_Y*2;
948 details->editable_text_height += TEXT_BACK_PADDING_Y*2;
949
950 /* extra to make it look nicer */
951 details->text_width += TEXT_BACK_PADDING_X*2;
952
953 if (editable_layout) {
954 g_object_unref (editable_layout);
955 }
956
957 if (additional_layout) {
958 g_object_unref (additional_layout);
959 }
960 }
961
962 static void
963 draw_label_text (NautilusCanvasItem *item,
964 cairo_t *cr,
965 EelIRect icon_rect)
966 {
967 NautilusCanvasItemDetails *details;
968 NautilusCanvasContainer *container;
969 PangoLayout *editable_layout;
970 PangoLayout *additional_layout;
971 GtkStyleContext *context;
972 GtkStateFlags state, base_state;
973 gboolean have_editable, have_additional;
974 gboolean needs_highlight, prelight_label;
975 EelIRect text_rect;
976 int x;
977 int max_text_width;
978 gdouble frame_w, frame_h, frame_x, frame_y;
979 gboolean draw_frame = TRUE;
980
981 #ifdef PERFORMANCE_TEST_DRAW_DISABLE
982 return;
983 #endif
984
985 details = item->details;
986
987 measure_label_text (item);
988 if (details->text_height == 0 ||
989 details->text_width == 0) {
990 return;
991 }
992
993 container = NAUTILUS_CANVAS_CONTAINER (EEL_CANVAS_ITEM (item)->canvas);
994 context = gtk_widget_get_style_context (GTK_WIDGET (container));
995
996 text_rect = compute_text_rectangle (item, icon_rect, TRUE, BOUNDS_USAGE_FOR_DISPLAY);
997
998 needs_highlight = details->is_highlighted_for_selection || details->is_highlighted_for_drop;
999
1000 editable_layout = NULL;
1001 additional_layout = NULL;
1002
1003 have_editable = details->editable_text != NULL && details->editable_text[0] != '\0';
1004 have_additional = details->additional_text != NULL && details->additional_text[0] != '\0';
1005 g_assert (have_editable || have_additional);
1006
1007 max_text_width = floor (nautilus_canvas_item_get_max_text_width (item));
1008
1009 base_state = gtk_widget_get_state_flags (GTK_WIDGET (container));
1010 base_state &= ~(GTK_STATE_FLAG_SELECTED |
1011 GTK_STATE_FLAG_PRELIGHT);
1012 state = base_state;
1013
1014 gtk_widget_style_get (GTK_WIDGET (container),
1015 "activate_prelight_icon_label", &prelight_label,
1016 NULL);
1017
1018 /* if the canvas is highlighted, do some set-up */
1019 if (needs_highlight &&
1020 !details->is_renaming) {
1021 state |= GTK_STATE_FLAG_SELECTED;
1022
1023 frame_x = text_rect.x0;
1024 frame_y = text_rect.y0;
1025 frame_w = text_rect.x1 - text_rect.x0;
1026 frame_h = text_rect.y1 - text_rect.y0;
1027 } else if (!needs_highlight && have_editable &&
1028 details->text_width > 0 && details->text_height > 0 &&
1029 prelight_label && item->details->is_prelit) {
1030 state |= GTK_STATE_FLAG_PRELIGHT;
1031
1032 frame_x = text_rect.x0;
1033 frame_y = text_rect.y0;
1034 frame_w = text_rect.x1 - text_rect.x0;
1035 frame_h = text_rect.y1 - text_rect.y0;
1036 } else {
1037 draw_frame = FALSE;
1038 }
1039
1040 if (draw_frame) {
1041 gtk_style_context_save (context);
1042 gtk_style_context_set_state (context, state);
1043
1044 gtk_render_frame (context, cr,
1045 frame_x, frame_y,
1046 frame_w, frame_h);
1047 gtk_render_background (context, cr,
1048 frame_x, frame_y,
1049 frame_w, frame_h);
1050
1051 gtk_style_context_restore (context);
1052 }
1053
1054 x = text_rect.x0 + ((text_rect.x1 - text_rect.x0) - max_text_width) / 2;
1055
1056 if (have_editable &&
1057 !details->is_renaming) {
1058 state = base_state;
1059
1060 if (prelight_label && item->details->is_prelit) {
1061 state |= GTK_STATE_FLAG_PRELIGHT;
1062 }
1063
1064 if (needs_highlight) {
1065 state |= GTK_STATE_FLAG_SELECTED;
1066 }
1067
1068 editable_layout = get_label_layout (&item->details->editable_text_layout, item, item->details->editable_text);
1069 prepare_pango_layout_for_draw (item, editable_layout);
1070
1071 gtk_style_context_save (context);
1072 gtk_style_context_set_state (context, state);
1073
1074 gtk_render_layout (context, cr,
1075 x, text_rect.y0 + TEXT_BACK_PADDING_Y,
1076 editable_layout);
1077
1078 gtk_style_context_restore (context);
1079 }
1080
1081 if (have_additional &&
1082 !details->is_renaming) {
1083 state = base_state;
1084
1085 if (needs_highlight) {
1086 state |= GTK_STATE_FLAG_SELECTED;
1087 }
1088
1089 additional_layout = get_label_layout (&item->details->additional_text_layout, item, item->details->additional_text);
1090 prepare_pango_layout_for_draw (item, additional_layout);
1091
1092 gtk_style_context_save (context);
1093 gtk_style_context_set_state (context, state);
1094 gtk_style_context_add_class (context, "dim-label");
1095
1096 gtk_render_layout (context, cr,
1097 x, text_rect.y0 + details->editable_text_height + LABEL_LINE_SPACING + TEXT_BACK_PADDING_Y,
1098 additional_layout);
1099 }
1100
1101 if (item->details->is_highlighted_as_keyboard_focus) {
1102 if (needs_highlight) {
1103 state = GTK_STATE_FLAG_SELECTED;
1104 }
1105
1106 gtk_style_context_save (context);
1107 gtk_style_context_set_state (context, state);
1108
1109 gtk_render_focus (context,
1110 cr,
1111 text_rect.x0,
1112 text_rect.y0,
1113 text_rect.x1 - text_rect.x0,
1114 text_rect.y1 - text_rect.y0);
1115
1116 gtk_style_context_restore (context);
1117 }
1118
1119 if (editable_layout != NULL) {
1120 g_object_unref (editable_layout);
1121 }
1122
1123 if (additional_layout != NULL) {
1124 g_object_unref (additional_layout);
1125 }
1126 }
1127
1128 void
1129 nautilus_canvas_item_set_is_visible (NautilusCanvasItem *item,
1130 gboolean visible)
1131 {
1132 if (item->details->is_visible == visible)
1133 return;
1134
1135 item->details->is_visible = visible;
1136
1137 if (!visible) {
1138 nautilus_canvas_item_invalidate_label (item);
1139 }
1140 }
1141
1142 void
1143 nautilus_canvas_item_invalidate_label (NautilusCanvasItem *item)
1144 {
1145 nautilus_canvas_item_invalidate_label_size (item);
1146
1147 if (item->details->editable_text_layout) {
1148 g_object_unref (item->details->editable_text_layout);
1149 item->details->editable_text_layout = NULL;
1150 }
1151
1152 if (item->details->additional_text_layout) {
1153 g_object_unref (item->details->additional_text_layout);
1154 item->details->additional_text_layout = NULL;
1155 }
1156
1157 if (item->details->embedded_text_layout) {
1158 g_object_unref (item->details->embedded_text_layout);
1159 item->details->embedded_text_layout = NULL;
1160 }
1161 }
1162
1163
1164 static GdkPixbuf *
1165 get_knob_pixbuf (void)
1166 {
1167 GdkPixbuf *knob_pixbuf;
1168
1169 knob_pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (),
1170 "stock-nautilus-knob",
1171 8, 0, NULL);
1172 if (!knob_pixbuf) {
1173 GInputStream *stream = g_resources_open_stream ("/org/gnome/nautilus/icons/knob.png", 0, NULL);
1174 if (stream != NULL) {
1175 knob_pixbuf = gdk_pixbuf_new_from_stream (stream, NULL, NULL);
1176 g_object_unref (stream);
1177 }
1178 }
1179
1180 return knob_pixbuf;
1181 }
1182
1183 static void
1184 draw_stretch_handles (NautilusCanvasItem *item,
1185 cairo_t *cr,
1186 const EelIRect *rect)
1187 {
1188 GtkWidget *widget;
1189 GdkPixbuf *knob_pixbuf;
1190 int knob_width, knob_height;
1191 double dash = { 2.0 };
1192 GtkStyleContext *style;
1193 GdkRGBA color;
1194
1195 if (!item->details->show_stretch_handles) {
1196 return;
1197 }
1198
1199 widget = GTK_WIDGET (EEL_CANVAS_ITEM (item)->canvas);
1200 style = gtk_widget_get_style_context (widget);
1201
1202 cairo_save (cr);
1203 knob_pixbuf = get_knob_pixbuf ();
1204 knob_width = gdk_pixbuf_get_width (knob_pixbuf);
1205 knob_height = gdk_pixbuf_get_height (knob_pixbuf);
1206
1207 /* first draw the box */
1208 gtk_style_context_get_color (style, GTK_STATE_FLAG_SELECTED, &color);
1209 gdk_cairo_set_source_rgba (cr, &color);
1210 cairo_set_dash (cr, &dash, 1, 0);
1211 cairo_set_line_width (cr, 1.0);
1212 cairo_rectangle (cr,
1213 rect->x0 + 0.5,
1214 rect->y0 + 0.5,
1215 rect->x1 - rect->x0 - 1,
1216 rect->y1 - rect->y0 - 1);
1217 cairo_stroke (cr);
1218
1219 cairo_restore (cr);
1220
1221 /* draw the stretch handles themselves */
1222 draw_pixbuf (knob_pixbuf, cr, rect->x0, rect->y0);
1223 draw_pixbuf (knob_pixbuf, cr, rect->x0, rect->y1 - knob_height);
1224 draw_pixbuf (knob_pixbuf, cr, rect->x1 - knob_width, rect->y0);
1225 draw_pixbuf (knob_pixbuf, cr, rect->x1 - knob_width, rect->y1 - knob_height);
1226
1227 g_object_unref (knob_pixbuf);
1228 }
1229
1230 static void
1231 draw_pixbuf (GdkPixbuf *pixbuf,
1232 cairo_t *cr,
1233 int x, int y)
1234 {
1235 cairo_save (cr);
1236 gdk_cairo_set_source_pixbuf (cr, pixbuf, x, y);
1237 cairo_paint (cr);
1238 cairo_restore (cr);
1239 }
1240
1241 /* shared code to highlight or dim the passed-in pixbuf */
1242 static GdkPixbuf *
1243 real_map_pixbuf (NautilusCanvasItem *canvas_item)
1244 {
1245 EelCanvas *canvas;
1246 GdkPixbuf *temp_pixbuf, *old_pixbuf;
1247 GtkStyleContext *style;
1248 GdkRGBA color;
1249
1250 temp_pixbuf = canvas_item->details->pixbuf;
1251 canvas = EEL_CANVAS_ITEM(canvas_item)->canvas;
1252
1253 g_object_ref (temp_pixbuf);
1254
1255 if (canvas_item->details->is_prelit ||
1256 canvas_item->details->is_highlighted_for_clipboard) {
1257 old_pixbuf = temp_pixbuf;
1258
1259 temp_pixbuf = eel_create_spotlight_pixbuf (temp_pixbuf);
1260 g_object_unref (old_pixbuf);
1261 }
1262
1263 if (canvas_item->details->is_highlighted_for_selection
1264 || canvas_item->details->is_highlighted_for_drop) {
1265 style = gtk_widget_get_style_context (GTK_WIDGET (canvas));
1266
1267 if (gtk_widget_has_focus (GTK_WIDGET (canvas))) {
1268 gtk_style_context_get_background_color (style, GTK_STATE_FLAG_SELECTED, &color);
1269 } else {
1270 gtk_style_context_get_background_color (style, GTK_STATE_FLAG_ACTIVE, &color);
1271 }
1272
1273 old_pixbuf = temp_pixbuf;
1274 temp_pixbuf = eel_create_colorized_pixbuf (temp_pixbuf, &color);
1275
1276 g_object_unref (old_pixbuf);
1277 }
1278
1279 return temp_pixbuf;
1280 }
1281
1282 static GdkPixbuf *
1283 map_pixbuf (NautilusCanvasItem *canvas_item)
1284 {
1285 if (!(canvas_item->details->rendered_pixbuf != NULL
1286 && canvas_item->details->rendered_is_prelit == canvas_item->details->is_prelit
1287 && canvas_item->details->rendered_is_highlighted_for_selection == canvas_item->details->is_highlighted_for_selection
1288 && canvas_item->details->rendered_is_highlighted_for_drop == canvas_item->details->is_highlighted_for_drop
1289 && canvas_item->details->rendered_is_highlighted_for_clipboard == canvas_item->details->is_highlighted_for_clipboard
1290 && (canvas_item->details->is_highlighted_for_selection && canvas_item->details->rendered_is_focused == gtk_widget_has_focus (GTK_WIDGET (EEL_CANVAS_ITEM (canvas_item)->canvas))))) {
1291 if (canvas_item->details->rendered_pixbuf != NULL) {
1292 g_object_unref (canvas_item->details->rendered_pixbuf);
1293 }
1294 canvas_item->details->rendered_pixbuf = real_map_pixbuf (canvas_item);
1295 canvas_item->details->rendered_is_prelit = canvas_item->details->is_prelit;
1296 canvas_item->details->rendered_is_highlighted_for_selection = canvas_item->details->is_highlighted_for_selection;
1297 canvas_item->details->rendered_is_highlighted_for_drop = canvas_item->details->is_highlighted_for_drop;
1298 canvas_item->details->rendered_is_highlighted_for_clipboard = canvas_item->details->is_highlighted_for_clipboard;
1299 canvas_item->details->rendered_is_focused = gtk_widget_has_focus (GTK_WIDGET (EEL_CANVAS_ITEM (canvas_item)->canvas));
1300 }
1301
1302 g_object_ref (canvas_item->details->rendered_pixbuf);
1303
1304 return canvas_item->details->rendered_pixbuf;
1305 }
1306
1307 static void
1308 draw_embedded_text (NautilusCanvasItem *item,
1309 cairo_t *cr,
1310 int x, int y)
1311 {
1312 PangoLayout *layout;
1313 PangoContext *context;
1314 PangoFontDescription *desc;
1315 GtkWidget *widget;
1316 GtkStyleContext *style_context;
1317
1318 if (item->details->embedded_text == NULL ||
1319 item->details->embedded_text_rect.width == 0 ||
1320 item->details->embedded_text_rect.height == 0) {
1321 return;
1322 }
1323
1324 widget = GTK_WIDGET (EEL_CANVAS_ITEM (item)->canvas);
1325
1326 if (item->details->embedded_text_layout != NULL) {
1327 layout = g_object_ref (item->details->embedded_text_layout);
1328 } else {
1329 context = gtk_widget_get_pango_context (widget);
1330 layout = pango_layout_new (context);
1331 pango_layout_set_text (layout, item->details->embedded_text, -1);
1332
1333 desc = pango_font_description_from_string ("monospace 6");
1334 pango_layout_set_font_description (layout, desc);
1335 pango_font_description_free (desc);
1336
1337 if (item->details->is_visible) {
1338 item->details->embedded_text_layout = g_object_ref (layout);
1339 }
1340 }
1341
1342 style_context = gtk_widget_get_style_context (widget);
1343 gtk_style_context_save (style_context);
1344 gtk_style_context_add_class (style_context, "icon-embedded-text");
1345
1346 cairo_save (cr);
1347
1348 cairo_rectangle (cr,
1349 x + item->details->embedded_text_rect.x,
1350 y + item->details->embedded_text_rect.y,
1351 item->details->embedded_text_rect.width,
1352 item->details->embedded_text_rect.height);
1353 cairo_clip (cr);
1354
1355 gtk_render_layout (style_context, cr,
1356 x + item->details->embedded_text_rect.x,
1357 y + item->details->embedded_text_rect.y,
1358 layout);
1359
1360 gtk_style_context_restore (style_context);
1361 cairo_restore (cr);
1362 }
1363
1364 /* Draw the canvas item for non-anti-aliased mode. */
1365 static void
1366 nautilus_canvas_item_draw (EelCanvasItem *item,
1367 cairo_t *cr,
1368 cairo_region_t *region)
1369 {
1370 NautilusCanvasContainer *container;
1371 NautilusCanvasItem *canvas_item;
1372 NautilusCanvasItemDetails *details;
1373 EelIRect icon_rect;
1374 GdkPixbuf *temp_pixbuf;
1375 GtkStyleContext *context;
1376
1377 container = NAUTILUS_CANVAS_CONTAINER (item->canvas);
1378 canvas_item = NAUTILUS_CANVAS_ITEM (item);
1379 details = canvas_item->details;
1380
1381 /* Draw the pixbuf. */
1382 if (details->pixbuf == NULL) {
1383 return;
1384 }
1385
1386 context = gtk_widget_get_style_context (GTK_WIDGET (container));
1387 gtk_style_context_save (context);
1388 gtk_style_context_add_class (context, "nautilus-canvas-item");
1389
1390 icon_rect = canvas_item->details->icon_rect;
1391 temp_pixbuf = map_pixbuf (canvas_item);
1392
1393 gtk_render_icon (context, cr,
1394 temp_pixbuf,
1395 icon_rect.x0, icon_rect.y0);
1396 g_object_unref (temp_pixbuf);
1397
1398 draw_embedded_text (canvas_item, cr, icon_rect.x0, icon_rect.y0);
1399
1400 /* Draw stretching handles (if necessary). */
1401 draw_stretch_handles (canvas_item, cr, &icon_rect);
1402
1403 /* Draw the label text. */
1404 draw_label_text (canvas_item, cr, icon_rect);
1405
1406 gtk_style_context_restore (context);
1407 }
1408
1409 #define ZERO_WIDTH_SPACE "\xE2\x80\x8B"
1410
1411 #define ZERO_OR_THREE_DIGITS(p) \
1412 (!g_ascii_isdigit (*(p)) || \
1413 (g_ascii_isdigit (*(p+1)) && \
1414 g_ascii_isdigit (*(p+2))))
1415
1416
1417 static PangoLayout *
1418 create_label_layout (NautilusCanvasItem *item,
1419 const char *text)
1420 {
1421 PangoLayout *layout;
1422 PangoContext *context;
1423 PangoFontDescription *desc;
1424 NautilusCanvasContainer *container;
1425 EelCanvasItem *canvas_item;
1426 GString *str;
1427 char *zeroified_text;
1428 const char *p;
1429
1430 canvas_item = EEL_CANVAS_ITEM (item);
1431
1432 container = NAUTILUS_CANVAS_CONTAINER (canvas_item->canvas);
1433 context = gtk_widget_get_pango_context (GTK_WIDGET (canvas_item->canvas));
1434 layout = pango_layout_new (context);
1435
1436 zeroified_text = NULL;
1437
1438 if (text != NULL) {
1439 str = g_string_new (NULL);
1440
1441 for (p = text; *p != '\0'; p++) {
1442 str = g_string_append_c (str, *p);
1443
1444 if (*p == '_' || *p == '-' || (*p == '.' && ZERO_OR_THREE_DIGITS (p+1))) {
1445 /* Ensure that we allow to break after '_' or '.' characters,
1446 * if they are not likely to be part of a version information, to
1447 * not break wrapping of foobar-0.0.1.
1448 * Wrap before IPs and long numbers, though. */
1449 str = g_string_append (str, ZERO_WIDTH_SPACE);
1450 }
1451 }
1452
1453 zeroified_text = g_string_free (str, FALSE);
1454 }
1455
1456 pango_layout_set_text (layout, zeroified_text, -1);
1457 pango_layout_set_auto_dir (layout, FALSE);
1458 pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER);
1459
1460 pango_layout_set_spacing (layout, LABEL_LINE_SPACING);
1461 pango_layout_set_wrap (layout, PANGO_WRAP_WORD_CHAR);
1462
1463 /* Create a font description */
1464 if (container->details->font) {
1465 desc = pango_font_description_from_string (container->details->font);
1466 } else {
1467 desc = pango_font_description_copy (pango_context_get_font_description (context));
1468 }
1469 pango_layout_set_font_description (layout, desc);
1470 pango_font_description_free (desc);
1471 g_free (zeroified_text);
1472
1473 return layout;
1474 }
1475
1476 static PangoLayout *
1477 get_label_layout (PangoLayout **layout_cache,
1478 NautilusCanvasItem *item,
1479 const char *text)
1480 {
1481 PangoLayout *layout;
1482
1483 if (*layout_cache != NULL) {
1484 return g_object_ref (*layout_cache);
1485 }
1486
1487 layout = create_label_layout (item, text);
1488
1489 if (item->details->is_visible) {
1490 *layout_cache = g_object_ref (layout);
1491 }
1492
1493 return layout;
1494 }
1495
1496 /* handle events */
1497
1498 static int
1499 nautilus_canvas_item_event (EelCanvasItem *item, GdkEvent *event)
1500 {
1501 NautilusCanvasItem *canvas_item;
1502 GdkCursor *cursor;
1503 GdkWindow *cursor_window;
1504
1505 canvas_item = NAUTILUS_CANVAS_ITEM (item);
1506 cursor_window = ((GdkEventAny *)event)->window;
1507
1508 switch (event->type) {
1509 case GDK_ENTER_NOTIFY:
1510 if (!canvas_item->details->is_prelit) {
1511 canvas_item->details->is_prelit = TRUE;
1512 nautilus_canvas_item_invalidate_label_size (canvas_item);
1513 eel_canvas_item_request_update (item);
1514 eel_canvas_item_send_behind (item,
1515 NAUTILUS_CANVAS_CONTAINER (item->canvas)->details->rubberband_info.selection_rectangle);
1516
1517 /* show a hand cursor */
1518 if (in_single_click_mode ()) {
1519 cursor = gdk_cursor_new_for_display (gdk_display_get_default(),
1520 GDK_HAND2);
1521 gdk_window_set_cursor (cursor_window, cursor);
1522 g_object_unref (cursor);
1523
1524 canvas_item->details->cursor_window = g_object_ref (cursor_window);
1525 }
1526 }
1527 return TRUE;
1528
1529 case GDK_LEAVE_NOTIFY:
1530 if (canvas_item->details->is_prelit
1531 || canvas_item->details->is_highlighted_for_drop) {
1532 /* When leaving, turn of the prelight state and the
1533 * higlighted for drop. The latter gets turned on
1534 * by the drag&drop motion callback.
1535 */
1536 canvas_item->details->is_prelit = FALSE;
1537 canvas_item->details->is_highlighted_for_drop = FALSE;
1538 nautilus_canvas_item_invalidate_label_size (canvas_item);
1539 eel_canvas_item_request_update (item);
1540
1541 /* show default cursor */
1542 gdk_window_set_cursor (cursor_window, NULL);
1543 g_clear_object (&canvas_item->details->cursor_window);
1544 }
1545 return TRUE;
1546
1547 default:
1548 /* Don't eat up other events; canvas container might use them. */
1549 return FALSE;
1550 }
1551 }
1552
1553 static gboolean
1554 hit_test (NautilusCanvasItem *canvas_item, EelIRect icon_rect)
1555 {
1556 NautilusCanvasItemDetails *details;
1557
1558 details = canvas_item->details;
1559
1560 /* Quick check to see if the rect hits the canvas or text at all. */
1561 if (!eel_irect_hits_irect (canvas_item->details->icon_rect, icon_rect)
1562 && (!eel_irect_hits_irect (details->text_rect, icon_rect))) {
1563 return FALSE;
1564 }
1565
1566 /* Check for hits in the stretch handles. */
1567 if (hit_test_stretch_handle (canvas_item, icon_rect, NULL)) {
1568 return TRUE;
1569 }
1570
1571 /* Check for hit in the canvas. */
1572 if (eel_irect_hits_irect (canvas_item->details->icon_rect, icon_rect)) {
1573 return TRUE;
1574 }
1575
1576 /* Check for hit in the text. */
1577 if (eel_irect_hits_irect (details->text_rect, icon_rect)
1578 && !canvas_item->details->is_renaming) {
1579 return TRUE;
1580 }
1581
1582 return FALSE;
1583 }
1584
1585 /* Point handler for the canvas canvas item. */
1586 static double
1587 nautilus_canvas_item_point (EelCanvasItem *item, double x, double y, int cx, int cy,
1588 EelCanvasItem **actual_item)
1589 {
1590 EelIRect icon_rect;
1591
1592 *actual_item = item;
1593 icon_rect.x0 = cx;
1594 icon_rect.y0 = cy;
1595 icon_rect.x1 = cx + 1;
1596 icon_rect.y1 = cy + 1;
1597 if (hit_test (NAUTILUS_CANVAS_ITEM (item), icon_rect)) {
1598 return 0.0;
1599 } else {
1600 /* This value means not hit.
1601 * It's kind of arbitrary. Can we do better?
1602 */
1603 return item->canvas->pixels_per_unit * 2 + 10;
1604 }
1605 }
1606
1607 static void
1608 nautilus_canvas_item_translate (EelCanvasItem *item, double dx, double dy)
1609 {
1610 NautilusCanvasItem *canvas_item;
1611 NautilusCanvasItemDetails *details;
1612
1613 canvas_item = NAUTILUS_CANVAS_ITEM (item);
1614 details = canvas_item->details;
1615
1616 details->x += dx;
1617 details->y += dy;
1618 }
1619
1620 void
1621 nautilus_canvas_item_get_bounds_for_layout (NautilusCanvasItem *canvas_item,
1622 double *x1, double *y1, double *x2, double *y2)
1623 {
1624 NautilusCanvasItemDetails *details;
1625 EelIRect *total_rect;
1626
1627 details = canvas_item->details;
1628
1629 nautilus_canvas_item_ensure_bounds_up_to_date (canvas_item);
1630 g_assert (details->bounds_cached);
1631
1632 total_rect = &details->bounds_cache_for_layout;
1633
1634 /* Return the result. */
1635 if (x1 != NULL) {
1636 *x1 = (int)details->x + total_rect->x0;
1637 }
1638 if (y1 != NULL) {
1639 *y1 = (int)details->y + total_rect->y0;
1640 }
1641 if (x2 != NULL) {
1642 *x2 = (int)details->x + total_rect->x1 + 1;
1643 }
1644 if (y2 != NULL) {
1645 *y2 = (int)details->y + total_rect->y1 + 1;
1646 }
1647 }
1648
1649 void
1650 nautilus_canvas_item_get_bounds_for_entire_item (NautilusCanvasItem *canvas_item,
1651 double *x1, double *y1, double *x2, double *y2)
1652 {
1653 NautilusCanvasItemDetails *details;
1654 EelIRect *total_rect;
1655
1656 details = canvas_item->details;
1657
1658 nautilus_canvas_item_ensure_bounds_up_to_date (canvas_item);
1659 g_assert (details->bounds_cached);
1660
1661 total_rect = &details->bounds_cache_for_entire_item;
1662
1663 /* Return the result. */
1664 if (x1 != NULL) {
1665 *x1 = (int)details->x + total_rect->x0;
1666 }
1667 if (y1 != NULL) {
1668 *y1 = (int)details->y + total_rect->y0;
1669 }
1670 if (x2 != NULL) {
1671 *x2 = (int)details->x + total_rect->x1 + 1;
1672 }
1673 if (y2 != NULL) {
1674 *y2 = (int)details->y + total_rect->y1 + 1;
1675 }
1676 }
1677
1678 /* Bounds handler for the canvas canvas item. */
1679 static void
1680 nautilus_canvas_item_bounds (EelCanvasItem *item,
1681 double *x1, double *y1, double *x2, double *y2)
1682 {
1683 NautilusCanvasItem *canvas_item;
1684 NautilusCanvasItemDetails *details;
1685 EelIRect *total_rect;
1686
1687 canvas_item = NAUTILUS_CANVAS_ITEM (item);
1688 details = canvas_item->details;
1689
1690 g_assert (x1 != NULL);
1691 g_assert (y1 != NULL);
1692 g_assert (x2 != NULL);
1693 g_assert (y2 != NULL);
1694
1695 nautilus_canvas_item_ensure_bounds_up_to_date (canvas_item);
1696 g_assert (details->bounds_cached);
1697
1698 total_rect = &details->bounds_cache;
1699
1700 /* Return the result. */
1701 *x1 = (int)details->x + total_rect->x0;
1702 *y1 = (int)details->y + total_rect->y0;
1703 *x2 = (int)details->x + total_rect->x1 + 1;
1704 *y2 = (int)details->y + total_rect->y1 + 1;
1705 }
1706
1707 static void
1708 nautilus_canvas_item_ensure_bounds_up_to_date (NautilusCanvasItem *canvas_item)
1709 {
1710 NautilusCanvasItemDetails *details;
1711 EelIRect icon_rect, icon_rect_raw;
1712 EelIRect text_rect, text_rect_for_layout, text_rect_for_entire_text;
1713 EelIRect total_rect, total_rect_for_layout, total_rect_for_entire_text;
1714 EelCanvasItem *item;
1715 double pixels_per_unit;
1716
1717 details = canvas_item->details;
1718 item = EEL_CANVAS_ITEM (canvas_item);
1719
1720 if (!details->bounds_cached) {
1721 measure_label_text (canvas_item);
1722
1723 pixels_per_unit = EEL_CANVAS_ITEM (item)->canvas->pixels_per_unit;
1724
1725 /* Compute raw and scaled canvas rectangle. */
1726 icon_rect.x0 = 0;
1727 icon_rect.y0 = 0;
1728 icon_rect_raw.x0 = 0;
1729 icon_rect_raw.y0 = 0;
1730 if (details->pixbuf == NULL) {
1731 icon_rect.x1 = icon_rect.x0;
1732 icon_rect.y1 = icon_rect.y0;
1733 icon_rect_raw.x1 = icon_rect_raw.x0;
1734 icon_rect_raw.y1 = icon_rect_raw.y0;
1735 } else {
1736 icon_rect_raw.x1 = icon_rect_raw.x0 + gdk_pixbuf_get_width (details->pixbuf);
1737 icon_rect_raw.y1 = icon_rect_raw.y0 + gdk_pixbuf_get_height (details->pixbuf);
1738 icon_rect.x1 = icon_rect_raw.x1 / pixels_per_unit;
1739 icon_rect.y1 = icon_rect_raw.y1 / pixels_per_unit;
1740 }
1741
1742 /* Compute text rectangle. */
1743 text_rect = compute_text_rectangle (canvas_item, icon_rect, FALSE, BOUNDS_USAGE_FOR_DISPLAY);
1744 text_rect_for_layout = compute_text_rectangle (canvas_item, icon_rect, FALSE, BOUNDS_USAGE_FOR_LAYOUT);
1745 text_rect_for_entire_text = compute_text_rectangle (canvas_item, icon_rect, FALSE, BOUNDS_USAGE_FOR_ENTIRE_ITEM);
1746
1747 /* Compute total rectangle */
1748 eel_irect_union (&total_rect, &icon_rect, &text_rect);
1749 eel_irect_union (&total_rect_for_layout, &icon_rect, &text_rect_for_layout);
1750 eel_irect_union (&total_rect_for_entire_text, &icon_rect, &text_rect_for_entire_text);
1751
1752 details->bounds_cache = total_rect;
1753 details->bounds_cache_for_layout = total_rect_for_layout;
1754 details->bounds_cache_for_entire_item = total_rect_for_entire_text;
1755 details->bounds_cached = TRUE;
1756 }
1757 }
1758
1759 /* Get the rectangle of the canvas only, in world coordinates. */
1760 EelDRect
1761 nautilus_canvas_item_get_icon_rectangle (const NautilusCanvasItem *item)
1762 {
1763 EelDRect rectangle;
1764 double pixels_per_unit;
1765 GdkPixbuf *pixbuf;
1766
1767 g_return_val_if_fail (NAUTILUS_IS_CANVAS_ITEM (item), eel_drect_empty);
1768
1769 rectangle.x0 = item->details->x;
1770 rectangle.y0 = item->details->y;
1771
1772 pixbuf = item->details->pixbuf;
1773
1774 pixels_per_unit = EEL_CANVAS_ITEM (item)->canvas->pixels_per_unit;
1775 rectangle.x1 = rectangle.x0 + (pixbuf == NULL ? 0 : gdk_pixbuf_get_width (pixbuf)) / pixels_per_unit;
1776 rectangle.y1 = rectangle.y0 + (pixbuf == NULL ? 0 : gdk_pixbuf_get_height (pixbuf)) / pixels_per_unit;
1777
1778 eel_canvas_item_i2w (EEL_CANVAS_ITEM (item),
1779 &rectangle.x0,
1780 &rectangle.y0);
1781 eel_canvas_item_i2w (EEL_CANVAS_ITEM (item),
1782 &rectangle.x1,
1783 &rectangle.y1);
1784
1785 return rectangle;
1786 }
1787
1788 EelDRect
1789 nautilus_canvas_item_get_text_rectangle (NautilusCanvasItem *item,
1790 gboolean for_layout)
1791 {
1792 /* FIXME */
1793 EelIRect icon_rectangle;
1794 EelIRect text_rectangle;
1795 EelDRect ret;
1796 double pixels_per_unit;
1797 GdkPixbuf *pixbuf;
1798
1799 g_return_val_if_fail (NAUTILUS_IS_CANVAS_ITEM (item), eel_drect_empty);
1800
1801 icon_rectangle.x0 = item->details->x;
1802 icon_rectangle.y0 = item->details->y;
1803
1804 pixbuf = item->details->pixbuf;
1805
1806 pixels_per_unit = EEL_CANVAS_ITEM (item)->canvas->pixels_per_unit;
1807 icon_rectangle.x1 = icon_rectangle.x0 + (pixbuf == NULL ? 0 : gdk_pixbuf_get_width (pixbuf)) / pixels_per_unit;
1808 icon_rectangle.y1 = icon_rectangle.y0 + (pixbuf == NULL ? 0 : gdk_pixbuf_get_height (pixbuf)) / pixels_per_unit;
1809
1810 measure_label_text (item);
1811
1812 text_rectangle = compute_text_rectangle (item, icon_rectangle, FALSE,
1813 for_layout ? BOUNDS_USAGE_FOR_LAYOUT : BOUNDS_USAGE_FOR_DISPLAY);
1814
1815 ret.x0 = text_rectangle.x0;
1816 ret.y0 = text_rectangle.y0;
1817 ret.x1 = text_rectangle.x1;
1818 ret.y1 = text_rectangle.y1;
1819
1820 eel_canvas_item_i2w (EEL_CANVAS_ITEM (item),
1821 &ret.x0,
1822 &ret.y0);
1823 eel_canvas_item_i2w (EEL_CANVAS_ITEM (item),
1824 &ret.x1,
1825 &ret.y1);
1826
1827 return ret;
1828 }
1829
1830
1831 /* Get the rectangle of the icon only, in canvas coordinates. */
1832 static void
1833 get_icon_rectangle (NautilusCanvasItem *item,
1834 EelIRect *rect)
1835 {
1836 GdkPixbuf *pixbuf;
1837
1838 g_assert (NAUTILUS_IS_CANVAS_ITEM (item));
1839 g_assert (rect != NULL);
1840
1841 eel_canvas_w2c (EEL_CANVAS_ITEM (item)->canvas,
1842 item->details->x,
1843 item->details->y,
1844 &rect->x0,
1845 &rect->y0);
1846
1847 pixbuf = item->details->pixbuf;
1848
1849 rect->x1 = rect->x0 + (pixbuf == NULL ? 0 : gdk_pixbuf_get_width (pixbuf));
1850 rect->y1 = rect->y0 + (pixbuf == NULL ? 0 : gdk_pixbuf_get_height (pixbuf));
1851 }
1852
1853 void
1854 nautilus_canvas_item_set_show_stretch_handles (NautilusCanvasItem *item,
1855 gboolean show_stretch_handles)
1856 {
1857 g_return_if_fail (NAUTILUS_IS_CANVAS_ITEM (item));
1858 g_return_if_fail (show_stretch_handles == FALSE || show_stretch_handles == TRUE);
1859
1860 if (!item->details->show_stretch_handles == !show_stretch_handles) {
1861 return;
1862 }
1863
1864 item->details->show_stretch_handles = show_stretch_handles;
1865 eel_canvas_item_request_update (EEL_CANVAS_ITEM (item));
1866 }
1867
1868 /* Check if one of the stretch handles was hit. */
1869 static gboolean
1870 hit_test_stretch_handle (NautilusCanvasItem *item,
1871 EelIRect probe_icon_rect,
1872 GtkCornerType *corner)
1873 {
1874 EelIRect icon_rect;
1875 GdkPixbuf *knob_pixbuf;
1876 int knob_width, knob_height;
1877 int hit_corner;
1878
1879 g_assert (NAUTILUS_IS_CANVAS_ITEM (item));
1880
1881 /* Make sure there are handles to hit. */
1882 if (!item->details->show_stretch_handles) {
1883 return FALSE;
1884 }
1885
1886 /* Quick check to see if the rect hits the canvas at all. */
1887 icon_rect = item->details->icon_rect;
1888 if (!eel_irect_hits_irect (probe_icon_rect, icon_rect)) {
1889 return FALSE;
1890 }
1891
1892 knob_pixbuf = get_knob_pixbuf ();
1893 knob_width = gdk_pixbuf_get_width (knob_pixbuf);
1894 knob_height = gdk_pixbuf_get_height (knob_pixbuf);
1895 g_object_unref (knob_pixbuf);
1896
1897 /* Check for hits in the stretch handles. */
1898 hit_corner = -1;
1899 if (probe_icon_rect.x0 < icon_rect.x0 + knob_width) {
1900 if (probe_icon_rect.y0 < icon_rect.y0 + knob_height)
1901 hit_corner = GTK_CORNER_TOP_LEFT;
1902 else if (probe_icon_rect.y1 >= icon_rect.y1 - knob_height)
1903 hit_corner = GTK_CORNER_BOTTOM_LEFT;
1904 }
1905 else if (probe_icon_rect.x1 >= icon_rect.x1 - knob_width) {
1906 if (probe_icon_rect.y0 < icon_rect.y0 + knob_height)
1907 hit_corner = GTK_CORNER_TOP_RIGHT;
1908 else if (probe_icon_rect.y1 >= icon_rect.y1 - knob_height)
1909 hit_corner = GTK_CORNER_BOTTOM_RIGHT;
1910 }
1911 if (corner)
1912 *corner = hit_corner;
1913
1914 return hit_corner != -1;
1915 }
1916
1917 gboolean
1918 nautilus_canvas_item_hit_test_stretch_handles (NautilusCanvasItem *item,
1919 gdouble world_x,
1920 gdouble world_y,
1921 GtkCornerType *corner)
1922 {
1923 EelIRect icon_rect;
1924
1925 g_return_val_if_fail (NAUTILUS_IS_CANVAS_ITEM (item), FALSE);
1926
1927 eel_canvas_w2c (EEL_CANVAS_ITEM (item)->canvas,
1928 world_x,
1929 world_y,
1930 &icon_rect.x0,
1931 &icon_rect.y0);
1932 icon_rect.x1 = icon_rect.x0 + 1;
1933 icon_rect.y1 = icon_rect.y0 + 1;
1934 return hit_test_stretch_handle (item, icon_rect, corner);
1935 }
1936
1937 /* nautilus_canvas_item_hit_test_rectangle
1938 *
1939 * Check and see if there is an intersection between the item and the
1940 * canvas rect.
1941 */
1942 gboolean
1943 nautilus_canvas_item_hit_test_rectangle (NautilusCanvasItem *item, EelIRect icon_rect)
1944 {
1945 g_return_val_if_fail (NAUTILUS_IS_CANVAS_ITEM (item), FALSE);
1946
1947 return hit_test (item, icon_rect);
1948 }
1949
1950 const char *
1951 nautilus_canvas_item_get_editable_text (NautilusCanvasItem *canvas_item)
1952 {
1953 g_return_val_if_fail (NAUTILUS_IS_CANVAS_ITEM (canvas_item), NULL);
1954
1955 return canvas_item->details->editable_text;
1956 }
1957
1958 void
1959 nautilus_canvas_item_set_renaming (NautilusCanvasItem *item, gboolean state)
1960 {
1961 g_return_if_fail (NAUTILUS_IS_CANVAS_ITEM (item));
1962 g_return_if_fail (state == FALSE || state == TRUE);
1963
1964 if (!item->details->is_renaming == !state) {
1965 return;
1966 }
1967
1968 item->details->is_renaming = state;
1969 eel_canvas_item_request_update (EEL_CANVAS_ITEM (item));
1970 }
1971
1972 double
1973 nautilus_canvas_item_get_max_text_width (NautilusCanvasItem *item)
1974 {
1975 EelCanvasItem *canvas_item;
1976
1977 canvas_item = EEL_CANVAS_ITEM (item);
1978
1979 return MAX_TEXT_WIDTH_STANDARD * canvas_item->canvas->pixels_per_unit;
1980 }
1981
1982 void
1983 nautilus_canvas_item_set_entire_text (NautilusCanvasItem *item,
1984 gboolean entire_text)
1985 {
1986 if (item->details->entire_text != entire_text) {
1987 item->details->entire_text = entire_text;
1988
1989 nautilus_canvas_item_invalidate_label_size (item);
1990 eel_canvas_item_request_update (EEL_CANVAS_ITEM (item));
1991 }
1992 }
1993
1994 /* Class initialization function for the canvas canvas item. */
1995 static void
1996 nautilus_canvas_item_class_init (NautilusCanvasItemClass *class)
1997 {
1998 GObjectClass *object_class;
1999 EelCanvasItemClass *item_class;
2000
2001 object_class = G_OBJECT_CLASS (class);
2002 item_class = EEL_CANVAS_ITEM_CLASS (class);
2003
2004 object_class->finalize = nautilus_canvas_item_finalize;
2005 object_class->set_property = nautilus_canvas_item_set_property;
2006 object_class->get_property = nautilus_canvas_item_get_property;
2007
2008 g_object_class_install_property (
2009 object_class,
2010 PROP_EDITABLE_TEXT,
2011 g_param_spec_string ("editable_text",
2012 "editable text",
2013 "the editable label",
2014 "", G_PARAM_READWRITE));
2015
2016 g_object_class_install_property (
2017 object_class,
2018 PROP_ADDITIONAL_TEXT,
2019 g_param_spec_string ("additional_text",
2020 "additional text",
2021 "some more text",
2022 "", G_PARAM_READWRITE));
2023
2024 g_object_class_install_property (
2025 object_class,
2026 PROP_HIGHLIGHTED_FOR_SELECTION,
2027 g_param_spec_boolean ("highlighted_for_selection",
2028 "highlighted for selection",
2029 "whether we are highlighted for a selection",
2030 FALSE, G_PARAM_READWRITE));
2031
2032 g_object_class_install_property (
2033 object_class,
2034 PROP_HIGHLIGHTED_AS_KEYBOARD_FOCUS,
2035 g_param_spec_boolean ("highlighted_as_keyboard_focus",
2036 "highlighted as keyboard focus",
2037 "whether we are highlighted to render keyboard focus",
2038 FALSE, G_PARAM_READWRITE));
2039
2040
2041 g_object_class_install_property (
2042 object_class,
2043 PROP_HIGHLIGHTED_FOR_DROP,
2044 g_param_spec_boolean ("highlighted_for_drop",
2045 "highlighted for drop",
2046 "whether we are highlighted for a D&D drop",
2047 FALSE, G_PARAM_READWRITE));
2048
2049 g_object_class_install_property (
2050 object_class,
2051 PROP_HIGHLIGHTED_FOR_CLIPBOARD,
2052 g_param_spec_boolean ("highlighted_for_clipboard",
2053 "highlighted for clipboard",
2054 "whether we are highlighted for a clipboard paste (after we have been cut)",
2055 FALSE, G_PARAM_READWRITE));
2056
2057 item_class->update = nautilus_canvas_item_update;
2058 item_class->draw = nautilus_canvas_item_draw;
2059 item_class->point = nautilus_canvas_item_point;
2060 item_class->translate = nautilus_canvas_item_translate;
2061 item_class->bounds = nautilus_canvas_item_bounds;
2062 item_class->event = nautilus_canvas_item_event;
2063
2064 atk_registry_set_factory_type (atk_get_default_registry (),
2065 NAUTILUS_TYPE_CANVAS_ITEM,
2066 nautilus_canvas_item_accessible_factory_get_type ());
2067
2068 g_type_class_add_private (class, sizeof (NautilusCanvasItemDetails));
2069 }
2070
2071 static GailTextUtil *
2072 nautilus_canvas_item_get_text (GObject *text)
2073 {
2074 return NAUTILUS_CANVAS_ITEM (text)->details->text_util;
2075 }
2076
2077 static void
2078 nautilus_canvas_item_text_interface_init (EelAccessibleTextIface *iface)
2079 {
2080 iface->get_text = nautilus_canvas_item_get_text;
2081 }
2082
2083 /* ============================= a11y interfaces =========================== */
2084
2085 static const char *nautilus_canvas_item_accessible_action_names[] = {
2086 "open",
2087 "menu",
2088 NULL
2089 };
2090
2091 static const char *nautilus_canvas_item_accessible_action_descriptions[] = {
2092 "Open item",
2093 "Popup context menu",
2094 NULL
2095 };
2096
2097 enum {
2098 ACTION_OPEN,
2099 ACTION_MENU,
2100 LAST_ACTION
2101 };
2102
2103 typedef struct {
2104 char *action_descriptions[LAST_ACTION];
2105 char *image_description;
2106 char *description;
2107 } NautilusCanvasItemAccessiblePrivate;
2108
2109 typedef struct {
2110 NautilusCanvasItem *item;
2111 gint action_number;
2112 } NautilusCanvasItemAccessibleActionContext;
2113
2114 typedef struct {
2115 EelCanvasItemAccessible parent;
2116 NautilusCanvasItemAccessiblePrivate *priv;
2117 } NautilusCanvasItemAccessible;
2118
2119 typedef struct {
2120 EelCanvasItemAccessibleClass parent_class;
2121 } NautilusCanvasItemAccessibleClass;
2122
2123 #define GET_ACCESSIBLE_PRIV(o) ((NautilusCanvasItemAccessible *) o)->priv;
2124
2125 /* accessible AtkAction interface */
2126 static gboolean
2127 nautilus_canvas_item_accessible_idle_do_action (gpointer data)
2128 {
2129 NautilusCanvasItem *item;
2130 NautilusCanvasItemAccessibleActionContext *ctx;
2131 NautilusCanvasIcon *icon;
2132 NautilusCanvasContainer *container;
2133 GList* selection;
2134 GList file_list;
2135 GdkEventButton button_event = { 0 };
2136 gint action_number;
2137
2138 container = NAUTILUS_CANVAS_CONTAINER (data);
2139 container->details->a11y_item_action_idle_handler = 0;
2140 while (!g_queue_is_empty (container->details->a11y_item_action_queue)) {
2141 ctx = g_queue_pop_head (container->details->a11y_item_action_queue);
2142 action_number = ctx->action_number;
2143 item = ctx->item;
2144 g_free (ctx);
2145 icon = item->user_data;
2146
2147 switch (action_number) {
2148 case ACTION_OPEN:
2149 file_list.data = icon->data;
2150 file_list.next = NULL;
2151 file_list.prev = NULL;
2152 g_signal_emit_by_name (container, "activate", &file_list);
2153 break;
2154 case ACTION_MENU:
2155 selection = nautilus_canvas_container_get_selection (container);
2156 if (selection == NULL ||
2157 g_list_length (selection) != 1 ||
2158 selection->data != icon->data) {
2159 g_list_free (selection);
2160 return FALSE;
2161 }
2162 g_list_free (selection);
2163 g_signal_emit_by_name (container, "context_click_selection", &button_event);
2164 break;
2165 default :
2166 g_assert_not_reached ();
2167 break;
2168 }
2169 }
2170 return FALSE;
2171 }
2172
2173 static gboolean
2174 nautilus_canvas_item_accessible_do_action (AtkAction *accessible,
2175 int i)
2176 {
2177 NautilusCanvasItem *item;
2178 NautilusCanvasItemAccessibleActionContext *ctx;
2179 NautilusCanvasContainer *container;
2180
2181 g_assert (i < LAST_ACTION);
2182
2183 item = NAUTILUS_CANVAS_ITEM (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (accessible)));
2184 if (!item) {
2185 return FALSE;
2186 }
2187
2188 container = NAUTILUS_CANVAS_CONTAINER (EEL_CANVAS_ITEM (item)->canvas);
2189 switch (i) {
2190 case ACTION_OPEN:
2191 case ACTION_MENU:
2192 if (container->details->a11y_item_action_queue == NULL) {
2193 container->details->a11y_item_action_queue = g_queue_new ();
2194 }
2195 ctx = g_new (NautilusCanvasItemAccessibleActionContext, 1);
2196 ctx->action_number = i;
2197 ctx->item = item;
2198 g_queue_push_head (container->details->a11y_item_action_queue, ctx);
2199 if (container->details->a11y_item_action_idle_handler == 0) {
2200 container->details->a11y_item_action_idle_handler = g_idle_add (nautilus_canvas_item_accessible_idle_do_action, container);
2201 }
2202 break;
2203 default :
2204 g_warning ("Invalid action passed to NautilusCanvasItemAccessible::do_action");
2205 return FALSE;
2206 }
2207
2208 return TRUE;
2209 }
2210
2211 static int
2212 nautilus_canvas_item_accessible_get_n_actions (AtkAction *accessible)
2213 {
2214 return LAST_ACTION;
2215 }
2216
2217 static const char *
2218 nautilus_canvas_item_accessible_action_get_description (AtkAction *accessible,
2219 int i)
2220 {
2221 NautilusCanvasItemAccessiblePrivate *priv;
2222
2223 g_assert (i < LAST_ACTION);
2224
2225 priv = GET_ACCESSIBLE_PRIV (accessible);
2226
2227 if (priv->action_descriptions[i]) {
2228 return priv->action_descriptions[i];
2229 } else {
2230 return nautilus_canvas_item_accessible_action_descriptions[i];
2231 }
2232 }
2233
2234 static const char *
2235 nautilus_canvas_item_accessible_action_get_name (AtkAction *accessible, int i)
2236 {
2237 g_assert (i < LAST_ACTION);
2238
2239 return nautilus_canvas_item_accessible_action_names[i];
2240 }
2241
2242 static const char *
2243 nautilus_canvas_item_accessible_action_get_keybinding (AtkAction *accessible,
2244 int i)
2245 {
2246 g_assert (i < LAST_ACTION);
2247
2248 return NULL;
2249 }
2250
2251 static gboolean
2252 nautilus_canvas_item_accessible_action_set_description (AtkAction *accessible,
2253 int i,
2254 const char *description)
2255 {
2256 NautilusCanvasItemAccessiblePrivate *priv;
2257
2258 g_assert (i < LAST_ACTION);
2259
2260 priv = GET_ACCESSIBLE_PRIV (accessible);
2261
2262 if (priv->action_descriptions[i]) {
2263 g_free (priv->action_descriptions[i]);
2264 }
2265 priv->action_descriptions[i] = g_strdup (description);
2266
2267 return TRUE;
2268 }
2269
2270 static void
2271 nautilus_canvas_item_accessible_action_interface_init (AtkActionIface *iface)
2272 {
2273 iface->do_action = nautilus_canvas_item_accessible_do_action;
2274 iface->get_n_actions = nautilus_canvas_item_accessible_get_n_actions;
2275 iface->get_description = nautilus_canvas_item_accessible_action_get_description;
2276 iface->get_keybinding = nautilus_canvas_item_accessible_action_get_keybinding;
2277 iface->get_name = nautilus_canvas_item_accessible_action_get_name;
2278 iface->set_description = nautilus_canvas_item_accessible_action_set_description;
2279 }
2280
2281 static const gchar *
2282 nautilus_canvas_item_accessible_get_name (AtkObject *accessible)
2283 {
2284 NautilusCanvasItem *item;
2285
2286 if (accessible->name) {
2287 return accessible->name;
2288 }
2289
2290 item = NAUTILUS_CANVAS_ITEM (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (accessible)));
2291 if (!item) {
2292 return NULL;
2293 }
2294 return item->details->editable_text;
2295 }
2296
2297 static const gchar*
2298 nautilus_canvas_item_accessible_get_description (AtkObject *accessible)
2299 {
2300 NautilusCanvasItem *item;
2301
2302 item = NAUTILUS_CANVAS_ITEM (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (accessible)));
2303 if (!item) {
2304 return NULL;
2305 }
2306
2307 return item->details->additional_text;
2308 }
2309
2310 static AtkObject *
2311 nautilus_canvas_item_accessible_get_parent (AtkObject *accessible)
2312 {
2313 NautilusCanvasItem *item;
2314
2315 item = NAUTILUS_CANVAS_ITEM (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (accessible)));
2316 if (!item) {
2317 return NULL;
2318 }
2319
2320 return gtk_widget_get_accessible (GTK_WIDGET (EEL_CANVAS_ITEM (item)->canvas));
2321 }
2322
2323 static int
2324 nautilus_canvas_item_accessible_get_index_in_parent (AtkObject *accessible)
2325 {
2326 NautilusCanvasItem *item;
2327 NautilusCanvasContainer *container;
2328 GList *l;
2329 NautilusCanvasIcon *icon;
2330 int i;
2331
2332 item = NAUTILUS_CANVAS_ITEM (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (accessible)));
2333 if (!item) {
2334 return -1;
2335 }
2336
2337 container = NAUTILUS_CANVAS_CONTAINER (EEL_CANVAS_ITEM (item)->canvas);
2338
2339 l = container->details->icons;
2340 i = 0;
2341 while (l) {
2342 icon = l->data;
2343
2344 if (icon->item == item) {
2345 return i;
2346 }
2347
2348 i++;
2349 l = l->next;
2350 }
2351
2352 return -1;
2353 }
2354
2355 static const gchar *
2356 nautilus_canvas_item_accessible_get_image_description (AtkImage *image)
2357 {
2358 NautilusCanvasItemAccessiblePrivate *priv;
2359 NautilusCanvasItem *item;
2360 NautilusCanvasIcon *icon;
2361 NautilusCanvasContainer *container;
2362 char *description;
2363
2364 priv = GET_ACCESSIBLE_PRIV (image);
2365
2366 if (priv->image_description) {
2367 return priv->image_description;
2368 } else {
2369 item = NAUTILUS_CANVAS_ITEM (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (image)));
2370 if (item == NULL) {
2371 return NULL;
2372 }
2373 icon = item->user_data;
2374 container = NAUTILUS_CANVAS_CONTAINER (EEL_CANVAS_ITEM (item)->canvas);
2375 description = nautilus_canvas_container_get_icon_description (container, icon->data);
2376 g_free (priv->description);
2377 priv->description = description;
2378 return priv->description;
2379 }
2380 }
2381
2382 static void
2383 nautilus_canvas_item_accessible_get_image_size
2384 (AtkImage *image,
2385 gint *width,
2386 gint *height)
2387 {
2388 NautilusCanvasItem *item;
2389
2390 item = NAUTILUS_CANVAS_ITEM (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (image)));
2391
2392 if (!item || !item->details->pixbuf) {
2393 *width = *height = 0;
2394 } else {
2395 *width = gdk_pixbuf_get_width (item->details->pixbuf);
2396 *height = gdk_pixbuf_get_height (item->details->pixbuf);
2397 }
2398 }
2399
2400 static void
2401 nautilus_canvas_item_accessible_get_image_position
2402 (AtkImage *image,
2403 gint *x,
2404 gint *y,
2405 AtkCoordType coord_type)
2406 {
2407 NautilusCanvasItem *item;
2408 gint x_offset, y_offset, itmp;
2409
2410 item = NAUTILUS_CANVAS_ITEM (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (image)));
2411 if (!item) {
2412 return;
2413 }
2414 if (!item->details->icon_rect.x0 && !item->details->icon_rect.x1) {
2415 return;
2416 } else {
2417 x_offset = 0;
2418 y_offset = 0;
2419 if (item->details->text_width) {
2420 itmp = item->details->icon_rect.x0 -
2421 item->details->text_rect.x0;
2422 if (itmp > x_offset) {
2423 x_offset = itmp;
2424 }
2425 itmp = item->details->icon_rect.y0 -
2426 item->details->text_rect.y0;
2427 if (itmp > y_offset) {
2428 y_offset = itmp;
2429 }
2430 }
2431 }
2432 atk_component_get_position (ATK_COMPONENT (image), x, y, coord_type);
2433 *x += x_offset;
2434 *y += y_offset;
2435 }
2436
2437 static gboolean
2438 nautilus_canvas_item_accessible_set_image_description (AtkImage *image,
2439 const gchar *description)
2440 {
2441 NautilusCanvasItemAccessiblePrivate *priv;
2442
2443 priv = GET_ACCESSIBLE_PRIV (image);
2444
2445 g_free (priv->image_description);
2446 priv->image_description = g_strdup (description);
2447
2448 return TRUE;
2449 }
2450
2451 static void
2452 nautilus_canvas_item_accessible_image_interface_init (AtkImageIface *iface)
2453 {
2454 iface->get_image_description = nautilus_canvas_item_accessible_get_image_description;
2455 iface->set_image_description = nautilus_canvas_item_accessible_set_image_description;
2456 iface->get_image_size = nautilus_canvas_item_accessible_get_image_size;
2457 iface->get_image_position = nautilus_canvas_item_accessible_get_image_position;
2458 }
2459
2460 /* accessible text interface */
2461 static gint
2462 nautilus_canvas_item_accessible_get_offset_at_point (AtkText *text,
2463 gint x,
2464 gint y,
2465 AtkCoordType coords)
2466 {
2467 gint real_x, real_y, real_width, real_height;
2468 NautilusCanvasItem *item;
2469 gint editable_height;
2470 gint offset = 0;
2471 gint index;
2472 PangoLayout *layout, *editable_layout, *additional_layout;
2473 PangoRectangle rect0;
2474 char *canvas_text;
2475 gboolean have_editable;
2476 gboolean have_additional;
2477 gint text_offset;
2478
2479 atk_component_get_extents (ATK_COMPONENT (text), &real_x, &real_y,
2480 &real_width, &real_height, coords);
2481
2482 x -= real_x;
2483 y -= real_y;
2484
2485 item = NAUTILUS_CANVAS_ITEM (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (text)));
2486
2487 if (item->details->pixbuf) {
2488 y -= gdk_pixbuf_get_height (item->details->pixbuf);
2489 }
2490 have_editable = item->details->editable_text != NULL &&
2491 item->details->editable_text[0] != '\0';
2492 have_additional = item->details->additional_text != NULL &&item->details->additional_text[0] != '\0';
2493
2494 editable_layout = NULL;
2495 additional_layout = NULL;
2496 if (have_editable) {
2497 editable_layout = get_label_layout (&item->details->editable_text_layout, item, item->details->editable_text);
2498 prepare_pango_layout_for_draw (item, editable_layout);
2499 pango_layout_get_pixel_size (editable_layout, NULL, &editable_height);
2500 if (y >= editable_height &&
2501 have_additional) {
2502 prepare_pango_layout_for_draw (item, editable_layout);
2503 additional_layout = get_label_layout (&item->details->additional_text_layout, item, item->details->additional_text);
2504 layout = additional_layout;
2505 canvas_text = item->details->additional_text;
2506 y -= editable_height + LABEL_LINE_SPACING;
2507 } else {
2508 layout = editable_layout;
2509 canvas_text = item->details->editable_text;
2510 }
2511 } else if (have_additional) {
2512 additional_layout = get_label_layout (&item->details->additional_text_layout, item, item->details->additional_text);
2513 prepare_pango_layout_for_draw (item, additional_layout);
2514 layout = additional_layout;
2515 canvas_text = item->details->additional_text;
2516 } else {
2517 return 0;
2518 }
2519
2520 text_offset = 0;
2521 if (have_editable) {
2522 pango_layout_index_to_pos (editable_layout, 0, &rect0);
2523 text_offset = PANGO_PIXELS (rect0.x);
2524 }
2525 if (have_additional) {
2526 gint itmp;
2527
2528 pango_layout_index_to_pos (additional_layout, 0, &rect0);
2529 itmp = PANGO_PIXELS (rect0.x);
2530 if (itmp < text_offset) {
2531 text_offset = itmp;
2532 }
2533 }
2534 pango_layout_index_to_pos (layout, 0, &rect0);
2535 x += text_offset;
2536 if (!pango_layout_xy_to_index (layout,
2537 x * PANGO_SCALE,
2538 y * PANGO_SCALE,
2539 &index, NULL)) {
2540 if (x < 0 || y < 0) {
2541 index = 0;
2542 } else {
2543 index = -1;
2544 }
2545 }
2546 if (index == -1) {
2547 offset = g_utf8_strlen (canvas_text, -1);
2548 } else {
2549 offset = g_utf8_pointer_to_offset (canvas_text, canvas_text + index);
2550 }
2551 if (layout == additional_layout) {
2552 offset += g_utf8_strlen (item->details->editable_text, -1);
2553 }
2554
2555 if (editable_layout != NULL) {
2556 g_object_unref (editable_layout);
2557 }
2558
2559 if (additional_layout != NULL) {
2560 g_object_unref (additional_layout);
2561 }
2562
2563 return offset;
2564 }
2565
2566 static void
2567 nautilus_canvas_item_accessible_get_character_extents (AtkText *text,
2568 gint offset,
2569 gint *x,
2570 gint *y,
2571 gint *width,
2572 gint *height,
2573 AtkCoordType coords)
2574 {
2575 gint pos_x, pos_y;
2576 gint len, byte_offset;
2577 gint editable_height;
2578 gchar *canvas_text;
2579 NautilusCanvasItem *item;
2580 PangoLayout *layout, *editable_layout, *additional_layout;
2581 PangoRectangle rect;
2582 PangoRectangle rect0;
2583 gboolean have_editable;
2584 gint text_offset;
2585
2586 atk_component_get_position (ATK_COMPONENT (text), &pos_x, &pos_y, coords);
2587 item = NAUTILUS_CANVAS_ITEM (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (text)));
2588
2589 if (item->details->pixbuf) {
2590 pos_y += gdk_pixbuf_get_height (item->details->pixbuf);
2591 }
2592
2593 have_editable = item->details->editable_text != NULL &&
2594 item->details->editable_text[0] != '\0';
2595 if (have_editable) {
2596 len = g_utf8_strlen (item->details->editable_text, -1);
2597 } else {
2598 len = 0;
2599 }
2600
2601 editable_layout = get_label_layout (&item->details->editable_text_layout, item, item->details->editable_text);
2602 additional_layout = get_label_layout (&item->details->additional_text_layout, item, item->details->additional_text);
2603
2604 if (offset < len) {
2605 canvas_text = item->details->editable_text;
2606 layout = editable_layout;
2607 } else {
2608 offset -= len;
2609 canvas_text = item->details->additional_text;
2610 layout = additional_layout;
2611 pos_y += LABEL_LINE_SPACING;
2612 if (have_editable) {
2613 pango_layout_get_pixel_size (editable_layout, NULL, &editable_height);
2614 pos_y += editable_height;
2615 }
2616 }
2617 byte_offset = g_utf8_offset_to_pointer (canvas_text, offset) - canvas_text;
2618 pango_layout_index_to_pos (layout, byte_offset, &rect);
2619 text_offset = 0;
2620 if (have_editable) {
2621 pango_layout_index_to_pos (editable_layout, 0, &rect0);
2622 text_offset = PANGO_PIXELS (rect0.x);
2623 }
2624 if (item->details->additional_text != NULL &&
2625 item->details->additional_text[0] != '\0') {
2626 gint itmp;
2627
2628 pango_layout_index_to_pos (additional_layout, 0, &rect0);
2629 itmp = PANGO_PIXELS (rect0.x);
2630 if (itmp < text_offset) {
2631 text_offset = itmp;
2632 }
2633 }
2634
2635 g_object_unref (editable_layout);
2636 g_object_unref (additional_layout);
2637
2638 *x = pos_x + PANGO_PIXELS (rect.x) - text_offset;
2639 *y = pos_y + PANGO_PIXELS (rect.y);
2640 *width = PANGO_PIXELS (rect.width);
2641 *height = PANGO_PIXELS (rect.height);
2642 }
2643
2644 static void
2645 nautilus_canvas_item_accessible_text_interface_init (AtkTextIface *iface)
2646 {
2647 iface->get_text = eel_accessibility_text_get_text;
2648 iface->get_character_at_offset = eel_accessibility_text_get_character_at_offset;
2649 iface->get_text_before_offset = eel_accessibility_text_get_text_before_offset;
2650 iface->get_text_at_offset = eel_accessibility_text_get_text_at_offset;
2651 iface->get_text_after_offset = eel_accessibility_text_get_text_after_offset;
2652 iface->get_character_count = eel_accessibility_text_get_character_count;
2653 iface->get_character_extents = nautilus_canvas_item_accessible_get_character_extents;
2654 iface->get_offset_at_point = nautilus_canvas_item_accessible_get_offset_at_point;
2655 }
2656
2657 static GType nautilus_canvas_item_accessible_get_type (void);
2658
2659 G_DEFINE_TYPE_WITH_CODE (NautilusCanvasItemAccessible,
2660 nautilus_canvas_item_accessible,
2661 eel_canvas_item_accessible_get_type (),
2662 G_IMPLEMENT_INTERFACE (ATK_TYPE_IMAGE,
2663 nautilus_canvas_item_accessible_image_interface_init)
2664 G_IMPLEMENT_INTERFACE (ATK_TYPE_TEXT,
2665 nautilus_canvas_item_accessible_text_interface_init)
2666 G_IMPLEMENT_INTERFACE (ATK_TYPE_ACTION,
2667 nautilus_canvas_item_accessible_action_interface_init));
2668
2669 static AtkStateSet*
2670 nautilus_canvas_item_accessible_ref_state_set (AtkObject *accessible)
2671 {
2672 AtkStateSet *state_set;
2673 NautilusCanvasItem *item;
2674 NautilusCanvasContainer *container;
2675 NautilusCanvasIcon *icon;
2676 GList *l;
2677 gboolean one_item_selected;
2678
2679 state_set = ATK_OBJECT_CLASS (nautilus_canvas_item_accessible_parent_class)->ref_state_set (accessible);
2680
2681 item = NAUTILUS_CANVAS_ITEM (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (accessible)));
2682 if (!item) {
2683 atk_state_set_add_state (state_set, ATK_STATE_DEFUNCT);
2684 return state_set;
2685 }
2686 container = NAUTILUS_CANVAS_CONTAINER (EEL_CANVAS_ITEM (item)->canvas);
2687 if (item->details->is_highlighted_as_keyboard_focus) {
2688 atk_state_set_add_state (state_set, ATK_STATE_FOCUSED);
2689 } else if (!container->details->keyboard_focus) {
2690
2691 one_item_selected = FALSE;
2692 l = container->details->icons;
2693 while (l) {
2694 icon = l->data;
2695
2696 if (icon->item == item) {
2697 if (icon->is_selected) {
2698 one_item_selected = TRUE;
2699 } else {
2700 break;
2701 }
2702 } else if (icon->is_selected) {
2703 one_item_selected = FALSE;
2704 break;
2705 }
2706
2707 l = l->next;
2708 }
2709
2710 if (one_item_selected) {
2711 atk_state_set_add_state (state_set, ATK_STATE_FOCUSED);
2712 }
2713 }
2714
2715 return state_set;
2716 }
2717
2718 static void
2719 nautilus_canvas_item_accessible_finalize (GObject *object)
2720 {
2721 NautilusCanvasItemAccessiblePrivate *priv;
2722 int i;
2723
2724 priv = GET_ACCESSIBLE_PRIV (object);
2725
2726 for (i = 0; i < LAST_ACTION; i++) {
2727 g_free (priv->action_descriptions[i]);
2728 }
2729 g_free (priv->image_description);
2730 g_free (priv->description);
2731
2732 G_OBJECT_CLASS (nautilus_canvas_item_accessible_parent_class)->finalize (object);
2733 }
2734
2735 static void
2736 nautilus_canvas_item_accessible_initialize (AtkObject *accessible,
2737 gpointer widget)
2738 {
2739 ATK_OBJECT_CLASS (nautilus_canvas_item_accessible_parent_class)->initialize (accessible, widget);
2740
2741 atk_object_set_role (accessible, ATK_ROLE_CANVAS);
2742 }
2743
2744 static void
2745 nautilus_canvas_item_accessible_class_init (NautilusCanvasItemAccessibleClass *klass)
2746 {
2747 AtkObjectClass *aclass = ATK_OBJECT_CLASS (klass);
2748 GObjectClass *oclass = G_OBJECT_CLASS (klass);
2749
2750 oclass->finalize = nautilus_canvas_item_accessible_finalize;
2751
2752 aclass->initialize = nautilus_canvas_item_accessible_initialize;
2753
2754 aclass->get_name = nautilus_canvas_item_accessible_get_name;
2755 aclass->get_description = nautilus_canvas_item_accessible_get_description;
2756 aclass->get_parent = nautilus_canvas_item_accessible_get_parent;
2757 aclass->get_index_in_parent = nautilus_canvas_item_accessible_get_index_in_parent;
2758 aclass->ref_state_set = nautilus_canvas_item_accessible_ref_state_set;
2759
2760 g_type_class_add_private (klass, sizeof (NautilusCanvasItemAccessiblePrivate));
2761 }
2762
2763 static void
2764 nautilus_canvas_item_accessible_init (NautilusCanvasItemAccessible *self)
2765 {
2766 self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, nautilus_canvas_item_accessible_get_type (),
2767 NautilusCanvasItemAccessiblePrivate);
2768 }
2769
2770 /* dummy typedef */
2771 typedef AtkObjectFactory NautilusCanvasItemAccessibleFactory;
2772 typedef AtkObjectFactoryClass NautilusCanvasItemAccessibleFactoryClass;
2773
2774 G_DEFINE_TYPE (NautilusCanvasItemAccessibleFactory, nautilus_canvas_item_accessible_factory,
2775 ATK_TYPE_OBJECT_FACTORY);
2776
2777 static AtkObject *
2778 nautilus_canvas_item_accessible_factory_create_accessible (GObject *for_object)
2779 {
2780 AtkObject *accessible;
2781 NautilusCanvasItem *item;
2782 GString *item_text;
2783
2784 item = NAUTILUS_CANVAS_ITEM (for_object);
2785 g_assert (item != NULL);
2786
2787 item_text = g_string_new (NULL);
2788 if (item->details->editable_text) {
2789 g_string_append (item_text, item->details->editable_text);
2790 }
2791 if (item->details->additional_text) {
2792 g_string_append (item_text, item->details->additional_text);
2793 }
2794
2795 item->details->text_util = gail_text_util_new ();
2796 gail_text_util_text_setup (item->details->text_util,
2797 item_text->str);
2798 g_string_free (item_text, TRUE);
2799
2800 accessible = g_object_new (nautilus_canvas_item_accessible_get_type (), NULL);
2801 atk_object_initialize (accessible, for_object);
2802
2803 return accessible;
2804 }
2805
2806 static GType
2807 nautilus_canvas_item_accessible_factory_get_accessible_type (void)
2808 {
2809 return nautilus_canvas_item_accessible_get_type ();
2810 }
2811
2812 static void
2813 nautilus_canvas_item_accessible_factory_init (NautilusCanvasItemAccessibleFactory *self)
2814 {
2815 }
2816
2817 static void
2818 nautilus_canvas_item_accessible_factory_class_init (NautilusCanvasItemAccessibleFactoryClass *klass)
2819 {
2820 klass->create_accessible = nautilus_canvas_item_accessible_factory_create_accessible;
2821 klass->get_accessible_type = nautilus_canvas_item_accessible_factory_get_accessible_type;
2822 }