#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;
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 ()
}
}
-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 = ¬ebook;
- _tab_title = tab_title;
+
+ if (tab_requested_by_state) {
+ attach ();
+ }
}
Window*
if (parent) {
parent->remove (_contents);
}
- _own_notebook.append_page (_contents, _tab_box);
+ _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*
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;
}
_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;
}
}
}
-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
*/
/* 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 ();
}
}
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 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 ();
}
}
}
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);
}