changeset 341:215da0caf90b

- Implemented tabbed browsing.
author jcid
date Thu, 18 Sep 2008 00:16:38 +0200
parents da33058e94be
children d43e952efbf3
files ChangeLog src/findbar.cc src/findbar.hh src/menu.cc src/nav.c src/pixmaps.h src/ui.cc src/ui.hh src/uicmd.cc src/uicmd.hh
diffstat 10 files changed, 253 insertions(+), 165 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Wed Sep 17 23:40:06 2008 +0200
+++ b/ChangeLog	Thu Sep 18 00:16:38 2008 +0200
@@ -57,6 +57,7 @@
  - Hooked a decoder for text/plain with charset.
  - Forbid dpi GET and POST from non dpi-generated urls.
  - Cleaned up a_Url_new().
+ - Implemented tabbed browsing.
    Patches: Jorge Arellano Cid
 +- Connected signals to <li> elements (fixes links within lists).
  - Enabled text, background-color, panel_size, geometry, fullscreen,
--- a/src/findbar.cc	Wed Sep 17 23:40:06 2008 +0200
+++ b/src/findbar.cc	Thu Sep 18 00:16:38 2008 +0200
@@ -10,13 +10,13 @@
  */
 
 #include <fltk/events.h>
+#include <fltk/Window.h>
 #include "findbar.hh"
 
 #include "msg.h"
 #include "pixmaps.h"
 #include "uicmd.hh"
 #include "bw.h"
-#include "ui.hh"
 
 /*
  * Local sub class
@@ -31,12 +31,12 @@
 
 int MyInput::handle(int e)
 {
-   _MSG("findbar NewInput::handle()\n");
+   _MSG("findbar MyInput::handle()\n");
    int ret = 1, k = event_key();
    unsigned modifier = event_state() & (SHIFT | CTRL | ALT | META);
    if (modifier == 0) {
       if (e == KEY && k == EscapeKey) {
-         _MSG("findbar NewInput: caught EscapeKey\n");
+         _MSG("findbar CustInput: caught EscapeKey\n");
          ret = 0;
       }
    }
@@ -55,7 +55,7 @@
    bool case_sens = fb->cb->value();
 
    if (key[0] != '\0')
-      a_UIcmd_findtext_search((BrowserWindow *) fb->ui->user_data(),
+      a_UIcmd_findtext_search((BrowserWindow *) fb->window()->user_data(),
                               key, case_sens);
 }
 
@@ -83,7 +83,7 @@
 /*
  * Construct text search bar
  */
-Findbar::Findbar(int width, int height, UI *ui) :
+Findbar::Findbar(int width, int height) :
    Group(0, 0, width, height)
 {
    int button_width = 70;
@@ -93,8 +93,8 @@
    int x = border;
    height -= 2 * border;
 
-   this->ui = ui;
-   this->hide();
+   box(PLASTIC_UP_BOX);
+   Group::hide();
 
    begin();
     hidebutton = new HighlightButton(x, border, 16, height, 0);
@@ -113,7 +113,7 @@
     i->callback(search_cb2, this);
 
     // todo: search previous would be nice
-    findb = new HighlightButton(x, border, button_width, height, "&Next");
+    findb = new HighlightButton(x, border, button_width, height, "Next");
     x += button_width + gap;
     findb->tooltip("Find next occurrence of the search phrase");
     findb->add_shortcut(ReturnKey);
@@ -140,7 +140,7 @@
    int k = event_key();
    unsigned modifier = event_state() & (SHIFT | CTRL | ALT | META);
 
-   if (modifier == 0 && k == EscapeKey) {
+   if (event == KEY && modifier == 0 && k == EscapeKey) {
       hide();
       ret = 1;
    }
@@ -170,6 +170,7 @@
    BrowserWindow *bw;
 
    Group::hide();
-   if ((bw = (BrowserWindow *) ui->user_data()))
+   if ((bw = (BrowserWindow *) this->window()->user_data()))
       a_UIcmd_findtext_reset(bw);
+   a_UIcmd_focus_main_area(bw);
 }
--- a/src/findbar.hh	Wed Sep 17 23:40:06 2008 +0200
+++ b/src/findbar.hh	Thu Sep 18 00:16:38 2008 +0200
@@ -9,9 +9,6 @@
 #include <fltk/Group.h>
 #include <fltk/CheckButton.h>
 
-// simple declaration to avoid circular include
-class UI;
-
 using namespace fltk;
 
 /*
@@ -22,7 +19,6 @@
    Button *clrb;
    HighlightButton *hidebutton;
    xpmImage *hideImg;
-   UI *ui;
    Input *i;
    CheckButton *cb;
    
@@ -31,7 +27,7 @@
    static void hide_cb (Widget *, void *);
 
 public:
-   Findbar(int width, int height, UI *ui);
+   Findbar(int width, int height);
    ~Findbar();
    int handle(int event);
    void show();
--- a/src/menu.cc	Wed Sep 17 23:40:06 2008 +0200
+++ b/src/menu.cc	Thu Sep 18 00:16:38 2008 +0200
@@ -49,10 +49,10 @@
  * Used to add the hint for history popup menus, and to remember
  * the mouse button pressed over a menu item.
  */
-class NewItem : public Item {
+class CustItem : public Item {
    int EventButton;
 public:
-   NewItem (const char* label) : Item(label) { EventButton = 0; };
+   CustItem (const char* label) : Item(label) { EventButton = 0; };
    int button () { return EventButton; };
    void draw();
    int handle(int e) {
@@ -65,7 +65,7 @@
  * This adds a call to a_UIcmd_set_msg() to show the URL in the status bar
  * TODO: erase the URL on popup close.
  */
-void NewItem::draw() {
+void CustItem::draw() {
    DilloUrl *url;
 
    if (flags() & SELECTED) {
@@ -217,7 +217,7 @@
  */
 static void Menu_history_cb(Widget *wid, void *data)
 {
-   int mb = ((NewItem*)wid)->button();
+   int mb = ((CustItem*)wid)->button();
    int offset = history_direction * VOIDP2INT(data);
 
    if (mb == 2) {
@@ -438,7 +438,7 @@
    pm->begin();
     for (i = 0; history_list[i] != -1; i += 1) {
        // TODO: restrict title size
-       it = new NewItem(a_History_get_title(history_list[i], 1));
+       it = new CustItem(a_History_get_title(history_list[i], 1));
        it->callback(Menu_history_cb, (void*)(i+1));
     }
    pm->type(PopupMenu::POPUP123);
--- a/src/nav.c	Wed Sep 17 23:40:06 2008 +0200
+++ b/src/nav.c	Thu Sep 18 00:16:38 2008 +0200
@@ -396,7 +396,7 @@
    BrowserWindow *newbw;
 
    a_UIcmd_get_wh(bw, &w, &h);
-   newbw = a_UIcmd_browser_window_new(w, h, bw->ui);
+   newbw = a_UIcmd_browser_window_new(w, h, bw);
    a_Nav_push(newbw, url);
 }
 
--- a/src/pixmaps.h	Wed Sep 17 23:40:06 2008 +0200
+++ b/src/pixmaps.h	Thu Sep 18 00:16:38 2008 +0200
@@ -1497,8 +1497,8 @@
 /* XPM */
 static const char *const imgload_on_xpm[] = {
 "15 15 2 1",
-" 	c #FFFFFFFFFFFF",
-".	c #00000000CF3C",
+"       c None",
+".      c #00000000CF3C",
 "               ",
 " . .   .  ...  ",
 " . .. .. .   . ",
@@ -1518,8 +1518,8 @@
 /* XPM */
 static const char *const imgload_off_xpm[] = {
 "15 15 2 1",
-" 	c #FFFFFFFFFFFF",
-".	c #CF3C00000000",
+"       c None",
+".      c #CF3C00000000",
 "               ",
 " . .   .  ...  ",
 " . .. .. .   . ",
--- a/src/ui.cc	Wed Sep 17 23:40:06 2008 +0200
+++ b/src/ui.cc	Thu Sep 18 00:16:38 2008 +0200
@@ -39,9 +39,9 @@
  * (Used to avoid certain shortcuts in the location bar)
  */
 
-class NewInput : public Input {
+class CustInput : public Input {
 public:
-   NewInput (int x, int y, int w, int h, const char* l=0) :
+   CustInput (int x, int y, int w, int h, const char* l=0) :
       Input(x,y,w,h,l) {};
    int handle(int e);
 };
@@ -50,25 +50,31 @@
  * Disable: UpKey, DownKey, PageUpKey, PageDownKey and 
  * CTRL+{o,r,HomeKey,EndKey}
  */
-int NewInput::handle(int e)
+int CustInput::handle(int e)
 {
    int k = event_key();
 
-   _MSG("NewInput::handle event=%d\n", e);
+   _MSG("CustInput::handle event=%d\n", e);
+
+   // We're only interested in some flags
+   unsigned modifier = event_state() & (SHIFT | CTRL | ALT);
 
    // Don't focus with arrow keys
    if (e == FOCUS &&
-       (k == UpKey || k == DownKey || k == LeftKey|| k == RightKey)) {
+       (k == UpKey || k == DownKey || k == LeftKey || k == RightKey)) {
       return 0;
-   }
-
-   if (event_state(CTRL)) {
-      if (e == KEY && k == 'l') {
-         // Make text selected when already focused.
-         position(size(), 0);
-         return 0;
-      } else if (k == 'o' || k == 'r' || k == HomeKey || k == EndKey)
-         return 0;
+   } else if (e == KEY) {
+      if (modifier == CTRL) {
+         if (k == 'l') {
+            // Make text selected when already focused.
+            position(size(), 0);
+            return 1;
+         } else if (k == 'o') 
+            return 0;
+      } else if (modifier == SHIFT) {
+         if (k == LeftKey || k == RightKey)
+            return 0;
+      }
    }
    _MSG("\n");
 
@@ -80,14 +86,14 @@
 /*
  * Used to handle "paste" within the toolbar's Clear button.
  */
-class NewHighlightButton : public HighlightButton {
+class CustHighlightButton : public HighlightButton {
 public:
-   NewHighlightButton(int x, int y, int w, int h, const char *l=0) :
+   CustHighlightButton(int x, int y, int w, int h, const char *l=0) :
       HighlightButton(x,y,w,h,l) {};
    int handle(int e);
 };
 
-int NewHighlightButton::handle(int e)
+int CustHighlightButton::handle(int e)
 {
    if (e == PASTE) {
       const char* t = event_text();
@@ -105,9 +111,9 @@
 /*
  * Used to resize the progress boxes automatically.
  */
-class NewProgressBox : public InvisibleBox {
+class CustProgressBox : public InvisibleBox {
 public:
-   NewProgressBox(int x, int y, int w, int h, const char *l=0) :
+   CustProgressBox(int x, int y, int w, int h, const char *l=0) :
       InvisibleBox(x,y,w,h,l) {};
    void update_label(const char *lbl) {
       static int padding = 0;
@@ -138,14 +144,6 @@
 //
 
 /*
- * Callback handler for the close window event.
- */
-static void close_window_cb(Widget *wid, void *data)
-{
-   a_UIcmd_close_bw(data);
-}
-
-/*
  * Callback for the search button.
  */
 static void search_cb(Widget *wid, void *data)
@@ -197,7 +195,7 @@
 static void location_cb(Widget *wid, void *data)
 {
    Input *i = (Input*)wid;
-   UI *ui = (UI*)i->window();
+   UI *ui = (UI*)data;
 
    /* This test is necessary because WHEN_ENTER_KEY also includes
     * other events we're not interested in. For instance pressing
@@ -281,15 +279,15 @@
 /*
  * Callback for the bug meter button.
  */
-static void bugmeter_cb(Widget *w, void *data)
+static void bugmeter_cb(Widget *wid, void *data)
 {
    int k = event_key();
    if (k && k <= 7)
       MSG("[BugMeter], mouse button %d was pressed\n", k);
    if (k == 1) {
-      a_UIcmd_view_page_bugs(((UI*)data)->user_data());
+      a_UIcmd_view_page_bugs(wid->window()->user_data());
    } else if (k == 3) {
-      a_UIcmd_bugmeter_popup(((UI*)data)->user_data());
+      a_UIcmd_bugmeter_popup(wid->window()->user_data());
    }
 }
 
@@ -415,14 +413,14 @@
    Button *b;
    PackedGroup *pg = new PackedGroup(0,0,0,0);
    pg->begin();
-    Clear = b = new NewHighlightButton(2,2,16,22,0);
+    Clear = b = new CustHighlightButton(2,2,16,22,0);
     ImgClear = new xpmImage(new_s_xpm);
     b->image(ImgClear);
     b->tooltip("Clear the URL box.\nMiddle-click to paste a URL.");
-    b->callback(clear_cb, (void *)this);
+    b->callback(clear_cb, this);
     b->clear_tab_to_focus();
 
-    Input *i = Location = new NewInput(0,0,0,0,0);
+    Input *i = Location = new CustInput(0,0,0,0,0);
     i->tooltip("Location");
     i->color(CuteColor);
     i->when(WHEN_ENTER_KEY);
@@ -433,7 +431,7 @@
     ImgSearch = new xpmImage(search_xpm);
     b->image(ImgSearch);
     b->tooltip("Search the Web");
-    b->callback(search_cb, (void *)this);
+    b->callback(search_cb, this);
     b->clear_tab_to_focus();
 
    pg->type(PackedGroup::ALL_CHILDREN_VERTICAL);
@@ -451,12 +449,12 @@
    ProgBox = new PackedGroup(0,0,0,0);
    ProgBox->begin();
     // Images
-    IProg = new NewProgressBox(0,0,0,0);
+    IProg = new CustProgressBox(0,0,0,0);
     IProg->box(thin_up ? THIN_UP_BOX : EMBOSSED_BOX);
     IProg->labelcolor(GRAY10);
     IProg->update_label(wide ? "Images\n0 of 0" : "0 of 0");
     // Page
-    PProg = new NewProgressBox(0,0,0,0);
+    PProg = new CustProgressBox(0,0,0,0);
     PProg->box(thin_up ? THIN_UP_BOX : EMBOSSED_BOX);
     PProg->labelcolor(GRAY10);
     PProg->update_label(wide ? "Page\n0.0KB" : "0.0KB");
@@ -471,19 +469,15 @@
  */
 static void menubar_cb(Widget *wid, void *data)
 {
-   UI *ui = (UI*)wid->window();
-
    if (strcmp((char*)data, "nb") == 0) {
-      a_UIcmd_browser_window_new(wid->window()->w(), wid->window()->h(), ui);
+      a_UIcmd_browser_window_new(wid->window()->w(), wid->window()->h(),
+                                 wid->window()->user_data());
    } else if (strcmp((char*)data, "of") == 0) {
-      a_UIcmd_open_file(ui->user_data());
+      a_UIcmd_open_file(wid->window()->user_data());
    } else if (strcmp((char*)data, "ou") == 0) {
-      if (ui->get_panelmode() == UI_HIDDEN) {
-         ui->set_panelmode(UI_TEMPORARILY_SHOW_PANELS);
-      }
-      ui->focus_location();
+      a_UIcmd_focus_location(wid->window()->user_data());
    } else if (strcmp((char*)data, "cw") == 0) {
-      a_UIcmd_close_bw(ui->user_data());
+      a_UIcmd_close_bw(wid->window()->user_data());
    } else if (strcmp((char*)data, "ed") == 0) {
       a_UIcmd_close_all_bw();
    }
@@ -608,13 +602,14 @@
 /*
  * User Interface constructor
  */ 
-UI::UI(int win_w, int win_h, const char* label, const UI *cur_ui) :
-  Window(win_w, win_h, label)
+UI::UI(int x, int y, int ww, int wh, const char* label, const UI *cur_ui) :
+  Group(x, y, ww, wh, label)
 {
    int s_h = 20;
-   clear_double_buffer();
 
-   TopGroup = new PackedGroup(0, 0, win_w, win_h);
+   Tabs = NULL;
+   TabTooltip = NULL;
+   TopGroup = new PackedGroup(0, 0, ww, wh);
    add(TopGroup);
    resizable(TopGroup);
    
@@ -633,12 +628,8 @@
    }
 
 
-   // Set handler for the close window event
-   // (the argument is set later via user_data())
-   callback(close_window_cb);
-
    // Control panel
-   Panel = make_panel(win_w);
+   Panel = make_panel(ww);
    TopGroup->add(Panel);
 
 
@@ -655,17 +646,16 @@
    MainIdx = TopGroup->find(Main);
 
    // Find text bar
-   findbar = new Findbar(win_w, 30, this);
+   findbar = new Findbar(ww, 28);
    TopGroup->add(findbar); 
 
    // Status Panel
-   StatusPanel = new Group(0, 0, win_w, s_h, 0);
+   StatusPanel = new Group(0, 0, ww, s_h, 0);
    // Status box
    int il_w = 16;
    int bm_w = 16;
-   Status = new Output(0, 0, win_w-bm_w-il_w, s_h, 0);
+   Status = new Output(0, 0, ww-bm_w-il_w, s_h, 0);
    Status->value("");
-   //Status->box(UP_BOX);
    Status->box(THIN_DOWN_BOX);
    Status->clear_click_to_focus();
    Status->clear_tab_to_focus();
@@ -674,7 +664,7 @@
    //Status->throw_focus();
 
    // Image loading indicator
-   ImageLoad = new HighlightButton(win_w-il_w-bm_w,0,il_w,s_h,0);
+   ImageLoad = new HighlightButton(ww-il_w-bm_w,0,il_w,s_h,0);
    ImgImageLoadOn = new xpmImage(imgload_on_xpm);
    ImgImageLoadOff = new xpmImage(imgload_off_xpm);
    if (prefs.load_images) {
@@ -685,19 +675,19 @@
    ImageLoad->box(THIN_DOWN_BOX);
    ImageLoad->align(ALIGN_INSIDE|ALIGN_CLIP|ALIGN_LEFT);
    ImageLoad->tooltip("Toggle image loading");
-   ImageLoad->callback(imageload_cb, (void *)this);
+   ImageLoad->callback(imageload_cb, this);
    ImageLoad->clear_tab_to_focus();
    StatusPanel->add(ImageLoad);
 
    // Bug Meter
-   BugMeter = new HighlightButton(win_w-bm_w,0,bm_w,s_h,0);
+   BugMeter = new HighlightButton(ww-bm_w,0,bm_w,s_h,0);
    ImgMeterOK = new xpmImage(mini_ok_xpm);
    ImgMeterBug = new xpmImage(mini_bug_xpm);
    BugMeter->image(ImgMeterOK);
    BugMeter->box(THIN_DOWN_BOX);
    BugMeter->align(ALIGN_INSIDE|ALIGN_CLIP|ALIGN_LEFT);
    BugMeter->tooltip("Show HTML bugs\n(right-click for menu)");
-   BugMeter->callback(bugmeter_cb, (void *)this);
+   BugMeter->callback(bugmeter_cb, this);
    BugMeter->clear_tab_to_focus();
    StatusPanel->add(BugMeter);
 
@@ -712,7 +702,7 @@
    ImgFullScreenOff = new xpmImage(full_screen_off_xpm);
    //FullScreen->image(ImgFullScreenOn);
    //FullScreen->tooltip("Hide Controls");
-   //FullScreen->callback(fullscreen_cb, (void *)this);
+   //FullScreen->callback(fullscreen_cb, this);
 
    customize(0);
 
@@ -729,6 +719,8 @@
  */
 UI::~UI()
 {
+   _MSG("UI::~UI()\n");
+   dFree(TabTooltip);
    delete_panel_images();
    delete_status_panel_images();
    delete ImgFullScreenOn;
@@ -751,79 +743,75 @@
  */
 int UI::handle(int event)
 {
-   _MSG("UI::handle event=%d\n", event);
+   _MSG("UI::handle event=%d (%d,%d)\n", event, event_x(), event_y());
+   _MSG("Panel->h()=%d Main->h()=%d\n", Panel->h() , Main->h());
+
    int ret = 0, k = event_key();
 
-   // We're only interested in some flags 
-   // (not whether numlock is on for example)
-   unsigned modifier = event_state() & (SHIFT | CTRL | ALT | META);
+   // We're only interested in some flags
+   unsigned modifier = event_state() & (SHIFT | CTRL | ALT);
 
-   // Let FLTK pass these events to child widgets.
    if (event == KEY) {
-      if (k == UpKey || k == DownKey || k == SpaceKey ||
-          k == LeftKey || k == RightKey)
-         return 0;
-      // Ignore Escape for main window.
-      if (k == EscapeKey)
-         ret = 1;
+      return 0; // Receive as shortcut
 
    } else if (event == SHORTCUT) {
-      // Handle these shortcuts here.
+      // Handle keyboard shortcuts here.
       if (modifier == CTRL) {
          if (k == 'b') {
-            a_UIcmd_book(user_data());
+            a_UIcmd_book(this->window()->user_data());
             ret = 1;
          } else if (k == 'f') {
             set_findbar_visibility(1);
             ret = 1;
          } else if (k == 'l') {
-            if (Panelmode == UI_HIDDEN) {
-               set_panelmode(UI_TEMPORARILY_SHOW_PANELS);
-            }
             focus_location();
             ret = 1;
          } else if (k == 'n') {
-            a_UIcmd_browser_window_new(w(), h(), this);
+            a_UIcmd_browser_window_new(w(), h(), this->window()->user_data());
             ret = 1;
          } else if (k == 'o') {
-            a_UIcmd_open_file(user_data());
+            a_UIcmd_open_file(this->window()->user_data());
             ret = 1;
          } else if (k == 'q') {
-            a_UIcmd_close_bw(user_data());
+            a_UIcmd_close_bw(this->window()->user_data());
             ret = 1;
          } else if (k == 'r') {
-            a_UIcmd_reload(user_data());
+            a_UIcmd_reload(this->window()->user_data());
             ret = 1;
          } else if (k == 's') {
-            a_UIcmd_search_dialog(user_data());
+            a_UIcmd_search_dialog(this->window()->user_data());
             ret = 1;
          } else if (k == ' ') {
             panelmode_cb_i();
             ret = 1;
          }
-      }
-
-      if (event_key_state(LeftAltKey) && modifier == ALT && k == 'q') {
-         a_UIcmd_close_all_bw();
-         ret = 1;
+      } else {
+         // Back and Forward navigation shortcuts
+         if (modifier == 0 && (k == BackSpaceKey ||  k == ',')) {
+            a_UIcmd_back(this->window()->user_data());
+            ret = 1;
+         } else if ((modifier == 0 && k == '.') ||
+                    (modifier == SHIFT && k == BackSpaceKey)) {
+            a_UIcmd_forw(this->window()->user_data());
+            ret = 1;
+         }
       }
 
-      // Back and Forward navigation shortcuts
-      if ((modifier == 0 && k == BackSpaceKey) ||
-          (modifier == 0 && k == ',')) {
-         a_UIcmd_back(user_data());
-         ret = 1;
-      } else if ((modifier == SHIFT && k == BackSpaceKey) ||
-                 (modifier == 0 &&k == '.')) {
-         a_UIcmd_forw(user_data());
-         ret = 1;
-      }
+   } else if (event == FOCUS_CHANGE) {
+      // The "bw" for this tab is stored in the parent window.
+      // Update "bw" each time we switch tabs.
+      window()->user_data(vbw());
+      ret = 0;
    }
 
-   if (ret == 0) {
-      ret = Window::handle(event);
-   }
+   if (!ret)
+      ret = Group::handle(event);
    return ret;
+
+   // if (event_key_state(LeftAltKey) && modifier == ALT && k == 'q') {
+   //    a_UIcmd_close_all_bw();
+   //    ret = 1;
+   // }
 }
 
 
@@ -850,15 +838,27 @@
 
 /*
  * Focus location entry.
+ * If it's not visible, show it until the callback is done.
  */
 void UI::focus_location()
 {
+   if (get_panelmode() == UI_HIDDEN) {
+      set_panelmode(UI_TEMPORARILY_SHOW_PANELS);
+   }
    Location->take_focus();
    // Make text selected when already focused.
    Location->position(Location->size(), 0);
 }
 
 /*
+ * Focus Main area.
+ */
+void UI::focus_main()
+{
+   Main->take_focus();
+}
+
+/*
  * Set a new message in the status bar.
  */
 void UI::set_status(const char *str)
@@ -1074,9 +1074,25 @@
 {
    char title[128];
 
+   dReturn_if_fail(label != NULL);
+
    snprintf(title, 128, "Dillo: %s", label);
-   this->copy_label(title);
-   this->redraw_label();
+   this->window()->copy_label(title);
+   this->window()->redraw_label();
+
+   if (tabs() && *label) {
+      const size_t tab_chars = 18;
+      snprintf(title, tab_chars + 1, "%s", label);
+      if (strlen(label) > tab_chars)
+         snprintf(title + tab_chars, 4, "...");
+      this->copy_label(title);
+      this->redraw_label();
+
+      // Disabled because of a bug in fltk::Tabgroup
+      //dFree(TabTooltip);
+      //TabTooltip = dStrdup(label);
+      //this->tooltip(TabTooltip);
+   }
 }
 
 /*
--- a/src/ui.hh	Wed Sep 17 23:40:06 2008 +0200
+++ b/src/ui.hh	Thu Sep 18 00:16:38 2008 +0200
@@ -12,6 +12,7 @@
 #include <fltk/Image.h>
 #include <fltk/MultiImage.h>
 #include <fltk/MenuBuild.h>
+#include <fltk/TabGroup.h>
 
 #include "findbar.hh"
 
@@ -36,18 +37,22 @@
 } UIPanelmode;
 
 // Private class 
-class NewProgressBox;
+class CustProgressBox;
 
 //
 // UI class definition -------------------------------------------------------
 //
-class UI : public fltk::Window {
+class UI : public fltk::Group {
+   void *Bw;
+   TabGroup *Tabs;
+   char *TabTooltip;
+
    Group *TopGroup;
    Button *Back, *Forw, *Home, *Reload, *Save, *Stop, *Bookmarks,
           *Clear, *Search, *FullScreen, *ImageLoad, *BugMeter;
    Input  *Location;
    PackedGroup *ProgBox;
-   NewProgressBox *PProg, *IProg;
+   CustProgressBox *PProg, *IProg;
    Image *ImgLeftIns, *ImgLeftSens, *ImgRightIns, *ImgRightSens,
          *ImgStopIns, *ImgStopSens, *ImgFullScreenOn, *ImgFullScreenOff,
          *ImgImageLoadOn, *ImgImageLoadOff, *ImgMeterOK, *ImgMeterBug,
@@ -75,7 +80,7 @@
    void delete_status_panel_images();
 public:
 
-   UI(int w, int h, const char* label = 0, const UI *cur_ui = NULL);
+   UI(int x,int y,int w,int h, const char* label = 0, const UI *cur_ui=NULL);
    ~UI();
 
    // To manage what events to catch and which to let pass
@@ -84,6 +89,7 @@
    const char *get_location();
    void set_location(const char *str);
    void focus_location();
+   void focus_main();
    void set_status(const char *str);
    void set_page_prog(size_t nbytes, int cmd);
    void set_img_prog(int n_img, int t_img, int cmd);
@@ -96,10 +102,14 @@
    void set_panelmode(UIPanelmode mode);
    UIPanelmode get_panelmode();
    void set_findbar_visibility(bool visible);
-
    Widget *fullscreen_button() { return FullScreen; }
    void fullscreen_toggle() { FullScreen->do_callback(); }
 
+   TabGroup *tabs() { return Tabs; }
+   void tabs(TabGroup *tabs) { Tabs = tabs; }
+   void *vbw() { return Bw; }
+   void vbw(void *v_bw) { Bw = v_bw; }
+
    // Hooks to method callbacks
    void panel_cb_i();
    void color_change_cb_i();
--- a/src/uicmd.cc	Wed Sep 17 23:40:06 2008 +0200
+++ b/src/uicmd.cc	Thu Sep 18 00:16:38 2008 +0200
@@ -16,6 +16,7 @@
 #include <stdarg.h>
 #include <math.h>       /* for rint */
 #include <fltk/Widget.h>
+#include <fltk/TabGroup.h>
 
 #include "dir.h"
 #include "ui.hh"
@@ -32,53 +33,100 @@
 
 #include "nav.h"
 
+// Handy macro
+#define BW2UI(bw) ((UI*)(bw->ui))
+
 // Platform idependent part
 using namespace dw::core;
 // FLTK related
 using namespace dw::fltk;
 
-typedef struct {
-   UI *ui;
-   BrowserWindow *bw;
-} Uibw;
 
 /*
  * Local data
  */
-// A matching table for all open ui/bw pairs
-// BUG: must be dynamic.
-static Uibw uibws[32];
-static int uibws_num = 0, uibws_max = 32;
-
 static char *save_dir = NULL;
 
 using namespace fltk;
 
+//
+// For custom handling of keyboard
+//
+class CustTabGroup : public fltk::TabGroup {
+public:
+   CustTabGroup (int x, int y, int ww, int wh, const char *lbl=0) :
+      TabGroup(x,y,ww,wh,lbl) {};
+   int handle(int e) {
+      // Don't focus with arrow keys
+      _MSG("CustTabGroup::handle %d\n", e);
+      int k = event_key();
+      // We're only interested in some flags
+      unsigned modifier = event_state() & (SHIFT | CTRL | ALT);
+      if (e == KEY) {
+         if (k == UpKey || k == DownKey || k == TabKey) {
+            return 0;
+         } else if (k == LeftKey || k == RightKey) {
+            if (modifier == SHIFT) {
+               int i = value();
+               if (k == LeftKey) {i = i ? i-1 : children()-1;}
+               else {i++; if (i >= children()) i = 0;}
+               if (value(i)) do_callback();
+               return 1;
+            }
+            return 0;
+         }
+      }
+      return TabGroup::handle(e);
+   }
+};
+
 
 /*
  * Create a new UI and its associated BrowserWindow data structure.
  * Use style from v_ui. If non-NULL it must be of type UI*.
  */
-BrowserWindow *a_UIcmd_browser_window_new(int ww, int wh, const void *v_ui)
+BrowserWindow *a_UIcmd_browser_window_new(int ww, int wh, const void *vbw)
 {
+   static TabGroup *DilloTabs = NULL;
+   BrowserWindow *old_bw = (BrowserWindow*)vbw;
+   BrowserWindow *new_bw = NULL;
+
    if (ww <= 0 || wh <= 0) {
       // Set default geometry from dillorc.
       ww = prefs.width;
       wh = prefs.height;
    }
 
+   if (!DilloTabs) {
+       {Window *o = new Window(ww, wh);
+        o->shortcut(0); // Ignore Escape
+        o->clear_double_buffer();
+        DilloTabs = new CustTabGroup(0, 0, ww, wh);
+        DilloTabs->selection_color(156);
+        //DilloTabs->clear_tab_to_focus();
+        o->add(DilloTabs);
+       }
+       wh -= 20;
+   }
+
    // Create and set the UI
-   UI *new_ui = new UI(ww, wh, "Dillo: UI", (UI*) v_ui);
+   UI *new_ui = new UI(0, 20, ww, wh, "Label", old_bw ? BW2UI(old_bw) : NULL);
    new_ui->set_status("http://www.dillo.org/");
+   new_ui->tabs(DilloTabs);
    //new_ui->set_location("http://dillo.org/");
    //new_ui->customize(12);
 
-   if (v_ui == NULL && prefs.xpos >= 0 && prefs.ypos >= 0) {
+   DilloTabs->add(new_ui);
+   DilloTabs->resizable(new_ui);
+   DilloTabs->window()->resizable(new_ui);
+   DilloTabs->window()->show();
+
+   if (old_bw == NULL && prefs.xpos >= 0 && prefs.ypos >= 0) {
       // position the first window according to preferences
       fltk::Rectangle r;
-      new_ui->borders(&r);
+      new_ui->window()->borders(&r);
       // borders() gives x and y border sizes as negative values
-      new_ui->position(prefs.xpos - r.x(), prefs.ypos - r.y());
+      new_ui->window()->position(prefs.xpos - r.x(), prefs.ypos - r.y());
    }
 
    // Now create the Dw render layout and viewport
@@ -93,24 +141,16 @@
    viewport->setScrollStep((int) rint(14.0 * prefs.font_factor));
 
    // Now, create a new browser window structure
-   BrowserWindow *new_bw = a_Bw_new();
+   new_bw = a_Bw_new();
 
-   // Set new_bw as callback data for UI
-   new_ui->user_data(new_bw);
+   // Store new_bw for callback data inside UI
+   new_ui->vbw(new_bw);
+
    // Reference the UI from the bw
    new_bw->ui = (void *)new_ui;
    // Copy the layout pointer into the bw data
    new_bw->render_layout = (void*)layout;
 
-   // insert the new ui/bw pair in the table
-   if (uibws_num < uibws_max) {
-      uibws[uibws_num].ui = new_ui;
-      uibws[uibws_num].bw = new_bw;
-      uibws_num++;
-   }
-
-   new_ui->show();
-
    return new_bw;
 }
 
@@ -120,12 +160,19 @@
 void a_UIcmd_close_bw(void *vbw)
 {
    BrowserWindow *bw = (BrowserWindow *)vbw;
-   UI *ui = (UI*)bw->ui;
+   UI *ui = BW2UI(bw);
    Layout *layout = (Layout*)bw->render_layout;
 
    MSG("a_UIcmd_close_bw\n");
    a_Bw_stop_clients(bw, BW_Root + BW_Img + Bw_Force);
    delete(layout);
+   if (ui->tabs()) {
+      ui->tabs()->remove(ui);
+      if (ui->tabs()->value() != -1)
+         ui->tabs()->selected_child()->take_focus();
+      else
+         ui->tabs()->window()->hide();
+   }
    delete(ui);
    a_Bw_free(bw);
 }
@@ -562,8 +609,6 @@
 
 // UI binding functions -------------------------------------------------------
 
-#define BW2UI(bw) ((UI*)(bw->ui))
-
 /*
  * Return browser window width and height
  */
@@ -757,3 +802,20 @@
    a_UIcmd_set_msg(bw, "");
 }
 
+/*
+ * Focus the rendered area.
+ */
+void a_UIcmd_focus_main_area(BrowserWindow *bw)
+{
+   BW2UI(bw)->focus_main();
+}
+
+/*
+ * Focus the location bar.
+ */
+void a_UIcmd_focus_location(void *vbw)
+{
+   BrowserWindow *bw = (BrowserWindow*)vbw;
+   BW2UI(bw)->focus_location();
+}
+
--- a/src/uicmd.hh	Wed Sep 17 23:40:06 2008 +0200
+++ b/src/uicmd.hh	Thu Sep 18 00:16:38 2008 +0200
@@ -8,7 +8,7 @@
 #endif /* __cplusplus */
 
 
-BrowserWindow *a_UIcmd_browser_window_new(int ww, int wh, const void *v_ui);
+BrowserWindow *a_UIcmd_browser_window_new(int ww, int wh, const void *v_bw);
 void a_UIcmd_open_urlstr(void *vbw, const char *urlstr);
 void a_UIcmd_open_url(BrowserWindow *bw, const DilloUrl *url);
 void a_UIcmd_open_url_nw(BrowserWindow *bw, const DilloUrl *url);
@@ -31,6 +31,8 @@
 void a_UIcmd_findtext_dialog(BrowserWindow *bw);
 void a_UIcmd_findtext_search(BrowserWindow *bw, const char *key, int case_sens);
 void a_UIcmd_findtext_reset(BrowserWindow *bw);
+void a_UIcmd_focus_main_area(BrowserWindow *bw);
+void a_UIcmd_focus_location(void *vbw);
 void a_UIcmd_page_popup(void *vbw, const DilloUrl *url,
                         const char *bugs_txt, bool_t unloaded_imgs);
 void a_UIcmd_link_popup(void *vbw, const DilloUrl *url);