⛏️ index : haiku.git

/*
 * Copyright 2003-2023 Haiku, Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *		Stephan Aßmus, superstippi@gmx.de
 *		DarkWyrm, bpmagic@columbus.rr.com
 *		Marc Flerackers, mflerackers@androme.be
 *		François Revol, revol@free.fr
 *		John Scipione, jscipione@gmail.com
 *		Clemens Zeidler, haiku@clemens-zeidler.de
 */


/*! BControlLook resembling BeOS R5 */


#include "BeControlLook.h"

#include <algorithm>

#include <Alignment.h>
#include <Bitmap.h>
#include <Button.h>
#include <Control.h>
#include <LayoutUtils.h>
#include <Region.h>
#include <Shape.h>
#include <String.h>
#include <TabView.h>
#include <View.h>
#include <Window.h>
#include <WindowPrivate.h>

//#define DEBUG_CONTROL_LOOK
#ifdef DEBUG_CONTROL_LOOK
#  define STRACE(x) printf x
#else
#  define STRACE(x) ;
#endif


namespace BPrivate {

static const float kButtonPopUpIndicatorWidth = 11;

static const rgb_color kBlack = { 0, 0, 0, 255 };
static const rgb_color kWhite = { 255, 255, 255, 255 };


BeControlLook::BeControlLook(image_id id)
	:
	fCachedOutline(false)
{
}


BeControlLook::~BeControlLook()
{
}


BAlignment
BeControlLook::DefaultLabelAlignment() const
{
	return BAlignment(B_ALIGN_LEFT, B_ALIGN_VERTICAL_CENTER);
}


float
BeControlLook::DefaultLabelSpacing() const
{
	return ceilf(be_plain_font->Size() / 2.0);
}


float
BeControlLook::DefaultItemSpacing() const
{
	return ceilf(be_plain_font->Size() * 0.85);
}


uint32
BeControlLook::Flags(BControl* control) const
{
	uint32 flags = B_IS_CONTROL;

	if (!control->IsEnabled())
		flags |= B_DISABLED;

	if (control->IsFocus() && control->Window() != NULL
		&& control->Window()->IsActive()) {
		flags |= B_FOCUSED;
	}

	switch (control->Value()) {
		case B_CONTROL_ON:
			flags |= B_ACTIVATED;
			break;
		case B_CONTROL_PARTIALLY_ON:
			flags |= B_PARTIALLY_ACTIVATED;
			break;
	}

	if (control->Parent() != NULL
		&& (control->Parent()->Flags() & B_DRAW_ON_CHILDREN) != 0) {
		// In this constellation, assume we want to render the control
		// against the already existing view contents of the parent view.
		flags |= B_BLEND_FRAME;
	}

	return flags;
}


void
BeControlLook::DrawButtonFrame(BView* view, BRect& rect,
	const BRect& updateRect, const rgb_color& base,
	const rgb_color& background, uint32 flags, uint32 borders)
{
	_DrawButtonFrame(view, rect, updateRect, 0, 0, 0, 0, base,
		background, 1.0, 1.0, flags, borders);
}


void
BeControlLook::DrawButtonFrame(BView* view, BRect& rect,
	const BRect& updateRect, float, const rgb_color& base,
	const rgb_color& background, uint32 flags, uint32 borders)
{
	_DrawButtonFrame(view, rect, updateRect, 0, 0, 0, 0, base,
		background, 1.0, 1.0, flags, borders);
}


void
BeControlLook::DrawButtonFrame(BView* view, BRect& rect,
	const BRect& updateRect, float, float, float, float, const rgb_color& base,
	const rgb_color& background, uint32 flags, uint32 borders)
{
	_DrawButtonFrame(view, rect, updateRect, 0, 0, 0, 0, base,
		background, 1.0, 1.0, flags, borders);
}


void
BeControlLook::DrawButtonBackground(BView* view, BRect& rect,
	const BRect& updateRect, const rgb_color& base, uint32 flags,
	uint32 borders, orientation orientation)
{
	_DrawButtonBackground(view, rect, updateRect, 0, 0, 0, 0,
		base, false, flags, borders, orientation);
}


void
BeControlLook::DrawButtonBackground(BView* view, BRect& rect,
	const BRect& updateRect, float, const rgb_color& base, uint32 flags,
	uint32 borders, orientation orientation)
{
	_DrawButtonBackground(view, rect, updateRect, 0, 0, 0, 0,
		base, false, flags, borders, orientation);
}


void
BeControlLook::DrawButtonBackground(BView* view, BRect& rect,
	const BRect& updateRect, float, float, float, float, const rgb_color& base,
	uint32 flags, uint32 borders, orientation orientation)
{
	_DrawButtonBackground(view, rect, updateRect, 0, 0, 0, 0,
		base, false, flags, borders, orientation);
}


void
BeControlLook::DrawCheckBox(BView* view, BRect& rect, const BRect& updateRect,
	const rgb_color& base, uint32 flags)
{
	if (!rect.Intersects(updateRect))
		return;

	bool isEnabled = (flags & B_DISABLED) == 0;
	bool isActivated = (flags & B_ACTIVATED) != 0;
	bool isFocused = (flags & B_FOCUSED) != 0;
	bool isClicked = (flags & B_CLICKED) != 0;

	rgb_color lighten1 = tint_color(base, B_LIGHTEN_1_TINT);
	rgb_color lightenMax = tint_color(base, B_LIGHTEN_MAX_TINT);
	rgb_color darken1 = tint_color(base, B_DARKEN_1_TINT);
	rgb_color darken2 = tint_color(base, B_DARKEN_2_TINT);
	rgb_color darken3 = tint_color(base, B_DARKEN_3_TINT);
	rgb_color darken4 = tint_color(base, B_DARKEN_4_TINT);

	view->SetLowColor(base);

	if (isEnabled) {
		// Filling
		view->SetHighColor(lightenMax);
		view->FillRect(rect);

		// Box
		if (isClicked) {
			view->SetHighColor(darken3);
			view->StrokeRect(rect);

			rect.InsetBy(1, 1);

			view->BeginLineArray(6);

			view->AddLine(BPoint(rect.left, rect.bottom),
				BPoint(rect.left, rect.top), darken2);
			view->AddLine(BPoint(rect.left, rect.top),
				BPoint(rect.right, rect.top), darken2);
			view->AddLine(BPoint(rect.left, rect.bottom),
				BPoint(rect.right, rect.bottom), darken4);
			view->AddLine(BPoint(rect.right, rect.bottom),
				BPoint(rect.right, rect.top), darken4);

			view->EndLineArray();
		} else {
			view->BeginLineArray(6);

			view->AddLine(BPoint(rect.left, rect.bottom),
				BPoint(rect.left, rect.top), darken1);
			view->AddLine(BPoint(rect.left, rect.top),
				BPoint(rect.right, rect.top), darken1);

			rect.InsetBy(1, 1);
			view->AddLine(BPoint(rect.left, rect.bottom),
				BPoint(rect.left, rect.top), darken4);
			view->AddLine(BPoint(rect.left, rect.top),
				BPoint(rect.right, rect.top), darken4);
			view->AddLine(BPoint(rect.left + 1, rect.bottom),
				BPoint(rect.right, rect.bottom), base);
			view->AddLine(BPoint(rect.right, rect.bottom),
				BPoint(rect.right, rect.top + 1), base);

			view->EndLineArray();
		}

		// Focus
		if (isFocused) {
			view->SetDrawingMode(B_OP_OVER);
			view->SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR));
			view->StrokeRect(rect);
			view->SetDrawingMode(B_OP_COPY);
		}
	} else {
		// Filling
		view->SetHighColor(lighten1);
		view->FillRect(rect);

		// Box
		view->BeginLineArray(6);

		view->AddLine(BPoint(rect.left, rect.bottom),
				BPoint(rect.left, rect.top), base);
		view->AddLine(BPoint(rect.left, rect.top),
				BPoint(rect.right, rect.top), base);

		rect.InsetBy(1, 1);
		view->AddLine(BPoint(rect.left, rect.bottom),
				BPoint(rect.left, rect.top), darken2);
		view->AddLine(BPoint(rect.left, rect.top),
				BPoint(rect.right, rect.top), darken2);
		view->AddLine(BPoint(rect.left + 1, rect.bottom),
				BPoint(rect.right, rect.bottom), darken1);
		view->AddLine(BPoint(rect.right, rect.bottom),
				BPoint(rect.right, rect.top + 1), darken1);

		view->EndLineArray();
	}

	// Checkmark
	if (isActivated) {
		rect.InsetBy(4, 4);

		float penSize = std::max(1, 2);
		if (penSize > 1 && fmodf(penSize, 2) == 0) {
			// Tweak ends to "include" the pixel at the index,
			// we need to do this in order to produce results like R5,
			// where coordinates were inclusive
			rect.right++;
			rect.bottom++;
		}

		view->SetPenSize(penSize);
		view->SetDrawingMode(B_OP_OVER);
		if (isEnabled)
			view->SetHighColor(ui_color(B_CONTROL_MARK_COLOR));
		else {
			view->SetHighColor(tint_color(ui_color(B_CONTROL_MARK_COLOR),
				B_DISABLED_MARK_TINT));
		}
		view->StrokeLine(rect.LeftTop(), rect.RightBottom());
		view->StrokeLine(rect.LeftBottom(), rect.RightTop());
	}
}


void
BeControlLook::DrawRadioButton(BView* view, BRect& rect,
	const BRect& updateRect, const rgb_color& base, uint32 flags)
{
	if (!ShouldDraw(view, rect, updateRect))
		return;

	bool isEnabled = (flags & B_DISABLED) == 0;
	bool isActivated = (flags & B_ACTIVATED) != 0;
	bool isFocused = (flags & B_FOCUSED) != 0;

	// colors
	rgb_color bg = ui_color(B_PANEL_BACKGROUND_COLOR);
	rgb_color lightenmax;
	rgb_color lighten1;
	rgb_color darken1;
	rgb_color darken2;
	rgb_color darken3;

	rgb_color markColor = ui_color(B_CONTROL_MARK_COLOR);
	rgb_color knob;
	rgb_color knobDark;
	rgb_color knobLight;

	if (isEnabled) {
		lightenmax	= tint_color(bg, B_LIGHTEN_MAX_TINT);
		lighten1	= tint_color(bg, B_LIGHTEN_1_TINT);
		darken1		= tint_color(bg, B_DARKEN_1_TINT);
		darken2		= tint_color(bg, B_DARKEN_2_TINT);
		darken3		= tint_color(bg, B_DARKEN_3_TINT);

		knob		= markColor;
		knobDark	= tint_color(markColor, B_DARKEN_3_TINT);
		knobLight	= tint_color(markColor, 0.15);
	} else {
		lightenmax	= tint_color(bg, B_LIGHTEN_2_TINT);
		lighten1	= bg;
		darken1		= bg;
		darken2		= tint_color(bg, B_DARKEN_1_TINT);
		darken3		= tint_color(bg, B_DARKEN_2_TINT);

		knob		= tint_color(markColor, B_LIGHTEN_2_TINT);
		knobDark	= tint_color(markColor, B_LIGHTEN_1_TINT);
		knobLight	= tint_color(markColor,
			(B_LIGHTEN_2_TINT + B_LIGHTEN_MAX_TINT) / 2.0);
	}

	rect.InsetBy(2, 2);

	view->SetLowColor(bg);

	// dot
	if (isActivated) {
		// full
		view->SetHighColor(knobDark);
		view->FillEllipse(rect);

		view->SetHighColor(knob);
		view->FillEllipse(BRect(rect.left + 2, rect.top + 2, rect.right - 3,
			rect.bottom - 3));

		view->SetHighColor(knobLight);
		view->FillEllipse(BRect(rect.left + 3, rect.top + 3, rect.right - 5,
			rect.bottom - 5));
	} else {
		// empty
		view->SetHighColor(lightenmax);
		view->FillEllipse(rect);
	}

	rect.InsetBy(-1, -1);

	// outer circle
	if (isFocused) {
		// indicating "about to change value"
		view->SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR));
		view->SetPenSize(2);
		view->SetDrawingMode(B_OP_OVER);
		view->StrokeEllipse(rect.InsetByCopy(1, 1));
		view->SetDrawingMode(B_OP_COPY);
		view->SetPenSize(1);
	} else {
		view->SetHighColor(darken1);
		view->StrokeArc(rect, 45.0, 180.0);
		view->SetHighColor(lightenmax);
		view->StrokeArc(rect, 45.0, -180.0);
	}

	// inner circle
	view->SetHighColor(darken3);
	view->StrokeArc(rect, 45.0, 180.0);
	view->SetHighColor(bg);
	view->StrokeArc(rect, 45.0, -180.0);

	// for faster font rendering, we restore B_OP_COPY
	view->SetDrawingMode(B_OP_COPY);
}


void
BeControlLook::DrawScrollBarBorder(BView* view, BRect rect,
	const BRect& updateRect, const rgb_color& base, uint32 flags,
	orientation orientation)
{
	if (!ShouldDraw(view, rect, updateRect))
		return;

	view->PushState();

	view->ClipToRect(rect);

	bool isEnabled = (flags & B_DISABLED) == 0;
	bool isFocused = (flags & B_FOCUSED) != 0;

	if (isEnabled && isFocused)
		view->SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR));
	else
		view->SetHighColor(tint_color(base, B_DARKEN_2_TINT));

	view->StrokeRect(rect);

	view->PopState();
}


void
BeControlLook::DrawScrollBarButton(BView* view, BRect rect,
	const BRect& updateRect, const rgb_color& base, const rgb_color& text,
	uint32 flags, int32 direction, orientation orientation, bool down)
{
	view->PushState();

	bool isEnabled = (flags & B_DISABLED) == 0;

	// border = 152, shine = 144/255, shadow = 208/184/192
	rgb_color shine = down ? tint_color(base, 1.333)
		: tint_color(base, B_LIGHTEN_MAX_TINT);
	rgb_color shadow;
	if (isEnabled && down)
		shadow = tint_color(base, 1.037);
	else if (isEnabled)
		shadow = tint_color(base, B_DARKEN_1_TINT);
	else
		shadow = tint_color(base, 1.111);

	view->BeginLineArray(4);
	view->AddLine(rect.LeftTop(), rect.LeftBottom() - BPoint(0, 1), shine);
	view->AddLine(rect.LeftTop(), rect.RightTop() - BPoint(1, 0), shine);
	view->AddLine(rect.RightTop(), rect.RightBottom() - BPoint(0, 1), shadow);
	view->AddLine(rect.LeftBottom(), rect.RightBottom(), shadow);
	view->EndLineArray();

	rgb_color bg;
	if (isEnabled) {
		// bg = 176/216
		bg = down ? tint_color(base, 1.185) : base;
	} else {
		// bg = 240
		rgb_color lighten2 = tint_color(base, B_LIGHTEN_2_TINT);
		lighten2.red++; lighten2.green++; lighten2.blue++;
			// lighten2 = 239, 240 = 239 + 1
		bg = lighten2;
	}
	view->SetHighColor(bg);
	rect.InsetBy(1, 1);
	view->FillRect(rect);

	// draw button triangle
	// don't use DrawArrowShape because we use that to draw arrows differently
	// in menus and outline list view

	rect.InsetBy(1, 1);
	rect.OffsetBy(-3, -3);

	BPoint tri1, tri2, tri3;
	BPoint off1, off2, off3;
	BRect r(rect.left, rect.top, rect.left + 14, rect.top + 14);
	rgb_color lightenmax = tint_color(base, B_LIGHTEN_MAX_TINT);
	rgb_color light, dark, arrow, arrow2;

	switch(direction) {
		case B_LEFT_ARROW:
			tri1.Set(r.left + 3, floorf((r.top + r.bottom) / 2));
			tri2.Set(r.right - 4, r.top + 4);
			tri3.Set(r.right - 4, r.bottom - 4);
			break;

		default:
		case B_RIGHT_ARROW:
			tri1.Set(r.left + 4, r.bottom - 4);
			tri2.Set(r.left + 4, r.top + 4);
			tri3.Set(r.right - 3, floorf((r.top + r.bottom) / 2));
			break;

		case B_UP_ARROW:
			tri1.Set(r.left + 4, r.bottom - 4);
			tri2.Set(floorf((r.left + r.right) / 2), r.top + 3);
			tri3.Set(r.right - 4, r.bottom - 4);
			break;

		case B_DOWN_ARROW:
			tri1.Set(r.left + 4, r.top + 4);
			tri2.Set(r.right - 4, r.top + 4);
			tri3.Set(floorf((r.left + r.right) / 2), r.bottom - 3);
			break;
	}

	r.InsetBy(1, 1);

	float tint = B_NO_TINT;
	if (!isEnabled)
		tint = (tint + B_NO_TINT + B_NO_TINT) / 3;

	view->SetHighColor(tint_color(base, tint));
	view->MovePenTo(B_ORIGIN);
	view->SetDrawingMode(B_OP_OVER);

	view->SetHighColor(tint_color(base, tint));

	if (isEnabled) {
		arrow2 = light = tint_color(base, B_DARKEN_2_TINT);
		dark = tint_color(base, B_DARKEN_3_TINT);
		arrow = tint_color(base, B_DARKEN_MAX_TINT);
	} else
		arrow = arrow2 = light = dark = tint_color(base, B_DARKEN_1_TINT);

	// white triangle offset by 1px
	off1.Set(tri1.x + 1, tri1.y + 1);
	off2.Set(tri2.x + 1, tri2.y + 1);
	off3.Set(tri3.x + 1, tri3.y + 1);

	// draw white triangle
	view->BeginLineArray(3);
	view->AddLine(off2, off3, lightenmax);
	view->AddLine(off1, off3, lightenmax);
	view->AddLine(off1, off2, lightenmax);
	view->EndLineArray();

	// draw triangle on top
	view->BeginLineArray(3);
	view->AddLine(tri2, tri3, dark);
	view->AddLine(tri1, tri3, dark);
	view->AddLine(tri1, tri2, arrow2);
	view->EndLineArray();

	view->PopState();
}


void
BeControlLook::DrawScrollBarBackground(BView* view, BRect& rect1, BRect& rect2,
	const BRect& updateRect, const rgb_color& base, uint32 flags,
	orientation orientation)
{
	_DrawScrollBarBackgroundFirst(view, rect1, updateRect, base, flags,
		orientation);
	_DrawScrollBarBackgroundSecond(view, rect2, updateRect, base, flags,
		orientation);
}


void
BeControlLook::DrawScrollBarBackground(BView* view, BRect& rect,
	const BRect& updateRect, const rgb_color& base, uint32 flags,
	orientation orientation)
{
	_DrawScrollBarBackgroundFirst(view, rect, updateRect, base, flags,
		orientation);
}


void
BeControlLook::DrawScrollBarThumb(BView* view, BRect& rect,
	const BRect& updateRect, const rgb_color& base, uint32 flags,
	orientation orientation, uint32 knobStyle)
{
	if (!ShouldDraw(view, rect, updateRect))
		return;

	view->PushState();

	view->ClipToRect(rect);

	bool isEnabled = (flags & B_DISABLED) == 0;

	BRect orig(rect);

	// shine = 255
	rgb_color shine = tint_color(base, B_LIGHTEN_MAX_TINT);
	rgb_color bg;
	if (isEnabled) {
		// bg = 216
		bg = base;
	} else {
		// bg = 240
		rgb_color lighten2 = tint_color(base, B_LIGHTEN_2_TINT);
		lighten2.red++; lighten2.green++; lighten2.blue++;
			// lighten2 = 239, 240 = 239 + 1
		bg = lighten2;
	}

	// draw thumb over background
	view->SetDrawingMode(B_OP_OVER);

	view->BeginLineArray(2);
	if (orientation == B_VERTICAL) {
		// shine
		view->AddLine(rect.LeftTop(), rect.LeftBottom(), shine);
		rect.left++;
		view->AddLine(rect.LeftTop(), rect.RightTop(), shine);
		rect.top++;
	} else {
		// shine
		view->AddLine(rect.LeftTop(), rect.LeftBottom(), shine);
		rect.left++;
		view->AddLine(rect.LeftTop(), rect.RightTop(), shine);
		rect.top++;
	}
	view->EndLineArray();

	// fill bg
	view->SetHighColor(bg);
	view->FillRect(rect);

	// undraw right top or left bottom point
	view->BeginLineArray(1);
	if (orientation == B_VERTICAL) {
		rect.right--;
		view->AddLine(rect.RightTop(), rect.RightTop(), base);
	} else {
		rect.bottom--;
		view->AddLine(rect.LeftBottom(), rect.LeftBottom(), base);
	}
	view->EndLineArray();

	// restore rect
	rect = orig;

	// knobs

	if (knobStyle != B_KNOB_NONE) {
		float hcenter = rect.left + rect.Width() / 2;
		float vmiddle = rect.top + rect.Height() / 2;
		rgb_color knobDark = tint_color(base, B_DARKEN_1_TINT);
		rgb_color knobLight = tint_color(base, B_LIGHTEN_MAX_TINT);

		if (knobStyle == B_KNOB_DOTS) {
			// center/middle dot
			_DrawScrollBarKnobDot(view, hcenter, vmiddle, knobDark, knobLight,
				orientation);
			if (orientation == B_HORIZONTAL) {
				float spacer = rect.Height();
				// left dot
				if (rect.left + 7 < hcenter - spacer) {
					_DrawScrollBarKnobDot(view, hcenter - 7, vmiddle, knobDark,
						knobLight, orientation);
				}
				// right dot
				if (rect.right - 7 > hcenter + spacer) {
					_DrawScrollBarKnobDot(view, hcenter + 7, vmiddle, knobDark,
						knobLight, orientation);
				}
			} else {
				float spacer = rect.Width();
				// top dot
				if (rect.top + 7 < vmiddle - spacer) {
					_DrawScrollBarKnobDot(view, hcenter, vmiddle - 7, knobDark,
						knobLight, orientation);
				}
				// bottom dot
				if (rect.bottom - 7 > vmiddle + spacer) {
					_DrawScrollBarKnobDot(view, hcenter, vmiddle + 7, knobDark,
						knobLight, orientation);
				}
			}
		} else if (knobStyle == B_KNOB_LINES) {
			// center/middle line
			_DrawScrollBarKnobLine(view, hcenter, vmiddle, knobDark, knobLight,
				orientation);
			if (orientation == B_HORIZONTAL) {
				float spacer = rect.Height();
				// left line
				if (rect.left + 4 < hcenter - spacer) {
					_DrawScrollBarKnobLine(view, hcenter - 4, vmiddle, knobDark,
						knobLight, orientation);
				}
				// right line
				if (rect.right - 4 > hcenter + spacer) {
					_DrawScrollBarKnobLine(view, hcenter + 4, vmiddle, knobDark,
						knobLight, orientation);
				}
			} else {
				float spacer = rect.Width();
				// top line
				if (rect.top + 4 < vmiddle - spacer) {
					_DrawScrollBarKnobLine(view, hcenter, vmiddle - 4, knobDark,
						knobLight, orientation);
				}
				// bottom line
				if (rect.bottom - 5 > vmiddle + spacer) {
					_DrawScrollBarKnobLine(view, hcenter, vmiddle + 4, knobDark,
						knobLight, orientation);
				}
			}
		}
	}

	view->PopState();
}



void
BeControlLook::DrawScrollViewFrame(BView* view, BRect& rect,
	const BRect& updateRect, BRect verticalScrollBarFrame,
	BRect horizontalScrollBarFrame, const rgb_color& base,
	border_style borderStyle, uint32 flags, uint32 _borders)
{
	rgb_color darken1 = tint_color(base, B_DARKEN_1_TINT);
	rgb_color lightenmax = tint_color(base, B_LIGHTEN_MAX_TINT);

	view->BeginLineArray(4);
	view->AddLine(rect.LeftBottom(), rect.LeftTop(), darken1);
	view->AddLine(rect.LeftTop(), rect.RightTop(), darken1);
	view->AddLine(rect.RightTop(), rect.RightBottom(), lightenmax);
	view->AddLine(rect.RightBottom(), rect.LeftBottom(), lightenmax);
	view->EndLineArray();

	rect.InsetBy(1, 1);

	bool isEnabled = (flags & B_DISABLED) == 0;
	bool isFocused = (flags & B_FOCUSED) != 0;

	if (isEnabled && isFocused)
		view->SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR));
	else
		view->SetHighColor(tint_color(base, B_DARKEN_2_TINT));

	view->StrokeRect(rect);
}


void
BeControlLook::DrawArrowShape(BView* view, BRect& rect, const BRect& updateRect,
	const rgb_color& base, uint32 direction, uint32 flags, float tint)
{
	if (!ShouldDraw(view, rect, updateRect))
		return;

	view->PushState();

	rgb_color fill = tint_color(base, 1.074); // 200
	rgb_color stroke = tint_color(base, 1.629); // 80

	switch(direction) {
		case B_LEFT_ARROW:
			view->SetHighColor(fill);
			view->FillTriangle(rect.LeftTop() + BPoint(4, 6),
				rect.LeftTop() + BPoint(8, 2),
				rect.LeftTop() + BPoint(8, 10));
			view->SetHighColor(stroke);
			view->StrokeTriangle(rect.LeftTop() + BPoint(4, 6),
				rect.LeftTop() + BPoint(8, 2),
				rect.LeftTop() + BPoint(8, 10));
			break;

		default:
		case B_RIGHT_ARROW:
			view->SetHighColor(fill);
			view->FillTriangle(rect.LeftTop() + BPoint(4, 2),
				rect.LeftTop() + BPoint(4, 10),
				rect.LeftTop() + BPoint(8, 6));
			view->SetHighColor(stroke);
			view->StrokeTriangle(rect.LeftTop() + BPoint(4, 2),
				rect.LeftTop() + BPoint(4, 10),
				rect.LeftTop() + BPoint(8, 6));
			break;

		case B_UP_ARROW:
			view->SetHighColor(fill);
			view->FillTriangle(rect.LeftTop() + BPoint(6, 4),
				rect.LeftTop() + BPoint(2, 8),
				rect.LeftTop() + BPoint(10, 8));
			view->SetHighColor(stroke);
			view->StrokeTriangle(rect.LeftTop() + BPoint(6, 4),
				rect.LeftTop() + BPoint(2, 8),
				rect.LeftTop() + BPoint(10, 8));
			break;

		case B_DOWN_ARROW:
			view->SetHighColor(fill);
			view->FillTriangle(rect.LeftTop() + BPoint(2, 4),
				rect.LeftTop() + BPoint(10, 4),
				rect.LeftTop() + BPoint(6, 8));
			view->SetHighColor(stroke);
			view->StrokeTriangle(rect.LeftTop() + BPoint(2, 4),
				rect.LeftTop() + BPoint(10, 4),
				rect.LeftTop() + BPoint(6, 8));
			break;
	}

	view->PopState();
}


void
BeControlLook::DrawMenuBarBackground(BView* view, BRect& rect,
	const BRect& updateRect, const rgb_color& base, uint32 flags,
	uint32 borders)
{
	if (!ShouldDraw(view, rect, updateRect))
		return;

	view->PushState();

	// restore the background color in case a menu item was selected
	view->SetHighColor(base);
	view->FillRect(rect);

	rgb_color lighten2 = tint_color(base, B_LIGHTEN_2_TINT);
	rgb_color darken1 = tint_color(base, B_DARKEN_1_TINT);

	view->BeginLineArray(3);
	view->AddLine(rect.LeftTop(), rect.RightTop(), lighten2);
	// left bottom pixel is base color
	view->AddLine(rect.LeftTop(), rect.LeftBottom() - BPoint(0, 1),
		lighten2);
	view->AddLine(rect.LeftBottom() + BPoint(1, 0), rect.RightBottom(),
		darken1);
	view->EndLineArray();

	view->PopState();
}


void
BeControlLook::DrawMenuFieldFrame(BView* view, BRect& rect,
	const BRect& updateRect, const rgb_color& base,
	const rgb_color& background, uint32 flags, uint32 borders)
{
	// BeControlLook does not support rounded corners and it never will
	DrawMenuFieldFrame(view, rect, updateRect, 0, base, background, flags,
		borders);
}


void
BeControlLook::DrawMenuFieldFrame(BView* view, BRect& rect,
	const BRect& updateRect, float, const rgb_color& base,
	const rgb_color& background, uint32 flags, uint32 borders)
{
	// BeControlLook does not support rounded corners and it never will
	DrawMenuFieldFrame(view, rect, updateRect, 0, 0, 0, 0, base,
		background, flags, borders);
}


void
BeControlLook::DrawMenuFieldFrame(BView* view, BRect& rect,
	const BRect& updateRect, float, float, float, float, const rgb_color& base,
	const rgb_color& background, uint32 flags, uint32 borders)
{
	if (!ShouldDraw(view, rect, updateRect))
		return;

	bool isEnabled = (flags & B_DISABLED) == 0;
	bool isFocused = (flags & B_FOCUSED) != 0;

	// inset the frame by 2 and draw the outer border
	rect.InsetBy(2, 2);

	rgb_color darken2 = tint_color(base, B_DARKEN_2_TINT);

	// draw left and top side and top right corner
	view->BeginLineArray(3);
	view->AddLine(BPoint(rect.left - 1, rect.top - 1),
		BPoint(rect.left - 1, rect.bottom - 1), darken2);
	view->AddLine(BPoint(rect.left - 1, rect.top - 1),
		BPoint(rect.right - 1, rect.top - 1), darken2);
	view->AddLine(BPoint(rect.right, rect.top - 1),
		BPoint(rect.right, rect.top - 1), darken2);
	view->EndLineArray();

	if (isEnabled && isFocused) {
		// draw the focus ring on top of the frame
		// Note that this is an intentional deviation from BeOS R5
		// which draws the frame around the outside of the frame
		// but that doesn't look as good.
		view->SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR));
		view->StrokeRect(rect.InsetByCopy(-1, -1));
	}
}


void
BeControlLook::DrawMenuFieldBackground(BView* view, BRect& rect,
	const BRect& updateRect, const rgb_color& base, bool popupIndicator,
	uint32 flags)
{
	_DrawMenuFieldBackgroundOutside(view, rect, updateRect,
		0, 0, 0, 0, base, popupIndicator, flags);
}


void
BeControlLook::DrawMenuFieldBackground(BView* view, BRect& rect,
	const BRect& updateRect, const rgb_color& base, uint32 flags,
	uint32 borders)
{
	_DrawMenuFieldBackgroundInside(view, rect, updateRect,
		0, 0, 0, 0, base, flags, borders);
}


void
BeControlLook::DrawMenuFieldBackground(BView* view, BRect& rect,
	const BRect& updateRect, float, const rgb_color& base,
	bool popupIndicator, uint32 flags)
{
	_DrawMenuFieldBackgroundOutside(view, rect, updateRect, 0, 0,
		0, 0, base, popupIndicator, flags);
}


void
BeControlLook::DrawMenuFieldBackground(BView* view, BRect& rect,
	const BRect& updateRect, float, float, float, float, const rgb_color& base,
	bool popupIndicator, uint32 flags)
{
	_DrawMenuFieldBackgroundOutside(view, rect, updateRect, 0, 0,
		0, 0, base, popupIndicator, flags);
}


void
BeControlLook::DrawMenuBackground(BView* view, BRect& rect,
	const BRect& updateRect, const rgb_color& base, uint32 flags,
	uint32 borders)
{
	if (!ShouldDraw(view, rect, updateRect))
		return;

	view->PushState();

	view->SetHighColor(base);
	view->FillRect(rect);

	view->PopState();
}


void
BeControlLook::DrawMenuItemBackground(BView* view, BRect& rect,
	const BRect& updateRect, const rgb_color& base, uint32 flags,
	uint32 borders)
{
	if (!ShouldDraw(view, rect, updateRect))
		return;

	view->PushState();

	view->SetHighColor(base);
	view->FillRect(rect);

	view->PopState();
}


void
BeControlLook::DrawStatusBar(BView* view, BRect& rect, const BRect& updateRect,
	const rgb_color& base, const rgb_color& barColor, float progressPosition)
{
	if (!ShouldDraw(view, rect, updateRect))
		return;

	view->PushState();

	// draw background
	view->SetHighColor(base);
	view->FillRect(rect);

	// draw frame around bar
	DrawTextControlBorder(view, rect, updateRect, tint_color(base, B_DARKEN_1_TINT));

	// filled and nonfilled rects
	BRect filledRect(rect);
	filledRect.right = progressPosition - 1;
	BRect nonfilledRect(rect);
	nonfilledRect.left = progressPosition;

	bool drawFilled = filledRect.Width() > 0;
	bool drawNonfilled = nonfilledRect.Width() > 0;

	// filled
	if (drawFilled) {
		rgb_color light = tint_color(barColor, 0.6);
		rgb_color dark = tint_color(barColor, 1.4);
		_DrawFrame(view, filledRect, light, light, dark, dark);

		view->SetHighColor(barColor);
		view->FillRect(filledRect);
	}

	// nonfilled
	if (drawNonfilled) {
		view->SetHighColor(ui_color(B_CONTROL_BACKGROUND_COLOR));
		view->FillRect(nonfilledRect);
	}

	view->PopState();
}


rgb_color
BeControlLook::SliderBarColor(const rgb_color& base)
{
	return tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), B_DARKEN_1_TINT);
}


void
BeControlLook::DrawSliderBar(BView* view, BRect rect, const BRect& updateRect,
	const rgb_color& base, rgb_color leftFillColor, rgb_color rightFillColor,
	float sliderScale, uint32 flags, orientation orientation)
{
	if (!ShouldDraw(view, rect, updateRect))
		return;

	// separate the bar in two sides
	float sliderPosition;
	BRect leftBarSide = rect;
	BRect rightBarSide = rect;

	if (orientation == B_HORIZONTAL) {
		sliderPosition = floorf(rect.left + 2 + (rect.Width() - 2)
			* sliderScale);
		leftBarSide.right = sliderPosition - 1;
		rightBarSide.left = sliderPosition;
	} else {
		// NOTE: position is reverse of coords
		sliderPosition = floorf(rect.top + 2 + (rect.Height() - 2)
			* (1.0 - sliderScale));
		leftBarSide.top = sliderPosition;
		rightBarSide.bottom = sliderPosition - 1;
	}

	view->PushState();
	view->ClipToRect(leftBarSide);

	DrawSliderBar(view, rect, updateRect, base, leftFillColor, flags,
		orientation);
	view->PopState();

	view->PushState();
	view->ClipToRect(rightBarSide);

	DrawSliderBar(view, rect, updateRect, base, rightFillColor, flags,
		orientation);
	view->PopState();
}


void
BeControlLook::DrawSliderBar(BView* view, BRect rect, const BRect& updateRect,
	const rgb_color& base, rgb_color fillColor, uint32 flags,
	orientation orientation)
{
	if (!ShouldDraw(view, rect, updateRect))
		return;

	view->SetHighColor(fillColor);
	view->FillRect(rect);

	rgb_color lightenmax = tint_color(base, B_LIGHTEN_MAX_TINT);
	rgb_color darken1 = tint_color(base, B_DARKEN_1_TINT);
	rgb_color darken2 = tint_color(base, B_DARKEN_2_TINT);
	rgb_color darkenmax = tint_color(base, B_DARKEN_MAX_TINT);

	view->BeginLineArray(9);

	view->AddLine(BPoint(rect.left, rect.top),
		BPoint(rect.left + 1, rect.top), darken1);
	view->AddLine(BPoint(rect.left, rect.bottom),
		BPoint(rect.left + 1, rect.bottom), darken1);
	view->AddLine(BPoint(rect.right - 1, rect.top),
		BPoint(rect.right, rect.top), darken1);

	view->AddLine(BPoint(rect.left + 1, rect.top),
		BPoint(rect.right - 1, rect.top), darken2);
	view->AddLine(BPoint(rect.left, rect.bottom - 1),
		BPoint(rect.left, rect.top + 1), darken2);

	view->AddLine(BPoint(rect.left + 1, rect.bottom),
		BPoint(rect.right, rect.bottom), lightenmax);
	view->AddLine(BPoint(rect.right, rect.top + 1),
		BPoint(rect.right, rect.bottom), lightenmax);

	rect.InsetBy(1, 1);

	view->AddLine(BPoint(rect.left, rect.top),
		BPoint(rect.left, rect.bottom), darkenmax);
	view->AddLine(BPoint(rect.left, rect.top),
		BPoint(rect.right, rect.top), darkenmax);

	view->EndLineArray();
}


void
BeControlLook::DrawSliderThumb(BView* view, BRect& rect, const BRect& updateRect,
	const rgb_color& base, uint32 flags, orientation orientation)
{
	if (!ShouldDraw(view, rect, updateRect))
		return;

	rgb_color lighten2 = tint_color(base, B_LIGHTEN_2_TINT);
	rgb_color dark = tint_color(base, 1.333); // 144
	rgb_color darker = tint_color(base, 1.444); // 120
	rgb_color darkenmax = tint_color(base, B_DARKEN_MAX_TINT);

	view->BeginLineArray(14);

	// outline
	view->AddLine(BPoint(rect.left, rect.bottom - 2),
		BPoint(rect.left, rect.top + 1), darker);
	view->AddLine(BPoint(rect.left + 1, rect.top),
		BPoint(rect.right - 2, rect.top), darker);
	view->AddLine(BPoint(rect.right, rect.top + 2),
		BPoint(rect.right, rect.bottom - 1), darker);
	view->AddLine(BPoint(rect.left + 2, rect.bottom),
		BPoint(rect.right - 1, rect.bottom), darker);

	// first bevel
	rect.InsetBy(1, 1);

	view->SetHighColor(lighten2);
	view->FillRect(rect);

	view->AddLine(BPoint(rect.left, rect.bottom),
		BPoint(rect.right - 1, rect.bottom), darkenmax);
	view->AddLine(BPoint(rect.right, rect.bottom - 1),
		BPoint(rect.right, rect.top), darkenmax);

	rect.InsetBy(1, 1);

	// second bevel and center dots
	view->SetHighColor(dark);
	view->AddLine(BPoint(rect.left, rect.bottom),
		BPoint(rect.right, rect.bottom), dark);
	view->AddLine(BPoint(rect.right, rect.top),
		BPoint(rect.right, rect.bottom), dark);

	// center dots
	float hCenter = rect.Width() / 2;
	float vMiddle = rect.Height() / 2;
	if (orientation == B_HORIZONTAL) {
		view->AddLine(BPoint(rect.left + hCenter, rect.top + 1),
			BPoint(rect.left + hCenter, rect.top + 1), dark);
		view->AddLine(BPoint(rect.left + hCenter, rect.top + 3),
			BPoint(rect.left + hCenter, rect.top + 3), dark);
		view->AddLine(BPoint(rect.left + hCenter, rect.top + 5),
			BPoint(rect.left + hCenter, rect.top + 5), dark);
	} else {
		view->AddLine(BPoint(rect.left + 1, rect.top + vMiddle),
			BPoint(rect.left + 1, rect.top + vMiddle), dark);
		view->AddLine(BPoint(rect.left + 3, rect.top + vMiddle),
			BPoint(rect.left + 3, rect.top + vMiddle), dark);
		view->AddLine(BPoint(rect.left + 5, rect.top + vMiddle - 1),
			BPoint(rect.left + 5, rect.top + vMiddle), dark);
	}

	view->AddLine(BPoint(rect.right + 1, rect.bottom + 1),
		BPoint(rect.right + 1, rect.bottom + 1), dark);

	rect.InsetBy(1, 1);

	// third bevel
	view->AddLine(BPoint(rect.left, rect.bottom),
		BPoint(rect.right, rect.bottom), base);
	view->AddLine(BPoint(rect.right, rect.top),
		BPoint(rect.right, rect.bottom), base);

	view->EndLineArray();
}


void
BeControlLook::DrawSliderTriangle(BView* view, BRect& rect,
	const BRect& updateRect, const rgb_color& base, uint32 flags,
	orientation orientation)
{
	DrawSliderTriangle(view, rect, updateRect, base, base, flags, orientation);
}


void
BeControlLook::DrawSliderTriangle(BView* view, BRect& rect,
	const BRect& updateRect, const rgb_color& base, const rgb_color& fill,
	uint32 flags, orientation orientation)
{
	if (!ShouldDraw(view, rect, updateRect))
		return;

	rgb_color lighten1 = tint_color(base, B_LIGHTEN_1_TINT);
	rgb_color darken2 = tint_color(base, B_DARKEN_2_TINT);
	rgb_color darkenmax = tint_color(base, B_DARKEN_MAX_TINT);

	if (orientation == B_HORIZONTAL) {
		view->SetHighColor(lighten1);
		view->FillTriangle(BPoint(rect.left, rect.bottom - 1),
			BPoint(rect.left + 6, rect.top),
			BPoint(rect.right, rect.bottom - 1));

		view->SetHighColor(darkenmax);
		view->StrokeLine(BPoint(rect.right, rect.bottom + 1),
			BPoint(rect.left, rect.bottom + 1));
		view->StrokeLine(BPoint(rect.right, rect.bottom),
			BPoint(rect.left + 6, rect.top));

		view->SetHighColor(darken2);
		view->StrokeLine(BPoint(rect.right - 1, rect.bottom),
			BPoint(rect.left, rect.bottom));
		view->StrokeLine(BPoint(rect.left, rect.bottom),
			BPoint(rect.left + 5, rect.top + 1));

		view->SetHighColor(base);
		view->StrokeLine(BPoint(rect.right - 2, rect.bottom - 1),
			BPoint(rect.left + 3, rect.bottom - 1));
		view->StrokeLine(BPoint(rect.right - 3, rect.bottom - 2),
			BPoint(rect.left + 6, rect.top + 1));
	} else {
		view->SetHighColor(lighten1);
		view->FillTriangle(BPoint(rect.left + 1, rect.top),
			BPoint(rect.left + 7, rect.top + 6),
			BPoint(rect.left + 1, rect.bottom));

		view->SetHighColor(darkenmax);
		view->StrokeLine(BPoint(rect.left, rect.top + 1),
			BPoint(rect.left, rect.bottom));
		view->StrokeLine(BPoint(rect.left + 1, rect.bottom),
			BPoint(rect.left + 7, rect.top + 6));

		view->SetHighColor(darken2);
		view->StrokeLine(BPoint(rect.left, rect.top),
			BPoint(rect.left, rect.bottom - 1));
		view->StrokeLine(BPoint(rect.left + 1, rect.top),
			BPoint(rect.left + 6, rect.top + 5));

		view->SetHighColor(base);
		view->StrokeLine(BPoint(rect.left + 1, rect.top + 2),
			BPoint(rect.left + 1, rect.bottom - 1));
		view->StrokeLine(BPoint(rect.left + 2, rect.bottom - 2),
			BPoint(rect.left + 6, rect.top + 6));
	}
}


void
BeControlLook::DrawSliderHashMarks(BView* view, BRect& rect,
	const BRect& updateRect, const rgb_color& base, int32 count,
	hash_mark_location location, uint32 flags, orientation orientation)
{
	if (!ShouldDraw(view, rect, updateRect))
		return;

	rgb_color lightColor;
	rgb_color darkColor;

	if ((flags & B_DISABLED) != 0) {
		lightColor = tint_color(base, 0.9);
		darkColor = tint_color(base, 1.07);
	} else {
		lightColor = tint_color(base, 0.8);
		darkColor = tint_color(base, 1.14);
	}

	int32 hashMarkCount = std::max(count, (int32)2);
		// draw at least two hashmarks at min/max if
		// fHashMarks != B_HASH_MARKS_NONE
	float factor;
	float startPos;
	if (orientation == B_HORIZONTAL) {
		factor = (rect.Width() - 2) / (hashMarkCount - 1);
		startPos = rect.left + 1;
	} else {
		factor = (rect.Height() - 2) / (hashMarkCount - 1);
		startPos = rect.top + 1;
	}

	if (location & B_HASH_MARKS_TOP) {
		view->BeginLineArray(hashMarkCount * 2);

		if (orientation == B_HORIZONTAL) {
			float pos = startPos;
			for (int32 i = 0; i < hashMarkCount; i++) {
				view->AddLine(BPoint(pos, rect.top),
							  BPoint(pos, rect.top + 4), darkColor);
				view->AddLine(BPoint(pos + 1, rect.top),
							  BPoint(pos + 1, rect.top + 4), lightColor);

				pos += factor;
			}
		} else {
			float pos = startPos;
			for (int32 i = 0; i < hashMarkCount; i++) {
				view->AddLine(BPoint(rect.left, pos),
							  BPoint(rect.left + 4, pos), darkColor);
				view->AddLine(BPoint(rect.left, pos + 1),
							  BPoint(rect.left + 4, pos + 1), lightColor);

				pos += factor;
			}
		}

		view->EndLineArray();
	}

	if ((location & B_HASH_MARKS_BOTTOM) != 0) {
		view->BeginLineArray(hashMarkCount * 2);

		if (orientation == B_HORIZONTAL) {
			float pos = startPos;
			for (int32 i = 0; i < hashMarkCount; i++) {
				view->AddLine(BPoint(pos, rect.bottom - 4),
							  BPoint(pos, rect.bottom), darkColor);
				view->AddLine(BPoint(pos + 1, rect.bottom - 4),
							  BPoint(pos + 1, rect.bottom), lightColor);

				pos += factor;
			}
		} else {
			float pos = startPos;
			for (int32 i = 0; i < hashMarkCount; i++) {
				view->AddLine(BPoint(rect.right - 4, pos),
							  BPoint(rect.right, pos), darkColor);
				view->AddLine(BPoint(rect.right - 4, pos + 1),
							  BPoint(rect.right, pos + 1), lightColor);

				pos += factor;
			}
		}

		view->EndLineArray();
	}
}


void
BeControlLook::DrawTabFrame(BView* view, BRect& rect, const BRect& updateRect,
	const rgb_color& base, uint32 flags, uint32 borders,
	border_style borderStyle, uint32 side)
{
	view->SetHighColor(base);
	view->FillRect(rect);

	rgb_color lightenmax = tint_color(base, B_LIGHTEN_MAX_TINT);

	view->BeginLineArray(1);

	switch(side) {
		case BTabView::kLeftSide:
			view->AddLine(BPoint(rect.right, rect.top),
				BPoint(rect.right, rect.bottom), lightenmax);
			break;

		case BTabView::kRightSide:
			view->AddLine(BPoint(rect.left, rect.top),
				BPoint(rect.left, rect.bottom), lightenmax);
			break;

		default:
		case BTabView::kTopSide:
			view->AddLine(BPoint(rect.left, rect.bottom),
				BPoint(rect.right, rect.bottom), lightenmax);
			break;

		case BTabView::kBottomSide:
			view->AddLine(BPoint(rect.left, rect.top),
				BPoint(rect.right, rect.top), lightenmax);
			break;
	}

	view->EndLineArray();
}


void
BeControlLook::DrawActiveTab(BView* view, BRect& rect,
	const BRect& updateRect, const rgb_color& base, uint32 flags,
	uint32 borders, uint32 side, int32, int32, int32, int32)
{
	if (!ShouldDraw(view, rect, updateRect))
		return;

	view->PushState();

	// clip draw rect to rect plus 2px
	view->ClipToRect(rect.InsetByCopy(-2, -2));

	// set colors and draw

	rgb_color lightenmax = tint_color(base, B_LIGHTEN_MAX_TINT);
	rgb_color darken4 = tint_color(base, B_DARKEN_4_TINT);
	rgb_color darkenmax = tint_color(base, B_DARKEN_MAX_TINT);

	view->SetHighColor(darkenmax);
	view->SetLowColor(base);

	view->BeginLineArray(12);

	switch (side) {
		case BTabView::kLeftSide:
			// before going left
			view->AddLine(BPoint(rect.right - 1, rect.top - 1),
				BPoint(rect.right - 1, rect.top - 1), lightenmax);
			view->AddLine(BPoint(rect.right - 2, rect.top),
				BPoint(rect.right - 3, rect.top), lightenmax);

			// going left
			view->AddLine(BPoint(rect.right - 4, rect.top + 1),
				BPoint(rect.left + 5, rect.top + 1), lightenmax);

			// before going down
			view->AddLine(BPoint(rect.left + 2, rect.top + 2),
				BPoint(rect.left + 4, rect.top + 2), lightenmax);
			view->AddLine(BPoint(rect.left + 1, rect.top + 3),
				BPoint(rect.left + 1, rect.top + 4 ), lightenmax);

			// going down
			view->AddLine(BPoint(rect.left, rect.top + 5),
				BPoint(rect.left, rect.bottom - 5), lightenmax);

			// after going down
			view->AddLine(BPoint(rect.left + 1, rect.bottom - 4),
				BPoint(rect.left + 1, rect.bottom - 3), lightenmax);
			view->AddLine(BPoint(rect.left + 2, rect.bottom - 2),
				BPoint(rect.left + 3, rect.bottom - 2), darken4);

			// going right
			view->AddLine(BPoint(rect.left + 4, rect.bottom - 1),
				BPoint(rect.right - 4, rect.bottom - 1), darken4);

			// after going right
			view->AddLine(BPoint(rect.right - 3, rect.bottom),
				BPoint(rect.right - 2, rect.bottom), darken4);
			view->AddLine(BPoint(rect.right - 1, rect.bottom + 1),
				BPoint(rect.right - 1, rect.bottom + 1), darken4);
			view->AddLine(BPoint(rect.right, rect.bottom + 2),
				BPoint(rect.right, rect.bottom + 2), darken4);
			break;

		case BTabView::kRightSide:
			// before going right
			view->AddLine(BPoint(rect.left - 1, rect.top - 1),
				BPoint(rect.left - 1, rect.top - 1), lightenmax);
			view->AddLine(BPoint(rect.left - 2, rect.top),
				BPoint(rect.left - 3, rect.top), lightenmax);

			// going right
			view->AddLine(BPoint(rect.left - 4, rect.top + 1),
				BPoint(rect.right + 5, rect.top + 1), lightenmax);

			// before going down
			view->AddLine(BPoint(rect.right + 2, rect.top + 2),
				BPoint(rect.right + 4, rect.top + 2), lightenmax);
			view->AddLine(BPoint(rect.right + 1, rect.top + 3),
				BPoint(rect.right + 1, rect.top + 4 ), lightenmax);

			// going down
			view->AddLine(BPoint(rect.right, rect.top + 5),
				BPoint(rect.right, rect.bottom - 5), lightenmax);

			// after going down
			view->AddLine(BPoint(rect.right + 1, rect.bottom - 4),
				BPoint(rect.right + 1, rect.bottom - 3), lightenmax);
			view->AddLine(BPoint(rect.right + 2, rect.bottom - 2),
				BPoint(rect.right + 3, rect.bottom - 2), darken4);

			// going left
			view->AddLine(BPoint(rect.right + 4, rect.bottom - 1),
				BPoint(rect.left - 4, rect.bottom - 1), darken4);

			// after going left
			view->AddLine(BPoint(rect.left - 3, rect.bottom),
				BPoint(rect.left - 2, rect.bottom), darken4);
			view->AddLine(BPoint(rect.left - 1, rect.bottom + 1),
				BPoint(rect.left - 1, rect.bottom + 1), darken4);
			view->AddLine(BPoint(rect.left, rect.bottom + 2),
				BPoint(rect.left, rect.bottom + 2), darken4);
			break;

		default:
		case BTabView::kTopSide:
			// before going up
			view->AddLine(BPoint(rect.left - 1, rect.bottom - 1),
				BPoint(rect.left - 1, rect.bottom - 1), lightenmax);
			view->AddLine(BPoint(rect.left, rect.bottom - 2),
				BPoint(rect.left, rect.bottom - 3), lightenmax);

			// going up
			view->AddLine(BPoint(rect.left + 1, rect.bottom - 4),
				BPoint(rect.left + 1, rect.top + 5), lightenmax);

			// before going right
			view->AddLine(BPoint(rect.left + 2, rect.top + 4),
				BPoint(rect.left + 2, rect.top + 2), lightenmax);
			view->AddLine(BPoint(rect.left + 3, rect.top + 1),
				BPoint(rect.left + 4, rect.top + 1), lightenmax);

			// going right
			view->AddLine(BPoint(rect.left + 5, rect.top),
				BPoint(rect.right - 5, rect.top), lightenmax);

			// after going right
			view->AddLine(BPoint(rect.right - 4, rect.top + 1),
				BPoint(rect.right - 3, rect.top + 1), lightenmax);
			view->AddLine(BPoint(rect.right - 2, rect.top + 2),
				BPoint(rect.right - 2, rect.top + 3), darken4);

			// going down
			view->AddLine(BPoint(rect.right - 1, rect.top + 4),
				BPoint(rect.right - 1, rect.bottom - 4), darken4);

			// after going down
			view->AddLine(BPoint(rect.right, rect.bottom - 3),
				BPoint(rect.right, rect.bottom - 2), darken4);
			view->AddLine(BPoint(rect.right + 1, rect.bottom - 1),
				BPoint(rect.right + 1, rect.bottom - 1), darken4);
			view->AddLine(BPoint(rect.right + 2, rect.bottom),
				BPoint(rect.right + 2, rect.bottom), darken4);
			break;

		case BTabView::kBottomSide:
			// before going down
			view->AddLine(BPoint(rect.left - 1, rect.top - 1),
				BPoint(rect.left - 1, rect.top - 1), lightenmax);
			view->AddLine(BPoint(rect.left, rect.top - 2),
				BPoint(rect.left, rect.top - 3), lightenmax);

			// going down
			view->AddLine(BPoint(rect.left + 1, rect.top - 4),
				BPoint(rect.left + 1, rect.bottom + 5), lightenmax);

			// before going right
			view->AddLine(BPoint(rect.left + 2, rect.bottom + 4),
				BPoint(rect.left + 2, rect.bottom + 2), lightenmax);
			view->AddLine(BPoint(rect.left + 3, rect.bottom + 1),
				BPoint(rect.left + 4, rect.bottom + 1), lightenmax);

			// going right
			view->AddLine(BPoint(rect.left + 5, rect.bottom),
				BPoint(rect.right - 5, rect.bottom), lightenmax);

			// after going right
			view->AddLine(BPoint(rect.right - 4, rect.bottom + 1),
				BPoint(rect.right - 3, rect.bottom + 1), lightenmax);
			view->AddLine(BPoint(rect.right - 2, rect.bottom + 2),
				BPoint(rect.right - 2, rect.bottom + 3), darken4);

			// going up
			view->AddLine(BPoint(rect.right - 1, rect.bottom + 4),
				BPoint(rect.right - 1, rect.top - 4), darken4);

			// after going up
			view->AddLine(BPoint(rect.right, rect.top - 3),
				BPoint(rect.right, rect.top - 2), darken4);
			view->AddLine(BPoint(rect.right + 1, rect.top - 1),
				BPoint(rect.right + 1, rect.top - 1), darken4);
			view->AddLine(BPoint(rect.right + 2, rect.top),
				BPoint(rect.right + 2, rect.top), darken4);
			break;
	}
	view->EndLineArray();

	// undraw white line
	view->BeginLineArray(1);
	switch (side) {
		case BTabView::kLeftSide:
			view->AddLine(BPoint(rect.right, rect.top - 1),
				BPoint(rect.right, rect.bottom + 1), base);
			break;

		case BTabView::kRightSide:
			view->AddLine(BPoint(rect.left, rect.top - 1),
				BPoint(rect.left, rect.bottom + 1), base);
			break;

		default:
		case BTabView::kTopSide:
			view->AddLine(BPoint(rect.left - 1, rect.bottom),
				BPoint(rect.right + 1, rect.bottom), base);
			break;

		case BTabView::kBottomSide:
			view->AddLine(BPoint(rect.left - 1, rect.top),
				BPoint(rect.right + 1, rect.top), base);
			break;
	}
	view->EndLineArray();

	// inset rect for view contents
	rect.InsetBy(2, 2);

	view->PopState();
}


void
BeControlLook::DrawInactiveTab(BView* view, BRect& rect,
	const BRect& updateRect, const rgb_color& base, uint32 flags,
	uint32 borders, uint32 side, int32 index, int32 selected,
	int32 first, int32 last)
{
	if (!ShouldDraw(view, rect, updateRect))
		return;

	bool isFirst = index == first;
	bool isFull = index != selected - 1;

	view->PushState();

	// clip draw rect to rect plus 2px
	view->ClipToRect(rect.InsetByCopy(-2, -2));

	// set colors and draw

	rgb_color lightenmax = tint_color(base, B_LIGHTEN_MAX_TINT);
	rgb_color darken4 = tint_color(base, B_DARKEN_4_TINT);
	rgb_color darkenmax = tint_color(base, B_DARKEN_MAX_TINT);

	view->SetHighColor(darkenmax);
	view->SetLowColor(base);

	view->BeginLineArray(12);

	switch (side) {
		case BTabView::kLeftSide:
			// only draw if first tab is unselected
			if (isFirst) {
				// before going left
				view->AddLine(BPoint(rect.right - 1, rect.top - 1),
					BPoint(rect.right - 1, rect.top - 1), lightenmax);
				view->AddLine(BPoint(rect.right - 2, rect.top),
					BPoint(rect.right - 3, rect.top), lightenmax);
			}

			// going left
			view->AddLine(BPoint(rect.right - 4, rect.top + 1),
				BPoint(rect.left + 5, rect.top + 1), lightenmax);

			// before going down
			view->AddLine(BPoint(rect.left + 2, rect.top + 2),
				BPoint(rect.left + 4, rect.top + 2), lightenmax);
			view->AddLine(BPoint(rect.left + 1, rect.top + 3),
				BPoint(rect.left + 1, rect.top + 4 ), lightenmax);

			// going down
			view->AddLine(BPoint(rect.left, rect.top + 5),
				BPoint(rect.left, rect.bottom - 5), lightenmax);

			// after going down
			view->AddLine(BPoint(rect.left + 1, rect.bottom - 4),
				BPoint(rect.left + 1, rect.bottom - 3), lightenmax);
			view->AddLine(BPoint(rect.left + 2, rect.bottom - 2),
				BPoint(rect.left + 3, rect.bottom - 2), darken4);

			// going right
			view->AddLine(BPoint(rect.left + 4, rect.bottom - 1),
				BPoint(rect.right - 4, rect.bottom - 1), darken4);

			// only draw if not before selected tab
			if (isFull) {
				// after going right
				view->AddLine(BPoint(rect.right - 3, rect.bottom),
					BPoint(rect.right - 2, rect.bottom), darken4);
				view->AddLine(BPoint(rect.right - 1, rect.bottom + 1),
					BPoint(rect.right - 1, rect.bottom + 1), darken4);
			}
			break;

		case BTabView::kRightSide:
			// only draw if first tab is unselected
			if (isFirst) {
				// before going right
				view->AddLine(BPoint(rect.left - 1, rect.top - 1),
					BPoint(rect.left - 1, rect.top - 1), lightenmax);
				view->AddLine(BPoint(rect.left - 2, rect.top),
					BPoint(rect.left - 3, rect.top), lightenmax);
			}

			// going right
			view->AddLine(BPoint(rect.left - 4, rect.top + 1),
				BPoint(rect.right + 5, rect.top + 1), lightenmax);

			// before going down
			view->AddLine(BPoint(rect.right + 2, rect.top + 2),
				BPoint(rect.right + 4, rect.top + 2), lightenmax);
			view->AddLine(BPoint(rect.right + 1, rect.top + 3),
				BPoint(rect.right + 1, rect.top + 4 ), lightenmax);

			// going down
			view->AddLine(BPoint(rect.right, rect.top + 5),
				BPoint(rect.right, rect.bottom - 5), lightenmax);

			// after going down
			view->AddLine(BPoint(rect.right + 1, rect.bottom - 4),
				BPoint(rect.right + 1, rect.bottom - 3), lightenmax);
			view->AddLine(BPoint(rect.right + 2, rect.bottom - 2),
				BPoint(rect.right + 3, rect.bottom - 2), darken4);

			// going left
			view->AddLine(BPoint(rect.right + 4, rect.bottom - 1),
				BPoint(rect.left - 4, rect.bottom - 1), darken4);

			// only draw if not before selected tab
			if (isFull) {
				// after going left
				view->AddLine(BPoint(rect.left - 3, rect.bottom),
					BPoint(rect.left - 2, rect.bottom), darken4);
				view->AddLine(BPoint(rect.left - 1, rect.bottom + 1),
					BPoint(rect.left - 1, rect.bottom + 1), darken4);
			}
			break;

		default:
		case BTabView::kTopSide:
			// only draw if first tab is unselected
			if (isFirst) {
				// before going up
				view->AddLine(BPoint(rect.left - 1, rect.bottom - 1),
					BPoint(rect.left - 1, rect.bottom - 1), lightenmax);
				view->AddLine(BPoint(rect.left, rect.bottom - 2),
					BPoint(rect.left, rect.bottom - 3), lightenmax);;
			}

			// going up
			view->AddLine(BPoint(rect.left + 1, rect.bottom - 4),
				BPoint(rect.left + 1, rect.top + 5), lightenmax);

			// before going right
			view->AddLine(BPoint(rect.left + 2, rect.top + 4),
				BPoint(rect.left + 2, rect.top + 2), lightenmax);
			view->AddLine(BPoint(rect.left + 3, rect.top + 1),
				BPoint(rect.left + 4, rect.top + 1), lightenmax);

			// going right
			view->AddLine(BPoint(rect.left + 5, rect.top),
				BPoint(rect.right - 5, rect.top), lightenmax);

			// after going right
			view->AddLine(BPoint(rect.right - 4, rect.top + 1),
				BPoint(rect.right - 3, rect.top + 1), lightenmax);
			view->AddLine(BPoint(rect.right - 2, rect.top + 2),
				BPoint(rect.right - 2, rect.top + 3), darken4);

			// going down
			view->AddLine(BPoint(rect.right - 1, rect.top + 4),
				BPoint(rect.right - 1, rect.bottom - 4), darken4);

			// only draw if not before selected tab
			if (isFull) {
				// after going down
				view->AddLine(BPoint(rect.right, rect.bottom - 3),
					BPoint(rect.right, rect.bottom - 2), darken4);
				view->AddLine(BPoint(rect.right + 1, rect.bottom - 1),
					BPoint(rect.right + 1, rect.bottom - 1), darken4);
			}
			break;

		case BTabView::kBottomSide:
			// only draw if first tab is unselected
			if (isFirst) {
				// before going down
				view->AddLine(BPoint(rect.left - 1, rect.top - 1),
					BPoint(rect.left - 1, rect.top - 1), lightenmax);
				view->AddLine(BPoint(rect.left, rect.top - 2),
					BPoint(rect.left, rect.top - 3), lightenmax);
			}

			// before going down
			view->AddLine(BPoint(rect.left + 1, rect.top - 4),
				BPoint(rect.left + 1, rect.bottom + 5), lightenmax);

			// before going right
			view->AddLine(BPoint(rect.left + 2, rect.bottom + 4),
				BPoint(rect.left + 2, rect.bottom + 2), lightenmax);
			view->AddLine(BPoint(rect.left + 3, rect.bottom + 1),
				BPoint(rect.left + 4, rect.bottom + 1), lightenmax);

			// going right
			view->AddLine(BPoint(rect.left + 5, rect.bottom),
				BPoint(rect.right - 5, rect.bottom), lightenmax);

			// after going right
			view->AddLine(BPoint(rect.right - 4, rect.bottom + 1),
				BPoint(rect.right - 3, rect.bottom + 1), lightenmax);
			view->AddLine(BPoint(rect.right - 2, rect.bottom + 2),
				BPoint(rect.right - 2, rect.bottom + 3), darken4);

			// going up
			view->AddLine(BPoint(rect.right - 1, rect.bottom + 4),
				BPoint(rect.right - 1, rect.top - 4), darken4);

			// only draw if not before selected tab
			if (isFull) {
				// after going up
				view->AddLine(BPoint(rect.right, rect.top - 3),
					BPoint(rect.right, rect.top - 2), darken4);
				view->AddLine(BPoint(rect.right + 1, rect.top - 1),
					BPoint(rect.right + 1, rect.top - 1), darken4);
			}
			break;
	}

	view->EndLineArray();

	// inset rect for view contents
	rect.InsetBy(2, 2);

	view->PopState();
}


void
BeControlLook::DrawSplitter(BView* view, BRect& rect, const BRect& updateRect,
	const rgb_color& base, orientation orientation, uint32 flags,
	uint32 borders)
{
	if (!ShouldDraw(view, rect, updateRect))
		return;

	rgb_color background;
	if ((flags & (B_CLICKED | B_ACTIVATED)) != 0)
		background = tint_color(base, B_DARKEN_1_TINT);
	else
		background = base;

	rgb_color light = tint_color(background, 0.6);
	rgb_color shadow = tint_color(background, 1.21);

	// frame
	if (borders != 0 && rect.Width() > 3 && rect.Height() > 3)
		DrawRaisedBorder(view, rect, updateRect, background, flags, borders);

	// dots and rest of background
	if (orientation == B_HORIZONTAL) {
		if (rect.Width() > 2) {
			// background on left/right
			BRegion region(rect);
			rect.left = floorf((rect.left + rect.right) / 2.0 - 0.5);
			rect.right = rect.left + 1;
			region.Exclude(rect);
			view->SetHighColor(background);
			view->FillRegion(&region);
		}

		BPoint dot = rect.LeftTop();
		BPoint stop = rect.LeftBottom();
		int32 num = 1;
		while (dot.y <= stop.y) {
			rgb_color col1;
			rgb_color col2;
			switch (num) {
				case 1:
					col1 = background;
					col2 = background;
					break;
				case 2:
					col1 = shadow;
					col2 = background;
					break;
				case 3:
				default:
					col1 = background;
					col2 = light;
					num = 0;
					break;
			}
			view->SetHighColor(col1);
			view->StrokeLine(dot, dot, B_SOLID_HIGH);
			view->SetHighColor(col2);
			dot.x++;
			view->StrokeLine(dot, dot, B_SOLID_HIGH);
			dot.x -= 1.0;
			// next pixel
			num++;
			dot.y++;
		}
	} else {
		if (rect.Height() > 2) {
			// background on left/right
			BRegion region(rect);
			rect.top = floorf((rect.top + rect.bottom) / 2.0 - 0.5);
			rect.bottom = rect.top + 1;
			region.Exclude(rect);
			view->SetHighColor(background);
			view->FillRegion(&region);
		}

		BPoint dot = rect.LeftTop();
		BPoint stop = rect.RightTop();
		int32 num = 1;
		while (dot.x <= stop.x) {
			rgb_color col1;
			rgb_color col2;
			switch (num) {
				case 1:
					col1 = background;
					col2 = background;
					break;
				case 2:
					col1 = shadow;
					col2 = background;
					break;
				case 3:
				default:
					col1 = background;
					col2 = light;
					num = 0;
					break;
			}
			view->SetHighColor(col1);
			view->StrokeLine(dot, dot, B_SOLID_HIGH);
			view->SetHighColor(col2);
			dot.y++;
			view->StrokeLine(dot, dot, B_SOLID_HIGH);
			dot.y -= 1.0;
			// next pixel
			num++;
			dot.x++;
		}
	}
}


//	#pragma mark - various borders


void
BeControlLook::DrawBorder(BView* view, BRect& rect, const BRect& updateRect,
	const rgb_color& base, border_style borderStyle, uint32 flags,
	uint32 borders)
{
	if (borderStyle == B_NO_BORDER)
		return;

	if (!ShouldDraw(view, rect, updateRect))
		return;

	view->PushState();

	view->ClipToRect(rect);

	rgb_color lightColor;
	rgb_color shadowColor;
	if (base.IsLight()) {
		lightColor = tint_color(base, B_DARKEN_2_TINT);
		shadowColor = tint_color(base, B_LIGHTEN_2_TINT);
	} else {
		lightColor = tint_color(base, B_LIGHTEN_MAX_TINT);
		shadowColor = tint_color(base, B_DARKEN_3_TINT);
	}

	view->BeginLineArray(8);

	if (borderStyle == B_FANCY_BORDER) {
		if ((borders & B_LEFT_BORDER) != 0) {
			view->AddLine(rect.LeftBottom(), rect.LeftTop(), shadowColor);
			rect.left++;
		}
		if ((borders & B_TOP_BORDER) != 0) {
			view->AddLine(rect.LeftTop(), rect.RightTop(), shadowColor);
			rect.top++;
		}
		if ((borders & B_RIGHT_BORDER) != 0) {
			view->AddLine(rect.RightTop(), rect.RightBottom(), shadowColor);
			rect.right--;
		}
		if ((borders & B_BOTTOM_BORDER) != 0) {
			view->AddLine(rect.RightBottom(), rect.LeftBottom(), shadowColor);
			rect.bottom--;
		}

		if ((borders & B_LEFT_BORDER) != 0) {
			view->AddLine(rect.LeftBottom(), rect.LeftTop(), lightColor);
			rect.left++;
		}
		if ((borders & B_TOP_BORDER) != 0) {
			view->AddLine(rect.LeftTop(), rect.RightTop(), lightColor);
			rect.top++;
		}
		if ((borders & B_RIGHT_BORDER) != 0) {
			view->AddLine(rect.RightTop(), rect.RightBottom(), lightColor);
			rect.right--;
		}
		if ((borders & B_BOTTOM_BORDER) != 0) {
			view->AddLine(rect.RightBottom(), rect.LeftBottom(), lightColor);
			rect.bottom--;
		}
	} else if (borderStyle == B_PLAIN_BORDER) {
		if ((borders & B_LEFT_BORDER) != 0) {
			view->AddLine(rect.LeftBottom(), rect.LeftTop(), shadowColor);
			rect.left++;
		}
		if ((borders & B_TOP_BORDER) != 0) {
			view->AddLine(rect.LeftTop(), rect.RightTop(), shadowColor);
			rect.top++;
		}
		if ((borders & B_RIGHT_BORDER) != 0) {
			view->AddLine(rect.RightTop(), rect.RightBottom(), shadowColor);
			rect.right--;
		}
		if ((borders & B_BOTTOM_BORDER) != 0) {
			view->AddLine(rect.RightBottom(), rect.LeftBottom(), shadowColor);
			rect.bottom--;
		}

		if ((borders & B_LEFT_BORDER) != 0) {
			view->AddLine(rect.LeftBottom(), rect.LeftTop(), lightColor);
			rect.left++;
		}
		if ((borders & B_TOP_BORDER) != 0) {
			view->AddLine(rect.LeftTop(), rect.RightTop(), lightColor);
			rect.top++;
		}
		if ((borders & B_RIGHT_BORDER) != 0) {
			view->AddLine(rect.RightTop(), rect.RightBottom(), lightColor);
			rect.right--;
		}
		if ((borders & B_BOTTOM_BORDER) != 0) {
			view->AddLine(rect.RightBottom(), rect.LeftBottom(), lightColor);
			rect.bottom--;
		}
	}

	view->EndLineArray();

	view->PopState();
}


void
BeControlLook::DrawRaisedBorder(BView* view, BRect& rect,
	const BRect& updateRect, const rgb_color& base, uint32 flags,
	uint32 borders)
{
	if (!ShouldDraw(view, rect, updateRect))
		return;

	view->PushState();

	view->ClipToRect(rect);

	rgb_color lightColor;
	rgb_color shadowColor;

	if ((flags & B_DISABLED) != 0) {
		lightColor = base;
		shadowColor = base;
	} else {
		lightColor = tint_color(base, 0.85);
		shadowColor = tint_color(base, 1.07);
	}

	view->BeginLineArray(8);

	if ((borders & B_LEFT_BORDER) != 0) {
		view->AddLine(rect.LeftBottom(), rect.LeftTop(), lightColor);
		rect.left++;
	}
	if ((borders & B_TOP_BORDER) != 0) {
		view->AddLine(rect.LeftTop(), rect.RightTop(), lightColor);
		rect.top++;
	}
	if ((borders & B_RIGHT_BORDER) != 0) {
		view->AddLine(rect.RightTop(), rect.RightBottom(), lightColor);
		rect.right--;
	}
	if ((borders & B_BOTTOM_BORDER) != 0) {
		view->AddLine(rect.RightBottom(), rect.LeftBottom(), lightColor);
		rect.bottom--;
	}

	if ((borders & B_LEFT_BORDER) != 0) {
		view->AddLine(rect.LeftBottom(), rect.LeftTop(), shadowColor);
		rect.left++;
	}
	if ((borders & B_TOP_BORDER) != 0) {
		view->AddLine(rect.LeftTop(), rect.RightTop(), shadowColor);
		rect.top++;
	}
	if ((borders & B_RIGHT_BORDER) != 0) {
		view->AddLine(rect.RightTop(), rect.RightBottom(), shadowColor);
		rect.right--;
	}
	if ((borders & B_BOTTOM_BORDER) != 0) {
		view->AddLine(rect.RightBottom(), rect.LeftBottom(), shadowColor);
		rect.bottom--;
	}

	view->EndLineArray();

	view->PopState();
}


void
BeControlLook::DrawTextControlBorder(BView* view, BRect& rect,
	const BRect& updateRect, const rgb_color& base, uint32 flags,
	uint32 borders)
{
	if (!ShouldDraw(view, rect, updateRect))
		return;

	view->PushState();

	view->ClipToRect(rect);

	bool isEnabled = (flags & B_DISABLED) == 0;
	bool isFocused = (flags & B_FOCUSED) != 0;

	rgb_color dark;
	rgb_color light;

	// outer bevel

	dark = tint_color(base, (isEnabled ? B_DARKEN_1_TINT : B_DARKEN_2_TINT));
	light = tint_color(base, (isEnabled ? B_LIGHTEN_MAX_TINT : B_LIGHTEN_2_TINT));

	_DrawFrame(view, rect, dark, dark, light, light);

	// inner bevel

	if (isEnabled && isFocused) {
		view->SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR));
		view->StrokeRect(rect);
		rect.InsetBy(1, 1);
	} else {
		dark = tint_color(base, (isEnabled ? B_DARKEN_4_TINT : B_LIGHTEN_2_TINT));
		light = base;

		_DrawFrame(view, rect, dark, dark, light, light);
	}

	view->PopState();
}


void
BeControlLook::DrawGroupFrame(BView* view, BRect& rect, const BRect& updateRect,
	const rgb_color& base, uint32 borders)
{
	DrawBorder(view, rect, updateRect, base, B_FANCY_BORDER, 0, borders);
}


//	#pragma mark - Labels


void
BeControlLook::DrawLabel(BView* view, const char* label, BRect rect,
	const BRect& updateRect, const rgb_color& base, uint32 flags,
	const rgb_color* textColor)
{
	DrawLabel(view, label, NULL, rect, updateRect, base, flags,
		DefaultLabelAlignment(), textColor);
}


void
BeControlLook::DrawLabel(BView* view, const char* label, BRect rect,
	const BRect& updateRect, const rgb_color& base, uint32 flags,
	const BAlignment& alignment, const rgb_color* textColor)
{
	DrawLabel(view, label, NULL, rect, updateRect, base, flags, alignment,
		textColor);
}


void
BeControlLook::DrawLabel(BView* view, const char* label, const rgb_color& base,
	uint32 flags, const BPoint& where, const rgb_color* textColor)
{
	BWindow* window = view->Window();
	bool isDesktop = window != NULL && window->Feel() == kDesktopWindowFeel
		&& window->Look() == kDesktopWindowLook && view->Parent() != NULL
		&& view->Parent()->Parent() == NULL && (flags & B_IGNORE_OUTLINE) == 0;

	rgb_color low;
	rgb_color color;
	rgb_color glowColor;

	if (textColor != NULL)
		glowColor = *textColor;
	else if (view->Parent() != NULL)
		glowColor = view->Parent()->HighColor();
	else if ((flags & B_IS_CONTROL) != 0)
		glowColor = ui_color(B_CONTROL_TEXT_COLOR);
	else
		glowColor = ui_color(B_PANEL_TEXT_COLOR);

	color = glowColor;

	if (isDesktop)
		low = view->Parent()->ViewColor();
	else
		low = base;

	view->PushState();

	if (isDesktop) {
		// enforce proper use of desktop label colors
		if (low.Brightness() <= ui_color(B_DESKTOP_COLOR).Brightness()) {
			color = kWhite;
			glowColor = kBlack;
		} else {
			color = kBlack;
			glowColor = kWhite;
		}

		// drawing occurs on the desktop
		if (fCachedWorkspace != current_workspace()) {
			int8 indice = 0;
			int32 mask;
			bool tmpOutline;
			while (fBackgroundInfo.FindInt32("be:bgndimginfoworkspaces",
					indice, &mask) == B_OK
				&& fBackgroundInfo.FindBool("be:bgndimginfoerasetext",
					indice, &tmpOutline) == B_OK) {

				if (((1 << current_workspace()) & mask) != 0) {
					fCachedOutline = tmpOutline;
					fCachedWorkspace = current_workspace();
					break;
				}
				indice++;
			}
		}

		if (fCachedOutline) {
			BFont font;
			view->GetFont(&font);

			view->SetDrawingMode(B_OP_ALPHA);
			view->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_OVERLAY);

			// Draw glow or outline
			if (glowColor.IsLight()) {
				font.SetFalseBoldWidth(2.0);
				view->SetFont(&font, B_FONT_FALSE_BOLD_WIDTH);

				glowColor.alpha = 30;
				view->SetHighColor(glowColor);
				view->DrawString(label, where);

				font.SetFalseBoldWidth(1.0);
				view->SetFont(&font, B_FONT_FALSE_BOLD_WIDTH);

				glowColor.alpha = 65;
				view->SetHighColor(glowColor);
				view->DrawString(label, where);

				font.SetFalseBoldWidth(0.0);
				view->SetFont(&font, B_FONT_FALSE_BOLD_WIDTH);
			} else {
				font.SetFalseBoldWidth(1.0);
				view->SetFont(&font, B_FONT_FALSE_BOLD_WIDTH);

				glowColor.alpha = 30;
				view->SetHighColor(glowColor);
				view->DrawString(label, where);

				font.SetFalseBoldWidth(0.0);
				view->SetFont(&font, B_FONT_FALSE_BOLD_WIDTH);

				glowColor.alpha = 200;
				view->SetHighColor(glowColor);
				view->DrawString(label, BPoint(where.x + 1, where.y + 1));
			}
		}
	}

	if ((flags & B_DISABLED) != 0)
		color = disable_color(color, low);

	bool isButton = (flags & B_FLAT) != 0 || (flags & B_HOVER) != 0
		|| (flags & B_DEFAULT_BUTTON) != 0;
	if (isButton && (flags & B_DISABLED) == 0 && (flags & B_ACTIVATED) != 0) {
		// only for enabled and activated buttons
		color.red = 255 - color.red;
		color.green = 255 - color.green;
		color.blue = 255 - color.blue;
	}

	view->SetLowColor(color);
	view->SetHighColor(color);
	view->SetDrawingMode(B_OP_OVER);
	view->DrawString(label, where);
	view->PopState();
}


void
BeControlLook::DrawLabel(BView* view, const char* label, const BBitmap* icon,
	BRect rect, const BRect& updateRect, const rgb_color& base, uint32 flags,
	const BAlignment& alignment, const rgb_color* textColor)
{
	if (!ShouldDraw(view, rect, updateRect))
		return;

	if (label == NULL && icon == NULL)
		return;

	if (label == NULL && icon != NULL) {
		// icon only
		BRect alignedRect = BLayoutUtils::AlignInFrame(
			rect.OffsetByCopy(-2, -2),
			icon->Bounds().Size(), alignment);
		view->SetDrawingMode(B_OP_OVER);
		view->DrawBitmap(icon, alignedRect.LeftTop());
		view->SetDrawingMode(B_OP_COPY);
		return;
	}

	view->PushState();

	// label, possibly with icon
	float availableWidth = rect.Width() + 1;
	float width = 0;
	float textOffset = 0;
	float height = 0;

	if (icon != NULL && label != NULL) {
		// move text over to fit icon
		width = icon->Bounds().Width() + DefaultLabelSpacing() + 1;
		height = icon->Bounds().Height() + 1;
		textOffset = width;
		availableWidth -= textOffset;
	}

	// truncate the label if necessary and get the width and height
	BString truncatedLabel(label);

	BFont font;
	view->GetFont(&font);

	font.TruncateString(&truncatedLabel, B_TRUNCATE_MIDDLE, availableWidth);
	width += ceilf(font.StringWidth(truncatedLabel.String()));

	font_height fontHeight;
	font.GetHeight(&fontHeight);
	float textHeight = ceilf(fontHeight.ascent) + ceilf(fontHeight.descent);
	height = std::max(height, textHeight);

	// handle alignment
	BRect alignedRect(BLayoutUtils::AlignOnRect(rect,
		BSize(width - 1, height - 1), alignment));

	if (icon != NULL) {
		BPoint location(alignedRect.LeftTop());
		if (icon->Bounds().Height() + 1 < height)
			location.y += ceilf((height - icon->Bounds().Height() - 1) / 2);

		view->SetDrawingMode(B_OP_OVER);
		view->DrawBitmap(icon, location);
		view->SetDrawingMode(B_OP_COPY);
	}

	BPoint location(alignedRect.left + textOffset,
		alignedRect.top + ceilf(fontHeight.ascent));
	if (textHeight < height)
		location.y += ceilf((height - textHeight) / 2);

	if ((flags & B_FOCUSED) != 0) {
		// draw underline under label
		float x = location.x;
		float y = location.y + ceilf(fontHeight.descent);
		view->SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR));
		view->StrokeLine(BPoint(x, y),
			BPoint(x + view->StringWidth(truncatedLabel.String()), y));
	}

	DrawLabel(view, truncatedLabel.String(), base, flags, location, textColor);

	view->PopState();
}


void
BeControlLook::GetFrameInsets(frame_type frameType, uint32 flags, float& _left,
	float& _top, float& _right, float& _bottom)
{
	// All frames have the same inset on each side.
	float inset = 0;

	switch (frameType) {
		case B_BUTTON_FRAME:
			inset = (flags & B_DEFAULT_BUTTON) != 0 ? 5 : 2;
			break;

		case B_GROUP_FRAME:
		case B_MENU_FIELD_FRAME:
			inset = 3;
			break;

		case B_SCROLL_VIEW_FRAME:
		case B_TEXT_CONTROL_FRAME:
			inset = 2;
			break;
	}

	_left = inset;
	_top = inset;
	_right = inset;
	_bottom = inset;
}


void
BeControlLook::GetBackgroundInsets(background_type backgroundType,
	uint32 flags, float& _left, float& _top, float& _right, float& _bottom)
{
	// Most backgrounds have the same inset on each side.
	float inset = 0;

	switch (backgroundType) {
		case B_BUTTON_BACKGROUND:
		case B_MENU_BACKGROUND:
		case B_MENU_BAR_BACKGROUND:
		case B_MENU_FIELD_BACKGROUND:
		case B_MENU_ITEM_BACKGROUND:
			inset = 1;
			break;
		case B_BUTTON_WITH_POP_UP_BACKGROUND:
			_left = 1;
			_top = 1;
			_right = 1 + kButtonPopUpIndicatorWidth;
			_bottom = 1;
			return;
		case B_HORIZONTAL_SCROLL_BAR_BACKGROUND:
			_left = 2;
			_top = 0;
			_right = 1;
			_bottom = 0;
			return;
		case B_VERTICAL_SCROLL_BAR_BACKGROUND:
			_left = 0;
			_top = 2;
			_right = 0;
			_bottom = 1;
			return;
	}

	_left = inset;
	_top = inset;
	_right = inset;
	_bottom = inset;
}


void
BeControlLook::DrawButtonWithPopUpBackground(BView* view, BRect& rect,
	const BRect& updateRect, const rgb_color& base, uint32 flags,
	uint32 borders, orientation orientation)
{
	_DrawButtonBackground(view, rect, updateRect, 0.0f, 0.0f, 0.0f, 0.0f,
		base, true, flags, borders, orientation);
}


void
BeControlLook::DrawButtonWithPopUpBackground(BView* view, BRect& rect,
	const BRect& updateRect, float radius, const rgb_color& base, uint32 flags,
	uint32 borders, orientation orientation)
{
	_DrawButtonBackground(view, rect, updateRect, radius, radius, radius,
		radius, base, true, flags, borders, orientation);
}


void
BeControlLook::DrawButtonWithPopUpBackground(BView* view, BRect& rect,
	const BRect& updateRect, float leftTopRadius, float rightTopRadius,
	float leftBottomRadius, float rightBottomRadius, const rgb_color& base,
	uint32 flags, uint32 borders, orientation orientation)
{
	_DrawButtonBackground(view, rect, updateRect, leftTopRadius,
		rightTopRadius, leftBottomRadius, rightBottomRadius, base, true, flags,
		borders, orientation);
}


//	#pragma mark - protected methods


void
BeControlLook::_DrawButtonFrame(BView* view, BRect& rect,
	const BRect& updateRect, float, float, float, float, const rgb_color& base,
	const rgb_color& background, float contrast, float brightness,
	uint32 flags, uint32 borders)
{
	if (!ShouldDraw(view, rect, updateRect))
		return;

	view->PushState();

	view->ClipToRect(rect);

	// flags
	bool isEnabled = (flags & B_DISABLED) == 0;
	bool isDefault = (flags & B_DEFAULT_BUTTON) != 0;

	// colors
	rgb_color lighten1 = tint_color(base, B_LIGHTEN_1_TINT); // 231
	lighten1.red++; lighten1.green++; lighten1.blue++; // 232 = 231 + 1
	rgb_color lighten2 = tint_color(base, B_LIGHTEN_2_TINT);
	rgb_color lightenMax = tint_color(base, B_LIGHTEN_MAX_TINT);
	rgb_color darken1 = tint_color(base, B_DARKEN_1_TINT); // 184
	rgb_color darken2 = tint_color(base, B_DARKEN_2_TINT); // 158
	rgb_color darken3 = tint_color(base, B_DARKEN_3_TINT);
	rgb_color darken4 = tint_color(base, B_DARKEN_4_TINT); // 96

	rgb_color buttonBgColor = lighten1;
	rgb_color lightColor;

	rgb_color dark1BorderColor;
	rgb_color dark2BorderColor;

	rgb_color bevelColor1;
	rgb_color bevelColor2;
	rgb_color bevelColorRBCorner;

	rgb_color borderBevelShadow;
	rgb_color borderBevelLight;

	if (isEnabled) {
		lightColor = tint_color(base, B_LIGHTEN_2_TINT);
		dark1BorderColor = darken3;
		dark2BorderColor = darken4;
		bevelColor1 = darken2;
		bevelColor2 = lighten1;

		if (isDefault) {
			borderBevelShadow = tint_color(dark1BorderColor,
				(B_NO_TINT + B_DARKEN_1_TINT) / 2);
			borderBevelLight = tint_color(dark1BorderColor, B_LIGHTEN_1_TINT);

			borderBevelLight.red = (borderBevelLight.red + base.red) / 2;
			borderBevelLight.green = (borderBevelLight.green + base.green) / 2;
			borderBevelLight.blue = (borderBevelLight.blue + base.blue) / 2;

			dark1BorderColor = darken3;
			dark2BorderColor = darken4;

			bevelColorRBCorner = borderBevelShadow;
		} else {
			borderBevelShadow = tint_color(base,
				(B_NO_TINT + B_DARKEN_1_TINT) / 2);
			borderBevelLight = buttonBgColor;
			bevelColorRBCorner = dark1BorderColor;
		}
	} else {
		lightColor = lighten2;
		dark1BorderColor = darken1;
		dark2BorderColor = darken2;
		bevelColor1 = base;
		bevelColor2 = buttonBgColor;

		if (isDefault) {
			borderBevelShadow = dark1BorderColor;
			borderBevelLight = base;
			dark1BorderColor = tint_color(dark1BorderColor, B_DARKEN_1_TINT);
			dark2BorderColor = tint_color(dark1BorderColor, 1.16);
		} else {
			borderBevelShadow = base;
			borderBevelLight = base;
		}

		bevelColorRBCorner = tint_color(base, 1.08);
	}

	if (isDefault) {
		if (isEnabled) {
			// dark border
			view->BeginLineArray(4);
			if ((borders & B_LEFT_BORDER) != 0) {
				view->AddLine(BPoint(rect.left, rect.top + 1),
					BPoint(rect.left, rect.bottom - 1), dark2BorderColor);
				rect.left++;
			}
			if ((borders & B_TOP_BORDER) != 0) {
				view->AddLine(BPoint(rect.left, rect.top),
					BPoint(rect.right - 1, rect.top), dark2BorderColor);
				rect.top++;
			}
			if ((borders & B_RIGHT_BORDER) != 0) {
				view->AddLine(BPoint(rect.right, rect.top),
					BPoint(rect.right, rect.bottom - 1), dark2BorderColor);
				rect.right--;
			}
			if ((borders & B_BOTTOM_BORDER) != 0) {
				view->AddLine(BPoint(rect.left, rect.bottom),
					BPoint(rect.right, rect.bottom), dark2BorderColor);
				rect.bottom--;
			}
			view->EndLineArray();

			// bevel
			view->SetHighColor(darken1);
			view->StrokeRect(rect);

			rect.InsetBy(1, 1);

			// fill
			view->SetHighColor(lighten1);
			view->FillRect(rect);

			rect.InsetBy(2, 2);
		} else {
			// dark border
			view->BeginLineArray(4);
			if ((borders & B_LEFT_BORDER) != 0) {
				view->AddLine(BPoint(rect.left, rect.top + 1),
					BPoint(rect.left, rect.bottom - 1), darken1);
				rect.left++;
			}
			if ((borders & B_TOP_BORDER) != 0) {
				view->AddLine(BPoint(rect.left, rect.top),
					BPoint(rect.right - 1, rect.top), darken1);
				rect.top++;
			}
			if ((borders & B_RIGHT_BORDER) != 0) {
				view->AddLine(BPoint(rect.right, rect.top),
					BPoint(rect.right, rect.bottom - 1), darken1);
				rect.right--;
			}
			if ((borders & B_BOTTOM_BORDER) != 0) {
				view->AddLine(BPoint(rect.left, rect.bottom),
					BPoint(rect.right, rect.bottom), darken1);
				rect.bottom--;
			}
			view->EndLineArray();

			// fill
			view->SetHighColor(lighten1);
			view->FillRect(rect);

			rect.InsetBy(3, 3);
		}
	} else {
		// if not default button, inset top and bottom by 1px
		rect.InsetBy(1, 0);
	}

	// stroke frame to draw four corners, then write on top
	view->SetHighColor(ui_color(B_PANEL_BACKGROUND_COLOR));
	view->StrokeRect(rect);

	view->BeginLineArray(16);

	// external border
	view->AddLine(BPoint(rect.left, rect.top + 1),
		BPoint(rect.left, rect.bottom - 1),
		(borders & B_LEFT_BORDER) != 0 ? dark2BorderColor : darken1);
	view->AddLine(BPoint(rect.left + 1, rect.top),
		BPoint(rect.right - 1, rect.top),
		(borders & B_TOP_BORDER) != 0 ? dark2BorderColor : darken1);
	view->AddLine(BPoint(rect.right, rect.top + 1),
		BPoint(rect.right, rect.bottom - 1),
		(borders & B_RIGHT_BORDER) != 0 ? dark2BorderColor : darken1);
	view->AddLine(BPoint(rect.left + 1, rect.bottom),
		BPoint(rect.right - 1, rect.bottom),
		(borders & B_BOTTOM_BORDER) != 0 ? dark2BorderColor : darken1);

	// inset past external border
	rect.InsetBy(1, 1);

	// internal bevel
	view->AddLine(rect.LeftBottom(), rect.LeftTop(), bevelColor2);
	view->AddLine(rect.LeftTop(), rect.RightTop(), bevelColor2);
	view->AddLine(BPoint(rect.right, rect.top + 1), rect.RightBottom(),
		bevelColor1);
	view->AddLine(rect.RightBottom(), BPoint(rect.left + 1, rect.bottom),
		bevelColor1);

	// inset past internal bevel
	rect.InsetBy(1, 1);

	// internal gloss outside
	view->AddLine(BPoint(rect.left, rect.bottom + 1), rect.LeftTop(),
		lightenMax); // come down an extra pixel
	view->AddLine(rect.LeftTop(), rect.RightTop(), lightenMax);
	view->AddLine(rect.RightTop(), rect.RightBottom(), base);
	view->AddLine(rect.RightBottom(), BPoint(rect.left + 1, rect.bottom),
		base); // compensate for extra pixel

	// inset past gloss outside
	rect.InsetBy(1, 1);

	// internal gloss inside
	view->AddLine(BPoint(rect.left, rect.bottom + 1), rect.LeftTop(),
		lightenMax); // come down an extra pixel
	view->AddLine(rect.LeftTop(), rect.RightTop(), lightenMax);
	view->AddLine(rect.RightTop(), rect.RightBottom(), buttonBgColor);
	view->AddLine(rect.RightBottom(), BPoint(rect.left + 1, rect.bottom),
		buttonBgColor); // compensate for extra pixel

	// inset past gloss inside
	rect.InsetBy(1, 1);

	view->EndLineArray();

	view->PopState();
}


void
BeControlLook::_DrawButtonBackground(BView* view, BRect& rect,
	const BRect& updateRect, float, float, float, float, const rgb_color& base,
	bool popupIndicator, uint32 flags, uint32 borders, orientation orientation)
{
	if (!ShouldDraw(view, rect, updateRect))
		return;

	// fill the button area
	view->SetHighColor(tint_color(base, B_LIGHTEN_1_TINT));
	view->FillRect(rect);

	bool isEnabled = (flags & B_DISABLED) == 0;
	bool isActivated = (flags & B_ACTIVATED) != 0;

	if (isEnabled && isActivated) {
		// invert if clicked without altering rect
		BRect invertRect(rect.InsetByCopy(-3, -3));
		view->SetDrawingMode(B_OP_INVERT);
		view->FillRect(invertRect);
	}
}


void
BeControlLook::_DrawPopUpMarker(BView* view, const BRect& rect,
	const rgb_color& base, uint32 flags)
{
	bool isEnabled = (flags & B_DISABLED) == 0;

	BPoint position(rect.right - 8, rect.bottom - 8);
	BPoint triangle[3];
	triangle[0] = position + BPoint(-2.5, -0.5);
	triangle[1] = position + BPoint(2.5, -0.5);
	triangle[2] = position + BPoint(0.0, 2.0);

	uint32 viewFlags = view->Flags();
	view->SetFlags(viewFlags | B_SUBPIXEL_PRECISE);

	rgb_color markerColor = isEnabled
		? tint_color(base, B_DARKEN_4_TINT)
		: tint_color(base, B_DARKEN_2_TINT);

	view->SetHighColor(markerColor);
	view->FillTriangle(triangle[0], triangle[1], triangle[2]);

	view->SetFlags(viewFlags);
}


void
BeControlLook::_DrawMenuFieldBackgroundOutside(BView* view, BRect& rect,
	const BRect& updateRect, float, float, float, float, const rgb_color& base,
	bool popupIndicator, uint32 flags)
{
	if (!ShouldDraw(view, rect, updateRect))
		return;

	// BeControlLook does not support rounded corners and it never will
	if (popupIndicator) {
		BRect rightRect(rect);
		rightRect.left = rect.right - 10;

		_DrawMenuFieldBackgroundInside(view, rect, updateRect,
			0, 0, 0, 0, base, flags, B_ALL_BORDERS);
		_DrawPopUpMarker(view, rightRect, base, flags);
	} else {
		_DrawMenuFieldBackgroundInside(view, rect, updateRect, 0, 0,
			0, 0, base, flags);
	}
}


void
BeControlLook::_DrawMenuFieldBackgroundInside(BView* view, BRect& rect,
	const BRect& updateRect, float, float, float, float, const rgb_color& base,
	uint32 flags, uint32 borders)
{
	if (!ShouldDraw(view, rect, updateRect))
		return;

	view->PushState();

	view->ClipToRect(rect);

	// flags
	bool isEnabled = (flags & B_DISABLED) == 0;

	// colors
	rgb_color darken4;
	rgb_color darken1;
	rgb_color lighten1;
	rgb_color lighten2;

	if (isEnabled) {
		darken4 = tint_color(base, B_DARKEN_4_TINT);
		darken1 = tint_color(base, B_DARKEN_1_TINT);
		lighten1 = tint_color(base, B_LIGHTEN_1_TINT);
		lighten2 = tint_color(base, B_LIGHTEN_2_TINT);
	} else {
		darken4 = tint_color(base, B_DARKEN_2_TINT);
		darken1 = tint_color(base, (B_NO_TINT + B_DARKEN_1_TINT) / 2.0);
		lighten1 = tint_color(base, (B_NO_TINT + B_LIGHTEN_1_TINT) / 2.0);
		lighten2 = tint_color(base, B_LIGHTEN_1_TINT);
	}

	// fill background
	view->SetHighColor(base);
	view->FillRect(rect);

	// draw shadow lines around bottom and right sides
	view->BeginLineArray(6);

	// bottom below item text, darker then BMenuBar
	// would normaly draw it
	view->AddLine(BPoint(rect.left, rect.bottom),
		BPoint(rect.left - 1, rect.bottom), darken4);

	// bottom below popup marker
	view->AddLine(BPoint(rect.left, rect.bottom),
		BPoint(rect.right, rect.bottom), darken4);
	// right of popup marker
	view->AddLine(BPoint(rect.right, rect.bottom - 1),
		BPoint(rect.right, rect.top), darken4);
	// top above popup marker
	view->AddLine(BPoint(rect.left, rect.top),
		BPoint(rect.right - 2, rect.top), lighten2);

	rect.top += 1;
	rect.bottom -= 1;
	rect.right -= 1;

	// bottom below popup marker
	view->AddLine(BPoint(rect.left, rect.bottom),
		BPoint(rect.right, rect.bottom), darken1);
	// right of popup marker
	view->AddLine(BPoint(rect.right, rect.bottom - 1),
		BPoint(rect.right, rect.top), darken1);

	view->EndLineArray();

	rect.bottom -= 1;
	rect.right -= 1;
	view->SetHighColor(base);
	view->FillRect(rect);

	view->PopState();
}


void
BeControlLook::_DrawScrollBarBackgroundFirst(BView* view, BRect& rect,
	const BRect& updateRect, const rgb_color& base, uint32 flags,
	orientation orientation)
{
	if (!ShouldDraw(view, rect, updateRect))
		return;

	view->PushState();

	view->ClipToRect(rect);

	bool isEnabled = (flags & B_DISABLED) == 0;
	BRect orig(rect);

	// border = 152
	rgb_color border = tint_color(base, B_DARKEN_2_TINT);
	rgb_color shine, shadow, bg;
	if (isEnabled) {
		// shine = 216, shadow = 184, bg = 200
		shine = base;
		shadow = tint_color(base, B_DARKEN_1_TINT);
		bg = tint_color(base, 1.074);
	} else {
		// shine = 255, shadow = bg = 240
		shine = tint_color(base, B_LIGHTEN_MAX_TINT);
		rgb_color lighten2 = tint_color(base, B_LIGHTEN_2_TINT);
		lighten2.red++; lighten2.green++; lighten2.blue++;
			// lighten2 = 239, 240 = 239 + 1
		shadow = bg = lighten2;
	}

	// fill background, we'll draw arrows and thumb on top
	view->SetDrawingMode(B_OP_COPY);

	view->BeginLineArray(5);
	if (orientation == B_VERTICAL) {
		// left shadow
		view->AddLine(rect.LeftTop(), rect.LeftBottom(),
			isEnabled ? shadow : shine);
		rect.left++;
		view->AddLine(rect.LeftTop(), rect.LeftBottom(), shadow);
		rect.left++;
		// top shadow
		view->AddLine(rect.LeftTop(), rect.RightTop(),
			isEnabled ? shadow : shine);
		rect.top++;
		view->AddLine(rect.LeftTop(), rect.RightTop(), shadow);
		rect.top++;
		// shine
		view->AddLine(rect.RightTop(), rect.RightBottom(), base);
		rect.right--;
	} else {
		// left shadow
		view->AddLine(rect.LeftTop(), rect.LeftBottom(), shadow);
		rect.left++;
		view->AddLine(rect.LeftTop(), rect.LeftBottom(), shadow);
		rect.left++;
		// top shadow
		view->AddLine(rect.LeftTop(), rect.RightTop(), shadow);
		rect.top++;
		view->AddLine(rect.LeftTop(), rect.RightTop(), shadow);
		rect.top++;
		// shine
		view->AddLine(rect.LeftBottom(), rect.RightBottom(), base);
		rect.bottom--;
	}
	view->EndLineArray();

	// fill bg
	view->SetHighColor(bg);
	view->FillRect(rect);

	rect = orig;

	// draw border last
	view->BeginLineArray(2);
	if (orientation == B_VERTICAL) {
		// top border
		view->AddLine(rect.LeftTop(), rect.RightTop(), border);
		// bottom border
		view->AddLine(rect.LeftBottom(), rect.RightBottom(), border);
	} else {
		// left border
		view->AddLine(rect.LeftTop(), rect.LeftBottom(), border);
		// right border
		view->AddLine(rect.RightTop(), rect.RightBottom(), border);
	}
	view->EndLineArray();

	view->PopState();
}


void
BeControlLook::_DrawScrollBarBackgroundSecond(BView* view, BRect& rect,
	const BRect& updateRect, const rgb_color& base, uint32 flags,
	orientation orientation)
{
	if (!ShouldDraw(view, rect, updateRect))
		return;

	view->PushState();

	view->ClipToRect(rect);

	bool isEnabled = (flags & B_DISABLED) == 0;

	BRect orig(rect);

	// border = 152
	rgb_color border = tint_color(base, B_DARKEN_2_TINT);
	rgb_color darkBorder, shine, shadow, bg;
	if (isEnabled) {
		// darkBorder = 96 shine = 216, shadow = 184, bg = 200
		darkBorder = tint_color(base, B_DARKEN_4_TINT);
		shine = base;
		shadow = tint_color(base, B_DARKEN_1_TINT);
		bg = tint_color(base, 1.074);
	} else {
		// darkBorder = 184, shine = 255, shadow = bg = 240
		darkBorder = tint_color(base, B_DARKEN_1_TINT);
		shine = tint_color(base, B_LIGHTEN_MAX_TINT);
		rgb_color lighten2 = tint_color(base, B_LIGHTEN_2_TINT);
		lighten2.red++; lighten2.green++; lighten2.blue++;
			// lighten2 = 239, 240 = 239 + 1
		shadow = bg = lighten2;
	}

	// fill background, we'll draw arrows and thumb on top
	view->SetDrawingMode(B_OP_COPY);

	view->BeginLineArray(3);
	if (orientation == B_VERTICAL) {
		// left shadow (no top shadow on second rect)
		view->AddLine(rect.LeftTop(), rect.LeftBottom(),
			isEnabled ? shadow : shine);
		rect.left++;
		view->AddLine(rect.LeftTop(), rect.LeftBottom(), shadow);
		rect.left++;
		// shine (use base color)
		view->AddLine(rect.RightTop(), rect.RightBottom(), base);
		rect.right--;
	} else {
		// left shadow (no top shadow on second rect)
		view->AddLine(rect.LeftTop(), rect.RightTop(),
			isEnabled ? shadow : shine);
		rect.top++;
		view->AddLine(rect.LeftTop(), rect.RightTop(), shadow);
		rect.top++;
		// shine (use base color)
		view->AddLine(rect.LeftBottom(), rect.RightBottom(), base);
		rect.bottom--;
	}
	view->EndLineArray();

	// fill bg
	view->SetHighColor(bg);
	view->FillRect(rect);

	rect = orig;

	// draw border over bg
	view->BeginLineArray(2);
	if (orientation == B_VERTICAL) {
		// top border
		view->AddLine(rect.LeftTop(), rect.RightTop(), darkBorder);
		// bottom border
		view->AddLine(rect.LeftBottom(), rect.RightBottom(), border);
	} else {
		// left border
		view->AddLine(rect.LeftTop(), rect.LeftBottom(), darkBorder);
		// right border
		view->AddLine(rect.RightTop(), rect.RightBottom(), border);
	}
	view->EndLineArray();

	view->PopState();
}

void
BeControlLook::_DrawScrollBarKnobDot(BView* view,
	float hcenter, float vmiddle, rgb_color dark, rgb_color light,
	orientation orientation)
{
	// orientation is unused
	view->BeginLineArray(4);
	view->AddLine(BPoint(hcenter + 2, vmiddle - 2),
		BPoint(hcenter + 2, vmiddle + 2), dark);
	view->AddLine(BPoint(hcenter + 2, vmiddle + 2),
		BPoint(hcenter - 2, vmiddle + 2), dark);
	view->AddLine(BPoint(hcenter - 2, vmiddle + 1),
		BPoint(hcenter - 2, vmiddle - 1), light);
	view->AddLine(BPoint(hcenter - 2, vmiddle - 1),
		BPoint(hcenter - 2, vmiddle + 1), light);
	view->EndLineArray();
}


void
BeControlLook::_DrawScrollBarKnobLine(BView* view,
	float hcenter, float vmiddle, rgb_color dark, rgb_color light,
	orientation orientation)
{
	if (orientation == B_HORIZONTAL) {
		view->BeginLineArray(4);
		view->AddLine(BPoint(hcenter, vmiddle + 3),
			BPoint(hcenter + 1, vmiddle + 3), dark);
		view->AddLine(BPoint(hcenter + 1, vmiddle + 3),
			BPoint(hcenter + 1, vmiddle - 3), dark);
		view->AddLine(BPoint(hcenter, vmiddle - 3),
			BPoint(hcenter - 1, vmiddle - 3), light);
		view->AddLine(BPoint(hcenter - 1, vmiddle - 3),
			BPoint(hcenter - 1, vmiddle + 3), light);
		view->EndLineArray();
	} else {
		view->BeginLineArray(4);
		view->AddLine(BPoint(hcenter + 3, vmiddle),
			BPoint(hcenter + 3, vmiddle + 1), dark);
		view->AddLine(BPoint(hcenter + 3, vmiddle + 1),
			BPoint(hcenter - 3, vmiddle + 1), dark);
		view->AddLine(BPoint(hcenter - 3, vmiddle),
			BPoint(hcenter - 3, vmiddle - 1), light);
		view->AddLine(BPoint(hcenter - 3, vmiddle - 1),
			BPoint(hcenter + 3, vmiddle - 1), light);
		view->EndLineArray();
	}
}


void
BeControlLook::_DrawFrame(BView* view, BRect& rect, const rgb_color& left,
	const rgb_color& top, const rgb_color& right, const rgb_color& bottom,
	uint32 borders)
{
	view->BeginLineArray(4);

	// draw in reverse so that bottom and right corners cover top and left

	if ((borders & B_BOTTOM_BORDER) != 0) {
		view->AddLine(
			BPoint(rect.left, rect.bottom),
			BPoint(rect.right, rect.bottom), bottom);
		rect.bottom--;
	}
	if ((borders & B_RIGHT_BORDER) != 0) {
		view->AddLine(
			BPoint(rect.right, rect.top),
			BPoint(rect.right, rect.bottom), right);
		rect.right--;
	}
	if ((borders & B_TOP_BORDER) != 0) {
		view->AddLine(
			BPoint(rect.left, rect.top),
			BPoint(rect.right, rect.top), top);
		rect.top++;
	}
	if ((borders & B_LEFT_BORDER) != 0) {
		view->AddLine(
			BPoint(rect.left, rect.bottom),
			BPoint(rect.left, rect.top), left);
		rect.left++;
	}

	view->EndLineArray();
}

} // namespace BPrivate


extern "C" BControlLook* (instantiate_control_look)(image_id id)
{
	return new (std::nothrow)BPrivate::BeControlLook(id);
}