diff -pruN dillo/ChangeLog dillo_tabs/ChangeLog
--- dillo/ChangeLog	2003-11-24 14:32:40.038785544 +0100
+++ dillo_tabs/ChangeLog	2003-11-24 14:32:40.143769584 +0100
@@ -4,6 +4,27 @@ Dillo project
 
 dillo-0.8.0 [??, 2003]
 
+ - * Implemented tabbed browsing, separated 'document' from 'interface' code
+     - Moved all document-specific code into DilloDoc (src/doc.c/h and
+       src/browser.h for definition of DilloDoc)
+     - Added tab browsing as an option (enabled by default, disable
+       with --disable-tabs configure option)
+   * Focus is on location bar on new empty window/tab
+   * Open new window on link-shift-click1, new tab on link-control-click1
+     - optionally open tab on link-click2 (pref tab_on_middle_click=YES)
+   * Implemented experimental frame and iframe support
+     - Added frame and iframe support as an option (enabled by default,
+       disable with --enable-xhtml-dtd=strict configure option)
+     - Added support for named windows/frames (target= and name= parameters)
+   * Added popup menu navigation buttons (back, forward, stop, reload). (can be
+   disabled with preference 'show_popup_navigation=NO', default enabled)
+   * duplicate page (open current page in new window, keyboard ALT-N) and,
+   if tab support is enabled, duplicate tab (open current page in new tab,
+   keyboard ALT-T) options in popup menu.
+   * Added CTRL-R (reload document) accelerator key.
+   * Title of window/tab set to url if file has no title (this can be disabled
+   with the preference 'document_title_use_url=NO', default enabled)
+   Patch: Frank de Lange
  - * Added a right-mouse-button popup for images!
    Patch: Frank de Lange, Eric Gaudet, Jorge Arellano
  - * Made main document window grab focus on startup, fullwindow,
diff -pruN dillo/Makefile.am dillo_tabs/Makefile.am
--- dillo/Makefile.am	2003-11-24 14:32:40.054783112 +0100
+++ dillo_tabs/Makefile.am	2003-11-24 14:32:40.164766392 +0100
@@ -3,3 +3,4 @@ SUBDIRS = doc src dpid dpi
 EXTRA_DIST = ChangeLog.old dillorc
 
 sysconf_DATA = dillorc
+pkgdata_DATA = dillo.html
diff -pruN dillo/README dillo_tabs/README
--- dillo/README	2003-11-24 14:32:40.055782960 +0100
+++ dillo_tabs/README	2003-11-24 14:32:40.164766392 +0100
@@ -40,7 +40,6 @@ is to read the help! (there's a link fro
 
   Here's a list of some well known problems:
 
-      * no FRAMES rendering
       * no FTP
       * no https
 
diff -pruN dillo/configure.in dillo_tabs/configure.in
--- dillo/configure.in	2003-11-24 14:32:40.055782960 +0100
+++ dillo_tabs/configure.in	2003-11-24 14:32:40.165766240 +0100
@@ -14,7 +14,7 @@ AC_ARG_WITH(jpeg-lib, [  --with-jpeg-lib
 AC_ARG_WITH(jpeg-inc, [  --with-jpeg-inc=DIR     Specify where to find libjpeg's headers], LIBJPEG_INCDIR=$withval)
 
 AC_ARG_ENABLE(efence, [  --enable-efence         Try to compile and run with Electric Fence],
-                    , enable_fence=no)
+                    , enable_efence=no)
 AC_ARG_ENABLE(gprof,  [  --enable-gprof          Try to compile and run with profiling enabled],
                     , enable_gprof=no)
 AC_ARG_ENABLE(insure, [  --enable-insure         Try to compile and run with Insure++],
@@ -25,6 +25,7 @@ AC_ARG_ENABLE(cookies,[  --disable-cooki
                     , enable_cookies=yes)
 AC_ARG_ENABLE(ipv6,   [  --enable-ipv6           Build with support for IPv6], , )
 
+
 AC_PROG_CC
 AM_PROG_CC_STDC
 AC_PROG_RANLIB
@@ -225,3 +226,19 @@ AC_SUBST(src doc bin util lib)
 
 AC_OUTPUT(Makefile dpid/Makefile dpi/Makefile doc/Makefile src/Makefile src/IO/Makefile)
 
+dnl ----------------------
+dnl Show configure summary
+dnl ----------------------
+echo ""
+echo "Finished configure:"
+echo "        cookies: $enable_cookies";
+dnl echo "           ipv6: $enable_ipv6";
+echo "         efence: $enable_efence";
+echo "          gprof: $enable_gprof";
+echo "         insure: $enable_insure";
+
+echo "         prefix: $prefix";
+                                                                                
+echo ""
+echo "Run configure --help for explanation of these options,"
+echo "otherwise type 'make' to build Dillo."
diff -pruN dillo/dillo.html dillo_tabs/dillo.html
--- dillo/dillo.html	1970-01-01 01:00:00.000000000 +0100
+++ dillo_tabs/dillo.html	2003-11-24 14:32:40.274749672 +0100
@@ -0,0 +1,398 @@
+<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>
+<html>
+<head>
+<title>Splash screen for dillo-0.8.0-pre</title>
+</head>
+<body bgcolor='#778899' text='#000000' link='#000000' vlink='#000000'>
+
+
+<!--                          -->
+<!--   the head of the page   -->
+<!--                          -->
+
+<table width='100%' border='0' cellspacing='1' cellpadding='3'>
+ <tr><td>
+   <table border='1' cellspacing='1' cellpadding='0'>
+    <tr>
+    <td bgcolor='#000000'>
+     <table width='100%' border='0' bgcolor='#ffffff'>
+     <tr>
+       <td valign='top' align='left'>
+         <h1>&nbsp;Welcome to Dillo 0.8.0-pre&nbsp;</h1>
+       </td>
+     </tr>
+     </table>
+    </tr>
+   </table>
+ </td></tr>
+</table>
+
+<!-- a small horizontal spacer -->
+<br>
+
+
+<!--                                   -->
+<!-- the main layout table, definition -->
+<!--                                   -->
+
+<table width='100%' border='0' cellspacing='0' cellpadding='0'>
+<tr><td valign='top' width='150' align='center'>
+
+
+<!--                        -->
+<!--   The navigation bar   -->
+<!--                        -->
+
+<table border='0' cellspacing='0' cellpadding='0' width='140' bgcolor='#000000'>
+<tr>
+  <td>
+    <table width='100%' border='0' cellspacing='1' cellpadding='3'>
+    <tr>
+      <td colspan='1' bgcolor='#CCCCCC'>
+        Dillo
+      </td>
+    </tr>
+    <tr>
+      <td bgcolor='#FFFFFF'>
+        <table border='0' cellspacing='0' cellpadding='5'><tr><td>
+        <table border='0' cellspacing='0' cellpadding='2'><tr>
+        <td></td>
+        <td>
+         <a href='http://www.dillo.org/dillo-help.html'>
+         Help</a></td>
+        </tr><tr>
+        <td>&nbsp;&nbsp;</td>
+        <td>
+         <a href='http://www.dillo.org/'>Home</a></td>
+        </tr><tr>
+        <td>&nbsp;&nbsp;</td>
+        <td>
+         <a href='http://www.dillo.org/funding/objectives.html'>
+         Objectives</a></td>
+        </tr><tr>
+        <td>&nbsp;&nbsp;</td>
+        <td>
+         <a href='http://www.dillo.org/ChangeLog.html'>
+         ChangeLog</a></td>
+        </tr><tr>
+        <td>&nbsp;&nbsp;</td>
+        <td>
+         <a href='http://www.dillo.org/interview.html'>
+           Interview</a></td>
+        </tr><tr>
+        <td>&nbsp;&nbsp;</td>
+        <td>
+         <a href='http://www.dillo.org/D_authors.html'>
+         Authors</a></td>
+        </tr></table>
+        </td></tr></table>
+      </td>
+    </tr>
+    </table>
+  </td>
+</tr>
+</table>
+
+<!-- a small horizontal spacer -->
+<br>
+
+<table border='0' cellspacing='0' cellpadding='0' width='140' bgcolor='#000000'>
+<tr>
+  <td>
+    <table width='100%' border='0' cellspacing='1' cellpadding='3'>
+    <tr>
+      <td colspan='1' bgcolor='#CCCCCC'>
+        Magazines
+      </td>
+    </tr>
+    <tr>
+      <td bgcolor='#FFFFFF'>
+        <table border='0' cellspacing='0' cellpadding='5'><tr><td>
+        <table border='0' cellpadding='2'><tr>
+        <td>&nbsp;&nbsp;</td>
+        <td>
+         <a href='http://lwn.net'>LWN</a></td>
+        </tr><tr>
+        <td>&nbsp;&nbsp;</td>
+        <td>
+         <a href='http://slashdot.org/'>Slashdot</a></td>
+        </tr><tr>
+        <td>&nbsp;&nbsp;</td>
+        <td>
+         <a href='http://www.kuro5hin.org/?op=section;section=__all__'>KuroShin</a></td>
+        </tr><tr>
+        <td>&nbsp;&nbsp;</td>
+        <td>
+         <a href='http://www.nexusmagazine.com'>Nexus&nbsp;M.</a></td>
+        </tr><tr>
+        <td>&nbsp;&nbsp;</td>
+        <td>
+         <a href='http://linuxtoday.com/'>Linux Today</a></td>
+        </tr><tr>
+        <td>&nbsp;&nbsp;</td>
+        <td>
+         <a href='http://www.gnu-darwin.org/update.html'>Monster News</a></td>
+        </tr><tr>
+        <td>&nbsp;&nbsp;</td>
+        <td>
+         <a href='http://www.theregister.co.uk/index.html'>The Register</a></td>
+        </tr><tr>
+        <td>&nbsp;&nbsp;</td>
+        <td>
+         <a href='http://www.infoanarchy.org/'>InfoAnarchy</a></td>
+        </tr><tr>
+        <td>&nbsp;&nbsp;</td>
+        <td>
+         <a href='http://www.linuxforkids.org'>Linux4Kids</a></td>
+        </tr></table>
+        </td></tr></table>
+      </td>
+    </tr>
+    </table>
+  </td>
+</tr>
+</table>
+
+<!-- a small horizontal spacer -->
+<br>
+
+<table border='0' cellspacing='0' cellpadding='0' width='140' bgcolor='#000000'>
+<tr>
+  <td>
+    <table width='100%' border='0' cellspacing='1' cellpadding='3'>
+    <tr>
+      <td colspan='1' bgcolor='#CCCCCC'>
+        Additional Stuff
+      </td>
+    </tr>
+    <tr>
+      <td bgcolor='#FFFFFF'>
+        <table border='0' cellspacing='0' cellpadding='5'><tr><td>
+        <table border='0' cellpadding='2'><tr>
+        <td>&nbsp;&nbsp;</td>
+        <td><a href='http://www.google.com/'>Google</a></td>
+        </tr><tr>
+        <td>&nbsp;&nbsp;</td>
+        <td><a href='http://freshmeat.net/'>FreshMeat</a></td>
+        </tr><tr>
+        <td>&nbsp;&nbsp;</td>
+        <td><a href='http://www.gnu.org/gnu/thegnuproject.html'>GNU
+         project</a></td>
+        </tr><tr>
+        <td>&nbsp;&nbsp;</td>
+        <td><a href='http://www.linuxfund.org/'>LinuxFund</a></td>
+        </tr></table>
+        </td></tr></table>
+      </td>
+    </tr>
+    </table>
+  </td>
+</tr>
+</table>
+
+<!-- a small horizontal spacer -->
+<br>
+
+<table border='0' cellspacing='0' cellpadding='0' width='140' bgcolor='#000000'>
+<tr>
+  <td>
+    <table width='100%' border='0' cellspacing='1' cellpadding='3'>
+    <tr>
+      <td colspan='1' bgcolor='#CCCCCC'>
+        Essential Reading
+      </td>
+    </tr>
+    <tr>
+      <td bgcolor='#FFFFFF'>
+        <table border='0' cellspacing='0' cellpadding='5'><tr><td>
+        <table border='0' cellpadding='2'><tr>
+        <td>&nbsp;&nbsp;</td>
+        <td><a href='http://www.violence.de'>Peace&amp;Violence</a></td>
+        </tr></table>
+        </td></tr></table>
+      </td>
+    </tr>
+    </table>
+  </td>
+</tr>
+</table>
+<!-- a small horizontal spacer -->
+<table border='0' width='100%' cellpadding='0' cellspacing='0'><tr><td height='10'></td></tr></table>
+
+
+
+<!--                                                -->
+<!-- the main layout table, a small vertical spacer -->
+<!--                                                -->
+
+</td><td width='20'></td><td valign='top'>
+
+
+<!--                           -->
+<!--   Main Part of the page   -->
+<!--                           -->
+
+<table border='0' cellpadding='0' cellspacing='0' align='center' bgcolor='#000000' width='100%'><tr><td>
+<table border='0' cellpadding='5' cellspacing='1' width='100%'>
+<tr>
+  <td bgcolor='#CCCCCC'>
+    <h4>Free Software</h4>
+  </td>
+</tr><tr>
+  <td bgcolor='#FFFFFF'>
+    <table border='0' cellspacing='0' cellpadding='5'><tr><td>
+ <p>
+ Dillo is Free Software in the terms of the GPL.
+ This means you have four basic freedoms:
+ <ul>
+  <li>Freedom to use the program any way you see fit.
+  <li>Freedom to study and modify the source code.
+  <li>Freedom to make backup copies.
+  <li>Freedom to redistribute it.
+ </ul>
+ The
+<a href='http://www.gnu.org/licenses/gpl.html'>GPL</a>
+ is the legal mechanism that gives you these freedoms.
+It also protects them from being taken away: any derivative work
+based on the program must be under the GPL.
+ <br>
+    </td></tr></table>
+  </td>
+</tr>
+</table>
+</td></tr></table>
+
+<!-- a small horizontal spacer -->
+<br>
+
+<table border='0' cellpadding='0' cellspacing='0' align='center' bgcolor='#000000' width='100%'><tr><td>
+<table border='0' cellpadding='5' cellspacing='1' width='100%'>
+<tr>
+  <td bgcolor='#CCCCCC'>
+    <h4>Release overview</h4>
+    ??, 2003
+  </td>
+</tr><tr>
+  <td bgcolor='#FFFFFF'>
+    <table border='0' cellspacing='0' cellpadding='5'>
+    <tr>
+     <td>
+
+<p>
+[...]
+<p>
+Remember that dillo project uses a release model where every new
+browser shall be better than the former.
+<EM>Keep up with the latest one!</EM>
+
+     </td>
+    </tr></table>
+  </td>
+</tr>
+</table>
+</td></tr></table>
+
+<!-- a small horizontal spacer -->
+<br>
+
+<table border='0' cellpadding='0' cellspacing='0' align='center' bgcolor='#000000' width='100%'><tr><td>
+<table border='0' cellpadding='5' cellspacing='1' width='100%'>
+<tr>
+  <td bgcolor='#CCCCCC'>
+    <h4>ChangeLog highlights</h4>
+    (Extracted from the
+    <a href='http://www.dillo.org/ChangeLog.html'>full
+    ChangeLog</a>)
+  </td>
+</tr><tr>
+  <td bgcolor='#FFFFFF'>
+    <table border='0' cellspacing='0' cellpadding='5'>
+    <tr>
+     <td>
+<p>
+[...]
+<p>
+     </td>
+    </tr></table>
+  </td>
+</tr>
+</table>
+</td></tr></table>
+
+<!-- a small horizontal spacer -->
+<br>
+
+<table border='0' cellpadding='0' cellspacing='0' align='center' bgcolor='#000000' width='100%'><tr><td>
+<table border='0' cellpadding='5' cellspacing='1' width='100%'>
+<tr>
+  <td bgcolor='#CCCCCC'>
+    <h4>Notes</h4>
+  </td>
+</tr><tr>
+  <td bgcolor='#FFFFFF'>
+    <table border='0' cellspacing='0' cellpadding='5'>
+    <tr>
+     <td>
+ <ul>
+   <li> There's a
+     <a href='http://www.dillo.org/dillorc'>dillorc</a>
+     (readable  config)  file within the tarball; It is well commented
+     and  has  plenty  of  options to customize dillo, so <STRONG>copy
+     it</STRONG>  to  your  <STRONG>~/.dillo/</STRONG>  directory, and
+     modify to your taste.
+   <li> There's documentation for developers in the <CODE>/doc</CODE>
+     dir  within  the  tarball;  you can find directions on everything
+     else at the home page.
+   <li> Dillo has context sensitive menus using the
+     right mouse button (available on pages, links, images,
+     and the Back and Forward buttons).
+   <li> Dillo behaves very nicely when browsing local files, images, and HTML.
+     It's also very good for Internet searching (try Google!).
+   <li> This release is mainly intended <strong>for developers</strong>
+        and <em>advanced users</em>
+   <li> Frames, Java and Javascript are not supported.
+ </ul>
+ <br>
+     </td>
+    </tr></table>
+  </td>
+</tr>
+</table>
+</td></tr></table>
+
+<!-- a small horizontal spacer -->
+<table border='0' width='100%' cellpadding='0' cellspacing='0'><tr><td height='10'></td></tr></table>
+
+
+<!--                                                -->
+<!-- the main layout table, a small vertical spacer -->
+<!--                                                -->
+
+</td><td width='20'></td>
+
+
+
+<!--                             -->
+<!--   The right column (info)   -->
+<!--                             -->
+<td valign='top' align='center'>
+</td>
+
+
+<!--                              -->
+<!-- end of the main layout table -->
+<!--                              -->
+
+</tr>
+</table>
+
+<!--               -->
+<!--   footnotes   -->
+<!--               -->
+
+<br><br><center>
+<hr size='2'>
+<hr size='2'>
+</center>
+</body>
+</html>
diff -pruN dillo/dillorc dillo_tabs/dillorc
--- dillo/dillorc	2003-11-24 14:32:40.056782808 +0100
+++ dillo_tabs/dillorc	2003-11-24 14:32:40.165766240 +0100
@@ -3,7 +3,6 @@
 # Copy this file to ~/.dillo/dillorc and edit to your taste.
 # Lines that start with a '#' are comments.
 
-
 #-------------------------------------------------------------------------
 #                             FIRST SECTION                             :)
 #-------------------------------------------------------------------------
@@ -15,6 +14,10 @@ geometry=640x550
 # If you have a lot of memory and a slow CPU, use YES, otherwise use NO
 use_dicache=NO
 
+# Set the splash screen location.
+# Set this to NO to suppress the splash screen on startup.
+# Leave setting commented for default splash screen location.
+# splash=NO
 
 #-------------------------------------------------------------------------
 #                           RENDERING SECTION
@@ -52,8 +55,10 @@ limit_text_width=NO
 # Set the home location
 home=http://www.dillo.org/
 
-# Set search url to use with "s <keywords>".
-# %s is replaced with keywords separated by '+'.
+# Set search url to use with "s <parameters>".
+# %s is replaced by (escaped) parameters.
+# see also keywords section
+# search_url is used as definition for the keyword "search"
 search_url="http://www.google.com/search?q=%s"
 #search_url="http://search.lycos.com/default.asp?query=%s"
 #search_url="http://www.alltheweb.com/search?cat=web&query=%s"
@@ -121,13 +126,55 @@ small_icons=NO
 #show_search=NO
 #show_progress_box=NO
 
+# Show or hide navigation controls (back, forward, etc) in popup menu...
+#show_popup_navigation=NO
+
 # Start dillo windows with a hidden panel?
 fullwindow_start=NO
 
+# Use popup dialogs (YES/NO, default YES). Disable this to use the status
+# bar and location bar instead of popup dialogs for several functions
+# popup_dialogs=NO
+
 # Enabling this will restrain OpenUrl and FindText, but may be required
 # for the ION window manager.
 transient_dialogs=NO
 
+# This defines how to handle the "_blank" target. The setting is also
+# used for link_middle_click. The possible values are:
+#
+# self   : open in the same window
+# window : open in a new window [DEFAULT]
+# tab    : open in a new tab
+# target_blank=window
+
+# Enabling this will make 'load link in new tab' be executed in the
+# background, leaving the focus on the current tab
+# tab_load_in_background=NO
+
+# Show the tab bar (sometimes called the 'gutter') even when there is
+# only a single tab (document) loaded in the browser window?
+# tab_bar_show_single_tab=NO
+
+# Show the tab bar even in full screen mode?
+# tab_bar_show_fullscreen=NO
+
+# When there are more tabs than fit on the tab bar, add scrolling arrows
+# to the tab bar.
+# tab_bar_scroller=NO
+
+# Compress titles in tabs by removing vowels? If this is set to NO,
+# tabs will be shortened instead. This is what it looks like:
+#
+# Normal                Compressed               Shortened
+# The Quick Brown Fox   Th Qck Brwn Fx           The Q...Fox
+# tab_title_compress=YES
+
+# A semicolon-separated string of common prefixes. The compression
+# algorithm begins by trying to discard anything from the title
+# which matches this pattern.
+# compress_common_prefixes="index of ;re: ;fwd: ;www.;welcome to ;the "
+
 # When filling forms, our default behaviour is to submit on enterpress,
 # but only when there's a single text entry (to avoid incomplete submits).
 # OTOH, if you have to fill the same form lots of times, you may find
@@ -140,6 +187,40 @@ enterpress_forces_submit=NO
 generate_submit=NO
 
 #-------------------------------------------------------------------------
+#                        KEY BINDING SECTION
+#-------------------------------------------------------------------------
+
+# This defines custom keyboard navigation keys which are used *in addition*
+# to the default mappings. The example shows how to configure Dillo for the
+# "hjkl" mapping as used by the vi text editor. See the document on
+# keyboard navigation in the doc/ directory for more information.
+# key_bindings = 
+# "binding 'vi-keys' { 
+#	bind 'k' { 'focus_move' (2) } 
+#	bind 'j' { 'focus_move' (3) } 
+#	bind 'h' { 'focus_move' (4) } 
+#	bind 'l' { 'focus_move' (5) } 
+#	} 
+# widget '*dillo-content' binding 'vi-keys'"
+
+#-------------------------------------------------------------------------
+#                        KEYWORDS SECTION
+#-------------------------------------------------------------------------
+
+# Keywords can be used to access sites and locations with short commands
+# in the location bar. As an example, with a keyword like
+# "s:http://www.google.com/search?q=", entering "s dillo" in the location
+# bar will execute a search on google for the word "dillo".
+#
+# keywords = "<keyword>:<url> <keyword>:<url>"
+#             %s is in url is replaced by (escaped) location bar contents
+#             keyword:url pairs should be separated with a single space
+
+# keywords =
+# "s:http://www.google.com/search?q=%s
+#  ~:file:~"
+
+#-------------------------------------------------------------------------
 #                        DEBUG MESSAGES SECTION
 #-------------------------------------------------------------------------
 
@@ -148,7 +229,7 @@ generate_submit=NO
 #show_html_warnings=NO
 
 # The new parser is more accurate detecting HTML errors. 
-# Also pages may look better or worst (after all is bad-formed HTML).
+# Also pages may look better or worse (after all it is bad-formed HTML).
 # Uncomment this line if you want to use the old parser.
 #use_old_parser=YES
 
diff -pruN dillo/doc/Browser_Frames.txt dillo_tabs/doc/Browser_Frames.txt
--- dillo/doc/Browser_Frames.txt	1970-01-01 01:00:00.000000000 +0100
+++ dillo_tabs/doc/Browser_Frames.txt	2003-11-24 14:32:40.154767912 +0100
@@ -0,0 +1,34 @@
+Jul 2003, Frank de Lange <frank@unternet.org>
+last revision: Aug 2003, Frank de Lange <frank@unternet.org>
+
+=======================
+Browser Frames in Dillo
+=======================
+
+Dillo now has frame and iframe support, which is enabled by
+default. You can disable this feature by using the configure option
+--enable-xhtml-dtd=strict, which will create a version of Dillo with
+the old, Lynx/w3m like frame feature. The --enable-xhtml-dtd compile
+option can either be set to 'strict' (no frameset or iframe support),
+'transitional' (iframe support, no frameset support) or 'frameset'
+(both iframe and frameset support, this is the default configuration).
+
+Any combination of nested framesets and iframes is (or should be...)
+supported. Dillo currently does NOT check for frame loops (framesets
+loading themselves in a frame or similar endless loops), so beware
+where you point your browser at. This will be implemented at a later
+stage.
+
+Frame-related preferences
+=========================
+
+  frame_blank_in_tab=([NO]/YES): show frames with "_blank" target in
+                                 new tab (instead of new window)
+
+
+TODO
+====
+
+The 'frameborder="0"' attribute does not completely remove the frame
+border, as dw_gtk_scrolled_frame insists on drawing a focus shadow
+even when the border width is set to 0.
diff -pruN dillo/doc/Browser_Tabs.txt dillo_tabs/doc/Browser_Tabs.txt
--- dillo/doc/Browser_Tabs.txt	1970-01-01 01:00:00.000000000 +0100
+++ dillo_tabs/doc/Browser_Tabs.txt	2003-11-24 14:32:40.155767760 +0100
@@ -0,0 +1,141 @@
+Jun 2003, Frank de Lange <frank@unternet.org>
+
+=====================
+Browser Tabs in Dillo
+=====================
+
+Dillo can display several documents in a single window through the use
+of browser tabs. To use this feature, it has to be compiled in (this
+is the default).
+
+If you do not want to use this feature, you can disable it by using
+the configure option --disable-tabs. This will reduce the size of the
+binary by about 7500 bytes (see below), so it can not be said that the
+tab feature is a space hog...
+
+Tabs can be opened through the File->New Tab menu option, by using the
+accelerator key (normally <CTRL>T) or through the tab popup menu
+(right mouse button click on any tab). A document can be opened in a
+new tab through the link popup menu, by holding down the control key
+when clicking a link, or - if the preference 'tab_on_middle_click' is
+set to 'YES' - by middle-clicking a link. By setting the
+tab_load_in_background preference to YES, tabs can be loaded in the
+background, leaving the current tab in focus. To close a tab, use the
+File->Close Tab menu option or its associated accelerator (normally
+<CTRL>W). You can switch tabs in several ways:
+
+ - by clicking on the tab label (duh...)
+ - by selecting 'Previous Tab' or 'Next Tab' from the View menu
+ - by using the accelerator keys for the View menu options, normally
+   defined as "<CTRL>-," (previous tab) and "<CTRL>-." (next tab)
+
+Tab Preferences
+===============
+
+There are several preference settings related to browser tabs which
+can be used to customize the behaviour of this feature. These are:
+
+ tab_load_in_background (NO/YES; default NO)
+
+  This defines the behaviour of the 'Open Link in new Tab' option from
+  the Link Options popup menu. When this is set to 'YES', the new tab is
+  opened in the background, leaving the current tab unchanged. 
+
+ tab_on_middle_click (NO/YES; default NO)
+
+  Setting this to 'YES' will open a document in a new tab (instead of
+  a new window) on link-middle-click.
+
+ tab_bar_show_fullscreen (NO/YES; default NO)
+
+  This defines whether the tab bar (sometimes also called the 'gutter')
+  will be visible even when the browser is in full screen mode.
+
+ tab_bar_show_single_tab (NO/YES; default NO)
+
+  This defines whether the tab bar will be shown when there is only a
+  single tab loaded in the window. Set it to YES to always show the tab
+  bar, even when there is only one tab in the browser window.
+
+ tab_bar_scroller (NO/YES), default NO)
+
+  This controls the behaviour of the tab bar when there are more tabs than
+  will fit. If this is set to YES, a pair of scrolling arrows will be added
+  and only a limited number of tabs are shown in the tab bar. To get to the
+  other tabs, use the scroll arrows or the accellerator keys (default <CTRL>,
+  for previous tab, <CTRL>. for next tab)
+
+ tab_bar_homogeneous (NO/YES, default YES)
+
+  This defines whether all tabs will be of the same width. Setting it to
+  NO makes each tabs as wide as it needs to be to contain the tab
+  title, setting it to YES makes all tabs of equal width.
+
+ tab_title_compress (NO/YES, default YES)
+
+  This defines whether the tab titles will be compressed to fit the
+  available space in the tab bar. If set to NO, tab titles will be
+  shortened instead. The compression algorithm is configurable
+  using the following two preferences:
+
+   compress_vowels (default: aeiouyåäöøæAEIOUYÅÄÖØÆ)
+
+    This defines which vowels are considered 'expendable' by the tab
+    title compression algorithm. The tab title will be shortened by
+    removing characters matching this pattern, starting from the end
+    of the tab title. Adjust this to taste or language if needed.
+
+   compress_common_prefixes
+      (default: "index of ;re: ;fwd: ;www.;welcome to ;the ")
+
+    semicolon-separated string of common prefixes, the compression
+    algorithm begins by trying to discard anything from the title
+    which matched this pattern.
+
+Of course, these preferences are ONLY available when Dillo has been
+compiled with tab support. If you use a version of Dillo which does
+not have tab support and you have one or more of these preferences in
+your dillorc, it will complain about 'unexpected string constants'
+when it encounters these option settings.
+
+
+Miscellaneous
+=============
+
+More about title compression
+----------------------------
+
+For title compression and shortening to work correctly, Dillo has to
+figure out how wide the average character in the GTK theme used for
+GtkLabel widgets is. There is no easy way to find this information in
+GTK, so a somewhat hackish approach is followed instead. When Dillo
+initiates the tab browser, it temporarily creates a text label with a
+predefined, 'representative' text sample (defined in src/tab:
+D_QUICK_BROWN_FOX, default "The Quick Brown Fox Jumped Over The Lazy
+Dog". This approach works for indo-european languages using the
+single-byte character sets, but I have not tried it with any other
+character sets. So, if you use Dillo with any other character set, you
+might want to change this string to something more representative for
+your language group.
+
+Dillo Binary Sizes with and without tabs
+----------------------------------------
+
+I did some comparisons of binary sizes for Dillo with the tab feature
+enabled and disabled, with several compiler settings, (before) and
+after applying the tab patch. These are the results:
+
+         CFLAGS
+-------------------------------------------------------------------------
+tabs    | -g        -g stripped -O2     -O2 stripped -Os     -Os stripped
+........|----------------------------------------------------------------
+enabled | 3558892   390204      384453  316444       359821  291868
+        |
+disabled| 3468465   381916      374382  308188       350454  284316
+        |
+(before)| 3332856   377920      373773  308000       349141  283424
+
+Versions and settings
+---------------------
+gcc:     3.2.2
+binutils:2.13.90.0.18
diff -pruN dillo/doc/DilloDoc.txt dillo_tabs/doc/DilloDoc.txt
--- dillo/doc/DilloDoc.txt	1970-01-01 01:00:00.000000000 +0100
+++ dillo_tabs/doc/DilloDoc.txt	2003-11-24 14:32:40.155767760 +0100
@@ -0,0 +1,101 @@
+Jun 2003, Frank de lange <frank@unternet.org>
+
+==============================
+DilloDoc: Document abstraction
+==============================
+
+The DilloDoc document abstraction was implemented to make it possible
+to have more than one document bound to a single browser
+window. While the need for this abstraction arose during the
+development of document tabs, it can also be useful for implementing
+full frame support.
+
+The current implementation of DilloDoc is still somewhat
+interface-specific, in that it focuses on the display of several
+'Root' documents in a single browser window (in essence, the use of a
+tabbed browser window). For frames support, it is probably best to
+separate the remaining interface-related items from DilloDoc into a
+separate DilloTab abstraction (partly done).
+
+
+Notes on Naming
+===============
+
+In the context of DilloDoc the word 'document' refers to the contents
+of a single browser tab, a single frame, or any other single display
+widget containing a 'Root' URL. Documents each have their own browsing
+history. To get an overview of the contents of a document, just have a
+look at the definition of DilloDoc in src/browser.h
+
+
+From BrowserWindow to DilloDoc
+==============================
+
+Before DilloDoc came around, both window-related and document-related
+data were kept in a single structure named 'BrowserWindow'. After
+DilloDoc, the document-related stuff moved to DilloDoc while the
+window-related data remains in BrowserWindow. Where BrowserWindow
+used to have a 'docwin' member (containing a GTK_DW_SCROLLED_WINDOW),
+it now has a 'tabbrowser' member (containing a GTK_NOTEBOOK).
+
+DilloDoc has a member 'bw', which points to the BrowserWindow it is
+attached to. Likewise, BrowserWindow has a member 'dd' which points to
+the active DilloDoc in the BrowserWindow. Interface code which used to
+refer to bw->docwin to get to the document was changed to refer to
+dd->bw->docwin instead. In many cases, references to bw->docwin were
+actually not related to interface activity. In those cases the API was
+changed to work with DilloDoc instead of BrowserWindow objects, and
+the reference to bw->docwin was simply replaced by one to dd->docwin.
+
+In the documentation there might still be references to the old
+structure (everything in BrowserWindow). These need to be changed to
+fit the current model.
+
+
+Anatomy of DilloDoc
+===================
+
+See src/browser.h for a definition of DilloDoc. Some specifics of the
+current implementation follow.
+
+
+The Pagemark menu
+-----------------
+
+While DilloDoc generally does not contain interface-related data
+(apart from the GTK_DW_SCROLLED_WINDOW of course), there is currently
+one exception: the pagemarks menu. This menu is created 'on the fly'
+while parsing the document, and it is document specific. Currently,
+the menu is directly attached to the DilloDoc. It is kept alive by a
+gtk_object_reference, so it can be attached to and detached from the
+main browser window popup menu without problems. The reference is
+created in a_Pagemark_new and destroyed in a_Pagemark_destroy.
+
+
+The Progress members
+--------------------
+
+A DilloDoc has several members related to the progress of the
+document. These are used by code in src/doc.c to update the
+BrowserWindow's progress bars if and only if the DilloDoc is the
+'current' document in that window. The members are:
+
+ gint NumImages    - the number of images in this document
+ gint NumImagesGot - the number of images already downloaded
+ gfloat progress   - the size of the current 'Root' document
+ gboolean ready    - TRUE when all data has been downloaded
+
+
+TODO
+====
+
+DilloDoc probably needs to be split up in two parts, one 'pure'
+document abstraction and a DilloTab. The 'pure' version of DilloDoc
+can then be contained in either a DilloTab or a DilloFrame.
+
+DilloDoc currently contains a direct reference to a BrowserWindow, and
+BrowserWindow in turn has a reference to a DilloDoc. For frames, this
+might not be a workable solution, since there will be more than one
+current DilloDoc per BrowserWindow (wrapped in another DilloDoc, which
+contains the FRAMESET).
+
diff -pruN dillo/doc/GtkFrameset.txt dillo_tabs/doc/GtkFrameset.txt
--- dillo/doc/GtkFrameset.txt	1970-01-01 01:00:00.000000000 +0100
+++ dillo_tabs/doc/GtkFrameset.txt	2003-11-24 14:32:40.155767760 +0100
@@ -0,0 +1,115 @@
+Aug 2003, Frank de Lange <frank@unternet.org>
+Last update: Aug 2003
+
+===========
+GtkFrameset
+===========
+
+GtkFrameset is a GTK container widget which implements the XHTML
+FRAMESET tag as defined in the XHTML DTD xhtml-dtd-frameset from 20020801.
+It is also compatible with the HTML 4 definition of FRAMESET as described in
+the HTML 4.01 Specification from 19991224. As a GTK container widget,
+it can be used to contain (and constrain) other widgets. In use,
+GtkFrameset is similar to GtkTable, but there are some notable
+differences.
+
+Why not use Dw? DwTable?
+========================
+
+The problem of frameset rendering is more suited to GTK's approach to
+widget display than Dw's. A frameset is by definition limited in size
+and does not scroll. Dw widgets are presented in a viewport, which
+does not limit their size. This is unnecessary for the display of a
+frameset. A frameset widget should control the dimensions of its
+children, not the other way around. DwTable sizes cells based on
+textual content.
+
+Another reason for choosing GTK over Dw is that GTK is less of a
+moving target than Dw.
+
+Usage hints
+===========
+
+GtkFrameset is quite easy to use in most cases. The
+gtk_container_add() has been overloaded to provide somewhat
+intelligent child placement, in that it fills frames sequentially
+(left-to-right, top-to-bottom). Use gtk_container_add_with_args() to
+set the frame attributes (currently marginwidth, marginheight, border,
+noresize).
+
+See the header file (gtkframeset.h) for the widget-specific
+commands. All commands which take gchar *rows/gchar* columns as
+arguments expect Length or MultiLengths. 
+
+So, to create a frameset with two same-sized columns, use:
+
+ (GtkFrameset *) frameset = gtk_frameset_new("*", "*,*");
+
+This can also be done like this:
+
+ (GtkFrameset *) frameset = gtk_frameset_new(NULL, "*,*");
+
+as GtkFrameset interprets NULL as "*" (one row or column spanning the
+whole frameset).
+
+Any valid Length/MultiLength can be passed as argument. Bare numbers
+are interpreted as pixel values, numbers followed by '%' are
+percentages, number followed by '*' are relative sizes. A bare '*' is
+identical to '1*'.
+
+
+Some notes on child dimensioning
+================================
+
+Starting point is the frameset width and height. First come frames
+with absolute dimensions. No frame can be bigger than the frameset, so
+checks are performed to make sure that absolute frame dimensions do
+not exceed frameset dimensions. When the total of all frames with
+absolute dimensions exceeds the frameset dimensions, the available
+space is divided by ratio to those frames. If there are only
+absolute dimensioned frames the dimensions are changed into percentual
+dimensions by ratio.
+
+Next come frames with percentual dimensions. They get to divide the
+remaining space after all absolute dimensions have been allocated. If
+the total of all percentual dimensions adds up to more than 100, the
+calculation is normalised to 100%.
+
+Last come frames with relative dimensions. They get to divide the
+remaining space after all absolute and percentual dimensions have been
+allocated. All available space is divided by ratio to relative weights
+and allocated to frames. Internally, relative dimensions are
+translated into percentual dimensions.
+
+The calculations are performed in two stages. In the first stage, the
+total weight of absolute, percentual and relative dimensions is
+calculated. All dimensions are translated into either absolute or
+percentual dimensions. The results of these calculations are used in
+the size_allocate callback to calculate the actual widget
+dimensions. When frames are resized, the dimensions are recalculated.
+
+
+Can this be optimised? Sure, but... later...
+
+Source files
+============
+
+Source for GtkFrameset is contained in gtkframeset.[ch]
+
+
+Limitations
+===========
+
+ - The scrolling attribute is not supported yet, as I have not found
+   an easy way to disable scrolling for the docwin.
+ - The frameborder="0" attribute does not completely remove the frame
+   border, as the docwin insists on drawing a shadow border around
+   itself even when its border is set to 0.
+
+TODO
+====
+
+ - implement the scrolling attribute in some way
+ - find a way to stop the docwin from drawing a border around itself
+   when its border attribute is set to 0.
+ 
diff -pruN dillo/doc/Keyboard_Navigation.txt dillo_tabs/doc/Keyboard_Navigation.txt
--- dillo/doc/Keyboard_Navigation.txt	1970-01-01 01:00:00.000000000 +0100
+++ dillo_tabs/doc/Keyboard_Navigation.txt	2003-11-24 14:32:40.156767608 +0100
@@ -0,0 +1,178 @@
+October 2003, Frank de Lange <frank@unternet.org>
+Last modified: 20031120, Frank de Lange <frank@unternet.org>
+
+
+Keyboard Navigation
+===================
+
+You can use the keyboard to navigate links and controls in Dillo. In
+the default configuration, the following keys are used:
+
+ Alt-Up:    link UP
+ Alt-Down:  link DOWN
+ Alt-Left:  link LEFT
+ Alt-Right: link RIGHT
+ Enter:	    follow link or activate GTK+ widget
+ ALT-Enter: popup context menu
+ Escape:    unhighlight link
+
+ The Tab and Shift-Tab key combinations are NOT used for link
+ movement. Their function is to move focus between form elements, the
+ location bar, the search entry (when using status bar search, an
+ optional feature in the tab/frame patch) and other interface
+ elements. These keys are also used to move focus between frames in
+ the frame-capable version of Dillo.
+
+Keyboard navigation tries to minimize the number of scrolling
+movements for the document by highlighting links within the currently
+visible part of the document whenever possible. It selects the next
+link to highlight according to these criteria:
+
+ - if this is the first time keyboard navigation is used on this
+   document, select the first (when moving DOWN or RIGHT) or last (when
+   moving UP or LEFT) link in the currently visible part of the
+   document. If there are no links in the currently visible part of the
+   document, select the first link searching downwards from the currently
+   visible part of the document, or upwards from the end of the document.
+ - if keyboard navigation has been used before on this document, try
+   to highlight the next or previous link (UP, DOWN, LEFT, RIGHT) in the
+   visible part of the document. If the user has scrolled the document
+   after the last keyboard navigation action, start the search in the
+   currently visible part of the document.
+
+Search and keyboard navigation
+------------------------------
+
+Keyboard navigation also works with the Search feature. If search hits
+a link, it will be focused. Press <Enter> in the viewport to follow
+the link. When using the status bar for search (an optional feature in
+the tabbed version of Dillo), press <Esc> to close the search entry
+followed by <Enter> to follow the link. A second <Esc> will
+unhighlight the focused link.
+
+History
+-------
+
+The focus location is saved when navigating to another document in the
+same window or tab. This makes it possible to go back in history and
+continue to the next link on the page.
+
+Form fields
+-----------
+
+The same keys used for link navigation work for form field
+navigation. Do note however that form fields, being embedded GTK+
+widgets, can have their own bindings for some functions. As an
+example, the space bar is bound to the activate function in radio
+buttons. To move between form elements you can use both the normal
+navigation keys as well as the traditional Tab/Shift-Tab
+combination. Tab and Shift-Tab can also be used to move focus to the
+next or previous document in a frameset by skipping through all form
+elements (if any).
+
+Context Menus
+-------------
+
+The link context menu can be popped up by pressing Alt-Enter when a
+link has been highlighted. When no link has been highlighted, the page
+popup menu is popped up instead. Use the cursor keys to navigate the
+popup menu, press Enter to select an option. Escape makes the popup
+menu disappear.
+
+Keyboard bindings
+=================
+
+It is possible to change the keyboard bindings used for keyboard
+navigation, either through editing the dillorc or by adding a section
+to any of the current GTK+ rc-files. The syntax is the same, the only
+difference is the addition of the preference keyword "key_bindings" in
+the dillorc and the fact that the bindings have to be enclosed in
+double quotes.
+
+As an example, this configuration (in gtkrc) uses the numerical keypad
+for link navigation:
+
+binding "keypad-keys" {
+  bind "KP_Add"      { "focus_move" (0) }
+  bind "KP_Subtract" { "focus_move" (1) }
+  bind "KP_8"        { "focus_move" (2) }
+  bind "KP_2"        { "focus_move" (3) }
+  bind "KP_4"        { "focus_move" (4) }
+  bind "KP_6"        { "focus_move" (5) }
+  bind "KP_Enter"    { "focus_activate" () }
+  }
+
+class "GtkDwScrolledFrame" binding "keypad-keys"
+
+You can also use 'widget "*dillo-content" instead of 'class
+"GtkDwScrolledFrame" if you prefer to use widget names:
+
+widget "*dillo-content" binding "keypad-keys"
+
+This one (in dillorc, note the enclosing double quotes, the use of
+single quotes and the preference name 'key_bindings') makes Dillo behave
+like the vi editor when it comes to link navigation:
+
+key_bindings =
+"binding 'vi-keys' {
+  bind 'k' { 'focus_move' (2) }
+  bind 'j' { 'focus_move' (3) }
+  bind 'h' { 'focus_move' (4) }
+  bind 'l' { 'focus_move' (5) }
+  }
+class 'GtkDwScrolledFrame' binding 'vi-keys'"
+
+The numerical values after "focus_move" are actually GTK enumeration
+values, but the current implementation does not support this. It would
+of course look much better to be able to write "focus_move"
+(GTK_DIR_DOWN) instead of "focus_move" (3)...
+
+It is also possible to bind a sequence of actions to a single keypress
+by adding more actions after the first.
+
+
+Internals
+=========
+
+Focus state is kept in the focus_state structure which is tied to the
+viewport. Focus movement is controlled through the gtk_container_focus
+signal, which is handled by the viewport, scrolled_frame and
+scrolled_window. The actual movement of focus within a document is
+initiated by the viewport calling A_Focus_state_focus_next, which
+returns TRUE when it succeeded in focusing a link. Focus is trapped
+within a document for links, but not for form elements (moving focus
+past the first or last form element will pass focus to the next
+container child).
+
+focus_state contains two GList pointers (links and links_y) which
+contain the focusable content for the current document. The links_y
+list is sorted in y.x order. These lists get filled by calls to
+a_Focus_state_focusable_add from the DwPage. The location for
+focusable content is set through a_Focus_state_focusable_allocate,
+which gets called by the _allocate methods for DwPage and other
+widgets. The list points to FocusLink structures, which contain all
+information pertaining focusable content.
+
+There are currently two types of FocusLink: DW_FOCUSABLE_LINK and
+DW_FOCUSABLE_WIDGET. The former is used for anything which points to a
+link, the latter is used for form elements (which are embedded GTK+
+widgets).
+
+Focusable content in a document is tagged as such by using the content
+type DW_CONTENT_FOCUSABLE. This content type can be used as a modifier
+for other content types, eg. a textual link has content type
+DW_CONTENT_TEXT|DW_CONTENT_FOCUSABLE. To get all focusable content in
+a document, use a dw_ext_iterator with a mask of DW_CONTENT_FOCUSABLE.
+
+See the source for more details on how to handle focusable
+content. When in doubt, ask around on the mailing list or contact the author
+(frank@unternet.org).
+
+
+TODO
+====
+
+Add more GTK+ bindings, replacing current key_press event
+handling.
+
+...
diff -pruN dillo/doc/README dillo_tabs/doc/README
--- dillo/doc/README	2003-11-24 14:32:40.047784176 +0100
+++ dillo_tabs/doc/README	2003-11-24 14:32:40.153768064 +0100
@@ -21,6 +21,12 @@
    Selection.txt       Selections, and link activation      Current (?)
    Cookies.txt         Explains how to enable cookies       Current
    Dpid.txt            Dillo plugin daemon                  Current
+   Browser_Tabs.txt    Description of the tab feature       Current
+   Browser_Frames.txt  Description of the frames feature    Current
+   GtkFrameset.txt     The GtkFrameset widget               Current
+   DilloDoc.txt        The DilloDoc document abstraction    Current
+   Keyboard_Naviga...  Explains how to use and configure    Current
+                       keyboard navigation
  --------------------------------------------------------------------------
  [This documents cover dillo's internal working. They're NOT a user manual]
  --------------------------------------------------------------------------
diff -pruN dillo/src/IO/about.c dillo_tabs/src/IO/about.c
--- dillo/src/IO/about.c	2003-11-24 14:32:40.063781744 +0100
+++ dillo_tabs/src/IO/about.c	2003-11-24 14:32:40.172765176 +0100
@@ -14,6 +14,7 @@
 
 #include <config.h>
 #include "Url.h"
+#include "../prefs.h"
 #include "../nav.h"
 #include "../web.h"
 
@@ -24,444 +25,6 @@ struct _SplashInfo {
    gint FD_Write;
 };
 
-
-/*
- * HTML text for startup screen
- */
-static char *Splash=
-"Content-type: text/html\n"
-"\n"
-"<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>\n"
-"<html>\n"
-"<head>\n"
-"<title>Splash screen for dillo-" VERSION "</title>\n"
-"</head>\n"
-"<body bgcolor='#778899' text='#000000' link='#000000' vlink='#000000'>\n"
-"\n"
-"\n"
-"<!--                          -->\n"
-"<!--   the head of the page   -->\n"
-"<!--                          -->\n"
-"\n"
-"<table width='100%' border='0' cellspacing='1' cellpadding='3'>\n"
-" <tr><td>\n"
-"   <table border='1' cellspacing='1' cellpadding='0'>\n"
-"    <tr>\n"
-"    <td bgcolor='#000000'>\n"
-"     <table width='100%' border='0' bgcolor='#ffffff'>\n"
-"     <tr>\n"
-"       <td valign='top' align='left'>\n"
-"         <h1>&nbsp;Welcome to Dillo " VERSION "&nbsp;</h1>\n"
-"       </td>\n"
-"     </tr>\n"
-"     </table>\n"
-"    </tr>\n"
-"   </table>\n"
-" </td></tr>\n"
-"</table>\n"
-"\n"
-"<!-- a small horizontal spacer -->\n"
-"<br>\n"
-"\n"
-"\n"
-"<!--                                   -->\n"
-"<!-- the main layout table, definition -->\n"
-"<!--                                   -->\n"
-"\n"
-"<table width='100%' border='0' cellspacing='0' cellpadding='0'>\n"
-"<tr><td valign='top' width='150' align='center'>\n"
-"\n"
-"\n"
-"<!--                        -->\n"
-"<!--   The navigation bar   -->\n"
-"<!--                        -->\n"
-"\n"
-"<table border='0' cellspacing='0' cellpadding='0' width='140' bgcolor='#000000'>\n"
-"<tr>\n"
-"  <td>\n"
-"    <table width='100%' border='0' cellspacing='1' cellpadding='3'>\n"
-"    <tr>\n"
-"      <td colspan='1' bgcolor='#CCCCCC'>\n"
-"        Dillo\n"
-"      </td>\n"
-"    </tr>\n"
-"    <tr>\n"
-"      <td bgcolor='#FFFFFF'>\n"
-"        <table border='0' cellspacing='0' cellpadding='5'><tr><td>\n"
-"        <table border='0' cellspacing='0' cellpadding='2'><tr>\n"
-"        <td></td>\n"
-"        <td>\n"
-"         <a href='http://www.dillo.org/dillo-help.html'>\n"
-"         Help</a></td>\n"
-"        </tr><tr>\n"
-"        <td>&nbsp;&nbsp;</td>\n"
-"        <td>\n"
-"         <a href='http://www.dillo.org/'>Home</a></td>\n"
-"        </tr><tr>\n"
-"        <td>&nbsp;&nbsp;</td>\n"
-"        <td>\n"
-"         <a href='http://www.dillo.org/funding/objectives.html'>\n"
-"         Objectives</a></td>\n"
-"        </tr><tr>\n"
-"        <td>&nbsp;&nbsp;</td>\n"
-"        <td>\n"
-"         <a href='http://www.dillo.org/ChangeLog.html'>\n"
-"         ChangeLog</a></td>\n"
-"        </tr><tr>\n"
-"        <td>&nbsp;&nbsp;</td>\n"
-"        <td>\n"
-"         <a href='http://www.dillo.org/interview.html'>\n"
-"           Interview</a></td>\n"
-"        </tr><tr>\n"
-"        <td>&nbsp;&nbsp;</td>\n"
-"        <td>\n"
-"         <a href='http://www.dillo.org/D_authors.html'>\n"
-"         Authors</a></td>\n"
-"        </tr></table>\n"
-"        </td></tr></table>\n"
-"      </td>\n"
-"    </tr>\n"
-"    </table>\n"
-"  </td>\n"
-"</tr>\n"
-"</table>\n"
-"\n"
-"<!-- a small horizontal spacer -->\n"
-"<br>\n"
-"\n"
-"<table border='0' cellspacing='0' cellpadding='0' width='140' bgcolor='#000000'>\n"
-"<tr>\n"
-"  <td>\n"
-"    <table width='100%' border='0' cellspacing='1' cellpadding='3'>\n"
-"    <tr>\n"
-"      <td colspan='1' bgcolor='#CCCCCC'>\n"
-"        Magazines\n"
-"      </td>\n"
-"    </tr>\n"
-"    <tr>\n"
-"      <td bgcolor='#FFFFFF'>\n"
-"        <table border='0' cellspacing='0' cellpadding='5'><tr><td>\n"
-"        <table border='0' cellpadding='2'><tr>\n"
-"        <td>&nbsp;&nbsp;</td>\n"
-"        <td>\n"
-"         <a href='http://lwn.net'>LWN</a></td>\n"
-"        </tr><tr>\n"
-"        <td>&nbsp;&nbsp;</td>\n"
-"        <td>\n"
-"         <a href='http://slashdot.org/'>Slashdot</a></td>\n"
-"        </tr><tr>\n"
-"        <td>&nbsp;&nbsp;</td>\n"
-"        <td>\n"
-"         <a href='http://www.kuro5hin.org/?op=section;section=__all__'>KuroShin</a></td>\n"
-"        </tr><tr>\n"
-"        <td>&nbsp;&nbsp;</td>\n"
-"        <td>\n"
-"         <a href='http://www.nexusmagazine.com'>Nexus&nbsp;M.</a></td>\n"
-"        </tr><tr>\n"
-"        <td>&nbsp;&nbsp;</td>\n"
-"        <td>\n"
-"         <a href='http://linuxtoday.com/'>Linux Today</a></td>\n"
-"        </tr><tr>\n"
-"        <td>&nbsp;&nbsp;</td>\n"
-"        <td>\n"
-"         <a href='http://www.gnu-darwin.org/update.html'>Monster News</a></td>\n"
-"        </tr><tr>\n"
-"        <td>&nbsp;&nbsp;</td>\n"
-"        <td>\n"
-"         <a href='http://www.theregister.co.uk/index.html'>The Register</a></td>\n"
-"        </tr><tr>\n"
-"        <td>&nbsp;&nbsp;</td>\n"
-"        <td>\n"
-"         <a href='http://www.infoanarchy.org/'>InfoAnarchy</a></td>\n"
-"        </tr><tr>\n"
-"        <td>&nbsp;&nbsp;</td>\n"
-"        <td>\n"
-"         <a href='http://www.linuxforkids.org'>Linux4Kids</a></td>\n"
-"        </tr></table>\n"
-"        </td></tr></table>\n"
-"      </td>\n"
-"    </tr>\n"
-"    </table>\n"
-"  </td>\n"
-"</tr>\n"
-"</table>\n"
-"\n"
-"<!-- a small horizontal spacer -->\n"
-"<br>\n"
-"\n"
-"<table border='0' cellspacing='0' cellpadding='0' width='140' bgcolor='#000000'>\n"
-"<tr>\n"
-"  <td>\n"
-"    <table width='100%' border='0' cellspacing='1' cellpadding='3'>\n"
-"    <tr>\n"
-"      <td colspan='1' bgcolor='#CCCCCC'>\n"
-"        Additional Stuff\n"
-"      </td>\n"
-"    </tr>\n"
-"    <tr>\n"
-"      <td bgcolor='#FFFFFF'>\n"
-"        <table border='0' cellspacing='0' cellpadding='5'><tr><td>\n"
-"        <table border='0' cellpadding='2'><tr>\n"
-"        <td>&nbsp;&nbsp;</td>\n"
-"        <td><a href='http://www.google.com/'>Google</a></td>\n"
-"        </tr><tr>\n"
-"        <td>&nbsp;&nbsp;</td>\n"
-"        <td><a href='http://freshmeat.net/'>FreshMeat</a></td>\n"
-"        </tr><tr>\n"
-"        <td>&nbsp;&nbsp;</td>\n"
-"        <td><a href='http://www.gnu.org/gnu/thegnuproject.html'>GNU\n"
-"         project</a></td>\n"
-"        </tr><tr>\n"
-"        <td>&nbsp;&nbsp;</td>\n"
-"        <td><a href='http://www.linuxfund.org/'>LinuxFund</a></td>\n"
-"        </tr></table>\n"
-"        </td></tr></table>\n"
-"      </td>\n"
-"    </tr>\n"
-"    </table>\n"
-"  </td>\n"
-"</tr>\n"
-"</table>\n"
-"\n"
-"<!-- a small horizontal spacer -->\n"
-"<br>\n"
-"\n"
-"<table border='0' cellspacing='0' cellpadding='0' width='140' bgcolor='#000000'>\n"
-"<tr>\n"
-"  <td>\n"
-"    <table width='100%' border='0' cellspacing='1' cellpadding='3'>\n"
-"    <tr>\n"
-"      <td colspan='1' bgcolor='#CCCCCC'>\n"
-"        Essential Reading\n"
-"      </td>\n"
-"    </tr>\n"
-"    <tr>\n"
-"      <td bgcolor='#FFFFFF'>\n"
-"        <table border='0' cellspacing='0' cellpadding='5'><tr><td>\n"
-"        <table border='0' cellpadding='2'><tr>\n"
-"        <td>&nbsp;&nbsp;</td>\n"
-"        <td><a href='http://www.violence.de'>Peace&amp;Violence</a></td>\n"
-"        </tr></table>\n"
-"        </td></tr></table>\n"
-"      </td>\n"
-"    </tr>\n"
-"    </table>\n"
-"  </td>\n"
-"</tr>\n"
-"</table>\n"
-"<!-- a small horizontal spacer -->\n"
-"<table border='0' width='100%' cellpadding='0' cellspacing='0'><tr><td height='10'></td></tr></table>\n"
-"\n"
-"\n"
-"\n"
-"<!--                                                -->\n"
-"<!-- the main layout table, a small vertical spacer -->\n"
-"<!--                                                -->\n"
-"\n"
-"</td><td width='20'></td><td valign='top'>\n"
-"\n"
-"\n"
-"<!--                           -->\n"
-"<!--   Main Part of the page   -->\n"
-"<!--                           -->\n"
-"\n"
-"<table border='0' cellpadding='0' cellspacing='0' align='center' bgcolor='#000000' width='100%'><tr><td>\n"
-"<table border='0' cellpadding='5' cellspacing='1' width='100%'>\n"
-"<tr>\n"
-"  <td bgcolor='#CCCCCC'>\n"
-"    <h4>Free Software</h4>\n"
-"  </td>\n"
-"</tr><tr>\n"
-"  <td bgcolor='#FFFFFF'>\n"
-"    <table border='0' cellspacing='0' cellpadding='5'><tr><td>\n"
-" <p>\n"
-" Dillo is Free Software in the terms of the GPL.\n"
-" This means you have four basic freedoms:\n"
-" <ul>\n"
-"  <li>Freedom to use the program any way you see fit.\n"
-"  <li>Freedom to study and modify the source code.\n"
-"  <li>Freedom to make backup copies.\n"
-"  <li>Freedom to redistribute it.\n"
-" </ul>\n"
-" The\n"
-"<a href='http://www.gnu.org/licenses/gpl.html'>GPL</a>\n"
-" is the legal mechanism that gives you these freedoms.\n"
-"It also protects them from being taken away: any derivative work\n"
-"based on the program must be under the GPL.\n"
-" <br>\n"
-"    </td></tr></table>\n"
-"  </td>\n"
-"</tr>\n"
-"</table>\n"
-"</td></tr></table>\n"
-"\n"
-"<!-- a small horizontal spacer -->\n"
-"<br>\n"
-"\n"
-"<table border='0' cellpadding='0' cellspacing='0' align='center' bgcolor='#000000' width='100%'><tr><td>\n"
-"<table border='0' cellpadding='5' cellspacing='1' width='100%'>\n"
-"<tr>\n"
-"  <td bgcolor='#CCCCCC'>\n"
-"    <h4>Release overview</h4>\n"
-"    ??, 2003\n"
-"  </td>\n"
-"</tr><tr>\n"
-"  <td bgcolor='#FFFFFF'>\n"
-"    <table border='0' cellspacing='0' cellpadding='5'>\n"
-"    <tr>\n"
-"     <td>\n"
-"\n"
-"<p>\n"
-"[...]\n"
-"<p>\n"
-"Remember that dillo project uses a release model where every new\n"
-"browser shall be better than the former.\n"
-"<EM>Keep up with the latest one!</EM>\n"
-"\n"
-"     </td>\n"
-"    </tr></table>\n"
-"  </td>\n"
-"</tr>\n"
-"</table>\n"
-"</td></tr></table>\n"
-"\n"
-"<!-- a small horizontal spacer -->\n"
-"<br>\n"
-"\n"
-"<table border='0' cellpadding='0' cellspacing='0' align='center' bgcolor='#000000' width='100%'><tr><td>\n"
-"<table border='0' cellpadding='5' cellspacing='1' width='100%'>\n"
-"<tr>\n"
-"  <td bgcolor='#CCCCCC'>\n"
-"    <h4>ChangeLog highlights</h4>\n"
-"    (Extracted from the\n"
-"    <a href='http://www.dillo.org/ChangeLog.html'>full\n"
-"    ChangeLog</a>)\n"
-"  </td>\n"
-"</tr><tr>\n"
-"  <td bgcolor='#FFFFFF'>\n"
-"    <table border='0' cellspacing='0' cellpadding='5'>\n"
-"    <tr>\n"
-"     <td>\n"
-"<p>\n"
-"[...]\n"
-"<p>\n"
-"     </td>\n"
-"    </tr></table>\n"
-"  </td>\n"
-"</tr>\n"
-"</table>\n"
-"</td></tr></table>\n"
-"\n"
-"<!-- a small horizontal spacer -->\n"
-"<br>\n"
-"\n"
-"<table border='0' cellpadding='0' cellspacing='0' align='center' bgcolor='#000000' width='100%'><tr><td>\n"
-"<table border='0' cellpadding='5' cellspacing='1' width='100%'>\n"
-"<tr>\n"
-"  <td bgcolor='#CCCCCC'>\n"
-"    <h4>Notes</h4>\n"
-"  </td>\n"
-"</tr><tr>\n"
-"  <td bgcolor='#FFFFFF'>\n"
-"    <table border='0' cellspacing='0' cellpadding='5'>\n"
-"    <tr>\n"
-"     <td>\n"
-" <ul>\n"
-"   <li> There's a\n"
-"     <a href='http://www.dillo.org/dillorc'>dillorc</a>\n"
-"     (readable  config)  file within the tarball; It is well commented\n"
-"     and  has  plenty  of  options to customize dillo, so <STRONG>copy\n"
-"     it</STRONG>  to  your  <STRONG>~/.dillo/</STRONG>  directory, and\n"
-"     modify to your taste.\n"
-"   <li> There's documentation for developers in the <CODE>/doc</CODE>\n"
-"     dir  within  the  tarball;  you can find directions on everything\n"
-"     else at the home page.\n"
-"   <li> Dillo has context sensitive menus using the\n"
-"     right mouse button (available on pages, links, images,\n"
-"     and the Back and Forward buttons).\n"
-"   <li> Dillo behaves very nicely when browsing local files, images, and HTML.\n"
-"     It's also very good for Internet searching (try Google!).\n"
-"   <li> This release is mainly intended <strong>for developers</strong>\n"
-"        and <em>advanced users</em>\n"
-"   <li> Frames, Java and Javascript are not supported.\n"
-" </ul>\n"
-" <br>\n"
-"     </td>\n"
-"    </tr></table>\n"
-"  </td>\n"
-"</tr>\n"
-"</table>\n"
-"</td></tr></table>\n"
-"\n"
-"<!-- a small horizontal spacer -->\n"
-"<table border='0' width='100%' cellpadding='0' cellspacing='0'><tr><td height='10'></td></tr></table>\n"
-"\n"
-"\n"
-"<!--                                                -->\n"
-"<!-- the main layout table, a small vertical spacer -->\n"
-"<!--                                                -->\n"
-"\n"
-"</td><td width='20'></td>\n"
-"\n"
-"\n"
-"\n"
-"<!--                             -->\n"
-"<!--   The right column (info)   -->\n"
-"<!--                             -->\n"
-"<td valign='top' align='center'>\n"
-"</td>\n"
-"\n"
-"\n"
-"<!--                              -->\n"
-"<!-- end of the main layout table -->\n"
-"<!--                              -->\n"
-"\n"
-"</tr>\n"
-"</table>\n"
-"\n"
-"<!--               -->\n"
-"<!--   footnotes   -->\n"
-"<!--               -->\n"
-"\n"
-"<br><br><center>\n"
-"<hr size='2'>\n"
-"<hr size='2'>\n"
-"</center>\n"
-"</body>\n"
-"</html>\n";
-
-
-
-/*
- * Send the splash screen through the IO using a pipe.
- */
-static gint About_send_splash(ChainLink *Info, DilloUrl *Url)
-{
-   gint SplashPipe[2];
-   IOData_t *io1;
-   SplashInfo_t *SpInfo;
-
-   if (pipe(SplashPipe))
-      return -1;
-
-   SpInfo = g_new(SplashInfo_t, 1);
-   SpInfo->FD_Read  = SplashPipe[0];
-   SpInfo->FD_Write = SplashPipe[1];
-   Info->LocalKey = SpInfo;
-
-   /* send splash */
-   io1 = a_IO_new(IOWrite, SpInfo->FD_Write);
-   a_IO_set_buf(io1, Splash, strlen(Splash));
-   io1->Flags |= (IOFlag_ForceClose + IOFlag_SingleWrite);
-   a_Chain_link_new(Info, a_About_ccc, BCK, a_IO_ccc, 1, 1);
-   a_Chain_bcb(OpStart, Info, io1, NULL);
-   a_Chain_bcb(OpSend, Info, io1, NULL);
-
-   /* Tell the cache to receive answer */
-   a_Chain_fcb(OpSend, Info, (void *)SpInfo->FD_Read, NULL);
-   return SpInfo->FD_Read;
-}
-
 /*
  * Push the right URL for each supported "about"
  * ( Data1 = Requested URL; Data2 = Web structure )
@@ -476,11 +39,9 @@ static gint About_get(ChainLink *Info, v
 
    tail = URL_PATH(Url);
 
-   if (!strcmp(tail, "splash")) {
-      return About_send_splash(Info, Url);
-   }
-
-   if (!strcmp(tail, "jwz"))
+   if (!strcmp(tail, "splash"))
+      loc = prefs.splash;
+   else if (!strcmp(tail, "jwz"))
       loc = "http://www.jwz.org/";
    else if (!strcmp(tail, "raph"))
       loc = "http://www.levien.com/";
@@ -496,7 +57,7 @@ static gint About_get(ChainLink *Info, v
       loc = "http://www.google.com/";
 
    LocUrl = a_Url_new(loc, NULL, 0, 0);
-   a_Nav_push(web->bw, LocUrl);
+   a_Nav_push(web->dd, LocUrl);
    a_Url_free(LocUrl);
    return -1;
 }
diff -pruN dillo/src/IO/dpi.c dillo_tabs/src/IO/dpi.c
--- dillo/src/IO/dpi.c	2003-11-24 14:32:40.064781592 +0100
+++ dillo_tabs/src/IO/dpi.c	2003-11-24 14:32:40.172765176 +0100
@@ -250,7 +250,7 @@ static void Dpi_parse_token(conn_data_t 
    }
 
    tag = g_strndup(conn->Tok, conn->TokSize);
-   MSG("Dpi_parse_token: [%s]\n", tag);
+   _MSG("Dpi_parse_token: [%s]\n", tag);
    g_free(tag);
 
    cmd = Get_attr_value(conn->Tok, conn->TokSize, "cmd");
@@ -301,7 +301,7 @@ static void Dpi_process_io(int Op, void 
       }
 
    } else if (Op == IOClose) {
-      MSG("Dpi: [Dpi_process_io] IOClose\n");
+      _MSG("Dpi: [Dpi_process_io] IOClose\n");
    }
 }
 
@@ -397,11 +397,11 @@ gchar *Dpi_get_dpid_uds_dir(void)
          *p = 0;
       if (access(dpid_uds_dir, F_OK) == 0)
          p = g_strdup(dpid_uds_dir);                        ///
-         MSG("Dpi_get_dpid_uds_dir:: %s\n", p);         ///
+         _MSG("Dpi_get_dpid_uds_dir:: %s\n", p);         ///
          return p;                                          ///
    }
 
-   MSG("Dpi_get_dpid_uds_dir: %s \n", g_strerror(errno));
+   _MSG("Dpi_get_dpid_uds_dir: %s \n", g_strerror(errno));
    return NULL;
 }
 
@@ -528,7 +528,7 @@ static gint Dpi_connect_socket(gchar *se
 
    /* Query dpid for the UDS name for this server */
    server_uds_name = Dpi_get_server_uds_name(server_name);
-   MSG("server_uds_name = [%s]\n", server_uds_name);
+   _MSG("server_uds_name = [%s]\n", server_uds_name);
 
    if (access(server_uds_name, F_OK) != 0) {
       MSG("server socket was NOT found\n");
diff -pruN dillo/src/IO/file.c dillo_tabs/src/IO/file.c
--- dillo/src/IO/file.c	2003-11-24 14:32:40.065781440 +0100
+++ dillo_tabs/src/IO/file.c	2003-11-24 14:32:40.173765024 +0100
@@ -646,7 +646,7 @@ static GString *File_dir2html(DilloDir *
 static void File_not_found_msg(DilloWeb *web, const char *filename, int fd)
 {
    if ( web->flags & WEB_RootUrl )
-      a_Interface_msg(web->bw, "ERROR: Can't find %s %s",
+      a_Interface_msg(web->dd->bw, "ERROR: Can't find %s %s",
                          (fd == -2) ? "" : "file", filename);
    else
       DEBUG_MSG(4, "Warning: Can't find <%s>\n", filename);
diff -pruN dillo/src/IO/http.c dillo_tabs/src/IO/http.c
--- dillo/src/IO/http.c	2003-11-24 14:32:40.065781440 +0100
+++ dillo_tabs/src/IO/http.c	2003-11-24 14:32:40.174764872 +0100
@@ -46,10 +46,10 @@
 #define BW_MSG(web, root, fmt...)                                     \
 G_STMT_START {                                                        \
    if (a_Web_valid((web)) && (!(root) || (web)->flags & WEB_RootUrl)) \
-      a_Interface_msg((web)->bw, fmt);                                \
+      a_Interface_msg((web)->dd->bw, fmt);                                \
 } G_STMT_END
 
-#define DEBUG_LEVEL 5
+/*#define DEBUG_LEVEL 5*/
 #include "../debug.h"
 
 
@@ -289,8 +289,10 @@ static int Http_connect_socket(ChainLink
       sin->sin_family = dh->af;
       sin->sin_port = S->port ? htons(S->port) : htons(DILLO_URL_HTTP_PORT);
       memcpy(&sin->sin_addr, dh->data, dh->alen);
+#ifdef DEBUG_LEVEL
       if (a_Web_valid(S->web) && (S->web->flags & WEB_RootUrl))
          DEBUG_MSG(5, "Connecting to %s\n", inet_ntoa(sin->sin_addr));
+#endif
       break;
    }
 #ifdef ENABLE_IPV6
diff -pruN dillo/src/Makefile.am dillo_tabs/src/Makefile.am
--- dillo/src/Makefile.am	2003-11-24 14:32:40.067781136 +0100
+++ dillo_tabs/src/Makefile.am	2003-11-24 14:32:40.176764568 +0100
@@ -1,5 +1,5 @@
-AM_CPPFLAGS=-DDILLORC_SYS='"$(sysconfdir)/dillorc"'
-
+AM_CPPFLAGS=-DDILLORC_SYS='"$(sysconfdir)/dillorc"' \
+            -DDILLO_SPLASH='"$(datadir)/dillo/dillo.html"'
 SUBDIRS = IO
 
 bin_PROGRAMS = dillo
@@ -18,6 +18,8 @@ dillo_SOURCES = \
 	capi.c \
 	capi.h \
 	debug.h \
+	doc.c \
+	doc.h \
 	dw.h \
 	dw.c \
 	dw_aligned_page.c \
@@ -60,12 +62,18 @@ dillo_SOURCES = \
 	dw_tooltip.h \
 	dw_widget.c \
 	dw_widget.h \
+	gtkframeset.c \
+	gtkframeset.h \
 	findtext.c \
 	findtext.h \
 	selection.c \
 	selection.h \
+	focus.c \
+	focus.h \
 	web.c	\
 	web.h	\
+	pagemark.c \
+	pagemark.h \
 	progressbar.c \
 	progressbar.h \
 	dillo.c \
@@ -86,6 +94,8 @@ dillo_SOURCES = \
 	image.h \
 	misc.c \
 	misc.h \
+	tab.c \
+	tab.h \
 	interface.h \
 	interface.c \
 	history.c \
diff -pruN dillo/src/bookmark.c dillo_tabs/src/bookmark.c
--- dillo/src/bookmark.c	2003-11-24 14:32:40.068780984 +0100
+++ dillo_tabs/src/bookmark.c	2003-11-24 14:32:40.177764416 +0100
@@ -37,36 +37,36 @@ void a_Bookmarks_init(void)
  * and finally ask it to add a new bookmark.
  * (this is an example of dpi chat)
  */
-void a_Bookmarks_chat_add(BrowserWindow *Bw, char *Cmd, char *answer)
+void a_Bookmarks_chat_add(DilloDoc *Dd, char *Cmd, char *answer)
 {
    char *cmd1 = "<dpi cmd='chat' msg='Hi server'>";
    char *cmd2 = "<dpi cmd='chat' msg='I want to set a bookmark'>";
    char *cmd3 = "<dpi cmd='chat' msg='Sure it is!'>";
-   static BrowserWindow *bw = NULL;
+   static DilloDoc *dd = NULL;
    static gchar *cmd4 = NULL;
 
    _MSG("a_Bookmarks_chat_add\n answer=%s\n", answer ? answer : "(null)");
 
-   if (Bw)
-      bw = Bw;
+   if (Dd)
+      dd = Dd;
    if (!cmd4 && Cmd)
       cmd4 = g_strdup(Cmd);
 
    if (!answer) {
-      a_Capi_dpi_send_cmd(NULL, bw, cmd1, "bookmarks", 1);
+      a_Capi_dpi_send_cmd(NULL, dd, cmd1, "bookmarks", 1);
 
    } else {
       /* we have an answer */
       if (answer) {
          if (*answer == 'H') {
             /* "Hi browser" */
-            a_Capi_dpi_send_cmd(NULL, bw, cmd2, "bookmarks", 0);
+            a_Capi_dpi_send_cmd(NULL, dd, cmd2, "bookmarks", 0);
          } else if (*answer == 'I') {
             /* "Is it worth?" */
-            a_Capi_dpi_send_cmd(NULL, bw, cmd3, "bookmarks", 0);
+            a_Capi_dpi_send_cmd(NULL, dd, cmd3, "bookmarks", 0);
          } else if (*answer == 'O') {
             /* "OK, send it!" */
-            a_Capi_dpi_send_cmd(NULL, bw, cmd4, "bookmarks", 0);
+            a_Capi_dpi_send_cmd(NULL, dd, cmd4, "bookmarks", 0);
             g_free(cmd4);
             cmd4 = NULL;
          }
@@ -77,15 +77,12 @@ void a_Bookmarks_chat_add(BrowserWindow 
 /*
  * Add the new bookmark through the bookmarks server
  */
-void a_Bookmarks_add(GtkWidget *widget, gpointer client_data)
+void a_Bookmarks_add(DilloDoc *dd, DilloUrl *url)
 {
-   BrowserWindow *bw = (BrowserWindow *)client_data;
    gchar *title, *t;
-   DilloUrl *url;
    gchar *cmd = NULL;
 
-   url = a_Menu_popup_get_url(bw);
-   g_return_if_fail(url != NULL);
+   g_return_if_fail(url != NULL && dd != NULL);
 
    /* if the page has no title, we'll use the url string */
    title = (gchar *) a_History_get_title_by_url(url, 1);
@@ -93,19 +90,19 @@ void a_Bookmarks_add(GtkWidget *widget, 
 
    cmd = g_strdup_printf("<dpi cmd='add_bookmark' url='%s' title='%s'>",
                          URL_STR(url), title);
-   a_Bookmarks_chat_add(bw, cmd, NULL);
+   a_Bookmarks_chat_add(dd, cmd, NULL);
    g_free(cmd);
 }
 
 /*
  * Request the server to show the bookmarks
  */
-void a_Bookmarks_show(BrowserWindow *bw)
+void a_Bookmarks_show(DilloDoc *dd)
 {
    DilloUrl *url;
 
    url = a_Url_new("dpi:/bm/", NULL, 0, 0);
-   a_Nav_push(bw, url);
+   a_Nav_push(dd, url);
    a_Url_free(url);
 }
 
diff -pruN dillo/src/bookmark.h dillo_tabs/src/bookmark.h
--- dillo/src/bookmark.h	2003-11-24 14:32:40.070780680 +0100
+++ dillo_tabs/src/bookmark.h	2003-11-24 14:32:40.178764264 +0100
@@ -5,10 +5,11 @@
 
 
 void a_Bookmarks_init(void);
-void a_Bookmarks_add(GtkWidget *widget, gpointer client_data);
-void a_Bookmarks_show(BrowserWindow *bw);
+//void a_Bookmarks_add(GtkWidget *widget, gpointer client_data);
+void a_Bookmarks_add(DilloDoc *dd, DilloUrl *url);
+void a_Bookmarks_show(DilloDoc *dd);
 
 /* todo: this is for testing purposes */
-void a_Bookmarks_chat_add(BrowserWindow *Bw, char *Cmd, char *answer);
+void a_Bookmarks_chat_add(DilloDoc *Dd, char *Cmd, char *answer);
 
 #endif /* __BOOKMARK_H__ */
diff -pruN dillo/src/browser.h dillo_tabs/src/browser.h
--- dillo/src/browser.h	2003-11-24 14:32:40.070780680 +0100
+++ dillo_tabs/src/browser.h	2003-11-24 14:32:40.178764264 +0100
@@ -4,17 +4,17 @@
 #include <sys/types.h>
 #include <gdk/gdk.h>
 #include <gtk/gtk.h>
-
-#include "url.h"     /* for DilloUrl */
-
+#include "url.h"       /* for DilloUrl */
+#include "dw_style.h"  /* for DwStyle */
 
 typedef struct _BrowserWindow BrowserWindow;
 typedef struct _DilloMenuPopup DilloMenuPopup;
+typedef struct _DilloDoc DilloDoc;
 
 typedef struct {
     DilloUrl *Url;   /* URL-key for this cache connection */
     gint Flags;      /* {WEB_RootUrl, WEB_Image, WEB_Download} */
-} BwUrls;
+} DdUrls;
 
 /* The popup menus so that we can call them. */
 struct _DilloMenuPopup
@@ -22,14 +22,14 @@ struct _DilloMenuPopup
    GtkWidget *over_page;
    GtkWidget *over_link;
    GtkWidget *over_image;
-   GtkWidget *over_back;
-   GtkWidget *over_forw;
+   GtkWidget *over_history;
+   GtkWidget *over_tab;
    DilloUrl *url;
    DilloUrl *url2;
    GtkWidget *ol_oi_submenu;
 };
 
-/* browser_window contains all widgets to create a single window */
+/* browser_window contains everything related to a window */
 struct _BrowserWindow
 {
    /* Control-Panel handleboxes --used for hiding */
@@ -60,19 +60,105 @@ struct _BrowserWindow
    /* the keyboard accelerator table */
    GtkAccelGroup *accel_group;
 
+   /* the tooltips group */
+   GtkTooltips *tooltips;
+
    /* Popup menu for this BrowserWindow */
    DilloMenuPopup menu_popup;
 
    /* The bookmarks menu so that we can add things to it. */
    GtkWidget *bookmarks_menu;
 
+   /* widgets for dialog boxes off main window */
+   GtkWidget *open_dialog_window;
+   GtkWidget *open_dialog_entry;
+   GtkWidget *openfile_dialog_window;
+   GtkWidget *quit_dialog_window;
+   GtkWidget *save_dialog_window;
+   GtkWidget *save_link_dialog_window;
+   GtkWidget *findtext_dialog;
+   GtkWidget *findtext_dialog_check;
+   GtkWidget *findtext_dialog_entry;
+   GtkWidget *search_dialog_window;
+   GtkWidget *search_dialog_entry;
+   GtkWidget *question_dialog_window;
+   gpointer  question_dialog_data;
+   GtkWidget *viewsource_window;
+   GtkWidget *full_screen_button;
+   GtkWidget *full_screen_off_button;
+
+   /* the container widget (== the tabbrowser) */
+   GtkContainer *container;
+
+   /* tab width adjustment magic. Yes, it is magic. In essence,
+    * this contains the average width of a character in a string
+    * in the current display context and with the current language.
+    * This is used to calculate the expected width of tab labels
+    *
+    * the value is calculated when the tabbrowser is loaded
+    */
+   gfloat tab_label_average_character_width;
+
+   /* the currently displayed document */
+   DilloDoc *dd;
+
+   /* the document to use for findtext */
+   DilloDoc *findtext_root;
+
+   /* the current document with a highlighted search result */
+   DilloDoc *findtext_current;
+
+   /* the last focused document */
+   DilloDoc *current;
+
+   /* the findtext state (TRUE if search string has been found
+    * in the document or any of its children
+    */
+   gboolean found;
+
+   /* the findtext mode (TRUE for incremental search)
+      and the "changed" signal connection_id  */
+   gboolean findtext_incremental;
+   guint findtext_incremental_connection_id;
+};
+
+/* dillo_doc contains all data pertaining to a single document */
+struct _DilloDoc
+{
+   /* the BrowserWindow this document is currently part of */
+   BrowserWindow *bw;
+
+   /* This is the main document widget. (HTML rendering or whatever) */
+   GtkWidget *docwin;
+
+   /* The initial style for the widget */
+   DwStyle *style;
+
+   /* for nested documents, points to parent document */
+   DilloDoc *parent;
+
+   /* the list of documents to search through in findtext */
+   GList *search_list;
+   /* the current index for search_list */
+   guint search_list_index;
+
+   /* This is the frameset widget. */
+   GtkWidget *frameset;
+
+   /* document name (as used in 'target=window_or_frame_name' attribute */
+   gchar *name;
+
+   /* The id for the idle function that sets button sensitivity. */
+   guint sens_idle_id;
+
    /* The "Headings" and "Anchors" menus */
-   GtkWidget *pagemarks_menuitem;
    GtkWidget *pagemarks_menu;
    GtkWidget *pagemarks_last;
 
-   /* This is the main document widget. (HTML rendering or whatever) */
-   GtkWidget *docwin;
+   /* button sensitivity info */
+   gboolean stop_sensitive;
+   gboolean back_sensitive;
+   gboolean forw_sensitive;
 
    /* Current cursor type */
    GdkCursorType CursorType;
@@ -90,29 +176,16 @@ struct _BrowserWindow
    gint NumImages;
    /* Number of different images already loaded */
    gint NumImagesGot;
+   /* the current document progress */
+   gfloat progress;
+   /* 'ready' is true when the root document has finished loading */
+   gboolean ready;
 
    /* List of all Urls requested by this page (and its types) */
-   BwUrls *PageUrls;
+   DdUrls *PageUrls;
    gint NumPageUrls;
    gint MaxPageUrls;
 
-   /* widgets for dialog boxes off main window */
-   GtkWidget *open_dialog_window;
-   GtkWidget *open_dialog_entry;
-   GtkWidget *openfile_dialog_window;
-   GtkWidget *quit_dialog_window;
-   GtkWidget *save_dialog_window;
-   GtkWidget *save_link_dialog_window;
-   GtkWidget *findtext_dialog_window;
-   GtkWidget *findtext_dialog_check;
-   GtkWidget *findtext_dialog_entry;
-   GtkWidget *search_dialog_window;
-   GtkWidget *search_dialog_entry;
-   GtkWidget *question_dialog_window;
-   gpointer  question_dialog_data;
-   GtkWidget *viewsource_window;
-   GtkWidget *full_screen_off_button;
-
    /* Dillo navigation stack (holds indexes to history list) */
    gint *nav_stack;
    gint nav_stack_size;       /* [1 based] */
@@ -126,12 +199,7 @@ struct _BrowserWindow
    /* 'nav_expecting' is true if the last URL is being loaded for
     * the first time and has not gotten the dw yet. */
    gboolean nav_expecting;
-
-   /* The id for the idle function that sets button sensitivity. */
-   guint sens_idle_id;
 };
 
-
-
 #endif /* __BROWSER_H__ */
 
diff -pruN dillo/src/cache.c dillo_tabs/src/cache.c
--- dillo/src/cache.c	2003-11-24 14:32:40.071780528 +0100
+++ dillo_tabs/src/cache.c	2003-11-24 14:32:40.179764112 +0100
@@ -27,6 +27,7 @@
 #include "IO/Url.h"
 #include "IO/IO.h"
 #include "web.h"
+#include "doc.h" 
 #include "dicache.h"
 #include "interface.h"
 #include "nav.h"
@@ -370,7 +371,7 @@ static gint Cache_open_url(DilloWeb *Web
                                  a_Cache_ccc, BCK, cccFunct, 1, 1);
          a_Chain_bcb(OpStart, entry->CCCQuery, (void *)entry->Url, Web);
       } else {
-         a_Interface_msg(Web->bw, "ERROR: unsupported protocol");
+         a_Interface_msg(Web->dd->bw, "ERROR: unsupported protocol");
          a_Cache_ccc(OpAbort, 1, FWD, entry->CCCQuery, NULL, NULL);
          ClientKey = 0; /* aborted */
       }
@@ -715,13 +716,13 @@ static void Cache_process_io(int Op, voi
  * Process redirections (HTTP 30x answers)
  * (This is a work in progress --not finished yet)
  */
-static gint Cache_redirect(CacheData_t *entry, gint Flags, BrowserWindow *bw)
+static gint Cache_redirect(CacheData_t *entry, gint Flags, DilloDoc *dd)
 {
    DilloUrl *NewUrl;
 
    /* if there's a redirect loop, stop now */
    if (entry->Flags & CA_RedirectLoop) {
-     a_Interface_msg(bw, "ERROR: redirect loop for: %s", URL_STR_(entry->Url));
+     a_Interface_msg(dd->bw, "ERROR: redirect loop for: %s", URL_STR_(entry->Url));
      return 0;
    }
 
@@ -735,9 +736,9 @@ static gint Cache_redirect(CacheData_t *
 
       if ( Flags & WEB_RootUrl ) {
          /* Redirection of the main page */
-         a_Nav_remove_top_url(bw);
+         a_Nav_remove_top_url(dd);
          NewUrl =a_Url_new(URL_STR_(entry->Location),URL_STR_(entry->Url),0,0);
-         a_Nav_push(bw, NewUrl);
+         a_Nav_push(dd, NewUrl);
          a_Url_free(NewUrl);
       } else {
          /* Sub entity redirection (most probably an image) */
@@ -794,7 +795,7 @@ static void Cache_process_queue(CacheDat
 
          /* For root URLs, clear the "expecting for reply..." message */
          if (ClientWeb->flags & WEB_RootUrl && !(entry->Flags & CA_MsgErased)){
-            a_Interface_msg(ClientWeb->bw, "");
+            a_Interface_msg(ClientWeb->dd->bw, "");
             entry->Flags |= CA_MsgErased;
          }
          /* For non root URLs, ignore redirections and 404 answers */
@@ -815,7 +816,7 @@ static void Cache_process_queue(CacheDat
          /* Remove client when done */
          if ( (entry->Flags & CA_GotData) ) {
             /* Copy Client data to local vars */
-            void *bw = ClientWeb->bw;
+            void *dd = ClientWeb->dd;
             gint flags = ClientWeb->flags;
             /* We finished sending data, let the client know */
             if (!Client->Callback)
@@ -825,7 +826,7 @@ static void Cache_process_queue(CacheDat
             Cache_client_dequeue(Client, NULLKey);
             --i; /* Keep the index value in the next iteration */
             if ( entry->Flags & CA_Redirect )
-               Cache_redirect(entry, flags, bw);
+               Cache_redirect(entry, flags, dd);
          }
       }
    } /* for */
@@ -876,7 +877,7 @@ static void Cache_remove_interface_clien
    for ( i = 0; (Client = g_slist_nth_data(ClientQueue, i)); ++i ) {
       if ( Client->Url == Url ) {
          Web = Client->Web;
-         a_Interface_remove_client(Web->bw, Client->Key);
+         a_Doc_remove_client(Web->dd, Client->Key);
       }
    }
 }
diff -pruN dillo/src/capi.c dillo_tabs/src/capi.c
--- dillo/src/capi.c	2003-11-24 14:32:40.072780376 +0100
+++ dillo_tabs/src/capi.c	2003-11-24 14:32:40.181763808 +0100
@@ -32,7 +32,7 @@
 
 typedef struct {
    DilloWeb *web;        /* Necessary for <dpi cmd='open_url' ...> */
-   void *bw;
+   DilloDoc *dd;
    gchar *server;
    gchar *datastr;
    gint SockFD;
@@ -75,13 +75,13 @@ void a_Capi_ccc(int Op, int Branch, int 
  * Create a new connection data structure
  */
 static dpi_conn_t *
- Capi_dpi_conn_new(DilloWeb *web, void *bw, char *server, gchar *datastr)
+ Capi_dpi_conn_new(DilloWeb *web, void *dd, char *server, gchar *datastr)
 {
    dpi_conn_t *conn;
 
    conn = g_new(dpi_conn_t, 1);
    conn->web = web;
-   conn->bw = bw;
+   conn->dd = dd;
    conn->server = g_strdup(server);
    conn->datastr = g_strdup(datastr);
    conn->SockFD = -1;
@@ -201,8 +201,8 @@ gint Capi_verify_dpi_url_request(DilloWe
    /* test POST and GET */
    if (strchr(URL_STR(web->url), '?') || URL_DATA_(web->url)) {
       /* safety measure: only allow dpi requests from dpi-generated urls */
-      if (a_Nav_stack_size(web->bw)) {
-         referer = a_History_get_url(NAV_TOP(web->bw));
+      if (a_Nav_stack_size(web->dd)) {
+         referer = a_History_get_url(NAV_TOP(web->dd));
          if (g_strncasecmp(URL_STR(referer), "dpi:/", 5) == 0)
             allow = TRUE;
       }
@@ -244,7 +244,7 @@ gint a_Capi_open_url(DilloWeb *web, CA_C
 
          /* Send dpip command */
          cmd = g_strdup_printf("<dpi cmd='open_url' url='%s'>", url_str);
-         a_Capi_dpi_send_cmd(web, web->bw, cmd, server, 1);
+         a_Capi_dpi_send_cmd(web, web->dd, cmd, server, 1);
          g_free(cmd);
          g_free(server);
       }
@@ -253,14 +253,14 @@ gint a_Capi_open_url(DilloWeb *web, CA_C
               (a_Capi_url_read(web->url, &sz) == NULL ||
                (URL_FLAGS(web->url) & URL_E2EReload)) ) {
       cmd = g_strdup_printf("<dpi cmd='open_url' url='%s'>", url_str);
-      a_Capi_dpi_send_cmd(web, web->bw, cmd, "ftp", 1);
+      a_Capi_dpi_send_cmd(web, web->dd, cmd, "ftp", 1);
       g_free(cmd);
 
    } else if (g_strncasecmp(url_str, "https:/", 7) == 0 && !Call &&
               (a_Capi_url_read(web->url, &sz) == NULL ||
                (URL_FLAGS(web->url) & URL_E2EReload)) ) {
       cmd = g_strdup_printf("<dpi cmd='open_url' url='%s'>", url_str);
-      a_Capi_dpi_send_cmd(web, web->bw, cmd, "https", 1);
+      a_Capi_dpi_send_cmd(web, web->dd, cmd, "https", 1);
       g_free(cmd);
 
    } else {
@@ -286,7 +286,7 @@ static gint Capi_open_url_raw(DilloWeb *
    /* Create a new web structure 'Web' because the original URL
     * inside 'web' may have changed (we keep the original flags) */
    Web = a_Web_new(Url);
-   Web->bw = web->bw;
+   Web->dd = web->dd;
    Web->flags |= web->flags;
    Web->Image = web->Image;
    web->Image = NULL;         /* avoid a double free */
@@ -312,20 +312,20 @@ char *a_Capi_url_read(const DilloUrl *Ur
  * Send a dpi cmd.
  * (For instance: add_bookmark, open_url, send_preferences, ...)
  */
-gint a_Capi_dpi_send_cmd(DilloWeb *web, void *bw, char *cmd, char *server,
+gint a_Capi_dpi_send_cmd(DilloWeb *web, void *dd, char *cmd, char *server,
                          gint flags)
 {
    dpi_conn_t *conn;
    DataBuf *dbuf;
 
    //**/sleep(1);
-   //**/a_Interface_msg(bw, cmd);
+   //**/a_Interface_msg(dd->bw, cmd);
 
    if (flags & 1) {
       /* open a new connection to server */
 
       /* Create a new connection data struct and add it to the list */
-      conn = Capi_dpi_conn_new(web, bw, server, cmd);
+      conn = Capi_dpi_conn_new(web, dd, server, cmd);
       Capi_dpi_conn_ref(conn);
       /* start the CCC operations */
       a_Capi_ccc(OpStart, 1, BCK, conn->InfoSend, conn, server);
@@ -405,7 +405,7 @@ void a_Capi_ccc(int Op, int Branch, int 
          case OpAbort:
             conn = Info->LocalKey;
             if (Data1 && !strcmp(Data1, "ERR_dpid"))
-               a_Interface_msg(conn->bw, "ERROR: can't start dpid daemon!");
+               a_Interface_msg(conn->dd->bw, "ERROR: can't start dpid daemon!");
             Capi_dpi_conn_unref(conn);
             g_free(Info);
             break;
@@ -438,9 +438,9 @@ void a_Capi_ccc(int Op, int Branch, int 
          case OpSend:
             conn = Info->LocalKey;
             if (strcmp(Data2, "send_status_message") == 0) {
-               a_Interface_msg(conn->bw, Data1);
+               a_Interface_msg(conn->dd->bw, Data1);
             } else if (strcmp(Data2, "chat") == 0) {
-               a_Interface_msg(conn->bw, Data1);
+               a_Interface_msg(conn->dd->bw, Data1);
                a_Bookmarks_chat_add(NULL, NULL, Data1);
             } else if (strcmp(Data2, "start_send_page") == 0) {
                /* start the pipe-writing chain */
@@ -452,7 +452,7 @@ void a_Capi_ccc(int Op, int Branch, int 
             } else if (strcmp(Data2, "send_page_2eof") == 0) {
                a_Capi_ccc(OpSend, 3, BCK, conn->InfoPipe, Data1, NULL);
             } else if (strcmp(Data2, "reload_request") == 0) {
-               a_Nav_reload(conn->bw);
+               a_Nav_reload(conn->dd);
             }
             break;
          case OpEnd:
diff -pruN dillo/src/capi.h dillo_tabs/src/capi.h
--- dillo/src/capi.h	2003-11-24 14:32:40.073780224 +0100
+++ dillo_tabs/src/capi.h	2003-11-24 14:32:40.181763808 +0100
@@ -10,7 +10,7 @@
  */
 gint a_Capi_open_url(DilloWeb *web, CA_Callback_t Call, void *CbData);
 char *a_Capi_url_read(const DilloUrl *Url, gint *Size);
-gint a_Capi_dpi_send_cmd(DilloWeb *web, void *bw, char *cmd, char *server,
+gint a_Capi_dpi_send_cmd(DilloWeb *web, void *dd, char *cmd, char *server,
                          gint flags);
 gint a_Capi_url_uses_dpi(gchar *url_str, gchar **server_ptr);
 
diff -pruN dillo/src/commands.c dillo_tabs/src/commands.c
--- dillo/src/commands.c	2003-11-24 14:32:40.077779616 +0100
+++ dillo_tabs/src/commands.c	2003-11-24 14:32:40.184763352 +0100
@@ -19,14 +19,23 @@
 #include "msg.h"
 #include "bookmark.h"
 #include "interface.h"
+#include "tab.h"
 #include "history.h"
 #include "nav.h"
+#include "doc.h"
 #include "misc.h"
 #include "commands.h"
 #include "prefs.h"
 #include "menu.h"
 #include "capi.h"
 #include "selection.h"
+#include "dw_gtk_scrolled_frame.h"  /* for a_Dw_gtk_scrolled_frame_get_type */
+
+#include <gdk/gdkkeysyms.h>
+
+#define DEBUG_EVENT 10
+/*#define DEBUG_LEVEL 10*/
+#include "debug.h"
 
 /* FILE MENU */
 
@@ -35,12 +44,19 @@
  */
 void a_Commands_new_callback(GtkWidget *widget, gpointer client_data)
 {
-   BrowserWindow *nbw, *bw = (BrowserWindow *)client_data;
+   BrowserWindow *bw = (BrowserWindow *)client_data;
 
-   nbw = a_Interface_browser_window_new(bw->main_window->allocation.width,
+   a_Interface_browser_window_new(bw->main_window->allocation.width,
                                   bw->main_window->allocation.height, 0);
-   /* focus the location bar */
-   gtk_widget_grab_focus(nbw->location);
+}
+
+/*
+ * Create a new browser tab
+ */
+void a_Commands_new_tab_callback(GtkWidget *widget, gpointer client_data)
+{
+   BrowserWindow *bw = (BrowserWindow *) client_data;
+   a_Tab_new(bw);
 }
 
 /*
@@ -55,11 +71,16 @@ void a_Commands_openfile_callback(GtkWid
 
 /*
  * Create and show the "Open Url" dialog window
+ *  OR (when prefs.popup_dialogs = FALSE)
+ * focus location bar and select contents
  */
 void a_Commands_openurl_callback(GtkWidget *widget, gpointer client_data)
 {
    BrowserWindow *bw = (BrowserWindow *) client_data;
-   a_Interface_open_dialog(widget, bw);
+   if(prefs.popup_dialogs)
+      a_Interface_open_dialog(widget, bw);
+   else
+      gtk_widget_grab_focus(bw->location);
 }
 
 /*
@@ -79,6 +100,29 @@ void a_Commands_close_callback(GtkWidget
 }
 
 /*
+ * Close current browser tab, and exit dillo if it's the last one.
+ */
+void a_Commands_close_current_tab_callback(GtkWidget *widget,
+                                           gpointer client_data)
+{
+   BrowserWindow *bw = (BrowserWindow *)client_data;
+   a_Tab_close(bw->dd);
+}
+
+/*
+ * Close a browser tab, and exit dillo if it's the last one.
+ *
+ * The difference with a_Commands_close_current_tab_callback is that this
+ * callback is called with a specific dd as parameter, while the former is
+ * called with only a bw
+ */
+void a_Commands_close_tab_callback(GtkWidget *widget, gpointer client_data)
+{
+   DilloDoc *dd = (DilloDoc *)client_data;
+   a_Tab_close(dd);
+}
+
+/*
  * Free memory and quit dillo
  */
 void a_Commands_exit_callback(GtkWidget *widget, gpointer client_data)
@@ -90,21 +134,34 @@ void a_Commands_exit_callback(GtkWidget 
 /* PAGE MENU */
 
 /*
- * Show current page's source code.
+ * Show current root document's source
  */
-void a_Commands_viewsource_callback (GtkWidget *widget, gpointer client_data)
+void a_Commands_viewsource_callback(GtkWidget *widget, gpointer client_data)
 {
-   BrowserWindow *bw = (BrowserWindow *)client_data;
+   BrowserWindow *bw = (BrowserWindow *) client_data;
+
+   a_Commands_doc_viewsource_callback(widget, (gpointer) bw->dd);
+}
+
+/*
+ * Show document's source code.
+ */
+void a_Commands_doc_viewsource_callback (GtkWidget *widget,
+                                         gpointer client_data)
+{
+   DilloDoc *dd = (DilloDoc *)client_data;
    char *buf;
    gint size, xsize, ysize;
    GtkWidget *window, *box1, *button, *scrolled_window, *text;
+   GtkAccelGroup *accel_group;
 
-   if (bw->viewsource_window)
-      gtk_widget_destroy (bw->viewsource_window);
+   if (dd->bw->viewsource_window)
+      gtk_widget_destroy (dd->bw->viewsource_window);
 
    /* -RL :: This code is adapted from testgtk. */
-   if ( !bw->viewsource_window ) {
-      window = bw->viewsource_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+   if ( !dd->bw->viewsource_window ) {
+      window = dd->bw->viewsource_window =
+         gtk_window_new (GTK_WINDOW_TOPLEVEL);
       gtk_window_set_wmclass(GTK_WINDOW(window), "view_source", "Dillo");
       gtk_widget_set_name (window, "text window");
       xsize = (prefs.width < 500) ? prefs.width : 500;
@@ -114,11 +171,15 @@ void a_Commands_viewsource_callback (Gtk
 
       gtk_signal_connect (GTK_OBJECT (window), "destroy",
                           GTK_SIGNAL_FUNC(gtk_widget_destroyed),
-                          &bw->viewsource_window);
+                          &dd->bw->viewsource_window);
 
       gtk_window_set_title (GTK_WINDOW (window), "View Source");
       gtk_container_border_width (GTK_CONTAINER (window), 0);
 
+      /* Create and attach an accel group to the dialog window */
+      accel_group = gtk_accel_group_new();
+      gtk_window_add_accel_group(GTK_WINDOW(window), accel_group);
+
       box1 = gtk_vbox_new (FALSE, 0);
       gtk_container_add (GTK_CONTAINER (window), box1);
       gtk_widget_show (box1);
@@ -136,7 +197,7 @@ void a_Commands_viewsource_callback (Gtk
       gtk_widget_show (text);
 
       gtk_text_freeze (GTK_TEXT (text));
-      buf = a_Capi_url_read(a_History_get_url(NAV_TOP(bw)), &size);
+      buf = a_Capi_url_read(a_History_get_url(NAV_TOP(dd)), &size);
       gtk_text_insert (GTK_TEXT (text), NULL, NULL, NULL, buf, size);
       gtk_text_thaw (GTK_TEXT (text));
 
@@ -144,14 +205,16 @@ void a_Commands_viewsource_callback (Gtk
       gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
                                  GTK_SIGNAL_FUNC(gtk_widget_destroy),
                                  GTK_OBJECT (window));
+      gtk_widget_add_accelerator(button, "clicked", accel_group,
+				 GDK_Escape, 0, GTK_ACCEL_LOCKED);
       gtk_box_pack_start (GTK_BOX (box1), button, FALSE, FALSE, 0);
       GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
       gtk_widget_grab_default (button);
       gtk_widget_show (button);
    }
 
-   if (!GTK_WIDGET_VISIBLE (bw->viewsource_window))
-      gtk_widget_show (bw->viewsource_window);
+   if (!GTK_WIDGET_VISIBLE (dd->bw->viewsource_window))
+      gtk_widget_show (dd->bw->viewsource_window);
 }
 
 /*
@@ -162,13 +225,43 @@ void a_Commands_selectall_callback(GtkWi
 }
 
 /*
- * Create and show the "Find Text" dialog window
+ * Show the "Find Text" dialog for this document
  */
 void a_Commands_findtext_callback(GtkWidget *widget, gpointer client_data)
 {
+   DilloDoc *dd = (DilloDoc *) client_data;
+
+   dd->bw->findtext_incremental = FALSE;
+   dd->bw->findtext_root = dd;
+   dd->bw->current = dd;
+   a_Interface_findtext_show( dd->bw );
+}
+/*
+ * Show the "Find Text" dialog for this page
+ * with incremental search set
+ */
+void a_Commands_findtext_inc_callback(GtkWidget *widget, gpointer client_data)
+{
+   DilloDoc *dd = (DilloDoc *) client_data;
+
+   dd->bw->findtext_incremental = TRUE;
+   dd->bw->findtext_root = dd;
+   dd->bw->current = dd;
+   a_Interface_findtext_show( dd->bw );
+}
+
+/*
+ * Hide the "Find Text" dialog, unhighlight selection
+ */
+void a_Commands_findtext_hide(GtkWidget *widget, gpointer client_data)
+{
    BrowserWindow *bw = (BrowserWindow *) client_data;
 
-   a_Interface_findtext_dialog( bw );
+   a_Interface_findtext_hide( bw );
+   if(bw->current) {
+      gtk_widget_grab_focus(GTK_BIN(bw->current->docwin)->child);
+      bw->current = NULL;
+   }
 }
 
 /*
@@ -180,6 +273,62 @@ void a_Commands_print_callback(GtkWidget
 {
 }
 
+/* DOCUMENT (FRAME/IFRAME) MENU */
+
+/*
+ * Bring up the save doc dialog
+ */
+void a_Commands_doc_save_callback(GtkWidget *widget, gpointer client_data)
+{
+   DilloDoc *dd = (DilloDoc *) client_data;
+
+   a_Menu_popup_set_url(dd, a_History_get_url(NAV_TOP(dd)));
+   a_Interface_save_link_dialog(widget, dd->bw);
+}
+
+/*
+ * Show only this doc in the page
+ */
+void a_Commands_doc_showonly_callback(GtkWidget *widget, gpointer client_data)
+{
+   DilloDoc *dd = (DilloDoc *) client_data;
+
+   a_Nav_push(dd->bw->dd, a_History_get_url(NAV_TOP(dd)));
+}
+
+/*
+ * Abort all active connections for this page
+ * (Downloads MUST keep flowing)
+ */
+void a_Commands_doc_stop_callback(GtkWidget *widget, gpointer client_data)
+{
+   DilloDoc *dd = (DilloDoc *) client_data;
+
+   a_Nav_cancel_expect(dd);
+   a_Doc_stop(dd);
+   a_Interface_set_button_sens(dd->bw);
+   a_Interface_msg(dd->bw, "Stopped");
+}
+
+/*
+ *  Back to previous page
+ */
+void a_Commands_doc_back_callback(GtkWidget *widget, gpointer client_data)
+{
+   DilloDoc *dd = (DilloDoc *) client_data;
+
+   a_Nav_back(dd);
+}
+
+/*
+ * Go to the next page in the history buffer
+ */
+void a_Commands_doc_forw_callback(GtkWidget *widget, gpointer client_data)
+{
+   DilloDoc *dd = (DilloDoc *) client_data;
+
+   a_Nav_forw(dd);
+}
 
 /* TOOLBAR MENU */
 
@@ -190,8 +339,8 @@ void a_Commands_print_callback(GtkWidget
 void a_Commands_stop_callback(GtkWidget *widget, gpointer client_data)
 {
    BrowserWindow *bw = client_data;
-   a_Nav_cancel_expect(bw);
-   a_Interface_stop(bw);
+   a_Nav_cancel_expect(bw->dd);
+   a_Doc_stop(bw->dd);
    a_Interface_set_button_sens(bw);
    a_Interface_msg(bw, "Stopped");
 }
@@ -203,7 +352,7 @@ void a_Commands_back_callback(GtkWidget 
 {
    BrowserWindow *bw = (BrowserWindow *) client_data;
 
-   a_Nav_back(bw);
+   a_Nav_back(bw->dd);
 }
 
 /*
@@ -223,20 +372,10 @@ void a_Commands_navpress_callback(GtkWid
       /* Not used yet */
       break;
    case 3:
-      if (widget == bw->back_button) {
-         if (bw->menu_popup.over_back)
-            gtk_widget_destroy(bw->menu_popup.over_back);
-         bw->menu_popup.over_back = a_Menu_popup_history_new(bw, -1);
-         gtk_menu_popup(GTK_MENU (bw->menu_popup.over_back), NULL, NULL,
-                        NULL, NULL, event->button, event->time);
-
-      } else if (widget == bw->forw_button) {
-         if (bw->menu_popup.over_forw)
-            gtk_widget_destroy(bw->menu_popup.over_forw);
-         bw->menu_popup.over_forw = a_Menu_popup_history_new(bw, +1);
-         gtk_menu_popup(GTK_MENU (bw->menu_popup.over_forw), NULL, NULL,
-                        NULL, NULL, event->button, event->time);
-      }
+      if (widget == bw->back_button)
+         a_Menu_popup_over_history(bw->dd, event, -1);
+      else if (widget == bw->forw_button)
+         a_Menu_popup_over_history(bw->dd, event, 1);
       break;
    }
 }
@@ -253,14 +392,17 @@ void a_Commands_historypress_callback(Gt
    switch (event->button) {
    case 1:
       /* Open link in the same bw */
-      a_Nav_jump_callback(widget, bw, 0);
+      a_Nav_jump_callback(widget, bw->dd, 0);
       break;
    case 2:
       /* Open link in a new bw */
-      a_Nav_jump_callback(widget, bw, 1);
+      a_Nav_jump_callback(widget, bw->dd, 1);
       break;
    case 3:
-      /* Not used */
+      /* Open link in a new tab */
+      a_Nav_jump_callback(widget, bw->dd, 2);
+      break;
+   default:
       break;
    }
 }
@@ -272,17 +414,27 @@ void a_Commands_forw_callback(GtkWidget 
 {
    BrowserWindow *bw = (BrowserWindow *) client_data;
 
-   a_Nav_forw(bw);
+   a_Nav_forw(bw->dd);
+}
+
+/*
+ * Start the reload process for a document
+ */
+void a_Commands_doc_reload_callback(GtkWidget *widget, gpointer client_data)
+{
+   DilloDoc *dd = (DilloDoc *)client_data;
+
+   a_Nav_reload(dd);
 }
 
 /*
- * Start the reload process
+ * Start the reload process for the page
  */
 void a_Commands_reload_callback(GtkWidget *widget, gpointer client_data)
 {
    BrowserWindow *bw = (BrowserWindow *) client_data;
 
-   a_Nav_reload(bw);
+   a_Nav_reload(bw->dd);
 }
 
 /*
@@ -292,7 +444,7 @@ void a_Commands_home_callback(GtkWidget 
 {
    BrowserWindow *bw = (BrowserWindow *) client_data;
 
-   a_Nav_home(bw);
+   a_Nav_home(bw->dd);
 }
 
 /*
@@ -300,11 +452,12 @@ void a_Commands_home_callback(GtkWidget 
  */
 void a_Commands_save_callback(GtkWidget *widget, gpointer client_data)
 {
-   BrowserWindow *bw = (BrowserWindow *) client_data;
+   BrowserWindow *bw = (BrowserWindow *) client_data;  
 
    a_Interface_save_dialog(widget, bw);
 }
 
+
 /*
  * Bring up the save link dialog
  */
@@ -315,15 +468,53 @@ void a_Commands_save_link_callback(GtkWi
    a_Interface_save_link_dialog(widget, bw);
 }
 
+/* VIEW MENU */
+void a_Commands_tab_prev_callback(GtkWidget *widget, gpointer client_data)
+{
+   BrowserWindow *bw = (BrowserWindow *) client_data;
+   if ((guint) gtk_notebook_get_current_page(GTK_NOTEBOOK(bw->container)))
+      gtk_notebook_prev_page(GTK_NOTEBOOK(bw->container));
+   else
+      gtk_notebook_set_page(GTK_NOTEBOOK(bw->container),
+                            g_list_length(gtk_container_children(bw->container))-1);
+}
 
-/* BOOKMARKS MENU */
+void a_Commands_tab_next_callback(GtkWidget *widget, gpointer client_data)
+{
+   BrowserWindow *bw = (BrowserWindow *) client_data;
+   if((guint) gtk_notebook_get_current_page(GTK_NOTEBOOK(bw->container)) !=
+      g_list_length(gtk_container_children(bw->container))-1)
+      gtk_notebook_next_page(GTK_NOTEBOOK(bw->container));
+   else
+      gtk_notebook_set_page(GTK_NOTEBOOK(bw->container), 0);
+}
 
+/* BOOKMARKS MENU */
 /*
- * Add a bookmark to the current bookmark widget.
+ * Add a bookmark for the current doc to the current bookmark widget.
  */
 void a_Commands_addbm_callback(GtkWidget *widget, gpointer client_data)
 {
-   a_Bookmarks_add(widget, client_data);
+   BrowserWindow *bw = (BrowserWindow *) client_data;
+
+   a_Bookmarks_add(bw->dd, a_History_get_url(NAV_TOP(bw->dd)));
+}
+
+void a_Commands_doc_addbm_callback(GtkWidget *widget, gpointer client_data)
+{
+   DilloDoc *dd = (DilloDoc *) client_data;
+
+   a_Bookmarks_add(dd, a_History_get_url(NAV_TOP(dd)));
+}
+
+/*
+ * add a bookmark for the current link
+ */
+void a_Commands_addbm_link_callback(GtkWidget *widget, gpointer client_data)
+{
+   BrowserWindow *bw = (BrowserWindow *) client_data;
+
+   a_Bookmarks_add(bw->dd, a_Menu_popup_get_url(bw->dd));
 }
 
 /*
@@ -333,7 +524,7 @@ void a_Commands_viewbm_callback(GtkWidge
 {
    BrowserWindow *bw = (BrowserWindow *) client_data;
 
-   a_Bookmarks_show(bw);
+   a_Bookmarks_show(bw->dd);
 }
 
 
@@ -348,7 +539,7 @@ void a_Commands_helphome_callback(GtkWid
    BrowserWindow *bw = (BrowserWindow *) client_data;
    DilloUrl *url = a_Url_new(DILLO_HOME, NULL, 0, 0);
 
-   a_Nav_push(bw, url);
+   a_Nav_push(bw->dd, url);
    a_Url_free(url);
 }
 
@@ -362,7 +553,7 @@ void a_Commands_open_link_callback(GtkWi
 {
    BrowserWindow *bw = (BrowserWindow *)client_data;
 
-   a_Nav_push(bw, a_Menu_popup_get_url(bw));
+   a_Nav_push(bw->dd, a_Menu_popup_get_url(bw->dd));
 }
 
 /*
@@ -376,7 +567,21 @@ void a_Commands_open_link_nw_callback(Gt
 
    gdk_window_get_size (bw->main_window->window, &width, &height);
    newbw = a_Interface_browser_window_new(width, height, 0);
-   a_Nav_push(newbw, a_Menu_popup_get_url(bw));
+   a_Nav_push(newbw->dd, a_Menu_popup_get_url(bw->dd));
+}
+
+/*
+ * Duplicate current page in a new window
+ */
+void a_Commands_duplicate_callback(GtkWidget *widget, gpointer client_data)
+{
+   DilloDoc *dd = (DilloDoc *) client_data;
+
+   if(dd->nav_stack_ptr != -1) {
+      a_Menu_popup_set_url(dd, a_History_get_url(NAV_TOP(dd)));
+      a_Commands_open_link_nw_callback(widget, dd->bw);
+   } else
+      a_Commands_new_callback(widget, dd->bw);
 }
 
 /*
@@ -385,6 +590,485 @@ void a_Commands_open_link_nw_callback(Gt
 void a_Commands_select_popup_url_callback(GtkWidget *widget, gpointer data)
 {
    BrowserWindow *bw = (BrowserWindow *)data;
-   a_Selection_set_selection(widget, URL_STR(a_Menu_popup_get_url(bw)));
+   a_Selection_set_selection(widget, URL_STR(a_Menu_popup_get_url(bw->dd)));
+}
+
+/*
+ * Open link in another tab
+ */
+void a_Commands_open_link_nw_tab_callback(GtkWidget *widget, gpointer client_data)
+{
+   BrowserWindow *bw = (BrowserWindow *)client_data;
+   DilloDoc *newdd;
+
+   newdd = a_Doc_new();
+   a_Tab_add(bw, newdd);
+
+   if(!prefs.tab_load_in_background)
+      a_Tab_switch(newdd);
+
+   a_Nav_push(newdd, a_Menu_popup_get_url(bw->dd));
 }
 
+/*
+ * Duplicate current page in a new tab
+ */
+void a_Commands_duplicate_tab_callback(GtkWidget *widget, gpointer client_data)
+{
+   DilloDoc *dd = (DilloDoc *) client_data;
+
+   if(dd->nav_stack_ptr != -1) {
+      a_Menu_popup_set_url(dd, a_History_get_url(NAV_TOP(dd)));
+      a_Commands_open_link_nw_tab_callback(widget, dd->bw);
+   } else
+      a_Commands_new_tab_callback(widget, dd->bw);
+}
+
+/* MAIN WINDOW */
+
+/*
+ * callback for full screen toggle
+ */
+void a_Commands_full_screen_callback (gpointer client_data)
+{
+   BrowserWindow *bw = (BrowserWindow *) client_data;
+   a_Interface_toggle_panel(bw, TRUE);
+}
+
+/*
+ * Handler for double-mouse-clicks that don't belong to the viewport.
+ */
+gint a_Commands_click_callback(gpointer client_data, GdkEventButton *event)
+{
+   BrowserWindow *bw = (BrowserWindow *) client_data;
+   if (event->type == GDK_2BUTTON_PRESS && event->button == 1)
+      a_Interface_toggle_panel(bw, TRUE);
+   return TRUE;
+}
+
+/*
+ * Set default key bindings
+ */
+void a_Commands_init(void)
+{
+   GtkBindingSet *binding_set;
+
+   binding_set = 
+      gtk_binding_set_by_class(gtk_type_class(
+                               a_Dw_gtk_scrolled_frame_get_type()));
+
+   /* content command bindings */
+
+   /* back */
+   command_key (GDK_BackSpace, 0, 
+		DILLO_COMMAND_NAVIGATE_BACK);
+   command_key (GDK_comma, 0, 
+		DILLO_COMMAND_NAVIGATE_BACK);
+
+   /* forward */
+   command_key (GDK_BackSpace, GDK_SHIFT_MASK, 
+		DILLO_COMMAND_NAVIGATE_FORWARD);
+   command_key (GDK_period, 0, 
+		DILLO_COMMAND_NAVIGATE_FORWARD);
+
+   /* reload */
+   command_key (GDK_r, GDK_CONTROL_MASK, 
+		DILLO_COMMAND_NAVIGATE_RELOAD);
+
+   /* home */
+   command_key (GDK_h, GDK_MOD1_MASK, 
+		DILLO_COMMAND_NAVIGATE_HOME);
+
+   /* stop */
+   /* no binding */
+
+   /* duplicate window */
+   command_key (GDK_n, GDK_MOD1_MASK, 
+		DILLO_COMMAND_DUPLICATE_WINDOW);
+
+   /* duplicate tab */
+   command_key (GDK_t, GDK_MOD1_MASK, 
+		DILLO_COMMAND_DUPLICATE_TAB);
+   
+   /* findtext */
+   command_key (GDK_f, GDK_CONTROL_MASK, 
+		DILLO_COMMAND_FINDTEXT);
+
+   /* findtext incremental */
+   command_key (GDK_slash, 0, 
+		DILLO_COMMAND_FINDTEXT_INCREMENTAL);
+
+   /* findtext repeat last */
+   command_key (GDK_g, GDK_CONTROL_MASK, 
+		DILLO_COMMAND_FINDTEXT_REPEAT_LAST_SEARCH);
+
+   /* findtext unhighlight */
+   command_key (GDK_Escape, 0, 
+		DILLO_COMMAND_FINDTEXT_UNHIGHLIGHT);
+
+   /* save */
+   command_key (GDK_s, GDK_CONTROL_MASK, 
+		DILLO_COMMAND_SAVE);
+
+   /* bookmark */
+   command_key (GDK_b, GDK_CONTROL_MASK, 
+		DILLO_COMMAND_BOOKMARK);
+
+   /* new window */
+   command_key (GDK_n, GDK_CONTROL_MASK, 
+		DILLO_COMMAND_NEW_WINDOW);
+
+   /* new tab */
+   command_key (GDK_t, GDK_CONTROL_MASK, 
+		DILLO_COMMAND_NEW_TAB);
+
+   /* open file */
+   command_key (GDK_o, GDK_CONTROL_MASK, 
+		DILLO_COMMAND_OPEN_FILE);
+
+   /* open url */
+   command_key (GDK_l, GDK_CONTROL_MASK, 
+		DILLO_COMMAND_OPEN_URL);
+
+   /* close window */
+   command_key (GDK_q, GDK_CONTROL_MASK, 
+		DILLO_COMMAND_CLOSE_WINDOW);
+
+   /* close tab */
+   command_key (GDK_w, GDK_CONTROL_MASK, 
+		DILLO_COMMAND_CLOSE_TAB);
+
+   /* exit */
+   command_key (GDK_q, GDK_MOD1_MASK, 
+		DILLO_COMMAND_EXIT);
+
+   /* previous tab */
+   command_key (GDK_comma, GDK_CONTROL_MASK, 
+		DILLO_COMMAND_TAB_PREVIOUS);
+
+   /* next tab */
+   command_key (GDK_period, GDK_CONTROL_MASK, 
+                DILLO_COMMAND_TAB_NEXT);
+
+   /* view source */
+   command_key (GDK_u, GDK_MOD1_MASK, 
+		DILLO_COMMAND_VIEW_SOURCE);
+
+   /* view bookmarks */
+   command_key (GDK_semicolon, 0, 
+		DILLO_COMMAND_VIEW_BOOKMARKS);
+   command_key (GDK_b, GDK_MOD1_MASK, 
+		DILLO_COMMAND_VIEW_BOOKMARKS);
+
+   /* focus location entry */
+   command_key (GDK_u, GDK_CONTROL_MASK, 
+		DILLO_COMMAND_FOCUS_LOCATION);
+
+   /* websearch */
+   command_key (GDK_s, GDK_MOD1_MASK, 
+		DILLO_COMMAND_WEBSEARCH);
+
+   /* toggle fullwindow */
+   command_key (GDK_h, GDK_CONTROL_MASK, 
+		DILLO_COMMAND_TOGGLE_FULLWINDOW);
+
+   /* focus bindings */
+
+   /* activate */
+   command_key (GDK_Return, 0,
+		DILLO_COMMAND_FOCUS_ACTIVATE);
+   command_key (GDK_KP_Enter, 0, 
+		DILLO_COMMAND_FOCUS_ACTIVATE);
+   command_key (GDK_Return, GDK_SHIFT_MASK,
+		DILLO_COMMAND_FOCUS_ACTIVATE_NEW_WINDOW);
+   command_key (GDK_KP_Enter, GDK_SHIFT_MASK,
+		DILLO_COMMAND_FOCUS_ACTIVATE_NEW_WINDOW);
+   command_key (GDK_Return, GDK_CONTROL_MASK,
+		DILLO_COMMAND_FOCUS_ACTIVATE_NEW_TAB);
+   command_key (GDK_KP_Enter, GDK_CONTROL_MASK,
+		DILLO_COMMAND_FOCUS_ACTIVATE_NEW_TAB);
+   /* popup */
+   command_key (GDK_Return, GDK_MOD1_MASK,
+		DILLO_COMMAND_FOCUS_POPUP);
+   command_key (GDK_KP_Enter, GDK_MOD1_MASK,
+		DILLO_COMMAND_FOCUS_POPUP);
+   /* hide */
+   command_key (GDK_Escape, 0,
+		DILLO_COMMAND_FOCUS_HIDE);
+
+   /* do NOT bind Tab and Shift-Tab, these need to be bound
+    * at a lower level in the widget tree. If these keys are
+    * bound here, they can NOT be used to move focus between
+    * frames in a frameset document! */
+
+   /* focus up */
+   command_key (GDK_Up, GDK_MOD1_MASK,
+		DILLO_COMMAND_FOCUS_MOVE_UP);
+   command_key (GDK_KP_Up, GDK_MOD1_MASK,
+		DILLO_COMMAND_FOCUS_MOVE_UP);
+   /* focus down */
+   command_key (GDK_Down, GDK_MOD1_MASK,
+		DILLO_COMMAND_FOCUS_MOVE_DOWN);
+   command_key (GDK_KP_Down, GDK_MOD1_MASK,
+		DILLO_COMMAND_FOCUS_MOVE_DOWN);
+   /* focus left */
+   command_key (GDK_Left, GDK_MOD1_MASK,
+		DILLO_COMMAND_FOCUS_MOVE_LEFT);
+   command_key (GDK_KP_Left, GDK_MOD1_MASK,
+		DILLO_COMMAND_FOCUS_MOVE_LEFT);
+   /* focus right */
+   command_key (GDK_Right, GDK_MOD1_MASK,
+		DILLO_COMMAND_FOCUS_MOVE_RIGHT);
+   command_key (GDK_KP_Right, GDK_MOD1_MASK,
+		DILLO_COMMAND_FOCUS_MOVE_RIGHT);
+   
+   /* viewport bindings */
+   
+   /* step back */
+   command_key (GDK_Up, 0,
+		DILLO_COMMAND_SCROLL_UP);
+   command_key (GDK_KP_Up, 0,
+		DILLO_COMMAND_SCROLL_UP);
+   /* step forward */
+   command_key (GDK_Down, 0,
+		DILLO_COMMAND_SCROLL_DOWN);
+   command_key (GDK_KP_Down, 0,
+		DILLO_COMMAND_SCROLL_DOWN);
+   /* step left */
+   command_key (GDK_Left, 0,
+		DILLO_COMMAND_SCROLL_LEFT);
+   command_key (GDK_KP_Left, 0,
+		DILLO_COMMAND_SCROLL_LEFT);
+   /* step right */
+   command_key (GDK_Right, 0,
+		DILLO_COMMAND_SCROLL_RIGHT);
+   command_key (GDK_KP_Right, 0,
+		DILLO_COMMAND_SCROLL_RIGHT);
+   /* page back */
+   command_key (GDK_Page_Up, 0,
+		DILLO_COMMAND_SCROLL_PAGE_UP);
+   command_key (GDK_KP_Page_Up, 0,
+		DILLO_COMMAND_SCROLL_PAGE_UP);
+   command_key (GDK_b, 0,
+		DILLO_COMMAND_SCROLL_PAGE_UP);
+   /* page forward */
+   command_key (GDK_Page_Down, 0,
+		DILLO_COMMAND_SCROLL_PAGE_DOWN);
+   command_key (GDK_KP_Page_Down, 0,
+		DILLO_COMMAND_SCROLL_PAGE_DOWN);
+   command_key (GDK_space, 0,
+		DILLO_COMMAND_SCROLL_PAGE_DOWN);
+   /* page left */
+   command_key (GDK_Page_Up, GDK_CONTROL_MASK,
+		DILLO_COMMAND_SCROLL_PAGE_LEFT);
+   command_key (GDK_KP_Page_Up, GDK_CONTROL_MASK,
+		DILLO_COMMAND_SCROLL_PAGE_LEFT);
+   command_key (GDK_b, GDK_CONTROL_MASK,
+		DILLO_COMMAND_SCROLL_PAGE_LEFT);
+   /* page right */
+   command_key (GDK_Page_Down, GDK_CONTROL_MASK,
+		DILLO_COMMAND_SCROLL_PAGE_RIGHT);
+   command_key (GDK_KP_Page_Down, GDK_CONTROL_MASK,
+		DILLO_COMMAND_SCROLL_PAGE_RIGHT);
+   command_key (GDK_space, GDK_CONTROL_MASK,
+		DILLO_COMMAND_SCROLL_PAGE_RIGHT);
+   /* top of page */
+   command_key (GDK_Home, 0,
+		DILLO_COMMAND_SCROLL_EDGE_TOP);
+   command_key (GDK_KP_Home, 0,
+		DILLO_COMMAND_SCROLL_EDGE_TOP);
+   /* bottom of page */
+   command_key (GDK_End, 0,
+		DILLO_COMMAND_SCROLL_EDGE_BOTTOM);
+   command_key (GDK_KP_End, 0,
+		DILLO_COMMAND_SCROLL_EDGE_BOTTOM);
+   /* left edge */
+   command_key (GDK_Home, GDK_CONTROL_MASK,
+		DILLO_COMMAND_SCROLL_EDGE_LEFT);
+   command_key (GDK_KP_Home, GDK_CONTROL_MASK,
+		DILLO_COMMAND_SCROLL_EDGE_LEFT);
+   /* right edge */
+   command_key (GDK_End, GDK_CONTROL_MASK,
+		DILLO_COMMAND_SCROLL_EDGE_RIGHT);
+   command_key (GDK_KP_End, GDK_CONTROL_MASK,
+		DILLO_COMMAND_SCROLL_EDGE_RIGHT);
+}
+
+/*
+ * Handler for content command signal
+ */
+void a_Commands_handler(GtkWidget *widget,
+			DilloCommand command,
+			gpointer client_data)
+{
+   DilloDoc *dd = client_data;
+   GtkObject *object = GTK_OBJECT(widget);
+
+   DEBUG_MSG(DEBUG_EVENT,
+	     "content_command_handler (%p,%i,%p)\n",
+	     widget, command, client_data);
+
+   switch (command) {
+   case DILLO_COMMAND_NAVIGATE_FORWARD:
+      a_Commands_doc_forw_callback(NULL, a_Doc_get_root(dd));
+      break;
+   case DILLO_COMMAND_NAVIGATE_BACK:
+      a_Commands_doc_back_callback(NULL, a_Doc_get_root(dd));
+      break;
+   case DILLO_COMMAND_NAVIGATE_RELOAD:
+      a_Commands_doc_reload_callback(NULL, a_Doc_get_root(dd));
+      break;
+   case DILLO_COMMAND_NAVIGATE_STOP:
+      a_Commands_doc_stop_callback(NULL, a_Doc_get_root(dd));
+      break;
+   case DILLO_COMMAND_NAVIGATE_HOME:
+      a_Commands_home_callback(NULL, dd->bw);
+      break;
+   case DILLO_COMMAND_DUPLICATE_WINDOW:
+      a_Commands_duplicate_callback(NULL, a_Doc_get_root(dd));
+      break;
+   case DILLO_COMMAND_DUPLICATE_TAB:
+      a_Commands_duplicate_tab_callback(NULL, a_Doc_get_root(dd));
+      break;
+   case DILLO_COMMAND_FINDTEXT:
+      a_Commands_findtext_callback(NULL, a_Doc_get_root(dd));
+      break;
+   case DILLO_COMMAND_FINDTEXT_INCREMENTAL:
+      a_Commands_findtext_inc_callback(NULL, a_Doc_get_root(dd));
+      break;
+   case DILLO_COMMAND_FINDTEXT_REPEAT_LAST_SEARCH:
+      a_Interface_findtext_search(NULL, dd->bw);
+      break;
+   case DILLO_COMMAND_FINDTEXT_UNHIGHLIGHT:
+      a_Commands_findtext_hide(NULL, dd->bw);
+      break;
+   case DILLO_COMMAND_SAVE:
+      a_Commands_doc_save_callback(NULL, a_Doc_get_root(dd));
+      break;
+   case DILLO_COMMAND_SAVE_LINK:
+      a_Commands_save_link_callback(NULL, dd->bw);
+      break;
+   case DILLO_COMMAND_BOOKMARK:
+      a_Commands_doc_addbm_callback(NULL, a_Doc_get_root(dd));
+      break;
+   case DILLO_COMMAND_NEW_WINDOW:
+      a_Commands_new_callback(NULL, dd->bw);
+      break;
+   case DILLO_COMMAND_NEW_TAB:
+      a_Commands_new_tab_callback(NULL, dd->bw);
+      break;
+   case DILLO_COMMAND_OPEN_FILE:
+      a_Commands_openfile_callback(NULL, dd->bw);
+      break;
+   case DILLO_COMMAND_OPEN_URL:
+      a_Commands_openurl_callback(NULL, dd->bw);
+      break;
+   case DILLO_COMMAND_OPEN_LINK:
+   case DILLO_COMMAND_OPEN_LINK_NEW_WINDOW:
+   case DILLO_COMMAND_OPEN_LINK_NEW_TAB:
+      /* these are handled in the focus code for now */
+      break;
+   case DILLO_COMMAND_CLOSE_WINDOW:
+      a_Commands_close_callback(NULL, dd->bw);
+      break;
+   case DILLO_COMMAND_CLOSE_TAB:
+      a_Commands_close_tab_callback(NULL, a_Doc_get_root(dd));
+      break;
+   case DILLO_COMMAND_EXIT:
+      a_Commands_exit_callback(NULL, NULL);
+      break;
+   case DILLO_COMMAND_TAB_PREVIOUS:
+      a_Commands_tab_prev_callback(NULL, dd->bw);
+      break;
+   case DILLO_COMMAND_TAB_NEXT:
+      a_Commands_tab_next_callback(NULL, dd->bw);
+      break;
+   case DILLO_COMMAND_VIEW_SOURCE:
+      a_Commands_doc_viewsource_callback(NULL, a_Doc_get_root(dd));
+      break;
+   case DILLO_COMMAND_VIEW_BOOKMARKS:
+      a_Commands_viewbm_callback(NULL, dd->bw);
+      break;
+   case DILLO_COMMAND_FOCUS_LOCATION:
+      gtk_widget_grab_focus(dd->bw->location);
+      break;
+   case DILLO_COMMAND_WEBSEARCH:
+      a_Interface_search_dialog(NULL, dd->bw);
+      break;
+   case DILLO_COMMAND_TOGGLE_FULLWINDOW:
+      a_Commands_full_screen_callback(dd->bw);
+      break;
+   case DILLO_COMMAND_FOCUS_ACTIVATE:
+      gtk_signal_emit_by_name(object, "focus_activate", 0);
+      break;
+   case DILLO_COMMAND_FOCUS_ACTIVATE_NEW_WINDOW:
+      gtk_signal_emit_by_name(object, "focus_activate", GDK_SHIFT_MASK);
+      break;
+   case DILLO_COMMAND_FOCUS_ACTIVATE_NEW_TAB:
+      gtk_signal_emit_by_name(object, "focus_activate", GDK_CONTROL_MASK);
+      break;
+   case DILLO_COMMAND_FOCUS_POPUP:
+      gtk_signal_emit_by_name(object, "focus_activate", GDK_MOD1_MASK);
+      break;
+   case DILLO_COMMAND_FOCUS_HIDE:
+      gtk_signal_emit_by_name(object, "focus_hide");
+      break;
+   case DILLO_COMMAND_FOCUS_MOVE_UP:
+      gtk_signal_emit_by_name(object, "focus_move", GTK_DIR_UP);
+      break;
+   case DILLO_COMMAND_FOCUS_MOVE_DOWN:
+      gtk_signal_emit_by_name(object, "focus_move", GTK_DIR_DOWN);
+      break;
+   case DILLO_COMMAND_FOCUS_MOVE_LEFT:
+      gtk_signal_emit_by_name(object, "focus_move", GTK_DIR_LEFT);
+      break;
+   case DILLO_COMMAND_FOCUS_MOVE_RIGHT:
+      gtk_signal_emit_by_name(object, "focus_move", GTK_DIR_RIGHT);
+      break;
+   case DILLO_COMMAND_FOCUS_MOVE_TAB_BACK:
+   case DILLO_COMMAND_FOCUS_MOVE_TAB_FORWARD:
+      /* these two are handled through the normal
+       * Tab/Shift-Tab key bindings */
+      break;
+   case DILLO_COMMAND_SCROLL_UP:
+      gtk_signal_emit_by_name(object, "viewport_move", DW_SCROLL_STEP_BACKWARD);
+      break;
+   case DILLO_COMMAND_SCROLL_DOWN:
+      gtk_signal_emit_by_name(object, "viewport_move", DW_SCROLL_STEP_FORWARD);
+      break;
+   case DILLO_COMMAND_SCROLL_LEFT:
+      gtk_signal_emit_by_name(object, "viewport_move", DW_SCROLL_STEP_LEFT);
+      break;
+   case DILLO_COMMAND_SCROLL_RIGHT:
+      gtk_signal_emit_by_name(object, "viewport_move", DW_SCROLL_STEP_RIGHT);
+      break;
+   case DILLO_COMMAND_SCROLL_PAGE_UP:
+      gtk_signal_emit_by_name(object, "viewport_move", DW_SCROLL_PAGE_BACKWARD);
+      break;
+   case DILLO_COMMAND_SCROLL_PAGE_DOWN:
+      gtk_signal_emit_by_name(object, "viewport_move", DW_SCROLL_PAGE_FORWARD);
+      break;
+   case DILLO_COMMAND_SCROLL_PAGE_LEFT:
+      gtk_signal_emit_by_name(object, "viewport_move", DW_SCROLL_PAGE_LEFT);
+      break;
+   case DILLO_COMMAND_SCROLL_PAGE_RIGHT:
+      gtk_signal_emit_by_name(object, "viewport_move", DW_SCROLL_PAGE_RIGHT);
+      break;
+   case DILLO_COMMAND_SCROLL_EDGE_TOP:
+      gtk_signal_emit_by_name(object, "viewport_move", DW_SCROLL_EDGE_BACKWARD);
+      break;
+   case DILLO_COMMAND_SCROLL_EDGE_BOTTOM:
+      gtk_signal_emit_by_name(object, "viewport_move", DW_SCROLL_EDGE_FORWARD);
+      break;
+   case DILLO_COMMAND_SCROLL_EDGE_LEFT:
+      gtk_signal_emit_by_name(object, "viewport_move", DW_SCROLL_EDGE_LEFT);
+      break;
+   case DILLO_COMMAND_SCROLL_EDGE_RIGHT:
+      gtk_signal_emit_by_name(object, "viewport_move", DW_SCROLL_EDGE_RIGHT);
+      break;
+ 
+   default:
+      MSG("a_Commands_handler: unknown DilloCommand %i\n", command);
+      break;
+   }
+}
diff -pruN dillo/src/commands.h dillo_tabs/src/commands.h
--- dillo/src/commands.h	2003-11-24 14:32:40.077779616 +0100
+++ dillo_tabs/src/commands.h	2003-11-24 14:32:40.184763352 +0100
@@ -1,6 +1,73 @@
 #ifndef __COMMANDS_H__
 #define __COMMANDS_H__
 
+typedef enum
+{
+   DILLO_COMMAND_NAVIGATE_BACK,               /*  0 */
+   DILLO_COMMAND_NAVIGATE_FORWARD,            /*  1 */
+   DILLO_COMMAND_NAVIGATE_RELOAD,             /*  2 */
+   DILLO_COMMAND_NAVIGATE_STOP,               /*  3 */
+   DILLO_COMMAND_NAVIGATE_HOME,               /*  4 */
+   DILLO_COMMAND_DUPLICATE_WINDOW,            /*  5 */
+   DILLO_COMMAND_DUPLICATE_TAB,               /*  6 */
+   DILLO_COMMAND_FINDTEXT,                    /*  7 */
+   DILLO_COMMAND_FINDTEXT_INCREMENTAL,        /*  8 */
+   DILLO_COMMAND_FINDTEXT_REPEAT_LAST_SEARCH, /*  9 */
+   DILLO_COMMAND_FINDTEXT_UNHIGHLIGHT,        /* 10 */
+   DILLO_COMMAND_SAVE,                        /* 11 */
+   DILLO_COMMAND_SAVE_LINK,                   /* 12 */
+   DILLO_COMMAND_BOOKMARK,                    /* 13 */
+   DILLO_COMMAND_NEW_WINDOW,                  /* 14 */
+   DILLO_COMMAND_NEW_TAB,                     /* 15 */
+   DILLO_COMMAND_OPEN_FILE,                   /* 16 */
+   DILLO_COMMAND_OPEN_URL,                    /* 17 */
+   DILLO_COMMAND_OPEN_LINK,                   /* 18 */
+   DILLO_COMMAND_OPEN_LINK_NEW_WINDOW,        /* 19 */
+   DILLO_COMMAND_OPEN_LINK_NEW_TAB,           /* 20 */
+   DILLO_COMMAND_CLOSE_WINDOW,                /* 21 */
+   DILLO_COMMAND_CLOSE_TAB,                   /* 22 */
+   DILLO_COMMAND_EXIT,                        /* 23 */
+   DILLO_COMMAND_TAB_PREVIOUS,                /* 24 */
+   DILLO_COMMAND_TAB_NEXT,                    /* 25 */
+   DILLO_COMMAND_VIEW_SOURCE,                 /* 26 */
+   DILLO_COMMAND_VIEW_BOOKMARKS,              /* 27 */
+   DILLO_COMMAND_FOCUS_LOCATION,              /* 28 */
+   DILLO_COMMAND_WEBSEARCH,                   /* 29 */
+   DILLO_COMMAND_TOGGLE_FULLWINDOW,           /* 30 */
+   DILLO_COMMAND_FOCUS_ACTIVATE,              /* 31 */
+   DILLO_COMMAND_FOCUS_ACTIVATE_NEW_WINDOW,   /* 32 */
+   DILLO_COMMAND_FOCUS_ACTIVATE_NEW_TAB,      /* 33 */
+   DILLO_COMMAND_FOCUS_POPUP,                 /* 34 */
+   DILLO_COMMAND_FOCUS_HIDE,                  /* 35 */
+   DILLO_COMMAND_FOCUS_MOVE_UP,               /* 36 */
+   DILLO_COMMAND_FOCUS_MOVE_DOWN,             /* 37 */
+   DILLO_COMMAND_FOCUS_MOVE_LEFT,             /* 38 */
+   DILLO_COMMAND_FOCUS_MOVE_RIGHT,            /* 39 */
+   DILLO_COMMAND_FOCUS_MOVE_TAB_BACK,         /* 40 */
+   DILLO_COMMAND_FOCUS_MOVE_TAB_FORWARD,      /* 41 */
+   DILLO_COMMAND_SCROLL_UP,                   /* 42 */
+   DILLO_COMMAND_SCROLL_DOWN,                 /* 43 */
+   DILLO_COMMAND_SCROLL_LEFT,                 /* 44 */
+   DILLO_COMMAND_SCROLL_RIGHT,                /* 45 */
+   DILLO_COMMAND_SCROLL_PAGE_UP,              /* 46 */
+   DILLO_COMMAND_SCROLL_PAGE_DOWN,            /* 47 */
+   DILLO_COMMAND_SCROLL_PAGE_LEFT,            /* 48 */
+   DILLO_COMMAND_SCROLL_PAGE_RIGHT,           /* 49 */
+   DILLO_COMMAND_SCROLL_EDGE_TOP,             /* 50 */
+   DILLO_COMMAND_SCROLL_EDGE_BOTTOM,          /* 51 */
+   DILLO_COMMAND_SCROLL_EDGE_LEFT,            /* 52 */
+   DILLO_COMMAND_SCROLL_EDGE_RIGHT            /* 53 */
+} DilloCommand;
+
+/* set key bindings, assumes valid GtkBindingSet *binding_set */
+#define key_reg(args...) gtk_binding_entry_add_signal (binding_set, args)
+/* set content_command key bindings, see key_reg */
+#define command_key(key,mask,command) \
+         gtk_binding_entry_add_signal (binding_set, key, mask, \
+                                       "content_command", 1, \
+                                       GTK_TYPE_ENUM, command)
+
+void a_Commands_init(void);
 void a_Commands_new_callback(GtkWidget * widget, gpointer client_data);
 void a_Commands_openfile_callback (GtkWidget *widget, gpointer client_data);
 void a_Commands_openurl_callback (GtkWidget *widget, gpointer client_data);
@@ -9,8 +76,13 @@ void a_Commands_close_callback(GtkWidget
 void a_Commands_exit_callback (GtkWidget *widget, gpointer client_data);
 
 void a_Commands_viewsource_callback (GtkWidget *widget, gpointer client_data);
+void a_Commands_doc_viewsource_callback (GtkWidget *widget, gpointer client_data);
 void a_Commands_selectall_callback (GtkWidget *widget, gpointer client_data);
 void a_Commands_findtext_callback (GtkWidget *widget, gpointer client_data);
+void a_Commands_findtext_inc_callback (GtkWidget *widget, gpointer client_data);
+
+void a_Commands_findtext_hide (GtkWidget *widget, gpointer client_data);
+void a_Commands_doc_showonly_callback(GtkWidget *widget, gpointer client_data);
 void a_Commands_print_callback (GtkWidget *widget, gpointer client_data);
 
 void a_Commands_navpress_callback (
@@ -22,17 +94,38 @@ void a_Commands_back_callback (GtkWidget
 void a_Commands_forw_callback (GtkWidget *widget, gpointer client_data);
 void a_Commands_reload_callback (GtkWidget *widget, gpointer client_data);
 void a_Commands_stop_callback (GtkWidget *widget, gpointer client_data);
+void a_Commands_doc_back_callback (GtkWidget *widget, gpointer client_data);
+void a_Commands_doc_forw_callback (GtkWidget *widget, gpointer client_data);
+void a_Commands_doc_reload_callback (GtkWidget *widget, gpointer client_data);
+void a_Commands_doc_stop_callback (GtkWidget *widget, gpointer client_data);
+
 void a_Commands_home_callback (GtkWidget *widget, gpointer client_data);
 void a_Commands_save_callback (GtkWidget *widget, gpointer client_data);
+void a_Commands_doc_save_callback (GtkWidget *widget, gpointer client_data);
 void a_Commands_save_link_callback (GtkWidget *widget, gpointer client_data);
 
 void a_Commands_addbm_callback (GtkWidget *widget, gpointer client_data);
 void a_Commands_viewbm_callback (GtkWidget *widget, gpointer client_data);
+void a_Commands_doc_addbm_callback (GtkWidget *widget, gpointer client_data);
+void a_Commands_addbm_link_callback (GtkWidget *widget, gpointer client_data);
 
 void a_Commands_helphome_callback (GtkWidget *widget, gpointer client_data);
 
 void a_Commands_open_link_callback(GtkWidget *widget, gpointer client_data);
 void a_Commands_open_link_nw_callback(GtkWidget *widget, gpointer client_data);
+void a_Commands_duplicate_callback(GtkWidget *widget, gpointer client_data);
 void a_Commands_select_popup_url_callback(GtkWidget *widget, gpointer data);
 
+void a_Commands_full_screen_callback(gpointer client_data);
+gint a_Commands_click_callback(gpointer client_data, GdkEventButton *event);
+void a_Commands_handler(GtkWidget *widget,
+			DilloCommand command, gpointer client_data);
+
+void a_Commands_new_tab_callback(GtkWidget *widget, gpointer client_data);
+void a_Commands_close_current_tab_callback(GtkWidget *widget, gpointer client_data);
+void a_Commands_close_tab_callback(GtkWidget *widget, gpointer client_data);
+void a_Commands_tab_prev_callback (GtkWidget *widget, gpointer client_data);
+void a_Commands_tab_next_callback (GtkWidget *widget, gpointer client_data);
+void a_Commands_open_link_nw_tab_callback(GtkWidget *widget, gpointer client_data);
+void a_Commands_duplicate_tab_callback(GtkWidget *widget, gpointer client_data);
 #endif /* __COMMANDS_H__ */
diff -pruN dillo/src/dicache.c dillo_tabs/src/dicache.c
--- dillo/src/dicache.c	2003-11-24 14:32:40.080779160 +0100
+++ dillo_tabs/src/dicache.c	2003-11-24 14:32:40.187762896 +0100
@@ -17,6 +17,7 @@
 
 #include "image.h"
 #include "web.h"
+#include "doc.h"
 #include "dicache.h"
 #include "cache.h"
 #include "interface.h"
@@ -261,7 +262,7 @@ void a_Dicache_callback(int Op, CacheCli
       }
    } else if ( Op == CA_Close || Op == CA_Abort ) {
       a_Image_close(Web->Image);
-      a_Interface_close_client(Web->bw, Client->Key);
+      a_Doc_close_client(Web->dd, Client->Key);
    }
 }
 
@@ -364,7 +365,7 @@ void a_Dicache_close(DilloUrl *url, gint
       DicEntry->cmap = NULL;
    }
    a_Image_close(Web->Image);
-   a_Interface_close_client(Web->bw, Client->Key);
+   a_Doc_close_client(Web->dd, Client->Key);
 }
 
 /* ------------------------------------------------------------------------- */
diff -pruN dillo/src/dillo.c dillo_tabs/src/dillo.c
--- dillo/src/dillo.c	2003-11-24 14:32:40.081779008 +0100
+++ dillo_tabs/src/dillo.c	2003-11-24 14:32:40.188762744 +0100
@@ -27,7 +27,6 @@
 #include <string.h>
 #include <errno.h>
 #include <fcntl.h>
-#include <unistd.h>
 #include <stdlib.h>
 #include <signal.h>
 #include <sys/wait.h>
@@ -39,6 +38,7 @@
 #include "dillo.h"
 #include "misc.h"
 #include "nav.h"
+#include "doc.h"
 #include "history.h"
 #include "bookmark.h"
 #include "dicache.h"
@@ -46,8 +46,14 @@
 #include "IO/mime.h"
 #include "prefs.h"
 #include "interface.h"
+#include "tab.h"
 #include "dw.h"
 #include "cookies.h"
+#include "commands.h"
+
+#define DEBUG_EVENT  10
+/* #define DEBUG_LEVEL 10 */
+#include "debug.h"
 
 
 /*
@@ -127,45 +133,24 @@ static gint Dillo_get_max_opt(CLI_option
 /*
  * Given a command line argument, build a DilloUrl for it.
  */
-static DilloUrl *Dillo_make_start_url(gchar *str)
+static DilloUrl *Dillo_make_start_url(gchar *text)
 {
-   gchar *url_str, *p, *cd;
-   DilloUrl *start_url;
-   gint is_file = FALSE;
+   gchar *kw_url_str, *lc_url_str, *fl_url_str;
+   DilloUrl *url;
 
-   /* This check is for files with non-URL characters */
-   if (access(str, F_OK) == 0) {
-      p = a_Misc_escape_chars(str, "% ");
-      url_str = g_strdup(p ? p : str);
-      is_file = TRUE;
-   } else {
-      /* Filter URL string */
-      url_str = a_Url_string_strip_delimiters(str);
-   }
+   /* process keywords */
+   kw_url_str = a_Url_string_process_keywords(text);
+   /* process local content */
+   lc_url_str = a_Url_string_process_local(kw_url_str, TRUE);
+   /* Filter URL string */
+   fl_url_str = a_Url_string_strip_delimiters(lc_url_str);
+   url = a_Url_new(fl_url_str, NULL, 0, 0);
+
+   g_free(kw_url_str);
+   g_free(lc_url_str);
+   g_free(fl_url_str);
 
-   if (is_file || access(url_str, F_OK) == 0) {
-      GString *UrlStr = g_string_sized_new(128);
-
-      if (url_str[0] == '/') {
-         g_string_sprintf(UrlStr, "file:%s", url_str);
-      } else {
-         cd = g_get_current_dir();
-         p = a_Misc_escape_chars(cd, "% ");
-         g_string_sprintf(UrlStr, "file:%s", p ? p : cd);
-         g_free(p);
-         g_free(cd);
-         if (UrlStr->str[UrlStr->len - 1] != '/')
-            g_string_append(UrlStr, "/");
-         g_string_append(UrlStr, url_str);
-      }
-      start_url = a_Url_new(UrlStr->str, NULL, 0, 0);
-      g_string_free(UrlStr, TRUE);
-   } else {
-      start_url = a_Url_new(url_str, NULL, 0, 0);
-   }
-
-   g_free(url_str);
-   return start_url;
+   return url;
 }
 
 /*
@@ -197,6 +182,7 @@ gint main(int argc, char *argv[])
    gchar *dir, *curr_locale;
    DilloUrl *start_url;
    BrowserWindow *bw;
+   DilloDoc *dd;
    guint32 xid = 0;
    guint32 options_got = 0;
    gint idx = 0;
@@ -228,7 +214,7 @@ gint main(int argc, char *argv[])
            xid = strtol(opt_argv[0], NULL, 10);
         } else {
            g_printerr("Error: the XID must be an unsigned decimal numerical "
-                      "value.\nThe offending value was: %s\n", opt_argv[0]);
+		      "value.\nThe offending value was: %s\n", opt_argv[0]);
            return -1;
         }
         break;
@@ -246,7 +232,7 @@ gint main(int argc, char *argv[])
    }
 
    /* Send a delayed locale-related message */
-   MSG("Setting locale to %s...\n", curr_locale);
+   DEBUG_MSG(DEBUG_EVENT, "Setting locale to %s...\n", curr_locale);
    g_free(curr_locale);
 
    /* This lets threads in the file module end peacefully when aborted
@@ -271,6 +257,8 @@ gint main(int argc, char *argv[])
    a_Interface_init();
    a_Dw_init();
    a_Cookies_init();
+   a_Doc_init();
+   a_Commands_init();   /* set default key bindings */
 
    /* -f overrides dillorc */
    if (options_got & DILLO_CLI_FULLWINDOW)
@@ -279,23 +267,33 @@ gint main(int argc, char *argv[])
    /* a_Nav_init() has been moved into this call because it needs to be
     * initialized with the new browser_window structure */
    bw = a_Interface_browser_window_new(prefs.width, prefs.height, xid);
+   dd = bw->dd;
 
    a_Bookmarks_init();
 
-   /* Send dillo startup screen */
-   start_url = a_Url_new("splash", "about:", 0, 0);
-   a_Nav_push(bw, start_url);
-   a_Url_free(start_url);
+   if(prefs.show_splash) {
+     /* Send dillo startup screen */
+     start_url = a_Url_new("splash", "about:", 0, 0);
+     a_Nav_push(dd, start_url);
+     a_Url_free(start_url);
+   }
 
    for (i = idx; i < argc; i++) {
-      /* If more than one URL/FILE, open in new window */
-      if (i > idx)
-         bw = a_Interface_browser_window_new(prefs.width, prefs.height, 0);
+      /* If more than one URL/FILE, open in new window or tab */
+      if (i > idx) {
+         if(prefs.target_blank == TARGET_TAB) {
+	   dd = a_Doc_new();
+	   a_Tab_add(bw, dd);
+	 } else {
+	   bw = a_Interface_browser_window_new(prefs.width, prefs.height, 0);
+	   dd = bw->dd;
+	 }
+      }
 
       start_url = Dillo_make_start_url(argv[i]);
       if (options_got & DILLO_CLI_LOCAL)
          a_Url_set_flags(start_url, URL_FLAGS(start_url) | URL_SpamSafe);
-      a_Nav_push(bw, start_url);
+      a_Nav_push(dd, start_url);
       a_Url_free(start_url);
    }
 
@@ -317,7 +315,10 @@ gint main(int argc, char *argv[])
    a_History_free();
 
    //a_Dpi_bye_dpid();
-   MSG("Dillo: normal exit!\n");
+#ifdef DEBUG
+   g_mem_profile();
+#endif
+   _MSG("Dillo: normal exit!\n");
    return 0;
 }
 
@@ -332,7 +333,7 @@ static void Dillo_check_home_dir(char *d
    if ( stat(dir, &st) == -1 ) {
       if ( errno == ENOENT && mkdir(dir, 0700) < 0 ) {
          MSG("Dillo: error creating directory %s: %s\n",
-             dir, g_strerror(errno));
+	     dir, g_strerror(errno));
       } else
          MSG("Dillo: error reading %s: %s\n", dir, g_strerror(errno));
    }
@@ -384,7 +385,7 @@ Dillo_get_opt(CLI_options *options, int 
          /* Optional arguments have opt_argc < 0 */
          if (i < n_arg) {
             g_printerr("Option %s requires %d argument(s)\n",
-                       argv[idx - i - 1], n_arg);
+		       argv[idx - i - 1], n_arg);
             state = O_ERROR;
          } else
             state = O_OK;
@@ -419,7 +420,7 @@ Dillo_get_opt(CLI_options *options, int 
 static void Dillo_print_help(CLI_options *options)
 {
    g_print("\nUsage: dillo [OPTIONS] [URL|FILE]...\n"
-              "Options:\n");
+	   "Options:\n");
 
    for (; options && options->sh_opt; options++) {
       if (options->help)
diff -pruN dillo/src/dns.c dillo_tabs/src/dns.c
--- dillo/src/dns.c	2003-11-24 14:32:40.082778856 +0100
+++ dillo_tabs/src/dns.c	2003-11-24 14:32:40.188762744 +0100
@@ -31,7 +31,7 @@
 #include "dns.h"
 #include "list.h"
 
-#define DEBUG_LEVEL 5
+/* #define DEBUG_LEVEL 5 */
 #include "debug.h"
 
 
diff -pruN dillo/src/doc.c dillo_tabs/src/doc.c
--- dillo/src/doc.c	1970-01-01 01:00:00.000000000 +0100
+++ dillo_tabs/src/doc.c	2003-11-24 14:32:40.241754688 +0100
@@ -0,0 +1,781 @@
+/*
+ * File: doc.c
+ *
+ * Copyright (C) 2003 Frank de Lange
+ * includes code with (C) 1997 Raph Levien <raph@acm.org>
+ *                    (C) 1999 Sammy Mannaert <nstalkie@tvd.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include "dw_widget.h"
+#include "dw_gtk_scrolled_window.h"
+#include "dw_gtk_viewport.h"
+#include "dw_container.h"
+#include "doc.h"
+#include "list.h"
+#include "debug.h"
+#include "cache.h"
+#include "nav.h"
+#include "pagemark.h"
+#include "interface.h"
+#include "commands.h"
+#include <math.h>                     /* for rint() */
+#include "tab.h"
+#include "progressbar.h"
+#include "prefs.h"
+
+#define DEBUG_FIND 10
+
+#define DEBUG_LEVEL 10
+#include "debug.h"
+
+/*
+ * Local Data
+ */
+
+/* DilloDoc holds everything pertaining to a single document */
+static DilloDoc **dillo_doc;
+static gint num_dd, num_dd_max;
+
+/*
+ * Initialize global data
+ */
+void a_Doc_init(void)
+{
+   num_dd = 0;
+   num_dd_max = 16;
+   dillo_doc = NULL;
+}
+
+/* callbacks */
+
+/*
+ * callback for docwin delete
+ *
+ * this will call a_Doc_destroy for the associated DilloDoc
+ */
+void
+Doc_docwin_destroy_callback(GtkWidget *widget,
+                           gpointer client_data)
+{
+   DilloDoc *dd = (DilloDoc *) client_data;
+
+   a_Doc_destroy(dd);
+}
+
+/*
+ * callback for docwin map
+ *
+ * For top-level documents set focus to:
+ *  - document loaded: the scrolled_frame
+ *  - no document loaded: the bw's location bar
+ */
+void
+Doc_docwin_map_callback(GtkWidget *widget,
+			gpointer client_data)
+{
+   DilloDoc *dd = (DilloDoc *) client_data;
+
+   if(!dd->parent) {
+     if (GTK_BIN(dd->docwin)->child &&
+	 dd->nav_stack_ptr != -1)
+       gtk_widget_grab_focus(GTK_BIN(dd->docwin)->child);
+     else
+       gtk_widget_grab_focus(dd->bw->location);
+   }
+}
+
+/* public functions */
+
+/*
+ * Stop all active connections for the document (except downloads)
+ */
+void a_Doc_stop(DilloDoc *dd)
+{
+   g_return_if_fail ( dd != NULL );
+
+   /* Remove root clients */
+   while ( dd->NumRootClients ) {
+      a_Cache_stop_client(dd->RootClients[0]);
+      a_List_remove(dd->RootClients, 0, dd->NumRootClients);
+   }
+   /* Remove image clients */
+   while ( dd->NumImageClients ) {
+      a_Cache_stop_client(dd->ImageClients[0]);
+      a_List_remove(dd->ImageClients, 0, dd->NumImageClients);
+   }
+}
+
+/*
+ * Empty RootClients, ImageClients and PageUrls lists and
+ * reset progress bar data.
+ */
+void a_Doc_clean(DilloDoc *dd)
+{
+   g_return_if_fail ( dd != NULL );
+
+   while ( dd->NumRootClients )
+      a_List_remove(dd->RootClients, 0, dd->NumRootClients);
+
+   while ( dd->NumImageClients )
+      a_List_remove(dd->ImageClients, 0, dd->NumImageClients);
+
+   while ( dd->NumPageUrls ) {
+      a_Url_free(dd->PageUrls[0].Url);
+      a_List_remove(dd->PageUrls, 0, dd->NumPageUrls);
+   }
+
+   /* Zero image-progressbar data */
+   dd->NumImages = 0;
+   dd->NumImagesGot = 0;
+
+   /* Zero progressbar data and ready state */
+   dd->progress = 0.0;
+   dd->ready = TRUE;
+}
+
+/*
+ * Remove the cache-client from the dd list
+ * (client can be a image or a html page)
+ */
+void a_Doc_remove_client(DilloDoc *dd, gint ClientKey)
+{
+   gint i;
+   gboolean Found = FALSE;
+
+   g_return_if_fail ( dd != NULL );
+
+   for ( i = 0; !Found && i < dd->NumRootClients; ++i)
+      if ( dd->RootClients[i] == ClientKey ) {
+         a_List_remove(dd->RootClients, i, dd->NumRootClients);
+         Found = TRUE;
+      }
+
+   for ( i = 0; !Found && i < dd->NumImageClients; ++i)
+      if ( dd->ImageClients[i] == ClientKey ) {
+         a_List_remove(dd->ImageClients, i, dd->NumImageClients);
+         dd->NumImagesGot++;
+         Found = TRUE;
+      }
+
+   a_Interface_set_button_sens(dd->bw);
+}
+
+
+/*
+ * Remove the cache-client from the dd list
+ * (client can be a image or a html page)
+ */
+void a_Doc_close_client(DilloDoc *dd, gint ClientKey)
+{
+   g_return_if_fail ( dd != NULL );
+
+   a_Doc_remove_client(dd, ClientKey);
+   a_Doc_progress_update(dd);
+}
+
+/*
+ * update progress bar, set button sensitivity
+ */
+void
+a_Doc_progress_update(DilloDoc *dd)
+{
+   gchar progress[PBAR_L];
+
+   g_return_if_fail ( dd != NULL );
+
+   a_Doc_set_button_sens(dd);
+   
+   if(dd->bw->dd == dd) {
+     g_snprintf(progress, PBAR_L, "%s%d of %d",
+		PBAR_ISTR(prefs.panel_size == 1),
+		dd->NumImagesGot, dd->NumImages);
+     a_Progressbar_update(dd->bw->imgprogress, progress,
+			  (dd->NumImagesGot == dd->NumImages) ? 0 : 1 );
+     g_snprintf(progress, PBAR_L, "%s%.1f Kb",
+		PBAR_PSTR(prefs.panel_size == 1),
+		(float)dd->progress);
+     a_Progressbar_update(dd->bw->progress, progress, (dd->ready) ? 0 : 1 );
+   }
+}
+
+/*
+ * Set the sensitivity on navigation buttons
+ */
+static gint Doc_sens_idle_func(DilloDoc *dd)
+{
+   dd->stop_sensitive = (dd->NumRootClients > 0);
+   dd->back_sensitive = a_Nav_stack_ptr(dd) > 0;
+   dd->forw_sensitive = (a_Nav_stack_ptr(dd) < a_Nav_stack_size(dd) - 1 &&
+			 !dd->nav_expecting);
+
+   /* if this is the top level document:
+    * set interface button sensitivity */
+   if(dd->bw->dd == dd)
+     a_Interface_set_button_sens(dd->bw);
+
+   dd->sens_idle_id = 0;
+   return FALSE;
+}
+
+void a_Doc_set_button_sens(DilloDoc *dd)
+{
+   if (dd->sens_idle_id != 0)
+      return;
+   dd->sens_idle_id =
+     gtk_idle_add((GtkFunction) Doc_sens_idle_func, dd);
+}
+
+/*
+ * Add a reference to the cache-client in the document's list.
+ * This helps us keep track of which are active in the document so that it's
+ * possible to abort them.
+ * (Root: Flag, whether a Root URL or not)
+ */
+void a_Doc_add_client(DilloDoc *dd, gint Key, gint Root)
+{
+   gint nc;
+
+   g_return_if_fail ( dd != NULL );
+
+   if ( Root ) {
+      nc = dd->NumRootClients;
+      a_List_add(dd->RootClients, nc, dd->MaxRootClients);
+      dd->RootClients[nc] = Key;
+      dd->NumRootClients++;
+   } else {
+      nc = dd->NumImageClients;
+      a_List_add(dd->ImageClients, nc, dd->MaxImageClients);
+      dd->ImageClients[nc] = Key;
+      dd->NumImageClients++;
+      dd->NumImages++;
+   }
+
+   a_Doc_findtext_reset(dd);
+
+   /* if this is a top level document: grab focus for docwin */
+   if(DD_HAS_FOCUS(dd, dd->bw) && !dd->parent)
+     gtk_widget_grab_focus(GTK_BIN(dd->docwin)->child);
+
+   a_Doc_progress_update(dd);
+}
+
+/*
+ * Add an URL to the document's list.
+ * This helps us keep track of page requested URLs so that it's
+ * possible to stop, abort and reload them.)
+ *   Flags: Chosen from {DD_Root, DD_Image, DD_Download}
+ */
+void a_Doc_add_url(DilloDoc *dd, const DilloUrl *Url, gint Flags)
+{
+   gint nu, i;
+   gboolean found = FALSE;
+
+   g_return_if_fail ( dd != NULL && Url != NULL );
+
+   nu = dd->NumPageUrls;
+   for ( i = 0; i < nu; i++ ) {
+      if ( !a_Url_cmp(Url, dd->PageUrls[i].Url) ) {
+         found = TRUE;
+         break;
+      }
+   }
+   if ( !found ) {
+      a_List_add(dd->PageUrls, nu, dd->MaxPageUrls);
+      dd->PageUrls[nu].Url = a_Url_dup(Url);
+      dd->PageUrls[nu].Flags = Flags;
+      dd->NumPageUrls++;
+   }
+}
+
+/*
+ * set document title
+ *
+ * this will propagate to the current interface element
+ *
+ * currently just forwards to a_Tab_title_set (when tabs
+ * are compiled in) or a_Interface_set_page_title (when
+ * compiled without tab support)
+ */
+void
+a_Doc_title_set(DilloDoc *dd, gchar *title)
+{
+   g_return_if_fail ( dd != NULL && title != NULL );
+
+   /* only set title for top level document */
+   if(dd->parent == NULL)
+    a_Tab_title_set(dd, title);
+}
+
+/*
+ * set document location
+ */
+void
+a_Doc_location_set(DilloDoc *dd, gchar *location)
+{
+   g_return_if_fail ( dd != NULL && location != NULL );
+
+   /* only set location text if this is the current tab */
+   if((dd->parent == NULL) && dd->bw->dd == dd) {
+     a_Interface_set_location_text(dd->bw, location);
+   }
+}
+
+/*
+ * destroy a document
+ */
+void a_Doc_destroy(DilloDoc *dd)
+{
+   gint i;
+
+   g_return_if_fail ( dd != NULL );
+   
+   /* stop/abort open connections. */
+   a_Doc_stop(dd);
+   
+   for (i = 0; i < num_dd; i++)
+     if (dillo_doc[i] == dd) {
+       dillo_doc[i] = dillo_doc[--num_dd];
+       break;
+     }
+   
+   /* free nav_stack and nav_expect stuff */
+   a_Nav_free(dd);
+
+   /* unref the style */
+   if(dd->style)
+     a_Dw_style_unref(dd->style);
+   
+   /* destroy pagemark menu */
+   a_Pagemark_destroy(dd);
+
+   /* destroy search_list */
+   a_Doc_findtext_reset(dd);
+   
+   g_free(dd->RootClients);
+   g_free(dd->ImageClients);
+   
+   if(dd->name)
+     g_free(dd->name);
+  
+   for (i = 0; i < dd->NumPageUrls; i++)
+     a_Url_free(dd->PageUrls[i].Url);
+   g_free(dd->PageUrls);
+   g_free(dd);
+}
+
+/*
+ * set the dd's cursor type, and update the docwin if it is
+ * mapped.
+ */
+void a_Doc_set_cursor(DilloDoc *dd, GdkCursorType CursorType)
+{
+   GdkCursor *cursor;
+
+   g_return_if_fail ( dd != NULL );
+
+   if ( dd->CursorType != CursorType ) {
+     if(GTK_WIDGET_MAPPED(dd->docwin)) {
+       cursor = gdk_cursor_new(CursorType);
+       gdk_window_set_cursor(dd->docwin->window, cursor);
+       gdk_cursor_destroy(cursor);
+     }
+     dd->CursorType = CursorType;
+   }
+}
+
+/*
+ * Get the DilloDoc which contains *docwin
+ * returns NULL if dd not found
+ */
+DilloDoc *
+a_Doc_get_by_docwin(GtkWidget *docwin)
+{
+   gint i;
+
+   g_return_val_if_fail ( docwin != NULL, NULL );
+
+   for (i = 0; i < num_dd; i++)
+     if (dillo_doc[i]->docwin == docwin)
+       return dillo_doc[i];
+   
+   return NULL;
+}
+
+/*
+ * set the dd's name (used for targeted links)
+ */
+void a_Doc_set_name(DilloDoc *dd, gchar *name)
+{
+   g_return_if_fail((dd != NULL) && (name != NULL));
+
+   if (dd && name) {
+     if (dd->name)
+       g_free((gchar *)dd->name);
+     dd->name = g_strdup(name);
+   }
+}
+
+/*
+ * Get a named DilloDoc
+ *
+ * First checks if name is one of the reserved names
+ * ("_blank", "_self", "_parent" and "_top") and acts
+ * accordingly if so. If not, it searches for a document
+ * with the same root as the document which initiated the
+ * search. If still not found, it searches for the first
+ * document with the given name.
+ *
+ * returns NULL if no matching document found
+ */
+DilloDoc *
+a_Doc_get_by_name(DilloDoc *dd, gchar *name)
+{
+  gint i, n;
+  DilloDoc *parent, *document;
+  gint width, height;
+  BrowserWindow *newbw;
+
+  g_return_val_if_fail((dd != NULL) && (name != NULL), NULL);
+
+  parent = a_Doc_get_root(dd);
+  document = NULL;
+
+  /* first see if the target name is one of the reserved names,
+   * if so act appropriately */
+  if(!g_strcasecmp(name, "_blank"))
+    switch(prefs.target_blank) {
+    case TARGET_SELF:
+      document = dd;
+      break;
+    case TARGET_TAB:
+      document = a_Doc_new();
+      a_Tab_add(parent->bw, document);
+      break;
+    default:
+      gdk_window_get_size(parent->bw->main_window->window, &width, &height);
+      newbw = a_Interface_browser_window_new(width, height, 0);
+      document = newbw->dd;
+      break;
+    }
+
+  else if(!g_strcasecmp(name, "_self"))
+    document = dd;
+  else if(!g_strcasecmp(name, "_parent"))
+    document = a_Doc_get_parent(dd);
+  else if(!g_strcasecmp(name, "_top"))
+    document = a_Doc_get_root(dd);
+
+  /* if previous search did not match, first try to find document with same parent, 
+   * if not found try to find first matching named document
+   */
+  if(!document)
+    for (n = 1; n >= 0; n--) {
+      for (i = 0; i < num_dd; i++)
+	if (dillo_doc[i]->name &&
+	    (!g_strcasecmp(name, dillo_doc[i]->name)) &&
+	    (n ? (a_Doc_get_root(dillo_doc[i]) == parent) : TRUE)) {
+	  document = dillo_doc[i];
+	  break;
+	}
+      if(document)
+	break;
+    }
+
+  return document;
+}
+
+/*
+ * Get the parent document
+ * For unparented documents it just returns the document itself
+ */
+DilloDoc *
+a_Doc_get_parent(DilloDoc *dd)
+{
+   DilloDoc *parent;
+
+   g_return_val_if_fail(dd != NULL, NULL);
+
+   parent = dd;
+   if(parent->parent)
+     parent = parent->parent;
+
+   return parent;
+}
+
+/*
+ * Get the root document
+ * For unparented documents it just returns the document itself.
+ */
+DilloDoc *
+a_Doc_get_root(DilloDoc *dd)
+{
+   DilloDoc *parent;
+
+   g_return_val_if_fail(dd != NULL, NULL);
+
+   parent = dd;
+   while(parent->parent)
+     parent = parent->parent;
+
+   return parent;
+}
+
+/*
+ * get all visible children (nested documents) for this document
+ *
+ * returns: pointer to GList containing all visible descendants of
+ *          the document (including the document itself as the first
+ *          item if it is visible. Frameset documents are not visible,
+ *          so they are not included in the list).
+ *
+ * returned GList must be g_list_free()'d by caller
+ */
+GList *
+a_Doc_get_visible_children(DilloDoc *dd)
+{
+   gint i;
+   GList *children;
+
+   g_return_val_if_fail(dd != NULL, NULL);
+
+   children = NULL;
+
+   /* first add 'self' to list */
+   if(GTK_WIDGET_VISIBLE(dd->docwin))
+     children = g_list_append(children, dd);
+   for (i = 0; i < num_dd; i++)
+     if (dillo_doc[i]->parent == dd)
+       children = g_list_concat(children, 
+				a_Doc_get_visible_children(dillo_doc[i]));
+
+   return children;
+}
+
+/*
+ * Set parent
+ */
+void
+a_Doc_set_parent(DilloDoc *dd, DilloDoc *parent)
+{
+   g_return_if_fail (dd != NULL && parent != NULL);
+   
+   dd->parent = parent;
+   a_Doc_set_browserwindow(dd, parent->bw);
+}
+
+/*   
+ * set browserwindow
+ */
+void
+a_Doc_set_browserwindow(DilloDoc *dd, BrowserWindow *bw)
+{
+   DwStyle style_attrs;
+   DwStyleFont font;
+
+   g_return_if_fail (dd != NULL && bw != NULL);
+
+   /* set dd's current window */
+   dd->bw = bw;
+
+   /* connect content_command signal */
+   gtk_signal_connect(GTK_OBJECT(GTK_BIN(dd->docwin)->child),
+		      "content_command",
+		      GTK_SIGNAL_FUNC(a_Commands_handler),
+		      dd);
+   
+   /* Full screen mode via double click is done in two ways: First,
+    * a feature of the selection is used, since in complex pages,
+    * getting events back to the viewport is quite difficult. Second,
+    * a simple callback, called e.g. when viewing image resources.
+    */
+   a_Selection_set_dclick_callback(
+      GTK_DW_VIEWPORT(GTK_BIN(GTK_BIN(dd->docwin)->child)->child)->selection,
+      (void(*)(gpointer))a_Commands_full_screen_callback,
+      bw);
+   /* Selection requires an owner widget */
+   a_Selection_set_owner(
+      GTK_DW_VIEWPORT(GTK_BIN(GTK_BIN(dd->docwin)->child)->child)->selection,
+      GTK_BIN(dd->docwin)->child);
+
+   /* deselect location bar contents when the scrolled_frame gets focus */
+   gtk_signal_connect_object(GTK_OBJECT(GTK_BIN(dd->docwin)->child),
+			     "grab-focus",
+			     a_Interface_content_focus_callback,
+			     (gpointer) bw);
+
+   gtk_signal_connect_object_after(GTK_OBJECT(GTK_BIN(dd->docwin)->child),
+                                   "button_press_event",
+                                   GTK_SIGNAL_FUNC(a_Commands_click_callback),
+                                   (gpointer) bw);
+
+   /* create style for Dw */
+   font.name = prefs.vw_fontname; /* must be defined */
+   font.size = rint(DOC_DEFAULT_FONT_SIZE * prefs.font_factor);
+   font.weight = 400;
+   font.style = DW_STYLE_FONT_STYLE_NORMAL;
+   
+   a_Dw_style_init_values(&style_attrs, dd->bw->main_window->window);
+   a_Dw_style_box_set_val(&style_attrs.margin, DOC_DEFAULT_MARGIN);
+   style_attrs.font = a_Dw_style_font_new (&font);
+   style_attrs.color =
+     a_Dw_style_color_new(prefs.text_color, dd->bw->main_window->window);
+   style_attrs.background_color =
+     a_Dw_style_color_new(prefs.bg_color, dd->bw->main_window->window);
+   dd->style = a_Dw_style_new(&style_attrs, dd->bw->main_window->window);
+}
+
+/*
+ * Reset search and clear search list
+ */
+void a_Doc_findtext_reset(DilloDoc *dd)
+{
+   DilloDoc *current_dd;
+
+   DEBUG_MSG(DEBUG_FIND, "a_Doc_findtext_reset(%p)\n", dd);
+
+   current_dd = g_list_nth_data(dd->search_list,
+				dd->search_list_index);
+
+   if(current_dd) {
+     /* unhighlight search term, reset search */
+     a_Dw_gtk_scrolled_window_reset_search((void *) current_dd->docwin, TRUE);
+   }
+
+   if(dd->search_list)
+     g_list_free(dd->search_list);
+ 
+   dd->search_list = NULL;
+   dd->search_list_index = 0;
+}
+
+/*
+ * Initialise search list
+ */
+void a_Doc_findtext_init(DilloDoc *dd)
+{
+   DEBUG_MSG(DEBUG_FIND, "a_Doc_findtext_init(%p)\n", dd);
+
+   a_Doc_findtext_reset(dd);
+   dd->search_list = a_Doc_get_visible_children(dd);
+   dd->search_list_index = 0;
+}
+
+/*
+ * Search for $query in this document, or
+ * pass the request on to a child document
+ * returns FindTextResult
+ */
+FindtextResult
+a_Doc_findtext_search(DilloDoc *dd,
+		      gchar *query,
+		      gboolean case_sens)
+{
+   DilloDoc *search_dd;
+   FindtextResult return_val;
+
+   DEBUG_MSG(DEBUG_FIND,
+	     "a_Doc_findtext_search(%p,\"%s\",%i)\n",
+	     dd, query, case_sens);
+
+   if(!dd->search_list)
+     a_Doc_findtext_init(dd);
+
+   search_dd = g_list_nth_data(dd->search_list,
+			       dd->search_list_index);
+
+   return_val = a_Dw_gtk_scrolled_window_search(
+	           GTK_DW_SCROLLED_WINDOW(search_dd->docwin),
+		   query, case_sens);
+
+   if(return_val == FINDTEXT_RESTART ||
+      return_val == FINDTEXT_NOT_FOUND) {
+     dd->bw->findtext_current = NULL;
+     a_Dw_gtk_scrolled_window_reset_search((void *) search_dd->docwin, TRUE);
+     a_Dw_gtk_scrolled_window_reset_focus((void *) search_dd->docwin);
+     if(++(dd->search_list_index) < g_list_length(dd->search_list))
+       return_val = a_Doc_findtext_search(dd, query, case_sens);
+     else
+       dd->search_list_index = 0;
+   } else
+     /* search found a result */
+     dd->bw->findtext_current = search_dd;
+     
+   return return_val;
+}
+
+/*
+ * Create a new DilloDoc
+ * (the new document is stored in dillo_doc)
+ */
+DilloDoc *
+a_Doc_new(void)
+{
+   DilloDoc *dd;
+
+   dd = g_new0(DilloDoc, 1);
+   a_List_add(dillo_doc, num_dd, num_dd_max);
+   dillo_doc[num_dd++] = dd;
+
+   a_Nav_init(dd);
+
+   dd->docwin = a_Dw_gtk_scrolled_window_new();
+   dd->style = NULL;
+   dd->parent = NULL;
+
+   dd->frameset = NULL;
+   dd->name = NULL;
+
+   gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(dd->docwin),
+                                  GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+
+   /* this callback NULLs the docwin->gadget (to keep GTK from
+    * complaining when it tries to destroy a non-existing widget - there
+    * should be a check in dw_gtk_scrolled_window to see if scrolled->gadget
+    * really points at a GTK widget...
+    *
+    * it also destroys the DilloDoc by the way...
+    */
+   gtk_signal_connect(GTK_OBJECT(dd->docwin),
+                      "destroy",
+                      GTK_SIGNAL_FUNC(Doc_docwin_destroy_callback),
+                      (gpointer) dd);
+   /* focus the docwin when it is mapped */
+   gtk_signal_connect(GTK_OBJECT(dd->docwin),
+                      "map",
+                      GTK_SIGNAL_FUNC(Doc_docwin_map_callback),
+                      (gpointer) dd);
+
+   gtk_widget_show(dd->docwin);
+
+   /* initialize the rest of the dd's data. */
+   dd->RootClients = NULL;
+   dd->NumRootClients = 0;
+   dd->MaxRootClients = 8;
+
+   dd->ImageClients = NULL;
+   dd->NumImageClients = 0;
+   dd->MaxImageClients = 8;
+   dd->NumImages = 0;
+   dd->NumImagesGot = 0;
+
+   dd->PageUrls = NULL;
+   dd->NumPageUrls = 0;
+   dd->MaxPageUrls = 8;
+
+   dd->pagemarks_menu = NULL;
+   dd->pagemarks_last = NULL;
+
+   dd->CursorType = -1;
+
+   dd->progress = 0.0;
+   dd->ready = TRUE;
+
+   return dd;
+}
+
+
diff -pruN dillo/src/doc.h dillo_tabs/src/doc.h
--- dillo/src/doc.h	1970-01-01 01:00:00.000000000 +0100
+++ dillo_tabs/src/doc.h	2003-11-24 14:32:40.241754688 +0100
@@ -0,0 +1,42 @@
+#ifndef __DOC_H__
+#define __DOC_H__
+
+#include "browser.h"   /* for DilloDoc */
+#include "findtext.h"  /* for FindtextResult */
+
+/* used to set default margin for documents */
+#define DOC_DEFAULT_MARGIN 5
+#define DOC_DEFAULT_FONT_SIZE 12.0
+
+/* macros for documents */
+#define DD_HAS_FOCUS(dd,bw) (bw)->dd == (dd)
+
+DilloDoc * a_Doc_new(void);
+void a_Doc_init(void);
+void a_Doc_stop(DilloDoc *dd);
+void a_Doc_clean(DilloDoc *dd);
+void a_Doc_title_set(DilloDoc *dd, gchar *title);
+void a_Doc_location_set(DilloDoc *dd, gchar *location);
+void a_Doc_set_button_sens(DilloDoc *dd);
+void a_Doc_remove_client(DilloDoc *dd, gint ClientKey);
+void a_Doc_close_client(DilloDoc *dd, gint ClientKey);
+void a_Doc_add_client(DilloDoc *dd, gint Key, gint Root);
+void a_Doc_add_url(DilloDoc *dd, const DilloUrl *Url, gint Flags);
+void a_Doc_destroy(DilloDoc *dd);
+void a_Doc_set_cursor(DilloDoc *dd, GdkCursorType CursorType);
+void a_Doc_set_name(DilloDoc *dd, gchar *name);
+DilloDoc * a_Doc_get_by_name(DilloDoc *dd, gchar *name);
+void a_Doc_set_parent(DilloDoc *dd, DilloDoc *parent);
+void a_Doc_set_browserwindow(DilloDoc *dd, BrowserWindow *bw);
+void a_Doc_progress_update(DilloDoc *dd);
+GList * a_Doc_get_visible_children(DilloDoc *dd);
+DilloDoc * a_Doc_get_by_docwin(GtkWidget *docwin);
+DilloDoc * a_Doc_get_parent(DilloDoc *dd);
+DilloDoc * a_Doc_get_root(DilloDoc *dd);
+void a_Doc_findtext_init(DilloDoc *dd);
+void a_Doc_findtext_reset(DilloDoc *dd);
+FindtextResult a_Doc_findtext_search(DilloDoc *dd,
+				     gchar *query,
+				     gboolean case_sens);
+
+#endif /* __DOC_H__ */
diff -pruN dillo/src/dw_bullet.c dillo_tabs/src/dw_bullet.c
--- dillo/src/dw_bullet.c	2003-11-24 14:32:40.084778552 +0100
+++ dillo_tabs/src/dw_bullet.c	2003-11-24 14:32:40.192762136 +0100
@@ -68,6 +68,7 @@ DwWidget* a_Dw_bullet_new (void)
 static void Dw_bullet_init (DwBullet *bullet)
 {
    int i;
+
    for (i = 0; i < DW_HIGHLIGHT_NUM_LAYERS; i++)
       bullet->selected[i] = FALSE;
 }
@@ -105,6 +106,12 @@ static void Dw_bullet_draw (DwWidget *wi
    int i;
    gboolean selected = FALSE;
 
+   if(DW_WIDGET_HAS_LINK(widget))
+     a_Dw_gtk_viewport_focusable_allocate(widget,
+					  (gpointer) DW_WIDGET_LINK(widget),
+					  widget->allocation.x,
+					  widget->allocation.y);
+
    for (i = 0; i < DW_HIGHLIGHT_NUM_LAYERS && !selected; i++)
       selected = DW_BULLET(widget)->selected[i];
 
@@ -172,7 +179,7 @@ static void Dw_bullet_iterator_highlight
                                           gint end,
                                           DwHighlightLayer layer)
 {
-   if (it->content.type == DW_CONTENT_TEXT) {
+   if (it->content.type & DW_CONTENT_TEXT) {
       DW_BULLET(it->widget)->selected[layer] = (start == 0 && end >= 1);
       p_Dw_widget_queue_draw (it->widget);
    }
diff -pruN dillo/src/dw_button.c dillo_tabs/src/dw_button.c
--- dillo/src/dw_button.c	2003-11-24 14:32:40.085778400 +0100
+++ dillo_tabs/src/dw_button.c	2003-11-24 14:32:40.193761984 +0100
@@ -562,7 +562,7 @@ static DwIterator *Dw_button_iterator (D
                                        gint mask,
                                        gboolean at_end)
 {
-   DwIterator *it = g_new (DwIterator, 1);
+   DwIterator *it = g_new0 (DwIterator, 1);
    it->widget = widget;
    it->mask = mask;
    it->content.type = (at_end ? DW_CONTENT_END : DW_CONTENT_START);
@@ -573,6 +573,8 @@ static DwIterator *Dw_button_iterator (D
    it->free = p_Dw_iterator_free_std;
    it->highlight = p_Dw_iterator_highlight_std;
    it->scroll_to = p_Dw_iterator_scroll_to_std;
+   it->focus = p_Dw_iterator_focus_std;
+   it->get_link = p_Dw_iterator_get_link_std;
    return it;
 }
 
diff -pruN dillo/src/dw_embed_gtk.c dillo_tabs/src/dw_embed_gtk.c
--- dillo/src/dw_embed_gtk.c	2003-11-24 14:32:40.086778248 +0100
+++ dillo_tabs/src/dw_embed_gtk.c	2003-11-24 14:32:40.194761832 +0100
@@ -19,6 +19,15 @@ static void Dw_embed_gtk_draw           
 
 static void Dw_embed_gtk_remove_gtk     (DwEmbedGtk *embed_gtk);
 
+static DwIterator* Dw_embed_gtk_iterator       (DwWidget *widget,
+					        gint32 mask,
+					        gboolean at_end);
+static gboolean Dw_embed_gtk_iterator_next     (DwIterator *it);
+static gboolean Dw_embed_gtk_iterator_prev     (DwIterator *it);
+static void Dw_embed_gtk_iterator_scroll_to    (DwIterator *it,
+						DwPosition pos);
+static void Dw_embed_gtk_iterator_focus        (DwIterator *it);
+
 static GtkWidgetClass *parent_class;
 
 /*
@@ -86,6 +95,7 @@ static void Dw_embed_gtk_class_init (DwE
    widget_class->size_allocate = Dw_embed_gtk_size_allocate;
    widget_class->size_request = Dw_embed_gtk_size_request;
    widget_class->draw = Dw_embed_gtk_draw;
+   widget_class->iterator = Dw_embed_gtk_iterator;
 }
 
 
@@ -172,6 +182,10 @@ static void Dw_embed_gtk_size_allocate (
          gtk_widget_hide (embed_gtk->child);
       else {
          gtk_widget_show (embed_gtk->child);
+	 /* allocate focusable (the widget pointer is used as identifier
+	  * for this focusable (second parameter)) */
+	 a_Dw_gtk_viewport_focusable_allocate(widget, (gpointer) widget,
+					      allocation->x, allocation->y);
 
          if (allocation->width != widget->allocation.width ||
              allocation->ascent + allocation->descent
@@ -294,3 +308,94 @@ void a_Dw_embed_gtk_add_gtk (DwEmbedGtk 
 
    p_Dw_widget_queue_resize (DW_WIDGET (embed_gtk), 0, TRUE);
 }
+
+
+static DwIterator * Dw_embed_gtk_iterator (DwWidget *widget,
+					   gint32 mask,
+					   gboolean at_end)
+{
+   DwIterator *it = NULL;
+
+   if (mask & DW_CONTENT_FOCUSABLE) {
+      it = g_new0 (DwIterator, 1);
+      it->widget = widget;
+      it->mask = mask;
+      it->content.type = (at_end ? DW_CONTENT_END : DW_CONTENT_START);
+      it->next = Dw_embed_gtk_iterator_next;
+      it->prev = Dw_embed_gtk_iterator_prev;
+      it->clone = p_Dw_iterator_clone_std;
+      it->compare = p_Dw_iterator_text_compare;
+      it->free = p_Dw_iterator_free_std;
+      it->highlight = p_Dw_iterator_highlight_std;
+      it->scroll_to = Dw_embed_gtk_iterator_scroll_to;
+      it->get_link = p_Dw_iterator_get_link_std;
+      it->focus = Dw_embed_gtk_iterator_focus;
+   } else
+      it = NULL;
+
+   return (DwIterator*) it;
+}
+
+gboolean Dw_embed_gtk_iterator_next (DwIterator *it)
+{
+   DwEmbedGtk *embed = (DwEmbedGtk *) it->widget;
+   gboolean return_val;
+
+   return_val = FALSE;
+
+   if (embed->child &&
+       it->content.type == DW_CONTENT_START) {
+      it->content.type = DW_CONTENT_FOCUSABLE;
+      return_val = TRUE;
+   } else {
+      it->content.type = DW_CONTENT_END;
+   }
+
+   return return_val;
+}
+
+gboolean Dw_embed_gtk_iterator_prev (DwIterator *it)
+{
+   DwEmbedGtk *embed = (DwEmbedGtk *) it->widget;
+   gboolean return_val;
+
+   return_val = FALSE;
+
+   if (embed->child &&
+       it->content.type == DW_CONTENT_END) {
+      it->content.type = DW_CONTENT_FOCUSABLE;
+      return_val = TRUE;
+   } else {
+      it->content.type = DW_CONTENT_START;
+   }
+
+   return return_val;
+}
+
+void Dw_embed_gtk_iterator_scroll_to (DwIterator *it,
+				      DwPosition pos)
+{
+   /* scroll to the embed_gtk widget itself */
+   if (it->content.type & DW_CONTENT_FOCUSABLE) {
+      switch(pos) {
+      case DW_POSITION_TOP:
+         a_Dw_widget_scroll_to (it->widget, pos, 0);
+         break;
+      case DW_POSITION_CENTER:
+         a_Dw_widget_scroll_to (it->widget, pos, DW_WIDGET_HEIGHT(it->widget) / 2);
+         break;
+      case DW_POSITION_BOTTOM:
+         a_Dw_widget_scroll_to (it->widget, pos, DW_WIDGET_HEIGHT(it->widget));
+         break;
+      }
+   }
+}
+
+void Dw_embed_gtk_iterator_focus (DwIterator *it)
+{
+   DwEmbedGtk *embed = (DwEmbedGtk *) it->widget;
+
+   if (it->content.type & DW_CONTENT_FOCUSABLE &&
+       embed->child)
+     gtk_widget_grab_focus(embed->child);
+}
diff -pruN dillo/src/dw_ext_iterator.c dillo_tabs/src/dw_ext_iterator.c
--- dillo/src/dw_ext_iterator.c	2003-11-24 14:32:40.087778096 +0100
+++ dillo_tabs/src/dw_ext_iterator.c	2003-11-24 14:32:40.195761680 +0100
@@ -54,7 +54,7 @@ static DwIterator *Dw_ext_iterator_searc
    DEBUG_MSG (1, "%*smoving down (%swards) from %s\n",
               indent, "", from_end ? "back" : "for", a_Dw_iterator_text (it));
 
-   g_return_val_if_fail (it->content.type == DW_CONTENT_WIDGET, NULL);
+   g_return_val_if_fail (it->content.type & DW_CONTENT_WIDGET, NULL);
    it2 = a_Dw_widget_iterator (it->content.data.widget, it->mask, from_end);
 
    if (it2 == NULL) {
@@ -67,7 +67,7 @@ static DwIterator *Dw_ext_iterator_searc
       DEBUG_MSG (1, "%*sexamining %s\n",
                  indent, "", a_Dw_iterator_text (it2));
 
-      if (it2->content.type == DW_CONTENT_WIDGET) {
+      if (it2->content.type & DW_CONTENT_WIDGET) {
          /* Another widget. Search in it downwards. */
          it3 = Dw_ext_iterator_search_downward (it2, from_end, indent + 3);
          if (it3 != NULL) {
@@ -103,11 +103,11 @@ static DwIterator *Dw_ext_iterator_searc
    DEBUG_MSG (1, "%*smoving %swards from %s\n",
               indent, "", from_end ? "back" : "for", a_Dw_iterator_text (it));
 
-   g_return_val_if_fail (it->content.type == DW_CONTENT_WIDGET, NULL);
+   g_return_val_if_fail (it->content.type & DW_CONTENT_WIDGET, NULL);
    it2 = a_Dw_iterator_clone (it);
 
    while (from_end ? a_Dw_iterator_prev (it2) : a_Dw_iterator_next (it2)) {
-      if (it2->content.type == DW_CONTENT_WIDGET) {
+      if (it2->content.type & DW_CONTENT_WIDGET) {
          /* Search downwards in this widget. */
          it3 = Dw_ext_iterator_search_downward (it2, from_end, indent + 3);
          if (it3 != NULL) {
@@ -137,7 +137,7 @@ static DwIterator *Dw_ext_iterator_searc
             a_Dw_iterator_free (it2);
             return NULL;
          }
-         if (it2->content.type == DW_CONTENT_WIDGET &&
+         if (it2->content.type & DW_CONTENT_WIDGET &&
              it2->content.data.widget == it->widget) {
             it3 = Dw_ext_iterator_search_sideward (it2, from_end, indent + 3);
             a_Dw_iterator_free (it2);
@@ -181,7 +181,7 @@ DwExtIterator* a_Dw_ext_iterator_new (Dw
 
    /* If "it" points to a widget, find a near non-widget content,
     * since an DwExtIterator should never return widgets. */
-   if (it->content.type == DW_CONTENT_WIDGET) {
+   if (it->content.type & DW_CONTENT_WIDGET) {
       /* The second argument of Dw_ext_iterator_search_downward is
        * actually a matter of taste :-) */
       if ((it2 = Dw_ext_iterator_search_downward (it, FALSE, 3)) ||
@@ -200,7 +200,7 @@ DwExtIterator* a_Dw_ext_iterator_new (Dw
 
    DEBUG_MSG (1, "  => %s\n", a_Dw_iterator_text (it));
 
-   eit = g_new (DwExtIterator, 1);
+   eit = g_new0 (DwExtIterator, 1);
    eit->stack_top = 0;
    /* If this widget has parents, we must construct appropiate iterators.
     * todo: There may be a faster way instead of iterating through the
@@ -210,7 +210,7 @@ DwExtIterator* a_Dw_ext_iterator_new (Dw
    eit->stack_max = 4;
    while (eit->stack_top >= eit->stack_max)
       eit->stack_max <<= 1;
-   eit->stack = g_new (DwIterator*, eit->stack_max);
+   eit->stack = g_new0 (DwIterator*, eit->stack_max);
 
    /* Construct the iterators. */
    for (w = it->widget, sp = eit->stack_top - 1;
@@ -222,7 +222,7 @@ DwExtIterator* a_Dw_ext_iterator_new (Dw
             g_warning ("BUG in DwExtIterator!");
             return NULL;
          }
-         if (eit->stack[sp]->content.type == DW_CONTENT_WIDGET &&
+         if (eit->stack[sp]->content.type & DW_CONTENT_WIDGET &&
              eit->stack[sp]->content.data.widget == w)
             break;
       }
@@ -258,7 +258,7 @@ gboolean a_Dw_ext_iterator_next (DwExtIt
    DwIterator *it = eit->stack[eit->stack_top];
 
    if (a_Dw_iterator_next(it)) {
-      if (it->content.type == DW_CONTENT_WIDGET) {
+      if (it->content.type & DW_CONTENT_WIDGET) {
          /* Widget: new iterator on stack, to search in this widget. */
          eit->stack_top++;
          a_List_add (eit->stack, eit->stack_top, eit->stack_max);
@@ -294,7 +294,7 @@ gboolean a_Dw_ext_iterator_prev (DwExtIt
    DwIterator *it = eit->stack[eit->stack_top];
 
    if (a_Dw_iterator_prev(it)) {
-      if (it->content.type == DW_CONTENT_WIDGET) {
+      if (it->content.type & DW_CONTENT_WIDGET) {
          /* Widget: new iterator on stack, to search in this widget. */
          eit->stack_top++;
          a_List_add (eit->stack, eit->stack_top, eit->stack_max);
@@ -329,13 +329,12 @@ gboolean a_Dw_ext_iterator_prev (DwExtIt
 DwExtIterator* a_Dw_ext_iterator_clone (DwExtIterator *eit)
 {
    int i;
-   DwExtIterator *eit2 = g_new (DwExtIterator, 1);
+   DwExtIterator *eit2 = g_new0 (DwExtIterator, 1);
 
    *eit2 = *eit;
-   eit2->stack = g_new (DwIterator*, eit2->stack_max);
+   eit2->stack = g_new0 (DwIterator*, eit2->stack_max);
    for (i = 0; i <= eit2->stack_top; i++)
       eit2->stack[i] = a_Dw_iterator_clone (eit->stack[i]);
-
    return eit2;
 }
 
@@ -381,7 +380,7 @@ DwWordIterator* a_Dw_word_iterator_new (
    DwWordIterator *it;
    DwIterator *it0;
 
-   it = g_new (DwWordIterator, 1);
+   it = g_new0 (DwWordIterator, 1);
    it->word = NULL;
    it0 = a_Dw_widget_iterator (widget, DW_CONTENT_TEXT | DW_CONTENT_WIDGET,
                                FALSE);
@@ -502,7 +501,7 @@ DwWordIterator* a_Dw_word_iterator_clone
 {
    DwWordIterator *it2;
 
-   it2 = g_new (DwWordIterator, 1);
+   it2 = g_new0 (DwWordIterator, 1);
    *it2 = *it;
    it2->iterator = a_Dw_ext_iterator_clone (it->iterator);
    it2->word = it->word ? g_strdup (it->word) : NULL;
diff -pruN dillo/src/dw_gtk_scrolled_frame.c dillo_tabs/src/dw_gtk_scrolled_frame.c
--- dillo/src/dw_gtk_scrolled_frame.c	2003-11-24 14:32:40.088777944 +0100
+++ dillo_tabs/src/dw_gtk_scrolled_frame.c	2003-11-24 14:32:40.196761528 +0100
@@ -18,6 +18,7 @@
 #include <gtk/gtk.h>
 #include <gdk/gdkkeysyms.h>
 #include "dw_gtk_scrolled_frame.h"
+#include "dw_gtk_viewport.h"
 
 static GtkBinClass *parent_class = NULL;
 
@@ -41,8 +42,6 @@ static void Dw_gtk_scrolled_frame_draw  
 static void Dw_gtk_scrolled_frame_draw_focus    (GtkWidget *widget);
 static gint Dw_gtk_scrolled_frame_expose        (GtkWidget *widget,
                                                  GdkEventExpose *event);
-static gint Dw_gtk_scrolled_frame_key_press     (GtkWidget *widget,
-                                                 GdkEventKey *event);
 static gint Dw_gtk_scrolled_frame_focus_in      (GtkWidget *widget,
                                                  GdkEventFocus *event);
 static gint Dw_gtk_scrolled_frame_focus_out     (GtkWidget *widget,
@@ -68,6 +67,13 @@ static void Dw_gtk_scrolled_frame_set_sc
                                                           GtkAdjustment
                                                           *vadjustment);
 
+static void Dw_gtk_scrolled_frame_viewport_move (GtkDwScrolledFrame *frame,
+						 DwScrollDirection direction);
+static void Dw_gtk_scrolled_frame_focus_move (GtkDwScrolledFrame *frame,
+					       GtkDirectionType direction);
+static void Dw_gtk_scrolled_frame_focus_hide (GtkDwScrolledFrame *frame);
+static void Dw_gtk_scrolled_frame_focus_activate (GtkDwScrolledFrame *frame,
+						  GdkModifierType modifier);
 
 static void Dw_gtk_scrolled_frame_move_x_to (GtkDwScrolledFrame *frame,
                                              gfloat x);
@@ -87,6 +93,11 @@ enum
    SET_SCROLL_ADJUSTMENTS,
    USER_HCHANGED,
    USER_VCHANGED,
+   VIEWPORT_MOVE,
+   FOCUS_MOVE,
+   FOCUS_HIDE,
+   FOCUS_ACTIVATE,
+   CONTENT_COMMAND,
    LAST_SIGNAL
 };
 
@@ -129,6 +140,8 @@ GtkWidget* a_Dw_gtk_scrolled_frame_new (
 
    widget = gtk_widget_new (GTK_TYPE_DW_SCROLLED_FRAME, NULL);
 
+   gtk_widget_set_name(widget, "dillo-content");
+
    frame = GTK_DW_SCROLLED_FRAME (widget);
    frame->hadjustment = hadjustment;
    frame->vadjustment = vadjustment;
@@ -200,9 +213,55 @@ static void Dw_gtk_scrolled_frame_class_
                                          user_vchanged),
                       gtk_marshal_NONE__NONE,
                       GTK_TYPE_NONE, 0);
-
+   frame_signals[VIEWPORT_MOVE] =
+      gtk_signal_new ("viewport_move",
+		      GTK_RUN_LAST | GTK_RUN_ACTION,
+		      object_class->type,
+		      GTK_SIGNAL_OFFSET (GtkDwScrolledFrameClass,
+					 viewport_move),
+		      gtk_marshal_NONE__ENUM,
+		      GTK_TYPE_NONE,
+		      1, GTK_TYPE_ENUM);
+   frame_signals[FOCUS_MOVE] = 
+      gtk_signal_new ("focus_move",
+		      GTK_RUN_LAST | GTK_RUN_ACTION,
+		      object_class->type,
+		      GTK_SIGNAL_OFFSET (GtkDwScrolledFrameClass,
+					 focus_move),
+		      gtk_marshal_NONE__ENUM,
+		      GTK_TYPE_NONE,
+		      1, GTK_TYPE_DIRECTION_TYPE);
+   frame_signals[FOCUS_HIDE] =
+      gtk_signal_new ("focus_hide",
+                      GTK_RUN_LAST | GTK_RUN_ACTION,
+                      object_class->type,
+                      GTK_SIGNAL_OFFSET (GtkDwScrolledFrameClass,
+                                         focus_hide),
+                      gtk_marshal_NONE__NONE,
+                      GTK_TYPE_NONE, 0);
+   frame_signals[FOCUS_ACTIVATE] =
+      gtk_signal_new ("focus_activate",
+                      GTK_RUN_LAST | GTK_RUN_ACTION,
+                      object_class->type,
+                      GTK_SIGNAL_OFFSET (GtkDwScrolledFrameClass,
+                                         focus_activate),
+                      gtk_marshal_NONE__ENUM,
+                      GTK_TYPE_NONE,
+		      1, GTK_TYPE_ENUM);
+   frame_signals[CONTENT_COMMAND] =
+      gtk_signal_new ("content_command",
+                      GTK_RUN_LAST | GTK_RUN_ACTION,
+                      object_class->type,
+                      GTK_SIGNAL_OFFSET (GtkDwScrolledFrameClass,
+                                         content_command),
+                      gtk_marshal_NONE__ENUM,
+                      GTK_TYPE_NONE,
+		      1, GTK_TYPE_ENUM);
+   
    gtk_object_class_add_signals (object_class, frame_signals, LAST_SIGNAL);
 
+   /* default bindings for the scrolled_frame are set in a_Commands_init() */
+
    object_class->destroy = Dw_gtk_scrolled_frame_destroy;
 
    widget_class->realize = Dw_gtk_scrolled_frame_realize;
@@ -212,7 +271,6 @@ static void Dw_gtk_scrolled_frame_class_
    widget_class->draw_focus = Dw_gtk_scrolled_frame_draw_focus;
    widget_class->draw = Dw_gtk_scrolled_frame_draw;
    widget_class->expose_event = Dw_gtk_scrolled_frame_expose;
-   widget_class->key_press_event = Dw_gtk_scrolled_frame_key_press;
    widget_class->focus_in_event = Dw_gtk_scrolled_frame_focus_in;
    widget_class->focus_out_event = Dw_gtk_scrolled_frame_focus_out;
    widget_class->button_press_event = Dw_gtk_scrolled_frame_button_press;
@@ -226,6 +284,11 @@ static void Dw_gtk_scrolled_frame_class_
       Dw_gtk_scrolled_frame_set_scroll_adjustments;
    klass->user_hchanged = NULL;
    klass->user_vchanged = NULL;
+   klass->viewport_move = Dw_gtk_scrolled_frame_viewport_move;
+   klass->focus_move = Dw_gtk_scrolled_frame_focus_move;
+   klass->focus_hide = Dw_gtk_scrolled_frame_focus_hide;
+   klass->focus_activate = Dw_gtk_scrolled_frame_focus_activate;
+   klass->content_command = NULL;
 }
 
 
@@ -438,103 +501,14 @@ static gint Dw_gtk_scrolled_frame_expose
 /*
  * Standard Gtk+ function
  */
-static gint Dw_gtk_scrolled_frame_key_press (GtkWidget *widget,
-                                             GdkEventKey *event)
-{
-   GtkContainer *container;
-   GtkDwScrolledFrame *frame;
-
-   container = GTK_CONTAINER (widget);
-   frame = GTK_DW_SCROLLED_FRAME (widget);
-
-   switch (event->keyval) {
-   case GDK_Up:
-   case GDK_KP_Up:
-      Dw_gtk_scrolled_frame_move_by (frame, 0,
-                                     - frame->vadjustment->step_increment);
-      return TRUE;
-
-   case GDK_Down:
-   case GDK_KP_Down:
-      Dw_gtk_scrolled_frame_move_by (frame, 0,
-                                     + frame->vadjustment->step_increment);
-      return TRUE;
-
-   case GDK_Left:
-   case GDK_KP_Left:
-      Dw_gtk_scrolled_frame_move_by (frame,
-                                     - frame->hadjustment->step_increment, 0);
-      return TRUE;
-
-   case GDK_Right:
-   case GDK_KP_Right:
-      Dw_gtk_scrolled_frame_move_by (frame,
-                                     + frame->hadjustment->step_increment, 0);
-      return TRUE;
-
-   case GDK_Tab:
-      if (event->state & GDK_SHIFT_MASK)
-         return gtk_container_focus (container, GTK_DIR_TAB_BACKWARD);
-      else
-         return gtk_container_focus (container, GTK_DIR_TAB_FORWARD);
-
-   case GDK_b: case GDK_B:
-      if (event->state & GDK_MOD1_MASK)
-         return FALSE;
-      /* continues */
-   case GDK_Page_Up:
-   case GDK_KP_Page_Up:
-      if (event->state & GDK_CONTROL_MASK)
-         Dw_gtk_scrolled_frame_move_by (frame,
-                                        - frame->hadjustment->page_increment,
-                                        0);
-      else
-         Dw_gtk_scrolled_frame_move_by (frame, 0,
-                                        - frame->vadjustment->page_increment);
-      return TRUE;
-
-   case GDK_Page_Down:
-   case GDK_KP_Page_Down:
-   case GDK_space:
-      if (event->state & GDK_CONTROL_MASK)
-         Dw_gtk_scrolled_frame_move_by (frame,
-                                        + frame->hadjustment->page_increment,
-                                        0);
-      else
-         Dw_gtk_scrolled_frame_move_by (frame, 0,
-                                        + frame->vadjustment->page_increment);
-      return TRUE;
-
-   case GDK_Home:
-   case GDK_KP_Home:
-      if (event->state & GDK_CONTROL_MASK)
-         Dw_gtk_scrolled_frame_move_x_to (frame, 0);
-      else
-         Dw_gtk_scrolled_frame_move_y_to (frame, 0);
-      return TRUE;
-
-   case GDK_End:
-   case GDK_KP_End:
-      if (event->state & GDK_CONTROL_MASK)
-         Dw_gtk_scrolled_frame_move_x_to (frame, G_MAXFLOAT);
-      else
-         Dw_gtk_scrolled_frame_move_y_to (frame, G_MAXFLOAT);
-      return TRUE;
-
-   default:
-      return FALSE;
-   }
-}
-
-
-/*
- * Standard Gtk+ function
- */
 static gint Dw_gtk_scrolled_frame_focus_in (GtkWidget *widget,
                                             GdkEventFocus *event)
 {
+   GtkDwViewport *viewport = (GtkDwViewport *) GTK_BIN(widget)->child;
+
    GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
    gtk_widget_draw_focus (widget);
+   a_Focus_state_draw_focus(viewport->focus_state, TRUE);
    return FALSE;
 }
 
@@ -545,8 +519,11 @@ static gint Dw_gtk_scrolled_frame_focus_
 static gint Dw_gtk_scrolled_frame_focus_out (GtkWidget *widget,
                                              GdkEventFocus *event)
 {
+   GtkDwViewport *viewport = (GtkDwViewport *) GTK_BIN(widget)->child;
+
    GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
    gtk_widget_draw_focus (widget);
+   a_Focus_state_draw_focus(viewport->focus_state, FALSE);
    return FALSE;
 }
 
@@ -637,66 +614,30 @@ static void Dw_gtk_scrolled_frame_add (G
                  "child is non scrollable");
 }
 
-
 /*
  * Standard Gtk+ function
  */
 static gint Dw_gtk_scrolled_frame_focus (GtkContainer *container,
-                                         GtkDirectionType direction)
+					 GtkDirectionType direction)
 {
-   /* todo:
-    * This crashed if the child is not a container (what will never happen
-    * in dillo, where GtkDwScrolledFrame always contains a GtkDwViewport).
-    */
    GtkDwScrolledFrame *frame;
+   gboolean return_val;
 
    frame = GTK_DW_SCROLLED_FRAME (container);
+   return_val = FALSE;
 
-   if (!GTK_WIDGET_HAS_FOCUS (container) && container->focus_child == NULL){
-      // no focus on the GtkDwScrolledFrame or any child widget => grab focus!
+   if (GTK_WIDGET_CAN_FOCUS (container) &&
+       !GTK_WIDGET_HAS_FOCUS (container) &&
+       container->focus_child == NULL ) {
       gtk_widget_grab_focus (GTK_WIDGET (container));
-      return TRUE;
-   }
-
-   switch (direction) {
-   case GTK_DIR_TAB_FORWARD:
-      /* deliver to child */
-      if (!gtk_container_focus (GTK_CONTAINER (GTK_BIN(container)->child),
-                                direction)) {
-         /* todo: it would be nice to keep focus on this last child widget,
-          * instead of the container... Anyway this may change with GTK2 */
-         gtk_widget_grab_focus (GTK_WIDGET (container));
-      }
-      return TRUE;
-
-   case GTK_DIR_TAB_BACKWARD:
-      if (GTK_WIDGET_HAS_FOCUS (container)) {
-         /* will focus the widget "before" */
-         return FALSE;
-      } else {
-         if (!gtk_container_focus (GTK_CONTAINER (GTK_BIN(container)->child),
-                                   GTK_DIR_TAB_BACKWARD))
-            /* first child of the child unfocussed */
-            gtk_widget_grab_focus (GTK_WIDGET (container));
-
-         return TRUE;
-      }
-      break;
-
-   /*
-   case GTK_DIR_LEFT:
-   case GTK_DIR_RIGHT:
-   case GTK_DIR_UP:
-   case GTK_DIR_DOWN:
-   */
-   default:
-      /* focus the GtkDwScrolledFrame */
-      gtk_widget_grab_focus (GTK_WIDGET (container));
-      return TRUE;
-   }
+      return_val = TRUE;
+   } else
+     if (GTK_IS_CONTAINER(GTK_BIN(container)->child))
+       return_val = 
+	 gtk_container_focus(GTK_CONTAINER(GTK_BIN(container)->child),
+			     direction);
 
-   /* make compiler happy */
-   return FALSE;
+   return return_val;
 }
 
 
@@ -729,6 +670,102 @@ Dw_gtk_scrolled_frame_set_scroll_adjustm
    }
 }
 
+/*
+ * Move viewport focus (used for keyboard navigation)
+ * in <direction>
+ */
+static void
+Dw_gtk_scrolled_frame_focus_move (GtkDwScrolledFrame *frame,
+				  GtkDirectionType direction)
+{
+   gint return_val;
+
+   gtk_signal_emit_by_name(GTK_OBJECT(frame), "focus",
+			   direction, &return_val);
+}
+
+/*
+ * Activate focus object (used for keyboard navigation)
+ */
+static void
+Dw_gtk_scrolled_frame_focus_activate (GtkDwScrolledFrame *frame,
+				      GdkModifierType modifier)
+{
+   GtkDwViewport *viewport = (GtkDwViewport *) GTK_BIN(frame)->child;
+
+   a_Focus_state_activate(viewport->focus_state, modifier);
+}
+
+/*
+ * Hide focus (used for keyboard navigation)
+ */
+static void
+Dw_gtk_scrolled_frame_focus_hide (GtkDwScrolledFrame *frame)
+{
+   GtkDwViewport *viewport = (GtkDwViewport *) GTK_BIN(frame)->child;
+
+   a_Focus_state_reset(viewport->focus_state);
+}
+
+/*
+ * Move (scroll) viewport in <direction>
+ * <direction> DwScrollDirection is defined in dw_widget.h
+ */
+static void
+Dw_gtk_scrolled_frame_viewport_move (GtkDwScrolledFrame *frame,
+				     DwScrollDirection direction)
+{
+   switch(direction) {
+   case DW_SCROLL_STEP_BACKWARD:
+      Dw_gtk_scrolled_frame_move_by (frame, 0,
+				     - frame->vadjustment->step_increment);
+      break;
+   case DW_SCROLL_STEP_FORWARD:
+      Dw_gtk_scrolled_frame_move_by (frame, 0,
+				     + frame->vadjustment->step_increment);
+      break;
+   case DW_SCROLL_STEP_LEFT:
+      Dw_gtk_scrolled_frame_move_by (frame,
+				     - frame->hadjustment->step_increment, 0);
+      break;
+   case DW_SCROLL_STEP_RIGHT:
+      Dw_gtk_scrolled_frame_move_by (frame,
+				     + frame->hadjustment->step_increment, 0);
+      break;
+   case DW_SCROLL_PAGE_BACKWARD:
+      Dw_gtk_scrolled_frame_move_by (frame, 0,
+				     - frame->vadjustment->page_increment);
+      break;
+   case DW_SCROLL_PAGE_FORWARD:
+      Dw_gtk_scrolled_frame_move_by (frame, 0,
+				     + frame->vadjustment->page_increment);
+      break;
+   case DW_SCROLL_PAGE_LEFT:
+      Dw_gtk_scrolled_frame_move_by (frame,
+				     - frame->hadjustment->page_increment,
+				     0);
+      break;
+   case DW_SCROLL_PAGE_RIGHT:
+      Dw_gtk_scrolled_frame_move_by (frame,
+				     + frame->hadjustment->page_increment,
+				     0);
+      break;
+   case DW_SCROLL_EDGE_BACKWARD:
+      Dw_gtk_scrolled_frame_move_y_to (frame, 0);
+      break;
+   case DW_SCROLL_EDGE_FORWARD:
+      Dw_gtk_scrolled_frame_move_y_to (frame, G_MAXFLOAT); 
+      break;
+   case DW_SCROLL_EDGE_LEFT:
+      Dw_gtk_scrolled_frame_move_x_to (frame, 0);
+      break;
+   case DW_SCROLL_EDGE_RIGHT:
+      Dw_gtk_scrolled_frame_move_x_to (frame, G_MAXFLOAT);
+      break;
+   default:
+      break;
+   }
+}
 
 /*
  * Functions for moving the viewport. This is queued and done in an
diff -pruN dillo/src/dw_gtk_scrolled_frame.h dillo_tabs/src/dw_gtk_scrolled_frame.h
--- dillo/src/dw_gtk_scrolled_frame.h	2003-11-24 14:32:40.089777792 +0100
+++ dillo_tabs/src/dw_gtk_scrolled_frame.h	2003-11-24 14:32:40.197761376 +0100
@@ -4,6 +4,8 @@
 #include <gdk/gdk.h>
 #include <gtk/gtkbin.h>
 #include <gtk/gtkadjustment.h>
+#include "dw_widget.h"            /* for DwScrollDirection */
+#include "commands.h"             /* for DilloCommand */
 
 #ifdef __cplusplus
 extern "C" {
@@ -22,7 +24,6 @@ extern "C" {
 typedef struct _GtkDwScrolledFrame      GtkDwScrolledFrame;
 typedef struct _GtkDwScrolledFrameClass GtkDwScrolledFrameClass;
 
-
 struct _GtkDwScrolledFrame
 {
    GtkBin bin;
@@ -53,6 +54,15 @@ struct _GtkDwScrolledFrameClass
                                    GtkAdjustment  *vadjustment);
    void (*user_hchanged)          (GtkDwScrolledFrame *scrolled_frame);
    void (*user_vchanged)          (GtkDwScrolledFrame *scrolled_frame);
+   void (*viewport_move)          (GtkDwScrolledFrame *scrolled_frame,
+				   DwScrollDirection direction);
+   void (*focus_move)             (GtkDwScrolledFrame *scrolled_frame,
+				   GtkDirectionType direction);
+   void (*focus_hide)             (GtkDwScrolledFrame *scrolled_frame);
+   void (*focus_activate)         (GtkDwScrolledFrame *scrolled_frame,
+				   GdkModifierType modifier);
+   void (*content_command)        (GtkDwScrolledFrame *scrolled_frame,
+				   DilloCommand command);
 };
 
 
diff -pruN dillo/src/dw_gtk_scrolled_window.c dillo_tabs/src/dw_gtk_scrolled_window.c
--- dillo/src/dw_gtk_scrolled_window.c	2003-11-24 14:32:40.089777792 +0100
+++ dillo_tabs/src/dw_gtk_scrolled_window.c	2003-11-24 14:32:40.197761376 +0100
@@ -39,6 +39,9 @@ static void Dw_gtk_scrolled_window_foral
                                                   GtkCallback callback,
                                                   gpointer callback_data);
 
+static gboolean Dw_gtk_scrolled_window_focus     (GtkContainer *container,
+						  GtkDirectionType direction);
+
 static void Dw_gtk_scrolled_window_changed1  (GtkDwScrolledWindow *scrolled);
 static void Dw_gtk_scrolled_window_changed2  (GtkDwScrolledWindow *scrolled);
 
@@ -170,6 +173,7 @@ static void Dw_gtk_scrolled_window_class
 
    container_class->remove = Dw_gtk_scrolled_window_remove;
    container_class->forall = Dw_gtk_scrolled_window_forall;
+   container_class->focus = Dw_gtk_scrolled_window_focus;
 }
 
 /*
@@ -329,6 +333,16 @@ static void Dw_gtk_scrolled_window_foral
       callback (scrolled->gadget, callback_data);
 }
 
+/*
+ * Standard Gtk+ function
+ */
+static gboolean Dw_gtk_scrolled_window_focus (GtkContainer *container,
+					  GtkDirectionType direction)
+{
+   return
+     gtk_container_focus(GTK_CONTAINER(GTK_BIN(container)->child),
+			 direction);
+}
 
 /*
  * Adds the gadget widget.
@@ -407,6 +421,28 @@ void a_Dw_gtk_scrolled_window_set_scroll
    a_Dw_gtk_viewport_set_scrolling_position (GTK_DW_VIEWPORT (viewport), pos);
 }
 
+/*
+ * Get cursor
+ */
+DwLocation *
+a_Dw_gtk_scrolled_window_get_cursor (GtkDwScrolledWindow *scrolled)
+{
+   GtkDwViewport *viewport = GTK_DW_VIEWPORT(GTK_BIN(GTK_BIN(scrolled)->child)->child);
+
+   return a_Dw_gtk_viewport_get_cursor(viewport);
+}
+
+/*
+ * Set cursor
+ */
+void a_Dw_gtk_scrolled_window_set_cursor (GtkDwScrolledWindow *scrolled,
+					  DwLocation *cursor)
+{
+   GtkWidget *viewport;
+
+   viewport = GTK_BIN(GTK_BIN(scrolled)->child)->child;
+   a_Dw_gtk_viewport_set_cursor (GTK_DW_VIEWPORT (viewport), cursor);
+}
 
 /*
  * See also Dw_gtk_scrolled_window_init.
@@ -439,23 +475,44 @@ gboolean a_Dw_gtk_scrolled_window_search
                                           gchar *string,
                                           gboolean case_sens)
 {
-   GtkWidget *viewport;
+   GtkDwViewport *viewport;
+   FindtextResult return_val;
 
-   viewport = GTK_BIN(GTK_BIN(scrolled)->child)->child;
-   return
+   viewport = GTK_DW_VIEWPORT(GTK_BIN(GTK_BIN(scrolled)->child)->child);
+
+   return_val = 
       a_Findtext_search (GTK_DW_VIEWPORT(viewport)->findtext_state, string,
                          case_sens);
+
+   if(return_val == FINDTEXT_SUCCESS)
+     a_Focus_state_focus(viewport->focus_state,
+	      viewport->findtext_state->hl_iterator->iterator->stack[
+	       viewport->findtext_state->hl_iterator->iterator->stack_top]);
+
+   return (gboolean) return_val;
 }
 
 /*
  * Convenience function. See a_Findtext_reset_search.
  */
-void a_Dw_gtk_scrolled_window_reset_search (GtkDwScrolledWindow *scrolled)
+void a_Dw_gtk_scrolled_window_reset_search (GtkDwScrolledWindow *scrolled,
+					    gboolean reset)
+{
+   GtkWidget *viewport;
+
+   viewport = GTK_BIN(GTK_BIN(scrolled)->child)->child;
+   a_Findtext_reset_search (GTK_DW_VIEWPORT(viewport)->findtext_state, reset);
+}
+
+/*
+ * Convenience function. See a_Focus_state_reset.
+ */
+void a_Dw_gtk_scrolled_window_reset_focus (GtkDwScrolledWindow *scrolled)
 {
    GtkWidget *viewport;
 
    viewport = GTK_BIN(GTK_BIN(scrolled)->child)->child;
-   a_Findtext_reset_search (GTK_DW_VIEWPORT(viewport)->findtext_state);
+   a_Focus_state_reset (GTK_DW_VIEWPORT(viewport)->focus_state);
 }
 
 /*
diff -pruN dillo/src/dw_gtk_scrolled_window.h dillo_tabs/src/dw_gtk_scrolled_window.h
--- dillo/src/dw_gtk_scrolled_window.h	2003-11-24 14:32:40.089777792 +0100
+++ dillo_tabs/src/dw_gtk_scrolled_window.h	2003-11-24 14:32:40.198761224 +0100
@@ -57,11 +57,18 @@ gint       a_Dw_gtk_scrolled_window_get_
               GtkDwScrolledWindow *scrolled);
 void       a_Dw_gtk_scrolled_window_set_scrolling_position (
               GtkDwScrolledWindow *scrolled, gint pos);
+DwLocation * a_Dw_gtk_scrolled_window_get_cursor (
+	      GtkDwScrolledWindow *scrolled);
+void       a_Dw_gtk_scrolled_window_set_cursor (
+              GtkDwScrolledWindow *scrolled,
+              DwLocation *cursor);
 
 gboolean   a_Dw_gtk_scrolled_window_search (
               GtkDwScrolledWindow *scrolled, gchar *string,
               gboolean case_sens);
 void       a_Dw_gtk_scrolled_window_reset_search (
+              GtkDwScrolledWindow *scrolled, gboolean reset);
+void       a_Dw_gtk_scrolled_window_reset_focus (
               GtkDwScrolledWindow *scrolled);
 
 DwWidget*  a_Dw_gtk_scrolled_window_widget_at_viewport_point (
diff -pruN dillo/src/dw_gtk_viewport.c dillo_tabs/src/dw_gtk_viewport.c
--- dillo/src/dw_gtk_viewport.c	2003-11-24 14:32:40.092777336 +0100
+++ dillo_tabs/src/dw_gtk_viewport.c	2003-11-24 14:32:40.200760920 +0100
@@ -39,6 +39,8 @@ static void Dw_gtk_viewport_draw        
                                            GdkRectangle *area);
 static gint Dw_gtk_viewport_expose        (GtkWidget *widget,
                                            GdkEventExpose *event);
+static gint Dw_gtk_viewport_focus         (GtkContainer *container,
+					   GtkDirectionType direction);
 static gint Dw_gtk_viewport_button_press  (GtkWidget *widget,
                                            GdkEventButton *event);
 static gint Dw_gtk_viewport_button_release(GtkWidget *widget,
@@ -117,7 +119,7 @@ GtkWidget* a_Dw_gtk_viewport_new (GtkAdj
 static void Dw_gtk_viewport_init (GtkDwViewport *viewport)
 {
    GTK_WIDGET_UNSET_FLAGS (viewport, GTK_NO_WINDOW);
-   GTK_WIDGET_UNSET_FLAGS (viewport, GTK_CAN_FOCUS);
+   GTK_WIDGET_SET_FLAGS (viewport, GTK_CAN_FOCUS);
 
    /* Without this, gtk_layout_{draw|expose} will clear the window.
       Look at gtklayout.c */
@@ -130,6 +132,7 @@ static void Dw_gtk_viewport_init (GtkDwV
    viewport->anchor = NULL;
    viewport->anchor_idle_id = 0;
    viewport->findtext_state = a_Findtext_state_new ();
+   viewport->focus_state = a_Focus_state_new ();
    viewport->selection = a_Selection_new ();
    viewport->anchors_table = g_hash_table_new (g_str_hash, g_str_equal);
 }
@@ -142,14 +145,18 @@ static void Dw_gtk_viewport_class_init (
 {
    GtkObjectClass *object_class;
    GtkWidgetClass *widget_class;
+   GtkContainerClass *container_class;
 
    parent_class = gtk_type_class (gtk_layout_get_type ());
 
    object_class = (GtkObjectClass*) klass;
    widget_class = (GtkWidgetClass*) klass;
+   container_class = (GtkContainerClass *) klass;
 
    object_class->destroy = Dw_gtk_viewport_destroy;
 
+   container_class->focus = Dw_gtk_viewport_focus;
+
    widget_class->realize = Dw_gtk_viewport_realize;
    widget_class->unrealize = Dw_gtk_viewport_unrealize;
    widget_class->draw = Dw_gtk_viewport_draw;
@@ -344,6 +351,7 @@ static void Dw_gtk_viewport_draw (GtkWid
 
 /*
  * Standard Gtk+ function
+ * Used by keyboard navigation to retain focus for viewport
  */
 static gint Dw_gtk_viewport_expose (GtkWidget *widget,
                                     GdkEventExpose *event)
@@ -352,6 +360,17 @@ static gint Dw_gtk_viewport_expose (GtkW
    return GTK_WIDGET_CLASS(parent_class)->expose_event (widget, event);
 }
 
+/*
+ * Standard Gtk+ function
+ */
+static gint Dw_gtk_viewport_focus (GtkContainer *container,
+				   GtkDirectionType direction)
+{
+   GtkDwViewport *viewport = (GtkDwViewport *) container;
+
+   return 
+      a_Focus_state_focus_next(viewport->focus_state, direction);
+}
 
 /*
  * Handle the mouse event and deliver it to the Dw widget.
@@ -382,7 +401,6 @@ static gint Dw_gtk_viewport_mouse_event 
    return FALSE;
 }
 
-
 /*
  * Standard Gtk+ function
  */
@@ -486,6 +504,7 @@ void a_Dw_gtk_viewport_add_dw (GtkDwView
    Dw_gtk_viewport_remove_anchor (viewport);
 
    a_Findtext_state_set_widget (viewport->findtext_state, widget);
+   a_Focus_state_set_widget(viewport->focus_state, widget);
    a_Selection_reset (viewport->selection);
 }
 
@@ -503,6 +522,7 @@ void a_Dw_gtk_viewport_add_dw (GtkDwView
 void Dw_gtk_viewport_remove_dw (GtkDwViewport *viewport)
 {
    a_Findtext_state_set_widget (viewport->findtext_state, NULL);
+   a_Focus_state_set_widget (viewport->focus_state, NULL);
    viewport->child = NULL;
    Dw_gtk_viewport_remove_anchor (viewport);
    Dw_gtk_viewport_calc_size (viewport);
@@ -831,7 +851,6 @@ void a_Dw_gtk_viewport_set_anchor (GtkDw
    }
 }
 
-
 /*
  * Sets the position to scroll to. The current anchor will be removed.
  */
@@ -842,6 +861,74 @@ void a_Dw_gtk_viewport_set_scrolling_pos
    Dw_gtk_viewport_scroll_to (viewport, pos);
 }
 
+/*************
+ * focusable *
+ *************/
+
+/*
+ * Add focusable to focus_state
+ */
+void a_Dw_gtk_viewport_focusable_add (DwWidget *widget, gpointer link,
+				      DwIterator *it, DwFocusableType type)
+{
+   FocusState *state;
+
+   state = GTK_DW_VIEWPORT(widget->viewport)->focus_state;
+
+   a_Focus_state_focusable_add (state, link, it, type);
+}
+
+/*
+ * Allocate focusable
+ */
+void a_Dw_gtk_viewport_focusable_allocate (DwWidget *widget, gpointer link,
+					   gint32 x, gint32 y)
+{
+   FocusState *state;
+
+   state = GTK_DW_VIEWPORT(widget->viewport)->focus_state;
+
+   a_Focus_state_focusable_allocate (state, link, x, y);
+}
+
+/*
+ * Get cursor (used for keyboard navigation)
+ */
+DwLocation *
+a_Dw_gtk_viewport_get_cursor (GtkDwViewport *viewport)
+{
+   return a_Focus_state_get_cursor(viewport->focus_state);
+}
+
+/*
+ * Set cursor (used for keyboard navigation)
+ */
+void a_Dw_gtk_viewport_set_cursor (GtkDwViewport *viewport,
+				   DwLocation *cursor)
+{
+   a_Focus_state_set_cursor(viewport->focus_state, cursor);
+}
+
+/*
+ * Get viewport view (window on world)
+ */
+DwRectangle *
+a_Dw_gtk_viewport_get_view (GtkDwViewport *viewport)
+{
+   DwRectangle *view = &viewport->view;
+
+   view->x =
+     (gint32) gtk_layout_get_hadjustment(GTK_LAYOUT(viewport))->value;
+   view->y =
+     (gint32) gtk_layout_get_vadjustment(GTK_LAYOUT(viewport))->value;
+   view->width =
+     (gint32) GTK_WIDGET(viewport)->allocation.width;
+   view->height =
+     (gint32) GTK_WIDGET(viewport)->allocation.height;
+
+   return view;
+}
+
 /*
  * Scrolls the viewport to position y.
  * This is done in an idle function.
diff -pruN dillo/src/dw_gtk_viewport.h dillo_tabs/src/dw_gtk_viewport.h
--- dillo/src/dw_gtk_viewport.h	2003-11-24 14:32:40.092777336 +0100
+++ dillo_tabs/src/dw_gtk_viewport.h	2003-11-24 14:32:40.200760920 +0100
@@ -5,6 +5,7 @@
 #include "dw_widget.h"
 #include "findtext.h"
 #include "selection.h"
+#include "focus.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -39,12 +40,17 @@ struct _GtkDwViewport
    /* updated by Dw_gtk_viewport_motion_notify */
    gdouble mouse_x, mouse_y;
 
+   /* viewport's 'view' on the world,
+    * updated by Dw_gtk_viewport_get_view */
+   DwRectangle view;
+
    gchar *anchor;
    gint32 anchor_y;
    gint anchor_idle_id;
 
    FindtextState *findtext_state;
    Selection *selection;
+   FocusState *focus_state;
 
    /* Anchors of the widget tree.
     * Key: gchar*, has to be stored elsewhere
@@ -71,6 +77,20 @@ void           a_Dw_gtk_viewport_set_scr
                                                          *viewport,
                                                          gint pos);
 
+void           a_Dw_gtk_viewport_focusable_add      (DwWidget *widget,
+						     gpointer link,
+						     DwIterator *it,
+						     DwFocusableType type);
+void           a_Dw_gtk_viewport_focusable_allocate (DwWidget *widget,
+						     gpointer link,
+						     gint32 x,
+						     gint32 y);
+DwLocation *   a_Dw_gtk_viewport_get_cursor      (GtkDwViewport *viewport);
+void           a_Dw_gtk_viewport_set_cursor      (GtkDwViewport *viewport,
+						  DwLocation *cursor);
+
+DwRectangle *  a_Dw_gtk_viewport_get_view        (GtkDwViewport *viewport);
+
 DwWidget*      a_Dw_gtk_viewport_widget_at_viewport_point (GtkDwViewport
                                                            *viewport,
                                                            gint32 vx,
diff -pruN dillo/src/dw_hruler.c dillo_tabs/src/dw_hruler.c
--- dillo/src/dw_hruler.c	2003-11-24 14:32:40.093777184 +0100
+++ dillo_tabs/src/dw_hruler.c	2003-11-24 14:32:40.201760768 +0100
@@ -131,7 +131,7 @@ static void Dw_hruler_iterator_highlight
                                           gint end,
                                           DwHighlightLayer layer)
 {
-   if (it->content.type == DW_CONTENT_TEXT) {
+   if (it->content.type & DW_CONTENT_TEXT) {
       DW_HRULER(it->widget)->selected[layer] = (start == 0 && end >= 1);
       p_Dw_widget_queue_draw (it->widget);
    }
diff -pruN dillo/src/dw_image.c dillo_tabs/src/dw_image.c
--- dillo/src/dw_image.c	2003-11-24 14:32:40.094777032 +0100
+++ dillo_tabs/src/dw_image.c	2003-11-24 14:32:40.202760616 +0100
@@ -79,20 +79,8 @@ static gint Dw_image_map_list_find_link 
                  p_Dw_style_box_diff_height ( ((DwWidget*)(image))->style ) ) \
              / ((DwImage*)(image))->height )
 
-
-enum
-{
-   LINK_ENTERED,
-   LINK_PRESSED,
-   LINK_RELEASED,
-   LINK_CLICKED,
-   LAST_SIGNAL
-};
-
-static guint image_signals[LAST_SIGNAL] = { 0 };
 static DwWidgetClass *parent_class;
 
-
 /*
  * Standard Gtk+ function.
  */
@@ -172,43 +160,6 @@ static void Dw_image_class_init (DwImage
    object_class = (GtkObjectClass*)klass;
    object_class->destroy = Dw_image_destroy;
 
-   image_signals[LINK_ENTERED] =
-      gtk_signal_new ("link_entered",
-                      GTK_RUN_LAST,
-                      object_class->type,
-                      GTK_SIGNAL_OFFSET (DwImageClass, link_entered),
-                      p_Dw_marshal_link_enter,
-                      GTK_TYPE_BOOL,
-                      3, GTK_TYPE_INT, GTK_TYPE_INT, GTK_TYPE_INT);
-   image_signals[LINK_PRESSED] =
-      gtk_signal_new ("link_pressed",
-                      GTK_RUN_LAST,
-                      object_class->type,
-                      GTK_SIGNAL_OFFSET (DwImageClass, link_pressed),
-                      p_Dw_marshal_link_button,
-                      GTK_TYPE_BOOL,
-                      4, GTK_TYPE_INT, GTK_TYPE_INT, GTK_TYPE_INT,
-                      GTK_TYPE_GDK_EVENT);
-   image_signals[LINK_RELEASED] =
-      gtk_signal_new ("link_released",
-                      GTK_RUN_LAST,
-                      object_class->type,
-                      GTK_SIGNAL_OFFSET (DwImageClass, link_released),
-                      p_Dw_marshal_link_button,
-                      GTK_TYPE_BOOL,
-                      4, GTK_TYPE_INT, GTK_TYPE_INT, GTK_TYPE_INT,
-                      GTK_TYPE_GDK_EVENT);
-   image_signals[LINK_CLICKED] =
-      gtk_signal_new ("link_clicked",
-                      GTK_RUN_LAST,
-                      object_class->type,
-                      GTK_SIGNAL_OFFSET (DwImageClass, link_clicked),
-                      p_Dw_marshal_link_button,
-                      GTK_TYPE_BOOL,
-                      4, GTK_TYPE_INT, GTK_TYPE_INT, GTK_TYPE_INT,
-                      GTK_TYPE_GDK_EVENT);
-   gtk_object_class_add_signals (object_class, image_signals, LAST_SIGNAL);
-
    widget_class = (DwWidgetClass*)klass;
    widget_class->size_request = Dw_image_size_request;
    widget_class->size_allocate = Dw_image_size_allocate;
@@ -283,6 +234,12 @@ static void Dw_image_size_allocate (DwWi
 {
    DwImage *image;
 
+   /* if image has a link, allocate focusable */
+   if(DW_WIDGET_HAS_LINK(widget))
+     a_Dw_gtk_viewport_focusable_allocate(widget,
+					  (gpointer) DW_WIDGET_LINK(widget),
+					  allocation->x, allocation->y);
+
    /* if image is moved only */
    if (allocation->width == widget->allocation.width &&
        allocation->ascent + allocation->descent == DW_WIDGET_HEIGHT(widget))
@@ -402,8 +359,8 @@ static gboolean Dw_image_leave_notify (D
 
    if (image->hover_link != -1) {
       image->hover_link = -1;
-      gtk_signal_emit (GTK_OBJECT (widget), image_signals[LINK_ENTERED],
-                       -1, -1, -1, &return_val);
+      gtk_signal_emit_by_name (GTK_OBJECT (widget), "link_entered",
+			       -1, -1, -1, &return_val);
       return return_val;
    }
 
@@ -426,9 +383,9 @@ static gboolean Dw_image_button_press (D
    Dw_image_find_link (image, x, y, &image->pressed_link, &link_x, &link_y);
 
    if (image->pressed_link >= 0)
-      gtk_signal_emit (GTK_OBJECT (widget), image_signals[LINK_PRESSED],
-                       image->pressed_link, link_x, link_y, event,
-                       &return_val);
+      gtk_signal_emit_by_name (GTK_OBJECT (widget), "link_pressed",
+			       image->pressed_link, link_x, link_y, event,
+			       &return_val);
 
    return return_val;
 }
@@ -451,11 +408,15 @@ static gboolean Dw_image_button_release 
    image->pressed_link = -1;
 
    if (link_released >= 0) {
-      gtk_signal_emit (GTK_OBJECT (widget), image_signals[LINK_RELEASED],
-                       link_released, link_x, link_y, event, &return_val1);
+      gtk_signal_emit_by_name (GTK_OBJECT (widget),
+			       "link_released",
+			       link_released, link_x, link_y, event,
+			       &return_val1);
       if (link_pressed == link_released) {
-         gtk_signal_emit (GTK_OBJECT (widget), image_signals[LINK_CLICKED],
-                          link_released, link_x, link_y, event, &return_val2);
+         gtk_signal_emit_by_name (GTK_OBJECT (widget),
+				  "link_clicked",
+				  link_released, link_x, link_y, event,
+				  &return_val2);
          return return_val1 || return_val2;
       }
       
@@ -486,8 +447,8 @@ static gboolean Dw_image_motion_notify (
 
    /* In case of a ISMAP image, the signal has to be emitted always */
    if (link_x != -1 || link != link_old) {
-      gtk_signal_emit (GTK_OBJECT (widget), image_signals[LINK_ENTERED],
-                       link, link_x, link_y, &return_val);
+      gtk_signal_emit_by_name (GTK_OBJECT (widget), "link_entered",
+			       link, link_x, link_y, &return_val);
       return return_val;
    } else
       return (link != -1);
@@ -868,6 +829,10 @@ static gint Dw_image_map_list_find_link 
    return -1;
 }
 
+/*
+ * The iterator returns a single space for images without alt-text to
+ * make it possible to highlight those images.
+ */
 static DwIterator *Dw_image_iterator (DwWidget *widget,
                                        gint32 mask,
                                        gboolean at_end)
@@ -875,13 +840,12 @@ static DwIterator *Dw_image_iterator (Dw
    DwIterator *it;
    DwImage *image = DW_IMAGE (widget);
 
-   if (image->alt_text) {
-      it = p_Dw_widget_text_iterator (widget, mask, at_end, image->alt_text);
-      if (it)
-         it->highlight = Dw_image_iterator_highlight;
-      return it;
-   } else
-      return NULL;
+   it = p_Dw_widget_text_iterator (widget, mask, at_end,
+				   (image->alt_text ? image->alt_text : " "));
+   if(it)
+      it->highlight = Dw_image_iterator_highlight;
+   
+   return it;
 }
 
 static void Dw_image_iterator_highlight (DwIterator *it,
@@ -889,7 +853,7 @@ static void Dw_image_iterator_highlight 
                                           gint end,
                                           DwHighlightLayer layer)
 {
-   if (it->content.type == DW_CONTENT_TEXT) {
+   if (it->content.type & DW_CONTENT_TEXT) {
       DW_IMAGE(it->widget)->selected[layer] = (start == 0 && end >= 1);
       p_Dw_widget_queue_draw (it->widget);
    }
diff -pruN dillo/src/dw_image.h dillo_tabs/src/dw_image.h
--- dillo/src/dw_image.h	2003-11-24 14:32:40.094777032 +0100
+++ dillo_tabs/src/dw_image.h	2003-11-24 14:32:40.202760616 +0100
@@ -61,18 +61,6 @@ struct _DwImage
 struct _DwImageClass
 {
    DwWidgetClass parent_class;
-
-   gboolean (*link_entered)  (DwImage *page,
-                              gint link, gint x, gint y);
-   gboolean (*link_pressed)  (DwImage *page,
-                              gint link, gint x, gint y,
-                              GdkEventButton *event);
-   gboolean (*link_released) (DwImage *page,
-                              gint link, gint x, gint y,
-                              GdkEventButton *event);
-   gboolean (*link_clicked)  (DwImage *page,
-                              gint link, gint x, gint y,
-                              GdkEventButton *event);
 };
 
 
diff -pruN dillo/src/dw_page.c dillo_tabs/src/dw_page.c
--- dillo/src/dw_page.c	2003-11-24 14:32:40.097776576 +0100
+++ dillo_tabs/src/dw_page.c	2003-11-24 14:32:40.206760008 +0100
@@ -27,6 +27,7 @@
 #include "dw_page.h"
 #include "dw_marshal.h"
 #include "dw_gtk_viewport.h"
+#include "dw_embed_gtk.h"     /* for DW_IS_EMBED_GTK() */
 
 #include "prefs.h"
 
@@ -100,6 +101,7 @@ static void        Dw_page_iterator_high
                                                DwHighlightLayer layer);
 static void        Dw_page_iterator_scroll_to (DwIterator *it,
                                                DwPosition pos);
+static gint        Dw_page_iterator_get_link  (DwIterator *it);
 
 static void Dw_page_rewrap            (DwPage *page);
 
@@ -142,18 +144,7 @@ static void Dw_page_rewrap            (D
 #define Dw_page_word_wrap(page, word_ind) \
    (((DwPageClass*)(((GtkObject*)(page))->klass))->word_wrap (page, word_ind))
 
-enum
-{
-   LINK_ENTERED,
-   LINK_PRESSED,
-   LINK_RELEASED,
-   LINK_CLICKED,
-   LAST_SIGNAL
-};
-
 static DwContainerClass *parent_class;
-static guint page_signals[LAST_SIGNAL] = { 0 };
-
 
 /*
  * Return the type of DwPage
@@ -257,43 +248,6 @@ static void Dw_page_class_init (DwPageCl
    container_class = (DwContainerClass*) klass;
    parent_class = gtk_type_class (DW_TYPE_CONTAINER);
 
-   page_signals[LINK_ENTERED] =
-      gtk_signal_new ("link_entered",
-                      GTK_RUN_LAST,
-                      object_class->type,
-                      GTK_SIGNAL_OFFSET (DwPageClass, link_entered),
-                      p_Dw_marshal_link_enter,
-                      GTK_TYPE_BOOL,
-                      3, GTK_TYPE_INT, GTK_TYPE_INT, GTK_TYPE_INT);
-   page_signals[LINK_PRESSED] =
-      gtk_signal_new ("link_pressed",
-                      GTK_RUN_LAST,
-                      object_class->type,
-                      GTK_SIGNAL_OFFSET (DwPageClass, link_pressed),
-                      p_Dw_marshal_link_button,
-                      GTK_TYPE_BOOL,
-                      4, GTK_TYPE_INT, GTK_TYPE_INT, GTK_TYPE_INT,
-                      GTK_TYPE_GDK_EVENT);
-   page_signals[LINK_RELEASED] =
-      gtk_signal_new ("link_released",
-                      GTK_RUN_LAST,
-                      object_class->type,
-                      GTK_SIGNAL_OFFSET (DwPageClass, link_released),
-                      p_Dw_marshal_link_button,
-                      GTK_TYPE_BOOL,
-                      4, GTK_TYPE_INT, GTK_TYPE_INT, GTK_TYPE_INT,
-                      GTK_TYPE_GDK_EVENT);
-   page_signals[LINK_CLICKED] =
-      gtk_signal_new ("link_clicked",
-                      GTK_RUN_LAST,
-                      object_class->type,
-                      GTK_SIGNAL_OFFSET (DwPageClass, link_clicked),
-                      p_Dw_marshal_link_button,
-                      GTK_TYPE_BOOL,
-                      4, GTK_TYPE_INT, GTK_TYPE_INT, GTK_TYPE_INT,
-                      GTK_TYPE_GDK_EVENT);
-   gtk_object_class_add_signals (object_class, page_signals, LAST_SIGNAL);
-
    object_class->destroy = Dw_page_destroy;
 
    widget_class->size_request = Dw_page_size_request;
@@ -315,10 +269,6 @@ static void Dw_page_class_init (DwPageCl
    container_class->remove = Dw_page_remove;
    container_class->forall = Dw_page_forall;
 
-   klass->link_entered = NULL;
-   klass->link_pressed = NULL;
-   klass->link_released = NULL;
-   klass->link_clicked = NULL;
    klass->word_wrap = Dw_page_real_word_wrap;
 }
 
@@ -336,11 +286,11 @@ static void Dw_page_destroy (GtkObject *
 
    for (i = 0; i < page->num_words; i++) {
       word = &page->words[i];
-      if (word->content.type == DW_CONTENT_WIDGET)
+      if (word->content.type & DW_CONTENT_WIDGET)
          gtk_object_unref (GTK_OBJECT(word->content.data.widget));
-      else if (word->content.type == DW_CONTENT_TEXT)
+      else if (word->content.type & DW_CONTENT_TEXT)
          g_free (word->content.data.text);
-      else if (word->content.type == DW_CONTENT_ANCHOR)
+      else if (word->content.type & DW_CONTENT_ANCHOR)
          g_free (word->content.data.anchor);
 
       a_Dw_style_unref (word->style);
@@ -405,7 +355,7 @@ static void Dw_page_size_request (DwWidg
 static void Dw_page_get_word_extremes (DwPageWord *word,
                                        DwExtremes *extremes)
 {
-   if (word->content.type == DW_CONTENT_WIDGET) {
+   if (word->content.type & DW_CONTENT_WIDGET) {
       if (DW_WIDGET_USES_HINTS (word->content.data.widget))
          a_Dw_widget_get_extremes (word->content.data.widget, extremes);
       else {
@@ -526,7 +476,7 @@ static void Dw_page_get_extremes (DwWidg
 
          if ( ( line->last_word > line->first_word &&
                 page->words[line->last_word - 1].content.type
-                == DW_CONTENT_BREAK ) ||
+                & DW_CONTENT_BREAK ) ||
               line_index == page->num_lines - 1 ) {
             word = &page->words[line->last_word - 1];
             par_max -= word->orig_space;
@@ -597,7 +547,8 @@ static void Dw_page_size_allocate (DwWid
            word_index++) {
          word = &(page->words[word_index]);
 
-         switch (word->content.type) {
+      /* XOR out DW_CONTENT_FOCUSABLE from word->content.type */
+         switch (word->content.type & (DW_CONTENT_ALL ^ DW_CONTENT_FOCUSABLE)) {
          case DW_CONTENT_WIDGET:
             /* todo: justification within the line is done here! */
             child_allocation.x = x_cursor + allocation->x;
@@ -626,6 +577,24 @@ static void Dw_page_size_allocate (DwWid
             /* make compiler happy */
             break;
          }
+	 
+	 /* This hairy clause is there to make sure only the first
+	  * word in a multi-word link is allocated 
+	  * Note that the individual word size is ignored for link allocation, this
+	  * to avoid issues with images with different attributes on the same line
+	  * To take word ascent into account use (line->ascent - word->size.ascent)
+	  * instead of line->ascent
+	  */
+	 if(word->style->x_link != -1 &&
+	    !(word_index &&
+	      ((&page->words[word_index - 1])->style->x_link ==
+	       word->style->x_link)))
+	   a_Dw_gtk_viewport_focusable_allocate(DW_WIDGET(page),
+	       (gpointer) word->style->x_link,
+		x_cursor + allocation->x,
+		allocation->y +
+		Dw_page_line_total_y_offset_allocation (page, line, allocation) +
+		line->ascent);
 
          x_cursor += (word->size.width + word->eff_space);
       }
@@ -765,7 +734,7 @@ static void Dw_page_forall (DwContainer 
    for (word_index = 0; word_index < page->num_words; word_index++) {
       word = &page->words[word_index];
 
-      if (word->content.type == DW_CONTENT_WIDGET)
+      if (word->content.type & DW_CONTENT_WIDGET)
          (*callback) (word->content.data.widget, callback_data);
    }
 }
@@ -935,7 +904,7 @@ static void Dw_page_real_word_wrap (DwPa
 
       if (page->num_words > 0) {
          prev_word = &page->words[word_ind - 1];
-         if (prev_word->content.type == DW_CONTENT_BREAK) {
+         if (prev_word->content.type & DW_CONTENT_BREAK) {
             DBG_MSG (page, "wrap", 0, "after a break");
             /* previous word is a break */
             new_line = TRUE;
@@ -989,7 +958,7 @@ static void Dw_page_real_word_wrap (DwPa
    last_line->ascent = MAX (last_line->ascent, word->size.ascent);
    last_line->descent = MAX (last_line->descent, word->size.descent);
 
-   if (word->content.type == DW_CONTENT_WIDGET) {
+   if (word->content.type & DW_CONTENT_WIDGET) {
       last_line->margin_descent =
          MAX (last_line->margin_descent, 
               word->size.descent +
@@ -1014,7 +983,7 @@ static void Dw_page_real_word_wrap (DwPa
    Dw_page_get_word_extremes (word, &word_extremes);
    last_space = (word_ind > 0) ? page->words[word_ind - 1].orig_space : 0;
 
-   if (word->content.type == DW_CONTENT_BREAK)
+   if (word->content.type & DW_CONTENT_BREAK)
       last_line->break_space =
          MAX3 (word->content.data.break_space,
                last_line->margin_descent - last_line->descent,
@@ -1203,12 +1172,12 @@ static void Dw_page_rewrap (DwPage *page
    for (; word_index < page->num_words; word_index++) {
       word = &page->words[word_index];
 
-      if (word->content.type == DW_CONTENT_WIDGET)
+      if (word->content.type & DW_CONTENT_WIDGET)
          Dw_page_calc_widget_size (page, word->content.data.widget,
                                    &word->size);
       Dw_page_word_wrap (page, word_index);
 
-      if (word->content.type == DW_CONTENT_WIDGET)
+      if (word->content.type & DW_CONTENT_WIDGET)
          word->content.data.widget->parent_ref = page->num_lines - 1;
 
       DEBUG_MSG(DEBUG_REWRAP_LEVEL,
@@ -1216,7 +1185,7 @@ static void Dw_page_rewrap (DwPage *page
                 "in page with %d word(s)\n",
                 page->num_lines - 1, word_index, page->num_words);
 
-      if ( word->content.type == DW_CONTENT_ANCHOR )
+      if ( word->content.type & DW_CONTENT_ANCHOR )
          p_Dw_gtk_viewport_put_anchor
             (widget, word->content.data.anchor,
              Dw_page_line_total_y_offset (page,
@@ -1249,6 +1218,8 @@ static void Dw_page_draw_line (DwPage *p
    GdkWindow *window;
    GdkGC *gc, *hl_gc;
    DwStyleColor *page_bg_color, *word_bg_color;
+   gboolean space_drawn;
+   gchar dash_list[] = {2,2};
 
    /* Here's an idea on how to optimize this routine to minimize the number
     * of calls to gdk_draw_string:
@@ -1278,7 +1249,8 @@ static void Dw_page_draw_line (DwPage *p
       diff = 0;
       gc = word->style->color->gc;
 
-      switch (word->content.type) {
+      /* XOR out DW_CONTENT_FOCUSABLE from word->content.type */
+      switch ((word->content.type & (DW_CONTENT_ALL ^ DW_CONTENT_FOCUSABLE))) {
       case DW_CONTENT_TEXT:
          if (word->style->background_color)
             word_bg_color = word->style->background_color;
@@ -1372,43 +1344,172 @@ static void Dw_page_draw_line (DwPage *p
                 * words, but this is not yet defined very well. */
                if (word->hl_end[layer] > eff_hl_end &&
                    word_index < page->num_words &&
-                   word_index != line->last_word - 1)
-                  width_hl += word->eff_space;
+                   word_index != line->last_word - 1) {
+		  width_hl += word->eff_space;
+		  space_drawn = TRUE;
+	       } else
+		  space_drawn = FALSE;
+
+	       switch (layer) {
+	       case DW_HIGHLIGHT_SELECTION:
+	       case DW_HIGHLIGHT_FINDTEXT:
+                  if (width_hl != 0) {
+		    /* Draw background for highlighted text. */
+		    gdk_draw_rectangle (window, word_bg_color->inverse_gc,
+					TRUE, start_hl,
+					y_viewport_base - word->size.ascent,
+					width_hl,
+					word->size.ascent + word->size.descent);
+		    
+		    /* Highlight the text. */
+		    hl_gc = word->style->color->inverse_gc;
+		    gdk_draw_text (window, word->style->font->font, hl_gc,
+				   start_hl, y_viewport_base + diff,
+				   word->content.data.text
+				   + word->hl_start[layer],
+				   eff_hl_end - word->hl_start[layer]);
+		    
+		    /* underline and strike-through */
+		    if (word->style->text_decoration
+			& DW_STYLE_TEXT_DECORATION_UNDERLINE)
+		      gdk_draw_line (window, hl_gc,
+				     start_hl, y_viewport_base + 1 + diff,
+				     start_hl + width_hl - 1,
+				     y_viewport_base + 1 + diff);
+		    if (word->style->text_decoration
+			& DW_STYLE_TEXT_DECORATION_LINE_THROUGH)
+		      gdk_draw_line (window, hl_gc,
+				     start_hl,
+				     y_viewport_base
+				     - word->size.ascent / 2 + diff,
+				     start_hl + width_hl - 1,
+				     y_viewport_base
+				     - word->size.ascent / 2 + diff);
+		  }
+		  break;
+
+	       case DW_HIGHLIGHT_FOCUS:
+	          hl_gc = word->style->color->inverse_gc;
+		  /* set dashed line attribute for box around link
+		   * TODO: see below (reset line attributes) */
+		  gdk_gc_set_dashes(hl_gc, 2, dash_list, 2);
+		  gdk_gc_set_line_attributes(hl_gc, 1,
+					     GDK_LINE_ON_OFF_DASH,
+					     GDK_CAP_BUTT,
+					     GDK_JOIN_MITER);
 
 
-               if (width_hl != 0) {
-                  /* Draw background for highlighted text. */
-                  gdk_draw_rectangle (window, word_bg_color->inverse_gc,
-                                      TRUE, start_hl,
-                                      y_viewport_base - word->size.ascent,
-                                      width_hl,
-                                      word->size.ascent + word->size.descent);
 
-                  /* Highlight the text. */
-                  hl_gc = word->style->color->inverse_gc;
+		  /* Draw background for highlighted text. */
+                  gdk_draw_rectangle (window, word_bg_color->inverse_gc,
+				     TRUE, start_hl,
+				     y_viewport_base - word->size.ascent,
+				     width_hl,
+				     word->size.ascent
+				     + word->size.descent);
+		  /* Highlight the text. */
                   gdk_draw_text (window, word->style->font->font, hl_gc,
-                                 start_hl, y_viewport_base + diff,
-                                 word->content.data.text
-                                 + word->hl_start[layer],
-                                 eff_hl_end - word->hl_start[layer]);
-
-                  /* underline and strike-through */
-                  if (word->style->text_decoration
-                      & DW_STYLE_TEXT_DECORATION_UNDERLINE)
-                     gdk_draw_line (window, hl_gc,
-                                    start_hl, y_viewport_base + 1 + diff,
-                                    start_hl + width_hl - 1,
-                                    y_viewport_base + 1 + diff);
-                  if (word->style->text_decoration
-                      & DW_STYLE_TEXT_DECORATION_LINE_THROUGH)
+				start_hl, y_viewport_base + diff,
+				word->content.data.text
+				+ word->hl_start[layer],
+				eff_hl_end - word->hl_start[layer]);
+
+                  /* top line */
+                  gdk_draw_line (window, hl_gc,
+                                 start_hl, y_viewport_base - word->size.ascent,
+                                 start_hl + width_hl,
+                                 y_viewport_base - word->size.ascent);
+
+                  /* bottom line */
+                  gdk_draw_line (window, hl_gc,
+                                 start_hl, y_viewport_base + word->size.descent - 1,
+                                 start_hl + width_hl,
+                                 y_viewport_base + word->size.descent - 1);
+
+                  /* left line */
+                  if (/* First character focused? */
+                      word->hl_start[layer] == 0 &&
+                      word_index > line->first_word &&
+                      /* Previous character also focused? */
+                      /* todo: currently, only text words are considered. */
+                      page->words[word_index - 1].content.type
+                      & DW_CONTENT_TEXT &&
+                      page->words[word_index - 1].hl_start[layer] != -1 &&
+                      (guint) page->words[word_index - 1].hl_end[layer]
+                      > strlen (page->words[word_index
+                                            - 1].content.data.text)) {
+                     /* Left border is not fully drawn, only partially, and
+                      * only when previous word height is "smaller". todo:
+                      * Spaces are drawn wrong, when the space has another
+                      * font etc. than the word. */
+                     if (word->size.ascent >
+                         page->words[word_index - 1].size.ascent)
+                        gdk_draw_line (window, hl_gc,
+                                       start_hl,
+                                       y_viewport_base - word->size.ascent,
+                                       start_hl,
+                                       y_viewport_base - page->words[word_index +
+                                                              1].size.ascent);
+                     if (word->size.descent >
+                         page->words[word_index - 1].size.descent)
+                        gdk_draw_line (window, hl_gc,
+                                       start_hl,
+                                       y_viewport_base + word->size.descent - 1,
+                                       start_hl,
+                                       y_viewport_base
+                                       + page->words[word_index +
+                                                     1].size.descent - 1);
+                  } else
+                     /* Left border is fully drawn. */
                      gdk_draw_line (window, hl_gc,
                                     start_hl,
-                                    y_viewport_base
-                                    - word->size.ascent / 2 + diff,
-                                    start_hl + width_hl - 1,
-                                    y_viewport_base
-                                    - word->size.ascent / 2 + diff);
-               }
+                                    y_viewport_base - word->size.ascent,
+                                    start_hl,
+                                    y_viewport_base + word->size.descent - 1);
+
+                  /* right line */
+                  if (space_drawn &&
+                      /* Next character also focussed? */
+                      page->words[word_index + 1].hl_start[layer] == 0) {
+                     /* Right border is not fully drawn, only partially, and
+                      * only when next word height is "smaller". Note: Since 
+                      * space_drawn is true, we can assume that there is
+                      * a next word. todo: See above. */
+                     if (word->size.ascent >
+                         page->words[word_index + 1].size.ascent)
+                        gdk_draw_line (window, hl_gc,
+                                       start_hl + width_hl,
+                                       y_viewport_base - word->size.ascent,
+                                       start_hl + width_hl,
+                                       y_viewport_base - page->words[word_index +
+                                                              1].size.ascent);
+                     if (word->size.descent >
+                         page->words[word_index + 1].size.descent)
+                        gdk_draw_line (window, hl_gc,
+                                       start_hl + width_hl,
+                                       y_viewport_base + word->size.descent - 1,
+                                       start_hl + width_hl,
+                                       y_viewport_base
+                                       + page->words[word_index +
+                                                     1].size.descent - 1);
+                  } else
+                     /* Right border is fully drawn. */
+                     gdk_draw_line (window, hl_gc,
+                                    start_hl + width_hl,
+                                    y_viewport_base - word->size.ascent,
+                                    start_hl + width_hl,
+                                    y_viewport_base + word->size.descent - 1);
+		  /* reset line attributes
+		   * TODO: this assumes a fixed set of attributes, it does not
+		   * explicitly restore the previous set for lack of means of
+		   * retreiving those (no gdk_gc_get_line_attributes()) */
+		  gdk_gc_set_line_attributes(hl_gc, 1,
+					     GDK_LINE_SOLID,
+					     GDK_CAP_BUTT,
+					     GDK_JOIN_MITER);
+
+                  break;
+	       }
             }
          }
          break;
@@ -1574,11 +1675,13 @@ static gint Dw_page_find_link (DwPage *p
 /*
  * Construct an iterator for a word.
  */
-static DwIterator *Dw_page_construct_iterator (DwPage *page, gint word_index)
+static DwIterator *Dw_page_construct_iterator (DwPage *page,
+					       gint mask,
+					       gint word_index)
 {
    DwIterator *it;
 
-   it = Dw_page_iterator (DW_WIDGET (page), DW_CONTENT_ALL, FALSE);
+   it = Dw_page_iterator (DW_WIDGET (page), mask , FALSE);
    //DW_CONTENT_TEXT | DW_CONTENT_WIDGET, FALSE);
    ((DwIteratorInt*)it)->pos = word_index;
    it->content = page->words[word_index].content;
@@ -1635,7 +1738,7 @@ static gboolean Dw_page_send_selection_e
       within_content = FALSE;
       word_index = page->num_words - 1;
       word = &page->words[word_index];
-      char_pos = word->content.type == DW_CONTENT_TEXT ?
+      char_pos = word->content.type & DW_CONTENT_TEXT ?
          strlen (word->content.data.text) : 0;
    } else {
       line_index = Dw_page_find_line_index (page, y);
@@ -1676,7 +1779,7 @@ static gboolean Dw_page_send_selection_e
                           word_index, a_Dw_content_text (&word->content));
 
                /* We have found the word. */
-               if (word->content.type == DW_CONTENT_TEXT) {
+               if (word->content.type & DW_CONTENT_TEXT) {
                   /* Search the character the mouse pointer is in.
                    * next_word_x is the right side of this character. */
                   char_pos = 0;
@@ -1721,14 +1824,14 @@ static gboolean Dw_page_send_selection_e
             if (word_index >= page->num_words)
                word_index--;
             word = &page->words[word_index];
-            char_pos = word->content.type == DW_CONTENT_TEXT ?
+            char_pos = word->content.type & DW_CONTENT_TEXT ?
                strlen (word->content.data.text) : SELECTION_EOW;
          }
       }
    }
 
    word = &page->words[word_index];
-   it = Dw_page_construct_iterator (page, word_index);
+   it = Dw_page_construct_iterator (page, DW_CONTENT_ALL, word_index);
    DEBUG_MSG (DEBUG_EVENT_LEVEL, "-> word %d (of %d), char %d, link %d: %s\n",
               word_index, page->num_words, char_pos, link,
               a_Dw_iterator_text (it));
@@ -1788,8 +1891,8 @@ static gboolean Dw_page_motion_notify (D
       page->hover_link = Dw_page_find_link (page, x, y);
 
       if (page->hover_link != link_old) {
-         gtk_signal_emit (GTK_OBJECT (widget), page_signals[LINK_ENTERED],
-                          page->hover_link, -1, -1, &return_val);
+         gtk_signal_emit_by_name (GTK_OBJECT (widget), "link_entered",
+				  page->hover_link, -1, -1, &return_val);
          return return_val;
       } else
          return (page->hover_link != -1);
@@ -1809,8 +1912,8 @@ static gboolean Dw_page_leave_notify (Dw
 
    if (page->hover_link != -1) {
       page->hover_link = -1;
-      gtk_signal_emit (GTK_OBJECT (widget), page_signals[LINK_ENTERED],
-                       -1, -1, -1, &return_val);
+      gtk_signal_emit_by_name (GTK_OBJECT (widget), "link_entered",
+			       -1, -1, -1, &return_val);
       return return_val;
    }
 
@@ -1872,10 +1975,11 @@ static void Dw_page_calc_text_size  (DwP
       size->ascent += (size->ascent / 3);
 }
 
-
 /*
  * Add a word to the page structure. Stashes the argument pointer in
  * the page data structure so that it will be deallocated on destroy.
+ * If the word has a link (style->x_link != -1) add focusable to
+ * the viewport and adjust content type.
  */
 void a_Dw_page_add_text (DwPage *page, char *text, DwStyle *style)
 {
@@ -1888,10 +1992,23 @@ void a_Dw_page_add_text (DwPage *page, c
    word->content.type = DW_CONTENT_TEXT;
    word->content.data.text = text;
    Dw_page_word_wrap (page, page->num_words - 1);
+
+   /* if this is a link: add focusable */
+   if(style->x_link != -1) {
+     word->content.type |= DW_CONTENT_FOCUSABLE;
+     a_Dw_gtk_viewport_focusable_add (DW_WIDGET(page),
+		 (gpointer) style->x_link,
+		  Dw_page_construct_iterator(page,
+		  DW_CONTENT_TEXT|DW_CONTENT_FOCUSABLE,
+		  page->num_words - 1),
+		  DW_FOCUSABLE_LINK);
+   }
 }
 
 /*
  * Add a widget (word type) to the page.
+ * If the word has a link (style->x_link != -1) or if this is an embed
+ * widget, add focusable to  the viewport and adjust content type.
  */
 void a_Dw_page_add_widget (DwPage *page,
                            DwWidget *widget,
@@ -1899,6 +2016,7 @@ void a_Dw_page_add_widget (DwPage *page,
 {
    DwPageWord *word;
    DwRequisition size;
+   DwIterator *it;
 
    /* We first assign -1 as parent_ref, since the call of widget->size_request
     * will otherwise let this DwPage be rewrapped from the beginning.
@@ -1919,6 +2037,27 @@ void a_Dw_page_add_widget (DwPage *page,
    Dw_page_word_wrap (page, page->num_words - 1);
    word->content.data.widget->parent_ref = page->num_lines - 1;
 
+   /* if this is a link or an embedded widget: add focusable
+    * This is done here (instead of in the widget code) to Keep
+    * It Simple. For embedded widgets, the widget pointer is used
+    * as identifier for the focusable (second parameter) */
+   if(DW_WIDGET_HAS_LINK(widget)) {
+     word->content.type |= DW_CONTENT_FOCUSABLE;
+     it = a_Dw_widget_iterator(widget, DW_CONTENT_FOCUSABLE, FALSE);
+     if(a_Dw_iterator_next(it))
+       a_Dw_gtk_viewport_focusable_add (DW_WIDGET(page),
+					(gpointer) DW_WIDGET_LINK(widget),
+					it,
+					DW_FOCUSABLE_LINK);
+   } else if (DW_IS_EMBED_GTK(widget)) {
+     word->content.type |= DW_CONTENT_FOCUSABLE;
+     it = a_Dw_widget_iterator(widget, DW_CONTENT_FOCUSABLE, FALSE);
+     if(a_Dw_iterator_next(it))
+       a_Dw_gtk_viewport_focusable_add (DW_WIDGET(page),
+					(gpointer) widget,
+					it,
+					DW_FOCUSABLE_WIDGET);
+   }
    DEBUG_MSG(DEBUG_REWRAP_LEVEL,
              "Assigning parent_ref = %d to added word %d, "
              "in page with %d word(s)\n",
@@ -1999,17 +2138,17 @@ void a_Dw_page_add_parbreak (DwPage *pag
            widget = widget->parent) {
          page2 = DW_PAGE (widget->parent);
          if (page2->list_item)
-            isfirst = (page2->words[1].content.type == DW_CONTENT_WIDGET
+            isfirst = (page2->words[1].content.type & DW_CONTENT_WIDGET
                        && page2->words[1].content.data.widget == widget);
          else
-            isfirst = (page2->words[0].content.type == DW_CONTENT_WIDGET
+            isfirst = (page2->words[0].content.type & DW_CONTENT_WIDGET
                         && page2->words[0].content.data.widget == widget);
          if (!isfirst) {
             /* The page we searched for has been found. */
             lineno = widget->parent_ref;
             if (lineno > 0 &&
                 (word2 = &page2->words[page2->lines[lineno - 1].first_word]) &&
-                word2->content.type == DW_CONTENT_BREAK) {
+                word2->content.type & DW_CONTENT_BREAK) {
                if (word2->content.data.break_space < space) {
                   word2->content.data.break_space = space;
                   p_Dw_widget_queue_resize (DW_WIDGET (page2), lineno, FALSE);
@@ -2026,7 +2165,7 @@ void a_Dw_page_add_parbreak (DwPage *pag
 
    /* Another break before? */
    if ((word = &page->words[page->num_words - 1]) &&
-       word->content.type == DW_CONTENT_BREAK) {
+       word->content.type & DW_CONTENT_BREAK) {
       word->content.data.break_space =
          MAX (word->content.data.break_space, space);
       return;
@@ -2046,7 +2185,7 @@ void a_Dw_page_add_linebreak (DwPage *pa
    DwPageWord *word;
 
    if (page->num_words == 0 ||
-       page->words[page->num_words - 1].content.type == DW_CONTENT_BREAK)
+       page->words[page->num_words - 1].content.type & DW_CONTENT_BREAK)
        /* An <BR> in an empty line gets the height of the current font
         * (why would someone else place it here?), ...  */
       word = Dw_page_add_word (page, 0, style->font->font->ascent,
@@ -2155,7 +2294,7 @@ void a_Dw_page_change_word_style (DwPage
    for (i = MAX (from - 1, 0); i < to; i++) {
       word = &page->words[i];
 
-      if (word->content.type == DW_CONTENT_TEXT) {
+      if (word->content.type & DW_CONTENT_TEXT) {
          if (i >= from) {
             old_style = word->style;
             word->style = style;
@@ -2226,7 +2365,7 @@ static DwIterator *Dw_page_iterator (DwW
                                      gint mask,
                                      gboolean at_end)
 {
-   DwIteratorInt *it = g_new (DwIteratorInt, 1);
+   DwIteratorInt *it = g_new0 (DwIteratorInt, 1);
    it->it.widget = widget;
    it->it.mask = mask;
    it->it.next = Dw_page_iterator_next;
@@ -2236,6 +2375,8 @@ static DwIterator *Dw_page_iterator (DwW
    it->it.free = p_Dw_iterator_free_std;
    it->it.highlight = Dw_page_iterator_highlight;
    it->it.scroll_to = Dw_page_iterator_scroll_to;
+   it->it.focus = p_Dw_iterator_focus_std;
+   it->it.get_link = Dw_page_iterator_get_link;
 
    if (at_end) {
       it->it.content.type = DW_CONTENT_END;
@@ -2353,3 +2494,17 @@ void Dw_page_iterator_scroll_to (DwItera
       break;
    }
 }
+
+/*
+ * Standard Dw function.
+ */
+gint Dw_page_iterator_get_link (DwIterator *it)
+{
+   DwIteratorInt *ii = (DwIteratorInt*)it;
+   DwPage *page = DW_PAGE(it->widget);
+
+   return page->words[ii->pos].style->x_link;
+}
+     
+
+   
diff -pruN dillo/src/dw_page.h dillo_tabs/src/dw_page.h
--- dillo/src/dw_page.h	2003-11-24 14:32:40.098776424 +0100
+++ dillo_tabs/src/dw_page.h	2003-11-24 14:32:40.206760008 +0100
@@ -55,6 +55,7 @@ struct _DwPageLine
 struct _DwPageWord {
    /* todo: perhaps add a x_left? */
    DwRequisition size;
+
    /* Space after the word, only if it's not a break: */
    gint32 orig_space; /* from font, set by a_Dw_page_add_space */
    gint32 eff_space;  /* effective space, set by Dw_page_word_wrap,
@@ -132,18 +133,6 @@ struct _DwPageClass
 {
    DwContainerClass parent_class;
 
-   gboolean (*link_entered)  (DwPage *page,
-                              gint link, gint x, gint y);
-   gboolean (*link_pressed)  (DwPage *page,
-                              gint link, gint x, gint y,
-                              GdkEventButton *event);
-   gboolean (*link_released) (DwPage *page,
-                              gint link, gint x, gint y,
-                              GdkEventButton *event);
-   gboolean (*link_clicked)  (DwPage *page,
-                              gint link, gint x, gint y,
-                              GdkEventButton *event);
-
    void     (*word_wrap)     (DwPage *page,
                               gint word_ind);
 };
diff -pruN dillo/src/dw_table.c dillo_tabs/src/dw_table.c
--- dillo/src/dw_table.c	2003-11-24 14:32:40.101775968 +0100
+++ dillo_tabs/src/dw_table.c	2003-11-24 14:32:40.209759552 +0100
@@ -660,7 +660,7 @@ static DwIterator *Dw_table_construct_la
       it = a_Dw_widget_iterator (child->data.cell.widget,
                                  DW_CONTENT_ALL, TRUE);
       if (it != NULL && a_Dw_iterator_prev (it)) {
-         if (it->content.type == DW_CONTENT_TEXT)
+         if (it->content.type & DW_CONTENT_TEXT)
             *char_pos = strlen (it->content.data.text);
       } else {
          a_Dw_iterator_free (it);
@@ -1668,7 +1668,7 @@ static DwIterator *Dw_table_iterator (Dw
                                       gint mask,
                                       gboolean at_end)
 {
-   DwIteratorInt *it = g_new (DwIteratorInt, 1);
+   DwIteratorInt *it = g_new0 (DwIteratorInt, 1);
    it->it.widget = widget;
    it->it.mask = mask;
    it->it.next = Dw_table_iterator_next;
@@ -1678,6 +1678,8 @@ static DwIterator *Dw_table_iterator (Dw
    it->it.free = p_Dw_iterator_free_std;
    it->it.highlight = p_Dw_iterator_highlight_std;
    it->it.scroll_to = p_Dw_iterator_scroll_to_std;
+   it->it.focus = p_Dw_iterator_focus_std;
+   it->it.get_link = p_Dw_iterator_get_link_std;
 
    if (at_end) {
       it->it.content.type = DW_CONTENT_END;
diff -pruN dillo/src/dw_widget.c dillo_tabs/src/dw_widget.c
--- dillo/src/dw_widget.c	2003-11-24 14:32:40.104775512 +0100
+++ dillo_tabs/src/dw_widget.c	2003-11-24 14:32:40.213758944 +0100
@@ -55,6 +55,10 @@ enum
    MOTION_NOTIFY_EVENT,
    ENTER_NOTIFY_EVENT,
    LEAVE_NOTIFY_EVENT,
+   LINK_ENTERED,
+   LINK_PRESSED,
+   LINK_RELEASED,
+   LINK_CLICKED,
    LAST_SIGNAL
 };
 
@@ -184,46 +188,81 @@ static void Dw_widget_class_init (DwWidg
                       GTK_SIGNAL_OFFSET (DwWidgetClass, unrealize),
                       gtk_marshal_NONE__NONE,
                       GTK_TYPE_NONE, 0);
-  widget_signals[BUTTON_PRESS_EVENT] =
-     gtk_signal_new ("button_press_event",
-                     GTK_RUN_LAST,
-                     object_class->type,
-                     GTK_SIGNAL_OFFSET (DwWidgetClass, button_press_event),
-                     p_Dw_marshal_BOOL__INT_INT_POINTER,
-                     GTK_TYPE_BOOL,
-                     3, GTK_TYPE_INT, GTK_TYPE_INT, GTK_TYPE_GDK_EVENT);
-  widget_signals[BUTTON_RELEASE_EVENT] =
-     gtk_signal_new ("button_release_event",
-                     GTK_RUN_LAST,
-                     object_class->type,
-                     GTK_SIGNAL_OFFSET (DwWidgetClass, button_release_event),
-                     p_Dw_marshal_BOOL__INT_INT_POINTER,
-                     GTK_TYPE_BOOL,
-                     3, GTK_TYPE_INT, GTK_TYPE_INT, GTK_TYPE_GDK_EVENT);
-  widget_signals[MOTION_NOTIFY_EVENT] =
-     gtk_signal_new ("motion_notify_event",
-                     GTK_RUN_LAST,
-                     object_class->type,
-                     GTK_SIGNAL_OFFSET (DwWidgetClass, motion_notify_event),
-                     p_Dw_marshal_BOOL__INT_INT_POINTER,
-                     GTK_TYPE_BOOL,
-                     3, GTK_TYPE_INT, GTK_TYPE_INT, GTK_TYPE_GDK_EVENT);
-  widget_signals[ENTER_NOTIFY_EVENT] =
-     gtk_signal_new ("enter_notify_event",
-                     GTK_RUN_LAST,
-                     object_class->type,
-                     GTK_SIGNAL_OFFSET (DwWidgetClass, enter_notify_event),
-                     p_Dw_marshal_BOOL__POINTER_POINTER,
-                     GTK_TYPE_BOOL,
-                     2, GTK_TYPE_POINTER, GTK_TYPE_GDK_EVENT);
-  widget_signals[LEAVE_NOTIFY_EVENT] =
-     gtk_signal_new ("leave_notify_event",
-                     GTK_RUN_LAST,
-                     object_class->type,
-                     GTK_SIGNAL_OFFSET (DwWidgetClass, leave_notify_event),
-                     p_Dw_marshal_BOOL__POINTER_POINTER,
-                     GTK_TYPE_BOOL,
-                     2, GTK_TYPE_POINTER, GTK_TYPE_GDK_EVENT);
+   widget_signals[BUTTON_PRESS_EVENT] =
+      gtk_signal_new ("button_press_event",
+		      GTK_RUN_LAST,
+		      object_class->type,
+		      GTK_SIGNAL_OFFSET (DwWidgetClass, button_press_event),
+		      p_Dw_marshal_BOOL__INT_INT_POINTER,
+		      GTK_TYPE_BOOL,
+		      3, GTK_TYPE_INT, GTK_TYPE_INT, GTK_TYPE_GDK_EVENT);
+   widget_signals[BUTTON_RELEASE_EVENT] =
+      gtk_signal_new ("button_release_event",
+		      GTK_RUN_LAST,
+		      object_class->type,
+		      GTK_SIGNAL_OFFSET (DwWidgetClass, button_release_event),
+		      p_Dw_marshal_BOOL__INT_INT_POINTER,
+		      GTK_TYPE_BOOL,
+		      3, GTK_TYPE_INT, GTK_TYPE_INT, GTK_TYPE_GDK_EVENT);
+   widget_signals[MOTION_NOTIFY_EVENT] =
+      gtk_signal_new ("motion_notify_event",
+		      GTK_RUN_LAST,
+		      object_class->type,
+		      GTK_SIGNAL_OFFSET (DwWidgetClass, motion_notify_event),
+		      p_Dw_marshal_BOOL__INT_INT_POINTER,
+		      GTK_TYPE_BOOL,
+		      3, GTK_TYPE_INT, GTK_TYPE_INT, GTK_TYPE_GDK_EVENT);
+   widget_signals[ENTER_NOTIFY_EVENT] =
+      gtk_signal_new ("enter_notify_event",
+		      GTK_RUN_LAST,
+		      object_class->type,
+		      GTK_SIGNAL_OFFSET (DwWidgetClass, enter_notify_event),
+		      p_Dw_marshal_BOOL__POINTER_POINTER,
+		      GTK_TYPE_BOOL,
+		      2, GTK_TYPE_POINTER, GTK_TYPE_GDK_EVENT);
+   widget_signals[LEAVE_NOTIFY_EVENT] =
+      gtk_signal_new ("leave_notify_event",
+		      GTK_RUN_LAST,
+		      object_class->type,
+		      GTK_SIGNAL_OFFSET (DwWidgetClass, leave_notify_event),
+		      p_Dw_marshal_BOOL__POINTER_POINTER,
+		      GTK_TYPE_BOOL,
+		      2, GTK_TYPE_POINTER, GTK_TYPE_GDK_EVENT);
+   widget_signals[LINK_ENTERED] =
+      gtk_signal_new ("link_entered",
+                      GTK_RUN_LAST,
+                      object_class->type,
+                      GTK_SIGNAL_OFFSET (DwWidgetClass, link_entered),
+                      p_Dw_marshal_link_enter,
+                      GTK_TYPE_BOOL,
+                      3, GTK_TYPE_INT, GTK_TYPE_INT, GTK_TYPE_INT);
+   widget_signals[LINK_PRESSED] =
+      gtk_signal_new ("link_pressed",
+                      GTK_RUN_LAST,
+                      object_class->type,
+                      GTK_SIGNAL_OFFSET (DwWidgetClass, link_pressed),
+                      p_Dw_marshal_link_button,
+                      GTK_TYPE_BOOL,
+                      4, GTK_TYPE_INT, GTK_TYPE_INT, GTK_TYPE_INT,
+                      GTK_TYPE_GDK_EVENT);
+   widget_signals[LINK_RELEASED] =
+      gtk_signal_new ("link_released",
+                      GTK_RUN_LAST,
+                      object_class->type,
+                      GTK_SIGNAL_OFFSET (DwWidgetClass, link_released),
+                      p_Dw_marshal_link_button,
+                      GTK_TYPE_BOOL,
+                      4, GTK_TYPE_INT, GTK_TYPE_INT, GTK_TYPE_INT,
+                      GTK_TYPE_GDK_EVENT);
+   widget_signals[LINK_CLICKED] =
+      gtk_signal_new ("link_clicked",
+                      GTK_RUN_LAST,
+                      object_class->type,
+                      GTK_SIGNAL_OFFSET (DwWidgetClass, link_clicked),
+                      p_Dw_marshal_link_button,
+                      GTK_TYPE_BOOL,
+                      4, GTK_TYPE_INT, GTK_TYPE_INT, GTK_TYPE_INT,
+                      GTK_TYPE_GDK_EVENT);
    gtk_object_class_add_signals (object_class, widget_signals, LAST_SIGNAL);
 
    object_class->shutdown = Dw_widget_shutdown;
@@ -245,6 +284,11 @@ static void Dw_widget_class_init (DwWidg
    klass->motion_notify_event = NULL;
    klass->enter_notify_event = NULL;
    klass->leave_notify_event = NULL;
+   klass->link_entered = NULL;
+   klass->link_pressed = NULL;
+   klass->link_released = NULL;
+   klass->link_clicked = NULL;
+
    klass->iterator = NULL;
 }
 
@@ -849,25 +893,48 @@ void p_Dw_iterator_highlight_std (DwIter
    /* do nothing */
 }
 
+/*
+ * Focus iterator contents
+ */
+void p_Dw_iterator_focus_std (DwIterator *it)
+{
+   /* do nothing */
+}
+
+/*
+ * Get link for iterator (returns x_link from iterator widget's style)
+ */
+gint p_Dw_iterator_get_link_std (DwIterator *it)
+{
+   return it->widget->style->x_link;
+}
+
+/*
+ * For widget iterators, scroll to current widget
+ * Otherwise, scroll to it->widget
+ */
 void p_Dw_iterator_scroll_to_std (DwIterator *it,
                                   DwPosition pos)
 {
-   DwWidget *child;
+   DwWidget *widget;
 
-   /* for widgets containing nothing that widgets. */
-   if (it->content.type == DW_CONTENT_WIDGET) {
-      child = it->content.data.widget;
-      switch(pos) {
-      case DW_POSITION_TOP:
-         a_Dw_widget_scroll_to (child, pos, 0);
-         break;
-      case DW_POSITION_CENTER:
-         a_Dw_widget_scroll_to (child, pos, DW_WIDGET_HEIGHT(child) / 2);
-         break;
-      case DW_POSITION_BOTTOM:
-         a_Dw_widget_scroll_to (child, pos, DW_WIDGET_HEIGHT(child));
-         break;
-      }
+   if (it->content.type & DW_CONTENT_WIDGET)
+     /* for widgets containing nothing that widgets. */
+     widget = it->content.data.widget;
+   else
+     /* scroll to iterator widget */
+     widget = it->widget;
+
+   switch(pos) {
+   case DW_POSITION_TOP:
+     a_Dw_widget_scroll_to (widget, pos, 0);
+     break;
+   case DW_POSITION_CENTER:
+     a_Dw_widget_scroll_to (widget, pos, DW_WIDGET_HEIGHT(widget) / 2);
+     break;
+   case DW_POSITION_BOTTOM:
+     a_Dw_widget_scroll_to (widget, pos, DW_WIDGET_HEIGHT(widget));
+     break;
    }
 }
 
@@ -1061,7 +1128,7 @@ static void Dw_widget_print_tree0 (DwWid
         indent, "", gtk_type_name (GTK_OBJECT_TYPE (widget)), widget);
    it = a_Dw_widget_iterator (widget, 0xff, FALSE);
    while (a_Dw_iterator_next (it)) {
-      if (it->content.type == DW_CONTENT_WIDGET)
+      if (it->content.type & DW_CONTENT_WIDGET)
          Dw_widget_print_tree0 (it->content.data.widget, indent + 3);
       else
          MSG ("%*s%s\n", indent + 3, "", a_Dw_content_text (&it->content));
@@ -1079,6 +1146,8 @@ void a_Dw_widget_print_tree (DwWidget *w
 /*
  * The following is a standard implementation for iterators containing
  * exactly one piece of text.
+ * If the widget has a link (x_link != -1) it will return
+ * DW_CONTENT_TEXT|DW_CONTENT_FOCUSABLE as content type
  */
 
 DwIterator* p_Dw_widget_text_iterator (DwWidget *widget,
@@ -1087,9 +1156,12 @@ DwIterator* p_Dw_widget_text_iterator (D
                                        gchar *text)
 {
    DwIteratorText *it = NULL;
+   gint link = widget->style->x_link;
 
-   if (mask & DW_CONTENT_TEXT) {
-      it = g_new (DwIteratorText, 1);
+   if (mask & (link == -1 ?
+	       DW_CONTENT_TEXT :
+	       DW_CONTENT_TEXT|DW_CONTENT_FOCUSABLE)) {
+      it = g_new0 (DwIteratorText, 1);
       it->it.widget = widget;
       it->it.mask = mask;
       it->it.content.type = (at_end ? DW_CONTENT_END : DW_CONTENT_START);
@@ -1099,6 +1171,8 @@ DwIterator* p_Dw_widget_text_iterator (D
       it->it.compare = p_Dw_iterator_text_compare;
       it->it.free = p_Dw_iterator_free_std;
       it->it.highlight = p_Dw_iterator_highlight_std;
+      it->it.focus = p_Dw_iterator_focus_std;
+      it->it.get_link = p_Dw_iterator_get_link_std;
       it->it.scroll_to = p_Dw_iterator_scroll_to_std;
       it->text = text;
    } else
@@ -1109,9 +1183,12 @@ DwIterator* p_Dw_widget_text_iterator (D
 
 gboolean p_Dw_iterator_text_next (DwIterator *it)
 {
+   gint link = it->widget->style->x_link;
    if (it->content.type == DW_CONTENT_START) {
       it->content.type = DW_CONTENT_TEXT;
       it->content.data.text = ((DwIteratorText*)it)->text;
+      if(link != -1)
+	it->content.type |= DW_CONTENT_FOCUSABLE;
       return TRUE;
    } else {
       it->content.type = DW_CONTENT_END;
@@ -1121,9 +1198,12 @@ gboolean p_Dw_iterator_text_next (DwIter
 
 gboolean p_Dw_iterator_text_prev (DwIterator *it)
 {
+   gint link = it->widget->style->x_link;
    if (it->content.type == DW_CONTENT_END) {
       it->content.type = DW_CONTENT_TEXT;
       it->content.data.text = ((DwIteratorText*)it)->text;
+      if(link != -1)
+	it->content.type |= DW_CONTENT_FOCUSABLE;
       return TRUE;
    } else {
       it->content.type = DW_CONTENT_START;
@@ -1159,8 +1239,6 @@ gint p_Dw_iterator_text_compare (DwItera
    }
 }
 
-
-
 /*
  * ...
  * The function has been copied from gdktrectangle.c
diff -pruN dillo/src/dw_widget.h dillo_tabs/src/dw_widget.h
--- dillo/src/dw_widget.h	2003-11-24 14:32:40.105775360 +0100
+++ dillo_tabs/src/dw_widget.h	2003-11-24 14:32:40.214758792 +0100
@@ -43,6 +43,10 @@ extern "C" {
 #define DW_WIDGET_CONTENT_WIDTH(wid)  ((wid)->allocation.width - \
                                        p_Dw_style_box_diff_width((wid)->style))
 
+#define DW_WIDGET_LINK(wid)     (((wid)->style ? (wid)->style->x_link : -1))
+
+#define DW_WIDGET_HAS_LINK(wid) ((DW_WIDGET_LINK(wid) != -1))
+
 typedef enum {
    DW_NEEDS_RESIZE     = 1 << 0,
    DW_NEEDS_ALLOCATE   = 1 << 1,
@@ -52,6 +56,21 @@ typedef enum {
    DW_HAS_CONTENT      = 1 << 5,
 } DwWidgetFlags;
 
+typedef enum {
+   DW_SCROLL_STEP_BACKWARD,
+   DW_SCROLL_STEP_FORWARD,
+   DW_SCROLL_STEP_LEFT,
+   DW_SCROLL_STEP_RIGHT,
+   DW_SCROLL_PAGE_BACKWARD,
+   DW_SCROLL_PAGE_FORWARD,
+   DW_SCROLL_PAGE_LEFT,
+   DW_SCROLL_PAGE_RIGHT,
+   DW_SCROLL_EDGE_BACKWARD,
+   DW_SCROLL_EDGE_FORWARD,
+   DW_SCROLL_EDGE_LEFT,
+   DW_SCROLL_EDGE_RIGHT,
+} DwScrollDirection;
+
 typedef enum
 {
    DW_POSITION_TOP,
@@ -68,14 +87,23 @@ typedef enum
    DW_CONTENT_WIDGET = 1 << 3,
    DW_CONTENT_ANCHOR = 1 << 4,
    DW_CONTENT_BREAK  = 1 << 5,
+   DW_CONTENT_FOCUSABLE = 1 << 6,
    DW_CONTENT_ALL    = 0xff
 } DwContentType;
 
+/* focusable types */
+typedef enum
+{
+   DW_FOCUSABLE_LINK,
+   DW_FOCUSABLE_WIDGET
+} DwFocusableType;
+
 /* Different "layers" may be highlighted in a widget. */
 typedef enum
 {
    DW_HIGHLIGHT_SELECTION,
    DW_HIGHLIGHT_FINDTEXT,
+   DW_HIGHLIGHT_FOCUS,
    DW_HIGHLIGHT_NUM_LAYERS
 } DwHighlightLayer;
 
@@ -84,6 +112,7 @@ typedef enum
       ((GtkDwViewport*)(widget)->viewport)->back_pixmap)
 
 typedef struct _DwRectangle             DwRectangle;
+typedef struct _DwLocation              DwLocation;
 typedef struct _DwAllocation            DwAllocation;
 typedef struct _DwRequisition           DwRequisition;
 typedef struct _DwExtremes              DwExtremes;
@@ -106,6 +135,11 @@ struct _DwRectangle
    gint32 height;
 };
 
+struct _DwLocation
+{
+   gint32 x;
+   gint32 y;
+};
 
 struct _DwAllocation
 {
@@ -131,7 +165,6 @@ struct _DwExtremes
    gint32 max_width;
 };
 
-
 struct _DwContent
 {
    DwContentType type;
@@ -178,6 +211,17 @@ struct _DwIterator
                              DwHighlightLayer layer);
 
    /*
+    * Focus iterator contents. This grabs focus for embedded GTK+ widgets,
+    * currently does nothing for other widgets
+    */
+   void        (*focus)     (DwIterator *it);
+
+   /*
+    * Get x_link for iterator
+    */
+   gint        (*get_link)  (DwIterator *it);
+
+   /*
     * Scroll to an appropriate position, so that the current content is
     * visible at the top, the center, or the buttom of the screen.
     */
@@ -305,6 +349,17 @@ struct _DwWidgetClass
    gboolean (*leave_notify_event)   (DwWidget *widget,
                                      DwWidget *next_widget,
                                      GdkEventMotion *event);
+   gboolean (*link_entered)         (DwWidget *widget,
+				     gint link, gint x, gint y);
+   gboolean (*link_pressed)         (DwWidget *widget,
+				     gint link, gint x, gint y,
+				     GdkEventButton *event);
+   gboolean (*link_released)        (DwWidget *widget,
+				     gint link, gint x, gint y,
+				     GdkEventButton *event);
+   gboolean (*link_clicked)         (DwWidget *widget,
+				     gint link, gint x, gint y,
+				     GdkEventButton *event);
 
    DwIterator* (*iterator)       (DwWidget*,
                                   gint32 mask,
@@ -356,6 +411,8 @@ DwIterator* a_Dw_widget_iterator   (DwWi
                                     (it)->highlight(it, s, e, l) : (void)0)
 #define     a_Dw_iterator_scroll_to(it, p) ((it) ? (it)->scroll_to(it, p) : \
                                                       (void)0)
+#define     a_Dw_iterator_focus(it) ((it) ? (it)->focus(it) : (void)0)
+#define     a_Dw_iterator_get_link(it) ((it) ? (it)->get_link(it) : -1)
 #define     a_Dw_iterator_clone(it) ((it) ? (it)->clone(it) : NULL)
 #define     a_Dw_iterator_compare(it1, it2) ((it1)->compare(it1, it2))
 #define     a_Dw_iterator_free(it)  ((it) ? (it)->free(it) : (void)0)
@@ -370,6 +427,8 @@ void        p_Dw_iterator_highlight_std 
                                            gint from,
                                            gint start,
                                            DwHighlightLayer layer);
+void        p_Dw_iterator_focus_std       (DwIterator *it);
+gint        p_Dw_iterator_get_link_std    (DwIterator *it);
 void        p_Dw_iterator_scroll_to_std   (DwIterator *it,
                                            DwPosition pos);
 DwIterator* p_Dw_iterator_clone_std_int   (DwIterator *it);
diff -pruN dillo/src/findtext.c dillo_tabs/src/findtext.c
--- dillo/src/findtext.c	2003-11-24 14:32:40.105775360 +0100
+++ dillo_tabs/src/findtext.c	2003-11-24 14:32:40.215758640 +0100
@@ -49,6 +49,7 @@
  *       searching).
  */
 
+
 #include "msg.h"
 #include "findtext.h"
 #include "misc.h"
@@ -176,7 +177,7 @@ FindtextKey *Findtext_key_new (gchar *st
                i++;
                j++;
                key->nexttab[i] = j;
-               _MSG ("nexttab[%d] = %d\n", i, j);
+               _MSG("nexttab[%d] = %d\n", i, j);
             } else {
                j = key->nexttab[j];
             }
@@ -395,25 +396,27 @@ FindtextResult a_Findtext_search (Findte
 }
 
 /*
- * This function is called when the user closes the "find text" dialog.
+ * This function is called when the user hides the "find text" dialog.
+ * Call with reset=TRUE to free/NULL the keystr (thereby causing a next
+ * call to a_Findtext_search() to begin a the start of the document)
  */
-void a_Findtext_reset_search (FindtextState *state)
+void a_Findtext_reset_search (FindtextState *state, gboolean reset)
 {
    int i;
 
-   if (state->keystr != NULL)
-      g_free (state->keystr);
-   state->keystr = NULL;
-
    if (state->hl_iterator) {
-      for (i = 0; i < state->key->len - 1; i++) {
+      i = state->key->len;
+      do
          a_Dw_word_iterator_unhighlight (state->hl_iterator,
                                          DW_HIGHLIGHT_FINDTEXT);
-         a_Dw_word_iterator_next (state->hl_iterator);
-      }
-      a_Dw_word_iterator_unhighlight (state->hl_iterator,
-                                      DW_HIGHLIGHT_FINDTEXT);
+      while (--i && a_Dw_word_iterator_next (state->hl_iterator));
       a_Dw_word_iterator_free (state->hl_iterator);
       state->hl_iterator = NULL;
    }
+
+   if(reset) {
+     if (state->keystr != NULL)
+        g_free (state->keystr);
+     state->keystr = NULL;
+   }
 }
diff -pruN dillo/src/findtext.h dillo_tabs/src/findtext.h
--- dillo/src/findtext.h	2003-11-24 14:32:40.106775208 +0100
+++ dillo_tabs/src/findtext.h	2003-11-24 14:32:40.215758640 +0100
@@ -61,6 +61,7 @@ void           a_Findtext_state_set_widg
 FindtextResult a_Findtext_search           (FindtextState *state,
                                             gchar *str,
                                             gboolean case_sens);
-void           a_Findtext_reset_search     (FindtextState *state);
+void           a_Findtext_reset_search     (FindtextState *state,
+					    gboolean reset);
 
 #endif /* __FINDTEXT_H__ */
diff -pruN dillo/src/focus.c dillo_tabs/src/focus.c
--- dillo/src/focus.c	1970-01-01 01:00:00.000000000 +0100
+++ dillo_tabs/src/focus.c	2003-11-24 14:32:40.244754232 +0100
@@ -0,0 +1,846 @@
+/*
+ * File: focus.c
+ *
+ * Copyright 2003 Frank de Lange <frank@unternet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+/*
+ * This module handles keyboard navigation and focus
+ */
+
+#include "focus.h"
+#include "list.h"
+#include "dw_page.h"
+#include "dw_image.h"
+#include "dw_table.h"
+#include "dw_embed_gtk.h"
+#include "dw_gtk_viewport.h"
+#include <gtk/gtk.h>
+
+#define DEBUG_EVENT 10
+#define DEBUG_ALLOCATE 9
+/*#define DEBUG_LEVEL 10*/
+#include "debug.h"
+
+/* PRIVATE */
+
+/* Focus_link */
+
+/*
+ * Free memory held by FocusLink
+ * used with g_list_foreach()
+ */
+void
+Focus_link_destroy_callback(gpointer data, gpointer user_data)
+{
+   FocusLink *link = (FocusLink *) data;
+   a_Dw_iterator_free(link->it);
+   g_free(link);
+}
+
+/*
+ * Highlight FocusLink
+ *
+ * This emits a link_entered signal with the coordinates set to
+ * (-2,-2), so Html_handle_status() knows not to (re)set the
+ * X cursor for keyboard navigation.
+ */
+void
+Focus_link_highlight (FocusLink *link)
+{
+   DwIterator *hilit;
+   gint n;
+   gboolean return_val;
+
+   DEBUG_MSG(DEBUG_EVENT,
+	     "Focus_link_highlight(%p)\n",
+	     link);
+
+   hilit = a_Dw_iterator_clone(link->it);
+   n = link->link_length;
+
+   do
+      a_Dw_iterator_highlight(hilit, 0,
+                              strlen(hilit->content.data.text)
+                              + (n > 1 ? 1 : 0),
+                              DW_HIGHLIGHT_FOCUS);
+   while (--n && a_Dw_iterator_next(hilit));
+
+   a_Dw_iterator_free(hilit);
+   link->highlight = TRUE;
+
+   gtk_signal_emit_by_name(GTK_OBJECT(link->it->widget),
+			   "link_entered",
+			   link->link, -2, -2, &return_val);
+}
+
+/*
+ * Unhighlight FocusLink
+ * See Highlight focuslink for more info.
+ */
+void
+Focus_link_unhighlight (FocusLink *link)
+{			  
+   DwIterator *hilit;
+   gint n;
+   gboolean return_val;
+
+   DEBUG_MSG(DEBUG_EVENT,
+	     "Focus_link_unhighlight(%p)\n",
+	     link);
+
+   if(link->highlight) {
+      hilit = a_Dw_iterator_clone(link->it);
+      n = link->link_length;
+
+      do
+         a_Dw_iterator_highlight(hilit,
+                                 -1, -1, DW_HIGHLIGHT_FOCUS);
+      while (--n && a_Dw_iterator_next(hilit));
+
+      a_Dw_iterator_free(hilit);
+      link->highlight = FALSE;
+
+      if(GTK_IS_WIDGET(link->it->widget))
+         gtk_signal_emit_by_name(GTK_OBJECT(link->it->widget),
+                                 "link_entered",
+                                 -1, -2, -2, &return_val);
+   }
+}
+
+/* Focus_state */
+
+/*
+ * Free focus link lists
+ */
+void
+Focus_state_links_free(FocusState *state)
+{
+   DEBUG_MSG(DEBUG_EVENT,
+	     "Focus_state_links_free(%p)\n",
+	     state);
+
+   if(state->links) {
+      g_list_foreach(state->links, &Focus_link_destroy_callback, NULL);
+      g_list_free(state->links);
+      g_list_free(state->links_y);
+      state->links = NULL;
+      state->links_y = NULL;
+   }
+}
+
+/*
+ * comparison function for GList, sorts on y.x
+ */
+gint
+Focus_state_links_sort_y(gconstpointer a, gconstpointer b)
+{
+   FocusLink *link1 = (FocusLink *) a;
+   FocusLink *link2 = (FocusLink *) b;
+   gint result;
+
+   result = link1->location.y - link2->location.y;
+   if (!result)
+      result = link1->location.x - link2->location.x;
+
+   return result;
+}
+
+/*
+ * comparison function for g_list_sort_custom, compares link
+ * in FocusLink a to gint link b
+ */
+gint
+Focus_state_links_find_link(gconstpointer a, gconstpointer b)
+{
+   FocusLink *link1 = (FocusLink *) a;
+   gpointer link = (gpointer) b;
+
+   return !(link1->link == link);
+}
+
+/*
+ * Focus current link
+ */
+gboolean
+Focus_state_focus_link(FocusState *state)
+{
+   DwRectangle *view;
+
+   g_return_val_if_fail((state != NULL &&
+			 state->widget != NULL &&
+			 state->link != NULL), FALSE);
+
+   view =
+      a_Dw_gtk_viewport_get_view(GTK_DW_VIEWPORT((state->widget)->viewport));
+
+   if(!Location_in_view_offset(&state->link->location, view, 15))
+      a_Dw_iterator_scroll_to(state->link->it, DW_POSITION_CENTER);
+   if(state->link->type == DW_FOCUSABLE_LINK)
+      Focus_link_highlight(state->link);
+   else
+      a_Dw_iterator_focus(state->link->it);
+   
+   return TRUE;
+}
+
+/*
+ * add focusable to FocusState
+ * Uses gpointer link as identifier, which contains either
+ * a link number or a widget pointer.
+ * The iterator is added to the focusable and will be
+ * destroyed with it. If you want to keep on using it,
+ * pass a clone to this function.
+ */
+void
+a_Focus_state_focusable_add(FocusState *state, gpointer link,
+			    DwIterator *it,
+			    DwFocusableType type)
+{
+   GList *list;
+   FocusLink *focus_link;
+
+   DEBUG_MSG(DEBUG_ALLOCATE,
+	     "focusable_add (%p,%p,%p,%i)\n",
+	     state, link, it, type);
+
+   if(state->widget) {
+      list = g_list_find_custom(state->links, link,
+                                &Focus_state_links_find_link);
+      if(list) {
+         focus_link = (FocusLink *) list->data;
+         (focus_link->link_length)++;
+         a_Dw_iterator_free(it);
+         DEBUG_MSG(DEBUG_ALLOCATE,
+                   "increase link length for link %p to %i\n",
+                   link, focus_link->link_length);
+      } else {
+         focus_link = g_new0(FocusLink, 1);
+         focus_link->link_length = 1;
+         focus_link->location.x = -1;
+         focus_link->location.y = -1;
+         focus_link->link = link;
+         focus_link->type = type;
+         focus_link->it = it;
+         state->links = g_list_append(state->links, (gpointer) focus_link);
+         state->links_y =
+            g_list_insert_sorted(state->links_y, (gpointer) focus_link,
+                                 &Focus_state_links_sort_y);
+      }
+   }
+}
+
+/*
+ * Allocate focusable
+ */
+void
+a_Focus_state_focusable_allocate(FocusState *state, gpointer link,
+				 gint32 x, gint32 y)
+{
+   GList *list;
+   FocusLink *focus_link;
+
+   DEBUG_MSG(DEBUG_ALLOCATE,
+	     "focusable_allocate (%p,%p,(%i,%i))\n",
+	     state, link, x, y);
+
+   if(state->widget) {
+      list = g_list_find_custom(state->links, link,
+				&Focus_state_links_find_link);
+      if(list) {
+         focus_link = (FocusLink *) list->data;
+         focus_link->location.x = x;
+         focus_link->location.y = y;
+      }
+   }
+}
+
+/*
+ * Find <direction> list element
+ * TODO: optimize for size
+ */
+GList *
+Focus_state_find_list(FocusState *state, GtkDirectionType direction)
+{
+   GList *current, *list, *lower, *higher;
+   FocusLink *current_link, *lower_link, *higher_link;
+   DwRectangle *view;
+
+   view =
+      a_Dw_gtk_viewport_get_view(GTK_DW_VIEWPORT((state->widget)->viewport));
+   current = g_list_find(state->links, (gpointer) state->link);
+   current_link = (FocusLink *) (current)->data;
+   lower = NULL;
+   higher = NULL;
+   list = NULL;
+
+   /* the purpose of this long-winded function is to find the link which
+    * lies closest to the last link, starting in the currently visible
+    * part of the document. If the current link is in the visible part
+    * of the document, it will return the next/previous link closest to
+    * the current link's x coordinate. If the current link is outside the
+    * visible area of the document, it will return the next/previous
+    * closest link in the currently visible part of the document. This
+    * way scrolling is minimized.
+    */
+   switch(direction) {
+   case GTK_DIR_UP:
+      if(Location_in_view(&state->cursor, view)) {
+         list = g_list_find(state->links_y, (gpointer) state->link);
+         while(list &&
+               (state->link = ((FocusLink *) (list)->data)) &&
+               Focus_link_on_cursor_y(state->link, state->cursor))
+            list = g_list_previous(list);
+      } else {
+         list = g_list_last(state->links_y);
+         while(list &&
+               (((state->link = ((FocusLink *) (list)->data)) &&
+                 state->link->location.y > (view->y + view->height - 15)) ||
+                state->link == current_link))
+            list = g_list_previous(list);
+      }
+
+      if(list) {
+         state->cursor.y = state->link->location.y;
+         higher = list;
+         while((list = g_list_previous(list)) &&
+               (state->link = ((FocusLink *) (list)->data)) &&
+               Focus_link_on_cursor_y(state->link, state->cursor)) {
+            if(state->link->location.x <= state->cursor.x) {
+               lower = list;
+               break;
+            } else
+               higher = list;
+         }
+     
+         if(lower && higher) {
+            lower_link = ((FocusLink *) (lower)->data);
+            higher_link = ((FocusLink *) (higher)->data);
+            if((state->cursor.x - lower_link->location.x) <
+               (higher_link->location.x - state->cursor.x))
+               list = lower;
+            else
+               list = higher;
+         } else
+            list = higher;
+      }
+
+      if(!list)
+         list = current;
+      break;	 
+     
+   case GTK_DIR_DOWN:
+      if(Location_in_view(&state->cursor, view)) {
+         list = g_list_find(state->links_y, (gpointer) state->link);
+         while(list &&
+               (state->link = ((FocusLink *) (list)->data)) &&
+               Focus_link_on_cursor_y(state->link, state->cursor))
+            list = g_list_next(list);
+      } else {
+         list = g_list_first(state->links_y);
+         while(list &&
+               (((state->link = ((FocusLink *) (list)->data)) &&
+                 state->link->location.y < view->y) ||
+                state->link == current_link))
+            list = g_list_next(list);
+      }
+
+      if(list) {
+         state->cursor.y = state->link->location.y;
+         lower = list;
+         while((list = g_list_next(list)) &&
+               (state->link = ((FocusLink *) (list)->data)) &&
+               Focus_link_on_cursor_y(state->link, state->cursor)) {
+            if(state->link->location.x < state->cursor.x)
+               lower = list;
+            else {
+               higher = list;
+               break;
+            }
+         }
+
+         if(lower && higher) {
+            lower_link = ((FocusLink *) (lower)->data);
+            higher_link = ((FocusLink *) (higher)->data);
+            if((state->cursor.x - lower_link->location.x) <
+               (higher_link->location.x - state->cursor.x))
+               list = lower;
+            else
+               list = higher;
+         } else
+            list = lower;
+      }
+
+      if(!list)
+         list = current;
+      break;
+     
+   case GTK_DIR_LEFT:
+      if(Location_in_view(&state->cursor, view)) {
+         list = g_list_find(state->links_y, (gpointer) state->link);
+         if((list = g_list_previous(list)) &&
+            (state->link = ((FocusLink *) (list)->data)) &&
+            state->link->location.y != -1);
+         else
+            list = g_list_previous(current);
+      } else {
+         list = g_list_last(state->links_y);
+         while(list &&
+               (((state->link = ((FocusLink *) (list)->data)) &&
+                 state->link->location.y > (view->y + view->height - 15)) ||
+                state->link == current_link))
+            list = g_list_previous(list);
+      }
+
+      if(list) {
+         state->link = ((FocusLink *) (list)->data);
+         a_Focus_state_set_cursor(state, &state->link->location);
+      } else
+         list = current;
+      break;
+     
+   case GTK_DIR_RIGHT:
+      if(Location_in_view(&state->cursor, view)) {
+         list = g_list_find(state->links_y, (gpointer) state->link);
+         if((list = g_list_next(list)) &&
+            (state->link = ((FocusLink *) (list)->data)) &&
+            state->link->location.y != -1);
+         else
+            list = g_list_next(current);
+      } else {
+         list = g_list_first(state->links_y);
+         while(list &&
+               (((state->link = ((FocusLink *) (list)->data)) &&
+                 state->link->location.y <  view->y) ||
+                state->link == current_link))
+            list = g_list_next(list);
+      }
+      if(list) {
+         state->link = ((FocusLink *) (list)->data);
+         a_Focus_state_set_cursor(state, &state->link->location);
+      } else
+         list = current;
+      break;
+     
+   case GTK_DIR_TAB_BACKWARD:
+      list = current;
+      do {
+         list = g_list_previous(list);
+      } while (list &&
+               (state->link = ((FocusLink *) (list)->data)) &&
+               state->link->type != DW_FOCUSABLE_WIDGET);
+      if(list &&
+         state->link->type == DW_FOCUSABLE_WIDGET)
+         state->cursor.y = state->link->location.y;
+      break;
+     
+   case GTK_DIR_TAB_FORWARD:
+      list = current;
+      do {
+         list = g_list_next(list);
+      } while (list &&
+               (state->link = ((FocusLink *) (list)->data)) &&
+               state->link->type != DW_FOCUSABLE_WIDGET);
+      if(list &&
+         state->link->type == DW_FOCUSABLE_WIDGET)
+         state->cursor.y = state->link->location.y;
+      break;
+   default:
+      break;
+   }
+
+   return list;
+}
+
+/*
+ * Find link, searching <direction>
+ * TODO: optimize for size
+ */
+gboolean
+Focus_state_find_link (FocusState *state, GtkDirectionType direction)
+{
+   gboolean return_val;
+   GList *list;
+   DwRectangle *view;
+
+   return_val = FALSE;
+   list = NULL;
+
+   DEBUG_MSG(DEBUG_EVENT,
+	     "==(Focus_state_find_link)==\n(start) cursor x %i y %i\n",
+	     state->cursor.x, state->cursor.y);
+
+   if(state->links) {
+      view =
+         a_Dw_gtk_viewport_get_view(GTK_DW_VIEWPORT((state->widget)->viewport));
+      state->links_y = 
+         g_list_sort(state->links_y, &Focus_state_links_sort_y);
+      if(state->link)
+         /* previous link: start at last focused link */
+         list = Focus_state_find_list(state, direction);
+      else if(state->cursor.x || state->cursor.y) {
+         /* cursor is set: start at cursor */
+         list = g_list_first(state->links);
+         while(list &&
+               (state->link = ((FocusLink *) (list)->data)) &&
+               (state->link->location.x != state->cursor.x ||
+                state->link->location.y != state->cursor.y))
+            list = g_list_next(list);
+         list = Focus_state_find_list(state, direction);
+      } else
+         /* no previous link or cursor: start at top or bottom */
+         switch(direction) {
+         case GTK_DIR_TAB_BACKWARD:
+            list = g_list_last(state->links);
+            state->link = ((FocusLink *) (list)->data);
+            if(state->link->type != DW_FOCUSABLE_WIDGET) do
+               list = g_list_previous(list);
+            while (list &&
+                   (state->link = ((FocusLink *) (list)->data)) &&
+                   state->link->type != DW_FOCUSABLE_WIDGET);
+            if(list &&
+               state->link->type == DW_FOCUSABLE_WIDGET)
+               state->cursor.y = state->link->location.y;
+            break;
+	 
+         case GTK_DIR_TAB_FORWARD:
+            list = g_list_first(state->links);
+            state->link = ((FocusLink *) (list)->data);
+            if(state->link->type != DW_FOCUSABLE_WIDGET) do
+               list = g_list_next(list);
+            while (list &&
+                   (state->link = ((FocusLink *) (list)->data)) &&
+                   state->link->type != DW_FOCUSABLE_WIDGET);
+            if(list &&
+               state->link->type == DW_FOCUSABLE_WIDGET)
+               state->cursor.y = state->link->location.y;
+            break;
+	 
+         case GTK_DIR_UP:
+         case GTK_DIR_LEFT:
+            list = g_list_last(state->links);
+            while(list &&
+                  (state->link = ((FocusLink *) (list)->data)) &&
+                  state->link->location.y > view->y + view->height)
+               list = g_list_previous(list);
+            if (list)
+               state->cursor.y = state->link->location.y;
+            break;
+	 
+         case GTK_DIR_DOWN:
+         case GTK_DIR_RIGHT:
+            list = g_list_first(state->links);
+            state->link = ((FocusLink *) (list)->data);
+            while(list &&
+                  (state->link = ((FocusLink *) (list)->data)) &&
+                  state->link->location.y < view->y)
+               list = g_list_next(list);
+            if(list)
+               state->cursor.y = state->link->location.y;
+            break;
+	 
+         default:
+            break;
+         }
+     
+      if(list) {
+         state->link = ((FocusLink *) (list)->data);
+         return_val = TRUE;
+         DEBUG_MSG(DEBUG_EVENT,
+                   "(end)   cursor x %i y %i\n",
+                   state->cursor.x, state->cursor.y);
+         DEBUG_MSG(DEBUG_EVENT,
+                   "        link x %i y %i\n--(Focus_state_find_link)--\n",
+                   state->link->location.x, state->link->location.y);
+      } else {
+         DEBUG_MSG(DEBUG_EVENT,
+                   "(end)   NOT FOUND, link/cursor reset\n");
+         state->link = NULL;
+         state->cursor.x = 0;
+         state->cursor.y = 0;
+      }
+   }
+   
+   return return_val;
+}
+
+/* PUBLIC */
+
+/*
+ * Create new FocusState
+ */
+FocusState *
+a_Focus_state_new(void)
+{
+   return g_new0(FocusState, 1);
+}
+
+/*
+ * Destroy a FocusState
+ */
+void
+a_Focus_state_destroy(FocusState *state)
+{
+   Focus_state_links_free(state);
+   g_free(state);
+}
+
+/*
+ * Set the base wiget
+ */
+void
+a_Focus_state_set_widget(FocusState *state,
+			 DwWidget *widget)
+{
+   a_Focus_state_reset(state);
+   Focus_state_links_free(state);
+   state->widget = widget;
+}  
+
+/*
+ * Get cursor
+ */
+DwLocation *
+a_Focus_state_get_cursor(FocusState *state)
+{
+   if((state->link) &&
+      state->link->highlight) {
+      /* set cursor to highlighted link */
+      state->cursor.x = state->link->location.x;
+      state->cursor.y = state->link->location.y;
+   }
+   return &state->cursor;
+}
+
+/*
+ * Set cursor
+ */
+void
+a_Focus_state_set_cursor(FocusState *state,
+			 DwLocation *cursor)
+{
+   if(cursor) {
+      state->cursor.x = cursor->x;
+      state->cursor.y = cursor->y;
+   }
+}
+
+/*
+ * Find and focus a link
+ *
+ * First check iterator for link (link != -1), then search
+ * links list for this link and focus it. Returns FALSE if
+ * no link is focused
+ */
+gboolean
+a_Focus_state_focus(FocusState *state,
+		    DwIterator *it)
+{
+   GList *list;
+   gint link;
+   gboolean return_val;
+
+   return_val = FALSE;
+   
+   if(state->widget &&
+      state->links) {
+      /* unhighlight focus */
+      if(state->link &&
+         state->link->highlight)
+         Focus_link_unhighlight(state->link);
+     
+      link = a_Dw_iterator_get_link(it);
+     
+      if(link != -1) {
+         list = g_list_find_custom(state->links, (gpointer) link,
+                                   &Focus_state_links_find_link);
+         if(list) {
+            state->link = ((FocusLink *) (list)->data);
+            return_val = Focus_state_focus_link(state);
+         }
+      }
+   }
+   
+   return return_val;
+}
+
+/*
+ * Focus <direction> next link
+ * Returns FALSE if no link is focused
+ */
+gboolean
+a_Focus_state_focus_next(FocusState *state,
+			 GtkDirectionType direction)
+{
+   gboolean return_val;
+
+   return_val = FALSE;
+
+   if(state->widget &&
+      state->links) {
+      /* Grab focus for the scrolled_frame (it may have been
+       * grabbed by an embedded GTK+ widget) */
+      if(!GTK_WIDGET_HAS_FOCUS(GTK_WIDGET(state->widget->viewport)->parent))
+         gtk_widget_grab_focus(GTK_WIDGET(state->widget->viewport)->parent);
+
+      /* unhighlight focus */
+      if(state->link &&
+         state->link->highlight)
+         Focus_link_unhighlight(state->link);
+
+      if(Focus_state_find_link(state, direction))
+         return_val = Focus_state_focus_link(state);
+   }
+
+   return return_val;
+}
+
+/*
+ * Draw focus when has_focus, otherwise draw normal
+ */
+void
+a_Focus_state_draw_focus(FocusState *state, gboolean has_focus)
+{
+   if(state && state->link) {
+      if(has_focus) {
+         if(state->link->type == DW_FOCUSABLE_LINK)
+            Focus_link_highlight(state->link);
+         else
+            a_Dw_iterator_focus(state->link->it);
+      } else
+         if(state->link->type == DW_FOCUSABLE_LINK)
+            Focus_link_unhighlight(state->link);
+   }
+}
+
+/*
+ * Reset focus
+ */
+gboolean
+a_Focus_state_reset (FocusState *state)
+{
+   gboolean return_val;
+
+   return_val = FALSE;
+
+   DEBUG_MSG(DEBUG_EVENT,
+	     "reset focus_state\n");
+
+   /* unhighlight focus */
+   if(state->link) {
+      if((return_val = state->link->highlight))
+         Focus_link_unhighlight(state->link);
+      /* save last link location in cursor */
+      state->cursor.x = state->link->location.x;
+      state->cursor.y = state->link->location.y;
+   }
+   
+   state->link = NULL;
+
+   return return_val;
+}
+
+/*
+ * Activate link
+ *
+ * Send a synthetic link_clicked event to
+ * the state's link.
+ *
+ * TODO: change popup hack into something more elegant
+ */
+gboolean
+a_Focus_state_activate(FocusState *state, GdkModifierType modifier)
+{
+   DEBUG_MSG(DEBUG_EVENT,
+	     "a_Focus_state_activate\n");
+   if(modifier == GDK_MOD1_MASK)
+      return a_Focus_state_popup(state);
+   else
+      return
+         a_Focus_state_button_event(state, "link_clicked",
+                                    GDK_BUTTON_RELEASE, 1, modifier);
+}
+
+/*
+ * Popup context menu
+ *
+ * Send a synthetic link_pressed event to
+ * the state's link.
+ */
+gboolean
+a_Focus_state_popup(FocusState *state)
+{
+   DEBUG_MSG(DEBUG_EVENT,
+	     "a_Focus_state_popup\n");
+   return
+      a_Focus_state_button_event(state, "link_pressed",
+                                 GDK_BUTTON_PRESS, 3, 0);
+}
+
+/*
+ * Send a synthetic button_event to the focusable
+ * If there is no link, popup the page menu
+ */
+gboolean
+a_Focus_state_button_event(FocusState *state,
+                           gchar *signal_name,
+                           GdkEventType type,
+                           gint button, GdkModifierType modifier)
+{
+   GdkEventButton *button_event;
+   gboolean return_val;
+
+   DEBUG_MSG(DEBUG_EVENT,
+	     "a_Focus_state_button_event: ");
+
+   return_val = FALSE;
+
+   if(state && state->widget) {
+      /* create button event structure,
+       * as that is what the callback expects */
+      button_event = g_new0(GdkEventButton, 1);
+      button_event->type = type;
+      button_event->state = 0 | modifier;
+      button_event->button = button;
+      button_event->window = state->widget->viewport->window;
+      
+      if((state->link) &&
+         state->link->highlight &&
+         state->link->it->content.data.widget &&
+         state->link->type != DW_FOCUSABLE_WIDGET) {
+         DEBUG_MSG(DEBUG_EVENT,
+                   "popup link menu for link %i\n", (gint) state->link->link);
+         /* set coordinates to link location */
+         button_event->x =
+            (gdouble) p_Dw_widget_x_world_to_viewport(state->link->it->widget,
+                                                      state->link->location.x);
+         button_event->y =
+            (gdouble) p_Dw_widget_y_world_to_viewport(state->link->it->widget,
+                                                      state->link->location.y);
+         gtk_signal_emit(GTK_OBJECT(state->link->it->widget),
+                         gtk_signal_lookup(signal_name, DW_TYPE_WIDGET),
+                         (gint) state->link->link,
+                         -2, -2, button_event,
+                         &return_val);
+      } else {
+         DEBUG_MSG(DEBUG_EVENT,
+                   "no link, popup page menu\n");
+         /* no link, popup page menu instead */
+         /* set coordinates to top left of window */
+         button_event->x = (gdouble) 10;
+         button_event->y = (gdouble) 10;
+         gtk_signal_emit(GTK_OBJECT(state->widget->viewport->parent),
+                         gtk_signal_lookup("button_press_event", GTK_TYPE_WIDGET),
+                         button_event,
+                         &return_val);
+      }
+      g_free(button_event);
+   }
+
+   return return_val;
+}
diff -pruN dillo/src/focus.h dillo_tabs/src/focus.h
--- dillo/src/focus.h	1970-01-01 01:00:00.000000000 +0100
+++ dillo_tabs/src/focus.h	2003-11-24 14:32:40.244754232 +0100
@@ -0,0 +1,88 @@
+#ifndef __FOCUS_H__
+#define __FOCUS_H__
+
+#include "dw_widget.h"
+
+typedef struct _FocusState FocusState;
+typedef struct _FocusLink  FocusLink;
+
+struct _FocusLink
+{
+   DwIterator *it;
+   DwLocation location;
+   gpointer link;
+   gint link_length;
+   DwFocusableType type;
+   gboolean highlight;
+};
+
+/*
+ * FocusState
+ */
+struct _FocusState
+{
+   DwWidget *widget;
+   GList *links, *links_y;
+   FocusLink *link;
+   DwLocation cursor;
+   DwRectangle position;
+};
+
+FocusState* a_Focus_state_new               (void);
+void a_Focus_state_destroy                  (FocusState *state);
+void a_Focus_state_set_widget               (FocusState *state,
+					     DwWidget *widget);
+void a_Focus_state_focusable_add            (FocusState *state,
+					     gpointer link,
+					     DwIterator *it,
+					     DwFocusableType type);
+void a_Focus_state_focusable_allocate       (FocusState *state,
+					     gpointer link,
+					     gint32 x,
+					     gint32 y);
+gboolean a_Focus_state_activate             (FocusState *state,
+					     GdkModifierType modifier);
+gboolean a_Focus_state_popup                (FocusState *state);
+gboolean a_Focus_state_button_event         (FocusState *state,
+                                             gchar *signal_name,
+                                             GdkEventType type,
+                                             gint button,
+					     GdkModifierType modifier);
+gboolean a_Focus_state_reset                (FocusState *state);
+gboolean a_Focus_state_focus                (FocusState *state,
+					     DwIterator *it);
+gboolean a_Focus_state_focus_next           (FocusState *state,
+					     GtkDirectionType direction);
+DwLocation * a_Focus_state_get_cursor       (FocusState *state);
+void a_Focus_state_set_cursor               (FocusState *state,
+					     DwLocation *cursor);
+void a_Focus_state_draw_focus               (FocusState *state,
+					     gboolean has_focus);
+
+/* Location_in_view() returns TRUE when the location is within
+ * the view region. Location_in_view_offset() does the same
+ * for the view region plus or minus an (y) offset. */
+#define Location_in_view(location, view) \
+   Location_in_view_offset(location, view, 0)
+/* there are two versions of this macro: a 'correct' one
+ * which takes both x and y coordinates into account, and
+ * a 'currently the best we can do' one which ignores x
+ * coordinates (currently only the y coordinate is used in 
+ * anchors, URLs, iterators, etc.)
+ */
+#if 0
+#define Location_in_view_offset(location, view, offset)  \
+   (((location)->x >= (view)->x) && \
+    ((location)->x <= ((view)->x + (view)->width)) && \
+    ((location)->y >= (view)->y) && \
+    ((location)->y <= ((view)->y + (view)->height - offset)))
+#else
+#define Location_in_view_offset(location, view, offset)  \
+   (((location)->y >= (view)->y) && \
+    ((location)->y <= ((view)->y + (view)->height - offset)))
+#endif
+#define Focus_link_on_cursor_y(link, cursor) \
+   ((((link)->location.y) != -1) && \
+    (((link)->location.y) == ((cursor).y)))
+
+#endif /* __FOCUS_H__ */
diff -pruN dillo/src/gtkframeset.c dillo_tabs/src/gtkframeset.c
--- dillo/src/gtkframeset.c	1970-01-01 01:00:00.000000000 +0100
+++ dillo_tabs/src/gtkframeset.c	2003-11-24 14:32:40.245754080 +0100
@@ -0,0 +1,1305 @@
+/*
+ * File: gtkframeset.c
+ * Copyright (C) 2003 Frank de Lange <frank@unternet.org>
+ *
+ *   a frameset widget for GTK - The GIMP Toolkit
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "gtkframeset.h"
+#include <string.h>              /* for strpbrk() */
+#include <locale.h>              /* for setlocale */
+
+#define DEBUG_ALLOC 10
+#define DEBUG_EVENT 10
+/* #define DEBUG_LEVEL 10 */
+#include "debug.h"
+
+/* child args */
+enum
+{
+  CHILD_ARG_0,
+  CHILD_ARG_ROW_ATTACH,
+  CHILD_ARG_COL_ATTACH,
+  CHILD_ARG_X_PADDING,
+  CHILD_ARG_Y_PADDING,
+  CHILD_ARG_BORDER,
+  CHILD_ARG_NORESIZE
+};
+  
+/* declarations */
+static void gtk_frameset_class_init          (GtkFramesetClass  *klass);
+static void gtk_frameset_init	             (GtkFrameset       *frameset);
+static void gtk_frameset_finalize	     (GtkObject	        *object);
+static void gtk_frameset_size_allocate       (GtkWidget	        *widget,
+					      GtkAllocation     *allocation);
+static void gtk_frameset_map	             (GtkWidget	        *widget);
+static void gtk_frameset_unmap	             (GtkWidget	        *widget);
+static void gtk_frameset_draw	             (GtkWidget	        *widget,
+					      GdkRectangle      *area);
+static gint gtk_frameset_expose	             (GtkWidget	        *widget,
+					      GdkEventExpose    *event);
+static void gtk_frameset_set_child_arg       (GtkContainer      *container,
+					      GtkWidget         *child,
+					      GtkArg            *arg,
+					      guint              arg_id);
+static void gtk_frameset_get_child_arg       (GtkContainer      *container,
+					      GtkWidget         *child,
+					      GtkArg            *arg,
+					      guint              arg_id);
+static GtkType gtk_frameset_child_type       (GtkContainer      *container);
+static void gtk_frameset_add	             (GtkContainer      *container,
+					      GtkWidget	        *widget);
+static void gtk_frameset_remove	             (GtkContainer      *container,
+					      GtkWidget	        *widget);
+static void gtk_frameset_forall	             (GtkContainer      *container,
+					      gboolean	         include_internals,
+					      GtkCallback        callback,
+					      gpointer	         callback_data);
+static void gtk_frameset_realize             (GtkWidget         *widget);
+static void gtk_frameset_unrealize           (GtkWidget         *widget);
+static gint gtk_frameset_button_press        (GtkWidget         *widget,
+					      GdkEventButton    *event);
+static gint gtk_frameset_button_release      (GtkWidget         *widget,
+					      GdkEventButton    *event);
+static gint gtk_frameset_motion              (GtkWidget         *widget,
+					      GdkEventMotion    *event);
+static gint gtk_frameset_enter               (GtkWidget         *widget,
+					      GdkEventCrossing  *event);
+static gint gtk_frameset_leave               (GtkWidget         *widget,
+					      GdkEventCrossing  *event);
+
+/* private functions */
+static void gtk_frameset_size_allocate_init (GtkFrameset        *frameset);
+static void gtk_frameset_size_allocate_pass1(GtkFrameset        *frameset);
+static GSList *gtk_frameset_get_multi_length(const gchar        *attr);
+static void gtk_frameset_calculate_lengths  (GtkFrameset        *frameset);
+static gint gtk_frameset_set_resize_rowcol  (GtkWidget          *widget,
+					     gint               x,
+					     gint               y);
+
+static GtkContainerClass *parent_class = NULL;
+
+/* standard GTK function */
+GtkType
+gtk_frameset_get_type (void)
+{
+  static GtkType frameset_type = 0;
+  
+  if (!frameset_type)
+    {
+      static const GtkTypeInfo frameset_info =
+      {
+	"GtkFrameset",
+	sizeof (GtkFrameset),
+	sizeof (GtkFramesetClass),
+	(GtkClassInitFunc) gtk_frameset_class_init,
+	(GtkObjectInitFunc) gtk_frameset_init,
+        /* reserved_1 */ NULL,
+	/* reserved_2 */ NULL,
+	(GtkClassInitFunc) NULL,
+      };
+      
+      frameset_type = gtk_type_unique (gtk_container_get_type (), &frameset_info);
+    }
+  
+  return frameset_type;
+}
+
+/* standard GTK function */
+static void
+gtk_frameset_class_init (GtkFramesetClass *class)
+{
+  GtkObjectClass *object_class;
+  GtkWidgetClass *widget_class;
+  GtkContainerClass *container_class;
+  
+  object_class = (GtkObjectClass*) class;
+  widget_class = (GtkWidgetClass*) class;
+  container_class = (GtkContainerClass*) class;
+  
+  parent_class = gtk_type_class (gtk_container_get_type ());
+  
+  gtk_container_add_child_arg_type ("GtkFrameset::row_attach", GTK_TYPE_UINT, GTK_ARG_READWRITE, CHILD_ARG_ROW_ATTACH);
+  gtk_container_add_child_arg_type ("GtkFrameset::col_attach", GTK_TYPE_UINT, GTK_ARG_READWRITE, CHILD_ARG_COL_ATTACH);
+  gtk_container_add_child_arg_type ("GtkFrameset::x_padding", GTK_TYPE_UINT, GTK_ARG_READWRITE, CHILD_ARG_X_PADDING);
+  gtk_container_add_child_arg_type ("GtkFrameset::y_padding", GTK_TYPE_UINT, GTK_ARG_READWRITE, CHILD_ARG_Y_PADDING);
+  gtk_container_add_child_arg_type ("GtkFrameset::border", GTK_TYPE_BOOL, GTK_ARG_READWRITE, CHILD_ARG_BORDER);
+  gtk_container_add_child_arg_type ("GtkFrameset::noresize", GTK_TYPE_BOOL, GTK_ARG_READWRITE, CHILD_ARG_NORESIZE);
+
+  object_class->finalize = gtk_frameset_finalize;
+  
+  widget_class->size_allocate = gtk_frameset_size_allocate;
+  widget_class->map = gtk_frameset_map;
+  widget_class->unmap = gtk_frameset_unmap;
+  widget_class->draw = gtk_frameset_draw;
+  widget_class->expose_event = gtk_frameset_expose;
+  widget_class->realize = gtk_frameset_realize;
+  widget_class->unrealize = gtk_frameset_unrealize;
+  widget_class->button_press_event = gtk_frameset_button_press;
+  widget_class->button_release_event = gtk_frameset_button_release;
+  widget_class->motion_notify_event = gtk_frameset_motion;
+  widget_class->enter_notify_event = gtk_frameset_enter;
+  widget_class->leave_notify_event = gtk_frameset_leave;
+  
+  container_class->add = gtk_frameset_add;
+  container_class->remove = gtk_frameset_remove;
+  container_class->forall = gtk_frameset_forall;
+  container_class->child_type = gtk_frameset_child_type;
+  container_class->set_child_arg = gtk_frameset_set_child_arg;
+  container_class->get_child_arg = gtk_frameset_get_child_arg;
+}
+
+/* standard GTK function */
+static GtkType
+gtk_frameset_child_type (GtkContainer   *container)
+{
+  return GTK_TYPE_WIDGET;
+}
+
+/* standard GTK function */
+static void
+gtk_frameset_set_child_arg (GtkContainer   *container,
+			    GtkWidget      *child,
+			    GtkArg         *arg,
+			    guint           arg_id)
+{
+  GtkFrameset *frameset;
+  GtkFramesetChild *frameset_child;
+  GList *list;
+
+  frameset = GTK_FRAMESET (container);
+  frameset_child = NULL;
+  for (list = frameset->children; list; list = list->next)
+    {
+      frameset_child = list->data;
+
+      if (frameset_child->widget == child)
+	break;
+    }
+  if (!list)
+    return;
+
+  switch (arg_id)
+    {
+    case CHILD_ARG_ROW_ATTACH:
+      frameset_child->row_attach = GTK_VALUE_UINT (*arg);
+      break;
+    case CHILD_ARG_COL_ATTACH:
+      frameset_child->col_attach = GTK_VALUE_UINT (*arg);
+      break;
+    case CHILD_ARG_X_PADDING:
+      frameset_child->xpadding = GTK_VALUE_UINT (*arg);
+      break;
+    case CHILD_ARG_Y_PADDING:
+      frameset_child->ypadding = GTK_VALUE_UINT (*arg);
+      break;
+    case CHILD_ARG_BORDER:
+      frameset_child->border = GTK_VALUE_BOOL (*arg);
+      /* only SET this value (it is set to FALSE by default), otherwise
+       * border frames followed by noborder frames will lose their border
+       * 
+       * This feature can also be provided by manipulating the child widget's
+       * border directly. I have tested this and it works. I might use it in a later
+       * version of GtkFrameset */
+      if(GTK_VALUE_BOOL(*arg)) {
+	if(frameset_child->row_attach > 0)
+	  /* set top border */
+	  frameset->rows[frameset_child->row_attach - 1].border = GTK_VALUE_BOOL (*arg);
+	frameset->rows[frameset_child->row_attach].border = GTK_VALUE_BOOL (*arg);
+	if(frameset_child->col_attach > 0)
+	  /* set left border */
+	  frameset->cols[frameset_child->col_attach - 1].border = GTK_VALUE_BOOL (*arg);
+	frameset->cols[frameset_child->col_attach].border = GTK_VALUE_BOOL (*arg);
+      }
+      break;
+    case CHILD_ARG_NORESIZE:
+      frameset_child->noresize = GTK_VALUE_BOOL (*arg);
+      /* only SET this value (it is set to FALSE by default), otherwise
+       * noresize frames followed by resizable frames will be resizable as
+       * well through the resizable frames' left/top edge */
+      if(GTK_VALUE_BOOL(*arg)) {
+	if(frameset_child->row_attach + 1 < frameset->nrows)
+	  /* make lower edge unresizable */
+	  frameset->rows[frameset_child->row_attach + 1].noresize = GTK_VALUE_BOOL (*arg);
+	frameset->rows[frameset_child->row_attach].noresize = GTK_VALUE_BOOL (*arg);
+	if(frameset_child->col_attach + 1 < frameset->ncols)
+	  /* make right edge unresizable */
+	  frameset->cols[frameset_child->col_attach + 1].noresize = GTK_VALUE_BOOL (*arg);
+	frameset->cols[frameset_child->col_attach].noresize = GTK_VALUE_BOOL (*arg);
+      }
+      break;
+    default:
+      break;
+    }
+  if (GTK_WIDGET_VISIBLE (child) && GTK_WIDGET_VISIBLE (frameset))
+    gtk_widget_queue_resize (child);
+}
+
+/* standard GTK function */
+static void
+gtk_frameset_get_child_arg (GtkContainer   *container,
+                            GtkWidget      *child,
+			    GtkArg         *arg,
+			    guint           arg_id)
+{
+  GtkFrameset *frameset;
+  GtkFramesetChild *frameset_child;
+  GList *list;
+
+  frameset = GTK_FRAMESET (container);
+  frameset_child = NULL;
+  for (list = frameset->children; list; list = list->next)
+    {
+      frameset_child = list->data;
+
+      if (frameset_child->widget == child)
+	break;
+    }
+  if (!list)
+    return;
+
+  switch (arg_id)
+    {
+    case CHILD_ARG_ROW_ATTACH:
+      GTK_VALUE_UINT (*arg) = frameset_child->row_attach;
+      break;
+    case CHILD_ARG_COL_ATTACH:
+      GTK_VALUE_UINT (*arg) = frameset_child->col_attach;
+      break;
+    case CHILD_ARG_X_PADDING:
+      GTK_VALUE_UINT (*arg) = frameset_child->xpadding;
+      break;
+    case CHILD_ARG_Y_PADDING:
+      GTK_VALUE_UINT (*arg) = frameset_child->ypadding;
+      break;
+    case CHILD_ARG_BORDER:
+      GTK_VALUE_BOOL (*arg) = frameset_child->border;
+      break;
+    case CHILD_ARG_NORESIZE:
+      GTK_VALUE_BOOL (*arg) = frameset_child->noresize;
+      break;
+    default:
+      arg->type = GTK_TYPE_INVALID;
+      break;
+    }
+}
+
+/*
+ * Standard GTK function
+ */
+static void
+gtk_frameset_realize (GtkWidget *widget)
+{
+  GtkFrameset *frameset = GTK_FRAMESET(widget);
+  GdkWindowAttr attributes;
+  gint attributes_mask;
+
+  GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
+  
+  attributes.x = widget->allocation.x;
+  attributes.y = widget->allocation.y;
+  attributes.width = widget->allocation.width;
+  attributes.height = widget->allocation.height;
+  attributes.window_type = GDK_WINDOW_CHILD;
+  attributes.wclass = GDK_INPUT_OUTPUT;
+  attributes.visual = gtk_widget_get_visual (widget);
+  attributes.colormap = gtk_widget_get_colormap (widget);
+  attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
+  attributes_mask = (GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP);
+  attributes.event_mask |= (GDK_BUTTON_PRESS_MASK |
+			    GDK_BUTTON_RELEASE_MASK |
+			    GDK_POINTER_MOTION_MASK |
+			    GDK_POINTER_MOTION_HINT_MASK |
+			    GDK_ENTER_NOTIFY_MASK |
+			    GDK_LEAVE_NOTIFY_MASK);
+  
+  widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
+				   &attributes, attributes_mask);
+
+  frameset->cursor_rowcol = gdk_cursor_new(GDK_FLEUR);
+  frameset->cursor_row = gdk_cursor_new(GDK_SB_V_DOUBLE_ARROW);
+  frameset->cursor_col = gdk_cursor_new(GDK_SB_H_DOUBLE_ARROW);
+
+  gdk_window_set_user_data (widget->window, frameset);
+  widget->style = gtk_style_attach (widget->style, widget->window);
+  gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
+  gdk_window_set_back_pixmap (widget->window, NULL, TRUE);
+}
+
+/*
+ * standard GTK function
+ */
+static void
+gtk_frameset_unrealize (GtkWidget *widget)
+{
+  GtkFrameset *frameset = GTK_FRAMESET(widget);
+
+  gdk_cursor_destroy(frameset->cursor_rowcol);
+  gdk_cursor_destroy(frameset->cursor_row);
+  gdk_cursor_destroy(frameset->cursor_col);
+
+  GTK_WIDGET_CLASS(parent_class)->unrealize (widget);
+}
+  
+/* standard GTK function */
+static void
+gtk_frameset_init (GtkFrameset *frameset)
+{
+  GTK_WIDGET_UNSET_FLAGS (frameset, GTK_NO_WINDOW);
+
+  frameset->children = NULL;
+  frameset->rows = NULL;
+  frameset->cols = NULL;
+  frameset->nrows = 0;
+  frameset->ncols = 0;
+  frameset->resize_row = RESIZE_NONE;
+  frameset->resize_col = RESIZE_NONE;
+  frameset->in_drag = FALSE;
+  frameset->cursor_rowcol = NULL;
+  frameset->cursor_row = NULL;
+  frameset->cursor_col = NULL;
+
+  gtk_frameset_resize (frameset, "*", "*");
+}
+
+/*
+ * standard GTK function
+ *
+ * resize frameset. It is only possible to enlarge the
+ * frameset (or change dimensions while keeping the same
+ * size) as reducing the size could orphan children.
+ */
+void
+gtk_frameset_resize (GtkFrameset *frameset,
+		     gchar *row_multilengths,
+		     gchar *col_multilengths)
+{
+  gint n_rows, n_cols, n;
+  GSList *rows, *cols;
+  gfloat total_percentage, total_relative;
+  gfloat row_per_relative, row_per_percent;
+  gfloat col_per_relative, col_per_percent;
+
+  g_return_if_fail (frameset != NULL);
+  g_return_if_fail (GTK_IS_FRAMESET(frameset));
+  g_return_if_fail (row_multilengths || col_multilengths);
+
+  DEBUG_MSG(DEBUG_EVENT, "gtk_frameset_resize(%d, %s, %s)\n", (gint) frameset, row_multilengths, col_multilengths);
+  
+  rows = gtk_frameset_get_multi_length(row_multilengths);
+  cols = gtk_frameset_get_multi_length(col_multilengths);
+
+  /* (older versions of) compiler happiness */
+  row_per_relative = 0;
+  row_per_percent = 0;
+  col_per_relative = 0;
+  col_per_percent = 0;
+
+  n_rows = g_slist_length(rows);
+  n_cols = g_slist_length(cols);
+
+   /* calculate row and column dimensions. These calculations are slightly hairy...
+    * The theory goes as follows:
+    * 
+    * Starting point is the frameset width and height. 
+    * First come frames with absolute dimensions. No frame can be bigger than the frameset,
+    * so checks are performed to make sure that absolute frame dimensions do not
+    * exceed frameset dimensions. When the total number of frames with absolute dimensions
+    * exceeds the frameset dimensions, the available space is divided by ratio to those frames.
+    * In case there are only absolute dimensioned frames, available space is divided by ratio to
+    * these frames and the absolute dimensions are transformed into percentual dimensions.
+    *
+    * Next come frames with percentual dimensions. They get to divide the remaining space after
+    * all absolute dimensions have been allocated. If the total of all percentual dimensions adds
+    * up to more than 100, the calculation is normalised to 100%.
+    *
+    * Last come frames with relative dimensions. They get to divide the remaining space after
+    * all absolute and percentual dimensions have been allocated. All available space is divided
+    * by ratio to relative weights and allocated to frames. In the second stage, relative dimensions
+    * are transformed into percentual dimensions so the frameset ends up having only absolute (pixel)
+    * and percentual dimensions.
+    *
+    * The calculations are performed in two stages. In the first (aggregation) stage, the total weight of
+    * absolute, percentual and relative dimensions is calculated. In the second (normalisation) stage, relative
+    * dimensions are transformed into percentual dimensions. The actual calculation of pixel dimensions
+    * is performed in the callback function as it depends on information about the current size of the
+    * frameset widget.
+    *
+    * Can this be optimised? Sure, but... later...
+    */
+
+  if(n_rows >= frameset->nrows)
+    {
+      frameset->nrows = n_rows;
+      frameset->rows = g_realloc (frameset->rows, frameset->nrows * sizeof (GtkFramesetRowCol));
+
+      frameset->row_total_absolute = 0;
+      total_percentage = 0;
+      total_relative = 0;
+      
+      for(n = 0; n < frameset->nrows; n++) {
+	frameset->rows[n].length = GPOINTER_TO_INT(g_slist_nth_data(rows, n));
+	frameset->rows[n].noresize = FALSE;
+	frameset->rows[n].border = FALSE;
+	if(LENGTH_IS_RELATIVE(frameset->rows[n].length))
+	  (LENGTH_GET_RELATIVE(frameset->rows[n].length) ?
+	   total_relative += LENGTH_GET_RELATIVE(frameset->rows[n].length) :
+	   total_relative++);
+	else if(LENGTH_IS_PERCENTAGE(frameset->rows[n].length))
+	  total_percentage += LENGTH_GET_PERCENTAGE(frameset->rows[n].length);
+	else if(LENGTH_IS_ABSOLUTE(frameset->rows[n].length)) {
+	  frameset->row_total_absolute += LENGTH_GET_ABSOLUTE(frameset->rows[n].length);
+	}
+      }
+      
+      if((total_percentage == 0) && (total_relative == 0)) {
+	for(n = 0; n < frameset->nrows; n++)
+	  if(LENGTH_IS_ABSOLUTE(frameset->rows[n].length))
+	    frameset->rows[n].length =
+	      LENGTH_CREATE_PERCENTAGE((gfloat) LENGTH_GET_ABSOLUTE(frameset->rows[n].length) /
+				       frameset->row_total_absolute);
+	frameset->row_total_absolute = 0;
+	row_per_percent = 1;
+      } else if(total_percentage < 1) {
+	row_per_relative = (gfloat) (1 - total_percentage) / total_relative;
+	row_per_percent = (gfloat) (total_relative > 0 ? 1 : 1.0 / total_percentage);
+      } else {
+	row_per_percent = (gfloat) (1 / total_percentage);
+	row_per_relative = 0;
+      }
+    }
+  
+  if(n_cols >= frameset->ncols)
+    {
+      frameset->ncols = n_cols;
+      frameset->cols = g_realloc (frameset->cols, frameset->ncols * sizeof (GtkFramesetRowCol));
+      
+      frameset->col_total_absolute = 0;
+      total_percentage = 0;
+      total_relative = 0;
+      
+      for(n = 0; n < frameset->ncols; n++) {
+	frameset->cols[n].length = GPOINTER_TO_INT(g_slist_nth_data(cols, n));
+	frameset->cols[n].noresize = FALSE;
+	frameset->cols[n].border = FALSE;
+	if(LENGTH_IS_RELATIVE(frameset->cols[n].length))
+	  (LENGTH_GET_RELATIVE(frameset->cols[n].length) ?
+	   total_relative += LENGTH_GET_RELATIVE(frameset->cols[n].length) :
+	   total_relative++);
+	else if(LENGTH_IS_PERCENTAGE(frameset->cols[n].length))
+	  total_percentage += LENGTH_GET_PERCENTAGE(frameset->cols[n].length);
+	else if(LENGTH_IS_ABSOLUTE(frameset->cols[n].length)) {
+	  frameset->col_total_absolute += LENGTH_GET_ABSOLUTE(frameset->cols[n].length);
+	}
+      }
+
+      /* normalize values */
+      if((total_percentage == 0) && (total_relative == 0)) {
+	for(n = 0; n < frameset->ncols; n++)
+	  if(LENGTH_IS_ABSOLUTE(frameset->cols[n].length))
+	    frameset->cols[n].length =
+	      LENGTH_CREATE_PERCENTAGE((gfloat) LENGTH_GET_ABSOLUTE(frameset->cols[n].length) /
+				       frameset->col_total_absolute);
+	frameset->col_total_absolute = 0;
+	col_per_percent = 1;
+	col_per_relative = 0;
+      } else if(total_percentage < 1) {
+	col_per_relative = (gfloat) (1 - total_percentage) / total_relative;
+	col_per_percent = (gfloat) (total_relative > 0 ? 1 : 1.0 / total_percentage);
+      } else {
+	col_per_percent = (gfloat) (1 / total_percentage);
+	col_per_relative = 0;
+      }
+    }
+
+  /* now, calculate actual width/height distribution */
+  if(frameset->nrows > 1) {
+     for(n_rows = 0; n_rows < frameset->nrows; n_rows++) {
+       if(LENGTH_IS_RELATIVE(frameset->rows[n_rows].length))
+	 frameset->rows[n_rows].length = 
+	   LENGTH_CREATE_PERCENTAGE((LENGTH_GET_RELATIVE(frameset->rows[n_rows].length) ?
+				     (LENGTH_GET_RELATIVE(frameset->rows[n_rows].length) * row_per_relative) :
+				     row_per_relative));
+       else if(LENGTH_IS_PERCENTAGE(frameset->rows[n_rows].length))
+	 frameset->rows[n_rows].length =
+	   LENGTH_CREATE_PERCENTAGE((LENGTH_GET_PERCENTAGE(frameset->rows[n_rows].length) * row_per_percent));
+     }
+  } else
+    frameset->rows[0].length = LENGTH_CREATE_PERCENTAGE(1);
+
+  if(frameset->ncols > 1) {
+     for(n_cols = 0; n_cols < frameset->ncols; n_cols++) {
+       if(LENGTH_IS_RELATIVE(frameset->cols[n_cols].length))
+	 frameset->cols[n_cols].length = 
+	   LENGTH_CREATE_PERCENTAGE((LENGTH_GET_RELATIVE(frameset->cols[n_cols].length) ?
+				     (LENGTH_GET_RELATIVE(frameset->cols[n_cols].length) * col_per_relative) :
+				     col_per_relative));
+       else if(LENGTH_IS_PERCENTAGE(frameset->cols[n_cols].length))
+	 frameset->cols[n_cols].length =
+	   LENGTH_CREATE_PERCENTAGE(LENGTH_GET_PERCENTAGE(frameset->cols[n_cols].length) * col_per_percent);
+     }
+  } else
+    frameset->cols[0].length = LENGTH_CREATE_PERCENTAGE(1);
+
+  g_slist_free(rows);
+  g_slist_free(cols);
+}
+
+/* standard GTK function */
+GtkWidget*
+gtk_frameset_new (gchar	*row_multilengths,
+		  gchar	*col_multilengths)
+{
+  GtkFrameset *frameset;
+
+  if (!row_multilengths)
+    row_multilengths = "*";
+  if (!col_multilengths)
+    col_multilengths = "*";
+  
+  frameset = gtk_type_new (gtk_frameset_get_type ());
+
+  DEBUG_MSG(DEBUG_EVENT, "/*\n");
+
+  gtk_frameset_resize(frameset, row_multilengths, col_multilengths);
+
+  DEBUG_MSG(DEBUG_EVENT, " * NEW frameset: %d\n", (gint) frameset);
+  DEBUG_MSG(DEBUG_EVENT, " *               %d rows, %d cols\n", frameset->nrows, frameset->ncols);
+  DEBUG_MSG(DEBUG_EVENT, " */\n");
+  
+  return GTK_WIDGET (frameset);
+}
+
+/* standard GTK function */
+void
+gtk_frameset_attach (GtkFrameset	  *frameset,
+		     GtkWidget	          *child,
+		     guint		   row_attach,
+		     guint		   col_attach,
+		     guint                 xpadding,
+		     guint                 ypadding,
+		     gboolean              border,
+		     gboolean              noresize)
+{
+  GtkFramesetChild *frameset_child;
+  
+  g_return_if_fail (frameset != NULL);
+  g_return_if_fail (GTK_IS_FRAMESET (frameset));
+  g_return_if_fail (child != NULL);
+  g_return_if_fail (GTK_IS_WIDGET (child));
+  g_return_if_fail (child->parent == NULL);
+  g_return_if_fail (col_attach < frameset->ncols);
+  g_return_if_fail (row_attach < frameset->nrows);
+
+  DEBUG_MSG(DEBUG_EVENT, "gtk_frameset_attach(%d, %d, %d, %d, %d, %d, %d, %d)\n",
+	    (gint) frameset, (gint) child, row_attach, col_attach,
+	    xpadding, ypadding, border, noresize);
+  
+  frameset_child = g_new (GtkFramesetChild, 1);
+  frameset_child->widget = child;
+  frameset_child->row_attach = row_attach;
+  frameset_child->col_attach = col_attach;
+  frameset_child->xpadding = xpadding;
+  frameset_child->ypadding = ypadding;
+  frameset_child->border = border;
+  frameset_child->noresize = noresize;
+  
+  frameset->children = g_list_prepend (frameset->children, frameset_child);
+  
+  gtk_widget_set_parent (child, GTK_WIDGET (frameset));
+  
+  if (GTK_WIDGET_REALIZED (child->parent))
+    gtk_widget_realize (child);
+
+  if (GTK_WIDGET_VISIBLE (child->parent) && GTK_WIDGET_VISIBLE (child))
+    {
+      if (GTK_WIDGET_MAPPED (child->parent))
+	gtk_widget_map (child);
+      
+      gtk_widget_queue_resize (child);
+    }
+}
+
+/*
+ * Standard GTK function
+ *
+ * add a widget (frame) to the frameset. The widget
+ * will be put in the current_frame (which will
+ * be increased in the process)
+ */
+static void
+gtk_frameset_add (GtkContainer *frameset,
+		  GtkWidget    *widget)
+{
+  guint row, col;
+
+  /* is there space left in the frameset for this frame? */
+  if (GTK_FRAMESET(frameset)->current_frame >= 
+      (GTK_FRAMESET(frameset)->nrows * GTK_FRAMESET(frameset)->ncols)) {
+    DEBUG_MSG(DEBUG_EVENT, "No space in frameset for frame\n");
+    return;
+  }
+  
+  /* calculate row and column for frame */
+  col = GTK_FRAMESET(frameset)->current_frame % GTK_FRAMESET(frameset)->ncols;
+  row = GTK_FRAMESET(frameset)->current_frame / GTK_FRAMESET(frameset)->ncols;
+  
+  DEBUG_MSG(DEBUG_EVENT, "NEW FRAME in frameset %d\n", (gint) frameset);
+  DEBUG_MSG(DEBUG_EVENT, "      ROW %d COL %d\n", row, col);
+  
+  gtk_frameset_attach(GTK_FRAMESET(frameset), /* the frameset widget */
+		      widget,                 /* the child widget */
+		      row,                    /* row to attach to */
+		      col,                    /* column to attach to */
+		      0,                      /* marginwidth */
+		      0,                      /* marginheight */
+		      FALSE,                  /* border */
+		      FALSE);                 /* noresize */
+
+  (GTK_FRAMESET(frameset)->current_frame)++;
+}
+
+/* standard GTK function */
+static void
+gtk_frameset_remove (GtkContainer *container,
+		     GtkWidget    *widget)
+{
+  GtkFrameset *frameset;
+  GtkFramesetChild *child;
+  GList *children;
+  
+  g_return_if_fail (container != NULL);
+  g_return_if_fail (GTK_IS_FRAMESET (container));
+  g_return_if_fail (widget != NULL);
+  
+  frameset = GTK_FRAMESET (container);
+  children = frameset->children;
+  
+  while (children)
+    {
+      child = children->data;
+      children = children->next;
+      
+      if (child->widget == widget)
+	{
+	  gboolean was_visible = GTK_WIDGET_VISIBLE (widget);
+	  
+	  gtk_widget_unparent (widget);
+	  
+	  frameset->children = g_list_remove (frameset->children, child);
+	  g_free (child);
+	  
+	  if (was_visible && GTK_WIDGET_VISIBLE (container))
+	    gtk_widget_queue_resize (GTK_WIDGET (container));
+	  break;
+	}
+    }
+}
+
+/* standard GTK function */
+static void
+gtk_frameset_forall (GtkContainer *container,
+		  gboolean	include_internals,
+		  GtkCallback	callback,
+		  gpointer	callback_data)
+{
+  GtkFrameset *frameset;
+  GtkFramesetChild *child;
+  GList *children;
+  
+  g_return_if_fail (container != NULL);
+  g_return_if_fail (GTK_IS_FRAMESET (container));
+  g_return_if_fail (callback != NULL);
+  
+  frameset = GTK_FRAMESET (container);
+  children = frameset->children;
+  
+  while (children)
+    {
+      child = children->data;
+      children = children->next;
+      
+      (* callback) (child->widget, callback_data);
+    }
+}
+
+/* standard GTK function */
+static void
+gtk_frameset_finalize (GtkObject *object)
+{
+  GtkFrameset *frameset;
+  
+  g_return_if_fail (object != NULL);
+  g_return_if_fail (GTK_IS_FRAMESET (object));
+  
+  frameset = GTK_FRAMESET (object);
+  
+  g_free (frameset->rows);
+  g_free (frameset->cols);
+  
+  (* GTK_OBJECT_CLASS (parent_class)->finalize) (object);
+}
+
+/* standard GTK function */
+static void
+gtk_frameset_map (GtkWidget *widget)
+{
+  GtkFrameset *frameset;
+  GtkFramesetChild *child;
+  GList *children;
+  
+  g_return_if_fail (widget != NULL);
+  g_return_if_fail (GTK_IS_FRAMESET (widget));
+  
+  frameset = GTK_FRAMESET (widget);
+  GTK_WIDGET_SET_FLAGS (frameset, GTK_MAPPED);
+  
+  children = frameset->children;
+  while (children)
+    {
+      child = children->data;
+      children = children->next;
+      
+      if (GTK_WIDGET_VISIBLE (child->widget) &&
+	  !GTK_WIDGET_MAPPED (child->widget))
+	gtk_widget_map (child->widget);
+    }
+
+  gdk_window_show(widget->window);
+}
+
+/* standard GTK function */
+static void
+gtk_frameset_unmap (GtkWidget *widget)
+{
+  GtkFrameset *frameset;
+  GtkFramesetChild *child;
+  GList *children;
+  
+  g_return_if_fail (widget != NULL);
+  g_return_if_fail (GTK_IS_FRAMESET (widget));
+  
+  frameset = GTK_FRAMESET (widget);
+  GTK_WIDGET_UNSET_FLAGS (frameset, GTK_MAPPED);
+  
+  children = frameset->children;
+  while (children)
+    {
+      child = children->data;
+      children = children->next;
+      
+      if (GTK_WIDGET_VISIBLE (child->widget) &&
+	  GTK_WIDGET_MAPPED (child->widget))
+	gtk_widget_unmap (child->widget);
+    }
+
+  gdk_window_hide(widget->window);
+}
+
+/* standard GTK function */
+static void
+gtk_frameset_draw (GtkWidget    *widget,
+		   GdkRectangle *area)
+{
+  GtkFrameset *frameset;
+  GtkFramesetChild *child;
+  GList *children;
+  GdkRectangle child_area;
+  
+  g_return_if_fail (widget != NULL);
+  g_return_if_fail (GTK_IS_FRAMESET (widget));
+  
+  if (GTK_WIDGET_VISIBLE (widget) && GTK_WIDGET_MAPPED (widget))
+    {
+      frameset = GTK_FRAMESET (widget);
+      children = frameset->children;
+      while (children)
+	{
+	  child = children->data;
+	  children = children->next;
+	  
+	  if (gtk_widget_intersect (child->widget, area, &child_area))
+	    gtk_widget_draw (child->widget, &child_area);
+	}
+    }
+}
+
+/* standard GTK function */
+static gint
+gtk_frameset_expose (GtkWidget	    *widget,
+		     GdkEventExpose *event)
+{
+  GtkFrameset *frameset;
+  GtkFramesetChild *child;
+  GList *children;
+  GdkEventExpose child_event;
+  
+  g_return_val_if_fail (widget != NULL, FALSE);
+  g_return_val_if_fail (GTK_IS_FRAMESET (widget), FALSE);
+  
+  if (GTK_WIDGET_VISIBLE (widget) && GTK_WIDGET_MAPPED (widget))
+    {
+      frameset = GTK_FRAMESET (widget);
+      
+      child_event = *event;
+      
+      children = frameset->children;
+      while (children)
+	{
+	  child = children->data;
+	  children = children->next;
+	  
+	  if (GTK_WIDGET_NO_WINDOW (child->widget) &&
+	      gtk_widget_intersect (child->widget, &event->area, &child_event.area))
+	    gtk_widget_event (child->widget, (GdkEvent*) &child_event);
+	}
+    }
+  
+  return FALSE;
+}
+
+/* standard GTK function */
+static void
+gtk_frameset_size_allocate (GtkWidget     *widget,
+			    GtkAllocation *allocation)
+{
+  GtkFrameset *frameset;
+  
+  g_return_if_fail (widget != NULL);
+  g_return_if_fail (GTK_IS_FRAMESET (widget));
+  g_return_if_fail (allocation != NULL);
+
+  DEBUG_MSG(DEBUG_EVENT, "gtk_frameset_size_allocate(%d, {w:%d, h:%d, x:%d, y:%d})\n",
+	    (gint) widget, allocation->width, allocation->height, allocation->x, allocation->y);
+  
+  widget->allocation = *allocation;
+  frameset = GTK_FRAMESET (widget);
+
+  if (GTK_WIDGET_REALIZED (widget))
+    gdk_window_move_resize (widget->window,
+			    allocation->x, allocation->y,
+			    allocation->width, allocation->height);
+  
+  gtk_frameset_size_allocate_init (frameset);
+  gtk_frameset_size_allocate_pass1 (frameset);
+}
+
+/*
+ * Calculate child dimensions relative to those of frameset,
+ * based on row and column length specifications
+ */
+void
+gtk_frameset_size_allocate_init(GtkFrameset *frameset)
+{
+  gint n_rows, n_cols;
+  gfloat per_percent;
+  guint real_width, real_height;
+  guint row_borderspace, col_borderspace;
+  guint location;
+
+  /* calculate total amount of border space used */
+  row_borderspace = 0;
+  col_borderspace = 0;
+
+  for(n_rows = 0; n_rows < frameset->nrows - 1; n_rows++)
+    row_borderspace += (frameset->rows[n_rows].border ? GTKFRAMESET_DEFAULT_BORDER_SIZE : 0);
+  for(n_cols = 0; n_cols < frameset->ncols - 1; n_cols++)
+    col_borderspace += (frameset->cols[n_cols].border ? GTKFRAMESET_DEFAULT_BORDER_SIZE : 0);
+
+  real_width =
+    (col_borderspace < GTK_WIDGET(frameset)->allocation.width ?
+     GTK_WIDGET(frameset)->allocation.width - col_borderspace : 0);
+  real_height =
+    (row_borderspace < GTK_WIDGET(frameset)->allocation.height ?
+     GTK_WIDGET(frameset)->allocation.height - row_borderspace : 0);
+
+  location = 0;
+  if(frameset->nrows > 1) {
+    /* calculate number of pixels to dole out per percent */
+    per_percent = (gfloat) (real_height > frameset->row_total_absolute ?
+			    ((real_height - frameset->row_total_absolute) / 100.0) : 0);
+    for(n_rows = 0; n_rows < frameset->nrows; n_rows++) {
+      if(LENGTH_IS_PERCENTAGE(frameset->rows[n_rows].length))
+	frameset->rows[n_rows].allocation =
+	  (gint) (per_percent * 100 * LENGTH_GET_PERCENTAGE(frameset->rows[n_rows].length));
+      else if(LENGTH_IS_ABSOLUTE(frameset->rows[n_rows].length))
+	frameset->rows[n_rows].allocation =
+	  (gint) LENGTH_GET_ABSOLUTE(frameset->rows[n_rows].length);
+      
+      frameset->rows[n_rows].location = location;
+      location += frameset->rows[n_rows].allocation +
+	(frameset->rows[n_rows].border ? GTKFRAMESET_DEFAULT_BORDER_SIZE : 0);
+    }
+  } else {
+    frameset->rows[0].allocation = real_height;
+    frameset->rows[0].location = 0;
+  }
+  
+  location = 0;
+  if(frameset->ncols > 1) {
+    /* calculate number of pixels to dole out per percent */
+    per_percent = (gfloat) (real_width > frameset->col_total_absolute ?
+			    ((real_width - frameset->col_total_absolute) / 100.0) : 0);
+    for(n_cols = 0; n_cols < frameset->ncols; n_cols++) {
+      if(LENGTH_IS_PERCENTAGE(frameset->cols[n_cols].length))
+	frameset->cols[n_cols].allocation =
+	  (gint) (per_percent * 100 * LENGTH_GET_PERCENTAGE(frameset->cols[n_cols].length));
+      else if(LENGTH_IS_ABSOLUTE(frameset->cols[n_cols].length))
+	frameset->cols[n_cols].allocation =
+	  (gint) LENGTH_GET_ABSOLUTE(frameset->cols[n_cols].length);
+      
+      frameset->cols[n_cols].location = location;
+      location += frameset->cols[n_cols].allocation +
+	(frameset->cols[n_cols].border ? GTKFRAMESET_DEFAULT_BORDER_SIZE : 0);
+    }
+  } else {
+    frameset->cols[0].allocation = real_width;
+    frameset->cols[0].location = 0;
+  }
+}
+
+/*
+ * allocate children according to their placement in frameset
+ */
+static void
+gtk_frameset_size_allocate_pass1(GtkFrameset *frameset)
+{
+  GtkFramesetChild *child;
+  GList *children;
+  GtkAllocation *alloc;
+
+  alloc = g_new0(GtkAllocation, 1);
+
+  children = frameset->children;
+  while(children)
+    {
+      child = children->data;
+      children = children->next;
+      
+      if(GTK_WIDGET_VISIBLE(child->widget)) {
+	alloc->width = (frameset->cols[child->col_attach].allocation < (2 * child->xpadding) ?
+			0 : frameset->cols[child->col_attach].allocation - (2 * child->xpadding));
+	
+	alloc->height = (frameset->rows[child->row_attach].allocation < (2 * child->ypadding) ?
+			 0 : frameset->rows[child->row_attach].allocation - (2 * child->ypadding));
+	alloc->x = frameset->cols[child->col_attach].location + child->xpadding;
+	alloc->y = frameset->rows[child->row_attach].location + child->ypadding;
+	gtk_widget_size_allocate(GTK_WIDGET(child->widget), alloc);
+
+	DEBUG_MSG(DEBUG_ALLOC, "widget %d (row %d, col %d) allocated (w:%d, h:%d, x:%d, y:%d)\n",
+		  (gint) child->widget, child->row_attach, child->col_attach,
+		  alloc->width, alloc->height,
+		  frameset->cols[child->col_attach].location,
+		  frameset->rows[child->row_attach].location);
+      }
+    }
+
+  g_free(alloc);
+}
+
+/*
+ * Parse a comma-separated list of %MultiLengths, and returns a GSList
+ * of lenghts. The caller has to free the GSList.
+ */
+static GSList*
+gtk_frameset_get_multi_length (const gchar *attr)
+{
+  GSList *list;
+  gdouble value;
+  gchar *end, *locale;
+  Length length;
+
+  g_return_val_if_fail(attr != NULL, NULL);
+
+  /* Set 'C' locale to avoid parsing problems with float numbers */
+  locale = g_strdup (setlocale (LC_NUMERIC, NULL));
+  setlocale (LC_NUMERIC, "C");
+
+  list = NULL;
+
+  while(TRUE) {
+    value = g_strtod (attr, &end);
+    switch (*end) {
+    case '%':
+      length = LENGTH_CREATE_PERCENTAGE (value / 100);
+      break;
+      
+    case '*':
+      length = LENGTH_CREATE_RELATIVE (value);
+      break;
+      
+    default:
+      length = LENGTH_CREATE_ABSOLUTE ((gint) value);
+      break;
+    }
+
+    list = g_slist_append(list, GINT_TO_POINTER(length));
+
+    /* there MUST be a comma between values */
+    if(!(end = strchr(end, ',')))
+      break;
+    /* valid %MultiLength characters: 0123456789%* */
+    if(!(attr = strpbrk(end, "0123456789%*")))
+      break;
+  }
+
+  setlocale (LC_NUMERIC, locale);
+  g_free (locale);
+
+  return list;
+}
+
+/*
+ * given the current location and allocation values, calculate
+ * row/col length values. The results are used in size_allocate
+ */
+static void
+gtk_frameset_calculate_lengths(GtkFrameset *frameset)
+{
+  gint n;
+  guint total_percentage;
+
+  total_percentage = 0;
+  frameset->col_total_absolute = 0;
+  for(n = 0; n < frameset->ncols; n++)
+    if(LENGTH_IS_ABSOLUTE(frameset->cols[n].length)) {
+      frameset->cols[n].length = LENGTH_CREATE_ABSOLUTE(frameset->cols[n].allocation);
+      frameset->col_total_absolute += frameset->cols[n].allocation;
+    } else
+      total_percentage += frameset->cols[n].allocation;
+
+  for(n = 0; n < frameset->ncols; n++) {
+    if(!(LENGTH_IS_ABSOLUTE(frameset->cols[n].length)))
+      frameset->cols[n].length =
+	LENGTH_CREATE_PERCENTAGE(((gfloat) frameset->cols[n].allocation) / total_percentage);
+  }
+
+  total_percentage = 0;
+  frameset->row_total_absolute = 0;
+  for(n = 0; n < frameset->nrows; n++)
+    if(LENGTH_IS_ABSOLUTE(frameset->rows[n].length)) {
+      frameset->rows[n].length = LENGTH_CREATE_ABSOLUTE(frameset->rows[n].allocation);
+      frameset->row_total_absolute += frameset->rows[n].allocation;
+    } else
+      total_percentage += frameset->rows[n].allocation;
+
+  for(n = 0; n < frameset->nrows; n++) {
+    if(!(LENGTH_IS_ABSOLUTE(frameset->rows[n].length)))
+      frameset->rows[n].length =
+	LENGTH_CREATE_PERCENTAGE(((gfloat) frameset->rows[n].allocation) / total_percentage);
+  }
+}
+
+/*
+ * set the resize_row and resize_col attributes and set resize cursor (if any)
+ */
+static gint
+gtk_frameset_set_resize_rowcol(GtkWidget *widget, gint x, gint y)
+{
+  GtkFrameset *frameset = GTK_FRAMESET(widget);
+  guint n;
+
+  g_return_val_if_fail (widget != NULL,FALSE);
+  g_return_val_if_fail (GTK_IS_FRAMESET (widget),FALSE);
+
+  frameset->resize_row = RESIZE_NONE;
+  frameset->resize_col = RESIZE_NONE;
+
+  for(n=1; n < frameset->ncols; n++) {
+    if(frameset->cols[n - 1].allocation + frameset->cols[n - 1].location - GTKFRAMESET_DEFAULT_BORDER_SIZE <= x &&
+       frameset->cols[n].location + GTKFRAMESET_DEFAULT_BORDER_SIZE >= x &&
+       !frameset->cols[n].noresize) {
+      frameset->resize_col = n - 1;
+      break;
+    }
+  }
+
+  for(n=1; n < frameset->nrows; n++) {
+    if(frameset->rows[n - 1].allocation + frameset->rows[n - 1].location - GTKFRAMESET_DEFAULT_BORDER_SIZE <= y &&
+       frameset->rows[n].location + GTKFRAMESET_DEFAULT_BORDER_SIZE >= y &&
+       !frameset->rows[n].noresize) {
+      frameset->resize_row = n - 1;
+      break;
+    }
+  }
+
+  if((frameset->resize_row != RESIZE_NONE) && (frameset->resize_col != RESIZE_NONE))
+    gdk_window_set_cursor(widget->window, frameset->cursor_rowcol);
+  else if(frameset->resize_row != RESIZE_NONE)
+    gdk_window_set_cursor(widget->window, frameset->cursor_row);
+  else if(frameset->resize_col != RESIZE_NONE)
+    gdk_window_set_cursor(widget->window, frameset->cursor_col);
+  else
+    gdk_window_set_cursor(widget->window, NULL);
+
+  return TRUE;
+}
+
+/*
+ * standard GTK function
+ *
+ * enter drag mode on button_1_press over frame border
+ */
+static gint
+gtk_frameset_button_press (GtkWidget *widget, GdkEventButton *event)
+{
+  GtkFrameset *frameset = GTK_FRAMESET(widget);
+
+  g_return_val_if_fail (widget != NULL,FALSE);
+  g_return_val_if_fail (GTK_IS_FRAMESET (widget),FALSE);
+
+  if(!frameset->in_drag &&
+     (event->button == 1) &&
+     (frameset->resize_row != RESIZE_NONE || frameset->resize_col != RESIZE_NONE))
+    {
+      frameset->in_drag = TRUE;
+      gdk_pointer_grab (widget->window, FALSE,
+			GDK_POINTER_MOTION_HINT_MASK 
+			| GDK_BUTTON1_MOTION_MASK 
+			| GDK_BUTTON_RELEASE_MASK,
+			NULL, NULL, event->time);
+    }
+  
+  return TRUE;
+}
+
+/*
+ * standard GTK function
+ */
+static gint
+gtk_frameset_button_release (GtkWidget *widget, GdkEventButton *event)
+{
+  GtkFrameset *frameset = GTK_FRAMESET(widget);
+
+  g_return_val_if_fail (widget != NULL,FALSE);
+  g_return_val_if_fail (GTK_IS_FRAMESET (widget),FALSE);
+
+  if(frameset->in_drag && (event->button == 1))
+    {
+      frameset->in_drag = FALSE;
+      gdk_pointer_ungrab (event->time);
+      gtk_widget_queue_resize(GTK_WIDGET(frameset));
+    }
+
+  return TRUE;
+}
+
+/*
+ * standard GTK function
+ *
+ * determine row/col to resize (from x, y and noresize attribute),
+ * set appropriate cursor (row resize, col resize or both)
+ */
+static gint
+gtk_frameset_enter (GtkWidget *widget, GdkEventCrossing *event)
+{
+  g_return_val_if_fail (widget != NULL,FALSE);
+  g_return_val_if_fail (GTK_IS_FRAMESET (widget),FALSE);
+
+  return gtk_frameset_set_resize_rowcol(widget, event->x, event->y);
+}
+
+/*
+ * standard GTK function
+ *
+ * unset resize cursor and resize_row/resize_col
+ */
+static gint
+gtk_frameset_leave (GtkWidget *widget, GdkEventCrossing *event)
+{
+  GtkFrameset *frameset = GTK_FRAMESET(widget);
+
+  g_return_val_if_fail (widget != NULL,FALSE);
+  g_return_val_if_fail (GTK_IS_FRAMESET (widget),FALSE);
+
+  frameset->resize_row = RESIZE_NONE;
+  frameset->resize_col = RESIZE_NONE;
+  gdk_window_set_cursor(widget->window, NULL);
+
+  return TRUE;
+}
+
+/*
+ * standard GTK function
+ *
+ * resize row and/or column when in_drag is true
+ *  otherwise
+ * if resize_row or resize_col is set
+ *  set resize_row/resize_col and cursor
+ *
+ * This way, motion events without prior enter events will not set the
+ * cursor
+ */
+static gint
+gtk_frameset_motion (GtkWidget *widget, GdkEventMotion *event)
+{
+  GtkFrameset *frameset = GTK_FRAMESET(widget);
+  guint x, y;
+  gint diff;
+  gboolean return_val;
+
+  g_return_val_if_fail (widget != NULL, FALSE);
+  g_return_val_if_fail (GTK_IS_FRAMESET (widget), FALSE);
+
+  if (event->is_hint)
+    gtk_widget_get_pointer(widget, &x, &y);
+  else {
+    x = event->x;
+    y = event->y;
+  }
+  
+  if (frameset->in_drag)
+    {
+      if(frameset->resize_row >= 0) {
+	diff = (y - frameset->rows[frameset->resize_row].location) -
+	  frameset->rows[frameset->resize_row].allocation;
+	if(frameset->rows[frameset->resize_row].allocation + diff > 0 &&
+	   frameset->rows[frameset->resize_row + 1].allocation - diff > 0) {
+	  frameset->rows[frameset->resize_row].allocation += diff;
+	  frameset->rows[frameset->resize_row + 1].location += diff;
+	  frameset->rows[frameset->resize_row + 1].allocation -= diff;
+	}
+      }
+
+      if(frameset->resize_col >= 0) {
+	diff = (x - frameset->cols[frameset->resize_col].location) -
+	  frameset->cols[frameset->resize_col].allocation;
+	if(frameset->cols[frameset->resize_col].allocation + diff > 0 &&
+	   frameset->cols[frameset->resize_col + 1].allocation - diff > 0) {
+	  frameset->cols[frameset->resize_col].allocation += diff;
+	  frameset->cols[frameset->resize_col + 1].location += diff;
+	  frameset->cols[frameset->resize_col + 1].allocation -= diff;
+	}
+      }
+
+      gtk_frameset_calculate_lengths(frameset);
+      gtk_widget_queue_resize(GTK_WIDGET(frameset));
+      return_val = TRUE;
+    } else
+      /* only adjust resize_row/resize_col if either of these is already set,
+       * this way the cursor will not be changed by motion events without prior
+       * enter event */
+      if(frameset->resize_row != RESIZE_NONE ||
+	 frameset->resize_col != RESIZE_NONE)
+	return_val = gtk_frameset_set_resize_rowcol(widget, x, y);
+      else
+	return_val = TRUE;
+
+  return return_val;
+}
+
diff -pruN dillo/src/gtkframeset.h dillo_tabs/src/gtkframeset.h
--- dillo/src/gtkframeset.h	1970-01-01 01:00:00.000000000 +0100
+++ dillo_tabs/src/gtkframeset.h	2003-11-24 14:32:40.246753928 +0100
@@ -0,0 +1,133 @@
+/*
+ * File: gtkframeset.h
+ * Copyright (C) 2003 Frank de Lange <frank@unternet.org>
+ *
+ *   a frameset widget for GTK - The GIMP Toolkit
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_FRAMESET_H__
+#define __GTK_FRAMESET_H__
+
+#include <gdk/gdk.h>
+#include <gtk/gtkcontainer.h>
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+#define GTK_FRAMESET(obj)	     (GTK_CHECK_CAST ((obj), GTK_TYPE_FRAMESET, GtkFrameset))
+#define GTK_TYPE_FRAMESET	     (gtk_frameset_get_type ())
+#define GTK_FRAMESET_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_FRAMESET, GtkFramesetClass))
+#define GTK_IS_FRAMESET(obj)	     (GTK_CHECK_TYPE ((obj), GTK_TYPE_FRAMESET))
+#define GTK_IS_FRAMESET_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FRAMESET))
+
+
+typedef struct _GtkFrameset	     GtkFrameset;
+typedef struct _GtkFramesetClass     GtkFramesetClass;
+typedef struct _GtkFramesetChild     GtkFramesetChild;
+typedef struct _GtkFramesetRowCol    GtkFramesetRowCol;
+typedef gint32 Length;
+
+/* (adapted from dw_style.h) Lengths */
+#define LENGTH_CREATE_ABSOLUTE(n)     (((n) << 2) | 1)
+#define LENGTH_CREATE_PERCENTAGE(n)   ((LENGTH_FLOAT_TO_REAL (n) << 3) | 2)
+#define LENGTH_CREATE_RELATIVE(n)     ((LENGTH_FLOAT_TO_REAL (n) << 3) | 6)
+#define LENGTH_UNDEF_LENGTH           0
+
+#define LENGTH_IS_ABSOLUTE(l)         ((l) & 1)
+#define LENGTH_IS_PERCENTAGE(l)       (((l) & 7) == 2)
+#define LENGTH_IS_RELATIVE(l)         (((l) & 7) == 6)
+
+#define LENGTH_GET_ABSOLUTE(l)        ((l) >> 2)
+#define LENGTH_GET_PERCENTAGE(l)      LENGTH_REAL_TO_FLOAT ((l) >> 3)
+#define LENGTH_GET_RELATIVE(l)        LENGTH_REAL_TO_FLOAT ((l) >> 3)
+
+#define LENGTH_REAL_TO_FLOAT(v)       ((gfloat)(v) / 0x10000)
+#define LENGTH_FLOAT_TO_REAL(v)       ((gint)((v) * 0x10000))
+
+/* used in frame resize */
+#define RESIZE_NONE                   -1
+
+/* default border size */
+#define GTKFRAMESET_DEFAULT_BORDER_SIZE 2
+
+struct _GtkFrameset
+{
+  GtkContainer container;
+  
+  GList *children;
+  GtkFramesetRowCol *rows;
+  GtkFramesetRowCol *cols;
+  guint row_total_absolute, col_total_absolute;
+  guint16 nrows;
+  guint16 ncols;
+  guint16 current_frame;
+  gboolean in_drag : 1;
+  gint16 resize_row, resize_col;
+  GdkCursor *cursor_rowcol, *cursor_row, *cursor_col;
+};
+
+struct _GtkFramesetClass
+{
+  GtkContainerClass parent_class;
+};
+
+struct _GtkFramesetChild
+{
+  GtkWidget *widget;
+  guint16 row_attach;
+  guint16 col_attach;
+  guint16 xpadding;
+  guint16 ypadding;
+  gboolean border : 1;
+  gboolean noresize : 1;
+};
+
+struct _GtkFramesetRowCol
+{
+  guint16 location;
+  guint16 allocation;
+  gboolean noresize : 1;
+  gboolean border: 1;
+  Length length;
+  guint16 spacing;
+};
+
+
+GtkType	   gtk_frameset_get_type        (void);
+GtkWidget* gtk_frameset_new	        (gchar	       *rows,
+					 gchar	       *columns);
+void       gtk_frameset_resize          (GtkFrameset   *frameset,
+					 gchar	       *rows,
+					 gchar	       *columns);
+void	   gtk_frameset_attach	        (GtkFrameset   *frameset,
+					 GtkWidget     *child,
+					 guint		row_attach,
+					 guint		col_attach,
+					 guint          xpadding,
+					 guint          ypadding,
+					 gboolean       border,
+					 gboolean       noresize);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __GTK_FRAMESET_H__ */
diff -pruN dillo/src/html.c dillo_tabs/src/html.c
--- dillo/src/html.c	2003-11-24 14:32:40.111774448 +0100
+++ dillo_tabs/src/html.c	2003-11-24 14:32:40.221757728 +0100
@@ -30,7 +30,9 @@
 #include "dillo.h"
 #include "history.h"
 #include "nav.h"
+#include "doc.h"
 #include "menu.h"
+#include "pagemark.h"
 #include "commands.h"
 #include "dw.h"         /* for Dw_cursor_hand */
 
@@ -48,13 +50,18 @@
 #include "IO/IO.h"
 #include "IO/Url.h"
 #include "interface.h"
-#include "progressbar.h"
 #include "prefs.h"
 #include "misc.h"
 #include "capi.h"
 #include "html.h"
 
-#define DEBUG_LEVEL 10
+#include "gtkframeset.h"
+
+#define DEBUG_EVENT 10
+#define DEBUG_SIZE  10
+#define DEBUG_ALLOC 10
+
+/* #define DEBUG_LEVEL 10 */
 #include "debug.h"
 
 typedef void (*TagFunct) (DilloHtml *Html, char *Tag, gint Tagsize);
@@ -79,7 +86,7 @@ static void Html_add_widget(DilloHtml *h
 static void Html_write(DilloHtml *html, char *Buf, gint BufSize, gint Eof);
 static void Html_close(DilloHtml *html, gint ClientKey);
 static void Html_callback(int Op, CacheClient_t *Client);
-static DilloHtml *Html_new(BrowserWindow *bw, const DilloUrl *url);
+static DilloHtml *Html_new(DilloDoc *dd, const DilloUrl *url);
 static void Html_tag_open_input(DilloHtml *html, char *tag, gint tagsize);
 static void Html_add_input(DilloHtmlForm *form,
                            DilloHtmlInputType type,
@@ -94,7 +101,6 @@ static void Html_reset_form(GtkWidget *r
 static gchar* Html_tags_get_name(gint tag_idx);
 static gchar Html_tags_get_endtag(gint tag_idx);
 
-
 /*
  * Local Data
  */
@@ -104,7 +110,6 @@ static const gint FontSizes[] = {8, 10, 
 static const gint FontSizesNum = 6;
 static const gint FontSizesBase = 2;
 
-
 /*
  * Return the line number of the tag being processed by the parser.
  */
@@ -168,7 +173,7 @@ DwWidget *a_Html_text(const char *Type, 
                       void **Data)
 {
    DilloWeb *web = P;
-   DilloHtml *html = Html_new(web->bw, web->url);
+   DilloHtml *html = Html_new(web->dd, web->url);
 
    *Data = (void *) html;
    *Call = (CA_Callback_t) Html_callback;
@@ -180,11 +185,11 @@ DwWidget *a_Html_text(const char *Type, 
 /*
  * We'll make the linkblock first to get it out of the way.
  */
-static DilloHtmlLB *Html_lb_new(BrowserWindow *bw, const DilloUrl *url)
+static DilloHtmlLB *Html_lb_new(DilloDoc *dd, const DilloUrl *url)
 {
    DilloHtmlLB *html_lb = g_new(DilloHtmlLB, 1);
 
-   html_lb->bw = bw;
+   html_lb->dd = dd;
    html_lb->base_url = a_Url_dup(url);
    html_lb->num_forms_max = 1;
    html_lb->num_forms = 0;
@@ -253,7 +258,7 @@ static void Html_set_link_coordinates(Di
 {
    gchar data[64];
 
-   if (x != -1) {
+   if (x > -1) {
       g_snprintf(data, 64, "?%d,%d", x, y);
       a_Url_set_ismap_coords(lb->links[link], data);
    }
@@ -262,6 +267,8 @@ static void Html_set_link_coordinates(Di
 /*
  * Handle the status function generated by the dw scroller,
  * and show the url in the browser status-bar.
+ * Does not (re)set cursor if link x coordinate is -2
+ * (used by keyboard navigation)
  */
 static void Html_handle_status(DwWidget *widget, gint link, gint x, gint y,
                                DilloHtmlLB *lb)
@@ -271,15 +278,17 @@ static void Html_handle_status(DwWidget 
    url = (link == -1) ? NULL : lb->links[link];
    if (url) {
       Html_set_link_coordinates(lb, link, x, y);
-      a_Interface_msg(lb->bw, "%s",
-                         URL_ALT_(url) ? URL_ALT_(url) : URL_STR_(url));
-      a_Dw_widget_set_cursor (widget, Dw_cursor_hand);
-      lb->bw->status_is_link = 1;
+      a_Interface_msg(lb->dd->bw, "%s",
+		      URL_ALT_(url) ? URL_ALT_(url) : URL_STR_(url));
+      if(x != -2)
+	a_Dw_widget_set_cursor (widget, Dw_cursor_hand);
+      lb->dd->bw->status_is_link = 1;
 
    } else {
-      if (lb->bw->status_is_link)
-         a_Interface_msg(lb->bw, "");
-      a_Dw_widget_set_cursor (widget, NULL);
+      if (lb->dd->bw->status_is_link)
+         a_Interface_msg(lb->dd->bw, "");
+      if(x != -2)
+	a_Dw_widget_set_cursor (widget, NULL);
    }
 }
 
@@ -287,38 +296,20 @@ static void Html_handle_status(DwWidget 
  * Popup the link menu ("link_pressed" callback of the page)
  */
 static gboolean Html_link_menu(DwWidget *widget, gint link, gint x, gint y,
-                               GdkEventButton *event, DilloHtmlLB *lb)
+			       GdkEventButton *event, DilloHtmlLB *lb)
 {
-   DwWidget *widget_at_cursor;
-   gboolean show_oi = FALSE;
+   gboolean return_val;
+
+   return_val = FALSE;
 
    if (event->button == 3) {
       Html_set_link_coordinates(lb, link, x, y);
-      a_Menu_popup_set_url(lb->bw, lb->links[link]);
-
-      /* if we've got an image, prepare the image popup */
-      widget_at_cursor =
-         a_Dw_gtk_scrolled_window_widget_at_viewport_point(
-            GTK_DW_SCROLLED_WINDOW (lb->bw->docwin), event->x, event->y);
-      if (widget_at_cursor && DW_IS_IMAGE (widget_at_cursor)) {
-         DwImage *image = DW_IMAGE (widget_at_cursor);
-         /* test image->url (it may have not started to arrive yet!) */
-         if (image->url) {
-            /* use the second URL for this popup */
-            gtk_object_set_data(GTK_OBJECT (lb->bw->menu_popup.over_image),
-                                "url2", GINT_TO_POINTER(2));
-            a_Menu_popup_set_url2(lb->bw, image->url);
-            show_oi = TRUE;
-         }
-      }
-      a_Menu_popup_ol_show_oi(lb->bw, show_oi);
-
-      gtk_menu_popup(GTK_MENU(lb->bw->menu_popup.over_link), NULL, NULL,
-                     NULL, NULL, event->button, event->time);
-      return TRUE;
+      a_Menu_popup_set_url(lb->dd, lb->links[link]);
+      a_Menu_popup_over_link(lb->dd, event);
+      return_val = TRUE;
    }
 
-   return FALSE;
+   return return_val;
 }
 
 
@@ -326,59 +317,96 @@ static gboolean Html_link_menu(DwWidget 
  * Activate a link ("link_clicked" callback of the page)
  */
 static gboolean Html_link_clicked(DwWidget *widget, gint link, gint x, gint y,
-                                  GdkEventButton *event, DilloHtmlLB *lb)
+                              GdkEventButton *event, DilloHtmlLB *lb)
 {
+   DilloDoc *named_dd;
+   gchar *target;
+   gboolean return_val;
+
+   return_val = FALSE;
+
    Html_set_link_coordinates(lb, link, x, y);
-   if (event->button == 1)
-      a_Nav_push(lb->bw, lb->links[link]);
-   else if (event->button == 2) {
-      a_Nav_push_nw(lb->bw, lb->links[link]);
-   } else {
-      return FALSE;
+
+   switch (event->button) {
+   case 1:
+     if (event->state & GDK_SHIFT_MASK) {
+       /* shift-click: open in new window */
+       a_Menu_popup_set_url(lb->dd, lb->links[link]);
+       a_Commands_open_link_nw_callback(NULL, lb->dd->bw);
+     } else if (event->state & GDK_CONTROL_MASK) {
+       /* control-click: open in new tab */
+       a_Menu_popup_set_url(lb->dd, lb->links[link]);
+       a_Commands_open_link_nw_tab_callback(NULL, lb->dd->bw);
+     } else {
+       if((target = (gchar *) URL_TARGET_(lb->links[link])))
+	 /* targeted link or base target, open in either existing
+	  * named document or new document */
+	 if ((named_dd = a_Doc_get_by_name(lb->dd, (gchar *) target)))
+	   a_Nav_push(named_dd, lb->links[link]);
+	 else {
+	   a_Menu_popup_set_url(lb->dd, lb->links[link]);
+	   a_Commands_open_link_nw_callback(NULL, lb->dd->bw);
+	 }
+       else
+	 a_Nav_push(lb->dd, lb->links[link]);
+     }
+     return_val = TRUE;
+     break;
+   case 2:
+     a_Menu_popup_set_url(lb->dd, lb->links[link]);
+       if(prefs.target_blank == TARGET_TAB)
+       a_Commands_open_link_nw_tab_callback(NULL, lb->dd->bw);
+     else
+       a_Commands_open_link_nw_callback(NULL, lb->dd->bw);
+     return_val = TRUE;
+     break;
+   default:
+     break;
    }
 
    if (DW_IS_PAGE (widget))
       a_Dw_page_change_link_color (DW_PAGE (widget), link, lb->visited_color);
 
-   return TRUE;
+   return return_val;
 }
 
 /*
  * Popup the image menu ("button_press_event" callback of image)
  */
-static gboolean Html_image_menu(DwWidget *widget,
-                                gint32 x, gint32 y, GdkEventButton *event,
-                                BrowserWindow *bw)
+static gboolean Html_image_menu(GtkWidget *widget,
+				gint32 x, gint32 y, GdkEventButton *event,
+				DilloDoc *dd)
 {
    DwImage *image = DW_IMAGE (widget);
+   gboolean return_val;
+
+   return_val = FALSE;
+
    if (event->button == 3 && image->url) {
-      a_Menu_popup_set_url(bw, image->url);
-      gtk_menu_popup(GTK_MENU(bw->menu_popup.over_image), NULL, NULL,
-                     NULL, NULL, event->button, event->time);
+      a_Menu_popup_set_url(dd, image->url);
+      a_Menu_popup_over_image(dd, event);
       /* If the signal is not stopped, Dw_image_button_press will be called,
        * and return FALSE, so that the "button_press_event" signal is regarded
        * as not processed. The fact that here, TRUE is returned, does not seem
        * to play a role!? */
       gtk_signal_emit_stop_by_name (GTK_OBJECT (image), "button_press_event");
-      return TRUE;
+      return_val = TRUE;
    }
 
-   return FALSE;
+   return return_val;
 }
 
 /*
  * Popup the page menu ("button_press_event" callback of the viewport)
  */
 static int Html_page_menu(GtkWidget *viewport, GdkEventButton *event,
-                          BrowserWindow *bw)
+                          DilloDoc *dd)
 {
-   if (event->button == 3) {
-      a_Menu_popup_set_url(bw, a_History_get_url(NAV_TOP(bw)));
-      gtk_menu_popup(GTK_MENU(bw->menu_popup.over_page), NULL, NULL,
-                     NULL, NULL, event->button, event->time);
-      return TRUE;
-   } else
-      return FALSE;
+  if (event->button == 3) {
+    a_Menu_popup_over_page(dd, event);
+    return TRUE;
+  } else
+    return FALSE;
 }
 
 /*
@@ -452,7 +480,7 @@ static gint Html_form_new(DilloHtmlLB *h
       style_attrs = *old_style; \
       style_attrs.var = (val); \
       (html)->stack[(html)->stack_top].style = \
-         a_Dw_style_new (&style_attrs, (html)->bw->main_window->window); \
+         a_Dw_style_new (&style_attrs, (html)->dd->bw->main_window->window); \
       a_Dw_style_unref (old_style); \
    } while (FALSE)
 
@@ -563,13 +591,13 @@ static void Html_add_indented_widget(Dil
    a_Dw_style_box_set_border_color
       (&style_attrs,
        a_Dw_style_shaded_color_new(style_attrs.color->color_val,
-                                   html->bw->main_window->window));
+                                   html->dd->bw->main_window->window));
    a_Dw_style_box_set_border_style(&style_attrs, DW_STYLE_BORDER_DASHED);
 #endif
 
    style_attrs.margin.left = left;
    style_attrs.margin.right = right;
-   style = a_Dw_style_new (&style_attrs, html->bw->main_window->window);
+   style = a_Dw_style_new (&style_attrs, html->dd->bw->main_window->window);
 
    a_Dw_page_add_parbreak (DW_PAGE (html->dw), space, style);
    a_Dw_page_add_widget (DW_PAGE (html->dw), page, style);
@@ -645,12 +673,12 @@ static void Html_set_dwpage(DilloHtml *h
    font.weight = 400;
    font.style = DW_STYLE_FONT_STYLE_NORMAL;
 
-   a_Dw_style_init_values (&style_attrs, html->bw->main_window->window);
+   a_Dw_style_init_values (&style_attrs, html->dd->bw->main_window->window);
    style_attrs.font = a_Dw_style_font_new (&font);
    style_attrs.color = a_Dw_style_color_new (prefs.text_color,
-                                             html->bw->main_window->window);
+                                             html->dd->bw->main_window->window);
    html->stack[0].style = a_Dw_style_new (&style_attrs,
-                                          html->bw->main_window->window);
+                                          html->dd->bw->main_window->window);
 
    html->stack[0].table_cell_style = NULL;
 
@@ -658,8 +686,8 @@ static void Html_set_dwpage(DilloHtml *h
    Html_connect_signals(html, GTK_OBJECT(widget));
 
    gtk_signal_connect_while_alive (
-      GTK_OBJECT(GTK_BIN(html->bw->docwin)->child), "button_press_event",
-      GTK_SIGNAL_FUNC(Html_page_menu), (gpointer)html->bw, GTK_OBJECT (page));
+      GTK_OBJECT(GTK_BIN(html->dd->docwin)->child), "button_press_event",
+      GTK_SIGNAL_FUNC(Html_page_menu), (gpointer)html->dd, GTK_OBJECT (page));
 
    /* Destroy the linkblock when the DwPage is destroyed */
    gtk_signal_connect_object(GTK_OBJECT(page), "destroy",
@@ -670,7 +698,7 @@ static void Html_set_dwpage(DilloHtml *h
 /*
  * Create and initialize a new DilloHtml structure
  */
-static DilloHtml *Html_new(BrowserWindow *bw, const DilloUrl *url)
+static DilloHtml *Html_new(DilloDoc *dd, const DilloUrl *url)
 {
    DilloHtml *html;
 
@@ -684,8 +712,8 @@ static DilloHtml *Html_new(BrowserWindow
    html->OldTagLine = 1;
 
    html->dw = NULL;
-   html->bw = bw;
-   html->linkblock = Html_lb_new(bw, url);
+   html->dd = dd;
+   html->linkblock = Html_lb_new(dd, url);
 
    html->stack_max = 16;
    html->stack_top = 0;
@@ -693,6 +721,8 @@ static DilloHtml *Html_new(BrowserWindow
    html->stack[0].tag = g_strdup("none");
    html->stack[0].parse_mode = DILLO_HTML_PARSE_MODE_INIT;
    html->stack[0].table_mode = DILLO_HTML_TABLE_MODE_NONE;
+   html->stack[0].frame_mode = DILLO_HTML_FRAME_MODE_NONE;
+   html->stack[0].frameset = NULL;
    html->stack[0].cell_text_align_set = FALSE;
    html->stack[0].list_type = HTML_LIST_NONE; /* no <ul> or <ol> open */
    html->stack[0].list_number = 0;
@@ -1347,8 +1377,8 @@ static void Html_tag_close_title(DilloHt
 {
    if (html->InFlags & IN_HEAD) {
       /* title is only valid inside HEAD */
-      a_Interface_set_page_title(html->linkblock->bw, html->Stash->str);
-      a_History_set_title(NAV_TOP(html->linkblock->bw), html->Stash->str);
+      a_Doc_title_set(html->linkblock->dd, html->Stash->str);
+      a_History_set_title(NAV_TOP(html->linkblock->dd), html->Stash->str);
    } else {
       MSG_HTML("the TITLE element must be inside the HEAD section\n");
    }
@@ -1407,6 +1437,17 @@ static void Html_tag_open_body(DilloHtml
    DwStyle style_attrs, *style;
    gint32 color;
 
+   /* if document contains frameset, ignore body */
+   if(html->InFlags & IN_FRAMESET)
+     return;
+   
+   /* in body, so hide/remove frameset and show docwin */
+   if(html->dd->frameset && GTK_IS_WIDGET(html->dd->frameset)) {
+     gtk_widget_destroy(html->dd->frameset);
+     html->dd->frameset = NULL;
+     gtk_widget_show(GTK_WIDGET(html->dd->docwin));
+   }
+
    page = DW_PAGE (html->dw);
 
    /* HEAD to BODY section transition was done by Html_test_body_section() */
@@ -1420,8 +1461,8 @@ static void Html_tag_open_body(DilloHtml
 
          style_attrs = *html->dw->style;
          style_attrs.background_color =
-            a_Dw_style_color_new (color, html->bw->main_window->window);
-         style = a_Dw_style_new (&style_attrs, html->bw->main_window->window);
+            a_Dw_style_color_new (color, html->dd->bw->main_window->window);
+         style = a_Dw_style_new (&style_attrs, html->dd->bw->main_window->window);
          a_Dw_widget_set_style (html->dw, style);
          a_Dw_style_unref (style);
          html->stack[html->stack_top].current_bg_color = color;
@@ -1431,7 +1472,7 @@ static void Html_tag_open_body(DilloHtml
          color = Html_color_parse(html, attrbuf, prefs.text_color);
          HTML_SET_TOP_ATTR
             (html, color,
-             a_Dw_style_color_new (color, html->bw->main_window->window));
+             a_Dw_style_color_new (color, html->dd->bw->main_window->window));
       }
 
       if ((attrbuf = Html_get_attr(html, tag, tagsize, "link")))
@@ -1505,7 +1546,7 @@ static void Html_tag_open_table(DilloHtm
       (&style_attrs,
        a_Dw_style_shaded_color_new (
           html->stack[html->stack_top].current_bg_color,
-          html->bw->main_window->window));
+          html->dd->bw->main_window->window));
    a_Dw_style_box_set_border_style (&style_attrs, DW_STYLE_BORDER_OUTSET);
    style_attrs.border_spacing = cellspacing;
 
@@ -1529,11 +1570,11 @@ static void Html_tag_open_table(DilloHtm
             bgcolor = prefs.bg_color;
          html->stack[html->stack_top].current_bg_color = bgcolor;
          style_attrs.background_color =
-            a_Dw_style_color_new (bgcolor, html->bw->main_window->window);
+            a_Dw_style_color_new (bgcolor, html->dd->bw->main_window->window);
       }
    }
 
-   tstyle = a_Dw_style_new (&style_attrs, html->bw->main_window->window);
+   tstyle = a_Dw_style_new (&style_attrs, html->dd->bw->main_window->window);
 
    /* The style for the cells */
    style_attrs = *html->stack[html->stack_top].style;
@@ -1550,7 +1591,7 @@ static void Html_tag_open_table(DilloHtm
 
    old_style = html->stack[html->stack_top].table_cell_style;
    html->stack[html->stack_top].table_cell_style =
-      a_Dw_style_new (&style_attrs, html->bw->main_window->window);
+      a_Dw_style_new (&style_attrs, html->dd->bw->main_window->window);
    if (old_style)
       a_Dw_style_unref (old_style);
 
@@ -1607,7 +1648,7 @@ static void Html_tag_open_table_cell(Dil
          style_attrs.white_space = DW_STYLE_WHITE_SPACE_NORMAL;
 
       html->stack[html->stack_top].style =
-         a_Dw_style_new (&style_attrs, html->bw->main_window->window);
+         a_Dw_style_new (&style_attrs, html->dd->bw->main_window->window);
       a_Dw_style_unref (old_style);
       Html_tag_set_align_attr (html, tag, tagsize);
 
@@ -1632,7 +1673,7 @@ static void Html_tag_open_table_cell(Dil
 
             new_style = TRUE;
             style_attrs.background_color =
-               a_Dw_style_color_new (bgcolor, html->bw->main_window->window);
+               a_Dw_style_color_new (bgcolor, html->dd->bw->main_window->window);
             html->stack[html->stack_top].current_bg_color = bgcolor;
          }
       }
@@ -1646,7 +1687,7 @@ static void Html_tag_open_table_cell(Dil
          col_page = a_Dw_page_new ();
 
       if (new_style) {
-         style = a_Dw_style_new (&style_attrs, html->bw->main_window->window);
+         style = a_Dw_style_new (&style_attrs, html->dd->bw->main_window->window);
          a_Dw_widget_set_style (col_page, style);
          a_Dw_style_unref (style);
       } else
@@ -1736,9 +1777,9 @@ static void Html_tag_open_tr(DilloHtml *
 
             style_attrs = *html->stack[html->stack_top].style;
             style_attrs.background_color =
-               a_Dw_style_color_new (bgcolor, html->bw->main_window->window);
+               a_Dw_style_color_new (bgcolor, html->dd->bw->main_window->window);
             style =
-               a_Dw_style_new (&style_attrs, html->bw->main_window->window);
+               a_Dw_style_new (&style_attrs, html->dd->bw->main_window->window);
             html->stack[html->stack_top].current_bg_color = bgcolor;
          }
       }
@@ -1757,7 +1798,7 @@ static void Html_tag_open_tr(DilloHtml *
       if (Html_tag_set_valign_attr (html, tag, tagsize, &style_attrs)) {
          old_style = html->stack[html->stack_top].table_cell_style;
          html->stack[html->stack_top].table_cell_style =
-            a_Dw_style_new (&style_attrs, html->bw->main_window->window);
+            a_Dw_style_new (&style_attrs, html->dd->bw->main_window->window);
          a_Dw_style_unref (old_style);
       } else
 
@@ -1775,86 +1816,278 @@ static void Html_tag_open_tr(DilloHtml *
 #endif
 }
 
-/*
- * <FRAME>, <IFRAME>
- * todo: This is just a temporary fix while real frame support
- *       isn't finished. Imitates lynx/w3m's frames.
+/* 
+ * <IFRAME>
  */
-static void Html_tag_open_frame (DilloHtml *html, gchar *tag, gint tagsize)
+static void Html_tag_open_iframe (DilloHtml *html, gchar *tag, gint tagsize)
 {
    const char *attrbuf;
-   gchar *src;
    DilloUrl *url;
+   GtkWidget *box;
+   DwWidget *embed;
+   DilloDoc *dd;
    DwPage *page;
-   DwStyle style_attrs, *link_style;
-   DwWidget *bullet;
-   gint dummy;
+   //   DwStyle style_attrs, *estyle, *fstyle; /* estyle is for embedding widget, fstyle for iframe */
+   DwStyle style_attrs, *estyle;
+   gchar *width_ptr, *height_ptr;
+   gboolean frameborder;
+   guint marginwidth, marginheight;
+   GtkPolicyType scrolling;
 
-   page = DW_PAGE(html->dw);
+   Html_cleanup_tag(html, "p>");
+   Html_cleanup_tag(html, "frame>");
+
+   Html_push_tag(html, tag, tagsize);
+   Html_tag_set_align_attr(html, tag, tagsize);
+   /* ignore the rest of the text inside the tag
+    *(it is the equivalent of a <noframes> block) */
+   html->stack[html->stack_top].parse_mode = DILLO_HTML_PARSE_MODE_VERBATIM;
+   html->stack[html->stack_top].frame_mode = DILLO_HTML_FRAME_MODE_IFRAME;
 
+   /* no link == return */
    if ( !(attrbuf = Html_get_attr(html, tag, tagsize, "src")) )
-      return;
+     return;
+   if (!(url = Html_url_new(html, attrbuf, NULL, 0, 0, 0)))
+     return;
+   
+   page = DW_PAGE (html->dw);
+   style_attrs = *html->stack[html->stack_top].style;
+
+   /* use default for width (300) and height (150) if not specified */
+   if ((attrbuf = Html_get_attr(html, tag, tagsize, "width")))
+     width_ptr = g_strdup(attrbuf);
+   else
+     width_ptr = g_strdup("300");
+
+   if ((attrbuf = Html_get_attr(html, tag, tagsize, "height")))
+     height_ptr = g_strdup(attrbuf);
+   else
+     height_ptr = g_strdup("150");
+
+   if ((attrbuf = Html_get_attr(html, tag, tagsize, "frameborder")))
+     frameborder = (strtol (attrbuf, NULL, 10) == 0 ? FALSE : TRUE);
+   else
+     frameborder = TRUE;
+
+   if ((attrbuf = Html_get_attr(html, tag, tagsize, "marginwidth")))
+     marginwidth = strtol (attrbuf, NULL, 10);
+   else
+     marginwidth = 0;
+
+   if ((attrbuf = Html_get_attr(html, tag, tagsize, "marginheight")))
+     marginheight = strtol (attrbuf, NULL, 10);
+   else
+     marginheight = 0;
+
+   if ((attrbuf = Html_get_attr(html, tag, tagsize, "scrolling")))
+     scrolling = (!g_strcasecmp(attrbuf, "no") ? GTK_POLICY_NEVER :
+		  (!g_strcasecmp(attrbuf, "yes") ? GTK_POLICY_ALWAYS : GTK_POLICY_AUTOMATIC));
+   else
+     scrolling = GTK_POLICY_AUTOMATIC;
+     
+   dd = a_Doc_new();
+   a_Doc_set_parent(dd, (html)->dd);
+
+   /* set margins */
+   (dd->style)->margin.left = (dd->style)->margin.right = marginwidth;
+   (dd->style)->margin.top = (dd->style)->margin.bottom = marginheight;
+
+   box = gtk_hbox_new(TRUE, 0);
+   gtk_container_set_border_width(GTK_CONTAINER(dd->docwin), 0);
+   gtk_container_set_border_width(GTK_CONTAINER(box), 0);
+   gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(dd->docwin), scrolling, scrolling);
+   gtk_box_pack_start(GTK_BOX(box), GTK_WIDGET(dd->docwin), TRUE, TRUE, 0);
+   gtk_widget_show(GTK_WIDGET(box));
+
+   /* style for the embedding widget */
+   a_Dw_style_box_set_border_style(&style_attrs, DW_STYLE_BORDER_NONE);
+   a_Dw_style_box_set_val(&style_attrs.margin, 0);
+   a_Dw_style_box_set_val(&style_attrs.border_width, 0);
+   estyle = a_Dw_style_new (&style_attrs, html->dd->bw->main_window->window);
+
+   embed = a_Dw_embed_gtk_new();
+   a_Dw_embed_gtk_add_gtk(DW_EMBED_GTK(embed), GTK_WIDGET(box));
+   Html_add_widget(html, (DwWidget *) embed, width_ptr, height_ptr, estyle);
+   
+   a_Dw_style_unref(estyle);
+   g_free(width_ptr);
+   g_free(height_ptr);
+
+   /* set iframe name if specified */
+   if((attrbuf = Html_get_attr(html, tag, tagsize, "name")))
+     a_Doc_set_name(dd, (gchar *) attrbuf);
+
+   a_Nav_push(dd, url);
+}
 
+/*
+ * <FRAME>
+ */
+static void Html_tag_open_frame (DilloHtml *html, gchar *tag, gint tagsize)
+{
+   const char *attrbuf;
+   DilloUrl *url;
+   GtkWidget *box;
+   DilloDoc *dd;
+   gboolean frameborder, noresize;
+   guint marginwidth, marginheight;
+   GtkPolicyType scrolling;
+
+   Html_cleanup_tag(html, "p>");
+   Html_cleanup_tag(html, "frame>");
+
+   /* no link == return */
+   if ( !(attrbuf = Html_get_attr(html, tag, tagsize, "src")) )
+     return;
+   
    if (!(url = Html_url_new(html, attrbuf, NULL, 0, 0, 0)))
-      return;
+     return;
 
-   src = g_strdup(attrbuf);
+   if ((attrbuf = Html_get_attr(html, tag, tagsize, "frameborder")))
+     frameborder = (strtol (attrbuf, NULL, 10) == 0 ? FALSE : TRUE);
+   else
+     frameborder = TRUE;
 
-   style_attrs = *(html->stack[html->stack_top].style);
+   if ((attrbuf = Html_get_attr(html, tag, tagsize, "noresize")))
+     noresize = TRUE;
+   else
+     noresize = FALSE;
 
-   if (a_Capi_url_read(url, &dummy))  /* visited frame */
-      style_attrs.color = a_Dw_style_color_new
-         (html->linkblock->visited_color, html->bw->main_window->window);
-   else                                /* unvisited frame */
-      style_attrs.color = a_Dw_style_color_new
-         (html->linkblock->link_color,  html->bw->main_window->window);
+   /* if margins have not been set explicitly, use defaults */
+   if ((attrbuf = Html_get_attr(html, tag, tagsize, "marginwidth")))
+     marginwidth = strtol (attrbuf, NULL, 10);
+   else
+     marginwidth = DOC_DEFAULT_MARGIN;
 
-   style_attrs.text_decoration |= DW_STYLE_TEXT_DECORATION_UNDERLINE;
-   style_attrs.x_link = Html_set_new_link(html, &url);
-   link_style = a_Dw_style_new (&style_attrs,
-                                html->bw->main_window->window);
-
-   a_Dw_page_add_parbreak(page, 5, html->stack[(html)->stack_top].style);
-
-   /* The bullet will be assigned the current list style, which should
-    * be "disc" by default, but may in very weird pages be different.
-    * Anyway, there should be no harm. */
-   bullet = a_Dw_bullet_new();
-   a_Dw_page_add_widget(page, bullet, html->stack[html->stack_top].style);
-   a_Dw_page_add_space(page, html->stack[html->stack_top].style);
-
-   if (tolower(tag[1]) == 'i') {
-      /* IFRAME usually comes with very long advertising/spying URLS,
-       * to not break rendering we will force name="IFRAME" */
-      a_Dw_page_add_text(page, g_strdup("IFRAME"), link_style);
+   if ((attrbuf = Html_get_attr(html, tag, tagsize, "marginheight")))
+     marginheight = strtol (attrbuf, NULL, 10);
+   else
+     marginheight = DOC_DEFAULT_MARGIN;
 
-   } else {
-      /* FRAME:
-       * If 'name' tag is present use it, if not use 'src' value */
-      if ( !(attrbuf = Html_get_attr(html, tag, tagsize, "name")) ) {
-         a_Dw_page_add_text(page, g_strdup(src), link_style);
-      } else {
-         a_Dw_page_add_text(page, g_strdup(attrbuf), link_style);
-      }
-   }
+   if ((attrbuf = Html_get_attr(html, tag, tagsize, "scrolling")))
+     scrolling = (!g_strcasecmp(attrbuf, "no") ? GTK_POLICY_NEVER :
+		  (!g_strcasecmp(attrbuf, "yes") ? GTK_POLICY_ALWAYS : GTK_POLICY_AUTOMATIC));
+   else
+     scrolling = GTK_POLICY_AUTOMATIC;
 
-   a_Dw_page_add_parbreak(page, 5, html->stack[(html)->stack_top].style);
+   DEBUG_MSG(DEBUG_EVENT, "      URL %s\n", attrbuf);
 
-   a_Dw_style_unref(link_style);
-   g_free(src);
+   switch(html->stack[html->stack_top].frame_mode) {
+   case DILLO_HTML_FRAME_MODE_NONE:
+     MSG_HTML("<frame> outside of <frameset>\n");
+     return;
+   case DILLO_HTML_FRAME_MODE_NOFRAMES:
+     MSG_HTML("<frame> inside of <noframes>\n");
+     return;
+   case DILLO_HTML_FRAME_MODE_IFRAME:
+     MSG_HTML("<frame> inside of <iframe>\n");
+     return;
+   case DILLO_HTML_FRAME_MODE_FRAMESET:
+     dd = a_Doc_new();
+     a_Doc_set_parent(dd, (html)->dd);
+
+     /* set marginwidth & height */
+     (dd->style)->margin.left = (dd->style)->margin.right = marginwidth;
+     (dd->style)->margin.top = (dd->style)->margin.bottom = marginheight;
+
+     box = gtk_hbox_new(TRUE, 0);
+     gtk_box_pack_start(GTK_BOX(box), GTK_WIDGET(dd->docwin), TRUE, TRUE, 0);
+     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(dd->docwin), scrolling, scrolling);
+     gtk_container_set_border_width(GTK_CONTAINER(dd->docwin), 0);
+     gtk_container_set_border_width(GTK_CONTAINER(box), 0);
+     gtk_widget_show(GTK_WIDGET(box));
+     gtk_container_add_with_args(GTK_CONTAINER(html->stack[html->stack_top].frameset),
+				 GTK_WIDGET(box),
+				 "GtkFrameset::x_padding", 0,
+				 "GtkFrameset::y_padding", 0,
+				 "GtkFrameset::noresize", noresize,
+				 "GtkFrameset::border", frameborder,
+				 NULL);
+     a_Nav_push(dd, url);
+
+     /* set frame name if specified */
+     if((attrbuf = Html_get_attr(html, tag, tagsize, "name")))
+       a_Doc_set_name(dd, (gchar *) attrbuf);
+     break;
+     
+   default:
+     return;
+   }
 }
 
 /*
  * <FRAMESET>
- * todo: This is just a temporary fix while real frame support
- *       isn't finished. Imitates lynx/w3m's frames.
  */
 static void Html_tag_open_frameset (DilloHtml *html, gchar *tag, gint tagsize)
 {
+   const char *attrbuf;
+   GtkWidget *frameset;
+   gchar *rows, *cols;
+
+   /* (older versions of) compiler happiness */
+   frameset = NULL;
+
+   /* first, see if this frameset tag is in the right place... */
+   if(html->stack[html->stack_top].parse_mode != DILLO_HTML_PARSE_MODE_INIT) {
+     MSG_HTML("incorrectly placed <frameset>\n");
+     return;
+   }
+
+   /* get frameset attributes */
+   if ((attrbuf = Html_get_attr(html, tag, tagsize, "rows")))
+     rows = g_strdup(attrbuf);
+   else
+     rows = NULL;
+   if ((attrbuf = Html_get_attr(html, tag, tagsize, "cols")))
+     cols = g_strdup(attrbuf);
+   else
+     cols = NULL;
+
+   switch(html->stack[html->stack_top].frame_mode) {
+   case DILLO_HTML_FRAME_MODE_NOFRAMES:
+     MSG_HTML("<frameset> inside of <noframes>\n");
+     g_free(rows);
+     g_free(cols);
+     return;
+   case DILLO_HTML_FRAME_MODE_IFRAME:
+     MSG_HTML("<frameset> inside of <iframe>\n");
+     g_free(rows);
+     g_free(cols);
+     return;
+   case DILLO_HTML_FRAME_MODE_NONE:
+     /* 'root' frameset, so add the frameset to the dd */
+     if(html->dd->frameset)
+       gtk_widget_destroy(html->dd->frameset);
+     frameset = gtk_frameset_new(rows, cols);
+     html->dd->frameset = frameset;
+     gtk_container_add(GTK_CONTAINER(GTK_WIDGET(html->dd->docwin)->parent),
+		       GTK_WIDGET(frameset));
+     /* hide the docwin, show the frameset */     
+     gtk_widget_hide(GTK_WIDGET(html->dd->docwin));
+     gtk_widget_show(GTK_WIDGET(html->dd->frameset));
+     break;
+   case DILLO_HTML_FRAME_MODE_FRAMESET:
+     /* nested frameset */
+     frameset = gtk_frameset_new(rows, cols);
+     gtk_container_add(GTK_CONTAINER(html->stack[html->stack_top].frameset), GTK_WIDGET(frameset));
+     gtk_widget_show(GTK_WIDGET(frameset));
+     break;
+   default:
+     break;
+   }
+
+   g_free(rows);
+   g_free(cols);
+
    Html_par_push_tag(html, tag, tagsize);
-   a_Dw_page_add_text(DW_PAGE(html->dw), g_strdup("--FRAME--"),
-                      html->stack[html->stack_top].style);
-   Html_add_indented(html, 40, 0, 5);
+
+   /* once set, this flag does not get reset, even when the frameset tag
+    * is closed. This is intentional. */
+   html->InFlags |= IN_FRAMESET;
+
+   /* put the current frameset state on the stack */
+   html->stack[html->stack_top].frame_mode = DILLO_HTML_FRAME_MODE_FRAMESET;
+   html->stack[html->stack_top].frameset = frameset;
 }
 
 /*
@@ -1871,8 +2104,8 @@ static void Html_tag_open_h(DilloHtml *h
    Html_tag_set_align_attr (html, tag, tagsize);
 
    /* First finalize unclosed H tags (we test if already named anyway) */
-   a_Menu_pagemarks_set_text(html->bw, html->Stash->str);
-   a_Menu_pagemarks_add(html->bw, DW_PAGE (html->dw),
+   a_Pagemark_set_text(html->dd, html->Stash->str);
+   a_Pagemark_add(html->dd, DW_PAGE (html->dw),
                         html->stack[html->stack_top].style, (tag[2] - '0'));
    Html_stash_init(html);
    html->stack[html->stack_top].parse_mode =
@@ -1884,7 +2117,7 @@ static void Html_tag_open_h(DilloHtml *h
  */
 static void Html_tag_close_h(DilloHtml *html, char *tag, gint tagsize)
 {
-   a_Menu_pagemarks_set_text(html->bw, html->Stash->str);
+   a_Pagemark_set_text(html->dd, html->Stash->str);
    Html_pop_tag(html, tag, tagsize);
    a_Dw_page_add_parbreak(DW_PAGE (html->dw), 9,
                           html->stack[(html)->stack_top].style);
@@ -1938,7 +2171,7 @@ static void Html_tag_open_button(DilloHt
    a_Dw_style_box_set_val(&style_attrs.margin, 0);
    a_Dw_style_box_set_val(&style_attrs.border_width, 0);
    a_Dw_style_box_set_val(&style_attrs.padding, 0);
-   style = a_Dw_style_new (&style_attrs, html->bw->main_window->window);
+   style = a_Dw_style_new (&style_attrs, html->dd->bw->main_window->window);
    button = a_Dw_button_new (DW_USES_HINTS, TRUE);
 
    /* The new button is not set button-insensitive, since nested buttons
@@ -1952,7 +2185,7 @@ static void Html_tag_open_button(DilloHt
    a_Dw_style_unref (style);
 
    a_Dw_style_box_set_val(&style_attrs.margin, 5);
-   style = a_Dw_style_new (&style_attrs, html->bw->main_window->window);
+   style = a_Dw_style_new (&style_attrs, html->dd->bw->main_window->window);
    page = a_Dw_page_new ();
    a_Dw_widget_set_style (page, style);
    a_Dw_style_unref (style);
@@ -2021,7 +2254,7 @@ static void Html_tag_open_font(DilloHtml
             color = Html_color_parse(
                        html, attrbuf, style_attrs.color->color_val);
             style_attrs.color = a_Dw_style_color_new
-               (color, html->bw->main_window->window);
+               (color, html->dd->bw->main_window->window);
          }
       }
 
@@ -2034,7 +2267,7 @@ static void Html_tag_open_font(DilloHtml
 #endif
 
       html->stack[html->stack_top].style =
-         a_Dw_style_new (&style_attrs, html->bw->main_window->window);
+         a_Dw_style_new (&style_attrs, html->dd->bw->main_window->window);
       a_Dw_style_unref (old_style);
    }
 
@@ -2181,13 +2414,13 @@ static void Html_load_image(DilloHtml *h
    gint ClientKey;
    /* Fill a Web structure for the cache query */
    Web = a_Web_new(url);
-   Web->bw = html->bw;
+   Web->dd = html->dd;
    Web->Image = Image;
    Web->flags |= WEB_Image;
    /* Request image data from the cache */
    if ((ClientKey = a_Capi_open_url(Web, NULL, NULL)) != 0) {
-      a_Interface_add_client(html->bw, ClientKey, 0);
-      a_Interface_add_url(html->bw, url, WEB_Image);
+      a_Doc_add_client(html->dd, ClientKey, 0);
+      a_Doc_add_url(html->dd, url, WEB_Image);
    }
 }
 
@@ -2235,12 +2468,12 @@ static void Html_tag_open_img(DilloHtml 
          a_Dw_style_box_set_border_color
             (&style_attrs,
              a_Dw_style_shaded_color_new (style_attrs.color->color_val,
-                                          html->bw->main_window->window));
+                                          html->dd->bw->main_window->window));
       else
          a_Dw_style_box_set_border_color
             (&style_attrs,
              a_Dw_style_shaded_color_new (html->linkblock->link_color,
-                                          html->bw->main_window->window));
+                                          html->dd->bw->main_window->window));
 
       a_Dw_style_box_set_border_style (&style_attrs, DW_STYLE_BORDER_SOLID);
       a_Dw_style_box_set_val (&style_attrs.border_width, border);
@@ -2249,7 +2482,7 @@ static void Html_tag_open_img(DilloHtml 
    Image = Html_add_new_image(html, tag, tagsize, &style_attrs, TRUE);
    Html_connect_signals(html, GTK_OBJECT(Image->dw));
    gtk_signal_connect(GTK_OBJECT(Image->dw), "button_press_event",
-                      GTK_SIGNAL_FUNC(Html_image_menu), html->bw);
+                      GTK_SIGNAL_FUNC(Html_image_menu), html->dd);
 
    /* Image maps */
    if (Html_get_attr(html, tag, tagsize, "ismap")) {
@@ -2262,7 +2495,7 @@ static void Html_tag_open_img(DilloHtml 
               usemap_url == NULL)
       /* For simple links, we have to suppress the "image_pressed" signal.
        * This is overridden for USEMAP images. */
-      a_Dw_widget_set_button_sensitive (DW_WIDGET (Image->dw), FALSE);
+     a_Dw_widget_set_button_sensitive (DW_WIDGET (Image->dw), FALSE);
 
    if (usemap_url) {
       a_Dw_image_set_usemap (Image->dw, &html->linkblock->maps, usemap_url);
@@ -2385,6 +2618,10 @@ static void Html_tag_open_area(DilloHtml
       g_return_if_fail ( url != NULL );
       if ((attrbuf = Html_get_attr(html, tag, tagsize, "alt")))
          a_Url_set_alt(url, attrbuf);
+      if ((attrbuf = Html_get_attr(html, tag, tagsize, "target")))
+         a_Url_set_target(url, attrbuf);
+      else if (URL_TARGET_(html->linkblock->base_url))
+	a_Url_set_target(url, URL_TARGET_(html->linkblock->base_url));
 
       link = Html_set_new_link(html, &url);
    }
@@ -2415,23 +2652,28 @@ static void Html_tag_open_a(DilloHtml *h
       url = Html_url_new(html, attrbuf, NULL, 0, 0, 0);
       g_return_if_fail ( url != NULL );
 
+      if ((attrbuf = Html_get_attr(html, tag, tagsize, "target")))
+	 a_Url_set_target(url, attrbuf);
+      else if (URL_TARGET_(html->linkblock->base_url))
+         a_Url_set_target(url, URL_TARGET_(html->linkblock->base_url));
+
       old_style = html->stack[html->stack_top].style;
       style_attrs = *old_style;
 
       if (a_Capi_url_read(url, &dummy)) {
          html->InVisitedLink = TRUE;
          style_attrs.color = a_Dw_style_color_new
-            (html->linkblock->visited_color, html->bw->main_window->window);
+            (html->linkblock->visited_color, html->dd->bw->main_window->window);
       } else {
          style_attrs.color = a_Dw_style_color_new
-            (html->linkblock->link_color,  html->bw->main_window->window);
+            (html->linkblock->link_color,  html->dd->bw->main_window->window);
       }
 
       style_attrs.text_decoration |= DW_STYLE_TEXT_DECORATION_UNDERLINE;
       style_attrs.x_link = Html_set_new_link(html, &url);
 
       html->stack[html->stack_top].style =
-         a_Dw_style_new (&style_attrs, html->bw->main_window->window);
+         a_Dw_style_new (&style_attrs, html->dd->bw->main_window->window);
       a_Dw_style_unref (old_style);
    }
 
@@ -2464,7 +2706,7 @@ static void Html_tag_open_u(DilloHtml *h
    style_attrs = *style;
    style_attrs.text_decoration |= DW_STYLE_TEXT_DECORATION_UNDERLINE;
    html->stack[html->stack_top].style =
-      a_Dw_style_new(&style_attrs, html->bw->main_window->window);
+      a_Dw_style_new(&style_attrs, html->dd->bw->main_window->window);
    a_Dw_style_unref(style);
 }
 
@@ -2482,7 +2724,7 @@ static void Html_tag_open_strike(DilloHt
    style_attrs = *style;
    style_attrs.text_decoration |= DW_STYLE_TEXT_DECORATION_LINE_THROUGH;
    html->stack[html->stack_top].style =
-      a_Dw_style_new(&style_attrs, html->bw->main_window->window);
+      a_Dw_style_new(&style_attrs, html->dd->bw->main_window->window);
    a_Dw_style_unref(style);
 }
 
@@ -2683,7 +2925,7 @@ static void Html_tag_open_hr(DilloHtml *
       a_Dw_style_box_set_border_color
          (&style_attrs,
           a_Dw_style_shaded_color_new (style_attrs.color->color_val,
-                                       html->bw->main_window->window));
+                                       html->dd->bw->main_window->window));
       if (size < 1)
          size = 1;
    } else {
@@ -2692,7 +2934,7 @@ static void Html_tag_open_hr(DilloHtml *
          (&style_attrs,
           a_Dw_style_shaded_color_new
              (html->stack[html->stack_top].current_bg_color,
-              html->bw->main_window->window));
+              html->dd->bw->main_window->window));
       if (size < 2)
          size = 2;
    }
@@ -2788,6 +3030,12 @@ static void Html_tag_open_form(DilloHtml
       action = Html_url_new(html, attrbuf, NULL, 0, 0, 0);
    else
       action = a_Url_dup(html->linkblock->base_url);
+
+   if ((attrbuf = Html_get_attr(html, tag, tagsize, "target")))
+     a_Url_set_target(action, attrbuf);
+   else if (URL_TARGET_(html->linkblock->base_url))
+     a_Url_set_target(action, URL_TARGET_(html->linkblock->base_url));
+
    enc = DILLO_HTML_ENC_URLENCODING;
    if ( (attrbuf = Html_get_attr(html, tag, tagsize, "encoding")) ) {
       /* todo: maybe deal with unknown encodings? */
@@ -3117,6 +3365,9 @@ static void Html_submit_form(GtkWidget *
    DilloHtmlInput *input;
    DilloUrl *new_url;
    gchar *url_str, *action_str, *p;
+   DilloDoc *name_dd;
+
+   name_dd = NULL;
 
    /* Search the form that generated the submit event */
    if ( (i = Html_find_form(submit, html_lb)) == -1 )
@@ -3214,7 +3465,15 @@ static void Html_submit_form(GtkWidget *
          g_free(url_str);
       }
 
-      a_Nav_push(html_lb->bw, new_url);
+      if(URL_TARGET_(form->action)) {
+	a_Url_set_target(new_url, (gchar *) URL_TARGET_(form->action));
+	name_dd = a_Doc_get_by_name(html_lb->dd, (gchar *) URL_TARGET_(new_url));
+      }
+
+      if(name_dd)
+      	a_Nav_push(name_dd, new_url);
+      else
+	a_Nav_push(html_lb->dd, new_url);
       g_free(action_str);
       g_string_free(DataStr, TRUE);
       a_Url_free(new_url);
@@ -3875,6 +4134,8 @@ static void Html_tag_open_base(DilloHtml
             a_Url_free(BaseUrl);
          }
       }
+      if ((attrbuf = Html_get_attr(html, tag, tagsize, "target")))
+	a_Url_set_target(html->linkblock->base_url, attrbuf);
    } else {
       MSG_HTML("the BASE element must appear in the HEAD section\n");
    }
@@ -4033,7 +4294,7 @@ static const TagInfo Tags[] = {
   {"hr", 'F', Html_tag_open_hr, Html_tag_close_nop},
   /* html */
   {"i", 'R', Html_tag_open_i, Html_tag_close_default},
-  {"iframe", 'R', Html_tag_open_frame, Html_tag_close_nop},
+  {"iframe", 'R', Html_tag_open_iframe, Html_tag_close_par},
   {"img", 'F', Html_tag_open_img, Html_tag_close_nop},
   {"input", 'F', Html_tag_open_input, Html_tag_close_nop},
   /* ins */
@@ -4326,7 +4587,7 @@ static void Html_add_widget(DilloHtml *h
       Html_parse_length (html, width_str) : DW_STYLE_LENGTH_AUTO;
    new_style_attrs.height = height_str ?
       Html_parse_length (html, height_str) : DW_STYLE_LENGTH_AUTO;
-   style = a_Dw_style_new (&new_style_attrs, (html)->bw->main_window->window);
+   style = a_Dw_style_new (&new_style_attrs, (html)->dd->bw->main_window->window);
    a_Dw_page_add_widget(DW_PAGE (html->dw), widget, style);
    a_Dw_style_unref (style);
 }
@@ -4479,7 +4740,6 @@ static gint Html_write_raw(DilloHtml *ht
 static void Html_write(DilloHtml *html, char *Buf, gint BufSize, gint Eof)
 {
    DwPage *page;
-   char completestr[32];
    gint token_start;
    char *buf = Buf + html->Start_Ofs;
    gint bufsize = BufSize - html->Start_Ofs;
@@ -4490,12 +4750,10 @@ static void Html_write(DilloHtml *html, 
    token_start = Html_write_raw(html, buf, bufsize, Eof);
    html->Start_Ofs += token_start;
 
-   if ( html->bw ) {
-      g_snprintf(
-         completestr, 32, "%s%.1f Kb",
-         PBAR_PSTR(prefs.panel_size == 1),
-         (float)html->Start_Ofs/1024);
-      a_Progressbar_update(html->bw->progress, completestr, 1);
+   if ( html->dd ) {
+      html->dd->progress = html->Start_Ofs/1024;
+      html->dd->ready = FALSE;
+      a_Doc_progress_update(html->dd);
    }
 }
 
@@ -4523,10 +4781,11 @@ static void Html_close(DilloHtml *html, 
    g_string_free(html->attr_data, TRUE);
 
    /* Remove this client from our active list */
-   a_Interface_close_client(html->bw, ClientKey);
+   a_Doc_close_client(html->dd, ClientKey);
 
-   /* Set progress bar insensitive */
-   a_Progressbar_update(html->bw->progress, NULL, 0);
+   /* update progress */
+   html->dd->ready = TRUE;
+   a_Doc_progress_update(html->dd);
 
    g_free(html);
 }
diff -pruN dillo/src/html.h dillo_tabs/src/html.h
--- dillo/src/html.h	2003-11-24 14:32:40.111774448 +0100
+++ dillo_tabs/src/html.h	2003-11-24 14:32:40.222757576 +0100
@@ -4,7 +4,7 @@
 #include <gdk/gdk.h>
 #include <gtk/gtkcontainer.h>
 
-#include "browser.h"         /* for BrowserWindow */
+#include "browser.h"         /* for DilloDoc */
 #include "dw_widget.h"       /* for DwWidget */
 #include "dw_image.h"        /* for DwImageMapList */
 
@@ -27,9 +27,8 @@ typedef struct _DilloHtmlOption  DilloHt
 typedef struct _DilloHtmlSelect  DilloHtmlSelect;
 typedef struct _DilloHtmlInput   DilloHtmlInput;
 
-
 struct _DilloHtmlLB {
-  BrowserWindow *bw;
+  DilloDoc *dd;
   DilloUrl *base_url;
 
   DilloHtmlForm *forms;
@@ -79,6 +78,12 @@ typedef enum {
   DILLO_HTML_TABLE_MODE_TD     /* inside of <td> */
 } DilloHtmlTableMode;
 
+typedef enum {
+  DILLO_HTML_FRAME_MODE_NONE,     /* no frameset at all */
+  DILLO_HTML_FRAME_MODE_IFRAME,   /* inside of <iframe> */
+  DILLO_HTML_FRAME_MODE_FRAMESET, /* inside of <frameset> */
+  DILLO_HTML_FRAME_MODE_NOFRAMES  /* inside of <noframes> */
+} DilloHtmlFrameMode;
 
 typedef enum {
   IN_HTML        = 1 << 0,
@@ -87,15 +92,17 @@ typedef enum {
   IN_FORM        = 1 << 3,
   IN_SELECT      = 1 << 4,
   IN_TEXTAREA    = 1 << 5,
-  IN_MAP         = 1 << 6
+  IN_MAP         = 1 << 6,
+  IN_FRAMESET    = 1 << 7
 } DilloHtmlProcessingState;
 
-
 struct _DilloHtmlState {
   char *tag;
   DwStyle *style, *table_cell_style;
   DilloHtmlParseMode parse_mode;
   DilloHtmlTableMode table_mode;
+  DilloHtmlFrameMode frame_mode;
+  GtkWidget *frameset;
   gboolean cell_text_align_set;
   enum { HTML_LIST_NONE, HTML_LIST_UNORDERED, HTML_LIST_ORDERED } list_type;
   gint list_number;
@@ -210,7 +217,7 @@ struct _DilloHtml {
 
   GString *attr_data;
 
-  BrowserWindow *bw;
+  DilloDoc *dd;
 };
 
 #ifdef __cplusplus
diff -pruN dillo/src/interface.c dillo_tabs/src/interface.c
--- dillo/src/interface.c	2003-11-24 14:32:40.113774144 +0100
+++ dillo_tabs/src/interface.c	2003-11-24 14:32:40.224757272 +0100
@@ -3,6 +3,7 @@
  *
  * Copyright (C) 1997 Raph Levien <raph@acm.org>
  * Copyright (C) 1999 Sammy Mannaert <nstalkie@tvd.be>
+ * Copyright (C) 2003 Frank de Lange <frank@unternet.org>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -20,18 +21,18 @@
 #include <sys/time.h>
 #include <fcntl.h>
 
-#include "msg.h"
 #include "list.h"
 #include "misc.h"
 #include "dillo.h"
 #include "history.h"
 #include "nav.h"
+#include "doc.h"
 #include "IO/Url.h"
 #include "IO/IO.h"
 #include "interface.h"
+#include "tab.h"
 #include "commands.h"
 #include "menu.h"
-#include "bookmark.h"
 #include "prefs.h"
 #include "url.h"
 #include "capi.h"
@@ -46,9 +47,18 @@
 #include "pixmaps.h"
 #include <gdk/gdkkeysyms.h>
 
-#define DEBUG_LEVEL 0
+#define DEBUG_FIND 10
+/*#define DEBUG_LEVEL 10*/
 #include "debug.h"
 
+/*
+ * declarations
+ */
+static void
+Interface_make_dialog(GtkWidget **DialogWindow, char *WmName, char *WmClass,
+		      char *WTitle, gboolean destroy, GtkWidget **DialogEntry, char *EntryStr,
+		      GtkWidget **CheckButton, char *CheckButtonText,
+		      char *B1Label, GtkSignalFunc B1CallBack, void *B1CbData);
 
 /*
  * Local Data
@@ -61,7 +71,6 @@ static gint num_bw, num_bw_max;
 /* We need only one of them. */
 static GtkTooltips *tooltips = NULL;
 
-
 /*
  * Initialize global data
  */
@@ -73,195 +82,19 @@ void a_Interface_init(void)
    tooltips = gtk_tooltips_new ();
 }
 
-/*
- * Stop all active connections in the browser window (except downloads)
- */
-void a_Interface_stop(BrowserWindow *bw)
-{
-   DEBUG_MSG(3, "a_Interface_stop: hi!\n");
-
-   /* Remove root clients */
-   while ( bw->NumRootClients ) {
-      a_Cache_stop_client(bw->RootClients[0]);
-      a_List_remove(bw->RootClients, 0, bw->NumRootClients);
-   }
-   /* Remove image clients */
-   while ( bw->NumImageClients ) {
-      a_Cache_stop_client(bw->ImageClients[0]);
-      a_List_remove(bw->ImageClients, 0, bw->NumImageClients);
-   }
-}
-
-/*
- * Empty RootClients, ImageClients and PageUrls lists and
- * reset progress bar data.
- */
-void a_Interface_clean(BrowserWindow *bw)
-{
-   g_return_if_fail ( bw != NULL );
-
-   while ( bw->NumRootClients )
-      a_List_remove(bw->RootClients, 0, bw->NumRootClients);
-
-   while ( bw->NumImageClients )
-      a_List_remove(bw->ImageClients, 0, bw->NumImageClients);
-
-   while ( bw->NumPageUrls ) {
-      a_Url_free(bw->PageUrls[0].Url);
-      a_List_remove(bw->PageUrls, 0, bw->NumPageUrls);
-   }
-
-   /* Zero image-progressbar data */
-   bw->NumImages = 0;
-   bw->NumImagesGot = 0;
-}
-
 /*=== Browser Window Interface Updating =====================================*/
 /*
- * Remove the cache-client from the bw list
- * (client can be a image or a html page)
- */
-void a_Interface_remove_client(BrowserWindow *bw, gint ClientKey)
-{
-   gint i;
-   gboolean Found = FALSE;
-
-   for ( i = 0; !Found && i < bw->NumRootClients; ++i)
-      if ( bw->RootClients[i] == ClientKey ) {
-         a_List_remove(bw->RootClients, i, bw->NumRootClients);
-         Found = TRUE;
-      }
-
-   for ( i = 0; !Found && i < bw->NumImageClients; ++i)
-      if ( bw->ImageClients[i] == ClientKey ) {
-         a_List_remove(bw->ImageClients, i, bw->NumImageClients);
-         bw->NumImagesGot++;
-         Found = TRUE;
-      }
-
-   a_Interface_set_button_sens(bw);
-}
-
-/*
- * Remove the cache-client from the bw list
- * (client can be a image or a html page)
- */
-void a_Interface_close_client(BrowserWindow *bw, gint ClientKey)
-{
-   gchar numstr[32];
-
-   a_Interface_remove_client(bw, ClientKey);
-
-   /* --Progress bars stuff-- */
-   g_snprintf(numstr, 32, "%s%d of %d", PBAR_ISTR(prefs.panel_size == 1),
-              bw->NumImagesGot, bw->NumImages);
-   a_Progressbar_update(bw->imgprogress, numstr,
-                        (bw->NumImagesGot == bw->NumImages) ? 0 : 1 );
-}
-
-/*
- * Set the sensitivity on back/forw buttons and menu entries.
- */
-static gint Interface_sens_idle_func(BrowserWindow *bw)
-{
-   gboolean back_sensitive, forw_sensitive, stop_sensitive;
-
-   /* Stop button */
-   stop_sensitive = (bw->NumRootClients > 0);
-   gtk_widget_set_sensitive(bw->stop_button, stop_sensitive);
-
-   /* Back and Forward buttons */
-   back_sensitive = a_Nav_stack_ptr(bw) > 0;
-   gtk_widget_set_sensitive(bw->back_button, back_sensitive);
-   forw_sensitive = (a_Nav_stack_ptr(bw) < a_Nav_stack_size(bw) - 1 &&
-                     !bw->nav_expecting);
-   gtk_widget_set_sensitive(bw->forw_button, forw_sensitive);
-
-   bw->sens_idle_id = 0;
-   return FALSE;
-}
-
-/*
  * Set the sensitivity on back/forw buttons and menu entries.
  */
 void a_Interface_set_button_sens(BrowserWindow *bw)
 {
-   if (bw->sens_idle_id == 0)
-      bw->sens_idle_id = gtk_idle_add(
-                             (GtkFunction)Interface_sens_idle_func, bw);
-}
-
-/*
- * Add a reference to the cache-client in the browser window's list.
- * This helps us keep track of which are active in the window so that it's
- * possible to abort them.
- * (Root: Flag, whether a Root URL or not)
- */
-void a_Interface_add_client(BrowserWindow *bw, gint Key, gint Root)
-{
-   gint nc;
-   char numstr[32];
-
-   g_return_if_fail ( bw != NULL );
-
-   if ( Root ) {
-      nc = bw->NumRootClients;
-      a_List_add(bw->RootClients, nc, bw->MaxRootClients);
-      bw->RootClients[nc] = Key;
-      bw->NumRootClients++;
-      a_Interface_set_button_sens(bw);
-   } else {
-      nc = bw->NumImageClients;
-      a_List_add(bw->ImageClients, nc, bw->MaxImageClients);
-      bw->ImageClients[nc] = Key;
-      bw->NumImageClients++;
-      bw->NumImages++;
-      a_Interface_set_button_sens(bw);
-
-      /* --Progress bar stuff-- */
-      g_snprintf(numstr, 32, "%s%d of %d", PBAR_ISTR(prefs.panel_size == 1),
-                 bw->NumImagesGot, bw->NumImages);
-      a_Progressbar_update(bw->imgprogress, numstr, 1);
-   }
-}
-
-/*
- * Add an URL to the browser window's list.
- * This helps us keep track of page requested URLs so that it's
- * possible to stop, abort and reload them.)
- *   Flags: Chosen from {BW_Root, BW_Image, BW_Download}
- */
-void a_Interface_add_url(BrowserWindow *bw, const DilloUrl *Url, gint Flags)
-{
-   gint nu, i;
-   gboolean found = FALSE;
-
-   g_return_if_fail ( bw != NULL && Url != NULL );
-
-   nu = bw->NumPageUrls;
-   for ( i = 0; i < nu; i++ ) {
-      if ( !a_Url_cmp(Url, bw->PageUrls[i].Url) ) {
-         found = TRUE;
-         break;
-      }
-   }
-   if ( !found ) {
-      a_List_add(bw->PageUrls, nu, bw->MaxPageUrls);
-      bw->PageUrls[nu].Url = a_Url_dup(Url);
-      bw->PageUrls[nu].Flags = Flags;
-      bw->NumPageUrls++;
-   }
-
-   /* test:
-   MSG("Urls:\n");
-   for (i = 0; i < bw->NumPageUrls; i++)
-      MSG("%s\n", bw->PageUrls[i].Url);
-   MSG("---\n");
-   */
+   gtk_widget_set_sensitive(bw->stop_button, bw->dd->stop_sensitive);
+   gtk_widget_set_sensitive(bw->back_button, bw->dd->back_sensitive);
+   gtk_widget_set_sensitive(bw->forw_button, bw->dd->forw_sensitive);
 }
 
 /*
- * Remove a single browser window. This includes all its open childs,
+ * Remove a single browser window. This includes all its open documents,
  * freeing all resources associated with them, and exiting gtk
  * if no browser windows are left.
  */
@@ -269,8 +102,10 @@ static gboolean Interface_quit(GtkWidget
 {
    gint i;
 
-   /* stop/abort open connections. */
-   a_Interface_stop(bw);
+   /* destroy the container. It should take all its contents with
+    * it, relying on callbacks to destroy non-widget data
+    */
+   gtk_widget_destroy(GTK_WIDGET(bw->container));
 
    g_slist_free(bw->PanelHandles);
 
@@ -280,8 +115,8 @@ static gboolean Interface_quit(GtkWidget
       gtk_widget_destroy(bw->openfile_dialog_window);
    if (bw->quit_dialog_window != NULL)
       gtk_widget_destroy(bw->quit_dialog_window);
-   if (bw->findtext_dialog_window != NULL)
-      gtk_widget_destroy(bw->findtext_dialog_window);
+   if (bw->findtext_dialog != NULL)
+      gtk_widget_destroy(bw->findtext_dialog);
    if (bw->search_dialog_window != NULL)
       gtk_widget_destroy(bw->search_dialog_window);
    if (bw->question_dialog_window != NULL)
@@ -290,35 +125,24 @@ static gboolean Interface_quit(GtkWidget
    if (bw->menu_popup.over_page)
       gtk_widget_destroy(bw->menu_popup.over_page);
    if (bw->menu_popup.over_link)
-      /* this also destroys menu_popup.over_image */
       gtk_widget_destroy(bw->menu_popup.over_link);
-   if (bw->menu_popup.over_back)
-      gtk_widget_destroy(bw->menu_popup.over_back);
-   if (bw->menu_popup.over_forw)
-      gtk_widget_destroy(bw->menu_popup.over_forw);
+   if (bw->menu_popup.over_image)
+      gtk_widget_destroy(bw->menu_popup.over_image);
+   if (bw->menu_popup.over_history)
+      gtk_widget_destroy(bw->menu_popup.over_history);
+   if (bw->menu_popup.over_tab)
+      gtk_widget_destroy(bw->menu_popup.over_tab);
    if (bw->menu_popup.url)
       a_Url_free(bw->menu_popup.url);
    if (bw->menu_popup.url2)
       a_Url_free(bw->menu_popup.url2);
 
-   if (bw->sens_idle_id)
-      gtk_idle_remove(bw->sens_idle_id);
-
    for (i = 0; i < num_bw; i++)
       if (browser_window[i] == bw) {
          browser_window[i] = browser_window[--num_bw];
          break;
       }
 
-   /* free nav_stack and nav_expect stuff */
-   a_Nav_free(bw);
-
-   g_free(bw->RootClients);
-   g_free(bw->ImageClients);
-
-   for (i = 0; i < bw->NumPageUrls; i++)
-      a_Url_free(bw->PageUrls[i].Url);
-   g_free(bw->PageUrls);
    g_free(bw);
 
    if (num_bw == 0)
@@ -359,32 +183,30 @@ static GtkWidget *Interface_pixmap_new(G
 }
 
 /*
- * Set the bw's cursor type
+ * Select/deselect GtkEditable contents
  */
-void a_Interface_set_cursor(BrowserWindow *bw, GdkCursorType CursorType)
+void a_Interface_editable_select_all(GtkWidget *widget)
 {
-   GdkCursor *cursor;
-
-   if ( bw->CursorType != CursorType ) {
-      cursor = gdk_cursor_new(CursorType);
-      gdk_window_set_cursor(bw->docwin->window, cursor);
-      gdk_cursor_destroy(cursor);
-      bw->CursorType = CursorType;
-   }
+   if(GTK_IS_EDITABLE(widget))
+     gtk_editable_select_region(GTK_EDITABLE(widget), 0, -1);
 }
 
-/*
- * Connect button's "clicked" event with (key, key_mod) pair.
- */
-static void Interface_set_button_accel(GtkButton *button,
-                                       gint key,
-                                       gint key_mod,
-                                       GtkAccelGroup *accel_group)
+void a_Interface_editable_deselect_all(GtkWidget *widget)
 {
-   gtk_widget_add_accelerator(GTK_WIDGET(button), "clicked", accel_group,
-                              key, key_mod, GTK_ACCEL_LOCKED);
+   if(GTK_IS_EDITABLE(widget))
+     gtk_editable_select_region(GTK_EDITABLE(widget), 0, 0);
 }
 
+
+/* GTK1 HACK, remove after port to GTK2
+ * 
+ * replace GtkToolBar->focus signal function with our own, which does not
+ * trap focus inside the toolbar
+ */
+static gint Interface_toolbar_focus (GtkContainer *container,
+				     GtkDirectionType direction)
+{ return FALSE; }
+
 /*
  * Create the "NEW" button, its location-entry and the search button.
  */
@@ -398,14 +220,13 @@ static GtkWidget *Interface_locbar_new(B
    bw->location = gtk_entry_new();
    gtk_signal_connect(GTK_OBJECT(bw->location), "activate",
                       (GtkSignalFunc) a_Interface_entry_open_url, bw);
-   gtk_widget_add_accelerator(GTK_WIDGET(bw->location), "grab_focus",
-                              bw->accel_group, GDK_u,
-                              GDK_CONTROL_MASK, GTK_ACCEL_LOCKED);
+   gtk_signal_connect_object(GTK_OBJECT(bw->location), "grab-focus",
+			     a_Interface_editable_select_all,
+			     GTK_OBJECT(bw->location));
 
    /* left toolbar (Clear url) */
    toolbar_l = gtk_toolbar_new(GTK_ORIENTATION_HORIZONTAL, GTK_TOOLBAR_BOTH);
    gtk_toolbar_set_button_relief(GTK_TOOLBAR(toolbar_l), GTK_RELIEF_NONE);
-   GTK_WIDGET_UNSET_FLAGS (toolbar_l, GTK_CAN_FOCUS);
 
    bw->clear_url_button = gtk_toolbar_append_item(
                              GTK_TOOLBAR(toolbar_l),
@@ -419,7 +240,6 @@ static GtkWidget *Interface_locbar_new(B
    /* right toolbar (Search) */
    toolbar_r = gtk_toolbar_new(GTK_ORIENTATION_HORIZONTAL, GTK_TOOLBAR_BOTH);
    gtk_toolbar_set_button_relief(GTK_TOOLBAR(toolbar_r), GTK_RELIEF_NONE);
-   GTK_WIDGET_UNSET_FLAGS (toolbar_r, GTK_CAN_FOCUS);
 
    bw->search_button = gtk_toolbar_append_item(
                           GTK_TOOLBAR(toolbar_r),
@@ -427,9 +247,7 @@ static GtkWidget *Interface_locbar_new(B
                           Interface_pixmap_new(bw->main_window, search_xpm),
                           GTK_SIGNAL_FUNC(a_Interface_search_dialog),
                           bw);
-   gtk_widget_add_accelerator(GTK_WIDGET(bw->search_button), "clicked",
-                              bw->accel_group, GDK_s,
-                              GDK_CONTROL_MASK, GTK_ACCEL_LOCKED);
+
    gtk_box_pack_start(GTK_BOX(hbox), toolbar_l, FALSE, FALSE, 0);
    gtk_widget_show(toolbar_l);
    gtk_box_pack_start(GTK_BOX(hbox), bw->location, TRUE, TRUE, 0);
@@ -450,6 +268,13 @@ static GtkWidget *Interface_toolbar_new(
    gboolean s = prefs.small_icons;
 
    toolbar = gtk_toolbar_new(GTK_ORIENTATION_HORIZONTAL, GTK_TOOLBAR_BOTH);
+   /* GTK1 HACK, remove after port to GTK2
+    * 
+    * replace GtkToolBar->focus signal function with our own, which does not
+    * trap focus inside the toolbar
+    */
+   GTK_CONTAINER_CLASS(GTK_OBJECT(toolbar)->klass)->focus =
+     Interface_toolbar_focus;
    gtk_toolbar_set_button_relief(GTK_TOOLBAR(toolbar), GTK_RELIEF_NONE);
 
    /* back button */
@@ -461,8 +286,6 @@ static GtkWidget *Interface_toolbar_new(
                                              s ? s_left_xpm : left_xpm),
                         (GtkSignalFunc) a_Commands_back_callback, bw);
    gtk_widget_set_sensitive(bw->back_button, FALSE);
-   Interface_set_button_accel(GTK_BUTTON(bw->back_button), GDK_comma,
-                              0, bw->accel_group);
    gtk_signal_connect(GTK_OBJECT(bw->back_button), "button-press-event",
                       GTK_SIGNAL_FUNC(a_Commands_navpress_callback), bw);
 
@@ -475,8 +298,6 @@ static GtkWidget *Interface_toolbar_new(
                                              s ? s_right_xpm : right_xpm),
                         (GtkSignalFunc) a_Commands_forw_callback, bw);
    gtk_widget_set_sensitive(bw->forw_button, FALSE);
-   Interface_set_button_accel(GTK_BUTTON(bw->forw_button), GDK_period,
-                              0, bw->accel_group);
    gtk_signal_connect(GTK_OBJECT(bw->forw_button), "button-press-event",
                       GTK_SIGNAL_FUNC(a_Commands_navpress_callback), bw);
 
@@ -499,8 +320,6 @@ static GtkWidget *Interface_toolbar_new(
                           Interface_pixmap_new(bw->main_window,
                                                s ? s_reload_xpm : reload_xpm),
                           (GtkSignalFunc) a_Commands_reload_callback, bw);
-   Interface_set_button_accel(GTK_BUTTON(bw->reload_button), GDK_r,
-                              GDK_CONTROL_MASK, bw->accel_group);
 
    /* save button */
    bw->save_button = gtk_toolbar_append_item(
@@ -510,6 +329,7 @@ static GtkWidget *Interface_toolbar_new(
                         Interface_pixmap_new(bw->main_window,
                                              s ? s_save_xpm : save_xpm),
                         (GtkSignalFunc) a_Commands_save_callback, bw);
+
    /* stop button */
    bw->stop_button = gtk_toolbar_append_item(
                         GTK_TOOLBAR(toolbar),
@@ -529,10 +349,6 @@ static GtkWidget *Interface_toolbar_new(
                                                   s ? s_bm_xpm : bm_xpm),
                              (GtkSignalFunc) a_Commands_viewbm_callback, bw);
    gtk_widget_set_sensitive(bw->bookmarks_button, TRUE);
-   Interface_set_button_accel(GTK_BUTTON(bw->bookmarks_button), GDK_semicolon,
-                              0, bw->accel_group);
-   Interface_set_button_accel(GTK_BUTTON(bw->bookmarks_button), GDK_B,
-                              GDK_MOD1_MASK, bw->accel_group);
    gtk_signal_connect(GTK_OBJECT(bw->bookmarks_button), "button-press-event",
                       GTK_SIGNAL_FUNC(a_Commands_navpress_callback), bw);
 
@@ -559,23 +375,95 @@ static GtkWidget *Interface_progressbox_
 }
 
 /*
+ * Create search box/popup
+ */
+static void Interface_add_search_dialog(BrowserWindow *bw)
+{
+   /* does the user want popup windows? */
+   if(prefs.popup_dialogs) {
+     /* YES: create search dialog popup */
+     Interface_make_dialog(&(bw->findtext_dialog),
+			   "findtext_dialog", "Dillo", "Dillo: Find text in page", FALSE,
+			   &(bw->findtext_dialog_entry), "",
+			   &(bw->findtext_dialog_check), "Case sensitive",
+			   "Find", (GtkSignalFunc) a_Interface_findtext_search, (void *)bw);
+     if (prefs.transient_dialogs)
+       gtk_window_set_transient_for(GTK_WINDOW(bw->findtext_dialog),
+				    GTK_WINDOW(bw->main_window));
+     gtk_signal_connect_object(GTK_OBJECT(bw->findtext_dialog),
+			       "hide",
+			       (GtkSignalFunc) a_Interface_findtext_hide,
+			       (void*) bw);
+
+     gtk_window_set_position(GTK_WINDOW(bw->findtext_dialog),
+			     GTK_WIN_POS_NONE);
+   } else {
+     /* NO: create search dialog widgets */
+     bw->findtext_dialog = gtk_hbox_new(FALSE, 0);
+     bw->findtext_dialog_entry = gtk_entry_new();
+     bw->findtext_dialog_check = gtk_check_button_new_with_label("aA");
+     gtk_widget_set_usize(bw->findtext_dialog, 100, 0);
+     gtk_widget_show(bw->findtext_dialog_entry);
+     gtk_widget_show(bw->findtext_dialog_check);
+     gtk_tooltips_set_tip(tooltips, bw->findtext_dialog_entry,
+			  "press <Enter> to (repeat) search, <Esc> to close search",
+			  "Search");
+     gtk_tooltips_set_tip(tooltips, bw->findtext_dialog_check,
+			  "Case sensitive",
+			  "Case sensitive");
+     gtk_box_pack_start(GTK_BOX(bw->findtext_dialog),
+			bw->findtext_dialog_check, FALSE, FALSE, 0);
+     gtk_box_pack_start(GTK_BOX(bw->findtext_dialog),
+			bw->findtext_dialog_entry, TRUE, TRUE, 0);
+     gtk_box_pack_start(GTK_BOX(bw->status_box),
+			bw->findtext_dialog, TRUE, TRUE, 2);
+     gtk_signal_connect(GTK_OBJECT(bw->findtext_dialog_entry), "activate",
+			a_Interface_findtext_search, (void *) bw);
+     gtk_signal_connect_object(GTK_OBJECT(bw->findtext_dialog), "hide",
+			       a_Interface_findtext_hide, (void *) bw);
+     gtk_widget_add_accelerator(bw->findtext_dialog, "hide",
+				bw->accel_group, GDK_Escape, 0, 0);
+   }
+
+   gtk_signal_connect_object(GTK_OBJECT(bw->findtext_dialog_entry),
+			     "grab-focus",
+			     a_Interface_editable_select_all,
+			     GTK_OBJECT(bw->findtext_dialog_entry));
+}
+
+/*
  * Hide/Unhide this bw's control panels.
  * toggle: Flag [toggle or set].
  */
-void Interface_toggle_panel(BrowserWindow *bw, gint toggle)
+void a_Interface_toggle_panel(BrowserWindow *bw, gboolean toggle)
 {
    if (toggle)
       bw->fullwindow = !bw->fullwindow;
 
    if (bw->fullwindow) {
       g_slist_foreach(bw->PanelHandles, (GFunc)gtk_widget_hide, NULL);
-      gtk_widget_hide(bw->status_box);
+      gtk_widget_hide (bw->status_box);
+      gtk_widget_hide (bw->full_screen_button);
       gtk_widget_show (bw->full_screen_off_button);
-      gtk_widget_grab_focus(GTK_BIN(bw->docwin)->child);
+      gtk_widget_grab_focus(GTK_BIN(bw->dd->docwin)->child);
    } else {
       g_slist_foreach(bw->PanelHandles, (GFunc)gtk_widget_show, NULL);
-      gtk_widget_show(bw->status_box);
       gtk_widget_hide (bw->full_screen_off_button);
+      gtk_widget_show (bw->full_screen_button);
+      gtk_widget_show (bw->status_box);
+   }
+   a_Tab_visibility_update(bw);
+}
+
+/*
+ * Reset interface elements when content window gets focus
+ */
+void a_Interface_content_focus_callback(BrowserWindow *bw)
+{
+   a_Interface_editable_deselect_all(bw->location);
+   if(bw->fullwindow) {
+      g_slist_foreach(bw->PanelHandles, (GFunc)gtk_widget_hide, NULL);
+      gtk_widget_hide(bw->status_box);
    }
 }
 
@@ -610,50 +498,7 @@ static void Interface_browser_window_cus
       gtk_widget_hide(bw->progress_box);
 
    bw->fullwindow = prefs.fullwindow_start;
-   Interface_toggle_panel(bw, FALSE);
-}
-
-static void Interface_full_screen_callback (BrowserWindow *bw)
-{
-   Interface_toggle_panel(bw, TRUE);
-}
-
-/*
- * Handler for double-mouse-clicks that don't belong to the viewport.
- */
-static gint Interface_click_callback(BrowserWindow *bw, GdkEventButton *event)
-{
-   if (event->type == GDK_2BUTTON_PRESS && event->button == 1)
-      Interface_toggle_panel(bw, TRUE);
-   return TRUE;
-}
-
-/*
- * Handler for key presses that don't belong to the viewport.
- * (Used to customize the interface a bit)
- */
-static void Interface_key_press_handler(GtkWidget *widget,
-                                        GdkEventKey *event,
-                                        gpointer client_data)
-{
-   BrowserWindow *bw = client_data;
-
-   switch (event->keyval) {
-   case GDK_BackSpace:
-      /* This key is handled here because GTK accel group ignores it */
-      if (event->state & GDK_SHIFT_MASK)
-         a_Commands_forw_callback(NULL, bw);
-      else
-         a_Commands_back_callback(NULL, bw);
-      break;
-   case GDK_slash:
-      /* This key is handled here because GTK accel group ignores it */
-      a_Commands_findtext_callback(NULL, bw);
-      break;
-   default:
-      _MSG(">> Key pressed!\n");
-      break;
-   }
+   a_Interface_toggle_panel(bw, FALSE);
 }
 
 /*
@@ -662,7 +507,7 @@ static void Interface_key_press_handler(
 static void Interface_add_full_screen_button (BrowserWindow *bw, GtkBox *box)
 {
    /* The button is put into a vbox, so that it is not enlarged vertically. */
-   GtkWidget *vbox, *dummy, *button, *pixmap;
+   GtkWidget *vbox, *dummy, *pixmap;
 
    vbox = gtk_vbox_new (FALSE, 0);
    gtk_box_pack_start (GTK_BOX (box), vbox, FALSE, FALSE, 0);
@@ -674,22 +519,26 @@ static void Interface_add_full_screen_bu
    gtk_box_pack_start (GTK_BOX (vbox), dummy, TRUE, TRUE, 0);
    gtk_widget_show (dummy);
 
-   button = gtk_button_new ();
-   gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
-   gtk_tooltips_set_tip (tooltips, button, "Hide Controls", "Show Controls");
-   GTK_WIDGET_UNSET_FLAGS (button, GTK_CAN_FOCUS);
-   gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
-   gtk_widget_show (button);
+   bw->full_screen_button = gtk_button_new ();
+   gtk_button_set_relief (GTK_BUTTON (bw->full_screen_button), GTK_RELIEF_NONE);
+   gtk_tooltips_set_tip (tooltips, bw->full_screen_button, "Hide Controls", "Show Controls");
+   GTK_WIDGET_UNSET_FLAGS (bw->full_screen_button, GTK_CAN_FOCUS);
+   gtk_box_pack_start (GTK_BOX (vbox), bw->full_screen_button, FALSE, FALSE, 0);
+   gtk_widget_show (bw->full_screen_button);
 
    pixmap = Interface_pixmap_new(bw->main_window, full_screen_on_xpm);
-   gtk_container_add (GTK_CONTAINER (button), pixmap);
+   gtk_container_add (GTK_CONTAINER (bw->full_screen_button), pixmap);
    gtk_widget_show (pixmap);
 
    gtk_signal_connect_object
-      (GTK_OBJECT (button), "clicked",
-       GTK_SIGNAL_FUNC (Interface_full_screen_callback), (gpointer)bw);
+      (GTK_OBJECT (bw->full_screen_button), "clicked",
+       GTK_SIGNAL_FUNC (a_Commands_full_screen_callback), (gpointer)bw);
 }
 
+/*******************
+ * browser windows *
+ *******************/
+
 /*
  * Create a new browser window and return it.
  * (the new window is stored in browser_window[])
@@ -702,19 +551,23 @@ a_Interface_browser_window_new(gint widt
    BrowserWindow *bw;
    char buf[64];
 
-   /* We use g_new0() to zero the memory */
+   /* We use g_new0() to zero the memory, so it is not
+    *  necessary to NULLify individual pointers */
    bw = g_new0(BrowserWindow, 1);
    a_List_add(browser_window, num_bw, num_bw_max);
    browser_window[num_bw++] = bw;
 
-   /* initialize nav_stack struct in browser_window struct */
-   a_Nav_init(bw);
-
    if (!xid)
        bw->main_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    else
        bw->main_window = gtk_plug_new(xid);
 
+   /* set tooltips.
+    * TODO: this is a global, change to browser-window local?
+    */
+   bw->tooltips = tooltips;
+   gtk_tooltips_enable(bw->tooltips);
+
    gtk_window_set_policy(GTK_WINDOW(bw->main_window), TRUE, TRUE, FALSE);
    gtk_signal_connect(GTK_OBJECT(bw->main_window), "delete_event",
                       GTK_SIGNAL_FUNC(gtk_object_destroy), bw);
@@ -724,6 +577,8 @@ a_Interface_browser_window_new(gint widt
 
    gtk_window_set_wmclass(GTK_WINDOW(bw->main_window), "dillo", "Dillo");
 
+   gtk_widget_set_usize(bw->main_window, width, height);
+
    /* -RL :: I must realize the window to see it correctly */
    gtk_widget_realize(bw->main_window);
 
@@ -731,10 +586,6 @@ a_Interface_browser_window_new(gint widt
    bw->accel_group = gtk_accel_group_new();
    gtk_window_add_accel_group(GTK_WINDOW(bw->main_window), bw->accel_group);
 
-   /* set window title */
-   g_snprintf(buf, 64, "Version %s", VERSION);
-   a_Interface_set_page_title(bw, buf);
-
    box1 = gtk_vbox_new(FALSE, 0);
 
    /* setup the control panel */
@@ -830,121 +681,55 @@ a_Interface_browser_window_new(gint widt
    /* Add box1 */
    gtk_container_add(GTK_CONTAINER(bw->main_window), box1);
 
-   /* Now the main document window */
-   bw->docwin = a_Dw_gtk_scrolled_window_new();
-   gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(bw->docwin),
-                                  GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
-   gtk_box_pack_start(GTK_BOX(box1), bw->docwin, TRUE, TRUE, 0);
-   gtk_widget_show(bw->docwin);
-
-   /* todo (GTK2): this call is a bit of a workaround to missing focus
-    * between the location box and the docwin. It's fixed in GTK2:
-    * http://mail.gnome.org/archives/gtk-list/2003-June/msg00307.html  */
-   gtk_widget_grab_focus(GTK_BIN(bw->docwin)->child);
-
-   /* Full screen mode via double click is done in two ways: First,
-    * a feature of the selection is used, since in complex pages,
-    * getting events back to the viewport is quite difficult. Second,
-    * a simple callback, called e.g. when viewing image resources. */
-   a_Selection_set_dclick_callback(
-      GTK_DW_VIEWPORT(GTK_BIN(GTK_BIN(bw->docwin)->child)->child)->selection,
-      (void(*)(gpointer))Interface_full_screen_callback, bw);
-   /* Selection requires an owner widget */
-   a_Selection_set_owner(
-      GTK_DW_VIEWPORT(GTK_BIN(GTK_BIN(bw->docwin)->child)->child)->selection,
-      GTK_BIN(bw->docwin)->child);
-
-   gtk_signal_connect_object_after(GTK_OBJECT(GTK_BIN(bw->docwin)->child),
-                                   "button_press_event",
-                                   GTK_SIGNAL_FUNC(Interface_click_callback),
-                                   (gpointer)bw);
-
+   /* Create the full_screen_off_button. This button is shared between all
+    * documents in a tabbed browser. On switching tabs it is reparented
+    * to the new current tab */
    bw->full_screen_off_button = gtk_button_new ();
    gtk_button_set_relief (GTK_BUTTON (bw->full_screen_off_button),
                           GTK_RELIEF_NONE);
    gtk_tooltips_set_tip (tooltips, bw->full_screen_off_button,
                          "Show Controls", "Hide Controls");
    GTK_WIDGET_UNSET_FLAGS (bw->full_screen_off_button, GTK_CAN_FOCUS);
-   a_Dw_gtk_scrolled_window_add_gadget (GTK_DW_SCROLLED_WINDOW (bw->docwin),
-                                        bw->full_screen_off_button);
+
    pixmap = Interface_pixmap_new(bw->main_window, full_screen_off_xpm);
    gtk_container_add (GTK_CONTAINER (bw->full_screen_off_button), pixmap);
    gtk_widget_show (pixmap);
 
    gtk_signal_connect_object
       (GTK_OBJECT (bw->full_screen_off_button), "clicked",
-       GTK_SIGNAL_FUNC (Interface_full_screen_callback), (gpointer)bw);
-   Interface_set_button_accel(GTK_BUTTON(bw->full_screen_off_button), GDK_h,
-                              GDK_CONTROL_MASK, bw->accel_group);
-
-   /* Catch key_press event */
-   gtk_signal_connect(GTK_OBJECT(GTK_BIN(bw->docwin)->child),
-                      "key_press_event",
-                      GTK_SIGNAL_FUNC(Interface_key_press_handler), bw);
+       GTK_SIGNAL_FUNC (a_Commands_full_screen_callback), (gpointer)bw);
 
-   gtk_widget_set_usize(bw->main_window, width, height);
+   /* add tab browser */
+   bw->container = a_Tab_browser_new(bw);
+   /* Add initial tab */
+   a_Tab_new(bw);
+
+   gtk_box_pack_start(GTK_BOX(box1), GTK_WIDGET(bw->container), TRUE, TRUE, 0);
+   gtk_widget_show(GTK_WIDGET(bw->container));
 
    /* status widget */
    bw->status = a_Dw_gtk_statuslabel_new("");
    gtk_misc_set_alignment(GTK_MISC(bw->status), 0.0, 0.5);
    bw->status_box = gtk_hbox_new(FALSE, 0);
-
    gtk_box_pack_start(GTK_BOX(bw->status_box), bw->status, TRUE, TRUE, 2);
+   /* add search dialog window/entry */
+   Interface_add_search_dialog(bw);
    gtk_widget_show(bw->status);
-
+   /* findtext */
+   bw->found = FALSE;
+   bw->findtext_incremental = FALSE;
+   bw->findtext_incremental_connection_id = 0;
+   /* */
    Interface_add_full_screen_button (bw, GTK_BOX (bw->status_box));
-
    gtk_box_pack_start(GTK_BOX(box1), bw->status_box, FALSE, FALSE, 0);
    gtk_widget_show(bw->status_box);
 
    gtk_widget_show(bw->main_window);
    gtk_widget_show(box1);
 
-   /* initialize the rest of the bw's data. */
-   bw->pagemarks_menuitem = NULL;
-   bw->pagemarks_menu = NULL;
-   bw->pagemarks_last = NULL;
-
-   /* the image menu is created first because it is used by the link menu */
-   bw->menu_popup.over_image = a_Menu_popup_oi_new(bw);
-   bw->menu_popup.over_link = a_Menu_popup_ol_new(bw);
-   bw->menu_popup.over_page = a_Menu_popup_op_new(bw);
-   bw->menu_popup.over_back = NULL;
-   bw->menu_popup.over_forw = NULL;
-   bw->menu_popup.url = NULL;
-   bw->menu_popup.url2 = NULL;
-   bw->sens_idle_id = 0;
-
-   bw->CursorType = -1;
-
-   bw->RootClients = NULL;
-   bw->NumRootClients = 0;
-   bw->MaxRootClients = 8;
-
-   bw->ImageClients = NULL;
-   bw->NumImageClients = 0;
-   bw->MaxImageClients = 8;
-   bw->NumImages = 0;
-   bw->NumImagesGot = 0;
-
-   bw->PageUrls = NULL;
-   bw->NumPageUrls = 0;
-   bw->MaxPageUrls = 8;
-
-   bw->open_dialog_window = NULL;
-   bw->open_dialog_entry = NULL;
-   bw->openfile_dialog_window = NULL;
-   bw->quit_dialog_window = NULL;
-   bw->save_dialog_window = NULL;
-   bw->save_link_dialog_window = NULL;
-   bw->findtext_dialog_window = NULL;
-   bw->findtext_dialog_check = NULL;
-   bw->findtext_dialog_entry = NULL;
-   bw->search_dialog_window = NULL;
-   bw->search_dialog_entry = NULL;
-   bw->question_dialog_window = NULL;
-   bw->question_dialog_data = NULL;
-   bw->viewsource_window = NULL;
+   /* set document (and window) title */
+   g_snprintf(buf, 64, "Version %s", VERSION);
+   a_Doc_title_set(bw->dd, buf);
 
    /* now that the bw is made, let's customize it.. */
    Interface_browser_window_customize(bw);
@@ -952,6 +737,8 @@ a_Interface_browser_window_new(gint widt
    return bw;
 }
 
+
+
 /*
  * Set the title of the browser window to start with "Dillo: "
  * prepended to it.
@@ -974,6 +761,11 @@ void a_Interface_set_page_title(BrowserW
 void a_Interface_set_location_text(BrowserWindow *bw, char *text)
 {
    gtk_entry_set_text(GTK_ENTRY(bw->location), text);
+
+   /* set focus to location? */
+   if(!bw->fullwindow &&
+      !strcmp(text, ""))
+      gtk_widget_grab_focus(bw->location);
 }
 
 /*
@@ -1019,6 +811,15 @@ static void Interface_destroy_window(Gtk
    *window = NULL;
 }
 
+/*
+ * Called from `destroy' callback in Interface_make_*_dialog
+ */
+static gboolean Interface_hide_window(GtkWidget *widget, GdkEvent *event,
+				      gpointer user_data)
+{
+   gtk_widget_hide(widget);
+   return TRUE;
+}
 
 /*
  * Close and free every single browser_window (called at exit time)
@@ -1094,7 +895,7 @@ static void
    Cfn = (s = a_Misc_escape_chars(fn, "% ")) ? s : fn;
    g_string_sprintf(UrlStr, "file:%s", Cfn);
    url = a_Url_new(UrlStr->str, NULL, 0, 0);
-   a_Nav_push(bw, url);
+   a_Nav_push(bw->dd, url);
    a_Url_free(url);
    g_string_free(UrlStr, TRUE);
    g_free(s);
@@ -1102,74 +903,39 @@ static void
    gtk_widget_destroy(bw->openfile_dialog_window);
 }
 
-/* 
- * Returns a newly allocated string holding a search url generated from 
- * a string of keywords (separarated by blanks) and prefs.search_url.
- * Any '"' or '+' in the keywords are escaped. 
- */
-static gchar *Interface_make_search_url(const gchar *str)
-{
-   gchar *p, *keys, *new_str;
-
-   keys = g_new(gchar, 3*strlen(str)+1);
-
-   for (p = keys; *str; str++) {
-      switch(*str) {
-      case ' ':
-         *p++ = '+';
-         break;
-      case '"':
-         *p++ = '%';
-         *p++ = '2';
-         *p++ = '2';
-         break;
-      case '+':
-         *p++ = '%';
-         *p++ = '2';
-         *p++ = 'B';
-         break;
-      default:
-         *p++ = *str;
-      }
-   }
-   *p = '\0';
-
-   new_str = g_strdup_printf(prefs.search_url, keys);
-   g_free(keys);
-
-   return new_str;
-}
-
 /*
  * Open an url string.
- * The URL is not sent "as is", illegal chars are ripped out,
- * then it's fully parsed by a_Url_new().
+ * Process keywords and local content, filter the result,
+ * open url, grab focus for the scrolled_frame.
  */
 static void Interface_open_url_string(gchar *text, BrowserWindow *bw)
 {
-   gchar *new_text;
+   gchar *kw_url_str, *lc_url_str, *fl_url_str;
    DilloUrl *url;
 
    if (text && *text) {
+      /* process keywords */
+      kw_url_str = a_Url_string_process_keywords(text);
+      /* process local content (only absolute paths) */
+      lc_url_str = a_Url_string_process_local(kw_url_str, FALSE);
       /* Filter URL string */
-      new_text = a_Url_string_strip_delimiters(text);
-
-      url = a_Url_new(new_text, NULL, 0, 0);
+      fl_url_str = a_Url_string_strip_delimiters(lc_url_str);
+      url = a_Url_new(fl_url_str, NULL, 0, 0);
       if (url) {
-         a_Nav_push(bw, url);
+         a_Nav_push(bw->dd, url);
          a_Url_free(url);
       }
-      g_free(new_text);
+      g_free(kw_url_str);
+      g_free(lc_url_str);
+      g_free(fl_url_str);
    }
 
    /* let the rendered area have focus */
-   gtk_widget_grab_focus(GTK_BIN(bw->docwin)->child);
+   gtk_widget_grab_focus(GTK_BIN(bw->dd->docwin)->child);
 }
 
-/*
- * Open an URL specified in the location entry, or in the open URL dialog.
- */
-void a_Interface_entry_open_url(GtkWidget *widget, BrowserWindow *bw)
+void a_Interface_entry_open_url(GtkWidget *widget,
+				BrowserWindow *bw)
 {
    gchar *text;
    GtkEntry *entry;
@@ -1210,7 +976,7 @@ void a_Interface_openfile_dialog(Browser
  */
 static void
  Interface_make_dialog(GtkWidget **DialogWindow, char *WmName, char *WmClass,
-   char *WTitle, GtkWidget **DialogEntry, char *EntryStr,
+   char *WTitle, gboolean destroy, GtkWidget **DialogEntry, char *EntryStr,
    GtkWidget **CheckButton, char *CheckButtonText,
    char *B1Label, GtkSignalFunc B1CallBack, void *B1CbData)
 {
@@ -1221,8 +987,14 @@ static void
    gtk_window_set_wmclass(GTK_WINDOW(*DialogWindow), WmName, WmClass);
    gtk_window_set_position(GTK_WINDOW(*DialogWindow), GTK_WIN_POS_CENTER);
    gtk_window_set_title(GTK_WINDOW(*DialogWindow), WTitle);
-   gtk_signal_connect(GTK_OBJECT(*DialogWindow), "destroy",
-                      (GtkSignalFunc) Interface_destroy_window, DialogWindow);
+   if(destroy)
+     gtk_signal_connect(GTK_OBJECT(*DialogWindow), "destroy",
+			(GFunc) Interface_destroy_window,
+			DialogWindow);
+   else
+     gtk_signal_connect(GTK_OBJECT(*DialogWindow), "delete-event",
+			(GFunc) Interface_hide_window,
+			DialogWindow);
 
    /* Create and attach an accel group to the dialog window */
    accel_group = gtk_accel_group_new();
@@ -1235,11 +1007,13 @@ static void
    gtk_widget_show(box1);
 
    entry = gtk_entry_new();
-   GTK_WIDGET_SET_FLAGS(entry, GTK_HAS_FOCUS);
    gtk_widget_set_usize(entry, 250, 0);
    gtk_entry_set_text(GTK_ENTRY(entry), EntryStr);
    gtk_box_pack_start(GTK_BOX(box1), entry, FALSE, FALSE, 0);
    *DialogEntry = GTK_WIDGET(entry);
+   gtk_signal_connect_object(GTK_OBJECT(entry), "focus_in_event",
+			     a_Interface_editable_select_all,
+			     GTK_OBJECT(entry));
    gtk_widget_show(entry);
 
    if (CheckButton && CheckButtonText) {
@@ -1274,7 +1048,9 @@ static void
 
    button = gtk_button_new_with_label("Cancel");
    gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
-                             (GtkSignalFunc) gtk_widget_destroy,
+			     (destroy ?
+			      (GFunc) gtk_widget_destroy :
+			      (GFunc) gtk_widget_hide),
                              GTK_OBJECT(*DialogWindow));
    gtk_widget_add_accelerator(button, "clicked", accel_group,
                               GDK_Escape, 0, GTK_ACCEL_LOCKED);
@@ -1363,7 +1139,7 @@ void a_Interface_open_dialog(GtkWidget *
 {
    if (!bw->open_dialog_window) {
       Interface_make_dialog(&(bw->open_dialog_window),
-         "open_dialog", "Dillo", "Dillo: Open URL",
+         "open_dialog", "Dillo", "Dillo: Open URL", TRUE,
          &(bw->open_dialog_entry), "", NULL, NULL,
          "OK", (GtkSignalFunc) a_Interface_entry_open_url, (void *)bw);
       if (prefs.transient_dialogs)
@@ -1391,7 +1167,7 @@ static void Interface_save_callback(int 
       fflush(Web->stream);
       fstat(fileno(Web->stream), &st);
       fclose(Web->stream);
-      a_Interface_msg(Web->bw, "File saved (%d Bytes)", st.st_size);
+      a_Interface_msg(Web->dd->bw, "File saved (%d Bytes)", st.st_size);
    } else {
       if ( (Bytes = Client->BufSize - Web->SavedBytes) > 0 ) {
          Bytes = fwrite(Client->Buf + Web->SavedBytes, 1, Bytes, Web->stream);
@@ -1414,11 +1190,11 @@ static void Interface_file_save_url(GtkW
    choosefile = GTK_FILE_SELECTION(bw->save_dialog_window);
    entry_url = GTK_ENTRY(bw->location);
    name = gtk_file_selection_get_filename(choosefile);
-   url = a_Url_dup(a_History_get_url(NAV_TOP(bw)));
+   url = a_Url_dup(a_History_get_url(NAV_TOP(bw->dd)));
 
    if ( strlen(name) && (out = fopen(name, "w")) != NULL ) {
       DilloWeb *Web = a_Web_new(url);
-      Web->bw = bw;
+      Web->dd = bw->dd;
       Web->stream = out;
       Web->flags |= WEB_Download;
       /* todo: keep track of this client */
@@ -1442,21 +1218,21 @@ static void Interface_file_save_link(Gtk
 
    name = gtk_file_selection_get_filename(
              GTK_FILE_SELECTION(bw->save_link_dialog_window));
-   url  = a_Menu_popup_get_url(bw);
+   url  = a_Menu_popup_get_url(bw->dd);
 
    if (a_Capi_url_read(url, &sz) == NULL) {
       /* Not cached, ask the downloads server to get it */
       cmd = g_strdup_printf(
                "<dpi cmd='download' url='%s' destination='%s'>\n",
                URL_STR(url), name);
-      a_Capi_dpi_send_cmd(NULL, bw, cmd, "downloads", 1);
+      a_Capi_dpi_send_cmd(NULL, bw->dd, cmd, "downloads", 1);
       g_free(cmd);
 
    } else {
       /* Cached! Save from the cache */
       if ( strlen(name) && (out = fopen(name, "w")) != NULL ) {
          DilloWeb *Web = a_Web_new(url);
-         Web->bw = bw;
+         Web->dd = bw->dd;
          Web->stream = out;
          Web->flags |= WEB_Download;
          /* todo: keep track of this client */
@@ -1522,7 +1298,7 @@ void a_Interface_save_link_dialog(GtkWid
          (GtkSignalFunc) Interface_file_save_link,
          (void *)bw);
    }
-   SuggestedName = Interface_make_save_name(a_Menu_popup_get_url(bw));
+   SuggestedName = Interface_make_save_name(a_Menu_popup_get_url(bw->dd));
    gtk_file_selection_set_filename(
       GTK_FILE_SELECTION(bw->save_link_dialog_window), SuggestedName);
    g_free(SuggestedName);
@@ -1536,73 +1312,138 @@ void a_Interface_save_link_dialog(GtkWid
 /*
  * Scroll to an occurence of a string in the open page
  */
-static void Interface_entry_search(GtkWidget *widget, BrowserWindow* bw)
+void a_Interface_findtext_search(GtkWidget *widget, BrowserWindow* bw)
 {
    char *string;
    gboolean case_sens;
 
    string = gtk_editable_get_chars(GTK_EDITABLE(bw->findtext_dialog_entry),
-                                   0, -1);
+				   0, -1);
    case_sens = gtk_toggle_button_get_active
-      (GTK_TOGGLE_BUTTON(bw->findtext_dialog_check));
-   switch(a_Dw_gtk_scrolled_window_search(GTK_DW_SCROLLED_WINDOW(bw->docwin),
-                                          string, case_sens)) {
-   case FINDTEXT_RESTART:
-      a_Interface_message_window("Dillo: Find text",
-                                 "No further occurence of \"%s\". "
-                                 "Restarting from the beginning.", string);
-      break;
-   case FINDTEXT_NOT_FOUND:
-      a_Interface_message_window("Dillo: Find text",
-                                 "Cannot find \"%s\".", string);
-      break;
+     (GTK_TOGGLE_BUTTON(bw->findtext_dialog_check));
+
+   DEBUG_MSG(DEBUG_FIND,
+	     "a_Interface_findtext_search \"%s\", inc %i bw %p root %p\n",
+	     string, bw->findtext_incremental, bw, bw->findtext_root);   
+   
+   if(strcmp(string, "")) {
+     switch(a_Doc_findtext_search(bw->findtext_root, string, case_sens)) {
+     case FINDTEXT_RESTART:
+     case FINDTEXT_NOT_FOUND:
+       if(!bw->found) {
+	 if(prefs.popup_dialogs)
+	   a_Interface_message_window("Dillo: Find text",
+				      "Cannot find \"%s\".", string);
+	 else
+	   a_Interface_msg(bw, "(Find) \"%s\" not found", string);
+       } else {
+	 if(prefs.popup_dialogs)
+	   a_Interface_message_window("Dillo: Find text",
+				      "No further occurence of \"%s\". "
+				      "Restarting from the beginning.", string);
+	 else
+	   a_Interface_msg(bw, "(Find) \"%s\": restart from beginning" , string);
+       
+	 bw->found = FALSE;
+       }
+       break;
+     case FINDTEXT_SUCCESS:
+       bw->found = TRUE;
+       if(!prefs.popup_dialogs)
+	 a_Interface_msg(bw, "(Find) \"%s\"", string);
+       break;
+     default:
+       g_warning("Unexpected return from a_Dw_gtk_scrolled_window_search()\n");
+       break;
+     }
    }
+   
    g_free(string);
 }
 
 /*
- * Show the dialog interface for finding text in a page
+ * Callback for findtext hide, hides find UI elements
  */
-void a_Interface_findtext_dialog(BrowserWindow *bw)
+void a_Interface_findtext_hide(BrowserWindow *bw)
 {
-   if (!bw->findtext_dialog_window) {
-      Interface_make_dialog(&(bw->findtext_dialog_window),
-         "findtext_dialog", "Dillo", "Dillo: Find text in page",
-         &(bw->findtext_dialog_entry), "",
-         &(bw->findtext_dialog_check), "Case sensitive",
-         "Find", (GtkSignalFunc) Interface_entry_search, (void *)bw);
-      if (prefs.transient_dialogs)
-         gtk_window_set_transient_for(GTK_WINDOW(bw->findtext_dialog_window),
-                                      GTK_WINDOW(bw->main_window));
-      gtk_signal_connect_object
-         (GTK_OBJECT(bw->findtext_dialog_window), "destroy",
-          (GtkSignalFunc) a_Dw_gtk_scrolled_window_reset_search,
-          (void*)bw->docwin);
-      gtk_window_set_position(GTK_WINDOW(bw->findtext_dialog_window),
-                              GTK_WIN_POS_NONE);
+   DEBUG_MSG(DEBUG_FIND, "findtext_hide %p\n", bw);
+
+   if(prefs.popup_dialogs)
+     gtk_widget_hide(bw->findtext_dialog);
+   else {
+     a_Interface_msg(bw, "");
+     if(bw->findtext_current)
+       gtk_widget_grab_focus(GTK_WIDGET(GTK_BIN(bw->findtext_current->docwin)->child));
+     else
+       gtk_widget_grab_focus(GTK_WIDGET(GTK_BIN(bw->dd->docwin)->child));
+     if(GTK_WIDGET_VISIBLE(GTK_WIDGET(bw->findtext_dialog)))
+       gtk_widget_hide(GTK_WIDGET(bw->findtext_dialog));
+
+   }
+
+   /* unhighlight search term, but do not reset search */
+   if(bw->findtext_current) {
+     a_Dw_gtk_scrolled_window_reset_search((void *) bw->findtext_current->docwin, FALSE);
+     bw->findtext_current = NULL;
    }
+}
 
-   a_Interface_set_nice_window_pos(bw->findtext_dialog_window,
-                                   bw->main_window);
+/*
+ * Show the dialog interface for finding text in a page
+ */
+void a_Interface_findtext_show(BrowserWindow *bw)
+{
+   DEBUG_MSG(DEBUG_FIND, "a_Interface_findtext_show(%p)\n", bw);
 
-   if (!GTK_WIDGET_VISIBLE(bw->findtext_dialog_window))
-      gtk_widget_show(bw->findtext_dialog_window);
-   else
-      gdk_window_raise(bw->findtext_dialog_window->window);
+   if(prefs.popup_dialogs) {
+     /* show search popup */
+     a_Interface_set_nice_window_pos(bw->findtext_dialog,
+				     bw->main_window);
+     
+     if (!GTK_WIDGET_VISIBLE(bw->findtext_dialog))
+       gtk_widget_show_all(bw->findtext_dialog);
+     else
+       gdk_window_raise(bw->findtext_dialog->window);
+   } else {
+     /* show search entry in status bar and select contents */
+     if(bw->fullwindow)
+       gtk_widget_show(bw->status_box);
+     gtk_widget_show(bw->findtext_dialog);
+     gtk_widget_grab_focus(bw->findtext_dialog_entry);
+   }
+   
+   /* enable or disable incremental search */
+   if(bw->findtext_incremental &&
+      !bw->findtext_incremental_connection_id)
+     bw->findtext_incremental_connection_id = 
+       gtk_signal_connect(GTK_OBJECT(bw->findtext_dialog_entry),
+			  "changed", a_Interface_findtext_search, (void *) bw);
+   else if(!bw->findtext_incremental &&
+	   bw->findtext_incremental_connection_id) {
+     gtk_signal_disconnect(GTK_OBJECT(bw->findtext_dialog_entry),
+			   bw->findtext_incremental_connection_id);
+     bw->findtext_incremental_connection_id = 0;
+   }
 }
 
 /*
- * Use the search dialog's entry to feed a web search engine.
+ * Use the web search entry or location bar to feed a web search engine.
  */
 static void Interface_search_callback(GtkWidget *widget, BrowserWindow *bw)
 {
-   gchar *keyw, *url_str;
+   gchar *text, *keyw;
+   GtkEntry *entry;
 
-   keyw = gtk_editable_get_chars(GTK_EDITABLE(bw->search_dialog_entry), 0, -1);
-   if (keyw) { 
-      url_str = Interface_make_search_url(keyw);
-      Interface_open_url_string(url_str, bw);
-      g_free(url_str);
+   entry = GTK_ENTRY(widget == bw->location ? widget : bw->search_dialog_entry);
+
+   text = gtk_editable_get_chars(GTK_EDITABLE(entry), 0, -1);
+   if (text) {
+      if(strncmp("search " , text, 7))
+         keyw = g_strconcat("search ", text, NULL);
+      else
+         keyw = g_strdup(text);
+      Interface_open_url_string(keyw, bw);
+      g_free(text);
       g_free(keyw);
    }
    if (bw->search_dialog_window != NULL)
@@ -1611,23 +1452,53 @@ static void Interface_search_callback(Gt
 
 /*
  * Show the dialog interface for web search engine.
+ *  OR
+ * activate/initialize search through location bar
  */
 void a_Interface_search_dialog(GtkWidget *widget, BrowserWindow *bw)
 {
-   if (!bw->search_dialog_window) {
-      Interface_make_dialog(&(bw->search_dialog_window),
-         "search_dialog", "Dillo", "Dillo: Search the Web",
-         &(bw->search_dialog_entry), "", NULL, NULL,
-         "Search", (GtkSignalFunc)Interface_search_callback, (void *)bw);
-      if (prefs.transient_dialogs)
-         gtk_window_set_transient_for(GTK_WINDOW(bw->search_dialog_window),
-                                      GTK_WINDOW(bw->main_window));
+   if(prefs.popup_dialogs) {
+     /* use web search popup */
+     if (!bw->search_dialog_window) {
+       Interface_make_dialog(&(bw->search_dialog_window),
+			     "search_dialog", "Dillo",
+			     "Dillo: Search the Web",
+			     TRUE,
+			     &(bw->search_dialog_entry),
+			     "", NULL, NULL, "Search",
+			     (GtkSignalFunc)Interface_search_callback,
+			     (void *)bw);
+       if (prefs.transient_dialogs)
+	 gtk_window_set_transient_for(GTK_WINDOW(bw->search_dialog_window),
+				      GTK_WINDOW(bw->main_window));
+     }
+     
+     if (!GTK_WIDGET_VISIBLE(bw->search_dialog_window))
+       gtk_widget_show(bw->search_dialog_window);
+     else
+       gdk_window_raise(bw->search_dialog_window->window);
+   } else {
+     /* use location bar for web searching:
+      * if (location bar contains text and document is empty OR
+      *    location bar contents not equal current location)
+      *   activate search with location bar contents
+      * else
+      *   grab focus for location bar and enter search keyword
+      */
+     if(strcmp("search ", gtk_entry_get_text(GTK_ENTRY(bw->location))) &&
+	((bw->dd->nav_stack_ptr == -1 &&
+	  strcmp("", gtk_entry_get_text(GTK_ENTRY(bw->location)))) ||
+	 (bw->dd->nav_stack_ptr != -1 &&
+	  strcmp(URL_STR(a_History_get_url(NAV_TOP(bw->dd))),
+		 gtk_entry_get_text(GTK_ENTRY(bw->location))))))
+       Interface_search_callback(bw->location, bw);
+     else {
+       if(bw->fullwindow)
+	 g_slist_foreach(bw->PanelHandles, (GFunc)gtk_widget_show, NULL);
+       gtk_widget_grab_focus(bw->location);
+       gtk_entry_set_text(GTK_ENTRY(bw->location), "search ");
+     }
    }
-
-   if (!GTK_WIDGET_VISIBLE(bw->search_dialog_window))
-      gtk_widget_show(bw->search_dialog_window);
-   else
-      gdk_window_raise(bw->search_dialog_window->window);
 }
 
 /*
@@ -1638,7 +1509,7 @@ void a_Interface_scroll_popup(GtkWidget 
 {
    /*
     * todo:
-    *   1) Scrolling menues should rather be the task of Gtk+. This is
+    *   1) Scrolling menus should rather be the task of Gtk+. This is
     *      a hack, and I don't know if it does not break anything.
     *   2) It could be improved, e.g. a timeout could be added for
     *      better mouse navigation.
diff -pruN dillo/src/interface.h dillo_tabs/src/interface.h
--- dillo/src/interface.h	2003-11-24 14:32:40.114773992 +0100
+++ dillo_tabs/src/interface.h	2003-11-24 14:32:40.224757272 +0100
@@ -4,14 +4,8 @@
 #include "browser.h"
 
 void a_Interface_init(void);
-void a_Interface_stop(BrowserWindow *bw);
-void a_Interface_clean(BrowserWindow *bw);
-void a_Interface_quit_all(void);
 
-void a_Interface_add_client(BrowserWindow *bw, gint Key, gint Root);
-void a_Interface_remove_client(BrowserWindow *bw, gint ClientKey);
-void a_Interface_add_url(BrowserWindow *bw, const DilloUrl *Url, gint Flags);
-void a_Interface_close_client(BrowserWindow *bw, gint ClientKey);
+void a_Interface_quit_all(void);
 
 void a_Interface_msg(BrowserWindow *bw, const char *format, ... );
 
@@ -20,15 +14,28 @@ void a_Interface_open_dialog(GtkWidget *
 void a_Interface_save_dialog(GtkWidget *widget, BrowserWindow *bw);
 void a_Interface_save_link_dialog(GtkWidget *widget, BrowserWindow *bw);
 void a_Interface_search_dialog(GtkWidget *widget, BrowserWindow *bw);
-void a_Interface_findtext_dialog(BrowserWindow *bw);
 void a_Interface_quit_dialog(BrowserWindow *bw);
 
+/* findtext */
+void a_Interface_findtext_show(BrowserWindow *bw);
+void a_Interface_findtext_hide(BrowserWindow *bw);
+void a_Interface_findtext_search(GtkWidget *widget, BrowserWindow *bw);
+
 void a_Interface_set_page_title(BrowserWindow *bw, char *title);
 void a_Interface_set_location_text(BrowserWindow *bw, char *text);
 gchar *a_Interface_get_location_text(BrowserWindow *bw);
 void a_Interface_reset_progress_bars(BrowserWindow *bw);
 void a_Interface_entry_open_url(GtkWidget *widget, BrowserWindow *bw);
-void a_Interface_set_cursor(BrowserWindow *bw, GdkCursorType CursorType);
+
+void a_Interface_toggle_panel(BrowserWindow *bw, gboolean toggle);
+
+/* content (scrolled_frame and everything in it) focus callback */
+void a_Interface_content_focus_callback(BrowserWindow *bw);
+
+/* editable callbacks */
+void a_Interface_editable_select_all(GtkWidget *widget);
+void a_Interface_editable_deselect_all(GtkWidget *widget);
+
 BrowserWindow *
 a_Interface_browser_window_new(gint width, gint height, guint32 xid);
 
diff -pruN dillo/src/menu.c dillo_tabs/src/menu.c
--- dillo/src/menu.c	2003-11-24 14:32:40.116773688 +0100
+++ dillo_tabs/src/menu.c	2003-11-24 14:32:40.226756968 +0100
@@ -28,24 +28,74 @@
 #include "history.h"
 #include "nav.h"
 #include "commands.h"
-#include "dw_gtk_scrolled_window.h"
-#include "dw_page.h"         /* for a_Dw_page_add_anchor */
 #include "bookmark.h"
 #include "interface.h"
 #include "menu.h"
 #include "selection.h"
+#include "prefs.h"
+#include "dw_image.h"                  /* for link/image menu */
+#include "dw_gtk_scrolled_window.h"    /* for link/image menu */
 
+/* CALLBACKS */
 /*
- * Forward declarations
+ * Put the url in the status bar when moving cursor over menuitem.
+ */
+static void
+Menu_popup_history_select_callback(GtkWidget *widget, gpointer data)
+{
+   gint idx;
+   BrowserWindow *bw = data;
+
+   idx = GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT (widget), "nav_idx"));
+   if (idx >= 0 && idx < a_Nav_stack_size(bw->dd)) {
+      a_Interface_msg(bw, "%s", URL_STR_(a_History_get_url(NAV_IDX(bw->dd,idx))));
+      bw->status_is_link = 1;
+   }
+}
+
+/*
+ * Remove the url in the status bar when removing cursor from menuitem.
  */
-static void Menu_pagemarks_goto_pagemark(GtkWidget *widget,
-                                         gpointer client_data);
+static void
+Menu_popup_history_deselect_callback(GtkWidget *widget, gpointer data) {
+   BrowserWindow *bw = data;
+
+   a_Interface_msg(bw, "");
+}
+
+/*
+ * Set popup menu position
+ * Sets menu position to event origin
+ */
+void
+Menu_position_callback(GtkMenu *menu, gint *x, gint *y, gpointer data)
+{
+   GdkEventButton *event = (GdkEventButton *) data;
+   GtkRequisition *req = g_new(GtkRequisition, 1);
+   gint window_x, window_y;
+
+   gdk_window_get_origin(event->window, &window_x, &window_y);
+   gtk_widget_size_request(GTK_WIDGET(menu), req);
+
+   if(event->x + window_x != *x ||
+      event->y + window_y != *y) {
+      gtk_widget_size_request(GTK_WIDGET(menu), req);
+      *x = CLAMP(event->x + window_x - 2, 0,
+                 MAX (0, gdk_screen_width() - req->width));
+      *y = CLAMP(event->y + window_y - 2, 0,
+                 MAX (0, gdk_screen_height() - req->height));
+      g_free(req);
+   }
+}
+
+/* PRIVATE FUNCTIONS */
 
 /*
  * Make a new menu, insert it into the menu bar, and return it.
  */
-static GtkWidget* Menu_new(GtkWidget *menubar, const char *name,
-                           gboolean right_justify, BrowserWindow *bw)
+static GtkWidget *
+Menu_new(GtkWidget *menubar, const char *name,
+         gboolean right_justify, BrowserWindow *bw)
 {
    GtkWidget *menu;
    GtkWidget *menuitem;
@@ -74,10 +124,11 @@ void Menu_pick_url (GtkWidget *widget, g
    BrowserWindow *bw = (BrowserWindow *)client_data;
 
    if (gtk_object_get_data(GTK_OBJECT (GTK_MENU (widget->parent)), "url2")) {
-      a_Menu_popup_set_url(bw, bw->menu_popup.url2);
+      a_Menu_popup_set_url(bw->dd, bw->menu_popup.url2);
    }
    /* clean the url2 tag (currently "over_image" is the only one using it) */
-   gtk_object_remove_data(GTK_OBJECT (bw->menu_popup.over_image), "url2");
+   if(bw->menu_popup.over_image)
+      gtk_object_remove_data(GTK_OBJECT (bw->menu_popup.over_image), "url2");
 }
 
 /*
@@ -85,9 +136,9 @@ void Menu_pick_url (GtkWidget *widget, g
  * implemented), and a callback function for activation.
  */
 static GtkWidget *
- Menu_add (GtkWidget *menu, const char *name, const char *accel,
-           BrowserWindow *bw,
-           void (*callback) (GtkWidget *widget, void *data), void *data)
+Menu_add (GtkWidget *menu, const char *name, const char *accel,
+          BrowserWindow *bw,
+          void (*callback) (GtkWidget *widget, void *data), void *data)
 {
    GtkWidget *menuitem;
    GtkAccelGroup *menu_accels;
@@ -106,7 +157,7 @@ static GtkWidget *
    if (accel != NULL) {
       gtk_accelerator_parse(accel, &accel_key, &accel_mods);
       gtk_widget_add_accelerator(menuitem, "activate", bw->accel_group,
-         accel_key, (guint)accel_mods, GTK_ACCEL_VISIBLE);
+                                 accel_key, (guint)accel_mods, GTK_ACCEL_VISIBLE);
    }
    if (callback != NULL) {
       gtk_signal_connect(GTK_OBJECT(menuitem), "activate",
@@ -131,6 +182,130 @@ static void Menu_sep(GtkWidget *menu)
 }
 
 /*
+ * Add menu name
+ */
+static void Menu_name(GtkWidget *menu, gchar *name)
+{
+   GtkWidget *menuitem;
+
+   menuitem = gtk_menu_item_new_with_label((char *) name);
+   gtk_widget_set_sensitive(menuitem, FALSE);
+   gtk_widget_show(menuitem);
+   Menu_sep(menu);
+   gtk_menu_append(GTK_MENU(menu), menuitem);
+}
+
+/*
+ * Make a new frame menu and return it
+ */
+GtkWidget *Menu_popup_of_new(DilloDoc *dd)
+{
+   GtkWidget *menu;
+
+   menu = gtk_menu_new();
+
+   if(prefs.show_popup_navigation) {
+      gtk_widget_set_sensitive(Menu_add(menu, "Back", NULL, dd->bw,
+                                        a_Commands_doc_back_callback, dd),
+                               dd->back_sensitive);
+      gtk_widget_set_sensitive(Menu_add(menu, "Forward", NULL, dd->bw,
+					a_Commands_doc_forw_callback, dd),
+                               dd->forw_sensitive);
+      gtk_widget_set_sensitive(Menu_add(menu, "Stop", NULL, dd->bw,
+                                        a_Commands_doc_stop_callback, dd),
+                               dd->stop_sensitive);
+      Menu_add(menu, "_Reload", NULL, dd->bw,
+               a_Commands_doc_reload_callback, dd);
+      Menu_sep(menu);
+   }
+   Menu_add(menu, "View frame Source", NULL, dd->bw,
+	    a_Commands_doc_viewsource_callback, dd);
+   Menu_add(menu, "Bookmark this Frame", NULL, dd->bw,
+	    a_Commands_doc_addbm_callback, dd);
+   Menu_sep(menu);
+   Menu_add(menu, "Show only this Frame", NULL, dd->bw,
+	    a_Commands_doc_showonly_callback, dd);
+   Menu_add(menu, "Open Frame in new Window", NULL, dd->bw,
+	    a_Commands_open_link_nw_callback, dd->bw);
+   Menu_add(menu, "Open Frame in new Tab", NULL, dd->bw,
+	    a_Commands_open_link_nw_tab_callback, dd->bw);
+   Menu_sep(menu);
+   Menu_add(menu, "_Find Text in this Frame", NULL, dd->bw,
+	    a_Commands_findtext_callback, dd);
+   Menu_sep(menu);
+   Menu_add(menu, "Save Frame As...", NULL, dd->bw,
+	    a_Commands_doc_save_callback, dd);
+
+   Menu_name(menu, "    FRAME OPTIONS");
+   
+   return menu;
+}
+
+/*
+ * Make a new frameset menu and return it
+ *
+ */
+GtkWidget *Menu_popup_ofs_new(DilloDoc *dd)
+{
+   GtkWidget *frameset_menu;
+
+   frameset_menu = gtk_menu_new();
+   Menu_add(frameset_menu, "View Frameset Source", NULL, dd->bw,
+	    a_Commands_doc_viewsource_callback, dd->parent);
+   Menu_add(frameset_menu, "Bookmark this Frameset", NULL, dd->bw,
+	    a_Commands_doc_addbm_callback, dd->parent);
+   Menu_add(frameset_menu, "Reload this Frameset", NULL, dd->bw,
+	    a_Commands_doc_reload_callback, dd->parent);
+   Menu_sep(frameset_menu);
+   Menu_add(frameset_menu, "Show only this Frameset", NULL, dd->bw,
+	    a_Commands_doc_showonly_callback, dd->parent);
+   Menu_sep(frameset_menu);
+   Menu_add(frameset_menu, "_Find Text in this Frameset", NULL, dd->bw,
+	    a_Commands_findtext_callback, dd->parent);
+   Menu_sep(frameset_menu);
+   Menu_add(frameset_menu, "Save Frameset As...", NULL, dd->bw,
+	    a_Commands_doc_save_callback, dd->parent);
+
+   Menu_name(frameset_menu, "     FRAMESET OPTIONS");
+
+   
+   return frameset_menu;
+}
+
+/*
+ * Make a new popup menu for when the mouse is over an image
+ */
+GtkWidget *Menu_popup_oi_new (DilloDoc *dd)
+{
+   GtkWidget *menu;
+   GtkWidget *copy;
+
+   menu = gtk_menu_new();
+   Menu_add(menu, "Open Image in New Window", NULL, dd->bw,
+            a_Commands_open_link_nw_callback, dd->bw);
+   Menu_add(menu, "Open Image in New Tab", NULL, dd->bw,
+            a_Commands_open_link_nw_tab_callback, dd->bw);
+   Menu_add(menu, "Open Image", NULL, dd->bw,
+            a_Commands_open_link_callback, dd->bw);
+   Menu_sep(menu);
+   Menu_add(menu, "Bookmark this Image", NULL, dd->bw,
+            a_Commands_addbm_link_callback, dd->bw);
+   copy = Menu_add(menu, "Copy Image location", NULL, dd->bw,
+                   a_Commands_select_popup_url_callback, dd->bw);
+   a_Selection_init_selection(copy);
+
+   Menu_sep(menu);
+   Menu_add(menu, "Save Image As...", NULL, dd->bw,
+            a_Commands_save_link_callback, dd->bw);
+
+   Menu_name(menu, "     IMAGE OPTIONS");
+
+   return menu;
+}
+
+/* PUBLIC FUNCTIONS */
+
+/*
  * Make up a new menubar for a main browser window. The accelerator table
  * is stored in bw->accel_group.
  * Currently does not deal with dynamic menu items (bookmarks and history).
@@ -140,6 +315,8 @@ GtkWidget *a_Menu_mainbar_new(BrowserWin
 {
    GtkWidget *menubar;
    GtkWidget *file_menu;
+   GtkWidget *view_menu;
+
    /* GtkWidget *bookmarks_menu; */
    /* GtkWidget *help_menu; */
 
@@ -149,327 +326,330 @@ GtkWidget *a_Menu_mainbar_new(BrowserWin
    file_menu = Menu_new(menubar, tiny ? "_F" : "_File", FALSE, bw);
    Menu_add(file_menu, "_New Browser", "<ctrl>N", bw,
             a_Commands_new_callback, bw);
+   Menu_add(file_menu, "New _Tab", "<ctrl>T", bw,
+            a_Commands_new_tab_callback, bw);
+   Menu_sep(file_menu);
    Menu_add(file_menu, "_Open File...", "<ctrl>O", bw,
             a_Commands_openfile_callback, bw);
    Menu_add(file_menu, "Open _URL...", "<ctrl>L", bw,
             a_Commands_openurl_callback, bw);
    /*
-   Menu_add(file_menu, "_Preferences", "<ctrl>E", bw,
-            a_Commands_prefs_callback, bw);
+     Menu_add(file_menu, "_Preferences", "<ctrl>E", bw,
+     a_Commands_prefs_callback, bw);
    */
+   Menu_sep(file_menu);
    Menu_add(file_menu, "Close Window", "<ctrl>Q", bw,
             a_Commands_close_callback, bw);
+   Menu_add(file_menu, "Close Tab", "<ctrl>W", bw,
+            a_Commands_close_current_tab_callback, bw);
    Menu_sep(file_menu);
    Menu_add(file_menu, "Exit Dillo", "<alt>Q", bw,
             a_Commands_exit_callback, bw);
 
+   /* VIEW MENU */
+   view_menu = Menu_new(menubar, tiny? "_V" : "_View", FALSE, bw);
+   Menu_add(view_menu, "Previous tab", "<ctrl>comma", bw,
+            a_Commands_tab_prev_callback, bw);
+   Menu_add(view_menu, "Next tab", "<ctrl>period", bw,
+            a_Commands_tab_next_callback, bw);
+
    /* BOOKMARKS MENU
-   bookmarks_menu = Menu_new(menubar, tiny ? "_B" : "_Bookmarks", FALSE, bw);
-   bw->bookmarks_menu = bookmarks_menu;
-   Menu_add(bookmarks_menu, "_View Bookmarks", NULL, bw,
-            a_Commands_viewbm_callback, bw);
-   Menu_sep(bookmarks_menu);
+      bookmarks_menu = Menu_new(menubar, tiny ? "_B" : "_Bookmarks", FALSE, bw);
+      bw->bookmarks_menu = bookmarks_menu;
+      Menu_add(bookmarks_menu, "_View Bookmarks", NULL, bw,
+      a_Commands_viewbm_callback, bw);
+      Menu_sep(bookmarks_menu);
    */
 
    /* HELP MENU
-   help_menu = Menu_new(menubar, "_Help", TRUE, bw);
-   Menu_add(help_menu, "Dillo _Home", NULL, bw,
-            a_Commands_helphome_callback, bw);
+      help_menu = Menu_new(menubar, "_Help", TRUE, bw);
+      Menu_add(help_menu, "Dillo _Home", NULL, bw,
+      a_Commands_helphome_callback, bw);
    */
    return menubar;
 }
 
+/* NEW STYLE POPUP MENU's (using dd interface, created on demand) */
+
 /*
- * Make a new popup menu and return it.
+ * Popup page menu
  */
-GtkWidget *a_Menu_popup_op_new(BrowserWindow *bw)
+void a_Menu_popup_over_page(DilloDoc *dd, GdkEventButton *event)
 {
-   GtkWidget *menu;
+   GtkWidget *menu, *pagemarks_menuitem;
+
+   if(dd->bw->menu_popup.over_page)
+      gtk_widget_destroy(dd->bw->menu_popup.over_page);
 
    menu = gtk_menu_new();
-   Menu_sep(menu);
-   Menu_add(menu, "       PAGE OPTIONS", NULL, bw, NULL, NULL);
-   Menu_sep(menu);
-   Menu_add(menu, "View page Source", NULL, bw,
-            a_Commands_viewsource_callback, bw);
+   if(prefs.show_popup_navigation) {
+      gtk_widget_set_sensitive(
+                 Menu_add(menu, "Back", "<ctrl>comma", dd->bw,
+                          a_Commands_doc_back_callback, dd->bw->dd),
+                 dd->bw->dd->back_sensitive);
+      gtk_widget_set_sensitive(
+                 Menu_add(menu, "Forward", "<ctrl>period" , dd->bw,
+                          a_Commands_doc_forw_callback, dd->bw->dd),
+                 dd->bw->dd->forw_sensitive);
+      gtk_widget_set_sensitive(
+                 Menu_add(menu, "Stop", NULL, dd->bw,
+                          a_Commands_stop_callback, dd->bw->dd),
+                 dd->bw->dd->stop_sensitive);
+      Menu_add(menu, "_Reload", "<ctrl>r", dd->bw,
+               a_Commands_doc_reload_callback, dd->bw->dd);
+      Menu_sep(menu);
+   }
+   Menu_add(menu, "View page So_urce", "<alt>u", dd->bw,
+            a_Commands_doc_viewsource_callback, dd->bw->dd);
+   Menu_add(menu, "_Bookmark this Page", "<ctrl>b", dd->bw,
+            a_Commands_doc_addbm_callback, dd->bw->dd);
+   Menu_sep(menu);
+   Menu_add(menu, "Duplicate this Page", "<alt>n", dd->bw,
+            a_Commands_duplicate_callback, dd->bw->dd);
+   Menu_add(menu, "Duplicate this tab", "<alt>t", dd->bw,
+            a_Commands_duplicate_tab_callback, dd->bw->dd);
+   Menu_sep(menu);
+   Menu_add(menu, "_Find Text", "<ctrl>F", dd->bw,
+            a_Commands_findtext_callback, dd->bw->dd);
+
+   pagemarks_menuitem = Menu_add(menu, "Jump to...", NULL, dd->bw, NULL, NULL);
+
+   if(dd->pagemarks_menu) {
+      gtk_menu_item_set_submenu(GTK_MENU_ITEM(pagemarks_menuitem),
+                                dd->pagemarks_menu);
+      gtk_signal_connect_object(GTK_OBJECT(pagemarks_menuitem), "destroy",
+                                (GFunc) gtk_menu_detach,
+                                (gpointer) dd->pagemarks_menu);
+      gtk_widget_set_sensitive(pagemarks_menuitem, TRUE);
+   } else
+      gtk_widget_set_sensitive(pagemarks_menuitem, FALSE);
+
+   Menu_sep(menu);
+   Menu_add(menu, "Save Page As...", "<ctrl>s", dd->bw,
+            a_Commands_doc_save_callback, dd->bw->dd);
+
+   if(dd->parent) {
+      Menu_sep(menu);
+      gtk_menu_item_set_submenu(
+               GTK_MENU_ITEM(Menu_add(menu, "This Frame...",
+                                      NULL, dd->bw, NULL, NULL)),
+               Menu_popup_of_new(dd));
+      if((dd->parent)->parent)
+         gtk_menu_item_set_submenu(
+                  GTK_MENU_ITEM(Menu_add(menu, "This Frameset...",
+                                         NULL, dd->bw, NULL, NULL)),
+                  Menu_popup_ofs_new(dd->parent));
+   }
+   Menu_name(menu,"     PAGE OPTIONS");
 
-   Menu_add(menu, "Bookmark this page", NULL, bw,
-            a_Commands_addbm_callback, bw);
-   Menu_sep(menu);
-   Menu_add(menu, "_Find Text", "<ctrl>F", bw,
-            a_Commands_findtext_callback, bw);
 
-   bw->pagemarks_menuitem = Menu_add(menu, "Jump to...", NULL, bw, NULL, bw);
+   dd->bw->menu_popup.over_page = menu;
 
-   Menu_sep(menu);
-   Menu_add(menu, "Save page As...", NULL, bw,
-            a_Commands_save_callback, bw);
+   a_Menu_popup_set_url(dd->bw->dd, a_History_get_url(NAV_TOP(dd->bw->dd))); 
 
-   return menu;
-}
+   gtk_menu_popup(GTK_MENU(dd->bw->menu_popup.over_page), NULL, NULL,
+		  (GtkMenuPositionFunc) Menu_position_callback, event,
+                  event->button, event->time);
 
-/*
- * Set the popup's primary DilloUrl
- */
-void a_Menu_popup_set_url(BrowserWindow *bw, DilloUrl *url)
-{
-   if (bw->menu_popup.url)
-      a_Url_free(bw->menu_popup.url);
-   bw->menu_popup.url = a_Url_dup(url);
 }
 
 /*
- * Set the popup's secondary DilloUrl
- * Note: this URL is used by the image submenu of the link popup.
- * (If the need arises, this can be extended using an URL array in the popup
- * structure, and by setting the to-be-picked-URL-index in the popup)
+ * Popup tab menu
  */
-void a_Menu_popup_set_url2(BrowserWindow *bw, DilloUrl *url)
+void a_Menu_popup_over_tab(DilloDoc *dd, GdkEventButton *event)
 {
-   if (bw->menu_popup.url2)
-      a_Url_free(bw->menu_popup.url2);
-   bw->menu_popup.url2 = a_Url_dup(url);
-}
+   GtkWidget *menu;
 
-/*
- * Get the popup's DilloUrl
- */
-DilloUrl *a_Menu_popup_get_url(BrowserWindow *bw)
-{
-   return bw->menu_popup.url;
+   if (dd->bw->menu_popup.over_tab)
+      gtk_widget_destroy(dd->bw->menu_popup.over_tab);
+
+   menu = gtk_menu_new();
+
+   Menu_add(menu, "Close Tab", NULL, dd->bw,
+	    a_Commands_close_tab_callback, dd);
+   Menu_add(menu, "New Tab", NULL, dd->bw,
+	    a_Commands_new_tab_callback, dd->bw);
+   Menu_add(menu, "Reload Tab", NULL, dd->bw,
+	    a_Commands_doc_reload_callback, dd);
+
+   Menu_name(menu, "TAB OPTIONS");
+
+   dd->bw->menu_popup.over_tab = menu;
+   
+   gtk_menu_popup(GTK_MENU(dd->bw->menu_popup.over_tab), NULL, NULL,
+		  (GtkMenuPositionFunc) Menu_position_callback, event,
+                  event->button, event->time);
 }
 
 /*
- * Put the url in the status bar when moving cursor over menuitem.
+ * Popup link menu
+ * Add image submenu if this is an image link
  */
-static void
-Menu_popup_history_select_callback(GtkWidget *widget, gpointer data)
+void a_Menu_popup_over_link(DilloDoc *dd, GdkEventButton *event)
 {
-   gint idx;
-   BrowserWindow *bw = data;
+   GtkWidget *menu;
+   GtkWidget *copy;
+   GtkWidget *image_menu;
+   DwWidget *widget_at_cursor;
+   DwImage *image;
 
-   idx = GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT (widget), "nav_idx"));
-   if (idx >= 0 && idx < a_Nav_stack_size(bw)) {
-      a_Interface_msg(bw, "%s", URL_STR_(a_History_get_url(NAV_IDX(bw,idx))));
-      bw->status_is_link = 1;
+   if (dd->bw->menu_popup.over_link)
+      gtk_widget_destroy(dd->bw->menu_popup.over_link);
+
+   menu = gtk_menu_new();
+   Menu_add(menu, "Open Link in New Window", NULL, dd->bw,
+            a_Commands_open_link_nw_callback, dd->bw);
+   Menu_add(menu, "Open Link in New Tab", NULL, dd->bw,
+            a_Commands_open_link_nw_tab_callback, dd->bw);
+   Menu_add(menu, "Open Link", NULL, dd->bw,
+            a_Commands_open_link_callback, dd->bw);
+   Menu_sep(menu);
+   Menu_add(menu, "Bookmark this Link", NULL, dd->bw,
+            a_Commands_addbm_link_callback, dd->bw);
+
+   copy = Menu_add(menu, "Copy Link location", NULL, dd->bw,
+                   a_Commands_select_popup_url_callback, dd->bw);
+   a_Selection_init_selection(copy);
+
+   Menu_sep(menu);
+   Menu_add(menu, "Save Link As...", NULL, dd->bw,
+            a_Commands_save_link_callback, dd->bw);
+
+   widget_at_cursor =
+      a_Dw_gtk_scrolled_window_widget_at_viewport_point(
+          GTK_DW_SCROLLED_WINDOW (dd->docwin), event->x, event->y);
+   if (widget_at_cursor && DW_IS_IMAGE (widget_at_cursor)) {
+      image = DW_IMAGE (widget_at_cursor);
+      /* test image->url (it may have not started to arrive yet!) */
+      if (image->url) {
+         Menu_sep(menu);
+         image_menu = Menu_popup_oi_new(dd);
+         /* use the second URL for this popup */
+         gtk_object_set_data(GTK_OBJECT (image_menu),
+                             "url2", GINT_TO_POINTER(2));
+         a_Menu_popup_set_url2(dd, image->url);
+         gtk_menu_item_set_submenu(
+               GTK_MENU_ITEM(Menu_add(menu, "This Image...",
+                                      NULL, dd->bw, NULL, NULL)),
+               image_menu);
+      }
    }
+
+   Menu_name(menu, "     LINK OPTIONS");
+
+   dd->bw->menu_popup.over_link = menu;
+   
+   gtk_menu_popup(GTK_MENU(dd->bw->menu_popup.over_link), NULL, NULL,
+		  (GtkMenuPositionFunc) Menu_position_callback, event,
+                  event->button, event->time);
 }
 
 /*
- * Remove the url in the status bar when removing cursor from menuitem.
+ * Popup image menu
  */
-static void
-Menu_popup_history_deselect_callback(GtkWidget *widget, gpointer data) {
-   BrowserWindow *bw = data;
+void a_Menu_popup_over_image(DilloDoc *dd, GdkEventButton *event)
+{
+   if (dd->bw->menu_popup.over_image)
+      gtk_widget_destroy(dd->bw->menu_popup.over_image);
 
-   a_Interface_msg(bw, "");
+   dd->bw->menu_popup.over_image = Menu_popup_oi_new(dd);
+   
+   gtk_menu_popup(GTK_MENU(dd->bw->menu_popup.over_image), NULL, NULL,
+		  (GtkMenuPositionFunc) Menu_position_callback, event,
+                  event->button, event->time);
 }
 
 /*
- * Make a new popup menu for history browsing
+ * Popup menu for history browsing
  * direction: {backward = -1, forward = 1}
  */
-GtkWidget *a_Menu_popup_history_new(BrowserWindow *bw, gint direction)
+void a_Menu_popup_over_history(DilloDoc *dd, GdkEventButton *event,
+                               gint direction)
 {
    int i;
    GtkWidget *menu, *menu_item;
    GString *text = g_string_sized_new(128);
 
+   if (dd->bw->menu_popup.over_history)
+      gtk_widget_destroy(dd->bw->menu_popup.over_history);
+
    menu = gtk_menu_new();
-   Menu_sep(menu);
-   if (direction>0) {
-      Menu_add(menu, "     FOLLOWING PAGES     ", NULL, bw, NULL, NULL);
-      i = a_Nav_stack_ptr(bw) + 1;
-   } else {
-      Menu_add(menu, "     PREVIOUS PAGES     ", NULL, bw, NULL, NULL);
-      i = a_Nav_stack_ptr(bw) - 1;
-   }
-   Menu_sep(menu);
+   if (direction>0)
+      i = a_Nav_stack_ptr(dd) + 1;
+   else
+      i = a_Nav_stack_ptr(dd) - 1;
 
-   for ( ; i >= 0 && i < a_Nav_stack_size(bw); i+=direction ) {
-      if (a_History_get_title(NAV_IDX(bw,i))) {
-         g_string_assign(text, a_History_get_title(NAV_IDX(bw,i)));
+   for ( ; i >= 0 && i < a_Nav_stack_size(dd); i+=direction ) {
+      if (a_History_get_title(NAV_IDX(dd,i))) {
+         g_string_assign(text, a_History_get_title(NAV_IDX(dd,i)));
       } else {
-         g_string_assign(text, URL_STR(a_History_get_url(NAV_IDX(bw,i))));
+         g_string_assign(text, URL_STR(a_History_get_url(NAV_IDX(dd,i))));
       }
       if ( text->len > 64 ) {
          g_string_truncate(text, 64);
          g_string_append(text, "...");
       }
-      menu_item = Menu_add(menu, text->str, NULL, bw, NULL, NULL);
+      menu_item = Menu_add(menu, text->str, NULL, dd->bw, NULL, NULL);
       /* attach the nav_stack index to the menu item */
       gtk_object_set_data(GTK_OBJECT (menu_item), "nav_idx",
                           GINT_TO_POINTER(i));
 
       gtk_signal_connect (
-         GTK_OBJECT (menu_item), "select",
-         GTK_SIGNAL_FUNC (a_Interface_scroll_popup), NULL);
+                 GTK_OBJECT (menu_item), "select",
+                 GTK_SIGNAL_FUNC (a_Interface_scroll_popup), NULL);
       gtk_signal_connect (
-         GTK_OBJECT (menu_item), "select",
-         GTK_SIGNAL_FUNC (Menu_popup_history_select_callback), bw);
+                 GTK_OBJECT (menu_item), "select",
+                 GTK_SIGNAL_FUNC (Menu_popup_history_select_callback), dd->bw);
       gtk_signal_connect (
-         GTK_OBJECT (menu_item), "deselect",
-         GTK_SIGNAL_FUNC (Menu_popup_history_deselect_callback), bw);
+                 GTK_OBJECT (menu_item), "deselect",
+                 GTK_SIGNAL_FUNC (Menu_popup_history_deselect_callback), dd->bw);
       gtk_signal_connect (
-         GTK_OBJECT (menu_item), "button-press-event",
-         GTK_SIGNAL_FUNC (a_Commands_historypress_callback), bw);
+                 GTK_OBJECT (menu_item), "button-press-event",
+                 GTK_SIGNAL_FUNC (a_Commands_historypress_callback), dd->bw);
    }
 
-   g_string_free(text, TRUE);
-   return menu;
-}
-
-/*
- * Make a new popup menu for when the mouse is over a link.
- */
-GtkWidget *a_Menu_popup_ol_new(BrowserWindow *bw)
-{
-   GtkWidget *menu;
-   GtkWidget *copy;
-   GtkWidget *menuitem;
-
-   menu = gtk_menu_new();
-   Menu_sep(menu);
-   Menu_add(menu, "       LINK OPTIONS", NULL, bw, NULL, NULL);
-   Menu_sep(menu);
-   Menu_add(menu, "Open Link in New Window", NULL, bw,
-            a_Commands_open_link_nw_callback, bw);
-   Menu_add(menu, "Bookmark this Link", NULL, bw,
-            a_Commands_addbm_callback, bw);
-
-   copy = Menu_add(menu, "Copy Link location", NULL, bw,
-                   a_Commands_select_popup_url_callback, bw);
-   a_Selection_init_selection(copy);
-
-   Menu_sep(menu);
-   Menu_add(menu, "Save Link As...", NULL, bw,
-            a_Commands_save_link_callback, bw);
-
-   Menu_sep(menu);
-   menuitem = Menu_add(menu, "Image Menu...", NULL, bw, NULL, bw);
-   gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem),
-                             bw->menu_popup.over_image);
-   bw->menu_popup.ol_oi_submenu = menuitem;
-
-   return menu;
-}
-
-/*
- * Make a new popup menu for when the mouse is over an image
- */
-GtkWidget *a_Menu_popup_oi_new (BrowserWindow *bw)
-{
-   GtkWidget *menu;
-   GtkWidget *copy;
+   Menu_name(menu, direction > 0 ?
+             "FOLLOWING PAGES" :
+             "PREVIOUS PAGES");
 
-   menu = gtk_menu_new();
-   Menu_sep(menu);
-   Menu_add(menu, "       IMAGE OPTIONS", NULL, bw, NULL, NULL);
-   Menu_sep(menu);
-   Menu_add(menu, "Isolate Image", NULL, bw,
-            a_Commands_open_link_callback, bw);
-   Menu_add(menu, "Open Image in New Window", NULL, bw,
-            a_Commands_open_link_nw_callback, bw);
-   Menu_add(menu, "Bookmark this Image", NULL, bw,
-            a_Commands_addbm_callback, bw);
-   copy = Menu_add(menu, "Copy Image location", NULL, bw,
-                   a_Commands_select_popup_url_callback, bw);
-   a_Selection_init_selection(copy);
+   g_string_free(text, TRUE);
 
-   Menu_sep(menu);
-   Menu_add(menu, "Save Image As...", NULL, bw,
-            a_Commands_save_link_callback, bw);
-   return menu;
-}
+   dd->bw->menu_popup.over_history = menu;
 
-/*
- * Show or Hide the image submenu.
- */
-void a_Menu_popup_ol_show_oi(BrowserWindow *bw, gboolean show)
-{
-   if (show)
-      gtk_widget_show(bw->menu_popup.ol_oi_submenu);
-   else
-      gtk_widget_hide(bw->menu_popup.ol_oi_submenu);
+   gtk_menu_popup(GTK_MENU(dd->bw->menu_popup.over_history), NULL, NULL,
+		  (GtkMenuPositionFunc) Menu_position_callback, event,
+		  event->button, event->time);
 }
 
 /*
- * Functions to manipulate Pagemarks menu.
- * Should this be in a separate "pagemark.c" ?
- */
-
-/*
- * make the page scroll to the pagemark
+ * Set the popup's primary DilloUrl
  */
-static void Menu_pagemarks_goto_pagemark(GtkWidget *widget,
-                                         gpointer client_data)
+void a_Menu_popup_set_url(DilloDoc *dd, DilloUrl *url)
 {
-   BrowserWindow *bw = (BrowserWindow *) client_data;
-   char anchor[32];
-
-   g_snprintf (anchor, 32, "#%ld", (long int)widget);
-   a_Dw_gtk_scrolled_window_set_anchor(GTK_DW_SCROLLED_WINDOW(bw->docwin),
-                                       anchor);
+   if (dd->bw->menu_popup.url)
+      a_Url_free(dd->bw->menu_popup.url);
+   dd->bw->menu_popup.url = a_Url_dup(url);
 }
 
 /*
- * Deallocate the memory used by a pagemarks menu and create a new one
+ * Set the popup's secondary DilloUrl
+ * Note: this URL is used by the image submenu of the link popup.
+ * (If the need arises, this can be extended using an URL array in the popup
+ * structure, and by setting the to-be-picked-URL-index in the popup)
  */
-void a_Menu_pagemarks_new(BrowserWindow *bw)
+void a_Menu_popup_set_url2(DilloDoc *dd, DilloUrl *url)
 {
-   gtk_widget_set_sensitive(bw->pagemarks_menuitem, FALSE);
-
-   if (bw->pagemarks_menu)
-      gtk_widget_destroy(bw->pagemarks_menu);
-   bw->pagemarks_menu = gtk_menu_new();
-   bw->pagemarks_last = NULL;
-
-   gtk_menu_item_set_submenu(GTK_MENU_ITEM(bw->pagemarks_menuitem),
-                             bw->pagemarks_menu);
+   if (dd->bw->menu_popup.url2)
+      a_Url_free(dd->bw->menu_popup.url2);
+   dd->bw->menu_popup.url2 = a_Url_dup(url);
 }
 
 /*
- * Add a new pagemark (opening H tag).
- * The text can be set only at the closing H tag.
- * level is the level of the heading (1-6).
+ * Get the popup's DilloUrl
  */
-void a_Menu_pagemarks_add(BrowserWindow *bw, void *page, void *style,
-                          gint level)
+DilloUrl *a_Menu_popup_get_url(DilloDoc *dd)
 {
-   char anchor[32], spaces[32], name[32];
-
-   gtk_widget_set_sensitive(bw->pagemarks_menuitem, TRUE);
-   g_snprintf(spaces, 32, "%*s", 3 * (level - 1), "");
-   bw->pagemarks_last = Menu_add(bw->pagemarks_menu, spaces, NULL, bw,
-                                 Menu_pagemarks_goto_pagemark, bw);
-   g_snprintf(anchor, 32, "#%ld", (glong)(bw->pagemarks_last));
-   a_Dw_page_add_anchor(page, anchor, style);
-   gtk_signal_connect (GTK_OBJECT (bw->pagemarks_last), "select",
-                       GTK_SIGNAL_FUNC (a_Interface_scroll_popup), NULL);
-   g_snprintf(name, 32, "dilloHeading%d", level);
-   gtk_widget_set_name(bw->pagemarks_last, name);
+   return dd->bw->menu_popup.url;
 }
 
-/*
- * Set the text for the last created pagemark.
- */
-void a_Menu_pagemarks_set_text(BrowserWindow *bw, const char *str)
-{
-   GString *text;
-   GtkWidget *child;
 
-   /* Avoid problems with lonely closing tags and nested headings */
-   if ( bw->pagemarks_last &&
-       (child = GTK_BIN (bw->pagemarks_last)->child) &&
-       GTK_IS_LABEL (child) ) {
-      text = g_string_new(GTK_LABEL(child)->label);
-      g_string_append(text, str);
-      if ( text->len > 64 ) {
-         g_string_truncate(text, 64);
-         g_string_append(text, "...");
-      }
-      gtk_label_set_text(GTK_LABEL (child), text->str);
-      g_string_free(text, 1);
-      bw->pagemarks_last = NULL;
-   }
-}
diff -pruN dillo/src/menu.h dillo_tabs/src/menu.h
--- dillo/src/menu.h	2003-11-24 14:32:40.116773688 +0100
+++ dillo_tabs/src/menu.h	2003-11-24 14:32:40.227756816 +0100
@@ -4,19 +4,16 @@
 #include <gtk/gtk.h>
 
 GtkWidget *a_Menu_mainbar_new (BrowserWindow *bw, gint tiny);
-GtkWidget *a_Menu_popup_op_new (BrowserWindow *bw);
-GtkWidget *a_Menu_popup_ol_new (BrowserWindow *bw);
-GtkWidget *a_Menu_popup_oi_new (BrowserWindow *bw);
-GtkWidget *a_Menu_popup_history_new(BrowserWindow *bw, gint direction);
-void a_Menu_popup_ol_show_oi(BrowserWindow *bw, gboolean show);
-void a_Menu_popup_set_url(BrowserWindow *bw, DilloUrl *url);
-void a_Menu_popup_set_url2(BrowserWindow *bw, DilloUrl *url);
-DilloUrl *a_Menu_popup_get_url(BrowserWindow *bw);
-
-void a_Menu_pagemarks_new (BrowserWindow *bw);
-void a_Menu_pagemarks_destroy (BrowserWindow *bw);
-void a_Menu_pagemarks_add(BrowserWindow *bw, void *page, void *style,
-                          gint level);
-void a_Menu_pagemarks_set_text(BrowserWindow *bw, const char *str);
+/* new style (with dd interface, created on demand) */
+void a_Menu_popup_over_page (DilloDoc *dd, GdkEventButton *event);
+void a_Menu_popup_over_tab(DilloDoc *dd, GdkEventButton *event);
+void a_Menu_popup_over_link(DilloDoc *dd, GdkEventButton *event);
+void a_Menu_popup_over_image(DilloDoc *dd, GdkEventButton *event);
+void a_Menu_popup_over_history(DilloDoc *dd, GdkEventButton *event,
+                               gint direction);
+/* old style (with bw interface) */
+void a_Menu_popup_set_url(DilloDoc *dd, DilloUrl *url);
+void a_Menu_popup_set_url2(DilloDoc *dd, DilloUrl *url);
+DilloUrl *a_Menu_popup_get_url(DilloDoc *dd);
 
 #endif /* MENU_H */
diff -pruN dillo/src/misc.c dillo_tabs/src/misc.c
--- dillo/src/misc.c	2003-11-24 14:32:40.117773536 +0100
+++ dillo_tabs/src/misc.c	2003-11-24 14:32:40.227756816 +0100
@@ -19,6 +19,8 @@
 #include <ctype.h>
 
 #include "misc.h"
+#include "prefs.h"
+
 
 /*
  * Prepend the users home-dir to 'file' string i.e,
@@ -265,3 +267,143 @@ const gchar *a_Misc_get_content_type_fro
    return (Types[Type]);
 }
 
+/*
+ * (adapted from Galeon's misc_string_shorten_name())
+ * try to shorten a string to target_length or less
+ * this is quite language-specific, ie. it will not work
+ * with non indo-european languages
+ *
+ * minimum returned string length is 1, even if target_length
+ * is set to a lower value
+ *
+ * caller has to free the returned string
+ */
+gchar *
+a_Misc_string_compress(const gchar *input, gint target_length)
+{
+   gint i, j, length;
+   gchar *name, **common_prefixes = prefs.compress_common_prefixes_vec;
+   gchar *shortened_name;
+   char c;
+
+   /* it does not make sense to compress a string into nothing... */
+   if (target_length < 1)
+     target_length = 1;
+   
+   if (input == NULL)
+     return (a_Misc_string_shorten(g_strdup("Untitled"),target_length));
+   
+   /* copy and clean name */
+   name = g_strdup (input);
+   g_strstrip (name);
+   length = strlen (name);
+
+   /* remove common prefixes from string */
+   if (common_prefixes)
+     for(i=0; common_prefixes[i]; ++i)
+       if(strncasecmp(name, common_prefixes[i],strlen(common_prefixes[i])) == 0) {
+         length -= strlen(common_prefixes[i]);
+         memmove(name, name + strlen(common_prefixes[i]), length + 1);
+       }
+
+   /* remove trailing whitespace, if any */
+   g_strchug(name);
+   
+   /* remove vowels from string */
+   for (i = length - 1, j = length - 1; i >= 0; i--) {
+     c = name[i];
+     /* keep the letter if the string is short enough
+      * already, if the letter is at the beginning of a
+      * word, or if it's not a lowercase vowel */
+     if (length <= target_length || i == 0 ||
+         (i > 0 && name[i - 1] == ' ') ||
+         /* COMPRESS_VOWELS contains those characters that
+	    can be skipped without making a word totally
+            irrecognizable. In most languages, those are the
+            vowels (with and without accents) */
+         !strchr (COMPRESS_VOWELS, c))
+       {
+         name[j] = c;
+         j--;
+       }
+     else
+       {
+         length--;
+       }
+   }
+   /* shift */
+   memmove(name, name + j + 1, length + 1);
+   
+   /* short enough yet? */
+   if (length <= target_length)
+     {
+       return name;
+     }
+
+   /* no - try chopping out whole words */
+   for (i = target_length; i > 0; i--) {
+     if (ispunct((int) name[i]) || isspace((int) name[i]))
+       {
+         while (ispunct((int) name[i]) || isspace((int) name[i]))
+           {
+             i--;
+           }
+         /* this will do */
+         name[i + 1] = '\0';
+         g_strchomp (name);
+         return name;
+       }
+   }
+   
+   /* still too long - use shortening on the compressed string */
+   shortened_name = a_Misc_string_shorten(name, target_length);
+   g_free(name);
+   return shortened_name;
+}
+
+/*
+ * shorten a string to no more than target_length characters
+ * by replacing part of the middle of the string by
+ * three ellipsis ('...').
+ *
+ * minimum returned string length is 3, even if target_length
+ * is set to a lower value.
+ *
+ * caller must free returned string
+ */
+gchar *
+a_Misc_string_shorten(const gchar *input, gint target_length)
+{
+   gchar *output;
+   gint actual_length, first_length, second_length;
+   
+   if (!input) return NULL;
+   
+   /* target length can not be smaller than 3 */
+   if (target_length < 3)
+     target_length = 3;
+   
+   actual_length = strlen (input);
+   
+   /* if the string is already short enough, or if it's too short for
+    * us to shorten it, return a new copy */
+   if (actual_length <= target_length ||
+       actual_length <= 3)
+     return g_strdup (input);
+   
+   /* allocate new string */
+   output = g_new (gchar, target_length + 1);
+   
+   /* calc lengths to take from beginning and ending of input */
+   second_length = (target_length - 3) / 2;
+   first_length = target_length - 3 - second_length;
+   
+   /* create string */
+   strncpy (output, input, first_length);
+   strncpy (output + first_length, "...", 3);
+   strncpy (output + first_length + 3,
+            input + actual_length - second_length, second_length);
+   output[target_length] = '\0';
+   
+   return output;
+}
diff -pruN dillo/src/misc.h dillo_tabs/src/misc.h
--- dillo/src/misc.h	2003-11-24 14:32:40.117773536 +0100
+++ dillo_tabs/src/misc.h	2003-11-24 14:32:40.228756664 +0100
@@ -2,6 +2,8 @@
 #define __MISC_H__
 #include <stdlib.h>
 
+#define COMPRESS_VOWELS "aeiouyåäöøæAEIOUYÅÄÖØÆ"
+
 gchar *a_Misc_prepend_user_home(const char *file);
 gchar *a_Misc_escape_chars(const gchar *str, gchar *esc_set);
 gchar *a_Misc_stuff_chars(const gchar *str, const gchar *esc_set);
@@ -12,7 +14,8 @@ gchar *a_Misc_strsep(char **orig, const 
 gint *a_Misc_strsplitpos(const gchar *str, const gchar *delim);
 gint *a_Misc_strsplitposdup(gint *pos);
 const gchar *a_Misc_get_content_type_from_data(void *Data, size_t Size);
-
+gchar *a_Misc_string_compress(const gchar *input, gint target_length);
+gchar *a_Misc_string_shorten(const gchar *input, gint target_length);
 /* Return a NULL-terminated string containing the characters from p1 to p2. */
 #define a_Misc_strpdup(s, p1, p2) g_strndup((s) + (p1), (p2) - (p1))
 
diff -pruN dillo/src/nav.c dillo_tabs/src/nav.c
--- dillo/src/nav.c	2003-11-24 14:32:40.117773536 +0100
+++ dillo_tabs/src/nav.c	2003-11-24 14:32:40.228756664 +0100
@@ -19,43 +19,46 @@
 #include "nav.h"
 #include "history.h"
 #include "web.h"
+#include "doc.h"
 #include "menu.h"
+#include "pagemark.h"
 #include "interface.h"
 #include "dw_gtk_scrolled_window.h"
 #include "prefs.h"
 #include "commands.h"
 #include "capi.h"
 
-//#define DEBUG_LEVEL 3
+#define DEBUG_EVENT  0
+/* #define DEBUG_LEVEL 3 */
 #include "debug.h"
 
 /*
  * Forward declarations
  */
-static void Nav_reload(BrowserWindow *bw);
-void a_Nav_cancel_expect(BrowserWindow *bw);
+static void Nav_reload(DilloDoc *dd);
+void a_Nav_cancel_expect(DilloDoc *dd);
 
 
 /*
  * Initialize the navigation structure with safe values
  */
-void a_Nav_init(BrowserWindow *bw)
+void a_Nav_init(DilloDoc *dd)
 {
-   bw->nav_stack_size = 0;
-   bw->nav_stack_size_max = 16;
-   bw->nav_stack = NULL;
-   bw->nav_stack_ptr = -1;
-   bw->nav_expecting = FALSE;
-   bw->nav_expect_url = NULL;
+   dd->nav_stack_size = 0;
+   dd->nav_stack_size_max = 16;
+   dd->nav_stack = NULL;
+   dd->nav_stack_ptr = -1;
+   dd->nav_expecting = FALSE;
+   dd->nav_expect_url = NULL;
 }
 
 /*
  * Free memory used by this module
  */
-void a_Nav_free(BrowserWindow *bw)
+void a_Nav_free(DilloDoc *dd)
 {
-   a_Nav_cancel_expect(bw);
-   g_free(bw->nav_stack);
+   a_Nav_cancel_expect(dd);
+   g_free(dd->nav_stack);
 }
 
 
@@ -64,79 +67,79 @@ void a_Nav_free(BrowserWindow *bw)
 /*
  * Return current nav_stack pointer [0 based; -1 = empty]
  */
-gint a_Nav_stack_ptr(BrowserWindow *bw)
+gint a_Nav_stack_ptr(DilloDoc *dd)
 {
-   return bw->nav_stack_ptr;
+   return dd->nav_stack_ptr;
 }
 
 /*
  * Move the nav_stack pointer
  */
-static void Nav_stack_move_ptr(BrowserWindow *bw, gint offset)
+static void Nav_stack_move_ptr(DilloDoc *dd, gint offset)
 {
    gint nptr;
 
-   g_return_if_fail (bw != NULL);
+   g_return_if_fail (dd != NULL);
    if (offset != 0) {
-      nptr = bw->nav_stack_ptr + offset;
-      g_return_if_fail (nptr >= 0 && nptr < bw->nav_stack_size);
-      bw->nav_stack_ptr = nptr;
+      nptr = dd->nav_stack_ptr + offset;
+      g_return_if_fail (nptr >= 0 && nptr < dd->nav_stack_size);
+      dd->nav_stack_ptr = nptr;
    }
 }
 
 /*
  * Return size of nav_stack [1 based]
  */
-gint a_Nav_stack_size(BrowserWindow *bw)
+gint a_Nav_stack_size(DilloDoc *dd)
 {
-   return bw->nav_stack_size;
+   return dd->nav_stack_size;
 }
 
 /*
  * Add an URL-index in the navigation stack.
  */
-static void Nav_stack_add(BrowserWindow *bw, gint idx)
+static void Nav_stack_add(DilloDoc *dd, gint idx)
 {
-   g_return_if_fail (bw != NULL);
+   g_return_if_fail (dd != NULL);
 
-   ++bw->nav_stack_ptr;
-   if ( bw->nav_stack_ptr == bw->nav_stack_size) {
-      a_List_add(bw->nav_stack, bw->nav_stack_size, bw->nav_stack_size_max);
-      ++bw->nav_stack_size;
+   ++dd->nav_stack_ptr;
+   if ( dd->nav_stack_ptr == dd->nav_stack_size) {
+      a_List_add(dd->nav_stack, dd->nav_stack_size, dd->nav_stack_size_max);
+      ++dd->nav_stack_size;
    } else {
-      bw->nav_stack_size = bw->nav_stack_ptr + 1;
+      dd->nav_stack_size = dd->nav_stack_ptr + 1;
    }
-   bw->nav_stack[bw->nav_stack_ptr] = idx;
+   dd->nav_stack[dd->nav_stack_ptr] = idx;
 }
 
 /*
  * Remove an URL-index from the navigation stack.
  */
-static void Nav_stack_remove(BrowserWindow *bw, gint idx)
+static void Nav_stack_remove(DilloDoc *dd, gint idx)
 {
-   gint sz = a_Nav_stack_size(bw);
+   gint sz = a_Nav_stack_size(dd);
 
-   g_return_if_fail (bw != NULL && idx >=0 && idx < sz);
+   g_return_if_fail (dd != NULL && idx >=0 && idx < sz);
 
    for (  ; idx < sz - 1; ++idx)
-      bw->nav_stack[idx] = bw->nav_stack[idx + 1];
-   if ( bw->nav_stack_ptr == --bw->nav_stack_size )
-      --bw->nav_stack_ptr;
+      dd->nav_stack[idx] = dd->nav_stack[idx + 1];
+   if ( dd->nav_stack_ptr == --dd->nav_stack_size )
+      --dd->nav_stack_ptr;
 }
 
 /*
- * Remove equal adyacent URLs at the top of the stack.
+ * Remove equal adjacent URLs at the top of the stack.
  * (It may happen with redirections)
  */
-static void Nav_stack_clean(BrowserWindow *bw)
+static void Nav_stack_clean(DilloDoc *dd)
 {
    gint i;
 
-   g_return_if_fail (bw != NULL);
+   g_return_if_fail (dd != NULL);
 
-   if ((i = a_Nav_stack_size(bw)) >= 2 &&
-       bw->nav_stack[i-2] == bw->nav_stack[i-1])
-         Nav_stack_remove(bw, i - 1);
+   if ((i = a_Nav_stack_size(dd)) >= 2 &&
+       dd->nav_stack[i-2] == dd->nav_stack[i-1])
+         Nav_stack_remove(dd, i - 1);
 }
 
 
@@ -148,7 +151,7 @@ static void Nav_stack_clean(BrowserWindo
  * This function requests the page's root-URL; images and related stuff
  * are fetched directly by the HTML module.
  */
-static void Nav_open_url(BrowserWindow *bw, const DilloUrl *url, gint offset)
+static void Nav_open_url(DilloDoc *dd, const DilloUrl *url, gint offset)
 {
    DilloUrl *old_url = NULL;
    gboolean MustLoad;
@@ -156,52 +159,59 @@ static void Nav_open_url(BrowserWindow *
    DilloWeb *Web;
    gboolean ForceReload = (URL_FLAGS(url) & URL_E2EReload);
 
-   MSG("Nav_open_url: Url=>%s<\n", URL_STR_(url));
+   _MSG("Nav_open_url: Url=>%s<\n", URL_STR_(url));
 
    /* Get the url of the current page */
-   if ( a_Nav_stack_ptr(bw) != -1 )
-      old_url = a_History_get_url(NAV_TOP(bw));
+   if ( a_Nav_stack_ptr(dd) != -1 )
+      old_url = a_History_get_url(NAV_TOP(dd));
 
    /* Record current scrolling position
-    * (the strcmp check is necessary because of redirections) */
+    * (the strcmp check is necessary because of redirections)
+    *
+    * TODO: how does this cope with background/(i)frame load? The
+    * comparison with a_Interface_get_location_text() is not valid then... */
    if (old_url &&
-       !strcmp(URL_STR(old_url), a_Interface_get_location_text(bw))) {
-      old_url->scrolling_position =
-        a_Dw_gtk_scrolled_window_get_scrolling_position(
-           GTK_DW_SCROLLED_WINDOW(bw->docwin));
+       !strcmp(URL_STR(old_url), a_Interface_get_location_text(dd->bw))) {
+      a_Url_set_pos(old_url,
+		    a_Dw_gtk_scrolled_window_get_scrolling_position(
+                    GTK_DW_SCROLLED_WINDOW(dd->docwin)));
+      a_Url_set_cursor(old_url, 
+		       a_Dw_gtk_scrolled_window_get_cursor(
+                       GTK_DW_SCROLLED_WINDOW(dd->docwin)));
    }
 
    /* Update navigation-stack-pointer (offset may be zero) */
-   Nav_stack_move_ptr(bw, offset);
+   Nav_stack_move_ptr(dd, offset);
 
    /* Page must be reloaded, if old and new url (without anchor) differ */
    MustLoad = ForceReload || !old_url;
    if (old_url){
       MustLoad |= a_Url_cmp(old_url, url);
-      MustLoad |= strcmp(URL_STR(old_url), a_Interface_get_location_text(bw));
+      /* TODO: possible issue with background/(i)frame load, see above */
+      MustLoad |= strcmp(URL_STR(old_url), a_Interface_get_location_text(dd->bw));
    }
 
    if ( MustLoad ) {
-      a_Interface_stop(bw);
-      a_Interface_clean(bw);
+      a_Doc_stop(dd);
+      a_Doc_clean(dd);
 
-      a_Menu_pagemarks_new(bw);
+      a_Pagemark_new(dd);
 
       Web = a_Web_new(url);
-      Web->bw = bw;
+      Web->dd = dd;
       Web->flags |= WEB_RootUrl;
       if ((ClientKey = a_Capi_open_url(Web, NULL, NULL)) != 0) {
-         a_Interface_add_client(bw, ClientKey, 1);
-         a_Interface_add_url(bw, url, WEB_RootUrl);
+         a_Doc_add_client(dd, ClientKey, 1);
+         a_Doc_add_url(dd, url, WEB_RootUrl);
       }
-      a_Interface_set_cursor(bw, GDK_LEFT_PTR);
+      a_Doc_set_cursor(dd, GDK_LEFT_PTR);
    }
 
    /* Jump to #anchor position */
    if (URL_FRAGMENT_(url)) {
       /* todo: push on stack */
       a_Dw_gtk_scrolled_window_set_anchor(
-         GTK_DW_SCROLLED_WINDOW(bw->docwin), URL_FRAGMENT_(url));
+         GTK_DW_SCROLLED_WINDOW(dd->docwin), URL_FRAGMENT_(url));
    }
 }
 
@@ -209,191 +219,194 @@ static void Nav_open_url(BrowserWindow *
  * Cancel the last expected url if present. The responsibility
  * for actually aborting the data stream remains with the caller.
  */
-void a_Nav_cancel_expect(BrowserWindow *bw)
+void a_Nav_cancel_expect(DilloDoc *dd)
 {
-   if (bw->nav_expecting) {
-      if (bw->nav_expect_url) {
-         a_Url_free(bw->nav_expect_url);
-         bw->nav_expect_url = NULL;
+   if (dd->nav_expecting) {
+      if (dd->nav_expect_url) {
+         a_Url_free(dd->nav_expect_url);
+         dd->nav_expect_url = NULL;
       }
-      bw->nav_expecting = FALSE;
+      dd->nav_expecting = FALSE;
    }
 }
 
 /*
  * We have an answer! Set things accordingly.
  */
-void a_Nav_expect_done(BrowserWindow *bw)
+void a_Nav_expect_done(DilloDoc *dd)
 {
    gint idx;
    DilloUrl *url;
 
-   g_return_if_fail(bw != NULL);
+   g_return_if_fail(dd != NULL);
 
-   if (bw->nav_expecting) {
-      url = bw->nav_expect_url;
+   if (dd->nav_expecting) {
+      url = dd->nav_expect_url;
       /* unset E2EReload before adding this url to history */
       a_Url_set_flags(url, URL_FLAGS(url) & ~URL_E2EReload);
       idx = a_History_add_url(url);
-      Nav_stack_add(bw, idx);
+      Nav_stack_add(dd, idx);
 
       a_Url_free(url);
-      bw->nav_expect_url = NULL;
-      bw->nav_expecting = FALSE;
+      dd->nav_expect_url = NULL;
+      dd->nav_expecting = FALSE;
    }
-   Nav_stack_clean(bw);
-   a_Interface_set_button_sens(bw);
+   Nav_stack_clean(dd);
+   a_Doc_progress_update(dd);
 }
 
 /*
  * Remove top-URL from the navigation stack.
  * (Used to remove URLs that force redirection)
  */
-void a_Nav_remove_top_url(BrowserWindow *bw)
+void a_Nav_remove_top_url(DilloDoc *dd)
 {
-   g_return_if_fail (bw != NULL);
+   g_return_if_fail (dd != NULL);
 
    /* Deallocate the URL a the top of the stack */
-   Nav_stack_remove(bw, a_Nav_stack_size(bw) - 1);
+   Nav_stack_remove(dd, a_Nav_stack_size(dd) - 1);
 }
 
 /*
  * Make 'url' the current browsed page (upon data arrival)
- * - Set bw to expect the URL data
+ * - Set dd to expect the URL data
  * - Ask the cache to feed back the requested URL (via Nav_open_url)
  */
-void a_Nav_push(BrowserWindow *bw, const DilloUrl *url)
+void a_Nav_push(DilloDoc *dd, const DilloUrl *url)
 {
-   g_return_if_fail (bw != NULL);
+   g_return_if_fail (dd != NULL);
 
-   if (bw->nav_expecting && a_Url_cmp(bw->nav_expect_url, url) == 0 &&
-       URL_STRCAMP_EQ(URL_FRAGMENT_(bw->nav_expect_url), URL_FRAGMENT_(url))) {
+   if (dd->nav_expecting && a_Url_cmp(dd->nav_expect_url, url) == 0 &&
+       URL_STRCAMP_EQ(URL_FRAGMENT_(dd->nav_expect_url), URL_FRAGMENT_(url))) {
       /* we're already expecting that url (most probably a double-click) */
       return;
    }
-   a_Nav_cancel_expect(bw);
-   bw->nav_expect_url = a_Url_dup(url);
-   bw->nav_expecting = TRUE;
-   Nav_open_url(bw, url, 0);
-}
-
-/*
- * Same as a_Nav_push() but in a new window.
- */
-void a_Nav_push_nw(BrowserWindow *bw, const DilloUrl *url)
-{
-   gint width, height;
-   BrowserWindow *newbw;
-
-   gdk_window_get_size (bw->main_window->window, &width, &height);
-   newbw = a_Interface_browser_window_new(width, height, 0);
-   a_Nav_push(newbw, url);
+   a_Nav_cancel_expect(dd);
+   dd->nav_expect_url = a_Url_dup(url);
+   dd->nav_expecting = TRUE;
+   /* is this is a targeted URL, (re)name the document after it */
+   if(URL_TARGET_(url))
+     a_Doc_set_name(dd, (gchar *) URL_TARGET_(url));
+   Nav_open_url(dd, url, 0);
 }
 
 /*
  * Wraps a_Nav_push to match 'DwPage->link' function type
  */
-void a_Nav_vpush(void *vbw, const DilloUrl *url)
+void a_Nav_vpush(void *vdd, const DilloUrl *url)
 {
-   a_Nav_push(vbw, url);
+   a_Nav_push(vdd, url);
 }
 
 /*
  * Send the browser back to previous page
  */
-void a_Nav_back(BrowserWindow *bw)
+void a_Nav_back(DilloDoc *dd)
 {
-   gint idx = a_Nav_stack_ptr(bw);
+   gint idx = a_Nav_stack_ptr(dd);
 
-   a_Nav_cancel_expect(bw);
+   a_Nav_cancel_expect(dd);
    if ( --idx >= 0 ){
-      a_Interface_msg(bw, "");
-      Nav_open_url(bw, a_History_get_url(NAV_IDX(bw,idx)), -1);
+      a_Interface_msg(dd->bw, "");
+      Nav_open_url(dd, a_History_get_url(NAV_IDX(dd,idx)), -1);
    }
 }
 
 /*
  * Send the browser to next page in the history list
  */
-void a_Nav_forw(BrowserWindow *bw)
+void a_Nav_forw(DilloDoc *dd)
 {
-   gint idx = a_Nav_stack_ptr(bw);
+   gint idx = a_Nav_stack_ptr(dd);
 
-   a_Nav_cancel_expect(bw);
-   if (++idx < a_Nav_stack_size(bw)) {
-      a_Interface_msg(bw, "");
-      Nav_open_url(bw, a_History_get_url(NAV_IDX(bw,idx)), +1);
+   a_Nav_cancel_expect(dd);
+   if (++idx < a_Nav_stack_size(dd)) {
+      a_Interface_msg(dd->bw, "");
+      Nav_open_url(dd, a_History_get_url(NAV_IDX(dd,idx)), +1);
    }
 }
 
 /*
  * Redirect the browser to the HOME page!
  */
-void a_Nav_home(BrowserWindow *bw)
+void a_Nav_home(DilloDoc *dd)
 {
-   a_Nav_push(bw, prefs.home);
+   a_Nav_push(dd, prefs.home);
 }
 
 /*
  * Jump to an URL within the stack history
- * NewBw: {0 = same window, 1 = new window}
+ * NewDd: {0 = same window, 1 = new window, 2 = new tab}
  */
-void a_Nav_jump_callback(GtkWidget *widget, gpointer client_data, gint NewBw)
+void a_Nav_jump_callback(GtkWidget *widget, gpointer client_data, gint NewDd)
 {
    int idx;
-   BrowserWindow *bw = client_data;
+   DilloDoc *dd = client_data;
 
    idx = GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT (widget), "nav_idx"));
-   if (idx >= 0 && idx < a_Nav_stack_size(bw)) {
-      if (NewBw == 1) {
-         a_Nav_push_nw(bw, a_History_get_url(NAV_IDX(bw,idx)));
-      } else {
-         Nav_open_url(bw, a_History_get_url(NAV_IDX(bw,idx)),
-                      idx - a_Nav_stack_ptr(bw));
-      }
+   if (idx >= 0 && idx < a_Nav_stack_size(dd)) {
+     switch (NewDd) {
+     case 0:
+       /* Open link in same bw */
+       Nav_open_url(dd, a_History_get_url(NAV_IDX(dd,idx)),
+                    idx - a_Nav_stack_ptr(dd));
+       break;
+     case 1:
+       /* Open link in a new bw */
+       a_Menu_popup_set_url(dd, a_History_get_url(NAV_IDX(dd,idx)));
+       a_Commands_open_link_nw_callback(widget, dd->bw);
+       break;
+     case 2:
+       /* Open link in a new tab */
+       a_Menu_popup_set_url(dd, a_History_get_url(NAV_IDX(dd,idx)));
+       a_Commands_open_link_nw_tab_callback(widget, dd->bw);
+       break;
+     default:
+       g_error("Unhandled switch type %d in a_Nav_jump_callback in src/nav.c\n", NewDd);
+     }
    }
 }
 
 /*
  * Callback for reload confirmation
  */
-static void Nav_reload_confirmed(BrowserWindow *bw)
+static void Nav_reload_confirmed(DilloDoc *dd)
 {
    DEBUG_MSG(3, "Nav_reload_confirmed\n");
-   if ( a_Nav_stack_size(bw) &&
-        bw->question_dialog_data == a_History_get_url(NAV_TOP(bw)) ) {
+   if ( a_Nav_stack_size(dd) &&
+        dd->bw->question_dialog_data == a_History_get_url(NAV_TOP(dd)) ) {
       /* a genuine confirmation! */
       DEBUG_MSG(3, "Nav_reload_confirmed test: OK\n");
-      bw->question_dialog_data = NULL;
-      Nav_reload(bw);
+      dd->bw->question_dialog_data = NULL;
+      Nav_reload(dd);
    }
 }
 
 /*
  * Callback for reload refusal
  */
-static void Nav_reload_refused(BrowserWindow *bw)
+static void Nav_reload_refused(DilloDoc *dd)
 {
    DEBUG_MSG(3, "Nav_reload_refused\n");
-   bw->question_dialog_data = NULL;
+   dd->bw->question_dialog_data = NULL;
 }
 
 /*
  * This one does a_Nav_reload's job!
  */
-static void Nav_reload(BrowserWindow *bw)
+static void Nav_reload(DilloDoc *dd)
 {
    DilloUrl *url, *ReqURL;
 
-   a_Nav_cancel_expect(bw);
-   if ( a_Nav_stack_size(bw) ) {
-      url = a_History_get_url(NAV_TOP(bw));
-      ReqURL = a_Url_dup(a_History_get_url(NAV_TOP(bw)));
+   a_Nav_cancel_expect(dd);
+   if ( a_Nav_stack_size(dd) ) {
+      url = a_History_get_url(NAV_TOP(dd));
+      ReqURL = a_Url_dup(a_History_get_url(NAV_TOP(dd)));
       /* Let's make reload be end-to-end */
       a_Url_set_flags(ReqURL, URL_FLAGS(ReqURL) | URL_E2EReload);
       /* This is an explicit reload, so clear the SpamSafe flag */
       a_Url_set_flags(ReqURL, URL_FLAGS(ReqURL) & ~URL_SpamSafe);
-      Nav_open_url(bw, ReqURL, 0);
+      Nav_open_url(dd, ReqURL, 0);
       a_Url_free(ReqURL);
    }
 }
@@ -402,22 +415,22 @@ static void Nav_reload(BrowserWindow *bw
  * Implement the RELOAD button functionality.
  * (We haven't defined it yet ;)
  */
-void a_Nav_reload(BrowserWindow *bw)
+void a_Nav_reload(DilloDoc *dd)
 {
    DilloUrl *url;
 
-   a_Nav_cancel_expect(bw);
-   if ( a_Nav_stack_size(bw) ) {
-      url = a_History_get_url(NAV_TOP(bw));
+   a_Nav_cancel_expect(dd);
+   if ( a_Nav_stack_size(dd) ) {
+      url = a_History_get_url(NAV_TOP(dd));
       if (URL_FLAGS(url) & URL_Post) {
          /* Attempt to repost data, let's confirm... */
-         bw->question_dialog_data = (gpointer)url;
-         a_Interface_question_dialog(bw, "Repost form data?",
-                                     Nav_reload_confirmed, bw,
-                                     Nav_reload_refused, bw);
+         dd->bw->question_dialog_data = (gpointer)url;
+         a_Interface_question_dialog(dd->bw, "Repost form data?",
+                                     Nav_reload_confirmed, dd,
+                                     Nav_reload_refused, dd);
 
       } else {
-         Nav_reload(bw);
+         Nav_reload(dd);
       }
    }
 }
diff -pruN dillo/src/nav.h dillo_tabs/src/nav.h
--- dillo/src/nav.h	2003-11-24 14:32:40.118773384 +0100
+++ dillo_tabs/src/nav.h	2003-11-24 14:32:40.228756664 +0100
@@ -6,29 +6,28 @@
 
 
 /* useful macros for the navigation stack */
-#define NAV_IDX(bw, i)   (bw)->nav_stack[i]
-#define NAV_TOP(bw)      (bw)->nav_stack[(bw)->nav_stack_ptr]
+#define NAV_IDX(dd, i)   (dd)->nav_stack[i]
+#define NAV_TOP(dd)      (dd)->nav_stack[(dd)->nav_stack_ptr]
 
 
 #ifdef __cplusplus
 extern "C" {
 #endif /* __cplusplus */
 
-void a_Nav_push(BrowserWindow *bw, const DilloUrl *url);
-void a_Nav_push_nw(BrowserWindow *bw, const DilloUrl *url);
-void a_Nav_vpush(void *vbw, const DilloUrl *url);
-void a_Nav_back(BrowserWindow *bw);
-void a_Nav_forw(BrowserWindow *bw);
-void a_Nav_home(BrowserWindow *bw);
-void a_Nav_reload(BrowserWindow *bw);
-void a_Nav_init(BrowserWindow *bw);
-void a_Nav_free(BrowserWindow *bw);
-void a_Nav_cancel_expect (BrowserWindow *bw);
-void a_Nav_expect_done(BrowserWindow *bw);
-void a_Nav_remove_top_url(BrowserWindow *bw);
-void a_Nav_jump_callback(GtkWidget *widget, gpointer client_data, gint NewBw);
-gint a_Nav_stack_ptr(BrowserWindow *bw);
-gint a_Nav_stack_size(BrowserWindow *bw);
+void a_Nav_push(DilloDoc *dd, const DilloUrl *url);
+void a_Nav_vpush(void *vdd, const DilloUrl *url);
+void a_Nav_back(DilloDoc *dd);
+void a_Nav_forw(DilloDoc *dd);
+void a_Nav_home(DilloDoc *dd);
+void a_Nav_reload(DilloDoc *dd);
+void a_Nav_init(DilloDoc *dd);
+void a_Nav_free(DilloDoc *dd);
+void a_Nav_cancel_expect (DilloDoc *dd);
+void a_Nav_expect_done(DilloDoc *dd);
+void a_Nav_remove_top_url(DilloDoc *dd);
+void a_Nav_jump_callback(GtkWidget *widget, gpointer client_data, gint NewDd);
+gint a_Nav_stack_ptr(DilloDoc *dd);
+gint a_Nav_stack_size(DilloDoc *dd);
 
 
 #ifdef __cplusplus
diff -pruN dillo/src/pagemark.c dillo_tabs/src/pagemark.c
--- dillo/src/pagemark.c	1970-01-01 01:00:00.000000000 +0100
+++ dillo_tabs/src/pagemark.c	2003-11-24 14:32:40.253752864 +0100
@@ -0,0 +1,132 @@
+/*
+ * Dillo
+ *
+ * Some code copied from:
+ * The GIMP -- an image manipulation program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+#include "dw_gtk_scrolled_window.h"
+#include "browser.h"
+#include "interface.h"
+#include "dw_page.h"         /* for a_Dw_page_add_anchor */
+
+/*
+ * Functions to manipulate Pagemarks menu.
+ */
+
+/*
+ * make the page scroll to the pagemark
+ */
+static void Pagemark_goto_pagemark(GtkWidget *widget,
+                                         gpointer client_data)
+{
+   DilloDoc *dd = (DilloDoc *) client_data;
+   char anchor[32];
+
+   g_snprintf (anchor, 32, "#%ld", (long int)widget);
+   a_Dw_gtk_scrolled_window_set_anchor(GTK_DW_SCROLLED_WINDOW(dd->docwin),
+                                       anchor);
+}
+
+/*
+ * Deallocate memory used by a pagemark menu
+ */
+void a_Pagemark_destroy(DilloDoc *dd)
+{
+  if (GTK_IS_WIDGET(dd->pagemarks_menu))
+    {
+      gtk_object_unref(GTK_OBJECT(dd->pagemarks_menu));
+      dd->pagemarks_menu = NULL;
+      dd->pagemarks_last = NULL;
+    }
+}
+
+/*
+ * Deallocate the memory used by a pagemarks menu and create a new one
+ */
+void a_Pagemark_new(DilloDoc *dd)
+{
+   a_Pagemark_destroy(dd);
+   dd->pagemarks_menu = gtk_menu_new();
+   /* add a reference to the object, otherwise it will be destroyed
+    * when the document (tab) is switched */
+   gtk_object_ref(GTK_OBJECT(dd->pagemarks_menu));
+   dd->pagemarks_last = NULL;
+
+   /* todo: add a scroller so one can access  all menu entries,
+    * even if there's a lot of pagemarks. */
+   /* --EG: I haven't found a way to pass a scroller
+    * as widget for the submenu */
+}
+
+/*
+ * Add a new pagemark (opening H tag).
+ * The text can be set only at the closing H tag.
+ * level is the level of the heading (1-6).
+ */
+void a_Pagemark_add(DilloDoc *dd, void *page, void *style,
+                          gint level)
+{
+   char anchor[32], spaces[32], name[32];
+
+   g_snprintf(spaces, 32, "%*s", 3 * (level - 1), "");
+   dd->pagemarks_last = gtk_menu_item_new_with_label(spaces);
+   gtk_menu_append(GTK_MENU(dd->pagemarks_menu),dd->pagemarks_last);
+   gtk_widget_show(dd->pagemarks_last);
+   gtk_signal_connect(GTK_OBJECT(dd->pagemarks_last), "activate",
+                      (GtkSignalFunc) Pagemark_goto_pagemark, dd);
+   /* was:
+    * Menu_add(dd->pagemarks_menu, spaces, NULL, dd,
+    *          Pagemark_goto_pagemark, dd);
+    * but Menu_add is a private function for menu.c so... */
+   g_snprintf(anchor, 32, "#%ld", (glong)(dd->pagemarks_last));
+   a_Dw_page_add_anchor(page, anchor, style);
+   gtk_signal_connect (GTK_OBJECT (dd->pagemarks_last), "select",
+                       GTK_SIGNAL_FUNC (a_Interface_scroll_popup), NULL);
+   g_snprintf(name, 32, "dilloHeading%d", level);
+   gtk_widget_set_name(dd->pagemarks_last, name);
+}
+
+/*
+ * Set the text for the last created pagemark.
+ */
+void a_Pagemark_set_text(DilloDoc *dd, const char *str)
+{
+   GString *text;
+   GtkWidget *child;
+
+   /* Avoid problems with lonely closing tags and nested headings */
+   if ( dd->pagemarks_last &&
+       (child = GTK_BIN (dd->pagemarks_last)->child) &&
+       GTK_IS_LABEL (child) ) {
+      text = g_string_new(GTK_LABEL(child)->label);
+      g_string_append(text, str);
+      if ( text->len > 64 ) {
+         g_string_truncate(text, 64);
+         g_string_append(text, "...");
+      }
+      gtk_label_set_text(GTK_LABEL (child), text->str);
+      g_string_free(text, 1);
+      dd->pagemarks_last = NULL;
+   }
+}
diff -pruN dillo/src/pagemark.h dillo_tabs/src/pagemark.h
--- dillo/src/pagemark.h	1970-01-01 01:00:00.000000000 +0100
+++ dillo_tabs/src/pagemark.h	2003-11-24 14:32:40.253752864 +0100
@@ -0,0 +1,13 @@
+#ifndef __PAGEMARK_H__
+#define __PAGEMARK_H__
+
+#include <gtk/gtk.h>
+
+void a_Pagemark_new (DilloDoc *dd);
+void a_Pagemark_destroy (DilloDoc *dd);
+void a_Pagemark_add(DilloDoc *dd, void *page, void *style,
+                          gint level);
+void a_Pagemark_set_text(DilloDoc *dd, const char *str);
+
+
+#endif /* __PAGEMARK_H__ */
diff -pruN dillo/src/plain.c dillo_tabs/src/plain.c
--- dillo/src/plain.c	2003-11-24 14:32:40.119773232 +0100
+++ dillo_tabs/src/plain.c	2003-11-24 14:32:40.230756360 +0100
@@ -23,8 +23,7 @@
 #include "cache.h"
 #include "browser.h"
 #include "web.h"
-#include "interface.h"
-#include "progressbar.h"
+#include "doc.h"
 #include "misc.h"
 #include "history.h"
 #include "nav.h"
@@ -35,7 +34,7 @@ typedef struct _DilloPlain {
    DwWidget *dw;
    size_t Start_Ofs;    /* Offset of where to start reading next */
    DwStyle *style;
-   BrowserWindow *bw;
+   DilloDoc *dd;
    gint state;
 } DilloPlain;
 
@@ -59,9 +58,8 @@ static int Plain_page_menu(GtkWidget *vi
                            BrowserWindow *bw)
 {
    if (event->button == 3) {
-      a_Menu_popup_set_url(bw, a_History_get_url(NAV_TOP(bw)));
-      gtk_menu_popup(GTK_MENU(bw->menu_popup.over_page), NULL, NULL,
-                     NULL, NULL, event->button, event->time);
+      a_Menu_popup_set_url(bw->dd, a_History_get_url(NAV_TOP(bw->dd)));
+      a_Menu_popup_over_page(bw->dd, event);
       return TRUE;
    } else
       return FALSE;
@@ -70,38 +68,26 @@ static int Plain_page_menu(GtkWidget *vi
 /*
  * Create and initialize a new DilloPlain structure.
  */
-static DilloPlain *Plain_new(BrowserWindow *bw)
+static DilloPlain *Plain_new(DilloDoc *dd)
 {
    DilloPlain *plain;
    DwPage *page;
-   DwStyle style_attrs;
-   DwStyleFont font_attrs;
 
    plain = g_new(DilloPlain, 1);
    plain->state = ST_SeekingEol;
    plain->Start_Ofs = 0;
-   plain->bw = bw;
+   plain->dd = dd;
    plain->dw = a_Dw_page_new();
    page = (DwPage *) plain->dw;
 
-   /* Create the font and attribute for the page. */
-   font_attrs.name = prefs.fw_fontname;
-   font_attrs.size = rint(12.0 * prefs.font_factor);
-   font_attrs.weight = 400;
-   font_attrs.style = DW_STYLE_FONT_STYLE_NORMAL;
-
-   a_Dw_style_init_values (&style_attrs, plain->bw->main_window->window);
-   style_attrs.font = a_Dw_style_font_new (&font_attrs);
-   style_attrs.color = a_Dw_style_color_new (prefs.text_color,
-                                             plain->bw->main_window->window);
-   plain->style = a_Dw_style_new (&style_attrs,
-                                  plain->bw->main_window->window);
-   //a_Dw_widget_set_style (plain->dw, plain->style);
+   /* assign dd's style to plain */
+   plain->style = dd->style;
+   a_Dw_style_ref (plain->style);   
 
    /* The context menu */
    gtk_signal_connect_while_alive
-      (GTK_OBJECT(GTK_BIN(plain->bw->docwin)->child),"button_press_event",
-       GTK_SIGNAL_FUNC(Plain_page_menu), (gpointer)plain->bw,
+      (GTK_OBJECT(GTK_BIN(plain->dd->docwin)->child),"button_press_event",
+       GTK_SIGNAL_FUNC(Plain_page_menu), (gpointer)plain->dd->bw,
        GTK_OBJECT (page));
 
    return plain;
@@ -114,7 +100,7 @@ DwWidget* a_Plain_text(const char *type,
                        void **Data)
 {
    DilloWeb *web = P;
-   DilloPlain *plain = Plain_new(web->bw);
+   DilloPlain *plain = Plain_new(web->dd);
 
    *Call = (CA_Callback_t)Plain_callback;
    *Data = (void*)plain;
@@ -135,10 +121,8 @@ static void Plain_callback(int Op, Cache
       if (plain->Start_Ofs < Client->BufSize)
          Plain_write(plain, Client->Buf, Client->BufSize, 1);
       /* remove this client from our active list */
-      a_Interface_close_client(plain->bw, Client->Key);
-      /* set progress bar insensitive */
-      a_Progressbar_update(plain->bw->progress, NULL, 0);
-
+      plain->dd->ready = TRUE;
+      a_Doc_close_client(plain->dd, Client->Key);
       a_Dw_style_unref (plain->style);
       g_free(plain);
    } else {
@@ -192,12 +176,9 @@ static void Plain_write(DilloPlain *plai
       plain->Start_Ofs += len;
    }
 
-   if ( plain->bw ) {
-      gchar completestr[32];
-      g_snprintf(
-         completestr, 32, "%s%.1f Kb",
-         PBAR_PSTR(prefs.panel_size == 1),
-         (float)plain->Start_Ofs/1024);
-      a_Progressbar_update(plain->bw->progress, completestr, 1);
+   if ( plain->dd ) {
+      plain->dd->progress = plain->Start_Ofs/1024;
+      plain->dd->ready = FALSE;
+      a_Doc_progress_update(plain->dd);
    }
 }
diff -pruN dillo/src/prefs.c dillo_tabs/src/prefs.c
--- dillo/src/prefs.c	2003-11-24 14:32:40.120773080 +0100
+++ dillo_tabs/src/prefs.c	2003-11-24 14:32:40.232756056 +0100
@@ -19,6 +19,7 @@
  */
 
 #include <glib.h>
+#include <gtk/gtk.h>      /* for gtk_rc_parse_string() */
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <stdlib.h>
@@ -46,6 +47,8 @@ static const struct {
    { "text_color", DRC_TOKEN_TEXT_COLOR },
    { "use_oblique", DRC_TOKEN_USE_OBLIQUE },
    { "home", DRC_TOKEN_HOME },
+   { "splash", DRC_TOKEN_SPLASH },
+   { "key_bindings", DRC_TOKEN_KEY_BINDINGS },
    { "show_tooltip", DRC_TOKEN_SHOW_TOOLTIP },
    { "panel_size", DRC_TOKEN_PANEL_SIZE },
    { "small_icons", DRC_TOKEN_SMALL_ICONS },
@@ -64,8 +67,10 @@ static const struct {
    { "show_url", DRC_TOKEN_SHOW_URL },
    { "show_search", DRC_TOKEN_SHOW_SEARCH },
    { "show_progress_box", DRC_TOKEN_SHOW_PROGRESS_BOX },
+   { "show_popup_navigation", DRC_TOKEN_SHOW_POPUP_NAVIGATION },
    { "fullwindow_start", DRC_TOKEN_FULLWINDOW_START },
    { "transient_dialogs", DRC_TOKEN_TRANSIENT_DIALOGS },
+   { "popup_dialogs", DRC_TOKEN_POPUP_DIALOGS },
    { "vw_fontname", DRC_TOKEN_VW_FONT },
    { "fw_fontname", DRC_TOKEN_FW_FONT },
    { "generate_submit", DRC_TOKEN_GENERATE_SUBMIT },
@@ -73,7 +78,15 @@ static const struct {
    { "search_url", DRC_TOKEN_SEARCH_URL },
    { "show_html_warnings", DRC_TOKEN_SHOW_HTML_WARNINGS },
    { "show_msg", DRC_TOKEN_SHOW_MSG },
-   { "use_old_parser", DRC_TOKEN_USE_OLD_PARSER }
+   { "use_old_parser", DRC_TOKEN_USE_OLD_PARSER },
+   { "keywords", DRC_TOKEN_KEYWORDS },
+   { "target_blank", DRC_TOKEN_TARGET_BLANK },
+   { "tab_load_in_background", DRC_TOKEN_TAB_LOAD_IN_BACKGROUND },
+   { "tab_bar_show_fullscreen", DRC_TOKEN_TAB_BAR_SHOW_FULLSCREEN },
+   { "tab_bar_show_single_tab", DRC_TOKEN_TAB_BAR_SHOW_SINGLE_TAB },
+   { "tab_bar_scroller", DRC_TOKEN_TAB_BAR_SCROLLER },
+   { "tab_title_compress", DRC_TOKEN_TAB_TITLE_COMPRESS },
+   { "compress_common_prefixes", DRC_TOKEN_COMPRESS_COMMON_PREFIXES },
 };
 
 static const guint n_symbols = sizeof (symbols) / sizeof (symbols[0]);
@@ -86,7 +99,7 @@ static const guint n_symbols = sizeof (s
 static guint Prefs_parser(GScanner *scanner)
 {
    gint st;
-   guint symbol;
+   guint symbol, n, i;
 
    /* expect a valid symbol */
    g_scanner_get_next_token(scanner);
@@ -173,6 +186,18 @@ static guint Prefs_parser(GScanner *scan
       a_Url_free(prefs.home);
       prefs.home = a_Url_new(scanner->value.v_string, NULL, 0, 0);
       break;
+   case DRC_TOKEN_SPLASH:
+      if(strcmp(scanner->value.v_string, "NO") == 0)
+	 prefs.show_splash = FALSE;
+      else {
+         g_free(prefs.splash);
+	 prefs.splash = g_strdup(scanner->value.v_string);
+	 prefs.show_splash = TRUE;
+      }
+      break;
+   case DRC_TOKEN_KEY_BINDINGS:
+      gtk_rc_parse_string(scanner->value.v_string);
+      break;
    case DRC_TOKEN_SHOW_TOOLTIP:
       prefs.show_tooltip = (strcmp(scanner->value.v_string, "YES") == 0);
       break;
@@ -221,12 +246,18 @@ static guint Prefs_parser(GScanner *scan
    case DRC_TOKEN_SHOW_PROGRESS_BOX:
       prefs.show_progress_box = (strcmp(scanner->value.v_string, "YES") == 0);
       break;
+   case DRC_TOKEN_SHOW_POPUP_NAVIGATION:
+      prefs.show_popup_navigation = (strcmp(scanner->value.v_string, "YES") == 0);
+      break;
    case DRC_TOKEN_FULLWINDOW_START:
       prefs.fullwindow_start = (strcmp(scanner->value.v_string, "YES") == 0);
       break;
    case DRC_TOKEN_TRANSIENT_DIALOGS:
       prefs.transient_dialogs = (strcmp(scanner->value.v_string, "YES") == 0);
       break;
+   case DRC_TOKEN_POPUP_DIALOGS:
+      prefs.popup_dialogs = (strcmp(scanner->value.v_string, "YES") == 0);
+      break;
    case DRC_TOKEN_FW_FONT:
       g_free(prefs.fw_fontname);
       prefs.fw_fontname = g_strdup(scanner->value.v_string);
@@ -245,6 +276,7 @@ static guint Prefs_parser(GScanner *scan
    case DRC_TOKEN_SEARCH_URL:
       g_free(prefs.search_url);
       prefs.search_url = g_strdup(scanner->value.v_string);
+      /* TODO: also change keyword search */
       break;
    case DRC_TOKEN_SHOW_HTML_WARNINGS:
       prefs.show_html_warnings =
@@ -258,6 +290,63 @@ static guint Prefs_parser(GScanner *scan
       prefs.use_old_parser =
          (strcmp(scanner->value.v_string, "YES") == 0);
       break;
+   case DRC_TOKEN_KEYWORDS:
+      g_free(prefs.keywords);
+      prefs.keywords = g_strconcat("search:",
+				   prefs.search_url, " ",
+				   scanner->value.v_string,
+				   NULL);
+      prefs.keywords = g_strdelimit(prefs.keywords, " \t\n", ' ');
+      prefs.keywords_vec = g_strsplit(prefs.keywords, " ", 0);
+      /* sanitize keywords
+       * removes empty keywords and strips whitespace from 
+       * remaining keyword strings */
+      for(n = 0, i = 0; prefs.keywords_vec[n]; n++, i++) {
+         while(prefs.keywords_vec[i] &&
+               !strcmp(prefs.keywords_vec[i], ""))
+            g_free(prefs.keywords_vec[i++]);
+         if(prefs.keywords_vec[i])
+            prefs.keywords_vec[n] = g_strstrip(prefs.keywords_vec[i]);
+         else {
+            prefs.keywords_vec[n] = NULL;
+            break;
+         }
+      }
+      break;
+   case DRC_TOKEN_TARGET_BLANK:
+      if (!g_strcasecmp(scanner->value.v_string, "self"))
+         prefs.target_blank = TARGET_SELF;
+      else if (!g_strcasecmp(scanner->value.v_string, "tab"))
+         prefs.target_blank = TARGET_TAB;
+      else /* default to "window" */
+         prefs.target_blank = TARGET_WINDOW;
+      break;
+   case DRC_TOKEN_TAB_LOAD_IN_BACKGROUND:
+      prefs.tab_load_in_background =
+         (strcmp(scanner->value.v_string, "YES") == 0);
+      break;
+   case DRC_TOKEN_TAB_BAR_SHOW_FULLSCREEN:
+      prefs.tab_bar_show_fullscreen =
+         (strcmp(scanner->value.v_string, "YES") == 0);
+      break;
+   case DRC_TOKEN_TAB_BAR_SHOW_SINGLE_TAB:
+      prefs.tab_bar_show_single_tab =
+         (strcmp(scanner->value.v_string, "YES") == 0);
+      break;
+   case DRC_TOKEN_TAB_BAR_SCROLLER:
+      prefs.tab_bar_scroller =
+         (strcmp(scanner->value.v_string, "YES") == 0);
+      break;
+   case DRC_TOKEN_TAB_TITLE_COMPRESS:
+      prefs.tab_title_compress =
+         (strcmp(scanner->value.v_string, "YES") == 0);
+      break;
+   case DRC_TOKEN_COMPRESS_COMMON_PREFIXES:
+      g_free(prefs.compress_common_prefixes);
+      prefs.compress_common_prefixes = g_strdup(scanner->value.v_string);
+      prefs.compress_common_prefixes_vec = 
+        g_strsplit(prefs.compress_common_prefixes, ";", 0);
+      break;
    default:
       break;   /* Not reached */
    }
@@ -363,6 +452,8 @@ void a_Prefs_init(void)
    prefs.text_color = DW_COLOR_DEFAULT_BLACK;
    prefs.use_oblique = FALSE;
    prefs.home = a_Url_new(DILLO_HOME, NULL, 0, 0);
+   prefs.splash = g_strdup(DILLO_SPLASH);
+   prefs.show_splash = TRUE;
    prefs.allow_white_bg = TRUE;
    prefs.force_my_colors = FALSE;
    prefs.force_visited_color = FALSE;
@@ -384,8 +475,10 @@ void a_Prefs_init(void)
    prefs.show_url=TRUE;
    prefs.show_search=TRUE;
    prefs.show_progress_box=TRUE;
+   prefs.show_popup_navigation=TRUE;
    prefs.fullwindow_start=FALSE;
    prefs.transient_dialogs=FALSE;
+   prefs.popup_dialogs=TRUE;
    prefs.vw_fontname = g_strdup("helvetica");
    prefs.fw_fontname = g_strdup("courier");
    prefs.generate_submit = FALSE;
@@ -394,7 +487,17 @@ void a_Prefs_init(void)
    prefs.show_html_warnings = TRUE;
    prefs.show_msg = TRUE;
    prefs.use_old_parser = FALSE;
-
+   prefs.keywords = NULL;
+   prefs.keywords_vec = NULL;
+   prefs.target_blank = TARGET_WINDOW;
+   prefs.tab_load_in_background = FALSE;
+   prefs.tab_bar_show_fullscreen = FALSE;
+   prefs.tab_bar_show_single_tab = FALSE;
+   prefs.tab_bar_scroller = FALSE;
+   prefs.tab_title_compress = TRUE;
+   prefs.compress_common_prefixes = g_strdup(COMPRESS_COMMON_PREFIXES);
+   prefs.compress_common_prefixes_vec = 
+     g_strsplit(prefs.compress_common_prefixes, ";", 0);
    /* this locale stuff is to avoid parsing problems with float numbers */
    old_locale = g_strdup (setlocale (LC_NUMERIC, NULL));
    setlocale (LC_NUMERIC, "C");
@@ -414,9 +517,16 @@ void a_Prefs_freeall(void)
    g_free(prefs.no_proxy);
    if (prefs.no_proxy_vec)
       g_strfreev(prefs.no_proxy_vec);
+   g_free(prefs.keywords);
+   if (prefs.keywords_vec)
+     g_strfreev(prefs.keywords_vec);
    a_Url_free(prefs.http_proxy);
    g_free(prefs.fw_fontname);
    g_free(prefs.vw_fontname);
    a_Url_free(prefs.home);
+   g_free(prefs.splash);
    g_free(prefs.search_url);
+   g_free(prefs.compress_common_prefixes);
+   if (prefs.compress_common_prefixes_vec)
+      g_strfreev(prefs.compress_common_prefixes_vec);
 }
diff -pruN dillo/src/prefs.h dillo_tabs/src/prefs.h
--- dillo/src/prefs.h	2003-11-24 14:32:40.121772928 +0100
+++ dillo_tabs/src/prefs.h	2003-11-24 14:32:40.232756056 +0100
@@ -8,6 +8,10 @@ extern "C" {
 #endif /* __cplusplus */
 
 #define DILLO_HOME "http://www.dillo.org/"
+#ifndef DILLO_SPLASH
+#define DILLO_SPLASH "file:/usr/share/dillo/dillo.html"
+#endif
+
 #define D_GEOMETRY_DEFAULT_WIDTH   640
 #define D_GEOMETRY_DEFAULT_HEIGHT  550
 
@@ -17,6 +21,15 @@ extern "C" {
 #define DW_COLOR_DEFAULT_PURPLE 0x800080
 #define DW_COLOR_DEFAULT_BGND   0xd6d6c0
 
+#define COMPRESS_COMMON_PREFIXES "index of ;re: ;fwd: ;www.;welcome to ;the "
+
+/* target enumeration */
+enum {
+  TARGET_SELF,
+  TARGET_TAB,
+  TARGET_WINDOW
+};
+
 /* define enumeration values to be returned */
 enum {
    PARSE_OK = 0,
@@ -38,6 +51,8 @@ typedef enum {
    DRC_TOKEN_TEXT_COLOR,
    DRC_TOKEN_USE_OBLIQUE,
    DRC_TOKEN_HOME,
+   DRC_TOKEN_SPLASH,
+   DRC_TOKEN_KEY_BINDINGS,
    DRC_TOKEN_PANEL_SIZE,
    DRC_TOKEN_SMALL_ICONS,
    DRC_TOKEN_FONT_FACTOR,
@@ -56,8 +71,10 @@ typedef enum {
    DRC_TOKEN_SHOW_URL,
    DRC_TOKEN_SHOW_SEARCH,
    DRC_TOKEN_SHOW_PROGRESS_BOX,
+   DRC_TOKEN_SHOW_POPUP_NAVIGATION,
    DRC_TOKEN_FULLWINDOW_START,
    DRC_TOKEN_TRANSIENT_DIALOGS,
+   DRC_TOKEN_POPUP_DIALOGS,
    DRC_TOKEN_FW_FONT,
    DRC_TOKEN_VW_FONT,
    DRC_TOKEN_GENERATE_SUBMIT,
@@ -66,6 +83,14 @@ typedef enum {
    DRC_TOKEN_SHOW_HTML_WARNINGS,
    DRC_TOKEN_SHOW_MSG,
    DRC_TOKEN_USE_OLD_PARSER,
+   DRC_TOKEN_KEYWORDS,
+   DRC_TOKEN_TARGET_BLANK,
+   DRC_TOKEN_TAB_LOAD_IN_BACKGROUND,
+   DRC_TOKEN_TAB_BAR_SHOW_FULLSCREEN,
+   DRC_TOKEN_TAB_BAR_SHOW_SINGLE_TAB,
+   DRC_TOKEN_TAB_BAR_SCROLLER,
+   DRC_TOKEN_TAB_TITLE_COMPRESS,
+   DRC_TOKEN_COMPRESS_COMMON_PREFIXES,
 
    DRC_TOKEN_LAST
 } Dillo_Rc_TokenType;
@@ -79,6 +104,8 @@ struct _DilloPrefs {
    gchar *no_proxy;
    gchar **no_proxy_vec;
    DilloUrl *home;
+   gchar *splash;
+   gboolean show_splash;
    guint32 link_color;
    guint32 visited_color;
    guint32 bg_color;
@@ -105,8 +132,10 @@ struct _DilloPrefs {
    gboolean show_url;
    gboolean show_search;
    gboolean show_progress_box;
+   gboolean show_popup_navigation;
    gboolean fullwindow_start;
    gboolean transient_dialogs;
+   gboolean popup_dialogs;
    gchar *vw_fontname;
    gchar *fw_fontname;
    gboolean generate_submit;
@@ -115,6 +144,16 @@ struct _DilloPrefs {
    gboolean show_html_warnings;
    gboolean show_msg;
    gboolean use_old_parser;
+   gchar *keywords;
+   gchar **keywords_vec;
+   gint target_blank;
+   gboolean tab_load_in_background;
+   gboolean tab_bar_show_fullscreen;
+   gboolean tab_bar_show_single_tab;
+   gboolean tab_bar_scroller;
+   gboolean tab_title_compress;
+   gchar *compress_common_prefixes;
+   gchar **compress_common_prefixes_vec;
 };
 
 /* Global Data */
diff -pruN dillo/src/progressbar.h dillo_tabs/src/progressbar.h
--- dillo/src/progressbar.h	2003-11-24 14:32:40.121772928 +0100
+++ dillo_tabs/src/progressbar.h	2003-11-24 14:32:40.233755904 +0100
@@ -27,6 +27,8 @@
 extern "C" {
 #endif /* __cplusplus */
 
+#define PBAR_L 32 /* max length of progress bar string */
+
 #define PBAR_ISTR(s)  (s) ? "" : "Images: "
 #define PBAR_PSTR(s)  (s) ? "" : "Page: "
 
diff -pruN dillo/src/selection.c dillo_tabs/src/selection.c
--- dillo/src/selection.c	2003-11-24 14:32:40.122772776 +0100
+++ dillo_tabs/src/selection.c	2003-11-24 14:32:40.233755904 +0100
@@ -76,7 +76,10 @@ void a_Selection_set_dclick_callback (Se
                                       void (*fn) (gpointer data),
                                       gpointer data)
 {
-   g_return_if_fail (selection->dclick_callback == NULL);
+   /* this used to return when the callback was already set,
+    * changed function to replace callback instead.
+    * g_return_if_fail (selection->dclick_callback == NULL);
+    */
    selection->dclick_callback = fn;
    selection->callback_data = data;
 }
@@ -426,7 +429,7 @@ static gint Selection_correct_char_pos (
    DwIterator *top = it->stack[it->stack_top];
    gint len;
 
-   if (top->content.type == DW_CONTENT_TEXT)
+   if (top->content.type & DW_CONTENT_TEXT)
       len = strlen(top->content.data.text);
    else
       len = 1;
@@ -486,7 +489,7 @@ static void  Selection_highlight0 (gbool
       for (i = a_Dw_ext_iterator_clone (a), start = TRUE;
            (cmp = a_Dw_ext_iterator_compare (i, b)) <= 0;
            a_Dw_ext_iterator_next (i), start = FALSE) {
-         if (i->content.type == DW_CONTENT_TEXT) {
+         if (i->content.type & DW_CONTENT_TEXT) {
             if (fl) {
                if (start) {
                   DEBUG_MSG (2, "    highlighting %s from %d to %d\n",
@@ -537,7 +540,7 @@ static void Selection_copy(Selection *se
 
       cmp = a_Dw_ext_iterator_compare (selection->from, selection->to);
       if (cmp == 0) {
-         if (selection->from->content.type == DW_CONTENT_TEXT) {
+         if (selection->from->content.type & DW_CONTENT_TEXT) {
             si = selection->from->stack[selection->from->stack_top];
             if (selection->from_char < selection->to_char)
                tmp = g_strndup(si->content.data.text + selection->from_char,
diff -pruN dillo/src/tab.c dillo_tabs/src/tab.c
--- dillo/src/tab.c	1970-01-01 01:00:00.000000000 +0100
+++ dillo_tabs/src/tab.c	2003-11-24 14:32:40.254752712 +0100
@@ -0,0 +1,490 @@
+/*
+ * File: tab.c
+ *
+ * Copyright (C) 2003 Frank de Lange <frank@unternet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version. */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <ctype.h>
+#include <gtk/gtk.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <fcntl.h>
+
+#include "misc.h"
+#include "dillo.h"
+#include "history.h"
+#include "nav.h"
+#include "doc.h"
+#include "IO/Url.h"
+#include "interface.h"
+#include "tab.h"
+#include "commands.h"
+#include "prefs.h"
+#include "menu.h"
+
+#include "dw_gtk_scrolled_window.h"
+#include "dw_gtk_viewport.h"
+
+#include <gdk/gdkkeysyms.h>
+
+#define DEBUG_EVENT  0
+#define DEBUG_SIZE  10
+#define DEBUG_ALLOC 10
+
+#define DEBUG_LEVEL 0
+#include "debug.h"
+
+/* BROWSER TABS */
+
+/* callbacks */
+
+/*
+ * callback for tab label resize_request. This adjusts
+ * the tab title to the width of the tab, compressing
+ * or shortening (according to preference) the title if needed 
+ */
+void
+Tab_label_size_request_callback(GtkWidget *widget,
+                                GtkAllocation *alloc,
+                                gpointer client_data)
+{
+   DilloDoc *dd = (DilloDoc *) client_data;
+   GtkLabel *tab_label = (GtkLabel *) widget;
+   gchar *tab_title, *short_title;
+   gint tab_title_size;
+
+   /* the tool tip is attached to the parent of the label, an event box */
+   tab_title = ((GtkTooltipsData *) gtk_tooltips_data_get(
+                                       GTK_WIDGET(
+                                        GTK_WIDGET(
+                                         tab_label)->parent)))->tip_text;
+
+   tab_title_size =
+     (gint) ((gfloat) alloc->width /
+	     dd->bw->tab_label_average_character_width);
+
+   /* compress or shorten the tab title? */
+   if (prefs.tab_title_compress) {
+     /* use title compression algorithm */
+     short_title = a_Misc_string_compress(tab_title,tab_title_size);
+   } else {
+     /* use simple shortening */
+     short_title = a_Misc_string_shorten(tab_title,tab_title_size);
+   }
+   gtk_label_set_text(GTK_LABEL(tab_label), short_title);
+   g_free(short_title);
+}
+
+/*
+ * callback for tab label button_press_event
+ *
+ * used to popup menu with tab options
+ *
+ * even though this is called 'tab_label_..., the callback is in
+ * fact attached to an event box surrounding the tab_label, as gtk
+ * label widgets can not receive events
+ */
+void
+Tab_label_button_press_event_callback(GtkWidget *widget,
+                                      GdkEventButton *event,
+                                      gpointer client_data)
+{
+   DilloDoc *dd = (DilloDoc *) client_data;
+   switch (event->button) {
+   case 1:
+     a_Tab_switch(dd);
+     break;
+   case 2:
+     /* currently unused */
+     DEBUG_MSG(DEBUG_EVENT,
+	       "You pressed button (%d) over a tab...\n",
+	       (gint) event->button);
+     break;
+   case 3:
+     /* popup tab options menu */
+     a_Menu_popup_over_tab(dd, event);
+     break;
+   default:
+     /* weird pointer with more than 3 buttons, ignore */
+     DEBUG_MSG(DEBUG_EVENT, 
+	       "You pressed button (%d) over a tab...\n",
+	       (gint) event->button);
+     break;
+   }
+}
+  
+/*
+ * callback for tab switch. This just calls a_Tab_switch for the docwin
+ * passed as page
+ */
+void
+Tab_switch_callback(GtkWidget *tabbrowser,
+                    GtkNotebookPage *page,
+                    gint page_num,
+                    gpointer client_data)
+{
+   DilloDoc *dd;
+   GtkWidget *docwin;
+
+   /* the notebook page contains a box, which contains the docwin as
+    * its first child, hence this hairy function... */
+   docwin =
+     g_list_nth_data(gtk_container_children(
+                      GTK_CONTAINER(gtk_notebook_get_nth_page(
+                       GTK_NOTEBOOK(tabbrowser),
+		      page_num))), 0);
+   dd = a_Doc_get_by_docwin(docwin);
+
+   if(dd)
+     a_Tab_switch(dd);
+}
+
+/*
+ * callback for tab add
+ */
+void
+Tab_add_callback(GtkContainer *container,
+                 GtkWidget *widget,
+                 gpointer client_data)
+{
+   DEBUG_MSG(DEBUG_EVENT,
+	     "I am the tab_add callback in src/tab.c!\n");
+}
+
+/*
+ * callback for tab remove
+ */
+void
+Tab_remove_callback(GtkContainer *container,
+                    GtkWidget *widget,
+                    gpointer client_data)
+{
+   DEBUG_MSG(DEBUG_EVENT,
+	     "I am the tab remove callback, in src/tab.c!\n");
+}
+
+/*
+ * callback for tab browser destroy
+ */
+void
+Tab_browser_destroy_callback(GtkContainer *container,
+			     GtkWidget *widget,
+			     gpointer client_data)
+{
+   DEBUG_MSG(DEBUG_EVENT,
+	     "I am the tab browser destroy callback, in src/tab.c!\n");
+}
+
+/* Public functions */
+
+/*
+ * return a new tab browser
+ */
+GtkContainer *
+a_Tab_browser_new(BrowserWindow *bw)
+{
+   GtkRequisition *req;
+   GtkWidget *label;
+   GtkNotebook *tabbrowser;
+
+   /* try to calulate the average width of a character in a label
+    * in the current context. This is something of a hack...
+    */
+   req = g_new0(GtkRequisition, 1);
+   label = gtk_label_new(D_QUICK_BROWN_FOX);
+   gtk_widget_show(label);
+   gtk_widget_size_request(GTK_WIDGET(label), req);
+   bw->tab_label_average_character_width =
+     (gfloat) ((gfloat) req->width / (gfloat) strlen(GTK_LABEL(label)->label));
+   g_free(req);
+
+   /* Add tabbrowser */
+   tabbrowser = GTK_NOTEBOOK(gtk_notebook_new());
+   gtk_notebook_set_show_border(GTK_NOTEBOOK(tabbrowser), FALSE);
+   gtk_notebook_set_scrollable(GTK_NOTEBOOK(tabbrowser),
+			       prefs.tab_bar_scroller);
+   gtk_notebook_set_homogeneous_tabs(GTK_NOTEBOOK(tabbrowser), TRUE);
+   gtk_signal_connect_after(GTK_OBJECT(tabbrowser),
+			    "switch_page",
+			    GTK_SIGNAL_FUNC(Tab_switch_callback),
+			    (gpointer) bw);
+   
+   gtk_signal_connect_after(GTK_OBJECT(tabbrowser),
+                            "add",
+                            GTK_SIGNAL_FUNC(Tab_add_callback),
+                            (gpointer) bw);
+
+   gtk_signal_connect(GTK_OBJECT(tabbrowser),
+                            "remove",
+                            GTK_SIGNAL_FUNC(Tab_remove_callback),
+                            (gpointer) bw);
+   /* commented out, currently not in use...  
+   gtk_signal_connect(GTK_OBJECT(tabbrowser),
+                            "destroy",
+                            GTK_SIGNAL_FUNC(Tab_browser_destroy_callback),
+                            (gpointer) bw);
+   */
+
+   return GTK_CONTAINER(tabbrowser);
+}
+
+/*
+ * switch tab, update interface
+ */
+void
+a_Tab_switch(DilloDoc *dd)
+{
+   /* set bw's current root document */
+   dd->bw->dd = a_Doc_get_root(dd);
+
+   /* add the browser window's full_screen_off_button to the dd's
+    * docwin. This button is owned by the window (this makes it easier
+    * to move tabs between windows, if needed), so it needs to
+    * be moved from tab to tab when the tab becomes current (as
+    * GTK does not allow a widget to have more than one parent).
+    *
+    * XXX:HACK
+    */
+   if (GTK_WIDGET(dd->bw->full_screen_off_button)->parent != dd->docwin) {
+     /* keep a ref to the widget to keep it alive in transit... */
+     gtk_object_ref(GTK_OBJECT(dd->bw->full_screen_off_button));
+     if (GTK_WIDGET(dd->bw->full_screen_off_button)->parent)
+       gtk_container_remove(GTK_CONTAINER(
+                  GTK_WIDGET(dd->bw->full_screen_off_button)->parent),
+		  GTK_WIDGET(dd->bw->full_screen_off_button));
+     a_Dw_gtk_scrolled_window_add_gadget(GTK_DW_SCROLLED_WINDOW (dd->docwin),
+					 dd->bw->full_screen_off_button);
+     /* ref is no longer needed, discard it... */
+     gtk_object_unref(GTK_OBJECT(dd->bw->full_screen_off_button));
+
+     /* XXX:HACK the docwin does not show (or raise?) the
+      * widget when needed... */
+     if(GTK_WIDGET_VISIBLE(dd->bw->full_screen_off_button)) {
+       gtk_widget_hide(dd->bw->full_screen_off_button);
+       gtk_widget_show(dd->bw->full_screen_off_button);
+     }
+   }
+
+   /* update progress bar and adjust button sensitivity */
+   a_Doc_progress_update(dd);
+
+   /* adjust tab specific interface elements */
+   if (dd->nav_stack_ptr != -1)
+     /* this tab has a document loaded, set values accordingly */
+     {
+       a_Interface_set_page_title(dd->bw,
+				  (gchar *) a_History_get_title(NAV_TOP(dd)));
+       a_Interface_set_location_text(dd->bw,
+				     URL_STR_(a_History_get_url(NAV_TOP(dd))));
+     } else {
+       /* no document loaded in tab, use default values */
+       a_Interface_set_page_title(dd->bw, "(Untitled)");
+       a_Interface_set_location_text(dd->bw,"");
+     }
+
+   /* show tab */
+   gtk_notebook_set_page(GTK_NOTEBOOK(dd->bw->container),
+			 gtk_notebook_page_num(GTK_NOTEBOOK(dd->bw->container),
+			 GTK_WIDGET(dd->docwin)->parent));
+}
+
+/*
+ * set visibility of tab bar
+ */
+void
+a_Tab_visibility_update(BrowserWindow *bw)
+{
+   gboolean hide_tabs;
+
+   g_return_if_fail(bw != NULL);
+
+   /* this gets called in the tab switching code, which in turn gets
+    * called when a tab is deleted. This includes the final tab, which
+    * in turn causes the main_window to be destroyed, which of course
+    * includes the tabbrowser... So, check first if there is still a
+    * tabbrowser around to update before doing anything else...
+    */
+   if(!GTK_IS_CONTAINER(bw->container))
+     return;
+   
+   /* HIDE tab bar IF
+    * -  there is only one tab left AND
+    *    the preference tab_bar_show_single_tab is set to NO
+    *  OR
+    * - when using fullscreen AND
+    *    the preference tab_bar_show_fullscreen is set to NO
+    * OTHERWISE
+    * SHOW tab bar
+    */
+   hide_tabs = 
+     ((g_list_length(gtk_container_children(
+		      GTK_CONTAINER(bw->container))) == 1)
+                && (!prefs.tab_bar_show_single_tab));
+   hide_tabs |= (bw->fullwindow
+                 && (!prefs.tab_bar_show_fullscreen));
+   gtk_notebook_set_show_tabs(GTK_NOTEBOOK(bw->container), !hide_tabs);
+}
+
+
+/*
+ * close a specific browser tab,
+ * close window if no tabs left
+ *
+ * NOTE: this will destroy the document which
+ * was displayed in the tab, through the destroy callback
+ * for the docwin (in src/doc.c). If documents have to
+ * be moved between tabs or frames, unparent the
+ * pagemarks menu and reparent dd->docwin before calling
+ * this function!
+ */
+void
+a_Tab_close(DilloDoc *dd)
+{
+   gint page_num;
+   BrowserWindow *bw = dd->bw;
+
+   g_return_if_fail (dd != NULL);
+
+   page_num = gtk_notebook_page_num(GTK_NOTEBOOK(bw->container),
+				    GTK_WIDGET(dd->docwin)->parent);
+
+   /* left-to-right tab deletion:
+    * if this is the current page, show next page */
+   if(gtk_notebook_get_current_page(GTK_NOTEBOOK(bw->container)) == page_num)
+     gtk_notebook_next_page(GTK_NOTEBOOK(bw->container));
+
+   /* this will destroy dd->docwin (and the DilloDoc)
+    * if there are no references left to it */
+   gtk_notebook_remove_page(GTK_NOTEBOOK(bw->container), page_num);
+
+   if (gtk_notebook_get_current_page(GTK_NOTEBOOK(bw->container)) == -1)
+     {
+       /* destroying the main window causes its destroy
+          callback to be fired. This takes down the
+          rest of the BrowserWindow */
+       gtk_widget_destroy(GTK_WIDGET(bw->main_window));
+     }
+
+   a_Tab_visibility_update(bw);
+}
+
+/*
+ * Add a tab (containing a new DilloDoc) to a browser window
+ */
+void
+a_Tab_new(BrowserWindow *bw)
+{
+   DilloDoc *dd;
+
+   g_return_if_fail (bw != NULL);
+
+   dd = a_Doc_new();
+   a_Tab_add(bw, dd);
+
+   /* always show new tabs, even when background loading */
+   a_Tab_switch(dd);
+}
+
+
+/*
+ * Add a tab (containing an existing DilloDoc) to a browser window
+ */
+void
+a_Tab_add(BrowserWindow *bw, DilloDoc *dd)
+{
+   GString *tab_title;
+   GtkWidget *tab_label, *tab_label_event_box, *box;
+   GtkPackType *pack_type = g_new0(GtkPackType, 1);
+
+   g_return_if_fail (bw != NULL && dd != NULL);
+
+   a_Doc_set_browserwindow(dd, bw);
+
+   if (dd->nav_stack_ptr != -1)
+     {
+       tab_title = g_string_new(a_History_get_title(NAV_TOP(dd)));
+     } else {
+       tab_title = g_string_new("(Untitled)");
+     }
+   
+   tab_label_event_box = gtk_event_box_new();
+   tab_label = gtk_label_new(tab_title->str);
+   gtk_widget_show(tab_label);
+   gtk_container_add(GTK_CONTAINER(tab_label_event_box), tab_label);
+   box = gtk_hbox_new(TRUE, 0);
+   gtk_container_set_border_width(GTK_CONTAINER(box), 0);
+   gtk_box_pack_start(GTK_BOX(box), GTK_WIDGET(dd->docwin), TRUE, TRUE, 0);
+   if(dd->frameset)
+     gtk_box_pack_start(GTK_BOX(box), GTK_WIDGET(dd->frameset), TRUE, TRUE, 0);
+   gtk_widget_show(box);
+   gtk_notebook_append_page(GTK_NOTEBOOK(bw->container),
+			    box, tab_label_event_box);
+
+   /* add tooltip with full title. This has to be attached to the event box,
+    * since label widgets are windowless and hence can not receive events */
+   gtk_tooltips_set_tip(GTK_TOOLTIPS(dd->bw->tooltips),
+                        GTK_WIDGET(tab_label_event_box),
+                        tab_title->str,
+                        tab_title->str);
+
+   /* make sure the tab labels adjust their
+    * size according to available space */
+   gtk_signal_connect_after(GTK_OBJECT(tab_label),
+                            "size_allocate",
+                            GTK_SIGNAL_FUNC(Tab_label_size_request_callback),
+                            (gpointer) dd);
+
+   /* catch button press events for showing menu, etc. This has to be attached
+    * to the event box, since label widgets are windowless and hence can not
+    * receive events */
+   gtk_signal_connect_after(GTK_OBJECT(tab_label_event_box),
+                      "button_press_event",
+                      GTK_SIGNAL_FUNC(Tab_label_button_press_event_callback),
+                      (gpointer) dd);
+
+   g_string_free(tab_title, TRUE);
+   gtk_notebook_query_tab_label_packing(GTK_NOTEBOOK(bw->container),
+					GTK_WIDGET(dd->docwin)->parent,
+					NULL, NULL, pack_type);
+   gtk_notebook_set_tab_label_packing(GTK_NOTEBOOK(bw->container),
+				      GTK_WIDGET(dd->docwin)->parent,
+				      TRUE, TRUE, *pack_type);
+   g_free(pack_type);
+   a_Tab_visibility_update(bw);
+}
+
+
+/*
+ * set tab title
+ * also sets window title if this tab is the current tab
+ */
+void
+a_Tab_title_set(DilloDoc *dd, gchar *title)
+{
+   GtkWidget *tab_label;
+
+   g_return_if_fail (dd != NULL && title != NULL);
+
+   /* set tooltip on tab label container (a GtkEventBox), and queue a resize
+    * for the label itself so the title is redrawn */
+   tab_label = gtk_notebook_get_tab_label(GTK_NOTEBOOK(dd->bw->container),
+					  GTK_WIDGET(dd->docwin)->parent);
+   gtk_tooltips_set_tip(GTK_TOOLTIPS(dd->bw->tooltips),
+                       GTK_WIDGET(tab_label),
+                       title,
+                       title);
+
+   gtk_widget_queue_resize(GTK_WIDGET(GTK_BIN(tab_label)->child));
+
+   /* if this is the current tab, set window title */
+   if (dd->bw->dd == dd)
+     a_Interface_set_page_title(dd->bw, title);
+}
diff -pruN dillo/src/tab.h dillo_tabs/src/tab.h
--- dillo/src/tab.h	1970-01-01 01:00:00.000000000 +0100
+++ dillo_tabs/src/tab.h	2003-11-24 14:32:40.254752712 +0100
@@ -0,0 +1,20 @@
+#ifndef __TAB_H__
+#define __TAB_H__
+
+#include "browser.h"
+
+/* this string is used to calculate the average width of a
+ * character in a GTK_LABEL in the current context.
+ */
+#define D_QUICK_BROWN_FOX "The Quick Brown Fox Jumped Over The Lazy Dog"
+
+GtkContainer *
+a_Tab_browser_new(BrowserWindow *bw);
+void a_Tab_new(BrowserWindow *bw);
+void a_Tab_close(DilloDoc *dd);
+void a_Tab_add(BrowserWindow *bw, DilloDoc *dd);
+void a_Tab_switch(DilloDoc *dd);
+void a_Tab_title_set(DilloDoc *dd, gchar *title);
+void a_Tab_visibility_update(BrowserWindow *bw);
+
+#endif /* __TAB_H__ */
diff -pruN dillo/src/url.c dillo_tabs/src/url.c
--- dillo/src/url.c	2003-11-24 14:32:40.123772624 +0100
+++ dillo_tabs/src/url.c	2003-11-24 14:32:40.235755600 +0100
@@ -36,8 +36,11 @@
 #include <string.h>
 #include <ctype.h>
 #include <glib.h>
+#include <unistd.h>      /* for access() */
 
 #include "url.h"
+#include "misc.h"        /* for a_Misc_escape_chars() */
+#include "prefs.h"       /* for prefs.keywords_vec */
 
 //#define DEBUG_LEVEL 2
 #include "debug.h"
@@ -172,6 +175,7 @@ void a_Url_free(DilloUrl *url)
       g_free((gchar *)url->buffer);
       g_free((gchar *)url->data);
       g_free((gchar *)url->alt);
+      g_free((gchar *)url->target);
       g_free(url);
    }
 }
@@ -323,8 +327,10 @@ done:
  *     flags              = 0
  *     data               = NULL
  *     alt                = NULL
+ *     target             = NULL
  *     ismap_url_len      = 0
  *     scrolling_position = 0
+ *     cursor             = (0,0)
  *  }
  *
  *  Return NULL if URL is badly formed.
@@ -390,8 +396,10 @@ DilloUrl* a_Url_dup(const DilloUrl *ori)
    url->flags              = ori->flags;
    url->data               = g_strdup(ori->data);
    url->alt                = g_strdup(ori->alt);
+   url->target             = g_strdup(ori->target);
    url->ismap_url_len      = ori->ismap_url_len;
    url->scrolling_position = ori->scrolling_position;
+   url->cursor             = ori->cursor;
 
    return url;
 }
@@ -452,6 +460,18 @@ void a_Url_set_alt(DilloUrl *u, const gc
 }
 
 /*
+ * Set DilloUrl target (used to target link at specific frame or window)
+ */
+void a_Url_set_target(DilloUrl *u, const gchar *target)
+{
+   if (u) {
+      if (u->target)
+ 	 g_free((gchar *)u->target);
+      u->target = g_strdup(target);
+   }
+}
+
+/*
  * Set DilloUrl scrolling position
  */
 void a_Url_set_pos(DilloUrl *u, gint pos)
@@ -461,6 +481,17 @@ void a_Url_set_pos(DilloUrl *u, gint pos
 }
 
 /*
+ * Set DilloUrl cursor
+ */
+void a_Url_set_cursor(DilloUrl *u, DwLocation *cursor)
+{
+  if (u && cursor) {
+     u->cursor.x = cursor->x;
+     u->cursor.y = cursor->y;
+  }
+}
+
+/*
  * Set DilloUrl ismap coordinates
  * (this is optimized for not hogging the CPU)
  */
@@ -554,3 +585,96 @@ gchar *a_Url_string_strip_delimiters(gch
    }
    return new_str;
 }
+
+/*
+ * Process keywords in url string. This scans the start of the url string
+ * for known keywords, and transforms the string if one is found.
+ * The returned string should be g_free()'d by the caller
+ */
+gchar *a_Url_string_process_keywords(gchar *str)
+{
+   gchar **keywords, **keyword, **contents;
+   gchar *parameters, *url_str;
+   gint n;
+
+   url_str = NULL;
+
+   if (str && *str) {
+      keywords = prefs.keywords_vec;
+      contents = g_strsplit(g_strstrip(str), " ", 1);
+      
+      /* handle keywords */
+      if(keywords) {
+         for(n = 0; keywords[n]; n++) {
+            keyword = g_strsplit(keywords[n], ":", 1);
+            if(keywords[n] &&
+               keyword[1] &&
+               !strcmp(contents[0], keyword[0])) {
+               url_str = keyword[1];
+               if(contents[1] &&
+                  (parameters = a_Misc_escape_chars(contents[1], " +\""))) {
+                  url_str = g_strdup_printf(keyword[1], parameters);
+                  g_free(parameters);
+               } else
+                  url_str = g_strdup_printf(keyword[1], (contents[1] ?
+                                                         contents[1] :
+                                                         ""));
+               g_strfreev(keyword);
+               break;
+            }
+         }
+      }
+      
+      if(!url_str)
+         url_str = g_strjoinv(NULL, contents);
+   }
+   return url_str;
+}
+
+/*
+ * Check string for local content. If found, it transforms a
+ * unix path declaration into a valid file: url. Otherwise it
+ * just returns a copy of the string.
+ * set relative to TRUE if you also want to handle relative
+ * paths (from the directory where dillo was started)
+ * the returned string should be g_free()'d by the caller
+ */
+gchar *a_Url_string_process_local(gchar *str, gboolean relative)
+{
+   gchar *url_str, *copy_url_str, *p, *cd;
+   gboolean is_file = FALSE;
+
+   url_str = copy_url_str = NULL;
+
+   /* This check is for files with non-URL characters */
+   if (access(str, F_OK) == 0) {
+      p = a_Misc_escape_chars(str, "% ");
+      url_str = g_strdup(p ? p : str);
+      is_file = TRUE;
+   } else
+      url_str = copy_url_str = g_strdup(str);
+
+   if ((is_file || access(url_str, F_OK) == 0) &&
+       (relative || (url_str[0] == '/'))) {
+      GString *UrlStr = g_string_sized_new(128);
+
+      if (url_str[0] == '/') {
+         g_string_sprintf(UrlStr, "file:%s", url_str);
+      } else {
+         cd = g_get_current_dir();
+         p = a_Misc_escape_chars(cd, "% ");
+         g_string_sprintf(UrlStr, "file:%s", p ? p : cd);
+         g_free(p);
+         g_free(cd);
+         if (UrlStr->str[UrlStr->len - 1] != '/')
+            g_string_append(UrlStr, "/");
+         g_string_append(UrlStr, url_str);
+      }
+      url_str = UrlStr->str;
+      g_string_free(UrlStr, FALSE);
+      g_free(copy_url_str);
+   }
+
+   return url_str;
+}
+
diff -pruN dillo/src/url.h dillo_tabs/src/url.h
--- dillo/src/url.h	2003-11-24 14:32:40.124772472 +0100
+++ dillo_tabs/src/url.h	2003-11-24 14:32:40.235755600 +0100
@@ -12,6 +12,7 @@
 
 #include <string.h>       /* for strcmp */
 #include <glib.h>
+#include "dw_widget.h"    /* for DwLocation */
 
 
 #define DILLO_URL_HTTP_PORT        80
@@ -55,11 +56,14 @@
 #define URL_DATA_(u)           u->data
 #define URL_ALT_(u)            u->alt
 #define URL_STR_(u)            a_Url_str(u)
+#define URL_TARGET_(u)         u->target
 /* these return an integer */
 #define URL_PORT_(u)           (URL_HOST(u) ? u->port : u->port)
 #define URL_FLAGS_(u)          u->flags
 #define URL_POS_(u)            u->scrolling_position
-#define URL_ILLEGAL_CHARS_(u)  url->illegal_chars
+#define URL_ILLEGAL_CHARS_(u)  u->illegal_chars
+/* this returns a pointer to DwLocation */
+#define URL_CURSOR_(u)         &u->cursor
 
 /*
  * Access methods that always return a string:
@@ -76,11 +80,12 @@
 #define URL_DATA(u)           NPTR2STR(URL_DATA_(u))
 #define URL_ALT(u)            NPTR2STR(URL_ALT_(u))
 #define URL_STR(u)            NPTR2STR(URL_STR_(u))
+#define URL_TARGET(u)         NPTR2STR(URL_TARGET_(u))
 #define URL_PORT(u)           URL_PORT_(u)
 #define URL_FLAGS(u)          URL_FLAGS_(u)
 #define URL_POS(u)            URL_POS_(u)
 #define URL_ILLEGAL_CHARS(u)  URL_ILLEGAL_CHARS_(u)
-
+#define URL_CURSOR(u)         URL_CURSOR_(u)
 
 
 /* URL-camp compare methods */
@@ -113,9 +118,11 @@ struct _DilloUrl {
    gint flags;
    const gchar *data;              /* POST */
    const gchar *alt;               /* "alt" text (used by image maps) */
+   const gchar *target;            /* target frame/window */
    gint ismap_url_len;             /* Used by server side image maps */
    gint scrolling_position;        /* remember position of visited urls */
    gint illegal_chars;             /* number of illegal chars */
+   DwLocation cursor;              /* remember cursor for visited urls */
 };
 
 
@@ -129,10 +136,14 @@ gint a_Url_cmp(const DilloUrl* A, const 
 void a_Url_set_flags(DilloUrl *u, gint flags);
 void a_Url_set_data(DilloUrl *u, gchar *data);
 void a_Url_set_alt(DilloUrl *u, const gchar *alt);
+void a_Url_set_target(DilloUrl *u, const gchar *target);
 void a_Url_set_pos(DilloUrl *u, gint pos);
+void a_Url_set_cursor(DilloUrl *u, DwLocation *cursor);
 void a_Url_set_ismap_coords(DilloUrl *u, gchar *coord_str);
 gchar *a_Url_parse_hex_path(const DilloUrl *u);
 gchar *a_Url_string_strip_delimiters(gchar *str);
+gchar *a_Url_string_process_keywords(gchar *str);
+gchar *a_Url_string_process_local(gchar *str, gboolean relative);
 
 #ifdef __cplusplus
 }
diff -pruN dillo/src/web.c dillo_tabs/src/web.c
--- dillo/src/web.c	2003-11-24 14:32:40.124772472 +0100
+++ dillo_tabs/src/web.c	2003-11-24 14:32:40.236755448 +0100
@@ -17,7 +17,7 @@
 #include "msg.h"
 #include "browser.h"
 #include "nav.h"
-#include "interface.h"
+#include "doc.h"
 #include "IO/IO.h"
 #include "IO/mime.h"
 
@@ -44,52 +44,45 @@ DwWidget* a_Web_dispatch_by_type (const 
                                   CA_Callback_t *Call, void **Data)
 {
    DwWidget *dw = NULL;
-   DwStyle style_attrs, *style;
-   DwStyleFont font;
+   //   DwStyle style_attrs, *style;
+   //   DwStyleFont font;
 
    DEBUG_MSG(1, "a_Web_dispatch_by_type\n");
 
-   g_return_val_if_fail(Web->bw != NULL, NULL);
+   g_return_val_if_fail(Web->dd != NULL, NULL);
 
    if (Web->flags & WEB_RootUrl) {
       /* We have RootUrl! */
       dw = a_Mime_set_viewer(Type, Web, Call, Data);
       g_return_val_if_fail(dw != NULL, NULL);
 
-      /* Set a style for the widget */
-      font.name = prefs.vw_fontname; /* must be defined */
-      font.size = rint(12.0 * prefs.font_factor);
-      font.weight = 400;
-      font.style = DW_STYLE_FONT_STYLE_NORMAL;
-
-      a_Dw_style_init_values (&style_attrs, Web->bw->main_window->window);
-      a_Dw_style_box_set_val (&style_attrs.margin, 5);
-      style_attrs.font = a_Dw_style_font_new (&font);
-      style_attrs.color =
-         a_Dw_style_color_new (prefs.text_color, Web->bw->main_window->window);
-      style_attrs.background_color =
-         a_Dw_style_color_new (prefs.bg_color, Web->bw->main_window->window);
-      style = a_Dw_style_new (&style_attrs, Web->bw->main_window->window);
-      a_Dw_widget_set_style (dw, style);
-      a_Dw_style_unref (style);
+      /* set widget style */
+      a_Dw_widget_set_style(dw, Web->dd->style);
 
       a_Dw_gtk_scrolled_window_set_dw(
-         GTK_DW_SCROLLED_WINDOW(Web->bw->docwin), dw);
+         GTK_DW_SCROLLED_WINDOW(Web->dd->docwin), dw);
 
       if (URL_POS(Web->url))
         a_Dw_gtk_scrolled_window_set_scrolling_position(
-           GTK_DW_SCROLLED_WINDOW(Web->bw->docwin), URL_POS(Web->url));
+           GTK_DW_SCROLLED_WINDOW(Web->dd->docwin), URL_POS(Web->url));
       else
         a_Dw_gtk_scrolled_window_set_anchor(
-           GTK_DW_SCROLLED_WINDOW(Web->bw->docwin), URL_FRAGMENT_(Web->url));
+           GTK_DW_SCROLLED_WINDOW(Web->dd->docwin), URL_FRAGMENT_(Web->url));
 
-      /* Clear the title bar for pages without a <TITLE> tag */
-      a_Interface_set_page_title(Web->bw, "");
-      a_Interface_set_location_text(Web->bw, URL_STR(Web->url));
-      a_Interface_reset_progress_bars(Web->bw);
+      if (URL_CURSOR(Web->url))
+        a_Dw_gtk_scrolled_window_set_cursor(
+           GTK_DW_SCROLLED_WINDOW(Web->dd->docwin), URL_CURSOR(Web->url));
+      
+      /* Set document title to url, will be replaced by real title
+       * if this page has a <TITLE> tag */
+      a_Doc_title_set(Web->dd, URL_STR(Web->url));
+
+      /* update progress */
+      a_Doc_progress_update(Web->dd);
+      a_Doc_location_set(Web->dd, URL_STR(Web->url));
 
       /* Let the Nav module know... */
-      a_Nav_expect_done(Web->bw);
+      a_Nav_expect_done(Web->dd);
 
    } else {
       /* A non-RootUrl. At this moment we only handle image-children */
@@ -116,7 +109,7 @@ DilloWeb* a_Web_new(const DilloUrl *url)
    DilloWeb *web= g_new(DilloWeb, 1);
 
    web->url = a_Url_dup(url);
-   web->bw = NULL;
+   web->dd = NULL;
    web->flags = 0;
    web->Image = NULL;
    web->stream  = NULL;
diff -pruN dillo/src/web.h dillo_tabs/src/web.h
--- dillo/src/web.h	2003-11-24 14:32:40.124772472 +0100
+++ dillo_tabs/src/web.h	2003-11-24 14:32:40.236755448 +0100
@@ -23,7 +23,7 @@ typedef struct _DilloWeb DilloWeb;
 
 struct _DilloWeb {
   DilloUrl *url;              /* Requested URL */
-  BrowserWindow *bw;          /* The requesting browser window [reference] */
+  DilloDoc *dd;               /* The requesting browser document [reference] */
   gint flags;                 /* Additional info */
 
   DilloImage *Image;          /* For image urls [reference] */
