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