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