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