enough with umpteen "i18n.h" files. Consolidate on pbd/i18n.h
[ardour.git] / libs / gtkmm2ext / tabbable.cc
index e4c86e838469096110c7e499a71097db616a8182..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 "pbd/stacktrace.h"
+
+#include "pbd/i18n.h"
+
 using namespace Gtkmm2ext;
 using namespace Gtk;
 using std::string;
@@ -31,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)
 {
 }
 
@@ -43,30 +52,40 @@ Tabbable::~Tabbable ()
 }
 
 void
-Tabbable::add_to_notebook (Notebook& notebook, const string& tab_title, int position)
+Tabbable::add_to_notebook (Notebook& notebook, const string& tab_title)
 {
-       notebook.insert_page (_contents, tab_title, position, false);
-       notebook.set_tab_detachable (_contents);
-
        _parent_notebook = &notebook;
-       _tab_title = tab_title;
-       _notebook_position = position;
+
+       if (tab_requested_by_state) {
+               attach ();
+       }
 }
 
 Window*
-Tabbable::use_own_window ()
+Tabbable::use_own_window (bool and_pack_it)
 {
-       return get (true);
+       Gtk::Window* win = get (true);
+
+       if (and_pack_it) {
+               Gtk::Container* parent = _contents.get_parent();
+               if (parent) {
+                       parent->remove (_contents);
+               }
+               _own_notebook.append_page (_contents);
+       }
+
+       return win;
+
 }
 
 bool
-Tabbable::window_visible ()
+Tabbable::window_visible () const
 {
-       if (!own_window()) {
+       if (!_window) {
                return false;
        }
 
-       return visible();
+       return _window->is_visible();
 }
 
 Window*
@@ -80,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;
        }
@@ -91,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 ();
-       
+       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();
        }
+}
+
+void
+Tabbable::make_visible ()
+{
+       if (_window && (current_toplevel() == _window)) {
+               set_pos ();
+               _window->present ();
+       } else {
 
-       if (toplevel != _window) {
-               /* not in its own window, just switch parent notebook to show
-                  this Tabbable.
-               */
-               if (_parent_notebook) {
-                       _parent_notebook->set_current_page (_notebook_position);
+               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 ();
+       }
+}
 
-       if (_window == toplevel) {
+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
                 */
@@ -164,44 +249,62 @@ Tabbable::delete_event_handler (GdkEventAny *ev)
                /* leave the window around */
 
                _window->hide ();
-               
-               if (_parent_notebook) {
+       }
 
-                       _parent_notebook->insert_page (_contents, _tab_title, _notebook_position);
-                       _parent_notebook->set_tab_detachable (_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) {
+       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 ();
        }
 }
 
@@ -210,3 +313,72 @@ Tabbable::current_toplevel () const
 {
        return dynamic_cast<Gtk::Window*> (contents().get_toplevel());
 }
+
+string
+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;
+}
+
+int
+Tabbable::set_state (const XMLNode& node, int version)
+{
+       int ret;
+
+       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);
+}