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