1 /*
2  * Cantata
3  *
4  * Copyright (c) 2011-2020 Craig Drummond <craig.p.drummond@gmail.com>
5  *
6  * ----
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; see the file COPYING.  If not, write to
20  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  */
23 
24 #include "mpdbrowsepage.h"
25 #include "mpd-interface/mpdconnection.h"
26 #include "settings.h"
27 #include "support/messagebox.h"
28 #include "support/action.h"
29 #include "support/utils.h"
30 #include "support/monoicon.h"
31 #include "models/mpdlibrarymodel.h"
32 #include "widgets/menubutton.h"
33 #include "widgets/icons.h"
34 #include "stdactions.h"
35 #include "customactions.h"
36 #include <QDesktopServices>
37 #include <QUrl>
38 
MpdBrowsePage(QWidget * p)39 MpdBrowsePage::MpdBrowsePage(QWidget *p)
40     : SinglePageWidget(p)
41     , model(this)
42 {
43     QColor col=Utils::monoIconColor();
44     browseAction = new Action(MonoIcon::icon(FontAwesome::folderopen, col), tr("Open In File Manager"), this);
45     connect(view, SIGNAL(itemsSelected(bool)), this, SLOT(controlActions()));
46     connect(view, SIGNAL(doubleClicked(const QModelIndex &)), this, SLOT(itemDoubleClicked(const QModelIndex &)));
47     connect(view, SIGNAL(headerClicked(int)), SLOT(headerClicked(int)));
48     connect(browseAction, SIGNAL(triggered()), this, SLOT(openFileManager()));
49     connect(MPDConnection::self(), SIGNAL(updatingFileList()), view, SLOT(updating()));
50     connect(MPDConnection::self(), SIGNAL(updatedFileList()), view, SLOT(updated()));
51     connect(MPDConnection::self(), SIGNAL(updatingDatabase()), view, SLOT(updating()));
52     connect(MPDConnection::self(), SIGNAL(updatedDatabase()), view, SLOT(updated()));
53     Configuration config(metaObject()->className());
54     view->setMode(ItemView::Mode_DetailedTree);
55     view->load(config);
56     MenuButton *menu=new MenuButton(this);
57     menu->addActions(createViewActions(QList<ItemView::Mode>() << ItemView::Mode_BasicTree << ItemView::Mode_SimpleTree
58                                                                << ItemView::Mode_DetailedTree));
59     init(ReplacePlayQueue|AppendToPlayQueue, QList<QWidget *>() << menu);
60 
61     view->addAction(StdActions::self()->addToStoredPlaylistAction);
62     view->addAction(CustomActions::self());
63     #ifdef TAGLIB_FOUND
64     #ifdef ENABLE_DEVICES_SUPPORT
65     view->addAction(StdActions::self()->copyToDeviceAction);
66     #endif
67     view->addAction(StdActions::self()->organiseFilesAction);
68     view->addAction(StdActions::self()->editTagsAction);
69     #ifdef ENABLE_REPLAYGAIN_SUPPORT
70     view->addAction(StdActions::self()->replaygainAction);
71     #endif // TAGLIB_FOUND
72     #endif
73     view->addAction(browseAction);
74     #ifdef ENABLE_DEVICES_SUPPORT
75     view->addSeparator();
76     view->addAction(StdActions::self()->deleteSongsAction);
77     #endif
78     view->setModel(&model);
79     view->closeSearch();
80     view->alwaysShowHeader();
81     connect(view, SIGNAL(updateToPlayQueue(QModelIndex,bool)), this, SLOT(updateToPlayQueue(QModelIndex,bool)));
82     view->setInfoText(tr("No folders? Looks like your MPD is not configured correctly."));
83 }
84 
~MpdBrowsePage()85 MpdBrowsePage::~MpdBrowsePage()
86 {
87     Configuration config(metaObject()->className());
88     view->save(config);
89 }
90 
showEvent(QShowEvent * e)91 void MpdBrowsePage::showEvent(QShowEvent *e)
92 {
93     view->focusView();
94     SinglePageWidget::showEvent(e);
95     model.setEnabled(true);
96     model.load();
97 }
98 
controlActions()99 void MpdBrowsePage::controlActions()
100 {
101     QModelIndexList selected=view->selectedIndexes(false); // Dont need sorted selection here...
102     bool enable=selected.count()>0;
103     bool trackSelected=false;
104     bool folderSelected=false;
105 
106     for (const QModelIndex &idx: selected) {
107         if (static_cast<BrowseModel::Item *>(idx.internalPointer())->isFolder()) {
108             folderSelected=true;
109         } else {
110             trackSelected=true;
111         }
112     }
113 
114     StdActions::self()->enableAddToPlayQueue(enable);
115     StdActions::self()->addToStoredPlaylistAction->setEnabled(enable);
116     bool fileActions = trackSelected && !folderSelected && MPDConnection::self()->getDetails().dirReadable;
117     CustomActions::self()->setEnabled(fileActions);
118     #ifdef TAGLIB_FOUND
119     StdActions::self()->organiseFilesAction->setEnabled(fileActions);
120     StdActions::self()->editTagsAction->setEnabled(StdActions::self()->organiseFilesAction->isEnabled());
121     #ifdef ENABLE_REPLAYGAIN_SUPPORT
122     StdActions::self()->replaygainAction->setEnabled(StdActions::self()->organiseFilesAction->isEnabled());
123     #endif
124     #ifdef ENABLE_DEVICES_SUPPORT
125     StdActions::self()->deleteSongsAction->setEnabled(StdActions::self()->organiseFilesAction->isEnabled());
126     StdActions::self()->copyToDeviceAction->setEnabled(StdActions::self()->organiseFilesAction->isEnabled());
127     #endif
128     #endif // TAGLIB_FOUND
129 
130     browseAction->setEnabled(enable && 1==selected.count() && folderSelected);
131 }
132 
itemDoubleClicked(const QModelIndex &)133 void MpdBrowsePage::itemDoubleClicked(const QModelIndex &)
134 {
135     const QModelIndexList selected = view->selectedIndexes(false); // Dont need sorted selection here...
136     if (1!=selected.size()) {
137         return; //doubleclick should only have one selected item
138     }
139 
140     if (!static_cast<BrowseModel::Item *>(selected.at(0).internalPointer())->isFolder()) {
141         addSelectionToPlaylist();
142     }
143 }
144 
openFileManager()145 void MpdBrowsePage::openFileManager()
146 {
147     const QModelIndexList selected = view->selectedIndexes(false); // Dont need sorted selection here...
148     if (1!=selected.size()) {
149         return;
150     }
151 
152     BrowseModel::Item *item = static_cast<BrowseModel::Item *>(selected.at(0).internalPointer());
153     if (item->isFolder()) {
154         QDesktopServices::openUrl(QUrl::fromLocalFile(MPDConnection::self()->getDetails().dir+static_cast<BrowseModel::FolderItem *>(item)->getPath()));
155     }
156 }
157 
updateToPlayQueue(const QModelIndex & idx,bool replace)158 void MpdBrowsePage::updateToPlayQueue(const QModelIndex &idx, bool replace)
159 {
160     BrowseModel::Item *item = static_cast<BrowseModel::Item *>(idx.internalPointer());
161     if (item->isFolder()) {
162         emit add(QStringList() << MPDConnection::constDirPrefix+static_cast<BrowseModel::FolderItem *>(item)->getPath(),
163                  replace ? MPDConnection::ReplaceAndplay : MPDConnection::Append, 0, false);
164     }
165 }
166 
headerClicked(int level)167 void MpdBrowsePage::headerClicked(int level)
168 {
169     if (0==level) {
170         emit close();
171     }
172 }
173 
selectedSongs(bool allowPlaylists) const174 QList<Song> MpdBrowsePage::selectedSongs(bool allowPlaylists) const
175 {
176     return model.songs(view->selectedIndexes(), allowPlaylists);
177 }
178 
selectedFiles(bool allowPlaylists) const179 QStringList MpdBrowsePage::selectedFiles(bool allowPlaylists) const
180 {
181     QList<Song> songs=selectedSongs(allowPlaylists);
182     QStringList files;
183     for (const Song &s: songs) {
184         files.append(s.file);
185     }
186     return files;
187 }
188 
addSelectionToPlaylist(const QString & name,int action,quint8 priority,bool decreasePriority)189 void MpdBrowsePage::addSelectionToPlaylist(const QString &name, int action, quint8 priority, bool decreasePriority)
190 {
191     QModelIndexList selected=view->selectedIndexes();
192     QStringList dirs;
193     QStringList files;
194 
195     for (const QModelIndex &idx: selected) {
196         if (static_cast<BrowseModel::Item *>(idx.internalPointer())->isFolder()) {
197             files+=static_cast<BrowseModel::FolderItem *>(idx.internalPointer())->allEntries(false);
198         } else {
199             files.append(static_cast<BrowseModel::TrackItem *>(idx.internalPointer())->getSong().file);
200         }
201     }
202 
203     if (!files.isEmpty()) {
204         if (name.isEmpty()) {
205             emit add(files, action, priority, decreasePriority);
206         } else {
207             emit addSongsToPlaylist(name, files);
208         }
209         view->clearSelection();
210     }
211 }
212 
213 #ifdef ENABLE_DEVICES_SUPPORT
addSelectionToDevice(const QString & udi)214 void MpdBrowsePage::addSelectionToDevice(const QString &udi)
215 {
216     QList<Song> songs=selectedSongs();
217 
218     if (!songs.isEmpty()) {
219         emit addToDevice(QString(), udi, songs);
220         view->clearSelection();
221     }
222 }
223 
deleteSongs()224 void MpdBrowsePage::deleteSongs()
225 {
226     QList<Song> songs=selectedSongs();
227 
228     if (!songs.isEmpty()) {
229         if (MessageBox::Yes==MessageBox::warningYesNo(this, tr("Are you sure you wish to delete the selected songs?\n\nThis cannot be undone."),
230                                                       tr("Delete Songs"), StdGuiItem::del(), StdGuiItem::cancel())) {
231             emit deleteSongs(QString(), songs);
232         }
233         view->clearSelection();
234     }
235 }
236 #endif
237 
238 #include "moc_mpdbrowsepage.cpp"
239