Merge pull request #473 from Montellese/onplaybackspeedchanged
[vuplus_xbmc] / xbmc / addons / GUIWindowAddonBrowser.cpp
1 /*
2  *      Copyright (C) 2005-2010 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 "GUIWindowAddonBrowser.h"
23 #include "addons/AddonManager.h"
24 #include "addons/Repository.h"
25 #include "dialogs/GUIDialogContextMenu.h"
26 #include "GUIDialogAddonInfo.h"
27 #include "GUIDialogAddonSettings.h"
28 #include "dialogs/GUIDialogKeyboard.h"
29 #include "dialogs/GUIDialogYesNo.h"
30 #include "dialogs/GUIDialogSelect.h"
31 #include "dialogs/GUIDialogFileBrowser.h"
32 #include "GUIUserMessages.h"
33 #include "guilib/GUIWindowManager.h"
34 #include "utils/URIUtils.h"
35 #include "URL.h"
36 #include "FileItem.h"
37 #include "filesystem/File.h"
38 #include "filesystem/Directory.h"
39 #include "filesystem/AddonsDirectory.h"
40 #include "addons/AddonInstaller.h"
41 #include "utils/JobManager.h"
42 #include "utils/log.h"
43 #include "threads/SingleLock.h"
44 #include "settings/Settings.h"
45 #include "utils/StringUtils.h"
46 #include "Application.h"
47 #include "AddonDatabase.h"
48 #include "settings/AdvancedSettings.h"
49 #include "storage/MediaManager.h"
50 #include "settings/GUISettings.h"
51
52 #define CONTROL_AUTOUPDATE 5
53 #define CONTROL_SHUTUP     6
54
55 using namespace ADDON;
56 using namespace XFILE;
57 using namespace std;
58
59 CGUIWindowAddonBrowser::CGUIWindowAddonBrowser(void)
60 : CGUIMediaWindow(WINDOW_ADDON_BROWSER, "AddonBrowser.xml")
61 {
62   m_thumbLoader.SetNumOfWorkers(1);
63 }
64
65 CGUIWindowAddonBrowser::~CGUIWindowAddonBrowser()
66 {
67 }
68
69 bool CGUIWindowAddonBrowser::OnMessage(CGUIMessage& message)
70 {
71   switch ( message.GetMessage() )
72   {
73     case GUI_MSG_WINDOW_DEINIT:
74     {
75       if (m_thumbLoader.IsLoading())
76         m_thumbLoader.StopThread();
77     }
78     break;
79   case GUI_MSG_WINDOW_INIT:
80     {
81       m_rootDir.AllowNonLocalSources(false);
82
83       // is this the first time the window is opened?
84       if (m_vecItems->GetPath() == "?" && message.GetStringParam().IsEmpty())
85         m_vecItems->SetPath("");
86     }
87     break;
88   case GUI_MSG_CLICKED:
89     {
90       int iControl = message.GetSenderId();
91       if (iControl == CONTROL_AUTOUPDATE)
92       {
93         g_settings.m_bAddonAutoUpdate = !g_settings.m_bAddonAutoUpdate;
94         g_settings.Save();
95         return true;
96       }
97       else if (iControl == CONTROL_SHUTUP)
98       {
99         g_settings.m_bAddonNotifications = !g_settings.m_bAddonNotifications;
100         g_settings.Save();
101         return true;
102       }
103       else if (m_viewControl.HasControl(iControl))  // list/thumb control
104       {
105         // get selected item
106         int iItem = m_viewControl.GetSelectedItem();
107         int iAction = message.GetParam1();
108
109         // iItem is checked for validity inside these routines
110         if (iAction == ACTION_SHOW_INFO)
111         {
112           if (!m_vecItems->Get(iItem)->GetProperty("Addon.ID").empty())
113             return CGUIDialogAddonInfo::ShowForItem((*m_vecItems)[iItem]);
114           return false;
115         }
116       }
117     }
118     break;
119   case GUI_MSG_NOTIFY_ALL:
120     {
121       if (message.GetParam1() == GUI_MSG_UPDATE_ITEM && IsActive() && message.GetNumStringParams() == 1)
122       { // update this item
123         for (int i = 0; i < m_vecItems->Size(); ++i)
124         {
125           CFileItemPtr item = m_vecItems->Get(i);
126           if (item->GetProperty("Addon.ID") == message.GetStringParam())
127           {
128             SetItemLabel2(item);
129             return true;
130           }
131         }
132       }
133     }
134     break;
135    default:
136      break;
137   }
138   return CGUIMediaWindow::OnMessage(message);
139 }
140
141 void CGUIWindowAddonBrowser::GetContextButtons(int itemNumber,
142                                                CContextButtons& buttons)
143 {
144   CFileItemPtr pItem = m_vecItems->Get(itemNumber);
145   if (pItem->GetPath().Equals("addons://enabled/"))
146     buttons.Add(CONTEXT_BUTTON_SCAN,24034);
147   
148   AddonPtr addon;
149   if (!CAddonMgr::Get().GetAddon(pItem->GetProperty("Addon.ID").asString(), addon, ADDON_UNKNOWN, false)) // allow disabled addons
150     return;
151
152   if (addon->Type() == ADDON_REPOSITORY && pItem->m_bIsFolder)
153   {
154     buttons.Add(CONTEXT_BUTTON_SCAN,24034);
155     buttons.Add(CONTEXT_BUTTON_UPDATE_LIBRARY,24035);
156   }
157
158   buttons.Add(CONTEXT_BUTTON_INFO,24003);
159
160   if (addon->HasSettings())
161     buttons.Add(CONTEXT_BUTTON_SETTINGS,24020);
162 }
163
164 bool CGUIWindowAddonBrowser::OnContextButton(int itemNumber,
165                                              CONTEXT_BUTTON button)
166 {
167   CFileItemPtr pItem = m_vecItems->Get(itemNumber);
168   if (pItem->GetPath().Equals("addons://enabled/"))
169   {
170     if (button == CONTEXT_BUTTON_SCAN)
171     {
172       CAddonMgr::Get().FindAddons();
173       return true;
174     }
175   }
176   AddonPtr addon;
177   if (!CAddonMgr::Get().GetAddon(pItem->GetProperty("Addon.ID").asString(), addon, ADDON_UNKNOWN, false)) // allow disabled addons
178     return false;
179
180   if (button == CONTEXT_BUTTON_SETTINGS)
181     return CGUIDialogAddonSettings::ShowAndGetInput(addon);
182
183   if (button == CONTEXT_BUTTON_UPDATE_LIBRARY)
184   {
185     CAddonDatabase database;
186     database.Open();
187     database.DeleteRepository(addon->ID());
188     button = CONTEXT_BUTTON_SCAN;
189   }
190
191   if (button == CONTEXT_BUTTON_SCAN)
192   {
193     RepositoryPtr repo = boost::dynamic_pointer_cast<CRepository>(addon);
194     CAddonInstaller::Get().UpdateRepos(true);
195     return true;
196   }
197
198   if (button == CONTEXT_BUTTON_INFO)
199   {
200     CGUIDialogAddonInfo::ShowForItem(pItem);
201     return true;
202   }
203
204   return CGUIMediaWindow::OnContextButton(itemNumber, button);
205 }
206
207 bool CGUIWindowAddonBrowser::OnClick(int iItem)
208 {
209   CFileItemPtr item = m_vecItems->Get(iItem);
210   if (item->GetPath() == "addons://install/")
211   {
212     // pop up filebrowser to grab an installed folder
213     VECSOURCES shares = g_settings.m_fileSources;
214     g_mediaManager.GetLocalDrives(shares);
215     CStdString path;
216     if (CGUIDialogFileBrowser::ShowAndGetFile(shares, "*.zip", g_localizeStrings.Get(24041), path))
217       CAddonInstaller::Get().InstallFromZip(path);
218     return true;
219   }
220   if (!item->m_bIsFolder)
221   {
222     // cancel a downloading job
223     if (item->HasProperty("Addon.Downloading"))
224     {
225       if (CGUIDialogYesNo::ShowAndGetInput(g_localizeStrings.Get(24000),
226                                            item->GetProperty("Addon.Name").asString(),
227                                            g_localizeStrings.Get(24066),""))
228       {
229         if (CAddonInstaller::Get().Cancel(item->GetProperty("Addon.ID").asString()))
230           Update(m_vecItems->GetPath());
231       }
232       return true;
233     }
234
235     CGUIDialogAddonInfo::ShowForItem(item);
236     return true;
237   }
238   if (item->GetPath().Equals("addons://search/"))
239     return Update(item->GetPath());
240
241   return CGUIMediaWindow::OnClick(iItem);
242 }
243
244 void CGUIWindowAddonBrowser::UpdateButtons()
245 {
246   SET_CONTROL_SELECTED(GetID(),CONTROL_AUTOUPDATE,g_settings.m_bAddonAutoUpdate);
247   SET_CONTROL_SELECTED(GetID(),CONTROL_SHUTUP,g_settings.m_bAddonNotifications);
248   CGUIMediaWindow::UpdateButtons();
249 }
250
251 bool CGUIWindowAddonBrowser::GetDirectory(const CStdString& strDirectory,
252                                           CFileItemList& items)
253 {
254   bool result;
255   if (strDirectory.Equals("addons://downloading/"))
256   {
257     VECADDONS addons;
258     CAddonInstaller::Get().GetInstallList(addons);
259
260     CURL url(strDirectory);
261     CAddonsDirectory::GenerateListing(url,addons,items);
262     result = true;
263     items.SetProperty("reponame",g_localizeStrings.Get(24067));
264     items.SetPath(strDirectory);
265
266     if (m_guiState.get() && !m_guiState->HideParentDirItems())
267     {
268       CFileItemPtr pItem(new CFileItem(".."));
269       pItem->SetPath(m_history.GetParentPath());
270       pItem->m_bIsFolder = true;
271       pItem->m_bIsShareOrDrive = false;
272       items.AddFront(pItem, 0);
273     }
274
275   }
276   else
277     result = CGUIMediaWindow::GetDirectory(strDirectory,items);
278
279   if (strDirectory.IsEmpty() && CAddonInstaller::Get().IsDownloading())
280   {
281     CFileItemPtr item(new CFileItem("addons://downloading/",true));
282     item->SetLabel(g_localizeStrings.Get(24067));
283     item->SetLabelPreformated(true);
284     item->SetIconImage("DefaultNetwork.png");
285     items.Add(item);
286   }
287
288   items.SetContent("addons");
289
290   for (int i=0;i<items.Size();++i)
291     SetItemLabel2(items[i]);
292
293   return result;
294 }
295
296 void CGUIWindowAddonBrowser::SetItemLabel2(CFileItemPtr item)
297 {
298   if (!item || item->m_bIsFolder) return;
299   unsigned int percent;
300   if (CAddonInstaller::Get().GetProgress(item->GetProperty("Addon.ID").asString(), percent))
301   {
302     CStdString progress;
303     progress.Format(g_localizeStrings.Get(24042).c_str(), percent);
304     item->SetProperty("Addon.Status", progress);
305     item->SetProperty("Addon.Downloading", true);
306   }
307   else
308     item->ClearProperty("Addon.Downloading");
309   item->SetLabel2(item->GetProperty("Addon.Status").asString());
310   // to avoid the view state overriding label 2
311   item->SetLabelPreformated(true);
312 }
313
314 bool CGUIWindowAddonBrowser::Update(const CStdString &strDirectory)
315 {
316   if (m_thumbLoader.IsLoading())
317     m_thumbLoader.StopThread();
318
319   if (!CGUIMediaWindow::Update(strDirectory))
320     return false;
321
322   m_thumbLoader.Load(*m_vecItems);
323
324   return true;
325 }
326
327 int CGUIWindowAddonBrowser::SelectAddonID(TYPE type, CStdString &addonID, bool showNone /*= false*/)
328 {
329   vector<ADDON::TYPE> types;
330   types.push_back(type);
331   return SelectAddonID(types, addonID, showNone);
332 }
333
334 int CGUIWindowAddonBrowser::SelectAddonID(ADDON::TYPE type, CStdStringArray &addonIDs, bool showNone /*= false*/, bool multipleSelection /*= true*/)
335 {
336   vector<ADDON::TYPE> types;
337   types.push_back(type);
338   return SelectAddonID(types, addonIDs, showNone, multipleSelection);
339 }
340
341 int CGUIWindowAddonBrowser::SelectAddonID(const vector<ADDON::TYPE> &types, CStdString &addonID, bool showNone /*= false*/)
342 {
343   CStdStringArray addonIDs;
344   if (!addonID.IsEmpty())
345     addonIDs.push_back(addonID);
346   int retval = SelectAddonID(types, addonIDs, showNone, false);
347   if (addonIDs.size() > 0)
348     addonID = addonIDs.at(0);
349   else
350     addonID = "";
351   return retval;
352 }
353
354 int CGUIWindowAddonBrowser::SelectAddonID(const vector<ADDON::TYPE> &types, CStdStringArray &addonIDs, bool showNone /*= false*/, bool multipleSelection /*= true*/)
355 {
356   CGUIDialogSelect *dialog = (CGUIDialogSelect*)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
357   if (!dialog)
358     return 0;
359
360   CFileItemList items;
361   CStdString heading;
362   int iTypes = 0;
363   for (vector<ADDON::TYPE>::const_iterator it = types.begin(); it != types.end(); ++it)
364   {
365     if (*it == ADDON_UNKNOWN)
366       continue;
367     ADDON::VECADDONS addons;
368     iTypes++;
369     if (*it == ADDON_AUDIO)
370       CAddonsDirectory::GetScriptsAndPlugins("audio",addons);
371     else if (*it == ADDON_EXECUTABLE)
372       CAddonsDirectory::GetScriptsAndPlugins("executable",addons);
373     else if (*it == ADDON_IMAGE)
374       CAddonsDirectory::GetScriptsAndPlugins("image",addons);
375     else if (*it == ADDON_VIDEO)
376       CAddonsDirectory::GetScriptsAndPlugins("video",addons);
377     else
378       CAddonMgr::Get().GetAddons(*it, addons);
379     for (ADDON::IVECADDONS it2 = addons.begin() ; it2 != addons.end() ; ++it2)
380     {
381       CFileItemPtr item(CAddonsDirectory::FileItemFromAddon(*it2, ""));
382       if (!items.Contains(item->GetPath()))
383         items.Add(item);
384     }
385
386     if (!heading.IsEmpty())
387       heading += ", ";
388     heading += TranslateType(*it, true);
389   }
390
391   if (iTypes == 0)
392     return 0;
393
394   dialog->SetHeading(heading);
395   dialog->Reset();
396   dialog->SetUseDetails(true);
397   if (multipleSelection || iTypes > 1)
398   {
399     showNone = false;
400     dialog->EnableButton(true, 186);
401   }
402   else
403     dialog->EnableButton(true, 21452);
404   if (showNone)
405   {
406     CFileItemPtr item(new CFileItem("", false));
407     item->SetLabel(g_localizeStrings.Get(231));
408     item->SetLabel2(g_localizeStrings.Get(24040));
409     item->SetIconImage("DefaultAddonNone.png");
410     item->SetSpecialSort(SORT_ON_TOP);
411     items.Add(item);
412   }
413   items.Sort(SORT_METHOD_LABEL, SORT_ORDER_ASC);
414
415   if (addonIDs.size() > 0)
416   {
417     for (CStdStringArray::const_iterator it = addonIDs.begin(); it != addonIDs.end() ; it++)
418     {
419       CFileItemPtr item = items.Get(*it);
420       if (item)
421         item->Select(true);
422     }
423   }
424   dialog->SetItems(&items);
425   dialog->SetMultiSelection(multipleSelection);
426   dialog->DoModal();
427   if (!multipleSelection && iTypes == 1 && dialog->IsButtonPressed())
428   { // switch to the addons browser.
429     vector<CStdString> params;
430     params.push_back("addons://all/"+TranslateType(types[0],false)+"/");
431     params.push_back("return");
432     g_windowManager.ActivateWindow(WINDOW_ADDON_BROWSER, params);
433     return 2;
434   }
435   if (!multipleSelection && dialog->GetSelectedLabel() == -1)
436     return 0;
437   addonIDs.clear();
438   const CFileItemList& list = dialog->GetSelectedItems();
439   for (int i = 0 ; i < list.Size() ; i++)
440     addonIDs.push_back(list.Get(i)->GetPath());
441   return 1;
442 }
443
444 CStdString CGUIWindowAddonBrowser::GetStartFolder(const CStdString &dir)
445 {
446   if (dir.Left(9).Equals("addons://"))
447     return dir;
448   return CGUIMediaWindow::GetStartFolder(dir);
449 }