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