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_TRANSLATE_CONTEXT 50 #define B_TRANSLATE_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->Go(NULL); 1294 return; 1295 } 1296 1297 BMessage message; 1298 BString path; 1299 1300 for (int32 index = 0; ; index++) { 1301 BStringItem* item = dynamic_cast<BStringItem*>( 1302 fSearchResults->ItemAt(index)); 1303 if (item == NULL) 1304 break; 1305 1306 if (!item->IsSelected() || item->OutlineLevel() != 0) 1307 continue; 1308 1309 if (path == item->Text()) 1310 continue; 1311 1312 path = item->Text(); 1313 entry_ref ref; 1314 if (get_ref_for_path(path.String(), &ref) == B_OK) 1315 message.AddRef("refs", &ref); 1316 } 1317 1318 fModel->fDirectory = entry_ref(); 1319 // invalidated on purpose 1320 1321 fModel->fSelectedFiles.MakeEmpty(); 1322 fModel->fSelectedFiles = message; 1323 1324 PostMessage(MSG_START_CANCEL); 1325 1326 _SetWindowTitle(); 1327 } 1328 1329 1330 void 1331 GrepWindow::_OnCopyText() 1332 { 1333 bool onlyCopySelection = true; 1334 1335 if (fSearchResults->CurrentSelection() < 0) 1336 onlyCopySelection = false; 1337 1338 BString buffer; 1339 1340 for (int32 index = 0; ; index++) { 1341 BStringItem* item = dynamic_cast<BStringItem*>( 1342 fSearchResults->ItemAt(index)); 1343 if (item == NULL) 1344 break; 1345 1346 if (onlyCopySelection) { 1347 if (item->IsSelected()) 1348 buffer << item->Text() << "\n"; 1349 } else 1350 buffer << item->Text() << "\n"; 1351 } 1352 1353 status_t status = B_OK; 1354 1355 BMessage* clip = NULL; 1356 1357 if (be_clipboard->Lock()) { 1358 be_clipboard->Clear(); 1359 1360 clip = be_clipboard->Data(); 1361 1362 clip->AddData("text/plain", B_MIME_TYPE, buffer.String(), 1363 buffer.Length()); 1364 1365 status = be_clipboard->Commit(); 1366 1367 if (status != B_OK) { 1368 be_clipboard->Unlock(); 1369 return; 1370 } 1371 1372 be_clipboard->Unlock(); 1373 } 1374 } 1375 1376 1377 void 1378 GrepWindow::_OnSelectInTracker() 1379 { 1380 if (fSearchResults->CurrentSelection() < 0) { 1381 BAlert* alert = new BAlert("Info", 1382 B_TRANSLATE("Please select the files you wish to have selected for you in " 1383 "Tracker."), 1384 B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT); 1385 alert->Go(NULL); 1386 return; 1387 } 1388 1389 BMessage message; 1390 BString filePath; 1391 BPath folderPath; 1392 BList folderList; 1393 BString lastFolderAddedToList; 1394 1395 for (int32 index = 0; ; index++) { 1396 BStringItem* item = dynamic_cast<BStringItem*>( 1397 fSearchResults->ItemAt(index)); 1398 if (item == NULL) 1399 break; 1400 1401 // only open selected and top level (file) items 1402 if (!item->IsSelected() || item->OutlineLevel() > 0) 1403 continue; 1404 1405 // check if this was previously opened 1406 if (filePath == item->Text()) 1407 continue; 1408 1409 filePath = item->Text(); 1410 entry_ref file_ref; 1411 if (get_ref_for_path(filePath.String(), &file_ref) != B_OK) 1412 continue; 1413 1414 message.AddRef("refs", &file_ref); 1415 1416 // add parent folder to list of folders to open 1417 folderPath.SetTo(filePath.String()); 1418 if (folderPath.GetParent(&folderPath) == B_OK) { 1419 BPath* path = new BPath(folderPath); 1420 if (path->Path() != lastFolderAddedToList) { 1421 // catches some duplicates 1422 folderList.AddItem(path); 1423 lastFolderAddedToList = path->Path(); 1424 } else 1425 delete path; 1426 } 1427 } 1428 1429 _RemoveFolderListDuplicates(&folderList); 1430 _OpenFoldersInTracker(&folderList); 1431 1432 int32 aShortWhile = 100000; 1433 snooze(aShortWhile); 1434 1435 if (!_AreAllFoldersOpenInTracker(&folderList)) { 1436 for (int32 x = 0; x < 5; x++) { 1437 aShortWhile += 100000; 1438 snooze(aShortWhile); 1439 _OpenFoldersInTracker(&folderList); 1440 } 1441 } 1442 1443 if (!_AreAllFoldersOpenInTracker(&folderList)) { 1444 BString str1; 1445 str1 << B_TRANSLATE("%APP_NAME couldn't open one or more folders."); 1446 str1.ReplaceFirst("%APP_NAME",APP_NAME); 1447 BAlert* alert = new BAlert(NULL, str1.String(), B_TRANSLATE("OK"), 1448 NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT); 1449 alert->Go(NULL); 1450 goto out; 1451 } 1452 1453 _SelectFilesInTracker(&folderList, &message); 1454 1455 out: 1456 // delete folderList contents 1457 int32 folderCount = folderList.CountItems(); 1458 for (int32 x = 0; x < folderCount; x++) 1459 delete static_cast<BPath*>(folderList.ItemAt(x)); 1460 } 1461 1462 1463 void 1464 GrepWindow::_OnQuitNow() 1465 { 1466 if (be_app->Lock()) { 1467 be_app->PostMessage(B_QUIT_REQUESTED); 1468 be_app->Unlock(); 1469 } 1470 } 1471 1472 1473 void 1474 GrepWindow::_OnFileDrop(BMessage* message) 1475 { 1476 if (fModel->fState != STATE_IDLE) 1477 return; 1478 1479 entry_ref directory; 1480 _InitRefsReceived(&directory, message); 1481 1482 fModel->fDirectory = directory; 1483 fModel->fSelectedFiles.MakeEmpty(); 1484 fModel->fSelectedFiles = *message; 1485 1486 fSearchResults->MakeEmpty(); 1487 1488 _SetWindowTitle(); 1489 } 1490 1491 1492 void 1493 GrepWindow::_OnRefsReceived(BMessage* message) 1494 { 1495 _OnFileDrop(message); 1496 // It seems a B_CANCEL always follows a B_REFS_RECEIVED 1497 // from a BFilePanel in Open mode. 1498 // 1499 // _OnOpenPanelCancel() is called on B_CANCEL. 1500 // That's where saving the current dir of the file panel occurs, for now, 1501 // and also the neccesary deletion of the file panel object. 1502 // A hidden file panel would otherwise jam the shutdown process. 1503 } 1504 1505 1506 void 1507 GrepWindow::_OnOpenPanel() 1508 { 1509 if (fFilePanel != NULL) 1510 return; 1511 1512 entry_ref path; 1513 if (get_ref_for_path(fModel->fFilePanelPath.String(), &path) != B_OK) 1514 return; 1515 1516 BMessenger messenger(this); 1517 BMessage message(MSG_REFS_RECEIVED); 1518 fFilePanel = new BFilePanel(B_OPEN_PANEL, &messenger, &path, 1519 B_FILE_NODE | B_DIRECTORY_NODE | B_SYMLINK_NODE, true, 1520 &message, NULL, true, true); 1521 1522 fFilePanel->Show(); 1523 } 1524 1525 1526 void 1527 GrepWindow::_OnOpenPanelCancel() 1528 { 1529 entry_ref panelDirRef; 1530 fFilePanel->GetPanelDirectory(&panelDirRef); 1531 BPath path(&panelDirRef); 1532 fModel->fFilePanelPath = path.Path(); 1533 delete fFilePanel; 1534 fFilePanel = NULL; 1535 } 1536 1537 1538 void 1539 GrepWindow::_OnSelectAll(BMessage *message) 1540 { 1541 BMessenger messenger(fSearchResults); 1542 messenger.SendMessage(B_SELECT_ALL); 1543 } 1544 1545 1546 void 1547 GrepWindow::_OnNewWindow() 1548 { 1549 BMessage cloneRefs; 1550 // we don't want GrepWindow::InitRefsReceived() 1551 // to mess with the refs of the current window 1552 1553 cloneRefs = fModel->fSelectedFiles; 1554 cloneRefs.AddRef("dir_ref", &(fModel->fDirectory)); 1555 1556 new GrepWindow(&cloneRefs); 1557 } 1558 1559 1560 // #pragma mark - 1561 1562 1563 void 1564 GrepWindow::_ModelChanged() 1565 { 1566 CALLED(); 1567 1568 _StopNodeMonitoring(); 1569 _SavePrefs(); 1570 } 1571 1572 bool 1573 GrepWindow::_OpenInPe(const entry_ref &ref, int32 lineNum) 1574 { 1575 BMessage message('Cmdl'); 1576 message.AddRef("refs", &ref); 1577 1578 if (lineNum != -1) 1579 message.AddInt32("line", lineNum); 1580 1581 entry_ref pe; 1582 if (be_roster->FindApp(PE_SIGNATURE, &pe) != B_OK) 1583 return false; 1584 1585 if (be_roster->IsRunning(&pe)) { 1586 BMessenger msngr(NULL, be_roster->TeamFor(&pe)); 1587 if (msngr.SendMessage(&message) != B_OK) 1588 return false; 1589 } else { 1590 if (be_roster->Launch(&pe, &message) != B_OK) 1591 return false; 1592 } 1593 1594 return true; 1595 } 1596 1597 1598 void 1599 GrepWindow::_RemoveFolderListDuplicates(BList* folderList) 1600 { 1601 if (folderList == NULL) 1602 return; 1603 1604 int32 folderCount = folderList->CountItems(); 1605 BString folderX; 1606 BString folderY; 1607 1608 for (int32 x = 0; x < folderCount; x++) { 1609 BPath* path = static_cast<BPath*>(folderList->ItemAt(x)); 1610 folderX = path->Path(); 1611 1612 for (int32 y = x + 1; y < folderCount; y++) { 1613 path = static_cast<BPath*>(folderList->ItemAt(y)); 1614 folderY = path->Path(); 1615 if (folderX == folderY) { 1616 delete static_cast<BPath*>(folderList->RemoveItem(y)); 1617 folderCount--; 1618 y--; 1619 } 1620 } 1621 } 1622 } 1623 1624 1625 status_t 1626 GrepWindow::_OpenFoldersInTracker(BList* folderList) 1627 { 1628 status_t status = B_OK; 1629 BMessage refsMsg(B_REFS_RECEIVED); 1630 1631 int32 folderCount = folderList->CountItems(); 1632 for (int32 index = 0; index < folderCount; index++) { 1633 BPath* path = static_cast<BPath*>(folderList->ItemAt(index)); 1634 1635 entry_ref folderRef; 1636 status = get_ref_for_path(path->Path(), &folderRef); 1637 if (status != B_OK) 1638 return status; 1639 1640 status = refsMsg.AddRef("refs", &folderRef); 1641 if (status != B_OK) 1642 return status; 1643 } 1644 1645 status = be_roster->Launch(TRACKER_SIGNATURE, &refsMsg); 1646 if (status != B_OK && status != B_ALREADY_RUNNING) 1647 return status; 1648 1649 return B_OK; 1650 } 1651 1652 1653 bool 1654 GrepWindow::_AreAllFoldersOpenInTracker(BList *folderList) 1655 { 1656 // Compare the folders we want open in Tracker to 1657 // the actual Tracker windows currently open. 1658 1659 // We build a list of open Tracker windows, and compare 1660 // it to the list of folders we want open in Tracker. 1661 1662 // If all folders exists in the list of Tracker windows 1663 // return true 1664 1665 status_t status = B_OK; 1666 BMessenger trackerMessenger(TRACKER_SIGNATURE); 1667 BMessage sendMessage; 1668 BMessage replyMessage; 1669 BList windowList; 1670 1671 if (!trackerMessenger.IsValid()) 1672 return false; 1673 1674 for (int32 count = 1; ; count++) { 1675 sendMessage.MakeEmpty(); 1676 replyMessage.MakeEmpty(); 1677 1678 sendMessage.what = B_GET_PROPERTY; 1679 sendMessage.AddSpecifier("Path"); 1680 sendMessage.AddSpecifier("Poses"); 1681 sendMessage.AddSpecifier("Window", count); 1682 1683 status = trackerMessenger.SendMessage(&sendMessage, &replyMessage); 1684 if (status != B_OK) 1685 return false; 1686 1687 entry_ref* trackerRef = new (nothrow) entry_ref; 1688 status = replyMessage.FindRef("result", trackerRef); 1689 if (status != B_OK || !windowList.AddItem(trackerRef)) { 1690 delete trackerRef; 1691 break; 1692 } 1693 } 1694 1695 int32 folderCount = folderList->CountItems(); 1696 int32 windowCount = windowList.CountItems(); 1697 1698 int32 found = 0; 1699 BPath* folderPath; 1700 entry_ref* windowRef; 1701 BString folderString; 1702 BString windowString; 1703 bool result = false; 1704 1705 if (folderCount > windowCount) { 1706 // at least one folder is not open in Tracker 1707 goto out; 1708 } 1709 1710 // Loop over the two lists and see if all folders exist as window 1711 for (int32 x = 0; x < folderCount; x++) { 1712 for (int32 y = 0; y < windowCount; y++) { 1713 1714 folderPath = static_cast<BPath*>(folderList->ItemAt(x)); 1715 windowRef = static_cast<entry_ref*>(windowList.ItemAt(y)); 1716 1717 if (folderPath == NULL) 1718 break; 1719 1720 if (windowRef == NULL) 1721 break; 1722 1723 folderString = folderPath->Path(); 1724 1725 BEntry entry; 1726 BPath path; 1727 1728 if (entry.SetTo(windowRef) == B_OK && path.SetTo(&entry) == B_OK) { 1729 1730 windowString = path.Path(); 1731 1732 if (folderString == windowString) { 1733 found++; 1734 break; 1735 } 1736 } 1737 } 1738 } 1739 1740 result = found == folderCount; 1741 1742 out: 1743 // delete list of window entry_refs 1744 for (int32 x = 0; x < windowCount; x++) 1745 delete static_cast<entry_ref*>(windowList.ItemAt(x)); 1746 1747 return result; 1748 } 1749 1750 1751 status_t 1752 GrepWindow::_SelectFilesInTracker(BList* folderList, BMessage* refsMessage) 1753 { 1754 // loops over Tracker windows, find each windowRef, 1755 // extract the refs that are children of windowRef, 1756 // add refs to selection-message 1757 1758 status_t status = B_OK; 1759 BMessenger trackerMessenger(TRACKER_SIGNATURE); 1760 BMessage windowSendMessage; 1761 BMessage windowReplyMessage; 1762 BMessage selectionSendMessage; 1763 BMessage selectionReplyMessage; 1764 1765 if (!trackerMessenger.IsValid()) 1766 return status; 1767 1768 // loop over Tracker windows 1769 for (int32 windowCount = 1; ; windowCount++) { 1770 1771 windowSendMessage.MakeEmpty(); 1772 windowReplyMessage.MakeEmpty(); 1773 1774 windowSendMessage.what = B_GET_PROPERTY; 1775 windowSendMessage.AddSpecifier("Path"); 1776 windowSendMessage.AddSpecifier("Poses"); 1777 windowSendMessage.AddSpecifier("Window", windowCount); 1778 1779 status = trackerMessenger.SendMessage(&windowSendMessage, 1780 &windowReplyMessage); 1781 1782 if (status != B_OK) 1783 return status; 1784 1785 entry_ref windowRef; 1786 status = windowReplyMessage.FindRef("result", &windowRef); 1787 if (status != B_OK) 1788 break; 1789 1790 int32 folderCount = folderList->CountItems(); 1791 1792 // loop over folders in folderList 1793 for (int32 x = 0; x < folderCount; x++) { 1794 BPath* folderPath = static_cast<BPath*>(folderList->ItemAt(x)); 1795 if (folderPath == NULL) 1796 break; 1797 1798 BString folderString = folderPath->Path(); 1799 1800 BEntry windowEntry; 1801 BPath windowPath; 1802 BString windowString; 1803 1804 status = windowEntry.SetTo(&windowRef); 1805 if (status != B_OK) 1806 break; 1807 1808 status = windowPath.SetTo(&windowEntry); 1809 if (status != B_OK) 1810 break; 1811 1812 windowString = windowPath.Path(); 1813 1814 // if match, loop over items in refsMessage 1815 // and add those that live in window/folder 1816 // to a selection message 1817 1818 if (windowString == folderString) { 1819 selectionSendMessage.MakeEmpty(); 1820 selectionSendMessage.what = B_SET_PROPERTY; 1821 selectionReplyMessage.MakeEmpty(); 1822 1823 // loop over refs and add to message 1824 entry_ref ref; 1825 for (int32 index = 0; ; index++) { 1826 status = refsMessage->FindRef("refs", index, &ref); 1827 if (status != B_OK) 1828 break; 1829 1830 BDirectory directory(&windowRef); 1831 BEntry entry(&ref); 1832 if (directory.Contains(&entry)) 1833 selectionSendMessage.AddRef("data", &ref); 1834 } 1835 1836 // finish selection message 1837 selectionSendMessage.AddSpecifier("Selection"); 1838 selectionSendMessage.AddSpecifier("Poses"); 1839 selectionSendMessage.AddSpecifier("Window", windowCount); 1840 1841 trackerMessenger.SendMessage(&selectionSendMessage, 1842 &selectionReplyMessage); 1843 } 1844 } 1845 } 1846 1847 return B_OK; 1848 } 1849