1/*
2 * Copyright 2023 Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		John Scipione, jscipione@gmail.com
7 */
8
9
10#include "StatusMenuField.h"
11
12#include <stdio.h>
13#include <stdlib.h>
14#include <string.h>
15
16#include <Bitmap.h>
17#include <Catalog.h>
18#include <ControlLook.h>
19#include <IconUtils.h>
20#include <InterfaceDefs.h>
21#include <LayoutUtils.h>
22
23
24#undef B_TRANSLATION_CONTEXT
25#define B_TRANSLATION_CONTEXT "Modifier keys window"
26
27
28#ifdef DEBUG_ALERT
29#	define FTRACE(x) fprintf(x)
30#else
31#	define FTRACE(x) /* nothing */
32#endif
33
34
35static const char* kDuplicate = "duplicate";
36static const char* kUnmatched = "unmatched";
37
38
39//	#pragma mark - StatusMenuItem
40
41
42StatusMenuItem::StatusMenuItem(const char* name, BMessage* message)
43	:
44	BMenuItem(name, message),
45	fIcon(NULL)
46{
47}
48
49
50StatusMenuItem::StatusMenuItem(BMessage* archive)
51	:
52	BMenuItem(archive),
53	fIcon(NULL)
54{
55}
56
57
58BArchivable*
59StatusMenuItem::Instantiate(BMessage* data)
60{
61	if (validate_instantiation(data, "StatusMenuItem"))
62		return new StatusMenuItem(data);
63
64	return NULL;
65}
66
67
68status_t
69StatusMenuItem::Archive(BMessage* data, bool deep) const
70{
71	status_t result = BMenuItem::Archive(data, deep);
72
73	return result;
74}
75
76
77void
78StatusMenuItem::DrawContent()
79{
80	if (fIcon == NULL)
81		return BMenuItem::DrawContent();
82
83	// blend transparency
84	Menu()->SetDrawingMode(B_OP_ALPHA);
85	Menu()->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_OVERLAY);
86
87	// draw bitmap
88	Menu()->DrawBitmapAsync(fIcon, IconRect().LeftTop());
89
90	BMenuItem::DrawContent();
91}
92
93
94void
95StatusMenuItem::GetContentSize(float* _width, float* _height)
96{
97	float width;
98	float height;
99	BMenuItem::GetContentSize(&width, &height);
100
101	// prevent label from drawing over icon
102	if (_width != NULL)
103		*_width = Menu()->Bounds().Width() - IconRect().Width() - Spacing() * 4;
104
105	if (_height != NULL)
106		*_height = height;
107}
108
109
110BBitmap*
111StatusMenuItem::Icon()
112{
113	return fIcon;
114}
115
116
117void
118StatusMenuItem::SetIcon(BBitmap* icon)
119{
120	fIcon = icon;
121}
122
123
124BRect
125StatusMenuItem::IconRect()
126{
127	BRect bounds(Menu()->Bounds());
128	bounds.top += roundf(Spacing() / 2); // center inside menu field vertically
129	bounds.right -= Spacing() * 3; // move inside menu field horizontally
130	return BLayoutUtils::AlignInFrame(bounds, IconSize(),
131		BAlignment(B_ALIGN_RIGHT, B_ALIGN_TOP));
132}
133
134
135BSize
136StatusMenuItem::IconSize()
137{
138	return be_control_look->ComposeIconSize(B_MINI_ICON);
139}
140
141
142float
143StatusMenuItem::Spacing()
144{
145	return be_control_look->DefaultLabelSpacing();
146}
147
148
149//	#pragma mark - StatusMenuField
150
151
152StatusMenuField::StatusMenuField(const char* label, BMenu* menu)
153	:
154	BMenuField(label, menu),
155	fStatus(B_EMPTY_STRING),
156	fStopIcon(NULL),
157	fWarnIcon(NULL)
158{
159	_FillIcons();
160}
161
162
163StatusMenuField::~StatusMenuField()
164{
165	delete fStopIcon;
166	delete fWarnIcon;
167}
168
169
170void
171StatusMenuField::SetDuplicate(bool on)
172{
173	ShowStopIcon(on);
174	on ? SetStatus(kDuplicate) : ClearStatus();
175	Invalidate();
176}
177
178
179void
180StatusMenuField::SetUnmatched(bool on)
181{
182	ShowWarnIcon(on);
183	on ? SetStatus(kUnmatched) : ClearStatus();
184	Invalidate();
185}
186
187
188void
189StatusMenuField::ShowStopIcon(bool show)
190{
191	// show or hide the stop icon
192	StatusMenuItem* item = dynamic_cast<StatusMenuItem*>(MenuItem());
193	if (item != NULL)
194		item->SetIcon(show ? fStopIcon : NULL);
195}
196
197
198void
199StatusMenuField::ShowWarnIcon(bool show)
200{
201	// show or hide the warn icon
202	StatusMenuItem* item = dynamic_cast<StatusMenuItem*>(MenuItem());
203	if (item != NULL)
204		item->SetIcon(show ? fWarnIcon : NULL);
205}
206
207
208void
209StatusMenuField::ClearStatus()
210{
211	fStatus = B_EMPTY_STRING;
212	SetToolTip((const char*)NULL);
213}
214
215
216void
217StatusMenuField::SetStatus(BString status)
218{
219	fStatus = status;
220
221	const char* tooltip = B_EMPTY_STRING;
222	if (fStatus == kDuplicate)
223		tooltip = B_TRANSLATE("Error: duplicate keys");
224	else if (fStatus == kUnmatched)
225		tooltip = B_TRANSLATE("Warning: left and right key roles do not match");
226
227	SetToolTip(tooltip);
228}
229
230
231//	#pragma mark - StatusMenuField private methods
232
233
234void
235StatusMenuField::_FillIcons()
236{
237	// fill out the icons with the stop and warn icons from app_server
238	// TODO find better icons
239
240	if (fStopIcon == NULL) {
241		// allocate the fStopIcon bitmap
242		fStopIcon = new (std::nothrow) BBitmap(_IconRect(), 0, B_RGBA32);
243		if (fStopIcon == NULL || fStopIcon->InitCheck() != B_OK) {
244			FTRACE((stderr, "MKW::_FillIcons() - No memory for stop bitmap\n"));
245			delete fStopIcon;
246			fStopIcon = NULL;
247			return;
248		}
249
250		// load dialog-error icon bitmap
251		if (BIconUtils::GetSystemIcon("dialog-error", fStopIcon) != B_OK) {
252			delete fStopIcon;
253			fStopIcon = NULL;
254			return;
255		}
256	}
257
258	if (fWarnIcon == NULL) {
259		// allocate the fWarnIcon bitmap
260		fWarnIcon = new (std::nothrow) BBitmap(_IconRect(), 0, B_RGBA32);
261		if (fWarnIcon == NULL || fWarnIcon->InitCheck() != B_OK) {
262			FTRACE((stderr, "MKW::_FillIcons() - No memory for warn bitmap\n"));
263			delete fWarnIcon;
264			fWarnIcon = NULL;
265			return;
266		}
267
268		// load dialog-warning icon bitmap
269		if (BIconUtils::GetSystemIcon("dialog-warning", fWarnIcon) != B_OK) {
270			delete fWarnIcon;
271			fWarnIcon = NULL;
272			return;
273		}
274	}
275}
276
277
278BRect
279StatusMenuField::_IconRect()
280{
281	BSize iconSize = be_control_look->ComposeIconSize(B_MINI_ICON);
282	return BRect(0, 0, iconSize.Width() - 1, iconSize.Height() - 1);
283}
284