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