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 1053 if (count == 0) 1054 return; 1055 1056 if (item == NULL) { 1057 item = new ResultItem(ref); 1058 fSearchResults->AddItem(item); 1059 item->SetExpanded(fModel->fShowContents); 1060 } 1061 1062 const char* buf; 1063 while (message->FindString("text", --count, &buf) == B_OK) { 1064 uchar* temp = (uchar*)strdup(buf); 1065 uchar* ptr = temp; 1066 1067 while (true) { 1068 // replace all non-printable characters by spaces 1069 uchar c = *ptr; 1070 1071 if (c == '\0') 1072 break; 1073 1074 if (!(c & 0x80) && iscntrl(c)) 1075 *ptr = ' '; 1076 1077 ++ptr; 1078 } 1079 1080 fSearchResults->AddUnder(new BStringItem((const char*)temp), item); 1081 1082 free(temp); 1083 } 1084 } 1085 1086 1087 void 1088 GrepWindow::_OnReportError(BMessage *message) 1089 { 1090 const char* buf; 1091 if (message->FindString("error", &buf) == B_OK) 1092 fSearchResults->AddItem(new BStringItem(buf)); 1093 } 1094 1095 1096 void 1097 GrepWindow::_OnRecurseLinks() 1098 { 1099 fModel->fRecurseLinks = !fModel->fRecurseLinks; 1100 fRecurseLinks->SetMarked(fModel->fRecurseLinks); 1101 _ModelChanged(); 1102 } 1103 1104 1105 void 1106 GrepWindow::_OnRecurseDirs() 1107 { 1108 fModel->fRecurseDirs = !fModel->fRecurseDirs; 1109 fRecurseDirs->SetMarked(fModel->fRecurseDirs); 1110 _ModelChanged(); 1111 } 1112 1113 1114 void 1115 GrepWindow::_OnSkipDotDirs() 1116 { 1117 fModel->fSkipDotDirs = !fModel->fSkipDotDirs; 1118 fSkipDotDirs->SetMarked(fModel->fSkipDotDirs); 1119 _ModelChanged(); 1120 } 1121 1122 1123 void 1124 GrepWindow::_OnEscapeText() 1125 { 1126 fModel->fEscapeText = !fModel->fEscapeText; 1127 fEscapeText->SetMarked(fModel->fEscapeText); 1128 _ModelChanged(); 1129 } 1130 1131 1132 void 1133 GrepWindow::_OnCaseSensitive() 1134 { 1135 fModel->fCaseSensitive = !fModel->fCaseSensitive; 1136 fCaseSensitive->SetMarked(fModel->fCaseSensitive); 1137 _ModelChanged(); 1138 } 1139 1140 1141 void 1142 GrepWindow::_OnTextOnly() 1143 { 1144 fModel->fTextOnly = !fModel->fTextOnly; 1145 fTextOnly->SetMarked(fModel->fTextOnly); 1146 _ModelChanged(); 1147 } 1148 1149 1150 void 1151 GrepWindow::_OnInvokePe() 1152 { 1153 fModel->fInvokePe = !fModel->fInvokePe; 1154 fInvokePe->SetMarked(fModel->fInvokePe); 1155 _SavePrefs(); 1156 } 1157 1158 1159 void 1160 GrepWindow::_OnCheckboxShowLines() 1161 { 1162 // toggle checkbox and menuitem 1163 fModel->fShowContents = !fModel->fShowContents; 1164 fShowLinesMenuitem->SetMarked(!fShowLinesMenuitem->IsMarked()); 1165 1166 // Selection in BOutlineListView in multiple selection mode 1167 // gets weird when collapsing. I've tried all sorts of things. 1168 // It seems impossible to make it behave just right. 1169 1170 // Going from collapsed to expande mode, the superitems 1171 // keep their selection, the subitems don't (yet) have 1172 // a selection. This works as expected, AFAIK. 1173 1174 // Going from expanded to collapsed mode, I would like 1175 // for a selected subitem (line) to select its superitem, 1176 // (its file) and the subitem be unselected. 1177 1178 // I've successfully tried code patches that apply the 1179 // selection pattern that I want, but with weird effects 1180 // on subsequent manual selection. 1181 // Lines stay selected while the user tries to select 1182 // some other line. It just gets weird. 1183 1184 // It's as though listItem->Select() and Deselect() 1185 // put the items in some semi-selected state. 1186 // Or maybe I've got it all wrong. 1187 1188 // So, here's the plain basic collapse/expand. 1189 // I think it's the least bad of what's possible on BeOS R5, 1190 // but perhaps someone comes along with a patch of magic. 1191 1192 int32 numItems = fSearchResults->FullListCountItems(); 1193 for (int32 x = 0; x < numItems; ++x) { 1194 BListItem* listItem = fSearchResults->FullListItemAt(x); 1195 if (listItem->OutlineLevel() == 0) { 1196 if (fModel->fShowContents) { 1197 if (!fSearchResults->IsExpanded(x)) 1198 fSearchResults->Expand(listItem); 1199 } else { 1200 if (fSearchResults->IsExpanded(x)) 1201 fSearchResults->Collapse(listItem); 1202 } 1203 } 1204 } 1205 1206 fSearchResults->Invalidate(); 1207 1208 _SavePrefs(); 1209 } 1210 1211 1212 void 1213 GrepWindow::_OnMenuShowLines() 1214 { 1215 // toggle companion checkbox 1216 fShowLinesCheckbox->SetValue(!fShowLinesCheckbox->Value()); 1217 _OnCheckboxShowLines(); 1218 } 1219 1220 1221 void 1222 GrepWindow::_OnInvokeItem() 1223 { 1224 for (int32 selectionIndex = 0; ; selectionIndex++) { 1225 int32 itemIndex = fSearchResults->CurrentSelection(selectionIndex); 1226 BListItem* item = fSearchResults->ItemAt(itemIndex); 1227 if (item == NULL) 1228 break; 1229 1230 int32 level = item->OutlineLevel(); 1231 int32 lineNum = -1; 1232 1233 // Get the line number. 1234 // only this level has line numbers 1235 if (level == 1) { 1236 BStringItem *str = dynamic_cast<BStringItem*>(item); 1237 if (str != NULL) { 1238 lineNum = atol(str->Text()); 1239 // fortunately, atol knows when to stop the conversion 1240 } 1241 } 1242 1243 // Get the top-most item and launch its entry_ref. 1244 while (level != 0) { 1245 item = fSearchResults->Superitem(item); 1246 if (item == NULL) 1247 break; 1248 level = item->OutlineLevel(); 1249 } 1250 1251 ResultItem* entry = dynamic_cast<ResultItem*>(item); 1252 if (entry != NULL) { 1253 bool done = false; 1254 1255 if (fModel->fInvokePe) 1256 done = _OpenInPe(entry->ref, lineNum); 1257 1258 if (!done) 1259 be_roster->Launch(&entry->ref); 1260 } 1261 } 1262 } 1263 1264 1265 void 1266 GrepWindow::_OnSearchText() 1267 { 1268 CALLED(); 1269 1270 bool enabled = fSearchText->TextView()->TextLength() != 0; 1271 fButton->SetEnabled(enabled); 1272 fSearch->SetEnabled(enabled); 1273 _StopNodeMonitoring(); 1274 } 1275 1276 1277 void 1278 GrepWindow::_OnHistoryItem(BMessage* message) 1279 { 1280 const char* buf; 1281 if (message->FindString("text", &buf) == B_OK) 1282 fSearchText->SetText(buf); 1283 } 1284 1285 1286 void 1287 GrepWindow::_OnTrimSelection() 1288 { 1289 if (fSearchResults->CurrentSelection() < 0) { 1290 BString text; 1291 text << B_TRANSLATE("Please select the files you wish to keep searching."); 1292 text << "\n"; 1293 text << B_TRANSLATE("The unselected files will be removed from the list."); 1294 text << "\n"; 1295 BAlert* alert = new BAlert(NULL, text.String(), B_TRANSLATE("OK"), NULL, NULL, 1296 B_WIDTH_AS_USUAL, B_WARNING_ALERT); 1297 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 1298 alert->Go(NULL); 1299 return; 1300 } 1301 1302 BMessage message; 1303 BString path; 1304 1305 for (int32 index = 0; ; index++) { 1306 BStringItem* item = dynamic_cast<BStringItem*>( 1307 fSearchResults->ItemAt(index)); 1308 if (item == NULL) 1309 break; 1310 1311 if (!item->IsSelected() || item->OutlineLevel() != 0) 1312 continue; 1313 1314 if (path == item->Text()) 1315 continue; 1316 1317 path = item->Text(); 1318 entry_ref ref; 1319 if (get_ref_for_path(path.String(), &ref) == B_OK) 1320 message.AddRef("refs", &ref); 1321 } 1322 1323 fModel->fDirectory = entry_ref(); 1324 // invalidated on purpose 1325 1326 fModel->fSelectedFiles.MakeEmpty(); 1327 fModel->fSelectedFiles = message; 1328 1329 PostMessage(MSG_START_CANCEL); 1330 1331 _SetWindowTitle(); 1332 } 1333 1334 1335 void 1336 GrepWindow::_OnCopyText() 1337 { 1338 bool onlyCopySelection = true; 1339 1340 if (fSearchResults->CurrentSelection() < 0) 1341 onlyCopySelection = false; 1342 1343 BString buffer; 1344 1345 for (int32 index = 0; ; index++) { 1346 BStringItem* item = dynamic_cast<BStringItem*>( 1347 fSearchResults->ItemAt(index)); 1348 if (item == NULL) 1349 break; 1350 1351 if (onlyCopySelection) { 1352 if (item->IsSelected()) 1353 buffer << item->Text() << "\n"; 1354 } else 1355 buffer << item->Text() << "\n"; 1356 } 1357 1358 status_t status = B_OK; 1359 1360 BMessage* clip = NULL; 1361 1362 if (be_clipboard->Lock()) { 1363 be_clipboard->Clear(); 1364 1365 clip = be_clipboard->Data(); 1366 1367 clip->AddData("text/plain", B_MIME_TYPE, buffer.String(), 1368 buffer.Length()); 1369 1370 status = be_clipboard->Commit(); 1371 1372 if (status != B_OK) { 1373 be_clipboard->Unlock(); 1374 return; 1375 } 1376 1377 be_clipboard->Unlock(); 1378 } 1379 } 1380 1381 1382 void 1383 GrepWindow::_OnSelectInTracker() 1384 { 1385 if (fSearchResults->CurrentSelection() < 0) { 1386 BAlert* alert = new BAlert("Info", 1387 B_TRANSLATE("Please select the files you wish to have selected for you in " 1388 "Tracker."), 1389 B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT); 1390 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 1391 alert->Go(NULL); 1392 return; 1393 } 1394 1395 BMessage message; 1396 BString filePath; 1397 BPath folderPath; 1398 BList folderList; 1399 BString lastFolderAddedToList; 1400 1401 for (int32 index = 0; ; index++) { 1402 BStringItem* item = dynamic_cast<BStringItem*>( 1403 fSearchResults->ItemAt(index)); 1404 if (item == NULL) 1405 break; 1406 1407 // only open selected and top level (file) items 1408 if (!item->IsSelected() || item->OutlineLevel() > 0) 1409 continue; 1410 1411 // check if this was previously opened 1412 if (filePath == item->Text()) 1413 continue; 1414 1415 filePath = item->Text(); 1416 entry_ref file_ref; 1417 if (get_ref_for_path(filePath.String(), &file_ref) != B_OK) 1418 continue; 1419 1420 message.AddRef("refs", &file_ref); 1421 1422 // add parent folder to list of folders to open 1423 folderPath.SetTo(filePath.String()); 1424 if (folderPath.GetParent(&folderPath) == B_OK) { 1425 BPath* path = new BPath(folderPath); 1426 if (path->Path() != lastFolderAddedToList) { 1427 // catches some duplicates 1428 folderList.AddItem(path); 1429 lastFolderAddedToList = path->Path(); 1430 } else 1431 delete path; 1432 } 1433 } 1434 1435 _RemoveFolderListDuplicates(&folderList); 1436 _OpenFoldersInTracker(&folderList); 1437 1438 int32 aShortWhile = 100000; 1439 snooze(aShortWhile); 1440 1441 if (!_AreAllFoldersOpenInTracker(&folderList)) { 1442 for (int32 x = 0; x < 5; x++) { 1443 aShortWhile += 100000; 1444 snooze(aShortWhile); 1445 _OpenFoldersInTracker(&folderList); 1446 } 1447 } 1448 1449 if (!_AreAllFoldersOpenInTracker(&folderList)) { 1450 BString str1; 1451 str1 << B_TRANSLATE("%APP_NAME couldn't open one or more folders."); 1452 str1.ReplaceFirst("%APP_NAME",APP_NAME); 1453 BAlert* alert = new BAlert(NULL, str1.String(), B_TRANSLATE("OK"), 1454 NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT); 1455 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 1456 alert->Go(NULL); 1457 goto out; 1458 } 1459 1460 _SelectFilesInTracker(&folderList, &message); 1461 1462 out: 1463 // delete folderList contents 1464 int32 folderCount = folderList.CountItems(); 1465 for (int32 x = 0; x < folderCount; x++) 1466 delete static_cast<BPath*>(folderList.ItemAt(x)); 1467 } 1468 1469 1470 void 1471 GrepWindow::_OnQuitNow() 1472 { 1473 if (be_app->Lock()) { 1474 be_app->PostMessage(B_QUIT_REQUESTED); 1475 be_app->Unlock(); 1476 } 1477 } 1478 1479 1480 void 1481 GrepWindow::_OnFileDrop(BMessage* message) 1482 { 1483 if (fModel->fState != STATE_IDLE) 1484 return; 1485 1486 entry_ref directory; 1487 _InitRefsReceived(&directory, message); 1488 1489 fModel->fDirectory = directory; 1490 fModel->fSelectedFiles.MakeEmpty(); 1491 fModel->fSelectedFiles = *message; 1492 1493 fSearchResults->MakeEmpty(); 1494 1495 _SetWindowTitle(); 1496 } 1497 1498 1499 void 1500 GrepWindow::_OnRefsReceived(BMessage* message) 1501 { 1502 _OnFileDrop(message); 1503 // It seems a B_CANCEL always follows a B_REFS_RECEIVED 1504 // from a BFilePanel in Open mode. 1505 // 1506 // _OnOpenPanelCancel() is called on B_CANCEL. 1507 // That's where saving the current dir of the file panel occurs, for now, 1508 // and also the neccesary deletion of the file panel object. 1509 // A hidden file panel would otherwise jam the shutdown process. 1510 } 1511 1512 1513 void 1514 GrepWindow::_OnOpenPanel() 1515 { 1516 if (fFilePanel != NULL) 1517 return; 1518 1519 entry_ref path; 1520 if (get_ref_for_path(fModel->fFilePanelPath.String(), &path) != B_OK) 1521 return; 1522 1523 BMessenger messenger(this); 1524 BMessage message(MSG_REFS_RECEIVED); 1525 fFilePanel = new BFilePanel(B_OPEN_PANEL, &messenger, &path, 1526 B_FILE_NODE | B_DIRECTORY_NODE | B_SYMLINK_NODE, true, 1527 &message, NULL, true, true); 1528 1529 fFilePanel->Show(); 1530 } 1531 1532 1533 void 1534 GrepWindow::_OnOpenPanelCancel() 1535 { 1536 entry_ref panelDirRef; 1537 fFilePanel->GetPanelDirectory(&panelDirRef); 1538 BPath path(&panelDirRef); 1539 fModel->fFilePanelPath = path.Path(); 1540 delete fFilePanel; 1541 fFilePanel = NULL; 1542 } 1543 1544 1545 void 1546 GrepWindow::_OnSelectAll(BMessage *message) 1547 { 1548 BMessenger messenger(fSearchResults); 1549 messenger.SendMessage(B_SELECT_ALL); 1550 } 1551 1552 1553 void 1554 GrepWindow::_OnNewWindow() 1555 { 1556 BMessage cloneRefs; 1557 // we don't want GrepWindow::InitRefsReceived() 1558 // to mess with the refs of the current window 1559 1560 cloneRefs = fModel->fSelectedFiles; 1561 cloneRefs.AddRef("dir_ref", &(fModel->fDirectory)); 1562 1563 new GrepWindow(&cloneRefs); 1564 } 1565 1566 1567 // #pragma mark - 1568 1569 1570 void 1571 GrepWindow::_ModelChanged() 1572 { 1573 CALLED(); 1574 1575 _StopNodeMonitoring(); 1576 _SavePrefs(); 1577 } 1578 1579 bool 1580 GrepWindow::_OpenInPe(const entry_ref &ref, int32 lineNum) 1581 { 1582 BMessage message('Cmdl'); 1583 message.AddRef("refs", &ref); 1584 1585 if (lineNum != -1) 1586 message.AddInt32("line", lineNum); 1587 1588 entry_ref pe; 1589 if (be_roster->FindApp(PE_SIGNATURE, &pe) != B_OK) 1590 return false; 1591 1592 if (be_roster->IsRunning(&pe)) { 1593 BMessenger msngr(NULL, be_roster->TeamFor(&pe)); 1594 if (msngr.SendMessage(&message) != B_OK) 1595 return false; 1596 } else { 1597 if (be_roster->Launch(&pe, &message) != B_OK) 1598 return false; 1599 } 1600 1601 return true; 1602 } 1603 1604 1605 void 1606 GrepWindow::_RemoveFolderListDuplicates(BList* folderList) 1607 { 1608 if (folderList == NULL) 1609 return; 1610 1611 int32 folderCount = folderList->CountItems(); 1612 BString folderX; 1613 BString folderY; 1614 1615 for (int32 x = 0; x < folderCount; x++) { 1616 BPath* path = static_cast<BPath*>(folderList->ItemAt(x)); 1617 folderX = path->Path(); 1618 1619 for (int32 y = x + 1; y < folderCount; y++) { 1620 path = static_cast<BPath*>(folderList->ItemAt(y)); 1621 folderY = path->Path(); 1622 if (folderX == folderY) { 1623 delete static_cast<BPath*>(folderList->RemoveItem(y)); 1624 folderCount--; 1625 y--; 1626 } 1627 } 1628 } 1629 } 1630 1631 1632 status_t 1633 GrepWindow::_OpenFoldersInTracker(BList* folderList) 1634 { 1635 status_t status = B_OK; 1636 BMessage refsMsg(B_REFS_RECEIVED); 1637 1638 int32 folderCount = folderList->CountItems(); 1639 for (int32 index = 0; index < folderCount; index++) { 1640 BPath* path = static_cast<BPath*>(folderList->ItemAt(index)); 1641 1642 entry_ref folderRef; 1643 status = get_ref_for_path(path->Path(), &folderRef); 1644 if (status != B_OK) 1645 return status; 1646 1647 status = refsMsg.AddRef("refs", &folderRef); 1648 if (status != B_OK) 1649 return status; 1650 } 1651 1652 status = be_roster->Launch(TRACKER_SIGNATURE, &refsMsg); 1653 if (status != B_OK && status != B_ALREADY_RUNNING) 1654 return status; 1655 1656 return B_OK; 1657 } 1658 1659 1660 bool 1661 GrepWindow::_AreAllFoldersOpenInTracker(BList *folderList) 1662 { 1663 // Compare the folders we want open in Tracker to 1664 // the actual Tracker windows currently open. 1665 1666 // We build a list of open Tracker windows, and compare 1667 // it to the list of folders we want open in Tracker. 1668 1669 // If all folders exists in the list of Tracker windows 1670 // return true 1671 1672 status_t status = B_OK; 1673 BMessenger trackerMessenger(TRACKER_SIGNATURE); 1674 BMessage sendMessage; 1675 BMessage replyMessage; 1676 BList windowList; 1677 1678 if (!trackerMessenger.IsValid()) 1679 return false; 1680 1681 for (int32 count = 1; ; count++) { 1682 sendMessage.MakeEmpty(); 1683 replyMessage.MakeEmpty(); 1684 1685 sendMessage.what = B_GET_PROPERTY; 1686 sendMessage.AddSpecifier("Path"); 1687 sendMessage.AddSpecifier("Poses"); 1688 sendMessage.AddSpecifier("Window", count); 1689 1690 status = trackerMessenger.SendMessage(&sendMessage, &replyMessage); 1691 if (status != B_OK) 1692 return false; 1693 1694 entry_ref* trackerRef = new (nothrow) entry_ref; 1695 status = replyMessage.FindRef("result", trackerRef); 1696 if (status != B_OK || !windowList.AddItem(trackerRef)) { 1697 delete trackerRef; 1698 break; 1699 } 1700 } 1701 1702 int32 folderCount = folderList->CountItems(); 1703 int32 windowCount = windowList.CountItems(); 1704 1705 int32 found = 0; 1706 BPath* folderPath; 1707 entry_ref* windowRef; 1708 BString folderString; 1709 BString windowString; 1710 bool result = false; 1711 1712 if (folderCount > windowCount) { 1713 // at least one folder is not open in Tracker 1714 goto out; 1715 } 1716 1717 // Loop over the two lists and see if all folders exist as window 1718 for (int32 x = 0; x < folderCount; x++) { 1719 for (int32 y = 0; y < windowCount; y++) { 1720 1721 folderPath = static_cast<BPath*>(folderList->ItemAt(x)); 1722 windowRef = static_cast<entry_ref*>(windowList.ItemAt(y)); 1723 1724 if (folderPath == NULL) 1725 break; 1726 1727 if (windowRef == NULL) 1728 break; 1729 1730 folderString = folderPath->Path(); 1731 1732 BEntry entry; 1733 BPath path; 1734 1735 if (entry.SetTo(windowRef) == B_OK && path.SetTo(&entry) == B_OK) { 1736 1737 windowString = path.Path(); 1738 1739 if (folderString == windowString) { 1740 found++; 1741 break; 1742 } 1743 } 1744 } 1745 } 1746 1747 result = found == folderCount; 1748 1749 out: 1750 // delete list of window entry_refs 1751 for (int32 x = 0; x < windowCount; x++) 1752 delete static_cast<entry_ref*>(windowList.ItemAt(x)); 1753 1754 return result; 1755 } 1756 1757 1758 status_t 1759 GrepWindow::_SelectFilesInTracker(BList* folderList, BMessage* refsMessage) 1760 { 1761 // loops over Tracker windows, find each windowRef, 1762 // extract the refs that are children of windowRef, 1763 // add refs to selection-message 1764 1765 status_t status = B_OK; 1766 BMessenger trackerMessenger(TRACKER_SIGNATURE); 1767 BMessage windowSendMessage; 1768 BMessage windowReplyMessage; 1769 BMessage selectionSendMessage; 1770 BMessage selectionReplyMessage; 1771 1772 if (!trackerMessenger.IsValid()) 1773 return status; 1774 1775 // loop over Tracker windows 1776 for (int32 windowCount = 1; ; windowCount++) { 1777 1778 windowSendMessage.MakeEmpty(); 1779 windowReplyMessage.MakeEmpty(); 1780 1781 windowSendMessage.what = B_GET_PROPERTY; 1782 windowSendMessage.AddSpecifier("Path"); 1783 windowSendMessage.AddSpecifier("Poses"); 1784 windowSendMessage.AddSpecifier("Window", windowCount); 1785 1786 status = trackerMessenger.SendMessage(&windowSendMessage, 1787 &windowReplyMessage); 1788 1789 if (status != B_OK) 1790 return status; 1791 1792 entry_ref windowRef; 1793 status = windowReplyMessage.FindRef("result", &windowRef); 1794 if (status != B_OK) 1795 break; 1796 1797 int32 folderCount = folderList->CountItems(); 1798 1799 // loop over folders in folderList 1800 for (int32 x = 0; x < folderCount; x++) { 1801 BPath* folderPath = static_cast<BPath*>(folderList->ItemAt(x)); 1802 if (folderPath == NULL) 1803 break; 1804 1805 BString folderString = folderPath->Path(); 1806 1807 BEntry windowEntry; 1808 BPath windowPath; 1809 BString windowString; 1810 1811 status = windowEntry.SetTo(&windowRef); 1812 if (status != B_OK) 1813 break; 1814 1815 status = windowPath.SetTo(&windowEntry); 1816 if (status != B_OK) 1817 break; 1818 1819 windowString = windowPath.Path(); 1820 1821 // if match, loop over items in refsMessage 1822 // and add those that live in window/folder 1823 // to a selection message 1824 1825 if (windowString == folderString) { 1826 selectionSendMessage.MakeEmpty(); 1827 selectionSendMessage.what = B_SET_PROPERTY; 1828 selectionReplyMessage.MakeEmpty(); 1829 1830 // loop over refs and add to message 1831 entry_ref ref; 1832 for (int32 index = 0; ; index++) { 1833 status = refsMessage->FindRef("refs", index, &ref); 1834 if (status != B_OK) 1835 break; 1836 1837 BDirectory directory(&windowRef); 1838 BEntry entry(&ref); 1839 if (directory.Contains(&entry)) 1840 selectionSendMessage.AddRef("data", &ref); 1841 } 1842 1843 // finish selection message 1844 selectionSendMessage.AddSpecifier("Selection"); 1845 selectionSendMessage.AddSpecifier("Poses"); 1846 selectionSendMessage.AddSpecifier("Window", windowCount); 1847 1848 trackerMessenger.SendMessage(&selectionSendMessage, 1849 &selectionReplyMessage); 1850 } 1851 } 1852 } 1853 1854 return B_OK; 1855 } 1856