xref: /haiku/src/apps/icon-o-matic/generic/gui/scrollview/ScrollView.cpp (revision 1acbe440b8dd798953bec31d18ee589aa3f71b73)
1 /*
2  * Copyright 2006, Haiku.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Stephan Aßmus <superstippi@gmx.de>
7  *		Ingo Weinhold <bonefish@cs.tu-berlin.de>
8  */
9 
10 #include "ScrollView.h"
11 
12 #include <algobase.h>
13 #include <stdio.h>
14 #include <string.h>
15 
16 #include <Bitmap.h>
17 #include <Message.h>
18 #include <ScrollBar.h>
19 #include <Window.h>
20 
21 #include "Scrollable.h"
22 #include "ScrollCornerBitmaps.h"
23 
24 
25 // InternalScrollBar
26 
27 class InternalScrollBar : public BScrollBar {
28  public:
29 								InternalScrollBar(ScrollView* scrollView,
30 												  BRect frame,
31 												  orientation posture);
32 	virtual						~InternalScrollBar();
33 
34 	virtual	void				ValueChanged(float value);
35 
36  private:
37 	ScrollView*					fScrollView;
38 };
39 
40 // constructor
41 InternalScrollBar::InternalScrollBar(ScrollView* scrollView, BRect frame,
42 									 orientation posture)
43 	: BScrollBar(frame, NULL, NULL, 0, 0, posture),
44 	  fScrollView(scrollView)
45 {
46 }
47 
48 // destructor
49 InternalScrollBar::~InternalScrollBar()
50 {
51 }
52 
53 // ValueChanged
54 void
55 InternalScrollBar::ValueChanged(float value)
56 {
57 	// Notify our parent scroll view. Note: the value already has changed,
58 	// so that we can't check, if it really has changed.
59 	if (fScrollView)
60 		fScrollView->_ScrollValueChanged(this, value);
61 }
62 
63 
64 
65 // ScrollCorner
66 
67 class ScrollCorner : public BView {
68  public:
69 								ScrollCorner(ScrollView* scrollView);
70 	virtual						~ScrollCorner();
71 
72 	virtual	void				MouseDown(BPoint point);
73 	virtual	void				MouseUp(BPoint point);
74 	virtual	void				MouseMoved(BPoint point, uint32 transit,
75 										   const BMessage* message);
76 
77 	virtual	void				Draw(BRect updateRect);
78 	virtual	void				WindowActivated(bool active);
79 
80 			void				SetActive(bool active);
81 	inline	bool				IsActive() const
82 									{ return fState & STATE_ACTIVE; }
83 
84  private:
85 			ScrollView*			fScrollView;
86 			uint32				fState;
87 			BPoint				fStartPoint;
88 			BPoint				fStartScrollOffset;
89 			BBitmap*			fBitmaps[3];
90 
91 	inline	bool				IsEnabled() const
92 									{ return ((fState & STATE_ENABLED) ==
93 											  STATE_ENABLED); }
94 
95 			void				SetDragging(bool dragging);
96 	inline	bool				IsDragging() const
97 									{ return (fState & STATE_DRAGGING); }
98 
99 	enum {
100 		STATE_DRAGGING			= 0x01,
101 		STATE_WINDOW_ACTIVE		= 0x02,
102 		STATE_ACTIVE			= 0x04,
103 		STATE_ENABLED			= STATE_WINDOW_ACTIVE | STATE_ACTIVE,
104 	};
105 };
106 
107 // constructor
108 ScrollCorner::ScrollCorner(ScrollView* scrollView)
109 	: BView(BRect(0.0, 0.0, B_V_SCROLL_BAR_WIDTH - 1.0f, B_H_SCROLL_BAR_HEIGHT - 1.0f), NULL,
110 			0, B_WILL_DRAW),
111 	  fScrollView(scrollView),
112 	  fState(0),
113 	  fStartPoint(0, 0),
114 	  fStartScrollOffset(0, 0)
115 {
116 //printf("ScrollCorner::ScrollCorner(%p)\n", scrollView);
117 	SetViewColor(B_TRANSPARENT_32_BIT);
118 //printf("setting up bitmap 0\n");
119 	fBitmaps[0] = new BBitmap(BRect(0.0f, 0.0f, sBitmapWidth, sBitmapHeight), sColorSpace);
120 //	fBitmaps[0]->SetBits((void *)sScrollCornerNormalBits, fBitmaps[0]->BitsLength(), 0L, sColorSpace);
121 	char *bits = (char *)fBitmaps[0]->Bits();
122 	int32 bpr = fBitmaps[0]->BytesPerRow();
123 	for (int i = 0; i <= sBitmapHeight; i++, bits += bpr)
124 		memcpy(bits, &sScrollCornerNormalBits[i * sBitmapHeight * 4], sBitmapWidth * 4);
125 
126 //printf("setting up bitmap 1\n");
127 	fBitmaps[1] = new BBitmap(BRect(0.0f, 0.0f, sBitmapWidth, sBitmapHeight), sColorSpace);
128 //	fBitmaps[1]->SetBits((void *)sScrollCornerPushedBits, fBitmaps[1]->BitsLength(), 0L, sColorSpace);
129 	bits = (char *)fBitmaps[1]->Bits();
130 	bpr = fBitmaps[1]->BytesPerRow();
131 	for (int i = 0; i <= sBitmapHeight; i++, bits += bpr)
132 		memcpy(bits, &sScrollCornerPushedBits[i * sBitmapHeight * 4], sBitmapWidth * 4);
133 
134 //printf("setting up bitmap 2\n");
135 	fBitmaps[2] = new BBitmap(BRect(0.0f, 0.0f, sBitmapWidth, sBitmapHeight), sColorSpace);
136 //	fBitmaps[2]->SetBits((void *)sScrollCornerDisabledBits, fBitmaps[2]->BitsLength(), 0L, sColorSpace);
137 	bits = (char *)fBitmaps[2]->Bits();
138 	bpr = fBitmaps[2]->BytesPerRow();
139 	for (int i = 0; i <= sBitmapHeight; i++, bits += bpr)
140 		memcpy(bits, &sScrollCornerDisabledBits[i * sBitmapHeight * 4], sBitmapWidth * 4);
141 }
142 
143 // destructor
144 ScrollCorner::~ScrollCorner()
145 {
146 	for (int i = 0; i < 3; i++)
147 		delete fBitmaps[i];
148 }
149 
150 // MouseDown
151 void
152 ScrollCorner::MouseDown(BPoint point)
153 {
154 	BView::MouseDown(point);
155 	uint32 buttons = 0;
156 	Window()->CurrentMessage()->FindInt32("buttons", (int32 *)&buttons);
157 	if (buttons & B_PRIMARY_MOUSE_BUTTON) {
158 		SetMouseEventMask(B_POINTER_EVENTS);
159 		if (fScrollView && IsEnabled() && Bounds().Contains(point)) {
160 			SetDragging(true);
161 			fStartPoint = point;
162 			fStartScrollOffset = fScrollView->ScrollOffset();
163 		}
164 	}
165 }
166 
167 // MouseUp
168 void
169 ScrollCorner::MouseUp(BPoint point)
170 {
171 	BView::MouseUp(point);
172 	uint32 buttons = 0;
173 	Window()->CurrentMessage()->FindInt32("buttons", (int32 *)&buttons);
174 	if (!(buttons & B_PRIMARY_MOUSE_BUTTON))
175 		SetDragging(false);
176 }
177 
178 // MouseMoved
179 void
180 ScrollCorner::MouseMoved(BPoint point, uint32 transit, const BMessage* message)
181 {
182 	BView::MouseMoved(point, transit, message);
183 	if (IsDragging()) {
184 		uint32 buttons = 0;
185 		Window()->CurrentMessage()->FindInt32("buttons", (int32 *)&buttons);
186 		// This is a work-around for a BeOS bug: We sometimes don't get a
187 		// MouseUp(), but fortunately it seems, that within the last
188 		// MouseMoved() the button is not longer pressed.
189 		if (buttons & B_PRIMARY_MOUSE_BUTTON) {
190 			BPoint diff = point - fStartPoint;
191 			if (fScrollView) {
192 				fScrollView->_ScrollCornerValueChanged(fStartScrollOffset
193 													   - diff);
194 //													   + diff);
195 			}
196 		} else
197 			SetDragging(false);
198 	}
199 }
200 
201 // Draw
202 void
203 ScrollCorner::Draw(BRect updateRect)
204 {
205 	if (IsEnabled()) {
206 		if (IsDragging())
207 			DrawBitmap(fBitmaps[1], BPoint(0.0f, 0.0f));
208 		else
209 			DrawBitmap(fBitmaps[0], BPoint(0.0f, 0.0f));
210 	}
211 	else
212 		DrawBitmap(fBitmaps[2], BPoint(0.0f, 0.0f));
213 }
214 
215 // WindowActivated
216 void
217 ScrollCorner::WindowActivated(bool active)
218 {
219 	if (active != (fState & STATE_WINDOW_ACTIVE)) {
220 		bool enabled = IsEnabled();
221 		if (active)
222 			fState |= STATE_WINDOW_ACTIVE;
223 		else
224 			fState &= ~STATE_WINDOW_ACTIVE;
225 		if (enabled != IsEnabled())
226 			Invalidate();
227 	}
228 }
229 
230 // SetActive
231 void
232 ScrollCorner::SetActive(bool active)
233 {
234 	if (active != IsActive()) {
235 		bool enabled = IsEnabled();
236 		if (active)
237 			fState |= STATE_ACTIVE;
238 		else
239 			fState &= ~STATE_ACTIVE;
240 		if (enabled != IsEnabled())
241 			Invalidate();
242 	}
243 }
244 
245 // SetDragging
246 void
247 ScrollCorner::SetDragging(bool dragging)
248 {
249 	if (dragging != IsDragging()) {
250 		if (dragging)
251 			fState |= STATE_DRAGGING;
252 		else
253 			fState &= ~STATE_DRAGGING;
254 		Invalidate();
255 	}
256 }
257 
258 
259 
260 // ScrollView
261 
262 // constructor
263 ScrollView::ScrollView(BView* child, uint32 scrollingFlags, BRect frame,
264 					   const char *name, uint32 resizingMode, uint32 flags)
265 	: BView(frame, name, resizingMode, flags | B_FRAME_EVENTS | B_WILL_DRAW
266 										| B_FULL_UPDATE_ON_RESIZE),
267 	  Scroller(),
268 	  fChild(NULL),
269 	  fScrollingFlags(scrollingFlags),
270 	  fHScrollBar(NULL),
271 	  fVScrollBar(NULL),
272 	  fScrollCorner(NULL),
273 	  fHVisible(true),
274 	  fVVisible(true),
275 	  fCornerVisible(true),
276 	  fWindowActive(false),
277 	  fChildFocused(false),
278 	  fHSmallStep(1),
279 	  fVSmallStep(1)
280 {
281 	// Set transparent view color -- our area is completely covered by
282 	// our children.
283 	SetViewColor(B_TRANSPARENT_32_BIT);
284 	// create scroll bars
285 	if (fScrollingFlags & (SCROLL_HORIZONTAL | SCROLL_HORIZONTAL_MAGIC)) {
286 		fHScrollBar = new InternalScrollBar(this,
287 				BRect(0.0, 0.0, 100.0, B_H_SCROLL_BAR_HEIGHT), B_HORIZONTAL);
288 		AddChild(fHScrollBar);
289 	}
290 	if (fScrollingFlags & (SCROLL_VERTICAL | SCROLL_VERTICAL_MAGIC)) {
291 		fVScrollBar = new InternalScrollBar(this,
292 				BRect(0.0, 0.0, B_V_SCROLL_BAR_WIDTH, 100.0), B_VERTICAL);
293 		AddChild(fVScrollBar);
294 	}
295 	// Create a scroll corner, if we can scroll into both direction.
296 	if (fHScrollBar && fVScrollBar) {
297 		fScrollCorner = new ScrollCorner(this);
298 		AddChild(fScrollCorner);
299 	}
300 	// add child
301 	if (child) {
302 		fChild = child;
303 		AddChild(child);
304 		if (Scrollable* scrollable = dynamic_cast<Scrollable*>(child))
305 			SetScrollTarget(scrollable);
306 	}
307 }
308 
309 // destructor
310 ScrollView::~ScrollView()
311 {
312 }
313 
314 // AllAttached
315 void
316 ScrollView::AllAttached()
317 {
318 	// do a first layout
319 	_Layout(_UpdateScrollBarVisibility());
320 }
321 
322 // Draw
323 void ScrollView::Draw(BRect updateRect)
324 {
325 	rgb_color keyboardFocus = keyboard_navigation_color();
326 	rgb_color light = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR),
327 								 B_LIGHTEN_MAX_TINT);
328 	rgb_color shadow = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR),
329 								  B_DARKEN_1_TINT);
330 	rgb_color darkShadow = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR),
331 								  B_DARKEN_2_TINT);
332 	rgb_color darkerShadow = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR),
333 								  B_DARKEN_3_TINT);
334 	float left = Bounds().left, right = Bounds().right;
335 	float top = Bounds().top, bottom = Bounds().bottom;
336 	if (fChildFocused && fWindowActive) {
337 		BeginLineArray(4);
338 		AddLine(BPoint(left, bottom),
339 				BPoint(left, top), keyboardFocus);
340 		AddLine(BPoint(left + 1.0, top),
341 				BPoint(right, top), keyboardFocus);
342 		AddLine(BPoint(right, top + 1.0),
343 				BPoint(right, bottom), keyboardFocus);
344 		AddLine(BPoint(right - 1.0, bottom),
345 				BPoint(left + 1.0, bottom), keyboardFocus);
346 		EndLineArray();
347 	} else {
348 		BeginLineArray(4);
349 		AddLine(BPoint(left, bottom),
350 				BPoint(left, top), shadow);
351 		AddLine(BPoint(left + 1.0, top),
352 				BPoint(right, top), shadow);
353 		AddLine(BPoint(right, top + 1.0),
354 				BPoint(right, bottom), light);
355 		AddLine(BPoint(right - 1.0, bottom),
356 				BPoint(left + 1.0, bottom), light);
357 		EndLineArray();
358 	}
359 	// The right and bottom lines will be hidden if the scroll views are
360 	// visible. But that doesn't harm.
361 	BRect innerRect(_InnerRect());
362 	left = innerRect.left;
363 	top = innerRect.top;
364 	right = innerRect.right;
365 	bottom = innerRect.bottom;
366 	BeginLineArray(4);
367 	AddLine(BPoint(left, bottom),
368 			BPoint(left, top), darkerShadow);
369 	AddLine(BPoint(left + 1.0, top),
370 			BPoint(right, top), darkShadow);
371 	AddLine(BPoint(right, top + 1.0),
372 			BPoint(right, bottom), darkShadow);
373 	AddLine(BPoint(right - 1.0, bottom),
374 			BPoint(left + 1.0, bottom), darkShadow);
375 	EndLineArray();
376 }
377 
378 // FrameResized
379 void
380 ScrollView::FrameResized(float width, float height)
381 {
382 	_Layout(0);
383 }
384 
385 // WindowActivated
386 void ScrollView::WindowActivated(bool activated)
387 {
388 	fWindowActive = activated;
389 	if (fChildFocused)
390 		Invalidate();
391 }
392 
393 // ScrollingFlags
394 uint32
395 ScrollView::ScrollingFlags() const
396 {
397 	return fScrollingFlags;
398 }
399 
400 // SetVisibleRectIsChildBounds
401 void
402 ScrollView::SetVisibleRectIsChildBounds(bool flag)
403 {
404 	if (flag != VisibleRectIsChildBounds()) {
405 		if (flag)
406 			fScrollingFlags |= SCROLL_VISIBLE_RECT_IS_CHILD_BOUNDS;
407 		else
408 			fScrollingFlags &= ~SCROLL_VISIBLE_RECT_IS_CHILD_BOUNDS;
409 		if (fChild && _UpdateScrollBarVisibility())
410 			_Layout(0);
411 	}
412 }
413 
414 // VisibleRectIsChildBounds
415 bool
416 ScrollView::VisibleRectIsChildBounds() const
417 {
418 	return (fScrollingFlags & SCROLL_VISIBLE_RECT_IS_CHILD_BOUNDS);
419 }
420 
421 // Child
422 BView*
423 ScrollView::Child() const
424 {
425 	return fChild;
426 }
427 
428 // ChildFocusChanged
429 //
430 // To be called by the scroll child, when its has got or lost the focus.
431 // We need this to know, when to draw the blue focus frame.
432 void
433 ScrollView::ChildFocusChanged(bool focused)
434 {
435 	if (fChildFocused != focused) {
436 		fChildFocused = focused;
437 		Invalidate();
438 	}
439 }
440 
441 // HScrollBar
442 BScrollBar*
443 ScrollView::HScrollBar() const
444 {
445 	return fHScrollBar;
446 }
447 
448 // VScrollBar
449 BScrollBar*
450 ScrollView::VScrollBar() const
451 {
452 	return fVScrollBar;
453 }
454 
455 // HVScrollCorner
456 BView*
457 ScrollView::HVScrollCorner() const
458 {
459 	return fScrollCorner;
460 }
461 
462 // SetHSmallStep
463 void
464 ScrollView::SetHSmallStep(float hStep)
465 {
466 	SetSmallSteps(hStep, fVSmallStep);
467 }
468 
469 // SetVSmallStep
470 void
471 ScrollView::SetVSmallStep(float vStep)
472 {
473 	SetSmallSteps(fHSmallStep, vStep);
474 }
475 
476 // SetSmallSteps
477 void
478 ScrollView::SetSmallSteps(float hStep, float vStep)
479 {
480 	if (fHSmallStep != hStep || fVSmallStep != vStep) {
481 		fHSmallStep = hStep;
482 		fVSmallStep = vStep;
483 		_UpdateScrollBars();
484 	}
485 }
486 
487 // GetSmallSteps
488 void
489 ScrollView::GetSmallSteps(float* hStep, float* vStep) const
490 {
491 	*hStep = fHSmallStep;
492 	*vStep = fVSmallStep;
493 }
494 
495 // HSmallStep
496 float
497 ScrollView::HSmallStep() const
498 {
499 	return fHSmallStep;
500 }
501 
502 // VSmallStep
503 float
504 ScrollView::VSmallStep() const
505 {
506 	return fVSmallStep;
507 }
508 
509 // DataRectChanged
510 void
511 ScrollView::DataRectChanged(BRect /*oldDataRect*/, BRect /*newDataRect*/)
512 {
513 	if (ScrollTarget()) {
514 		if (_UpdateScrollBarVisibility())
515 			_Layout(0);
516 		else
517 			_UpdateScrollBars();
518 	}
519 }
520 
521 // ScrollOffsetChanged
522 void
523 ScrollView::ScrollOffsetChanged(BPoint /*oldOffset*/, BPoint newOffset)
524 {
525 	if (fHScrollBar && fHScrollBar->Value() != newOffset.x)
526 		fHScrollBar->SetValue(newOffset.x);
527 	if (fVScrollBar && fVScrollBar->Value() != newOffset.y)
528 		fVScrollBar->SetValue(newOffset.y);
529 }
530 
531 // VisibleSizeChanged
532 void
533 ScrollView::VisibleSizeChanged(float /*oldWidth*/, float /*oldHeight*/,
534 							   float /*newWidth*/, float /*newHeight*/)
535 {
536 	if (ScrollTarget()) {
537 		if (_UpdateScrollBarVisibility())
538 			_Layout(0);
539 		else
540 			_UpdateScrollBars();
541 	}
542 }
543 
544 // ScrollTargetChanged
545 void
546 ScrollView::ScrollTargetChanged(Scrollable* /*oldTarget*/,
547 								Scrollable* newTarget)
548 {
549 /*	// remove the old child
550 	if (fChild)
551 		RemoveChild(fChild);
552 	// add the new child
553 	BView* view = dynamic_cast<BView*>(newTarget);
554 	fChild = view;
555 	if (view)
556 		AddChild(view);
557 	else if (newTarget)	// set the scroll target to NULL, if it isn't a BView
558 		SetScrollTarget(NULL);
559 */
560 }
561 
562 // _ScrollValueChanged
563 void
564 ScrollView::_ScrollValueChanged(InternalScrollBar* scrollBar, float value)
565 {
566 	switch (scrollBar->Orientation()) {
567 		case B_HORIZONTAL:
568 			if (fHScrollBar)
569 				SetScrollOffset(BPoint(value, ScrollOffset().y));
570 			break;
571 		case B_VERTICAL:
572 			if (fVScrollBar)
573 				SetScrollOffset(BPoint(ScrollOffset().x, value));
574 			break;
575 		default:
576 			break;
577 	}
578 }
579 
580 // _ScrollCornerValueChanged
581 void
582 ScrollView::_ScrollCornerValueChanged(BPoint offset)
583 {
584 	// The logic in Scrollable::SetScrollOffset() handles offsets, that
585 	// are out of range.
586 	SetScrollOffset(offset);
587 }
588 
589 // _Layout
590 //
591 // Relayouts all children (fChild, scroll bars).
592 // flags indicates which scrollbars' visibility has changed.
593 // May be overridden to do a custom layout -- the SCROLL_*_MAGIC must
594 // be disabled in this case, or strange things happen.
595 void
596 ScrollView::_Layout(uint32 flags)
597 {
598 	bool hbar = (fHScrollBar && fHVisible);
599 	bool vbar = (fVScrollBar && fVVisible);
600 	bool corner = (fScrollCorner && fCornerVisible);
601 	BRect childRect(_ChildRect());
602 	float innerWidth = childRect.Width();
603 	float innerHeight = childRect.Height();
604 	BPoint scrollLT(_InnerRect().LeftTop());
605 	BPoint scrollRB(childRect.RightBottom() + BPoint(1.0f, 1.0f));
606 	if (fScrollingFlags & SCROLL_NO_FRAME) {
607 		// cut off the top line and left line of the
608 		// scroll bars, otherwise they are used for the
609 		// frame appearance
610 		scrollLT.x--;
611 		scrollLT.y--;
612 	}
613 	// layout scroll bars and scroll corner
614 	if (corner) {
615 		// In this case the scrollbars overlap one pixel.
616 		fHScrollBar->MoveTo(scrollLT.x, scrollRB.y);
617 		fHScrollBar->ResizeTo(innerWidth + 2.0, B_H_SCROLL_BAR_HEIGHT);
618 		fVScrollBar->MoveTo(scrollRB.x, scrollLT.y);
619 		fVScrollBar->ResizeTo(B_V_SCROLL_BAR_WIDTH, innerHeight + 2.0);
620 		fScrollCorner->MoveTo(childRect.right + 2.0, childRect.bottom + 2.0);
621 	} else if (hbar) {
622 		fHScrollBar->MoveTo(scrollLT.x, scrollRB.y);
623 		fHScrollBar->ResizeTo(innerWidth + 2.0, B_H_SCROLL_BAR_HEIGHT);
624 	} else if (vbar) {
625 		fVScrollBar->MoveTo(scrollRB.x, scrollLT.y);
626 		fVScrollBar->ResizeTo(B_V_SCROLL_BAR_WIDTH, innerHeight + 2.0);
627 	}
628 	// layout child
629 	if (fChild) {
630 		fChild->MoveTo(childRect.LeftTop());
631 		fChild->ResizeTo(innerWidth, innerHeight);
632 		if (VisibleRectIsChildBounds())
633 			SetVisibleSize(innerWidth, innerHeight);
634 		// Due to a BeOS bug sometimes the area under a recently hidden
635 		// scroll bar isn't updated correctly.
636 		// We force this manually: The position of hidden scroll bar isn't
637 		// updated any longer, so we can't just invalidate it.
638 		if (fChild->Window()) {
639 			if (flags & SCROLL_HORIZONTAL && !fHVisible)
640 				fChild->Invalidate(fHScrollBar->Frame());
641 			if (flags & SCROLL_VERTICAL && !fVVisible)
642 				fChild->Invalidate(fVScrollBar->Frame());
643 		}
644 	}
645 }
646 
647 // _UpdateScrollBars
648 //
649 // Probably somewhat misnamed. This function updates the scroll bars'
650 // proportion, range attributes and step widths according to the scroll
651 // target's DataRect() and VisibleBounds(). May also be called, if there's
652 // no scroll target -- then the scroll bars are disabled.
653 void
654 ScrollView::_UpdateScrollBars()
655 {
656 	BRect dataRect = DataRect();
657 	BRect visibleBounds = VisibleBounds();
658 	if (!fScrollTarget) {
659 		dataRect.Set(0.0, 0.0, 0.0, 0.0);
660 		visibleBounds.Set(0.0, 0.0, 0.0, 0.0);
661 	}
662 	float hProportion = min(1.0f, (visibleBounds.Width() + 1.0f) /
663 								  (dataRect.Width() + 1.0f));
664 	float hMaxValue = max(dataRect.left,
665 						  dataRect.Width() - visibleBounds.Width());
666 	float vProportion = min(1.0f, (visibleBounds.Height() + 1.0f) /
667 								  (dataRect.Height() + 1.0f));
668 	float vMaxValue = max(dataRect.top,
669 						  dataRect.Height() - visibleBounds.Height());
670 	// update horizontal scroll bar
671 	if (fHScrollBar) {
672 		fHScrollBar->SetProportion(hProportion);
673 		fHScrollBar->SetRange(dataRect.left, hMaxValue);
674 		// This obviously ineffective line works around a BScrollBar bug:
675 		// As documented the scrollbar's value is adjusted, if the range
676 		// has been changed and it therefore falls out of the range. But if,
677 		// after resetting the range to what it has been before, the user
678 		// moves the scrollbar to the original value via one click
679 		// it is failed to invoke BScrollBar::ValueChanged().
680 		fHScrollBar->SetValue(fHScrollBar->Value());
681 		fHScrollBar->SetSteps(fHSmallStep, visibleBounds.Width());
682 	}
683 	// update vertical scroll bar
684 	if (fVScrollBar) {
685 		fVScrollBar->SetProportion(vProportion);
686 		fVScrollBar->SetRange(dataRect.top, vMaxValue);
687 		// This obviously ineffective line works around a BScrollBar bug.
688 		fVScrollBar->SetValue(fVScrollBar->Value());
689 		fVScrollBar->SetSteps(fVSmallStep, visibleBounds.Height());
690 	}
691 	// update scroll corner
692 	if (fScrollCorner) {
693 		fScrollCorner->SetActive(hProportion < 1.0f || vProportion < 1.0f);
694 	}
695 }
696 
697 // set_visible_state
698 //
699 // Convenience function: Sets a view's visibility state to /visible/.
700 // Returns true, if the state was actually changed, false otherwise.
701 // This function never calls Hide() on a hidden or Show() on a visible
702 // view. /view/ must be valid.
703 static inline
704 bool
705 set_visible_state(BView* view, bool visible, bool* currentlyVisible)
706 {
707 	bool changed = false;
708 	if (*currentlyVisible != visible) {
709 		if (visible)
710 			view->Show();
711 		else
712 			view->Hide();
713 		*currentlyVisible = visible;
714 		changed = true;
715 	}
716 	return changed;
717 }
718 
719 // _UpdateScrollBarVisibility
720 //
721 // Checks which of scroll bars need to be visible according to
722 // SCROLL_*_MAGIG and shows/hides them, if necessary.
723 // Returns a bitwise combination of SCROLL_HORIZONTAL and SCROLL_VERTICAL
724 // according to which scroll bar's visibility state has changed, 0 if none.
725 // A return value != 0 usually means that the layout isn't valid any longer.
726 uint32
727 ScrollView::_UpdateScrollBarVisibility()
728 {
729 	uint32 changed = 0;
730 	BRect childRect(_MaxVisibleRect());
731 	float width = childRect.Width();
732 	float height = childRect.Height();
733 	BRect dataRect = DataRect();			// Invalid if !ScrollTarget(),
734 	float dataWidth = dataRect.Width();		// but that doesn't harm.
735 	float dataHeight = dataRect.Height();	//
736 	bool hbar = (fScrollingFlags & SCROLL_HORIZONTAL_MAGIC);
737 	bool vbar = (fScrollingFlags & SCROLL_VERTICAL_MAGIC);
738 	if (!ScrollTarget()) {
739 		if (hbar) {
740 			if (set_visible_state(fHScrollBar, false, &fHVisible))
741 				changed |= SCROLL_HORIZONTAL;
742 		}
743 		if (vbar) {
744 			if (set_visible_state(fVScrollBar, false, &fVVisible))
745 				changed |= SCROLL_VERTICAL;
746 		}
747 	} else if (hbar && width >= dataWidth && vbar && height >= dataHeight) {
748 		// none
749 		if (set_visible_state(fHScrollBar, false, &fHVisible))
750 			changed |= SCROLL_HORIZONTAL;
751 		if (set_visible_state(fVScrollBar, false, &fVVisible))
752 			changed |= SCROLL_VERTICAL;
753 	} else {
754 		// The case, that both scroll bars are magic and invisible is catched,
755 		// so that while checking one bar we can suppose, that the other one
756 		// is visible (if it does exist at all).
757 		BRect innerRect(_GuessVisibleRect(fHScrollBar, fVScrollBar));
758 		float innerWidth = innerRect.Width();
759 		float innerHeight = innerRect.Height();
760 		// the horizontal one?
761 		if (hbar) {
762 			if (innerWidth >= dataWidth) {
763 				if (set_visible_state(fHScrollBar, false, &fHVisible))
764 					changed |= SCROLL_HORIZONTAL;
765 			} else {
766 				if (set_visible_state(fHScrollBar, true, &fHVisible))
767 					changed |= SCROLL_HORIZONTAL;
768 			}
769 		}
770 		// the vertical one?
771 		if (vbar) {
772 			if (innerHeight >= dataHeight) {
773 				if (set_visible_state(fVScrollBar, false, &fVVisible))
774 					changed |= SCROLL_VERTICAL;
775 			} else {
776 				if (set_visible_state(fVScrollBar, true, &fVVisible))
777 					changed |= SCROLL_VERTICAL;
778 			}
779 		}
780 	}
781 	// If anything has changed, update the scroll corner as well.
782 	if (changed && fScrollCorner)
783 		set_visible_state(fScrollCorner, fHVisible && fVVisible, &fCornerVisible);
784 	return changed;
785 }
786 
787 // _InnerRect
788 //
789 // Returns the rectangle that actually can be used for the child and the
790 // scroll bars, i.e. the view's Bounds() subtracted the space for the
791 // decorative frame.
792 BRect
793 ScrollView::_InnerRect() const
794 {
795 	if (fScrollingFlags & SCROLL_NO_FRAME)
796 		return Bounds();
797 	return Bounds().InsetBySelf(1.0f, 1.0f);
798 }
799 
800 // _ChildRect
801 //
802 // Returns the rectangle, that should be the current child frame.
803 // `should' because 1. we might not have a child at all or 2. a
804 // relayout is pending.
805 BRect
806 ScrollView::_ChildRect() const
807 {
808 	return _ChildRect(fHScrollBar && fHVisible, fVScrollBar && fVVisible);
809 }
810 
811 // _ChildRect
812 //
813 // The same as _ChildRect() with the exception that not the current
814 // scroll bar visibility, but a fictitious one given by /hbar/ and /vbar/
815 // is considered.
816 BRect
817 ScrollView::_ChildRect(bool hbar, bool vbar) const
818 {
819 	BRect rect(_InnerRect());
820 	float frameWidth = (fScrollingFlags & SCROLL_NO_FRAME) ? 0.0 : 1.0;
821 
822 	if (hbar)
823 		rect.bottom -= B_H_SCROLL_BAR_HEIGHT + frameWidth;
824 	else
825 		rect.bottom -= frameWidth;
826 	if (vbar)
827 		rect.right -= B_V_SCROLL_BAR_WIDTH + frameWidth;
828 	else
829 		rect.right -= frameWidth;
830 	rect.top += frameWidth;
831 	rect.left += frameWidth;
832 	return rect;
833 }
834 
835 // _GuessVisibleRect
836 //
837 // Returns an approximation of the visible rect for the
838 // fictitious scroll bar visibility given by /hbar/ and /vbar/.
839 // In the case !VisibleRectIsChildBounds() it is simply the current
840 // visible rect.
841 BRect
842 ScrollView::_GuessVisibleRect(bool hbar, bool vbar) const
843 {
844 	if (VisibleRectIsChildBounds())
845 		return _ChildRect(hbar, vbar).OffsetToCopy(ScrollOffset());
846 	return VisibleRect();
847 }
848 
849 // _MaxVisibleRect
850 //
851 // Returns the maximal possible visible rect in the current situation, that
852 // is depending on if the visible rect is the child's bounds either the
853 // rectangle the child covers when both scroll bars are hidden (offset to
854 // the scroll offset) or the current visible rect.
855 BRect
856 ScrollView::_MaxVisibleRect() const
857 {
858 	return _GuessVisibleRect(true, true);
859 }
860 
861 
862