1 /* 2 * Copyright (c) 1998-2007 Matthijs Hollemans 3 * All rights reserved. Distributed under the terms of the MIT License. 4 */ 5 #include "GrepWindow.h" 6 7 #include <ctype.h> 8 #include <errno.h> 9 #include <new> 10 #include <stdio.h> 11 #include <stdlib.h> 12 #include <string.h> 13 #include <unistd.h> 14 15 #include <Application.h> 16 #include <AppFileInfo.h> 17 #include <Alert.h> 18 #include <Clipboard.h> 19 #include <LayoutBuilder.h> 20 #include <MessageRunner.h> 21 #include <Path.h> 22 #include <PathMonitor.h> 23 #include <Roster.h> 24 #include <SpaceLayoutItem.h> 25 #include <String.h> 26 #include <UTF8.h> 27 28 #include "ChangesIterator.h" 29 #include "GlobalDefs.h" 30 #include "Grepper.h" 31 #include "InitialIterator.h" 32 33 #undef B_TRANSLATION_CONTEXT 34 #define B_TRANSLATION_CONTEXT "GrepWindow" 35 36 37 using std::nothrow; 38 39 static const bigtime_t kChangesPulseInterval = 150000; 40 41 #define TRACE_NODE_MONITORING 42 #ifdef TRACE_NODE_MONITORING 43 # define TRACE_NM(x...) printf(x) 44 #else 45 # define TRACE_NM(x...) 46 #endif 47 48 //#define TRACE_FUNCTIONS 49 #ifdef TRACE_FUNCTIONS 50 class FunctionTracer { 51 public: 52 FunctionTracer(const char* functionName) 53 : fName(functionName) 54 { 55 printf("%s - enter\n", fName.String()); 56 } 57 ~FunctionTracer() 58 { 59 printf("%s - exit\n", fName.String()); 60 } 61 private: 62 BString fName; 63 }; 64 # define CALLED() FunctionTracer functionTracer(__PRETTY_FUNCTION__) 65 #else 66 # define CALLED() 67 #endif // TRACE_FUNCTIONS 68 69 70 GrepWindow::GrepWindow(BMessage* message) 71 : BWindow(BRect(0, 0, 525, 430), NULL, B_DOCUMENT_WINDOW, 72 B_AUTO_UPDATE_SIZE_LIMITS), 73 fSearchText(NULL), 74 fSearchResults(NULL), 75 fMenuBar(NULL), 76 fFileMenu(NULL), 77 fNew(NULL), 78 fOpen(NULL), 79 fClose(NULL), 80 fQuit(NULL), 81 fActionMenu(NULL), 82 fSelectAll(NULL), 83 fSearch(NULL), 84 fTrimSelection(NULL), 85 fCopyText(NULL), 86 fSelectInTracker(NULL), 87 fOpenSelection(NULL), 88 fPreferencesMenu(NULL), 89 fRecurseLinks(NULL), 90 fRecurseDirs(NULL), 91 fSkipDotDirs(NULL), 92 fCaseSensitive(NULL), 93 fEscapeText(NULL), 94 fTextOnly(NULL), 95 fInvokePe(NULL), 96 fHistoryMenu(NULL), 97 fEncodingMenu(NULL), 98 fUTF8(NULL), 99 fShiftJIS(NULL), 100 fEUC(NULL), 101 fJIS(NULL), 102 103 fShowLinesCheckbox(NULL), 104 fButton(NULL), 105 106 fGrepper(NULL), 107 fOldPattern(""), 108 fModel(new (nothrow) Model()), 109 fLastNodeMonitorEvent(system_time()), 110 fChangesIterator(NULL), 111 fChangesPulse(NULL), 112 113 fFilePanel(NULL) 114 { 115 if (fModel == NULL) 116 return; 117 118 entry_ref directory; 119 _InitRefsReceived(&directory, message); 120 121 fModel->fDirectory = directory; 122 fModel->fSelectedFiles = *message; 123 124 _SetWindowTitle(); 125 _CreateMenus(); 126 _CreateViews(); 127 _LayoutViews(); 128 _LoadPrefs(); 129 _TileIfMultipleWindows(); 130 131 fSearchBoxWidth = fSearchText->Bounds().Width(); 132 Show(); 133 } 134 135 136 GrepWindow::~GrepWindow() 137 { 138 delete fGrepper; 139 delete fModel; 140 } 141 142 143 void GrepWindow::FrameResized(float width, float height) 144 { 145 BWindow::FrameResized(width, height); 146 fModel->fFrame = Frame(); 147 _SavePrefs(); 148 } 149 150 151 void GrepWindow::FrameMoved(BPoint origin) 152 { 153 BWindow::FrameMoved(origin); 154 fModel->fFrame = Frame(); 155 _SavePrefs(); 156 } 157 158 159 void GrepWindow::MenusBeginning() 160 { 161 fModel->FillHistoryMenu(fHistoryMenu); 162 BWindow::MenusBeginning(); 163 } 164 165 166 void GrepWindow::MenusEnded() 167 { 168 for (int32 t = fHistoryMenu->CountItems(); t > 0; --t) 169 delete fHistoryMenu->RemoveItem(t - 1); 170 171 BWindow::MenusEnded(); 172 } 173 174 175 void GrepWindow::MessageReceived(BMessage* message) 176 { 177 switch (message->what) { 178 case MSG_NEW_WINDOW: 179 _OnNewWindow(); 180 break; 181 182 case B_SIMPLE_DATA: 183 _OnFileDrop(message); 184 break; 185 186 case MSG_OPEN_PANEL: 187 _OnOpenPanel(); 188 break; 189 190 case MSG_REFS_RECEIVED: 191 _OnRefsReceived(message); 192 break; 193 194 case B_CANCEL: 195 _OnOpenPanelCancel(); 196 break; 197 198 case MSG_RECURSE_LINKS: 199 _OnRecurseLinks(); 200 break; 201 202 case MSG_RECURSE_DIRS: 203 _OnRecurseDirs(); 204 break; 205 206 case MSG_SKIP_DOT_DIRS: 207 _OnSkipDotDirs(); 208 break; 209 210 case MSG_CASE_SENSITIVE: 211 _OnCaseSensitive(); 212 break; 213 214 case MSG_ESCAPE_TEXT: 215 _OnEscapeText(); 216 break; 217 218 case MSG_TEXT_ONLY: 219 _OnTextOnly(); 220 break; 221 222 case MSG_INVOKE_PE: 223 _OnInvokePe(); 224 break; 225 226 case MSG_SEARCH_TEXT: 227 _OnSearchText(); 228 break; 229 230 case MSG_SELECT_HISTORY: 231 _OnHistoryItem(message); 232 break; 233 234 case MSG_START_CANCEL: 235 _OnStartCancel(); 236 break; 237 238 case MSG_SEARCH_FINISHED: 239 _OnSearchFinished(); 240 break; 241 242 case MSG_START_NODE_MONITORING: 243 _StartNodeMonitoring(); 244 break; 245 246 case B_PATH_MONITOR: 247 _OnNodeMonitorEvent(message); 248 break; 249 250 case MSG_NODE_MONITOR_PULSE: 251 _OnNodeMonitorPulse(); 252 break; 253 254 case MSG_REPORT_FILE_NAME: 255 _OnReportFileName(message); 256 break; 257 258 case MSG_REPORT_RESULT: 259 _OnReportResult(message); 260 break; 261 262 case MSG_REPORT_ERROR: 263 _OnReportError(message); 264 break; 265 266 case MSG_SELECT_ALL: 267 _OnSelectAll(message); 268 break; 269 270 case MSG_TRIM_SELECTION: 271 _OnTrimSelection(); 272 break; 273 274 case MSG_COPY_TEXT: 275 _OnCopyText(); 276 break; 277 278 case MSG_SELECT_IN_TRACKER: 279 _OnSelectInTracker(); 280 break; 281 282 case MSG_CHECKBOX_SHOW_LINES: 283 _OnCheckboxShowLines(); 284 break; 285 286 case MSG_OPEN_SELECTION: 287 // fall through 288 case MSG_INVOKE_ITEM: 289 _OnInvokeItem(); 290 break; 291 292 case MSG_QUIT_NOW: 293 _OnQuitNow(); 294 break; 295 296 case 'utf8': 297 fModel->fEncoding = 0; 298 break; 299 300 case B_SJIS_CONVERSION: 301 fModel->fEncoding = B_SJIS_CONVERSION; 302 break; 303 304 case B_EUC_CONVERSION: 305 fModel->fEncoding = B_EUC_CONVERSION; 306 break; 307 308 case B_JIS_CONVERSION: 309 fModel->fEncoding = B_JIS_CONVERSION; 310 break; 311 312 default: 313 BWindow::MessageReceived(message); 314 break; 315 } 316 } 317 318 319 void 320 GrepWindow::Quit() 321 { 322 CALLED(); 323 324 _StopNodeMonitoring(); 325 _SavePrefs(); 326 327 // TODO: stippi: Looks like this could be done 328 // by maintaining a counter in GrepApp with the number of open 329 // grep windows... and just quit when it goes zero 330 if (be_app->Lock()) { 331 be_app->PostMessage(MSG_TRY_QUIT); 332 be_app->Unlock(); 333 BWindow::Quit(); 334 } 335 } 336 337 338 // #pragma mark - 339 340 341 void 342 GrepWindow::_InitRefsReceived(entry_ref* directory, BMessage* message) 343 { 344 // HACK-HACK-HACK: 345 // If the user selected a single folder and invoked TextSearch on it, 346 // but recurse directories is switched off, TextSearch would do nothing. 347 // In that special case, we'd like it to recurse into that folder (but 348 // not go any deeper after that). 349 350 type_code code; 351 int32 count; 352 message->GetInfo("refs", &code, &count); 353 354 if (count == 0) { 355 if (message->FindRef("dir_ref", 0, directory) == B_OK) 356 message->MakeEmpty(); 357 } 358 359 if (count == 1) { 360 entry_ref ref; 361 if (message->FindRef("refs", 0, &ref) == B_OK) { 362 BEntry entry(&ref, true); 363 if (entry.IsDirectory()) { 364 // ok, special case, we use this folder as base directory 365 // and pretend nothing had been selected: 366 *directory = ref; 367 message->MakeEmpty(); 368 } 369 } 370 } 371 } 372 373 374 void 375 GrepWindow::_SetWindowTitle() 376 { 377 BEntry entry(&fModel->fDirectory, true); 378 BString title; 379 if (entry.InitCheck() == B_OK) { 380 BPath path; 381 if (entry.GetPath(&path) == B_OK) { 382 if (fOldPattern.Length()) { 383 title = B_TRANSLATE("%appname% : %path% : %searchtext%"); 384 title.ReplaceAll("%searchtext%", fOldPattern.String()); 385 } else 386 title = B_TRANSLATE("%appname% : %path%"); 387 388 title.ReplaceAll("%appname%", B_TRANSLATE(APP_NAME)); 389 title.ReplaceAll("%path%", path.Path()); 390 } 391 } 392 393 if (!title.Length()) 394 title = B_TRANSLATE(APP_NAME); 395 396 SetTitle(title.String()); 397 } 398 399 400 void 401 GrepWindow::_CreateMenus() 402 { 403 fMenuBar = new BMenuBar("menubar"); 404 405 fFileMenu = new BMenu(B_TRANSLATE("File")); 406 fActionMenu = new BMenu(B_TRANSLATE("Actions")); 407 fPreferencesMenu = new BMenu(B_TRANSLATE("Settings")); 408 fHistoryMenu = new BMenu(B_TRANSLATE("History")); 409 fEncodingMenu = new BMenu(B_TRANSLATE("Encoding")); 410 411 fNew = new BMenuItem( 412 B_TRANSLATE("New window"), new BMessage(MSG_NEW_WINDOW), 'N'); 413 414 fOpen = new BMenuItem( 415 B_TRANSLATE("Set target" B_UTF8_ELLIPSIS), new BMessage(MSG_OPEN_PANEL), 'F'); 416 417 fClose = new BMenuItem( 418 B_TRANSLATE("Close"), new BMessage(B_QUIT_REQUESTED), 'W'); 419 420 fQuit = new BMenuItem( 421 B_TRANSLATE("Quit"), new BMessage(MSG_QUIT_NOW), 'Q'); 422 423 fSearch = new BMenuItem( 424 B_TRANSLATE("Search"), new BMessage(MSG_START_CANCEL), 'S'); 425 426 fSelectAll = new BMenuItem( 427 B_TRANSLATE("Select all"), new BMessage(MSG_SELECT_ALL), 'A'); 428 429 fTrimSelection = new BMenuItem( 430 B_TRANSLATE("Trim to selection"), new BMessage(MSG_TRIM_SELECTION), 'T'); 431 432 fOpenSelection = new BMenuItem( 433 B_TRANSLATE("Open selection"), new BMessage(MSG_OPEN_SELECTION), 'O'); 434 435 fSelectInTracker = new BMenuItem( 436 B_TRANSLATE("Show files in Tracker"), 437 new BMessage(MSG_SELECT_IN_TRACKER), 'K'); 438 439 fCopyText = new BMenuItem( 440 B_TRANSLATE("Copy text to clipboard"), new BMessage(MSG_COPY_TEXT), 'B'); 441 442 fRecurseLinks = new BMenuItem( 443 B_TRANSLATE("Follow symbolic links"), new BMessage(MSG_RECURSE_LINKS)); 444 445 fRecurseDirs = new BMenuItem( 446 B_TRANSLATE("Look in sub-folders"), new BMessage(MSG_RECURSE_DIRS)); 447 448 fSkipDotDirs = new BMenuItem( 449 B_TRANSLATE("Skip folders starting with a dot"), 450 new BMessage(MSG_SKIP_DOT_DIRS)); 451 452 fCaseSensitive = new BMenuItem( 453 B_TRANSLATE("Case-sensitive"), new BMessage(MSG_CASE_SENSITIVE)); 454 455 fEscapeText = new BMenuItem( 456 B_TRANSLATE("Escape search text"), new BMessage(MSG_ESCAPE_TEXT)); 457 458 fTextOnly = new BMenuItem( 459 B_TRANSLATE("Text files only"), new BMessage(MSG_TEXT_ONLY)); 460 461 fInvokePe = new BMenuItem( 462 B_TRANSLATE("Open files in Pe"), new BMessage(MSG_INVOKE_PE)); 463 464 fUTF8 = new BMenuItem("UTF8", new BMessage('utf8')); 465 fShiftJIS = new BMenuItem("ShiftJIS", new BMessage(B_SJIS_CONVERSION)); 466 fEUC = new BMenuItem("EUC", new BMessage(B_EUC_CONVERSION)); 467 fJIS = new BMenuItem("JIS", new BMessage(B_JIS_CONVERSION)); 468 469 fFileMenu->AddItem(fNew); 470 fFileMenu->AddSeparatorItem(); 471 fFileMenu->AddItem(fOpen); 472 fFileMenu->AddItem(fClose); 473 fFileMenu->AddSeparatorItem(); 474 fFileMenu->AddItem(fQuit); 475 476 fActionMenu->AddItem(fSearch); 477 fActionMenu->AddSeparatorItem(); 478 fActionMenu->AddItem(fSelectAll); 479 fActionMenu->AddItem(fTrimSelection); 480 fActionMenu->AddSeparatorItem(); 481 fActionMenu->AddItem(fOpenSelection); 482 fActionMenu->AddItem(fSelectInTracker); 483 fActionMenu->AddItem(fCopyText); 484 485 fPreferencesMenu->AddItem(fRecurseLinks); 486 fPreferencesMenu->AddItem(fRecurseDirs); 487 fPreferencesMenu->AddItem(fSkipDotDirs); 488 fPreferencesMenu->AddItem(fCaseSensitive); 489 fPreferencesMenu->AddItem(fEscapeText); 490 fPreferencesMenu->AddItem(fTextOnly); 491 fPreferencesMenu->AddItem(fInvokePe); 492 493 fEncodingMenu->AddItem(fUTF8); 494 fEncodingMenu->AddItem(fShiftJIS); 495 fEncodingMenu->AddItem(fEUC); 496 fEncodingMenu->AddItem(fJIS); 497 498 // fEncodingMenu->SetLabelFromMarked(true); 499 // Do we really want this ? 500 fEncodingMenu->SetRadioMode(true); 501 fEncodingMenu->ItemAt(0)->SetMarked(true); 502 503 fMenuBar->AddItem(fFileMenu); 504 fMenuBar->AddItem(fActionMenu); 505 fMenuBar->AddItem(fPreferencesMenu); 506 fMenuBar->AddItem(fHistoryMenu); 507 fMenuBar->AddItem(fEncodingMenu); 508 509 fSearch->SetEnabled(false); 510 } 511 512 513 void 514 GrepWindow::_CreateViews() 515 { 516 // The search pattern entry field does not send a message when 517 // <Enter> is pressed, because the "Search/Cancel" button already 518 // does this and we don't want to send the same message twice. 519 520 fSearchText = new BTextControl( 521 "SearchText", NULL, NULL, NULL, 522 B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE | B_NAVIGABLE); 523 524 fSearchText->TextView()->SetMaxBytes(1000); 525 fSearchText->SetModificationMessage(new BMessage(MSG_SEARCH_TEXT)); 526 527 fButton = new BButton( 528 "Button", B_TRANSLATE("Search"), 529 new BMessage(MSG_START_CANCEL)); 530 fButton->MakeDefault(true); 531 fButton->SetEnabled(false); 532 533 fShowLinesCheckbox = new BCheckBox( 534 "ShowLines", B_TRANSLATE("Show lines"), 535 new BMessage(MSG_CHECKBOX_SHOW_LINES)); 536 fShowLinesCheckbox->SetValue(B_CONTROL_ON); 537 538 fSearchResults = new GrepListView(); 539 fSearchResults->SetInvocationMessage(new BMessage(MSG_INVOKE_ITEM)); 540 } 541 542 543 void 544 GrepWindow::_LayoutViews() 545 { 546 BScrollView* scroller = new BScrollView( 547 "ScrollSearchResults", fSearchResults, 548 B_FULL_UPDATE_ON_RESIZE, true, true); 549 550 BLayoutBuilder::Group<>(this, B_VERTICAL, 0) 551 .SetInsets(0, 0, -1, -1) 552 .Add(fMenuBar) 553 .AddGrid(B_USE_HALF_ITEM_INSETS) 554 .SetInsets(B_USE_WINDOW_SPACING, B_USE_WINDOW_SPACING, 555 B_USE_WINDOW_SPACING, B_USE_DEFAULT_SPACING) 556 .Add(fSearchText, 0, 0, 3) 557 .Add(fShowLinesCheckbox, 0, 1) 558 .Add(BSpaceLayoutItem::CreateGlue(), 1, 1) 559 .Add(fButton, 2, 1) 560 .End() 561 .AddGroup(B_VERTICAL, 0) 562 .SetInsets(-2, 0, -1, -1) 563 .Add(scroller) 564 .End() 565 .End(); 566 567 fSearchText->MakeFocus(true); 568 569 SetKeyMenuBar(fMenuBar); 570 } 571 572 573 void 574 GrepWindow::_TileIfMultipleWindows() 575 { 576 if (be_app->Lock()) { 577 int32 windowCount = be_app->CountWindows(); 578 be_app->Unlock(); 579 580 if (windowCount > 1) 581 MoveBy(20, 20); 582 } 583 584 BScreen screen(this); 585 BRect screenFrame = screen.Frame(); 586 BRect windowFrame = Frame(); 587 588 if (windowFrame.left > screenFrame.right 589 || windowFrame.top > screenFrame.bottom 590 || windowFrame.right < screenFrame.left 591 || windowFrame.bottom < screenFrame.top) 592 MoveTo(50, 50); 593 } 594 595 596 // #pragma mark - 597 598 599 void 600 GrepWindow::_LoadPrefs() 601 { 602 Lock(); 603 604 fModel->LoadPrefs(); 605 606 fRecurseDirs->SetMarked(fModel->fRecurseDirs); 607 fRecurseLinks->SetMarked(fModel->fRecurseLinks); 608 fSkipDotDirs->SetMarked(fModel->fSkipDotDirs); 609 fCaseSensitive->SetMarked(fModel->fCaseSensitive); 610 fEscapeText->SetMarked(fModel->fEscapeText); 611 fTextOnly->SetMarked(fModel->fTextOnly); 612 fInvokePe->SetMarked(fModel->fInvokePe); 613 614 switch (fModel->fEncoding) { 615 case 0: 616 fUTF8->SetMarked(true); 617 break; 618 case B_SJIS_CONVERSION: 619 fShiftJIS->SetMarked(true); 620 break; 621 case B_EUC_CONVERSION: 622 fEUC->SetMarked(true); 623 break; 624 case B_JIS_CONVERSION: 625 fJIS->SetMarked(true); 626 break; 627 default: 628 printf("Woops. Bad fModel->fEncoding value.\n"); 629 break; 630 } 631 632 MoveTo(fModel->fFrame.left, fModel->fFrame.top); 633 ResizeTo(fModel->fFrame.Width(), fModel->fFrame.Height()); 634 635 Unlock(); 636 } 637 638 639 void 640 GrepWindow::_SavePrefs() 641 { 642 fModel->SavePrefs(); 643 } 644 645 646 void 647 GrepWindow::_StartNodeMonitoring() 648 { 649 CALLED(); 650 651 _StopNodeMonitoring(); 652 653 BMessenger messenger(this); 654 uint32 fileFlags = B_WATCH_NAME | B_WATCH_STAT | B_WATCH_ATTR; 655 656 657 // watch the top level folder only, rest should be done through filtering 658 // the node monitor notifications 659 BPath path(&fModel->fDirectory); 660 if (path.InitCheck() == B_OK) { 661 TRACE_NM("start monitoring root folder: %s\n", path.Path()); 662 BPrivate::BPathMonitor::StartWatching(path.Path(), 663 fileFlags | B_WATCH_RECURSIVELY | B_WATCH_FILES_ONLY, messenger); 664 } 665 666 if (fChangesPulse == NULL) { 667 BMessage message(MSG_NODE_MONITOR_PULSE); 668 fChangesPulse = new BMessageRunner(BMessenger(this), &message, 669 kChangesPulseInterval); 670 } 671 } 672 673 674 void 675 GrepWindow::_StopNodeMonitoring() 676 { 677 if (fChangesPulse == NULL) 678 return; 679 680 CALLED(); 681 682 BPrivate::BPathMonitor::StopWatching(BMessenger(this)); 683 delete fChangesIterator; 684 fChangesIterator = NULL; 685 delete fChangesPulse; 686 fChangesPulse = NULL; 687 } 688 689 690 // #pragma mark - events 691 692 693 void 694 GrepWindow::_OnStartCancel() 695 { 696 CALLED(); 697 698 _StopNodeMonitoring(); 699 700 if (fModel->fState == STATE_IDLE) { 701 fSearchResults->MakeEmpty(); 702 703 if (fSearchText->TextView()->TextLength() == 0) 704 return; 705 706 fModel->fState = STATE_SEARCH; 707 708 fModel->AddToHistory(fSearchText->Text()); 709 710 // From now on, we don't want to be notified when the 711 // search pattern changes, because the control will be 712 // displaying the names of the files we are grepping. 713 714 fSearchText->SetModificationMessage(NULL); 715 716 fFileMenu->SetEnabled(false); 717 fActionMenu->SetEnabled(false); 718 fPreferencesMenu->SetEnabled(false); 719 fHistoryMenu->SetEnabled(false); 720 fEncodingMenu->SetEnabled(false); 721 722 fSearchText->SetEnabled(false); 723 724 fButton->MakeFocus(true); 725 fButton->SetLabel(B_TRANSLATE("Cancel")); 726 727 fSearch->SetEnabled(false); 728 729 // We need to remember the search pattern, because during 730 // the grepping, the text control's text will be replaced 731 // by the name of the file that's currently being grepped. 732 // When the grepping finishes, we need to restore the old 733 // search pattern. 734 735 fOldPattern = fSearchText->Text(); 736 737 _SetWindowTitle(); 738 739 FileIterator* iterator = new (nothrow) InitialIterator(fModel); 740 fGrepper = new (nothrow) Grepper(fOldPattern.String(), fModel, 741 this, iterator); 742 if (fGrepper != NULL && fGrepper->IsValid()) 743 fGrepper->Start(); 744 else { 745 // roll back in case of problems 746 if (fGrepper == NULL) 747 delete iterator; 748 else { 749 // Grepper owns iterator 750 delete fGrepper; 751 fGrepper = NULL; 752 } 753 fModel->fState = STATE_IDLE; 754 // TODO: better notification to user 755 fprintf(stderr, "Out of memory.\n"); 756 } 757 } else if (fModel->fState == STATE_SEARCH) { 758 fModel->fState = STATE_CANCEL; 759 fGrepper->Cancel(); 760 } 761 } 762 763 764 void 765 GrepWindow::_OnSearchFinished() 766 { 767 fModel->fState = STATE_IDLE; 768 769 delete fGrepper; 770 fGrepper = NULL; 771 772 fFileMenu->SetEnabled(true); 773 fActionMenu->SetEnabled(true); 774 fPreferencesMenu->SetEnabled(true); 775 fHistoryMenu->SetEnabled(true); 776 fEncodingMenu->SetEnabled(true); 777 778 fButton->SetLabel(B_TRANSLATE("Search")); 779 780 fButton->SetEnabled(true); 781 fSearch->SetEnabled(true); 782 783 fSearchText->SetEnabled(true); 784 fSearchText->MakeFocus(true); 785 fSearchText->SetText(fOldPattern.String()); 786 fSearchText->TextView()->SelectAll(); 787 fSearchText->SetModificationMessage(new BMessage(MSG_SEARCH_TEXT)); 788 789 PostMessage(MSG_START_NODE_MONITORING); 790 } 791 792 793 void 794 GrepWindow::_OnNodeMonitorEvent(BMessage* message) 795 { 796 int32 opCode; 797 if (message->FindInt32("opcode", &opCode) != B_OK) 798 return; 799 800 if (fChangesIterator == NULL) { 801 fChangesIterator = new (nothrow) ChangesIterator(fModel); 802 if (fChangesIterator == NULL || !fChangesIterator->IsValid()) { 803 delete fChangesIterator; 804 fChangesIterator = NULL; 805 } 806 } 807 808 switch (opCode) { 809 case B_ENTRY_CREATED: 810 case B_ENTRY_REMOVED: 811 { 812 TRACE_NM("%s\n", opCode == B_ENTRY_CREATED ? "B_ENTRY_CREATED" 813 : "B_ENTRY_REMOVED"); 814 BString path; 815 if (message->FindString("path", &path) == B_OK) { 816 if (opCode == B_ENTRY_CREATED) 817 fChangesIterator->EntryAdded(path.String()); 818 else { 819 // in order to remove temporary files 820 fChangesIterator->EntryRemoved(path.String()); 821 // remove from the list view already 822 BEntry entry(path.String()); 823 entry_ref ref; 824 if (entry.GetRef(&ref) == B_OK) 825 fSearchResults->RemoveResults(ref, true); 826 } 827 } else { 828 #ifdef TRACE_NODE_MONITORING 829 printf("incompatible message:\n"); 830 message->PrintToStream(); 831 #endif 832 } 833 TRACE_NM("path: %s\n", path.String()); 834 break; 835 } 836 case B_ENTRY_MOVED: 837 { 838 TRACE_NM("B_ENTRY_MOVED\n"); 839 840 BString path; 841 if (message->FindString("path", &path) != B_OK) { 842 #ifdef TRACE_NODE_MONITORING 843 printf("incompatible message:\n"); 844 message->PrintToStream(); 845 #endif 846 break; 847 } 848 849 bool added; 850 if (message->FindBool("added", &added) != B_OK) 851 added = false; 852 bool removed; 853 if (message->FindBool("removed", &removed) != B_OK) 854 removed = false; 855 856 if (added) { 857 // new files 858 } else if (removed) { 859 // remove files 860 } else { 861 // files changed location, but are still within the search 862 // path! 863 BEntry entry(path.String()); 864 entry_ref ref; 865 if (entry.GetRef(&ref) == B_OK) { 866 int32 index; 867 ResultItem* item = fSearchResults->FindItem(ref, &index); 868 item->SetText(path.String()); 869 // take care of invalidation, the index is currently 870 // the full list index, but needs to be the visible 871 // items index for this 872 index = fSearchResults->IndexOf(item); 873 fSearchResults->InvalidateItem(index); 874 } 875 } 876 break; 877 } 878 case B_STAT_CHANGED: 879 case B_ATTR_CHANGED: 880 { 881 TRACE_NM("%s\n", opCode == B_STAT_CHANGED ? "B_STAT_CHANGED" 882 : "B_ATTR_CHANGED"); 883 // For directly watched files, the path will include the 884 // name. When the event occurs for a file in a watched directory, 885 // the message will have an extra name field for the respective 886 // file. 887 BString path; 888 if (message->FindString("path", &path) == B_OK) { 889 fChangesIterator->EntryChanged(path.String()); 890 } else { 891 #ifdef TRACE_NODE_MONITORING 892 printf("incompatible message:\n"); 893 message->PrintToStream(); 894 #endif 895 } 896 TRACE_NM("path: %s\n", path.String()); 897 // message->PrintToStream(); 898 break; 899 } 900 901 default: 902 TRACE_NM("unkown op code\n"); 903 break; 904 } 905 906 fLastNodeMonitorEvent = system_time(); 907 } 908 909 910 void 911 GrepWindow::_OnNodeMonitorPulse() 912 { 913 if (fChangesIterator == NULL || fChangesIterator->IsEmpty()) 914 return; 915 916 if (system_time() - fLastNodeMonitorEvent < kChangesPulseInterval) { 917 // wait for things to settle down before running the search for changes 918 return; 919 } 920 921 if (fModel->fState != STATE_IDLE) { 922 // An update or search is still in progress. New node monitor messages 923 // may arrive while an update is still running. They should not arrive 924 // during a regular search, but we want to be prepared for that anyways 925 // and check != STATE_IDLE. 926 return; 927 } 928 929 fOldPattern = fSearchText->Text(); 930 931 #ifdef TRACE_NODE_MONITORING 932 fChangesIterator->PrintToStream(); 933 #endif 934 935 fGrepper = new (nothrow) Grepper(fOldPattern.String(), fModel, 936 this, fChangesIterator); 937 if (fGrepper != NULL && fGrepper->IsValid()) { 938 fGrepper->Start(); 939 fChangesIterator = NULL; 940 fModel->fState = STATE_UPDATE; 941 } else { 942 // roll back in case of problems 943 if (fGrepper == NULL) 944 delete fChangesIterator; 945 else { 946 // Grepper owns iterator 947 delete fGrepper; 948 fGrepper = NULL; 949 } 950 fprintf(stderr, "Out of memory.\n"); 951 } 952 } 953 954 955 void 956 GrepWindow::_OnReportFileName(BMessage* message) 957 { 958 if (fModel->fState != STATE_UPDATE) { 959 BString name = message->FindString("filename"); 960 fSearchText->TruncateString(&name, B_TRUNCATE_MIDDLE, 961 fSearchBoxWidth - 10); 962 963 fSearchText->SetText(name); 964 } 965 } 966 967 968 void 969 GrepWindow::_OnReportResult(BMessage* message) 970 { 971 CALLED(); 972 973 entry_ref ref; 974 if (message->FindRef("ref", &ref) != B_OK) 975 return; 976 977 type_code type; 978 int32 count; 979 message->GetInfo("text", &type, &count); 980 981 BStringItem* item = NULL; 982 if (fModel->fState == STATE_UPDATE) { 983 // During updates because of node monitor events, negatives are 984 // also reported (count == 0). 985 item = fSearchResults->RemoveResults(ref, count == 0); 986 } 987 988 if (count == 0) 989 return; 990 991 if (item == NULL) { 992 item = new ResultItem(ref); 993 fSearchResults->AddItem(item); 994 item->SetExpanded(fShowLinesCheckbox->Value() == 1); 995 } 996 997 const char* buf; 998 while (message->FindString("text", --count, &buf) == B_OK) { 999 uchar* temp = (uchar*)strdup(buf); 1000 uchar* ptr = temp; 1001 1002 while (true) { 1003 // replace all non-printable characters by spaces 1004 uchar c = *ptr; 1005 1006 if (c == '\0') 1007 break; 1008 1009 if (!(c & 0x80) && iscntrl(c)) 1010 *ptr = ' '; 1011 1012 ++ptr; 1013 } 1014 1015 fSearchResults->AddUnder(new BStringItem((const char*)temp), item); 1016 1017 free(temp); 1018 } 1019 } 1020 1021 1022 void 1023 GrepWindow::_OnReportError(BMessage* message) 1024 { 1025 const char* buf; 1026 if (message->FindString("error", &buf) == B_OK) 1027 fSearchResults->AddItem(new BStringItem(buf)); 1028 } 1029 1030 1031 void 1032 GrepWindow::_OnRecurseLinks() 1033 { 1034 fModel->fRecurseLinks = !fModel->fRecurseLinks; 1035 fRecurseLinks->SetMarked(fModel->fRecurseLinks); 1036 _ModelChanged(); 1037 } 1038 1039 1040 void 1041 GrepWindow::_OnRecurseDirs() 1042 { 1043 fModel->fRecurseDirs = !fModel->fRecurseDirs; 1044 fRecurseDirs->SetMarked(fModel->fRecurseDirs); 1045 _ModelChanged(); 1046 } 1047 1048 1049 void 1050 GrepWindow::_OnSkipDotDirs() 1051 { 1052 fModel->fSkipDotDirs = !fModel->fSkipDotDirs; 1053 fSkipDotDirs->SetMarked(fModel->fSkipDotDirs); 1054 _ModelChanged(); 1055 } 1056 1057 1058 void 1059 GrepWindow::_OnEscapeText() 1060 { 1061 fModel->fEscapeText = !fModel->fEscapeText; 1062 fEscapeText->SetMarked(fModel->fEscapeText); 1063 _ModelChanged(); 1064 } 1065 1066 1067 void 1068 GrepWindow::_OnCaseSensitive() 1069 { 1070 fModel->fCaseSensitive = !fModel->fCaseSensitive; 1071 fCaseSensitive->SetMarked(fModel->fCaseSensitive); 1072 _ModelChanged(); 1073 } 1074 1075 1076 void 1077 GrepWindow::_OnTextOnly() 1078 { 1079 fModel->fTextOnly = !fModel->fTextOnly; 1080 fTextOnly->SetMarked(fModel->fTextOnly); 1081 _ModelChanged(); 1082 } 1083 1084 1085 void 1086 GrepWindow::_OnInvokePe() 1087 { 1088 fModel->fInvokePe = !fModel->fInvokePe; 1089 fInvokePe->SetMarked(fModel->fInvokePe); 1090 _SavePrefs(); 1091 } 1092 1093 1094 void 1095 GrepWindow::_OnCheckboxShowLines() 1096 { 1097 // Selection in BOutlineListView in multiple selection mode 1098 // gets weird when collapsing. I've tried all sorts of things. 1099 // It seems impossible to make it behave just right. 1100 1101 // Going from collapsed to expande mode, the superitems 1102 // keep their selection, the subitems don't (yet) have 1103 // a selection. This works as expected, AFAIK. 1104 1105 // Going from expanded to collapsed mode, I would like 1106 // for a selected subitem (line) to select its superitem, 1107 // (its file) and the subitem be unselected. 1108 1109 // I've successfully tried code patches that apply the 1110 // selection pattern that I want, but with weird effects 1111 // on subsequent manual selection. 1112 // Lines stay selected while the user tries to select 1113 // some other line. It just gets weird. 1114 1115 // It's as though listItem->Select() and Deselect() 1116 // put the items in some semi-selected state. 1117 // Or maybe I've got it all wrong. 1118 1119 // So, here's the plain basic collapse/expand. 1120 // I think it's the least bad of what's possible on BeOS R5, 1121 // but perhaps someone comes along with a patch of magic. 1122 1123 int32 numItems = fSearchResults->FullListCountItems(); 1124 for (int32 x = 0; x < numItems; ++x) { 1125 BListItem* listItem = fSearchResults->FullListItemAt(x); 1126 if (listItem->OutlineLevel() == 0) { 1127 if (fShowLinesCheckbox->Value() == 1) { 1128 if (!fSearchResults->IsExpanded(x)) 1129 fSearchResults->Expand(listItem); 1130 } else { 1131 if (fSearchResults->IsExpanded(x)) 1132 fSearchResults->Collapse(listItem); 1133 } 1134 } 1135 } 1136 1137 fSearchResults->Invalidate(); 1138 1139 _SavePrefs(); 1140 } 1141 1142 1143 void 1144 GrepWindow::_OnInvokeItem() 1145 { 1146 for (int32 selectionIndex = 0; ; selectionIndex++) { 1147 int32 itemIndex = fSearchResults->CurrentSelection(selectionIndex); 1148 BListItem* item = fSearchResults->ItemAt(itemIndex); 1149 if (item == NULL) 1150 break; 1151 1152 int32 level = item->OutlineLevel(); 1153 int32 lineNum = -1; 1154 1155 // Get the line number. 1156 // only this level has line numbers 1157 if (level == 1) { 1158 BStringItem* str = dynamic_cast<BStringItem*>(item); 1159 if (str != NULL) { 1160 lineNum = atol(str->Text()); 1161 // fortunately, atol knows when to stop the conversion 1162 } 1163 } 1164 1165 // Get the top-most item and launch its entry_ref. 1166 while (level != 0) { 1167 item = fSearchResults->Superitem(item); 1168 if (item == NULL) 1169 break; 1170 level = item->OutlineLevel(); 1171 } 1172 1173 ResultItem* entry = dynamic_cast<ResultItem*>(item); 1174 if (entry != NULL) { 1175 bool done = false; 1176 1177 if (fModel->fInvokePe) 1178 done = _OpenInPe(entry->ref, lineNum); 1179 1180 if (!done) 1181 be_roster->Launch(&entry->ref); 1182 } 1183 } 1184 } 1185 1186 1187 void 1188 GrepWindow::_OnSearchText() 1189 { 1190 CALLED(); 1191 1192 bool enabled = fSearchText->TextView()->TextLength() != 0; 1193 fButton->SetEnabled(enabled); 1194 fSearch->SetEnabled(enabled); 1195 _StopNodeMonitoring(); 1196 } 1197 1198 1199 void 1200 GrepWindow::_OnHistoryItem(BMessage* message) 1201 { 1202 const char* buf; 1203 if (message->FindString("text", &buf) == B_OK) 1204 fSearchText->SetText(buf); 1205 } 1206 1207 1208 void 1209 GrepWindow::_OnTrimSelection() 1210 { 1211 if (fSearchResults->CurrentSelection() < 0) { 1212 BString text; 1213 text << B_TRANSLATE("Please select the files you wish to keep searching."); 1214 text << "\n"; 1215 text << B_TRANSLATE("The unselected files will be removed from the list."); 1216 text << "\n"; 1217 BAlert* alert = new BAlert(NULL, text.String(), B_TRANSLATE("OK"), NULL, NULL, 1218 B_WIDTH_AS_USUAL, B_WARNING_ALERT); 1219 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 1220 alert->Go(NULL); 1221 return; 1222 } 1223 1224 BMessage message; 1225 BString path; 1226 1227 for (int32 index = 0; ; index++) { 1228 BStringItem* item = dynamic_cast<BStringItem*>( 1229 fSearchResults->ItemAt(index)); 1230 if (item == NULL) 1231 break; 1232 1233 if (!item->IsSelected() || item->OutlineLevel() != 0) 1234 continue; 1235 1236 if (path == item->Text()) 1237 continue; 1238 1239 path = item->Text(); 1240 entry_ref ref; 1241 if (get_ref_for_path(path.String(), &ref) == B_OK) 1242 message.AddRef("refs", &ref); 1243 } 1244 1245 fModel->fDirectory = entry_ref(); 1246 // invalidated on purpose 1247 1248 fModel->fSelectedFiles.MakeEmpty(); 1249 fModel->fSelectedFiles = message; 1250 1251 PostMessage(MSG_START_CANCEL); 1252 1253 _SetWindowTitle(); 1254 } 1255 1256 1257 void 1258 GrepWindow::_OnCopyText() 1259 { 1260 bool onlyCopySelection = true; 1261 1262 if (fSearchResults->CurrentSelection() < 0) 1263 onlyCopySelection = false; 1264 1265 BString buffer; 1266 1267 for (int32 index = 0; ; index++) { 1268 BStringItem* item = dynamic_cast<BStringItem*>( 1269 fSearchResults->ItemAt(index)); 1270 if (item == NULL) 1271 break; 1272 1273 if (onlyCopySelection) { 1274 if (item->IsSelected()) 1275 buffer << item->Text() << "\n"; 1276 } else 1277 buffer << item->Text() << "\n"; 1278 } 1279 1280 status_t status = B_OK; 1281 1282 BMessage* clip = NULL; 1283 1284 if (be_clipboard->Lock()) { 1285 be_clipboard->Clear(); 1286 1287 clip = be_clipboard->Data(); 1288 1289 clip->AddData("text/plain", B_MIME_TYPE, buffer.String(), 1290 buffer.Length()); 1291 1292 status = be_clipboard->Commit(); 1293 1294 if (status != B_OK) { 1295 be_clipboard->Unlock(); 1296 return; 1297 } 1298 1299 be_clipboard->Unlock(); 1300 } 1301 } 1302 1303 1304 void 1305 GrepWindow::_OnSelectInTracker() 1306 { 1307 if (fSearchResults->CurrentSelection() < 0) { 1308 BAlert* alert = new BAlert("Info", 1309 B_TRANSLATE("Please select the files you wish to have selected for you in " 1310 "Tracker."), 1311 B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT); 1312 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 1313 alert->Go(NULL); 1314 return; 1315 } 1316 1317 BMessage message; 1318 BString filePath; 1319 BPath folderPath; 1320 BList folderList; 1321 BString lastFolderAddedToList; 1322 1323 for (int32 index = 0; ; index++) { 1324 BStringItem* item = dynamic_cast<BStringItem*>( 1325 fSearchResults->ItemAt(index)); 1326 if (item == NULL) 1327 break; 1328 1329 // only open selected and top level (file) items 1330 if (!item->IsSelected() || item->OutlineLevel() > 0) 1331 continue; 1332 1333 // check if this was previously opened 1334 if (filePath == item->Text()) 1335 continue; 1336 1337 filePath = item->Text(); 1338 entry_ref file_ref; 1339 if (get_ref_for_path(filePath.String(), &file_ref) != B_OK) 1340 continue; 1341 1342 message.AddRef("refs", &file_ref); 1343 1344 // add parent folder to list of folders to open 1345 folderPath.SetTo(filePath.String()); 1346 if (folderPath.GetParent(&folderPath) == B_OK) { 1347 BPath* path = new BPath(folderPath); 1348 if (path->Path() != lastFolderAddedToList) { 1349 // catches some duplicates 1350 folderList.AddItem(path); 1351 lastFolderAddedToList = path->Path(); 1352 } else 1353 delete path; 1354 } 1355 } 1356 1357 _RemoveFolderListDuplicates(&folderList); 1358 _OpenFoldersInTracker(&folderList); 1359 1360 int32 aShortWhile = 100000; 1361 snooze(aShortWhile); 1362 1363 if (!_AreAllFoldersOpenInTracker(&folderList)) { 1364 for (int32 x = 0; x < 5; x++) { 1365 aShortWhile += 100000; 1366 snooze(aShortWhile); 1367 _OpenFoldersInTracker(&folderList); 1368 } 1369 } 1370 1371 if (!_AreAllFoldersOpenInTracker(&folderList)) { 1372 BString str1; 1373 str1 << B_TRANSLATE("%APP_NAME couldn't open one or more folders."); 1374 str1.ReplaceFirst("%APP_NAME",APP_NAME); 1375 BAlert* alert = new BAlert(NULL, str1.String(), B_TRANSLATE("OK"), 1376 NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT); 1377 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 1378 alert->Go(NULL); 1379 goto out; 1380 } 1381 1382 _SelectFilesInTracker(&folderList, &message); 1383 1384 out: 1385 // delete folderList contents 1386 int32 folderCount = folderList.CountItems(); 1387 for (int32 x = 0; x < folderCount; x++) 1388 delete static_cast<BPath*>(folderList.ItemAt(x)); 1389 } 1390 1391 1392 void 1393 GrepWindow::_OnQuitNow() 1394 { 1395 if (be_app->Lock()) { 1396 be_app->PostMessage(B_QUIT_REQUESTED); 1397 be_app->Unlock(); 1398 } 1399 } 1400 1401 1402 void 1403 GrepWindow::_OnFileDrop(BMessage* message) 1404 { 1405 if (fModel->fState != STATE_IDLE) 1406 return; 1407 1408 entry_ref directory; 1409 _InitRefsReceived(&directory, message); 1410 1411 fModel->fDirectory = directory; 1412 fModel->fSelectedFiles.MakeEmpty(); 1413 fModel->fSelectedFiles = *message; 1414 1415 fSearchResults->MakeEmpty(); 1416 fOldPattern = ""; 1417 1418 _SetWindowTitle(); 1419 } 1420 1421 1422 void 1423 GrepWindow::_OnRefsReceived(BMessage* message) 1424 { 1425 _OnFileDrop(message); 1426 fOldPattern = ""; 1427 // It seems a B_CANCEL always follows a B_REFS_RECEIVED 1428 // from a BFilePanel in Open mode. 1429 // 1430 // _OnOpenPanelCancel() is called on B_CANCEL. 1431 // That's where saving the current dir of the file panel occurs, for now, 1432 // and also the neccesary deletion of the file panel object. 1433 // A hidden file panel would otherwise jam the shutdown process. 1434 } 1435 1436 1437 void 1438 GrepWindow::_OnOpenPanel() 1439 { 1440 if (fFilePanel != NULL) 1441 return; 1442 1443 entry_ref path; 1444 if (get_ref_for_path(fModel->fFilePanelPath.String(), &path) != B_OK) 1445 return; 1446 1447 BMessenger messenger(this); 1448 BMessage message(MSG_REFS_RECEIVED); 1449 fFilePanel = new BFilePanel(B_OPEN_PANEL, &messenger, &path, 1450 B_FILE_NODE | B_DIRECTORY_NODE | B_SYMLINK_NODE, true, 1451 &message, NULL, true, true); 1452 1453 fFilePanel->Show(); 1454 } 1455 1456 1457 void 1458 GrepWindow::_OnOpenPanelCancel() 1459 { 1460 entry_ref panelDirRef; 1461 fFilePanel->GetPanelDirectory(&panelDirRef); 1462 BPath path(&panelDirRef); 1463 fModel->fFilePanelPath = path.Path(); 1464 delete fFilePanel; 1465 fFilePanel = NULL; 1466 } 1467 1468 1469 void 1470 GrepWindow::_OnSelectAll(BMessage* message) 1471 { 1472 BMessenger messenger(fSearchResults); 1473 messenger.SendMessage(B_SELECT_ALL); 1474 } 1475 1476 1477 void 1478 GrepWindow::_OnNewWindow() 1479 { 1480 BMessage cloneRefs; 1481 // we don't want GrepWindow::InitRefsReceived() 1482 // to mess with the refs of the current window 1483 1484 cloneRefs = fModel->fSelectedFiles; 1485 cloneRefs.AddRef("dir_ref", &(fModel->fDirectory)); 1486 1487 new GrepWindow(&cloneRefs); 1488 } 1489 1490 1491 // #pragma mark - 1492 1493 1494 void 1495 GrepWindow::_ModelChanged() 1496 { 1497 CALLED(); 1498 1499 _StopNodeMonitoring(); 1500 _SavePrefs(); 1501 } 1502 1503 1504 bool 1505 GrepWindow::_OpenInPe(const entry_ref &ref, int32 lineNum) 1506 { 1507 BMessage message('Cmdl'); 1508 message.AddRef("refs", &ref); 1509 1510 if (lineNum != -1) 1511 message.AddInt32("line", lineNum); 1512 1513 entry_ref pe; 1514 if (be_roster->FindApp(PE_SIGNATURE, &pe) != B_OK) 1515 return false; 1516 1517 if (be_roster->IsRunning(&pe)) { 1518 BMessenger msngr(NULL, be_roster->TeamFor(&pe)); 1519 if (msngr.SendMessage(&message) != B_OK) 1520 return false; 1521 } else { 1522 if (be_roster->Launch(&pe, &message) != B_OK) 1523 return false; 1524 } 1525 1526 return true; 1527 } 1528 1529 1530 void 1531 GrepWindow::_RemoveFolderListDuplicates(BList* folderList) 1532 { 1533 if (folderList == NULL) 1534 return; 1535 1536 int32 folderCount = folderList->CountItems(); 1537 BString folderX; 1538 BString folderY; 1539 1540 for (int32 x = 0; x < folderCount; x++) { 1541 BPath* path = static_cast<BPath*>(folderList->ItemAt(x)); 1542 folderX = path->Path(); 1543 1544 for (int32 y = x + 1; y < folderCount; y++) { 1545 path = static_cast<BPath*>(folderList->ItemAt(y)); 1546 folderY = path->Path(); 1547 if (folderX == folderY) { 1548 delete static_cast<BPath*>(folderList->RemoveItem(y)); 1549 folderCount--; 1550 y--; 1551 } 1552 } 1553 } 1554 } 1555 1556 1557 status_t 1558 GrepWindow::_OpenFoldersInTracker(BList* folderList) 1559 { 1560 status_t status = B_OK; 1561 BMessage refsMsg(B_REFS_RECEIVED); 1562 1563 int32 folderCount = folderList->CountItems(); 1564 for (int32 index = 0; index < folderCount; index++) { 1565 BPath* path = static_cast<BPath*>(folderList->ItemAt(index)); 1566 1567 entry_ref folderRef; 1568 status = get_ref_for_path(path->Path(), &folderRef); 1569 if (status != B_OK) 1570 return status; 1571 1572 status = refsMsg.AddRef("refs", &folderRef); 1573 if (status != B_OK) 1574 return status; 1575 } 1576 1577 status = be_roster->Launch(TRACKER_SIGNATURE, &refsMsg); 1578 if (status != B_OK && status != B_ALREADY_RUNNING) 1579 return status; 1580 1581 return B_OK; 1582 } 1583 1584 1585 bool 1586 GrepWindow::_AreAllFoldersOpenInTracker(BList* folderList) 1587 { 1588 // Compare the folders we want open in Tracker to 1589 // the actual Tracker windows currently open. 1590 1591 // We build a list of open Tracker windows, and compare 1592 // it to the list of folders we want open in Tracker. 1593 1594 // If all folders exists in the list of Tracker windows 1595 // return true 1596 1597 status_t status = B_OK; 1598 BMessenger trackerMessenger(TRACKER_SIGNATURE); 1599 BMessage sendMessage; 1600 BMessage replyMessage; 1601 BList windowList; 1602 1603 if (!trackerMessenger.IsValid()) 1604 return false; 1605 1606 for (int32 count = 1; ; count++) { 1607 sendMessage.MakeEmpty(); 1608 replyMessage.MakeEmpty(); 1609 1610 sendMessage.what = B_GET_PROPERTY; 1611 sendMessage.AddSpecifier("Path"); 1612 sendMessage.AddSpecifier("Poses"); 1613 sendMessage.AddSpecifier("Window", count); 1614 1615 status = trackerMessenger.SendMessage(&sendMessage, &replyMessage); 1616 if (status != B_OK) 1617 return false; 1618 1619 entry_ref* trackerRef = new (nothrow) entry_ref; 1620 status = replyMessage.FindRef("result", trackerRef); 1621 if (status != B_OK || !windowList.AddItem(trackerRef)) { 1622 delete trackerRef; 1623 break; 1624 } 1625 } 1626 1627 int32 folderCount = folderList->CountItems(); 1628 int32 windowCount = windowList.CountItems(); 1629 1630 int32 found = 0; 1631 BPath* folderPath; 1632 entry_ref* windowRef; 1633 BString folderString; 1634 BString windowString; 1635 bool result = false; 1636 1637 if (folderCount > windowCount) { 1638 // at least one folder is not open in Tracker 1639 goto out; 1640 } 1641 1642 // Loop over the two lists and see if all folders exist as window 1643 for (int32 x = 0; x < folderCount; x++) { 1644 for (int32 y = 0; y < windowCount; y++) { 1645 1646 folderPath = static_cast<BPath*>(folderList->ItemAt(x)); 1647 windowRef = static_cast<entry_ref*>(windowList.ItemAt(y)); 1648 1649 if (folderPath == NULL) 1650 break; 1651 1652 if (windowRef == NULL) 1653 break; 1654 1655 folderString = folderPath->Path(); 1656 1657 BEntry entry; 1658 BPath path; 1659 1660 if (entry.SetTo(windowRef) == B_OK && path.SetTo(&entry) == B_OK) { 1661 1662 windowString = path.Path(); 1663 1664 if (folderString == windowString) { 1665 found++; 1666 break; 1667 } 1668 } 1669 } 1670 } 1671 1672 result = found == folderCount; 1673 1674 out: 1675 // delete list of window entry_refs 1676 for (int32 x = 0; x < windowCount; x++) 1677 delete static_cast<entry_ref*>(windowList.ItemAt(x)); 1678 1679 return result; 1680 } 1681 1682 1683 status_t 1684 GrepWindow::_SelectFilesInTracker(BList* folderList, BMessage* refsMessage) 1685 { 1686 // loops over Tracker windows, find each windowRef, 1687 // extract the refs that are children of windowRef, 1688 // add refs to selection-message 1689 1690 status_t status = B_OK; 1691 BMessenger trackerMessenger(TRACKER_SIGNATURE); 1692 BMessage windowSendMessage; 1693 BMessage windowReplyMessage; 1694 BMessage selectionSendMessage; 1695 BMessage selectionReplyMessage; 1696 1697 if (!trackerMessenger.IsValid()) 1698 return status; 1699 1700 // loop over Tracker windows 1701 for (int32 windowCount = 1; ; windowCount++) { 1702 1703 windowSendMessage.MakeEmpty(); 1704 windowReplyMessage.MakeEmpty(); 1705 1706 windowSendMessage.what = B_GET_PROPERTY; 1707 windowSendMessage.AddSpecifier("Path"); 1708 windowSendMessage.AddSpecifier("Poses"); 1709 windowSendMessage.AddSpecifier("Window", windowCount); 1710 1711 status = trackerMessenger.SendMessage(&windowSendMessage, 1712 &windowReplyMessage); 1713 1714 if (status != B_OK) 1715 return status; 1716 1717 entry_ref windowRef; 1718 status = windowReplyMessage.FindRef("result", &windowRef); 1719 if (status != B_OK) 1720 break; 1721 1722 int32 folderCount = folderList->CountItems(); 1723 1724 // loop over folders in folderList 1725 for (int32 x = 0; x < folderCount; x++) { 1726 BPath* folderPath = static_cast<BPath*>(folderList->ItemAt(x)); 1727 if (folderPath == NULL) 1728 break; 1729 1730 BString folderString = folderPath->Path(); 1731 1732 BEntry windowEntry; 1733 BPath windowPath; 1734 BString windowString; 1735 1736 status = windowEntry.SetTo(&windowRef); 1737 if (status != B_OK) 1738 break; 1739 1740 status = windowPath.SetTo(&windowEntry); 1741 if (status != B_OK) 1742 break; 1743 1744 windowString = windowPath.Path(); 1745 1746 // if match, loop over items in refsMessage 1747 // and add those that live in window/folder 1748 // to a selection message 1749 1750 if (windowString == folderString) { 1751 selectionSendMessage.MakeEmpty(); 1752 selectionSendMessage.what = B_SET_PROPERTY; 1753 selectionReplyMessage.MakeEmpty(); 1754 1755 // loop over refs and add to message 1756 entry_ref ref; 1757 for (int32 index = 0; ; index++) { 1758 status = refsMessage->FindRef("refs", index, &ref); 1759 if (status != B_OK) 1760 break; 1761 1762 BDirectory directory(&windowRef); 1763 BEntry entry(&ref); 1764 if (directory.Contains(&entry)) 1765 selectionSendMessage.AddRef("data", &ref); 1766 } 1767 1768 // finish selection message 1769 selectionSendMessage.AddSpecifier("Selection"); 1770 selectionSendMessage.AddSpecifier("Poses"); 1771 selectionSendMessage.AddSpecifier("Window", windowCount); 1772 1773 trackerMessenger.SendMessage(&selectionSendMessage, 1774 &selectionReplyMessage); 1775 } 1776 } 1777 } 1778 1779 return B_OK; 1780 } 1781