Move "Feedback" option to control-portocol settings
[ardour.git] / libs / gtkmm2ext / pane.cc
index fa24a7d4f7e03a9b99e82a5c224b746cbccac9c6..60a6dc543c195831d6e085831e3d01b51bf8f95f 100644 (file)
@@ -20,7 +20,7 @@
 #include <gdkmm/cursor.h>
 #include "gtkmm2ext/pane.h"
 
-#include "i18n.h"
+#include "pbd/i18n.h"
 
 using namespace PBD;
 using namespace Gtk;
@@ -30,7 +30,8 @@ using namespace std;
 Pane::Pane (bool h)
        : horizontal (h)
        , did_move (false)
-       , divider_width (5)
+       , divider_width (2)
+       , check_fract (false)
 {
        using namespace Gdk;
 
@@ -40,23 +41,29 @@ Pane::Pane (bool h)
        if (horizontal) {
                drag_cursor = Cursor (SB_H_DOUBLE_ARROW);
        } else {
-               drag_cursor = Cursor (SB_H_DOUBLE_ARROW);
+               drag_cursor = Cursor (SB_V_DOUBLE_ARROW);
        }
 }
 
-void
-Pane::set_child_minsize (Children::size_type n, int32_t minsize)
+Pane::~Pane ()
 {
-       Children::iterator c = children.begin();
+       for (Children::iterator c = children.begin(); c != children.end(); ++c) {
+               c->show_con.disconnect ();
+               c->hide_con.disconnect ();
+               c->w->remove_destroy_notify_callback (&(*c));
+               c->w->unparent ();
+       }
+}
 
-       while (n--) {
-               if (c == children.end()) {
-                       return;
+void
+Pane::set_child_minsize (Gtk::Widget const& w, int32_t minsize)
+{
+       for (Children::iterator c = children.begin(); c != children.end(); ++c) {
+               if (c->w == &w) {
+                       c->minsize = minsize;
+                       break;
                }
-               ++c;
        }
-
-       c->minsize = minsize;
 }
 
 void
@@ -115,6 +122,7 @@ void
 Pane::add_divider ()
 {
        Divider* d = new Divider;
+       d->set_name (X_("Divider"));
        d->signal_button_press_event().connect (sigc::bind (sigc::mem_fun (*this, &Pane::handle_press_event), d), false);
        d->signal_button_release_event().connect (sigc::bind (sigc::mem_fun (*this, &Pane::handle_release_event), d), false);
        d->signal_motion_notify_event().connect (sigc::bind (sigc::mem_fun (*this, &Pane::handle_motion_event), d), false);
@@ -126,25 +134,63 @@ Pane::add_divider ()
        dividers.push_back (d);
 }
 
+void
+Pane::handle_child_visibility ()
+{
+       reallocate (get_allocation());
+}
+
 void
 Pane::on_add (Widget* w)
 {
-       children.push_back (Child (w, 0));
+       children.push_back (Child (this, w, 0));
+       Child& kid = children.back ();
 
        w->set_parent (*this);
+       /* Gtkmm 2.4 does not correctly arrange for ::on_remove() to be called
+          for custom containers that derive from Gtk::Container. So ... we need
+          to ensure that we hear about child destruction ourselves.
+       */
+       w->add_destroy_notify_callback (&children.back(), &Pane::notify_child_destroyed);
+
+       kid.show_con = w->signal_show().connect (sigc::mem_fun (*this, &Pane::handle_child_visibility));
+       kid.hide_con = w->signal_hide().connect (sigc::mem_fun (*this, &Pane::handle_child_visibility));
 
        while (dividers.size() < (children.size() - 1)) {
                add_divider ();
        }
 }
 
+void*
+Pane::notify_child_destroyed (void* data)
+{
+       Child* child = reinterpret_cast<Child*> (data);
+       return child->pane->child_destroyed (child->w);
+}
+
+void*
+Pane::child_destroyed (Gtk::Widget* w)
+{
+       for (Children::iterator c = children.begin(); c != children.end(); ++c) {
+               if (c->w == w) {
+                       c->show_con.disconnect ();
+                       c->hide_con.disconnect ();
+                       children.erase (c);
+                       break;
+               }
+       }
+       return 0;
+}
+
 void
 Pane::on_remove (Widget* w)
 {
-       w->unparent ();
-
        for (Children::iterator c = children.begin(); c != children.end(); ++c) {
                if (c->w == w) {
+                       c->show_con.disconnect ();
+                       c->hide_con.disconnect ();
+                       w->remove_destroy_notify_callback (&(*c));
+                       w->unparent ();
                        children.erase (c);
                        break;
                }
@@ -186,11 +232,30 @@ Pane::reallocate (Gtk::Allocation const & alloc)
         Children::iterator next;
         Dividers::iterator div;
 
-        for (child = children.begin(), div = dividers.begin(); child != children.end(); ) {
+        child = children.begin();
+
+        /* skip initial hidden children */
+
+        while (child != children.end()) {
+               if (child->w->is_visible()) {
+                       break;
+               }
+               ++child;
+        }
+
+        for (div = dividers.begin(); child != children.end(); ) {
 
                Gtk::Allocation child_alloc;
+
                next = child;
-               ++next;
+
+               /* Move on to next *visible* child */
+
+               while (++next != children.end()) {
+                       if (next->w->is_visible()) {
+                               break;
+                       }
+               }
 
                child_alloc.set_x (xpos);
                child_alloc.set_y (ypos);
@@ -227,13 +292,14 @@ Pane::reallocate (Gtk::Allocation const & alloc)
                }
 
                child->w->size_allocate (child_alloc);
-               ++child;
 
-               if (child == children.end()) {
+               if (next == children.end()) {
                        /* done, no more children, no need for a divider */
                        break;
                }
 
+               child = next;
+
                /* add a divider between children */
 
                Gtk::Allocation divider_allocation;
@@ -254,6 +320,14 @@ Pane::reallocate (Gtk::Allocation const & alloc)
                }
 
                (*div)->size_allocate (divider_allocation);
+               (*div)->show ();
+               ++div;
+        }
+
+        /* hide all remaining dividers */
+
+        while (div != dividers.end()) {
+               (*div)->hide ();
                ++div;
         }
 }
@@ -264,14 +338,17 @@ Pane::on_expose_event (GdkEventExpose* ev)
        Children::iterator child;
        Dividers::iterator div;
 
-       for (child = children.begin(), div = dividers.begin(); child != children.end(); ++child, ++div) {
+       for (child = children.begin(), div = dividers.begin(); child != children.end(); ++child) {
 
                if (child->w->is_visible()) {
                        propagate_expose (*(child->w), ev);
                }
 
                if (div != dividers.end()) {
-                       propagate_expose (*(child->w), ev);
+                       if ((*div)->is_visible()) {
+                               propagate_expose (**div, ev);
+                       }
+                       ++div;
                }
         }
 
@@ -292,13 +369,74 @@ Pane::handle_release_event (GdkEventButton* ev, Divider* d)
 {
        d->dragging = false;
 
-       if (did_move) {
+       if (did_move && !children.empty()) {
                children.front().w->queue_resize ();
                did_move = false;
        }
 
        return false;
 }
+void
+Pane::set_check_divider_position (bool yn)
+{
+       check_fract = yn;
+}
+
+bool
+Pane::fract_is_ok (Dividers::size_type div, float fract)
+{
+#ifdef __APPLE__
+       if (!check_fract) {
+               return true;
+       }
+
+
+       if (get_allocation().get_width() == 1 && get_allocation().get_height() == 1) {
+               /* space not * allocated - * divider being set from startup code. Let it pass,
+                  since our goal is mostly to catch drags to a position that will interfere with window
+                  resizing.
+               */
+               return true;
+       }
+
+       /* On Quartz, if the pane handle (divider) gets to
+          be adjacent to the window edge, you can no longer grab it:
+          any attempt to do so is interpreted by the Quartz window
+          manager ("Finder") as a resize drag on the window edge.
+       */
+
+       if (horizontal) {
+               if (div == dividers.size() - 1) {
+                       if (get_allocation().get_width() * (1.0 - fract) < (divider_width*2)) {
+                               /* too close to right edge */
+                               return false;
+                       }
+               }
+
+               if (div == 0) {
+                       if (get_allocation().get_width() * fract < (divider_width*2)) {
+                               /* too close to left edge */
+                               return false;
+                       }
+               }
+       } else {
+               if (div == dividers.size() - 1) {
+                       if (get_allocation().get_height() * (1.0 - fract) < (divider_width*2)) {
+                               /* too close to bottom */
+                               return false;
+                       }
+               }
+
+               if (div == 0) {
+                       if (get_allocation().get_width() * fract < (divider_width*2)) {
+                               /* too close to top */
+                               return false;
+                       }
+               }
+       }
+#endif
+       return true;
+}
 
 bool
 Pane::handle_motion_event (GdkEventMotion* ev, Divider* d)
@@ -317,8 +455,9 @@ Pane::handle_motion_event (GdkEventMotion* ev, Divider* d)
        d->translate_coordinates (*this, ev->x, ev->y, px, py);
 
        Dividers::iterator prev = dividers.end();
+       Dividers::size_type div = 0;
 
-       for (Dividers::iterator di = dividers.begin(); di != dividers.end(); ++di) {
+       for (Dividers::iterator di = dividers.begin(); di != dividers.end(); ++di, ++div) {
                if (*di == d) {
                        break;
                }
@@ -348,6 +487,10 @@ Pane::handle_motion_event (GdkEventMotion* ev, Divider* d)
 
        new_fract = min (1.0f, max (0.0f, new_fract));
 
+       if (!fract_is_ok (div, new_fract)) {
+               return true;
+       }
+
        if (new_fract != d->fract) {
                d->fract = new_fract;
                reallocate (get_allocation ());
@@ -360,26 +503,27 @@ Pane::handle_motion_event (GdkEventMotion* ev, Divider* d)
 void
 Pane::set_divider (Dividers::size_type div, float fract)
 {
-       bool redraw = false;
-
        Dividers::iterator d = dividers.begin();
 
-       while (div--) {
-               ++d;
-               if (d == dividers.end()) {
-                       /* caller is trying to set divider that does not exist
-                        * yet.
-                        */
-                       return;
-               }
+       for (d = dividers.begin(); d != dividers.end() && div != 0; ++d, --div) {
+               /* relax */
        }
 
-       if (fract != (*d)->fract) {
-               (*d)->fract = fract;
-               redraw = true;
+       if (d == dividers.end()) {
+               /* caller is trying to set divider that does not exist
+                * yet.
+                */
+               return;
+       }
+
+       fract = max (0.0f, min (1.0f, fract));
+
+       if (!fract_is_ok (div, fract)) {
+               return;
        }
 
-       if (redraw) {
+       if (fract != (*d)->fract) {
+               (*d)->fract = fract;
                /* our size hasn't changed, but our internal allocations have */
                reallocate (get_allocation());
                queue_draw ();
@@ -391,14 +535,15 @@ Pane::get_divider (Dividers::size_type div)
 {
        Dividers::iterator d = dividers.begin();
 
-       while (div--) {
-               ++d;
-               if (d == dividers.end()) {
-                       /* caller is trying to set divider that does not exist
-                        * yet.
-                        */
-                       return -1.0f;
-               }
+       for (d = dividers.begin(); d != dividers.end() && div != 0; ++d, --div) {
+               /* relax */
+       }
+
+       if (d == dividers.end()) {
+               /* caller is trying to set divider that does not exist
+                * yet.
+                */
+               return -1.0f;
        }
 
        return (*d)->fract;
@@ -467,5 +612,6 @@ Pane::handle_leave_event (GdkEventCrossing*, Divider* d)
 {
        d->get_window()->set_cursor ();
        d->set_state (Gtk::STATE_NORMAL);
+       d->queue_draw ();
        return true;
 }