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