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