Make browser insensitive when downloading. Fill model, then add to view, for speedup.
[gmpc-shoutcast-browser.git] / shoutbrowser.vala
blobaa72f49b69644fa4c58eef9f9cc4b4fbecd78c3a
1 /* Gnome Music Player Client Shoutcast Browser plugin (gmpc-shoutcast-browser)
2 * Copyright (C) 2010 Anton Romanov <theli.ua@gmail.com>
3 * Project homepage: http://gmpc.wikia.com/
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 using GLib;
20 using Gmpc;
21 using Gmpc.Plugin;
22 using Xml;
24 public class ShoutBrowser : Gmpc.Plugin.Base, Gmpc.Plugin.BrowserIface/*, Gmpc.Plugin.PreferencesIface*/ {
25 public const int[] pversion = {0,0,1};
27 public override void set_enabled(bool enabled) {
28 //var old = this.get_enabled();
29 if(this.get_name() != null)
30 Gmpc.config.set_int(this.get_name(), "enabled", (int)enabled);
32 /* Name */
33 public override unowned string get_name() {
34 return "Shoutcast.com browser";
36 /* Version */
37 public override unowned int[] get_version() {
38 return pversion;
40 construct {
41 /* Set the plugin as an internal one and of type pl_browser */
42 this.plugin_type = 2; //Browser
43 if(this.get_enabled()) {
46 ~ShoutBrowser() {
47 if(this.get_enabled()) {
51 /**
52 * Browser Interface bindings
54 public void browser_add (Gtk.Widget category_tree)
56 Gtk.TreeIter iter;
57 Gtk.TreeView tree = (Gtk.TreeView)category_tree;
58 Gtk.ListStore store = (Gtk.ListStore)tree.get_model();
59 Gmpc.Browser.insert(out iter, config.get_int_with_default(this.get_name(), "position", 100));
60 store.set(iter, 0, this.id, 1, this.get_name(), 3, "gmpc-metabrowser");
61 /* Create a row reference */
63 Gtk.ListStore model = tree.get_model();
64 this.rref = new Gtk.TreeRowReference(model, model.get_path(iter));
67 public void browser_selected (Gtk.Container container)
69 string genre;
70 this.selected = true;
71 this.browser_init();
72 container.add(this.paned);
74 /* update if non selected */
75 genre = browser_get_selected_genre();
76 if(genre == null) {
77 genre_box_clear();
78 genre_box_update();
82 private string? browser_get_selected_genre()
84 Gtk.TreeIter iter;
85 var sel = this.tree_genre.get_selection();
86 Gtk.TreeModel model = null;//this.model_genre;
87 if(sel.get_selected(out model, out iter))
89 string genre = null;
90 model.get(iter, 0,out genre, -1);
91 return genre;
93 return null;
96 private string? browser_get_selected_id()
98 Gtk.TreeIter iter;
99 var sel = this.tree_station.get_selection();
100 Gtk.TreeModel model = null;//this.model_genre;
101 if(sel.get_selected(out model, out iter))
103 string id = null;
104 model.get(iter, 6,out id, -1);
105 return id;
107 return null;
109 private bool downloading = false;
110 private void genre_box_clear()
112 this.model_genre.clear ();
114 private void genre_callback(Gmpc.AsyncDownload.Handle handle, Gmpc.AsyncDownload.Status status){
115 if (status != Gmpc.AsyncDownload.Status.DONE)
116 return;
117 downloading = false;
118 Xml.Doc* doc = Xml.Parser.parse_doc((string)handle.get_data());
119 if (doc == null) {
120 return;
122 // Get the root node. notice the dereferencing operator -> instead of .
123 Xml.Node* node = doc->get_root_element ();
124 if (node == null) {
125 // Free the document manually before returning
126 delete doc;
127 return;
129 Gtk.TreeIter iter;
130 // Loop over the node's children
131 for (Xml.Node* xmliter = node->children; xmliter != null; xmliter = xmliter->next) {
132 // Spaces between tags are also nodes, discard them
133 if (xmliter->type != ElementType.ELEMENT_NODE) {
134 continue;
136 string genre_name = "";
137 for (Xml.Attr* prop = xmliter->properties; prop != null; prop = prop->next) {
138 if (prop->name == "name")
139 genre_name = prop->children->content;
141 if (genre_name != "")
143 this.model_genre.append (out iter);
144 this.model_genre.set (iter, 0, genre_name);
148 // Free the document
149 delete doc;
151 private bool tree_right_menu(Gdk.EventButton event)
153 if(event.button == 3)
155 var menu = new Gtk.Menu();
156 var item = new Gtk.ImageMenuItem.from_stock("gtk-media-play",null);
157 item.activate += station_browser_play_clicked;
158 menu.append(item);
160 item = new Gtk.ImageMenuItem.from_stock("gtk-add",null);
161 item.activate += station_browser_add_clicked;
162 menu.append(item);
164 //item = new Gtk.ImageMenuItem.with_mnemonic("_Replace");
165 //item.set_image(new Gtk.Image.from_stock("gtk-redo", Gtk.IconSize.MENU));
166 //item.activate += replace_clicked;
167 //menu.append(item);
169 menu.popup(null, null, null, event.button, event.time);
170 menu.show_all();
171 return true;
173 return false;
175 private void station_browser_play_clicked(Gtk.ImageMenuItem item)
177 MPD.PlayQueue.clear(server);
178 station_browser_add_clicked(item);
179 MPD.Player.play(server);
181 private void station_browser_add_clicked(Gtk.ImageMenuItem item)
183 var id = browser_get_selected_id();
184 downloading = true;
185 Gmpc.AsyncDownload.download("http://shoutcast.com/sbin/tunein-station.pls?id=" + id,playlist_callback);
187 private void playlist_callback(Gmpc.AsyncDownload.Handle handle, Gmpc.AsyncDownload.Status status){
188 //MPD.PlayQueue.add_song(server,"http://91.121.201.88:8000");
189 downloading = false;
190 if (status != Gmpc.AsyncDownload.Status.DONE)return;
191 var re = new Regex ("File[0-9]+=(.*)", 0,0);
192 MatchInfo mi;
193 if (re.match((string)handle.get_data(),0,out mi))
194 do MPD.PlayQueue.add_song(server,mi.fetch (1)); while (mi.next());
196 private void genre_box_update()
198 if (!downloading)
200 downloading = true;
201 Gmpc.AsyncDownload.download("http://www.shoutcast.com/sbin/newxml.phtml",genre_callback);
205 private void station_callback(Gmpc.AsyncDownload.Handle handle, Gmpc.AsyncDownload.Status status){
206 if (status != Gmpc.AsyncDownload.Status.DONE)
207 return;
208 /* Disconnecting/reconnecting speeds things up */
209 this.tree_station.model = null;
210 downloading = false;
211 Xml.Doc* doc = Xml.Parser.parse_doc((string)handle.get_data());
212 if (doc == null) {
213 return;
215 // Get the root node. notice the dereferencing operator -> instead of .
216 Xml.Node* node = doc->get_root_element ();
217 if (node == null) {
218 // Free the document manually before returning
219 delete doc;
220 return;
222 Gtk.TreeIter iter;
223 // Loop over the node's children
224 for (Xml.Node* xmliter = node->children; xmliter != null; xmliter = xmliter->next) {
225 // Spaces between tags are also nodes, discard them
226 if (xmliter->type != ElementType.ELEMENT_NODE) {
227 continue;
229 string station_name = "";
230 string genre_name = "";
231 string br = "";
232 string mt = "";
233 string ct = "";
234 string id = "";
235 string lc = "";
236 for (Xml.Attr* prop = xmliter->properties; prop != null; prop = prop->next) {
237 if (prop->name == "name")
238 station_name = prop->children->content.replace(" - [SHOUTcast.com]","");
239 else if(prop->name == "genre")
240 genre_name = prop->children->content;
241 else if(prop->name == "br")
242 br = prop->children->content;
243 else if(prop->name == "mt")
244 mt = prop->children->content;
245 else if(prop->name == "ct")
246 ct = prop->children->content;
247 else if(prop->name == "id")
248 id = prop->children->content;
249 else if(prop->name == "lc")
250 lc = prop->children->content;
253 if (station_name != "")
255 this.model_station.append (out iter);
256 this.model_station.set (iter, 0, station_name, 1, ct ,2,lc,3,genre_name, 4, br, 5, mt ,6,id );
259 /* Disconnecting/reconnecting speeds things up */
260 this.tree_station.set_model(this.model_station);
261 tree_station.sensitive = true;
262 tree_genre.sensitive = true;
265 // Free the document
266 delete doc;
268 private void browser_genre_changed(Gtk.TreeSelection sel)
272 string genre = browser_get_selected_genre();
273 if(genre != null)
275 downloading = true;
276 tree_station.sensitive = false;
277 tree_genre.sensitive = false;
278 this.model_station.clear ();
279 Gmpc.AsyncDownload.download("http://www.shoutcast.com/sbin/newxml.phtml?genre=" + genre,station_callback);
286 * UI
289 * */
290 /* genre */
291 private Gtk.TreeView tree_genre = null;
292 private Gtk.ListStore model_genre = null;
293 private Gtk.TreeModelFilter model_filter_genre = null;
294 private Gtk.Entry genre_filter_entry = null;
296 /* station */
297 private Gtk.TreeView tree_station = null;
298 private Gtk.ListStore model_station = null;
299 private Gtk.TreeModelFilter model_filter_station = null;
300 private Gtk.Entry station_filter_entry = null;
302 /* 'base' widget */
303 private Gtk.Paned paned = null;
304 private void browser_init()
306 if(this.paned == null)
308 this.paned = new Gtk.HPaned();
309 paned_size_group.add_paned(this.paned);
312 /* genre list */
313 this.genre_filter_entry = new Gtk.Entry();
314 this.genre_filter_entry.set_no_show_all(true);
315 //this.genre_filter_entry.changed += browser_genre_entry_changed;
316 this.model_genre = new Gtk.ListStore(1,typeof (string));
317 this.model_filter_genre = new Gtk.TreeModelFilter(this.model_genre, null);
318 //this.model_filter_genre.set_visible_func(visible_func_genre);
319 this.tree_genre = new Gtk.TreeView.with_model(this.model_filter_genre);
320 this.tree_genre.rules_hint = true;
321 var box = new Gtk.VBox(false, 2);
322 box.pack_start(this.genre_filter_entry, false, false, 0);
323 var sw = new Gtk.ScrolledWindow(null, null);
324 sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC);
325 sw.set_shadow_type(Gtk.ShadowType.ETCHED_IN);
326 box.pack_start(sw, true, true, 0);
327 this.tree_genre.set_enable_search(false);
328 //this.tree_artist.button_press_event+=browser_button_press_event;
329 //this.tree_artist.button_release_event+=artist_browser_button_release_event;
330 //this.tree_artist.key_press_event += browser_artist_key_press_event;
331 sw.add(tree_genre);
332 this.tree_genre.insert_column_with_attributes (-1, "Genre", new Gtk.CellRendererText (), "text", 0);
333 this.tree_genre.get_selection().changed += browser_genre_changed;
334 this.paned.add1(box);
336 /* Station List */
337 this.station_filter_entry = new Gtk.Entry();
338 this.station_filter_entry.set_no_show_all(true);
339 //this.station_filter_entry.changed += browser_station_entry_changed;
340 this.model_station = new Gtk.ListStore(7,typeof (string),typeof (string),
341 typeof (string),typeof (string),typeof (string),typeof (string),typeof (string));
342 this.model_filter_station = new Gtk.TreeModelFilter(this.model_station, null);
343 //this.model_filter_station.set_visible_func(visible_func_station);
344 this.tree_station = new Gtk.TreeView.with_model(this.model_filter_station);
345 this.tree_station.rules_hint = true;
346 box = new Gtk.VBox(false, 2);
347 box.pack_start(this.station_filter_entry, false, false, 0);
348 sw = new Gtk.ScrolledWindow(null, null);
349 sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC);
350 sw.set_shadow_type(Gtk.ShadowType.ETCHED_IN);
351 box.pack_start(sw, true, true, 0);
352 this.tree_station.set_enable_search(false);
353 //this.tree_artist.button_press_event+=browser_button_press_event;
354 //this.tree_artist.button_release_event+=artist_browser_button_release_event;
355 //this.tree_artist.key_press_event += browser_artist_key_press_event;
356 sw.add(tree_station);
357 this.tree_station.insert_column_with_attributes (-1, "Title", new Gtk.CellRendererText (), "text", 0);
358 this.tree_station.insert_column_with_attributes (-1, "Current Track", new Gtk.CellRendererText (), "text", 1);
359 this.tree_station.insert_column_with_attributes (-1, "Listeners", new Gtk.CellRendererText (), "text", 2);
360 this.tree_station.insert_column_with_attributes (-1, "Genre", new Gtk.CellRendererText (), "text", 3);
361 this.tree_station.insert_column_with_attributes (-1, "Bitrate", new Gtk.CellRendererText (), "text", 4);
362 this.tree_station.insert_column_with_attributes (-1, "Mimetype", new Gtk.CellRendererText (), "text", 5);
363 this.tree_station.insert_column_with_attributes (-1, "ID", new Gtk.CellRendererText (), "text", 6);
365 //this.tree_station.get_selection().changed += browser_station_changed;
366 this.tree_station.button_release_event += tree_right_menu;
368 this.paned.add2(box);
373 this.paned.show_all();
377 private bool selected = false;
378 public void browser_unselected(Gtk.Container container)
380 this.selected = false;
381 container.remove(this.paned);
384 [ModuleInit]
385 public Type plugin_get_type () {
386 return typeof (ShoutBrowser);