2 * Copyright (c) 1998-2007 Matthijs Hollemans
3 * All rights reserved. Distributed under the terms of the MIT License.
5 #include "GrepWindow.h"
15 #include <Application.h>
16 #include <AppFileInfo.h>
18 #include <Clipboard.h>
19 #include <LayoutBuilder.h>
20 #include <MessageRunner.h>
22 #include <PathMonitor.h>
24 #include <SpaceLayoutItem.h>
28 #include "ChangesIterator.h"
29 #include "GlobalDefs.h"
31 #include "InitialIterator.h"
33 #undef B_TRANSLATION_CONTEXT
34 #define B_TRANSLATION_CONTEXT "GrepWindow"
39 static const bigtime_t kChangesPulseInterval
= 150000;
41 #define TRACE_NODE_MONITORING
42 #ifdef TRACE_NODE_MONITORING
43 # define TRACE_NM(x...) printf(x)
45 # define TRACE_NM(x...)
48 //#define TRACE_FUNCTIONS
49 #ifdef TRACE_FUNCTIONS
50 class FunctionTracer
{
52 FunctionTracer(const char* functionName
)
55 printf("%s - enter\n", fName
.String());
59 printf("%s - exit\n", fName
.String());
64 # define CALLED() FunctionTracer functionTracer(__PRETTY_FUNCTION__)
67 #endif // TRACE_FUNCTIONS
70 GrepWindow::GrepWindow(BMessage
* message
)
71 : BWindow(BRect(0, 0, 525, 430), NULL
, B_DOCUMENT_WINDOW
,
72 B_AUTO_UPDATE_SIZE_LIMITS
),
86 fSelectInTracker(NULL
),
88 fPreferencesMenu(NULL
),
93 fRegularExpression(NULL
),
103 fShowLinesCheckbox(NULL
),
108 fModel(new (nothrow
) Model()),
109 fLastNodeMonitorEvent(system_time()),
110 fChangesIterator(NULL
),
119 _InitRefsReceived(&directory
, message
);
121 fModel
->fDirectory
= directory
;
122 fModel
->fSelectedFiles
= *message
;
129 _TileIfMultipleWindows();
135 GrepWindow::~GrepWindow()
142 void GrepWindow::FrameResized(float width
, float height
)
144 BWindow::FrameResized(width
, height
);
145 fModel
->fFrame
= Frame();
150 void GrepWindow::FrameMoved(BPoint origin
)
152 BWindow::FrameMoved(origin
);
153 fModel
->fFrame
= Frame();
158 void GrepWindow::MenusBeginning()
160 fModel
->FillHistoryMenu(fHistoryMenu
);
161 BWindow::MenusBeginning();
165 void GrepWindow::MenusEnded()
167 for (int32 t
= fHistoryMenu
->CountItems(); t
> 0; --t
)
168 delete fHistoryMenu
->RemoveItem(t
- 1);
170 BWindow::MenusEnded();
174 void GrepWindow::MessageReceived(BMessage
* message
)
176 switch (message
->what
) {
182 _OnFileDrop(message
);
189 case MSG_REFS_RECEIVED
:
190 _OnRefsReceived(message
);
194 _OnOpenPanelCancel();
197 case MSG_RECURSE_LINKS
:
201 case MSG_RECURSE_DIRS
:
205 case MSG_SKIP_DOT_DIRS
:
209 case MSG_CASE_SENSITIVE
:
213 case MSG_REGULAR_EXPRESSION
:
214 _OnRegularExpression();
225 case MSG_SEARCH_TEXT
:
229 case MSG_SELECT_HISTORY
:
230 _OnHistoryItem(message
);
233 case MSG_START_CANCEL
:
237 case MSG_SEARCH_FINISHED
:
241 case MSG_START_NODE_MONITORING
:
242 _StartNodeMonitoring();
246 _OnNodeMonitorEvent(message
);
249 case MSG_NODE_MONITOR_PULSE
:
250 _OnNodeMonitorPulse();
253 case MSG_REPORT_FILE_NAME
:
254 _OnReportFileName(message
);
257 case MSG_REPORT_RESULT
:
258 _OnReportResult(message
);
261 case MSG_REPORT_ERROR
:
262 _OnReportError(message
);
266 _OnSelectAll(message
);
269 case MSG_TRIM_SELECTION
:
277 case MSG_SELECT_IN_TRACKER
:
278 _OnSelectInTracker();
281 case MSG_CHECKBOX_SHOW_LINES
:
282 _OnCheckboxShowLines();
285 case MSG_OPEN_SELECTION
:
287 case MSG_INVOKE_ITEM
:
296 fModel
->fEncoding
= 0;
299 case B_SJIS_CONVERSION
:
300 fModel
->fEncoding
= B_SJIS_CONVERSION
;
303 case B_EUC_CONVERSION
:
304 fModel
->fEncoding
= B_EUC_CONVERSION
;
307 case B_JIS_CONVERSION
:
308 fModel
->fEncoding
= B_JIS_CONVERSION
;
312 BWindow::MessageReceived(message
);
323 _StopNodeMonitoring();
326 // TODO: stippi: Looks like this could be done
327 // by maintaining a counter in GrepApp with the number of open
328 // grep windows... and just quit when it goes zero
329 if (be_app
->Lock()) {
330 be_app
->PostMessage(MSG_TRY_QUIT
);
341 GrepWindow::_InitRefsReceived(entry_ref
* directory
, BMessage
* message
)
344 // If the user selected a single folder and invoked TextSearch on it,
345 // but recurse directories is switched off, TextSearch would do nothing.
346 // In that special case, we'd like it to recurse into that folder (but
347 // not go any deeper after that).
351 message
->GetInfo("refs", &code
, &count
);
354 if (message
->FindRef("dir_ref", 0, directory
) == B_OK
)
355 message
->MakeEmpty();
360 if (message
->FindRef("refs", 0, &ref
) == B_OK
) {
361 BEntry
entry(&ref
, true);
362 if (entry
.IsDirectory()) {
363 // ok, special case, we use this folder as base directory
364 // and pretend nothing had been selected:
366 message
->MakeEmpty();
374 GrepWindow::_SetWindowTitle()
376 BEntry
entry(&fModel
->fDirectory
, true);
378 if (entry
.InitCheck() == B_OK
) {
380 if (entry
.GetPath(&path
) == B_OK
) {
381 if (fOldPattern
.Length()) {
382 title
= B_TRANSLATE("%appname% : %path% : %searchtext%");
383 title
.ReplaceAll("%searchtext%", fOldPattern
.String());
385 title
= B_TRANSLATE("%appname% : %path%");
387 title
.ReplaceAll("%appname%", B_TRANSLATE(APP_NAME
));
388 title
.ReplaceAll("%path%", path
.Path());
393 title
= B_TRANSLATE(APP_NAME
);
395 SetTitle(title
.String());
400 GrepWindow::_CreateMenus()
402 fMenuBar
= new BMenuBar("menubar");
404 fFileMenu
= new BMenu(B_TRANSLATE("File"));
405 fActionMenu
= new BMenu(B_TRANSLATE("Actions"));
406 fPreferencesMenu
= new BMenu(B_TRANSLATE("Settings"));
407 fHistoryMenu
= new BMenu(B_TRANSLATE("History"));
408 fEncodingMenu
= new BMenu(B_TRANSLATE("Encoding"));
410 fNew
= new BMenuItem(
411 B_TRANSLATE("New window"), new BMessage(MSG_NEW_WINDOW
), 'N');
413 fOpen
= new BMenuItem(
414 B_TRANSLATE("Set target" B_UTF8_ELLIPSIS
), new BMessage(MSG_OPEN_PANEL
), 'F');
416 fClose
= new BMenuItem(
417 B_TRANSLATE("Close"), new BMessage(B_QUIT_REQUESTED
), 'W');
419 fQuit
= new BMenuItem(
420 B_TRANSLATE("Quit"), new BMessage(MSG_QUIT_NOW
), 'Q');
422 fSearch
= new BMenuItem(
423 B_TRANSLATE("Search"), new BMessage(MSG_START_CANCEL
), 'S');
425 fSelectAll
= new BMenuItem(
426 B_TRANSLATE("Select all"), new BMessage(MSG_SELECT_ALL
), 'A');
428 fTrimSelection
= new BMenuItem(
429 B_TRANSLATE("Trim to selection"), new BMessage(MSG_TRIM_SELECTION
), 'T');
431 fOpenSelection
= new BMenuItem(
432 B_TRANSLATE("Open selection"), new BMessage(MSG_OPEN_SELECTION
), 'O');
434 fSelectInTracker
= new BMenuItem(
435 B_TRANSLATE("Show files in Tracker"),
436 new BMessage(MSG_SELECT_IN_TRACKER
), 'K');
438 fCopyText
= new BMenuItem(
439 B_TRANSLATE("Copy text to clipboard"), new BMessage(MSG_COPY_TEXT
), 'B');
441 fRecurseLinks
= new BMenuItem(
442 B_TRANSLATE("Follow symbolic links"), new BMessage(MSG_RECURSE_LINKS
));
444 fRecurseDirs
= new BMenuItem(
445 B_TRANSLATE("Look in sub-folders"), new BMessage(MSG_RECURSE_DIRS
));
447 fSkipDotDirs
= new BMenuItem(
448 B_TRANSLATE("Skip folders starting with a dot"),
449 new BMessage(MSG_SKIP_DOT_DIRS
));
451 fCaseSensitive
= new BMenuItem(
452 B_TRANSLATE("Case-sensitive"), new BMessage(MSG_CASE_SENSITIVE
));
454 fRegularExpression
= new BMenuItem(
455 B_TRANSLATE("Regular expression"), new BMessage(MSG_REGULAR_EXPRESSION
));
457 fTextOnly
= new BMenuItem(
458 B_TRANSLATE("Text files only"), new BMessage(MSG_TEXT_ONLY
));
460 fInvokePe
= new BMenuItem(
461 B_TRANSLATE("Open files in Pe"), new BMessage(MSG_INVOKE_PE
));
463 fUTF8
= new BMenuItem("UTF8", new BMessage('utf8'));
464 fShiftJIS
= new BMenuItem("ShiftJIS", new BMessage(B_SJIS_CONVERSION
));
465 fEUC
= new BMenuItem("EUC", new BMessage(B_EUC_CONVERSION
));
466 fJIS
= new BMenuItem("JIS", new BMessage(B_JIS_CONVERSION
));
468 fFileMenu
->AddItem(fNew
);
469 fFileMenu
->AddSeparatorItem();
470 fFileMenu
->AddItem(fOpen
);
471 fFileMenu
->AddItem(fClose
);
472 fFileMenu
->AddSeparatorItem();
473 fFileMenu
->AddItem(fQuit
);
475 fActionMenu
->AddItem(fSearch
);
476 fActionMenu
->AddSeparatorItem();
477 fActionMenu
->AddItem(fSelectAll
);
478 fActionMenu
->AddItem(fTrimSelection
);
479 fActionMenu
->AddSeparatorItem();
480 fActionMenu
->AddItem(fOpenSelection
);
481 fActionMenu
->AddItem(fSelectInTracker
);
482 fActionMenu
->AddItem(fCopyText
);
484 fPreferencesMenu
->AddItem(fRecurseLinks
);
485 fPreferencesMenu
->AddItem(fRecurseDirs
);
486 fPreferencesMenu
->AddItem(fSkipDotDirs
);
487 fPreferencesMenu
->AddItem(fCaseSensitive
);
488 fPreferencesMenu
->AddItem(fRegularExpression
);
489 fPreferencesMenu
->AddItem(fTextOnly
);
490 fPreferencesMenu
->AddItem(fInvokePe
);
492 fEncodingMenu
->AddItem(fUTF8
);
493 fEncodingMenu
->AddItem(fShiftJIS
);
494 fEncodingMenu
->AddItem(fEUC
);
495 fEncodingMenu
->AddItem(fJIS
);
497 // fEncodingMenu->SetLabelFromMarked(true);
498 // Do we really want this ?
499 fEncodingMenu
->SetRadioMode(true);
500 fEncodingMenu
->ItemAt(0)->SetMarked(true);
502 fMenuBar
->AddItem(fFileMenu
);
503 fMenuBar
->AddItem(fActionMenu
);
504 fMenuBar
->AddItem(fPreferencesMenu
);
505 fMenuBar
->AddItem(fHistoryMenu
);
506 fMenuBar
->AddItem(fEncodingMenu
);
508 fSearch
->SetEnabled(false);
513 GrepWindow::_CreateViews()
515 // The search pattern entry field does not send a message when
516 // <Enter> is pressed, because the "Search/Cancel" button already
517 // does this and we don't want to send the same message twice.
519 fSearchText
= new BTextControl(
520 "SearchText", NULL
, NULL
, NULL
,
521 B_WILL_DRAW
| B_FULL_UPDATE_ON_RESIZE
| B_NAVIGABLE
);
523 fSearchText
->TextView()->SetMaxBytes(1000);
524 fSearchText
->SetModificationMessage(new BMessage(MSG_SEARCH_TEXT
));
526 fButton
= new BButton(
527 "Button", B_TRANSLATE("Search"),
528 new BMessage(MSG_START_CANCEL
));
529 fButton
->MakeDefault(true);
530 fButton
->SetEnabled(false);
532 fShowLinesCheckbox
= new BCheckBox(
533 "ShowLines", B_TRANSLATE("Show lines"),
534 new BMessage(MSG_CHECKBOX_SHOW_LINES
));
535 fShowLinesCheckbox
->SetValue(B_CONTROL_ON
);
537 fSearchResults
= new GrepListView();
538 fSearchResults
->SetInvocationMessage(new BMessage(MSG_INVOKE_ITEM
));
543 GrepWindow::_LayoutViews()
545 BScrollView
* scroller
= new BScrollView(
546 "ScrollSearchResults", fSearchResults
,
547 B_FULL_UPDATE_ON_RESIZE
, true, true);
549 BLayoutBuilder::Group
<>(this, B_VERTICAL
, 0)
550 .SetInsets(0, 0, -1, -1)
552 .AddGrid(B_USE_HALF_ITEM_INSETS
)
553 .SetInsets(B_USE_WINDOW_SPACING
, B_USE_WINDOW_SPACING
,
554 B_USE_WINDOW_SPACING
, B_USE_DEFAULT_SPACING
)
555 .Add(fSearchText
, 0, 0, 3)
556 .Add(fShowLinesCheckbox
, 0, 1)
557 .Add(BSpaceLayoutItem::CreateGlue(), 1, 1)
560 .AddGroup(B_VERTICAL
, 0)
561 .SetInsets(-2, 0, -1, -1)
566 fSearchText
->MakeFocus(true);
568 SetKeyMenuBar(fMenuBar
);
573 GrepWindow::_TileIfMultipleWindows()
575 if (be_app
->Lock()) {
576 int32 windowCount
= be_app
->CountWindows();
583 BScreen
screen(this);
584 BRect screenFrame
= screen
.Frame();
585 BRect windowFrame
= Frame();
587 if (windowFrame
.left
> screenFrame
.right
588 || windowFrame
.top
> screenFrame
.bottom
589 || windowFrame
.right
< screenFrame
.left
590 || windowFrame
.bottom
< screenFrame
.top
)
599 GrepWindow::_LoadPrefs()
605 fRecurseDirs
->SetMarked(fModel
->fRecurseDirs
);
606 fRecurseLinks
->SetMarked(fModel
->fRecurseLinks
);
607 fSkipDotDirs
->SetMarked(fModel
->fSkipDotDirs
);
608 fCaseSensitive
->SetMarked(fModel
->fCaseSensitive
);
609 fRegularExpression
->SetMarked(fModel
->fRegularExpression
);
610 fTextOnly
->SetMarked(fModel
->fTextOnly
);
611 fInvokePe
->SetMarked(fModel
->fInvokePe
);
613 fShowLinesCheckbox
->SetValue(fModel
->fShowLines
);
615 switch (fModel
->fEncoding
) {
617 fUTF8
->SetMarked(true);
619 case B_SJIS_CONVERSION
:
620 fShiftJIS
->SetMarked(true);
622 case B_EUC_CONVERSION
:
623 fEUC
->SetMarked(true);
625 case B_JIS_CONVERSION
:
626 fJIS
->SetMarked(true);
629 printf("Woops. Bad fModel->fEncoding value.\n");
633 MoveTo(fModel
->fFrame
.left
, fModel
->fFrame
.top
);
634 ResizeTo(fModel
->fFrame
.Width(), fModel
->fFrame
.Height());
641 GrepWindow::_SavePrefs()
648 GrepWindow::_StartNodeMonitoring()
652 _StopNodeMonitoring();
654 BMessenger
messenger(this);
655 uint32 fileFlags
= B_WATCH_NAME
| B_WATCH_STAT
| B_WATCH_ATTR
;
658 // watch the top level folder only, rest should be done through filtering
659 // the node monitor notifications
660 BPath
path(&fModel
->fDirectory
);
661 if (path
.InitCheck() == B_OK
) {
662 TRACE_NM("start monitoring root folder: %s\n", path
.Path());
663 BPrivate::BPathMonitor::StartWatching(path
.Path(),
664 fileFlags
| B_WATCH_RECURSIVELY
| B_WATCH_FILES_ONLY
, messenger
);
667 if (fChangesPulse
== NULL
) {
668 BMessage
message(MSG_NODE_MONITOR_PULSE
);
669 fChangesPulse
= new BMessageRunner(BMessenger(this), &message
,
670 kChangesPulseInterval
);
676 GrepWindow::_StopNodeMonitoring()
678 if (fChangesPulse
== NULL
)
683 BPrivate::BPathMonitor::StopWatching(BMessenger(this));
684 delete fChangesIterator
;
685 fChangesIterator
= NULL
;
686 delete fChangesPulse
;
687 fChangesPulse
= NULL
;
691 // #pragma mark - events
695 GrepWindow::_OnStartCancel()
699 _StopNodeMonitoring();
701 if (fModel
->fState
== STATE_IDLE
) {
702 fSearchResults
->MakeEmpty();
704 if (fSearchText
->TextView()->TextLength() == 0)
707 fModel
->fState
= STATE_SEARCH
;
709 fModel
->AddToHistory(fSearchText
->Text());
711 // From now on, we don't want to be notified when the
712 // search pattern changes, because the control will be
713 // displaying the names of the files we are grepping.
715 fSearchText
->SetModificationMessage(NULL
);
717 fFileMenu
->SetEnabled(false);
718 fActionMenu
->SetEnabled(false);
719 fPreferencesMenu
->SetEnabled(false);
720 fHistoryMenu
->SetEnabled(false);
721 fEncodingMenu
->SetEnabled(false);
723 fSearchText
->SetEnabled(false);
725 fButton
->MakeFocus(true);
726 fButton
->SetLabel(B_TRANSLATE("Cancel"));
728 fSearch
->SetEnabled(false);
730 // We need to remember the search pattern, because during
731 // the grepping, the text control's text will be replaced
732 // by the name of the file that's currently being grepped.
733 // When the grepping finishes, we need to restore the old
736 fOldPattern
= fSearchText
->Text();
740 FileIterator
* iterator
= new (nothrow
) InitialIterator(fModel
);
741 fGrepper
= new (nothrow
) Grepper(fOldPattern
.String(), fModel
,
743 if (fGrepper
!= NULL
&& fGrepper
->IsValid())
746 // roll back in case of problems
747 if (fGrepper
== NULL
)
750 // Grepper owns iterator
754 fModel
->fState
= STATE_IDLE
;
755 // TODO: better notification to user
756 fprintf(stderr
, "Out of memory.\n");
758 } else if (fModel
->fState
== STATE_SEARCH
) {
759 fModel
->fState
= STATE_CANCEL
;
766 GrepWindow::_OnSearchFinished()
768 fModel
->fState
= STATE_IDLE
;
773 fFileMenu
->SetEnabled(true);
774 fActionMenu
->SetEnabled(true);
775 fPreferencesMenu
->SetEnabled(true);
776 fHistoryMenu
->SetEnabled(true);
777 fEncodingMenu
->SetEnabled(true);
779 fButton
->SetLabel(B_TRANSLATE("Search"));
781 fButton
->SetEnabled(true);
782 fSearch
->SetEnabled(true);
784 fSearchText
->SetEnabled(true);
785 fSearchText
->MakeFocus(true);
786 fSearchText
->SetText(fOldPattern
.String());
787 fSearchText
->TextView()->SelectAll();
788 fSearchText
->SetModificationMessage(new BMessage(MSG_SEARCH_TEXT
));
790 PostMessage(MSG_START_NODE_MONITORING
);
795 GrepWindow::_OnNodeMonitorEvent(BMessage
* message
)
798 if (message
->FindInt32("opcode", &opCode
) != B_OK
)
801 if (fChangesIterator
== NULL
) {
802 fChangesIterator
= new (nothrow
) ChangesIterator(fModel
);
803 if (fChangesIterator
== NULL
|| !fChangesIterator
->IsValid()) {
804 delete fChangesIterator
;
805 fChangesIterator
= NULL
;
810 case B_ENTRY_CREATED
:
811 case B_ENTRY_REMOVED
:
813 TRACE_NM("%s\n", opCode
== B_ENTRY_CREATED
? "B_ENTRY_CREATED"
814 : "B_ENTRY_REMOVED");
816 if (message
->FindString("path", &path
) == B_OK
) {
817 if (opCode
== B_ENTRY_CREATED
) {
818 if (fChangesIterator
!= NULL
)
819 fChangesIterator
->EntryAdded(path
.String());
821 // in order to remove temporary files
822 if (fChangesIterator
!= NULL
)
823 fChangesIterator
->EntryRemoved(path
.String());
824 // remove from the list view already
825 BEntry
entry(path
.String());
827 if (entry
.GetRef(&ref
) == B_OK
)
828 fSearchResults
->RemoveResults(ref
, true);
831 #ifdef TRACE_NODE_MONITORING
832 printf("incompatible message:\n");
833 message
->PrintToStream();
836 TRACE_NM("path: %s\n", path
.String());
841 TRACE_NM("B_ENTRY_MOVED\n");
844 if (message
->FindString("path", &path
) != B_OK
) {
845 #ifdef TRACE_NODE_MONITORING
846 printf("incompatible message:\n");
847 message
->PrintToStream();
853 if (message
->FindBool("added", &added
) != B_OK
)
856 if (message
->FindBool("removed", &removed
) != B_OK
)
861 } else if (removed
) {
864 // files changed location, but are still within the search
866 BEntry
entry(path
.String());
868 if (entry
.GetRef(&ref
) == B_OK
) {
870 ResultItem
* item
= fSearchResults
->FindItem(ref
, &index
);
872 item
->SetText(path
.String());
873 // take care of invalidation, the index is currently
874 // the full list index, but needs to be the visible
875 // items index for this
876 index
= fSearchResults
->IndexOf(item
);
877 fSearchResults
->InvalidateItem(index
);
886 TRACE_NM("%s\n", opCode
== B_STAT_CHANGED
? "B_STAT_CHANGED"
888 // For directly watched files, the path will include the
889 // name. When the event occurs for a file in a watched directory,
890 // the message will have an extra name field for the respective
893 if (message
->FindString("path", &path
) == B_OK
) {
894 if (fChangesIterator
!= NULL
)
895 fChangesIterator
->EntryChanged(path
.String());
897 #ifdef TRACE_NODE_MONITORING
898 printf("incompatible message:\n");
899 message
->PrintToStream();
902 TRACE_NM("path: %s\n", path
.String());
903 // message->PrintToStream();
908 TRACE_NM("unkown op code\n");
912 fLastNodeMonitorEvent
= system_time();
917 GrepWindow::_OnNodeMonitorPulse()
919 if (fChangesIterator
== NULL
|| fChangesIterator
->IsEmpty())
922 if (system_time() - fLastNodeMonitorEvent
< kChangesPulseInterval
) {
923 // wait for things to settle down before running the search for changes
927 if (fModel
->fState
!= STATE_IDLE
) {
928 // An update or search is still in progress. New node monitor messages
929 // may arrive while an update is still running. They should not arrive
930 // during a regular search, but we want to be prepared for that anyways
931 // and check != STATE_IDLE.
935 fOldPattern
= fSearchText
->Text();
937 #ifdef TRACE_NODE_MONITORING
938 fChangesIterator
->PrintToStream();
941 fGrepper
= new (nothrow
) Grepper(fOldPattern
.String(), fModel
,
942 this, fChangesIterator
);
943 if (fGrepper
!= NULL
&& fGrepper
->IsValid()) {
945 fChangesIterator
= NULL
;
946 fModel
->fState
= STATE_UPDATE
;
948 // roll back in case of problems
949 if (fGrepper
== NULL
)
950 delete fChangesIterator
;
952 // Grepper owns iterator
956 fprintf(stderr
, "Out of memory.\n");
962 GrepWindow::_OnReportFileName(BMessage
* message
)
964 if (fModel
->fState
!= STATE_UPDATE
) {
965 BString name
= message
->FindString("filename");
966 fSearchText
->TruncateString(&name
, B_TRUNCATE_MIDDLE
,
967 fSearchText
->Bounds().Width() - 10);
969 fSearchText
->SetText(name
);
975 GrepWindow::_OnReportResult(BMessage
* message
)
979 if (message
->FindRef("ref", &ref
) != B_OK
)
984 message
->GetInfo("text", &type
, &count
);
986 BStringItem
* item
= NULL
;
987 if (fModel
->fState
== STATE_UPDATE
) {
988 // During updates because of node monitor events, negatives are
989 // also reported (count == 0).
990 item
= fSearchResults
->RemoveResults(ref
, count
== 0);
997 item
= new ResultItem(ref
);
998 fSearchResults
->AddItem(item
);
999 item
->SetExpanded(fShowLinesCheckbox
->Value() == 1);
1003 while (message
->FindString("text", --count
, &buf
) == B_OK
) {
1004 uchar
* temp
= (uchar
*)strdup(buf
);
1008 // replace all non-printable characters by spaces
1014 if (!(c
& 0x80) && iscntrl(c
))
1020 fSearchResults
->AddUnder(new BStringItem((const char*)temp
), item
);
1028 GrepWindow::_OnReportError(BMessage
* message
)
1031 if (message
->FindString("error", &buf
) == B_OK
)
1032 fSearchResults
->AddItem(new BStringItem(buf
));
1037 GrepWindow::_OnRecurseLinks()
1039 fModel
->fRecurseLinks
= !fModel
->fRecurseLinks
;
1040 fRecurseLinks
->SetMarked(fModel
->fRecurseLinks
);
1046 GrepWindow::_OnRecurseDirs()
1048 fModel
->fRecurseDirs
= !fModel
->fRecurseDirs
;
1049 fRecurseDirs
->SetMarked(fModel
->fRecurseDirs
);
1055 GrepWindow::_OnSkipDotDirs()
1057 fModel
->fSkipDotDirs
= !fModel
->fSkipDotDirs
;
1058 fSkipDotDirs
->SetMarked(fModel
->fSkipDotDirs
);
1064 GrepWindow::_OnRegularExpression()
1066 fModel
->fRegularExpression
= !fModel
->fRegularExpression
;
1067 fRegularExpression
->SetMarked(fModel
->fRegularExpression
);
1073 GrepWindow::_OnCaseSensitive()
1075 fModel
->fCaseSensitive
= !fModel
->fCaseSensitive
;
1076 fCaseSensitive
->SetMarked(fModel
->fCaseSensitive
);
1082 GrepWindow::_OnTextOnly()
1084 fModel
->fTextOnly
= !fModel
->fTextOnly
;
1085 fTextOnly
->SetMarked(fModel
->fTextOnly
);
1091 GrepWindow::_OnInvokePe()
1093 fModel
->fInvokePe
= !fModel
->fInvokePe
;
1094 fInvokePe
->SetMarked(fModel
->fInvokePe
);
1100 GrepWindow::_OnCheckboxShowLines()
1102 // Selection in BOutlineListView in multiple selection mode
1103 // gets weird when collapsing. I've tried all sorts of things.
1104 // It seems impossible to make it behave just right.
1106 // Going from collapsed to expande mode, the superitems
1107 // keep their selection, the subitems don't (yet) have
1108 // a selection. This works as expected, AFAIK.
1110 // Going from expanded to collapsed mode, I would like
1111 // for a selected subitem (line) to select its superitem,
1112 // (its file) and the subitem be unselected.
1114 // I've successfully tried code patches that apply the
1115 // selection pattern that I want, but with weird effects
1116 // on subsequent manual selection.
1117 // Lines stay selected while the user tries to select
1118 // some other line. It just gets weird.
1120 // It's as though listItem->Select() and Deselect()
1121 // put the items in some semi-selected state.
1122 // Or maybe I've got it all wrong.
1124 // So, here's the plain basic collapse/expand.
1125 // I think it's the least bad of what's possible on BeOS R5,
1126 // but perhaps someone comes along with a patch of magic.
1128 fModel
->fShowLines
= (fShowLinesCheckbox
->Value() == 1);
1130 int32 numItems
= fSearchResults
->FullListCountItems();
1131 for (int32 x
= 0; x
< numItems
; ++x
) {
1132 BListItem
* listItem
= fSearchResults
->FullListItemAt(x
);
1133 if (listItem
->OutlineLevel() == 0) {
1134 if (fModel
->fShowLines
) {
1135 if (!fSearchResults
->IsExpanded(x
))
1136 fSearchResults
->Expand(listItem
);
1138 if (fSearchResults
->IsExpanded(x
))
1139 fSearchResults
->Collapse(listItem
);
1144 fSearchResults
->Invalidate();
1151 GrepWindow::_OnInvokeItem()
1153 for (int32 selectionIndex
= 0; ; selectionIndex
++) {
1154 int32 itemIndex
= fSearchResults
->CurrentSelection(selectionIndex
);
1155 BListItem
* item
= fSearchResults
->ItemAt(itemIndex
);
1159 int32 level
= item
->OutlineLevel();
1162 // Get the line number.
1163 // only this level has line numbers
1165 BStringItem
* str
= dynamic_cast<BStringItem
*>(item
);
1167 lineNum
= atol(str
->Text());
1168 // fortunately, atol knows when to stop the conversion
1172 // Get the top-most item and launch its entry_ref.
1173 while (level
!= 0) {
1174 item
= fSearchResults
->Superitem(item
);
1177 level
= item
->OutlineLevel();
1180 ResultItem
* entry
= dynamic_cast<ResultItem
*>(item
);
1181 if (entry
!= NULL
) {
1182 if (fModel
->fInvokePe
&& _OpenInPe(entry
->ref
, lineNum
))
1185 // ask tracker to open it for us
1186 BMessenger
target(TRACKER_SIGNATURE
);
1187 BMessage
message(B_REFS_RECEIVED
);
1188 message
.AddRef("refs", &entry
->ref
);
1190 message
.AddInt32("be:line", lineNum
);
1192 target
.SendMessage(&message
);
1199 GrepWindow::_OnSearchText()
1203 bool enabled
= fSearchText
->TextView()->TextLength() != 0;
1204 fButton
->SetEnabled(enabled
);
1205 fSearch
->SetEnabled(enabled
);
1206 _StopNodeMonitoring();
1211 GrepWindow::_OnHistoryItem(BMessage
* message
)
1214 if (message
->FindString("text", &buf
) == B_OK
)
1215 fSearchText
->SetText(buf
);
1220 GrepWindow::_OnTrimSelection()
1222 if (fSearchResults
->CurrentSelection() < 0) {
1224 text
<< B_TRANSLATE("Please select the files you wish to keep searching.");
1226 text
<< B_TRANSLATE("The unselected files will be removed from the list.");
1228 BAlert
* alert
= new BAlert(NULL
, text
.String(), B_TRANSLATE("OK"), NULL
, NULL
,
1229 B_WIDTH_AS_USUAL
, B_WARNING_ALERT
);
1230 alert
->SetFlags(alert
->Flags() | B_CLOSE_ON_ESCAPE
);
1238 for (int32 index
= 0; ; index
++) {
1239 BStringItem
* item
= dynamic_cast<BStringItem
*>(
1240 fSearchResults
->ItemAt(index
));
1244 if (!item
->IsSelected() || item
->OutlineLevel() != 0)
1247 if (path
== item
->Text())
1250 path
= item
->Text();
1252 if (get_ref_for_path(path
.String(), &ref
) == B_OK
)
1253 message
.AddRef("refs", &ref
);
1256 fModel
->fDirectory
= entry_ref();
1257 // invalidated on purpose
1259 fModel
->fSelectedFiles
.MakeEmpty();
1260 fModel
->fSelectedFiles
= message
;
1262 PostMessage(MSG_START_CANCEL
);
1269 GrepWindow::_OnCopyText()
1271 bool onlyCopySelection
= true;
1273 if (fSearchResults
->CurrentSelection() < 0)
1274 onlyCopySelection
= false;
1278 for (int32 index
= 0; ; index
++) {
1279 BStringItem
* item
= dynamic_cast<BStringItem
*>(
1280 fSearchResults
->ItemAt(index
));
1284 if (onlyCopySelection
) {
1285 if (item
->IsSelected())
1286 buffer
<< item
->Text() << "\n";
1288 buffer
<< item
->Text() << "\n";
1291 status_t status
= B_OK
;
1293 BMessage
* clip
= NULL
;
1295 if (be_clipboard
->Lock()) {
1296 be_clipboard
->Clear();
1298 clip
= be_clipboard
->Data();
1300 clip
->AddData("text/plain", B_MIME_TYPE
, buffer
.String(),
1303 status
= be_clipboard
->Commit();
1305 if (status
!= B_OK
) {
1306 be_clipboard
->Unlock();
1310 be_clipboard
->Unlock();
1316 GrepWindow::_OnSelectInTracker()
1318 if (fSearchResults
->CurrentSelection() < 0) {
1319 BAlert
* alert
= new BAlert("Info",
1320 B_TRANSLATE("Please select the files you wish to have selected for you in "
1322 B_TRANSLATE("OK"), NULL
, NULL
, B_WIDTH_AS_USUAL
, B_WARNING_ALERT
);
1323 alert
->SetFlags(alert
->Flags() | B_CLOSE_ON_ESCAPE
);
1332 BString lastFolderAddedToList
;
1334 for (int32 index
= 0; ; index
++) {
1335 BStringItem
* item
= dynamic_cast<BStringItem
*>(
1336 fSearchResults
->ItemAt(index
));
1340 // only open selected and top level (file) items
1341 if (!item
->IsSelected() || item
->OutlineLevel() > 0)
1344 // check if this was previously opened
1345 if (filePath
== item
->Text())
1348 filePath
= item
->Text();
1350 if (get_ref_for_path(filePath
.String(), &file_ref
) != B_OK
)
1353 message
.AddRef("refs", &file_ref
);
1355 // add parent folder to list of folders to open
1356 folderPath
.SetTo(filePath
.String());
1357 if (folderPath
.GetParent(&folderPath
) == B_OK
) {
1358 BPath
* path
= new BPath(folderPath
);
1359 if (path
->Path() != lastFolderAddedToList
) {
1360 // catches some duplicates
1361 folderList
.AddItem(path
);
1362 lastFolderAddedToList
= path
->Path();
1368 _RemoveFolderListDuplicates(&folderList
);
1369 _OpenFoldersInTracker(&folderList
);
1371 int32 aShortWhile
= 100000;
1372 snooze(aShortWhile
);
1374 if (!_AreAllFoldersOpenInTracker(&folderList
)) {
1375 for (int32 x
= 0; x
< 5; x
++) {
1376 aShortWhile
+= 100000;
1377 snooze(aShortWhile
);
1378 _OpenFoldersInTracker(&folderList
);
1382 if (!_AreAllFoldersOpenInTracker(&folderList
)) {
1384 str1
<< B_TRANSLATE("%APP_NAME couldn't open one or more folders.");
1385 str1
.ReplaceFirst("%APP_NAME",APP_NAME
);
1386 BAlert
* alert
= new BAlert(NULL
, str1
.String(), B_TRANSLATE("OK"),
1387 NULL
, NULL
, B_WIDTH_AS_USUAL
, B_STOP_ALERT
);
1388 alert
->SetFlags(alert
->Flags() | B_CLOSE_ON_ESCAPE
);
1393 _SelectFilesInTracker(&folderList
, &message
);
1396 // delete folderList contents
1397 int32 folderCount
= folderList
.CountItems();
1398 for (int32 x
= 0; x
< folderCount
; x
++)
1399 delete static_cast<BPath
*>(folderList
.ItemAt(x
));
1404 GrepWindow::_OnQuitNow()
1406 if (be_app
->Lock()) {
1407 be_app
->PostMessage(B_QUIT_REQUESTED
);
1414 GrepWindow::_OnFileDrop(BMessage
* message
)
1416 if (fModel
->fState
!= STATE_IDLE
)
1419 entry_ref directory
;
1420 _InitRefsReceived(&directory
, message
);
1422 fModel
->fDirectory
= directory
;
1423 fModel
->fSelectedFiles
.MakeEmpty();
1424 fModel
->fSelectedFiles
= *message
;
1426 fSearchResults
->MakeEmpty();
1434 GrepWindow::_OnRefsReceived(BMessage
* message
)
1436 _OnFileDrop(message
);
1438 // It seems a B_CANCEL always follows a B_REFS_RECEIVED
1439 // from a BFilePanel in Open mode.
1441 // _OnOpenPanelCancel() is called on B_CANCEL.
1442 // That's where saving the current dir of the file panel occurs, for now,
1443 // and also the neccesary deletion of the file panel object.
1444 // A hidden file panel would otherwise jam the shutdown process.
1449 GrepWindow::_OnOpenPanel()
1451 if (fFilePanel
!= NULL
)
1455 if (get_ref_for_path(fModel
->fFilePanelPath
.String(), &path
) != B_OK
)
1458 BMessenger
messenger(this);
1459 BMessage
message(MSG_REFS_RECEIVED
);
1460 fFilePanel
= new BFilePanel(B_OPEN_PANEL
, &messenger
, &path
,
1461 B_FILE_NODE
| B_DIRECTORY_NODE
| B_SYMLINK_NODE
, true,
1462 &message
, NULL
, true, true);
1469 GrepWindow::_OnOpenPanelCancel()
1471 entry_ref panelDirRef
;
1472 fFilePanel
->GetPanelDirectory(&panelDirRef
);
1473 BPath
path(&panelDirRef
);
1474 fModel
->fFilePanelPath
= path
.Path();
1481 GrepWindow::_OnSelectAll(BMessage
* message
)
1483 BMessenger
messenger(fSearchResults
);
1484 messenger
.SendMessage(B_SELECT_ALL
);
1489 GrepWindow::_OnNewWindow()
1492 // we don't want GrepWindow::InitRefsReceived()
1493 // to mess with the refs of the current window
1495 cloneRefs
= fModel
->fSelectedFiles
;
1496 cloneRefs
.AddRef("dir_ref", &(fModel
->fDirectory
));
1498 new GrepWindow(&cloneRefs
);
1506 GrepWindow::_ModelChanged()
1510 _StopNodeMonitoring();
1516 GrepWindow::_OpenInPe(const entry_ref
&ref
, int32 lineNum
)
1518 BMessage
message('Cmdl');
1519 message
.AddRef("refs", &ref
);
1522 message
.AddInt32("line", lineNum
);
1525 if (be_roster
->FindApp(PE_SIGNATURE
, &pe
) != B_OK
)
1528 if (be_roster
->IsRunning(&pe
)) {
1529 BMessenger
msngr(NULL
, be_roster
->TeamFor(&pe
));
1530 if (msngr
.SendMessage(&message
) != B_OK
)
1533 if (be_roster
->Launch(&pe
, &message
) != B_OK
)
1542 GrepWindow::_RemoveFolderListDuplicates(BList
* folderList
)
1544 if (folderList
== NULL
)
1547 int32 folderCount
= folderList
->CountItems();
1551 for (int32 x
= 0; x
< folderCount
; x
++) {
1552 BPath
* path
= static_cast<BPath
*>(folderList
->ItemAt(x
));
1553 folderX
= path
->Path();
1555 for (int32 y
= x
+ 1; y
< folderCount
; y
++) {
1556 path
= static_cast<BPath
*>(folderList
->ItemAt(y
));
1557 folderY
= path
->Path();
1558 if (folderX
== folderY
) {
1559 delete static_cast<BPath
*>(folderList
->RemoveItem(y
));
1569 GrepWindow::_OpenFoldersInTracker(BList
* folderList
)
1571 status_t status
= B_OK
;
1572 BMessage
refsMsg(B_REFS_RECEIVED
);
1574 int32 folderCount
= folderList
->CountItems();
1575 for (int32 index
= 0; index
< folderCount
; index
++) {
1576 BPath
* path
= static_cast<BPath
*>(folderList
->ItemAt(index
));
1578 entry_ref folderRef
;
1579 status
= get_ref_for_path(path
->Path(), &folderRef
);
1583 status
= refsMsg
.AddRef("refs", &folderRef
);
1588 status
= be_roster
->Launch(TRACKER_SIGNATURE
, &refsMsg
);
1589 if (status
!= B_OK
&& status
!= B_ALREADY_RUNNING
)
1597 GrepWindow::_AreAllFoldersOpenInTracker(BList
* folderList
)
1599 // Compare the folders we want open in Tracker to
1600 // the actual Tracker windows currently open.
1602 // We build a list of open Tracker windows, and compare
1603 // it to the list of folders we want open in Tracker.
1605 // If all folders exists in the list of Tracker windows
1608 status_t status
= B_OK
;
1609 BMessenger
trackerMessenger(TRACKER_SIGNATURE
);
1610 BMessage sendMessage
;
1611 BMessage replyMessage
;
1614 if (!trackerMessenger
.IsValid())
1617 for (int32 count
= 1; ; count
++) {
1618 sendMessage
.MakeEmpty();
1619 replyMessage
.MakeEmpty();
1621 sendMessage
.what
= B_GET_PROPERTY
;
1622 sendMessage
.AddSpecifier("Path");
1623 sendMessage
.AddSpecifier("Poses");
1624 sendMessage
.AddSpecifier("Window", count
);
1626 status
= trackerMessenger
.SendMessage(&sendMessage
, &replyMessage
);
1630 entry_ref
* trackerRef
= new (nothrow
) entry_ref
;
1631 status
= replyMessage
.FindRef("result", trackerRef
);
1632 if (status
!= B_OK
|| !windowList
.AddItem(trackerRef
)) {
1638 int32 folderCount
= folderList
->CountItems();
1639 int32 windowCount
= windowList
.CountItems();
1643 entry_ref
* windowRef
;
1644 BString folderString
;
1645 BString windowString
;
1646 bool result
= false;
1648 if (folderCount
> windowCount
) {
1649 // at least one folder is not open in Tracker
1653 // Loop over the two lists and see if all folders exist as window
1654 for (int32 x
= 0; x
< folderCount
; x
++) {
1655 for (int32 y
= 0; y
< windowCount
; y
++) {
1657 folderPath
= static_cast<BPath
*>(folderList
->ItemAt(x
));
1658 windowRef
= static_cast<entry_ref
*>(windowList
.ItemAt(y
));
1660 if (folderPath
== NULL
)
1663 if (windowRef
== NULL
)
1666 folderString
= folderPath
->Path();
1671 if (entry
.SetTo(windowRef
) == B_OK
&& path
.SetTo(&entry
) == B_OK
) {
1673 windowString
= path
.Path();
1675 if (folderString
== windowString
) {
1683 result
= found
== folderCount
;
1686 // delete list of window entry_refs
1687 for (int32 x
= 0; x
< windowCount
; x
++)
1688 delete static_cast<entry_ref
*>(windowList
.ItemAt(x
));
1695 GrepWindow::_SelectFilesInTracker(BList
* folderList
, BMessage
* refsMessage
)
1697 // loops over Tracker windows, find each windowRef,
1698 // extract the refs that are children of windowRef,
1699 // add refs to selection-message
1701 status_t status
= B_OK
;
1702 BMessenger
trackerMessenger(TRACKER_SIGNATURE
);
1703 BMessage windowSendMessage
;
1704 BMessage windowReplyMessage
;
1705 BMessage selectionSendMessage
;
1706 BMessage selectionReplyMessage
;
1708 if (!trackerMessenger
.IsValid())
1711 // loop over Tracker windows
1712 for (int32 windowCount
= 1; ; windowCount
++) {
1714 windowSendMessage
.MakeEmpty();
1715 windowReplyMessage
.MakeEmpty();
1717 windowSendMessage
.what
= B_GET_PROPERTY
;
1718 windowSendMessage
.AddSpecifier("Path");
1719 windowSendMessage
.AddSpecifier("Poses");
1720 windowSendMessage
.AddSpecifier("Window", windowCount
);
1722 status
= trackerMessenger
.SendMessage(&windowSendMessage
,
1723 &windowReplyMessage
);
1728 entry_ref windowRef
;
1729 status
= windowReplyMessage
.FindRef("result", &windowRef
);
1733 int32 folderCount
= folderList
->CountItems();
1735 // loop over folders in folderList
1736 for (int32 x
= 0; x
< folderCount
; x
++) {
1737 BPath
* folderPath
= static_cast<BPath
*>(folderList
->ItemAt(x
));
1738 if (folderPath
== NULL
)
1741 BString folderString
= folderPath
->Path();
1745 BString windowString
;
1747 status
= windowEntry
.SetTo(&windowRef
);
1751 status
= windowPath
.SetTo(&windowEntry
);
1755 windowString
= windowPath
.Path();
1757 // if match, loop over items in refsMessage
1758 // and add those that live in window/folder
1759 // to a selection message
1761 if (windowString
== folderString
) {
1762 selectionSendMessage
.MakeEmpty();
1763 selectionSendMessage
.what
= B_SET_PROPERTY
;
1764 selectionReplyMessage
.MakeEmpty();
1766 // loop over refs and add to message
1768 for (int32 index
= 0; ; index
++) {
1769 status
= refsMessage
->FindRef("refs", index
, &ref
);
1773 BDirectory
directory(&windowRef
);
1775 if (directory
.Contains(&entry
))
1776 selectionSendMessage
.AddRef("data", &ref
);
1779 // finish selection message
1780 selectionSendMessage
.AddSpecifier("Selection");
1781 selectionSendMessage
.AddSpecifier("Poses");
1782 selectionSendMessage
.AddSpecifier("Window", windowCount
);
1784 trackerMessenger
.SendMessage(&selectionSendMessage
,
1785 &selectionReplyMessage
);