2 * Copyright (C) 2005-2013 Team XBMC
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)
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.
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/>.
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/GUIDialogBusy.h"
27 #include "dialogs/GUIDialogYesNo.h"
28 #include "dialogs/GUIDialogSelect.h"
29 #include "dialogs/GUIDialogFileBrowser.h"
30 #include "GUIUserMessages.h"
31 #include "guilib/GUIWindowManager.h"
32 #include "utils/URIUtils.h"
35 #include "filesystem/File.h"
36 #include "filesystem/Directory.h"
37 #include "filesystem/AddonsDirectory.h"
38 #include "addons/AddonInstaller.h"
39 #include "utils/JobManager.h"
40 #include "utils/log.h"
41 #include "threads/SingleLock.h"
42 #include "settings/Settings.h"
43 #include "settings/MediaSourceSettings.h"
44 #include "utils/StringUtils.h"
45 #include "AddonDatabase.h"
46 #include "settings/AdvancedSettings.h"
47 #include "storage/MediaManager.h"
49 #include "guilib/Key.h"
51 #define CONTROL_AUTOUPDATE 5
52 #define CONTROL_SHUTUP 6
53 #define CONTROL_FOREIGNFILTER 7
55 using namespace ADDON;
56 using namespace XFILE;
59 CGUIWindowAddonBrowser::CGUIWindowAddonBrowser(void)
60 : CGUIMediaWindow(WINDOW_ADDON_BROWSER, "AddonBrowser.xml")
64 CGUIWindowAddonBrowser::~CGUIWindowAddonBrowser()
68 bool CGUIWindowAddonBrowser::OnMessage(CGUIMessage& message)
70 switch ( message.GetMessage() )
72 case GUI_MSG_WINDOW_DEINIT:
74 if (m_thumbLoader.IsLoading())
75 m_thumbLoader.StopThread();
78 case GUI_MSG_WINDOW_INIT:
80 m_rootDir.AllowNonLocalSources(false);
82 // is this the first time the window is opened?
83 if (m_vecItems->GetPath() == "?" && message.GetStringParam().empty())
84 m_vecItems->SetPath("");
89 int iControl = message.GetSenderId();
90 if (iControl == CONTROL_AUTOUPDATE)
92 CSettings::Get().ToggleBool("general.addonautoupdate");
95 else if (iControl == CONTROL_SHUTUP)
97 CSettings::Get().ToggleBool("general.addonnotifications");
98 CSettings::Get().Save();
101 else if (iControl == CONTROL_FOREIGNFILTER)
103 CSettings::Get().ToggleBool("general.addonforeignfilter");
104 CSettings::Get().Save();
108 else if (m_viewControl.HasControl(iControl)) // list/thumb control
111 int iItem = m_viewControl.GetSelectedItem();
112 int iAction = message.GetParam1();
114 // iItem is checked for validity inside these routines
115 if (iAction == ACTION_SHOW_INFO)
117 if (!m_vecItems->Get(iItem)->GetProperty("Addon.ID").empty())
118 return CGUIDialogAddonInfo::ShowForItem((*m_vecItems)[iItem]);
124 case GUI_MSG_NOTIFY_ALL:
126 if (message.GetParam1() == GUI_MSG_UPDATE_ITEM && IsActive() && message.GetNumStringParams() == 1)
127 { // update this item
128 for (int i = 0; i < m_vecItems->Size(); ++i)
130 CFileItemPtr item = m_vecItems->Get(i);
131 if (item->GetProperty("Addon.ID") == message.GetStringParam())
143 return CGUIMediaWindow::OnMessage(message);
146 void CGUIWindowAddonBrowser::GetContextButtons(int itemNumber,
147 CContextButtons& buttons)
149 CFileItemPtr pItem = m_vecItems->Get(itemNumber);
150 if (pItem->GetPath().Equals("addons://enabled/"))
151 buttons.Add(CONTEXT_BUTTON_SCAN,24034);
154 if (!CAddonMgr::Get().GetAddon(pItem->GetProperty("Addon.ID").asString(), addon, ADDON_UNKNOWN, false)) // allow disabled addons
157 if (addon->Type() == ADDON_REPOSITORY && pItem->m_bIsFolder)
159 buttons.Add(CONTEXT_BUTTON_SCAN,24034);
160 buttons.Add(CONTEXT_BUTTON_REFRESH,24035);
163 buttons.Add(CONTEXT_BUTTON_INFO,24003);
165 if (addon->HasSettings())
166 buttons.Add(CONTEXT_BUTTON_SETTINGS,24020);
169 bool CGUIWindowAddonBrowser::OnContextButton(int itemNumber,
170 CONTEXT_BUTTON button)
172 CFileItemPtr pItem = m_vecItems->Get(itemNumber);
173 if (pItem->GetPath().Equals("addons://enabled/"))
175 if (button == CONTEXT_BUTTON_SCAN)
177 CAddonMgr::Get().FindAddons();
182 if (!CAddonMgr::Get().GetAddon(pItem->GetProperty("Addon.ID").asString(), addon, ADDON_UNKNOWN, false)) // allow disabled addons
185 if (button == CONTEXT_BUTTON_SETTINGS)
186 return CGUIDialogAddonSettings::ShowAndGetInput(addon);
188 if (button == CONTEXT_BUTTON_REFRESH)
190 CAddonDatabase database;
192 database.DeleteRepository(addon->ID());
193 button = CONTEXT_BUTTON_SCAN;
196 if (button == CONTEXT_BUTTON_SCAN)
198 RepositoryPtr repo = boost::dynamic_pointer_cast<CRepository>(addon);
199 CAddonInstaller::Get().UpdateRepos(true);
203 if (button == CONTEXT_BUTTON_INFO)
205 CGUIDialogAddonInfo::ShowForItem(pItem);
209 return CGUIMediaWindow::OnContextButton(itemNumber, button);
212 class UpdateAddons : public IRunnable
217 CAddonMgr::Get().GetAllOutdatedAddons(addons, true); // get local
218 for (VECADDONS::iterator i = addons.begin(); i != addons.end(); ++i)
220 CStdString referer = StringUtils::Format("Referer=%s-%s.zip",(*i)->ID().c_str(),(*i)->Version().c_str());
221 CAddonInstaller::Get().Install((*i)->ID(), true, referer); // force install
226 bool CGUIWindowAddonBrowser::OnClick(int iItem)
228 CFileItemPtr item = m_vecItems->Get(iItem);
229 if (item->GetPath() == "addons://install/")
231 // pop up filebrowser to grab an installed folder
232 VECSOURCES shares = *CMediaSourceSettings::Get().GetSources("files");
233 g_mediaManager.GetLocalDrives(shares);
234 g_mediaManager.GetNetworkLocations(shares);
236 if (CGUIDialogFileBrowser::ShowAndGetFile(shares, "*.zip", g_localizeStrings.Get(24041), path))
237 CAddonInstaller::Get().InstallFromZip(path);
240 if (item->GetPath() == "addons://update_all/")
242 // fire off a threaded update of all addons
243 UpdateAddons updater;
244 if (CGUIDialogBusy::Wait(&updater))
245 return Update("addons://downloading/");
248 if (!item->m_bIsFolder)
250 // cancel a downloading job
251 if (item->HasProperty("Addon.Downloading"))
253 if (CGUIDialogYesNo::ShowAndGetInput(g_localizeStrings.Get(24000),
254 item->GetProperty("Addon.Name").asString(),
255 g_localizeStrings.Get(24066),""))
257 if (CAddonInstaller::Get().Cancel(item->GetProperty("Addon.ID").asString()))
263 CGUIDialogAddonInfo::ShowForItem(item);
266 if (item->GetPath().Equals("addons://search/"))
267 return Update(item->GetPath());
269 return CGUIMediaWindow::OnClick(iItem);
272 void CGUIWindowAddonBrowser::UpdateButtons()
274 SET_CONTROL_SELECTED(GetID(),CONTROL_AUTOUPDATE, CSettings::Get().GetBool("general.addonautoupdate"));
275 SET_CONTROL_SELECTED(GetID(),CONTROL_SHUTUP, CSettings::Get().GetBool("general.addonnotifications"));
276 SET_CONTROL_SELECTED(GetID(),CONTROL_FOREIGNFILTER, CSettings::Get().GetBool("general.addonforeignfilter"));
277 CGUIMediaWindow::UpdateButtons();
280 static bool FilterVar(bool valid, const CVariant& variant,
281 const std::string& check)
286 if (variant.isNull() || variant.asString().empty())
289 std::string regions = variant.asString();
290 return regions.find(check) == std::string::npos;
293 bool CGUIWindowAddonBrowser::GetDirectory(const CStdString& strDirectory,
294 CFileItemList& items)
297 if (strDirectory.Equals("addons://downloading/"))
300 CAddonInstaller::Get().GetInstallList(addons);
302 CURL url(strDirectory);
303 CAddonsDirectory::GenerateListing(url,addons,items);
305 items.SetProperty("reponame",g_localizeStrings.Get(24067));
306 items.SetPath(strDirectory);
308 if (m_guiState.get() && !m_guiState->HideParentDirItems())
310 CFileItemPtr pItem(new CFileItem(".."));
311 pItem->SetPath(m_history.GetParentPath());
312 pItem->m_bIsFolder = true;
313 pItem->m_bIsShareOrDrive = false;
314 items.AddFront(pItem, 0);
320 result = CGUIMediaWindow::GetDirectory(strDirectory,items);
321 if (CSettings::Get().GetBool("general.addonforeignfilter"))
324 while (i < items.Size())
326 if (!FilterVar(CSettings::Get().GetBool("general.addonforeignfilter"),
327 items[i]->GetProperty("Addon.Language"), "en") ||
328 !FilterVar(CSettings::Get().GetBool("general.addonforeignfilter"),
329 items[i]->GetProperty("Addon.Language"),
330 g_langInfo.GetLanguageLocale()))
340 if (strDirectory.empty() && CAddonInstaller::Get().IsDownloading())
342 CFileItemPtr item(new CFileItem("addons://downloading/",true));
343 item->SetLabel(g_localizeStrings.Get(24067));
344 item->SetLabelPreformated(true);
345 item->SetIconImage("DefaultNetwork.png");
349 items.SetContent("addons");
351 for (int i=0;i<items.Size();++i)
352 SetItemLabel2(items[i]);
357 void CGUIWindowAddonBrowser::SetItemLabel2(CFileItemPtr item)
359 if (!item || item->m_bIsFolder) return;
360 unsigned int percent;
361 if (CAddonInstaller::Get().GetProgress(item->GetProperty("Addon.ID").asString(), percent))
363 CStdString progress = StringUtils::Format(g_localizeStrings.Get(24042).c_str(), percent);
364 item->SetProperty("Addon.Status", progress);
365 item->SetProperty("Addon.Downloading", true);
368 item->ClearProperty("Addon.Downloading");
369 item->SetLabel2(item->GetProperty("Addon.Status").asString());
370 // to avoid the view state overriding label 2
371 item->SetLabelPreformated(true);
374 bool CGUIWindowAddonBrowser::Update(const CStdString &strDirectory, bool updateFilterPath /* = true */)
376 if (m_thumbLoader.IsLoading())
377 m_thumbLoader.StopThread();
379 if (!CGUIMediaWindow::Update(strDirectory, updateFilterPath))
382 m_thumbLoader.Load(*m_vecItems);
387 int CGUIWindowAddonBrowser::SelectAddonID(TYPE type, CStdString &addonID, bool showNone /*= false*/)
389 vector<ADDON::TYPE> types;
390 types.push_back(type);
391 return SelectAddonID(types, addonID, showNone);
394 int CGUIWindowAddonBrowser::SelectAddonID(ADDON::TYPE type, CStdStringArray &addonIDs, bool showNone /*= false*/, bool multipleSelection /*= true*/)
396 vector<ADDON::TYPE> types;
397 types.push_back(type);
398 return SelectAddonID(types, addonIDs, showNone, multipleSelection);
401 int CGUIWindowAddonBrowser::SelectAddonID(const vector<ADDON::TYPE> &types, CStdString &addonID, bool showNone /*= false*/)
403 CStdStringArray addonIDs;
404 if (!addonID.empty())
405 addonIDs.push_back(addonID);
406 int retval = SelectAddonID(types, addonIDs, showNone, false);
407 if (addonIDs.size() > 0)
408 addonID = addonIDs.at(0);
414 int CGUIWindowAddonBrowser::SelectAddonID(const vector<ADDON::TYPE> &types, CStdStringArray &addonIDs, bool showNone /*= false*/, bool multipleSelection /*= true*/)
416 CGUIDialogSelect *dialog = (CGUIDialogSelect*)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
423 for (vector<ADDON::TYPE>::const_iterator it = types.begin(); it != types.end(); ++it)
425 if (*it == ADDON_UNKNOWN)
427 ADDON::VECADDONS addons;
429 if (*it == ADDON_AUDIO)
430 CAddonsDirectory::GetScriptsAndPlugins("audio",addons);
431 else if (*it == ADDON_EXECUTABLE)
432 CAddonsDirectory::GetScriptsAndPlugins("executable",addons);
433 else if (*it == ADDON_IMAGE)
434 CAddonsDirectory::GetScriptsAndPlugins("image",addons);
435 else if (*it == ADDON_VIDEO)
436 CAddonsDirectory::GetScriptsAndPlugins("video",addons);
438 CAddonMgr::Get().GetAddons(*it, addons);
439 for (ADDON::IVECADDONS it2 = addons.begin() ; it2 != addons.end() ; ++it2)
441 CFileItemPtr item(CAddonsDirectory::FileItemFromAddon(*it2, ""));
442 if (!items.Contains(item->GetPath()))
446 if (!heading.empty())
448 heading += TranslateType(*it, true);
454 dialog->SetHeading(heading);
456 dialog->SetUseDetails(true);
457 if (multipleSelection)
459 if (multipleSelection || iTypes > 1)
460 dialog->EnableButton(true, 186);
462 dialog->EnableButton(true, 21452);
465 CFileItemPtr item(new CFileItem("", false));
466 item->SetLabel(g_localizeStrings.Get(231));
467 item->SetLabel2(g_localizeStrings.Get(24040));
468 item->SetIconImage("DefaultAddonNone.png");
469 item->SetSpecialSort(SortSpecialOnTop);
472 items.Sort(SortByLabel, SortOrderAscending);
474 if (addonIDs.size() > 0)
476 for (CStdStringArray::const_iterator it = addonIDs.begin(); it != addonIDs.end() ; it++)
478 CFileItemPtr item = items.Get(*it);
483 dialog->SetItems(&items);
484 dialog->SetMultiSelection(multipleSelection);
486 if (!multipleSelection && iTypes == 1 && dialog->IsButtonPressed())
487 { // switch to the addons browser.
488 vector<CStdString> params;
489 params.push_back("addons://all/"+TranslateType(types[0],false)+"/");
490 params.push_back("return");
491 g_windowManager.ActivateWindow(WINDOW_ADDON_BROWSER, params);
494 if (!dialog->IsConfirmed())
497 const CFileItemList& list = dialog->GetSelectedItems();
498 for (int i = 0 ; i < list.Size() ; i++)
499 addonIDs.push_back(list.Get(i)->GetPath());
503 CStdString CGUIWindowAddonBrowser::GetStartFolder(const CStdString &dir)
505 if (StringUtils::StartsWithNoCase(dir, "addons://"))
507 return CGUIMediaWindow::GetStartFolder(dir);