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