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