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