1 /*******************************************************************************
2 * Goggles Music Manager *
3 ********************************************************************************
4 * Copyright (C) 2007-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 "GMTrack.h"
21 #include "GMList.h"
22 #include "GMDatabase.h"
23 #include "GMTrackDatabase.h"
24 #include "GMTrackList.h"
25 #include "GMTrackItem.h"
26 #include "GMTrackView.h"
27 #include "GMSource.h"
28 #include "GMSourceView.h"
29 #include "GMClipboard.h"
30 #include "GMStreamSource.h"
31 #include "GMPlayerManager.h"
32 #include "GMWindow.h"
33 #include "GMIconTheme.h"
34 #include "GMFilename.h"
35
36
37 FXDEFMAP(GMStreamSource) GMStreamSourceMap[]={
38 FXMAPFUNC(SEL_COMMAND,GMStreamSource::ID_NEW_STATION,GMStreamSource::onCmdNewStation),
39 FXMAPFUNC(SEL_COMMAND,GMStreamSource::ID_EDIT_STATION,GMStreamSource::onCmdEditStation),
40 FXMAPFUNC(SEL_COMMAND,GMStreamSource::ID_DELETE_STATION,GMStreamSource::onCmdDeleteStation),
41 FXMAPFUNC(SEL_UPDATE,GMStreamSource::ID_EXPORT,GMStreamSource::onUpdExport)
42
43 };
44
45 FXIMPLEMENT(GMStreamSource,GMSource,GMStreamSourceMap,ARRAYNUMBER(GMStreamSourceMap));
46
47
GMStreamSource(GMTrackDatabase * database)48 GMStreamSource::GMStreamSource(GMTrackDatabase * database) : db(database) {
49 FXASSERT(db);
50 }
51
~GMStreamSource()52 GMStreamSource::~GMStreamSource(){
53 }
54
55
configure(GMColumnList & list)56 void GMStreamSource::configure(GMColumnList& list){
57 list.no(4);
58 list[0]=GMColumn(notr("No"),HEADER_TRACK,GMStreamTrackItem::ascendingTrack,GMStreamTrackItem::descendingTrack,60,true,true,0);
59 list[1]=GMColumn(notr("Station"),HEADER_TITLE,GMStreamTrackItem::ascendingTitle,GMStreamTrackItem::descendingTitle,200,true,true,1);
60 list[2]=GMColumn(notr("Bit Rate"),HEADER_BITRATE,GMStreamTrackItem::ascendingTime,GMStreamTrackItem::descendingTime,80,true,true,2);
61 list[3]=GMColumn(notr("Genre"),HEADER_TAG,GMStreamTrackItem::ascendingTrack,GMStreamTrackItem::descendingTrack,150,true,true,3);
62 }
63
64
hasCurrentTrack(GMSource * src) const65 FXbool GMStreamSource::hasCurrentTrack(GMSource * src) const {
66 if (src==this) return true;
67 return false;
68 }
69
setTrack(GMTrack & track) const70 FXbool GMStreamSource::setTrack(GMTrack & track) const {
71 if (current_track>=0 && track.bitrate>0) {
72 db->setStreamBitrate(current_track,track.bitrate);
73 if (GMPlayerManager::instance()->getTrackView()->getSource()==this)
74 GMPlayerManager::instance()->getTrackView()->refresh();
75 }
76 return true;
77 }
78
getTrack(GMTrack & info) const79 FXbool GMStreamSource::getTrack(GMTrack & info) const {
80 info.clear();
81 GMStream sinfo;
82 if (db->getStream(current_track,sinfo)){
83 info.url = sinfo.url;
84 return false;
85 }
86 return false;
87 }
88
source_menu(FXMenuPane * pane)89 FXbool GMStreamSource::source_menu(FXMenuPane * pane){
90 new GMMenuCommand(pane,fxtr("Add Radio Station…"),nullptr,this,ID_NEW_STATION);
91 return true;
92 }
93
source_context_menu(FXMenuPane * pane)94 FXbool GMStreamSource::source_context_menu(FXMenuPane * pane){
95 new GMMenuCommand(pane,fxtr("Add Radio Station…"),nullptr,this,ID_NEW_STATION);
96 return true;
97 }
98
track_context_menu(FXMenuPane * pane)99 FXbool GMStreamSource::track_context_menu(FXMenuPane * pane){
100 new GMMenuCommand(pane,fxtr("Edit…"),GMIconTheme::instance()->icon_edit,this,ID_EDIT_STATION);
101 new GMMenuCommand(pane,fxtr("Add Radio Station…"),nullptr,this,ID_NEW_STATION);
102 new GMMenuCommand(pane,fxtr("Remove\t\tRemove."),GMIconTheme::instance()->icon_delete,this,ID_DELETE_STATION);
103 return true;
104 }
105
listTracks(GMTrackList * tracklist,const FXIntList &,const FXIntList &)106 FXbool GMStreamSource::listTracks(GMTrackList * tracklist,const FXIntList &/* albumlist*/,const FXIntList & /*genre*/){
107 GMQuery q;
108 FXString query;
109 // const FXchar * c_artist;
110 //const FXchar * c_albumname;
111 const FXchar * c_title;
112 const FXchar * c_genre;
113 // FXint time;
114 // FXint no;
115 FXint id;
116 FXint bitrate;
117 FXint queue=1;
118 try {
119
120 query = "SELECT streams.id, streams.description, streams.bitrate, tags.name "
121 "FROM streams, tags "
122 "WHERE tags.id == streams.genre;";
123
124 q = db->compile(query);
125
126 while(q.row()){
127 q.get(0,id);
128 c_title = q.get(1);
129 q.get(2,bitrate);
130 c_genre = q.get(3);
131 GMStreamTrackItem * item = new GMStreamTrackItem(id,c_title,c_genre,queue++,bitrate);
132 tracklist->appendItem(item);
133 }
134 GMStreamTrackItem::max_trackno = tracklist->getFont()->getTextWidth(FXString('8',GMDBTrackItem::max_digits(queue)));
135 }
136 catch(GMDatabaseException & e){
137 tracklist->clearItems();
138 return false;
139 }
140 return true;
141 }
142
onCmdNewStation(FXObject *,FXSelector,void *)143 long GMStreamSource::onCmdNewStation(FXObject*,FXSelector,void*){
144 FXDialogBox dialog(GMPlayerManager::instance()->getMainWindow(),fxtr("New Internet Radio Station"),DECOR_TITLE|DECOR_BORDER|DECOR_RESIZE,0,0,0,0,0,0,0,0,0,0);
145 GMPlayerManager::instance()->getMainWindow()->create_dialog_header(&dialog,fxtr("New Internet Radio Station"),fxtr("Specify url and description of new station"),nullptr);
146 FXHorizontalFrame *closebox=new FXHorizontalFrame(&dialog,LAYOUT_SIDE_BOTTOM|LAYOUT_FILL_X|PACK_UNIFORM_WIDTH,0,0,0,0);
147 new GMButton(closebox,fxtr("C&reate"),nullptr,&dialog,FXDialogBox::ID_ACCEPT,BUTTON_INITIAL|BUTTON_DEFAULT|LAYOUT_RIGHT|FRAME_RAISED|FRAME_THICK,0,0,0,0, 15,15);
148 new GMButton(closebox,fxtr("&Cancel"),nullptr,&dialog,FXDialogBox::ID_CANCEL,BUTTON_DEFAULT|LAYOUT_RIGHT|FRAME_RAISED|FRAME_THICK,0,0,0,0, 15,15);
149 new FXSeparator(&dialog,SEPARATOR_GROOVE|LAYOUT_FILL_X|LAYOUT_SIDE_BOTTOM);
150 FXVerticalFrame * main = new FXVerticalFrame(&dialog,LAYOUT_FILL_X|LAYOUT_FILL_Y,0,0,0,0,10,5,10,10);
151 FXMatrix * matrix = new FXMatrix(main,2,LAYOUT_FILL_X|MATRIX_BY_COLUMNS);
152 new FXLabel(matrix,fxtr("Location"),nullptr,LABEL_NORMAL|LAYOUT_RIGHT|LAYOUT_CENTER_Y);
153 GMTextField * location_field = new GMTextField(matrix,40,nullptr,0,LAYOUT_FILL_X|LAYOUT_FILL_COLUMN|FRAME_SUNKEN|FRAME_THICK);
154 new FXLabel(matrix,fxtr("Description"),nullptr,LABEL_NORMAL|LAYOUT_RIGHT|LAYOUT_CENTER_Y);
155 GMTextField * description_field = new GMTextField(matrix,30,nullptr,0,LAYOUT_FILL_X|LAYOUT_FILL_COLUMN|FRAME_SUNKEN|FRAME_THICK);
156 new FXLabel(matrix,fxtr("Tag"),nullptr,LABEL_NORMAL|LAYOUT_RIGHT|LAYOUT_CENTER_Y);
157 GMComboBox * tagbox = new GMComboBox(matrix,20,nullptr,0,LAYOUT_FILL_X|FRAME_LINE);
158 db->listTags(tagbox);
159 tagbox->setSortFunc(genre_list_sort);
160 tagbox->setNumVisible(FXMIN(10,tagbox->getNumItems()));
161 tagbox->sortItems();
162 tagbox->setCurrentItem(-1);
163 if (dialog.execute()) {
164 FXString url=location_field->getText().trim();
165 FXString name=description_field->getText().trim();
166 FXString genre=tagbox->getText().trim();
167 if (!url.empty()) {
168 if (genre.empty()) genre=fxtr("Untitled");
169 db->insertStream(url,name,genre);
170 GMPlayerManager::instance()->getTrackView()->refresh();
171 }
172 }
173 return 1;
174 }
175
176
onCmdEditStation(FXObject *,FXSelector,void *)177 long GMStreamSource::onCmdEditStation(FXObject*,FXSelector,void*){
178 GMTextField * location_field=nullptr;
179 FXIntList tracks;
180 GMPlayerManager::instance()->getTrackView()->getSelectedTracks(tracks);
181 FXDialogBox dialog(GMPlayerManager::instance()->getMainWindow(),fxtr("Edit Internet Radio Station"),DECOR_TITLE|DECOR_BORDER|DECOR_RESIZE|DECOR_CLOSE,0,0,0,0,0,0,0,0,0,0);
182 GMPlayerManager::instance()->getMainWindow()->create_dialog_header(&dialog,fxtr("Edit Internet Radio Station"),fxtr("Update url and description of station"),nullptr);
183 FXHorizontalFrame *closebox=new FXHorizontalFrame(&dialog,LAYOUT_SIDE_BOTTOM|LAYOUT_FILL_X|PACK_UNIFORM_WIDTH,0,0,0,0);
184 new GMButton(closebox,fxtr("&Save"),nullptr,&dialog,FXDialogBox::ID_ACCEPT,BUTTON_INITIAL|BUTTON_DEFAULT|LAYOUT_RIGHT|FRAME_RAISED|FRAME_THICK,0,0,0,0, 15,15);
185 new GMButton(closebox,fxtr("&Cancel"),nullptr,&dialog,FXDialogBox::ID_CANCEL,BUTTON_DEFAULT|LAYOUT_RIGHT|FRAME_RAISED|FRAME_THICK,0,0,0,0, 15,15);
186 new FXSeparator(&dialog,SEPARATOR_GROOVE|LAYOUT_FILL_X|LAYOUT_SIDE_BOTTOM);
187 FXVerticalFrame * main = new FXVerticalFrame(&dialog,LAYOUT_FILL_X|LAYOUT_FILL_Y,0,0,0,0,10,5,10,10);
188 FXMatrix * matrix = new FXMatrix(main,2,LAYOUT_FILL_X|MATRIX_BY_COLUMNS);
189 if (tracks.no()==1) {
190 new FXLabel(matrix,fxtr("Location"),nullptr,LABEL_NORMAL|LAYOUT_RIGHT|LAYOUT_CENTER_Y);
191 location_field = new GMTextField(matrix,40,nullptr,0,LAYOUT_FILL_X|LAYOUT_FILL_COLUMN|FRAME_SUNKEN|FRAME_THICK);
192 }
193 new FXLabel(matrix,fxtr("Description"),nullptr,LABEL_NORMAL|LAYOUT_RIGHT|LAYOUT_CENTER_Y);
194 GMTextField * description_field = new GMTextField(matrix,30,nullptr,0,LAYOUT_FILL_X|LAYOUT_FILL_COLUMN|FRAME_SUNKEN|FRAME_THICK);
195 new FXLabel(matrix,fxtr("Genre"),nullptr,LABEL_NORMAL|LAYOUT_RIGHT|LAYOUT_CENTER_Y);
196 GMComboBox * tagbox = new GMComboBox(matrix,20,nullptr,0,LAYOUT_FILL_X|FRAME_LINE);
197 db->listTags(tagbox);
198 tagbox->setSortFunc(genre_list_sort);
199 tagbox->setCurrentItem(-1);
200 tagbox->setNumVisible(FXMIN(10,tagbox->getNumItems()));
201 tagbox->sortItems();
202
203 GMStream info;
204
205 if (tracks.no()==1) {
206 db->getStream(tracks[0],info);
207 location_field->setText(info.url);
208 description_field->setText(info.description);
209 tagbox->setCurrentItem(tagbox->findItem(info.tag));
210 }
211
212 if (dialog.execute()) {
213 FXbool changed=false;
214
215
216 if (tracks.no()==1 && location_field->getText()!=info.url && !location_field->getText().empty()) {
217 db->setStreamFilename(tracks[0],location_field->getText());
218 changed=true;
219 }
220
221 if (description_field->getText()!=info.description && !description_field->getText().empty()){
222 for (FXint i=0;i<tracks.no();i++)
223 db->setStreamDescription(tracks[i],description_field->getText());
224 changed=true;
225 }
226
227 if (tagbox->getText()!=info.tag && !tagbox->getText().empty()){
228 for (FXint i=0;i<tracks.no();i++)
229 db->setStreamGenre(tracks[i],tagbox->getText());
230 changed=true;
231 }
232
233 if (changed) GMPlayerManager::instance()->getTrackView()->refresh();
234
235 }
236 return 1;
237 }
238
onCmdDeleteStation(FXObject *,FXSelector,void *)239 long GMStreamSource::onCmdDeleteStation(FXObject*,FXSelector,void*){
240 const FXString title=fxtr("Remove Internet Radio Station(s)?");
241 const FXString subtitle=fxtr("Remove Internet Radio Station(s) from library?");
242 FXDialogBox dialog(GMPlayerManager::instance()->getMainWindow(),title,DECOR_TITLE|DECOR_BORDER|DECOR_RESIZE|DECOR_CLOSE,0,0,0,0,0,0,0,0,0,0);
243 GMPlayerManager::instance()->getMainWindow()->create_dialog_header(&dialog,title,subtitle,nullptr);
244 FXHorizontalFrame *closebox=new FXHorizontalFrame(&dialog,LAYOUT_SIDE_BOTTOM|LAYOUT_FILL_X|PACK_UNIFORM_WIDTH,0,0,0,0);
245 new GMButton(closebox,fxtr("&Remove"),nullptr,&dialog,FXDialogBox::ID_ACCEPT,BUTTON_INITIAL|BUTTON_DEFAULT|LAYOUT_RIGHT|FRAME_RAISED|FRAME_THICK,0,0,0,0, 15,15);
246 new GMButton(closebox,fxtr("&Cancel"),nullptr,&dialog,FXDialogBox::ID_CANCEL,BUTTON_DEFAULT|LAYOUT_RIGHT|FRAME_RAISED|FRAME_THICK,0,0,0,0, 15,15);
247 // new FXSeparator(&dialog,SEPARATOR_GROOVE|LAYOUT_FILL_X|LAYOUT_SIDE_BOTTOM);
248 if (dialog.execute()){
249 FXIntList tracks;
250 GMPlayerManager::instance()->getTrackView()->getSelectedTracks(tracks);
251 // db->beginDelete();
252
253 for (int i=0;i<tracks.no();i++){
254 if (!db->removeStream(tracks[i])){
255 FXMessageBox::error(GMPlayerManager::instance()->getMainWindow(),MBOX_OK,fxtr("Library Error"),fxtrformat("Unable to remove station from the library."));
256 }
257 }
258
259 // db->endDelete();
260 GMPlayerManager::instance()->getTrackView()->refresh();
261 }
262 return 1;
263 }
264
265
onUpdExport(FXObject * sender,FXSelector,void *)266 long GMStreamSource::onUpdExport(FXObject*sender,FXSelector,void*){
267 sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_DISABLE),nullptr);
268 return 1;
269 }
270
271
272