diff --git a/src/kits/tracker/FindPanel.cpp b/src/kits/tracker/FindPanel.cpp index 8110343db2..4d9a8c4f61 100644 --- a/src/kits/tracker/FindPanel.cpp +++ b/src/kits/tracker/FindPanel.cpp @@ -133,7 +133,6 @@ static const char* operatorLabels[] = { B_TRANSLATE_MARK("after") }; - namespace BPrivate { class MostUsedNames { @@ -212,6 +211,26 @@ MoreOptionsStruct::QueryTemporary(const BNode* node) // #pragma mark - FindWindow +int32 +GetNumberOfVolumes() +{ + static int32 numberOfVolumes = -1; + if (numberOfVolumes >= 0) + return numberOfVolumes; + + int32 count = 0; + BVolumeRoster volumeRoster; + BVolume volume; + while (volumeRoster.GetNextVolume(&volume) == B_OK) { + if (volume.IsPersistent() && volume.KnowsQuery() && volume.KnowsAttr()) + ++count; + } + + numberOfVolumes = count; + return numberOfVolumes; +} + + FindWindow::FindWindow(const entry_ref* newRef, bool editIfTemplateOnly) : BWindow(BRect(), B_TRANSLATE("Find"), B_TITLED_WINDOW, @@ -667,6 +686,25 @@ FindWindow::SaveQueryAttributes(BNode* file, bool queryTemplate) } +void +MarkVolumeAccordingToDirectoryFilter(BMenu* menu, entry_ref* ref) +{ + int32 startingIndex = 2; + int32 endingIndex = 2 + GetNumberOfVolumes(); + for (int32 index = startingIndex; index < endingIndex; ++index) { + BMenuItem* item = menu->ItemAt(index); + if (item->IsMarked()) + continue; + BMessage* message = item->Message(); + dev_t device; + if (message->FindInt32("device", &device) != B_OK) + continue; + if (device == ref->device) + item->SetMarked(true); + } +} + + status_t FindWindow::SaveQueryAsAttributes(BNode* file, BEntry* entry, bool queryTemplate, const BMessage* oldAttributes, const BPoint* oldLocation, bool temporary) @@ -697,42 +735,41 @@ FindWindow::SaveQueryAsAttributes(BNode* file, BEntry* entry, bool queryTemplate int32 tmp = 1; file->WriteAttr("_trk/recentQuery", B_INT32_TYPE, 0, &tmp, sizeof(int32)); - // write some useful info to help locate the volume to query - BMenuItem* item = fBackground->VolMenu()->FindMarked(); - if (item != NULL) { - dev_t dev; - BMessage message; - uint32 count = 0; + fBackground->SaveDirectoryFiltersToFile(file); - int32 itemCount = fBackground->VolMenu()->CountItems(); - for (int32 index = 2; index < itemCount; index++) { - BMenuItem* item = fBackground->VolMenu()->ItemAt(index); + BMenu* volMenu = fBackground->VolMenu(); + ASSERT(volMenu != NULL); - if (!item->IsMarked()) - continue; + int32 numberOfDirectoryFilters = fBackground->fDirectoryFilters.CountItems(); + for (int32 i = 0; i < numberOfDirectoryFilters; ++i) + MarkVolumeAccordingToDirectoryFilter(volMenu, fBackground->fDirectoryFilters.ItemAt(i)); - if (item->Message()->FindInt32("device", &dev) != B_OK) - continue; + bool addAllVolumes = volMenu->ItemAt(0)->IsMarked(); + int32 numberOfVolumes = GetNumberOfVolumes(); + BMessage messageContainingVolumeInfo; + for (int32 i = 2; i < numberOfVolumes + 2; ++i) { + BMenuItem* volumeMenuItem = volMenu->ItemAt(i); + BMessage* messageOfVolumeMenuItem = volumeMenuItem->Message(); + dev_t device; + if (messageOfVolumeMenuItem->FindInt32("device", &device) != B_OK) + continue; - count++; - BVolume volume(dev); - EmbedUniqueVolumeInfo(&message, &volume); + if (volumeMenuItem->IsMarked() || addAllVolumes) { + BVolume volume(device); + EmbedUniqueVolumeInfo(&messageContainingVolumeInfo, &volume); } + } - if (count > 0) { - // do we need to embed any volumes - ssize_t size = message.FlattenedSize(); - BString buffer; - status_t result = message.Flatten(buffer.LockBuffer(size), size); - if (result == B_OK) { - if (file->WriteAttr(kAttrQueryVolume, B_MESSAGE_TYPE, 0, - buffer.String(), (size_t)size) != size) { - return B_IO_ERROR; - } - } - buffer.UnlockBuffer(); + ssize_t flattenedSize = messageContainingVolumeInfo.FlattenedSize(); + if (flattenedSize > 0) { + BString bufferString; + char* buffer = bufferString.LockBuffer(flattenedSize); + messageContainingVolumeInfo.Flatten(buffer, flattenedSize); + if (fFile->WriteAttr(kAttrQueryVolume, B_MESSAGE_TYPE, 0, buffer, + static_cast(flattenedSize)) + != flattenedSize) { + return B_ERROR; } - // default to query for everything } file->WriteAttr("_trk/temporary", B_BOOL_TYPE, 0, &temporary, sizeof(temporary)); @@ -1056,6 +1093,7 @@ FindWindow::MessageReceived(BMessage* message) SwitchToTemplate(&ref); UpdateFileReferences(&ref); + fBackground->LoadDirectoryFiltersFromFile(fFile); fSaveQueryOrTemplateItem->SetEnabled(true); break; } @@ -1081,16 +1119,32 @@ FindWindow::MessageReceived(BMessage* message) } +bool +FolderFilter::Filter(const entry_ref* ref, BNode* node, struct stat_beos* stat, + const char* mimeType) +{ + ASSERT(node->InitCheck() == B_OK); + if (node->IsDirectory()) { + return true; + } else if (node->IsSymLink()) { + BEntry entry(ref, true); + return entry.IsDirectory(); + } + return false; +} + + // #pragma mark - FindPanel -FindPanel::FindPanel(BFile* node, FindWindow* parent, bool fromTemplate, - bool editTemplateOnly) +FindPanel::FindPanel(BFile* node, FindWindow* parent, bool fromTemplate, bool editTemplateOnly) : BView("MainView", B_WILL_DRAW), fMode(kByNameItem), fAttrGrid(NULL), - fDraggableIcon(NULL) + fDraggableIcon(NULL), + fDirectorySelectPanel(NULL), + fAddSeparatorItemState(true) { SetViewUIColor(B_PANEL_BACKGROUND_COLOR); SetLowUIColor(ViewUIColor()); @@ -1125,6 +1179,12 @@ FindPanel::FindPanel(BFile* node, FindWindow* parent, bool fromTemplate, BMenuField* volumeField = new BMenuField("", B_TRANSLATE("On"), fVolMenu); volumeField->SetDivider(volumeField->StringWidth(volumeField->Label()) + 8); AddVolumes(fVolMenu); + fVolMenu->AddSeparatorItem(); + if (fDirectoryFilters.CountItems() > 0) + fVolMenu->AddSeparatorItem(); + fVolMenu->AddItem(new BMenuItem(B_TRANSLATE("Select folders" B_UTF8_ELLIPSIS), + new BMessage(kSelectDirectoryFilter))); + LoadDirectoryFiltersFromFile(node); // add Search button BButton* button; @@ -1186,6 +1246,192 @@ FindPanel::FindPanel(BFile* node, FindWindow* parent, bool fromTemplate, FindPanel::~FindPanel() { + int32 count = fDirectoryFilters.CountItems(); + for (int32 i = 0; i < count; i++) + delete fDirectoryFilters.RemoveItemAt(i); +} + + +status_t +FindPanel::AddDirectoryFiltersToMenu(BMenu* menu, BHandler* target) +{ + if (menu == NULL) + return B_BAD_VALUE; + + int32 count = fDirectoryFilters.CountItems(); + for (int32 i = 0; i < count; i++) { + entry_ref* filter = fDirectoryFilters.ItemAt(i); + if (filter != NULL) + FindPanel::AddDirectoryFilterItemToMenu(menu, filter, target); + } + + return B_OK; +} + + +void +FindPanel::LoadDirectoryFiltersFromFile(const BNode* node) +{ + if (node == NULL) + return; + + struct attr_info info; + if (node->GetAttrInfo("_trk/directories", &info) != B_OK) + return; + + BString bufferString; + char* buffer = bufferString.LockBuffer(info.size); + if (node->ReadAttr("_trk/directories", B_MESSAGE_TYPE, 0, buffer, (size_t)info.size) + != info.size) { + return; + } + + BMessage message; + if (message.Unflatten(buffer) != B_OK) + return; + + int32 count; + if (message.GetInfo("refs", NULL, &count) != B_OK) + return; + + for (int32 i = 0; i < count; i++) { + entry_ref ref; + if (message.FindRef("refs", i, &ref) != B_OK) + continue; + + BEntry entry(&ref); + if (entry.InitCheck() == B_OK && entry.Exists() && !entry.IsDirectory()) + continue; + + AddDirectoryFilter(&ref); + } + + bufferString.UnlockBuffer(); +} + + +status_t +FindPanel::AddDirectoryFilterItemToMenu(BMenu* menu, const entry_ref* ref, BHandler* target, + int32 index) +{ + if (menu == NULL || ref == NULL || target == NULL) + return B_BAD_VALUE; + + BEntry entry(ref, true); + if (entry.InitCheck() != B_OK) + return B_ERROR; + + if (entry.Exists() && entry.IsDirectory()) { + entry_ref symlinkTraversedDirectory; + entry.GetRef(&symlinkTraversedDirectory); + + // Adding the options into the fVolMenu + Model model(&entry); + BMenuItem* item = new ModelMenuItem(&model, model.Name(), NULL); + BMessage* message = new BMessage(kRemoveDirectoryFilter); + message->AddPointer("pointer", item); + message->AddRef("refs", &symlinkTraversedDirectory); + item->SetMessage(message); + item->SetMarked(true); + item->SetTarget(target); + + bool status = false; + if (index == -1) + status = menu->AddItem(item); + else + status = menu->AddItem(item, index); + + return status ? B_OK : B_ERROR; + + } else if (!entry.IsDirectory()) { + return B_NOT_A_DIRECTORY; + } else { + return B_ENTRY_NOT_FOUND; + } +} + + +status_t +FindPanel::AddDirectoryFilter(const entry_ref* ref, bool addToMenu) +{ + if (ref == NULL) + return B_BAD_VALUE; + + // Check for Duplicate Entry + int32 count = fDirectoryFilters.CountItems(); + for (int32 i = 0; i < count; i++) { + entry_ref* item = fDirectoryFilters.ItemAt(i); + if (ref != NULL && item != NULL && *item == *ref) + return B_CANCELED; + } + + status_t error = B_OK; + + if (addToMenu) { + if (fAddSeparatorItemState) { + BMenuItem* addDirectoriesItem = fVolMenu->RemoveItem(fVolMenu->CountItems() - 1); + error = FindPanel::AddDirectoryFilterItemToMenu(fVolMenu, ref, this); + fVolMenu->AddSeparatorItem(); + fVolMenu->AddItem(addDirectoriesItem); + fAddSeparatorItemState = false; + } else { + int32 index = fVolMenu->CountItems() - 2; + error = FindPanel::AddDirectoryFilterItemToMenu(fVolMenu, ref, this, index); + } + + UnmarkDisks(); + } + + if (error == B_OK) { + fDirectoryFilters.AddItem(new entry_ref(*ref)); + return B_OK; + } else { + return B_ERROR; + } +} + + +void +FindPanel::RemoveDirectoryFilter(const entry_ref* ref) +{ + ASSERT(ref != NULL); + int32 count = fDirectoryFilters.CountItems(); + for (int32 i = 0; i < count; i++) { + entry_ref* item = fDirectoryFilters.ItemAt(i); + if (item != NULL && ref != NULL && (*item) == (*ref)) + fDirectoryFilters.RemoveItemAt(i); + } +} + + +status_t +FindPanel::SaveDirectoryFiltersToFile(BNode* node) +{ + if (node->InitCheck() != B_OK) + return B_NO_INIT; + + // Store the entry_refs of the fDirectoryFilters to a BMessage + // So that it can be serialized. + BMessage message; + int32 count = fDirectoryFilters.CountItems(); + for (int32 i = 0; i < count; i++) { + entry_ref* ref = fDirectoryFilters.ItemAt(i); + if (message.AddRef("refs", ref) != B_OK) + return B_ERROR; + } + + // Serialize and Write the Attribute + ssize_t size = message.FlattenedSize(); + BString bufferString; + char* buffer = bufferString.LockBuffer(size); + if (message.Flatten(buffer, size) == B_OK) { + if (node->WriteAttr("_trk/directories", B_MESSAGE_TYPE, 0, buffer, (size_t)size) != size) + return B_IO_ERROR; + else + return B_OK; + } + + return B_ERROR; } @@ -1294,6 +1540,8 @@ FindPanel::ResizeMenuField(BMenuField* menuField) } } + width = std::max(width, menuField->StringWidth(B_TRANSLATE("Multiple selections"))); + float maxWidth = be_control_look->DefaultItemSpacing() * 20; size.width = std::min(width + padding, maxWidth); menuField->SetExplicitSize(size); @@ -1318,38 +1566,35 @@ PopUpMenuSetTitle(BMenu* menu, const char* title) void FindPanel::ShowVolumeMenuLabel() { - if (fVolMenu->ItemAt(0)->IsMarked()) { - // "all disks" selected - PopUpMenuSetTitle(fVolMenu, fVolMenu->ItemAt(0)->Label()); - return; - } - // find out if more than one items are marked - int32 count = fVolMenu->CountItems(); - int32 countSelected = 0; - BMenuItem* tmpItem = NULL; - for (int32 index = 2; index < count; index++) { - BMenuItem* item = fVolMenu->ItemAt(index); - if (item->IsMarked()) { - countSelected++; - tmpItem = item; + int32 totalVolumes = GetNumberOfVolumes(); + int32 selectedVolumesCount = 0; + + BMenuItem* lastSelectedVolumeItem = NULL; + + for (int32 i = 2; i < totalVolumes + 2; ++i) { + BMenuItem* volumeItem = fVolMenu->ItemAt(i); + if (volumeItem->IsMarked()) { + ++selectedVolumesCount; + lastSelectedVolumeItem = volumeItem; } } - if (countSelected == 0) { - // no disk selected, for now revert to search all disks - // ToDo: - // show no disks here and add a check that will not let the - // query go if the user doesn't pick at least one + if (fDirectoryFilters.CountItems() > 1) { + PopUpMenuSetTitle(fVolMenu, B_TRANSLATE_COMMENT("multiple selections", + "The user has selected multiple menu items")); + } else if (fDirectoryFilters.CountItems() == 1) { + PopUpMenuSetTitle(fVolMenu, fDirectoryFilters.ItemAt(0)->name); + } else if (selectedVolumesCount == 0 || selectedVolumesCount == totalVolumes) { fVolMenu->ItemAt(0)->SetMarked(true); PopUpMenuSetTitle(fVolMenu, fVolMenu->ItemAt(0)->Label()); - } else if (countSelected > 1) - // if more than two disks selected, don't use the disk name - // as a label - PopUpMenuSetTitle(fVolMenu, B_TRANSLATE("multiple disks")); - else { - ASSERT(tmpItem); - PopUpMenuSetTitle(fVolMenu, tmpItem->Label()); + } else if (selectedVolumesCount == 1) { + fVolMenu->ItemAt(0)->SetMarked(false); + PopUpMenuSetTitle(fVolMenu, lastSelectedVolumeItem->Label()); + } else { + fVolMenu->ItemAt(0)->SetMarked(false); + PopUpMenuSetTitle(fVolMenu, B_TRANSLATE_COMMENT("multiple selections", + "The user has selected multiple menu items")); } } @@ -1403,6 +1648,18 @@ FindPanel::Draw(BRect) } +void +FindPanel::UnmarkDisks() +{ + int32 startingIndex = 2; + int32 endingIndex = 2 + GetNumberOfVolumes(); + for (int32 i = startingIndex; i < endingIndex; ++i) + fVolMenu->ItemAt(i)->SetMarked(false); + + fVolMenu->ItemAt(0)->SetMarked(false); +} + + void FindPanel::MessageReceived(BMessage* message) { @@ -1427,8 +1684,18 @@ FindPanel::MessageReceived(BMessage* message) if (dev == -1) { // all disks selected, uncheck everything else - int32 count = menu->CountItems(); - for (int32 index = 2; index < count; index++) + int32 count = 0; + BVolumeRoster roster; + BVolume volume; + while (roster.GetNextVolume(&volume) == B_OK) { + if (volume.IsPersistent() && volume.KnowsQuery()) { + BDirectory root; + if (volume.GetRootDirectory(&root) != B_OK) + continue; + count++; + } + } + for (int32 index = 2; index < count + 2; index++) menu->ItemAt(index)->SetMarked(false); // make all disks the title and check it @@ -1450,12 +1717,74 @@ FindPanel::MessageReceived(BMessage* message) } } } + + int32 count = fVolMenu->CountItems(); + int32 startingIndex = 3 + GetNumberOfVolumes(); + int32 endingIndex = count - 2; + for (int32 i = startingIndex; i < endingIndex; ++i) { + BMenuItem* menuItem = fVolMenu->ItemAt(i); + BMessage* message = menuItem->Message(); + entry_ref ref; + if (!message || message->FindRef("refs", &ref) != B_OK) + continue; + RemoveDirectoryFilter(&ref); + menuItem->SetMarked(false); + } + // make sure the right label is showing ShowVolumeMenuLabel(); break; } + case kSelectDirectoryFilter: + { + if (fDirectorySelectPanel == NULL) { + BRefFilter* filter = new FolderFilter(); + fDirectorySelectPanel = new BFilePanel(B_OPEN_PANEL, new BMessenger(this), NULL, + B_DIRECTORY_NODE, true, new BMessage(kAddDirectoryFilters), filter); + } + + fDirectorySelectPanel->Window()->SetTitle(B_TRANSLATE("Select folders")); + fDirectorySelectPanel->Show(); + break; + } + + case kAddDirectoryFilters: + { + int32 count; + message->GetInfo("refs", NULL, &count); + for (int32 i = 0; i < count; i++) { + entry_ref ref; + status_t error = message->FindRef("refs", i, &ref); + if (error == B_OK) + AddDirectoryFilter(&ref); + } + ShowVolumeMenuLabel(); + break; + } + + case kRemoveDirectoryFilter: + { + BMenuItem* item; + entry_ref ref; + if (message->FindPointer("pointer", (void**)&item) == B_OK + && message->FindRef("refs", &ref) == B_OK) { + + if (item->IsMarked()) { + RemoveDirectoryFilter(&ref); + item->SetMarked(false); + } else { + AddDirectoryFilter(&ref, false); + item->SetMarked(true); + UnmarkDisks(); + } + + ShowVolumeMenuLabel(); + } + break; + } + case kByAttributeItem: case kByNameItem: case kByFormulaItem: @@ -2331,19 +2660,21 @@ FindPanel::AddRecentQueries(BMenu* menu, bool addSaveAsItem, const BMessenger* t struct attr_info info; if (node.GetAttrInfo(kAttrQueryLastChange, &info) != B_OK) continue; - + if (info.type == B_MESSAGE_TYPE) { - char* buffer = new char[info.size]; + BString bufferString; + char* buffer = bufferString.LockBuffer(info.size); BMessage message; if (node.ReadAttr(kAttrQueryLastChange, B_MESSAGE_TYPE, 0, buffer, static_cast(info.size)) != info.size || message.Unflatten(buffer) != B_OK) continue; - + bufferString.UnlockBuffer(); + int32 count; if (message.GetInfo(kAttrQueryLastChange, NULL, &count) != B_OK) continue; - + for (int32 i = 0; i < count; i++) { int32 time; if (message.FindInt32(kAttrQueryLastChange, i, &time) @@ -2725,7 +3056,8 @@ FindPanel::RestoreWindowState(const BNode* node) } // get volumes to perform query on - bool searchAllVolumes = true; + + int32 selectedVolumes = 0; attr_info info; if (node->GetAttrInfo(kAttrQueryVolume, &info) == B_OK) { @@ -2744,8 +3076,8 @@ FindPanel::RestoreWindowState(const BNode* node) if (result == B_OK) { char name[256]; volume.GetName(name); - SelectItemWithLabel(fVolMenu, name); - searchAllVolumes = false; + if (SelectItemWithLabel(fVolMenu, name) != -1) + ++selectedVolumes; } else if (result != B_DEV_BAD_DRIVE_NUM) // if B_DEV_BAD_DRIVE_NUM, the volume just isn't // mounted this time around, keep looking for more @@ -2756,8 +3088,14 @@ FindPanel::RestoreWindowState(const BNode* node) } delete[] buffer; } + + LoadDirectoryFiltersFromFile(node); // mark or unmark "All disks" - fVolMenu->ItemAt(0)->SetMarked(searchAllVolumes); + if (selectedVolumes == GetNumberOfVolumes()) { + fVolMenu->ItemAt(0)->SetMarked(true); + for (int32 i = 0; i < GetNumberOfVolumes() + 2; ++i) + fVolMenu->ItemAt(i)->SetMarked(false); + } ShowVolumeMenuLabel(); switch (Mode()) { diff --git a/src/kits/tracker/FindPanel.h b/src/kits/tracker/FindPanel.h index b2e83ff092..6a0807f437 100644 --- a/src/kits/tracker/FindPanel.h +++ b/src/kits/tracker/FindPanel.h @@ -36,9 +36,10 @@ All rights reserved. #include +#include #include -#include #include +#include #include "DialogPane.h" #include "MimeTypeList.h" @@ -81,6 +82,10 @@ const uint32 kOpenLoadQueryPanel = 'Folo'; const uint32 kTemporaryOptionClicked = 'FTCl'; const uint32 kSearchInTrashOptionClicked = 'FSCl'; +const uint32 kSelectDirectoryFilter = 'FSeD'; +const uint32 kAddDirectoryFilters = 'FAdF'; +const uint32 kRemoveDirectoryFilter = 'FRmD'; + #ifdef _IMPEXP_TRACKER _IMPEXP_TRACKER #endif @@ -246,6 +251,9 @@ public: bool includeTemporaryQueries = true, bool includePersistedQueries = true); + status_t SaveDirectoryFiltersToFile(BNode*); + void LoadDirectoryFiltersFromFile(const BNode*); + private: // populates the type menu void AddMimeTypesToMenu(); @@ -299,6 +307,17 @@ private: void ResizeMenuField(BMenuField*); + status_t AddDirectoryFiltersToMenu(BMenu*, BHandler* target); + status_t AddDirectoryFilter(const entry_ref* ref, bool addToMenu = true); + void RemoveDirectoryFilter(const entry_ref* ref); + static status_t AddDirectoryFilterItemToMenu(BMenu*, const entry_ref* ref, + BHandler* target, int32 index = -1); + void UnmarkDisks(); + void MarkDiskAccordingToDirectoryFilter(entry_ref*); + +public: + BObjectList fDirectoryFilters; + private: uint32 fMode; BGridLayout* fAttrGrid; @@ -313,6 +332,10 @@ private: DraggableIcon* fDraggableIcon; + BFilePanel* fDirectorySelectPanel; + + bool fAddSeparatorItemState; + typedef BView _inherited; friend class RecentQueriesPopUp; @@ -381,6 +404,12 @@ protected: virtual void Draw(BRect); }; + +class FolderFilter : public BRefFilter { +public: + virtual bool Filter(const entry_ref*, BNode*, struct stat_beos*, const char*); +}; + } // namespace BPrivate using namespace BPrivate; diff --git a/src/kits/tracker/PoseView.cpp b/src/kits/tracker/PoseView.cpp index bd83e0a1c6..dbd465fc34 100644 --- a/src/kits/tracker/PoseView.cpp +++ b/src/kits/tracker/PoseView.cpp @@ -1385,7 +1385,6 @@ BPoseView::AddPosesTask(void* castToParams) AddPosesParams* params = (AddPosesParams*)castToParams; BMessenger target(params->target); entry_ref ref(params->ref); - delete params; AutoLockingMessenger lock(target); @@ -1456,6 +1455,7 @@ BPoseView::AddPosesTask(void* castToParams) // have to node monitor ahead of time because Model will // cache up the file type and preferred app // OK to call when poseView is not locked + model = new Model(&dirNode, &itemNode, eptr->d_name, false); result = model->InitCheck(); modelChunkIndex++; diff --git a/src/kits/tracker/QueryPoseView.cpp b/src/kits/tracker/QueryPoseView.cpp index 3781cadb51..592fc98dc2 100644 --- a/src/kits/tracker/QueryPoseView.cpp +++ b/src/kits/tracker/QueryPoseView.cpp @@ -94,6 +94,45 @@ BQueryPoseView::~BQueryPoseView() } +bool +FolderFilterFunction(const entry_ref* directory, const entry_ref* model) +{ + if (directory == NULL || model == NULL) + return false; + + BPath directoryPath(directory); + BPath modelPath(model); + + if (directoryPath.InitCheck() != B_OK || modelPath.InitCheck() != B_OK) + return false; + + char* requiredDirectoryPath = const_cast(directoryPath.Path()); + strcat(requiredDirectoryPath, "/"); + // only supports searching completely in the directories as well as subdirectories for now. + return strncmp(requiredDirectoryPath, modelPath.Path(), strlen(requiredDirectoryPath)) == 0; +} + + +bool +QueryRefFilter::PassThroughDirectoryFilters(const entry_ref* ref) const +{ + int32 count = fDirectoryFilters.CountItems(); + bool passed = count == 0; + // in this context, even if the model passes through a single filter, it is considered + // as the folder selections are combined with OR! and not AND! + + for (int32 i = 0; i < count; i++) { + entry_ref* filterDirectory = fDirectoryFilters.ItemAt(i); + if (FolderFilterFunction(filterDirectory, ref)) { + passed = true; + break; + } + } + + return passed; +} + + void BQueryPoseView::MessageReceived(BMessage* message) { @@ -322,7 +361,11 @@ BQueryPoseView::InitDirentIterator(const entry_ref* ref) delta); } - SetRefFilter(new QueryRefFilter(fQueryListContainer->ShowResultsFromTrash())); + QueryRefFilter* filter = new QueryRefFilter(fQueryListContainer->ShowResultsFromTrash()); + TargetModel()->OpenNode(); + filter->LoadDirectoryFiltersFromFile(TargetModel()->Node()); + TargetModel()->CloseNode(); + SetRefFilter(filter); return fQueryListContainer->Clone(); } @@ -391,13 +434,85 @@ QueryRefFilter::QueryRefFilter(bool showResultsFromTrash) } +QueryRefFilter::~QueryRefFilter() +{ + int32 count = fDirectoryFilters.CountItems(); + for (int32 i = 0; i < count; i++) + delete fDirectoryFilters.RemoveItemAt(0); +} + + +status_t +QueryRefFilter::LoadDirectoryFiltersFromFile(const BNode* node) +{ + // params checking + if (node == NULL || node->InitCheck() != B_OK) + return B_BAD_VALUE; + + struct attr_info info; + status_t error = node->GetAttrInfo("_trk/directories", &info); + if (error != B_OK) + return error; + + BString bufferString; + char* buffer = bufferString.LockBuffer(info.size); + if (node->ReadAttr("_trk/directories", B_MESSAGE_TYPE, 0, buffer, info.size) != info.size) + return B_ERROR; + + BMessage message; + error = message.Unflatten(buffer); + if (error != B_OK) + return error; + + int32 count; + if ((error = message.GetInfo("refs", NULL, &count)) != B_OK) + return error; + + for (int32 i = 0; i < count; i++) { + entry_ref ref; + if ((error = message.FindRef("refs", i, &ref)) != B_OK) + continue; + + AddDirectoryFilter(&ref); + } + + return B_OK; +} + + +status_t +QueryRefFilter::AddDirectoryFilter(const entry_ref* ref) +{ + if (ref == NULL) + return B_BAD_VALUE; + + // checking for duplicates + int32 count = fDirectoryFilters.CountItems(); + for (int32 i = 0; i < count; i++) { + entry_ref* item = fDirectoryFilters.ItemAt(i); + if (ref != NULL && item != NULL && *item == *ref) + return B_CANCELED; + } + + BEntry entry(ref, true); + if (entry.InitCheck() != B_OK || !entry.Exists() || !entry.IsDirectory()) + return B_ERROR; + + entry_ref symlinkTraversedRef; + entry.GetRef(&symlinkTraversedRef); + + fDirectoryFilters.AddItem(new entry_ref(symlinkTraversedRef)); + return B_OK; +} + + bool QueryRefFilter::Filter(const entry_ref* ref, BNode* node, stat_beos* st, const char* filetype) { TTracker* tracker = dynamic_cast(be_app); - return !(!fShowResultsFromTrash && tracker != NULL - && tracker->InTrashNode(ref)); + return !(!fShowResultsFromTrash && tracker != NULL && tracker->InTrashNode(ref)) + && PassThroughDirectoryFilters(ref); } diff --git a/src/kits/tracker/QueryPoseView.h b/src/kits/tracker/QueryPoseView.h index cb4f363af4..2d535fa1ef 100644 --- a/src/kits/tracker/QueryPoseView.h +++ b/src/kits/tracker/QueryPoseView.h @@ -96,11 +96,17 @@ private: class QueryRefFilter : public BRefFilter { public: QueryRefFilter(bool showResultsFromTrash); - bool Filter(const entry_ref* ref, BNode* node, stat_beos* st, + virtual ~QueryRefFilter(); + virtual bool Filter(const entry_ref* ref, BNode* node, stat_beos* st, const char* filetype); + status_t LoadDirectoryFiltersFromFile(const BNode*); + status_t AddDirectoryFilter(const entry_ref*); + bool PassThroughDirectoryFilters(const entry_ref*) const; + private: bool fShowResultsFromTrash; + BObjectList fDirectoryFilters; };