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