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 queryIterator->PushUniqueSignature(model.PreferredAppSignature()); 594 595 // got one, mark it as preferred for this node 596 if (be_roster->FindApp(model.PreferredAppSignature(), &preferredRef) == B_OK) { 597 preferredAppFromNode = true; 598 queryIterator->TrySettingPreferredAppForFile(&preferredRef); 599 } 600 } 601 602 mimeType = model.MimeType(); 603 mimeType.ToLower(); 604 605 if (mimeType.Length() && !mimeType.ICompare(B_FILE_MIMETYPE) == 0) 606 queryIterator->NonGenericFileFound(); 607 608 // get supporting apps for type 609 AddSupportingAppForTypeToQuery(queryIterator, mimeType.String()); 610 611 // find the preferred app for this type 612 if (be_roster->FindApp(mimeType.String(), &preferredRef) == B_OK) 613 queryIterator->TrySettingPreferredApp(&preferredRef); 614 615 return NULL; 616 } 617 618 619 EntryListBase * 620 OpenWithPoseView::InitDirentIterator(const entry_ref *) 621 { 622 OpenWithContainerWindow *window = ContainerWindow(); 623 624 const BMessage *entryList = window->EntryList(); 625 626 fIterator = new SearchForSignatureEntryList(true); 627 628 // push all the supporting apps from all the entries into the 629 // search for signature iterator 630 EachEntryRef(entryList, AddOneRefSignatures, fIterator, 100); 631 632 // push superhandlers 633 AddSupportingAppForTypeToQuery(fIterator, B_FILE_MIMETYPE); 634 fHaveCommonPreferredApp = fIterator->GetPreferredApp(&fPreferredRef); 635 636 if (fIterator->Rewind() != B_OK) { 637 delete fIterator; 638 fIterator = NULL; 639 HideBarberPole(); 640 return NULL; 641 } 642 return fIterator; 643 } 644 645 646 void 647 OpenWithPoseView::OpenSelection(BPose *pose, int32 *) 648 { 649 OpenWithContainerWindow *window = ContainerWindow(); 650 651 int32 count = fSelectionList->CountItems(); 652 if (!count) 653 return; 654 655 if (!pose) 656 pose = fSelectionList->FirstItem(); 657 658 ASSERT(pose); 659 660 BEntry entry(pose->TargetModel()->EntryRef()); 661 if (entry.InitCheck() != B_OK) { 662 BString errorString; 663 errorString << "Could not find application \"" 664 << pose->TargetModel()->Name() << "\""; 665 666 (new BAlert("", errorString.String(), "OK", 0, 0, B_WIDTH_AS_USUAL, 667 B_WARNING_ALERT))->Go(); 668 return; 669 } 670 671 if (OpenWithRelation(pose->TargetModel()) == kNoRelation) { 672 if (!fIterator->GenericFilesOnly()) { 673 BString warning; 674 warning << "The application \"" << pose->TargetModel()->Name() 675 << "\" does not support the type of document you are " 676 "about to open. Are you sure you want to proceed? If you know that " 677 "the application supports the document type, you should contact the " 678 "publisher of the application and ask them to update their application " 679 "to list the type of your document as supported."; 680 681 if ((new BAlert("", warning.String(), "Cancel", "Open", 0, 682 B_WIDTH_AS_USUAL, B_WARNING_ALERT))->Go() == 0) 683 return; 684 } 685 // else - once we have an extensible sniffer, tell users to ask 686 // publishers to fix up sniffers 687 } 688 689 BMessage message(*window->EntryList()); 690 // make a clone to send 691 message.RemoveName("launchUsingSelector"); 692 // make sure the old selector is not in the message 693 message.AddRef("handler", pose->TargetModel()->EntryRef()); 694 // add ref of the selected handler 695 696 ASSERT(fSelectionHandler); 697 698 if (fSelectionHandler) 699 fSelectionHandler->PostMessage(&message); 700 701 window->PostMessage(B_QUIT_REQUESTED); 702 } 703 704 705 void 706 OpenWithPoseView::Pulse() 707 { 708 // disable the Open and make default button if the default 709 // app matches the selected app 710 // 711 // disable the Open button if no apps selected 712 713 OpenWithContainerWindow *window = ContainerWindow(); 714 715 if (!fSelectionList->CountItems()) { 716 window->SetCanSetAppAsDefault(false); 717 window->SetCanOpen(false); 718 _inherited::Pulse(); 719 return; 720 } 721 722 // if we selected a non-handling application, don't allow setting 723 // it as preferred 724 Model *firstSelected = fSelectionList->FirstItem()->TargetModel(); 725 if (OpenWithRelation(firstSelected) == kNoRelation) { 726 window->SetCanSetAppAsDefault(false); 727 window->SetCanOpen(true); 728 _inherited::Pulse(); 729 return; 730 } 731 732 // make the open button enabled, because we have na app selected 733 window->SetCanOpen(true); 734 if (!fHaveCommonPreferredApp) { 735 window->SetCanSetAppAsDefault(true); 736 _inherited::Pulse(); 737 return; 738 } 739 740 ASSERT(fSelectionList->CountItems() == 1); 741 742 // enable the Open and make default if selected application different 743 // from preferred app ref 744 window->SetCanSetAppAsDefault((*fSelectionList->FirstItem()-> 745 TargetModel()->EntryRef()) != fPreferredRef); 746 747 _inherited::Pulse(); 748 } 749 750 751 void 752 OpenWithPoseView::SetUpDefaultColumnsIfNeeded() 753 { 754 // in case there were errors getting some columns 755 if (fColumnList->CountItems() != 0) 756 return; 757 758 BColumn *nameColumn = new BColumn("Name", kColumnStart, 125, B_ALIGN_LEFT, 759 kAttrStatName, B_STRING_TYPE, true, true); 760 fColumnList->AddItem(nameColumn); 761 BColumn *relationColumn = new BColumn("Relation", 180, 100, B_ALIGN_LEFT, 762 kAttrOpenWithRelation, B_STRING_TYPE, false, false); 763 fColumnList->AddItem(relationColumn); 764 fColumnList->AddItem(new BColumn("Path", 290, 225, B_ALIGN_LEFT, 765 kAttrPath, B_STRING_TYPE, true, false)); 766 fColumnList->AddItem(new BColumn("Version", 525, 70, B_ALIGN_LEFT, 767 kAttrAppVersion, B_STRING_TYPE, false, false)); 768 769 // sort by relation and by name 770 SetPrimarySort(relationColumn->AttrHash()); 771 SetSecondarySort(nameColumn->AttrHash()); 772 } 773 774 775 bool 776 OpenWithPoseView::AddPosesThreadValid(const entry_ref *) const 777 { 778 return true; 779 } 780 781 782 void 783 OpenWithPoseView::CreatePoses(Model **models, PoseInfo *poseInfoArray, int32 count, 784 BPose **resultingPoses, bool insertionSort, int32 *lastPoseIndexPtr, 785 BRect *boundsPtr, bool forceDraw) 786 { 787 // overridden to try to select the preferred handling app 788 _inherited::CreatePoses(models, poseInfoArray, count, resultingPoses, insertionSort, 789 lastPoseIndexPtr, boundsPtr, forceDraw); 790 791 if (resultingPoses) { 792 for (int32 index = 0; index < count; index++) { 793 if (resultingPoses[index] && fHaveCommonPreferredApp 794 && *(models[index]->EntryRef()) == fPreferredRef) { 795 // this is our preferred app, select it's pose 796 SelectPose(resultingPoses[index], IndexOfPose(resultingPoses[index])); 797 } 798 } 799 } 800 } 801 802 803 void 804 OpenWithPoseView::KeyDown(const char *bytes, int32 count) 805 { 806 if (bytes[0] == B_TAB) { 807 // just shift the focus, don't tab to the next pose 808 BView::KeyDown(bytes, count); 809 } else 810 _inherited::KeyDown(bytes, count); 811 } 812 813 814 void 815 OpenWithPoseView::SaveState(AttributeStreamNode *node) 816 { 817 _inherited::SaveState(node); 818 } 819 820 821 void 822 OpenWithPoseView::RestoreState(AttributeStreamNode *node) 823 { 824 _inherited::RestoreState(node); 825 fViewState->SetViewMode(kListMode); 826 } 827 828 829 void 830 OpenWithPoseView::SaveState(BMessage &message) const 831 { 832 _inherited::SaveState(message); 833 } 834 835 836 void 837 OpenWithPoseView::RestoreState(const BMessage &message) 838 { 839 _inherited::RestoreState(message); 840 fViewState->SetViewMode(kListMode); 841 } 842 843 844 void 845 OpenWithPoseView::SavePoseLocations(BRect *) 846 { 847 // do nothing 848 } 849 850 851 void 852 OpenWithPoseView::MoveSelectionToTrash(bool) 853 { 854 } 855 856 857 void 858 OpenWithPoseView::MoveSelectionTo(BPoint, BPoint, BContainerWindow *) 859 { 860 } 861 862 863 void 864 OpenWithPoseView::MoveSelectionInto(Model *, BContainerWindow *, bool, bool) 865 { 866 } 867 868 869 bool 870 OpenWithPoseView::Represents(const node_ref *) const 871 { 872 return false; 873 } 874 875 876 bool 877 OpenWithPoseView::Represents(const entry_ref *) const 878 { 879 return false; 880 } 881 882 883 bool 884 OpenWithPoseView::HandleMessageDropped(BMessage *DEBUG_ONLY(message)) 885 { 886 #if DEBUG 887 // in debug mode allow tweaking the colors 888 const rgb_color *color; 889 int32 size; 890 // handle roColour-style color drops 891 if (message->FindData("RGBColor", 'RGBC', (const void **)&color, &size) == B_OK) { 892 SetViewColor(*color); 893 SetLowColor(*color); 894 Invalidate(); 895 return true; 896 } 897 #endif 898 return false; 899 } 900 901 902 int32 903 OpenWithPoseView::OpenWithRelation(const Model *model) const 904 { 905 OpenWithContainerWindow *window = ContainerWindow(); 906 907 return SearchForSignatureEntryList::Relation(window->EntryList(), 908 model, fHaveCommonPreferredApp ? &fPreferredRef : 0, 0); 909 } 910 911 912 void 913 OpenWithPoseView::OpenWithRelationDescription(const Model *model, 914 BString *description) const 915 { 916 OpenWithContainerWindow *window = ContainerWindow(); 917 918 SearchForSignatureEntryList::RelationDescription(window->EntryList(), 919 model, description, fHaveCommonPreferredApp ? &fPreferredRef : 0, 0); 920 } 921 922 923 bool 924 OpenWithPoseView::ShouldShowPose(const Model *model, const PoseInfo *poseInfo) 925 { 926 OpenWithContainerWindow *window = ContainerWindow(); 927 // filter for add_poses 928 if (!fIterator->CanOpenWithFilter(model, window->EntryList(), 929 fHaveCommonPreferredApp ? &fPreferredRef : 0)) 930 return false; 931 932 return _inherited::ShouldShowPose(model, poseInfo); 933 } 934 935 936 // #pragma mark - 937 938 939 RelationCachingModelProxy::RelationCachingModelProxy(Model *model) 940 : 941 fModel(model), 942 fRelation(kUnknownRelation) 943 { 944 } 945 946 947 RelationCachingModelProxy::~RelationCachingModelProxy() 948 { 949 delete fModel; 950 } 951 952 953 int32 954 RelationCachingModelProxy::Relation(SearchForSignatureEntryList *iterator, 955 BMessage *entries) const 956 { 957 if (fRelation == kUnknownRelation) 958 fRelation = iterator->Relation(entries, fModel); 959 960 return fRelation; 961 } 962 963 964 // #pragma mark - 965 966 967 OpenWithMenu::OpenWithMenu(const char *label, const BMessage *entriesToOpen, 968 BWindow *parentWindow, BHandler *target) 969 : BSlowMenu(label), 970 fEntriesToOpen(*entriesToOpen), 971 target(target), 972 fIterator(NULL), 973 fSupportingAppList(NULL), 974 fParentWindow(parentWindow) 975 { 976 InitIconPreloader(); 977 978 SetFont(be_plain_font); 979 980 // too long to have triggers 981 SetTriggersEnabled(false); 982 } 983 984 985 OpenWithMenu::OpenWithMenu(const char *label, const BMessage *entriesToOpen, 986 BWindow *parentWindow, const BMessenger &messenger) 987 : BSlowMenu(label), 988 fEntriesToOpen(*entriesToOpen), 989 target(NULL), 990 fMessenger(messenger), 991 fIterator(NULL), 992 fSupportingAppList(NULL), 993 fParentWindow(parentWindow) 994 { 995 InitIconPreloader(); 996 997 SetFont(be_plain_font); 998 999 // too long to have triggers 1000 SetTriggersEnabled(false); 1001 } 1002 1003 1004 namespace BPrivate { 1005 1006 int 1007 SortByRelationAndName(const RelationCachingModelProxy *model1, 1008 const RelationCachingModelProxy *model2, void *castToMenu) 1009 { 1010 OpenWithMenu *menu = (OpenWithMenu *)castToMenu; 1011 1012 // find out the relations of app models to the opened entries 1013 int32 relation1 = model1->Relation(menu->fIterator, &menu->fEntriesToOpen); 1014 int32 relation2 = model2->Relation(menu->fIterator, &menu->fEntriesToOpen); 1015 1016 if (relation1 < relation2) { 1017 // relation with the lowest number goes first 1018 return 1; 1019 } else if (relation1 > relation2) 1020 return -1; 1021 1022 // if relations match, sort by app name 1023 return strcmp(model1->fModel->Name(), model2->fModel->Name()); 1024 } 1025 1026 } // namespace BPrivate 1027 1028 1029 bool 1030 OpenWithMenu::StartBuildingItemList() 1031 { 1032 fIterator = new SearchForSignatureEntryList(false); 1033 // push all the supporting apps from all the entries into the 1034 // search for signature iterator 1035 EachEntryRef(&fEntriesToOpen, AddOneRefSignatures, fIterator, 100); 1036 // add superhandlers 1037 AddSupportingAppForTypeToQuery(fIterator, B_FILE_MIMETYPE); 1038 1039 fHaveCommonPreferredApp = fIterator->GetPreferredApp(&fPreferredRef); 1040 status_t error = fIterator->Rewind(); 1041 if (error != B_OK) { 1042 PRINT(("failed to initialize iterator %s\n", strerror(error))); 1043 return false; 1044 } 1045 1046 fSupportingAppList = new BObjectList<RelationCachingModelProxy>(20, true); 1047 1048 //queryRetrieval = new BStopWatch("get next entry on BQuery"); 1049 return true; 1050 } 1051 1052 1053 bool 1054 OpenWithMenu::AddNextItem() 1055 { 1056 BEntry entry; 1057 if (fIterator->GetNextEntry(&entry) != B_OK) 1058 return false; 1059 1060 Model *model = new Model(&entry, true); 1061 if (model->InitCheck() != B_OK 1062 || !fIterator->CanOpenWithFilter(model, &fEntriesToOpen, 1063 fHaveCommonPreferredApp ? &fPreferredRef : 0)) { 1064 // only allow executables, filter out multiple copies of the 1065 // Tracker, filter out version that don't list the correct types, 1066 // etc. 1067 delete model; 1068 } else 1069 fSupportingAppList->AddItem(new RelationCachingModelProxy(model)); 1070 1071 return true; 1072 } 1073 1074 1075 void 1076 OpenWithMenu::DoneBuildingItemList() 1077 { 1078 // sort by app name 1079 fSupportingAppList->SortItems(SortByRelationAndName, this); 1080 1081 // check if each app is unique 1082 bool unique = true; 1083 int32 count = fSupportingAppList->CountItems(); 1084 for (int32 index = 0; index < count - 1; index++) { 1085 // the list is sorted, just compare two adjacent models 1086 if (strcmp(fSupportingAppList->ItemAt(index)->fModel->Name(), 1087 fSupportingAppList->ItemAt(index + 1)->fModel->Name()) == 0) { 1088 unique = false; 1089 break; 1090 } 1091 } 1092 1093 // add apps as menu items 1094 BFont font; 1095 GetFont(&font); 1096 1097 int32 lastRelation = -1; 1098 for (int32 index = 0; index < count ; index++) { 1099 RelationCachingModelProxy *modelProxy = fSupportingAppList->ItemAt(index); 1100 Model *model = modelProxy->fModel; 1101 BMessage *message = new BMessage(fEntriesToOpen); 1102 message->AddRef("handler", model->EntryRef()); 1103 BContainerWindow *window = dynamic_cast<BContainerWindow *>(fParentWindow); 1104 if (window) 1105 message->AddData("nodeRefsToClose", B_RAW_TYPE, window->TargetModel()->NodeRef(), 1106 sizeof (node_ref)); 1107 1108 BString result; 1109 if (unique) { 1110 // just use the app name 1111 result = model->Name(); 1112 } else { 1113 // get a truncated full path 1114 BPath path; 1115 BEntry entry(model->EntryRef()); 1116 if (entry.GetPath(&path) != B_OK) { 1117 PRINT(("stale entry ref %s\n", model->Name())); 1118 delete message; 1119 continue; 1120 } 1121 result = path.Path(); 1122 font.TruncateString(&result, B_TRUNCATE_MIDDLE, kMaxMenuWidth); 1123 } 1124 #if DEBUG 1125 BString relationDescription; 1126 fIterator->RelationDescription(&fEntriesToOpen, model, &relationDescription); 1127 result += " ("; 1128 result += relationDescription; 1129 result += ")"; 1130 #endif 1131 1132 // divide different relations of opening with a separator 1133 int32 relation = modelProxy->Relation(fIterator, &fEntriesToOpen); 1134 if (lastRelation != -1 && relation != lastRelation) 1135 AddSeparatorItem(); 1136 lastRelation = relation; 1137 1138 ModelMenuItem *item = new ModelMenuItem(model, result.String(), message); 1139 AddItem(item); 1140 // mark item if it represents the preferred app 1141 if (fHaveCommonPreferredApp && *(model->EntryRef()) == fPreferredRef) { 1142 //PRINT(("marking item for % as preferred", model->Name())); 1143 item->SetMarked(true); 1144 } 1145 } 1146 1147 // target the menu 1148 if (target) 1149 SetTargetForItems(target); 1150 else 1151 SetTargetForItems(fMessenger); 1152 1153 if (!CountItems()) { 1154 BMenuItem *item = new BMenuItem("no supporting apps", 0); 1155 item->SetEnabled(false); 1156 AddItem(item); 1157 } 1158 } 1159 1160 1161 void 1162 OpenWithMenu::ClearMenuBuildingState() 1163 { 1164 delete fIterator; 1165 fIterator = NULL; 1166 delete fSupportingAppList; 1167 fSupportingAppList = NULL; 1168 } 1169 1170 1171 // #pragma mark - 1172 1173 1174 SearchForSignatureEntryList::SearchForSignatureEntryList(bool canAddAllApps) 1175 : 1176 fIteratorList(NULL), 1177 fSignatures(20, true), 1178 fPreferredAppCount(0), 1179 fPreferredAppForFileCount(0), 1180 fGenericFilesOnly(true), 1181 fCanAddAllApps(canAddAllApps), 1182 fFoundOneNonSuperHandler(false) 1183 { 1184 } 1185 1186 1187 SearchForSignatureEntryList::~SearchForSignatureEntryList() 1188 { 1189 delete fIteratorList; 1190 } 1191 1192 1193 void 1194 SearchForSignatureEntryList::PushUniqueSignature(const char *str) 1195 { 1196 // do a unique add 1197 if (fSignatures.EachElement(FindOne, (void *)str)) 1198 return; 1199 1200 fSignatures.AddItem(new BString(str)); 1201 } 1202 1203 1204 status_t 1205 SearchForSignatureEntryList::GetNextEntry(BEntry *entry, bool) 1206 { 1207 return fIteratorList->GetNextEntry(entry); 1208 } 1209 1210 1211 status_t 1212 SearchForSignatureEntryList::GetNextRef(entry_ref *ref) 1213 { 1214 return fIteratorList->GetNextRef(ref); 1215 } 1216 1217 1218 int32 1219 SearchForSignatureEntryList::GetNextDirents(struct dirent *buffer, 1220 size_t length, int32 count) 1221 { 1222 return fIteratorList->GetNextDirents(buffer, length, count); 1223 } 1224 1225 1226 struct AddOneTermParams { 1227 BString *result; 1228 bool first; 1229 }; 1230 1231 static const BString * 1232 AddOnePredicateTerm(const BString *item, void *castToParams) 1233 { 1234 AddOneTermParams *params = (AddOneTermParams *)castToParams; 1235 if (!params->first) 1236 (*params->result) << " || "; 1237 (*params->result) << kAttrAppSignature << " = " << item->String(); 1238 1239 params->first = false; 1240 1241 return 0; 1242 } 1243 1244 1245 status_t 1246 SearchForSignatureEntryList::Rewind() 1247 { 1248 if (fIteratorList) 1249 return fIteratorList->Rewind(); 1250 1251 if (!fSignatures.CountItems()) 1252 return ENOENT; 1253 1254 // build up the iterator 1255 fIteratorList = new CachedEntryIteratorList(false); 1256 // We cannot sort the cached inodes, as CanOpenWithFilter() relies 1257 // on the fact that ConditionalAllAppsIterator results come last. 1258 1259 // build the predicate string by oring queries for the individual 1260 // signatures 1261 BString predicateString; 1262 1263 AddOneTermParams params; 1264 params.result = &predicateString; 1265 params.first = true; 1266 1267 fSignatures.EachElement(AddOnePredicateTerm, ¶ms); 1268 1269 ASSERT(predicateString.Length()); 1270 // PRINT(("query predicate %s\n", predicateString.String())); 1271 fIteratorList->AddItem(new TWalkerWrapper( 1272 new WALKER_NS::TQueryWalker(predicateString.String()))); 1273 fIteratorList->AddItem(new ConditionalAllAppsIterator(this)); 1274 1275 return fIteratorList->Rewind(); 1276 } 1277 1278 1279 int32 1280 SearchForSignatureEntryList::CountEntries() 1281 { 1282 return 0; 1283 } 1284 1285 1286 bool 1287 SearchForSignatureEntryList::GetPreferredApp(entry_ref *ref) const 1288 { 1289 if (fPreferredAppCount == 1) 1290 *ref = fPreferredRef; 1291 1292 return fPreferredAppCount == 1; 1293 } 1294 1295 1296 void 1297 SearchForSignatureEntryList::TrySettingPreferredApp(const entry_ref *ref) 1298 { 1299 if (!fPreferredAppCount) { 1300 fPreferredRef = *ref; 1301 fPreferredAppCount++; 1302 } else if (fPreferredRef != *ref) 1303 // if more than one, will not return any 1304 fPreferredAppCount++; 1305 } 1306 1307 1308 void 1309 SearchForSignatureEntryList::TrySettingPreferredAppForFile(const entry_ref *ref) 1310 { 1311 if (!fPreferredAppForFileCount) { 1312 fPreferredRefForFile = *ref; 1313 fPreferredAppForFileCount++; 1314 } else if (fPreferredRefForFile != *ref) { 1315 // if more than one, will not return any 1316 fPreferredAppForFileCount++; 1317 } 1318 } 1319 1320 1321 void 1322 SearchForSignatureEntryList::NonGenericFileFound() 1323 { 1324 fGenericFilesOnly = false; 1325 } 1326 1327 1328 bool 1329 SearchForSignatureEntryList::GenericFilesOnly() const 1330 { 1331 return fGenericFilesOnly; 1332 } 1333 1334 1335 bool 1336 SearchForSignatureEntryList::ShowAllApplications() const 1337 { 1338 return fCanAddAllApps && !fFoundOneNonSuperHandler; 1339 } 1340 1341 1342 int32 1343 SearchForSignatureEntryList::Relation(const Model *nodeModel, 1344 const Model *applicationModel) 1345 { 1346 switch (applicationModel->SupportsMimeType(nodeModel->MimeType(), 0, true)) { 1347 case kDoesNotSupportType: 1348 return kNoRelation; 1349 1350 case kSuperhandlerModel: 1351 return kSuperhandler; 1352 1353 case kModelSupportsSupertype: 1354 return kSupportsSupertype; 1355 1356 case kModelSupportsType: 1357 return kSupportsType; 1358 } 1359 1360 TRESPASS(); 1361 return kNoRelation; 1362 } 1363 1364 1365 int32 1366 SearchForSignatureEntryList::Relation(const BMessage *entriesToOpen, 1367 const Model *model) const 1368 { 1369 return Relation(entriesToOpen, model, 1370 fPreferredAppCount == 1 ? &fPreferredRef : 0, 1371 fPreferredAppForFileCount == 1 ? &fPreferredRefForFile : 0); 1372 } 1373 1374 1375 void 1376 SearchForSignatureEntryList::RelationDescription(const BMessage *entriesToOpen, 1377 const Model *model, BString *description) const 1378 { 1379 RelationDescription(entriesToOpen, model, description, 1380 fPreferredAppCount == 1 ? &fPreferredRef : 0, 1381 fPreferredAppForFileCount == 1 ? &fPreferredRefForFile : 0); 1382 } 1383 1384 1385 int32 1386 SearchForSignatureEntryList::Relation(const BMessage *entriesToOpen, 1387 const Model *applicationModel, const entry_ref *preferredApp, 1388 const entry_ref *preferredAppForFile) 1389 { 1390 for (int32 index = 0; ; index++) { 1391 entry_ref ref; 1392 if (entriesToOpen->FindRef("refs", index, &ref) != B_OK) 1393 break; 1394 1395 // need to init a model so that typeless folders etc. will still appear to 1396 // have a mime type 1397 1398 Model model(&ref, true, true); 1399 if (model.InitCheck()) 1400 continue; 1401 1402 int32 result = Relation(&model, applicationModel); 1403 if (result != kNoRelation) { 1404 if (preferredAppForFile 1405 && *applicationModel->EntryRef() == *preferredAppForFile) 1406 return kPreferredForFile; 1407 1408 if (result == kSupportsType && preferredApp 1409 && *applicationModel->EntryRef() == *preferredApp) 1410 // application matches cached preferred app, we are done 1411 return kPreferredForType; 1412 1413 return result; 1414 } 1415 } 1416 1417 return kNoRelation; 1418 } 1419 1420 1421 void 1422 SearchForSignatureEntryList::RelationDescription(const BMessage *entriesToOpen, 1423 const Model *applicationModel, BString *description, const entry_ref *preferredApp, 1424 const entry_ref *preferredAppForFile) 1425 { 1426 for (int32 index = 0; ;index++) { 1427 entry_ref ref; 1428 if (entriesToOpen->FindRef("refs", index, &ref) != B_OK) 1429 break; 1430 1431 if (preferredAppForFile && ref == *preferredAppForFile) { 1432 *description = "Preferred for file"; 1433 return; 1434 } 1435 1436 Model model(&ref, true, true); 1437 if (model.InitCheck()) 1438 continue; 1439 1440 BMimeType mimeType; 1441 int32 result = Relation(&model, applicationModel); 1442 switch (result) { 1443 case kDoesNotSupportType: 1444 continue; 1445 1446 case kSuperhandler: 1447 *description = "Handles any file"; 1448 return; 1449 1450 case kSupportsSupertype: 1451 { 1452 mimeType.SetTo(model.MimeType()); 1453 // status_t result = mimeType.GetSupertype(&mimeType); 1454 1455 char *type = (char *)mimeType.Type(); 1456 char *tmp = strchr(type, '/'); 1457 if (tmp) 1458 *tmp = '\0'; 1459 1460 //PRINT(("getting supertype for %s, result %s, got %s\n", 1461 // model.MimeType(), strerror(result), mimeType.Type())); 1462 *description = "Handles any "; 1463 // *description += mimeType.Type(); 1464 *description += type; 1465 return; 1466 } 1467 1468 case kSupportsType: 1469 { 1470 mimeType.SetTo(model.MimeType()); 1471 1472 if (preferredApp && *applicationModel->EntryRef() == *preferredApp) 1473 // application matches cached preferred app, we are done 1474 *description = "Preferred for "; 1475 else 1476 *description = "Handles "; 1477 1478 char shortDescription[256]; 1479 if (mimeType.GetShortDescription(shortDescription) == B_OK) 1480 *description += shortDescription; 1481 else 1482 *description += mimeType.Type(); 1483 return; 1484 } 1485 } 1486 } 1487 1488 *description = "Does not handle file"; 1489 } 1490 1491 1492 bool 1493 SearchForSignatureEntryList::CanOpenWithFilter(const Model *appModel, 1494 const BMessage *entriesToOpen, const entry_ref *preferredApp) 1495 { 1496 if (!appModel->IsExecutable() || !appModel->Node()) { 1497 // weed out non-executable 1498 #if xDEBUG 1499 BPath path; 1500 BEntry entry(appModel->EntryRef()); 1501 entry.GetPath(&path); 1502 PRINT(("filtering out %s- not executable \n", path.Path())); 1503 #endif 1504 return false; 1505 } 1506 1507 if (strcasecmp(appModel->MimeType(), B_APP_MIME_TYPE) != 0) { 1508 // filter out pe containers on PPC etc. 1509 return false; 1510 } 1511 1512 ASSERT(dynamic_cast<BFile *>(appModel->Node())); 1513 char signature[B_MIME_TYPE_LENGTH]; 1514 status_t result = GetAppSignatureFromAttr( 1515 dynamic_cast<BFile *>(appModel->Node()), signature); 1516 1517 if (result == B_OK && strcasecmp(signature, kTrackerSignature) == 0) { 1518 // special case the Tracker - make sure only the running copy is 1519 // in the list 1520 app_info trackerInfo; 1521 result = be_roster->GetActiveAppInfo(&trackerInfo); 1522 if (*appModel->EntryRef() != trackerInfo.ref) { 1523 // this is an inactive copy of the Tracker, remove it 1524 1525 #if xDEBUG 1526 BPath path, path2; 1527 BEntry entry(appModel->EntryRef()); 1528 entry.GetPath(&path); 1529 1530 BEntry entry2(&trackerInfo.ref); 1531 entry2.GetPath(&path2); 1532 1533 PRINT(("filtering out %s, sig %s, active Tracker at %s, result %s, refName %s\n", 1534 path.Path(), signature, path2.Path(), strerror(result), 1535 trackerInfo.ref.name)); 1536 #endif 1537 return false; 1538 } 1539 } 1540 1541 if (FSInTrashDir(appModel->EntryRef())) 1542 return false; 1543 1544 if (ShowAllApplications()) { 1545 // don't check for these if we didn't look for every single app 1546 // to not slow filtering down 1547 uint32 flags; 1548 BAppFileInfo appFileInfo(dynamic_cast<BFile *>(appModel->Node())); 1549 if (appFileInfo.GetAppFlags(&flags) != B_OK) 1550 return false; 1551 1552 if ((flags & B_BACKGROUND_APP) || (flags & B_ARGV_ONLY)) 1553 return false; 1554 1555 if (!signature[0]) 1556 // weed out apps with empty signatures 1557 return false; 1558 } 1559 1560 int32 relation = Relation(entriesToOpen, appModel, preferredApp, 0); 1561 if (relation == kNoRelation && !ShowAllApplications()) { 1562 #if xDEBUG 1563 BPath path; 1564 BEntry entry(appModel->EntryRef()); 1565 entry.GetPath(&path); 1566 1567 PRINT(("filtering out %s, does not handle any of opened files\n", 1568 path.Path())); 1569 #endif 1570 return false; 1571 } 1572 1573 if (relation != kNoRelation && relation != kSuperhandler && !fGenericFilesOnly) { 1574 // we hit at least one app that is not a superhandler and 1575 // handles the document 1576 fFoundOneNonSuperHandler = true; 1577 } 1578 1579 return true; 1580 } 1581 1582 1583 // #pragma mark - 1584 1585 1586 ConditionalAllAppsIterator::ConditionalAllAppsIterator( 1587 SearchForSignatureEntryList *parent) 1588 : 1589 fParent(parent), 1590 fWalker(NULL) 1591 { 1592 } 1593 1594 1595 void 1596 ConditionalAllAppsIterator::Instantiate() 1597 { 1598 if (fWalker) 1599 return; 1600 1601 BString lookForAppsPredicate; 1602 lookForAppsPredicate << "(" << kAttrAppSignature << " = \"*\" ) && ( " 1603 << kAttrMIMEType << " = " << B_APP_MIME_TYPE << " ) "; 1604 fWalker = new WALKER_NS::TQueryWalker(lookForAppsPredicate.String()); 1605 } 1606 1607 1608 ConditionalAllAppsIterator::~ConditionalAllAppsIterator() 1609 { 1610 delete fWalker; 1611 } 1612 1613 1614 status_t 1615 ConditionalAllAppsIterator::GetNextEntry(BEntry *entry, bool traverse) 1616 { 1617 if (!Iterate()) 1618 return B_ENTRY_NOT_FOUND; 1619 1620 Instantiate(); 1621 return fWalker->GetNextEntry(entry, traverse); 1622 } 1623 1624 1625 status_t 1626 ConditionalAllAppsIterator::GetNextRef(entry_ref *ref) 1627 { 1628 if (!Iterate()) 1629 return B_ENTRY_NOT_FOUND; 1630 1631 Instantiate(); 1632 return fWalker->GetNextRef(ref); 1633 } 1634 1635 1636 int32 1637 ConditionalAllAppsIterator::GetNextDirents(struct dirent *buffer, size_t length, 1638 int32 count) 1639 { 1640 if (!Iterate()) 1641 return 0; 1642 1643 Instantiate(); 1644 return fWalker->GetNextDirents(buffer, length, count); 1645 } 1646 1647 1648 status_t 1649 ConditionalAllAppsIterator::Rewind() 1650 { 1651 if (!Iterate()) 1652 return B_OK; 1653 1654 Instantiate(); 1655 return fWalker->Rewind(); 1656 } 1657 1658 1659 int32 1660 ConditionalAllAppsIterator::CountEntries() 1661 { 1662 if (!Iterate()) 1663 return 0; 1664 1665 Instantiate(); 1666 return fWalker->CountEntries(); 1667 } 1668 1669 1670 bool 1671 ConditionalAllAppsIterator::Iterate() const 1672 { 1673 return fParent->ShowAllApplications(); 1674 } 1675 1676