X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fgtkmm2ext%2Ftabbable.cc;h=451405db63df1987095ec1d16e41624e9a756ac2;hb=d6f3a006d29341cbefd5d97881ef28a43bf1f757;hp=123649036ac8cc9d462038620e9c2f4c6a3c1a9f;hpb=a29f76c1c45e112279d9cb0a002f41d0fb9155ab;p=ardour.git diff --git a/libs/gtkmm2ext/tabbable.cc b/libs/gtkmm2ext/tabbable.cc index 123649036a..451405db63 100644 --- a/libs/gtkmm2ext/tabbable.cc +++ b/libs/gtkmm2ext/tabbable.cc @@ -20,17 +20,33 @@ #include #include #include +#include #include "gtkmm2ext/tabbable.h" +#include "gtkmm2ext/gtk_ui.h" +#include "gtkmm2ext/utils.h" #include "gtkmm2ext/visibility_tracker.h" +#include "i18n.h" + using namespace Gtkmm2ext; using namespace Gtk; using std::string; -Tabbable::Tabbable (Widget& w) - : _contents (w) +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 () @@ -42,20 +58,39 @@ Tabbable::~Tabbable () } void -Tabbable::add_to_notebook (Notebook& notebook, const string& tab_title, int position) +Tabbable::tab_close_clicked () { - notebook.insert_page (_contents, tab_title, position, false); - notebook.set_tab_detachable (_contents); + hide_tab (); +} +void +Tabbable::add_to_notebook (Notebook& notebook, const string& tab_title) +{ _parent_notebook = ¬ebook; _tab_title = tab_title; - _notebook_position = position; + _tab_label.set_text (tab_title); + _tab_box.show_all (); + + 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, _tab_box); + } + + return win; + } bool @@ -65,7 +100,7 @@ Tabbable::window_visible () return false; } - return visible(); + return _window->is_visible(); } Window* @@ -78,74 +113,158 @@ Tabbable::get (bool create) if (!create) { return 0; } + + /* From here on, we're creating the window + */ if ((_window = new Window (WINDOW_TOPLEVEL)) == 0) { return 0; } - /* allow parent window to become the key focus window */ - _window->set_flags (CAN_FOCUS); + _window->add (_own_notebook); + _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; } void -Tabbable::show_window () +Tabbable::show_own_window (bool and_pack_it) { - Window* toplevel = dynamic_cast (_contents.get_toplevel()); + Gtk::Widget* parent = _contents.get_parent(); + Gtk::Allocation alloc; - if (toplevel == _window) { - _window->present (); + 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()); } - if (!_visible) { /* was hidden, update status */ - set_pos_and_size (); + _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 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. + */ + + show_own_window (false); + return &_own_notebook; +} + +void +Tabbable::show_window () +{ + make_visible (); + + if (_window && (current_toplevel() == _window)) { + if (!_visible) { /* was hidden, update status */ + set_pos_and_size (); + } } +} - 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); +void +Tabbable::make_visible () +{ + if (_window && (current_toplevel() == _window)) { + _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 () +{ + if (_window && (current_toplevel() == _window)) { + _window->hide (); + } else { + hide_tab (); + } +} + +void +Tabbable::detach () +{ + show_own_window (true); +} + +void +Tabbable::attach () { - Window* toplevel = dynamic_cast (_contents.get_toplevel()); + 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) { - - _parent_notebook->insert_page (_contents, _tab_title, _notebook_position); - _parent_notebook->set_tab_detachable (_contents); - } + _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)); - show_all (); + 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 @@ -157,9 +276,110 @@ 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)); + } +} + +Gtk::Window* +Tabbable::current_toplevel () const +{ + return dynamic_cast (contents().get_toplevel()); +} + +string +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; +} + +int +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; + } + } + } + + 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::window_mapped () +{ + StateChange (*this); +} + +void +Tabbable::window_unmapped () +{ + StateChange (*this); +} +