#include <gdkmm/cursor.h>
#include "gtkmm2ext/pane.h"
-#include "i18n.h"
+#include "pbd/i18n.h"
using namespace PBD;
using namespace Gtk;
Pane::Pane (bool h)
: horizontal (h)
, did_move (false)
- , divider_width (5)
+ , divider_width (2)
+ , check_fract (false)
{
using namespace Gdk;
if (horizontal) {
drag_cursor = Cursor (SB_H_DOUBLE_ARROW);
} else {
- drag_cursor = Cursor (SB_H_DOUBLE_ARROW);
+ drag_cursor = Cursor (SB_V_DOUBLE_ARROW);
+ }
+}
+
+Pane::~Pane ()
+{
+ 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 ();
+ }
+}
+
+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;
+ }
}
}
for (Children::iterator child = children.begin(); child != children.end(); ++child) {
GtkRequisition r;
- (*child)->size_request (r);
+ child->w->size_request (r);
if (horizontal) {
largest.height = max (largest.height, r.height);
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);
dividers.push_back (d);
}
+void
+Pane::handle_child_visibility ()
+{
+ reallocate (get_allocation());
+}
+
void
Pane::on_add (Widget* w)
{
- children.push_back (w);
+ 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 ();
- children.remove (w);
+ 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;
+ }
+ }
}
void
if (children.size() == 1) {
/* only child gets the full allocation */
- children.front()->size_allocate (alloc);
+ children.front().w->size_allocate (alloc);
return;
}
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);
}
Gtk::Requisition cr;
- (*child)->size_request (cr);
+ child->w->size_request (cr);
if (horizontal) {
child_alloc.set_width ((gint) floor (remaining * fract));
ypos += child_alloc.get_height ();
}
- (*child)->size_allocate (child_alloc);
- ++child;
+ if (child->minsize) {
+ if (horizontal) {
+ child_alloc.set_width (max (child_alloc.get_width(), child->minsize));
+ } else {
+ child_alloc.set_height (max (child_alloc.get_height(), child->minsize));
+ }
+ }
+
+ child->w->size_allocate (child_alloc);
- 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;
}
(*div)->size_allocate (divider_allocation);
+ (*div)->show ();
+ ++div;
+ }
+
+ /* hide all remaining dividers */
+
+ while (div != dividers.end()) {
+ (*div)->hide ();
++div;
}
}
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) {
- propagate_expose (**child, ev);
+ if (child->w->is_visible()) {
+ propagate_expose (*(child->w), ev);
+ }
if (div != dividers.end()) {
- propagate_expose (**div, ev);
+ if ((*div)->is_visible()) {
+ propagate_expose (**div, ev);
+ }
+ ++div;
}
}
{
d->dragging = false;
- if (did_move) {
- children.front()->queue_resize ();
+ 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)
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;
}
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 ());
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 ();
{
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;
* the iterators safe;
*/
- for (Children::iterator w = children.begin(); w != children.end(); ) {
- Children::iterator next = w;
+ for (Children::iterator c = children.begin(); c != children.end(); ) {
+ Children::iterator next = c;
++next;
- callback ((*w)->gobj(), callback_data);
- w = next;
+ callback (c->w->gobj(), callback_data);
+ c = next;
}
if (include_internals) {
{
d->get_window()->set_cursor ();
d->set_state (Gtk::STATE_NORMAL);
+ d->queue_draw ();
return true;
}