1/*
2 * Copyright 2001-2009, Ingo Weinhold <ingo_weinhold@gmx.de>
3 * All rights reserved. Distributed under the terms of the MIT license.
4 */
5
6#include "Scrollable.h"
7
8#include <algorithm>
9#include <stdio.h>
10
11#include "Scroller.h"
12
13// constructor
14Scrollable::Scrollable()
15	: fDataRect(0.0, 0.0, 0.0, 0.0),
16	  fScrollOffset(0.0, 0.0),
17	  fVisibleWidth(0),
18	  fVisibleHeight(0),
19	  fScrollSource(NULL)
20{
21}
22
23// destructor
24Scrollable::~Scrollable()
25{
26	if (fScrollSource)
27		fScrollSource->SetScrollTarget(NULL);
28}
29
30// SetScrollSource
31//
32// Sets a new scroll source. Notifies the old and the new source
33// of the change if necessary .
34void
35Scrollable::SetScrollSource(Scroller* source)
36{
37	Scroller* oldSource = fScrollSource;
38	if (oldSource != source) {
39		fScrollSource = NULL;
40		// Notify the old source, if it doesn't know about the change.
41		if (oldSource && oldSource->ScrollTarget() == this)
42			oldSource->SetScrollTarget(NULL);
43		fScrollSource = source;
44		// Notify the new source, if it doesn't know about the change.
45		if (source && source->ScrollTarget() != this)
46			source->SetScrollTarget(this);
47		// Notify ourselves.
48		ScrollSourceChanged(oldSource, fScrollSource);
49	}
50}
51
52// ScrollSource
53//
54// Returns the current scroll source. May be NULL, if we don't have any.
55Scroller*
56Scrollable::ScrollSource() const
57{
58	return fScrollSource;
59}
60
61// SetDataRect
62//
63// Sets the data rect.
64void
65Scrollable::SetDataRect(BRect dataRect, bool validateScrollOffset)
66{
67	if (fDataRect != dataRect && dataRect.IsValid()) {
68		BRect oldDataRect = fDataRect;
69		fDataRect = dataRect;
70		// notify ourselves
71		DataRectChanged(oldDataRect, fDataRect);
72		// notify scroller
73		if (fScrollSource)
74			fScrollSource->DataRectChanged(oldDataRect, fDataRect);
75		// adjust the scroll offset, if necessary
76		if (validateScrollOffset) {
77			BPoint offset = ValidScrollOffsetFor(fScrollOffset);
78			if (offset != fScrollOffset)
79				SetScrollOffset(offset);
80		}
81	}
82}
83
84// DataRect
85//
86// Returns the current data rect.
87BRect
88Scrollable::DataRect() const
89{
90	return fDataRect;
91}
92
93// SetScrollOffset
94//
95// Sets the scroll offset.
96void
97Scrollable::SetScrollOffset(BPoint offset)
98{
99	// adjust the supplied offset to be valid
100	offset = ValidScrollOffsetFor(offset);
101	if (fScrollOffset != offset) {
102		BPoint oldOffset = fScrollOffset;
103		fScrollOffset = offset;
104		// notify ourselves
105		ScrollOffsetChanged(oldOffset, fScrollOffset);
106		// notify scroller
107		if (fScrollSource)
108			fScrollSource->ScrollOffsetChanged(oldOffset, fScrollOffset);
109	}
110}
111
112// ScrollOffset
113//
114// Returns the current scroll offset.
115BPoint
116Scrollable::ScrollOffset() const
117{
118	return fScrollOffset;
119}
120
121// SetDataRect
122//
123// Sets the data rect.
124void
125Scrollable::SetDataRectAndScrollOffset(BRect dataRect, BPoint offset)
126{
127	if (fDataRect != dataRect && dataRect.IsValid()) {
128
129		BRect oldDataRect = fDataRect;
130		fDataRect = dataRect;
131		// notify ourselves
132		DataRectChanged(oldDataRect, fDataRect);
133		// notify scroller
134		if (fScrollSource) {
135			fScrollSource->SetScrollingEnabled(false);
136			fScrollSource->DataRectChanged(oldDataRect, fDataRect);
137		}
138		// adjust the scroll offset, if necessary
139		offset = ValidScrollOffsetFor(offset);
140		if (offset != fScrollOffset)
141			SetScrollOffset(offset);
142
143		if (fScrollSource)
144			fScrollSource->SetScrollingEnabled(true);
145	}
146}
147
148// ValidScrollOffsetFor
149//
150// Returns the valid scroll offset next to the supplied offset.
151BPoint
152Scrollable::ValidScrollOffsetFor(BPoint offset) const
153{
154	return ValidScrollOffsetFor(offset, fDataRect);
155}
156
157// ValidScrollOffsetFor
158//
159// Returns the valid scroll offset next to the supplied offset.
160BPoint
161Scrollable::ValidScrollOffsetFor(BPoint offset, const BRect& dataRect) const
162{
163	float maxX = max_c(dataRect.left, dataRect.right - fVisibleWidth);
164	float maxY = max_c(dataRect.top, dataRect.bottom - fVisibleHeight);
165	// adjust the offset to be valid
166	if (offset.x < dataRect.left)
167		offset.x = dataRect.left;
168	else if (offset.x > maxX)
169		offset.x = maxX;
170	if (offset.y < dataRect.top)
171		offset.y = dataRect.top;
172	else if (offset.y > maxY)
173		offset.y = maxY;
174	return offset;
175}
176
177// SetVisibleSize
178//
179// Sets the visible size.
180void
181Scrollable::SetVisibleSize(float width, float height)
182{
183	if ((fVisibleWidth != width || fVisibleHeight != height) &&
184		width >= 0 && height >= 0) {
185		float oldWidth = fVisibleWidth;
186		float oldHeight = fVisibleHeight;
187		fVisibleWidth = width;
188		fVisibleHeight = height;
189		// notify ourselves
190		VisibleSizeChanged(oldWidth, oldHeight, fVisibleWidth, fVisibleHeight);
191		// notify scroller
192		if (fScrollSource) {
193			fScrollSource->VisibleSizeChanged(oldWidth, oldHeight,
194											  fVisibleWidth, fVisibleHeight);
195		}
196		// adjust the scroll offset, if necessary
197		BPoint offset = ValidScrollOffsetFor(fScrollOffset);
198		if (offset != fScrollOffset)
199			SetScrollOffset(offset);
200	}
201}
202
203// VisibleBounds
204//
205// Returns the visible bounds, i.e. a rectangle of the visible size
206// located at (0.0, 0.0).
207BRect
208Scrollable::VisibleBounds() const
209{
210	return BRect(0.0, 0.0, fVisibleWidth, fVisibleHeight);
211}
212
213// VisibleRect
214//
215// Returns the visible rect, i.e. a rectangle of the visible size located
216// at the scroll offset.
217BRect
218Scrollable::VisibleRect() const
219{
220	BRect rect(0.0, 0.0, fVisibleWidth, fVisibleHeight);
221	rect.OffsetBy(fScrollOffset);
222	return rect;
223}
224
225// DataRectChanged
226//
227// Hook function. Implemented by derived classes to get notified when
228// the data rect has changed.
229void
230Scrollable::DataRectChanged(BRect /*oldDataRect*/, BRect /*newDataRect*/)
231{
232}
233
234// ScrollOffsetChanged
235//
236// Hook function. Implemented by derived classes to get notified when
237// the scroll offset has changed.
238void
239Scrollable::ScrollOffsetChanged(BPoint /*oldOffset*/, BPoint /*newOffset*/)
240{
241}
242
243// VisibleSizeChanged
244//
245// Hook function. Implemented by derived classes to get notified when
246// the visible size has changed.
247void
248Scrollable::VisibleSizeChanged(float /*oldWidth*/, float /*oldHeight*/,
249							   float /*newWidth*/, float /*newHeight*/)
250{
251}
252
253// ScrollSourceChanged
254//
255// Hook function. Implemented by derived classes to get notified when
256// the scroll source has changed.
257void
258Scrollable::ScrollSourceChanged(Scroller* /*oldSource*/,
259								Scroller* /*newSource*/)
260{
261}
262
263
264