vfs: check userland buffers before reading them.
[haiku.git] / src / kits / tracker / FindPanel.cpp
blob2951a4d1d698cbc6a31c5cf07d0de28d4b1604c9
1 /*
2 Open Tracker License
4 Terms and Conditions
6 Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
8 Permission is hereby granted, free of charge, to any person obtaining a copy of
9 this software and associated documentation files (the "Software"), to deal in
10 the Software without restriction, including without limitation the rights to
11 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
12 of the Software, and to permit persons to whom the Software is furnished to do
13 so, subject to the following conditions:
15 The above copyright notice and this permission notice applies to all licensees
16 and shall be included in all copies or substantial portions of the Software.
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY,
20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
22 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
23 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 Except as contained in this notice, the name of Be Incorporated shall not be
26 used in advertising or otherwise to promote the sale, use or other dealings in
27 this Software without prior written authorization from Be Incorporated.
29 Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks
30 of Be Incorporated in the United States and other countries. Other brand product
31 names are registered trademarks or trademarks of their respective holders.
32 All rights reserved.
36 #include "FindPanel.h"
38 #include <utility>
40 #include <errno.h>
41 #include <fs_attr.h>
42 #include <parsedate.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <strings.h>
47 #include <Application.h>
48 #include <Box.h>
49 #include <Button.h>
50 #include <Catalog.h>
51 #include <CheckBox.h>
52 #include <ControlLook.h>
53 #include <Debug.h>
54 #include <Directory.h>
55 #include <FindDirectory.h>
56 #include <File.h>
57 #include <FilePanel.h>
58 #include <GroupLayout.h>
59 #include <InterfaceDefs.h>
60 #include <LayoutBuilder.h>
61 #include <Locale.h>
62 #include <MenuField.h>
63 #include <MenuItem.h>
64 #include <Mime.h>
65 #include <NodeInfo.h>
66 #include <PopUpMenu.h>
67 #include <Path.h>
68 #include <Query.h>
69 #include <SeparatorView.h>
70 #include <Size.h>
71 #include <SpaceLayoutItem.h>
72 #include <TextControl.h>
73 #include <TextView.h>
74 #include <View.h>
75 #include <Volume.h>
76 #include <VolumeRoster.h>
78 #include "Attributes.h"
79 #include "AutoLock.h"
80 #include "Commands.h"
81 #include "ContainerWindow.h"
82 #include "FSUtils.h"
83 #include "FunctionObject.h"
84 #include "IconMenuItem.h"
85 #include "MimeTypes.h"
86 #include "Tracker.h"
89 #undef B_TRANSLATION_CONTEXT
90 #define B_TRANSLATION_CONTEXT "FindPanel"
93 const char* kAllMimeTypes = "mime/ALLTYPES";
95 const BRect kInitialRect(0, 0, 0, 0);
96 const int32 kInitialAttrModeWindowHeight = 140;
97 const int32 kIncrementPerAttribute = 30;
98 const float kMoreOptionsDelta = 20;
100 const uint32 kMoreOptionsMessage = 'mrop';
101 const uint32 kNameModifiedMessage = 'nmmd';
102 const uint32 kSwitchToQueryTemplate = 'swqt';
103 const uint32 kRunSaveAsTemplatePanel = 'svtm';
104 const uint32 kLatchChanged = 'ltch';
106 const char* kDragNDropTypes[] = {
107 B_QUERY_MIMETYPE,
108 B_QUERY_TEMPLATE_MIMETYPE
110 static const char* kDragNDropActionSpecifiers[] = {
111 B_TRANSLATE_MARK("Create a Query"),
112 B_TRANSLATE_MARK("Create a Query template")
115 const uint32 kAttachFile = 'attf';
117 const int32 operators[] = {
118 B_CONTAINS,
119 B_EQ,
120 B_NE,
121 B_BEGINS_WITH,
122 B_ENDS_WITH,
123 B_GE,
124 B_LE
127 static const char* operatorLabels[] = {
128 B_TRANSLATE_MARK("contains"),
129 B_TRANSLATE_MARK("is"),
130 B_TRANSLATE_MARK("is not"),
131 B_TRANSLATE_MARK("starts with"),
132 B_TRANSLATE_MARK("ends with"),
133 B_TRANSLATE_MARK("greater than"),
134 B_TRANSLATE_MARK("less than"),
135 B_TRANSLATE_MARK("before"),
136 B_TRANSLATE_MARK("after")
140 namespace BPrivate {
142 class MostUsedNames {
143 public:
144 MostUsedNames(const char* fileName, const char* directory,
145 int32 maxCount = 5);
146 ~MostUsedNames();
148 bool ObtainList(BList* list);
149 void ReleaseList();
151 void AddName(const char*);
153 protected:
154 struct list_entry {
155 char* name;
156 int32 count;
159 static int CompareNames(const void* a, const void* b);
160 void LoadList();
161 void UpdateList();
163 const char* fFileName;
164 const char* fDirectory;
165 bool fLoaded;
166 mutable Benaphore fLock;
167 BList fList;
168 int32 fCount;
172 MostUsedNames gMostUsedMimeTypes("MostUsedMimeTypes", "Tracker");
175 // #pragma mark - MoreOptionsStruct
178 void
179 MoreOptionsStruct::EndianSwap(void*)
181 // noop for now
185 void
186 MoreOptionsStruct::SetQueryTemporary(BNode* node, bool on)
188 MoreOptionsStruct saveMoreOptions;
190 if (ReadAttr(node, kAttrQueryMoreOptions, kAttrQueryMoreOptionsForeign,
191 B_RAW_TYPE, 0, &saveMoreOptions, sizeof(MoreOptionsStruct),
192 &MoreOptionsStruct::EndianSwap) == B_OK) {
193 saveMoreOptions.temporary = on;
194 node->WriteAttr(kAttrQueryMoreOptions, B_RAW_TYPE, 0, &saveMoreOptions,
195 sizeof(saveMoreOptions));
200 bool
201 MoreOptionsStruct::QueryTemporary(const BNode* node)
203 MoreOptionsStruct saveMoreOptions;
205 if (ReadAttr(node, kAttrQueryMoreOptions, kAttrQueryMoreOptionsForeign,
206 B_RAW_TYPE, 0, &saveMoreOptions, sizeof(MoreOptionsStruct),
207 &MoreOptionsStruct::EndianSwap) == kReadAttrFailed) {
208 return false;
211 return saveMoreOptions.temporary;
215 // #pragma mark - FindWindow
218 FindWindow::FindWindow(const entry_ref* newRef, bool editIfTemplateOnly)
220 BWindow(kInitialRect, B_TRANSLATE("Find"), B_TITLED_WINDOW,
221 B_NOT_RESIZABLE | B_NOT_ZOOMABLE | B_CLOSE_ON_ESCAPE
222 | B_AUTO_UPDATE_SIZE_LIMITS),
223 fFile(TryOpening(newRef)),
224 fFromTemplate(false),
225 fEditTemplateOnly(false),
226 fSaveAsTemplatePanel(NULL)
228 if (fFile != NULL) {
229 fRef = *newRef;
230 if (editIfTemplateOnly) {
231 char type[B_MIME_TYPE_LENGTH];
232 if (BNodeInfo(fFile).GetType(type) == B_OK
233 && strcasecmp(type, B_QUERY_TEMPLATE_MIMETYPE) == 0) {
234 fEditTemplateOnly = true;
235 SetTitle(B_TRANSLATE("Edit Query template"));
238 } else {
239 // no initial query, fall back on the default query template
240 BEntry entry;
241 GetDefaultQuery(entry);
242 entry.GetRef(&fRef);
244 if (entry.Exists())
245 fFile = TryOpening(&fRef);
246 else {
247 // no default query template yet
248 fFile = new BFile(&entry, O_RDWR | O_CREAT);
249 if (fFile->InitCheck() < B_OK) {
250 delete fFile;
251 fFile = NULL;
252 } else
253 SaveQueryAttributes(fFile, true);
257 fFromTemplate = IsQueryTemplate(fFile);
259 fBackground = new FindPanel(fFile, this, fFromTemplate,
260 fEditTemplateOnly);
261 SetLayout(new BGroupLayout(B_VERTICAL));
262 GetLayout()->AddView(fBackground);
263 CenterOnScreen();
267 FindWindow::~FindWindow()
269 delete fFile;
270 delete fSaveAsTemplatePanel;
274 BFile*
275 FindWindow::TryOpening(const entry_ref* ref)
277 if (!ref)
278 return NULL;
280 BFile* result = new BFile(ref, O_RDWR);
281 if (result->InitCheck() != B_OK) {
282 delete result;
283 result = NULL;
285 return result;
289 void
290 FindWindow::GetDefaultQuery(BEntry& entry)
292 BPath path;
293 if (find_directory(B_USER_DIRECTORY, &path, true) == B_OK
294 && path.Append("queries") == B_OK
295 && (mkdir(path.Path(), 0777) == 0 || errno == EEXIST)) {
296 BDirectory directory(path.Path());
297 entry.SetTo(&directory, "default");
302 bool
303 FindWindow::IsQueryTemplate(BNode* file)
305 char type[B_MIME_TYPE_LENGTH];
306 if (BNodeInfo(file).GetType(type) != B_OK)
307 return false;
309 return strcasecmp(type, B_QUERY_TEMPLATE_MIMETYPE) == 0;
313 void
314 FindWindow::SwitchToTemplate(const entry_ref* ref)
316 try {
317 BEntry entry(ref, true);
318 BFile templateFile(&entry, O_RDONLY);
320 ThrowOnInitCheckError(&templateFile);
321 fBackground->SwitchToTemplate(&templateFile);
322 } catch (...) {
328 const char*
329 FindWindow::QueryName() const
331 if (fFromTemplate) {
332 if (!fQueryNameFromTemplate.Length()) {
333 fFile->ReadAttrString(kAttrQueryTemplateName,
334 &fQueryNameFromTemplate);
337 return fQueryNameFromTemplate.String();
339 if (!fFile)
340 return "";
342 return fRef.name;
346 static const char*
347 MakeValidFilename(BString& string)
349 // make a file name that is legal under bfs and hfs - possibly could
350 // add code here to accomodate FAT32 etc. too
351 if (string.Length() > B_FILE_NAME_LENGTH - 1) {
352 string.Truncate(B_FILE_NAME_LENGTH - 4);
353 string += B_UTF8_ELLIPSIS;
356 // replace slashes
357 int32 length = string.Length();
358 char* buf = string.LockBuffer(length);
359 for (int32 index = length; index-- > 0;) {
360 if (buf[index] == '/' /*|| buf[index] == ':'*/)
361 buf[index] = '_';
363 string.UnlockBuffer(length);
365 return string.String();
369 void
370 FindWindow::GetPredicateString(BString& predicate, bool& dynamicDate)
372 BQuery query;
373 switch (fBackground->Mode()) {
374 case kByNameItem:
375 fBackground->GetByNamePredicate(&query);
376 query.GetPredicate(&predicate);
377 break;
379 case kByFormulaItem:
381 BTextControl* textControl
382 = dynamic_cast<BTextControl*>(FindView("TextControl"));
383 if (textControl != NULL)
384 predicate.SetTo(textControl->Text(), 1023);
385 break;
388 case kByAttributeItem:
389 fBackground->GetByAttrPredicate(&query, dynamicDate);
390 query.GetPredicate(&predicate);
391 break;
396 void
397 FindWindow::GetDefaultName(BString& name)
399 fBackground->GetDefaultName(name);
401 time_t timeValue = time(0);
402 char namebuf[B_FILE_NAME_LENGTH];
404 tm timeData;
405 localtime_r(&timeValue, &timeData);
407 strftime(namebuf, 32, " - %b %d, %I:%M:%S %p", &timeData);
408 name << namebuf;
410 MakeValidFilename(name);
414 void
415 FindWindow::SaveQueryAttributes(BNode* file, bool queryTemplate)
417 ThrowOnError(BNodeInfo(file).SetType(
418 queryTemplate ? B_QUERY_TEMPLATE_MIMETYPE : B_QUERY_MIMETYPE));
420 // save date/time info for recent query support and transient query killer
421 int32 currentTime = (int32)time(0);
422 file->WriteAttr(kAttrQueryLastChange, B_INT32_TYPE, 0, &currentTime,
423 sizeof(int32));
424 int32 tmp = 1;
425 file->WriteAttr("_trk/recentQuery", B_INT32_TYPE, 0, &tmp, sizeof(int32));
429 status_t
430 FindWindow::SaveQueryAsAttributes(BNode* file, BEntry* entry,
431 bool queryTemplate, const BMessage* oldAttributes,
432 const BPoint* oldLocation)
434 if (oldAttributes != NULL) {
435 // revive old window settings
436 BContainerWindow::SetLayoutState(file, oldAttributes);
439 if (oldLocation != NULL) {
440 // and the file's location
441 FSSetPoseLocation(entry, *oldLocation);
444 BNodeInfo(file).SetType(queryTemplate
445 ? B_QUERY_TEMPLATE_MIMETYPE : B_QUERY_MIMETYPE);
447 BString predicate;
448 bool dynamicDate;
449 GetPredicateString(predicate, dynamicDate);
450 file->WriteAttrString(kAttrQueryString, &predicate);
452 if (dynamicDate) {
453 file->WriteAttr(kAttrDynamicDateQuery, B_BOOL_TYPE, 0, &dynamicDate,
454 sizeof(dynamicDate));
457 int32 tmp = 1;
458 file->WriteAttr("_trk/recentQuery", B_INT32_TYPE, 0, &tmp, sizeof(int32));
460 // write some useful info to help locate the volume to query
461 BMenuItem* item = fBackground->VolMenu()->FindMarked();
462 if (item != NULL) {
463 dev_t dev;
464 BMessage message;
465 uint32 count = 0;
467 int32 itemCount = fBackground->VolMenu()->CountItems();
468 for (int32 index = 2; index < itemCount; index++) {
469 BMenuItem* item = fBackground->VolMenu()->ItemAt(index);
471 if (!item->IsMarked())
472 continue;
474 if (item->Message()->FindInt32("device", &dev) != B_OK)
475 continue;
477 count++;
478 BVolume volume(dev);
479 EmbedUniqueVolumeInfo(&message, &volume);
482 if (count > 0) {
483 // do we need to embed any volumes
484 ssize_t size = message.FlattenedSize();
485 BString buffer;
486 status_t result = message.Flatten(buffer.LockBuffer(size), size);
487 if (result == B_OK) {
488 if (file->WriteAttr(kAttrQueryVolume, B_MESSAGE_TYPE, 0,
489 buffer.String(), (size_t)size) != size) {
490 return B_IO_ERROR;
493 buffer.UnlockBuffer();
495 // default to query for everything
498 fBackground->SaveWindowState(file, fEditTemplateOnly);
499 // write out all the dialog items as attributes so that the query can
500 // be reopened and edited later
502 BView* focusedItem = CurrentFocus();
503 if (focusedItem != NULL) {
504 // text controls never get the focus, their internal text views do
505 BView* parent = focusedItem->Parent();
506 if (dynamic_cast<BTextControl*>(parent) != NULL)
507 focusedItem = parent;
509 // write out the current focus and, if text control, selection
510 BString name(focusedItem->Name());
511 file->WriteAttrString("_trk/focusedView", &name);
512 BTextControl* textControl = dynamic_cast<BTextControl*>(focusedItem);
513 if (textControl != NULL && textControl->TextView() != NULL) {
514 int32 selStart;
515 int32 selEnd;
516 textControl->TextView()->GetSelection(&selStart, &selEnd);
517 file->WriteAttr("_trk/focusedSelStart", B_INT32_TYPE, 0,
518 &selStart, sizeof(selStart));
519 file->WriteAttr("_trk/focusedSelEnd", B_INT32_TYPE, 0,
520 &selEnd, sizeof(selEnd));
524 return B_OK;
528 void
529 FindWindow::Save()
531 FindSaveCommon(false);
533 // close the find panel
534 PostMessage(B_QUIT_REQUESTED);
538 void
539 FindWindow::Find()
541 if (!FindSaveCommon(true)) {
542 // have to wait for the node monitor to force old query to close
543 // to avoid a race condition
544 TTracker* tracker = dynamic_cast<TTracker*>(be_app);
545 ASSERT(tracker != NULL);
547 for (int32 timeOut = 0; ; timeOut++) {
548 if (tracker != NULL && !tracker->EntryHasWindowOpen(&fRef)) {
549 // window quit, we can post refs received to open a
550 // new copy
551 break;
554 // PRINT(("waiting for query window to quit, %d\n", timeOut));
555 if (timeOut == 5000) {
556 // the old query window would not quit for some reason
557 TRESPASS();
558 PostMessage(B_QUIT_REQUESTED);
559 return;
561 snooze(1000);
565 int32 currentTime = (int32)time(0);
566 fFile->WriteAttr(kAttrQueryLastChange, B_INT32_TYPE, 0, &currentTime,
567 sizeof(int32));
569 // tell the tracker about it
570 BMessage message(B_REFS_RECEIVED);
571 message.AddRef("refs", &fRef);
572 be_app->PostMessage(&message);
574 // close the find panel
575 PostMessage(B_QUIT_REQUESTED);
579 bool
580 FindWindow::FindSaveCommon(bool find)
582 // figure out what we need to do
583 bool readFromOldFile = fFile != NULL;
584 bool replaceOriginal = fFile && (!fFromTemplate || fEditTemplateOnly);
585 bool keepPoseLocation = replaceOriginal;
586 bool newFile = !fFile || (fFromTemplate && !fEditTemplateOnly);
588 BEntry entry;
589 BMessage oldAttributes;
590 BPoint location;
591 bool hadLocation = false;
592 const char* userSpecifiedName = fBackground->UserSpecifiedName();
594 if (readFromOldFile) {
595 entry.SetTo(&fRef);
596 BContainerWindow::GetLayoutState(fFile, &oldAttributes);
597 hadLocation = FSGetPoseLocation(fFile, &location);
600 if (replaceOriginal) {
601 fFile->Unset();
602 entry.Remove();
603 // remove the current entry - need to do this to quit the
604 // running query and to close the corresponding window
606 if (userSpecifiedName != NULL && !fEditTemplateOnly) {
607 // change the name of the old query per users request
608 fRef.set_name(userSpecifiedName);
609 entry.SetTo(&fRef);
613 if (newFile) {
614 // create query file in the user's directory
615 BPath path;
616 // there might be no queries folder yet, create one
617 if (find_directory(B_USER_DIRECTORY, &path, true) == B_OK
618 && path.Append("queries") == B_OK
619 && (mkdir(path.Path(), 0777) == 0 || errno == EEXIST)) {
620 // either use the user specified name, or go with the name
621 // generated from the predicate, etc.
622 BString name;
623 if (userSpecifiedName == NULL)
624 GetDefaultName(name);
625 else
626 name << userSpecifiedName;
628 if (path.Append(name.String()) == B_OK) {
629 entry.SetTo(path.Path());
630 entry.Remove();
631 entry.GetRef(&fRef);
636 fFile = new BFile(&entry, O_RDWR | O_CREAT);
637 ASSERT(fFile->InitCheck() == B_OK);
639 SaveQueryAsAttributes(fFile, &entry, !find, newFile ? 0 : &oldAttributes,
640 (hadLocation && keepPoseLocation) ? &location : 0);
642 return newFile;
646 void
647 FindWindow::MessageReceived(BMessage* message)
649 switch (message->what) {
650 case kFindButton:
651 Find();
652 break;
654 case kSaveButton:
655 Save();
656 break;
658 case kAttachFile:
660 entry_ref dir;
661 const char* name;
662 bool queryTemplate;
663 if (message->FindString("name", &name) == B_OK
664 && message->FindRef("directory", &dir) == B_OK
665 && message->FindBool("template", &queryTemplate)
666 == B_OK) {
667 delete fFile;
668 fFile = NULL;
669 BDirectory directory(&dir);
670 BEntry entry(&directory, name);
671 entry_ref tmpRef;
672 entry.GetRef(&tmpRef);
673 fFile = TryOpening(&tmpRef);
674 if (fFile != NULL) {
675 fRef = tmpRef;
676 SaveQueryAsAttributes(fFile, &entry, queryTemplate,
677 0, 0);
678 // try to save whatever state we aleady have
679 // to the new query so that if the user
680 // opens it before runing it from the find panel,
681 // something reasonable happens
685 break;
687 case kSwitchToQueryTemplate:
689 entry_ref ref;
690 if (message->FindRef("refs", &ref) == B_OK)
691 SwitchToTemplate(&ref);
693 break;
696 case kRunSaveAsTemplatePanel:
697 if (fSaveAsTemplatePanel != NULL)
698 fSaveAsTemplatePanel->Show();
699 else {
700 BMessenger panel(BackgroundView());
701 fSaveAsTemplatePanel = new BFilePanel(B_SAVE_PANEL, &panel);
702 fSaveAsTemplatePanel->SetSaveText(
703 B_TRANSLATE("Query template"));
704 fSaveAsTemplatePanel->Window()->SetTitle(
705 B_TRANSLATE("Save as Query template:"));
706 fSaveAsTemplatePanel->Show();
708 break;
710 default:
711 _inherited::MessageReceived(message);
712 break;
717 // #pragma mark - FindPanel
720 FindPanel::FindPanel(BFile* node, FindWindow* parent, bool fromTemplate,
721 bool editTemplateOnly)
723 BView("MainView", B_WILL_DRAW),
724 fMode(kByNameItem),
725 fAttrGrid(NULL),
726 fDraggableIcon(NULL)
728 SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
729 SetLowUIColor(ViewUIColor());
731 uint32 initialMode = InitialMode(node);
733 BMessenger self(this);
734 fRecentQueries = new BPopUpMenu(B_TRANSLATE("Recent queries"), false,
735 false);
736 AddRecentQueries(fRecentQueries, true, &self, kSwitchToQueryTemplate);
738 // add popup for mime types
739 fMimeTypeMenu = new BPopUpMenu("MimeTypeMenu");
740 fMimeTypeMenu->SetRadioMode(false);
741 AddMimeTypesToMenu();
743 fMimeTypeField = new BMenuField("MimeTypeMenu", "", fMimeTypeMenu);
744 fMimeTypeField->SetDivider(0.0f);
745 fMimeTypeField->MenuItem()->SetLabel(B_TRANSLATE("All files and folders"));
746 // add popup for search criteria
747 fSearchModeMenu = new BPopUpMenu("searchMode");
748 fSearchModeMenu->AddItem(new BMenuItem(B_TRANSLATE("by name"),
749 new BMessage(kByNameItem)));
750 fSearchModeMenu->AddItem(new BMenuItem(B_TRANSLATE("by attribute"),
751 new BMessage(kByAttributeItem)));
752 fSearchModeMenu->AddItem(new BMenuItem(B_TRANSLATE("by formula"),
753 new BMessage(kByFormulaItem)));
755 fSearchModeMenu->ItemAt(initialMode == kByNameItem ? 0 :
756 (initialMode == kByAttributeItem ? 1 : 2))->SetMarked(true);
757 // mark the appropriate mode
758 BMenuField* searchModeField = new BMenuField("", "", fSearchModeMenu);
759 searchModeField->SetDivider(0.0f);
761 // add popup for volume list
762 fVolMenu = new BPopUpMenu("", false, false);
763 BMenuField* volumeField = new BMenuField("", B_TRANSLATE("On"), fVolMenu);
764 volumeField->SetDivider(volumeField->StringWidth(volumeField->Label()) + 8);
765 AddVolumes(fVolMenu);
767 if (!editTemplateOnly) {
768 BPoint draggableIconOrigin(0, 0);
769 BMessage dragNDropMessage(B_SIMPLE_DATA);
770 dragNDropMessage.AddInt32("be:actions", B_COPY_TARGET);
771 dragNDropMessage.AddString("be:types", B_FILE_MIME_TYPE);
772 dragNDropMessage.AddString("be:filetypes", kDragNDropTypes[0]);
773 dragNDropMessage.AddString("be:filetypes", kDragNDropTypes[1]);
774 dragNDropMessage.AddString("be:actionspecifier",
775 B_TRANSLATE_NOCOLLECT(kDragNDropActionSpecifiers[0]));
776 dragNDropMessage.AddString("be:actionspecifier",
777 B_TRANSLATE_NOCOLLECT(kDragNDropActionSpecifiers[1]));
779 BMessenger self(this);
780 BRect draggableRect = DraggableIcon::PreferredRect(draggableIconOrigin,
781 B_LARGE_ICON);
782 fDraggableIcon = new DraggableQueryIcon(draggableRect,
783 "saveHere", &dragNDropMessage, self,
784 B_FOLLOW_LEFT | B_FOLLOW_BOTTOM);
785 fDraggableIcon->SetExplicitMaxSize(
786 BSize(draggableRect.right - draggableRect.left,
787 draggableRect.bottom - draggableRect.top));
790 fQueryName = new BTextControl("query name", B_TRANSLATE("Query name:"),
791 "", NULL, B_WILL_DRAW | B_NAVIGABLE | B_NAVIGABLE_JUMP);
792 FillCurrentQueryName(fQueryName, parent);
793 fSearchTrashCheck = new BCheckBox("searchTrash",
794 B_TRANSLATE("Include trash"), NULL);
795 fTemporaryCheck = new BCheckBox("temporary",
796 B_TRANSLATE("Temporary"), NULL);
797 fTemporaryCheck->SetValue(B_CONTROL_ON);
799 BView* checkboxGroup = BLayoutBuilder::Group<>(B_HORIZONTAL)
800 .Add(fSearchTrashCheck)
801 .Add(fTemporaryCheck)
802 .View();
804 // add the more options collapsible pane
805 fMoreOptions = new BBox(B_NO_BORDER, BLayoutBuilder::Group<>()
806 .AddGrid(B_USE_SMALL_SPACING, B_USE_SMALL_SPACING)
807 .Add(fQueryName->CreateLabelLayoutItem(), 0, 0)
808 .Add(fQueryName->CreateTextViewLayoutItem(), 1, 0)
809 .Add(BSpaceLayoutItem::CreateHorizontalStrut(0), 0, 1)
810 .Add(checkboxGroup, 1, 1)
811 .End()
812 .View());
814 fLatch = new PaneSwitch("optionsLatch", true, B_WILL_DRAW);
815 fLatch->SetLabels(B_TRANSLATE("Fewer options"), B_TRANSLATE("More options"));
816 fLatch->SetValue(0);
817 fLatch->SetMessage(new BMessage(kLatchChanged));
818 fLatch->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT,
819 B_ALIGN_VERTICAL_CENTER));
820 fMoreOptions->Hide();
822 // add Search button
823 BButton* button;
824 if (editTemplateOnly) {
825 button = new BButton("save", B_TRANSLATE("Save"),
826 new BMessage(kSaveButton), B_FOLLOW_RIGHT + B_FOLLOW_BOTTOM);
827 } else {
828 button = new BButton("find", B_TRANSLATE("Search"),
829 new BMessage(kFindButton), B_FOLLOW_RIGHT + B_FOLLOW_BOTTOM);
831 button->MakeDefault(true);
833 BView* icon = fDraggableIcon;
834 if (icon == NULL) {
835 icon = new BBox("no draggable icon", B_WILL_DRAW, B_NO_BORDER);
836 icon->SetExplicitMaxSize(BSize(0, 0));
839 BView* mimeTypeFieldSpacer = new BBox("MimeTypeMenuSpacer", B_WILL_DRAW,
840 B_NO_BORDER);
841 mimeTypeFieldSpacer->SetExplicitMaxSize(BSize(0, 0));
843 BBox* queryControls = new BBox("Box");
844 queryControls->SetBorder(B_NO_BORDER);
846 BBox* queryBox = new BBox("Outer Controls");
847 queryBox->SetLabel(new BMenuField("RecentQueries", NULL, fRecentQueries));
849 BGroupView* queryBoxView = new BGroupView(B_VERTICAL,
850 B_USE_DEFAULT_SPACING);
851 queryBoxView->GroupLayout()->SetInsets(B_USE_DEFAULT_SPACING);
852 queryBox->AddChild(queryBoxView);
854 icon->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT, B_ALIGN_BOTTOM));
855 button->SetExplicitAlignment(BAlignment(B_ALIGN_RIGHT, B_ALIGN_BOTTOM));
857 BLayoutBuilder::Group<>(queryBoxView, B_VERTICAL, B_USE_DEFAULT_SPACING)
858 .SetInsets(B_USE_DEFAULT_SPACING)
859 .AddGroup(B_HORIZONTAL, B_USE_SMALL_SPACING)
860 .Add(fMimeTypeField)
861 .Add(mimeTypeFieldSpacer)
862 .Add(searchModeField)
863 .AddStrut(B_USE_DEFAULT_SPACING)
864 .Add(volumeField)
865 .End()
866 .Add(new BSeparatorView(B_HORIZONTAL, B_PLAIN_BORDER))
867 .Add(queryControls);
868 BLayoutBuilder::Group<>(this, B_VERTICAL, B_USE_DEFAULT_SPACING)
869 .SetInsets(B_USE_WINDOW_SPACING)
870 .Add(queryBox)
871 .AddGroup(B_HORIZONTAL, B_USE_DEFAULT_SPACING)
872 .Add(icon)
873 .AddGroup(B_VERTICAL)
874 .AddGroup(B_HORIZONTAL)
875 .Add(fLatch)
876 .Add(new BSeparatorView(B_HORIZONTAL, B_PLAIN_BORDER))
877 .End()
878 .Add(fMoreOptions)
879 .End()
880 .Add(button)
881 .End();
883 if (initialMode != kByAttributeItem)
884 AddByNameOrFormulaItems();
885 else
886 AddByAttributeItems(node);
888 ResizeMenuField(fMimeTypeField);
889 ResizeMenuField(searchModeField);
890 ResizeMenuField(volumeField);
894 FindPanel::~FindPanel()
899 void
900 FindPanel::AttachedToWindow()
902 FindWindow* findWindow = dynamic_cast<FindWindow*>(Window());
903 ASSERT(findWindow != NULL);
905 if (findWindow == NULL)
906 return;
908 BNode* node = findWindow->QueryNode();
909 fSearchModeMenu->SetTargetForItems(this);
910 fQueryName->SetTarget(this);
911 fLatch->SetTarget(this);
912 RestoreMimeTypeMenuSelection(node);
913 // preselect the mime we used the last time have to do it here
914 // because AddByAttributeItems will build different menus based
915 // on which mime type is preselected
916 RestoreWindowState(node);
918 if (!findWindow->CurrentFocus()) {
919 // try to pick a good focus if we restore to one already
920 BTextControl* textControl
921 = dynamic_cast<BTextControl*>(FindView("TextControl"));
922 if (textControl == NULL) {
923 // pick the last text control in the attribute view
924 BString title("TextEntry");
925 title << (fAttrGrid->CountRows() - 1);
926 textControl = dynamic_cast<BTextControl*>(FindView(title.String()));
928 if (textControl != NULL)
929 textControl->MakeFocus();
932 BButton* button = dynamic_cast<BButton*>(FindView("remove button"));
933 if (button != NULL)
934 button->SetTarget(this);
936 button = dynamic_cast<BButton*>(FindView("add button"));
937 if (button != NULL)
938 button->SetTarget(this);
940 fVolMenu->SetTargetForItems(this);
942 // set target for MIME type items
943 for (int32 index = MimeTypeMenu()->CountItems(); index-- > 2;) {
944 BMenu* submenu = MimeTypeMenu()->ItemAt(index)->Submenu();
945 if (submenu != NULL)
946 submenu->SetTargetForItems(this);
948 fMimeTypeMenu->SetTargetForItems(this);
950 BMenuItem* firstItem = fMimeTypeMenu->ItemAt(0);
951 if (firstItem != NULL)
952 firstItem->SetMarked(true);
954 if (fDraggableIcon != NULL)
955 fDraggableIcon->SetTarget(BMessenger(this));
957 fRecentQueries->SetTargetForItems(findWindow);
961 void
962 FindPanel::ResizeMenuField(BMenuField* menuField)
964 BSize size;
965 menuField->GetPreferredSize(&size.width, &size.height);
967 BMenu* menu = menuField->Menu();
969 float padding = 0.0f;
970 float width = 0.0f;
972 BMenuItem* markedItem = menu->FindMarked();
973 if (markedItem != NULL) {
974 if (markedItem->Submenu() != NULL) {
975 BMenuItem* markedSubItem = markedItem->Submenu()->FindMarked();
976 if (markedSubItem != NULL && markedSubItem->Label() != NULL) {
977 float labelWidth
978 = menuField->StringWidth(markedSubItem->Label());
979 padding = size.width - labelWidth;
981 } else if (markedItem->Label() != NULL) {
982 float labelWidth = menuField->StringWidth(markedItem->Label());
983 padding = size.width - labelWidth;
987 for (int32 index = menu->CountItems(); index-- > 0; ) {
988 BMenuItem* item = menu->ItemAt(index);
989 if (item->Label() != NULL)
990 width = std::max(width, menuField->StringWidth(item->Label()));
992 BMenu* submenu = item->Submenu();
993 if (submenu != NULL) {
994 for (int32 subIndex = submenu->CountItems(); subIndex-- > 0; ) {
995 BMenuItem* subItem = submenu->ItemAt(subIndex);
996 if (subItem->Label() == NULL)
997 continue;
999 width = std::max(width,
1000 menuField->StringWidth(subItem->Label()));
1005 float maxWidth = be_control_look->DefaultItemSpacing() * 20;
1006 size.width = std::min(width + padding, maxWidth);
1007 menuField->SetExplicitSize(size);
1010 static void
1011 PopUpMenuSetTitle(BMenu* menu, const char* title)
1013 // This should really be in BMenuField
1014 BMenu* bar = menu->Supermenu();
1016 ASSERT(bar);
1017 ASSERT(bar->ItemAt(0));
1018 if (bar == NULL || !bar->ItemAt(0))
1019 return;
1021 bar->ItemAt(0)->SetLabel(title);
1025 void
1026 FindPanel::ShowVolumeMenuLabel()
1028 if (fVolMenu->ItemAt(0)->IsMarked()) {
1029 // "all disks" selected
1030 PopUpMenuSetTitle(fVolMenu, fVolMenu->ItemAt(0)->Label());
1031 return;
1034 // find out if more than one items are marked
1035 int32 count = fVolMenu->CountItems();
1036 int32 countSelected = 0;
1037 BMenuItem* tmpItem = NULL;
1038 for (int32 index = 2; index < count; index++) {
1039 BMenuItem* item = fVolMenu->ItemAt(index);
1040 if (item->IsMarked()) {
1041 countSelected++;
1042 tmpItem = item;
1046 if (countSelected == 0) {
1047 // no disk selected, for now revert to search all disks
1048 // ToDo:
1049 // show no disks here and add a check that will not let the
1050 // query go if the user doesn't pick at least one
1051 fVolMenu->ItemAt(0)->SetMarked(true);
1052 PopUpMenuSetTitle(fVolMenu, fVolMenu->ItemAt(0)->Label());
1053 } else if (countSelected > 1)
1054 // if more than two disks selected, don't use the disk name
1055 // as a label
1056 PopUpMenuSetTitle(fVolMenu, B_TRANSLATE("multiple disks"));
1057 else {
1058 ASSERT(tmpItem);
1059 PopUpMenuSetTitle(fVolMenu, tmpItem->Label());
1064 void
1065 FindPanel::Draw(BRect)
1067 if (fAttrGrid == NULL)
1068 return;
1070 for (int32 index = 0; index < fAttrGrid->CountRows(); index++) {
1071 BMenuField* menuField
1072 = dynamic_cast<BMenuField*>(FindAttrView("MenuField", index));
1073 if (menuField == NULL)
1074 continue;
1076 BLayoutItem* stringViewLayoutItem = fAttrGrid->ItemAt(1, index);
1077 if (stringViewLayoutItem == NULL)
1078 continue;
1080 BMenu* menuFieldMenu = menuField->Menu();
1081 if (menuFieldMenu == NULL)
1082 continue;
1084 BMenuItem* item = menuFieldMenu->FindMarked();
1085 if (item == NULL || item->Submenu() == NULL
1086 || item->Submenu()->FindMarked() == NULL) {
1087 continue;
1090 if (stringViewLayoutItem == NULL) {
1091 stringViewLayoutItem = fAttrGrid->AddView(new BStringView("",
1092 item->Submenu()->FindMarked()->Label()), 1, index);
1093 stringViewLayoutItem->SetExplicitAlignment(BAlignment(B_ALIGN_RIGHT,
1094 B_ALIGN_VERTICAL_UNSET));
1097 if (stringViewLayoutItem != NULL) {
1098 BStringView* stringView
1099 = dynamic_cast<BStringView*>(stringViewLayoutItem->View());
1100 if (stringView != NULL) {
1101 BMenu* submenu = item->Submenu();
1102 if (submenu != NULL) {
1103 BMenuItem* selected = submenu->FindMarked();
1104 if (selected != NULL)
1105 stringView->SetText(selected->Label());
1113 void
1114 FindPanel::MessageReceived(BMessage* message)
1116 entry_ref dir;
1117 const char* name;
1118 BMenuItem* item;
1120 switch (message->what) {
1121 case kVolumeItem:
1123 // volume changed
1124 BMenuItem* invokedItem;
1125 dev_t dev;
1126 if (message->FindPointer("source", (void**)&invokedItem) != B_OK)
1127 return;
1129 if (message->FindInt32("device", &dev) != B_OK)
1130 break;
1132 BMenu* menu = invokedItem->Menu();
1133 ASSERT(menu);
1135 if (dev == -1) {
1136 // all disks selected, uncheck everything else
1137 int32 count = menu->CountItems();
1138 for (int32 index = 2; index < count; index++)
1139 menu->ItemAt(index)->SetMarked(false);
1141 // make all disks the title and check it
1142 PopUpMenuSetTitle(menu, menu->ItemAt(0)->Label());
1143 menu->ItemAt(0)->SetMarked(true);
1144 } else {
1145 // a specific volume selected, unmark "all disks"
1146 menu->ItemAt(0)->SetMarked(false);
1148 // toggle mark on invoked item
1149 int32 count = menu->CountItems();
1150 for (int32 index = 2; index < count; index++) {
1151 BMenuItem* item = menu->ItemAt(index);
1153 if (invokedItem == item) {
1154 // we just selected this
1155 bool wasMarked = item->IsMarked();
1156 item->SetMarked(!wasMarked);
1160 // make sure the right label is showing
1161 ShowVolumeMenuLabel();
1163 break;
1166 case kByAttributeItem:
1167 case kByNameItem:
1168 case kByFormulaItem:
1169 SwitchMode(message->what);
1170 break;
1172 case kAddItem:
1173 AddAttrRow();
1174 break;
1176 case kRemoveItem:
1177 RemoveAttrRow();
1178 break;
1180 case kMIMETypeItem:
1182 if (fMode == kByAttributeItem) {
1183 // the attributes for this type may be different
1184 RemoveAttrViewItems(false);
1185 AddAttrRow();
1188 BMenuItem* item;
1189 if (message->FindPointer("source", (void**)&item) == B_OK) {
1190 // don't add the "All files and folders" to the list
1191 if (fMimeTypeMenu->IndexOf(item) != 0)
1192 gMostUsedMimeTypes.AddName(item->Label());
1194 SetCurrentMimeType(item);
1197 break;
1200 case kNameModifiedMessage:
1201 // the query name was edited, make the query permanent
1202 fTemporaryCheck->SetValue(0);
1203 break;
1205 case kAttributeItem:
1206 if (message->FindPointer("source", (void**)&item) != B_OK)
1207 return;
1209 item->Menu()->Superitem()->SetMarked(true);
1210 Invalidate();
1211 break;
1213 case kAttributeItemMain:
1214 // in case someone selected just an attribute without the
1215 // comparator
1216 if (message->FindPointer("source", (void**)&item) != B_OK)
1217 return;
1219 if (item->Submenu()->ItemAt(0) != NULL)
1220 item->Submenu()->ItemAt(0)->SetMarked(true);
1222 Invalidate();
1223 break;
1225 case kLatchChanged:
1227 int32 value;
1228 if (message->FindInt32("be:value", &value) != B_OK)
1229 break;
1231 if (value == 0 && !fMoreOptions->IsHidden(this))
1232 fMoreOptions->Hide();
1233 else if (value == 1 && fMoreOptions->IsHidden(this))
1234 fMoreOptions->Show();
1236 break;
1239 case B_SAVE_REQUESTED:
1241 // finish saving query template from a SaveAs panel
1242 entry_ref ref;
1243 status_t error = message->FindRef("refs", &ref);
1245 if (error == B_OK) {
1246 // direct entry selected, convert to parent dir and name
1247 BEntry entry(&ref);
1248 error = entry.GetParent(&entry);
1249 if (error == B_OK) {
1250 entry.GetRef(&dir);
1251 name = ref.name;
1253 } else {
1254 // parent dir and name selected
1255 error = message->FindRef("directory", &dir);
1256 if (error == B_OK)
1257 error = message->FindString("name", &name);
1260 if (error == B_OK)
1261 SaveAsQueryOrTemplate(&dir, name, true);
1263 break;
1266 case B_COPY_TARGET:
1268 // finish drag&drop
1269 const char* str;
1270 const char* mimeType = NULL;
1271 const char* actionSpecifier = NULL;
1273 if (message->FindString("be:types", &str) == B_OK
1274 && strcasecmp(str, B_FILE_MIME_TYPE) == 0
1275 && (message->FindString("be:actionspecifier",
1276 &actionSpecifier) == B_OK
1277 || message->FindString("be:filetypes", &mimeType) == B_OK)
1278 && message->FindString("name", &name) == B_OK
1279 && message->FindRef("directory", &dir) == B_OK) {
1281 bool query = false;
1282 bool queryTemplate = false;
1284 if (actionSpecifier
1285 && strcasecmp(actionSpecifier,
1286 B_TRANSLATE_NOCOLLECT(
1287 kDragNDropActionSpecifiers[0])) == 0) {
1288 query = true;
1289 } else if (actionSpecifier
1290 && strcasecmp(actionSpecifier,
1291 B_TRANSLATE_NOCOLLECT(
1292 kDragNDropActionSpecifiers[1])) == 0) {
1293 queryTemplate = true;
1294 } else if (mimeType && strcasecmp(mimeType,
1295 kDragNDropTypes[0]) == 0) {
1296 query = true;
1297 } else if (mimeType && strcasecmp(mimeType,
1298 kDragNDropTypes[1]) == 0) {
1299 queryTemplate = true;
1302 if (query || queryTemplate)
1303 SaveAsQueryOrTemplate(&dir, name, queryTemplate);
1306 break;
1309 default:
1310 _inherited::MessageReceived(message);
1311 break;
1316 void
1317 FindPanel::SaveAsQueryOrTemplate(const entry_ref* dir, const char* name,
1318 bool queryTemplate)
1320 BDirectory directory(dir);
1321 BFile file(&directory, name, O_RDWR | O_CREAT | O_TRUNC);
1322 BNodeInfo(&file).SetType(queryTemplate
1323 ? B_QUERY_TEMPLATE_MIMETYPE : B_QUERY_MIMETYPE);
1325 BMessage attach(kAttachFile);
1326 attach.AddRef("directory", dir);
1327 attach.AddString("name", name);
1328 attach.AddBool("template", queryTemplate);
1329 Window()->PostMessage(&attach, 0);
1333 BView*
1334 FindPanel::FindAttrView(const char* name, int row) const
1336 for (int32 index = 0; index < fAttrGrid->CountColumns(); index++) {
1338 BLayoutItem* item = fAttrGrid->ItemAt(index, row);
1339 if (item == NULL)
1340 continue;
1342 BView* view = item->View();
1343 if (view == NULL)
1344 continue;
1346 view = view->FindView(name);
1347 if (view != NULL)
1348 return view;
1352 return NULL;
1355 void
1356 FindPanel::BuildAttrQuery(BQuery* query, bool &dynamicDate) const
1358 dynamicDate = false;
1360 // go through each attrview and add the attr and comparison info
1361 for (int32 index = 0; index < fAttrGrid->CountRows(); index++) {
1363 BString title;
1364 title << "TextEntry" << index;
1366 BTextControl* textControl = dynamic_cast<BTextControl*>(
1367 FindAttrView(title, index));
1368 if (textControl == NULL)
1369 return;
1371 BMenuField* menuField = dynamic_cast<BMenuField*>(
1372 FindAttrView("MenuField", index));
1373 if (menuField == NULL)
1374 return;
1376 BMenuItem* item = menuField->Menu()->FindMarked();
1377 if (item == NULL)
1378 continue;
1380 BMessage* message = item->Message();
1381 int32 type;
1382 if (message->FindInt32("type", &type) == B_OK) {
1384 const char* str;
1385 if (message->FindString("name", &str) == B_OK)
1386 query->PushAttr(str);
1387 else
1388 query->PushAttr(item->Label());
1390 switch (type) {
1391 case B_STRING_TYPE:
1392 query->PushString(textControl->Text(), true);
1393 break;
1395 case B_TIME_TYPE:
1397 int flags = 0;
1398 DEBUG_ONLY(time_t result =)
1399 parsedate_etc(textControl->Text(), -1,
1400 &flags);
1401 dynamicDate = (flags & PARSEDATE_RELATIVE_TIME) != 0;
1402 PRINT(("parsedate_etc - date is %srelative, %"
1403 B_PRIdTIME "\n",
1404 dynamicDate ? "" : "not ", result));
1406 query->PushDate(textControl->Text());
1407 break;
1410 case B_BOOL_TYPE:
1412 uint32 value;
1413 if (strcasecmp(textControl->Text(),
1414 "true") == 0) {
1415 value = 1;
1416 } else if (strcasecmp(textControl->Text(),
1417 "true") == 0) {
1418 value = 1;
1419 } else
1420 value = (uint32)atoi(textControl->Text());
1422 value %= 2;
1423 query->PushUInt32(value);
1424 break;
1427 case B_UINT8_TYPE:
1428 case B_UINT16_TYPE:
1429 case B_UINT32_TYPE:
1430 query->PushUInt32((uint32)StringToScalar(
1431 textControl->Text()));
1432 break;
1434 case B_INT8_TYPE:
1435 case B_INT16_TYPE:
1436 case B_INT32_TYPE:
1437 query->PushInt32((int32)StringToScalar(
1438 textControl->Text()));
1439 break;
1441 case B_UINT64_TYPE:
1442 query->PushUInt64((uint64)StringToScalar(
1443 textControl->Text()));
1444 break;
1446 case B_OFF_T_TYPE:
1447 case B_INT64_TYPE:
1448 query->PushInt64(StringToScalar(
1449 textControl->Text()));
1450 break;
1452 case B_FLOAT_TYPE:
1454 float floatVal;
1455 sscanf(textControl->Text(), "%f",
1456 &floatVal);
1457 query->PushFloat(floatVal);
1458 break;
1461 case B_DOUBLE_TYPE:
1463 double doubleVal;
1464 sscanf(textControl->Text(), "%lf",
1465 &doubleVal);
1466 query->PushDouble(doubleVal);
1467 break;
1472 query_op theOperator;
1473 BMenuItem* operatorItem = item->Submenu()->FindMarked();
1474 if (operatorItem && operatorItem->Message() != NULL) {
1475 operatorItem->Message()->FindInt32("operator",
1476 (int32*)&theOperator);
1477 query->PushOp(theOperator);
1478 } else
1479 query->PushOp(B_EQ);
1481 // add logic based on selection in Logic menufield
1482 if (index > 0) {
1483 menuField = dynamic_cast<BMenuField*>(
1484 FindAttrView("Logic", index - 1));
1485 if (menuField) {
1486 item = menuField->Menu()->FindMarked();
1487 if (item) {
1488 message = item->Message();
1489 message->FindInt32("combine", (int32*)&theOperator);
1490 query->PushOp(theOperator);
1492 } else
1493 query->PushOp(B_AND);
1499 void
1500 FindPanel::PushMimeType(BQuery* query) const
1502 const char* type;
1503 if (CurrentMimeType(&type) == NULL)
1504 return;
1506 if (strcmp(kAllMimeTypes, type)) {
1507 // add an asterisk if we are searching for a supertype
1508 char buffer[B_FILE_NAME_LENGTH];
1509 if (strchr(type, '/') == NULL) {
1510 strlcpy(buffer, type, sizeof(buffer));
1511 strlcat(buffer, "/*", sizeof(buffer));
1512 type = buffer;
1515 query->PushAttr(kAttrMIMEType);
1516 query->PushString(type);
1517 query->PushOp(B_EQ);
1518 query->PushOp(B_AND);
1523 void
1524 FindPanel::GetByAttrPredicate(BQuery* query, bool &dynamicDate) const
1526 ASSERT(Mode() == (int32)kByAttributeItem);
1527 BuildAttrQuery(query, dynamicDate);
1528 PushMimeType(query);
1532 void
1533 FindPanel::GetDefaultName(BString& name) const
1535 BTextControl* textControl = dynamic_cast<BTextControl*>(
1536 FindView("TextControl"));
1538 switch (Mode()) {
1539 case kByNameItem:
1540 if (textControl != NULL) {
1541 name.SetTo(B_TRANSLATE_COMMENT("Name = %name",
1542 "FindResultTitle"));
1543 name.ReplaceFirst("%name", textControl->Text());
1545 break;
1547 case kByFormulaItem:
1548 if (textControl != NULL) {
1549 name.SetTo(B_TRANSLATE_COMMENT("Formula %formula",
1550 "FindResultTitle"));
1551 name.ReplaceFirst("%formula", textControl->Text());
1553 break;
1555 case kByAttributeItem:
1557 BMenuItem* item = fMimeTypeMenu->FindMarked();
1558 if (item != NULL)
1559 name << item->Label() << ": ";
1561 for (int32 i = 0; i < fAttrGrid->CountRows(); i++) {
1562 GetDefaultAttrName(name, i);
1563 if (i + 1 < fAttrGrid->CountRows())
1564 name << ", ";
1566 break;
1572 const char*
1573 FindPanel::UserSpecifiedName() const
1575 if (fQueryName->Text()[0] == '\0')
1576 return NULL;
1578 return fQueryName->Text();
1582 void
1583 FindPanel::GetByNamePredicate(BQuery* query) const
1585 ASSERT(Mode() == (int32)kByNameItem);
1587 BTextControl* textControl
1588 = dynamic_cast<BTextControl*>(FindView("TextControl"));
1590 ASSERT(textControl != NULL);
1592 if (textControl == NULL)
1593 return;
1595 query->PushAttr("name");
1596 query->PushString(textControl->Text(), true);
1598 if (strstr(textControl->Text(), "*") != NULL) {
1599 // assume pattern is a regular expression, try doing an exact match
1600 query->PushOp(B_EQ);
1601 } else
1602 query->PushOp(B_CONTAINS);
1604 PushMimeType(query);
1608 void
1609 FindPanel::SwitchMode(uint32 mode)
1611 if (fMode == mode)
1612 // no work, bail
1613 return;
1615 uint32 oldMode = fMode;
1616 BString buffer;
1618 switch (mode) {
1619 case kByFormulaItem:
1621 if (oldMode == kByAttributeItem || oldMode == kByNameItem) {
1622 BQuery query;
1623 if (oldMode == kByAttributeItem) {
1624 bool dummy;
1625 GetByAttrPredicate(&query, dummy);
1626 } else
1627 GetByNamePredicate(&query);
1629 query.GetPredicate(&buffer);
1632 // fall-through
1633 case kByNameItem:
1635 fMode = mode;
1636 RemoveByAttributeItems();
1637 ShowOrHideMimeTypeMenu();
1638 AddByNameOrFormulaItems();
1640 if (buffer.Length() > 0) {
1641 ASSERT(mode == kByFormulaItem
1642 || oldMode == kByAttributeItem);
1643 BTextControl* textControl
1644 = dynamic_cast<BTextControl*>(FindView("TextControl"));
1645 if (textControl != NULL)
1646 textControl->SetText(buffer.String());
1648 break;
1651 case kByAttributeItem:
1653 fMode = mode;
1654 BTextControl* textControl
1655 = dynamic_cast<BTextControl*>(FindView("TextControl"));
1656 if (textControl != NULL) {
1657 textControl->RemoveSelf();
1658 delete textControl;
1661 ShowOrHideMimeTypeMenu();
1662 AddAttrRow();
1663 break;
1669 BMenuItem*
1670 FindPanel::CurrentMimeType(const char** type) const
1672 // search for marked item in the list
1673 BMenuItem* item = MimeTypeMenu()->FindMarked();
1675 if (item != NULL && MimeTypeMenu()->IndexOf(item) != 0
1676 && item->Submenu() == NULL) {
1677 // if it's one of the most used items, ignore it
1678 item = NULL;
1681 if (item == NULL) {
1682 for (int32 index = MimeTypeMenu()->CountItems(); index-- > 0;) {
1683 BMenu* submenu = MimeTypeMenu()->ItemAt(index)->Submenu();
1684 if (submenu != NULL && (item = submenu->FindMarked()) != NULL)
1685 break;
1689 if (type != NULL && item != NULL) {
1690 BMessage* message = item->Message();
1691 if (message == NULL)
1692 return NULL;
1694 if (message->FindString("mimetype", type) != B_OK)
1695 return NULL;
1697 return item;
1701 status_t
1702 FindPanel::SetCurrentMimeType(BMenuItem* item)
1704 // unmark old MIME type (in most used list, and the tree)
1706 BMenuItem* marked = CurrentMimeType();
1707 if (marked != NULL) {
1708 marked->SetMarked(false);
1710 if ((marked = MimeTypeMenu()->FindMarked()) != NULL)
1711 marked->SetMarked(false);
1714 // mark new MIME type (in most used list, and the tree)
1716 if (item != NULL) {
1717 item->SetMarked(true);
1718 fMimeTypeField->MenuItem()->SetLabel(item->Label());
1720 BMenuItem* search;
1721 for (int32 i = 2; (search = MimeTypeMenu()->ItemAt(i)) != NULL; i++) {
1722 if (item == search || search->Label() == NULL)
1723 continue;
1725 if (strcmp(item->Label(), search->Label()) == 0) {
1726 search->SetMarked(true);
1727 break;
1730 BMenu* submenu = search->Submenu();
1731 if (submenu == NULL)
1732 continue;
1734 for (int32 j = submenu->CountItems(); j-- > 0;) {
1735 BMenuItem* sub = submenu->ItemAt(j);
1736 if (strcmp(item->Label(), sub->Label()) == 0) {
1737 sub->SetMarked(true);
1738 break;
1744 return B_OK;
1748 status_t
1749 FindPanel::SetCurrentMimeType(const char* label)
1751 // unmark old MIME type (in most used list, and the tree)
1753 BMenuItem* marked = CurrentMimeType();
1754 if (marked != NULL) {
1755 marked->SetMarked(false);
1757 if ((marked = MimeTypeMenu()->FindMarked()) != NULL)
1758 marked->SetMarked(false);
1761 // mark new MIME type (in most used list, and the tree)
1763 fMimeTypeField->MenuItem()->SetLabel(label);
1764 bool found = false;
1766 for (int32 index = MimeTypeMenu()->CountItems(); index-- > 0;) {
1767 BMenuItem* item = MimeTypeMenu()->ItemAt(index);
1768 BMenu* submenu = item->Submenu();
1769 if (submenu != NULL && !found) {
1770 for (int32 subIndex = submenu->CountItems(); subIndex-- > 0;) {
1771 BMenuItem* subItem = submenu->ItemAt(subIndex);
1772 if (subItem->Label() != NULL
1773 && strcmp(label, subItem->Label()) == 0) {
1774 subItem->SetMarked(true);
1775 found = true;
1780 if (item->Label() != NULL && strcmp(label, item->Label()) == 0) {
1781 item->SetMarked(true);
1782 return B_OK;
1786 return found ? B_OK : B_ENTRY_NOT_FOUND;
1790 static
1791 void AddSubtype(BString& text, const BMimeType& type)
1793 text.Append(" (");
1794 text.Append(strchr(type.Type(), '/') + 1);
1795 // omit the slash
1796 text.Append(")");
1800 bool
1801 FindPanel::AddOneMimeTypeToMenu(const ShortMimeInfo* info, void* castToMenu)
1803 BPopUpMenu* menu = static_cast<BPopUpMenu*>(castToMenu);
1805 BMimeType type(info->InternalName());
1806 BMimeType super;
1807 type.GetSupertype(&super);
1808 if (super.InitCheck() < B_OK)
1809 return false;
1811 BMenuItem* superItem = menu->FindItem(super.Type());
1812 if (superItem != NULL) {
1813 BMessage* message = new BMessage(kMIMETypeItem);
1814 message->AddString("mimetype", info->InternalName());
1816 // check to ensure previous item's name differs
1817 BMenu* menu = superItem->Submenu();
1818 BMenuItem* previous = menu->ItemAt(menu->CountItems() - 1);
1819 BString text = info->ShortDescription();
1820 if (previous != NULL
1821 && strcasecmp(previous->Label(), info->ShortDescription()) == 0) {
1822 AddSubtype(text, type);
1824 // update the previous item as well
1825 BMimeType type(previous->Message()->GetString("mimetype", NULL));
1826 BString label = ShortMimeInfo(type).ShortDescription();
1827 AddSubtype(label, type);
1828 previous->SetLabel(label.String());
1831 menu->AddItem(new IconMenuItem(text.String(), message,
1832 info->InternalName()));
1835 return false;
1839 void
1840 FindPanel::AddMimeTypesToMenu()
1842 BMessage* itemMessage = new BMessage(kMIMETypeItem);
1843 itemMessage->AddString("mimetype", kAllMimeTypes);
1845 IconMenuItem* firstItem = new IconMenuItem(
1846 B_TRANSLATE("All files and folders"), itemMessage,
1847 static_cast<BBitmap*>(NULL));
1848 MimeTypeMenu()->AddItem(firstItem);
1849 MimeTypeMenu()->AddSeparatorItem();
1851 // add recent MIME types
1853 TTracker* tracker = dynamic_cast<TTracker*>(be_app);
1854 ASSERT(tracker != NULL);
1856 BList list;
1857 if (tracker != NULL && gMostUsedMimeTypes.ObtainList(&list)) {
1858 int32 count = 0;
1859 for (int32 index = 0; index < list.CountItems(); index++) {
1860 const char* name = (const char*)list.ItemAt(index);
1862 MimeTypeList* mimeTypes = tracker->MimeTypes();
1863 if (mimeTypes != NULL) {
1864 const ShortMimeInfo* info = mimeTypes->FindMimeType(name);
1865 if (info == NULL)
1866 continue;
1868 BMessage* message = new BMessage(kMIMETypeItem);
1869 message->AddString("mimetype", info->InternalName());
1871 MimeTypeMenu()->AddItem(new BMenuItem(name, message));
1872 count++;
1875 if (count != 0)
1876 MimeTypeMenu()->AddSeparatorItem();
1878 gMostUsedMimeTypes.ReleaseList();
1881 // add MIME type tree list
1883 BMessage types;
1884 if (BMimeType::GetInstalledSupertypes(&types) == B_OK) {
1885 const char* superType;
1886 int32 index = 0;
1888 while (types.FindString("super_types", index++, &superType) == B_OK) {
1889 BMenu* superMenu = new BMenu(superType);
1891 BMessage* message = new BMessage(kMIMETypeItem);
1892 message->AddString("mimetype", superType);
1894 MimeTypeMenu()->AddItem(new IconMenuItem(superMenu, message,
1895 superType));
1897 // the MimeTypeMenu's font is not correct at this time
1898 superMenu->SetFont(be_plain_font);
1902 if (tracker != NULL) {
1903 tracker->MimeTypes()->EachCommonType(
1904 &FindPanel::AddOneMimeTypeToMenu, MimeTypeMenu());
1907 // remove empty super type menus (and set target)
1909 for (int32 index = MimeTypeMenu()->CountItems(); index-- > 2;) {
1910 BMenuItem* item = MimeTypeMenu()->ItemAt(index);
1911 BMenu* submenu = item->Submenu();
1912 if (submenu == NULL)
1913 continue;
1915 if (submenu->CountItems() == 0) {
1916 MimeTypeMenu()->RemoveItem(item);
1917 delete item;
1918 } else
1919 submenu->SetTargetForItems(this);
1924 void
1925 FindPanel::AddVolumes(BMenu* menu)
1927 // ToDo: add calls to this to rebuild the menu when a volume gets mounted
1929 BMessage* message = new BMessage(kVolumeItem);
1930 message->AddInt32("device", -1);
1931 menu->AddItem(new BMenuItem(B_TRANSLATE("All disks"), message));
1932 menu->AddSeparatorItem();
1933 PopUpMenuSetTitle(menu, B_TRANSLATE("All disks"));
1935 BVolumeRoster roster;
1936 BVolume volume;
1937 roster.Rewind();
1938 while (roster.GetNextVolume(&volume) == B_OK) {
1939 if (volume.IsPersistent() && volume.KnowsQuery()) {
1940 BDirectory root;
1941 if (volume.GetRootDirectory(&root) != B_OK)
1942 continue;
1944 BEntry entry;
1945 root.GetEntry(&entry);
1947 Model model(&entry, true);
1948 if (model.InitCheck() != B_OK)
1949 continue;
1951 message = new BMessage(kVolumeItem);
1952 message->AddInt32("device", volume.Device());
1953 menu->AddItem(new ModelMenuItem(&model, model.Name(), message));
1957 if (menu->ItemAt(0))
1958 menu->ItemAt(0)->SetMarked(true);
1960 menu->SetTargetForItems(this);
1964 typedef std::pair<entry_ref, uint32> EntryWithDate;
1966 static int
1967 SortByDatePredicate(const EntryWithDate* entry1, const EntryWithDate* entry2)
1969 return entry1->second > entry2->second ?
1970 -1 : (entry1->second == entry2->second ? 0 : 1);
1973 struct AddOneRecentParams {
1974 BMenu* menu;
1975 const BMessenger* target;
1976 uint32 what;
1979 static const entry_ref*
1980 AddOneRecentItem(const entry_ref* ref, void* castToParams)
1982 AddOneRecentParams* params = (AddOneRecentParams*)castToParams;
1984 BMessage* message = new BMessage(params->what);
1985 message->AddRef("refs", ref);
1987 char type[B_MIME_TYPE_LENGTH];
1988 BNode node(ref);
1989 BNodeInfo(&node).GetType(type);
1990 BMenuItem* item = new IconMenuItem(ref->name, message, type);
1991 item->SetTarget(*params->target);
1992 params->menu->AddItem(item);
1994 return NULL;
1998 void
1999 FindPanel::AddRecentQueries(BMenu* menu, bool addSaveAsItem,
2000 const BMessenger* target, uint32 what)
2002 BObjectList<entry_ref> templates(10, true);
2003 BObjectList<EntryWithDate> recentQueries(10, true);
2005 // find all the queries on all volumes
2006 BVolumeRoster roster;
2007 BVolume volume;
2008 roster.Rewind();
2009 while (roster.GetNextVolume(&volume) == B_OK) {
2010 if (volume.IsPersistent() && volume.KnowsQuery()
2011 && volume.KnowsAttr()) {
2012 BQuery query;
2013 query.SetVolume(&volume);
2014 query.SetPredicate("_trk/recentQuery == 1");
2015 if (query.Fetch() != B_OK)
2016 continue;
2018 entry_ref ref;
2019 while (query.GetNextRef(&ref) == B_OK) {
2020 // ignore queries in the Trash
2021 if (FSInTrashDir(&ref))
2022 continue;
2024 char type[B_MIME_TYPE_LENGTH];
2025 BNode node(&ref);
2026 BNodeInfo(&node).GetType(type);
2028 if (strcasecmp(type, B_QUERY_TEMPLATE_MIMETYPE) == 0)
2029 templates.AddItem(new entry_ref(ref));
2030 else {
2031 uint32 changeTime;
2032 if (node.ReadAttr(kAttrQueryLastChange, B_INT32_TYPE, 0,
2033 &changeTime, sizeof(uint32)) != sizeof(uint32))
2034 continue;
2036 recentQueries.AddItem(new EntryWithDate(ref, changeTime));
2042 // we are only adding last ten queries
2043 recentQueries.SortItems(SortByDatePredicate);
2045 // but all templates
2046 AddOneRecentParams params;
2047 params.menu = menu;
2048 params.target = target;
2049 params.what = what;
2050 templates.EachElement(AddOneRecentItem, &params);
2052 int32 count = recentQueries.CountItems();
2053 if (count > 10) {
2054 // show only up to 10 recent queries
2055 count = 10;
2056 } else if (count < 0)
2057 count = 0;
2059 if (templates.CountItems() > 0 && count > 0)
2060 menu->AddSeparatorItem();
2062 for (int32 index = 0; index < count; index++)
2063 AddOneRecentItem(&recentQueries.ItemAt(index)->first, &params);
2065 if (addSaveAsItem) {
2066 // add a Save as template item
2067 if (count > 0 || templates.CountItems() > 0)
2068 menu->AddSeparatorItem();
2070 BMessage* message = new BMessage(kRunSaveAsTemplatePanel);
2071 BMenuItem* item = new BMenuItem(
2072 B_TRANSLATE("Save query as template" B_UTF8_ELLIPSIS), message);
2073 menu->AddItem(item);
2078 void
2079 FindPanel::SetUpAddRemoveButtons()
2081 BBox* box = dynamic_cast<BBox*>(FindView("Box"));
2083 ASSERT(box != NULL);
2085 if (box == NULL)
2086 return;
2088 BButton* removeButton = new BButton("remove button", B_TRANSLATE("Remove"),
2089 new BMessage(kRemoveItem));
2090 removeButton->SetEnabled(false);
2091 removeButton->SetTarget(this);
2093 BButton* addButton = new BButton("add button", B_TRANSLATE("Add"),
2094 new BMessage(kAddItem));
2095 addButton->SetTarget(this);
2097 BGroupLayout* layout = dynamic_cast<BGroupLayout*>(box->GetLayout());
2099 ASSERT(layout != NULL);
2101 if (layout == NULL)
2102 return;
2104 BLayoutBuilder::Group<>(layout)
2105 .AddGroup(B_HORIZONTAL)
2106 .AddGlue()
2107 .Add(removeButton)
2108 .Add(addButton)
2109 .End()
2110 .End();
2114 void
2115 FindPanel::FillCurrentQueryName(BTextControl* queryName, FindWindow* window)
2117 ASSERT(window);
2118 queryName->SetText(window->QueryName());
2122 void
2123 FindPanel::AddAttrRow()
2125 BBox* box = dynamic_cast<BBox*>(FindView("Box"));
2127 ASSERT(box != NULL);
2129 if (box == NULL)
2130 return;
2132 BGridView* grid = dynamic_cast<BGridView*>(box->FindView("AttrFields"));
2133 if (grid == NULL) {
2134 // reset layout
2135 BLayoutBuilder::Group<>(box, B_VERTICAL);
2137 grid = new BGridView("AttrFields");
2138 box->AddChild(grid);
2141 fAttrGrid = grid->GridLayout();
2143 AddAttributeControls(fAttrGrid->CountRows());
2145 // add logic to previous attrview
2146 if (fAttrGrid->CountRows() > 1)
2147 AddLogicMenu(fAttrGrid->CountRows() - 2);
2149 BButton* removeButton = dynamic_cast<BButton*>(
2150 box->FindView("remove button"));
2151 if (removeButton != NULL)
2152 removeButton->SetEnabled(fAttrGrid->CountRows() > 1);
2153 else
2154 SetUpAddRemoveButtons();
2158 void
2159 FindPanel::RemoveAttrRow()
2161 if (fAttrGrid->CountRows() < 2)
2162 return;
2164 BView* view;
2166 int32 row = fAttrGrid->CountRows() - 1;
2167 for (int32 col = fAttrGrid->CountColumns(); col > 0; col--) {
2168 BLayoutItem* item = fAttrGrid->ItemAt(col - 1, row);
2169 if (item == NULL)
2170 continue;
2172 view = item->View();
2173 if (view == NULL)
2174 continue;
2176 view->RemoveSelf();
2177 delete view;
2180 BString string = "TextEntry";
2181 string << (row - 1);
2182 view = FindAttrView(string.String(), row - 1);
2183 if (view != NULL)
2184 view->MakeFocus();
2186 if (fAttrGrid->CountRows() > 1) {
2187 // remove the And/Or menu field of the previous row
2188 BLayoutItem* item = fAttrGrid->ItemAt(3, row - 1);
2189 if (item == NULL)
2190 return;
2192 view = item->View();
2193 if (view == NULL)
2194 return;
2196 view->RemoveSelf();
2197 delete view;
2198 return;
2201 // only one row remains
2203 // disable the remove button
2204 BButton* button = dynamic_cast<BButton*>(FindView("remove button"));
2205 if (button != NULL)
2206 button->SetEnabled(false);
2208 // remove the And/Or menu field
2209 BLayoutItem* item = fAttrGrid->RemoveItem(3);
2210 if (item == NULL)
2211 return;
2213 view = item->View();
2214 if (view == NULL)
2215 return;
2217 view->RemoveSelf();
2218 delete view;
2222 uint32
2223 FindPanel::InitialMode(const BNode* node)
2225 if (node == NULL || node->InitCheck() != B_OK)
2226 return kByNameItem;
2228 uint32 result;
2229 if (node->ReadAttr(kAttrQueryInitialMode, B_INT32_TYPE, 0,
2230 (int32*)&result, sizeof(int32)) <= 0)
2231 return kByNameItem;
2233 return result;
2237 int32
2238 FindPanel::InitialAttrCount(const BNode* node)
2240 if (node == NULL || node->InitCheck() != B_OK)
2241 return 1;
2243 int32 result;
2244 if (node->ReadAttr(kAttrQueryInitialNumAttrs, B_INT32_TYPE, 0,
2245 &result, sizeof(int32)) <= 0)
2246 return 1;
2248 return result;
2252 static int32
2253 SelectItemWithLabel(BMenu* menu, const char* label)
2255 for (int32 index = menu->CountItems(); index-- > 0;) {
2256 BMenuItem* item = menu->ItemAt(index);
2258 if (strcmp(label, item->Label()) == 0) {
2259 item->SetMarked(true);
2260 return index;
2263 return -1;
2267 void
2268 FindPanel::SaveWindowState(BNode* node, bool editTemplate)
2270 ASSERT(node->InitCheck() == B_OK);
2272 BMenuItem* item = CurrentMimeType();
2273 if (item) {
2274 BString label(item->Label());
2275 node->WriteAttrString(kAttrQueryInitialMime, &label);
2278 uint32 mode = Mode();
2279 node->WriteAttr(kAttrQueryInitialMode, B_INT32_TYPE, 0,
2280 (int32*)&mode, sizeof(int32));
2282 MoreOptionsStruct saveMoreOptions;
2283 saveMoreOptions.showMoreOptions = fLatch->Value() != 0;
2285 saveMoreOptions.searchTrash = fSearchTrashCheck->Value() != 0;
2286 saveMoreOptions.temporary = fTemporaryCheck->Value() != 0;
2288 if (node->WriteAttr(kAttrQueryMoreOptions, B_RAW_TYPE, 0,
2289 &saveMoreOptions,
2290 sizeof(saveMoreOptions)) == sizeof(saveMoreOptions)) {
2291 node->RemoveAttr(kAttrQueryMoreOptionsForeign);
2294 if (editTemplate) {
2295 if (UserSpecifiedName()) {
2296 BString name(UserSpecifiedName());
2297 node->WriteAttrString(kAttrQueryTemplateName, &name);
2301 switch (Mode()) {
2302 case kByAttributeItem:
2304 BMessage message;
2305 int32 count = fAttrGrid->CountRows();
2306 node->WriteAttr(kAttrQueryInitialNumAttrs, B_INT32_TYPE, 0,
2307 &count, sizeof(int32));
2309 for (int32 index = 0; index < count; index++)
2310 SaveAttrState(&message, index);
2312 ssize_t size = message.FlattenedSize();
2313 if (size > 0) {
2314 char* buffer = new char[(size_t)size];
2315 status_t result = message.Flatten(buffer, size);
2316 if (result == B_OK) {
2317 node->WriteAttr(kAttrQueryInitialAttrs, B_MESSAGE_TYPE, 0,
2318 buffer, (size_t)size);
2320 delete[] buffer;
2322 break;
2325 case kByNameItem:
2326 case kByFormulaItem:
2328 BTextControl* textControl = dynamic_cast<BTextControl*>(
2329 FindView("TextControl"));
2331 ASSERT(textControl != NULL);
2333 if (textControl != NULL) {
2334 BString formula(textControl->Text());
2335 node->WriteAttrString(kAttrQueryInitialString, &formula);
2337 break;
2343 void
2344 FindPanel::SwitchToTemplate(const BNode* node)
2346 SwitchMode(InitialMode(node));
2347 // update the menu to correspond to the mode
2348 MarkNamedMenuItem(fSearchModeMenu, InitialMode(node), true);
2350 if (Mode() == (int32)kByAttributeItem) {
2351 RemoveByAttributeItems();
2352 AddByAttributeItems(node);
2355 RestoreWindowState(node);
2359 void
2360 FindPanel::RestoreMimeTypeMenuSelection(const BNode* node)
2362 if (Mode() == (int32)kByFormulaItem || node == NULL
2363 || node->InitCheck() != B_OK) {
2364 return;
2367 BString buffer;
2368 if (node->ReadAttrString(kAttrQueryInitialMime, &buffer) == B_OK)
2369 SetCurrentMimeType(buffer.String());
2373 void
2374 FindPanel::RestoreWindowState(const BNode* node)
2376 fMode = InitialMode(node);
2377 if (node == NULL || node->InitCheck() != B_OK)
2378 return;
2380 ShowOrHideMimeTypeMenu();
2381 RestoreMimeTypeMenuSelection(node);
2382 MoreOptionsStruct saveMoreOptions;
2384 bool storesMoreOptions = ReadAttr(node, kAttrQueryMoreOptions,
2385 kAttrQueryMoreOptionsForeign, B_RAW_TYPE, 0, &saveMoreOptions,
2386 sizeof(saveMoreOptions), &MoreOptionsStruct::EndianSwap)
2387 != kReadAttrFailed;
2389 if (storesMoreOptions) {
2390 // need to sanitize to true or false here, could have picked
2391 // up garbage from attributes
2393 saveMoreOptions.showMoreOptions =
2394 (saveMoreOptions.showMoreOptions != 0);
2396 fLatch->SetValue(saveMoreOptions.showMoreOptions);
2397 if (saveMoreOptions.showMoreOptions == 1 && fMoreOptions->IsHidden())
2398 fMoreOptions->Show();
2399 else if (saveMoreOptions.showMoreOptions == 0 && !fMoreOptions->IsHidden())
2400 fMoreOptions->Hide();
2402 fSearchTrashCheck->SetValue(saveMoreOptions.searchTrash);
2403 fTemporaryCheck->SetValue(saveMoreOptions.temporary);
2405 fQueryName->SetModificationMessage(NULL);
2406 FindWindow* findWindow = dynamic_cast<FindWindow*>(Window());
2407 if (findWindow != NULL)
2408 FillCurrentQueryName(fQueryName, findWindow);
2410 // set modification message after checking the temporary check box,
2411 // and filling out the text control so that we do not always trigger
2412 // clearing of the temporary check box.
2413 fQueryName->SetModificationMessage(
2414 new BMessage(kNameModifiedMessage));
2417 // get volumes to perform query on
2418 bool searchAllVolumes = true;
2420 attr_info info;
2421 if (node->GetAttrInfo(kAttrQueryVolume, &info) == B_OK) {
2422 char* buffer = new char[info.size];
2423 if (node->ReadAttr(kAttrQueryVolume, B_MESSAGE_TYPE, 0, buffer,
2424 (size_t)info.size) == info.size) {
2425 BMessage message;
2426 if (message.Unflatten(buffer) == B_OK) {
2427 for (int32 index = 0; ;index++) {
2428 ASSERT(index < 100);
2429 BVolume volume;
2430 // match a volume with the info embedded in
2431 // the message
2432 status_t result
2433 = MatchArchivedVolume(&volume, &message, index);
2434 if (result == B_OK) {
2435 char name[256];
2436 volume.GetName(name);
2437 SelectItemWithLabel(fVolMenu, name);
2438 searchAllVolumes = false;
2439 } else if (result != B_DEV_BAD_DRIVE_NUM)
2440 // if B_DEV_BAD_DRIVE_NUM, the volume just isn't
2441 // mounted this time around, keep looking for more
2442 // if other error, bail
2443 break;
2447 delete[] buffer;
2449 // mark or unmark "All disks"
2450 fVolMenu->ItemAt(0)->SetMarked(searchAllVolumes);
2451 ShowVolumeMenuLabel();
2453 switch (Mode()) {
2454 case kByAttributeItem:
2456 int32 count = InitialAttrCount(node);
2458 attr_info info;
2459 if (node->GetAttrInfo(kAttrQueryInitialAttrs, &info) != B_OK)
2460 break;
2461 char* buffer = new char[info.size];
2462 if (node->ReadAttr(kAttrQueryInitialAttrs, B_MESSAGE_TYPE, 0,
2463 buffer, (size_t)info.size) == info.size) {
2464 BMessage message;
2465 if (message.Unflatten(buffer) == B_OK) {
2466 for (int32 index = 0; index < count; index++)
2467 RestoreAttrState(message, index);
2470 delete[] buffer;
2471 break;
2474 case kByNameItem:
2475 case kByFormulaItem:
2477 BString buffer;
2478 if (node->ReadAttrString(kAttrQueryInitialString, &buffer)
2479 == B_OK) {
2480 BTextControl* textControl = dynamic_cast<BTextControl*>(
2481 FindView("TextControl"));
2483 ASSERT(textControl != NULL);
2485 if (textControl != NULL)
2486 textControl->SetText(buffer.String());
2488 break;
2492 // try to restore focus and possibly text selection
2493 BString focusedView;
2494 if (node->ReadAttrString("_trk/focusedView", &focusedView) == B_OK) {
2495 BView* view = FindView(focusedView.String());
2496 if (view != NULL) {
2497 view->MakeFocus();
2498 BTextControl* textControl = dynamic_cast<BTextControl*>(view);
2499 if (textControl != NULL && Mode() == kByFormulaItem) {
2500 int32 selStart = 0;
2501 int32 selEnd = INT32_MAX;
2502 node->ReadAttr("_trk/focusedSelStart", B_INT32_TYPE, 0,
2503 &selStart, sizeof(selStart));
2504 node->ReadAttr("_trk/focusedSelEnd", B_INT32_TYPE, 0,
2505 &selEnd, sizeof(selEnd));
2506 textControl->TextView()->Select(selStart, selEnd);
2513 void
2514 FindPanel::AddByAttributeItems(const BNode* node)
2516 int32 numAttributes = InitialAttrCount(node);
2517 if (numAttributes < 1)
2518 numAttributes = 1;
2520 for (int32 index = 0; index < numAttributes; index ++)
2521 AddAttrRow();
2525 void
2526 FindPanel::AddByNameOrFormulaItems()
2528 BBox* box = dynamic_cast<BBox*>(FindView("Box"));
2530 ASSERT(box != NULL);
2532 if (box == NULL)
2533 return;
2535 // reset layout
2536 BLayoutBuilder::Group<>(box, B_VERTICAL);
2538 BTextControl* textControl = new BTextControl("TextControl",
2539 "", "", NULL);
2540 textControl->SetDivider(0.0f);
2541 box->SetBorder(B_NO_BORDER);
2542 box->AddChild(textControl);
2543 textControl->MakeFocus();
2547 void
2548 FindPanel::RemoveAttrViewItems(bool removeGrid)
2550 if (fAttrGrid == NULL)
2551 return;
2553 BView* view = fAttrGrid->View();
2554 for (int32 index = view->CountChildren(); index > 0; index--) {
2555 BView* child = view->ChildAt(index - 1);
2556 child->RemoveSelf();
2557 delete child;
2560 if (removeGrid) {
2561 view->RemoveSelf();
2562 delete view;
2563 fAttrGrid = NULL;
2568 void
2569 FindPanel::RemoveByAttributeItems()
2571 RemoveAttrViewItems();
2572 BView* view = FindView("add button");
2573 if (view) {
2574 view->RemoveSelf();
2575 delete view;
2578 view = FindView("remove button");
2579 if (view) {
2580 view->RemoveSelf();
2581 delete view;
2584 view = FindView("TextControl");
2585 if (view) {
2586 view->RemoveSelf();
2587 delete view;
2592 void
2593 FindPanel::ShowOrHideMimeTypeMenu()
2595 BView* menuFieldSpacer = FindView("MimeTypeMenuSpacer");
2596 BMenuField* menuField
2597 = dynamic_cast<BMenuField*>(FindView("MimeTypeMenu"));
2598 if (menuFieldSpacer == NULL || menuField == NULL)
2599 return;
2601 if (Mode() == (int32)kByFormulaItem && !menuField->IsHidden(this)) {
2602 BSize size = menuField->ExplicitMinSize();
2603 menuField->Hide();
2604 menuFieldSpacer->SetExplicitMinSize(size);
2605 menuFieldSpacer->SetExplicitMaxSize(size);
2606 if (menuFieldSpacer->IsHidden(this))
2607 menuFieldSpacer->Show();
2608 } else if (menuField->IsHidden(this)) {
2609 menuFieldSpacer->Hide();
2610 menuField->Show();
2615 void
2616 FindPanel::AddAttributeControls(int32 gridRow)
2618 BPopUpMenu* menu = new BPopUpMenu("PopUp");
2620 // add NAME attribute to popup
2621 BMenu* submenu = new BMenu(B_TRANSLATE("Name"));
2622 submenu->SetRadioMode(true);
2623 submenu->SetFont(be_plain_font);
2624 BMessage* message = new BMessage(kAttributeItemMain);
2625 message->AddString("name", "name");
2626 message->AddInt32("type", B_STRING_TYPE);
2627 BMenuItem* item = new BMenuItem(submenu, message);
2628 menu->AddItem(item);
2630 for (int32 i = 0; i < 5; i++) {
2631 message = new BMessage(kAttributeItem);
2632 message->AddInt32("operator", operators[i]);
2633 submenu->AddItem(new BMenuItem(
2634 B_TRANSLATE_NOCOLLECT(operatorLabels[i]), message));
2637 // mark first items initially
2638 menu->ItemAt(0)->SetMarked(true);
2639 submenu->ItemAt(0)->SetMarked(true);
2641 // add SIZE attribute
2642 submenu = new BMenu(B_TRANSLATE("Size"));
2643 submenu->SetRadioMode(true);
2644 submenu->SetFont(be_plain_font);
2645 message = new BMessage(kAttributeItemMain);
2646 message->AddString("name", "size");
2647 message->AddInt32("type", B_OFF_T_TYPE);
2648 item = new BMenuItem(submenu, message);
2649 menu->AddItem(item);
2651 message = new BMessage(kAttributeItem);
2652 message->AddInt32("operator", B_GE);
2653 submenu->AddItem(new BMenuItem(B_TRANSLATE_NOCOLLECT(operatorLabels[5]),
2654 message));
2656 message = new BMessage(kAttributeItem);
2657 message->AddInt32("operator", B_LE);
2658 submenu->AddItem(new BMenuItem(B_TRANSLATE_NOCOLLECT(operatorLabels[6]),
2659 message));
2661 message = new BMessage(kAttributeItem);
2662 message->AddInt32("operator", B_EQ);
2663 submenu->AddItem(new BMenuItem(B_TRANSLATE_NOCOLLECT(operatorLabels[1]),
2664 message));
2666 // add "modified" field
2667 submenu = new BMenu(B_TRANSLATE("Modified"));
2668 submenu->SetRadioMode(true);
2669 submenu->SetFont(be_plain_font);
2670 message = new BMessage(kAttributeItemMain);
2671 message->AddString("name", "last_modified");
2672 message->AddInt32("type", B_TIME_TYPE);
2673 item = new BMenuItem(submenu, message);
2674 menu->AddItem(item);
2676 message = new BMessage(kAttributeItem);
2677 message->AddInt32("operator", B_LE);
2678 submenu->AddItem(new BMenuItem(B_TRANSLATE_NOCOLLECT(operatorLabels[7]),
2679 message));
2681 message = new BMessage(kAttributeItem);
2682 message->AddInt32("operator", B_GE);
2683 submenu->AddItem(new BMenuItem(B_TRANSLATE_NOCOLLECT(operatorLabels[8]),
2684 message));
2686 BMenuField* menuField = new BMenuField("MenuField", "", menu);
2687 menuField->SetDivider(0.0f);
2688 fAttrGrid->AddView(menuField, 0, gridRow);
2690 BStringView* stringView = new BStringView("",
2691 menu->FindMarked()->Submenu()->FindMarked()->Label());
2692 BLayoutItem* layoutItem = fAttrGrid->AddView(stringView, 1, gridRow);
2693 layoutItem->SetExplicitAlignment(BAlignment(B_ALIGN_RIGHT,
2694 B_ALIGN_VERTICAL_UNSET));
2696 BString title("TextEntry");
2697 title << gridRow;
2698 BTextControl* textControl = new BTextControl(title.String(), "", "", NULL);
2699 textControl->SetDivider(0.0f);
2700 fAttrGrid->AddView(textControl, 2, gridRow);
2701 textControl->MakeFocus();
2703 // target everything
2704 menu->SetTargetForItems(this);
2705 for (int32 index = menu->CountItems() - 1; index >= 0; index--) {
2706 BMenu* submenuAtIndex = menu->SubmenuAt(index);
2707 if (submenuAtIndex != NULL)
2708 submenuAtIndex->SetTargetForItems(this);
2711 // populate mime popup
2712 AddMimeTypeAttrs(menu);
2716 void
2717 FindPanel::RestoreAttrState(const BMessage &message, int32 index)
2719 BMenuField* menuField
2720 = dynamic_cast<BMenuField*>(FindAttrView("MenuField", index));
2721 if (menuField != NULL) {
2722 // decode menu selections
2723 BMenu* menu = menuField->Menu();
2725 ASSERT(menu != NULL);
2727 AddMimeTypeAttrs(menu);
2728 const char* label;
2729 if (message.FindString("menuSelection", index, &label) == B_OK) {
2730 int32 itemIndex = SelectItemWithLabel(menu, label);
2731 if (itemIndex >= 0) {
2732 menu = menu->SubmenuAt(itemIndex);
2733 if (menu != NULL && message.FindString("subMenuSelection",
2734 index, &label) == B_OK) {
2735 SelectItemWithLabel(menu, label);
2741 // decode attribute text
2742 BString textEntryString = "TextEntry";
2743 textEntryString << index;
2744 BTextControl* textControl = dynamic_cast<BTextControl*>(
2745 FindAttrView(textEntryString.String(), index));
2747 ASSERT(textControl != NULL);
2749 const char* string;
2750 if (textControl != NULL
2751 && message.FindString("attrViewText", index, &string) == B_OK) {
2752 textControl->SetText(string);
2755 int32 logicMenuSelectedIndex;
2756 if (message.FindInt32("logicalRelation", index,
2757 &logicMenuSelectedIndex) == B_OK) {
2758 BMenuField* field = dynamic_cast<BMenuField*>(
2759 FindAttrView("Logic", index));
2760 if (field != NULL) {
2761 BMenu* fieldMenu = field->Menu();
2762 if (fieldMenu != NULL) {
2763 BMenuItem* logicItem
2764 = fieldMenu->ItemAt(logicMenuSelectedIndex);
2765 if (logicItem != NULL) {
2766 logicItem->SetMarked(true);
2767 return;
2772 AddLogicMenu(index, logicMenuSelectedIndex == 0);
2777 void
2778 FindPanel::SaveAttrState(BMessage* message, int32 index)
2780 BMenu* menu = dynamic_cast<BMenuField*>(FindAttrView("MenuField", index))
2781 ->Menu();
2783 // encode main attribute menu selection
2784 BMenuItem* item = menu->FindMarked();
2785 message->AddString("menuSelection", item ? item->Label() : "");
2787 // encode submenu selection
2788 const char* label = "";
2789 if (item) {
2790 BMenu* submenu = menu->SubmenuAt(menu->IndexOf(item));
2791 if (submenu) {
2792 item = submenu->FindMarked();
2793 if (item)
2794 label = item->Label();
2797 message->AddString("subMenuSelection", label);
2799 // encode attribute text
2800 BString textEntryString = "TextEntry";
2801 textEntryString << index;
2802 BTextControl* textControl = dynamic_cast<BTextControl*>(FindAttrView(
2803 textEntryString.String(), index));
2805 ASSERT(textControl != NULL);
2807 if (textControl != NULL)
2808 message->AddString("attrViewText", textControl->Text());
2810 BMenuField* field = dynamic_cast<BMenuField*>(FindAttrView("Logic", index));
2811 if (field != NULL) {
2812 BMenu* fieldMenu = field->Menu();
2813 if (fieldMenu != NULL) {
2814 BMenuItem* item = fieldMenu->FindMarked();
2815 ASSERT(item != NULL);
2816 message->AddInt32("logicalRelation",
2817 item != NULL ? field->Menu()->IndexOf(item) : 0);
2823 void
2824 FindPanel::AddLogicMenu(int32 index, bool selectAnd)
2826 // add "AND/OR" menu
2827 BPopUpMenu* menu = new BPopUpMenu("");
2828 BMessage* message = new BMessage();
2829 message->AddInt32("combine", B_AND);
2830 BMenuItem* item = new BMenuItem(B_TRANSLATE("And"), message);
2831 menu->AddItem(item);
2832 if (selectAnd)
2833 item->SetMarked(true);
2835 message = new BMessage();
2836 message->AddInt32("combine", B_OR);
2837 item = new BMenuItem(B_TRANSLATE("Or"), message);
2838 menu->AddItem(item);
2839 if (!selectAnd)
2840 item->SetMarked(true);
2842 menu->SetTargetForItems(this);
2844 BMenuField* menufield = new BMenuField("Logic", "", menu, B_WILL_DRAW);
2845 menufield->SetDivider(0.0f);
2847 ResizeMenuField(menufield);
2849 fAttrGrid->AddView(menufield, 3, index);
2853 void
2854 FindPanel::RemoveLogicMenu(int32 index)
2856 BMenuField* menufield = dynamic_cast<BMenuField*>(FindAttrView("Logic", index));
2857 if (menufield) {
2858 menufield->RemoveSelf();
2859 delete menufield;
2864 void
2865 FindPanel::AddAttributes(BMenu* menu, const BMimeType &mimeType)
2867 // only add things to menu which have "user-visible" data
2868 BMessage attributeMessage;
2869 if (mimeType.GetAttrInfo(&attributeMessage) != B_OK)
2870 return;
2872 char desc[B_MIME_TYPE_LENGTH];
2873 mimeType.GetShortDescription(desc);
2875 // go through each field in meta mime and add it to a menu
2876 for (int32 index = 0; ; index++) {
2877 const char* publicName;
2878 if (attributeMessage.FindString("attr:public_name", index,
2879 &publicName) != B_OK) {
2880 break;
2883 if (!attributeMessage.FindBool("attr:viewable"))
2884 continue;
2886 const char* attributeName;
2887 if (attributeMessage.FindString("attr:name", index, &attributeName)
2888 != B_OK) {
2889 continue;
2892 int32 type;
2893 if (attributeMessage.FindInt32("attr:type", index, &type) != B_OK)
2894 continue;
2896 BMenu* submenu = new BMenu(publicName);
2897 submenu->SetRadioMode(true);
2898 submenu->SetFont(be_plain_font);
2899 BMessage* message = new BMessage(kAttributeItemMain);
2900 message->AddString("name", attributeName);
2901 message->AddInt32("type", type);
2902 BMenuItem* item = new BMenuItem(submenu, message);
2903 menu->AddItem(item);
2904 menu->SetTargetForItems(this);
2906 switch (type) {
2907 case B_STRING_TYPE:
2908 message = new BMessage(kAttributeItem);
2909 message->AddInt32("operator", B_CONTAINS);
2910 submenu->AddItem(new BMenuItem(operatorLabels[0], message));
2912 message = new BMessage(kAttributeItem);
2913 message->AddInt32("operator", B_EQ);
2914 submenu->AddItem(new BMenuItem(operatorLabels[1], message));
2916 message = new BMessage(kAttributeItem);
2917 message->AddInt32("operator", B_NE);
2918 submenu->AddItem(new BMenuItem(operatorLabels[2], message));
2919 submenu->SetTargetForItems(this);
2921 message = new BMessage(kAttributeItem);
2922 message->AddInt32("operator", B_BEGINS_WITH);
2923 submenu->AddItem(new BMenuItem(operatorLabels[3], message));
2924 submenu->SetTargetForItems(this);
2926 message = new BMessage(kAttributeItem);
2927 message->AddInt32("operator", B_ENDS_WITH);
2928 submenu->AddItem(new BMenuItem(operatorLabels[4], message));
2929 break;
2931 case B_BOOL_TYPE:
2932 case B_INT16_TYPE:
2933 case B_UINT8_TYPE:
2934 case B_INT8_TYPE:
2935 case B_UINT16_TYPE:
2936 case B_INT32_TYPE:
2937 case B_UINT32_TYPE:
2938 case B_INT64_TYPE:
2939 case B_UINT64_TYPE:
2940 case B_OFF_T_TYPE:
2941 case B_FLOAT_TYPE:
2942 case B_DOUBLE_TYPE:
2943 message = new BMessage(kAttributeItem);
2944 message->AddInt32("operator", B_EQ);
2945 submenu->AddItem(new BMenuItem(operatorLabels[1], message));
2947 message = new BMessage(kAttributeItem);
2948 message->AddInt32("operator", B_GE);
2949 submenu->AddItem(new BMenuItem(operatorLabels[5], message));
2951 message = new BMessage(kAttributeItem);
2952 message->AddInt32("operator", B_LE);
2953 submenu->AddItem(new BMenuItem(operatorLabels[6], message));
2954 break;
2956 case B_TIME_TYPE:
2957 message = new BMessage(kAttributeItem);
2958 message->AddInt32("operator", B_LE);
2959 submenu->AddItem(new BMenuItem(operatorLabels[7], message));
2961 message = new BMessage(kAttributeItem);
2962 message->AddInt32("operator", B_GE);
2963 submenu->AddItem(new BMenuItem(operatorLabels[8], message));
2964 break;
2966 submenu->SetTargetForItems(this);
2971 void
2972 FindPanel::AddMimeTypeAttrs(BMenu* menu)
2974 const char* typeName;
2975 if (CurrentMimeType(&typeName) == NULL)
2976 return;
2978 BMimeType mimeType(typeName);
2979 if (!mimeType.IsInstalled())
2980 return;
2982 if (!mimeType.IsSupertypeOnly()) {
2983 // add supertype attributes
2984 BMimeType supertype;
2985 mimeType.GetSupertype(&supertype);
2986 AddAttributes(menu, supertype);
2989 AddAttributes(menu, mimeType);
2993 void
2994 FindPanel::GetDefaultAttrName(BString& attrName, int32 row) const
2996 BMenuItem* item = NULL;
2997 BMenuField* menuField
2998 = dynamic_cast<BMenuField*>(fAttrGrid->ItemAt(0, row)->View());
2999 if (menuField != NULL && menuField->Menu() != NULL)
3000 item = menuField->Menu()->FindMarked();
3002 if (item != NULL)
3003 attrName << item->Label();
3004 else
3005 attrName << B_TRANSLATE("Name");
3007 if (item != NULL && item->Submenu() != NULL)
3008 item = item->Submenu()->FindMarked();
3009 else
3010 item = NULL;
3012 if (item != NULL)
3013 attrName << " " << item->Label() << " ";
3014 else
3015 attrName << " = ";
3017 BTextControl* textControl
3018 = dynamic_cast<BTextControl*>(fAttrGrid->ItemAt(2, row)->View());
3019 if (textControl != NULL)
3020 attrName << textControl->Text();
3024 // #pragma mark -
3027 DeleteTransientQueriesTask::DeleteTransientQueriesTask()
3029 state(kInitial),
3030 fWalker(NULL)
3035 DeleteTransientQueriesTask::~DeleteTransientQueriesTask()
3037 delete fWalker;
3041 bool
3042 DeleteTransientQueriesTask::DoSomeWork()
3044 switch (state) {
3045 case kInitial:
3046 Initialize();
3047 break;
3049 case kAllocatedWalker:
3050 case kTraversing:
3051 if (GetSome()) {
3052 PRINT(("transient query killer done\n"));
3053 return true;
3055 break;
3057 case kError:
3058 return true;
3061 return false;
3065 void
3066 DeleteTransientQueriesTask::Initialize()
3068 PRINT(("starting up transient query killer\n"));
3069 BPath path;
3070 status_t result = find_directory(B_USER_DIRECTORY, &path, false);
3071 if (result != B_OK) {
3072 state = kError;
3073 return;
3075 fWalker = new BTrackerPrivate::TNodeWalker(path.Path());
3076 state = kAllocatedWalker;
3080 const int32 kBatchCount = 100;
3082 bool
3083 DeleteTransientQueriesTask::GetSome()
3085 state = kTraversing;
3086 for (int32 count = kBatchCount; count > 0; count--) {
3087 entry_ref ref;
3088 if (fWalker->GetNextRef(&ref) != B_OK) {
3089 state = kError;
3090 return true;
3092 Model model(&ref);
3093 if (model.IsQuery())
3094 ProcessOneRef(&model);
3095 #if xDEBUG
3096 else
3097 PRINT(("transient query killer: %s not a query\n", model.Name()));
3098 #endif
3100 return false;
3104 const int32 kDaysToExpire = 7;
3106 static bool
3107 QueryOldEnough(Model* model)
3109 // check if it is old and ready to be deleted
3110 time_t now = time(0);
3112 tm nowTimeData;
3113 tm fileModData;
3115 localtime_r(&now, &nowTimeData);
3116 localtime_r(&model->StatBuf()->st_ctime, &fileModData);
3118 if ((nowTimeData.tm_mday - fileModData.tm_mday) < kDaysToExpire
3119 && (nowTimeData.tm_mday - fileModData.tm_mday) > -kDaysToExpire) {
3120 PRINT(("query %s, not old enough\n", model->Name()));
3121 return false;
3123 return true;
3127 bool
3128 DeleteTransientQueriesTask::ProcessOneRef(Model* model)
3130 BModelOpener opener(model);
3132 // is this a temporary query
3133 if (!MoreOptionsStruct::QueryTemporary(model->Node())) {
3134 PRINT(("query %s, not temporary\n", model->Name()));
3135 return false;
3138 if (!QueryOldEnough(model))
3139 return false;
3141 TTracker* tracker = dynamic_cast<TTracker*>(be_app);
3142 ASSERT(tracker != NULL);
3144 // check that it is not showing
3145 if (tracker != NULL && tracker->EntryHasWindowOpen(model->EntryRef())) {
3146 PRINT(("query %s, showing, can't delete\n", model->Name()));
3147 return false;
3150 PRINT(("query %s, old, temporary, not shownig - deleting\n",
3151 model->Name()));
3153 BEntry entry(model->EntryRef());
3154 entry.Remove();
3156 return true;
3160 class DeleteTransientQueriesFunctor : public FunctionObjectWithResult<bool> {
3161 public:
3162 DeleteTransientQueriesFunctor(DeleteTransientQueriesTask* task)
3163 : task(task)
3166 virtual ~DeleteTransientQueriesFunctor()
3168 delete task;
3171 virtual void operator()()
3172 { result = task->DoSomeWork(); }
3174 private:
3175 DeleteTransientQueriesTask* task;
3179 void
3180 DeleteTransientQueriesTask::StartUpTransientQueryCleaner()
3182 TTracker* tracker = dynamic_cast<TTracker*>(be_app);
3183 ASSERT(tracker != NULL);
3185 if (tracker == NULL)
3186 return;
3187 // set up a task that wakes up when the machine is idle and starts
3188 // killing off old transient queries
3189 DeleteTransientQueriesFunctor* worker
3190 = new DeleteTransientQueriesFunctor(new DeleteTransientQueriesTask());
3192 tracker->MainTaskLoop()->RunWhenIdle(worker,
3193 30 * 60 * 1000000, // half an hour initial delay
3194 5 * 60 * 1000000, // idle for five minutes
3195 10 * 1000000);
3199 // #pragma mark -
3202 RecentFindItemsMenu::RecentFindItemsMenu(const char* title,
3203 const BMessenger* target, uint32 what)
3205 BMenu(title, B_ITEMS_IN_COLUMN),
3206 fTarget(*target),
3207 fWhat(what)
3212 void
3213 RecentFindItemsMenu::AttachedToWindow()
3215 // re-populate the menu with fresh items
3216 for (int32 index = CountItems() - 1; index >= 0; index--)
3217 delete RemoveItem(index);
3219 FindPanel::AddRecentQueries(this, false, &fTarget, fWhat);
3220 BMenu::AttachedToWindow();
3224 #if !B_BEOS_VERSION_DANO
3225 _IMPEXP_TRACKER
3226 #endif
3227 BMenu*
3228 TrackerBuildRecentFindItemsMenu(const char* title)
3230 BMessenger trackerMessenger(kTrackerSignature);
3231 return new RecentFindItemsMenu(title, &trackerMessenger, B_REFS_RECEIVED);
3235 // #pragma mark -
3238 DraggableQueryIcon::DraggableQueryIcon(BRect frame, const char* name,
3239 const BMessage* message, BMessenger messenger, uint32 resizeFlags,
3240 uint32 flags)
3242 DraggableIcon(frame, name, B_QUERY_MIMETYPE, B_LARGE_ICON,
3243 message, messenger, resizeFlags, flags)
3248 bool
3249 DraggableQueryIcon::DragStarted(BMessage* dragMessage)
3251 // override to substitute the user-specified query name
3252 dragMessage->RemoveData("be:clip_name");
3254 FindWindow* window = dynamic_cast<FindWindow*>(Window());
3256 ASSERT(window != NULL);
3258 return window != NULL && dragMessage->AddString("be:clip_name",
3259 window->BackgroundView()->UserSpecifiedName() != NULL
3260 ? window->BackgroundView()->UserSpecifiedName()
3261 : B_TRANSLATE("New Query")) == B_OK;
3265 // #pragma mark -
3268 MostUsedNames::MostUsedNames(const char* fileName, const char* directory,
3269 int32 maxCount)
3271 fFileName(fileName),
3272 fDirectory(directory),
3273 fLoaded(false),
3274 fCount(maxCount)
3279 MostUsedNames::~MostUsedNames()
3281 // only write back settings when we've been used
3282 if (!fLoaded)
3283 return;
3285 // write most used list to file
3287 BPath path;
3288 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path, true) != B_OK
3289 || path.Append(fDirectory) != B_OK || path.Append(fFileName) != B_OK) {
3290 return;
3293 BFile file(path.Path(), B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
3294 if (file.InitCheck() == B_OK) {
3295 for (int32 i = 0; i < fList.CountItems(); i++) {
3296 list_entry* entry = static_cast<list_entry*>(fList.ItemAt(i));
3298 char line[B_FILE_NAME_LENGTH + 5];
3300 // limit upper bound to react more dynamically to changes
3301 if (--entry->count > 20)
3302 entry->count = 20;
3304 // if the item hasn't been chosen in a while, remove it
3305 // (but leave at least one item in the list)
3306 if (entry->count < -10 && i > 0)
3307 continue;
3309 sprintf(line, "%" B_PRId32 " %s\n", entry->count, entry->name);
3310 if (file.Write(line, strlen(line)) < B_OK)
3311 break;
3314 file.Unset();
3316 // free data
3318 for (int32 i = fList.CountItems(); i-- > 0;) {
3319 list_entry* entry = static_cast<list_entry*>(fList.ItemAt(i));
3320 free(entry->name);
3321 delete entry;
3326 bool
3327 MostUsedNames::ObtainList(BList* list)
3329 if (list == NULL)
3330 return false;
3332 if (!fLoaded)
3333 UpdateList();
3335 fLock.Lock();
3337 list->MakeEmpty();
3338 for (int32 i = 0; i < fCount; i++) {
3339 list_entry* entry = static_cast<list_entry*>(fList.ItemAt(i));
3340 if (entry == NULL)
3341 return true;
3343 list->AddItem(entry->name);
3345 return true;
3349 void
3350 MostUsedNames::ReleaseList()
3352 fLock.Unlock();
3356 void
3357 MostUsedNames::AddName(const char* name)
3359 fLock.Lock();
3361 if (!fLoaded)
3362 LoadList();
3364 // remove last entry if there are more than
3365 // 2*fCount entries in the list
3367 list_entry* entry = NULL;
3369 if (fList.CountItems() > fCount * 2) {
3370 entry = static_cast<list_entry*>(
3371 fList.RemoveItem(fList.CountItems() - 1));
3373 // is this the name we want to add here?
3374 if (strcmp(name, entry->name)) {
3375 free(entry->name);
3376 delete entry;
3377 entry = NULL;
3378 } else
3379 fList.AddItem(entry);
3382 if (entry == NULL) {
3383 for (int32 i = 0;
3384 (entry = static_cast<list_entry*>(fList.ItemAt(i))) != NULL; i++) {
3385 if (strcmp(entry->name, name) == 0)
3386 break;
3390 if (entry == NULL) {
3391 entry = new list_entry;
3392 entry->name = strdup(name);
3393 entry->count = 1;
3395 fList.AddItem(entry);
3396 } else if (entry->count < 0)
3397 entry->count = 1;
3398 else
3399 entry->count++;
3401 fLock.Unlock();
3402 UpdateList();
3407 MostUsedNames::CompareNames(const void* a,const void* b)
3409 list_entry* entryA = *(list_entry**)a;
3410 list_entry* entryB = *(list_entry**)b;
3412 if (entryA->count == entryB->count)
3413 return strcasecmp(entryA->name,entryB->name);
3415 return entryB->count - entryA->count;
3419 void
3420 MostUsedNames::LoadList()
3422 if (fLoaded)
3423 return;
3424 fLoaded = true;
3426 // load the most used names list
3428 BPath path;
3429 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path, true) != B_OK
3430 || path.Append(fDirectory) != B_OK || path.Append(fFileName) != B_OK) {
3431 return;
3434 FILE* file = fopen(path.Path(), "r");
3435 if (file == NULL)
3436 return;
3438 char line[B_FILE_NAME_LENGTH + 5];
3439 while (fgets(line, sizeof(line), file) != NULL) {
3440 int32 length = (int32)strlen(line) - 1;
3441 if (length >= 0 && line[length] == '\n')
3442 line[length] = '\0';
3444 int32 count = atoi(line);
3446 char* name = strchr(line, ' ');
3447 if (name == NULL || *(++name) == '\0')
3448 continue;
3450 list_entry* entry = new list_entry;
3451 entry->name = strdup(name);
3452 entry->count = count;
3454 fList.AddItem(entry);
3456 fclose(file);
3460 void
3461 MostUsedNames::UpdateList()
3463 AutoLock<Benaphore> locker(fLock);
3465 if (!fLoaded)
3466 LoadList();
3468 // sort list items
3470 fList.SortItems(MostUsedNames::CompareNames);
3473 } // namespace BPrivate