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