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(_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
42BScrollView::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
53BScrollView::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
99BScrollView::~BScrollView()
100{
101}
102
103
104// #pragma mark - Archiving
105
106
107BArchivable*
108BScrollView::Instantiate(BMessage* archive)
109{
110	if (validate_instantiation(archive, "BScrollView"))
111		return new BScrollView(archive);
112
113	return NULL;
114}
115
116
117status_t
118BScrollView::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
140status_t
141BScrollView::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
187void
188BScrollView::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
217void
218BScrollView::DetachedFromWindow()
219{
220	BView::DetachedFromWindow();
221}
222
223
224void
225BScrollView::AllAttached()
226{
227	BView::AllAttached();
228}
229
230
231void
232BScrollView::AllDetached()
233{
234	BView::AllDetached();
235}
236
237
238void
239BScrollView::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
262void
263BScrollView::FrameMoved(BPoint newPosition)
264{
265	BView::FrameMoved(newPosition);
266}
267
268
269void
270BScrollView::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
354void
355BScrollView::MessageReceived(BMessage* message)
356{
357	BView::MessageReceived(message);
358}
359
360
361void
362BScrollView::MouseDown(BPoint where)
363{
364	BView::MouseDown(where);
365}
366
367
368void
369BScrollView::MouseMoved(BPoint where, uint32 code,
370	const BMessage* dragMessage)
371{
372	BView::MouseMoved(where, code, dragMessage);
373}
374
375
376void
377BScrollView::MouseUp(BPoint where)
378{
379	BView::MouseUp(where);
380}
381
382
383void
384BScrollView::WindowActivated(bool active)
385{
386	if (fHighlighted)
387		Invalidate();
388
389	BView::WindowActivated(active);
390}
391
392
393// #pragma mark - Size methods
394
395
396void
397BScrollView::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
409void
410BScrollView::ResizeToPreferred()
411{
412	if (Window() == NULL)
413		return;
414	BView::ResizeToPreferred();
415}
416
417
418void
419BScrollView::MakeFocus(bool focus)
420{
421	BView::MakeFocus(focus);
422}
423
424
425BSize
426BScrollView::MinSize()
427{
428	BSize size = _ComputeSize(fTarget != NULL ? fTarget->MinSize()
429		: BSize(16, 16));
430
431	return BLayoutUtils::ComposeSize(ExplicitMinSize(), size);
432}
433
434
435BSize
436BScrollView::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
445BSize
446BScrollView::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
458BScrollBar*
459BScrollView::ScrollBar(orientation direction) const
460{
461	if (direction == B_HORIZONTAL)
462		return fHorizontalScrollBar;
463
464	return fVerticalScrollBar;
465}
466
467
468void
469BScrollView::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
528border_style
529BScrollView::Border() const
530{
531	return fBorder;
532}
533
534
535void
536BScrollView::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
547uint32
548BScrollView::Borders() const
549{
550	return fBorders;
551}
552
553
554status_t
555BScrollView::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
585bool
586BScrollView::IsBorderHighlighted() const
587{
588	return fHighlighted;
589}
590
591
592void
593BScrollView::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
630BView*
631BScrollView::Target() const
632{
633	return fTarget;
634}
635
636
637// #pragma mark - Scripting methods
638
639
640
641BHandler*
642BScrollView::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
649status_t
650BScrollView::GetSupportedSuites(BMessage* message)
651{
652	return BView::GetSupportedSuites(message);
653}
654
655
656//	#pragma mark - Perform
657
658
659status_t
660BScrollView::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
726void
727BScrollView::LayoutInvalidated(bool descendants)
728{
729}
730
731
732void
733BScrollView::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
761void
762BScrollView::_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
814float
815BScrollView::_BorderSize() const
816{
817	return _BorderSize(fBorder);
818}
819
820
821BRect
822BScrollView::_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
844BSize
845BScrollView::_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
854BRect
855BScrollView::_ComputeFrame(BRect targetRect) const
856{
857	return _ComputeFrame(targetRect, fHorizontalScrollBar != NULL,
858		fVerticalScrollBar != NULL, fBorder, fBorders);
859}
860
861
862void
863BScrollView::_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
911BScrollView::_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
933BScrollView::_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
944BScrollView::_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
959BScrollView::_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
977BScrollView::_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
996BScrollView&
997BScrollView::operator=(const BScrollView &)
998{
999	return *this;
1000}
1001
1002
1003void BScrollView::_ReservedScrollView1() {}
1004void BScrollView::_ReservedScrollView2() {}
1005void BScrollView::_ReservedScrollView3() {}
1006void BScrollView::_ReservedScrollView4() {}
1007
1008
1009extern "C" void
1010B_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