/* * Copyright (c) 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. */ /* * Slider - common slider behavior */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define default_minimum_thumb_size 28.0 class SliderImpl { private: friend class Slider; Glyph* normal_thumb_; Glyph* visible_thumb_; Patch* thumb_patch_; Patch* old_thumb_; Coord min_thumb_size_; boolean dragging_ : 1; boolean aborted_ : 1; boolean showing_old_thumb_ : 1; Stepper* forward_; Stepper* backward_; Stepper* stepper_; Coord xoffset_; Coord yoffset_; int hit_thumb(Slider*, const Event&); void get_position(Slider*, const Event&, Coord& x, Coord& y); }; Slider::Slider(Style* style) : ActiveHandler(nil, style) { SliderImpl* s = new SliderImpl; impl_ = s; s->normal_thumb_ = nil; s->visible_thumb_ = nil; s->thumb_patch_ = nil; s->old_thumb_ = nil; s->min_thumb_size_ = default_minimum_thumb_size; style->find_attribute("minimumThumbSize", s->min_thumb_size_); s->dragging_ = false; s->aborted_ = false; s->showing_old_thumb_ = false; s->forward_ = nil; s->backward_ = nil; s->stepper_ = nil; s->xoffset_ = 0.0; s->yoffset_ = 0.0; } Slider::~Slider() { SliderImpl* s = impl_; Resource::unref(s->normal_thumb_); Resource::unref(s->visible_thumb_); Resource::unref(s->old_thumb_); Resource::unref(s->thumb_patch_); Resource::unref(s->forward_); Resource::unref(s->backward_); delete s; } void Slider::normal_thumb(Glyph* g) { SliderImpl& s = *impl_; Resource::ref(g); Resource::unref(s.normal_thumb_); s.normal_thumb_ = g; Resource::unref(s.thumb_patch_); s.thumb_patch_ = new Patch(g); Resource::ref(s.thumb_patch_); } void Slider::visible_thumb(Glyph* g) { SliderImpl& s = *impl_; Resource::ref(g); Resource::unref(s.visible_thumb_); s.visible_thumb_ = g; } void Slider::old_thumb(Glyph* g) { SliderImpl& s = *impl_; Patch* patch = new Patch(g); Resource::ref(patch); Resource::unref(s.old_thumb_); s.old_thumb_ = patch; } Coord Slider::minimum_thumb_size() const { return impl_->min_thumb_size_; } void Slider::request(Requisition& req) const { Requirement default_size(22.0, fil, 22.0, 0.0); req.require(Dimension_X, default_size); req.require(Dimension_Y, default_size); } void Slider::allocate(Canvas* c, const Allocation& a, Extension& ext) { ext.merge(c, a); ActiveHandler::allocate(c, a, ext); } void Slider::draw(Canvas* c, const Allocation& a) const { SliderImpl& s = *impl_; c->push_clipping(); c->clip_rect(a.left(), a.bottom(), a.right(), a.top()); if (s.showing_old_thumb_) { s.old_thumb_->draw(c, s.old_thumb_->allocation()); } s.thumb_patch_->draw(c, s.thumb_patch_->allocation()); c->pop_clipping(); } void Slider::undraw() { SliderImpl& s = *impl_; if (s.thumb_patch_ != nil) { s.thumb_patch_->undraw(); } if (s.old_thumb_ != nil) { s.old_thumb_->undraw(); } ActiveHandler::undraw(); } void Slider::move(const Event& e) { SliderImpl& s = *impl_; if (s.visible_thumb_ != nil) { Glyph* g = (s.hit_thumb(this, e) == 0) ? s.visible_thumb_ : s.normal_thumb_; Patch& thumb = *s.thumb_patch_; if (thumb.body() != g) { thumb.body(g); thumb.reallocate(); thumb.redraw(); } } ActiveHandler::move(e); } void Slider::press(const Event& e) { EventButton b = e.pointer_button(); if (b == Event::right) { return; } Coord x, y; SliderImpl& s = *impl_; s.get_position(this, e, x, y); const Allocation& slider = allocation(); const Allocation& a = s.thumb_patch_->allocation(); int rel = s.hit_thumb(this, e); if (rel == 0) { apply_adjustment(&Adjustable::begin_adjustment); s.xoffset_ = slider.left() + x - a.left(); s.yoffset_ = slider.bottom() + y - a.bottom(); s.dragging_ = true; } else if (b == Event::left) { s.stepper_ = (rel == 1) ? s.forward_ : s.backward_; if (s.stepper_ != nil) { s.stepper_->start_stepping(); } } else { /* middle ==> drag */ apply_adjustment(&Adjustable::begin_adjustment); s.dragging_ = true; s.xoffset_ = slider.left() + (a.right() - a.left()) / 2.0; s.yoffset_ = slider.bottom() + (a.top() - a.bottom()) / 2.0; move_to(x - s.xoffset_, y - s.yoffset_); move(e); } } void Slider::drag(const Event& e) { SliderImpl& s = *impl_; if (!s.aborted_ && s.dragging_) { if (!s.showing_old_thumb_ && s.old_thumb_ != nil) { s.showing_old_thumb_ = true; Patch* p = s.thumb_patch_; Canvas* c = canvas(); c->push_transform(); c->transformer(transformer()); Extension ext; ext.clear(); s.old_thumb_->allocate(c, p->allocation(), ext); c->pop_transform(); } Coord x, y; s.get_position(this, e, x, y); move_to(x - s.xoffset_, y - s.yoffset_); } } void Slider::release(const Event& e) { SliderImpl& s = *impl_; if (s.dragging_) { if (s.showing_old_thumb_) { s.showing_old_thumb_ = false; s.old_thumb_->redraw(); } s.dragging_ = false; if (s.aborted_) { s.aborted_ = false; return; } Coord x, y; s.get_position(this, e, x, y); move_to(x - s.xoffset_, y - s.yoffset_); redraw_thumb(); move(e); apply_adjustment(&Adjustable::commit_adjustment); } else if (s.stepper_ != nil) { s.stepper_->stop_stepping(); s.stepper_ = nil; move(e); } } void Slider::allocation_changed(Canvas*, const Allocation& a) { allocate_thumb(a); } void Slider::update(Observable*) { if (canvas() != nil) { allocate_thumb(allocation()); } } void Slider::forward_stepper(Stepper* stepper) { SliderImpl& s = *impl_; Resource::ref(stepper); Resource::unref(s.forward_); s.forward_ = stepper; } void Slider::backward_stepper(Stepper* stepper) { SliderImpl& s = *impl_; Resource::ref(stepper); Resource::unref(s.backward_); s.backward_ = stepper; } void Slider::move_to(Coord, Coord) { } void Slider::allocate_thumb(const Allocation&) { } void Slider::apply_adjustment(SliderAdjustment) { } void Slider::do_adjustment( Adjustable* a, SliderAdjustment s, DimensionName d ) { if (a != nil) { (a->*s)(d); } } void Slider::allot_thumb_major_axis( const Allocation& slider, DimensionName d, Adjustable* adj, Coord min_thumb_size, float& scale, Allotment& new_a ) { const Allotment& a = slider.allotment(d); Coord length = adj->length(d); Coord cur_length = adj->cur_length(d); Coord slider_size = a.span(); Coord thumb_size; Coord thumb_start; if (Math::equal(length, float(0.0), float(1e-3)) || Math::equal(length, cur_length, float(1e-3)) ) { thumb_size = slider_size; thumb_start = 0.0; scale = 1.0; } else { thumb_size = slider_size * cur_length / length; if (thumb_size > slider_size) { thumb_size = slider_size; thumb_start = 0.0; scale = 1.0; } else { if (thumb_size < min_thumb_size) { thumb_size = min_thumb_size; } scale = (slider_size - thumb_size) / (length - cur_length); thumb_start = scale * (adj->cur_lower(d) - adj->lower(d)); } } new_a.origin(a.begin() + thumb_start); new_a.span(thumb_size); new_a.alignment(0.0); } void Slider::allot_thumb_minor_axis(const Allotment& a, Allotment& new_a) { new_a.origin(a.begin()); new_a.span(a.span()); new_a.alignment(0.0); } void Slider::redraw_thumb() { impl_->thumb_patch_->redraw(); } void Slider::reallocate_thumb(const Allocation& a) { Patch& thumb = *impl_->thumb_patch_; Canvas* c = canvas(); c->push_transform(); c->transformer(transformer()); Extension ext; ext.clear(); thumb.allocate(c, a, ext); c->pop_transform(); thumb.redraw(); } /* class SliderImpl */ int SliderImpl::hit_thumb(Slider* s, const Event& event) { Coord x = event.pointer_x(); Coord y = event.pointer_y(); const Extension& e = thumb_patch_->extension(); if (x >= e.left() && x < e.right() && y >= e.bottom() && y < e.top()) { Canvas* c = s->canvas(); const Transformer& t = s->transformer(); Hit hit(&event); hit.transform(t); c->push_transform(); c->transformer(t); thumb_patch_->pick(c, thumb_patch_->allocation(), 0, hit); c->pop_transform(); return hit.any() ? 0 : 1; } if (x < e.left() || y < e.bottom()) { return -1; } return 1; } void SliderImpl::get_position(Slider* s, const Event& e, Coord& x, Coord& y) { x = e.pointer_x(); y = e.pointer_y(); s->transformer().inverse_transform(x, y); } /* class XSlider */ XSlider::XSlider(Style* s, Adjustable* a) : Slider(s) { adjustable_ = a; a->attach(Dimension_X, this); TelltaleState* t = new TelltaleState; forward_stepper(new ForwardPager(nil, s, t, a, Dimension_X)); backward_stepper(new BackwardPager(nil, s, t, a, Dimension_X)); } XSlider::~XSlider() { if (adjustable_ != nil) { adjustable_->detach(Dimension_X, this); } } void XSlider::move_to(Coord x, Coord) { Adjustable* a = adjustable_; a->scroll_to(Dimension_X, a->lower(Dimension_X) + x / xscale_); } void XSlider::allocate_thumb(const Allocation& a) { redraw_thumb(); Allocation thumb_a; allot_thumb_major_axis( a, Dimension_X, adjustable_, minimum_thumb_size(), xscale_, thumb_a.x_allotment() ); allot_thumb_minor_axis(a.y_allotment(), thumb_a.y_allotment()); reallocate_thumb(thumb_a); } void XSlider::disconnect(Observable*) { adjustable_ = nil; } void XSlider::apply_adjustment(SliderAdjustment s) { do_adjustment(adjustable_, s, Dimension_X); } /* class YSlider */ YSlider::YSlider(Style* s, Adjustable* a) : Slider(s) { adjustable_ = a; a->attach(Dimension_Y, this); TelltaleState* t = new TelltaleState; forward_stepper(new ForwardPager(nil, s, t, a, Dimension_Y)); backward_stepper(new BackwardPager(nil, s, t, a, Dimension_Y)); } YSlider::~YSlider() { if (adjustable_ != nil) { adjustable_->detach(Dimension_Y, this); } } void YSlider::move_to(Coord, Coord y) { Adjustable* a = adjustable_; a->scroll_to(Dimension_Y, a->lower(Dimension_Y) + y / yscale_); } void YSlider::allocate_thumb(const Allocation& a) { redraw_thumb(); Allocation thumb_a; allot_thumb_major_axis( a, Dimension_Y, adjustable_, minimum_thumb_size(), yscale_, thumb_a.y_allotment() ); allot_thumb_minor_axis(a.x_allotment(), thumb_a.x_allotment()); reallocate_thumb(thumb_a); } void YSlider::disconnect(Observable*) { adjustable_ = nil; } void YSlider::apply_adjustment(SliderAdjustment s) { do_adjustment(adjustable_, s, Dimension_Y); } /* class XYSlider */ XYSlider::XYSlider(Style* s, Adjustable* x, Adjustable* y) : Slider(s) { x_adjustable_ = x; y_adjustable_ = y; x->attach(Dimension_X, this); y->attach(Dimension_Y, this); } XYSlider::~XYSlider() { if (x_adjustable_ != nil) { x_adjustable_->detach(Dimension_X, this); } if (y_adjustable_ != nil) { y_adjustable_->detach(Dimension_Y, this); } } void XYSlider::move_to(Coord x, Coord y) { Adjustable* x_adjust = x_adjustable_; Adjustable* y_adjust = y_adjustable_; x_adjust->scroll_to( Dimension_X, x_adjust->lower(Dimension_X) + x / xscale_ ); y_adjust->scroll_to( Dimension_Y, y_adjust->lower(Dimension_Y) + y / yscale_ ); } void XYSlider::allocate_thumb(const Allocation& a) { redraw_thumb(); Allocation thumb_a; allot_thumb_major_axis( a, Dimension_X, x_adjustable_, minimum_thumb_size(), xscale_, thumb_a.x_allotment() ); allot_thumb_major_axis( a, Dimension_Y, y_adjustable_, minimum_thumb_size(), yscale_, thumb_a.y_allotment() ); reallocate_thumb(thumb_a); } void XYSlider::disconnect(Observable*) { x_adjustable_ = nil; y_adjustable_ = nil; } void XYSlider::apply_adjustment(SliderAdjustment s) { do_adjustment(x_adjustable_, s, Dimension_X); do_adjustment(y_adjustable_, s, Dimension_Y); }