⛏️ index : haiku.git

/*
 * Copyright 2003-2013 Haiku, Inc. All Rights Reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *		Michael Phipps
 *		Axel Dörfler, axeld@pinc-software.de
 */

#include "ScreenCornerSelector.h"

#include <stdio.h>

#include <Rect.h>
#include <Point.h>
#include <Shape.h>
#include <Screen.h>
#include <Window.h>

#include "Constants.h"
#include "Utility.h"


static const float kAspectRatio = 4.0f / 3.0f;
static const float kMonitorBorderSize = 3.0f;
static const float kArrowSize = 11.0f;
static const float kStopSize = 15.0f;


ScreenCornerSelector::ScreenCornerSelector(BRect frame, const char* name,
	BMessage* message, uint32 resizingMode)
	:
	BControl(frame, name, NULL, message, resizingMode,
		B_WILL_DRAW | B_NAVIGABLE | B_FULL_UPDATE_ON_RESIZE),
	fCurrentCorner(NO_CORNER),
	fPreviousCorner(-1)
{
	SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
}


BRect
ScreenCornerSelector::_MonitorFrame() const
{
	float width = Bounds().Width();
	float height = Bounds().Height();

	if (width / kAspectRatio > height)
		width = height * kAspectRatio;
	else if (height * kAspectRatio > width)
		height = width / kAspectRatio;

	return BRect((Bounds().Width() - width) / 2,
		(Bounds().Height() - height) / 2,
		(Bounds().Width() + width) / 2, (Bounds().Height() + height) / 2);
}


BRect
ScreenCornerSelector::_InnerFrame(BRect monitorFrame) const
{
	return monitorFrame.InsetByCopy(kMonitorBorderSize + 3,
		kMonitorBorderSize + 3);
}


BRect
ScreenCornerSelector::_CenterFrame(BRect innerFrame) const
{
	return innerFrame.InsetByCopy(kArrowSize, kArrowSize);
}


void
ScreenCornerSelector::Draw(BRect updateRect)
{
	rgb_color darkColor = {160, 160, 160, 255};
	rgb_color blackColor = {0, 0, 0, 255};
	rgb_color redColor = {228, 0, 0, 255};

	BRect outerRect = _MonitorFrame();
	BRect innerRect(outerRect.InsetByCopy(kMonitorBorderSize + 2,
		kMonitorBorderSize + 2));

	SetDrawingMode(B_OP_OVER);

	if (!_InnerFrame(outerRect).Contains(updateRect)) {
		// frame & background

		// if the focus is changing, we don't redraw the whole view, but only
		// the part that's affected by the change
		if (!IsFocusChanging()) {
			SetHighColor(darkColor);
			FillRoundRect(outerRect, kMonitorBorderSize * 3 / 2,
				kMonitorBorderSize * 3 / 2);
		}

		if (IsFocus() && Window()->IsActive())
			SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR));
		else
			SetHighColor(blackColor);

		StrokeRoundRect(outerRect, kMonitorBorderSize * 3 / 2,
			kMonitorBorderSize * 3 / 2);

		if (IsFocusChanging())
			return;

		// power light

		SetHighColor(redColor);
		BPoint powerPos(outerRect.left + kMonitorBorderSize * 2, outerRect.bottom
			- kMonitorBorderSize);
		StrokeLine(powerPos, BPoint(powerPos.x + 2, powerPos.y));
	}

	if (!IsFocusChanging()) {
		SetHighColor(210, 210, 255);
		FillRoundRect(innerRect, kMonitorBorderSize, kMonitorBorderSize);
	}

	if (IsFocus() && Window()->IsActive())
		SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR));
	else
		SetHighColor(blackColor);
	StrokeRoundRect(innerRect, kMonitorBorderSize, kMonitorBorderSize);

	innerRect = _InnerFrame(outerRect);

	if (fCurrentCorner != NO_CORNER)
		_DrawArrow(innerRect);
	else
		_DrawStop(innerRect);

	SetDrawingMode(B_OP_COPY);
}


int32
ScreenCornerSelector::Value()
{
	return (int32)fCurrentCorner;
}


void
ScreenCornerSelector::SetValue(int32 corner)
{
	switch (corner) {
		case UP_LEFT_CORNER:
		case UP_RIGHT_CORNER:
		case DOWN_LEFT_CORNER:
		case DOWN_RIGHT_CORNER:
		case NO_CORNER:
			break;

		default:
			corner = NO_CORNER;
	}
	if ((screen_corner)corner == fCurrentCorner)
		return;

	fCurrentCorner = (screen_corner)corner;
	Invalidate(_InnerFrame(_MonitorFrame()));
	Invoke();
}


screen_corner
ScreenCornerSelector::Corner() const
{
	return fCurrentCorner;
}


void
ScreenCornerSelector::SetCorner(screen_corner corner)
{
	// redirected to SetValue() to make sure only valid values are set
	SetValue((int32)corner);
}


void
ScreenCornerSelector::_DrawStop(BRect innerFrame)
{
	BRect centerRect = _CenterFrame(innerFrame);
	float size = kStopSize;
	BRect rect;
	rect.left = centerRect.left + (centerRect.Width() - size) / 2;
	rect.top = centerRect.top + (centerRect.Height() - size) / 2;
	if (rect.left < centerRect.left || rect.top < centerRect.top) {
		size = centerRect.Height();
		rect.top = centerRect.top;
		rect.left = centerRect.left + (centerRect.Width() - size) / 2;
	}
	rect.right = rect.left + size - 1;
	rect.bottom = rect.top + size - 1;

	SetHighColor(255, 0, 0);
	SetPenSize(2);
	SetFlags(Flags() | B_SUBPIXEL_PRECISE);

	StrokeEllipse(rect);

	size -= sin(M_PI / 4) * size + 2;
	rect.InsetBy(size, size);
	StrokeLine(rect.RightTop(), rect.LeftBottom());

	SetFlags(Flags() & ~B_SUBPIXEL_PRECISE);
	SetPenSize(1);
}


void
ScreenCornerSelector::_DrawArrow(BRect innerFrame)
{
	float size = kArrowSize;
	float sizeX = fCurrentCorner == UP_LEFT_CORNER
		|| fCurrentCorner == DOWN_LEFT_CORNER ? size : -size;
	float sizeY = fCurrentCorner == UP_LEFT_CORNER
		|| fCurrentCorner == UP_RIGHT_CORNER ? size : -size;

	innerFrame.InsetBy(2, 2);
	BPoint origin(sizeX < 0 ? innerFrame.right : innerFrame.left,
		sizeY < 0 ? innerFrame.bottom : innerFrame.top);

	SetHighColor(kBlack);
	FillTriangle(BPoint(origin.x, origin.y), BPoint(origin.x, origin.y + sizeY),
		BPoint(origin.x + sizeX, origin.y));
}


screen_corner
ScreenCornerSelector::_ScreenCorner(BPoint point,
	screen_corner previousCorner) const
{
	BRect innerFrame = _InnerFrame(_MonitorFrame());

	if (!innerFrame.Contains(point))
		return previousCorner;

	if (_CenterFrame(innerFrame).Contains(point))
		return NO_CORNER;

	float centerX = innerFrame.left + innerFrame.Width() / 2;
	float centerY = innerFrame.top + innerFrame.Height() / 2;
	if (point.x < centerX)
		return point.y < centerY ? UP_LEFT_CORNER : DOWN_LEFT_CORNER;

	return point.y < centerY ? UP_RIGHT_CORNER : DOWN_RIGHT_CORNER;
}


void
ScreenCornerSelector::MouseDown(BPoint where)
{
	fPreviousCorner = Value();

	SetValue(_ScreenCorner(where, (screen_corner)fPreviousCorner));
	SetMouseEventMask(B_POINTER_EVENTS, B_NO_POINTER_HISTORY);
}


void
ScreenCornerSelector::MouseUp(BPoint where)
{
	fPreviousCorner = -1;
}


void
ScreenCornerSelector::MouseMoved(BPoint where, uint32 transit,
	const BMessage* dragMessage)
{
	if (fPreviousCorner == -1)
		return;

	SetValue(_ScreenCorner(where, (screen_corner)fPreviousCorner));
}


void
ScreenCornerSelector::KeyDown(const char* bytes, int32 numBytes)
{
	switch (bytes[0]) {
		// arrow keys

		case B_LEFT_ARROW:
		case '4':
			if (Corner() == UP_RIGHT_CORNER)
				SetCorner(UP_LEFT_CORNER);
			else if (Corner() == DOWN_RIGHT_CORNER)
				SetCorner(DOWN_LEFT_CORNER);
			break;
		case B_RIGHT_ARROW:
		case '6':
			if (Corner() == UP_LEFT_CORNER)
				SetCorner(UP_RIGHT_CORNER);
			else if (Corner() == DOWN_LEFT_CORNER)
				SetCorner(DOWN_RIGHT_CORNER);
			break;
		case B_UP_ARROW:
		case '8':
			if (Corner() == DOWN_LEFT_CORNER)
				SetCorner(UP_LEFT_CORNER);
			else if (Corner() == DOWN_RIGHT_CORNER)
				SetCorner(UP_RIGHT_CORNER);
			break;
		case B_DOWN_ARROW:
		case '2':
			if (Corner() == UP_LEFT_CORNER)
				SetCorner(DOWN_LEFT_CORNER);
			else if (Corner() == UP_RIGHT_CORNER)
				SetCorner(DOWN_RIGHT_CORNER);
			break;

		// numlock keys

		case B_HOME:
		case '7':
			SetCorner(UP_LEFT_CORNER);
			break;
		case B_PAGE_UP:
		case '9':
			SetCorner(UP_RIGHT_CORNER);
			break;
		case B_PAGE_DOWN:
		case '3':
			SetCorner(DOWN_RIGHT_CORNER);
			break;
		case B_END:
		case '1':
			SetCorner(DOWN_LEFT_CORNER);
			break;

		default:
			BControl::KeyDown(bytes, numBytes);
	}
}