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