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
StatusMenuItem(const char * name,BMessage * message)42 StatusMenuItem::StatusMenuItem(const char* name, BMessage* message)
43 :
44 BMenuItem(name, message),
45 fIcon(NULL)
46 {
47 }
48
49
StatusMenuItem(BMessage * archive)50 StatusMenuItem::StatusMenuItem(BMessage* archive)
51 :
52 BMenuItem(archive),
53 fIcon(NULL)
54 {
55 }
56
57
58 BArchivable*
Instantiate(BMessage * data)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
Archive(BMessage * data,bool deep) const69 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
DrawContent()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
GetContentSize(float * _width,float * _height)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*
Icon()111 StatusMenuItem::Icon()
112 {
113 return fIcon;
114 }
115
116
117 void
SetIcon(BBitmap * icon)118 StatusMenuItem::SetIcon(BBitmap* icon)
119 {
120 fIcon = icon;
121 }
122
123
124 BRect
IconRect()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
IconSize()136 StatusMenuItem::IconSize()
137 {
138 return be_control_look->ComposeIconSize(B_MINI_ICON);
139 }
140
141
142 float
Spacing()143 StatusMenuItem::Spacing()
144 {
145 return be_control_look->DefaultLabelSpacing();
146 }
147
148
149 // #pragma mark - StatusMenuField
150
151
StatusMenuField(const char * label,BMenu * menu)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
~StatusMenuField()163 StatusMenuField::~StatusMenuField()
164 {
165 delete fStopIcon;
166 delete fWarnIcon;
167 }
168
169
170 void
SetDuplicate(bool on)171 StatusMenuField::SetDuplicate(bool on)
172 {
173 ShowStopIcon(on);
174 on ? SetStatus(kDuplicate) : ClearStatus();
175 Invalidate();
176 }
177
178
179 void
SetUnmatched(bool on)180 StatusMenuField::SetUnmatched(bool on)
181 {
182 ShowWarnIcon(on);
183 on ? SetStatus(kUnmatched) : ClearStatus();
184 Invalidate();
185 }
186
187
188 void
ShowStopIcon(bool show)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
ShowWarnIcon(bool show)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
ClearStatus()209 StatusMenuField::ClearStatus()
210 {
211 fStatus = B_EMPTY_STRING;
212 SetToolTip((const char*)NULL);
213 }
214
215
216 void
SetStatus(BString status)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
_FillIcons()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
_IconRect()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