⛏️ index : haiku.git

/*
 * Copyright 2005-2015, Haiku Inc. All Rights Reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *		Stefano Ceccherini (stefano.ceccherini@gmail.com)
 *		Stephan Aßmus <superstippi@gmx.de>
 */

#include <ChannelSlider.h>

#include <new>

#include <Bitmap.h>
#include <ControlLook.h>
#include <Debug.h>
#include <PropertyInfo.h>
#include <Screen.h>
#include <Window.h>

#include <binary_compatibility/Support.h>


const static unsigned char
kVerticalKnobData[] = {
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
	0xff, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0xff,
	0xff, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0xff,
	0xff, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0x12,
	0xff, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0x12,
	0xff, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0x12,
	0xff, 0x00, 0x3f, 0x3f, 0xcb, 0xcb, 0xcb, 0xcb, 0x3f, 0x3f, 0x00, 0x12,
	0xff, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0x12,
	0xff, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0x12,
	0xff, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0x12,
	0xff, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0x12,
	0xff, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0x12,
	0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x12,
	0xff, 0xff, 0xff, 0xff, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0xff
};


const static unsigned char
kHorizontalKnobData[] = {
	0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
	0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0xff, 0xff, 0xff, 0x00, 0x3f, 0x3f,
	0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0xff, 0xff,
	0xff, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0xcb, 0x3f, 0x3f, 0x3f, 0x3f,
	0x3f, 0x00, 0x12, 0xff, 0xff, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0xcb,
	0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0x12, 0xff, 0xff, 0x00, 0x3f, 0x3f,
	0x3f, 0x3f, 0x3f, 0xcb, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0x12, 0xff,
	0xff, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0xcb, 0x3f, 0x3f, 0x3f, 0x3f,
	0x3f, 0x00, 0x12, 0xff, 0xff, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
	0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0x12, 0xff, 0xff, 0x00, 0x3f, 0x3f,
	0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0x12, 0xff,
	0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x0c, 0x12, 0xff, 0xff, 0xff, 0xff, 0xff, 0x12, 0x12, 0x12, 0x12,
	0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};


static property_info
sPropertyInfo[] = {
	{ "Orientation",
		{ B_GET_PROPERTY, B_SET_PROPERTY, 0 },
		{ B_DIRECT_SPECIFIER, 0 }, NULL, 0, { B_INT32_TYPE }
	},

	{ 0 }
};


const static float kPadding = 3.0;


BChannelSlider::BChannelSlider(BRect area, const char* name, const char* label,
	BMessage* model, int32 channels, uint32 resizeMode, uint32 flags)
	: BChannelControl(area, name, label, model, channels, resizeMode, flags)
{
	_InitData();
}


BChannelSlider::BChannelSlider(BRect area, const char* name, const char* label,
	BMessage* model, orientation orientation, int32 channels,
		uint32 resizeMode, uint32 flags)
	: BChannelControl(area, name, label, model, channels, resizeMode, flags)

{
	_InitData();
	SetOrientation(orientation);
}


BChannelSlider::BChannelSlider(const char* name, const char* label,
	BMessage* model, orientation orientation, int32 channels,
		uint32 flags)
	: BChannelControl(name, label, model, channels, flags)

{
	_InitData();
	SetOrientation(orientation);
}


BChannelSlider::BChannelSlider(BMessage* archive)
	: BChannelControl(archive)
{
	_InitData();

	orientation orient;
	if (archive->FindInt32("_orient", (int32*)&orient) == B_OK)
		SetOrientation(orient);
}


BChannelSlider::~BChannelSlider()
{
	delete fBacking;
	delete fLeftKnob;
	delete fMidKnob;
	delete fRightKnob;
	delete[] fInitialValues;
}


BArchivable*
BChannelSlider::Instantiate(BMessage* archive)
{
	if (validate_instantiation(archive, "BChannelSlider"))
		return new (std::nothrow) BChannelSlider(archive);

	return NULL;
}


status_t
BChannelSlider::Archive(BMessage* into, bool deep) const
{
	status_t status = BChannelControl::Archive(into, deep);
	if (status == B_OK)
		status = into->AddInt32("_orient", (int32)Orientation());

	return status;
}


void
BChannelSlider::AttachedToWindow()
{
	AdoptParentColors();
	BChannelControl::AttachedToWindow();
}


void
BChannelSlider::AllAttached()
{
	BChannelControl::AllAttached();
}


void
BChannelSlider::DetachedFromWindow()
{
	BChannelControl::DetachedFromWindow();
}


void
BChannelSlider::AllDetached()
{
	BChannelControl::AllDetached();
}


void
BChannelSlider::MessageReceived(BMessage* message)
{
	if (message->what == B_COLORS_UPDATED
		&& fBacking != NULL && fBackingView != NULL) {
		rgb_color color;
		if (message->FindColor(ui_color_name(B_PANEL_BACKGROUND_COLOR), &color)
				== B_OK
			&& fBacking->Lock()) {

			if (fBackingView->LockLooper()) {
				fBackingView->SetLowColor(color);
				fBackingView->UnlockLooper();
			}
			fBacking->Unlock();
		}
	}

	switch (message->what) {
		case B_SET_PROPERTY: {
		case B_GET_PROPERTY:
			BMessage reply(B_REPLY);
			int32 index = 0;
			BMessage specifier;
			int32 what = 0;
			const char* property = NULL;
			bool handled = false;
			status_t status = message->GetCurrentSpecifier(&index, &specifier,
				&what, &property);
			BPropertyInfo propInfo(sPropertyInfo);
			if (status == B_OK
				&& propInfo.FindMatch(message, index, &specifier, what,
					property) >= 0) {
				handled = true;
				if (message->what == B_SET_PROPERTY) {
					orientation orient;
					if (specifier.FindInt32("data", (int32*)&orient) == B_OK) {
						SetOrientation(orient);
						Invalidate(Bounds());
					}
				} else if (message->what == B_GET_PROPERTY)
					reply.AddInt32("result", (int32)Orientation());
				else
					status = B_BAD_SCRIPT_SYNTAX;
			}

			if (handled) {
				reply.AddInt32("error", status);
				message->SendReply(&reply);
			} else {
				BChannelControl::MessageReceived(message);
			}
		}	break;

		default:
			BChannelControl::MessageReceived(message);
			break;
	}
}


void
BChannelSlider::Draw(BRect updateRect)
{
	_UpdateFontDimens();
	_DrawThumbs();

	SetHighColor(ui_color(B_PANEL_TEXT_COLOR));
	BRect bounds(Bounds());
	if (Label()) {
		float labelWidth = StringWidth(Label());
		DrawString(Label(), BPoint((bounds.Width() - labelWidth) / 2.0,
			fBaseLine));
	}

	if (MinLimitLabel()) {
		if (fIsVertical) {
			if (MinLimitLabel()) {
				float x = (bounds.Width() - StringWidth(MinLimitLabel()))
					/ 2.0;
				DrawString(MinLimitLabel(), BPoint(x, bounds.bottom
					- kPadding));
			}
		} else {
			if (MinLimitLabel()) {
				DrawString(MinLimitLabel(), BPoint(kPadding, bounds.bottom
					- kPadding));
			}
		}
	}

	if (MaxLimitLabel()) {
		if (fIsVertical) {
			if (MaxLimitLabel()) {
				float x = (bounds.Width() - StringWidth(MaxLimitLabel()))
					/ 2.0;
				DrawString(MaxLimitLabel(), BPoint(x, 2 * fLineFeed));
			}
		} else {
			if (MaxLimitLabel()) {
				DrawString(MaxLimitLabel(), BPoint(bounds.right - kPadding
					- StringWidth(MaxLimitLabel()), bounds.bottom - kPadding));
			}
		}
	}
}


void
BChannelSlider::MouseDown(BPoint where)
{
	if (!IsEnabled())
		BControl::MouseDown(where);
	else {
		fCurrentChannel = -1;
		fMinPoint = 0;

		// Search the channel on which the mouse was over
		int32 numChannels = CountChannels();
		for (int32 channel = 0; channel < numChannels; channel++) {
			BRect frame = ThumbFrameFor(channel);
			frame.OffsetBy(fClickDelta);

			float range = ThumbRangeFor(channel);
			if (fIsVertical) {
				fMinPoint = frame.top + frame.Height() / 2;
				frame.bottom += range;
			} else {
				// TODO: Fix this, the clickzone isn't perfect
				frame.right += range;
				fMinPoint = frame.Width();
			}

			// Click was on a slider.
			if (frame.Contains(where)) {
				fCurrentChannel = channel;
				SetCurrentChannel(channel);
				break;
			}
		}

		// Click wasn't on a slider. Bail out.
		if (fCurrentChannel == -1)
			return;

		uint32 buttons = 0;
		BMessage* currentMessage = Window()->CurrentMessage();
		if (currentMessage != NULL)
			currentMessage->FindInt32("buttons", (int32*)&buttons);

		fAllChannels = (buttons & B_SECONDARY_MOUSE_BUTTON) == 0;

		if (fInitialValues != NULL && fAllChannels) {
			delete[] fInitialValues;
			fInitialValues = NULL;
		}

		if (fInitialValues == NULL)
			fInitialValues = new (std::nothrow) int32[numChannels];

		if (fInitialValues) {
			if (fAllChannels) {
				for (int32 i = 0; i < numChannels; i++)
					fInitialValues[i] = ValueFor(i);
			} else {
				fInitialValues[fCurrentChannel] = ValueFor(fCurrentChannel);
			}
		}

		if (Window()->Flags() & B_ASYNCHRONOUS_CONTROLS) {
			if (!IsTracking()) {
				SetTracking(true);
				_DrawThumbs();
				Flush();
			}

			_MouseMovedCommon(where, B_ORIGIN);
			SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS |
				B_NO_POINTER_HISTORY);
		} else {
			do {
				snooze(30000);
				GetMouse(&where, &buttons);
				_MouseMovedCommon(where, B_ORIGIN);
			} while (buttons != 0);
			_FinishChange();
			fCurrentChannel = -1;
			fAllChannels = false;
		}
	}
}


void
BChannelSlider::MouseUp(BPoint where)
{
	if (IsEnabled() && IsTracking()) {
		_FinishChange();
		SetTracking(false);
		fAllChannels = false;
		fCurrentChannel = -1;
		fMinPoint = 0;
	} else {
		BChannelControl::MouseUp(where);
	}
}


void
BChannelSlider::MouseMoved(BPoint where, uint32 code, const BMessage* message)
{
	if (IsEnabled() && IsTracking())
		_MouseMovedCommon(where, B_ORIGIN);
	else
		BChannelControl::MouseMoved(where, code, message);
}


void
BChannelSlider::WindowActivated(bool state)
{
	BChannelControl::WindowActivated(state);
}


void
BChannelSlider::KeyDown(const char* bytes, int32 numBytes)
{
	BControl::KeyDown(bytes, numBytes);
}


void
BChannelSlider::KeyUp(const char* bytes, int32 numBytes)
{
	BView::KeyUp(bytes, numBytes);
}


void
BChannelSlider::FrameResized(float newWidth, float newHeight)
{
	BChannelControl::FrameResized(newWidth, newHeight);

	delete fBacking;
	fBacking = NULL;

	Invalidate(Bounds());
}


void
BChannelSlider::SetFont(const BFont* font, uint32 mask)
{
	BChannelControl::SetFont(font, mask);
}


void
BChannelSlider::MakeFocus(bool focusState)
{
	if (focusState && !IsFocus())
		fFocusChannel = -1;
	BChannelControl::MakeFocus(focusState);
}


void
BChannelSlider::GetPreferredSize(float* width, float* height)
{
	_UpdateFontDimens();

	if (fIsVertical) {
		*width = 11.0 * CountChannels();
		*width = max_c(*width, ceilf(StringWidth(Label())));
		*width = max_c(*width, ceilf(StringWidth(MinLimitLabel())));
		*width = max_c(*width, ceilf(StringWidth(MaxLimitLabel())));
		*width += kPadding * 2.0;

		*height = (fLineFeed * 3.0) + (kPadding * 2.0) + 147.0;
	} else {
		*width = max_c(64.0, ceilf(StringWidth(Label())));
		*width = max_c(*width, ceilf(StringWidth(MinLimitLabel()))
			+ ceilf(StringWidth(MaxLimitLabel())) + 10.0);
		*width += kPadding * 2.0;

		*height = 11.0 * CountChannels() + (fLineFeed * 2.0)
			+ (kPadding * 2.0);
	}
}


BHandler*
BChannelSlider::ResolveSpecifier(BMessage* message, int32 index,
	BMessage* specifier, int32 form, const char* property)
{
	BHandler* target = this;
	BPropertyInfo propertyInfo(sPropertyInfo);
	if (propertyInfo.FindMatch(message, index, specifier, form,
		property) != B_OK) {
		target = BChannelControl::ResolveSpecifier(message, index, specifier,
			form, property);
	}
	return target;
}


status_t
BChannelSlider::GetSupportedSuites(BMessage* data)
{
	if (data == NULL)
		return B_BAD_VALUE;

	status_t err = data->AddString("suites", "suite/vnd.Be-channel-slider");

	BPropertyInfo propInfo(sPropertyInfo);
	if (err == B_OK)
		err = data->AddFlat("messages", &propInfo);

	if (err == B_OK)
		return BChannelControl::GetSupportedSuites(data);
	return err;
}


void
BChannelSlider::SetEnabled(bool on)
{
	BChannelControl::SetEnabled(on);
}


orientation
BChannelSlider::Orientation() const
{
	return fIsVertical ? B_VERTICAL : B_HORIZONTAL;
}


void
BChannelSlider::SetOrientation(orientation orientation)
{
	bool isVertical = orientation == B_VERTICAL;
	if (isVertical != fIsVertical) {
		fIsVertical = isVertical;
		InvalidateLayout();
		Invalidate(Bounds());
	}
}


int32
BChannelSlider::MaxChannelCount() const
{
	return 32;
}


bool
BChannelSlider::SupportsIndividualLimits() const
{
	return false;
}


void
BChannelSlider::DrawChannel(BView* into, int32 channel, BRect area,
	bool pressed)
{
	float hCenter = area.Width() / 2;
	float vCenter = area.Height() / 2;

	BPoint leftTop;
	BPoint bottomRight;
	if (fIsVertical) {
		leftTop.Set(area.left + hCenter, area.top + vCenter);
		bottomRight.Set(leftTop.x, leftTop.y + ThumbRangeFor(channel));
	} else {
		leftTop.Set(area.left, area.top + vCenter);
		bottomRight.Set(area.left + ThumbRangeFor(channel), leftTop.y);
	}

	DrawGroove(into, channel, leftTop, bottomRight);

	BPoint thumbLocation = leftTop;
	if (fIsVertical)
		thumbLocation.y += ThumbDeltaFor(channel);
	else
		thumbLocation.x += ThumbDeltaFor(channel);

	DrawThumb(into, channel, thumbLocation, pressed);
}


void
BChannelSlider::DrawGroove(BView* into, int32 channel, BPoint leftTop,
	BPoint bottomRight)
{
	ASSERT(into != NULL);
	BRect rect(leftTop, bottomRight);

	rect.InsetBy(-2.5, -2.5);
	rect.left = floorf(rect.left);
	rect.top = floorf(rect.top);
	rect.right = floorf(rect.right);
	rect.bottom = floorf(rect.bottom);
	rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR);
	rgb_color barColor = be_control_look->SliderBarColor(base);
	uint32 flags = 0;
	be_control_look->DrawSliderBar(into, rect, rect, base,
		barColor, flags, Orientation());
}


void
BChannelSlider::DrawThumb(BView* into, int32 channel, BPoint where,
	bool pressed)
{
	ASSERT(into != NULL);

	const BBitmap* thumb = ThumbFor(channel, pressed);
	if (thumb == NULL)
		return;

	BRect bitmapBounds(thumb->Bounds());
	where.x -= bitmapBounds.right / 2.0;
	where.y -= bitmapBounds.bottom / 2.0;

	BRect rect(bitmapBounds.OffsetToCopy(where));
	rect.InsetBy(1, 1);
	rect.left = floorf(rect.left);
	rect.top = floorf(rect.top);
	rect.right = ceilf(rect.right + 0.5);
	rect.bottom = ceilf(rect.bottom + 0.5);
	rgb_color base = ui_color(B_CONTROL_BACKGROUND_COLOR);
	uint32 flags = 0;
	be_control_look->DrawSliderThumb(into, rect, rect, base,
		flags, Orientation());
}


const BBitmap*
BChannelSlider::ThumbFor(int32 channel, bool pressed)
{
	if (fLeftKnob != NULL)
		return fLeftKnob;

	if (fIsVertical) {
		fLeftKnob = new (std::nothrow) BBitmap(BRect(0, 0, 11, 14),
			B_CMAP8);
		if (fLeftKnob != NULL) {
			fLeftKnob->SetBits(kVerticalKnobData,
					sizeof(kVerticalKnobData), 0, B_CMAP8);
		}
	} else {
		fLeftKnob = new (std::nothrow) BBitmap(BRect(0, 0, 14, 11),
			B_CMAP8);
		if (fLeftKnob != NULL) {
			fLeftKnob->SetBits(kHorizontalKnobData,
					sizeof(kHorizontalKnobData), 0, B_CMAP8);
		}
	}

	return fLeftKnob;
}


BRect
BChannelSlider::ThumbFrameFor(int32 channel)
{
	_UpdateFontDimens();

	BRect frame(0.0, 0.0, 0.0, 0.0);
	const BBitmap* thumb = ThumbFor(channel, false);
	if (thumb != NULL) {
		frame = thumb->Bounds();
		if (fIsVertical) {
			frame.OffsetBy(channel * frame.Width(), (frame.Height() / 2.0) -
				(kPadding * 2.0) - 1.0);
		} else {
			frame.OffsetBy(frame.Width() / 2.0, channel * frame.Height()
				+ 1.0);
		}
	}
	return frame;
}


float
BChannelSlider::ThumbDeltaFor(int32 channel)
{
	float delta = 0.0;
	if (channel >= 0 && channel < MaxChannelCount()) {
		float range = ThumbRangeFor(channel);
		int32 limitRange = MaxLimitList()[channel] - MinLimitList()[channel];
		delta = (ValueList()[channel] - MinLimitList()[channel]) * range
			/ limitRange;

		if (fIsVertical)
			delta = range - delta;
	}

	return delta;
}


float
BChannelSlider::ThumbRangeFor(int32 channel)
{
	_UpdateFontDimens();

	float range = 0;
	BRect bounds = Bounds();
	BRect frame = ThumbFrameFor(channel);
	if (fIsVertical) {
		// *height = (fLineFeed * 3.0) + (kPadding * 2.0) + 100.0;
		range = bounds.Height() - frame.Height() - (fLineFeed * 3.0) -
			(kPadding * 2.0);
	} else {
		// *width = some width + kPadding * 2.0;
		range = bounds.Width() - frame.Width() - (kPadding * 2.0);
	}
	return range;
}


void
BChannelSlider::UpdateToolTip(int32 currentValue)
{
	BString valueString;
	valueString.SetToFormat("%" B_PRId32, currentValue);
	SetToolTip(valueString);
}


// #pragma mark -


void
BChannelSlider::_InitData()
{
	_UpdateFontDimens();

	fLeftKnob = NULL;
	fMidKnob = NULL;
	fRightKnob = NULL;
	fBacking = NULL;
	fBackingView = NULL;
	fIsVertical = Bounds().Width() / Bounds().Height() < 1;
	fClickDelta = B_ORIGIN;

	fCurrentChannel = -1;
	fAllChannels = false;
	fInitialValues = NULL;
	fMinPoint = 0;
	fFocusChannel = -1;
}


void
BChannelSlider::_FinishChange(bool update)
{
	if (fInitialValues != NULL) {
		bool* inMask = NULL;
		int32 numChannels = CountChannels();
		if (!fAllChannels) {
			inMask = new (std::nothrow) bool[CountChannels()];
			if (inMask) {
				for (int i = 0; i < numChannels; i++)
					inMask[i] = false;
				inMask[fCurrentChannel] = true;
			}
		}
		InvokeChannel(update ? ModificationMessage() : NULL, 0, numChannels,
			inMask);

		delete[] inMask;
	}

	if (!update) {
		SetTracking(false);
		Invalidate();
	}
}


void
BChannelSlider::_UpdateFontDimens()
{
	font_height height;
	GetFontHeight(&height);
	fBaseLine = height.ascent + height.leading;
	fLineFeed = fBaseLine + height.descent;
}


void
BChannelSlider::_DrawThumbs()
{
	if (fBacking == NULL) {
		// This is the idea: we build a bitmap by taking the coordinates
		// of the first and last thumb frames (top/left and bottom/right)
		BRect first = ThumbFrameFor(0);
		BRect last = ThumbFrameFor(CountChannels() - 1);
		BRect rect(first.LeftTop(), last.RightBottom());

		if (fIsVertical)
			rect.top -= ThumbRangeFor(0);
		else
			rect.right += ThumbRangeFor(0);

		rect.OffsetTo(B_ORIGIN);
		fBacking = new (std::nothrow) BBitmap(rect, B_RGB32, true);
		if (fBacking) {
			fBackingView = new (std::nothrow) BView(rect, "", 0, B_WILL_DRAW);
			if (fBackingView) {
				if (fBacking->Lock()) {
					fBacking->AddChild(fBackingView);
					fBackingView->SetFontSize(10.0);
					fBackingView->SetLowColor(
						ui_color(B_PANEL_BACKGROUND_COLOR));
					fBackingView->SetViewColor(
						ui_color(B_PANEL_BACKGROUND_COLOR));
					fBacking->Unlock();
				}
			} else {
				delete fBacking;
				fBacking = NULL;
			}
		}
	}

	if (fBacking && fBackingView) {
		BPoint drawHere;

		BRect bounds(fBacking->Bounds());
		drawHere.x = (Bounds().Width() - bounds.Width()) / 2.0;
		drawHere.y = (Bounds().Height() - bounds.Height()) - kPadding
			- fLineFeed;

		if (fBacking->Lock()) {
			// Clear the view's background
			fBackingView->FillRect(fBackingView->Bounds(), B_SOLID_LOW);

			BRect channelArea;
			// draw the entire control
			for (int32 channel = 0; channel < CountChannels(); channel++) {
				channelArea = ThumbFrameFor(channel);
				bool pressed = IsTracking()
					&& (channel == fCurrentChannel || fAllChannels);
				DrawChannel(fBackingView, channel, channelArea, pressed);
			}

			// draw some kind of current value tool tip
			if (fCurrentChannel != -1 && fMinPoint != 0) {
				UpdateToolTip(ValueFor(fCurrentChannel));
				ShowToolTip(ToolTip());
			} else {
				HideToolTip();
			}

			fBackingView->Sync();
			fBacking->Unlock();
		}

		DrawBitmapAsync(fBacking, drawHere);

		// fClickDelta is used in MouseMoved()
		fClickDelta = drawHere;
	}
}


void
BChannelSlider::_DrawGrooveFrame(BView* into, const BRect& area)
{
	if (into) {
		rgb_color oldColor = into->HighColor();

		into->SetHighColor(255, 255, 255);
		into->StrokeRect(area);
		into->SetHighColor(tint_color(into->ViewColor(), B_DARKEN_1_TINT));
		into->StrokeLine(area.LeftTop(), BPoint(area.right, area.top));
		into->StrokeLine(area.LeftTop(), BPoint(area.left, area.bottom - 1));
		into->SetHighColor(tint_color(into->ViewColor(), B_DARKEN_2_TINT));
		into->StrokeLine(BPoint(area.left + 1, area.top + 1),
			BPoint(area.right - 1, area.top + 1));
		into->StrokeLine(BPoint(area.left + 1, area.top + 1),
			BPoint(area.left + 1, area.bottom - 2));

		into->SetHighColor(oldColor);
	}
}


void
BChannelSlider::_MouseMovedCommon(BPoint point, BPoint point2)
{
	float floatValue = 0;
	int32 limitRange = MaxLimitList()[fCurrentChannel] -
			MinLimitList()[fCurrentChannel];
	float range = ThumbRangeFor(fCurrentChannel);
	if (fIsVertical)
		floatValue = range - (point.y - fMinPoint);
	else
		floatValue = range + (point.x - fMinPoint);

	int32 value = (int32)(floatValue / range * limitRange) +
		MinLimitList()[fCurrentChannel];
	if (fAllChannels)
		SetAllValue(value);
	else
		SetValueFor(fCurrentChannel, value);

	if (ModificationMessage()) 
		_FinishChange(true);

	_DrawThumbs();
}


// #pragma mark - FBC padding


void BChannelSlider::_Reserved_BChannelSlider_1(void*, ...) {}
void BChannelSlider::_Reserved_BChannelSlider_2(void*, ...) {}
void BChannelSlider::_Reserved_BChannelSlider_3(void*, ...) {}
void BChannelSlider::_Reserved_BChannelSlider_4(void*, ...) {}
void BChannelSlider::_Reserved_BChannelSlider_5(void*, ...) {}
void BChannelSlider::_Reserved_BChannelSlider_6(void*, ...) {}
void BChannelSlider::_Reserved_BChannelSlider_7(void*, ...) {}


//	#pragma mark - binary compatibility


extern "C" void
B_IF_GCC_2(_Reserved_BChannelSlider_0__14BChannelSliderPve,
	_ZN14BChannelSlider26_Reserved_BChannelSlider_0EPvz)(
	BChannelSlider* channelSlider, int32 currentValue)
{
	channelSlider->BChannelSlider::UpdateToolTip(currentValue);
}