scale CairoIcon by Gtkmm2ext::UI::ui_scale
[ardour.git] / libs / gtkmm2ext / tabbable.cc
index b2430c832d5128ed2080dee2ddfd0addfe87af22..451405db63df1987095ec1d16e41624e9a756ac2 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"
@@ -34,7 +36,17 @@ using std::string;
 Tabbable::Tabbable (Widget& w, const string& name)
        : WindowProxy (name)
        , _contents (w)
+       , tab_close_image (ArdourIcon::CloseCross, 0xffffffff)
+       , tab_requested_by_state (true)
 {
+       tab_close_image.set_size_request (15,15);
+       
+       _tab_box.set_spacing (2);
+       _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 ()
@@ -46,22 +58,22 @@ Tabbable::~Tabbable ()
 }
 
 void
-Tabbable::add_to_notebook (Notebook& notebook, const string& tab_title)
+Tabbable::tab_close_clicked ()
 {
-       notebook.append_page (_contents, tab_title, false);
-       Gtk::Widget* tab_label = notebook.get_tab_label (_contents);
-
-       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));
-       }
-       
-       notebook.set_tab_detachable (_contents);
-       notebook.set_tab_reorderable (_contents);
+       hide_tab ();
+}
 
+void
+Tabbable::add_to_notebook (Notebook& notebook, const string& tab_title)
+{
        _parent_notebook = &notebook;
        _tab_title = tab_title;
+       _tab_label.set_text (tab_title);
+       _tab_box.show_all ();
+
+       if (tab_requested_by_state) {
+               attach ();
+       }
 }
 
 Window*
@@ -74,7 +86,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, _tab_box);
        }
 
        return win;
@@ -88,7 +100,7 @@ Tabbable::window_visible ()
                return false;
        }
 
-       return visible();
+       return _window->is_visible();
 }
 
 Window*
@@ -113,6 +125,9 @@ 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 ();
@@ -122,86 +137,134 @@ Tabbable::get (bool create)
        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();
+       if (parent) {
+               alloc = parent->get_allocation();
+       }
        
-       (void) use_own_window (false);
+       (void) use_own_window (and_pack_it);
        
+       if (parent) {
+               _window->set_default_size (alloc.get_width(), alloc.get_height());
+       }
+
+       _window->show_all ();
+       _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 (!_visible) { /* was hidden, update status */
-               set_pos_and_size ();
-       }
+void
+Tabbable::make_visible ()
+{
+       if (_window && (current_toplevel() == _window)) {
+               _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 (_parent_notebook->page_num (_contents));
+               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
                 */
-
+               
                save_pos_and_size ();
-
+               
                _contents.get_parent()->remove (_contents);
-
+       
                /* leave the window around */
-
-               _window->hide ();
                
-               if (_parent_notebook) {
+               _window->hide ();
+       }
+       
+       _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, _tab_title);
-                       _parent_notebook->set_tab_detachable (_contents);
-                       _parent_notebook->set_tab_reorderable (_contents);
-                       _parent_notebook->set_current_page (_parent_notebook->page_num (_contents));
-               }
+       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));
 
-               /* don't let anything else handle this */
-               
-               return true;
-       } 
 
-       /* nothing to do */
-       return false;
+       /* have to force this on, which is semantically correct, since
+        * the user has effectively asked for it.
+        */
+
+       tab_requested_by_state = true;
+       StateChange (*this);
+}
+
+bool
+Tabbable::delete_event_handler (GdkEventAny *ev)
+{
+       _window->hide();
+
+       return true;
 }
 
 bool
@@ -213,17 +276,30 @@ Tabbable::is_tabbed () const
                return false;
        }
 
-       if (_parent_notebook) {
+       if (_parent_notebook && _contents.get_parent()) {
                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));
        }
 }
@@ -240,11 +316,19 @@ Tabbable::xml_node_name()
        return WindowProxy::xml_node_name();
 }
 
+bool
+Tabbable::tabbed () const
+{
+       return _parent_notebook && (_parent_notebook->page_num (_contents) >= 0);
+}
+
 XMLNode&
 Tabbable::get_state()
 {
        XMLNode& node (WindowProxy::get_state());
 
+       node.add_property (X_("tabbed"),  tabbed() ? X_("yes") : X_("no"));
+       
        return node;
 }
 
@@ -261,15 +345,41 @@ Tabbable::set_state (const XMLNode& node, int version)
                }
        }
 
+       XMLNodeList children = node.children ();
+       XMLNode* window_node = node.child ("Window");
+
+       if (window_node) {
+               const XMLProperty* prop = window_node->property (X_("tabbed"));
+               if (prop) {
+                       tab_requested_by_state = PBD::string_is_affirmative (prop->value());
+               }
+       }
+
+       if (tab_requested_by_state) {
+
+               std::cerr << name() << " pn " << _parent_notebook << std::endl;
+               if (_parent_notebook) {
+                       std::cerr << "\t page " << _parent_notebook->page_num (_contents) << std::endl;
+               }
+
+               attach ();
+       } else {
+               /* this does nothing if not tabbed */
+               hide_tab ();
+       }
+       
        return ret;
 }
 
 void
-Tabbable::make_visible ()
+Tabbable::window_mapped ()
 {
-       if (current_toplevel() == _window) {
-               _window->present ();
-       } else {
-               show_tab ();
-       }
+       StateChange (*this);
+}
+
+void
+Tabbable::window_unmapped ()
+{
+       StateChange (*this);
 }
+