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