1 /* 2 * Copyright (c) 2008 Stephan Aßmus <superstippi@gmx.de>. 3 * Copyright (c) 2009 Philippe Saint-Pierre, stpere@gmail.com 4 * All rights reserved. Distributed under the terms of the MIT license. 5 * 6 * Copyright (c) 1999 Mike Steed. You are free to use and distribute this software 7 * as long as it is accompanied by it's documentation and this copyright notice. 8 * The software comes with no warranty, etc. 9 */ 10 11 12 #include "PieView.h" 13 14 #include <fs_info.h> 15 #include <math.h> 16 17 #include <AppFileInfo.h> 18 #include <Bitmap.h> 19 #include <Catalog.h> 20 #include <ControlLook.h> 21 #include <Entry.h> 22 #include <File.h> 23 #include <MenuItem.h> 24 #include <Messenger.h> 25 #include <Path.h> 26 #include <PopUpMenu.h> 27 #include <Roster.h> 28 #include <String.h> 29 #include <Volume.h> 30 31 #include <tracker_private.h> 32 33 #include "Commands.h" 34 #include "DiskUsage.h" 35 #include "InfoWindow.h" 36 #include "MainWindow.h" 37 #include "Scanner.h" 38 39 #undef B_TRANSLATE_CONTEXT 40 #define B_TRANSLATE_CONTEXT "Pie View" 41 42 static const int32 kIdxGetInfo = 0; 43 static const int32 kIdxOpen = 1; 44 static const int32 kIdxOpenWith = 2; 45 static const int32 kIdxRescan = 3; 46 47 48 class AppMenuItem : public BMenuItem { 49 public: 50 AppMenuItem(const char* appSig, int category); 51 virtual ~AppMenuItem(); 52 53 virtual void GetContentSize(float* _width, float* _height); 54 virtual void DrawContent(); 55 56 int Category() const 57 { return fCategory; } 58 const entry_ref* AppRef() const 59 { return &fAppRef; } 60 bool IsValid() const 61 { return fIsValid; } 62 63 private: 64 int fCategory; 65 BBitmap* fIcon; 66 entry_ref fAppRef; 67 bool fIsValid; 68 }; 69 70 71 AppMenuItem::AppMenuItem(const char* appSig, int category) 72 : 73 BMenuItem(kEmptyStr, NULL), 74 fCategory(category), 75 fIcon(NULL), 76 fIsValid(false) 77 { 78 if (be_roster->FindApp(appSig, &fAppRef) == B_NO_ERROR) { 79 fIcon = new BBitmap(BRect(0.0, 0.0, 15.0, 15.0), B_RGBA32); 80 if (BNodeInfo::GetTrackerIcon(&fAppRef, fIcon, B_MINI_ICON) == B_OK) { 81 BEntry appEntry(&fAppRef); 82 if (appEntry.InitCheck() == B_OK) { 83 char name[B_FILE_NAME_LENGTH]; 84 appEntry.GetName(name); 85 SetLabel(name); 86 fIsValid = true; 87 } 88 } 89 } 90 } 91 92 93 AppMenuItem::~AppMenuItem() 94 { 95 delete fIcon; 96 } 97 98 99 void 100 AppMenuItem::GetContentSize(float* _width, float* _height) 101 { 102 if (_width) 103 *_width = fIcon->Bounds().Width() + 104 be_plain_font->StringWidth(Label()); 105 106 if (_height) { 107 struct font_height fh; 108 be_plain_font->GetHeight(&fh); 109 float fontHeight = ceilf(fh.ascent) + ceilf(fh.descent) 110 + ceilf(fh.leading); 111 *_height = max_c(fontHeight, fIcon->Bounds().Height()); 112 } 113 } 114 115 116 void 117 AppMenuItem::DrawContent() 118 { 119 Menu()->SetDrawingMode(B_OP_OVER); 120 Menu()->MovePenBy(0.0, -1.0); 121 Menu()->DrawBitmap(fIcon); 122 Menu()->MovePenBy(fIcon->Bounds().Width() + kSmallHMargin, 0.0); 123 BMenuItem::DrawContent(); 124 } 125 126 127 // #pragma mark - PieView 128 129 130 PieView::PieView(BVolume* volume) 131 : 132 BView(NULL, B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE | B_SUBPIXEL_PRECISE), 133 fWindow(NULL), 134 fScanner(NULL), 135 fVolume(volume), 136 fMouseOverInfo(), 137 fClicked(false), 138 fDragging(false), 139 fOutdated(false) 140 { 141 fMouseOverMenu = new BPopUpMenu(kEmptyStr, false, false); 142 fMouseOverMenu->AddItem(new BMenuItem(B_TRANSLATE("Get Info"), NULL), 143 kIdxGetInfo); 144 fMouseOverMenu->AddItem(new BMenuItem(B_TRANSLATE("Open"), NULL), 145 kIdxOpen); 146 147 fFileUnavailableMenu = new BPopUpMenu(kEmptyStr, false, false); 148 BMenuItem* item = new BMenuItem(B_TRANSLATE("file unavailable"), NULL); 149 item->SetEnabled(false); 150 fFileUnavailableMenu->AddItem(item); 151 152 BFont font; 153 GetFont(&font); 154 font.SetSize(ceilf(font.Size() * 1.33)); 155 font.SetFace(B_BOLD_FACE); 156 SetFont(&font); 157 158 struct font_height fh; 159 font.GetHeight(&fh); 160 fFontHeight = ceilf(fh.ascent) + ceilf(fh.descent) + ceilf(fh.leading); 161 } 162 163 164 void 165 PieView::AttachedToWindow() 166 { 167 fWindow = (MainWindow*)Window(); 168 } 169 170 171 PieView::~PieView() 172 { 173 delete fMouseOverMenu; 174 delete fFileUnavailableMenu; 175 if (fScanner != NULL) 176 fScanner->RequestQuit(); 177 } 178 179 180 void 181 PieView::MessageReceived(BMessage* message) 182 { 183 switch (message->what) { 184 case kBtnRescan: 185 if (fVolume != NULL) { 186 if (fScanner != NULL) 187 fScanner->Refresh(); 188 else 189 _ShowVolume(fVolume); 190 191 fOutdated = false; 192 Invalidate(); 193 } 194 break; 195 196 case kScanProgress: 197 case kScanDone: 198 { 199 Invalidate(); 200 break; 201 } 202 203 case kOutdatedMsg: 204 { 205 if (!fScanner->IsBusy()) { 206 fOutdated = true; 207 Invalidate(); 208 } 209 break; 210 } 211 212 default: 213 BView::MessageReceived(message); 214 break; 215 } 216 } 217 218 219 void 220 PieView::MouseDown(BPoint where) 221 { 222 uint32 buttons; 223 BMessage* current = Window()->CurrentMessage(); 224 if (current->FindInt32("buttons", (int32*)&buttons) != B_OK) 225 buttons = B_PRIMARY_MOUSE_BUTTON; 226 227 FileInfo* info = _FileAt(where); 228 if (info == NULL || info->pseudo) 229 return; 230 231 if (buttons & B_PRIMARY_MOUSE_BUTTON) { 232 fClicked = true; 233 fDragStart = where; 234 fClickedFile = info; 235 SetMouseEventMask(B_POINTER_EVENTS); 236 } else if (buttons & B_SECONDARY_MOUSE_BUTTON) { 237 where = ConvertToScreen(where); 238 _ShowContextMenu(info, where); 239 } 240 } 241 242 243 void 244 PieView::MouseUp(BPoint where) 245 { 246 // If the primary button was released and there was no dragging happening, 247 // just zoom in or out. 248 if (fClicked && !fDragging) { 249 FileInfo* info = _FileAt(where); 250 if (info != NULL) { 251 if (info == fScanner->CurrentDir()) { 252 fScanner->ChangeDir(info->parent); 253 fOutdated = fScanner->IsOutdated(); 254 Invalidate(); 255 } else if (info->children.size() > 0) { 256 fScanner->ChangeDir(info); 257 fOutdated = fScanner->IsOutdated(); 258 Invalidate(); 259 } 260 } 261 } 262 263 fClicked = false; 264 fDragging = false; 265 } 266 267 268 void 269 PieView::MouseMoved(BPoint where, uint32 transit, const BMessage* dragMessage) 270 { 271 if (fClicked) { 272 // Primary mouse button is down. 273 if (fDragging) 274 return; 275 // If the mouse has moved far enough, initiate dragging. 276 BPoint diff = where - fDragStart; 277 float distance = sqrtf(diff.x * diff.x + diff.y * diff.x); 278 if (distance > kDragThreshold) { 279 fDragging = true; 280 281 BBitmap* icon = new BBitmap(BRect(0.0, 0.0, 31.0, 31.0), B_RGBA32); 282 if (BNodeInfo::GetTrackerIcon(&fClickedFile->ref, icon, 283 B_LARGE_ICON) == B_OK) { 284 BMessage msg(B_SIMPLE_DATA); 285 msg.AddRef("refs", &fClickedFile->ref); 286 DragMessage(&msg, icon, B_OP_BLEND, BPoint(15.0, 15.0)); 287 } else 288 delete icon; 289 } 290 } else { 291 // Mouse button is not down, display file info. 292 if (transit == B_EXITED_VIEW) { 293 // Clear status view 294 fWindow->ShowInfo(NULL); 295 } else { 296 // Display file information. 297 fWindow->ShowInfo(_FileAt(where)); 298 } 299 } 300 } 301 302 303 void 304 PieView::Draw(BRect updateRect) 305 { 306 if (fScanner != NULL) { 307 // There is a current volume. 308 if (fScanner->IsBusy()) { 309 // Show progress of scanning. 310 _DrawProgressBar(updateRect); 311 if (fWindow != NULL) 312 fWindow->SetRescanEnabled(false); 313 } else { 314 _DrawPieChart(updateRect); 315 if (fWindow != NULL) 316 fWindow->SetRescanEnabled(true); 317 } 318 } 319 } 320 321 322 void 323 PieView::SetPath(BPath path) 324 { 325 if (fScanner == NULL) 326 _ShowVolume(fVolume); 327 328 if (fScanner != NULL) { 329 string desiredPath(path.Path()); 330 fScanner->SetDesiredPath(desiredPath); 331 Invalidate(); 332 } 333 } 334 335 336 // #pragma mark - private 337 338 339 void 340 PieView::_ShowVolume(BVolume* volume) 341 { 342 if (volume != NULL) { 343 if (fScanner == NULL) 344 fScanner = new Scanner(volume, this); 345 346 if (fScanner->Snapshot() == NULL) 347 fScanner->Refresh(); 348 } 349 350 Invalidate(); 351 } 352 353 354 void 355 PieView::_DrawProgressBar(BRect updateRect) 356 { 357 // Show the progress of the scanning operation. 358 359 fMouseOverInfo.clear(); 360 361 FillRect(updateRect, B_SOLID_LOW); 362 363 // Draw the progress bar. 364 BRect b = Bounds(); 365 float bx = floorf((b.left + b.Width() - kProgBarWidth) / 2.0); 366 float by = floorf((b.top + b.Height() - kProgBarHeight) / 2.0); 367 float ex = bx + kProgBarWidth; 368 float ey = by + kProgBarHeight; 369 float mx = bx + floorf((kProgBarWidth - 2.0) 370 * fScanner->Progress() / 100.0 + 0.5); 371 372 const rgb_color kBarColor = {50, 150, 255, 255}; 373 BRect barFrame(bx, by, ex, ey); 374 be_control_look->DrawStatusBar(this, barFrame, updateRect, 375 ui_color(B_PANEL_BACKGROUND_COLOR), kBarColor, mx); 376 377 // Tell what we are doing. 378 const char* task = fScanner->Task(); 379 float strWidth = StringWidth(task); 380 bx = (b.left + b.Width() - strWidth) / 2.0; 381 by -= fFontHeight + 2.0 * kSmallVMargin; 382 SetHighColor(0, 0, 0); 383 DrawString(task, BPoint(bx, by)); 384 } 385 386 387 void 388 PieView::_DrawPieChart(BRect updateRect) 389 { 390 BRect pieRect = Bounds(); 391 if (!updateRect.Intersects(pieRect)) 392 return; 393 394 pieRect.InsetBy(kPieOuterMargin, kPieOuterMargin); 395 396 SetHighColor(kPieBGColor); 397 FillRect(updateRect); 398 399 // constraint proportions 400 if (pieRect.Width() > pieRect.Height()) { 401 float moveBy = (pieRect.Width() - pieRect.Height()) / 2; 402 pieRect.left += moveBy; 403 pieRect.right -= moveBy; 404 } else { 405 float moveBy = (pieRect.Height() - pieRect.Width()) / 2; 406 pieRect.top -= moveBy; 407 pieRect.bottom += moveBy; 408 } 409 int colorIdx = 0; 410 FileInfo* currentDir = fScanner->CurrentDir(); 411 FileInfo* parent = currentDir; 412 while (parent != NULL) { 413 parent = parent->parent; 414 colorIdx++; 415 } 416 _DrawDirectory(pieRect, currentDir, 0.0, 0.0, 417 colorIdx % kBasePieColorCount, 0); 418 419 if (fOutdated) { 420 421 BRect b = Bounds(); 422 423 float strWidth = StringWidth(B_TRANSLATE("Outdated view")); 424 float bx = (b.Width() - strWidth - kSmallHMargin); 425 426 struct font_height fh; 427 be_plain_font->GetHeight(&fh); 428 429 float by = (b.Height() - ceil(fh.descent) - kSmallVMargin); 430 SetHighColor(0x00, 0x00, 0x00); 431 DrawString(B_TRANSLATE("Outdated view"), BPoint(bx, by)); 432 } 433 } 434 435 436 float 437 PieView::_DrawDirectory(BRect b, FileInfo* info, float parentSpan, 438 float beginAngle, int colorIdx, int level) 439 { 440 if (b.Width() < 2.0 * (kPieCenterSize + level * kPieRingSize 441 + kPieOuterMargin + kPieInnerMargin)) { 442 return 0.0; 443 } 444 445 if (info != NULL && info->color >= 0 && level == 0) 446 colorIdx = info->color; 447 else if (info != NULL) 448 info->color = colorIdx; 449 450 VolumeSnapshot* snapshot = fScanner->Snapshot(); 451 452 float cx = floorf(b.left + b.Width() / 2.0 + 0.5); 453 float cy = floorf(b.top + b.Height() / 2.0 + 0.5); 454 455 float mySpan; 456 457 if (level == 0) { 458 // Make room for mouse over info. 459 fMouseOverInfo.clear(); 460 fMouseOverInfo[0] = SegmentList(); 461 462 // Draw the center circle. 463 const char* displayName; 464 if (info == NULL) { 465 // NULL represents the entire volume. Show used and free space in 466 // the center circle, with the used segment representing the 467 // volume's root directory. 468 off_t volCapacity = snapshot->capacity; 469 mySpan = 360.0 * (volCapacity - snapshot->freeBytes) / volCapacity; 470 471 SetHighColor(kEmptySpcColor); 472 FillEllipse(BPoint(cx, cy), kPieCenterSize, kPieCenterSize); 473 474 SetHighColor(kBasePieColor[0]); 475 FillArc(BPoint(cx, cy), kPieCenterSize, kPieCenterSize, 0.0, 476 mySpan); 477 478 // Show total volume capacity. 479 char label[B_PATH_NAME_LENGTH]; 480 size_to_string(volCapacity, label, sizeof(label)); 481 SetHighColor(kPieBGColor); 482 SetDrawingMode(B_OP_OVER); 483 DrawString(label, BPoint(cx - StringWidth(label) / 2.0, 484 cy + fFontHeight + kSmallVMargin)); 485 SetDrawingMode(B_OP_COPY); 486 487 displayName = snapshot->name.c_str(); 488 489 // Record in-use space and free space for use during MouseMoved(). 490 info = snapshot->rootDir; 491 info->color = colorIdx; 492 fMouseOverInfo[0].push_back(Segment(0.0, mySpan, info)); 493 if (mySpan < 360.0 - kMinSegmentSpan) { 494 fMouseOverInfo[0].push_back(Segment(mySpan, 360.0, 495 snapshot->freeSpace)); 496 } 497 } else { 498 // Show a normal directory. 499 SetHighColor(kBasePieColor[colorIdx]); 500 FillEllipse(BRect(cx - kPieCenterSize, cy - kPieCenterSize, 501 cx + kPieCenterSize + 0.5, cy + kPieCenterSize + 0.5)); 502 displayName = info->ref.name; 503 mySpan = 360.0; 504 505 // Record the segment for use during MouseMoved(). 506 fMouseOverInfo[0].push_back(Segment(0.0, mySpan, info)); 507 } 508 509 SetPenSize(1.0); 510 SetHighColor(kOutlineColor); 511 StrokeEllipse(BPoint(cx, cy), kPieCenterSize + 0.5, 512 kPieCenterSize + 0.5); 513 514 // Show the name of the volume or directory. 515 BString label(displayName); 516 BFont font; 517 GetFont(&font); 518 font.TruncateString(&label, B_TRUNCATE_END, 519 2.0 * (kPieCenterSize - kSmallHMargin)); 520 float labelWidth = font.StringWidth(label.String()); 521 522 SetHighColor(kPieBGColor); 523 SetDrawingMode(B_OP_OVER); 524 DrawString(label.String(), BPoint(cx - labelWidth / 2.0, cy)); 525 SetDrawingMode(B_OP_COPY); 526 beginAngle = 0.0; 527 } else { 528 // Draw an exterior segment. 529 float parentSize; 530 if (info->parent == NULL) 531 parentSize = (float)snapshot->capacity; 532 else 533 parentSize = (float)info->parent->size; 534 535 mySpan = parentSpan * (float)info->size / parentSize; 536 if (mySpan >= kMinSegmentSpan) { 537 rgb_color color = kBasePieColor[colorIdx]; 538 color.red += kLightenFactor * level; 539 color.green += kLightenFactor * level; 540 color.blue += kLightenFactor * level; 541 SetHighColor(color); 542 SetPenSize(kPieRingSize); 543 float radius = kPieCenterSize + level * kPieRingSize 544 - kPieRingSize / 2.0; 545 StrokeArc(BPoint(cx, cy), radius, radius, beginAngle, mySpan); 546 547 SetHighColor(kOutlineColor); 548 SetPenSize(0.0); 549 float segBeginRadius = kPieCenterSize + (level - 1) 550 * kPieRingSize + 0.5; 551 float segLength = kPieRingSize - 0.5; 552 float rad = deg2rad(beginAngle); 553 float bx = cx + segBeginRadius * cos(rad); 554 float by = cy - segBeginRadius * sin(rad); 555 float ex = bx + segLength * cos(rad); 556 float ey = by - segLength * sin(rad); 557 StrokeLine(BPoint(bx, by), BPoint(ex, ey)); 558 559 rad = deg2rad(beginAngle + mySpan); 560 bx = cx + segBeginRadius * cos(rad); 561 by = cy - segBeginRadius * sin(rad); 562 ex = bx + segLength * cos(rad); 563 ey = by - segLength * sin(rad); 564 StrokeLine(BPoint(bx, by), BPoint(ex, ey)); 565 566 SetPenSize(0.0); 567 SetHighColor(kOutlineColor); 568 radius += kPieRingSize / 2.0; 569 StrokeArc(BPoint(cx, cy), radius, radius, beginAngle, mySpan); 570 571 // Record the segment for use during MouseMoved(). 572 if (fMouseOverInfo.find(level) == fMouseOverInfo.end()) 573 fMouseOverInfo[level] = SegmentList(); 574 575 fMouseOverInfo[level].push_back( 576 Segment(beginAngle, beginAngle + mySpan, info)); 577 } 578 } 579 580 // Draw children. 581 vector<FileInfo*>::iterator i = info->children.begin(); 582 while (i != info->children.end()) { 583 float childSpan 584 = _DrawDirectory(b, *i, mySpan, beginAngle, colorIdx, level + 1); 585 if (childSpan >= kMinSegmentSpan) { 586 beginAngle += childSpan; 587 colorIdx = (colorIdx + 1) % kBasePieColorCount; 588 } 589 i++; 590 } 591 592 return mySpan; 593 } 594 595 596 FileInfo* 597 PieView::_FileAt(const BPoint& where) 598 { 599 BRect b = Bounds(); 600 float cx = b.left + b.Width() / 2.0; 601 float cy = b.top + b.Height() / 2.0; 602 float dx = where.x - cx; 603 float dy = where.y - cy; 604 float dist = sqrt(dx*dx + dy*dy); 605 606 int level; 607 if (dist < kPieCenterSize) 608 level = 0; 609 else 610 level = 1 + (int)((dist - kPieCenterSize) / kPieRingSize); 611 612 float angle = rad2deg(atan(dy / dx)); 613 angle = ((dx < 0.0) ? 180.0 : (dy < 0.0) ? 0.0 : 360.0) - angle; 614 615 if (fMouseOverInfo.find(level) == fMouseOverInfo.end()) { 616 // No files in this level (ring) of the pie. 617 return NULL; 618 } 619 620 SegmentList s = fMouseOverInfo[level]; 621 SegmentList::iterator i = s.begin(); 622 while (i != s.end() && (angle < (*i).begin || (*i).end < angle)) 623 i++; 624 if (i == s.end()) { 625 // Nothing at this angle. 626 return NULL; 627 } 628 629 return (*i).info; 630 } 631 632 633 void 634 PieView::_AddAppToList(vector<AppMenuItem*>& list, const char* appSig, 635 int category) 636 { 637 // skip self. 638 if (strcmp(appSig, kAppSignature) == 0) 639 return; 640 641 AppMenuItem* item = new AppMenuItem(appSig, category); 642 if (item->IsValid()) { 643 vector<AppMenuItem*>::iterator i = list.begin(); 644 while (i != list.end()) { 645 if (*item->AppRef() == *(*i)->AppRef()) { 646 // Skip duplicates. 647 delete item; 648 return; 649 } 650 i++; 651 } 652 list.push_back(item); 653 } else { 654 // Skip items that weren't constructed successfully. 655 delete item; 656 } 657 } 658 659 660 BMenu* 661 PieView::_BuildOpenWithMenu(FileInfo* info) 662 { 663 vector<AppMenuItem*> appList; 664 665 // Get preferred app. 666 BMimeType* type = info->Type(); 667 char appSignature[B_MIME_TYPE_LENGTH]; 668 if (type->GetPreferredApp(appSignature) == B_OK) 669 _AddAppToList(appList, appSignature, 1); 670 671 // Get apps that handle this subtype and supertype. 672 BMessage msg; 673 if (type->GetSupportingApps(&msg) == B_OK) { 674 int32 subs, supers, i; 675 msg.FindInt32("be:sub", &subs); 676 msg.FindInt32("be:super", &supers); 677 678 const char* appSig; 679 for (i = 0; i < subs; i++) { 680 msg.FindString("applications", i, &appSig); 681 _AddAppToList(appList, appSig, 2); 682 } 683 int hold = i; 684 for (i = 0; i < supers; i++) { 685 msg.FindString("applications", i + hold, &appSig); 686 _AddAppToList(appList, appSig, 3); 687 } 688 } 689 690 // Get apps that handle any type. 691 if (BMimeType::GetWildcardApps(&msg) == B_OK) { 692 const char* appSig; 693 for (int32 i = 0; true; i++) { 694 if (msg.FindString("applications", i, &appSig) == B_OK) 695 _AddAppToList(appList, appSig, 4); 696 else 697 break; 698 } 699 } 700 701 delete type; 702 703 BMenu* openWith = new BMenu(B_TRANSLATE("Open With")); 704 705 if (appList.size() == 0) { 706 BMenuItem* item = new BMenuItem(B_TRANSLATE("no supporting apps"), 707 NULL); 708 item->SetEnabled(false); 709 openWith->AddItem(item); 710 } else { 711 vector<AppMenuItem*>::iterator i = appList.begin(); 712 int category = (*i)->Category(); 713 while (i != appList.end()) { 714 if (category != (*i)->Category()) { 715 openWith->AddSeparatorItem(); 716 category = (*i)->Category(); 717 } 718 openWith->AddItem(*i); 719 i++; 720 } 721 } 722 723 return openWith; 724 } 725 726 727 void 728 PieView::_ShowContextMenu(FileInfo* info, BPoint p) 729 { 730 BRect openRect(p.x - 5.0, p.y - 5.0, p.x + 5.0, p.y + 5.0); 731 732 // Display the open-with menu only if the file is still available. 733 BNode node(&info->ref); 734 if (node.InitCheck() == B_OK) { 735 // Add "Open With" submenu. 736 BMenu* openWith = _BuildOpenWithMenu(info); 737 fMouseOverMenu->AddItem(openWith, kIdxOpenWith); 738 739 // Add a "Rescan" option for folders. 740 BMenuItem* rescan = NULL; 741 if (info->children.size() > 0) { 742 rescan = new BMenuItem(B_TRANSLATE("Rescan"), NULL); 743 fMouseOverMenu->AddItem(rescan, kIdxRescan); 744 } 745 746 BMenuItem* item = fMouseOverMenu->Go(p, false, true, openRect); 747 if (item != NULL) { 748 switch (fMouseOverMenu->IndexOf(item)) { 749 case kIdxGetInfo: 750 _OpenInfo(info, p); 751 break; 752 case kIdxOpen: 753 _Launch(info); 754 break; 755 case kIdxRescan: 756 fScanner->Refresh(info); 757 Invalidate(); 758 break; 759 default: // must be "Open With" submenu 760 _Launch(info, ((AppMenuItem*)item)->AppRef()); 761 break; 762 } 763 } 764 765 if (rescan != NULL) { 766 fMouseOverMenu->RemoveItem(rescan); 767 delete rescan; 768 } 769 770 fMouseOverMenu->RemoveItem(openWith); 771 delete openWith; 772 } 773 else { 774 // The file is no longer available. 775 fFileUnavailableMenu->Go(p, false, true, openRect); 776 } 777 } 778 779 780 void 781 PieView::_Launch(FileInfo* info, const entry_ref* appRef) 782 { 783 BMessage msg(B_REFS_RECEIVED); 784 msg.AddRef("refs", &info->ref); 785 786 if (appRef == NULL) { 787 // Let the registrar pick an app based on the file's MIME type. 788 BMimeType* type = info->Type(); 789 be_roster->Launch(type->Type(), &msg); 790 delete type; 791 } else { 792 // Launch a designated app to handle this file. 793 be_roster->Launch(appRef, &msg); 794 } 795 } 796 797 798 void 799 PieView::_OpenInfo(FileInfo* info, BPoint p) 800 { 801 BMessenger tracker(kTrackerSignature); 802 if (!tracker.IsValid()) { 803 new InfoWin(p, info, Window()); 804 } else { 805 BMessage message(kGetInfo); 806 message.AddRef("refs", &info->ref); 807 tracker.SendMessage(&message); 808 } 809 } 810