From 3f368f17e456e884c25d8c9b87f0c58d2ce5213d Mon Sep 17 00:00:00 2001 From: emeric Date: Sun, 7 Dec 2014 19:43:22 +0100 Subject: [PATCH 1/8] [UI] PlayQueue on the left, added covers for each entry --- src/Makefile.am | 1 + src/cover/CoverArtGrabber.cpp | 19 +++++++ src/cover/CoverArtGrabber.hpp | 1 + src/ui/audio/Audio.cpp | 14 ++--- src/ui/audio/PlayQueue.cpp | 95 ++++++++++++++++++------------- src/ui/resource/CoverResource.cpp | 70 +++++++++++++++++++++++ src/ui/resource/CoverResource.hpp | 50 ++++++++++++++++ 7 files changed, 205 insertions(+), 45 deletions(-) create mode 100644 src/ui/resource/CoverResource.cpp create mode 100644 src/ui/resource/CoverResource.hpp diff --git a/src/Makefile.am b/src/Makefile.am index ca1124bc..6912e71a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -51,6 +51,7 @@ lms_SOURCES = \ $(srcdir)/ui/common/DirectoryValidator.cpp \ $(srcdir)/ui/common/SessionData.cpp \ $(srcdir)/ui/resource/AvConvTranscodeStreamResource.cpp \ + $(srcdir)/ui/resource/CoverResource.cpp \ $(srcdir)/ui/video/VideoWidget.cpp \ $(srcdir)/ui/video/VideoDatabaseWidget.cpp \ $(srcdir)/ui/video/VideoMediaPlayerWidget.cpp \ diff --git a/src/cover/CoverArtGrabber.cpp b/src/cover/CoverArtGrabber.cpp index 7dabf4c5..dccfaf77 100644 --- a/src/cover/CoverArtGrabber.cpp +++ b/src/cover/CoverArtGrabber.cpp @@ -52,6 +52,25 @@ Grabber::getFromInputFormatContext(const Av::InputFormatContext& input) return res; } +std::vector +Grabber::getFromTrack(const boost::filesystem::path& p) +{ + std::vector res; + + try + { + Av::InputFormatContext input(p); + + return getFromInputFormatContext(input); + + } + catch(std::exception& e) + { + LMS_LOG(MOD_COVER, SEV_ERROR) << "Cannot get pictures: " << e.what(); + } + + return res; +} diff --git a/src/cover/CoverArtGrabber.hpp b/src/cover/CoverArtGrabber.hpp index d9ce3a82..757356e7 100644 --- a/src/cover/CoverArtGrabber.hpp +++ b/src/cover/CoverArtGrabber.hpp @@ -36,6 +36,7 @@ class Grabber static std::vector getFromInputFormatContext(const Av::InputFormatContext& input); static std::vector getFromTrack(Database::Track::pointer track); + static std::vector getFromTrack(const boost::filesystem::path& path); }; } // namespace CoverArt diff --git a/src/ui/audio/Audio.cpp b/src/ui/audio/Audio.cpp index b26eb648..abcaa61d 100644 --- a/src/ui/audio/Audio.cpp +++ b/src/ui/audio/Audio.cpp @@ -61,7 +61,7 @@ _playQueue(nullptr) filterLayout->addWidget(filterRelease); _filterChain.addFilter(filterRelease); - mainLayout->addLayout(filterLayout, 0, 0); + mainLayout->addLayout(filterLayout, 0, 1); // ENDOF(Filters) // TODO ADD some stats (nb files, total duration, etc.) @@ -83,7 +83,7 @@ _playQueue(nullptr) trackLayout->addLayout(trackControls); - mainLayout->addLayout(trackLayout, 1, 0); + mainLayout->addLayout(trackLayout, 1, 1); _filterChain.addFilter(_trackView); @@ -142,15 +142,15 @@ _playQueue(nullptr) playQueueLayout->addWidget( _playQueue, 1); - mainLayout->addLayout(playQueueLayout, 0, 1, 2, 1); - } + _mediaPlayer = new AudioMediaPlayer(); + playQueueLayout->addWidget(_mediaPlayer); - _mediaPlayer = new AudioMediaPlayer(); - mainLayout->addWidget(_mediaPlayer, 2, 0, 1, 2); + mainLayout->addLayout(playQueueLayout, 0, 0, 2, 1); + } mainLayout->setRowStretch(1, 1); mainLayout->setRowResizable(0, true, Wt::WLength(200, Wt::WLength::Pixel)); - mainLayout->setColumnResizable(0, true); + mainLayout->setColumnResizable(0, true, Wt::WLength(400, Wt::WLength::Pixel)); // Double click on track // Set the selected tracks to the play queue diff --git a/src/ui/audio/PlayQueue.cpp b/src/ui/audio/PlayQueue.cpp index 2183b3c1..ab1eb83f 100644 --- a/src/ui/audio/PlayQueue.cpp +++ b/src/ui/audio/PlayQueue.cpp @@ -20,7 +20,12 @@ #include #include +#include +#include #include +#include + +#include "resource/CoverResource.hpp" #include "logger/Logger.hpp" @@ -28,12 +33,6 @@ namespace { - void modelForceRefreshDataRow(Wt::WStandardItemModel *model, int row) - { - for (int i = 0; i < model->columnCount(); ++i) - model->setData(row, i, model->data(row, i)); - } - void swapRows(Wt::WStandardItemModel *model, int row1, int row2) { // Copy column by column @@ -44,11 +43,21 @@ namespace { model->setData(row2, i, tmp); // tmp-> 2 } } - } namespace UserInterface { +static const int CoverRole = Wt::UserRole + 1; + +enum ColumnId +{ + COLUMN_ID_TRACK_ID = 0, + COLUMN_ID_POS = 1, + COLUMN_ID_COVER = 2, + COLUMN_ID_NAME = 3, + COLUMN_ID_DURATION = 4 +}; + static const int trackPosInvalid = -1; class TrackSelector @@ -90,7 +99,7 @@ _curPos(0) { } - void +void TrackSelector::refreshPositions() { _trackPos.clear(); @@ -200,55 +209,59 @@ TrackSelector::setSize(std::size_t size) class PlayQueueItemDelegate : public Wt::WItemDelegate { public: - PlayQueueItemDelegate(Wt::WObject *parent = 0) : Wt::WItemDelegate(parent), _selectedRowPos(trackPosInvalid) {} - - void setSelectedRowPos(int rowPos) { _selectedRowPos = rowPos; } + PlayQueueItemDelegate(Wt::WObject *parent = 0) : Wt::WItemDelegate(parent) {} Wt::WWidget* update(Wt::WWidget *widget, const Wt::WModelIndex &index, Wt::WFlags< Wt::ViewItemRenderFlag > flags) { - Wt::WWidget* res = Wt::WItemDelegate::update(widget, index, flags); + Wt::WWidget* res; - if (res && index.isValid()) + Wt::WString path = Wt::asString(index.data(CoverRole)); + if (!path.empty()) { - if (_selectedRowPos != trackPosInvalid && index.row() == _selectedRowPos) - res->toggleStyleClass("playqueue-playing", true); - else - res->toggleStyleClass("playqueue-playing", false); + // Create an image for this track + Wt::WImage *image = new Wt::WImage( ); + CoverResource *resource = new CoverResource(boost::filesystem::path(path.toUTF8()), 64, image); + image->setImageLink(Wt::WLink(resource)); + + res = image; + res->setObjectName("z"); } + else + res = Wt::WItemDelegate::update(widget, index, flags); return res; } private: - int _selectedRowPos; }; - PlayQueue::PlayQueue(Database::Handler& db, Wt::WContainerWidget* parent) : Wt::WTableView(parent), _db(db), _curPlayedTrackPos(trackPosInvalid), _trackSelector(new TrackSelector()) { - _model = new Wt::WStandardItemModel(0, 4, this); + _model = new Wt::WStandardItemModel(0, 5, this); // 0 Column is hidden (track id) - _model->setHeaderData(0, Wt::WString("#")); - _model->setHeaderData(1, Wt::WString("#")); - _model->setHeaderData(2, Wt::WString("Track")); - _model->setHeaderData(3, Wt::WString("Duration")); + _model->setHeaderData(COLUMN_ID_TRACK_ID, Wt::WString("#")); + _model->setHeaderData(COLUMN_ID_POS, Wt::WString("#")); + _model->setHeaderData(COLUMN_ID_COVER, Wt::WString("Cover")); + _model->setHeaderData(COLUMN_ID_NAME, Wt::WString("Track")); + _model->setHeaderData(COLUMN_ID_DURATION, Wt::WString("Duration")); this->setModel(_model); this->setSelectionMode(Wt::ExtendedSelection); this->setSortingEnabled(false); this->setAlternatingRowColors(true); + this->setRowHeight(64); + this->setColumnWidth(COLUMN_ID_COVER, 64); + this->setColumnWidth(COLUMN_ID_POS, 50); + this->setColumnWidth(COLUMN_ID_DURATION, 75); - this->setColumnWidth(0, 50); - this->setColumnWidth(1, 50); - - this->setColumnHidden(0, true); + this->setColumnHidden(COLUMN_ID_TRACK_ID, true); _itemDelegate = new PlayQueueItemDelegate(); this->setItemDelegate(_itemDelegate); @@ -318,10 +331,11 @@ PlayQueue::addTracks(const std::vector& trackIds) _model->insertRows(dataRow, 1); - _model->setData(dataRow, 0, track.id()); - _model->setData(dataRow, 1, dataRow + 1); - _model->setData(dataRow, 2, track->getArtistName() + " - " + track->getName()); - _model->setData(dataRow, 3, track->getDuration()); + _model->setData(dataRow, COLUMN_ID_TRACK_ID, track.id()); + _model->setData(dataRow, COLUMN_ID_POS, dataRow + 1); + _model->setData(dataRow, COLUMN_ID_COVER, track->getPath(), CoverRole); + _model->setData(dataRow, COLUMN_ID_NAME, track->getArtistName() + " - " + track->getName()); + _model->setData(dataRow, COLUMN_ID_DURATION, track->getDuration()); } } @@ -386,7 +400,7 @@ PlayQueue::readTrack(int rowPos) Wt::Dbo::Transaction transaction(_db.getSession()); LMS_LOG(MOD_UI, SEV_DEBUG) << "Reading track at pos " << rowPos; - Database::Track::id_type trackId = boost::any_cast(_model->data(rowPos, 0)); + Database::Track::id_type trackId = boost::any_cast(_model->data(rowPos, COLUMN_ID_TRACK_ID)); Database::Track::pointer track = Database::Track::getById(_db.getSession(), trackId); if (track) @@ -407,15 +421,21 @@ PlayQueue::setPlayingTrackPos(int newRowPos) int oldRowPos = _curPlayedTrackPos; _curPlayedTrackPos = newRowPos; - _itemDelegate->setSelectedRowPos(newRowPos); - // Hack re-set the data in order to trigger the rerending of the widget // calling the update method of our custom item delegate gives bad results if (oldRowPos >= 0) - modelForceRefreshDataRow(_model, oldRowPos); + { + _model->setData(oldRowPos, COLUMN_ID_POS, boost::any(), Wt::StyleClassRole); + _model->setData(oldRowPos, COLUMN_ID_NAME, boost::any(), Wt::StyleClassRole); + _model->setData(oldRowPos, COLUMN_ID_DURATION, boost::any(), Wt::StyleClassRole); + } if (newRowPos >= 0) - modelForceRefreshDataRow(_model, newRowPos); + { + _model->setData(newRowPos, COLUMN_ID_POS, "playqueue-playing", Wt::StyleClassRole); + _model->setData(newRowPos, COLUMN_ID_NAME, "playqueue-playing", Wt::StyleClassRole); + _model->setData(newRowPos, COLUMN_ID_DURATION, "playqueue-playing", Wt::StyleClassRole); + } } void @@ -432,7 +452,6 @@ PlayQueue::delSelected(void) } // If the current played track is removed, make sure to unselect it - _itemDelegate->setSelectedRowPos(trackPosInvalid); _trackSelector->setSize(_model->rowCount()); renumber(minId, _model->rowCount() - 1); diff --git a/src/ui/resource/CoverResource.cpp b/src/ui/resource/CoverResource.cpp new file mode 100644 index 00000000..d169278c --- /dev/null +++ b/src/ui/resource/CoverResource.cpp @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2014 Emeric Poupon + * + * This file is part of LMS. + * + * LMS is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * LMS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with LMS. If not, see . + */ + +#include + +#include + +#include "logger/Logger.hpp" + +#include "cover/CoverArtGrabber.hpp" + +#include "CoverResource.hpp" + +namespace UserInterface { + +CoverResource::CoverResource(const boost::filesystem::path& path, std::size_t size, Wt::WObject *parent) +: Wt::WResource(parent), +_path(path), +_size(size) +{ +} + +CoverResource:: ~CoverResource() +{ + beingDeleted(); +} + + +void +CoverResource::handleRequest(const Wt::Http::Request& request, Wt::Http::Response& response) +{ + if (_data.empty()) + { + std::vector covers = CoverArt::Grabber::getFromTrack( _path ); + + BOOST_FOREACH(CoverArt::CoverArt& cover, covers) + { + cover.scale(_size); + _data = cover.getData(); + _mimeType = cover.getMimeType(); + break; + } + + // TODO if not found fallback on an empty image + } + + response.setMimeType(_mimeType); + + for (unsigned int i = 0; i < _data.size(); ++i) + response.out().put(_data[i]); +} + + +} // namespace UserInterface diff --git a/src/ui/resource/CoverResource.hpp b/src/ui/resource/CoverResource.hpp new file mode 100644 index 00000000..a8554da9 --- /dev/null +++ b/src/ui/resource/CoverResource.hpp @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2014 Emeric Poupon + * + * This file is part of LMS. + * + * LMS is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * LMS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with LMS. If not, see . + */ + +#include +#include + +#include + +#include "database/AudioTypes.hpp" + +namespace UserInterface { + +class CoverResource : public Wt::WResource +{ + public: + CoverResource(const boost::filesystem::path& p, // track to get cover from + std::size_t size, // size * size pixels + Wt::WObject *parent = 0); + ~CoverResource(); + + void handleRequest(const Wt::Http::Request& request, Wt::Http::Response& response); + + private: + + boost::filesystem::path _path; + std::size_t _size; + std::string _mimeType; + + std::vector _data; +}; + + + +} // namespace UserInterface From a341c3e50849b18812e7e9ca82f594ae98666683 Mon Sep 17 00:00:00 2001 From: emeric Date: Tue, 9 Dec 2014 12:38:47 +0100 Subject: [PATCH 2/8] [UI] Workaround to make covers move in the playqueue --- src/ui/audio/PlayQueue.cpp | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/src/ui/audio/PlayQueue.cpp b/src/ui/audio/PlayQueue.cpp index ab1eb83f..3e13f5cc 100644 --- a/src/ui/audio/PlayQueue.cpp +++ b/src/ui/audio/PlayQueue.cpp @@ -31,24 +31,38 @@ #include "PlayQueue.hpp" +static const int CoverRole = Wt::UserRole + 1; + namespace { void swapRows(Wt::WStandardItemModel *model, int row1, int row2) { - // Copy column by column + LMS_LOG(MOD_UI, SEV_DEBUG) << "Swap called row1 = " << row1 << ", row2 = " << row2 << ", CoverRole = " << CoverRole; + + // Swap data column by column for (int i = 0; i < model->columnCount(); ++i) { - boost::any tmp = model->data(row1, i); // 1 -> tmp - model->setData(row1, i, model->data(row2, i)); // 2 -> 1 - model->setData(row2, i, tmp); // tmp-> 2 + Wt::WModelIndex index1 = model->index(row1, i); + Wt::WModelIndex index2 = model->index(row2, i); + + { + auto tmp = model->itemData(index1); // 1 -> tmp + model->setItemData(index1, model->itemData(index2)); // 2 -> 1 + model->setItemData(index2, tmp); // tmp-> 2 + } + + // Workaround, do the same thing for the CoverRole + { + auto tmp = model->data(index1, CoverRole); + model->setData(index1, model->data(index2, CoverRole), CoverRole); + model->setData(index2, tmp, CoverRole); + } } } } namespace UserInterface { -static const int CoverRole = Wt::UserRole + 1; - enum ColumnId { COLUMN_ID_TRACK_ID = 0, @@ -215,9 +229,13 @@ class PlayQueueItemDelegate : public Wt::WItemDelegate { Wt::WWidget* res; + LMS_LOG(MOD_UI, SEV_DEBUG) << "Update called! widget = " << widget << ", row = " << index.row() << ", col = " << index.column(); + Wt::WString path = Wt::asString(index.data(CoverRole)); if (!path.empty()) { + LMS_LOG(MOD_UI, SEV_DEBUG) << "Path = " << path ; + // Create an image for this track Wt::WImage *image = new Wt::WImage( ); CoverResource *resource = new CoverResource(boost::filesystem::path(path.toUTF8()), 64, image); @@ -331,7 +349,7 @@ PlayQueue::addTracks(const std::vector& trackIds) _model->insertRows(dataRow, 1); - _model->setData(dataRow, COLUMN_ID_TRACK_ID, track.id()); + _model->setData(dataRow, COLUMN_ID_TRACK_ID, track.id(), Wt::UserRole); _model->setData(dataRow, COLUMN_ID_POS, dataRow + 1); _model->setData(dataRow, COLUMN_ID_COVER, track->getPath(), CoverRole); _model->setData(dataRow, COLUMN_ID_NAME, track->getArtistName() + " - " + track->getName()); @@ -400,7 +418,7 @@ PlayQueue::readTrack(int rowPos) Wt::Dbo::Transaction transaction(_db.getSession()); LMS_LOG(MOD_UI, SEV_DEBUG) << "Reading track at pos " << rowPos; - Database::Track::id_type trackId = boost::any_cast(_model->data(rowPos, COLUMN_ID_TRACK_ID)); + Database::Track::id_type trackId = boost::any_cast(_model->data(rowPos, COLUMN_ID_TRACK_ID, Wt::UserRole)); Database::Track::pointer track = Database::Track::getById(_db.getSession(), trackId); if (track) From 5dc7818c124093147b34361a5528d5b4e8e9ea38 Mon Sep 17 00:00:00 2001 From: emeric Date: Tue, 9 Dec 2014 12:44:57 +0100 Subject: [PATCH 3/8] [UI] Removed margins from the bottom of the nav bar --- src/ui/docroot/css/lms.css | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/ui/docroot/css/lms.css b/src/ui/docroot/css/lms.css index 48f18b91..791cd461 100644 --- a/src/ui/docroot/css/lms.css +++ b/src/ui/docroot/css/lms.css @@ -1,5 +1,8 @@ - -.playqueue-playing { - font-weight: bold; +.main-nav { + margin-bottom: 0px; +} + +.playqueue-playing { + font-weight: bold; } From c5937ead52cf09ef4fb48d426cd5ee58af890ed8 Mon Sep 17 00:00:00 2001 From: emeric Date: Tue, 9 Dec 2014 13:00:10 +0100 Subject: [PATCH 4/8] [UI] Added left padding in the setings menu --- src/ui/docroot/css/lms.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ui/docroot/css/lms.css b/src/ui/docroot/css/lms.css index 791cd461..605eaac6 100644 --- a/src/ui/docroot/css/lms.css +++ b/src/ui/docroot/css/lms.css @@ -2,6 +2,10 @@ margin-bottom: 0px; } +div.contents { + padding: 0px 12px 6px; +} + .playqueue-playing { font-weight: bold; } From a03a890f5bc9b4c08ef9603d1bcadacc1a0a936b Mon Sep 17 00:00:00 2001 From: emeric Date: Sat, 13 Dec 2014 18:21:57 +0100 Subject: [PATCH 5/8] [UI] Various improvments in audio view --- src/av/InputFormatContext.cpp | 13 +++++++++ src/av/InputFormatContext.hpp | 1 + src/database-updater/DatabaseUpdater.cpp | 5 ++++ src/database/AudioTypes.hpp | 4 +++ src/database/Track.cpp | 3 ++- src/metadata/AvFormat.cpp | 3 +++ src/metadata/MetaData.hpp | 8 +----- src/ui/audio/AudioMediaPlayer.cpp | 11 +++++--- src/ui/audio/PlayQueue.cpp | 33 +++++++++++++---------- src/ui/audio/TableFilter.cpp | 13 +++++---- src/ui/docroot/css/lms.css | 13 +++++++++ src/ui/docroot/images/unknown-cover.jpg | Bin 0 -> 1120 bytes src/ui/settings/Settings.cpp | 1 + 13 files changed, 78 insertions(+), 30 deletions(-) create mode 100755 src/ui/docroot/images/unknown-cover.jpg diff --git a/src/av/InputFormatContext.cpp b/src/av/InputFormatContext.cpp index 346702fe..6fcdb432 100644 --- a/src/av/InputFormatContext.cpp +++ b/src/av/InputFormatContext.cpp @@ -114,6 +114,19 @@ InputFormatContext::getMetadata(void) } +std::size_t +InputFormatContext::getNbPictures(void) const +{ + std::size_t res = 0; + + for (std::size_t i = 0; i < native()->nb_streams; ++i) + { + if (native()->streams[i]->disposition & AV_DISPOSITION_ATTACHED_PIC) + ++res; + } + return res; +} + void InputFormatContext::getPictures(std::vector< std::vector >& pictures) const { diff --git a/src/av/InputFormatContext.hpp b/src/av/InputFormatContext.hpp index 11fe1c89..91bb2725 100644 --- a/src/av/InputFormatContext.hpp +++ b/src/av/InputFormatContext.hpp @@ -46,6 +46,7 @@ class InputFormatContext : public FormatContext // Get attached pictures + std::size_t getNbPictures(void) const; void getPictures(std::vector< std::vector >& pictures) const; // Get the streams diff --git a/src/database-updater/DatabaseUpdater.cpp b/src/database-updater/DatabaseUpdater.cpp index 22b7e530..37c1c196 100644 --- a/src/database-updater/DatabaseUpdater.cpp +++ b/src/database-updater/DatabaseUpdater.cpp @@ -402,6 +402,11 @@ Updater::processAudioFile( const boost::filesystem::path& file, Stats& stats) track.modify()->setDate( boost::any_cast(items[MetaData::Type::OriginalDate]) ); } + if (items.find(MetaData::Type::HasCover) != items.end()) + { + track.modify()->setHasCover( boost::any_cast(items[MetaData::Type::HasCover])); + } + transaction.commit(); } diff --git a/src/database/AudioTypes.hpp b/src/database/AudioTypes.hpp index ddc21bf6..08ccc1d5 100644 --- a/src/database/AudioTypes.hpp +++ b/src/database/AudioTypes.hpp @@ -117,6 +117,7 @@ class Track void setOriginalDate(const boost::posix_time::ptime& date) { _originalDate = date; } void setGenres(const std::string& genreList) { _genreList = genreList; } void setGenres(std::vector genres); + void setHasCover(bool hasCover) { _hasCover = hasCover; } int getTrackNumber(void) const { return _trackNumber; } int getDiscNumber(void) const { return _discNumber; } @@ -132,6 +133,7 @@ class Track boost::posix_time::ptime getLastWriteTime(void) const { return _fileLastWrite; } const std::vector& getChecksum(void) const { return _fileChecksum; } + bool hasCover(void) const { return _hasCover; } template void persist(Action& a) @@ -148,6 +150,7 @@ class Track Wt::Dbo::field(a, _filePath, "path"); Wt::Dbo::field(a, _fileLastWrite, "last_write"); Wt::Dbo::field(a, _fileChecksum, "checksum"); + Wt::Dbo::field(a, _hasCover, "has_cover"); Wt::Dbo::hasMany(a, _genres, Wt::Dbo::ManyToMany, "track_genre", "", Wt::Dbo::OnDeleteCascade); } @@ -167,6 +170,7 @@ class Track std::string _filePath; std::vector _fileChecksum; boost::posix_time::ptime _fileLastWrite; + bool _hasCover; Wt::Dbo::collection< Genre::pointer > _genres; // Tracks that belong to this genre }; diff --git a/src/database/Track.cpp b/src/database/Track.cpp index d0e966cb..f2df60e2 100644 --- a/src/database/Track.cpp +++ b/src/database/Track.cpp @@ -29,7 +29,8 @@ Track::Track(const boost::filesystem::path& p) : _trackNumber(0), _discNumber(0), -_filePath( p.string() ) +_filePath( p.string() ), +_hasCover(false) { } diff --git a/src/metadata/AvFormat.cpp b/src/metadata/AvFormat.cpp index d7d9d87f..821d6191 100644 --- a/src/metadata/AvFormat.cpp +++ b/src/metadata/AvFormat.cpp @@ -114,6 +114,9 @@ AvFormat::parse(const boost::filesystem::path& p, Items& items) // Duration items.insert( std::make_pair(MetaData::Type::Duration, boost::posix_time::time_duration( boost::posix_time::seconds( input.getDurationSecs() )) )); + // Cover + items.insert( std::make_pair(MetaData::Type::HasCover, input.getNbPictures() > 0)); + // Embedded MetaData // Make sure to convert strings into UTF-8 std::map::const_iterator it; diff --git a/src/metadata/MetaData.hpp b/src/metadata/MetaData.hpp index 93fc1154..07dec04d 100644 --- a/src/metadata/MetaData.hpp +++ b/src/metadata/MetaData.hpp @@ -39,18 +39,12 @@ namespace MetaData DiscNumber, // size_t Date, // boost::posix_time::ptime OriginalDate, // boost::posix_time::ptime - Cover, // GenericData + HasCover, // bool AudioStreams, // vector VideoStreams, // vector SubtitleStreams, // vector }; - // Used by Cover - struct GenericData { - std::string mimeType; - std::vector data; - }; - // Used by Streams struct AudioStream { std::size_t nbChannels; diff --git a/src/ui/audio/AudioMediaPlayer.cpp b/src/ui/audio/AudioMediaPlayer.cpp index c3308062..7efba7f8 100644 --- a/src/ui/audio/AudioMediaPlayer.cpp +++ b/src/ui/audio/AudioMediaPlayer.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include "AudioMediaPlayer.hpp" @@ -30,6 +31,9 @@ AudioMediaPlayer::AudioMediaPlayer( Wt::WContainerWidget *parent) : Wt::WContainerWidget(parent), _mediaResource(nullptr) { + + this->setStyleClass("mediaplayer"); + _mediaPlayer = new Wt::WMediaPlayer( Wt::WMediaPlayer::Audio, this ); // _mediaPlayer->setAlternativeContent (new Wt::WText("You don't have HTML5 audio support!")); // _mediaPlayer->setOptions( Wt::WMediaPlayer::Autoplay ); @@ -52,14 +56,15 @@ AudioMediaPlayer::AudioMediaPlayer( Wt::WContainerWidget *parent) _shuffle.emit( shuffle->checkState() == Wt::Checked); })); + _curTime = new Wt::WText("00:00:00", container); + _timeSlider = new Wt::WSlider( container ); + _duration = new Wt::WText("00:00:00", container); + Wt::WPushButton *prevBtn = new Wt::WPushButton("<<", container ); _playBtn = new Wt::WPushButton("Play", container ); _pauseBtn = new Wt::WPushButton("Pause", container ); Wt::WPushButton *nextBtn = new Wt::WPushButton(">>", container ); - _curTime = new Wt::WText(container); - _timeSlider = new Wt::WSlider( container ); - _duration = new Wt::WText(container); _volumeSlider = new Wt::WSlider( container ); _volumeSlider->setRange(0,100); diff --git a/src/ui/audio/PlayQueue.cpp b/src/ui/audio/PlayQueue.cpp index 3e13f5cc..8ea21ac7 100644 --- a/src/ui/audio/PlayQueue.cpp +++ b/src/ui/audio/PlayQueue.cpp @@ -20,10 +20,12 @@ #include #include +#include #include #include #include #include +#include #include "resource/CoverResource.hpp" @@ -82,8 +84,8 @@ class TrackSelector void setShuffle(bool enable); void setLoop(bool enable) { _loop = enable; } - int getPrevious(void); - int getNext(void); + int previous(void); + int next(void); int getCurrent(void); // Set the internal pos thanks to the track pos @@ -125,7 +127,7 @@ TrackSelector::refreshPositions() _trackPos.push_back(i); // Now shuffle - // Perform size/2 permutations + // Source: http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle boost::random::mt19937 rng; // produces randomness out of thin air rng.seed(static_cast(std::time(0))); @@ -157,7 +159,7 @@ TrackSelector::setShuffle(bool enable) } int -TrackSelector::getNext() +TrackSelector::next() { if (_size == 0) return trackPosInvalid; @@ -176,7 +178,7 @@ TrackSelector::getNext() } int -TrackSelector::getPrevious() +TrackSelector::previous() { if (_size == 0) return trackPosInvalid; @@ -229,16 +231,17 @@ class PlayQueueItemDelegate : public Wt::WItemDelegate { Wt::WWidget* res; - LMS_LOG(MOD_UI, SEV_DEBUG) << "Update called! widget = " << widget << ", row = " << index.row() << ", col = " << index.column(); - Wt::WString path = Wt::asString(index.data(CoverRole)); if (!path.empty()) { - LMS_LOG(MOD_UI, SEV_DEBUG) << "Path = " << path ; - // Create an image for this track - Wt::WImage *image = new Wt::WImage( ); - CoverResource *resource = new CoverResource(boost::filesystem::path(path.toUTF8()), 64, image); + Wt::WImage* image = new Wt::WImage( ); + Wt::WResource* resource; + if (path != "none") + resource = new CoverResource(boost::filesystem::path(path.toUTF8()), 64, image); + else + resource = new Wt::WFileResource("image/jpeg", Wt::WApplication::instance()->docRoot() + "/images/unknown-cover.jpg", image); + image->setImageLink(Wt::WLink(resource)); res = image; @@ -351,7 +354,7 @@ PlayQueue::addTracks(const std::vector& trackIds) _model->setData(dataRow, COLUMN_ID_TRACK_ID, track.id(), Wt::UserRole); _model->setData(dataRow, COLUMN_ID_POS, dataRow + 1); - _model->setData(dataRow, COLUMN_ID_COVER, track->getPath(), CoverRole); + _model->setData(dataRow, COLUMN_ID_COVER, track->hasCover() ? track->getPath() : "none", CoverRole); _model->setData(dataRow, COLUMN_ID_NAME, track->getArtistName() + " - " + track->getName()); _model->setData(dataRow, COLUMN_ID_DURATION, track->getDuration()); } @@ -383,7 +386,7 @@ PlayQueue::playNext(void) while (nbTries > 0) { - int pos = _trackSelector->getNext(); + int pos = _trackSelector->next(); if (pos == trackPosInvalid) break; @@ -401,7 +404,7 @@ PlayQueue::playPrevious(void) while (nbTries > 0) { - int pos = _trackSelector->getPrevious(); + int pos = _trackSelector->previous(); if (pos == trackPosInvalid) break; @@ -427,6 +430,8 @@ PlayQueue::readTrack(int rowPos) _sigTrackPlay.emit(track->getPath()); + this->scrollTo( _model->index(_trackSelector->getCurrent(), 0)); + return true; } else diff --git a/src/ui/audio/TableFilter.cpp b/src/ui/audio/TableFilter.cpp index 4c5fbf6d..5a521918 100644 --- a/src/ui/audio/TableFilter.cpp +++ b/src/ui/audio/TableFilter.cpp @@ -41,6 +41,8 @@ _field(field) this->setAlternatingRowColors(true); this->setModel(&_queryModel); + this->setColumnWidth(1, 50); + this->selectionChanged().connect(this, &TableFilter::emitUpdate); setLayoutSizeAware(true); @@ -66,14 +68,15 @@ _field(field) void TableFilter::layoutSizeChanged (int width, int height) { - LMS_LOG(MOD_UI, SEV_DEBUG) << "LAYOUT CHANGED!"; + LMS_LOG(MOD_UI, SEV_DEBUG) << "LAYOUT CHANGED, new width = " << width; - /* TODO - std::size_t trackColumnSize = this->columnWidth(1).toPixels() + 30 ; + LMS_LOG(MOD_UI, SEV_DEBUG) << "Before: sizes = " << this->columnWidth(0).toPixels() << ", " << this->columnWidth(1).toPixels(); + + std::size_t trackColumnSize = this->columnWidth(1).toPixels(); // Set the remaining size for the name column - this->setColumnWidth(0, width - 7 - trackColumnSize); - */ + this->setColumnWidth(0, width - 7 - trackColumnSize - 23 /*Asc wdth */); + LMS_LOG(MOD_UI, SEV_DEBUG) << "After: sizes = " << this->columnWidth(0).toPixels() << ", " << this->columnWidth(1).toPixels(); } // Set constraints on this filter diff --git a/src/ui/docroot/css/lms.css b/src/ui/docroot/css/lms.css index 605eaac6..131ffa8d 100644 --- a/src/ui/docroot/css/lms.css +++ b/src/ui/docroot/css/lms.css @@ -2,6 +2,14 @@ margin-bottom: 0px; } +.Wt-hrh2 { + background-color: white; +} + +.Wt-vrh2 { + background-color: white; +} + div.contents { padding: 0px 12px 6px; } @@ -10,3 +18,8 @@ div.contents { font-weight: bold; } +.mediaplayer { + background-color: #ccc; + border-radius: 15px; +} + diff --git a/src/ui/docroot/images/unknown-cover.jpg b/src/ui/docroot/images/unknown-cover.jpg new file mode 100755 index 0000000000000000000000000000000000000000..19ffc70603848bfc9ccb2ba7a57b3e53f3c24ffe GIT binary patch literal 1120 zcmex=J{_I=b4Ls^MSn`pH6GNTmDdP z?|I+tHyG9>&Q93ivSd^73D!R*GH18Gy^(+Iim>?6^%8p|^DUHrPyQtMCD?IM!VCA< zf2F6p*{58(!4sjUJ1MEAW{Vs9kLSi*4Eca;sdE zFGuEGJE!wnq+n+Ci6xxUC#EYGUypsea{E%vsqWFWC-;g!wydo8*$=fBrzRD%0FJN3zx)WtQ>o8Fo^L$tv#wUS8|7S9>B@|de5|Iq_9NeN$0SGIyy;6`7|lrD zdhPkOnH3fl#g7#Nm{@y{?|jbwL7Q7Owp8qNz@;ER)40$>=X{%{`_J6?Pi^XxqpNyX zzVFzeZg48?@X|fe#cm#d59epUt&ch*8zH2!YeJ5r*jB%f)nR87TCC0QT{%7z_ zRNb@WZB^#Oc`Ap}Boa-6l)Rd!Wz5{_eB;-@+`^?w{+f0!>udJwANK8zyr=EO+S28< zf&EH$cn_D#^ofPKKC}E%XFBu5*}P%6DUucOP4=wLk6vZt%>rk<1#e8QTBoBT;1?M% z{V9XBVcPbc*A}nWytK(@(>rS^`-2_FVp5J|xMbMH9Qo&}*SZO+XWIy(ig z=&Y;p)Uhvo`8t7N|56j4_e=hnK0H|{^Rq2#`t-^~>HWX;*B4&DU(Pjk$rr9!Kax6n zB-&UE%oKVZ7yXQiXpR!go%nR;ME#Bi&fG+;%IMeEpI3?OQBlr5+^^{F-aX^5qkPxm z6SFSLyT9MYKHoj~PDo8%bV~@&fy{U+q6X4ZN7! jxT9Vr(de;Z2Sc<;;D#%*hvfq=l-#eKnH923`TtD-N!6}S literal 0 HcmV?d00001 diff --git a/src/ui/settings/Settings.cpp b/src/ui/settings/Settings.cpp index 2f8d4515..4ccc57e1 100644 --- a/src/ui/settings/Settings.cpp +++ b/src/ui/settings/Settings.cpp @@ -51,6 +51,7 @@ _sessionData(sessionData) contents->setStyleClass("contents"); contents->setOverflow(WContainerWidget::OverflowHidden); + // TODO menu style on hover Wt::WMenu *menu = new Wt::WMenu(contents, Wt::Vertical); menu->setStyleClass("nav nav-pills nav-stacked submenu"); menu->setWidth(150); From d1c4678d82f2c3b7c233f580bee9c5a8c4c42d49 Mon Sep 17 00:00:00 2001 From: emeric Date: Sun, 14 Dec 2014 17:06:12 +0100 Subject: [PATCH 6/8] [UI] corrected segfault when enabling shuffle on en empty play queue --- src/ui/audio/PlayQueue.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/ui/audio/PlayQueue.cpp b/src/ui/audio/PlayQueue.cpp index 8ea21ac7..3e17d9ef 100644 --- a/src/ui/audio/PlayQueue.cpp +++ b/src/ui/audio/PlayQueue.cpp @@ -111,6 +111,7 @@ class TrackSelector TrackSelector::TrackSelector() : _loop(false), _shuffle(false), +_size(0), _curPos(0) { } @@ -152,10 +153,14 @@ TrackSelector::setShuffle(bool enable) { _shuffle = enable; - if (_shuffle) - setPosByRowId(_curPos); - else - setPos(_trackPos[_curPos]); + if (_size != 0) + { + // Update the current index we are playing if we switch shuffle + if (_shuffle) + setPosByRowId(_curPos); + else + setPos(_trackPos[_curPos]); + } } int From 799d7a4b32db68242edf13091e968c257a7900a0 Mon Sep 17 00:00:00 2001 From: emeric Date: Sun, 14 Dec 2014 17:09:29 +0100 Subject: [PATCH 7/8] [UI] Slightly better looking MediaPlayer --- src/ui/approot/templates.xml | 10 +++ src/ui/audio/Audio.cpp | 10 +-- src/ui/audio/AudioMediaPlayer.cpp | 134 ++++++++++++++++++------------ src/ui/audio/TableFilter.cpp | 2 +- src/ui/docroot/css/lms.css | 2 +- 5 files changed, 97 insertions(+), 61 deletions(-) diff --git a/src/ui/approot/templates.xml b/src/ui/approot/templates.xml index f4feb002..4a635a85 100644 --- a/src/ui/approot/templates.xml +++ b/src/ui/approot/templates.xml @@ -308,6 +308,16 @@ ${apply-info} + + +
+ ${prev} + ${play} + ${pause} + ${next} +
+
+ diff --git a/src/ui/audio/Audio.cpp b/src/ui/audio/Audio.cpp index abcaa61d..766f880b 100644 --- a/src/ui/audio/Audio.cpp +++ b/src/ui/audio/Audio.cpp @@ -96,6 +96,11 @@ _playQueue(nullptr) Wt::WVBoxLayout* playQueueLayout = new Wt::WVBoxLayout(); + _mediaPlayer = new AudioMediaPlayer(); + playQueueLayout->addWidget(_mediaPlayer); + + playQueueLayout->addWidget( _playQueue, 1); + Wt::WHBoxLayout* playlistControls = new Wt::WHBoxLayout(); Wt::WPushButton *saveBtn = new Wt::WPushButton("Save"); @@ -140,11 +145,6 @@ _playQueue(nullptr) playQueueLayout->addLayout(playlistControls); - playQueueLayout->addWidget( _playQueue, 1); - - _mediaPlayer = new AudioMediaPlayer(); - playQueueLayout->addWidget(_mediaPlayer); - mainLayout->addLayout(playQueueLayout, 0, 0, 2, 1); } diff --git a/src/ui/audio/AudioMediaPlayer.cpp b/src/ui/audio/AudioMediaPlayer.cpp index 7efba7f8..610c59f2 100644 --- a/src/ui/audio/AudioMediaPlayer.cpp +++ b/src/ui/audio/AudioMediaPlayer.cpp @@ -17,11 +17,12 @@ * along with LMS. If not, see . */ - #include #include #include #include +#include +#include #include "AudioMediaPlayer.hpp" @@ -31,69 +32,94 @@ AudioMediaPlayer::AudioMediaPlayer( Wt::WContainerWidget *parent) : Wt::WContainerWidget(parent), _mediaResource(nullptr) { - this->setStyleClass("mediaplayer"); + this->setHeight(100); + + Wt::WContainerWidget *sliderContainer = new Wt::WContainerWidget(this); + Wt::WHBoxLayout *sliderLayout = new Wt::WHBoxLayout(); + sliderContainer->setLayout(sliderLayout); + + sliderLayout->addWidget(_curTime = new Wt::WText("00:00:00")); + _curTime->setLineHeight(30); + sliderLayout->addWidget(_timeSlider = new Wt::WSlider( ), 1); + _timeSlider->setHeight(30); + sliderLayout->addWidget(_duration = new Wt::WText("00:00:00")); + _duration->setLineHeight(30); + + Wt::WContainerWidget *controlsContainer = new Wt::WContainerWidget(this); + Wt::WHBoxLayout *controlsLayout = new Wt::WHBoxLayout(); + controlsContainer->setLayout(controlsLayout); + + Wt::WContainerWidget *btnContainer = new Wt::WContainerWidget(); + + Wt::WTemplate *t = new Wt::WTemplate(Wt::WString::tr("mediaplayer-controls"), btnContainer); + + Wt::WPushButton *prevBtn = new Wt::WPushButton("<<"); + t->bindWidget("prev", prevBtn); + + _playBtn = new Wt::WPushButton("Play"); + t->bindWidget("play", _playBtn); + _playBtn->setWidth(70); + + _pauseBtn = new Wt::WPushButton("Pause"); + t->bindWidget("pause", _pauseBtn); + _pauseBtn->setWidth(70); + + Wt::WPushButton *nextBtn = new Wt::WPushButton(">>"); + t->bindWidget("next", nextBtn); + + controlsLayout->addWidget(btnContainer); + + _volumeSlider = new Wt::WSlider(); + _volumeSlider->setRange(0,100); + _volumeSlider->setWidth(60); + _volumeSlider->setMinimumSize(60, Wt::WLength::Auto); + _volumeSlider->setHeight(30); + controlsLayout->addWidget(_volumeSlider); + +/* Wt::WText* name = new Wt::WText("My track name"); + controlsLayout->addWidget(name, 1);*/ + + Wt::WCheckBox *loop = new Wt::WCheckBox("Loop", controlsContainer); + loop->changed().connect(std::bind([=] () + { + _loop.emit( loop->checkState() == Wt::Checked); + })); + + Wt::WCheckBox *shuffle = new Wt::WCheckBox("Shuffle", controlsContainer); + shuffle->changed().connect(std::bind([=] () + { + _shuffle.emit( shuffle->checkState() == Wt::Checked); + })); + + controlsLayout->addWidget(loop); + controlsLayout->addWidget(shuffle); _mediaPlayer = new Wt::WMediaPlayer( Wt::WMediaPlayer::Audio, this ); -// _mediaPlayer->setAlternativeContent (new Wt::WText("You don't have HTML5 audio support!")); -// _mediaPlayer->setOptions( Wt::WMediaPlayer::Autoplay ); _mediaPlayer->addSource( Wt::WMediaPlayer::OGA, "" ); - _mediaPlayer->ended().connect(this, &AudioMediaPlayer::handleTrackEnded); - { - Wt::WContainerWidget *container = new Wt::WContainerWidget(this); + _mediaPlayer->setControlsWidget( 0 ); + _mediaPlayer->setButton(Wt::WMediaPlayer::Play, _playBtn); + _mediaPlayer->setButton(Wt::WMediaPlayer::Pause, _pauseBtn); - Wt::WCheckBox *loop = new Wt::WCheckBox("Loop", this); - loop->changed().connect(std::bind([=] () - { - _loop.emit( loop->checkState() == Wt::Checked); - })); + _mediaPlayer->timeUpdated().connect(this, &AudioMediaPlayer::handleTimeUpdated); - Wt::WCheckBox *shuffle = new Wt::WCheckBox("Shuffle", this); - shuffle->changed().connect(std::bind([=] () - { - _shuffle.emit( shuffle->checkState() == Wt::Checked); - })); + _volumeSlider->setValue(_mediaPlayer->volume() * 100); - _curTime = new Wt::WText("00:00:00", container); - _timeSlider = new Wt::WSlider( container ); - _duration = new Wt::WText("00:00:00", container); + nextBtn->clicked().connect(std::bind([=] () + { + _mediaPlayer->stop(); + _playNext.emit(); + })); - Wt::WPushButton *prevBtn = new Wt::WPushButton("<<", container ); - _playBtn = new Wt::WPushButton("Play", container ); - _pauseBtn = new Wt::WPushButton("Pause", container ); - Wt::WPushButton *nextBtn = new Wt::WPushButton(">>", container ); - - - _volumeSlider = new Wt::WSlider( container ); - _volumeSlider->setRange(0,100); - _volumeSlider->setValue(_mediaPlayer->volume() * 100); - - _mediaPlayer->setControlsWidget( container ); - _mediaPlayer->setButton(Wt::WMediaPlayer::Play, _playBtn); - _mediaPlayer->setButton(Wt::WMediaPlayer::Pause, _pauseBtn); - - _mediaPlayer->setText( Wt::WMediaPlayer::CurrentTime, _curTime); - _mediaPlayer->setText( Wt::WMediaPlayer::Duration, _duration); - - _mediaPlayer->timeUpdated().connect(this, &AudioMediaPlayer::handleTimeUpdated); - - nextBtn->clicked().connect(std::bind([=] () - { - _mediaPlayer->stop(); - _playNext.emit(); - })); - - prevBtn->clicked().connect(std::bind([=] () - { - _mediaPlayer->stop(); - _playPrevious.emit(); - })); - } - - _timeSlider->valueChanged().connect(this, &AudioMediaPlayer::handlePlayOffset); - _timeSlider->sliderMoved().connect(this, &AudioMediaPlayer::handleSliderMoved); + prevBtn->clicked().connect(std::bind([=] () + { + _mediaPlayer->stop(); + _playPrevious.emit(); + })); + _timeSlider->valueChanged().connect(this, &AudioMediaPlayer::handlePlayOffset); +// _timeSlider->sliderMoved().connect(this, &AudioMediaPlayer::handleSliderMoved); _timeSlider->setDisabled(true); _volumeSlider->sliderMoved().connect(this, &AudioMediaPlayer::handleVolumeSliderMoved); diff --git a/src/ui/audio/TableFilter.cpp b/src/ui/audio/TableFilter.cpp index 5a521918..b2c156db 100644 --- a/src/ui/audio/TableFilter.cpp +++ b/src/ui/audio/TableFilter.cpp @@ -74,7 +74,7 @@ TableFilter::layoutSizeChanged (int width, int height) std::size_t trackColumnSize = this->columnWidth(1).toPixels(); // Set the remaining size for the name column - this->setColumnWidth(0, width - 7 - trackColumnSize - 23 /*Asc wdth */); + this->setColumnWidth(0, width - trackColumnSize - (7 * 2) - 2); LMS_LOG(MOD_UI, SEV_DEBUG) << "After: sizes = " << this->columnWidth(0).toPixels() << ", " << this->columnWidth(1).toPixels(); } diff --git a/src/ui/docroot/css/lms.css b/src/ui/docroot/css/lms.css index 131ffa8d..1b5ed3f8 100644 --- a/src/ui/docroot/css/lms.css +++ b/src/ui/docroot/css/lms.css @@ -20,6 +20,6 @@ div.contents { .mediaplayer { background-color: #ccc; - border-radius: 15px; + border-radius: 10px; } From 4517e5d2acef9a2ad2671fd78dfd9fb242a97dae Mon Sep 17 00:00:00 2001 From: emeric Date: Sun, 14 Dec 2014 17:50:35 +0100 Subject: [PATCH 8/8] [UI] minor source reorg --- src/ui/LmsApplication.cpp | 38 ++++++++++++++++++++++++-------------- src/ui/LmsApplication.hpp | 2 ++ 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/src/ui/LmsApplication.cpp b/src/ui/LmsApplication.cpp index a4328edc..ad803ae6 100644 --- a/src/ui/LmsApplication.cpp +++ b/src/ui/LmsApplication.cpp @@ -92,25 +92,35 @@ LmsApplication::LmsApplication(const Wt::WEnvironment& env, boost::filesystem::p // If here is no account in the database, launch the first connection wizard if (firstConnection) - { - // Hack, use the auth widget builtin strings - builtinLocalizedStrings().useBuiltin(skeletons::AuthStrings_xml1); - - root()->addWidget( new Settings::FirstConnectionFormView(_sessionData)); - } + createFirstConnectionUI(); else - { - _sessionData.getDatabaseHandler().getLogin().changed().connect(this, &LmsApplication::handleAuthEvent); + createLmsUI(); - LmsAuth *authWidget = new LmsAuth(_sessionData.getDatabaseHandler()); +} - authWidget->model()->addPasswordAuth(&Database::Handler::getPasswordService()); - authWidget->setRegistrationEnabled(false); +void +LmsApplication::createFirstConnectionUI() +{ + // Hack, use the auth widget builtin strings + builtinLocalizedStrings().useBuiltin(skeletons::AuthStrings_xml1); - authWidget->processEnvironment(); + root()->addWidget( new Settings::FirstConnectionFormView(_sessionData)); +} - root()->addWidget(authWidget); - } +void +LmsApplication::createLmsUI() +{ + + _sessionData.getDatabaseHandler().getLogin().changed().connect(this, &LmsApplication::handleAuthEvent); + + LmsAuth *authWidget = new LmsAuth(_sessionData.getDatabaseHandler()); + + authWidget->model()->addPasswordAuth(&Database::Handler::getPasswordService()); + authWidget->setRegistrationEnabled(false); + + authWidget->processEnvironment(); + + root()->addWidget(authWidget); } diff --git a/src/ui/LmsApplication.hpp b/src/ui/LmsApplication.hpp index 6d4b1259..f1f814d0 100644 --- a/src/ui/LmsApplication.hpp +++ b/src/ui/LmsApplication.hpp @@ -38,6 +38,8 @@ class LmsApplication : public Wt::WApplication private: void handleAuthEvent(void); + void createFirstConnectionUI(); + void createLmsUI(); SessionData _sessionData;