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