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