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