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