only call IPlayerCallback::OnPlayBackSpeedChanged if the speed has actually changed
[vuplus_xbmc] / xbmc / dialogs / GUIDialogMediaSource.cpp
1 /*
2  *      Copyright (C) 2005-2008 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, write to
17  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18  *  http://www.gnu.org/copyleft/gpl.html
19  *
20  */
21
22 #include "GUIDialogMediaSource.h"
23 #include "GUIDialogKeyboard.h"
24 #include "GUIDialogFileBrowser.h"
25 #include "video/windows/GUIWindowVideoBase.h"
26 #include "video/dialogs/GUIDialogVideoScan.h"
27 #include "guilib/GUIWindowManager.h"
28 #include "Util.h"
29 #include "utils/URIUtils.h"
30 #include "filesystem/Directory.h"
31 #include "GUIDialogYesNo.h"
32 #include "FileItem.h"
33 #include "settings/Settings.h"
34 #include "settings/GUISettings.h"
35 #include "guilib/LocalizeStrings.h"
36
37 using namespace std;
38 using namespace XFILE;
39
40 #define CONTROL_HEADING          2
41 #define CONTROL_PATH            10
42 #define CONTROL_PATH_BROWSE     11
43 #define CONTROL_NAME            12
44 #define CONTROL_PATH_ADD        13
45 #define CONTROL_PATH_REMOVE     14
46 #define CONTROL_OK              18
47 #define CONTROL_CANCEL          19
48 #define CONTROL_CONTENT         20
49
50 CGUIDialogMediaSource::CGUIDialogMediaSource(void)
51     : CGUIDialog(WINDOW_DIALOG_MEDIA_SOURCE, "DialogMediaSource.xml")
52 {
53   m_paths =  new CFileItemList;
54 }
55
56 CGUIDialogMediaSource::~CGUIDialogMediaSource()
57 {
58   delete m_paths;
59 }
60
61 bool CGUIDialogMediaSource::OnBack(int actionID)
62 {
63   m_confirmed = false;
64   return CGUIDialog::OnBack(actionID);
65 }
66
67 bool CGUIDialogMediaSource::OnMessage(CGUIMessage& message)
68 {
69   switch ( message.GetMessage() )
70   {
71   case GUI_MSG_CLICKED:
72     {
73       int iControl = message.GetSenderId();
74       int iAction = message.GetParam1();
75       if (iControl == CONTROL_PATH && (iAction == ACTION_SELECT_ITEM || iAction == ACTION_MOUSE_LEFT_CLICK))
76         OnPath(GetSelectedItem());
77       else if (iControl == CONTROL_PATH_BROWSE)
78         OnPathBrowse(GetSelectedItem());
79       else if (iControl == CONTROL_PATH_ADD)
80         OnPathAdd();
81       else if (iControl == CONTROL_PATH_REMOVE)
82         OnPathRemove(GetSelectedItem());
83       else if (iControl == CONTROL_NAME)
84       {
85         OnEditChanged(iControl, m_name);
86         UpdateButtons();
87       }
88       else if (iControl == CONTROL_OK)
89         OnOK();
90       else if (iControl == CONTROL_CANCEL)
91         OnCancel();
92       return true;
93     }
94     break;
95   case GUI_MSG_WINDOW_INIT:
96     {
97       m_confirmed = false;
98       m_bRunScan = false;
99       m_bNameChanged=false;
100       UpdateButtons();
101     }
102     break;
103   case GUI_MSG_SETFOCUS:
104     if (message.GetControlId() == CONTROL_PATH_BROWSE ||
105              message.GetControlId() == CONTROL_PATH_ADD ||
106              message.GetControlId() == CONTROL_PATH_REMOVE)
107     {
108       HighlightItem(GetSelectedItem());
109     }
110     else
111       HighlightItem(-1);
112     break;
113   }
114   return CGUIDialog::OnMessage(message);
115 }
116
117 // \brief Show CGUIDialogMediaSource dialog and prompt for a new media source.
118 // \return True if the media source is added, false otherwise.
119 bool CGUIDialogMediaSource::ShowAndAddMediaSource(const CStdString &type)
120 {
121   CGUIDialogMediaSource *dialog = (CGUIDialogMediaSource *)g_windowManager.GetWindow(WINDOW_DIALOG_MEDIA_SOURCE);
122   if (!dialog) return false;
123   dialog->Initialize();
124   dialog->SetShare(CMediaSource());
125   dialog->SetTypeOfMedia(type);
126   dialog->DoModal();
127   bool confirmed(dialog->IsConfirmed());
128   if (confirmed)
129   { // yay, add this share
130     CMediaSource share;
131     unsigned int i,j=2;
132     bool bConfirmed=false;
133     VECSOURCES* pShares = g_settings.GetSourcesFromType(type);
134     CStdString strName = dialog->m_name;
135     while (!bConfirmed)
136     {
137       for (i=0;i<pShares->size();++i)
138       {
139         if ((*pShares)[i].strName.Equals(strName))
140           break;
141       }
142       if (i < pShares->size()) // found a match -  try next
143         strName.Format("%s (%i)",dialog->m_name,j++);
144       else
145         bConfirmed = true;
146     }
147     share.FromNameAndPaths(type, strName, dialog->GetPaths());
148     if (dialog->m_paths->Size() > 0) {
149       share.m_strThumbnailImage = dialog->m_paths->Get(0)->GetThumbnailImage();
150     }
151     g_settings.AddShare(type, share);
152
153     if (type == "video")
154     {
155       if (dialog->m_bRunScan)
156       {
157         CGUIDialogVideoScan* scanner = (CGUIDialogVideoScan*)g_windowManager.GetWindow(WINDOW_DIALOG_VIDEO_SCAN);
158         if (scanner)
159           scanner->StartScanning(share.strPath, true);
160       }
161     }
162   }
163   dialog->m_paths->Clear();
164   return confirmed;
165 }
166
167 bool CGUIDialogMediaSource::ShowAndEditMediaSource(const CStdString &type, const CStdString&share)
168 {
169   VECSOURCES* pShares=NULL;
170
171   if (pShares)
172   {
173     for (unsigned int i=0;i<pShares->size();++i)
174     {
175       if ((*pShares)[i].strName.Equals(share))
176         return ShowAndEditMediaSource(type,(*pShares)[i]);
177     }
178   }
179
180   return false;
181 }
182
183 bool CGUIDialogMediaSource::ShowAndEditMediaSource(const CStdString &type, const CMediaSource &share)
184 {
185   CStdString strOldName = share.strName;
186   CGUIDialogMediaSource *dialog = (CGUIDialogMediaSource *)g_windowManager.GetWindow(WINDOW_DIALOG_MEDIA_SOURCE);
187   if (!dialog) return false;
188   dialog->Initialize();
189   dialog->SetShare(share);
190   dialog->SetTypeOfMedia(type, true);
191   dialog->DoModal();
192   bool confirmed(dialog->IsConfirmed());
193   if (confirmed)
194   { // yay, add this share
195     unsigned int i,j=2;
196     bool bConfirmed=false;
197     VECSOURCES* pShares = g_settings.GetSourcesFromType(type);
198     CStdString strName = dialog->m_name;
199     while (!bConfirmed)
200     {
201       for (i=0;i<pShares->size();++i)
202       {
203         if ((*pShares)[i].strName.Equals(strName))
204           break;
205       }
206       if (i < pShares->size() && (*pShares)[i].strName != strOldName) // found a match -  try next
207         strName.Format("%s (%i)",dialog->m_name,j++);
208       else
209         bConfirmed = true;
210     }
211
212     CMediaSource newShare;
213     newShare.FromNameAndPaths(type, strName, dialog->GetPaths());
214     g_settings.UpdateShare(type, strOldName, newShare);
215   }
216   dialog->m_paths->Clear();
217   return confirmed;
218 }
219
220 void CGUIDialogMediaSource::OnPathBrowse(int item)
221 {
222   if (item < 0 || item > m_paths->Size()) return;
223   // Browse is called.  Open the filebrowser dialog.
224   // Ignore current path is best at this stage??
225   CStdString path;
226   bool allowNetworkShares(m_type != "programs");
227   VECSOURCES extraShares;
228
229   if (m_name != CUtil::GetTitleFromPath(m_paths->Get(item)->GetPath()))
230     m_bNameChanged=true;
231
232   if (m_type == "music")
233   { // add the music playlist location
234     CMediaSource share1;
235     share1.strPath = "special://musicplaylists/";
236     share1.strName = g_localizeStrings.Get(20011);
237     share1.m_ignore = true;
238     extraShares.push_back(share1);
239
240 #ifdef HAS_FILESYSTEM_SMB
241     share1.strPath = "smb://";
242     share1.strName = g_localizeStrings.Get(20171);
243     extraShares.push_back(share1);
244 #endif
245
246 #ifdef HAS_FILESYSTEM_NFS
247     share1.strPath = "nfs://";
248     share1.strName = g_localizeStrings.Get(20259);
249     extraShares.push_back(share1);
250 #endif// HAS_FILESYSTEM_NFS
251
252     share1.strPath = "upnp://";
253     share1.strName = "UPnP Devices";
254     extraShares.push_back(share1);
255
256     share1.strPath = "sap://";
257     share1.strName = "SAP Streams";
258     extraShares.push_back(share1);
259
260     share1.strPath = "zeroconf://";
261     share1.strName = "Zeroconf Browser";
262     extraShares.push_back(share1);
263
264     if (g_guiSettings.GetString("audiocds.recordingpath",false) != "")
265     {
266       share1.strPath = "special://recordings/";
267       share1.strName = g_localizeStrings.Get(21883);
268       extraShares.push_back(share1);
269     }
270
271     if (g_guiSettings.GetString("scrobbler.lastfmusername") != "")
272     {
273       share1.strName = "Last.FM";
274       share1.strPath = "lastfm://";
275       extraShares.push_back(share1);
276     }
277  }
278   else if (m_type == "video")
279   { // add the music playlist location
280     CMediaSource share1;
281     share1.m_ignore = true;
282     share1.strPath = "special://videoplaylists/";
283     share1.strName = g_localizeStrings.Get(20012);
284     extraShares.push_back(share1);
285
286     share1.strPath = "rtv://*/";
287     share1.strName = "ReplayTV Devices";
288     extraShares.push_back(share1);
289
290 #ifdef HAS_FILESYSTEM_SMB
291     share1.strPath = "smb://";
292     share1.strName = g_localizeStrings.Get(20171);
293     extraShares.push_back(share1);
294 #endif
295
296 #ifdef HAS_FILESYSTEM_NFS
297     share1.strPath = "nfs://";
298     share1.strName = g_localizeStrings.Get(20259);
299     extraShares.push_back(share1);
300 #endif// HAS_FILESYSTEM_NFS
301
302     share1.strPath = "hdhomerun://";
303     share1.strName = "HDHomerun Devices";
304     extraShares.push_back(share1);
305
306     share1.strPath = "sap://";
307     share1.strName = "SAP Streams";
308     extraShares.push_back(share1);
309
310     share1.strPath = "upnp://";
311     share1.strName = "UPnP Devices";
312     extraShares.push_back(share1);
313
314     share1.strPath = "zeroconf://";
315     share1.strName = "Zeroconf Browser";
316     extraShares.push_back(share1);
317   }
318   else if (m_type == "pictures")
319   {
320     CMediaSource share1;
321     share1.m_ignore = true;
322     if (g_guiSettings.GetString("debug.screenshotpath",false)!= "")
323     {
324       share1.strPath = "special://screenshots/";
325       share1.strName = g_localizeStrings.Get(20008);
326       extraShares.push_back(share1);
327     }
328
329 #ifdef HAS_FILESYSTEM_SMB
330     share1.strPath = "smb://";
331     share1.strName = g_localizeStrings.Get(20171);
332     extraShares.push_back(share1);
333 #endif
334
335 #ifdef HAS_FILESYSTEM_NFS
336     share1.strPath = "nfs://";
337     share1.strName = g_localizeStrings.Get(20259);
338     extraShares.push_back(share1);
339 #endif// HAS_FILESYSTEM_NFS
340
341     share1.strPath = "upnp://";
342     share1.strName = "UPnP Devices";
343     extraShares.push_back(share1);
344
345     share1.strPath = "zeroconf://";
346     share1.strName = "Zeroconf Browser";
347     extraShares.push_back(share1);
348   }
349   else if (m_type == "programs")
350   {
351     // nothing to add
352   }
353   if (CGUIDialogFileBrowser::ShowAndGetSource(path, allowNetworkShares, extraShares.size()==0?NULL:&extraShares))
354   {
355     if (item < m_paths->Size()) // if the skin does funky things, m_paths may have been cleared
356       m_paths->Get(item)->SetPath(path);
357     if (!m_bNameChanged || m_name.IsEmpty())
358     {
359       CURL url(path);
360       m_name = url.GetWithoutUserDetails();
361       URIUtils::RemoveSlashAtEnd(m_name);
362       m_name = CUtil::GetTitleFromPath(m_name);
363     }
364     UpdateButtons();
365   }
366 }
367
368 void CGUIDialogMediaSource::OnPath(int item)
369 {
370   if (item < 0 || item > m_paths->Size()) return;
371
372   if (m_name != CUtil::GetTitleFromPath(m_paths->Get(item)->GetPath()))
373     m_bNameChanged=true;
374
375   CStdString path(m_paths->Get(item)->GetPath());
376   CGUIDialogKeyboard::ShowAndGetInput(path, g_localizeStrings.Get(1021), false);
377   URIUtils::AddSlashAtEnd(path);
378   m_paths->Get(item)->SetPath(path);
379
380   if (!m_bNameChanged || m_name.IsEmpty())
381   {
382     CURL url(m_paths->Get(item)->GetPath());
383     m_name = url.GetWithoutUserDetails();
384     URIUtils::RemoveSlashAtEnd(m_name);
385     m_name = CUtil::GetTitleFromPath(m_name);
386   }
387   UpdateButtons();
388 }
389
390 void CGUIDialogMediaSource::OnOK()
391 {
392   // verify the path by doing a GetDirectory.
393   CFileItemList items;
394
395   CMediaSource share;
396   share.FromNameAndPaths(m_type, m_name, GetPaths());
397   // hack: Need to temporarily add the share, then get path, then remove share
398   VECSOURCES *shares = g_settings.GetSourcesFromType(m_type);
399   if (shares)
400     shares->push_back(share);
401   if (share.strPath.Left(9).Equals("plugin://") || CDirectory::GetDirectory(share.strPath, items, "", false, true) || CGUIDialogYesNo::ShowAndGetInput(1001,1025,1003,1004))
402   {
403     m_confirmed = true;
404     Close();
405     if (m_type == "video" && !URIUtils::IsLiveTV(share.strPath) && 
406         !share.strPath.Left(6).Equals("rss://"))
407     {
408       CGUIWindowVideoBase::OnAssignContent(share.strPath, 0, m_info, m_settings);
409     }
410   }
411
412   // and remove the share again
413   if (shares)
414     shares->erase(--shares->end());
415 }
416
417 void CGUIDialogMediaSource::OnCancel()
418 {
419   m_confirmed = false;
420   Close();
421 }
422
423 void CGUIDialogMediaSource::UpdateButtons()
424 {
425   if (!m_paths->Size()) // sanity
426     return;
427
428   CONTROL_ENABLE_ON_CONDITION(CONTROL_OK, !m_paths->Get(0)->GetPath().IsEmpty() && !m_name.IsEmpty());
429   CONTROL_ENABLE_ON_CONDITION(CONTROL_PATH_REMOVE, m_paths->Size() > 1);
430   // name
431   SET_CONTROL_LABEL2(CONTROL_NAME, m_name);
432   SendMessage(GUI_MSG_SET_TYPE, CONTROL_NAME, 0, 1022);
433
434   int currentItem = GetSelectedItem();
435   SendMessage(GUI_MSG_LABEL_RESET, CONTROL_PATH);
436   for (int i = 0; i < m_paths->Size(); i++)
437   {
438     CFileItemPtr item = m_paths->Get(i);
439     CStdString path;
440     CURL url(item->GetPath());
441     path = url.GetWithoutUserDetails();
442     if (path.IsEmpty()) path = "<"+g_localizeStrings.Get(231)+">"; // <None>
443     item->SetLabel(path);
444   }
445   CGUIMessage msg(GUI_MSG_LABEL_BIND, GetID(), CONTROL_PATH, 0, 0, m_paths);
446   OnMessage(msg);
447   SendMessage(GUI_MSG_ITEM_SELECT, CONTROL_PATH, currentItem);
448
449   SET_CONTROL_HIDDEN(CONTROL_CONTENT);
450 }
451
452 void CGUIDialogMediaSource::SetShare(const CMediaSource &share)
453 {
454   m_paths->Clear();
455   for (unsigned int i = 0; i < share.vecPaths.size(); i++)
456   {
457     CFileItemPtr item(new CFileItem(share.vecPaths[i], true));
458     m_paths->Add(item);
459   }
460   if (0 == share.vecPaths.size())
461   {
462     CFileItemPtr item(new CFileItem("", true));
463     m_paths->Add(item);
464   }
465   m_name = share.strName;
466   UpdateButtons();
467 }
468
469 void CGUIDialogMediaSource::SetTypeOfMedia(const CStdString &type, bool editNotAdd)
470 {
471   m_type = type;
472   int typeStringID = -1;
473   if (type == "music")
474     typeStringID = 249; // "Music"
475   else if (type == "video")
476     typeStringID = 291;  // "Video"
477   else if (type == "programs")
478     typeStringID = 350;  // "Programs"
479   else if (type == "pictures")
480     typeStringID = 1213;  // "Pictures"
481   else // if (type == "files");
482     typeStringID = 744;  // "Files"
483   CStdString format;
484   format.Format(g_localizeStrings.Get(editNotAdd ? 1028 : 1020).c_str(), g_localizeStrings.Get(typeStringID).c_str());
485   SET_CONTROL_LABEL(CONTROL_HEADING, format);
486 }
487
488 void CGUIDialogMediaSource::OnWindowLoaded()
489 {
490   CGUIDialog::OnWindowLoaded();
491   ChangeButtonToEdit(CONTROL_NAME, true);  // true for single label
492 }
493
494 int CGUIDialogMediaSource::GetSelectedItem()
495 {
496   CGUIMessage message(GUI_MSG_ITEM_SELECTED, GetID(), CONTROL_PATH);
497   OnMessage(message);
498   int value = message.GetParam1();
499   if (value < 0 || value > m_paths->Size()) return 0;
500   return value;
501 }
502
503 void CGUIDialogMediaSource::HighlightItem(int item)
504 {
505   for (int i = 0; i < m_paths->Size(); i++)
506     m_paths->Get(i)->Select(false);
507   if (item >= 0 && item < m_paths->Size())
508     m_paths->Get(item)->Select(true);
509   CGUIMessage msg(GUI_MSG_ITEM_SELECT, GetID(), CONTROL_PATH, item);
510   OnMessage(msg);
511 }
512
513 void CGUIDialogMediaSource::OnPathRemove(int item)
514 {
515   m_paths->Remove(item);
516   UpdateButtons();
517   if (item >= m_paths->Size())
518     HighlightItem(m_paths->Size() - 1);
519   else
520     HighlightItem(item);
521   if (m_paths->Size() <= 1)
522   {
523     SET_CONTROL_FOCUS(CONTROL_PATH_ADD, 0);
524   }
525 }
526
527 void CGUIDialogMediaSource::OnPathAdd()
528 {
529   // add a new item and select it as well
530   CFileItemPtr item(new CFileItem("", true));
531   m_paths->Add(item);
532   UpdateButtons();
533   HighlightItem(m_paths->Size() - 1);
534 }
535
536 vector<CStdString> CGUIDialogMediaSource::GetPaths()
537 {
538   vector<CStdString> paths;
539   for (int i = 0; i < m_paths->Size(); i++)
540     if (!m_paths->Get(i)->GetPath().IsEmpty())
541       paths.push_back(m_paths->Get(i)->GetPath());
542   return paths;
543 }