enough with umpteen "i18n.h" files. Consolidate on pbd/i18n.h
[ardour.git] / libs / gtkmm2ext / tabbable.cc
index 4249da7fff2f4ace4cf349572d60fc7b808f32aa..5e4752454e6cfd6c8547b56799f59399b2307165 100644 (file)
 #include <gtkmm/action.h>
 #include <gtkmm/notebook.h>
 #include <gtkmm/window.h>
+#include <gtkmm/stock.h>
 
 #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;
@@ -34,6 +38,8 @@ using std::string;
 Tabbable::Tabbable (Widget& w, const string& name)
        : WindowProxy (name)
        , _contents (w)
+       , _parent_notebook (0)
+       , tab_requested_by_state (true)
 {
 }
 
@@ -48,20 +54,11 @@ Tabbable::~Tabbable ()
 void
 Tabbable::add_to_notebook (Notebook& notebook, const string& tab_title)
 {
-       notebook.append_page (_contents, tab_title, false);
-       Gtk::Widget* tab_label = notebook.get_tab_label (_contents);
+       _parent_notebook = &notebook;
 
-       if (tab_label) {
-               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));
+       if (tab_requested_by_state) {
+               attach ();
        }
-       
-       notebook.set_tab_detachable (_contents);
-       notebook.set_tab_reorderable (_contents);
-
-       _parent_notebook = &notebook;
-       _tab_title = tab_title;
 }
 
 Window*
@@ -74,7 +71,7 @@ Tabbable::use_own_window (bool and_pack_it)
                if (parent) {
                        parent->remove (_contents);
                }
-               _own_notebook.append_page (_contents, _tab_title);
+               _own_notebook.append_page (_contents);
        }
 
        return win;
@@ -82,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*
@@ -102,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;
        }
@@ -113,68 +110,134 @@ 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;
 }
 
 void
 Tabbable::show_window ()
 {
-       Window* toplevel = dynamic_cast<Window*> (_contents.get_toplevel());
+       make_visible ();
 
-       if (toplevel == _window) {
-               _window->present ();
+       if (_window && (current_toplevel() == _window)) {
+               if (!_visible) { /* was hidden, update status */
+                       set_pos_and_size ();
+               }
+       }
+}
+
+/** 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 (!_visible) { /* was hidden, update status */
-               set_pos_and_size ();
+       if (_window && (current_toplevel() == _window)) {
+               /* Use WindowProxy method which will rotate then hide */
+               toggle();
        }
+}
 
-       if (toplevel != _window) {
-               /* not in its own window, just switch parent notebook to show
-                  this Tabbable.
-               */
-               if (_parent_notebook) {
-                       _parent_notebook->set_current_page (_parent_notebook->page_num (_contents));
+void
+Tabbable::make_visible ()
+{
+       if (_window && (current_toplevel() == _window)) {
+               set_pos ();
+               _window->present ();
+       } else {
+
+               if (!tab_requested_by_state) {
+                       show_own_window (true);
+               } else {
+                       show_tab ();
                }
        }
 }
 
-bool
-Tabbable::delete_event_handler (GdkEventAny *ev)
+void
+Tabbable::make_invisible ()
 {
-       Window* toplevel = dynamic_cast<Window*> (_contents.get_toplevel());
+       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 == toplevel) {
 
+       if (_window && current_toplevel() == _window) {
                /* unpack Tabbable from parent, put it back in the main tabbed
                 * notebook
                 */
@@ -186,45 +249,62 @@ Tabbable::delete_event_handler (GdkEventAny *ev)
                /* leave the window around */
 
                _window->hide ();
-               
-               if (_parent_notebook) {
+       }
 
-                       _parent_notebook->append_page (_contents, _tab_title);
-                       _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();
+
+       return true;
+}
 
-       if (_window && (toplevel == _window)) {
+bool
+Tabbable::tabbed () const
+{
+       if (_window && (current_toplevel() == _window)) {
                return false;
        }
 
-       if (_parent_notebook) {
+       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 ();
        }
 }
 
@@ -240,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;
 }
 
@@ -253,13 +341,44 @@ 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 ();
                }
        }
 
        return ret;
 }
+
+void
+Tabbable::window_mapped ()
+{
+       StateChange (*this);
+}
+
+void
+Tabbable::window_unmapped ()
+{
+       StateChange (*this);
+}