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