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
Category() const57 int Category() const
58 { return fCategory; }
AppRef() const59 const entry_ref* AppRef() const
60 { return &fAppRef; }
IsValid() const61 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
AppMenuItem(const char * appSig,int category)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
~AppMenuItem()94 AppMenuItem::~AppMenuItem()
95 {
96 delete fIcon;
97 }
98
99
100 void
GetContentSize(float * _width,float * _height)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
DrawContent()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
PieView(BVolume * volume)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
AttachedToWindow()163 PieView::AttachedToWindow()
164 {
165 fWindow = (MainWindow*)Window();
166 if (Parent()) {
167 SetViewColor(Parent()->ViewColor());
168 SetLowColor(Parent()->ViewColor());
169 }
170 }
171
172
~PieView()173 PieView::~PieView()
174 {
175 delete fMouseOverMenu;
176 delete fFileUnavailableMenu;
177 if (fScanner != NULL)
178 fScanner->RequestQuit();
179 }
180
181
182 void
MessageReceived(BMessage * message)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
MouseDown(BPoint where)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
MouseUp(BPoint where)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
MouseMoved(BPoint where,uint32 transit,const BMessage * dragMessage)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
Draw(BRect updateRect)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
SetPath(BPath path)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
_ShowVolume(BVolume * volume)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
_DrawProgressBar(BRect updateRect)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(ui_color(B_PANEL_TEXT_COLOR));
390 DrawString(task, BPoint(bx, by));
391 }
392
393
394 void
_DrawPieChart(BRect updateRect)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
_DrawDirectory(BRect b,FileInfo * info,float parentSpan,float beginAngle,int colorIdx,int level)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*
_FileAt(const BPoint & where)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
_AddAppToList(vector<AppMenuItem * > & list,const char * appSig,int category)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*
_BuildOpenWithMenu(FileInfo * info)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
_ShowContextMenu(FileInfo * info,BPoint p)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
_Launch(FileInfo * info,const entry_ref * appRef)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
_OpenInfo(FileInfo * info,BPoint p)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