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