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