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