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