1 /* 2 Open Tracker License 3 4 Terms and Conditions 5 6 Copyright (c) 1991-2000, Be Incorporated. All rights reserved. 7 8 Permission is hereby granted, free of charge, to any person obtaining a copy of 9 this software and associated documentation files (the "Software"), to deal in 10 the Software without restriction, including without limitation the rights to 11 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 12 of the Software, and to permit persons to whom the Software is furnished to do 13 so, subject to the following conditions: 14 15 The above copyright notice and this permission notice applies to all licensees 16 and shall be included in all copies or substantial portions of the Software. 17 18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY, 20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 21 BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 22 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION 23 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 25 Except as contained in this notice, the name of Be Incorporated shall not be 26 used in advertising or otherwise to promote the sale, use or other dealings in 27 this Software without prior written authorization from Be Incorporated. 28 29 Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks 30 of Be Incorporated in the United States and other countries. Other brand product 31 names are registered trademarks or trademarks of their respective holders. 32 All rights reserved. 33 */ 34 35 //! Menu items with small icons. 36 37 38 #include "IconMenuItem.h" 39 40 #include <ControlLook.h> 41 #include <Debug.h> 42 #include <Menu.h> 43 #include <MenuField.h> 44 #include <NodeInfo.h> 45 46 #include "IconCache.h" 47 48 49 static void 50 DimmedIconBlitter(BView* view, BPoint where, BBitmap* bitmap, void*) 51 { 52 if (bitmap->ColorSpace() == B_RGBA32) { 53 rgb_color oldHighColor = view->HighColor(); 54 view->SetHighColor(0, 0, 0, 128); 55 view->SetDrawingMode(B_OP_ALPHA); 56 view->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_OVERLAY); 57 view->DrawBitmap(bitmap, where); 58 view->SetHighColor(oldHighColor); 59 } else { 60 view->SetDrawingMode(B_OP_BLEND); 61 view->DrawBitmap(bitmap, where); 62 } 63 view->SetDrawingMode(B_OP_OVER); 64 } 65 66 67 // #pragma mark - ModelMenuItem 68 69 70 ModelMenuItem::ModelMenuItem(const Model* model, const char* title, 71 BMessage* message, char shortcut, uint32 modifiers, 72 bool drawText, bool extraPad) 73 : 74 BMenuItem(title, message, shortcut, modifiers), 75 fModel(*model), 76 fHeightDelta(0), 77 fDrawText(drawText), 78 fExtraPad(extraPad) 79 { 80 ThrowOnInitCheckError(&fModel); 81 // The 'fExtraPad' field is used to when this menu item is added to 82 // a menubar instead of a menu. Menus and MenuBars space out items 83 // differently (more space around items in a menu). This class wants 84 // to be able to space item the same, no matter where they are. The 85 // fExtraPad field allows for that. 86 87 if (model->IsRoot()) 88 SetLabel(model->Name()); 89 90 // ModelMenuItem is used in synchronously invoked menus, make sure 91 // we invoke with a timeout 92 SetTimeout(kSynchMenuInvokeTimeout); 93 } 94 95 96 ModelMenuItem::ModelMenuItem(const Model* model, BMenu* menu, bool drawText, 97 bool extraPad) 98 : 99 BMenuItem(menu), 100 fModel(*model), 101 fHeightDelta(0), 102 fDrawText(drawText), 103 fExtraPad(extraPad) 104 { 105 ThrowOnInitCheckError(&fModel); 106 // ModelMenuItem is used in synchronously invoked menus, make sure 107 // we invoke with a timeout 108 SetTimeout(kSynchMenuInvokeTimeout); 109 } 110 111 112 ModelMenuItem::~ModelMenuItem() 113 { 114 } 115 116 117 status_t 118 ModelMenuItem::SetEntry(const BEntry* entry) 119 { 120 return fModel.SetTo(entry); 121 } 122 123 124 void 125 ModelMenuItem::DrawContent() 126 { 127 if (fDrawText) { 128 BPoint drawPoint(ContentLocation()); 129 drawPoint.x += ListIconSize() + ListIconSize() / 4 130 + (fExtraPad ? 6 : 0); 131 if (fHeightDelta > 0) 132 drawPoint.y += ceil(fHeightDelta / 2); 133 Menu()->MovePenTo(drawPoint); 134 _inherited::DrawContent(); 135 } 136 DrawIcon(); 137 } 138 139 140 void 141 ModelMenuItem::Highlight(bool hilited) 142 { 143 _inherited::Highlight(hilited); 144 DrawIcon(); 145 } 146 147 148 void 149 ModelMenuItem::DrawIcon() 150 { 151 Menu()->PushState(); 152 153 BPoint where(ContentLocation()); 154 // center icon with text. 155 156 float deltaHeight = fHeightDelta < 0 ? -fHeightDelta : 0; 157 where.y += ceil(deltaHeight / 2); 158 159 if (fExtraPad) 160 where.x += 6; 161 162 Menu()->SetDrawingMode(B_OP_OVER); 163 Menu()->SetLowColor(B_TRANSPARENT_32_BIT); 164 165 // draw small icon, synchronously 166 if (IsEnabled()) { 167 IconCache::sIconCache->Draw(fModel.ResolveIfLink(), Menu(), where, 168 kNormalIcon, (icon_size)ListIconSize()); 169 } else { 170 // dimmed, for now use a special blitter; icon cache should 171 // know how to blit one eventually 172 IconCache::sIconCache->SyncDraw(fModel.ResolveIfLink(), Menu(), where, 173 kNormalIcon, (icon_size)ListIconSize(), DimmedIconBlitter); 174 } 175 176 Menu()->PopState(); 177 } 178 179 180 void 181 ModelMenuItem::GetContentSize(float* width, float* height) 182 { 183 _inherited::GetContentSize(width, height); 184 float iconSize = ListIconSize(); 185 fHeightDelta = iconSize - *height; 186 if (*height < iconSize) 187 *height = iconSize; 188 *width = *width + iconSize / 4 + iconSize + (fExtraPad ? 18 : 0); 189 } 190 191 192 status_t 193 ModelMenuItem::Invoke(BMessage* message) 194 { 195 if (Menu() == NULL) 196 return B_ERROR; 197 198 if (!IsEnabled()) 199 return B_ERROR; 200 201 if (message == NULL) 202 message = Message(); 203 204 if (message == NULL) 205 return B_BAD_VALUE; 206 207 BMessage clone(*message); 208 clone.AddInt32("index", Menu()->IndexOf(this)); 209 clone.AddInt64("when", system_time()); 210 clone.AddPointer("source", this); 211 212 if ((modifiers() & B_OPTION_KEY) == 0) { 213 // if option not held, remove refs to close to prevent closing 214 // parent window 215 clone.RemoveData("nodeRefsToClose"); 216 } 217 218 return BInvoker::Invoke(&clone); 219 } 220 221 222 // #pragma mark - SpecialModelMenuItem 223 224 225 /*! A ModelMenuItem subclass that draws its label in italics. 226 227 It's used for example in the "Copy To" menu to indicate some special 228 folders like the parent folder. 229 */ 230 SpecialModelMenuItem::SpecialModelMenuItem(const Model* model, BMenu* menu) 231 : ModelMenuItem(model, menu) 232 { 233 } 234 235 236 void 237 SpecialModelMenuItem::DrawContent() 238 { 239 Menu()->PushState(); 240 241 BFont font; 242 Menu()->GetFont(&font); 243 font.SetFace(B_ITALIC_FACE); 244 Menu()->SetFont(&font); 245 246 _inherited::DrawContent(); 247 Menu()->PopState(); 248 } 249 250 251 // #pragma mark - IconMenuItem 252 253 254 /*! A menu item that draws an icon alongside the label. 255 256 It's currently used in the mount and new file template menus. 257 */ 258 IconMenuItem::IconMenuItem(const char* label, BMessage* message, BBitmap* icon, 259 icon_size which) 260 : 261 PositionPassingMenuItem(label, message), 262 fDeviceIcon(NULL), 263 fHeightDelta(0), 264 fWhich(which) 265 { 266 SetIcon(icon); 267 268 // IconMenuItem is used in synchronously invoked menus, make sure 269 // we invoke with a timeout 270 SetTimeout(kSynchMenuInvokeTimeout); 271 } 272 273 274 IconMenuItem::IconMenuItem(const char* label, BMessage* message, 275 const BNodeInfo* nodeInfo, icon_size which) 276 : 277 PositionPassingMenuItem(label, message), 278 fDeviceIcon(NULL), 279 fHeightDelta(0), 280 fWhich(which) 281 { 282 if (nodeInfo != NULL) { 283 fDeviceIcon = new BBitmap(BRect(0, 0, which - 1, which - 1), 284 kDefaultIconDepth); 285 286 if (nodeInfo->GetTrackerIcon(fDeviceIcon, B_MINI_ICON) != B_OK) { 287 delete fDeviceIcon; 288 fDeviceIcon = NULL; 289 } 290 } 291 292 // IconMenuItem is used in synchronously invoked menus, make sure 293 // we invoke with a timeout 294 SetTimeout(kSynchMenuInvokeTimeout); 295 } 296 297 298 IconMenuItem::IconMenuItem(const char* label, BMessage* message, 299 const char* iconType, icon_size which) 300 : 301 PositionPassingMenuItem(label, message), 302 fDeviceIcon(NULL), 303 fHeightDelta(0), 304 fWhich(which) 305 { 306 BMimeType mime(iconType); 307 fDeviceIcon = new BBitmap(BRect(0, 0, which - 1, which - 1), 308 kDefaultIconDepth); 309 310 if (mime.GetIcon(fDeviceIcon, which) != B_OK) { 311 BMimeType super; 312 mime.GetSupertype(&super); 313 if (super.GetIcon(fDeviceIcon, which) != B_OK) { 314 delete fDeviceIcon; 315 fDeviceIcon = NULL; 316 } 317 } 318 319 // IconMenuItem is used in synchronously invoked menus, make sure 320 // we invoke with a timeout 321 SetTimeout(kSynchMenuInvokeTimeout); 322 } 323 324 325 IconMenuItem::IconMenuItem(BMenu* submenu, BMessage* message, 326 const char* iconType, icon_size which) 327 : 328 PositionPassingMenuItem(submenu, message), 329 fDeviceIcon(NULL), 330 fHeightDelta(0), 331 fWhich(which) 332 { 333 BMimeType mime(iconType); 334 fDeviceIcon = new BBitmap(BRect(0, 0, which - 1, which - 1), 335 kDefaultIconDepth); 336 337 if (mime.GetIcon(fDeviceIcon, which) != B_OK) { 338 BMimeType super; 339 mime.GetSupertype(&super); 340 if (super.GetIcon(fDeviceIcon, which) != B_OK) { 341 delete fDeviceIcon; 342 fDeviceIcon = NULL; 343 } 344 } 345 346 // IconMenuItem is used in synchronously invoked menus, make sure 347 // we invoke with a timeout 348 SetTimeout(kSynchMenuInvokeTimeout); 349 } 350 351 352 IconMenuItem::IconMenuItem(BMessage* data) 353 : 354 PositionPassingMenuItem(data), 355 fDeviceIcon(NULL), 356 fHeightDelta(0), 357 fWhich(B_MINI_ICON) 358 { 359 if (data != NULL) { 360 fWhich = (icon_size)data->GetInt32("_which", B_MINI_ICON); 361 362 fDeviceIcon = new BBitmap(BRect(0, 0, fWhich - 1, fWhich - 1), 363 kDefaultIconDepth); 364 365 if (data->HasData("_deviceIconBits", B_RAW_TYPE)) { 366 ssize_t numBytes; 367 const void* bits; 368 if (data->FindData("_deviceIconBits", B_RAW_TYPE, &bits, &numBytes) 369 == B_OK) { 370 fDeviceIcon->SetBits(bits, numBytes, (int32)0, 371 kDefaultIconDepth); 372 } 373 } 374 } 375 376 // IconMenuItem is used in synchronously invoked menus, make sure 377 // we invoke with a timeout 378 SetTimeout(kSynchMenuInvokeTimeout); 379 } 380 381 382 BArchivable* 383 IconMenuItem::Instantiate(BMessage* data) 384 { 385 //if (validate_instantiation(data, "IconMenuItem")) 386 return new IconMenuItem(data); 387 388 return NULL; 389 } 390 391 392 status_t 393 IconMenuItem::Archive(BMessage* data, bool deep) const 394 { 395 status_t result = PositionPassingMenuItem::Archive(data, deep); 396 397 if (result == B_OK) 398 result = data->AddInt32("_which", (int32)fWhich); 399 400 if (result == B_OK && fDeviceIcon != NULL) { 401 result = data->AddData("_deviceIconBits", B_RAW_TYPE, 402 fDeviceIcon->Bits(), fDeviceIcon->BitsLength()); 403 } 404 405 return result; 406 } 407 408 409 IconMenuItem::~IconMenuItem() 410 { 411 delete fDeviceIcon; 412 } 413 414 415 void 416 IconMenuItem::GetContentSize(float* width, float* height) 417 { 418 _inherited::GetContentSize(width, height); 419 420 fHeightDelta = 16 - *height; 421 if (*height < 16) 422 *height = 16; 423 424 *width += 20; 425 } 426 427 428 void 429 IconMenuItem::DrawContent() 430 { 431 BPoint drawPoint(ContentLocation()); 432 if (fDeviceIcon != NULL) 433 drawPoint.x += (float)fWhich + 4.0f; 434 435 if (fHeightDelta > 0) 436 drawPoint.y += ceilf(fHeightDelta / 2); 437 438 Menu()->MovePenTo(drawPoint); 439 _inherited::DrawContent(); 440 441 Menu()->PushState(); 442 443 BPoint where(ContentLocation()); 444 float deltaHeight = fHeightDelta < 0 ? -fHeightDelta : 0; 445 where.y += ceilf(deltaHeight / 2); 446 447 if (fDeviceIcon != NULL) { 448 if (IsEnabled()) 449 Menu()->SetDrawingMode(B_OP_ALPHA); 450 else { 451 Menu()->SetDrawingMode(B_OP_ALPHA); 452 Menu()->SetHighColor(0, 0, 0, 64); 453 Menu()->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_OVERLAY); 454 } 455 Menu()->DrawBitmapAsync(fDeviceIcon, where); 456 } 457 458 Menu()->PopState(); 459 } 460 461 462 void 463 IconMenuItem::SetMarked(bool mark) 464 { 465 _inherited::SetMarked(mark); 466 467 if (!mark) 468 return; 469 470 // we are marking the item 471 472 BMenu* menu = Menu(); 473 if (menu == NULL) 474 return; 475 476 // we have a parent menu 477 478 BMenu* _menu = menu; 479 while ((_menu = _menu->Supermenu()) != NULL) 480 menu = _menu; 481 482 // went up the hierarchy to found the topmost menu 483 484 if (menu == NULL || menu->Parent() == NULL) 485 return; 486 487 // our topmost menu has a parent 488 489 if (dynamic_cast<BMenuField*>(menu->Parent()) == NULL) 490 return; 491 492 // our topmost menu's parent is a BMenuField 493 494 BMenuItem* topLevelItem = menu->ItemAt((int32)0); 495 496 if (topLevelItem == NULL) 497 return; 498 499 // our topmost menu has a menu item 500 501 IconMenuItem* topLevelIconMenuItem 502 = dynamic_cast<IconMenuItem*>(topLevelItem); 503 if (topLevelIconMenuItem == NULL) 504 return; 505 506 // our topmost menu's item is an IconMenuItem 507 508 // update the icon 509 topLevelIconMenuItem->SetIcon(fDeviceIcon); 510 menu->Invalidate(); 511 } 512 513 514 void 515 IconMenuItem::SetIcon(BBitmap* icon) 516 { 517 if (icon != NULL) { 518 if (fDeviceIcon != NULL) 519 delete fDeviceIcon; 520 521 fDeviceIcon = new BBitmap(BRect(0, 0, fWhich - 1, fWhich - 1), 522 icon->ColorSpace()); 523 fDeviceIcon->SetBits(icon->Bits(), icon->BitsLength(), 0, 524 icon->ColorSpace()); 525 } else { 526 delete fDeviceIcon; 527 fDeviceIcon = NULL; 528 } 529 } 530