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