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