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, ¤t_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 (>K_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 (>K_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