Find Panel: Implement Folder Filtering
This commit introduces folder filtering to the Find Panel and associated results window. --> Features: --> Directory Selection: Users can select directories using the "Select a Directory..." option, now available in the same menu as volume selection. --> File Panel Integration: Upon selecting this option, a File panel will appear, displaying only folders or symlinks to folders for selection. --> Folder Filter Management: Selected Folders are shown in the same menu, with the corresponding menu item marked. Clicking on the menu item removes the folder filter. --> Recursive Search: The folder filters are combined using the OR method. Results will show items from any selected folder, recursively checking all subfolders. --> Bug Fixes --> Resolved an issue with the saving functionality when resetting volumes or loading a query. Change-Id: I8eaab6ad8ebd3de09944a8bcf03f100c451225ae Reviewed-on: https://review.haiku-os.org/c/haiku/+/7845 Reviewed-by: Niels Sascha Reedijk <[email protected]>
This commit is contained in:
committed by
Niels Sascha Reedijk
parent
c8dfbad953
commit
78c0fc35a7
+407
-69
@@ -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<size_t>(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<size_t>(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()) {
|
||||
|
||||
@@ -36,9 +36,10 @@ All rights reserved.
|
||||
|
||||
|
||||
#include <ByteOrder.h>
|
||||
#include <FilePanel.h>
|
||||
#include <ObjectList.h>
|
||||
#include <Window.h>
|
||||
#include <View.h>
|
||||
#include <Window.h>
|
||||
|
||||
#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<entry_ref> 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;
|
||||
|
||||
@@ -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++;
|
||||
|
||||
@@ -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<char*>(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<TTracker*>(be_app);
|
||||
return !(!fShowResultsFromTrash && tracker != NULL
|
||||
&& tracker->InTrashNode(ref));
|
||||
return !(!fShowResultsFromTrash && tracker != NULL && tracker->InTrashNode(ref))
|
||||
&& PassThroughDirectoryFilters(ref);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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<entry_ref> fDirectoryFilters;
|
||||
};
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user