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