/* * Copyright (c) 1987, 1988, 1989 Stanford University * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided * that the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Stanford not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. Stanford makes no representations about * the suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * STANFORD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. * IN NO EVENT SHALL STANFORD BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Basic composite object for interaction. */ #include #include class BoxElement { public: Interactor* child; bool visible; BoxElement* next; }; class BoxDimension { public: int natural; int stretch; int shrink; }; class BoxCanonical { public: BoxDimension major; BoxDimension minor; }; Box::Box () : Scene(nil, nil) { nelements = 0; head = nil; tail = nil; } Box::~Box () { register BoxElement* e; register BoxElement* next; for (e = head; e != nil; e = next) { next = e->next; delete e->child; delete e; } } void Box::Align (Alignment a) { align = a; } void Box::DoInsert (Interactor* i, bool, Coord&, Coord&) { register BoxElement* e; ++nelements; e = new BoxElement; e->child = i; e->next = nil; if (head == nil) { head = e; tail = e; } else { tail->next = e; tail = e; } } void Box::DoInsert (Interactor* i, bool, Coord&, Coord&, int n) { register BoxElement* e, *t1, *t2; /* * Bump num of elems and alloc a new elem. */ ++nelements; e = new BoxElement; e->child = i; e->next = nil; /* * If box is empty, make new elem all there is. */ if (head == nil) { head = e; tail = e; return; } /* * If insert point is < 1, insert in front of current head. */ if (n < 1) { e->next = head; head = e; return; } /* * If n >= *entering* num elems, insert after current tail. */ if (n >= nelements - 1) { tail->next = e; tail = e; return; } /* * Cruise to nth elem and insert. Note that boundary insert cases have * already been handled, so we'll always end up between two non-null nodes * after this cruise. */ for (t1 = head; --n && t1; t1 = t1->next) ; /* * Splice the puppy in. */ t2 = t1->next; t1->next = e; e->next = t2; } void Box::DoChange (Interactor*) { Reconfig(); } void Box::DoRemove (Interactor* i) { register BoxElement* e, * prev; --nelements; prev = nil; for (e = head; e != nil; e = e->next) { if (e->child == i) { if (prev == nil) { head = e->next; } else { prev->next = e->next; } if (e == tail) { tail = prev; } // delete e; break; } prev = e; } ComputeShape(shape); } void Box::Reconfig () { ComputeShape(shape); } void Box::Resize () { register BoxElement* e; /* box element */ Shape aggrshape; /* combined shape of components */ BoxCanonical total; /* components' shape along major axis */ int major, minor; /* actual dimensions of box */ register int have; /* how much box is willing to change */ register int need; /* how much box needs to change to fit */ bool grow; /* true if stretching, false if shrinking */ BoxCanonical s; /* element shape along major axis */ register int pos; /* where to put next element on major axis */ register int len; /* size of element along major axis */ register int n; /* temporary variable */ ComputeShape(&aggrshape); GetActual(major, minor); GetCanonical(&aggrshape, total); n = total.major.natural; if (major > n) { /* more space than desired ==> stretch elements */ grow = true; have = total.major.stretch; need = min(major - n, have); } else { /* less (or equal) space than desired ==> (maybe) shrink elements */ grow = false; have = total.major.shrink; need = min(n - major, have); } pos = 0; for (e = head; e != nil; e = e->next) { GetCanonical(e->child->GetShape(), s); len = s.major.natural; if (have > 0) { if (grow) { n = int(double(s.major.stretch)*double(need)/double(have)); len += n; have -= s.major.stretch; } else { n = int(double(s.major.shrink)*double(need)/double(have)); len -= n; have -= s.major.shrink; } need -= n; } n = s.minor.natural; if (n == 0) { n = minor; } else if (n > minor) { n = max(n - s.minor.shrink, minor); } else if (n < minor) { n = min(n + s.minor.stretch, minor); } if (n > 0 && len > 0) { e->visible = true; PlaceElement(e->child, pos, len, minor, n); } else { e->visible = false; } pos += len; } } void Box::Draw () { register BoxElement* e; for (e = head; e != nil; e = e->next) { if (e->visible) { e->child->Draw(); } } } void Box::GetComponents (Interactor** c, int nc, Interactor**& a, int& n) { register BoxElement* e; register Interactor** ap; n = nelements; a = (n <= nc) ? c : new Interactor*[n]; ap = a; for (e = head; e != nil; e = e->next) { *ap++ = e->child; } } BoxElement* Box::Head () { return head; } /* * Default virtuals. */ void Box::ComputeShape (Shape*) {} void Box::GetActual (int& major, int& minor) {} void Box::GetCanonical (Shape*, BoxCanonical&) {} void Box::PlaceElement (Interactor*, Coord, int, int, int) {} void HBox::Init () { SetClassName("HBox"); align = Bottom; if (! rigid) { shape->Rigid(0, 0, vfil, vfil); } } HBox::HBox () { Init(); } HBox::HBox (Interactor* i1) { Init(); Insert(i1); } HBox::HBox (Interactor* i1, Interactor* i2) { Init(); Insert(i1); Insert(i2); } HBox::HBox (Interactor* i1, Interactor* i2, Interactor* i3) { Init(); Insert(i1); Insert(i2); Insert(i3); } HBox::HBox (Interactor* i1, Interactor* i2, Interactor* i3, Interactor* i4) { Init(); Insert(i1); Insert(i2); Insert(i3); Insert(i4); } HBox::HBox ( Interactor* i1, Interactor* i2, Interactor* i3, Interactor* i4, Interactor* i5 ) { Init(); Insert(i1); Insert(i2); Insert(i3); Insert(i4); Insert(i5); } HBox::HBox ( Interactor* i1, Interactor* i2, Interactor* i3, Interactor* i4, Interactor* i5, Interactor* i6 ) { Init(); Insert(i1); Insert(i2); Insert(i3); Insert(i4); Insert(i5); Insert(i6); } HBox::HBox ( Interactor* i1, Interactor* i2, Interactor* i3, Interactor* i4, Interactor* i5, Interactor* i6, Interactor* i7 ) { Init(); Insert(i1); Insert(i2); Insert(i3); Insert(i4); Insert(i5); Insert(i6); Insert(i7); } HBox::~HBox() { } void HBox::ComputeShape (register Shape* box) { register BoxElement* e; register Shape* s; register int vmin, vmax; box->width = 0; box->height = 0; if (! rigid) { box->Rigid(0, 0, vfil, vfil); } vmin = -vfil; vmax = vfil; for (e = Head(); e != nil; e = e->next) { s = e->child->GetShape(); box->width += s->width; box->height = max(box->height, s->height); box->hstretch += s->hstretch; box->hshrink += s->hshrink; vmin = max(s->height - s->vshrink, vmin); vmax = min(s->height + s->vstretch, vmax); } box->vstretch = max(0, vmax - box->height); box->vshrink = max(0, box->height - vmin); } void HBox::GetActual (int& major, int& minor) { major = xmax + 1; minor = ymax + 1; } void HBox::GetCanonical (register Shape* s, register BoxCanonical& b) { b.major.natural = s->width; b.major.shrink = s->hshrink; b.major.stretch = s->hstretch; b.minor.natural = s->height; b.minor.shrink = s->vshrink; b.minor.stretch = s->vstretch; } void HBox::PlaceElement (Interactor* i, Coord x, int length, int size, int h) { Coord x1, y1, x2, y2; x1 = x; if (align == Top) { y1 = size - h; } else if (align == Center) { y1 = (size - h) / 2; } else /* Bottom */ { y1 = 0; } x2 = x1 + length - 1; y2 = y1 + h - 1; Place(i, x1, y1, x2, y2); } void VBox::Init () { SetClassName("VBox"); align = Left; if (! rigid) { shape->Rigid(hfil, hfil, 0, 0); } } VBox::VBox () { Init(); } VBox::VBox (Interactor* i1) { Init(); Insert(i1); } VBox::VBox (Interactor* i1, Interactor* i2) { Init(); Insert(i1); Insert(i2); } VBox::VBox (Interactor* i1, Interactor* i2, Interactor* i3) { Init(); Insert(i1); Insert(i2); Insert(i3); } VBox::VBox (Interactor* i1, Interactor* i2, Interactor* i3, Interactor* i4) { Init(); Insert(i1); Insert(i2); Insert(i3); Insert(i4); } VBox::VBox ( Interactor* i1, Interactor* i2, Interactor* i3, Interactor* i4, Interactor* i5 ) { Init(); Insert(i1); Insert(i2); Insert(i3); Insert(i4); Insert(i5); } VBox::VBox ( Interactor* i1, Interactor* i2, Interactor* i3, Interactor* i4, Interactor* i5, Interactor* i6 ) { Init(); Insert(i1); Insert(i2); Insert(i3); Insert(i4); Insert(i5); Insert(i6); } VBox::VBox ( Interactor* i1, Interactor* i2, Interactor* i3, Interactor* i4, Interactor* i5, Interactor* i6, Interactor* i7 ) { Init(); Insert(i1); Insert(i2); Insert(i3); Insert(i4); Insert(i5); Insert(i6); Insert(i7); } VBox::~VBox() { } void VBox::ComputeShape (register Shape* box) { register BoxElement* e; register Shape* s; register int hmin, hmax; box->width = 0; box->height = 0; if (! rigid) { box->Rigid(hfil, hfil, 0, 0); } hmin = -hfil; hmax = hfil; for (e = Head(); e != nil; e = e->next) { s = e->child->GetShape(); box->width = max(box->width, s->width); box->height += s->height; box->vstretch += s->vstretch; box->vshrink += s->vshrink; hmin = max(s->width - s->hshrink, hmin); hmax = min(s->width + s->hstretch, hmax); } box->hstretch = max(0, hmax - box->width); box->hshrink = max(0, box->width - hmin); } void VBox::GetActual (int& major, int& minor) { major = ymax + 1; minor = xmax + 1; } void VBox::GetCanonical (register Shape* s, register BoxCanonical& b) { b.major.natural = s->height; b.major.shrink = s->vshrink; b.major.stretch = s->vstretch; b.minor.natural = s->width; b.minor.shrink = s->hshrink; b.minor.stretch = s->hstretch; } void VBox::PlaceElement (Interactor* i, Coord y, int length, int size, int w) { Coord x1, y1, x2, y2; if (align == Right) { x1 = size - w; } else if (align == Center) { x1 = (size - w) / 2; } else /* Left */ { x1 = 0; } x2 = x1 + w - 1; y2 = ymax - y; y1 = y2 - length + 1; Place(i, x1, y1, x2, y2); }