1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2
3 /* Caja - 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 St, Fifth Floor,
22 * Boston, MA 02110-1301, USA.
23 */
24
25 #include <config.h>
26 #include <math.h>
27 #include <glib/gi18n.h>
28 #include <gdk-pixbuf/gdk-pixbuf.h>
29 #include <gtk/gtk.h>
30 #include <gdk/gdk.h>
31 #include <glib/gi18n.h>
32 #include <atk/atkimage.h>
33 #include <atk/atkcomponent.h>
34 #include <atk/atknoopobject.h>
35 #include <stdio.h>
36 #include <string.h>
37
38 #include <eel/eel-art-extensions.h>
39 #include <eel/eel-gdk-extensions.h>
40 #include <eel/eel-gdk-pixbuf-extensions.h>
41 #include <eel/eel-glib-extensions.h>
42 #include <eel/eel-graphic-effects.h>
43 #include <eel/eel-gtk-macros.h>
44 #include <eel/eel-string.h>
45 #include <eel/eel-accessibility.h>
46
47 #include "caja-icon-canvas-item.h"
48 #include "caja-file-utilities.h"
49 #include "caja-global-preferences.h"
50 #include "caja-icon-private.h"
51
52 #define EMBLEM_SPACING 2
53
54 /* gap between bottom of icon and start of text box */
55 #define LABEL_OFFSET 1
56 #define LABEL_LINE_SPACING 0
57
58 #define MAX_TEXT_WIDTH_STANDARD 135
59 #define MAX_TEXT_WIDTH_TIGHTER 80
60 #define MAX_TEXT_WIDTH_BESIDE 90
61 #define MAX_TEXT_WIDTH_BESIDE_TOP_TO_BOTTOM 150
62
63 #ifndef PANGO_CHECK_VERSION
64 #define PANGO_CHECK_VERSION(major, minor, micro) \
65 (PANGO_VERSION_MAJOR > (major) || \
66 (PANGO_VERSION_MAJOR == (major) && PANGO_VERSION_MINOR > (minor)) || \
67 (PANGO_VERSION_MAJOR == (major) && PANGO_VERSION_MINOR == (minor) && \
68 PANGO_VERSION_MICRO >= (micro)))
69 #endif
70
71 /* special text height handling
72 * each item has three text height variables:
73 * + text_height: actual height of the displayed (i.e. on-screen) PangoLayout.
74 * + text_height_for_layout: height used in icon grid layout algorithms.
75 * “sane amount” of text.
76 * “sane amount“ as of
77 * + hard-coded to three lines in text-below-icon mode.
78 * + unlimited in text-besides-icon mode (see VOODOO-TODO)
79 *
80 * This layout height is used by grid layout algorithms, even
81 * though the actually displayed and/or requested text size may be larger
82 * and overlap adjacent icons, if an icon is selected.
83 *
84 * + text_height_for_entire_text: height needed to display the entire PangoLayout,
85 * if it wasn't ellipsized.
86 */
87
88 /* Private part of the CajaIconCanvasItem structure. */
89 struct _CajaIconCanvasItemPrivate
90 {
91 /* The image, text, font. */
92 double x, y;
93 GdkPixbuf *pixbuf;
94 cairo_surface_t *rendered_surface;
95 GList *emblem_pixbufs;
96 char *editable_text; /* Text that can be modified by a renaming function */
97 char *additional_text; /* Text that cannot be modifed, such as file size, etc. */
98 GdkPoint *attach_points;
99 int n_attach_points;
100
101 /* Size of the text at current font. */
102 int text_dx;
103 int text_width;
104
105 /* actual size required for rendering the text to display */
106 int text_height;
107 /* actual size that would be required for rendering the entire text if it wasn't ellipsized */
108 int text_height_for_entire_text;
109 /* actual size needed for rendering a “sane amount” of text */
110 int text_height_for_layout;
111
112 int editable_text_height;
113
114 /* whether the entire text must always be visible. In that case,
115 * text_height_for_layout will always be equal to text_height.
116 * Used for the last line of a line-wise icon layout. */
117 guint entire_text : 1;
118
119 /* preview state */
120 guint is_active : 1;
121
122 /* Highlight state. */
123 guint is_highlighted_for_selection : 1;
124 guint is_highlighted_as_keyboard_focus: 1;
125 guint is_highlighted_for_drop : 1;
126 guint is_highlighted_for_clipboard : 1;
127 guint show_stretch_handles : 1;
128 guint is_prelit : 1;
129
130 guint rendered_is_active : 1;
131 guint rendered_is_highlighted_for_selection : 1;
132 guint rendered_is_highlighted_for_drop : 1;
133 guint rendered_is_highlighted_for_clipboard : 1;
134 guint rendered_is_prelit : 1;
135 guint rendered_is_focused : 1;
136
137 guint is_renaming : 1;
138
139 guint bounds_cached : 1;
140
141 guint is_visible : 1;
142
143 GdkRectangle embedded_text_rect;
144 char *embedded_text;
145
146 /* Cached PangoLayouts. Only used if the icon is visible */
147 PangoLayout *editable_text_layout;
148 PangoLayout *additional_text_layout;
149 PangoLayout *embedded_text_layout;
150
151 /* Cached rectangle in canvas coordinates */
152 EelIRect canvas_rect;
153 EelIRect text_rect;
154 EelIRect emblem_rect;
155
156 EelIRect bounds_cache;
157 EelIRect bounds_cache_for_layout;
158 EelIRect bounds_cache_for_entire_item;
159
160 GdkWindow *cursor_window;
161
162 /* Accessibility bits */
163 GailTextUtil *text_util;
164 };
165
166 /* Object argument IDs. */
167 enum
168 {
169 PROP_0,
170 PROP_EDITABLE_TEXT,
171 PROP_ADDITIONAL_TEXT,
172 PROP_HIGHLIGHTED_FOR_SELECTION,
173 PROP_HIGHLIGHTED_AS_KEYBOARD_FOCUS,
174 PROP_HIGHLIGHTED_FOR_DROP,
175 PROP_HIGHLIGHTED_FOR_CLIPBOARD
176 };
177
178 typedef enum
179 {
180 RIGHT_SIDE,
181 BOTTOM_SIDE,
182 LEFT_SIDE,
183 TOP_SIDE
184 } RectangleSide;
185
186 typedef struct
187 {
188 CajaIconCanvasItem *icon_item;
189 EelIRect icon_rect;
190 RectangleSide side;
191 int position;
192 int index;
193 GList *emblem;
194 } EmblemLayout;
195
196 static int click_policy_auto_value;
197
198 static void caja_icon_canvas_item_text_interface_init (EelAccessibleTextIface *iface);
199 static GType caja_icon_canvas_item_accessible_factory_get_type (void);
200
201 G_DEFINE_TYPE_WITH_CODE (CajaIconCanvasItem, caja_icon_canvas_item, EEL_TYPE_CANVAS_ITEM,
202 G_ADD_PRIVATE (CajaIconCanvasItem)
203 G_IMPLEMENT_INTERFACE (EEL_TYPE_ACCESSIBLE_TEXT,
204 caja_icon_canvas_item_text_interface_init));
205
206 /* private */
207 static void draw_label_text (CajaIconCanvasItem *item,
208 cairo_t *cr,
209 gboolean create_mask,
210 EelIRect icon_rect);
211 static void measure_label_text (CajaIconCanvasItem *item);
212 static void get_icon_canvas_rectangle (CajaIconCanvasItem *item,
213 EelIRect *rect);
214 static void emblem_layout_reset (EmblemLayout *layout,
215 CajaIconCanvasItem *icon_item,
216 EelIRect icon_rect,
217 gboolean is_rtl);
218 static gboolean emblem_layout_next (EmblemLayout *layout,
219 GdkPixbuf **emblem_pixbuf,
220 EelIRect *emblem_rect,
221 gboolean is_rtl);
222 static void draw_pixbuf (GdkPixbuf *pixbuf,
223 cairo_t *cr,
224 int x,
225 int y);
226 static PangoLayout *get_label_layout (PangoLayout **layout,
227 CajaIconCanvasItem *item,
228 const char *text);
229
230 static gboolean hit_test_stretch_handle (CajaIconCanvasItem *item,
231 EelIRect canvas_rect,
232 GtkCornerType *corner);
233 static void draw_embedded_text (CajaIconCanvasItem *icon_item,
234 cairo_t *cr,
235 int x,
236 int y);
237
238 static void caja_icon_canvas_item_ensure_bounds_up_to_date (CajaIconCanvasItem *icon_item);
239
240
241 /* Object initialization function for the icon item. */
242 static void
caja_icon_canvas_item_init(CajaIconCanvasItem * icon_item)243 caja_icon_canvas_item_init (CajaIconCanvasItem *icon_item)
244 {
245 static gboolean setup_auto_enums = FALSE;
246
247 if (!setup_auto_enums)
248 {
249 eel_g_settings_add_auto_enum
250 (caja_preferences,
251 CAJA_PREFERENCES_CLICK_POLICY,
252 &click_policy_auto_value);
253 setup_auto_enums = TRUE;
254 }
255
256 icon_item->details = caja_icon_canvas_item_get_instance_private (icon_item);
257 caja_icon_canvas_item_invalidate_label_size (icon_item);
258 }
259
260 static void
caja_icon_canvas_item_finalize(GObject * object)261 caja_icon_canvas_item_finalize (GObject *object)
262 {
263 CajaIconCanvasItemPrivate *details;
264
265 g_assert (CAJA_IS_ICON_CANVAS_ITEM (object));
266
267 details = CAJA_ICON_CANVAS_ITEM (object)->details;
268
269 if (details->cursor_window != NULL)
270 {
271 gdk_window_set_cursor (details->cursor_window, NULL);
272 g_object_unref (details->cursor_window);
273 }
274
275 if (details->pixbuf != NULL)
276 {
277 g_object_unref (details->pixbuf);
278 }
279
280 if (details->text_util != NULL)
281 {
282 g_object_unref (details->text_util);
283 }
284
285 g_list_free_full (details->emblem_pixbufs, g_object_unref);
286 g_free (details->editable_text);
287 g_free (details->additional_text);
288 g_free (details->attach_points);
289
290 if (details->rendered_surface != NULL)
291 {
292 cairo_surface_destroy (details->rendered_surface);
293 }
294
295 if (details->editable_text_layout != NULL)
296 {
297 g_object_unref (details->editable_text_layout);
298 }
299
300 if (details->additional_text_layout != NULL)
301 {
302 g_object_unref (details->additional_text_layout);
303 }
304
305 if (details->embedded_text_layout != NULL)
306 {
307 g_object_unref (details->embedded_text_layout);
308 }
309
310 g_free (details->embedded_text);
311
312 G_OBJECT_CLASS (caja_icon_canvas_item_parent_class)->finalize (object);
313 }
314
315 /* Currently we require pixbufs in this format (for hit testing).
316 * Perhaps gdk-pixbuf will be changed so it can do the hit testing
317 * and we won't have this requirement any more.
318 */
319 static gboolean
pixbuf_is_acceptable(GdkPixbuf * pixbuf)320 pixbuf_is_acceptable (GdkPixbuf *pixbuf)
321 {
322 return gdk_pixbuf_get_colorspace (pixbuf) == GDK_COLORSPACE_RGB
323 && ((!gdk_pixbuf_get_has_alpha (pixbuf)
324 && gdk_pixbuf_get_n_channels (pixbuf) == 3)
325 || (gdk_pixbuf_get_has_alpha (pixbuf)
326 && gdk_pixbuf_get_n_channels (pixbuf) == 4))
327 && gdk_pixbuf_get_bits_per_sample (pixbuf) == 8;
328 }
329
330 static void
caja_icon_canvas_item_invalidate_bounds_cache(CajaIconCanvasItem * item)331 caja_icon_canvas_item_invalidate_bounds_cache (CajaIconCanvasItem *item)
332 {
333 item->details->bounds_cached = FALSE;
334 }
335
336 /* invalidate the text width and height cached in the item details. */
337 void
caja_icon_canvas_item_invalidate_label_size(CajaIconCanvasItem * item)338 caja_icon_canvas_item_invalidate_label_size (CajaIconCanvasItem *item)
339 {
340 if (item->details->editable_text_layout != NULL)
341 {
342 pango_layout_context_changed (item->details->editable_text_layout);
343 }
344 if (item->details->additional_text_layout != NULL)
345 {
346 pango_layout_context_changed (item->details->additional_text_layout);
347 }
348 if (item->details->embedded_text_layout != NULL)
349 {
350 pango_layout_context_changed (item->details->embedded_text_layout);
351 }
352 caja_icon_canvas_item_invalidate_bounds_cache (item);
353 item->details->text_width = -1;
354 item->details->text_height = -1;
355 item->details->text_height_for_layout = -1;
356 item->details->text_height_for_entire_text = -1;
357 item->details->editable_text_height = -1;
358 }
359
360 /* Set property handler for the icon item. */
361 static void
caja_icon_canvas_item_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)362 caja_icon_canvas_item_set_property (GObject *object,
363 guint property_id,
364 const GValue *value,
365 GParamSpec *pspec)
366 {
367 CajaIconCanvasItem *item;
368 CajaIconCanvasItemPrivate *details;
369 AtkObject *accessible;
370
371 item = CAJA_ICON_CANVAS_ITEM (object);
372 details = item->details;
373 accessible = atk_gobject_accessible_for_object (G_OBJECT (item));
374
375 switch (property_id)
376 {
377
378 case PROP_EDITABLE_TEXT:
379 if (g_strcmp0 (details->editable_text,
380 g_value_get_string (value)) == 0)
381 {
382 return;
383 }
384
385 g_free (details->editable_text);
386 details->editable_text = g_strdup (g_value_get_string (value));
387 if (details->text_util)
388 {
389 gail_text_util_text_setup (details->text_util,
390 details->editable_text);
391 }
392
393 caja_icon_canvas_item_invalidate_label_size (item);
394 if (details->editable_text_layout)
395 {
396 g_object_unref (details->editable_text_layout);
397 details->editable_text_layout = NULL;
398 }
399 break;
400
401 case PROP_ADDITIONAL_TEXT:
402 if (g_strcmp0 (details->additional_text,
403 g_value_get_string (value)) == 0)
404 {
405 return;
406 }
407
408 g_free (details->additional_text);
409 details->additional_text = g_strdup (g_value_get_string (value));
410
411 caja_icon_canvas_item_invalidate_label_size (item);
412 if (details->additional_text_layout)
413 {
414 g_object_unref (details->additional_text_layout);
415 details->additional_text_layout = NULL;
416 }
417 break;
418
419 case PROP_HIGHLIGHTED_FOR_SELECTION:
420 if (!details->is_highlighted_for_selection == !g_value_get_boolean (value))
421 {
422 return;
423 }
424 details->is_highlighted_for_selection = g_value_get_boolean (value);
425 caja_icon_canvas_item_invalidate_label_size (item);
426
427 atk_object_notify_state_change (accessible, ATK_STATE_SELECTED,
428 details->is_highlighted_for_selection);
429 break;
430
431 case PROP_HIGHLIGHTED_AS_KEYBOARD_FOCUS:
432 if (!details->is_highlighted_as_keyboard_focus == !g_value_get_boolean (value))
433 {
434 return;
435 }
436 details->is_highlighted_as_keyboard_focus = g_value_get_boolean (value);
437 atk_object_notify_state_change (accessible, ATK_STATE_FOCUSED,
438 details->is_highlighted_as_keyboard_focus);
439 break;
440
441 case PROP_HIGHLIGHTED_FOR_DROP:
442 if (!details->is_highlighted_for_drop == !g_value_get_boolean (value))
443 {
444 return;
445 }
446 details->is_highlighted_for_drop = g_value_get_boolean (value);
447 break;
448
449 case PROP_HIGHLIGHTED_FOR_CLIPBOARD:
450 if (!details->is_highlighted_for_clipboard == !g_value_get_boolean (value))
451 {
452 return;
453 }
454 details->is_highlighted_for_clipboard = g_value_get_boolean (value);
455 break;
456
457 default:
458 g_warning ("caja_icons_view_item_item_set_arg on unknown argument");
459 return;
460 }
461
462 eel_canvas_item_request_update (EEL_CANVAS_ITEM (object));
463 }
464
465 /* Get property handler for the icon item */
466 static void
caja_icon_canvas_item_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)467 caja_icon_canvas_item_get_property (GObject *object,
468 guint property_id,
469 GValue *value,
470 GParamSpec *pspec)
471 {
472 CajaIconCanvasItemPrivate *details;
473
474 details = CAJA_ICON_CANVAS_ITEM (object)->details;
475
476 switch (property_id)
477 {
478
479 case PROP_EDITABLE_TEXT:
480 g_value_set_string (value, details->editable_text);
481 break;
482
483 case PROP_ADDITIONAL_TEXT:
484 g_value_set_string (value, details->additional_text);
485 break;
486
487 case PROP_HIGHLIGHTED_FOR_SELECTION:
488 g_value_set_boolean (value, details->is_highlighted_for_selection);
489 break;
490
491 case PROP_HIGHLIGHTED_AS_KEYBOARD_FOCUS:
492 g_value_set_boolean (value, details->is_highlighted_as_keyboard_focus);
493 break;
494
495 case PROP_HIGHLIGHTED_FOR_DROP:
496 g_value_set_boolean (value, details->is_highlighted_for_drop);
497 break;
498
499 case PROP_HIGHLIGHTED_FOR_CLIPBOARD:
500 g_value_set_boolean (value, details->is_highlighted_for_clipboard);
501 break;
502
503 default:
504 g_warning ("invalid property %d", property_id);
505 break;
506 }
507 }
508
509 static void
get_scaled_icon_size(CajaIconCanvasItem * item,gint * width,gint * height)510 get_scaled_icon_size (CajaIconCanvasItem *item,
511 gint *width,
512 gint *height)
513 {
514 GdkPixbuf *pixbuf = NULL;
515 gint scale = 1;
516
517 if (item != NULL) {
518 EelCanvas *canvas;
519
520 canvas = EEL_CANVAS_ITEM (item)->canvas;
521 scale = gtk_widget_get_scale_factor (GTK_WIDGET (canvas));
522 pixbuf = item->details->pixbuf;
523 }
524
525 if (width)
526 *width = (pixbuf == NULL) ? 0 : (gdk_pixbuf_get_width (pixbuf) / scale);
527 if (height)
528 *height = (pixbuf == NULL) ? 0 : (gdk_pixbuf_get_height (pixbuf) / scale);
529 }
530
531 cairo_surface_t *
caja_icon_canvas_item_get_drag_surface(CajaIconCanvasItem * item)532 caja_icon_canvas_item_get_drag_surface (CajaIconCanvasItem *item)
533 {
534 cairo_surface_t *surface;
535
536 EelCanvas *canvas;
537 int width, height;
538 int pix_width, pix_height;
539 int item_offset_x, item_offset_y;
540 EelIRect icon_rect;
541 EelIRect emblem_rect;
542 GdkPixbuf *emblem_pixbuf;
543 EmblemLayout emblem_layout;
544 double item_x, item_y;
545 gboolean is_rtl;
546 cairo_t *cr;
547 GtkStyleContext *context;
548 cairo_surface_t *drag_surface;
549
550 g_return_val_if_fail (CAJA_IS_ICON_CANVAS_ITEM (item), NULL);
551
552 canvas = EEL_CANVAS_ITEM (item)->canvas;
553 context = gtk_widget_get_style_context (GTK_WIDGET (canvas));
554
555 gtk_style_context_save (context);
556 gtk_style_context_add_class (context, "caja-canvas-item");
557
558 /* Assume we're updated so canvas item data is right */
559
560 /* Calculate the offset from the top-left corner of the
561 new image to the item position (where the pixmap is placed) */
562 eel_canvas_world_to_window (canvas,
563 item->details->x, item->details->y,
564 &item_x, &item_y);
565
566 item_offset_x = item_x - EEL_CANVAS_ITEM (item)->x1;
567 item_offset_y = item_y - EEL_CANVAS_ITEM (item)->y1;
568
569 /* Calculate the width of the item */
570 width = EEL_CANVAS_ITEM (item)->x2 - EEL_CANVAS_ITEM (item)->x1;
571 height = EEL_CANVAS_ITEM (item)->y2 - EEL_CANVAS_ITEM (item)->y1;
572
573 surface = gdk_window_create_similar_surface (gtk_widget_get_window (GTK_WIDGET (canvas)),
574 CAIRO_CONTENT_COLOR_ALPHA,
575 width, height);
576
577 cr = cairo_create (surface);
578
579 drag_surface = gdk_cairo_surface_create_from_pixbuf (item->details->pixbuf,
580 gtk_widget_get_scale_factor (GTK_WIDGET (canvas)),
581 gtk_widget_get_window (GTK_WIDGET (canvas)));
582 gtk_render_icon_surface (context, cr, drag_surface,
583 item_offset_x, item_offset_y);
584 cairo_surface_destroy (drag_surface);
585
586 get_scaled_icon_size (item, &pix_width, &pix_height);
587
588 icon_rect.x0 = item_offset_x;
589 icon_rect.y0 = item_offset_y;
590 icon_rect.x1 = item_offset_x + pix_width;
591 icon_rect.y1 = item_offset_y + pix_height;
592
593 is_rtl = caja_icon_container_is_layout_rtl (CAJA_ICON_CONTAINER (canvas));
594
595 emblem_layout_reset (&emblem_layout, item, icon_rect, is_rtl);
596
597 while (emblem_layout_next (&emblem_layout, &emblem_pixbuf, &emblem_rect, is_rtl))
598 {
599 gdk_cairo_set_source_pixbuf (cr, emblem_pixbuf, emblem_rect.x0, emblem_rect.y0);
600 cairo_rectangle (cr, emblem_rect.x0, emblem_rect.y0,
601 gdk_pixbuf_get_width (emblem_pixbuf),
602 gdk_pixbuf_get_height (emblem_pixbuf));
603 cairo_fill (cr);
604 }
605
606 draw_embedded_text (item, cr,
607 item_offset_x, item_offset_y);
608 draw_label_text (item, cr, FALSE, icon_rect);
609 cairo_destroy (cr);
610
611 gtk_style_context_restore (context);
612
613 return surface;
614
615 }
616
617 void
caja_icon_canvas_item_set_image(CajaIconCanvasItem * item,GdkPixbuf * image)618 caja_icon_canvas_item_set_image (CajaIconCanvasItem *item,
619 GdkPixbuf *image)
620 {
621 CajaIconCanvasItemPrivate *details;
622
623 g_return_if_fail (CAJA_IS_ICON_CANVAS_ITEM (item));
624 g_return_if_fail (image == NULL || pixbuf_is_acceptable (image));
625
626 details = item->details;
627 if (details->pixbuf == image)
628 {
629 return;
630 }
631
632 if (image != NULL)
633 {
634 g_object_ref (image);
635 }
636 if (details->pixbuf != NULL)
637 {
638 g_object_unref (details->pixbuf);
639 }
640 if (details->rendered_surface != NULL)
641 {
642 cairo_surface_destroy (details->rendered_surface);
643 details->rendered_surface = NULL;
644 }
645
646 details->pixbuf = image;
647
648 caja_icon_canvas_item_invalidate_bounds_cache (item);
649 eel_canvas_item_request_update (EEL_CANVAS_ITEM (item));
650 }
651
652 void
caja_icon_canvas_item_set_emblems(CajaIconCanvasItem * item,GList * emblem_pixbufs)653 caja_icon_canvas_item_set_emblems (CajaIconCanvasItem *item,
654 GList *emblem_pixbufs)
655 {
656 GList *p;
657
658 g_return_if_fail (CAJA_IS_ICON_CANVAS_ITEM (item));
659
660 g_assert (item->details->emblem_pixbufs != emblem_pixbufs || emblem_pixbufs == NULL);
661
662 /* The case where the emblems are identical is fairly common,
663 * so lets take the time to check for it.
664 */
665 if (eel_g_list_equal (item->details->emblem_pixbufs, emblem_pixbufs))
666 {
667 return;
668 }
669
670 /* Check if they are acceptable. */
671 for (p = emblem_pixbufs; p != NULL; p = p->next)
672 {
673 g_return_if_fail (pixbuf_is_acceptable (p->data));
674 }
675
676 /* Take in the new list of emblems. */
677 g_list_foreach (emblem_pixbufs, (GFunc) g_object_ref, NULL);
678 g_list_free_full (item->details->emblem_pixbufs, g_object_unref);
679 item->details->emblem_pixbufs = g_list_copy (emblem_pixbufs);
680
681 caja_icon_canvas_item_invalidate_bounds_cache (item);
682 eel_canvas_item_request_update (EEL_CANVAS_ITEM (item));
683 }
684
685 void
caja_icon_canvas_item_set_attach_points(CajaIconCanvasItem * item,GdkPoint * attach_points,int n_attach_points)686 caja_icon_canvas_item_set_attach_points (CajaIconCanvasItem *item,
687 GdkPoint *attach_points,
688 int n_attach_points)
689 {
690 g_free (item->details->attach_points);
691 item->details->attach_points = NULL;
692 item->details->n_attach_points = 0;
693
694 if (attach_points != NULL && n_attach_points != 0)
695 {
696 item->details->attach_points = g_memdup (attach_points, n_attach_points * sizeof (GdkPoint));
697 item->details->n_attach_points = n_attach_points;
698 }
699
700 caja_icon_canvas_item_invalidate_bounds_cache (item);
701 }
702
703 void
caja_icon_canvas_item_set_embedded_text_rect(CajaIconCanvasItem * item,const GdkRectangle * text_rect)704 caja_icon_canvas_item_set_embedded_text_rect (CajaIconCanvasItem *item,
705 const GdkRectangle *text_rect)
706 {
707 item->details->embedded_text_rect = *text_rect;
708
709 caja_icon_canvas_item_invalidate_bounds_cache (item);
710 eel_canvas_item_request_update (EEL_CANVAS_ITEM (item));
711 }
712
713 void
caja_icon_canvas_item_set_embedded_text(CajaIconCanvasItem * item,const char * text)714 caja_icon_canvas_item_set_embedded_text (CajaIconCanvasItem *item,
715 const char *text)
716 {
717 g_free (item->details->embedded_text);
718 item->details->embedded_text = g_strdup (text);
719
720 if (item->details->embedded_text_layout != NULL)
721 {
722 if (text != NULL)
723 {
724 pango_layout_set_text (item->details->embedded_text_layout, text, -1);
725 }
726 else
727 {
728 pango_layout_set_text (item->details->embedded_text_layout, "", -1);
729 }
730 }
731
732 eel_canvas_item_request_update (EEL_CANVAS_ITEM (item));
733 }
734
735
736 /* Recomputes the bounding box of a icon canvas item.
737 * This is a generic implementation that could be used for any canvas item
738 * class, it has no assumptions about how the item is used.
739 */
740 static void
recompute_bounding_box(CajaIconCanvasItem * icon_item,double i2w_dx,double i2w_dy)741 recompute_bounding_box (CajaIconCanvasItem *icon_item,
742 double i2w_dx, double i2w_dy)
743 {
744 /* The bounds stored in the item is the same as what get_bounds
745 * returns, except it's in canvas coordinates instead of the item's
746 * parent's coordinates.
747 */
748
749 EelCanvasItem *item;
750 EelDPoint top_left, bottom_right;
751
752 item = EEL_CANVAS_ITEM (icon_item);
753
754 eel_canvas_item_get_bounds (item,
755 &top_left.x, &top_left.y,
756 &bottom_right.x, &bottom_right.y);
757
758 top_left.x += i2w_dx;
759 top_left.y += i2w_dy;
760 bottom_right.x += i2w_dx;
761 bottom_right.y += i2w_dy;
762 eel_canvas_w2c_d (item->canvas,
763 top_left.x, top_left.y,
764 &item->x1, &item->y1);
765 eel_canvas_w2c_d (item->canvas,
766 bottom_right.x, bottom_right.y,
767 &item->x2, &item->y2);
768 }
769
770 static EelIRect
compute_text_rectangle(const CajaIconCanvasItem * item,EelIRect icon_rectangle,gboolean canvas_coords,CajaIconCanvasItemBoundsUsage usage)771 compute_text_rectangle (const CajaIconCanvasItem *item,
772 EelIRect icon_rectangle,
773 gboolean canvas_coords,
774 CajaIconCanvasItemBoundsUsage usage)
775 {
776 EelIRect text_rectangle;
777 double pixels_per_unit;
778 double text_width, text_height, text_height_for_layout, text_height_for_entire_text, real_text_height, text_dx;
779
780 pixels_per_unit = EEL_CANVAS_ITEM (item)->canvas->pixels_per_unit;
781 if (canvas_coords)
782 {
783 text_width = item->details->text_width;
784 text_height = item->details->text_height;
785 text_height_for_layout = item->details->text_height_for_layout;
786 text_height_for_entire_text = item->details->text_height_for_entire_text;
787 text_dx = item->details->text_dx;
788 }
789 else
790 {
791 text_width = item->details->text_width / pixels_per_unit;
792 text_height = item->details->text_height / pixels_per_unit;
793 text_height_for_layout = item->details->text_height_for_layout / pixels_per_unit;
794 text_height_for_entire_text = item->details->text_height_for_entire_text / pixels_per_unit;
795 text_dx = item->details->text_dx / pixels_per_unit;
796 }
797
798 if (CAJA_ICON_CONTAINER (EEL_CANVAS_ITEM (item)->canvas)->details->label_position == CAJA_ICON_LABEL_POSITION_BESIDE)
799 {
800 if (!caja_icon_container_is_layout_rtl (CAJA_ICON_CONTAINER (EEL_CANVAS_ITEM (item)->canvas)))
801 {
802 text_rectangle.x0 = icon_rectangle.x1;
803 text_rectangle.x1 = text_rectangle.x0 + text_dx + text_width;
804 }
805 else
806 {
807 text_rectangle.x1 = icon_rectangle.x0;
808 text_rectangle.x0 = text_rectangle.x1 - text_dx - text_width;
809 }
810
811 /* VOODOO-TODO */
812 #if 0
813 if (for_layout)
814 {
815 /* in this case, we should be more smart and calculate the size according to the maximum
816 * number of lines fitting next to the icon. However, this requires a more complex layout logic.
817 * It would mean that when measuring the label, the icon dimensions must be known already,
818 * and we
819 * 1. start with an unlimited layout
820 * 2. measure how many lines of this layout fit next to the icon
821 * 3. limit the number of lines to the given number of fitting lines
822 */
823 real_text_height = VOODOO();
824 }
825 else
826 {
827 #endif
828 real_text_height = text_height_for_entire_text;
829 #if 0
830 }
831 #endif
832
833 text_rectangle.y0 = (icon_rectangle.y0 + icon_rectangle.y1) / 2- (int) real_text_height / 2;
834 text_rectangle.y1 = text_rectangle.y0 + real_text_height;
835 }
836 else
837 {
838 text_rectangle.x0 = (icon_rectangle.x0 + icon_rectangle.x1) / 2 - (int) text_width / 2;
839 text_rectangle.y0 = icon_rectangle.y1;
840 text_rectangle.x1 = text_rectangle.x0 + text_width;
841
842 switch (usage)
843 {
844 case BOUNDS_USAGE_FOR_LAYOUT:
845 real_text_height = text_height_for_layout;
846 break;
847 case BOUNDS_USAGE_FOR_ENTIRE_ITEM:
848 real_text_height = text_height_for_entire_text;
849 break;
850 case BOUNDS_USAGE_FOR_DISPLAY:
851 real_text_height = text_height;
852 break;
853 default:
854 g_assert_not_reached ();
855 break;
856 }
857
858 text_rectangle.y1 = text_rectangle.y0 + real_text_height + LABEL_OFFSET / pixels_per_unit;
859 }
860
861 return text_rectangle;
862 }
863
864 static EelIRect
get_current_canvas_bounds(EelCanvasItem * item)865 get_current_canvas_bounds (EelCanvasItem *item)
866 {
867 EelIRect bounds;
868
869 g_assert (EEL_IS_CANVAS_ITEM (item));
870
871 bounds.x0 = item->x1;
872 bounds.y0 = item->y1;
873 bounds.x1 = item->x2;
874 bounds.y1 = item->y2;
875
876 return bounds;
877 }
878
879 void
caja_icon_canvas_item_update_bounds(CajaIconCanvasItem * item,double i2w_dx,double i2w_dy)880 caja_icon_canvas_item_update_bounds (CajaIconCanvasItem *item,
881 double i2w_dx, double i2w_dy)
882 {
883 EelIRect before, after, emblem_rect;
884 EmblemLayout emblem_layout;
885 EelCanvasItem *canvas_item;
886 GdkPixbuf *emblem_pixbuf;
887 gboolean is_rtl;
888
889 canvas_item = EEL_CANVAS_ITEM (item);
890
891 /* Compute new bounds. */
892 before = get_current_canvas_bounds (canvas_item);
893 recompute_bounding_box (item, i2w_dx, i2w_dy);
894 after = get_current_canvas_bounds (canvas_item);
895
896 /* If the bounds didn't change, we are done. */
897 if (eel_irect_equal (before, after))
898 {
899 return;
900 }
901
902 is_rtl = caja_icon_container_is_layout_rtl (CAJA_ICON_CONTAINER (canvas_item->canvas));
903
904 /* Update canvas and text rect cache */
905 get_icon_canvas_rectangle (item, &item->details->canvas_rect);
906 item->details->text_rect = compute_text_rectangle (item, item->details->canvas_rect,
907 TRUE, BOUNDS_USAGE_FOR_DISPLAY);
908
909 /* Update emblem rect cache */
910 item->details->emblem_rect.x0 = 0;
911 item->details->emblem_rect.x1 = 0;
912 item->details->emblem_rect.y0 = 0;
913 item->details->emblem_rect.y1 = 0;
914 emblem_layout_reset (&emblem_layout, item, item->details->canvas_rect, is_rtl);
915 while (emblem_layout_next (&emblem_layout, &emblem_pixbuf, &emblem_rect, is_rtl))
916 {
917 eel_irect_union (&item->details->emblem_rect, &item->details->emblem_rect, &emblem_rect);
918 }
919
920 /* queue a redraw. */
921 eel_canvas_request_redraw (canvas_item->canvas,
922 before.x0, before.y0,
923 before.x1 + 1, before.y1 + 1);
924 }
925
926 /* Update handler for the icon canvas item. */
927 static void
caja_icon_canvas_item_update(EelCanvasItem * item,double i2w_dx,double i2w_dy,gint flags)928 caja_icon_canvas_item_update (EelCanvasItem *item,
929 double i2w_dx, double i2w_dy,
930 gint flags)
931 {
932 caja_icon_canvas_item_update_bounds (CAJA_ICON_CANVAS_ITEM (item), i2w_dx, i2w_dy);
933
934 eel_canvas_item_request_redraw (EEL_CANVAS_ITEM (item));
935
936 EEL_CANVAS_ITEM_CLASS (caja_icon_canvas_item_parent_class)->update (item, i2w_dx, i2w_dy, flags);
937 }
938
939 /* Rendering */
940 static gboolean
in_single_click_mode(void)941 in_single_click_mode (void)
942 {
943 return click_policy_auto_value == CAJA_CLICK_POLICY_SINGLE;
944 }
945
946
947 /* Keep these for a bit while we work on performance of draw_or_measure_label_text. */
948 /*
949 #define PERFORMANCE_TEST_DRAW_DISABLE
950 #define PERFORMANCE_TEST_MEASURE_DISABLE
951 */
952
953 /* This gets the size of the layout from the position of the layout.
954 * This means that if the layout is right aligned we get the full width
955 * of the layout, not just the width of the text snippet on the right side
956 */
957 static void
layout_get_full_size(PangoLayout * layout,int * width,int * height,int * dx)958 layout_get_full_size (PangoLayout *layout,
959 int *width,
960 int *height,
961 int *dx)
962 {
963 PangoRectangle logical_rect;
964 int the_width, total_width;
965
966 pango_layout_get_extents (layout, NULL, &logical_rect);
967 the_width = (logical_rect.width + PANGO_SCALE / 2) / PANGO_SCALE;
968 total_width = (logical_rect.x + logical_rect.width + PANGO_SCALE / 2) / PANGO_SCALE;
969
970 if (width != NULL)
971 {
972 *width = the_width;
973 }
974
975 if (height != NULL)
976 {
977 *height = (logical_rect.height + PANGO_SCALE / 2) / PANGO_SCALE;
978 }
979
980 if (dx != NULL)
981 {
982 *dx = total_width - the_width;
983 }
984 }
985
986 static void
layout_get_size_for_layout(PangoLayout * layout,int max_layout_line_count,int height_for_entire_text,int * height_for_layout)987 layout_get_size_for_layout (PangoLayout *layout,
988 int max_layout_line_count,
989 int height_for_entire_text,
990 int *height_for_layout)
991 {
992 PangoRectangle logical_rect;
993
994 /* only use the first max_layout_line_count lines for the gridded auto layout */
995 if (pango_layout_get_line_count (layout) <= max_layout_line_count)
996 {
997 *height_for_layout = height_for_entire_text;
998 }
999 else
1000 {
1001 PangoLayoutIter *iter;
1002 int i;
1003
1004 *height_for_layout = 0;
1005 iter = pango_layout_get_iter (layout);
1006 /* VOODOO-TODO, determine number of lines based on the icon size for text besides icon.
1007 * cf. compute_text_rectangle() */
1008 for (i = 0; i < max_layout_line_count; i++)
1009 {
1010 pango_layout_iter_get_line_extents (iter, NULL, &logical_rect);
1011 *height_for_layout += (logical_rect.height + PANGO_SCALE / 2) / PANGO_SCALE;
1012
1013 if (!pango_layout_iter_next_line (iter))
1014 {
1015 break;
1016 }
1017
1018 *height_for_layout += pango_layout_get_spacing (layout);
1019 }
1020 pango_layout_iter_free (iter);
1021 }
1022 }
1023
1024 #define IS_COMPACT_VIEW(container) \
1025 ((container->details->layout_mode == CAJA_ICON_LAYOUT_T_B_L_R || \
1026 container->details->layout_mode == CAJA_ICON_LAYOUT_T_B_R_L) && \
1027 container->details->label_position == CAJA_ICON_LABEL_POSITION_BESIDE)
1028
1029 #define TEXT_BACK_PADDING_X 4
1030 #define TEXT_BACK_PADDING_Y 1
1031
1032 static void
prepare_pango_layout_width(CajaIconCanvasItem * item,PangoLayout * layout)1033 prepare_pango_layout_width (CajaIconCanvasItem *item,
1034 PangoLayout *layout)
1035 {
1036 if (caja_icon_canvas_item_get_max_text_width (item) < 0)
1037 {
1038 pango_layout_set_width (layout, -1);
1039 }
1040 else
1041 {
1042 pango_layout_set_width (layout, floor (caja_icon_canvas_item_get_max_text_width (item)) * PANGO_SCALE);
1043 pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_END);
1044 }
1045 }
1046
1047 static void
prepare_pango_layout_for_measure_entire_text(CajaIconCanvasItem * item,PangoLayout * layout)1048 prepare_pango_layout_for_measure_entire_text (CajaIconCanvasItem *item,
1049 PangoLayout *layout)
1050 {
1051 CajaIconContainer *container;
1052
1053 prepare_pango_layout_width (item, layout);
1054
1055 container = CAJA_ICON_CONTAINER (EEL_CANVAS_ITEM (item)->canvas);
1056
1057 if (IS_COMPACT_VIEW (container))
1058 {
1059 pango_layout_set_height (layout, -1);
1060 }
1061 else
1062 {
1063 pango_layout_set_height (layout, G_MININT);
1064 }
1065 }
1066
1067 static void
prepare_pango_layout_for_draw(CajaIconCanvasItem * item,PangoLayout * layout)1068 prepare_pango_layout_for_draw (CajaIconCanvasItem *item,
1069 PangoLayout *layout)
1070 {
1071 CajaIconCanvasItemPrivate *details;
1072 CajaIconContainer *container;
1073 gboolean needs_highlight;
1074
1075 prepare_pango_layout_width (item, layout);
1076
1077 container = CAJA_ICON_CONTAINER (EEL_CANVAS_ITEM (item)->canvas);
1078 details = item->details;
1079
1080 needs_highlight = details->is_highlighted_for_selection || details->is_highlighted_for_drop;
1081
1082 if (IS_COMPACT_VIEW (container))
1083 {
1084 pango_layout_set_height (layout, -1);
1085 }
1086 else if (needs_highlight ||
1087 details->is_highlighted_as_keyboard_focus ||
1088 details->entire_text ||
1089 container->details->label_position == CAJA_ICON_LABEL_POSITION_BESIDE)
1090 {
1091 /* VOODOO-TODO, cf. compute_text_rectangle() */
1092 pango_layout_set_height (layout, G_MININT);
1093 }
1094 else
1095 {
1096 /* TODO? we might save some resources, when the re-layout is not neccessary in case
1097 * the layout height already fits into max. layout lines. But pango should figure this
1098 * out itself (which it doesn't ATM).
1099 */
1100 pango_layout_set_height (layout,
1101 caja_icon_container_get_max_layout_lines_for_pango (container));
1102 }
1103 }
1104
1105 static void
measure_label_text(CajaIconCanvasItem * item)1106 measure_label_text (CajaIconCanvasItem *item)
1107 {
1108 CajaIconCanvasItemPrivate *details;
1109 CajaIconContainer *container;
1110 gint editable_height, editable_height_for_layout, editable_height_for_entire_text, editable_width, editable_dx;
1111 gint additional_height, additional_width, additional_dx;
1112 PangoLayout *editable_layout;
1113 PangoLayout *additional_layout;
1114 gboolean have_editable, have_additional;
1115
1116 /* check to see if the cached values are still valid; if so, there's
1117 * no work necessary
1118 */
1119
1120 if (item->details->text_width >= 0 && item->details->text_height >= 0)
1121 {
1122 return;
1123 }
1124
1125 details = item->details;
1126
1127 have_editable = details->editable_text != NULL && details->editable_text[0] != '\0';
1128 have_additional = details->additional_text != NULL && details->additional_text[0] != '\0';
1129
1130 /* No font or no text, then do no work. */
1131 if (!have_editable && !have_additional)
1132 {
1133 details->text_height = 0;
1134 details->text_height_for_layout = 0;
1135 details->text_height_for_entire_text = 0;
1136 details->text_width = 0;
1137 return;
1138 }
1139
1140 #ifdef PERFORMANCE_TEST_MEASURE_DISABLE
1141 /* fake out the width */
1142 details->text_width = 80;
1143 details->text_height = 20;
1144 details->text_height_for_layout = 20;
1145 details->text_height_for_entire_text = 20;
1146 return;
1147 #endif
1148
1149 editable_width = 0;
1150 editable_height = 0;
1151 editable_height_for_layout = 0;
1152 editable_height_for_entire_text = 0;
1153 editable_dx = 0;
1154 additional_width = 0;
1155 additional_height = 0;
1156 additional_dx = 0;
1157
1158 container = CAJA_ICON_CONTAINER (EEL_CANVAS_ITEM (item)->canvas);
1159 editable_layout = NULL;
1160 additional_layout = NULL;
1161
1162 if (have_editable)
1163 {
1164 /* first, measure required text height: editable_height_for_entire_text
1165 * then, measure text height applicable for layout: editable_height_for_layout
1166 * next, measure actually displayed height: editable_height
1167 */
1168 editable_layout = get_label_layout (&details->editable_text_layout, item, details->editable_text);
1169
1170 prepare_pango_layout_for_measure_entire_text (item, editable_layout);
1171 layout_get_full_size (editable_layout,
1172 NULL,
1173 &editable_height_for_entire_text,
1174 NULL);
1175 layout_get_size_for_layout (editable_layout,
1176 caja_icon_container_get_max_layout_lines (container),
1177 editable_height_for_entire_text,
1178 &editable_height_for_layout);
1179
1180 prepare_pango_layout_for_draw (item, editable_layout);
1181 layout_get_full_size (editable_layout,
1182 &editable_width,
1183 &editable_height,
1184 &editable_dx);
1185 }
1186
1187 if (have_additional)
1188 {
1189 additional_layout = get_label_layout (&details->additional_text_layout, item, details->additional_text);
1190 prepare_pango_layout_for_draw (item, additional_layout);
1191 layout_get_full_size (additional_layout,
1192 &additional_width, &additional_height, &additional_dx);
1193 }
1194
1195 details->editable_text_height = editable_height;
1196
1197 if (editable_width > additional_width)
1198 {
1199 details->text_width = editable_width;
1200 details->text_dx = editable_dx;
1201 }
1202 else
1203 {
1204 details->text_width = additional_width;
1205 details->text_dx = additional_dx;
1206 }
1207
1208 if (have_additional)
1209 {
1210 details->text_height = editable_height + LABEL_LINE_SPACING + additional_height;
1211 details->text_height_for_layout = editable_height_for_layout + LABEL_LINE_SPACING + additional_height;
1212 details->text_height_for_entire_text = editable_height_for_entire_text + LABEL_LINE_SPACING + additional_height;
1213 }
1214 else
1215 {
1216 details->text_height = editable_height;
1217 details->text_height_for_layout = editable_height_for_layout;
1218 details->text_height_for_entire_text = editable_height_for_entire_text;
1219 }
1220
1221 /* add some extra space for highlighting even when we don't highlight so things won't move */
1222
1223 /* extra slop for nicer highlighting */
1224 details->text_height += TEXT_BACK_PADDING_Y*2;
1225 details->text_height_for_layout += TEXT_BACK_PADDING_Y*2;
1226 details->text_height_for_entire_text += TEXT_BACK_PADDING_Y*2;
1227 details->editable_text_height += TEXT_BACK_PADDING_Y*2;
1228
1229 /* extra to make it look nicer */
1230 details->text_width += TEXT_BACK_PADDING_X*2;
1231
1232 if (editable_layout)
1233 {
1234 g_object_unref (editable_layout);
1235 }
1236
1237 if (additional_layout)
1238 {
1239 g_object_unref (additional_layout);
1240 }
1241 }
1242
1243 static void
draw_label_text(CajaIconCanvasItem * item,cairo_t * cr,gboolean create_mask,EelIRect icon_rect)1244 draw_label_text (CajaIconCanvasItem *item,
1245 cairo_t *cr,
1246 gboolean create_mask,
1247 EelIRect icon_rect)
1248 {
1249 CajaIconCanvasItemPrivate *details;
1250 CajaIconContainer *container;
1251 PangoLayout *editable_layout;
1252 PangoLayout *additional_layout;
1253 GtkStyleContext *context;
1254 GtkStateFlags state, base_state;
1255 gboolean have_editable, have_additional;
1256 gboolean needs_highlight, prelight_label, is_rtl_label_beside;
1257 EelIRect text_rect;
1258 int x;
1259 int max_text_width;
1260 gdouble frame_w, frame_h, frame_x, frame_y;
1261 gboolean draw_frame = TRUE;
1262
1263 #ifdef PERFORMANCE_TEST_DRAW_DISABLE
1264 return;
1265 #endif
1266
1267 details = item->details;
1268
1269 measure_label_text (item);
1270 if (details->text_height == 0 ||
1271 details->text_width == 0)
1272 {
1273 return;
1274 }
1275
1276 container = CAJA_ICON_CONTAINER (EEL_CANVAS_ITEM (item)->canvas);
1277 context = gtk_widget_get_style_context (GTK_WIDGET (container));
1278
1279 text_rect = compute_text_rectangle (item, icon_rect, TRUE, BOUNDS_USAGE_FOR_DISPLAY);
1280
1281 needs_highlight = details->is_highlighted_for_selection || details->is_highlighted_for_drop;
1282 is_rtl_label_beside = caja_icon_container_is_layout_rtl (container) &&
1283 container->details->label_position == CAJA_ICON_LABEL_POSITION_BESIDE;
1284
1285 editable_layout = NULL;
1286 additional_layout = NULL;
1287
1288 have_editable = details->editable_text != NULL && details->editable_text[0] != '\0';
1289 have_additional = details->additional_text != NULL && details->additional_text[0] != '\0';
1290 g_assert (have_editable || have_additional);
1291
1292 max_text_width = floor (caja_icon_canvas_item_get_max_text_width (item));
1293
1294 base_state = gtk_widget_get_state_flags (GTK_WIDGET (container));
1295 base_state &= ~(GTK_STATE_FLAG_SELECTED | GTK_STATE_FLAG_PRELIGHT);
1296 state = base_state;
1297
1298 gtk_widget_style_get (GTK_WIDGET (container),
1299 "activate_prelight_icon_label", &prelight_label,
1300 NULL);
1301
1302 /* if the icon needs a background, do some set-up */
1303 if (!needs_highlight && have_editable &&
1304 details->text_width > 0 && details->text_height > 0 &&
1305 prelight_label && item->details->is_prelit) {
1306 state |= GTK_STATE_FLAG_PRELIGHT;
1307
1308 frame_x = text_rect.x0;
1309 frame_y = text_rect.y0;
1310 frame_w = text_rect.x1 - text_rect.x0;
1311 frame_h = text_rect.y1 - text_rect.y0;
1312 } else if (!details->is_renaming) {
1313 /* always draw a background but when renaming where the editing
1314 * area is on top already. The default background will be transparent,
1315 * but drawing it already allows the theme to change that. */
1316
1317 if (needs_highlight)
1318 state |= GTK_STATE_FLAG_SELECTED;
1319
1320 frame_x = is_rtl_label_beside ? text_rect.x0 + item->details->text_dx : text_rect.x0;
1321 frame_y = text_rect.y0;
1322 frame_w = is_rtl_label_beside ? text_rect.x1 - text_rect.x0 - item->details->text_dx : text_rect.x1 - text_rect.x0;
1323 frame_h = text_rect.y1 - text_rect.y0;
1324 } else {
1325 draw_frame = FALSE;
1326 }
1327
1328 if (draw_frame) {
1329 gtk_style_context_save (context);
1330 gtk_style_context_set_state (context, state);
1331
1332 gtk_render_frame (context, cr,
1333 frame_x, frame_y,
1334 frame_w, frame_h);
1335 gtk_render_background (context, cr,
1336 frame_x, frame_y,
1337 frame_w, frame_h);
1338
1339 gtk_style_context_restore (context);
1340 }
1341
1342 if (container->details->label_position == CAJA_ICON_LABEL_POSITION_BESIDE)
1343 {
1344 x = text_rect.x0 + 2;
1345 }
1346 else
1347 {
1348 x = text_rect.x0 + ((text_rect.x1 - text_rect.x0) - max_text_width) / 2;
1349 }
1350
1351 if (have_editable &&
1352 !details->is_renaming)
1353 {
1354 state = base_state;
1355
1356 if (prelight_label && item->details->is_prelit) {
1357 state |= GTK_STATE_FLAG_PRELIGHT;
1358 }
1359
1360 if (needs_highlight) {
1361 state |= GTK_STATE_FLAG_SELECTED;
1362 }
1363
1364 editable_layout = get_label_layout (&item->details->editable_text_layout, item, item->details->editable_text);
1365 prepare_pango_layout_for_draw (item, editable_layout);
1366
1367 gtk_style_context_save (context);
1368 gtk_style_context_set_state (context, state);
1369
1370 gtk_render_layout (context, cr,
1371 x, text_rect.y0 + TEXT_BACK_PADDING_Y,
1372 editable_layout);
1373
1374 gtk_style_context_restore (context);
1375 }
1376
1377 if (have_additional &&
1378 !details->is_renaming)
1379 {
1380 state = base_state;
1381
1382 if (needs_highlight) {
1383 state |= GTK_STATE_FLAG_SELECTED;
1384 }
1385
1386 additional_layout = get_label_layout (&item->details->additional_text_layout, item, item->details->additional_text);
1387 prepare_pango_layout_for_draw (item, additional_layout);
1388
1389 gtk_style_context_save (context);
1390 gtk_style_context_set_state (context, state);
1391 gtk_style_context_add_class (context, "dim-label");
1392
1393 gtk_render_layout (context, cr,
1394 x, text_rect.y0 + details->editable_text_height + LABEL_LINE_SPACING + TEXT_BACK_PADDING_Y,
1395 additional_layout);
1396
1397 gtk_style_context_restore (context);
1398 }
1399
1400 if (!create_mask && item->details->is_highlighted_as_keyboard_focus)
1401 {
1402 if (needs_highlight) {
1403 state = GTK_STATE_FLAG_SELECTED;
1404 }
1405
1406 gtk_style_context_save (context);
1407 gtk_style_context_set_state (context, state);
1408
1409 gtk_render_focus (context,
1410 cr,
1411 text_rect.x0,
1412 text_rect.y0,
1413 text_rect.x1 - text_rect.x0,
1414 text_rect.y1 - text_rect.y0);
1415
1416 gtk_style_context_restore (context);
1417 }
1418
1419 if (editable_layout != NULL)
1420 {
1421 g_object_unref (editable_layout);
1422 }
1423
1424 if (additional_layout != NULL)
1425 {
1426 g_object_unref (additional_layout);
1427 }
1428 }
1429
1430 void
caja_icon_canvas_item_set_is_visible(CajaIconCanvasItem * item,gboolean visible)1431 caja_icon_canvas_item_set_is_visible (CajaIconCanvasItem *item,
1432 gboolean visible)
1433 {
1434 if (item->details->is_visible == visible)
1435 return;
1436
1437 item->details->is_visible = visible;
1438
1439 if (!visible)
1440 {
1441 caja_icon_canvas_item_invalidate_label (item);
1442 }
1443 }
1444
1445 void
caja_icon_canvas_item_invalidate_label(CajaIconCanvasItem * item)1446 caja_icon_canvas_item_invalidate_label (CajaIconCanvasItem *item)
1447 {
1448 caja_icon_canvas_item_invalidate_label_size (item);
1449
1450 if (item->details->editable_text_layout)
1451 {
1452 g_object_unref (item->details->editable_text_layout);
1453 item->details->editable_text_layout = NULL;
1454 }
1455
1456 if (item->details->additional_text_layout)
1457 {
1458 g_object_unref (item->details->additional_text_layout);
1459 item->details->additional_text_layout = NULL;
1460 }
1461
1462 if (item->details->embedded_text_layout)
1463 {
1464 g_object_unref (item->details->embedded_text_layout);
1465 item->details->embedded_text_layout = NULL;
1466 }
1467 }
1468
1469
1470 static GdkPixbuf *
get_knob_pixbuf(void)1471 get_knob_pixbuf (void)
1472 {
1473 GdkPixbuf *knob_pixbuf;
1474
1475 knob_pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (),
1476 "stock-caja-knob",
1477 8, 0, NULL);
1478 if (!knob_pixbuf)
1479 {
1480 char *knob_filename;
1481
1482 knob_filename = caja_pixmap_file ("knob.png");
1483 knob_pixbuf = gdk_pixbuf_new_from_file (knob_filename, NULL);
1484 g_free (knob_filename);
1485 }
1486
1487 return knob_pixbuf;
1488 }
1489
1490 static void
draw_stretch_handles(CajaIconCanvasItem * item,cairo_t * cr,const EelIRect * rect)1491 draw_stretch_handles (CajaIconCanvasItem *item,
1492 cairo_t *cr,
1493 const EelIRect *rect)
1494 {
1495 GtkWidget *widget;
1496 GdkPixbuf *knob_pixbuf;
1497 int knob_width, knob_height;
1498 double dash = { 2.0 };
1499 GtkStyleContext *style;
1500 GdkRGBA color;
1501
1502 if (!item->details->show_stretch_handles)
1503 {
1504 return;
1505 }
1506
1507 widget = GTK_WIDGET (EEL_CANVAS_ITEM (item)->canvas);
1508 style = gtk_widget_get_style_context (widget);
1509
1510 cairo_save (cr);
1511
1512 knob_pixbuf = get_knob_pixbuf ();
1513 knob_width = gdk_pixbuf_get_width (knob_pixbuf);
1514 knob_height = gdk_pixbuf_get_height (knob_pixbuf);
1515
1516 /* first draw the box */
1517 gtk_style_context_get_color (style, GTK_STATE_FLAG_SELECTED, &color);
1518 gdk_cairo_set_source_rgba (cr, &color);
1519
1520 cairo_set_dash (cr, &dash, 1, 0);
1521 cairo_set_line_width (cr, 1.0);
1522 cairo_rectangle (cr,
1523 rect->x0 + 0.5,
1524 rect->y0 + 0.5,
1525 rect->x1 - rect->x0 - 1,
1526 rect->y1 - rect->y0 - 1);
1527 cairo_stroke (cr);
1528
1529 cairo_restore (cr);
1530
1531 /* draw the stretch handles themselves */
1532 draw_pixbuf (knob_pixbuf, cr, rect->x0, rect->y0);
1533 draw_pixbuf (knob_pixbuf, cr, rect->x0, rect->y1 - knob_height);
1534 draw_pixbuf (knob_pixbuf, cr, rect->x1 - knob_width, rect->y0);
1535 draw_pixbuf (knob_pixbuf, cr, rect->x1 - knob_width, rect->y1 - knob_height);
1536
1537 g_object_unref (knob_pixbuf);
1538 }
1539
1540 static void
emblem_layout_reset(EmblemLayout * layout,CajaIconCanvasItem * icon_item,EelIRect icon_rect,gboolean is_rtl)1541 emblem_layout_reset (EmblemLayout *layout, CajaIconCanvasItem *icon_item, EelIRect icon_rect, gboolean is_rtl)
1542 {
1543 layout->icon_item = icon_item;
1544 layout->icon_rect = icon_rect;
1545 layout->side = is_rtl ? LEFT_SIDE : RIGHT_SIDE;
1546 layout->position = 0;
1547 layout->index = 0;
1548 layout->emblem = icon_item->details->emblem_pixbufs;
1549 }
1550
1551 static gboolean
emblem_layout_next(EmblemLayout * layout,GdkPixbuf ** emblem_pixbuf,EelIRect * emblem_rect,gboolean is_rtl)1552 emblem_layout_next (EmblemLayout *layout,
1553 GdkPixbuf **emblem_pixbuf,
1554 EelIRect *emblem_rect,
1555 gboolean is_rtl)
1556 {
1557 GdkPixbuf *pixbuf;
1558 int width, height, x, y;
1559 GdkPoint *attach_points;
1560
1561 /* Check if we have layed out all of the pixbufs. */
1562 if (layout->emblem == NULL)
1563 {
1564 return FALSE;
1565 }
1566
1567 /* Get the pixbuf. */
1568 pixbuf = layout->emblem->data;
1569 width = gdk_pixbuf_get_width (pixbuf);
1570 height = gdk_pixbuf_get_height (pixbuf);
1571
1572
1573 /* Advance to the next emblem. */
1574 layout->emblem = layout->emblem->next;
1575
1576 attach_points = layout->icon_item->details->attach_points;
1577 if (attach_points != NULL)
1578 {
1579 if (layout->index >= layout->icon_item->details->n_attach_points)
1580 {
1581 return FALSE;
1582 }
1583
1584 x = layout->icon_rect.x0 + attach_points[layout->index].x;
1585 y = layout->icon_rect.y0 + attach_points[layout->index].y;
1586
1587 layout->index += 1;
1588
1589 /* Return the rectangle and pixbuf. */
1590 *emblem_pixbuf = pixbuf;
1591 emblem_rect->x0 = x - width / 2;
1592 emblem_rect->y0 = y - height / 2;
1593 emblem_rect->x1 = emblem_rect->x0 + width;
1594 emblem_rect->y1 = emblem_rect->y0 + height;
1595
1596 return TRUE;
1597
1598 }
1599
1600 for (;;)
1601 {
1602
1603 /* Find the side to lay out along. */
1604 switch (layout->side)
1605 {
1606 case RIGHT_SIDE:
1607 x = layout->icon_rect.x1;
1608 y = is_rtl ? layout->icon_rect.y1 : layout->icon_rect.y0;
1609 break;
1610 case BOTTOM_SIDE:
1611 x = is_rtl ? layout->icon_rect.x0 : layout->icon_rect.x1;
1612 y = layout->icon_rect.y1;
1613 break;
1614 case LEFT_SIDE:
1615 x = layout->icon_rect.x0;
1616 y = is_rtl ? layout->icon_rect.y0 : layout->icon_rect.y1;
1617 break;
1618 case TOP_SIDE:
1619 x = is_rtl ? layout->icon_rect.x1 : layout->icon_rect.x0;
1620 y = layout->icon_rect.y0;
1621 break;
1622 default:
1623 g_assert_not_reached ();
1624 x = 0;
1625 y = 0;
1626 break;
1627 }
1628 if (layout->position != 0)
1629 {
1630 switch (layout->side)
1631 {
1632 case RIGHT_SIDE:
1633 y += (is_rtl ? -1 : 1) * (layout->position + height / 2);
1634 break;
1635 case BOTTOM_SIDE:
1636 x += (is_rtl ? 1 : -1 ) * (layout->position + width / 2);
1637 break;
1638 case LEFT_SIDE:
1639 y += (is_rtl ? 1 : -1) * (layout->position + height / 2);
1640 break;
1641 case TOP_SIDE:
1642 x += (is_rtl ? -1 : 1) * (layout->position + width / 2);
1643 break;
1644 }
1645 }
1646
1647 /* Check to see if emblem fits in current side. */
1648 if (x >= layout->icon_rect.x0 && x <= layout->icon_rect.x1
1649 && y >= layout->icon_rect.y0 && y <= layout->icon_rect.y1)
1650 {
1651
1652 /* It fits. */
1653
1654 /* Advance along the side. */
1655 switch (layout->side)
1656 {
1657 case RIGHT_SIDE:
1658 case LEFT_SIDE:
1659 layout->position += height + EMBLEM_SPACING;
1660 break;
1661 case BOTTOM_SIDE:
1662 case TOP_SIDE:
1663 layout->position += width + EMBLEM_SPACING;
1664 break;
1665 }
1666
1667 /* Return the rectangle and pixbuf. */
1668 *emblem_pixbuf = pixbuf;
1669 emblem_rect->x0 = x - width / 2;
1670 emblem_rect->y0 = y - height / 2;
1671 emblem_rect->x1 = emblem_rect->x0 + width;
1672 emblem_rect->y1 = emblem_rect->y0 + height;
1673
1674 return TRUE;
1675 }
1676
1677 /* It doesn't fit, so move to the next side. */
1678 switch (layout->side)
1679 {
1680 case RIGHT_SIDE:
1681 layout->side = is_rtl ? TOP_SIDE : BOTTOM_SIDE;
1682 break;
1683 case BOTTOM_SIDE:
1684 layout->side = is_rtl ? RIGHT_SIDE : LEFT_SIDE;
1685 break;
1686 case LEFT_SIDE:
1687 layout->side = is_rtl ? BOTTOM_SIDE : TOP_SIDE;
1688 break;
1689 case TOP_SIDE:
1690 default:
1691 return FALSE;
1692 }
1693 layout->position = 0;
1694 }
1695 }
1696
1697 static void
draw_pixbuf(GdkPixbuf * pixbuf,cairo_t * cr,int x,int y)1698 draw_pixbuf (GdkPixbuf *pixbuf,
1699 cairo_t *cr,
1700 int x, int y)
1701 {
1702 cairo_save (cr);
1703 gdk_cairo_set_source_pixbuf (cr, pixbuf, x, y);
1704 cairo_paint (cr);
1705 cairo_restore (cr);
1706 }
1707
1708 /* shared code to highlight or dim the passed-in pixbuf */
1709 static cairo_surface_t *
real_map_surface(CajaIconCanvasItem * icon_item)1710 real_map_surface (CajaIconCanvasItem *icon_item)
1711 {
1712 EelCanvas *canvas;
1713 GdkPixbuf *temp_pixbuf, *old_pixbuf;
1714 GdkRGBA color;
1715 GdkRGBA *c;
1716 cairo_surface_t *surface;
1717
1718 temp_pixbuf = icon_item->details->pixbuf;
1719 canvas = EEL_CANVAS_ITEM(icon_item)->canvas;
1720
1721 g_object_ref (temp_pixbuf);
1722
1723 if (icon_item->details->is_prelit ||
1724 icon_item->details->is_highlighted_for_clipboard)
1725 {
1726 old_pixbuf = temp_pixbuf;
1727
1728 temp_pixbuf = eel_create_spotlight_pixbuf (temp_pixbuf);
1729 g_object_unref (old_pixbuf);
1730
1731 /* FIXME bugzilla.gnome.org 42471: This hard-wired image is inappropriate to
1732 * this level of code, which shouldn't know that the
1733 * preview is audio, nor should it have an icon
1734 * hard-wired in.
1735 */
1736
1737 /* if the icon is currently being previewed, superimpose an image to indicate that */
1738 /* audio is the only kind of previewing right now, so this code isn't as general as it could be */
1739 if (icon_item->details->is_active)
1740 {
1741 char *audio_filename;
1742 GdkPixbuf *audio_pixbuf;
1743 int emblem_size;
1744
1745 emblem_size = caja_icon_get_emblem_size_for_icon_size (gdk_pixbuf_get_width (temp_pixbuf));
1746 /* Load the audio symbol. */
1747 audio_filename = caja_pixmap_file ("audio.svg");
1748 if (audio_filename != NULL)
1749 {
1750 audio_pixbuf = gdk_pixbuf_new_from_file_at_scale (audio_filename,
1751 emblem_size, emblem_size,
1752 TRUE,
1753 NULL);
1754 }
1755 else
1756 {
1757 audio_pixbuf = NULL;
1758 }
1759
1760 /* Composite it onto the icon. */
1761 if (audio_pixbuf != NULL)
1762 {
1763 gdk_pixbuf_composite
1764 (audio_pixbuf,
1765 temp_pixbuf,
1766 0, 0,
1767 gdk_pixbuf_get_width (audio_pixbuf),
1768 gdk_pixbuf_get_height (audio_pixbuf),
1769 0, 0,
1770 1.0, 1.0,
1771 GDK_INTERP_BILINEAR, 0xFF);
1772
1773 g_object_unref (audio_pixbuf);
1774 }
1775
1776 g_free (audio_filename);
1777 }
1778 }
1779
1780 if (icon_item->details->is_highlighted_for_selection
1781 || icon_item->details->is_highlighted_for_drop)
1782 {
1783 GtkStyleContext *style;
1784
1785 style = gtk_widget_get_style_context (GTK_WIDGET (canvas));
1786
1787 if (gtk_widget_has_focus (GTK_WIDGET (canvas))) {
1788 gtk_style_context_get (style, GTK_STATE_FLAG_SELECTED,
1789 GTK_STYLE_PROPERTY_BACKGROUND_COLOR,
1790 &c, NULL);
1791 } else {
1792 gtk_style_context_get (style, GTK_STATE_FLAG_ACTIVE,
1793 GTK_STYLE_PROPERTY_BACKGROUND_COLOR,
1794 &c, NULL);
1795 }
1796
1797 color = *c;
1798 gdk_rgba_free (c);
1799
1800 old_pixbuf = temp_pixbuf;
1801 temp_pixbuf = eel_create_colorized_pixbuf (temp_pixbuf, &color);
1802
1803 g_object_unref (old_pixbuf);
1804 }
1805
1806 surface = gdk_cairo_surface_create_from_pixbuf (temp_pixbuf,
1807 gtk_widget_get_scale_factor (GTK_WIDGET (canvas)),
1808 gtk_widget_get_window (GTK_WIDGET (canvas)));
1809 g_object_unref (temp_pixbuf);
1810
1811 return surface;
1812 }
1813
1814 static cairo_surface_t *
map_surface(CajaIconCanvasItem * icon_item)1815 map_surface (CajaIconCanvasItem *icon_item)
1816 {
1817 if (!(icon_item->details->rendered_surface != NULL
1818 && icon_item->details->rendered_is_active == icon_item->details->is_active
1819 && icon_item->details->rendered_is_prelit == icon_item->details->is_prelit
1820 && icon_item->details->rendered_is_highlighted_for_selection == icon_item->details->is_highlighted_for_selection
1821 && icon_item->details->rendered_is_highlighted_for_drop == icon_item->details->is_highlighted_for_drop
1822 && icon_item->details->rendered_is_highlighted_for_clipboard == icon_item->details->is_highlighted_for_clipboard
1823 && (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)))))
1824 {
1825 if (icon_item->details->rendered_surface != NULL)
1826 {
1827 cairo_surface_destroy (icon_item->details->rendered_surface);
1828 }
1829 icon_item->details->rendered_surface = real_map_surface (icon_item);
1830 icon_item->details->rendered_is_active = icon_item->details->is_active;
1831 icon_item->details->rendered_is_prelit = icon_item->details->is_prelit;
1832 icon_item->details->rendered_is_highlighted_for_selection = icon_item->details->is_highlighted_for_selection;
1833 icon_item->details->rendered_is_highlighted_for_drop = icon_item->details->is_highlighted_for_drop;
1834 icon_item->details->rendered_is_highlighted_for_clipboard = icon_item->details->is_highlighted_for_clipboard;
1835 icon_item->details->rendered_is_focused = gtk_widget_has_focus (GTK_WIDGET (EEL_CANVAS_ITEM (icon_item)->canvas));
1836 }
1837
1838 cairo_surface_reference (icon_item->details->rendered_surface);
1839
1840 return icon_item->details->rendered_surface;
1841 }
1842
1843 static void
draw_embedded_text(CajaIconCanvasItem * item,cairo_t * cr,int x,int y)1844 draw_embedded_text (CajaIconCanvasItem *item,
1845 cairo_t *cr,
1846 int x, int y)
1847 {
1848 PangoLayout *layout;
1849 GtkWidget *widget;
1850 GtkStyleContext *style_context;
1851
1852 if (item->details->embedded_text == NULL ||
1853 item->details->embedded_text_rect.width == 0 ||
1854 item->details->embedded_text_rect.height == 0)
1855 {
1856 return;
1857 }
1858
1859 widget = GTK_WIDGET (EEL_CANVAS_ITEM (item)->canvas);
1860
1861 if (item->details->embedded_text_layout != NULL)
1862 {
1863 layout = g_object_ref (item->details->embedded_text_layout);
1864 }
1865 else
1866 {
1867 PangoContext *context;
1868 PangoFontDescription *desc;
1869
1870 context = gtk_widget_get_pango_context (widget);
1871 layout = pango_layout_new (context);
1872 pango_layout_set_text (layout, item->details->embedded_text, -1);
1873
1874 desc = pango_font_description_from_string ("monospace 6");
1875 pango_layout_set_font_description (layout, desc);
1876 pango_font_description_free (desc);
1877
1878 if (item->details->is_visible)
1879 {
1880 item->details->embedded_text_layout = g_object_ref (layout);
1881 }
1882 }
1883
1884 style_context = gtk_widget_get_style_context (widget);
1885 gtk_style_context_save (style_context);
1886 gtk_style_context_add_class (style_context, "icon-embedded-text");
1887
1888 cairo_save (cr);
1889
1890 cairo_rectangle (cr,
1891 x + item->details->embedded_text_rect.x,
1892 y + item->details->embedded_text_rect.y,
1893 item->details->embedded_text_rect.width,
1894 item->details->embedded_text_rect.height);
1895 cairo_clip (cr);
1896
1897 gtk_render_layout (style_context, cr,
1898 x + item->details->embedded_text_rect.x,
1899 y + item->details->embedded_text_rect.y,
1900 layout);
1901
1902 gtk_style_context_restore (style_context);
1903 cairo_restore (cr);
1904 }
1905
1906 /* Draw the icon item for non-anti-aliased mode. */
1907 static void
caja_icon_canvas_item_draw(EelCanvasItem * item,cairo_t * cr,cairo_region_t * region)1908 caja_icon_canvas_item_draw (EelCanvasItem *item,
1909 cairo_t *cr,
1910 cairo_region_t *region)
1911 {
1912 CajaIconContainer *container;
1913 CajaIconCanvasItem *icon_item;
1914 CajaIconCanvasItemPrivate *details;
1915 EelIRect icon_rect, emblem_rect;
1916 EmblemLayout emblem_layout;
1917 GdkPixbuf *emblem_pixbuf;
1918 cairo_surface_t *temp_surface;
1919 GtkStyleContext *context;
1920
1921 container = CAJA_ICON_CONTAINER (item->canvas);
1922 gboolean is_rtl;
1923
1924 icon_item = CAJA_ICON_CANVAS_ITEM (item);
1925 details = icon_item->details;
1926
1927 /* Draw the pixbuf. */
1928 if (details->pixbuf == NULL)
1929 {
1930 return;
1931 }
1932
1933 context = gtk_widget_get_style_context (GTK_WIDGET (container));
1934 gtk_style_context_save (context);
1935 gtk_style_context_add_class (context, "caja-canvas-item");
1936
1937 icon_rect = icon_item->details->canvas_rect;
1938
1939 temp_surface = map_surface (icon_item);
1940
1941 gtk_render_icon_surface (context, cr,
1942 temp_surface,
1943 icon_rect.x0, icon_rect.y0);
1944 cairo_surface_destroy (temp_surface);
1945
1946 draw_embedded_text (icon_item, cr, icon_rect.x0, icon_rect.y0);
1947
1948 is_rtl = caja_icon_container_is_layout_rtl (CAJA_ICON_CONTAINER (item->canvas));
1949
1950 /* Draw the emblem pixbufs. */
1951 emblem_layout_reset (&emblem_layout, icon_item, icon_rect, is_rtl);
1952 while (emblem_layout_next (&emblem_layout, &emblem_pixbuf, &emblem_rect, is_rtl))
1953 {
1954 draw_pixbuf (emblem_pixbuf, cr, emblem_rect.x0, emblem_rect.y0);
1955 }
1956
1957 /* Draw stretching handles (if necessary). */
1958 draw_stretch_handles (icon_item, cr, &icon_rect);
1959
1960 /* Draw the label text. */
1961 draw_label_text (icon_item, cr, FALSE, icon_rect);
1962
1963 gtk_style_context_restore (context);
1964 }
1965
1966 #define ZERO_WIDTH_SPACE "\xE2\x80\x8B"
1967
1968 #define ZERO_OR_THREE_DIGITS(p) \
1969 (!g_ascii_isdigit (*p) || \
1970 (g_ascii_isdigit (*(p+1)) && \
1971 g_ascii_isdigit (*(p+2))))
1972
1973
1974 static PangoLayout *
create_label_layout(CajaIconCanvasItem * item,const char * text)1975 create_label_layout (CajaIconCanvasItem *item,
1976 const char *text)
1977 {
1978 PangoLayout *layout;
1979 PangoContext *context;
1980 PangoFontDescription *desc;
1981 CajaIconContainer *container;
1982 EelCanvasItem *canvas_item;
1983 char *zeroified_text;
1984 #if PANGO_CHECK_VERSION (1, 44, 0)
1985 PangoAttrList *attr_list;
1986 #endif
1987
1988 canvas_item = EEL_CANVAS_ITEM (item);
1989
1990 container = CAJA_ICON_CONTAINER (canvas_item->canvas);
1991 context = gtk_widget_get_pango_context (GTK_WIDGET (canvas_item->canvas));
1992 layout = pango_layout_new (context);
1993 #if PANGO_CHECK_VERSION (1, 44, 0)
1994 attr_list = pango_attr_list_new ();
1995 #endif
1996
1997 zeroified_text = NULL;
1998
1999 if (text != NULL)
2000 {
2001 GString *str;
2002 const char *p;
2003
2004 str = g_string_new (NULL);
2005
2006 for (p = text; *p != '\0'; p++)
2007 {
2008 str = g_string_append_c (str, *p);
2009
2010 if (*p == '_' || *p == '-' || (*p == '.' && ZERO_OR_THREE_DIGITS (p+1)))
2011 {
2012 /* Ensure that we allow to break after '_' or '.' characters,
2013 * if they are not likely to be part of a version information, to
2014 * not break wrapping of foobar-0.0.1.
2015 * Wrap before IPs and long numbers, though. */
2016 str = g_string_append (str, ZERO_WIDTH_SPACE);
2017 }
2018 }
2019
2020 zeroified_text = g_string_free (str, FALSE);
2021 }
2022
2023 pango_layout_set_text (layout, zeroified_text, -1);
2024 pango_layout_set_auto_dir (layout, FALSE);
2025
2026 if (container->details->label_position == CAJA_ICON_LABEL_POSITION_BESIDE)
2027 {
2028 if (!caja_icon_container_is_layout_rtl (container))
2029 {
2030 pango_layout_set_alignment (layout, PANGO_ALIGN_LEFT);
2031 }
2032 else
2033 {
2034 pango_layout_set_alignment (layout, PANGO_ALIGN_RIGHT);
2035 }
2036 }
2037 else
2038 {
2039 pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER);
2040 }
2041
2042 pango_layout_set_spacing (layout, LABEL_LINE_SPACING);
2043 pango_layout_set_wrap (layout, PANGO_WRAP_WORD_CHAR);
2044
2045 #if PANGO_CHECK_VERSION (1, 44, 0)
2046 pango_attr_list_insert (attr_list, pango_attr_insert_hyphens_new (FALSE));
2047 pango_layout_set_attributes (layout, attr_list);
2048 #endif
2049
2050 /* Create a font description */
2051 if (container->details->font)
2052 {
2053 desc = pango_font_description_from_string (container->details->font);
2054 }
2055 else
2056 {
2057 desc = pango_font_description_copy (pango_context_get_font_description (context));
2058 pango_font_description_set_size (desc,
2059 pango_font_description_get_size (desc) +
2060 container->details->font_size_table [container->details->zoom_level]);
2061 }
2062 pango_layout_set_font_description (layout, desc);
2063 pango_font_description_free (desc);
2064 g_free (zeroified_text);
2065 #if PANGO_CHECK_VERSION (1, 44, 0)
2066 pango_attr_list_unref (attr_list);
2067 #endif
2068
2069 return layout;
2070 }
2071
2072 static PangoLayout *
get_label_layout(PangoLayout ** layout_cache,CajaIconCanvasItem * item,const char * text)2073 get_label_layout (PangoLayout **layout_cache,
2074 CajaIconCanvasItem *item,
2075 const char *text)
2076 {
2077 PangoLayout *layout;
2078
2079 if (*layout_cache != NULL)
2080 {
2081 return g_object_ref (*layout_cache);
2082 }
2083
2084 layout = create_label_layout (item, text);
2085
2086 if (item->details->is_visible)
2087 {
2088 *layout_cache = g_object_ref (layout);
2089 }
2090
2091 return layout;
2092 }
2093
2094
2095 /* handle events */
2096 static int
caja_icon_canvas_item_event(EelCanvasItem * item,GdkEvent * event)2097 caja_icon_canvas_item_event (EelCanvasItem *item, GdkEvent *event)
2098 {
2099 CajaIconCanvasItem *icon_item;
2100 GdkWindow *cursor_window;
2101
2102 icon_item = CAJA_ICON_CANVAS_ITEM (item);
2103 cursor_window = ((GdkEventAny *) event)->window;
2104
2105 switch (event->type)
2106 {
2107 case GDK_ENTER_NOTIFY:
2108 if (!icon_item->details->is_prelit)
2109 {
2110 icon_item->details->is_prelit = TRUE;
2111 caja_icon_canvas_item_invalidate_label_size (icon_item);
2112 eel_canvas_item_request_update (item);
2113 eel_canvas_item_send_behind (item,
2114 CAJA_ICON_CONTAINER (item->canvas)->details->rubberband_info.selection_rectangle);
2115
2116 /* show a hand cursor */
2117 if (in_single_click_mode ())
2118 {
2119 GdkCursor *cursor;
2120
2121 cursor = gdk_cursor_new_for_display (gdk_display_get_default(),
2122 GDK_HAND2);
2123 gdk_window_set_cursor (cursor_window, cursor);
2124 g_object_unref (cursor);
2125
2126 icon_item->details->cursor_window = g_object_ref (cursor_window);
2127 }
2128
2129 /* FIXME bugzilla.gnome.org 42473:
2130 * We should emit our own signal here,
2131 * not one from the container; it could hook
2132 * up to that signal and emit one of its
2133 * own. Doing it this way hard-codes what
2134 * "user_data" is. Also, the two signals
2135 * should be separate. The "unpreview" signal
2136 * does not have a return value.
2137 */
2138 icon_item->details->is_active = caja_icon_container_emit_preview_signal
2139 (CAJA_ICON_CONTAINER (item->canvas),
2140 CAJA_ICON_CANVAS_ITEM (item)->user_data,
2141 TRUE);
2142 }
2143 return TRUE;
2144
2145 case GDK_LEAVE_NOTIFY:
2146 if (icon_item->details->is_prelit
2147 || icon_item->details->is_highlighted_for_drop)
2148 {
2149 /* When leaving, turn of the prelight state and the
2150 * higlighted for drop. The latter gets turned on
2151 * by the drag&drop motion callback.
2152 */
2153 /* FIXME bugzilla.gnome.org 42473:
2154 * We should emit our own signal here,
2155 * not one from the containe; it could hook up
2156 * to that signal and emit one of its
2157 * ownr. Doing it this way hard-codes what
2158 * "user_data" is. Also, the two signals
2159 * should be separate. The "unpreview" signal
2160 * does not have a return value.
2161 */
2162 caja_icon_container_emit_preview_signal
2163 (CAJA_ICON_CONTAINER (item->canvas),
2164 CAJA_ICON_CANVAS_ITEM (item)->user_data,
2165 FALSE);
2166 icon_item->details->is_prelit = FALSE;
2167 icon_item->details->is_active = 0;
2168 icon_item->details->is_highlighted_for_drop = FALSE;
2169 caja_icon_canvas_item_invalidate_label_size (icon_item);
2170 eel_canvas_item_request_update (item);
2171
2172 /* show default cursor */
2173 gdk_window_set_cursor (cursor_window, NULL);
2174 g_clear_object (&icon_item->details->cursor_window);
2175 }
2176 return TRUE;
2177
2178 default:
2179 /* Don't eat up other events; icon container might use them. */
2180 return FALSE;
2181 }
2182 }
2183
2184 static gboolean
hit_test_pixbuf(GdkPixbuf * pixbuf,EelIRect pixbuf_location,EelIRect probe_rect)2185 hit_test_pixbuf (GdkPixbuf *pixbuf, EelIRect pixbuf_location, EelIRect probe_rect)
2186 {
2187 EelIRect relative_rect, pixbuf_rect;
2188 int x, y;
2189 guint8 *pixel;
2190
2191 /* You can get here without a pixbuf in some strange cases. */
2192 if (pixbuf == NULL)
2193 {
2194 return FALSE;
2195 }
2196
2197 /* Check to see if it's within the rectangle at all. */
2198 relative_rect.x0 = probe_rect.x0 - pixbuf_location.x0;
2199 relative_rect.y0 = probe_rect.y0 - pixbuf_location.y0;
2200 relative_rect.x1 = probe_rect.x1 - pixbuf_location.x0;
2201 relative_rect.y1 = probe_rect.y1 - pixbuf_location.y0;
2202 pixbuf_rect.x0 = 0;
2203 pixbuf_rect.y0 = 0;
2204 pixbuf_rect.x1 = gdk_pixbuf_get_width (pixbuf);
2205 pixbuf_rect.y1 = gdk_pixbuf_get_height (pixbuf);
2206 eel_irect_intersect (&relative_rect, &relative_rect, &pixbuf_rect);
2207 if (eel_irect_is_empty (&relative_rect))
2208 {
2209 return FALSE;
2210 }
2211
2212 /* If there's no alpha channel, it's opaque and we have a hit. */
2213 if (!gdk_pixbuf_get_has_alpha (pixbuf))
2214 {
2215 return TRUE;
2216 }
2217 g_assert (gdk_pixbuf_get_n_channels (pixbuf) == 4);
2218
2219 /* Check the alpha channel of the pixel to see if we have a hit. */
2220 for (x = relative_rect.x0; x < relative_rect.x1; x++)
2221 {
2222 for (y = relative_rect.y0; y < relative_rect.y1; y++)
2223 {
2224 pixel = gdk_pixbuf_get_pixels (pixbuf)
2225 + y * gdk_pixbuf_get_rowstride (pixbuf)
2226 + x * 4;
2227 if (pixel[3] > 1)
2228 {
2229 return TRUE;
2230 }
2231 }
2232 }
2233 return FALSE;
2234 }
2235
2236 static gboolean
hit_test(CajaIconCanvasItem * icon_item,EelIRect canvas_rect)2237 hit_test (CajaIconCanvasItem *icon_item, EelIRect canvas_rect)
2238 {
2239 CajaIconCanvasItemPrivate *details;
2240 EelIRect emblem_rect;
2241 EmblemLayout emblem_layout;
2242 GdkPixbuf *emblem_pixbuf;
2243 gboolean is_rtl;
2244
2245 details = icon_item->details;
2246
2247 /* Quick check to see if the rect hits the icon, text or emblems at all. */
2248 if (!eel_irect_hits_irect (icon_item->details->canvas_rect, canvas_rect)
2249 && (!eel_irect_hits_irect (details->text_rect, canvas_rect))
2250 && (!eel_irect_hits_irect (details->emblem_rect, canvas_rect)))
2251 {
2252 return FALSE;
2253 }
2254
2255 /* Check for hits in the stretch handles. */
2256 if (hit_test_stretch_handle (icon_item, canvas_rect, NULL))
2257 {
2258 return TRUE;
2259 }
2260
2261 /* Check for hit in the icon. */
2262 if (eel_irect_hits_irect (icon_item->details->canvas_rect, canvas_rect))
2263 {
2264 return TRUE;
2265 }
2266
2267 /* Check for hit in the text. */
2268 if (eel_irect_hits_irect (details->text_rect, canvas_rect)
2269 && !icon_item->details->is_renaming)
2270 {
2271 return TRUE;
2272 }
2273
2274 is_rtl = caja_icon_container_is_layout_rtl (CAJA_ICON_CONTAINER (EEL_CANVAS_ITEM (icon_item)->canvas));
2275
2276 /* Check for hit in the emblem pixbufs. */
2277 emblem_layout_reset (&emblem_layout, icon_item, icon_item->details->canvas_rect, is_rtl);
2278 while (emblem_layout_next (&emblem_layout, &emblem_pixbuf, &emblem_rect, is_rtl))
2279 {
2280 if (hit_test_pixbuf (emblem_pixbuf, emblem_rect, canvas_rect))
2281 {
2282 return TRUE;
2283 }
2284 }
2285
2286 return FALSE;
2287 }
2288
2289 /* Point handler for the icon canvas item. */
2290 static double
caja_icon_canvas_item_point(EelCanvasItem * item,double x,double y,int cx,int cy,EelCanvasItem ** actual_item)2291 caja_icon_canvas_item_point (EelCanvasItem *item, double x, double y, int cx, int cy,
2292 EelCanvasItem **actual_item)
2293 {
2294 EelIRect canvas_rect;
2295
2296 *actual_item = item;
2297 canvas_rect.x0 = cx;
2298 canvas_rect.y0 = cy;
2299 canvas_rect.x1 = cx + 1;
2300 canvas_rect.y1 = cy + 1;
2301 if (hit_test (CAJA_ICON_CANVAS_ITEM (item), canvas_rect))
2302 {
2303 return 0.0;
2304 }
2305 else
2306 {
2307 /* This value means not hit.
2308 * It's kind of arbitrary. Can we do better?
2309 */
2310 return item->canvas->pixels_per_unit * 2 + 10;
2311 }
2312 }
2313
2314 static void
caja_icon_canvas_item_translate(EelCanvasItem * item,double dx,double dy)2315 caja_icon_canvas_item_translate (EelCanvasItem *item, double dx, double dy)
2316 {
2317 CajaIconCanvasItem *icon_item;
2318 CajaIconCanvasItemPrivate *details;
2319
2320 icon_item = CAJA_ICON_CANVAS_ITEM (item);
2321 details = icon_item->details;
2322
2323 details->x += dx;
2324 details->y += dy;
2325 }
2326
2327 void
caja_icon_canvas_item_get_bounds_for_layout(CajaIconCanvasItem * icon_item,double * x1,double * y1,double * x2,double * y2)2328 caja_icon_canvas_item_get_bounds_for_layout (CajaIconCanvasItem *icon_item,
2329 double *x1, double *y1, double *x2, double *y2)
2330 {
2331 CajaIconCanvasItemPrivate *details;
2332 EelIRect *total_rect;
2333
2334 details = icon_item->details;
2335
2336 caja_icon_canvas_item_ensure_bounds_up_to_date (icon_item);
2337 g_assert (details->bounds_cached);
2338
2339 total_rect = &details->bounds_cache_for_layout;
2340
2341 /* Return the result. */
2342 if (x1 != NULL)
2343 {
2344 *x1 = (int)details->x + total_rect->x0;
2345 }
2346 if (y1 != NULL)
2347 {
2348 *y1 = (int)details->y + total_rect->y0;
2349 }
2350 if (x2 != NULL)
2351 {
2352 *x2 = (int)details->x + total_rect->x1 + 1;
2353 }
2354 if (y2 != NULL)
2355 {
2356 *y2 = (int)details->y + total_rect->y1 + 1;
2357 }
2358 }
2359
2360 void
caja_icon_canvas_item_get_bounds_for_entire_item(CajaIconCanvasItem * icon_item,double * x1,double * y1,double * x2,double * y2)2361 caja_icon_canvas_item_get_bounds_for_entire_item (CajaIconCanvasItem *icon_item,
2362 double *x1, double *y1, double *x2, double *y2)
2363 {
2364 CajaIconCanvasItemPrivate *details;
2365 EelIRect *total_rect;
2366
2367 details = icon_item->details;
2368
2369 caja_icon_canvas_item_ensure_bounds_up_to_date (icon_item);
2370 g_assert (details->bounds_cached);
2371
2372 total_rect = &details->bounds_cache_for_entire_item;
2373
2374 /* Return the result. */
2375 if (x1 != NULL)
2376 {
2377 *x1 = (int)details->x + total_rect->x0;
2378 }
2379 if (y1 != NULL)
2380 {
2381 *y1 = (int)details->y + total_rect->y0;
2382 }
2383 if (x2 != NULL)
2384 {
2385 *x2 = (int)details->x + total_rect->x1 + 1;
2386 }
2387 if (y2 != NULL)
2388 {
2389 *y2 = (int)details->y + total_rect->y1 + 1;
2390 }
2391 }
2392
2393 /* Bounds handler for the icon canvas item. */
2394 static void
caja_icon_canvas_item_bounds(EelCanvasItem * item,double * x1,double * y1,double * x2,double * y2)2395 caja_icon_canvas_item_bounds (EelCanvasItem *item,
2396 double *x1, double *y1, double *x2, double *y2)
2397 {
2398 CajaIconCanvasItem *icon_item;
2399 CajaIconCanvasItemPrivate *details;
2400 EelIRect *total_rect;
2401
2402 icon_item = CAJA_ICON_CANVAS_ITEM (item);
2403 details = icon_item->details;
2404
2405 g_assert (x1 != NULL);
2406 g_assert (y1 != NULL);
2407 g_assert (x2 != NULL);
2408 g_assert (y2 != NULL);
2409
2410 caja_icon_canvas_item_ensure_bounds_up_to_date (icon_item);
2411 g_assert (details->bounds_cached);
2412
2413 total_rect = &details->bounds_cache;
2414
2415 /* Return the result. */
2416 *x1 = (int)details->x + total_rect->x0;
2417 *y1 = (int)details->y + total_rect->y0;
2418 *x2 = (int)details->x + total_rect->x1 + 1;
2419 *y2 = (int)details->y + total_rect->y1 + 1;
2420 }
2421
2422 static void
caja_icon_canvas_item_ensure_bounds_up_to_date(CajaIconCanvasItem * icon_item)2423 caja_icon_canvas_item_ensure_bounds_up_to_date (CajaIconCanvasItem *icon_item)
2424 {
2425 CajaIconCanvasItemPrivate *details;
2426 EelIRect icon_rect, emblem_rect, icon_rect_raw;
2427 EelIRect text_rect, text_rect_for_layout, text_rect_for_entire_text;
2428 EelIRect total_rect, total_rect_for_layout, total_rect_for_entire_text;
2429 EelCanvasItem *item;
2430 gint width, height;
2431 EmblemLayout emblem_layout;
2432 GdkPixbuf *emblem_pixbuf;
2433 gboolean is_rtl;
2434
2435 details = icon_item->details;
2436 item = EEL_CANVAS_ITEM (icon_item);
2437
2438 if (!details->bounds_cached)
2439 {
2440 double pixels_per_unit;
2441
2442 measure_label_text (icon_item);
2443
2444 pixels_per_unit = EEL_CANVAS_ITEM (item)->canvas->pixels_per_unit;
2445
2446 /* Compute raw and scaled icon rectangle. */
2447 icon_rect.x0 = 0;
2448 icon_rect.y0 = 0;
2449 icon_rect_raw.x0 = 0;
2450 icon_rect_raw.y0 = 0;
2451
2452 get_scaled_icon_size (icon_item, &width, &height);
2453
2454 icon_rect_raw.x1 = icon_rect_raw.x0 + width;
2455 icon_rect_raw.y1 = icon_rect_raw.y0 + height;
2456 icon_rect.x1 = icon_rect_raw.x1 / pixels_per_unit;
2457 icon_rect.y1 = icon_rect_raw.y1 / pixels_per_unit;
2458
2459 /* Compute text rectangle. */
2460 text_rect = compute_text_rectangle (icon_item, icon_rect, FALSE, BOUNDS_USAGE_FOR_DISPLAY);
2461 text_rect_for_layout = compute_text_rectangle (icon_item, icon_rect, FALSE, BOUNDS_USAGE_FOR_LAYOUT);
2462 text_rect_for_entire_text = compute_text_rectangle (icon_item, icon_rect, FALSE, BOUNDS_USAGE_FOR_ENTIRE_ITEM);
2463
2464 is_rtl = caja_icon_container_is_layout_rtl (CAJA_ICON_CONTAINER (item->canvas));
2465
2466 /* Compute total rectangle, adding in emblem rectangles. */
2467 eel_irect_union (&total_rect, &icon_rect, &text_rect);
2468 eel_irect_union (&total_rect_for_layout, &icon_rect, &text_rect_for_layout);
2469 eel_irect_union (&total_rect_for_entire_text, &icon_rect, &text_rect_for_entire_text);
2470 emblem_layout_reset (&emblem_layout, icon_item, icon_rect_raw, is_rtl);
2471 while (emblem_layout_next (&emblem_layout, &emblem_pixbuf, &emblem_rect, is_rtl))
2472 {
2473 emblem_rect.x0 = floor (emblem_rect.x0 / pixels_per_unit);
2474 emblem_rect.y0 = floor (emblem_rect.y0 / pixels_per_unit);
2475 emblem_rect.x1 = ceil (emblem_rect.x1 / pixels_per_unit);
2476 emblem_rect.y1 = ceil (emblem_rect.y1 / pixels_per_unit);
2477
2478 eel_irect_union (&total_rect, &total_rect, &emblem_rect);
2479 eel_irect_union (&total_rect_for_layout, &total_rect_for_layout, &emblem_rect);
2480 eel_irect_union (&total_rect_for_entire_text, &total_rect_for_entire_text, &emblem_rect);
2481 }
2482
2483 details->bounds_cache = total_rect;
2484 details->bounds_cache_for_layout = total_rect_for_layout;
2485 details->bounds_cache_for_entire_item = total_rect_for_entire_text;
2486 details->bounds_cached = TRUE;
2487 }
2488 }
2489
2490 /* Get the rectangle of the icon only, in world coordinates. */
2491 EelDRect
caja_icon_canvas_item_get_icon_rectangle(const CajaIconCanvasItem * item)2492 caja_icon_canvas_item_get_icon_rectangle (const CajaIconCanvasItem *item)
2493 {
2494 EelDRect rectangle;
2495 double pixels_per_unit;
2496 gint width, height;
2497
2498 g_return_val_if_fail (CAJA_IS_ICON_CANVAS_ITEM (item), eel_drect_empty);
2499
2500 rectangle.x0 = item->details->x;
2501 rectangle.y0 = item->details->y;
2502
2503 pixels_per_unit = EEL_CANVAS_ITEM (item)->canvas->pixels_per_unit;
2504 get_scaled_icon_size (CAJA_ICON_CANVAS_ITEM (item), &width, &height);
2505 rectangle.x1 = rectangle.x0 + width / pixels_per_unit;
2506 rectangle.y1 = rectangle.y0 + height / pixels_per_unit;
2507
2508 eel_canvas_item_i2w (EEL_CANVAS_ITEM (item),
2509 &rectangle.x0,
2510 &rectangle.y0);
2511 eel_canvas_item_i2w (EEL_CANVAS_ITEM (item),
2512 &rectangle.x1,
2513 &rectangle.y1);
2514
2515 return rectangle;
2516 }
2517
2518 EelDRect
caja_icon_canvas_item_get_text_rectangle(CajaIconCanvasItem * item,gboolean for_layout)2519 caja_icon_canvas_item_get_text_rectangle (CajaIconCanvasItem *item,
2520 gboolean for_layout)
2521 {
2522 /* FIXME */
2523 EelIRect icon_rectangle;
2524 EelIRect text_rectangle;
2525 EelDRect ret;
2526 double pixels_per_unit;
2527 gint width, height;
2528
2529 g_return_val_if_fail (CAJA_IS_ICON_CANVAS_ITEM (item), eel_drect_empty);
2530
2531 icon_rectangle.x0 = item->details->x;
2532 icon_rectangle.y0 = item->details->y;
2533
2534 pixels_per_unit = EEL_CANVAS_ITEM (item)->canvas->pixels_per_unit;
2535 get_scaled_icon_size (item, &width, &height);
2536 icon_rectangle.x1 = icon_rectangle.x0 + width / pixels_per_unit;
2537 icon_rectangle.y1 = icon_rectangle.y0 + height / pixels_per_unit;
2538
2539 measure_label_text (item);
2540
2541 text_rectangle = compute_text_rectangle (item, icon_rectangle, FALSE,
2542 for_layout ? BOUNDS_USAGE_FOR_LAYOUT : BOUNDS_USAGE_FOR_DISPLAY);
2543
2544 ret.x0 = text_rectangle.x0;
2545 ret.y0 = text_rectangle.y0;
2546 ret.x1 = text_rectangle.x1;
2547 ret.y1 = text_rectangle.y1;
2548
2549 eel_canvas_item_i2w (EEL_CANVAS_ITEM (item),
2550 &ret.x0,
2551 &ret.y0);
2552 eel_canvas_item_i2w (EEL_CANVAS_ITEM (item),
2553 &ret.x1,
2554 &ret.y1);
2555
2556 return ret;
2557 }
2558
2559 /* Get the rectangle of the icon only, in canvas coordinates. */
2560 static void
get_icon_canvas_rectangle(CajaIconCanvasItem * item,EelIRect * rect)2561 get_icon_canvas_rectangle (CajaIconCanvasItem *item,
2562 EelIRect *rect)
2563 {
2564 gint width, height;
2565
2566 g_assert (CAJA_IS_ICON_CANVAS_ITEM (item));
2567 g_assert (rect != NULL);
2568
2569
2570 eel_canvas_w2c (EEL_CANVAS_ITEM (item)->canvas,
2571 item->details->x,
2572 item->details->y,
2573 &rect->x0,
2574 &rect->y0);
2575
2576 get_scaled_icon_size (item, &width, &height);
2577
2578 rect->x1 = rect->x0 + width;
2579 rect->y1 = rect->y0 + height;
2580 }
2581
2582 void
caja_icon_canvas_item_set_show_stretch_handles(CajaIconCanvasItem * item,gboolean show_stretch_handles)2583 caja_icon_canvas_item_set_show_stretch_handles (CajaIconCanvasItem *item,
2584 gboolean show_stretch_handles)
2585 {
2586 g_return_if_fail (CAJA_IS_ICON_CANVAS_ITEM (item));
2587 g_return_if_fail (show_stretch_handles == FALSE || show_stretch_handles == TRUE);
2588
2589 if (!item->details->show_stretch_handles == !show_stretch_handles)
2590 {
2591 return;
2592 }
2593
2594 item->details->show_stretch_handles = show_stretch_handles;
2595 eel_canvas_item_request_update (EEL_CANVAS_ITEM (item));
2596 }
2597
2598 /* Check if one of the stretch handles was hit. */
2599 static gboolean
hit_test_stretch_handle(CajaIconCanvasItem * item,EelIRect probe_canvas_rect,GtkCornerType * corner)2600 hit_test_stretch_handle (CajaIconCanvasItem *item,
2601 EelIRect probe_canvas_rect,
2602 GtkCornerType *corner)
2603 {
2604 EelIRect icon_rect;
2605 GdkPixbuf *knob_pixbuf;
2606 int knob_width, knob_height;
2607 int hit_corner;
2608
2609 g_assert (CAJA_IS_ICON_CANVAS_ITEM (item));
2610
2611 /* Make sure there are handles to hit. */
2612 if (!item->details->show_stretch_handles)
2613 {
2614 return FALSE;
2615 }
2616
2617 /* Quick check to see if the rect hits the icon at all. */
2618 icon_rect = item->details->canvas_rect;
2619 if (!eel_irect_hits_irect (probe_canvas_rect, icon_rect))
2620 {
2621 return FALSE;
2622 }
2623
2624 knob_pixbuf = get_knob_pixbuf ();
2625 knob_width = gdk_pixbuf_get_width (knob_pixbuf);
2626 knob_height = gdk_pixbuf_get_height (knob_pixbuf);
2627 g_object_unref (knob_pixbuf);
2628
2629 /* Check for hits in the stretch handles. */
2630 hit_corner = -1;
2631 if (probe_canvas_rect.x0 < icon_rect.x0 + knob_width)
2632 {
2633 if (probe_canvas_rect.y0 < icon_rect.y0 + knob_height)
2634 hit_corner = GTK_CORNER_TOP_LEFT;
2635 else if (probe_canvas_rect.y1 >= icon_rect.y1 - knob_height)
2636 hit_corner = GTK_CORNER_BOTTOM_LEFT;
2637 }
2638 else if (probe_canvas_rect.x1 >= icon_rect.x1 - knob_width)
2639 {
2640 if (probe_canvas_rect.y0 < icon_rect.y0 + knob_height)
2641 hit_corner = GTK_CORNER_TOP_RIGHT;
2642 else if (probe_canvas_rect.y1 >= icon_rect.y1 - knob_height)
2643 hit_corner = GTK_CORNER_BOTTOM_RIGHT;
2644 }
2645 if (corner)
2646 *corner = hit_corner;
2647
2648 return hit_corner != -1;
2649 }
2650
2651 gboolean
caja_icon_canvas_item_hit_test_stretch_handles(CajaIconCanvasItem * item,EelDPoint world_point,GtkCornerType * corner)2652 caja_icon_canvas_item_hit_test_stretch_handles (CajaIconCanvasItem *item,
2653 EelDPoint world_point,
2654 GtkCornerType *corner)
2655 {
2656 EelIRect canvas_rect;
2657
2658 g_return_val_if_fail (CAJA_IS_ICON_CANVAS_ITEM (item), FALSE);
2659
2660 eel_canvas_w2c (EEL_CANVAS_ITEM (item)->canvas,
2661 world_point.x,
2662 world_point.y,
2663 &canvas_rect.x0,
2664 &canvas_rect.y0);
2665 canvas_rect.x1 = canvas_rect.x0 + 1;
2666 canvas_rect.y1 = canvas_rect.y0 + 1;
2667 return hit_test_stretch_handle (item, canvas_rect, corner);
2668 }
2669
2670 /* caja_icon_canvas_item_hit_test_rectangle
2671 *
2672 * Check and see if there is an intersection between the item and the
2673 * canvas rect.
2674 */
2675 gboolean
caja_icon_canvas_item_hit_test_rectangle(CajaIconCanvasItem * item,EelIRect canvas_rect)2676 caja_icon_canvas_item_hit_test_rectangle (CajaIconCanvasItem *item, EelIRect canvas_rect)
2677 {
2678 g_return_val_if_fail (CAJA_IS_ICON_CANVAS_ITEM (item), FALSE);
2679
2680 return hit_test (item, canvas_rect);
2681 }
2682
2683 const char *
caja_icon_canvas_item_get_editable_text(CajaIconCanvasItem * icon_item)2684 caja_icon_canvas_item_get_editable_text (CajaIconCanvasItem *icon_item)
2685 {
2686 g_return_val_if_fail (CAJA_IS_ICON_CANVAS_ITEM (icon_item), NULL);
2687
2688 return icon_item->details->editable_text;
2689 }
2690
2691 void
caja_icon_canvas_item_set_renaming(CajaIconCanvasItem * item,gboolean state)2692 caja_icon_canvas_item_set_renaming (CajaIconCanvasItem *item, gboolean state)
2693 {
2694 g_return_if_fail (CAJA_IS_ICON_CANVAS_ITEM (item));
2695 g_return_if_fail (state == FALSE || state == TRUE);
2696
2697 if (!item->details->is_renaming == !state)
2698 {
2699 return;
2700 }
2701
2702 item->details->is_renaming = state;
2703 eel_canvas_item_request_update (EEL_CANVAS_ITEM (item));
2704 }
2705
2706 double
caja_icon_canvas_item_get_max_text_width(CajaIconCanvasItem * item)2707 caja_icon_canvas_item_get_max_text_width (CajaIconCanvasItem *item)
2708 {
2709 EelCanvasItem *canvas_item;
2710 CajaIconContainer *container;
2711
2712 canvas_item = EEL_CANVAS_ITEM (item);
2713 container = CAJA_ICON_CONTAINER (canvas_item->canvas);
2714
2715 if (caja_icon_container_is_tighter_layout (container))
2716 {
2717 return MAX_TEXT_WIDTH_TIGHTER * canvas_item->canvas->pixels_per_unit;
2718 }
2719 else
2720 {
2721
2722 if (container->details->label_position == CAJA_ICON_LABEL_POSITION_BESIDE)
2723 {
2724 if (container->details->layout_mode == CAJA_ICON_LAYOUT_T_B_L_R ||
2725 container->details->layout_mode == CAJA_ICON_LAYOUT_T_B_R_L)
2726 {
2727 if (container->details->all_columns_same_width)
2728 {
2729 return MAX_TEXT_WIDTH_BESIDE_TOP_TO_BOTTOM * canvas_item->canvas->pixels_per_unit;
2730 }
2731 else
2732 {
2733 return -1;
2734 }
2735 }
2736 else
2737 {
2738 return MAX_TEXT_WIDTH_BESIDE * canvas_item->canvas->pixels_per_unit;
2739 }
2740 }
2741 else
2742 {
2743 return MAX_TEXT_WIDTH_STANDARD * canvas_item->canvas->pixels_per_unit;
2744 }
2745
2746
2747 }
2748
2749 }
2750
2751 void
caja_icon_canvas_item_set_entire_text(CajaIconCanvasItem * item,gboolean entire_text)2752 caja_icon_canvas_item_set_entire_text (CajaIconCanvasItem *item,
2753 gboolean entire_text)
2754 {
2755 if (item->details->entire_text != entire_text) {
2756 item->details->entire_text = entire_text;
2757
2758 caja_icon_canvas_item_invalidate_label_size (item);
2759 eel_canvas_item_request_update (EEL_CANVAS_ITEM (item));
2760 }
2761 }
2762
2763 /* Class initialization function for the icon canvas item. */
2764 static void
caja_icon_canvas_item_class_init(CajaIconCanvasItemClass * class)2765 caja_icon_canvas_item_class_init (CajaIconCanvasItemClass *class)
2766 {
2767 GObjectClass *object_class;
2768 EelCanvasItemClass *item_class;
2769
2770 object_class = G_OBJECT_CLASS (class);
2771 item_class = EEL_CANVAS_ITEM_CLASS (class);
2772
2773 object_class->finalize = caja_icon_canvas_item_finalize;
2774 object_class->set_property = caja_icon_canvas_item_set_property;
2775 object_class->get_property = caja_icon_canvas_item_get_property;
2776
2777 g_object_class_install_property (
2778 object_class,
2779 PROP_EDITABLE_TEXT,
2780 g_param_spec_string ("editable_text",
2781 "editable text",
2782 "the editable label",
2783 "", G_PARAM_READWRITE));
2784
2785 g_object_class_install_property (
2786 object_class,
2787 PROP_ADDITIONAL_TEXT,
2788 g_param_spec_string ("additional_text",
2789 "additional text",
2790 "some more text",
2791 "", G_PARAM_READWRITE));
2792
2793 g_object_class_install_property (
2794 object_class,
2795 PROP_HIGHLIGHTED_FOR_SELECTION,
2796 g_param_spec_boolean ("highlighted_for_selection",
2797 "highlighted for selection",
2798 "whether we are highlighted for a selection",
2799 FALSE, G_PARAM_READWRITE));
2800
2801 g_object_class_install_property (
2802 object_class,
2803 PROP_HIGHLIGHTED_AS_KEYBOARD_FOCUS,
2804 g_param_spec_boolean ("highlighted_as_keyboard_focus",
2805 "highlighted as keyboard focus",
2806 "whether we are highlighted to render keyboard focus",
2807 FALSE, G_PARAM_READWRITE));
2808
2809
2810 g_object_class_install_property (
2811 object_class,
2812 PROP_HIGHLIGHTED_FOR_DROP,
2813 g_param_spec_boolean ("highlighted_for_drop",
2814 "highlighted for drop",
2815 "whether we are highlighted for a D&D drop",
2816 FALSE, G_PARAM_READWRITE));
2817
2818 g_object_class_install_property (
2819 object_class,
2820 PROP_HIGHLIGHTED_FOR_CLIPBOARD,
2821 g_param_spec_boolean ("highlighted_for_clipboard",
2822 "highlighted for clipboard",
2823 "whether we are highlighted for a clipboard paste (after we have been cut)",
2824 FALSE, G_PARAM_READWRITE));
2825
2826 item_class->update = caja_icon_canvas_item_update;
2827 item_class->draw = caja_icon_canvas_item_draw;
2828 item_class->point = caja_icon_canvas_item_point;
2829 item_class->translate = caja_icon_canvas_item_translate;
2830 item_class->bounds = caja_icon_canvas_item_bounds;
2831 item_class->event = caja_icon_canvas_item_event;
2832
2833 atk_registry_set_factory_type (atk_get_default_registry (),
2834 CAJA_TYPE_ICON_CANVAS_ITEM,
2835 caja_icon_canvas_item_accessible_factory_get_type ());
2836 }
2837
2838 static GailTextUtil *
caja_icon_canvas_item_get_text(GObject * text)2839 caja_icon_canvas_item_get_text (GObject *text)
2840 {
2841 return CAJA_ICON_CANVAS_ITEM (text)->details->text_util;
2842 }
2843
2844 static void
caja_icon_canvas_item_text_interface_init(EelAccessibleTextIface * iface)2845 caja_icon_canvas_item_text_interface_init (EelAccessibleTextIface *iface)
2846 {
2847 iface->get_text = caja_icon_canvas_item_get_text;
2848 }
2849
2850 /* ============================= a11y interfaces =========================== */
2851
2852 static const char *caja_icon_canvas_item_accessible_action_names[] = {
2853 "open",
2854 "menu",
2855 NULL
2856 };
2857
2858 static const char *caja_icon_canvas_item_accessible_action_descriptions[] = {
2859 "Open item",
2860 "Popup context menu",
2861 NULL
2862 };
2863
2864 enum {
2865 ACTION_OPEN,
2866 ACTION_MENU,
2867 LAST_ACTION
2868 };
2869
2870 typedef struct {
2871 char *action_descriptions[LAST_ACTION];
2872 char *image_description;
2873 char *description;
2874 } CajaIconCanvasItemAccessiblePrivate;
2875
2876 typedef struct {
2877 CajaIconCanvasItem *item;
2878 gint action_number;
2879 } CajaIconCanvasItemAccessibleActionContext;
2880
2881 static GType caja_icon_canvas_item_accessible_get_type (void);
2882
2883 #define GET_PRIV(o) G_TYPE_INSTANCE_GET_PRIVATE(o, caja_icon_canvas_item_accessible_get_type (), CajaIconCanvasItemAccessiblePrivate);
2884
2885 /* accessible AtkAction interface */
2886
2887 static gboolean
caja_icon_canvas_item_accessible_idle_do_action(gpointer data)2888 caja_icon_canvas_item_accessible_idle_do_action (gpointer data)
2889 {
2890 CajaIconCanvasItem *item;
2891 CajaIconCanvasItemAccessibleActionContext *ctx;
2892 CajaIcon *icon;
2893 CajaIconContainer *container;
2894 GList* selection;
2895 GList file_list;
2896 GdkEventButton button_event = { 0 };
2897 gint action_number;
2898
2899 container = CAJA_ICON_CONTAINER (data);
2900 container->details->a11y_item_action_idle_handler = 0;
2901 while (!g_queue_is_empty (container->details->a11y_item_action_queue))
2902 {
2903 ctx = g_queue_pop_head (container->details->a11y_item_action_queue);
2904 action_number = ctx->action_number;
2905 item = ctx->item;
2906 g_free (ctx);
2907 icon = item->user_data;
2908
2909 switch (action_number)
2910 {
2911 case ACTION_OPEN:
2912 file_list.data = icon->data;
2913 file_list.next = NULL;
2914 file_list.prev = NULL;
2915 g_signal_emit_by_name (container, "activate", &file_list);
2916 break;
2917 case ACTION_MENU:
2918 selection = caja_icon_container_get_selection (container);
2919 if (selection == NULL ||
2920 g_list_length (selection) != 1 ||
2921 selection->data != icon->data)
2922 {
2923 g_list_free (selection);
2924 return FALSE;
2925 }
2926 g_list_free (selection);
2927 g_signal_emit_by_name (container, "context_click_selection", &button_event);
2928 break;
2929 default :
2930 g_assert_not_reached ();
2931 break;
2932 }
2933 }
2934 return FALSE;
2935 }
2936
2937 static gboolean
caja_icon_canvas_item_accessible_do_action(AtkAction * accessible,int i)2938 caja_icon_canvas_item_accessible_do_action (AtkAction *accessible, int i)
2939 {
2940 CajaIconCanvasItem *item;
2941 CajaIconCanvasItemAccessibleActionContext *ctx;
2942 CajaIconContainer *container;
2943
2944 g_assert (i < LAST_ACTION);
2945
2946 item = CAJA_ICON_CANVAS_ITEM (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (accessible)));
2947 if (!item)
2948 {
2949 return FALSE;
2950 }
2951 container = CAJA_ICON_CONTAINER (EEL_CANVAS_ITEM (item)->canvas);
2952 switch (i)
2953 {
2954 case ACTION_OPEN:
2955 case ACTION_MENU:
2956 if (container->details->a11y_item_action_queue == NULL)
2957 {
2958 container->details->a11y_item_action_queue = g_queue_new ();
2959 }
2960 ctx = g_new (CajaIconCanvasItemAccessibleActionContext, 1);
2961 ctx->action_number = i;
2962 ctx->item = item;
2963 g_queue_push_head (container->details->a11y_item_action_queue, ctx);
2964 if (container->details->a11y_item_action_idle_handler == 0)
2965 {
2966 container->details->a11y_item_action_idle_handler = g_idle_add (caja_icon_canvas_item_accessible_idle_do_action, container);
2967 }
2968 break;
2969 default :
2970 g_warning ("Invalid action passed to CajaIconCanvasItemAccessible::do_action");
2971 return FALSE;
2972 }
2973
2974 return TRUE;
2975 }
2976
2977 static int
caja_icon_canvas_item_accessible_get_n_actions(AtkAction * accessible)2978 caja_icon_canvas_item_accessible_get_n_actions (AtkAction *accessible)
2979 {
2980 return LAST_ACTION;
2981 }
2982
2983 static const char *
caja_icon_canvas_item_accessible_action_get_description(AtkAction * accessible,int i)2984 caja_icon_canvas_item_accessible_action_get_description (AtkAction *accessible,
2985 int i)
2986 {
2987 CajaIconCanvasItemAccessiblePrivate *priv;
2988
2989 g_assert (i < LAST_ACTION);
2990
2991 priv = GET_PRIV (accessible);
2992 if (priv->action_descriptions[i])
2993 {
2994 return priv->action_descriptions[i];
2995 }
2996 else
2997 {
2998 return caja_icon_canvas_item_accessible_action_descriptions[i];
2999 }
3000 }
3001
3002 static const char *
caja_icon_canvas_item_accessible_action_get_name(AtkAction * accessible,int i)3003 caja_icon_canvas_item_accessible_action_get_name (AtkAction *accessible, int i)
3004 {
3005 g_assert (i < LAST_ACTION);
3006
3007 return caja_icon_canvas_item_accessible_action_names[i];
3008 }
3009
3010 static const char *
caja_icon_canvas_item_accessible_action_get_keybinding(AtkAction * accessible,int i)3011 caja_icon_canvas_item_accessible_action_get_keybinding (AtkAction *accessible,
3012 int i)
3013 {
3014 g_assert (i < LAST_ACTION);
3015
3016 return NULL;
3017 }
3018
3019 static gboolean
caja_icon_canvas_item_accessible_action_set_description(AtkAction * accessible,int i,const char * description)3020 caja_icon_canvas_item_accessible_action_set_description (AtkAction *accessible,
3021 int i,
3022 const char *description)
3023 {
3024 CajaIconCanvasItemAccessiblePrivate *priv;
3025
3026 g_assert (i < LAST_ACTION);
3027
3028 priv = GET_PRIV (accessible);
3029
3030 if (priv->action_descriptions[i])
3031 {
3032 g_free (priv->action_descriptions[i]);
3033 }
3034 priv->action_descriptions[i] = g_strdup (description);
3035
3036 return TRUE;
3037 }
3038
3039 static void
caja_icon_canvas_item_accessible_action_interface_init(AtkActionIface * iface)3040 caja_icon_canvas_item_accessible_action_interface_init (AtkActionIface *iface)
3041 {
3042 iface->do_action = caja_icon_canvas_item_accessible_do_action;
3043 iface->get_n_actions = caja_icon_canvas_item_accessible_get_n_actions;
3044 iface->get_description = caja_icon_canvas_item_accessible_action_get_description;
3045 iface->get_keybinding = caja_icon_canvas_item_accessible_action_get_keybinding;
3046 iface->get_name = caja_icon_canvas_item_accessible_action_get_name;
3047 iface->set_description = caja_icon_canvas_item_accessible_action_set_description;
3048 }
3049
caja_icon_canvas_item_accessible_get_name(AtkObject * accessible)3050 static const gchar* caja_icon_canvas_item_accessible_get_name(AtkObject* accessible)
3051 {
3052 CajaIconCanvasItem* item;
3053
3054 if (accessible->name)
3055 {
3056 return accessible->name;
3057 }
3058
3059 item = CAJA_ICON_CANVAS_ITEM (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (accessible)));
3060
3061 if (!item)
3062 {
3063 return NULL;
3064 }
3065
3066 return item->details->editable_text;
3067 }
3068
caja_icon_canvas_item_accessible_get_description(AtkObject * accessible)3069 static const gchar* caja_icon_canvas_item_accessible_get_description(AtkObject* accessible)
3070 {
3071 CajaIconCanvasItem* item;
3072
3073 item = CAJA_ICON_CANVAS_ITEM (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (accessible)));
3074
3075 if (!item)
3076 {
3077 return NULL;
3078 }
3079
3080 return item->details->additional_text;
3081 }
3082
3083 static AtkObject *
caja_icon_canvas_item_accessible_get_parent(AtkObject * accessible)3084 caja_icon_canvas_item_accessible_get_parent (AtkObject *accessible)
3085 {
3086 CajaIconCanvasItem *item;
3087
3088 item = CAJA_ICON_CANVAS_ITEM (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (accessible)));
3089 if (!item)
3090 {
3091 return NULL;
3092 }
3093
3094 return gtk_widget_get_accessible (GTK_WIDGET (EEL_CANVAS_ITEM (item)->canvas));
3095 }
3096
3097 static int
caja_icon_canvas_item_accessible_get_index_in_parent(AtkObject * accessible)3098 caja_icon_canvas_item_accessible_get_index_in_parent (AtkObject *accessible)
3099 {
3100 CajaIconCanvasItem *item;
3101 CajaIconContainer *container;
3102 GList *l;
3103 int i;
3104 CajaIcon *icon = NULL;
3105
3106 item = CAJA_ICON_CANVAS_ITEM (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (accessible)));
3107 if (!item)
3108 {
3109 return -1;
3110 }
3111
3112 container = CAJA_ICON_CONTAINER (EEL_CANVAS_ITEM (item)->canvas);
3113
3114 l = container->details->icons;
3115 i = 0;
3116 while (l)
3117 {
3118 icon = l->data;
3119
3120 if (icon->item == item)
3121 {
3122 return i;
3123 }
3124
3125 i++;
3126 l = l->next;
3127 }
3128
3129 return -1;
3130 }
3131
3132
caja_icon_canvas_item_accessible_get_image_description(AtkImage * image)3133 static const gchar* caja_icon_canvas_item_accessible_get_image_description(AtkImage* image)
3134 {
3135 CajaIconCanvasItemAccessiblePrivate* priv;
3136 CajaIconCanvasItem* item;
3137 char* description;
3138
3139 priv = GET_PRIV (image);
3140
3141 if (priv->image_description)
3142 {
3143 return priv->image_description;
3144 }
3145 else
3146 {
3147 CajaIcon* icon;
3148 CajaIconContainer* container;
3149
3150 item = CAJA_ICON_CANVAS_ITEM (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (image)));
3151
3152 if (item == NULL)
3153 {
3154 return NULL;
3155 }
3156
3157 icon = item->user_data;
3158 container = CAJA_ICON_CONTAINER(EEL_CANVAS_ITEM(item)->canvas);
3159 description = caja_icon_container_get_icon_description(container, icon->data);
3160 g_free(priv->description);
3161 priv->description = description;
3162
3163 return priv->description;
3164 }
3165 }
3166
3167 static void
caja_icon_canvas_item_accessible_get_image_size(AtkImage * image,gint * width,gint * height)3168 caja_icon_canvas_item_accessible_get_image_size
3169 (AtkImage *image,
3170 gint *width,
3171 gint *height)
3172 {
3173 CajaIconCanvasItem *item;
3174
3175 item = CAJA_ICON_CANVAS_ITEM (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (image)));
3176
3177 get_scaled_icon_size (item, width, height);
3178 }
3179
3180 static void
caja_icon_canvas_item_accessible_get_image_position(AtkImage * image,gint * x,gint * y,AtkCoordType coord_type)3181 caja_icon_canvas_item_accessible_get_image_position
3182 (AtkImage *image,
3183 gint *x,
3184 gint *y,
3185 AtkCoordType coord_type)
3186 {
3187 CajaIconCanvasItem *item;
3188 gint x_offset, y_offset, itmp;
3189
3190 item = CAJA_ICON_CANVAS_ITEM (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (image)));
3191 if (!item)
3192 {
3193 return;
3194 }
3195 if (!item->details->canvas_rect.x0 && !item->details->canvas_rect.x1)
3196 {
3197 return;
3198 }
3199 else
3200 {
3201 x_offset = 0;
3202 y_offset = 0;
3203 if (item->details->text_width)
3204 {
3205 itmp = item->details->canvas_rect.x0 -
3206 item->details->text_rect.x0;
3207 if (itmp > x_offset)
3208 {
3209 x_offset = itmp;
3210 }
3211 itmp = item->details->canvas_rect.y0 -
3212 item->details->text_rect.y0;
3213 if (itmp > y_offset)
3214 {
3215 y_offset = itmp;
3216 }
3217 }
3218 if (item->details->emblem_pixbufs)
3219 {
3220 itmp = item->details->canvas_rect.x0 -
3221 item->details->emblem_rect.x0;
3222 if (itmp > x_offset)
3223 {
3224 x_offset = itmp;
3225 }
3226 itmp = item->details->canvas_rect.y0 -
3227 item->details->emblem_rect.y0;
3228 if (itmp > y_offset)
3229 {
3230 y_offset = itmp;
3231 }
3232 }
3233 }
3234 atk_component_get_extents (ATK_COMPONENT (image), x, y, NULL, NULL, coord_type);
3235 *x += x_offset;
3236 *y += y_offset;
3237 }
3238
3239 static gboolean
caja_icon_canvas_item_accessible_set_image_description(AtkImage * image,const gchar * description)3240 caja_icon_canvas_item_accessible_set_image_description
3241 (AtkImage *image,
3242 const gchar *description)
3243 {
3244 CajaIconCanvasItemAccessiblePrivate *priv;
3245
3246 priv = GET_PRIV (image);
3247
3248 g_free (priv->image_description);
3249 priv->image_description = g_strdup (description);
3250
3251 return TRUE;
3252 }
3253
3254 static void
caja_icon_canvas_item_accessible_image_interface_init(AtkImageIface * iface)3255 caja_icon_canvas_item_accessible_image_interface_init (AtkImageIface *iface)
3256 {
3257 iface->get_image_description = caja_icon_canvas_item_accessible_get_image_description;
3258 iface->set_image_description = caja_icon_canvas_item_accessible_set_image_description;
3259 iface->get_image_size = caja_icon_canvas_item_accessible_get_image_size;
3260 iface->get_image_position = caja_icon_canvas_item_accessible_get_image_position;
3261 }
3262
3263 static gint
caja_icon_canvas_item_accessible_get_offset_at_point(AtkText * text,gint x,gint y,AtkCoordType coords)3264 caja_icon_canvas_item_accessible_get_offset_at_point (AtkText *text,
3265 gint x,
3266 gint y,
3267 AtkCoordType coords)
3268 {
3269 gint real_x, real_y, real_width, real_height;
3270 CajaIconCanvasItem *item;
3271 gint editable_height;
3272 gint offset = 0;
3273 gint index;
3274 PangoLayout *layout, *editable_layout, *additional_layout;
3275 PangoRectangle rect0;
3276 char *icon_text;
3277 gboolean have_editable;
3278 gboolean have_additional;
3279 gint text_offset, height;
3280
3281 atk_component_get_extents (ATK_COMPONENT (text), &real_x, &real_y,
3282 &real_width, &real_height, coords);
3283
3284 x -= real_x;
3285 y -= real_y;
3286
3287 item = CAJA_ICON_CANVAS_ITEM (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (text)));
3288
3289 if (item->details->pixbuf)
3290 {
3291 get_scaled_icon_size (item, NULL, &height);
3292 y -= height;
3293 }
3294 have_editable = item->details->editable_text != NULL &&
3295 item->details->editable_text[0] != '\0';
3296 have_additional = item->details->additional_text != NULL &&item->details->additional_text[0] != '\0';
3297
3298 editable_layout = NULL;
3299 additional_layout = NULL;
3300 if (have_editable)
3301 {
3302 editable_layout = get_label_layout (&item->details->editable_text_layout, item, item->details->editable_text);
3303 prepare_pango_layout_for_draw (item, editable_layout);
3304 pango_layout_get_pixel_size (editable_layout, NULL, &editable_height);
3305 if (y >= editable_height &&
3306 have_additional)
3307 {
3308 prepare_pango_layout_for_draw (item, editable_layout);
3309 additional_layout = get_label_layout (&item->details->additional_text_layout, item, item->details->additional_text);
3310 layout = additional_layout;
3311 icon_text = item->details->additional_text;
3312 y -= editable_height + LABEL_LINE_SPACING;
3313 }
3314 else
3315 {
3316 layout = editable_layout;
3317 icon_text = item->details->editable_text;
3318 }
3319 }
3320 else if (have_additional)
3321 {
3322 additional_layout = get_label_layout (&item->details->additional_text_layout, item, item->details->additional_text);
3323 prepare_pango_layout_for_draw (item, additional_layout);
3324 layout = additional_layout;
3325 icon_text = item->details->additional_text;
3326 }
3327 else
3328 {
3329 return 0;
3330 }
3331
3332 text_offset = 0;
3333 if (have_editable)
3334 {
3335 pango_layout_index_to_pos (editable_layout, 0, &rect0);
3336 text_offset = PANGO_PIXELS (rect0.x);
3337 }
3338 if (have_additional)
3339 {
3340 gint itmp;
3341
3342 pango_layout_index_to_pos (additional_layout, 0, &rect0);
3343 itmp = PANGO_PIXELS (rect0.x);
3344 if (itmp < text_offset)
3345 {
3346 text_offset = itmp;
3347 }
3348 }
3349 pango_layout_index_to_pos (layout, 0, &rect0);
3350 x += text_offset;
3351 if (!pango_layout_xy_to_index (layout,
3352 x * PANGO_SCALE,
3353 y * PANGO_SCALE,
3354 &index, NULL))
3355 {
3356 if (x < 0 || y < 0)
3357 {
3358 index = 0;
3359 }
3360 else
3361 {
3362 index = -1;
3363 }
3364 }
3365 if (index == -1)
3366 {
3367 offset = g_utf8_strlen (icon_text, -1);
3368 }
3369 else
3370 {
3371 offset = g_utf8_pointer_to_offset (icon_text, icon_text + index);
3372 }
3373 if (layout == additional_layout)
3374 {
3375 offset += g_utf8_strlen (item->details->editable_text, -1);
3376 }
3377
3378 if (editable_layout != NULL)
3379 {
3380 g_object_unref (editable_layout);
3381 }
3382
3383 if (additional_layout != NULL)
3384 {
3385 g_object_unref (additional_layout);
3386 }
3387
3388 return offset;
3389 }
3390
3391 static void
caja_icon_canvas_item_accessible_get_character_extents(AtkText * text,gint offset,gint * x,gint * y,gint * width,gint * height,AtkCoordType coords)3392 caja_icon_canvas_item_accessible_get_character_extents (AtkText *text,
3393 gint offset,
3394 gint *x,
3395 gint *y,
3396 gint *width,
3397 gint *height,
3398 AtkCoordType coords)
3399 {
3400 gint pos_x, pos_y;
3401 gint len, byte_offset;
3402 gint editable_height;
3403 gchar *icon_text;
3404 CajaIconCanvasItem *item;
3405 PangoLayout *layout, *editable_layout, *additional_layout;
3406 PangoRectangle rect;
3407 PangoRectangle rect0;
3408 gboolean have_editable;
3409 gint text_offset, pix_height;
3410
3411 atk_component_get_extents (ATK_COMPONENT (text), &pos_x, &pos_y, NULL, NULL, coords);
3412 item = CAJA_ICON_CANVAS_ITEM (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (text)));
3413
3414 if (item->details->pixbuf)
3415 {
3416 get_scaled_icon_size (item, NULL, &pix_height);
3417 pos_y += pix_height;
3418 }
3419
3420 have_editable = item->details->editable_text != NULL &&
3421 item->details->editable_text[0] != '\0';
3422 if (have_editable)
3423 {
3424 len = g_utf8_strlen (item->details->editable_text, -1);
3425 }
3426 else
3427 {
3428 len = 0;
3429 }
3430
3431 editable_layout = get_label_layout (&item->details->editable_text_layout, item, item->details->editable_text);
3432 additional_layout = get_label_layout (&item->details->additional_text_layout, item, item->details->additional_text);
3433
3434 if (offset < len)
3435 {
3436 icon_text = item->details->editable_text;
3437 layout = editable_layout;
3438 }
3439 else
3440 {
3441 offset -= len;
3442 icon_text = item->details->additional_text;
3443 layout = additional_layout;
3444 pos_y += LABEL_LINE_SPACING;
3445 if (have_editable)
3446 {
3447 pango_layout_get_pixel_size (editable_layout, NULL, &editable_height);
3448 pos_y += editable_height;
3449 }
3450 }
3451 byte_offset = g_utf8_offset_to_pointer (icon_text, offset) - icon_text;
3452 pango_layout_index_to_pos (layout, byte_offset, &rect);
3453 text_offset = 0;
3454 if (have_editable)
3455 {
3456 pango_layout_index_to_pos (editable_layout, 0, &rect0);
3457 text_offset = PANGO_PIXELS (rect0.x);
3458 }
3459 if (item->details->additional_text != NULL &&
3460 item->details->additional_text[0] != '\0')
3461 {
3462 gint itmp;
3463
3464 pango_layout_index_to_pos (additional_layout, 0, &rect0);
3465 itmp = PANGO_PIXELS (rect0.x);
3466 if (itmp < text_offset)
3467 {
3468 text_offset = itmp;
3469 }
3470 }
3471
3472 g_object_unref (editable_layout);
3473 g_object_unref (additional_layout);
3474
3475 *x = pos_x + PANGO_PIXELS (rect.x) - text_offset;
3476 *y = pos_y + PANGO_PIXELS (rect.y);
3477 *width = PANGO_PIXELS (rect.width);
3478 *height = PANGO_PIXELS (rect.height);
3479 }
3480
3481 static void
caja_icon_canvas_item_accessible_text_interface_init(AtkTextIface * iface)3482 caja_icon_canvas_item_accessible_text_interface_init (AtkTextIface *iface)
3483 {
3484 iface->get_text = eel_accessibility_text_get_text;
3485 iface->get_character_at_offset = eel_accessibility_text_get_character_at_offset;
3486 iface->get_text_before_offset = eel_accessibility_text_get_text_before_offset;
3487 iface->get_text_at_offset = eel_accessibility_text_get_text_at_offset;
3488 iface->get_text_after_offset = eel_accessibility_text_get_text_after_offset;
3489 iface->get_character_count = eel_accessibility_text_get_character_count;
3490 iface->get_character_extents = caja_icon_canvas_item_accessible_get_character_extents;
3491 iface->get_offset_at_point = caja_icon_canvas_item_accessible_get_offset_at_point;
3492 }
3493
3494 typedef struct {
3495 EelCanvasItemAccessible parent;
3496 } CajaIconCanvasItemAccessible;
3497
3498 typedef struct {
3499 EelCanvasItemAccessibleClass parent_class;
3500 } CajaIconCanvasItemAccessibleClass;
3501
3502 G_DEFINE_TYPE_WITH_CODE (CajaIconCanvasItemAccessible,
3503 caja_icon_canvas_item_accessible,
3504 eel_canvas_item_accessible_get_type (),
3505 G_ADD_PRIVATE (CajaIconCanvasItemAccessible)
3506 G_IMPLEMENT_INTERFACE (ATK_TYPE_IMAGE,
3507 caja_icon_canvas_item_accessible_image_interface_init)
3508 G_IMPLEMENT_INTERFACE (ATK_TYPE_TEXT,
3509 caja_icon_canvas_item_accessible_text_interface_init)
3510 G_IMPLEMENT_INTERFACE (ATK_TYPE_ACTION,
3511 caja_icon_canvas_item_accessible_action_interface_init));
3512
3513 static AtkStateSet*
caja_icon_canvas_item_accessible_ref_state_set(AtkObject * accessible)3514 caja_icon_canvas_item_accessible_ref_state_set (AtkObject *accessible)
3515 {
3516 AtkStateSet *state_set;
3517 CajaIconCanvasItem *item;
3518 CajaIconContainer *container;
3519 CajaIcon *icon;
3520 GList *l;
3521 gboolean one_item_selected;
3522
3523 state_set = ATK_OBJECT_CLASS (caja_icon_canvas_item_accessible_parent_class)->ref_state_set (accessible);
3524
3525 item = CAJA_ICON_CANVAS_ITEM (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (accessible)));
3526 if (!item) {
3527 atk_state_set_add_state (state_set, ATK_STATE_DEFUNCT);
3528 return state_set;
3529 }
3530 container = CAJA_ICON_CONTAINER (EEL_CANVAS_ITEM (item)->canvas);
3531 if (item->details->is_highlighted_as_keyboard_focus) {
3532 atk_state_set_add_state (state_set, ATK_STATE_FOCUSED);
3533 } else if (!container->details->keyboard_focus) {
3534
3535 one_item_selected = FALSE;
3536 l = container->details->icons;
3537 while (l) {
3538 icon = l->data;
3539
3540 if (icon->item == item) {
3541 if (icon->is_selected) {
3542 one_item_selected = TRUE;
3543 } else {
3544 break;
3545 }
3546 } else if (icon->is_selected) {
3547 one_item_selected = FALSE;
3548 break;
3549 }
3550
3551 l = l->next;
3552 }
3553
3554 if (one_item_selected) {
3555 atk_state_set_add_state (state_set, ATK_STATE_FOCUSED);
3556 }
3557 }
3558
3559 return state_set;
3560 }
3561
3562 static void
caja_icon_canvas_item_accessible_finalize(GObject * object)3563 caja_icon_canvas_item_accessible_finalize (GObject *object)
3564 {
3565 CajaIconCanvasItemAccessiblePrivate *priv;
3566 int i;
3567
3568 priv = GET_PRIV (object);
3569
3570 for (i = 0; i < LAST_ACTION; i++) {
3571 g_free (priv->action_descriptions[i]);
3572 }
3573 g_free (priv->image_description);
3574 g_free (priv->description);
3575
3576 G_OBJECT_CLASS (caja_icon_canvas_item_accessible_parent_class)->finalize (object);
3577 }
3578
3579 static void
caja_icon_canvas_item_accessible_initialize(AtkObject * accessible,gpointer widget)3580 caja_icon_canvas_item_accessible_initialize (AtkObject *accessible,
3581 gpointer widget)
3582 {
3583 ATK_OBJECT_CLASS (caja_icon_canvas_item_accessible_parent_class)->initialize (accessible, widget);
3584
3585 atk_object_set_role (accessible, ATK_ROLE_ICON);
3586 }
3587
3588 static void
caja_icon_canvas_item_accessible_class_init(CajaIconCanvasItemAccessibleClass * klass)3589 caja_icon_canvas_item_accessible_class_init (CajaIconCanvasItemAccessibleClass *klass)
3590 {
3591 AtkObjectClass *aclass = ATK_OBJECT_CLASS (klass);
3592 GObjectClass *oclass = G_OBJECT_CLASS (klass);
3593
3594 oclass->finalize = caja_icon_canvas_item_accessible_finalize;
3595
3596 aclass->initialize = caja_icon_canvas_item_accessible_initialize;
3597
3598 aclass->get_name = caja_icon_canvas_item_accessible_get_name;
3599 aclass->get_description = caja_icon_canvas_item_accessible_get_description;
3600 aclass->get_parent = caja_icon_canvas_item_accessible_get_parent;
3601 aclass->get_index_in_parent = caja_icon_canvas_item_accessible_get_index_in_parent;
3602 aclass->ref_state_set = caja_icon_canvas_item_accessible_ref_state_set;
3603 }
3604
3605 static void
caja_icon_canvas_item_accessible_init(CajaIconCanvasItemAccessible * self)3606 caja_icon_canvas_item_accessible_init (CajaIconCanvasItemAccessible *self)
3607 {
3608 }
3609
3610 /* dummy typedef */
3611 typedef AtkObjectFactory CajaIconCanvasItemAccessibleFactory;
3612 typedef AtkObjectFactoryClass CajaIconCanvasItemAccessibleFactoryClass;
3613
3614 G_DEFINE_TYPE (CajaIconCanvasItemAccessibleFactory, caja_icon_canvas_item_accessible_factory,
3615 ATK_TYPE_OBJECT_FACTORY);
3616
3617
3618 static AtkObject *
caja_icon_canvas_item_accessible_factory_create_accessible(GObject * for_object)3619 caja_icon_canvas_item_accessible_factory_create_accessible (GObject *for_object)
3620 {
3621 AtkObject *accessible;
3622 CajaIconCanvasItem *item;
3623 GString *item_text;
3624
3625 item = CAJA_ICON_CANVAS_ITEM (for_object);
3626 g_assert (item != NULL);
3627
3628 item_text = g_string_new (NULL);
3629 if (item->details->editable_text)
3630 {
3631 g_string_append (item_text, item->details->editable_text);
3632 }
3633 if (item->details->additional_text)
3634 {
3635 if (item_text->len > 0)
3636 g_string_append_c (item_text, ' ');
3637 g_string_append (item_text, item->details->additional_text);
3638 }
3639 item->details->text_util = gail_text_util_new ();
3640 gail_text_util_text_setup (item->details->text_util,
3641 item_text->str);
3642 g_string_free (item_text, TRUE);
3643
3644 accessible = g_object_new (caja_icon_canvas_item_accessible_get_type (), NULL);
3645 atk_object_initialize (accessible, for_object);
3646 return accessible;
3647 }
3648
3649 static GType
caja_icon_canvas_item_accessible_factory_get_accessible_type(void)3650 caja_icon_canvas_item_accessible_factory_get_accessible_type (void)
3651 {
3652 return caja_icon_canvas_item_accessible_get_type ();
3653 }
3654
3655 static void
caja_icon_canvas_item_accessible_factory_init(CajaIconCanvasItemAccessibleFactory * self)3656 caja_icon_canvas_item_accessible_factory_init (CajaIconCanvasItemAccessibleFactory *self)
3657 {
3658 }
3659
3660 static void
caja_icon_canvas_item_accessible_factory_class_init(CajaIconCanvasItemAccessibleFactoryClass * klass)3661 caja_icon_canvas_item_accessible_factory_class_init (CajaIconCanvasItemAccessibleFactoryClass *klass)
3662 {
3663 klass->create_accessible = caja_icon_canvas_item_accessible_factory_create_accessible;
3664 klass->get_accessible_type = caja_icon_canvas_item_accessible_factory_get_accessible_type;
3665 }
3666
3667