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