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