Merge pull request #2023 from jmarshallnz/allow_zero_auto_width_labels
[vuplus_xbmc] / xbmc / PlayListPlayer.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://www.xbmc.org
4  *
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, or (at your option)
8  *  any later version.
9  *
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.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, see
17  *  <http://www.gnu.org/licenses/>.
18  *
19  */
20
21 #include "threads/SystemClock.h"
22 #include "PlayListPlayer.h"
23 #include "playlists/PlayListFactory.h"
24 #include "Application.h"
25 #include "PartyModeManager.h"
26 #include "settings/AdvancedSettings.h"
27 #include "GUIUserMessages.h"
28 #include "guilib/GUIWindowManager.h"
29 #include "dialogs/GUIDialogOK.h"
30 #include "playlists/PlayList.h"
31 #include "utils/log.h"
32 #include "utils/TimeUtils.h"
33 #include "music/tags/MusicInfoTag.h"
34 #include "dialogs/GUIDialogKaiToast.h"
35 #include "guilib/LocalizeStrings.h"
36 #include "interfaces/AnnouncementManager.h"
37
38 using namespace PLAYLIST;
39
40 CPlayListPlayer::CPlayListPlayer(void)
41 {
42   m_PlaylistMusic = new CPlayList(PLAYLIST_MUSIC);
43   m_PlaylistVideo = new CPlayList(PLAYLIST_VIDEO);
44   m_PlaylistEmpty = new CPlayList;
45   m_iCurrentSong = -1;
46   m_bPlayedFirstFile = false;
47   m_bPlaybackStarted = false;
48   m_iCurrentPlayList = PLAYLIST_NONE;
49   for (int i = 0; i < 2; i++)
50     m_repeatState[i] = REPEAT_NONE;
51   m_iFailedSongs = 0;
52   m_failedSongsStart = 0;
53 }
54
55 CPlayListPlayer::~CPlayListPlayer(void)
56 {
57   Clear();
58   delete m_PlaylistMusic;
59   delete m_PlaylistVideo;
60   delete m_PlaylistEmpty;
61 }
62
63 bool CPlayListPlayer::OnMessage(CGUIMessage &message)
64 {
65   switch (message.GetMessage())
66   {
67   case GUI_MSG_NOTIFY_ALL:
68     if (message.GetParam1() == GUI_MSG_UPDATE_ITEM && message.GetItem())
69     {
70       // update the items in our playlist(s) if necessary
71       for (int i = PLAYLIST_MUSIC; i <= PLAYLIST_VIDEO; i++)
72       {
73         CPlayList &playlist = GetPlaylist(i);
74         CFileItemPtr item = boost::static_pointer_cast<CFileItem>(message.GetItem());
75         playlist.UpdateItem(item.get());
76       }
77     }
78     break;
79   case GUI_MSG_PLAYBACK_STOPPED:
80     {
81       if (m_iCurrentPlayList != PLAYLIST_NONE && m_bPlaybackStarted)
82       {
83         CGUIMessage msg(GUI_MSG_PLAYLISTPLAYER_STOPPED, 0, 0, m_iCurrentPlayList, m_iCurrentSong);
84         g_windowManager.SendThreadMessage(msg);
85         Reset();
86         m_iCurrentPlayList = PLAYLIST_NONE;
87         return true;
88       }
89     }
90     break;
91   }
92
93   return false;
94 }
95
96 int CPlayListPlayer::GetNextSong(int offset) const
97 {
98   if (m_iCurrentPlayList == PLAYLIST_NONE)
99     return -1;
100
101   const CPlayList& playlist = GetPlaylist(m_iCurrentPlayList);
102   if (playlist.size() <= 0)
103     return -1;
104
105   int song = m_iCurrentSong;
106
107   // party mode
108   if (g_partyModeManager.IsEnabled() && GetCurrentPlaylist() == PLAYLIST_MUSIC)
109     return song + offset;
110
111   // wrap around in the case of repeating
112   if (RepeatedOne(m_iCurrentPlayList))
113     return song;
114
115   song += offset;
116   if (song >= playlist.size() && Repeated(m_iCurrentPlayList))
117     song %= playlist.size();
118
119   return song;
120 }
121
122 int CPlayListPlayer::GetNextSong()
123 {
124   if (m_iCurrentPlayList == PLAYLIST_NONE)
125     return -1;
126   CPlayList& playlist = GetPlaylist(m_iCurrentPlayList);
127   if (playlist.size() <= 0)
128     return -1;
129   int iSong = m_iCurrentSong;
130
131   // party mode
132   if (g_partyModeManager.IsEnabled() && GetCurrentPlaylist() == PLAYLIST_MUSIC)
133     return iSong + 1;
134
135   // if repeat one, keep playing the current song if its valid
136   if (RepeatedOne(m_iCurrentPlayList))
137   {
138     // otherwise immediately abort playback
139     if (m_iCurrentSong >= 0 && m_iCurrentSong < playlist.size() && playlist[m_iCurrentSong]->GetProperty("unplayable").asBoolean())
140     {
141       CLog::Log(LOGERROR,"Playlist Player: RepeatOne stuck on unplayable item: %i, path [%s]", m_iCurrentSong, playlist[m_iCurrentSong]->GetPath().c_str());
142       CGUIMessage msg(GUI_MSG_PLAYLISTPLAYER_STOPPED, 0, 0, m_iCurrentPlayList, m_iCurrentSong);
143       g_windowManager.SendThreadMessage(msg);
144       Reset();
145       m_iCurrentPlayList = PLAYLIST_NONE;
146       return -1;
147     }
148     return iSong;
149   }
150
151   // if we've gone beyond the playlist and repeat all is enabled,
152   // then we clear played status and wrap around
153   iSong++;
154   if (iSong >= playlist.size() && Repeated(m_iCurrentPlayList))
155     iSong = 0;
156
157   return iSong;
158 }
159
160 bool CPlayListPlayer::PlayNext(int offset, bool bAutoPlay)
161 {
162   int iSong = GetNextSong(offset);
163   CPlayList& playlist = GetPlaylist(m_iCurrentPlayList);
164
165   if ((iSong < 0) || (iSong >= playlist.size()) || (playlist.GetPlayable() <= 0))
166   {
167     if(!bAutoPlay)
168       CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(559), g_localizeStrings.Get(34201));
169
170     CGUIMessage msg(GUI_MSG_PLAYLISTPLAYER_STOPPED, 0, 0, m_iCurrentPlayList, m_iCurrentSong);
171     g_windowManager.SendThreadMessage(msg);
172     Reset();
173     m_iCurrentPlayList = PLAYLIST_NONE;
174     return false;
175   }
176
177   return Play(iSong, false);
178 }
179
180 bool CPlayListPlayer::PlayPrevious()
181 {
182   if (m_iCurrentPlayList == PLAYLIST_NONE)
183     return false;
184
185   CPlayList& playlist = GetPlaylist(m_iCurrentPlayList);
186   int iSong = m_iCurrentSong;
187
188   if (!RepeatedOne(m_iCurrentPlayList))
189     iSong--;
190
191   if (iSong < 0 && Repeated(m_iCurrentPlayList))
192     iSong = playlist.size() - 1;
193
194   if (iSong < 0 || playlist.size() <= 0)
195   {
196     CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(559), g_localizeStrings.Get(34202));
197     return false;
198   }
199
200   return Play(iSong, false, true);
201 }
202
203 bool CPlayListPlayer::Play()
204 {
205   if (m_iCurrentPlayList == PLAYLIST_NONE)
206     return false;
207
208   CPlayList& playlist = GetPlaylist(m_iCurrentPlayList);
209   if (playlist.size() <= 0) 
210     return false;
211
212   return Play(0);
213 }
214
215 bool CPlayListPlayer::PlaySongId(int songId)
216 {
217   if (m_iCurrentPlayList == PLAYLIST_NONE)
218     return false;
219
220   CPlayList& playlist = GetPlaylist(m_iCurrentPlayList);
221   if (playlist.size() <= 0) 
222     return Play();
223
224   for (int i = 0; i < playlist.size(); i++)
225   {
226     if (playlist[i]->HasMusicInfoTag() && playlist[i]->GetMusicInfoTag()->GetDatabaseId() == songId)
227       return Play(i);
228   }
229   return Play();
230 }
231
232 bool CPlayListPlayer::Play(int iSong, bool bAutoPlay /* = false */, bool bPlayPrevious /* = false */)
233 {
234   if (m_iCurrentPlayList == PLAYLIST_NONE)
235     return false;
236
237   CPlayList& playlist = GetPlaylist(m_iCurrentPlayList);
238   if (playlist.size() <= 0) 
239     return false;
240   if (iSong < 0) 
241     iSong = 0;
242   if (iSong >= playlist.size()) 
243     iSong = playlist.size() - 1;
244
245   // check if the item itself is a playlist, and can be expanded
246   // only allow a few levels, this could end up in a loop
247   // if they refer to each other in a loop
248   for(int i=0;i<5;i++)
249   {
250      if(!playlist.Expand(iSong))
251         break;
252   }
253
254   m_iCurrentSong = iSong;
255   CFileItemPtr item = playlist[m_iCurrentSong];
256   playlist.SetPlayed(true);
257
258   m_bPlaybackStarted = false;
259
260   unsigned int playAttempt = XbmcThreads::SystemClockMillis();
261   if (!g_application.PlayFile(*item, bAutoPlay))
262   {
263     CLog::Log(LOGERROR,"Playlist Player: skipping unplayable item: %i, path [%s]", m_iCurrentSong, item->GetPath().c_str());
264     playlist.SetUnPlayable(m_iCurrentSong);
265
266     // abort on 100 failed CONSECTUTIVE songs
267     if (!m_iFailedSongs)
268       m_failedSongsStart = playAttempt;
269     m_iFailedSongs++;
270     if ((m_iFailedSongs >= g_advancedSettings.m_playlistRetries && g_advancedSettings.m_playlistRetries >= 0)
271         || ((XbmcThreads::SystemClockMillis() - m_failedSongsStart  >= (unsigned int)g_advancedSettings.m_playlistTimeout * 1000) && g_advancedSettings.m_playlistTimeout))
272     {
273       CLog::Log(LOGDEBUG,"Playlist Player: one or more items failed to play... aborting playback");
274
275       // open error dialog
276       CGUIDialogOK::ShowAndGetInput(16026, 16027, 16029, 0);
277
278       CGUIMessage msg(GUI_MSG_PLAYLISTPLAYER_STOPPED, 0, 0, m_iCurrentPlayList, m_iCurrentSong);
279       g_windowManager.SendThreadMessage(msg);
280       Reset();
281       GetPlaylist(m_iCurrentPlayList).Clear();
282       m_iCurrentPlayList = PLAYLIST_NONE;
283       m_iFailedSongs = 0;
284       m_failedSongsStart = 0;
285       return false;
286     }
287
288     // how many playable items are in the playlist?
289     if (playlist.GetPlayable() > 0)
290     {
291       return bPlayPrevious ? PlayPrevious() : PlayNext();
292     }
293     // none? then abort playback
294     else
295     {
296       CLog::Log(LOGDEBUG,"Playlist Player: no more playable items... aborting playback");
297       CGUIMessage msg(GUI_MSG_PLAYLISTPLAYER_STOPPED, 0, 0, m_iCurrentPlayList, m_iCurrentSong);
298       g_windowManager.SendThreadMessage(msg);
299       Reset();
300       m_iCurrentPlayList = PLAYLIST_NONE;
301       return false;
302     }
303   }
304
305   // reset the start offset of this item
306   if (item->m_lStartOffset == STARTOFFSET_RESUME)
307     item->m_lStartOffset = 0;
308
309   // TODO - move the above failure logic and the below success logic
310   //        to callbacks instead so we don't rely on the return value
311   //        of PlayFile()
312
313   // consecutive error counter so reset if the current item is playing
314   m_iFailedSongs = 0;
315   m_failedSongsStart = 0;
316   m_bPlaybackStarted = true;
317   m_bPlayedFirstFile = true;
318   return true;
319 }
320
321 void CPlayListPlayer::SetCurrentSong(int iSong)
322 {
323   if (iSong >= -1 && iSong < GetPlaylist(m_iCurrentPlayList).size())
324     m_iCurrentSong = iSong;
325 }
326
327 int CPlayListPlayer::GetCurrentSong() const
328 {
329   return m_iCurrentSong;
330 }
331
332 int CPlayListPlayer::GetCurrentPlaylist() const
333 {
334   return m_iCurrentPlayList;
335 }
336
337 void CPlayListPlayer::SetCurrentPlaylist(int iPlaylist)
338 {
339   if (iPlaylist == m_iCurrentPlayList)
340     return;
341
342   // changing the current playlist while party mode is on
343   // disables party mode
344   if (g_partyModeManager.IsEnabled())
345     g_partyModeManager.Disable();
346
347   m_iCurrentPlayList = iPlaylist;
348   m_bPlayedFirstFile = false;
349 }
350
351 void CPlayListPlayer::ClearPlaylist(int iPlaylist)
352 {
353   // clear our applications playlist file
354   g_application.m_strPlayListFile.Empty();
355
356   CPlayList& playlist = GetPlaylist(iPlaylist);
357   playlist.Clear();
358
359   // its likely that the playlist changed
360   CGUIMessage msg(GUI_MSG_PLAYLIST_CHANGED, 0, 0);
361   g_windowManager.SendMessage(msg);
362 }
363
364 CPlayList& CPlayListPlayer::GetPlaylist(int iPlaylist)
365 {
366   switch ( iPlaylist )
367   {
368   case PLAYLIST_MUSIC:
369     return *m_PlaylistMusic;
370     break;
371   case PLAYLIST_VIDEO:
372     return *m_PlaylistVideo;
373     break;
374   default:
375     m_PlaylistEmpty->Clear();
376     return *m_PlaylistEmpty;
377     break;
378   }
379 }
380
381 const CPlayList& CPlayListPlayer::GetPlaylist(int iPlaylist) const
382 {
383   switch ( iPlaylist )
384   {
385   case PLAYLIST_MUSIC:
386     return *m_PlaylistMusic;
387     break;
388   case PLAYLIST_VIDEO:
389     return *m_PlaylistVideo;
390     break;
391   default:
392     // NOTE: This playlist may not be empty if the caller of the non-const version alters it!
393     return *m_PlaylistEmpty;
394     break;
395   }
396 }
397
398 int CPlayListPlayer::RemoveDVDItems()
399 {
400   int nRemovedM = m_PlaylistMusic->RemoveDVDItems();
401   int nRemovedV = m_PlaylistVideo->RemoveDVDItems();
402
403   return nRemovedM + nRemovedV;
404 }
405
406 void CPlayListPlayer::Reset()
407 {
408   m_iCurrentSong = -1;
409   m_bPlayedFirstFile = false;
410   m_bPlaybackStarted = false;
411
412   // its likely that the playlist changed
413   CGUIMessage msg(GUI_MSG_PLAYLIST_CHANGED, 0, 0);
414   g_windowManager.SendMessage(msg);
415 }
416
417 bool CPlayListPlayer::HasPlayedFirstFile() const
418 {
419   return m_bPlayedFirstFile;
420 }
421
422 bool CPlayListPlayer::Repeated(int iPlaylist) const
423 {
424   if (iPlaylist >= PLAYLIST_MUSIC && iPlaylist <= PLAYLIST_VIDEO)
425     return (m_repeatState[iPlaylist] == REPEAT_ALL);
426   return false;
427 }
428
429 bool CPlayListPlayer::RepeatedOne(int iPlaylist) const
430 {
431   if (iPlaylist == PLAYLIST_MUSIC || iPlaylist == PLAYLIST_VIDEO)
432     return (m_repeatState[iPlaylist] == REPEAT_ONE);
433   return false;
434 }
435
436 void CPlayListPlayer::SetShuffle(int iPlaylist, bool bYesNo, bool bNotify /* = false */)
437 {
438   if (iPlaylist != PLAYLIST_MUSIC && iPlaylist != PLAYLIST_VIDEO)
439     return;
440
441   // disable shuffle in party mode
442   if (g_partyModeManager.IsEnabled() && iPlaylist == PLAYLIST_MUSIC)
443     return;
444
445   // do we even need to do anything?
446   if (bYesNo != IsShuffled(iPlaylist))
447   {
448     // save the order value of the current song so we can use it find its new location later
449     int iOrder = -1;
450     CPlayList &playlist = GetPlaylist(iPlaylist);
451     if (m_iCurrentSong >= 0 && m_iCurrentSong < playlist.size())
452       iOrder = playlist[m_iCurrentSong]->m_iprogramCount;
453
454     // shuffle or unshuffle as necessary
455     if (bYesNo)
456       playlist.Shuffle();
457     else
458       playlist.UnShuffle();
459
460     if (bNotify)
461     {
462       CStdString shuffleStr;
463       shuffleStr.Format("%s: %s", g_localizeStrings.Get(191), g_localizeStrings.Get(bYesNo ? 593 : 591)); // Shuffle: All/Off
464       CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(559),  shuffleStr);
465     }
466
467     // find the previous order value and fix the current song marker
468     if (iOrder >= 0)
469     {
470       int iIndex = playlist.FindOrder(iOrder);
471       if (iIndex >= 0)
472         m_iCurrentSong = iIndex;
473       // if iIndex < 0, something unexpected happened
474       // so dont do anything
475     }
476   }
477   
478   AnnouncePropertyChanged(iPlaylist, "shuffled", IsShuffled(iPlaylist));
479 }
480
481 bool CPlayListPlayer::IsShuffled(int iPlaylist) const
482 {
483   // even if shuffled, party mode says its not
484   if (g_partyModeManager.IsEnabled() && iPlaylist == PLAYLIST_MUSIC)
485     return false;
486
487   if (iPlaylist == PLAYLIST_MUSIC || iPlaylist == PLAYLIST_VIDEO)
488     return GetPlaylist(iPlaylist).IsShuffled();
489
490   return false;
491 }
492
493 void CPlayListPlayer::SetRepeat(int iPlaylist, REPEAT_STATE state, bool bNotify /* = false */)
494 {
495   if (iPlaylist != PLAYLIST_MUSIC && iPlaylist != PLAYLIST_VIDEO)
496     return;
497
498   // disable repeat in party mode
499   if (g_partyModeManager.IsEnabled() && iPlaylist == PLAYLIST_MUSIC)
500     state = REPEAT_NONE;
501
502   // notify the user if there was a change in the repeat state
503   if (m_repeatState[iPlaylist] != state && bNotify)
504   {
505     int iLocalizedString;
506     if (state == REPEAT_NONE)
507       iLocalizedString = 595; // Repeat: Off
508     else if (state == REPEAT_ONE)
509       iLocalizedString = 596; // Repeat: One
510     else
511       iLocalizedString = 597; // Repeat: All
512     CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(559), g_localizeStrings.Get(iLocalizedString));
513   }
514
515   m_repeatState[iPlaylist] = state;
516
517   CVariant data;
518   switch (state)
519   {
520   case REPEAT_ONE:
521     data = "one";
522     break;
523   case REPEAT_ALL:
524     data = "all";
525     break;
526   default:
527     data = "off";
528     break;
529   }
530   AnnouncePropertyChanged(iPlaylist, "repeat", data);
531 }
532
533 REPEAT_STATE CPlayListPlayer::GetRepeat(int iPlaylist) const
534 {
535   if (iPlaylist == PLAYLIST_MUSIC || iPlaylist == PLAYLIST_VIDEO)
536     return m_repeatState[iPlaylist];
537   return REPEAT_NONE;
538 }
539
540 void CPlayListPlayer::ReShuffle(int iPlaylist, int iPosition)
541 {
542   // playlist has not played yet so shuffle the entire list
543   // (this only really works for new video playlists)
544   if (!GetPlaylist(iPlaylist).WasPlayed())
545   {
546     GetPlaylist(iPlaylist).Shuffle();
547   }
548   // we're trying to shuffle new items into the curently playing playlist
549   // so we shuffle starting at two positions below the current item
550   else if (iPlaylist == m_iCurrentPlayList)
551   {
552     if (
553       (g_application.IsPlayingAudio() && iPlaylist == PLAYLIST_MUSIC) ||
554       (g_application.IsPlayingVideo() && iPlaylist == PLAYLIST_VIDEO)
555       )
556     {
557       g_playlistPlayer.GetPlaylist(iPlaylist).Shuffle(m_iCurrentSong + 2);
558     }
559   }
560   // otherwise, shuffle from the passed position
561   // which is the position of the first new item added
562   else
563   {
564     g_playlistPlayer.GetPlaylist(iPlaylist).Shuffle(iPosition);
565   }
566 }
567
568 void CPlayListPlayer::Add(int iPlaylist, CPlayList& playlist)
569 {
570   if (iPlaylist != PLAYLIST_MUSIC && iPlaylist != PLAYLIST_VIDEO)
571     return;
572   CPlayList& list = GetPlaylist(iPlaylist);
573   int iSize = list.size();
574   list.Add(playlist);
575   if (list.IsShuffled())
576     ReShuffle(iPlaylist, iSize);
577 }
578
579 void CPlayListPlayer::Add(int iPlaylist, const CFileItemPtr &pItem)
580 {
581   if (iPlaylist != PLAYLIST_MUSIC && iPlaylist != PLAYLIST_VIDEO)
582     return;
583   CPlayList& list = GetPlaylist(iPlaylist);
584   int iSize = list.size();
585   list.Add(pItem);
586   if (list.IsShuffled())
587     ReShuffle(iPlaylist, iSize);
588 }
589
590 void CPlayListPlayer::Add(int iPlaylist, CFileItemList& items)
591 {
592   if (iPlaylist != PLAYLIST_MUSIC && iPlaylist != PLAYLIST_VIDEO)
593     return;
594   CPlayList& list = GetPlaylist(iPlaylist);
595   int iSize = list.size();
596   list.Add(items);
597   if (list.IsShuffled())
598     ReShuffle(iPlaylist, iSize);
599 }
600
601 void CPlayListPlayer::Insert(int iPlaylist, CPlayList& playlist, int iIndex)
602 {
603   if (iPlaylist != PLAYLIST_MUSIC && iPlaylist != PLAYLIST_VIDEO)
604     return;
605   CPlayList& list = GetPlaylist(iPlaylist);
606   int iSize = list.size();
607   list.Insert(playlist, iIndex);
608   if (list.IsShuffled())
609     ReShuffle(iPlaylist, iSize);
610   else if (m_iCurrentPlayList == iPlaylist && m_iCurrentSong >= iIndex)
611     m_iCurrentSong++;
612 }
613
614 void CPlayListPlayer::Insert(int iPlaylist, const CFileItemPtr &pItem, int iIndex)
615 {
616   if (iPlaylist != PLAYLIST_MUSIC && iPlaylist != PLAYLIST_VIDEO)
617     return;
618   CPlayList& list = GetPlaylist(iPlaylist);
619   int iSize = list.size();
620   list.Insert(pItem, iIndex);
621   if (list.IsShuffled())
622     ReShuffle(iPlaylist, iSize);
623   else if (m_iCurrentPlayList == iPlaylist && m_iCurrentSong >= iIndex)
624     m_iCurrentSong++;
625 }
626
627 void CPlayListPlayer::Insert(int iPlaylist, CFileItemList& items, int iIndex)
628 {
629   if (iPlaylist != PLAYLIST_MUSIC && iPlaylist != PLAYLIST_VIDEO)
630     return;
631   CPlayList& list = GetPlaylist(iPlaylist);
632   int iSize = list.size();
633   list.Insert(items, iIndex);
634   if (list.IsShuffled())
635     ReShuffle(iPlaylist, iSize);
636   else if (m_iCurrentPlayList == iPlaylist && m_iCurrentSong >= iIndex)
637     m_iCurrentSong++;
638 }
639
640 void CPlayListPlayer::Remove(int iPlaylist, int iPosition)
641 {
642   if (iPlaylist != PLAYLIST_MUSIC && iPlaylist != PLAYLIST_VIDEO)
643     return;
644   CPlayList& list = GetPlaylist(iPlaylist);
645   list.Remove(iPosition);
646   if (m_iCurrentPlayList == iPlaylist && m_iCurrentSong >= iPosition)
647     m_iCurrentSong--;
648
649   // its likely that the playlist changed
650   CGUIMessage msg(GUI_MSG_PLAYLIST_CHANGED, 0, 0);
651   g_windowManager.SendMessage(msg);
652 }
653
654 void CPlayListPlayer::Clear()
655 {
656   if (m_PlaylistMusic)
657     m_PlaylistMusic->Clear();
658   if (m_PlaylistVideo)
659     m_PlaylistVideo->Clear();
660   if (m_PlaylistEmpty)
661     m_PlaylistEmpty->Clear();
662 }
663
664 void CPlayListPlayer::Swap(int iPlaylist, int indexItem1, int indexItem2)
665 {
666   if (iPlaylist != PLAYLIST_MUSIC && iPlaylist != PLAYLIST_VIDEO)
667     return;
668
669   CPlayList& list = GetPlaylist(iPlaylist);
670   if (list.Swap(indexItem1, indexItem2) && iPlaylist == m_iCurrentPlayList)
671   {
672     if (m_iCurrentSong == indexItem1)
673       m_iCurrentSong = indexItem2;
674     else if (m_iCurrentSong == indexItem2)
675       m_iCurrentSong = indexItem1;
676   }
677
678   // its likely that the playlist changed
679   CGUIMessage msg(GUI_MSG_PLAYLIST_CHANGED, 0, 0);
680   g_windowManager.SendMessage(msg);
681 }
682
683 void CPlayListPlayer::AnnouncePropertyChanged(int iPlaylist, const std::string &strProperty, const CVariant &value)
684 {
685   if (strProperty.empty() || value.isNull() ||
686      (iPlaylist == PLAYLIST_VIDEO && !g_application.IsPlayingVideo()) ||
687      (iPlaylist == PLAYLIST_MUSIC && !g_application.IsPlayingAudio()))
688     return;
689
690   CVariant data;
691   data["player"]["playerid"] = iPlaylist;
692   data["property"][strProperty] = value;
693   ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::Player, "xbmc", "OnPropertyChanged", data);
694 }