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