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
25static const float kFancyBorderSize = 2;
26static const float kPlainBorderSize = 1;
27
28
29BScrollView::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
40BScrollView::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
51BScrollView::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
97BScrollView::~BScrollView()
98{
99}
100
101
102// #pragma mark - Archiving
103
104
105BArchivable*
106BScrollView::Instantiate(BMessage* archive)
107{
108	if (validate_instantiation(archive, "BScrollView"))
109		return new BScrollView(archive);
110
111	return NULL;
112}
113
114
115status_t
116BScrollView::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
138status_t
139BScrollView::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
185void
186BScrollView::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
215void
216BScrollView::DetachedFromWindow()
217{
218	BView::DetachedFromWindow();
219}
220
221
222void
223BScrollView::AllAttached()
224{
225	BView::AllAttached();
226}
227
228
229void
230BScrollView::AllDetached()
231{
232	BView::AllDetached();
233}
234
235
236void
237BScrollView::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
260void
261BScrollView::FrameMoved(BPoint newPosition)
262{
263	BView::FrameMoved(newPosition);
264}
265
266
267void
268BScrollView::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
352void
353BScrollView::MessageReceived(BMessage* message)
354{
355	BView::MessageReceived(message);
356}
357
358
359void
360BScrollView::MouseDown(BPoint where)
361{
362	BView::MouseDown(where);
363}
364
365
366void
367BScrollView::MouseMoved(BPoint where, uint32 code,
368	const BMessage* dragMessage)
369{
370	BView::MouseMoved(where, code, dragMessage);
371}
372
373
374void
375BScrollView::MouseUp(BPoint where)
376{
377	BView::MouseUp(where);
378}
379
380
381void
382BScrollView::WindowActivated(bool active)
383{
384	if (fHighlighted)
385		Invalidate();
386
387	BView::WindowActivated(active);
388}
389
390
391// #pragma mark - Size methods
392
393
394void
395BScrollView::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
407void
408BScrollView::ResizeToPreferred()
409{
410	if (Window() == NULL)
411		return;
412	BView::ResizeToPreferred();
413}
414
415
416void
417BScrollView::MakeFocus(bool focus)
418{
419	BView::MakeFocus(focus);
420}
421
422
423BSize
424BScrollView::MinSize()
425{
426	BSize size = _ComputeSize(fTarget != NULL ? fTarget->MinSize()
427		: BSize(16, 16));
428
429	return BLayoutUtils::ComposeSize(ExplicitMinSize(), size);
430}
431
432
433BSize
434BScrollView::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
443BSize
444BScrollView::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
456BScrollBar*
457BScrollView::ScrollBar(orientation direction) const
458{
459	if (direction == B_HORIZONTAL)
460		return fHorizontalScrollBar;
461
462	return fVerticalScrollBar;
463}
464
465
466void
467BScrollView::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
526border_style
527BScrollView::Border() const
528{
529	return fBorder;
530}
531
532
533void
534BScrollView::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
545uint32
546BScrollView::Borders() const
547{
548	return fBorders;
549}
550
551
552status_t
553BScrollView::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
583bool
584BScrollView::IsBorderHighlighted() const
585{
586	return fHighlighted;
587}
588
589
590void
591BScrollView::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
628BView*
629BScrollView::Target() const
630{
631	return fTarget;
632}
633
634
635// #pragma mark - Scripting methods
636
637
638
639BHandler*
640BScrollView::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
647status_t
648BScrollView::GetSupportedSuites(BMessage* message)
649{
650	return BView::GetSupportedSuites(message);
651}
652
653
654//	#pragma mark - Perform
655
656
657status_t
658BScrollView::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
724void
725BScrollView::LayoutInvalidated(bool descendants)
726{
727}
728
729
730void
731BScrollView::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
759void
760BScrollView::_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
819float
820BScrollView::_BorderSize() const
821{
822	return _BorderSize(fBorder);
823}
824
825
826BRect
827BScrollView::_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
849BSize
850BScrollView::_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
859BRect
860BScrollView::_ComputeFrame(BRect targetRect) const
861{
862	return _ComputeFrame(targetRect, fHorizontalScrollBar,
863		fVerticalScrollBar, fBorder, fBorders);
864}
865
866
867void
868BScrollView::_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
916BScrollView::_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
948BScrollView::_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
959BScrollView::_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
974BScrollView::_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
992BScrollView::_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
1011BScrollView&
1012BScrollView::operator=(const BScrollView &)
1013{
1014	return *this;
1015}
1016
1017
1018void BScrollView::_ReservedScrollView1() {}
1019void BScrollView::_ReservedScrollView2() {}
1020void BScrollView::_ReservedScrollView3() {}
1021void BScrollView::_ReservedScrollView4() {}
1022
1023
1024extern "C" void
1025B_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