mirror of
https://github.com/clangen/musikcube.git
synced 2025-03-14 04:18:36 +00:00
- 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:
parent
adb0ed14b1
commit
c4111850fe
@ -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" />
|
||||
|
@ -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
20
src/core/library/IIndexer.h
Executable 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
25
src/core/library/ILibrary.h
Executable 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
33
src/core/library/IQuery.h
Executable 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;
|
||||
};
|
||||
|
||||
} }
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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();
|
||||
}
|
@ -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;
|
||||
} }
|
@ -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();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
} }
|
||||
|
@ -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();
|
||||
}
|
@ -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;
|
||||
};
|
||||
|
||||
} } }
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
} } }
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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() {
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
16
src/musikbox/CategoryListQuery.cpp
Executable file
16
src/musikbox/CategoryListQuery.cpp
Executable file
@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "CategoryListQuery.h"
|
||||
|
||||
CategoryListQuery::CategoryListQuery() {
|
||||
|
||||
}
|
||||
|
||||
CategoryListQuery::~CategoryListQuery() {
|
||||
|
||||
}
|
||||
|
||||
bool CategoryListQuery::OnRun(Connection& db) {
|
||||
return false;
|
||||
}
|
20
src/musikbox/CategoryListQuery.h
Executable file
20
src/musikbox/CategoryListQuery.h
Executable 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);
|
||||
};
|
@ -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();
|
||||
|
@ -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
11
src/musikbox/IScrollable.h
Executable 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;
|
||||
};
|
@ -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");
|
||||
}
|
||||
|
@ -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
96
src/musikbox/MainLayout.cpp
Executable 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
37
src/musikbox/MainLayout.h
Executable 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;
|
||||
};
|
@ -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() {
|
||||
|
@ -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() {
|
||||
|
@ -27,6 +27,11 @@ void ScrollableWindow::OnAdapterChanged() {
|
||||
}
|
||||
}
|
||||
|
||||
void ScrollableWindow::Create() {
|
||||
Window::Create();
|
||||
this->OnAdapterChanged();
|
||||
}
|
||||
|
||||
size_t ScrollableWindow::GetFirstVisible() {
|
||||
return scrollPosition;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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: ");
|
||||
|
@ -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" />
|
||||
|
@ -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>
|
Loading…
x
Reference in New Issue
Block a user