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