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