aboutsummaryrefslogtreecommitdiffstats
path: root/widgets/shortcut-bar/e-icon-bar-text-item.c
diff options
context:
space:
mode:
authorArturo Espinosa <unammx@src.gnome.org>2000-01-06 13:48:27 +0800
committerArturo Espinosa <unammx@src.gnome.org>2000-01-06 13:48:27 +0800
commited4e8afecd5a7f9fab04715ca26a67b2917d78f3 (patch)
treefb33e24ef2eb7c01b225e094d69228c395b1afb4 /widgets/shortcut-bar/e-icon-bar-text-item.c
parent36e5e42d31a56499b1b2534fdf9f6a720b3813e0 (diff)
downloadgsoc2013-evolution-ed4e8afecd5a7f9fab04715ca26a67b2917d78f3.tar
gsoc2013-evolution-ed4e8afecd5a7f9fab04715ca26a67b2917d78f3.tar.gz
gsoc2013-evolution-ed4e8afecd5a7f9fab04715ca26a67b2917d78f3.tar.bz2
gsoc2013-evolution-ed4e8afecd5a7f9fab04715ca26a67b2917d78f3.tar.lz
gsoc2013-evolution-ed4e8afecd5a7f9fab04715ca26a67b2917d78f3.tar.xz
gsoc2013-evolution-ed4e8afecd5a7f9fab04715ca26a67b2917d78f3.tar.zst
gsoc2013-evolution-ed4e8afecd5a7f9fab04715ca26a67b2917d78f3.zip
Lots of reorganization to get the Evolution shell to begin its life. It
Lots of reorganization to get the Evolution shell to begin its life. It also includes a new evolution widget from Damon. Miguel. svn path=/trunk/; revision=1536
Diffstat (limited to 'widgets/shortcut-bar/e-icon-bar-text-item.c')
-rw-r--r--widgets/shortcut-bar/e-icon-bar-text-item.c1696
1 files changed, 1696 insertions, 0 deletions
diff --git a/widgets/shortcut-bar/e-icon-bar-text-item.c b/widgets/shortcut-bar/e-icon-bar-text-item.c
new file mode 100644
index 0000000000..5548c630b3
--- /dev/null
+++ b/widgets/shortcut-bar/e-icon-bar-text-item.c
@@ -0,0 +1,1696 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * Author :
+ * Damon Chaplin <damon@gtk.org>
+ *
+ * Copyright 1999, Helix Code, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+/*
+ * Based on gnome-icon-text-item: an editable text block with word wrapping
+ * for the GNOME canvas.
+ *
+ * Copyright (C) 1998, 1999 The Free Software Foundation
+ *
+ * Authors: Miguel de Icaza <miguel@gnu.org>
+ * Federico Mena <federico@gimp.org>
+ */
+
+/*
+ * EIconBarTextItem - An editable canvas text item for the EIconBar.
+ */
+
+#include <math.h>
+#include <gdk/gdkkeysyms.h>
+#include <gtk/gtkmain.h>
+#include <gtk/gtksignal.h>
+#include <gtk/gtkwindow.h>
+#include <libgnome/gnome-defs.h>
+#include <libgnome/gnome-i18n.h>
+
+#include "e-icon-bar-text-item.h"
+
+
+/* Margins used to display the information */
+#define MARGIN_X 2
+#define MARGIN_Y 2
+
+/* Default fontset to be used if the user specified fontset is not found */
+#define DEFAULT_FONT_NAME "-adobe-helvetica-medium-r-normal--*-100-*-*-*-*-*-*," \
+ "-*-*-medium-r-normal--10-*-*-*-*-*-*-*,*"
+
+/* Separators for text layout */
+#define DEFAULT_SEPARATORS " \t-.[]#"
+
+/* This is the string to draw when the text is clipped, e.g. '...'. */
+static gchar *e_icon_bar_text_item_ellipsis;
+
+/* Aliases to minimize screen use in my laptop */
+#define ITI(x) E_ICON_BAR_TEXT_ITEM (x)
+#define ITI_CLASS(x) E_ICON_BAR_TEXT_ITEM_CLASS (x)
+#define IS_ITI(x) E_IS_ICON_BAR_TEXT_ITEM (x)
+
+
+typedef EIconBarTextItem Iti;
+
+/* Private part of the EIconBarTextItem structure */
+typedef struct {
+ /* Font */
+ GdkFont *font;
+
+ /* Hack: create an offscreen window and place an entry inside it */
+ GtkEntry *entry;
+ GtkWidget *entry_top;
+
+ /* Whether the user pressed the mouse while the item was unselected */
+ guint unselected_click : 1;
+
+ /* Whether we need to update the position */
+ guint need_pos_update : 1;
+
+ /* Whether we need to update the font */
+ guint need_font_update : 1;
+
+ /* Whether we need to update the text */
+ guint need_text_update : 1;
+
+ /* Whether we need to update because the editing/selected state changed */
+ guint need_state_update : 1;
+} ItiPrivate;
+
+typedef struct _EIconBarTextItemInfoRow EIconBarTextItemInfoRow;
+
+struct _EIconBarTextItemInfoRow {
+ gchar *text;
+ gint width;
+ GdkWChar *text_wc; /* text in wide characters */
+ gint text_length; /* number of characters */
+};
+
+struct _EIconBarTextItemInfo {
+ GList *rows;
+ GdkFont *font;
+ gint width;
+ gint height;
+ gint baseline_skip;
+};
+
+static GnomeCanvasItemClass *parent_class;
+
+enum {
+ ARG_0,
+ ARG_XALIGN,
+ ARG_JUSTIFY,
+ ARG_MAX_LINES,
+ ARG_SHOW_ELLIPSIS
+};
+
+enum {
+ TEXT_CHANGED,
+ HEIGHT_CHANGED,
+ WIDTH_CHANGED,
+ EDITING_STARTED,
+ EDITING_STOPPED,
+ SELECTION_STARTED,
+ SELECTION_STOPPED,
+ LAST_SIGNAL
+};
+
+static guint iti_signals [LAST_SIGNAL] = { 0 };
+
+static GdkFont *default_font;
+
+static void e_icon_bar_text_item_free_info (EIconBarTextItemInfo *ti);
+static EIconBarTextItemInfo *e_icon_bar_text_item_layout_text (EIconBarTextItem *iti, GdkFont *font, const gchar *text, const gchar *separators, gint max_width, gboolean confine);
+static void e_icon_bar_text_item_paint_text (EIconBarTextItem *iti,
+ EIconBarTextItemInfo *ti,
+ GdkDrawable *drawable,
+ GdkGC *gc,
+ gint x,
+ gint y,
+ GtkJustification just);
+
+
+/* Stops the editing state of an icon text item */
+static void
+iti_stop_editing (Iti *iti)
+{
+ ItiPrivate *priv;
+
+ priv = iti->priv;
+
+ iti->editing = FALSE;
+
+ gtk_widget_destroy (priv->entry_top);
+ priv->entry = NULL;
+ priv->entry_top = NULL;
+
+ priv->need_state_update = TRUE;
+ gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (iti));
+
+ gtk_signal_emit (GTK_OBJECT (iti), iti_signals[EDITING_STOPPED]);
+}
+
+/* Lays out the text in an icon item */
+static void
+layout_text (Iti *iti)
+{
+ ItiPrivate *priv;
+ char *text;
+ int old_width, old_height;
+ int width, height;
+
+ priv = iti->priv;
+
+ /* Save old size */
+
+ if (iti->ti) {
+ old_width = iti->ti->width + 2 * MARGIN_X;
+ old_height = iti->ti->height + 2 * MARGIN_Y;
+
+ e_icon_bar_text_item_free_info (iti->ti);
+ } else {
+ old_width = 2 * MARGIN_X;
+ old_height = 2 * MARGIN_Y;
+ }
+
+ /* Change the text layout */
+
+ if (iti->editing)
+ text = gtk_entry_get_text (priv->entry);
+ else
+ text = iti->text;
+
+ iti->ti = e_icon_bar_text_item_layout_text (iti, priv->font,
+ text,
+ DEFAULT_SEPARATORS,
+ iti->width - 2 * MARGIN_X,
+ TRUE);
+
+ /* Check the sizes and see if we need to emit any signals */
+
+ width = iti->ti->width + 2 * MARGIN_X;
+ height = iti->ti->height + 2 * MARGIN_Y;
+
+ if (width != old_width)
+ gtk_signal_emit (GTK_OBJECT (iti), iti_signals[WIDTH_CHANGED]);
+
+ if (height != old_height)
+ gtk_signal_emit (GTK_OBJECT (iti), iti_signals[HEIGHT_CHANGED]);
+}
+
+/* Accepts the text in the off-screen entry of an icon text item */
+static void
+iti_edition_accept (Iti *iti)
+{
+ ItiPrivate *priv;
+ gboolean accept;
+
+ priv = iti->priv;
+ accept = TRUE;
+
+ gtk_signal_emit (GTK_OBJECT (iti), iti_signals [TEXT_CHANGED], &accept);
+
+ if (iti->editing){
+ if (accept) {
+ if (iti->is_text_allocated)
+ g_free (iti->text);
+
+ iti->text = g_strdup (gtk_entry_get_text (priv->entry));
+ iti->is_text_allocated = 1;
+ }
+
+ iti_stop_editing (iti);
+ }
+
+ priv->need_text_update = TRUE;
+ gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (iti));
+}
+
+/* Callback used when the off-screen entry of an icon text item is activated.
+ * When this happens, we have to accept edition.
+ */
+static void
+iti_entry_activate (GtkWidget *entry, Iti *iti)
+{
+ iti_edition_accept (iti);
+}
+
+/* Starts the editing state of an icon text item */
+static void
+iti_start_editing (Iti *iti)
+{
+ ItiPrivate *priv;
+
+ priv = iti->priv;
+
+ if (iti->editing)
+ return;
+
+ /* Trick: The actual edition of the entry takes place in a GtkEntry
+ * which is placed offscreen. That way we get all of the advantages
+ * from GtkEntry without duplicating code. Yes, this is a hack.
+ */
+ priv->entry = (GtkEntry *) gtk_entry_new ();
+ gtk_entry_set_text (priv->entry, iti->text);
+ gtk_signal_connect (GTK_OBJECT (priv->entry), "activate",
+ GTK_SIGNAL_FUNC (iti_entry_activate), iti);
+
+ priv->entry_top = gtk_window_new (GTK_WINDOW_POPUP);
+ gtk_container_add (GTK_CONTAINER (priv->entry_top), GTK_WIDGET (priv->entry));
+ gtk_widget_set_uposition (priv->entry_top, 20000, 20000);
+ gtk_widget_show_all (priv->entry_top);
+
+ gtk_editable_select_region (GTK_EDITABLE (priv->entry), 0, -1);
+
+ iti->editing = TRUE;
+
+ priv->need_text_update = TRUE;
+ priv->need_state_update = TRUE;
+ gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (iti));
+
+ gtk_signal_emit (GTK_OBJECT (iti), iti_signals[EDITING_STARTED]);
+}
+
+/* Destroy method handler for the icon text item */
+static void
+iti_destroy (GtkObject *object)
+{
+ Iti *iti;
+ ItiPrivate *priv;
+ GnomeCanvasItem *item;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (IS_ITI (object));
+
+ iti = ITI (object);
+ priv = iti->priv;
+ item = GNOME_CANVAS_ITEM (object);
+
+ /* FIXME: stop selection and editing */
+
+ /* Queue redraw of bounding box */
+
+ gnome_canvas_request_redraw (item->canvas, item->x1, item->y1, item->x2, item->y2);
+
+ /* Free everything */
+
+ if (iti->fontname)
+ g_free (iti->fontname);
+
+ if (iti->text && iti->is_text_allocated)
+ g_free (iti->text);
+
+ if (iti->ti)
+ e_icon_bar_text_item_free_info (iti->ti);
+
+ if (priv->font)
+ gdk_font_unref (priv->font);
+
+ if (priv->entry_top)
+ gtk_widget_destroy (priv->entry_top);
+
+ g_free (priv);
+
+ if (GTK_OBJECT_CLASS (parent_class)->destroy)
+ (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+
+/* set_arg handler for the icon text item */
+static void
+iti_set_arg (GtkObject *object, GtkArg *arg, guint arg_id)
+{
+ Iti *iti;
+ GnomeCanvasItem *item;
+ ItiPrivate *priv;
+ gfloat xalign;
+ gint max_lines;
+ gboolean show_ellipsis;
+ GtkJustification justification;
+
+ iti = ITI (object);
+ item = GNOME_CANVAS_ITEM (object);
+ priv = iti->priv;
+
+ switch (arg_id) {
+ case ARG_XALIGN:
+ xalign = GTK_VALUE_FLOAT (*arg);
+ if (iti->xalign != xalign) {
+ iti->xalign = xalign;
+ priv->need_pos_update = TRUE;
+ gnome_canvas_item_request_update (item);
+ }
+ break;
+ case ARG_JUSTIFY:
+ justification = GTK_VALUE_ENUM (*arg);
+ if (iti->justification != justification) {
+ iti->justification = justification;
+ priv->need_text_update = TRUE;
+ gnome_canvas_item_request_update (item);
+ }
+ break;
+ case ARG_MAX_LINES:
+ max_lines = GTK_VALUE_INT (*arg);
+ if (iti->max_lines != max_lines) {
+ iti->max_lines = max_lines;
+ priv->need_text_update = TRUE;
+ gnome_canvas_item_request_update (item);
+ }
+ break;
+ case ARG_SHOW_ELLIPSIS:
+ show_ellipsis = GTK_VALUE_BOOL (*arg);
+ if (iti->show_ellipsis != show_ellipsis) {
+ iti->show_ellipsis = show_ellipsis;
+ priv->need_text_update = TRUE;
+ gnome_canvas_item_request_update (item);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+iti_get_arg (GtkObject *object, GtkArg *arg, guint arg_id)
+{
+ Iti *iti;
+ ItiPrivate *priv;
+
+ iti = ITI (object);
+ priv = iti->priv;
+
+ switch (arg_id) {
+ case ARG_XALIGN:
+ GTK_VALUE_FLOAT (*arg) = iti->xalign;
+ break;
+ case ARG_JUSTIFY:
+ GTK_VALUE_ENUM (*arg) = iti->justification;
+ break;
+ case ARG_MAX_LINES:
+ GTK_VALUE_INT (*arg) = iti->max_lines;
+ break;
+ case ARG_SHOW_ELLIPSIS:
+ GTK_VALUE_BOOL (*arg) = iti->show_ellipsis;
+ break;
+ default:
+ arg->type = GTK_TYPE_INVALID;
+ break;
+ }
+}
+
+/* Loads the default font for icon text items if necessary */
+static GdkFont *
+get_default_font (void)
+{
+ if (!default_font) {
+ /* FIXME: this is never unref-ed */
+ default_font = gdk_fontset_load (DEFAULT_FONT_NAME);
+ g_assert (default_font != NULL);
+ }
+
+ return gdk_font_ref (default_font);
+}
+
+/* Recomputes the bounding box of an icon text item */
+static void
+recompute_bounding_box (Iti *iti)
+{
+ GnomeCanvasItem *item;
+ double affine[6];
+ ArtPoint p, q;
+ int x1, y1, x2, y2;
+ int width, height;
+
+ item = GNOME_CANVAS_ITEM (iti);
+
+ /* Compute width, height, position */
+
+ width = iti->ti->width + 2 * MARGIN_X;
+ height = iti->ti->height + 2 * MARGIN_Y;
+
+ x1 = iti->x + (iti->width - width) * iti->xalign;
+ y1 = iti->y;
+ x2 = x1 + width;
+ y2 = y1 + height;
+
+ /* Translate to world coordinates */
+
+ gnome_canvas_item_i2w_affine (item, affine);
+
+ p.x = x1;
+ p.y = y1;
+ art_affine_point (&q, &p, affine);
+ item->x1 = q.x;
+ item->y1 = q.y;
+
+ p.x = x2;
+ p.y = y2;
+ art_affine_point (&q, &p, affine);
+ item->x2 = q.x;
+ item->y2 = q.y;
+}
+
+/* Update method for the icon text item */
+static void
+iti_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags)
+{
+ Iti *iti;
+ ItiPrivate *priv;
+
+ iti = ITI (item);
+ priv = iti->priv;
+
+ if (parent_class->update)
+ (* parent_class->update) (item, affine, clip_path, flags);
+
+ /* If necessary, queue a redraw of the old bounding box */
+
+ if ((flags & GNOME_CANVAS_UPDATE_VISIBILITY)
+ || (flags & GNOME_CANVAS_UPDATE_AFFINE)
+ || priv->need_pos_update
+ || priv->need_font_update
+ || priv->need_text_update)
+ gnome_canvas_request_redraw (item->canvas, item->x1, item->y1, item->x2, item->y2);
+
+ if (priv->need_text_update)
+ layout_text (iti);
+
+ /* Compute new bounds */
+
+ if (priv->need_pos_update
+ || priv->need_font_update
+ || priv->need_text_update)
+ recompute_bounding_box (iti);
+
+ /* Queue redraw */
+
+ gnome_canvas_request_redraw (item->canvas, item->x1, item->y1, item->x2, item->y2);
+
+ priv->need_pos_update = FALSE;
+ priv->need_font_update = FALSE;
+ priv->need_text_update = FALSE;
+ priv->need_state_update = FALSE;
+}
+
+/* Draw the icon text item's text when it is being edited */
+static void
+iti_paint_text (Iti *iti, GdkDrawable *drawable, int x, int y)
+{
+ ItiPrivate *priv;
+ EIconBarTextItemInfoRow *row;
+ EIconBarTextItemInfo *ti;
+ GtkStyle *style;
+ GdkGC *fg_gc, *bg_gc;
+ GdkGC *gc, *bgc, *sgc, *bsgc;
+ GList *item;
+ int xpos, len;
+
+ priv = iti->priv;
+ style = GTK_WIDGET (GNOME_CANVAS_ITEM (iti)->canvas)->style;
+
+ ti = iti->ti;
+ len = 0;
+ y += ti->font->ascent;
+
+ /*
+ * Pointers to all of the GCs we use
+ */
+ gc = style->black_gc;
+ bgc = style->white_gc;
+ sgc = style->fg_gc [GTK_STATE_SELECTED];
+ bsgc = style->bg_gc [GTK_STATE_SELECTED];
+
+ for (item = ti->rows; item; item = item->next, len += (row ? row->text_length : 0)) {
+ GdkWChar *text_wc;
+ int text_length;
+ int cursor, offset, i;
+ int sel_start, sel_end;
+
+ row = item->data;
+
+ if (!row) {
+ y += ti->baseline_skip;
+ continue;
+ }
+
+ text_wc = row->text_wc;
+ text_length = row->text_length;
+
+ switch (iti->justification) {
+ case GTK_JUSTIFY_LEFT:
+ xpos = 0;
+ break;
+
+ case GTK_JUSTIFY_RIGHT:
+ xpos = ti->width - row->width;
+ break;
+
+ case GTK_JUSTIFY_CENTER:
+ xpos = (ti->width - row->width) / 2;
+ break;
+
+ default:
+ /* Anyone care to implement GTK_JUSTIFY_FILL? */
+ g_warning ("Justification type %d not supported. Using left-justification.",
+ (int) iti->justification);
+ xpos = 0;
+ }
+
+ sel_start = GTK_EDITABLE (priv->entry)->selection_start_pos - len;
+ sel_end = GTK_EDITABLE (priv->entry)->selection_end_pos - len;
+ offset = 0;
+ cursor = GTK_EDITABLE (priv->entry)->current_pos - len;
+
+ for (i = 0; *text_wc; text_wc++, i++) {
+ int size, px;
+
+ size = gdk_text_width_wc (ti->font, text_wc, 1);
+
+ if (i >= sel_start && i < sel_end) {
+ fg_gc = sgc;
+ bg_gc = bsgc;
+ } else {
+ fg_gc = gc;
+ bg_gc = bgc;
+ }
+
+ px = x + xpos + offset;
+ gdk_draw_rectangle (drawable,
+ bg_gc,
+ TRUE,
+ px,
+ y - ti->font->ascent,
+ size, ti->baseline_skip);
+
+ gdk_draw_text_wc (drawable,
+ ti->font,
+ fg_gc,
+ px, y,
+ text_wc, 1);
+
+ if (cursor == i)
+ gdk_draw_line (drawable,
+ gc,
+ px - 1,
+ y - ti->font->ascent,
+ px - 1,
+ y + ti->font->descent - 1);
+
+ offset += size;
+ }
+
+ if (cursor == i) {
+ int px = x + xpos + offset;
+
+ gdk_draw_line (drawable,
+ gc,
+ px - 1,
+ y - ti->font->ascent,
+ px - 1,
+ y + ti->font->descent - 1);
+ }
+
+ y += ti->baseline_skip;
+ }
+}
+
+/* Draw method handler for the icon text item */
+static void
+iti_draw (GnomeCanvasItem *item, GdkDrawable *drawable, int x, int y, int width, int height)
+{
+ Iti *iti;
+ GtkStyle *style;
+ int w, h;
+ int xofs, yofs;
+
+ iti = ITI (item);
+
+ if (iti->ti) {
+ w = iti->ti->width + 2 * MARGIN_X;
+ h = iti->ti->height + 2 * MARGIN_Y;
+ } else {
+ w = 2 * MARGIN_X;
+ h = 2 * MARGIN_Y;
+ }
+
+ xofs = item->x1 - x;
+ yofs = item->y1 - y;
+
+ style = GTK_WIDGET (item->canvas)->style;
+
+ if (iti->selected && !iti->editing)
+ gdk_draw_rectangle (drawable,
+ style->bg_gc[GTK_STATE_SELECTED],
+ TRUE,
+ xofs, yofs,
+ w, h);
+
+ if (iti->editing) {
+ gdk_draw_rectangle (drawable,
+ style->white_gc,
+ TRUE,
+ xofs + 1, yofs + 1,
+ w - 2, h - 2);
+ gdk_draw_rectangle (drawable,
+ style->black_gc,
+ FALSE,
+ xofs, yofs,
+ w - 1, h - 1);
+
+ iti_paint_text (iti, drawable, xofs + MARGIN_X, yofs + MARGIN_Y);
+ } else
+ e_icon_bar_text_item_paint_text (iti, iti->ti,
+ drawable,
+ style->fg_gc[(iti->selected
+ ? GTK_STATE_SELECTED
+ : GTK_STATE_NORMAL)],
+ xofs + MARGIN_X,
+ yofs + MARGIN_Y,
+ iti->justification);
+}
+
+/* Point method handler for the icon text item */
+static double
+iti_point (GnomeCanvasItem *item, double x, double y, int cx, int cy, GnomeCanvasItem **actual_item)
+{
+ double dx, dy;
+
+ *actual_item = item;
+
+ if (cx < item->x1)
+ dx = item->x1 - cx;
+ else if (cx > item->x2)
+ dx = cx - item->x2;
+ else
+ dx = 0.0;
+
+ if (cy < item->y1)
+ dy = item->y1 - cy;
+ else if (cy > item->y2)
+ dy = cy - item->y2;
+ else
+ dy = 0.0;
+
+ return sqrt (dx * dx + dy * dy);
+}
+
+/* Given X, Y, a mouse position, return a valid index inside the edited text */
+static int
+iti_idx_from_x_y (Iti *iti, int x, int y)
+{
+ ItiPrivate *priv;
+ EIconBarTextItemInfoRow *row;
+ int lines;
+ int line, col, i, idx;
+ GList *l;
+
+ priv = iti->priv;
+
+ if (iti->ti->rows == NULL)
+ return 0;
+
+ lines = g_list_length (iti->ti->rows);
+ line = y / iti->ti->baseline_skip;
+
+ if (line < 0)
+ line = 0;
+ else if (lines < line + 1)
+ line = lines - 1;
+
+ /* Compute the base index for this line */
+ for (l = iti->ti->rows, idx = i = 0; i < line; l = l->next, i++) {
+ row = l->data;
+ idx += row->text_length;
+ }
+
+ row = g_list_nth (iti->ti->rows, line)->data;
+ col = 0;
+ if (row != NULL) {
+ int first_char;
+ int last_char;
+
+ first_char = (iti->ti->width - row->width) / 2;
+ last_char = first_char + row->width;
+
+ if (x < first_char) {
+ /* nothing */
+ } else if (x > last_char) {
+ col = row->text_length;
+ } else {
+ GdkWChar *s = row->text_wc;
+ int pos = first_char;
+
+ while (pos < last_char) {
+ pos += gdk_text_width_wc (iti->ti->font, s, 1);
+ if (pos > x)
+ break;
+ col++;
+ s++;
+ }
+ }
+ }
+
+ idx += col;
+
+ g_assert (idx <= priv->entry->text_size);
+
+ return idx;
+}
+
+/* Starts the selection state in the icon text item */
+static void
+iti_start_selecting (Iti *iti, int idx, guint32 event_time)
+{
+ ItiPrivate *priv;
+ GtkEditable *e;
+ GdkCursor *ibeam;
+
+ priv = iti->priv;
+ e = GTK_EDITABLE (priv->entry);
+
+ gtk_editable_select_region (e, idx, idx);
+ gtk_editable_set_position (e, idx);
+ ibeam = gdk_cursor_new (GDK_XTERM);
+ gnome_canvas_item_grab (GNOME_CANVAS_ITEM (iti),
+ GDK_BUTTON_RELEASE_MASK |
+ GDK_POINTER_MOTION_MASK,
+ ibeam, event_time);
+ gdk_cursor_destroy (ibeam);
+
+ gtk_editable_select_region (e, idx, idx);
+ e->current_pos = e->selection_start_pos;
+ e->has_selection = TRUE;
+ iti->selecting = TRUE;
+
+ priv->need_state_update = TRUE;
+ gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (iti));
+
+ gtk_signal_emit (GTK_OBJECT (iti), iti_signals[SELECTION_STARTED]);
+}
+
+/* Stops the selection state in the icon text item */
+static void
+iti_stop_selecting (Iti *iti, guint32 event_time)
+{
+ ItiPrivate *priv;
+ GnomeCanvasItem *item;
+ GtkEditable *e;
+
+ priv = iti->priv;
+ item = GNOME_CANVAS_ITEM (iti);
+ e = GTK_EDITABLE (priv->entry);
+
+ gnome_canvas_item_ungrab (item, event_time);
+ e->has_selection = FALSE;
+ iti->selecting = FALSE;
+
+ priv->need_state_update = TRUE;
+ gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (iti));
+ gtk_signal_emit (GTK_OBJECT (iti), iti_signals[SELECTION_STOPPED]);
+}
+
+/* Handles selection range changes on the icon text item */
+static void
+iti_selection_motion (Iti *iti, int idx)
+{
+ ItiPrivate *priv;
+ GtkEditable *e;
+
+ priv = iti->priv;
+ e = GTK_EDITABLE (priv->entry);
+
+ if (idx < e->current_pos) {
+ e->selection_start_pos = idx;
+ e->selection_end_pos = e->current_pos;
+ } else {
+ e->selection_start_pos = e->current_pos;
+ e->selection_end_pos = idx;
+ }
+
+ priv->need_state_update = TRUE;
+ gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (iti));
+}
+
+/* Event handler for icon text items */
+static gint
+iti_event (GnomeCanvasItem *item, GdkEvent *event)
+{
+ Iti *iti;
+ ItiPrivate *priv;
+ int idx;
+ double x, y;
+
+ iti = ITI (item);
+ priv = iti->priv;
+
+ switch (event->type) {
+ case GDK_KEY_PRESS:
+ if (!iti->editing)
+ break;
+
+ if (event->key.keyval == GDK_Escape)
+ iti_stop_editing (iti);
+ else
+ gtk_widget_event (GTK_WIDGET (priv->entry), event);
+
+ priv->need_text_update = TRUE;
+ gnome_canvas_item_request_update (item);
+ return TRUE;
+
+ case GDK_BUTTON_PRESS:
+ if (!iti->editing)
+ break;
+
+ if (iti->editing && event->button.button == 1) {
+ x = event->button.x - (item->x1 + MARGIN_X);
+ y = event->button.y - (item->y1 + MARGIN_Y);
+ idx = iti_idx_from_x_y (iti, x, y);
+
+ iti_start_selecting (iti, idx, event->button.time);
+ }
+
+ return TRUE;
+
+ case GDK_MOTION_NOTIFY:
+ if (!iti->selecting)
+ break;
+
+ x = event->motion.x - (item->x1 + MARGIN_X);
+ y = event->motion.y - (item->y1 + MARGIN_Y);
+ idx = iti_idx_from_x_y (iti, x, y);
+ iti_selection_motion (iti, idx);
+ return TRUE;
+
+ case GDK_BUTTON_RELEASE:
+ if (iti->selecting && event->button.button == 1)
+ iti_stop_selecting (iti, event->button.time);
+ else
+ break;
+
+ return TRUE;
+
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+/* Bounds method handler for the icon text item */
+static void
+iti_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
+{
+ Iti *iti;
+ ItiPrivate *priv;
+ int width, height;
+
+ iti = ITI (item);
+ priv = iti->priv;
+
+ if (priv->need_text_update) {
+ layout_text (iti);
+ priv->need_text_update = FALSE;
+ }
+
+ if (iti->ti) {
+ width = iti->ti->width + 2 * MARGIN_X;
+ height = iti->ti->height + 2 * MARGIN_Y;
+ } else {
+ width = 2 * MARGIN_X;
+ height = 2 * MARGIN_Y;
+ }
+
+ *x1 = iti->x + (iti->width - width) * iti->xalign;
+ *y1 = iti->y;
+ *x2 = *x1 + width;
+ *y2 = *y1 + height;
+}
+
+/* Class initialization function for the icon text item */
+static void
+iti_class_init (EIconBarTextItemClass *text_item_class)
+{
+ GtkObjectClass *object_class;
+ GnomeCanvasItemClass *item_class;
+
+ object_class = (GtkObjectClass *) text_item_class;
+ item_class = (GnomeCanvasItemClass *) text_item_class;
+
+ parent_class = gtk_type_class (gnome_canvas_item_get_type ());
+
+ gtk_object_add_arg_type ("EIconBarTextItem::xalign", GTK_TYPE_FLOAT, GTK_ARG_READWRITE, ARG_XALIGN);
+ gtk_object_add_arg_type ("EIconBarTextItem::justify", GTK_TYPE_JUSTIFICATION, GTK_ARG_READWRITE, ARG_JUSTIFY);
+ gtk_object_add_arg_type ("EIconBarTextItem::max_lines", GTK_TYPE_INT, GTK_ARG_READWRITE, ARG_MAX_LINES);
+ gtk_object_add_arg_type ("EIconBarTextItem::show_ellipsis", GTK_TYPE_BOOL, GTK_ARG_READWRITE, ARG_SHOW_ELLIPSIS);
+
+ iti_signals [TEXT_CHANGED] =
+ gtk_signal_new (
+ "text_changed",
+ GTK_RUN_LAST,
+ object_class->type,
+ GTK_SIGNAL_OFFSET (EIconBarTextItemClass, text_changed),
+ gtk_marshal_BOOL__NONE,
+ GTK_TYPE_BOOL, 0);
+
+ iti_signals [HEIGHT_CHANGED] =
+ gtk_signal_new (
+ "height_changed",
+ GTK_RUN_LAST,
+ object_class->type,
+ GTK_SIGNAL_OFFSET (EIconBarTextItemClass, height_changed),
+ gtk_marshal_NONE__NONE,
+ GTK_TYPE_NONE, 0);
+
+ iti_signals [WIDTH_CHANGED] =
+ gtk_signal_new (
+ "width_changed",
+ GTK_RUN_LAST,
+ object_class->type,
+ GTK_SIGNAL_OFFSET (EIconBarTextItemClass, width_changed),
+ gtk_marshal_NONE__NONE,
+ GTK_TYPE_NONE, 0);
+
+ iti_signals[EDITING_STARTED] =
+ gtk_signal_new (
+ "editing_started",
+ GTK_RUN_LAST,
+ object_class->type,
+ GTK_SIGNAL_OFFSET (EIconBarTextItemClass, editing_started),
+ gtk_marshal_NONE__NONE,
+ GTK_TYPE_NONE, 0);
+
+ iti_signals[EDITING_STOPPED] =
+ gtk_signal_new (
+ "editing_stopped",
+ GTK_RUN_LAST,
+ object_class->type,
+ GTK_SIGNAL_OFFSET (EIconBarTextItemClass, editing_stopped),
+ gtk_marshal_NONE__NONE,
+ GTK_TYPE_NONE, 0);
+
+ iti_signals[SELECTION_STARTED] =
+ gtk_signal_new (
+ "selection_started",
+ GTK_RUN_FIRST,
+ object_class->type,
+ GTK_SIGNAL_OFFSET (EIconBarTextItemClass, selection_started),
+ gtk_marshal_NONE__NONE,
+ GTK_TYPE_NONE, 0);
+
+ iti_signals[SELECTION_STOPPED] =
+ gtk_signal_new (
+ "selection_stopped",
+ GTK_RUN_FIRST,
+ object_class->type,
+ GTK_SIGNAL_OFFSET (EIconBarTextItemClass, selection_stopped),
+ gtk_marshal_NONE__NONE,
+ GTK_TYPE_NONE, 0);
+
+ gtk_object_class_add_signals (object_class, iti_signals, LAST_SIGNAL);
+
+ object_class->destroy = iti_destroy;
+ object_class->get_arg = iti_get_arg;
+ object_class->set_arg = iti_set_arg;
+
+ item_class->update = iti_update;
+ item_class->draw = iti_draw;
+ item_class->point = iti_point;
+ item_class->bounds = iti_bounds;
+ item_class->event = iti_event;
+
+ e_icon_bar_text_item_ellipsis = _("...");
+}
+
+/* Object initialization function for the icon text item */
+static void
+iti_init (EIconBarTextItem *iti)
+{
+ ItiPrivate *priv;
+
+ priv = g_new0 (ItiPrivate, 1);
+ iti->priv = priv;
+
+ iti->xalign = 0.5;
+ iti->justification = GTK_JUSTIFY_CENTER;
+ iti->max_lines = -1;
+ iti->show_ellipsis = TRUE;
+}
+
+/**
+ * e_icon_bar_text_item_configure:
+ * @iti: An #EIconBarTextItem.
+ * @x: X position in which to place the item.
+ * @y: Y position in which to place the item.
+ * @width: Maximum width allowed for this item, to be used for word wrapping.
+ * @fontname: Name of the fontset that should be used to display the text.
+ * @text: Text that is going to be displayed.
+ * @is_static: Whether @text points to a static string or not.
+ *
+ * This routine is used to configure an #EIconBarTextItem.
+ *
+ * @x and @y specify the coordinates where the item is placed in the canvas.
+ * The @x coordinate should be the leftmost position that the item can
+ * assume at any one time, that is, the left margin of the column in which the
+ * icon is to be placed. The @y coordinate specifies the top of the item.
+ *
+ * @width is the maximum width allowed for this icon text item. The coordinates
+ * define the upper-left corner of an item with maximum width; this may
+ * actually be outside the bounding box of the item if the text is narrower
+ * than the maximum width.
+ *
+ * If @is_static is true, it means that there is no need for the item to
+ * allocate memory for the string (it is a guarantee that the text is allocated
+ * by the caller and it will not be deallocated during the lifetime of this
+ * item). This is an optimization to reduce memory usage for large icon sets.
+ */
+void
+e_icon_bar_text_item_configure (EIconBarTextItem *iti, int x, int y,
+ int width, const char *fontname,
+ const char *text,
+ gboolean is_static)
+{
+ ItiPrivate *priv;
+
+ g_return_if_fail (iti != NULL);
+ g_return_if_fail (IS_ITI (iti));
+ g_return_if_fail (width > 2 * MARGIN_X);
+ g_return_if_fail (text != NULL);
+
+ priv = iti->priv;
+
+ iti->x = x;
+ iti->y = y;
+ iti->width = width;
+
+ if (iti->text && iti->is_text_allocated)
+ g_free (iti->text);
+
+ iti->is_text_allocated = !is_static;
+
+ /* This cast is to shut up the compiler */
+ if (is_static)
+ iti->text = (char *) text;
+ else
+ iti->text = g_strdup (text);
+
+ if (iti->fontname)
+ g_free (iti->fontname);
+
+ iti->fontname = g_strdup (fontname ? fontname : DEFAULT_FONT_NAME);
+
+ if (priv->font)
+ gdk_font_unref (priv->font);
+
+ priv->font = NULL;
+ if (fontname)
+ priv->font = gdk_fontset_load (iti->fontname);
+ if (!priv->font)
+ priv->font = get_default_font ();
+
+ /* Request update */
+
+ priv->need_pos_update = TRUE;
+ priv->need_font_update = TRUE;
+ priv->need_text_update = TRUE;
+ gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (iti));
+}
+
+/**
+ * e_icon_bar_text_item_set_width:
+ * @iti: An #EIconBarTextItem.
+ * @width: Maximum width allowed for this item, to be used for word wrapping.
+ *
+ * This routine is used to set the maximum width of an #EIconBarTextItem.
+ */
+void
+e_icon_bar_text_item_set_width (EIconBarTextItem *iti, int width)
+{
+ ItiPrivate *priv;
+
+ g_return_if_fail (iti != NULL);
+ g_return_if_fail (IS_ITI (iti));
+ g_return_if_fail (width > 2 * MARGIN_X);
+
+ priv = iti->priv;
+
+ if (iti->width == width)
+ return;
+
+ iti->width = width;
+
+ /* Request update */
+ priv->need_text_update = TRUE;
+ gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (iti));
+}
+
+/**
+ * e_icon_bar_text_item_setxy:
+ * @iti: An #EIconBarTextItem.
+ * @x: X position.
+ * @y: Y position.
+ *
+ * Sets the coordinates at which the #EIconBarTextItem should be placed.
+ *
+ * See also: e_icon_bar_text_item_configure().
+ */
+void
+e_icon_bar_text_item_setxy (EIconBarTextItem *iti, int x, int y)
+{
+ ItiPrivate *priv;
+
+ g_return_if_fail (iti != NULL);
+ g_return_if_fail (IS_ITI (iti));
+
+ priv = iti->priv;
+
+ iti->x = x;
+ iti->y = y;
+
+ priv->need_pos_update = TRUE;
+ gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (iti));
+}
+
+/**
+ * e_icon_bar_text_item_select:
+ * @iti: An #EIconBarTextItem.
+ * @sel: Whether the item should be displayed as selected.
+ *
+ * This function is used to control whether an icon text item is displayed as
+ * selected or not. Mouse events are ignored by the item when it is unselected;
+ * when the user clicks on a selected icon text item, it will start the text
+ * editing process.
+ */
+void
+e_icon_bar_text_item_select (EIconBarTextItem *iti, int sel)
+{
+ ItiPrivate *priv;
+
+ g_return_if_fail (iti != NULL);
+ g_return_if_fail (IS_ITI (iti));
+
+ priv = iti->priv;
+
+ if (!iti->selected == !sel)
+ return;
+
+ iti->selected = sel ? TRUE : FALSE;
+
+ if (!iti->selected && iti->editing)
+ iti_edition_accept (iti);
+
+ priv->need_state_update = TRUE;
+ gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (iti));
+}
+
+/**
+ * e_icon_bar_text_item_get_text:
+ * @iti: An #EIconBarTextItem.
+ *
+ * Returns the current text. The client should not free this string, as it is
+ * internal to the #EIconBarTextItem.
+ */
+char *
+e_icon_bar_text_item_get_text (EIconBarTextItem *iti)
+{
+ ItiPrivate *priv;
+
+ g_return_val_if_fail (iti != NULL, NULL);
+ g_return_val_if_fail (IS_ITI (iti), NULL);
+
+ priv = iti->priv;
+
+ if (iti->editing)
+ return gtk_entry_get_text (priv->entry);
+ else
+ return iti->text;
+}
+
+
+/**
+ * e_icon_bar_text_item_set_text:
+ * @iti: An #EIconBarTextItem.
+ * @text: Text that is going to be displayed.
+ * @is_static: Whether @text points to a static string or not.
+ *
+ * If @is_static is true, it means that there is no need for the item to
+ * allocate memory for the string (it is a guarantee that the text is allocated
+ * by the caller and it will not be deallocated during the lifetime of this
+ * item). This is an optimization to reduce memory usage for large icon sets.
+ */
+void
+e_icon_bar_text_item_set_text (EIconBarTextItem *iti, const char *text,
+ gboolean is_static)
+{
+ ItiPrivate *priv;
+
+ g_return_if_fail (iti != NULL);
+ g_return_if_fail (IS_ITI (iti));
+ g_return_if_fail (text != NULL);
+
+ priv = iti->priv;
+
+ if (iti->text && iti->is_text_allocated)
+ g_free (iti->text);
+
+ iti->is_text_allocated = !is_static;
+
+ /* This cast is to shut up the compiler */
+ if (is_static)
+ iti->text = (char *) text;
+ else
+ iti->text = g_strdup (text);
+
+ /* Request update */
+
+ priv->need_text_update = TRUE;
+ gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (iti));
+}
+
+
+/**
+ * e_icon_bar_text_item_start_editing:
+ * @iti: An #EIconBarTextItem.
+ *
+ * Starts the editing state of an #EIconBarTextItem.
+ **/
+void
+e_icon_bar_text_item_start_editing (EIconBarTextItem *iti)
+{
+ g_return_if_fail (iti != NULL);
+ g_return_if_fail (IS_ITI (iti));
+
+ if (iti->editing)
+ return;
+
+ iti->selected = TRUE; /* Ensure that we are selected */
+ gnome_canvas_item_grab_focus (GNOME_CANVAS_ITEM (iti));
+ iti_start_editing (iti);
+}
+
+/**
+ * e_icon_bar_text_item_stop_editing:
+ * @iti: An #EIconBarTextItem.
+ * @accept: Whether to accept the current text or to discard it.
+ *
+ * Terminates the editing state of an icon text item. The @accept argument
+ * controls whether the item's current text should be accepted or discarded.
+ * If it is discarded, then the icon's original text will be restored.
+ **/
+void
+e_icon_bar_text_item_stop_editing (EIconBarTextItem *iti,
+ gboolean accept)
+{
+ g_return_if_fail (iti != NULL);
+ g_return_if_fail (IS_ITI (iti));
+
+ if (!iti->editing)
+ return;
+
+ if (accept)
+ iti_edition_accept (iti);
+ else
+ iti_stop_editing (iti);
+}
+
+
+/**
+ * e_icon_bar_text_item_get_type:
+ *
+ * Registers the &EIconBarTextItem class if necessary, and returns the type ID
+ * associated to it.
+ *
+ * Return value: the type ID of the #EIconBarTextItem class.
+ **/
+GtkType
+e_icon_bar_text_item_get_type (void)
+{
+ static GtkType iti_type = 0;
+
+ if (!iti_type) {
+ static const GtkTypeInfo iti_info = {
+ "EIconBarTextItem",
+ sizeof (EIconBarTextItem),
+ sizeof (EIconBarTextItemClass),
+ (GtkClassInitFunc) iti_class_init,
+ (GtkObjectInitFunc) iti_init,
+ NULL, /* reserved_1 */
+ NULL, /* reserved_2 */
+ (GtkClassInitFunc) NULL
+ };
+
+ iti_type = gtk_type_unique (gnome_canvas_item_get_type (), &iti_info);
+ }
+
+ return iti_type;
+}
+
+
+static void
+free_row (gpointer data, gpointer user_data)
+{
+ EIconBarTextItemInfoRow *row;
+
+ if (data) {
+ row = data;
+ g_free (row->text);
+ g_free (row->text_wc);
+ g_free (row);
+ }
+}
+
+/*
+ * e_icon_bar_text_item_free_info:
+ * @ti: An icon text info structure.
+ *
+ * Frees a &EIconBarTextItemInfo structure. You should call this instead of
+ * freeing the structure yourself.
+ */
+static void
+e_icon_bar_text_item_free_info (EIconBarTextItemInfo *ti)
+{
+ g_list_foreach (ti->rows, free_row, NULL);
+ g_list_free (ti->rows);
+ g_free (ti);
+}
+
+/*
+ * e_icon_bar_text_item_layout_text:
+ * @font: Name of the font that will be used to render the text.
+ * @text: Text to be formatted.
+ * @separators: Separators used for word wrapping, can be NULL.
+ * @max_width: Width in pixels to be used for word wrapping.
+ * @confine: Whether it is mandatory to wrap at @max_width.
+ *
+ * Creates a new &EIconBarTextItemInfo structure by wrapping the specified
+ * text. If non-NULL, the @separators argument defines a set of characters
+ * to be used as word delimiters for performing word wrapping. If it is
+ * NULL, then only spaces will be used as word delimiters.
+ *
+ * The @max_width argument is used to specify the width at which word
+ * wrapping will be performed. If there is a very long word that does not
+ * fit in a single line, the @confine argument can be used to specify
+ * whether the word should be unconditionally split to fit or whether
+ * the maximum width should be increased as necessary.
+ *
+ * Return value: A newly-created &EIconBarTextItemInfo structure.
+ */
+static EIconBarTextItemInfo *
+e_icon_bar_text_item_layout_text (EIconBarTextItem *iti, GdkFont *font,
+ const gchar *text, const gchar *separators,
+ gint max_width, gboolean confine)
+{
+ EIconBarTextItemInfo *ti;
+ EIconBarTextItemInfoRow *row;
+ GdkWChar *row_end;
+ GdkWChar *s, *word_start, *word_end, *old_word_end;
+ GdkWChar *sub_text;
+ int i, w_len, w;
+ GdkWChar *text_wc, *text_iter, *separators_wc;
+ int text_len_wc, separators_len_wc;
+ gboolean restrict_lines;
+ int lines;
+
+ g_return_val_if_fail (font != NULL, NULL);
+ g_return_val_if_fail (text != NULL, NULL);
+
+ if (!separators)
+ separators = " ";
+
+ text_wc = g_new (GdkWChar, strlen (text) + 1);
+ text_len_wc = gdk_mbstowcs (text_wc, text, strlen (text));
+ if (text_len_wc < 0) text_len_wc = 0;
+ text_wc[text_len_wc] = 0;
+
+ separators_wc = g_new (GdkWChar, strlen (separators) + 1);
+ separators_len_wc = gdk_mbstowcs (separators_wc, separators, strlen (separators));
+ if (separators_len_wc < 0) separators_len_wc = 0;
+ separators_wc[separators_len_wc] = 0;
+
+ ti = g_new (EIconBarTextItemInfo, 1);
+
+ ti->rows = NULL;
+ ti->font = font;
+ ti->width = 0;
+ ti->height = 0;
+ ti->baseline_skip = font->ascent + font->descent;
+
+ word_end = NULL;
+
+ if (!iti->editing && iti->max_lines != -1)
+ restrict_lines = TRUE;
+ else
+ restrict_lines = FALSE;
+
+ text_iter = text_wc;
+ lines = 0;
+ while (*text_iter) {
+ /* If we are restricting the height, and this is the last line,
+ and we are displaying the ellipsis, then subtract the width
+ of the ellipsis from our max_width. */
+ if (restrict_lines && lines == iti->max_lines - 1
+ && iti->show_ellipsis) {
+ max_width -= gdk_string_measure (font, e_icon_bar_text_item_ellipsis);
+ }
+
+ for (row_end = text_iter; *row_end != 0 && *row_end != '\n'; row_end++);
+
+ /* Accumulate words from this row until they don't fit in the max_width */
+
+ s = text_iter;
+
+ while (s < row_end) {
+ word_start = s;
+ old_word_end = word_end;
+ for (word_end = word_start; *word_end; word_end++) {
+ GdkWChar *p;
+ for (p = separators_wc; *p; p++) {
+ if (*word_end == *p)
+ goto found;
+ }
+ }
+ found:
+ if (word_end < row_end)
+ word_end++;
+
+ if (gdk_text_width_wc (font, text_iter, word_end - text_iter) > max_width) {
+ if (word_start == text_iter
+ || (restrict_lines
+ && lines == iti->max_lines - 1)) {
+ if (confine) {
+ /* We must force-split the word. Look for a proper
+ * place to do it.
+ */
+
+ w_len = word_end - text_iter;
+
+ for (i = 1; i < w_len; i++) {
+ w = gdk_text_width_wc (font, text_iter, i);
+ if (w > max_width) {
+ if (i == 1)
+ /* Shit, not even a single character fits */
+ max_width = w;
+ else
+ break;
+ }
+ }
+
+ /* Create sub-row with the chars that fit */
+
+ sub_text = g_new (GdkWChar, i);
+ memcpy (sub_text, text_iter, (i - 1) * sizeof (GdkWChar));
+ sub_text[i - 1] = 0;
+
+ row = g_new (EIconBarTextItemInfoRow, 1);
+ row->text_wc = sub_text;
+ row->text_length = i - 1;
+ row->width = gdk_text_width_wc (font, sub_text, i - 1);
+ row->text = gdk_wcstombs(sub_text);
+ if (row->text == NULL)
+ row->text = g_strdup("");
+
+ ti->rows = g_list_append (ti->rows, row);
+
+ if (row->width > ti->width)
+ ti->width = row->width;
+
+ ti->height += ti->baseline_skip;
+
+ /* Bump the text pointer */
+
+ text_iter += i - 1;
+ s = text_iter;
+
+ lines++;
+ if (restrict_lines
+ && lines >= iti->max_lines)
+ break;
+
+ continue;
+ } else
+ max_width = gdk_text_width_wc (font, word_start, word_end - word_start);
+
+ continue; /* Retry split */
+ } else {
+ word_end = old_word_end; /* Restore to region that does fit */
+ break; /* Stop the loop because we found something that doesn't fit */
+ }
+ }
+
+ s = word_end;
+ }
+
+ if (restrict_lines && lines >= iti->max_lines)
+ break;
+
+ /* Append row */
+
+ if (text_iter == row_end) {
+ /* We are on a newline, so append an empty row */
+
+ ti->rows = g_list_append (ti->rows, NULL);
+ ti->height += ti->baseline_skip;
+
+ /* Next! */
+
+ text_iter = row_end + 1;
+
+ lines++;
+ if (restrict_lines && lines >= iti->max_lines)
+ break;
+
+ } else {
+ /* Create subrow and append it to the list */
+
+ int sub_len;
+ sub_len = word_end - text_iter;
+
+ sub_text = g_new (GdkWChar, sub_len + 1);
+ memcpy (sub_text, text_iter, sub_len * sizeof (GdkWChar));
+ sub_text[sub_len] = 0;
+
+ row = g_new (EIconBarTextItemInfoRow, 1);
+ row->text_wc = sub_text;
+ row->text_length = sub_len;
+ row->width = gdk_text_width_wc (font, sub_text, sub_len);
+ row->text = gdk_wcstombs(sub_text);
+ if (row->text == NULL)
+ row->text = g_strdup("");
+
+ ti->rows = g_list_append (ti->rows, row);
+
+ if (row->width > ti->width)
+ ti->width = row->width;
+
+ ti->height += ti->baseline_skip;
+
+ /* Next! */
+
+ text_iter = word_end;
+
+ lines++;
+ if (restrict_lines && lines >= iti->max_lines)
+ break;
+ }
+ }
+
+ /* Check if we've had to clip the text. */
+ iti->is_clipped = *text_iter ? TRUE : FALSE;
+
+ g_free (text_wc);
+ g_free (separators_wc);
+ return ti;
+}
+
+/*
+ * e_icon_bar_text_item_paint_text:
+ * @ti: An icon text info structure.
+ * @drawable: Target drawable.
+ * @gc: GC used to render the string.
+ * @x: Left coordinate for text.
+ * @y: Upper coordinate for text.
+ * @just: Justification for text.
+ *
+ * Paints the formatted text in the icon text info structure onto a drawable.
+ * This is just a sample implementation; applications can choose to use other
+ * rendering functions.
+ */
+static void
+e_icon_bar_text_item_paint_text (EIconBarTextItem *iti,
+ EIconBarTextItemInfo *ti,
+ GdkDrawable *drawable, GdkGC *gc,
+ gint x, gint y, GtkJustification just)
+{
+ GList *item;
+ EIconBarTextItemInfoRow *row;
+ int xpos, line, width;
+ gboolean show_ellipsis;
+
+ g_return_if_fail (ti != NULL);
+ g_return_if_fail (drawable != NULL);
+ g_return_if_fail (gc != NULL);
+
+ y += ti->font->ascent;
+
+ for (item = ti->rows, line = 1; item; item = item->next, line++) {
+
+ if (item->data) {
+ row = item->data;
+ width = row->width;
+ }
+
+ /* If this is the last line, and the text has been clipped,
+ and show_ellipsis is TRUE, display '...' */
+ if (line == iti->max_lines && iti->is_clipped) {
+ show_ellipsis = TRUE;
+ width += gdk_string_measure (ti->font, e_icon_bar_text_item_ellipsis);
+ } else {
+ show_ellipsis = FALSE;
+ }
+
+ switch (just) {
+ case GTK_JUSTIFY_LEFT:
+ xpos = 0;
+ break;
+
+ case GTK_JUSTIFY_RIGHT:
+ xpos = ti->width - width;
+ break;
+
+ case GTK_JUSTIFY_CENTER:
+ xpos = (ti->width - width) / 2;
+ break;
+
+ default:
+ /* Anyone care to implement GTK_JUSTIFY_FILL? */
+ g_warning ("Justification type %d not supported. Using left-justification.",
+ (int) just);
+ xpos = 0;
+ }
+
+ if (item->data)
+ gdk_draw_text_wc (drawable, ti->font, gc, x + xpos, y, row->text_wc, row->text_length);
+
+ if (show_ellipsis)
+ gdk_draw_string (drawable, ti->font, gc,
+ x + xpos + row->width, y,
+ e_icon_bar_text_item_ellipsis);
+
+ y += ti->baseline_skip;
+ }
+}