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 which files to search"), 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 sub-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 fSearch->SetEnabled(false); 801 802 // We need to remember the search pattern, because during 803 // the grepping, the text control's text will be replaced 804 // by the name of the file that's currently being grepped. 805 // When the grepping finishes, we need to restore the old 806 // search pattern. 807 808 fOldPattern = fSearchText->Text(); 809 810 FileIterator* iterator = new (nothrow) InitialIterator(fModel); 811 fGrepper = new (nothrow) Grepper(fOldPattern.String(), fModel, 812 this, iterator); 813 if (fGrepper != NULL && fGrepper->IsValid()) 814 fGrepper->Start(); 815 else { 816 // roll back in case of problems 817 if (fGrepper == NULL) 818 delete iterator; 819 else { 820 // Grepper owns iterator 821 delete fGrepper; 822 fGrepper = NULL; 823 } 824 fModel->fState = STATE_IDLE; 825 // TODO: better notification to user 826 fprintf(stderr, "Out of memory.\n"); 827 } 828 } else if (fModel->fState == STATE_SEARCH) { 829 fModel->fState = STATE_CANCEL; 830 fGrepper->Cancel(); 831 } 832 } 833 834 835 void 836 GrepWindow::_OnSearchFinished() 837 { 838 fModel->fState = STATE_IDLE; 839 840 delete fGrepper; 841 fGrepper = NULL; 842 843 fFileMenu->SetEnabled(true); 844 fActionMenu->SetEnabled(true); 845 fPreferencesMenu->SetEnabled(true); 846 fHistoryMenu->SetEnabled(true); 847 fEncodingMenu->SetEnabled(true); 848 849 fButton->SetLabel(B_TRANSLATE("Search")); 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::_OnAboutRequested() 1475 { 1476 BString text; 1477 text << B_TRANSLATE(APP_NAME) << " " << "\n\n"; 1478 int32 titleLength = text.Length(); 1479 text << B_TRANSLATE("Created by Matthijs Hollemans." "\n\n" 1480 "Contributed to by " 1481 "Peter Hinely, Serge Fantino, Hideki Naito, Oscar Lesta, " 1482 "Oliver Tappe, Jonas Sundström, Luc Schrijvers and" 1483 " momoziro.\n"); 1484 1485 BAlert* alert = new BAlert("TextSearch", text.String(), B_TRANSLATE("OK"), NULL, 1486 NULL, B_WIDTH_AS_USUAL, B_INFO_ALERT); 1487 1488 BTextView* view = alert->TextView(); 1489 BFont font; 1490 view->SetStylable(true); 1491 view->GetFont(&font); 1492 font.SetSize(font.Size() * 1.5); 1493 font.SetFace(B_BOLD_FACE); 1494 view->SetFontAndColor(0, titleLength, &font); 1495 1496 alert->Go(NULL); 1497 } 1498 1499 1500 void 1501 GrepWindow::_OnFileDrop(BMessage* message) 1502 { 1503 if (fModel->fState != STATE_IDLE) 1504 return; 1505 1506 entry_ref directory; 1507 _InitRefsReceived(&directory, message); 1508 1509 fModel->fDirectory = directory; 1510 fModel->fSelectedFiles.MakeEmpty(); 1511 fModel->fSelectedFiles = *message; 1512 1513 fSearchResults->MakeEmpty(); 1514 1515 _SetWindowTitle(); 1516 } 1517 1518 1519 void 1520 GrepWindow::_OnRefsReceived(BMessage* message) 1521 { 1522 _OnFileDrop(message); 1523 // It seems a B_CANCEL always follows a B_REFS_RECEIVED 1524 // from a BFilePanel in Open mode. 1525 // 1526 // _OnOpenPanelCancel() is called on B_CANCEL. 1527 // That's where saving the current dir of the file panel occurs, for now, 1528 // and also the neccesary deletion of the file panel object. 1529 // A hidden file panel would otherwise jam the shutdown process. 1530 } 1531 1532 1533 void 1534 GrepWindow::_OnOpenPanel() 1535 { 1536 if (fFilePanel != NULL) 1537 return; 1538 1539 entry_ref path; 1540 if (get_ref_for_path(fModel->fFilePanelPath.String(), &path) != B_OK) 1541 return; 1542 1543 BMessenger messenger(this); 1544 BMessage message(MSG_REFS_RECEIVED); 1545 fFilePanel = new BFilePanel(B_OPEN_PANEL, &messenger, &path, 1546 B_FILE_NODE | B_DIRECTORY_NODE | B_SYMLINK_NODE, true, 1547 &message, NULL, true, true); 1548 1549 fFilePanel->Show(); 1550 } 1551 1552 1553 void 1554 GrepWindow::_OnOpenPanelCancel() 1555 { 1556 entry_ref panelDirRef; 1557 fFilePanel->GetPanelDirectory(&panelDirRef); 1558 BPath path(&panelDirRef); 1559 fModel->fFilePanelPath = path.Path(); 1560 delete fFilePanel; 1561 fFilePanel = NULL; 1562 } 1563 1564 1565 void 1566 GrepWindow::_OnSelectAll(BMessage *message) 1567 { 1568 BMessenger messenger(fSearchResults); 1569 messenger.SendMessage(B_SELECT_ALL); 1570 } 1571 1572 1573 void 1574 GrepWindow::_OnNewWindow() 1575 { 1576 BMessage cloneRefs; 1577 // we don't want GrepWindow::InitRefsReceived() 1578 // to mess with the refs of the current window 1579 1580 cloneRefs = fModel->fSelectedFiles; 1581 cloneRefs.AddRef("dir_ref", &(fModel->fDirectory)); 1582 1583 new GrepWindow(&cloneRefs); 1584 } 1585 1586 1587 // #pragma mark - 1588 1589 1590 void 1591 GrepWindow::_ModelChanged() 1592 { 1593 CALLED(); 1594 1595 _StopNodeMonitoring(); 1596 _SavePrefs(); 1597 } 1598 1599 bool 1600 GrepWindow::_OpenInPe(const entry_ref &ref, int32 lineNum) 1601 { 1602 BMessage message('Cmdl'); 1603 message.AddRef("refs", &ref); 1604 1605 if (lineNum != -1) 1606 message.AddInt32("line", lineNum); 1607 1608 entry_ref pe; 1609 if (be_roster->FindApp(PE_SIGNATURE, &pe) != B_OK) 1610 return false; 1611 1612 if (be_roster->IsRunning(&pe)) { 1613 BMessenger msngr(NULL, be_roster->TeamFor(&pe)); 1614 if (msngr.SendMessage(&message) != B_OK) 1615 return false; 1616 } else { 1617 if (be_roster->Launch(&pe, &message) != B_OK) 1618 return false; 1619 } 1620 1621 return true; 1622 } 1623 1624 1625 void 1626 GrepWindow::_RemoveFolderListDuplicates(BList* folderList) 1627 { 1628 if (folderList == NULL) 1629 return; 1630 1631 int32 folderCount = folderList->CountItems(); 1632 BString folderX; 1633 BString folderY; 1634 1635 for (int32 x = 0; x < folderCount; x++) { 1636 BPath* path = static_cast<BPath*>(folderList->ItemAt(x)); 1637 folderX = path->Path(); 1638 1639 for (int32 y = x + 1; y < folderCount; y++) { 1640 path = static_cast<BPath*>(folderList->ItemAt(y)); 1641 folderY = path->Path(); 1642 if (folderX == folderY) { 1643 delete static_cast<BPath*>(folderList->RemoveItem(y)); 1644 folderCount--; 1645 y--; 1646 } 1647 } 1648 } 1649 } 1650 1651 1652 status_t 1653 GrepWindow::_OpenFoldersInTracker(BList* folderList) 1654 { 1655 status_t status = B_OK; 1656 BMessage refsMsg(B_REFS_RECEIVED); 1657 1658 int32 folderCount = folderList->CountItems(); 1659 for (int32 index = 0; index < folderCount; index++) { 1660 BPath* path = static_cast<BPath*>(folderList->ItemAt(index)); 1661 1662 entry_ref folderRef; 1663 status = get_ref_for_path(path->Path(), &folderRef); 1664 if (status != B_OK) 1665 return status; 1666 1667 status = refsMsg.AddRef("refs", &folderRef); 1668 if (status != B_OK) 1669 return status; 1670 } 1671 1672 status = be_roster->Launch(TRACKER_SIGNATURE, &refsMsg); 1673 if (status != B_OK && status != B_ALREADY_RUNNING) 1674 return status; 1675 1676 return B_OK; 1677 } 1678 1679 1680 bool 1681 GrepWindow::_AreAllFoldersOpenInTracker(BList *folderList) 1682 { 1683 // Compare the folders we want open in Tracker to 1684 // the actual Tracker windows currently open. 1685 1686 // We build a list of open Tracker windows, and compare 1687 // it to the list of folders we want open in Tracker. 1688 1689 // If all folders exists in the list of Tracker windows 1690 // return true 1691 1692 status_t status = B_OK; 1693 BMessenger trackerMessenger(TRACKER_SIGNATURE); 1694 BMessage sendMessage; 1695 BMessage replyMessage; 1696 BList windowList; 1697 1698 if (!trackerMessenger.IsValid()) 1699 return false; 1700 1701 for (int32 count = 1; ; count++) { 1702 sendMessage.MakeEmpty(); 1703 replyMessage.MakeEmpty(); 1704 1705 sendMessage.what = B_GET_PROPERTY; 1706 sendMessage.AddSpecifier("Path"); 1707 sendMessage.AddSpecifier("Poses"); 1708 sendMessage.AddSpecifier("Window", count); 1709 1710 status = trackerMessenger.SendMessage(&sendMessage, &replyMessage); 1711 if (status != B_OK) 1712 return false; 1713 1714 entry_ref* trackerRef = new (nothrow) entry_ref; 1715 status = replyMessage.FindRef("result", trackerRef); 1716 if (status != B_OK || !windowList.AddItem(trackerRef)) { 1717 delete trackerRef; 1718 break; 1719 } 1720 } 1721 1722 int32 folderCount = folderList->CountItems(); 1723 int32 windowCount = windowList.CountItems(); 1724 1725 int32 found = 0; 1726 BPath* folderPath; 1727 entry_ref* windowRef; 1728 BString folderString; 1729 BString windowString; 1730 bool result = false; 1731 1732 if (folderCount > windowCount) { 1733 // at least one folder is not open in Tracker 1734 goto out; 1735 } 1736 1737 // Loop over the two lists and see if all folders exist as window 1738 for (int32 x = 0; x < folderCount; x++) { 1739 for (int32 y = 0; y < windowCount; y++) { 1740 1741 folderPath = static_cast<BPath*>(folderList->ItemAt(x)); 1742 windowRef = static_cast<entry_ref*>(windowList.ItemAt(y)); 1743 1744 if (folderPath == NULL) 1745 break; 1746 1747 if (windowRef == NULL) 1748 break; 1749 1750 folderString = folderPath->Path(); 1751 1752 BEntry entry; 1753 BPath path; 1754 1755 if (entry.SetTo(windowRef) == B_OK && path.SetTo(&entry) == B_OK) { 1756 1757 windowString = path.Path(); 1758 1759 if (folderString == windowString) { 1760 found++; 1761 break; 1762 } 1763 } 1764 } 1765 } 1766 1767 result = found == folderCount; 1768 1769 out: 1770 // delete list of window entry_refs 1771 for (int32 x = 0; x < windowCount; x++) 1772 delete static_cast<entry_ref*>(windowList.ItemAt(x)); 1773 1774 return result; 1775 } 1776 1777 1778 status_t 1779 GrepWindow::_SelectFilesInTracker(BList* folderList, BMessage* refsMessage) 1780 { 1781 // loops over Tracker windows, find each windowRef, 1782 // extract the refs that are children of windowRef, 1783 // add refs to selection-message 1784 1785 status_t status = B_OK; 1786 BMessenger trackerMessenger(TRACKER_SIGNATURE); 1787 BMessage windowSendMessage; 1788 BMessage windowReplyMessage; 1789 BMessage selectionSendMessage; 1790 BMessage selectionReplyMessage; 1791 1792 if (!trackerMessenger.IsValid()) 1793 return status; 1794 1795 // loop over Tracker windows 1796 for (int32 windowCount = 1; ; windowCount++) { 1797 1798 windowSendMessage.MakeEmpty(); 1799 windowReplyMessage.MakeEmpty(); 1800 1801 windowSendMessage.what = B_GET_PROPERTY; 1802 windowSendMessage.AddSpecifier("Path"); 1803 windowSendMessage.AddSpecifier("Poses"); 1804 windowSendMessage.AddSpecifier("Window", windowCount); 1805 1806 status = trackerMessenger.SendMessage(&windowSendMessage, 1807 &windowReplyMessage); 1808 1809 if (status != B_OK) 1810 return status; 1811 1812 entry_ref windowRef; 1813 status = windowReplyMessage.FindRef("result", &windowRef); 1814 if (status != B_OK) 1815 break; 1816 1817 int32 folderCount = folderList->CountItems(); 1818 1819 // loop over folders in folderList 1820 for (int32 x = 0; x < folderCount; x++) { 1821 BPath* folderPath = static_cast<BPath*>(folderList->ItemAt(x)); 1822 if (folderPath == NULL) 1823 break; 1824 1825 BString folderString = folderPath->Path(); 1826 1827 BEntry windowEntry; 1828 BPath windowPath; 1829 BString windowString; 1830 1831 status = windowEntry.SetTo(&windowRef); 1832 if (status != B_OK) 1833 break; 1834 1835 status = windowPath.SetTo(&windowEntry); 1836 if (status != B_OK) 1837 break; 1838 1839 windowString = windowPath.Path(); 1840 1841 // if match, loop over items in refsMessage 1842 // and add those that live in window/folder 1843 // to a selection message 1844 1845 if (windowString == folderString) { 1846 selectionSendMessage.MakeEmpty(); 1847 selectionSendMessage.what = B_SET_PROPERTY; 1848 selectionReplyMessage.MakeEmpty(); 1849 1850 // loop over refs and add to message 1851 entry_ref ref; 1852 for (int32 index = 0; ; index++) { 1853 status = refsMessage->FindRef("refs", index, &ref); 1854 if (status != B_OK) 1855 break; 1856 1857 BDirectory directory(&windowRef); 1858 BEntry entry(&ref); 1859 if (directory.Contains(&entry)) 1860 selectionSendMessage.AddRef("data", &ref); 1861 } 1862 1863 // finish selection message 1864 selectionSendMessage.AddSpecifier("Selection"); 1865 selectionSendMessage.AddSpecifier("Poses"); 1866 selectionSendMessage.AddSpecifier("Window", windowCount); 1867 1868 trackerMessenger.SendMessage(&selectionSendMessage, 1869 &selectionReplyMessage); 1870 } 1871 } 1872 } 1873 1874 return B_OK; 1875 } 1876