xref: /haiku/src/kits/interface/ScrollView.cpp (revision 97dfeb96704e5dbc5bec32ad7b21379d0125e031)
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 	if (be_control_look != NULL)
545 		bounds.InsetBy(1, 1);
546 
547 	Invalidate(BRect(bounds.left, bounds.top, bounds.right, bounds.top));
548 	Invalidate(BRect(bounds.left, bounds.top + 1, bounds.left,
549 		bounds.bottom - 1));
550 	Invalidate(BRect(bounds.right, bounds.top + 1, bounds.right,
551 		bounds.bottom - 1));
552 	Invalidate(BRect(bounds.left, bounds.bottom, bounds.right, bounds.bottom));
553 
554 	return B_OK;
555 }
556 
557 
558 bool
559 BScrollView::IsBorderHighlighted() const
560 {
561 	return fHighlighted;
562 }
563 
564 
565 void
566 BScrollView::SetTarget(BView* target)
567 {
568 	if (fTarget == target)
569 		return;
570 
571 	if (fTarget != NULL) {
572 		fTarget->TargetedByScrollView(NULL);
573 		RemoveChild(fTarget);
574 
575 		// we are not supposed to delete the view
576 	}
577 
578 	fTarget = target;
579 	if (fHorizontalScrollBar != NULL)
580 		fHorizontalScrollBar->SetTarget(target);
581 	if (fVerticalScrollBar != NULL)
582 		fVerticalScrollBar->SetTarget(target);
583 
584 	if (target != NULL) {
585 		float borderSize = _BorderSize();
586 		target->MoveTo((fBorders & BControlLook::B_LEFT_BORDER) != 0
587 			? borderSize : 0, (fBorders & BControlLook::B_TOP_BORDER) != 0
588 				? borderSize : 0);
589 		BRect innerFrame = _InnerFrame();
590 		target->ResizeTo(innerFrame.Width() - 1, innerFrame.Height() - 1);
591 		target->TargetedByScrollView(this);
592 
593 		AddChild(target, ChildAt(0));
594 			// This way, we are making sure that the target will
595 			// be added top most in the list (which is important
596 			// for unarchiving)
597 	}
598 }
599 
600 
601 BView*
602 BScrollView::Target() const
603 {
604 	return fTarget;
605 }
606 
607 
608 // #pragma mark - Scripting methods
609 
610 
611 
612 BHandler*
613 BScrollView::ResolveSpecifier(BMessage* message, int32 index,
614 	BMessage* specifier, int32 what, const char* property)
615 {
616 	return BView::ResolveSpecifier(message, index, specifier, what, property);
617 }
618 
619 
620 status_t
621 BScrollView::GetSupportedSuites(BMessage* message)
622 {
623 	return BView::GetSupportedSuites(message);
624 }
625 
626 
627 //	#pragma mark - Perform
628 
629 
630 status_t
631 BScrollView::Perform(perform_code code, void* _data)
632 {
633 	switch (code) {
634 		case PERFORM_CODE_MIN_SIZE:
635 			((perform_data_min_size*)_data)->return_value
636 				= BScrollView::MinSize();
637 			return B_OK;
638 
639 		case PERFORM_CODE_MAX_SIZE:
640 			((perform_data_max_size*)_data)->return_value
641 				= BScrollView::MaxSize();
642 			return B_OK;
643 
644 		case PERFORM_CODE_PREFERRED_SIZE:
645 			((perform_data_preferred_size*)_data)->return_value
646 				= BScrollView::PreferredSize();
647 			return B_OK;
648 
649 		case PERFORM_CODE_LAYOUT_ALIGNMENT:
650 			((perform_data_layout_alignment*)_data)->return_value
651 				= BScrollView::LayoutAlignment();
652 			return B_OK;
653 
654 		case PERFORM_CODE_HAS_HEIGHT_FOR_WIDTH:
655 			((perform_data_has_height_for_width*)_data)->return_value
656 				= BScrollView::HasHeightForWidth();
657 			return B_OK;
658 
659 		case PERFORM_CODE_GET_HEIGHT_FOR_WIDTH:
660 		{
661 			perform_data_get_height_for_width* data
662 				= (perform_data_get_height_for_width*)_data;
663 			BScrollView::GetHeightForWidth(data->width, &data->min, &data->max,
664 				&data->preferred);
665 			return B_OK;
666 		}
667 
668 		case PERFORM_CODE_SET_LAYOUT:
669 		{
670 			perform_data_set_layout* data = (perform_data_set_layout*)_data;
671 			BScrollView::SetLayout(data->layout);
672 			return B_OK;
673 		}
674 
675 		case PERFORM_CODE_LAYOUT_INVALIDATED:
676 		{
677 			perform_data_layout_invalidated* data
678 				= (perform_data_layout_invalidated*)_data;
679 			BScrollView::LayoutInvalidated(data->descendants);
680 			return B_OK;
681 		}
682 
683 		case PERFORM_CODE_DO_LAYOUT:
684 		{
685 			BScrollView::DoLayout();
686 			return B_OK;
687 		}
688 	}
689 
690 	return BView::Perform(code, _data);
691 }
692 
693 
694 //	#pragma mark - Protected methods
695 
696 
697 void
698 BScrollView::LayoutInvalidated(bool descendants)
699 {
700 }
701 
702 
703 void
704 BScrollView::DoLayout()
705 {
706 	if ((Flags() & B_SUPPORTS_LAYOUT) == 0)
707 		return;
708 
709 	// If the user set a layout, we let the base class version call its hook.
710 	if (GetLayout() != NULL) {
711 		BView::DoLayout();
712 		return;
713 	}
714 
715 	BRect innerFrame = _InnerFrame();
716 
717 	if (fTarget != NULL) {
718 		fTarget->MoveTo(innerFrame.left, innerFrame.top);
719 		fTarget->ResizeTo(innerFrame.Width(), innerFrame.Height());
720 
721 		//BLayoutUtils::AlignInFrame(fTarget, fTarget->Bounds());
722 	}
723 
724 	_AlignScrollBars(fHorizontalScrollBar != NULL, fVerticalScrollBar != NULL,
725 		innerFrame);
726 }
727 
728 
729 // #pragma mark - Private methods
730 
731 
732 void
733 BScrollView::_Init(bool horizontal, bool vertical)
734 {
735 	fHorizontalScrollBar = NULL;
736 	fVerticalScrollBar = NULL;
737 	fHighlighted = false;
738 	fBorders = BControlLook::B_ALL_BORDERS;
739 
740 	if (be_control_look != NULL)
741 		SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
742 
743 	if (horizontal) {
744 		fHorizontalScrollBar = new BScrollBar(BRect(0, 0, 14, 14), "_HSB_",
745 			fTarget, 0, 1000, B_HORIZONTAL);
746 		AddChild(fHorizontalScrollBar);
747 	}
748 
749 	if (vertical) {
750 		fVerticalScrollBar = new BScrollBar(BRect(0, 0, 14, 14), "_VSB_",
751 			fTarget, 0, 1000, B_VERTICAL);
752 		AddChild(fVerticalScrollBar);
753 	}
754 
755 	BRect targetFrame;
756 	if (fTarget) {
757 		// layout target and add it
758 		fTarget->TargetedByScrollView(this);
759 		fTarget->MoveTo(B_ORIGIN);
760 
761 		if (fBorder != B_NO_BORDER)
762 			fTarget->MoveBy(_BorderSize(), _BorderSize());
763 
764 		AddChild(fTarget);
765 		targetFrame = fTarget->Frame();
766 	} else {
767 		// no target specified
768 		targetFrame = Bounds();
769 		if (horizontal)
770 			targetFrame.bottom -= B_H_SCROLL_BAR_HEIGHT + 1;
771 		if (vertical)
772 			targetFrame.right -= B_V_SCROLL_BAR_WIDTH + 1;
773 		if (fBorder == B_FANCY_BORDER) {
774 			targetFrame.bottom--;
775 			targetFrame.right--;
776 		}
777 	}
778 
779 	_AlignScrollBars(horizontal, vertical, targetFrame);
780 
781 	fPreviousWidth = uint16(Bounds().Width());
782 	fPreviousHeight = uint16(Bounds().Height());
783 }
784 
785 
786 float
787 BScrollView::_BorderSize() const
788 {
789 	return _BorderSize(fBorder);
790 }
791 
792 
793 BRect
794 BScrollView::_InnerFrame() const
795 {
796 	BRect frame = Bounds();
797 	_InsetBorders(frame, fBorder, fBorders);
798 
799 	float borderSize = _BorderSize();
800 
801 	if (fHorizontalScrollBar != NULL) {
802 		frame.bottom -= B_H_SCROLL_BAR_HEIGHT;
803 		if (borderSize == 0)
804 			frame.bottom--;
805 	}
806 	if (fVerticalScrollBar != NULL) {
807 		frame.right -= B_V_SCROLL_BAR_WIDTH;
808 		if (borderSize == 0)
809 			frame.right--;
810 	}
811 
812 	return frame;
813 }
814 
815 
816 BSize
817 BScrollView::_ComputeSize(BSize targetSize) const
818 {
819 	BRect frame = _ComputeFrame(
820 		BRect(0, 0, targetSize.width, targetSize.height));
821 
822 	return BSize(frame.Width(), frame.Height());
823 }
824 
825 
826 BRect
827 BScrollView::_ComputeFrame(BRect targetRect) const
828 {
829 	return _ComputeFrame(targetRect, fHorizontalScrollBar != NULL,
830 		fVerticalScrollBar != NULL, fBorder, fBorders);
831 }
832 
833 
834 void
835 BScrollView::_AlignScrollBars(bool horizontal, bool vertical, BRect targetFrame)
836 {
837 	if (horizontal) {
838 		BRect rect = targetFrame;
839 		rect.top = rect.bottom + 1;
840 		rect.bottom = rect.top + B_H_SCROLL_BAR_HEIGHT;
841 		if (fBorder != B_NO_BORDER || vertical) {
842 			// extend scrollbar so that it overlaps one pixel with vertical
843 			// scrollbar
844 			rect.right++;
845 		}
846 
847 		if (fBorder != B_NO_BORDER) {
848 			// the scrollbar draws part of the surrounding frame on the left
849 			rect.left--;
850 		}
851 
852 		fHorizontalScrollBar->MoveTo(rect.left, rect.top);
853 		fHorizontalScrollBar->ResizeTo(rect.Width(), rect.Height());
854 	}
855 
856 	if (vertical) {
857 		BRect rect = targetFrame;
858 		rect.left = rect.right + 1;
859 		rect.right = rect.left + B_V_SCROLL_BAR_WIDTH;
860 		if (fBorder != B_NO_BORDER || horizontal) {
861 			// extend scrollbar so that it overlaps one pixel with vertical
862 			// scrollbar
863 			rect.bottom++;
864 		}
865 
866 		if (fBorder != B_NO_BORDER) {
867 			// the scrollbar draws part of the surrounding frame on the left
868 			rect.top--;
869 		}
870 
871 		fVerticalScrollBar->MoveTo(rect.left, rect.top);
872 		fVerticalScrollBar->ResizeTo(rect.Width(), rect.Height());
873 	}
874 }
875 
876 
877 /*!	This static method is used to calculate the frame that the
878 	ScrollView will cover depending on the frame of its target
879 	and which border style is used.
880 	It is used in the constructor and at other places.
881 */
882 /*static*/ BRect
883 BScrollView::_ComputeFrame(BRect frame, bool horizontal, bool vertical,
884 	border_style border, uint32 borders)
885 {
886 	if (vertical)
887 		frame.right += B_V_SCROLL_BAR_WIDTH;
888 	if (horizontal)
889 		frame.bottom += B_H_SCROLL_BAR_HEIGHT;
890 
891 	_InsetBorders(frame, border, borders, true);
892 
893 	if (_BorderSize(border) == 0) {
894 		if (vertical)
895 			frame.right++;
896 		if (horizontal)
897 			frame.bottom++;
898 	}
899 
900 	return frame;
901 }
902 
903 
904 /*static*/ BRect
905 BScrollView::_ComputeFrame(BView *target, bool horizontal, bool vertical,
906 	border_style border, uint32 borders)
907 {
908 	return _ComputeFrame(target != NULL ? target->Frame()
909 		: BRect(0, 0, 16, 16), horizontal, vertical, border, borders);
910 }
911 
912 
913 /*! This method returns the size of the specified border.
914 */
915 /*static*/ float
916 BScrollView::_BorderSize(border_style border)
917 {
918 	if (border == B_FANCY_BORDER)
919 		return kFancyBorderSize;
920 	if (border == B_PLAIN_BORDER)
921 		return kPlainBorderSize;
922 
923 	return 0;
924 }
925 
926 
927 /*!	This method changes the "flags" argument as passed on to
928 	the BView constructor.
929 */
930 /*static*/ int32
931 BScrollView::_ModifyFlags(int32 flags, border_style border)
932 {
933 	// We either need B_FULL_UPDATE_ON_RESIZE or
934 	// B_FRAME_EVENTS if we have to draw a border
935 	if (border != B_NO_BORDER)
936 		return flags | B_WILL_DRAW | (flags & B_FULL_UPDATE_ON_RESIZE ? 0 : B_FRAME_EVENTS);
937 
938 	return flags & ~(B_WILL_DRAW | B_FRAME_EVENTS | B_FULL_UPDATE_ON_RESIZE);
939 }
940 
941 
942 /*static*/ void
943 BScrollView::_InsetBorders(BRect& frame, border_style border, uint32 borders, bool expand)
944 {
945 	float borderSize = _BorderSize(border);
946 	if (expand)
947 		borderSize = -borderSize;
948 	if ((borders & BControlLook::B_LEFT_BORDER) != 0)
949 		frame.left += borderSize;
950 	if ((borders & BControlLook::B_TOP_BORDER) != 0)
951 		frame.top += borderSize;
952 	if ((borders & BControlLook::B_RIGHT_BORDER) != 0)
953 		frame.right -= borderSize;
954 	if ((borders & BControlLook::B_BOTTOM_BORDER) != 0)
955 		frame.bottom -= borderSize;
956 }
957 
958 
959 //	#pragma mark - FBC and forbidden
960 
961 
962 BScrollView&
963 BScrollView::operator=(const BScrollView &)
964 {
965 	return *this;
966 }
967 
968 
969 void BScrollView::_ReservedScrollView1() {}
970 void BScrollView::_ReservedScrollView2() {}
971 void BScrollView::_ReservedScrollView3() {}
972 void BScrollView::_ReservedScrollView4() {}
973 
974 
975 extern "C" void
976 B_IF_GCC_2(InvalidateLayout__11BScrollViewb,
977 	_ZN11BScrollView16InvalidateLayoutEb)(BScrollView* view, bool descendants)
978 {
979 	perform_data_layout_invalidated data;
980 	data.descendants = descendants;
981 
982 	view->Perform(PERFORM_CODE_LAYOUT_INVALIDATED, &data);
983 }
984