Fix crash for external windows
[ardour.git] / libs / gtkmm2ext / window_proxy.cc
index e18596dd9e9f6a8c1e3f7c534bbd2a6329574c92..9ba0e320de6e27461aaf3ebd541011af1d42b4be 100644 (file)
 #include <gtkmm/action.h>
 #include <gtkmm/window.h>
 
-#include "pbd/convert.h"
 #include "pbd/xml++.h"
 
 #include "gtkmm2ext/window_proxy.h"
 #include "gtkmm2ext/visibility_tracker.h"
 
-#include "i18n.h"
+#include "pbd/i18n.h"
 
 using namespace Gtk;
 using namespace Gtkmm2ext;
@@ -39,8 +38,9 @@ WindowProxy::WindowProxy (const std::string& name)
        , _x_off (-1)
        , _y_off (-1)
        , _width (-1)
-       , _height (-1) 
+       , _height (-1)
        , vistracker (0)
+       , _state_mask (StateMask (Position|Size))
 {
 }
 
@@ -52,8 +52,9 @@ WindowProxy::WindowProxy (const std::string& name, const std::string& menu_name)
        , _x_off (-1)
        , _y_off (-1)
        , _width (-1)
-       , _height (-1) 
+       , _height (-1)
        , vistracker (0)
+       , _state_mask (StateMask (Position|Size))
 {
 }
 
@@ -65,8 +66,9 @@ WindowProxy::WindowProxy (const std::string& name, const std::string& menu_name,
        , _x_off (-1)
        , _y_off (-1)
        , _width (-1)
-       , _height (-1) 
+       , _height (-1)
        , vistracker (0)
+       , _state_mask (StateMask (Position|Size))
 {
        set_state (node, 0);
 }
@@ -81,12 +83,14 @@ int
 WindowProxy::set_state (const XMLNode& node, int /* version */)
 {
        XMLNodeList children = node.children ();
-
+       XMLNode const * child;
        XMLNodeList::const_iterator i = children.begin ();
 
        while (i != children.end()) {
-               XMLProperty* prop = (*i)->property (X_("name"));
-               if ((*i)->name() == X_("Window") && prop && prop->value() == _name) {
+               child = *i;
+               std::string name;
+               if (child->name () == X_("Window") && child->get_property (X_("name"), name) &&
+                   name == _name) {
                        break;
                }
 
@@ -95,26 +99,13 @@ WindowProxy::set_state (const XMLNode& node, int /* version */)
 
        if (i != children.end()) {
 
-               XMLProperty* prop;
-
-               std::cerr << " PB setting state\n";
-               
-               if ((prop = (*i)->property (X_("visible"))) != 0) {
-                       _visible = PBD::string_is_affirmative (prop->value ());
-               }
+               child = *i;
 
-               if ((prop = (*i)->property (X_("x-off"))) != 0) {
-                       _x_off = atoi (prop->value());
-               }
-               if ((prop = (*i)->property (X_("y-off"))) != 0) {
-                       _y_off = atoi (prop->value());
-               }
-               if ((prop = (*i)->property (X_("x-size"))) != 0) {
-                       _width = atoi (prop->value());
-               }
-               if ((prop = (*i)->property (X_("y-size"))) != 0) {
-                       _height = atoi (prop->value());
-               }
+               child->get_property (X_("visible"), _visible);
+               child->get_property (X_("x-off"), _x_off);
+               child->get_property (X_("y-off"), _y_off);
+               child->get_property (X_("x-size"), _width);
+               child->get_property (X_("y-size"), _height);
        }
 
        if (_window) {
@@ -131,16 +122,17 @@ WindowProxy::set_action (Glib::RefPtr<Gtk::Action> act)
 }
 
 std::string
-WindowProxy::action_name() const 
+WindowProxy::action_name() const
 {
        return string_compose (X_("toggle-%1"), _name);
 }
 
 void
-WindowProxy::toggle() 
+WindowProxy::toggle()
 {
        if (!_window) {
                (void) get (true);
+               setup ();
                assert (_window);
                /* XXX this is a hack - the window object should really
                   ensure its components are all visible. sigh.
@@ -148,12 +140,17 @@ WindowProxy::toggle()
                _window->show_all();
                /* we'd like to just call this and nothing else */
                _window->present ();
-
        } else {
                if (_window->is_mapped()) {
                        save_pos_and_size();
                }
-               vistracker->cycle_visibility ();
+
+               if (vistracker) {
+                       vistracker->cycle_visibility ();
+               } else {
+                       _window->present ();
+               }
+
                if (_window->is_mapped()) {
                        if (_width != -1 && _height != -1) {
                                _window->set_default_size (_width, _height);
@@ -175,12 +172,11 @@ XMLNode&
 WindowProxy::get_state ()
 {
        XMLNode* node = new XMLNode (xml_node_name());
-       char buf[32];   
 
-       node->add_property (X_("name"), _name);
+       node->set_property (X_("name"), _name);
 
        if (_window && vistracker) {
-               
+
                /* we have a window, so use current state */
 
                _visible = vistracker->partially_visible ();
@@ -188,15 +184,29 @@ WindowProxy::get_state ()
                _window->get_size (_width, _height);
        }
 
-       node->add_property (X_("visible"), _visible? X_("yes") : X_("no"));
-       snprintf (buf, sizeof (buf), "%d", _x_off);
-       node->add_property (X_("x-off"), buf);
-       snprintf (buf, sizeof (buf), "%d", _y_off);
-       node->add_property (X_("y-off"), buf);
-       snprintf (buf, sizeof (buf), "%d", _width);
-       node->add_property (X_("x-size"), buf);
-       snprintf (buf, sizeof (buf), "%d", _height);
-       node->add_property (X_("y-size"), buf);
+       int x, y, w, h;
+
+       if (_state_mask & Position) {
+               x = _x_off;
+               y = _y_off;
+       } else {
+               x = -1;
+               y = -1;
+       }
+
+       if (_state_mask & Size) {
+               w = _width;
+               h = _height;
+       } else {
+               w = -1;
+               h = -1;
+       }
+
+       node->set_property (X_("visible"), _visible);
+       node->set_property (X_("x-off"), x);
+       node->set_property (X_("y-off"), y);
+       node->set_property (X_("x-size"), w);
+       node->set_property (X_("y-size"), h);
 
        return *node;
 }
@@ -206,6 +216,10 @@ WindowProxy::drop_window ()
 {
        if (_window) {
                _window->hide ();
+               delete_connection.disconnect ();
+               configure_connection.disconnect ();
+               map_connection.disconnect ();
+               unmap_connection.disconnect ();
                delete _window;
                _window = 0;
                delete vistracker;
@@ -226,12 +240,68 @@ WindowProxy::setup ()
 {
        assert (_window);
 
-       vistracker = new Gtkmm2ext::VisibilityTracker (*_window);
-       _window->signal_delete_event().connect (sigc::mem_fun (*this, &WindowProxy::delete_event_handler));
+       assert (_window);
+
+       delete_connection = _window->signal_delete_event().connect (sigc::mem_fun (*this, &WindowProxy::delete_event_handler));
+       configure_connection = _window->signal_configure_event().connect (sigc::mem_fun (*this, &WindowProxy::configure_handler), false);
+       map_connection = _window->signal_map().connect (sigc::mem_fun (*this, &WindowProxy::map_handler), false);
+       unmap_connection = _window->signal_unmap().connect (sigc::mem_fun (*this, &WindowProxy::unmap_handler), false);
 
        set_pos_and_size ();
 }
-       
+
+void
+WindowProxy::map_handler ()
+{
+       vistracker = new Gtkmm2ext::VisibilityTracker (*_window);
+       /* emit our own signal */
+       signal_map ();
+}
+
+void
+WindowProxy::unmap_handler ()
+{
+       /* emit out own signal */
+       signal_unmap ();
+}
+
+bool
+WindowProxy::configure_handler (GdkEventConfigure* ev)
+{
+       /* stupidly, the geometry data in the event isn't the same as we get
+          from the window geometry APIs.so we have to actively interrogate
+          them to get the new information.
+
+          the difference is generally down to window manager framing.
+       */
+       if (!visible() || !_window->is_mapped()) {
+               return false;
+       }
+       save_pos_and_size ();
+       return false;
+}
+
+
+bool
+WindowProxy::visible() const
+{
+       if (vistracker) {
+               /* update with current state */
+               _visible = vistracker->partially_visible();
+       }
+       return _visible;
+}
+
+bool
+WindowProxy::fully_visible () const
+{
+       if (!vistracker) {
+               /* no vistracker .. no window .. cannot be fully visible */
+               return false;
+       }
+       return vistracker->fully_visible();
+}
+
 void
 WindowProxy::show ()
 {
@@ -281,7 +351,12 @@ WindowProxy::hide ()
 bool
 WindowProxy::delete_event_handler (GdkEventAny* /*ev*/)
 {
-       hide();
+       if (_action) {
+               _action->activate ();
+       } else {
+               hide();
+       }
+
        return true;
 }
 
@@ -301,13 +376,34 @@ WindowProxy::set_pos_and_size ()
                return;
        }
 
-       if (_width != -1 || _height != -1 || _x_off != -1 || _y_off != -1) {
+       if ((_state_mask & Position) && (_width != -1 || _height != -1 || _x_off != -1 || _y_off != -1)) {
                /* cancel any mouse-based positioning */
                _window->set_position (Gtk::WIN_POS_NONE);
        }
 
-       if (_width != -1 && _height != -1) {
-               _window->set_default_size (_width, _height);
+       if ((_state_mask & Size) && _width != -1 && _height != -1) {
+               _window->resize (_width, _height);
+       }
+
+       if ((_state_mask & Position) && _x_off != -1 && _y_off != -1) {
+               _window->move (_x_off, _y_off);
+       }
+}
+
+void
+WindowProxy::set_pos ()
+{
+       if (!_window) {
+               return;
+       }
+
+       if (!(_state_mask & Position)) {
+               return;
+       }
+
+       if (_width != -1 || _height != -1 || _x_off != -1 || _y_off != -1) {
+               /* cancel any mouse-based positioning */
+               _window->set_position (Gtk::WIN_POS_NONE);
        }
 
        if (_x_off != -1 && _y_off != -1) {
@@ -315,3 +411,8 @@ WindowProxy::set_pos_and_size ()
        }
 }
 
+void
+WindowProxy::set_state_mask (StateMask sm)
+{
+       _state_mask = sm;
+}