enough with umpteen "i18n.h" files. Consolidate on pbd/i18n.h
[ardour.git] / libs / gtkmm2ext / tabbable.cc
index ebec13b9e932f6bf239db88a6e9f41164d0b4650..5e4752454e6cfd6c8547b56799f59399b2307165 100644 (file)
 
 #include "gtkmm2ext/tabbable.h"
 #include "gtkmm2ext/gtk_ui.h"
+#include "gtkmm2ext/utils.h"
 #include "gtkmm2ext/visibility_tracker.h"
 
-#include "i18n.h"
+#include "pbd/stacktrace.h"
+
+#include "pbd/i18n.h"
 
 using namespace Gtkmm2ext;
 using namespace Gtk;
@@ -35,13 +38,9 @@ using std::string;
 Tabbable::Tabbable (Widget& w, const string& name)
        : WindowProxy (name)
        , _contents (w)
-       , tab_close_image (Stock::CLOSE, ICON_SIZE_BUTTON)
+       , _parent_notebook (0)
+       , tab_requested_by_state (true)
 {
-       _tab_box.pack_start (_tab_label, true, true);
-       _tab_box.pack_start (_tab_close_button, false, false);
-       _tab_close_button.add (tab_close_image);
-
-       _tab_close_button.signal_clicked().connect (sigc::mem_fun (*this, &Tabbable::tab_close_clicked));
 }
 
 Tabbable::~Tabbable ()
@@ -52,36 +51,14 @@ Tabbable::~Tabbable ()
        }
 }
 
-void
-Tabbable::tab_close_clicked ()
-{
-       /* for this to happen, the tab must be visible so we
-          can assume that the contents are displayed in the
-          parent notebook
-       */
-       
-       if (_parent_notebook) {
-               _parent_notebook->remove_page (_contents);
-       }
-}
-
 void
 Tabbable::add_to_notebook (Notebook& notebook, const string& tab_title)
 {
-       _tab_label.set_text (tab_title);
-       _tab_box.show_all ();
-       
-       notebook.append_page (_contents, _tab_box);
-
-       Gtkmm2ext::UI::instance()->set_tip (_tab_label,
-                                           string_compose (_("Drag this tab to the desktop to show %1 in its own window\n\n"
-                                                             "To put the window back, click on its \"close\" button"), tab_title));
-       
-       notebook.set_tab_detachable (_contents);
-       notebook.set_tab_reorderable (_contents);
-
        _parent_notebook = &notebook;
-       _tab_title = tab_title;
+
+       if (tab_requested_by_state) {
+               attach ();
+       }
 }
 
 Window*
@@ -94,7 +71,7 @@ Tabbable::use_own_window (bool and_pack_it)
                if (parent) {
                        parent->remove (_contents);
                }
-               _own_notebook.append_page (_contents, _tab_box);
+               _own_notebook.append_page (_contents);
        }
 
        return win;
@@ -102,13 +79,13 @@ Tabbable::use_own_window (bool and_pack_it)
 }
 
 bool
-Tabbable::window_visible ()
+Tabbable::window_visible () const
 {
-       if (!own_window()) {
+       if (!_window) {
                return false;
        }
 
-       return visible();
+       return _window->is_visible();
 }
 
 Window*
@@ -122,9 +99,9 @@ Tabbable::get (bool create)
                return 0;
        }
 
-       /* From here on, we're creating the window 
+       /* From here on, we're creating the window
         */
-       
+
        if ((_window = new Window (WINDOW_TOPLEVEL)) == 0) {
                return 0;
        }
@@ -133,35 +110,50 @@ Tabbable::get (bool create)
        _own_notebook.show ();
        _own_notebook.set_show_tabs (false);
 
+       _window->signal_map().connect (sigc::mem_fun (*this, &Tabbable::window_mapped));
+       _window->signal_unmap().connect (sigc::mem_fun (*this, &Tabbable::window_unmapped));
+
        /* do other window-related setup */
 
        setup ();
 
        /* window should be ready for derived classes to do something with it */
-       
+
        return _window;
 }
 
-Gtk::Notebook*
-Tabbable::tab_root_drop ()
+void
+Tabbable::show_own_window (bool and_pack_it)
 {
+       Gtk::Widget* parent = _contents.get_parent();
        Gtk::Allocation alloc;
 
-       alloc = _contents.get_parent()->get_allocation();
-       
-       (void) use_own_window (false);
-       
+       if (parent) {
+               alloc = parent->get_allocation();
+       }
+
+       (void) use_own_window (and_pack_it);
+
+       if (parent) {
+               _window->set_default_size (alloc.get_width(), alloc.get_height());
+       }
+
+       tab_requested_by_state = false;
+
+       _window->present ();
+}
+
+Gtk::Notebook*
+Tabbable::tab_root_drop ()
+{
        /* This is called after a drop of a tab onto the root window. Its
-        * responsibility is to return the notebook that this Tabbable's
+        * responsibility xois to return the notebook that this Tabbable's
         * contents should be packed into before the drop handling is
         * completed. It is not responsible for actually taking care of this
         * packing.
         */
 
-       _window->set_default_size (alloc.get_width(), alloc.get_height());
-       _window->show_all ();
-       _window->present ();
-
+       show_own_window (false);
        return &_own_notebook;
 }
 
@@ -177,13 +169,75 @@ Tabbable::show_window ()
        }
 }
 
-bool
-Tabbable::delete_event_handler (GdkEventAny *ev)
+/** If this Tabbable is currently parented by a tab, ensure that the tab is the
+ * current one. If it is parented by a window, then toggle the visibility of
+ * that window.
+ */
+void
+Tabbable::change_visibility ()
+{
+       if (tabbed()) {
+               _parent_notebook->set_current_page (_parent_notebook->page_num (_contents));
+               return;
+       }
+
+       if (tab_requested_by_state) {
+               /* should be tabbed, but currently isn't parented by a notebook */
+               return;
+       }
+
+       if (_window && (current_toplevel() == _window)) {
+               /* Use WindowProxy method which will rotate then hide */
+               toggle();
+       }
+}
+
+void
+Tabbable::make_visible ()
 {
-       Window* toplevel = dynamic_cast<Window*> (_contents.get_toplevel());
+       if (_window && (current_toplevel() == _window)) {
+               set_pos ();
+               _window->present ();
+       } else {
 
-       if (_window == toplevel) {
+               if (!tab_requested_by_state) {
+                       show_own_window (true);
+               } else {
+                       show_tab ();
+               }
+       }
+}
 
+void
+Tabbable::make_invisible ()
+{
+       if (_window && (current_toplevel() == _window)) {
+               _window->hide ();
+       } else {
+               hide_tab ();
+       }
+}
+
+void
+Tabbable::detach ()
+{
+       show_own_window (true);
+}
+
+void
+Tabbable::attach ()
+{
+       if (!_parent_notebook) {
+               return;
+       }
+
+       if (tabbed()) {
+               /* already tabbed */
+               return;
+       }
+
+
+       if (_window && current_toplevel() == _window) {
                /* unpack Tabbable from parent, put it back in the main tabbed
                 * notebook
                 */
@@ -195,48 +249,62 @@ Tabbable::delete_event_handler (GdkEventAny *ev)
                /* leave the window around */
 
                _window->hide ();
-               
-               if (_parent_notebook) {
+       }
 
-                       _parent_notebook->append_page (_contents, _tab_box);
-                       _parent_notebook->set_tab_detachable (_contents);
-                       _parent_notebook->set_tab_reorderable (_contents);
-                       _parent_notebook->set_current_page (_parent_notebook->page_num (_contents));
-               }
+       _parent_notebook->append_page (_contents);
+       _parent_notebook->set_tab_detachable (_contents);
+       _parent_notebook->set_tab_reorderable (_contents);
+       _parent_notebook->set_current_page (_parent_notebook->page_num (_contents));
 
-               /* don't let anything else handle this */
-               
-               return true;
-       } 
+       /* have to force this on, which is semantically correct, since
+        * the user has effectively asked for it.
+        */
 
-       /* nothing to do */
-       return false;
+       tab_requested_by_state = true;
+       StateChange (*this);
 }
 
 bool
-Tabbable::is_tabbed () const
+Tabbable::delete_event_handler (GdkEventAny *ev)
 {
-       Window* toplevel = (Window*) _contents.get_toplevel();
+       _window->hide();
 
-       if (_window && (toplevel == _window)) {
+       return true;
+}
+
+bool
+Tabbable::tabbed () const
+{
+       if (_window && (current_toplevel() == _window)) {
                return false;
        }
 
-       if (_parent_notebook && _contents.get_parent()) {
+       if (_parent_notebook && (_parent_notebook->page_num (_contents) >= 0)) {
                return true;
        }
-       
+
        return false;
 }
 
+void
+Tabbable::hide_tab ()
+{
+       if (tabbed()) {
+               _parent_notebook->remove_page (_contents);
+               StateChange (*this);
+       }
+}
+
 void
 Tabbable::show_tab ()
 {
        if (!window_visible() && _parent_notebook) {
                if (_contents.get_parent() == 0) {
+                       tab_requested_by_state = true;
                        add_to_notebook (*_parent_notebook, _tab_title);
                }
                _parent_notebook->set_current_page (_parent_notebook->page_num (_contents));
+               current_toplevel()->present ();
        }
 }
 
@@ -252,11 +320,19 @@ Tabbable::xml_node_name()
        return WindowProxy::xml_node_name();
 }
 
+bool
+Tabbable::tabbed_by_default() const
+{
+       return tab_requested_by_state;
+}
+
 XMLNode&
 Tabbable::get_state()
 {
        XMLNode& node (WindowProxy::get_state());
 
+       node.add_property (X_("tabbed"),  tabbed() ? X_("yes") : X_("no"));
+
        return node;
 }
 
@@ -265,11 +341,30 @@ Tabbable::set_state (const XMLNode& node, int version)
 {
        int ret;
 
-       if ((ret = WindowProxy::set_state (node, version)) == 0) {
-               if (_visible) {
-                       if (use_own_window (true) == 0) {
-                               ret = -1;
-                       }
+       if ((ret = WindowProxy::set_state (node, version)) != 0) {
+               return ret;
+       }
+
+       if (_visible) {
+               show_own_window (true);
+       }
+
+       XMLNodeList children = node.children ();
+       XMLNode* window_node = node.child ("Window");
+
+       if (window_node) {
+               XMLProperty const * prop = window_node->property (X_("tabbed"));
+               if (prop) {
+                       tab_requested_by_state = PBD::string_is_affirmative (prop->value());
+               }
+       }
+
+       if (!_visible) {
+               if (tab_requested_by_state) {
+                       attach ();
+               } else {
+                       /* this does nothing if not tabbed */
+                       hide_tab ();
                }
        }
 
@@ -277,11 +372,13 @@ Tabbable::set_state (const XMLNode& node, int version)
 }
 
 void
-Tabbable::make_visible ()
+Tabbable::window_mapped ()
 {
-       if (_window && (current_toplevel() == _window)) {
-               _window->present ();
-       } else {
-               show_tab ();
-       }
+       StateChange (*this);
+}
+
+void
+Tabbable::window_unmapped ()
+{
+       StateChange (*this);
 }