xref: /haiku/src/preferences/keymap/StatusMenuField.cpp (revision 1322e37a03386ffe6321ca3fbb03bd3c4e443074)
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 
35 static const char* kDuplicate = "duplicate";
36 static const char* kUnmatched = "unmatched";
37 
38 
39 //	#pragma mark - StatusMenuItem
40 
41 
42 StatusMenuItem::StatusMenuItem(const char* name, BMessage* message)
43 	:
44 	BMenuItem(name, message),
45 	fIcon(NULL)
46 {
47 }
48 
49 
50 StatusMenuItem::StatusMenuItem(BMessage* archive)
51 	:
52 	BMenuItem(archive),
53 	fIcon(NULL)
54 {
55 }
56 
57 
58 BArchivable*
59 StatusMenuItem::Instantiate(BMessage* data)
60 {
61 	if (validate_instantiation(data, "StatusMenuItem"))
62 		return new StatusMenuItem(data);
63 
64 	return NULL;
65 }
66 
67 
68 status_t
69 StatusMenuItem::Archive(BMessage* data, bool deep) const
70 {
71 	status_t result = BMenuItem::Archive(data, deep);
72 
73 	return result;
74 }
75 
76 
77 void
78 StatusMenuItem::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 
94 void
95 StatusMenuItem::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 
110 BBitmap*
111 StatusMenuItem::Icon()
112 {
113 	return fIcon;
114 }
115 
116 
117 void
118 StatusMenuItem::SetIcon(BBitmap* icon)
119 {
120 	fIcon = icon;
121 }
122 
123 
124 BRect
125 StatusMenuItem::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 
135 BSize
136 StatusMenuItem::IconSize()
137 {
138 	return be_control_look->ComposeIconSize(B_MINI_ICON);
139 }
140 
141 
142 float
143 StatusMenuItem::Spacing()
144 {
145 	return be_control_look->DefaultLabelSpacing();
146 }
147 
148 
149 //	#pragma mark - StatusMenuField
150 
151 
152 StatusMenuField::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 
163 StatusMenuField::~StatusMenuField()
164 {
165 	delete fStopIcon;
166 	delete fWarnIcon;
167 }
168 
169 
170 void
171 StatusMenuField::SetDuplicate(bool on)
172 {
173 	ShowStopIcon(on);
174 	on ? SetStatus(kDuplicate) : ClearStatus();
175 	Invalidate();
176 }
177 
178 
179 void
180 StatusMenuField::SetUnmatched(bool on)
181 {
182 	ShowWarnIcon(on);
183 	on ? SetStatus(kUnmatched) : ClearStatus();
184 	Invalidate();
185 }
186 
187 
188 void
189 StatusMenuField::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 
198 void
199 StatusMenuField::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 
208 void
209 StatusMenuField::ClearStatus()
210 {
211 	fStatus = B_EMPTY_STRING;
212 	SetToolTip((const char*)NULL);
213 }
214 
215 
216 void
217 StatusMenuField::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 
234 void
235 StatusMenuField::_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 
278 BRect
279 StatusMenuField::_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