diff --git a/SoundBand/FileDialog.qml b/SoundBand/FileDialog.qml index 9157f49..e5cc89a 100644 --- a/SoundBand/FileDialog.qml +++ b/SoundBand/FileDialog.qml @@ -40,7 +40,7 @@ Item { function map(obj){ var index = selectedFiles.indexOf(obj); if(index === -1){ - selectedFiles.push(obj); + selectedFiles.push(folderListModel.folder + "/" + obj); return true; }else{ selectedFiles.splice(index,1); diff --git a/SoundBand/PlayListEditPane.qml b/SoundBand/PlayListEditPane.qml index 08e6824..d8254d6 100644 --- a/SoundBand/PlayListEditPane.qml +++ b/SoundBand/PlayListEditPane.qml @@ -1,5 +1,8 @@ import QtQuick 2.4 import QtQuick.Controls 2.0 +import QtQuick.Extras 1.4 +import QtQuick.Window 2.0 + import "./base" as Base import "base/utils.js" as Utils @@ -7,93 +10,153 @@ Rectangle { id: playListPane; property string name: "no selected" - property var selectedSongs : [] + readonly property real rowHeight: Utils.dp(Screen.pixelDensity, 36) + readonly property real rowWidth: parent.width; + readonly property real textmargin: Utils.dp(Screen.pixelDensity, 8) + readonly property real textSize: Utils.dp(Screen.pixelDensity, 10) + readonly property real buttonHeight: Utils.dp(Screen.pixelDensity, 24) + + function showPlayList(name){ + playListModel.setNewList(name); + this.name = name; + } - signal select(); color: Utils.backgroundColor() - Base.BaseText{ - id:namePalyList - height: 30 - text: (parent.name) - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - anchors.top: parent.top - anchors.left: parent.left - anchors.right: parent.right - } - - Item{ - - id: buttons; - height: 30 - Button{ - id: ok - text: qsTr("ok") - onClicked: { - for(var i = 0; i < model.count; i++ ){ - if(model.get(i).isSelected){ - selectedSongs.push(model.get(i).getName()); - } - } - select(); - switch_pane(playListsControlSource); - - } - anchors.left: parent.left - anchors.leftMargin: 10 - - } - - Button{ - id: cancel; - text: qsTr("cancel") - onClicked: { - switch_pane(playListsControlSource); - } - anchors.left: ok.right - anchors.leftMargin: 10; - } - - Button{ - id: add; - text: qsTr("add") - onClicked: { - switch_pane(fileDialog); - } - anchors.right: remove.left - anchors.rightMargin: 10 - } - - Button{ - id: remove; - text: qsTr("remove") - onClicked: { - } - anchors.right: parent.right - anchors.rightMargin: 10 - } - - anchors.bottom: listView.top - anchors.left: parent.left - anchors.right: parent.right - } - ListView { id: listView - model: ListModel { - id: model + model: playListModel + Component { + id: playListDelegate + + Item { + height: rowHeight + width: listView.width + id: item + + Rectangle { + color: Utils.baseColor(); + id: rectangle; + anchors.fill: item + + MouseArea { + anchors.fill: parent; + + onClicked: { + indicator.active = playListModel.select(songId) + } + } + + Text { + id: textNameSong + height: width + anchors.left: image.right + anchors.top: rectangle.top + anchors.bottom: rectangle.bottom + anchors.right: indicator.left + text: songName !== undefined ? songName : "" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + + StatusIndicator{ + id: indicator + width: height * 0.9 + color: "#4fc1e9" + active: playListModel.isSelected(songId); + anchors.right: rectangle.right + anchors.leftMargin: textmargin + anchors.verticalCenter: rectangle.verticalCenter + + } + + Image { + id: image + height: buttonHeight + width: height + anchors.left: rectangle.left + anchors.leftMargin: textmargin + anchors.verticalCenter: rectangle.verticalCenter + source: "image://collection/" + songId + } + } + } } -// delegate: SongDelegateSelection{ + delegate: playListDelegate -// } - anchors.top:name.bottom - anchors.bottom: buttons.bottom + ScrollIndicator.horizontal: ScrollIndicator { } + ScrollIndicator.vertical: ScrollIndicator { } + + anchors.top:controlBox.bottom + anchors.bottom: parent.bottom anchors.left: parent.left anchors.right: parent.right } + + Rectangle{ + id:controlBox + color: Utils.baseColor() + + height: Utils.dp(Screen.pixelDensity, 40) + + Base.BaseText{ + id:namePalyList + text: playListPane.name + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: buttons.left + } + + Rectangle{ + + id: buttons; + height: namePalyList.height + color: Utils.baseColor() + width: Utils.dp(Screen.pixelDensity, 160) + + Base.BaseButton{ + id: ok + text: qsTr("ok") + + onClicked: { + + switch_pane(playListsControlSource); + + } + anchors.left: add.right + anchors.right: parent.right + + + + } + + Base.BaseButton{ + id: add; + text: qsTr("add") + onClicked: { + switch_pane(fileDialog); + } + anchors.left: parent.left + anchors.rightMargin: 10 + } + + anchors.right: parent.right + } + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + + } + + + + } diff --git a/SoundBand/PlayListsControl.qml b/SoundBand/PlayListsControl.qml index e36eb4a..92b3558 100644 --- a/SoundBand/PlayListsControl.qml +++ b/SoundBand/PlayListsControl.qml @@ -77,18 +77,20 @@ Item { anchors.top: parent.top onClicked: { switch_pane(editPlayList); - popup.close() + editPlayList.showPlayList(playListName); + popup.close(); } } Button { - text: qsTr("Remove") - anchors.left: parent.left - anchors.right: parent.right - anchors.bottom: parent.bottom - height: parent.height/2 + text: qsTr("Remove"); + anchors.left: parent.left; + anchors.right: parent.right; + anchors.bottom: parent.bottom; + height: parent.height/2; onClicked: { - popup.close() + syncEngine.removePlayList(playListName); + popup.close(); } } @@ -153,6 +155,7 @@ Item { anchors.bottom: parent.bottom onClicked: { + getName.open(); } @@ -165,6 +168,67 @@ Item { anchors.fill: playListsControl; } + Popup{ + id:getName + modal: true + focus: true + width: controlBox.width; + height: Utils.dp(Screen.pixelDensity, 60); + x: 0; + y: controlBox.height / 2 + + Base.BaseText{ + id:text + text : qsTr("write name:"); + anchors.left: parent.left; + anchors.top:parent.top; + anchors.bottom:parent.bottom; + + } + + Rectangle{ + color: Qt.rgba(0,0,0,0) + border.color: Utils.primaryColor(); + radius: Utils.dp(Screen.pixelDensity, 2); + TextEdit{ + id:newName; + text : qsTr("new PlayList"); + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + font.pixelSize: Utils.baseFontSize(Screen.pixelDensity) + anchors.fill: parent + } + anchors.top:parent.top; + anchors.topMargin: Utils.dp(Screen.pixelDensity, 5); + + anchors.bottom:parent.bottom; + anchors.bottomMargin: Utils.dp(Screen.pixelDensity, 5); + + anchors.left:text.right + anchors.leftMargin: Utils.dp(Screen.pixelDensity, 10); + + anchors.right: ok.left + anchors.rightMargin: Utils.dp(Screen.pixelDensity, 10); + + } + + + Base.BaseButton{ + id:ok; + width: 80; + anchors.top:parent.top; + anchors.bottom:parent.bottom; + anchors.right: parent.right; + + onClicked: { + syncEngine.createPlayList(newName.text); + getName.close(); + + } + + } + } + PlayListEditPane{ @@ -183,8 +247,11 @@ Item { nameFilters: "*.mp3" onFilesSelected: { switch_pane(editPlayList); - // messageDialog.text = "Cannot open file "+ currentFolder() + "/" + fileName - // messageDialog.open() + + for(var i = 0; i < selectedFiles.length; i++){ + syncEngine.addSong(selectedFiles[i]); + } + } } diff --git a/SoundBand/app.cpp b/SoundBand/app.cpp index f33cd86..a957693 100644 --- a/SoundBand/app.cpp +++ b/SoundBand/app.cpp @@ -23,6 +23,9 @@ App::App(QObject* ptr): currentPlayListModel = new CurrentPlayListModel(); currentPlayListModel->setSource(syncEngine); + + playListModel = new PlayListModel(); + playListModel->setSource(syncEngine); } bool App::run(){ @@ -34,6 +37,8 @@ bool App::run(){ ctxt->setContextProperty("serverListModel", serverListModel); ctxt->setContextProperty("playListsModel", playListsModel); ctxt->setContextProperty("currentPlayListModel", currentPlayListModel); + ctxt->setContextProperty("playListModel", playListModel); + qmlEngine->load(QUrl(QStringLiteral("qrc:/main.qml"))); if (qmlEngine->rootObjects().isEmpty()) diff --git a/SoundBand/app.h b/SoundBand/app.h index ca57615..635efd7 100644 --- a/SoundBand/app.h +++ b/SoundBand/app.h @@ -26,6 +26,8 @@ private: ServerListModel *serverListModel; PlayListsModel *playListsModel; CurrentPlayListModel *currentPlayListModel; + PlayListModel *playListModel; + public: diff --git a/SoundBand/base/BaseButton.qml b/SoundBand/base/BaseButton.qml new file mode 100644 index 0000000..661344c --- /dev/null +++ b/SoundBand/base/BaseButton.qml @@ -0,0 +1,12 @@ +import QtQuick 2.4 +import QtQuick.Controls 2.0 +import QtQuick.Window 2.0 +import "utils.js" as Utils + +Button{ + id: ok + text: qsTr("ok") + width: Utils.dp(Screen.pixelDensity, 80) + height: Utils.dp(Screen.pixelDensity, 40) + +} diff --git a/SoundBand/base/utils.js b/SoundBand/base/utils.js index c0cf75a..1287107 100644 --- a/SoundBand/base/utils.js +++ b/SoundBand/base/utils.js @@ -1,3 +1,4 @@ + function dp(pixelDensity,x) { return (pixelDensity * 25.4 < 120) ? x : x*(pixelDensity * 25.4/160); } @@ -26,3 +27,7 @@ function backgroundAltColor() { return "#333333" } +function baseFontSize(pixelDensity){ + return dp(pixelDensity, 14); +} + diff --git a/SoundBand/currentplaylistmodel.cpp b/SoundBand/currentplaylistmodel.cpp index 2ac6ffe..fbaa202 100644 --- a/SoundBand/currentplaylistmodel.cpp +++ b/SoundBand/currentplaylistmodel.cpp @@ -36,7 +36,7 @@ void CurrentPlayListModel::onPlayListChanged(){ bool CurrentPlayListModel::canFetchMore(const QModelIndex & /* index */) const { - if (playList && itemCount < playList->size()) + if (playList && itemCount != playList->size()) return true; else return false; @@ -47,11 +47,19 @@ void CurrentPlayListModel::fetchMore(const QModelIndex & /* index */) int remainder = playList->size() - itemCount; int itemsToFetch = qMin(100, remainder); - beginInsertRows(QModelIndex(), itemCount, itemCount + itemsToFetch - 1); + if(itemsToFetch < 0){ + beginRemoveRows(QModelIndex(), 0, 0 - itemsToFetch - 1 ); - itemCount += itemsToFetch; + itemCount += itemsToFetch; - endInsertRows(); + endRemoveRows(); + }else{ + beginInsertRows(QModelIndex(), itemCount, itemCount + itemsToFetch - 1); + + itemCount += itemsToFetch; + + endInsertRows(); + } } int CurrentPlayListModel::rowCount(const QModelIndex & /* parent */) const diff --git a/SoundBand/playlistmodel.cpp b/SoundBand/playlistmodel.cpp index e4e0598..1cf184c 100644 --- a/SoundBand/playlistmodel.cpp +++ b/SoundBand/playlistmodel.cpp @@ -15,6 +15,7 @@ void PlayListModel::setSource(SyncEngine *engine){ disconnect(syncEngine, SIGNAL(songsCountChanged()) ,this, SLOT(onPlayListChanged())); syncEngine = engine; connect(syncEngine, SIGNAL(songsCountChanged()),this ,SLOT(onPlayListChanged())); + onPlayListChanged(); } QHash PlayListModel::roleNames()const{ @@ -37,7 +38,7 @@ void PlayListModel::onPlayListChanged(){ bool PlayListModel::canFetchMore(const QModelIndex & /* index */) const { - if (itemCount < playList.size()) + if (itemCount != playList.size()) return true; else return false; @@ -48,11 +49,20 @@ void PlayListModel::fetchMore(const QModelIndex & /* index */) int remainder = playList.size() - itemCount; int itemsToFetch = qMin(100, remainder); - beginInsertRows(QModelIndex(), itemCount, itemCount + itemsToFetch - 1); + if(itemsToFetch < 0){ + beginRemoveRows(QModelIndex(), 0, 0 - itemsToFetch - 1 ); - itemCount += itemsToFetch; + itemCount += itemsToFetch; + + endRemoveRows(); + }else{ + beginInsertRows(QModelIndex(), itemCount, itemCount + itemsToFetch - 1); + + itemCount += itemsToFetch; + + endInsertRows(); + } - endInsertRows(); } int PlayListModel::rowCount(const QModelIndex & /* parent */) const @@ -84,21 +94,17 @@ QVariant PlayListModel::data(const QModelIndex &index, int role) const bool PlayListModel::select(int id){ - for(QList::Iterator i = 0; i < playList.end(); i++){ + for(QList::Iterator i = playList.begin(); i < playList.end(); i++){ if(i->id == id){ - return i->isSelected = true; - } - } - return false; -} + if((i->isSelected = !i->isSelected)){ + syncEngine->addToPlayList(id, playListName); + } + else{ + syncEngine->removeFromPlayList(id, playListName); + } -bool PlayListModel::unSelect(int id){ - - for(QList::Iterator i = 0; i < playList.end(); i++){ - if(i->id == id){ - i->isSelected = false; - return true; + return i->isSelected; } } @@ -108,7 +114,7 @@ bool PlayListModel::unSelect(int id){ QList PlayListModel::getSelected(){ QList result; - for(QList::Iterator i = 0; i < playList.end(); i++){ + for(QList::Iterator i = playList.begin(); i < playList.end(); i++){ if(i->isSelected){ result.push_back(i->id); } @@ -118,7 +124,7 @@ QList PlayListModel::getSelected(){ bool PlayListModel::isSelected(int id){ - for(QList::Iterator i = 0; i < playList.end(); i++){ + for(QList::Iterator i = playList.begin(); i < playList.end(); i++){ if(i->id == id){ return i->isSelected; } diff --git a/SoundBand/playlistmodel.h b/SoundBand/playlistmodel.h index f9ac2b1..58b7c41 100644 --- a/SoundBand/playlistmodel.h +++ b/SoundBand/playlistmodel.h @@ -74,19 +74,12 @@ signals: public slots: /** - * @brief select a song from playList; + * @brief select a song from playList or unselected if item has been selected; * @param id - if of song * @return true if all done */ bool select(int id); - /** - * @brief unselect a song from playList; - * @param id - if of song - * @return true if all done - */ - bool unSelect(int id); - /** * @brief getSelected * @return list of selected songs diff --git a/SoundBand/playlistsmodel.cpp b/SoundBand/playlistsmodel.cpp index a40bb2d..1493e30 100644 --- a/SoundBand/playlistsmodel.cpp +++ b/SoundBand/playlistsmodel.cpp @@ -31,7 +31,7 @@ void PlayListsModel::onPlayListsChanged(){ bool PlayListsModel::canFetchMore(const QModelIndex & /* index */) const { - if (itemCount < playLists.size()) + if (itemCount != playLists.size()) return true; else return false; @@ -42,11 +42,20 @@ void PlayListsModel::fetchMore(const QModelIndex & /* index */) int remainder = playLists.size() - itemCount; int itemsToFetch = qMin(100, remainder); - beginInsertRows(QModelIndex(), itemCount, itemCount + itemsToFetch - 1); + if(itemsToFetch < 0){ + beginRemoveRows(QModelIndex(), 0, 0 - itemsToFetch - 1 ); - itemCount += itemsToFetch; + itemCount += itemsToFetch; + + endRemoveRows(); + }else{ + beginInsertRows(QModelIndex(), itemCount, itemCount + itemsToFetch - 1); + + itemCount += itemsToFetch; + + endInsertRows(); + } - endInsertRows(); } int PlayListsModel::rowCount(const QModelIndex & /* parent */) const diff --git a/SoundBand/qml.qrc b/SoundBand/qml.qrc index 4402eb2..6196136 100644 --- a/SoundBand/qml.qrc +++ b/SoundBand/qml.qrc @@ -13,6 +13,7 @@ FileDialog.qml base/utils.js base/StatusIndicator.qml + base/BaseButton.qml res/logo.png diff --git a/SoundBand/serverlistmodel.cpp b/SoundBand/serverlistmodel.cpp index 8babf21..955378c 100644 --- a/SoundBand/serverlistmodel.cpp +++ b/SoundBand/serverlistmodel.cpp @@ -30,7 +30,7 @@ void ServerListModel::onServersListsChanged(){ bool ServerListModel::canFetchMore(const QModelIndex & /* index */) const { - if (servers && itemCount < servers->size()) + if (servers && itemCount != servers->size()) return true; else return false; @@ -41,11 +41,19 @@ void ServerListModel::fetchMore(const QModelIndex & /* index */) int remainder = servers->size() - itemCount; int itemsToFetch = qMin(100, remainder); - beginInsertRows(QModelIndex(), itemCount, itemCount + itemsToFetch - 1); + if(itemsToFetch < 0){ + beginRemoveRows(QModelIndex(), 0, 0 - itemsToFetch - 1 ); - itemCount += itemsToFetch; + itemCount += itemsToFetch; - endInsertRows(); + endRemoveRows(); + }else{ + beginInsertRows(QModelIndex(), itemCount, itemCount + itemsToFetch - 1); + + itemCount += itemsToFetch; + + endInsertRows(); + } } int ServerListModel::rowCount(const QModelIndex & /* parent */) const diff --git a/SoundBand/syncengine.cpp b/SoundBand/syncengine.cpp index 14adc43..e3d4d40 100644 --- a/SoundBand/syncengine.cpp +++ b/SoundBand/syncengine.cpp @@ -164,7 +164,7 @@ bool SyncEngine::setPlayList(const QString& name){ } bool SyncEngine::getPlayList(QList &playList, const QString &name){ - return sqlApi->updateAvailableSongs(playList, name); + return sqlApi->updateAvailableSongs(playList, name, true); } const QString& SyncEngine::lastError() const{ diff --git a/sync/mysql.cpp b/sync/mysql.cpp index 5f02c34..50cdcd9 100644 --- a/sync/mysql.cpp +++ b/sync/mysql.cpp @@ -50,7 +50,20 @@ void MySql::initDB(const QString &database){ db->setDatabaseName(d.absolutePath()); if(db->open()){ qyery = new QSqlQuery(*db); - QString qyer = QString("CREATE TABLE IF NOT EXISTS songs(" + + /* + *https://stackoverflow.com/questions/40863216/sqlite-why-is-foreign-key-constraint-not-working-here + */ + + QString qyer = QString("PRAGMA foreign_keys = ON"); + if(!qyery->exec(qyer)){ + sqlErrorLog(qyer); + throw InitDBError(); + delete db; + return; + } + + qyer = QString("CREATE TABLE IF NOT EXISTS songs(" "id INTEGER PRIMARY KEY AUTOINCREMENT, " "name VARCHAR(100), " "size INT NOT NULL, " @@ -63,6 +76,7 @@ void MySql::initDB(const QString &database){ return; } + qyer = QString("CREATE UNIQUE INDEX IF NOT EXISTS isongs ON songs(name,size)"); if(!qyery->exec(qyer)){ sqlErrorLog(qyer); @@ -87,7 +101,7 @@ void MySql::initDB(const QString &database){ qyer = QString("CREATE TABLE IF NOT EXISTS playlistsdata(" "playlist INT NOT NULL," "song INT NOT NULL," - "FOREIGN KEY(playlist) REFERENCES playlists(id)" + "FOREIGN KEY(playlist) REFERENCES playlists(name)" " ON UPDATE CASCADE" " ON DELETE CASCADE," "FOREIGN KEY(song) REFERENCES songs(id)" @@ -155,8 +169,8 @@ int MySql::save(const Song &song){ return result; } -int MySql::save(const QString &url){ - QFile f(url); +int MySql::save(const QString &url){ + QFile f(QUrl(url).toLocalFile()); if(!f.open(QIODevice::ReadOnly)){ return -1; } @@ -203,9 +217,10 @@ bool MySql::load(const SongHeader &song,Song &result){ return true; } -bool MySql::updateAvailableSongs(QList& list, const QString& playList){ +bool MySql::updateAvailableSongs(QList& list, const QString& playList, bool forEditing){ QString qyer; - if(playList.isEmpty() || playList == ALL_SONGS_LIST){ + + if(playList.isEmpty() || playList == ALL_SONGS_LIST || forEditing){ qyer = QString("SELECT id,name,size from songs"); }else{ qyer = QString("SELECT id,name,size from songs where " @@ -222,12 +237,35 @@ bool MySql::updateAvailableSongs(QList& list, const QString& playLis while(qyery->next()){ SongHeader song; + song.isSelected = !forEditing || playList == ALL_SONGS_LIST; song.id = qyery->value(0).toInt(); song.name = qyery->value(1).toString(); song.size = qyery->value(2).toInt(); list.push_back(song); } + if(forEditing && list.size() > 0 && playList != ALL_SONGS_LIST){ + QString qyer; + qyer = QString("select song from playlistsdata where " + " playlist='%0'").arg(playList); + + if(!qyery->exec(qyer)){ + sqlErrorLog(qyer); + return false; + } + + while(qyery->next()){ + for(SongHeader& item:list){ + int id = qyery->value(0).toInt(); + if(item.id == id){ + item.isSelected = true; + break; + } + } + } + } + + return true; } diff --git a/sync/mysql.h b/sync/mysql.h index cb98185..125defa 100644 --- a/sync/mysql.h +++ b/sync/mysql.h @@ -59,9 +59,10 @@ public: * @brief updateAvelableSongs will update the list of participants of songs. * @param list - [out value] list of avelable song. * @param playList - play list of songs. + * @param forEdit - flag for editing play list. If this flag = true then return all available songs with corect flag 'isSelect' * @return true if all done */ - bool updateAvailableSongs(QList& list, const QString &playList = ""); + bool updateAvailableSongs(QList& list, const QString &playList = "", bool forEditing = false); /** * @brief updateAvelableSongs will update the list of participants of songs.