evolution-3.6.4/widgets/text/e-text.c

Location Tool Test ID Function Issue
e-text.c:1277:20 clang-analyzer Value stored to 'clip_region' during its initialization is never read
e-text.c:1277:20 clang-analyzer Value stored to 'clip_region' during its initialization is never read
   1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
   2 /*
   3  * e-text.c - Text item for evolution.
   4  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
   5  *
   6  * Authors:
   7  *   Chris Lahey <clahey@ximian.com>
   8  *   Jon Trowbridge <trow@ximian.com>
   9  *
  10  * A majority of code taken from:
  11  *
  12  * Text item type for GnomeCanvas widget
  13  *
  14  * GnomeCanvas is basically a port of the Tk toolkit's most excellent
  15  * canvas widget.  Tk is copyrighted by the Regents of the University
  16  * of California, Sun Microsystems, and other parties.
  17  *
  18  * Copyright (C) 1998 The Free Software Foundation
  19  *
  20  * Author: Federico Mena <federico@nuclecu.unam.mx>
  21  *
  22  * This library is free software; you can redistribute it and/or
  23  * modify it under the terms of the GNU Library General Public License as
  24  * published by the Free Software Foundation; either version 2 of the
  25  * License, or (at your option) any later version.
  26  *
  27  * This library is distributed in the hope that it will be useful, but
  28  * WITHOUT ANY WARRANTY; without even the implied warranty of
  29  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  30  * Library General Public License for more details.
  31  *
  32  * You should have received a copy of the GNU Library General Public
  33  * License along with this library; if not, write to the Free Software
  34  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  35  * 02110-1301, USA.
  36  */
  37 
  38 #ifdef HAVE_CONFIG_H
  39 #include <config.h>
  40 #endif
  41 
  42 #include <math.h>
  43 #include <ctype.h>
  44 #include <string.h>
  45 
  46 #include <gdk/gdkkeysyms.h>
  47 #include <gtk/gtk.h>
  48 
  49 #include "gal-a11y-e-text.h"
  50 #include "misc/e-canvas.h"
  51 #include "misc/e-canvas-utils.h"
  52 #include "e-util/e-unicode.h"
  53 #include <glib/gi18n.h>
  54 #include "e-util/e-text-event-processor-emacs-like.h"
  55 #include "e-util/e-util.h"
  56 
  57 #include "e-text.h"
  58 
  59 G_DEFINE_TYPE (EText, e_text, GNOME_TYPE_CANVAS_ITEM)
  60 
  61 enum {
  62 	E_TEXT_CHANGED,
  63 	E_TEXT_ACTIVATE,
  64 	E_TEXT_KEYPRESS,
  65 	E_TEXT_POPULATE_POPUP,
  66 	E_TEXT_LAST_SIGNAL
  67 };
  68 
  69 static GQuark e_text_signals[E_TEXT_LAST_SIGNAL] = { 0 };
  70 
  71 /* Object argument IDs */
  72 enum {
  73 	PROP_0,
  74 	PROP_MODEL,
  75 	PROP_EVENT_PROCESSOR,
  76 	PROP_TEXT,
  77 	PROP_BOLD,
  78 	PROP_STRIKEOUT,
  79 	PROP_ANCHOR,
  80 	PROP_JUSTIFICATION,
  81 	PROP_CLIP_WIDTH,
  82 	PROP_CLIP_HEIGHT,
  83 	PROP_CLIP,
  84 	PROP_FILL_CLIP_RECTANGLE,
  85 	PROP_X_OFFSET,
  86 	PROP_Y_OFFSET,
  87 	PROP_FILL_COLOR,
  88 	PROP_FILL_COLOR_GDK,
  89 	PROP_FILL_COLOR_RGBA,
  90 	PROP_TEXT_WIDTH,
  91 	PROP_TEXT_HEIGHT,
  92 	PROP_EDITABLE,
  93 	PROP_USE_ELLIPSIS,
  94 	PROP_ELLIPSIS,
  95 	PROP_LINE_WRAP,
  96 	PROP_BREAK_CHARACTERS,
  97 	PROP_MAX_LINES,
  98 	PROP_WIDTH,
  99 	PROP_HEIGHT,
 100 	PROP_ALLOW_NEWLINES,
 101 	PROP_CURSOR_POS,
 102 	PROP_IM_CONTEXT,
 103 	PROP_HANDLE_POPUP
 104 };
 105 
 106 static void	e_text_command			(ETextEventProcessor *tep,
 107 						 ETextEventProcessorCommand *command,
 108 						 gpointer data);
 109 
 110 static void	e_text_text_model_changed	(ETextModel *model,
 111 						 EText *text);
 112 static void	e_text_text_model_reposition	(ETextModel *model,
 113 						 ETextModelReposFn fn,
 114 						 gpointer repos_data,
 115 						 gpointer data);
 116 
 117 static void _get_tep (EText *text);
 118 
 119 static void calc_height (EText *text);
 120 
 121 static gboolean show_pango_rectangle (EText *text, PangoRectangle rect);
 122 
 123 static void e_text_do_popup (EText *text, GdkEventButton *button, gint position);
 124 
 125 static void e_text_update_primary_selection (EText *text);
 126 static void e_text_paste (EText *text, GdkAtom selection);
 127 static void e_text_insert (EText *text, const gchar *string);
 128 static void e_text_reset_im_context (EText *text);
 129 
 130 static void reset_layout_attrs (EText *text);
 131 
 132 /* IM Context Callbacks */
 133 static void     e_text_commit_cb               (GtkIMContext *context,
 134 						const gchar  *str,
 135 						EText        *text);
 136 static void     e_text_preedit_changed_cb      (GtkIMContext *context,
 137 						EText        *text);
 138 static gboolean e_text_retrieve_surrounding_cb (GtkIMContext *context,
 139 						EText        *text);
 140 static gboolean e_text_delete_surrounding_cb   (GtkIMContext *context,
 141 						gint          offset,
 142 						gint          n_chars,
 143 						EText        *text);
 144 
 145 static GdkAtom clipboard_atom = GDK_NONE;
 146 
 147 static void
 148 disconnect_im_context (EText *text)
 149 {
 150 	if (!text || !text->im_context)
 151 		return;
 152 
 153 	g_signal_handlers_disconnect_matched (
 154 		text->im_context, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, text);
 155 	text->im_context_signals_registered = FALSE;
 156 }
 157 
 158 /* Dispose handler for the text item */
 159 static void
 160 e_text_dispose (GObject *object)
 161 {
 162 	EText *text;
 163 
 164 	g_return_if_fail (object != NULL);
 165 	g_return_if_fail (E_IS_TEXT (object));
 166 
 167 	text = E_TEXT (object);
 168 
 169 	if (text->model_changed_signal_id)
 170 		g_signal_handler_disconnect (
 171 			text->model,
 172 			text->model_changed_signal_id);
 173 	text->model_changed_signal_id = 0;
 174 
 175 	if (text->model_repos_signal_id)
 176 		g_signal_handler_disconnect (
 177 			text->model,
 178 			text->model_repos_signal_id);
 179 	text->model_repos_signal_id = 0;
 180 
 181 	if (text->model)
 182 		g_object_unref (text->model);
 183 	text->model = NULL;
 184 
 185 	if (text->tep_command_id)
 186 		g_signal_handler_disconnect (
 187 			text->tep,
 188 			text->tep_command_id);
 189 	text->tep_command_id = 0;
 190 
 191 	if (text->tep)
 192 		g_object_unref (text->tep);
 193 	text->tep = NULL;
 194 
 195 	g_free (text->revert);
 196 	text->revert = NULL;
 197 
 198 	if (text->timeout_id) {
 199 		g_source_remove (text->timeout_id);
 200 		text->timeout_id = 0;
 201 	}
 202 
 203 	if (text->timer) {
 204 		g_timer_stop (text->timer);
 205 		g_timer_destroy (text->timer);
 206 		text->timer = NULL;
 207 	}
 208 
 209 	if (text->dbl_timeout) {
 210 		g_source_remove (text->dbl_timeout);
 211 		text->dbl_timeout = 0;
 212 	}
 213 
 214 	if (text->tpl_timeout) {
 215 		g_source_remove (text->tpl_timeout);
 216 		text->tpl_timeout = 0;
 217 	}
 218 
 219 	if (text->layout) {
 220 		g_object_unref (text->layout);
 221 		text->layout = NULL;
 222 	}
 223 
 224 	if (text->im_context) {
 225 		disconnect_im_context (text);
 226 		g_object_unref (text->im_context);
 227 		text->im_context = NULL;
 228 	}
 229 
 230 	if (text->font_desc) {
 231 		pango_font_description_free (text->font_desc);
 232 		text->font_desc = NULL;
 233 	}
 234 
 235 	/* Chain up to parent's dispose() method. */
 236 	G_OBJECT_CLASS (e_text_parent_class)->dispose (object);
 237 }
 238 
 239 static void
 240 insert_preedit_text (EText *text)
 241 {
 242 	PangoAttrList *attrs = NULL;
 243 	PangoAttrList *preedit_attrs = NULL;
 244 	gchar *preedit_string = NULL;
 245 	GString *tmp_string = g_string_new (NULL);
 246 	gint length = 0, cpos = 0;
 247 	gboolean new_attrs = FALSE;
 248 
 249 	if (text->layout == NULL || !GTK_IS_IM_CONTEXT (text->im_context))
 250 		return;
 251 
 252 	text->text = e_text_model_get_text (text->model);
 253 	length = strlen (text->text);
 254 
 255 	g_string_prepend_len (tmp_string, text->text,length);
 256 
 257 	/* we came into this function only when text->preedit_len was not 0
 258 	 * so we can safely fetch the preedit string */
 259 	gtk_im_context_get_preedit_string (
 260 		text->im_context, &preedit_string, &preedit_attrs, NULL);
 261 
 262 	if (preedit_string && g_utf8_validate (preedit_string, -1, NULL)) {
 263 
 264 		text->preedit_len = strlen (preedit_string);
 265 
 266 		cpos = g_utf8_offset_to_pointer (
 267 			text->text, text->selection_start) - text->text;
 268 
 269 		g_string_insert (tmp_string, cpos, preedit_string);
 270 
 271 		reset_layout_attrs (text);
 272 
 273 		attrs = pango_layout_get_attributes (text->layout);
 274 		if (!attrs) {
 275 			attrs = pango_attr_list_new ();
 276 			new_attrs = TRUE;
 277 		}
 278 
 279 		pango_layout_set_text (text->layout, tmp_string->str, tmp_string->len);
 280 
 281 		pango_attr_list_splice (attrs, preedit_attrs, cpos, text->preedit_len);
 282 
 283 		if (new_attrs) {
 284 			pango_layout_set_attributes (text->layout, attrs);
 285 			pango_attr_list_unref (attrs);
 286 		}
 287 	} else
 288 		text->preedit_len = 0;
 289 
 290 	if (preedit_string)
 291 		g_free (preedit_string);
 292 	if (preedit_attrs)
 293 		pango_attr_list_unref (preedit_attrs);
 294 	if (tmp_string)
 295 		g_string_free (tmp_string, TRUE);
 296 }
 297 
 298 static void
 299 reset_layout_attrs (EText *text)
 300 {
 301 	PangoAttrList *attrs = NULL;
 302 	gint object_count;
 303 
 304 	if (text->layout == NULL)
 305 		return;
 306 
 307 	object_count = e_text_model_object_count (text->model);
 308 
 309 	if (text->bold || text->strikeout || object_count > 0) {
 310 		gint length = 0;
 311 		gint i;
 312 
 313 		attrs = pango_attr_list_new ();
 314 
 315 		for (i = 0; i < object_count; i++) {
 316 			gint start_pos, end_pos;
 317 			PangoAttribute *attr;
 318 
 319 			attr = pango_attr_underline_new (PANGO_UNDERLINE_SINGLE);
 320 
 321 			e_text_model_get_nth_object_bounds (
 322 				text->model, i, &start_pos, &end_pos);
 323 
 324 			attr->start_index = g_utf8_offset_to_pointer (
 325 				text->text, start_pos) - text->text;
 326 			attr->end_index = g_utf8_offset_to_pointer (
 327 				text->text, end_pos) - text->text;
 328 
 329 			pango_attr_list_insert (attrs, attr);
 330 		}
 331 
 332 		if (text->bold || text->strikeout)
 333 			length = strlen (text->text);
 334 
 335 		if (text->bold) {
 336 			PangoAttribute *attr = pango_attr_weight_new (PANGO_WEIGHT_BOLD);
 337 			attr->start_index = 0;
 338 			attr->end_index = length;
 339 
 340 			pango_attr_list_insert_before (attrs, attr);
 341 		}
 342 		if (text->strikeout) {
 343 			PangoAttribute *attr = pango_attr_strikethrough_new (TRUE);
 344 			attr->start_index = 0;
 345 			attr->end_index = length;
 346 
 347 			pango_attr_list_insert_before (attrs, attr);
 348 		}
 349 	}
 350 
 351 	pango_layout_set_attributes (text->layout, attrs);
 352 
 353 	if (attrs)
 354 		pango_attr_list_unref (attrs);
 355 
 356 	calc_height (text);
 357 }
 358 
 359 static void
 360 create_layout (EText *text)
 361 {
 362 	GnomeCanvasItem *item = GNOME_CANVAS_ITEM (text);
 363 
 364 	if (text->layout)
 365 		return;
 366 
 367 	text->layout = gtk_widget_create_pango_layout (
 368 		GTK_WIDGET (item->canvas), text->text);
 369 	if (text->line_wrap)
 370 		pango_layout_set_width (
 371 			text->layout, text->clip_width < 0
 372 			? -1 : text->clip_width * PANGO_SCALE);
 373 	reset_layout_attrs (text);
 374 }
 375 
 376 static void
 377 reset_layout (EText *text)
 378 {
 379 	GnomeCanvasItem *item = GNOME_CANVAS_ITEM (text);
 380 
 381 	if (text->layout == NULL) {
 382 		create_layout (text);
 383 	}
 384 	else {
 385 		GtkStyle *style;
 386 
 387 		style = gtk_widget_get_style (GTK_WIDGET (item->canvas));
 388 
 389 		if (text->font_desc) {
 390 			pango_font_description_free (text->font_desc);
 391 		}
 392 		text->font_desc = pango_font_description_new ();
 393 		if (!pango_font_description_get_size_is_absolute (style->font_desc))
 394 			pango_font_description_set_size (
 395 				text->font_desc,
 396 				pango_font_description_get_size (style->font_desc));
 397 		else
 398 			pango_font_description_set_absolute_size (
 399 				text->font_desc,
 400 				pango_font_description_get_size (style->font_desc));
 401 		pango_font_description_set_family (
 402 			text->font_desc,
 403 			pango_font_description_get_family (style->font_desc));
 404 		pango_layout_set_font_description (text->layout, text->font_desc);
 405 
 406 		pango_layout_set_text (text->layout, text->text, -1);
 407 		reset_layout_attrs (text);
 408 	}
 409 
 410 	if (!text->button_down) {
 411 		PangoRectangle strong_pos, weak_pos;
 412 		gchar *offs = g_utf8_offset_to_pointer (text->text, text->selection_start);
 413 
 414 		pango_layout_get_cursor_pos (
 415 			text->layout, offs - text->text,
 416 			&strong_pos, &weak_pos);
 417 
 418 		if (strong_pos.x != weak_pos.x ||
 419 		    strong_pos.y != weak_pos.y ||
 420 		    strong_pos.width != weak_pos.width ||
 421 		    strong_pos.height != weak_pos.height)
 422 			show_pango_rectangle (text, weak_pos);
 423 
 424 		show_pango_rectangle (text, strong_pos);
 425 	}
 426 }
 427 
 428 static void
 429 e_text_text_model_changed (ETextModel *model,
 430                            EText *text)
 431 {
 432 	gint model_len = e_text_model_get_text_length (model);
 433 	text->text = e_text_model_get_text (model);
 434 
 435 	/* Make sure our selection doesn't extend past the bounds of our text. */
 436 	text->selection_start = CLAMP (text->selection_start, 0, model_len);
 437 	text->selection_end   = CLAMP (text->selection_end,   0, model_len);
 438 
 439 	text->needs_reset_layout = 1;
 440 	text->needs_split_into_lines = 1;
 441 	text->needs_redraw = 1;
 442 	e_canvas_item_request_reflow (GNOME_CANVAS_ITEM (text));
 443 	gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (text));
 444 
 445 	g_signal_emit (text, e_text_signals[E_TEXT_CHANGED], 0);
 446 }
 447 
 448 static void
 449 e_text_text_model_reposition (ETextModel *model,
 450                               ETextModelReposFn fn,
 451                               gpointer repos_data,
 452                               gpointer user_data)
 453 {
 454 	EText *text = E_TEXT (user_data);
 455 	gint model_len = e_text_model_get_text_length (model);
 456 
 457 	text->selection_start = fn (text->selection_start, repos_data);
 458 	text->selection_end   = fn (text->selection_end,   repos_data);
 459 
 460 	/* Our repos function should make sure we don't overrun the buffer, but it never
 461 	 * hurts to be paranoid. */
 462 	text->selection_start = CLAMP (text->selection_start, 0, model_len);
 463 	text->selection_end   = CLAMP (text->selection_end,   0, model_len);
 464 
 465 	if (text->selection_start > text->selection_end) {
 466 		gint tmp = text->selection_start;
 467 		text->selection_start = text->selection_end;
 468 		text->selection_end = tmp;
 469 	}
 470 }
 471 
 472 static void
 473 get_bounds (EText *text,
 474             gdouble *px1,
 475             gdouble *py1,
 476             gdouble *px2,
 477             gdouble *py2)
 478 {
 479 	GnomeCanvasItem *item;
 480 	gdouble wx, wy, clip_width, clip_height;
 481 
 482 	item = GNOME_CANVAS_ITEM (text);
 483 
 484 	/* Get canvas pixel coordinates for text position */
 485 
 486 	wx = 0;
 487 	wy = 0;
 488 	gnome_canvas_item_i2w (item, &wx, &wy);
 489 	gnome_canvas_w2c (item->canvas, wx, wy, &text->cx, &text->cy);
 490 	gnome_canvas_w2c (item->canvas, wx, wy, &text->clip_cx, &text->clip_cy);
 491 
 492 	if (text->clip_width < 0)
 493 		clip_width = text->width;
 494 	else
 495 		clip_width = text->clip_width;
 496 
 497 	if (text->clip_height < 0)
 498 		clip_height = text->height;
 499 	else
 500 		clip_height = text->clip_height;
 501 
 502 	/* Get canvas pixel coordinates for clip rectangle position */
 503 	text->clip_cwidth = clip_width;
 504 	text->clip_cheight = clip_height;
 505 
 506 	text->text_cx = text->cx;
 507 	text->text_cy = text->cy;
 508 
 509 	/* Bounds */
 510 
 511 	if (text->clip) {
 512 		*px1 = text->clip_cx;
 513 		*py1 = text->clip_cy;
 514 		*px2 = text->clip_cx + text->clip_cwidth;
 515 		*py2 = text->clip_cy + text->clip_cheight;
 516 	} else {
 517 		*px1 = text->cx;
 518 		*py1 = text->cy;
 519 		*px2 = text->cx + text->width;
 520 		*py2 = text->cy + text->height;
 521 	}
 522 }
 523 
 524 static void
 525 calc_height (EText *text)
 526 {
 527 	GnomeCanvasItem *item;
 528 	gint old_height;
 529 	gint old_width;
 530 	gint width = 0;
 531 	gint height = 0;
 532 
 533 	item = GNOME_CANVAS_ITEM (text);
 534 
 535 	/* Calculate text dimensions */
 536 
 537 	old_height = text->height;
 538 	old_width = text->width;
 539 
 540 	if (text->layout)
 541 		pango_layout_get_pixel_size (text->layout, &width, &height);
 542 
 543 	text->height = height;
 544 	text->width = width;
 545 
 546 	if (old_height != text->height || old_width != text->width)
 547 		e_canvas_item_request_parent_reflow (item);
 548 }
 549 
 550 static void
 551 calc_ellipsis (EText *text)
 552 {
 553 /* FIXME: a pango layout per calc_ellipsis sucks */
 554 	gint width;
 555 	PangoLayout *layout = gtk_widget_create_pango_layout (
 556 		GTK_WIDGET (GNOME_CANVAS_ITEM (text)->canvas),
 557 		text->ellipsis ? text->ellipsis : "...");
 558 	pango_layout_get_size (layout, &width, NULL);
 559 
 560 	text->ellipsis_width = width;
 561 
 562 	g_object_unref (layout);
 563 }
 564 
 565 static void
 566 split_into_lines (EText *text)
 567 {
 568 	text->num_lines = pango_layout_get_line_count (text->layout);
 569 }
 570 
 571 /* Set_arg handler for the text item */
 572 static void
 573 e_text_set_property (GObject *object,
 574                     guint property_id,
 575                     const GValue *value,
 576                     GParamSpec *pspec)
 577 {
 578 	GnomeCanvasItem *item;
 579 	EText *text;
 580 	GdkColor color = { 0, 0, 0, 0, };
 581 	GdkColor *pcolor;
 582 
 583 	gboolean needs_update = 0;
 584 	gboolean needs_reflow = 0;
 585 
 586 	item = GNOME_CANVAS_ITEM (object);
 587 	text = E_TEXT (object);
 588 
 589 	switch (property_id) {
 590 	case PROP_MODEL:
 591 
 592 		if (text->model_changed_signal_id)
 593 			g_signal_handler_disconnect (
 594 				text->model,
 595 				text->model_changed_signal_id);
 596 
 597 		if (text->model_repos_signal_id)
 598 			g_signal_handler_disconnect (
 599 				text->model,
 600 				text->model_repos_signal_id);
 601 
 602 		g_object_unref (text->model);
 603 		text->model = E_TEXT_MODEL (g_value_get_object (value));
 604 		g_object_ref (text->model);
 605 
 606 		text->model_changed_signal_id = g_signal_connect (
 607 			text->model, "changed",
 608 			G_CALLBACK (e_text_text_model_changed), text);
 609 
 610 		text->model_repos_signal_id = g_signal_connect (
 611 			text->model, "reposition",
 612 			G_CALLBACK (e_text_text_model_reposition), text);
 613 
 614 		text->text = e_text_model_get_text (text->model);
 615 		g_signal_emit (text, e_text_signals[E_TEXT_CHANGED], 0);
 616 
 617 		text->needs_split_into_lines = 1;
 618 		needs_reflow = 1;
 619 		break;
 620 
 621 	case PROP_EVENT_PROCESSOR:
 622 		if (text->tep && text->tep_command_id)
 623 			g_signal_handler_disconnect (
 624 				text->tep,
 625 				text->tep_command_id);
 626 		if (text->tep) {
 627 			g_object_unref (text->tep);
 628 		}
 629 		text->tep = E_TEXT_EVENT_PROCESSOR (g_value_get_object (value));
 630 		g_object_ref (text->tep);
 631 
 632 		text->tep_command_id = g_signal_connect (
 633 			text->tep, "command",
 634 			G_CALLBACK (e_text_command), text);
 635 
 636 		if (!text->allow_newlines)
 637 			g_object_set (
 638 				text->tep,
 639 				"allow_newlines", FALSE,
 640 				NULL);
 641 		break;
 642 
 643 	case PROP_TEXT:
 644 		e_text_model_set_text (text->model, g_value_get_string (value));
 645 		break;
 646 
 647 	case PROP_BOLD:
 648 		text->bold = g_value_get_boolean (value);
 649 
 650 		text->needs_redraw = 1;
 651 		text->needs_recalc_bounds = 1;
 652 		if (text->line_wrap)
 653 			text->needs_split_into_lines = 1;
 654 		else {
 655 			text->needs_calc_height = 1;
 656 		}
 657 		needs_update = 1;
 658 		needs_reflow = 1;
 659 		break;
 660 
 661 	case PROP_STRIKEOUT:
 662 		text->strikeout = g_value_get_boolean (value);
 663 		text->needs_redraw = 1;
 664 		needs_update = 1;
 665 		break;
 666 
 667 	case PROP_JUSTIFICATION:
 668 		text->justification = g_value_get_enum (value);
 669 		text->needs_redraw = 1;
 670 		needs_update = 1;
 671 		break;
 672 
 673 	case PROP_CLIP_WIDTH:
 674 		text->clip_width = fabs (g_value_get_double (value));
 675 		calc_ellipsis (text);
 676 		if (text->line_wrap) {
 677 			if (text->layout)
 678 				pango_layout_set_width (
 679 					text->layout, text->clip_width < 0
 680 					? -1 : text->clip_width * PANGO_SCALE);
 681 			text->needs_split_into_lines = 1;
 682 		} else {
 683 			text->needs_calc_height = 1;
 684 		}
 685 		needs_reflow = 1;
 686 		break;
 687 
 688 	case PROP_CLIP_HEIGHT:
 689 		text->clip_height = fabs (g_value_get_double (value));
 690 		text->needs_recalc_bounds = 1;
 691 		/* toshok: kind of a hack - set needs_reset_layout
 692 		 * here so when something about the style/them
 693 		 * changes, we redraw the text at the proper size/with
 694 		 * the proper font. */
 695 		text->needs_reset_layout = 1;
 696 		needs_reflow = 1;
 697 		break;
 698 
 699 	case PROP_CLIP:
 700 		text->clip = g_value_get_boolean (value);
 701 		calc_ellipsis (text);
 702 		if (text->line_wrap)
 703 			text->needs_split_into_lines = 1;
 704 		else {
 705 			text->needs_calc_height = 1;
 706 		}
 707 		needs_reflow = 1;
 708 		break;
 709 
 710 	case PROP_FILL_CLIP_RECTANGLE:
 711 		text->fill_clip_rectangle = g_value_get_boolean (value);
 712 		needs_update = 1;
 713 		break;
 714 
 715 	case PROP_X_OFFSET:
 716 		text->xofs = g_value_get_double (value);
 717 		text->needs_recalc_bounds = 1;
 718 		needs_update = 1;
 719 		break;
 720 
 721 	case PROP_Y_OFFSET:
 722 		text->yofs = g_value_get_double (value);
 723 		text->needs_recalc_bounds = 1;
 724 		needs_update = 1;
 725 		break;
 726 
 727 	case PROP_FILL_COLOR:
 728 		if (g_value_get_string (value))
 729 			gdk_color_parse (g_value_get_string (value), &color);
 730 
 731 		text->rgba = ((color.red & 0xff00) << 16 |
 732 			      (color.green & 0xff00) << 8 |
 733 			      (color.blue & 0xff00) |
 734 			      0xff);
 735 		text->rgba_set = TRUE;
 736 		text->needs_redraw = 1;
 737 		needs_update = 1;
 738 		break;
 739 
 740 	case PROP_FILL_COLOR_GDK:
 741 		pcolor = g_value_get_boxed (value);
 742 		if (pcolor) {
 743 			color = *pcolor;
 744 		}
 745 
 746 		text->rgba = ((color.red & 0xff00) << 16 |
 747 			      (color.green & 0xff00) << 8 |
 748 			      (color.blue & 0xff00) |
 749 			      0xff);
 750 		text->rgba_set = TRUE;
 751 		text->needs_redraw = 1;
 752 		needs_update = 1;
 753 		break;
 754 
 755 	case PROP_FILL_COLOR_RGBA:
 756 		text->rgba = g_value_get_uint (value);
 757 		color.red = ((text->rgba >> 24) & 0xff) * 0x101;
 758 		color.green = ((text->rgba >> 16) & 0xff) * 0x101;
 759 		color.blue = ((text->rgba >> 8) & 0xff) * 0x101;
 760 		text->rgba_set = TRUE;
 761 		text->needs_redraw = 1;
 762 		needs_update = 1;
 763 		break;
 764 
 765 	case PROP_EDITABLE:
 766 		text->editable = g_value_get_boolean (value);
 767 		text->needs_redraw = 1;
 768 		needs_update = 1;
 769 		break;
 770 
 771 	case PROP_USE_ELLIPSIS:
 772 		text->use_ellipsis = g_value_get_boolean (value);
 773 		needs_reflow = 1;
 774 		break;
 775 
 776 	case PROP_ELLIPSIS:
 777 		if (text->ellipsis)
 778 			g_free (text->ellipsis);
 779 
 780 		text->ellipsis = g_strdup (g_value_get_string (value));
 781 		calc_ellipsis (text);
 782 		needs_reflow = 1;
 783 		break;
 784 
 785 	case PROP_LINE_WRAP:
 786 		text->line_wrap = g_value_get_boolean (value);
 787 		if (text->line_wrap) {
 788 			if (text->layout) {
 789 				pango_layout_set_width (
 790 					text->layout, text->width < 0
 791 					? -1 : text->width * PANGO_SCALE);
 792 			}
 793 		}
 794 		text->needs_split_into_lines = 1;
 795 		needs_reflow = 1;
 796 		break;
 797 
 798 	case PROP_BREAK_CHARACTERS:
 799 		if (text->break_characters) {
 800 			g_free (text->break_characters);
 801 			text->break_characters = NULL;
 802 		}
 803 		if (g_value_get_string (value))
 804 			text->break_characters = g_strdup (g_value_get_string (value));
 805 		text->needs_split_into_lines = 1;
 806 		needs_reflow = 1;
 807 		break;
 808 
 809 	case PROP_MAX_LINES:
 810 		text->max_lines = g_value_get_int (value);
 811 		text->needs_split_into_lines = 1;
 812 		needs_reflow = 1;
 813 		break;
 814 
 815 	case PROP_WIDTH:
 816 		text->clip_width = fabs (g_value_get_double (value));
 817 		calc_ellipsis (text);
 818 		if (text->line_wrap) {
 819 			if (text->layout) {
 820 				pango_layout_set_width (
 821 					text->layout, text->width < 0 ?
 822 					-1 : text->width * PANGO_SCALE);
 823 			}
 824 			text->needs_split_into_lines = 1;
 825 		}
 826 		else {
 827 			text->needs_calc_height = 1;
 828 		}
 829 		needs_reflow = 1;
 830 		break;
 831 
 832 	case PROP_ALLOW_NEWLINES:
 833 		text->allow_newlines = g_value_get_boolean (value);
 834 		_get_tep (text);
 835 		g_object_set (
 836 			text->tep,
 837 			"allow_newlines", g_value_get_boolean (value),
 838 			NULL);
 839 		break;
 840 
 841 	case PROP_CURSOR_POS: {
 842 		ETextEventProcessorCommand command;
 843 
 844 		command.action = E_TEP_MOVE;
 845 		command.position = E_TEP_VALUE;
 846 		command.value = g_value_get_int (value);
 847 		command.time = GDK_CURRENT_TIME;
 848 		e_text_command (text->tep, &command, text);
 849 		break;
 850 	}
 851 
 852 	case PROP_IM_CONTEXT:
 853 		if (text->im_context) {
 854 			disconnect_im_context (text);
 855 			g_object_unref (text->im_context);
 856 		}
 857 
 858 		text->im_context = g_value_get_object (value);
 859 		if (text->im_context)
 860 			g_object_ref (text->im_context);
 861 
 862 		text->need_im_reset = TRUE;
 863 		break;
 864 
 865 	case PROP_HANDLE_POPUP:
 866 		text->handle_popup = g_value_get_boolean (value);
 867 		break;
 868 
 869 	default:
 870 		return;
 871 	}
 872 
 873 	if (needs_reflow)
 874 		e_canvas_item_request_reflow (item);
 875 	if (needs_update)
 876 		gnome_canvas_item_request_update (item);
 877 }
 878 
 879 /* Get_arg handler for the text item */
 880 static void
 881 e_text_get_property (GObject *object,
 882                     guint property_id,
 883                     GValue *value,
 884                     GParamSpec *pspec)
 885 {
 886 	EText *text;
 887 
 888 	text = E_TEXT (object);
 889 
 890 	switch (property_id) {
 891 	case PROP_MODEL:
 892 		g_value_set_object (value, text->model);
 893 		break;
 894 
 895 	case PROP_EVENT_PROCESSOR:
 896 		_get_tep (text);
 897 		g_value_set_object (value, text->tep);
 898 		break;
 899 
 900 	case PROP_TEXT:
 901 		g_value_set_string (value, text->text);
 902 		break;
 903 
 904 	case PROP_BOLD:
 905 		g_value_set_boolean (value, text->bold);
 906 		break;
 907 
 908 	case PROP_STRIKEOUT:
 909 		g_value_set_boolean (value, text->strikeout);
 910 		break;
 911 
 912 	case PROP_JUSTIFICATION:
 913 		g_value_set_enum (value, text->justification);
 914 		break;
 915 
 916 	case PROP_CLIP_WIDTH:
 917 		g_value_set_double (value, text->clip_width);
 918 		break;
 919 
 920 	case PROP_CLIP_HEIGHT:
 921 		g_value_set_double (value, text->clip_height);
 922 		break;
 923 
 924 	case PROP_CLIP:
 925 		g_value_set_boolean (value, text->clip);
 926 		break;
 927 
 928 	case PROP_FILL_CLIP_RECTANGLE:
 929 		g_value_set_boolean (value, text->fill_clip_rectangle);
 930 		break;
 931 
 932 	case PROP_X_OFFSET:
 933 		g_value_set_double (value, text->xofs);
 934 		break;
 935 
 936 	case PROP_Y_OFFSET:
 937 		g_value_set_double (value, text->yofs);
 938 		break;
 939 
 940 	case PROP_FILL_COLOR_RGBA:
 941 		g_value_set_uint (value, text->rgba);
 942 		break;
 943 
 944 	case PROP_TEXT_WIDTH:
 945 		g_value_set_double (value, text->width);
 946 		break;
 947 
 948 	case PROP_TEXT_HEIGHT:
 949 		g_value_set_double (value, text->height);
 950 		break;
 951 
 952 	case PROP_EDITABLE:
 953 		g_value_set_boolean (value, text->editable);
 954 		break;
 955 
 956 	case PROP_USE_ELLIPSIS:
 957 		g_value_set_boolean (value, text->use_ellipsis);
 958 		break;
 959 
 960 	case PROP_ELLIPSIS:
 961 		g_value_set_string (value, text->ellipsis);
 962 		break;
 963 
 964 	case PROP_LINE_WRAP:
 965 		g_value_set_boolean (value, text->line_wrap);
 966 		break;
 967 
 968 	case PROP_BREAK_CHARACTERS:
 969 		g_value_set_string (value, text->break_characters);
 970 		break;
 971 
 972 	case PROP_MAX_LINES:
 973 		g_value_set_int (value, text->max_lines);
 974 		break;
 975 
 976 	case PROP_WIDTH:
 977 		g_value_set_double (value, text->clip_width);
 978 		break;
 979 
 980 	case PROP_HEIGHT:
 981 		g_value_set_double (
 982 			value, text->clip &&
 983 			text->clip_height != -1 ?
 984 			text->clip_height : text->height);
 985 		break;
 986 
 987 	case PROP_ALLOW_NEWLINES:
 988 		g_value_set_boolean (value, text->allow_newlines);
 989 		break;
 990 
 991 	case PROP_CURSOR_POS:
 992 		g_value_set_int (value, text->selection_start);
 993 		break;
 994 
 995 	case PROP_IM_CONTEXT:
 996 		g_value_set_object (value, text->im_context);
 997 		break;
 998 
 999 	case PROP_HANDLE_POPUP:
1000 		g_value_set_boolean (value, text->handle_popup);
1001 		break;
1002 
1003 	default:
1004 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1005 		break;
1006 	}
1007 }
1008 
1009 /* Update handler for the text item */
1010 static void
1011 e_text_reflow (GnomeCanvasItem *item,
1012                gint flags)
1013 {
1014 	EText *text;
1015 
1016 	text = E_TEXT (item);
1017 
1018 	if (text->needs_reset_layout) {
1019 		reset_layout (text);
1020 		text->needs_reset_layout = 0;
1021 		text->needs_calc_height = 1;
1022 	}
1023 
1024 	if (text->needs_split_into_lines) {
1025 		split_into_lines (text);
1026 
1027 		text->needs_split_into_lines = 0;
1028 		text->needs_calc_height = 1;
1029 	}
1030 
1031 	if (text->needs_calc_height) {
1032 		calc_height (text);
1033 		gnome_canvas_item_request_update (item);
1034 		text->needs_calc_height = 0;
1035 		text->needs_recalc_bounds = 1;
1036 	}
1037 }
1038 
1039 /* Update handler for the text item */
1040 static void
1041 e_text_update (GnomeCanvasItem *item,
1042                const cairo_matrix_t *i2c,
1043                gint flags)
1044 {
1045 	EText *text;
1046 	gdouble x1, y1, x2, y2;
1047 
1048 	text = E_TEXT (item);
1049 
1050 	if (GNOME_CANVAS_ITEM_CLASS (e_text_parent_class)->update)
1051 		GNOME_CANVAS_ITEM_CLASS (e_text_parent_class)->update (
1052 			item, i2c, flags);
1053 
1054 	if (text->needs_recalc_bounds
1055 	     || (flags & GNOME_CANVAS_UPDATE_AFFINE)) {
1056 		get_bounds (text, &x1, &y1, &x2, &y2);
1057 		if (item->x1 != x1 ||
1058 		    item->x2 != x2 ||
1059 		    item->y1 != y1 ||
1060 		    item->y2 != y2) {
1061 			gnome_canvas_request_redraw (
1062 				item->canvas, item->x1, item->y1,
1063 				item->x2, item->y2);
1064 			item->x1 = x1;
1065 			item->y1 = y1;
1066 			item->x2 = x2;
1067 			item->y2 = y2;
1068 			text->needs_redraw = 1;
1069 			item->canvas->need_repick = TRUE;
1070 		}
1071 		if (!text->fill_clip_rectangle)
1072 			item->canvas->need_repick = TRUE;
1073 		text->needs_recalc_bounds = 0;
1074 	}
1075 	if (text->needs_redraw) {
1076 		gnome_canvas_request_redraw (
1077 			item->canvas, item->x1, item->y1, item->x2, item->y2);
1078 		text->needs_redraw = 0;
1079 	}
1080 }
1081 
1082 /* Realize handler for the text item */
1083 static void
1084 e_text_realize (GnomeCanvasItem *item)
1085 {
1086 	EText *text;
1087 
1088 	text = E_TEXT (item);
1089 
1090 	if (GNOME_CANVAS_ITEM_CLASS (e_text_parent_class)->realize)
1091 		(* GNOME_CANVAS_ITEM_CLASS (e_text_parent_class)->realize) (item);
1092 
1093 	create_layout (text);
1094 
1095 	text->i_cursor = gdk_cursor_new (GDK_XTERM);
1096 	text->default_cursor = gdk_cursor_new (GDK_LEFT_PTR);
1097 }
1098 
1099 /* Unrealize handler for the text item */
1100 static void
1101 e_text_unrealize (GnomeCanvasItem *item)
1102 {
1103 	EText *text;
1104 
1105 	text = E_TEXT (item);
1106 
1107 	g_object_unref (text->i_cursor);
1108 	text->i_cursor = NULL;
1109 	g_object_unref (text->default_cursor);
1110 	text->default_cursor = NULL;
1111 
1112 	if (GNOME_CANVAS_ITEM_CLASS (e_text_parent_class)->unrealize)
1113 		(* GNOME_CANVAS_ITEM_CLASS (e_text_parent_class)->unrealize) (item);
1114 }
1115 
1116 static void
1117 _get_tep (EText *text)
1118 {
1119 	if (!text->tep) {
1120 		text->tep = e_text_event_processor_emacs_like_new ();
1121 
1122 		text->tep_command_id = g_signal_connect (
1123 			text->tep, "command",
1124 			G_CALLBACK (e_text_command), text);
1125 	}
1126 }
1127 
1128 static void
1129 draw_pango_rectangle (cairo_t *cr,
1130                       gint x1,
1131                       gint y1,
1132                       PangoRectangle rect)
1133 {
1134 	gint width = rect.width / PANGO_SCALE;
1135 	gint height = rect.height / PANGO_SCALE;
1136 
1137 	if (width <= 0)
1138 		width = 1;
1139 	if (height <= 0)
1140 		height = 1;
1141 
1142 	cairo_rectangle (
1143 		cr, x1 + rect.x / PANGO_SCALE,
1144 		y1 + rect.y / PANGO_SCALE, width, height);
1145 	cairo_fill (cr);
1146 }
1147 
1148 static gboolean
1149 show_pango_rectangle (EText *text,
1150                       PangoRectangle rect)
1151 {
1152 	gint x1 = rect.x / PANGO_SCALE;
1153 	gint x2 = (rect.x + rect.width) / PANGO_SCALE;
1154 
1155 	gint y1 = rect.y / PANGO_SCALE;
1156 	gint y2 = (rect.y + rect.height) / PANGO_SCALE;
1157 
1158 	gint new_xofs_edit = text->xofs_edit;
1159 	gint new_yofs_edit = text->yofs_edit;
1160 
1161 	gint clip_width, clip_height;
1162 
1163 	clip_width = text->clip_width;
1164 	clip_height = text->clip_height;
1165 
1166 	if (x1 < new_xofs_edit)
1167 		new_xofs_edit = x1;
1168 
1169 	if (y1 < new_yofs_edit)
1170 		new_yofs_edit = y1;
1171 
1172 	if (clip_width >= 0) {
1173 		if (2 + x2 - clip_width > new_xofs_edit)
1174 			new_xofs_edit = 2 + x2 - clip_width;
1175 	} else {
1176 		new_xofs_edit = 0;
1177 	}
1178 
1179 	if (clip_height >= 0) {
1180 		if (y2 - clip_height > new_yofs_edit)
1181 			new_yofs_edit = y2 - clip_height;
1182 	} else {
1183 		new_yofs_edit = 0;
1184 	}
1185 
1186 	if (new_xofs_edit < 0)
1187 		new_xofs_edit = 0;
1188 	if (new_yofs_edit < 0)
1189 		new_yofs_edit = 0;
1190 
1191 	if (new_xofs_edit != text->xofs_edit ||
1192 	    new_yofs_edit != text->yofs_edit) {
1193 		text->xofs_edit = new_xofs_edit;
1194 		text->yofs_edit = new_yofs_edit;
1195 		return TRUE;
1196 	}
1197 
1198 	return FALSE;
1199 }
1200 
1201 /* Draw handler for the text item */
1202 static void
1203 e_text_draw (GnomeCanvasItem *item,
1204              cairo_t *cr,
1205              gint x,
1206              gint y,
1207              gint width,
1208              gint height)
1209 {
1210 	EText *text;
1211 	gint xpos, ypos;
1212 	GnomeCanvas *canvas;
1213 	GtkWidget *widget;
1214 	GtkStyle *style;
1215 	GtkStateType state;
1216 
1217 	text = E_TEXT (item);
1218 	canvas = GNOME_CANVAS_ITEM (text)->canvas;
1219 	widget = GTK_WIDGET (canvas);
1220 	state = gtk_widget_get_state (widget);
1221 	style = gtk_widget_get_style (widget);
1222 
1223 	cairo_save (cr);
1224 
1225 	if (!text->rgba_set) {
1226 		gdk_cairo_set_source_color (cr, &style->fg[state]);
1227 	} else {
1228 		cairo_set_source_rgba (
1229 			cr,
1230 			((text->rgba >> 24) & 0xff) / 255.0,
1231 			((text->rgba >> 16) & 0xff) / 255.0,
1232 			((text->rgba >>  8) & 0xff) / 255.0,
1233 			( text->rgba        & 0xff) / 255.0);
1234 	}
1235 
1236 	/* Insert preedit text only when im_context signals are connected &
1237 	 * text->preedit_len is not zero */
1238 	if (text->im_context_signals_registered && text->preedit_len)
1239 		insert_preedit_text (text);
1240 
1241 	/* Need to reset the layout to cleanly clear the preedit buffer when
1242 	 * typing in CJK & using backspace on the preedit */
1243 	if (!text->preedit_len)
1244 		reset_layout (text);
1245 
1246 	if (!pango_layout_get_text (text->layout)) {
1247 		cairo_restore (cr);
1248 		return;
1249 	}
1250 
1251 	xpos = text->text_cx;
1252 	ypos = text->text_cy;
1253 
1254 	xpos = xpos - x + text->xofs;
1255 	ypos = ypos - y + text->yofs;
1256 
1257 	cairo_save (cr);
1258 
1259 	if (text->clip) {
1260 		cairo_rectangle (
1261 			cr, xpos, ypos,
1262 			text->clip_cwidth - text->xofs,
1263 			text->clip_cheight - text->yofs);
1264 		cairo_clip (cr);
1265 	}
1266 
1267 	if (text->editing) {
1268 		xpos -= text->xofs_edit;
1269 		ypos -= text->yofs_edit;
1270 	}
1271 
1272 	cairo_move_to (cr, xpos, ypos);
1273 	pango_cairo_show_layout (cr, text->layout);
1274 
1275 	if (text->editing) {
1276 		if (text->selection_start != text->selection_end) {
1277 			cairo_region_t *clip_region = cairo_region_create ();
Value stored to 'clip_region' during its initialization is never read
(emitted by clang-analyzer)

TODO: a detailed trace is available in the data model (not yet rendered in this report)

Value stored to 'clip_region' during its initialization is never read
(emitted by clang-analyzer)

TODO: a detailed trace is available in the data model (not yet rendered in this report)

1278 gint indices[2]; 1279 GtkStateType state; 1280 1281 state = GTK_STATE_ACTIVE; 1282 1283 indices[0] = MIN ( 1284 text->selection_start, 1285 text->selection_end); 1286 indices[1] = MAX ( 1287 text->selection_start, 1288 text->selection_end); 1289 1290 /* convert these into byte indices */ 1291 indices[0] = g_utf8_offset_to_pointer ( 1292 text->text, indices[0]) - text->text; 1293 indices[1] = g_utf8_offset_to_pointer ( 1294 text->text, indices[1]) - text->text; 1295 1296 clip_region = gdk_pango_layout_get_clip_region ( 1297 text->layout, xpos, ypos, indices, 1); 1298 gdk_cairo_region (cr, clip_region); 1299 cairo_clip (cr); 1300 cairo_region_destroy (clip_region); 1301 1302 gdk_cairo_set_source_color (cr, &style->base[state]); 1303 cairo_paint (cr); 1304 1305 gdk_cairo_set_source_color (cr, &style->text[state]); 1306 cairo_move_to (cr, xpos, ypos); 1307 pango_cairo_show_layout (cr, text->layout); 1308 } else { 1309 if (text->show_cursor) { 1310 PangoRectangle strong_pos, weak_pos; 1311 gchar *offs; 1312 1313 offs = g_utf8_offset_to_pointer ( 1314 text->text, text->selection_start); 1315 1316 pango_layout_get_cursor_pos ( 1317 text->layout, offs - text->text + 1318 text->preedit_len, &strong_pos, 1319 &weak_pos); 1320 draw_pango_rectangle (cr, xpos, ypos, strong_pos); 1321 if (strong_pos.x != weak_pos.x || 1322 strong_pos.y != weak_pos.y || 1323 strong_pos.width != weak_pos.width || 1324 strong_pos.height != weak_pos.height) 1325 draw_pango_rectangle (cr, xpos, ypos, weak_pos); 1326 } 1327 } 1328 } 1329 1330 cairo_restore (cr); 1331 cairo_restore (cr); 1332 } 1333 1334 /* Point handler for the text item */ 1335 static GnomeCanvasItem * 1336 e_text_point (GnomeCanvasItem *item, 1337 gdouble x, 1338 gdouble y, 1339 gint cx, 1340 gint cy) 1341 { 1342 EText *text; 1343 gdouble clip_width; 1344 gdouble clip_height; 1345 1346 text = E_TEXT (item); 1347 1348 /* The idea is to build bounding rectangles for each of the lines of 1349 * text (clipped by the clipping rectangle, if it is activated) and see 1350 * whether the point is inside any of these. If it is, we are done. 1351 * Otherwise, calculate the distance to the nearest rectangle. 1352 */ 1353 1354 if (text->clip_width < 0) 1355 clip_width = text->width; 1356 else 1357 clip_width = text->clip_width; 1358 1359 if (text->clip_height < 0) 1360 clip_height = text->height; 1361 else 1362 clip_height = text->clip_height; 1363 1364 if (cx < text->clip_cx || 1365 cx > text->clip_cx + clip_width || 1366 cy < text->clip_cy || 1367 cy > text->clip_cy + clip_height) 1368 return NULL; 1369 1370 if (text->fill_clip_rectangle || !text->text || !*text->text) 1371 return item; 1372 1373 cx -= text->cx; 1374 1375 if (pango_layout_xy_to_index (text->layout, cx, cy, NULL, NULL)) 1376 return item; 1377 1378 return NULL; 1379 } 1380 1381 /* Bounds handler for the text item */ 1382 static void 1383 e_text_bounds (GnomeCanvasItem *item, 1384 gdouble *x1, 1385 gdouble *y1, 1386 gdouble *x2, 1387 gdouble *y2) 1388 { 1389 EText *text; 1390 gdouble width, height; 1391 1392 text = E_TEXT (item); 1393 1394 *x1 = 0; 1395 *y1 = 0; 1396 1397 width = text->width; 1398 height = text->height; 1399 1400 if (text->clip) { 1401 if (text->clip_width >= 0) 1402 width = text->clip_width; 1403 if (text->clip_height >= 0) 1404 height = text->clip_height; 1405 } 1406 1407 *x2 = *x1 + width; 1408 *y2 = *y1 + height; 1409 } 1410 1411 static gint 1412 get_position_from_xy (EText *text, 1413 gint x, 1414 gint y) 1415 { 1416 gint index; 1417 gint trailing; 1418 1419 x -= text->xofs; 1420 y -= text->yofs; 1421 1422 if (text->editing) { 1423 x += text->xofs_edit; 1424 y += text->yofs_edit; 1425 } 1426 1427 x -= text->cx; 1428 y -= text->cy; 1429 1430 pango_layout_xy_to_index ( 1431 text->layout, x * PANGO_SCALE, 1432 y * PANGO_SCALE, &index, &trailing); 1433 1434 return g_utf8_pointer_to_offset (text->text, text->text + index + trailing); 1435 } 1436 1437 #define SCROLL_WAIT_TIME 30000 1438 1439 static gboolean 1440 _blink_scroll_timeout (gpointer data) 1441 { 1442 EText *text = E_TEXT (data); 1443 gulong current_time; 1444 gboolean scroll = FALSE; 1445 gboolean redraw = FALSE; 1446 1447 g_timer_elapsed (text->timer, &current_time); 1448 1449 if (text->scroll_start + SCROLL_WAIT_TIME > 1000000) { 1450 if (current_time > text->scroll_start - (1000000 - SCROLL_WAIT_TIME) && 1451 current_time < text->scroll_start) 1452 scroll = TRUE; 1453 } else { 1454 if (current_time > text->scroll_start + SCROLL_WAIT_TIME || 1455 current_time < text->scroll_start) 1456 scroll = TRUE; 1457 } 1458 if (scroll && text->button_down && text->clip) { 1459 gint old_xofs_edit = text->xofs_edit; 1460 gint old_yofs_edit = text->yofs_edit; 1461 1462 if (text->clip_cwidth >= 0 && 1463 text->lastx - text->clip_cx > text->clip_cwidth && 1464 text->xofs_edit < text->width - text->clip_cwidth) { 1465 text->xofs_edit += 4; 1466 if (text->xofs_edit > text->width - text->clip_cwidth + 1) 1467 text->xofs_edit = text->width - text->clip_cwidth + 1; 1468 } 1469 if (text->lastx - text->clip_cx < 0 && 1470 text->xofs_edit > 0) { 1471 text->xofs_edit -= 4; 1472 if (text->xofs_edit < 0) 1473 text->xofs_edit = 0; 1474 } 1475 1476 if (text->clip_cheight >= 0 && 1477 text->lasty - text->clip_cy > text->clip_cheight && 1478 text->yofs_edit < text->height - text->clip_cheight) { 1479 text->yofs_edit += 4; 1480 if (text->yofs_edit > text->height - text->clip_cheight + 1) 1481 text->yofs_edit = text->height - text->clip_cheight + 1; 1482 } 1483 if (text->lasty - text->clip_cy < 0 && 1484 text->yofs_edit > 0) { 1485 text->yofs_edit -= 4; 1486 if (text->yofs_edit < 0) 1487 text->yofs_edit = 0; 1488 } 1489 1490 if (old_xofs_edit != text->xofs_edit || 1491 old_yofs_edit != text->yofs_edit) { 1492 ETextEventProcessorEvent e_tep_event; 1493 e_tep_event.type = GDK_MOTION_NOTIFY; 1494 e_tep_event.motion.state = text->last_state; 1495 e_tep_event.motion.time = 0; 1496 e_tep_event.motion.position = 1497 get_position_from_xy ( 1498 text, text->lastx, text->lasty); 1499 _get_tep (text); 1500 e_text_event_processor_handle_event ( 1501 text->tep, 1502 &e_tep_event); 1503 text->scroll_start = current_time; 1504 redraw = TRUE; 1505 } 1506 } 1507 1508 if (!((current_time / 500000) % 2)) { 1509 if (!text->show_cursor) 1510 redraw = TRUE; 1511 text->show_cursor = TRUE; 1512 } else { 1513 if (text->show_cursor) 1514 redraw = TRUE; 1515 text->show_cursor = FALSE; 1516 } 1517 if (redraw) { 1518 text->needs_redraw = 1; 1519 gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (text)); 1520 } 1521 return TRUE; 1522 } 1523 1524 static void 1525 start_editing (EText *text) 1526 { 1527 if (text->editing) 1528 return; 1529 1530 e_text_reset_im_context (text); 1531 1532 g_free (text->revert); 1533 text->revert = g_strdup (text->text); 1534 1535 text->editing = TRUE; 1536 if (text->pointer_in) { 1537 GdkWindow *window; 1538 1539 window = gtk_widget_get_window ( 1540 GTK_WIDGET (GNOME_CANVAS_ITEM (text)->canvas)); 1541 1542 if (text->default_cursor_shown) { 1543 gdk_window_set_cursor (window, text->i_cursor); 1544 text->default_cursor_shown = FALSE; 1545 } 1546 } 1547 text->select_by_word = FALSE; 1548 text->xofs_edit = 0; 1549 text->yofs_edit = 0; 1550 if (text->timeout_id == 0) 1551 text->timeout_id = g_timeout_add (10, _blink_scroll_timeout, text); 1552 text->timer = g_timer_new (); 1553 g_timer_elapsed (text->timer, &(text->scroll_start)); 1554 g_timer_start (text->timer); 1555 } 1556 1557 void 1558 e_text_stop_editing (EText *text) 1559 { 1560 if (!text->editing) 1561 return; 1562 1563 g_free (text->revert); 1564 text->revert = NULL; 1565 1566 text->editing = FALSE; 1567 if (!text->default_cursor_shown) { 1568 GdkWindow *window; 1569 1570 window = gtk_widget_get_window ( 1571 GTK_WIDGET (GNOME_CANVAS_ITEM (text)->canvas)); 1572 gdk_window_set_cursor (window, text->default_cursor); 1573 text->default_cursor_shown = TRUE; 1574 } 1575 if (text->timer) { 1576 g_timer_stop (text->timer); 1577 g_timer_destroy (text->timer); 1578 text->timer = NULL; 1579 } 1580 1581 text->need_im_reset = TRUE; 1582 text->preedit_len = 0; 1583 text->preedit_pos = 0; 1584 } 1585 1586 void 1587 e_text_cancel_editing (EText *text) 1588 { 1589 if (text->revert) 1590 e_text_model_set_text (text->model, text->revert); 1591 e_text_stop_editing (text); 1592 } 1593 1594 static gboolean 1595 _click (gpointer data) 1596 { 1597 *(gint *)data = 0; 1598 return FALSE; 1599 } 1600 1601 static gint 1602 e_text_event (GnomeCanvasItem *item, 1603 GdkEvent *event) 1604 { 1605 EText *text = E_TEXT (item); 1606 ETextEventProcessorEvent e_tep_event; 1607 GdkWindow *window; 1608 gint return_val = 0; 1609 1610 if (!text->model) 1611 return 0; 1612 1613 window = gtk_widget_get_window (GTK_WIDGET (item->canvas)); 1614 1615 e_tep_event.type = event->type; 1616 switch (event->type) { 1617 case GDK_FOCUS_CHANGE: 1618 if (text->editable) { 1619 GdkEventFocus *focus_event; 1620 focus_event = (GdkEventFocus *) event; 1621 if (focus_event->in) { 1622 if (text->im_context) { 1623 if (!text->im_context_signals_registered) { 1624 g_signal_connect ( 1625 text->im_context, "commit", 1626 G_CALLBACK (e_text_commit_cb), text); 1627 g_signal_connect ( 1628 text->im_context, "preedit_changed", 1629 G_CALLBACK (e_text_preedit_changed_cb), text); 1630 g_signal_connect ( 1631 text->im_context, "retrieve_surrounding", 1632 G_CALLBACK (e_text_retrieve_surrounding_cb), text); 1633 g_signal_connect ( 1634 text->im_context, "delete_surrounding", 1635 G_CALLBACK (e_text_delete_surrounding_cb), text); 1636 text->im_context_signals_registered = TRUE; 1637 } 1638 gtk_im_context_focus_in (text->im_context); 1639 } 1640 1641 start_editing (text); 1642 1643 /* So we'll redraw and the 1644 * cursor will be shown. */ 1645 text->show_cursor = FALSE; 1646 } else { 1647 if (text->im_context) { 1648 gtk_im_context_focus_out (text->im_context); 1649 disconnect_im_context (text); 1650 text->need_im_reset = TRUE; 1651 } 1652 1653 e_text_stop_editing (text); 1654 if (text->timeout_id) { 1655 g_source_remove (text->timeout_id); 1656 text->timeout_id = 0; 1657 } 1658 if (text->show_cursor) { 1659 text->show_cursor = FALSE; 1660 text->needs_redraw = 1; 1661 gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (text)); 1662 } 1663 } 1664 if (text->line_wrap) 1665 text->needs_split_into_lines = 1; 1666 e_canvas_item_request_reflow (GNOME_CANVAS_ITEM (text)); 1667 } 1668 return_val = 0; 1669 break; 1670 case GDK_KEY_PRESS: 1671 1672 /* Handle S-F10 key binding here. */ 1673 1674 if (event->key.keyval == GDK_KEY_F10 1675 && (event->key.state & GDK_SHIFT_MASK) 1676 && text->handle_popup) { 1677 1678 /* Simulate a GdkEventButton here, so that we can 1679 * call e_text_do_popup directly */ 1680 1681 GdkEventButton *button; 1682 1683 button = (GdkEventButton *) 1684 gdk_event_new (GDK_BUTTON_PRESS); 1685 button->time = event->key.time; 1686 button->button = 0; 1687 e_text_do_popup (text, button, 0); 1688 return 1; 1689 } 1690 1691 /* Fall Through */ 1692 1693 case GDK_KEY_RELEASE: 1694 1695 if (text->editing) { 1696 GdkEventKey key; 1697 gint ret; 1698 1699 if (text->im_context && 1700 gtk_im_context_filter_keypress ( 1701 text->im_context, 1702 (GdkEventKey *) event)) { 1703 text->need_im_reset = TRUE; 1704 return 1; 1705 } 1706 1707 key = event->key; 1708 e_tep_event.key.time = key.time; 1709 e_tep_event.key.state = key.state; 1710 e_tep_event.key.keyval = key.keyval; 1711 1712 /* This is probably ugly hack, but we 1713 * have to handle UTF-8 input somehow. */ 1714 e_tep_event.key.string = e_utf8_from_gtk_event_key ( 1715 GTK_WIDGET (item->canvas), 1716 key.keyval, key.string); 1717 if (e_tep_event.key.string != NULL) { 1718 e_tep_event.key.length = strlen (e_tep_event.key.string); 1719 } else { 1720 e_tep_event.key.length = 0; 1721 } 1722 1723 _get_tep (text); 1724 ret = e_text_event_processor_handle_event (text->tep, &e_tep_event); 1725 1726 if (event->type == GDK_KEY_PRESS) 1727 g_signal_emit ( 1728 text, e_text_signals[E_TEXT_KEYPRESS], 0, 1729 e_tep_event.key.keyval, e_tep_event.key.state); 1730 1731 if (e_tep_event.key.string) 1732 g_free ((gpointer) e_tep_event.key.string); 1733 1734 return ret; 1735 } 1736 break; 1737 case GDK_BUTTON_PRESS: /* Fall Through */ 1738 case GDK_BUTTON_RELEASE: 1739 if ((!text->editing) 1740 && text->editable 1741 && (event->button.button == 1 || 1742 event->button.button == 2)) { 1743 e_canvas_item_grab_focus (item, TRUE); 1744 start_editing (text); 1745 } 1746 1747 /* We follow convention and emit popup events on right-clicks. */ 1748 if (event->type == GDK_BUTTON_PRESS && event->button.button == 3) { 1749 if (text->handle_popup) { 1750 e_text_do_popup ( 1751 text, &(event->button), 1752 get_position_from_xy ( 1753 text, event->button.x, 1754 event->button.y)); 1755 return 1; 1756 } 1757 else { 1758 break; 1759 } 1760 } 1761 1762 /* Create our own double and triple click events, 1763 * as gnome-canvas doesn't forward them to us */ 1764 if (event->type == GDK_BUTTON_PRESS) { 1765 if (text->dbl_timeout == 0 && 1766 text->tpl_timeout == 0) { 1767 text->dbl_timeout = g_timeout_add ( 1768 200, _click, &(text->dbl_timeout)); 1769 } else { 1770 if (text->tpl_timeout == 0) { 1771 e_tep_event.type = GDK_2BUTTON_PRESS; 1772 text->tpl_timeout = g_timeout_add ( 1773 200, _click, &(text->tpl_timeout)); 1774 } else { 1775 e_tep_event.type = GDK_3BUTTON_PRESS; 1776 } 1777 } 1778 } 1779 1780 if (text->editing) { 1781 GdkEventButton button = event->button; 1782 e_tep_event.button.time = button.time; 1783 e_tep_event.button.state = button.state; 1784 e_tep_event.button.button = button.button; 1785 e_tep_event.button.position = 1786 get_position_from_xy ( 1787 text, button.x, button.y); 1788 _get_tep (text); 1789 return_val = e_text_event_processor_handle_event ( 1790 text->tep, &e_tep_event); 1791 if (event->button.button == 1) { 1792 if (event->type == GDK_BUTTON_PRESS) 1793 text->button_down = TRUE; 1794 else 1795 text->button_down = FALSE; 1796 } 1797 text->lastx = button.x; 1798 text->lasty = button.y; 1799 text->last_state = button.state; 1800 } 1801 break; 1802 case GDK_MOTION_NOTIFY: 1803 if (text->editing) { 1804 GdkEventMotion motion = event->motion; 1805 e_tep_event.motion.time = motion.time; 1806 e_tep_event.motion.state = motion.state; 1807 e_tep_event.motion.position = 1808 get_position_from_xy ( 1809 text, motion.x, motion.y); 1810 _get_tep (text); 1811 return_val = e_text_event_processor_handle_event ( 1812 text->tep, &e_tep_event); 1813 text->lastx = motion.x; 1814 text->lasty = motion.y; 1815 text->last_state = motion.state; 1816 } 1817 break; 1818 case GDK_ENTER_NOTIFY: 1819 text->pointer_in = TRUE; 1820 if (text->editing) { 1821 if (text->default_cursor_shown) { 1822 gdk_window_set_cursor (window, text->i_cursor); 1823 text->default_cursor_shown = FALSE; 1824 } 1825 } 1826 break; 1827 case GDK_LEAVE_NOTIFY: 1828 text->pointer_in = FALSE; 1829 if (text->editing) { 1830 if (!text->default_cursor_shown) { 1831 gdk_window_set_cursor ( 1832 window, text->default_cursor); 1833 text->default_cursor_shown = TRUE; 1834 } 1835 } 1836 break; 1837 default: 1838 break; 1839 } 1840 if (return_val) 1841 return return_val; 1842 if (GNOME_CANVAS_ITEM_CLASS (e_text_parent_class)->event) 1843 return GNOME_CANVAS_ITEM_CLASS (e_text_parent_class)->event (item, event); 1844 else 1845 return 0; 1846 } 1847 1848 void 1849 e_text_copy_clipboard (EText *text) 1850 { 1851 gint selection_start_pos; 1852 gint selection_end_pos; 1853 1854 selection_start_pos = MIN (text->selection_start, text->selection_end); 1855 selection_end_pos = MAX (text->selection_start, text->selection_end); 1856 1857 /* convert sel_start/sel_end to byte indices */ 1858 selection_start_pos = g_utf8_offset_to_pointer ( 1859 text->text, selection_start_pos) - text->text; 1860 selection_end_pos = g_utf8_offset_to_pointer ( 1861 text->text, selection_end_pos) - text->text; 1862 1863 gtk_clipboard_set_text ( 1864 gtk_widget_get_clipboard ( 1865 GTK_WIDGET (GNOME_CANVAS_ITEM (text)->canvas), 1866 GDK_SELECTION_CLIPBOARD), 1867 text->text + selection_start_pos, 1868 selection_end_pos - selection_start_pos); 1869 } 1870 1871 void 1872 e_text_delete_selection (EText *text) 1873 { 1874 gint sel_start, sel_end; 1875 1876 sel_start = MIN (text->selection_start, text->selection_end); 1877 sel_end = MAX (text->selection_start, text->selection_end); 1878 1879 if (sel_start != sel_end) 1880 e_text_model_delete (text->model, sel_start, sel_end - sel_start); 1881 text->need_im_reset = TRUE; 1882 } 1883 1884 void 1885 e_text_cut_clipboard (EText *text) 1886 { 1887 e_text_copy_clipboard (text); 1888 e_text_delete_selection (text); 1889 } 1890 1891 void 1892 e_text_paste_clipboard (EText *text) 1893 { 1894 ETextEventProcessorCommand command; 1895 1896 command.action = E_TEP_PASTE; 1897 command.position = E_TEP_SELECTION; 1898 command.string = ""; 1899 command.value = 0; 1900 e_text_command (text->tep, &command, text); 1901 } 1902 1903 void 1904 e_text_select_all (EText *text) 1905 { 1906 ETextEventProcessorCommand command; 1907 1908 command.action = E_TEP_SELECT; 1909 command.position = E_TEP_SELECT_ALL; 1910 command.string = ""; 1911 command.value = 0; 1912 e_text_command (text->tep, &command, text); 1913 } 1914 1915 static void 1916 primary_get_cb (GtkClipboard *clipboard, 1917 GtkSelectionData *selection_data, 1918 guint info, 1919 gpointer data) 1920 { 1921 EText *text = E_TEXT (data); 1922 gint sel_start, sel_end; 1923 1924 sel_start = MIN (text->selection_start, text->selection_end); 1925 sel_end = MAX (text->selection_start, text->selection_end); 1926 1927 /* convert sel_start/sel_end to byte indices */ 1928 sel_start = g_utf8_offset_to_pointer (text->text, sel_start) - text->text; 1929 sel_end = g_utf8_offset_to_pointer (text->text, sel_end) - text->text; 1930 1931 if (sel_start != sel_end) { 1932 gtk_selection_data_set_text ( 1933 selection_data, 1934 text->text + sel_start, 1935 sel_end - sel_start); 1936 } 1937 } 1938 1939 static void 1940 primary_clear_cb (GtkClipboard *clipboard, 1941 gpointer data) 1942 { 1943 #ifdef notyet 1944 /* XXX */ 1945 gtk_editable_select_region ( 1946 GTK_EDITABLE (entry), entry->current_pos, entry->current_pos); 1947 #endif 1948 } 1949 1950 static void 1951 e_text_update_primary_selection (EText *text) 1952 { 1953 static const GtkTargetEntry targets[] = { 1954 { (gchar *) "UTF8_STRING", 0, 0 }, 1955 { (gchar *) "UTF-8", 0, 0 }, 1956 { (gchar *) "STRING", 0, 0 }, 1957 { (gchar *) "TEXT", 0, 0 }, 1958 { (gchar *) "COMPOUND_TEXT", 0, 0 } 1959 }; 1960 GtkClipboard *clipboard; 1961 1962 clipboard = gtk_widget_get_clipboard ( 1963 GTK_WIDGET (GNOME_CANVAS_ITEM (text)->canvas), 1964 GDK_SELECTION_PRIMARY); 1965 1966 if (text->selection_start != text->selection_end) { 1967 if (!gtk_clipboard_set_with_owner ( 1968 clipboard, targets, G_N_ELEMENTS (targets), 1969 primary_get_cb, primary_clear_cb, G_OBJECT (text))) 1970 primary_clear_cb (clipboard, text); 1971 } else { 1972 if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (text)) 1973 gtk_clipboard_clear (clipboard); 1974 } 1975 } 1976 1977 static void 1978 paste_received (GtkClipboard *clipboard, 1979 const gchar *text, 1980 gpointer data) 1981 { 1982 EText *etext = E_TEXT (data); 1983 1984 if (text && g_utf8_validate (text, strlen (text), NULL)) { 1985 if (etext->selection_end != etext->selection_start) 1986 e_text_delete_selection (etext); 1987 1988 e_text_insert (etext, text); 1989 } 1990 1991 g_object_unref (etext); 1992 } 1993 1994 static void 1995 e_text_paste (EText *text, 1996 GdkAtom selection) 1997 { 1998 g_object_ref (text); 1999 gtk_clipboard_request_text ( 2000 gtk_widget_get_clipboard ( 2001 GTK_WIDGET (GNOME_CANVAS_ITEM (text)->canvas), 2002 selection), paste_received, text); 2003 } 2004 2005 typedef struct { 2006 EText *text; 2007 GdkEventButton *button; 2008 gint position; 2009 } PopupClosure; 2010 2011 static void 2012 popup_menu_detach (GtkWidget *attach_widget, 2013 GtkMenu *menu) 2014 { 2015 } 2016 2017 static void 2018 popup_menu_placement_cb (GtkMenu *menu, 2019 gint *x, 2020 gint *y, 2021 gboolean *push_in, 2022 gpointer user_data) 2023 { 2024 EText *text = E_TEXT (user_data); 2025 GnomeCanvasItem *item = &text->item; 2026 GnomeCanvas *parent = item->canvas; 2027 2028 if (parent) { 2029 GdkWindow *window; 2030 2031 window = gtk_widget_get_window (GTK_WIDGET (parent)); 2032 gdk_window_get_origin (window, x, y); 2033 *x += item->x1 + text->width / 2; 2034 *y += item->y1 + text->height / 2; 2035 } 2036 2037 return; 2038 } 2039 2040 static void 2041 popup_targets_received (GtkClipboard *clipboard, 2042 GtkSelectionData *data, 2043 gpointer user_data) 2044 { 2045 PopupClosure *closure = user_data; 2046 EText *text = closure->text; 2047 GdkEventButton *button = closure->button; 2048 gint position = closure->position; 2049 GtkWidget *popup_menu = gtk_menu_new (); 2050 GtkWidget *menuitem, *submenu; 2051 2052 g_free (closure); 2053 2054 gtk_menu_attach_to_widget ( 2055 GTK_MENU (popup_menu), 2056 GTK_WIDGET (GNOME_CANVAS_ITEM (text)->canvas), 2057 popup_menu_detach); 2058 2059 /* cut menu item */ 2060 menuitem = gtk_image_menu_item_new_from_stock (GTK_STOCK_CUT, NULL); 2061 gtk_widget_show (menuitem); 2062 gtk_menu_shell_append (GTK_MENU_SHELL (popup_menu), menuitem); 2063 g_signal_connect_swapped ( 2064 menuitem, "activate", 2065 G_CALLBACK (e_text_cut_clipboard), text); 2066 gtk_widget_set_sensitive ( 2067 menuitem, text->editable && 2068 (text->selection_start != text->selection_end)); 2069 2070 /* copy menu item */ 2071 menuitem = gtk_image_menu_item_new_from_stock (GTK_STOCK_COPY, NULL); 2072 gtk_widget_show (menuitem); 2073 gtk_menu_shell_append (GTK_MENU_SHELL (popup_menu), menuitem); 2074 g_signal_connect_swapped ( 2075 menuitem, "activate", 2076 G_CALLBACK (e_text_copy_clipboard), text); 2077 gtk_widget_set_sensitive (menuitem, text->selection_start != text->selection_end); 2078 2079 /* paste menu item */ 2080 menuitem = gtk_image_menu_item_new_from_stock (GTK_STOCK_PASTE, NULL); 2081 gtk_widget_show (menuitem); 2082 gtk_menu_shell_append (GTK_MENU_SHELL (popup_menu), menuitem); 2083 g_signal_connect_swapped ( 2084 menuitem, "activate", 2085 G_CALLBACK (e_text_paste_clipboard), text); 2086 gtk_widget_set_sensitive ( 2087 menuitem, text->editable && 2088 gtk_selection_data_targets_include_text (data)); 2089 2090 menuitem = gtk_menu_item_new_with_label (_("Select All")); 2091 gtk_widget_show (menuitem); 2092 gtk_menu_shell_append (GTK_MENU_SHELL (popup_menu), menuitem); 2093 g_signal_connect_swapped ( 2094 menuitem, "activate", 2095 G_CALLBACK (e_text_select_all), text); 2096 gtk_widget_set_sensitive (menuitem, strlen (text->text) > 0); 2097 2098 menuitem = gtk_separator_menu_item_new (); 2099 gtk_widget_show (menuitem); 2100 gtk_menu_shell_append (GTK_MENU_SHELL (popup_menu), menuitem); 2101 2102 if (text->im_context && GTK_IS_IM_MULTICONTEXT (text->im_context)) { 2103 menuitem = gtk_menu_item_new_with_label (_("Input Methods")); 2104 gtk_widget_show (menuitem); 2105 submenu = gtk_menu_new (); 2106 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), submenu); 2107 2108 gtk_menu_shell_append (GTK_MENU_SHELL (popup_menu), menuitem); 2109 2110 gtk_im_multicontext_append_menuitems ( 2111 GTK_IM_MULTICONTEXT (text->im_context), 2112 GTK_MENU_SHELL (submenu)); 2113 } 2114 2115 g_signal_emit (text, 2116 e_text_signals[E_TEXT_POPULATE_POPUP], 2117 0, 2118 button, position, 2119 popup_menu); 2120 2121 /* If invoked by S-F10 key binding, button will be 0. */ 2122 if (button->button == 0) { 2123 gtk_menu_popup (GTK_MENU (popup_menu), NULL, NULL, 2124 popup_menu_placement_cb, (gpointer) text, 2125 button->button, GDK_CURRENT_TIME); 2126 } else { 2127 gtk_menu_popup (GTK_MENU (popup_menu), NULL, NULL, 2128 NULL, NULL, 2129 button->button, button->time); 2130 } 2131 2132 g_object_unref (text); 2133 gdk_event_free ((GdkEvent *) button); 2134 } 2135 2136 static void 2137 e_text_do_popup (EText *text, 2138 GdkEventButton *button, 2139 gint position) 2140 { 2141 PopupClosure *closure = g_new (PopupClosure, 1); 2142 2143 closure->text = g_object_ref (text); 2144 closure->button = (GdkEventButton *) gdk_event_copy ((GdkEvent *) button); 2145 closure->position = position; 2146 2147 gtk_clipboard_request_contents ( 2148 gtk_widget_get_clipboard ( 2149 GTK_WIDGET (GNOME_CANVAS_ITEM (text)->canvas), 2150 GDK_SELECTION_CLIPBOARD), 2151 gdk_atom_intern ("TARGETS", FALSE), 2152 popup_targets_received, 2153 closure); 2154 } 2155 2156 static void 2157 e_text_reset_im_context (EText *text) 2158 { 2159 if (text->need_im_reset && text->im_context) { 2160 text->need_im_reset = FALSE; 2161 gtk_im_context_reset (text->im_context); 2162 } 2163 } 2164 2165 /* fixme: */ 2166 2167 static gint 2168 next_word (EText *text, 2169 gint start) 2170 { 2171 gchar *p = g_utf8_offset_to_pointer (text->text, start); 2172 gint length; 2173 2174 length = g_utf8_strlen (text->text, -1); 2175 2176 if (start >= length) { 2177 return length; 2178 } else { 2179 p = g_utf8_next_char (p); 2180 start++; 2181 2182 while (p && *p) { 2183 gunichar unival = g_utf8_get_char (p); 2184 if (g_unichar_isspace (unival)) { 2185 return start + 1; 2186 } 2187 else { 2188 p = g_utf8_next_char (p); 2189 start++; 2190 } 2191 } 2192 } 2193 2194 return g_utf8_pointer_to_offset (text->text, p); 2195 } 2196 2197 static gint 2198 find_offset_into_line (EText *text, 2199 gint offset_into_text, 2200 gchar **start_of_line) 2201 { 2202 gchar *p; 2203 2204 p = g_utf8_offset_to_pointer (text->text, offset_into_text); 2205 2206 if (p == text->text) { 2207 if (start_of_line) 2208 *start_of_line = (gchar *)text->text; 2209 return 0; 2210 } 2211 else { 2212 p = g_utf8_find_prev_char (text->text, p); 2213 2214 while (p && p > text->text) { 2215 if (*p == '\n') { 2216 if (start_of_line) 2217 *start_of_line = p+1; 2218 return offset_into_text - 2219 g_utf8_pointer_to_offset ( 2220 text->text, p + 1); 2221 } 2222 p = g_utf8_find_prev_char (text->text, p); 2223 } 2224 2225 if (start_of_line) 2226 *start_of_line = (gchar *)text->text; 2227 return offset_into_text; 2228 } 2229 } 2230 2231 /* direction = TRUE (move forward), FALSE (move backward) 2232 * Any error shall return length (text->text) or 0 or 2233 * text->selection_end (as deemed fit) */ 2234 static gint 2235 _get_updated_position (EText *text, 2236 gboolean direction) 2237 { 2238 PangoLogAttr *log_attrs = NULL; 2239 gint n_attrs; 2240 gchar *p = NULL; 2241 gint new_pos = 0; 2242 gint length = 0; 2243 2244 /* Basic sanity test, return whatever position we are currently at. */ 2245 g_return_val_if_fail (text->layout != NULL, text->selection_end); 2246 2247 length = g_utf8_strlen (text->text, -1); 2248 2249 /* length checks to make sure we are not wandering 2250 * off into nonexistant memory... */ 2251 if ((text->selection_end >= length) && (TRUE == direction)) /* forward */ 2252 return length; 2253 /* checking for -ve value wont hurt! */ 2254 if ((text->selection_end <= 0) && (FALSE == direction)) /* backward */ 2255 return 0; 2256 2257 /* check for validness of full text->text */ 2258 if (!g_utf8_validate (text->text, -1, NULL)) 2259 return text->selection_end; 2260 2261 /* get layout's PangoLogAttr to facilitate moving when 2262 * moving across grapheme cluster as in indic langs */ 2263 pango_layout_get_log_attrs (text->layout, &log_attrs, &n_attrs); 2264 2265 /* Fetch the current gchar index in the line & keep moving 2266 * forward until we can display cursor */ 2267 p = g_utf8_offset_to_pointer (text->text, text->selection_end); 2268 2269 new_pos = text->selection_end; 2270 while (1) 2271 { 2272 /* check before moving forward/backwards 2273 * if we have more chars to move or not */ 2274 if (TRUE == direction) 2275 p = g_utf8_next_char (p); 2276 else 2277 p = g_utf8_prev_char (p); 2278 2279 /* validate the new string & return with original position if check fails */ 2280 if (!g_utf8_validate (p, -1, NULL)) 2281 break; /* will return old value of new_pos */ 2282 2283 new_pos = g_utf8_pointer_to_offset (text->text, p); 2284 2285 /* if is_cursor_position is set, cursor can appear in front of character. 2286 * i.e. this is a grapheme boundary AND make some sanity checks */ 2287 if ((new_pos >=0) && (new_pos < n_attrs) && 2288 (log_attrs[new_pos].is_cursor_position)) 2289 break; 2290 else if ((new_pos < 0) || (new_pos >= n_attrs)) 2291 { 2292 new_pos = text->selection_end; 2293 break; 2294 } 2295 } 2296 2297 if (log_attrs) 2298 g_free (log_attrs); 2299 2300 return new_pos; 2301 } 2302 2303 static gint 2304 _get_position (EText *text, 2305 ETextEventProcessorCommand *command) 2306 { 2307 gint length, obj_num; 2308 gunichar unival; 2309 gchar *p = NULL; 2310 gint new_pos = 0; 2311 2312 switch (command->position) { 2313 2314 case E_TEP_VALUE: 2315 new_pos = command->value; 2316 break; 2317 2318 case E_TEP_SELECTION: 2319 new_pos = text->selection_end; 2320 break; 2321 2322 case E_TEP_START_OF_BUFFER: 2323 new_pos = 0; 2324 break; 2325 2326 case E_TEP_END_OF_BUFFER: 2327 new_pos = strlen (text->text); 2328 break; 2329 2330 case E_TEP_START_OF_LINE: 2331 2332 if (text->selection_end >= 1) { 2333 2334 p = g_utf8_offset_to_pointer (text->text, text->selection_end); 2335 if (p != text->text) { 2336 p = g_utf8_find_prev_char (text->text, p); 2337 while (p && p > text->text) { 2338 if (*p == '\n') { 2339 new_pos = g_utf8_pointer_to_offset (text->text, p) + 1; 2340 break; 2341 } 2342 p = g_utf8_find_prev_char (text->text, p); 2343 } 2344 } 2345 } 2346 2347 break; 2348 2349 case E_TEP_END_OF_LINE: 2350 new_pos = -1; 2351 length = g_utf8_strlen (text->text, -1); 2352 2353 if (text->selection_end >= length) { 2354 new_pos = length; 2355 } else { 2356 2357 p = g_utf8_offset_to_pointer (text->text, text->selection_end); 2358 2359 while (p && *p) { 2360 if (*p == '\n') { 2361 new_pos = g_utf8_pointer_to_offset (text->text, p); 2362 p = NULL; 2363 } else 2364 p = g_utf8_next_char (p); 2365 } 2366 } 2367 2368 if (new_pos == -1) 2369 new_pos = g_utf8_pointer_to_offset (text->text, p); 2370 2371 break; 2372 2373 case E_TEP_FORWARD_CHARACTER: 2374 length = g_utf8_strlen (text->text, -1); 2375 2376 if (text->selection_end >= length) 2377 new_pos = length; 2378 else 2379 /* get updated position to display cursor */ 2380 new_pos = _get_updated_position (text, TRUE); 2381 2382 break; 2383 2384 case E_TEP_BACKWARD_CHARACTER: 2385 new_pos = 0; 2386 if (text->selection_end >= 1) 2387 /* get updated position to display cursor */ 2388 new_pos = _get_updated_position (text, FALSE); 2389 2390 break; 2391 2392 case E_TEP_FORWARD_WORD: 2393 new_pos = next_word (text, text->selection_end); 2394 break; 2395 2396 case E_TEP_BACKWARD_WORD: 2397 new_pos = 0; 2398 if (text->selection_end >= 1) { 2399 gint pos = text->selection_end; 2400 2401 p = g_utf8_find_prev_char ( 2402 text->text, g_utf8_offset_to_pointer ( 2403 text->text, text->selection_end)); 2404 pos--; 2405 2406 if (p != text->text) { 2407 p = g_utf8_find_prev_char (text->text, p); 2408 pos--; 2409 2410 while (p && p > text->text) { 2411 unival = g_utf8_get_char (p); 2412 if (g_unichar_isspace (unival)) { 2413 new_pos = pos + 1; 2414 p = NULL; 2415 } 2416 else { 2417 p = g_utf8_find_prev_char (text->text, p); 2418 pos--; 2419 } 2420 } 2421 } 2422 } 2423 2424 break; 2425 2426 case E_TEP_FORWARD_LINE: { 2427 gint offset_into_line; 2428 2429 offset_into_line = find_offset_into_line (text, text->selection_end, NULL); 2430 if (offset_into_line == -1) 2431 return text->selection_end; 2432 2433 /* now we search forward til we hit a \n, and then 2434 * offset_into_line more characters */ 2435 p = g_utf8_offset_to_pointer (text->text, text->selection_end); 2436 while (p && *p) { 2437 if (*p == '\n') 2438 break; 2439 p = g_utf8_next_char (p); 2440 } 2441 if (p && *p == '\n') { 2442 /* now we loop forward offset_into_line 2443 * characters, or until we hit \n or \0 */ 2444 2445 p = g_utf8_next_char (p); 2446 while (offset_into_line > 0 && p && *p != '\n' && *p != '\0') { 2447 p = g_utf8_next_char (p); 2448 offset_into_line--; 2449 } 2450 } 2451 2452 /* at this point, p points to the new location, 2453 * convert it to an offset and we're done */ 2454 new_pos = g_utf8_pointer_to_offset (text->text, p); 2455 break; 2456 } 2457 case E_TEP_BACKWARD_LINE: { 2458 gint offset_into_line; 2459 2460 offset_into_line = find_offset_into_line ( 2461 text, text->selection_end, &p); 2462 2463 if (offset_into_line == -1) 2464 return text->selection_end; 2465 2466 /* p points to the first character on our line. if we 2467 * have a \n before it, skip it and scan til we hit 2468 * the next one */ 2469 if (p != text->text) { 2470 p = g_utf8_find_prev_char (text->text, p); 2471 if (*p == '\n') { 2472 p = g_utf8_find_prev_char (text->text, p); 2473 while (p > text->text) { 2474 if (*p == '\n') { 2475 p++; 2476 break; 2477 } 2478 p = g_utf8_find_prev_char (text->text, p); 2479 } 2480 } 2481 } 2482 2483 /* at this point 'p' points to the start of the 2484 * previous line, move forward 'offset_into_line' 2485 * times. */ 2486 2487 while (offset_into_line > 0 && p && *p != '\n' && *p != '\0') { 2488 p = g_utf8_next_char (p); 2489 offset_into_line--; 2490 } 2491 2492 /* at this point, p points to the new location, 2493 * convert it to an offset and we're done */ 2494 new_pos = g_utf8_pointer_to_offset (text->text, p); 2495 break; 2496 } 2497 case E_TEP_SELECT_WORD: 2498 /* This is a silly hack to cause double-clicking on an object 2499 * to activate that object. 2500 * (Normally, double click == select word, which is why this is here.) */ 2501 2502 obj_num = e_text_model_get_object_at_offset ( 2503 text->model, text->selection_start); 2504 if (obj_num != -1) { 2505 e_text_model_activate_nth_object (text->model, obj_num); 2506 new_pos = text->selection_start; 2507 break; 2508 } 2509 2510 if (text->selection_end < 1) { 2511 new_pos = 0; 2512 break; 2513 } 2514 2515 p = g_utf8_offset_to_pointer (text->text, text->selection_end); 2516 2517 p = g_utf8_find_prev_char (text->text, p); 2518 2519 while (p && p > text->text) { 2520 unival = g_utf8_get_char (p); 2521 if (g_unichar_isspace (unival)) { 2522 p = g_utf8_next_char (p); 2523 break; 2524 } 2525 p = g_utf8_find_prev_char (text->text, p); 2526 } 2527 2528 if (!p) 2529 text->selection_start = 0; 2530 else 2531 text->selection_start = g_utf8_pointer_to_offset (text->text, p); 2532 2533 text->selection_start = 2534 e_text_model_validate_position ( 2535 text->model, text->selection_start); 2536 2537 length = g_utf8_strlen (text->text, -1); 2538 if (text->selection_end >= length) { 2539 new_pos = length; 2540 break; 2541 } 2542 2543 p = g_utf8_offset_to_pointer (text->text, text->selection_end); 2544 while (p && *p) { 2545 unival = g_utf8_get_char (p); 2546 if (g_unichar_isspace (unival)) { 2547 new_pos = g_utf8_pointer_to_offset (text->text, p); 2548 break; 2549 } else 2550 p = g_utf8_next_char (p); 2551 } 2552 2553 if (!new_pos) 2554 new_pos = g_utf8_strlen (text->text, -1); 2555 2556 return new_pos; 2557 2558 case E_TEP_SELECT_ALL: 2559 text->selection_start = 0; 2560 new_pos = g_utf8_strlen (text->text, -1); 2561 break; 2562 2563 case E_TEP_FORWARD_PARAGRAPH: 2564 case E_TEP_BACKWARD_PARAGRAPH: 2565 2566 case E_TEP_FORWARD_PAGE: 2567 case E_TEP_BACKWARD_PAGE: 2568 new_pos = text->selection_end; 2569 break; 2570 2571 default: 2572 new_pos = text->selection_end; 2573 break; 2574 } 2575 2576 new_pos = e_text_model_validate_position (text->model, new_pos); 2577 2578 return new_pos; 2579 } 2580 2581 static void 2582 e_text_insert (EText *text, 2583 const gchar *string) 2584 { 2585 gint len = strlen (string); 2586 2587 if (len > 0) { 2588 gint utf8len = 0; 2589 2590 if (!text->allow_newlines) { 2591 const gchar *i; 2592 gchar *new_string = g_malloc (len + 1); 2593 gchar *j = new_string; 2594 2595 for (i = string; *i; i = g_utf8_next_char (i)) { 2596 if (*i != '\n') { 2597 gunichar c; 2598 gint charlen; 2599 2600 c = g_utf8_get_char (i); 2601 charlen = g_unichar_to_utf8 (c, j); 2602 j += charlen; 2603 utf8len++; 2604 } 2605 } 2606 *j = 0; 2607 e_text_model_insert_length ( 2608 text->model, text->selection_start, 2609 new_string, utf8len); 2610 g_free (new_string); 2611 } 2612 else { 2613 utf8len = g_utf8_strlen (string, -1); 2614 e_text_model_insert_length ( 2615 text->model, text->selection_start, 2616 string, utf8len); 2617 } 2618 } 2619 } 2620 2621 static void 2622 capitalize (EText *text, 2623 gint start, 2624 gint end, 2625 ETextEventProcessorCaps type) 2626 { 2627 gboolean first = TRUE; 2628 const gchar *p = g_utf8_offset_to_pointer (text->text, start); 2629 const gchar *text_end = g_utf8_offset_to_pointer (text->text, end); 2630 gint utf8len = text_end - p; 2631 2632 if (utf8len > 0) { 2633 gchar *new_text = g_new0 (char, utf8len * 6); 2634 gchar *output = new_text; 2635 2636 while (p && *p && p < text_end) { 2637 gunichar unival = g_utf8_get_char (p); 2638 gunichar newval = unival; 2639 2640 switch (type) { 2641 case E_TEP_CAPS_UPPER: 2642 newval = g_unichar_toupper (unival); 2643 break; 2644 case E_TEP_CAPS_LOWER: 2645 newval = g_unichar_tolower (unival); 2646 break; 2647 case E_TEP_CAPS_TITLE: 2648 if (g_unichar_isalpha (unival)) { 2649 if (first) 2650 newval = g_unichar_totitle (unival); 2651 else 2652 newval = g_unichar_tolower (unival); 2653 first = FALSE; 2654 } else { 2655 first = TRUE; 2656 } 2657 break; 2658 } 2659 g_unichar_to_utf8 (newval, output); 2660 output = g_utf8_next_char (output); 2661 2662 p = g_utf8_next_char (p); 2663 } 2664 *output = 0; 2665 2666 e_text_model_delete (text->model, start, utf8len); 2667 e_text_model_insert_length (text->model, start, new_text, utf8len); 2668 g_free (new_text); 2669 } 2670 } 2671 2672 static void 2673 e_text_command (ETextEventProcessor *tep, 2674 ETextEventProcessorCommand *command, 2675 gpointer data) 2676 { 2677 EText *text = E_TEXT (data); 2678 gboolean scroll = TRUE; 2679 gboolean use_start = TRUE; 2680 2681 switch (command->action) { 2682 case E_TEP_MOVE: 2683 text->selection_start = _get_position (text, command); 2684 text->selection_end = text->selection_start; 2685 if (text->timer) { 2686 g_timer_reset (text->timer); 2687 } 2688 2689 text->need_im_reset = TRUE; 2690 use_start = TRUE; 2691 break; 2692 case E_TEP_SELECT: 2693 text->selection_start = 2694 e_text_model_validate_position ( 2695 text->model, text->selection_start); /* paranoia */ 2696 text->selection_end = _get_position (text, command); 2697 2698 e_text_update_primary_selection (text); 2699 2700 text->need_im_reset = TRUE; 2701 use_start = FALSE; 2702 2703 break; 2704 case E_TEP_DELETE: 2705 if (text->selection_end == text->selection_start) { 2706 text->selection_end = _get_position (text, command); 2707 } 2708 e_text_delete_selection (text); 2709 if (text->timer) { 2710 g_timer_reset (text->timer); 2711 } 2712 2713 text->need_im_reset = TRUE; 2714 use_start = FALSE; 2715 2716 break; 2717 2718 case E_TEP_INSERT: 2719 if (g_utf8_validate (command->string, command->value, NULL)) { 2720 if (text->selection_end != text->selection_start) { 2721 e_text_delete_selection (text); 2722 } 2723 e_text_insert (text, command->string); 2724 if (text->timer) { 2725 g_timer_reset (text->timer); 2726 } 2727 text->need_im_reset = TRUE; 2728 } 2729 break; 2730 case E_TEP_COPY: 2731 e_text_copy_clipboard (text); 2732 2733 if (text->timer) { 2734 g_timer_reset (text->timer); 2735 } 2736 scroll = FALSE; 2737 break; 2738 case E_TEP_PASTE: 2739 e_text_paste (text, GDK_NONE); 2740 if (text->timer) { 2741 g_timer_reset (text->timer); 2742 } 2743 text->need_im_reset = TRUE; 2744 break; 2745 case E_TEP_GET_SELECTION: 2746 e_text_paste (text, GDK_SELECTION_PRIMARY); 2747 break; 2748 case E_TEP_ACTIVATE: 2749 g_signal_emit (text, e_text_signals[E_TEXT_ACTIVATE], 0); 2750 if (text->timer) { 2751 g_timer_reset (text->timer); 2752 } 2753 break; 2754 case E_TEP_SET_SELECT_BY_WORD: 2755 text->select_by_word = command->value; 2756 break; 2757 case E_TEP_GRAB: 2758 e_canvas_item_grab ( 2759 E_CANVAS (GNOME_CANVAS_ITEM (text)->canvas), 2760 GNOME_CANVAS_ITEM (text), 2761 GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK, 2762 text->i_cursor, 2763 command->time, 2764 NULL, 2765 NULL); 2766 scroll = FALSE; 2767 break; 2768 case E_TEP_UNGRAB: 2769 e_canvas_item_ungrab ( 2770 E_CANVAS (GNOME_CANVAS_ITEM (text)->canvas), 2771 GNOME_CANVAS_ITEM (text), 2772 command->time); 2773 scroll = FALSE; 2774 break; 2775 case E_TEP_CAPS: 2776 if (text->selection_start == text->selection_end) { 2777 capitalize ( 2778 text, text->selection_start, 2779 next_word (text, text->selection_start), 2780 command->value); 2781 } else { 2782 gint selection_start = MIN ( 2783 text->selection_start, text->selection_end); 2784 gint selection_end = MAX ( 2785 text->selection_start, text->selection_end); 2786 capitalize ( 2787 text, selection_start, 2788 selection_end, command->value); 2789 } 2790 break; 2791 case E_TEP_NOP: 2792 scroll = FALSE; 2793 break; 2794 } 2795 2796 e_text_reset_im_context (text); 2797 2798 /* it's possible to get here without ever having been realized 2799 * by our canvas (if the e-text started completely obscured.) 2800 * so let's create our layout object if we don't already have 2801 * one. */ 2802 if (!text->layout) 2803 create_layout (text); 2804 2805 /* We move cursor only if scroll is TRUE */ 2806 if (scroll && !text->button_down) { 2807 /* XXX do we really need the @trailing logic here? if 2808 * we don't we can scrap the loop and just use 2809 * pango_layout_index_to_pos */ 2810 PangoLayoutLine *cur_line = NULL; 2811 gint selection_index; 2812 PangoLayoutIter *iter = pango_layout_get_iter (text->layout); 2813 2814 /* check if we are using selection_start or selection_end for moving? */ 2815 selection_index = use_start ? text->selection_start : text->selection_end; 2816 2817 /* convert to a byte index */ 2818 selection_index = g_utf8_offset_to_pointer ( 2819 text->text, selection_index) - text->text; 2820 2821 do { 2822 PangoLayoutLine *line = pango_layout_iter_get_line (iter); 2823 2824 if (selection_index >= line->start_index && 2825 selection_index <= line->start_index + line->length) { 2826 /* found the line with the start of the selection */ 2827 cur_line = line; 2828 break; 2829 } 2830 2831 } while (pango_layout_iter_next_line (iter)); 2832 2833 if (cur_line) { 2834 gint xpos, ypos; 2835 gdouble clip_width, clip_height; 2836 /* gboolean trailing = FALSE; */ 2837 PangoRectangle pango_pos; 2838 2839 if (selection_index > 0 && selection_index == 2840 cur_line->start_index + cur_line->length) { 2841 selection_index--; 2842 /* trailing = TRUE; */ 2843 } 2844 2845 pango_layout_index_to_pos (text->layout, selection_index, &pango_pos); 2846 2847 pango_pos.x = PANGO_PIXELS (pango_pos.x); 2848 pango_pos.y = PANGO_PIXELS (pango_pos.y); 2849 pango_pos.width = (pango_pos.width + PANGO_SCALE / 2) / PANGO_SCALE; 2850 pango_pos.height = (pango_pos.height + PANGO_SCALE / 2) / PANGO_SCALE; 2851 2852 /* scroll for X */ 2853 xpos = pango_pos.x; /* + (trailing ? 0 : pango_pos.width);*/ 2854 2855 if (xpos + 2 < text->xofs_edit) { 2856 text->xofs_edit = xpos; 2857 } 2858 2859 clip_width = text->clip_width; 2860 2861 if (xpos + pango_pos.width - clip_width > text->xofs_edit) { 2862 text->xofs_edit = xpos + pango_pos.width - clip_width; 2863 } 2864 2865 /* scroll for Y */ 2866 if (pango_pos.y + 2 < text->yofs_edit) { 2867 ypos = pango_pos.y; 2868 text->yofs_edit = ypos; 2869 } 2870 else { 2871 ypos = pango_pos.y + pango_pos.height; 2872 } 2873 2874 if (text->clip_height < 0) 2875 clip_height = text->height; 2876 else 2877 clip_height = text->clip_height; 2878 2879 if (ypos - clip_height > text->yofs_edit) { 2880 text->yofs_edit = ypos - clip_height; 2881 } 2882 2883 } 2884 2885 pango_layout_iter_free (iter); 2886 } 2887 2888 text->needs_redraw = 1; 2889 gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (text)); 2890 } 2891 2892 /* Class initialization function for the text item */ 2893 static void 2894 e_text_class_init (ETextClass *class) 2895 { 2896 GObjectClass *gobject_class; 2897 GnomeCanvasItemClass *item_class; 2898 2899 gobject_class = (GObjectClass *) class; 2900 item_class = (GnomeCanvasItemClass *) class; 2901 2902 gobject_class->dispose = e_text_dispose; 2903 gobject_class->set_property = e_text_set_property; 2904 gobject_class->get_property = e_text_get_property; 2905 2906 item_class->update = e_text_update; 2907 item_class->realize = e_text_realize; 2908 item_class->unrealize = e_text_unrealize; 2909 item_class->draw = e_text_draw; 2910 item_class->point = e_text_point; 2911 item_class->bounds = e_text_bounds; 2912 item_class->event = e_text_event; 2913 2914 class->changed = NULL; 2915 class->activate = NULL; 2916 2917 e_text_signals[E_TEXT_CHANGED] = g_signal_new ( 2918 "changed", 2919 G_OBJECT_CLASS_TYPE (gobject_class), 2920 G_SIGNAL_RUN_LAST, 2921 G_STRUCT_OFFSET (ETextClass, changed), 2922 NULL, NULL, 2923 g_cclosure_marshal_VOID__VOID, 2924 G_TYPE_NONE, 0); 2925 2926 e_text_signals[E_TEXT_ACTIVATE] = g_signal_new ( 2927 "activate", 2928 G_OBJECT_CLASS_TYPE (gobject_class), 2929 G_SIGNAL_RUN_LAST, 2930 G_STRUCT_OFFSET (ETextClass, activate), 2931 NULL, NULL, 2932 g_cclosure_marshal_VOID__VOID, 2933 G_TYPE_NONE, 0); 2934 2935 e_text_signals[E_TEXT_KEYPRESS] = g_signal_new ( 2936 "keypress", 2937 G_OBJECT_CLASS_TYPE (gobject_class), 2938 G_SIGNAL_RUN_LAST, 2939 G_STRUCT_OFFSET (ETextClass, keypress), 2940 NULL, NULL, 2941 e_marshal_NONE__INT_INT, 2942 G_TYPE_NONE, 2, 2943 G_TYPE_UINT, 2944 G_TYPE_UINT); 2945 2946 e_text_signals[E_TEXT_POPULATE_POPUP] = g_signal_new ( 2947 "populate_popup", 2948 G_OBJECT_CLASS_TYPE (gobject_class), 2949 G_SIGNAL_RUN_LAST, 2950 G_STRUCT_OFFSET (ETextClass, populate_popup), 2951 NULL, NULL, 2952 e_marshal_NONE__POINTER_INT_OBJECT, 2953 G_TYPE_NONE, 3, 2954 G_TYPE_POINTER, 2955 G_TYPE_INT, 2956 GTK_TYPE_MENU); 2957 2958 g_object_class_install_property ( 2959 gobject_class, 2960 PROP_MODEL, 2961 g_param_spec_object ( 2962 "model", 2963 "Model", 2964 "Model", 2965 E_TYPE_TEXT_MODEL, 2966 G_PARAM_READWRITE)); 2967 2968 g_object_class_install_property ( 2969 gobject_class, 2970 PROP_EVENT_PROCESSOR, 2971 g_param_spec_object ( 2972 "event_processor", 2973 "Event Processor", 2974 "Event Processor", 2975 E_TEXT_EVENT_PROCESSOR_TYPE, 2976 G_PARAM_READWRITE)); 2977 2978 g_object_class_install_property ( 2979 gobject_class, 2980 PROP_TEXT, 2981 g_param_spec_string ( 2982 "text", 2983 "Text", 2984 "Text", 2985 NULL, 2986 G_PARAM_READWRITE)); 2987 2988 g_object_class_install_property ( 2989 gobject_class, 2990 PROP_BOLD, 2991 g_param_spec_boolean ( 2992 "bold", 2993 "Bold", 2994 "Bold", 2995 FALSE, 2996 G_PARAM_READWRITE)); 2997 2998 g_object_class_install_property ( 2999 gobject_class, 3000 PROP_STRIKEOUT, 3001 g_param_spec_boolean ( 3002 "strikeout", 3003 "Strikeout", 3004 "Strikeout", 3005 FALSE, 3006 G_PARAM_READWRITE)); 3007 3008 g_object_class_install_property ( 3009 gobject_class, 3010 PROP_JUSTIFICATION, 3011 g_param_spec_enum ( 3012 "justification", 3013 "Justification", 3014 "Justification", 3015 GTK_TYPE_JUSTIFICATION, 3016 GTK_JUSTIFY_LEFT, 3017 G_PARAM_READWRITE)); 3018 3019 g_object_class_install_property ( 3020 gobject_class, 3021 PROP_CLIP_WIDTH, 3022 g_param_spec_double ( 3023 "clip_width", 3024 "Clip Width", 3025 "Clip Width", 3026 0.0, G_MAXDOUBLE, 0.0, 3027 G_PARAM_READWRITE)); 3028 3029 g_object_class_install_property ( 3030 gobject_class, 3031 PROP_CLIP_HEIGHT, 3032 g_param_spec_double ( 3033 "clip_height", 3034 "Clip Height", 3035 "Clip Height", 3036 0.0, G_MAXDOUBLE, 0.0, 3037 G_PARAM_READWRITE)); 3038 3039 g_object_class_install_property ( 3040 gobject_class, 3041 PROP_CLIP, 3042 g_param_spec_boolean ( 3043 "clip", 3044 "Clip", 3045 "Clip", 3046 FALSE, 3047 G_PARAM_READWRITE)); 3048 3049 g_object_class_install_property ( 3050 gobject_class, 3051 PROP_FILL_CLIP_RECTANGLE, 3052 g_param_spec_boolean ( 3053 "fill_clip_rectangle", 3054 "Fill clip rectangle", 3055 "Fill clip rectangle", 3056 FALSE, 3057 G_PARAM_READWRITE)); 3058 3059 g_object_class_install_property ( 3060 gobject_class, 3061 PROP_X_OFFSET, 3062 g_param_spec_double ( 3063 "x_offset", 3064 "X Offset", 3065 "X Offset", 3066 0.0, G_MAXDOUBLE, 0.0, 3067 G_PARAM_READWRITE)); 3068 3069 g_object_class_install_property ( 3070 gobject_class, 3071 PROP_Y_OFFSET, 3072 g_param_spec_double ( 3073 "y_offset", 3074 "Y Offset", 3075 "Y Offset", 3076 0.0, G_MAXDOUBLE, 0.0, 3077 G_PARAM_READWRITE)); 3078 3079 g_object_class_install_property ( 3080 gobject_class, 3081 PROP_FILL_COLOR, 3082 g_param_spec_string ( 3083 "fill_color", 3084 "Fill color", 3085 "Fill color", 3086 NULL, 3087 G_PARAM_WRITABLE)); 3088 3089 g_object_class_install_property ( 3090 gobject_class, 3091 PROP_FILL_COLOR_GDK, 3092 g_param_spec_boxed ( 3093 "fill_color_gdk", 3094 "GDK fill color", 3095 "GDK fill color", 3096 GDK_TYPE_COLOR, 3097 G_PARAM_WRITABLE)); 3098 3099 g_object_class_install_property ( 3100 gobject_class, 3101 PROP_FILL_COLOR_RGBA, 3102 g_param_spec_uint ( 3103 "fill_color_rgba", 3104 "GDK fill color", 3105 "GDK fill color", 3106 0, G_MAXUINT, 0, 3107 G_PARAM_READWRITE)); 3108 3109 g_object_class_install_property ( 3110 gobject_class, 3111 PROP_TEXT_WIDTH, 3112 g_param_spec_double ( 3113 "text_width", 3114 "Text width", 3115 "Text width", 3116 0.0, G_MAXDOUBLE, 0.0, 3117 G_PARAM_READABLE)); 3118 3119 g_object_class_install_property ( 3120 gobject_class, 3121 PROP_TEXT_HEIGHT, 3122 g_param_spec_double ( 3123 "text_height", 3124 "Text height", 3125 "Text height", 3126 0.0, G_MAXDOUBLE, 0.0, 3127 G_PARAM_READABLE)); 3128 3129 g_object_class_install_property ( 3130 gobject_class, 3131 PROP_EDITABLE, 3132 g_param_spec_boolean ( 3133 "editable", 3134 "Editable", 3135 "Editable", 3136 FALSE, 3137 G_PARAM_READWRITE)); 3138 3139 g_object_class_install_property ( 3140 gobject_class, 3141 PROP_USE_ELLIPSIS, 3142 g_param_spec_boolean ( 3143 "use_ellipsis", 3144 "Use ellipsis", 3145 "Use ellipsis", 3146 FALSE, 3147 G_PARAM_READWRITE)); 3148 3149 g_object_class_install_property ( 3150 gobject_class, 3151 PROP_ELLIPSIS, 3152 g_param_spec_string ( 3153 "ellipsis", 3154 "Ellipsis", 3155 "Ellipsis", 3156 NULL, 3157 G_PARAM_READWRITE)); 3158 3159 g_object_class_install_property ( 3160 gobject_class, 3161 PROP_LINE_WRAP, 3162 g_param_spec_boolean ( 3163 "line_wrap", 3164 "Line wrap", 3165 "Line wrap", 3166 FALSE, 3167 G_PARAM_READWRITE)); 3168 3169 g_object_class_install_property ( 3170 gobject_class, 3171 PROP_BREAK_CHARACTERS, 3172 g_param_spec_string ( 3173 "break_characters", 3174 "Break characters", 3175 "Break characters", 3176 NULL, 3177 G_PARAM_READWRITE)); 3178 3179 g_object_class_install_property ( 3180 gobject_class, PROP_MAX_LINES, 3181 g_param_spec_int ( 3182 "max_lines", 3183 "Max lines", 3184 "Max lines", 3185 0, G_MAXINT, 0, 3186 G_PARAM_READWRITE)); 3187 3188 g_object_class_install_property ( 3189 gobject_class, 3190 PROP_WIDTH, 3191 g_param_spec_double ( 3192 "width", 3193 "Width", 3194 "Width", 3195 0.0, G_MAXDOUBLE, 0.0, 3196 G_PARAM_READWRITE)); 3197 3198 g_object_class_install_property ( 3199 gobject_class, 3200 PROP_HEIGHT, 3201 g_param_spec_double ( 3202 "height", 3203 "Height", 3204 "Height", 3205 0.0, G_MAXDOUBLE, 0.0, 3206 G_PARAM_READWRITE)); 3207 3208 g_object_class_install_property ( 3209 gobject_class, 3210 PROP_ALLOW_NEWLINES, 3211 g_param_spec_boolean ( 3212 "allow_newlines", 3213 "Allow newlines", 3214 "Allow newlines", 3215 FALSE, 3216 G_PARAM_READWRITE)); 3217 3218 g_object_class_install_property ( 3219 gobject_class, 3220 PROP_CURSOR_POS, 3221 g_param_spec_int ( 3222 "cursor_pos", 3223 "Cursor position", 3224 "Cursor position", 3225 0, G_MAXINT, 0, 3226 G_PARAM_READWRITE)); 3227 3228 g_object_class_install_property ( 3229 gobject_class, 3230 PROP_IM_CONTEXT, 3231 g_param_spec_object ( 3232 "im_context", 3233 "IM Context", 3234 "IM Context", 3235 GTK_TYPE_IM_CONTEXT, 3236 G_PARAM_READWRITE)); 3237 3238 g_object_class_install_property ( 3239 gobject_class, 3240 PROP_HANDLE_POPUP, 3241 g_param_spec_boolean ( 3242 "handle_popup", 3243 "Handle Popup", 3244 "Handle Popup", 3245 FALSE, 3246 G_PARAM_READWRITE)); 3247 3248 if (!clipboard_atom) 3249 clipboard_atom = gdk_atom_intern ("CLIPBOARD", FALSE); 3250 3251 gal_a11y_e_text_init (); 3252 } 3253 3254 /* Object initialization function for the text item */ 3255 static void 3256 e_text_init (EText *text) 3257 { 3258 text->model = e_text_model_new (); 3259 text->text = e_text_model_get_text (text->model); 3260 text->preedit_len = 0; 3261 text->preedit_pos = 0; 3262 text->layout = NULL; 3263 3264 text->revert = NULL; 3265 3266 text->model_changed_signal_id = g_signal_connect ( 3267 text->model, "changed", 3268 G_CALLBACK (e_text_text_model_changed), text); 3269 3270 text->model_repos_signal_id = g_signal_connect ( 3271 text->model, "reposition", 3272 G_CALLBACK (e_text_text_model_reposition), text); 3273 3274 text->justification = GTK_JUSTIFY_LEFT; 3275 text->clip_width = -1.0; 3276 text->clip_height = -1.0; 3277 text->xofs = 0.0; 3278 text->yofs = 0.0; 3279 3280 text->ellipsis = NULL; 3281 text->use_ellipsis = FALSE; 3282 text->ellipsis_width = 0; 3283 3284 text->editable = FALSE; 3285 text->editing = FALSE; 3286 text->xofs_edit = 0; 3287 text->yofs_edit = 0; 3288 3289 text->selection_start = 0; 3290 text->selection_end = 0; 3291 text->select_by_word = FALSE; 3292 3293 text->timeout_id = 0; 3294 text->timer = NULL; 3295 3296 text->lastx = 0; 3297 text->lasty = 0; 3298 text->last_state = 0; 3299 3300 text->scroll_start = 0; 3301 text->show_cursor = TRUE; 3302 text->button_down = FALSE; 3303 3304 text->tep = NULL; 3305 text->tep_command_id = 0; 3306 3307 text->pointer_in = FALSE; 3308 text->default_cursor_shown = TRUE; 3309 text->line_wrap = FALSE; 3310 text->break_characters = NULL; 3311 text->max_lines = -1; 3312 text->dbl_timeout = 0; 3313 text->tpl_timeout = 0; 3314 3315 text->bold = FALSE; 3316 text->strikeout = FALSE; 3317 3318 text->allow_newlines = TRUE; 3319 3320 text->last_type_request = -1; 3321 text->last_time_request = 0; 3322 text->queued_requests = NULL; 3323 3324 text->im_context = NULL; 3325 text->need_im_reset = FALSE; 3326 text->im_context_signals_registered = FALSE; 3327 3328 text->handle_popup = FALSE; 3329 text->rgba_set = FALSE; 3330 3331 e_canvas_item_set_reflow_callback (GNOME_CANVAS_ITEM (text), e_text_reflow); 3332 } 3333 3334 /* IM Context Callbacks */ 3335 static void 3336 e_text_commit_cb (GtkIMContext *context, 3337 const gchar *str, 3338 EText *text) 3339 { 3340 if (g_utf8_validate (str, strlen (str), NULL)) { 3341 if (text->selection_end != text->selection_start) 3342 e_text_delete_selection (text); 3343 e_text_insert (text, str); 3344 g_signal_emit (text, e_text_signals[E_TEXT_KEYPRESS], 0, 0, 0); 3345 } 3346 } 3347 3348 static void 3349 e_text_preedit_changed_cb (GtkIMContext *context, 3350 EText *etext) 3351 { 3352 gchar *preedit_string = NULL; 3353 gint cursor_pos; 3354 3355 gtk_im_context_get_preedit_string ( 3356 context, &preedit_string, 3357 NULL, &cursor_pos); 3358 3359 cursor_pos = CLAMP (cursor_pos, 0, g_utf8_strlen (preedit_string, -1)); 3360 etext->preedit_len = strlen (preedit_string); 3361 etext->preedit_pos = g_utf8_offset_to_pointer ( 3362 preedit_string, cursor_pos) - preedit_string; 3363 g_free (preedit_string); 3364 3365 g_signal_emit (etext, e_text_signals[E_TEXT_KEYPRESS], 0, 0, 0); 3366 } 3367 3368 static gboolean 3369 e_text_retrieve_surrounding_cb (GtkIMContext *context, 3370 EText *text) 3371 { 3372 gtk_im_context_set_surrounding ( 3373 context, text->text, strlen (text->text), 3374 g_utf8_offset_to_pointer (text->text, MIN ( 3375 text->selection_start, text->selection_end)) - text->text); 3376 3377 return TRUE; 3378 } 3379 3380 static gboolean 3381 e_text_delete_surrounding_cb (GtkIMContext *context, 3382 gint offset, 3383 gint n_chars, 3384 EText *text) 3385 { 3386 e_text_model_delete ( 3387 text->model, 3388 MIN (text->selection_start, text->selection_end) + offset, 3389 n_chars); 3390 3391 return TRUE; 3392 }