xref: /haiku/src/kits/interface/ScrollView.cpp (revision c0936b5a0384bc6fe654d296ee54222a0f45d2b6)
1 /*
2  * Copyright 2004-2015, Axel Dörfler, axeld@pinc-software.de.
3  * Copyright 2009 Stephan Aßmus, superstippi@gmx.de.
4  * Copyright 2014-2015 Haiku, Inc. All rights reserved.
5  * Distributed under the terms of the MIT License.
6  *
7  * Authors:
8  *		Stephan Aßmus, superstippi@gmx.de
9  *		Axel Dörfler, axeld@pinc-software.de
10  *		John Scipione, jscpione@gmail.com
11  */
12 
13 
14 #include <ScrollView.h>
15 
16 #include <ControlLook.h>
17 #include <LayoutUtils.h>
18 #include <Message.h>
19 #include <Region.h>
20 #include <Window.h>
21 
22 #include <binary_compatibility/Interface.h>
23 
24 
25 static const float kFancyBorderSize = 2;
26 static const float kPlainBorderSize = 1;
27 
28 
29 BScrollView::BScrollView(const char* name, BView* target, uint32 resizingMode,
30 	uint32 flags, bool horizontal, bool vertical, border_style border)
31 	:
32 	BView(_ComputeFrame(target, horizontal, vertical, border,
33 		BControlLook::B_ALL_BORDERS), name, resizingMode,
34 		_ModifyFlags(flags, border)),
35 	fTarget(target),
36 	fBorder(border)
37 {
38 	_Init(horizontal, vertical);
39 }
40 
41 
42 BScrollView::BScrollView(const char* name, BView* target, uint32 flags,
43 	bool horizontal, bool vertical, border_style border)
44 	:
45 	BView(name, _ModifyFlags(flags, border)),
46 	fTarget(target),
47 	fBorder(border)
48 {
49 	_Init(horizontal, vertical);
50 }
51 
52 
53 BScrollView::BScrollView(BMessage* archive)
54 	:
55 	BView(archive),
56 	fHighlighted(false)
57 {
58 	int32 border;
59 	fBorder = archive->FindInt32("_style", &border) == B_OK ?
60 		(border_style)border : B_FANCY_BORDER;
61 
62 	// in a shallow archive, we may not have a target anymore. We must
63 	// be prepared for this case
64 
65 	// don't confuse our scroll bars with our (eventual) target
66 	int32 firstBar = 0;
67 	if (!archive->FindBool("_no_target_")) {
68 		fTarget = ChildAt(0);
69 		firstBar++;
70 	} else
71 		fTarget = NULL;
72 
73 	// search for our scroll bars
74 	// This will not work for managed archives (when the layout kit is used).
75 	// In that case the children are attached later, and we perform the search
76 	// again in the AllUnarchived method.
77 
78 	fHorizontalScrollBar = NULL;
79 	fVerticalScrollBar = NULL;
80 
81 	BView* view;
82 	while ((view = ChildAt(firstBar++)) != NULL) {
83 		BScrollBar *bar = dynamic_cast<BScrollBar *>(view);
84 		if (bar == NULL)
85 			continue;
86 
87 		if (bar->Orientation() == B_HORIZONTAL)
88 			fHorizontalScrollBar = bar;
89 		else if (bar->Orientation() == B_VERTICAL)
90 			fVerticalScrollBar = bar;
91 	}
92 
93 	fPreviousWidth = uint16(Bounds().Width());
94 	fPreviousHeight = uint16(Bounds().Height());
95 
96 }
97 
98 
99 BScrollView::~BScrollView()
100 {
101 }
102 
103 
104 // #pragma mark - Archiving
105 
106 
107 BArchivable*
108 BScrollView::Instantiate(BMessage* archive)
109 {
110 	if (validate_instantiation(archive, "BScrollView"))
111 		return new BScrollView(archive);
112 
113 	return NULL;
114 }
115 
116 
117 status_t
118 BScrollView::Archive(BMessage* archive, bool deep) const
119 {
120 	status_t status = BView::Archive(archive, deep);
121 	if (status != B_OK)
122 		return status;
123 
124 	// If this is a deep archive, the BView class will take care
125 	// of our children.
126 
127 	if (status == B_OK && fBorder != B_FANCY_BORDER)
128 		status = archive->AddInt32("_style", fBorder);
129 	if (status == B_OK && fTarget == NULL)
130 		status = archive->AddBool("_no_target_", true);
131 
132 	// The highlighted state is not archived, but since it is
133 	// usually (or should be) used to indicate focus, this
134 	// is probably the right thing to do.
135 
136 	return status;
137 }
138 
139 
140 status_t
141 BScrollView::AllUnarchived(const BMessage* archive)
142 {
143 	status_t result = BView::AllUnarchived(archive);
144 	if (result != B_OK)
145 		return result;
146 
147 	// search for our scroll bars and target
148 	int32 firstBar = 0;
149 	BView* view;
150 	while ((view = ChildAt(firstBar++)) != NULL) {
151 		BScrollBar *bar = dynamic_cast<BScrollBar *>(view);
152 		// We assume that the first non-scrollbar child view is the target.
153 		// So the target view can't be a BScrollBar, but who would do that?
154 		if (bar == NULL) {
155 			// in a shallow archive, we may not have a target anymore. We must
156 			// be prepared for this case
157 			if (fTarget == NULL && !archive->FindBool("_no_target_"))
158 				fTarget = view;
159 			continue;
160 		}
161 
162 		if (bar->Orientation() == B_HORIZONTAL)
163 			fHorizontalScrollBar = bar;
164 		else if (bar->Orientation() == B_VERTICAL)
165 			fVerticalScrollBar = bar;
166 	}
167 
168 	// Now connect the bars to the target, and make the target aware of them
169 	if (fHorizontalScrollBar)
170 		fHorizontalScrollBar->SetTarget(fTarget);
171 	if (fVerticalScrollBar)
172 		fVerticalScrollBar->SetTarget(fTarget);
173 
174 	if (fTarget)
175 		fTarget->TargetedByScrollView(this);
176 
177 	fPreviousWidth = uint16(Bounds().Width());
178 	fPreviousHeight = uint16(Bounds().Height());
179 
180 	return B_OK;
181 }
182 
183 
184 // #pragma mark - Hook methods
185 
186 
187 void
188 BScrollView::AttachedToWindow()
189 {
190 	BView::AttachedToWindow();
191 
192 	if ((fHorizontalScrollBar == NULL && fVerticalScrollBar == NULL)
193 		|| (fHorizontalScrollBar != NULL && fVerticalScrollBar != NULL)
194 		|| Window()->Look() != B_DOCUMENT_WINDOW_LOOK) {
195 		return;
196 	}
197 
198 	// If we have only one bar, we need to check if we are in the
199 	// bottom right edge of a window with the B_DOCUMENT_LOOK to
200 	// adjust the size of the bar to acknowledge the resize knob.
201 
202 	BRect bounds = ConvertToScreen(Bounds());
203 	BRect windowBounds = Window()->Frame();
204 
205 	if (bounds.right - _BorderSize() != windowBounds.right
206 		|| bounds.bottom - _BorderSize() != windowBounds.bottom) {
207 		return;
208 	}
209 
210 	if (fHorizontalScrollBar != NULL)
211 		fHorizontalScrollBar->ResizeBy(-B_V_SCROLL_BAR_WIDTH, 0);
212 	else if (fVerticalScrollBar != NULL)
213 		fVerticalScrollBar->ResizeBy(0, -B_H_SCROLL_BAR_HEIGHT);
214 }
215 
216 
217 void
218 BScrollView::DetachedFromWindow()
219 {
220 	BView::DetachedFromWindow();
221 }
222 
223 
224 void
225 BScrollView::AllAttached()
226 {
227 	BView::AllAttached();
228 }
229 
230 
231 void
232 BScrollView::AllDetached()
233 {
234 	BView::AllDetached();
235 }
236 
237 
238 void
239 BScrollView::Draw(BRect updateRect)
240 {
241 	uint32 flags = 0;
242 	if (fHighlighted && Window()->IsActive())
243 		flags |= BControlLook::B_FOCUSED;
244 
245 	BRect rect(Bounds());
246 	rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR);
247 
248 	BRect verticalScrollBarFrame(0, 0, -1, -1);
249 	if (fVerticalScrollBar)
250 		verticalScrollBarFrame = fVerticalScrollBar->Frame();
251 
252 	BRect horizontalScrollBarFrame(0, 0, -1, -1);
253 	if (fHorizontalScrollBar)
254 		horizontalScrollBarFrame = fHorizontalScrollBar->Frame();
255 
256 	be_control_look->DrawScrollViewFrame(this, rect, updateRect,
257 		verticalScrollBarFrame, horizontalScrollBarFrame, base, fBorder,
258 		flags, fBorders);
259 }
260 
261 
262 void
263 BScrollView::FrameMoved(BPoint newPosition)
264 {
265 	BView::FrameMoved(newPosition);
266 }
267 
268 
269 void
270 BScrollView::FrameResized(float newWidth, float newHeight)
271 {
272 	BView::FrameResized(newWidth, newHeight);
273 
274 	if (fBorder == B_NO_BORDER)
275 		return;
276 
277 	BRect bounds = Bounds();
278 	float border = _BorderSize() - 1;
279 
280 	if (fHorizontalScrollBar != NULL && fVerticalScrollBar != NULL) {
281 		BRect scrollCorner(bounds);
282 		scrollCorner.left = min_c(
283 			fPreviousWidth - fVerticalScrollBar->Frame().Height(),
284 			fHorizontalScrollBar->Frame().right + 1);
285 		scrollCorner.top = min_c(
286 			fPreviousHeight - fHorizontalScrollBar->Frame().Width(),
287 			fVerticalScrollBar->Frame().bottom + 1);
288 		Invalidate(scrollCorner);
289 	}
290 
291 	// changes in newWidth
292 
293 	if (bounds.Width() > fPreviousWidth) {
294 		// invalidate the region between the old and the new right border
295 		BRect rect = bounds;
296 		rect.left += fPreviousWidth - border;
297 		rect.right--;
298 		Invalidate(rect);
299 	} else if (bounds.Width() < fPreviousWidth) {
300 		// invalidate the region of the new right border
301 		BRect rect = bounds;
302 		rect.left = rect.right - border;
303 		Invalidate(rect);
304 	}
305 
306 	// changes in newHeight
307 
308 	if (bounds.Height() > fPreviousHeight) {
309 		// invalidate the region between the old and the new bottom border
310 		BRect rect = bounds;
311 		rect.top += fPreviousHeight - border;
312 		rect.bottom--;
313 		Invalidate(rect);
314 	} else if (bounds.Height() < fPreviousHeight) {
315 		// invalidate the region of the new bottom border
316 		BRect rect = bounds;
317 		rect.top = rect.bottom - border;
318 		Invalidate(rect);
319 	}
320 
321 	fPreviousWidth = uint16(bounds.Width());
322 	fPreviousHeight = uint16(bounds.Height());
323 }
324 
325 
326 void
327 BScrollView::MessageReceived(BMessage* message)
328 {
329 	BView::MessageReceived(message);
330 }
331 
332 
333 void
334 BScrollView::MouseDown(BPoint where)
335 {
336 	BView::MouseDown(where);
337 }
338 
339 
340 void
341 BScrollView::MouseMoved(BPoint where, uint32 code,
342 	const BMessage* dragMessage)
343 {
344 	BView::MouseMoved(where, code, dragMessage);
345 }
346 
347 
348 void
349 BScrollView::MouseUp(BPoint where)
350 {
351 	BView::MouseUp(where);
352 }
353 
354 
355 void
356 BScrollView::WindowActivated(bool active)
357 {
358 	if (fHighlighted)
359 		Invalidate();
360 
361 	BView::WindowActivated(active);
362 }
363 
364 
365 // #pragma mark - Size methods
366 
367 
368 void
369 BScrollView::GetPreferredSize(float* _width, float* _height)
370 {
371 	BSize size = PreferredSize();
372 
373 	if (_width)
374 		*_width = size.width;
375 
376 	if (_height)
377 		*_height = size.height;
378 }
379 
380 
381 void
382 BScrollView::ResizeToPreferred()
383 {
384 	if (Window() == NULL)
385 		return;
386 	BView::ResizeToPreferred();
387 }
388 
389 
390 void
391 BScrollView::MakeFocus(bool focus)
392 {
393 	BView::MakeFocus(focus);
394 }
395 
396 
397 BSize
398 BScrollView::MinSize()
399 {
400 	BSize size = _ComputeSize(fTarget != NULL ? fTarget->MinSize()
401 		: BSize(16, 16));
402 
403 	return BLayoutUtils::ComposeSize(ExplicitMinSize(), size);
404 }
405 
406 
407 BSize
408 BScrollView::MaxSize()
409 {
410 	BSize size = _ComputeSize(fTarget != NULL ? fTarget->MaxSize()
411 		: BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED));
412 
413 	return BLayoutUtils::ComposeSize(ExplicitMaxSize(), size);
414 }
415 
416 
417 BSize
418 BScrollView::PreferredSize()
419 {
420 	BSize size = _ComputeSize(fTarget != NULL ? fTarget->PreferredSize()
421 		: BSize(32, 32));
422 
423 	return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), size);
424 }
425 
426 
427 // #pragma mark - BScrollView methods
428 
429 
430 BScrollBar*
431 BScrollView::ScrollBar(orientation direction) const
432 {
433 	if (direction == B_HORIZONTAL)
434 		return fHorizontalScrollBar;
435 
436 	return fVerticalScrollBar;
437 }
438 
439 
440 void
441 BScrollView::SetBorder(border_style border)
442 {
443 	if (fBorder == border)
444 		return;
445 
446 	if (Flags() & B_SUPPORTS_LAYOUT) {
447 		fBorder = border;
448 		SetFlags(_ModifyFlags(Flags(), border));
449 
450 		DoLayout();
451 		Invalidate();
452 		return;
453 	}
454 
455 	float offset = _BorderSize() - _BorderSize(border);
456 	float resize = 2 * offset;
457 
458 	float horizontalGap = 0, verticalGap = 0;
459 	float change = 0;
460 	if (border == B_NO_BORDER || fBorder == B_NO_BORDER) {
461 		if (fHorizontalScrollBar != NULL)
462 			verticalGap = border != B_NO_BORDER ? 1 : -1;
463 		if (fVerticalScrollBar != NULL)
464 			horizontalGap = border != B_NO_BORDER ? 1 : -1;
465 
466 		change = border != B_NO_BORDER ? -1 : 1;
467 		if (fHorizontalScrollBar == NULL || fVerticalScrollBar == NULL)
468 			change *= 2;
469 	}
470 
471 	fBorder = border;
472 
473 	int32 savedResizingMode = 0;
474 	if (fTarget != NULL) {
475 		savedResizingMode = fTarget->ResizingMode();
476 		fTarget->SetResizingMode(B_FOLLOW_NONE);
477 	}
478 
479 	MoveBy(offset, offset);
480 	ResizeBy(-resize - horizontalGap, -resize - verticalGap);
481 
482 	if (fTarget != NULL) {
483 		fTarget->MoveBy(-offset, -offset);
484 		fTarget->SetResizingMode(savedResizingMode);
485 	}
486 
487 	if (fHorizontalScrollBar != NULL) {
488 		fHorizontalScrollBar->MoveBy(-offset - verticalGap, offset + verticalGap);
489 		fHorizontalScrollBar->ResizeBy(resize + horizontalGap - change, 0);
490 	}
491 	if (fVerticalScrollBar != NULL) {
492 		fVerticalScrollBar->MoveBy(offset + horizontalGap, -offset - horizontalGap);
493 		fVerticalScrollBar->ResizeBy(0, resize + verticalGap - change);
494 	}
495 
496 	SetFlags(_ModifyFlags(Flags(), border));
497 }
498 
499 
500 border_style
501 BScrollView::Border() const
502 {
503 	return fBorder;
504 }
505 
506 
507 void
508 BScrollView::SetBorders(uint32 borders)
509 {
510 	if (fBorders == borders || (Flags() & B_SUPPORTS_LAYOUT) == 0)
511 		return;
512 
513 	fBorders = borders;
514 	DoLayout();
515 	Invalidate();
516 }
517 
518 
519 uint32
520 BScrollView::Borders() const
521 {
522 	return fBorders;
523 }
524 
525 
526 status_t
527 BScrollView::SetBorderHighlighted(bool highlight)
528 {
529 	if (fHighlighted == highlight)
530 		return B_OK;
531 
532 	if (fBorder != B_FANCY_BORDER)
533 		// highlighting only works for B_FANCY_BORDER
534 		return B_ERROR;
535 
536 	fHighlighted = highlight;
537 
538 	if (fHorizontalScrollBar != NULL)
539 		fHorizontalScrollBar->SetBorderHighlighted(highlight);
540 	if (fVerticalScrollBar != NULL)
541 		fVerticalScrollBar->SetBorderHighlighted(highlight);
542 
543 	BRect bounds = Bounds();
544 	bounds.InsetBy(1, 1);
545 
546 	Invalidate(BRect(bounds.left, bounds.top, bounds.right, bounds.top));
547 	Invalidate(BRect(bounds.left, bounds.top + 1, bounds.left,
548 		bounds.bottom - 1));
549 	Invalidate(BRect(bounds.right, bounds.top + 1, bounds.right,
550 		bounds.bottom - 1));
551 	Invalidate(BRect(bounds.left, bounds.bottom, bounds.right, bounds.bottom));
552 
553 	return B_OK;
554 }
555 
556 
557 bool
558 BScrollView::IsBorderHighlighted() const
559 {
560 	return fHighlighted;
561 }
562 
563 
564 void
565 BScrollView::SetTarget(BView* target)
566 {
567 	if (fTarget == target)
568 		return;
569 
570 	if (fTarget != NULL) {
571 		fTarget->TargetedByScrollView(NULL);
572 		RemoveChild(fTarget);
573 
574 		// we are not supposed to delete the view
575 	}
576 
577 	fTarget = target;
578 	if (fHorizontalScrollBar != NULL)
579 		fHorizontalScrollBar->SetTarget(target);
580 	if (fVerticalScrollBar != NULL)
581 		fVerticalScrollBar->SetTarget(target);
582 
583 	if (target != NULL) {
584 		float borderSize = _BorderSize();
585 		target->MoveTo((fBorders & BControlLook::B_LEFT_BORDER) != 0
586 			? borderSize : 0, (fBorders & BControlLook::B_TOP_BORDER) != 0
587 				? borderSize : 0);
588 		BRect innerFrame = _InnerFrame();
589 		target->ResizeTo(innerFrame.Width() - 1, innerFrame.Height() - 1);
590 		target->TargetedByScrollView(this);
591 
592 		AddChild(target, ChildAt(0));
593 			// This way, we are making sure that the target will
594 			// be added top most in the list (which is important
595 			// for unarchiving)
596 	}
597 }
598 
599 
600 BView*
601 BScrollView::Target() const
602 {
603 	return fTarget;
604 }
605 
606 
607 // #pragma mark - Scripting methods
608 
609 
610 
611 BHandler*
612 BScrollView::ResolveSpecifier(BMessage* message, int32 index,
613 	BMessage* specifier, int32 what, const char* property)
614 {
615 	return BView::ResolveSpecifier(message, index, specifier, what, property);
616 }
617 
618 
619 status_t
620 BScrollView::GetSupportedSuites(BMessage* message)
621 {
622 	return BView::GetSupportedSuites(message);
623 }
624 
625 
626 //	#pragma mark - Perform
627 
628 
629 status_t
630 BScrollView::Perform(perform_code code, void* _data)
631 {
632 	switch (code) {
633 		case PERFORM_CODE_MIN_SIZE:
634 			((perform_data_min_size*)_data)->return_value
635 				= BScrollView::MinSize();
636 			return B_OK;
637 
638 		case PERFORM_CODE_MAX_SIZE:
639 			((perform_data_max_size*)_data)->return_value
640 				= BScrollView::MaxSize();
641 			return B_OK;
642 
643 		case PERFORM_CODE_PREFERRED_SIZE:
644 			((perform_data_preferred_size*)_data)->return_value
645 				= BScrollView::PreferredSize();
646 			return B_OK;
647 
648 		case PERFORM_CODE_LAYOUT_ALIGNMENT:
649 			((perform_data_layout_alignment*)_data)->return_value
650 				= BScrollView::LayoutAlignment();
651 			return B_OK;
652 
653 		case PERFORM_CODE_HAS_HEIGHT_FOR_WIDTH:
654 			((perform_data_has_height_for_width*)_data)->return_value
655 				= BScrollView::HasHeightForWidth();
656 			return B_OK;
657 
658 		case PERFORM_CODE_GET_HEIGHT_FOR_WIDTH:
659 		{
660 			perform_data_get_height_for_width* data
661 				= (perform_data_get_height_for_width*)_data;
662 			BScrollView::GetHeightForWidth(data->width, &data->min, &data->max,
663 				&data->preferred);
664 			return B_OK;
665 		}
666 
667 		case PERFORM_CODE_SET_LAYOUT:
668 		{
669 			perform_data_set_layout* data = (perform_data_set_layout*)_data;
670 			BScrollView::SetLayout(data->layout);
671 			return B_OK;
672 		}
673 
674 		case PERFORM_CODE_LAYOUT_INVALIDATED:
675 		{
676 			perform_data_layout_invalidated* data
677 				= (perform_data_layout_invalidated*)_data;
678 			BScrollView::LayoutInvalidated(data->descendants);
679 			return B_OK;
680 		}
681 
682 		case PERFORM_CODE_DO_LAYOUT:
683 		{
684 			BScrollView::DoLayout();
685 			return B_OK;
686 		}
687 	}
688 
689 	return BView::Perform(code, _data);
690 }
691 
692 
693 //	#pragma mark - Protected methods
694 
695 
696 void
697 BScrollView::LayoutInvalidated(bool descendants)
698 {
699 }
700 
701 
702 void
703 BScrollView::DoLayout()
704 {
705 	if ((Flags() & B_SUPPORTS_LAYOUT) == 0)
706 		return;
707 
708 	// If the user set a layout, we let the base class version call its hook.
709 	if (GetLayout() != NULL) {
710 		BView::DoLayout();
711 		return;
712 	}
713 
714 	BRect innerFrame = _InnerFrame();
715 
716 	if (fTarget != NULL) {
717 		fTarget->MoveTo(innerFrame.left, innerFrame.top);
718 		fTarget->ResizeTo(innerFrame.Width(), innerFrame.Height());
719 
720 		//BLayoutUtils::AlignInFrame(fTarget, fTarget->Bounds());
721 	}
722 
723 	_AlignScrollBars(fHorizontalScrollBar != NULL, fVerticalScrollBar != NULL,
724 		innerFrame);
725 }
726 
727 
728 // #pragma mark - Private methods
729 
730 
731 void
732 BScrollView::_Init(bool horizontal, bool vertical)
733 {
734 	fHorizontalScrollBar = NULL;
735 	fVerticalScrollBar = NULL;
736 	fHighlighted = false;
737 	fBorders = BControlLook::B_ALL_BORDERS;
738 
739 	SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
740 
741 	if (horizontal) {
742 		fHorizontalScrollBar = new BScrollBar(BRect(0, 0, 14, 14), "_HSB_",
743 			fTarget, 0, 1000, B_HORIZONTAL);
744 		AddChild(fHorizontalScrollBar);
745 	}
746 
747 	if (vertical) {
748 		fVerticalScrollBar = new BScrollBar(BRect(0, 0, 14, 14), "_VSB_",
749 			fTarget, 0, 1000, B_VERTICAL);
750 		AddChild(fVerticalScrollBar);
751 	}
752 
753 	BRect targetFrame;
754 	if (fTarget) {
755 		// layout target and add it
756 		fTarget->TargetedByScrollView(this);
757 		fTarget->MoveTo(B_ORIGIN);
758 
759 		if (fBorder != B_NO_BORDER)
760 			fTarget->MoveBy(_BorderSize(), _BorderSize());
761 
762 		AddChild(fTarget);
763 		targetFrame = fTarget->Frame();
764 	} else {
765 		// no target specified
766 		targetFrame = Bounds();
767 		if (horizontal)
768 			targetFrame.bottom -= B_H_SCROLL_BAR_HEIGHT + 1;
769 		if (vertical)
770 			targetFrame.right -= B_V_SCROLL_BAR_WIDTH + 1;
771 		if (fBorder == B_FANCY_BORDER) {
772 			targetFrame.bottom--;
773 			targetFrame.right--;
774 		}
775 	}
776 
777 	_AlignScrollBars(horizontal, vertical, targetFrame);
778 
779 	fPreviousWidth = uint16(Bounds().Width());
780 	fPreviousHeight = uint16(Bounds().Height());
781 }
782 
783 
784 float
785 BScrollView::_BorderSize() const
786 {
787 	return _BorderSize(fBorder);
788 }
789 
790 
791 BRect
792 BScrollView::_InnerFrame() const
793 {
794 	BRect frame = Bounds();
795 	_InsetBorders(frame, fBorder, fBorders);
796 
797 	float borderSize = _BorderSize();
798 
799 	if (fHorizontalScrollBar != NULL) {
800 		frame.bottom -= B_H_SCROLL_BAR_HEIGHT;
801 		if (borderSize == 0)
802 			frame.bottom--;
803 	}
804 	if (fVerticalScrollBar != NULL) {
805 		frame.right -= B_V_SCROLL_BAR_WIDTH;
806 		if (borderSize == 0)
807 			frame.right--;
808 	}
809 
810 	return frame;
811 }
812 
813 
814 BSize
815 BScrollView::_ComputeSize(BSize targetSize) const
816 {
817 	BRect frame = _ComputeFrame(
818 		BRect(0, 0, targetSize.width, targetSize.height));
819 
820 	return BSize(frame.Width(), frame.Height());
821 }
822 
823 
824 BRect
825 BScrollView::_ComputeFrame(BRect targetRect) const
826 {
827 	return _ComputeFrame(targetRect, fHorizontalScrollBar != NULL,
828 		fVerticalScrollBar != NULL, fBorder, fBorders);
829 }
830 
831 
832 void
833 BScrollView::_AlignScrollBars(bool horizontal, bool vertical, BRect targetFrame)
834 {
835 	if (horizontal) {
836 		BRect rect = targetFrame;
837 		rect.top = rect.bottom + 1;
838 		rect.bottom = rect.top + B_H_SCROLL_BAR_HEIGHT;
839 		if (fBorder != B_NO_BORDER || vertical) {
840 			// extend scrollbar so that it overlaps one pixel with vertical
841 			// scrollbar
842 			rect.right++;
843 		}
844 
845 		if (fBorder != B_NO_BORDER) {
846 			// the scrollbar draws part of the surrounding frame on the left
847 			rect.left--;
848 		}
849 
850 		fHorizontalScrollBar->MoveTo(rect.left, rect.top);
851 		fHorizontalScrollBar->ResizeTo(rect.Width(), rect.Height());
852 	}
853 
854 	if (vertical) {
855 		BRect rect = targetFrame;
856 		rect.left = rect.right + 1;
857 		rect.right = rect.left + B_V_SCROLL_BAR_WIDTH;
858 		if (fBorder != B_NO_BORDER || horizontal) {
859 			// extend scrollbar so that it overlaps one pixel with vertical
860 			// scrollbar
861 			rect.bottom++;
862 		}
863 
864 		if (fBorder != B_NO_BORDER) {
865 			// the scrollbar draws part of the surrounding frame on the left
866 			rect.top--;
867 		}
868 
869 		fVerticalScrollBar->MoveTo(rect.left, rect.top);
870 		fVerticalScrollBar->ResizeTo(rect.Width(), rect.Height());
871 	}
872 }
873 
874 
875 /*!	This static method is used to calculate the frame that the
876 	ScrollView will cover depending on the frame of its target
877 	and which border style is used.
878 	It is used in the constructor and at other places.
879 */
880 /*static*/ BRect
881 BScrollView::_ComputeFrame(BRect frame, bool horizontal, bool vertical,
882 	border_style border, uint32 borders)
883 {
884 	if (vertical)
885 		frame.right += B_V_SCROLL_BAR_WIDTH;
886 	if (horizontal)
887 		frame.bottom += B_H_SCROLL_BAR_HEIGHT;
888 
889 	_InsetBorders(frame, border, borders, true);
890 
891 	if (_BorderSize(border) == 0) {
892 		if (vertical)
893 			frame.right++;
894 		if (horizontal)
895 			frame.bottom++;
896 	}
897 
898 	return frame;
899 }
900 
901 
902 /*static*/ BRect
903 BScrollView::_ComputeFrame(BView *target, bool horizontal, bool vertical,
904 	border_style border, uint32 borders)
905 {
906 	return _ComputeFrame(target != NULL ? target->Frame()
907 		: BRect(0, 0, 16, 16), horizontal, vertical, border, borders);
908 }
909 
910 
911 /*! This method returns the size of the specified border.
912 */
913 /*static*/ float
914 BScrollView::_BorderSize(border_style border)
915 {
916 	if (border == B_FANCY_BORDER)
917 		return kFancyBorderSize;
918 	if (border == B_PLAIN_BORDER)
919 		return kPlainBorderSize;
920 
921 	return 0;
922 }
923 
924 
925 /*!	This method changes the "flags" argument as passed on to
926 	the BView constructor.
927 */
928 /*static*/ int32
929 BScrollView::_ModifyFlags(int32 flags, border_style border)
930 {
931 	// We either need B_FULL_UPDATE_ON_RESIZE or
932 	// B_FRAME_EVENTS if we have to draw a border
933 	if (border != B_NO_BORDER)
934 		return flags | B_WILL_DRAW | (flags & B_FULL_UPDATE_ON_RESIZE ? 0 : B_FRAME_EVENTS);
935 
936 	return flags & ~(B_WILL_DRAW | B_FRAME_EVENTS | B_FULL_UPDATE_ON_RESIZE);
937 }
938 
939 
940 /*static*/ void
941 BScrollView::_InsetBorders(BRect& frame, border_style border, uint32 borders, bool expand)
942 {
943 	float borderSize = _BorderSize(border);
944 	if (expand)
945 		borderSize = -borderSize;
946 	if ((borders & BControlLook::B_LEFT_BORDER) != 0)
947 		frame.left += borderSize;
948 	if ((borders & BControlLook::B_TOP_BORDER) != 0)
949 		frame.top += borderSize;
950 	if ((borders & BControlLook::B_RIGHT_BORDER) != 0)
951 		frame.right -= borderSize;
952 	if ((borders & BControlLook::B_BOTTOM_BORDER) != 0)
953 		frame.bottom -= borderSize;
954 }
955 
956 
957 //	#pragma mark - FBC and forbidden
958 
959 
960 BScrollView&
961 BScrollView::operator=(const BScrollView &)
962 {
963 	return *this;
964 }
965 
966 
967 void BScrollView::_ReservedScrollView1() {}
968 void BScrollView::_ReservedScrollView2() {}
969 void BScrollView::_ReservedScrollView3() {}
970 void BScrollView::_ReservedScrollView4() {}
971 
972 
973 extern "C" void
974 B_IF_GCC_2(InvalidateLayout__11BScrollViewb,
975 	_ZN11BScrollView16InvalidateLayoutEb)(BScrollView* view, bool descendants)
976 {
977 	perform_data_layout_invalidated data;
978 	data.descendants = descendants;
979 
980 	view->Perform(PERFORM_CODE_LAYOUT_INVALIDATED, &data);
981 }
982