1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*  This file is part of the GtkHTML library.
3  *
4  *  Copyright 1999, 2000 Helix Code, Inc.
5  *
6  *  This library is free software; you can redistribute it and/or
7  *  modify it under the terms of the GNU Library General Public
8  *  License as published by the Free Software Foundation; either
9  *  version 2 of the License, or (at your option) any later version.
10  *
11  *  This library is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  *  Library General Public License for more details.
15  *
16  *  You should have received a copy of the GNU Library General Public License
17  *  along with this library; see the file COPYING.LIB.  If not, write to
18  *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  *  Boston, MA 02110-1301, USA.
20 */
21 
22 #include <config.h>
23 #include <ctype.h>
24 
25 #include <gdk/gdkkeysyms.h>
26 #include <glib/gi18n-lib.h>
27 #include <string.h>
28 
29 #include "../a11y/object.h"
30 
31 #include "htmlcolorset.h"
32 #include "htmlcluev.h"
33 #include "htmlcursor.h"
34 #include "htmldrawqueue.h"
35 #include "htmlengine-edit.h"
36 #include "htmlengine-edit-clueflowstyle.h"
37 #include "htmlengine-edit-cut-and-paste.h"
38 #include "htmlengine-edit-fontstyle.h"
39 #include "htmlengine-edit-rule.h"
40 #include "htmlengine-edit-movement.h"
41 #include "htmlengine-edit-cursor.h"
42 #include "htmlengine-edit-table.h"
43 #include "htmlengine-edit-tablecell.h"
44 #include "htmlengine-edit-text.h"
45 #include "htmlengine-edit-selection-updater.h"
46 #include "htmlengine-print.h"
47 #include "htmlengine-save.h"
48 #include "htmlform.h"
49 #include "htmlframe.h"
50 #include "htmlframeset.h"
51 #include "htmliframe.h"
52 #include "htmlimage.h"
53 #include "htmlinterval.h"
54 #include "htmlmarshal.h"
55 #include "htmlplainpainter.h"
56 #include "htmlsettings.h"
57 #include "htmltable.h"
58 #include "htmltext.h"
59 #include "htmltextslave.h"
60 #include "htmlselection.h"
61 #include "htmlundo.h"
62 
63 #include "gtkhtml.h"
64 #include "gtkhtml-embedded.h"
65 #include "gtkhtml-keybinding.h"
66 #include "gtkhtml-search.h"
67 #include "gtkhtml-stream.h"
68 #include "gtkhtml-private.h"
69 #include "gtkhtml-properties.h"
70 #include "math.h"
71 
72 enum DndTargetType {
73 	DND_TARGET_TYPE_TEXT_URI_LIST,
74 	DND_TARGET_TYPE_MOZILLA_URL,
75 	DND_TARGET_TYPE_TEXT_HTML,
76 	DND_TARGET_TYPE_UTF8_STRING,
77 	DND_TARGET_TYPE_TEXT_PLAIN,
78 	DND_TARGET_TYPE_STRING
79 };
80 
81 static GtkTargetEntry drag_dest_targets[] = {
82 	{ (gchar *) "text/uri-list", 0, DND_TARGET_TYPE_TEXT_URI_LIST },
83 	{ (gchar *) "_NETSCAPE_URL", 0, DND_TARGET_TYPE_MOZILLA_URL },
84 	{ (gchar *) "text/html", 0, DND_TARGET_TYPE_TEXT_HTML },
85 	{ (gchar *) "UTF8_STRING", 0, DND_TARGET_TYPE_UTF8_STRING },
86 	{ (gchar *) "text/plain", 0, DND_TARGET_TYPE_TEXT_PLAIN },
87 	{ (gchar *) "STRING", 0, DND_TARGET_TYPE_STRING },
88 };
89 
90 static GtkTargetEntry drag_source_targets[] = {
91 	{ (gchar *) "text/uri-list", 0, DND_TARGET_TYPE_TEXT_URI_LIST },
92 	{ (gchar *) "_NETSCAPE_URL", 0, DND_TARGET_TYPE_MOZILLA_URL },
93 	{ (gchar *) "text/html", 0, DND_TARGET_TYPE_TEXT_HTML },
94 	{ (gchar *) "UTF8_STRING", 0, DND_TARGET_TYPE_UTF8_STRING },
95 	{ (gchar *) "text/plain", 0, DND_TARGET_TYPE_TEXT_PLAIN },
96 	{ (gchar *) "STRING", 0, DND_TARGET_TYPE_STRING },
97 };
98 
99 enum _TargetInfo {
100 	TARGET_HTML,
101 	TARGET_UTF8_STRING,
102 	TARGET_COMPOUND_TEXT,
103 	TARGET_STRING,
104 	TARGET_TEXT
105 };
106 
107 typedef enum _TargetInfo TargetInfo;
108 
109 static const GtkTargetEntry selection_targets[] = {
110 	{ (gchar *) "text/html", GTK_TARGET_SAME_APP, TARGET_HTML },
111 	{ (gchar *) "UTF8_STRING", 0, TARGET_UTF8_STRING },
112 	{ (gchar *) "COMPOUND_TEXT", 0, TARGET_COMPOUND_TEXT },
113 	{ (gchar *) "STRING", 0, TARGET_STRING },
114 	{ (gchar *) "TEXT", 0, TARGET_TEXT }
115 };
116 
117 static const guint n_selection_targets = G_N_ELEMENTS (selection_targets);
118 
119 typedef struct _ClipboardContents ClipboardContents;
120 
121 struct _ClipboardContents {
122 	gchar *html_text;
123 	gchar *plain_text;
124 };
125 
126 #define d_s(x)
127 #define D_IM(x)
128 
129 static GtkLayoutClass *parent_class = NULL;
130 
131 enum {
132 	TITLE_CHANGED,
133 	URL_REQUESTED,
134 	LOAD_DONE,
135 	LINK_CLICKED,
136 	SET_BASE,
137 	SET_BASE_TARGET,
138 	ON_URL,
139 	REDIRECT,
140 	SUBMIT,
141 	OBJECT_REQUESTED,
142 	CURRENT_PARAGRAPH_STYLE_CHANGED,
143 	CURRENT_PARAGRAPH_INDENTATION_CHANGED,
144 	CURRENT_PARAGRAPH_ALIGNMENT_CHANGED,
145 	INSERTION_FONT_STYLE_CHANGED,
146 	INSERTION_COLOR_CHANGED,
147 	SIZE_CHANGED,
148 	IFRAME_CREATED,
149 	/* keybindings signals */
150 	SCROLL,
151 	CURSOR_MOVE,
152 	COMMAND,
153 	CURSOR_CHANGED,
154 	OBJECT_INSERTED,
155 	OBJECT_DELETE,
156 	/* now only last signal */
157 	LAST_SIGNAL
158 };
159 
160 /* #define USE_PROPS */
161 #ifdef USE_PROPS
162 enum {
163 	PROP_0,
164 	PROP_EDITABLE,
165 	PROP_TITLE,
166 	PROP_DOCUMENT_BASE,
167 	PROP_TARGET_BASE,
168 };
169 
170 static void     gtk_html_get_property  (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
171 static void     gtk_html_set_property  (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
172 #endif
173 
174 static guint signals[LAST_SIGNAL] = { 0 };
175 
176 static void
177 gtk_html_update_scrollbars_on_resize (GtkHTML *html,
178                                       gdouble old_doc_width,
179                                       gdouble old_doc_height,
180                                       gdouble old_width,
181                                       gdouble old_height,
182                                       gboolean *changed_x,
183                                       gboolean *changed_y);
184 
185 static void update_primary_selection (GtkHTML *html);
186 static void clipboard_paste_received_cb (GtkClipboard *clipboard,
187                                          GtkSelectionData *selection_data,
188                                          gpointer user_data);
189 static gboolean motion_notify_event (GtkWidget *widget,
190                                       GdkEventMotion *event);
191 
192 /* keybindings signal hadlers */
193 static void scroll (GtkHTML *html,
194                                       GtkOrientation orientation,
195                                       GtkScrollType scroll_type,
196                                       gfloat position);
197 static void cursor_move (GtkHTML *html,
198                                       GtkDirectionType dir_type,
199                                       GtkHTMLCursorSkipType skip);
200 static gboolean command (GtkHTML *html,
201                                       GtkHTMLCommandType com_type);
202 static gint mouse_change_pos (GtkWidget *widget,
203                                       GdkWindow *window,
204                                       gint x,
205                                       gint y,
206                                       gint state);
207 static void add_bindings (GtkHTMLClass *klass);
208 static const gchar *get_value_nick (GtkHTMLCommandType com_type);
209 static void gtk_html_adjust_cursor_position (GtkHTML *html);
210 static gboolean any_has_cursor_moved (GtkHTML *html);
211 static gboolean any_has_skip_update_cursor (GtkHTML *html);
212 
213 /* Interval for scrolling during selection.  */
214 #define SCROLL_TIMEOUT_INTERVAL 10
215 
216 
217 GtkHTMLParagraphStyle
clueflow_style_to_paragraph_style(HTMLClueFlowStyle style,HTMLListType item_type)218 clueflow_style_to_paragraph_style (HTMLClueFlowStyle style,
219                                    HTMLListType item_type)
220 {
221 	switch (style) {
222 	case HTML_CLUEFLOW_STYLE_NORMAL:
223 		return GTK_HTML_PARAGRAPH_STYLE_NORMAL;
224 	case HTML_CLUEFLOW_STYLE_H1:
225 		return GTK_HTML_PARAGRAPH_STYLE_H1;
226 	case HTML_CLUEFLOW_STYLE_H2:
227 		return GTK_HTML_PARAGRAPH_STYLE_H2;
228 	case HTML_CLUEFLOW_STYLE_H3:
229 		return GTK_HTML_PARAGRAPH_STYLE_H3;
230 	case HTML_CLUEFLOW_STYLE_H4:
231 		return GTK_HTML_PARAGRAPH_STYLE_H4;
232 	case HTML_CLUEFLOW_STYLE_H5:
233 		return GTK_HTML_PARAGRAPH_STYLE_H5;
234 	case HTML_CLUEFLOW_STYLE_H6:
235 		return GTK_HTML_PARAGRAPH_STYLE_H6;
236 	case HTML_CLUEFLOW_STYLE_ADDRESS:
237 		return GTK_HTML_PARAGRAPH_STYLE_ADDRESS;
238 	case HTML_CLUEFLOW_STYLE_PRE:
239 		return GTK_HTML_PARAGRAPH_STYLE_PRE;
240 	case HTML_CLUEFLOW_STYLE_LIST_ITEM:
241 		switch (item_type) {
242 		case HTML_LIST_TYPE_UNORDERED:
243 			return GTK_HTML_PARAGRAPH_STYLE_ITEMDOTTED;
244 		case HTML_LIST_TYPE_ORDERED_ARABIC:
245 			return GTK_HTML_PARAGRAPH_STYLE_ITEMDIGIT;
246 		case HTML_LIST_TYPE_ORDERED_LOWER_ROMAN:
247 		case HTML_LIST_TYPE_ORDERED_UPPER_ROMAN:
248 			return GTK_HTML_PARAGRAPH_STYLE_ITEMROMAN;
249 		case HTML_LIST_TYPE_ORDERED_LOWER_ALPHA:
250 		case HTML_LIST_TYPE_ORDERED_UPPER_ALPHA:
251 			return GTK_HTML_PARAGRAPH_STYLE_ITEMALPHA;
252 		default:
253 			return GTK_HTML_PARAGRAPH_STYLE_ITEMDOTTED;
254 		}
255 	default:		/* This should not really happen, though.  */
256 		return GTK_HTML_PARAGRAPH_STYLE_NORMAL;
257 	}
258 }
259 
260 void
paragraph_style_to_clueflow_style(GtkHTMLParagraphStyle style,HTMLClueFlowStyle * flow_style,HTMLListType * item_type)261 paragraph_style_to_clueflow_style (GtkHTMLParagraphStyle style,
262                                    HTMLClueFlowStyle *flow_style,
263                                    HTMLListType *item_type)
264 {
265 	*item_type = HTML_LIST_TYPE_BLOCKQUOTE;
266 	*flow_style = HTML_CLUEFLOW_STYLE_LIST_ITEM;
267 
268 	switch (style) {
269 	case GTK_HTML_PARAGRAPH_STYLE_NORMAL:
270 		*flow_style = HTML_CLUEFLOW_STYLE_NORMAL;
271 		break;
272 	case GTK_HTML_PARAGRAPH_STYLE_H1:
273 		*flow_style = HTML_CLUEFLOW_STYLE_H1;
274 		break;
275 	case GTK_HTML_PARAGRAPH_STYLE_H2:
276 		*flow_style = HTML_CLUEFLOW_STYLE_H2;
277 		break;
278 	case GTK_HTML_PARAGRAPH_STYLE_H3:
279 		*flow_style = HTML_CLUEFLOW_STYLE_H3;
280 		break;
281 	case GTK_HTML_PARAGRAPH_STYLE_H4:
282 		*flow_style = HTML_CLUEFLOW_STYLE_H4;
283 		break;
284 	case GTK_HTML_PARAGRAPH_STYLE_H5:
285 		*flow_style = HTML_CLUEFLOW_STYLE_H5;
286 		break;
287 	case GTK_HTML_PARAGRAPH_STYLE_H6:
288 		*flow_style = HTML_CLUEFLOW_STYLE_H6;
289 		break;
290 	case GTK_HTML_PARAGRAPH_STYLE_ADDRESS:
291 		*flow_style = HTML_CLUEFLOW_STYLE_ADDRESS;
292 		break;
293 	case GTK_HTML_PARAGRAPH_STYLE_PRE:
294 		*flow_style = HTML_CLUEFLOW_STYLE_PRE;
295 		break;
296 	case GTK_HTML_PARAGRAPH_STYLE_ITEMDOTTED:
297 		*item_type = HTML_LIST_TYPE_UNORDERED;
298 		break;
299 	case GTK_HTML_PARAGRAPH_STYLE_ITEMROMAN:
300 		*item_type = HTML_LIST_TYPE_ORDERED_UPPER_ROMAN;
301 		break;
302 	case GTK_HTML_PARAGRAPH_STYLE_ITEMALPHA:
303 		*item_type = HTML_LIST_TYPE_ORDERED_UPPER_ALPHA;
304 		break;
305 	case GTK_HTML_PARAGRAPH_STYLE_ITEMDIGIT:
306 		*item_type = HTML_LIST_TYPE_ORDERED_ARABIC;
307 		break;
308 	default:		/* This should not really happen, though.  */
309 		*flow_style = HTML_CLUEFLOW_STYLE_NORMAL;
310 	}
311 }
312 
313 HTMLHAlignType
paragraph_alignment_to_html(GtkHTMLParagraphAlignment alignment)314 paragraph_alignment_to_html (GtkHTMLParagraphAlignment alignment)
315 {
316 	switch (alignment) {
317 	case GTK_HTML_PARAGRAPH_ALIGNMENT_LEFT:
318 		return HTML_HALIGN_LEFT;
319 	case GTK_HTML_PARAGRAPH_ALIGNMENT_RIGHT:
320 		return HTML_HALIGN_RIGHT;
321 	case GTK_HTML_PARAGRAPH_ALIGNMENT_CENTER:
322 		return HTML_HALIGN_CENTER;
323 	default:
324 		return HTML_HALIGN_LEFT;
325 	}
326 }
327 
328 GtkHTMLParagraphAlignment
html_alignment_to_paragraph(HTMLHAlignType alignment)329 html_alignment_to_paragraph (HTMLHAlignType alignment)
330 {
331 	switch (alignment) {
332 	case HTML_HALIGN_LEFT:
333 		return GTK_HTML_PARAGRAPH_ALIGNMENT_LEFT;
334 	case HTML_HALIGN_CENTER:
335 		return GTK_HTML_PARAGRAPH_ALIGNMENT_CENTER;
336 	case HTML_HALIGN_RIGHT:
337 		return GTK_HTML_PARAGRAPH_ALIGNMENT_RIGHT;
338 	default:
339 		return GTK_HTML_PARAGRAPH_ALIGNMENT_LEFT;
340 	}
341 }
342 
343 void
gtk_html_update_styles(GtkHTML * html)344 gtk_html_update_styles (GtkHTML *html)
345 {
346 	GtkHTMLParagraphStyle paragraph_style;
347 	GtkHTMLParagraphAlignment alignment;
348 	HTMLEngine *engine;
349 	HTMLClueFlowStyle flow_style;
350 	HTMLListType item_type;
351 	guint indentation;
352 
353 	/* printf ("gtk_html_update_styles called\n"); */
354 
355 	if (!html_engine_get_editable (html->engine))
356 		return;
357 
358 	engine          = html->engine;
359 	html_engine_get_current_clueflow_style (engine, &flow_style, &item_type);
360 	paragraph_style = clueflow_style_to_paragraph_style (flow_style, item_type);
361 
362 	if (paragraph_style != html->priv->paragraph_style) {
363 		html->priv->paragraph_style = paragraph_style;
364 		g_signal_emit (html, signals[CURRENT_PARAGRAPH_STYLE_CHANGED], 0, paragraph_style);
365 	}
366 
367 	indentation = html_engine_get_current_clueflow_indentation (engine);
368 	if (indentation != html->priv->paragraph_indentation) {
369 		html->priv->paragraph_indentation = indentation;
370 		g_signal_emit (html, signals[CURRENT_PARAGRAPH_INDENTATION_CHANGED], 0, indentation);
371 	}
372 
373 	alignment = html_alignment_to_paragraph (html_engine_get_current_clueflow_alignment (engine));
374 	if (alignment != html->priv->paragraph_alignment) {
375 		html->priv->paragraph_alignment = alignment;
376 		g_signal_emit (html, signals[CURRENT_PARAGRAPH_ALIGNMENT_CHANGED], 0, alignment);
377 	}
378 
379 	if (html_engine_update_insertion_font_style (engine))
380 		g_signal_emit (html, signals[INSERTION_FONT_STYLE_CHANGED], 0, engine->insertion_font_style);
381 	if (html_engine_update_insertion_color (engine))
382 		g_signal_emit (html, signals[INSERTION_COLOR_CHANGED], 0, engine->insertion_color);
383 
384 	/* TODO add insertion_url_or_targed_changed signal */
385 	html_engine_update_insertion_url_and_target (engine);
386 }
387 
388 
389 /* GTK+ idle loop handler.  */
390 
391 static gint
idle_handler(gpointer data)392 idle_handler (gpointer data)
393 {
394 	GtkHTML *html;
395 	HTMLEngine *engine;
396 	gboolean also_update_cursor;
397 
398 	html = GTK_HTML (data);
399 	engine = html->engine;
400 
401 	also_update_cursor = any_has_cursor_moved (html) || !any_has_skip_update_cursor (html);
402 
403 	if (html->engine->thaw_idle_id == 0 && !html_engine_frozen (html->engine))
404 		html_engine_flush_draw_queue (engine);
405 
406 	if (also_update_cursor)
407 		gtk_html_adjust_cursor_position (html);
408 
409 	html->priv->idle_handler_id = 0;
410 	html->priv->skip_update_cursor = FALSE;
411 	html->priv->cursor_moved = FALSE;
412 
413 	while (html->iframe_parent) {
414 		html = GTK_HTML (html->iframe_parent);
415 
416 		if (html) {
417 			html->priv->skip_update_cursor = FALSE;
418 			html->priv->cursor_moved = FALSE;
419 		}
420 
421 		if (also_update_cursor)
422 			gtk_html_adjust_cursor_position (html);
423 	}
424 
425 	return FALSE;
426 }
427 
428 static void
gtk_html_adjust_cursor_position(GtkHTML * html)429 gtk_html_adjust_cursor_position (GtkHTML *html)
430 {
431 	HTMLEngine *e;
432 	GtkAdjustment *hadjustment;
433 	GtkAdjustment *vadjustment;
434 	e = html->engine;
435 
436 	if (html->priv->scroll_timeout_id == 0  &&
437 	    html->engine->thaw_idle_id == 0  &&
438 	    !html_engine_frozen (html->engine))
439 		html_engine_make_cursor_visible (e);
440 
441 	hadjustment = gtk_layout_get_hadjustment (GTK_LAYOUT (html));
442 	vadjustment = gtk_layout_get_vadjustment (GTK_LAYOUT (html));
443 
444 	gtk_adjustment_set_value (hadjustment, (gfloat) e->x_offset);
445 	gtk_adjustment_set_value (vadjustment, (gfloat) e->y_offset);
446 	gtk_html_private_calc_scrollbars (html, NULL, NULL);
447 
448 }
449 
450 static void
queue_draw(GtkHTML * html)451 queue_draw (GtkHTML *html)
452 {
453 	if (html->priv->idle_handler_id == 0)
454 		html->priv->idle_handler_id =
455 			/* schedule with priority higher than gtk+ uses for animations (check docs for G_PRIORITY_HIGH_IDLE)  */
456 			g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_handler, html, NULL);
457 }
458 
459 
460 /* HTMLEngine callbacks.  */
461 
462 static void
html_engine_title_changed_cb(HTMLEngine * engine,gpointer data)463 html_engine_title_changed_cb (HTMLEngine *engine,
464                               gpointer data)
465 {
466 	GtkHTML *gtk_html;
467 
468 	gtk_html = GTK_HTML (data);
469 	g_signal_emit (gtk_html, signals[TITLE_CHANGED], 0, engine->title->str);
470 }
471 
472 static void
html_engine_set_base_cb(HTMLEngine * engine,const gchar * base,gpointer data)473 html_engine_set_base_cb (HTMLEngine *engine,
474                          const gchar *base,
475                          gpointer data)
476 {
477 	GtkHTML *gtk_html;
478 
479 	gtk_html = GTK_HTML (data);
480 	gtk_html_set_base (gtk_html, base);
481 	g_signal_emit (gtk_html, signals[SET_BASE], 0, base);
482 }
483 
484 static void
html_engine_set_base_target_cb(HTMLEngine * engine,const gchar * base_target,gpointer data)485 html_engine_set_base_target_cb (HTMLEngine *engine,
486                                 const gchar *base_target,
487                                 gpointer data)
488 {
489 	GtkHTML *gtk_html;
490 
491 	gtk_html = GTK_HTML (data);
492 	g_signal_emit (gtk_html, signals[SET_BASE_TARGET], 0, base_target);
493 }
494 
495 static void
html_engine_load_done_cb(HTMLEngine * engine,gpointer data)496 html_engine_load_done_cb (HTMLEngine *engine,
497                           gpointer data)
498 {
499 	GtkHTML *gtk_html;
500 
501 	gtk_html = GTK_HTML (data);
502 	g_signal_emit (gtk_html, signals[LOAD_DONE], 0);
503 }
504 
505 static void
html_engine_url_requested_cb(HTMLEngine * engine,const gchar * url,GtkHTMLStream * handle,gpointer data)506 html_engine_url_requested_cb (HTMLEngine *engine,
507                               const gchar *url,
508                               GtkHTMLStream *handle,
509                               gpointer data)
510 {
511 	GtkHTML *gtk_html;
512 	gchar *expanded = NULL;
513 	gtk_html = GTK_HTML (data);
514 
515 	if (engine->stopped)
516 		return;
517 
518 	expanded = gtk_html_get_url_base_relative (gtk_html, url);
519 	g_signal_emit (gtk_html, signals[URL_REQUESTED], 0, expanded, handle);
520 	g_free (expanded);
521 }
522 
523 static void
html_engine_draw_pending_cb(HTMLEngine * engine,gpointer data)524 html_engine_draw_pending_cb (HTMLEngine *engine,
525                              gpointer data)
526 {
527 	GtkHTML *html;
528 
529 	html = GTK_HTML (data);
530 	html->priv->skip_update_cursor = TRUE;
531 	queue_draw (html);
532 }
533 
534 static void
html_engine_redirect_cb(HTMLEngine * engine,const gchar * url,gint delay,gpointer data)535 html_engine_redirect_cb (HTMLEngine *engine,
536                          const gchar *url,
537                          gint delay,
538                          gpointer data)
539 {
540 	GtkHTML *gtk_html;
541 
542 	gtk_html = GTK_HTML (data);
543 
544 	g_signal_emit (gtk_html, signals[REDIRECT], 0, url, delay);
545 }
546 
547 static void
html_engine_submit_cb(HTMLEngine * engine,const gchar * method,const gchar * url,const gchar * encoding,gpointer data)548 html_engine_submit_cb (HTMLEngine *engine,
549                        const gchar *method,
550                        const gchar *url,
551                        const gchar *encoding,
552                        gpointer data)
553 {
554 	GtkHTML *gtk_html;
555 
556 	gtk_html = GTK_HTML (data);
557 
558 	g_signal_emit (gtk_html, signals[SUBMIT], 0, method, url, encoding);
559 }
560 
561 static gboolean
html_engine_object_requested_cb(HTMLEngine * engine,GtkHTMLEmbedded * eb,gpointer data)562 html_engine_object_requested_cb (HTMLEngine *engine,
563                        GtkHTMLEmbedded *eb,
564                        gpointer data)
565 {
566 	GtkHTML *gtk_html;
567 	gboolean object_found = FALSE;
568 
569 	gtk_html = GTK_HTML (data);
570 
571 	object_found = FALSE;
572 	g_signal_emit (gtk_html, signals[OBJECT_REQUESTED], 0, eb, &object_found);
573 	return object_found;
574 }
575 
576 
577 /* GtkAdjustment handling.  */
578 
579 static void
scroll_update_mouse(GtkWidget * widget)580 scroll_update_mouse (GtkWidget *widget)
581 {
582 	GdkWindow *window;
583 	GdkWindow *bin_window;
584 	gint x, y;
585 
586 	if (!gtk_widget_get_realized (widget))
587 		return;
588 
589 	window = gtk_widget_get_window (widget);
590 	bin_window = gtk_layout_get_bin_window (GTK_LAYOUT (widget));
591 
592 	gdk_window_get_pointer (bin_window, &x, &y, NULL);
593 	mouse_change_pos (widget, window, x, y, 0);
594 }
595 
596 static void
vertical_scroll_cb(GtkAdjustment * adjustment,gpointer data)597 vertical_scroll_cb (GtkAdjustment *adjustment,
598                     gpointer data)
599 {
600 	GtkHTML *html = GTK_HTML (data);
601 
602 	if (html->engine->keep_scroll)
603 		return;
604 
605 	html->engine->y_offset = (gint) gtk_adjustment_get_value (adjustment);
606 	scroll_update_mouse (GTK_WIDGET (data));
607 }
608 
609 static void
horizontal_scroll_cb(GtkAdjustment * adjustment,gpointer data)610 horizontal_scroll_cb (GtkAdjustment *adjustment,
611                       gpointer data)
612 {
613 	GtkHTML *html = GTK_HTML (data);
614 
615 	if (html->engine->keep_scroll)
616 		return;
617 
618 	html->engine->x_offset = (gint) gtk_adjustment_get_value (adjustment);
619 	scroll_update_mouse (GTK_WIDGET (data));
620 }
621 
622 static void
hadjustment_notify_cb(GtkHTML * html)623 hadjustment_notify_cb (GtkHTML *html)
624 {
625 	GtkScrollable *scrollable;
626 	GtkAdjustment *hadjustment;
627 
628 	if (!html->priv)
629 		return;
630 
631 	scrollable = GTK_SCROLLABLE (html);
632 	hadjustment = gtk_scrollable_get_hadjustment (scrollable);
633 
634 	if (html->hadj_connection > 0) {
635 		g_signal_handler_disconnect (
636 			html->priv->hadjustment, html->hadj_connection);
637 		g_object_unref (html->priv->hadjustment);
638 	}
639 
640 	if (hadjustment != NULL) {
641 		html->hadj_connection = g_signal_connect (
642 			hadjustment, "value_changed",
643 			G_CALLBACK (horizontal_scroll_cb), html);
644 		html->priv->hadjustment = g_object_ref (hadjustment);
645 	} else {
646 		html->hadj_connection = 0;
647 		html->priv->hadjustment = NULL;
648 	}
649 }
650 
651 static void
vadjustment_notify_cb(GtkHTML * html)652 vadjustment_notify_cb (GtkHTML *html)
653 {
654 	GtkScrollable *scrollable;
655 	GtkAdjustment *vadjustment;
656 
657 	if (!html->priv)
658 		return;
659 
660 	scrollable = GTK_SCROLLABLE (html);
661 	vadjustment = gtk_scrollable_get_vadjustment (scrollable);
662 
663 	if (html->vadj_connection != 0) {
664 		g_signal_handler_disconnect (
665 			html->priv->vadjustment, html->vadj_connection);
666 		g_object_unref (html->priv->vadjustment);
667 	}
668 
669 	if (vadjustment != NULL) {
670 		html->vadj_connection = g_signal_connect (
671 			vadjustment, "value_changed",
672 			G_CALLBACK (vertical_scroll_cb), html);
673 		html->priv->vadjustment = g_object_ref (vadjustment);
674 	} else {
675 		html->vadj_connection = 0;
676 		html->priv->vadjustment = NULL;
677 	}
678 }
679 
680 
681 /* Scroll timeout handling.  */
682 
683 static void
inc_adjustment(GtkAdjustment * adj,gint doc_width,gint alloc_width,gint inc)684 inc_adjustment (GtkAdjustment *adj,
685                 gint doc_width,
686                 gint alloc_width,
687                 gint inc)
688 {
689 	gfloat value;
690 	gint max;
691 
692 	value = gtk_adjustment_get_value (adj) + (gfloat) inc;
693 
694 	if (doc_width > alloc_width)
695 		max = doc_width - alloc_width;
696 	else
697 		max = 0;
698 
699 	if (value > (gfloat) max)
700 		value = (gfloat) max;
701 	else if (value < 0)
702 		value = 0.0;
703 
704 	gtk_adjustment_set_value (adj, value);
705 }
706 
707 static gint
scroll_timeout_cb(gpointer data)708 scroll_timeout_cb (gpointer data)
709 {
710 	GtkWidget *widget;
711 	GdkWindow *window;
712 	GtkHTML *html;
713 	HTMLEngine *engine;
714 	GtkAllocation allocation;
715 	GtkAdjustment *hadjustment;
716 	GtkAdjustment *vadjustment;
717 	gint x_scroll, y_scroll;
718 	gint x, y;
719 
720 	widget = GTK_WIDGET (data);
721 	html = GTK_HTML (data);
722 	engine = html->engine;
723 
724 	window = gtk_widget_get_window (widget);
725 	gdk_window_get_pointer (window, &x, &y, NULL);
726 
727 	gtk_widget_get_allocation (widget, &allocation);
728 
729 	if (x < 0) {
730 		x_scroll = x;
731 		if (x + engine->x_offset >= 0)
732 			x = 0;
733 	} else if (x >= allocation.width) {
734 		x_scroll = x - allocation.width + 1;
735 		x = allocation.width;
736 	} else {
737 		x_scroll = 0;
738 	}
739 	x_scroll /= 2;
740 
741 	if (y < 0) {
742 		y_scroll = y;
743 		if (y + engine->y_offset >= 0)
744 			y = 0;
745 	} else if (y >= allocation.height) {
746 		y_scroll = y - allocation.height + 1;
747 		y = allocation.height;
748 	} else {
749 		y_scroll = 0;
750 	}
751 	y_scroll /= 2;
752 
753 	if (html->in_selection && (x_scroll != 0 || y_scroll != 0))
754 		html_engine_select_region (engine, html->selection_x1, html->selection_y1,
755 					   x + engine->x_offset, y + engine->y_offset);
756 
757 	hadjustment = gtk_layout_get_hadjustment (GTK_LAYOUT (widget));
758 	vadjustment = gtk_layout_get_vadjustment (GTK_LAYOUT (widget));
759 
760 	inc_adjustment (hadjustment, html_engine_get_doc_width (html->engine),
761 			allocation.width, x_scroll);
762 	inc_adjustment (vadjustment, html_engine_get_doc_height (html->engine),
763 			allocation.height, y_scroll);
764 
765 	return TRUE;
766 }
767 
768 static void
setup_scroll_timeout(GtkHTML * html)769 setup_scroll_timeout (GtkHTML *html)
770 {
771 	if (html->priv->scroll_timeout_id != 0)
772 		return;
773 
774 	html->priv->scroll_timeout_id = g_timeout_add (SCROLL_TIMEOUT_INTERVAL,
775 						   scroll_timeout_cb, html);
776 
777 	scroll_timeout_cb (html);
778 }
779 
780 static void
remove_scroll_timeout(GtkHTML * html)781 remove_scroll_timeout (GtkHTML *html)
782 {
783 	if (html->priv->scroll_timeout_id == 0)
784 		return;
785 
786 	g_source_remove (html->priv->scroll_timeout_id);
787 	html->priv->scroll_timeout_id = 0;
788 }
789 
790 
791 /* GObject methods.  */
792 
793 static void
dispose(GObject * object)794 dispose (GObject *object)
795 {
796 	GtkHTML *html;
797 
798 	html = GTK_HTML (object);
799 
800 	g_free (html->pointer_url);
801 	html->pointer_url = NULL;
802 
803 	if (html->hand_cursor) {
804 		g_object_unref (html->hand_cursor);
805 		html->hand_cursor = NULL;
806 	}
807 
808 	if (html->ibeam_cursor) {
809 		g_object_unref (html->ibeam_cursor);
810 		html->ibeam_cursor = NULL;
811 	}
812 
813 	gtk_scrollable_set_hadjustment (GTK_SCROLLABLE (html), NULL);
814 	gtk_scrollable_set_vadjustment (GTK_SCROLLABLE (html), NULL);
815 
816 	hadjustment_notify_cb (html);
817 	vadjustment_notify_cb (html);
818 
819 	g_signal_handlers_disconnect_by_func (html, hadjustment_notify_cb, NULL);
820 	g_signal_handlers_disconnect_by_func (html, vadjustment_notify_cb, NULL);
821 
822 	if (html->priv->idle_handler_id != 0) {
823 		g_source_remove (html->priv->idle_handler_id);
824 		html->priv->idle_handler_id = 0;
825 	}
826 
827 	if (html->priv->scroll_timeout_id != 0) {
828 		g_source_remove (html->priv->scroll_timeout_id);
829 		html->priv->scroll_timeout_id = 0;
830 	}
831 
832 	if (html->priv->desktop_interface != NULL) {
833 		g_signal_handlers_disconnect_matched (
834 			html->priv->desktop_interface,
835 			G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, html);
836 		g_object_unref (html->priv->desktop_interface);
837 		html->priv->desktop_interface = NULL;
838 	}
839 
840 	if (html->priv->resize_cursor) {
841 		g_object_unref (html->priv->resize_cursor);
842 		html->priv->resize_cursor = NULL;
843 	}
844 
845 	if (html->priv->im_context) {
846 		g_object_unref (html->priv->im_context);
847 		html->priv->im_context = NULL;
848 	}
849 
850 	g_free (html->priv->base_url);
851 	html->priv->base_url = NULL;
852 
853 	g_free (html->priv->caret_first_focus_anchor);
854 	html->priv->caret_first_focus_anchor = NULL;
855 
856 	if (html->engine) {
857 		g_object_unref (html->engine);
858 		html->engine = NULL;
859 	}
860 
861 	/* Chain up to parent's dispose() method. */
862 	G_OBJECT_CLASS (parent_class)->dispose (object);
863 }
864 
865 GtkHTML *
gtk_html_get_top_html(GtkHTML * html)866 gtk_html_get_top_html (GtkHTML *html)
867 {
868 	while (html->iframe_parent)
869 		html = GTK_HTML (html->iframe_parent);
870 
871 	return html;
872 }
873 
874 void
gtk_html_set_fonts(GtkHTML * html,HTMLPainter * painter)875 gtk_html_set_fonts (GtkHTML *html,
876                     HTMLPainter *painter)
877 {
878 	GtkWidget *top_level;
879 	GtkStyleContext *style_context;
880 	GdkScreen *screen;
881 	PangoFontDescription *fixed_desc = NULL;
882 	const PangoFontDescription *font_desc;
883 	gchar *fixed_name = NULL;
884 	const gchar *fixed_family = NULL;
885 	gint  fixed_size = 0;
886 	gboolean  fixed_points = FALSE;
887 	const gchar *font_var = NULL;
888 	gint  font_var_size = 0;
889 	gboolean  font_var_points = FALSE;
890 
891 	top_level = GTK_WIDGET (gtk_html_get_top_html (html));
892 	style_context = gtk_widget_get_style_context (top_level);
893 	font_desc = gtk_style_context_get_font (style_context, GTK_STATE_FLAG_NORMAL);
894 
895 	font_var = pango_font_description_get_family (font_desc);
896 	font_var_size = pango_font_description_get_size (font_desc);
897 	font_var_points = !pango_font_description_get_size_is_absolute (font_desc);
898 
899 	gtk_widget_style_get (GTK_WIDGET (top_level), "fixed_font_name", &fixed_name, NULL);
900 	if (fixed_name) {
901 		fixed_desc = pango_font_description_from_string (fixed_name);
902 		if (pango_font_description_get_family (fixed_desc)) {
903 			fixed_size = pango_font_description_get_size (fixed_desc);
904 			fixed_points = !pango_font_description_get_size_is_absolute (fixed_desc);
905 			fixed_family = pango_font_description_get_family (fixed_desc);
906 		} else {
907 			g_free (fixed_name);
908 			fixed_name = NULL;
909 		}
910 	}
911 
912 	if (!fixed_name) {
913 		GSettings *settings;
914 
915 		settings = g_settings_new ("org.gnome.desktop.interface");
916 		fixed_name = g_settings_get_string (settings, "monospace-font-name");
917 		g_object_unref (settings);
918 
919 		if (fixed_name) {
920 			fixed_desc = pango_font_description_from_string (fixed_name);
921 			if (fixed_desc) {
922 				fixed_size = pango_font_description_get_size (fixed_desc);
923 				fixed_points = !pango_font_description_get_size_is_absolute (fixed_desc);
924 				fixed_family = pango_font_description_get_family (fixed_desc);
925 			} else {
926 				g_free (fixed_name);
927 				fixed_name = NULL;
928 			}
929 		}
930 	}
931 
932 	if (!fixed_name) {
933 		fixed_family = "Monospace";
934 		fixed_size = font_var_size;
935 	}
936 
937 	html_font_manager_set_default (&painter->font_manager,
938 				       (gchar *) font_var, (gchar *) fixed_family,
939 				       font_var_size, font_var_points,
940 				       fixed_size, fixed_points);
941 	if (fixed_desc)
942 		pango_font_description_free (fixed_desc);
943 
944 	screen = gtk_widget_get_screen (GTK_WIDGET (html));
945 	if (screen != NULL)
946 		pango_cairo_context_set_font_options (
947 			painter->pango_context,
948 			gdk_screen_get_font_options (screen));
949 
950 	g_free (fixed_name);
951 }
952 
953 static void
set_caret_mode(HTMLEngine * engine,gboolean caret_mode)954 set_caret_mode (HTMLEngine *engine,
955                 gboolean caret_mode)
956 {
957 	if (engine->editable)
958 		return;
959 
960 	if (!caret_mode && engine->blinking_timer_id)
961 		html_engine_stop_blinking_cursor (engine);
962 
963 	engine->caret_mode = caret_mode;
964 
965 	if (caret_mode && !engine->parsing && !engine->timerId == 0)
966 		gtk_html_edit_make_cursor_visible (engine->widget);
967 
968 	/* Normally, blink cursor handler is setup in focus in event.
969 	 * However, in the case focus already in this engine, and user
970 	 * type F7 to enable cursor, we must setup the handler by
971 	 * ourselves.
972 	 */
973 	if (caret_mode && !engine->blinking_timer_id && engine->have_focus)
974 		html_engine_setup_blinking_cursor (engine);
975 
976 	return;
977 }
978 
979 /* GtkWidget methods.  */
980 
981 static void
style_updated(GtkWidget * widget)982 style_updated (GtkWidget *widget)
983 {
984 	HTMLEngine *engine = GTK_HTML (widget)->engine;
985 
986 	/* we don't need to set font's in idle time so call idle callback directly to avoid
987 	 * recalculating whole document
988 	*/
989 	if (engine) {
990 		gtk_html_set_fonts (GTK_HTML (widget), engine->painter);
991 		html_engine_refresh_fonts (engine);
992 	}
993 
994 	html_colorset_set_style (engine->defaultSettings->color_set, widget);
995 	html_colorset_set_unchanged (engine->settings->color_set,
996 				     engine->defaultSettings->color_set);
997 
998 	/* link and quotation colors might changed => need to refresh pango info in text objects */
999 	if (engine->clue)
1000 		html_object_change_set_down (engine->clue, HTML_CHANGE_RECALC_PI);
1001 	html_engine_schedule_update (engine);
1002 }
1003 
1004 static void
update_mouse_cursor(GtkWidget * widget,guint state)1005 update_mouse_cursor (GtkWidget *widget,
1006                      guint state)
1007 {
1008 	GdkEventMotion event;
1009 
1010 	/* a bit hacky here */
1011 	memset (&event, 0, sizeof (GdkEventMotion));
1012 	event.type = GDK_MOTION_NOTIFY;
1013 	event.window = gtk_widget_get_window (widget);
1014 	event.send_event = FALSE;
1015 	event.state = state;
1016 
1017 	motion_notify_event (widget, &event);
1018 }
1019 
1020 static gboolean
key_press_event(GtkWidget * widget,GdkEventKey * event)1021 key_press_event (GtkWidget *widget,
1022                  GdkEventKey *event)
1023 {
1024 	GtkHTML *html = GTK_HTML (widget);
1025 	GtkHTMLClass *html_class = GTK_HTML_CLASS (GTK_WIDGET_GET_CLASS (html));
1026 	gboolean retval = FALSE, update = TRUE;
1027 	HTMLObject *focus_object;
1028 	gint focus_object_offset;
1029 	gboolean url_test_mode;
1030 
1031 	html->binding_handled = FALSE;
1032 	html->priv->update_styles = FALSE;
1033 	html->priv->event_time = event->time;
1034 
1035 	if ((event->keyval == GDK_KEY_Control_L || event->keyval == GDK_KEY_Control_R)
1036 	    && html_engine_get_editable (html->engine))
1037 		url_test_mode = TRUE;
1038 	else
1039 		url_test_mode = FALSE;
1040 
1041 	if (html->priv->in_url_test_mode != url_test_mode) {
1042 		html->priv->in_url_test_mode = url_test_mode;
1043 		update_mouse_cursor (widget, event->state);
1044 	}
1045 
1046 	if (html_engine_get_editable (html->engine)) {
1047 		if (gtk_im_context_filter_keypress (html->priv->im_context, event)) {
1048 			html_engine_reset_blinking_cursor (html->engine);
1049 			html->priv->need_im_reset = TRUE;
1050 			return TRUE;
1051 		}
1052 	}
1053 
1054 	if (html_class->use_emacs_bindings && html_class->emacs_bindings && !html->binding_handled)
1055 		gtk_binding_set_activate (html_class->emacs_bindings, event->keyval, event->state, G_OBJECT (widget));
1056 
1057 	if (!html->binding_handled) {
1058 		html->priv->in_key_binding = TRUE;
1059 		retval = GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event);
1060 		html->priv->in_key_binding = FALSE;
1061 	}
1062 
1063 	retval = retval || html->binding_handled;
1064 	update = html->priv->update_styles;
1065 
1066 	if (retval && update)
1067 		gtk_html_update_styles (html);
1068 
1069 	html->priv->event_time = 0;
1070 
1071 	/* FIXME: use bindings */
1072 	if (!html_engine_get_editable (html->engine)) {
1073 		switch (event->keyval) {
1074 		case GDK_KEY_Return:
1075 		case GDK_KEY_KP_Enter:
1076 			/* the toplevel gtkhtml's focus object may be a frame or ifame */
1077 			focus_object = html_engine_get_focus_object (html->engine, &focus_object_offset);
1078 
1079 			if (focus_object) {
1080 				gchar *url;
1081 				url = html_object_get_complete_url (focus_object, focus_object_offset);
1082 				if (url) {
1083 					/* printf ("link clicked: %s\n", url); */
1084 					if (HTML_IS_TEXT (focus_object)) {
1085 						html_text_set_link_visited (HTML_TEXT (focus_object), focus_object_offset, html->engine, TRUE);
1086 					g_signal_emit (html, signals[LINK_CLICKED], 0, url);
1087 					}
1088 					g_free (url);
1089 				}
1090 			}
1091 			break;
1092 		default:
1093 			;
1094 		}
1095 	}
1096 
1097 	if (retval && (html_engine_get_editable (html->engine) || html->engine->caret_mode))
1098 		html_engine_reset_blinking_cursor (html->engine);
1099 
1100 	/* printf ("retval: %d\n", retval); */
1101 
1102 	return retval;
1103 }
1104 
1105 static gboolean
key_release_event(GtkWidget * widget,GdkEventKey * event)1106 key_release_event (GtkWidget *widget,
1107                    GdkEventKey *event)
1108 {
1109 	GtkHTML *html = GTK_HTML (widget);
1110 
1111 	if (html->priv->in_url_test_mode) {
1112 		html->priv->in_url_test_mode = FALSE;
1113 		update_mouse_cursor (widget, event->state);
1114 	}
1115 
1116 	if (!html->binding_handled && html_engine_get_editable (html->engine)) {
1117 		if (gtk_im_context_filter_keypress (html->priv->im_context, event)) {
1118 			html->priv->need_im_reset = TRUE;
1119 			return TRUE;
1120 		}
1121 	}
1122 
1123 	return GTK_WIDGET_CLASS (parent_class)->key_release_event (widget, event);
1124 }
1125 
1126 void
gtk_html_drag_dest_set(GtkHTML * html)1127 gtk_html_drag_dest_set (GtkHTML *html)
1128 {
1129 	if (html_engine_get_editable (html->engine))
1130 		gtk_drag_dest_set (
1131 			GTK_WIDGET (html),
1132 			GTK_DEST_DEFAULT_HIGHLIGHT | GTK_DEST_DEFAULT_DROP,
1133 			drag_dest_targets, G_N_ELEMENTS (drag_dest_targets),
1134 			GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
1135 	else
1136 		gtk_drag_dest_unset (GTK_WIDGET (html));
1137 }
1138 
1139 static void
realize(GtkWidget * widget)1140 realize (GtkWidget *widget)
1141 {
1142 	GtkHTML *html;
1143 	GdkWindow *window;
1144 	GdkWindow *bin_window;
1145 	GtkAdjustment *hadjustment;
1146 	GtkAdjustment *vadjustment;
1147 
1148 	g_return_if_fail (widget != NULL);
1149 	g_return_if_fail (GTK_IS_HTML (widget));
1150 
1151 	html = GTK_HTML (widget);
1152 	hadjustment = gtk_layout_get_hadjustment (GTK_LAYOUT (widget));
1153 	vadjustment = gtk_layout_get_vadjustment (GTK_LAYOUT (widget));
1154 
1155 	if (GTK_WIDGET_CLASS (parent_class)->realize)
1156 		(* GTK_WIDGET_CLASS (parent_class)->realize) (widget);
1157 
1158 	window = gtk_widget_get_window (widget);
1159 	bin_window = gtk_layout_get_bin_window (&html->layout);
1160 
1161 	gdk_window_set_events (bin_window,
1162 			       (gdk_window_get_events (bin_window)
1163 				| GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK
1164 				| GDK_ENTER_NOTIFY_MASK
1165 				| GDK_BUTTON_PRESS_MASK
1166 				| GDK_BUTTON_RELEASE_MASK
1167 				| GDK_VISIBILITY_NOTIFY_MASK
1168 				| GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK));
1169 
1170 	html_engine_realize (html->engine, bin_window);
1171 
1172 	gdk_window_set_cursor (window, NULL);
1173 
1174 	/* This sets the backing pixmap to None, so that scrolling does not
1175 	 * erase the newly exposed area, thus making the thing smoother.  */
1176 	gdk_window_set_background_pattern (bin_window, NULL);
1177 
1178 	/* If someone was silly enough to stick us in something that doesn't
1179 	 * have adjustments, go ahead and create them now since we expect them
1180 	 * and love them and pat them
1181 	 */
1182 	if (hadjustment == NULL) {
1183 		hadjustment = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
1184 		gtk_layout_set_hadjustment (GTK_LAYOUT (widget), hadjustment);
1185 	}
1186 
1187 	if (vadjustment == NULL) {
1188 		vadjustment = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
1189 		gtk_layout_set_vadjustment (GTK_LAYOUT (widget), vadjustment);
1190 	}
1191 
1192 	gtk_html_drag_dest_set (html);
1193 
1194 	gtk_im_context_set_client_window (html->priv->im_context, window);
1195 
1196 	html_image_factory_start_animations (html->engine->image_factory);
1197 }
1198 
1199 static void
unrealize(GtkWidget * widget)1200 unrealize (GtkWidget *widget)
1201 {
1202 	GtkHTML *html = GTK_HTML (widget);
1203 
1204 	html_engine_unrealize (html->engine);
1205 
1206 	gtk_im_context_set_client_window (html->priv->im_context, NULL);
1207 
1208 	html_image_factory_stop_animations (html->engine->image_factory);
1209 
1210 	if (GTK_WIDGET_CLASS (parent_class)->unrealize)
1211 		(* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
1212 }
1213 
1214 static gboolean
draw(GtkWidget * widget,cairo_t * cr)1215 draw (GtkWidget *widget,
1216       cairo_t *cr)
1217 {
1218 	html_engine_draw_cb (GTK_HTML (widget)->engine, cr);
1219 
1220 	if (GTK_WIDGET_CLASS (parent_class)->draw)
1221 		(* GTK_WIDGET_CLASS (parent_class)->draw) (widget, cr);
1222 
1223 	return FALSE;
1224 }
1225 
1226 static void
gtk_html_get_preferred_height(GtkWidget * widget,gint * minimum_height,gint * natural_height)1227 gtk_html_get_preferred_height (GtkWidget *widget,
1228                                gint *minimum_height,
1229                                gint *natural_height)
1230 {
1231 	HTMLEngine *e = GTK_HTML (widget)->engine;
1232 	if (!e->writing) {
1233 		gint old_height;
1234 
1235 		old_height = e->height;
1236 		e->height = *minimum_height;
1237 		html_engine_calc_size (e, NULL);
1238 		*minimum_height = *natural_height = html_engine_get_doc_height (e);
1239 		e->height = old_height;
1240 		html_engine_calc_size (e, NULL);
1241 	} else {
1242 		*minimum_height = *natural_height = html_engine_get_doc_height (e);
1243 	}
1244 }
1245 
1246 static void
gtk_html_get_preferred_width(GtkWidget * widget,gint * minimum_width,gint * natural_width)1247 gtk_html_get_preferred_width (GtkWidget *widget,
1248                               gint *minimum_width,
1249                               gint *natural_width)
1250 {
1251 	HTMLEngine *e = GTK_HTML (widget)->engine;
1252 	if (!e->writing) {
1253 		gint old_width;
1254 
1255 		old_width = e->width;
1256 		e->width = *minimum_width;
1257 		html_engine_calc_size (e, NULL);
1258 		*minimum_width = *natural_width = html_engine_get_doc_width (e);
1259 		e->width = old_width;
1260 		html_engine_calc_size (e, NULL);
1261 	} else {
1262 		*minimum_width = *natural_width = html_engine_get_doc_width (e);
1263 	}
1264 }
1265 
1266 static void
child_size_allocate(HTMLObject * o,HTMLEngine * e,gpointer data)1267 child_size_allocate (HTMLObject *o,
1268                      HTMLEngine *e,
1269                      gpointer data)
1270 {
1271 	if (html_object_is_embedded (o)) {
1272 		HTMLEmbedded *eo = HTML_EMBEDDED (o);
1273 
1274 		if (eo->widget) {
1275 			GtkAllocation allocation;
1276 
1277 			html_object_calc_abs_position_in_frame (o, &allocation.x, &allocation.y);
1278 			allocation.y -= o->ascent;
1279 			allocation.width = o->width;
1280 			allocation.height = o->ascent + o->descent;
1281 			gtk_widget_size_allocate (eo->widget, &allocation);
1282 		}
1283 	}
1284 }
1285 
1286 static void
set_adjustment_upper(GtkAdjustment * adjustment,gdouble upper)1287 set_adjustment_upper (GtkAdjustment *adjustment,
1288                       gdouble upper)
1289 {
1290 	gdouble page_size;
1291 	gdouble value;
1292 	gdouble min;
1293 
1294 	/* XXX Stolen from gtklayout.c and simplified. */
1295 
1296 	value = gtk_adjustment_get_value (adjustment);
1297 	page_size = gtk_adjustment_get_page_size (adjustment);
1298 	min = MAX (0., upper - page_size);
1299 
1300 	gtk_adjustment_set_upper (adjustment, upper);
1301 
1302 	if (value > min)
1303 		gtk_adjustment_set_value (adjustment, min);
1304 }
1305 
1306 static void
gtk_layout_faux_size_allocate(GtkWidget * widget,GtkAllocation * allocation)1307 gtk_layout_faux_size_allocate (GtkWidget *widget,
1308                                GtkAllocation *allocation)
1309 {
1310 	GtkLayout *layout = GTK_LAYOUT (widget);
1311 	GtkAdjustment *adjustment;
1312 	guint width, height;
1313 
1314 	/* XXX This is essentially a copy of GtkLayout's size_allocate()
1315 	 *     method, but with the GtkLayoutChild loop removed.  We call
1316 	 *     this instead of chaining up to GtkLayout. */
1317 
1318 	gtk_widget_set_allocation (widget, allocation);
1319 	gtk_layout_get_size (layout, &width, &height);
1320 
1321 	if (gtk_widget_get_realized (widget)) {
1322 		gdk_window_move_resize (
1323 			gtk_widget_get_window (widget),
1324 			allocation->x, allocation->y,
1325 			allocation->width, allocation->height);
1326 
1327 		gdk_window_resize (
1328 			gtk_layout_get_bin_window (layout),
1329 			MAX (width, allocation->width),
1330 			MAX (height, allocation->height));
1331 	}
1332 
1333 	/* XXX Does the previous logic alter the GtkLayout size?
1334 	 *     Not sure, so best refetch the size just to be safe. */
1335 	gtk_layout_get_size (layout, &width, &height);
1336 
1337 	adjustment = gtk_layout_get_hadjustment (layout);
1338 	g_object_freeze_notify (G_OBJECT (adjustment));
1339 	gtk_adjustment_set_page_size (adjustment, allocation->width);
1340 	gtk_adjustment_set_page_increment (adjustment, allocation->width * 0.9);
1341 	gtk_adjustment_set_lower (adjustment, 0);
1342 	set_adjustment_upper (adjustment, MAX (allocation->width, width));
1343 	g_object_thaw_notify (G_OBJECT (adjustment));
1344 
1345 	adjustment = gtk_layout_get_vadjustment (layout);
1346 	g_object_freeze_notify (G_OBJECT (adjustment));
1347 	gtk_adjustment_set_page_size (adjustment, allocation->height);
1348 	gtk_adjustment_set_page_increment (adjustment, allocation->height * 0.9);
1349 	gtk_adjustment_set_lower (adjustment, 0);
1350 	set_adjustment_upper (adjustment, MAX (allocation->height, height));
1351 	g_object_thaw_notify (G_OBJECT (adjustment));
1352 }
1353 
1354 static void
size_allocate(GtkWidget * widget,GtkAllocation * allocation)1355 size_allocate (GtkWidget *widget,
1356                GtkAllocation *allocation)
1357 {
1358 	GtkHTML *html;
1359 	gboolean changed_x = FALSE, changed_y = FALSE;
1360 
1361 	g_return_if_fail (widget != NULL);
1362 	g_return_if_fail (GTK_IS_HTML (widget));
1363 	g_return_if_fail (allocation != NULL);
1364 
1365 	html = GTK_HTML (widget);
1366 
1367 	/* isolate children from layout - we want to set them after calc size is performed
1368 	 * and we know the position of the children */
1369 
1370 	gtk_layout_faux_size_allocate (widget, allocation);
1371 
1372 	if (html->engine->width != allocation->width
1373 	    || html->engine->height != allocation->height) {
1374 		HTMLEngine *e = html->engine;
1375 		gint old_doc_width, old_doc_height, old_width, old_height;
1376 
1377 		old_doc_width = html_engine_get_doc_width (html->engine);
1378 		old_doc_height = html_engine_get_doc_height (html->engine);
1379 		old_width = e->width;
1380 		old_height = e->height;
1381 
1382 		e->width  = allocation->width;
1383 		e->height = allocation->height;
1384 
1385 		html_engine_calc_size (html->engine, NULL);
1386 		gtk_html_update_scrollbars_on_resize (html, old_doc_width, old_doc_height, old_width, old_height,
1387 						      &changed_x, &changed_y);
1388 	}
1389 
1390 	if (!html->engine->keep_scroll)
1391 		gtk_html_private_calc_scrollbars (html, &changed_x, &changed_y);
1392 
1393 	if (html->engine->clue)
1394 		html_object_forall (html->engine->clue, html->engine, child_size_allocate, NULL);
1395 }
1396 
1397 static void
set_pointer_url(GtkHTML * html,const gchar * url)1398 set_pointer_url (GtkHTML *html,
1399                  const gchar *url)
1400 {
1401 	if (url == html->pointer_url)
1402 		return;
1403 
1404 	if (url && html->pointer_url && !strcmp (url, html->pointer_url))
1405 		return;
1406 
1407 	g_free (html->pointer_url);
1408 	html->pointer_url = url ? g_strdup (url) : NULL;
1409 	g_signal_emit (html,  signals[ON_URL], 0, html->pointer_url);
1410 }
1411 
1412 static void
dnd_link_set(GtkWidget * widget,HTMLObject * o,gint offset)1413 dnd_link_set (GtkWidget *widget,
1414               HTMLObject *o,
1415               gint offset)
1416 {
1417 	if (!html_engine_get_editable (GTK_HTML (widget)->engine)) {
1418 		/* printf ("dnd_link_set %p\n", o); */
1419 
1420 		gtk_drag_source_set (
1421 			widget, GDK_BUTTON1_MASK,
1422 			drag_source_targets, G_N_ELEMENTS (drag_source_targets),
1423 			GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
1424 		GTK_HTML (widget)->priv->dnd_object = o;
1425 		GTK_HTML (widget)->priv->dnd_object_offset = offset;
1426 	}
1427 }
1428 
1429 static void
dnd_link_unset(GtkWidget * widget)1430 dnd_link_unset (GtkWidget *widget)
1431 {
1432 	if (!html_engine_get_editable (GTK_HTML (widget)->engine)) {
1433 		/* printf ("dnd_link_unset\n"); */
1434 
1435 		gtk_drag_source_unset (widget);
1436 		GTK_HTML (widget)->priv->dnd_object = NULL;
1437 	}
1438 }
1439 
1440 static void
on_object(GtkWidget * widget,GdkWindow * window,HTMLObject * obj,gint offset,gint x,gint y)1441 on_object (GtkWidget *widget,
1442            GdkWindow *window,
1443            HTMLObject *obj,
1444            gint offset,
1445            gint x,
1446            gint y)
1447 {
1448 	GtkHTML *html = GTK_HTML (widget);
1449 
1450 	if (obj) {
1451 		gchar *url;
1452 
1453 		if (gtk_html_get_editable (html)) {
1454 			if (HTML_IS_IMAGE (obj)) {
1455 				gint ox, oy;
1456 
1457 				html_object_calc_abs_position (obj, &ox, &oy);
1458 				if (ox + obj->width - 5 <= x && oy + obj->descent - 5 <= y) {
1459 					gdk_window_set_cursor (window, html->priv->resize_cursor);
1460 
1461 					return;
1462 				}
1463 			}
1464 		}
1465 
1466 		url = gtk_html_get_url_object_relative (html, obj,
1467 							html_object_get_url (obj, offset));
1468 		if (url != NULL) {
1469 			set_pointer_url (html, url);
1470 			dnd_link_set (widget, obj, offset);
1471 
1472 			if (html->engine->editable && !html->priv->in_url_test_mode)
1473 				gdk_window_set_cursor (window, html->ibeam_cursor);
1474 			else {
1475 				gdk_window_set_cursor (window, html->hand_cursor);
1476 			}
1477 		} else {
1478 			set_pointer_url (html, NULL);
1479 			dnd_link_unset (widget);
1480 
1481 			if (html_object_is_text (obj) && html->allow_selection)
1482 				gdk_window_set_cursor (window, html->ibeam_cursor);
1483 			else
1484 				gdk_window_set_cursor (window, NULL);
1485 		}
1486 
1487 		g_free (url);
1488 	} else {
1489 		set_pointer_url (html, NULL);
1490 		dnd_link_unset (widget);
1491 
1492 		gdk_window_set_cursor (window, NULL);
1493 	}
1494 }
1495 
1496 #define HTML_DIST(x,y) sqrt(x*x + y*y)
1497 
1498 static gint
mouse_change_pos(GtkWidget * widget,GdkWindow * window,gint x,gint y,gint state)1499 mouse_change_pos (GtkWidget *widget,
1500                   GdkWindow *window,
1501                   gint x,
1502                   gint y,
1503                   gint state)
1504 {
1505 	GtkHTML *html;
1506 	HTMLEngine *engine;
1507 	HTMLObject *obj;
1508 	HTMLType type;
1509 	gint offset;
1510 
1511 	if (!gtk_widget_get_realized (widget))
1512 		return FALSE;
1513 
1514 	html   = GTK_HTML (widget);
1515 	engine = html->engine;
1516 	obj    = html_engine_get_object_at (engine, x, y, (guint *) &offset, FALSE);
1517 
1518 	if ((html->in_selection || html->in_selection_drag) && html->allow_selection) {
1519 		GtkAllocation allocation;
1520 		gboolean need_scroll;
1521 
1522 		gtk_widget_get_allocation (widget, &allocation);
1523 
1524 		if (obj) {
1525 			type = HTML_OBJECT_TYPE (obj);
1526 
1527 			/* FIXME this is broken */
1528 
1529 			if (type == HTML_TYPE_BUTTON ||
1530 			    type == HTML_TYPE_CHECKBOX ||
1531 			    type == HTML_TYPE_EMBEDDED ||
1532 			    type == HTML_TYPE_HIDDEN ||
1533 			    type == HTML_TYPE_IMAGEINPUT ||
1534 			    type == HTML_TYPE_RADIO ||
1535 			    type == HTML_TYPE_SELECT ||
1536 			    type == HTML_TYPE_TEXTAREA ||
1537 			    type == HTML_TYPE_TEXTINPUT) {
1538 				return FALSE;
1539 			}
1540 		}
1541 
1542 		if (HTML_DIST ((x - html->selection_x1), (y  - html->selection_y1))
1543 		    > html_painter_get_space_width (engine->painter, GTK_HTML_FONT_STYLE_SIZE_3, NULL)) {
1544 			html->in_selection = TRUE;
1545 			html->in_selection_drag = TRUE;
1546 		}
1547 
1548 		need_scroll = FALSE;
1549 
1550 		if (x < html->engine->x_offset) {
1551 			need_scroll = TRUE;
1552 		} else if (x >= allocation.width) {
1553 			need_scroll = TRUE;
1554 		}
1555 
1556 		if (y < html->engine->y_offset) {
1557 			need_scroll = TRUE;
1558 		} else if (y >= allocation.height) {
1559 			need_scroll = TRUE;
1560 		}
1561 
1562 		if (need_scroll)
1563 			setup_scroll_timeout (html);
1564 		else
1565 			remove_scroll_timeout (html);
1566 
1567 		/* This will put the mark at the position of the
1568 		 * previous click.  */
1569 		if (engine->mark == NULL && engine->editable)
1570 			html_engine_set_mark (engine);
1571 
1572 		html_engine_select_region (engine, html->selection_x1, html->selection_y1, x, y);
1573 	}
1574 
1575 	if (html->priv->in_object_resize) {
1576 		HTMLObject *o = html->priv->resize_object;
1577 		gint ox, oy;
1578 
1579 		html_object_calc_abs_position (o, &ox, &oy);
1580 		oy -= o->ascent;
1581 		g_assert (HTML_IS_IMAGE (o));
1582 		if (x > ox && y > oy) {
1583 			gint w, h;
1584 
1585 			w = x - ox;
1586 			h = y - oy;
1587 			if (!(state & GDK_SHIFT_MASK)) {
1588 				w = MAX (w, h);
1589 				h = -1;
1590 			}
1591 			html_image_set_size (HTML_IMAGE (o), w, h, FALSE, FALSE);
1592 		}
1593 	} else
1594 		on_object (widget, window, obj, offset, x, y);
1595 
1596 	return TRUE;
1597 }
1598 
1599 static const gchar *
skip_host(const gchar * url)1600 skip_host (const gchar *url)
1601 {
1602 	const gchar *host;
1603 
1604 	host = url;
1605 	while (*host && (*host != '/') && (*host != ':'))
1606 	       host++;
1607 
1608 	if (*host == ':') {
1609 		host++;
1610 
1611 		if (*host == '/')
1612 			host++;
1613 
1614 		url = host;
1615 
1616 		if (*host == '/') {
1617 			host++;
1618 
1619 			while (*host && (*host != '/'))
1620 				host++;
1621 
1622 			url = host;
1623 		}
1624 	}
1625 
1626 	return url;
1627 }
1628 
1629 static gsize
path_len(const gchar * base,gboolean absolute)1630 path_len (const gchar *base,
1631           gboolean absolute)
1632 {
1633 	const gchar *last;
1634 	const gchar *cur;
1635 	const gchar *start;
1636 
1637 	start = last = skip_host (base);
1638 	if (!absolute) {
1639 		cur = strrchr (start, '/');
1640 
1641 		if (cur)
1642 			last = cur;
1643 	}
1644 
1645 	return last - base;
1646 }
1647 
1648 #if 0
1649 gchar *
1650 collapse_path (gchar *url)
1651 {
1652 	gchar *start;
1653 	gchar *end;
1654 	gchar *cur;
1655 	gsize len;
1656 
1657 	start = skip_host (url);
1658 
1659 	cur = start;
1660 	while ((cur = strstr (cur, "/../"))) {
1661 		end = cur + 3;
1662 
1663 		/* handle the case of a rootlevel /../ specialy */
1664 		if (cur == start) {
1665 			len = strlen (end);
1666 			memmove (cur, end, len + 1);
1667 		}
1668 
1669 		while (cur > start) {
1670 			cur--;
1671 			if ((*cur == '/') || (cur == start)) {
1672 				len = strlen (end);
1673 				memmove (cur, end, len + 1);
1674 				break;
1675 			}
1676 		}
1677 	}
1678 	return url;
1679 }
1680 #endif
1681 
1682 static gboolean
url_is_absolute(const gchar * url)1683 url_is_absolute (const gchar *url)
1684 {
1685 	/*
1686 	 * URI Syntactic Components
1687 	 *
1688 	 * The URI syntax is dependent upon the scheme.  In general, absolute
1689 	 * URI are written as follows:
1690 	 *
1691 	 * <scheme>:<scheme-specific-part>
1692 	 *
1693 	 * scheme        = alpha *( alpha | digit | "+" | "-" | "." )
1694 	 */
1695 
1696 	if (!url)
1697 		return FALSE;
1698 
1699 	if (!isalpha (*url))
1700 		return FALSE;
1701 	url++;
1702 
1703 	while (*url && (isalnum (*url) || *url == '+' || *url == '-' || *url == '.'))
1704 		url++;
1705 
1706 	return *url && *url == ':';
1707 }
1708 
1709 static gchar *
expand_relative(const gchar * base,const gchar * url)1710 expand_relative (const gchar *base,
1711                  const gchar *url)
1712 {
1713 	gchar *new_url = NULL;
1714 	gsize base_len, url_len;
1715 	gboolean absolute = FALSE;
1716 
1717 	if (!base || url_is_absolute (url)) {
1718 		/*
1719 		  g_warning ("base = %s url = %s new_url = %s",
1720 		  base, url, new_url);
1721 		*/
1722 		return g_strdup (url);
1723 	}
1724 
1725 	if (*url == '/') {
1726 		absolute = TRUE;;
1727 	}
1728 
1729 	base_len = path_len (base, absolute);
1730 	url_len = strlen (url);
1731 
1732 	new_url = g_malloc (base_len + url_len + 2);
1733 
1734 	if (base_len) {
1735 		memcpy (new_url, base, base_len);
1736 
1737 		if (base[base_len - 1] != '/')
1738 			new_url[base_len++] = '/';
1739 		if (absolute)
1740 			url++;
1741 	}
1742 
1743 	memcpy (new_url + base_len, url, url_len);
1744 	new_url[base_len + url_len] = '\0';
1745 
1746 	/*
1747 	 * g_warning ("base = %s url = %s new_url = %s",
1748 	 * base, url, new_url);
1749 	*/
1750 	return new_url;
1751 }
1752 
1753 gchar *
gtk_html_get_url_base_relative(GtkHTML * html,const gchar * url)1754 gtk_html_get_url_base_relative (GtkHTML *html,
1755                                 const gchar *url)
1756 {
1757 	return expand_relative (gtk_html_get_base (html), url);
1758 }
1759 
1760 static gchar *
expand_frame_url(GtkHTML * html,const gchar * url)1761 expand_frame_url (GtkHTML *html,
1762                   const gchar *url)
1763 {
1764 	gchar *new_url;
1765 
1766 	new_url = gtk_html_get_url_base_relative (html, url);
1767 	while (html->iframe_parent) {
1768 		gchar *expanded;
1769 
1770 		expanded = gtk_html_get_url_base_relative (GTK_HTML (html->iframe_parent),
1771 						       new_url);
1772 		g_free (new_url);
1773 		new_url = expanded;
1774 
1775 		html = GTK_HTML (html->iframe_parent);
1776 	}
1777 	return new_url;
1778 }
1779 
1780 gchar *
gtk_html_get_url_object_relative(GtkHTML * html,HTMLObject * o,const gchar * url)1781 gtk_html_get_url_object_relative (GtkHTML *html,
1782                                   HTMLObject *o,
1783                                   const gchar *url)
1784 {
1785 	HTMLEngine *e;
1786 	HTMLObject *parent;
1787 
1788 	g_return_val_if_fail (GTK_IS_HTML (html), NULL);
1789 
1790 	/* start at the top always */
1791 	while (html->iframe_parent)
1792 		html = GTK_HTML (html->iframe_parent);
1793 
1794 	parent = o;
1795 	while (parent->parent) {
1796 		parent = parent->parent;
1797 		if ((HTML_OBJECT_TYPE (parent) == HTML_TYPE_FRAME)
1798 		    || (HTML_OBJECT_TYPE (parent) == HTML_TYPE_IFRAME))
1799 			break;
1800 	}
1801 
1802 	e = html_object_get_engine (parent, html->engine);
1803 
1804 	if (!e) {
1805 		g_warning ("Cannot find object for url");
1806 		return NULL;
1807 	}
1808 
1809 	/*
1810 	if (e == html->engine)
1811 		g_warning ("engine matches engine");
1812 	*/
1813 	return url ? expand_frame_url (e->widget, url) : NULL;
1814 }
1815 
1816 static GtkWidget *
shift_to_iframe_parent(GtkWidget * widget,gint * x,gint * y)1817 shift_to_iframe_parent (GtkWidget *widget,
1818                         gint *x,
1819                         gint *y)
1820 {
1821 	while (GTK_HTML (widget)->iframe_parent) {
1822 		GtkWidget *scrolled_window;
1823 		GtkAllocation allocation;
1824 
1825 		scrolled_window = gtk_widget_get_parent (widget);
1826 
1827 		g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), widget);
1828 
1829 		gtk_widget_get_allocation (scrolled_window, &allocation);
1830 
1831 		if (x)
1832 			*x += allocation.x - GTK_HTML (widget)->engine->x_offset;
1833 		if (y)
1834 			*y += allocation.y - GTK_HTML (widget)->engine->y_offset;
1835 
1836 		widget = GTK_HTML (widget)->iframe_parent;
1837 
1838 	}
1839 
1840 	return widget;
1841 }
1842 
1843 static gboolean
motion_notify_event(GtkWidget * widget,GdkEventMotion * event)1844 motion_notify_event (GtkWidget *widget,
1845                      GdkEventMotion *event)
1846 {
1847 	GdkWindow *window;
1848 	GdkWindow *bin_window;
1849 	HTMLEngine *engine;
1850 	gint x, y;
1851 
1852 	g_return_val_if_fail (widget != NULL, 0);
1853 	g_return_val_if_fail (GTK_IS_HTML (widget), 0);
1854 	g_return_val_if_fail (event != NULL, 0);
1855 
1856 	/* printf ("motion_notify_event\n"); */
1857 
1858 	if (GTK_HTML (widget)->priv->dnd_in_progress)
1859 		return TRUE;
1860 
1861 	widget = shift_to_iframe_parent (widget, &x, &y);
1862 
1863 	window = gtk_widget_get_window (widget);
1864 	bin_window = gtk_layout_get_bin_window (GTK_LAYOUT (widget));
1865 
1866 	gdk_window_get_pointer (bin_window, &x, &y, NULL);
1867 
1868 	if (!mouse_change_pos (widget, window, x, y, event->state))
1869 		return FALSE;
1870 
1871 	engine = GTK_HTML (widget)->engine;
1872 	if (GTK_HTML (widget)->in_selection_drag && html_engine_get_editable (engine))
1873 		html_engine_jump_at (engine, x, y);
1874 	return TRUE;
1875 }
1876 
1877 static gboolean
toplevel_unmap(GtkWidget * widget,GdkEvent * event,GtkHTML * html)1878 toplevel_unmap (GtkWidget *widget,
1879                 GdkEvent *event,
1880                 GtkHTML *html)
1881 {
1882 	html_image_factory_stop_animations (html->engine->image_factory);
1883 
1884 	return FALSE;
1885 }
1886 
1887 static void
hierarchy_changed(GtkWidget * widget,GtkWidget * old_toplevel)1888 hierarchy_changed (GtkWidget *widget,
1889                    GtkWidget *old_toplevel)
1890 {
1891 	GtkWidget *toplevel;
1892 	GtkHTMLPrivate   *priv = GTK_HTML (widget)->priv;
1893 
1894 	if (old_toplevel && priv->toplevel_unmap_handler) {
1895 		g_signal_handler_disconnect (old_toplevel,
1896 					     priv->toplevel_unmap_handler);
1897 		priv->toplevel_unmap_handler = 0;
1898 	}
1899 
1900 	toplevel = gtk_widget_get_toplevel (widget);
1901 
1902 	if (gtk_widget_is_toplevel (toplevel) && priv->toplevel_unmap_handler == 0) {
1903 		priv->toplevel_unmap_handler = g_signal_connect (G_OBJECT (toplevel), "unmap-event",
1904 								 G_CALLBACK (toplevel_unmap), widget);
1905 	}
1906 }
1907 
1908 static gint
visibility_notify_event(GtkWidget * widget,GdkEventVisibility * event)1909 visibility_notify_event (GtkWidget *widget,
1910                          GdkEventVisibility *event)
1911 {
1912 	if (event->state == GDK_VISIBILITY_FULLY_OBSCURED)
1913 		html_image_factory_stop_animations (GTK_HTML (widget)->engine->image_factory);
1914 	else
1915 		html_image_factory_start_animations (GTK_HTML (widget)->engine->image_factory);
1916 
1917 	return FALSE;
1918 }
1919 
1920 static gint
button_press_event(GtkWidget * widget,GdkEventButton * event)1921 button_press_event (GtkWidget *widget,
1922                     GdkEventButton *event)
1923 {
1924 	GtkHTML *html;
1925 	GtkWidget *orig_widget = widget;
1926 	HTMLEngine *engine;
1927 	gint value, x, y;
1928 
1929 	/* printf ("button_press_event\n"); */
1930 
1931 	x = event->x;
1932 	y = event->y;
1933 
1934 	widget = shift_to_iframe_parent (widget, &x, &y);
1935 	html   = GTK_HTML (widget);
1936 	engine = html->engine;
1937 
1938 	if (event->button == 1 || ((event->button == 2 || event->button == 3) && html_engine_get_editable (engine))) {
1939 		html->priv->is_first_focus = FALSE;
1940 		html->priv->skip_update_cursor = TRUE;
1941 		html->priv->cursor_moved = FALSE;
1942 		gtk_widget_grab_focus (widget);
1943 	}
1944 
1945 	if (event->type == GDK_BUTTON_PRESS) {
1946 		GtkAdjustment *adjustment;
1947 		gdouble adj_value;
1948 		gdouble lower;
1949 		gdouble upper;
1950 		gdouble page_size;
1951 		gdouble step_increment;
1952 
1953 		adjustment = gtk_layout_get_vadjustment (GTK_LAYOUT (widget));
1954 		adj_value = gtk_adjustment_get_value (adjustment);
1955 		lower = gtk_adjustment_get_lower (adjustment);
1956 		upper = gtk_adjustment_get_upper (adjustment);
1957 		page_size = gtk_adjustment_get_page_size (adjustment);
1958 		step_increment = gtk_adjustment_get_step_increment (adjustment);
1959 
1960 		switch (event->button) {
1961 		case 4:
1962 			/* Mouse wheel scroll up.  */
1963 			if (event->state & GDK_CONTROL_MASK)
1964 				gtk_html_command (html, "zoom-out");
1965 			else {
1966 				value = adj_value - step_increment * 3;
1967 
1968 				if (value < lower)
1969 					value = lower;
1970 
1971 				gtk_adjustment_set_value (adjustment, value);
1972 			}
1973 			return TRUE;
1974 		case 5:
1975 			/* Mouse wheel scroll down.  */
1976 			if (event->state & GDK_CONTROL_MASK)
1977 				gtk_html_command (html, "zoom-in");
1978 			else {
1979 				value = adj_value + step_increment * 3;
1980 
1981 				if (value > (upper - page_size))
1982 					value = upper - page_size;
1983 
1984 				gtk_adjustment_set_value (adjustment, value);
1985 			}
1986 			return TRUE;
1987 		case 2:
1988 			if (html_engine_get_editable (engine)) {
1989 				gint type;
1990 				html_engine_disable_selection (html->engine);
1991 				html_engine_jump_at (engine, x, y);
1992 				gtk_html_update_styles (html);
1993 				html->priv->selection_as_cite = event->state & GDK_SHIFT_MASK;
1994 				type = event->state & GDK_CONTROL_MASK ? 1 : 0;
1995 				gtk_clipboard_request_contents (
1996 					gtk_widget_get_clipboard (GTK_WIDGET (html), GDK_SELECTION_PRIMARY),
1997 					gdk_atom_intern (selection_targets[type].target, FALSE),
1998 					clipboard_paste_received_cb, html);
1999 				return TRUE;
2000 			}
2001 			break;
2002 		case 1:
2003 			html->in_selection_drag = TRUE;
2004 			if (html_engine_get_editable (engine)) {
2005 				HTMLObject *obj;
2006 
2007 				obj = html_engine_get_object_at (engine, x, y, NULL, FALSE);
2008 
2009 				if (obj && HTML_IS_IMAGE (obj)) {
2010 					gint ox, oy;
2011 
2012 					html_object_calc_abs_position (obj, &ox, &oy);
2013 					if (ox + obj->width - 5 <= x && oy + obj->descent - 5 <= y) {
2014 						html->priv->in_object_resize = TRUE;
2015 						html->priv->resize_object = obj;
2016 						html->in_selection_drag = FALSE;
2017 					}
2018 				}
2019 
2020 				if (html->allow_selection && !html->priv->in_object_resize)
2021 					if (!(event->state & GDK_SHIFT_MASK)
2022 					    || (!engine->mark && event->state & GDK_SHIFT_MASK))
2023 						html_engine_set_mark (engine);
2024 				html_engine_jump_at (engine, x, y);
2025 			} else {
2026 				HTMLObject *obj;
2027 				HTMLEngine *orig_e;
2028 				gint offset;
2029 				gchar *url = NULL;
2030 
2031 				orig_e = GTK_HTML (orig_widget)->engine;
2032 				obj = html_engine_get_object_at (engine, x, y, (guint *) &offset, FALSE);
2033 				if (obj && ((HTML_IS_IMAGE (obj) && HTML_IMAGE (obj)->url && *HTML_IMAGE (obj)->url)
2034 					    || (HTML_IS_TEXT (obj) && (url = html_object_get_complete_url (obj, offset))))) {
2035 					g_free (url);
2036 					html_engine_set_focus_object (orig_e, obj, offset);
2037 				} else {
2038 					html_engine_set_focus_object (orig_e, NULL, 0);
2039 					if (orig_e->caret_mode || engine->caret_mode)
2040 						html_engine_jump_at (engine, x, y);
2041 				}
2042 			}
2043 			if (html->allow_selection && !html->priv->in_object_resize) {
2044 				if (event->state & GDK_SHIFT_MASK)
2045 					html_engine_select_region (engine,
2046 								   html->selection_x1, html->selection_y1, x, y);
2047 				else {
2048 					GdkWindow *bin_window;
2049 
2050 					bin_window = gtk_layout_get_bin_window (GTK_LAYOUT (widget));
2051 					html_engine_disable_selection (engine);
2052 					if (gdk_pointer_grab (bin_window, FALSE,
2053 							      (GDK_BUTTON_RELEASE_MASK
2054 							       | GDK_BUTTON_MOTION_MASK
2055 							       | GDK_POINTER_MOTION_HINT_MASK),
2056 							      NULL, NULL, event->time) == 0) {
2057 						html->selection_x1 = x;
2058 						html->selection_y1 = y;
2059 					}
2060 				}
2061 			}
2062 
2063 			engine->selection_mode = FALSE;
2064 			if (html_engine_get_editable (engine))
2065 				gtk_html_update_styles (html);
2066 			break;
2067 		default:
2068 			break;
2069 		}
2070 	} else if (event->button == 1 && html->allow_selection) {
2071 		if (event->type == GDK_2BUTTON_PRESS) {
2072 			html->in_selection_drag = FALSE;
2073 			gtk_html_select_word (html);
2074 			html->in_selection = TRUE;
2075 		}
2076 		else if (event->type == GDK_3BUTTON_PRESS) {
2077 			html->in_selection_drag = FALSE;
2078 			gtk_html_select_line (html);
2079 			html->in_selection = TRUE;
2080 		}
2081 	}
2082 
2083 	return FALSE;
2084 }
2085 
2086 static gboolean
any_has_cursor_moved(GtkHTML * html)2087 any_has_cursor_moved (GtkHTML *html)
2088 {
2089 	while (html) {
2090 		if (html->priv->cursor_moved)
2091 			return TRUE;
2092 
2093 		html = html->iframe_parent ? GTK_HTML (html->iframe_parent) : NULL;
2094 	}
2095 
2096 	return FALSE;
2097 }
2098 
2099 static gboolean
any_has_skip_update_cursor(GtkHTML * html)2100 any_has_skip_update_cursor (GtkHTML *html)
2101 {
2102 	while (html) {
2103 		if (html->priv->skip_update_cursor)
2104 			return TRUE;
2105 
2106 		html = html->iframe_parent ? GTK_HTML (html->iframe_parent) : NULL;
2107 	}
2108 
2109 	return FALSE;
2110 }
2111 
2112 static gint
button_release_event(GtkWidget * initial_widget,GdkEventButton * event)2113 button_release_event (GtkWidget *initial_widget,
2114                       GdkEventButton *event)
2115 {
2116 	GtkWidget *widget;
2117 	GtkHTML *html;
2118 	HTMLEngine *engine;
2119 	gint x, y;
2120 	HTMLObject *focus_object;
2121 	gint focus_object_offset;
2122 
2123 	/* printf ("button_release_event\n"); */
2124 
2125 	x = event->x;
2126 	y = event->y;
2127 	widget = shift_to_iframe_parent (initial_widget, &x, &y);
2128 	html   = GTK_HTML (widget);
2129 
2130 	remove_scroll_timeout (html);
2131 	gtk_grab_remove (widget);
2132 	gdk_pointer_ungrab (event->time);
2133 
2134 	engine =  html->engine;
2135 
2136 	if (html->in_selection && !html->priv->dnd_in_progress) {
2137 		html_engine_update_selection_active_state (html->engine, html->priv->event_time);
2138 		if (html->in_selection_drag)
2139 			html_engine_select_region (engine, html->selection_x1, html->selection_y1,
2140 						   x, y);
2141 		gtk_html_update_styles (html);
2142 		update_primary_selection (html);
2143 		queue_draw (html);
2144 	}
2145 
2146 	if (event->button == 1) {
2147 
2148 		if (html->in_selection_drag && html_engine_get_editable (engine))
2149 			html_engine_jump_at (engine, x, y);
2150 
2151 		html->in_selection_drag = FALSE;
2152 
2153 		if (!html->priv->dnd_in_progress
2154 		    && html->pointer_url != NULL && !html->in_selection
2155 		    && (!gtk_html_get_editable (html) || html->priv->in_url_test_mode)) {
2156 			g_signal_emit (widget,  signals[LINK_CLICKED], 0, html->pointer_url);
2157 			focus_object = html_engine_get_focus_object (html->engine, &focus_object_offset);
2158 			if (HTML_IS_TEXT (focus_object)) {
2159 				html_text_set_link_visited (HTML_TEXT (focus_object), focus_object_offset, html->engine, TRUE);
2160 			}
2161 
2162 			if (html->priv->in_url_test_mode) {
2163 				GValue arg;
2164 				guint offset;
2165 
2166 				memset (&arg, 0, sizeof (GValue));
2167 				g_value_init (&arg, G_TYPE_STRING);
2168 				g_value_set_string (&arg, html->pointer_url);
2169 
2170 				gtk_html_editor_event (html, GTK_HTML_EDITOR_EVENT_LINK_CLICKED, &arg);
2171 
2172 				g_value_unset (&arg);
2173 
2174 				focus_object = html_engine_get_object_at (html->engine, x, y, &offset, TRUE);
2175 				if (HTML_IS_TEXT (focus_object))
2176 					html_text_set_link_visited (HTML_TEXT (focus_object), (gint) offset, html->engine, TRUE);
2177 			}
2178 
2179 			html->priv->skip_update_cursor = TRUE;
2180 		}
2181 	}
2182 
2183 	html->in_selection = FALSE;
2184 	html->priv->in_object_resize = FALSE;
2185 
2186 	return TRUE;
2187 }
2188 
2189 static void
gtk_html_keymap_direction_changed(GdkKeymap * keymap,GtkHTML * html)2190 gtk_html_keymap_direction_changed (GdkKeymap *keymap,
2191                                    GtkHTML *html)
2192 {
2193 	if (html_engine_get_editable (html->engine)) {
2194 		html_engine_edit_set_direction (html->engine, html_text_direction_pango_to_html (gdk_keymap_get_direction (keymap)));
2195 	}
2196 }
2197 
2198 static gboolean
goto_caret_anchor(GtkHTML * html)2199 goto_caret_anchor (GtkHTML *html)
2200 {
2201 	gint x = 0, y = 0;
2202 
2203 	g_return_val_if_fail (html != NULL, FALSE);
2204 	g_return_val_if_fail (GTK_IS_HTML (html), FALSE);
2205 
2206 	if (!html->priv->is_first_focus)
2207 		return FALSE;
2208 
2209 	html->priv->is_first_focus = FALSE;
2210 
2211 	if (html->priv->caret_first_focus_anchor && html_object_find_anchor (html->engine->clue, html->priv->caret_first_focus_anchor, &x, &y)) {
2212 		GtkAdjustment *adjustment;
2213 		GtkLayout *layout;
2214 		gdouble page_size;
2215 		gdouble value;
2216 
2217 		html_engine_jump_at (html->engine, x, y);
2218 
2219 		layout = GTK_LAYOUT (html->engine->widget);
2220 		adjustment = gtk_layout_get_vadjustment (layout);
2221 		page_size = gtk_adjustment_get_page_size (adjustment);
2222 		value = gtk_adjustment_get_value (adjustment);
2223 
2224 		/* scroll to the position on screen if not visible */
2225 		if (y < value || y > value + page_size)
2226 			gtk_adjustment_set_value (adjustment, y);
2227 
2228 		return TRUE;
2229 	}
2230 
2231 	return FALSE;
2232 }
2233 
2234 static gint
focus_in_event(GtkWidget * widget,GdkEventFocus * event)2235 focus_in_event (GtkWidget *widget,
2236                 GdkEventFocus *event)
2237 {
2238 	GtkHTML *html = GTK_HTML (widget);
2239 
2240 	/* printf ("focus in\n"); */
2241 	if (!html->iframe_parent) {
2242 		if (html->engine->cursor && html->engine->cursor->position == 0 && html->engine->caret_mode)
2243 			goto_caret_anchor (html);
2244 		html_engine_set_focus (html->engine, TRUE);
2245 	} else {
2246 		GtkWidget *window = gtk_widget_get_ancestor (widget, gtk_window_get_type ());
2247 		if (window)
2248 			gtk_window_set_focus (GTK_WINDOW (window), html->iframe_parent);
2249 	}
2250 
2251 	html->priv->need_im_reset = TRUE;
2252 	gtk_im_context_focus_in (html->priv->im_context);
2253 
2254 	gtk_html_keymap_direction_changed (gdk_keymap_get_for_display (gtk_widget_get_display (widget)),
2255 					   html);
2256 	g_signal_connect (gdk_keymap_get_for_display (gtk_widget_get_display (widget)),
2257 			  "direction_changed", G_CALLBACK (gtk_html_keymap_direction_changed), html);
2258 
2259 	return FALSE;
2260 }
2261 
2262 static gint
focus_out_event(GtkWidget * widget,GdkEventFocus * event)2263 focus_out_event (GtkWidget *widget,
2264                  GdkEventFocus *event)
2265 {
2266 	GtkHTML *html = GTK_HTML (widget);
2267 
2268 	html_painter_set_focus (html->engine->painter, FALSE);
2269 	html_engine_redraw_selection (html->engine);
2270 	/* printf ("focus out\n"); */
2271 	if (!html->iframe_parent) {
2272 		html_engine_set_focus (html->engine, FALSE);
2273 	}
2274 
2275 	html->priv->need_im_reset = TRUE;
2276 	gtk_im_context_focus_out (html->priv->im_context);
2277 
2278 	g_signal_handlers_disconnect_by_func (gdk_keymap_get_for_display (gtk_widget_get_display (widget)),
2279 					      gtk_html_keymap_direction_changed, html);
2280 
2281 	return FALSE;
2282 }
2283 
2284 static gint
enter_notify_event(GtkWidget * widget,GdkEventCrossing * event)2285 enter_notify_event (GtkWidget *widget,
2286                     GdkEventCrossing *event)
2287 {
2288 	GdkWindow *window;
2289 	gint x, y;
2290 
2291 	x = event->x;
2292 	y = event->y;
2293 	window = gtk_widget_get_window (widget);
2294 	widget = shift_to_iframe_parent (widget, &x, &y);
2295 
2296 	mouse_change_pos (widget, window, x, y, event->state);
2297 
2298 	return TRUE;
2299 }
2300 
2301 /* X11 selection support.  */
2302 
2303 static const gchar *
utf16_order(gboolean swap)2304 utf16_order (gboolean swap)
2305 {
2306 	gboolean be;
2307 
2308 	/*
2309 	 * FIXME owen tells me this logic probably isn't needed
2310 	 * because smart iconvs will notice the BOM and act accordingly
2311 	 * I don't have as much faith in the various iconv implementations
2312 	 * so I am leaving it in for now
2313 	 */
2314 
2315 	be = G_BYTE_ORDER == G_BIG_ENDIAN;
2316 	be = swap ? be : !be;
2317 
2318 	if (be)
2319 		return "UTF-16BE";
2320 	else
2321 		return "UTF-16LE";
2322 
2323 }
2324 
2325 static gchar *
get_selection_string(GtkHTML * html,gint * len,gboolean selection,gboolean primary,gboolean html_format)2326 get_selection_string (GtkHTML *html,
2327                       gint *len,
2328                       gboolean selection,
2329                       gboolean primary,
2330                       gboolean html_format)
2331 {
2332 	HTMLObject *selection_object = NULL;
2333 	gchar *selection_string = NULL;
2334 	gboolean free_object = FALSE;
2335 
2336 	if (selection && html_engine_is_selection_active (html->engine)) {
2337 		guint selection_len;
2338 		html_engine_copy_object (html->engine, &selection_object, &selection_len);
2339 		free_object = TRUE;
2340 	} else {
2341 		if (primary) {
2342 			if (html->engine->primary) {
2343 				selection_object = html->engine->primary;
2344 			}
2345 		} else	/* CLIPBOARD */ {
2346 			if (html->engine->clipboard) {
2347 				selection_object = html->engine->clipboard;
2348 			}
2349 		}
2350 	}
2351 
2352 	if (html_format) {
2353 		if (selection_object) {
2354 			HTMLEngineSaveState *state;
2355 			GString *buffer;
2356 
2357 			state = html_engine_save_buffer_new (html->engine, TRUE);
2358 			buffer = (GString *) state->user_data;
2359 
2360 			html_object_save (selection_object, state);
2361 			g_string_append_unichar (buffer, 0x0000);
2362 
2363 			if (len)
2364 				*len = buffer->len;
2365 			selection_string = html_engine_save_buffer_free (state, FALSE);
2366 		}
2367 	} else {
2368 		if (selection_object)
2369 			selection_string = html_object_get_selection_string (selection_object, html->engine);
2370 		if (len && selection_string)
2371 			*len = strlen (selection_string);
2372 	}
2373 
2374 	if (selection_object && free_object)
2375 		html_object_destroy (selection_object);
2376 
2377 	return selection_string;
2378 }
2379 
2380 /* returned pointer should be freed with g_free */
2381 gchar *
gtk_html_get_selection_html(GtkHTML * html,gint * len)2382 gtk_html_get_selection_html (GtkHTML *html,
2383                              gint *len)
2384 {
2385 	return get_selection_string (html, len, TRUE, FALSE, TRUE);
2386 }
2387 
2388 /* returned pointer should be freed with g_free */
2389 gchar *
gtk_html_get_selection_plain_text(GtkHTML * html,gint * len)2390 gtk_html_get_selection_plain_text (GtkHTML *html,
2391                                    gint *len)
2392 {
2393 	return get_selection_string (html, len, TRUE, FALSE, FALSE);
2394 }
2395 
2396 static gchar *
utf16_to_utf8_with_bom_check(guchar * data,guint len)2397 utf16_to_utf8_with_bom_check (guchar *data,
2398                               guint len)
2399 {
2400 	const gchar *fromcode = NULL;
2401 	GError *error = NULL;
2402 	guint16 c;
2403 	gsize read_len;
2404 	gsize written_len;
2405 	gchar *utf8_ret;
2406 
2407 	/*
2408 	 * Unicode Techinical Report 20
2409 	 * (http://www.unicode.org/unicode/reports/tr20/) says to treat an
2410 	 * initial 0xfeff (ZWNBSP) as a byte order indicator so that is
2411 	 * what we do.  If there is no indicator assume it is in the default
2412 	 * order
2413 	 */
2414 
2415 	memcpy (&c, data, 2);
2416 
2417 	switch (c) {
2418 	case 0xfeff:
2419 	case 0xfffe:
2420 		fromcode = utf16_order (c == 0xfeff);
2421 		data += 2;
2422 		len  -= 2;
2423 		break;
2424 	default:
2425 		fromcode = "UTF-16";
2426 		break;
2427 	}
2428 
2429 	utf8_ret = g_convert ((gchar *) data,
2430 			      len,
2431 			      "UTF-8",
2432 			      fromcode,
2433 			      &read_len,
2434 			      &written_len,
2435 			      &error);
2436 
2437 	if (error) {
2438 		g_warning ("g_convert error: %s\n", error->message);
2439 		g_error_free (error);
2440 	}
2441 	return (utf8_ret);
2442 }
2443 
2444 /* removes useless leading BOM from UTF-8 string if present */
2445 static gchar *
utf8_filter_out_bom(gchar * str)2446 utf8_filter_out_bom (gchar *str)
2447 {
2448 	if (!str)
2449 		return NULL;
2450 
2451 	/* input is always valid, NUL-terminated UTF-8 sequence, we don't need
2452 	 * to validated it again */
2453 	if (g_utf8_get_char (str) == 0xfeff) {
2454 		gchar *out = g_strdup (g_utf8_next_char (str));
2455 		g_free (str);
2456 		return out;
2457 	}
2458 
2459 	return str;
2460 }
2461 
2462 /* Initialization.  */
2463 static void
set_focus_child(GtkContainer * containter,GtkWidget * w)2464 set_focus_child (GtkContainer *containter,
2465                  GtkWidget *w)
2466 {
2467 	HTMLObject *o = NULL;
2468 
2469 	while (w && !(o = g_object_get_data (G_OBJECT (w), "embeddedelement")))
2470 		w = gtk_widget_get_parent (w);
2471 
2472 	if (o && !html_object_is_frame (o))
2473 		html_engine_set_focus_object (GTK_HTML (containter)->engine, o, 0);
2474 
2475 	(*GTK_CONTAINER_CLASS (parent_class)->set_focus_child) (containter, w);
2476 }
2477 
2478 static gboolean
focus(GtkWidget * w,GtkDirectionType direction)2479 focus (GtkWidget *w,
2480        GtkDirectionType direction)
2481 {
2482 	HTMLEngine *e = GTK_HTML (w)->engine;
2483 
2484 	if (html_engine_get_editable (e)) {
2485 		gboolean rv;
2486 
2487 		rv = (*GTK_WIDGET_CLASS (parent_class)->focus) (w, direction);
2488 		html_engine_set_focus (GTK_HTML (w)->engine, rv);
2489 		return rv;
2490 	}
2491 
2492 	/* Reset selection. */
2493 	if (e->shift_selection || e->mark) {
2494 		html_engine_disable_selection (e);
2495 		html_engine_edit_selection_updater_schedule (e->selection_updater);
2496 		e->shift_selection = FALSE;
2497 	}
2498 
2499 	if (!gtk_widget_has_focus (w) && e->caret_mode) {
2500 		if (goto_caret_anchor (GTK_HTML (w))) {
2501 			gtk_widget_grab_focus (w);
2502 
2503 			update_primary_selection (GTK_HTML (w));
2504 			g_signal_emit (GTK_HTML (w), signals[CURSOR_CHANGED], 0);
2505 
2506 			return TRUE;
2507 		}
2508 	}
2509 
2510 	if (((e->focus_object && !(gtk_widget_has_focus (w))) || html_engine_focus (e, direction)) && e->focus_object) {
2511 		gint offset;
2512 		HTMLObject *obj = html_engine_get_focus_object (e, &offset);
2513 		gint x1, y1, x2, y2, xo, yo;
2514 
2515 		xo = e->x_offset;
2516 		yo = e->y_offset;
2517 
2518 		if (HTML_IS_TEXT (obj)) {
2519 			if (!html_text_get_link_rectangle (HTML_TEXT (obj), e->painter, offset, &x1, &y1, &x2, &y2))
2520 				return FALSE;
2521 		} else {
2522 			html_object_calc_abs_position (obj, &x1, &y1);
2523 			y2 = y1 + obj->descent;
2524 			x2 = x1 + obj->width;
2525 			y1 -= obj->ascent;
2526 		}
2527 
2528 		/* printf ("child pos: %d,%d x %d,%d\n", x1, y1, x2, y2); */
2529 
2530 		if (x2 > e->x_offset + e->width)
2531 			e->x_offset = x2 - e->width;
2532 		if (x1 < e->x_offset)
2533 			e->x_offset = x1;
2534 		if (e->width > 2 * RIGHT_BORDER && e->x_offset == x2 - e->width)
2535 			e->x_offset = MIN (x2 - e->width + RIGHT_BORDER + 1,
2536 					   html_engine_get_doc_width (e) - e->width);
2537 		if (e->width > 2 * LEFT_BORDER && e->x_offset >= x1)
2538 			e->x_offset = MAX (x1 - LEFT_BORDER, 0);
2539 
2540 		if (y2 >= e->y_offset + e->height)
2541 			e->y_offset = y2 - e->height + 1;
2542 		if (y1 < e->y_offset)
2543 			e->y_offset = y1;
2544 		if (e->height > 2 * BOTTOM_BORDER && e->y_offset == y2 - e->height + 1)
2545 			e->y_offset = MIN (y2 - e->height + BOTTOM_BORDER + 1,
2546 					   html_engine_get_doc_height (e) - e->height);
2547 		if (e->height > 2 * TOP_BORDER && e->y_offset >= y1)
2548 			e->y_offset = MAX (y1 - TOP_BORDER, 0);
2549 
2550 		if (e->x_offset != xo) {
2551 			GtkAdjustment *adjustment;
2552 
2553 			adjustment = gtk_layout_get_hadjustment (GTK_LAYOUT (w));
2554 			gtk_adjustment_set_value (adjustment, (gfloat) e->x_offset);
2555 		}
2556 		if (e->y_offset != yo) {
2557 			GtkAdjustment *adjustment;
2558 
2559 			adjustment = gtk_layout_get_vadjustment (GTK_LAYOUT (w));
2560 			gtk_adjustment_set_value (adjustment, (gfloat) e->y_offset);
2561 		}
2562 		/* printf ("engine pos: %d,%d x %d,%d\n",
2563 		 * e->x_offset, e->y_offset, e->x_offset + e->width, e->y_offset + e->height); */
2564 
2565 		if (!gtk_widget_has_focus (w) && !html_object_is_embedded (obj))
2566 			gtk_widget_grab_focus (w);
2567 		if (e->caret_mode) {
2568 			html_engine_jump_to_object (e, obj, offset);
2569 		}
2570 
2571 		update_primary_selection (GTK_HTML (w));
2572 		g_signal_emit (GTK_HTML (w), signals[CURSOR_CHANGED], 0);
2573 
2574 		return TRUE;
2575 	}
2576 
2577 	return FALSE;
2578 }
2579 
2580 /* dnd begin */
2581 
2582 static void
drag_begin(GtkWidget * widget,GdkDragContext * context)2583 drag_begin (GtkWidget *widget,
2584             GdkDragContext *context)
2585 {
2586 	HTMLInterval *i;
2587 	HTMLObject *o;
2588 
2589 	/* printf ("drag_begin\n"); */
2590 	GTK_HTML (widget)->priv->dnd_real_object = o = GTK_HTML (widget)->priv->dnd_object;
2591 	GTK_HTML (widget)->priv->dnd_real_object_offset = GTK_HTML (widget)->priv->dnd_object_offset;
2592 	GTK_HTML (widget)->priv->dnd_in_progress = TRUE;
2593 
2594 	i = html_interval_new (o, o, 0, html_object_get_length (o));
2595 	html_engine_select_interval (GTK_HTML (widget)->engine, i);
2596 }
2597 
2598 static void
drag_end(GtkWidget * widget,GdkDragContext * context)2599 drag_end (GtkWidget *widget,
2600           GdkDragContext *context)
2601 {
2602 	GtkHTML *html;
2603 
2604 	/* printf ("drag_end\n"); */
2605 	g_return_if_fail (GTK_IS_HTML (widget));
2606 
2607 	html = GTK_HTML (widget);
2608 	if (html->priv)
2609 		html->priv->dnd_in_progress = FALSE;
2610 }
2611 
2612 static void
drag_data_get(GtkWidget * widget,GdkDragContext * context,GtkSelectionData * selection_data,guint info,guint time)2613 drag_data_get (GtkWidget *widget,
2614                GdkDragContext *context,
2615                GtkSelectionData *selection_data,
2616                guint info,
2617                guint time)
2618 {
2619 	/* printf ("drag_data_get\n"); */
2620 	switch (info) {
2621 	case DND_TARGET_TYPE_MOZILLA_URL:
2622 	case DND_TARGET_TYPE_TEXT_URI_LIST:
2623 		/* printf ("\ttext/uri-list\n"); */
2624 	case DND_TARGET_TYPE_TEXT_HTML:
2625 	case DND_TARGET_TYPE_TEXT_PLAIN:
2626 	case DND_TARGET_TYPE_UTF8_STRING:
2627 	case DND_TARGET_TYPE_STRING: {
2628 		HTMLObject *obj = GTK_HTML (widget)->priv->dnd_real_object;
2629 		gint offset = GTK_HTML (widget)->priv->dnd_real_object_offset;
2630 		const gchar *url, *target;
2631 		gchar *complete_url;
2632 
2633 		/* printf ("\ttext/plain\n"); */
2634 		if (obj) {
2635 			/* printf ("obj %p\n", obj); */
2636 			url = html_object_get_url (obj, offset);
2637 			target = html_object_get_target (obj, offset);
2638 			if (url && *url) {
2639 
2640 				complete_url = g_strconcat (url, target && *target ? "#" : NULL, target, NULL);
2641 
2642 				if (info == DND_TARGET_TYPE_MOZILLA_URL) {
2643 					/* MOZ_URL is in UTF-16 but in format 8. BROKEN!
2644 					 *
2645 					 * The data contains the URL, a \n, then the
2646 					 * title of the web page.
2647 					 */
2648 					gchar *utf16;
2649 					gchar *utf8;
2650 					gsize written_len;
2651 					GdkAtom atom;
2652 
2653 					if (HTML_IS_TEXT (obj)) {
2654 						Link *link = html_text_get_link_at_offset (HTML_TEXT (obj), offset);
2655 						gchar *text;
2656 
2657 						g_return_if_fail (link);
2658 						text = g_strndup (HTML_TEXT (obj)->text + link->start_index, link->end_index - link->start_index);
2659 						utf8 = g_strconcat (complete_url, "\n", text, NULL);
2660 					} else
2661 						utf8 = g_strconcat (complete_url, "\n", complete_url, NULL);
2662 
2663 					utf16 = g_convert (utf8, strlen (utf8), "UTF-16", "UTF-8", NULL, &written_len, NULL);
2664 					atom = gtk_selection_data_get_target (selection_data);
2665 					gtk_selection_data_set (selection_data, atom, 8,
2666 								(guchar *) utf16, written_len);
2667 					g_free (utf8);
2668 					g_free (complete_url);
2669 					GTK_HTML (widget)->priv->dnd_url = utf16;
2670 				} else {
2671 					GdkAtom atom;
2672 
2673 					atom = gtk_selection_data_get_target (selection_data);
2674 					gtk_selection_data_set (selection_data, atom, 8,
2675 								(guchar *) complete_url, strlen (complete_url));
2676 					/* printf ("complete URL %s\n", complete_url); */
2677 					GTK_HTML (widget)->priv->dnd_url = complete_url;
2678 				}
2679 			}
2680 		}
2681 	}
2682 	break;
2683 	}
2684 }
2685 
2686 static void
drag_data_delete(GtkWidget * widget,GdkDragContext * context)2687 drag_data_delete (GtkWidget *widget,
2688                   GdkDragContext *context)
2689 {
2690 	g_free (GTK_HTML (widget)->priv->dnd_url);
2691 	GTK_HTML (widget)->priv->dnd_url = NULL;
2692 }
2693 
2694 static gchar *
next_uri(guchar ** uri_list,gint * len,gint * list_len)2695 next_uri (guchar **uri_list,
2696           gint *len,
2697           gint *list_len)
2698 {
2699 	guchar *uri, *begin;
2700 
2701 	begin = *uri_list;
2702 	*len = 0;
2703 	while (**uri_list && **uri_list != '\n' && **uri_list != '\r' && *list_len) {
2704 		(*uri_list) ++;
2705 		(*len) ++;
2706 		(*list_len) --;
2707 	}
2708 
2709 	uri = (guchar *) g_strndup ((gchar *) begin, *len);
2710 
2711 	while ((!**uri_list || **uri_list == '\n' || **uri_list == '\r') && *list_len) {
2712 		(*uri_list) ++;
2713 		(*list_len) --;
2714 	}
2715 
2716 	return (gchar *) uri;
2717 }
2718 
2719 static HTMLObject *
new_img_obj_from_uri(HTMLEngine * e,gchar * uri,gchar * title,gint len)2720 new_img_obj_from_uri (HTMLEngine *e,
2721                       gchar *uri,
2722                       gchar *title,
2723                       gint len)
2724 {
2725 	if (!strncmp (uri, "file:", 5)) {
2726 		if (!HTML_IS_PLAIN_PAINTER (e->painter)) {
2727 			GdkPixbuf *pixbuf = NULL;
2728 			gchar *img_path = g_filename_from_uri (uri, NULL, NULL);
2729 			if (img_path) {
2730 				pixbuf = gdk_pixbuf_new_from_file (img_path, NULL);
2731 				g_free (img_path);
2732 			}
2733 			if (pixbuf) {
2734 				g_object_unref (pixbuf);
2735 				return html_image_new (html_engine_get_image_factory (e), uri,
2736 						       NULL, NULL, -1, -1, FALSE, FALSE, 0,
2737 						       html_colorset_get_color (e->settings->color_set, HTMLTextColor),
2738 						       HTML_VALIGN_BOTTOM, TRUE);
2739 			}
2740 		}
2741 	}
2742 	return NULL;
2743 }
2744 
2745 static void
move_before_paste(GtkWidget * widget,gint x,gint y)2746 move_before_paste (GtkWidget *widget,
2747                    gint x,
2748                    gint y)
2749 {
2750 	HTMLEngine *engine = GTK_HTML (widget)->engine;
2751 
2752 	if (html_engine_is_selection_active (engine)) {
2753 		HTMLObject *obj;
2754 		guint offset;
2755 
2756 		obj = html_engine_get_object_at (engine, x, y, &offset, FALSE);
2757 		if (!html_engine_point_in_selection (engine, obj, offset)) {
2758 			html_engine_disable_selection (engine);
2759 			html_engine_edit_selection_updater_update_now (engine->selection_updater);
2760 		}
2761 	}
2762 	if (!html_engine_is_selection_active (engine)) {
2763 
2764 		html_engine_jump_at (engine, x, y);
2765 		gtk_html_update_styles (GTK_HTML (widget));
2766 	}
2767 }
2768 
2769 static void
drag_data_received(GtkWidget * widget,GdkDragContext * context,gint x,gint y,GtkSelectionData * selection_data,guint info,guint time)2770 drag_data_received (GtkWidget *widget,
2771                     GdkDragContext *context,
2772                     gint x,
2773                     gint y,
2774                     GtkSelectionData *selection_data,
2775                     guint info,
2776                     guint time)
2777 {
2778 	HTMLEngine *engine = GTK_HTML (widget)->engine;
2779 	GdkWindow *bin_window;
2780 	gboolean pasted = FALSE;
2781 	const guchar *data;
2782 	gint length;
2783 
2784 	/* printf ("drag data received at %d,%d\n", x, y); */
2785 
2786 	data = gtk_selection_data_get_data (selection_data);
2787 	length = gtk_selection_data_get_length (selection_data);
2788 
2789 	if (!data || length < 0 || !html_engine_get_editable (engine))
2790 		return;
2791 
2792 	bin_window = gtk_layout_get_bin_window (GTK_LAYOUT (widget));
2793 	gdk_window_get_pointer (bin_window, &x, &y, NULL);
2794 	move_before_paste (widget, x, y);
2795 
2796 	switch (info) {
2797 	case DND_TARGET_TYPE_TEXT_PLAIN:
2798 	case DND_TARGET_TYPE_UTF8_STRING:
2799 	case DND_TARGET_TYPE_STRING:
2800 	case DND_TARGET_TYPE_TEXT_HTML:
2801 		clipboard_paste_received_cb (
2802 			gtk_widget_get_clipboard (GTK_WIDGET (widget), GDK_SELECTION_PRIMARY),
2803 			selection_data, widget);
2804 		pasted = TRUE;
2805 		break;
2806 	case DND_TARGET_TYPE_MOZILLA_URL  :
2807 		break;
2808 	case DND_TARGET_TYPE_TEXT_URI_LIST:
2809 		if (!HTML_IS_PLAIN_PAINTER (engine->painter)) {
2810 		 HTMLObject *obj;
2811 		 gint list_len, len;
2812 		 gchar *uri;
2813 		 html_undo_level_begin (engine->undo, "Dropped URI(s)", "Remove Dropped URI(s)");
2814 		 list_len = length;
2815 		 do {
2816 			 uri = next_uri ((guchar **) &data, &len, &list_len);
2817 			 obj = new_img_obj_from_uri (engine, uri, NULL, -1);
2818 			 if (obj) {
2819 				 html_engine_paste_object (engine, obj, html_object_get_length (obj));
2820 				 pasted = TRUE;
2821 			 }
2822 		 } while (list_len);
2823 		 html_undo_level_end (engine->undo, engine);
2824 	}
2825 	break;
2826 	}
2827 	gtk_drag_finish (context, pasted, FALSE, time);
2828 }
2829 
2830 static gboolean
drag_motion(GtkWidget * widget,GdkDragContext * context,gint x,gint y,guint time)2831 drag_motion (GtkWidget *widget,
2832              GdkDragContext *context,
2833              gint x,
2834              gint y,
2835              guint time)
2836 {
2837 	GdkWindow *window;
2838 	GdkWindow *bin_window;
2839 
2840 	if (!gtk_html_get_editable (GTK_HTML (widget)))
2841 		return FALSE;
2842 
2843 	window = gtk_widget_get_window (widget);
2844 	bin_window = gtk_layout_get_bin_window (GTK_LAYOUT (widget));
2845 	gdk_window_get_pointer (bin_window, &x, &y, NULL);
2846 
2847 	html_engine_disable_selection (GTK_HTML (widget)->engine);
2848 	html_engine_jump_at (GTK_HTML (widget)->engine, x, y);
2849 	html_engine_show_cursor (GTK_HTML (widget)->engine);
2850 
2851 	mouse_change_pos (widget, window, x, y, 0);
2852 
2853 	return TRUE;
2854 }
2855 
2856 /* dnd end */
2857 
2858 static void
settings_key_theme_changed(GSettings * settings,const gchar * key,GtkHTMLClass * html_class)2859 settings_key_theme_changed (GSettings *settings,
2860                             const gchar *key,
2861                             GtkHTMLClass *html_class)
2862 {
2863 	gchar *key_theme;
2864 
2865 	key_theme = g_settings_get_string (settings, key);
2866 	html_class->use_emacs_bindings = (g_strcmp0 (key_theme, "Emacs") == 0);
2867 	g_free (key_theme);
2868 }
2869 
2870 static void
settings_monospace_font_name_changed(GSettings * settings,const gchar * key,GtkHTML * html)2871 settings_monospace_font_name_changed (GSettings *settings,
2872                                       const gchar *key,
2873                                       GtkHTML *html)
2874 {
2875 	if (html->engine != NULL && html->engine->painter != NULL) {
2876 		gtk_html_set_fonts (html, html->engine->painter);
2877 		html_engine_refresh_fonts (html->engine);
2878 	}
2879 }
2880 
2881 static void
settings_cursor_blink_changed(GSettings * settings,const gchar * key,GtkHTMLClass * html_class)2882 settings_cursor_blink_changed (GSettings *settings,
2883                                const gchar *key,
2884                                GtkHTMLClass *html_class)
2885 {
2886 	gint blink_time = 0;
2887 
2888 	if (g_settings_get_boolean (settings, "cursor-blink"))
2889 		blink_time = g_settings_get_int (settings, "cursor-blink-time");
2890 
2891 	html_engine_set_cursor_blink_timeout (blink_time / 2);
2892 }
2893 
2894 static void
gtk_html_direction_changed(GtkWidget * widget,GtkTextDirection previous_dir)2895 gtk_html_direction_changed (GtkWidget *widget,
2896                             GtkTextDirection previous_dir)
2897 {
2898 	GtkHTML *html = GTK_HTML (widget);
2899 
2900 	if (html->engine->clue) {
2901 		HTMLDirection old_direction = html_object_get_direction (html->engine->clue);
2902 
2903 		switch (gtk_widget_get_direction (widget)) {
2904 		case GTK_TEXT_DIR_NONE:
2905 			HTML_CLUEV (html->engine->clue)->dir = HTML_DIRECTION_DERIVED;
2906 			break;
2907 		case GTK_TEXT_DIR_LTR:
2908 			HTML_CLUEV (html->engine->clue)->dir = HTML_DIRECTION_LTR;
2909 			break;
2910 		case GTK_TEXT_DIR_RTL:
2911 			HTML_CLUEV (html->engine->clue)->dir = HTML_DIRECTION_RTL;
2912 			break;
2913 		}
2914 
2915 		if (old_direction != html_object_get_direction (html->engine->clue))
2916 			html_engine_schedule_update (html->engine);
2917 	}
2918 
2919 	GTK_WIDGET_CLASS (parent_class)->direction_changed (widget, previous_dir);
2920 }
2921 
2922 static void
gtk_html_class_init(GtkHTMLClass * klass)2923 gtk_html_class_init (GtkHTMLClass *klass)
2924 {
2925 	GObjectClass      *object_class;
2926 	GtkHTMLClass      *html_class;
2927 	GtkWidgetClass    *widget_class;
2928 	GtkContainerClass *container_class;
2929 	gchar *filename;
2930 	GSettings *settings;
2931 
2932 	g_type_class_add_private (klass, sizeof (GtkHTMLPrivate));
2933 
2934 	html_class = (GtkHTMLClass *) klass;
2935 	object_class = (GObjectClass *) klass;
2936 	widget_class = (GtkWidgetClass *) klass;
2937 	container_class = (GtkContainerClass *) klass;
2938 
2939 	parent_class = g_type_class_peek_parent (klass);
2940 
2941 	signals[TITLE_CHANGED] =
2942 		g_signal_new ("title_changed",
2943 			      G_TYPE_FROM_CLASS (object_class),
2944 			      G_SIGNAL_RUN_FIRST,
2945 			      G_STRUCT_OFFSET (GtkHTMLClass, title_changed),
2946 			      NULL, NULL,
2947 			      g_cclosure_marshal_VOID__STRING,
2948 			      G_TYPE_NONE, 1,
2949 			      G_TYPE_STRING);
2950 	signals[URL_REQUESTED] =
2951 		g_signal_new ("url_requested",
2952 			      G_TYPE_FROM_CLASS (object_class),
2953 			      G_SIGNAL_RUN_LAST,
2954 			      G_STRUCT_OFFSET (GtkHTMLClass, url_requested),
2955 			      NULL, NULL,
2956 			      html_g_cclosure_marshal_VOID__STRING_POINTER,
2957 			      G_TYPE_NONE, 2,
2958 			      G_TYPE_STRING,
2959 			      G_TYPE_POINTER);
2960 	signals[LOAD_DONE] =
2961 		g_signal_new ("load_done",
2962 			      G_TYPE_FROM_CLASS (object_class),
2963 			      G_SIGNAL_RUN_FIRST,
2964 			      G_STRUCT_OFFSET (GtkHTMLClass, load_done),
2965 			      NULL, NULL,
2966 			      g_cclosure_marshal_VOID__VOID,
2967 			      G_TYPE_NONE, 0);
2968 	signals[LINK_CLICKED] =
2969 		g_signal_new ("link_clicked",
2970 			      G_TYPE_FROM_CLASS (object_class),
2971 			      G_SIGNAL_RUN_FIRST,
2972 			      G_STRUCT_OFFSET (GtkHTMLClass, link_clicked),
2973 			      NULL, NULL,
2974 			      g_cclosure_marshal_VOID__STRING,
2975 			      G_TYPE_NONE, 1,
2976 			      G_TYPE_STRING);
2977 	signals[SET_BASE] =
2978 		g_signal_new ("set_base",
2979 			      G_TYPE_FROM_CLASS (object_class),
2980 			      G_SIGNAL_RUN_FIRST,
2981 			      G_STRUCT_OFFSET (GtkHTMLClass, set_base),
2982 			      NULL, NULL,
2983 			      g_cclosure_marshal_VOID__STRING,
2984 			      G_TYPE_NONE, 1,
2985 			      G_TYPE_STRING);
2986 	signals[SET_BASE_TARGET] =
2987 		g_signal_new ("set_base_target",
2988 			      G_TYPE_FROM_CLASS (object_class),
2989 			      G_SIGNAL_RUN_FIRST,
2990 			      G_STRUCT_OFFSET (GtkHTMLClass, set_base_target),
2991 			      NULL, NULL,
2992 			      g_cclosure_marshal_VOID__STRING,
2993 			      G_TYPE_NONE, 1,
2994 			      G_TYPE_STRING);
2995 
2996 	signals[ON_URL] =
2997 		g_signal_new ("on_url",
2998 			      G_TYPE_FROM_CLASS (object_class),
2999 			      G_SIGNAL_RUN_FIRST,
3000 			      G_STRUCT_OFFSET (GtkHTMLClass, on_url),
3001 			      NULL, NULL,
3002 			      g_cclosure_marshal_VOID__STRING,
3003 			      G_TYPE_NONE, 1,
3004 			      G_TYPE_STRING);
3005 
3006 	signals[REDIRECT] =
3007 		g_signal_new ("redirect",
3008 			      G_TYPE_FROM_CLASS (object_class),
3009 			      G_SIGNAL_RUN_FIRST,
3010 			      G_STRUCT_OFFSET (GtkHTMLClass, redirect),
3011 			      NULL, NULL,
3012 			      html_g_cclosure_marshal_VOID__POINTER_INT,
3013 			      G_TYPE_NONE, 2,
3014 			      G_TYPE_STRING,
3015 			      G_TYPE_INT);
3016 
3017 	signals[SUBMIT] =
3018 		g_signal_new ("submit",
3019 			      G_TYPE_FROM_CLASS (object_class),
3020 			      G_SIGNAL_RUN_FIRST,
3021 			      G_STRUCT_OFFSET (GtkHTMLClass, submit),
3022 			      NULL, NULL,
3023 			      html_g_cclosure_marshal_VOID__STRING_STRING_STRING,
3024 			      G_TYPE_NONE, 3,
3025 			      G_TYPE_STRING,
3026 			      G_TYPE_STRING,
3027 			      G_TYPE_STRING);
3028 
3029 	signals[OBJECT_REQUESTED] =
3030 		g_signal_new ("object_requested",
3031 			      G_TYPE_FROM_CLASS (object_class),
3032 			      G_SIGNAL_RUN_LAST,
3033 			      G_STRUCT_OFFSET (GtkHTMLClass, object_requested),
3034 			      NULL, NULL,
3035 			      html_g_cclosure_marshal_BOOL__OBJECT,
3036 			      G_TYPE_BOOLEAN, 1,
3037 			      G_TYPE_OBJECT);
3038 
3039 	signals[CURRENT_PARAGRAPH_STYLE_CHANGED] =
3040 		g_signal_new ("current_paragraph_style_changed",
3041 			      G_TYPE_FROM_CLASS (object_class),
3042 			      G_SIGNAL_RUN_FIRST,
3043 			      G_STRUCT_OFFSET (GtkHTMLClass, current_paragraph_style_changed),
3044 			      NULL, NULL,
3045 			      g_cclosure_marshal_VOID__INT,
3046 			      G_TYPE_NONE, 1,
3047 			      G_TYPE_INT);
3048 
3049 	signals[CURRENT_PARAGRAPH_INDENTATION_CHANGED] =
3050 		g_signal_new ("current_paragraph_indentation_changed",
3051 			      G_TYPE_FROM_CLASS (object_class),
3052 			      G_SIGNAL_RUN_FIRST,
3053 			      G_STRUCT_OFFSET (GtkHTMLClass, current_paragraph_indentation_changed),
3054 			      NULL, NULL,
3055 			      g_cclosure_marshal_VOID__INT,
3056 			      G_TYPE_NONE, 1,
3057 			      G_TYPE_INT);
3058 
3059 	signals[CURRENT_PARAGRAPH_ALIGNMENT_CHANGED] =
3060 		g_signal_new ("current_paragraph_alignment_changed",
3061 			      G_TYPE_FROM_CLASS (object_class),
3062 			      G_SIGNAL_RUN_FIRST,
3063 			      G_STRUCT_OFFSET (GtkHTMLClass, current_paragraph_alignment_changed),
3064 			      NULL, NULL,
3065 			      g_cclosure_marshal_VOID__INT,
3066 			      G_TYPE_NONE, 1,
3067 			      G_TYPE_INT);
3068 
3069 	signals[INSERTION_FONT_STYLE_CHANGED] =
3070 		g_signal_new ("insertion_font_style_changed",
3071 			      G_TYPE_FROM_CLASS (object_class),
3072 			      G_SIGNAL_RUN_FIRST,
3073 			      G_STRUCT_OFFSET (GtkHTMLClass, insertion_font_style_changed),
3074 			      NULL, NULL,
3075 			      g_cclosure_marshal_VOID__INT,
3076 			      G_TYPE_NONE, 1,
3077 			      G_TYPE_INT);
3078 
3079 	signals[INSERTION_COLOR_CHANGED] =
3080 		g_signal_new ("insertion_color_changed",
3081 			      G_TYPE_FROM_CLASS (object_class),
3082 			      G_SIGNAL_RUN_FIRST,
3083 			      G_STRUCT_OFFSET (GtkHTMLClass, insertion_color_changed),
3084 			      NULL, NULL,
3085 			      g_cclosure_marshal_VOID__POINTER,
3086 			      G_TYPE_NONE, 1,
3087 			      G_TYPE_POINTER);
3088 
3089 	signals[SIZE_CHANGED] =
3090 		g_signal_new ("size_changed",
3091 			      G_TYPE_FROM_CLASS (object_class),
3092 			      G_SIGNAL_RUN_FIRST,
3093 			      G_STRUCT_OFFSET (GtkHTMLClass, size_changed),
3094 			      NULL, NULL,
3095 			      g_cclosure_marshal_VOID__VOID,
3096 			      G_TYPE_NONE, 0);
3097 	signals[IFRAME_CREATED] =
3098 		g_signal_new ("iframe_created",
3099 			      G_TYPE_FROM_CLASS (object_class),
3100 			      G_SIGNAL_RUN_FIRST,
3101 			      G_STRUCT_OFFSET (GtkHTMLClass, iframe_created),
3102 			      NULL, NULL,
3103 			      g_cclosure_marshal_VOID__OBJECT,
3104 			      G_TYPE_NONE, 1,
3105 			      GTK_TYPE_HTML);
3106 
3107 	signals[SCROLL] =
3108 		g_signal_new ("scroll",
3109 			      G_TYPE_FROM_CLASS (object_class),
3110 			      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
3111 			      G_STRUCT_OFFSET (GtkHTMLClass, scroll),
3112 			      NULL, NULL,
3113 			      html_g_cclosure_marshal_VOID__ENUM_ENUM_FLOAT,
3114 			      G_TYPE_NONE, 3,
3115 			      GTK_TYPE_ORIENTATION,
3116 			      GTK_TYPE_SCROLL_TYPE, G_TYPE_FLOAT);
3117 
3118 	signals[CURSOR_MOVE] =
3119 		g_signal_new ("cursor_move",
3120 			      G_TYPE_FROM_CLASS (object_class),
3121 			      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
3122 			      G_STRUCT_OFFSET (GtkHTMLClass, cursor_move),
3123 			      NULL, NULL,
3124 			      html_g_cclosure_marshal_VOID__ENUM_ENUM,
3125 			      G_TYPE_NONE, 2, GTK_TYPE_DIRECTION_TYPE, GTK_TYPE_HTML_CURSOR_SKIP);
3126 
3127 	signals[COMMAND] =
3128 		g_signal_new ("command",
3129 			      G_TYPE_FROM_CLASS (object_class),
3130 			      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
3131 			      G_STRUCT_OFFSET (GtkHTMLClass, command),
3132 			      NULL, NULL,
3133 			      html_g_cclosure_marshal_BOOL__ENUM,
3134 			      G_TYPE_BOOLEAN, 1, GTK_TYPE_HTML_COMMAND);
3135 
3136 	signals[CURSOR_CHANGED] =
3137 		g_signal_new ("cursor_changed",
3138 			      G_TYPE_FROM_CLASS (object_class),
3139 			      G_SIGNAL_RUN_FIRST,
3140 			      G_STRUCT_OFFSET (GtkHTMLClass, cursor_changed),
3141 			      NULL, NULL,
3142 			      g_cclosure_marshal_VOID__VOID,
3143 			      G_TYPE_NONE, 0);
3144 
3145 	signals[OBJECT_INSERTED] =
3146 		g_signal_new ("object_inserted",
3147 			      G_TYPE_FROM_CLASS (object_class),
3148 			      G_SIGNAL_RUN_FIRST,
3149 			      G_STRUCT_OFFSET (GtkHTMLClass, object_inserted),
3150 			      NULL, NULL,
3151 			      html_g_cclosure_marshal_VOID__INT_INT,
3152 			      G_TYPE_NONE, 2,
3153 			      G_TYPE_INT, G_TYPE_INT);
3154 
3155 	signals[OBJECT_DELETE] =
3156 		g_signal_new ("object_delete",
3157 			      G_TYPE_FROM_CLASS (object_class),
3158 			      G_SIGNAL_RUN_FIRST,
3159 			      G_STRUCT_OFFSET (GtkHTMLClass, object_delete),
3160 			      NULL, NULL,
3161 			      html_g_cclosure_marshal_VOID__INT_INT,
3162 			      G_TYPE_NONE, 2,
3163 			      G_TYPE_INT, G_TYPE_INT);
3164 	object_class->dispose = dispose;
3165 
3166 #ifdef USE_PROPS
3167 	object_class->get_property = gtk_html_get_property;
3168 	object_class->set_property = gtk_html_set_property;
3169 
3170 	g_object_class_install_property (object_class,
3171 					 PROP_EDITABLE,
3172 					 g_param_spec_boolean ("editable",
3173 							       "Editable",
3174 							       "Whether the html can be edited",
3175 							       FALSE,
3176 							       G_PARAM_READABLE | G_PARAM_WRITABLE));
3177 	g_object_class_install_property (object_class,
3178 					 PROP_TITLE,
3179 					 g_param_spec_string ("title",
3180 							      "Document Title",
3181 							      "The title of the current document",
3182 							      NULL,
3183 							      G_PARAM_WRITABLE | G_PARAM_READABLE));
3184 	g_object_class_install_property (object_class,
3185 					 PROP_DOCUMENT_BASE,
3186 					 g_param_spec_string ("document_base",
3187 							      "Document Base",
3188 							      "The base URL for relative references",
3189 							      NULL,
3190 							      G_PARAM_WRITABLE | G_PARAM_READABLE));
3191 	g_object_class_install_property (object_class,
3192 					 PROP_TARGET_BASE,
3193 					 g_param_spec_string ("target_base",
3194 							      "Target Base",
3195 							      "The base URL of the target frame",
3196 							      NULL,
3197 							      G_PARAM_WRITABLE | G_PARAM_READABLE));
3198 
3199 #endif
3200 
3201 	gtk_widget_class_install_style_property (widget_class,
3202 						 g_param_spec_string ("fixed_font_name",
3203 								     "Fixed Width Font",
3204 								     "The Monospace font to use for typewriter text",
3205 								     NULL,
3206 								     G_PARAM_READABLE));
3207 
3208 	gtk_widget_class_install_style_property (widget_class,
3209 						 g_param_spec_boxed ("link_color",
3210 								     "New Link Color",
3211 								     "The color of new link elements",
3212 								     GDK_TYPE_COLOR,
3213 								     G_PARAM_READABLE));
3214 	gtk_widget_class_install_style_property (widget_class,
3215 						 g_param_spec_boxed ("vlink_color",
3216 								     "Visited Link Color",
3217 								     "The color of visited link elements",
3218 								     GDK_TYPE_COLOR,
3219 								     G_PARAM_READABLE));
3220 	gtk_widget_class_install_style_property (widget_class,
3221 						 g_param_spec_boxed ("alink_color",
3222 								     "Active Link Color",
3223 								     "The color of active link elements",
3224 								     GDK_TYPE_COLOR,
3225 								     G_PARAM_READABLE));
3226 	gtk_widget_class_install_style_property (widget_class,
3227 						 g_param_spec_boxed ("spell_error_color",
3228 								     "Spelling Error Color",
3229 								     "The color of the spelling error markers",
3230 								     GDK_TYPE_COLOR,
3231 								     G_PARAM_READABLE));
3232 	gtk_widget_class_install_style_property (widget_class,
3233 						 g_param_spec_boxed ("cite_color",
3234 								     "Cite Quotation Color",
3235 								     "The color of the cited text",
3236 								     GDK_TYPE_COLOR,
3237 								     G_PARAM_READABLE));
3238 
3239 	widget_class->realize = realize;
3240 	widget_class->unrealize = unrealize;
3241 	widget_class->style_updated = style_updated;
3242 	widget_class->key_press_event = key_press_event;
3243 	widget_class->key_release_event = key_release_event;
3244 	widget_class->draw = draw;
3245 	widget_class->get_preferred_width = gtk_html_get_preferred_width;
3246 	widget_class->get_preferred_height = gtk_html_get_preferred_height;
3247 	widget_class->size_allocate = size_allocate;
3248 	widget_class->motion_notify_event = motion_notify_event;
3249 	widget_class->visibility_notify_event = visibility_notify_event;
3250 	widget_class->hierarchy_changed = hierarchy_changed;
3251 	widget_class->button_press_event = button_press_event;
3252 	widget_class->button_release_event = button_release_event;
3253 	widget_class->focus_in_event = focus_in_event;
3254 	widget_class->focus_out_event = focus_out_event;
3255 	widget_class->enter_notify_event = enter_notify_event;
3256 	widget_class->drag_data_get = drag_data_get;
3257 	widget_class->drag_data_delete = drag_data_delete;
3258 	widget_class->drag_begin = drag_begin;
3259 	widget_class->drag_end = drag_end;
3260 	widget_class->drag_data_received = drag_data_received;
3261 	widget_class->drag_motion = drag_motion;
3262 	widget_class->focus = focus;
3263 	widget_class->direction_changed = gtk_html_direction_changed;
3264 
3265 	container_class->set_focus_child = set_focus_child;
3266 
3267 	html_class->scroll            = scroll;
3268 	html_class->cursor_move       = cursor_move;
3269 	html_class->command           = command;
3270 	html_class->properties        = gtk_html_class_properties_new ();
3271 
3272 	add_bindings (klass);
3273 	gtk_widget_class_set_accessible_type (widget_class, G_TYPE_GTK_HTML_A11Y);
3274 
3275 	filename = g_build_filename (PREFIX, "share", GTKHTML_RELEASE_STRING, "keybindingsrc.emacs", NULL);
3276 	gtk_rc_parse (filename);
3277 	g_free (filename);
3278 	html_class->emacs_bindings = gtk_binding_set_find ("gtkhtml-bindings-emacs");
3279 
3280 	/* XXX We leak this GSettings reference but...
3281 	 *     meh, GtkHTML will be orphaned soon. */
3282 	settings = g_settings_new ("org.gnome.desktop.interface");
3283 
3284 	settings_key_theme_changed (settings, "gtk-key-theme", html_class);
3285 
3286 	g_signal_connect (
3287 		settings, "changed::gtk-key-theme",
3288 		G_CALLBACK (settings_key_theme_changed), html_class);
3289 
3290 	g_signal_connect (
3291 		settings, "changed::cursor-blink",
3292 		G_CALLBACK (settings_cursor_blink_changed), html_class);
3293 	g_signal_connect (
3294 		settings, "changed::cursor-blink-time",
3295 		G_CALLBACK (settings_cursor_blink_changed), html_class);
3296 
3297 	settings_cursor_blink_changed (settings, "cursor-blink", html_class);
3298 }
3299 
3300 void
gtk_html_im_reset(GtkHTML * html)3301 gtk_html_im_reset (GtkHTML *html)
3302 {
3303 	if (!html->priv->im_block_reset) {
3304 		D_IM (printf ("IM reset requested\n");)
3305 		if (html->priv->need_im_reset) {
3306 			if (html->engine->freeze_count == 1)
3307 				html_engine_thaw_idle_flush (html->engine);
3308 			html->priv->need_im_reset = FALSE;
3309 			gtk_im_context_reset (html->priv->im_context);
3310 			D_IM (printf ("IM reset called\n");)
3311 		}
3312 	}
3313 }
3314 
3315 static void
gtk_html_im_commit_cb(GtkIMContext * context,const gchar * str,GtkHTML * html)3316 gtk_html_im_commit_cb (GtkIMContext *context,
3317                        const gchar *str,
3318                        GtkHTML *html)
3319 {
3320 	gboolean state = html->priv->im_block_reset;
3321 	gint pos;
3322 
3323 	html->priv->im_block_reset = TRUE;
3324 
3325 	if (html->priv->im_pre_len > 0) {
3326 		D_IM (printf ("IM delete last preedit %d + %d\n", html->priv->im_pre_pos, html->priv->im_pre_len);)
3327 
3328 		html_undo_freeze (html->engine->undo);
3329 		html_cursor_exactly_jump_to_position_no_spell (html->engine->cursor, html->engine, html->priv->im_pre_pos);
3330 		html_engine_set_mark (html->engine);
3331 		html_cursor_exactly_jump_to_position_no_spell (html->engine->cursor, html->engine, html->priv->im_pre_pos + html->priv->im_pre_len);
3332 		html_engine_delete (html->engine);
3333 		html->priv->im_pre_len = 0;
3334 		html_undo_thaw (html->engine->undo);
3335 	}
3336 
3337 	pos = html->engine->cursor->position;
3338 	if (html->engine->mark && html->engine->mark->position > pos)
3339 		pos = html->engine->mark->position;
3340 
3341 	D_IM (printf ("IM commit %s\n", str);)
3342 	html_engine_paste_text (html->engine, str, -1);
3343 	html->priv->im_block_reset = state;
3344 
3345 	D_IM (printf ("IM commit pos: %d pre_pos: %d\n", pos, html->priv->im_pre_pos);)
3346 	if (html->priv->im_pre_pos >= pos)
3347 		html->priv->im_pre_pos += html->engine->cursor->position - pos;
3348 }
3349 
3350 static void
gtk_html_im_preedit_start_cb(GtkIMContext * context,GtkHTML * html)3351 gtk_html_im_preedit_start_cb (GtkIMContext *context,
3352                               GtkHTML *html)
3353 {
3354 	html->priv->im_pre_len = 0;
3355 }
3356 
3357 static void
gtk_html_im_preedit_changed_cb(GtkIMContext * context,GtkHTML * html)3358 gtk_html_im_preedit_changed_cb (GtkIMContext *context,
3359                                 GtkHTML *html)
3360 {
3361 	PangoAttrList *attrs;
3362 	gchar *preedit_string;
3363 	gint cursor_pos, initial_position;
3364 	gboolean state = html->priv->im_block_reset;
3365 	gboolean pop_selection = FALSE;
3366 	gint deleted = 0;
3367 
3368 	D_IM (printf ("IM preedit changed cb [begin] cursor %d(%p) mark %d(%p) active: %d\n",
3369 		      html->engine->cursor ? html->engine->cursor->position : 0, html->engine->cursor,
3370 		      html->engine->mark ? html->engine->mark->position : 0, html->engine->mark,
3371 		      html_engine_is_selection_active (html->engine));)
3372 
3373 	if (!html->engine->cursor) {
3374 		return;
3375 	}
3376 
3377 	html->priv->im_block_reset = TRUE;
3378 
3379 	if (html->engine->mark && html_engine_is_selection_active (html->engine)) {
3380 		D_IM (printf ("IM push selection\n");)
3381 		html_engine_selection_push (html->engine);
3382 		html_engine_disable_selection (html->engine);
3383 		html_engine_edit_selection_updater_update_now (html->engine->selection_updater);
3384 		pop_selection = TRUE;
3385 	}
3386 	initial_position = html->engine->cursor->position;
3387 	D_IM (printf ("IM initial position %d\n", initial_position);)
3388 
3389 	html_undo_freeze (html->engine->undo);
3390 
3391 	if (html->priv->im_pre_len > 0) {
3392 		D_IM (printf ("IM delete last preedit %d + %d\n", html->priv->im_pre_pos, html->priv->im_pre_len);)
3393 
3394 		html_cursor_exactly_jump_to_position_no_spell (html->engine->cursor, html->engine, html->priv->im_pre_pos);
3395 		html_engine_set_mark (html->engine);
3396 		html_cursor_exactly_jump_to_position_no_spell (html->engine->cursor, html->engine, html->priv->im_pre_pos + html->priv->im_pre_len);
3397 		html_engine_delete (html->engine);
3398 		deleted = html->priv->im_pre_len;
3399 	} else
3400 		html->priv->im_orig_style = html_engine_get_font_style (html->engine);
3401 
3402 	gtk_im_context_get_preedit_string (html->priv->im_context, &preedit_string, &attrs, &cursor_pos);
3403 
3404 	D_IM (printf ("IM preedit changed to %s\n", preedit_string);)
3405 	html->priv->im_pre_len = g_utf8_strlen (preedit_string, -1);
3406 
3407 	if (html->priv->im_pre_len > 0) {
3408 		cursor_pos = CLAMP (cursor_pos, 0, html->priv->im_pre_len);
3409 		html->priv->im_pre_pos = html->engine->cursor->position;
3410 		html_engine_paste_text_with_extra_attributes (html->engine, preedit_string, html->priv->im_pre_len, attrs);
3411 		html_cursor_exactly_jump_to_position_no_spell (html->engine->cursor, html->engine, html->priv->im_pre_pos + cursor_pos);
3412 	} else
3413 		html_engine_set_font_style (html->engine, 0, html->priv->im_orig_style);
3414 	g_free (preedit_string);
3415 
3416 	if (pop_selection) {
3417 		gint position= html->engine->cursor->position, cpos, mpos;
3418 		D_IM (printf ("IM pop selection\n");)
3419 		g_assert (html_engine_selection_stack_top (html->engine, &cpos, &mpos));
3420 		if (position < MAX (cpos, mpos) + html->priv->im_pre_len - deleted)
3421 			g_assert (html_engine_selection_stack_top_modify (html->engine, html->priv->im_pre_len - deleted));
3422 		html_engine_selection_pop (html->engine);
3423 	}
3424 	/* that works for now, but idealy we should be able to have cursor positioned outside selection, so that preedit
3425 	 * cursor is in the right place */
3426 	if (html->priv->im_pre_len == 0)
3427 		html_cursor_jump_to_position_no_spell (html->engine->cursor, html->engine,
3428 						       initial_position >= html->priv->im_pre_pos + deleted ? initial_position - deleted : initial_position);
3429 
3430 	if (html->engine->freeze_count == 1)
3431 		html_engine_thaw_idle_flush (html->engine);
3432 	/* FIXME gtk_im_context_set_cursor_location (im_context, &area); */
3433 	html->priv->im_block_reset = state;
3434 
3435 	html_undo_thaw (html->engine->undo);
3436 
3437 	D_IM (printf ("IM preedit changed cb [end] cursor %d(%p) mark %d(%p) active: %d\n",
3438 		      html->engine->cursor ? html->engine->cursor->position : 0, html->engine->cursor,
3439 		      html->engine->mark ? html->engine->mark->position : 0, html->engine->mark,
3440 		      html_engine_is_selection_active (html->engine));)
3441 }
3442 
3443 static gchar *
get_surrounding_text(HTMLEngine * e,gint * offset)3444 get_surrounding_text (HTMLEngine *e,
3445                       gint *offset)
3446 {
3447 	HTMLObject *o = e->cursor->object;
3448 	HTMLObject *prev;
3449 	gchar *text = NULL;
3450 
3451 	if (!html_object_is_text (o)) {
3452 		*offset = 0;
3453 		if (e->cursor->offset == 0) {
3454 			prev = html_object_prev_not_slave (o);
3455 			if (html_object_is_text (prev)) {
3456 				o = prev;
3457 			} else
3458 				return NULL;
3459 		} else if (e->cursor->offset == html_object_get_length (e->cursor->object)) {
3460 			HTMLObject *next;
3461 
3462 			next = html_object_next_not_slave (o);
3463 			if (html_object_is_text (next)) {
3464 				o = next;
3465 			} else
3466 				return NULL;
3467 		}
3468 	} else
3469 		*offset = e->cursor->offset;
3470 
3471 	while ((prev = html_object_prev_not_slave (o)) && html_object_is_text (prev)) {
3472 		o = prev;
3473 		*offset += HTML_TEXT (o)->text_len;
3474 	}
3475 
3476 	while (o) {
3477 		if (html_object_is_text (o)) {
3478 			if (!text)
3479 				text = g_strdup (HTML_TEXT (o)->text);
3480 			else {
3481 				gchar *concat = g_strconcat (text, HTML_TEXT (o)->text, NULL);
3482 				g_free (text);
3483 				text = concat;
3484 			}
3485 		}
3486 		o = html_object_next_not_slave (o);
3487 	}
3488 
3489 	return text;
3490 }
3491 
3492 static gboolean
gtk_html_im_retrieve_surrounding_cb(GtkIMContext * context,GtkHTML * html)3493 gtk_html_im_retrieve_surrounding_cb (GtkIMContext *context,
3494                                      GtkHTML *html)
3495 {
3496 	gint offset = 0;
3497 	gchar *text;
3498 
3499 	D_IM (printf ("IM gtk_html_im_retrieve_surrounding_cb\n");)
3500 
3501 	text = get_surrounding_text (html->engine, &offset);
3502 	if (text) {
3503 		/* convert gchar offset to byte offset */
3504 		offset = g_utf8_offset_to_pointer (text, offset) - text;
3505 		gtk_im_context_set_surrounding (context, text, -1, offset);
3506 		g_free (text);
3507 	} else
3508 		gtk_im_context_set_surrounding (context, NULL, 0, 0);
3509 
3510 	return TRUE;
3511 }
3512 
3513 static gboolean
gtk_html_im_delete_surrounding_cb(GtkIMContext * slave,gint offset,gint n_chars,GtkHTML * html)3514 gtk_html_im_delete_surrounding_cb (GtkIMContext *slave,
3515                                    gint offset,
3516                                    gint n_chars,
3517                                    GtkHTML *html)
3518 {
3519 	D_IM (printf ("IM gtk_html_im_delete_surrounding_cb\n");)
3520 	if (html_engine_get_editable (html->engine) && !html_engine_is_selection_active (html->engine)) {
3521 		gint orig_position = html->engine->cursor->position;
3522 
3523 		html_cursor_exactly_jump_to_position_no_spell (html->engine->cursor, html->engine, orig_position + offset);
3524 		html_engine_set_mark (html->engine);
3525 		html_cursor_exactly_jump_to_position_no_spell (html->engine->cursor, html->engine, orig_position + offset + n_chars);
3526 		html_engine_delete (html->engine);
3527 		if (offset < 0)
3528 			orig_position -= MIN (n_chars, - offset);
3529 		html_cursor_jump_to_position_no_spell (html->engine->cursor, html->engine, orig_position);
3530 	}
3531 	return TRUE;
3532 }
3533 
3534 static void
gtk_html_init(GtkHTML * html)3535 gtk_html_init (GtkHTML *html)
3536 {
3537 	GSettings *settings;
3538 
3539 	gtk_widget_set_can_focus (GTK_WIDGET (html), TRUE);
3540 	gtk_widget_set_app_paintable (GTK_WIDGET (html), TRUE);
3541 	gtk_widget_set_double_buffered (GTK_WIDGET (html), TRUE);
3542 
3543 	html->editor_api = NULL;
3544 	html->debug = FALSE;
3545 	html->allow_selection = TRUE;
3546 
3547 	html->pointer_url = NULL;
3548 	html->hand_cursor = gdk_cursor_new (GDK_HAND2);
3549 	html->ibeam_cursor = gdk_cursor_new (GDK_XTERM);
3550 	html->hadj_connection = 0;
3551 	html->vadj_connection = 0;
3552 
3553 	html->selection_x1 = 0;
3554 	html->selection_y1 = 0;
3555 
3556 	html->in_selection = FALSE;
3557 	html->in_selection_drag = FALSE;
3558 
3559 	html->priv = G_TYPE_INSTANCE_GET_PRIVATE (
3560 		html, GTK_TYPE_HTML, GtkHTMLPrivate);
3561 
3562 	html->priv->idle_handler_id = 0;
3563 	html->priv->scroll_timeout_id = 0;
3564 	html->priv->skip_update_cursor = FALSE;
3565 	html->priv->cursor_moved = FALSE;
3566 	html->priv->paragraph_style = GTK_HTML_PARAGRAPH_STYLE_NORMAL;
3567 	html->priv->paragraph_alignment = GTK_HTML_PARAGRAPH_ALIGNMENT_LEFT;
3568 	html->priv->paragraph_indentation = 0;
3569 	html->priv->insertion_font_style = GTK_HTML_FONT_STYLE_DEFAULT;
3570 	html->priv->selection_type = -1;
3571 	html->priv->selection_as_cite = FALSE;
3572 	html->priv->search_input_line = NULL;
3573 	html->priv->in_object_resize = FALSE;
3574 	html->priv->resize_cursor = gdk_cursor_new (GDK_BOTTOM_RIGHT_CORNER);
3575 	html->priv->in_url_test_mode = FALSE;
3576 	html->priv->in_key_binding = FALSE;
3577 
3578 	html->priv->caret_first_focus_anchor = NULL;
3579 	html->priv->is_first_focus = TRUE;
3580 
3581 	html->priv->hadjustment = NULL;
3582 	html->priv->vadjustment = NULL;
3583 
3584 	/* IM Context */
3585 	html->priv->im_context = gtk_im_multicontext_new ();
3586 	html->priv->need_im_reset = FALSE;
3587 	html->priv->im_block_reset = FALSE;
3588 	html->priv->im_pre_len = 0;
3589 
3590 	g_signal_connect (
3591 		html, "notify::hadjustment",
3592 		G_CALLBACK (hadjustment_notify_cb), NULL);
3593 
3594 	g_signal_connect (
3595 		html, "notify::vadjustment",
3596 		G_CALLBACK (vadjustment_notify_cb), NULL);
3597 
3598 	g_signal_connect (
3599 		html->priv->im_context, "commit",
3600 		G_CALLBACK (gtk_html_im_commit_cb), html);
3601 
3602 	g_signal_connect (
3603 		html->priv->im_context, "preedit_start",
3604 		G_CALLBACK (gtk_html_im_preedit_start_cb), html);
3605 
3606 	g_signal_connect (
3607 		html->priv->im_context, "preedit_changed",
3608 		G_CALLBACK (gtk_html_im_preedit_changed_cb), html);
3609 
3610 	g_signal_connect (
3611 		html->priv->im_context, "retrieve_surrounding",
3612 		G_CALLBACK (gtk_html_im_retrieve_surrounding_cb), html);
3613 
3614 	g_signal_connect (
3615 		html->priv->im_context, "delete_surrounding",
3616 		G_CALLBACK (gtk_html_im_delete_surrounding_cb), html);
3617 
3618 	settings = g_settings_new ("org.gnome.desktop.interface");
3619 
3620 	g_signal_connect (
3621 		settings, "changed::monospace-font-name",
3622 		G_CALLBACK (settings_monospace_font_name_changed), html);
3623 
3624 	/* The signal is disconnected and the GSettings
3625 	 * object unreferenced in our dispose() method. */
3626 	html->priv->desktop_interface = settings;
3627 
3628 	gtk_html_construct (html);
3629 }
3630 
3631 GType
gtk_html_get_type(void)3632 gtk_html_get_type (void)
3633 {
3634 	static GType html_type = 0;
3635 
3636 	if (!html_type) {
3637 		static const GTypeInfo html_info = {
3638 			sizeof (GtkHTMLClass),
3639 			NULL,           /* base_init */
3640 			NULL,           /* base_finalize */
3641 			(GClassInitFunc) gtk_html_class_init,
3642 			NULL,           /* class_finalize */
3643 			NULL,           /* class_data */
3644 			sizeof (GtkHTML),
3645 			1,              /* n_preallocs */
3646 			(GInstanceInitFunc) gtk_html_init,
3647 		};
3648 
3649 		html_type = g_type_register_static (GTK_TYPE_LAYOUT, "GtkHTML", &html_info, 0);
3650 	}
3651 
3652 	return html_type;
3653 }
3654 
3655 /**
3656  * gtk_html_new:
3657  * @void:
3658  *
3659  * GtkHTML widget contructor. It creates an empty GtkHTML widget.
3660  *
3661  * Return value: A GtkHTML widget, newly created and empty.
3662  **/
3663 
3664 GtkWidget *
gtk_html_new(void)3665 gtk_html_new (void)
3666 {
3667 	GtkWidget *html;
3668 
3669 	html = g_object_new (GTK_TYPE_HTML, NULL);
3670 
3671 	return html;
3672 }
3673 
3674 /**
3675  * gtk_html_new_from_string:
3676  * @str: A string containing HTML source.
3677  * @len: A length of @str, if @len == -1 then it will be computed using strlen.
3678  *
3679  * GtkHTML widget constructor. It creates an new GtkHTML widget and loads HTML source from @str.
3680  * It is intended for simple creation. For more complicated loading you probably want to use
3681  * #GtkHTMLStream. See #gtk_html_begin.
3682  *
3683  * Return value: A GtkHTML widget, newly created, containing document loaded from input @str.
3684  **/
3685 
3686 GtkWidget *
gtk_html_new_from_string(const gchar * str,gint len)3687 gtk_html_new_from_string (const gchar *str,
3688                           gint len)
3689 {
3690 	GtkWidget *html;
3691 
3692 	html = g_object_new (GTK_TYPE_HTML, NULL);
3693 	gtk_html_load_from_string (GTK_HTML (html), str, len);
3694 
3695 	return html;
3696 }
3697 
3698 void
gtk_html_construct(GtkHTML * html)3699 gtk_html_construct (GtkHTML *html)
3700 {
3701 	g_return_if_fail (html != NULL);
3702 	g_return_if_fail (GTK_IS_HTML (html));
3703 
3704 	html->engine        = html_engine_new (GTK_WIDGET (html));
3705 	html->iframe_parent = NULL;
3706 
3707 	g_signal_connect (G_OBJECT (html->engine), "title_changed",
3708 			  G_CALLBACK (html_engine_title_changed_cb), html);
3709 	g_signal_connect (G_OBJECT (html->engine), "set_base",
3710 			  G_CALLBACK (html_engine_set_base_cb), html);
3711 	g_signal_connect (G_OBJECT (html->engine), "set_base_target",
3712 			  G_CALLBACK (html_engine_set_base_target_cb), html);
3713 	g_signal_connect (G_OBJECT (html->engine), "load_done",
3714 			  G_CALLBACK (html_engine_load_done_cb), html);
3715 	g_signal_connect (G_OBJECT (html->engine), "url_requested",
3716 			  G_CALLBACK (html_engine_url_requested_cb), html);
3717 	g_signal_connect (G_OBJECT (html->engine), "draw_pending",
3718 			  G_CALLBACK (html_engine_draw_pending_cb), html);
3719 	g_signal_connect (G_OBJECT (html->engine), "redirect",
3720 			  G_CALLBACK (html_engine_redirect_cb), html);
3721 	g_signal_connect (G_OBJECT (html->engine), "submit",
3722 			  G_CALLBACK (html_engine_submit_cb), html);
3723 	g_signal_connect (G_OBJECT (html->engine), "object_requested",
3724 			  G_CALLBACK (html_engine_object_requested_cb), html);
3725 }
3726 
3727 
3728 void
gtk_html_enable_debug(GtkHTML * html,gboolean debug)3729 gtk_html_enable_debug (GtkHTML *html,
3730                        gboolean debug)
3731 {
3732 	g_return_if_fail (html != NULL);
3733 	g_return_if_fail (GTK_IS_HTML (html));
3734 
3735 	html->debug = debug;
3736 }
3737 
3738 
3739 void
gtk_html_allow_selection(GtkHTML * html,gboolean allow)3740 gtk_html_allow_selection (GtkHTML *html,
3741                           gboolean allow)
3742 {
3743 	g_return_if_fail (html != NULL);
3744 	g_return_if_fail (GTK_IS_HTML (html));
3745 
3746 	html->allow_selection = allow;
3747 }
3748 
3749 /**
3750  * gtk_html_begin_full:
3751  * @html: the GtkHTML widget to operate on.
3752  * @target_frame: the string identifying the frame to load the data into
3753  * @content_type: the content_type of the data that we will be loading
3754  * @flags: the GtkHTMLBeginFlags that control the reload behavior handling
3755  *
3756  * Opens a new stream of type @content_type to the frame named @target_frame.
3757  * the flags in @flags allow control over what data is reloaded.
3758  *
3759  * Returns: a new GtkHTMLStream to specified frame
3760  */
3761 GtkHTMLStream *
gtk_html_begin_full(GtkHTML * html,gchar * target_frame,const gchar * content_type,GtkHTMLBeginFlags flags)3762 gtk_html_begin_full (GtkHTML *html,
3763                      gchar *target_frame,
3764                      const gchar *content_type,
3765                      GtkHTMLBeginFlags flags)
3766 {
3767 	GtkHTMLStream *handle;
3768 
3769 	g_return_val_if_fail (!gtk_html_get_editable (html), NULL);
3770 
3771 	if (flags & GTK_HTML_BEGIN_BLOCK_UPDATES)
3772 		gtk_html_set_blocking (html, TRUE);
3773 	else
3774 		gtk_html_set_blocking (html, FALSE);
3775 
3776 	if (flags & GTK_HTML_BEGIN_BLOCK_IMAGES)
3777 		gtk_html_set_images_blocking (html, TRUE);
3778 	else
3779 		gtk_html_set_images_blocking (html, FALSE);
3780 
3781 	if (flags & GTK_HTML_BEGIN_KEEP_IMAGES)
3782 		gtk_html_images_ref (html);
3783 
3784 	if (flags & GTK_HTML_BEGIN_KEEP_SCROLL)
3785 		html->engine->keep_scroll = TRUE;
3786 	else
3787 		html->engine->keep_scroll = FALSE;
3788 
3789 	html->priv->is_first_focus = TRUE;
3790 
3791 	handle = html_engine_begin (html->engine, content_type);
3792 	if (handle == NULL)
3793 		return NULL;
3794 
3795 	html_engine_parse (html->engine);
3796 
3797 	if (flags & GTK_HTML_BEGIN_KEEP_IMAGES)
3798 		gtk_html_images_unref (html);
3799 
3800 	if (flags & GTK_HTML_BEGIN_KEEP_SCROLL)
3801 		html->engine->newPage = FALSE;
3802 
3803 	/* Enable change content type in engine */
3804 	if (flags & GTK_HTML_BEGIN_CHANGECONTENTTYPE)
3805 		gtk_html_set_default_engine (html, TRUE);
3806 
3807 	return handle;
3808 }
3809 
3810 /**
3811  * gtk_html_begin:
3812  * @html: the html widget to operate on.
3813  *
3814  * Opens a new stream to load new content into the GtkHTML widget @html.
3815  *
3816  * Returns: a new GtkHTMLStream to store new content.
3817  **/
3818 GtkHTMLStream *
gtk_html_begin(GtkHTML * html)3819 gtk_html_begin (GtkHTML *html)
3820 {
3821 	g_return_val_if_fail (GTK_IS_HTML (html), NULL);
3822 
3823 	return gtk_html_begin_full (html, NULL, NULL, 0);
3824 }
3825 
3826 /**
3827  * gtk_html_begin_content:
3828  * @html: the html widget to operate on.
3829  * @content_type: a string listing the type of content to expect on the stream.
3830  *
3831  * Opens a new stream to load new content of type @content_type into
3832  * the GtkHTML widget given in @html.
3833  *
3834  * Returns: a new GtkHTMLStream to store new content.
3835  **/
3836 GtkHTMLStream *
gtk_html_begin_content(GtkHTML * html,const gchar * content_type)3837 gtk_html_begin_content (GtkHTML *html,
3838                         const gchar *content_type)
3839 {
3840 	g_return_val_if_fail (!gtk_html_get_editable (html), NULL);
3841 
3842 	return gtk_html_begin_full (html, NULL, content_type , 0);
3843 }
3844 
3845 /**
3846  * gtk_html_write:
3847  * @html: the GtkHTML widget the stream belongs to (unused)
3848  * @handle: the GkHTMLStream to write to.
3849  * @buffer: the data to write to the stream.
3850  * @size: the length of data to read from @buffer
3851  *
3852  * Writes @size bytes of @buffer to the stream pointed to by @stream.
3853  **/
3854 void
gtk_html_write(GtkHTML * html,GtkHTMLStream * handle,const gchar * buffer,gsize size)3855 gtk_html_write (GtkHTML *html,
3856                 GtkHTMLStream *handle,
3857                 const gchar *buffer,
3858                 gsize size)
3859 {
3860 	gtk_html_stream_write (handle, buffer, size);
3861 }
3862 
3863 /**
3864  * gtk_html_end:
3865  * @html: the GtkHTML widget the stream belongs to.
3866  * @handle: the GtkHTMLStream to close.
3867  * @status: the GtkHTMLStreamStatus representing the state of the stream when closed.
3868  *
3869  * Close the GtkHTMLStream represented by @stream and notify @html that is
3870  * should not expect any more content from that stream.
3871  **/
3872 void
gtk_html_end(GtkHTML * html,GtkHTMLStream * handle,GtkHTMLStreamStatus status)3873 gtk_html_end (GtkHTML *html,
3874               GtkHTMLStream *handle,
3875               GtkHTMLStreamStatus status)
3876 {
3877 	gtk_html_stream_close (handle, status);
3878 }
3879 
3880 /**
3881  * gtk_html_stop:
3882  * @html: the GtkHTML widget.
3883  *
3884  * Stop requesting any more data by url_requested signal.
3885  **/
3886 void
gtk_html_stop(GtkHTML * html)3887 gtk_html_stop (GtkHTML *html)
3888 {
3889 	g_return_if_fail (GTK_IS_HTML (html));
3890 
3891 	html_engine_stop (html->engine);
3892 }
3893 
3894 
3895 /**
3896  * gtk_html_get_title:
3897  * @html: The GtkHTML widget.
3898  *
3899  * Retrieve the title of the document currently loaded in the GtkHTML widget.
3900  *
3901  * Returns: the title of the current document
3902  **/
3903 const gchar *
gtk_html_get_title(GtkHTML * html)3904 gtk_html_get_title (GtkHTML *html)
3905 {
3906 	g_return_val_if_fail (html != NULL, NULL);
3907 	g_return_val_if_fail (GTK_IS_HTML (html), NULL);
3908 
3909 	if (html->engine->title == NULL)
3910 		return NULL;
3911 
3912 	return html->engine->title->str;
3913 }
3914 
3915 /**
3916  * gtk_html_set_title:
3917  * @html: The GtkHTML widget.
3918  *
3919  * Set the title of the document currently loaded in the GtkHTML widget.
3920  *
3921  **/
3922 void
gtk_html_set_title(GtkHTML * html,const gchar * title)3923 gtk_html_set_title (GtkHTML *html,
3924                     const gchar *title)
3925 {
3926 	g_return_if_fail (html != NULL);
3927 	g_return_if_fail (GTK_IS_HTML (html));
3928 
3929 	html_engine_set_title (html->engine, title);
3930 }
3931 
3932 
3933 /**
3934  * gtk_html_jump_to_anchor:
3935  * @html: the GtkHTML widget.
3936  * @anchor: a string containing the name of the anchor.
3937  *
3938  * Scroll the document display to show the HTML anchor listed in @anchor
3939  *
3940  * Returns: TRUE if the anchor is found, FALSE otherwise.
3941  **/
3942 gboolean
gtk_html_jump_to_anchor(GtkHTML * html,const gchar * anchor)3943 gtk_html_jump_to_anchor (GtkHTML *html,
3944                          const gchar *anchor)
3945 {
3946 	g_return_val_if_fail (html != NULL, FALSE);
3947 	g_return_val_if_fail (GTK_IS_HTML (html), FALSE);
3948 
3949 	return html_engine_goto_anchor (html->engine, anchor);
3950 }
3951 
3952 
3953 gboolean
gtk_html_save(GtkHTML * html,GtkHTMLSaveReceiverFn receiver,gpointer data)3954 gtk_html_save (GtkHTML *html,
3955                GtkHTMLSaveReceiverFn receiver,
3956                gpointer data)
3957 {
3958 	g_return_val_if_fail (html != NULL, FALSE);
3959 	g_return_val_if_fail (GTK_IS_HTML (html), FALSE);
3960 	g_return_val_if_fail (receiver != NULL, FALSE);
3961 
3962 	return html_engine_save (html->engine, receiver, data);
3963 }
3964 
3965 /**
3966  * gtk_html_export:
3967  * @html: the GtkHTML widget
3968  * @content_type: the expected content_type
3969  * @receiver:
3970  * @user_data: pointer to maintain user state.
3971  *
3972  * Export the current document into the content type given by @content_type,
3973  * by calling the function listed in @receiver data becomes avaiable.  When @receiver is
3974  * called @user_data is passed in as the user_data parameter.
3975  *
3976  * Returns: TRUE if the export was successful, FALSE otherwise.
3977  **/
3978 gboolean
gtk_html_export(GtkHTML * html,const gchar * content_type,GtkHTMLSaveReceiverFn receiver,gpointer user_data)3979 gtk_html_export (GtkHTML *html,
3980                  const gchar *content_type,
3981                  GtkHTMLSaveReceiverFn receiver,
3982                  gpointer user_data)
3983 {
3984 	g_return_val_if_fail (html != NULL, FALSE);
3985 	g_return_val_if_fail (GTK_IS_HTML (html), FALSE);
3986 	g_return_val_if_fail (receiver != NULL, FALSE);
3987 
3988 	if (strcmp (content_type, "text/html") == 0) {
3989 		return html_engine_save (html->engine, receiver, user_data);
3990 	} else if (strcmp (content_type, "text/plain") == 0) {
3991 		return html_engine_save_plain (html->engine, receiver,
3992 					       user_data);
3993 	} else {
3994 		return FALSE;
3995 	}
3996 }
3997 
3998 
3999 
4000 static void
gtk_html_update_scrollbars_on_resize(GtkHTML * html,gdouble old_doc_width,gdouble old_doc_height,gdouble old_width,gdouble old_height,gboolean * changed_x,gboolean * changed_y)4001 gtk_html_update_scrollbars_on_resize (GtkHTML *html,
4002                                       gdouble old_doc_width,
4003                                       gdouble old_doc_height,
4004                                       gdouble old_width,
4005                                       gdouble old_height,
4006                                       gboolean *changed_x,
4007                                       gboolean *changed_y)
4008 {
4009 	GtkAdjustment *vadj, *hadj;
4010 	gdouble doc_width, doc_height;
4011 
4012 	/* printf ("update on resize\n"); */
4013 
4014 	hadj = gtk_layout_get_hadjustment (GTK_LAYOUT (html));
4015 	vadj = gtk_layout_get_vadjustment (GTK_LAYOUT (html));
4016 
4017 	doc_height = html_engine_get_doc_height (html->engine);
4018 	doc_width  = html_engine_get_doc_width (html->engine);
4019 
4020 	if (!html->engine->keep_scroll) {
4021 		if (old_doc_width - old_width > 0) {
4022 			gdouble value;
4023 
4024 			value = gtk_adjustment_get_value (hadj);
4025 			html->engine->x_offset = (gint) (value * (doc_width - html->engine->width)
4026 							 / (old_doc_width - old_width));
4027 
4028 			gtk_adjustment_set_value (hadj, html->engine->x_offset);
4029 		}
4030 
4031 		if (old_doc_height - old_height > 0) {
4032 			gdouble value;
4033 
4034 			value = gtk_adjustment_get_value (vadj);
4035 			html->engine->y_offset = (gint) (value * (doc_height - html->engine->height)
4036 							 / (old_doc_height - old_height));
4037 			gtk_adjustment_set_value (vadj, html->engine->y_offset);
4038 		}
4039 	}
4040 }
4041 
4042 void
gtk_html_private_calc_scrollbars(GtkHTML * html,gboolean * changed_x,gboolean * changed_y)4043 gtk_html_private_calc_scrollbars (GtkHTML *html,
4044                                   gboolean *changed_x,
4045                                   gboolean *changed_y)
4046 {
4047 	GtkLayout *layout;
4048 	GtkAdjustment *vadj, *hadj;
4049 	guint layout_width, layout_height;
4050 	gint width, height;
4051 	gdouble value;
4052 
4053 	if (!gtk_widget_get_realized (GTK_WIDGET (html)))
4054 		return;
4055 
4056 	/* printf ("calc scrollbars\n"); */
4057 
4058 	height = html_engine_get_doc_height (html->engine);
4059 	width = html_engine_get_doc_width (html->engine);
4060 
4061 	layout = GTK_LAYOUT (html);
4062 	hadj = gtk_layout_get_hadjustment (layout);
4063 	vadj = gtk_layout_get_vadjustment (layout);
4064 
4065 	gtk_adjustment_set_page_size (vadj, html->engine->height);
4066 	gtk_adjustment_set_step_increment (vadj, 14);  /* FIXME */
4067 	gtk_adjustment_set_page_increment (vadj, html->engine->height);
4068 
4069 	value = gtk_adjustment_get_value (vadj);
4070 	if (value > height - html->engine->height) {
4071 		gtk_adjustment_set_value (vadj, height - html->engine->height);
4072 		if (changed_y)
4073 			*changed_y = TRUE;
4074 	}
4075 
4076 	gtk_adjustment_set_page_size (hadj, html->engine->width);
4077 	gtk_adjustment_set_step_increment (hadj, 14);  /* FIXME */
4078 	gtk_adjustment_set_page_increment (hadj, html->engine->width);
4079 
4080 	gtk_layout_get_size (layout, &layout_width, &layout_height);
4081 	if ((width != layout_width) || (height != layout_height)) {
4082 		g_signal_emit (html, signals[SIZE_CHANGED], 0);
4083 		gtk_layout_set_size (layout, width, height);
4084 	}
4085 
4086 	value = gtk_adjustment_get_value (hadj);
4087 	if (value > width - html->engine->width || value > MAX_WIDGET_WIDTH - html->engine->width) {
4088 		gtk_adjustment_set_value (hadj, MIN (width - html->engine->width, MAX_WIDGET_WIDTH - html->engine->width));
4089 		if (changed_x)
4090 			*changed_x = TRUE;
4091 	}
4092 
4093 }
4094 
4095 
4096 
4097 #ifdef USE_PROPS
4098 static void
gtk_html_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)4099 gtk_html_set_property (GObject *object,
4100                        guint prop_id,
4101                        const GValue *value,
4102                        GParamSpec *pspec)
4103 {
4104 	GtkHTML *html = GTK_HTML (object);
4105 
4106 	switch (prop_id) {
4107 	case PROP_EDITABLE:
4108 		gtk_html_set_editable (html, g_value_get_boolean (value));
4109 		break;
4110 	case PROP_TITLE:
4111 		gtk_html_set_title (html, g_value_get_string (value));
4112 		break;
4113 	case PROP_DOCUMENT_BASE:
4114 		gtk_html_set_base (html, g_value_get_string (value));
4115 		break;
4116 	case PROP_TARGET_BASE:
4117 		/* This doesn't do anything yet */
4118 		break;
4119 	default:
4120 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
4121 		break;
4122 	}
4123 }
4124 
4125 static void
gtk_html_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)4126 gtk_html_get_property (GObject *object,
4127                        guint prop_id,
4128                        GValue *value,
4129                        GParamSpec *pspec)
4130 {
4131 	GtkHTML *html = GTK_HTML (object);
4132 
4133 	switch (prop_id) {
4134 	case PROP_EDITABLE:
4135 		g_value_set_boolean (value, gtk_html_get_editable (html));
4136 		break;
4137 	case PROP_TITLE:
4138 		g_value_set_static_string (value, gtk_html_get_title (html));
4139 		break;
4140 	case PROP_DOCUMENT_BASE:
4141 		g_value_set_static_string (value, gtk_html_get_base (html));
4142 		break;
4143 	case PROP_TARGET_BASE:
4144 		g_value_set_static_string (value, gtk_html_get_base (html));
4145 		break;
4146 	default:
4147 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
4148 		break;
4149 	}
4150 }
4151 #endif
4152 
4153 void
gtk_html_set_editable(GtkHTML * html,gboolean editable)4154 gtk_html_set_editable (GtkHTML *html,
4155                        gboolean editable)
4156 {
4157 	g_return_if_fail (html != NULL);
4158 	g_return_if_fail (GTK_IS_HTML (html));
4159 
4160 	html_engine_set_editable (html->engine, editable);
4161 
4162 	if (editable)
4163 		gtk_html_update_styles (html);
4164 }
4165 
4166 gboolean
gtk_html_get_editable(const GtkHTML * html)4167 gtk_html_get_editable (const GtkHTML *html)
4168 {
4169 	g_return_val_if_fail (html != NULL, FALSE);
4170 	g_return_val_if_fail (GTK_IS_HTML (html), FALSE);
4171 
4172 	return html_engine_get_editable (html->engine);
4173 }
4174 
4175 void
gtk_html_set_inline_spelling(GtkHTML * html,gboolean inline_spell)4176 gtk_html_set_inline_spelling (GtkHTML *html,
4177                               gboolean inline_spell)
4178 {
4179 	g_return_if_fail (html != NULL);
4180 	g_return_if_fail (GTK_IS_HTML (html));
4181 
4182 	html->priv->inline_spelling = inline_spell;
4183 
4184 	/* do not update content, when there is none set (yet) */
4185 	if (!html->engine || !html->engine->clue)
4186 		return;
4187 
4188 	if (gtk_html_get_editable (html) && html->priv->inline_spelling)
4189 		html_engine_spell_check (html->engine);
4190 	else
4191 		html_engine_clear_spell_check (html->engine);
4192 }
4193 
4194 gboolean
gtk_html_get_inline_spelling(const GtkHTML * html)4195 gtk_html_get_inline_spelling (const GtkHTML *html)
4196 {
4197 	g_return_val_if_fail (html != NULL, FALSE);
4198 	g_return_val_if_fail (GTK_IS_HTML (html), FALSE);
4199 
4200 	return html->priv->inline_spelling;
4201 }
4202 
4203 void
gtk_html_set_magic_links(GtkHTML * html,gboolean links)4204 gtk_html_set_magic_links (GtkHTML *html,
4205                           gboolean links)
4206 {
4207 	g_return_if_fail (html != NULL);
4208 	g_return_if_fail (GTK_IS_HTML (html));
4209 
4210 	html->priv->magic_links = links;
4211 }
4212 
4213 gboolean
gtk_html_get_magic_links(const GtkHTML * html)4214 gtk_html_get_magic_links (const GtkHTML *html)
4215 {
4216 	g_return_val_if_fail (html != NULL, FALSE);
4217 	g_return_val_if_fail (GTK_IS_HTML (html), FALSE);
4218 
4219 	return	html->priv->magic_links;
4220 }
4221 
4222 void
gtk_html_set_magic_smileys(GtkHTML * html,gboolean smile)4223 gtk_html_set_magic_smileys (GtkHTML *html,
4224                             gboolean smile)
4225 {
4226 	g_return_if_fail (html != NULL);
4227 	g_return_if_fail (GTK_IS_HTML (html));
4228 
4229 	html->priv->magic_smileys = smile;
4230 }
4231 
4232 gboolean
gtk_html_get_magic_smileys(const GtkHTML * html)4233 gtk_html_get_magic_smileys (const GtkHTML *html)
4234 {
4235 	g_return_val_if_fail (html != NULL, FALSE);
4236 	g_return_val_if_fail (GTK_IS_HTML (html), FALSE);
4237 
4238 	return	html->priv->magic_smileys;
4239 }
4240 
4241 static void
frame_set_animate(HTMLObject * o,HTMLEngine * e,gpointer data)4242 frame_set_animate (HTMLObject *o,
4243                    HTMLEngine *e,
4244                    gpointer data)
4245 {
4246 	if (HTML_IS_FRAME (o)) {
4247 		html_image_factory_set_animate (GTK_HTML (HTML_FRAME (o)->html)->engine->image_factory,
4248 						*(gboolean *)data);
4249 	} else if (HTML_IS_IFRAME (o)) {
4250 		html_image_factory_set_animate (GTK_HTML (HTML_IFRAME (o)->html)->engine->image_factory,
4251 						*(gboolean *)data);
4252 	}
4253 }
4254 
4255 void
gtk_html_set_caret_mode(GtkHTML * html,gboolean caret_mode)4256 gtk_html_set_caret_mode (GtkHTML *html,
4257                          gboolean caret_mode)
4258 {
4259 	g_return_if_fail (GTK_IS_HTML (html));
4260 	g_return_if_fail (HTML_IS_ENGINE (html->engine));
4261 
4262 	set_caret_mode (html->engine, caret_mode);
4263 }
4264 
4265 gboolean
gtk_html_get_caret_mode(const GtkHTML * html)4266 gtk_html_get_caret_mode (const GtkHTML *html)
4267 {
4268 	g_return_val_if_fail (GTK_IS_HTML (html), FALSE);
4269 	g_return_val_if_fail (HTML_IS_ENGINE (html->engine), FALSE);
4270 
4271 	return html->engine->caret_mode;
4272 }
4273 
4274 /**
4275  * gtk_html_set_caret_first_focus_anchor:
4276  * When setting focus to the GtkHTML first time and is in caret mode,
4277  * then looks for an anchor of name @param name and tries to set focus
4278  * just after it. If NULL, then behaves as before.
4279  *
4280  * @param html GtkHTML instance.
4281  * @param name Name of the anchor to be set the first focus just after it,
4282  *             or NULL to not look for the anchor.
4283  **/
4284 void
gtk_html_set_caret_first_focus_anchor(GtkHTML * html,const gchar * name)4285 gtk_html_set_caret_first_focus_anchor (GtkHTML *html,
4286                                        const gchar *name)
4287 {
4288 	g_return_if_fail (GTK_IS_HTML (html));
4289 	g_return_if_fail (html->priv != NULL);
4290 
4291 	g_free (html->priv->caret_first_focus_anchor);
4292 	html->priv->caret_first_focus_anchor = g_strdup (name);
4293 }
4294 
4295 void
gtk_html_set_animate(GtkHTML * html,gboolean animate)4296 gtk_html_set_animate (GtkHTML *html,
4297                       gboolean animate)
4298 {
4299 	g_return_if_fail (GTK_IS_HTML (html));
4300 	g_return_if_fail (HTML_IS_ENGINE (html->engine));
4301 
4302 	html_image_factory_set_animate (html->engine->image_factory, animate);
4303 	if (html->engine->clue)
4304 		html_object_forall (html->engine->clue, html->engine, frame_set_animate, &animate);
4305 }
4306 
4307 gboolean
gtk_html_get_animate(const GtkHTML * html)4308 gtk_html_get_animate (const GtkHTML *html)
4309 {
4310 	g_return_val_if_fail (GTK_IS_HTML (html), FALSE);
4311 	g_return_val_if_fail (HTML_IS_ENGINE (html->engine), FALSE);
4312 
4313 	return html_image_factory_get_animate (html->engine->image_factory);
4314 }
4315 
4316 void
gtk_html_load_empty(GtkHTML * html)4317 gtk_html_load_empty (GtkHTML *html)
4318 {
4319 	g_return_if_fail (html != NULL);
4320 	g_return_if_fail (GTK_IS_HTML (html));
4321 
4322 	html_engine_load_empty (html->engine);
4323 }
4324 
4325 void
gtk_html_load_from_string(GtkHTML * html,const gchar * str,gint len)4326 gtk_html_load_from_string (GtkHTML *html,
4327                             const gchar *str,
4328                             gint len)
4329 {
4330 	GtkHTMLStream *stream;
4331 
4332 	stream = gtk_html_begin_content (html, "text/html; charset=utf-8");
4333 	gtk_html_stream_write (stream, str, (len == -1) ? strlen (str) : len);
4334 	gtk_html_stream_close (stream, GTK_HTML_STREAM_OK);
4335 }
4336 
4337 void
gtk_html_set_base(GtkHTML * html,const gchar * url)4338 gtk_html_set_base (GtkHTML *html,
4339                    const gchar *url)
4340 {
4341 	g_return_if_fail (GTK_IS_HTML (html));
4342 
4343 	g_free (html->priv->base_url);
4344 	html->priv->base_url = g_strdup (url);
4345 }
4346 
4347 const gchar *
gtk_html_get_base(GtkHTML * html)4348 gtk_html_get_base (GtkHTML *html)
4349 {
4350 	g_return_val_if_fail (GTK_IS_HTML (html), NULL);
4351 
4352 	return html->priv->base_url;
4353 }
4354 
4355 
4356 /* Printing.  */
4357 void
gtk_html_print_page(GtkHTML * html,GtkPrintContext * context)4358 gtk_html_print_page (GtkHTML *html,
4359                      GtkPrintContext *context)
4360 {
4361 	g_return_if_fail (html != NULL);
4362 	g_return_if_fail (GTK_IS_HTML (html));
4363 
4364 	html_engine_print (html->engine, context, .0, .0, NULL, NULL, NULL);
4365 }
4366 
4367 void
gtk_html_print_page_with_header_footer(GtkHTML * html,GtkPrintContext * context,gdouble header_height,gdouble footer_height,GtkHTMLPrintCallback header_print,GtkHTMLPrintCallback footer_print,gpointer user_data)4368 gtk_html_print_page_with_header_footer (GtkHTML *html,
4369                                         GtkPrintContext *context,
4370                                         gdouble header_height,
4371                                         gdouble footer_height,
4372                                         GtkHTMLPrintCallback header_print,
4373                                         GtkHTMLPrintCallback footer_print,
4374                                         gpointer user_data)
4375 {
4376 	g_return_if_fail (html != NULL);
4377 	g_return_if_fail (GTK_IS_HTML (html));
4378 
4379 	html_engine_print (
4380 		html->engine, context, header_height, footer_height,
4381 		header_print, footer_print, user_data);
4382 }
4383 
4384 
4385 /* Editing.  */
4386 
4387 void
gtk_html_set_paragraph_style(GtkHTML * html,GtkHTMLParagraphStyle style)4388 gtk_html_set_paragraph_style (GtkHTML *html,
4389                               GtkHTMLParagraphStyle style)
4390 {
4391 	HTMLClueFlowStyle current_style;
4392 	HTMLClueFlowStyle clueflow_style;
4393 	HTMLListType item_type;
4394 	HTMLListType cur_item_type;
4395 
4396 	g_return_if_fail (html != NULL);
4397 	g_return_if_fail (GTK_IS_HTML (html));
4398 
4399 	/* FIXME precondition: check if it's a valid style.  */
4400 
4401 	paragraph_style_to_clueflow_style (style, &clueflow_style, &item_type);
4402 
4403 	html_engine_get_current_clueflow_style (html->engine, &current_style, &cur_item_type);
4404 	if (!html_engine_is_selection_active (html->engine) && current_style == clueflow_style
4405 	    && (current_style != HTML_CLUEFLOW_STYLE_LIST_ITEM || item_type == cur_item_type))
4406 		return;
4407 
4408 	if (!html_engine_set_clueflow_style (html->engine, clueflow_style, item_type, 0, 0, NULL,
4409 					      HTML_ENGINE_SET_CLUEFLOW_STYLE, HTML_UNDO_UNDO, TRUE))
4410 		return;
4411 
4412 	html->priv->paragraph_style = style;
4413 
4414 	g_signal_emit (html, signals[CURRENT_PARAGRAPH_STYLE_CHANGED], 0, style);
4415 	queue_draw (html);
4416 }
4417 
4418 GtkHTMLParagraphStyle
gtk_html_get_paragraph_style(GtkHTML * html)4419 gtk_html_get_paragraph_style (GtkHTML *html)
4420 {
4421 	HTMLClueFlowStyle style;
4422 	HTMLListType item_type;
4423 
4424 	html_engine_get_current_clueflow_style (html->engine, &style, &item_type);
4425 
4426 	return clueflow_style_to_paragraph_style (style, item_type);
4427 }
4428 
4429 guint
gtk_html_get_paragraph_indentation(GtkHTML * html)4430 gtk_html_get_paragraph_indentation (GtkHTML *html)
4431 {
4432 	return html_engine_get_current_clueflow_indentation (html->engine);
4433 }
4434 
4435 void
gtk_html_set_indent(GtkHTML * html,GByteArray * levels)4436 gtk_html_set_indent (GtkHTML *html,
4437                      GByteArray *levels)
4438 {
4439 	g_return_if_fail (html != NULL);
4440 	g_return_if_fail (GTK_IS_HTML (html));
4441 
4442 	html_engine_set_clueflow_style (html->engine, 0, 0, 0,
4443 					levels ? levels->len : 0,
4444 					levels ? levels->data : NULL,
4445 					HTML_ENGINE_SET_CLUEFLOW_INDENTATION, HTML_UNDO_UNDO, TRUE);
4446 
4447 	gtk_html_update_styles (html);
4448 }
4449 
4450 static void
gtk_html_modify_indent_by_delta(GtkHTML * html,gint delta,guint8 * levels)4451 gtk_html_modify_indent_by_delta (GtkHTML *html,
4452                                  gint delta,
4453                                  guint8 *levels)
4454 {
4455 	g_return_if_fail (html != NULL);
4456 	g_return_if_fail (GTK_IS_HTML (html));
4457 
4458 	html_engine_set_clueflow_style (html->engine, 0, 0, 0, delta, levels,
4459 					HTML_ENGINE_SET_CLUEFLOW_INDENTATION_DELTA, HTML_UNDO_UNDO, TRUE);
4460 
4461 	gtk_html_update_styles (html);
4462 }
4463 
4464 void
gtk_html_indent_push_level(GtkHTML * html,HTMLListType level_type)4465 gtk_html_indent_push_level (GtkHTML *html,
4466                             HTMLListType level_type)
4467 {
4468 	guint8 type = (guint8) level_type;
4469 	gtk_html_modify_indent_by_delta (html, +1, &type);
4470 }
4471 
4472 void
gtk_html_indent_pop_level(GtkHTML * html)4473 gtk_html_indent_pop_level (GtkHTML *html)
4474 {
4475 	gtk_html_modify_indent_by_delta (html, -1, NULL);
4476 }
4477 
4478 void
gtk_html_set_font_style(GtkHTML * html,GtkHTMLFontStyle and_mask,GtkHTMLFontStyle or_mask)4479 gtk_html_set_font_style (GtkHTML *html,
4480                          GtkHTMLFontStyle and_mask,
4481                          GtkHTMLFontStyle or_mask)
4482 {
4483 	g_return_if_fail (html != NULL);
4484 	g_return_if_fail (GTK_IS_HTML (html));
4485 
4486 	if (html_engine_set_font_style (html->engine, and_mask, or_mask))
4487 		g_signal_emit (html, signals[INSERTION_FONT_STYLE_CHANGED], 0, html->engine->insertion_font_style);
4488 }
4489 
4490 void
gtk_html_set_color(GtkHTML * html,HTMLColor * color)4491 gtk_html_set_color (GtkHTML *html,
4492                     HTMLColor *color)
4493 {
4494 	g_return_if_fail (html != NULL);
4495 	g_return_if_fail (GTK_IS_HTML (html));
4496 
4497 	if (html_engine_set_color (html->engine, color))
4498 		g_signal_emit (html, signals[INSERTION_COLOR_CHANGED], 0, html->engine->insertion_color);
4499 }
4500 
4501 void
gtk_html_toggle_font_style(GtkHTML * html,GtkHTMLFontStyle style)4502 gtk_html_toggle_font_style (GtkHTML *html,
4503                             GtkHTMLFontStyle style)
4504 {
4505 	g_return_if_fail (html != NULL);
4506 	g_return_if_fail (GTK_IS_HTML (html));
4507 
4508 	if (HTML_IS_PLAIN_PAINTER (html->engine->painter))
4509 		return;
4510 
4511 	if (html_engine_toggle_font_style (html->engine, style))
4512 		g_signal_emit (html, signals[INSERTION_FONT_STYLE_CHANGED], 0, html->engine->insertion_font_style);
4513 }
4514 
4515 GtkHTMLParagraphAlignment
gtk_html_get_paragraph_alignment(GtkHTML * html)4516 gtk_html_get_paragraph_alignment (GtkHTML *html)
4517 {
4518 	/* This makes the function return a HTMLHalignType. Should the below
4519 	 * call really be html_alignment_to_paragraph()? */
4520 
4521 	return paragraph_alignment_to_html (html_engine_get_current_clueflow_alignment (html->engine));
4522 }
4523 
4524 void
gtk_html_set_paragraph_alignment(GtkHTML * html,GtkHTMLParagraphAlignment alignment)4525 gtk_html_set_paragraph_alignment (GtkHTML *html,
4526                                   GtkHTMLParagraphAlignment alignment)
4527 {
4528 	HTMLHAlignType align;
4529 
4530 	g_return_if_fail (html != NULL);
4531 	g_return_if_fail (GTK_IS_HTML (html));
4532 
4533 	align = paragraph_alignment_to_html (alignment);
4534 
4535 	if (html_engine_set_clueflow_style (html->engine, 0, 0, align, 0, NULL,
4536 					    HTML_ENGINE_SET_CLUEFLOW_ALIGNMENT, HTML_UNDO_UNDO, TRUE)) {
4537 		html->priv->paragraph_alignment = alignment;
4538 		g_signal_emit (html,  signals[CURRENT_PARAGRAPH_ALIGNMENT_CHANGED], 0, alignment);
4539 	}
4540 }
4541 
4542 
4543 /* Clipboard operations.  */
4544 
4545 static void
free_contents(ClipboardContents * contents)4546 free_contents (ClipboardContents *contents)
4547 {
4548 	if (contents->html_text)
4549 		g_free (contents->html_text);
4550 	if (contents->plain_text)
4551 		g_free (contents->plain_text);
4552 
4553 	contents->html_text = NULL;
4554 	contents->plain_text = NULL;
4555 
4556 	g_free (contents);
4557 }
4558 
4559 static void
clipboard_get_contents_cb(GtkClipboard * clipboard,GtkSelectionData * selection_data,guint info,gpointer data)4560 clipboard_get_contents_cb (GtkClipboard *clipboard,
4561                            GtkSelectionData *selection_data,
4562                            guint info,
4563                            gpointer data)
4564 {
4565 	ClipboardContents *contents = (ClipboardContents *) data;
4566 
4567 	if (info == TARGET_HTML && contents->html_text) {
4568 		gtk_selection_data_set (selection_data,
4569 					gdk_atom_intern ("text/html", FALSE), 8,
4570 					(const guchar *) contents->html_text,
4571 					(gint) strlen (contents->html_text));
4572 	} else if (contents->plain_text) {
4573 		gtk_selection_data_set_text (selection_data,
4574 					     contents->plain_text,
4575 					     (gint) strlen (contents->plain_text));
4576 	}
4577 }
4578 
4579 static void
clipboard_clear_contents_cb(GtkClipboard * clipboard,gpointer data)4580 clipboard_clear_contents_cb (GtkClipboard *clipboard,
4581                              gpointer data)
4582 {
4583 	ClipboardContents *contents = (ClipboardContents *) data;
4584 
4585 	free_contents (contents);
4586 }
4587 
4588 static void
clipboard_paste_received_cb(GtkClipboard * clipboard,GtkSelectionData * selection_data,gpointer user_data)4589 clipboard_paste_received_cb (GtkClipboard *clipboard,
4590                              GtkSelectionData *selection_data,
4591                              gpointer user_data)
4592 {
4593 	gint i = 0;
4594 	GtkWidget *widget = GTK_WIDGET (user_data);
4595 	GdkAtom data_type;
4596 	GdkAtom target;
4597 	gboolean as_cite;
4598 	HTMLEngine *e;
4599 	const guchar *data;
4600 	gint length;
4601 
4602 	e = GTK_HTML (widget)->engine;
4603 	as_cite = GTK_HTML (widget)->priv->selection_as_cite;
4604 
4605 	data = gtk_selection_data_get_data (selection_data);
4606 	length = gtk_selection_data_get_length (selection_data);
4607 	target = gtk_selection_data_get_target (selection_data);
4608 	data_type = gtk_selection_data_get_data_type (selection_data);
4609 
4610 	if (length > 0) {
4611 		gchar *utf8 = NULL;
4612 
4613 		if (data_type == gdk_atom_intern (selection_targets[TARGET_HTML].target, FALSE)) {
4614 			if (length > 1 &&
4615 			    !g_utf8_validate ((const gchar *) data, length - 1, NULL)) {
4616 				utf8 = utf16_to_utf8_with_bom_check ((guchar *) data, length);
4617 
4618 			} else {
4619 				utf8 = utf8_filter_out_bom (g_strndup ((const gchar *) data, length));
4620 			}
4621 
4622 			if (as_cite && utf8) {
4623 				gchar *cite;
4624 
4625 				cite = g_strdup_printf ("<br><blockquote type=\"cite\">%s</blockquote>", utf8);
4626 
4627 				g_free (utf8);
4628 				utf8 = cite;
4629 			}
4630 			if (utf8) {
4631 				gint leading_spaces = 0;
4632 
4633 				/* check for leading spaces */
4634 				while (g_ascii_isspace (utf8[leading_spaces]) &&
4635 				       utf8[leading_spaces] != '\n' &&
4636 				       utf8[leading_spaces] != '\r')
4637 					leading_spaces++;
4638 
4639 				if (leading_spaces)
4640 					html_engine_paste_text (e, utf8, leading_spaces);
4641 
4642 				if (utf8[leading_spaces])
4643 					gtk_html_insert_html (GTK_HTML (widget), utf8 + leading_spaces);
4644 
4645 				/* check for trailing spaces */
4646 				length = g_utf8_strlen (utf8, -1);
4647 				if (length > leading_spaces) {
4648 					const gchar *ptr, *from = NULL;
4649 
4650 					ptr = utf8;
4651 					while (ptr = g_utf8_next_char (ptr), ptr && *ptr) {
4652 						if (g_ascii_isspace (*ptr) && *ptr != '\n' && *ptr != '\r') {
4653 							if (!from)
4654 								from = ptr;
4655 						} else {
4656 							from = NULL;
4657 						}
4658 					}
4659 
4660 					if (from)
4661 						html_engine_paste_text (e, from, g_utf8_strlen (from, -1));
4662 				}
4663 			} else
4664 				g_warning ("selection was empty");
4665 		} else if ((utf8 = (gchar *) gtk_selection_data_get_text (selection_data))) {
4666 			utf8 = utf8_filter_out_bom (utf8);
4667 			if (as_cite) {
4668 				gchar *encoded;
4669 
4670 				encoded = html_encode_entities (utf8, g_utf8_strlen (utf8, -1), NULL);
4671 				g_free (utf8);
4672 				utf8 = g_strdup_printf ("<br><pre><blockquote type=\"cite\">%s</blockquote></pre>",
4673 						encoded);
4674 				g_free (encoded);
4675 				gtk_html_insert_html (GTK_HTML (widget), utf8);
4676 			} else {
4677 				html_engine_paste_text (e, utf8, g_utf8_strlen (utf8, -1));
4678 			}
4679 
4680 			if (HTML_IS_TEXT (e->cursor->object))
4681 				html_text_magic_link (HTML_TEXT (e->cursor->object), e, 1);
4682 		}
4683 
4684 		if (utf8)
4685 			g_free (utf8);
4686 
4687 		return;
4688 	}
4689 
4690 	while (i < n_selection_targets - 1) {
4691 		if (target == gdk_atom_intern (selection_targets[i].target, FALSE))
4692 			break;
4693 		i++;
4694 	}
4695 
4696 	if (i < n_selection_targets - 1) {
4697 		GTK_HTML (widget)->priv->selection_type = i + 1;
4698 		gtk_clipboard_request_contents (clipboard,
4699 						gdk_atom_intern (selection_targets[i + 1].target, FALSE),
4700 						clipboard_paste_received_cb,
4701 						widget);
4702 	}
4703 }
4704 
4705 static ClipboardContents *
create_clipboard_contents(GtkHTML * html)4706 create_clipboard_contents (GtkHTML *html)
4707 {
4708 	ClipboardContents *contents;
4709 	gint html_len, text_len;
4710 
4711 	contents = g_new0 (ClipboardContents, 1);
4712 
4713 	/* set html text */
4714 	contents->html_text = get_selection_string (html, &html_len, FALSE, FALSE, TRUE);
4715 
4716 	/* set plain text */
4717 	contents->plain_text = get_selection_string (html, &text_len, FALSE, FALSE, FALSE);
4718 
4719 	return contents;
4720 }
4721 
4722 void
gtk_html_cut(GtkHTML * html)4723 gtk_html_cut (GtkHTML *html)
4724 {
4725 	GtkClipboard *clipboard;
4726 	ClipboardContents *contents;
4727 
4728 	g_return_if_fail (html != NULL);
4729 	g_return_if_fail (GTK_IS_HTML (html));
4730 
4731 	html_engine_cut (html->engine);
4732 
4733 	contents = create_clipboard_contents (html);
4734 
4735 	clipboard = gtk_widget_get_clipboard (GTK_WIDGET (html), GDK_SELECTION_CLIPBOARD);
4736 
4737 	if (!gtk_clipboard_set_with_data (clipboard, selection_targets, n_selection_targets,
4738 					  clipboard_get_contents_cb,
4739 					  clipboard_clear_contents_cb,
4740 					  contents)) {
4741 		free_contents (contents);
4742 	} else {
4743 		gtk_clipboard_set_can_store (clipboard,
4744 					     selection_targets + 1,
4745 					     n_selection_targets - 1);
4746 	}
4747 }
4748 
4749 void
gtk_html_copy(GtkHTML * html)4750 gtk_html_copy (GtkHTML *html)
4751 {
4752 	GtkClipboard *clipboard;
4753 	ClipboardContents *contents;
4754 
4755 	g_return_if_fail (html != NULL);
4756 	g_return_if_fail (GTK_IS_HTML (html));
4757 
4758 	html_engine_copy (html->engine);
4759 
4760 	contents = create_clipboard_contents (html);
4761 
4762 	clipboard = gtk_widget_get_clipboard (GTK_WIDGET (html), GDK_SELECTION_CLIPBOARD);
4763 
4764 	if (!gtk_clipboard_set_with_data (clipboard, selection_targets, n_selection_targets,
4765 					  clipboard_get_contents_cb,
4766 					  clipboard_clear_contents_cb,
4767 					  contents)) {
4768 		free_contents (contents);
4769 	}
4770 	gtk_clipboard_set_can_store (clipboard,
4771 			NULL,
4772 			0);
4773 }
4774 
4775 void
gtk_html_paste(GtkHTML * html,gboolean as_cite)4776 gtk_html_paste (GtkHTML *html,
4777                 gboolean as_cite)
4778 {
4779 	g_return_if_fail (html != NULL);
4780 	g_return_if_fail (GTK_IS_HTML (html));
4781 
4782 	g_object_ref (html);
4783 	html->priv->selection_as_cite = as_cite;
4784 	html->priv->selection_type = 0;
4785 
4786 	gtk_clipboard_request_contents (gtk_widget_get_clipboard (GTK_WIDGET (html), GDK_SELECTION_CLIPBOARD),
4787 					gdk_atom_intern (selection_targets[0].target, FALSE),
4788 					clipboard_paste_received_cb, html);
4789 }
4790 
4791 static void
update_primary_selection(GtkHTML * html)4792 update_primary_selection (GtkHTML *html)
4793 {
4794 	GtkClipboard *clipboard;
4795 	gint text_len;
4796 	gchar *text;
4797 
4798 	g_return_if_fail (html != NULL);
4799 	g_return_if_fail (GTK_IS_HTML (html));
4800 
4801 	if (!html->allow_selection)
4802 		return;
4803 
4804 	text = get_selection_string (html, &text_len, FALSE, TRUE, FALSE);
4805 	if (!text)
4806 		return;
4807 
4808 	clipboard = gtk_widget_get_clipboard (GTK_WIDGET (html), GDK_SELECTION_PRIMARY);
4809 
4810 	gtk_clipboard_set_text (clipboard, text, text_len);
4811 
4812 	g_free (text);
4813 }
4814 
4815 
4816 /* Undo/redo.  */
4817 
4818 void
gtk_html_undo(GtkHTML * html)4819 gtk_html_undo (GtkHTML *html)
4820 {
4821 	g_return_if_fail (html != NULL);
4822 	g_return_if_fail (GTK_IS_HTML (html));
4823 
4824 	html_engine_undo (html->engine);
4825 	gtk_html_update_styles (html);
4826 }
4827 
4828 void
gtk_html_redo(GtkHTML * html)4829 gtk_html_redo (GtkHTML *html)
4830 {
4831 	g_return_if_fail (html != NULL);
4832 	g_return_if_fail (GTK_IS_HTML (html));
4833 
4834 	html_engine_redo (html->engine);
4835 	gtk_html_update_styles (html);
4836 }
4837 
4838 /* misc utils */
4839 /* if engine_type == false - default behaviour*/
4840 void
gtk_html_set_default_engine(GtkHTML * html,gboolean engine_type)4841 gtk_html_set_default_engine (GtkHTML *html,
4842                              gboolean engine_type)
4843 {
4844 	html_engine_set_engine_type (html->engine, engine_type);
4845 }
4846 
4847 gboolean
gtk_html_get_default_engine(GtkHTML * html)4848 gtk_html_get_default_engine (GtkHTML *html)
4849 {
4850 	return html_engine_get_engine_type (html->engine);
4851 }
4852 
4853 void
gtk_html_set_default_content_type(GtkHTML * html,const gchar * content_type)4854 gtk_html_set_default_content_type (GtkHTML *html,
4855                                    const gchar *content_type)
4856 {
4857     html_engine_set_content_type (html->engine, content_type);
4858 }
4859 
4860 const gchar *
gtk_html_get_default_content_type(GtkHTML * html)4861 gtk_html_get_default_content_type (GtkHTML *html)
4862 {
4863     return html_engine_get_content_type (html->engine);
4864 }
4865 
4866 gpointer
gtk_html_get_object_by_id(GtkHTML * html,const gchar * id)4867 gtk_html_get_object_by_id (GtkHTML *html,
4868                            const gchar *id)
4869 {
4870 	g_return_val_if_fail (html, NULL);
4871 	g_return_val_if_fail (id, NULL);
4872 	g_return_val_if_fail (GTK_IS_HTML (html), NULL);
4873 	g_return_val_if_fail (html->engine, NULL);
4874 
4875 	return html_engine_get_object_by_id (html->engine, id);
4876 }
4877 
4878 /*******************************************
4879  *
4880  * keybindings
4881  *
4882 */
4883 
4884 static gint
get_line_height(GtkHTML * html)4885 get_line_height (GtkHTML *html)
4886 {
4887 	gint w, a, d;
4888 
4889 	if (!html->engine || !html->engine->painter)
4890 		return 0;
4891 
4892 	html_painter_set_font_style (html->engine->painter, GTK_HTML_FONT_STYLE_SIZE_3);
4893 	html_painter_set_font_face (html->engine->painter, NULL);
4894 	html_painter_calc_text_size (html->engine->painter, "a", 1, &w, &a, &d);
4895 
4896 	return a + d;
4897 }
4898 
4899 static void
scroll(GtkHTML * html,GtkOrientation orientation,GtkScrollType scroll_type,gfloat position)4900 scroll (GtkHTML *html,
4901         GtkOrientation orientation,
4902         GtkScrollType scroll_type,
4903         gfloat position)
4904 {
4905 	GtkAdjustment *adjustment;
4906 	gint line_height;
4907 	gfloat delta;
4908 	gdouble value;
4909 	gdouble lower;
4910 	gdouble upper;
4911 	gdouble page_size;
4912 	gdouble page_increment;
4913 	gdouble step_increment;
4914 
4915 	/* we dont want scroll in editable (move cursor instead) */
4916 	if (html_engine_get_editable (html->engine) || html->engine->caret_mode)
4917 		return;
4918 
4919 	adjustment = (orientation == GTK_ORIENTATION_VERTICAL) ?
4920 		gtk_layout_get_vadjustment (GTK_LAYOUT (html)) :
4921 		gtk_layout_get_hadjustment (GTK_LAYOUT (html));
4922 
4923 	value = gtk_adjustment_get_value (adjustment);
4924 	lower = gtk_adjustment_get_lower (adjustment);
4925 	upper = gtk_adjustment_get_upper (adjustment);
4926 	page_size = gtk_adjustment_get_page_size (adjustment);
4927 	page_increment = gtk_adjustment_get_page_increment (adjustment);
4928 	step_increment = gtk_adjustment_get_step_increment (adjustment);
4929 
4930 	line_height = (html->engine && page_increment > (3 * get_line_height (html)))
4931 		? get_line_height (html)
4932 		: 0;
4933 
4934 	switch (scroll_type) {
4935 	case GTK_SCROLL_STEP_FORWARD:
4936 		delta = step_increment;
4937 		break;
4938 	case GTK_SCROLL_STEP_BACKWARD:
4939 		delta = -step_increment;
4940 		break;
4941 	case GTK_SCROLL_PAGE_FORWARD:
4942 		delta = page_increment - line_height;
4943 		break;
4944 	case GTK_SCROLL_PAGE_BACKWARD:
4945 		delta = -page_increment + line_height;
4946 		break;
4947 	default:
4948 		g_warning ("invalid scroll parameters: %d %d %f\n", orientation, scroll_type, position);
4949 		return;
4950 	}
4951 
4952 	if (position == 1.0) {
4953 		if (lower > (value + delta)) {
4954 			if (lower >= value) {
4955 				html->binding_handled = FALSE;
4956 				return;
4957 		}
4958 		} else if (MAX (0.0, upper - page_size) < (value + delta)) {
4959 
4960 			if (MAX (0.0, upper - page_size) <= value) {
4961 				html->binding_handled = FALSE;
4962 				return;
4963 			}
4964 		}
4965 	}
4966 
4967 	gtk_adjustment_set_value (adjustment, CLAMP (value + delta, lower, MAX (0.0, upper - page_size)));
4968 
4969 	html->binding_handled = TRUE;
4970 }
4971 
4972 static gboolean
scroll_command(GtkHTML * html,GtkScrollType scroll_type)4973 scroll_command (GtkHTML *html,
4974         GtkScrollType scroll_type)
4975 {
4976 	GtkAdjustment *adjustment;
4977 	gint line_height;
4978 	gfloat delta = 0;
4979 	gdouble value;
4980 	gdouble lower;
4981 	gdouble upper;
4982 	gdouble page_increment;
4983 	gdouble page_size;
4984 
4985 	/* we dont want scroll in editable (move cursor instead) */
4986 	if (html_engine_get_editable (html->engine))
4987 		return FALSE;
4988 
4989 	adjustment = gtk_layout_get_vadjustment (GTK_LAYOUT (html));
4990 	value = gtk_adjustment_get_value (adjustment);
4991 	lower = gtk_adjustment_get_lower (adjustment);
4992 	upper = gtk_adjustment_get_upper (adjustment);
4993 	page_increment = gtk_adjustment_get_page_increment (adjustment);
4994 	page_size = gtk_adjustment_get_page_size (adjustment);
4995 
4996 	line_height = (html->engine && page_increment > (3 * get_line_height (html)))
4997 		? get_line_height (html)
4998 		: 0;
4999 
5000 	switch (scroll_type) {
5001 	case GTK_SCROLL_PAGE_FORWARD:
5002 		delta = page_increment - line_height;
5003 		break;
5004 	case GTK_SCROLL_PAGE_BACKWARD:
5005 		delta = -page_increment + line_height;
5006 		break;
5007 	default:
5008 		break;
5009 		return FALSE;
5010 	}
5011 
5012 	d_s (printf ("%f %f %f\n", value + delta, lower, MAX (0.0, upper - page_size));)
5013 
5014 	if (lower > (value + delta)) {
5015 		if (lower >= value)
5016 			return FALSE;
5017 	} else if (MAX (0.0, upper - page_size) < (value + delta)) {
5018 
5019 		if (MAX (0.0, upper - page_size) <= value) {
5020 			return FALSE;
5021 		}
5022 	}
5023 
5024 	gtk_adjustment_set_value (adjustment, CLAMP (value + delta, lower, MAX (0.0, upper - page_size)));
5025 
5026 	return TRUE;
5027 }
5028 
5029 static void
scroll_by_amount(GtkHTML * html,gint amount)5030 scroll_by_amount (GtkHTML *html,
5031                   gint amount)
5032 {
5033 	GtkAdjustment *adjustment;
5034 	gdouble value;
5035 	gdouble lower;
5036 	gdouble upper;
5037 	gdouble page_size;
5038 
5039 	adjustment = gtk_layout_get_vadjustment (GTK_LAYOUT (html));
5040 	value = gtk_adjustment_get_value (adjustment);
5041 	lower = gtk_adjustment_get_lower (adjustment);
5042 	upper = gtk_adjustment_get_upper (adjustment);
5043 	page_size = gtk_adjustment_get_page_size (adjustment);
5044 
5045 	gtk_adjustment_set_value (
5046 		adjustment, CLAMP (value + (gfloat) amount,
5047 		lower, MAX (0.0, upper - page_size)));
5048 }
5049 
5050 static void
cursor_move(GtkHTML * html,GtkDirectionType dir_type,GtkHTMLCursorSkipType skip)5051 cursor_move (GtkHTML *html,
5052              GtkDirectionType dir_type,
5053              GtkHTMLCursorSkipType skip)
5054 {
5055 	gint amount;
5056 
5057 	if (!html->engine->caret_mode && !html_engine_get_editable (html->engine))
5058 		return;
5059 
5060 	html->priv->cursor_moved = TRUE;
5061 
5062 	if (skip == GTK_HTML_CURSOR_SKIP_NONE) {
5063 		update_primary_selection (html);
5064 		g_signal_emit (GTK_HTML (html), signals[CURSOR_CHANGED], 0);
5065 		return;
5066 	}
5067 
5068 	if (html->engine->selection_mode) {
5069 		if (!html->engine->mark)
5070 			html_engine_set_mark (html->engine);
5071 	} else if (html->engine->shift_selection || html->engine->mark) {
5072 		html_engine_disable_selection (html->engine);
5073 		html_engine_edit_selection_updater_schedule (html->engine->selection_updater);
5074 		html->engine->shift_selection = FALSE;
5075 	}
5076 	switch (skip) {
5077 	case GTK_HTML_CURSOR_SKIP_ONE:
5078 		switch (dir_type) {
5079 		case GTK_DIR_LEFT:
5080 			html_engine_move_cursor (html->engine, HTML_ENGINE_CURSOR_LEFT, 1);
5081 			break;
5082 		case GTK_DIR_RIGHT:
5083 			html_engine_move_cursor (html->engine, HTML_ENGINE_CURSOR_RIGHT, 1);
5084 			break;
5085 		case GTK_DIR_UP:
5086 			html_engine_move_cursor (html->engine, HTML_ENGINE_CURSOR_UP, 1);
5087 			break;
5088 		case GTK_DIR_DOWN:
5089 			html_engine_move_cursor (html->engine, HTML_ENGINE_CURSOR_DOWN, 1);
5090 			break;
5091 		default:
5092 			g_warning ("invalid cursor_move parameters\n");
5093 		}
5094 		break;
5095 	case GTK_HTML_CURSOR_SKIP_WORD:
5096 		switch (dir_type) {
5097 		case GTK_DIR_UP:
5098 		case GTK_DIR_LEFT:
5099 			html_engine_backward_word (html->engine);
5100 			break;
5101 		case GTK_DIR_DOWN:
5102 		case GTK_DIR_RIGHT:
5103 			html_engine_forward_word (html->engine);
5104 			break;
5105 		default:
5106 			g_warning ("invalid cursor_move parameters\n");
5107 		}
5108 		break;
5109 	case GTK_HTML_CURSOR_SKIP_PAGE: {
5110 		GtkAllocation allocation;
5111 		gint line_height;
5112 
5113 		gtk_widget_get_allocation (GTK_WIDGET (html), &allocation);
5114 		line_height = allocation.height > (3 * get_line_height (html))
5115 			? get_line_height (html) : 0;
5116 
5117 		switch (dir_type) {
5118 		case GTK_DIR_UP:
5119 		case GTK_DIR_LEFT:
5120 			if ((amount = html_engine_scroll_up (html->engine,
5121 							     allocation.height - line_height)) > 0)
5122 				scroll_by_amount (html, - amount);
5123 			break;
5124 		case GTK_DIR_DOWN:
5125 		case GTK_DIR_RIGHT:
5126 			if ((amount = html_engine_scroll_down (html->engine,
5127 							       allocation.height - line_height)) > 0)
5128 				scroll_by_amount (html, amount);
5129 			break;
5130 		default:
5131 			g_warning ("invalid cursor_move parameters\n");
5132 		}
5133 		break;
5134 	}
5135 	case GTK_HTML_CURSOR_SKIP_ALL:
5136 		switch (dir_type) {
5137 		case GTK_DIR_LEFT:
5138 			html_engine_beginning_of_line (html->engine);
5139 			break;
5140 		case GTK_DIR_RIGHT:
5141 			html_engine_end_of_line (html->engine);
5142 			break;
5143 		case GTK_DIR_UP:
5144 			html_engine_beginning_of_document (html->engine);
5145 			break;
5146 		case GTK_DIR_DOWN:
5147 			html_engine_end_of_document (html->engine);
5148 			break;
5149 		default:
5150 			g_warning ("invalid cursor_move parameters\n");
5151 		}
5152 		break;
5153 	default:
5154 		g_warning ("invalid cursor_move parameters\n");
5155 	}
5156 
5157 	html->binding_handled = TRUE;
5158 	html->priv->update_styles = TRUE;
5159 	gtk_html_edit_make_cursor_visible (html);
5160 	html_engine_update_selection_active_state (html->engine, html->priv->event_time);
5161 	update_primary_selection (html);
5162 	g_signal_emit (GTK_HTML (html), signals[CURSOR_CHANGED], 0);
5163 }
5164 
5165 static gboolean
move_selection(GtkHTML * html,GtkHTMLCommandType com_type)5166 move_selection (GtkHTML *html,
5167                 GtkHTMLCommandType com_type)
5168 {
5169 	GtkAllocation allocation;
5170 	gboolean rv;
5171 	gint amount;
5172 
5173 	if (!html_engine_get_editable (html->engine) && !html->engine->caret_mode)
5174 		return FALSE;
5175 
5176 	gtk_widget_get_allocation (GTK_WIDGET (html), &allocation);
5177 
5178 	html->priv->cursor_moved = TRUE;
5179 	html->engine->shift_selection = TRUE;
5180 	if (!html->engine->mark)
5181 		html_engine_set_mark (html->engine);
5182 	switch (com_type) {
5183 	case GTK_HTML_COMMAND_MODIFY_SELECTION_UP:
5184 		rv = html_engine_move_cursor (html->engine, HTML_ENGINE_CURSOR_UP, 1) > 0 ? TRUE : FALSE;
5185 		break;
5186 	case GTK_HTML_COMMAND_MODIFY_SELECTION_DOWN:
5187 		rv = html_engine_move_cursor (html->engine, HTML_ENGINE_CURSOR_DOWN, 1) > 0 ? TRUE : FALSE;
5188 		break;
5189 	case GTK_HTML_COMMAND_MODIFY_SELECTION_LEFT:
5190 		rv = html_engine_move_cursor (html->engine, HTML_ENGINE_CURSOR_LEFT, 1) > 0 ? TRUE : FALSE;
5191 		break;
5192 	case GTK_HTML_COMMAND_MODIFY_SELECTION_RIGHT:
5193 		rv = html_engine_move_cursor (html->engine, HTML_ENGINE_CURSOR_RIGHT, 1) > 0 ? TRUE : FALSE;
5194 		break;
5195 	case GTK_HTML_COMMAND_MODIFY_SELECTION_BOL:
5196 		rv = html_engine_beginning_of_line (html->engine);
5197 		break;
5198 	case GTK_HTML_COMMAND_MODIFY_SELECTION_EOL:
5199 		rv = html_engine_end_of_line (html->engine);
5200 		break;
5201 	case GTK_HTML_COMMAND_MODIFY_SELECTION_BOD:
5202 		html_engine_beginning_of_document (html->engine);
5203 		rv = TRUE;
5204 		break;
5205 	case GTK_HTML_COMMAND_MODIFY_SELECTION_EOD:
5206 		html_engine_end_of_document (html->engine);
5207 		rv = TRUE;
5208 		break;
5209 	case GTK_HTML_COMMAND_MODIFY_SELECTION_PREV_WORD:
5210 		rv = html_engine_backward_word (html->engine);
5211 		break;
5212 	case GTK_HTML_COMMAND_MODIFY_SELECTION_NEXT_WORD:
5213 		rv = html_engine_forward_word (html->engine);
5214 		break;
5215 	case GTK_HTML_COMMAND_MODIFY_SELECTION_PAGEUP:
5216 		if ((amount = html_engine_scroll_up (html->engine, allocation.height)) > 0) {
5217 			scroll_by_amount (html, - amount);
5218 			rv = TRUE;
5219 		} else
5220 			rv = FALSE;
5221 		break;
5222 	case GTK_HTML_COMMAND_MODIFY_SELECTION_PAGEDOWN:
5223 		if ((amount = html_engine_scroll_down (html->engine, allocation.height)) > 0) {
5224 			scroll_by_amount (html, amount);
5225 			rv = TRUE;
5226 		} else
5227 			rv = FALSE;
5228 		break;
5229 	default:
5230 		g_warning ("invalid move_selection parameters\n");
5231 		rv = FALSE;
5232 	}
5233 
5234 	html->binding_handled = TRUE;
5235 	html->priv->update_styles = TRUE;
5236 
5237 	html_engine_update_selection_active_state (html->engine, html->priv->event_time);
5238 
5239 	update_primary_selection (html);
5240 
5241 	return rv;
5242 }
5243 
5244 inline static void
delete_one(HTMLEngine * e,gboolean forward)5245 delete_one (HTMLEngine *e,
5246             gboolean forward)
5247 {
5248 	if (e->cursor->object && html_object_is_container (e->cursor->object)
5249 	    && ((forward && !e->cursor->offset) || (!forward && e->cursor->offset)))
5250 		html_engine_delete_container (e);
5251 	else
5252 		html_engine_delete_n (e, 1, forward);
5253 }
5254 
5255 inline static gboolean
insert_tab_or_next_cell(GtkHTML * html)5256 insert_tab_or_next_cell (GtkHTML *html)
5257 {
5258 	HTMLEngine *e = html->engine;
5259 	if (!html_engine_next_cell (e, TRUE)) {
5260 		if (html_clueflow_tabs (HTML_CLUEFLOW (e->cursor->object->parent), e->painter))
5261 			html_engine_paste_text (e, "\t", 1);
5262 		else
5263 			html_engine_paste_text (e, "\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0", 4);
5264 		return TRUE;
5265 	}
5266 
5267 	return TRUE;
5268 }
5269 
5270 inline static void
indent_more_or_next_cell(GtkHTML * html)5271 indent_more_or_next_cell (GtkHTML *html)
5272 {
5273 	if (!html_engine_next_cell (html->engine, TRUE))
5274 		gtk_html_indent_push_level (html, HTML_LIST_TYPE_BLOCKQUOTE);
5275 }
5276 
5277 static gboolean
command(GtkHTML * html,GtkHTMLCommandType com_type)5278 command (GtkHTML *html,
5279          GtkHTMLCommandType com_type)
5280 {
5281 	HTMLEngine *e = html->engine;
5282 	gboolean rv = TRUE;
5283 
5284 	/* printf ("command %d %s\n", com_type, get_value_nick (com_type)); */
5285 	html->binding_handled = TRUE;
5286 
5287 	#define ensure_key_binding_not_editable() \
5288 		if (html->priv->in_key_binding && html_engine_get_editable (e)) { \
5289 			html->binding_handled = FALSE; \
5290 			rv = FALSE; \
5291 			break; \
5292 		}
5293 
5294 	/* non-editable + editable commands */
5295 	switch (com_type) {
5296 	case GTK_HTML_COMMAND_ZOOM_IN:
5297 		ensure_key_binding_not_editable ();
5298 		gtk_html_zoom_in (html);
5299 		break;
5300 	case GTK_HTML_COMMAND_ZOOM_OUT:
5301 		ensure_key_binding_not_editable ();
5302 		gtk_html_zoom_out (html);
5303 		break;
5304 	case GTK_HTML_COMMAND_ZOOM_RESET:
5305 		ensure_key_binding_not_editable ();
5306 		gtk_html_zoom_reset (html);
5307 		break;
5308 	case GTK_HTML_COMMAND_SEARCH_INCREMENTAL_FORWARD:
5309 		gtk_html_isearch (html, TRUE);
5310 		break;
5311 	case GTK_HTML_COMMAND_SEARCH_INCREMENTAL_BACKWARD:
5312 		gtk_html_isearch (html, FALSE);
5313 		break;
5314 	case GTK_HTML_COMMAND_FOCUS_FORWARD:
5315 		if (!html_engine_get_editable (e))
5316 			html->binding_handled = gtk_widget_child_focus (GTK_WIDGET (html), GTK_DIR_TAB_FORWARD);
5317 		break;
5318 	case GTK_HTML_COMMAND_FOCUS_BACKWARD:
5319 		if (!html_engine_get_editable (e))
5320 			html->binding_handled = gtk_widget_child_focus (GTK_WIDGET (html), GTK_DIR_TAB_BACKWARD);
5321 		break;
5322 	case GTK_HTML_COMMAND_SCROLL_FORWARD:
5323 		rv = scroll_command (html, GTK_SCROLL_PAGE_FORWARD);
5324 		break;
5325 	case GTK_HTML_COMMAND_SCROLL_BACKWARD:
5326 		rv = scroll_command (html, GTK_SCROLL_PAGE_BACKWARD);
5327 		break;
5328 	case GTK_HTML_COMMAND_SCROLL_BOD:
5329 		if (!html_engine_get_editable (e) && !e->caret_mode)
5330 			gtk_adjustment_set_value (gtk_layout_get_vadjustment (GTK_LAYOUT (html)), 0);
5331 		break;
5332 	case GTK_HTML_COMMAND_SCROLL_EOD:
5333 		if (!html_engine_get_editable (e) && !e->caret_mode) {
5334 			GtkAdjustment *adjustment;
5335 			gdouble upper, page_size;
5336 
5337 			adjustment = gtk_layout_get_vadjustment (GTK_LAYOUT (html));
5338 			upper = gtk_adjustment_get_upper (adjustment);
5339 			page_size = gtk_adjustment_get_page_size (adjustment);
5340 			gtk_adjustment_set_value (adjustment, upper - page_size);
5341 		}
5342 		break;
5343 	case GTK_HTML_COMMAND_COPY:
5344 		gtk_html_copy (html);
5345 		break;
5346 
5347 	case GTK_HTML_COMMAND_MODIFY_SELECTION_UP:
5348 	case GTK_HTML_COMMAND_MODIFY_SELECTION_DOWN:
5349 	case GTK_HTML_COMMAND_MODIFY_SELECTION_LEFT:
5350 	case GTK_HTML_COMMAND_MODIFY_SELECTION_RIGHT:
5351 	case GTK_HTML_COMMAND_MODIFY_SELECTION_BOL:
5352 	case GTK_HTML_COMMAND_MODIFY_SELECTION_EOL:
5353 	case GTK_HTML_COMMAND_MODIFY_SELECTION_BOD:
5354 	case GTK_HTML_COMMAND_MODIFY_SELECTION_EOD:
5355 	case GTK_HTML_COMMAND_MODIFY_SELECTION_PAGEUP:
5356 	case GTK_HTML_COMMAND_MODIFY_SELECTION_PAGEDOWN:
5357 	case GTK_HTML_COMMAND_MODIFY_SELECTION_PREV_WORD:
5358 	case GTK_HTML_COMMAND_MODIFY_SELECTION_NEXT_WORD:
5359 		if (html->engine->caret_mode || html_engine_get_editable (e)) {
5360 			gtk_im_context_reset (html->priv->im_context);
5361 			rv = move_selection (html, com_type);
5362 		}
5363 		break;
5364 	case GTK_HTML_COMMAND_SELECT_ALL:
5365 		gtk_html_select_all (html);
5366 		break;
5367 	case GTK_HTML_COMMAND_EDITABLE_ON:
5368 		gtk_html_set_editable (html, TRUE);
5369 		break;
5370 	case GTK_HTML_COMMAND_EDITABLE_OFF:
5371 		gtk_html_set_editable (html, FALSE);
5372 		break;
5373 	case GTK_HTML_COMMAND_BLOCK_SELECTION:
5374 		html_engine_block_selection (html->engine);
5375 		break;
5376 	case GTK_HTML_COMMAND_UNBLOCK_SELECTION:
5377 		html_engine_unblock_selection (html->engine);
5378 		break;
5379 	case GTK_HTML_COMMAND_IS_SELECTION_ACTIVE:
5380 		rv = html_engine_is_selection_active (html->engine);
5381 		break;
5382 	case GTK_HTML_COMMAND_UNSELECT_ALL:
5383 		gtk_html_unselect_all (html);
5384 		break;
5385 
5386 	default:
5387 		rv = FALSE;
5388 		html->binding_handled = FALSE;
5389 	}
5390 
5391 	#undef ensure_key_binding_not_editable
5392 
5393 	if (!html_engine_get_editable (e) || html->binding_handled)
5394 		return rv;
5395 
5396 	html->binding_handled = TRUE;
5397 	html->priv->update_styles = FALSE;
5398 
5399 	/* editable commands only */
5400 	switch (com_type) {
5401 	case GTK_HTML_COMMAND_UNDO:
5402 		gtk_html_undo (html);
5403 		break;
5404 	case GTK_HTML_COMMAND_REDO:
5405 		gtk_html_redo (html);
5406 		break;
5407 	case GTK_HTML_COMMAND_COPY_AND_DISABLE_SELECTION:
5408 		gtk_html_copy (html);
5409 		html_engine_disable_selection (e);
5410 		html_engine_edit_selection_updater_schedule (e->selection_updater);
5411 		e->selection_mode = FALSE;
5412 		break;
5413 	case GTK_HTML_COMMAND_CUT:
5414 		gtk_html_cut (html);
5415 		html->priv->update_styles = TRUE;
5416 		break;
5417 	case GTK_HTML_COMMAND_CUT_LINE:
5418 		html_engine_cut_line (e);
5419 		html->priv->update_styles = TRUE;
5420 		break;
5421 	case GTK_HTML_COMMAND_PASTE:
5422 		gtk_html_paste (html, FALSE);
5423 		html->priv->update_styles = TRUE;
5424 		break;
5425 	case GTK_HTML_COMMAND_INSERT_RULE:
5426 		html_engine_insert_rule (e, 0, 100, 2, TRUE, HTML_HALIGN_LEFT);
5427 		break;
5428 	case GTK_HTML_COMMAND_INSERT_PARAGRAPH:
5429 		html_engine_delete (e);
5430 
5431 		/* stop inserting links after newlines */
5432 		html_engine_set_insertion_link (e, NULL, NULL);
5433 
5434 		html_engine_insert_empty_paragraph (e);
5435 		html->priv->update_styles = TRUE;
5436 		break;
5437 	case GTK_HTML_COMMAND_DELETE:
5438 		if (e->mark != NULL
5439 		    && e->mark->position != e->cursor->position)
5440 			html_engine_delete (e);
5441 		else
5442 			delete_one (e, TRUE);
5443 		html->priv->update_styles = TRUE;
5444 		break;
5445 	case GTK_HTML_COMMAND_DELETE_BACK:
5446 		if (html_engine_is_selection_active (e))
5447 			html_engine_delete (e);
5448 		else
5449 			delete_one (e, FALSE);
5450 		html->priv->update_styles = TRUE;
5451 		break;
5452 	case GTK_HTML_COMMAND_DELETE_BACK_OR_INDENT_DEC:
5453 		if (html_engine_is_selection_active (e))
5454 			html_engine_delete (e);
5455 		else if (html_engine_cursor_on_bop (e) && html_engine_get_indent (e) > 0
5456 			 && e->cursor->object->parent && HTML_IS_CLUEFLOW (e->cursor->object->parent)
5457 			 && HTML_CLUEFLOW (e->cursor->object->parent)->style != HTML_CLUEFLOW_STYLE_LIST_ITEM)
5458 			gtk_html_indent_pop_level (html);
5459 		else
5460 			delete_one (e, FALSE);
5461 		html->priv->update_styles = TRUE;
5462 		break;
5463 	case GTK_HTML_COMMAND_DELETE_TABLE:
5464 		html_engine_delete_table (e);
5465 		html->priv->update_styles = TRUE;
5466 		break;
5467 	case GTK_HTML_COMMAND_DELETE_TABLE_ROW:
5468 		html_engine_delete_table_row (e);
5469 		html->priv->update_styles = TRUE;
5470 		break;
5471 	case GTK_HTML_COMMAND_DELETE_TABLE_COLUMN:
5472 		html_engine_delete_table_column (e);
5473 		html->priv->update_styles = TRUE;
5474 		break;
5475 	case GTK_HTML_COMMAND_DELETE_TABLE_CELL_CONTENTS:
5476 		html_engine_delete_table_cell_contents (e);
5477 		html->priv->update_styles = TRUE;
5478 		break;
5479 	case GTK_HTML_COMMAND_SELECTION_MODE:
5480 		e->selection_mode = TRUE;
5481 		break;
5482 	case GTK_HTML_COMMAND_DISABLE_SELECTION:
5483 		html_engine_disable_selection (e);
5484 		html_engine_edit_selection_updater_schedule (e->selection_updater);
5485 		e->selection_mode = FALSE;
5486 		break;
5487 	case GTK_HTML_COMMAND_BOLD_ON:
5488 		gtk_html_set_font_style (html, GTK_HTML_FONT_STYLE_MAX, GTK_HTML_FONT_STYLE_BOLD);
5489 		break;
5490 	case GTK_HTML_COMMAND_BOLD_OFF:
5491 		gtk_html_set_font_style (html, ~GTK_HTML_FONT_STYLE_BOLD, 0);
5492 		break;
5493 	case GTK_HTML_COMMAND_BOLD_TOGGLE:
5494 		gtk_html_toggle_font_style (html, GTK_HTML_FONT_STYLE_BOLD);
5495 		break;
5496 	case GTK_HTML_COMMAND_ITALIC_ON:
5497 		gtk_html_set_font_style (html, GTK_HTML_FONT_STYLE_MAX, GTK_HTML_FONT_STYLE_ITALIC);
5498 		break;
5499 	case GTK_HTML_COMMAND_ITALIC_OFF:
5500 		gtk_html_set_font_style (html, ~GTK_HTML_FONT_STYLE_ITALIC, 0);
5501 		break;
5502 	case GTK_HTML_COMMAND_ITALIC_TOGGLE:
5503 		gtk_html_toggle_font_style (html, GTK_HTML_FONT_STYLE_ITALIC);
5504 		break;
5505 	case GTK_HTML_COMMAND_STRIKEOUT_ON:
5506 		gtk_html_set_font_style (html, GTK_HTML_FONT_STYLE_MAX, GTK_HTML_FONT_STYLE_STRIKEOUT);
5507 		break;
5508 	case GTK_HTML_COMMAND_STRIKEOUT_OFF:
5509 		gtk_html_set_font_style (html, ~GTK_HTML_FONT_STYLE_STRIKEOUT, 0);
5510 		break;
5511 	case GTK_HTML_COMMAND_STRIKEOUT_TOGGLE:
5512 		gtk_html_toggle_font_style (html, GTK_HTML_FONT_STYLE_STRIKEOUT);
5513 		break;
5514 	case GTK_HTML_COMMAND_UNDERLINE_ON:
5515 		gtk_html_set_font_style (html, GTK_HTML_FONT_STYLE_MAX, GTK_HTML_FONT_STYLE_UNDERLINE);
5516 		break;
5517 	case GTK_HTML_COMMAND_UNDERLINE_OFF:
5518 		gtk_html_set_font_style (html, ~GTK_HTML_FONT_STYLE_UNDERLINE, 0);
5519 		break;
5520 	case GTK_HTML_COMMAND_UNDERLINE_TOGGLE:
5521 		gtk_html_toggle_font_style (html, GTK_HTML_FONT_STYLE_UNDERLINE);
5522 		break;
5523 	case GTK_HTML_COMMAND_SIZE_MINUS_2:
5524 		gtk_html_set_font_style (html, ~GTK_HTML_FONT_STYLE_SIZE_MASK, GTK_HTML_FONT_STYLE_SIZE_1);
5525 		break;
5526 	case GTK_HTML_COMMAND_SIZE_MINUS_1:
5527 		gtk_html_set_font_style (html, ~GTK_HTML_FONT_STYLE_SIZE_MASK, GTK_HTML_FONT_STYLE_SIZE_2);
5528 		break;
5529 	case GTK_HTML_COMMAND_SIZE_PLUS_0:
5530 		gtk_html_set_font_style (html, ~GTK_HTML_FONT_STYLE_SIZE_MASK, GTK_HTML_FONT_STYLE_SIZE_3);
5531 		break;
5532 	case GTK_HTML_COMMAND_SIZE_PLUS_1:
5533 		gtk_html_set_font_style (html, ~GTK_HTML_FONT_STYLE_SIZE_MASK, GTK_HTML_FONT_STYLE_SIZE_4);
5534 		break;
5535 	case GTK_HTML_COMMAND_SIZE_PLUS_2:
5536 		gtk_html_set_font_style (html, ~GTK_HTML_FONT_STYLE_SIZE_MASK, GTK_HTML_FONT_STYLE_SIZE_5);
5537 		break;
5538 	case GTK_HTML_COMMAND_SIZE_PLUS_3:
5539 		gtk_html_set_font_style (html, ~GTK_HTML_FONT_STYLE_SIZE_MASK, GTK_HTML_FONT_STYLE_SIZE_6);
5540 		break;
5541 	case GTK_HTML_COMMAND_SIZE_PLUS_4:
5542 		gtk_html_set_font_style (html, ~GTK_HTML_FONT_STYLE_SIZE_MASK, GTK_HTML_FONT_STYLE_SIZE_7);
5543 		break;
5544 	case GTK_HTML_COMMAND_SIZE_INCREASE:
5545 		html_engine_font_size_inc_dec (e, TRUE);
5546 		break;
5547 	case GTK_HTML_COMMAND_SIZE_DECREASE:
5548 		html_engine_font_size_inc_dec (e, FALSE);
5549 		break;
5550 	case GTK_HTML_COMMAND_ALIGN_LEFT:
5551 		gtk_html_set_paragraph_alignment (html, GTK_HTML_PARAGRAPH_ALIGNMENT_LEFT);
5552 		break;
5553 	case GTK_HTML_COMMAND_ALIGN_CENTER:
5554 		gtk_html_set_paragraph_alignment (html, GTK_HTML_PARAGRAPH_ALIGNMENT_CENTER);
5555 		break;
5556 	case GTK_HTML_COMMAND_ALIGN_RIGHT:
5557 		gtk_html_set_paragraph_alignment (html, GTK_HTML_PARAGRAPH_ALIGNMENT_RIGHT);
5558 		break;
5559 	case GTK_HTML_COMMAND_INDENT_ZERO:
5560 		gtk_html_set_indent (html, NULL);
5561 		break;
5562 	case GTK_HTML_COMMAND_INDENT_INC:
5563 		gtk_html_indent_push_level (html, HTML_LIST_TYPE_BLOCKQUOTE);
5564 		break;
5565 	case GTK_HTML_COMMAND_INDENT_INC_OR_NEXT_CELL:
5566 		indent_more_or_next_cell (html);
5567 		break;
5568 	case GTK_HTML_COMMAND_INSERT_TAB:
5569 		if (!html_engine_is_selection_active (e)
5570 		    && html_clueflow_tabs (HTML_CLUEFLOW (e->cursor->object->parent), e->painter))
5571 			html_engine_insert_text (e, "\t", 1);
5572 		break;
5573 	case GTK_HTML_COMMAND_INSERT_TAB_OR_INDENT_MORE:
5574 		if (!html_engine_is_selection_active (e)
5575 		    && html_clueflow_tabs (HTML_CLUEFLOW (e->cursor->object->parent), e->painter))
5576 			html_engine_insert_text (e, "\t", 1);
5577 		else
5578 			gtk_html_indent_push_level (html, HTML_LIST_TYPE_BLOCKQUOTE);
5579 		break;
5580 	case GTK_HTML_COMMAND_INSERT_TAB_OR_NEXT_CELL:
5581 		html->binding_handled = insert_tab_or_next_cell (html);
5582 		break;
5583 	case GTK_HTML_COMMAND_INDENT_DEC:
5584 		gtk_html_indent_pop_level (html);
5585 		break;
5586 	case GTK_HTML_COMMAND_PREV_CELL:
5587 		html->binding_handled = html_engine_prev_cell (html->engine);
5588 		break;
5589 	case GTK_HTML_COMMAND_INDENT_PARAGRAPH:
5590 		html_engine_indent_paragraph (e);
5591 		break;
5592 	case GTK_HTML_COMMAND_BREAK_AND_FILL_LINE:
5593 		html_engine_break_and_fill_line (e);
5594 		break;
5595 	case GTK_HTML_COMMAND_SPACE_AND_FILL_LINE:
5596 		html_engine_space_and_fill_line (e);
5597 		break;
5598 	case GTK_HTML_COMMAND_PARAGRAPH_STYLE_NORMAL:
5599 		gtk_html_set_paragraph_style (html, GTK_HTML_PARAGRAPH_STYLE_NORMAL);
5600 		break;
5601 	case GTK_HTML_COMMAND_PARAGRAPH_STYLE_H1:
5602 		gtk_html_set_paragraph_style (html, GTK_HTML_PARAGRAPH_STYLE_H1);
5603 		break;
5604 	case GTK_HTML_COMMAND_PARAGRAPH_STYLE_H2:
5605 		gtk_html_set_paragraph_style (html, GTK_HTML_PARAGRAPH_STYLE_H2);
5606 		break;
5607 	case GTK_HTML_COMMAND_PARAGRAPH_STYLE_H3:
5608 		gtk_html_set_paragraph_style (html, GTK_HTML_PARAGRAPH_STYLE_H3);
5609 		break;
5610 	case GTK_HTML_COMMAND_PARAGRAPH_STYLE_H4:
5611 		gtk_html_set_paragraph_style (html, GTK_HTML_PARAGRAPH_STYLE_H4);
5612 		break;
5613 	case GTK_HTML_COMMAND_PARAGRAPH_STYLE_H5:
5614 		gtk_html_set_paragraph_style (html, GTK_HTML_PARAGRAPH_STYLE_H5);
5615 		break;
5616 	case GTK_HTML_COMMAND_PARAGRAPH_STYLE_H6:
5617 		gtk_html_set_paragraph_style (html, GTK_HTML_PARAGRAPH_STYLE_H6);
5618 		break;
5619 	case GTK_HTML_COMMAND_PARAGRAPH_STYLE_PRE:
5620 		gtk_html_set_paragraph_style (html, GTK_HTML_PARAGRAPH_STYLE_PRE);
5621 		break;
5622 	case GTK_HTML_COMMAND_PARAGRAPH_STYLE_ADDRESS:
5623 		gtk_html_set_paragraph_style (html, GTK_HTML_PARAGRAPH_STYLE_ADDRESS);
5624 		break;
5625 	case GTK_HTML_COMMAND_PARAGRAPH_STYLE_ITEMDOTTED:
5626 		gtk_html_set_paragraph_style (html, GTK_HTML_PARAGRAPH_STYLE_ITEMDOTTED);
5627 		break;
5628 	case GTK_HTML_COMMAND_PARAGRAPH_STYLE_ITEMROMAN:
5629 		gtk_html_set_paragraph_style (html, GTK_HTML_PARAGRAPH_STYLE_ITEMROMAN);
5630 		break;
5631 	case GTK_HTML_COMMAND_PARAGRAPH_STYLE_ITEMDIGIT:
5632 		gtk_html_set_paragraph_style (html, GTK_HTML_PARAGRAPH_STYLE_ITEMDIGIT);
5633 		break;
5634 	case GTK_HTML_COMMAND_PARAGRAPH_STYLE_ITEMALPHA:
5635 		gtk_html_set_paragraph_style (html, GTK_HTML_PARAGRAPH_STYLE_ITEMALPHA);
5636 		break;
5637 	case GTK_HTML_COMMAND_SELECT_WORD:
5638 		gtk_html_select_word (html);
5639 		break;
5640 	case GTK_HTML_COMMAND_SELECT_LINE:
5641 		gtk_html_select_line (html);
5642 		break;
5643 	case GTK_HTML_COMMAND_SELECT_PARAGRAPH:
5644 		gtk_html_select_paragraph (html);
5645 		break;
5646 	case GTK_HTML_COMMAND_SELECT_PARAGRAPH_EXTENDED:
5647 		gtk_html_select_paragraph_extended (html);
5648 		break;
5649 	case GTK_HTML_COMMAND_CURSOR_POSITION_SAVE:
5650 		html_engine_edit_cursor_position_save (html->engine);
5651 		break;
5652 	case GTK_HTML_COMMAND_CURSOR_POSITION_RESTORE:
5653 		html_engine_edit_cursor_position_restore (html->engine);
5654 		break;
5655 	case GTK_HTML_COMMAND_CAPITALIZE_WORD:
5656 		html_engine_capitalize_word (e);
5657 		break;
5658 	case GTK_HTML_COMMAND_UPCASE_WORD:
5659 		html_engine_upcase_downcase_word (e, TRUE);
5660 		break;
5661 	case GTK_HTML_COMMAND_DOWNCASE_WORD:
5662 		html_engine_upcase_downcase_word (e, FALSE);
5663 		break;
5664 	case GTK_HTML_COMMAND_SPELL_SUGGEST:
5665 		if (html->editor_api && !html_engine_spell_word_is_valid (e))
5666 			(*html->editor_api->suggestion_request) (html, html->editor_data);
5667 		break;
5668 	case GTK_HTML_COMMAND_SPELL_PERSONAL_DICTIONARY_ADD:
5669 	case GTK_HTML_COMMAND_SPELL_SESSION_DICTIONARY_ADD: {
5670 		gchar *word;
5671 		word = html_engine_get_spell_word (e);
5672 
5673 		if (word && html->editor_api) {
5674 			if (com_type == GTK_HTML_COMMAND_SPELL_PERSONAL_DICTIONARY_ADD)
5675 				/* FIXME fire popup menu with more than 1 language enabled */
5676 				(*html->editor_api->add_to_personal) (html, word, html_engine_get_language (html->engine), html->editor_data);
5677 			else
5678 				(*html->editor_api->add_to_session) (html, word, html->editor_data);
5679 			g_free (word);
5680 			html_engine_spell_check (e);
5681 			gtk_widget_queue_draw (GTK_WIDGET (html));
5682 		}
5683 		break;
5684 	}
5685 	case GTK_HTML_COMMAND_CURSOR_FORWARD:
5686 		rv = html_cursor_forward (html->engine->cursor, html->engine);
5687 		break;
5688 	case GTK_HTML_COMMAND_CURSOR_BACKWARD:
5689 		rv = html_cursor_backward (html->engine->cursor, html->engine);
5690 		break;
5691 	case GTK_HTML_COMMAND_INSERT_TABLE_1_1:
5692 		html_engine_insert_table_1_1 (e);
5693 		break;
5694 	case GTK_HTML_COMMAND_TABLE_INSERT_COL_BEFORE:
5695 		html_engine_insert_table_column (e, FALSE);
5696 		break;
5697 	case GTK_HTML_COMMAND_TABLE_INSERT_COL_AFTER:
5698 		html_engine_insert_table_column (e, TRUE);
5699 		break;
5700 	case GTK_HTML_COMMAND_TABLE_DELETE_COL:
5701 		html_engine_delete_table_column (e);
5702 		break;
5703 	case GTK_HTML_COMMAND_TABLE_INSERT_ROW_BEFORE:
5704 		html_engine_insert_table_row (e, FALSE);
5705 		break;
5706 	case GTK_HTML_COMMAND_TABLE_INSERT_ROW_AFTER:
5707 		html_engine_insert_table_row (e, TRUE);
5708 		break;
5709 	case GTK_HTML_COMMAND_TABLE_DELETE_ROW:
5710 		html_engine_delete_table_row (e);
5711 		break;
5712 	case GTK_HTML_COMMAND_TABLE_BORDER_WIDTH_INC:
5713 		html_engine_table_set_border_width (e, html_engine_get_table (e), 1, TRUE);
5714 		break;
5715 	case GTK_HTML_COMMAND_TABLE_BORDER_WIDTH_DEC:
5716 		html_engine_table_set_border_width (e, html_engine_get_table (e), -1, TRUE);
5717 		break;
5718 	case GTK_HTML_COMMAND_TABLE_BORDER_WIDTH_ZERO:
5719 		html_engine_table_set_border_width (e, html_engine_get_table (e), 0, FALSE);
5720 		break;
5721 	case GTK_HTML_COMMAND_TABLE_SPACING_INC:
5722 		html_engine_table_set_spacing (e, html_engine_get_table (e), 1, TRUE);
5723 		break;
5724 	case GTK_HTML_COMMAND_TABLE_SPACING_DEC:
5725 		html_engine_table_set_spacing (e, html_engine_get_table (e), -1, TRUE);
5726 		break;
5727 	case GTK_HTML_COMMAND_TABLE_SPACING_ZERO:
5728 		html_engine_table_set_spacing (e, html_engine_get_table (e), 0, FALSE);
5729 		break;
5730 	case GTK_HTML_COMMAND_TABLE_PADDING_INC:
5731 		html_engine_table_set_padding (e, html_engine_get_table (e), 1, TRUE);
5732 		break;
5733 	case GTK_HTML_COMMAND_TABLE_PADDING_DEC:
5734 		html_engine_table_set_padding (e, html_engine_get_table (e), -1, TRUE);
5735 		break;
5736 	case GTK_HTML_COMMAND_TABLE_PADDING_ZERO:
5737 		html_engine_table_set_padding (e, html_engine_get_table (e), 0, FALSE);
5738 		break;
5739 	case GTK_HTML_COMMAND_TEXT_SET_DEFAULT_COLOR:
5740 		html_engine_set_color (e, NULL);
5741 		break;
5742 	case GTK_HTML_COMMAND_CURSOR_BOD:
5743 		html_engine_beginning_of_document (e);
5744 		break;
5745 	case GTK_HTML_COMMAND_CURSOR_EOD:
5746 		html_engine_end_of_document (e);
5747 		break;
5748 	case GTK_HTML_COMMAND_BLOCK_REDRAW:
5749 		html_engine_block_redraw (e);
5750 		break;
5751 	case GTK_HTML_COMMAND_UNBLOCK_REDRAW:
5752 		html_engine_unblock_redraw (e);
5753 		break;
5754 	case GTK_HTML_COMMAND_GRAB_FOCUS:
5755 		gtk_widget_grab_focus (GTK_WIDGET (html));
5756 		break;
5757 	case GTK_HTML_COMMAND_KILL_WORD:
5758 	case GTK_HTML_COMMAND_KILL_WORD_BACKWARD:
5759 		html_engine_block_selection (e);
5760 		html_engine_set_mark (e);
5761 		html_engine_update_selection_if_necessary (e);
5762 		html_engine_freeze (e);
5763 		rv = com_type == GTK_HTML_COMMAND_KILL_WORD
5764 			? html_engine_forward_word (e)
5765 			: html_engine_backward_word (e);
5766 		if (rv)
5767 			html_engine_delete (e);
5768 		html_engine_unblock_selection (e);
5769 		html_engine_thaw (e);
5770 		break;
5771 	case GTK_HTML_COMMAND_SAVE_DATA_ON:
5772 		html->engine->save_data = TRUE;
5773 		break;
5774 	case GTK_HTML_COMMAND_SAVE_DATA_OFF:
5775 		html->engine->save_data = FALSE;
5776 		break;
5777 	case GTK_HTML_COMMAND_SAVED:
5778 		html_engine_saved (html->engine);
5779 		break;
5780 	case GTK_HTML_COMMAND_IS_SAVED:
5781 		rv = html_engine_is_saved (html->engine);
5782 		break;
5783 	case GTK_HTML_COMMAND_CELL_CSPAN_INC:
5784 		rv = html_engine_cspan_delta (html->engine, 1);
5785 		break;
5786 	case GTK_HTML_COMMAND_CELL_RSPAN_INC:
5787 		rv = html_engine_rspan_delta (html->engine, 1);
5788 		break;
5789 	case GTK_HTML_COMMAND_CELL_CSPAN_DEC:
5790 		rv = html_engine_cspan_delta (html->engine, -1);
5791 		break;
5792 	case GTK_HTML_COMMAND_CELL_RSPAN_DEC:
5793 		rv = html_engine_rspan_delta (html->engine, -1);
5794 		break;
5795 	default:
5796 		html->binding_handled = FALSE;
5797 	}
5798 
5799 	if (!html->binding_handled && html->editor_api)
5800 		html->binding_handled = (* html->editor_api->command) (html, com_type, html->editor_data);
5801 
5802 	return rv;
5803 }
5804 
5805 static void
add_bindings(GtkHTMLClass * klass)5806 add_bindings (GtkHTMLClass *klass)
5807 {
5808 	GtkBindingSet *binding_set;
5809 
5810 	/* ensure enums are defined */
5811 	gtk_html_cursor_skip_get_type ();
5812 	gtk_html_command_get_type ();
5813 
5814 	binding_set = gtk_binding_set_by_class (klass);
5815 
5816 	/* layout scrolling */
5817 #define BSCROLL(m,key,orient,sc) \
5818 	gtk_binding_entry_add_signal (binding_set, GDK_KEY_ ## key, m, \
5819 				      "scroll", 3, \
5820 				      GTK_TYPE_ORIENTATION, GTK_ORIENTATION_ ## orient, \
5821 				      GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_ ## sc, \
5822 				      G_TYPE_FLOAT, 0.0);
5823 
5824 #define BSPACESCROLL(m,key,orient,sc) \
5825 	gtk_binding_entry_add_signal (binding_set, GDK_KEY_ ## key, m, \
5826 				      "scroll", 3, \
5827 				      GTK_TYPE_ORIENTATION, GTK_ORIENTATION_ ## orient, \
5828 				      GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_ ## sc, \
5829 				      G_TYPE_FLOAT, 1.0);
5830 
5831 	BSCROLL (0, Up, VERTICAL, STEP_BACKWARD);
5832 	BSCROLL (0, KP_Up, VERTICAL, STEP_BACKWARD);
5833 	BSCROLL (0, Down, VERTICAL, STEP_FORWARD);
5834 	BSCROLL (0, KP_Down, VERTICAL, STEP_FORWARD);
5835 
5836 	BSCROLL (0, Left, HORIZONTAL, STEP_BACKWARD);
5837 	BSCROLL (0, KP_Left, HORIZONTAL, STEP_BACKWARD);
5838 	BSCROLL (0, Right, HORIZONTAL, STEP_FORWARD);
5839 	BSCROLL (0, KP_Right, HORIZONTAL, STEP_FORWARD);
5840 
5841 	BSCROLL (0, Page_Up, VERTICAL, PAGE_BACKWARD);
5842 	BSCROLL (0, KP_Page_Up, VERTICAL, PAGE_BACKWARD);
5843 	BSCROLL (0, Page_Down, VERTICAL, PAGE_FORWARD);
5844 	BSCROLL (0, KP_Page_Down, VERTICAL, PAGE_FORWARD);
5845 	BSPACESCROLL (0, BackSpace, VERTICAL, PAGE_BACKWARD);
5846 	BSPACESCROLL (0, space, VERTICAL, PAGE_FORWARD);
5847 	BSPACESCROLL (GDK_SHIFT_MASK, space, VERTICAL, PAGE_BACKWARD);
5848 
5849 	BSCROLL (GDK_SHIFT_MASK, Left, HORIZONTAL, PAGE_BACKWARD);
5850 	BSCROLL (GDK_SHIFT_MASK, KP_Left, HORIZONTAL, PAGE_BACKWARD);
5851 	BSCROLL (GDK_SHIFT_MASK, Right, HORIZONTAL, PAGE_FORWARD);
5852 	BSCROLL (GDK_SHIFT_MASK, KP_Right, HORIZONTAL, PAGE_FORWARD);
5853 
5854 	/* editing */
5855 
5856 #define BMOVE(m,key,dir,sk) \
5857 	gtk_binding_entry_add_signal (binding_set, GDK_KEY_ ## key, m, \
5858 				      "cursor_move", 2, \
5859 				      GTK_TYPE_DIRECTION_TYPE, GTK_DIR_ ## dir, \
5860 				      GTK_TYPE_HTML_CURSOR_SKIP, GTK_HTML_CURSOR_SKIP_ ## sk);
5861 
5862 	BMOVE (0, Left,     LEFT,  ONE);
5863 	BMOVE (0, KP_Left,  LEFT,  ONE);
5864 	BMOVE (0, Right,    RIGHT, ONE);
5865 	BMOVE (0, KP_Right, RIGHT, ONE);
5866 	BMOVE (0, Up,       UP  ,  ONE);
5867 	BMOVE (0, KP_Up,    UP  ,  ONE);
5868 	BMOVE (0, Down,     DOWN,  ONE);
5869 	BMOVE (0, KP_Down,  DOWN,  ONE);
5870 
5871 	BMOVE (GDK_CONTROL_MASK, KP_Left,  LEFT,  WORD);
5872 	BMOVE (GDK_CONTROL_MASK, Left,     LEFT,  WORD);
5873 	BMOVE (GDK_MOD1_MASK,    Left,     LEFT,  WORD);
5874 	BMOVE (GDK_SHIFT_MASK,   Left,     LEFT,  NONE);
5875 	BMOVE (GDK_SHIFT_MASK | GDK_CONTROL_MASK, Left,  LEFT,  NONE);
5876 
5877 	BMOVE (GDK_CONTROL_MASK, KP_Right, RIGHT, WORD);
5878 	BMOVE (GDK_CONTROL_MASK, Right,    RIGHT, WORD);
5879 	BMOVE (GDK_MOD1_MASK,    Right,    RIGHT, WORD);
5880 	BMOVE (GDK_SHIFT_MASK,   Right,    RIGHT, NONE);
5881 	BMOVE (GDK_SHIFT_MASK | GDK_CONTROL_MASK, Right, RIGHT, NONE);
5882 
5883 	BMOVE (0, Page_Up,       UP,   PAGE);
5884 	BMOVE (0, KP_Page_Up,    UP,   PAGE);
5885 	BMOVE (0, Page_Down,     DOWN, PAGE);
5886 	BMOVE (0, KP_Page_Down,  DOWN, PAGE);
5887 
5888 	BMOVE (0, Home, LEFT, ALL);
5889 	BMOVE (0, KP_Home, LEFT, ALL);
5890 	BMOVE (0, End, RIGHT, ALL);
5891 	BMOVE (0, KP_End, RIGHT, ALL);
5892 	BMOVE (GDK_CONTROL_MASK, Home, UP, ALL);
5893 	BMOVE (GDK_CONTROL_MASK, KP_Home, UP, ALL);
5894 	BMOVE (GDK_CONTROL_MASK, End, DOWN, ALL);
5895 	BMOVE (GDK_CONTROL_MASK, KP_End, DOWN, ALL);
5896 
5897 #define BCOM(m,key,com) \
5898 	gtk_binding_entry_add_signal (binding_set, GDK_KEY_ ## key, m, \
5899 				      "command", 1, \
5900 				      GTK_TYPE_HTML_COMMAND, GTK_HTML_COMMAND_ ## com);
5901 
5902 	BCOM (0, Home, SCROLL_BOD);
5903 	BCOM (0, KP_Home, SCROLL_BOD);
5904 	BCOM (0, End, SCROLL_EOD);
5905 	BCOM (0, KP_End, SCROLL_EOD);
5906 
5907 	BCOM (GDK_CONTROL_MASK, c, COPY);
5908 
5909 	BCOM (0, Return, INSERT_PARAGRAPH);
5910 	BCOM (GDK_SHIFT_MASK, Return, INSERT_PARAGRAPH);
5911 	BCOM (0, KP_Enter, INSERT_PARAGRAPH);
5912 	BCOM (GDK_SHIFT_MASK, KP_Enter, INSERT_PARAGRAPH);
5913 	BCOM (0, BackSpace, DELETE_BACK_OR_INDENT_DEC);
5914 	BCOM (GDK_SHIFT_MASK, BackSpace, DELETE_BACK_OR_INDENT_DEC);
5915 	BCOM (0, Delete, DELETE);
5916 	BCOM (0, KP_Delete, DELETE);
5917 
5918 	BCOM (GDK_CONTROL_MASK | GDK_SHIFT_MASK, plus, ZOOM_IN);
5919 	BCOM (GDK_CONTROL_MASK, plus, ZOOM_IN);
5920 	BCOM (GDK_CONTROL_MASK, equal, ZOOM_IN);
5921 	BCOM (GDK_CONTROL_MASK, KP_Add, ZOOM_IN);
5922 	BCOM (GDK_CONTROL_MASK, minus, ZOOM_OUT);
5923 	BCOM (GDK_CONTROL_MASK, KP_Subtract, ZOOM_OUT);
5924 	BCOM (GDK_CONTROL_MASK, 8, ZOOM_IN);
5925 	BCOM (GDK_CONTROL_MASK, 9, ZOOM_RESET);
5926 	BCOM (GDK_CONTROL_MASK, 0, ZOOM_OUT);
5927 	BCOM (GDK_CONTROL_MASK, KP_Multiply, ZOOM_RESET);
5928 
5929 	/* selection */
5930 	BCOM (GDK_SHIFT_MASK, Up, MODIFY_SELECTION_UP);
5931 	BCOM (GDK_SHIFT_MASK, Down, MODIFY_SELECTION_DOWN);
5932 	BCOM (GDK_SHIFT_MASK, Left, MODIFY_SELECTION_LEFT);
5933 	BCOM (GDK_SHIFT_MASK, Right, MODIFY_SELECTION_RIGHT);
5934 	BCOM (GDK_SHIFT_MASK | GDK_CONTROL_MASK, Left, MODIFY_SELECTION_PREV_WORD);
5935 	BCOM (GDK_SHIFT_MASK | GDK_CONTROL_MASK, Right, MODIFY_SELECTION_NEXT_WORD);
5936 	BCOM (GDK_SHIFT_MASK, Home, MODIFY_SELECTION_BOL);
5937 	BCOM (GDK_SHIFT_MASK, KP_Home, MODIFY_SELECTION_BOL);
5938 	BCOM (GDK_SHIFT_MASK, End, MODIFY_SELECTION_EOL);
5939 	BCOM (GDK_SHIFT_MASK, KP_End, MODIFY_SELECTION_EOL);
5940 	BCOM (GDK_SHIFT_MASK | GDK_CONTROL_MASK, Home, MODIFY_SELECTION_BOD);
5941 	BCOM (GDK_SHIFT_MASK | GDK_CONTROL_MASK, KP_Home, MODIFY_SELECTION_BOD);
5942 	BCOM (GDK_SHIFT_MASK | GDK_CONTROL_MASK, End, MODIFY_SELECTION_EOD);
5943 	BCOM (GDK_SHIFT_MASK | GDK_CONTROL_MASK, KP_End, MODIFY_SELECTION_EOD);
5944 	BCOM (GDK_SHIFT_MASK, Page_Up, MODIFY_SELECTION_PAGEUP);
5945 	BCOM (GDK_SHIFT_MASK, Page_Down, MODIFY_SELECTION_PAGEDOWN);
5946 	BCOM (GDK_SHIFT_MASK, KP_Page_Up, MODIFY_SELECTION_PAGEUP);
5947 	BCOM (GDK_SHIFT_MASK, KP_Page_Down, MODIFY_SELECTION_PAGEDOWN);
5948 	BCOM (GDK_CONTROL_MASK, a, SELECT_ALL);
5949 	BCOM (GDK_CONTROL_MASK, p, SELECT_PARAGRAPH);
5950 
5951 	/* copy, cut, paste, delete */
5952 	BCOM (GDK_CONTROL_MASK, c, COPY);
5953 	BCOM (GDK_CONTROL_MASK, Insert, COPY);
5954 	BCOM (GDK_CONTROL_MASK, KP_Insert, COPY);
5955 	BCOM (0, F16, COPY);
5956 	BCOM (GDK_CONTROL_MASK, x, CUT);
5957 	BCOM (GDK_SHIFT_MASK, Delete, CUT);
5958 	BCOM (GDK_SHIFT_MASK, KP_Delete, CUT);
5959 	BCOM (0, F20, CUT);
5960 	BCOM (GDK_CONTROL_MASK, v, PASTE);
5961 	BCOM (GDK_SHIFT_MASK, Insert, PASTE);
5962 	BCOM (GDK_SHIFT_MASK, KP_Insert, PASTE);
5963 	BCOM (0, F18, PASTE);
5964 	BCOM (GDK_CONTROL_MASK, Delete, KILL_WORD);
5965 	BCOM (GDK_CONTROL_MASK, BackSpace, KILL_WORD_BACKWARD);
5966 
5967 	/* font style */
5968 	BCOM (GDK_CONTROL_MASK, b, BOLD_TOGGLE);
5969 	BCOM (GDK_CONTROL_MASK, i, ITALIC_TOGGLE);
5970 	BCOM (GDK_CONTROL_MASK, u, UNDERLINE_TOGGLE);
5971 	BCOM (GDK_CONTROL_MASK, o, TEXT_COLOR_APPLY);
5972 	BCOM (GDK_MOD1_MASK, 1, SIZE_MINUS_2);
5973 	BCOM (GDK_MOD1_MASK, 2, SIZE_MINUS_1);
5974 	BCOM (GDK_MOD1_MASK, 3, SIZE_PLUS_0);
5975 	BCOM (GDK_MOD1_MASK, 4, SIZE_PLUS_1);
5976 	BCOM (GDK_MOD1_MASK, 5, SIZE_PLUS_2);
5977 	BCOM (GDK_MOD1_MASK, 6, SIZE_PLUS_3);
5978 	BCOM (GDK_MOD1_MASK, 7, SIZE_PLUS_4);
5979 	BCOM (GDK_MOD1_MASK, 8, SIZE_INCREASE);
5980 	BCOM (GDK_MOD1_MASK, 9, SIZE_DECREASE);
5981 
5982 	/* undo/redo */
5983 	BCOM (GDK_CONTROL_MASK, z, UNDO);
5984 	BCOM (0, F14, UNDO);
5985 	BCOM (GDK_CONTROL_MASK, r, REDO);
5986 
5987 	/* paragraph style */
5988 	BCOM (GDK_CONTROL_MASK | GDK_MOD1_MASK, l, ALIGN_LEFT);
5989 	BCOM (GDK_CONTROL_MASK | GDK_MOD1_MASK, r, ALIGN_RIGHT);
5990 	BCOM (GDK_CONTROL_MASK | GDK_MOD1_MASK, c, ALIGN_CENTER);
5991 
5992 	/* tabs */
5993 	BCOM (0, Tab, INSERT_TAB_OR_NEXT_CELL);
5994 	BCOM (GDK_SHIFT_MASK, Tab, PREV_CELL);
5995 
5996 	/* spell checking */
5997 	BCOM (GDK_SHIFT_MASK | GDK_CONTROL_MASK | GDK_MOD1_MASK, s, SPELL_SUGGEST);
5998 	BCOM (GDK_SHIFT_MASK | GDK_CONTROL_MASK | GDK_MOD1_MASK, p, SPELL_PERSONAL_DICTIONARY_ADD);
5999 	BCOM (GDK_SHIFT_MASK | GDK_CONTROL_MASK | GDK_MOD1_MASK, n, SPELL_SESSION_DICTIONARY_ADD);
6000 
6001 	/* popup menu, properties dialog */
6002 	BCOM (GDK_MOD1_MASK, space, POPUP_MENU);
6003 	BCOM (GDK_MOD1_MASK, Return, PROPERTIES_DIALOG);
6004 	BCOM (GDK_MOD1_MASK, KP_Enter, PROPERTIES_DIALOG);
6005 
6006 	/* tables */
6007 	BCOM (GDK_CONTROL_MASK | GDK_SHIFT_MASK, t, INSERT_TABLE_1_1);
6008 	BCOM (GDK_SHIFT_MASK | GDK_CONTROL_MASK, l, TABLE_INSERT_COL_AFTER);
6009 	BCOM (GDK_SHIFT_MASK | GDK_CONTROL_MASK, r, TABLE_INSERT_ROW_AFTER);
6010 	BCOM (GDK_SHIFT_MASK | GDK_CONTROL_MASK, s, TABLE_SPACING_INC);
6011 	BCOM (GDK_SHIFT_MASK | GDK_CONTROL_MASK, p, TABLE_PADDING_INC);
6012 	BCOM (GDK_SHIFT_MASK | GDK_CONTROL_MASK, o, TABLE_BORDER_WIDTH_INC);
6013 	BCOM (GDK_MOD1_MASK | GDK_CONTROL_MASK, l, TABLE_INSERT_COL_BEFORE);
6014 	BCOM (GDK_MOD1_MASK | GDK_CONTROL_MASK, r, TABLE_INSERT_ROW_BEFORE);
6015 	BCOM (GDK_MOD1_MASK | GDK_CONTROL_MASK, s, TABLE_SPACING_DEC);
6016 	BCOM (GDK_MOD1_MASK | GDK_CONTROL_MASK, p, TABLE_PADDING_DEC);
6017 	BCOM (GDK_MOD1_MASK | GDK_CONTROL_MASK, o, TABLE_BORDER_WIDTH_DEC);
6018 	BCOM (GDK_SHIFT_MASK | GDK_MOD1_MASK, l, TABLE_DELETE_COL);
6019 	BCOM (GDK_SHIFT_MASK | GDK_MOD1_MASK, r, TABLE_DELETE_ROW);
6020 	BCOM (GDK_SHIFT_MASK | GDK_MOD1_MASK, s, TABLE_SPACING_ZERO);
6021 	BCOM (GDK_SHIFT_MASK | GDK_MOD1_MASK, p, TABLE_PADDING_ZERO);
6022 	BCOM (GDK_SHIFT_MASK | GDK_MOD1_MASK, o, TABLE_BORDER_WIDTH_ZERO);
6023 }
6024 
6025 gint
gtk_html_set_iframe_parent(GtkHTML * html,GtkWidget * parent,HTMLObject * frame)6026 gtk_html_set_iframe_parent (GtkHTML *html,
6027                             GtkWidget *parent,
6028                             HTMLObject *frame)
6029 {
6030 	GtkWidget *top_level;
6031 	gint depth = 0;
6032 	g_assert (GTK_IS_HTML (parent));
6033 
6034 	gtk_html_set_animate (html, gtk_html_get_animate (GTK_HTML (parent)));
6035 
6036 	html->iframe_parent = parent;
6037 	html->frame = frame;
6038 
6039 	top_level = GTK_WIDGET (gtk_html_get_top_html (html));
6040 	if (html->engine && html->engine->painter) {
6041 		html_painter_set_widget (html->engine->painter, top_level);
6042 		gtk_html_set_fonts (html, html->engine->painter);
6043 	}
6044 	g_signal_emit (top_level, signals[IFRAME_CREATED], 0, html);
6045 
6046 	while (html->iframe_parent) {
6047 		depth++;
6048 		html = GTK_HTML (html->iframe_parent);
6049 	}
6050 
6051 	return depth;
6052 }
6053 
6054 void
gtk_html_select_word(GtkHTML * html)6055 gtk_html_select_word (GtkHTML *html)
6056 {
6057 	HTMLEngine *e;
6058 
6059 	if (!html->allow_selection)
6060 		return;
6061 
6062 	e = html->engine;
6063 	if (html_engine_get_editable (e))
6064 		html_engine_select_word_editable (e);
6065 	else
6066 		html_engine_select_word (e);
6067 
6068 	html_engine_update_selection_active_state (html->engine, html->priv->event_time);
6069 	update_primary_selection (html);
6070 }
6071 
6072 void
gtk_html_select_line(GtkHTML * html)6073 gtk_html_select_line (GtkHTML *html)
6074 {
6075 	HTMLEngine *e;
6076 
6077 	if (!html->allow_selection)
6078 		return;
6079 
6080 	e = html->engine;
6081 	if (html_engine_get_editable (e))
6082 		html_engine_select_line_editable (e);
6083 	else
6084 		html_engine_select_line (e);
6085 
6086 	html_engine_update_selection_active_state (html->engine, html->priv->event_time);
6087 	update_primary_selection (html);
6088 }
6089 
6090 void
gtk_html_select_paragraph(GtkHTML * html)6091 gtk_html_select_paragraph (GtkHTML *html)
6092 {
6093 	HTMLEngine *e;
6094 
6095 	if (!html->allow_selection)
6096 		return;
6097 
6098 	e = html->engine;
6099 	if (html_engine_get_editable (e))
6100 		html_engine_select_paragraph_editable (e);
6101 	/* FIXME: does anybody need this? if so bother me. rodo
6102 	 * else
6103 	 * html_engine_select_paragraph (e); */
6104 
6105 	html_engine_update_selection_active_state (html->engine, html->priv->event_time);
6106 	update_primary_selection (html);
6107 }
6108 
6109 void
gtk_html_select_paragraph_extended(GtkHTML * html)6110 gtk_html_select_paragraph_extended (GtkHTML *html)
6111 {
6112 	HTMLEngine *e;
6113 
6114 	if (!html->allow_selection)
6115 		return;
6116 
6117 	e = html->engine;
6118 	if (html_engine_get_editable (e))
6119 		html_engine_select_paragraph_extended (e);
6120 	/* FIXME: does anybody need this? if so bother me. rodo
6121 	 * else
6122 	 * html_engine_select_paragraph (e); */
6123 
6124 	html_engine_update_selection_active_state (html->engine, html->priv->event_time);
6125 	update_primary_selection (html);
6126 }
6127 
6128 void
gtk_html_select_all(GtkHTML * html)6129 gtk_html_select_all (GtkHTML *html)
6130 {
6131 	HTMLEngine *e;
6132 
6133 	if (!html->allow_selection)
6134 		return;
6135 
6136 	e = html->engine;
6137 
6138 	if (html_engine_get_editable (e))
6139 		html_engine_select_all_editable (e);
6140 	else {
6141 		html_engine_select_all (e);
6142 	}
6143 
6144 	html_engine_update_selection_active_state (html->engine, html->priv->event_time);
6145 	update_primary_selection (html);
6146 }
6147 
6148 void
gtk_html_unselect_all(GtkHTML * html)6149 gtk_html_unselect_all (GtkHTML *html)
6150 {
6151 	HTMLEngine *e;
6152 
6153 	e = html->engine;
6154 
6155 	html_engine_unselect_all (e);
6156 
6157 	html_engine_update_selection_active_state (html->engine, html->priv->event_time);
6158 	update_primary_selection (html);
6159 }
6160 
6161 void
gtk_html_api_set_language(GtkHTML * html)6162 gtk_html_api_set_language (GtkHTML *html)
6163 {
6164 	g_return_if_fail (GTK_IS_HTML (html));
6165 
6166 	if (html->editor_api) {
6167 		html->editor_api->set_language (html, html_engine_get_language (html->engine), html->editor_data);
6168 		html_engine_spell_check (html->engine);
6169 	}
6170 }
6171 
6172 void
gtk_html_set_editor_api(GtkHTML * html,GtkHTMLEditorAPI * api,gpointer data)6173 gtk_html_set_editor_api (GtkHTML *html,
6174                          GtkHTMLEditorAPI *api,
6175                          gpointer data)
6176 {
6177 	html->editor_api  = api;
6178 	html->editor_data = data;
6179 
6180 	gtk_html_api_set_language (html);
6181 }
6182 
6183 static const gchar *
get_value_nick(GtkHTMLCommandType com_type)6184 get_value_nick (GtkHTMLCommandType com_type)
6185 {
6186 	GEnumValue *val;
6187 	GEnumClass *enum_class;
6188 
6189 	enum_class = g_type_class_ref (GTK_TYPE_HTML_COMMAND);
6190 	val = g_enum_get_value (enum_class, com_type);
6191 	g_type_class_unref (enum_class);
6192 	if (val)
6193 		return val->value_nick;
6194 
6195 	g_warning ("Invalid GTK_TYPE_HTML_COMMAND enum value %d\n", com_type);
6196 
6197 	return NULL;
6198 }
6199 
6200 void
gtk_html_editor_event_command(GtkHTML * html,GtkHTMLCommandType com_type,gboolean before)6201 gtk_html_editor_event_command (GtkHTML *html,
6202                                GtkHTMLCommandType com_type,
6203                                gboolean before)
6204 {
6205 	GValue arg;
6206 
6207 	memset (&arg, 0, sizeof (GValue));
6208 	g_value_init (&arg, G_TYPE_STRING);
6209 	g_value_set_string (&arg, get_value_nick (com_type));
6210 
6211 	gtk_html_editor_event (html, before ? GTK_HTML_EDITOR_EVENT_COMMAND_BEFORE : GTK_HTML_EDITOR_EVENT_COMMAND_AFTER,
6212 			       &arg);
6213 
6214 	g_value_unset (&arg);
6215 }
6216 
6217 void
gtk_html_editor_event(GtkHTML * html,GtkHTMLEditorEventType event,GValue * args)6218 gtk_html_editor_event (GtkHTML *html,
6219                        GtkHTMLEditorEventType event,
6220                        GValue *args)
6221 {
6222 	GValue *retval = NULL;
6223 
6224 	if (html->editor_api && !html->engine->block_events)
6225 		retval = (*html->editor_api->event) (html, event, args, html->editor_data);
6226 
6227 	if (retval) {
6228 		g_value_unset (retval);
6229 		g_free (retval);
6230 	}
6231 }
6232 
6233 gboolean
gtk_html_command(GtkHTML * html,const gchar * command_name)6234 gtk_html_command (GtkHTML *html,
6235                   const gchar *command_name)
6236 {
6237 	GEnumClass *class;
6238 	GEnumValue *val;
6239 
6240 	g_return_val_if_fail (GTK_IS_HTML (html), FALSE);
6241 	g_return_val_if_fail (command_name != NULL, FALSE);
6242 
6243 	class = G_ENUM_CLASS (g_type_class_ref (GTK_TYPE_HTML_COMMAND));
6244 	val = g_enum_get_value_by_nick (class, command_name);
6245 	g_type_class_unref (class);
6246 	if (val) {
6247 		if (command (html, val->value)) {
6248 			if (html->priv->update_styles)
6249 				gtk_html_update_styles (html);
6250 			return TRUE;
6251 		}
6252 	}
6253 
6254 	return FALSE;
6255 }
6256 
6257 gboolean
gtk_html_edit_make_cursor_visible(GtkHTML * html)6258 gtk_html_edit_make_cursor_visible (GtkHTML *html)
6259 {
6260 	GtkAdjustment *hadjustment;
6261 	GtkAdjustment *vadjustment;
6262 	gboolean rv = FALSE;
6263 
6264 	g_return_val_if_fail (GTK_IS_HTML (html), rv);
6265 
6266 	hadjustment = gtk_layout_get_hadjustment (GTK_LAYOUT (html));
6267 	vadjustment = gtk_layout_get_vadjustment (GTK_LAYOUT (html));
6268 
6269 	html_engine_hide_cursor (html->engine);
6270 	if (html_engine_make_cursor_visible (html->engine)) {
6271 		gtk_adjustment_set_value (hadjustment, (gfloat) html->engine->x_offset);
6272 		gtk_adjustment_set_value (vadjustment, (gfloat) html->engine->y_offset);
6273 		rv = TRUE;
6274 	}
6275 	html_engine_show_cursor (html->engine);
6276 
6277 	return rv;
6278 }
6279 
6280 gboolean
gtk_html_build_with_gconf(void)6281 gtk_html_build_with_gconf (void)
6282 {
6283 	return TRUE;
6284 }
6285 
6286 static void
reparent_embedded(HTMLObject * o,HTMLEngine * e,gpointer data)6287 reparent_embedded (HTMLObject *o,
6288                    HTMLEngine *e,
6289                    gpointer data)
6290 {
6291 	if (html_object_is_embedded (o)) {
6292 		HTMLEmbedded *eo = HTML_EMBEDDED (o);
6293 		GtkWidget *parent = NULL;
6294 
6295 		if (eo->widget != NULL)
6296 			parent = gtk_widget_get_parent (eo->widget);
6297 
6298 		if (parent && GTK_IS_HTML (parent) &&
6299 		    GTK_HTML (parent)->iframe_parent == NULL) {
6300 			g_object_ref (eo->widget);
6301 			gtk_container_remove (GTK_CONTAINER (parent), eo->widget);
6302 			g_object_force_floating (G_OBJECT (eo->widget));
6303 		}
6304 		eo->parent = data;
6305 	}
6306 
6307 	if (HTML_IS_IFRAME (o) && GTK_HTML (HTML_IFRAME (o)->html)->iframe_parent &&
6308 	    GTK_HTML (GTK_HTML (HTML_IFRAME (o)->html)->iframe_parent)->iframe_parent == NULL)
6309 		gtk_html_set_iframe_parent (GTK_HTML (HTML_IFRAME (o)->html), data, o);
6310 
6311 	if (HTML_IS_FRAME (o) && GTK_HTML (HTML_FRAME (o)->html)->iframe_parent &&
6312 	    GTK_HTML (GTK_HTML (HTML_FRAME (o)->html)->iframe_parent)->iframe_parent == NULL)
6313 		gtk_html_set_iframe_parent (GTK_HTML (HTML_FRAME (o)->html), data, o);
6314 
6315 	if (HTML_IS_FRAMESET (o) && HTML_FRAMESET (o)->parent &&
6316 	    HTML_FRAMESET (o)->parent->iframe_parent == NULL) {
6317 		HTML_FRAMESET (o)->parent = data;
6318 	}
6319 }
6320 
6321 static void
gtk_html_insert_html_generic(GtkHTML * html,GtkHTML * tmp,const gchar * html_src,gboolean obj_only)6322 gtk_html_insert_html_generic (GtkHTML *html,
6323                               GtkHTML *tmp,
6324                               const gchar *html_src,
6325                               gboolean obj_only)
6326 {
6327 	GtkWidget *window, *sw;
6328 	HTMLObject *o;
6329 
6330 	html_engine_freeze (html->engine);
6331 	html_engine_delete (html->engine);
6332 	if (!tmp)
6333 		tmp    = GTK_HTML (gtk_html_new_from_string (html_src, -1));
6334 	window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
6335 	sw     = gtk_scrolled_window_new (NULL, NULL);
6336 	gtk_container_add (GTK_CONTAINER (window), GTK_WIDGET (sw));
6337 	gtk_container_add (GTK_CONTAINER (sw), GTK_WIDGET (tmp));
6338 	gtk_widget_realize (GTK_WIDGET (tmp));
6339 	html_image_factory_move_images (html->engine->image_factory, tmp->engine->image_factory);
6340 
6341 	/* copy the forms */
6342 	g_list_foreach (tmp->engine->formList, (GFunc) html_form_set_engine, html->engine);
6343 
6344 	/* move top level iframes and embedded widgets from tmp to html */
6345 	html_object_forall (tmp->engine->clue, html->engine, reparent_embedded, html);
6346 
6347 	if (tmp->engine->formList && html->engine->formList) {
6348 		GList *form_last;
6349 
6350 		form_last = g_list_last (html->engine->formList);
6351 		tmp->engine->formList->prev = form_last;
6352 		form_last->next = tmp->engine->formList;
6353 	} else if (tmp->engine->formList) {
6354 		html->engine->formList = tmp->engine->formList;
6355 	}
6356 	tmp->engine->formList = NULL;
6357 
6358 	if (obj_only) {
6359 		HTMLObject *next;
6360 		g_return_if_fail (tmp->engine->clue && HTML_CLUE (tmp->engine->clue)->head
6361 				  && HTML_CLUE (HTML_CLUE (tmp->engine->clue)->head)->head);
6362 
6363 		html_undo_level_begin (html->engine->undo, "Append HTML", "Remove appended HTML");
6364 		o = HTML_CLUE (tmp->engine->clue)->head;
6365 		for (; o; o = next) {
6366 			next = o->next;
6367 			html_object_remove_child (o->parent, o);
6368 			html_engine_append_flow (html->engine, o, html_object_get_recursive_length (o));
6369 		}
6370 		html_undo_level_end (html->engine->undo, html->engine);
6371 	} else {
6372 		g_return_if_fail (tmp->engine->clue);
6373 
6374 		o = tmp->engine->clue;
6375 		/* skip empty HTML */
6376 		if (html_object_get_recursive_length (o) > 0) {
6377 			tmp->engine->clue = NULL;
6378 			html_engine_insert_object (html->engine, o,
6379 						   html_object_get_recursive_length (o),
6380 						   html_engine_get_insert_level_for_object (html->engine, o));
6381 		}
6382 	}
6383 	gtk_widget_destroy (window);
6384 	html_engine_thaw (html->engine);
6385 }
6386 
6387 void
gtk_html_insert_html(GtkHTML * html,const gchar * html_src)6388 gtk_html_insert_html (GtkHTML *html,
6389                       const gchar *html_src)
6390 {
6391 	g_return_if_fail (GTK_IS_HTML (html));
6392 
6393 	gtk_html_insert_html_generic (html, NULL, html_src, FALSE);
6394 }
6395 
6396 void
gtk_html_insert_gtk_html(GtkHTML * html,GtkHTML * to_be_destroyed)6397 gtk_html_insert_gtk_html (GtkHTML *html,
6398                           GtkHTML *to_be_destroyed)
6399 {
6400 	g_return_if_fail (GTK_IS_HTML (html));
6401 
6402 	gtk_html_insert_html_generic (html, to_be_destroyed, NULL, FALSE);
6403 }
6404 
6405 void
gtk_html_append_html(GtkHTML * html,const gchar * html_src)6406 gtk_html_append_html (GtkHTML *html,
6407                       const gchar *html_src)
6408 {
6409 	g_return_if_fail (GTK_IS_HTML (html));
6410 
6411 	gtk_html_insert_html_generic (html, NULL, html_src, TRUE);
6412 }
6413 
6414 static void
set_magnification(HTMLObject * o,HTMLEngine * e,gpointer data)6415 set_magnification (HTMLObject *o,
6416                    HTMLEngine *e,
6417                    gpointer data)
6418 {
6419 	if (HTML_IS_FRAME (o)) {
6420 		html_font_manager_set_magnification (&GTK_HTML (HTML_FRAME (o)->html)->engine->painter->font_manager,
6421 						     *(gdouble *) data);
6422 	} else if (HTML_IS_IFRAME (o)) {
6423 		html_font_manager_set_magnification (&GTK_HTML (HTML_IFRAME (o)->html)->engine->painter->font_manager,
6424 						     *(gdouble *) data);
6425 	} else if (HTML_IS_TEXT (o))
6426 		html_text_calc_font_size (HTML_TEXT (o), e);
6427 }
6428 
6429 void
gtk_html_set_magnification(GtkHTML * html,gdouble magnification)6430 gtk_html_set_magnification (GtkHTML *html,
6431                             gdouble magnification)
6432 {
6433 	g_return_if_fail (GTK_IS_HTML (html));
6434 
6435 	if (magnification > 0.05 && magnification < 20.0
6436 	    && magnification * html->engine->painter->font_manager.var_size >= 4 * PANGO_SCALE
6437 	    && magnification * html->engine->painter->font_manager.fix_size >= 4 * PANGO_SCALE) {
6438 		html_font_manager_set_magnification (&html->engine->painter->font_manager, magnification);
6439 		if (html->engine->clue) {
6440 			html_object_forall (html->engine->clue, html->engine,
6441 					    set_magnification, &magnification);
6442 			html_object_change_set_down (html->engine->clue, HTML_CHANGE_ALL);
6443 		}
6444 
6445 		html_engine_schedule_update (html->engine);
6446 	}
6447 }
6448 
6449 #define MAG_STEP 1.1
6450 
6451 void
gtk_html_zoom_in(GtkHTML * html)6452 gtk_html_zoom_in (GtkHTML *html)
6453 {
6454 	g_return_if_fail (GTK_IS_HTML (html));
6455 
6456 	gtk_html_set_magnification (html, html->engine->painter->font_manager.magnification * MAG_STEP);
6457 }
6458 
6459 void
gtk_html_zoom_out(GtkHTML * html)6460 gtk_html_zoom_out (GtkHTML *html)
6461 {
6462 	g_return_if_fail (GTK_IS_HTML (html));
6463 	g_return_if_fail (HTML_IS_ENGINE (html->engine));
6464 
6465 	gtk_html_set_magnification (html, html->engine->painter->font_manager.magnification * (1.0 / MAG_STEP));
6466 }
6467 
6468 void
gtk_html_zoom_reset(GtkHTML * html)6469 gtk_html_zoom_reset (GtkHTML *html)
6470 {
6471 	g_return_if_fail (GTK_IS_HTML (html));
6472 
6473 	gtk_html_set_magnification (html, 1.0);
6474 }
6475 
6476 void
gtk_html_set_allow_frameset(GtkHTML * html,gboolean allow)6477 gtk_html_set_allow_frameset (GtkHTML *html,
6478                              gboolean allow)
6479 {
6480 	g_return_if_fail (GTK_IS_HTML (html));
6481 	g_return_if_fail (HTML_IS_ENGINE (html->engine));
6482 
6483 	html->engine->allow_frameset = allow;
6484 }
6485 
6486 gboolean
gtk_html_get_allow_frameset(GtkHTML * html)6487 gtk_html_get_allow_frameset (GtkHTML *html)
6488 {
6489 	g_return_val_if_fail (GTK_IS_HTML (html), FALSE);
6490 	g_return_val_if_fail (HTML_IS_ENGINE (html->engine), FALSE);
6491 
6492 	return html->engine->allow_frameset;
6493 }
6494 
6495 void
gtk_html_images_ref(GtkHTML * html)6496 gtk_html_images_ref (GtkHTML *html)
6497 {
6498 	html_image_factory_ref_all_images (HTML_IMAGE_FACTORY (html->engine->image_factory));
6499 }
6500 
6501 void
gtk_html_images_unref(GtkHTML * html)6502 gtk_html_images_unref (GtkHTML *html)
6503 {
6504 	html_image_factory_unref_all_images (HTML_IMAGE_FACTORY (html->engine->image_factory));
6505 }
6506 
6507 void
gtk_html_image_ref(GtkHTML * html,const gchar * url)6508 gtk_html_image_ref (GtkHTML *html,
6509                     const gchar *url)
6510 {
6511 	html_image_factory_ref_image_ptr (HTML_IMAGE_FACTORY (html->engine->image_factory), url);
6512 }
6513 
6514 void
gtk_html_image_unref(GtkHTML * html,const gchar * url)6515 gtk_html_image_unref (GtkHTML *html,
6516                       const gchar *url)
6517 {
6518 	html_image_factory_unref_image_ptr (HTML_IMAGE_FACTORY (html->engine->image_factory), url);
6519 }
6520 
6521 void
gtk_html_image_preload(GtkHTML * html,const gchar * url)6522 gtk_html_image_preload (GtkHTML *html,
6523                         const gchar *url)
6524 {
6525 	html_image_factory_register (HTML_IMAGE_FACTORY (html->engine->image_factory), NULL, url, FALSE);
6526 }
6527 
6528 void
gtk_html_set_blocking(GtkHTML * html,gboolean block)6529 gtk_html_set_blocking (GtkHTML *html,
6530                        gboolean block)
6531 {
6532 	html->engine->block = block;
6533 }
6534 
6535 void
gtk_html_set_images_blocking(GtkHTML * html,gboolean block)6536 gtk_html_set_images_blocking (GtkHTML *html,
6537                               gboolean block)
6538 {
6539 	html->engine->block_images = block;
6540 }
6541 
6542 gint
gtk_html_print_page_get_pages_num(GtkHTML * html,GtkPrintContext * context,gdouble header_height,gdouble footer_height)6543 gtk_html_print_page_get_pages_num (GtkHTML *html,
6544                                    GtkPrintContext *context,
6545                                    gdouble header_height,
6546                                    gdouble footer_height)
6547 {
6548 	return html_engine_print_get_pages_num (
6549 		html->engine, context, header_height, footer_height);
6550 }
6551 
6552 GtkPrintOperationResult
gtk_html_print_operation_run(GtkHTML * html,GtkPrintOperation * operation,GtkPrintOperationAction action,GtkWindow * parent,GtkHTMLPrintCalcHeight calc_header_height,GtkHTMLPrintCalcHeight calc_footer_height,GtkHTMLPrintDrawFunc draw_header,GtkHTMLPrintDrawFunc draw_footer,gpointer user_data,GError ** error)6553 gtk_html_print_operation_run (GtkHTML *html,
6554                               GtkPrintOperation *operation,
6555                               GtkPrintOperationAction action,
6556                               GtkWindow *parent,
6557                               GtkHTMLPrintCalcHeight calc_header_height,
6558                               GtkHTMLPrintCalcHeight calc_footer_height,
6559                               GtkHTMLPrintDrawFunc draw_header,
6560                               GtkHTMLPrintDrawFunc draw_footer,
6561                               gpointer user_data,
6562                               GError **error)
6563 {
6564 	return html_engine_print_operation_run (
6565 		html->engine, operation, action, parent,
6566 		calc_header_height, calc_footer_height,
6567 		draw_header, draw_footer, user_data, error);
6568 }
6569 
6570 gboolean
gtk_html_has_undo(GtkHTML * html)6571 gtk_html_has_undo (GtkHTML *html)
6572 {
6573 	return html_undo_has_undo_steps (html->engine->undo);
6574 }
6575 
6576 void
gtk_html_drop_undo(GtkHTML * html)6577 gtk_html_drop_undo (GtkHTML *html)
6578 {
6579 	html_undo_reset (html->engine->undo);
6580 }
6581 
6582 void
gtk_html_flush(GtkHTML * html)6583 gtk_html_flush (GtkHTML *html)
6584 {
6585 	html_engine_flush (html->engine);
6586 }
6587 
6588 const gchar *
gtk_html_get_object_id_at(GtkHTML * html,gint x,gint y)6589 gtk_html_get_object_id_at (GtkHTML *html,
6590                            gint x,
6591                            gint y)
6592 {
6593 	HTMLObject *o = html_engine_get_object_at (html->engine, x, y, NULL, FALSE);
6594 	const gchar *id = NULL;
6595 
6596 	while (o) {
6597 		id = html_object_get_id (o);
6598 		if (id)
6599 			break;
6600 		o = o->parent;
6601 	}
6602 
6603 	return id;
6604 }
6605 
6606 gchar *
gtk_html_get_url_at(GtkHTML * html,gint x,gint y)6607 gtk_html_get_url_at (GtkHTML *html,
6608                      gint x,
6609                      gint y)
6610 {
6611 	HTMLObject *obj;
6612 	gint offset;
6613 
6614 	g_return_val_if_fail (GTK_IS_HTML (html), NULL);
6615 
6616 	obj = html_engine_get_object_at (html->engine, x, y, (guint *) &offset, FALSE);
6617 
6618 	if (obj)
6619 		return gtk_html_get_url_object_relative (html, obj, html_object_get_url (obj, offset));
6620 
6621 	return NULL;
6622 }
6623 
6624 gchar *
gtk_html_get_cursor_url(GtkHTML * html)6625 gtk_html_get_cursor_url (GtkHTML *html)
6626 {
6627 	HTMLObject *obj;
6628 	gint offset;
6629 
6630 	g_return_val_if_fail (GTK_IS_HTML (html), NULL);
6631 
6632 	if (html->engine->caret_mode) {
6633 		obj = html->engine->cursor->object;
6634 		offset = html->engine->cursor->offset;
6635 	} else
6636 		obj = html_engine_get_focus_object (html->engine, &offset);
6637 
6638 	if (obj)
6639 		return gtk_html_get_url_object_relative (html, obj, html_object_get_url (obj, offset));
6640 
6641 	return NULL;
6642 }
6643 
6644 gchar *
gtk_html_get_image_src_at(GtkHTML * html,gint x,gint y)6645 gtk_html_get_image_src_at (GtkHTML *html,
6646                            gint x,
6647                            gint y)
6648 {
6649 	HTMLObject *obj;
6650 	gint offset;
6651 
6652 	g_return_val_if_fail (GTK_IS_HTML (html), NULL);
6653 
6654 	obj = html_engine_get_object_at (html->engine, x, y, (guint *) &offset, FALSE);
6655 
6656 	if (obj && HTML_IS_IMAGE (obj)) {
6657 		HTMLImage *image = (HTMLImage *) obj;
6658 
6659 		if (!image->image_ptr)
6660 			return NULL;
6661 
6662 		return g_strdup (image->image_ptr->url);
6663 	}
6664 
6665 	return NULL;
6666 }
6667 
6668 /* Unref when done with it */
6669 GdkPixbufAnimation *
gtk_html_get_image_at(GtkHTML * html,gint x,gint y)6670 gtk_html_get_image_at (GtkHTML *html,
6671                        gint x,
6672                        gint y)
6673 {
6674 	HTMLObject *obj;
6675 	gint offset;
6676 
6677 	g_return_val_if_fail (GTK_IS_HTML (html), NULL);
6678 
6679 	obj = html_engine_get_object_at (html->engine, x, y, (guint *) &offset, FALSE);
6680 
6681 	if (obj && HTML_IS_IMAGE (obj)) {
6682 		HTMLImage *image = (HTMLImage *) obj;
6683 
6684 		if (!image->image_ptr || !image->image_ptr->animation)
6685 			return NULL;
6686 
6687 		return g_object_ref (image->image_ptr->animation);
6688 	}
6689 
6690 	return NULL;
6691 }
6692 
6693 gchar *
gtk_html_get_cursor_image_src(GtkHTML * html)6694 gtk_html_get_cursor_image_src (GtkHTML *html)
6695 {
6696 	HTMLObject *obj;
6697 	gint offset;
6698 
6699 	g_return_val_if_fail (GTK_IS_HTML (html), NULL);
6700 
6701 	if (html->engine->caret_mode) {
6702 		obj = html->engine->cursor->object;
6703 		offset = html->engine->cursor->offset;
6704 	} else
6705 		obj = html_engine_get_focus_object (html->engine, &offset);
6706 
6707 	if (obj && HTML_IS_IMAGE (obj)) {
6708 		HTMLImage *image = (HTMLImage *) obj;
6709 
6710 		if (!image->image_ptr)
6711 			return NULL;
6712 
6713 		return g_strdup (image->image_ptr->url);
6714 	}
6715 
6716 	return NULL;
6717 }
6718 
6719 void
gtk_html_set_tokenizer(GtkHTML * html,HTMLTokenizer * tokenizer)6720 gtk_html_set_tokenizer (GtkHTML *html,
6721                         HTMLTokenizer *tokenizer)
6722 {
6723 	g_return_if_fail (GTK_IS_HTML (html));
6724 
6725 	html_engine_set_tokenizer (html->engine, tokenizer);
6726 }
6727 
6728 gboolean
gtk_html_get_cursor_pos(GtkHTML * html,gint * position,gint * offset)6729 gtk_html_get_cursor_pos (GtkHTML *html,
6730                          gint *position,
6731                          gint *offset)
6732 {
6733 	gboolean read = FALSE;
6734 
6735 	g_return_val_if_fail (html != NULL, FALSE);
6736 	g_return_val_if_fail (GTK_IS_HTML (html), FALSE);
6737 
6738 	if (html->engine && html->engine->cursor) {
6739 		read = TRUE;
6740 
6741 		if (position)
6742 			*position = html->engine->cursor->position;
6743 		if (offset)
6744 			*offset = html->engine->cursor->offset;
6745 	}
6746 
6747 	return read;
6748 }
6749 
6750 gchar *
gtk_html_filename_from_uri(const gchar * uri)6751 gtk_html_filename_from_uri (const gchar *uri)
6752 {
6753 	const gchar *relative_fpath;
6754 	gchar *temp_uri, *temp_filename;
6755 	gchar *retval;
6756 
6757 	if (!uri || !*uri)
6758 		return NULL;
6759 
6760 	if (g_ascii_strncasecmp (uri, "file://", 7) == 0)
6761 		return g_filename_from_uri (uri, NULL, NULL);
6762 
6763 	if (g_ascii_strncasecmp (uri, "file:", 5) == 0) {
6764 		/* Relative (file or other) URIs shouldn't contain the
6765 		 * scheme prefix at all. But accept such broken URIs
6766 		 * anyway. Whether they are URI-encoded or not is
6767 		 * anybody's guess, assume they are.
6768 		 */
6769 		relative_fpath = uri + 5;
6770 	} else {
6771 		/* A proper relative file URI. Just do the URI-decoding. */
6772 		relative_fpath = uri;
6773 	}
6774 
6775 	if (g_path_is_absolute (relative_fpath)) {
6776 		/* The totally broken case of "file:" followed
6777 		 * directly by an absolute pathname.
6778 		 */
6779 		/* file:/foo/bar.zap or file:c:/foo/bar.zap */
6780 #ifdef G_OS_WIN32
6781 		if (g_ascii_isalpha (relative_fpath[0]) && relative_fpath[1] == ':')
6782 			temp_uri = g_strconcat ("file:///", relative_fpath, NULL);
6783 		else
6784 			temp_uri = g_strconcat ("file://", relative_fpath, NULL);
6785 #else
6786 		temp_uri = g_strconcat ("file://", relative_fpath, NULL);
6787 #endif
6788 		retval = g_filename_from_uri (temp_uri, NULL, NULL);
6789 		g_free (temp_uri);
6790 
6791 		return retval;
6792 	}
6793 
6794 	/* Create a dummy absolute file: URI and call
6795 	 * g_filename_from_uri(), then strip off the dummy
6796 	 * prefix.
6797 	 */
6798 #ifdef G_OS_WIN32
6799 	if (g_ascii_isalpha (relative_fpath[0]) && relative_fpath[1] == ':') {
6800 		/* file:c:relative/path/foo.bar */
6801 		gchar drive_letter = relative_fpath[0];
6802 
6803 		temp_uri = g_strconcat ("file:///dummy/", relative_fpath + 2, NULL);
6804 		temp_filename = g_filename_from_uri (temp_uri, NULL, NULL);
6805 		g_free (temp_uri);
6806 
6807 		if (temp_filename == NULL)
6808 			return NULL;
6809 
6810 		g_assert (strncmp (temp_filename, G_DIR_SEPARATOR_S "dummy" G_DIR_SEPARATOR_S, 7) == 0);
6811 
6812 		retval = g_strdup_printf ("%c:%s", drive_letter, temp_filename + 7);
6813 		g_free (temp_filename);
6814 
6815 		return retval;
6816 	}
6817 #endif
6818 	temp_uri = g_strconcat ("file:///dummy/", relative_fpath, NULL);
6819 	temp_filename = g_filename_from_uri (temp_uri, NULL, NULL);
6820 	g_free (temp_uri);
6821 
6822 	if (temp_filename == NULL)
6823 		return NULL;
6824 
6825 	g_assert (strncmp (temp_filename, G_DIR_SEPARATOR_S "dummy" G_DIR_SEPARATOR_S, 7) == 0);
6826 
6827 	retval = g_strdup (temp_filename + 7);
6828 	g_free (temp_filename);
6829 
6830 	return retval;
6831 }
6832 
6833 gchar *
gtk_html_filename_to_uri(const gchar * filename)6834 gtk_html_filename_to_uri (const gchar *filename)
6835 {
6836 	gchar *fake_filename, *fake_uri, *retval;
6837 	const gchar dummy_prefix[] = "file:///dummy/";
6838 	const gint dummy_prefix_len = sizeof (dummy_prefix) - 1;
6839 #ifdef G_OS_WIN32
6840 	gchar drive_letter = 0;
6841 #else
6842 	gchar *first_end, *colon;
6843 #endif
6844 
6845 	if (!filename || !*filename)
6846 		return NULL;
6847 
6848 	if (g_path_is_absolute (filename))
6849 		return g_filename_to_uri (filename, NULL, NULL);
6850 
6851 	/* filename is a relative path, and the corresponding URI is
6852 	 * filename as such but URI-escaped. Instead of yet again
6853 	 * copy-pasteing the URI-escape code from gconvert.c (or
6854 	 * somewhere else), prefix a fake top-level directory to make
6855 	 * it into an absolute path, call g_filename_to_uri() to turn it
6856 	 * into a full file: URI, and then strip away the file:/// and
6857 	 * the fake top-level directory.
6858 	 */
6859 
6860 #ifdef G_OS_WIN32
6861 	if (g_ascii_isalpha (*filename) && filename[1] == ':') {
6862 		/* A non-absolute path, but with a drive letter. Ugh. */
6863 		drive_letter = *filename;
6864 		filename += 2;
6865 	}
6866 #endif
6867 	fake_filename = g_build_filename ("/dummy", filename, NULL);
6868 	fake_uri = g_filename_to_uri (fake_filename, NULL, NULL);
6869 	g_free (fake_filename);
6870 
6871 	if (fake_uri == NULL)
6872 		return NULL;
6873 
6874 	g_assert (strncmp (fake_uri, dummy_prefix, dummy_prefix_len) == 0);
6875 
6876 #ifdef G_OS_WIN32
6877 	/* Re-insert the drive letter if we had one. Double ugh.
6878 	 * URI-encode the colon so the drive letter isn't taken for a
6879 	 * URI scheme!
6880 	 */
6881 	if (drive_letter)
6882 		retval = g_strdup_printf ("%c%%3a%s",
6883 					  drive_letter,
6884 					  fake_uri + dummy_prefix_len);
6885 	else
6886 		retval = g_strdup (fake_uri + dummy_prefix_len);
6887 #else
6888 	retval = g_strdup (fake_uri + dummy_prefix_len);
6889 #endif
6890 	g_free (fake_uri);
6891 
6892 #ifdef G_OS_UNIX
6893 	/* Check if there are colons in the first component of the
6894 	 * pathname, and URI-encode them so that the part up to the
6895 	 * colon isn't taken for a URI scheme name! This isn't
6896 	 * necessary on Win32 as there can't be colons in a file name
6897 	 * in the first place.
6898 	 */
6899 	first_end = strchr (retval, '/');
6900 	if (first_end == NULL)
6901 		first_end = retval + strlen (retval);
6902 
6903 	while ((colon = strchr (retval, ':')) != NULL && colon < first_end) {
6904 		gchar *new_retval = g_malloc (strlen (retval) + 3);
6905 
6906 		strncpy (new_retval, retval, colon - retval);
6907 		strcpy (new_retval + (colon - retval), "%3a");
6908 		strcpy (new_retval + (colon - retval) + 3, colon + 1);
6909 
6910 		g_free (retval);
6911 		retval = new_retval;
6912 	}
6913 #endif
6914 
6915 	return retval;
6916 }
6917 
6918 #ifdef UNIT_TEST_URI_CONVERSIONS
6919 
6920 /* To test the uri<->filename code, cut&paste the above two functions
6921  * and this part into a separate file, insert #include <glib.h>, and
6922  * build with -DUNIT_TEST_URI_CONVERSIONS.
6923  */
6924 
6925 static const gchar *const tests[][3] = {
6926 	/* Each test case has three strings:
6927 	 *
6928 	 * 0) a URI, the source for the uri->filename conversion test,
6929 	 * or NULL if this test is only for the filename->uri
6930 	 * direction.
6931 	 *
6932 	 * 1) a filename or NULL, the expected result from
6933 	 * uri->filename conversion. If non-NULL also the source for
6934 	 * the filename->uri conversion test,
6935 	 *
6936 	 * 2) a URI if the expected result from filename->uri is
6937 	 * different than string 0, or NULL if the result should be
6938 	 * equal to string 0.
6939 	 */
6940 	{ "file:///top/s%20pace%20d/sub/file", "/top/s pace d/sub/file", NULL },
6941 	{ "file:///top/sub/sub/", "/top/sub/sub/", NULL },
6942 	{ "file:///top/sub/file#segment", NULL, NULL },
6943 	{ "file://tem", NULL, NULL },
6944 	{ "file:/tem", "/tem", "file:///tem" },
6945 	{ "file:sub/tem", "sub/tem", "sub/tem" },
6946 	{ "sub/tem", "sub/tem", NULL },
6947 	{ "s%20pace%20d/tem", "s pace d/tem", NULL },
6948 	{ "tem", "tem", NULL },
6949 	{ "tem#segment", NULL, NULL },
6950 #ifdef G_OS_WIN32
6951 	/* More or less same tests, but including a drive letter */
6952 	{ "file:///x:/top/s%20pace%20d/sub/file", "x:/top/s pace d/sub/file", NULL },
6953 	{ "file:///x:top/sub/sub/", "x:top/sub/sub/", "x%3atop/sub/sub/" },
6954 	{ "file:///x:top/sub/file#segment", NULL, NULL },
6955 	{ "file://x:tem", NULL, NULL },
6956 	{ "file:x:/tem", "x:/tem", "file:///x:/tem" },
6957 	{ "file:x:tem", "x:tem", "x%3atem" },
6958 	{ "file:x:sub/tem", "x:sub/tem", "x%3asub/tem" },
6959 	{ "x%3as%20pace%20d/tem", "x:s pace d/tem", NULL },
6960 	{ "x%3atem", "x:tem", NULL },
6961 	{ "x%3atem#segment", NULL, NULL },
6962 #endif
6963 #ifdef G_OS_UNIX
6964 	/* Test filenames with a colon in them. That's not possible on Win32 */
6965 	{ "file:///top/silly:name/bar", "/top/silly:name/bar", NULL },
6966 	{ "silly%3aname/bar", "silly:name/bar", NULL },
6967 	{ "silly%3aname", "silly:name", NULL },
6968 #endif
6969   { NULL, NULL }
6970 };
6971 
6972 gint
main(gint argc,gchar ** argv)6973 main (gint argc,
6974       gchar **argv)
6975 {
6976 	gint failures = 0;
6977 	gint i;
6978 
6979 	for (i = 0; i < G_N_ELEMENTS (tests); i++) {
6980 		gchar *filename;
6981 #ifdef G_OS_WIN32
6982 		gchar *expected_result;
6983 		gchar *slash;
6984 #else
6985 		const gchar *expected_result;
6986 #endif
6987 		if (tests[i][0] == NULL)
6988 			continue;
6989 
6990 		filename = gtk_html_filename_from_uri (tests[i][0]);
6991 #ifdef G_OS_WIN32
6992 		expected_result = g_strdup (tests[i][1]);
6993 		if (expected_result)
6994 			while ((slash = strchr (expected_result, '/')) != NULL)
6995 				*slash = '\\';
6996 #else
6997 		expected_result = tests[i][1];
6998 #endif
6999 
7000 		if (((filename == NULL) != (expected_result == NULL)) ||
7001 		    (filename != NULL && strcmp (filename, expected_result) != 0)) {
7002 			g_print ("FAIL: %s -> %s, GOT: %s\n",
7003 				 tests[i][0],
7004 				 expected_result ? expected_result : "NULL",
7005 				 filename ? filename : "NULL");
7006 			failures++;
7007 		} else {
7008 			g_print ("OK: %s -> %s\n",
7009 				 tests[i][0],
7010 				 filename ? filename : "NULL");
7011 		}
7012 	}
7013 
7014 	for (i = 0; i < G_N_ELEMENTS (tests); i++) {
7015 		gchar *uri;
7016 		const gchar *expected_result;
7017 
7018 		if (tests[i][1] == NULL)
7019 			continue;
7020 
7021 		uri = gtk_html_filename_to_uri (tests[i][1]);
7022 		expected_result = tests[i][2] ? tests[i][2] : tests[i][0];
7023 		if (((uri == NULL) != (expected_result == NULL)) ||
7024 		    (uri != NULL && strcmp (uri, expected_result) != 0)) {
7025 			g_printf ("FAIL: %s -> %s, GOT: %s\n",
7026 				  tests[i][1],
7027 				  expected_result ? expected_result : "NULL",
7028 				  uri ? uri : "NULL");
7029 			failures++;
7030 		} else {
7031 			g_print ("OK: %s -> %s\n",
7032 				 tests[i][1],
7033 				 uri ? uri : "NULL");
7034 		}
7035 	}
7036 
7037 #ifdef G_OS_WIN32
7038 	/* Test filename->uri also with backslashes */
7039 	for (i = 0; i < G_N_ELEMENTS (tests); i++) {
7040 		gchar *uri;
7041 		gchar *filename, *slash;
7042 		const gchar *expected_result;
7043 
7044 		if (tests[i][1] == NULL || strchr (tests[i][1], '/') == NULL)
7045 			continue;
7046 
7047 		filename = g_strdup (tests[i][1]);
7048 		while ((slash = strchr (filename, '/')) != NULL)
7049 			*slash = '\\';
7050 		uri = gtk_html_filename_to_uri (tests[i][1]);
7051 		expected_result = tests[i][2] ? tests[i][2] : tests[i][0];
7052 		if (((uri == NULL) != (expected_result == NULL)) ||
7053 		    (uri != NULL && strcmp (uri, expected_result) != 0)) {
7054 			g_printf ("FAIL: %s -> %s, GOT: %s\n",
7055 				  filename,
7056 				  expected_result ? expected_result : "NULL",
7057 				  uri ? uri : "NULL");
7058 			failures++;
7059 		} else {
7060 			g_print ("OK: %s -> %s\n",
7061 				 filename,
7062 				 uri ? uri : "NULL");
7063 		}
7064 	}
7065 #endif
7066 
7067 	return failures != 0;
7068 }
7069 
7070 #endif
7071