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