xref: /haiku/src/kits/interface/ScrollView.cpp (revision 495060760727dd782c9f8a90db71e5d727f19748)
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, target, 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, target, 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 	const BRect bounds = Bounds();
275 
276 	if (fTarget != NULL && (fTarget->Flags() & B_SUPPORTS_LAYOUT) != 0
277 			&& (fTarget->Flags() & B_SCROLL_VIEW_AWARE) == 0) {
278 		BSize size = fTarget->PreferredSize();
279 		if (fHorizontalScrollBar != NULL) {
280 			float delta = size.Width() - bounds.Width(),
281 				proportion = bounds.Width() / size.Width();
282 			if (delta < 0)
283 				delta = 0;
284 
285 			fHorizontalScrollBar->SetRange(0, delta);
286 			fHorizontalScrollBar->SetSteps(be_plain_font->Size() * 1.33,
287 				bounds.Width());
288 			fHorizontalScrollBar->SetProportion(proportion);
289 		}
290 		if (fVerticalScrollBar != NULL) {
291 			float delta = size.Height() - bounds.Height(),
292 				proportion = bounds.Height() / size.Height();
293 			if (delta < 0)
294 				delta = 0;
295 
296 			fVerticalScrollBar->SetRange(0, delta);
297 			fVerticalScrollBar->SetSteps(be_plain_font->Size() * 1.33,
298 				bounds.Height());
299 			fVerticalScrollBar->SetProportion(proportion);
300 		}
301 	}
302 
303 	if (fBorder == B_NO_BORDER)
304 		return;
305 
306 	float border = _BorderSize() - 1;
307 
308 	if (fHorizontalScrollBar != NULL && fVerticalScrollBar != NULL) {
309 		BRect scrollCorner(bounds);
310 		scrollCorner.left = min_c(
311 			fPreviousWidth - fVerticalScrollBar->Frame().Height(),
312 			fHorizontalScrollBar->Frame().right + 1);
313 		scrollCorner.top = min_c(
314 			fPreviousHeight - fHorizontalScrollBar->Frame().Width(),
315 			fVerticalScrollBar->Frame().bottom + 1);
316 		Invalidate(scrollCorner);
317 	}
318 
319 	// changes in newWidth
320 
321 	if (bounds.Width() > fPreviousWidth) {
322 		// invalidate the region between the old and the new right border
323 		BRect rect = bounds;
324 		rect.left += fPreviousWidth - border;
325 		rect.right--;
326 		Invalidate(rect);
327 	} else if (bounds.Width() < fPreviousWidth) {
328 		// invalidate the region of the new right border
329 		BRect rect = bounds;
330 		rect.left = rect.right - border;
331 		Invalidate(rect);
332 	}
333 
334 	// changes in newHeight
335 
336 	if (bounds.Height() > fPreviousHeight) {
337 		// invalidate the region between the old and the new bottom border
338 		BRect rect = bounds;
339 		rect.top += fPreviousHeight - border;
340 		rect.bottom--;
341 		Invalidate(rect);
342 	} else if (bounds.Height() < fPreviousHeight) {
343 		// invalidate the region of the new bottom border
344 		BRect rect = bounds;
345 		rect.top = rect.bottom - border;
346 		Invalidate(rect);
347 	}
348 
349 	fPreviousWidth = uint16(bounds.Width());
350 	fPreviousHeight = uint16(bounds.Height());
351 }
352 
353 
354 void
355 BScrollView::MessageReceived(BMessage* message)
356 {
357 	BView::MessageReceived(message);
358 }
359 
360 
361 void
362 BScrollView::MouseDown(BPoint where)
363 {
364 	BView::MouseDown(where);
365 }
366 
367 
368 void
369 BScrollView::MouseMoved(BPoint where, uint32 code,
370 	const BMessage* dragMessage)
371 {
372 	BView::MouseMoved(where, code, dragMessage);
373 }
374 
375 
376 void
377 BScrollView::MouseUp(BPoint where)
378 {
379 	BView::MouseUp(where);
380 }
381 
382 
383 void
384 BScrollView::WindowActivated(bool active)
385 {
386 	if (fHighlighted)
387 		Invalidate();
388 
389 	BView::WindowActivated(active);
390 }
391 
392 
393 // #pragma mark - Size methods
394 
395 
396 void
397 BScrollView::GetPreferredSize(float* _width, float* _height)
398 {
399 	BSize size = PreferredSize();
400 
401 	if (_width)
402 		*_width = size.width;
403 
404 	if (_height)
405 		*_height = size.height;
406 }
407 
408 
409 void
410 BScrollView::ResizeToPreferred()
411 {
412 	if (Window() == NULL)
413 		return;
414 	BView::ResizeToPreferred();
415 }
416 
417 
418 void
419 BScrollView::MakeFocus(bool focus)
420 {
421 	BView::MakeFocus(focus);
422 }
423 
424 
425 BSize
426 BScrollView::MinSize()
427 {
428 	BSize size = _ComputeSize(fTarget != NULL ? fTarget->MinSize()
429 		: BSize(16, 16));
430 
431 	return BLayoutUtils::ComposeSize(ExplicitMinSize(), size);
432 }
433 
434 
435 BSize
436 BScrollView::MaxSize()
437 {
438 	BSize size = _ComputeSize(fTarget != NULL ? fTarget->MaxSize()
439 		: BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED));
440 
441 	return BLayoutUtils::ComposeSize(ExplicitMaxSize(), size);
442 }
443 
444 
445 BSize
446 BScrollView::PreferredSize()
447 {
448 	BSize size = _ComputeSize(fTarget != NULL ? fTarget->PreferredSize()
449 		: BSize(32, 32));
450 
451 	return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), size);
452 }
453 
454 
455 // #pragma mark - BScrollView methods
456 
457 
458 BScrollBar*
459 BScrollView::ScrollBar(orientation direction) const
460 {
461 	if (direction == B_HORIZONTAL)
462 		return fHorizontalScrollBar;
463 
464 	return fVerticalScrollBar;
465 }
466 
467 
468 void
469 BScrollView::SetBorder(border_style border)
470 {
471 	if (fBorder == border)
472 		return;
473 
474 	if ((Flags() & B_SUPPORTS_LAYOUT) != 0) {
475 		fBorder = border;
476 		SetFlags(_ModifyFlags(Flags(), fTarget, border));
477 
478 		DoLayout();
479 		Invalidate();
480 		return;
481 	}
482 
483 	float offset = _BorderSize() - _BorderSize(border);
484 	float resize = 2 * offset;
485 
486 	float horizontalGap = 0, verticalGap = 0;
487 	float change = 0;
488 	if (border == B_NO_BORDER || fBorder == B_NO_BORDER) {
489 		if (fHorizontalScrollBar != NULL)
490 			verticalGap = border != B_NO_BORDER ? 1 : -1;
491 		if (fVerticalScrollBar != NULL)
492 			horizontalGap = border != B_NO_BORDER ? 1 : -1;
493 
494 		change = border != B_NO_BORDER ? -1 : 1;
495 		if (fHorizontalScrollBar == NULL || fVerticalScrollBar == NULL)
496 			change *= 2;
497 	}
498 
499 	fBorder = border;
500 
501 	int32 savedResizingMode = 0;
502 	if (fTarget != NULL) {
503 		savedResizingMode = fTarget->ResizingMode();
504 		fTarget->SetResizingMode(B_FOLLOW_NONE);
505 	}
506 
507 	MoveBy(offset, offset);
508 	ResizeBy(-resize - horizontalGap, -resize - verticalGap);
509 
510 	if (fTarget != NULL) {
511 		fTarget->MoveBy(-offset, -offset);
512 		fTarget->SetResizingMode(savedResizingMode);
513 	}
514 
515 	if (fHorizontalScrollBar != NULL) {
516 		fHorizontalScrollBar->MoveBy(-offset - verticalGap, offset + verticalGap);
517 		fHorizontalScrollBar->ResizeBy(resize + horizontalGap - change, 0);
518 	}
519 	if (fVerticalScrollBar != NULL) {
520 		fVerticalScrollBar->MoveBy(offset + horizontalGap, -offset - horizontalGap);
521 		fVerticalScrollBar->ResizeBy(0, resize + verticalGap - change);
522 	}
523 
524 	SetFlags(_ModifyFlags(Flags(), fTarget, border));
525 }
526 
527 
528 border_style
529 BScrollView::Border() const
530 {
531 	return fBorder;
532 }
533 
534 
535 void
536 BScrollView::SetBorders(uint32 borders)
537 {
538 	if (fBorders == borders || (Flags() & B_SUPPORTS_LAYOUT) == 0)
539 		return;
540 
541 	fBorders = borders;
542 	DoLayout();
543 	Invalidate();
544 }
545 
546 
547 uint32
548 BScrollView::Borders() const
549 {
550 	return fBorders;
551 }
552 
553 
554 status_t
555 BScrollView::SetBorderHighlighted(bool highlight)
556 {
557 	if (fHighlighted == highlight)
558 		return B_OK;
559 
560 	if (fBorder != B_FANCY_BORDER)
561 		// highlighting only works for B_FANCY_BORDER
562 		return B_ERROR;
563 
564 	fHighlighted = highlight;
565 
566 	if (fHorizontalScrollBar != NULL)
567 		fHorizontalScrollBar->SetBorderHighlighted(highlight);
568 	if (fVerticalScrollBar != NULL)
569 		fVerticalScrollBar->SetBorderHighlighted(highlight);
570 
571 	BRect bounds = Bounds();
572 	bounds.InsetBy(1, 1);
573 
574 	Invalidate(BRect(bounds.left, bounds.top, bounds.right, bounds.top));
575 	Invalidate(BRect(bounds.left, bounds.top + 1, bounds.left,
576 		bounds.bottom - 1));
577 	Invalidate(BRect(bounds.right, bounds.top + 1, bounds.right,
578 		bounds.bottom - 1));
579 	Invalidate(BRect(bounds.left, bounds.bottom, bounds.right, bounds.bottom));
580 
581 	return B_OK;
582 }
583 
584 
585 bool
586 BScrollView::IsBorderHighlighted() const
587 {
588 	return fHighlighted;
589 }
590 
591 
592 void
593 BScrollView::SetTarget(BView* target)
594 {
595 	if (fTarget == target)
596 		return;
597 
598 	if (fTarget != NULL) {
599 		fTarget->TargetedByScrollView(NULL);
600 		RemoveChild(fTarget);
601 
602 		// we are not supposed to delete the view
603 	}
604 
605 	fTarget = target;
606 	if (fHorizontalScrollBar != NULL)
607 		fHorizontalScrollBar->SetTarget(target);
608 	if (fVerticalScrollBar != NULL)
609 		fVerticalScrollBar->SetTarget(target);
610 
611 	if (target != NULL) {
612 		float borderSize = _BorderSize();
613 		target->MoveTo((fBorders & BControlLook::B_LEFT_BORDER) != 0
614 			? borderSize : 0, (fBorders & BControlLook::B_TOP_BORDER) != 0
615 				? borderSize : 0);
616 		BRect innerFrame = _InnerFrame();
617 		target->ResizeTo(innerFrame.Width() - 1, innerFrame.Height() - 1);
618 		target->TargetedByScrollView(this);
619 
620 		AddChild(target, ChildAt(0));
621 			// This way, we are making sure that the target will
622 			// be added top most in the list (which is important
623 			// for unarchiving)
624 	}
625 
626 	SetFlags(_ModifyFlags(Flags(), fTarget, fBorder));
627 }
628 
629 
630 BView*
631 BScrollView::Target() const
632 {
633 	return fTarget;
634 }
635 
636 
637 // #pragma mark - Scripting methods
638 
639 
640 
641 BHandler*
642 BScrollView::ResolveSpecifier(BMessage* message, int32 index,
643 	BMessage* specifier, int32 what, const char* property)
644 {
645 	return BView::ResolveSpecifier(message, index, specifier, what, property);
646 }
647 
648 
649 status_t
650 BScrollView::GetSupportedSuites(BMessage* message)
651 {
652 	return BView::GetSupportedSuites(message);
653 }
654 
655 
656 //	#pragma mark - Perform
657 
658 
659 status_t
660 BScrollView::Perform(perform_code code, void* _data)
661 {
662 	switch (code) {
663 		case PERFORM_CODE_MIN_SIZE:
664 			((perform_data_min_size*)_data)->return_value
665 				= BScrollView::MinSize();
666 			return B_OK;
667 
668 		case PERFORM_CODE_MAX_SIZE:
669 			((perform_data_max_size*)_data)->return_value
670 				= BScrollView::MaxSize();
671 			return B_OK;
672 
673 		case PERFORM_CODE_PREFERRED_SIZE:
674 			((perform_data_preferred_size*)_data)->return_value
675 				= BScrollView::PreferredSize();
676 			return B_OK;
677 
678 		case PERFORM_CODE_LAYOUT_ALIGNMENT:
679 			((perform_data_layout_alignment*)_data)->return_value
680 				= BScrollView::LayoutAlignment();
681 			return B_OK;
682 
683 		case PERFORM_CODE_HAS_HEIGHT_FOR_WIDTH:
684 			((perform_data_has_height_for_width*)_data)->return_value
685 				= BScrollView::HasHeightForWidth();
686 			return B_OK;
687 
688 		case PERFORM_CODE_GET_HEIGHT_FOR_WIDTH:
689 		{
690 			perform_data_get_height_for_width* data
691 				= (perform_data_get_height_for_width*)_data;
692 			BScrollView::GetHeightForWidth(data->width, &data->min, &data->max,
693 				&data->preferred);
694 			return B_OK;
695 		}
696 
697 		case PERFORM_CODE_SET_LAYOUT:
698 		{
699 			perform_data_set_layout* data = (perform_data_set_layout*)_data;
700 			BScrollView::SetLayout(data->layout);
701 			return B_OK;
702 		}
703 
704 		case PERFORM_CODE_LAYOUT_INVALIDATED:
705 		{
706 			perform_data_layout_invalidated* data
707 				= (perform_data_layout_invalidated*)_data;
708 			BScrollView::LayoutInvalidated(data->descendants);
709 			return B_OK;
710 		}
711 
712 		case PERFORM_CODE_DO_LAYOUT:
713 		{
714 			BScrollView::DoLayout();
715 			return B_OK;
716 		}
717 	}
718 
719 	return BView::Perform(code, _data);
720 }
721 
722 
723 //	#pragma mark - Protected methods
724 
725 
726 void
727 BScrollView::LayoutInvalidated(bool descendants)
728 {
729 }
730 
731 
732 void
733 BScrollView::DoLayout()
734 {
735 	if ((Flags() & B_SUPPORTS_LAYOUT) == 0)
736 		return;
737 
738 	// If the user set a layout, we let the base class version call its hook.
739 	if (GetLayout() != NULL) {
740 		BView::DoLayout();
741 		return;
742 	}
743 
744 	BRect innerFrame = _InnerFrame();
745 
746 	if (fTarget != NULL) {
747 		fTarget->MoveTo(innerFrame.left, innerFrame.top);
748 		fTarget->ResizeTo(innerFrame.Width(), innerFrame.Height());
749 
750 		//BLayoutUtils::AlignInFrame(fTarget, fTarget->Bounds());
751 	}
752 
753 	_AlignScrollBars(fHorizontalScrollBar != NULL, fVerticalScrollBar != NULL,
754 		innerFrame);
755 }
756 
757 
758 // #pragma mark - Private methods
759 
760 
761 void
762 BScrollView::_Init(bool horizontal, bool vertical)
763 {
764 	fHorizontalScrollBar = NULL;
765 	fVerticalScrollBar = NULL;
766 	fHighlighted = false;
767 	fBorders = BControlLook::B_ALL_BORDERS;
768 
769 	SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
770 
771 	if (horizontal) {
772 		fHorizontalScrollBar = new BScrollBar(BRect(0, 0, 14, 14), "_HSB_",
773 			fTarget, 0, 1000, B_HORIZONTAL);
774 		AddChild(fHorizontalScrollBar);
775 	}
776 
777 	if (vertical) {
778 		fVerticalScrollBar = new BScrollBar(BRect(0, 0, 14, 14), "_VSB_",
779 			fTarget, 0, 1000, B_VERTICAL);
780 		AddChild(fVerticalScrollBar);
781 	}
782 
783 	BRect targetFrame;
784 	if (fTarget) {
785 		// layout target and add it
786 		fTarget->TargetedByScrollView(this);
787 		fTarget->MoveTo(B_ORIGIN);
788 
789 		if (fBorder != B_NO_BORDER)
790 			fTarget->MoveBy(_BorderSize(), _BorderSize());
791 
792 		AddChild(fTarget);
793 		targetFrame = fTarget->Frame();
794 	} else {
795 		// no target specified
796 		targetFrame = Bounds();
797 		if (horizontal)
798 			targetFrame.bottom -= B_H_SCROLL_BAR_HEIGHT + 1;
799 		if (vertical)
800 			targetFrame.right -= B_V_SCROLL_BAR_WIDTH + 1;
801 		if (fBorder == B_FANCY_BORDER) {
802 			targetFrame.bottom--;
803 			targetFrame.right--;
804 		}
805 	}
806 
807 	_AlignScrollBars(horizontal, vertical, targetFrame);
808 
809 	fPreviousWidth = uint16(Bounds().Width());
810 	fPreviousHeight = uint16(Bounds().Height());
811 }
812 
813 
814 float
815 BScrollView::_BorderSize() const
816 {
817 	return _BorderSize(fBorder);
818 }
819 
820 
821 BRect
822 BScrollView::_InnerFrame() const
823 {
824 	BRect frame = Bounds();
825 	_InsetBorders(frame, fBorder, fBorders);
826 
827 	float borderSize = _BorderSize();
828 
829 	if (fHorizontalScrollBar != NULL) {
830 		frame.bottom -= B_H_SCROLL_BAR_HEIGHT;
831 		if (borderSize == 0)
832 			frame.bottom--;
833 	}
834 	if (fVerticalScrollBar != NULL) {
835 		frame.right -= B_V_SCROLL_BAR_WIDTH;
836 		if (borderSize == 0)
837 			frame.right--;
838 	}
839 
840 	return frame;
841 }
842 
843 
844 BSize
845 BScrollView::_ComputeSize(BSize targetSize) const
846 {
847 	BRect frame = _ComputeFrame(
848 		BRect(0, 0, targetSize.width, targetSize.height));
849 
850 	return BSize(frame.Width(), frame.Height());
851 }
852 
853 
854 BRect
855 BScrollView::_ComputeFrame(BRect targetRect) const
856 {
857 	return _ComputeFrame(targetRect, fHorizontalScrollBar != NULL,
858 		fVerticalScrollBar != NULL, fBorder, fBorders);
859 }
860 
861 
862 void
863 BScrollView::_AlignScrollBars(bool horizontal, bool vertical, BRect targetFrame)
864 {
865 	if (horizontal) {
866 		BRect rect = targetFrame;
867 		rect.top = rect.bottom + 1;
868 		rect.bottom = rect.top + B_H_SCROLL_BAR_HEIGHT;
869 		if (fBorder != B_NO_BORDER || vertical) {
870 			// extend scrollbar so that it overlaps one pixel with vertical
871 			// scrollbar
872 			rect.right++;
873 		}
874 
875 		if (fBorder != B_NO_BORDER) {
876 			// the scrollbar draws part of the surrounding frame on the left
877 			rect.left--;
878 		}
879 
880 		fHorizontalScrollBar->MoveTo(rect.left, rect.top);
881 		fHorizontalScrollBar->ResizeTo(rect.Width(), rect.Height());
882 	}
883 
884 	if (vertical) {
885 		BRect rect = targetFrame;
886 		rect.left = rect.right + 1;
887 		rect.right = rect.left + B_V_SCROLL_BAR_WIDTH;
888 		if (fBorder != B_NO_BORDER || horizontal) {
889 			// extend scrollbar so that it overlaps one pixel with vertical
890 			// scrollbar
891 			rect.bottom++;
892 		}
893 
894 		if (fBorder != B_NO_BORDER) {
895 			// the scrollbar draws part of the surrounding frame on the left
896 			rect.top--;
897 		}
898 
899 		fVerticalScrollBar->MoveTo(rect.left, rect.top);
900 		fVerticalScrollBar->ResizeTo(rect.Width(), rect.Height());
901 	}
902 }
903 
904 
905 /*!	This static method is used to calculate the frame that the
906 	ScrollView will cover depending on the frame of its target
907 	and which border style is used.
908 	It is used in the constructor and at other places.
909 */
910 /*static*/ BRect
911 BScrollView::_ComputeFrame(BRect frame, bool horizontal, bool vertical,
912 	border_style border, uint32 borders)
913 {
914 	if (vertical)
915 		frame.right += B_V_SCROLL_BAR_WIDTH;
916 	if (horizontal)
917 		frame.bottom += B_H_SCROLL_BAR_HEIGHT;
918 
919 	_InsetBorders(frame, border, borders, true);
920 
921 	if (_BorderSize(border) == 0) {
922 		if (vertical)
923 			frame.right++;
924 		if (horizontal)
925 			frame.bottom++;
926 	}
927 
928 	return frame;
929 }
930 
931 
932 /*static*/ BRect
933 BScrollView::_ComputeFrame(BView *target, bool horizontal, bool vertical,
934 	border_style border, uint32 borders)
935 {
936 	return _ComputeFrame(target != NULL ? target->Frame()
937 		: BRect(0, 0, 16, 16), horizontal, vertical, border, borders);
938 }
939 
940 
941 /*! This method returns the size of the specified border.
942 */
943 /*static*/ float
944 BScrollView::_BorderSize(border_style border)
945 {
946 	if (border == B_FANCY_BORDER)
947 		return kFancyBorderSize;
948 	if (border == B_PLAIN_BORDER)
949 		return kPlainBorderSize;
950 
951 	return 0;
952 }
953 
954 
955 /*!	This method changes the "flags" argument as passed on to
956 	the BView constructor.
957 */
958 /*static*/ uint32
959 BScrollView::_ModifyFlags(uint32 flags, BView* target, border_style border)
960 {
961 	if (target != NULL && (target->Flags() & B_SUPPORTS_LAYOUT) != 0
962 			&& (target->Flags() & B_SCROLL_VIEW_AWARE) == 0)
963 		flags |= B_FRAME_EVENTS;
964 
965 	// We either need B_FULL_UPDATE_ON_RESIZE or B_FRAME_EVENTS if we have
966 	// to draw a border.
967 	if (border != B_NO_BORDER) {
968 		flags |= B_WILL_DRAW | ((flags & B_FULL_UPDATE_ON_RESIZE) != 0 ?
969 			0 : B_FRAME_EVENTS);
970 	}
971 
972 	return flags;
973 }
974 
975 
976 /*static*/ void
977 BScrollView::_InsetBorders(BRect& frame, border_style border, uint32 borders, bool expand)
978 {
979 	float borderSize = _BorderSize(border);
980 	if (expand)
981 		borderSize = -borderSize;
982 	if ((borders & BControlLook::B_LEFT_BORDER) != 0)
983 		frame.left += borderSize;
984 	if ((borders & BControlLook::B_TOP_BORDER) != 0)
985 		frame.top += borderSize;
986 	if ((borders & BControlLook::B_RIGHT_BORDER) != 0)
987 		frame.right -= borderSize;
988 	if ((borders & BControlLook::B_BOTTOM_BORDER) != 0)
989 		frame.bottom -= borderSize;
990 }
991 
992 
993 //	#pragma mark - FBC and forbidden
994 
995 
996 BScrollView&
997 BScrollView::operator=(const BScrollView &)
998 {
999 	return *this;
1000 }
1001 
1002 
1003 void BScrollView::_ReservedScrollView1() {}
1004 void BScrollView::_ReservedScrollView2() {}
1005 void BScrollView::_ReservedScrollView3() {}
1006 void BScrollView::_ReservedScrollView4() {}
1007 
1008 
1009 extern "C" void
1010 B_IF_GCC_2(InvalidateLayout__11BScrollViewb,
1011 	_ZN11BScrollView16InvalidateLayoutEb)(BScrollView* view, bool descendants)
1012 {
1013 	perform_data_layout_invalidated data;
1014 	data.descendants = descendants;
1015 
1016 	view->Perform(PERFORM_CODE_LAYOUT_INVALIDATED, &data);
1017 }
1018