3
// LiveRadioPluginSourceContents.cs
6
// Frank Ziegler <funtastix@googlemail.com>
7
// Aaron Bockover <abockover@novell.com>
9
// Copyright (C) 2008 Novell, Inc.
10
// Copyright (C) 2010 Frank Ziegler
12
// Permission is hereby granted, free of charge, to any person obtaining
13
// a copy of this software and associated documentation files (the
14
// "Software"), to deal in the Software without restriction, including
15
// without limitation the rights to use, copy, modify, merge, publish,
16
// distribute, sublicense, and/or sell copies of the Software, and to
17
// permit persons to whom the Software is furnished to do so, subject to
18
// the following conditions:
20
// The above copyright notice and this permission notice shall be
21
// included in all copies or substantial portions of the Software.
23
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33
using System.Collections.Generic;
36
using ScrolledWindow = Gtk.ScrolledWindow;
44
using Banshee.Configuration;
45
using Banshee.Sources;
46
using Banshee.Sources.Gui;
47
using Banshee.ServiceStack;
48
using Banshee.Collection;
49
using Banshee.Collection.Database;
50
using Banshee.Collection.Gui;
52
using Banshee.LiveRadio.Plugins;
54
namespace Banshee.LiveRadio
58
/// The source contents for a plugin source. It creates a view with a genre choose box, a query field
59
/// and the view for the retrieved station tracks.
60
/// The source contents is also the connector between plugin and source, as it handles just about all
61
/// refresh/notify events coming from either side.
63
public class LiveRadioPluginSourceContents : VBox, ISourceContents, ITrackModelSourceContents
65
private TrackListView track_view;
66
private Gtk.ScrolledWindow main_scrolled_window;
67
protected ISource source;
69
private Paned container;
70
private Widget browser_container;
71
private InterfaceActionService action_service;
72
private ActionGroup browser_view_actions;
74
private ILiveRadioPlugin plugin;
75
private LiveRadioFilterView filter_box;
77
private static string menu_xml = @"
79
<menubar name=""MainMenu"">
80
<menu name=""ViewMenu"" action=""ViewMenuAction"">
81
<placeholder name=""BrowserViews"">
82
<menuitem name=""BrowserVisible"" action=""BrowserVisibleAction"" />
84
<menuitem name=""BrowserTop"" action=""BrowserTopAction"" />
85
<menuitem name=""BrowserLeft"" action=""BrowserLeftAction"" />
94
/// Constructor -- creates the source contents for a plugin and sets up the event handlers for the view
95
/// and the plugin refresh events.
97
/// <param name="plugin">
98
/// A <see cref="ILiveRadioPlugin"/> -- the plugin to set up the source contents for.
100
public LiveRadioPluginSourceContents (ILiveRadioPlugin plugin)
102
base.Name = plugin.Name;
103
this.plugin = plugin;
107
string position = ForcePosition == null ? BrowserPosition.Get () : ForcePosition;
108
if (position == "top") {
114
plugin.GenreListLoaded += OnPluginGenreListLoaded;
115
plugin.RequestResultRetrieved += OnPluginRequestResultRetrieved;
117
if (ForcePosition != null) {
121
if (ServiceManager.Contains ("InterfaceActionService")) {
122
action_service = ServiceManager.Get<InterfaceActionService> ();
124
if (action_service.FindActionGroup ("BrowserView") == null) {
125
browser_view_actions = new ActionGroup ("BrowserView");
127
browser_view_actions.Add (new RadioActionEntry [] {
128
new RadioActionEntry ("BrowserLeftAction", null,
129
AddinManager.CurrentLocalizer.GetString ("Browser on Left"), null,
130
AddinManager.CurrentLocalizer.GetString ("Show the artist/album browser to the left of the track list"), 0),
132
new RadioActionEntry ("BrowserTopAction", null,
133
AddinManager.CurrentLocalizer.GetString ("Browser on Top"), null,
134
AddinManager.CurrentLocalizer.GetString ("Show the artist/album browser above the track list"), 1),
135
}, position == "top" ? 1 : 0, null);
137
browser_view_actions.Add (new ToggleActionEntry [] {
138
new ToggleActionEntry ("BrowserVisibleAction", null,
139
AddinManager.CurrentLocalizer.GetString ("Show Browser"), "<control>B",
140
AddinManager.CurrentLocalizer.GetString ("Show or hide the artist/album browser"),
141
null, BrowserVisible.Get ())
145
action_service.AddActionGroup (browser_view_actions);
146
action_service.UIManager.AddUiFromString (menu_xml);
149
(action_service.FindAction ("BrowserView.BrowserLeftAction") as RadioAction).Changed += OnViewModeChanged;
150
(action_service.FindAction ("BrowserView.BrowserTopAction") as RadioAction).Changed += OnViewModeChanged;
151
action_service.FindAction ("BrowserView.BrowserVisibleAction").Activated += OnToggleBrowser;
154
ServiceManager.SourceManager.ActiveSourceChanged += delegate {
155
ThreadAssist.ProxyToMain (delegate {
156
browser_container.Visible = ActiveSourceCanHasBrowser ? BrowserVisible.Get () : false;
165
/// Initiate a refesh of the contents: clear the genre list, add a fake "loading" entry and
166
/// prohibit interaction with the elements
168
public void InitRefresh ()
170
main_scrolled_window.Sensitive = false;
171
List<Genre> fakeresult = new List<Genre> ();
172
fakeresult.Add (new Genre(AddinManager.CurrentLocalizer.GetString("Loading...")));
173
filter_box.UpdateGenres (fakeresult);
174
filter_box.Sensitive = false;
178
/// Handles when a new result for a previous query request is received from the corresponding plugin. Transfers the
179
/// received result to the plugin's source, and if there are none, set up a fake entry.
181
/// <param name="sender">
182
/// A <see cref="System.Object"/> -- The plugin that sent the results. Implements ILiveRadioPlugin.
184
/// <param name="request">
185
/// A <see cref="System.String"/> -- The requested query string, either a genre name or the freetext (see request_type).
187
/// <param name="request_type">
188
/// A <see cref="LiveRadioRequestType"/> -- The type of the request.
190
/// <param name="result">
191
/// A <see cref="List<DatabaseTrackInfo>"/> -- A list of DatabaseTrackInfo objects that fulfil the query.
193
void OnPluginRequestResultRetrieved (object sender,
195
LiveRadioRequestType request_type,
196
List<DatabaseTrackInfo> result)
198
if ((request_type == LiveRadioRequestType.ByFreetext)
199
|| (filter_box.GetSelectedGenre ().Name.Equals (request) && request_type == LiveRadioRequestType.ByGenre))
203
SetFakeTrack (AddinManager.CurrentLocalizer.GetString("Error... Please Reload"));
204
} else if (result.Count > 0) {
205
plugin.PluginSource.SetStations (result);
206
main_scrolled_window.Sensitive = true;
208
string message = (request_type == LiveRadioRequestType.ByGenre ? "Error... Please Reload" : "No Results");
209
SetFakeTrack (AddinManager.CurrentLocalizer.GetString(message));
215
/// Adds a fake track to the source and disables interaction with the track view.
217
/// <param name="info">
218
/// A <see cref="System.String"/> -- The information to display will be set as the fake track title and artist.
220
protected void SetFakeTrack (string info)
222
List<DatabaseTrackInfo> fakeresult = new List<DatabaseTrackInfo> ();
223
DatabaseTrackInfo faketrack = new DatabaseTrackInfo ();
224
faketrack.TrackTitle = info;
225
faketrack.ArtistName = info;
226
faketrack.AlbumArtist = info;
227
faketrack.Uri = new SafeUri ("http://test.com/test.pls");
229
fakeresult.Add (faketrack);
230
plugin.PluginSource.SetStations (fakeresult);
231
main_scrolled_window.Sensitive = false;
235
/// Handles when a genre list has been retrieved by the plugin. Fills the genre choose box with the results or
236
/// adds a info message in case of an empty result and disables interaction with the control.
238
/// <param name="sender">
239
/// A <see cref="System.Object"/>
241
/// <param name="genres">
242
/// A <see cref="List<Genre>"/>
244
void OnPluginGenreListLoaded (object sender, List<Genre> genres)
246
if (genres.Count > 0) {
247
filter_box.UpdateGenres (genres);
248
filter_box.Sensitive = true;
250
List<Genre> fakeresult = new List<Genre> ();
251
fakeresult.Add (new Genre(AddinManager.CurrentLocalizer.GetString("Error... Please Reload")));
252
filter_box.UpdateGenres (fakeresult);
253
filter_box.Sensitive = false;
258
/// Initialize the main track view
260
protected void InitializeViews ()
262
SetupMainView (track_view = new TrackListView ());
267
/// Setup the main track view and disable interaction
269
/// <param name="main_view">
270
/// A <see cref="ListView<T>"/> -- the main track view to set up
272
protected void SetupMainView<T> (ListView<T> main_view)
274
main_scrolled_window = SetupView (main_view);
275
main_scrolled_window.Sensitive = false;
279
/// Capsules a widget in a scrolled window to add scrolling.
281
/// <param name="view">
282
/// A <see cref="Widget"/> -- the view to capsule
285
/// A <see cref="ScrolledWindow"/> -- the scrolled window containing the capsuled view
287
private ScrolledWindow SetupView (Widget view)
289
ScrolledWindow window = null;
291
if (ApplicationContext.CommandLine.Contains ("smooth-scroll")) {
292
window = new SmoothScrolledWindow ();
294
window = new ScrolledWindow ();
298
window.HscrollbarPolicy = PolicyType.Automatic;
299
window.VscrollbarPolicy = PolicyType.Automatic;
305
/// Unparent the views' scrolled window parents so they can be re-packed in
306
/// a new layout. The main container gets destroyed since it will be recreated.
308
private void Reset ()
310
if (container != null && main_scrolled_window != null) {
311
container.Remove (main_scrolled_window);
314
if (container != null) {
320
/// Make a layout with the filter views on the left hand side
322
private void LayoutLeft ()
328
/// Make a layout with the filter views on top
330
private void LayoutTop ()
336
/// Create the contents of the source contents in the desired layout.
338
/// <param name="top">
339
/// A <see cref="System.Boolean"/> -- whether to create a LayoutTop (true) or a LayoutLeft (false)
341
private void Layout (bool top)
345
container = GetPane (!top);
346
filter_box = new LiveRadioFilterView ();
347
filter_box.Sensitive = false;
348
filter_box.GenreSelected += OnViewGenreSelected;
349
filter_box.GenreActivated += OnViewGenreSelected;
350
filter_box.QuerySent += OnViewQuerySent;
352
container.Pack1 (filter_box, false, false);
353
container.Pack2 (main_scrolled_window, true, false);
354
browser_container = filter_box;
356
container.Position = top ? 175 : 275;
361
/// Handles when the user sends a query through the Entry box of the UI and initiates request execution
362
/// in the plugin object. The track view is reset with a fake entry.
364
/// <param name="sender">
365
/// A <see cref="System.Object"/> -- the sender of the event
367
/// <param name="query">
368
/// A <see cref="System.String"/> -- the query string the user entered
370
void OnViewQuerySent (object sender, string query)
372
SetFakeTrack (AddinManager.CurrentLocalizer.GetString("Loading..."));
373
plugin.ExecuteRequest (LiveRadioRequestType.ByFreetext, query);
377
/// Handles when the user activates a genre in the genre choose box and initiates request execution
378
/// in the plugin object. The track view is reset with a fake entry.
380
/// <param name="sender">
381
/// A <see cref="System.Object"/> -- the sender of the event
383
/// <param name="genre">
384
/// A <see cref="Genre"/> -- the genre the user selected
386
void OnViewGenreSelected (object sender, Genre genre)
388
SetFakeTrack (AddinManager.CurrentLocalizer.GetString("Loading..."));
389
plugin.ExecuteRequest (LiveRadioRequestType.ByGenre, genre.Name);
392
/// Draw the source contents
394
private void ShowPack ()
396
PackStart (container, true, true, 0);
397
VBox instruct = new VBox ();
398
//instruct.ExposeEvent += (o, a) => {
399
// using (Cairo.Context cr = Gdk.CairoHelper.Create (instruct.GdkWindow)) {
400
// double radius = 10;
401
// int x = a.Event.Area.X;
402
// int y = a.Event.Area.Y;
403
// int width = a.Event.Area.Width;
404
// int height = a.Event.Area.Height;
406
// cr.MoveTo (x + radius, y);
407
// cr.Arc (x + width - radius, y + radius, radius, Math.PI * 1.5, Math.PI * 2);
408
// cr.Arc (x + width - radius, y + height - radius, radius, 0, Math.PI * .5);
409
// cr.Arc (x + radius, y + height - radius, radius, Math.PI * .5, Math.PI);
410
// cr.Arc (x + radius, y + radius, radius, Math.PI, Math.PI * 1.5);
411
// cr.Color = new Cairo.Color (.5, .5, .5, 1);
414
// not yet sure which is the better code.
415
// Gdk.Window win = a.Event.Window;
416
// Gdk.Rectangle area = a.Event.Area;
418
// win.DrawRectangle (Style.BaseGC (StateType.Active), false, area);
422
Label help_label = new Label (
423
AddinManager.CurrentLocalizer.GetString ("Click a genre to load/refresh entries or type a query. Use the refresh button to refresh genres."));
424
instruct.PackStart(help_label, false, true, 10);
425
PackStart (instruct, false, true, 10);
429
browser_container.Visible = ForcePosition != null || BrowserVisible.Get ();
433
/// Helper function to make a new Paned with the correct layout
435
/// <param name="hpane">
436
/// A <see cref="System.Boolean"/>
439
/// A <see cref="Paned"/>
441
private Paned GetPane (bool hpane)
444
return new HPaned ();
446
return new VPaned ();
450
/// Handles when the user selects a different view mode.
453
/// A <see cref="System.Object"/>
455
/// <param name="args">
456
/// A <see cref="ChangedArgs"/>
458
private void OnViewModeChanged (object o, ChangedArgs args)
460
if (args.Current.Value == 0) {
462
BrowserPosition.Set ("left");
465
BrowserPosition.Set ("top");
470
/// Handles when browser visibility is toggled
473
/// A <see cref="System.Object"/>
475
/// <param name="args">
476
/// A <see cref="EventArgs"/>
478
private void OnToggleBrowser (object o, EventArgs args)
480
ToggleAction action = (ToggleAction)o;
482
browser_container.Visible = action.Active && ActiveSourceCanHasBrowser;
483
BrowserVisible.Set (action.Active);
487
protected bool ActiveSourceCanHasBrowser {
491
protected string ForcePosition {
492
get { return "bottom"; }
495
#region Implement ISourceContents
498
/// Sets the source and the track model for the source contents
500
/// <param name="source">
501
/// A <see cref="ISource"/>
504
/// A <see cref="System.Boolean"/>
506
public bool SetSource (ISource source)
508
DatabaseSource track_source = source as DatabaseSource;
509
if (track_source == null) {
513
this.source = source;
515
track_view.SetModel (track_source.TrackModel);
521
/// Sets the source and track model references to null
523
public void ResetSource ()
526
track_view.SetModel (null);
530
/// The ISource source of this source contents
532
public ISource Source {
533
get { return source; }
537
/// The Widget of the source contents
539
public Widget Widget {
545
#region ITrackModelSourceContents implementation
548
/// returns the track view of this source contents
550
public IListView<TrackInfo> TrackView {
551
get { return track_view; }
556
public static readonly SchemaEntry<bool> BrowserVisible = new SchemaEntry<bool> (
557
"browser", "visible",
559
"Artist/Album Browser Visibility",
560
"Whether or not to show the Artist/Album browser"
563
public static readonly SchemaEntry<string> BrowserPosition = new SchemaEntry<string> (
564
"browser", "position",
566
"Artist/Album Browser Position",
567
"The position of the Artist/Album browser; either 'top' or 'left'"