summaryrefslogtreecommitdiff
path: root/libcaja-private/caja-icon-canvas-item.c
diff options
context:
space:
mode:
Diffstat (limited to 'libcaja-private/caja-icon-canvas-item.c')
-rw-r--r--libcaja-private/caja-icon-canvas-item.c3874
1 files changed, 3874 insertions, 0 deletions
diff --git a/libcaja-private/caja-icon-canvas-item.c b/libcaja-private/caja-icon-canvas-item.c
new file mode 100644
index 00000000..7362809b
--- /dev/null
+++ b/libcaja-private/caja-icon-canvas-item.c
@@ -0,0 +1,3874 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/* Caja - Icon canvas item class for icon container.
+ *
+ * Copyright (C) 2000 Eazel, Inc
+ *
+ * Author: Andy Hertzfeld <andy@eazel.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <math.h>
+#include "caja-icon-canvas-item.h"
+
+#include <glib/gi18n.h>
+
+#include "caja-file-utilities.h"
+#include "caja-global-preferences.h"
+#include "caja-icon-private.h"
+#include <eel/eel-art-extensions.h>
+#include <eel/eel-gdk-extensions.h>
+#include <eel/eel-gdk-pixbuf-extensions.h>
+#include <eel/eel-glib-extensions.h>
+#include <eel/eel-mate-extensions.h>
+#include <eel/eel-graphic-effects.h>
+#include <eel/eel-gtk-macros.h>
+#include <eel/eel-pango-extensions.h>
+#include <eel/eel-string.h>
+#include <eel/eel-accessibility.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gtk/gtk.h>
+#include <gdk/gdk.h>
+#include <glib/gi18n.h>
+#include <eel/eel-canvas-util.h>
+#include <atk/atkimage.h>
+#include <atk/atkcomponent.h>
+#include <atk/atknoopobject.h>
+#include <stdio.h>
+#include <string.h>
+
+#define EMBLEM_SPACING 2
+
+/* gap between bottom of icon and start of text box */
+#define LABEL_OFFSET 1
+#define LABEL_LINE_SPACING 0
+
+#define MAX_TEXT_WIDTH_STANDARD 135
+#define MAX_TEXT_WIDTH_TIGHTER 80
+#define MAX_TEXT_WIDTH_BESIDE 90
+#define MAX_TEXT_WIDTH_BESIDE_TOP_TO_BOTTOM 150
+
+/* special text height handling
+ * each item has three text height variables:
+ * + text_height: actual height of the displayed (i.e. on-screen) PangoLayout.
+ * + text_height_for_layout: height used in icon grid layout algorithms.
+ * “sane amount” of text.
+ * “sane amount“ as of
+ * + hard-coded to three lines in text-below-icon mode.
+ * + unlimited in text-besides-icon mode (see VOODOO-TODO)
+ *
+ * This layout height is used by grid layout algorithms, even
+ * though the actually displayed and/or requested text size may be larger
+ * and overlap adjacent icons, if an icon is selected.
+ *
+ * + text_height_for_entire_text: height needed to display the entire PangoLayout,
+ * if it wasn't ellipsized.
+ */
+
+/* Private part of the CajaIconCanvasItem structure. */
+struct CajaIconCanvasItemDetails
+{
+ /* The image, text, font. */
+ double x, y;
+ GdkPixbuf *pixbuf;
+ GdkPixbuf *rendered_pixbuf;
+ GList *emblem_pixbufs;
+ char *editable_text; /* Text that can be modified by a renaming function */
+ char *additional_text; /* Text that cannot be modifed, such as file size, etc. */
+ GdkPoint *attach_points;
+ int n_attach_points;
+
+ /* Size of the text at current font. */
+ int text_dx;
+ int text_width;
+
+ /* actual size required for rendering the text to display */
+ int text_height;
+ /* actual size that would be required for rendering the entire text if it wasn't ellipsized */
+ int text_height_for_entire_text;
+ /* actual size needed for rendering a “sane amount” of text */
+ int text_height_for_layout;
+
+ int editable_text_height;
+
+ /* whether the entire text must always be visible. In that case,
+ * text_height_for_layout will always be equal to text_height.
+ * Used for the last line of a line-wise icon layout. */
+ guint entire_text : 1;
+
+ /* preview state */
+ guint is_active : 1;
+
+ /* Highlight state. */
+ guint is_highlighted_for_selection : 1;
+ guint is_highlighted_as_keyboard_focus: 1;
+ guint is_highlighted_for_drop : 1;
+ guint is_highlighted_for_clipboard : 1;
+ guint show_stretch_handles : 1;
+ guint is_prelit : 1;
+
+ guint rendered_is_active : 1;
+ guint rendered_is_highlighted_for_selection : 1;
+ guint rendered_is_highlighted_for_drop : 1;
+ guint rendered_is_highlighted_for_clipboard : 1;
+ guint rendered_is_prelit : 1;
+ guint rendered_is_focused : 1;
+
+ guint is_renaming : 1;
+
+ guint bounds_cached : 1;
+
+ guint is_visible : 1;
+
+ GdkRectangle embedded_text_rect;
+ char *embedded_text;
+
+ /* Cached PangoLayouts. Only used if the icon is visible */
+ PangoLayout *editable_text_layout;
+ PangoLayout *additional_text_layout;
+ PangoLayout *embedded_text_layout;
+
+ /* Cached rectangle in canvas coordinates */
+ EelIRect canvas_rect;
+ EelIRect text_rect;
+ EelIRect emblem_rect;
+
+ EelIRect bounds_cache;
+ EelIRect bounds_cache_for_layout;
+ EelIRect bounds_cache_for_entire_item;
+
+ /* Accessibility bits */
+ GailTextUtil *text_util;
+};
+
+/* Object argument IDs. */
+enum
+{
+ PROP_0,
+ PROP_EDITABLE_TEXT,
+ PROP_ADDITIONAL_TEXT,
+ PROP_HIGHLIGHTED_FOR_SELECTION,
+ PROP_HIGHLIGHTED_AS_KEYBOARD_FOCUS,
+ PROP_HIGHLIGHTED_FOR_DROP,
+ PROP_HIGHLIGHTED_FOR_CLIPBOARD
+};
+
+typedef enum
+{
+ RIGHT_SIDE,
+ BOTTOM_SIDE,
+ LEFT_SIDE,
+ TOP_SIDE
+} RectangleSide;
+
+enum
+{
+ ACTION_OPEN,
+ ACTION_MENU,
+ LAST_ACTION
+};
+
+typedef struct
+{
+ char *action_descriptions[LAST_ACTION];
+ char *image_description;
+ char *description;
+} CajaIconCanvasItemAccessiblePrivate;
+
+typedef struct
+{
+ CajaIconCanvasItem *item;
+ gint action_number;
+} CajaIconCanvasItemAccessibleActionContext;
+
+typedef struct
+{
+ CajaIconCanvasItem *icon_item;
+ EelIRect icon_rect;
+ RectangleSide side;
+ int position;
+ int index;
+ GList *emblem;
+} EmblemLayout;
+
+static int click_policy_auto_value;
+
+static void caja_icon_canvas_item_text_interface_init (EelAccessibleTextIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (CajaIconCanvasItem, caja_icon_canvas_item, EEL_TYPE_CANVAS_ITEM,
+ G_IMPLEMENT_INTERFACE (EEL_TYPE_ACCESSIBLE_TEXT,
+ caja_icon_canvas_item_text_interface_init));
+
+/* private */
+static void draw_label_text (CajaIconCanvasItem *item,
+ GdkDrawable *drawable,
+ gboolean create_mask,
+ EelIRect icon_rect);
+static void measure_label_text (CajaIconCanvasItem *item);
+static void get_icon_canvas_rectangle (CajaIconCanvasItem *item,
+ EelIRect *rect);
+static void emblem_layout_reset (EmblemLayout *layout,
+ CajaIconCanvasItem *icon_item,
+ EelIRect icon_rect,
+ gboolean is_rtl);
+static gboolean emblem_layout_next (EmblemLayout *layout,
+ GdkPixbuf **emblem_pixbuf,
+ EelIRect *emblem_rect,
+ gboolean is_rtl);
+static void draw_pixbuf (GdkPixbuf *pixbuf,
+ GdkDrawable *drawable,
+ int x,
+ int y);
+static PangoLayout *get_label_layout (PangoLayout **layout,
+ CajaIconCanvasItem *item,
+ const char *text);
+static void draw_label_layout (CajaIconCanvasItem *item,
+ GdkDrawable *drawable,
+ PangoLayout *layout,
+ gboolean highlight,
+ GdkColor *label_color,
+ int x,
+ int y,
+ GdkGC *gc);
+static gboolean hit_test_stretch_handle (CajaIconCanvasItem *item,
+ EelIRect canvas_rect,
+ GtkCornerType *corner);
+static void draw_embedded_text (CajaIconCanvasItem *icon_item,
+ GdkDrawable *drawable,
+ int x,
+ int y);
+
+static void caja_icon_canvas_item_ensure_bounds_up_to_date (CajaIconCanvasItem *icon_item);
+
+
+static gpointer accessible_parent_class = NULL;
+
+static GQuark accessible_private_data_quark = 0;
+
+static const char *caja_icon_canvas_item_accessible_action_names[] =
+{
+ "open",
+ "menu",
+ NULL
+};
+
+static const char *caja_icon_canvas_item_accessible_action_descriptions[] =
+{
+ "Open item",
+ "Popup context menu",
+ NULL
+};
+
+
+/* Object initialization function for the icon item. */
+static void
+caja_icon_canvas_item_init (CajaIconCanvasItem *icon_item)
+{
+ static gboolean setup_auto_enums = FALSE;
+
+ if (!setup_auto_enums)
+ {
+ eel_preferences_add_auto_enum
+ (CAJA_PREFERENCES_CLICK_POLICY,
+ &click_policy_auto_value);
+ setup_auto_enums = TRUE;
+ }
+
+ icon_item->details = G_TYPE_INSTANCE_GET_PRIVATE ((icon_item), CAJA_TYPE_ICON_CANVAS_ITEM, CajaIconCanvasItemDetails);
+ caja_icon_canvas_item_invalidate_label_size (icon_item);
+}
+
+static void
+caja_icon_canvas_item_finalize (GObject *object)
+{
+ CajaIconCanvasItemDetails *details;
+
+ g_assert (CAJA_IS_ICON_CANVAS_ITEM (object));
+
+ details = CAJA_ICON_CANVAS_ITEM (object)->details;
+
+ if (details->pixbuf != NULL)
+ {
+ g_object_unref (details->pixbuf);
+ }
+
+ if (details->text_util != NULL)
+ {
+ g_object_unref (details->text_util);
+ }
+
+ eel_gdk_pixbuf_list_free (details->emblem_pixbufs);
+ g_free (details->editable_text);
+ g_free (details->additional_text);
+ g_free (details->attach_points);
+
+ if (details->rendered_pixbuf != NULL)
+ {
+ g_object_unref (details->rendered_pixbuf);
+ }
+
+ if (details->editable_text_layout != NULL)
+ {
+ g_object_unref (details->editable_text_layout);
+ }
+
+ if (details->additional_text_layout != NULL)
+ {
+ g_object_unref (details->additional_text_layout);
+ }
+
+ if (details->embedded_text_layout != NULL)
+ {
+ g_object_unref (details->embedded_text_layout);
+ }
+
+ g_free (details->embedded_text);
+
+ G_OBJECT_CLASS (caja_icon_canvas_item_parent_class)->finalize (object);
+}
+
+/* Currently we require pixbufs in this format (for hit testing).
+ * Perhaps gdk-pixbuf will be changed so it can do the hit testing
+ * and we won't have this requirement any more.
+ */
+static gboolean
+pixbuf_is_acceptable (GdkPixbuf *pixbuf)
+{
+ return gdk_pixbuf_get_colorspace (pixbuf) == GDK_COLORSPACE_RGB
+ && ((!gdk_pixbuf_get_has_alpha (pixbuf)
+ && gdk_pixbuf_get_n_channels (pixbuf) == 3)
+ || (gdk_pixbuf_get_has_alpha (pixbuf)
+ && gdk_pixbuf_get_n_channels (pixbuf) == 4))
+ && gdk_pixbuf_get_bits_per_sample (pixbuf) == 8;
+}
+
+static void
+caja_icon_canvas_item_invalidate_bounds_cache (CajaIconCanvasItem *item)
+{
+ item->details->bounds_cached = FALSE;
+}
+
+/* invalidate the text width and height cached in the item details. */
+void
+caja_icon_canvas_item_invalidate_label_size (CajaIconCanvasItem *item)
+{
+ if (item->details->editable_text_layout != NULL)
+ {
+ pango_layout_context_changed (item->details->editable_text_layout);
+ }
+ if (item->details->additional_text_layout != NULL)
+ {
+ pango_layout_context_changed (item->details->additional_text_layout);
+ }
+ if (item->details->embedded_text_layout != NULL)
+ {
+ pango_layout_context_changed (item->details->embedded_text_layout);
+ }
+ caja_icon_canvas_item_invalidate_bounds_cache (item);
+ item->details->text_width = -1;
+ item->details->text_height = -1;
+ item->details->text_height_for_layout = -1;
+ item->details->text_height_for_entire_text = -1;
+ item->details->editable_text_height = -1;
+}
+
+/* Set property handler for the icon item. */
+static void
+caja_icon_canvas_item_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ CajaIconCanvasItem *item;
+ CajaIconCanvasItemDetails *details;
+
+ item = CAJA_ICON_CANVAS_ITEM (object);
+ details = item->details;
+
+ switch (property_id)
+ {
+
+ case PROP_EDITABLE_TEXT:
+ if (eel_strcmp (details->editable_text,
+ g_value_get_string (value)) == 0)
+ {
+ return;
+ }
+
+ g_free (details->editable_text);
+ details->editable_text = g_strdup (g_value_get_string (value));
+ if (details->text_util)
+ {
+ AtkObject *accessible;
+
+ gail_text_util_text_setup (details->text_util,
+ details->editable_text);
+ accessible = eel_accessibility_get_atk_object (item);
+ g_object_notify (G_OBJECT(accessible), "accessible-name");
+ }
+
+ caja_icon_canvas_item_invalidate_label_size (item);
+ if (details->editable_text_layout)
+ {
+ g_object_unref (details->editable_text_layout);
+ details->editable_text_layout = NULL;
+ }
+ break;
+
+ case PROP_ADDITIONAL_TEXT:
+ if (eel_strcmp (details->additional_text,
+ g_value_get_string (value)) == 0)
+ {
+ return;
+ }
+
+ g_free (details->additional_text);
+ details->additional_text = g_strdup (g_value_get_string (value));
+
+ caja_icon_canvas_item_invalidate_label_size (item);
+ if (details->additional_text_layout)
+ {
+ g_object_unref (details->additional_text_layout);
+ details->additional_text_layout = NULL;
+ }
+ break;
+
+ case PROP_HIGHLIGHTED_FOR_SELECTION:
+ if (!details->is_highlighted_for_selection == !g_value_get_boolean (value))
+ {
+ return;
+ }
+ details->is_highlighted_for_selection = g_value_get_boolean (value);
+ caja_icon_canvas_item_invalidate_label_size (item);
+ break;
+
+ case PROP_HIGHLIGHTED_AS_KEYBOARD_FOCUS:
+ if (!details->is_highlighted_as_keyboard_focus == !g_value_get_boolean (value))
+ {
+ return;
+ }
+ details->is_highlighted_as_keyboard_focus = g_value_get_boolean (value);
+
+ if (details->is_highlighted_as_keyboard_focus)
+ {
+ AtkObject *atk_object = eel_accessibility_for_object (object);
+ atk_focus_tracker_notify (atk_object);
+ }
+ break;
+
+ case PROP_HIGHLIGHTED_FOR_DROP:
+ if (!details->is_highlighted_for_drop == !g_value_get_boolean (value))
+ {
+ return;
+ }
+ details->is_highlighted_for_drop = g_value_get_boolean (value);
+ break;
+
+ case PROP_HIGHLIGHTED_FOR_CLIPBOARD:
+ if (!details->is_highlighted_for_clipboard == !g_value_get_boolean (value))
+ {
+ return;
+ }
+ details->is_highlighted_for_clipboard = g_value_get_boolean (value);
+ break;
+
+ default:
+ g_warning ("caja_icons_view_item_item_set_arg on unknown argument");
+ return;
+ }
+
+ eel_canvas_item_request_update (EEL_CANVAS_ITEM (object));
+}
+
+/* Get property handler for the icon item */
+static void
+caja_icon_canvas_item_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ CajaIconCanvasItemDetails *details;
+
+ details = CAJA_ICON_CANVAS_ITEM (object)->details;
+
+ switch (property_id)
+ {
+
+ case PROP_EDITABLE_TEXT:
+ g_value_set_string (value, details->editable_text);
+ break;
+
+ case PROP_ADDITIONAL_TEXT:
+ g_value_set_string (value, details->additional_text);
+ break;
+
+ case PROP_HIGHLIGHTED_FOR_SELECTION:
+ g_value_set_boolean (value, details->is_highlighted_for_selection);
+ break;
+
+ case PROP_HIGHLIGHTED_AS_KEYBOARD_FOCUS:
+ g_value_set_boolean (value, details->is_highlighted_as_keyboard_focus);
+ break;
+
+ case PROP_HIGHLIGHTED_FOR_DROP:
+ g_value_set_boolean (value, details->is_highlighted_for_drop);
+ break;
+
+ case PROP_HIGHLIGHTED_FOR_CLIPBOARD:
+ g_value_set_boolean (value, details->is_highlighted_for_clipboard);
+ break;
+
+ default:
+ g_warning ("invalid property %d", property_id);
+ break;
+ }
+}
+
+GdkPixmap *
+caja_icon_canvas_item_get_image (CajaIconCanvasItem *item,
+ GdkBitmap **mask,
+ GdkColormap *colormap)
+{
+ GdkPixmap *pixmap;
+ EelCanvas *canvas;
+ GdkScreen *screen;
+ GdkGC *gc;
+ int width, height;
+ int item_offset_x, item_offset_y;
+ EelIRect icon_rect;
+ EelIRect emblem_rect;
+ GdkPixbuf *pixbuf;
+ GdkPixbuf *emblem_pixbuf;
+ EmblemLayout emblem_layout;
+ double item_x, item_y;
+ gboolean is_rtl;
+ cairo_t *cr;
+
+ g_return_val_if_fail (CAJA_IS_ICON_CANVAS_ITEM (item), NULL);
+
+ canvas = EEL_CANVAS_ITEM (item)->canvas;
+ screen = gdk_colormap_get_screen (colormap);
+
+ /* Assume we're updated so canvas item data is right */
+
+ /* Calculate the offset from the top-left corner of the
+ new image to the item position (where the pixmap is placed) */
+ eel_canvas_world_to_window (canvas,
+ item->details->x, item->details->y,
+ &item_x, &item_y);
+
+ item_offset_x = item_x - EEL_CANVAS_ITEM (item)->x1;
+ item_offset_y = item_y - EEL_CANVAS_ITEM (item)->y1;
+
+ /* Calculate the width of the item */
+ width = EEL_CANVAS_ITEM (item)->x2 - EEL_CANVAS_ITEM (item)->x1;
+ height = EEL_CANVAS_ITEM (item)->y2 - EEL_CANVAS_ITEM (item)->y1;
+
+ pixmap = gdk_pixmap_new (gdk_screen_get_root_window (screen),
+ width, height,
+ gdk_visual_get_depth (gdk_colormap_get_visual (colormap)));
+ gdk_drawable_set_colormap (GDK_DRAWABLE (pixmap), colormap);
+
+ pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
+ TRUE,
+ gdk_pixbuf_get_bits_per_sample (item->details->pixbuf),
+ width, height);
+ gdk_pixbuf_fill (pixbuf, 0x00000000);
+
+ gdk_pixbuf_composite (item->details->pixbuf, pixbuf,
+ item_offset_x, item_offset_y,
+ gdk_pixbuf_get_width (item->details->pixbuf),
+ gdk_pixbuf_get_height (item->details->pixbuf),
+ item_offset_x, item_offset_y, 1.0, 1.0,
+ GDK_INTERP_BILINEAR, 255);
+
+ icon_rect.x0 = item_offset_x;
+ icon_rect.y0 = item_offset_y;
+ icon_rect.x1 = item_offset_x + gdk_pixbuf_get_width (item->details->pixbuf);
+ icon_rect.y1 = item_offset_y + gdk_pixbuf_get_height (item->details->pixbuf);
+
+
+ is_rtl = caja_icon_container_is_layout_rtl (CAJA_ICON_CONTAINER (canvas));
+
+ emblem_layout_reset (&emblem_layout, item, icon_rect, is_rtl);
+ while (emblem_layout_next (&emblem_layout, &emblem_pixbuf, &emblem_rect, is_rtl))
+ {
+ gdk_pixbuf_composite (emblem_pixbuf, pixbuf,
+ emblem_rect.x0, emblem_rect.y0,
+ gdk_pixbuf_get_width (emblem_pixbuf),
+ gdk_pixbuf_get_height (emblem_pixbuf),
+ emblem_rect.x0, emblem_rect.y0,
+ 1.0, 1.0,
+ GDK_INTERP_BILINEAR, 255);
+ }
+
+ /* clear the pixmap */
+ cr = gdk_cairo_create (pixmap);
+ cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
+ cairo_paint (cr);
+ cairo_destroy (cr);
+
+ gc = gdk_gc_new (pixmap);
+ gdk_draw_pixbuf (pixmap, gc, pixbuf,
+ 0, 0, 0, 0,
+ gdk_pixbuf_get_width (pixbuf), gdk_pixbuf_get_height (pixbuf),
+ GDK_RGB_DITHER_NORMAL,
+ 0, 0);
+ g_object_unref (gc);
+
+ *mask = gdk_pixmap_new (gdk_screen_get_root_window (screen),
+ width, height,
+ 1);
+ gc = gdk_gc_new (*mask);
+ gdk_draw_rectangle (*mask, gc,
+ TRUE,
+ 0, 0,
+ width, height);
+ g_object_unref (gc);
+
+ gdk_pixbuf_render_threshold_alpha (pixbuf, *mask,
+ 0, 0, 0, 0,
+ gdk_pixbuf_get_width (pixbuf), gdk_pixbuf_get_height (pixbuf),
+ 128);
+
+ draw_embedded_text (item, GDK_DRAWABLE (pixmap),
+ item_offset_x, item_offset_y);
+
+ draw_label_text (item, GDK_DRAWABLE (pixmap), FALSE, icon_rect);
+ draw_label_text (item, GDK_DRAWABLE (*mask), TRUE, icon_rect);
+
+ g_object_unref (pixbuf);
+
+ return pixmap;
+}
+
+void
+caja_icon_canvas_item_set_image (CajaIconCanvasItem *item,
+ GdkPixbuf *image)
+{
+ CajaIconCanvasItemDetails *details;
+
+ g_return_if_fail (CAJA_IS_ICON_CANVAS_ITEM (item));
+ g_return_if_fail (image == NULL || pixbuf_is_acceptable (image));
+
+ details = item->details;
+ if (details->pixbuf == image)
+ {
+ return;
+ }
+
+ if (image != NULL)
+ {
+ g_object_ref (image);
+ }
+ if (details->pixbuf != NULL)
+ {
+ g_object_unref (details->pixbuf);
+ }
+ if (details->rendered_pixbuf != NULL)
+ {
+ g_object_unref (details->rendered_pixbuf);
+ details->rendered_pixbuf = NULL;
+ }
+
+ details->pixbuf = image;
+
+ caja_icon_canvas_item_invalidate_bounds_cache (item);
+ eel_canvas_item_request_update (EEL_CANVAS_ITEM (item));
+}
+
+void
+caja_icon_canvas_item_set_emblems (CajaIconCanvasItem *item,
+ GList *emblem_pixbufs)
+{
+ GList *p;
+
+ g_return_if_fail (CAJA_IS_ICON_CANVAS_ITEM (item));
+
+ g_assert (item->details->emblem_pixbufs != emblem_pixbufs || emblem_pixbufs == NULL);
+
+ /* The case where the emblems are identical is fairly common,
+ * so lets take the time to check for it.
+ */
+ if (eel_g_list_equal (item->details->emblem_pixbufs, emblem_pixbufs))
+ {
+ return;
+ }
+
+ /* Check if they are acceptable. */
+ for (p = emblem_pixbufs; p != NULL; p = p->next)
+ {
+ g_return_if_fail (pixbuf_is_acceptable (p->data));
+ }
+
+ /* Take in the new list of emblems. */
+ eel_gdk_pixbuf_list_ref (emblem_pixbufs);
+ eel_gdk_pixbuf_list_free (item->details->emblem_pixbufs);
+ item->details->emblem_pixbufs = g_list_copy (emblem_pixbufs);
+
+ caja_icon_canvas_item_invalidate_bounds_cache (item);
+ eel_canvas_item_request_update (EEL_CANVAS_ITEM (item));
+}
+
+void
+caja_icon_canvas_item_set_attach_points (CajaIconCanvasItem *item,
+ GdkPoint *attach_points,
+ int n_attach_points)
+{
+ g_free (item->details->attach_points);
+ item->details->attach_points = NULL;
+ item->details->n_attach_points = 0;
+
+ if (attach_points != NULL && n_attach_points != 0)
+ {
+ item->details->attach_points = g_memdup (attach_points, n_attach_points * sizeof (GdkPoint));
+ item->details->n_attach_points = n_attach_points;
+ }
+
+ caja_icon_canvas_item_invalidate_bounds_cache (item);
+}
+
+void
+caja_icon_canvas_item_set_embedded_text_rect (CajaIconCanvasItem *item,
+ const GdkRectangle *text_rect)
+{
+ item->details->embedded_text_rect = *text_rect;
+
+ caja_icon_canvas_item_invalidate_bounds_cache (item);
+ eel_canvas_item_request_update (EEL_CANVAS_ITEM (item));
+}
+
+void
+caja_icon_canvas_item_set_embedded_text (CajaIconCanvasItem *item,
+ const char *text)
+{
+ g_free (item->details->embedded_text);
+ item->details->embedded_text = g_strdup (text);
+
+ if (item->details->embedded_text_layout != NULL)
+ {
+ if (text != NULL)
+ {
+ pango_layout_set_text (item->details->embedded_text_layout, text, -1);
+ }
+ else
+ {
+ pango_layout_set_text (item->details->embedded_text_layout, "", -1);
+ }
+ }
+
+ eel_canvas_item_request_update (EEL_CANVAS_ITEM (item));
+}
+
+
+/* Recomputes the bounding box of a icon canvas item.
+ * This is a generic implementation that could be used for any canvas item
+ * class, it has no assumptions about how the item is used.
+ */
+static void
+recompute_bounding_box (CajaIconCanvasItem *icon_item,
+ double i2w_dx, double i2w_dy)
+{
+ /* The bounds stored in the item is the same as what get_bounds
+ * returns, except it's in canvas coordinates instead of the item's
+ * parent's coordinates.
+ */
+
+ EelCanvasItem *item;
+ EelDPoint top_left, bottom_right;
+
+ item = EEL_CANVAS_ITEM (icon_item);
+
+ eel_canvas_item_get_bounds (item,
+ &top_left.x, &top_left.y,
+ &bottom_right.x, &bottom_right.y);
+
+ top_left.x += i2w_dx;
+ top_left.y += i2w_dy;
+ bottom_right.x += i2w_dx;
+ bottom_right.y += i2w_dy;
+ eel_canvas_w2c_d (item->canvas,
+ top_left.x, top_left.y,
+ &item->x1, &item->y1);
+ eel_canvas_w2c_d (item->canvas,
+ bottom_right.x, bottom_right.y,
+ &item->x2, &item->y2);
+}
+
+static EelIRect
+compute_text_rectangle (const CajaIconCanvasItem *item,
+ EelIRect icon_rectangle,
+ gboolean canvas_coords,
+ CajaIconCanvasItemBoundsUsage usage)
+{
+ EelIRect text_rectangle;
+ double pixels_per_unit;
+ double text_width, text_height, text_height_for_layout, text_height_for_entire_text, real_text_height, text_dx;
+
+ pixels_per_unit = EEL_CANVAS_ITEM (item)->canvas->pixels_per_unit;
+ if (canvas_coords)
+ {
+ text_width = item->details->text_width;
+ text_height = item->details->text_height;
+ text_height_for_layout = item->details->text_height_for_layout;
+ text_height_for_entire_text = item->details->text_height_for_entire_text;
+ text_dx = item->details->text_dx;
+ }
+ else
+ {
+ text_width = item->details->text_width / pixels_per_unit;
+ text_height = item->details->text_height / pixels_per_unit;
+ text_height_for_layout = item->details->text_height_for_layout / pixels_per_unit;
+ text_height_for_entire_text = item->details->text_height_for_entire_text / pixels_per_unit;
+ text_dx = item->details->text_dx / pixels_per_unit;
+ }
+
+ if (CAJA_ICON_CONTAINER (EEL_CANVAS_ITEM (item)->canvas)->details->label_position == CAJA_ICON_LABEL_POSITION_BESIDE)
+ {
+ if (!caja_icon_container_is_layout_rtl (CAJA_ICON_CONTAINER (EEL_CANVAS_ITEM (item)->canvas)))
+ {
+ text_rectangle.x0 = icon_rectangle.x1;
+ text_rectangle.x1 = text_rectangle.x0 + text_dx + text_width;
+ }
+ else
+ {
+ text_rectangle.x1 = icon_rectangle.x0;
+ text_rectangle.x0 = text_rectangle.x1 - text_dx - text_width;
+ }
+
+ /* VOODOO-TODO */
+#if 0
+ if (for_layout)
+ {
+ /* in this case, we should be more smart and calculate the size according to the maximum
+ * number of lines fitting next to the icon. However, this requires a more complex layout logic.
+ * It would mean that when measuring the label, the icon dimensions must be known already,
+ * and we
+ * 1. start with an unlimited layout
+ * 2. measure how many lines of this layout fit next to the icon
+ * 3. limit the number of lines to the given number of fitting lines
+ */
+ real_text_height = VOODOO();
+ }
+ else
+ {
+#endif
+ real_text_height = text_height_for_entire_text;
+#if 0
+ }
+#endif
+
+ text_rectangle.y0 = (icon_rectangle.y0 + icon_rectangle.y1) / 2- (int) real_text_height / 2;
+ text_rectangle.y1 = text_rectangle.y0 + real_text_height;
+ }
+ else
+ {
+ text_rectangle.x0 = (icon_rectangle.x0 + icon_rectangle.x1) / 2 - (int) text_width / 2;
+ text_rectangle.y0 = icon_rectangle.y1;
+ text_rectangle.x1 = text_rectangle.x0 + text_width;
+
+ if (usage == BOUNDS_USAGE_FOR_LAYOUT)
+ {
+ real_text_height = text_height_for_layout;
+ }
+ else if (usage == BOUNDS_USAGE_FOR_ENTIRE_ITEM)
+ {
+ real_text_height = text_height_for_entire_text;
+ }
+ else if (usage == BOUNDS_USAGE_FOR_DISPLAY)
+ {
+ real_text_height = text_height;
+ }
+ else
+ {
+ g_assert_not_reached ();
+ }
+
+ text_rectangle.y1 = text_rectangle.y0 + real_text_height + LABEL_OFFSET / pixels_per_unit;
+ }
+
+ return text_rectangle;
+}
+
+static EelIRect
+get_current_canvas_bounds (EelCanvasItem *item)
+{
+ EelIRect bounds;
+
+ g_assert (EEL_IS_CANVAS_ITEM (item));
+
+ bounds.x0 = item->x1;
+ bounds.y0 = item->y1;
+ bounds.x1 = item->x2;
+ bounds.y1 = item->y2;
+
+ return bounds;
+}
+
+void
+caja_icon_canvas_item_update_bounds (CajaIconCanvasItem *item,
+ double i2w_dx, double i2w_dy)
+{
+ EelIRect before, after, emblem_rect;
+ EmblemLayout emblem_layout;
+ EelCanvasItem *canvas_item;
+ GdkPixbuf *emblem_pixbuf;
+ gboolean is_rtl;
+
+ canvas_item = EEL_CANVAS_ITEM (item);
+
+ /* Compute new bounds. */
+ before = get_current_canvas_bounds (canvas_item);
+ recompute_bounding_box (item, i2w_dx, i2w_dy);
+ after = get_current_canvas_bounds (canvas_item);
+
+ /* If the bounds didn't change, we are done. */
+ if (eel_irect_equal (before, after))
+ {
+ return;
+ }
+
+ is_rtl = caja_icon_container_is_layout_rtl (CAJA_ICON_CONTAINER (canvas_item->canvas));
+
+ /* Update canvas and text rect cache */
+ get_icon_canvas_rectangle (item, &item->details->canvas_rect);
+ item->details->text_rect = compute_text_rectangle (item, item->details->canvas_rect,
+ TRUE, BOUNDS_USAGE_FOR_DISPLAY);
+
+ /* Update emblem rect cache */
+ item->details->emblem_rect.x0 = 0;
+ item->details->emblem_rect.x1 = 0;
+ item->details->emblem_rect.y0 = 0;
+ item->details->emblem_rect.y1 = 0;
+ emblem_layout_reset (&emblem_layout, item, item->details->canvas_rect, is_rtl);
+ while (emblem_layout_next (&emblem_layout, &emblem_pixbuf, &emblem_rect, is_rtl))
+ {
+ eel_irect_union (&item->details->emblem_rect, &item->details->emblem_rect, &emblem_rect);
+ }
+
+ /* queue a redraw. */
+ eel_canvas_request_redraw (canvas_item->canvas,
+ before.x0, before.y0,
+ before.x1 + 1, before.y1 + 1);
+}
+
+/* Update handler for the icon canvas item. */
+static void
+caja_icon_canvas_item_update (EelCanvasItem *item,
+ double i2w_dx, double i2w_dy,
+ gint flags)
+{
+ caja_icon_canvas_item_update_bounds (CAJA_ICON_CANVAS_ITEM (item), i2w_dx, i2w_dy);
+
+ eel_canvas_item_request_redraw (EEL_CANVAS_ITEM (item));
+
+ EEL_CANVAS_ITEM_CLASS (caja_icon_canvas_item_parent_class)->update (item, i2w_dx, i2w_dy, flags);
+}
+
+/* Rendering */
+static gboolean
+in_single_click_mode (void)
+{
+ return click_policy_auto_value == CAJA_CLICK_POLICY_SINGLE;
+}
+
+
+/* Utility routine to create a rectangle with rounded corners.
+ * This could possibly move to Eel as a general purpose routine.
+ */
+static void
+make_round_rect (cairo_t *cr,
+ double x,
+ double y,
+ double width,
+ double height,
+ double radius)
+{
+ double cx, cy;
+
+ width -= 2 * radius;
+ height -= 2 * radius;
+
+ cairo_move_to (cr, x + radius, y);
+
+ cairo_rel_line_to (cr, width, 0.0);
+
+ cairo_get_current_point (cr, &cx, &cy);
+ cairo_arc (cr, cx, cy + radius, radius, 3.0 * G_PI_2, 0);
+
+ cairo_rel_line_to (cr, 0.0, height);
+
+ cairo_get_current_point (cr, &cx, &cy);
+ cairo_arc (cr, cx - radius, cy, radius, 0, G_PI_2);
+
+ cairo_rel_line_to (cr, - width, 0.0);
+
+ cairo_get_current_point (cr, &cx, &cy);
+ cairo_arc (cr, cx, cy - radius, radius, G_PI_2, G_PI);
+
+ cairo_rel_line_to (cr, 0.0, -height);
+
+ cairo_get_current_point (cr, &cx, &cy);
+ cairo_arc (cr, cx + radius, cy, radius, G_PI, 3.0 * G_PI_2);
+
+ cairo_close_path (cr);
+}
+
+static void
+draw_frame (CajaIconCanvasItem *item,
+ GdkDrawable *drawable,
+ guint color,
+ gboolean create_mask,
+ int x,
+ int y,
+ int width,
+ int height)
+{
+ CajaIconContainer *container;
+ cairo_t *cr;
+
+ container = CAJA_ICON_CONTAINER (EEL_CANVAS_ITEM (item)->canvas);
+
+ /* Get a cairo context */
+ cr = gdk_cairo_create (drawable);
+
+ /* Set the rounded rect clip region. Magic rounding value taken
+ * from old code.
+ */
+ make_round_rect (cr, x, y, width, height, 5);
+
+ if (create_mask)
+ {
+ /* Dunno how to do this with cairo...
+ * It used to threshold the rendering so that the
+ * bitmask didn't show white where alpha < 0.5
+ */
+ }
+
+ cairo_set_source_rgba (cr,
+ EEL_RGBA_COLOR_GET_R (color) / 255.0,
+ EEL_RGBA_COLOR_GET_G (color) / 255.0,
+ EEL_RGBA_COLOR_GET_B (color) / 255.0,
+ EEL_RGBA_COLOR_GET_A (color) / 255.0);
+
+ /* Paint into drawable now that we have set up the color and opacity */
+ cairo_fill (cr);
+
+ /* Clean up now that drawing is complete */
+ cairo_destroy (cr);
+}
+
+/* Keep these for a bit while we work on performance of draw_or_measure_label_text. */
+/*
+ #define PERFORMANCE_TEST_DRAW_DISABLE
+ #define PERFORMANCE_TEST_MEASURE_DISABLE
+*/
+
+/* This gets the size of the layout from the position of the layout.
+ * This means that if the layout is right aligned we get the full width
+ * of the layout, not just the width of the text snippet on the right side
+ */
+static void
+layout_get_full_size (PangoLayout *layout,
+ int *width,
+ int *height,
+ int *dx)
+{
+ PangoRectangle logical_rect;
+ int the_width, total_width;
+
+ pango_layout_get_extents (layout, NULL, &logical_rect);
+ the_width = (logical_rect.width + PANGO_SCALE / 2) / PANGO_SCALE;
+ total_width = (logical_rect.x + logical_rect.width + PANGO_SCALE / 2) / PANGO_SCALE;
+
+ if (width != NULL)
+ {
+ *width = the_width;
+ }
+
+ if (height != NULL)
+ {
+ *height = (logical_rect.height + PANGO_SCALE / 2) / PANGO_SCALE;
+ }
+
+ if (dx != NULL)
+ {
+ *dx = total_width - the_width;
+ }
+}
+
+static void
+layout_get_size_for_layout (PangoLayout *layout,
+ int max_layout_line_count,
+ int height_for_entire_text,
+ int *height_for_layout)
+{
+ PangoLayoutIter *iter;
+ PangoRectangle logical_rect;
+ int i;
+
+ /* only use the first max_layout_line_count lines for the gridded auto layout */
+ if (pango_layout_get_line_count (layout) <= max_layout_line_count)
+ {
+ *height_for_layout = height_for_entire_text;
+ }
+ else
+ {
+ *height_for_layout = 0;
+ iter = pango_layout_get_iter (layout);
+ /* VOODOO-TODO, determine number of lines based on the icon size for text besides icon.
+ * cf. compute_text_rectangle() */
+ for (i = 0; i < max_layout_line_count; i++)
+ {
+ pango_layout_iter_get_line_extents (iter, NULL, &logical_rect);
+ *height_for_layout += (logical_rect.height + PANGO_SCALE / 2) / PANGO_SCALE;
+
+ if (!pango_layout_iter_next_line (iter))
+ {
+ break;
+ }
+
+ *height_for_layout += pango_layout_get_spacing (layout);
+ }
+ pango_layout_iter_free (iter);
+ }
+}
+
+#define IS_COMPACT_VIEW(container) \
+ ((container->details->layout_mode == CAJA_ICON_LAYOUT_T_B_L_R || \
+ container->details->layout_mode == CAJA_ICON_LAYOUT_T_B_R_L) && \
+ container->details->label_position == CAJA_ICON_LABEL_POSITION_BESIDE)
+
+#define TEXT_BACK_PADDING_X 4
+#define TEXT_BACK_PADDING_Y 1
+
+static void
+prepare_pango_layout_width (CajaIconCanvasItem *item,
+ PangoLayout *layout)
+{
+ if (caja_icon_canvas_item_get_max_text_width (item) < 0)
+ {
+ pango_layout_set_width (layout, -1);
+ }
+ else
+ {
+ pango_layout_set_width (layout, floor (caja_icon_canvas_item_get_max_text_width (item)) * PANGO_SCALE);
+ pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_END);
+ }
+}
+
+static void
+prepare_pango_layout_for_measure_entire_text (CajaIconCanvasItem *item,
+ PangoLayout *layout)
+{
+ CajaIconContainer *container;
+
+ prepare_pango_layout_width (item, layout);
+
+ container = CAJA_ICON_CONTAINER (EEL_CANVAS_ITEM (item)->canvas);
+
+ if (IS_COMPACT_VIEW (container))
+ {
+ pango_layout_set_height (layout, -1);
+ }
+ else
+ {
+ pango_layout_set_height (layout, G_MININT);
+ }
+}
+
+static void
+prepare_pango_layout_for_draw (CajaIconCanvasItem *item,
+ PangoLayout *layout)
+{
+ CajaIconCanvasItemDetails *details;
+ CajaIconContainer *container;
+ gboolean needs_highlight;
+
+ prepare_pango_layout_width (item, layout);
+
+ container = CAJA_ICON_CONTAINER (EEL_CANVAS_ITEM (item)->canvas);
+ details = item->details;
+
+ needs_highlight = details->is_highlighted_for_selection || details->is_highlighted_for_drop;
+
+ if (IS_COMPACT_VIEW (container))
+ {
+ pango_layout_set_height (layout, -1);
+ }
+ else if (needs_highlight ||
+ details->is_prelit ||
+ details->is_highlighted_as_keyboard_focus ||
+ details->entire_text ||
+ container->details->label_position == CAJA_ICON_LABEL_POSITION_BESIDE)
+ {
+ /* VOODOO-TODO, cf. compute_text_rectangle() */
+ pango_layout_set_height (layout, G_MININT);
+ }
+ else
+ {
+ /* TODO? we might save some resources, when the re-layout is not neccessary in case
+ * the layout height already fits into max. layout lines. But pango should figure this
+ * out itself (which it doesn't ATM).
+ */
+ pango_layout_set_height (layout,
+ caja_icon_container_get_max_layout_lines_for_pango (container));
+ }
+}
+
+static void
+measure_label_text (CajaIconCanvasItem *item)
+{
+ CajaIconCanvasItemDetails *details;
+ CajaIconContainer *container;
+ gint editable_height, editable_height_for_layout, editable_height_for_entire_text, editable_width, editable_dx;
+ gint additional_height, additional_width, additional_dx;
+ EelCanvasItem *canvas_item;
+ PangoLayout *editable_layout;
+ PangoLayout *additional_layout;
+ gboolean have_editable, have_additional, needs_highlight;
+ int max_text_width;
+
+ /* check to see if the cached values are still valid; if so, there's
+ * no work necessary
+ */
+
+ if (item->details->text_width >= 0 && item->details->text_height >= 0)
+ {
+ return;
+ }
+
+ details = item->details;
+ needs_highlight = details->is_highlighted_for_selection || details->is_highlighted_for_drop;
+
+ have_editable = details->editable_text != NULL && details->editable_text[0] != '\0';
+ have_additional = details->additional_text != NULL && details->additional_text[0] != '\0';
+
+ /* No font or no text, then do no work. */
+ if (!have_editable && !have_additional)
+ {
+ details->text_height = 0;
+ details->text_height_for_layout = 0;
+ details->text_height_for_entire_text = 0;
+ details->text_width = 0;
+ return;
+ }
+
+#ifdef PERFORMANCE_TEST_MEASURE_DISABLE
+ /* fake out the width */
+ details->text_width = 80;
+ details->text_height = 20;
+ details->text_height_for_layout = 20;
+ details->text_height_for_entire_text = 20;
+ return;
+#endif
+
+ canvas_item = EEL_CANVAS_ITEM (item);
+
+ editable_width = 0;
+ editable_height = 0;
+ editable_height_for_layout = 0;
+ editable_height_for_entire_text = 0;
+ editable_dx = 0;
+ additional_width = 0;
+ additional_height = 0;
+ additional_dx = 0;
+
+ max_text_width = floor (caja_icon_canvas_item_get_max_text_width (item));
+
+ container = CAJA_ICON_CONTAINER (EEL_CANVAS_ITEM (item)->canvas);
+ editable_layout = NULL;
+ additional_layout = NULL;
+
+ if (have_editable)
+ {
+ /* first, measure required text height: editable_height_for_entire_text
+ * then, measure text height applicable for layout: editable_height_for_layout
+ * next, measure actually displayed height: editable_height
+ */
+ editable_layout = get_label_layout (&details->editable_text_layout, item, details->editable_text);
+
+ prepare_pango_layout_for_measure_entire_text (item, editable_layout);
+ layout_get_full_size (editable_layout,
+ NULL,
+ &editable_height_for_entire_text,
+ NULL);
+ layout_get_size_for_layout (editable_layout,
+ caja_icon_container_get_max_layout_lines (container),
+ editable_height_for_entire_text,
+ &editable_height_for_layout);
+
+ prepare_pango_layout_for_draw (item, editable_layout);
+ layout_get_full_size (editable_layout,
+ &editable_width,
+ &editable_height,
+ &editable_dx);
+ }
+
+ if (have_additional)
+ {
+ additional_layout = get_label_layout (&details->additional_text_layout, item, details->additional_text);
+ prepare_pango_layout_for_draw (item, additional_layout);
+ layout_get_full_size (additional_layout,
+ &additional_width, &additional_height, &additional_dx);
+ }
+
+ details->editable_text_height = editable_height;
+
+ if (editable_width > additional_width)
+ {
+ details->text_width = editable_width;
+ details->text_dx = editable_dx;
+ }
+ else
+ {
+ details->text_width = additional_width;
+ details->text_dx = additional_dx;
+ }
+
+ if (have_additional)
+ {
+ details->text_height = editable_height + LABEL_LINE_SPACING + additional_height;
+ details->text_height_for_layout = editable_height_for_layout + LABEL_LINE_SPACING + additional_height;
+ details->text_height_for_entire_text = editable_height_for_entire_text + LABEL_LINE_SPACING + additional_height;
+ }
+ else
+ {
+ details->text_height = editable_height;
+ details->text_height_for_layout = editable_height_for_layout;
+ details->text_height_for_entire_text = editable_height_for_entire_text;
+ }
+
+ /* add some extra space for highlighting even when we don't highlight so things won't move */
+
+ /* extra slop for nicer highlighting */
+ details->text_height += TEXT_BACK_PADDING_Y*2;
+ details->text_height_for_layout += TEXT_BACK_PADDING_Y*2;
+ details->text_height_for_entire_text += TEXT_BACK_PADDING_Y*2;
+ details->editable_text_height += TEXT_BACK_PADDING_Y*2;
+
+ /* extra to make it look nicer */
+ details->text_width += TEXT_BACK_PADDING_X*2;
+
+ if (editable_layout)
+ {
+ g_object_unref (editable_layout);
+ }
+
+ if (additional_layout)
+ {
+ g_object_unref (additional_layout);
+ }
+}
+
+static void
+draw_label_text (CajaIconCanvasItem *item,
+ GdkDrawable *drawable,
+ gboolean create_mask,
+ EelIRect icon_rect)
+{
+ EelCanvasItem *canvas_item;
+ CajaIconCanvasItemDetails *details;
+ CajaIconContainer *container;
+ PangoLayout *editable_layout;
+ PangoLayout *additional_layout;
+ GdkColor *label_color;
+ GdkGC *gc;
+ gboolean have_editable, have_additional;
+ gboolean needs_frame, needs_highlight, prelight_label, is_rtl_label_beside;
+ EelIRect text_rect;
+ int x;
+ int max_text_width;
+
+#ifdef PERFORMANCE_TEST_DRAW_DISABLE
+ return;
+#endif
+
+ gc = NULL;
+
+ canvas_item = EEL_CANVAS_ITEM (item);
+ details = item->details;
+
+ measure_label_text (item);
+ if (details->text_height == 0 ||
+ details->text_width == 0)
+ {
+ return;
+ }
+
+ container = CAJA_ICON_CONTAINER (EEL_CANVAS_ITEM (item)->canvas);
+
+ text_rect = compute_text_rectangle (item, icon_rect, TRUE, BOUNDS_USAGE_FOR_DISPLAY);
+
+ needs_highlight = details->is_highlighted_for_selection || details->is_highlighted_for_drop;
+ is_rtl_label_beside = caja_icon_container_is_layout_rtl (container) &&
+ container->details->label_position == CAJA_ICON_LABEL_POSITION_BESIDE;
+
+ editable_layout = NULL;
+ additional_layout = NULL;
+
+ have_editable = details->editable_text != NULL && details->editable_text[0] != '\0';
+ have_additional = details->additional_text != NULL && details->additional_text[0] != '\0';
+ g_assert (have_editable || have_additional);
+
+ max_text_width = floor (caja_icon_canvas_item_get_max_text_width (item));
+
+ /* if the icon is highlighted, do some set-up */
+ if (needs_highlight && !details->is_renaming)
+ {
+ draw_frame (item,
+ drawable,
+ gtk_widget_has_focus (GTK_WIDGET (container)) ? container->details->highlight_color_rgba : container->details->active_color_rgba,
+ create_mask,
+ is_rtl_label_beside ? text_rect.x0 + item->details->text_dx : text_rect.x0,
+ text_rect.y0,
+ is_rtl_label_beside ? text_rect.x1 - text_rect.x0 - item->details->text_dx : text_rect.x1 - text_rect.x0,
+ text_rect.y1 - text_rect.y0);
+ }
+ else if (!needs_highlight && !details->is_renaming &&
+ (details->is_prelit ||
+ details->is_highlighted_as_keyboard_focus))
+ {
+ /* clear the underlying icons, where the text or overlaps them. */
+ gdk_window_clear_area (gtk_layout_get_bin_window (&EEL_CANVAS (container)->layout),
+ text_rect.x0,
+ text_rect.y0,
+ text_rect.x1 - text_rect.x0,
+ text_rect.y1 - text_rect.y0);
+ }
+
+ if (container->details->label_position == CAJA_ICON_LABEL_POSITION_BESIDE)
+ {
+ x = text_rect.x0 + 2;
+ }
+ else
+ {
+ x = text_rect.x0 + ((text_rect.x1 - text_rect.x0) - max_text_width) / 2;
+ }
+
+ if (have_editable)
+ {
+ editable_layout = get_label_layout (&item->details->editable_text_layout, item, item->details->editable_text);
+ prepare_pango_layout_for_draw (item, editable_layout);
+
+ gtk_widget_style_get (GTK_WIDGET (container),
+ "frame_text", &needs_frame,
+ "activate_prelight_icon_label", &prelight_label,
+ NULL);
+ if (needs_frame && !needs_highlight && details->text_width > 0 && details->text_height > 0)
+ {
+ if (!(prelight_label && item->details->is_prelit))
+ {
+ draw_frame (item,
+ drawable,
+ container->details->normal_color_rgba,
+ create_mask,
+ text_rect.x0,
+ text_rect.y0,
+ text_rect.x1 - text_rect.x0,
+ text_rect.y1 - text_rect.y0);
+ }
+ else
+ {
+ draw_frame (item,
+ drawable,
+ container->details->prelight_color_rgba,
+ create_mask,
+ text_rect.x0,
+ text_rect.y0,
+ text_rect.x1 - text_rect.x0,
+ text_rect.y1 - text_rect.y0);
+ }
+ }
+
+ gc = caja_icon_container_get_label_color_and_gc
+ (CAJA_ICON_CONTAINER (canvas_item->canvas),
+ &label_color, TRUE, needs_highlight,
+ prelight_label & item->details->is_prelit);
+
+ draw_label_layout (item, drawable,
+ editable_layout, needs_highlight,
+ label_color,
+ x,
+ text_rect.y0 + TEXT_BACK_PADDING_Y, gc);
+ }
+
+ if (have_additional)
+ {
+ additional_layout = get_label_layout (&item->details->additional_text_layout, item, item->details->additional_text);
+ prepare_pango_layout_for_draw (item, additional_layout);
+
+ gc = caja_icon_container_get_label_color_and_gc
+ (CAJA_ICON_CONTAINER (canvas_item->canvas),
+ &label_color, FALSE, needs_highlight,
+ FALSE);
+
+ draw_label_layout (item, drawable,
+ additional_layout, needs_highlight,
+ label_color,
+ x,
+ text_rect.y0 + details->editable_text_height + LABEL_LINE_SPACING + TEXT_BACK_PADDING_Y, gc);
+ }
+
+ if (!create_mask && item->details->is_highlighted_as_keyboard_focus)
+ {
+ gtk_paint_focus (gtk_widget_get_style (GTK_WIDGET (EEL_CANVAS_ITEM (item)->canvas)),
+ drawable,
+ needs_highlight ? GTK_STATE_SELECTED : GTK_STATE_NORMAL,
+ NULL,
+ GTK_WIDGET (EEL_CANVAS_ITEM (item)->canvas),
+ "icon-container",
+ text_rect.x0,
+ text_rect.y0,
+ text_rect.x1 - text_rect.x0,
+ text_rect.y1 - text_rect.y0);
+ }
+
+ if (editable_layout != NULL)
+ {
+ g_object_unref (editable_layout);
+ }
+
+ if (additional_layout != NULL)
+ {
+ g_object_unref (additional_layout);
+ }
+}
+
+void
+caja_icon_canvas_item_set_is_visible (CajaIconCanvasItem *item,
+ gboolean visible)
+{
+ if (item->details->is_visible == visible)
+ return;
+
+ item->details->is_visible = visible;
+
+ if (!visible)
+ {
+ caja_icon_canvas_item_invalidate_label (item);
+ }
+}
+
+void
+caja_icon_canvas_item_invalidate_label (CajaIconCanvasItem *item)
+{
+ caja_icon_canvas_item_invalidate_label_size (item);
+
+ if (item->details->editable_text_layout)
+ {
+ g_object_unref (item->details->editable_text_layout);
+ item->details->editable_text_layout = NULL;
+ }
+
+ if (item->details->additional_text_layout)
+ {
+ g_object_unref (item->details->additional_text_layout);
+ item->details->additional_text_layout = NULL;
+ }
+
+ if (item->details->embedded_text_layout)
+ {
+ g_object_unref (item->details->embedded_text_layout);
+ item->details->embedded_text_layout = NULL;
+ }
+}
+
+
+static GdkPixbuf *
+get_knob_pixbuf (void)
+{
+ GdkPixbuf *knob_pixbuf;
+ char *knob_filename;
+
+ knob_pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (),
+ "stock-caja-knob",
+ 8, 0, NULL);
+ if (!knob_pixbuf)
+ {
+ knob_filename = caja_pixmap_file ("knob.png");
+ knob_pixbuf = gdk_pixbuf_new_from_file (knob_filename, NULL);
+ g_free (knob_filename);
+ }
+
+ return knob_pixbuf;
+}
+
+static void
+draw_stretch_handles (CajaIconCanvasItem *item, GdkDrawable *drawable,
+ const EelIRect *rect)
+{
+ GtkWidget *widget;
+ GdkGC *gc;
+ GdkPixbuf *knob_pixbuf;
+ GdkBitmap *stipple;
+ int knob_width, knob_height;
+ GtkStyle *style;
+
+ if (!item->details->show_stretch_handles)
+ {
+ return;
+ }
+
+ widget = GTK_WIDGET (EEL_CANVAS_ITEM (item)->canvas);
+ style = gtk_widget_get_style (widget);
+
+ gc = gdk_gc_new (drawable);
+ knob_pixbuf = get_knob_pixbuf ();
+ knob_width = gdk_pixbuf_get_width (knob_pixbuf);
+ knob_height = gdk_pixbuf_get_height (knob_pixbuf);
+
+ stipple = eel_stipple_bitmap_for_screen (
+ gdk_drawable_get_screen (GDK_DRAWABLE (drawable)));
+
+ /* first draw the box */
+ gdk_gc_set_rgb_fg_color (gc, &style->white);
+ gdk_draw_rectangle
+ (drawable, gc, FALSE,
+ rect->x0,
+ rect->y0,
+ rect->x1 - rect->x0 - 1,
+ rect->y1 - rect->y0 - 1);
+
+ gdk_gc_set_rgb_fg_color (gc, &style->black);
+ gdk_gc_set_stipple (gc, stipple);
+ gdk_gc_set_fill (gc, GDK_STIPPLED);
+ gdk_draw_rectangle
+ (drawable, gc, FALSE,
+ rect->x0,
+ rect->y0,
+ rect->x1 - rect->x0 - 1,
+ rect->y1 - rect->y0 - 1);
+
+ /* draw the stretch handles themselves */
+
+ draw_pixbuf (knob_pixbuf, drawable, rect->x0, rect->y0);
+ draw_pixbuf (knob_pixbuf, drawable, rect->x0, rect->y1 - knob_height);
+ draw_pixbuf (knob_pixbuf, drawable, rect->x1 - knob_width, rect->y0);
+ draw_pixbuf (knob_pixbuf, drawable, rect->x1 - knob_width, rect->y1 - knob_height);
+ g_object_unref (knob_pixbuf);
+
+ g_object_unref (gc);
+}
+
+static void
+emblem_layout_reset (EmblemLayout *layout, CajaIconCanvasItem *icon_item, EelIRect icon_rect, gboolean is_rtl)
+{
+ layout->icon_item = icon_item;
+ layout->icon_rect = icon_rect;
+ layout->side = is_rtl ? LEFT_SIDE : RIGHT_SIDE;
+ layout->position = 0;
+ layout->index = 0;
+ layout->emblem = icon_item->details->emblem_pixbufs;
+}
+
+static gboolean
+emblem_layout_next (EmblemLayout *layout,
+ GdkPixbuf **emblem_pixbuf,
+ EelIRect *emblem_rect,
+ gboolean is_rtl)
+{
+ GdkPixbuf *pixbuf;
+ int width, height, x, y;
+ GdkPoint *attach_points;
+
+ /* Check if we have layed out all of the pixbufs. */
+ if (layout->emblem == NULL)
+ {
+ return FALSE;
+ }
+
+ /* Get the pixbuf. */
+ pixbuf = layout->emblem->data;
+ width = gdk_pixbuf_get_width (pixbuf);
+ height = gdk_pixbuf_get_height (pixbuf);
+
+
+ /* Advance to the next emblem. */
+ layout->emblem = layout->emblem->next;
+
+ attach_points = layout->icon_item->details->attach_points;
+ if (attach_points != NULL)
+ {
+ if (layout->index >= layout->icon_item->details->n_attach_points)
+ {
+ return FALSE;
+ }
+
+ x = layout->icon_rect.x0 + attach_points[layout->index].x;
+ y = layout->icon_rect.y0 + attach_points[layout->index].y;
+
+ layout->index += 1;
+
+ /* Return the rectangle and pixbuf. */
+ *emblem_pixbuf = pixbuf;
+ emblem_rect->x0 = x - width / 2;
+ emblem_rect->y0 = y - height / 2;
+ emblem_rect->x1 = emblem_rect->x0 + width;
+ emblem_rect->y1 = emblem_rect->y0 + height;
+
+ return TRUE;
+
+ }
+
+ for (;;)
+ {
+
+ /* Find the side to lay out along. */
+ switch (layout->side)
+ {
+ case RIGHT_SIDE:
+ x = layout->icon_rect.x1;
+ y = is_rtl ? layout->icon_rect.y1 : layout->icon_rect.y0;
+ break;
+ case BOTTOM_SIDE:
+ x = is_rtl ? layout->icon_rect.x0 : layout->icon_rect.x1;
+ y = layout->icon_rect.y1;
+ break;
+ case LEFT_SIDE:
+ x = layout->icon_rect.x0;
+ y = is_rtl ? layout->icon_rect.y0 : layout->icon_rect.y1;
+ break;
+ case TOP_SIDE:
+ x = is_rtl ? layout->icon_rect.x1 : layout->icon_rect.x0;
+ y = layout->icon_rect.y0;
+ break;
+ default:
+ g_assert_not_reached ();
+ x = 0;
+ y = 0;
+ break;
+ }
+ if (layout->position != 0)
+ {
+ switch (layout->side)
+ {
+ case RIGHT_SIDE:
+ y += (is_rtl ? -1 : 1) * (layout->position + height / 2);
+ break;
+ case BOTTOM_SIDE:
+ x += (is_rtl ? 1 : -1 ) * (layout->position + width / 2);
+ break;
+ case LEFT_SIDE:
+ y += (is_rtl ? 1 : -1) * (layout->position + height / 2);
+ break;
+ case TOP_SIDE:
+ x += (is_rtl ? -1 : 1) * (layout->position + width / 2);
+ break;
+ }
+ }
+
+ /* Check to see if emblem fits in current side. */
+ if (x >= layout->icon_rect.x0 && x <= layout->icon_rect.x1
+ && y >= layout->icon_rect.y0 && y <= layout->icon_rect.y1)
+ {
+
+ /* It fits. */
+
+ /* Advance along the side. */
+ switch (layout->side)
+ {
+ case RIGHT_SIDE:
+ case LEFT_SIDE:
+ layout->position += height + EMBLEM_SPACING;
+ break;
+ case BOTTOM_SIDE:
+ case TOP_SIDE:
+ layout->position += width + EMBLEM_SPACING;
+ break;
+ }
+
+ /* Return the rectangle and pixbuf. */
+ *emblem_pixbuf = pixbuf;
+ emblem_rect->x0 = x - width / 2;
+ emblem_rect->y0 = y - height / 2;
+ emblem_rect->x1 = emblem_rect->x0 + width;
+ emblem_rect->y1 = emblem_rect->y0 + height;
+
+ return TRUE;
+ }
+
+ /* It doesn't fit, so move to the next side. */
+ switch (layout->side)
+ {
+ case RIGHT_SIDE:
+ layout->side = is_rtl ? TOP_SIDE : BOTTOM_SIDE;
+ break;
+ case BOTTOM_SIDE:
+ layout->side = is_rtl ? RIGHT_SIDE : LEFT_SIDE;
+ break;
+ case LEFT_SIDE:
+ layout->side = is_rtl ? BOTTOM_SIDE : TOP_SIDE;
+ break;
+ case TOP_SIDE:
+ default:
+ return FALSE;
+ }
+ layout->position = 0;
+ }
+}
+
+static void
+draw_pixbuf (GdkPixbuf *pixbuf, GdkDrawable *drawable, int x, int y)
+{
+ /* FIXME bugzilla.gnome.org 40703:
+ * Dither would be better if we passed dither values.
+ */
+ gdk_draw_pixbuf (drawable, NULL, pixbuf, 0, 0, x, y,
+ gdk_pixbuf_get_width (pixbuf),
+ gdk_pixbuf_get_height (pixbuf),
+ GDK_RGB_DITHER_NORMAL, 0, 0);
+}
+
+/* shared code to highlight or dim the passed-in pixbuf */
+static GdkPixbuf *
+real_map_pixbuf (CajaIconCanvasItem *icon_item)
+{
+ EelCanvas *canvas;
+ char *audio_filename;
+ CajaIconContainer *container;
+ GdkPixbuf *temp_pixbuf, *old_pixbuf, *audio_pixbuf;
+ int emblem_size;
+ guint render_mode, saturation, brightness, lighten;
+
+ temp_pixbuf = icon_item->details->pixbuf;
+ canvas = EEL_CANVAS_ITEM(icon_item)->canvas;
+ container = CAJA_ICON_CONTAINER (canvas);
+
+ g_object_ref (temp_pixbuf);
+
+ if (icon_item->details->is_prelit ||
+ icon_item->details->is_highlighted_for_clipboard)
+ {
+ old_pixbuf = temp_pixbuf;
+
+ gtk_widget_style_get (GTK_WIDGET (container),
+ "prelight_icon_render_mode", &render_mode,
+ "prelight_icon_saturation", &saturation,
+ "prelight_icon_brightness", &brightness,
+ "prelight_icon_lighten", &lighten,
+ NULL);
+
+ if (render_mode > 0 || saturation < 255 || brightness < 255)
+ {
+ temp_pixbuf = eel_gdk_pixbuf_render (temp_pixbuf,
+ render_mode,
+ saturation,
+ brightness,
+ lighten,
+ container->details->prelight_icon_color_rgba);
+ g_object_unref (old_pixbuf);
+ }
+
+
+
+ /* FIXME bugzilla.gnome.org 42471: This hard-wired image is inappropriate to
+ * this level of code, which shouldn't know that the
+ * preview is audio, nor should it have an icon
+ * hard-wired in.
+ */
+
+ /* if the icon is currently being previewed, superimpose an image to indicate that */
+ /* audio is the only kind of previewing right now, so this code isn't as general as it could be */
+ if (icon_item->details->is_active)
+ {
+ emblem_size = caja_icon_get_emblem_size_for_icon_size (gdk_pixbuf_get_width (temp_pixbuf));
+ /* Load the audio symbol. */
+ audio_filename = caja_pixmap_file ("audio.svg");
+ if (audio_filename != NULL)
+ {
+ audio_pixbuf = gdk_pixbuf_new_from_file_at_scale (audio_filename,
+ emblem_size, emblem_size,
+ TRUE,
+ NULL);
+ }
+ else
+ {
+ audio_pixbuf = NULL;
+ }
+
+ /* Composite it onto the icon. */
+ if (audio_pixbuf != NULL)
+ {
+ gdk_pixbuf_composite
+ (audio_pixbuf,
+ temp_pixbuf,
+ 0, 0,
+ gdk_pixbuf_get_width (audio_pixbuf),
+ gdk_pixbuf_get_height (audio_pixbuf),
+ 0, 0,
+ 1.0, 1.0,
+ GDK_INTERP_BILINEAR, 0xFF);
+
+ g_object_unref (audio_pixbuf);
+ }
+
+ g_free (audio_filename);
+ }
+ }
+
+ if (icon_item->details->is_highlighted_for_selection
+ || icon_item->details->is_highlighted_for_drop)
+ {
+ guint color;
+
+ old_pixbuf = temp_pixbuf;
+
+ color = gtk_widget_has_focus (GTK_WIDGET (canvas)) ? CAJA_ICON_CONTAINER (canvas)->details->highlight_color_rgba : CAJA_ICON_CONTAINER (canvas)->details->active_color_rgba;
+
+ temp_pixbuf = eel_create_colorized_pixbuf (temp_pixbuf,
+ EEL_RGBA_COLOR_GET_R (color),
+ EEL_RGBA_COLOR_GET_G (color),
+ EEL_RGBA_COLOR_GET_B (color));
+
+ g_object_unref (old_pixbuf);
+ }
+
+ if (!icon_item->details->is_active
+ && !icon_item->details->is_prelit
+ && !icon_item->details->is_highlighted_for_selection
+ && !icon_item->details->is_highlighted_for_drop)
+ {
+ old_pixbuf = temp_pixbuf;
+
+ gtk_widget_style_get (GTK_WIDGET (container),
+ "normal_icon_render_mode", &render_mode,
+ "normal_icon_saturation", &saturation,
+ "normal_icon_brightness", &brightness,
+ "normal_icon_lighten", &lighten,
+ NULL);
+ if (render_mode > 0 || saturation < 255 || brightness < 255)
+ {
+ /* if theme requests colorization */
+ temp_pixbuf = eel_gdk_pixbuf_render (temp_pixbuf,
+ render_mode,
+ saturation,
+ brightness,
+ lighten,
+ container->details->normal_icon_color_rgba);
+ g_object_unref (old_pixbuf);
+ }
+ }
+
+ return temp_pixbuf;
+}
+
+static GdkPixbuf *
+map_pixbuf (CajaIconCanvasItem *icon_item)
+{
+ if (!(icon_item->details->rendered_pixbuf != NULL
+ && icon_item->details->rendered_is_active == icon_item->details->is_active
+ && icon_item->details->rendered_is_prelit == icon_item->details->is_prelit
+ && icon_item->details->rendered_is_highlighted_for_selection == icon_item->details->is_highlighted_for_selection
+ && icon_item->details->rendered_is_highlighted_for_drop == icon_item->details->is_highlighted_for_drop
+ && icon_item->details->rendered_is_highlighted_for_clipboard == icon_item->details->is_highlighted_for_clipboard
+ && (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)))))
+ {
+ if (icon_item->details->rendered_pixbuf != NULL)
+ {
+ g_object_unref (icon_item->details->rendered_pixbuf);
+ }
+ icon_item->details->rendered_pixbuf = real_map_pixbuf (icon_item);
+ icon_item->details->rendered_is_active = icon_item->details->is_active;
+ icon_item->details->rendered_is_prelit = icon_item->details->is_prelit;
+ icon_item->details->rendered_is_highlighted_for_selection = icon_item->details->is_highlighted_for_selection;
+ icon_item->details->rendered_is_highlighted_for_drop = icon_item->details->is_highlighted_for_drop;
+ icon_item->details->rendered_is_highlighted_for_clipboard = icon_item->details->is_highlighted_for_clipboard;
+ icon_item->details->rendered_is_focused = gtk_widget_has_focus (GTK_WIDGET (EEL_CANVAS_ITEM (icon_item)->canvas));
+ }
+
+ g_object_ref (icon_item->details->rendered_pixbuf);
+
+ return icon_item->details->rendered_pixbuf;
+}
+
+static void
+draw_embedded_text (CajaIconCanvasItem *item,
+ GdkDrawable *drawable,
+ int x, int y)
+{
+ GdkGC *gc;
+ GdkRectangle clip_rect;
+ PangoLayout *layout;
+ PangoContext *context;
+ PangoFontDescription *desc;
+
+ if (item->details->embedded_text == NULL ||
+ item->details->embedded_text_rect.width == 0 ||
+ item->details->embedded_text_rect.height == 0)
+ {
+ return;
+ }
+
+ if (item->details->embedded_text_layout != NULL)
+ {
+ layout = g_object_ref (item->details->embedded_text_layout);
+ }
+ else
+ {
+ context = gtk_widget_get_pango_context (GTK_WIDGET (EEL_CANVAS_ITEM (item)->canvas));
+ layout = pango_layout_new (context);
+ pango_layout_set_text (layout, item->details->embedded_text, -1);
+
+ desc = pango_font_description_from_string ("monospace 6");
+ pango_layout_set_font_description (layout, desc);
+ pango_font_description_free (desc);
+
+ if (item->details->is_visible)
+ {
+ item->details->embedded_text_layout = g_object_ref (layout);
+ }
+ }
+
+ gc = gdk_gc_new (drawable);
+
+ clip_rect.x = x + item->details->embedded_text_rect.x;
+ clip_rect.y = y + item->details->embedded_text_rect.y;
+ clip_rect.width = item->details->embedded_text_rect.width;
+ clip_rect.height = item->details->embedded_text_rect.height;
+
+ gdk_gc_set_clip_rectangle (gc, &clip_rect);
+
+ gdk_draw_layout (drawable, gc,
+ x + item->details->embedded_text_rect.x,
+ y + item->details->embedded_text_rect.y,
+ layout);
+
+ g_object_unref (gc);
+ g_object_unref (layout);
+}
+
+/* Draw the icon item for non-anti-aliased mode. */
+static void
+caja_icon_canvas_item_draw (EelCanvasItem *item, GdkDrawable *drawable,
+ GdkEventExpose *expose)
+{
+ CajaIconCanvasItem *icon_item;
+ CajaIconCanvasItemDetails *details;
+ EelIRect icon_rect, emblem_rect;
+ EmblemLayout emblem_layout;
+ GdkPixbuf *emblem_pixbuf, *temp_pixbuf;
+ GdkRectangle draw_rect, pixbuf_rect;
+ gboolean is_rtl;
+
+ icon_item = CAJA_ICON_CANVAS_ITEM (item);
+ details = icon_item->details;
+
+ /* Draw the pixbuf. */
+ if (details->pixbuf == NULL)
+ {
+ return;
+ }
+
+ icon_rect = icon_item->details->canvas_rect;
+
+ /* if the pre-lit or selection flag is set, make a pre-lit or darkened pixbuf and draw that instead */
+ /* and colorize normal pixbuf if rc wants that */
+ temp_pixbuf = map_pixbuf (icon_item);
+ pixbuf_rect.x = icon_rect.x0;
+ pixbuf_rect.y = icon_rect.y0;
+ pixbuf_rect.width = gdk_pixbuf_get_width (temp_pixbuf);
+ pixbuf_rect.height = gdk_pixbuf_get_height (temp_pixbuf);
+ if (gdk_rectangle_intersect (&(expose->area), &pixbuf_rect, &draw_rect))
+ {
+ gdk_draw_pixbuf (drawable,
+ NULL,
+ temp_pixbuf,
+ draw_rect.x - pixbuf_rect.x,
+ draw_rect.y - pixbuf_rect.y,
+ draw_rect.x,
+ draw_rect.y,
+ draw_rect.width,
+ draw_rect.height,
+ GDK_RGB_DITHER_NORMAL,
+ 0,0);
+ }
+ g_object_unref (temp_pixbuf);
+
+ draw_embedded_text (icon_item, drawable, icon_rect.x0, icon_rect.y0);
+
+ is_rtl = caja_icon_container_is_layout_rtl (CAJA_ICON_CONTAINER (item->canvas));
+
+ /* Draw the emblem pixbufs. */
+ emblem_layout_reset (&emblem_layout, icon_item, icon_rect, is_rtl);
+ while (emblem_layout_next (&emblem_layout, &emblem_pixbuf, &emblem_rect, is_rtl))
+ {
+ draw_pixbuf (emblem_pixbuf, drawable, emblem_rect.x0, emblem_rect.y0);
+ }
+
+ /* Draw stretching handles (if necessary). */
+ draw_stretch_handles (icon_item, drawable, &icon_rect);
+
+ /* Draw the label text. */
+ draw_label_text (icon_item, drawable, FALSE, icon_rect);
+}
+
+#define ZERO_WIDTH_SPACE "\xE2\x80\x8B"
+
+#define ZERO_OR_THREE_DIGITS(p) \
+ (!g_ascii_isdigit (*p) || \
+ (g_ascii_isdigit (*(p+1)) && \
+ g_ascii_isdigit (*(p+2))))
+
+
+static PangoLayout *
+create_label_layout (CajaIconCanvasItem *item,
+ const char *text)
+{
+ PangoLayout *layout;
+ PangoContext *context;
+ PangoFontDescription *desc;
+ CajaIconContainer *container;
+ EelCanvasItem *canvas_item;
+ GString *str;
+ char *zeroified_text;
+ const char *p;
+
+ canvas_item = EEL_CANVAS_ITEM (item);
+
+ container = CAJA_ICON_CONTAINER (canvas_item->canvas);
+ context = gtk_widget_get_pango_context (GTK_WIDGET (canvas_item->canvas));
+ layout = pango_layout_new (context);
+
+ zeroified_text = NULL;
+
+ if (text != NULL)
+ {
+ str = g_string_new (NULL);
+
+ for (p = text; *p != '\0'; p++)
+ {
+ str = g_string_append_c (str, *p);
+
+ if (*p == '_' || *p == '-' || (*p == '.' && ZERO_OR_THREE_DIGITS (p+1)))
+ {
+ /* Ensure that we allow to break after '_' or '.' characters,
+ * if they are not likely to be part of a version information, to
+ * not break wrapping of foobar-0.0.1.
+ * Wrap before IPs and long numbers, though. */
+ str = g_string_append (str, ZERO_WIDTH_SPACE);
+ }
+ }
+
+ zeroified_text = g_string_free (str, FALSE);
+ }
+
+ pango_layout_set_text (layout, zeroified_text, -1);
+ pango_layout_set_auto_dir (layout, FALSE);
+
+ if (container->details->label_position == CAJA_ICON_LABEL_POSITION_BESIDE)
+ {
+ if (!caja_icon_container_is_layout_rtl (container))
+ {
+ pango_layout_set_alignment (layout, PANGO_ALIGN_LEFT);
+ }
+ else
+ {
+ pango_layout_set_alignment (layout, PANGO_ALIGN_RIGHT);
+ }
+ }
+ else
+ {
+ pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER);
+ }
+
+ pango_layout_set_spacing (layout, LABEL_LINE_SPACING);
+ pango_layout_set_wrap (layout, PANGO_WRAP_WORD_CHAR);
+
+ /* Create a font description */
+ if (container->details->font)
+ {
+ desc = pango_font_description_from_string (container->details->font);
+ }
+ else
+ {
+ desc = pango_font_description_copy (pango_context_get_font_description (context));
+ pango_font_description_set_size (desc,
+ pango_font_description_get_size (desc) +
+ container->details->font_size_table [container->details->zoom_level]);
+ }
+ pango_layout_set_font_description (layout, desc);
+ pango_font_description_free (desc);
+ g_free (zeroified_text);
+
+ return layout;
+}
+
+static PangoLayout *
+get_label_layout (PangoLayout **layout_cache,
+ CajaIconCanvasItem *item,
+ const char *text)
+{
+ PangoLayout *layout;
+
+ if (*layout_cache != NULL)
+ {
+ return g_object_ref (*layout_cache);
+ }
+
+ layout = create_label_layout (item, text);
+
+ if (item->details->is_visible)
+ {
+ *layout_cache = g_object_ref (layout);
+ }
+
+ return layout;
+}
+
+static void
+draw_label_layout (CajaIconCanvasItem *item,
+ GdkDrawable *drawable,
+ PangoLayout *layout,
+ gboolean highlight,
+ GdkColor *label_color,
+ int x,
+ int y,
+ GdkGC *gc)
+{
+ if (drawable == NULL)
+ {
+ return;
+ }
+
+ if (item->details->is_renaming)
+ {
+ return;
+ }
+
+ if (!highlight && (CAJA_ICON_CONTAINER (EEL_CANVAS_ITEM (item)->canvas)->details->use_drop_shadows))
+ {
+ /* draw a drop shadow */
+ eel_gdk_draw_layout_with_drop_shadow (drawable, gc,
+ label_color,
+ &gtk_widget_get_style (GTK_WIDGET (EEL_CANVAS_ITEM (item)->canvas))->black,
+ x, y,
+ layout);
+ }
+ else
+ {
+ gdk_draw_layout (drawable, gc,
+ x, y,
+ layout);
+ }
+}
+
+/* handle events */
+
+static int
+caja_icon_canvas_item_event (EelCanvasItem *item, GdkEvent *event)
+{
+ CajaIconCanvasItem *icon_item;
+ GdkCursor *cursor;
+
+ icon_item = CAJA_ICON_CANVAS_ITEM (item);
+
+ switch (event->type)
+ {
+ case GDK_ENTER_NOTIFY:
+ if (!icon_item->details->is_prelit)
+ {
+ icon_item->details->is_prelit = TRUE;
+ caja_icon_canvas_item_invalidate_label_size (icon_item);
+ eel_canvas_item_request_update (item);
+ eel_canvas_item_send_behind (item,
+ CAJA_ICON_CONTAINER (item->canvas)->details->rubberband_info.selection_rectangle);
+
+ /* show a hand cursor */
+ if (in_single_click_mode ())
+ {
+ cursor = gdk_cursor_new_for_display (gdk_display_get_default(),
+ GDK_HAND2);
+ gdk_window_set_cursor (((GdkEventAny *)event)->window, cursor);
+ gdk_cursor_unref (cursor);
+ }
+
+ /* FIXME bugzilla.gnome.org 42473:
+ * We should emit our own signal here,
+ * not one from the container; it could hook
+ * up to that signal and emit one of its
+ * own. Doing it this way hard-codes what
+ * "user_data" is. Also, the two signals
+ * should be separate. The "unpreview" signal
+ * does not have a return value.
+ */
+ icon_item->details->is_active = caja_icon_container_emit_preview_signal
+ (CAJA_ICON_CONTAINER (item->canvas),
+ CAJA_ICON_CANVAS_ITEM (item)->user_data,
+ TRUE);
+ }
+ return TRUE;
+
+ case GDK_LEAVE_NOTIFY:
+ if (icon_item->details->is_prelit
+ || icon_item->details->is_highlighted_for_drop)
+ {
+ /* When leaving, turn of the prelight state and the
+ * higlighted for drop. The latter gets turned on
+ * by the drag&drop motion callback.
+ */
+ /* FIXME bugzilla.gnome.org 42473:
+ * We should emit our own signal here,
+ * not one from the containe; it could hook up
+ * to that signal and emit one of its
+ * ownr. Doing it this way hard-codes what
+ * "user_data" is. Also, the two signals
+ * should be separate. The "unpreview" signal
+ * does not have a return value.
+ */
+ caja_icon_container_emit_preview_signal
+ (CAJA_ICON_CONTAINER (item->canvas),
+ CAJA_ICON_CANVAS_ITEM (item)->user_data,
+ FALSE);
+ icon_item->details->is_prelit = FALSE;
+ icon_item->details->is_active = 0;
+ icon_item->details->is_highlighted_for_drop = FALSE;
+ caja_icon_canvas_item_invalidate_label_size (icon_item);
+ eel_canvas_item_request_update (item);
+
+ /* show default cursor */
+ gdk_window_set_cursor (((GdkEventAny *)event)->window, NULL);
+ }
+ return TRUE;
+
+ default:
+ /* Don't eat up other events; icon container might use them. */
+ return FALSE;
+ }
+}
+
+static gboolean
+hit_test_pixbuf (GdkPixbuf *pixbuf, EelIRect pixbuf_location, EelIRect probe_rect)
+{
+ EelIRect relative_rect, pixbuf_rect;
+ int x, y;
+ guint8 *pixel;
+
+ /* You can get here without a pixbuf in some strange cases. */
+ if (pixbuf == NULL)
+ {
+ return FALSE;
+ }
+
+ /* Check to see if it's within the rectangle at all. */
+ relative_rect.x0 = probe_rect.x0 - pixbuf_location.x0;
+ relative_rect.y0 = probe_rect.y0 - pixbuf_location.y0;
+ relative_rect.x1 = probe_rect.x1 - pixbuf_location.x0;
+ relative_rect.y1 = probe_rect.y1 - pixbuf_location.y0;
+ pixbuf_rect.x0 = 0;
+ pixbuf_rect.y0 = 0;
+ pixbuf_rect.x1 = gdk_pixbuf_get_width (pixbuf);
+ pixbuf_rect.y1 = gdk_pixbuf_get_height (pixbuf);
+ eel_irect_intersect (&relative_rect, &relative_rect, &pixbuf_rect);
+ if (eel_irect_is_empty (&relative_rect))
+ {
+ return FALSE;
+ }
+
+ /* If there's no alpha channel, it's opaque and we have a hit. */
+ if (!gdk_pixbuf_get_has_alpha (pixbuf))
+ {
+ return TRUE;
+ }
+ g_assert (gdk_pixbuf_get_n_channels (pixbuf) == 4);
+
+ /* Check the alpha channel of the pixel to see if we have a hit. */
+ for (x = relative_rect.x0; x < relative_rect.x1; x++)
+ {
+ for (y = relative_rect.y0; y < relative_rect.y1; y++)
+ {
+ pixel = gdk_pixbuf_get_pixels (pixbuf)
+ + y * gdk_pixbuf_get_rowstride (pixbuf)
+ + x * 4;
+ if (pixel[3] > 1)
+ {
+ return TRUE;
+ }
+ }
+ }
+ return FALSE;
+}
+
+static gboolean
+hit_test (CajaIconCanvasItem *icon_item, EelIRect canvas_rect)
+{
+ CajaIconCanvasItemDetails *details;
+ EelIRect emblem_rect;
+ EmblemLayout emblem_layout;
+ GdkPixbuf *emblem_pixbuf;
+ gboolean is_rtl;
+
+ details = icon_item->details;
+
+ /* Quick check to see if the rect hits the icon, text or emblems at all. */
+ if (!eel_irect_hits_irect (icon_item->details->canvas_rect, canvas_rect)
+ && (!eel_irect_hits_irect (details->text_rect, canvas_rect))
+ && (!eel_irect_hits_irect (details->emblem_rect, canvas_rect)))
+ {
+ return FALSE;
+ }
+
+ /* Check for hits in the stretch handles. */
+ if (hit_test_stretch_handle (icon_item, canvas_rect, NULL))
+ {
+ return TRUE;
+ }
+
+ /* Check for hit in the icon. */
+ if (eel_irect_hits_irect (icon_item->details->canvas_rect, canvas_rect))
+ {
+ return TRUE;
+ }
+
+ /* Check for hit in the text. */
+ if (eel_irect_hits_irect (details->text_rect, canvas_rect)
+ && !icon_item->details->is_renaming)
+ {
+ return TRUE;
+ }
+
+ is_rtl = caja_icon_container_is_layout_rtl (CAJA_ICON_CONTAINER (EEL_CANVAS_ITEM (icon_item)->canvas));
+
+ /* Check for hit in the emblem pixbufs. */
+ emblem_layout_reset (&emblem_layout, icon_item, icon_item->details->canvas_rect, is_rtl);
+ while (emblem_layout_next (&emblem_layout, &emblem_pixbuf, &emblem_rect, is_rtl))
+ {
+ if (hit_test_pixbuf (emblem_pixbuf, emblem_rect, canvas_rect))
+ {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/* Point handler for the icon canvas item. */
+static double
+caja_icon_canvas_item_point (EelCanvasItem *item, double x, double y, int cx, int cy,
+ EelCanvasItem **actual_item)
+{
+ EelIRect canvas_rect;
+
+ *actual_item = item;
+ canvas_rect.x0 = cx;
+ canvas_rect.y0 = cy;
+ canvas_rect.x1 = cx + 1;
+ canvas_rect.y1 = cy + 1;
+ if (hit_test (CAJA_ICON_CANVAS_ITEM (item), canvas_rect))
+ {
+ return 0.0;
+ }
+ else
+ {
+ /* This value means not hit.
+ * It's kind of arbitrary. Can we do better?
+ */
+ return item->canvas->pixels_per_unit * 2 + 10;
+ }
+}
+
+static void
+caja_icon_canvas_item_translate (EelCanvasItem *item, double dx, double dy)
+{
+ CajaIconCanvasItem *icon_item;
+ CajaIconCanvasItemDetails *details;
+
+ icon_item = CAJA_ICON_CANVAS_ITEM (item);
+ details = icon_item->details;
+
+ details->x += dx;
+ details->y += dy;
+}
+
+void
+caja_icon_canvas_item_get_bounds_for_layout (CajaIconCanvasItem *icon_item,
+ double *x1, double *y1, double *x2, double *y2)
+{
+ CajaIconCanvasItemDetails *details;
+ EelIRect *total_rect;
+
+ details = icon_item->details;
+
+ caja_icon_canvas_item_ensure_bounds_up_to_date (icon_item);
+ g_assert (details->bounds_cached);
+
+ total_rect = &details->bounds_cache_for_layout;
+
+ /* Return the result. */
+ if (x1 != NULL)
+ {
+ *x1 = (int)details->x + total_rect->x0;
+ }
+ if (y1 != NULL)
+ {
+ *y1 = (int)details->y + total_rect->y0;
+ }
+ if (x2 != NULL)
+ {
+ *x2 = (int)details->x + total_rect->x1 + 1;
+ }
+ if (y2 != NULL)
+ {
+ *y2 = (int)details->y + total_rect->y1 + 1;
+ }
+}
+
+void
+caja_icon_canvas_item_get_bounds_for_entire_item (CajaIconCanvasItem *icon_item,
+ double *x1, double *y1, double *x2, double *y2)
+{
+ CajaIconCanvasItemDetails *details;
+ EelIRect *total_rect;
+
+ details = icon_item->details;
+
+ caja_icon_canvas_item_ensure_bounds_up_to_date (icon_item);
+ g_assert (details->bounds_cached);
+
+ total_rect = &details->bounds_cache_for_entire_item;
+
+ /* Return the result. */
+ if (x1 != NULL)
+ {
+ *x1 = (int)details->x + total_rect->x0;
+ }
+ if (y1 != NULL)
+ {
+ *y1 = (int)details->y + total_rect->y0;
+ }
+ if (x2 != NULL)
+ {
+ *x2 = (int)details->x + total_rect->x1 + 1;
+ }
+ if (y2 != NULL)
+ {
+ *y2 = (int)details->y + total_rect->y1 + 1;
+ }
+}
+
+/* Bounds handler for the icon canvas item. */
+static void
+caja_icon_canvas_item_bounds (EelCanvasItem *item,
+ double *x1, double *y1, double *x2, double *y2)
+{
+ CajaIconCanvasItem *icon_item;
+ CajaIconCanvasItemDetails *details;
+ EelIRect *total_rect;
+
+ icon_item = CAJA_ICON_CANVAS_ITEM (item);
+ details = icon_item->details;
+
+ g_assert (x1 != NULL);
+ g_assert (y1 != NULL);
+ g_assert (x2 != NULL);
+ g_assert (y2 != NULL);
+
+ caja_icon_canvas_item_ensure_bounds_up_to_date (icon_item);
+ g_assert (details->bounds_cached);
+
+ total_rect = &details->bounds_cache;
+
+ /* Return the result. */
+ *x1 = (int)details->x + total_rect->x0;
+ *y1 = (int)details->y + total_rect->y0;
+ *x2 = (int)details->x + total_rect->x1 + 1;
+ *y2 = (int)details->y + total_rect->y1 + 1;
+}
+
+static void
+caja_icon_canvas_item_ensure_bounds_up_to_date (CajaIconCanvasItem *icon_item)
+{
+ CajaIconCanvasItemDetails *details;
+ EelIRect icon_rect, emblem_rect, icon_rect_raw;
+ EelIRect text_rect, text_rect_for_layout, text_rect_for_entire_text;
+ EelIRect total_rect, total_rect_for_layout, total_rect_for_entire_text;
+ EelCanvasItem *item;
+ double pixels_per_unit;
+ EmblemLayout emblem_layout;
+ GdkPixbuf *emblem_pixbuf;
+ gboolean is_rtl;
+
+ details = icon_item->details;
+ item = EEL_CANVAS_ITEM (icon_item);
+
+ if (!details->bounds_cached)
+ {
+ measure_label_text (icon_item);
+
+ pixels_per_unit = EEL_CANVAS_ITEM (item)->canvas->pixels_per_unit;
+
+ /* Compute raw and scaled icon rectangle. */
+ icon_rect.x0 = 0;
+ icon_rect.y0 = 0;
+ icon_rect_raw.x0 = 0;
+ icon_rect_raw.y0 = 0;
+ if (details->pixbuf == NULL)
+ {
+ icon_rect.x1 = icon_rect.x0;
+ icon_rect.y1 = icon_rect.y0;
+ icon_rect_raw.x1 = icon_rect_raw.x0;
+ icon_rect_raw.y1 = icon_rect_raw.y0;
+ }
+ else
+ {
+ icon_rect_raw.x1 = icon_rect_raw.x0 + gdk_pixbuf_get_width (details->pixbuf);
+ icon_rect_raw.y1 = icon_rect_raw.y0 + gdk_pixbuf_get_height (details->pixbuf);
+ icon_rect.x1 = icon_rect_raw.x1 / pixels_per_unit;
+ icon_rect.y1 = icon_rect_raw.y1 / pixels_per_unit;
+ }
+
+ /* Compute text rectangle. */
+ text_rect = compute_text_rectangle (icon_item, icon_rect, FALSE, BOUNDS_USAGE_FOR_DISPLAY);
+ text_rect_for_layout = compute_text_rectangle (icon_item, icon_rect, FALSE, BOUNDS_USAGE_FOR_LAYOUT);
+ text_rect_for_entire_text = compute_text_rectangle (icon_item, icon_rect, FALSE, BOUNDS_USAGE_FOR_ENTIRE_ITEM);
+
+ is_rtl = caja_icon_container_is_layout_rtl (CAJA_ICON_CONTAINER (item->canvas));
+
+ /* Compute total rectangle, adding in emblem rectangles. */
+ eel_irect_union (&total_rect, &icon_rect, &text_rect);
+ eel_irect_union (&total_rect_for_layout, &icon_rect, &text_rect_for_layout);
+ eel_irect_union (&total_rect_for_entire_text, &icon_rect, &text_rect_for_entire_text);
+ emblem_layout_reset (&emblem_layout, icon_item, icon_rect_raw, is_rtl);
+ while (emblem_layout_next (&emblem_layout, &emblem_pixbuf, &emblem_rect, is_rtl))
+ {
+ emblem_rect.x0 = floor (emblem_rect.x0 / pixels_per_unit);
+ emblem_rect.y0 = floor (emblem_rect.y0 / pixels_per_unit);
+ emblem_rect.x1 = ceil (emblem_rect.x1 / pixels_per_unit);
+ emblem_rect.y1 = ceil (emblem_rect.y1 / pixels_per_unit);
+
+ eel_irect_union (&total_rect, &total_rect, &emblem_rect);
+ eel_irect_union (&total_rect_for_layout, &total_rect_for_layout, &emblem_rect);
+ eel_irect_union (&total_rect_for_entire_text, &total_rect_for_entire_text, &emblem_rect);
+ }
+
+ details->bounds_cache = total_rect;
+ details->bounds_cache_for_layout = total_rect_for_layout;
+ details->bounds_cache_for_entire_item = total_rect_for_entire_text;
+ details->bounds_cached = TRUE;
+ }
+}
+
+/* Get the rectangle of the icon only, in world coordinates. */
+EelDRect
+caja_icon_canvas_item_get_icon_rectangle (const CajaIconCanvasItem *item)
+{
+ EelDRect rectangle;
+ double pixels_per_unit;
+ GdkPixbuf *pixbuf;
+
+ g_return_val_if_fail (CAJA_IS_ICON_CANVAS_ITEM (item), eel_drect_empty);
+
+ rectangle.x0 = item->details->x;
+ rectangle.y0 = item->details->y;
+
+ pixbuf = item->details->pixbuf;
+
+ pixels_per_unit = EEL_CANVAS_ITEM (item)->canvas->pixels_per_unit;
+ rectangle.x1 = rectangle.x0 + (pixbuf == NULL ? 0 : gdk_pixbuf_get_width (pixbuf)) / pixels_per_unit;
+ rectangle.y1 = rectangle.y0 + (pixbuf == NULL ? 0 : gdk_pixbuf_get_height (pixbuf)) / pixels_per_unit;
+
+ eel_canvas_item_i2w (EEL_CANVAS_ITEM (item),
+ &rectangle.x0,
+ &rectangle.y0);
+ eel_canvas_item_i2w (EEL_CANVAS_ITEM (item),
+ &rectangle.x1,
+ &rectangle.y1);
+
+ return rectangle;
+}
+
+EelDRect
+caja_icon_canvas_item_get_text_rectangle (CajaIconCanvasItem *item,
+ gboolean for_layout)
+{
+ /* FIXME */
+ EelIRect icon_rectangle;
+ EelIRect text_rectangle;
+ EelDRect ret;
+ double pixels_per_unit;
+ GdkPixbuf *pixbuf;
+
+ g_return_val_if_fail (CAJA_IS_ICON_CANVAS_ITEM (item), eel_drect_empty);
+
+ icon_rectangle.x0 = item->details->x;
+ icon_rectangle.y0 = item->details->y;
+
+ pixbuf = item->details->pixbuf;
+
+ pixels_per_unit = EEL_CANVAS_ITEM (item)->canvas->pixels_per_unit;
+ icon_rectangle.x1 = icon_rectangle.x0 + (pixbuf == NULL ? 0 : gdk_pixbuf_get_width (pixbuf)) / pixels_per_unit;
+ icon_rectangle.y1 = icon_rectangle.y0 + (pixbuf == NULL ? 0 : gdk_pixbuf_get_height (pixbuf)) / pixels_per_unit;
+
+ measure_label_text (item);
+
+ text_rectangle = compute_text_rectangle (item, icon_rectangle, FALSE,
+ for_layout ? BOUNDS_USAGE_FOR_LAYOUT : BOUNDS_USAGE_FOR_DISPLAY);
+
+ ret.x0 = text_rectangle.x0;
+ ret.y0 = text_rectangle.y0;
+ ret.x1 = text_rectangle.x1;
+ ret.y1 = text_rectangle.y1;
+
+ eel_canvas_item_i2w (EEL_CANVAS_ITEM (item),
+ &ret.x0,
+ &ret.y0);
+ eel_canvas_item_i2w (EEL_CANVAS_ITEM (item),
+ &ret.x1,
+ &ret.y1);
+
+ return ret;
+}
+
+
+/* Get the rectangle of the icon only, in canvas coordinates. */
+static void
+get_icon_canvas_rectangle (CajaIconCanvasItem *item,
+ EelIRect *rect)
+{
+ GdkPixbuf *pixbuf;
+
+ g_assert (CAJA_IS_ICON_CANVAS_ITEM (item));
+ g_assert (rect != NULL);
+
+ eel_canvas_w2c (EEL_CANVAS_ITEM (item)->canvas,
+ item->details->x,
+ item->details->y,
+ &rect->x0,
+ &rect->y0);
+
+ pixbuf = item->details->pixbuf;
+
+ rect->x1 = rect->x0 + (pixbuf == NULL ? 0 : gdk_pixbuf_get_width (pixbuf));
+ rect->y1 = rect->y0 + (pixbuf == NULL ? 0 : gdk_pixbuf_get_height (pixbuf));
+}
+
+void
+caja_icon_canvas_item_set_show_stretch_handles (CajaIconCanvasItem *item,
+ gboolean show_stretch_handles)
+{
+ g_return_if_fail (CAJA_IS_ICON_CANVAS_ITEM (item));
+ g_return_if_fail (show_stretch_handles == FALSE || show_stretch_handles == TRUE);
+
+ if (!item->details->show_stretch_handles == !show_stretch_handles)
+ {
+ return;
+ }
+
+ item->details->show_stretch_handles = show_stretch_handles;
+ eel_canvas_item_request_update (EEL_CANVAS_ITEM (item));
+}
+
+/* Check if one of the stretch handles was hit. */
+static gboolean
+hit_test_stretch_handle (CajaIconCanvasItem *item,
+ EelIRect probe_canvas_rect,
+ GtkCornerType *corner)
+{
+ EelIRect icon_rect;
+ GdkPixbuf *knob_pixbuf;
+ int knob_width, knob_height;
+ int hit_corner;
+
+ g_assert (CAJA_IS_ICON_CANVAS_ITEM (item));
+
+ /* Make sure there are handles to hit. */
+ if (!item->details->show_stretch_handles)
+ {
+ return FALSE;
+ }
+
+ /* Quick check to see if the rect hits the icon at all. */
+ icon_rect = item->details->canvas_rect;
+ if (!eel_irect_hits_irect (probe_canvas_rect, icon_rect))
+ {
+ return FALSE;
+ }
+
+ knob_pixbuf = get_knob_pixbuf ();
+ knob_width = gdk_pixbuf_get_width (knob_pixbuf);
+ knob_height = gdk_pixbuf_get_height (knob_pixbuf);
+ g_object_unref (knob_pixbuf);
+
+ /* Check for hits in the stretch handles. */
+ hit_corner = -1;
+ if (probe_canvas_rect.x0 < icon_rect.x0 + knob_width)
+ {
+ if (probe_canvas_rect.y0 < icon_rect.y0 + knob_height)
+ hit_corner = GTK_CORNER_TOP_LEFT;
+ else if (probe_canvas_rect.y1 >= icon_rect.y1 - knob_height)
+ hit_corner = GTK_CORNER_BOTTOM_LEFT;
+ }
+ else if (probe_canvas_rect.x1 >= icon_rect.x1 - knob_width)
+ {
+ if (probe_canvas_rect.y0 < icon_rect.y0 + knob_height)
+ hit_corner = GTK_CORNER_TOP_RIGHT;
+ else if (probe_canvas_rect.y1 >= icon_rect.y1 - knob_height)
+ hit_corner = GTK_CORNER_BOTTOM_RIGHT;
+ }
+ if (corner)
+ *corner = hit_corner;
+
+ return hit_corner != -1;
+}
+
+gboolean
+caja_icon_canvas_item_hit_test_stretch_handles (CajaIconCanvasItem *item,
+ EelDPoint world_point,
+ GtkCornerType *corner)
+{
+ EelIRect canvas_rect;
+
+ g_return_val_if_fail (CAJA_IS_ICON_CANVAS_ITEM (item), FALSE);
+
+ eel_canvas_w2c (EEL_CANVAS_ITEM (item)->canvas,
+ world_point.x,
+ world_point.y,
+ &canvas_rect.x0,
+ &canvas_rect.y0);
+ canvas_rect.x1 = canvas_rect.x0 + 1;
+ canvas_rect.y1 = canvas_rect.y0 + 1;
+ return hit_test_stretch_handle (item, canvas_rect, corner);
+}
+
+/* caja_icon_canvas_item_hit_test_rectangle
+ *
+ * Check and see if there is an intersection between the item and the
+ * canvas rect.
+ */
+gboolean
+caja_icon_canvas_item_hit_test_rectangle (CajaIconCanvasItem *item, EelIRect canvas_rect)
+{
+ g_return_val_if_fail (CAJA_IS_ICON_CANVAS_ITEM (item), FALSE);
+
+ return hit_test (item, canvas_rect);
+}
+
+const char *
+caja_icon_canvas_item_get_editable_text (CajaIconCanvasItem *icon_item)
+{
+ g_return_val_if_fail (CAJA_IS_ICON_CANVAS_ITEM (icon_item), NULL);
+
+ return icon_item->details->editable_text;
+}
+
+void
+caja_icon_canvas_item_set_renaming (CajaIconCanvasItem *item, gboolean state)
+{
+ g_return_if_fail (CAJA_IS_ICON_CANVAS_ITEM (item));
+ g_return_if_fail (state == FALSE || state == TRUE);
+
+ if (!item->details->is_renaming == !state)
+ {
+ return;
+ }
+
+ item->details->is_renaming = state;
+ eel_canvas_item_request_update (EEL_CANVAS_ITEM (item));
+}
+
+double
+caja_icon_canvas_item_get_max_text_width (CajaIconCanvasItem *item)
+{
+ EelCanvasItem *canvas_item;
+ CajaIconContainer *container;
+
+ canvas_item = EEL_CANVAS_ITEM (item);
+ container = CAJA_ICON_CONTAINER (canvas_item->canvas);
+
+ if (caja_icon_container_is_tighter_layout (container))
+ {
+ return MAX_TEXT_WIDTH_TIGHTER * canvas_item->canvas->pixels_per_unit;
+ }
+ else
+ {
+
+ if (container->details->label_position == CAJA_ICON_LABEL_POSITION_BESIDE)
+ {
+ if (container->details->layout_mode == CAJA_ICON_LAYOUT_T_B_L_R ||
+ container->details->layout_mode == CAJA_ICON_LAYOUT_T_B_R_L)
+ {
+ if (container->details->all_columns_same_width)
+ {
+ return MAX_TEXT_WIDTH_BESIDE_TOP_TO_BOTTOM * canvas_item->canvas->pixels_per_unit;
+ }
+ else
+ {
+ return -1;
+ }
+ }
+ else
+ {
+ return MAX_TEXT_WIDTH_BESIDE * canvas_item->canvas->pixels_per_unit;
+ }
+ }
+ else
+ {
+ return MAX_TEXT_WIDTH_STANDARD * canvas_item->canvas->pixels_per_unit;
+ }
+
+
+ }
+
+}
+
+/* CajaIconCanvasItemAccessible */
+
+static CajaIconCanvasItemAccessiblePrivate *
+accessible_get_priv (AtkObject *accessible)
+{
+ CajaIconCanvasItemAccessiblePrivate *priv;
+
+ priv = g_object_get_qdata (G_OBJECT (accessible),
+ accessible_private_data_quark);
+
+ return priv;
+}
+
+/* AtkAction interface */
+
+static gboolean
+caja_icon_canvas_item_accessible_idle_do_action (gpointer data)
+{
+ CajaIconCanvasItem *item;
+ CajaIconCanvasItemAccessibleActionContext *ctx;
+ CajaIcon *icon;
+ CajaIconContainer *container;
+ GList* selection;
+ GList file_list;
+ GdkEventButton button_event = { 0 };
+ gint action_number;
+
+ container = CAJA_ICON_CONTAINER (data);
+ container->details->a11y_item_action_idle_handler = 0;
+ while (!g_queue_is_empty (container->details->a11y_item_action_queue))
+ {
+ ctx = g_queue_pop_head (container->details->a11y_item_action_queue);
+ action_number = ctx->action_number;
+ item = ctx->item;
+ g_free (ctx);
+ icon = item->user_data;
+
+ switch (action_number)
+ {
+ case ACTION_OPEN:
+ file_list.data = icon->data;
+ file_list.next = NULL;
+ file_list.prev = NULL;
+ g_signal_emit_by_name (container, "activate", &file_list);
+ break;
+ case ACTION_MENU:
+ selection = caja_icon_container_get_selection (container);
+ if (selection == NULL ||
+ g_list_length (selection) != 1 ||
+ selection->data != icon->data)
+ {
+ g_list_free (selection);
+ return FALSE;
+ }
+ g_list_free (selection);
+ g_signal_emit_by_name (container, "context_click_selection", &button_event);
+ break;
+ default :
+ g_assert_not_reached ();
+ break;
+ }
+ }
+ return FALSE;
+}
+
+static gboolean
+caja_icon_canvas_item_accessible_do_action (AtkAction *accessible, int i)
+{
+ CajaIconCanvasItem *item;
+ CajaIconCanvasItemAccessibleActionContext *ctx;
+ CajaIcon *icon;
+ CajaIconContainer *container;
+
+ g_assert (i < LAST_ACTION);
+
+ item = eel_accessibility_get_gobject (ATK_OBJECT (accessible));
+ if (!item)
+ {
+ return FALSE;
+ }
+ icon = item->user_data;
+ container = CAJA_ICON_CONTAINER (EEL_CANVAS_ITEM (item)->canvas);
+ switch (i)
+ {
+ case ACTION_OPEN:
+ case ACTION_MENU:
+ if (container->details->a11y_item_action_queue == NULL)
+ {
+ container->details->a11y_item_action_queue = g_queue_new ();
+ }
+ ctx = g_new (CajaIconCanvasItemAccessibleActionContext, 1);
+ ctx->action_number = i;
+ ctx->item = item;
+ g_queue_push_head (container->details->a11y_item_action_queue, ctx);
+ if (container->details->a11y_item_action_idle_handler == 0)
+ {
+ container->details->a11y_item_action_idle_handler = g_idle_add (caja_icon_canvas_item_accessible_idle_do_action, container);
+ }
+ break;
+ default :
+ g_warning ("Invalid action passed to CajaIconCanvasItemAccessible::do_action");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static int
+caja_icon_canvas_item_accessible_get_n_actions (AtkAction *accessible)
+{
+ return LAST_ACTION;
+}
+
+static const char *
+caja_icon_canvas_item_accessible_action_get_description (AtkAction *accessible,
+ int i)
+{
+ CajaIconCanvasItemAccessiblePrivate *priv;
+
+ g_assert (i < LAST_ACTION);
+
+ priv = accessible_get_priv (ATK_OBJECT (accessible));
+ if (priv->action_descriptions[i])
+ {
+ return priv->action_descriptions[i];
+ }
+ else
+ {
+ return caja_icon_canvas_item_accessible_action_descriptions[i];
+ }
+}
+
+static const char *
+caja_icon_canvas_item_accessible_action_get_name (AtkAction *accessible, int i)
+{
+ g_assert (i < LAST_ACTION);
+
+ return caja_icon_canvas_item_accessible_action_names[i];
+}
+
+static const char *
+caja_icon_canvas_item_accessible_action_get_keybinding (AtkAction *accessible,
+ int i)
+{
+ g_assert (i < LAST_ACTION);
+
+ return NULL;
+}
+
+static gboolean
+caja_icon_canvas_item_accessible_action_set_description (AtkAction *accessible,
+ int i,
+ const char *description)
+{
+ CajaIconCanvasItemAccessiblePrivate *priv;
+
+ g_assert (i < LAST_ACTION);
+
+ priv = accessible_get_priv (ATK_OBJECT (accessible));
+
+ if (priv->action_descriptions[i])
+ {
+ g_free (priv->action_descriptions[i]);
+ }
+ priv->action_descriptions[i] = g_strdup (description);
+
+ return TRUE;
+}
+
+static void
+caja_icon_canvas_item_accessible_action_interface_init (AtkActionIface *iface)
+{
+ iface->do_action = caja_icon_canvas_item_accessible_do_action;
+ iface->get_n_actions = caja_icon_canvas_item_accessible_get_n_actions;
+ iface->get_description = caja_icon_canvas_item_accessible_action_get_description;
+ iface->get_keybinding = caja_icon_canvas_item_accessible_action_get_keybinding;
+ iface->get_name = caja_icon_canvas_item_accessible_action_get_name;
+ iface->set_description = caja_icon_canvas_item_accessible_action_set_description;
+}
+
+static const gchar* caja_icon_canvas_item_accessible_get_name(AtkObject* accessible)
+{
+ CajaIconCanvasItem* item;
+
+ if (accessible->name)
+ {
+ return accessible->name;
+ }
+
+ item = eel_accessibility_get_gobject(accessible);
+
+ if (!item)
+ {
+ return NULL;
+ }
+
+ return item->details->editable_text;
+}
+
+static const gchar* caja_icon_canvas_item_accessible_get_description(AtkObject* accessible)
+{
+ CajaIconCanvasItem* item;
+
+ item = eel_accessibility_get_gobject(accessible);
+
+ if (!item)
+ {
+ return NULL;
+ }
+
+ return item->details->additional_text;
+}
+
+static AtkObject *
+caja_icon_canvas_item_accessible_get_parent (AtkObject *accessible)
+{
+ CajaIconCanvasItem *item;
+
+ item = eel_accessibility_get_gobject (accessible);
+ if (!item)
+ {
+ return NULL;
+ }
+
+ return gtk_widget_get_accessible (GTK_WIDGET (EEL_CANVAS_ITEM (item)->canvas));
+}
+
+static int
+caja_icon_canvas_item_accessible_get_index_in_parent (AtkObject *accessible)
+{
+ CajaIconCanvasItem *item;
+ CajaIconContainer *container;
+ GList *l;
+ CajaIcon *icon;
+ int i;
+
+ item = eel_accessibility_get_gobject (accessible);
+ if (!item)
+ {
+ return -1;
+ }
+
+ container = CAJA_ICON_CONTAINER (EEL_CANVAS_ITEM (item)->canvas);
+
+ l = container->details->icons;
+ i = 0;
+ while (l)
+ {
+ icon = l->data;
+
+ if (icon->item == item)
+ {
+ return i;
+ }
+
+ i++;
+ l = l->next;
+ }
+
+ return -1;
+}
+
+static AtkStateSet*
+caja_icon_canvas_item_accessible_ref_state_set (AtkObject *accessible)
+{
+ AtkStateSet *state_set;
+ CajaIconCanvasItem *item;
+ CajaIconContainer *container;
+ CajaIcon *icon;
+ GList *l;
+ gboolean one_item_selected;
+
+ state_set = ATK_OBJECT_CLASS (accessible_parent_class)->ref_state_set (accessible);
+
+ item = eel_accessibility_get_gobject (accessible);
+ if (!item)
+ {
+ atk_state_set_add_state (state_set, ATK_STATE_DEFUNCT);
+ return state_set;
+ }
+ container = CAJA_ICON_CONTAINER (EEL_CANVAS_ITEM (item)->canvas);
+ if (item->details->is_highlighted_as_keyboard_focus)
+ {
+ atk_state_set_add_state (state_set, ATK_STATE_FOCUSED);
+ }
+ else if (!container->details->keyboard_focus)
+ {
+
+ one_item_selected = FALSE;
+ l = container->details->icons;
+ while (l)
+ {
+ icon = l->data;
+
+ if (icon->item == item)
+ {
+ if (icon->is_selected)
+ {
+ one_item_selected = TRUE;
+ }
+ else
+ {
+ break;
+ }
+ }
+ else if (icon->is_selected)
+ {
+ one_item_selected = FALSE;
+ break;
+ }
+
+ l = l->next;
+ }
+
+ if (one_item_selected)
+ {
+ atk_state_set_add_state (state_set, ATK_STATE_FOCUSED);
+ }
+ }
+
+ return state_set;
+}
+
+static void
+caja_icon_canvas_item_accessible_initialize (AtkObject *accessible,
+ gpointer data)
+{
+ CajaIconCanvasItemAccessiblePrivate *priv;
+
+ if (ATK_OBJECT_CLASS (accessible_parent_class)->initialize)
+ {
+ ATK_OBJECT_CLASS (accessible_parent_class)->initialize (accessible, data);
+ }
+
+ priv = g_new0 (CajaIconCanvasItemAccessiblePrivate, 1);
+ g_object_set_qdata (G_OBJECT (accessible),
+ accessible_private_data_quark,
+ priv);
+}
+
+static void
+caja_icon_canvas_item_accessible_finalize (GObject *object)
+{
+ CajaIconCanvasItemAccessiblePrivate *priv;
+ int i;
+
+ priv = accessible_get_priv (ATK_OBJECT (object));
+
+ for (i = 0; i < LAST_ACTION; i++)
+ {
+ g_free (priv->action_descriptions[i]);
+ }
+ g_free (priv->image_description);
+ g_free (priv->description);
+
+ g_free (priv);
+
+ G_OBJECT_CLASS (accessible_parent_class)->finalize (object);
+}
+
+static void
+caja_icon_canvas_item_accessible_class_init (AtkObjectClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ accessible_parent_class = g_type_class_peek_parent (klass);
+
+ gobject_class->finalize = caja_icon_canvas_item_accessible_finalize;
+
+ klass->get_name = caja_icon_canvas_item_accessible_get_name;
+ klass->get_description = caja_icon_canvas_item_accessible_get_description;
+ klass->get_parent = caja_icon_canvas_item_accessible_get_parent;
+ klass->get_index_in_parent = caja_icon_canvas_item_accessible_get_index_in_parent;
+ klass->ref_state_set = caja_icon_canvas_item_accessible_ref_state_set;
+ klass->initialize = caja_icon_canvas_item_accessible_initialize;
+ accessible_private_data_quark = g_quark_from_static_string ("icon-canvas-item-accessible-private-data");
+}
+
+
+static const gchar* caja_icon_canvas_item_accessible_get_image_description(AtkImage* image)
+{
+ CajaIconCanvasItemAccessiblePrivate* priv;
+ CajaIconCanvasItem* item;
+ CajaIcon* icon;
+ CajaIconContainer* container;
+ char* description;
+
+ priv = accessible_get_priv(ATK_OBJECT(image));
+
+ if (priv->image_description)
+ {
+ return priv->image_description;
+ }
+ else
+ {
+ item = eel_accessibility_get_gobject(ATK_OBJECT (image));
+
+ if (item == NULL)
+ {
+ return NULL;
+ }
+
+ icon = item->user_data;
+ container = CAJA_ICON_CONTAINER(EEL_CANVAS_ITEM(item)->canvas);
+ description = caja_icon_container_get_icon_description(container, icon->data);
+ g_free(priv->description);
+ priv->description = description;
+
+ return priv->description;
+ }
+}
+
+static void
+caja_icon_canvas_item_accessible_get_image_size
+(AtkImage *image,
+ gint *width,
+ gint *height)
+{
+ CajaIconCanvasItem *item;
+
+ item = eel_accessibility_get_gobject (ATK_OBJECT (image));
+
+ if (!item || !item->details->pixbuf)
+ {
+ *width = *height = 0;
+ }
+ else
+ {
+ *width = gdk_pixbuf_get_width (item->details->pixbuf);
+ *height = gdk_pixbuf_get_height (item->details->pixbuf);
+ }
+}
+
+static void
+caja_icon_canvas_item_accessible_get_image_position
+(AtkImage *image,
+ gint *x,
+ gint *y,
+ AtkCoordType coord_type)
+{
+ CajaIconCanvasItem *item;
+ gint x_offset, y_offset, itmp;
+
+ item = eel_accessibility_get_gobject (ATK_OBJECT (image));
+ if (!item)
+ {
+ return;
+ }
+ if (!item->details->canvas_rect.x0 && !item->details->canvas_rect.x1)
+ {
+ return;
+ }
+ else
+ {
+ x_offset = 0;
+ y_offset = 0;
+ if (item->details->text_width)
+ {
+ itmp = item->details->canvas_rect.x0 -
+ item->details->text_rect.x0;
+ if (itmp > x_offset)
+ {
+ x_offset = itmp;
+ }
+ itmp = item->details->canvas_rect.y0 -
+ item->details->text_rect.y0;
+ if (itmp > y_offset)
+ {
+ y_offset = itmp;
+ }
+ }
+ if (item->details->emblem_pixbufs)
+ {
+ itmp = item->details->canvas_rect.x0 -
+ item->details->emblem_rect.x0;
+ if (itmp > x_offset)
+ {
+ x_offset = itmp;
+ }
+ itmp = item->details->canvas_rect.y0 -
+ item->details->emblem_rect.y0;
+ if (itmp > y_offset)
+ {
+ y_offset = itmp;
+ }
+ }
+ }
+ atk_component_get_position (ATK_COMPONENT (image), x, y, coord_type);
+ *x += x_offset;
+ *y += y_offset;
+}
+
+static gboolean
+caja_icon_canvas_item_accessible_set_image_description
+(AtkImage *image,
+ const gchar *description)
+{
+ CajaIconCanvasItemAccessiblePrivate *priv;
+
+ priv = accessible_get_priv (ATK_OBJECT (image));
+
+ g_free (priv->image_description);
+ priv->image_description = g_strdup (description);
+
+ return TRUE;
+}
+
+static void
+caja_icon_canvas_item_accessible_image_interface_init (AtkImageIface *iface)
+{
+ iface->get_image_description = caja_icon_canvas_item_accessible_get_image_description;
+ iface->set_image_description = caja_icon_canvas_item_accessible_set_image_description;
+ iface->get_image_size = caja_icon_canvas_item_accessible_get_image_size;
+ iface->get_image_position = caja_icon_canvas_item_accessible_get_image_position;
+}
+
+static gint
+caja_icon_canvas_item_accessible_get_offset_at_point (AtkText *text,
+ gint x,
+ gint y,
+ AtkCoordType coords)
+{
+ gint real_x, real_y, real_width, real_height;
+ CajaIconCanvasItem *item;
+ gint editable_height;
+ gint offset = 0;
+ gint index;
+ PangoLayout *layout, *editable_layout, *additional_layout;
+ PangoRectangle rect0;
+ char *icon_text;
+ gboolean have_editable;
+ gboolean have_additional;
+ gint text_offset;
+
+ atk_component_get_extents (ATK_COMPONENT (text), &real_x, &real_y,
+ &real_width, &real_height, coords);
+
+ x -= real_x;
+ y -= real_y;
+
+ item = eel_accessibility_get_gobject (ATK_OBJECT (text));
+
+ if (item->details->pixbuf)
+ {
+ y -= gdk_pixbuf_get_height (item->details->pixbuf);
+ }
+ have_editable = item->details->editable_text != NULL &&
+ item->details->editable_text[0] != '\0';
+ have_additional = item->details->additional_text != NULL &&item->details->additional_text[0] != '\0';
+
+ editable_layout = NULL;
+ additional_layout = NULL;
+ if (have_editable)
+ {
+ editable_layout = get_label_layout (&item->details->editable_text_layout, item, item->details->editable_text);
+ prepare_pango_layout_for_draw (item, editable_layout);
+ pango_layout_get_pixel_size (editable_layout, NULL, &editable_height);
+ if (y >= editable_height &&
+ have_additional)
+ {
+ prepare_pango_layout_for_draw (item, editable_layout);
+ additional_layout = get_label_layout (&item->details->additional_text_layout, item, item->details->additional_text);
+ layout = additional_layout;
+ icon_text = item->details->additional_text;
+ y -= editable_height + LABEL_LINE_SPACING;
+ }
+ else
+ {
+ layout = editable_layout;
+ icon_text = item->details->editable_text;
+ }
+ }
+ else if (have_additional)
+ {
+ additional_layout = get_label_layout (&item->details->additional_text_layout, item, item->details->additional_text);
+ prepare_pango_layout_for_draw (item, additional_layout);
+ layout = additional_layout;
+ icon_text = item->details->additional_text;
+ }
+ else
+ {
+ return 0;
+ }
+
+ text_offset = 0;
+ if (have_editable)
+ {
+ pango_layout_index_to_pos (editable_layout, 0, &rect0);
+ text_offset = PANGO_PIXELS (rect0.x);
+ }
+ if (have_additional)
+ {
+ gint itmp;
+
+ pango_layout_index_to_pos (additional_layout, 0, &rect0);
+ itmp = PANGO_PIXELS (rect0.x);
+ if (itmp < text_offset)
+ {
+ text_offset = itmp;
+ }
+ }
+ pango_layout_index_to_pos (layout, 0, &rect0);
+ x += text_offset;
+ if (!pango_layout_xy_to_index (layout,
+ x * PANGO_SCALE,
+ y * PANGO_SCALE,
+ &index, NULL))
+ {
+ if (x < 0 || y < 0)
+ {
+ index = 0;
+ }
+ else
+ {
+ index = -1;
+ }
+ }
+ if (index == -1)
+ {
+ offset = g_utf8_strlen (icon_text, -1);
+ }
+ else
+ {
+ offset = g_utf8_pointer_to_offset (icon_text, icon_text + index);
+ }
+ if (layout == additional_layout)
+ {
+ offset += g_utf8_strlen (item->details->editable_text, -1);
+ }
+
+ if (editable_layout != NULL)
+ {
+ g_object_unref (editable_layout);
+ }
+
+ if (additional_layout != NULL)
+ {
+ g_object_unref (additional_layout);
+ }
+
+ return offset;
+}
+
+static void
+caja_icon_canvas_item_accessible_get_character_extents (AtkText *text,
+ gint offset,
+ gint *x,
+ gint *y,
+ gint *width,
+ gint *height,
+ AtkCoordType coords)
+{
+ gint pos_x, pos_y;
+ gint len, byte_offset;
+ gint editable_height;
+ gchar *icon_text;
+ CajaIconCanvasItem *item;
+ PangoLayout *layout, *editable_layout, *additional_layout;
+ PangoRectangle rect;
+ PangoRectangle rect0;
+ gboolean have_editable;
+ gint text_offset;
+
+ atk_component_get_position (ATK_COMPONENT (text), &pos_x, &pos_y, coords);
+ item = eel_accessibility_get_gobject (ATK_OBJECT (text));
+
+ if (item->details->pixbuf)
+ {
+ pos_y += gdk_pixbuf_get_height (item->details->pixbuf);
+ }
+
+ have_editable = item->details->editable_text != NULL &&
+ item->details->editable_text[0] != '\0';
+ if (have_editable)
+ {
+ len = g_utf8_strlen (item->details->editable_text, -1);
+ }
+ else
+ {
+ len = 0;
+ }
+
+ editable_layout = get_label_layout (&item->details->editable_text_layout, item, item->details->editable_text);
+ additional_layout = get_label_layout (&item->details->additional_text_layout, item, item->details->additional_text);
+
+ if (offset < len)
+ {
+ icon_text = item->details->editable_text;
+ layout = editable_layout;
+ }
+ else
+ {
+ offset -= len;
+ icon_text = item->details->additional_text;
+ layout = additional_layout;
+ pos_y += LABEL_LINE_SPACING;
+ if (have_editable)
+ {
+ pango_layout_get_pixel_size (editable_layout, NULL, &editable_height);
+ pos_y += editable_height;
+ }
+ }
+ byte_offset = g_utf8_offset_to_pointer (icon_text, offset) - icon_text;
+ pango_layout_index_to_pos (layout, byte_offset, &rect);
+ text_offset = 0;
+ if (have_editable)
+ {
+ pango_layout_index_to_pos (editable_layout, 0, &rect0);
+ text_offset = PANGO_PIXELS (rect0.x);
+ }
+ if (item->details->additional_text != NULL &&
+ item->details->additional_text[0] != '\0')
+ {
+ gint itmp;
+
+ pango_layout_index_to_pos (additional_layout, 0, &rect0);
+ itmp = PANGO_PIXELS (rect0.x);
+ if (itmp < text_offset)
+ {
+ text_offset = itmp;
+ }
+ }
+
+ g_object_unref (editable_layout);
+ g_object_unref (additional_layout);
+
+ *x = pos_x + PANGO_PIXELS (rect.x) - text_offset;
+ *y = pos_y + PANGO_PIXELS (rect.y);
+ *width = PANGO_PIXELS (rect.width);
+ *height = PANGO_PIXELS (rect.height);
+}
+
+static void
+caja_icon_canvas_item_accessible_text_interface_init (AtkTextIface *iface)
+{
+ iface->get_text = eel_accessibility_text_get_text;
+ iface->get_character_at_offset = eel_accessibility_text_get_character_at_offset;
+ iface->get_text_before_offset = eel_accessibility_text_get_text_before_offset;
+ iface->get_text_at_offset = eel_accessibility_text_get_text_at_offset;
+ iface->get_text_after_offset = eel_accessibility_text_get_text_after_offset;
+ iface->get_character_count = eel_accessibility_text_get_character_count;
+ iface->get_character_extents = caja_icon_canvas_item_accessible_get_character_extents;
+ iface->get_offset_at_point = caja_icon_canvas_item_accessible_get_offset_at_point;
+}
+
+static GType
+caja_icon_canvas_item_accessible_get_type (void)
+{
+ static GType type = 0;
+
+ if (!type)
+ {
+ const GInterfaceInfo atk_image_info =
+ {
+ (GInterfaceInitFunc)
+ caja_icon_canvas_item_accessible_image_interface_init,
+ (GInterfaceFinalizeFunc) NULL,
+ NULL
+ };
+
+ const GInterfaceInfo atk_text_info =
+ {
+ (GInterfaceInitFunc)
+ caja_icon_canvas_item_accessible_text_interface_init,
+ (GInterfaceFinalizeFunc) NULL,
+ NULL
+ };
+
+ const GInterfaceInfo atk_action_info =
+ {
+ (GInterfaceInitFunc)
+ caja_icon_canvas_item_accessible_action_interface_init,
+ (GInterfaceFinalizeFunc) NULL,
+ NULL
+ };
+
+ type = eel_accessibility_create_derived_type (
+ "CajaIconCanvasItemAccessibility",
+ EEL_TYPE_CANVAS_ITEM,
+ caja_icon_canvas_item_accessible_class_init);
+
+ if (type != G_TYPE_INVALID)
+ {
+ g_type_add_interface_static (
+ type, ATK_TYPE_IMAGE, &atk_image_info);
+
+ g_type_add_interface_static (
+ type, ATK_TYPE_TEXT, &atk_text_info);
+
+ g_type_add_interface_static (
+ type, ATK_TYPE_ACTION, &atk_action_info);
+
+ }
+ }
+
+ return type;
+}
+
+static AtkObject *
+caja_icon_canvas_item_accessible_create (GObject *for_object)
+{
+ GType type;
+ AtkObject *accessible;
+ CajaIconCanvasItem *item;
+ GString *item_text;
+
+ item = CAJA_ICON_CANVAS_ITEM (for_object);
+ g_assert (item != NULL);
+
+ type = caja_icon_canvas_item_accessible_get_type ();
+
+ if (type == G_TYPE_INVALID)
+ {
+ return atk_no_op_object_new (for_object);
+ }
+
+ item_text = g_string_new (NULL);
+ if (item->details->editable_text)
+ {
+ g_string_append (item_text, item->details->editable_text);
+ }
+ if (item->details->additional_text)
+ {
+ g_string_append (item_text, item->details->additional_text);
+ }
+ item->details->text_util = gail_text_util_new ();
+ gail_text_util_text_setup (item->details->text_util,
+ item_text->str);
+ g_string_free (item_text, TRUE);
+
+ accessible = g_object_new (type, NULL);
+ accessible = eel_accessibility_set_atk_object_return
+ (for_object, accessible);
+ atk_object_set_role (accessible, ATK_ROLE_ICON);
+ return accessible;
+}
+
+EEL_ACCESSIBLE_FACTORY (caja_icon_canvas_item_accessible_get_type (),
+ "CajaIconCanvasItemAccessibilityFactory",
+ caja_icon_canvas_item_accessible,
+ caja_icon_canvas_item_accessible_create)
+
+
+static GailTextUtil *
+caja_icon_canvas_item_get_text (GObject *text)
+{
+ return CAJA_ICON_CANVAS_ITEM (text)->details->text_util;
+}
+
+static void
+caja_icon_canvas_item_text_interface_init (EelAccessibleTextIface *iface)
+{
+ iface->get_text = caja_icon_canvas_item_get_text;
+}
+
+void
+caja_icon_canvas_item_set_entire_text (CajaIconCanvasItem *item,
+ gboolean entire_text)
+{
+ if (item->details->entire_text != entire_text)
+ {
+ item->details->entire_text = entire_text;
+
+ caja_icon_canvas_item_invalidate_label_size (item);
+ eel_canvas_item_request_update (EEL_CANVAS_ITEM (item));
+ }
+}
+
+
+/* Class initialization function for the icon canvas item. */
+static void
+caja_icon_canvas_item_class_init (CajaIconCanvasItemClass *class)
+{
+ GObjectClass *object_class;
+ EelCanvasItemClass *item_class;
+
+ object_class = G_OBJECT_CLASS (class);
+ item_class = EEL_CANVAS_ITEM_CLASS (class);
+
+ object_class->finalize = caja_icon_canvas_item_finalize;
+ object_class->set_property = caja_icon_canvas_item_set_property;
+ object_class->get_property = caja_icon_canvas_item_get_property;
+
+ g_object_class_install_property (
+ object_class,
+ PROP_EDITABLE_TEXT,
+ g_param_spec_string ("editable_text",
+ "editable text",
+ "the editable label",
+ "", G_PARAM_READWRITE));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_ADDITIONAL_TEXT,
+ g_param_spec_string ("additional_text",
+ "additional text",
+ "some more text",
+ "", G_PARAM_READWRITE));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_HIGHLIGHTED_FOR_SELECTION,
+ g_param_spec_boolean ("highlighted_for_selection",
+ "highlighted for selection",
+ "whether we are highlighted for a selection",
+ FALSE, G_PARAM_READWRITE));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_HIGHLIGHTED_AS_KEYBOARD_FOCUS,
+ g_param_spec_boolean ("highlighted_as_keyboard_focus",
+ "highlighted as keyboard focus",
+ "whether we are highlighted to render keyboard focus",
+ FALSE, G_PARAM_READWRITE));
+
+
+ g_object_class_install_property (
+ object_class,
+ PROP_HIGHLIGHTED_FOR_DROP,
+ g_param_spec_boolean ("highlighted_for_drop",
+ "highlighted for drop",
+ "whether we are highlighted for a D&D drop",
+ FALSE, G_PARAM_READWRITE));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_HIGHLIGHTED_FOR_CLIPBOARD,
+ g_param_spec_boolean ("highlighted_for_clipboard",
+ "highlighted for clipboard",
+ "whether we are highlighted for a clipboard paste (after we have been cut)",
+ FALSE, G_PARAM_READWRITE));
+
+ item_class->update = caja_icon_canvas_item_update;
+ item_class->draw = caja_icon_canvas_item_draw;
+ item_class->point = caja_icon_canvas_item_point;
+ item_class->translate = caja_icon_canvas_item_translate;
+ item_class->bounds = caja_icon_canvas_item_bounds;
+ item_class->event = caja_icon_canvas_item_event;
+
+ EEL_OBJECT_SET_FACTORY (CAJA_TYPE_ICON_CANVAS_ITEM,
+ caja_icon_canvas_item_accessible);
+
+ g_type_class_add_private (class, sizeof (CajaIconCanvasItemDetails));
+}
+
+