Added AddonDialog and connected it into the whole non-blocking update stuff
[supertux.git] / src / supertux / menu / addon_menu.cpp
1 //  SuperTux
2 //  Copyright (C) 2009 Ingo Ruhnke <grumbel@gmail.com>
3 //
4 //  This program is free software: you can redistribute it and/or modify
5 //  it under the terms of the GNU General Public License as published by
6 //  the Free Software Foundation, either version 3 of the License, or
7 //  (at your option) any later version.
8 //
9 //  This program is distributed in the hope that it will be useful,
10 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
11 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 //  GNU General Public License for more details.
13 //
14 //  You should have received a copy of the GNU General Public License
15 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
17 #include "supertux/menu/addon_menu.hpp"
18
19 #include <config.h>
20 #include <algorithm>
21 #include <boost/format.hpp>
22
23 #include "addon/addon.hpp"
24 #include "addon/addon_manager.hpp"
25 #include "gui/menu.hpp"
26 #include "gui/menu_item.hpp"
27 #include "gui/menu_manager.hpp"
28 #include "supertux/menu/addon_dialog.hpp"
29 #include "util/gettext.hpp"
30
31 namespace {
32
33 #define IS_REPOSITORY_MENU_ID(idx) ((idx - MNID_ADDON_LIST_START) % 2 == 0)
34 #define IS_INSTALLED_MENU_ID(idx) ((idx - MNID_ADDON_LIST_START) % 2 == 1)
35
36 #define MAKE_REPOSITORY_MENU_ID(idx) (MNID_ADDON_LIST_START + 2*idx+0)
37 #define MAKE_INSTALLED_MENU_ID(idx) (MNID_ADDON_LIST_START + 2*idx+1)
38
39 #define UNPACK_REPOSITORY_MENU_ID(idx) (((idx - MNID_ADDON_LIST_START) - 0) / 2)
40 #define UNPACK_INSTALLED_MENU_ID(idx) (((idx - MNID_ADDON_LIST_START) - 1) / 2)
41
42 std::string addon_type_to_translated_string(Addon::Type type)
43 {
44   switch (type)
45   {
46     case Addon::LEVELSET:
47       return _("Levelset");
48
49     case Addon::WORLDMAP:
50       return _("Worldmap");
51
52     case Addon::WORLD:
53       return _("World");
54
55     default:
56       return _("Unknown");
57   }
58 }
59
60 std::string generate_menu_item_text(const Addon& addon)
61 {
62   std::string text;
63   std::string type = addon_type_to_translated_string(addon.get_type());
64
65   if(!addon.get_author().empty())
66   {
67     text = str(boost::format(_("%s \"%s\" by \"%s\""))
68                % type % addon.get_title() % addon.get_author());
69   }
70   else
71   {
72     // Only addon type and name, no need for translation.
73     text = str(boost::format("%s \"%s\"")
74                % type % addon.get_title());
75   }
76
77   return text;
78 }
79
80 } // namespace
81
82 AddonMenu::AddonMenu() :
83   m_addon_manager(*AddonManager::current()),
84   m_installed_addons(),
85   m_repository_addons()
86 {
87   refresh();
88 }
89
90 void
91 AddonMenu::refresh()
92 {
93   m_installed_addons = m_addon_manager.get_installed_addons();
94   m_repository_addons = m_addon_manager.get_repository_addons();
95
96 #ifdef GRUMBEL
97   std::sort(m_addons.begin(), m_addons.end(),
98             [](const Addon& lhs, const Addon& rhs)
99             {
100               return lhs.title < lhs.title;
101             });
102 #endif
103
104   rebuild_menu();
105 }
106
107 void
108 AddonMenu::rebuild_menu()
109 {
110   clear();
111   add_label(_("Add-ons"));
112   add_hl();
113
114
115   if (m_installed_addons.empty())
116   {
117     add_inactive(MNID_NOTHING_NEW, _("No Addons installed"));
118   }
119   else
120   {
121     int idx = 0;
122     for (const auto& addon_id : m_installed_addons)
123     {
124       const Addon& addon = m_addon_manager.get_installed_addon(addon_id);
125       std::string text = generate_menu_item_text(addon);
126       add_toggle(MAKE_INSTALLED_MENU_ID(idx), text, addon.is_enabled());
127       idx += 1;
128     }
129   }
130
131   add_hl();
132
133   {
134     bool have_new_stuff = false;
135     int idx = 0;
136     for (const auto& addon_id : m_repository_addons)
137     {
138       const Addon& addon = m_addon_manager.get_repository_addon(addon_id);
139       try
140       {
141         // addon is already installed, so check if they are the same
142         Addon& installed_addon = m_addon_manager.get_installed_addon(addon_id);
143         if (installed_addon.get_md5() == addon.get_md5() ||
144             installed_addon.get_version() > addon.get_version())
145         {
146           log_debug << "ignoring already installed addon " << installed_addon.get_id() << std::endl;
147         }
148         else
149         {
150           log_debug << installed_addon.get_id() << " is installed, but updated: '"
151                     << installed_addon.get_md5() << "' vs '" << addon.get_md5() << "'  '"
152                     << installed_addon.get_version() << "' vs '" << addon.get_version() << "'"
153                     << std::endl;
154           std::string text = generate_menu_item_text(addon);
155           add_entry(MAKE_REPOSITORY_MENU_ID(idx), "Install " + text + " *NEW*");
156           have_new_stuff = true;
157         }
158       }
159       catch(const std::exception& err)
160       {
161         // addon is not installed
162         std::string text = generate_menu_item_text(addon);
163         add_entry(MAKE_REPOSITORY_MENU_ID(idx), "Install " + text);
164         have_new_stuff = true;
165       }
166       idx += 1;
167     }
168
169     if (!have_new_stuff && m_addon_manager.has_been_updated())
170     {
171       add_inactive(MNID_NOTHING_NEW, _("No new Addons found"));
172     }
173   }
174
175   if (!m_addon_manager.has_online_support())
176   {
177     add_inactive(MNID_CHECK_ONLINE, std::string(_("Check Online (disabled)")));
178   }
179   else
180   {
181     add_entry(MNID_CHECK_ONLINE, std::string(_("Check Online")));
182   }
183
184   add_hl();
185   add_back(_("Back"));
186 }
187
188 void
189 AddonMenu::menu_action(MenuItem* item)
190 {
191   if (item->id == MNID_CHECK_ONLINE) // check if "Check Online" was chosen
192   {
193     try
194     {
195       m_addon_manager.check_online();
196       refresh();
197     }
198     catch (std::exception& e)
199     {
200       log_warning << "Check for available Add-ons failed: " << e.what() << std::endl;
201     }
202   }
203   else if (MNID_ADDON_LIST_START <= item->id)
204   {
205     if (IS_INSTALLED_MENU_ID(item->id))
206     {
207       int idx = UNPACK_INSTALLED_MENU_ID(item->id);
208       if (0 <= idx && idx < static_cast<int>(m_installed_addons.size()))
209       {
210         const Addon& addon = m_addon_manager.get_installed_addon(m_installed_addons[idx]);
211         if(addon.is_enabled())
212         {
213           m_addon_manager.disable_addon(addon.get_id());
214           set_toggled(item->id, addon.is_enabled());
215         }
216         else
217         {
218           m_addon_manager.enable_addon(addon.get_id());
219           set_toggled(item->id, addon.is_enabled());
220         }
221       }
222     }
223     else if (IS_REPOSITORY_MENU_ID(item->id))
224     {
225       int idx = UNPACK_REPOSITORY_MENU_ID(item->id);
226       if (0 <= idx && idx < static_cast<int>(m_repository_addons.size()))
227       {
228         const Addon& addon = m_addon_manager.get_repository_addon(m_repository_addons[idx]);
229
230         AddonManager::InstallStatusPtr status = m_addon_manager.request_install_addon(addon.get_id());
231
232         std::unique_ptr<AddonDialog> dialog(new AddonDialog(status));
233         MenuManager::instance().set_dialog(std::move(dialog));
234 #ifdef GRUMBEL
235         try
236         {
237           m_addon_manager.install_addon(addon.get_id());
238           m_addon_manager.enable_addon(addon.get_id());
239         }
240         catch(const std::exception& err)
241         {
242           log_warning << "Enabling addon failed: " << err.what() << std::endl;
243         }
244         refresh();
245 #endif
246       }
247     }
248   }
249   else
250   {
251        log_warning << "Unknown menu item clicked: " << item->id << std::endl;
252   }
253 }
254
255 /* EOF */