evolution-3.6.4/widgets/misc/e-web-view-gtkhtml.c

No issues found

   1 /*
   2  * e-web-view-gtkhtml.c
   3  *
   4  * This program is free software; you can redistribute it and/or
   5  * modify it under the terms of the GNU Lesser General Public
   6  * License as published by the Free Software Foundation; either
   7  * version 2 of the License, or (at your option) version 3.
   8  *
   9  * This program is distributed in the hope that it will be useful,
  10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  12  * Lesser General Public License for more details.
  13  *
  14  * You should have received a copy of the GNU Lesser General Public
  15  * License along with the program; if not, see <http://www.gnu.org/licenses/>
  16  *
  17  */
  18 
  19 #ifdef HAVE_CONFIG_H
  20 #include <config.h>
  21 #endif
  22 
  23 #include "e-web-view-gtkhtml.h"
  24 
  25 #include <string.h>
  26 #include <glib/gi18n-lib.h>
  27 
  28 #include <camel/camel.h>
  29 #include <libebackend/libebackend.h>
  30 
  31 #include <e-util/e-util.h>
  32 #include <e-util/e-plugin-ui.h>
  33 #include <libevolution-utils/e-alert-dialog.h>
  34 #include <libevolution-utils/e-alert-sink.h>
  35 
  36 #include "e-popup-action.h"
  37 #include "e-selectable.h"
  38 
  39 #define E_WEB_VIEW_GTKHTML_GET_PRIVATE(obj) \
  40 	(G_TYPE_INSTANCE_GET_PRIVATE \
  41 	((obj), E_TYPE_WEB_VIEW_GTKHTML, EWebViewGtkHTMLPrivate))
  42 
  43 typedef struct _EWebViewGtkHTMLRequest EWebViewGtkHTMLRequest;
  44 
  45 struct _EWebViewGtkHTMLPrivate {
  46 	GList *requests;
  47 	GtkUIManager *ui_manager;
  48 	gchar *selected_uri;
  49 	GdkPixbufAnimation *cursor_image;
  50 
  51 	GtkAction *open_proxy;
  52 	GtkAction *print_proxy;
  53 	GtkAction *save_as_proxy;
  54 
  55 	GtkTargetList *copy_target_list;
  56 	GtkTargetList *paste_target_list;
  57 
  58 	/* Lockdown Options */
  59 	guint disable_printing     : 1;
  60 	guint disable_save_to_disk : 1;
  61 };
  62 
  63 struct _EWebViewGtkHTMLRequest {
  64 	GFile *file;
  65 	EWebViewGtkHTML *web_view;
  66 	GCancellable *cancellable;
  67 	GInputStream *input_stream;
  68 	GtkHTMLStream *output_stream;
  69 	gchar buffer[4096];
  70 };
  71 
  72 enum {
  73 	PROP_0,
  74 	PROP_ANIMATE,
  75 	PROP_CARET_MODE,
  76 	PROP_COPY_TARGET_LIST,
  77 	PROP_DISABLE_PRINTING,
  78 	PROP_DISABLE_SAVE_TO_DISK,
  79 	PROP_EDITABLE,
  80 	PROP_INLINE_SPELLING,
  81 	PROP_MAGIC_LINKS,
  82 	PROP_MAGIC_SMILEYS,
  83 	PROP_OPEN_PROXY,
  84 	PROP_PASTE_TARGET_LIST,
  85 	PROP_PRINT_PROXY,
  86 	PROP_SAVE_AS_PROXY,
  87 	PROP_SELECTED_URI,
  88 	PROP_CURSOR_IMAGE
  89 };
  90 
  91 enum {
  92 	COPY_CLIPBOARD,
  93 	CUT_CLIPBOARD,
  94 	PASTE_CLIPBOARD,
  95 	POPUP_EVENT,
  96 	STATUS_MESSAGE,
  97 	STOP_LOADING,
  98 	UPDATE_ACTIONS,
  99 	PROCESS_MAILTO,
 100 	LAST_SIGNAL
 101 };
 102 
 103 static guint signals[LAST_SIGNAL];
 104 
 105 static const gchar *ui =
 106 "<ui>"
 107 "  <popup name='context'>"
 108 "    <menuitem action='copy-clipboard'/>"
 109 "    <separator/>"
 110 "    <placeholder name='custom-actions-1'>"
 111 "      <menuitem action='open'/>"
 112 "      <menuitem action='save-as'/>"
 113 "      <menuitem action='http-open'/>"
 114 "      <menuitem action='send-message'/>"
 115 "      <menuitem action='print'/>"
 116 "    </placeholder>"
 117 "    <placeholder name='custom-actions-2'>"
 118 "      <menuitem action='uri-copy'/>"
 119 "      <menuitem action='mailto-copy'/>"
 120 "      <menuitem action='image-copy'/>"
 121 "    </placeholder>"
 122 "    <placeholder name='custom-actions-3'/>"
 123 "    <separator/>"
 124 "    <menuitem action='select-all'/>"
 125 "  </popup>"
 126 "</ui>";
 127 
 128 /* Forward Declarations */
 129 static void e_web_view_gtkhtml_alert_sink_init (EAlertSinkInterface *interface);
 130 static void e_web_view_gtkhtml_selectable_init (ESelectableInterface *interface);
 131 
 132 G_DEFINE_TYPE_WITH_CODE (
 133 	EWebViewGtkHTML,
 134 	e_web_view_gtkhtml,
 135 	GTK_TYPE_HTML,
 136 	G_IMPLEMENT_INTERFACE (
 137 		E_TYPE_EXTENSIBLE, NULL)
 138 	G_IMPLEMENT_INTERFACE (
 139 		E_TYPE_ALERT_SINK,
 140 		e_web_view_gtkhtml_alert_sink_init)
 141 	G_IMPLEMENT_INTERFACE (
 142 		E_TYPE_SELECTABLE,
 143 		e_web_view_gtkhtml_selectable_init))
 144 
 145 static EWebViewGtkHTMLRequest *
 146 web_view_gtkhtml_request_new (EWebViewGtkHTML *web_view,
 147                       const gchar *uri,
 148                       GtkHTMLStream *stream)
 149 {
 150 	EWebViewGtkHTMLRequest *request;
 151 	GList *list;
 152 
 153 	request = g_slice_new (EWebViewGtkHTMLRequest);
 154 
 155 	/* Try to detect file paths posing as URIs. */
 156 	if (*uri == '/')
 157 		request->file = g_file_new_for_path (uri);
 158 	else
 159 		request->file = g_file_new_for_uri (uri);
 160 
 161 	request->web_view = g_object_ref (web_view);
 162 	request->cancellable = g_cancellable_new ();
 163 	request->input_stream = NULL;
 164 	request->output_stream = stream;
 165 
 166 	list = request->web_view->priv->requests;
 167 	list = g_list_prepend (list, request);
 168 	request->web_view->priv->requests = list;
 169 
 170 	return request;
 171 }
 172 
 173 static void
 174 web_view_gtkhtml_request_free (EWebViewGtkHTMLRequest *request)
 175 {
 176 	GList *list;
 177 
 178 	list = request->web_view->priv->requests;
 179 	list = g_list_remove (list, request);
 180 	request->web_view->priv->requests = list;
 181 
 182 	g_object_unref (request->file);
 183 	g_object_unref (request->web_view);
 184 	g_object_unref (request->cancellable);
 185 
 186 	if (request->input_stream != NULL)
 187 		g_object_unref (request->input_stream);
 188 
 189 	g_slice_free (EWebViewGtkHTMLRequest, request);
 190 }
 191 
 192 static void
 193 web_view_gtkhtml_request_cancel (EWebViewGtkHTMLRequest *request)
 194 {
 195 	g_cancellable_cancel (request->cancellable);
 196 }
 197 
 198 static gboolean
 199 web_view_gtkhtml_request_check_for_error (EWebViewGtkHTMLRequest *request,
 200                                   GError *error)
 201 {
 202 	GtkHTML *html;
 203 	GtkHTMLStream *stream;
 204 
 205 	if (error == NULL)
 206 		return FALSE;
 207 
 208 	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED)) {
 209 		/* use this error, but do not close the stream */
 210 		g_error_free (error);
 211 		return TRUE;
 212 	}
 213 
 214 	/* XXX Should we log errors that are not cancellations? */
 215 
 216 	html = GTK_HTML (request->web_view);
 217 	stream = request->output_stream;
 218 
 219 	gtk_html_end (html, stream, GTK_HTML_STREAM_ERROR);
 220 	web_view_gtkhtml_request_free (request);
 221 	g_error_free (error);
 222 
 223 	return TRUE;
 224 }
 225 
 226 static void
 227 web_view_gtkhtml_request_stream_read_cb (GInputStream *input_stream,
 228                                  GAsyncResult *result,
 229                                  EWebViewGtkHTMLRequest *request)
 230 {
 231 	gssize bytes_read;
 232 	GError *error = NULL;
 233 
 234 	bytes_read = g_input_stream_read_finish (input_stream, result, &error);
 235 
 236 	if (web_view_gtkhtml_request_check_for_error (request, error))
 237 		return;
 238 
 239 	if (bytes_read == 0) {
 240 		gtk_html_end (
 241 			GTK_HTML (request->web_view),
 242 			request->output_stream, GTK_HTML_STREAM_OK);
 243 		web_view_gtkhtml_request_free (request);
 244 		return;
 245 	}
 246 
 247 	gtk_html_write (
 248 		GTK_HTML (request->web_view),
 249 		request->output_stream, request->buffer, bytes_read);
 250 
 251 	g_input_stream_read_async (
 252 		request->input_stream, request->buffer,
 253 		sizeof (request->buffer), G_PRIORITY_DEFAULT,
 254 		request->cancellable, (GAsyncReadyCallback)
 255 		web_view_gtkhtml_request_stream_read_cb, request);
 256 }
 257 
 258 static void
 259 web_view_gtkhtml_request_read_cb (GFile *file,
 260                           GAsyncResult *result,
 261                           EWebViewGtkHTMLRequest *request)
 262 {
 263 	GFileInputStream *input_stream;
 264 	GError *error = NULL;
 265 
 266 	/* Input stream might be NULL, so don't use cast macro. */
 267 	input_stream = g_file_read_finish (file, result, &error);
 268 	request->input_stream = (GInputStream *) input_stream;
 269 
 270 	if (web_view_gtkhtml_request_check_for_error (request, error))
 271 		return;
 272 
 273 	g_input_stream_read_async (
 274 		request->input_stream, request->buffer,
 275 		sizeof (request->buffer), G_PRIORITY_DEFAULT,
 276 		request->cancellable, (GAsyncReadyCallback)
 277 		web_view_gtkhtml_request_stream_read_cb, request);
 278 }
 279 
 280 static void
 281 action_copy_clipboard_cb (GtkAction *action,
 282                           EWebViewGtkHTML *web_view)
 283 {
 284 	e_web_view_gtkhtml_copy_clipboard (web_view);
 285 }
 286 
 287 static void
 288 action_http_open_cb (GtkAction *action,
 289                      EWebViewGtkHTML *web_view)
 290 {
 291 	const gchar *uri;
 292 	gpointer parent;
 293 
 294 	parent = gtk_widget_get_toplevel (GTK_WIDGET (web_view));
 295 	parent = gtk_widget_is_toplevel (parent) ? parent : NULL;
 296 
 297 	uri = e_web_view_gtkhtml_get_selected_uri (web_view);
 298 	g_return_if_fail (uri != NULL);
 299 
 300 	e_show_uri (parent, uri);
 301 }
 302 
 303 static void
 304 action_mailto_copy_cb (GtkAction *action,
 305                        EWebViewGtkHTML *web_view)
 306 {
 307 	CamelURL *curl;
 308 	CamelInternetAddress *inet_addr;
 309 	GtkClipboard *clipboard;
 310 	const gchar *uri;
 311 	gchar *text;
 312 
 313 	uri = e_web_view_gtkhtml_get_selected_uri (web_view);
 314 	g_return_if_fail (uri != NULL);
 315 
 316 	/* This should work because we checked it in update_actions(). */
 317 	curl = camel_url_new (uri, NULL);
 318 	g_return_if_fail (curl != NULL);
 319 
 320 	inet_addr = camel_internet_address_new ();
 321 	camel_address_decode (CAMEL_ADDRESS (inet_addr), curl->path);
 322 	text = camel_address_format (CAMEL_ADDRESS (inet_addr));
 323 	if (text == NULL || *text == '\0')
 324 		text = g_strdup (uri + strlen ("mailto:"));
 325 
 326 	g_object_unref (inet_addr);
 327 	camel_url_free (curl);
 328 
 329 	clipboard = gtk_clipboard_get (GDK_SELECTION_PRIMARY);
 330 	gtk_clipboard_set_text (clipboard, text, -1);
 331 	gtk_clipboard_store (clipboard);
 332 
 333 	clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
 334 	gtk_clipboard_set_text (clipboard, text, -1);
 335 	gtk_clipboard_store (clipboard);
 336 
 337 	g_free (text);
 338 }
 339 
 340 static void
 341 action_select_all_cb (GtkAction *action,
 342                       EWebViewGtkHTML *web_view)
 343 {
 344 	e_web_view_gtkhtml_select_all (web_view);
 345 }
 346 
 347 static void
 348 action_send_message_cb (GtkAction *action,
 349                         EWebViewGtkHTML *web_view)
 350 {
 351 	const gchar *uri;
 352 	gpointer parent;
 353 	gboolean handled;
 354 
 355 	parent = gtk_widget_get_toplevel (GTK_WIDGET (web_view));
 356 	parent = gtk_widget_is_toplevel (parent) ? parent : NULL;
 357 
 358 	uri = e_web_view_gtkhtml_get_selected_uri (web_view);
 359 	g_return_if_fail (uri != NULL);
 360 
 361 	handled = FALSE;
 362 	g_signal_emit (web_view, signals[PROCESS_MAILTO], 0, uri, &handled);
 363 
 364 	if (!handled)
 365 		e_show_uri (parent, uri);
 366 }
 367 
 368 static void
 369 action_uri_copy_cb (GtkAction *action,
 370                     EWebViewGtkHTML *web_view)
 371 {
 372 	GtkClipboard *clipboard;
 373 	const gchar *uri;
 374 
 375 	clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
 376 	uri = e_web_view_gtkhtml_get_selected_uri (web_view);
 377 	g_return_if_fail (uri != NULL);
 378 
 379 	gtk_clipboard_set_text (clipboard, uri, -1);
 380 	gtk_clipboard_store (clipboard);
 381 }
 382 
 383 static void
 384 action_image_copy_cb (GtkAction *action,
 385                     EWebViewGtkHTML *web_view)
 386 {
 387 	GtkClipboard *clipboard;
 388 	GdkPixbufAnimation *animation;
 389 	GdkPixbuf *pixbuf;
 390 
 391 	clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
 392 	animation = e_web_view_gtkhtml_get_cursor_image (web_view);
 393 	g_return_if_fail (animation != NULL);
 394 
 395 	pixbuf = gdk_pixbuf_animation_get_static_image (animation);
 396 	if (!pixbuf)
 397 		return;
 398 
 399 	gtk_clipboard_set_image (clipboard, pixbuf);
 400 	gtk_clipboard_store (clipboard);
 401 }
 402 
 403 static GtkActionEntry uri_entries[] = {
 404 
 405 	{ "uri-copy",
 406 	  GTK_STOCK_COPY,
 407 	  N_("_Copy Link Location"),
 408 	  NULL,
 409 	  N_("Copy the link to the clipboard"),
 410 	  G_CALLBACK (action_uri_copy_cb) }
 411 };
 412 
 413 static GtkActionEntry http_entries[] = {
 414 
 415 	{ "http-open",
 416 	  "emblem-web",
 417 	  N_("_Open Link in Browser"),
 418 	  NULL,
 419 	  N_("Open the link in a web browser"),
 420 	  G_CALLBACK (action_http_open_cb) }
 421 };
 422 
 423 static GtkActionEntry mailto_entries[] = {
 424 
 425 	{ "mailto-copy",
 426 	  GTK_STOCK_COPY,
 427 	  N_("_Copy Email Address"),
 428 	  NULL,
 429 	  N_("Copy the email address to the clipboard"),
 430 	  G_CALLBACK (action_mailto_copy_cb) },
 431 
 432 	{ "send-message",
 433 	  "mail-message-new",
 434 	  N_("_Send New Message To..."),
 435 	  NULL,
 436 	  N_("Send a mail message to this address"),
 437 	  G_CALLBACK (action_send_message_cb) }
 438 };
 439 
 440 static GtkActionEntry image_entries[] = {
 441 
 442 	{ "image-copy",
 443 	  GTK_STOCK_COPY,
 444 	  N_("_Copy Image"),
 445 	  NULL,
 446 	  N_("Copy the image to the clipboard"),
 447 	  G_CALLBACK (action_image_copy_cb) }
 448 };
 449 
 450 static GtkActionEntry selection_entries[] = {
 451 
 452 	{ "copy-clipboard",
 453 	  GTK_STOCK_COPY,
 454 	  NULL,
 455 	  NULL,
 456 	  N_("Copy the selection"),
 457 	  G_CALLBACK (action_copy_clipboard_cb) },
 458 };
 459 
 460 static GtkActionEntry standard_entries[] = {
 461 
 462 	{ "select-all",
 463 	  GTK_STOCK_SELECT_ALL,
 464 	  NULL,
 465 	  NULL,
 466 	  N_("Select all text and images"),
 467 	  G_CALLBACK (action_select_all_cb) }
 468 };
 469 
 470 static gboolean
 471 web_view_gtkhtml_button_press_event_cb (EWebViewGtkHTML *web_view,
 472                                 GdkEventButton *event,
 473                                 GtkHTML *frame)
 474 {
 475 	gboolean event_handled = FALSE;
 476 	gchar *uri = NULL;
 477 
 478 	if (event) {
 479 		GdkPixbufAnimation *anim;
 480 
 481 		if (frame == NULL)
 482 			frame = GTK_HTML (web_view);
 483 
 484 		anim = gtk_html_get_image_at (frame, event->x, event->y);
 485 		e_web_view_gtkhtml_set_cursor_image (web_view, anim);
 486 		if (anim != NULL)
 487 			g_object_unref (anim);
 488 	}
 489 
 490 	if (event != NULL && event->button != 3)
 491 		return FALSE;
 492 
 493 	/* Only extract a URI if no selection is active.  Selected text
 494 	 * implies the user is more likely to want to copy the selection
 495 	 * to the clipboard than open a link within the selection. */
 496 	if (!e_web_view_gtkhtml_is_selection_active (web_view))
 497 		uri = e_web_view_gtkhtml_extract_uri (web_view, event, frame);
 498 
 499 	if (uri != NULL && g_str_has_prefix (uri, "##")) {
 500 		g_free (uri);
 501 		return FALSE;
 502 	}
 503 
 504 	g_signal_emit (
 505 		web_view, signals[POPUP_EVENT], 0,
 506 		event, uri, &event_handled);
 507 
 508 	g_free (uri);
 509 
 510 	return event_handled;
 511 }
 512 
 513 static void
 514 web_view_gtkhtml_menu_item_select_cb (EWebViewGtkHTML *web_view,
 515                               GtkWidget *widget)
 516 {
 517 	GtkAction *action;
 518 	GtkActivatable *activatable;
 519 	const gchar *tooltip;
 520 
 521 	activatable = GTK_ACTIVATABLE (widget);
 522 	action = gtk_activatable_get_related_action (activatable);
 523 	tooltip = gtk_action_get_tooltip (action);
 524 
 525 	if (tooltip == NULL)
 526 		return;
 527 
 528 	e_web_view_gtkhtml_status_message (web_view, tooltip);
 529 }
 530 
 531 static void
 532 web_view_gtkhtml_menu_item_deselect_cb (EWebViewGtkHTML *web_view)
 533 {
 534 	e_web_view_gtkhtml_status_message (web_view, NULL);
 535 }
 536 
 537 static void
 538 web_view_gtkhtml_connect_proxy_cb (EWebViewGtkHTML *web_view,
 539                            GtkAction *action,
 540                            GtkWidget *proxy)
 541 {
 542 	if (!GTK_IS_MENU_ITEM (proxy))
 543 		return;
 544 
 545 	g_signal_connect_swapped (
 546 		proxy, "select",
 547 		G_CALLBACK (web_view_gtkhtml_menu_item_select_cb), web_view);
 548 
 549 	g_signal_connect_swapped (
 550 		proxy, "deselect",
 551 		G_CALLBACK (web_view_gtkhtml_menu_item_deselect_cb), web_view);
 552 }
 553 
 554 static void
 555 web_view_gtkhtml_set_property (GObject *object,
 556                        guint property_id,
 557                        const GValue *value,
 558                        GParamSpec *pspec)
 559 {
 560 	switch (property_id) {
 561 		case PROP_ANIMATE:
 562 			e_web_view_gtkhtml_set_animate (
 563 				E_WEB_VIEW_GTKHTML (object),
 564 				g_value_get_boolean (value));
 565 			return;
 566 
 567 		case PROP_CARET_MODE:
 568 			e_web_view_gtkhtml_set_caret_mode (
 569 				E_WEB_VIEW_GTKHTML (object),
 570 				g_value_get_boolean (value));
 571 			return;
 572 
 573 		case PROP_DISABLE_PRINTING:
 574 			e_web_view_gtkhtml_set_disable_printing (
 575 				E_WEB_VIEW_GTKHTML (object),
 576 				g_value_get_boolean (value));
 577 			return;
 578 
 579 		case PROP_DISABLE_SAVE_TO_DISK:
 580 			e_web_view_gtkhtml_set_disable_save_to_disk (
 581 				E_WEB_VIEW_GTKHTML (object),
 582 				g_value_get_boolean (value));
 583 			return;
 584 
 585 		case PROP_EDITABLE:
 586 			e_web_view_gtkhtml_set_editable (
 587 				E_WEB_VIEW_GTKHTML (object),
 588 				g_value_get_boolean (value));
 589 			return;
 590 
 591 		case PROP_INLINE_SPELLING:
 592 			e_web_view_gtkhtml_set_inline_spelling (
 593 				E_WEB_VIEW_GTKHTML (object),
 594 				g_value_get_boolean (value));
 595 			return;
 596 
 597 		case PROP_MAGIC_LINKS:
 598 			e_web_view_gtkhtml_set_magic_links (
 599 				E_WEB_VIEW_GTKHTML (object),
 600 				g_value_get_boolean (value));
 601 			return;
 602 
 603 		case PROP_MAGIC_SMILEYS:
 604 			e_web_view_gtkhtml_set_magic_smileys (
 605 				E_WEB_VIEW_GTKHTML (object),
 606 				g_value_get_boolean (value));
 607 			return;
 608 
 609 		case PROP_OPEN_PROXY:
 610 			e_web_view_gtkhtml_set_open_proxy (
 611 				E_WEB_VIEW_GTKHTML (object),
 612 				g_value_get_object (value));
 613 			return;
 614 
 615 		case PROP_PRINT_PROXY:
 616 			e_web_view_gtkhtml_set_print_proxy (
 617 				E_WEB_VIEW_GTKHTML (object),
 618 				g_value_get_object (value));
 619 			return;
 620 
 621 		case PROP_SAVE_AS_PROXY:
 622 			e_web_view_gtkhtml_set_save_as_proxy (
 623 				E_WEB_VIEW_GTKHTML (object),
 624 				g_value_get_object (value));
 625 			return;
 626 
 627 		case PROP_SELECTED_URI:
 628 			e_web_view_gtkhtml_set_selected_uri (
 629 				E_WEB_VIEW_GTKHTML (object),
 630 				g_value_get_string (value));
 631 			return;
 632 		case PROP_CURSOR_IMAGE:
 633 			e_web_view_gtkhtml_set_cursor_image (
 634 				E_WEB_VIEW_GTKHTML (object),
 635 				g_value_get_object (value));
 636 			return;
 637 	}
 638 
 639 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 640 }
 641 
 642 static void
 643 web_view_gtkhtml_get_property (GObject *object,
 644                        guint property_id,
 645                        GValue *value,
 646                        GParamSpec *pspec)
 647 {
 648 	switch (property_id) {
 649 		case PROP_ANIMATE:
 650 			g_value_set_boolean (
 651 				value, e_web_view_gtkhtml_get_animate (
 652 				E_WEB_VIEW_GTKHTML (object)));
 653 			return;
 654 
 655 		case PROP_CARET_MODE:
 656 			g_value_set_boolean (
 657 				value, e_web_view_gtkhtml_get_caret_mode (
 658 				E_WEB_VIEW_GTKHTML (object)));
 659 			return;
 660 
 661 		case PROP_COPY_TARGET_LIST:
 662 			g_value_set_boxed (
 663 				value, e_web_view_gtkhtml_get_copy_target_list (
 664 				E_WEB_VIEW_GTKHTML (object)));
 665 			return;
 666 
 667 		case PROP_DISABLE_PRINTING:
 668 			g_value_set_boolean (
 669 				value, e_web_view_gtkhtml_get_disable_printing (
 670 				E_WEB_VIEW_GTKHTML (object)));
 671 			return;
 672 
 673 		case PROP_DISABLE_SAVE_TO_DISK:
 674 			g_value_set_boolean (
 675 				value, e_web_view_gtkhtml_get_disable_save_to_disk (
 676 				E_WEB_VIEW_GTKHTML (object)));
 677 			return;
 678 
 679 		case PROP_EDITABLE:
 680 			g_value_set_boolean (
 681 				value, e_web_view_gtkhtml_get_editable (
 682 				E_WEB_VIEW_GTKHTML (object)));
 683 			return;
 684 
 685 		case PROP_INLINE_SPELLING:
 686 			g_value_set_boolean (
 687 				value, e_web_view_gtkhtml_get_inline_spelling (
 688 				E_WEB_VIEW_GTKHTML (object)));
 689 			return;
 690 
 691 		case PROP_MAGIC_LINKS:
 692 			g_value_set_boolean (
 693 				value, e_web_view_gtkhtml_get_magic_links (
 694 				E_WEB_VIEW_GTKHTML (object)));
 695 			return;
 696 
 697 		case PROP_MAGIC_SMILEYS:
 698 			g_value_set_boolean (
 699 				value, e_web_view_gtkhtml_get_magic_smileys (
 700 				E_WEB_VIEW_GTKHTML (object)));
 701 			return;
 702 
 703 		case PROP_OPEN_PROXY:
 704 			g_value_set_object (
 705 				value, e_web_view_gtkhtml_get_open_proxy (
 706 				E_WEB_VIEW_GTKHTML (object)));
 707 			return;
 708 
 709 		case PROP_PASTE_TARGET_LIST:
 710 			g_value_set_boxed (
 711 				value, e_web_view_gtkhtml_get_paste_target_list (
 712 				E_WEB_VIEW_GTKHTML (object)));
 713 			return;
 714 
 715 		case PROP_PRINT_PROXY:
 716 			g_value_set_object (
 717 				value, e_web_view_gtkhtml_get_print_proxy (
 718 				E_WEB_VIEW_GTKHTML (object)));
 719 			return;
 720 
 721 		case PROP_SAVE_AS_PROXY:
 722 			g_value_set_object (
 723 				value, e_web_view_gtkhtml_get_save_as_proxy (
 724 				E_WEB_VIEW_GTKHTML (object)));
 725 			return;
 726 
 727 		case PROP_SELECTED_URI:
 728 			g_value_set_string (
 729 				value, e_web_view_gtkhtml_get_selected_uri (
 730 				E_WEB_VIEW_GTKHTML (object)));
 731 			return;
 732 
 733 		case PROP_CURSOR_IMAGE:
 734 			g_value_set_object (
 735 				value, e_web_view_gtkhtml_get_cursor_image (
 736 				E_WEB_VIEW_GTKHTML (object)));
 737 			return;
 738 	}
 739 
 740 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 741 }
 742 
 743 static void
 744 web_view_gtkhtml_dispose (GObject *object)
 745 {
 746 	EWebViewGtkHTMLPrivate *priv;
 747 
 748 	priv = E_WEB_VIEW_GTKHTML_GET_PRIVATE (object);
 749 
 750 	if (priv->ui_manager != NULL) {
 751 		g_object_unref (priv->ui_manager);
 752 		priv->ui_manager = NULL;
 753 	}
 754 
 755 	if (priv->open_proxy != NULL) {
 756 		g_object_unref (priv->open_proxy);
 757 		priv->open_proxy = NULL;
 758 	}
 759 
 760 	if (priv->print_proxy != NULL) {
 761 		g_object_unref (priv->print_proxy);
 762 		priv->print_proxy = NULL;
 763 	}
 764 
 765 	if (priv->save_as_proxy != NULL) {
 766 		g_object_unref (priv->save_as_proxy);
 767 		priv->save_as_proxy = NULL;
 768 	}
 769 
 770 	if (priv->copy_target_list != NULL) {
 771 		gtk_target_list_unref (priv->copy_target_list);
 772 		priv->copy_target_list = NULL;
 773 	}
 774 
 775 	if (priv->paste_target_list != NULL) {
 776 		gtk_target_list_unref (priv->paste_target_list);
 777 		priv->paste_target_list = NULL;
 778 	}
 779 
 780 	if (priv->cursor_image != NULL) {
 781 		g_object_unref (priv->cursor_image);
 782 		priv->cursor_image = NULL;
 783 	}
 784 
 785 	/* Chain up to parent's dispose() method. */
 786 	G_OBJECT_CLASS (e_web_view_gtkhtml_parent_class)->dispose (object);
 787 }
 788 
 789 static void
 790 web_view_gtkhtml_finalize (GObject *object)
 791 {
 792 	EWebViewGtkHTMLPrivate *priv;
 793 
 794 	priv = E_WEB_VIEW_GTKHTML_GET_PRIVATE (object);
 795 
 796 	/* All URI requests should be complete or cancelled by now. */
 797 	if (priv->requests != NULL)
 798 		g_warning ("Finalizing EWebViewGtkHTML with active URI requests");
 799 
 800 	g_free (priv->selected_uri);
 801 
 802 	/* Chain up to parent's finalize() method. */
 803 	G_OBJECT_CLASS (e_web_view_gtkhtml_parent_class)->finalize (object);
 804 }
 805 
 806 static void
 807 web_view_gtkhtml_constructed (GObject *object)
 808 {
 809 #ifndef G_OS_WIN32
 810 	GSettings *settings;
 811 
 812 	settings = g_settings_new ("org.gnome.desktop.lockdown");
 813 
 814 	g_settings_bind (
 815 		settings, "disable-printing",
 816 		object, "disable-printing",
 817 		G_SETTINGS_BIND_GET);
 818 
 819 	g_settings_bind (
 820 		settings, "disable-save-to-disk",
 821 		object, "disable-save-to-disk",
 822 		G_SETTINGS_BIND_GET);
 823 
 824 	g_object_unref (settings);
 825 #endif
 826 
 827 	/* Chain up to parent's constructed() method. */
 828 	G_OBJECT_CLASS (e_web_view_gtkhtml_parent_class)->constructed (object);
 829 }
 830 
 831 static gboolean
 832 web_view_gtkhtml_button_press_event (GtkWidget *widget,
 833                              GdkEventButton *event)
 834 {
 835 	GtkWidgetClass *widget_class;
 836 	EWebViewGtkHTML *web_view;
 837 
 838 	web_view = E_WEB_VIEW_GTKHTML (widget);
 839 
 840 	if (web_view_gtkhtml_button_press_event_cb (web_view, event, NULL))
 841 		return TRUE;
 842 
 843 	/* Chain up to parent's button_press_event() method. */
 844 	widget_class = GTK_WIDGET_CLASS (e_web_view_gtkhtml_parent_class);
 845 	return widget_class->button_press_event (widget, event);
 846 }
 847 
 848 static gboolean
 849 web_view_gtkhtml_scroll_event (GtkWidget *widget,
 850                        GdkEventScroll *event)
 851 {
 852 	if (event->state & GDK_CONTROL_MASK) {
 853 		GdkScrollDirection direction = event->direction;
 854 
 855 		#if GTK_CHECK_VERSION(3,3,18)
 856 		if (direction == GDK_SCROLL_SMOOTH) {
 857 			static gdouble total_delta_y = 0.0;
 858 
 859 			total_delta_y += event->delta_y;
 860 
 861 			if (total_delta_y >= 1.0) {
 862 				total_delta_y = 0.0;
 863 				direction = GDK_SCROLL_DOWN;
 864 			} else if (total_delta_y <= -1.0) {
 865 				total_delta_y = 0.0;
 866 				direction = GDK_SCROLL_UP;
 867 			} else {
 868 				return FALSE;
 869 			}
 870 		}
 871 		#endif
 872 
 873 		switch (direction) {
 874 			case GDK_SCROLL_UP:
 875 				gtk_html_zoom_in (GTK_HTML (widget));
 876 				return TRUE;
 877 			case GDK_SCROLL_DOWN:
 878 				gtk_html_zoom_out (GTK_HTML (widget));
 879 				return TRUE;
 880 			default:
 881 				break;
 882 		}
 883 	}
 884 
 885 	return FALSE;
 886 }
 887 
 888 static void
 889 web_view_gtkhtml_url_requested (GtkHTML *html,
 890                         const gchar *uri,
 891                         GtkHTMLStream *stream)
 892 {
 893 	EWebViewGtkHTMLRequest *request;
 894 
 895 	request = web_view_gtkhtml_request_new (E_WEB_VIEW_GTKHTML (html), uri, stream);
 896 
 897 	g_file_read_async (
 898 		request->file, G_PRIORITY_DEFAULT,
 899 		request->cancellable, (GAsyncReadyCallback)
 900 		web_view_gtkhtml_request_read_cb, request);
 901 }
 902 
 903 static void
 904 web_view_gtkhtml_gtkhtml_link_clicked (GtkHTML *html,
 905                                const gchar *uri)
 906 {
 907 	EWebViewGtkHTMLClass *class;
 908 	EWebViewGtkHTML *web_view;
 909 
 910 	web_view = E_WEB_VIEW_GTKHTML (html);
 911 
 912 	class = E_WEB_VIEW_GTKHTML_GET_CLASS (web_view);
 913 	g_return_if_fail (class->link_clicked != NULL);
 914 
 915 	class->link_clicked (web_view, uri);
 916 }
 917 
 918 static void
 919 web_view_gtkhtml_on_url (GtkHTML *html,
 920                  const gchar *uri)
 921 {
 922 	EWebViewGtkHTMLClass *class;
 923 	EWebViewGtkHTML *web_view;
 924 
 925 	web_view = E_WEB_VIEW_GTKHTML (html);
 926 
 927 	class = E_WEB_VIEW_GTKHTML_GET_CLASS (web_view);
 928 	g_return_if_fail (class->hovering_over_link != NULL);
 929 
 930 	/* XXX WebKit would supply a title here. */
 931 	class->hovering_over_link (web_view, NULL, uri);
 932 }
 933 
 934 static void
 935 web_view_gtkhtml_iframe_created (GtkHTML *html,
 936                          GtkHTML *iframe)
 937 {
 938 	g_signal_connect_swapped (
 939 		iframe, "button-press-event",
 940 		G_CALLBACK (web_view_gtkhtml_button_press_event_cb), html);
 941 }
 942 
 943 static gchar *
 944 web_view_gtkhtml_extract_uri (EWebViewGtkHTML *web_view,
 945                       GdkEventButton *event,
 946                       GtkHTML *html)
 947 {
 948 	gchar *uri;
 949 
 950 	if (event != NULL)
 951 		uri = gtk_html_get_url_at (html, event->x, event->y);
 952 	else
 953 		uri = gtk_html_get_cursor_url (html);
 954 
 955 	return uri;
 956 }
 957 
 958 static void
 959 web_view_gtkhtml_hovering_over_link (EWebViewGtkHTML *web_view,
 960                              const gchar *title,
 961                              const gchar *uri)
 962 {
 963 	CamelInternetAddress *address;
 964 	CamelURL *curl;
 965 	const gchar *format = NULL;
 966 	gchar *message = NULL;
 967 	gchar *who;
 968 
 969 	if (uri == NULL || *uri == '\0')
 970 		goto exit;
 971 
 972 	if (g_str_has_prefix (uri, "mailto:"))
 973 		format = _("Click to mail %s");
 974 	else if (g_str_has_prefix (uri, "callto:"))
 975 		format = _("Click to call %s");
 976 	else if (g_str_has_prefix (uri, "h323:"))
 977 		format = _("Click to call %s");
 978 	else if (g_str_has_prefix (uri, "sip:"))
 979 		format = _("Click to call %s");
 980 	else if (g_str_has_prefix (uri, "##"))
 981 		message = g_strdup (_("Click to hide/unhide addresses"));
 982 	else
 983 		message = g_strdup_printf (_("Click to open %s"), uri);
 984 
 985 	if (format == NULL)
 986 		goto exit;
 987 
 988 	/* XXX Use something other than Camel here.  Surely
 989 	 *     there's other APIs around that can do this. */
 990 	curl = camel_url_new (uri, NULL);
 991 	address = camel_internet_address_new ();
 992 	camel_address_decode (CAMEL_ADDRESS (address), curl->path);
 993 	who = camel_address_format (CAMEL_ADDRESS (address));
 994 	g_object_unref (address);
 995 	camel_url_free (curl);
 996 
 997 	if (who == NULL)
 998 		who = g_strdup (strchr (uri, ':') + 1);
 999 
1000 	message = g_strdup_printf (format, who);
1001 
1002 	g_free (who);
1003 
1004 exit:
1005 	e_web_view_gtkhtml_status_message (web_view, message);
1006 
1007 	g_free (message);
1008 }
1009 
1010 static void
1011 web_view_gtkhtml_link_clicked (EWebViewGtkHTML *web_view,
1012                        const gchar *uri)
1013 {
1014 	gpointer parent;
1015 
1016 	parent = gtk_widget_get_toplevel (GTK_WIDGET (web_view));
1017 	parent = gtk_widget_is_toplevel (parent) ? parent : NULL;
1018 
1019 	e_show_uri (parent, uri);
1020 }
1021 
1022 static void
1023 web_view_gtkhtml_load_string (EWebViewGtkHTML *web_view,
1024                       const gchar *string)
1025 {
1026 	if (string != NULL && *string != '\0')
1027 		gtk_html_load_from_string (GTK_HTML (web_view), string, -1);
1028 	else
1029 		e_web_view_gtkhtml_clear (web_view);
1030 }
1031 
1032 static void
1033 web_view_gtkhtml_copy_clipboard (EWebViewGtkHTML *web_view)
1034 {
1035 	gtk_html_command (GTK_HTML (web_view), "copy");
1036 }
1037 
1038 static void
1039 web_view_gtkhtml_cut_clipboard (EWebViewGtkHTML *web_view)
1040 {
1041 	if (e_web_view_gtkhtml_get_editable (web_view))
1042 		gtk_html_command (GTK_HTML (web_view), "cut");
1043 }
1044 
1045 static void
1046 web_view_gtkhtml_paste_clipboard (EWebViewGtkHTML *web_view)
1047 {
1048 	if (e_web_view_gtkhtml_get_editable (web_view))
1049 		gtk_html_command (GTK_HTML (web_view), "paste");
1050 }
1051 
1052 static gboolean
1053 web_view_gtkhtml_popup_event (EWebViewGtkHTML *web_view,
1054                       GdkEventButton *event,
1055                       const gchar *uri)
1056 {
1057 	e_web_view_gtkhtml_set_selected_uri (web_view, uri);
1058 	e_web_view_gtkhtml_show_popup_menu (web_view, event, NULL, NULL);
1059 
1060 	return TRUE;
1061 }
1062 
1063 static void
1064 web_view_gtkhtml_stop_loading (EWebViewGtkHTML *web_view)
1065 {
1066 	g_list_foreach (
1067 		web_view->priv->requests, (GFunc)
1068 		web_view_gtkhtml_request_cancel, NULL);
1069 
1070 	gtk_html_stop (GTK_HTML (web_view));
1071 }
1072 
1073 static void
1074 web_view_gtkhtml_update_actions (EWebViewGtkHTML *web_view)
1075 {
1076 	GtkActionGroup *action_group;
1077 	gboolean have_selection;
1078 	gboolean scheme_is_http = FALSE;
1079 	gboolean scheme_is_mailto = FALSE;
1080 	gboolean uri_is_valid = FALSE;
1081 	gboolean has_cursor_image;
1082 	gboolean visible;
1083 	const gchar *group_name;
1084 	const gchar *uri;
1085 
1086 	uri = e_web_view_gtkhtml_get_selected_uri (web_view);
1087 	have_selection = e_web_view_gtkhtml_is_selection_active (web_view);
1088 	has_cursor_image = e_web_view_gtkhtml_get_cursor_image (web_view) != NULL;
1089 
1090 	/* Parse the URI early so we know if the actions will work. */
1091 	if (uri != NULL) {
1092 		CamelURL *curl;
1093 
1094 		curl = camel_url_new (uri, NULL);
1095 		uri_is_valid = (curl != NULL);
1096 		camel_url_free (curl);
1097 
1098 		scheme_is_http =
1099 			(g_ascii_strncasecmp (uri, "http:", 5) == 0) ||
1100 			(g_ascii_strncasecmp (uri, "https:", 6) == 0);
1101 
1102 		scheme_is_mailto =
1103 			(g_ascii_strncasecmp (uri, "mailto:", 7) == 0);
1104 	}
1105 
1106 	/* Allow copying the URI even if it's malformed. */
1107 	group_name = "uri";
1108 	visible = (uri != NULL) && !scheme_is_mailto;
1109 	action_group = e_web_view_gtkhtml_get_action_group (web_view, group_name);
1110 	gtk_action_group_set_visible (action_group, visible);
1111 
1112 	group_name = "http";
1113 	visible = uri_is_valid && scheme_is_http;
1114 	action_group = e_web_view_gtkhtml_get_action_group (web_view, group_name);
1115 	gtk_action_group_set_visible (action_group, visible);
1116 
1117 	group_name = "mailto";
1118 	visible = uri_is_valid && scheme_is_mailto;
1119 	action_group = e_web_view_gtkhtml_get_action_group (web_view, group_name);
1120 	gtk_action_group_set_visible (action_group, visible);
1121 
1122 	group_name = "image";
1123 	visible = has_cursor_image;
1124 	action_group = e_web_view_gtkhtml_get_action_group (web_view, group_name);
1125 	gtk_action_group_set_visible (action_group, visible);
1126 
1127 	group_name = "selection";
1128 	visible = have_selection;
1129 	action_group = e_web_view_gtkhtml_get_action_group (web_view, group_name);
1130 	gtk_action_group_set_visible (action_group, visible);
1131 
1132 	group_name = "standard";
1133 	visible = (uri == NULL);
1134 	action_group = e_web_view_gtkhtml_get_action_group (web_view, group_name);
1135 	gtk_action_group_set_visible (action_group, visible);
1136 
1137 	group_name = "lockdown-printing";
1138 	visible = (uri == NULL) && !web_view->priv->disable_printing;
1139 	action_group = e_web_view_gtkhtml_get_action_group (web_view, group_name);
1140 	gtk_action_group_set_visible (action_group, visible);
1141 
1142 	group_name = "lockdown-save-to-disk";
1143 	visible = (uri == NULL) && !web_view->priv->disable_save_to_disk;
1144 	action_group = e_web_view_gtkhtml_get_action_group (web_view, group_name);
1145 	gtk_action_group_set_visible (action_group, visible);
1146 }
1147 
1148 static void
1149 web_view_gtkhtml_submit_alert (EAlertSink *alert_sink,
1150                        EAlert *alert)
1151 {
1152 	GtkIconInfo *icon_info;
1153 	EWebViewGtkHTML *web_view;
1154 	GtkWidget *dialog;
1155 	GString *buffer;
1156 	const gchar *icon_name = NULL;
1157 	const gchar *filename;
1158 	gpointer parent;
1159 	gchar *icon_uri;
1160 	gint size = 0;
1161 	GError *error = NULL;
1162 
1163 	web_view = E_WEB_VIEW_GTKHTML (alert_sink);
1164 
1165 	parent = gtk_widget_get_toplevel (GTK_WIDGET (web_view));
1166 	parent = gtk_widget_is_toplevel (parent) ? parent : NULL;
1167 
1168 	/* We use equivalent named icons instead of stock IDs,
1169 	 * since it's easier to get the filename of the icon. */
1170 	switch (e_alert_get_message_type (alert)) {
1171 		case GTK_MESSAGE_INFO:
1172 			icon_name = "dialog-information";
1173 			break;
1174 
1175 		case GTK_MESSAGE_WARNING:
1176 			icon_name = "dialog-warning";
1177 			break;
1178 
1179 		case GTK_MESSAGE_ERROR:
1180 			icon_name = "dialog-error";
1181 			break;
1182 
1183 		default:
1184 			dialog = e_alert_dialog_new (parent, alert);
1185 			gtk_dialog_run (GTK_DIALOG (dialog));
1186 			gtk_widget_destroy (dialog);
1187 			return;
1188 	}
1189 
1190 	gtk_icon_size_lookup (GTK_ICON_SIZE_DIALOG, &size, NULL);
1191 
1192 	icon_info = gtk_icon_theme_lookup_icon (
1193 		gtk_icon_theme_get_default (),
1194 		icon_name, size, GTK_ICON_LOOKUP_NO_SVG);
1195 	g_return_if_fail (icon_info != NULL);
1196 
1197 	filename = gtk_icon_info_get_filename (icon_info);
1198 	icon_uri = g_filename_to_uri (filename, NULL, &error);
1199 
1200 	if (error != NULL) {
1201 		g_warning ("%s", error->message);
1202 		g_clear_error (&error);
1203 	}
1204 
1205 	buffer = g_string_sized_new (512);
1206 
1207 	g_string_append (
1208 		buffer,
1209 		"<html>"
1210 		"<head>"
1211 		"<meta http-equiv=\"content-type\""
1212 		" content=\"text/html; charset=utf-8\">"
1213 		"</head>"
1214 		"<body>");
1215 
1216 	g_string_append (
1217 		buffer,
1218 		"<table bgcolor='#000000' width='100%'"
1219 		" cellpadding='1' cellspacing='0'>"
1220 		"<tr>"
1221 		"<td>"
1222 		"<table bgcolor='#dddddd' width='100%' cellpadding='6'>"
1223 		"<tr>");
1224 
1225 	g_string_append_printf (
1226 		buffer,
1227 		"<tr>"
1228 		"<td valign='top'>"
1229 		"<img src='%s'/>"
1230 		"</td>"
1231 		"<td align='left' width='100%%'>"
1232 		"<h3>%s</h3>"
1233 		"%s"
1234 		"</td>"
1235 		"</tr>",
1236 		icon_uri,
1237 		e_alert_get_primary_text (alert),
1238 		e_alert_get_secondary_text (alert));
1239 
1240 	g_string_append (
1241 		buffer,
1242 		"</table>"
1243 		"</td>"
1244 		"</tr>"
1245 		"</table>"
1246 		"</body>"
1247 		"</html>");
1248 
1249 	e_web_view_gtkhtml_load_string (web_view, buffer->str);
1250 
1251 	g_string_free (buffer, TRUE);
1252 
1253 	gtk_icon_info_free (icon_info);
1254 	g_free (icon_uri);
1255 }
1256 
1257 static void
1258 web_view_gtkhtml_selectable_update_actions (ESelectable *selectable,
1259                                     EFocusTracker *focus_tracker,
1260                                     GdkAtom *clipboard_targets,
1261                                     gint n_clipboard_targets)
1262 {
1263 	EWebViewGtkHTML *web_view;
1264 	GtkAction *action;
1265 	/*GtkTargetList *target_list;*/
1266 	gboolean can_paste = FALSE;
1267 	gboolean editable;
1268 	gboolean have_selection;
1269 	gboolean sensitive;
1270 	const gchar *tooltip;
1271 	/*gint ii;*/
1272 
1273 	web_view = E_WEB_VIEW_GTKHTML (selectable);
1274 	editable = e_web_view_gtkhtml_get_editable (web_view);
1275 	have_selection = e_web_view_gtkhtml_is_selection_active (web_view);
1276 
1277 	/* XXX GtkHtml implements its own clipboard instead of using
1278 	 *     GDK_SELECTION_CLIPBOARD, so we don't get notifications
1279 	 *     when the clipboard contents change.  The logic below
1280 	 *     is what we would do if GtkHtml worked properly.
1281 	 *     Instead, we need to keep the Paste action sensitive so
1282 	 *     its accelerator overrides GtkHtml's key binding. */
1283 #if 0
1284 	target_list = e_selectable_get_paste_target_list (selectable);
1285 	for (ii = 0; ii < n_clipboard_targets && !can_paste; ii++)
1286 		can_paste = gtk_target_list_find (
1287 			target_list, clipboard_targets[ii], NULL);
1288 #endif
1289 	can_paste = TRUE;
1290 
1291 	action = e_focus_tracker_get_cut_clipboard_action (focus_tracker);
1292 	sensitive = editable && have_selection;
1293 	tooltip = _("Cut the selection");
1294 	gtk_action_set_sensitive (action, sensitive);
1295 	gtk_action_set_tooltip (action, tooltip);
1296 
1297 	action = e_focus_tracker_get_copy_clipboard_action (focus_tracker);
1298 	sensitive = have_selection;
1299 	tooltip = _("Copy the selection");
1300 	gtk_action_set_sensitive (action, sensitive);
1301 	gtk_action_set_tooltip (action, tooltip);
1302 
1303 	action = e_focus_tracker_get_paste_clipboard_action (focus_tracker);
1304 	sensitive = editable && can_paste;
1305 	tooltip = _("Paste the clipboard");
1306 	gtk_action_set_sensitive (action, sensitive);
1307 	gtk_action_set_tooltip (action, tooltip);
1308 
1309 	action = e_focus_tracker_get_select_all_action (focus_tracker);
1310 	sensitive = TRUE;
1311 	tooltip = _("Select all text and images");
1312 	gtk_action_set_sensitive (action, sensitive);
1313 	gtk_action_set_tooltip (action, tooltip);
1314 }
1315 
1316 static void
1317 web_view_gtkhtml_selectable_cut_clipboard (ESelectable *selectable)
1318 {
1319 	e_web_view_gtkhtml_cut_clipboard (E_WEB_VIEW_GTKHTML (selectable));
1320 }
1321 
1322 static void
1323 web_view_gtkhtml_selectable_copy_clipboard (ESelectable *selectable)
1324 {
1325 	e_web_view_gtkhtml_copy_clipboard (E_WEB_VIEW_GTKHTML (selectable));
1326 }
1327 
1328 static void
1329 web_view_gtkhtml_selectable_paste_clipboard (ESelectable *selectable)
1330 {
1331 	e_web_view_gtkhtml_paste_clipboard (E_WEB_VIEW_GTKHTML (selectable));
1332 }
1333 
1334 static void
1335 web_view_gtkhtml_selectable_select_all (ESelectable *selectable)
1336 {
1337 	e_web_view_gtkhtml_select_all (E_WEB_VIEW_GTKHTML (selectable));
1338 }
1339 
1340 static void
1341 e_web_view_gtkhtml_class_init (EWebViewGtkHTMLClass *class)
1342 {
1343 	GObjectClass *object_class;
1344 	GtkWidgetClass *widget_class;
1345 	GtkHTMLClass *html_class;
1346 
1347 	g_type_class_add_private (class, sizeof (EWebViewGtkHTMLPrivate));
1348 
1349 	object_class = G_OBJECT_CLASS (class);
1350 	object_class->set_property = web_view_gtkhtml_set_property;
1351 	object_class->get_property = web_view_gtkhtml_get_property;
1352 	object_class->dispose = web_view_gtkhtml_dispose;
1353 	object_class->finalize = web_view_gtkhtml_finalize;
1354 	object_class->constructed = web_view_gtkhtml_constructed;
1355 
1356 	widget_class = GTK_WIDGET_CLASS (class);
1357 	widget_class->button_press_event = web_view_gtkhtml_button_press_event;
1358 	widget_class->scroll_event = web_view_gtkhtml_scroll_event;
1359 
1360 	html_class = GTK_HTML_CLASS (class);
1361 	html_class->url_requested = web_view_gtkhtml_url_requested;
1362 	html_class->link_clicked = web_view_gtkhtml_gtkhtml_link_clicked;
1363 	html_class->on_url = web_view_gtkhtml_on_url;
1364 	html_class->iframe_created = web_view_gtkhtml_iframe_created;
1365 
1366 	class->extract_uri = web_view_gtkhtml_extract_uri;
1367 	class->hovering_over_link = web_view_gtkhtml_hovering_over_link;
1368 	class->link_clicked = web_view_gtkhtml_link_clicked;
1369 	class->load_string = web_view_gtkhtml_load_string;
1370 	class->copy_clipboard = web_view_gtkhtml_copy_clipboard;
1371 	class->cut_clipboard = web_view_gtkhtml_cut_clipboard;
1372 	class->paste_clipboard = web_view_gtkhtml_paste_clipboard;
1373 	class->popup_event = web_view_gtkhtml_popup_event;
1374 	class->stop_loading = web_view_gtkhtml_stop_loading;
1375 	class->update_actions = web_view_gtkhtml_update_actions;
1376 
1377 	g_object_class_install_property (
1378 		object_class,
1379 		PROP_ANIMATE,
1380 		g_param_spec_boolean (
1381 			"animate",
1382 			"Animate Images",
1383 			NULL,
1384 			FALSE,
1385 			G_PARAM_READWRITE));
1386 
1387 	g_object_class_install_property (
1388 		object_class,
1389 		PROP_CARET_MODE,
1390 		g_param_spec_boolean (
1391 			"caret-mode",
1392 			"Caret Mode",
1393 			NULL,
1394 			FALSE,
1395 			G_PARAM_READWRITE));
1396 
1397 	/* Inherited from ESelectableInterface */
1398 	g_object_class_override_property (
1399 		object_class,
1400 		PROP_COPY_TARGET_LIST,
1401 		"copy-target-list");
1402 
1403 	g_object_class_install_property (
1404 		object_class,
1405 		PROP_DISABLE_PRINTING,
1406 		g_param_spec_boolean (
1407 			"disable-printing",
1408 			"Disable Printing",
1409 			NULL,
1410 			FALSE,
1411 			G_PARAM_READWRITE |
1412 			G_PARAM_CONSTRUCT));
1413 
1414 	g_object_class_install_property (
1415 		object_class,
1416 		PROP_DISABLE_SAVE_TO_DISK,
1417 		g_param_spec_boolean (
1418 			"disable-save-to-disk",
1419 			"Disable Save-to-Disk",
1420 			NULL,
1421 			FALSE,
1422 			G_PARAM_READWRITE |
1423 			G_PARAM_CONSTRUCT));
1424 
1425 	g_object_class_install_property (
1426 		object_class,
1427 		PROP_EDITABLE,
1428 		g_param_spec_boolean (
1429 			"editable",
1430 			"Editable",
1431 			NULL,
1432 			FALSE,
1433 			G_PARAM_READWRITE));
1434 
1435 	g_object_class_install_property (
1436 		object_class,
1437 		PROP_INLINE_SPELLING,
1438 		g_param_spec_boolean (
1439 			"inline-spelling",
1440 			"Inline Spelling",
1441 			NULL,
1442 			FALSE,
1443 			G_PARAM_READWRITE));
1444 
1445 	g_object_class_install_property (
1446 		object_class,
1447 		PROP_MAGIC_LINKS,
1448 		g_param_spec_boolean (
1449 			"magic-links",
1450 			"Magic Links",
1451 			NULL,
1452 			FALSE,
1453 			G_PARAM_READWRITE));
1454 
1455 	g_object_class_install_property (
1456 		object_class,
1457 		PROP_MAGIC_SMILEYS,
1458 		g_param_spec_boolean (
1459 			"magic-smileys",
1460 			"Magic Smileys",
1461 			NULL,
1462 			FALSE,
1463 			G_PARAM_READWRITE));
1464 
1465 	g_object_class_install_property (
1466 		object_class,
1467 		PROP_OPEN_PROXY,
1468 		g_param_spec_object (
1469 			"open-proxy",
1470 			"Open Proxy",
1471 			NULL,
1472 			GTK_TYPE_ACTION,
1473 			G_PARAM_READWRITE));
1474 
1475 	/* Inherited from ESelectableInterface */
1476 	g_object_class_override_property (
1477 		object_class,
1478 		PROP_PASTE_TARGET_LIST,
1479 		"paste-target-list");
1480 
1481 	g_object_class_install_property (
1482 		object_class,
1483 		PROP_PRINT_PROXY,
1484 		g_param_spec_object (
1485 			"print-proxy",
1486 			"Print Proxy",
1487 			NULL,
1488 			GTK_TYPE_ACTION,
1489 			G_PARAM_READWRITE));
1490 
1491 	g_object_class_install_property (
1492 		object_class,
1493 		PROP_SAVE_AS_PROXY,
1494 		g_param_spec_object (
1495 			"save-as-proxy",
1496 			"Save As Proxy",
1497 			NULL,
1498 			GTK_TYPE_ACTION,
1499 			G_PARAM_READWRITE));
1500 
1501 	g_object_class_install_property (
1502 		object_class,
1503 		PROP_SELECTED_URI,
1504 		g_param_spec_string (
1505 			"selected-uri",
1506 			"Selected URI",
1507 			NULL,
1508 			NULL,
1509 			G_PARAM_READWRITE));
1510 
1511 	g_object_class_install_property (
1512 		object_class,
1513 		PROP_CURSOR_IMAGE,
1514 		g_param_spec_object (
1515 			"cursor-image",
1516 			"Image animation at the mouse cursor",
1517 			NULL,
1518 			GDK_TYPE_PIXBUF_ANIMATION,
1519 			G_PARAM_READWRITE));
1520 
1521 	signals[COPY_CLIPBOARD] = g_signal_new (
1522 		"copy-clipboard",
1523 		G_TYPE_FROM_CLASS (class),
1524 		G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1525 		G_STRUCT_OFFSET (EWebViewGtkHTMLClass, copy_clipboard),
1526 		NULL, NULL,
1527 		g_cclosure_marshal_VOID__VOID,
1528 		G_TYPE_NONE, 0);
1529 
1530 	signals[CUT_CLIPBOARD] = g_signal_new (
1531 		"cut-clipboard",
1532 		G_TYPE_FROM_CLASS (class),
1533 		G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1534 		G_STRUCT_OFFSET (EWebViewGtkHTMLClass, cut_clipboard),
1535 		NULL, NULL,
1536 		g_cclosure_marshal_VOID__VOID,
1537 		G_TYPE_NONE, 0);
1538 
1539 	signals[PASTE_CLIPBOARD] = g_signal_new (
1540 		"paste-clipboard",
1541 		G_TYPE_FROM_CLASS (class),
1542 		G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1543 		G_STRUCT_OFFSET (EWebViewGtkHTMLClass, paste_clipboard),
1544 		NULL, NULL,
1545 		g_cclosure_marshal_VOID__VOID,
1546 		G_TYPE_NONE, 0);
1547 
1548 	signals[POPUP_EVENT] = g_signal_new (
1549 		"popup-event",
1550 		G_TYPE_FROM_CLASS (class),
1551 		G_SIGNAL_RUN_LAST,
1552 		G_STRUCT_OFFSET (EWebViewGtkHTMLClass, popup_event),
1553 		g_signal_accumulator_true_handled, NULL,
1554 		e_marshal_BOOLEAN__BOXED_STRING,
1555 		G_TYPE_BOOLEAN, 2,
1556 		GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE,
1557 		G_TYPE_STRING);
1558 
1559 	signals[STATUS_MESSAGE] = g_signal_new (
1560 		"status-message",
1561 		G_TYPE_FROM_CLASS (class),
1562 		G_SIGNAL_RUN_LAST,
1563 		G_STRUCT_OFFSET (EWebViewGtkHTMLClass, status_message),
1564 		NULL, NULL,
1565 		g_cclosure_marshal_VOID__STRING,
1566 		G_TYPE_NONE, 1,
1567 		G_TYPE_STRING);
1568 
1569 	signals[STOP_LOADING] = g_signal_new (
1570 		"stop-loading",
1571 		G_TYPE_FROM_CLASS (class),
1572 		G_SIGNAL_RUN_LAST,
1573 		G_STRUCT_OFFSET (EWebViewGtkHTMLClass, stop_loading),
1574 		NULL, NULL,
1575 		g_cclosure_marshal_VOID__VOID,
1576 		G_TYPE_NONE, 0);
1577 
1578 	signals[UPDATE_ACTIONS] = g_signal_new (
1579 		"update-actions",
1580 		G_TYPE_FROM_CLASS (class),
1581 		G_SIGNAL_RUN_LAST,
1582 		G_STRUCT_OFFSET (EWebViewGtkHTMLClass, update_actions),
1583 		NULL, NULL,
1584 		g_cclosure_marshal_VOID__VOID,
1585 		G_TYPE_NONE, 0);
1586 
1587 	/* return TRUE when a signal handler processed the mailto URI */
1588 	signals[PROCESS_MAILTO] = g_signal_new (
1589 		"process-mailto",
1590 		G_TYPE_FROM_CLASS (class),
1591 		G_SIGNAL_RUN_LAST,
1592 		G_STRUCT_OFFSET (EWebViewGtkHTMLClass, process_mailto),
1593 		NULL, NULL,
1594 		e_marshal_BOOLEAN__STRING,
1595 		G_TYPE_BOOLEAN, 1, G_TYPE_STRING);
1596 }
1597 
1598 static void
1599 e_web_view_gtkhtml_alert_sink_init (EAlertSinkInterface *interface)
1600 {
1601 	interface->submit_alert = web_view_gtkhtml_submit_alert;
1602 }
1603 
1604 static void
1605 e_web_view_gtkhtml_selectable_init (ESelectableInterface *interface)
1606 {
1607 	interface->update_actions = web_view_gtkhtml_selectable_update_actions;
1608 	interface->cut_clipboard = web_view_gtkhtml_selectable_cut_clipboard;
1609 	interface->copy_clipboard = web_view_gtkhtml_selectable_copy_clipboard;
1610 	interface->paste_clipboard = web_view_gtkhtml_selectable_paste_clipboard;
1611 	interface->select_all = web_view_gtkhtml_selectable_select_all;
1612 }
1613 
1614 static void
1615 e_web_view_gtkhtml_init (EWebViewGtkHTML *web_view)
1616 {
1617 	GtkUIManager *ui_manager;
1618 	GtkActionGroup *action_group;
1619 	GtkTargetList *target_list;
1620 	EPopupAction *popup_action;
1621 	const gchar *domain = GETTEXT_PACKAGE;
1622 	const gchar *id;
1623 	GError *error = NULL;
1624 
1625 	web_view->priv = E_WEB_VIEW_GTKHTML_GET_PRIVATE (web_view);
1626 
1627 	ui_manager = gtk_ui_manager_new ();
1628 	web_view->priv->ui_manager = ui_manager;
1629 
1630 	g_signal_connect_swapped (
1631 		ui_manager, "connect-proxy",
1632 		G_CALLBACK (web_view_gtkhtml_connect_proxy_cb), web_view);
1633 
1634 	target_list = gtk_target_list_new (NULL, 0);
1635 	web_view->priv->copy_target_list = target_list;
1636 
1637 	target_list = gtk_target_list_new (NULL, 0);
1638 	web_view->priv->paste_target_list = target_list;
1639 
1640 	action_group = gtk_action_group_new ("uri");
1641 	gtk_action_group_set_translation_domain (action_group, domain);
1642 	gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
1643 	g_object_unref (action_group);
1644 
1645 	gtk_action_group_add_actions (
1646 		action_group, uri_entries,
1647 		G_N_ELEMENTS (uri_entries), web_view);
1648 
1649 	action_group = gtk_action_group_new ("http");
1650 	gtk_action_group_set_translation_domain (action_group, domain);
1651 	gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
1652 	g_object_unref (action_group);
1653 
1654 	gtk_action_group_add_actions (
1655 		action_group, http_entries,
1656 		G_N_ELEMENTS (http_entries), web_view);
1657 
1658 	action_group = gtk_action_group_new ("mailto");
1659 	gtk_action_group_set_translation_domain (action_group, domain);
1660 	gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
1661 	g_object_unref (action_group);
1662 
1663 	gtk_action_group_add_actions (
1664 		action_group, mailto_entries,
1665 		G_N_ELEMENTS (mailto_entries), web_view);
1666 
1667 	action_group = gtk_action_group_new ("image");
1668 	gtk_action_group_set_translation_domain (action_group, domain);
1669 	gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
1670 	g_object_unref (action_group);
1671 
1672 	gtk_action_group_add_actions (
1673 		action_group, image_entries,
1674 		G_N_ELEMENTS (image_entries), web_view);
1675 
1676 	action_group = gtk_action_group_new ("selection");
1677 	gtk_action_group_set_translation_domain (action_group, domain);
1678 	gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
1679 	g_object_unref (action_group);
1680 
1681 	gtk_action_group_add_actions (
1682 		action_group, selection_entries,
1683 		G_N_ELEMENTS (selection_entries), web_view);
1684 
1685 	action_group = gtk_action_group_new ("standard");
1686 	gtk_action_group_set_translation_domain (action_group, domain);
1687 	gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
1688 	g_object_unref (action_group);
1689 
1690 	gtk_action_group_add_actions (
1691 		action_group, standard_entries,
1692 		G_N_ELEMENTS (standard_entries), web_view);
1693 
1694 	popup_action = e_popup_action_new ("open");
1695 	gtk_action_group_add_action (action_group, GTK_ACTION (popup_action));
1696 	g_object_unref (popup_action);
1697 
1698 	g_object_bind_property (
1699 		web_view, "open-proxy",
1700 		popup_action, "related-action",
1701 		G_BINDING_BIDIRECTIONAL |
1702 		G_BINDING_SYNC_CREATE);
1703 
1704 	/* Support lockdown. */
1705 
1706 	action_group = gtk_action_group_new ("lockdown-printing");
1707 	gtk_action_group_set_translation_domain (action_group, domain);
1708 	gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
1709 	g_object_unref (action_group);
1710 
1711 	popup_action = e_popup_action_new ("print");
1712 	gtk_action_group_add_action (action_group, GTK_ACTION (popup_action));
1713 	g_object_unref (popup_action);
1714 
1715 	g_object_bind_property (
1716 		web_view, "print-proxy",
1717 		popup_action, "related-action",
1718 		G_BINDING_BIDIRECTIONAL |
1719 		G_BINDING_SYNC_CREATE);
1720 
1721 	action_group = gtk_action_group_new ("lockdown-save-to-disk");
1722 	gtk_action_group_set_translation_domain (action_group, domain);
1723 	gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
1724 	g_object_unref (action_group);
1725 
1726 	popup_action = e_popup_action_new ("save-as");
1727 	gtk_action_group_add_action (action_group, GTK_ACTION (popup_action));
1728 	g_object_unref (popup_action);
1729 
1730 	g_object_bind_property (
1731 		web_view, "save-as-proxy",
1732 		popup_action, "related-action",
1733 		G_BINDING_BIDIRECTIONAL |
1734 		G_BINDING_SYNC_CREATE);
1735 
1736 	/* Because we are loading from a hard-coded string, there is
1737 	 * no chance of I/O errors.  Failure here implies a malformed
1738 	 * UI definition.  Full stop. */
1739 	gtk_ui_manager_add_ui_from_string (ui_manager, ui, -1, &error);
1740 	if (error != NULL)
1741 		g_error ("%s", error->message);
1742 
1743 	id = "org.gnome.evolution.webview";
1744 	e_plugin_ui_register_manager (ui_manager, id, web_view);
1745 	e_plugin_ui_enable_manager (ui_manager, id);
1746 
1747 	e_extensible_load_extensions (E_EXTENSIBLE (web_view));
1748 }
1749 
1750 GtkWidget *
1751 e_web_view_gtkhtml_new (void)
1752 {
1753 	return g_object_new (E_TYPE_WEB_VIEW_GTKHTML, NULL);
1754 }
1755 
1756 void
1757 e_web_view_gtkhtml_clear (EWebViewGtkHTML *web_view)
1758 {
1759 	g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
1760 
1761 	gtk_html_load_empty (GTK_HTML (web_view));
1762 }
1763 
1764 void
1765 e_web_view_gtkhtml_load_string (EWebViewGtkHTML *web_view,
1766                         const gchar *string)
1767 {
1768 	EWebViewGtkHTMLClass *class;
1769 
1770 	g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
1771 
1772 	class = E_WEB_VIEW_GTKHTML_GET_CLASS (web_view);
1773 	g_return_if_fail (class->load_string != NULL);
1774 
1775 	class->load_string (web_view, string);
1776 }
1777 
1778 gboolean
1779 e_web_view_gtkhtml_get_animate (EWebViewGtkHTML *web_view)
1780 {
1781 	/* XXX This is just here to maintain symmetry
1782 	 *     with e_web_view_set_animate(). */
1783 
1784 	g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), FALSE);
1785 
1786 	return gtk_html_get_animate (GTK_HTML (web_view));
1787 }
1788 
1789 void
1790 e_web_view_gtkhtml_set_animate (EWebViewGtkHTML *web_view,
1791                         gboolean animate)
1792 {
1793 	/* XXX GtkHTML does not utilize GObject properties as well
1794 	 *     as it could.  This just wraps gtk_html_set_animate()
1795 	 *     so we can get a "notify::animate" signal. */
1796 
1797 	g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
1798 
1799 	gtk_html_set_animate (GTK_HTML (web_view), animate);
1800 
1801 	g_object_notify (G_OBJECT (web_view), "animate");
1802 }
1803 
1804 gboolean
1805 e_web_view_gtkhtml_get_caret_mode (EWebViewGtkHTML *web_view)
1806 {
1807 	/* XXX This is just here to maintain symmetry
1808 	 *     with e_web_view_set_caret_mode(). */
1809 
1810 	g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), FALSE);
1811 
1812 	return gtk_html_get_caret_mode (GTK_HTML (web_view));
1813 }
1814 
1815 void
1816 e_web_view_gtkhtml_set_caret_mode (EWebViewGtkHTML *web_view,
1817                            gboolean caret_mode)
1818 {
1819 	/* XXX GtkHTML does not utilize GObject properties as well
1820 	 *     as it could.  This just wraps gtk_html_set_caret_mode()
1821 	 *     so we can get a "notify::caret-mode" signal. */
1822 
1823 	g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
1824 
1825 	gtk_html_set_caret_mode (GTK_HTML (web_view), caret_mode);
1826 
1827 	g_object_notify (G_OBJECT (web_view), "caret-mode");
1828 }
1829 
1830 GtkTargetList *
1831 e_web_view_gtkhtml_get_copy_target_list (EWebViewGtkHTML *web_view)
1832 {
1833 	g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), NULL);
1834 
1835 	return web_view->priv->copy_target_list;
1836 }
1837 
1838 gboolean
1839 e_web_view_gtkhtml_get_disable_printing (EWebViewGtkHTML *web_view)
1840 {
1841 	g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), FALSE);
1842 
1843 	return web_view->priv->disable_printing;
1844 }
1845 
1846 void
1847 e_web_view_gtkhtml_set_disable_printing (EWebViewGtkHTML *web_view,
1848                                  gboolean disable_printing)
1849 {
1850 	g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
1851 
1852 	web_view->priv->disable_printing = disable_printing;
1853 
1854 	g_object_notify (G_OBJECT (web_view), "disable-printing");
1855 }
1856 
1857 gboolean
1858 e_web_view_gtkhtml_get_disable_save_to_disk (EWebViewGtkHTML *web_view)
1859 {
1860 	g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), FALSE);
1861 
1862 	return web_view->priv->disable_save_to_disk;
1863 }
1864 
1865 void
1866 e_web_view_gtkhtml_set_disable_save_to_disk (EWebViewGtkHTML *web_view,
1867                                      gboolean disable_save_to_disk)
1868 {
1869 	g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
1870 
1871 	web_view->priv->disable_save_to_disk = disable_save_to_disk;
1872 
1873 	g_object_notify (G_OBJECT (web_view), "disable-save-to-disk");
1874 }
1875 
1876 gboolean
1877 e_web_view_gtkhtml_get_editable (EWebViewGtkHTML *web_view)
1878 {
1879 	/* XXX This is just here to maintain symmetry
1880 	 *     with e_web_view_set_editable(). */
1881 
1882 	g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), FALSE);
1883 
1884 	return gtk_html_get_editable (GTK_HTML (web_view));
1885 }
1886 
1887 void
1888 e_web_view_gtkhtml_set_editable (EWebViewGtkHTML *web_view,
1889                          gboolean editable)
1890 {
1891 	/* XXX GtkHTML does not utilize GObject properties as well
1892 	 *     as it could.  This just wraps gtk_html_set_editable()
1893 	 *     so we can get a "notify::editable" signal. */
1894 
1895 	g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
1896 
1897 	gtk_html_set_editable (GTK_HTML (web_view), editable);
1898 
1899 	g_object_notify (G_OBJECT (web_view), "editable");
1900 }
1901 
1902 gboolean
1903 e_web_view_gtkhtml_get_inline_spelling (EWebViewGtkHTML *web_view)
1904 {
1905 	/* XXX This is just here to maintain symmetry
1906 	 *     with e_web_view_set_inline_spelling(). */
1907 
1908 	g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), FALSE);
1909 
1910 	return gtk_html_get_inline_spelling (GTK_HTML (web_view));
1911 }
1912 
1913 void
1914 e_web_view_gtkhtml_set_inline_spelling (EWebViewGtkHTML *web_view,
1915                                 gboolean inline_spelling)
1916 {
1917 	/* XXX GtkHTML does not utilize GObject properties as well
1918 	 *     as it could.  This just wraps gtk_html_set_inline_spelling()
1919 	 *     so we get a "notify::inline-spelling" signal. */
1920 
1921 	g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
1922 
1923 	gtk_html_set_inline_spelling (GTK_HTML (web_view), inline_spelling);
1924 
1925 	g_object_notify (G_OBJECT (web_view), "inline-spelling");
1926 }
1927 
1928 gboolean
1929 e_web_view_gtkhtml_get_magic_links (EWebViewGtkHTML *web_view)
1930 {
1931 	/* XXX This is just here to maintain symmetry
1932 	 *     with e_web_view_set_magic_links(). */
1933 
1934 	g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), FALSE);
1935 
1936 	return gtk_html_get_magic_links (GTK_HTML (web_view));
1937 }
1938 
1939 void
1940 e_web_view_gtkhtml_set_magic_links (EWebViewGtkHTML *web_view,
1941                             gboolean magic_links)
1942 {
1943 	/* XXX GtkHTML does not utilize GObject properties as well
1944 	 *     as it could.  This just wraps gtk_html_set_magic_links()
1945 	 *     so we can get a "notify::magic-links" signal. */
1946 
1947 	g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
1948 
1949 	gtk_html_set_magic_links (GTK_HTML (web_view), magic_links);
1950 
1951 	g_object_notify (G_OBJECT (web_view), "magic-links");
1952 }
1953 
1954 gboolean
1955 e_web_view_gtkhtml_get_magic_smileys (EWebViewGtkHTML *web_view)
1956 {
1957 	/* XXX This is just here to maintain symmetry
1958 	 *     with e_web_view_set_magic_smileys(). */
1959 
1960 	g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), FALSE);
1961 
1962 	return gtk_html_get_magic_smileys (GTK_HTML (web_view));
1963 }
1964 
1965 void
1966 e_web_view_gtkhtml_set_magic_smileys (EWebViewGtkHTML *web_view,
1967                               gboolean magic_smileys)
1968 {
1969 	/* XXX GtkHTML does not utilize GObject properties as well
1970 	 *     as it could.  This just wraps gtk_html_set_magic_smileys()
1971 	 *     so we can get a "notify::magic-smileys" signal. */
1972 
1973 	g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
1974 
1975 	gtk_html_set_magic_smileys (GTK_HTML (web_view), magic_smileys);
1976 
1977 	g_object_notify (G_OBJECT (web_view), "magic-smileys");
1978 }
1979 
1980 const gchar *
1981 e_web_view_gtkhtml_get_selected_uri (EWebViewGtkHTML *web_view)
1982 {
1983 	g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), NULL);
1984 
1985 	return web_view->priv->selected_uri;
1986 }
1987 
1988 void
1989 e_web_view_gtkhtml_set_selected_uri (EWebViewGtkHTML *web_view,
1990                              const gchar *selected_uri)
1991 {
1992 	g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
1993 
1994 	g_free (web_view->priv->selected_uri);
1995 	web_view->priv->selected_uri = g_strdup (selected_uri);
1996 
1997 	g_object_notify (G_OBJECT (web_view), "selected-uri");
1998 }
1999 
2000 GdkPixbufAnimation *
2001 e_web_view_gtkhtml_get_cursor_image (EWebViewGtkHTML *web_view)
2002 {
2003 	g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), NULL);
2004 
2005 	return web_view->priv->cursor_image;
2006 }
2007 
2008 void
2009 e_web_view_gtkhtml_set_cursor_image (EWebViewGtkHTML *web_view,
2010                              GdkPixbufAnimation *image)
2011 {
2012 	g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
2013 
2014 	if (image != NULL)
2015 		g_object_ref (image);
2016 
2017 	if (web_view->priv->cursor_image != NULL)
2018 		g_object_unref (web_view->priv->cursor_image);
2019 
2020 	web_view->priv->cursor_image = image;
2021 
2022 	g_object_notify (G_OBJECT (web_view), "cursor-image");
2023 }
2024 
2025 GtkAction *
2026 e_web_view_gtkhtml_get_open_proxy (EWebViewGtkHTML *web_view)
2027 {
2028 	g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), FALSE);
2029 
2030 	return web_view->priv->open_proxy;
2031 }
2032 
2033 void
2034 e_web_view_gtkhtml_set_open_proxy (EWebViewGtkHTML *web_view,
2035                            GtkAction *open_proxy)
2036 {
2037 	g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
2038 
2039 	if (open_proxy != NULL) {
2040 		g_return_if_fail (GTK_IS_ACTION (open_proxy));
2041 		g_object_ref (open_proxy);
2042 	}
2043 
2044 	if (web_view->priv->open_proxy != NULL)
2045 		g_object_unref (web_view->priv->open_proxy);
2046 
2047 	web_view->priv->open_proxy = open_proxy;
2048 
2049 	g_object_notify (G_OBJECT (web_view), "open-proxy");
2050 }
2051 
2052 GtkTargetList *
2053 e_web_view_gtkhtml_get_paste_target_list (EWebViewGtkHTML *web_view)
2054 {
2055 	g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), NULL);
2056 
2057 	return web_view->priv->paste_target_list;
2058 }
2059 
2060 GtkAction *
2061 e_web_view_gtkhtml_get_print_proxy (EWebViewGtkHTML *web_view)
2062 {
2063 	g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), FALSE);
2064 
2065 	return web_view->priv->print_proxy;
2066 }
2067 
2068 void
2069 e_web_view_gtkhtml_set_print_proxy (EWebViewGtkHTML *web_view,
2070                             GtkAction *print_proxy)
2071 {
2072 	g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
2073 
2074 	if (print_proxy != NULL) {
2075 		g_return_if_fail (GTK_IS_ACTION (print_proxy));
2076 		g_object_ref (print_proxy);
2077 	}
2078 
2079 	if (web_view->priv->print_proxy != NULL)
2080 		g_object_unref (web_view->priv->print_proxy);
2081 
2082 	web_view->priv->print_proxy = print_proxy;
2083 
2084 	g_object_notify (G_OBJECT (web_view), "print-proxy");
2085 }
2086 
2087 GtkAction *
2088 e_web_view_gtkhtml_get_save_as_proxy (EWebViewGtkHTML *web_view)
2089 {
2090 	g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), FALSE);
2091 
2092 	return web_view->priv->save_as_proxy;
2093 }
2094 
2095 void
2096 e_web_view_gtkhtml_set_save_as_proxy (EWebViewGtkHTML *web_view,
2097                               GtkAction *save_as_proxy)
2098 {
2099 	g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
2100 
2101 	if (save_as_proxy != NULL) {
2102 		g_return_if_fail (GTK_IS_ACTION (save_as_proxy));
2103 		g_object_ref (save_as_proxy);
2104 	}
2105 
2106 	if (web_view->priv->save_as_proxy != NULL)
2107 		g_object_unref (web_view->priv->save_as_proxy);
2108 
2109 	web_view->priv->save_as_proxy = save_as_proxy;
2110 
2111 	g_object_notify (G_OBJECT (web_view), "save-as-proxy");
2112 }
2113 
2114 GtkAction *
2115 e_web_view_gtkhtml_get_action (EWebViewGtkHTML *web_view,
2116                        const gchar *action_name)
2117 {
2118 	GtkUIManager *ui_manager;
2119 
2120 	g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), NULL);
2121 	g_return_val_if_fail (action_name != NULL, NULL);
2122 
2123 	ui_manager = e_web_view_gtkhtml_get_ui_manager (web_view);
2124 
2125 	return e_lookup_action (ui_manager, action_name);
2126 }
2127 
2128 GtkActionGroup *
2129 e_web_view_gtkhtml_get_action_group (EWebViewGtkHTML *web_view,
2130                              const gchar *group_name)
2131 {
2132 	GtkUIManager *ui_manager;
2133 
2134 	g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), NULL);
2135 	g_return_val_if_fail (group_name != NULL, NULL);
2136 
2137 	ui_manager = e_web_view_gtkhtml_get_ui_manager (web_view);
2138 
2139 	return e_lookup_action_group (ui_manager, group_name);
2140 }
2141 
2142 gchar *
2143 e_web_view_gtkhtml_extract_uri (EWebViewGtkHTML *web_view,
2144                         GdkEventButton *event,
2145                         GtkHTML *frame)
2146 {
2147 	EWebViewGtkHTMLClass *class;
2148 
2149 	g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), NULL);
2150 
2151 	if (frame == NULL)
2152 		frame = GTK_HTML (web_view);
2153 
2154 	class = E_WEB_VIEW_GTKHTML_GET_CLASS (web_view);
2155 	g_return_val_if_fail (class->extract_uri != NULL, NULL);
2156 
2157 	return class->extract_uri (web_view, event, frame);
2158 }
2159 
2160 void
2161 e_web_view_gtkhtml_copy_clipboard (EWebViewGtkHTML *web_view)
2162 {
2163 	g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
2164 
2165 	g_signal_emit (web_view, signals[COPY_CLIPBOARD], 0);
2166 }
2167 
2168 void
2169 e_web_view_gtkhtml_cut_clipboard (EWebViewGtkHTML *web_view)
2170 {
2171 	g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
2172 
2173 	g_signal_emit (web_view, signals[CUT_CLIPBOARD], 0);
2174 }
2175 
2176 gboolean
2177 e_web_view_gtkhtml_is_selection_active (EWebViewGtkHTML *web_view)
2178 {
2179 	g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), FALSE);
2180 
2181 	return gtk_html_command (GTK_HTML (web_view), "is-selection-active");
2182 }
2183 
2184 void
2185 e_web_view_gtkhtml_paste_clipboard (EWebViewGtkHTML *web_view)
2186 {
2187 	g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
2188 
2189 	g_signal_emit (web_view, signals[PASTE_CLIPBOARD], 0);
2190 }
2191 
2192 gboolean
2193 e_web_view_gtkhtml_scroll_forward (EWebViewGtkHTML *web_view)
2194 {
2195 	g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), FALSE);
2196 
2197 	return gtk_html_command (GTK_HTML (web_view), "scroll-forward");
2198 }
2199 
2200 gboolean
2201 e_web_view_gtkhtml_scroll_backward (EWebViewGtkHTML *web_view)
2202 {
2203 	g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), FALSE);
2204 
2205 	return gtk_html_command (GTK_HTML (web_view), "scroll-backward");
2206 }
2207 
2208 void
2209 e_web_view_gtkhtml_select_all (EWebViewGtkHTML *web_view)
2210 {
2211 	g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
2212 
2213 	gtk_html_command (GTK_HTML (web_view), "select-all");
2214 }
2215 
2216 void
2217 e_web_view_gtkhtml_unselect_all (EWebViewGtkHTML *web_view)
2218 {
2219 	g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
2220 
2221 	gtk_html_command (GTK_HTML (web_view), "unselect-all");
2222 }
2223 
2224 void
2225 e_web_view_gtkhtml_zoom_100 (EWebViewGtkHTML *web_view)
2226 {
2227 	g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
2228 
2229 	gtk_html_command (GTK_HTML (web_view), "zoom-reset");
2230 }
2231 
2232 void
2233 e_web_view_gtkhtml_zoom_in (EWebViewGtkHTML *web_view)
2234 {
2235 	g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
2236 
2237 	gtk_html_command (GTK_HTML (web_view), "zoom-in");
2238 }
2239 
2240 void
2241 e_web_view_gtkhtml_zoom_out (EWebViewGtkHTML *web_view)
2242 {
2243 	g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
2244 
2245 	gtk_html_command (GTK_HTML (web_view), "zoom-out");
2246 }
2247 
2248 GtkUIManager *
2249 e_web_view_gtkhtml_get_ui_manager (EWebViewGtkHTML *web_view)
2250 {
2251 	g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), NULL);
2252 
2253 	return web_view->priv->ui_manager;
2254 }
2255 
2256 GtkWidget *
2257 e_web_view_gtkhtml_get_popup_menu (EWebViewGtkHTML *web_view)
2258 {
2259 	GtkUIManager *ui_manager;
2260 	GtkWidget *menu;
2261 
2262 	g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), NULL);
2263 
2264 	ui_manager = e_web_view_gtkhtml_get_ui_manager (web_view);
2265 	menu = gtk_ui_manager_get_widget (ui_manager, "/context");
2266 	g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
2267 
2268 	return menu;
2269 }
2270 
2271 void
2272 e_web_view_gtkhtml_show_popup_menu (EWebViewGtkHTML *web_view,
2273                             GdkEventButton *event,
2274                             GtkMenuPositionFunc func,
2275                             gpointer user_data)
2276 {
2277 	GtkWidget *menu;
2278 
2279 	g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
2280 
2281 	e_web_view_gtkhtml_update_actions (web_view);
2282 
2283 	menu = e_web_view_gtkhtml_get_popup_menu (web_view);
2284 
2285 	if (event != NULL)
2286 		gtk_menu_popup (
2287 			GTK_MENU (menu), NULL, NULL, func,
2288 			user_data, event->button, event->time);
2289 	else
2290 		gtk_menu_popup (
2291 			GTK_MENU (menu), NULL, NULL, func,
2292 			user_data, 0, gtk_get_current_event_time ());
2293 }
2294 
2295 void
2296 e_web_view_gtkhtml_status_message (EWebViewGtkHTML *web_view,
2297                            const gchar *status_message)
2298 {
2299 	g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
2300 
2301 	g_signal_emit (web_view, signals[STATUS_MESSAGE], 0, status_message);
2302 }
2303 
2304 void
2305 e_web_view_gtkhtml_stop_loading (EWebViewGtkHTML *web_view)
2306 {
2307 	g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
2308 
2309 	g_signal_emit (web_view, signals[STOP_LOADING], 0);
2310 }
2311 
2312 void
2313 e_web_view_gtkhtml_update_actions (EWebViewGtkHTML *web_view)
2314 {
2315 	g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
2316 
2317 	g_signal_emit (web_view, signals[UPDATE_ACTIONS], 0);
2318 }