1 /* 2 Open Tracker License 3 4 Terms and Conditions 5 6 Copyright (c) 1991-2000, Be Incorporated. All rights reserved. 7 8 Permission is hereby granted, free of charge, to any person obtaining a copy of 9 this software and associated documentation files (the "Software"), to deal in 10 the Software without restriction, including without limitation the rights to 11 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 12 of the Software, and to permit persons to whom the Software is furnished to do 13 so, subject to the following conditions: 14 15 The above copyright notice and this permission notice applies to all licensees 16 and shall be included in all copies or substantial portions of the Software. 17 18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY, 20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 21 BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 22 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION 23 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 25 Except as contained in this notice, the name of Be Incorporated shall not be 26 used in advertising or otherwise to promote the sale, use or other dealings in 27 this Software without prior written authorization from Be Incorporated. 28 29 Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks 30 of Be Incorporated in the United States and other countries. Other brand product 31 names are registered trademarks or trademarks of their respective holders. 32 All rights reserved. 33 */ 34 35 #include "Attributes.h" 36 #include "AutoLock.h" 37 #include "Commands.h" 38 #include "FSUtils.h" 39 #include "IconMenuItem.h" 40 #include "OpenWithWindow.h" 41 #include "MimeTypes.h" 42 #include "StopWatch.h" 43 #include "Tracker.h" 44 45 #include <Alert.h> 46 #include <Button.h> 47 #include <Catalog.h> 48 #include <Locale.h> 49 #include <Mime.h> 50 #include <NodeInfo.h> 51 #include <Path.h> 52 #include <Roster.h> 53 #include <Volume.h> 54 #include <VolumeRoster.h> 55 56 #include <stdlib.h> 57 #include <stdio.h> 58 #include <strings.h> 59 60 61 const char* kDefaultOpenWithTemplate = "OpenWithSettings"; 62 63 // ToDo: 64 // filter out trash 65 // allow column configuring 66 // make SaveState/RestoreState save the current window setting for 67 // other windows 68 69 const float kMaxMenuWidth = 150; 70 71 const int32 kDocumentKnobWidth = 16; 72 const int32 kOpenAndMakeDefault = 'OpDf'; 73 const rgb_color kOpenWithDefaultColor = { 0xFF, 0xFF, 0xCC, 255}; 74 75 76 // #pragma mark - OpenWithContainerWindow 77 78 79 #undef B_TRANSLATION_CONTEXT 80 #define B_TRANSLATION_CONTEXT "OpenWithWindow" 81 82 83 OpenWithContainerWindow::OpenWithContainerWindow(BMessage* entriesToOpen, 84 LockingList<BWindow>* windowList, window_look look, window_feel feel, 85 uint32 flags, uint32 workspace) 86 : 87 BContainerWindow(windowList, 0, look, feel, flags, workspace), 88 fEntriesToOpen(entriesToOpen) 89 { 90 AutoLock<BWindow> lock(this); 91 92 BRect windowRect(85, 50, 718, 296); 93 MoveTo(windowRect.LeftTop()); 94 ResizeTo(windowRect.Width(), windowRect.Height()); 95 96 // add a background view; use the standard BackgroundView here, the same 97 // as the file panel is using 98 BRect rect(Bounds()); 99 BackgroundView* backgroundView = new BackgroundView(rect); 100 AddChild(backgroundView); 101 102 rect = Bounds(); 103 104 // add buttons 105 106 fLaunchButton = new BButton(rect, "ok", B_TRANSLATE("Open"), 107 new BMessage(kDefaultButton), B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM); 108 fLaunchButton->ResizeToPreferred(); 109 fLaunchButton->MoveTo(rect.right - 10 - kDocumentKnobWidth 110 - fLaunchButton->Bounds().Width(), 111 rect.bottom - 10.0f - fLaunchButton->Bounds().Height()); 112 backgroundView->AddChild(fLaunchButton); 113 114 BRect buttonRect = fLaunchButton->Frame(); 115 fLaunchAndMakeDefaultButton = new BButton(buttonRect, "make default", 116 B_TRANSLATE("Open and make preferred"), 117 new BMessage(kOpenAndMakeDefault), B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM); 118 // wide button, have to resize to fit text 119 fLaunchAndMakeDefaultButton->ResizeToPreferred(); 120 fLaunchAndMakeDefaultButton->MoveBy(-10.0f 121 - fLaunchAndMakeDefaultButton->Bounds().Width(), 0.0f); 122 backgroundView->AddChild(fLaunchAndMakeDefaultButton); 123 fLaunchAndMakeDefaultButton->SetEnabled(false); 124 125 buttonRect = fLaunchAndMakeDefaultButton->Frame(); 126 BButton* button = new BButton(buttonRect, "cancel", B_TRANSLATE("Cancel"), 127 new BMessage(kCancelButton), B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM); 128 button->ResizeToPreferred(); 129 button->MoveBy(-10.0f - button->Bounds().Width(), 0.0f); 130 backgroundView->AddChild(button); 131 132 fMinimalWidth = button->Bounds().Width() + fLaunchButton->Bounds().Width() 133 + fLaunchAndMakeDefaultButton->Bounds().Width() + kDocumentKnobWidth 134 + 40.0f; 135 136 fLaunchButton->MakeDefault(true); 137 138 // add pose view 139 140 rect.OffsetTo(10.0f, 10.0f); 141 rect.bottom = buttonRect.top - 15.0f; 142 143 rect.right -= B_V_SCROLL_BAR_WIDTH + 20.0f; 144 rect.bottom -= B_H_SCROLL_BAR_HEIGHT; 145 // make room for scrollbars and a margin 146 fPoseView = NewPoseView(0, rect, kListMode); 147 backgroundView->AddChild(fPoseView); 148 149 fPoseView->SetFlags(fPoseView->Flags() | B_NAVIGABLE); 150 fPoseView->SetPoseEditing(false); 151 152 // set the window title 153 if (CountRefs(fEntriesToOpen) == 1) { 154 // if opening just one file, use it in the title 155 entry_ref ref; 156 fEntriesToOpen->FindRef("refs", &ref); 157 BString buffer(B_TRANSLATE("Open %name with:")); 158 buffer.ReplaceFirst("%name", ref.name); 159 160 SetTitle(buffer.String()); 161 } else { 162 // use generic title 163 SetTitle(B_TRANSLATE("Open selection with:")); 164 } 165 166 AddCommonFilter(new BMessageFilter(B_KEY_DOWN, 167 &OpenWithContainerWindow::KeyDownFilter)); 168 } 169 170 171 OpenWithContainerWindow::~OpenWithContainerWindow() 172 { 173 delete fEntriesToOpen; 174 } 175 176 177 BPoseView* 178 OpenWithContainerWindow::NewPoseView(Model*, BRect rect, uint32) 179 { 180 return new OpenWithPoseView(rect); 181 } 182 183 184 OpenWithPoseView* 185 OpenWithContainerWindow::PoseView() const 186 { 187 ASSERT(dynamic_cast<OpenWithPoseView*>(fPoseView) != NULL); 188 189 return static_cast<OpenWithPoseView*>(fPoseView); 190 } 191 192 193 const BMessage* 194 OpenWithContainerWindow::EntryList() const 195 { 196 return fEntriesToOpen; 197 } 198 199 200 void 201 OpenWithContainerWindow::OpenWithSelection() 202 { 203 int32 count = PoseView()->SelectionList()->CountItems(); 204 ASSERT(count == 1); 205 if (count == 0) 206 return; 207 208 PoseView()->OpenSelection(PoseView()->SelectionList()->FirstItem(), 0); 209 } 210 211 212 static const BString* 213 FindOne(const BString* element, void* castToString) 214 { 215 if (strcasecmp(element->String(), (const char*)castToString) == 0) 216 return element; 217 218 return 0; 219 } 220 221 222 static const entry_ref* 223 AddOneUniqueDocumentType(const entry_ref* ref, void* castToList) 224 { 225 BObjectList<BString>* list = (BObjectList<BString>*)castToList; 226 227 BEntry entry(ref, true); 228 // traverse symlinks 229 230 // get this documents type 231 char type[B_MIME_TYPE_LENGTH]; 232 BFile file(&entry, O_RDONLY); 233 if (file.InitCheck() != B_OK) 234 return 0; 235 236 BNodeInfo info(&file); 237 if (info.GetType(type) != B_OK) 238 return 0; 239 240 if (list->EachElement(FindOne, &type)) 241 // type already in list, bail 242 return 0; 243 244 // add type to list 245 list->AddItem(new BString(type)); 246 247 return 0; 248 } 249 250 251 static const BString* 252 SetDefaultAppForOneType(const BString* element, void* castToEntryRef) 253 { 254 const entry_ref* appRef = (const entry_ref*)castToEntryRef; 255 256 // set entry as default handler for one mime string 257 BMimeType mime(element->String()); 258 if (!mime.IsInstalled()) 259 return 0; 260 261 // first set it's app signature as the preferred type 262 BFile appFile(appRef, O_RDONLY); 263 if (appFile.InitCheck() != B_OK) 264 return 0; 265 266 char appSignature[B_MIME_TYPE_LENGTH]; 267 if (GetAppSignatureFromAttr(&appFile, appSignature) != B_OK) 268 return 0; 269 270 if (mime.SetPreferredApp(appSignature) != B_OK) 271 return 0; 272 273 // set the app hint on the metamime for this signature 274 mime.SetTo(appSignature); 275 #if xDEBUG 276 status_t result = 277 #endif 278 mime.SetAppHint(appRef); 279 280 #if xDEBUG 281 BEntry debugEntry(appRef); 282 BPath debugPath; 283 debugEntry.GetPath(&debugPath); 284 285 PRINT(("setting %s, sig %s as default app for %s, result %s\n", 286 debugPath.Path(), appSignature, element->String(), strerror(result))); 287 #endif 288 289 return 0; 290 } 291 292 293 void 294 OpenWithContainerWindow::MakeDefaultAndOpen() 295 { 296 int32 count = PoseView()->SelectionList()->CountItems(); 297 ASSERT(count == 1); 298 if (count == 0) 299 return; 300 301 BPose* selectedAppPose = PoseView()->SelectionList()->FirstItem(); 302 ASSERT(selectedAppPose != NULL); 303 if (selectedAppPose == NULL) 304 return; 305 306 // collect all the types of all the opened documents into a list 307 BObjectList<BString> openedFileTypes(10, true); 308 EachEntryRef(EntryList(), AddOneUniqueDocumentType, &openedFileTypes, 100); 309 310 // set the default application to be the selected pose for all the 311 // mime types in the list 312 openedFileTypes.EachElement(SetDefaultAppForOneType, 313 (void*)selectedAppPose->TargetModel()->EntryRef()); 314 315 // done setting the default application, now launch the app with the 316 // documents 317 OpenWithSelection(); 318 } 319 320 321 void 322 OpenWithContainerWindow::MessageReceived(BMessage* message) 323 { 324 switch (message->what) { 325 case kDefaultButton: 326 OpenWithSelection(); 327 PostMessage(B_QUIT_REQUESTED); 328 return; 329 330 case kOpenAndMakeDefault: 331 MakeDefaultAndOpen(); 332 PostMessage(B_QUIT_REQUESTED); 333 return; 334 335 case kCancelButton: 336 PostMessage(B_QUIT_REQUESTED); 337 return; 338 339 case B_OBSERVER_NOTICE_CHANGE: 340 return; 341 342 case kResizeToFit: 343 ResizeToFit(); 344 break; 345 } 346 347 _inherited::MessageReceived(message); 348 } 349 350 351 filter_result 352 OpenWithContainerWindow::KeyDownFilter(BMessage* message, BHandler**, 353 BMessageFilter* filter) 354 { 355 uchar key; 356 if (message->FindInt8("byte", (int8*)&key) != B_OK) 357 return B_DISPATCH_MESSAGE; 358 359 int32 modifiers = 0; 360 message->FindInt32("modifiers", &modifiers); 361 if (modifiers == 0 && key == B_ESCAPE) { 362 filter->Looper()->PostMessage(kCancelButton); 363 return B_SKIP_MESSAGE; 364 } 365 366 return B_DISPATCH_MESSAGE; 367 } 368 369 370 bool 371 OpenWithContainerWindow::ShouldAddMenus() const 372 { 373 return false; 374 } 375 376 377 void 378 OpenWithContainerWindow::ShowContextMenu(BPoint, const entry_ref*, BView*) 379 { 380 } 381 382 383 void 384 OpenWithContainerWindow::AddShortcuts() 385 { 386 AddShortcut('I', B_COMMAND_KEY, new BMessage(kGetInfo), PoseView()); 387 AddShortcut('Y', B_COMMAND_KEY, new BMessage(kResizeToFit), PoseView()); 388 } 389 390 391 void 392 OpenWithContainerWindow::NewAttributeMenu(BMenu* menu) 393 { 394 _inherited::NewAttributeMenu(menu); 395 396 BMessage* message = new BMessage(kAttributeItem); 397 message->AddString("attr_name", kAttrOpenWithRelation); 398 message->AddInt32("attr_type", B_STRING_TYPE); 399 message->AddInt32("attr_hash", 400 (int32)AttrHashString(kAttrOpenWithRelation, B_STRING_TYPE)); 401 message->AddFloat("attr_width", 180); 402 message->AddInt32("attr_align", B_ALIGN_LEFT); 403 message->AddBool("attr_editable", false); 404 message->AddBool("attr_statfield", false); 405 406 BMenuItem* item = new BMenuItem(B_TRANSLATE("Relation"), message); 407 menu->AddItem(item); 408 message = new BMessage(kAttributeItem); 409 message->AddString("attr_name", kAttrAppVersion); 410 message->AddInt32("attr_type", B_STRING_TYPE); 411 message->AddInt32("attr_hash", 412 (int32)AttrHashString(kAttrAppVersion, B_STRING_TYPE)); 413 message->AddFloat("attr_width", 70); 414 message->AddInt32("attr_align", B_ALIGN_LEFT); 415 message->AddBool("attr_editable", false); 416 message->AddBool("attr_statfield", false); 417 418 item = new BMenuItem(B_TRANSLATE("Version"), message); 419 menu->AddItem(item); 420 } 421 422 423 void 424 OpenWithContainerWindow::SaveState(bool) 425 { 426 BNode defaultingNode; 427 if (DefaultStateSourceNode(kDefaultOpenWithTemplate, &defaultingNode, 428 true, false)) { 429 AttributeStreamFileNode streamNodeDestination(&defaultingNode); 430 SaveWindowState(&streamNodeDestination); 431 fPoseView->SaveState(&streamNodeDestination); 432 } 433 } 434 435 436 void 437 OpenWithContainerWindow::SaveState(BMessage &message) const 438 { 439 _inherited::SaveState(message); 440 } 441 442 443 void 444 OpenWithContainerWindow::Init(const BMessage* message) 445 { 446 _inherited::Init(message); 447 } 448 449 450 void 451 OpenWithContainerWindow::RestoreState() 452 { 453 BNode defaultingNode; 454 if (DefaultStateSourceNode(kDefaultOpenWithTemplate, &defaultingNode, 455 false)) { 456 AttributeStreamFileNode streamNodeSource(&defaultingNode); 457 RestoreWindowState(&streamNodeSource); 458 fPoseView->Init(&streamNodeSource); 459 } else { 460 RestoreWindowState(NULL); 461 fPoseView->Init(NULL); 462 } 463 } 464 465 466 void 467 OpenWithContainerWindow::RestoreState(const BMessage &message) 468 { 469 _inherited::RestoreState(message); 470 } 471 472 473 void 474 OpenWithContainerWindow::RestoreWindowState(AttributeStreamNode* node) 475 { 476 SetSizeLimits(fMinimalWidth, 10000, 160, 10000); 477 if (node == NULL) 478 return; 479 480 const char* rectAttributeName = kAttrWindowFrame; 481 BRect frame(Frame()); 482 if (node->Read(rectAttributeName, 0, B_RECT_TYPE, sizeof(BRect), &frame) 483 == sizeof(BRect)) { 484 MoveTo(frame.LeftTop()); 485 ResizeTo(frame.Width(), frame.Height()); 486 } 487 } 488 489 490 void 491 OpenWithContainerWindow::RestoreWindowState(const BMessage &message) 492 { 493 _inherited::RestoreWindowState(message); 494 } 495 496 497 bool 498 OpenWithContainerWindow::NeedsDefaultStateSetup() 499 { 500 return true; 501 } 502 503 504 void 505 OpenWithContainerWindow::SetUpDefaultState() 506 { 507 } 508 509 510 bool 511 OpenWithContainerWindow::IsShowing(const node_ref*) const 512 { 513 return false; 514 } 515 516 517 bool 518 OpenWithContainerWindow::IsShowing(const entry_ref*) const 519 { 520 return false; 521 } 522 523 524 void 525 OpenWithContainerWindow::SetCanSetAppAsDefault(bool on) 526 { 527 fLaunchAndMakeDefaultButton->SetEnabled(on); 528 } 529 530 531 void 532 OpenWithContainerWindow::SetCanOpen(bool on) 533 { 534 fLaunchButton->SetEnabled(on); 535 } 536 537 538 // #pragma mark - OpenWithPoseView 539 540 541 OpenWithPoseView::OpenWithPoseView(BRect frame, uint32 resizeMask) 542 : 543 BPoseView(new Model(), frame, kListMode, resizeMask), 544 fHaveCommonPreferredApp(false), 545 fIterator(NULL) 546 { 547 fSavePoseLocations = false; 548 fMultipleSelection = false; 549 fDragEnabled = false; 550 } 551 552 553 OpenWithContainerWindow* 554 OpenWithPoseView::ContainerWindow() const 555 { 556 OpenWithContainerWindow* window 557 = dynamic_cast<OpenWithContainerWindow*>(Window()); 558 ASSERT(window != NULL); 559 560 return window; 561 } 562 563 564 void 565 OpenWithPoseView::AttachedToWindow() 566 { 567 _inherited::AttachedToWindow(); 568 569 SetViewColor(kOpenWithDefaultColor); 570 SetLowColor(kOpenWithDefaultColor); 571 } 572 573 574 bool 575 OpenWithPoseView::CanHandleDragSelection(const Model*, const BMessage*, bool) 576 { 577 return false; 578 } 579 580 581 static void 582 AddSupportingAppForTypeToQuery(SearchForSignatureEntryList* queryIterator, 583 const char* type) 584 { 585 // get supporting apps for type 586 BMimeType mime(type); 587 if (!mime.IsInstalled()) 588 return; 589 590 BMessage message; 591 mime.GetSupportingApps(&message); 592 593 // push each of the supporting apps signature uniquely 594 595 const char* signature; 596 for (int32 index = 0; message.FindString("applications", index, 597 &signature) == B_OK; index++) { 598 queryIterator->PushUniqueSignature(signature); 599 } 600 } 601 602 603 static const entry_ref* 604 AddOneRefSignatures(const entry_ref* ref, void* castToIterator) 605 { 606 // TODO: resolve cases where each entry has a different type and 607 // their supporting apps are disjoint sets 608 609 SearchForSignatureEntryList* queryIterator = 610 (SearchForSignatureEntryList*)castToIterator; 611 612 Model model(ref, true, true); 613 if (model.InitCheck() != B_OK) 614 return NULL; 615 616 BString mimeType(model.MimeType()); 617 618 if (!mimeType.Length() || mimeType.ICompare(B_FILE_MIMETYPE) == 0) 619 // if model is of unknown type, try mimeseting it first 620 model.Mimeset(true); 621 622 entry_ref preferredRef; 623 624 // add preferred app for file, if any 625 if (model.PreferredAppSignature()[0]) { 626 // got one, mark it as preferred for this node 627 if (be_roster->FindApp(model.PreferredAppSignature(), &preferredRef) 628 == B_OK) { 629 queryIterator->PushUniqueSignature(model.PreferredAppSignature()); 630 queryIterator->TrySettingPreferredAppForFile(&preferredRef); 631 } 632 } 633 634 mimeType = model.MimeType(); 635 mimeType.ToLower(); 636 637 if (mimeType.Length() && !mimeType.ICompare(B_FILE_MIMETYPE) == 0) 638 queryIterator->NonGenericFileFound(); 639 640 // get supporting apps for type 641 AddSupportingAppForTypeToQuery(queryIterator, mimeType.String()); 642 643 // find the preferred app for this type 644 if (be_roster->FindApp(mimeType.String(), &preferredRef) == B_OK) 645 queryIterator->TrySettingPreferredApp(&preferredRef); 646 647 return NULL; 648 } 649 650 651 EntryListBase* 652 OpenWithPoseView::InitDirentIterator(const entry_ref*) 653 { 654 OpenWithContainerWindow* window = ContainerWindow(); 655 656 const BMessage* entryList = window->EntryList(); 657 658 fIterator = new SearchForSignatureEntryList(true); 659 660 // push all the supporting apps from all the entries into the 661 // search for signature iterator 662 EachEntryRef(entryList, AddOneRefSignatures, fIterator, 100); 663 664 // push superhandlers 665 AddSupportingAppForTypeToQuery(fIterator, B_FILE_MIMETYPE); 666 fHaveCommonPreferredApp = fIterator->GetPreferredApp(&fPreferredRef); 667 668 if (fIterator->Rewind() != B_OK) { 669 delete fIterator; 670 fIterator = NULL; 671 HideBarberPole(); 672 return NULL; 673 } 674 SetRefFilter(new OpenWithRefFilter(fIterator, entryList, 675 (fHaveCommonPreferredApp ? &fPreferredRef : 0))); 676 677 return fIterator; 678 } 679 680 681 void 682 OpenWithPoseView::OpenSelection(BPose* pose, int32*) 683 { 684 OpenWithContainerWindow* window = ContainerWindow(); 685 686 int32 count = fSelectionList->CountItems(); 687 if (count == 0) 688 return; 689 690 if (pose == NULL) 691 pose = fSelectionList->FirstItem(); 692 693 ASSERT(pose != NULL); 694 695 BEntry entry(pose->TargetModel()->EntryRef()); 696 if (entry.InitCheck() != B_OK) { 697 BString errorString( 698 B_TRANSLATE("Could not find application \"%appname\"")); 699 errorString.ReplaceFirst("%appname", pose->TargetModel()->Name()); 700 701 BAlert* alert = new BAlert("", errorString.String(), B_TRANSLATE("OK"), 702 0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT); 703 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 704 alert->Go(); 705 return; 706 } 707 708 if (OpenWithRelation(pose->TargetModel()) == kNoRelation) { 709 if (!fIterator->GenericFilesOnly()) { 710 BString warning(B_TRANSLATE( 711 "The application \"%appname\" does not support the type of " 712 "document you are about to open.\nAre you sure you want to " 713 "proceed?\n\nIf you know that the application supports the " 714 "document type, you should contact the publisher of the " 715 "application and ask them to update their application to list " 716 "the type of your document as supported.")); 717 warning.ReplaceFirst("%appname", pose->TargetModel()->Name()); 718 719 BAlert* alert = new BAlert("", warning.String(), 720 B_TRANSLATE("Cancel"), B_TRANSLATE("Open"), 0, B_WIDTH_AS_USUAL, 721 B_WARNING_ALERT); 722 alert->SetShortcut(0, B_ESCAPE); 723 if (alert->Go() == 0) 724 return; 725 } 726 // else - once we have an extensible sniffer, tell users to ask 727 // publishers to fix up sniffers 728 } 729 730 BMessage message(*window->EntryList()); 731 // make a clone to send 732 message.RemoveName("launchUsingSelector"); 733 // make sure the old selector is not in the message 734 message.AddRef("handler", pose->TargetModel()->EntryRef()); 735 // add ref of the selected handler 736 737 ASSERT(fSelectionHandler != NULL); 738 if (fSelectionHandler != NULL) 739 fSelectionHandler->PostMessage(&message); 740 741 window->PostMessage(B_QUIT_REQUESTED); 742 } 743 744 745 void 746 OpenWithPoseView::Pulse() 747 { 748 // disable the Open and make default button if the default 749 // app matches the selected app 750 // 751 // disable the Open button if no apps selected 752 753 OpenWithContainerWindow* window = ContainerWindow(); 754 755 if (!fSelectionList->CountItems()) { 756 window->SetCanSetAppAsDefault(false); 757 window->SetCanOpen(false); 758 _inherited::Pulse(); 759 return; 760 } 761 762 // if we selected a non-handling application, don't allow setting 763 // it as preferred 764 Model* firstSelected = fSelectionList->FirstItem()->TargetModel(); 765 if (OpenWithRelation(firstSelected) == kNoRelation) { 766 window->SetCanSetAppAsDefault(false); 767 window->SetCanOpen(true); 768 _inherited::Pulse(); 769 return; 770 } 771 772 // make the open button enabled, because we have na app selected 773 window->SetCanOpen(true); 774 if (!fHaveCommonPreferredApp) { 775 window->SetCanSetAppAsDefault(true); 776 _inherited::Pulse(); 777 return; 778 } 779 780 ASSERT(fSelectionList->CountItems() == 1); 781 782 // enable the Open and make default if selected application different 783 // from preferred app ref 784 window->SetCanSetAppAsDefault((*fSelectionList->FirstItem()-> 785 TargetModel()->EntryRef()) != fPreferredRef); 786 787 _inherited::Pulse(); 788 } 789 790 791 void 792 OpenWithPoseView::SetUpDefaultColumnsIfNeeded() 793 { 794 // in case there were errors getting some columns 795 if (fColumnList->CountItems() != 0) 796 return; 797 798 BColumn* nameColumn = new BColumn(B_TRANSLATE("Name"), kColumnStart, 125, 799 B_ALIGN_LEFT, kAttrStatName, B_STRING_TYPE, true, true); 800 fColumnList->AddItem(nameColumn); 801 BColumn* relationColumn = new BColumn(B_TRANSLATE("Relation"), 180, 100, 802 B_ALIGN_LEFT, kAttrOpenWithRelation, B_STRING_TYPE, false, false); 803 fColumnList->AddItem(relationColumn); 804 fColumnList->AddItem(new BColumn(B_TRANSLATE("Location"), 290, 225, 805 B_ALIGN_LEFT, kAttrPath, B_STRING_TYPE, true, false)); 806 fColumnList->AddItem(new BColumn(B_TRANSLATE("Version"), 525, 70, 807 B_ALIGN_LEFT, kAttrAppVersion, B_STRING_TYPE, false, false)); 808 809 // sort by relation and by name 810 SetPrimarySort(relationColumn->AttrHash()); 811 SetSecondarySort(nameColumn->AttrHash()); 812 } 813 814 815 bool 816 OpenWithPoseView::AddPosesThreadValid(const entry_ref*) const 817 { 818 return true; 819 } 820 821 822 void 823 OpenWithPoseView::CreatePoses(Model** models, PoseInfo* poseInfoArray, 824 int32 count, BPose** resultingPoses, bool insertionSort, 825 int32* lastPoseIndexPtr, BRect* boundsPtr, bool forceDraw) 826 { 827 // overridden to try to select the preferred handling app 828 _inherited::CreatePoses(models, poseInfoArray, count, resultingPoses, 829 insertionSort, lastPoseIndexPtr, boundsPtr, forceDraw); 830 831 if (resultingPoses != NULL) { 832 for (int32 index = 0; index < count; index++) { 833 if (resultingPoses[index] && fHaveCommonPreferredApp 834 && *(models[index]->EntryRef()) == fPreferredRef) { 835 // this is our preferred app, select it's pose 836 SelectPose(resultingPoses[index], 837 IndexOfPose(resultingPoses[index])); 838 } 839 } 840 } 841 } 842 843 844 void 845 OpenWithPoseView::KeyDown(const char* bytes, int32 count) 846 { 847 if (bytes[0] == B_TAB) { 848 // just shift the focus, don't tab to the next pose 849 BView::KeyDown(bytes, count); 850 } else 851 _inherited::KeyDown(bytes, count); 852 } 853 854 855 void 856 OpenWithPoseView::SaveState(AttributeStreamNode* node) 857 { 858 _inherited::SaveState(node); 859 } 860 861 862 void 863 OpenWithPoseView::RestoreState(AttributeStreamNode* node) 864 { 865 _inherited::RestoreState(node); 866 fViewState->SetViewMode(kListMode); 867 } 868 869 870 void 871 OpenWithPoseView::SaveState(BMessage &message) const 872 { 873 _inherited::SaveState(message); 874 } 875 876 877 void 878 OpenWithPoseView::RestoreState(const BMessage &message) 879 { 880 _inherited::RestoreState(message); 881 fViewState->SetViewMode(kListMode); 882 } 883 884 885 void 886 OpenWithPoseView::SavePoseLocations(BRect*) 887 { 888 } 889 890 891 void 892 OpenWithPoseView::MoveSelectionToTrash(bool) 893 { 894 } 895 896 897 void 898 OpenWithPoseView::MoveSelectionTo(BPoint, BPoint, BContainerWindow*) 899 { 900 } 901 902 903 void 904 OpenWithPoseView::MoveSelectionInto(Model*, BContainerWindow*, bool, bool) 905 { 906 } 907 908 909 bool 910 OpenWithPoseView::Represents(const node_ref*) const 911 { 912 return false; 913 } 914 915 916 bool 917 OpenWithPoseView::Represents(const entry_ref*) const 918 { 919 return false; 920 } 921 922 923 bool 924 OpenWithPoseView::HandleMessageDropped(BMessage* DEBUG_ONLY(message)) 925 { 926 #if DEBUG 927 // in debug mode allow tweaking the colors 928 const rgb_color* color; 929 ssize_t size; 930 // handle roColour-style color drops 931 if (message->FindData("RGBColor", 'RGBC', (const void**)&color, &size) 932 == B_OK) { 933 SetViewColor(*color); 934 SetLowColor(*color); 935 Invalidate(); 936 return true; 937 } 938 #endif 939 return false; 940 } 941 942 943 int32 944 OpenWithPoseView::OpenWithRelation(const Model* model) const 945 { 946 OpenWithContainerWindow* window = ContainerWindow(); 947 948 return SearchForSignatureEntryList::Relation(window->EntryList(), 949 model, fHaveCommonPreferredApp ? &fPreferredRef : 0, 0); 950 } 951 952 953 void 954 OpenWithPoseView::OpenWithRelationDescription(const Model* model, 955 BString* description) const 956 { 957 OpenWithContainerWindow* window = ContainerWindow(); 958 959 SearchForSignatureEntryList::RelationDescription(window->EntryList(), 960 model, description, fHaveCommonPreferredApp ? &fPreferredRef : 0, 0); 961 } 962 963 964 // #pragma mark - OpenWithRefFilter 965 966 967 OpenWithRefFilter::OpenWithRefFilter(SearchForSignatureEntryList* iterator, 968 const BMessage *entryList, entry_ref* preferredRef) 969 : 970 fIterator(iterator), 971 fEntryList(entryList), 972 fPreferredRef(preferredRef) 973 { 974 } 975 976 977 bool 978 OpenWithRefFilter::Filter(const entry_ref* ref, BNode* node, stat_beos* st, 979 const char* filetype) 980 { 981 Model *model = new Model(ref, true, true); 982 bool canOpen = fIterator->CanOpenWithFilter(model, fEntryList, 983 fPreferredRef); 984 delete model; 985 986 return canOpen; 987 } 988 989 990 // #pragma mark - 991 992 993 RelationCachingModelProxy::RelationCachingModelProxy(Model* model) 994 : 995 fModel(model), 996 fRelation(kUnknownRelation) 997 { 998 } 999 1000 1001 RelationCachingModelProxy::~RelationCachingModelProxy() 1002 { 1003 delete fModel; 1004 } 1005 1006 1007 int32 1008 RelationCachingModelProxy::Relation(SearchForSignatureEntryList* iterator, 1009 BMessage* entries) const 1010 { 1011 if (fRelation == kUnknownRelation) 1012 fRelation = iterator->Relation(entries, fModel); 1013 1014 return fRelation; 1015 } 1016 1017 1018 // #pragma mark - OpenWithMenu 1019 1020 1021 OpenWithMenu::OpenWithMenu(const char* label, const BMessage* entriesToOpen, 1022 BWindow* parentWindow, BHandler* target) 1023 : 1024 BSlowMenu(label), 1025 fEntriesToOpen(*entriesToOpen), 1026 target(target), 1027 fIterator(NULL), 1028 fSupportingAppList(NULL), 1029 fParentWindow(parentWindow) 1030 { 1031 InitIconPreloader(); 1032 1033 SetFont(be_plain_font); 1034 1035 // too long to have triggers 1036 SetTriggersEnabled(false); 1037 } 1038 1039 1040 OpenWithMenu::OpenWithMenu(const char* label, const BMessage* entriesToOpen, 1041 BWindow* parentWindow, const BMessenger &messenger) 1042 : 1043 BSlowMenu(label), 1044 fEntriesToOpen(*entriesToOpen), 1045 target(NULL), 1046 fMessenger(messenger), 1047 fIterator(NULL), 1048 fSupportingAppList(NULL), 1049 fParentWindow(parentWindow) 1050 { 1051 InitIconPreloader(); 1052 1053 SetFont(be_plain_font); 1054 1055 // too long to have triggers 1056 SetTriggersEnabled(false); 1057 } 1058 1059 1060 namespace BPrivate { 1061 1062 int 1063 SortByRelationAndName(const RelationCachingModelProxy* model1, 1064 const RelationCachingModelProxy* model2, void* castToMenu) 1065 { 1066 OpenWithMenu* menu = (OpenWithMenu*)castToMenu; 1067 1068 // find out the relations of app models to the opened entries 1069 int32 relation1 = model1->Relation(menu->fIterator, &menu->fEntriesToOpen); 1070 int32 relation2 = model2->Relation(menu->fIterator, &menu->fEntriesToOpen); 1071 1072 if (relation1 < relation2) { 1073 // relation with the lowest number goes first 1074 return 1; 1075 } else if (relation1 > relation2) 1076 return -1; 1077 1078 // if relations match, sort by app name 1079 return strcmp(model1->fModel->Name(), model2->fModel->Name()); 1080 } 1081 1082 } // namespace BPrivate 1083 1084 1085 bool 1086 OpenWithMenu::StartBuildingItemList() 1087 { 1088 fIterator = new SearchForSignatureEntryList(false); 1089 // push all the supporting apps from all the entries into the 1090 // search for signature iterator 1091 EachEntryRef(&fEntriesToOpen, AddOneRefSignatures, fIterator, 100); 1092 // add superhandlers 1093 AddSupportingAppForTypeToQuery(fIterator, B_FILE_MIMETYPE); 1094 1095 fHaveCommonPreferredApp = fIterator->GetPreferredApp(&fPreferredRef); 1096 status_t error = fIterator->Rewind(); 1097 if (error != B_OK) { 1098 PRINT(("failed to initialize iterator %s\n", strerror(error))); 1099 return false; 1100 } 1101 1102 fSupportingAppList = new BObjectList<RelationCachingModelProxy>(20, true); 1103 1104 //queryRetrieval = new BStopWatch("get next entry on BQuery"); 1105 return true; 1106 } 1107 1108 1109 bool 1110 OpenWithMenu::AddNextItem() 1111 { 1112 BEntry entry; 1113 if (fIterator->GetNextEntry(&entry) != B_OK) 1114 return false; 1115 1116 Model* model = new Model(&entry, true); 1117 if (model->InitCheck() != B_OK 1118 || !fIterator->CanOpenWithFilter(model, &fEntriesToOpen, 1119 (fHaveCommonPreferredApp ? &fPreferredRef : 0))) { 1120 // only allow executables, filter out multiple copies of the Tracker, 1121 // filter out version that don't list the correct types, etc. 1122 delete model; 1123 } else 1124 fSupportingAppList->AddItem(new RelationCachingModelProxy(model)); 1125 1126 return true; 1127 } 1128 1129 1130 void 1131 OpenWithMenu::DoneBuildingItemList() 1132 { 1133 // sort by app name 1134 fSupportingAppList->SortItems(SortByRelationAndName, this); 1135 1136 // check if each app is unique 1137 bool isUnique = true; 1138 int32 count = fSupportingAppList->CountItems(); 1139 for (int32 index = 0; index < count - 1; index++) { 1140 // the list is sorted, just compare two adjacent models 1141 if (strcmp(fSupportingAppList->ItemAt(index)->fModel->Name(), 1142 fSupportingAppList->ItemAt(index + 1)->fModel->Name()) == 0) { 1143 isUnique = false; 1144 break; 1145 } 1146 } 1147 1148 // add apps as menu items 1149 BFont font; 1150 GetFont(&font); 1151 1152 int32 lastRelation = -1; 1153 for (int32 index = 0; index < count ; index++) { 1154 RelationCachingModelProxy* modelProxy 1155 = fSupportingAppList->ItemAt(index); 1156 Model* model = modelProxy->fModel; 1157 BMessage* message = new BMessage(fEntriesToOpen); 1158 message->AddRef("handler", model->EntryRef()); 1159 BContainerWindow* window 1160 = dynamic_cast<BContainerWindow*>(fParentWindow); 1161 if (window != NULL) { 1162 message->AddData("nodeRefsToClose", B_RAW_TYPE, 1163 window->TargetModel()->NodeRef(), sizeof(node_ref)); 1164 } 1165 1166 BString result; 1167 if (isUnique) { 1168 // just use the app name 1169 result = model->Name(); 1170 } else { 1171 // get a truncated full path 1172 BPath path; 1173 BEntry entry(model->EntryRef()); 1174 if (entry.GetPath(&path) != B_OK) { 1175 PRINT(("stale entry ref %s\n", model->Name())); 1176 delete message; 1177 continue; 1178 } 1179 result = path.Path(); 1180 font.TruncateString(&result, B_TRUNCATE_MIDDLE, kMaxMenuWidth); 1181 } 1182 #if DEBUG 1183 BString relationDescription; 1184 fIterator->RelationDescription(&fEntriesToOpen, model, &relationDescription); 1185 result += " ("; 1186 result += relationDescription; 1187 result += ")"; 1188 #endif 1189 1190 // divide different relations of opening with a separator 1191 int32 relation = modelProxy->Relation(fIterator, &fEntriesToOpen); 1192 if (lastRelation != -1 && relation != lastRelation) 1193 AddSeparatorItem(); 1194 lastRelation = relation; 1195 1196 ModelMenuItem* item = new ModelMenuItem(model, result.String(), 1197 message); 1198 AddItem(item); 1199 // mark item if it represents the preferred app 1200 if (fHaveCommonPreferredApp && *(model->EntryRef()) == fPreferredRef) { 1201 //PRINT(("marking item for % as preferred", model->Name())); 1202 item->SetMarked(true); 1203 } 1204 } 1205 1206 // target the menu 1207 if (target != NULL) 1208 SetTargetForItems(target); 1209 else 1210 SetTargetForItems(fMessenger); 1211 1212 if (CountItems() == 0) { 1213 BMenuItem* item = new BMenuItem(B_TRANSLATE("no supporting apps"), 0); 1214 item->SetEnabled(false); 1215 AddItem(item); 1216 } 1217 } 1218 1219 1220 void 1221 OpenWithMenu::ClearMenuBuildingState() 1222 { 1223 delete fIterator; 1224 fIterator = NULL; 1225 delete fSupportingAppList; 1226 fSupportingAppList = NULL; 1227 } 1228 1229 1230 // #pragma mark - SearchForSignatureEntryList 1231 1232 1233 SearchForSignatureEntryList::SearchForSignatureEntryList(bool canAddAllApps) 1234 : 1235 fIteratorList(NULL), 1236 fSignatures(20, true), 1237 fPreferredAppCount(0), 1238 fPreferredAppForFileCount(0), 1239 fGenericFilesOnly(true), 1240 fCanAddAllApps(canAddAllApps), 1241 fFoundOneNonSuperHandler(false) 1242 { 1243 } 1244 1245 1246 SearchForSignatureEntryList::~SearchForSignatureEntryList() 1247 { 1248 delete fIteratorList; 1249 } 1250 1251 1252 void 1253 SearchForSignatureEntryList::PushUniqueSignature(const char* str) 1254 { 1255 // do a unique add 1256 if (fSignatures.EachElement(FindOne, (void*)str)) 1257 return; 1258 1259 fSignatures.AddItem(new BString(str)); 1260 } 1261 1262 1263 status_t 1264 SearchForSignatureEntryList::GetNextEntry(BEntry* entry, bool) 1265 { 1266 return fIteratorList->GetNextEntry(entry); 1267 } 1268 1269 1270 status_t 1271 SearchForSignatureEntryList::GetNextRef(entry_ref* ref) 1272 { 1273 return fIteratorList->GetNextRef(ref); 1274 } 1275 1276 1277 int32 1278 SearchForSignatureEntryList::GetNextDirents(struct dirent* buffer, 1279 size_t length, int32 count) 1280 { 1281 return fIteratorList->GetNextDirents(buffer, length, count); 1282 } 1283 1284 1285 struct AddOneTermParams { 1286 BString* result; 1287 bool first; 1288 }; 1289 1290 1291 static const BString* 1292 AddOnePredicateTerm(const BString* item, void* castToParams) 1293 { 1294 AddOneTermParams* params = (AddOneTermParams*)castToParams; 1295 if (!params->first) 1296 (*params->result) << " || "; 1297 (*params->result) << kAttrAppSignature << " = " << item->String(); 1298 1299 params->first = false; 1300 1301 return 0; 1302 } 1303 1304 1305 status_t 1306 SearchForSignatureEntryList::Rewind() 1307 { 1308 if (fIteratorList) 1309 return fIteratorList->Rewind(); 1310 1311 if (!fSignatures.CountItems()) 1312 return ENOENT; 1313 1314 // build up the iterator 1315 fIteratorList = new CachedEntryIteratorList(false); 1316 // We cannot sort the cached inodes, as CanOpenWithFilter() relies 1317 // on the fact that ConditionalAllAppsIterator results come last. 1318 1319 // build the predicate string by oring queries for the individual 1320 // signatures 1321 BString predicateString; 1322 1323 AddOneTermParams params; 1324 params.result = &predicateString; 1325 params.first = true; 1326 1327 fSignatures.EachElement(AddOnePredicateTerm, ¶ms); 1328 1329 ASSERT(predicateString.Length()); 1330 // PRINT(("query predicate %s\n", predicateString.String())); 1331 fIteratorList->AddItem(new TWalkerWrapper( 1332 new BTrackerPrivate::TQueryWalker(predicateString.String()))); 1333 fIteratorList->AddItem(new ConditionalAllAppsIterator(this)); 1334 1335 return fIteratorList->Rewind(); 1336 } 1337 1338 1339 int32 1340 SearchForSignatureEntryList::CountEntries() 1341 { 1342 return 0; 1343 } 1344 1345 1346 bool 1347 SearchForSignatureEntryList::GetPreferredApp(entry_ref* ref) const 1348 { 1349 if (fPreferredAppCount == 1) 1350 *ref = fPreferredRef; 1351 1352 return fPreferredAppCount == 1; 1353 } 1354 1355 1356 void 1357 SearchForSignatureEntryList::TrySettingPreferredApp(const entry_ref* ref) 1358 { 1359 if (!fPreferredAppCount) { 1360 fPreferredRef = *ref; 1361 fPreferredAppCount++; 1362 } else if (fPreferredRef != *ref) { 1363 // if more than one, will not return any 1364 fPreferredAppCount++; 1365 } 1366 } 1367 1368 1369 void 1370 SearchForSignatureEntryList::TrySettingPreferredAppForFile(const entry_ref* ref) 1371 { 1372 if (!fPreferredAppForFileCount) { 1373 fPreferredRefForFile = *ref; 1374 fPreferredAppForFileCount++; 1375 } else if (fPreferredRefForFile != *ref) { 1376 // if more than one, will not return any 1377 fPreferredAppForFileCount++; 1378 } 1379 } 1380 1381 1382 void 1383 SearchForSignatureEntryList::NonGenericFileFound() 1384 { 1385 fGenericFilesOnly = false; 1386 } 1387 1388 1389 bool 1390 SearchForSignatureEntryList::GenericFilesOnly() const 1391 { 1392 return fGenericFilesOnly; 1393 } 1394 1395 1396 bool 1397 SearchForSignatureEntryList::ShowAllApplications() const 1398 { 1399 return fCanAddAllApps && !fFoundOneNonSuperHandler; 1400 } 1401 1402 1403 int32 1404 SearchForSignatureEntryList::Relation(const Model* nodeModel, 1405 const Model* applicationModel) 1406 { 1407 int32 supportsMimeType = applicationModel->SupportsMimeType( 1408 nodeModel->MimeType(), 0, true); 1409 switch (supportsMimeType) { 1410 case kDoesNotSupportType: 1411 return kNoRelation; 1412 1413 case kSuperhandlerModel: 1414 return kSuperhandler; 1415 1416 case kModelSupportsSupertype: 1417 return kSupportsSupertype; 1418 1419 case kModelSupportsType: 1420 return kSupportsType; 1421 } 1422 1423 TRESPASS(); 1424 return kNoRelation; 1425 } 1426 1427 1428 int32 1429 SearchForSignatureEntryList::Relation(const BMessage* entriesToOpen, 1430 const Model* model) const 1431 { 1432 return Relation(entriesToOpen, model, 1433 fPreferredAppCount == 1 ? &fPreferredRef : 0, 1434 fPreferredAppForFileCount == 1 ? &fPreferredRefForFile : 0); 1435 } 1436 1437 1438 void 1439 SearchForSignatureEntryList::RelationDescription(const BMessage* entriesToOpen, 1440 const Model* model, BString* description) const 1441 { 1442 RelationDescription(entriesToOpen, model, description, 1443 fPreferredAppCount == 1 ? &fPreferredRef : 0, 1444 fPreferredAppForFileCount == 1 ? &fPreferredRefForFile : 0); 1445 } 1446 1447 1448 int32 1449 SearchForSignatureEntryList::Relation(const BMessage* entriesToOpen, 1450 const Model* applicationModel, const entry_ref* preferredApp, 1451 const entry_ref* preferredAppForFile) 1452 { 1453 for (int32 index = 0; ; index++) { 1454 entry_ref ref; 1455 if (entriesToOpen->FindRef("refs", index, &ref) != B_OK) 1456 break; 1457 1458 // need to init a model so that typeless folders etc. will still 1459 // appear to have a mime type 1460 1461 Model model(&ref, true, true); 1462 if (model.InitCheck()) 1463 continue; 1464 1465 int32 result = Relation(&model, applicationModel); 1466 if (result != kNoRelation) { 1467 if (preferredAppForFile 1468 && *applicationModel->EntryRef() == *preferredAppForFile) { 1469 return kPreferredForFile; 1470 } 1471 1472 if (result == kSupportsType && preferredApp 1473 && *applicationModel->EntryRef() == *preferredApp) { 1474 // application matches cached preferred app, we are done 1475 return kPreferredForType; 1476 } 1477 1478 return result; 1479 } 1480 } 1481 1482 return kNoRelation; 1483 } 1484 1485 1486 void 1487 SearchForSignatureEntryList::RelationDescription(const BMessage* entriesToOpen, 1488 const Model* applicationModel, BString* description, 1489 const entry_ref* preferredApp, const entry_ref* preferredAppForFile) 1490 { 1491 for (int32 index = 0; ;index++) { 1492 entry_ref ref; 1493 if (entriesToOpen->FindRef("refs", index, &ref) != B_OK) 1494 break; 1495 1496 if (preferredAppForFile && ref == *preferredAppForFile) { 1497 description->SetTo(B_TRANSLATE("Preferred for file")); 1498 return; 1499 } 1500 1501 Model model(&ref, true, true); 1502 if (model.InitCheck()) 1503 continue; 1504 1505 BMimeType mimeType; 1506 int32 result = Relation(&model, applicationModel); 1507 switch (result) { 1508 case kDoesNotSupportType: 1509 continue; 1510 1511 case kSuperhandler: 1512 description->SetTo(B_TRANSLATE("Handles any file")); 1513 return; 1514 1515 case kSupportsSupertype: 1516 { 1517 mimeType.SetTo(model.MimeType()); 1518 // status_t result = mimeType.GetSupertype(&mimeType); 1519 1520 char* type = (char*)mimeType.Type(); 1521 char* tmp = strchr(type, '/'); 1522 if (tmp != NULL) 1523 *tmp = '\0'; 1524 1525 //PRINT(("getting supertype for %s, result %s, got %s\n", 1526 // model.MimeType(), strerror(result), mimeType.Type())); 1527 description->SetTo(B_TRANSLATE("Handles any %type")); 1528 //*description += mimeType.Type(); 1529 description->ReplaceFirst("%type", type); 1530 return; 1531 } 1532 1533 case kSupportsType: 1534 { 1535 mimeType.SetTo(model.MimeType()); 1536 1537 if (preferredApp != NULL 1538 && *applicationModel->EntryRef() == *preferredApp) { 1539 // application matches cached preferred app, we are done 1540 description->SetTo(B_TRANSLATE("Preferred for %type")); 1541 } else 1542 description->SetTo(B_TRANSLATE("Handles %type")); 1543 1544 char shortDescription[256]; 1545 if (mimeType.GetShortDescription(shortDescription) == B_OK) 1546 description->ReplaceFirst("%type", shortDescription); 1547 else 1548 description->ReplaceFirst("%type", mimeType.Type()); 1549 1550 return; 1551 } 1552 } 1553 } 1554 1555 description->SetTo(B_TRANSLATE("Does not handle file")); 1556 } 1557 1558 1559 bool 1560 SearchForSignatureEntryList::CanOpenWithFilter(const Model* appModel, 1561 const BMessage* entriesToOpen, const entry_ref* preferredApp) 1562 { 1563 ThrowOnAssert(appModel != NULL); 1564 1565 if (!appModel->IsExecutable() || !appModel->Node()) { 1566 // weed out non-executable 1567 #if xDEBUG 1568 BPath path; 1569 BEntry entry(appModel->EntryRef()); 1570 entry.GetPath(&path); 1571 PRINT(("filtering out %s- not executable \n", path.Path())); 1572 #endif 1573 return false; 1574 } 1575 1576 if (strcasecmp(appModel->MimeType(), B_APP_MIME_TYPE) != 0) { 1577 // filter out pe containers on PPC etc. 1578 return false; 1579 } 1580 1581 BFile* file = dynamic_cast<BFile*>(appModel->Node()); 1582 ASSERT(file != NULL); 1583 1584 char signature[B_MIME_TYPE_LENGTH]; 1585 if (GetAppSignatureFromAttr(file, signature) == B_OK 1586 && strcasecmp(signature, kTrackerSignature) == 0) { 1587 // special case the Tracker - make sure only the running copy is 1588 // in the list 1589 app_info trackerInfo; 1590 if (*appModel->EntryRef() != trackerInfo.ref) { 1591 // this is an inactive copy of the Tracker, remove it 1592 1593 #if xDEBUG 1594 BPath path1; 1595 BPath path2; 1596 BEntry entry(appModel->EntryRef()); 1597 entry.GetPath(&path1); 1598 1599 BEntry entry2(&trackerInfo.ref); 1600 entry2.GetPath(&path2); 1601 1602 PRINT(("filtering out %s, sig %s, active Tracker at %s, " 1603 "result %s, refName %s\n", 1604 path1.Path(), signature, path2.Path(), 1605 strerror(be_roster->GetActiveAppInfo(&trackerInfo)), 1606 trackerInfo.ref.name)); 1607 #endif 1608 return false; 1609 } 1610 } 1611 1612 if (FSInTrashDir(appModel->EntryRef())) 1613 return false; 1614 1615 if (ShowAllApplications()) { 1616 // don't check for these if we didn't look for every single app 1617 // to not slow filtering down 1618 uint32 flags; 1619 BAppFileInfo appFileInfo(dynamic_cast<BFile*>(appModel->Node())); 1620 if (appFileInfo.GetAppFlags(&flags) != B_OK) 1621 return false; 1622 1623 if ((flags & B_BACKGROUND_APP) || (flags & B_ARGV_ONLY)) 1624 return false; 1625 1626 if (!signature[0]) 1627 // weed out apps with empty signatures 1628 return false; 1629 } 1630 1631 int32 relation = Relation(entriesToOpen, appModel, preferredApp, 0); 1632 if (relation == kNoRelation && !ShowAllApplications()) { 1633 #if xDEBUG 1634 BPath path; 1635 BEntry entry(appModel->EntryRef()); 1636 entry.GetPath(&path); 1637 1638 PRINT(("filtering out %s, does not handle any of opened files\n", 1639 path.Path())); 1640 #endif 1641 return false; 1642 } 1643 1644 if (relation != kNoRelation && relation != kSuperhandler 1645 && !fGenericFilesOnly) { 1646 // we hit at least one app that is not a superhandler and 1647 // handles the document 1648 fFoundOneNonSuperHandler = true; 1649 } 1650 1651 return true; 1652 } 1653 1654 1655 // #pragma mark - ConditionalAllAppsIterator 1656 1657 1658 ConditionalAllAppsIterator::ConditionalAllAppsIterator( 1659 SearchForSignatureEntryList* parent) 1660 : 1661 fParent(parent), 1662 fWalker(NULL) 1663 { 1664 } 1665 1666 1667 void 1668 ConditionalAllAppsIterator::Instantiate() 1669 { 1670 if (fWalker != NULL) 1671 return; 1672 1673 BString lookForAppsPredicate; 1674 lookForAppsPredicate << "(" << kAttrAppSignature << " = \"*\" ) && ( " 1675 << kAttrMIMEType << " = " << B_APP_MIME_TYPE << " ) "; 1676 fWalker 1677 = new BTrackerPrivate::TQueryWalker(lookForAppsPredicate.String()); 1678 } 1679 1680 1681 ConditionalAllAppsIterator::~ConditionalAllAppsIterator() 1682 { 1683 delete fWalker; 1684 } 1685 1686 1687 status_t 1688 ConditionalAllAppsIterator::GetNextEntry(BEntry* entry, bool traverse) 1689 { 1690 if (!Iterate()) 1691 return B_ENTRY_NOT_FOUND; 1692 1693 Instantiate(); 1694 return fWalker->GetNextEntry(entry, traverse); 1695 } 1696 1697 1698 status_t 1699 ConditionalAllAppsIterator::GetNextRef(entry_ref* ref) 1700 { 1701 if (!Iterate()) 1702 return B_ENTRY_NOT_FOUND; 1703 1704 Instantiate(); 1705 return fWalker->GetNextRef(ref); 1706 } 1707 1708 1709 int32 1710 ConditionalAllAppsIterator::GetNextDirents(struct dirent* buffer, 1711 size_t length, int32 count) 1712 { 1713 if (!Iterate()) 1714 return 0; 1715 1716 Instantiate(); 1717 return fWalker->GetNextDirents(buffer, length, count); 1718 } 1719 1720 1721 status_t 1722 ConditionalAllAppsIterator::Rewind() 1723 { 1724 if (!Iterate()) 1725 return B_OK; 1726 1727 Instantiate(); 1728 return fWalker->Rewind(); 1729 } 1730 1731 1732 int32 1733 ConditionalAllAppsIterator::CountEntries() 1734 { 1735 if (!Iterate()) 1736 return 0; 1737 1738 Instantiate(); 1739 return fWalker->CountEntries(); 1740 } 1741 1742 1743 bool 1744 ConditionalAllAppsIterator::Iterate() const 1745 { 1746 return fParent->ShowAllApplications(); 1747 } 1748