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 += 20 + (fExtraPad ? 6 : 0); 130 if (fHeightDelta > 0) 131 drawPoint.y += ceil(fHeightDelta / 2); 132 Menu()->MovePenTo(drawPoint); 133 _inherited::DrawContent(); 134 } 135 DrawIcon(); 136 } 137 138 139 void 140 ModelMenuItem::Highlight(bool hilited) 141 { 142 _inherited::Highlight(hilited); 143 DrawIcon(); 144 } 145 146 147 void 148 ModelMenuItem::DrawIcon() 149 { 150 Menu()->PushState(); 151 152 BPoint where(ContentLocation()); 153 // center icon with text. 154 155 float deltaHeight = fHeightDelta < 0 ? -fHeightDelta : 0; 156 where.y += ceil(deltaHeight / 2); 157 158 if (fExtraPad) 159 where.x += 6; 160 161 Menu()->SetDrawingMode(B_OP_OVER); 162 Menu()->SetLowColor(B_TRANSPARENT_32_BIT); 163 164 // draw small icon, synchronously 165 if (IsEnabled()) { 166 IconCache::sIconCache->Draw(fModel.ResolveIfLink(), Menu(), where, 167 kNormalIcon, B_MINI_ICON); 168 } else { 169 // dimmed, for now use a special blitter; icon cache should 170 // know how to blit one eventually 171 IconCache::sIconCache->SyncDraw(fModel.ResolveIfLink(), Menu(), where, 172 kNormalIcon, B_MINI_ICON, DimmedIconBlitter); 173 } 174 175 Menu()->PopState(); 176 } 177 178 179 void 180 ModelMenuItem::GetContentSize(float* width, float* height) 181 { 182 _inherited::GetContentSize(width, height); 183 fHeightDelta = 16 - *height; 184 if (*height < 16) 185 *height = 16; 186 *width = *width + 20 + (fExtraPad ? 18 : 0); 187 } 188 189 190 status_t 191 ModelMenuItem::Invoke(BMessage* message) 192 { 193 if (Menu() == NULL) 194 return B_ERROR; 195 196 if (!IsEnabled()) 197 return B_ERROR; 198 199 if (message == NULL) 200 message = Message(); 201 202 if (message == NULL) 203 return B_BAD_VALUE; 204 205 BMessage clone(*message); 206 clone.AddInt32("index", Menu()->IndexOf(this)); 207 clone.AddInt64("when", system_time()); 208 clone.AddPointer("source", this); 209 210 if ((modifiers() & B_OPTION_KEY) == 0) { 211 // if option not held, remove refs to close to prevent closing 212 // parent window 213 clone.RemoveData("nodeRefsToClose"); 214 } 215 216 return BInvoker::Invoke(&clone); 217 } 218 219 220 // #pragma mark - SpecialModelMenuItem 221 222 223 /*! A ModelMenuItem subclass that draws its label in italics. 224 225 It's used for example in the "Copy To" menu to indicate some special 226 folders like the parent folder. 227 */ 228 SpecialModelMenuItem::SpecialModelMenuItem(const Model* model, BMenu* menu) 229 : ModelMenuItem(model, menu) 230 { 231 } 232 233 234 void 235 SpecialModelMenuItem::DrawContent() 236 { 237 Menu()->PushState(); 238 239 BFont font; 240 Menu()->GetFont(&font); 241 font.SetFace(B_ITALIC_FACE); 242 Menu()->SetFont(&font); 243 244 _inherited::DrawContent(); 245 Menu()->PopState(); 246 } 247 248 249 // #pragma mark - IconMenuItem 250 251 252 /*! A menu item that draws an icon alongside the label. 253 254 It's currently used in the mount and new file template menus. 255 */ 256 IconMenuItem::IconMenuItem(const char* label, BMessage* message, BBitmap* icon, 257 icon_size which) 258 : 259 PositionPassingMenuItem(label, message), 260 fDeviceIcon(NULL), 261 fHeightDelta(0), 262 fWhich(which) 263 { 264 SetIcon(icon); 265 266 // IconMenuItem is used in synchronously invoked menus, make sure 267 // we invoke with a timeout 268 SetTimeout(kSynchMenuInvokeTimeout); 269 } 270 271 272 IconMenuItem::IconMenuItem(const char* label, BMessage* message, 273 const BNodeInfo* nodeInfo, icon_size which) 274 : 275 PositionPassingMenuItem(label, message), 276 fDeviceIcon(NULL), 277 fHeightDelta(0), 278 fWhich(which) 279 { 280 if (nodeInfo != NULL) { 281 fDeviceIcon = new BBitmap(BRect(0, 0, which - 1, which - 1), 282 kDefaultIconDepth); 283 284 if (nodeInfo->GetTrackerIcon(fDeviceIcon, B_MINI_ICON) != B_OK) { 285 delete fDeviceIcon; 286 fDeviceIcon = NULL; 287 } 288 } 289 290 // IconMenuItem is used in synchronously invoked menus, make sure 291 // we invoke with a timeout 292 SetTimeout(kSynchMenuInvokeTimeout); 293 } 294 295 296 IconMenuItem::IconMenuItem(const char* label, BMessage* message, 297 const char* iconType, icon_size which) 298 : 299 PositionPassingMenuItem(label, message), 300 fDeviceIcon(NULL), 301 fHeightDelta(0), 302 fWhich(which) 303 { 304 BMimeType mime(iconType); 305 fDeviceIcon = new BBitmap(BRect(0, 0, which - 1, which - 1), 306 kDefaultIconDepth); 307 308 if (mime.GetIcon(fDeviceIcon, which) != B_OK) { 309 BMimeType super; 310 mime.GetSupertype(&super); 311 if (super.GetIcon(fDeviceIcon, which) != B_OK) { 312 delete fDeviceIcon; 313 fDeviceIcon = NULL; 314 } 315 } 316 317 // IconMenuItem is used in synchronously invoked menus, make sure 318 // we invoke with a timeout 319 SetTimeout(kSynchMenuInvokeTimeout); 320 } 321 322 323 IconMenuItem::IconMenuItem(BMenu* submenu, BMessage* message, 324 const char* iconType, icon_size which) 325 : 326 PositionPassingMenuItem(submenu, message), 327 fDeviceIcon(NULL), 328 fHeightDelta(0), 329 fWhich(which) 330 { 331 BMimeType mime(iconType); 332 fDeviceIcon = new BBitmap(BRect(0, 0, which - 1, which - 1), 333 kDefaultIconDepth); 334 335 if (mime.GetIcon(fDeviceIcon, which) != B_OK) { 336 BMimeType super; 337 mime.GetSupertype(&super); 338 if (super.GetIcon(fDeviceIcon, which) != B_OK) { 339 delete fDeviceIcon; 340 fDeviceIcon = NULL; 341 } 342 } 343 344 // IconMenuItem is used in synchronously invoked menus, make sure 345 // we invoke with a timeout 346 SetTimeout(kSynchMenuInvokeTimeout); 347 } 348 349 350 IconMenuItem::IconMenuItem(BMessage* data) 351 : 352 PositionPassingMenuItem(data), 353 fDeviceIcon(NULL), 354 fHeightDelta(0), 355 fWhich(B_MINI_ICON) 356 { 357 if (data != NULL) { 358 fWhich = (icon_size)data->GetInt32("_which", B_MINI_ICON); 359 360 fDeviceIcon = new BBitmap(BRect(0, 0, fWhich - 1, fWhich - 1), 361 kDefaultIconDepth); 362 363 if (data->HasData("_deviceIconBits", B_RAW_TYPE)) { 364 ssize_t numBytes; 365 const void* bits; 366 if (data->FindData("_deviceIconBits", B_RAW_TYPE, &bits, &numBytes) 367 == B_OK) { 368 fDeviceIcon->SetBits(bits, numBytes, (int32)0, 369 kDefaultIconDepth); 370 } 371 } 372 } 373 374 // IconMenuItem is used in synchronously invoked menus, make sure 375 // we invoke with a timeout 376 SetTimeout(kSynchMenuInvokeTimeout); 377 } 378 379 380 BArchivable* 381 IconMenuItem::Instantiate(BMessage* data) 382 { 383 //if (validate_instantiation(data, "IconMenuItem")) 384 return new IconMenuItem(data); 385 386 return NULL; 387 } 388 389 390 status_t 391 IconMenuItem::Archive(BMessage* data, bool deep) const 392 { 393 status_t result = PositionPassingMenuItem::Archive(data, deep); 394 395 if (result == B_OK) 396 result = data->AddInt32("_which", (int32)fWhich); 397 398 if (result == B_OK && fDeviceIcon != NULL) { 399 result = data->AddData("_deviceIconBits", B_RAW_TYPE, 400 fDeviceIcon->Bits(), fDeviceIcon->BitsLength()); 401 } 402 403 return result; 404 } 405 406 407 IconMenuItem::~IconMenuItem() 408 { 409 delete fDeviceIcon; 410 } 411 412 413 void 414 IconMenuItem::GetContentSize(float* width, float* height) 415 { 416 _inherited::GetContentSize(width, height); 417 418 fHeightDelta = 16 - *height; 419 if (*height < 16) 420 *height = 16; 421 422 *width += 20; 423 } 424 425 426 void 427 IconMenuItem::DrawContent() 428 { 429 BPoint drawPoint(ContentLocation()); 430 if (fDeviceIcon != NULL) 431 drawPoint.x += (float)fWhich + 4.0f; 432 433 if (fHeightDelta > 0) 434 drawPoint.y += ceilf(fHeightDelta / 2); 435 436 Menu()->MovePenTo(drawPoint); 437 _inherited::DrawContent(); 438 439 Menu()->PushState(); 440 441 BPoint where(ContentLocation()); 442 float deltaHeight = fHeightDelta < 0 ? -fHeightDelta : 0; 443 where.y += ceilf(deltaHeight / 2); 444 445 if (fDeviceIcon != NULL) { 446 if (IsEnabled()) 447 Menu()->SetDrawingMode(B_OP_ALPHA); 448 else { 449 Menu()->SetDrawingMode(B_OP_ALPHA); 450 Menu()->SetHighColor(0, 0, 0, 64); 451 Menu()->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_OVERLAY); 452 } 453 Menu()->DrawBitmapAsync(fDeviceIcon, where); 454 } 455 456 Menu()->PopState(); 457 } 458 459 460 void 461 IconMenuItem::SetMarked(bool mark) 462 { 463 _inherited::SetMarked(mark); 464 465 if (!mark) 466 return; 467 468 // we are marking the item 469 470 BMenu* menu = Menu(); 471 if (menu == NULL) 472 return; 473 474 // we have a parent menu 475 476 BMenu* _menu = menu; 477 while ((_menu = _menu->Supermenu()) != NULL) 478 menu = _menu; 479 480 // went up the hierarchy to found the topmost menu 481 482 if (menu == NULL || menu->Parent() == NULL) 483 return; 484 485 // our topmost menu has a parent 486 487 if (dynamic_cast<BMenuField*>(menu->Parent()) == NULL) 488 return; 489 490 // our topmost menu's parent is a BMenuField 491 492 BMenuItem* topLevelItem = menu->ItemAt((int32)0); 493 494 if (topLevelItem == NULL) 495 return; 496 497 // our topmost menu has a menu item 498 499 IconMenuItem* topLevelIconMenuItem 500 = dynamic_cast<IconMenuItem*>(topLevelItem); 501 if (topLevelIconMenuItem == NULL) 502 return; 503 504 // our topmost menu's item is an IconMenuItem 505 506 // update the icon 507 topLevelIconMenuItem->SetIcon(fDeviceIcon); 508 menu->Invalidate(); 509 } 510 511 512 void 513 IconMenuItem::SetIcon(BBitmap* icon) 514 { 515 if (icon != NULL) { 516 if (fDeviceIcon != NULL) 517 delete fDeviceIcon; 518 519 fDeviceIcon = new BBitmap(BRect(0, 0, fWhich - 1, fWhich - 1), 520 icon->ColorSpace()); 521 fDeviceIcon->SetBits(icon->Bits(), icon->BitsLength(), 0, 522 icon->ColorSpace()); 523 } else { 524 delete fDeviceIcon; 525 fDeviceIcon = NULL; 526 } 527 } 528