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