2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
9 #include "GUIWindowMusicPlaylistEditor.h"
13 #include "FileItemList.h"
14 #include "GUIUserMessages.h"
15 #include "ServiceBroker.h"
18 #include "dialogs/GUIDialogFileBrowser.h"
19 #include "dialogs/GUIDialogKaiToast.h"
20 #include "filesystem/PlaylistFileDirectory.h"
21 #include "guilib/GUIKeyboardFactory.h"
22 #include "guilib/LocalizeStrings.h"
23 #include "input/actions/Action.h"
24 #include "input/actions/ActionIDs.h"
25 #include "music/MusicUtils.h"
26 #include "playlists/PlayListM3U.h"
27 #include "settings/Settings.h"
28 #include "settings/SettingsComponent.h"
29 #include "utils/StringUtils.h"
30 #include "utils/URIUtils.h"
31 #include "utils/Variant.h"
33 #define CONTROL_LABELFILES 12
35 #define CONTROL_LOAD_PLAYLIST 6
36 #define CONTROL_SAVE_PLAYLIST 7
37 #define CONTROL_CLEAR_PLAYLIST 8
39 #define CONTROL_LIST 50
40 #define CONTROL_PLAYLIST 100
41 #define CONTROL_LABEL_PLAYLIST 101
45 CGUIWindowMusicPlaylistEditor::CGUIWindowMusicPlaylistEditor(void)
46 : CGUIWindowMusicBase(WINDOW_MUSIC_PLAYLIST_EDITOR
, "MyMusicPlaylistEditor.xml")
48 m_playlistThumbLoader
.SetObserver(this);
49 m_playlist
= new CFileItemList
;
52 CGUIWindowMusicPlaylistEditor::~CGUIWindowMusicPlaylistEditor(void)
57 bool CGUIWindowMusicPlaylistEditor::OnBack(int actionID
)
59 if (actionID
== ACTION_NAV_BACK
&& !m_viewControl
.HasControl(GetFocusedControlID()))
60 return CGUIWindow::OnBack(actionID
); // base class goes up a folder, but none to go up
61 return CGUIWindowMusicBase::OnBack(actionID
);
64 bool CGUIWindowMusicPlaylistEditor::OnAction(const CAction
&action
)
66 if (action
.GetID() == ACTION_CONTEXT_MENU
)
68 int iControl
= GetFocusedControlID();
69 if (iControl
== CONTROL_PLAYLIST
)
74 else if (iControl
== CONTROL_LIST
)
80 return CGUIWindow::OnAction(action
);
83 bool CGUIWindowMusicPlaylistEditor::OnClick(int iItem
, const std::string
& player
/* = "" */)
85 if (iItem
< 0 || iItem
>= m_vecItems
->Size()) return false;
86 CFileItemPtr item
= m_vecItems
->Get(iItem
);
88 // Expand .m3u files in sources list when clicked on regardless of <playlistasfolders>
89 if (item
->IsFileFolder(EFILEFOLDER_MASK_ONBROWSE
))
90 return Update(item
->GetPath());
91 // Avoid playback (default click behaviour) of media files
92 if (!item
->m_bIsFolder
)
95 return CGUIWindowMusicBase::OnClick(iItem
, player
);
98 bool CGUIWindowMusicPlaylistEditor::OnMessage(CGUIMessage
& message
)
100 switch ( message
.GetMessage() )
102 case GUI_MSG_WINDOW_DEINIT
:
103 if (m_thumbLoader
.IsLoading())
104 m_thumbLoader
.StopThread();
105 if (m_playlistThumbLoader
.IsLoading())
106 m_playlistThumbLoader
.StopThread();
107 CGUIWindowMusicBase::OnMessage(message
);
110 case GUI_MSG_WINDOW_INIT
:
112 if (m_vecItems
->GetPath() == "?")
113 m_vecItems
->SetPath("");
114 CGUIWindowMusicBase::OnMessage(message
);
116 if (message
.GetNumStringParams())
117 LoadPlaylist(message
.GetStringParam());
123 case GUI_MSG_NOTIFY_ALL
:
125 if (message
.GetParam1()==GUI_MSG_REMOVED_MEDIA
)
126 DeleteRemoveableMediaDirectoryCache();
130 case GUI_MSG_CLICKED
:
132 int control
= message
.GetSenderId();
133 if (control
== CONTROL_PLAYLIST
)
135 int item
= GetCurrentPlaylistItem();
136 int action
= message
.GetParam1();
137 if (action
== ACTION_CONTEXT_MENU
|| action
== ACTION_MOUSE_RIGHT_CLICK
)
139 else if (action
== ACTION_QUEUE_ITEM
|| action
== ACTION_DELETE_ITEM
||
140 action
== ACTION_MOUSE_MIDDLE_CLICK
)
141 OnDeletePlaylistItem(item
);
142 else if (action
== ACTION_MOVE_ITEM_UP
)
143 OnMovePlaylistItem(item
, -1);
144 else if (action
== ACTION_MOVE_ITEM_DOWN
)
145 OnMovePlaylistItem(item
, 1);
148 else if (control
== CONTROL_LIST
)
150 int action
= message
.GetParam1();
151 if (action
== ACTION_CONTEXT_MENU
|| action
== ACTION_MOUSE_RIGHT_CLICK
)
157 else if (control
== CONTROL_LOAD_PLAYLIST
)
162 else if (control
== CONTROL_SAVE_PLAYLIST
)
163 { // save the playlist
167 else if (control
== CONTROL_CLEAR_PLAYLIST
)
168 { // clear the playlist
176 return CGUIWindowMusicBase::OnMessage(message
);
179 bool CGUIWindowMusicPlaylistEditor::GetDirectory(const std::string
&strDirectory
, CFileItemList
&items
)
182 if (strDirectory
.empty())
183 { // root listing - list files:// and musicdb://
184 CFileItemPtr
files(new CFileItem("sources://music/", true));
185 files
->SetLabel(g_localizeStrings
.Get(744));
186 files
->SetLabelPreformatted(true);
187 files
->m_bIsShareOrDrive
= true;
190 CFileItemPtr
mdb(new CFileItem("library://music/", true));
191 mdb
->SetLabel(g_localizeStrings
.Get(14022));
192 mdb
->SetLabelPreformatted(true);
193 mdb
->m_bIsShareOrDrive
= true;
197 CFileItemPtr
vdb(new CFileItem("videodb://musicvideos/", true));
198 vdb
->SetLabel(g_localizeStrings
.Get(20389));
199 vdb
->SetLabelPreformatted(true);
200 vdb
->m_bIsShareOrDrive
= true;
207 if (!CGUIWindowMusicBase::GetDirectory(strDirectory
, items
))
210 // check for .CUE files here.
211 items
.FilterCueItems();
216 void CGUIWindowMusicPlaylistEditor::OnPrepareFileItems(CFileItemList
&items
)
218 CGUIWindowMusicBase::OnPrepareFileItems(items
);
223 void CGUIWindowMusicPlaylistEditor::UpdateButtons()
225 CGUIWindowMusicBase::UpdateButtons();
227 // Update object count label
228 std::string items
= StringUtils::Format("{} {}", m_vecItems
->GetObjectCount(),
229 g_localizeStrings
.Get(127)); // " 14 Objects"
230 SET_CONTROL_LABEL(CONTROL_LABELFILES
, items
);
233 void CGUIWindowMusicPlaylistEditor::DeleteRemoveableMediaDirectoryCache()
235 CUtil::DeleteDirectoryCache("r-");
238 void CGUIWindowMusicPlaylistEditor::PlayItem(int iItem
)
240 // unlike additemtoplaylist, we need to check the items here
241 // before calling it since the current playlist will be stopped
244 // we're at the root source listing
245 if (m_vecItems
->IsVirtualDirectoryRoot() && !m_vecItems
->Get(iItem
)->IsDVD())
248 #ifdef HAS_OPTICAL_DRIVE
249 if (m_vecItems
->Get(iItem
)->IsDVD())
250 MEDIA_DETECT::CAutorun::PlayDiscAskResume(m_vecItems
->Get(iItem
)->GetPath());
253 CGUIWindowMusicBase::PlayItem(iItem
);
256 void CGUIWindowMusicPlaylistEditor::OnQueueItem(int iItem
, bool)
258 if (iItem
< 0 || iItem
>= m_vecItems
->Size())
261 // add this item to our playlist. We make a new copy here as we may be rendering them side by side,
262 // and thus want a different layout for each item
263 CFileItemPtr
item(new CFileItem(*m_vecItems
->Get(iItem
)));
264 CFileItemList newItems
;
265 MUSIC_UTILS::GetItemsForPlayList(item
, newItems
);
266 AppendToPlaylist(newItems
);
269 bool CGUIWindowMusicPlaylistEditor::Update(const std::string
&strDirectory
, bool updateFilterPath
/* = true */)
271 if (m_thumbLoader
.IsLoading())
272 m_thumbLoader
.StopThread();
274 if (!CGUIMediaWindow::Update(strDirectory
, updateFilterPath
))
277 m_vecItems
->SetContent("files");
278 m_thumbLoader
.Load(*m_vecItems
);
280 // update our playlist control
285 void CGUIWindowMusicPlaylistEditor::ClearPlaylist()
287 CGUIMessage
msg(GUI_MSG_LABEL_RESET
, GetID(), CONTROL_PLAYLIST
);
293 void CGUIWindowMusicPlaylistEditor::UpdatePlaylist()
295 if (m_playlistThumbLoader
.IsLoading())
296 m_playlistThumbLoader
.StopThread();
298 // deselect all items
299 for (int i
= 0; i
< m_playlist
->Size(); i
++)
300 m_playlist
->Get(i
)->Select(false);
302 // bind them to the list
303 CGUIMessage
msg(GUI_MSG_LABEL_BIND
, GetID(), CONTROL_PLAYLIST
, 0, 0, m_playlist
);
306 // indicate how many songs we have
307 std::string items
= StringUtils::Format("{} {}", m_playlist
->Size(),
308 g_localizeStrings
.Get(134)); // "123 Songs"
309 SET_CONTROL_LABEL(CONTROL_LABEL_PLAYLIST
, items
);
311 m_playlistThumbLoader
.Load(*m_playlist
);
314 int CGUIWindowMusicPlaylistEditor::GetCurrentPlaylistItem()
316 CGUIMessage
msg(GUI_MSG_ITEM_SELECTED
, GetID(), CONTROL_PLAYLIST
);
318 int item
= msg
.GetParam1();
319 if (item
> m_playlist
->Size())
324 void CGUIWindowMusicPlaylistEditor::OnDeletePlaylistItem(int item
)
326 if (item
< 0) return;
327 m_playlist
->Remove(item
);
329 // select the next item
330 CGUIMessage
msg(GUI_MSG_ITEM_SELECT
, GetID(), CONTROL_PLAYLIST
, item
);
334 void CGUIWindowMusicPlaylistEditor::OnMovePlaylistItem(int item
, int direction
)
336 if (item
< 0) return;
337 if (item
+ direction
>= m_playlist
->Size() || item
+ direction
< 0)
339 m_playlist
->Swap(item
, item
+ direction
);
341 CGUIMessage
msg(GUI_MSG_ITEM_SELECT
, GetID(), CONTROL_PLAYLIST
, item
+ direction
);
345 void CGUIWindowMusicPlaylistEditor::OnLoadPlaylist()
347 // Prompt user for file to load from music playlists folder
348 std::string playlist
;
349 if (CGUIDialogFileBrowser::ShowAndGetFile("special://musicplaylists/",
350 ".m3u|.m3u8|.pls|.b4s|.wpl|.xspf", g_localizeStrings
.Get(656),
352 LoadPlaylist(playlist
);
355 void CGUIWindowMusicPlaylistEditor::LoadPlaylist(const std::string
&playlist
)
357 const CURL
pathToUrl(playlist
);
358 if (pathToUrl
.IsProtocol("newplaylist"))
361 m_strLoadedPlaylist
.clear();
365 XFILE::CPlaylistFileDirectory dir
;
367 if (dir
.GetDirectory(pathToUrl
, items
))
370 AppendToPlaylist(items
);
371 m_strLoadedPlaylist
= playlist
;
375 void CGUIWindowMusicPlaylistEditor::OnSavePlaylist()
377 // saves playlist to the playlist folder
378 std::string name
= URIUtils::GetFileName(m_strLoadedPlaylist
);
379 std::string extension
= URIUtils::GetExtension(m_strLoadedPlaylist
);
380 if (extension
.empty())
383 URIUtils::RemoveExtension(name
);
385 if (CGUIKeyboardFactory::ShowAndGetInput(name
, CVariant
{g_localizeStrings
.Get(16012)}, false))
387 PLAYLIST::CPlayListM3U playlist
;
388 playlist
.Add(*m_playlist
);
389 std::string path
= URIUtils::AddFileToFolder(
390 CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_SYSTEM_PLAYLISTSPATH
),
395 m_strLoadedPlaylist
= name
;
396 CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info
,
397 g_localizeStrings
.Get(559), // "Playlist"
398 g_localizeStrings
.Get(35259)); // "Saved"
402 void CGUIWindowMusicPlaylistEditor::AppendToPlaylist(CFileItemList
&newItems
)
404 OnRetrieveMusicInfo(newItems
);
405 FormatItemLabels(newItems
, LABEL_MASKS(CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_MUSICFILES_TRACKFORMAT
), "%D", "%L", ""));
406 m_playlist
->Append(newItems
);
410 void CGUIWindowMusicPlaylistEditor::OnSourcesContext()
412 static constexpr int CONTEXT_BUTTON_QUEUE_ITEM
= 0;
413 static constexpr int CONTEXT_BUTTON_BROWSE_INTO
= 1;
415 CFileItemPtr item
= GetCurrentListItem();
416 CContextButtons buttons
;
417 if (item
->IsFileFolder(EFILEFOLDER_MASK_ONBROWSE
))
418 buttons
.Add(CONTEXT_BUTTON_BROWSE_INTO
, 37015); //Browse into
419 if (item
&& !item
->IsParentFolder() && !m_vecItems
->IsVirtualDirectoryRoot())
420 buttons
.Add(CONTEXT_BUTTON_QUEUE_ITEM
, 15019); // Add (to playlist)
422 int btnid
= CGUIDialogContextMenu::ShowAndGetChoice(buttons
);
423 if (btnid
== CONTEXT_BUTTON_QUEUE_ITEM
)
424 OnQueueItem(m_viewControl
.GetSelectedItem(), false);
425 else if (btnid
== CONTEXT_BUTTON_BROWSE_INTO
)
426 Update(item
->GetPath());
429 void CGUIWindowMusicPlaylistEditor::OnPlaylistContext()
431 int item
= GetCurrentPlaylistItem();
432 CContextButtons buttons
;
434 buttons
.Add(CONTEXT_BUTTON_MOVE_ITEM_UP
, 13332);
435 if (item
>= 0 && item
< m_playlist
->Size() - 1)
436 buttons
.Add(CONTEXT_BUTTON_MOVE_ITEM_DOWN
, 13333);
438 buttons
.Add(CONTEXT_BUTTON_DELETE
, 1210);
440 int btnid
= CGUIDialogContextMenu::ShowAndGetChoice(buttons
);
441 if (btnid
== CONTEXT_BUTTON_MOVE_ITEM_UP
)
442 OnMovePlaylistItem(item
, -1);
443 else if (btnid
== CONTEXT_BUTTON_MOVE_ITEM_DOWN
)
444 OnMovePlaylistItem(item
, 1);
445 else if (btnid
== CONTEXT_BUTTON_DELETE
)
446 OnDeletePlaylistItem(item
);