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