1 /*******************************************************************************
2 *                         Goggles Music Manager                                *
3 ********************************************************************************
4 *           Copyright (C) 2009-2021 by Sander Jansen. All Rights Reserved      *
5 *                               ---                                            *
6 * This program is free software: you can redistribute it and/or modify         *
7 * it under the terms of the GNU General Public License as published by         *
8 * the Free Software Foundation, either version 3 of the License, or            *
9 * (at your option) any later version.                                          *
10 *                                                                              *
11 * This program is distributed in the hope that it will be useful,              *
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of               *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                *
14 * GNU General Public License for more details.                                 *
15 *                                                                              *
16 * You should have received a copy of the GNU General Public License            *
17 * along with this program.  If not, see http://www.gnu.org/licenses.           *
18 ********************************************************************************/
19 #include "gmdefs.h"
20 #include "gmutils.h"
21 #include "GMList.h"
22 #include "GMTrack.h"
23 #include "GMDatabase.h"
24 #include "GMTrackDatabase.h"
25 #include "GMTrackList.h"
26 #include "GMTrackItem.h"
27 #include "GMTrackView.h"
28 #include "GMSource.h"
29 #include "GMSourceView.h"
30 #include "GMClipboard.h"
31 #include "GMDatabaseSource.h"
32 #include "GMPlayListSource.h"
33 #include "GMPlayQueue.h"
34 #include "GMPlayerManager.h"
35 #include "GMWindow.h"
36 #include "GMIconTheme.h"
37 #include "GMFilename.h"
38 
39 extern void getSelectedTrackQueues(FXIntList & list);
40 
41 
42 FXDEFMAP(GMPlayQueue) GMPlayQueueMap[]={
43   FXMAPFUNC(SEL_COMMAND,GMPlayQueue::ID_DELETE_TRACK,GMPlayQueue::onCmdRemoveInPlaylist),
44   FXMAPFUNC(SEL_COMMAND,GMPlayQueue::ID_CLEAR,GMPlayQueue::onCmdClear)
45   };
46 
47 FXIMPLEMENT(GMPlayQueue,GMPlayListSource,GMPlayQueueMap,ARRAYNUMBER(GMPlayQueueMap));
48 
49 
GMPlayQueue(GMTrackDatabase * database)50 GMPlayQueue::GMPlayQueue(GMTrackDatabase * database) : GMPlayListSource(database,database->getPlayQueue())  {
51   updateTrackHash();
52   ntracks=0;
53   poptrack=false;
54   GMQuery q(db,"SELECT count(track) FROM playlist_tracks WHERE playlist==?");
55   q.execute(playlist,ntracks);
56   }
57 
~GMPlayQueue()58 GMPlayQueue::~GMPlayQueue() {
59   }
60 
61 
62 
63 
configure(GMColumnList & list)64 void GMPlayQueue::configure(GMColumnList& list) {
65   list.no(17);
66   FXint i=0;
67   list[i++]=GMColumn(notr("No"),HEADER_TRACK,GMDBTrackItem::ascendingTrack,GMDBTrackItem::descendingTrack,43,false ,false,0);
68   list[i++]=GMColumn(notr("Queue"),HEADER_QUEUE,GMDBTrackItem::ascendingQueue,GMDBTrackItem::descendingQueue,70,true,true,1);
69   list[i++]=GMColumn(notr("Artist"),HEADER_ARTIST,GMDBTrackItem::ascendingArtist,GMDBTrackItem::descendingArtist,200,true,true,2);
70   list[i++]=GMColumn(notr("Title"),HEADER_TITLE,GMDBTrackItem::ascendingTitle,GMDBTrackItem::descendingTitle,300,true,true,3);
71   list[i++]=GMColumn(notr("Album"),HEADER_ALBUM,GMDBTrackItem::ascendingAlbum,GMDBTrackItem::descendingAlbum,200,true,true,4);
72   list[i++]=GMColumn(notr("Year"),HEADER_YEAR,GMDBTrackItem::ascendingYear,GMDBTrackItem::descendingYear,60,false,false,5);
73   list[i++]=GMColumn(notr("Album Artist"),HEADER_ALBUM_ARTIST,GMDBTrackItem::ascendingAlbumArtist,GMDBTrackItem::descendingAlbumArtist,200,false,false,6);
74   list[i++]=GMColumn(notr("Disc"),HEADER_DISC,GMDBTrackItem::ascendingDisc,GMDBTrackItem::descendingDisc,43,false,false,7);
75   list[i++]=GMColumn(notr("Time"),HEADER_TIME,GMDBTrackItem::ascendingTime,GMDBTrackItem::descendingTime,60,true,true,8);
76   list[i++]=GMColumn(notr("Play Count"),HEADER_PLAYCOUNT,GMDBTrackItem::ascendingPlaycount,GMDBTrackItem::descendingPlaycount,60,false,false,9);
77   list[i++]=GMColumn(notr("Play Date"),HEADER_PLAYDATE,GMDBTrackItem::ascendingPlaydate,GMDBTrackItem::descendingPlaydate,60,false,false,10);
78   list[i++]=GMColumn(notr("Path"),HEADER_FILENAME,GMDBTrackItem::ascendingFilename,GMDBTrackItem::descendingFilename,400,false,false,11);
79   list[i++]=GMColumn(notr("File Type"),HEADER_FILETYPE,GMDBTrackItem::ascendingFiletype,GMDBTrackItem::descendingFiletype,30,false,false,12);
80   list[i++]=GMColumn(notr("Format"),HEADER_AUDIOFORMAT,GMDBTrackItem::ascendingFormat,GMDBTrackItem::descendingFormat,400,false,false,14);
81   list[i++]=GMColumn(notr("Composer"),HEADER_COMPOSER,GMDBTrackItem::ascendingComposer,GMDBTrackItem::descendingComposer,30,false,false,14);
82   list[i++]=GMColumn(notr("Conductor"),HEADER_CONDUCTOR,GMDBTrackItem::ascendingConductor,GMDBTrackItem::descendingConductor,400,false,false,15);
83   list[i++]=GMColumn(notr("Rating"),HEADER_RATING,GMDBTrackItem::ascendingRating,GMDBTrackItem::descendingRating,30,false,false,17,this,ID_EDIT_RATING);
84   }
85 
86 
87 
findCurrent(GMTrackList * list,GMSource * src)88 FXbool GMPlayQueue::findCurrent(GMTrackList * list,GMSource * src) {
89   if (src->getCurrentTrack()==-1) return false;
90   if (src==this) {
91     for (FXint i=0;i<list->getNumItems();i++){
92       if (list->getItemId(i)==current_track && dynamic_cast<GMDBTrackItem*>(list->getItem(i))->getTrackQueue()==1) {
93         list->setActiveItem(i);
94         list->setCurrentItem(i);
95         return true;
96         }
97       }
98     }
99   return false;
100   }
101 
102 
103 
getNumTracks() const104 FXint GMPlayQueue::getNumTracks() const {
105   return ntracks;
106   }
107 
108 
getName() const109 FXString GMPlayQueue::getName() const {
110   return FXString::value("Play Queue (%d)",ntracks);
111   }
112 
source_context_menu(FXMenuPane * pane)113 FXbool GMPlayQueue::source_context_menu(FXMenuPane * pane) {
114   new GMMenuCommand(pane,fxtr("Clear"),GMIconTheme::instance()->icon_delete,this,ID_CLEAR);
115   return true;
116   }
117 
118 
updateTrackHash()119 void GMPlayQueue::updateTrackHash() {
120   GM_TICKS_START();
121   FXint track;
122   FXint count;
123   tracks.clear();
124   try {
125     GMQuery q(db,"SELECT track,count(track) FROM playlist_tracks WHERE playlist==? GROUP BY track;");
126     q.set(0,playlist);
127     while(q.row()) {
128       q.get(0,track);
129       q.get(1,count);
130       tracks.insert(track,count);
131       }
132     }
133   catch(GMDatabaseException & e){
134     }
135   GM_TICKS_END();
136   }
137 
138 
139 
140 
track_context_menu(FXMenuPane * pane)141 FXbool GMPlayQueue::track_context_menu(FXMenuPane * pane){
142   new GMMenuCommand(pane,fxtr("Edit…\tF2\tEdit Track Information."),GMIconTheme::instance()->icon_edit,this,GMDatabaseSource::ID_EDIT_TRACK);
143   new GMMenuCommand(pane,fxtr("Copy\tCtrl-C\tCopy track(s) to clipboard."),GMIconTheme::instance()->icon_copy,this,ID_COPY_TRACK);
144   new FXMenuSeparator(pane);
145   new GMMenuCommand(pane,fxtr("Remove…\tDel\tRemove track(s) from play queue."),GMIconTheme::instance()->icon_delete,this,ID_DELETE_TRACK);
146   return true;
147   }
148 
149 
canPlaySource(GMSource * src) const150 FXbool GMPlayQueue::canPlaySource(GMSource * src) const {
151   return (src && (src->getType()==SOURCE_DATABASE || src->getType()==SOURCE_DATABASE_FILTER || src->getType()==SOURCE_DATABASE_PLAYLIST || src->getType()==SOURCE_PLAYQUEUE));
152   }
153 
addTracks(GMSource * src,const FXIntList & tracks)154 void GMPlayQueue::addTracks(GMSource * src,const FXIntList & tracks) {
155   if (src!=this && canPlaySource(src) && tracks.no() && db->insertPlaylistTracks(playlist,tracks)) {
156     ntracks+=tracks.no();
157     updateTrackHash();
158     GMPlayerManager::instance()->getSourceView()->refresh(this);
159     }
160   }
161 
onCmdClear(FXObject *,FXSelector,void *)162 long GMPlayQueue::onCmdClear(FXObject*,FXSelector,void*){
163   db->executeFormat("DELETE FROM playlist_tracks WHERE playlist == %d",playlist);
164   updateTrackHash();
165   ntracks=0;
166   poptrack=false;
167   GMPlayerManager::instance()->getSourceView()->refresh(this);
168   GMPlayerManager::instance()->getTrackView()->refresh();
169   return 1;
170   }
171 
172 
onCmdRemoveInPlaylist(FXObject *,FXSelector,void *)173 long GMPlayQueue::onCmdRemoveInPlaylist(FXObject*,FXSelector,void*){
174   FXIntList queue;
175   FXIntList tracks;
176   getSelectedTrackQueues(queue);
177   if (queue.no()) {
178     try {
179       GMLockTransaction transaction(db);
180       db->removePlaylistQueue(playlist,queue);
181       transaction.commit();
182       ntracks-=queue.no();
183       updateTrackHash();
184       }
185     catch(GMDatabaseException&){
186       return 1;
187       }
188     GMPlayerManager::instance()->getTrackView()->refresh();
189     GMPlayerManager::instance()->getSourceView()->refresh(this);
190     }
191   return 1;
192   }
193 
194 
195 
hasTrack(FXint id) const196 FXbool GMPlayQueue::hasTrack(FXint id) const{
197   return tracks.at(id)>0;
198   }
199 
200 
201 
getNext()202 FXint GMPlayQueue::getNext() {
203   GMQuery q(db,"SELECT track FROM playlist_tracks WHERE playlist == ? ORDER BY queue ASC LIMIT 1");
204 
205   current_track=-1;
206   q.execute(playlist,current_track);
207   if (current_track>0 && poptrack) {
208 
209     FXint cnt = tracks.at(current_track) - 1;
210     if (cnt>0)
211       tracks.insert(current_track,cnt);
212     else
213       tracks.remove(current_track);
214 
215     if (ntracks) ntracks--;
216 
217     db->executeFormat("DELETE FROM playlist_tracks WHERE playlist == %d AND queue == (SELECT MIN(queue) FROM playlist_tracks WHERE playlist == %d);",playlist,playlist);
218 
219     current_track=-1;
220     q.execute(playlist,current_track);
221     }
222   poptrack=true;
223   return current_track;
224   }
225 
226 
getCurrent()227 FXint GMPlayQueue::getCurrent() {
228   current_track=-1;
229   try {
230     GMQuery q(db,"SELECT track FROM playlist_tracks WHERE playlist == ? ORDER BY queue ASC LIMIT 1");
231     q.execute(playlist,current_track);
232     poptrack=true;
233     }
234   catch(GMDatabaseException & e){
235     return -1;
236     }
237   return current_track;
238   }
239