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