/* * Copyright (c) 1987, 1988, 1989, 1990, 1991 Stanford University * Copyright (c) 1991 Silicon Graphics, Inc. * * Permission to use, copy, modify, distribute, and sell this software and * its documentation for any purpose is hereby granted without fee, provided * that (i) the above copyright notices and this permission notice appear in * all copies of the software and related documentation, and (ii) the names of * Stanford and Silicon Graphics may not be used in any advertising or * publicity relating to the software without the specific, prior written * permission of Stanford and Silicon Graphics. * * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. * * IN NO EVENT SHALL STANFORD OR SILICON GRAPHICS BE LIABLE FOR * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE * OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include class CompositionComponent { public: Glyph* glyph_; }; declareList(CompositionComponent_List,CompositionComponent) implementList(CompositionComponent_List,CompositionComponent) static const int BreakViewed = 0x01; static const int BreakValid = 0x02; class Break { public: Break(); boolean viewed() const; void viewed(boolean); boolean valid() const; void valid(boolean); Patch* patch_; int status_; Coord begin_; Coord end_; GlyphIndex first_; GlyphIndex last_; }; Break::Break() { status_ = 0; } inline boolean Break::viewed() const { return (status_ & BreakViewed) != 0; } inline void Break::viewed(boolean v) { if (v) { status_ |= BreakViewed; } else { status_ &= ~BreakViewed; } } inline boolean Break::valid() const { return (status_ & BreakValid) != 0; } inline void Break::valid(boolean v) { if (v) { status_ |= BreakValid; } else { status_ &= ~BreakValid; } } declareList(Break_List,Break) implementList(Break_List,Break) const float epsilon = 0.1; static CompositorIndex __length; static Coord* __natural; static Coord* __stretch; static Coord* __shrink; static int* __penalties; static Coord* __spans; static CompositorIndex* __breaks; static void grow_arrays (CompositorIndex length) { Coord* natural = new Coord[length]; Coord* stretch = new Coord[length]; Coord* shrink = new Coord[length]; int* penalties = new int[length]; Coord* spans = new Coord[length]; CompositorIndex* breaks = new CompositorIndex[length]; for (CompositorIndex i = 0; i < __length; ++i) { natural[i] = __natural[i]; stretch[i] = __stretch[i]; shrink[i] = __shrink[i]; penalties[i] = __penalties[i]; spans[i] = __spans[i]; breaks[i] = __breaks[i]; } delete __natural; __natural = natural; delete __stretch; __stretch = stretch; delete __shrink; __shrink = shrink; delete __penalties; __penalties = penalties; delete __spans; __spans = spans; delete __breaks; __breaks = breaks; __length = length; } static GlyphIndex prev_forced_break( GlyphIndex i, CompositionComponent_List* component ) { GlyphIndex count = component->count(); while ((i >= 0) && (i < count)) { const CompositionComponent& comp = component->item_ref(i); Glyph* g = comp.glyph_; if (g != nil) { Requisition r; g->request(r); if (r.penalty() == PenaltyGood) { break; } } --i; } return i; } static GlyphIndex next_forced_break( GlyphIndex i, CompositionComponent_List* component ) { GlyphIndex count = component->count(); while (i < count) { const CompositionComponent& comp = component->item_ref(i); Glyph* g = comp.glyph_; if (g != nil) { Requisition r; g->request(r); if (r.penalty() == PenaltyGood) { break; } } ++i; } return Math::min(i, count - 1); } static GlyphIndex fill_arrays ( GlyphIndex first, DimensionName dimension, CompositionComponent_List* component ) { GlyphIndex count = component->count(); GlyphIndex last = first; while (last < count) { GlyphIndex index = last - first; if (index >= __length) { grow_arrays(next_forced_break(last, component) - first + 1); } __natural[index] = 0; __stretch[index] = 0; __shrink[index] = 0; __penalties[index] = PenaltyBad; const CompositionComponent& comp = component->item_ref(last); Glyph* g = comp.glyph_; if (g != nil) { Requisition r; g->request(r); Requirement& req = r.requirement(dimension); if (req.defined()) { __natural[index] = req.natural(); __stretch[index] = req.stretch(); __shrink[index] = req.shrink(); } __penalties[index] = r.penalty(); } if (__penalties[index] == PenaltyGood) { break; } else { ++last; } } return Math::min(last, count - 1); } Composition::Composition( Glyph* context, Compositor* compositor, Glyph* separator, DimensionName dimension, Coord span, Coord stretch, Coord shrink, GlyphIndex size ) : MonoGlyph( LayoutKit::instance()->variable_span(context, stretch, shrink) ) { compositor_ = compositor; component_ = new CompositionComponent_List(size); breaks_ = new Break_List(size/50); separator_ = separator; if (separator_ != nil) { separator_->ref(); } view_all_ = true; damaged_ = true; first_damage_ = -1; last_damage_ = -1; item_ = 0; dimension_ = dimension; span_ = span; resizable_ = ( !Math::equal(stretch, Coord(0), float(1e-4)) || !Math::equal(shrink, Coord(0), float(1e-4)) ); } Composition::~Composition() { Resource::unref(separator_); compositor_ = nil; GlyphIndex count = component_->count(); for (GlyphIndex i = 0; i < count; ++i) { CompositionComponent& component = component_->item_ref(i); Resource::unref(component.glyph_); } delete component_; delete breaks_; } void Composition::allocate(Canvas* c, const Allocation& a, Extension& ext) { if (resizable_) { Coord size = a.allotment(dimension_).span(); if (!Math::equal(size, span_, float(1e-4))) { span_ = size; GlyphIndex break_count = breaks_->count(); for (GlyphIndex b = 0; b < break_count; b++) { breaks_->item_ref(b).valid(false); } damage(0, component_->count() - 1); repair(); } } MonoGlyph::allocate(c, a, ext); } void Composition::damage(GlyphIndex first, GlyphIndex last) { if (damaged_) { first_damage_ = Math::min(first_damage_, first); last_damage_ = Math::max(last_damage_, last); } else { first_damage_ = first; last_damage_ = last; damaged_ = true; } } boolean Composition::repair() { if (damaged_) { GlyphIndex component_count = component_->count(); CompositorIndex break_count = breaks_->count(); CompositorIndex forced = prev_forced_break(first_damage_, component_); GlyphIndex break_index = 0; for (;;) { if (break_index < break_count) { const Break& br = breaks_->item_ref(break_index); if (br.first_ <= forced && br.last_ < forced) { ++break_index; } else { break; } } else { break; } } while ((forced < (component_count - 1)) && (forced < last_damage_)) { GlyphIndex next = fill_arrays(forced + 1, dimension_, component_); GlyphIndex count = next - forced; GlyphIndex b; for (b = 0; b < count && (b+break_index) < break_count; ++b) { const Break& br = breaks_->item_ref(b + break_index); __spans[b] = span_ - br.begin_ - br.end_; } if (b < count) { __spans[b] = span_; } CompositorIndex compose_count = compositor_->compose( __natural, __stretch, __shrink, __penalties, count, __spans, break_count - break_index + 1, __breaks, count ); do_repair(forced + 1, break_index, __breaks, compose_count); forced = next; break_index += compose_count; break_count = breaks_->count(); } damaged_ = false; return false; } else { return true; } } void Composition::do_repair( GlyphIndex first_component, GlyphIndex first_break, CompositorIndex* breaks, CompositorIndex count ) { GlyphIndex break_index = first_break; Glyph* contents = body(); for (GlyphIndex i = 0; i < count; ++i) { Break b; if (break_index < breaks_->count()) { const Break& br = breaks_->item_ref(break_index); b.begin_ = br.begin_; b.end_ = br.end_; } else { b.begin_ = 0; b.end_ = 0; } b.first_ = first_component + ((i == 0) ? 0 : breaks[i-1] + 1); b.last_ = first_component + (breaks[i] - 1); boolean re_break = (break_index == breaks_->count()); if (!re_break) { const Break& br = breaks_->item_ref(break_index); re_break = ( !br.valid() || br.first_ != b.first_ || br.last_ != b.last_ ); } if (re_break) { while ( break_index < breaks_->count() - 1 && breaks_->item_ref(break_index + 1).last_ <= b.last_ ) { contents->remove(break_index * 2 + 1); contents->remove(break_index * 2); breaks_->remove(break_index); } re_break = (break_index == breaks_->count()); if (!re_break) { const Break& br = breaks_->item_ref(break_index); re_break = ( (i < count-1 && br.last_ >= first_component + breaks[i+1] - 1) || (i == count-1 && br.first_ > b.last_ + 1) ); } if (re_break) { contents->insert(break_index * 2, make_item(b, view_all_)); contents->insert(break_index * 2 + 1, separator(b)); breaks_->insert(break_index, b); } else { Break& br = breaks_->item_ref(break_index); if (br.patch_ != nil) { br.patch_->redraw(); } contents->replace(break_index * 2, make_item(b, view_all_)); contents->replace(break_index * 2 + 1, separator(b)); br = b; } } ++break_index; } } void Composition::view(GlyphIndex first, GlyphIndex last) { GlyphIndex bc = breaks_->count(); Glyph* contents = body(); for (GlyphIndex b = 0; b < bc; ++b) { Break& br = breaks_->item_ref(b); if (br.last_ >= first && br.first_ <= last) { if (!br.viewed()) { contents->replace(b * 2, make_item(br, true)); } } else { if (br.viewed()) { contents->replace(b * 2, make_item(br, false)); } } } view_all_ = false; } Glyph* Composition::separator(Break& b) { if (b.last_ >= component_->count()-1) { return separator_; } else { Glyph* inbreak = component_->item_ref(b.last_ + 1).glyph_; if (inbreak != nil) { Glyph* g = inbreak->compose(in_break); if (g != nil) { return g; } else { return separator_; } } else { return separator_; } } } void Composition::margin(CompositorIndex item, Coord begin, Coord end) { CompositorIndex b = item/2; if (b < breaks_->count() && item%2 == 0) { Break& br = breaks_->item_ref(b); if (br.begin_ != begin || br.end_ != end) { br.begin_ = begin; br.end_ = end; br.valid(false); damage(br.first_ - 1, br.last_ + 1); } } } GlyphIndex Composition::beginning_of(CompositorIndex item) const { CompositorIndex i = Math::max(0L, Math::min(item/2, breaks_->count()-1)); return breaks_->item_ref(i).first_; } GlyphIndex Composition::end_of(CompositorIndex item) const { CompositorIndex i = Math::max(0L, Math::min(item/2, breaks_->count()-1)); return breaks_->item_ref(i).last_; } GlyphIndex Composition::count() const { return component_->count(); } Glyph* Composition::component(GlyphIndex index) const { return component_->item_ref(index).glyph_; } void Composition::change(GlyphIndex index) { CompositionComponent& component = component_->item_ref(index); Glyph* contents = body(); for (CompositorIndex b = item(index)/2; b < breaks_->count(); ++b) { Break& br = breaks_->item_ref(b); if (br.viewed() && index >= br.first_ && index <= br.last_+1) { contents->component(2 * b)->change(index - br.first_ + 2); contents->change(2 * b); } if (br.first_ > index) { break; } } } void Composition::append(Glyph* glyph) { insert(component_->count(), glyph); } void Composition::prepend(Glyph* glyph) { insert(0, glyph); } void Composition::insert(GlyphIndex index, Glyph* glyph) { if (glyph != nil) { glyph->ref(); } CompositionComponent component; component.glyph_ = glyph; component_->insert(index, component); Glyph* contents = body(); for (CompositorIndex b = item(index)/2; b < breaks_->count(); ++b) { Break& br = breaks_->item_ref(b); if (br.viewed() && index >= br.first_ && index <= br.last_+1) { br.valid(false); contents->component(2 * b)->insert(index - br.first_ + 2, nil); contents->change(2 * b); } if (index < br.first_) { ++br.first_; } if (index <= br.last_ + 1) { ++br.last_; } } damage(index - 1, index + 1); } void Composition::remove(GlyphIndex index) { CompositionComponent& component = component_->item_ref(index); if (component.glyph_ != nil) { component.glyph_->unref(); } component_->remove(index); Glyph* contents = body(); for (CompositorIndex b = item(index)/2; b < breaks_->count(); ++b) { Break& br = breaks_->item_ref(b); if (br.viewed() && index >= br.first_ && index <= br.last_+1) { br.valid(false); contents->component(2 * b)->remove(index - br.first_ + 2); contents->change(2 * b); } if (index < br.first_) { --br.first_; } if (index <= br.last_ + 1) { --br.last_; } } damage(index - 1, index); } void Composition::replace(GlyphIndex index, Glyph* glyph) { Requisition oldr, newr; if (glyph != nil) { glyph->request(newr); glyph->ref(); } CompositionComponent& component = component_->item_ref(index); if (component.glyph_ != nil) { component.glyph_->request(oldr); component.glyph_->unref(); } component.glyph_ = glyph; Glyph* contents = body(); for (CompositorIndex b = item(index)/2; b < breaks_->count(); ++b) { Break& br = breaks_->item_ref(b); if (br.viewed() && index >= br.first_-1 && index <= br.last_+1) { if (newr.equals(oldr, epsilon)) { Glyph* g = ( glyph == nil ? nil : index == br.first_-1 ? glyph->compose(post_break) : index == br.last_+1 ? glyph->compose(pre_break) : glyph->compose(no_break) ); contents->component(2 * b)->replace(index-br.first_+2, g); } else { br.valid(false); damage(index - 1, index + 1); } contents->change(2 * b); } if (br.first_ > index) { break; } } } CompositorIndex Composition::item(GlyphIndex index) const { CompositorIndex count = breaks_->count(); Composition* c = (Composition*)this; c->item_ = Math::min(Math::max(0L, item_), count-1); while (c->item_ < count-1 && c->breaks_->item_ref(item_).last_ < index) { c->item_ += 1; } while (c->item_ > 0 && c->breaks_->item_ref(item_).first_ > index) { c->item_ -= 1; } return Math::max(0L, c->item_) * 2; } void Composition::allotment( GlyphIndex i, DimensionName res, Allotment& a ) const { for (GlyphIndex b = item(i)/2; b < breaks_->count(); ++b) { Break& br = breaks_->item_ref(b); if (i >= br.first_ && i <= br.last_+1) { if (br.viewed()) { body()->component(2*b)->allotment(i - br.first_ + 2, res, a); } else { body()->allotment(2*b, res, a); } } } } /* * Translate a pick on the composition contents into what looks * like a pick on the elements. That is, if the composition * is a list of characters, it will be arranged into lines. * The pick on the lines will return , which needs * to be compressed into a single index for the composition. */ void Composition::pick( Canvas* c, const Allocation& allocation, int depth, Hit& h ) { long n = h.count(); MonoGlyph::pick(c, allocation, depth, h); if (h.count() > n) { /* * Retrieve the index of the line that was hit into "item". * Compositions create two glyphs per formatted line: * even-numbered elements refer to the lines and odd-numbered ones * refer to the separators between lines. We will compute * the element "index" from "item" and the break information. */ GlyphIndex item = h.index(depth); GlyphIndex b_count = breaks_->count(); GlyphIndex index; if (item % 2 == 0) { GlyphIndex b_index = item / 2; if (b_index < b_count) { /* * Hit on a line: calculate the character index by adding * the line's start to the offset into the line. */ const Break& br = breaks_->item_ref(b_index); index = br.first_ + h.index(depth+1) - 2; index = Math::max(br.first_, Math::min(br.last_ + 1, index)); } else { /* * This case only seems to happen when there is * a float at the end of the composition. */ index = breaks_->item_ref(b_count - 1).last_ + 1; } } else { GlyphIndex b_index = item / 2 + 1; if (b_index < b_count) { /* * Hit on the separator between lines: assume that * the hit refers to the beginning of the following line. */ index = breaks_->item_ref(b_index).first_; } else { /* * Hit on separator at the end of the composition: * return the end of the last line. */ index = breaks_->item_ref(b_index - 1).last_ + 1; } } /* * Remove the hit target for depth + 1, effectively * shifting information below it up one level. */ h.remove(depth + 1); /* * Finally, add the information for this level. */ h.retarget(depth, this, index); } } Glyph* Composition::make_item(Break& nb, boolean created) { LayoutKit* layout = LayoutKit::instance(); nb.valid(true); nb.viewed(created); if (created) { Glyph* g = make(nb); if (span_ > 0 && span_ < fil) { g = layout->fixed_span_dimension(g, dimension_, span_); } nb.patch_ = new Patch(g); return nb.patch_; } else { static GlyphIndex __req_count; static Requisition* __req; GlyphIndex count = nb.last_ - nb.first_ + 3; if (count >= __req_count) { delete __req; __req = new Requisition[count]; __req_count = count; } GlyphIndex index = 0; if (nb.first_ > 0) { Glyph* g = component_->item_ref(nb.first_ - 1).glyph_; if (g != nil) { g = g->compose(post_break); } if (g != nil) { g->request(__req[index]); ++index; } } for (GlyphIndex k = nb.first_; k <= nb.last_; ++k) { Glyph* g = component_->item_ref(k).glyph_; if (g != nil) { g = g->compose(no_break); } if (g != nil) { g->request(__req[index]); ++index; } } if (nb.last_ < component_->count()-1) { Glyph* g = component_->item_ref(nb.last_ + 1).glyph_; if (g != nil) { g = g->compose(pre_break); } if (g != nil) { g->request(__req[index]); ++index; } } Requisition r; DimensionName cross; if (dimension_ == Dimension_X) { if (span_ > 0 && span_ < fil) { Requirement require(span_); r.require(dimension_, require); } else { Tile tile(dimension_); tile.request(index, __req, r); } cross = Dimension_Y; } else { if (span_ > 0 && span_ < fil) { Requirement require(span_, 0, 0, 1.0); r.require(dimension_, require); } else { TileReversed tile(dimension_); tile.request(index, __req, r); } cross = Dimension_X; } Align align(cross); align.request(index, __req, r); nb.patch_ = nil; return layout->glue(r); } } Glyph* Composition::make(Break&) { return nil; } LRComposition::LRComposition( Glyph* context, Compositor* compositor, Glyph* separator, Coord width, Coord stretch, Coord shrink, GlyphIndex size ) : Composition( context, compositor, separator, Dimension_X, width, stretch, shrink, size ) { } LRComposition::~LRComposition() { } Glyph* LRComposition::make(Break& nb) { LayoutKit* layout = LayoutKit::instance(); Glyph* glyph = layout->hbox(nb.last_ - nb.first_ + 5); glyph->append(layout->hglue(nb.begin_, 0, 0, 0.0)); if (nb.first_ > 0) { Glyph* g = component(nb.first_ - 1); if (g != nil) { glyph->append(g->compose(post_break)); } else { glyph->append(nil); } } else { glyph->append(nil); } for (GlyphIndex k = nb.first_; k <= nb.last_; ++k) { Glyph* g = component(k); if (g != nil) { glyph->append(g->compose(no_break)); } else { glyph->append(nil); } } if (nb.last_ < count()-1) { Glyph* g = component(nb.last_ + 1); if (g != nil) { glyph->append(g->compose(pre_break)); } else { glyph->append(nil); } } else { glyph->append(nil); } glyph->append(layout->hglue(nb.end_, 0, 0, 1.0)); return glyph; } TBComposition::TBComposition( Glyph* context, Compositor* compositor, Glyph* separator, Coord height, Coord stretch, Coord shrink, GlyphIndex size ) : Composition( context, compositor, separator, Dimension_Y, height, stretch, shrink, size ) { } TBComposition::~TBComposition() { } Glyph* TBComposition::make(Break& nb) { LayoutKit* layout = LayoutKit::instance(); Glyph* glyph = layout->vbox(nb.last_ - nb.first_ + 5); glyph->append(layout->vglue(nb.begin_, 0, 0, 1.0)); if (nb.first_ > 0) { Glyph* g = component(nb.first_ - 1); if (g != nil) { glyph->append(g->compose(post_break)); } else { glyph->append(nil); } } else { glyph->append(nil); } for (GlyphIndex k = nb.first_; k <= nb.last_; ++k) { Glyph* g = component(k); if (g != nil) { glyph->append(g->compose(no_break)); } else { glyph->append(nil); } } if (nb.last_ < count()-1) { Glyph* g = component(nb.last_ + 1); if (g != nil) { glyph->append(g->compose(pre_break)); } else { glyph->append(nil); } } else { glyph->append(nil); } glyph->append(layout->vglue(nb.end_, 0, 0, 0.0)); return glyph; }