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, BSize(ListIconSize() - 1, ListIconSize() - 1)); 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, BSize(ListIconSize() - 1, ListIconSize() - 1), 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 185 const float iconSize = ListIconSize(); 186 fHeightDelta = iconSize - *height; 187 if (*height < iconSize) 188 *height = iconSize; 189 *width += iconSize / 4 + iconSize + (fExtraPad ? 18 : 0); 190 } 191 192 193 status_t 194 ModelMenuItem::Invoke(BMessage* message) 195 { 196 if (Menu() == NULL) 197 return B_ERROR; 198 199 if (!IsEnabled()) 200 return B_ERROR; 201 202 if (message == NULL) 203 message = Message(); 204 205 if (message == NULL) 206 return B_BAD_VALUE; 207 208 BMessage clone(*message); 209 clone.AddInt32("index", Menu()->IndexOf(this)); 210 clone.AddInt64("when", system_time()); 211 clone.AddPointer("source", this); 212 213 if ((modifiers() & B_OPTION_KEY) == 0) { 214 // if option not held, remove refs to close to prevent closing 215 // parent window 216 clone.RemoveData("nodeRefsToClose"); 217 } 218 219 return BInvoker::Invoke(&clone); 220 } 221 222 223 // #pragma mark - SpecialModelMenuItem 224 225 226 /*! A ModelMenuItem subclass that draws its label in italics. 227 228 It's used for example in the "Copy To" menu to indicate some special 229 folders like the parent folder. 230 */ 231 SpecialModelMenuItem::SpecialModelMenuItem(const Model* model, BMenu* menu) 232 : ModelMenuItem(model, menu) 233 { 234 } 235 236 237 void 238 SpecialModelMenuItem::DrawContent() 239 { 240 Menu()->PushState(); 241 242 BFont font; 243 Menu()->GetFont(&font); 244 font.SetFace(B_ITALIC_FACE); 245 Menu()->SetFont(&font); 246 247 _inherited::DrawContent(); 248 Menu()->PopState(); 249 } 250 251 252 // #pragma mark - IconMenuItem 253 254 255 /*! A menu item that draws an icon alongside the label. 256 257 It's currently used in the mount and new file template menus. 258 */ 259 IconMenuItem::IconMenuItem(const char* label, BMessage* message, BBitmap* icon, 260 icon_size which) 261 : 262 PositionPassingMenuItem(label, message), 263 fDeviceIcon(NULL), 264 fHeightDelta(0), 265 fWhich(which) 266 { 267 SetIcon(icon); 268 269 // IconMenuItem is used in synchronously invoked menus, make sure 270 // we invoke with a timeout 271 SetTimeout(kSynchMenuInvokeTimeout); 272 } 273 274 275 IconMenuItem::IconMenuItem(const char* label, BMessage* message, 276 const BNodeInfo* nodeInfo, icon_size which) 277 : 278 PositionPassingMenuItem(label, message), 279 fDeviceIcon(NULL), 280 fHeightDelta(0), 281 fWhich(which) 282 { 283 if (nodeInfo != NULL) { 284 fDeviceIcon = new BBitmap(BRect(BPoint(0, 0), 285 be_control_look->ComposeIconSize(which)), kDefaultIconDepth); 286 if (nodeInfo->GetTrackerIcon(fDeviceIcon, (icon_size)-1) != 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(BPoint(0, 0), be_control_look->ComposeIconSize(which)), 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(BPoint(0, 0), be_control_look->ComposeIconSize(which)), 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(BPoint(0, 0), be_control_look->ComposeIconSize(fWhich)), 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 int32 iconHeight = fWhich; 421 if (fDeviceIcon != NULL) 422 iconHeight = fDeviceIcon->Bounds().IntegerHeight() + 1; 423 424 fHeightDelta = iconHeight - *height; 425 if (*height < iconHeight) 426 *height = iconHeight; 427 428 if (fDeviceIcon != NULL) 429 *width += fDeviceIcon->Bounds().Width() + be_control_look->DefaultLabelSpacing(); 430 } 431 432 433 void 434 IconMenuItem::DrawContent() 435 { 436 BPoint drawPoint(ContentLocation()); 437 if (fDeviceIcon != NULL) 438 drawPoint.x += fDeviceIcon->Bounds().Width() + be_control_look->DefaultLabelSpacing(); 439 440 if (fHeightDelta > 0) 441 drawPoint.y += ceilf(fHeightDelta / 2); 442 443 Menu()->MovePenTo(drawPoint); 444 _inherited::DrawContent(); 445 446 Menu()->PushState(); 447 448 BPoint where(ContentLocation()); 449 float deltaHeight = fHeightDelta < 0 ? -fHeightDelta : 0; 450 where.y += ceilf(deltaHeight / 2); 451 452 if (fDeviceIcon != NULL) { 453 if (IsEnabled()) 454 Menu()->SetDrawingMode(B_OP_ALPHA); 455 else { 456 Menu()->SetDrawingMode(B_OP_ALPHA); 457 Menu()->SetHighColor(0, 0, 0, 64); 458 Menu()->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_OVERLAY); 459 } 460 Menu()->DrawBitmapAsync(fDeviceIcon, where); 461 } 462 463 Menu()->PopState(); 464 } 465 466 467 void 468 IconMenuItem::SetMarked(bool mark) 469 { 470 _inherited::SetMarked(mark); 471 472 if (!mark) 473 return; 474 475 // we are marking the item 476 477 BMenu* menu = Menu(); 478 if (menu == NULL) 479 return; 480 481 // we have a parent menu 482 483 BMenu* _menu = menu; 484 while ((_menu = _menu->Supermenu()) != NULL) 485 menu = _menu; 486 487 // went up the hierarchy to found the topmost menu 488 489 if (menu == NULL || menu->Parent() == NULL) 490 return; 491 492 // our topmost menu has a parent 493 494 if (dynamic_cast<BMenuField*>(menu->Parent()) == NULL) 495 return; 496 497 // our topmost menu's parent is a BMenuField 498 499 BMenuItem* topLevelItem = menu->ItemAt((int32)0); 500 501 if (topLevelItem == NULL) 502 return; 503 504 // our topmost menu has a menu item 505 506 IconMenuItem* topLevelMenuItem = dynamic_cast<IconMenuItem*>(topLevelItem); 507 if (topLevelMenuItem == NULL) 508 return; 509 510 // our topmost menu's item is an IconMenuItem 511 512 // update the icon 513 topLevelMenuItem->SetIcon(fDeviceIcon); 514 menu->Invalidate(); 515 } 516 517 518 void 519 IconMenuItem::SetIcon(BBitmap* icon) 520 { 521 if (icon != NULL) { 522 if (fDeviceIcon != NULL) 523 delete fDeviceIcon; 524 525 fDeviceIcon = new BBitmap(BRect(BPoint(0, 0), 526 be_control_look->ComposeIconSize(fWhich)), icon->ColorSpace()); 527 fDeviceIcon->ImportBits(icon); 528 } else { 529 delete fDeviceIcon; 530 fDeviceIcon = NULL; 531 } 532 } 533