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