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