source: trunk/third/gtkhtml3/src/gtkhtml.c @ 21460

Revision 21460, 156.9 KB checked in by ghudson, 20 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r21459, which included commits to RCS files with non-trunk default branches.
Line 
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., 59 Temple Place - Suite 330,
19    Boston, MA 02111-1307, USA.
20*/
21
22#include <config.h>
23#include <ctype.h>
24
25#include <gdk/gdkkeysyms.h>
26#include <gdk/gdkprivate.h>
27#include <gdk-pixbuf/gdk-pixbuf.h>
28#include <gtk/gtk.h>
29#include <string.h>
30
31#include <gnome.h>
32
33#include "../a11y/factory.h"
34
35#include "htmlcolorset.h"
36#include "htmlcursor.h"
37#include "htmldrawqueue.h"
38#include "htmlengine-edit.h"
39#include "htmlengine-edit-clueflowstyle.h"
40#include "htmlengine-edit-cut-and-paste.h"
41#include "htmlengine-edit-fontstyle.h"
42#include "htmlengine-edit-rule.h"
43#include "htmlengine-edit-movement.h"
44#include "htmlengine-edit-cursor.h"
45#include "htmlengine-edit-table.h"
46#include "htmlengine-edit-tablecell.h"
47#include "htmlengine-edit-text.h"
48#include "htmlengine-edit-selection-updater.h"
49#include "htmlengine-print.h"
50#include "htmlengine-save.h"
51#include "htmlform.h"
52#include "htmlframe.h"
53#include "htmliframe.h"
54#include "htmlimage.h"
55#include "htmlinterval.h"
56#include "htmlmarshal.h"
57#include "htmlplainpainter.h"
58#include "htmlsettings.h"
59#include "htmltable.h"
60#include "htmltext.h"
61#include "htmltextslave.h"
62#include "htmlselection.h"
63#include "htmlundo.h"
64
65#include "gtkhtml.h"
66#include "gtkhtml-embedded.h"
67#include "gtkhtml-keybinding.h"
68#include "gtkhtml-search.h"
69#include "gtkhtml-stream.h"
70#include "gtkhtml-private.h"
71#include "gtkhtml-properties.h"
72#include "math.h"
73#include <libgnome/gnome-util.h>
74
75enum DndTargetType {
76        DND_TARGET_TYPE_TEXT_URI_LIST,
77        DND_TARGET_TYPE_MOZILLA_URL,
78        DND_TARGET_TYPE_TEXT_HTML,
79        DND_TARGET_TYPE_UTF8_STRING,
80        DND_TARGET_TYPE_TEXT_PLAIN,
81        DND_TARGET_TYPE_STRING,
82};
83
84static GtkTargetEntry dnd_link_sources [] = {
85        { "text/uri-list", 0, DND_TARGET_TYPE_TEXT_URI_LIST },
86        { "text/x-moz-url", 0, DND_TARGET_TYPE_MOZILLA_URL },
87        { "text/html", 0, DND_TARGET_TYPE_TEXT_HTML },
88        { "UTF8_STRING", 0, DND_TARGET_TYPE_UTF8_STRING },
89        { "text/plain", 0, DND_TARGET_TYPE_TEXT_PLAIN },
90        { "STRING", 0, DND_TARGET_TYPE_STRING },
91};
92#define DND_LINK_SOURCES sizeof (dnd_link_sources) / sizeof (GtkTargetEntry)
93
94#define GNOME_SPELL_GCONF_DIR "/GNOME/Spell"
95
96#define d_s(x)
97#define D_IM(x)
98
99static GtkLayoutClass *parent_class = NULL;
100
101GConfClient *gconf_client = NULL;
102GError      *gconf_error  = NULL;
103
104enum {
105        TITLE_CHANGED,
106        URL_REQUESTED,
107        LOAD_DONE,
108        LINK_CLICKED,
109        SET_BASE,
110        SET_BASE_TARGET,
111        ON_URL,
112        REDIRECT,
113        SUBMIT,
114        OBJECT_REQUESTED,
115        CURRENT_PARAGRAPH_STYLE_CHANGED,
116        CURRENT_PARAGRAPH_INDENTATION_CHANGED,
117        CURRENT_PARAGRAPH_ALIGNMENT_CHANGED,
118        INSERTION_FONT_STYLE_CHANGED,
119        INSERTION_COLOR_CHANGED,
120        SIZE_CHANGED,
121        IFRAME_CREATED,
122        /* keybindings signals */
123        SCROLL,
124        CURSOR_MOVE,
125        COMMAND,
126        /* now only last signal */
127        LAST_SIGNAL
128};
129
130/* #define USE_PROPS */
131#ifdef USE_PROPS
132enum {
133        PROP_0,
134        PROP_EDITABLE,
135        PROP_TITLE,
136        PROP_DOCUMENT_BASE,
137        PROP_TARGET_BASE,
138};
139
140static void     gtk_html_get_property  (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
141static void     gtk_html_set_property  (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
142
143#endif
144
145static guint signals [LAST_SIGNAL] = { 0 };
146
147static void
148gtk_html_update_scrollbars_on_resize (GtkHTML *html,
149                                      gdouble old_doc_width, gdouble old_doc_height,
150                                      gdouble old_width, gdouble old_height,
151                                      gboolean *changed_x, gboolean *changed_y);
152
153/* keybindings signal hadlers */
154static void     scroll                 (GtkHTML *html, GtkOrientation orientation, GtkScrollType scroll_type, gfloat position);
155static void     cursor_move            (GtkHTML *html, GtkDirectionType dir_type, GtkHTMLCursorSkipType skip);
156static gboolean command                (GtkHTML *html, GtkHTMLCommandType com_type);
157static gint     mouse_change_pos       (GtkWidget *widget, GdkWindow *window, gint x, gint y, gint state);
158static void     add_bindings           (GtkHTMLClass *klass);
159static gchar *  get_value_nick         (GtkHTMLCommandType com_type);                                   
160
161
162/* Values for selection information.  FIXME: what about COMPOUND_STRING and
163   TEXT?  */
164enum _TargetInfo {
165        TARGET_HTML,
166        TARGET_UTF8_STRING,
167        TARGET_UTF8,
168        TARGET_COMPOUND_TEXT,
169        TARGET_STRING,
170        TARGET_TEXT
171};
172
173typedef enum _TargetInfo TargetInfo;
174
175/* Interval for scrolling during selection.  */
176#define SCROLL_TIMEOUT_INTERVAL 10
177
178
179GtkHTMLParagraphStyle
180clueflow_style_to_paragraph_style (HTMLClueFlowStyle style, HTMLListType item_type)
181{
182        switch (style) {
183        case HTML_CLUEFLOW_STYLE_NORMAL:
184                return GTK_HTML_PARAGRAPH_STYLE_NORMAL;
185        case HTML_CLUEFLOW_STYLE_H1:
186                return GTK_HTML_PARAGRAPH_STYLE_H1;
187        case HTML_CLUEFLOW_STYLE_H2:
188                return GTK_HTML_PARAGRAPH_STYLE_H2;
189        case HTML_CLUEFLOW_STYLE_H3:
190                return GTK_HTML_PARAGRAPH_STYLE_H3;
191        case HTML_CLUEFLOW_STYLE_H4:
192                return GTK_HTML_PARAGRAPH_STYLE_H4;
193        case HTML_CLUEFLOW_STYLE_H5:
194                return GTK_HTML_PARAGRAPH_STYLE_H5;
195        case HTML_CLUEFLOW_STYLE_H6:
196                return GTK_HTML_PARAGRAPH_STYLE_H6;
197        case HTML_CLUEFLOW_STYLE_ADDRESS:
198                return GTK_HTML_PARAGRAPH_STYLE_ADDRESS;
199        case HTML_CLUEFLOW_STYLE_PRE:
200                return GTK_HTML_PARAGRAPH_STYLE_PRE;
201        case HTML_CLUEFLOW_STYLE_LIST_ITEM:
202                switch (item_type) {
203                case HTML_LIST_TYPE_UNORDERED:
204                        return GTK_HTML_PARAGRAPH_STYLE_ITEMDOTTED;
205                case HTML_LIST_TYPE_ORDERED_ARABIC:
206                        return GTK_HTML_PARAGRAPH_STYLE_ITEMDIGIT;
207                case HTML_LIST_TYPE_ORDERED_LOWER_ROMAN:
208                case HTML_LIST_TYPE_ORDERED_UPPER_ROMAN:
209                        return GTK_HTML_PARAGRAPH_STYLE_ITEMROMAN;
210                case HTML_LIST_TYPE_ORDERED_LOWER_ALPHA:
211                case HTML_LIST_TYPE_ORDERED_UPPER_ALPHA:
212                        return GTK_HTML_PARAGRAPH_STYLE_ITEMALPHA;
213                default:
214                        return GTK_HTML_PARAGRAPH_STYLE_ITEMDOTTED;
215                }
216        default:                /* This should not really happen, though.  */
217                return GTK_HTML_PARAGRAPH_STYLE_NORMAL;
218        }
219}
220
221void
222paragraph_style_to_clueflow_style (GtkHTMLParagraphStyle style, HTMLClueFlowStyle *flow_style, HTMLListType *item_type)
223{
224        *item_type = HTML_LIST_TYPE_BLOCKQUOTE;
225        *flow_style = HTML_CLUEFLOW_STYLE_LIST_ITEM;
226
227        switch (style) {
228        case GTK_HTML_PARAGRAPH_STYLE_NORMAL:
229                *flow_style = HTML_CLUEFLOW_STYLE_NORMAL;
230                break;
231        case GTK_HTML_PARAGRAPH_STYLE_H1:
232                *flow_style = HTML_CLUEFLOW_STYLE_H1;
233                break;
234        case GTK_HTML_PARAGRAPH_STYLE_H2:
235                *flow_style = HTML_CLUEFLOW_STYLE_H2;
236                break;
237        case GTK_HTML_PARAGRAPH_STYLE_H3:
238                *flow_style = HTML_CLUEFLOW_STYLE_H3;
239                break;
240        case GTK_HTML_PARAGRAPH_STYLE_H4:
241                *flow_style = HTML_CLUEFLOW_STYLE_H4;
242                break;
243        case GTK_HTML_PARAGRAPH_STYLE_H5:
244                *flow_style = HTML_CLUEFLOW_STYLE_H5;
245                break;
246        case GTK_HTML_PARAGRAPH_STYLE_H6:
247                *flow_style = HTML_CLUEFLOW_STYLE_H6;
248                break;
249        case GTK_HTML_PARAGRAPH_STYLE_ADDRESS:
250                *flow_style = HTML_CLUEFLOW_STYLE_ADDRESS;
251                break;
252        case GTK_HTML_PARAGRAPH_STYLE_PRE:
253                *flow_style = HTML_CLUEFLOW_STYLE_PRE;
254                break;
255        case GTK_HTML_PARAGRAPH_STYLE_ITEMDOTTED:
256                *item_type = HTML_LIST_TYPE_UNORDERED;
257                break;
258        case GTK_HTML_PARAGRAPH_STYLE_ITEMROMAN:
259                *item_type = HTML_LIST_TYPE_ORDERED_UPPER_ROMAN;
260                break;
261        case GTK_HTML_PARAGRAPH_STYLE_ITEMALPHA:
262                *item_type = HTML_LIST_TYPE_ORDERED_UPPER_ALPHA;
263                break;
264        case GTK_HTML_PARAGRAPH_STYLE_ITEMDIGIT:
265                *item_type = HTML_LIST_TYPE_ORDERED_ARABIC;
266                break;
267        default:                /* This should not really happen, though.  */
268                *flow_style = HTML_CLUEFLOW_STYLE_NORMAL;
269        }
270}
271
272HTMLHAlignType
273paragraph_alignment_to_html (GtkHTMLParagraphAlignment alignment)
274{
275        switch (alignment) {
276        case GTK_HTML_PARAGRAPH_ALIGNMENT_LEFT:
277                return HTML_HALIGN_LEFT;
278        case GTK_HTML_PARAGRAPH_ALIGNMENT_RIGHT:
279                return HTML_HALIGN_RIGHT;
280        case GTK_HTML_PARAGRAPH_ALIGNMENT_CENTER:
281                return HTML_HALIGN_CENTER;
282        default:
283                return HTML_HALIGN_LEFT;
284        }
285}
286
287GtkHTMLParagraphAlignment
288html_alignment_to_paragraph (HTMLHAlignType alignment)
289{
290        switch (alignment) {
291        case HTML_HALIGN_LEFT:
292                return GTK_HTML_PARAGRAPH_ALIGNMENT_LEFT;
293        case HTML_HALIGN_CENTER:
294                return GTK_HTML_PARAGRAPH_ALIGNMENT_CENTER;
295        case HTML_HALIGN_RIGHT:
296                return GTK_HTML_PARAGRAPH_ALIGNMENT_RIGHT;
297        default:
298                return GTK_HTML_PARAGRAPH_ALIGNMENT_LEFT;
299        }
300}
301
302void
303gtk_html_update_styles (GtkHTML *html)
304{
305        GtkHTMLParagraphStyle paragraph_style;
306        GtkHTMLParagraphAlignment alignment;
307        HTMLEngine *engine;
308        HTMLClueFlowStyle flow_style;
309        HTMLListType item_type;
310        guint indentation;
311
312        /* printf ("gtk_html_update_styles called\n"); */
313
314        if (! html_engine_get_editable (html->engine))
315                return;
316
317        engine          = html->engine;
318        html_engine_get_current_clueflow_style (engine, &flow_style, &item_type);
319        paragraph_style = clueflow_style_to_paragraph_style (flow_style, item_type);
320
321        if (paragraph_style != html->priv->paragraph_style) {
322                html->priv->paragraph_style = paragraph_style;
323                g_signal_emit (html, signals [CURRENT_PARAGRAPH_STYLE_CHANGED], 0, paragraph_style);
324        }
325
326        indentation = html_engine_get_current_clueflow_indentation (engine);
327        if (indentation != html->priv->paragraph_indentation) {
328                html->priv->paragraph_indentation = indentation;
329                g_signal_emit (html, signals [CURRENT_PARAGRAPH_INDENTATION_CHANGED], 0, indentation);
330        }
331
332        alignment = html_alignment_to_paragraph (html_engine_get_current_clueflow_alignment (engine));
333        if (alignment != html->priv->paragraph_alignment) {
334                html->priv->paragraph_alignment = alignment;
335                g_signal_emit (html, signals [CURRENT_PARAGRAPH_ALIGNMENT_CHANGED], 0, alignment);
336        }
337
338        if (html_engine_update_insertion_font_style (engine))
339                g_signal_emit (html, signals [INSERTION_FONT_STYLE_CHANGED], 0, engine->insertion_font_style);
340        if (html_engine_update_insertion_color (engine))
341                g_signal_emit (html, signals [INSERTION_COLOR_CHANGED], 0, engine->insertion_color);
342
343        /* TODO add insertion_url_or_targed_changed signal */
344        html_engine_update_insertion_url_and_target (engine);
345}
346
347
348/* GTK+ idle loop handler.  */
349
350static gint
351idle_handler (gpointer data)
352{
353        GtkHTML *html;
354        HTMLEngine *engine;
355
356        html = GTK_HTML (data);
357        engine = html->engine;
358
359        if (html->priv->scroll_timeout_id == 0  &&
360            html->engine->thaw_idle_id == 0  &&
361            !html_engine_frozen (html->engine))
362                html_engine_make_cursor_visible (engine);
363
364        if (html->engine->thaw_idle_id == 0 && !html_engine_frozen (html->engine))
365                html_engine_flush_draw_queue (engine);
366
367        gtk_adjustment_set_value (GTK_LAYOUT (html)->hadjustment, (gfloat) engine->x_offset);
368        gtk_adjustment_set_value (GTK_LAYOUT (html)->vadjustment, (gfloat) engine->y_offset);
369
370        gtk_html_private_calc_scrollbars (html, NULL, NULL);
371       
372        html->priv->idle_handler_id = 0;
373        return FALSE;
374}
375
376static void
377queue_draw (GtkHTML *html)
378{
379        if (html->priv->idle_handler_id == 0)
380                html->priv->idle_handler_id = gtk_idle_add (idle_handler, html);
381}
382
383
384/* HTMLEngine callbacks.  */
385
386static void
387html_engine_title_changed_cb (HTMLEngine *engine, gpointer data)
388{
389        GtkHTML *gtk_html;
390
391        gtk_html = GTK_HTML (data);
392        g_signal_emit (gtk_html, signals [TITLE_CHANGED], 0, engine->title->str);
393}
394
395static void
396html_engine_set_base_cb (HTMLEngine *engine, const gchar *base, gpointer data)
397{
398        GtkHTML *gtk_html;
399
400        gtk_html = GTK_HTML (data);
401        gtk_html_set_base (gtk_html, base);
402        g_signal_emit (gtk_html, signals[SET_BASE], 0, base);
403}
404
405static void
406html_engine_set_base_target_cb (HTMLEngine *engine, const gchar *base_target, gpointer data)
407{
408        GtkHTML *gtk_html;
409
410        gtk_html = GTK_HTML (data);
411        g_signal_emit (gtk_html, signals[SET_BASE_TARGET], 0, base_target);
412}
413
414static void
415html_engine_load_done_cb (HTMLEngine *engine, gpointer data)
416{
417        GtkHTML *gtk_html;
418
419        gtk_html = GTK_HTML (data);
420        g_signal_emit (gtk_html, signals[LOAD_DONE], 0);
421}
422
423static void
424html_engine_url_requested_cb (HTMLEngine *engine,
425                              const gchar *url,
426                              GtkHTMLStream *handle,
427                              gpointer data)
428{
429        GtkHTML *gtk_html;
430        char *expanded = NULL;
431        gtk_html = GTK_HTML (data);
432
433        if (engine->stopped)
434                return;
435
436        expanded = gtk_html_get_url_base_relative (gtk_html, url);
437        g_signal_emit (gtk_html, signals[URL_REQUESTED], 0, expanded, handle);
438        g_free (expanded);
439}
440
441static void
442html_engine_draw_pending_cb (HTMLEngine *engine,
443                             gpointer data)
444{
445        GtkHTML *html;
446
447        html = GTK_HTML (data);
448        queue_draw (html);
449}
450
451static void
452html_engine_redirect_cb (HTMLEngine *engine,
453                         const gchar *url,
454                         int delay,
455                         gpointer data)
456{
457        GtkHTML *gtk_html;
458
459        gtk_html = GTK_HTML (data);
460
461        g_signal_emit (gtk_html, signals[REDIRECT], 0, url, delay);
462}
463
464static void
465html_engine_submit_cb (HTMLEngine *engine,
466                       const gchar *method,
467                       const gchar *url,
468                       const gchar *encoding,
469                       gpointer data)
470{
471        GtkHTML *gtk_html;
472
473        gtk_html = GTK_HTML (data);
474
475        g_signal_emit (gtk_html, signals[SUBMIT], 0, method, url, encoding);
476}
477
478static gboolean
479html_engine_object_requested_cb (HTMLEngine *engine,
480                       GtkHTMLEmbedded *eb,
481                       gpointer data)
482{
483        GtkHTML *gtk_html;
484        gboolean object_found = FALSE;
485
486        gtk_html = GTK_HTML (data);
487
488        object_found = FALSE;
489        g_signal_emit (gtk_html, signals[OBJECT_REQUESTED], 0, eb, &object_found);
490        return object_found;
491}
492
493
494/* GtkAdjustment handling.  */
495
496static void
497scroll_update_mouse (GtkWidget *widget)
498{
499        gint x, y;
500
501        if (GTK_WIDGET_REALIZED (widget)) {
502                gdk_window_get_pointer (GTK_LAYOUT (widget)->bin_window, &x, &y, NULL);
503                mouse_change_pos (widget, widget->window, x, y, 0);
504        }
505}
506
507static void
508vertical_scroll_cb (GtkAdjustment *adjustment, gpointer data)
509{
510        GtkHTML *html = GTK_HTML (data);
511
512        /* check if adjustment is valid, it's changed in
513           Layout::size_allocate and we can't do anything about it,
514           because it uses private fields we cannot access, so we have
515           to use it*/
516        if (html->engine->height != adjustment->page_increment)
517                return;
518               
519        html->engine->y_offset = (gint) adjustment->value;
520        scroll_update_mouse (GTK_WIDGET (data));
521}
522
523static void
524horizontal_scroll_cb (GtkAdjustment *adjustment, gpointer data)
525{
526        GtkHTML *html = GTK_HTML (data);
527
528        /* check if adjustment is valid, it's changed in
529           Layout::size_allocate and we can't do anything about it,
530           because it uses private fields we cannot access, so we have
531           to use it*/
532        if (html->engine->width != adjustment->page_increment)
533                return;
534               
535        html->engine->x_offset = (gint) adjustment->value;
536        scroll_update_mouse (GTK_WIDGET (data));
537}
538
539static void
540connect_adjustments (GtkHTML *html,
541                     GtkAdjustment *hadj,
542                     GtkAdjustment *vadj)
543{
544        GtkLayout *layout;
545
546        layout = GTK_LAYOUT (html);
547
548        if (html->hadj_connection != 0)
549                g_signal_handler_disconnect (layout->hadjustment, html->hadj_connection);
550
551        if (html->vadj_connection != 0)
552                g_signal_handler_disconnect (layout->vadjustment, html->vadj_connection);
553
554        if (vadj != NULL)
555                html->vadj_connection =
556                        g_signal_connect (vadj, "value_changed", G_CALLBACK (vertical_scroll_cb), (gpointer) html);
557        else
558                html->vadj_connection = 0;
559       
560        if (hadj != NULL)
561                html->hadj_connection =
562                        g_signal_connect (hadj, "value_changed", G_CALLBACK (horizontal_scroll_cb), (gpointer) html);
563        else
564                html->hadj_connection = 0;
565}
566
567
568/* Scroll timeout handling.  */
569
570static void
571inc_adjustment (GtkAdjustment *adj, gint doc_width, gint alloc_width, gint inc)
572{
573        gfloat value;
574        gint max;
575
576        value = adj->value + (gfloat) inc;
577       
578        if (doc_width > alloc_width)
579                max = doc_width - alloc_width;
580        else
581                max = 0;
582
583        if (value > (gfloat) max)
584                value = (gfloat) max;
585        else if (value < 0)
586                value = 0.0;
587
588        gtk_adjustment_set_value (adj, value);
589}
590
591static gint
592scroll_timeout_cb (gpointer data)
593{
594        GtkWidget *widget;
595        GtkHTML *html;
596        HTMLEngine *engine;
597
598        GtkLayout *layout;
599        gint x_scroll, y_scroll;
600        gint x, y;
601
602        GDK_THREADS_ENTER ();
603
604        widget = GTK_WIDGET (data);
605        html = GTK_HTML (data);
606        engine = html->engine;
607
608        gdk_window_get_pointer (widget->window, &x, &y, NULL);
609
610        if (x < 0) {
611                x_scroll = x;
612                if (x + engine->x_offset >= 0)
613                        x = 0;
614        } else if (x >= widget->allocation.width) {
615                x_scroll = x - widget->allocation.width + 1;
616                x = widget->allocation.width;
617        } else {
618                x_scroll = 0;
619        }
620        x_scroll /= 2;
621
622        if (y < 0) {
623                y_scroll = y;
624                if (y + engine->y_offset >= 0)
625                        y = 0;
626        } else if (y >= widget->allocation.height) {
627                y_scroll = y - widget->allocation.height + 1;
628                y = widget->allocation.height;
629        } else {
630                y_scroll = 0;
631        }
632        y_scroll /= 2;
633
634        if (html->in_selection && (x_scroll != 0 || y_scroll != 0))
635                html_engine_select_region (engine, html->selection_x1, html->selection_y1,
636                                           x + engine->x_offset, y + engine->y_offset);
637
638        layout = GTK_LAYOUT (widget);
639
640        inc_adjustment (layout->hadjustment, html_engine_get_doc_width (html->engine),
641                        widget->allocation.width, x_scroll);
642        inc_adjustment (layout->vadjustment, html_engine_get_doc_height (html->engine),
643                        widget->allocation.height, y_scroll);
644
645        GDK_THREADS_LEAVE ();
646
647        return TRUE;
648}
649
650static void
651setup_scroll_timeout (GtkHTML *html)
652{
653        if (html->priv->scroll_timeout_id != 0)
654                return;
655
656        html->priv->scroll_timeout_id = gtk_timeout_add (SCROLL_TIMEOUT_INTERVAL,
657                                                   scroll_timeout_cb, html);
658
659        GDK_THREADS_LEAVE();
660        scroll_timeout_cb (html);
661        GDK_THREADS_ENTER();
662}
663
664static void
665remove_scroll_timeout (GtkHTML *html)
666{
667        if (html->priv->scroll_timeout_id == 0)
668                return;
669
670        gtk_timeout_remove (html->priv->scroll_timeout_id);
671        html->priv->scroll_timeout_id = 0;
672}
673
674
675/* GtkObject methods.  */
676
677static void
678destroy (GtkObject *object)
679{
680        GtkHTML *html;
681
682        html = GTK_HTML (object);
683
684        g_free (html->pointer_url);
685        html->pointer_url = NULL;
686
687        if (html->hand_cursor) {
688                gdk_cursor_unref (html->hand_cursor);
689                html->hand_cursor = NULL;
690        }
691
692        if (html->ibeam_cursor) {
693                gdk_cursor_unref (html->ibeam_cursor);
694                html->ibeam_cursor = NULL;
695        }
696
697        connect_adjustments (html, NULL, NULL);
698
699        if (html->priv) {
700                if (html->priv->idle_handler_id != 0) {
701                        gtk_idle_remove (html->priv->idle_handler_id);
702                        html->priv->idle_handler_id = 0;
703                }
704
705                if (html->priv->scroll_timeout_id != 0) {
706                        gtk_timeout_remove (html->priv->scroll_timeout_id);
707                        html->priv->scroll_timeout_id = 0;
708                }
709
710                if (html->priv->notify_spell_id) {
711                        gconf_client_notify_remove (gconf_client, html->priv->notify_spell_id);
712                        html->priv->notify_spell_id = 0;
713                }
714
715                if (html->priv->notify_monospace_font_id) {
716                        gconf_client_notify_remove (gconf_client, html->priv->notify_monospace_font_id);
717                        html->priv->notify_monospace_font_id = 0;
718                }
719
720                if (html->priv->resize_cursor) {
721                        gdk_cursor_unref (html->priv->resize_cursor);
722                        html->priv->resize_cursor = NULL;
723                }
724
725                g_free (html->priv->content_type);
726                g_free (html->priv->base_url);
727                g_free (html->priv);
728                html->priv = NULL;
729        }
730
731        if (html->engine) {
732                g_object_unref (G_OBJECT (html->engine));
733                html->engine = NULL;
734        }
735
736        if (GTK_OBJECT_CLASS (parent_class)->destroy != NULL)
737                (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
738}
739
740GtkHTML *
741gtk_html_get_top_html (GtkHTML *html)
742{
743        while (html->iframe_parent)
744                html = GTK_HTML (html->iframe_parent);
745
746        return html;
747}
748
749void
750gtk_html_set_fonts (GtkHTML *html, HTMLPainter *painter)
751{
752        GtkWidget *top_level;
753        GtkStyle *style;
754        PangoFontDescription *fixed_desc = NULL;
755        char *fixed_name = NULL;
756        const char *fixed_family = NULL;
757        gint  fixed_size = 0;
758        const char *font_var = NULL;
759        gint  font_var_size = 0;
760
761        top_level = GTK_WIDGET (gtk_html_get_top_html (html));
762        style = gtk_widget_get_style (top_level);
763
764        font_var = pango_font_description_get_family (style->font_desc);
765        font_var_size = pango_font_description_get_size (style->font_desc);
766               
767        gtk_widget_style_get (GTK_WIDGET (top_level), "fixed_font_name", &fixed_name, NULL);
768        if (fixed_name) {
769                fixed_desc = pango_font_description_from_string (fixed_name);
770                if (pango_font_description_get_family (fixed_desc)) {
771                        fixed_size = pango_font_description_get_size (fixed_desc);
772                        fixed_family = pango_font_description_get_family (fixed_desc);
773                } else {
774                        g_free (fixed_name);
775                        fixed_name = NULL;
776                }
777        }
778               
779        if (!fixed_name) {
780                GConfClient *gconf;
781
782                gconf = gconf_client_get_default ();
783                fixed_name = gconf_client_get_string (gconf, "/desktop/gnome/interface/monospace_font_name", NULL);
784                if (fixed_name) {
785                        fixed_desc = pango_font_description_from_string (fixed_name);
786                        if (fixed_desc) {
787                                fixed_size = pango_font_description_get_size (fixed_desc);
788                                fixed_family = pango_font_description_get_family (fixed_desc);
789                        } else {
790                                g_free (fixed_name);
791                                fixed_name = NULL;
792                        }
793                }
794                g_object_unref (gconf);
795        }
796
797        if (!fixed_name) {
798                fixed_family = "Monospace";
799                fixed_size = font_var_size;
800        }
801
802        html_font_manager_set_default (&painter->font_manager,
803                                       (char *)font_var, (char *)fixed_family,
804                                       font_var_size, FALSE,
805                                       fixed_size, FALSE);
806        if (fixed_desc)
807                pango_font_description_free (fixed_desc);
808
809        g_free (fixed_name);
810}
811
812static void
813set_caret_mode(HTMLEngine *engine, gboolean caret_mode)
814{
815        if (engine->editable)
816                return;
817
818        if (!caret_mode && engine->blinking_timer_id)
819                html_engine_stop_blinking_cursor (engine);
820
821        engine->caret_mode = caret_mode;
822
823        if (caret_mode && !engine->parsing && !engine->timerId == 0)
824                gtk_html_edit_make_cursor_visible(engine->widget);
825
826        /* Normally, blink cursor handler is setup in focus in event.
827         * However, in the case focus already in this engine, and user
828         * type F7 to enable cursor, we must setup the handler by
829         * ourselves.
830         */
831        if (caret_mode && !engine->blinking_timer_id && engine->have_focus)
832                html_engine_setup_blinking_cursor (engine);
833
834        return;
835}
836
837/* GtkWidget methods.  */
838static void
839style_set (GtkWidget *widget, GtkStyle  *previous_style)
840{
841        HTMLEngine *engine = GTK_HTML (widget)->engine;
842
843        /* we don't need to set font's in idle time so call idle callback directly to avoid
844           recalculating whole document
845        */
846        if (engine) {
847                gtk_html_set_fonts (GTK_HTML (widget), engine->painter);
848                html_engine_refresh_fonts (engine);
849        }
850
851
852        html_colorset_set_style (engine->defaultSettings->color_set, widget);
853        html_colorset_set_unchanged (engine->settings->color_set,
854                                     engine->defaultSettings->color_set);
855
856        /* link and quotation colors might changed => need to refresh pango info in text objects */
857        if (engine->clue)
858                html_object_change_set_down (engine->clue, HTML_CHANGE_RECALC_PI);
859        html_engine_schedule_update (engine);
860}
861
862static gint
863key_press_event (GtkWidget *widget, GdkEventKey *event)
864{
865        GtkHTML *html = GTK_HTML (widget);
866        GtkHTMLClass *html_class = GTK_HTML_CLASS (GTK_WIDGET_GET_CLASS (html));
867        gboolean retval, update = TRUE;
868
869        html->binding_handled = FALSE;
870        html->priv->update_styles = FALSE;
871        html->priv->event_time = event->time;
872
873        if (html_engine_get_editable (html->engine)) {
874                if (gtk_im_context_filter_keypress (html->priv->im_context, event)) {
875                        html_engine_reset_blinking_cursor (html->engine);
876                        html->priv->need_im_reset = TRUE;
877                        return TRUE;
878                }
879        }
880
881        if (html_class->use_emacs_bindings && html_class->emacs_bindings && !html->binding_handled)
882                gtk_binding_set_activate (html_class->emacs_bindings, event->keyval, event->state, GTK_OBJECT (widget));
883
884        if (!html->binding_handled)
885                GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event);
886
887        retval = html->binding_handled;
888        update = html->priv->update_styles;
889
890        if (retval && update)
891                gtk_html_update_styles (html);
892
893        html->priv->event_time = 0;
894       
895        /* FIXME: use bindings */
896        if (!html_engine_get_editable (html->engine)) {
897                switch (event->keyval) {
898                case GDK_Return:
899                case GDK_KP_Enter:
900                        if (html->engine->focus_object) {
901                                gchar *url;
902                                url = html_object_get_complete_url (html->engine->focus_object, html->engine->focus_object_offset);
903                                if (url) {
904                                        /* printf ("link clicked: %s\n", url); */
905                                        g_signal_emit (html, signals [LINK_CLICKED], 0, url);
906                                        g_free (url);
907                                }
908                        }
909                        break;
910                default:
911                        ;
912                }
913        }
914
915        if (retval && (html_engine_get_editable (html->engine) || html->engine->caret_mode))
916                html_engine_reset_blinking_cursor (html->engine);
917
918        /* printf ("retval: %d\n", retval); */
919
920        return retval;
921}
922
923static gint
924key_release_event (GtkWidget *widget, GdkEventKey *event)
925{
926        GtkHTML *html = GTK_HTML (widget);
927
928        if (!html->binding_handled && html_engine_get_editable (html->engine)) {
929                if (gtk_im_context_filter_keypress (html->priv->im_context, event)) {
930                        html->priv->need_im_reset = TRUE;
931                        return TRUE;
932                }
933        }
934 
935        return GTK_WIDGET_CLASS (parent_class)->key_release_event (widget, event);
936}
937
938void
939gtk_html_drag_dest_set (GtkHTML *html)
940{
941        if (html_engine_get_editable (html->engine))
942                gtk_drag_dest_set (GTK_WIDGET (html), GTK_DEST_DEFAULT_ALL,
943                                   dnd_link_sources, DND_LINK_SOURCES, GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
944        else
945                gtk_drag_dest_unset (GTK_WIDGET (html));
946}
947
948static void
949realize (GtkWidget *widget)
950{
951        GtkHTML *html;
952        GtkLayout *layout;
953
954        g_return_if_fail (widget != NULL);
955        g_return_if_fail (GTK_IS_HTML (widget));
956
957        html = GTK_HTML (widget);
958        layout = GTK_LAYOUT (widget);
959
960        if (GTK_WIDGET_CLASS (parent_class)->realize)
961                (* GTK_WIDGET_CLASS (parent_class)->realize) (widget);
962
963        widget->style = gtk_style_attach (widget->style, widget->window);
964        gdk_window_set_events (html->layout.bin_window,
965                               (gdk_window_get_events (html->layout.bin_window)
966                                | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK
967                                | GDK_ENTER_NOTIFY_MASK
968                                | GDK_BUTTON_PRESS_MASK
969                                | GDK_BUTTON_RELEASE_MASK
970                                | GDK_VISIBILITY_NOTIFY_MASK
971                                | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK));
972
973        html_engine_realize (html->engine, html->layout.bin_window);
974
975        gdk_window_set_cursor (widget->window, NULL);
976
977        /* This sets the backing pixmap to None, so that scrolling does not
978           erase the newly exposed area, thus making the thing smoother.  */
979        gdk_window_set_back_pixmap (html->layout.bin_window, NULL, FALSE);
980
981        /* If someone was silly enough to stick us in something that doesn't
982         * have adjustments, go ahead and create them now since we expect them
983         * and love them and pat them
984         */
985        if (layout->hadjustment == NULL) {
986                layout->hadjustment = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
987
988                g_object_ref (layout->hadjustment);
989                gtk_object_sink (GTK_OBJECT (layout->hadjustment));
990        }
991
992        if (layout->vadjustment == NULL) {
993                layout->vadjustment = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
994               
995                g_object_ref (layout->vadjustment);
996                gtk_object_sink (GTK_OBJECT (layout->vadjustment));     
997        }
998
999        gtk_html_drag_dest_set (html);
1000
1001        gtk_im_context_set_client_window (html->priv->im_context, widget->window);
1002
1003        html_image_factory_start_animations (html->engine->image_factory);
1004}
1005
1006static void
1007unrealize (GtkWidget *widget)
1008{
1009        GtkHTML *html = GTK_HTML (widget);
1010
1011        html_engine_unrealize (html->engine);
1012
1013        gtk_im_context_set_client_window (html->priv->im_context, widget->window);
1014
1015        html_image_factory_stop_animations (html->engine->image_factory);
1016
1017        if (GTK_WIDGET_CLASS (parent_class)->unrealize)
1018                (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
1019}
1020
1021static gboolean
1022expose (GtkWidget *widget, GdkEventExpose *event)
1023{
1024        /* printf ("expose x: %d y: %d\n", GTK_HTML (widget)->engine->x_offset, GTK_HTML (widget)->engine->y_offset); */
1025
1026        html_engine_expose (GTK_HTML (widget)->engine, event);
1027
1028        if (GTK_WIDGET_CLASS (parent_class)->expose_event)
1029                (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event);
1030        /* printf ("expose END\n"); */
1031
1032        return FALSE;
1033}
1034
1035static void
1036gtk_html_size_request (GtkWidget *widget, GtkRequisition *requisition)
1037{
1038        HTMLEngine *e = GTK_HTML (widget)->engine;
1039        if (!e->writing) {
1040                int old_width, old_height;
1041
1042                old_width = e->width;
1043                old_height = e->height;
1044                e->width = requisition->width;
1045                e->height = requisition->height;
1046                html_engine_calc_size (e, NULL);
1047                requisition->width = html_engine_get_doc_width (e);
1048                requisition->height = html_engine_get_doc_height (e);
1049                e->width = old_width;
1050                e->height = old_height;
1051                html_engine_calc_size (e, NULL);
1052        } else {
1053                requisition->width = html_engine_get_doc_width (e);
1054                requisition->height = html_engine_get_doc_height (e);
1055        }
1056}
1057
1058static void
1059child_size_allocate (HTMLObject *o, HTMLEngine *e, gpointer data)
1060{
1061        if (html_object_is_embedded (o)) {
1062                HTMLEmbedded *eo = HTML_EMBEDDED (o);
1063
1064                if (eo->widget) {
1065                        GtkAllocation allocation;
1066
1067                        html_object_calc_abs_position (o, &allocation.x, &allocation.y);
1068                        allocation.y -= o->ascent;
1069                        allocation.width = o->width;
1070                        allocation.height = o->ascent + o->descent;
1071                        gtk_widget_size_allocate (eo->widget, &allocation);
1072                }
1073        }
1074}
1075
1076static void
1077size_allocate (GtkWidget *widget, GtkAllocation *allocation)
1078{
1079        GtkHTML *html;
1080        gboolean changed_x = FALSE, changed_y = FALSE;
1081        GList *children;
1082
1083        g_return_if_fail (widget != NULL);
1084        g_return_if_fail (GTK_IS_HTML (widget));
1085        g_return_if_fail (allocation != NULL);
1086       
1087        /* isolate childs from layout - we want to set them after calc size is performed
1088           and we know the children positions */
1089        children = GTK_LAYOUT (widget)->children;
1090        GTK_LAYOUT (widget)->children = NULL;
1091        if (GTK_WIDGET_CLASS (parent_class)->size_allocate)
1092                (*GTK_WIDGET_CLASS (parent_class)->size_allocate) (widget, allocation);
1093        GTK_LAYOUT (widget)->children = children;
1094
1095        html = GTK_HTML (widget);
1096
1097        if (html->engine->width != allocation->width
1098            || html->engine->height != allocation->height) {
1099                HTMLEngine *e = html->engine;
1100                gint old_doc_width, old_doc_height, old_width, old_height;
1101
1102                old_doc_width = html_engine_get_doc_width (html->engine);
1103                old_doc_height = html_engine_get_doc_height (html->engine);
1104                old_width = e->width;
1105                old_height = e->height;
1106
1107                e->width  = allocation->width;
1108                e->height = allocation->height;
1109
1110                html_engine_calc_size (html->engine, NULL);
1111                gtk_html_update_scrollbars_on_resize (html, old_doc_width, old_doc_height, old_width, old_height,
1112                                                      &changed_x, &changed_y);
1113        }
1114
1115        if (!html->engine->keep_scroll) {
1116                gtk_html_private_calc_scrollbars (html, &changed_x, &changed_y);
1117
1118                if (changed_x)
1119                        gtk_adjustment_value_changed (GTK_LAYOUT (html)->hadjustment);
1120                if (changed_y)
1121                        gtk_adjustment_value_changed (GTK_LAYOUT (html)->vadjustment);
1122        }
1123
1124        if (html->engine->clue)
1125                html_object_forall (html->engine->clue, html->engine, child_size_allocate, NULL);
1126}
1127
1128static void
1129set_pointer_url (GtkHTML *html, const char *url)
1130{
1131        if (url == html->pointer_url)
1132                return;
1133
1134        if (url && html->pointer_url && !strcmp (url, html->pointer_url))
1135                return;
1136               
1137        g_free (html->pointer_url);
1138        html->pointer_url = url ? g_strdup (url) : NULL;
1139        g_signal_emit (html,  signals[ON_URL], 0, html->pointer_url);
1140}
1141
1142static void
1143dnd_link_set (GtkWidget *widget, HTMLObject *o, gint offset)
1144{
1145        if (!html_engine_get_editable (GTK_HTML (widget)->engine)) {
1146                /* printf ("dnd_link_set %p\n", o); */
1147
1148                gtk_drag_source_set (widget, GDK_BUTTON1_MASK,
1149                                     dnd_link_sources, DND_LINK_SOURCES,
1150                                     GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
1151                GTK_HTML (widget)->priv->dnd_object = o;
1152                GTK_HTML (widget)->priv->dnd_object_offset = offset;
1153        }
1154}
1155
1156static void
1157dnd_link_unset (GtkWidget *widget)
1158{
1159        if (!html_engine_get_editable (GTK_HTML (widget)->engine)) {
1160                /* printf ("dnd_link_unset\n"); */
1161
1162                gtk_drag_source_unset (widget);
1163                GTK_HTML (widget)->priv->dnd_object = NULL;
1164        }
1165}
1166
1167static void
1168on_object (GtkWidget *widget, GdkWindow *window, HTMLObject *obj, gint offset, gint x, gint y)
1169{
1170        GtkHTML *html = GTK_HTML (widget);
1171
1172        if (obj) {
1173                gchar *url;
1174
1175                if (gtk_html_get_editable (html)) {
1176                        if (HTML_IS_IMAGE (obj)) {
1177                                gint ox, oy;
1178
1179                                html_object_calc_abs_position (obj, &ox, &oy);
1180                                if (ox + obj->width - 5 <= x && oy + obj->descent - 5 <= y) {
1181                                        gdk_window_set_cursor (window, html->priv->resize_cursor);
1182
1183                                        return;
1184                                }
1185                        }
1186                }
1187
1188                url = gtk_html_get_url_object_relative (html, obj,
1189                                                        html_object_get_url (obj, offset));
1190                if (url != NULL) {
1191                        set_pointer_url (html, url);
1192                        dnd_link_set (widget, obj, offset);
1193                       
1194                        if (html->engine->editable)
1195                                gdk_window_set_cursor (window, html->ibeam_cursor);
1196                        else {
1197                                gdk_window_set_cursor (window, html->hand_cursor);
1198                        }
1199                } else {
1200                        set_pointer_url (html, NULL);
1201                        dnd_link_unset (widget);                       
1202
1203                        if (html_object_is_text (obj) && html->allow_selection)
1204                                gdk_window_set_cursor (window, html->ibeam_cursor);
1205                        else
1206                                gdk_window_set_cursor (window, NULL);
1207                }
1208
1209                g_free (url);
1210        } else {
1211                set_pointer_url (html, NULL);
1212                dnd_link_unset (widget);                       
1213
1214                gdk_window_set_cursor (window, NULL);
1215        }
1216}
1217
1218#define HTML_DIST(x,y) sqrt(x*x + y*y)
1219
1220static gint
1221mouse_change_pos (GtkWidget *widget, GdkWindow *window, gint x, gint y, gint state)
1222{
1223        GtkHTML *html;
1224        HTMLEngine *engine;
1225        HTMLObject *obj;
1226        HTMLType type;
1227        gint offset;
1228
1229        if (!GTK_WIDGET_REALIZED (widget))
1230                return FALSE;
1231
1232        html   = GTK_HTML (widget);
1233        engine = html->engine;
1234        obj    = html_engine_get_object_at (engine, x, y, &offset, FALSE);
1235
1236        if ((html->in_selection || html->in_selection_drag) && html->allow_selection) {
1237                gboolean need_scroll;
1238
1239                if (obj) {
1240                        type = HTML_OBJECT_TYPE (obj);
1241
1242                        /* FIXME this is broken */
1243
1244                        if (type == HTML_TYPE_BUTTON ||
1245                            type ==  HTML_TYPE_CHECKBOX ||
1246                            type ==  HTML_TYPE_EMBEDDED ||
1247                            type ==  HTML_TYPE_HIDDEN ||
1248                            type ==  HTML_TYPE_IMAGEINPUT ||
1249                            type ==  HTML_TYPE_RADIO ||
1250                            type ==  HTML_TYPE_SELECT ||
1251                            type ==  HTML_TYPE_TEXTAREA ||
1252                            type ==  HTML_TYPE_TEXTINPUT ) {
1253                                return FALSE;
1254                        }
1255                }
1256
1257                if (HTML_DIST ((x - html->selection_x1), (y  - html->selection_y1))
1258                    > html_painter_get_space_width (engine->painter, GTK_HTML_FONT_STYLE_SIZE_3, NULL)) {
1259                        html->in_selection = TRUE;
1260                        html->in_selection_drag = TRUE;
1261                }
1262
1263                need_scroll = FALSE;
1264
1265                if (x < html->engine->x_offset) {
1266                        need_scroll = TRUE;
1267                } else if (x >= widget->allocation.width) {
1268                        need_scroll = TRUE;
1269                }
1270
1271                if (y < html->engine->y_offset) {
1272                        need_scroll = TRUE;
1273                } else if (y >= widget->allocation.height) {
1274                        need_scroll = TRUE;
1275                }
1276
1277                if (need_scroll)
1278                        setup_scroll_timeout (html);
1279                else
1280                        remove_scroll_timeout (html);
1281
1282                /* This will put the mark at the position of the
1283                   previous click.  */
1284                if (engine->mark == NULL && engine->editable)
1285                        html_engine_set_mark (engine);
1286
1287                html_engine_select_region (engine, html->selection_x1, html->selection_y1, x, y);
1288        }
1289
1290        if (html->priv->in_object_resize) {
1291                HTMLObject *o = html->priv->resize_object;
1292                gint ox, oy;
1293
1294                html_object_calc_abs_position (o, &ox, &oy);
1295                oy -= o->ascent;
1296                g_assert (HTML_IS_IMAGE (o));
1297                if (x > ox && y > oy) {
1298                        gint w, h;
1299
1300                        w = x - ox;
1301                        h = y - oy;
1302                        if (!(state & GDK_SHIFT_MASK)) {
1303                                w = MAX (w, h);
1304                                h = -1;
1305                        }
1306                        html_image_set_size (HTML_IMAGE (o), w, h, FALSE, FALSE);
1307                }
1308        } else
1309                on_object (widget, window, obj, offset, x, y);
1310
1311        return TRUE;
1312}
1313
1314static const char *
1315skip_host (const char *url)
1316{
1317        const char *host;
1318       
1319        host = url;
1320        while (*host && (*host != '/') && (*host != ':'))
1321               host++;
1322
1323        if (*host == ':') {
1324                url = host++;
1325
1326                if (*host == '/')
1327                        host++;
1328
1329                url = host;
1330
1331                if (*host == '/') {
1332                        host++;
1333
1334                        while (*host && (*host != '/'))
1335                                host++;
1336                       
1337                        url = host;
1338                }
1339        }               
1340       
1341        return url;
1342}
1343       
1344static size_t
1345path_len (const char *base, gboolean absolute)
1346{
1347        const char *last;
1348        const char *cur;
1349        const char *start;
1350
1351        start = last = skip_host (base);
1352        if (!absolute) {
1353                cur = strrchr (start, '/');
1354               
1355                if (cur)
1356                        last = cur;
1357        }
1358
1359        return last - base;
1360}
1361
1362#if 0
1363char *
1364collapse_path (char *url)
1365{
1366        char *start;
1367        char *end;
1368        char *cur;
1369        size_t len;
1370
1371        start = skip_host (url);
1372
1373        cur = start;
1374        while ((cur = strstr (cur, "/../"))) {
1375                end = cur + 3;
1376               
1377                /* handle the case of a rootlevel /../ specialy */
1378                if (cur == start) {
1379                        len = strlen (end);
1380                        memmove (cur, end, len + 1);
1381                }
1382                       
1383                while (cur > start) {
1384                        cur--;
1385                        if ((*cur == '/') || (cur == start)) {
1386                                len = strlen (end);
1387                                memmove (cur, end, len + 1);
1388                                break;
1389                        }
1390                }
1391        }
1392        return url;
1393}
1394#endif
1395
1396static gboolean
1397url_is_absolute (const char *url)
1398{
1399        /*
1400          URI Syntactic Components
1401
1402          The URI syntax is dependent upon the scheme.  In general, absolute
1403          URI are written as follows:
1404
1405          <scheme>:<scheme-specific-part>
1406
1407          scheme        = alpha *( alpha | digit | "+" | "-" | "." )
1408        */
1409
1410        if (!url)
1411                return FALSE;
1412
1413        if (!isalpha (*url))
1414                return FALSE;
1415        url ++;
1416
1417        while (*url && (isalnum (*url) || *url == '+' || *url == '-' || *url == '.'))
1418                url ++;
1419
1420        return *url && *url == ':';
1421}
1422
1423static char *
1424expand_relative (const char *base, const char *url)
1425{
1426        char *new_url = NULL;
1427        size_t base_len, url_len;
1428        gboolean absolute = FALSE;
1429
1430        if (!base || url_is_absolute (url)) {
1431                /*
1432                  g_warning ("base = %s url = %s new_url = %s",
1433                  base, url, new_url);
1434                */
1435                return g_strdup (url);
1436        }               
1437
1438        if (*url == '/') {
1439                absolute = TRUE;;
1440        }
1441       
1442        base_len = path_len (base, absolute);
1443        url_len = strlen (url);
1444
1445        new_url = g_malloc (base_len + url_len + 2);
1446       
1447        if (base_len) {
1448                memcpy (new_url, base, base_len);
1449
1450                if (base[base_len - 1] != '/')
1451                        new_url[base_len++] = '/';
1452                if (absolute)
1453                        url++;
1454        }
1455       
1456        memcpy (new_url + base_len, url, url_len);
1457        new_url[base_len + url_len] = '\0';
1458       
1459        /*
1460           g_warning ("base = %s url = %s new_url = %s",
1461           base, url, new_url);
1462        */
1463        return new_url;
1464}
1465
1466char *
1467gtk_html_get_url_base_relative (GtkHTML *html, const char *url)
1468{
1469        return expand_relative (gtk_html_get_base (html), url);
1470}
1471
1472static char *
1473expand_frame_url (GtkHTML *html, const char *url)
1474{
1475        char *new_url;
1476
1477        new_url = gtk_html_get_url_base_relative (html, url);
1478        while (html->iframe_parent) {
1479                char *expanded;
1480
1481                expanded = gtk_html_get_url_base_relative (GTK_HTML (html->iframe_parent),
1482                                                       new_url);
1483                g_free (new_url);
1484                new_url = expanded;
1485
1486                html = GTK_HTML (html->iframe_parent);
1487        }
1488        return new_url;
1489}
1490
1491char *
1492gtk_html_get_url_object_relative (GtkHTML *html, HTMLObject *o, const char *url)
1493{
1494        HTMLEngine *e;
1495        HTMLObject *parent;
1496
1497        g_return_val_if_fail (GTK_IS_HTML (html), NULL);
1498
1499        /* start at the top always */
1500        while (html->iframe_parent)
1501                html = GTK_HTML (html->iframe_parent);
1502       
1503        parent = o;
1504        while (parent->parent) {
1505                parent = parent->parent;
1506                if ((HTML_OBJECT_TYPE (parent) == HTML_TYPE_FRAME)
1507                    || (HTML_OBJECT_TYPE (parent) == HTML_TYPE_IFRAME))
1508                        break;
1509        }
1510
1511        e = html_object_get_engine (parent, html->engine);
1512       
1513        if (!e) {
1514                g_warning ("Cannot find object for url");
1515                return NULL;
1516        }
1517
1518        /*
1519        if (e == html->engine)
1520                g_warning ("engine matches engine");
1521        */
1522        return url ? expand_frame_url (e->widget, url) : NULL;
1523}
1524       
1525static GtkWidget *
1526shift_to_iframe_parent (GtkWidget *widget, gint *x, gint *y)
1527{
1528        while (GTK_HTML (widget)->iframe_parent) {
1529                if (x)
1530                        *x += widget->allocation.x - GTK_HTML (widget)->engine->x_offset;
1531                if (y)
1532                        *y += widget->allocation.y - GTK_HTML (widget)->engine->y_offset;
1533
1534                widget = GTK_HTML (widget)->iframe_parent;
1535               
1536                       
1537        }
1538
1539        return widget;
1540}
1541
1542static gint
1543motion_notify_event (GtkWidget *widget,
1544                     GdkEventMotion *event)
1545{
1546        GdkWindow *window = widget->window;
1547        HTMLEngine *engine;
1548        gint x, y;
1549
1550        g_return_val_if_fail (widget != NULL, 0);
1551        g_return_val_if_fail (GTK_IS_HTML (widget), 0);
1552        g_return_val_if_fail (event != NULL, 0);
1553
1554        /* printf ("motion_notify_event\n"); */
1555
1556        if (GTK_HTML (widget)->priv->dnd_in_progress)
1557                return TRUE;
1558
1559        if (!event->is_hint) {
1560                x = event->x;
1561                y = event->y;
1562        }
1563        widget = shift_to_iframe_parent (widget, &x, &y);
1564
1565        if (event->is_hint) {
1566                gdk_window_get_pointer (GTK_LAYOUT (widget)->bin_window, &x, &y, NULL);
1567        }
1568
1569        if (!mouse_change_pos (widget, window, x, y, event->state))
1570                return FALSE;
1571
1572        engine = GTK_HTML (widget)->engine;
1573        if (GTK_HTML (widget)->in_selection_drag && html_engine_get_editable (engine))
1574                html_engine_jump_at (engine, x, y);
1575        return TRUE;
1576}
1577
1578
1579static gboolean
1580toplevel_unmap (GtkWidget *widget, GdkEvent *event, GtkHTML *html)
1581{
1582        html_image_factory_stop_animations (html->engine->image_factory);       
1583
1584        return FALSE;
1585}
1586
1587static void
1588hierarchy_changed (GtkWidget *widget,
1589                   GtkWidget *old_toplevel)
1590{
1591        GtkWidget *toplevel;
1592        GtkHTMLPrivate   *priv = GTK_HTML (widget)->priv;
1593       
1594        if (old_toplevel && priv->toplevel_unmap_handler) {
1595                g_signal_handler_disconnect (old_toplevel,
1596                                             priv->toplevel_unmap_handler);
1597                priv->toplevel_unmap_handler = 0;
1598        }
1599
1600        toplevel = gtk_widget_get_toplevel (widget);
1601
1602        if (GTK_WIDGET_TOPLEVEL (toplevel) && priv->toplevel_unmap_handler == 0) {
1603                priv->toplevel_unmap_handler = g_signal_connect (G_OBJECT (toplevel), "unmap-event",
1604                                                                 G_CALLBACK (toplevel_unmap), widget);
1605        }
1606}
1607
1608static gint
1609visibility_notify_event (GtkWidget *widget,
1610                         GdkEventVisibility *event)
1611{
1612        if (event->state == GDK_VISIBILITY_FULLY_OBSCURED)
1613                html_image_factory_stop_animations (GTK_HTML (widget)->engine->image_factory);
1614        else
1615                html_image_factory_start_animations (GTK_HTML (widget)->engine->image_factory);
1616
1617        return FALSE;
1618}
1619
1620static gint
1621button_press_event (GtkWidget *widget,
1622                    GdkEventButton *event)
1623{
1624        GtkHTML *html;
1625        GtkWidget *orig_widget = widget;
1626        HTMLEngine *engine;
1627        gint value, x, y;
1628
1629        /* printf ("button_press_event\n"); */
1630
1631        x = event->x;
1632        y = event->y;
1633
1634        widget = shift_to_iframe_parent (widget, &x, &y);
1635        html   = GTK_HTML (widget);
1636        engine = html->engine;
1637
1638        if (event->button == 1 || ((event->button == 2 || event->button == 3)
1639                                   && html_engine_get_editable (engine)))
1640                gtk_widget_grab_focus (widget);
1641
1642        if (event->type == GDK_BUTTON_PRESS) {
1643                GtkAdjustment *vadj;
1644
1645                vadj   = GTK_LAYOUT (widget)->vadjustment;
1646               
1647                switch (event->button) {
1648                case 4:
1649                        /* Mouse wheel scroll up.  */
1650                        if (event->state & GDK_CONTROL_MASK)
1651                                gtk_html_command (html, "zoom-out");
1652                        else {
1653                                value = vadj->value - vadj->step_increment * 3;
1654                       
1655                                if (value < vadj->lower)
1656                                        value = vadj->lower;
1657                       
1658                                gtk_adjustment_set_value (vadj, value);
1659                        }
1660                        return TRUE;
1661                        break;
1662                case 5:
1663                        /* Mouse wheel scroll down.  */
1664                        if (event->state & GDK_CONTROL_MASK)
1665                                gtk_html_command (html, "zoom-in");
1666                        else {
1667                                value = vadj->value + vadj->step_increment * 3;
1668                       
1669                                if (value > (vadj->upper - vadj->page_size))
1670                                        value = vadj->upper - vadj->page_size;
1671                       
1672                                gtk_adjustment_set_value (vadj, value);
1673                        }
1674                        return TRUE;
1675                        break;
1676                case 2:
1677                        if (html_engine_get_editable (engine)) {
1678                                html_engine_disable_selection (html->engine);
1679                                html_engine_jump_at (engine, x, y);
1680                                gtk_html_update_styles (html);
1681                                gtk_html_request_paste (html, GDK_SELECTION_PRIMARY,
1682                                                        event->state & GDK_CONTROL_MASK ? 1 : 0,
1683                                                        event->time, event->state & GDK_SHIFT_MASK);
1684                                return TRUE;
1685                        }
1686                        break;
1687                case 1:
1688                        html->in_selection_drag = TRUE;
1689                        if (html_engine_get_editable (engine)) {
1690                                HTMLObject *obj;
1691
1692                                obj = html_engine_get_object_at (engine, x, y, NULL, FALSE);
1693
1694                                if (obj && HTML_IS_IMAGE (obj)) {
1695                                        gint ox, oy;
1696
1697                                        html_object_calc_abs_position (obj, &ox, &oy);
1698                                        if (ox + obj->width - 5 <= x && oy + obj->descent - 5 <= y) {
1699                                                html->priv->in_object_resize = TRUE;
1700                                                html->priv->resize_object = obj;
1701                                                html->in_selection_drag = FALSE;
1702                                        }
1703                                }
1704
1705                                if (html->allow_selection && !html->priv->in_object_resize)
1706                                        if (!(event->state & GDK_SHIFT_MASK)
1707                                            || (!engine->mark && event->state & GDK_SHIFT_MASK))
1708                                                html_engine_set_mark (engine);
1709                                html_engine_jump_at (engine, x, y);
1710                        } else {
1711                                HTMLObject *obj;
1712                                HTMLEngine *orig_e;
1713                                gint offset;
1714
1715                                orig_e = GTK_HTML (orig_widget)->engine;
1716                                obj = html_engine_get_object_at (engine, x, y, &offset, FALSE);
1717                                if (obj && ((HTML_IS_IMAGE (obj) && HTML_IMAGE (obj)->url && *HTML_IMAGE (obj)->url)
1718                                            || (HTML_IS_TEXT (obj) && html_object_get_complete_url (obj, offset))))
1719                                        html_engine_set_focus_object (orig_e, obj, offset);
1720                                else {
1721                                        html_engine_set_focus_object (orig_e, NULL, 0);
1722                                        if (orig_e->caret_mode)
1723                                                html_engine_jump_at (engine, x, y);
1724                                }
1725                        }
1726                        if (html->allow_selection && !html->priv->in_object_resize) {
1727                                if (event->state & GDK_SHIFT_MASK)
1728                                        html_engine_select_region (engine,
1729                                                                   html->selection_x1, html->selection_y1, x, y);
1730                                else {
1731                                        html_engine_disable_selection (engine);
1732                                        if (gdk_pointer_grab (GTK_LAYOUT (widget)->bin_window, FALSE,
1733                                                              (GDK_BUTTON_RELEASE_MASK
1734                                                               | GDK_BUTTON_MOTION_MASK
1735                                                               | GDK_POINTER_MOTION_HINT_MASK),
1736                                                              NULL, NULL, event->time) == 0) {
1737                                                html->selection_x1 = x;
1738                                                html->selection_y1 = y;
1739                                        }
1740                                }
1741                        }
1742
1743                        engine->selection_mode = FALSE;
1744                        if (html_engine_get_editable (engine))
1745                                gtk_html_update_styles (html);
1746                        break;
1747                default:
1748                        break;
1749                }
1750        } else if (event->button == 1 && html->allow_selection) {
1751                if (event->type == GDK_2BUTTON_PRESS) {
1752                        html->in_selection_drag = FALSE;
1753                        gtk_html_select_word (html);
1754                        html->in_selection = TRUE;
1755                }
1756                else if (event->type == GDK_3BUTTON_PRESS) {
1757                        html->in_selection_drag = FALSE;
1758                        gtk_html_select_line (html);
1759                        html->in_selection = TRUE;
1760                }
1761        }
1762
1763        return FALSE;
1764}
1765
1766static gint
1767button_release_event (GtkWidget *initial_widget,
1768                      GdkEventButton *event)
1769{
1770        GtkWidget *widget;
1771        GtkHTML *html;
1772        HTMLEngine *engine;
1773        gint x, y;
1774
1775        /* printf ("button_release_event\n"); */
1776
1777        x = event->x;
1778        y = event->y;
1779        widget = shift_to_iframe_parent (initial_widget, &x, &y);
1780        html   = GTK_HTML (widget);
1781
1782        remove_scroll_timeout (html);
1783        gtk_grab_remove (widget);
1784        gdk_pointer_ungrab (event->time);
1785
1786        engine =  html->engine;
1787
1788        if (html->in_selection) {
1789                html_engine_update_selection_active_state (html->engine, html->priv->event_time);
1790                if (html->in_selection_drag)
1791                        html_engine_select_region (engine, html->selection_x1, html->selection_y1,
1792                                                   x, y);
1793                gtk_html_update_styles (html);
1794                queue_draw (html);
1795        }
1796
1797        if (event->button == 1) {
1798
1799                if (html->in_selection_drag && html_engine_get_editable (engine))
1800                        html_engine_jump_at (engine, x, y);
1801
1802                html->in_selection_drag = FALSE;
1803
1804                if (!html->priv->dnd_in_progress
1805                    && html->pointer_url != NULL && ! html->in_selection)
1806                        g_signal_emit (widget,  signals[LINK_CLICKED], 0, html->pointer_url);
1807        }
1808
1809        html->in_selection = FALSE;
1810        html->priv->in_object_resize = FALSE;
1811
1812        return TRUE;
1813}
1814
1815static gint
1816focus_in_event (GtkWidget *widget,
1817                GdkEventFocus *event)
1818{
1819        GtkHTML *html = GTK_HTML (widget);
1820
1821        /* printf ("focus in\n"); */
1822        if (!html->iframe_parent) {
1823                GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
1824                html_engine_set_focus (html->engine, TRUE);
1825        } else {
1826                GtkWidget *window = gtk_widget_get_ancestor (widget, gtk_window_get_type ());
1827                if (window)
1828                        gtk_window_set_focus (GTK_WINDOW (window), html->iframe_parent);
1829        }
1830
1831        html->priv->need_im_reset = TRUE;
1832        gtk_im_context_focus_in (html->priv->im_context);
1833
1834        return FALSE;
1835}
1836
1837static gint
1838focus_out_event (GtkWidget *widget,
1839                 GdkEventFocus *event)
1840{
1841        GtkHTML *html = GTK_HTML (widget);
1842
1843        html_painter_set_focus (html->engine->painter, FALSE);
1844        html_engine_redraw_selection (html->engine);
1845        /* printf ("focus out\n"); */
1846        if (!html->iframe_parent) {
1847                GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
1848                html_engine_set_focus (html->engine, FALSE);
1849        }
1850
1851        html->priv->need_im_reset = TRUE;
1852        gtk_im_context_focus_out (html->priv->im_context);
1853
1854        return FALSE;
1855}
1856
1857static gint
1858enter_notify_event (GtkWidget *widget, GdkEventCrossing *event)
1859{
1860        gint x, y;
1861
1862        x = event->x;
1863        y = event->y;
1864        widget = shift_to_iframe_parent (widget, &x, &y);
1865
1866        mouse_change_pos (widget, widget->window, x, y, event->state);
1867
1868        return TRUE;
1869}
1870
1871/* X11 selection support.  */
1872
1873static char *
1874ucs2_order (gboolean swap)
1875{
1876        gboolean be;
1877       
1878        /*
1879         * FIXME owen tells me this logic probably isn't needed
1880         * because smart iconvs will notice the BOM and act accordingly
1881         * I don't have as much faith in the various iconv implementations
1882         * so I am leaving it in for now
1883         */
1884
1885        be = G_BYTE_ORDER == G_BIG_ENDIAN;
1886        be = swap ? be : !be;   
1887
1888        if (be)
1889                return "UCS-2BE";
1890        else
1891                return "UCS-2LE";
1892       
1893}
1894
1895static void
1896selection_get (GtkWidget        *widget,
1897               GtkSelectionData *selection_data,
1898               guint             info,
1899               guint             time)
1900{
1901        GtkHTML *html;
1902        gchar *selection_string = NULL;
1903        HTMLObject *selection_object = NULL;
1904        guint selection_object_len = 0;
1905
1906        g_return_if_fail (widget != NULL);
1907        g_return_if_fail (GTK_IS_HTML (widget));
1908       
1909        html = GTK_HTML (widget);
1910        if (selection_data->selection == GDK_SELECTION_PRIMARY) {
1911                if (html->engine->primary) {
1912                        selection_object = html->engine->primary;
1913                        selection_object_len = html->engine->primary_len;
1914                }                       
1915        } else  /* CLIPBOARD */ {
1916                if (html->engine->clipboard) {
1917                        selection_object = html->engine->clipboard;
1918                        selection_object_len = html->engine->clipboard_len;
1919                }
1920        }
1921 
1922        if (info == TARGET_HTML) {
1923                if (selection_object) {
1924                        HTMLEngineSaveState *state;
1925                        GString *buffer;
1926                        gsize len;
1927                       
1928                        state = html_engine_save_buffer_new (html->engine, TRUE);
1929                        buffer = (GString *)state->user_data;
1930
1931                        /* prepend a byte order marker (ZWNBSP) to the selection */
1932                        g_string_append_unichar (buffer, 0xfeff);
1933                        html_object_save (selection_object, state);
1934                        g_string_append_unichar (buffer, 0x0000);
1935
1936                        d_s(g_warning ("BUFFER = %s", buffer->str);)
1937                        selection_string = g_convert (buffer->str, buffer->len, "UCS-2", "UTF-8", NULL, &len, NULL);
1938                       
1939                        if (selection_string)
1940                                gtk_selection_data_set (selection_data,
1941                                                        gdk_atom_intern ("text/html", FALSE), 8,
1942                                                        selection_string,
1943                                                        len);
1944                       
1945                        //html_engine_save_buffer_free (state);
1946                }                               
1947        } else {
1948                if (selection_object)
1949                        selection_string = html_object_get_selection_string (selection_object, html->engine);
1950               
1951                if (selection_string)
1952                        gtk_selection_data_set_text (selection_data, selection_string, strlen (selection_string));
1953               
1954        }
1955
1956        g_free (selection_string);
1957
1958}
1959
1960static gchar *
1961ucs2_to_utf8_with_bom_check (guchar  *data, guint len) {
1962        char    *fromcode = NULL;
1963        GError  *error = NULL;
1964        guint16 c;
1965        gsize read_len, written_len;
1966        gchar *utf8_ret;
1967
1968        /*
1969         * Unicode Techinical Report 20
1970         * ( http://www.unicode.org/unicode/reports/tr20/ ) says to treat an
1971         * initial 0xfeff (ZWNBSP) as a byte order indicator so that is
1972         * what we do.  If there is no indicator assume it is in the default
1973         * order
1974         */
1975
1976        memcpy (&c, data, 2);
1977        switch (c) {
1978        case 0xfeff:
1979        case 0xfffe:
1980                fromcode = ucs2_order (c == 0xfeff);
1981                data += 2;
1982                len  -= 2;
1983                break;
1984        default:
1985                fromcode = "UCS-2";
1986                break;
1987        }
1988
1989        utf8_ret = g_convert (data, len, "UTF-8", fromcode, &read_len, &written_len, &error);
1990
1991        if (error) {
1992                g_warning ("g_convert error: %s\n", error->message);
1993                g_error_free (error);
1994        }
1995        return (utf8_ret);     
1996}
1997
1998/* receive a selection */
1999/* Signal handler called when the selections owner returns the data */
2000static void
2001selection_received (GtkWidget *widget,
2002                    GtkSelectionData *selection_data,
2003                    guint time)
2004{
2005        HTMLEngine *e;
2006        gboolean as_cite;
2007       
2008        g_return_if_fail (widget != NULL);
2009        g_return_if_fail (GTK_IS_HTML (widget));
2010        g_return_if_fail (selection_data != NULL);
2011       
2012        /* printf ("got selection from system\n"); */
2013       
2014        e = GTK_HTML (widget)->engine;
2015        as_cite = GTK_HTML (widget)->priv->selection_as_cite;
2016       
2017        /* If the Widget is editable,
2018        ** and we are the owner of the atom requested
2019        ** and we are not pasting as a citation
2020        ** then we are pasting between ourself and we
2021        ** need not do all the conversion.
2022        */
2023        if (html_engine_get_editable (e)
2024            && widget->window == gdk_selection_owner_get (selection_data->selection)
2025            && !as_cite) {
2026               
2027                /* Check which atom was requested (PRIMARY or CLIPBOARD) */
2028                if (selection_data->selection == gdk_atom_intern ("CLIPBOARD", FALSE)
2029                    && e->clipboard) {
2030                       
2031                        html_engine_paste (e);
2032                        return;
2033                       
2034                } else if (selection_data->selection == GDK_SELECTION_PRIMARY
2035                           && e->primary) {
2036                        HTMLObject *copy;
2037                        guint len = 0;
2038                       
2039                        copy = html_object_op_copy (e->primary, NULL, e, NULL, NULL, &len);
2040                        html_engine_paste_object (e, copy, len);
2041                       
2042                        return;
2043                }
2044        }
2045       
2046        /* **** IMPORTANT **** Check to see if retrieval succeeded  */
2047        /* If we have more selection types we can ask for, try the next one,
2048           until there are none left */
2049        if (selection_data->length < 0) {
2050                gint type = GTK_HTML (widget)->priv->selection_type;
2051               
2052                /* now, try again with next selection type */
2053                if (!gtk_html_request_paste (GTK_HTML (widget), selection_data->selection, type + 1, time, as_cite))
2054                        g_warning ("Selection retrieval failed\n");
2055                return;
2056        }
2057       
2058        /* Make sure we got the data in the expected form */
2059        if ((selection_data->type != gdk_atom_intern ("UTF8_STRING", FALSE))
2060            && (selection_data->type != GDK_SELECTION_TYPE_STRING)
2061            && (selection_data->type != gdk_atom_intern ("COMPOUND_TEXT", FALSE))
2062            && (selection_data->type != gdk_atom_intern ("TEXT", FALSE))
2063            && (selection_data->type != gdk_atom_intern ("text/html", FALSE))) {
2064                g_warning ("Selection \"STRING\" was not returned as strings!\n");
2065        } else if (selection_data->length > 0) {
2066                gchar   *utf8 = NULL;
2067                if (selection_data->type == gdk_atom_intern ("text/html", FALSE)) {
2068                        guint    len  = (guint)selection_data->length;
2069                        guchar  *data = selection_data->data;
2070
2071                        /*
2072                         * FIXME This hack decides the charset of the selection.  It seems that
2073                         * mozilla/netscape alway use ucs2 for text/html
2074                         * and openoffice.org seems to always use utf8 so we try to validate
2075                         * the string as utf8 and if that fails we assume it is ucs2
2076                         */
2077
2078                        if (len > 1 &&
2079                            !g_utf8_validate (data, len - 1, NULL)) {
2080                                utf8 = ucs2_to_utf8_with_bom_check (data, len);
2081                                d_s (g_warning ("UCS-2 selection = %s", utf8);)
2082                        } else if (len > 0) {
2083                                d_s (g_warning ("UTF-8 selection (%d) = %s", len, data);)
2084
2085                                utf8 = g_malloc0 (len + 1);
2086                                memcpy (utf8, data, len);
2087                        } else {
2088                                g_warning ("unable to determine selection charset");
2089                                return;
2090                        }
2091
2092                        if (as_cite) {
2093                                char *cite;
2094
2095                                cite = g_strdup_printf ("<br><blockquote type=\"cite\">%s</blockquote>", utf8);
2096
2097                                g_free (utf8);
2098                                utf8 = cite;
2099                        }
2100
2101                        if (utf8)
2102                                gtk_html_insert_html (GTK_HTML (widget), utf8);
2103                        else
2104                                g_warning ("selection was empty");
2105
2106                        g_free (utf8);                 
2107                } else if ((utf8 = gtk_selection_data_get_text (selection_data))) {
2108                        if (as_cite) {
2109                                char *encoded;
2110                               
2111                                /* FIXME there has to be a cleaner way to do this */
2112                                encoded = html_encode_entities (utf8, g_utf8_strlen (utf8, -1), NULL);
2113                                g_free (utf8);
2114                                utf8 = g_strdup_printf ("<br><pre><blockquote type=\"cite\">%s</blockquote></pre>",
2115                                                        encoded);
2116                                g_free (encoded);
2117                                gtk_html_insert_html (GTK_HTML (widget), utf8);
2118                        } else {
2119                                html_engine_paste_text (e, utf8, g_utf8_strlen (utf8, -1));
2120                        }
2121                        if (HTML_IS_TEXT (e->cursor->object))
2122                                html_text_magic_link (HTML_TEXT (e->cursor->object), e,
2123                                                      html_object_get_length (e->cursor->object));
2124                       
2125                        g_free (utf8);
2126                }
2127                return;
2128        }                         
2129       
2130        if (html_engine_get_editable (e))
2131                html_engine_paste (e);
2132}
2133
2134gint
2135gtk_html_request_paste (GtkHTML *html, GdkAtom selection, gint type, gint32 time, gboolean as_cite)
2136{
2137        GdkAtom format_atom;
2138        static char *formats[] = {"text/html", "UTF8_STRING", "COMPOUND_TEXT", "TEXT", "STRING"};
2139
2140        if (type >= sizeof (formats) / sizeof (formats[0])) {
2141                /* we have now tried all the slection types we support */
2142                html->priv->selection_type = -1;
2143                if (html_engine_get_editable (html->engine))
2144                        html_engine_paste (html->engine);
2145                return FALSE;
2146        }
2147       
2148        html->priv->selection_type = type;
2149        html->priv->selection_as_cite = as_cite;
2150
2151        format_atom = gdk_atom_intern (formats [type], FALSE);
2152       
2153        if (format_atom == GDK_NONE) {
2154                g_warning("Could not get requested atom\n");
2155        }
2156        /* And request the format target for the required selection */
2157        gtk_selection_convert (GTK_WIDGET (html), selection, format_atom,
2158                               time);
2159        return TRUE;
2160}
2161
2162
2163static gint
2164selection_clear_event (GtkWidget *widget,
2165                       GdkEventSelection *event)
2166{
2167        GtkHTML *html;
2168
2169        if (! gtk_selection_clear (widget, event))
2170                return FALSE;
2171
2172        html = GTK_HTML (widget);
2173
2174        if (!html_engine_get_editable (html->engine)) {
2175                html_engine_disable_selection (html->engine);
2176                html->in_selection = FALSE;
2177        }
2178
2179        return TRUE;
2180}
2181
2182
2183static void
2184set_adjustments (GtkLayout     *layout,
2185                 GtkAdjustment *hadj,
2186                 GtkAdjustment *vadj)
2187{
2188        GtkHTML *html = GTK_HTML(layout);
2189
2190        connect_adjustments (html, hadj, vadj);
2191       
2192        if (parent_class->set_scroll_adjustments)
2193                (* parent_class->set_scroll_adjustments) (layout, hadj, vadj);
2194}
2195
2196
2197/* Initialization.  */
2198static void
2199client_notify_spell_widget (GConfClient* client, guint cnxn_id, GConfEntry* entry, gpointer user_data)
2200{
2201        GtkHTML *html = (GtkHTML *) user_data;
2202        GtkHTMLClass *klass = GTK_HTML_CLASS (GTK_WIDGET_GET_CLASS (html));
2203        GtkHTMLClassProperties *prop = klass->properties;       
2204        gchar *tkey;
2205
2206        g_assert (client == gconf_client);
2207        g_assert (entry->key);
2208        tkey = strrchr (entry->key, '/');
2209        g_assert (tkey);
2210
2211        if (!strcmp (tkey, "/language")) {
2212                g_free (prop->language);
2213                prop->language = g_strdup (gconf_client_get_string (client, entry->key, NULL));
2214                if (!html->engine->language)
2215                        gtk_html_api_set_language (html);
2216        }
2217}
2218
2219static GtkHTMLClassProperties *
2220get_class_properties (GtkHTML *html)
2221{
2222        GtkHTMLClass *klass;
2223 
2224        klass = GTK_HTML_CLASS (GTK_WIDGET_GET_CLASS (html));
2225        if (!klass->properties) {
2226                klass->properties = gtk_html_class_properties_new (GTK_WIDGET (html));
2227               
2228                if (!gconf_is_initialized ()) {
2229                        char *argv[] = { "gtkhtml", NULL };
2230                       
2231                        g_warning ("gconf is not initialized, please call gconf_init before using GtkHTML library. "
2232                                   "Meanwhile it's initialized by gtkhtml itself.");
2233                        gconf_init (1, argv, &gconf_error);
2234                        if (gconf_error)
2235                                g_error ("gconf error: %s\n", gconf_error->message);
2236                }
2237               
2238                gconf_client = gconf_client_get_default ();
2239                if (!gconf_client)
2240                        g_error ("cannot create gconf_client\n");
2241                gconf_client_add_dir (gconf_client, GTK_HTML_GCONF_DIR, GCONF_CLIENT_PRELOAD_ONELEVEL, &gconf_error);
2242                if (gconf_error)
2243                        g_error ("gconf error: %s\n", gconf_error->message);
2244                gconf_client_add_dir (gconf_client, GNOME_SPELL_GCONF_DIR, GCONF_CLIENT_PRELOAD_ONELEVEL, &gconf_error);
2245                if (gconf_error)
2246                        g_error ("gconf error: %s\n", gconf_error->message);
2247                gtk_html_class_properties_load (klass->properties, gconf_client);
2248
2249                if (gconf_error)
2250                        g_warning ("gconf error: %s\n", gconf_error->message);
2251        }
2252       
2253        return klass->properties;
2254}
2255
2256static void
2257set_focus_child (GtkContainer *containter, GtkWidget *w)
2258{
2259        HTMLObject *o = NULL;
2260
2261        while (w && !(o = g_object_get_data (G_OBJECT (w), "embeddedelement")))
2262                w = w->parent;
2263
2264        if (o && !html_object_is_frame (o))
2265                html_engine_set_focus_object (GTK_HTML (containter)->engine, o, 0);
2266
2267        (*GTK_CONTAINER_CLASS (parent_class)->set_focus_child) (containter, w);
2268}
2269
2270static gboolean
2271focus (GtkWidget *w, GtkDirectionType direction)
2272{
2273        HTMLEngine *e = GTK_HTML (w)->engine;
2274
2275        if (html_engine_get_editable (e)) {
2276                gboolean rv;
2277
2278                rv = (*GTK_WIDGET_CLASS (parent_class)->focus) (w, direction);
2279                html_engine_set_focus (GTK_HTML (w)->engine, rv);
2280                return rv;
2281        }
2282
2283        /* Reset selection. */
2284        if (e->shift_selection || e->mark) {
2285                html_engine_disable_selection (e);
2286                html_engine_edit_selection_updater_schedule (e->selection_updater);
2287                e->shift_selection = FALSE;
2288        }
2289
2290        if (html_engine_focus (e, direction) && e->focus_object) {
2291                gint offset;
2292                HTMLObject *obj = html_engine_get_focus_object (e, &offset);
2293                gint x1, y1, x2, y2, xo, yo;
2294
2295                xo = e->x_offset;
2296                yo = e->y_offset;
2297
2298                if (HTML_IS_TEXT (obj)) {
2299                        if (!html_text_get_link_rectangle (HTML_TEXT (obj), e->painter, offset, &x1, &y1, &x2, &y2))
2300                                return FALSE;
2301                } else {
2302                        html_object_calc_abs_position (obj, &x1, &y1);
2303                        y2 = y1 + obj->descent;
2304                        x2 = x1 + obj->width;
2305                        y1 -= obj->ascent;
2306                }
2307
2308                /* printf ("child pos: %d,%d x %d,%d\n", x1, y1, x2, y2); */
2309
2310                if (x2 > e->x_offset + e->width)
2311                        e->x_offset = x2 - e->width;
2312                if (x1 < e->x_offset)
2313                        e->x_offset = x1;
2314                if (e->width > 2*RIGHT_BORDER && e->x_offset == x2 - e->width)
2315                        e->x_offset = MIN (x2 - e->width + RIGHT_BORDER + 1,
2316                                           html_engine_get_doc_width (e) - e->width);
2317                if (e->width > 2*LEFT_BORDER && e->x_offset >= x1)
2318                        e->x_offset = MAX (x1 - LEFT_BORDER, 0);
2319
2320                if (y2 >= e->y_offset + e->height)
2321                        e->y_offset = y2 - e->height + 1;
2322                if (y1 < e->y_offset)
2323                        e->y_offset = y1;
2324                if (e->height > 2*BOTTOM_BORDER && e->y_offset == y2 - e->height + 1)
2325                        e->y_offset = MIN (y2 - e->height + BOTTOM_BORDER + 1,
2326                                           html_engine_get_doc_height (e) - e->height);
2327                if (e->height > 2*TOP_BORDER && e->y_offset >= y1)
2328                        e->y_offset = MAX (y1 - TOP_BORDER, 0);
2329
2330                if (e->x_offset != xo)
2331                        gtk_adjustment_set_value (GTK_LAYOUT (w)->hadjustment, (gfloat) e->x_offset);
2332                if (e->y_offset != yo)
2333                        gtk_adjustment_set_value (GTK_LAYOUT (w)->vadjustment, (gfloat) e->y_offset);
2334                /* printf ("engine pos: %d,%d x %d,%d\n",
2335                   e->x_offset, e->y_offset, e->x_offset + e->width, e->y_offset + e->height); */
2336
2337                if (!GTK_WIDGET_HAS_FOCUS (w) && !html_object_is_embedded (obj))
2338                        gtk_widget_grab_focus (w);
2339                if (e->caret_mode)
2340                        html_engine_jump_to_object (e, obj, offset);
2341
2342                return TRUE;
2343        }
2344
2345        return FALSE;
2346}
2347
2348/* dnd begin */
2349
2350static void
2351drag_begin (GtkWidget *widget, GdkDragContext *context)
2352{
2353        HTMLInterval *i;
2354        HTMLObject *o;
2355
2356        /* printf ("drag_begin\n"); */
2357        GTK_HTML (widget)->priv->dnd_real_object = o = GTK_HTML (widget)->priv->dnd_object;
2358        GTK_HTML (widget)->priv->dnd_real_object_offset = GTK_HTML (widget)->priv->dnd_object_offset;
2359        GTK_HTML (widget)->priv->dnd_in_progress = TRUE;
2360
2361        i = html_interval_new (o, o, 0, html_object_get_length (o));
2362        html_engine_select_interval (GTK_HTML (widget)->engine, i);
2363}
2364
2365static void
2366drag_end (GtkWidget *widget, GdkDragContext *context)
2367{
2368        /* printf ("drag_end\n"); */
2369        GTK_HTML (widget)->priv->dnd_in_progress = FALSE;
2370}
2371
2372static void
2373drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data, guint info, guint time)
2374{
2375        /* printf ("drag_data_get\n"); */
2376        switch (info) {
2377        case DND_TARGET_TYPE_MOZILLA_URL:
2378        case DND_TARGET_TYPE_TEXT_URI_LIST:
2379                /* printf ("\ttext/uri-list\n"); */
2380        case DND_TARGET_TYPE_TEXT_HTML:
2381        case DND_TARGET_TYPE_TEXT_PLAIN:
2382        case DND_TARGET_TYPE_UTF8_STRING:
2383        case DND_TARGET_TYPE_STRING: {
2384                HTMLObject *obj = GTK_HTML (widget)->priv->dnd_real_object;
2385                gint offset = GTK_HTML (widget)->priv->dnd_real_object_offset;
2386                const gchar *url, *target;
2387                gchar *complete_url;
2388
2389                /* printf ("\ttext/plain\n"); */
2390                if (obj) {
2391                        /* printf ("obj %p\n", obj); */
2392                        url = html_object_get_url (obj, offset);
2393                        target = html_object_get_target (obj, offset);
2394                        if (url && *url) {
2395
2396                                complete_url = g_strconcat (url, target && *target ? "#" : NULL, target, NULL);
2397
2398                                if (info == DND_TARGET_TYPE_MOZILLA_URL) {
2399                                        /* MOZ_URL is in UCS-2 but in format 8. BROKEN!
2400                                         *
2401                                         * The data contains the URL, a \n, then the
2402                                         * title of the web page.
2403                                         */
2404                                        char *ucs2;
2405                                        char *utf8;
2406                                        int written_len;
2407
2408                                        if (HTML_IS_TEXT (obj)) {
2409                                                Link *link = html_text_get_link_at_offset (HTML_TEXT (obj), offset);
2410                                                char *text;
2411
2412                                                g_return_if_fail (link);
2413                                                text = g_strndup (HTML_TEXT (obj)->text + link->start_index, link->end_index - link->start_index);
2414                                                utf8 = g_strconcat (complete_url, "\n", text, NULL);
2415                                        } else
2416                                                utf8 = g_strconcat (complete_url, "\n", complete_url, NULL);
2417
2418                                        ucs2 = g_convert (utf8, strlen (utf8), "UCS-2", "UTF-8", NULL, &written_len, NULL);
2419                                        gtk_selection_data_set (selection_data, selection_data->target, 8,
2420                                                                ucs2, written_len);
2421                                        g_free (utf8);
2422                                        g_free (complete_url);
2423                                        GTK_HTML (widget)->priv->dnd_url = ucs2;
2424                                } else {
2425                                        gtk_selection_data_set (selection_data, selection_data->target, 8,
2426                                                                complete_url, strlen (complete_url));
2427                                        /* printf ("complete URL %s\n", complete_url); */
2428                                        GTK_HTML (widget)->priv->dnd_url = complete_url;
2429                                }
2430                        }
2431                }
2432        }
2433        break;
2434        }
2435}
2436
2437static void
2438drag_data_delete (GtkWidget *widget, GdkDragContext *context)
2439{
2440        g_free (GTK_HTML (widget)->priv->dnd_url);
2441        GTK_HTML (widget)->priv->dnd_url = NULL;
2442}
2443
2444static gchar *
2445next_uri (guchar **uri_list, gint *len, gint *list_len)
2446{
2447        guchar *uri, *begin;
2448
2449        begin = *uri_list;
2450        *len = 0;
2451        while (**uri_list && **uri_list != '\n' && **uri_list != '\r' && *list_len) {
2452                (*uri_list) ++;
2453                (*len) ++;
2454                (*list_len) --;
2455        }
2456
2457        uri = g_strndup (begin, *len);
2458
2459        while ((!**uri_list || **uri_list == '\n' || **uri_list == '\r') && *list_len) {
2460                (*uri_list) ++;
2461                (*list_len) --;
2462        }       
2463
2464        return uri;
2465}
2466
2467static gchar *known_protocols [] = {
2468        "http://",
2469        "ftp://",
2470        "nntp://",
2471        "news://",
2472        "mailto:",
2473        "file:",
2474        NULL
2475};
2476
2477static HTMLObject *
2478new_obj_from_uri (HTMLEngine *e, char *uri, char *title, gint len)
2479{
2480        gint i;
2481
2482        if (!strncmp (uri, "file:", 5)) {
2483                if (!HTML_IS_PLAIN_PAINTER(e->painter)) {
2484                        GdkPixbuf *pixbuf = NULL;
2485                        char *img_path = g_filename_from_uri (uri, NULL, NULL);
2486                        if (img_path) {
2487                                pixbuf = gdk_pixbuf_new_from_file(img_path, NULL);
2488                                g_free(img_path);
2489                        }
2490                        if (pixbuf) {
2491                                g_object_unref (pixbuf);
2492                                return html_image_new (html_engine_get_image_factory (e), uri,
2493                                                       NULL, NULL, -1, -1, FALSE, FALSE, 0,
2494                                                       html_colorset_get_color (e->settings->color_set, HTMLTextColor),
2495                                                       HTML_VALIGN_BOTTOM, TRUE);
2496                        }
2497                }
2498        }
2499
2500        for (i = 0; known_protocols [i]; i++) {
2501                if (!strncmp (uri, known_protocols [i], strlen (known_protocols [i]))) {
2502                        if (!title)
2503                                title = uri;
2504                        return html_engine_new_link (e, title, len, uri);
2505                }
2506        }       
2507
2508        return NULL;
2509}
2510
2511static void
2512move_before_paste (GtkWidget *widget, gint x, gint y)
2513{
2514        HTMLEngine *engine = GTK_HTML (widget)->engine;
2515
2516        if (html_engine_is_selection_active (engine)) {
2517                HTMLObject *obj;
2518                guint offset;
2519
2520                obj = html_engine_get_object_at (engine, x, y, &offset, FALSE);
2521                if (!html_engine_point_in_selection (engine, obj, offset)) {
2522                        html_engine_disable_selection (engine);
2523                        html_engine_edit_selection_updater_update_now (engine->selection_updater);
2524                }
2525        }
2526        if (!html_engine_is_selection_active (engine)) {
2527
2528                html_engine_jump_at (engine, x, y);
2529                gtk_html_update_styles (GTK_HTML (widget));
2530        }
2531}
2532
2533static void
2534drag_data_received (GtkWidget *widget, GdkDragContext *context,
2535                    gint x, gint y, GtkSelectionData *selection_data, guint info, guint time)
2536{
2537        HTMLEngine *engine = GTK_HTML (widget)->engine;
2538        gboolean pasted = FALSE;
2539
2540        /* printf ("drag data received at %d,%d\n", x, y); */
2541
2542        if (!selection_data->data || selection_data->length < 0 || !html_engine_get_editable (engine))
2543                return;
2544
2545        gdk_window_get_pointer (GTK_LAYOUT (widget)->bin_window, &x, &y, NULL);
2546        move_before_paste (widget, x, y);
2547
2548        switch (info) {
2549        case DND_TARGET_TYPE_TEXT_PLAIN:
2550        case DND_TARGET_TYPE_UTF8_STRING:
2551        case DND_TARGET_TYPE_STRING:
2552        case DND_TARGET_TYPE_TEXT_HTML:
2553                selection_received (widget, selection_data, time);
2554                pasted = TRUE;
2555                break;
2556        case DND_TARGET_TYPE_MOZILLA_URL  : {
2557                HTMLObject *obj;
2558                char *utf8, *title;
2559                               
2560                /* MOZ_URL is in UCS-2 but in format 8. BROKEN!
2561                 *
2562                 * The data contains the URL, a \n, then the
2563                 * title of the web page.
2564                 */
2565
2566                if (selection_data->format != 8 ||
2567                    selection_data->length == 0 ||
2568                    (selection_data->length % 2) != 0) {
2569                        g_printerr (_("Mozilla url dropped on Composer had wrong format (%d) or length (%d)\n"),
2570                                selection_data->format,
2571                                selection_data->length);
2572                        /* get out of the switch */
2573                        break;
2574                }
2575
2576                utf8 = ucs2_to_utf8_with_bom_check (selection_data->data, selection_data->length);
2577                title = strchr (utf8, '\n');
2578                if (title) {
2579                        *title = 0;
2580                        title ++;
2581                }
2582
2583                html_undo_level_begin (engine->undo, "Dropped URI(s)", "Remove Dropped URI(s)");
2584
2585                obj = new_obj_from_uri (engine, utf8, (HTML_IS_PLAIN_PAINTER (engine->painter) && context->action <= GDK_ACTION_COPY) ? utf8 : title, -1);
2586                if (obj) {
2587                        html_engine_paste_object (engine, obj, html_object_get_length (obj));
2588                        pasted = TRUE;
2589                }
2590                html_undo_level_end (engine->undo);
2591                g_free (utf8);
2592        }
2593        break;
2594
2595        case DND_TARGET_TYPE_TEXT_URI_LIST: {
2596                HTMLObject *obj;
2597                gint list_len, len;
2598                gchar *uri;
2599
2600                html_undo_level_begin (engine->undo, "Dropped URI(s)", "Remove Dropped URI(s)");
2601                list_len = selection_data->length;
2602                do {
2603                        uri = next_uri (&selection_data->data, &len, &list_len);
2604                        obj = new_obj_from_uri (engine, uri, NULL, -1);
2605                        if (obj) {
2606                                html_engine_paste_object (engine, obj, html_object_get_length (obj));
2607                                pasted = TRUE;
2608                        }
2609                } while (list_len);
2610                html_undo_level_end (engine->undo);
2611        }
2612        break;
2613        }
2614        gtk_drag_finish (context, pasted, FALSE, time);
2615}
2616
2617static gboolean
2618drag_motion (GtkWidget *widget, GdkDragContext *context, gint x, gint y, guint time)
2619{
2620        GdkWindow *window = widget->window;
2621
2622        if (!gtk_html_get_editable (GTK_HTML (widget)))
2623                return FALSE;
2624
2625        gdk_window_get_pointer (GTK_LAYOUT (widget)->bin_window, &x, &y, NULL);
2626
2627        html_engine_disable_selection (GTK_HTML (widget)->engine);
2628        html_engine_jump_at (GTK_HTML (widget)->engine, x, y);
2629        html_engine_show_cursor (GTK_HTML (widget)->engine);
2630
2631        mouse_change_pos (widget, window, x, y, 0);
2632
2633        return TRUE;
2634}
2635
2636/* dnd end */
2637
2638static void
2639read_key_theme (GtkHTMLClass *html_class)
2640{
2641        gchar *key_theme;
2642
2643        key_theme = gconf_client_get_string (gconf_client_get_default (), "/desktop/gnome/interface/gtk_key_theme", NULL);
2644        html_class->use_emacs_bindings = key_theme && !strcmp (key_theme, "Emacs");
2645        g_free (key_theme);
2646}
2647
2648static void
2649client_notify_key_theme (GConfClient* client, guint cnxn_id, GConfEntry* entry, gpointer data)
2650{
2651        read_key_theme ((GtkHTMLClass *) data);
2652}
2653
2654static void
2655client_notify_monospace_font (GConfClient* client, guint cnxn_id, GConfEntry* entry, gpointer data)
2656{
2657        GtkHTML *html = (GtkHTML *) data;
2658        HTMLEngine *e = html->engine;
2659        if (e && e->painter) {
2660                gtk_html_set_fonts (html, e->painter);
2661                html_engine_refresh_fonts (e);
2662        }
2663}
2664
2665static void
2666gtk_html_class_init (GtkHTMLClass *klass)
2667{
2668        GObjectClass      *gobject_class;
2669        GtkHTMLClass      *html_class;
2670        GtkWidgetClass    *widget_class;
2671        GtkObjectClass    *object_class;
2672        GtkLayoutClass    *layout_class;
2673        GtkContainerClass *container_class;
2674       
2675        html_class = (GtkHTMLClass *) klass;
2676        gobject_class = (GObjectClass *) klass;
2677        widget_class = (GtkWidgetClass *) klass;
2678        object_class = (GtkObjectClass *) klass;
2679        layout_class = (GtkLayoutClass *) klass;
2680        container_class = (GtkContainerClass *) klass;
2681
2682        object_class->destroy = destroy;
2683
2684        parent_class = gtk_type_class (GTK_TYPE_LAYOUT);
2685
2686        signals [TITLE_CHANGED] =
2687                g_signal_new ("title_changed",
2688                              G_TYPE_FROM_CLASS (object_class),
2689                              G_SIGNAL_RUN_FIRST,
2690                              G_STRUCT_OFFSET (GtkHTMLClass, title_changed),
2691                              NULL, NULL,
2692                              g_cclosure_marshal_VOID__STRING,
2693                              G_TYPE_NONE, 1,
2694                              G_TYPE_STRING);
2695        signals [URL_REQUESTED] =
2696                g_signal_new ("url_requested",
2697                              G_TYPE_FROM_CLASS (object_class),
2698                              G_SIGNAL_RUN_FIRST,
2699                              G_STRUCT_OFFSET (GtkHTMLClass, url_requested),
2700                              NULL, NULL,
2701                              html_g_cclosure_marshal_VOID__STRING_POINTER,
2702                              G_TYPE_NONE, 2,
2703                              G_TYPE_STRING,
2704                              G_TYPE_POINTER);
2705        signals [LOAD_DONE] =
2706                g_signal_new ("load_done",
2707                              G_TYPE_FROM_CLASS (object_class),
2708                              G_SIGNAL_RUN_FIRST,
2709                              G_STRUCT_OFFSET (GtkHTMLClass, load_done),
2710                              NULL, NULL,
2711                              g_cclosure_marshal_VOID__VOID,
2712                              G_TYPE_NONE, 0);
2713        signals [LINK_CLICKED] =
2714                g_signal_new ("link_clicked",
2715                              G_TYPE_FROM_CLASS (object_class),
2716                              G_SIGNAL_RUN_FIRST,
2717                              G_STRUCT_OFFSET (GtkHTMLClass, link_clicked),
2718                              NULL, NULL,
2719                              g_cclosure_marshal_VOID__STRING,
2720                              G_TYPE_NONE, 1,
2721                              G_TYPE_STRING);
2722        signals [SET_BASE] =
2723                g_signal_new ("set_base",
2724                              G_TYPE_FROM_CLASS (object_class),
2725                              G_SIGNAL_RUN_FIRST,
2726                              G_STRUCT_OFFSET (GtkHTMLClass, set_base),
2727                              NULL, NULL,
2728                              g_cclosure_marshal_VOID__STRING,
2729                              G_TYPE_NONE, 1,
2730                              G_TYPE_STRING);
2731        signals [SET_BASE_TARGET] =
2732                g_signal_new ("set_base_target",
2733                              G_TYPE_FROM_CLASS (object_class),
2734                              G_SIGNAL_RUN_FIRST,
2735                              G_STRUCT_OFFSET (GtkHTMLClass, set_base_target),
2736                              NULL, NULL,
2737                              g_cclosure_marshal_VOID__STRING,
2738                              G_TYPE_NONE, 1,
2739                              G_TYPE_STRING);
2740       
2741        signals [ON_URL] =
2742                g_signal_new ("on_url",
2743                              G_TYPE_FROM_CLASS (object_class),
2744                              G_SIGNAL_RUN_FIRST,
2745                              G_STRUCT_OFFSET (GtkHTMLClass, on_url),
2746                              NULL, NULL,
2747                              g_cclosure_marshal_VOID__STRING,
2748                              G_TYPE_NONE, 1,
2749                              G_TYPE_STRING);
2750       
2751        signals [REDIRECT] =
2752                g_signal_new ("redirect",
2753                              G_TYPE_FROM_CLASS (object_class),
2754                              G_SIGNAL_RUN_FIRST,
2755                              G_STRUCT_OFFSET (GtkHTMLClass, redirect),
2756                              NULL, NULL,
2757                              html_g_cclosure_marshal_VOID__POINTER_INT,
2758                              G_TYPE_NONE, 2,
2759                              G_TYPE_STRING,
2760                              G_TYPE_INT);
2761       
2762        signals [SUBMIT] =
2763                g_signal_new ("submit",
2764                              G_TYPE_FROM_CLASS (object_class),
2765                              G_SIGNAL_RUN_FIRST,
2766                              G_STRUCT_OFFSET (GtkHTMLClass, submit),
2767                              NULL, NULL,
2768                              html_g_cclosure_marshal_VOID__STRING_STRING_STRING,
2769                              G_TYPE_NONE, 3,
2770                              G_TYPE_STRING,
2771                              G_TYPE_STRING,
2772                              G_TYPE_STRING);
2773
2774        signals [OBJECT_REQUESTED] =
2775                g_signal_new ("object_requested",
2776                              G_TYPE_FROM_CLASS (object_class),
2777                              G_SIGNAL_RUN_LAST,
2778                              G_STRUCT_OFFSET (GtkHTMLClass, object_requested),
2779                              NULL, NULL,
2780                              html_g_cclosure_marshal_BOOL__OBJECT,
2781                              G_TYPE_BOOLEAN, 1,
2782                              G_TYPE_OBJECT);
2783       
2784        signals [CURRENT_PARAGRAPH_STYLE_CHANGED] =
2785                g_signal_new ("current_paragraph_style_changed",
2786                              G_TYPE_FROM_CLASS (object_class),
2787                              G_SIGNAL_RUN_FIRST,
2788                              G_STRUCT_OFFSET (GtkHTMLClass, current_paragraph_style_changed),
2789                              NULL, NULL,
2790                              g_cclosure_marshal_VOID__INT,
2791                              G_TYPE_NONE, 1,
2792                              G_TYPE_INT);
2793
2794        signals [CURRENT_PARAGRAPH_INDENTATION_CHANGED] =
2795                g_signal_new ("current_paragraph_indentation_changed",
2796                              G_TYPE_FROM_CLASS (object_class),
2797                              G_SIGNAL_RUN_FIRST,
2798                              G_STRUCT_OFFSET (GtkHTMLClass, current_paragraph_indentation_changed),
2799                              NULL, NULL,
2800                              g_cclosure_marshal_VOID__INT,
2801                              G_TYPE_NONE, 1,
2802                              G_TYPE_INT);
2803
2804        signals [CURRENT_PARAGRAPH_ALIGNMENT_CHANGED] =
2805                g_signal_new ("current_paragraph_alignment_changed",
2806                              G_TYPE_FROM_CLASS (object_class),
2807                              G_SIGNAL_RUN_FIRST,
2808                              G_STRUCT_OFFSET (GtkHTMLClass, current_paragraph_alignment_changed),
2809                              NULL, NULL,
2810                              g_cclosure_marshal_VOID__INT,
2811                              G_TYPE_NONE, 1,
2812                              G_TYPE_INT);
2813
2814        signals [INSERTION_FONT_STYLE_CHANGED] =
2815                g_signal_new ("insertion_font_style_changed",
2816                              G_TYPE_FROM_CLASS (object_class),
2817                              G_SIGNAL_RUN_FIRST,
2818                              G_STRUCT_OFFSET (GtkHTMLClass, insertion_font_style_changed),
2819                              NULL, NULL,
2820                              g_cclosure_marshal_VOID__INT,
2821                              G_TYPE_NONE, 1,
2822                              G_TYPE_INT);
2823       
2824        signals [INSERTION_COLOR_CHANGED] =
2825                g_signal_new ("insertion_color_changed",
2826                              G_TYPE_FROM_CLASS (object_class),
2827                              G_SIGNAL_RUN_FIRST,
2828                              G_STRUCT_OFFSET (GtkHTMLClass, insertion_color_changed),
2829                              NULL, NULL,
2830                              g_cclosure_marshal_VOID__POINTER,
2831                              G_TYPE_NONE, 1,
2832                              G_TYPE_POINTER);
2833       
2834        signals [SIZE_CHANGED] =
2835                g_signal_new ("size_changed",
2836                              G_TYPE_FROM_CLASS (object_class),
2837                              G_SIGNAL_RUN_FIRST,
2838                              G_STRUCT_OFFSET (GtkHTMLClass, size_changed),
2839                              NULL, NULL,
2840                              g_cclosure_marshal_VOID__VOID,
2841                              G_TYPE_NONE, 0);
2842        signals [IFRAME_CREATED] =
2843                g_signal_new ("iframe_created",
2844                              G_TYPE_FROM_CLASS (object_class),
2845                              G_SIGNAL_RUN_FIRST,
2846                              G_STRUCT_OFFSET (GtkHTMLClass, iframe_created),
2847                              NULL, NULL,
2848                              g_cclosure_marshal_VOID__OBJECT,
2849                              G_TYPE_NONE, 1,
2850                              GTK_TYPE_HTML);
2851
2852        signals [SCROLL] =
2853                g_signal_new ("scroll",
2854                              G_TYPE_FROM_CLASS (object_class),
2855                              G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
2856                              G_STRUCT_OFFSET (GtkHTMLClass, scroll),
2857                              NULL, NULL,
2858                              html_g_cclosure_marshal_VOID__ENUM_ENUM_FLOAT,
2859                              G_TYPE_NONE, 3,
2860                              GTK_TYPE_ORIENTATION,
2861                              GTK_TYPE_SCROLL_TYPE, G_TYPE_FLOAT);
2862
2863        signals [CURSOR_MOVE] =
2864                g_signal_new ("cursor_move",
2865                              G_TYPE_FROM_CLASS (object_class),
2866                              G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
2867                              G_STRUCT_OFFSET (GtkHTMLClass, cursor_move),
2868                              NULL, NULL,
2869                              html_g_cclosure_marshal_VOID__ENUM_ENUM,
2870                              G_TYPE_NONE, 2, GTK_TYPE_DIRECTION_TYPE, GTK_TYPE_HTML_CURSOR_SKIP);
2871
2872        signals [COMMAND] =
2873                g_signal_new ("command",
2874                              G_TYPE_FROM_CLASS (object_class),
2875                              G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
2876                              G_STRUCT_OFFSET (GtkHTMLClass, command),
2877                              NULL, NULL,
2878                              g_cclosure_marshal_VOID__ENUM,
2879                              G_TYPE_NONE, 1, GTK_TYPE_HTML_COMMAND);
2880
2881        object_class->destroy = destroy;
2882       
2883
2884#ifdef USE_PROPS
2885        gobject_class->get_property = gtk_html_get_property;
2886        gobject_class->set_property = gtk_html_set_property;
2887
2888        g_object_class_install_property (gobject_class,
2889                                         PROP_EDITABLE,
2890                                         g_param_spec_boolean ("editable",
2891                                                               _("Editable"),
2892                                                               _("Whether the html can be edited"),
2893                                                               FALSE,
2894                                                               G_PARAM_READABLE | G_PARAM_WRITABLE));
2895        g_object_class_install_property (gobject_class,
2896                                         PROP_TITLE,
2897                                         g_param_spec_string ("title",
2898                                                              _("Document Title"),
2899                                                              _("The title of the current document"),
2900                                                              NULL,
2901                                                              G_PARAM_WRITABLE | G_PARAM_READABLE));
2902        g_object_class_install_property (gobject_class,
2903                                         PROP_DOCUMENT_BASE,
2904                                         g_param_spec_string ("document_base",
2905                                                              _("Document Base"),
2906                                                              _("The base URL for relative references"),
2907                                                              NULL,
2908                                                              G_PARAM_WRITABLE | G_PARAM_READABLE));
2909        g_object_class_install_property (gobject_class,
2910                                         PROP_TARGET_BASE,
2911                                         g_param_spec_string ("target_base",
2912                                                              _("Target Base"),
2913                                                              _("The base URL of the targe frame"),
2914                                                              NULL,
2915                                                              G_PARAM_WRITABLE | G_PARAM_READABLE));
2916
2917
2918#endif
2919
2920        gtk_widget_class_install_style_property (widget_class,
2921                                                 g_param_spec_string ("fixed_font_name",
2922                                                                     _("Fixed Width Font"),
2923                                                                     _("The Monospace font to use for typewriter text"),
2924                                                                     NULL,
2925                                                                     G_PARAM_READABLE));
2926       
2927        gtk_widget_class_install_style_property (widget_class,
2928                                                 g_param_spec_boxed ("link_color",
2929                                                                     _("New Link Color"),
2930                                                                     _("The color of new link elements"),
2931                                                                     GDK_TYPE_COLOR,
2932                                                                     G_PARAM_READABLE));
2933        gtk_widget_class_install_style_property (widget_class,
2934                                                 g_param_spec_boxed ("vlink_color",
2935                                                                     _("Visited Link Color"),
2936                                                                     _("The color of visited link elements"),
2937                                                                     GDK_TYPE_COLOR,
2938                                                                     G_PARAM_READABLE));
2939        gtk_widget_class_install_style_property (widget_class,
2940                                                 g_param_spec_boxed ("alink_color",
2941                                                                     _("Active Link Color"),
2942                                                                     _("The color of active link elements"),
2943                                                                     GDK_TYPE_COLOR,
2944                                                                     G_PARAM_READABLE));
2945        gtk_widget_class_install_style_property (widget_class,
2946                                                 g_param_spec_boxed ("spell_error_color",
2947                                                                     _("Spelling Error Color"),
2948                                                                     _("The color of the spelling error markers"),
2949                                                                     GDK_TYPE_COLOR,
2950                                                                     G_PARAM_READABLE));
2951        gtk_widget_class_install_style_property (widget_class,
2952                                                 g_param_spec_boxed ("cite_color",
2953                                                                     _("Cite Quotation Color"),
2954                                                                     _("The color of the cited text"),
2955                                                                     GDK_TYPE_COLOR,
2956                                                                     G_PARAM_READABLE));
2957
2958
2959        widget_class->realize = realize;
2960        widget_class->unrealize = unrealize;
2961        widget_class->style_set = style_set;
2962        widget_class->key_press_event = key_press_event;
2963        widget_class->key_release_event = key_release_event;
2964        widget_class->expose_event  = expose;
2965        widget_class->size_request = gtk_html_size_request;
2966        widget_class->size_allocate = size_allocate;
2967        widget_class->motion_notify_event = motion_notify_event;
2968        widget_class->visibility_notify_event = visibility_notify_event;
2969        widget_class->hierarchy_changed = hierarchy_changed;
2970        widget_class->button_press_event = button_press_event;
2971        widget_class->button_release_event = button_release_event;
2972        widget_class->focus_in_event = focus_in_event;
2973        widget_class->focus_out_event = focus_out_event;
2974        widget_class->enter_notify_event = enter_notify_event;
2975        widget_class->selection_get = selection_get;
2976        widget_class->selection_received = selection_received;
2977        widget_class->selection_clear_event = selection_clear_event;
2978        widget_class->drag_data_get = drag_data_get;
2979        widget_class->drag_data_delete = drag_data_delete;
2980        widget_class->drag_begin = drag_begin;
2981        widget_class->drag_end = drag_end;
2982        widget_class->drag_data_received = drag_data_received;
2983        widget_class->drag_motion = drag_motion;
2984        widget_class->focus = focus;
2985
2986        container_class->set_focus_child = set_focus_child;
2987
2988        layout_class->set_scroll_adjustments = set_adjustments;
2989
2990        html_class->scroll            = scroll;
2991        html_class->cursor_move       = cursor_move;
2992        html_class->command           = command;
2993
2994        add_bindings (klass);
2995        gtk_html_accessibility_init ();
2996
2997        gtk_rc_parse (PREFIX "/share/" GTKHTML_RELEASE_STRING "/keybindingsrc.emacs");
2998        html_class->emacs_bindings = gtk_binding_set_find ("gtkhtml-bindings-emacs");
2999        read_key_theme (html_class);
3000        gconf_client_notify_add (gconf_client_get_default (), "/desktop/gnome/interface/gtk_key_theme",
3001                                 client_notify_key_theme, html_class, NULL, &gconf_error);
3002}
3003
3004static void
3005init_properties_widget (GtkHTML *html)
3006{
3007        GtkHTMLClassProperties *prop;
3008
3009        prop = get_class_properties (html);
3010
3011        html->priv->notify_spell_id = gconf_client_notify_add (gconf_client, GNOME_SPELL_GCONF_DIR,
3012                                                               client_notify_spell_widget, html, NULL, &gconf_error);
3013        if (gconf_error) {
3014                g_warning ("gconf error: %s\n", gconf_error->message);
3015                html->priv->notify_spell_id = 0;
3016        }
3017}
3018
3019void
3020gtk_html_im_reset (GtkHTML *html)
3021{
3022        if (!html->priv->im_block_reset) {
3023                D_IM (printf ("IM reset requested\n");)
3024                if (html->priv->need_im_reset) {
3025                        if (html->engine->freeze_count == 1)
3026                                html_engine_thaw_idle_flush (html->engine);
3027                        html->priv->need_im_reset = FALSE;
3028                        gtk_im_context_reset (html->priv->im_context);
3029                        D_IM (printf ("IM reset called\n");)
3030                }
3031        }
3032}
3033
3034static void
3035gtk_html_im_commit_cb (GtkIMContext *context, const gchar *str, GtkHTML *html)
3036{
3037        gboolean state = html->priv->im_block_reset;
3038        gint pos;
3039
3040        if (html->priv->im_pre_len > 0) {
3041                D_IM (printf ("IM delete last preedit %d + %d\n", html->priv->im_pre_pos, html->priv->im_pre_len);)
3042                                                                               
3043                html_undo_freeze (html->engine->undo);
3044                html_cursor_jump_to_position_no_spell (html->engine->cursor, html->engine, html->priv->im_pre_pos);
3045                html_engine_set_mark (html->engine);
3046                html_cursor_jump_to_position_no_spell (html->engine->cursor, html->engine, html->priv->im_pre_pos + html->priv->im_pre_len);
3047                html_engine_delete (html->engine);
3048                html->priv->im_pre_len = 0;
3049                html_undo_thaw (html->engine->undo);
3050        }
3051
3052        pos = html->engine->cursor->position;
3053        if (html->engine->mark && html->engine->mark->position > pos)
3054                pos = html->engine->mark->position;
3055
3056        html->priv->im_block_reset = TRUE;
3057        D_IM (printf ("IM commit %s\n", str);)
3058        html_engine_paste_text (html->engine, str, -1);
3059        html->priv->im_block_reset = state;
3060
3061        D_IM (printf ("IM commit pos: %d pre_pos: %d\n", pos, html->priv->im_pre_pos);)
3062        if (html->priv->im_pre_pos >= pos)
3063                html->priv->im_pre_pos += html->engine->cursor->position - pos;
3064}
3065
3066static void
3067gtk_html_im_preedit_start_cb (GtkIMContext *context, GtkHTML *html)
3068{
3069        html->priv->im_pre_len = 0;
3070}
3071
3072static void
3073gtk_html_im_preedit_changed_cb (GtkIMContext *context, GtkHTML *html)
3074{
3075        PangoAttrList *attrs;
3076        gchar *preedit_string;
3077        gint cursor_pos, initial_position;
3078        gboolean state = html->priv->im_block_reset;
3079        gboolean pop_selection = FALSE;
3080        gint deleted = 0;
3081
3082        D_IM (printf ("IM preedit changed cb [begin] cursor %d(%p) mark %d(%p) active: %d\n",
3083                      html->engine->cursor ? html->engine->cursor->position : 0, html->engine->cursor,
3084                      html->engine->mark ? html->engine->mark->position : 0, html->engine->mark,
3085                      html_engine_is_selection_active (html->engine));)
3086        html->priv->im_block_reset = TRUE;
3087
3088        if (html_engine_is_selection_active (html->engine)) {
3089                D_IM (printf ("IM push selection\n");)
3090                html_engine_selection_push (html->engine);
3091                html_engine_disable_selection (html->engine);
3092                html_engine_edit_selection_updater_update_now (html->engine->selection_updater);
3093                pop_selection = TRUE;
3094        }
3095        initial_position = html->engine->cursor->position;
3096        D_IM (printf ("IM initial position %d\n", initial_position);)
3097
3098        html_undo_freeze (html->engine->undo);
3099
3100        if (html->priv->im_pre_len > 0) {
3101                D_IM (printf ("IM delete last preedit %d + %d\n", html->priv->im_pre_pos, html->priv->im_pre_len);)
3102               
3103                html_cursor_jump_to_position_no_spell (html->engine->cursor, html->engine, html->priv->im_pre_pos);
3104                html_engine_set_mark (html->engine);
3105                html_cursor_jump_to_position_no_spell (html->engine->cursor, html->engine, html->priv->im_pre_pos + html->priv->im_pre_len);
3106                html_engine_delete (html->engine);
3107                deleted = html->priv->im_pre_len;
3108        } else
3109                html->priv->im_orig_style = html_engine_get_font_style (html->engine);
3110
3111        gtk_im_context_get_preedit_string (html->priv->im_context, &preedit_string, &attrs, &cursor_pos);
3112
3113        D_IM (printf ("IM preedit changed to %s\n", preedit_string);)
3114        html->priv->im_pre_len = g_utf8_strlen (preedit_string, -1);
3115
3116        if (html->priv->im_pre_len > 0) {
3117                cursor_pos = CLAMP (cursor_pos, 0, html->priv->im_pre_len);
3118                html->priv->im_pre_pos = html->engine->cursor->position;
3119                html_engine_paste_text_with_extra_attributes (html->engine, preedit_string, html->priv->im_pre_len, attrs);
3120                html_cursor_jump_to_position_no_spell (html->engine->cursor, html->engine, html->priv->im_pre_pos + cursor_pos);
3121        } else
3122                html_engine_set_font_style (html->engine, 0, html->priv->im_orig_style);
3123        g_free (preedit_string);
3124
3125        if (pop_selection) {
3126                gint position= html->engine->cursor->position, cpos, mpos;
3127                D_IM (printf ("IM pop selection\n");)
3128                g_assert (html_engine_selection_stack_top (html->engine, &cpos, &mpos));
3129                if (position < MAX (cpos, mpos) + html->priv->im_pre_len - deleted)
3130                        g_assert (html_engine_selection_stack_top_modify (html->engine, html->priv->im_pre_len - deleted));
3131                html_engine_selection_pop (html->engine);
3132        }
3133        /* that works for now, but idealy we should be able to have cursor positioned outside selection, so that preedit
3134           cursor is in the right place */
3135        if (html->priv->im_pre_len == 0)
3136                html_cursor_jump_to_position_no_spell (html->engine->cursor, html->engine,
3137                                                       initial_position >= html->priv->im_pre_pos + deleted ? initial_position - deleted : initial_position);
3138
3139        if (html->engine->freeze_count == 1)
3140                html_engine_thaw_idle_flush (html->engine);
3141        /* FIXME gtk_im_context_set_cursor_location (im_context, &area); */
3142        html->priv->im_block_reset = state;
3143
3144        html_undo_thaw (html->engine->undo);
3145
3146        D_IM (printf ("IM preedit changed cb [end] cursor %d(%p) mark %d(%p) active: %d\n",
3147                      html->engine->cursor ? html->engine->cursor->position : 0, html->engine->cursor,
3148                      html->engine->mark ? html->engine->mark->position : 0, html->engine->mark,
3149                      html_engine_is_selection_active (html->engine));)
3150}
3151
3152static gchar *
3153get_surrounding_text (HTMLEngine *e, gint *offset)
3154{
3155        HTMLObject *o = e->cursor->object;
3156        HTMLObject *prev;
3157        gchar *text = NULL;
3158
3159        if (!html_object_is_text (o)) {
3160                if (e->cursor->offset == 0) {
3161                        prev = html_object_prev_not_slave (o);
3162                        if (html_object_is_text (prev)) {
3163                                o = prev;
3164                        } else
3165                                return NULL;
3166                } else if (e->cursor->offset == html_object_get_length (e->cursor->object)) {
3167                        HTMLObject *next;
3168
3169                        next = html_object_next_not_slave (o);
3170                        if (html_object_is_text (next)) {
3171                                o = next;
3172                        } else
3173                                return NULL;
3174                }
3175                *offset = 0;
3176        } else
3177                *offset = e->cursor->offset;
3178
3179        while ((prev = html_object_prev_not_slave (o)) && html_object_is_text (prev)) {
3180                o = prev;
3181                *offset += HTML_TEXT (o)->text_len;
3182        }
3183
3184        while (o) {
3185                if (html_object_is_text (o))
3186                        text = g_strconcat (text, HTML_TEXT (o)->text, NULL);
3187                o = html_object_next_not_slave (o);
3188        }
3189
3190        return text;
3191}
3192
3193static gboolean
3194gtk_html_im_retrieve_surrounding_cb (GtkIMContext *context, GtkHTML *html)
3195{
3196        gint offset;
3197
3198        D_IM (printf ("IM gtk_html_im_retrieve_surrounding_cb\n");)
3199        gtk_im_context_set_surrounding (context, get_surrounding_text (html->engine, &offset), -1, offset);
3200
3201        return TRUE;
3202}
3203
3204static gboolean
3205gtk_html_im_delete_surrounding_cb (GtkIMContext *slave, gint offset, gint n_chars, GtkHTML *html)
3206{
3207        D_IM (printf ("IM gtk_html_im_delete_surrounding_cb\n");)
3208        if (html_engine_get_editable (html->engine) && !html_engine_is_selection_active (html->engine)) {
3209                gint orig_position = html->engine->cursor->position;
3210
3211                html_cursor_jump_to_position_no_spell (html->engine->cursor, html->engine, orig_position + offset);
3212                html_engine_set_mark (html->engine);
3213                html_cursor_jump_to_position_no_spell (html->engine->cursor, html->engine, orig_position + offset + n_chars);
3214                html_engine_delete (html->engine);
3215                if (offset >= 0)
3216                        html_cursor_jump_to_position_no_spell (html->engine->cursor, html->engine, orig_position);
3217        }
3218        return TRUE;
3219}
3220
3221static void
3222gtk_html_init (GtkHTML* html)
3223{
3224        static const GtkTargetEntry targets[] = {
3225                { "text/html", 0, TARGET_HTML },
3226                { "UTF8_STRING", 0, TARGET_UTF8_STRING },
3227                { "COMPOUND_TEXT", 0, TARGET_COMPOUND_TEXT },
3228                { "STRING", 0, TARGET_STRING },
3229                { "TEXT",   0, TARGET_TEXT }
3230        };
3231        static const gint n_targets = sizeof(targets) / sizeof(targets[0]);
3232
3233        GTK_WIDGET_SET_FLAGS (GTK_WIDGET (html), GTK_CAN_FOCUS);
3234        GTK_WIDGET_SET_FLAGS (GTK_WIDGET (html), GTK_APP_PAINTABLE);
3235
3236        html->editor_api = NULL;
3237        html->debug = FALSE;
3238        html->allow_selection = TRUE;
3239
3240        html->pointer_url = NULL;
3241        html->hand_cursor = gdk_cursor_new (GDK_HAND2);
3242        html->ibeam_cursor = gdk_cursor_new (GDK_XTERM);
3243        html->hadj_connection = 0;
3244        html->vadj_connection = 0;
3245
3246        html->selection_x1 = 0;
3247        html->selection_y1 = 0;
3248
3249        html->in_selection = FALSE;
3250        html->in_selection_drag = FALSE;
3251
3252        html->priv = g_new0 (GtkHTMLPrivate, 1);
3253        html->priv->idle_handler_id = 0;
3254        html->priv->scroll_timeout_id = 0;
3255        html->priv->paragraph_style = GTK_HTML_PARAGRAPH_STYLE_NORMAL;
3256        html->priv->paragraph_alignment = GTK_HTML_PARAGRAPH_ALIGNMENT_LEFT;
3257        html->priv->paragraph_indentation = 0;
3258        html->priv->insertion_font_style = GTK_HTML_FONT_STYLE_DEFAULT;
3259        html->priv->selection_type = -1;
3260        html->priv->selection_as_cite = FALSE;
3261        html->priv->content_type = g_strdup ("html/text; charset=utf-8");
3262        html->priv->search_input_line = NULL;
3263        html->priv->in_object_resize = FALSE;
3264        html->priv->resize_cursor = gdk_cursor_new (GDK_BOTTOM_RIGHT_CORNER);
3265
3266        gtk_selection_add_targets (GTK_WIDGET (html),
3267                                   GDK_SELECTION_PRIMARY,
3268                                   targets, n_targets);
3269        gtk_selection_add_targets (GTK_WIDGET (html),
3270                                   gdk_atom_intern ("CLIPBOARD", FALSE),
3271                                   targets, n_targets);
3272
3273        /* IM Context */
3274        html->priv->im_context = gtk_im_multicontext_new ();
3275        html->priv->need_im_reset = FALSE;
3276        html->priv->im_block_reset = FALSE;
3277        html->priv->im_pre_len = 0;
3278 
3279        g_signal_connect (G_OBJECT (html->priv->im_context), "commit",
3280                          G_CALLBACK (gtk_html_im_commit_cb), html);
3281        g_signal_connect (G_OBJECT (html->priv->im_context), "preedit_start",
3282                          G_CALLBACK (gtk_html_im_preedit_start_cb), html);
3283        g_signal_connect (G_OBJECT (html->priv->im_context), "preedit_changed",
3284                          G_CALLBACK (gtk_html_im_preedit_changed_cb), html);
3285        g_signal_connect (G_OBJECT (html->priv->im_context), "retrieve_surrounding",
3286                          G_CALLBACK (gtk_html_im_retrieve_surrounding_cb), html);
3287        g_signal_connect (G_OBJECT (html->priv->im_context), "delete_surrounding",
3288                          G_CALLBACK (gtk_html_im_delete_surrounding_cb), html);
3289
3290        html->priv->notify_monospace_font_id =
3291                gconf_client_notify_add (gconf_client_get_default (), "/desktop/gnome/interface/monospace_font_name",
3292                                         client_notify_monospace_font, html, NULL, &gconf_error);
3293
3294        gtk_html_construct (html);
3295}
3296
3297GType
3298gtk_html_get_type (void)
3299{
3300        static GType html_type = 0;
3301
3302        if (!html_type) {
3303                static const GTypeInfo html_info = {
3304                        sizeof (GtkHTMLClass),
3305                        NULL,           /* base_init */
3306                        NULL,           /* base_finalize */
3307                        (GClassInitFunc) gtk_html_class_init,
3308                        NULL,           /* class_finalize */
3309                        NULL,           /* class_data */
3310                        sizeof (GtkHTML),
3311                        1,              /* n_preallocs */
3312                        (GInstanceInitFunc) gtk_html_init,
3313                };
3314               
3315                html_type = g_type_register_static (GTK_TYPE_LAYOUT, "GtkHTML", &html_info, 0);
3316        }
3317
3318        return html_type;
3319}
3320
3321/**
3322 * gtk_html_new:
3323 * @void:
3324 *
3325 * GtkHTML widget contructor. It creates an empty GtkHTML widget.
3326 *
3327 * Return value: A GtkHTML widget, newly created and empty.
3328 **/
3329
3330GtkWidget *
3331gtk_html_new (void)
3332{
3333        GtkWidget *html;
3334
3335        html = g_object_new (GTK_TYPE_HTML, NULL);
3336
3337        return html;
3338}
3339
3340/**
3341 * gtk_html_new_from_string:
3342 * @str: A string containing HTML source.
3343 * @len: A length of @str, if @len == -1 then it will be computed using strlen.
3344 *
3345 * GtkHTML widget constructor. It creates an new GtkHTML widget and loads HTML source from @str.
3346 * It is intended for simple creation. For more complicated loading you probably want to use
3347 * #GtkHTMLStream. See #gtk_html_begin.
3348 *
3349 * Return value: A GtkHTML widget, newly created, containing document loaded from input @str.
3350 **/
3351
3352GtkWidget *
3353gtk_html_new_from_string (const gchar *str, gint len)
3354{
3355        GtkWidget *html;
3356
3357        html = g_object_new (GTK_TYPE_HTML, NULL);
3358        gtk_html_load_from_string (GTK_HTML (html), str, len);
3359
3360        return html;
3361}
3362
3363void
3364gtk_html_construct (GtkHTML *html)
3365{
3366        g_return_if_fail (html != NULL);
3367        g_return_if_fail (GTK_IS_HTML (html));
3368
3369        html->engine        = html_engine_new (GTK_WIDGET (html));
3370        html->iframe_parent = NULL;
3371       
3372        g_signal_connect (G_OBJECT (html->engine), "title_changed",
3373                          G_CALLBACK (html_engine_title_changed_cb), html);
3374        g_signal_connect (G_OBJECT (html->engine), "set_base",
3375                          G_CALLBACK (html_engine_set_base_cb), html);
3376        g_signal_connect (G_OBJECT (html->engine), "set_base_target",
3377                          G_CALLBACK (html_engine_set_base_target_cb), html);
3378        g_signal_connect (G_OBJECT (html->engine), "load_done",
3379                          G_CALLBACK (html_engine_load_done_cb), html);
3380        g_signal_connect (G_OBJECT (html->engine), "url_requested",
3381                          G_CALLBACK (html_engine_url_requested_cb), html);
3382        g_signal_connect (G_OBJECT (html->engine), "draw_pending",
3383                          G_CALLBACK (html_engine_draw_pending_cb), html);
3384        g_signal_connect (G_OBJECT (html->engine), "redirect",
3385                          G_CALLBACK (html_engine_redirect_cb), html);
3386        g_signal_connect (G_OBJECT (html->engine), "submit",
3387                          G_CALLBACK (html_engine_submit_cb), html);
3388        g_signal_connect (G_OBJECT (html->engine), "object_requested",
3389                          G_CALLBACK (html_engine_object_requested_cb), html);
3390
3391        init_properties_widget (html);
3392}
3393
3394
3395void
3396gtk_html_enable_debug (GtkHTML *html,
3397                       gboolean debug)
3398{
3399        g_return_if_fail (html != NULL);
3400        g_return_if_fail (GTK_IS_HTML (html));
3401
3402        html->debug = debug;
3403}
3404
3405
3406void
3407gtk_html_allow_selection (GtkHTML *html,
3408                          gboolean allow)
3409{
3410        g_return_if_fail (html != NULL);
3411        g_return_if_fail (GTK_IS_HTML (html));
3412
3413        html->allow_selection = allow;
3414}
3415
3416
3417/**
3418 * gtk_html_begin_full:
3419 * @html: the GtkHTML widget to operate on.
3420 * @target_frame: the string identifying the frame to load the data into
3421 * @content_type: the content_type of the data that we will be loading
3422 * @flags: the GtkHTMLBeginFlags that control the reload behavior handling
3423 *
3424 * Opens a new stream of type @content_type to the frame named @target_frame.
3425 * the flags in @flags allow control over what data is reloaded.
3426 *
3427 * Returns: a new GtkHTMLStream to specified frame
3428 */
3429GtkHTMLStream *
3430gtk_html_begin_full (GtkHTML           *html,
3431                     char              *target_frame,
3432                     char              *content_type,
3433                     GtkHTMLBeginFlags flags)
3434{
3435        GtkHTMLStream *handle;
3436       
3437        g_return_val_if_fail (!gtk_html_get_editable (html), NULL);
3438
3439        if (flags & GTK_HTML_BEGIN_BLOCK_UPDATES)
3440                gtk_html_set_blocking (html, TRUE);
3441        else
3442                gtk_html_set_blocking (html, FALSE);
3443
3444        if (flags & GTK_HTML_BEGIN_BLOCK_IMAGES)
3445                gtk_html_set_images_blocking (html, TRUE);
3446        else
3447                gtk_html_set_images_blocking (html, FALSE);
3448
3449        if (flags & GTK_HTML_BEGIN_KEEP_IMAGES)
3450                gtk_html_images_ref (html);
3451
3452        if (flags & GTK_HTML_BEGIN_KEEP_SCROLL)
3453                html->engine->keep_scroll = TRUE;
3454        else
3455                html->engine->keep_scroll = FALSE;
3456
3457        if (!content_type)
3458                content_type = html->priv->content_type;
3459
3460        handle = html_engine_begin (html->engine, content_type);
3461        if (handle == NULL)
3462                return NULL;
3463       
3464        html_engine_parse (html->engine);
3465
3466        if (flags & GTK_HTML_BEGIN_KEEP_IMAGES)
3467                gtk_html_images_unref (html);
3468
3469        if (flags & GTK_HTML_BEGIN_KEEP_SCROLL)
3470                html->engine->newPage = FALSE;
3471
3472        return handle;
3473}
3474
3475/**
3476 * gtk_html_begin:
3477 * @html: the html widget to operate on.
3478 *
3479 * Opens a new stream to load new content into the GtkHTML widget @html.
3480 *
3481 * Returns: a new GtkHTMLStream to store new content.
3482 **/
3483GtkHTMLStream *
3484gtk_html_begin (GtkHTML *html)
3485{
3486        g_return_val_if_fail (GTK_IS_HTML (html), NULL);
3487
3488        return gtk_html_begin_full (html, NULL, html->priv->content_type, 0);
3489}
3490
3491/**
3492 * gtk_html_begin_content:
3493 * @html: the html widget to operate on.
3494 * @content_type: a string listing the type of content to expect on the stream.
3495 *
3496 * Opens a new stream to load new content of type @content_type into
3497 * the GtkHTML widget given in @html.
3498 *
3499 * Returns: a new GtkHTMLStream to store new content.
3500 **/
3501GtkHTMLStream *
3502gtk_html_begin_content (GtkHTML *html, gchar *content_type)
3503{
3504        g_return_val_if_fail (! gtk_html_get_editable (html), NULL);
3505
3506        return gtk_html_begin_full (html, NULL, NULL, 0);
3507}
3508
3509/**
3510 * gtk_html_write:
3511 * @html: the GtkHTML widget the stream belongs to (unused)
3512 * @handle: the GkHTMLStream to write to.
3513 * @buffer: the data to write to the stream.
3514 * @size: the length of data to read from @buffer
3515 *
3516 * Writes @size bytes of @buffer to the stream pointed to by @stream.
3517 **/
3518void
3519gtk_html_write (GtkHTML *html,
3520                GtkHTMLStream *handle,
3521                const gchar *buffer,
3522                size_t size)
3523{
3524        gtk_html_stream_write (handle, buffer, size);
3525}
3526
3527/**
3528 * gtk_html_end:
3529 * @html: the GtkHTML widget the stream belongs to.
3530 * @handle: the GtkHTMLStream to close.
3531 * @status: the GtkHTMLStreamStatus representing the state of the stream when closed.
3532 *
3533 * Close the GtkHTMLStream represented by @stream and notify @html that is
3534 * should not expect any more content from that stream.
3535 **/
3536void
3537gtk_html_end (GtkHTML *html,
3538              GtkHTMLStream *handle,
3539              GtkHTMLStreamStatus status)
3540{
3541        gtk_html_stream_close (handle, status);
3542}
3543
3544/**
3545 * gtk_html_stop:
3546 * @html: the GtkHTML widget.
3547 *
3548 * Stop requesting any more data by url_requested signal.
3549 **/
3550void
3551gtk_html_stop (GtkHTML *html)
3552{
3553        g_return_if_fail (GTK_IS_HTML (html));
3554
3555        html_engine_stop (html->engine);
3556}
3557
3558
3559/**
3560 * gtk_html_get_title:
3561 * @html: The GtkHTML widget.
3562 *
3563 * Retrieve the title of the document currently loaded in the GtkHTML widget.
3564 *
3565 * Returns: the title of the current document
3566 **/
3567const gchar *
3568gtk_html_get_title (GtkHTML *html)
3569{
3570        g_return_val_if_fail (html != NULL, NULL);
3571        g_return_val_if_fail (GTK_IS_HTML (html), NULL);
3572
3573        if (html->engine->title == NULL)
3574                return NULL;
3575
3576        return html->engine->title->str;
3577}
3578
3579/**
3580 * gtk_html_set_title:
3581 * @html: The GtkHTML widget.
3582 *
3583 * Set the title of the document currently loaded in the GtkHTML widget.
3584 *
3585 **/
3586void
3587gtk_html_set_title (GtkHTML *html, const char *title)
3588{
3589        g_return_if_fail (html != NULL);
3590        g_return_if_fail (GTK_IS_HTML (html));
3591
3592        html_engine_set_title (html->engine, title);
3593}
3594
3595
3596/**
3597 * gtk_html_jump_to_anchor:
3598 * @html: the GtkHTML widget.
3599 * @anchor: a string containing the name of the anchor.
3600 *
3601 * Scroll the document display to show the HTML anchor listed in @anchor
3602 *
3603 * Returns: TRUE if the anchor is found, FALSE otherwise.
3604 **/
3605gboolean
3606gtk_html_jump_to_anchor (GtkHTML *html,
3607                         const gchar *anchor)
3608{
3609        g_return_val_if_fail (html != NULL, FALSE);
3610        g_return_val_if_fail (GTK_IS_HTML (html), FALSE);
3611       
3612        return html_engine_goto_anchor (html->engine, anchor);
3613}
3614
3615
3616gboolean
3617gtk_html_save (GtkHTML *html,
3618               GtkHTMLSaveReceiverFn receiver,
3619               gpointer data)
3620{
3621        g_return_val_if_fail (html != NULL, FALSE);
3622        g_return_val_if_fail (GTK_IS_HTML (html), FALSE);
3623        g_return_val_if_fail (receiver != NULL, FALSE);
3624       
3625        return html_engine_save (html->engine, receiver, data);
3626}
3627
3628/**
3629 * gtk_html_export:
3630 * @html: the GtkHTML widget
3631 * @content_type: the expected content_type
3632 * @receiver:
3633 * @user_data: pointer to maintain user state.
3634 *
3635 * Export the current document into the content type given by @content_type,
3636 * by calling the function listed in @receiver data becomes avaiable.  When @receiver is
3637 * called @user_data is passed in as the user_data parameter.
3638 *
3639 * Returns: TRUE if the export was successfull, FALSE otherwise.
3640 **/
3641gboolean
3642gtk_html_export (GtkHTML *html,
3643                 const char *content_type,
3644                 GtkHTMLSaveReceiverFn receiver,
3645                 gpointer user_data)
3646{
3647        g_return_val_if_fail (html != NULL, FALSE);
3648        g_return_val_if_fail (GTK_IS_HTML (html), FALSE);
3649        g_return_val_if_fail (receiver != NULL, FALSE);
3650       
3651        if (strcmp (content_type, "text/html") == 0) {
3652                return html_engine_save (html->engine, receiver, user_data);
3653        } else if (strcmp (content_type, "text/plain") == 0) {
3654                return html_engine_save_plain (html->engine, receiver,
3655                                               user_data); 
3656        } else {
3657                return FALSE;
3658        }
3659}
3660
3661
3662
3663static void
3664gtk_html_update_scrollbars_on_resize (GtkHTML *html,
3665                                      gdouble old_doc_width, gdouble old_doc_height,
3666                                      gdouble old_width, gdouble old_height,
3667                                      gboolean *changed_x, gboolean *changed_y)
3668{
3669        GtkLayout *layout;
3670        GtkAdjustment *vadj, *hadj;
3671        gdouble doc_width, doc_height;
3672
3673        /* printf ("update on resize\n"); */
3674
3675        layout = GTK_LAYOUT (html);
3676        hadj = layout->hadjustment;
3677        vadj = layout->vadjustment;
3678
3679        doc_height = html_engine_get_doc_height (html->engine);
3680        doc_width  = html_engine_get_doc_width (html->engine);
3681
3682        if (!html->engine->keep_scroll) {
3683                if (old_doc_width - old_width > 0) {
3684                        html->engine->x_offset = (gint) (hadj->value * (doc_width - html->engine->width)
3685                                                         / (old_doc_width - old_width));
3686
3687                        gtk_adjustment_set_value (hadj, html->engine->x_offset);
3688                }
3689
3690                if (old_doc_height - old_height > 0) {
3691                        html->engine->y_offset = (gint) (vadj->value * (doc_height - html->engine->height)
3692                                                         / (old_doc_height - old_height));
3693                        gtk_adjustment_set_value (vadj, html->engine->y_offset);
3694                }
3695        }
3696}
3697
3698void
3699gtk_html_private_calc_scrollbars (GtkHTML *html, gboolean *changed_x, gboolean *changed_y)
3700{
3701        GtkLayout *layout;
3702        GtkAdjustment *vadj, *hadj;
3703        gint width, height;
3704
3705        if (!GTK_WIDGET_REALIZED (html))
3706                return;
3707
3708        /* printf ("calc scrollbars\n"); */
3709
3710        height = html_engine_get_doc_height (html->engine);
3711        width = html_engine_get_doc_width (html->engine);
3712
3713        layout = GTK_LAYOUT (html);
3714        hadj = layout->hadjustment;
3715        vadj = layout->vadjustment;
3716
3717        vadj->page_size = html->engine->height;
3718        vadj->step_increment = 14; /* FIXME */
3719        vadj->page_increment = html->engine->height;
3720
3721        if (vadj->value > height - html->engine->height) {
3722                gtk_adjustment_set_value (vadj, height - html->engine->height);
3723                if (changed_y)
3724                        *changed_y = TRUE;
3725        }
3726
3727        hadj->page_size = html->engine->width;
3728        hadj->step_increment = 14; /* FIXME */
3729        hadj->page_increment = html->engine->width;
3730
3731        if ((width != layout->width) || (height != layout->height)) {
3732                g_signal_emit (html, signals [SIZE_CHANGED], 0);
3733                gtk_layout_set_size (layout, width, height);
3734        }
3735
3736        if (hadj->value > width - html->engine->width || hadj->value > MAX_WIDGET_WIDTH - html->engine->width) {
3737                gtk_adjustment_set_value (hadj, MIN (width - html->engine->width, MAX_WIDGET_WIDTH - html->engine->width));
3738                if (changed_x)
3739                        *changed_x = TRUE;
3740        }
3741
3742}
3743
3744
3745
3746#ifdef USE_PROPS
3747static void
3748gtk_html_set_property (GObject        *object,
3749                       guint           prop_id,
3750                       const GValue   *value,
3751                       GParamSpec     *pspec)
3752{
3753        GtkHTML *html = GTK_HTML (object);
3754
3755        switch (prop_id) {
3756        case PROP_EDITABLE:
3757                gtk_html_set_editable (html, g_value_get_boolean (value));
3758                break;
3759        case PROP_TITLE:
3760                gtk_html_set_title (html, g_value_get_string (value));
3761                break;
3762        case PROP_DOCUMENT_BASE:
3763                gtk_html_set_base (html, g_value_get_string (value));
3764                break;
3765        case PROP_TARGET_BASE:
3766                /* This doesn't do anything yet */
3767                break;
3768        default:
3769                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
3770                break;
3771        }
3772}
3773
3774static void
3775gtk_html_get_property (GObject    *object,
3776                       guint       prop_id,
3777                       GValue     *value,
3778                       GParamSpec *pspec)
3779{
3780        GtkHTML *html = GTK_HTML (object);
3781                       
3782        switch (prop_id) {
3783        case PROP_EDITABLE:
3784                g_value_set_boolean (value, gtk_html_get_editable (html));
3785                break;
3786        case PROP_TITLE:
3787                g_value_set_static_string (value, gtk_html_get_title (html));
3788                break;
3789        case PROP_DOCUMENT_BASE:
3790                g_value_set_static_string (value, gtk_html_get_base (html));
3791                break;
3792        case PROP_TARGET_BASE:
3793                g_value_set_static_string (value, gtk_html_get_base (html));
3794                break;
3795        default:
3796                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
3797                break;
3798        }
3799}
3800#endif
3801
3802void
3803gtk_html_set_editable (GtkHTML *html,
3804                       gboolean editable)
3805{
3806        g_return_if_fail (html != NULL);
3807        g_return_if_fail (GTK_IS_HTML (html));
3808
3809        html_engine_set_editable (html->engine, editable);
3810
3811        if (editable)
3812                gtk_html_update_styles (html);
3813}
3814
3815gboolean
3816gtk_html_get_editable  (const GtkHTML *html)
3817{
3818        g_return_val_if_fail (html != NULL, FALSE);
3819        g_return_val_if_fail (GTK_IS_HTML (html), FALSE);
3820
3821        return html_engine_get_editable (html->engine);
3822}
3823
3824void
3825gtk_html_set_inline_spelling (GtkHTML *html,
3826                              gboolean inline_spell)
3827{
3828        g_return_if_fail (html != NULL);
3829        g_return_if_fail (GTK_IS_HTML (html));
3830
3831        html->priv->inline_spelling = inline_spell;
3832
3833        if (gtk_html_get_editable (html) && html->priv->inline_spelling)
3834                html_engine_spell_check (html->engine);
3835        else
3836                html_engine_clear_spell_check (html->engine);
3837}       
3838
3839gboolean
3840gtk_html_get_inline_spelling (const GtkHTML *html)
3841{
3842        g_return_val_if_fail (html != NULL, FALSE);
3843        g_return_val_if_fail (GTK_IS_HTML (html), FALSE);
3844
3845        return html->priv->inline_spelling;
3846}
3847
3848void
3849gtk_html_set_magic_links (GtkHTML *html,
3850                          gboolean links)
3851{
3852        g_return_if_fail (html != NULL);
3853        g_return_if_fail (GTK_IS_HTML (html));
3854
3855        html->priv->magic_links = links;
3856}
3857
3858gboolean
3859gtk_html_get_magic_links (const GtkHTML *html)
3860{
3861        g_return_val_if_fail (html != NULL, FALSE);
3862        g_return_val_if_fail (GTK_IS_HTML (html), FALSE);
3863
3864        return  html->priv->magic_links;
3865}
3866
3867void
3868gtk_html_set_magic_smileys (GtkHTML *html,
3869                            gboolean smile)
3870{
3871        g_return_if_fail (html != NULL);
3872        g_return_if_fail (GTK_IS_HTML (html));
3873
3874        html->priv->magic_smileys = smile;
3875}
3876
3877gboolean
3878gtk_html_get_magic_smileys (const GtkHTML *html)
3879{
3880        g_return_val_if_fail (html != NULL, FALSE);
3881        g_return_val_if_fail (GTK_IS_HTML (html), FALSE);
3882
3883        return  html->priv->magic_smileys;
3884}
3885
3886static void
3887frame_set_animate (HTMLObject *o, HTMLEngine *e, gpointer data)
3888{
3889        if (HTML_IS_FRAME (o)) {
3890                html_image_factory_set_animate (GTK_HTML (HTML_FRAME (o)->html)->engine->image_factory,
3891                                                *(gboolean *)data);
3892        } else if (HTML_IS_IFRAME (o)) {
3893                html_image_factory_set_animate (GTK_HTML (HTML_IFRAME (o)->html)->engine->image_factory,
3894                                                *(gboolean *)data);
3895        }
3896}
3897
3898void
3899gtk_html_set_caret_mode(GtkHTML * html, gboolean caret_mode)
3900{
3901        g_return_if_fail (GTK_IS_HTML (html));
3902        g_return_if_fail (HTML_IS_ENGINE (html->engine));
3903
3904        set_caret_mode(html->engine, caret_mode);
3905}
3906
3907gboolean
3908gtk_html_get_caret_mode(const GtkHTML *html)
3909{
3910        g_return_val_if_fail (GTK_IS_HTML (html), FALSE);
3911        g_return_val_if_fail (HTML_IS_ENGINE (html->engine), FALSE);
3912
3913        return html->engine->caret_mode;
3914}
3915
3916void
3917gtk_html_set_animate (GtkHTML *html, gboolean animate)
3918{
3919        g_return_if_fail (GTK_IS_HTML (html));
3920        g_return_if_fail (HTML_IS_ENGINE (html->engine));
3921
3922        html_image_factory_set_animate (html->engine->image_factory, animate);
3923        if (html->engine->clue)
3924                html_object_forall (html->engine->clue, html->engine, frame_set_animate, &animate);
3925}
3926
3927gboolean
3928gtk_html_get_animate (const GtkHTML *html)
3929{
3930        g_return_val_if_fail (GTK_IS_HTML (html), FALSE);
3931        g_return_val_if_fail (HTML_IS_ENGINE (html->engine), FALSE);
3932
3933        return html_image_factory_get_animate (html->engine->image_factory);
3934}
3935
3936void
3937gtk_html_load_empty (GtkHTML *html)
3938{
3939        g_return_if_fail (html != NULL);
3940        g_return_if_fail (GTK_IS_HTML (html));
3941
3942        html_engine_load_empty (html->engine);
3943}
3944
3945void
3946gtk_html_load_from_string  (GtkHTML *html, const gchar *str, gint len)
3947{
3948        GtkHTMLStream *stream;
3949
3950        stream = gtk_html_begin_content (html, "text/html; charset=utf-8");
3951        gtk_html_stream_write (stream, str, (len == -1) ? strlen (str) : len);
3952        gtk_html_stream_close (stream, GTK_HTML_STREAM_OK);
3953}
3954
3955void
3956gtk_html_set_base (GtkHTML *html, const char *url)
3957{
3958        g_return_if_fail (GTK_IS_HTML (html));
3959       
3960        g_free (html->priv->base_url);
3961        html->priv->base_url = g_strdup (url);
3962}
3963
3964const char *
3965gtk_html_get_base (GtkHTML *html)
3966{
3967        g_return_val_if_fail (GTK_IS_HTML (html), NULL);
3968       
3969        return html->priv->base_url;
3970}
3971
3972
3973/* Printing.  */
3974
3975void
3976gtk_html_print (GtkHTML *html,
3977                GnomePrintContext *print_context)
3978{
3979        g_return_if_fail (html != NULL);
3980        g_return_if_fail (GTK_IS_HTML (html));
3981
3982        html_engine_print (html->engine, print_context);
3983}
3984
3985void
3986gtk_html_print_with_header_footer (GtkHTML *html, GnomePrintContext *print_context,
3987                                   gdouble header_height, gdouble footer_height,
3988                                   GtkHTMLPrintCallback header_print, GtkHTMLPrintCallback footer_print, gpointer user_data)
3989{
3990        g_return_if_fail (html != NULL);
3991        g_return_if_fail (GTK_IS_HTML (html));
3992
3993        html_engine_print_with_header_footer (html->engine, print_context,
3994                                              header_height, footer_height, header_print, footer_print, user_data);
3995}
3996
3997
3998/* Editing.  */
3999
4000void
4001gtk_html_set_paragraph_style (GtkHTML *html,
4002                              GtkHTMLParagraphStyle style)
4003{
4004        HTMLClueFlowStyle current_style;
4005        HTMLClueFlowStyle clueflow_style;
4006        HTMLListType item_type;
4007        HTMLListType cur_item_type;
4008
4009        g_return_if_fail (html != NULL);
4010        g_return_if_fail (GTK_IS_HTML (html));
4011
4012        /* FIXME precondition: check if it's a valid style.  */
4013
4014        paragraph_style_to_clueflow_style (style, &clueflow_style, &item_type);
4015
4016        html_engine_get_current_clueflow_style (html->engine, &current_style, &cur_item_type);
4017        if (!html_engine_is_selection_active (html->engine) && current_style == clueflow_style
4018            && (current_style != HTML_CLUEFLOW_STYLE_LIST_ITEM || item_type == cur_item_type))
4019                return;
4020
4021        if (! html_engine_set_clueflow_style (html->engine, clueflow_style, item_type, 0, 0, NULL,
4022                                              HTML_ENGINE_SET_CLUEFLOW_STYLE, HTML_UNDO_UNDO, TRUE))
4023                return;
4024
4025        html->priv->paragraph_style = style;
4026
4027        g_signal_emit (html, signals[CURRENT_PARAGRAPH_STYLE_CHANGED], 0, style);
4028        queue_draw (html);
4029}
4030
4031GtkHTMLParagraphStyle
4032gtk_html_get_paragraph_style (GtkHTML *html)
4033{
4034        HTMLClueFlowStyle style;
4035        HTMLListType item_type;
4036
4037        html_engine_get_current_clueflow_style (html->engine, &style, &item_type);
4038
4039        return clueflow_style_to_paragraph_style (style, item_type);
4040}
4041
4042guint
4043gtk_html_get_paragraph_indentation (GtkHTML *html)
4044{
4045        return html_engine_get_current_clueflow_indentation (html->engine);
4046}
4047
4048void
4049gtk_html_set_indent (GtkHTML *html,
4050                     GByteArray *levels)
4051{
4052        g_return_if_fail (html != NULL);
4053        g_return_if_fail (GTK_IS_HTML (html));
4054
4055        html_engine_set_clueflow_style (html->engine, 0, 0, 0,
4056                                        levels ? levels->len : 0,
4057                                        levels ? levels->data : NULL,
4058                                        HTML_ENGINE_SET_CLUEFLOW_INDENTATION, HTML_UNDO_UNDO, TRUE);
4059
4060        gtk_html_update_styles (html);
4061}
4062
4063static void
4064gtk_html_modify_indent_by_delta (GtkHTML *html,
4065                                 gint delta, guint8 *levels)
4066{
4067        g_return_if_fail (html != NULL);
4068        g_return_if_fail (GTK_IS_HTML (html));
4069
4070        html_engine_set_clueflow_style (html->engine, 0, 0, 0, delta, levels,
4071                                        HTML_ENGINE_SET_CLUEFLOW_INDENTATION_DELTA, HTML_UNDO_UNDO, TRUE);
4072
4073        gtk_html_update_styles (html);
4074}
4075
4076void
4077gtk_html_indent_push_level (GtkHTML *html, HTMLListType level_type)
4078{
4079        guint8 type = (guint8)level_type;
4080        gtk_html_modify_indent_by_delta (html, +1, &type);
4081}
4082
4083void
4084gtk_html_indent_pop_level (GtkHTML *html)
4085{
4086        gtk_html_modify_indent_by_delta (html, -1, NULL);
4087}
4088
4089void
4090gtk_html_set_font_style (GtkHTML *html,
4091                         GtkHTMLFontStyle and_mask,
4092                         GtkHTMLFontStyle or_mask)
4093{
4094        g_return_if_fail (html != NULL);
4095        g_return_if_fail (GTK_IS_HTML (html));
4096
4097        if (html_engine_set_font_style (html->engine, and_mask, or_mask))
4098                g_signal_emit (html, signals [INSERTION_FONT_STYLE_CHANGED], 0, html->engine->insertion_font_style);
4099}
4100
4101void
4102gtk_html_set_color (GtkHTML *html, HTMLColor *color)
4103{
4104        g_return_if_fail (html != NULL);
4105        g_return_if_fail (GTK_IS_HTML (html));
4106
4107        if (html_engine_set_color (html->engine, color))
4108                g_signal_emit (html, signals [INSERTION_COLOR_CHANGED], 0, html->engine->insertion_font_style);
4109}
4110
4111void
4112gtk_html_toggle_font_style (GtkHTML *html,
4113                            GtkHTMLFontStyle style)
4114{
4115        g_return_if_fail (html != NULL);
4116        g_return_if_fail (GTK_IS_HTML (html));
4117
4118        if (html_engine_toggle_font_style (html->engine, style))
4119                g_signal_emit (html, signals [INSERTION_FONT_STYLE_CHANGED], 0, html->engine->insertion_font_style);
4120}
4121
4122GtkHTMLParagraphAlignment
4123gtk_html_get_paragraph_alignment (GtkHTML *html)
4124{
4125        return paragraph_alignment_to_html (html_engine_get_current_clueflow_alignment (html->engine));
4126}
4127
4128void
4129gtk_html_set_paragraph_alignment (GtkHTML *html,
4130                                  GtkHTMLParagraphAlignment alignment)
4131{
4132        HTMLHAlignType align;
4133
4134        g_return_if_fail (html != NULL);
4135        g_return_if_fail (GTK_IS_HTML (html));
4136
4137        align = paragraph_alignment_to_html (alignment);
4138
4139        if (html_engine_set_clueflow_style (html->engine, 0, 0, align, 0, NULL,
4140                                            HTML_ENGINE_SET_CLUEFLOW_ALIGNMENT, HTML_UNDO_UNDO, TRUE)) {
4141                html->priv->paragraph_alignment = alignment;
4142                g_signal_emit (html,  signals [CURRENT_PARAGRAPH_ALIGNMENT_CHANGED], 0, alignment);
4143        }
4144}
4145
4146
4147/* Clipboard operations.  */
4148
4149void
4150gtk_html_cut (GtkHTML *html)
4151{
4152        g_return_if_fail (html != NULL);
4153        g_return_if_fail (GTK_IS_HTML (html));
4154
4155        html_engine_cut (html->engine);
4156        gtk_selection_owner_set (GTK_WIDGET (html), gdk_atom_intern ("CLIPBOARD", FALSE), gtk_get_current_event_time ());
4157}
4158
4159void
4160gtk_html_copy (GtkHTML *html)
4161{
4162        g_return_if_fail (html != NULL);
4163        g_return_if_fail (GTK_IS_HTML (html));
4164
4165        html_engine_copy (html->engine);
4166        gtk_selection_owner_set (GTK_WIDGET (html), gdk_atom_intern ("CLIPBOARD", FALSE), gtk_get_current_event_time ());
4167}
4168
4169void
4170gtk_html_paste (GtkHTML *html, gboolean as_cite)
4171{
4172        g_return_if_fail (html != NULL);
4173        g_return_if_fail (GTK_IS_HTML (html));
4174
4175        gtk_html_request_paste (html, gdk_atom_intern ("CLIPBOARD", FALSE), 0,
4176                                gtk_get_current_event_time (), as_cite);
4177}
4178
4179
4180/* Undo/redo.  */
4181
4182void
4183gtk_html_undo (GtkHTML *html)
4184{
4185        g_return_if_fail (html != NULL);
4186        g_return_if_fail (GTK_IS_HTML (html));
4187
4188        html_engine_undo (html->engine);
4189        gtk_html_update_styles (html);
4190}
4191
4192void
4193gtk_html_redo (GtkHTML *html)
4194{
4195        g_return_if_fail (html != NULL);
4196        g_return_if_fail (GTK_IS_HTML (html));
4197
4198        html_engine_redo (html->engine);
4199        gtk_html_update_styles (html);
4200}
4201
4202/* misc utils */
4203void
4204gtk_html_set_default_content_type (GtkHTML *html, gchar *content_type)
4205{
4206        g_free (html->priv->content_type);     
4207
4208        if (content_type) {
4209                html->priv->content_type = g_ascii_strdown (content_type, -1);
4210        } else
4211                html->priv->content_type = NULL;
4212}
4213
4214gpointer
4215gtk_html_get_object_by_id (GtkHTML *html, const gchar *id)
4216{
4217        g_return_val_if_fail (html, NULL);
4218        g_return_val_if_fail (id, NULL);
4219        g_return_val_if_fail (GTK_IS_HTML (html), NULL);
4220        g_return_val_if_fail (html->engine, NULL);
4221
4222        return html_engine_get_object_by_id (html->engine, id);
4223}
4224
4225/*******************************************
4226
4227   keybindings
4228
4229*/
4230
4231static gint
4232get_line_height (GtkHTML *html)
4233{
4234        gint line_offset = 0, w, a, d;
4235
4236        if (!html->engine || !html->engine->painter)
4237                return 0;
4238
4239        html_painter_calc_text_size (html->engine->painter, "a", 1, NULL, NULL, NULL, 0, &line_offset, GTK_HTML_FONT_STYLE_SIZE_3, NULL, &w, &a, &d);
4240
4241        return a + d;
4242}
4243
4244static void
4245scroll (GtkHTML *html,
4246        GtkOrientation orientation,
4247        GtkScrollType  scroll_type,
4248        gfloat         position)
4249{
4250        GtkAdjustment *adj;
4251        gint line_height;
4252        gfloat delta;
4253
4254        /* we dont want scroll in editable (move cursor instead) */
4255        if (html_engine_get_editable (html->engine))
4256                return;
4257
4258        adj = (orientation == GTK_ORIENTATION_VERTICAL)
4259                ? gtk_layout_get_vadjustment (GTK_LAYOUT (html)) : gtk_layout_get_hadjustment (GTK_LAYOUT (html));
4260
4261
4262        line_height = (html->engine && adj->page_increment > (3 * get_line_height (html)))
4263                ? get_line_height (html)
4264                : 0;
4265
4266        switch (scroll_type) {
4267        case GTK_SCROLL_STEP_FORWARD:
4268                delta = adj->step_increment;
4269                break;
4270        case GTK_SCROLL_STEP_BACKWARD:
4271                delta = -adj->step_increment;
4272                break;
4273        case GTK_SCROLL_PAGE_FORWARD:
4274                delta = adj->page_increment - line_height;
4275                break;
4276        case GTK_SCROLL_PAGE_BACKWARD:
4277                delta = -adj->page_increment + line_height;
4278                break;
4279        default:
4280                g_warning ("invalid scroll parameters: %d %d %f\n", orientation, scroll_type, position);
4281                delta = 0.0;
4282                return;
4283        }
4284
4285        gtk_adjustment_set_value (adj, CLAMP (adj->value + delta, adj->lower, MAX (0.0, adj->upper - adj->page_size)));
4286
4287        html->binding_handled = TRUE;
4288}
4289
4290static void
4291scroll_by_amount (GtkHTML *html, gint amount)
4292{
4293        GtkAdjustment *adj;
4294
4295        adj = GTK_LAYOUT (html)->vadjustment;
4296        gtk_adjustment_set_value (adj,
4297                                  CLAMP (adj->value + (gfloat) amount, adj->lower, MAX (0.0, adj->upper - adj->page_size)));
4298}
4299
4300static void
4301cursor_move (GtkHTML *html, GtkDirectionType dir_type, GtkHTMLCursorSkipType skip)
4302{
4303        gint amount;
4304
4305        if (!html->engine->caret_mode && !html_engine_get_editable (html->engine))
4306                return;
4307
4308        if (html->engine->selection_mode) {
4309                if (!html->engine->mark)
4310                        html_engine_set_mark (html->engine);
4311        } else if (html->engine->shift_selection || html->engine->mark) {
4312                html_engine_disable_selection (html->engine);
4313                html_engine_edit_selection_updater_schedule (html->engine->selection_updater);
4314                html->engine->shift_selection = FALSE;
4315        }
4316        switch (skip) {
4317        case GTK_HTML_CURSOR_SKIP_ONE:
4318                switch (dir_type) {
4319                case GTK_DIR_LEFT:
4320                        html_engine_move_cursor (html->engine, HTML_ENGINE_CURSOR_LEFT, 1);
4321                        break;
4322                case GTK_DIR_RIGHT:
4323                        html_engine_move_cursor (html->engine, HTML_ENGINE_CURSOR_RIGHT, 1);
4324                        break;
4325                case GTK_DIR_UP:
4326                        html_engine_move_cursor (html->engine, HTML_ENGINE_CURSOR_UP, 1);
4327                        break;
4328                case GTK_DIR_DOWN:
4329                        html_engine_move_cursor (html->engine, HTML_ENGINE_CURSOR_DOWN, 1);
4330                        break;
4331                default:
4332                        g_warning ("invalid cursor_move parameters\n");
4333                }
4334                break;
4335        case GTK_HTML_CURSOR_SKIP_WORD:
4336                switch (dir_type) {
4337                case GTK_DIR_UP:
4338                case GTK_DIR_LEFT:
4339                        html_engine_backward_word (html->engine);
4340                        break;
4341                case GTK_DIR_DOWN:
4342                case GTK_DIR_RIGHT:
4343                        html_engine_forward_word (html->engine);
4344                        break;
4345                default:
4346                        g_warning ("invalid cursor_move parameters\n");
4347                }
4348                break;
4349        case GTK_HTML_CURSOR_SKIP_PAGE: {
4350                gint line_height;
4351
4352                line_height =  GTK_WIDGET (html)->allocation.height > (3 * get_line_height (html))
4353                        ? get_line_height (html) : 0;
4354
4355
4356                switch (dir_type) {
4357                case GTK_DIR_UP:
4358                case GTK_DIR_LEFT:
4359                        if ((amount = html_engine_scroll_up (html->engine,
4360                                                             GTK_WIDGET (html)->allocation.height - line_height)) > 0)
4361                                scroll_by_amount (html, - amount);
4362                        break;
4363                case GTK_DIR_DOWN:
4364                case GTK_DIR_RIGHT:
4365                        if ((amount = html_engine_scroll_down (html->engine,
4366                                                               GTK_WIDGET (html)->allocation.height - line_height)) > 0)
4367                                scroll_by_amount (html, amount);
4368                        break;
4369                default:
4370                        g_warning ("invalid cursor_move parameters\n");
4371                }
4372                break;
4373        }
4374        case GTK_HTML_CURSOR_SKIP_ALL:
4375                switch (dir_type) {
4376                case GTK_DIR_LEFT:
4377                        html_engine_beginning_of_line (html->engine);
4378                        break;
4379                case GTK_DIR_RIGHT:
4380                        html_engine_end_of_line (html->engine);
4381                        break;
4382                case GTK_DIR_UP:
4383                        html_engine_beginning_of_document (html->engine);
4384                        break;
4385                case GTK_DIR_DOWN:
4386                        html_engine_end_of_document (html->engine);
4387                        break;
4388                default:
4389                        g_warning ("invalid cursor_move parameters\n");
4390                }
4391                break;
4392        default:
4393                g_warning ("invalid cursor_move parameters\n");
4394        }
4395
4396        html->binding_handled = TRUE;
4397        html->priv->update_styles = TRUE;
4398        gtk_html_edit_make_cursor_visible (html);
4399        html_engine_update_selection_active_state (html->engine, html->priv->event_time);
4400}
4401
4402static gboolean
4403move_selection (GtkHTML *html, GtkHTMLCommandType com_type)
4404{
4405        gboolean rv;
4406        gint amount;
4407
4408        if (!html_engine_get_editable (html->engine) && !html->engine->caret_mode)
4409                return FALSE;
4410
4411        html->engine->shift_selection = TRUE;
4412        if (!html->engine->mark)
4413                html_engine_set_mark (html->engine);
4414        switch (com_type) {
4415        case GTK_HTML_COMMAND_MODIFY_SELECTION_UP:
4416                rv = html_engine_move_cursor (html->engine, HTML_ENGINE_CURSOR_UP, 1) > 0 ? TRUE : FALSE;
4417                break;
4418        case GTK_HTML_COMMAND_MODIFY_SELECTION_DOWN:
4419                rv = html_engine_move_cursor (html->engine, HTML_ENGINE_CURSOR_DOWN, 1) > 0 ? TRUE : FALSE;
4420                break;
4421        case GTK_HTML_COMMAND_MODIFY_SELECTION_LEFT:
4422                rv = html_engine_move_cursor (html->engine, HTML_ENGINE_CURSOR_LEFT, 1) > 0 ? TRUE : FALSE;
4423                break;
4424        case GTK_HTML_COMMAND_MODIFY_SELECTION_RIGHT:
4425                rv = html_engine_move_cursor (html->engine, HTML_ENGINE_CURSOR_RIGHT, 1) > 0 ? TRUE : FALSE;
4426                break;
4427        case GTK_HTML_COMMAND_MODIFY_SELECTION_BOL:
4428                rv = html_engine_beginning_of_line (html->engine);
4429                break;
4430        case GTK_HTML_COMMAND_MODIFY_SELECTION_EOL:
4431                rv = html_engine_end_of_line (html->engine);
4432                break;
4433        case GTK_HTML_COMMAND_MODIFY_SELECTION_BOD:
4434                html_engine_beginning_of_document (html->engine);
4435                rv = TRUE;
4436                break;
4437        case GTK_HTML_COMMAND_MODIFY_SELECTION_EOD:
4438                html_engine_end_of_document (html->engine);
4439                rv = TRUE;
4440                break;
4441        case GTK_HTML_COMMAND_MODIFY_SELECTION_PREV_WORD:
4442                rv = html_engine_backward_word (html->engine);
4443                break;
4444        case GTK_HTML_COMMAND_MODIFY_SELECTION_NEXT_WORD:
4445                rv = html_engine_forward_word (html->engine);
4446                break;
4447        case GTK_HTML_COMMAND_MODIFY_SELECTION_PAGEUP:
4448                if ((amount = html_engine_scroll_up (html->engine, GTK_WIDGET (html)->allocation.height)) > 0) {
4449                        scroll_by_amount (html, - amount);
4450                        rv = TRUE;
4451                } else
4452                        rv = FALSE;
4453                break;
4454        case GTK_HTML_COMMAND_MODIFY_SELECTION_PAGEDOWN:
4455                if ((amount = html_engine_scroll_down (html->engine, GTK_WIDGET (html)->allocation.height)) > 0) {
4456                        scroll_by_amount (html, amount);
4457                        rv = TRUE;
4458                } else
4459                        rv = FALSE;
4460                break;
4461        default:
4462                g_warning ("invalid move_selection parameters\n");
4463                rv = FALSE;
4464        }
4465
4466        html->binding_handled = TRUE;
4467        html->priv->update_styles = TRUE;
4468
4469        html_engine_update_selection_active_state (html->engine, html->priv->event_time);
4470
4471        return rv;
4472}
4473
4474inline static void
4475delete_one (HTMLEngine *e, gboolean forward)
4476{
4477        if (e->cursor->object && html_object_is_container (e->cursor->object)
4478            && ((forward && !e->cursor->offset) || (!forward && e->cursor->offset)))
4479                html_engine_delete_container (e);
4480        else
4481                html_engine_delete_n (e, 1, forward);
4482}
4483
4484inline static gboolean
4485insert_tab_or_next_cell (GtkHTML *html)
4486{
4487        HTMLEngine *e = html->engine;
4488        if (!html_engine_next_cell (e, TRUE)) {
4489                if (html_clueflow_tabs (HTML_CLUEFLOW (e->cursor->object->parent), e->painter))
4490                        html_engine_paste_text (e, "\t", 1);
4491                else
4492                        html_engine_paste_text (e, "\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0", 4);
4493                return TRUE;
4494        }
4495
4496        return TRUE;
4497}
4498
4499inline static void
4500indent_more_or_next_cell (GtkHTML *html)
4501{
4502        if (!html_engine_next_cell (html->engine, TRUE))
4503                gtk_html_indent_push_level (html, HTML_LIST_TYPE_BLOCKQUOTE);
4504}
4505
4506static gboolean
4507command (GtkHTML *html, GtkHTMLCommandType com_type)
4508{
4509        HTMLEngine *e = html->engine;
4510        gboolean rv = TRUE;
4511
4512        /* printf ("command %d %s\n", com_type, get_value_nick (com_type)); */
4513        html->binding_handled = TRUE;
4514
4515        /* non-editable + editable commands */
4516        switch (com_type) {
4517        case GTK_HTML_COMMAND_ZOOM_IN:
4518                gtk_html_zoom_in (html);
4519                break;
4520        case GTK_HTML_COMMAND_ZOOM_OUT:
4521                gtk_html_zoom_out (html);
4522                break;
4523        case GTK_HTML_COMMAND_ZOOM_RESET:
4524                gtk_html_zoom_reset (html);
4525                break;
4526        case GTK_HTML_COMMAND_SEARCH_INCREMENTAL_FORWARD:
4527                gtk_html_isearch (html, TRUE);
4528                break;
4529        case GTK_HTML_COMMAND_SEARCH_INCREMENTAL_BACKWARD:
4530                gtk_html_isearch (html, FALSE);
4531                break;
4532        case GTK_HTML_COMMAND_FOCUS_FORWARD:
4533                if (!html_engine_get_editable (e))
4534                        html->binding_handled = gtk_widget_child_focus (GTK_WIDGET (html), GTK_DIR_TAB_FORWARD);
4535                break;
4536        case GTK_HTML_COMMAND_FOCUS_BACKWARD:
4537                if (!html_engine_get_editable (e))
4538                        html->binding_handled = gtk_widget_child_focus (GTK_WIDGET (html), GTK_DIR_TAB_BACKWARD);
4539                break;
4540        case GTK_HTML_COMMAND_SCROLL_BOD:
4541                if (!html_engine_get_editable (e))
4542                        gtk_adjustment_set_value (gtk_layout_get_vadjustment (GTK_LAYOUT (html)), 0);
4543                break;
4544        case GTK_HTML_COMMAND_SCROLL_EOD:
4545                if (!html_engine_get_editable (e)) {
4546                        GtkAdjustment *vadj = gtk_layout_get_vadjustment (GTK_LAYOUT (html));
4547                        gtk_adjustment_set_value (vadj, vadj->upper - vadj->page_size);
4548                }
4549                break;
4550        case GTK_HTML_COMMAND_COPY:
4551                gtk_html_copy (html);
4552                break;
4553
4554        case GTK_HTML_COMMAND_MODIFY_SELECTION_UP:
4555        case GTK_HTML_COMMAND_MODIFY_SELECTION_DOWN:
4556        case GTK_HTML_COMMAND_MODIFY_SELECTION_LEFT:
4557        case GTK_HTML_COMMAND_MODIFY_SELECTION_RIGHT:
4558        case GTK_HTML_COMMAND_MODIFY_SELECTION_BOL:
4559        case GTK_HTML_COMMAND_MODIFY_SELECTION_EOL:
4560        case GTK_HTML_COMMAND_MODIFY_SELECTION_BOD:
4561        case GTK_HTML_COMMAND_MODIFY_SELECTION_EOD:
4562        case GTK_HTML_COMMAND_MODIFY_SELECTION_PAGEUP:
4563        case GTK_HTML_COMMAND_MODIFY_SELECTION_PAGEDOWN:
4564        case GTK_HTML_COMMAND_MODIFY_SELECTION_PREV_WORD:
4565        case GTK_HTML_COMMAND_MODIFY_SELECTION_NEXT_WORD:
4566                if (html->engine->caret_mode || html_engine_get_editable(e))
4567                        rv = move_selection (html, com_type);
4568                break;
4569        case GTK_HTML_COMMAND_EDITABLE_ON:
4570                gtk_html_set_editable (html, TRUE);
4571                break;
4572        case GTK_HTML_COMMAND_EDITABLE_OFF:
4573                gtk_html_set_editable (html, FALSE);
4574                break;
4575
4576        default:
4577                html->binding_handled = FALSE;
4578        }
4579
4580        if (!html_engine_get_editable (e) || html->binding_handled)
4581                return rv;
4582
4583        html->binding_handled = TRUE;
4584        html->priv->update_styles = FALSE;
4585
4586        /* editable commands only */
4587        switch (com_type) {
4588        case GTK_HTML_COMMAND_UNDO:
4589                gtk_html_undo (html);
4590                break;
4591        case GTK_HTML_COMMAND_REDO:
4592                gtk_html_redo (html);
4593                break;
4594        case GTK_HTML_COMMAND_COPY_AND_DISABLE_SELECTION:
4595                gtk_html_copy (html);
4596                html_engine_disable_selection (e);
4597                html_engine_edit_selection_updater_schedule (e->selection_updater);
4598                e->selection_mode = FALSE;
4599                break;
4600        case GTK_HTML_COMMAND_CUT:
4601                gtk_html_cut (html);
4602                html->priv->update_styles = TRUE;
4603                break;
4604        case GTK_HTML_COMMAND_CUT_LINE:
4605                html_engine_cut_line (e);
4606                html->priv->update_styles = TRUE;
4607                break;
4608        case GTK_HTML_COMMAND_PASTE:
4609                gtk_html_paste (html, FALSE);
4610                html->priv->update_styles = TRUE;
4611                break;
4612        case GTK_HTML_COMMAND_INSERT_RULE:
4613                html_engine_insert_rule (e, 0, 100, 2, TRUE, HTML_HALIGN_LEFT);
4614                break;
4615        case GTK_HTML_COMMAND_INSERT_PARAGRAPH:
4616                html_engine_delete (e);
4617
4618                /* stop inserting links after newlines */
4619                html_engine_set_insertion_link (e, NULL, NULL);
4620
4621                html_engine_insert_empty_paragraph (e);
4622                html->priv->update_styles = TRUE;
4623                break;
4624        case GTK_HTML_COMMAND_DELETE:
4625                if (e->mark != NULL
4626                    && e->mark->position != e->cursor->position)
4627                        html_engine_delete (e);
4628                else
4629                        delete_one (e, TRUE);
4630                html->priv->update_styles = TRUE;
4631                break;
4632        case GTK_HTML_COMMAND_DELETE_BACK:
4633                if (html_engine_is_selection_active (e))
4634                        html_engine_delete (e);
4635                else
4636                        delete_one (e, FALSE);
4637                html->priv->update_styles = TRUE;
4638                break;
4639        case GTK_HTML_COMMAND_DELETE_BACK_OR_INDENT_DEC:
4640                if (html_engine_is_selection_active (e))
4641                        html_engine_delete (e);
4642                else if (html_engine_cursor_on_bop (e) && html_engine_get_indent (e) > 0
4643                         && e->cursor->object->parent && HTML_IS_CLUEFLOW (e->cursor->object->parent)
4644                         && HTML_CLUEFLOW (e->cursor->object->parent)->style != HTML_CLUEFLOW_STYLE_LIST_ITEM)
4645                        gtk_html_indent_pop_level (html);
4646                else
4647                        delete_one (e, FALSE);
4648                html->priv->update_styles = TRUE;
4649                break;
4650        case GTK_HTML_COMMAND_DELETE_TABLE:
4651                html_engine_delete_table (e);
4652                html->priv->update_styles = TRUE;
4653                break;
4654        case GTK_HTML_COMMAND_DELETE_TABLE_ROW:
4655                html_engine_delete_table_row (e);
4656                html->priv->update_styles = TRUE;
4657                break;
4658        case GTK_HTML_COMMAND_DELETE_TABLE_COLUMN:
4659                html_engine_delete_table_column (e);
4660                html->priv->update_styles = TRUE;
4661                break;
4662        case GTK_HTML_COMMAND_DELETE_TABLE_CELL_CONTENTS:
4663                html_engine_delete_table_cell_contents (e);
4664                html->priv->update_styles = TRUE;
4665                break;
4666        case GTK_HTML_COMMAND_SELECTION_MODE:
4667                e->selection_mode = TRUE;
4668                break;
4669        case GTK_HTML_COMMAND_DISABLE_SELECTION:
4670                html_engine_disable_selection (e);
4671                html_engine_edit_selection_updater_schedule (e->selection_updater);
4672                e->selection_mode = FALSE;
4673                break;
4674        case GTK_HTML_COMMAND_BOLD_ON:
4675                gtk_html_set_font_style (html, GTK_HTML_FONT_STYLE_MAX, GTK_HTML_FONT_STYLE_BOLD);
4676                break;
4677        case GTK_HTML_COMMAND_BOLD_OFF:
4678                gtk_html_set_font_style (html, ~GTK_HTML_FONT_STYLE_BOLD, 0);
4679                break;
4680        case GTK_HTML_COMMAND_BOLD_TOGGLE:
4681                gtk_html_toggle_font_style (html, GTK_HTML_FONT_STYLE_BOLD);
4682                break;
4683        case GTK_HTML_COMMAND_ITALIC_ON:
4684                gtk_html_set_font_style (html, GTK_HTML_FONT_STYLE_MAX, GTK_HTML_FONT_STYLE_ITALIC);
4685                break;
4686        case GTK_HTML_COMMAND_ITALIC_OFF:
4687                gtk_html_set_font_style (html, ~GTK_HTML_FONT_STYLE_ITALIC, 0);
4688                break;
4689        case GTK_HTML_COMMAND_ITALIC_TOGGLE:
4690                gtk_html_toggle_font_style (html, GTK_HTML_FONT_STYLE_ITALIC);
4691                break;
4692        case GTK_HTML_COMMAND_STRIKEOUT_ON:
4693                gtk_html_set_font_style (html, GTK_HTML_FONT_STYLE_MAX, GTK_HTML_FONT_STYLE_STRIKEOUT);
4694                break;
4695        case GTK_HTML_COMMAND_STRIKEOUT_OFF:
4696                gtk_html_set_font_style (html, ~GTK_HTML_FONT_STYLE_STRIKEOUT, 0);
4697                break;
4698        case GTK_HTML_COMMAND_STRIKEOUT_TOGGLE:
4699                gtk_html_toggle_font_style (html, GTK_HTML_FONT_STYLE_STRIKEOUT);
4700                break;
4701        case GTK_HTML_COMMAND_UNDERLINE_ON:
4702                gtk_html_set_font_style (html, GTK_HTML_FONT_STYLE_MAX, GTK_HTML_FONT_STYLE_UNDERLINE);
4703                break;
4704        case GTK_HTML_COMMAND_UNDERLINE_OFF:
4705                gtk_html_set_font_style (html, ~GTK_HTML_FONT_STYLE_UNDERLINE, 0);
4706                break;
4707        case GTK_HTML_COMMAND_UNDERLINE_TOGGLE:
4708                gtk_html_toggle_font_style (html, GTK_HTML_FONT_STYLE_UNDERLINE);
4709                break;
4710        case GTK_HTML_COMMAND_SIZE_MINUS_2:
4711                gtk_html_set_font_style (html, ~GTK_HTML_FONT_STYLE_SIZE_MASK, GTK_HTML_FONT_STYLE_SIZE_1);
4712                break;
4713        case GTK_HTML_COMMAND_SIZE_MINUS_1:
4714                gtk_html_set_font_style (html, ~GTK_HTML_FONT_STYLE_SIZE_MASK, GTK_HTML_FONT_STYLE_SIZE_2);
4715                break;
4716        case GTK_HTML_COMMAND_SIZE_PLUS_0:
4717                gtk_html_set_font_style (html, ~GTK_HTML_FONT_STYLE_SIZE_MASK, GTK_HTML_FONT_STYLE_SIZE_3);
4718                break;
4719        case GTK_HTML_COMMAND_SIZE_PLUS_1:
4720                gtk_html_set_font_style (html, ~GTK_HTML_FONT_STYLE_SIZE_MASK, GTK_HTML_FONT_STYLE_SIZE_4);
4721                break;
4722        case GTK_HTML_COMMAND_SIZE_PLUS_2:
4723                gtk_html_set_font_style (html, ~GTK_HTML_FONT_STYLE_SIZE_MASK, GTK_HTML_FONT_STYLE_SIZE_5);
4724                break;
4725        case GTK_HTML_COMMAND_SIZE_PLUS_3:
4726                gtk_html_set_font_style (html, ~GTK_HTML_FONT_STYLE_SIZE_MASK, GTK_HTML_FONT_STYLE_SIZE_6);
4727                break;
4728        case GTK_HTML_COMMAND_SIZE_PLUS_4:
4729                gtk_html_set_font_style (html, ~GTK_HTML_FONT_STYLE_SIZE_MASK, GTK_HTML_FONT_STYLE_SIZE_7);
4730                break;
4731        case GTK_HTML_COMMAND_SIZE_INCREASE:
4732                html_engine_font_size_inc_dec (e, TRUE);
4733                break;
4734        case GTK_HTML_COMMAND_SIZE_DECREASE:
4735                html_engine_font_size_inc_dec (e, FALSE);
4736                break;
4737        case GTK_HTML_COMMAND_ALIGN_LEFT:
4738                gtk_html_set_paragraph_alignment (html, GTK_HTML_PARAGRAPH_ALIGNMENT_LEFT);
4739                break;
4740        case GTK_HTML_COMMAND_ALIGN_CENTER:
4741                gtk_html_set_paragraph_alignment (html, GTK_HTML_PARAGRAPH_ALIGNMENT_CENTER);
4742                break;
4743        case GTK_HTML_COMMAND_ALIGN_RIGHT:
4744                gtk_html_set_paragraph_alignment (html, GTK_HTML_PARAGRAPH_ALIGNMENT_RIGHT);
4745                break;
4746        case GTK_HTML_COMMAND_INDENT_ZERO:
4747                gtk_html_set_indent (html, NULL);
4748                break;
4749        case GTK_HTML_COMMAND_INDENT_INC:
4750                gtk_html_indent_push_level (html, HTML_LIST_TYPE_BLOCKQUOTE);
4751                break;
4752        case GTK_HTML_COMMAND_INDENT_INC_OR_NEXT_CELL:
4753                indent_more_or_next_cell (html);
4754                break;
4755        case GTK_HTML_COMMAND_INSERT_TAB:
4756                if (!html_engine_is_selection_active (e)
4757                    && html_clueflow_tabs (HTML_CLUEFLOW (e->cursor->object->parent), e->painter))
4758                        html_engine_insert_text (e, "\t", 1);
4759                break;
4760        case GTK_HTML_COMMAND_INSERT_TAB_OR_INDENT_MORE:
4761                if (!html_engine_is_selection_active (e)
4762                    && html_clueflow_tabs (HTML_CLUEFLOW (e->cursor->object->parent), e->painter))
4763                        html_engine_insert_text (e, "\t", 1);
4764                else
4765                        gtk_html_indent_push_level (html, HTML_LIST_TYPE_BLOCKQUOTE);
4766                break;
4767        case GTK_HTML_COMMAND_INSERT_TAB_OR_NEXT_CELL:
4768                html->binding_handled = insert_tab_or_next_cell (html);
4769                break;
4770        case GTK_HTML_COMMAND_INDENT_DEC:
4771                gtk_html_indent_pop_level (html);
4772                break;
4773        case GTK_HTML_COMMAND_PREV_CELL:
4774                html->binding_handled = html_engine_prev_cell (html->engine);
4775                break;
4776        case GTK_HTML_COMMAND_INDENT_PARAGRAPH:
4777                html_engine_indent_paragraph (e);
4778                break;
4779        case GTK_HTML_COMMAND_BREAK_AND_FILL_LINE:
4780                html_engine_break_and_fill_line (e);
4781                break;
4782        case GTK_HTML_COMMAND_SPACE_AND_FILL_LINE:
4783                html_engine_space_and_fill_line (e);
4784                break;
4785        case GTK_HTML_COMMAND_PARAGRAPH_STYLE_NORMAL:
4786                gtk_html_set_paragraph_style (html, GTK_HTML_PARAGRAPH_STYLE_NORMAL);
4787                break;
4788        case GTK_HTML_COMMAND_PARAGRAPH_STYLE_H1:
4789                gtk_html_set_paragraph_style (html, GTK_HTML_PARAGRAPH_STYLE_H1);
4790                break;
4791        case GTK_HTML_COMMAND_PARAGRAPH_STYLE_H2:
4792                gtk_html_set_paragraph_style (html, GTK_HTML_PARAGRAPH_STYLE_H2);
4793                break;
4794        case GTK_HTML_COMMAND_PARAGRAPH_STYLE_H3:
4795                gtk_html_set_paragraph_style (html, GTK_HTML_PARAGRAPH_STYLE_H3);
4796                break;
4797        case GTK_HTML_COMMAND_PARAGRAPH_STYLE_H4:
4798                gtk_html_set_paragraph_style (html, GTK_HTML_PARAGRAPH_STYLE_H4);
4799                break;
4800        case GTK_HTML_COMMAND_PARAGRAPH_STYLE_H5:
4801                gtk_html_set_paragraph_style (html, GTK_HTML_PARAGRAPH_STYLE_H5);
4802                break;
4803        case GTK_HTML_COMMAND_PARAGRAPH_STYLE_H6:
4804                gtk_html_set_paragraph_style (html, GTK_HTML_PARAGRAPH_STYLE_H6);
4805                break;
4806        case GTK_HTML_COMMAND_PARAGRAPH_STYLE_PRE:
4807                gtk_html_set_paragraph_style (html, GTK_HTML_PARAGRAPH_STYLE_PRE);
4808                break;
4809        case GTK_HTML_COMMAND_PARAGRAPH_STYLE_ADDRESS:
4810                gtk_html_set_paragraph_style (html, GTK_HTML_PARAGRAPH_STYLE_ADDRESS);
4811                break;
4812        case GTK_HTML_COMMAND_PARAGRAPH_STYLE_ITEMDOTTED:
4813                gtk_html_set_paragraph_style (html, GTK_HTML_PARAGRAPH_STYLE_ITEMDOTTED);
4814                break;
4815        case GTK_HTML_COMMAND_PARAGRAPH_STYLE_ITEMROMAN:
4816                gtk_html_set_paragraph_style (html, GTK_HTML_PARAGRAPH_STYLE_ITEMROMAN);
4817                break;
4818        case GTK_HTML_COMMAND_PARAGRAPH_STYLE_ITEMDIGIT:
4819                gtk_html_set_paragraph_style (html, GTK_HTML_PARAGRAPH_STYLE_ITEMDIGIT);
4820                break;
4821        case GTK_HTML_COMMAND_PARAGRAPH_STYLE_ITEMALPHA:
4822                gtk_html_set_paragraph_style (html, GTK_HTML_PARAGRAPH_STYLE_ITEMALPHA);
4823                break;
4824        case GTK_HTML_COMMAND_SELECT_WORD:
4825                gtk_html_select_word (html);
4826                break;
4827        case GTK_HTML_COMMAND_SELECT_LINE:
4828                gtk_html_select_line (html);
4829                break;
4830        case GTK_HTML_COMMAND_SELECT_PARAGRAPH:
4831                gtk_html_select_paragraph (html);
4832                break;
4833        case GTK_HTML_COMMAND_SELECT_PARAGRAPH_EXTENDED:
4834                gtk_html_select_paragraph_extended (html);
4835                break;
4836        case GTK_HTML_COMMAND_SELECT_ALL:
4837                gtk_html_select_all (html);
4838                break;
4839        case GTK_HTML_COMMAND_CURSOR_POSITION_SAVE:
4840                html_engine_edit_cursor_position_save (html->engine);
4841                break;
4842        case GTK_HTML_COMMAND_CURSOR_POSITION_RESTORE:
4843                html_engine_edit_cursor_position_restore (html->engine);
4844                break;
4845        case GTK_HTML_COMMAND_CAPITALIZE_WORD:
4846                html_engine_capitalize_word (e);
4847                break;
4848        case GTK_HTML_COMMAND_UPCASE_WORD:
4849                html_engine_upcase_downcase_word (e, TRUE);
4850                break;
4851        case GTK_HTML_COMMAND_DOWNCASE_WORD:
4852                html_engine_upcase_downcase_word (e, FALSE);
4853                break;
4854        case GTK_HTML_COMMAND_SPELL_SUGGEST:
4855                if (html->editor_api && !html_engine_spell_word_is_valid (e))
4856                        (*html->editor_api->suggestion_request) (html, html->editor_data);
4857                break;
4858        case GTK_HTML_COMMAND_SPELL_PERSONAL_DICTIONARY_ADD:
4859        case GTK_HTML_COMMAND_SPELL_SESSION_DICTIONARY_ADD: {
4860                gchar *word;
4861                word = html_engine_get_spell_word (e);
4862
4863                if (word && html->editor_api) {
4864                        if (com_type == GTK_HTML_COMMAND_SPELL_PERSONAL_DICTIONARY_ADD)
4865                                /* FIXME fire popup menu with more than 1 language enabled */
4866                                (*html->editor_api->add_to_personal) (html, word, html_engine_get_language (html->engine), html->editor_data);
4867                        else
4868                                (*html->editor_api->add_to_session) (html, word, html->editor_data);
4869                        g_free (word);
4870                        html_engine_spell_check (e);
4871                        gtk_widget_queue_draw (GTK_WIDGET (html));
4872                }
4873                break;
4874        }
4875        case GTK_HTML_COMMAND_CURSOR_FORWARD:
4876                rv = html_cursor_forward (html->engine->cursor, html->engine);
4877                break;
4878        case GTK_HTML_COMMAND_CURSOR_BACKWARD:
4879                rv = html_cursor_backward (html->engine->cursor, html->engine);
4880                break;
4881        case GTK_HTML_COMMAND_INSERT_TABLE_1_1:
4882                html_engine_insert_table_1_1 (e);
4883                break;
4884        case GTK_HTML_COMMAND_TABLE_INSERT_COL_BEFORE:
4885                html_engine_insert_table_column (e, FALSE);
4886                break;
4887        case GTK_HTML_COMMAND_TABLE_INSERT_COL_AFTER:
4888                html_engine_insert_table_column (e, TRUE);
4889                break;
4890        case GTK_HTML_COMMAND_TABLE_DELETE_COL:
4891                html_engine_delete_table_column (e);
4892                break;
4893        case GTK_HTML_COMMAND_TABLE_INSERT_ROW_BEFORE:
4894                html_engine_insert_table_row (e, FALSE);
4895                break;
4896        case GTK_HTML_COMMAND_TABLE_INSERT_ROW_AFTER:
4897                html_engine_insert_table_row (e, TRUE);
4898                break;
4899        case GTK_HTML_COMMAND_TABLE_DELETE_ROW:
4900                html_engine_delete_table_row (e);
4901                break;
4902        case GTK_HTML_COMMAND_TABLE_BORDER_WIDTH_INC:
4903                html_engine_table_set_border_width (e, html_engine_get_table (e), 1, TRUE);
4904                break;
4905        case GTK_HTML_COMMAND_TABLE_BORDER_WIDTH_DEC:
4906                html_engine_table_set_border_width (e, html_engine_get_table (e), -1, TRUE);
4907                break;
4908        case GTK_HTML_COMMAND_TABLE_BORDER_WIDTH_ZERO:
4909                html_engine_table_set_border_width (e, html_engine_get_table (e), 0, FALSE);
4910                break;
4911        case GTK_HTML_COMMAND_TABLE_SPACING_INC:
4912                html_engine_table_set_spacing (e, html_engine_get_table (e), 1, TRUE);
4913                break;
4914        case GTK_HTML_COMMAND_TABLE_SPACING_DEC:
4915                html_engine_table_set_spacing (e, html_engine_get_table (e), -1, TRUE);
4916                break;
4917        case GTK_HTML_COMMAND_TABLE_SPACING_ZERO:
4918                html_engine_table_set_spacing (e, html_engine_get_table (e), 0, FALSE);
4919                break;
4920        case GTK_HTML_COMMAND_TABLE_PADDING_INC:
4921                html_engine_table_set_padding (e, html_engine_get_table (e), 1, TRUE);
4922                break;
4923        case GTK_HTML_COMMAND_TABLE_PADDING_DEC:
4924                html_engine_table_set_padding (e, html_engine_get_table (e), -1, TRUE);
4925                break;
4926        case GTK_HTML_COMMAND_TABLE_PADDING_ZERO:
4927                html_engine_table_set_padding (e, html_engine_get_table (e), 0, FALSE);
4928                break;
4929        case GTK_HTML_COMMAND_TEXT_SET_DEFAULT_COLOR:
4930                html_engine_set_color (e, NULL);
4931                break;
4932        case GTK_HTML_COMMAND_CURSOR_BOD:
4933                html_engine_beginning_of_document (e);         
4934                break;
4935        case GTK_HTML_COMMAND_CURSOR_EOD:
4936                html_engine_end_of_document (e);
4937                break;
4938        case GTK_HTML_COMMAND_BLOCK_REDRAW:
4939                html_engine_block_redraw (e);
4940                break;
4941        case GTK_HTML_COMMAND_UNBLOCK_REDRAW:
4942                html_engine_unblock_redraw (e);
4943                break;
4944        case GTK_HTML_COMMAND_GRAB_FOCUS:
4945                gtk_widget_grab_focus (GTK_WIDGET (html));
4946                break;
4947        case GTK_HTML_COMMAND_KILL_WORD:
4948        case GTK_HTML_COMMAND_KILL_WORD_BACKWARD:
4949                html_engine_disable_selection (e);
4950                html_engine_edit_selection_updater_schedule (e->selection_updater);
4951                html_engine_set_mark (html->engine);
4952                rv = com_type == GTK_HTML_COMMAND_KILL_WORD
4953                        ? html_engine_forward_word (html->engine)
4954                        : html_engine_backward_word (html->engine);
4955                html_engine_edit_selection_updater_update_now (e->selection_updater);
4956                html_draw_queue_clear (e->draw_queue);
4957                if (rv)
4958                        gtk_html_cut (html);
4959                html_engine_disable_selection (e);
4960                break;
4961        case GTK_HTML_COMMAND_SAVE_DATA_ON:
4962                html->engine->save_data = TRUE;
4963                break;
4964        case GTK_HTML_COMMAND_SAVE_DATA_OFF:
4965                html->engine->save_data = FALSE;
4966                break;
4967        case GTK_HTML_COMMAND_SAVED:
4968                html_engine_saved (html->engine);
4969                break;
4970        case GTK_HTML_COMMAND_IS_SAVED:
4971                rv = html_engine_is_saved (html->engine);
4972                break;
4973        case GTK_HTML_COMMAND_CELL_CSPAN_INC:
4974                rv = html_engine_cspan_delta (html->engine, 1);
4975                break;
4976        case GTK_HTML_COMMAND_CELL_RSPAN_INC:
4977                rv = html_engine_rspan_delta (html->engine, 1);
4978                break;
4979        case GTK_HTML_COMMAND_CELL_CSPAN_DEC:
4980                rv = html_engine_cspan_delta (html->engine, -1);
4981                break;
4982        case GTK_HTML_COMMAND_CELL_RSPAN_DEC:
4983                rv = html_engine_rspan_delta (html->engine, -1);
4984                break;
4985        default:
4986                html->binding_handled = FALSE;
4987        }
4988
4989        if (!html->binding_handled && html->editor_api)
4990                html->binding_handled = (* html->editor_api->command) (html, com_type, html->editor_data);
4991
4992        return rv;
4993}
4994
4995static void
4996add_bindings (GtkHTMLClass *klass)
4997{
4998        GtkBindingSet *binding_set;
4999
5000        /* ensure enums are defined */
5001        gtk_html_cursor_skip_get_type ();
5002        gtk_html_command_get_type ();
5003
5004        binding_set = gtk_binding_set_by_class (klass);
5005
5006        /* layout scrolling */
5007#define BSCROLL(m,key,orient,sc) \
5008        gtk_binding_entry_add_signal (binding_set, GDK_ ## key, m, \
5009                                      "scroll", 3, \
5010                                      GTK_TYPE_ORIENTATION, GTK_ORIENTATION_ ## orient, \
5011                                      GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_ ## sc, \
5012                                      G_TYPE_FLOAT, 0.0); \
5013
5014        BSCROLL (0, Up, VERTICAL, STEP_BACKWARD);
5015        BSCROLL (0, KP_Up, VERTICAL, STEP_BACKWARD);
5016        BSCROLL (0, Down, VERTICAL, STEP_FORWARD);
5017        BSCROLL (0, KP_Down, VERTICAL, STEP_FORWARD);
5018
5019        BSCROLL (0, Left, HORIZONTAL, STEP_BACKWARD);
5020        BSCROLL (0, KP_Left, HORIZONTAL, STEP_BACKWARD);
5021        BSCROLL (0, Right, HORIZONTAL, STEP_FORWARD);
5022        BSCROLL (0, KP_Right, HORIZONTAL, STEP_FORWARD);
5023
5024        BSCROLL (0, Page_Up, VERTICAL, PAGE_BACKWARD);
5025        BSCROLL (0, KP_Page_Up, VERTICAL, PAGE_BACKWARD);
5026        BSCROLL (0, Page_Down, VERTICAL, PAGE_FORWARD);
5027        BSCROLL (0, KP_Page_Down, VERTICAL, PAGE_FORWARD);
5028        BSCROLL (0, BackSpace, VERTICAL, PAGE_BACKWARD);
5029        BSCROLL (0, space, VERTICAL, PAGE_FORWARD);
5030
5031        BSCROLL (GDK_SHIFT_MASK, Left, HORIZONTAL, PAGE_BACKWARD);
5032        BSCROLL (GDK_SHIFT_MASK, KP_Left, HORIZONTAL, PAGE_BACKWARD);
5033        BSCROLL (GDK_SHIFT_MASK, Right, HORIZONTAL, PAGE_FORWARD);
5034        BSCROLL (GDK_SHIFT_MASK, KP_Right, HORIZONTAL, PAGE_FORWARD);
5035
5036        /* editing */
5037
5038#define BMOVE(m,key,dir,sk) \
5039        gtk_binding_entry_add_signal (binding_set, GDK_ ## key, m, \
5040                                      "cursor_move", 2, \
5041                                      GTK_TYPE_DIRECTION_TYPE, GTK_DIR_ ## dir, \
5042                                      GTK_TYPE_HTML_CURSOR_SKIP, GTK_HTML_CURSOR_SKIP_ ## sk);
5043
5044        BMOVE (0, Left,     LEFT,  ONE);
5045        BMOVE (0, KP_Left,  LEFT,  ONE);
5046        BMOVE (0, Right,    RIGHT, ONE);
5047        BMOVE (0, KP_Right, RIGHT, ONE);
5048        BMOVE (0, Up,       UP  ,  ONE);
5049        BMOVE (0, KP_Up,    UP  ,  ONE);
5050        BMOVE (0, Down,     DOWN,  ONE);
5051        BMOVE (0, KP_Down,  DOWN,  ONE);
5052
5053        BMOVE (GDK_CONTROL_MASK, KP_Left,  LEFT,  WORD);
5054        BMOVE (GDK_CONTROL_MASK, Left,     LEFT,  WORD);
5055        BMOVE (GDK_MOD1_MASK,    Left,     LEFT,  WORD);
5056
5057        BMOVE (GDK_CONTROL_MASK, KP_Right, RIGHT, WORD);
5058        BMOVE (GDK_CONTROL_MASK, Right,    RIGHT, WORD);
5059        BMOVE (GDK_MOD1_MASK,    Right,    RIGHT, WORD);
5060
5061        BMOVE (0, Page_Up,       UP,   PAGE);
5062        BMOVE (0, KP_Page_Up,    UP,   PAGE);
5063        BMOVE (0, Page_Down,     DOWN, PAGE);
5064        BMOVE (0, KP_Page_Down,  DOWN, PAGE);
5065
5066        BMOVE (0, Home, LEFT, ALL);
5067        BMOVE (0, KP_Home, LEFT, ALL);
5068        BMOVE (0, End, RIGHT, ALL);
5069        BMOVE (0, KP_End, RIGHT, ALL);
5070        BMOVE (GDK_CONTROL_MASK, Home, UP, ALL);
5071        BMOVE (GDK_CONTROL_MASK, KP_Home, UP, ALL);
5072        BMOVE (GDK_CONTROL_MASK, End, DOWN, ALL);
5073        BMOVE (GDK_CONTROL_MASK, KP_End, DOWN, ALL);
5074
5075#define BCOM(m,key,com) \
5076        gtk_binding_entry_add_signal (binding_set, GDK_ ## key, m, \
5077                                      "command", 1, \
5078                                      GTK_TYPE_HTML_COMMAND, GTK_HTML_COMMAND_ ## com);
5079        BCOM (0, Home, SCROLL_BOD);
5080        BCOM (0, KP_Home, SCROLL_BOD);
5081        BCOM (0, End, SCROLL_EOD);
5082        BCOM (0, KP_End, SCROLL_EOD);
5083
5084        BCOM (GDK_CONTROL_MASK, c, COPY);
5085
5086        BCOM (0, Return, INSERT_PARAGRAPH);
5087        BCOM (0, KP_Enter, INSERT_PARAGRAPH);
5088        BCOM (0, BackSpace, DELETE_BACK_OR_INDENT_DEC);
5089        BCOM (GDK_SHIFT_MASK, BackSpace, DELETE_BACK_OR_INDENT_DEC);
5090        BCOM (0, Delete, DELETE);
5091        BCOM (0, KP_Delete, DELETE);
5092
5093        BCOM (GDK_CONTROL_MASK | GDK_SHIFT_MASK, plus, ZOOM_IN);
5094        BCOM (GDK_CONTROL_MASK, plus, ZOOM_IN);
5095        BCOM (GDK_CONTROL_MASK, equal, ZOOM_IN);
5096        BCOM (GDK_CONTROL_MASK, KP_Add, ZOOM_IN);
5097        BCOM (GDK_CONTROL_MASK, minus, ZOOM_OUT);
5098        BCOM (GDK_CONTROL_MASK, KP_Subtract, ZOOM_OUT);
5099        BCOM (GDK_CONTROL_MASK, 8, ZOOM_IN);
5100        BCOM (GDK_CONTROL_MASK, 9, ZOOM_RESET);
5101        BCOM (GDK_CONTROL_MASK, 0, ZOOM_OUT);
5102        BCOM (GDK_CONTROL_MASK, KP_Multiply, ZOOM_RESET);
5103
5104        /* selection */
5105        BCOM (GDK_SHIFT_MASK, Up, MODIFY_SELECTION_UP);
5106        BCOM (GDK_SHIFT_MASK, Down, MODIFY_SELECTION_DOWN);
5107        BCOM (GDK_SHIFT_MASK, Left, MODIFY_SELECTION_LEFT);
5108        BCOM (GDK_SHIFT_MASK, Right, MODIFY_SELECTION_RIGHT);
5109        BCOM (GDK_SHIFT_MASK | GDK_CONTROL_MASK, Left, MODIFY_SELECTION_PREV_WORD);
5110        BCOM (GDK_SHIFT_MASK | GDK_CONTROL_MASK, Right, MODIFY_SELECTION_NEXT_WORD);
5111        BCOM (GDK_SHIFT_MASK, Home, MODIFY_SELECTION_BOL);
5112        BCOM (GDK_SHIFT_MASK, KP_Home, MODIFY_SELECTION_BOL);
5113        BCOM (GDK_SHIFT_MASK, End, MODIFY_SELECTION_EOL);
5114        BCOM (GDK_SHIFT_MASK, KP_End, MODIFY_SELECTION_EOL);
5115        BCOM (GDK_SHIFT_MASK | GDK_CONTROL_MASK, Home, MODIFY_SELECTION_BOD);
5116        BCOM (GDK_SHIFT_MASK | GDK_CONTROL_MASK, KP_Home, MODIFY_SELECTION_BOD);
5117        BCOM (GDK_SHIFT_MASK | GDK_CONTROL_MASK, End, MODIFY_SELECTION_EOD);
5118        BCOM (GDK_SHIFT_MASK | GDK_CONTROL_MASK, KP_End, MODIFY_SELECTION_EOD);
5119        BCOM (GDK_SHIFT_MASK, Page_Up, MODIFY_SELECTION_PAGEUP);
5120        BCOM (GDK_SHIFT_MASK, Page_Down, MODIFY_SELECTION_PAGEDOWN);
5121        BCOM (GDK_SHIFT_MASK, KP_Page_Up, MODIFY_SELECTION_PAGEUP);
5122        BCOM (GDK_SHIFT_MASK, KP_Page_Down, MODIFY_SELECTION_PAGEDOWN);
5123        BCOM (GDK_CONTROL_MASK, a, SELECT_ALL);
5124        BCOM (GDK_CONTROL_MASK, p, SELECT_PARAGRAPH);
5125
5126        /* copy, cut, paste, delete */
5127        BCOM (GDK_CONTROL_MASK, c, COPY);
5128        BCOM (GDK_CONTROL_MASK, Insert, COPY);
5129        BCOM (GDK_CONTROL_MASK, KP_Insert, COPY);
5130        BCOM (GDK_CONTROL_MASK, x, CUT);
5131        BCOM (GDK_SHIFT_MASK, Delete, CUT);
5132        BCOM (GDK_SHIFT_MASK, KP_Delete, CUT);
5133        BCOM (GDK_CONTROL_MASK, v, PASTE);
5134        BCOM (GDK_SHIFT_MASK, Insert, PASTE);
5135        BCOM (GDK_SHIFT_MASK, KP_Insert, PASTE);
5136        BCOM (GDK_CONTROL_MASK, Delete, KILL_WORD);
5137        BCOM (GDK_CONTROL_MASK, BackSpace, KILL_WORD_BACKWARD);
5138
5139        /* font style */
5140        BCOM (GDK_CONTROL_MASK, b, BOLD_TOGGLE);
5141        BCOM (GDK_CONTROL_MASK, i, ITALIC_TOGGLE);
5142        BCOM (GDK_CONTROL_MASK, u, UNDERLINE_TOGGLE);
5143        BCOM (GDK_CONTROL_MASK, o, TEXT_COLOR_APPLY);
5144        BCOM (GDK_MOD1_MASK, 1, SIZE_MINUS_2);
5145        BCOM (GDK_MOD1_MASK, 2, SIZE_MINUS_1);
5146        BCOM (GDK_MOD1_MASK, 3, SIZE_PLUS_0);
5147        BCOM (GDK_MOD1_MASK, 4, SIZE_PLUS_1);
5148        BCOM (GDK_MOD1_MASK, 5, SIZE_PLUS_2);
5149        BCOM (GDK_MOD1_MASK, 6, SIZE_PLUS_3);
5150        BCOM (GDK_MOD1_MASK, 7, SIZE_PLUS_4);
5151        BCOM (GDK_MOD1_MASK, 8, SIZE_INCREASE);
5152        BCOM (GDK_MOD1_MASK, 9, SIZE_DECREASE);
5153
5154        /* undo/redo */
5155        BCOM (GDK_CONTROL_MASK, z, UNDO);
5156        BCOM (GDK_CONTROL_MASK, r, REDO);
5157
5158        /* paragraph style */
5159        BCOM (GDK_CONTROL_MASK | GDK_MOD1_MASK, l, ALIGN_LEFT);
5160        BCOM (GDK_CONTROL_MASK | GDK_MOD1_MASK, r, ALIGN_RIGHT);
5161        BCOM (GDK_CONTROL_MASK | GDK_MOD1_MASK, c, ALIGN_CENTER);
5162
5163        /* tabs */
5164        BCOM (0, Tab, INSERT_TAB_OR_NEXT_CELL);
5165        BCOM (GDK_SHIFT_MASK, Tab, PREV_CELL);
5166
5167        /* spell checking */
5168        BCOM (GDK_SHIFT_MASK | GDK_CONTROL_MASK | GDK_MOD1_MASK, s, SPELL_SUGGEST);
5169        BCOM (GDK_SHIFT_MASK | GDK_CONTROL_MASK | GDK_MOD1_MASK, p, SPELL_PERSONAL_DICTIONARY_ADD);
5170        BCOM (GDK_SHIFT_MASK | GDK_CONTROL_MASK | GDK_MOD1_MASK, n, SPELL_SESSION_DICTIONARY_ADD);
5171
5172        /* popup menu, properties dialog */
5173        BCOM (GDK_MOD1_MASK, space, POPUP_MENU);
5174        BCOM (GDK_MOD1_MASK, Return, PROPERTIES_DIALOG);
5175        BCOM (GDK_MOD1_MASK, KP_Enter, PROPERTIES_DIALOG);
5176
5177        /* tables */
5178        BCOM (GDK_CONTROL_MASK | GDK_SHIFT_MASK, t, INSERT_TABLE_1_1);
5179        BCOM (GDK_SHIFT_MASK | GDK_CONTROL_MASK, c, TABLE_INSERT_COL_AFTER);
5180        BCOM (GDK_SHIFT_MASK | GDK_CONTROL_MASK, r, TABLE_INSERT_ROW_AFTER);
5181        BCOM (GDK_SHIFT_MASK | GDK_CONTROL_MASK, s, TABLE_SPACING_INC);
5182        BCOM (GDK_SHIFT_MASK | GDK_CONTROL_MASK, p, TABLE_PADDING_INC);
5183        BCOM (GDK_SHIFT_MASK | GDK_CONTROL_MASK, b, TABLE_BORDER_WIDTH_INC);
5184        BCOM (GDK_MOD1_MASK | GDK_CONTROL_MASK, c, TABLE_INSERT_COL_BEFORE);
5185        BCOM (GDK_MOD1_MASK | GDK_CONTROL_MASK, r, TABLE_INSERT_ROW_BEFORE);
5186        BCOM (GDK_MOD1_MASK | GDK_CONTROL_MASK, s, TABLE_SPACING_DEC);
5187        BCOM (GDK_MOD1_MASK | GDK_CONTROL_MASK, p, TABLE_PADDING_DEC);
5188        BCOM (GDK_MOD1_MASK | GDK_CONTROL_MASK, b, TABLE_BORDER_WIDTH_DEC);
5189        BCOM (GDK_SHIFT_MASK | GDK_MOD1_MASK, c, TABLE_DELETE_COL);
5190        BCOM (GDK_SHIFT_MASK | GDK_MOD1_MASK, r, TABLE_DELETE_ROW);
5191        BCOM (GDK_SHIFT_MASK | GDK_MOD1_MASK, s, TABLE_SPACING_ZERO);
5192        BCOM (GDK_SHIFT_MASK | GDK_MOD1_MASK, p, TABLE_PADDING_ZERO);
5193        BCOM (GDK_SHIFT_MASK | GDK_MOD1_MASK, b, TABLE_BORDER_WIDTH_ZERO);
5194}
5195
5196gint
5197gtk_html_set_iframe_parent (GtkHTML *html, GtkWidget *parent, HTMLObject *frame)
5198{
5199        GtkWidget *top_level;
5200        gint depth = 0;
5201        g_assert (GTK_IS_HTML (parent));
5202
5203        gtk_html_set_animate (html, gtk_html_get_animate (GTK_HTML (parent)));
5204
5205        html->iframe_parent = parent;
5206        html->frame = frame;
5207
5208        top_level = GTK_WIDGET (gtk_html_get_top_html (html));
5209        if (html->engine && html->engine->painter) {
5210                html_painter_set_widget (html->engine->painter, top_level);
5211                gtk_html_set_fonts (html, html->engine->painter);
5212        }
5213        g_signal_emit (top_level, signals [IFRAME_CREATED], 0, html);
5214
5215        while (html->iframe_parent) {
5216                depth++;
5217                html = GTK_HTML (html->iframe_parent);
5218        }
5219       
5220        return depth;
5221}
5222
5223void
5224gtk_html_select_word (GtkHTML *html)
5225{
5226        HTMLEngine *e;
5227
5228        if (!html->allow_selection)
5229                return;
5230
5231        e = html->engine;
5232        if (html_engine_get_editable (e))
5233                html_engine_select_word_editable (e);
5234        else
5235                html_engine_select_word (e);
5236
5237        html_engine_update_selection_active_state (html->engine, html->priv->event_time);
5238}
5239
5240void
5241gtk_html_select_line (GtkHTML *html)
5242{
5243        HTMLEngine *e;
5244
5245        if (!html->allow_selection)
5246                return;
5247
5248        e = html->engine;
5249        if (html_engine_get_editable (e))
5250                html_engine_select_line_editable (e);
5251        else
5252                html_engine_select_line (e);
5253
5254        html_engine_update_selection_active_state (html->engine, html->priv->event_time);
5255}
5256
5257void
5258gtk_html_select_paragraph (GtkHTML *html)
5259{
5260        HTMLEngine *e;
5261
5262        if (!html->allow_selection)
5263                return;
5264
5265        e = html->engine;
5266        if (html_engine_get_editable (e))
5267                html_engine_select_paragraph_editable (e);
5268        /* FIXME: does anybody need this? if so bother me. rodo
5269           else
5270           html_engine_select_paragraph (e); */
5271
5272        html_engine_update_selection_active_state (html->engine, html->priv->event_time);
5273}
5274
5275void
5276gtk_html_select_paragraph_extended (GtkHTML *html)
5277{
5278        HTMLEngine *e;
5279
5280        if (!html->allow_selection)
5281                return;
5282
5283        e = html->engine;
5284        if (html_engine_get_editable (e))
5285                html_engine_select_paragraph_extended (e);
5286        /* FIXME: does anybody need this? if so bother me. rodo
5287           else
5288           html_engine_select_paragraph (e); */
5289
5290        html_engine_update_selection_active_state (html->engine, html->priv->event_time);
5291}
5292
5293void
5294gtk_html_select_all (GtkHTML *html)
5295{
5296        HTMLEngine *e;
5297
5298        if (!html->allow_selection)
5299                return;
5300
5301        e = html->engine;
5302
5303        if (html_engine_get_editable (e))
5304                html_engine_select_all_editable (e);
5305        else {
5306                html_engine_select_all (e);
5307        }
5308
5309        html_engine_update_selection_active_state (html->engine, html->priv->event_time);
5310}
5311
5312void
5313gtk_html_api_set_language (GtkHTML *html)
5314{
5315        g_return_if_fail (GTK_IS_HTML (html));
5316
5317        if (html->editor_api) {
5318                html->editor_api->set_language (html, html_engine_get_language (html->engine), html->editor_data);
5319                html_engine_spell_check (html->engine);
5320        }
5321}
5322
5323void
5324gtk_html_set_editor_api (GtkHTML *html, GtkHTMLEditorAPI *api, gpointer data)
5325{
5326        html->editor_api  = api;
5327        html->editor_data = data;
5328
5329        gtk_html_api_set_language (html);
5330}
5331
5332static gchar *
5333get_value_nick (GtkHTMLCommandType com_type)
5334{
5335        GEnumValue *val;
5336        GEnumClass *enum_class;
5337
5338        enum_class = g_type_class_ref (GTK_TYPE_HTML_COMMAND);
5339        val = g_enum_get_value (enum_class, com_type);
5340        g_type_class_unref (enum_class);
5341        if (val)
5342                return val->value_nick;
5343
5344        g_warning ("Invalid GTK_TYPE_HTML_COMMAND enum value %d\n", com_type);
5345
5346        return NULL;
5347}
5348
5349void
5350gtk_html_editor_event_command (GtkHTML *html, GtkHTMLCommandType com_type, gboolean before)
5351{
5352        GValue arg;
5353
5354        memset (&arg, 0, sizeof (GValue));
5355        g_value_init (&arg, G_TYPE_STRING);
5356        g_value_set_string (&arg, get_value_nick (com_type));
5357
5358        /* printf ("sending %s\n", GTK_VALUE_STRING (*args [0])); */
5359        gtk_html_editor_event (html, before ? GTK_HTML_EDITOR_EVENT_COMMAND_BEFORE : GTK_HTML_EDITOR_EVENT_COMMAND_AFTER,
5360                               &arg);
5361
5362        g_value_unset (&arg);
5363}
5364
5365void
5366gtk_html_editor_event (GtkHTML *html, GtkHTMLEditorEventType event, GValue *args)
5367{
5368        GValue *retval = NULL;
5369
5370        if (html->editor_api && !html->engine->block_events)
5371                retval = (*html->editor_api->event) (html, event, args, html->editor_data);
5372
5373        if (retval) {
5374                g_value_unset (retval);
5375                g_free (retval);
5376        }
5377}
5378
5379gboolean
5380gtk_html_command (GtkHTML *html, const gchar *command_name)
5381{
5382        GEnumClass *class;
5383        GEnumValue *val;
5384
5385        class = G_ENUM_CLASS (g_type_class_ref (GTK_TYPE_HTML_COMMAND));
5386        val = g_enum_get_value_by_nick (class, command_name);
5387        g_type_class_unref (class);
5388        if (val) {
5389                if (command (html, val->value)) {
5390                        if (html->priv->update_styles)
5391                                gtk_html_update_styles (html);
5392                        return TRUE;
5393                }
5394        }
5395
5396        return FALSE;
5397}
5398
5399gboolean
5400gtk_html_edit_make_cursor_visible (GtkHTML *html)
5401{
5402        gboolean rv = FALSE;
5403
5404        g_return_val_if_fail (GTK_IS_HTML (html), rv);
5405
5406        html_engine_hide_cursor (html->engine);
5407        if (html_engine_make_cursor_visible (html->engine)) {
5408                gtk_adjustment_set_value (GTK_LAYOUT (html)->hadjustment, (gfloat) html->engine->x_offset);
5409                gtk_adjustment_set_value (GTK_LAYOUT (html)->vadjustment, (gfloat) html->engine->y_offset);
5410                rv = TRUE;
5411        }
5412        html_engine_show_cursor (html->engine);
5413
5414        return rv;
5415}
5416
5417gboolean
5418gtk_html_build_with_gconf ()
5419{
5420        return TRUE;
5421}
5422
5423static void
5424reparent_embedded (HTMLObject *o, HTMLEngine *e, gpointer data)
5425{
5426        if (html_object_is_embedded (o)) {
5427                HTMLEmbedded *eo = HTML_EMBEDDED (o);
5428
5429                if (eo->widget && eo->widget->parent && GTK_IS_HTML (eo->widget->parent) &&
5430                    GTK_HTML (eo->widget->parent)->iframe_parent == NULL) {
5431                        g_object_ref (eo->widget);
5432                        gtk_container_remove (GTK_CONTAINER (eo->widget->parent), eo->widget);
5433                        GTK_OBJECT_FLAGS (eo->widget) = GTK_FLOATING;
5434                }
5435                eo->parent = data;
5436        }
5437
5438        if (HTML_IS_IFRAME (o) && GTK_HTML (HTML_IFRAME (o)->html)->iframe_parent &&
5439            GTK_HTML (GTK_HTML (HTML_IFRAME (o)->html)->iframe_parent)->iframe_parent == NULL)
5440                gtk_html_set_iframe_parent (GTK_HTML (HTML_IFRAME (o)->html), data, o);
5441
5442        if (HTML_IS_FRAME (o) && GTK_HTML (HTML_FRAME (o)->html)->iframe_parent &&
5443            GTK_HTML (GTK_HTML (HTML_FRAME (o)->html)->iframe_parent)->iframe_parent == NULL)
5444                gtk_html_set_iframe_parent (GTK_HTML (HTML_FRAME (o)->html), data, o);
5445
5446}
5447
5448static void
5449gtk_html_insert_html_generic (GtkHTML *html, GtkHTML *tmp, const gchar *html_src, gboolean obj_only)
5450{
5451        GtkWidget *window, *sw;
5452        HTMLObject *o;
5453
5454        html_engine_freeze (html->engine);
5455        html_engine_delete (html->engine);
5456        if (!tmp)
5457                tmp    = GTK_HTML (gtk_html_new_from_string (html_src, -1));
5458        window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
5459        sw     = gtk_scrolled_window_new (NULL, NULL);
5460        gtk_container_add (GTK_CONTAINER (window), GTK_WIDGET (sw));
5461        gtk_container_add (GTK_CONTAINER (sw), GTK_WIDGET (tmp));
5462        gtk_widget_realize (GTK_WIDGET (tmp));
5463        html_image_factory_move_images (html->engine->image_factory, tmp->engine->image_factory);
5464       
5465        /* copy the forms */
5466        g_list_foreach (tmp->engine->formList, (GFunc)html_form_set_engine, html->engine);
5467
5468        /* move top level iframes and embedded widgets from tmp to html */
5469        html_object_forall (tmp->engine->clue, html->engine, reparent_embedded, html);
5470
5471        if (tmp->engine->formList && html->engine->formList) {
5472                GList *form_last;
5473               
5474                form_last = g_list_last (html->engine->formList);
5475                tmp->engine->formList->prev = form_last;
5476                form_last->next = tmp->engine->formList;
5477        } else if (tmp->engine->formList) {
5478                html->engine->formList = tmp->engine->formList;
5479        }
5480        tmp->engine->formList = NULL;
5481       
5482        if (obj_only) {
5483                HTMLObject *next;
5484                g_return_if_fail (tmp->engine->clue && HTML_CLUE (tmp->engine->clue)->head
5485                                  && HTML_CLUE (HTML_CLUE (tmp->engine->clue)->head)->head);
5486
5487                html_undo_level_begin (html->engine->undo, "Append HTML", "Remove appended HTML");
5488                o = HTML_CLUE (tmp->engine->clue)->head;
5489                for (; o; o = next) {
5490                        next = o->next;
5491                        html_object_remove_child (o->parent, o);
5492                        html_engine_append_flow (html->engine, o, html_object_get_recursive_length (o));
5493                }
5494                html_undo_level_end (html->engine->undo);
5495        } else {
5496                g_return_if_fail (tmp->engine->clue);
5497
5498                o = tmp->engine->clue;
5499                tmp->engine->clue = NULL;
5500                html_engine_insert_object (html->engine, o,
5501                                           html_object_get_recursive_length (o),
5502                                           html_object_get_insert_level (o));
5503        }
5504        gtk_widget_destroy (window);
5505        html_engine_thaw (html->engine);
5506}
5507
5508void
5509gtk_html_insert_html (GtkHTML *html, const gchar *html_src)
5510{
5511        g_return_if_fail (GTK_IS_HTML (html));
5512
5513        gtk_html_insert_html_generic (html, NULL, html_src, FALSE);
5514}
5515
5516void
5517gtk_html_insert_gtk_html (GtkHTML *html, GtkHTML *to_be_destroyed)
5518{
5519        g_return_if_fail (GTK_IS_HTML (html));
5520
5521        gtk_html_insert_html_generic (html, to_be_destroyed, NULL, FALSE);
5522}
5523
5524void
5525gtk_html_append_html (GtkHTML *html, const gchar *html_src)
5526{
5527        g_return_if_fail (GTK_IS_HTML (html));
5528
5529        gtk_html_insert_html_generic (html, NULL, html_src, TRUE);
5530}
5531
5532static void
5533set_magnification (HTMLObject *o, HTMLEngine *e, gpointer data)
5534{
5535        if (HTML_IS_FRAME (o)) {
5536                html_font_manager_set_magnification (&GTK_HTML (HTML_FRAME (o)->html)->engine->painter->font_manager,
5537                                                     *(gdouble *) data);
5538        } else if (HTML_IS_IFRAME (o)) {
5539                html_font_manager_set_magnification (&GTK_HTML (HTML_IFRAME (o)->html)->engine->painter->font_manager,
5540                                                     *(gdouble *) data);
5541        } else if (HTML_IS_TEXT (o))
5542                html_text_calc_font_size (HTML_TEXT (o), e);
5543}
5544
5545void
5546gtk_html_set_magnification (GtkHTML *html, gdouble magnification)
5547{
5548        g_return_if_fail (GTK_IS_HTML (html));
5549
5550        if (magnification > 0.05 && magnification < 20.0
5551            && magnification * html->engine->painter->font_manager.var_size >= 4*PANGO_SCALE
5552            && magnification * html->engine->painter->font_manager.fix_size >= 4*PANGO_SCALE) {
5553                html_font_manager_set_magnification (&html->engine->painter->font_manager, magnification);
5554                if (html->engine->clue) {
5555                        html_object_forall (html->engine->clue, html->engine,
5556                                            set_magnification, &magnification);         
5557                        html_object_change_set_down (html->engine->clue, HTML_CHANGE_ALL);
5558                }
5559
5560                html_engine_schedule_update (html->engine);
5561        }
5562}
5563
5564#define MAG_STEP 1.1
5565
5566void
5567gtk_html_zoom_in (GtkHTML *html)
5568{
5569        g_return_if_fail (GTK_IS_HTML (html));
5570
5571        gtk_html_set_magnification (html, html->engine->painter->font_manager.magnification * MAG_STEP);
5572}
5573
5574void
5575gtk_html_zoom_out (GtkHTML *html)
5576{
5577        g_return_if_fail (GTK_IS_HTML (html));
5578        g_return_if_fail (HTML_IS_ENGINE (html->engine));
5579
5580        gtk_html_set_magnification (html, html->engine->painter->font_manager.magnification * (1.0/MAG_STEP));
5581}
5582
5583void
5584gtk_html_zoom_reset (GtkHTML *html)
5585{
5586        g_return_if_fail (GTK_IS_HTML (html));
5587
5588        gtk_html_set_magnification (html, 1.0);
5589}
5590
5591void
5592gtk_html_set_allow_frameset (GtkHTML *html, gboolean allow)
5593{
5594        g_return_if_fail (GTK_IS_HTML (html));
5595        g_return_if_fail (HTML_IS_ENGINE (html->engine));
5596
5597        html->engine->allow_frameset = allow;
5598}
5599
5600gboolean
5601gtk_html_get_allow_frameset (GtkHTML *html)
5602{
5603        g_return_val_if_fail (GTK_IS_HTML (html), FALSE);
5604        g_return_val_if_fail (HTML_IS_ENGINE (html->engine), FALSE);
5605
5606        return html->engine->allow_frameset;   
5607}
5608
5609void
5610gtk_html_print_set_master (GtkHTML *html, GnomePrintJob *print_master)
5611{
5612        html->priv->print_master = print_master;
5613}
5614
5615void
5616gtk_html_images_ref (GtkHTML *html)
5617{
5618        html_image_factory_ref_all_images (HTML_IMAGE_FACTORY (html->engine->image_factory));
5619}
5620
5621void
5622gtk_html_images_unref (GtkHTML *html)
5623{
5624        html_image_factory_unref_all_images (HTML_IMAGE_FACTORY (html->engine->image_factory));
5625}
5626
5627void
5628gtk_html_image_ref (GtkHTML *html, const gchar *url)
5629{
5630        html_image_factory_ref_image_ptr (HTML_IMAGE_FACTORY (html->engine->image_factory), url);
5631}
5632
5633void
5634gtk_html_image_unref (GtkHTML *html, const gchar *url)
5635{
5636        html_image_factory_unref_image_ptr (HTML_IMAGE_FACTORY (html->engine->image_factory), url);
5637}
5638
5639void
5640gtk_html_image_preload (GtkHTML *html, const gchar *url)
5641{
5642        html_image_factory_register (HTML_IMAGE_FACTORY (html->engine->image_factory), NULL, url, FALSE);
5643}
5644
5645void
5646gtk_html_set_blocking (GtkHTML *html, gboolean block)
5647{
5648        html->engine->block = block;
5649}
5650
5651void
5652gtk_html_set_images_blocking (GtkHTML *html, gboolean block)
5653{
5654        html->engine->block_images = block;
5655}
5656
5657gint
5658gtk_html_print_get_pages_num (GtkHTML *html, GnomePrintContext *print_context, gdouble header_height, gdouble footer_height)
5659{
5660        return html_engine_print_get_pages_num (html->engine, print_context, header_height, footer_height);
5661}
5662
5663gboolean
5664gtk_html_has_undo (GtkHTML *html)
5665{
5666        return html_undo_has_undo_steps (html->engine->undo);
5667}
5668
5669void
5670gtk_html_drop_undo (GtkHTML *html)
5671{
5672        html_undo_reset (html->engine->undo);
5673}
5674
5675void
5676gtk_html_flush (GtkHTML *html)
5677{
5678        html_engine_flush (html->engine);
5679}
Note: See TracBrowser for help on using the repository browser.