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