1/*
2 * Copyright 2003-2009, Haiku.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Michael Phipps
7 *		Axel Dörfler, axeld@pinc-software.de
8 */
9
10#include "ScreenCornerSelector.h"
11#include "Constants.h"
12#include "Utility.h"
13
14#include <Rect.h>
15#include <Point.h>
16#include <Shape.h>
17#include <Screen.h>
18#include <Window.h>
19
20#include <stdio.h>
21
22
23static const float kAspectRatio = 4.0f / 3.0f;
24static const float kMonitorBorderSize = 3.0f;
25static const float kArrowSize = 11.0f;
26static const float kStopSize = 15.0f;
27
28
29ScreenCornerSelector::ScreenCornerSelector(BRect frame, const char *name,
30		BMessage* message, uint32 resizingMode)
31	: BControl(frame, name, NULL, message, resizingMode,
32		B_WILL_DRAW | B_NAVIGABLE),
33	fCurrentCorner(NO_CORNER),
34	fPreviousCorner(-1)
35{
36	SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
37}
38
39
40BRect
41ScreenCornerSelector::_MonitorFrame() const
42{
43	float width = Bounds().Width();
44	float height = Bounds().Height();
45
46	if (width / kAspectRatio > height)
47		width = height * kAspectRatio;
48	else if (height * kAspectRatio > width)
49		height = width / kAspectRatio;
50
51	return BRect((Bounds().Width() - width) / 2,
52		(Bounds().Height() - height) / 2,
53		(Bounds().Width() + width) / 2, (Bounds().Height() + height) / 2);
54}
55
56
57BRect
58ScreenCornerSelector::_InnerFrame(BRect monitorFrame) const
59{
60	return monitorFrame.InsetByCopy(kMonitorBorderSize + 3,
61		kMonitorBorderSize + 3);
62}
63
64
65BRect
66ScreenCornerSelector::_CenterFrame(BRect innerFrame) const
67{
68	return innerFrame.InsetByCopy(kArrowSize, kArrowSize);
69}
70
71
72void
73ScreenCornerSelector::Draw(BRect update)
74{
75	rgb_color darkColor = {160, 160, 160, 255};
76	rgb_color blackColor = {0, 0, 0, 255};
77	rgb_color redColor = {228, 0, 0, 255};
78
79	BRect outerRect = _MonitorFrame();
80	BRect innerRect(outerRect.InsetByCopy(kMonitorBorderSize + 2,
81		kMonitorBorderSize + 2));
82
83	SetDrawingMode(B_OP_OVER);
84
85	if (!_InnerFrame(outerRect).Contains(update)) {
86		// frame & background
87
88		// if the focus is changing, we don't redraw the whole view, but only
89		// the part that's affected by the change
90		if (!IsFocusChanging()) {
91			SetHighColor(darkColor);
92			FillRoundRect(outerRect, kMonitorBorderSize * 3 / 2,
93				kMonitorBorderSize * 3 / 2);
94		}
95
96		if (IsFocus() && Window()->IsActive())
97			SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR));
98		else
99			SetHighColor(blackColor);
100
101		StrokeRoundRect(outerRect, kMonitorBorderSize * 3 / 2,
102			kMonitorBorderSize * 3 / 2);
103
104		if (!IsFocusChanging()) {
105			SetHighColor(210, 210, 255);
106			FillRoundRect(innerRect, kMonitorBorderSize, kMonitorBorderSize);
107		}
108
109		if (IsFocus() && Window()->IsActive())
110			SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR));
111		else
112			SetHighColor(blackColor);
113		StrokeRoundRect(innerRect, kMonitorBorderSize, kMonitorBorderSize);
114
115		if (IsFocusChanging())
116			return;
117
118		// power light
119
120		SetHighColor(redColor);
121		BPoint powerPos(outerRect.left + kMonitorBorderSize * 2, outerRect.bottom
122			- kMonitorBorderSize);
123		StrokeLine(powerPos, BPoint(powerPos.x + 2, powerPos.y));
124	}
125
126	innerRect = _InnerFrame(outerRect);
127
128	if (fCurrentCorner != NO_CORNER)
129		_DrawArrow(innerRect);
130	else
131		_DrawStop(innerRect);
132
133	SetDrawingMode(B_OP_COPY);
134}
135
136
137int32
138ScreenCornerSelector::Value()
139{
140	return (int32)fCurrentCorner;
141}
142
143
144void
145ScreenCornerSelector::SetValue(int32 corner)
146{
147	switch (corner) {
148		case UP_LEFT_CORNER:
149		case UP_RIGHT_CORNER:
150		case DOWN_LEFT_CORNER:
151		case DOWN_RIGHT_CORNER:
152		case NO_CORNER:
153			break;
154
155		default:
156			corner = NO_CORNER;
157	}
158	if ((screen_corner)corner == fCurrentCorner)
159		return;
160
161	fCurrentCorner = (screen_corner)corner;
162	Invalidate(_InnerFrame(_MonitorFrame()));
163	Invoke();
164}
165
166
167screen_corner
168ScreenCornerSelector::Corner() const
169{
170	return fCurrentCorner;
171}
172
173
174void
175ScreenCornerSelector::SetCorner(screen_corner corner)
176{
177	// redirected to SetValue() to make sure only valid values are set
178	SetValue((int32)corner);
179}
180
181
182void
183ScreenCornerSelector::_DrawStop(BRect innerFrame)
184{
185	BRect centerRect = _CenterFrame(innerFrame);
186	float size = kStopSize;
187	BRect rect;
188	rect.left = centerRect.left + (centerRect.Width() - size) / 2;
189	rect.top = centerRect.top + (centerRect.Height() - size) / 2;
190	if (rect.left < centerRect.left || rect.top < centerRect.top) {
191		size = centerRect.Height();
192		rect.top = centerRect.top;
193		rect.left = centerRect.left + (centerRect.Width() - size) / 2;
194	}
195	rect.right = rect.left + size - 1;
196	rect.bottom = rect.top + size - 1;
197
198	SetHighColor(255, 0, 0);
199	SetPenSize(2);
200	StrokeEllipse(rect);
201
202	size -= ceilf(sin(M_PI / 4) * size + 2);
203	rect.InsetBy(size, size);
204	StrokeLine(rect.RightTop(), rect.LeftBottom());
205
206	SetPenSize(1);
207}
208
209
210void
211ScreenCornerSelector::_DrawArrow(BRect innerFrame)
212{
213	float size = kArrowSize;
214	float sizeX = fCurrentCorner == UP_LEFT_CORNER
215		|| fCurrentCorner == DOWN_LEFT_CORNER ? size : -size;
216	float sizeY = fCurrentCorner == UP_LEFT_CORNER
217		|| fCurrentCorner == UP_RIGHT_CORNER ? size : -size;
218
219	innerFrame.InsetBy(2, 2);
220	BPoint origin(sizeX < 0 ? innerFrame.right : innerFrame.left,
221		sizeY < 0 ? innerFrame.bottom : innerFrame.top);
222
223	SetHighColor(kBlack);
224	FillTriangle(BPoint(origin.x, origin.y), BPoint(origin.x, origin.y + sizeY),
225		BPoint(origin.x + sizeX, origin.y));
226}
227
228
229screen_corner
230ScreenCornerSelector::_ScreenCorner(BPoint point,
231	screen_corner previousCorner) const
232{
233	BRect innerFrame = _InnerFrame(_MonitorFrame());
234
235	if (!innerFrame.Contains(point))
236		return previousCorner;
237
238	if (_CenterFrame(innerFrame).Contains(point))
239		return NO_CORNER;
240
241	float centerX = innerFrame.left + innerFrame.Width() / 2;
242	float centerY = innerFrame.top + innerFrame.Height() / 2;
243	if (point.x < centerX)
244		return point.y < centerY ? UP_LEFT_CORNER : DOWN_LEFT_CORNER;
245
246	return point.y < centerY ? UP_RIGHT_CORNER : DOWN_RIGHT_CORNER;
247}
248
249
250void
251ScreenCornerSelector::MouseDown(BPoint where)
252{
253	fPreviousCorner = Value();
254
255	SetValue(_ScreenCorner(where, (screen_corner)fPreviousCorner));
256	SetMouseEventMask(B_POINTER_EVENTS, B_NO_POINTER_HISTORY);
257}
258
259
260void
261ScreenCornerSelector::MouseUp(BPoint where)
262{
263	fPreviousCorner = -1;
264}
265
266
267void
268ScreenCornerSelector::MouseMoved(BPoint where, uint32 transit,
269	const BMessage* dragMessage)
270{
271	if (fPreviousCorner == -1)
272		return;
273
274	SetValue(_ScreenCorner(where, (screen_corner)fPreviousCorner));
275}
276
277
278void
279ScreenCornerSelector::KeyDown(const char* bytes, int32 numBytes)
280{
281	switch (bytes[0]) {
282		// arrow keys
283
284		case B_LEFT_ARROW:
285		case '4':
286			if (Corner() == UP_RIGHT_CORNER)
287				SetCorner(UP_LEFT_CORNER);
288			else if (Corner() == DOWN_RIGHT_CORNER)
289				SetCorner(DOWN_LEFT_CORNER);
290			break;
291		case B_RIGHT_ARROW:
292		case '6':
293			if (Corner() == UP_LEFT_CORNER)
294				SetCorner(UP_RIGHT_CORNER);
295			else if (Corner() == DOWN_LEFT_CORNER)
296				SetCorner(DOWN_RIGHT_CORNER);
297			break;
298		case B_UP_ARROW:
299		case '8':
300			if (Corner() == DOWN_LEFT_CORNER)
301				SetCorner(UP_LEFT_CORNER);
302			else if (Corner() == DOWN_RIGHT_CORNER)
303				SetCorner(UP_RIGHT_CORNER);
304			break;
305		case B_DOWN_ARROW:
306		case '2':
307			if (Corner() == UP_LEFT_CORNER)
308				SetCorner(DOWN_LEFT_CORNER);
309			else if (Corner() == UP_RIGHT_CORNER)
310				SetCorner(DOWN_RIGHT_CORNER);
311			break;
312
313		// numlock keys
314
315		case B_HOME:
316		case '7':
317			SetCorner(UP_LEFT_CORNER);
318			break;
319		case B_PAGE_UP:
320		case '9':
321			SetCorner(UP_RIGHT_CORNER);
322			break;
323		case B_PAGE_DOWN:
324		case '3':
325			SetCorner(DOWN_RIGHT_CORNER);
326			break;
327		case B_END:
328		case '1':
329			SetCorner(DOWN_LEFT_CORNER);
330			break;
331
332		default:
333			BControl::KeyDown(bytes, numBytes);
334	}
335}
336
337