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