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