- Consolidated LibraryBase, LocalLibrary. introduced ILibrary interface

- Added IIndexer interface for ILibrary
- Fixed and simplified LocalLibrary and Indexer startup behavior
- Added IQuery interface, fixed QueryBase
- Fixed LocalLibrary.enqueue() to work properly with new IQuery
  interface
- Introduced ILayout interface and MainLayout implementation
This commit is contained in:
casey 2016-05-10 20:47:30 -07:00
parent adb0ed14b1
commit c4111850fe
39 changed files with 869 additions and 1026 deletions

View File

@ -88,9 +88,8 @@
<ClCompile Include="io\DataStreamFactory.cpp" />
<ClCompile Include="io\LocalFileStream.cpp" />
<ClCompile Include="library\Indexer.cpp" />
<ClCompile Include="library\LibraryBase.cpp" />
<ClCompile Include="library\LibraryFactory.cpp" />
<ClCompile Include="library\LocalLibrary.cpp" />
<ClCompile Include="library\LibraryFactory.cpp" />
<ClCompile Include="library\metadata\MetadataValue.cpp" />
<ClCompile Include="Library\query\QueryBase.cpp" />
<ClCompile Include="library\track\InMemoryTrack.cpp" />
@ -121,10 +120,12 @@
<ClInclude Include="debug.h" />
<ClInclude Include="io\DataStreamFactory.h" />
<ClInclude Include="io\LocalFileStream.h" />
<ClInclude Include="library\IIndexer.h" />
<ClInclude Include="library\ILibrary.h" />
<ClInclude Include="library\Indexer.h" />
<ClInclude Include="library\LibraryBase.h" />
<ClInclude Include="library\LibraryFactory.h" />
<ClInclude Include="library\IQuery.h" />
<ClInclude Include="library\LocalLibrary.h" />
<ClInclude Include="library\LibraryFactory.h" />
<ClInclude Include="library\metadata\MetadataValue.h" />
<ClInclude Include="Library\query\QueryBase.h" />
<ClInclude Include="library\track\InMemoryTrack.h" />

View File

@ -88,12 +88,6 @@
<ClCompile Include="library\Indexer.cpp">
<Filter>src\library</Filter>
</ClCompile>
<ClCompile Include="library\LibraryBase.cpp">
<Filter>src\library</Filter>
</ClCompile>
<ClCompile Include="library\LocalLibrary.cpp">
<Filter>src\library</Filter>
</ClCompile>
<ClCompile Include="library\track\IndexerTrack.cpp">
<Filter>src\library\track</Filter>
</ClCompile>
@ -127,6 +121,9 @@
<ClCompile Include="library\track\InMemoryTrack.cpp">
<Filter>src\library\track</Filter>
</ClCompile>
<ClCompile Include="library\LocalLibrary.cpp">
<Filter>src\library</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.hpp">
@ -207,12 +204,6 @@
<ClInclude Include="library\Indexer.h">
<Filter>src\library</Filter>
</ClInclude>
<ClInclude Include="library\LibraryBase.h">
<Filter>src\library</Filter>
</ClInclude>
<ClInclude Include="library\LocalLibrary.h">
<Filter>src\library</Filter>
</ClInclude>
<ClInclude Include="library\track\IndexerTrack.h">
<Filter>src\library\track</Filter>
</ClInclude>
@ -261,5 +252,17 @@
<ClInclude Include="library\track\InMemoryTrack.h">
<Filter>src\library\track</Filter>
</ClInclude>
<ClInclude Include="library\LocalLibrary.h">
<Filter>src\library</Filter>
</ClInclude>
<ClInclude Include="library\ILibrary.h">
<Filter>src\library</Filter>
</ClInclude>
<ClInclude Include="library\IIndexer.h">
<Filter>src\library</Filter>
</ClInclude>
<ClInclude Include="library\IQuery.h">
<Filter>src\library</Filter>
</ClInclude>
</ItemGroup>
</Project>

20
src/core/library/IIndexer.h Executable file
View File

@ -0,0 +1,20 @@
#pragma once
#include <string>
#include <vector>
#include <sigslot/sigslot.h>
namespace musik { namespace core {
class IIndexer {
public:
sigslot::signal0<> SynchronizeStart;
sigslot::signal0<> SynchronizeEnd;
sigslot::signal0<> PathsUpdated;
sigslot::signal0<> TrackRefreshed;
virtual void AddPath(const std::string& path) = 0;
virtual void RemovePath(const std::string& path) = 0;
virtual void GetPaths(std::vector<std::string>& paths) = 0;
virtual void Synchronize(bool restart = false) = 0;
};
} }

25
src/core/library/ILibrary.h Executable file
View File

@ -0,0 +1,25 @@
#pragma once
#include <string>
#include <vector>
#include <sigslot/sigslot.h>
#include <core/library/IIndexer.h>
#include <core/Library/IQuery.h>
namespace musik { namespace core {
class ILibrary;
typedef boost::shared_ptr<ILibrary> LibraryPtr;
class ILibrary {
public:
sigslot::signal1<QueryPtr> QueryCompleted;
virtual int Enqueue(QueryPtr query, unsigned int options = 0) = 0;
virtual IIndexer *Indexer() = 0;
virtual int Id() = 0;
virtual const std::string& Name() = 0;
};
} }

33
src/core/library/IQuery.h Executable file
View File

@ -0,0 +1,33 @@
#pragma once
#include <string>
#include <vector>
#include <sigslot/sigslot.h>
#include <boost/shared_ptr.hpp>
#include <core/library/IIndexer.h>
#include <core/db/Connection.h>
namespace musik { namespace core {
class IQuery;
typedef boost::shared_ptr<IQuery> QueryPtr;
class IQuery {
public:
sigslot::signal2<int, QueryPtr> QueryCompleted;
typedef enum {
Idle = 1,
Running = 2,
Failed = 3,
Finished = 4,
} Status;
virtual bool Run(db::Connection &db) = 0;
virtual int GetStatus() = 0;
virtual int GetId() = 0;
virtual int GetOptions() = 0;
};
} }

View File

@ -71,12 +71,15 @@ static std::string normalizePath(const std::string& path) {
return boost::filesystem::path(path).make_preferred().string();
}
Indexer::Indexer()
Indexer::Indexer(const std::string& libraryPath, const std::string& dbFilename)
: thread(NULL)
, status(0)
, restart(false)
, filesIndexed(0)
, filesSaved(0) {
this->dbFilename = dbFilename;
this->libraryPath = libraryPath;
this->thread = new boost::thread(boost::bind(&Indexer::ThreadLoop, this));
}
//////////////////////////////////////////
@ -101,14 +104,10 @@ Indexer::~Indexer(){
///\param bNewRestart
///Should if be restarted or not
//////////////////////////////////////////
void Indexer::RestartSync(bool restart){
void Indexer::Synchronize(bool restart) {
boost::mutex::scoped_lock lock(this->exitMutex);
this->restart = restart;
if (this->restart) {
this->Notify(); /* rename Notify()? */
}
this->Notify();
}
//////////////////////////////////////////
@ -117,7 +116,6 @@ void Indexer::RestartSync(bool restart){
//////////////////////////////////////////
bool Indexer::Restarted() {
boost::mutex::scoped_lock lock(this->exitMutex);
return this->restart;
}
@ -141,7 +139,7 @@ void Indexer::AddPath(const std::string& path) {
this->addRemoveQueue.push_back(context);
}
this->RestartSync();
this->Synchronize();
}
//////////////////////////////////////////
@ -161,14 +159,14 @@ void Indexer::RemovePath(const std::string& path) {
this->addRemoveQueue.push_back(context);
}
this->RestartSync();
this->Synchronize();
}
//////////////////////////////////////////
///\brief
///Main method for doing the synchronization.
//////////////////////////////////////////
void Indexer::Synchronize() {
void Indexer::SynchronizeInternal() {
/* load all of the metadata (tag) reader plugins */
typedef metadata::IMetadataReader PluginType;
typedef PluginFactory::DestroyDeleter<PluginType> Deleter;
@ -395,6 +393,12 @@ void Indexer::SyncDirectory(
///Main loop the thread is running in.
//////////////////////////////////////////
void Indexer::ThreadLoop() {
boost::filesystem::path thumbPath(this->libraryPath + "thumbs/");
if (!boost::filesystem::exists(thumbPath)) {
boost::filesystem::create_directories(thumbPath);
}
bool firstTime = true; /* through the loop */
while (!this->Exited()) {
@ -403,10 +407,9 @@ void Indexer::ThreadLoop() {
if(!firstTime || (firstTime && prefs.GetBool("SyncOnStartup", true))) { /* first time through the loop skips this */
this->SynchronizeStart();
this->dbConnection.Open(this->database.c_str(), 0); /* ensure the db is open*/
this->dbConnection.Open(this->dbFilename.c_str(), 0); /* ensure the db is open*/
this->RestartSync(false);
this->Synchronize();
this->SynchronizeInternal();
this->RunAnalyzers();
{
@ -421,12 +424,12 @@ void Indexer::ThreadLoop() {
firstTime = false;
int weightTime = prefs.GetInt("SyncTimeout", 3600); /* sleep before we try again... */
int waitTime = prefs.GetInt("SyncTimeout", 3600); /* sleep before we try again... */
if (weightTime) {
if (waitTime) {
boost::xtime waitTimeout;
boost::xtime_get(&waitTimeout, boost::TIME_UTC_);
waitTimeout.sec += weightTime;
waitTimeout.sec += waitTime;
if (!this->Restarted()) {
this->NotificationTimedWait(waitTimeout);
@ -440,35 +443,6 @@ void Indexer::ThreadLoop() {
}
}
//////////////////////////////////////////
///\brief
///Start up the Indexer.
///
///\returns
///true if successfully started.
///
///This method will start the Indexer thread that will
///start up ThreadLoop and then return.
///
///\see
///<ThreadLoop>
//////////////////////////////////////////
bool Indexer::Startup(std::string setLibraryPath) {
this->libraryPath = setLibraryPath;
/* ensure image artwork directory exists */
boost::filesystem::path thumbPath(this->libraryPath + "thumbs/");
if (!boost::filesystem::exists(thumbPath)) {
boost::filesystem::create_directories(thumbPath);
}
this->thread = new boost::thread(boost::bind(&Indexer::ThreadLoop, this));
return true;
}
//////////////////////////////////////////
///\brief
///Part of the synchronizer to delete removed files.
@ -648,7 +622,7 @@ void Indexer::SyncCleanup() {
//////////////////////////////////////////
void Indexer::GetPaths(std::vector<std::string>& paths) {
db::Connection connection;
connection.Open(this->database.c_str());
connection.Open(this->dbFilename.c_str());
db::Statement stmt("SELECT path FROM paths ORDER BY id", connection);
while (stmt.Step() == db::Row) {
@ -736,7 +710,7 @@ void Indexer::SyncOptimize() {
++count;
if (count % 1000 == 0) {
transaction.CommitAndRestart(); /* i guess we only commit the transaction every 1000 files?*/
transaction.CommitAndRestart();
}
}
}
@ -776,7 +750,6 @@ void Indexer::ProcessAddRemoveQueue() {
this->PathsUpdated();
}
void Indexer::RunAnalyzers() {
typedef audio::IAnalyzer PluginType;
typedef PluginFactory::DestroyDeleter<PluginType> Deleter;
@ -846,9 +819,7 @@ void Indexer::RunAnalyzers() {
++plugin;
}
else {
/* TODO: dangerous? should we be removing elements from
this vector as we're iterating over it? */
plugin = runningAnalyzers.erase(plugin);
plugin = runningAnalyzers.erase(plugin);
}
}
}
@ -880,6 +851,5 @@ void Indexer::RunAnalyzers() {
getNextTrack.BindInt(0, trackId);
}
}

View File

@ -39,6 +39,7 @@
#include <core/support/ThreadHelper.h>
#include <core/db/Connection.h>
#include <core/sdk/IMetadataReader.h>
#include <core/library/IIndexer.h>
#include <sigslot/sigslot.h>
@ -57,33 +58,43 @@ namespace musik { namespace core {
///but can also be used as a standalone class for indexing files.
///All you need to do is create a Indexer object and call Startup()
//////////////////////////////////////////
class Indexer : public ThreadHelper, private boost::noncopyable {
class Indexer : public IIndexer, public ThreadHelper, private boost::noncopyable {
public:
Indexer();
Indexer(
const std::string& libraryPath,
const std::string& dbFilename);
~Indexer();
void AddPath(const std::string& paths);
void RemovePath(const std::string& paths);
void GetPaths(std::vector<std::string>& paths);
bool Startup(std::string setLibraryPath);
void ThreadLoop();
void RestartSync(bool bNewRestart=true);
bool Restarted();
std::string database;
sigslot::signal0<> SynchronizeStart;
sigslot::signal0<> SynchronizeEnd;
sigslot::signal0<> PathsUpdated;
sigslot::signal0<> TrackRefreshed;
virtual void AddPath(const std::string& paths);
virtual void RemovePath(const std::string& paths);
virtual void GetPaths(std::vector<std::string>& paths);
virtual void Synchronize(bool restart = false);
private:
void ThreadLoop();
bool Restarted();
void SyncDelete(const std::vector<DBID>& paths);
void SyncCleanup();
void ProcessAddRemoveQueue();
void SyncOptimize();
void RunAnalyzers();
void SynchronizeInternal();
void SyncDirectory(
const std::string& syncRoot,
const std::string& currentPath,
DBID parentDirId,
DBID pathId);
db::Connection dbConnection;
std::string libraryPath;
std::string dbFilename;
int status;
bool restart;
@ -93,19 +104,6 @@ namespace musik { namespace core {
int filesIndexed;
int filesSaved;
void Synchronize();
void SyncDirectory(
const std::string& syncRoot,
const std::string& currentPath,
DBID parentDirId,
DBID pathId);
void SyncDelete(const std::vector<DBID>& paths);
void SyncCleanup();
void ProcessAddRemoveQueue();
void SyncOptimize();
void RunAnalyzers();
class AddRemoveContext {
public:

View File

@ -1,410 +0,0 @@
//////////////////////////////////////////////////////////////////////////////
//
// License Agreement:
//
// The following are Copyright © 2008, Daniel Önnerby
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// * Neither the name of the author nor the names of other contributors may
// be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
//////////////////////////////////////////////////////////////////////////////
#include "pch.hpp"
#include <core/library/LibraryBase.h>
#include <core/config.h>
#include <core/library/query/QueryBase.h>
#include <core/support/Common.h>
using namespace musik::core;
using namespace musik::core::library;
//////////////////////////////////////////
///\brief
///Constructor
//////////////////////////////////////////
LibraryBase::LibraryBase(std::string name,int id)
: name(name)
, id(id)
, exit(false)
{
this->identifier = boost::lexical_cast<std::string>(id);
}
//////////////////////////////////////////
///\brief
///Destructor
///
///The destructor will exit all threads created by the library
//////////////////////////////////////////
LibraryBase::~LibraryBase(){
this->Exit();
this->threads.join_all();
}
//////////////////////////////////////////
///\brief
///Get the librarys identifier. The identifier is unique for the library
///
///\returns
///A string with the identifier
//////////////////////////////////////////
const std::string& LibraryBase::Identifier(){
return this->identifier;
}
int LibraryBase::Id(){
return this->id;
}
//////////////////////////////////////////
///\brief
///Name of the library
//////////////////////////////////////////
const std::string& LibraryBase::Name(){
return this->name;
}
//////////////////////////////////////////
///\brief
///Get the directory-location of the library where you may store extra files.
///
///\returns
///String with the path
///
///The library directory is a directory where you may store
///the librarys database and other files like thumbnail cache.
///In a win32 environment this path will be located in the users
///$APPDATA/mC2/"identifier"/
///where the identifier is set in the library itself.
///
///\remarks
///If the directory does not exist, this method will create it.
//////////////////////////////////////////
std::string LibraryBase::GetLibraryDirectory() {
std::string directory(musik::core::GetDataDirectory());
if (!this->identifier.empty()) {
directory.append(this->identifier + "/" );
}
boost::filesystem::path dir(directory);
if(!boost::filesystem::exists(dir)){
boost::filesystem::create_directories(dir);
}
directory = dir.string();
return directory;
}
//////////////////////////////////////////
///\brief
///Get the full path to the database file.
///
///\returns
///String with the path
//////////////////////////////////////////
std::string LibraryBase::GetDatabasePath() {
return this->GetLibraryDirectory() + "musik.db";
}
//////////////////////////////////////////
///\brief
///Add a Query for parsing to the Library
///
///\param queryCopy
///Copy of the query that needs to be parsed. Need to be derivated from the query::QueryBase. Look at musik::core::query::copy
///
///\param options
///A bitfield with options for the query.
///Available options are:
/// - query::Options::AutoCallback : The callbacks in the Query should automaticaly be called.
/// Note that if you pass query::Options::AutoCallback without query::Options::Wait, the callbacks will be called from the Library thread.
/// - query::Options::Wait : Wait for the Query to finish executing and then continue.
/// - query::Options::Prioritize : Will make the library prioritize the Query.
/// - query::Options::CancelQueue : Cancel all other queries that are to be executed by the Library.
/// - query::Options::CancelSimilar : Cancel all similar queries. A similar query is a query that originates from the same query::QueryBase that is passed to the AddQuery.
/// - query::Options::UnCanceable : Under no circumstances is this Query allowed to be canceled.
/// - query::Options::UnCanceable : Under no circumstances is this Query allowed to be canceled.
///
///The query will be copied by the library and executed in the library thread.
///
///\returns
///true if successfully added to the queue
///
///\see
///musik::core::query::QueryBase::copy
//////////////////////////////////////////
bool LibraryBase::AddQuery(const query::QueryBase &query, unsigned int options) {
return false;
}
//////////////////////////////////////////
///\brief
///Call the callbacks (signals) for finished queries.
///
///\returns
///true when there is nothing else to call.
///
///Write detailed description for RunCallbacks here.
///
///\see
///OnQueryQueueEnd
//////////////////////////////////////////
bool LibraryBase::RunCallbacks() {
return false;
}
//////////////////////////////////////////
///\brief
///Has the library exited?
//////////////////////////////////////////
bool LibraryBase::Exited() {
boost::mutex::scoped_lock lock(this->libraryMutex);
return this->exit;
}
//////////////////////////////////////////
///\brief
///Exit the library
///
///Will set the library to Exited and notify all sleeping threads
//////////////////////////////////////////
void LibraryBase::Exit() {
{
boost::mutex::scoped_lock lock(this->libraryMutex);
this->exit = true;
}
this->waitCondition.notify_all();
}
//////////////////////////////////////////
///\brief
///Helper method to determin what metakeys are "static"
//////////////////////////////////////////
bool LibraryBase::IsStaticMetaKey(std::string &metakey){
static std::set<std::string> staticMetaKeys;
if (staticMetaKeys.empty()) {
staticMetaKeys.insert("track");
staticMetaKeys.insert("bpm");
staticMetaKeys.insert("duration");
staticMetaKeys.insert("filesize");
staticMetaKeys.insert("year");
staticMetaKeys.insert("title");
staticMetaKeys.insert("filename");
staticMetaKeys.insert("filetime");
}
return staticMetaKeys.find(metakey) != staticMetaKeys.end();
}
//////////////////////////////////////////
///\brief
///Helper method to determine what metakeys that have a special many to one relation
//////////////////////////////////////////
bool LibraryBase::IsSpecialMTOMetaKey(std::string &metakey){
static std::set<std::string> specialMTOMetaKeys;
if (specialMTOMetaKeys.empty()) {
specialMTOMetaKeys.insert("album");
specialMTOMetaKeys.insert("visual_genre");
specialMTOMetaKeys.insert("visual_artist");
specialMTOMetaKeys.insert("folder");
}
return specialMTOMetaKeys.find(metakey)!=specialMTOMetaKeys.end();
}
//////////////////////////////////////////
///\brief
///Helper method to determine what metakeys that have a special many to meny relation
//////////////////////////////////////////
bool LibraryBase::IsSpecialMTMMetaKey(std::string &metakey) {
static std::set<std::string> specialMTMMetaKeys;
if (specialMTMMetaKeys.empty()) {
specialMTMMetaKeys.insert("artist");
specialMTMMetaKeys.insert("genre");
}
return specialMTMMetaKeys.find(metakey) != specialMTMMetaKeys.end();
}
//////////////////////////////////////////
///\brief
///Get a pointer to the librarys Indexer (NULL if none)
//////////////////////////////////////////
musik::core::Indexer *LibraryBase::Indexer(){
return NULL;
}
//////////////////////////////////////////
///\brief
///Create all tables, indexes, etc in the database.
///
///This will assume that the database has been initialized.
//////////////////////////////////////////
void LibraryBase::CreateDatabase(db::Connection &db){
// Create the tracks-table
db.Execute("CREATE TABLE IF NOT EXISTS tracks ("
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
"track INTEGER DEFAULT 0,"
"bpm REAL DEFAULT 0,"
"duration INTEGER DEFAULT 0,"
"filesize INTEGER DEFAULT 0,"
"year INTEGER DEFAULT 0,"
"visual_genre_id INTEGER DEFAULT 0,"
"visual_artist_id INTEGER DEFAULT 0,"
"album_id INTEGER DEFAULT 0,"
"folder_id INTEGER DEFAULT 0,"
"title TEXT default '',"
"filename TEXT default '',"
"filetime INTEGER DEFAULT 0,"
"thumbnail_id INTEGER DEFAULT 0,"
"sort_order1 INTEGER)");
// Create the genres-table
db.Execute("CREATE TABLE IF NOT EXISTS genres ("
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
"name TEXT default '',"
"aggregated INTEGER DEFAULT 0,"
"sort_order INTEGER DEFAULT 0)");
db.Execute("CREATE TABLE IF NOT EXISTS track_genres ("
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
"track_id INTEGER DEFAULT 0,"
"genre_id INTEGER DEFAULT 0)");
// Create the artists-table
db.Execute("CREATE TABLE IF NOT EXISTS artists ("
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
"name TEXT default '',"
"aggregated INTEGER DEFAULT 0,"
"sort_order INTEGER DEFAULT 0)");
db.Execute("CREATE TABLE IF NOT EXISTS track_artists ("
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
"track_id INTEGER DEFAULT 0,"
"artist_id INTEGER DEFAULT 0)");
// Create the meta-tables
db.Execute("CREATE TABLE IF NOT EXISTS meta_keys ("
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
"name TEXT)");
db.Execute("CREATE TABLE IF NOT EXISTS meta_values ("
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
"meta_key_id INTEGER DEFAULT 0,"
"sort_order INTEGER DEFAULT 0,"
"content TEXT)");
db.Execute("CREATE TABLE IF NOT EXISTS track_meta ("
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
"track_id INTEGER DEFAULT 0,"
"meta_value_id INTEGER DEFAULT 0)");
// Create the albums-table
db.Execute("CREATE TABLE IF NOT EXISTS albums ("
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
"name TEXT default '',"
"thumbnail_id INTEGER default 0,"
"sort_order INTEGER DEFAULT 0)");
// Create the paths-table
db.Execute("CREATE TABLE IF NOT EXISTS paths ("
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
"path TEXT default ''"
")");
// Create the folders-table
db.Execute("CREATE TABLE IF NOT EXISTS folders ("
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
"name TEXT default '',"
"relative_path TEXT default '',"
"parent_id INTEGER DEFAULT 0,"
"path_id INTEGER DEFAULT 0"
")");
// Create the folders-table
db.Execute("CREATE TABLE IF NOT EXISTS thumbnails ("
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
"filename TEXT default '',"
"filesize INTEGER DEFAULT 0,"
"checksum INTEGER DEFAULT 0"
")");
// Create the playlists-table
db.Execute("CREATE TABLE IF NOT EXISTS playlists ("
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
"name TEXT default '',"
"user_id INTEGER default 0"
")");
// Create the playlists-table
db.Execute("CREATE TABLE IF NOT EXISTS playlist_tracks ("
"track_id INTEGER DEFAULT 0,"
"playlist_id INTEGER DEFAULT 0,"
"sort_order INTEGER DEFAULT 0"
")");
// Create the users-table
db.Execute("CREATE TABLE IF NOT EXISTS users ("
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
"name TEXT,"
"login TEXT,"
"password TEXT)");
// INDEXES
db.Execute("CREATE UNIQUE INDEX IF NOT EXISTS users_index ON users (login)");
db.Execute("CREATE UNIQUE INDEX IF NOT EXISTS folders_index ON folders (name,parent_id,path_id)");
db.Execute("CREATE UNIQUE INDEX IF NOT EXISTS paths_index ON paths (path)");
db.Execute("CREATE INDEX IF NOT EXISTS genre_index ON genres (sort_order)");
db.Execute("CREATE INDEX IF NOT EXISTS artist_index ON artists (sort_order)");
db.Execute("CREATE INDEX IF NOT EXISTS album_index ON albums (sort_order)");
db.Execute("CREATE INDEX IF NOT EXISTS track_index1 ON tracks (album_id,sort_order1)");
db.Execute("CREATE INDEX IF NOT EXISTS track_index7 ON tracks (folder_id)");
db.Execute("CREATE INDEX IF NOT EXISTS thumbnail_index ON thumbnails (filesize)");
db.Execute("CREATE INDEX IF NOT EXISTS trackgenre_index1 ON track_genres (track_id,genre_id)");
db.Execute("CREATE INDEX IF NOT EXISTS trackgenre_index2 ON track_genres (genre_id,track_id)");
db.Execute("CREATE INDEX IF NOT EXISTS trackartist_index1 ON track_artists (track_id,artist_id)");
db.Execute("CREATE INDEX IF NOT EXISTS trackartist_index2 ON track_artists (artist_id,track_id)");
db.Execute("CREATE INDEX IF NOT EXISTS trackmeta_index1 ON track_meta (track_id,meta_value_id)");
db.Execute("CREATE INDEX IF NOT EXISTS trackmeta_index2 ON track_meta (meta_value_id,track_id)");
db.Execute("CREATE INDEX IF NOT EXISTS metakey_index1 ON meta_keys (name)");
db.Execute("CREATE INDEX IF NOT EXISTS metavalues_index1 ON meta_values (meta_key_id)");
db.Execute("CREATE INDEX IF NOT EXISTS playlist_index ON playlist_tracks (playlist_id,sort_order)");
db.Analyze();
}

View File

@ -1,124 +0,0 @@
//////////////////////////////////////////////////////////////////////////////
//
// License Agreement:
//
// The following are Copyright © 2008, Daniel Önnerby
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// * Neither the name of the author nor the names of other contributors may
// be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
//////////////////////////////////////////////////////////////////////////////
#pragma once
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
#include <boost/lexical_cast.hpp>
#include <core/config.h>
#include <core/db/Connection.h>
#include <boost/thread/thread.hpp>
#include <boost/thread/condition.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/utility.hpp>
#include <sigslot/sigslot.h>
#include <string>
/* forward decl */
namespace musik { namespace core {
class Indexer;
namespace query {
class QueryBase;
typedef boost::shared_ptr<musik::core::query::QueryBase> Ptr;
}
namespace library {
class LibraryBase;
}
typedef boost::shared_ptr<library::LibraryBase> LibraryPtr;
typedef boost::weak_ptr<library::LibraryBase> LibraryWeakPtr;
} }
namespace musik { namespace core { namespace library {
class LibraryBase : boost::noncopyable {
protected:
LibraryBase(std::string name, int id);
public:
virtual ~LibraryBase();
virtual bool Startup() = 0;
virtual bool AddQuery(const query::QueryBase &query, unsigned int options = 0);
virtual bool RunCallbacks();
std::string GetLibraryDirectory();
std::string GetDatabasePath();
virtual musik::core::Indexer *Indexer();
const std::string& Identifier();
const std::string& Name();
int Id();
bool Exited();
static bool IsStaticMetaKey(std::string &metakey);
static bool IsSpecialMTOMetaKey(std::string &metakey);
static bool IsSpecialMTMMetaKey(std::string &metakey);
static void CreateDatabase(db::Connection &db);
protected:
virtual void Exit();
private:
typedef std::list<query::Ptr> QueryList;
QueryList incomingQueries;
QueryList outgoingQueries;
std::string identifier;
int id;
std::string name;
bool exit;
boost::thread_group threads;
boost::condition waitCondition;
boost::mutex libraryMutex;
};
} } }
namespace musik { namespace core {
typedef boost::shared_ptr<musik::core::library::LibraryBase> LibraryPtr;
} }

View File

@ -42,9 +42,7 @@
using namespace musik::core;
//LibraryFactory LibraryFactory::sInstance;
LibraryFactory& LibraryFactory::Instance(){
LibraryFactory& LibraryFactory::Instance() {
typedef boost::shared_ptr<LibraryFactory> InstanceType;
static InstanceType sInstance(new LibraryFactory());
return *sInstance;
@ -56,36 +54,32 @@ LibraryFactory& LibraryFactory::Instance(){
///Constructor
//////////////////////////////////////////
LibraryFactory::LibraryFactory() {
// Connect to the settings.db
// Connect to the settings.db
std::string dataDir = GetDataDirectory();
std::string dbFile = GetDataDirectory() + "settings.db";
musik::core::db::Connection db;
musik::core::db::Connection db;
db.Open(dbFile.c_str(), 0, 128);
Preferences::CreateDB(db);
// Get the libraries
db::Statement stmtGetLibs("SELECT id, name, type FROM libraries ORDER BY id",db);
// Get the libraries
db::Statement stmtGetLibs("SELECT id, name, type FROM libraries ORDER BY id", db);
while(stmtGetLibs.Step() == db::Row) {
while(stmtGetLibs.Step() == db::Row) {
int id = stmtGetLibs.ColumnInt(0);
std::string name = stmtGetLibs.ColumnText(1);
int type = stmtGetLibs.ColumnInt(2);
this->AddLibrary(id, name, type);
}
this->AddLibrary(id, type, name);
}
// If there are no libraries, add a LocalLibrary
if (this->libraries.empty()) {
this->CreateLibrary("Local Library", LibraryFactory::LocalLibrary);
}
// If there are no libraries, add a LocalLibrary
if (this->libraries.empty()) {
this->CreateLibrary("Local Library", LocalLibrary);
}
}
//////////////////////////////////////////
///\brief
///Destructor
//////////////////////////////////////////
LibraryFactory::~LibraryFactory(void){
LibraryFactory::~LibraryFactory() {
}
//////////////////////////////////////////
@ -107,36 +101,21 @@ LibraryFactory::~LibraryFactory(void){
///\returns
///LibraryPtr of the added library. (NULL pointer on failure)
//////////////////////////////////////////
LibraryPtr LibraryFactory::AddLibrary(int id, std::string name,int type,bool sendEvent,bool startup){
LibraryPtr lib;
switch(type) {
case LibraryFactory::LocalLibrary:
lib = library::LocalLibrary::Create(name, id);
break;
LibraryPtr LibraryFactory::AddLibrary(int id, int type, const std::string& name)
{
LibraryPtr library = library::LocalLibrary::Create(name, id);
if (library) {
this->libraries.push_back(library);
this->libraryMap[id] = library;
this->LibrariesUpdated();
}
default:
throw "invalid library type!";
}
if (lib) {
this->libraries.push_back(lib);
this->libraryMap[id] = lib;
if(sendEvent) {
this->LibrariesUpdated();
}
if(startup) {
lib->Startup();
}
}
return lib;
return library;
}
void LibraryFactory::RemoveLibrary(std::string name){
void LibraryFactory::Shutdown() {
Instance().libraries.clear();
}
//////////////////////////////////////////
@ -155,25 +134,22 @@ void LibraryFactory::RemoveLibrary(std::string name){
///\returns
///LibraryPtr of the added library. (NULL pointer on failure)
//////////////////////////////////////////
LibraryPtr LibraryFactory::CreateLibrary(std::string name,int type,bool startup){
// Connect to the settings.db
LibraryPtr LibraryFactory::CreateLibrary(const std::string& name, int type) {
// Connect to the settings.db
std::string dataDir = GetDataDirectory();
std::string dbFile = GetDataDirectory() + "settings.db";
musik::core::db::Connection db;
musik::core::db::Connection db;
db.Open(dbFile.c_str(), 0, 128);
db::Statement stmtInsert("INSERT OR FAIL INTO libraries (name,type) VALUES (?,?)", db);
stmtInsert.BindText(0, name);
stmtInsert.BindInt(1, type);
db::Statement stmtInsert("INSERT OR FAIL INTO libraries (name,type) VALUES (?,?)", db);
stmtInsert.BindText(0, name);
stmtInsert.BindInt(1, type);
if (stmtInsert.Step() == db::Done) {
return this->AddLibrary(db.LastInsertedId(), name, type, true, startup);
}
if (stmtInsert.Step() == db::Done) {
return this->AddLibrary(db.LastInsertedId(), type, name);
}
return LibraryPtr();
}
void LibraryFactory::DeleteLibrary(std::string name){
return LibraryPtr();
}
//////////////////////////////////////////

View File

@ -36,77 +36,41 @@
#pragma once
#include <core/config.h>
#include <core/library/LibraryBase.h>
#include <core/library/LocalLibrary.h>
#include <sigslot/sigslot.h>
#include <map>
#include <vector>
//////////////////////////////////////////////////////////////////////////////
namespace musik{ namespace core{
//////////////////////////////////////////////////////////////////////////////
class LibraryFactory{
public:
typedef std::vector<LibraryPtr> LibraryVector;
typedef std::map<int, LibraryPtr> LibraryMap;
typedef sigslot::signal0<> LibrariesUpdatedEvent;
//////////////////////////////////////////
///\brief
///Factory for Libraries
///
///LibraryFactory contains all Libraries (LocalLibrary and Remote)
///When the LibraryFactory is first initialized it will load all
///libraries from the settings.db database.
//////////////////////////////////////////
class LibraryFactory{
private:
// static LibraryFactory sInstance;
public:
enum LibraryType {
LocalLibrary = 1
};
//////////////////////////////////////////
///\brief
///enum for the different library types
//////////////////////////////////////////
typedef enum {
LocalLibrary=1,
Remote=2
} Types;
~LibraryFactory();
typedef std::vector<LibraryPtr> LibraryVector;
typedef std::map<int,LibraryPtr> LibraryMap;
static LibraryFactory& Instance();
static LibraryVector& Libraries();
//////////////////////////////////////////
///\brief
///Get the LibraryFactory singleton
//////////////////////////////////////////
static LibraryFactory& Instance();
LibraryPtr CreateLibrary(const std::string& name, int type);
void Shutdown();
static LibraryVector& Libraries();
LibraryPtr GetLibrary(int identifier);
LibrariesUpdatedEvent LibrariesUpdated;
LibraryPtr CreateLibrary(std::string name,int type,bool startup=true);
void DeleteLibrary(std::string name);
private:
LibraryFactory();
LibraryPtr GetLibrary(int identifier);
LibraryPtr AddLibrary(int id, int type, const std::string& name);
typedef sigslot::signal0<> LibrariesUpdatedEvent;
//////////////////////////////////////////
///\brief
///signal alerting that a library has been added/removed
//////////////////////////////////////////
LibrariesUpdatedEvent LibrariesUpdated;
~LibraryFactory(void);
private:
LibraryVector libraries;
LibraryMap libraryMap;
LibraryFactory(void);
LibraryPtr AddLibrary(int id, std::string name,int type,bool sendEvent=false,bool startup=true);
void RemoveLibrary(std::string name);
};
//////////////////////////////////////////////////////////////////////////////
} } // musik::core
//////////////////////////////////////////////////////////////////////////////
LibraryVector libraries;
LibraryMap libraryMap;
};
} }

View File

@ -37,95 +37,342 @@
#include "pch.hpp"
#include <core/library/LocalLibrary.h>
#include <core/config.h>
#include <core/library/query/QueryBase.h>
#include <core/support/Common.h>
#include <core/support/Preferences.h>
#include <boost/bind.hpp>
#include <core/library/Indexer.h>
using namespace musik::core;
using namespace musik::core::library;
//////////////////////////////////////////
///\brief
///Constructor.
///
///The constructor will not start the Library.
///
///\see
///Startup
//////////////////////////////////////////
LocalLibrary::LocalLibrary(std::string name,int id)
: LibraryBase(name, id) {
}
//////////////////////////////////////////
///\brief
///Create a LocalLibrary library
//////////////////////////////////////////
LibraryPtr LocalLibrary::Create(std::string name,int id) {
LibraryPtr LocalLibrary::Create(std::string name, int id) {
LibraryPtr lib(new LocalLibrary(name, id));
return lib;
}
//////////////////////////////////////////
///\brief
///Destructor that exits and joins all threads
//////////////////////////////////////////
LocalLibrary::LocalLibrary(std::string name,int id)
: name(name)
, id(id)
, exit(false) {
this->identifier = boost::lexical_cast<std::string>(id);
Preferences prefs("Library");
this->db.Open(
this->GetDatabaseFilename().c_str(),
0,
prefs.GetInt("DatabaseCache",
4096));
LocalLibrary::CreateDatabase(this->db);
this->indexer = new core::Indexer(
this->GetLibraryDirectory(),
this->GetDatabaseFilename());
this->thread = new boost::thread(boost::bind(&LocalLibrary::ThreadProc, this));
}
LocalLibrary::~LocalLibrary() {
this->Exit();
this->thread->join();
this->threads.join_all();
delete this->thread;
delete this->indexer;
}
int LocalLibrary::Id() {
return this->id;
}
//////////////////////////////////////////
///\brief
///Startup the library threads.
///Name of the library
//////////////////////////////////////////
const std::string& LocalLibrary::Name() {
return this->name;
}
//////////////////////////////////////////
///\brief
///Get the directory-location of the library where you may store extra files.
///
///\returns
///True if successfully started. This should always be true. Nothing else is expected.
///String with the path
///
///Start up the Library like this:
///\code
/// // Create a library
/// musik::core::library::LocalLibrary library;
/// // Start the library (and indexer that is included)
/// library.Startup();
/// // The library is now ready to receive queries
///\endcode
///The library directory is a directory where you may store
///the librarys database and other files like thumbnail cache.
///In a win32 environment this path will be located in the users
///$APPDATA/mC2/"identifier"/
///where the identifier is set in the library itself.
///
///\remarks
///If the directory does not exist, this method will create it.
//////////////////////////////////////////
bool LocalLibrary::Startup() {
this->thread = new boost::thread(boost::bind(&LocalLibrary::ThreadLoop, this));
return true;
std::string LocalLibrary::GetLibraryDirectory() {
std::string directory(musik::core::GetDataDirectory());
if (!this->identifier.empty()) {
directory.append(this->identifier + "/" );
}
boost::filesystem::path dir(directory);
if(!boost::filesystem::exists(dir)){
boost::filesystem::create_directories(dir);
}
directory = dir.string();
return directory;
}
std::string LocalLibrary::GetDatabaseFilename() {
return this->GetLibraryDirectory() + "musik.db";
}
int LocalLibrary::Enqueue(QueryPtr query, unsigned int options) {
boost::recursive_mutex::scoped_lock l(this->mutex);
queryQueue.push_back(query);
queueCondition.notify_all();
return query->GetId();
}
bool LocalLibrary::Exited() {
boost::recursive_mutex::scoped_lock lock(this->mutex);
return this->exit;
}
void LocalLibrary::Exit() {
{
boost::recursive_mutex::scoped_lock lock(this->mutex);
this->exit = true;
}
/* kick sleeping threads back to the top of the loop */
this->queueCondition.notify_all();
}
QueryPtr LocalLibrary::GetNextQuery() {
if (queryQueue.size()) {
QueryPtr front = queryQueue.front();
queryQueue.pop_front();
return front;
}
return QueryPtr();
}
void LocalLibrary::ThreadProc() {
while (!this->Exited()) {
QueryPtr query;
{
boost::recursive_mutex::scoped_lock lock(this->mutex);
query = GetNextQuery();
while (!query && !this->Exited()) {
this->queueCondition.wait(lock);
query = GetNextQuery();
}
}
if (query) {
query->Run(this->db);
this->QueryCompleted(query);
query.reset();
}
}
}
musik::core::IIndexer* LocalLibrary::Indexer() {
return this->indexer;
}
//////////////////////////////////////////
///\brief
///Main loop the library thread is running in.
///
///The loop will run until Exit() has been called.
///Helper method to determin what metakeys are "static"
//////////////////////////////////////////
void LocalLibrary::ThreadLoop() {
Preferences prefs("Library");
bool LocalLibrary::IsStaticMetaKey(std::string &metakey){
static std::set<std::string> staticMetaKeys;
std::string database(this->GetDatabasePath());
this->db.Open(database.c_str(), 0, prefs.GetInt("DatabaseCache", 4096));
if (staticMetaKeys.empty()) {
staticMetaKeys.insert("track");
staticMetaKeys.insert("bpm");
staticMetaKeys.insert("duration");
staticMetaKeys.insert("filesize");
staticMetaKeys.insert("year");
staticMetaKeys.insert("title");
staticMetaKeys.insert("filename");
staticMetaKeys.insert("filetime");
}
LibraryBase::CreateDatabase(this->db);
/* start the indexer running */
this->indexer.database = database;
this->indexer.Startup(this->GetLibraryDirectory());
//while (!this->Exited()) {
//}
return staticMetaKeys.find(metakey) != staticMetaKeys.end();
}
//////////////////////////////////////////
///\brief
///Get a pointer to the librarys Indexer (NULL if none)
///Helper method to determine what metakeys that have a special many to one relation
//////////////////////////////////////////
musik::core::Indexer* LocalLibrary::Indexer() {
return &this->indexer;
bool LocalLibrary::IsSpecialMTOMetaKey(std::string &metakey){
static std::set<std::string> specialMTOMetaKeys;
if (specialMTOMetaKeys.empty()) {
specialMTOMetaKeys.insert("album");
specialMTOMetaKeys.insert("visual_genre");
specialMTOMetaKeys.insert("visual_artist");
specialMTOMetaKeys.insert("folder");
}
return specialMTOMetaKeys.find(metakey)!=specialMTOMetaKeys.end();
}
//////////////////////////////////////////
///\brief
///Helper method to determine what metakeys that have a special many to meny relation
//////////////////////////////////////////
bool LocalLibrary::IsSpecialMTMMetaKey(std::string &metakey) {
static std::set<std::string> specialMTMMetaKeys;
if (specialMTMMetaKeys.empty()) {
specialMTMMetaKeys.insert("artist");
specialMTMMetaKeys.insert("genre");
}
return specialMTMMetaKeys.find(metakey) != specialMTMMetaKeys.end();
}
//////////////////////////////////////////
///\brief
///Create all tables, indexes, etc in the database.
///
///This will assume that the database has been initialized.
//////////////////////////////////////////
void LocalLibrary::CreateDatabase(db::Connection &db){
// Create the tracks-table
db.Execute("CREATE TABLE IF NOT EXISTS tracks ("
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
"track INTEGER DEFAULT 0,"
"bpm REAL DEFAULT 0,"
"duration INTEGER DEFAULT 0,"
"filesize INTEGER DEFAULT 0,"
"year INTEGER DEFAULT 0,"
"visual_genre_id INTEGER DEFAULT 0,"
"visual_artist_id INTEGER DEFAULT 0,"
"album_id INTEGER DEFAULT 0,"
"folder_id INTEGER DEFAULT 0,"
"title TEXT default '',"
"filename TEXT default '',"
"filetime INTEGER DEFAULT 0,"
"thumbnail_id INTEGER DEFAULT 0,"
"sort_order1 INTEGER)");
// Create the genres-table
db.Execute("CREATE TABLE IF NOT EXISTS genres ("
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
"name TEXT default '',"
"aggregated INTEGER DEFAULT 0,"
"sort_order INTEGER DEFAULT 0)");
db.Execute("CREATE TABLE IF NOT EXISTS track_genres ("
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
"track_id INTEGER DEFAULT 0,"
"genre_id INTEGER DEFAULT 0)");
// Create the artists-table
db.Execute("CREATE TABLE IF NOT EXISTS artists ("
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
"name TEXT default '',"
"aggregated INTEGER DEFAULT 0,"
"sort_order INTEGER DEFAULT 0)");
db.Execute("CREATE TABLE IF NOT EXISTS track_artists ("
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
"track_id INTEGER DEFAULT 0,"
"artist_id INTEGER DEFAULT 0)");
// Create the meta-tables
db.Execute("CREATE TABLE IF NOT EXISTS meta_keys ("
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
"name TEXT)");
db.Execute("CREATE TABLE IF NOT EXISTS meta_values ("
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
"meta_key_id INTEGER DEFAULT 0,"
"sort_order INTEGER DEFAULT 0,"
"content TEXT)");
db.Execute("CREATE TABLE IF NOT EXISTS track_meta ("
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
"track_id INTEGER DEFAULT 0,"
"meta_value_id INTEGER DEFAULT 0)");
// Create the albums-table
db.Execute("CREATE TABLE IF NOT EXISTS albums ("
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
"name TEXT default '',"
"thumbnail_id INTEGER default 0,"
"sort_order INTEGER DEFAULT 0)");
// Create the paths-table
db.Execute("CREATE TABLE IF NOT EXISTS paths ("
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
"path TEXT default ''"
")");
// Create the folders-table
db.Execute("CREATE TABLE IF NOT EXISTS folders ("
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
"name TEXT default '',"
"relative_path TEXT default '',"
"parent_id INTEGER DEFAULT 0,"
"path_id INTEGER DEFAULT 0"
")");
// Create the folders-table
db.Execute("CREATE TABLE IF NOT EXISTS thumbnails ("
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
"filename TEXT default '',"
"filesize INTEGER DEFAULT 0,"
"checksum INTEGER DEFAULT 0"
")");
// Create the playlists-table
db.Execute("CREATE TABLE IF NOT EXISTS playlists ("
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
"name TEXT default '',"
"user_id INTEGER default 0"
")");
// Create the playlists-table
db.Execute("CREATE TABLE IF NOT EXISTS playlist_tracks ("
"track_id INTEGER DEFAULT 0,"
"playlist_id INTEGER DEFAULT 0,"
"sort_order INTEGER DEFAULT 0"
")");
// INDEXES
db.Execute("CREATE UNIQUE INDEX IF NOT EXISTS users_index ON users (login)");
db.Execute("CREATE UNIQUE INDEX IF NOT EXISTS folders_index ON folders (name,parent_id,path_id)");
db.Execute("CREATE UNIQUE INDEX IF NOT EXISTS paths_index ON paths (path)");
db.Execute("CREATE INDEX IF NOT EXISTS genre_index ON genres (sort_order)");
db.Execute("CREATE INDEX IF NOT EXISTS artist_index ON artists (sort_order)");
db.Execute("CREATE INDEX IF NOT EXISTS album_index ON albums (sort_order)");
db.Execute("CREATE INDEX IF NOT EXISTS track_index1 ON tracks (album_id,sort_order1)");
db.Execute("CREATE INDEX IF NOT EXISTS track_index7 ON tracks (folder_id)");
db.Execute("CREATE INDEX IF NOT EXISTS thumbnail_index ON thumbnails (filesize)");
db.Execute("CREATE INDEX IF NOT EXISTS trackgenre_index1 ON track_genres (track_id,genre_id)");
db.Execute("CREATE INDEX IF NOT EXISTS trackgenre_index2 ON track_genres (genre_id,track_id)");
db.Execute("CREATE INDEX IF NOT EXISTS trackartist_index1 ON track_artists (track_id,artist_id)");
db.Execute("CREATE INDEX IF NOT EXISTS trackartist_index2 ON track_artists (artist_id,track_id)");
db.Execute("CREATE INDEX IF NOT EXISTS trackmeta_index1 ON track_meta (track_id,meta_value_id)");
db.Execute("CREATE INDEX IF NOT EXISTS trackmeta_index2 ON track_meta (meta_value_id,track_id)");
db.Execute("CREATE INDEX IF NOT EXISTS metakey_index1 ON meta_keys (name)");
db.Execute("CREATE INDEX IF NOT EXISTS metavalues_index1 ON meta_values (meta_key_id)");
db.Execute("CREATE INDEX IF NOT EXISTS playlist_index ON playlist_tracks (playlist_id,sort_order)");
db.Analyze();
}

View File

@ -36,38 +36,28 @@
#pragma once
//////////////////////////////////////////////////////////////////////////////
// Forward declare
namespace musik{ namespace core{
namespace query{
class Base;
}
} }
//////////////////////////////////////////////////////////////////////////////
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
#include <boost/lexical_cast.hpp>
#include <core/config.h>
#include <core/db/Connection.h>
#include <core/db/Statement.h>
#include <core/db/ScopedTransaction.h>
#include <core/library/LibraryBase.h>
#include <core/library/Indexer.h>
//////////////////////////////////////////////////////////////////////////////
#include <core/library/ILibrary.h>
#include <core/library/IIndexer.h>
#include <core/library/IQuery.h>
namespace musik{ namespace core{ namespace library{
#include <boost/thread/thread.hpp>
#include <boost/thread/condition.hpp>
#include <boost/thread/recursive_mutex.hpp>
#include <boost/utility.hpp>
#include <sigslot/sigslot.h>
#include <string>
//////////////////////////////////////////
///\brief
///Library used for your local music.
///
///This library is used for music located
///on you local computer.
///
///\see
///Indexer
//////////////////////////////////////////
class LocalLibrary : public library::LibraryBase {
private:
namespace musik { namespace core { namespace library {
class LocalLibrary : public ILibrary, boost::noncopyable {
protected:
LocalLibrary(std::string name, int id);
public:
@ -75,16 +65,44 @@ namespace musik{ namespace core{ namespace library{
virtual ~LocalLibrary();
bool Startup();
musik::core::Indexer *Indexer();
virtual int Enqueue(QueryPtr query, unsigned int options = 0);
virtual musik::core::IIndexer *Indexer();
virtual int Id();
virtual const std::string& Name();
std::string GetLibraryDirectory();
std::string GetDatabaseFilename();
static bool IsStaticMetaKey(std::string &metakey);
static bool IsSpecialMTOMetaKey(std::string &metakey);
static bool IsSpecialMTMMetaKey(std::string &metakey);
static void CreateDatabase(db::Connection &db);
protected:
virtual void Exit();
bool Exited();
void ThreadProc();
void NotifyQueryCompleted(QueryPtr query);
QueryPtr GetNextQuery();
private:
void ThreadLoop();
typedef std::list<QueryPtr> QueryList;
boost::recursive_mutex mutex;
QueryList queryQueue;
std::string identifier;
int id;
std::string name;
bool exit;
private:
db::Connection db;
musik::core::Indexer indexer;
boost::thread* thread;
boost::thread_group threads;
boost::condition queueCondition;
core::IIndexer *indexer;
core::db::Connection db;
};
} } }

View File

@ -37,17 +37,19 @@
#include "pch.hpp"
#include <core/library/query/QueryBase.h>
#include <core/library/LibraryBase.h>
#include <core/library/LocalLibrary.h>
#include <boost/atomic.hpp>
using namespace musik::core;
using namespace musik::core::query;
static boost::atomic<int> nextId(0);
QueryBase::QueryBase()
: status(0)
, options(0)
, queryId(0) {
static unsigned int uniqueQueryId = 0;
this->queryId = uniqueQueryId++;
this->queryId = nextId++;
}
QueryBase::~QueryBase() {
@ -57,6 +59,21 @@ std::string QueryBase::Name() {
return "QueryBase";
}
bool QueryBase::Run(db::Connection &db) {
this->SetStatus(Running);
try {
if (OnRun(db)) {
this->SetStatus(Finished);
return true;
}
}
catch (...) {
}
this->SetStatus(Failed);
return false;
}
int QueryBase::GetStatus() {
boost::mutex::scoped_lock lock(this->stateMutex);
return this->status;
@ -67,7 +84,7 @@ void QueryBase::SetStatus(int status) {
this->status = status;
}
int QueryBase::GetQueryId() {
int QueryBase::GetId() {
boost::mutex::scoped_lock lock(this->stateMutex);
return this->queryId;
}

View File

@ -39,69 +39,31 @@
#include <core/config.h>
#include <sigslot/sigslot.h>
#include <boost/shared_ptr.hpp>
/* forward decl */
namespace musik { namespace core {
namespace db {
class Connection;
}
namespace library {
class LibraryBase;
class LocalLibrary;
}
} }
#include <boost/thread/mutex.hpp>
#include <core/library/IQuery.h>
namespace musik { namespace core { namespace query {
class QueryBase;
typedef boost::shared_ptr<query::QueryBase> Ptr;
typedef enum {
AutoCallback = 1,
Prioritize = 4,
CancelQueue = 8,
CancelSimilar = 16,
UnCanceable = 32,
CopyUniqueId = 64
Prioritize = 1
} Options;
//////////////////////////////////////////
///\brief
///Interface class for all queries.
//////////////////////////////////////////
class QueryBase : public sigslot::has_slots<> {
class QueryBase : public IQuery, public sigslot::has_slots<> {
public:
typedef sigslot::signal3<
query::QueryBase*,
library::LibraryBase*,
bool> QueryFinishedEvent;
typedef enum {
Idle = 1,
Running = 2,
Canceled = 3,
Finished = 4,
} Status;
QueryFinishedEvent QueryFinished;
QueryBase();
virtual ~QueryBase();
int GetStatus();
int GetQueryId();
int GetOptions();
virtual bool Run(db::Connection &db);
virtual int GetStatus();
virtual int GetId();
virtual int GetOptions();
protected:
void SetStatus(int status);
void SetOptions(int options);
virtual bool RunCallbacks(library::LibraryBase *library) { return true; };
virtual bool RunQuery(library::LibraryBase *library, db::Connection &db) = 0;
virtual std::string Name();
virtual bool OnRun(db::Connection &db) = 0;
virtual std::string Name() = 0;
private:
unsigned int status;
@ -110,7 +72,4 @@ namespace musik { namespace core { namespace query {
boost::mutex stateMutex;
};
//////////////////////////////////////////////////////////////////////////////
} } } // musik::core::query
//////////////////////////////////////////////////////////////////////////////
} } }

View File

@ -38,7 +38,7 @@
#include <core/config.h>
#include <core/library/track/Track.h>
#include <core/library/LibraryBase.h>
#include <core/library/LocalLibrary.h>
#include <boost/weak_ptr.hpp>
#include <boost/thread/mutex.hpp>

View File

@ -41,7 +41,7 @@
#include <core/support/Common.h>
#include <core/db/Connection.h>
#include <core/db/Statement.h>
#include <core/library/LibraryBase.h>
#include <core/library/LocalLibrary.h>
#include <core/io/DataStreamFactory.h>
#include <boost/lexical_cast.hpp>

View File

@ -38,7 +38,7 @@
#include <core/config.h>
#include <core/library/track/Track.h>
#include <core/library/LibraryBase.h>
#include <core/library/LocalLibrary.h>
namespace musik { namespace core {

View File

@ -42,7 +42,7 @@
#include <core/support/Common.h>
#include <core/db/Connection.h>
#include <core/db/Statement.h>
#include <core/library/LibraryBase.h>
#include <core/library/LocalLibrary.h>
#include <boost/lexical_cast.hpp>
#include <boost/thread/mutex.hpp>
@ -128,30 +128,9 @@ void LibraryTrack::SetThumbnail(const char *data, long size) {
memcpy(this->meta->thumbnailData, data, size);
}
std::string LibraryTrack::URI(){
static std::string uri;
/* todo: don't use static; create during InitMeta() */
if (this->meta) {
uri =
"mcdb://" +
this->meta->library->Identifier() +
"/" +
boost::lexical_cast<std::string>(this->id);
return uri.c_str();
}
else {
uri =
"mcdb://" +
boost::lexical_cast<std::string>(this->libraryId) +
"/" +
boost::lexical_cast<std::string>(this->id);
return uri.c_str();
}
return NULL;
std::string LibraryTrack::URI() {
int libraryId = this->meta ? this->meta->library->Id() : this->libraryId;
return boost::str(boost::format("mcdb://%1%/%2%") % libraryId % this->id);
}
std::string LibraryTrack::URL() {

View File

@ -38,7 +38,7 @@
#include <core/config.h>
#include <core/library/track/Track.h>
#include <core/library/LibraryBase.h>
#include <core/library/LocalLibrary.h>
namespace musik { namespace core { namespace http {
class Responder;

View File

@ -37,7 +37,7 @@
#include "pch.hpp"
#include <core/library/track/Track.h>
#include <core/library/LibraryBase.h>
#include <core/library/LocalLibrary.h>
using namespace musik::core;

View File

@ -37,30 +37,22 @@
#pragma once
#include <core/sdk/IMetadataWriter.h>
#include <core/library/ILibrary.h>
#include <boost/shared_ptr.hpp>
#include <vector>
#include <map>
/* forward decl */
namespace musik { namespace core {
class Track;
namespace library {
class LibraryBase;
}
typedef boost::shared_ptr<library::LibraryBase> LibraryPtr;
typedef boost::shared_ptr<Track> TrackPtr;
typedef std::vector<TrackPtr> TrackVector;
} }
namespace musik{ namespace core{
//////////////////////////////////////////
///\brief
///The most basic implementation of a track
//////////////////////////////////////////
class Track : public IMetadataWriter {
class Track : public IMetadataWriter {
public:
typedef std::multimap<std::string, std::string> MetadataMap;
typedef std::pair<MetadataMap::iterator, MetadataMap::iterator> MetadataIteratorRange;

View File

@ -0,0 +1,16 @@
#pragma once
#include "stdafx.h"
#include "CategoryListQuery.h"
CategoryListQuery::CategoryListQuery() {
}
CategoryListQuery::~CategoryListQuery() {
}
bool CategoryListQuery::OnRun(Connection& db) {
return false;
}

View File

@ -0,0 +1,20 @@
#pragma once
#include <core/library/query/QueryBase.h>
#include <core/db/Connection.h>
using musik::core::query::QueryBase;
using musik::core::db::Connection;
class CategoryListQuery : public QueryBase {
public:
CategoryListQuery();
~CategoryListQuery();
std::string Name() {
return "CategoryListQuery";
}
protected:
virtual bool OnRun(Connection &db);
};

View File

@ -5,6 +5,8 @@
#include "Screen.h"
#include "Colors.h"
#include "CategoryListQuery.h"
#include <core/debug.h>
#include <core/sdk/IPlugin.h>
#include <core/plugin/PluginFactory.h>
@ -22,6 +24,7 @@ using musik::core::LibraryFactory;
using musik::core::LibraryPtr;
using musik::core::TrackFactory;
using musik::core::TrackPtr;
using musik::core::QueryPtr;
template <class T>
bool tostr(T& t, const std::string& s) {
@ -29,17 +32,16 @@ bool tostr(T& t, const std::string& s) {
return !(iss >> t).fail();
}
CommandWindow::CommandWindow(Transport& transport, OutputWindow& output) {
CommandWindow::CommandWindow(Transport& transport, OutputWindow& output)
: Window() {
this->transport = &transport;
this->buffer = new char[BUFFER_SIZE];
this->bufferPosition = 0;
this->SetSize(Screen::GetWidth() / 2, 3);
this->SetPosition(0, Screen::GetHeight() - 3);
this->Create();
this->output = &output;
this->paused = false;
this->library = LibraryFactory::Libraries().at(0);
this->output->WriteLine("type 'h' or 'help'\n", BOX_COLOR_BLACK_ON_GREY);
this->library->Enqueue(QueryPtr(new CategoryListQuery()));
}
CommandWindow::~CommandWindow() {
@ -157,7 +159,7 @@ bool CommandWindow::ProcessCommand(const std::string& cmd) {
this->output->WriteLine("");
}
else if (name == "rescan" || name == "scan" || name == "index") {
library->Indexer()->RestartSync();
library->Indexer()->Synchronize();
}
else if (name == "h" || name == "help") {
this->Help();

View File

@ -3,6 +3,10 @@
#include "IWindow.h"
class ILayout {
virtual IWindow* focusNext() = 0;
virtual IWindow* focusPrev() = 0;
public:
virtual IWindow* FocusNext() = 0;
virtual IWindow* FocusPrev() = 0;
virtual IWindow* GetFocus() = 0;
virtual void Layout() = 0;
virtual void OnIdle() = 0;
};

11
src/musikbox/IScrollable.h Executable file
View File

@ -0,0 +1,11 @@
#pragma once
class IScrollable {
public:
virtual void ScrollToTop() = 0;
virtual void ScrollToBottom() = 0;
virtual void ScrollUp(int delta = 1) = 0;
virtual void ScrollDown(int delta = 1) = 0;
virtual void PageUp() = 0;
virtual void PageDown() = 0;
};

View File

@ -6,17 +6,14 @@
#include "Screen.h"
#include <curses.h>
LogWindow::LogWindow() {
LogWindow::LogWindow()
: ScrollableWindow() {
this->SetContentColor(BOX_COLOR_WHITE_ON_BLUE);
this->SetSize(Screen::GetWidth() / 2, Screen::GetHeight() - 3);
this->SetPosition(Screen::GetWidth() / 2, 0);
this->adapter = new SimpleScrollAdapter();
this->adapter->SetDisplaySize(this->GetContentWidth(), this->GetContentHeight());
this->adapter->SetMaxEntries(500);
this->Create();
musik::debug::string_logged.connect(this, &LogWindow::OnLogged);
musik::debug::warn("LogWindow", "initialized");
}

View File

@ -39,6 +39,7 @@
#include "OutputWindow.h"
#include "TransportWindow.h"
#include "ResourcesWindow.h"
#include "MainLayout.h"
#include "IInput.h"
#include <boost/locale.hpp>
@ -49,6 +50,8 @@
#ifdef WIN32
#undef MOUSE_MOVED
#define IDLE_TIMEOUT_MS 500
int _main(int argc, _TCHAR* argv[]);
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow) {
@ -85,77 +88,48 @@ int main(int argc, char* argv[])
#endif
{
Colors::Init();
Transport tp;
tp.SetVolume(0.01);
Colors::Init();
LogWindow logs;
OutputWindow output;
ResourcesWindow resources;
CommandWindow command(tp, output);
TransportWindow transport(tp);
std::vector<IWindow*> order;
order.push_back(&command);
order.push_back(&logs);
order.push_back(&output);
/* set the initial state: select the command window and get
the focused state painting properly. it will be done automatically
every time after this */
size_t index = 0;
IWindow *focused = order.at(index);
ScrollableWindow *scrollable = NULL;
IInput *input = &command;
focused->SetFrameColor(BOX_COLOR_RED_ON_BLACK);
curs_set(1);
input->Focus();
wtimeout(focused->GetContent(), 500);
bool disable = false;
MainLayout layout(tp);
int ch;
timeout(500);
timeout(IDLE_TIMEOUT_MS);
bool quit = false;
IWindow* focused = layout.GetFocus();
IInput* input = dynamic_cast<IInput*>(focused);
IScrollable* scrollable = dynamic_cast<IScrollable*>(focused);
if (input != NULL) {
curs_set(1);
wtimeout(focused->GetContent(), IDLE_TIMEOUT_MS);
}
while (!quit) {
/* if the focused item is an IInput, then get characters from it,
so it can draw a pretty cursor if it wants */
ch = (input != NULL) ? wgetch(focused->GetContent()) : getch();
if (ch == -1) { /* timeout */
if (!disable) {
logs.Update();
}
transport.Repaint();
resources.Repaint();
}
else if (ch == 'f') {
disable = true;
layout.OnIdle();
}
else if (ch == 9) { /* tab */
if (input != NULL) {
/* losing focus, reset timeout */
wtimeout(focused->GetContent(), 0);
}
focused->SetFrameColor(BOX_COLOR_WHITE_ON_BLACK);
index++;
if (index >= order.size()) {
index = 0;
}
focused = order.at(index);
focused->SetFrameColor(BOX_COLOR_RED_ON_BLACK);
focused = layout.FocusNext();
scrollable = dynamic_cast<ScrollableWindow*>(focused);
input = dynamic_cast<IInput*>(focused);
if (input != NULL) {
curs_set(1);
input->Focus();
wtimeout(focused->GetContent(), 500);
wtimeout(focused->GetContent(), IDLE_TIMEOUT_MS);
}
else {
curs_set(0);
@ -191,6 +165,7 @@ int main(int argc, char* argv[])
endwin();
musik::core::LibraryFactory::Instance().Shutdown();
musik::debug::deinit();
return 0;

96
src/musikbox/MainLayout.cpp Executable file
View File

@ -0,0 +1,96 @@
#pragma once
#include "stdafx.h"
#include "MainLayout.h"
#include "Screen.h"
static inline IWindow* adjustFocus(IWindow* oldFocus, IWindow* newFocus) {
if (oldFocus) {
oldFocus->SetFrameColor(BOX_COLOR_WHITE_ON_BLACK);
}
if (newFocus) {
newFocus->SetFrameColor(BOX_COLOR_RED_ON_BLACK);
}
return newFocus;
}
MainLayout::MainLayout(Transport& transport) {
this->logs.reset(new LogWindow());
this->output.reset(new OutputWindow());
this->resources.reset(new ResourcesWindow());
this->commands.reset(new CommandWindow(transport, *this->output));
this->transport.reset(new TransportWindow(transport));
this->focusOrder.push_back(commands.get());
this->focusOrder.push_back(output.get());
this->focusOrder.push_back(logs.get());
this->focusIndex = 0;
this->Layout();
adjustFocus(NULL, GetFocus());
}
MainLayout::~MainLayout() {
}
IWindow* MainLayout::FocusNext() {
IWindow* oldFocus = GetFocus();
if (++focusIndex >= (int) focusOrder.size()) {
focusIndex = 0;
}
return adjustFocus(oldFocus, GetFocus());
}
IWindow* MainLayout::FocusPrev() {
IWindow* oldFocus = GetFocus();
if (--focusIndex <= 0) {
focusIndex = (int) focusOrder.size() - 1;
}
return adjustFocus(oldFocus, GetFocus());
}
IWindow* MainLayout::GetFocus() {
return focusOrder[focusIndex];
}
void MainLayout::Layout() {
/* top left */
this->transport->SetSize(Screen::GetWidth() / 2, 4);
this->transport->SetPosition(0, 0);
this->transport->Create();
this->transport->Repaint();
/* middle left */
this->output->SetSize(Screen::GetWidth() / 2, Screen::GetHeight() - 3 - 4);
this->output->SetPosition(0, 4);
this->output->Create();
this->output->Repaint();
/* bottom left */
this->commands->SetSize(Screen::GetWidth() / 2, 3);
this->commands->SetPosition(0, Screen::GetHeight() - 3);
this->commands->Create();
/* top right */
this->logs->SetSize(Screen::GetWidth() / 2, Screen::GetHeight() - 3);
this->logs->SetPosition(Screen::GetWidth() / 2, 0);
this->logs->Create();
/* bottom right */
this->resources->SetSize(Screen::GetWidth() / 2, 3);
this->resources->SetPosition(Screen::GetWidth() / 2, Screen::GetHeight() - 3);
this->resources->Create();
this->resources->Repaint();
}
void MainLayout::OnIdle() {
this->logs->Update();
this->transport->Repaint();
this->resources->Repaint();
}

37
src/musikbox/MainLayout.h Executable file
View File

@ -0,0 +1,37 @@
#pragma once
#include "ILayout.h"
#include "LogWindow.h"
#include "CommandWindow.h"
#include "OutputWindow.h"
#include "TransportWindow.h"
#include "ResourcesWindow.h"
#include <vector>
#include <core/playback/Transport.h>
#include <boost/shared_ptr.hpp>
using musik::core::audio::Transport;
class MainLayout : public ILayout {
public:
MainLayout(Transport& transport);
~MainLayout();
virtual IWindow* FocusNext();
virtual IWindow* FocusPrev();
virtual IWindow* GetFocus();
virtual void Layout();
virtual void OnIdle();
private:
boost::shared_ptr<LogWindow> logs;
boost::shared_ptr<CommandWindow> commands;
boost::shared_ptr<OutputWindow> output;
boost::shared_ptr<TransportWindow> transport;
boost::shared_ptr<ResourcesWindow> resources;
std::vector<IWindow*> focusOrder;
int focusIndex;
};

View File

@ -8,15 +8,11 @@
OutputWindow::OutputWindow()
: ScrollableWindow()
{
this->SetSize(Screen::GetWidth() / 2, Screen::GetHeight() - 3 - 4);
this->SetPosition(0, 4);
this->SetContentColor(BOX_COLOR_BLACK_ON_GREY);
this->adapter = new SimpleScrollAdapter();
this->adapter->SetDisplaySize(this->GetContentWidth(), this->GetContentHeight());
this->adapter->SetMaxEntries(500);
this->Create();
}
OutputWindow::~OutputWindow() {

View File

@ -9,16 +9,9 @@
#include <boost/format.hpp>
ResourcesWindow::ResourcesWindow() {
this->SetSize(Screen::GetWidth() / 2, 3);
this->SetPosition(
Screen::GetWidth() / 2,
Screen::GetHeight() - this->GetHeight());
ResourcesWindow::ResourcesWindow()
: Window() {
this->systemInfo = SystemInfo::Create();
this->Create();
}
ResourcesWindow::~ResourcesWindow() {

View File

@ -27,6 +27,11 @@ void ScrollableWindow::OnAdapterChanged() {
}
}
void ScrollableWindow::Create() {
Window::Create();
this->OnAdapterChanged();
}
size_t ScrollableWindow::GetFirstVisible() {
return scrollPosition;
}

View File

@ -3,18 +3,21 @@
#include "curses_config.h"
#include "Window.h"
#include "IScrollAdapter.h"
#include "IScrollable.h"
class ScrollableWindow : public Window {
class ScrollableWindow : public IScrollable, public Window {
public:
ScrollableWindow();
~ScrollableWindow();
void ScrollToTop();
void ScrollToBottom();
void ScrollUp(int delta = 1);
void ScrollDown(int delta = 1);
void PageUp();
void PageDown();
virtual void ScrollToTop();
virtual void ScrollToBottom();
virtual void ScrollUp(int delta = 1);
virtual void ScrollDown(int delta = 1);
virtual void PageUp();
virtual void PageDown();
virtual void Create();
protected:
virtual IScrollAdapter& GetScrollAdapter() = 0;

View File

@ -1,7 +1,7 @@
#pragma once
#include "curses_config.h"
#include "IScrollAdapter.h";
#include "IScrollAdapter.h"
#include <deque>
class SimpleScrollAdapter : public IScrollAdapter {
@ -44,7 +44,6 @@ class SimpleScrollAdapter : public IScrollAdapter {
typedef std::deque<boost::shared_ptr<Entry>> EntryList;
typedef EntryList::iterator Iterator;
void Reindex();
size_t FindEntryIndex(size_t index);

View File

@ -21,13 +21,11 @@
using musik::core::audio::Transport;
TransportWindow::TransportWindow(Transport& transport) {
this->SetSize(Screen::GetWidth() / 2, 4);
this->SetPosition(0, 0);
TransportWindow::TransportWindow(Transport& transport)
: Window() {
this->SetContentColor(BOX_COLOR_BLACK_ON_GREEN);
this->transport = &transport;
this->paused = false;
this->Create();
}
TransportWindow::~TransportWindow() {
@ -37,7 +35,7 @@ void TransportWindow::Repaint() {
this->Clear();
WINDOW *c = this->GetContent();
float volume = (this->transport->Volume() * 100.0);
double volume = (this->transport->Volume() * 100.0);
wprintw(c, "volume %.1f%%\n", volume);
wprintw(c, "filename: ");

View File

@ -115,6 +115,8 @@
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="CategoryListQuery.cpp" />
<ClCompile Include="MainLayout.cpp" />
<ClCompile Include="Window.cpp" />
<ClCompile Include="Colors.cpp" />
<ClCompile Include="CommandWindow.cpp" />
@ -135,6 +137,9 @@
<ClCompile Include="TransportWindow.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="CategoryListQuery.h" />
<ClInclude Include="IScrollable.h" />
<ClInclude Include="MainLayout.h" />
<ClInclude Include="Window.h" />
<ClInclude Include="Colors.h" />
<ClInclude Include="CommandWindow.h" />

View File

@ -42,6 +42,12 @@
<ClCompile Include="Window.cpp">
<Filter>curses</Filter>
</ClCompile>
<ClCompile Include="CategoryListQuery.cpp">
<Filter>queries</Filter>
</ClCompile>
<ClCompile Include="MainLayout.cpp">
<Filter>windows</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="stdafx.h" />
@ -87,6 +93,15 @@
<ClInclude Include="Window.h">
<Filter>curses</Filter>
</ClInclude>
<ClInclude Include="CategoryListQuery.h">
<Filter>queries</Filter>
</ClInclude>
<ClInclude Include="MainLayout.h">
<Filter>windows</Filter>
</ClInclude>
<ClInclude Include="IScrollable.h">
<Filter>curses</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Filter Include="curses">
@ -95,5 +110,8 @@
<Filter Include="windows">
<UniqueIdentifier>{c1ada983-8f4e-4ac4-86be-fa6fdffec375}</UniqueIdentifier>
</Filter>
<Filter Include="queries">
<UniqueIdentifier>{8de1fc82-96fd-4871-a6b0-fed0d6b21aa1}</UniqueIdentifier>
</Filter>
</ItemGroup>
</Project>