376 lines
8.4 KiB
C++
376 lines
8.4 KiB
C++
/*
|
|
* Copyright 2011, Haiku, Inc. All rights reserved.
|
|
* Distributed under the terms of the MIT License.
|
|
*
|
|
* Authors:
|
|
* Clemens Zeidler <haiku@clemens-zeidler.de>
|
|
*/
|
|
|
|
#include "MusicCollectionWindow.h"
|
|
|
|
#include <Application.h>
|
|
#include <ControlLook.h>
|
|
#include <Debug.h>
|
|
#include <ScrollView.h>
|
|
#include <VolumeRoster.h>
|
|
|
|
#include <NaturalCompare.h>
|
|
|
|
#include "ALMLayout.h"
|
|
#include "ALMLayoutBuilder.h"
|
|
|
|
|
|
static int
|
|
StringItemComp(const BListItem* first, const BListItem* second)
|
|
{
|
|
BStringItem* firstItem = (BStringItem*)first;
|
|
BStringItem* secondItem = (BStringItem*)second;
|
|
return BPrivate::NaturalCompare(firstItem->Text(), secondItem->Text());
|
|
}
|
|
|
|
|
|
template <class ListItem = FileListItem>
|
|
class ListViewListener : public EntryViewInterface {
|
|
public:
|
|
ListViewListener(BOutlineListView* list, BStringView* countView)
|
|
:
|
|
fListView(list),
|
|
fCountView(countView),
|
|
fItemCount(0)
|
|
{
|
|
|
|
}
|
|
|
|
|
|
void
|
|
SetQueryString(const char* string)
|
|
{
|
|
fQueryString = string;
|
|
}
|
|
|
|
|
|
void
|
|
EntryCreated(WatchedFile* file)
|
|
{
|
|
//ListItem* item1 = new ListItem(file->entry.name, file);
|
|
//fListView->AddItem(item1);
|
|
|
|
fItemCount++;
|
|
BString count("Count: ");
|
|
count << fItemCount;
|
|
fCountView->SetText(count);
|
|
|
|
const ssize_t bufferSize = 256;
|
|
char buffer[bufferSize];
|
|
BNode node(&file->entry);
|
|
|
|
ssize_t readBytes;
|
|
readBytes = node.ReadAttr("Audio:Artist", B_STRING_TYPE, 0, buffer,
|
|
bufferSize);
|
|
if (readBytes < 0)
|
|
readBytes = 0;
|
|
if (readBytes >= bufferSize)
|
|
readBytes = bufferSize - 1;
|
|
buffer[readBytes] = '\0';
|
|
|
|
BString artist = (strcmp(buffer, "") == 0) ? "Unknown" : buffer;
|
|
ListItem* artistItem = _AddSuperItem(artist, fArtistList, NULL);
|
|
|
|
readBytes = node.ReadAttr("Audio:Album", B_STRING_TYPE, 0, buffer,
|
|
bufferSize);
|
|
if (readBytes < 0)
|
|
readBytes = 0;
|
|
buffer[readBytes] = '\0';
|
|
BString album = (strcmp(buffer, "") == 0) ? "Unknown" : buffer;
|
|
ListItem* albumItem = _AddSuperItem(album, fAlbumList, artistItem);
|
|
|
|
readBytes = node.ReadAttr("Media:Title", B_STRING_TYPE, 0, buffer,
|
|
bufferSize);
|
|
if (readBytes < 0)
|
|
readBytes = 0;
|
|
buffer[readBytes] = '\0';
|
|
BString title= (strcmp(buffer, "") == 0) ? file->entry.name
|
|
: buffer;
|
|
|
|
ListItem* item = new ListItem(title, file);
|
|
file->cookie = item;
|
|
fListView->AddUnder(item, albumItem);
|
|
fListView->SortItemsUnder(albumItem, true, StringItemComp);
|
|
|
|
if (fQueryString == "")
|
|
return;
|
|
if (title.IFindFirst(fQueryString) >= 0) {
|
|
fListView->Expand(artistItem);
|
|
fListView->Expand(albumItem);
|
|
} else if (album.IFindFirst(fQueryString) >= 0) {
|
|
fListView->Expand(artistItem);
|
|
}
|
|
};
|
|
|
|
|
|
void
|
|
EntryRemoved(WatchedFile* file)
|
|
{
|
|
ListItem* item = (ListItem*)file->cookie;
|
|
ListItem* album = (ListItem*)fListView->Superitem(item);
|
|
fListView->RemoveItem(item);
|
|
if (album != NULL && fListView->CountItemsUnder(album, true) == 0) {
|
|
ListItem* artist = (ListItem*)fListView->Superitem(album);
|
|
fListView->RemoveItem(album);
|
|
if (artist != NULL && fListView->CountItemsUnder(artist, true) == 0)
|
|
fListView->RemoveItem(artist);
|
|
}
|
|
};
|
|
|
|
void
|
|
EntryMoved(WatchedFile* file)
|
|
{
|
|
AttrChanged(file);
|
|
};
|
|
|
|
|
|
void
|
|
AttrChanged(WatchedFile* file)
|
|
{
|
|
EntryRemoved(file);
|
|
EntryCreated(file);
|
|
}
|
|
|
|
|
|
void
|
|
EntriesCleared()
|
|
{
|
|
for (int32 i = 0; i < fListView->FullListCountItems(); i++)
|
|
delete fListView->FullListItemAt(i);
|
|
fListView->MakeEmpty();
|
|
|
|
fArtistList.MakeEmpty();
|
|
fAlbumList.MakeEmpty();
|
|
|
|
printf("prev count %i\n", (int)fItemCount);
|
|
fItemCount = 0;
|
|
fCountView->SetText("Count: 0");
|
|
}
|
|
|
|
|
|
private:
|
|
ListItem*
|
|
_AddSuperItem(const char* name, BObjectList<ListItem>& list,
|
|
ListItem* under)
|
|
{
|
|
ListItem* item = _FindStringItem(list, name, under);
|
|
if (item != NULL)
|
|
return item;
|
|
|
|
item = new ListItem(name);
|
|
fListView->AddUnder(item, under);
|
|
fListView->SortItemsUnder(under, true, StringItemComp);
|
|
list.AddItem(item);
|
|
|
|
fListView->Collapse(item);
|
|
|
|
return item;
|
|
}
|
|
|
|
ListItem*
|
|
_FindStringItem(BObjectList<ListItem>& list, const char* text,
|
|
ListItem* parent)
|
|
{
|
|
for (int32 i = 0; i < list.CountItems(); i++) {
|
|
ListItem* item = list.ItemAt(i);
|
|
ListItem* superItem = (ListItem*)fListView->Superitem(item);
|
|
if (parent != NULL && parent != superItem)
|
|
continue;
|
|
if (strcmp(item->Text(), text) == 0)
|
|
return item;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
BOutlineListView* fListView;
|
|
BStringView* fCountView;
|
|
|
|
BObjectList<ListItem> fArtistList;
|
|
BObjectList<ListItem> fAlbumList;
|
|
|
|
BString fQueryString;
|
|
int32 fItemCount;
|
|
};
|
|
|
|
|
|
const uint32 kMsgQueryInput = '&qin';
|
|
const uint32 kMsgItemInvoked = '&iin';
|
|
|
|
|
|
MusicCollectionWindow::MusicCollectionWindow(BRect frame, const char* title)
|
|
:
|
|
BWindow(frame, title, B_DOCUMENT_WINDOW, B_AVOID_FRONT)
|
|
{
|
|
fQueryField = new BTextControl("Search: ", "", NULL);
|
|
fQueryField->SetExplicitAlignment(BAlignment(B_ALIGN_HORIZONTAL_CENTER,
|
|
B_ALIGN_USE_FULL_HEIGHT));
|
|
fQueryField->SetModificationMessage(new BMessage(kMsgQueryInput));
|
|
|
|
fCountView = new BStringView("Count View", "Count:");
|
|
|
|
fFileListView = new MusicFileListView("File List View");
|
|
fFileListView->SetInvocationMessage(new BMessage(kMsgItemInvoked));
|
|
BScrollView* scrollView = new BScrollView("list scroll", fFileListView, 0,
|
|
true, true, B_PLAIN_BORDER);
|
|
|
|
BALMLayout* layout = new BALMLayout(B_USE_ITEM_SPACING, B_USE_ITEM_SPACING);
|
|
BALM::BALMLayoutBuilder(this, layout)
|
|
.SetInsets(B_USE_WINDOW_INSETS)
|
|
.Add(fQueryField, layout->Left(), layout->Top())
|
|
.StartingAt(fQueryField)
|
|
.AddToRight(fCountView, layout->Right())
|
|
.AddBelow(scrollView, layout->Bottom(), layout->Left(),
|
|
layout->Right());
|
|
|
|
Area* area = layout->AreaFor(scrollView);
|
|
area->SetLeftInset(0);
|
|
area->SetRightInset(0);
|
|
area->SetBottomInset(0);
|
|
|
|
BSize min = layout->MinSize();
|
|
BSize max = layout->MaxSize();
|
|
SetSizeLimits(min.Width(), max.Width(), min.Height(), max.Height());
|
|
|
|
fEntryViewInterface = new ListViewListener<FileListItem>(fFileListView,
|
|
fCountView);
|
|
fQueryHandler = new QueryHandler(fEntryViewInterface);
|
|
AddHandler(fQueryHandler);
|
|
fQueryReader = new QueryReader(fQueryHandler);
|
|
fQueryHandler->SetReadThread(fQueryReader);
|
|
|
|
// start initial query
|
|
PostMessage(kMsgQueryInput);
|
|
}
|
|
|
|
|
|
MusicCollectionWindow::~MusicCollectionWindow()
|
|
{
|
|
delete fQueryReader;
|
|
delete fQueryHandler;
|
|
delete fEntryViewInterface;
|
|
}
|
|
|
|
|
|
bool
|
|
MusicCollectionWindow::QuitRequested()
|
|
{
|
|
be_app->PostMessage(B_QUIT_REQUESTED);
|
|
return true;
|
|
}
|
|
|
|
|
|
void
|
|
MusicCollectionWindow::MessageReceived(BMessage* message)
|
|
{
|
|
switch (message->what) {
|
|
case kMsgQueryInput:
|
|
_StartNewQuery();
|
|
break;
|
|
|
|
case kMsgItemInvoked:
|
|
fFileListView->Launch(message);
|
|
break;
|
|
|
|
default:
|
|
BWindow::MessageReceived(message);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
CaseInsensitiveString(BString &instring, BString &outstring)
|
|
{
|
|
outstring = "";
|
|
|
|
for (int i = 0; instring[i]; i++) {
|
|
if (isupper(instring[i])) {
|
|
int ch = tolower(instring[i]);
|
|
outstring += "[";
|
|
outstring += ch;
|
|
outstring += instring[i];
|
|
outstring += "]";
|
|
} else if (islower(instring[i])) {
|
|
int ch = toupper(instring[i]);
|
|
outstring += "[";
|
|
outstring += instring[i];
|
|
outstring += ch;
|
|
outstring += "]";
|
|
} else
|
|
outstring += instring[i];
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
MusicCollectionWindow::_StartNewQuery()
|
|
{
|
|
fQueryReader->Reset();
|
|
fQueryHandler->Reset();
|
|
|
|
BString orgString = fQueryField->Text();
|
|
((ListViewListener<FileListItem>*)fEntryViewInterface)->SetQueryString(
|
|
orgString);
|
|
|
|
BVolume volume;
|
|
//BVolumeRoster().GetBootVolume(&volume);
|
|
BVolumeRoster roster;
|
|
while (roster.GetNextVolume(&volume) == B_OK) {
|
|
if (!volume.KnowsQuery())
|
|
continue;
|
|
BQuery* query = _CreateQuery(orgString);
|
|
query->SetVolume(&volume);
|
|
fQueryReader->AddQuery(query);
|
|
}
|
|
|
|
fQueryReader->Run();
|
|
}
|
|
|
|
|
|
BQuery*
|
|
MusicCollectionWindow::_CreateQuery(BString& orgString)
|
|
{
|
|
BQuery* query = new BQuery;
|
|
|
|
BString queryString;
|
|
CaseInsensitiveString(orgString, queryString);
|
|
|
|
query->PushAttr("Media:Title");
|
|
query->PushString(queryString);
|
|
query->PushOp(B_CONTAINS);
|
|
|
|
query->PushAttr("Audio:Album");
|
|
query->PushString(queryString);
|
|
query->PushOp(B_CONTAINS);
|
|
query->PushOp(B_OR);
|
|
|
|
query->PushAttr("Audio:Artist");
|
|
query->PushString(queryString);
|
|
query->PushOp(B_CONTAINS);
|
|
query->PushOp(B_OR);
|
|
|
|
if (queryString == "") {
|
|
query->PushAttr("BEOS:TYPE");
|
|
query->PushString("audio/");
|
|
query->PushOp(B_BEGINS_WITH);
|
|
query->PushOp(B_OR);
|
|
}
|
|
|
|
query->PushAttr("BEOS:TYPE");
|
|
query->PushString("audio/");
|
|
query->PushOp(B_BEGINS_WITH);
|
|
|
|
query->PushAttr("name");
|
|
query->PushString(queryString);
|
|
query->PushOp(B_CONTAINS);
|
|
query->PushOp(B_AND);
|
|
query->PushOp(B_OR);
|
|
|
|
return query;
|
|
}
|