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