#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;
Tabbable::Tabbable (Widget& w, const string& name)
: WindowProxy (name)
, _contents (w)
+ , _parent_notebook (0)
+ , tab_requested_by_state (true)
{
}
}
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 = ¬ebook;
- _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*
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 ();
-
+ 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
*/
/* 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 ();
}
}
{
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);
+}