mirror of
https://github.com/clangen/musikcube.git
synced 2024-10-02 13:02:35 +00:00
Much improved console UI with global hotkeys and some column formatting
This commit is contained in:
parent
e8b9c873db
commit
1d2541e100
@ -49,6 +49,8 @@ static const std::string TAG = "LocalLibrary";
|
||||
using namespace musik::core;
|
||||
using namespace musik::core::library;
|
||||
|
||||
#define VERBOSE_LOGGING 0
|
||||
|
||||
LibraryPtr LocalLibrary::Create(std::string name, int id) {
|
||||
LibraryPtr lib(new LocalLibrary(name, id));
|
||||
return lib;
|
||||
@ -140,7 +142,9 @@ int LocalLibrary::Enqueue(QueryPtr query, unsigned int options) {
|
||||
queryQueue.push_back(query);
|
||||
queueCondition.notify_all();
|
||||
|
||||
if (VERBOSE_LOGGING) {
|
||||
musik::debug::info(TAG, "query '" + query->Name() + "' enqueued");
|
||||
}
|
||||
|
||||
return query->GetId();
|
||||
}
|
||||
@ -185,13 +189,19 @@ void LocalLibrary::ThreadProc() {
|
||||
}
|
||||
|
||||
if (query) {
|
||||
if (VERBOSE_LOGGING) {
|
||||
musik::debug::info(TAG, "query '" + query->Name() + "' running");
|
||||
}
|
||||
|
||||
query->Run(this->db);
|
||||
this->QueryCompleted(query);
|
||||
|
||||
if (VERBOSE_LOGGING) {
|
||||
musik::debug::info(TAG, boost::str(boost::format(
|
||||
"query '%1%' finished with status=%2%") % query->Name() % query->GetStatus()));
|
||||
"query '%1%' finished with status=%2%")
|
||||
% query->Name()
|
||||
% query->GetStatus()));
|
||||
}
|
||||
|
||||
query.reset();
|
||||
}
|
||||
|
@ -46,8 +46,8 @@ namespace musik { namespace core { namespace library { namespace constants {
|
||||
static const char* DURATION = "duration";
|
||||
static const char* FILESIZE = "filesize";
|
||||
static const char* YEAR = "year";
|
||||
static const char* DISPLAY_GENRE_ID = "visual_genre_id";
|
||||
static const char* DISPLAY_ARTIST_ID = "visual_artist_id";
|
||||
static const char* GENRE_ID = "visual_genre_id";
|
||||
static const char* ARTIST_ID = "visual_artist_id";
|
||||
static const char* ALBUM_ID = "album_id";
|
||||
static const char* PATH_ID = "path_id";
|
||||
static const char* TITLE = "title";
|
||||
|
@ -41,7 +41,8 @@ using namespace musik::core::audio;
|
||||
static std::string TAG = "Transport";
|
||||
|
||||
Transport::Transport()
|
||||
: volume(1.0) {
|
||||
: volume(1.0)
|
||||
, state(StateStopped) {
|
||||
}
|
||||
|
||||
Transport::~Transport() {
|
||||
@ -49,6 +50,11 @@ Transport::~Transport() {
|
||||
this->currentPlayer.reset();
|
||||
}
|
||||
|
||||
Transport::PlaybackState Transport::GetPlaybackState() {
|
||||
boost::mutex::scoped_lock lock(this->stateMutex);
|
||||
return this->state;
|
||||
}
|
||||
|
||||
void Transport::PrepareNextTrack(std::string trackUrl) {
|
||||
PlayerPtr player = Player::Create(trackUrl);
|
||||
|
||||
@ -61,6 +67,12 @@ void Transport::PrepareNextTrack(std::string trackUrl) {
|
||||
void Transport::Start(std::string url) {
|
||||
musik::debug::info(TAG, "we were asked to start the track at " + url);
|
||||
|
||||
/* TODO FIXME: hack; the player is reference counted, we don't want the count
|
||||
to reach zero within the critical section, because its background thread may
|
||||
raise an event and cause a deadlock. do we really need shared_ptrs for these
|
||||
Player instances? */
|
||||
PlayerPtr current;
|
||||
|
||||
PlayerPtr player;
|
||||
|
||||
{
|
||||
@ -77,6 +89,7 @@ void Transport::Start(std::string url) {
|
||||
musik::debug::info(TAG, "Player created successfully");
|
||||
}
|
||||
|
||||
current = this->currentPlayer; /* see hack note above. */
|
||||
this->currentPlayer = newPlayer;
|
||||
|
||||
this->currentPlayer->PlaybackStarted.connect(this, &Transport::OnPlaybackStarted);
|
||||
@ -85,7 +98,7 @@ void Transport::Start(std::string url) {
|
||||
this->currentPlayer->PlaybackError.connect(this, &Transport::OnPlaybackError);
|
||||
|
||||
musik::debug::info(TAG, "play()");
|
||||
this->currentPlayer->Play();
|
||||
this->currentPlayer->Play(); /* non-blocking */
|
||||
|
||||
player = this->currentPlayer;
|
||||
}
|
||||
@ -272,6 +285,24 @@ void Transport::OnPlaybackError(Player *player) {
|
||||
}
|
||||
|
||||
void Transport::RaisePlaybackEvent(int type, PlayerPtr player) {
|
||||
/* TODO FIXME: should be either decoupled or merged with the playback
|
||||
event enum. */
|
||||
switch (type) {
|
||||
case EventStarted:
|
||||
case EventResumed:
|
||||
this->state = StatePlaying;
|
||||
break;
|
||||
|
||||
case EventFinished:
|
||||
case EventError:
|
||||
this->state = StateStopped;
|
||||
break;
|
||||
|
||||
case EventPaused:
|
||||
this->state = StatePaused;
|
||||
break;
|
||||
}
|
||||
|
||||
std::string uri = player ? player->GetUrl() : "";
|
||||
this->PlaybackEvent(type, uri);
|
||||
}
|
@ -37,12 +37,31 @@
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
#include <sigslot/sigslot.h>
|
||||
#include <boost/thread/mutex.hpp>
|
||||
#include <boost/thread/recursive_mutex.hpp>
|
||||
|
||||
namespace musik { namespace core { namespace audio {
|
||||
|
||||
class Transport : public sigslot::has_slots<> {
|
||||
public:
|
||||
sigslot::signal2<int, std::string> PlaybackEvent;
|
||||
sigslot::signal0<> VolumeChanged;
|
||||
|
||||
typedef enum {
|
||||
StateStopped,
|
||||
StatePaused,
|
||||
StatePlaying
|
||||
} PlaybackState;
|
||||
|
||||
typedef enum {
|
||||
EventScheduled = 0,
|
||||
EventStarted = 1,
|
||||
EventPaused = 2,
|
||||
EventResumed = 3,
|
||||
EventAlmostDone = 4,
|
||||
EventFinished = 5,
|
||||
EventError = -1
|
||||
} PlaybackEventType;
|
||||
|
||||
Transport();
|
||||
~Transport();
|
||||
|
||||
@ -58,19 +77,7 @@ namespace musik { namespace core { namespace audio {
|
||||
double Volume();
|
||||
void SetVolume(double volume);
|
||||
|
||||
public:
|
||||
typedef enum {
|
||||
EventScheduled = 0,
|
||||
EventStarted = 1,
|
||||
EventPaused = 2,
|
||||
EventResumed = 3,
|
||||
EventAlmostDone = 4,
|
||||
EventFinished = 5,
|
||||
EventError = -1
|
||||
} PlaybackEventType;
|
||||
|
||||
sigslot::signal2<int, std::string> PlaybackEvent;
|
||||
sigslot::signal0<> VolumeChanged;
|
||||
PlaybackState GetPlaybackState();
|
||||
|
||||
private:
|
||||
void RaisePlaybackEvent(int type, PlayerPtr player);
|
||||
@ -82,6 +89,7 @@ namespace musik { namespace core { namespace audio {
|
||||
|
||||
private:
|
||||
double volume;
|
||||
PlaybackState state;
|
||||
|
||||
boost::mutex stateMutex;
|
||||
PlayerPtr currentPlayer;
|
||||
|
@ -40,6 +40,7 @@
|
||||
|
||||
#include <app/layout/MainLayout.h>
|
||||
#include <app/layout/LibraryLayout.h>
|
||||
#include <app/util/GlobalHotkeys.h>
|
||||
|
||||
#include <boost/locale.hpp>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
@ -163,6 +164,8 @@ int main(int argc, char* argv[])
|
||||
using musik::core::LibraryFactory;
|
||||
LibraryPtr library = LibraryFactory::Libraries().at(0);
|
||||
|
||||
GlobalHotkeys globalHotkeys(tp);
|
||||
|
||||
LibraryLayout libraryLayout(tp, library);
|
||||
MainLayout mainLayout(tp, library);
|
||||
|
||||
@ -186,21 +189,16 @@ int main(int argc, char* argv[])
|
||||
}
|
||||
|
||||
if (ch != -1) { /* -1 = idle timeout */
|
||||
std::string kn = keyname(ch);
|
||||
std::string kn = keyname((int) ch);
|
||||
|
||||
if (ch == '\t') { /* tab */
|
||||
focusNextInLayout(state);
|
||||
}
|
||||
else if (kn == "^D") {
|
||||
else if (kn == "^D") { /* ctrl+d quits */
|
||||
quit = true;
|
||||
}
|
||||
else if (kn == "ALT_K") {
|
||||
tp.SetVolume(tp.Volume() + 0.05); /* 5% */
|
||||
}
|
||||
else if (kn == "ALT_J") {
|
||||
tp.SetVolume(tp.Volume() - 0.05);
|
||||
}
|
||||
else if (ch >= KEY_F(0) && ch <= KEY_F(12)) {
|
||||
else if (!globalHotkeys.Handle(ch)) {
|
||||
if (ch >= KEY_F(0) && ch <= KEY_F(12)) {
|
||||
if (ch == KEY_F(1)) {
|
||||
changeLayout(state, &mainLayout);
|
||||
}
|
||||
@ -215,6 +213,7 @@ int main(int argc, char* argv[])
|
||||
state.keyHandler->KeyPress(ch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Window::WriteToScreen();
|
||||
WindowMessageQueue::Instance().Dispatch();
|
||||
|
@ -1,10 +1,17 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
#include <cursespp/Colors.h>
|
||||
#include <cursespp/Screen.h>
|
||||
|
||||
#include <core/library/LocalLibraryConstants.h>
|
||||
|
||||
#include "LibraryLayout.h"
|
||||
|
||||
using namespace musik::core::library::constants;
|
||||
|
||||
#define CATEGORY_WIDTH 25
|
||||
#define TRANSPORT_HEIGHT 3
|
||||
#define DEFAULT_CATEGORY Track::ALBUM_ID
|
||||
|
||||
LibraryLayout::LibraryLayout(Transport& transport, LibraryPtr library)
|
||||
: LayoutBase()
|
||||
@ -21,9 +28,9 @@ void LibraryLayout::Layout() {
|
||||
this->SetSize(Screen::GetWidth(), Screen::GetHeight());
|
||||
this->SetPosition(0, 0);
|
||||
|
||||
this->albumList->SetPosition(0, 0);
|
||||
this->albumList->SetSize(CATEGORY_WIDTH, this->GetHeight() - TRANSPORT_HEIGHT);
|
||||
this->albumList->SetFocusOrder(0);
|
||||
this->categoryList->SetPosition(0, 0);
|
||||
this->categoryList->SetSize(CATEGORY_WIDTH, this->GetHeight() - TRANSPORT_HEIGHT);
|
||||
this->categoryList->SetFocusOrder(0);
|
||||
|
||||
this->trackList->SetPosition(CATEGORY_WIDTH, 0);
|
||||
this->trackList->SetSize(this->GetWidth() - CATEGORY_WIDTH, this->GetHeight() - TRANSPORT_HEIGHT);
|
||||
@ -35,15 +42,15 @@ void LibraryLayout::Layout() {
|
||||
}
|
||||
|
||||
void LibraryLayout::InitializeWindows() {
|
||||
this->albumList.reset(new CategoryListView(this->library));
|
||||
this->categoryList.reset(new CategoryListView(this->library, DEFAULT_CATEGORY));
|
||||
this->trackList.reset(new TrackListView(this->transport, this->library));
|
||||
this->transportView.reset(new TransportWindow(this->library, this->transport));
|
||||
|
||||
this->AddWindow(this->albumList);
|
||||
this->AddWindow(this->categoryList);
|
||||
this->AddWindow(this->trackList);
|
||||
this->AddWindow(this->transportView);
|
||||
|
||||
this->albumList->SelectionChanged.connect(
|
||||
this->categoryList->SelectionChanged.connect(
|
||||
this, &LibraryLayout::OnCategoryViewSelectionChanged);
|
||||
|
||||
this->Layout();
|
||||
@ -51,15 +58,15 @@ void LibraryLayout::InitializeWindows() {
|
||||
|
||||
void LibraryLayout::Show() {
|
||||
LayoutBase::Show();
|
||||
this->albumList->Requery();
|
||||
this->categoryList->Requery();
|
||||
}
|
||||
|
||||
void LibraryLayout::OnCategoryViewSelectionChanged(
|
||||
ListWindow *view, size_t newIndex, size_t oldIndex) {
|
||||
if (view == this->albumList.get()) {
|
||||
DBID id = this->albumList->GetSelectedId();
|
||||
if (view == this->categoryList.get()) {
|
||||
DBID id = this->categoryList->GetSelectedId();
|
||||
if (id != -1) {
|
||||
this->trackList->Requery("album_id", id);
|
||||
this->trackList->Requery(this->categoryList->GetFieldName(), id);
|
||||
}
|
||||
}
|
||||
}
|
@ -30,8 +30,7 @@ class LibraryLayout : public LayoutBase, public sigslot::has_slots<> {
|
||||
|
||||
Transport& transport;
|
||||
LibraryPtr library;
|
||||
std::shared_ptr<CategoryListView> albumList;
|
||||
std::shared_ptr<CategoryListView> categoryList;
|
||||
std::shared_ptr<TrackListView> trackList;
|
||||
std::shared_ptr<TransportWindow> transportView;
|
||||
|
||||
};
|
@ -3,15 +3,62 @@
|
||||
#include "stdafx.h"
|
||||
#include "CategoryListViewQuery.h"
|
||||
|
||||
#include <core/library/LocalLibraryConstants.h>
|
||||
#include <core/db/Statement.h>
|
||||
|
||||
#include <boost/thread/mutex.hpp>
|
||||
|
||||
#include <map>
|
||||
|
||||
using musik::core::db::Statement;
|
||||
using musik::core::db::Row;
|
||||
using namespace musik::core::library::constants;
|
||||
|
||||
#define reset(x) x.reset(new std::vector<std::shared_ptr<Result>>);
|
||||
|
||||
CategoryListViewQuery::CategoryListViewQuery() {
|
||||
static const std::string ALBUM_QUERY =
|
||||
"SELECT DISTINCT albums.id, albums.name "
|
||||
"FROM albums, tracks "
|
||||
"WHERE albums.id = tracks.album_id "
|
||||
"ORDER BY albums.sort_order;";
|
||||
|
||||
static const std::string ARTIST_QUERY =
|
||||
"SELECT DISTINCT artists.id, artists.name "
|
||||
"FROM artists, tracks "
|
||||
"WHERE artists.id = tracks.visual_artist_id "
|
||||
"ORDER BY artists.sort_order;";
|
||||
|
||||
static const std::string GENRE_QUERY =
|
||||
"SELECT DISTINCT genres.id, genres.name "
|
||||
"FROM genres, tracks "
|
||||
"WHERE genres.id = tracks.visual_genre_id "
|
||||
"ORDER BY genres.sort_order;";
|
||||
|
||||
static boost::mutex QUERY_MAP_MUTEX;
|
||||
static std::map<std::string, std::string> FIELD_TO_QUERY_MAP;
|
||||
|
||||
static void initFieldToQueryMap() {
|
||||
FIELD_TO_QUERY_MAP.emplace(Track::ALBUM_ID, ALBUM_QUERY);
|
||||
FIELD_TO_QUERY_MAP.emplace(Track::ARTIST_ID, ARTIST_QUERY);
|
||||
FIELD_TO_QUERY_MAP.emplace(Track::GENRE_ID, GENRE_QUERY);
|
||||
}
|
||||
|
||||
CategoryListViewQuery::CategoryListViewQuery(const std::string& trackField) {
|
||||
this->trackField = trackField;
|
||||
|
||||
reset(result);
|
||||
|
||||
{
|
||||
boost::mutex::scoped_lock lock(QUERY_MAP_MUTEX);
|
||||
|
||||
if (!FIELD_TO_QUERY_MAP.size()) {
|
||||
initFieldToQueryMap();
|
||||
}
|
||||
}
|
||||
|
||||
if (FIELD_TO_QUERY_MAP.find(trackField) == FIELD_TO_QUERY_MAP.end()) {
|
||||
throw "invalid field for CategoryListView specified";
|
||||
}
|
||||
}
|
||||
|
||||
CategoryListViewQuery::~CategoryListViewQuery() {
|
||||
@ -25,12 +72,7 @@ CategoryListViewQuery::ResultList CategoryListViewQuery::GetResult() {
|
||||
bool CategoryListViewQuery::OnRun(Connection& db) {
|
||||
reset(result);
|
||||
|
||||
std::string query =
|
||||
"SELECT DISTINCT albums.id, albums.name "
|
||||
"FROM albums, tracks "
|
||||
"WHERE albums.id = tracks.album_id "
|
||||
"ORDER BY albums.sort_order;";
|
||||
|
||||
std::string query = FIELD_TO_QUERY_MAP[this->trackField];
|
||||
Statement stmt(query.c_str(), db);
|
||||
|
||||
while (stmt.Step() == Row) {
|
||||
|
@ -17,17 +17,16 @@ class CategoryListViewQuery : public QueryBase {
|
||||
typedef std::shared_ptr<std::vector<
|
||||
std::shared_ptr<Result>>> ResultList;
|
||||
|
||||
CategoryListViewQuery();
|
||||
~CategoryListViewQuery();
|
||||
CategoryListViewQuery(const std::string& trackField);
|
||||
virtual ~CategoryListViewQuery();
|
||||
|
||||
std::string Name() {
|
||||
return "CategoryListViewQuery";
|
||||
}
|
||||
std::string Name() { return "CategoryListViewQuery"; }
|
||||
|
||||
virtual ResultList GetResult();
|
||||
|
||||
protected:
|
||||
virtual bool OnRun(Connection &db);
|
||||
|
||||
std::string trackField;
|
||||
ResultList result;
|
||||
};
|
@ -13,7 +13,7 @@ using musik::core::LibraryPtr;
|
||||
class SingleTrackQuery : public QueryBase {
|
||||
public:
|
||||
SingleTrackQuery(const std::string& path);
|
||||
~SingleTrackQuery();
|
||||
virtual ~SingleTrackQuery();
|
||||
|
||||
virtual std::string Name() { return "SingleTrackQuery"; }
|
||||
virtual TrackPtr GetResult();
|
||||
|
@ -36,7 +36,8 @@ bool TrackListViewQuery::OnRun(Connection& db) {
|
||||
std::string query = boost::str(boost::format(
|
||||
"SELECT DISTINCT t.track, t.bpm, t.duration, t.filesize, t.year, t.title, t.filename, t.thumbnail_id, al.name AS album, gn.name AS genre, ar.name AS artist, t.filetime " \
|
||||
"FROM tracks t, paths p, albums al, artists ar, genres gn " \
|
||||
"WHERE t.%s=? AND t.album_id=al.id AND t.visual_genre_id=gn.id AND t.visual_artist_id=ar.id") % this->column);
|
||||
"WHERE t.%s=? AND t.album_id=al.id AND t.visual_genre_id=gn.id AND t.visual_artist_id=ar.id "
|
||||
"ORDER BY album, track, artist") % this->column);
|
||||
|
||||
Statement trackQuery(query.c_str(), db);
|
||||
|
||||
@ -53,8 +54,8 @@ bool TrackListViewQuery::OnRun(Connection& db) {
|
||||
track->SetValue(Track::FILENAME, trackQuery.ColumnText(6));
|
||||
track->SetValue(Track::THUMBNAIL_ID, trackQuery.ColumnText(7));
|
||||
track->SetValue(Track::ALBUM_ID, trackQuery.ColumnText(8));
|
||||
track->SetValue(Track::DISPLAY_GENRE_ID, trackQuery.ColumnText(9));
|
||||
track->SetValue(Track::DISPLAY_ARTIST_ID, trackQuery.ColumnText(10));
|
||||
track->SetValue(Track::GENRE_ID, trackQuery.ColumnText(9));
|
||||
track->SetValue(Track::ARTIST_ID, trackQuery.ColumnText(10));
|
||||
track->SetValue(Track::FILETIME, trackQuery.ColumnText(11));
|
||||
|
||||
result->push_back(track);
|
||||
|
@ -15,10 +15,9 @@ class TrackListViewQuery : public QueryBase {
|
||||
typedef std::shared_ptr<std::vector<TrackPtr>> Result;
|
||||
|
||||
TrackListViewQuery(LibraryPtr library, const std::string& column, DBID id);
|
||||
~TrackListViewQuery();
|
||||
std::string Name() {
|
||||
return "TrackListViewQuery";
|
||||
}
|
||||
virtual ~TrackListViewQuery();
|
||||
|
||||
std::string Name() { return "TrackListViewQuery"; }
|
||||
|
||||
virtual Result GetResult();
|
||||
|
||||
|
35
src/musikbox/app/util/GlobalHotkeys.cpp
Executable file
35
src/musikbox/app/util/GlobalHotkeys.cpp
Executable file
@ -0,0 +1,35 @@
|
||||
#include "stdafx.h"
|
||||
#include "GlobalHotkeys.h"
|
||||
|
||||
GlobalHotkeys::GlobalHotkeys(Transport& transport)
|
||||
: transport(transport) {
|
||||
|
||||
}
|
||||
|
||||
GlobalHotkeys::~GlobalHotkeys() {
|
||||
|
||||
}
|
||||
|
||||
bool GlobalHotkeys::Handle(int64 ch) {
|
||||
std::string kn = keyname((int) ch);
|
||||
|
||||
if (kn == "ALT_K") {
|
||||
int state = this->transport.GetPlaybackState();
|
||||
if (state == Transport::StatePaused) {
|
||||
this->transport.Resume();
|
||||
}
|
||||
else if (state == Transport::StatePlaying) {
|
||||
this->transport.Pause();
|
||||
}
|
||||
}
|
||||
if (kn == "ALT_L") {
|
||||
this->transport.SetVolume(this->transport.Volume() + 0.05); /* 5% */
|
||||
return true;
|
||||
}
|
||||
else if (kn == "ALT_J") {
|
||||
this->transport.SetVolume(this->transport.Volume() - 0.05);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
18
src/musikbox/app/util/GlobalHotkeys.h
Executable file
18
src/musikbox/app/util/GlobalHotkeys.h
Executable file
@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
#include <core/playback/Transport.h>
|
||||
|
||||
using musik::core::audio::Transport;
|
||||
|
||||
class GlobalHotkeys {
|
||||
public:
|
||||
GlobalHotkeys(Transport& transport);
|
||||
~GlobalHotkeys(); /* non-virtual; do not use as a base class */
|
||||
|
||||
bool Handle(int64 ch);
|
||||
|
||||
private:
|
||||
Transport& transport;
|
||||
};
|
@ -7,20 +7,24 @@
|
||||
#include <cursespp/MultiLineEntry.h>
|
||||
#include <cursespp/IWindowMessage.h>
|
||||
|
||||
#include <core/library/LocalLibraryConstants.h>
|
||||
|
||||
#include <app/query/CategoryListViewQuery.h>
|
||||
|
||||
#include "CategoryListView.h"
|
||||
|
||||
using musik::core::LibraryPtr;
|
||||
using musik::core::IQuery;
|
||||
using namespace musik::core::library::constants;
|
||||
|
||||
#define WINDOW_MESSAGE_QUERY_COMPLETED 1002
|
||||
|
||||
CategoryListView::CategoryListView(LibraryPtr library, IWindow *parent)
|
||||
: ListWindow(parent) {
|
||||
CategoryListView::CategoryListView(LibraryPtr library, const std::string& fieldName)
|
||||
: ListWindow(NULL) {
|
||||
this->SetContentColor(BOX_COLOR_WHITE_ON_BLACK);
|
||||
this->library = library;
|
||||
this->library->QueryCompleted.connect(this, &CategoryListView::OnQueryCompleted);
|
||||
this->fieldName = fieldName;
|
||||
this->adapter = new Adapter(*this);
|
||||
}
|
||||
|
||||
@ -28,8 +32,25 @@ CategoryListView::~CategoryListView() {
|
||||
delete adapter;
|
||||
}
|
||||
|
||||
void CategoryListView::KeyPress(int64 ch) {
|
||||
std::string kn = keyname((int) ch);
|
||||
|
||||
if (kn == "ALT_1") {
|
||||
this->SetFieldName(Track::ARTIST_ID);
|
||||
}
|
||||
else if (kn == "ALT_2") {
|
||||
this->SetFieldName(Track::ALBUM_ID);
|
||||
}
|
||||
else if (kn == "ALT_3") {
|
||||
this->SetFieldName(Track::GENRE_ID);
|
||||
}
|
||||
else {
|
||||
ListWindow::KeyPress(ch);
|
||||
}
|
||||
}
|
||||
|
||||
void CategoryListView::Requery() {
|
||||
this->activeQuery.reset(new CategoryListViewQuery());
|
||||
this->activeQuery.reset(new CategoryListViewQuery(this->fieldName));
|
||||
this->library->Enqueue(activeQuery);
|
||||
}
|
||||
|
||||
@ -41,6 +62,17 @@ DBID CategoryListView::GetSelectedId() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::string CategoryListView::GetFieldName() {
|
||||
return this->fieldName;
|
||||
}
|
||||
|
||||
void CategoryListView::SetFieldName(const std::string& fieldName) {
|
||||
if (this->fieldName != fieldName) {
|
||||
this->fieldName = fieldName;
|
||||
this->Requery();
|
||||
}
|
||||
}
|
||||
|
||||
void CategoryListView::OnQueryCompleted(QueryPtr query) {
|
||||
if (query == this->activeQuery) {
|
||||
Post(WINDOW_MESSAGE_QUERY_COMPLETED);
|
||||
|
@ -15,12 +15,17 @@ using musik::core::LibraryPtr;
|
||||
|
||||
class CategoryListView : public ListWindow, public sigslot::has_slots<> {
|
||||
public:
|
||||
CategoryListView(LibraryPtr library, IWindow *parent = NULL);
|
||||
CategoryListView(LibraryPtr library, const std::string& fieldName);
|
||||
virtual ~CategoryListView();
|
||||
|
||||
void Requery();
|
||||
|
||||
virtual void ProcessMessage(IWindowMessage &message);
|
||||
virtual void KeyPress(int64 ch);
|
||||
|
||||
DBID GetSelectedId();
|
||||
std::string GetFieldName();
|
||||
void SetFieldName(const std::string& fieldName);
|
||||
|
||||
protected:
|
||||
virtual IScrollAdapter& GetScrollAdapter();
|
||||
@ -42,6 +47,7 @@ class CategoryListView : public ListWindow, public sigslot::has_slots<> {
|
||||
LibraryPtr library;
|
||||
Adapter *adapter;
|
||||
|
||||
std::string fieldName;
|
||||
std::shared_ptr<CategoryListViewQuery> activeQuery;
|
||||
CategoryListViewQuery::ResultList metadata;
|
||||
};
|
@ -82,16 +82,32 @@ size_t TrackListView::Adapter::GetEntryCount() {
|
||||
return parent.metadata ? parent.metadata->size() : 0;
|
||||
}
|
||||
|
||||
static inline void trunc(std::string& s, int max) {
|
||||
if (s.size() > max) {
|
||||
s = s.substr(0, max);
|
||||
}
|
||||
}
|
||||
|
||||
#define MAX_ARTIST 12
|
||||
#define MAX_ALBUM 12
|
||||
|
||||
IScrollAdapter::EntryPtr TrackListView::Adapter::GetEntry(size_t index) {
|
||||
int64 attrs = (index == parent.GetSelectedIndex()) ? COLOR_PAIR(BOX_COLOR_BLACK_ON_GREEN) : -1;
|
||||
|
||||
TrackPtr track = parent.metadata->at(index);
|
||||
std::string trackNum = track->GetValue("track");
|
||||
std::string title = track->GetValue("title");
|
||||
std::string trackNum = track->GetValue(Track::TRACK_NUM);
|
||||
std::string artist = track->GetValue(Track::ARTIST_ID);
|
||||
std::string album = track->GetValue(Track::ALBUM_ID);
|
||||
std::string title = track->GetValue(Track::TITLE);
|
||||
|
||||
trunc(artist, MAX_ARTIST);
|
||||
trunc(album, MAX_ALBUM);
|
||||
|
||||
std::string text = boost::str(
|
||||
boost::format("%s %s")
|
||||
boost::format("%s %s %s %s")
|
||||
% boost::io::group(std::setw(3), std::setfill(' '), trackNum)
|
||||
% boost::io::group(std::setw(MAX_ARTIST), std::setiosflags(std::ios::left), std::setfill(' '), artist)
|
||||
% boost::io::group(std::setw(MAX_ALBUM), std::setiosflags(std::ios::left), std::setfill(' '), album)
|
||||
% title);
|
||||
|
||||
IScrollAdapter::EntryPtr entry(new SingleLineEntry(text));
|
||||
|
@ -117,8 +117,7 @@ inline static void breakIntoSubLines(
|
||||
size_t wordLength = u8len(word);
|
||||
size_t extra = (i != 0);
|
||||
|
||||
/* we have enough space for this new word. accumulate it. the
|
||||
+1 here is to take the space into account */
|
||||
/* we have enough space for this new word. accumulate it. */
|
||||
|
||||
if (accumLength + extra + wordLength < width) {
|
||||
if (extra) {
|
||||
|
@ -213,9 +213,8 @@ void Window::Show() {
|
||||
}
|
||||
}
|
||||
|
||||
this->Repaint();
|
||||
|
||||
this->isVisible = true;
|
||||
this->Repaint();
|
||||
}
|
||||
|
||||
void Window::Hide() {
|
||||
|
@ -120,6 +120,7 @@
|
||||
<ClCompile Include="app\query\CategoryListViewQuery.cpp" />
|
||||
<ClCompile Include="app\query\SingleTrackQuery.cpp" />
|
||||
<ClCompile Include="app\query\TrackListViewQuery.cpp" />
|
||||
<ClCompile Include="app\util\GlobalHotkeys.cpp" />
|
||||
<ClCompile Include="app\util\SystemInfo.cpp" />
|
||||
<ClCompile Include="app\window\CategoryListView.cpp" />
|
||||
<ClCompile Include="app\window\CommandWindow.cpp" />
|
||||
@ -152,6 +153,7 @@
|
||||
<ClInclude Include="app\query\CategoryListViewQuery.h" />
|
||||
<ClInclude Include="app\query\SingleTrackQuery.h" />
|
||||
<ClInclude Include="app\query\TrackListViewQuery.h" />
|
||||
<ClInclude Include="app\util\GlobalHotkeys.h" />
|
||||
<ClInclude Include="app\util\SystemInfo.h" />
|
||||
<ClInclude Include="app\window\CategoryListView.h" />
|
||||
<ClInclude Include="app\window\CommandWindow.h" />
|
||||
|
@ -78,6 +78,9 @@
|
||||
<ClCompile Include="app\query\SingleTrackQuery.cpp">
|
||||
<Filter>app\query</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="app\util\GlobalHotkeys.cpp">
|
||||
<Filter>app\util</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="stdafx.h" />
|
||||
@ -186,6 +189,9 @@
|
||||
<ClInclude Include="app\query\SingleTrackQuery.h">
|
||||
<Filter>app\query</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="app\util\GlobalHotkeys.h">
|
||||
<Filter>app\util</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Filter Include="cursespp">
|
||||
|
Loading…
Reference in New Issue
Block a user