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