Added "Nothing New" indicator after check for new packages with no new results
[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 "util/gettext.hpp"
28
29 namespace {
30
31 #define IS_REPOSITORY_MENU_ID(idx) ((idx - MNID_ADDON_LIST_START) % 2 == 0)
32 #define IS_INSTALLED_MENU_ID(idx) ((idx - MNID_ADDON_LIST_START) % 2 == 1)
33
34 #define MAKE_REPOSITORY_MENU_ID(idx) (MNID_ADDON_LIST_START + 2*idx+0)
35 #define MAKE_INSTALLED_MENU_ID(idx) (MNID_ADDON_LIST_START + 2*idx+1)
36
37 #define UNPACK_REPOSITORY_MENU_ID(idx) (((idx - MNID_ADDON_LIST_START) - 0) / 2)
38 #define UNPACK_INSTALLED_MENU_ID(idx) (((idx - MNID_ADDON_LIST_START) - 1) / 2)
39
40 std::string addon_type_to_translated_string(Addon::Type type)
41 {
42   switch (type)
43   {
44     case Addon::LEVELSET:
45       return _("Levelset");
46
47     case Addon::WORLDMAP:
48       return _("Worldmap");
49
50     case Addon::WORLD:
51       return _("World");
52
53     default:
54       return _("Unknown");
55   }
56 }
57
58 std::string generate_menu_item_text(const Addon& addon)
59 {
60   std::string text;
61   std::string type = addon_type_to_translated_string(addon.get_type());
62
63   if(!addon.get_author().empty())
64   {
65     text = str(boost::format(_("%s \"%s\" by \"%s\""))
66                % type % addon.get_title() % addon.get_author());
67   }
68   else
69   {
70     // Only addon type and name, no need for translation.
71     text = str(boost::format("%s \"%s\"")
72                % type % addon.get_title());
73   }
74
75   return text;
76 }
77
78 } // namespace
79
80 AddonMenu::AddonMenu() :
81   m_addon_manager(*AddonManager::current()),
82   m_installed_addons(),
83   m_repository_addons()
84 {
85   refresh();
86 }
87
88 void
89 AddonMenu::refresh()
90 {
91   m_installed_addons = m_addon_manager.get_installed_addons();
92   m_repository_addons = m_addon_manager.get_repository_addons();
93
94 #ifdef GRUMBEL
95   std::sort(m_addons.begin(), m_addons.end(),
96             [](const Addon& lhs, const Addon& rhs)
97             {
98               return lhs.title < lhs.title;
99             });
100 #endif
101
102   rebuild_menu();
103 }
104
105 void
106 AddonMenu::rebuild_menu()
107 {
108   clear();
109   add_label(_("Add-ons"));
110   add_hl();
111
112
113   if (m_installed_addons.empty())
114   {
115     add_inactive(MNID_NOTHING_NEW, _("No Addons installed"));
116   }
117   else
118   {
119     int idx = 0;
120     for (const auto& addon_id : m_installed_addons)
121     {
122       const Addon& addon = m_addon_manager.get_installed_addon(addon_id);
123       std::string text = generate_menu_item_text(addon);
124       add_toggle(MAKE_INSTALLED_MENU_ID(idx), text, addon.is_enabled());
125       idx += 1;
126     }
127   }
128
129   add_hl();
130
131   {
132     bool have_new_stuff = false;
133     int idx = 0;
134     for (const auto& addon_id : m_repository_addons)
135     {
136       const Addon& addon = m_addon_manager.get_repository_addon(addon_id);
137       try
138       {
139         // addon is already installed, so check if they are the same
140         Addon& installed_addon = m_addon_manager.get_installed_addon(addon_id);
141         if (installed_addon.get_md5() == addon.get_md5() ||
142             installed_addon.get_version() > addon.get_version())
143         {
144           log_debug << "ignoring already installed addon " << installed_addon.get_id() << std::endl;
145         }
146         else
147         {
148           log_debug << installed_addon.get_id() << " is installed, but updated: '"
149                     << installed_addon.get_md5() << "' vs '" << addon.get_md5() << "'  '"
150                     << installed_addon.get_version() << "' vs '" << addon.get_version() << "'"
151                     << std::endl;
152           std::string text = generate_menu_item_text(addon);
153           add_entry(MAKE_REPOSITORY_MENU_ID(idx), "Install " + text + " *NEW*");
154           have_new_stuff = true;
155         }
156       }
157       catch(const std::exception& err)
158       {
159         // addon is not installed
160         std::string text = generate_menu_item_text(addon);
161         add_entry(MAKE_REPOSITORY_MENU_ID(idx), "Install " + text);
162         have_new_stuff = true;
163       }
164       idx += 1;
165     }
166
167     if (!have_new_stuff && m_addon_manager.has_been_updated())
168     {
169       add_inactive(MNID_NOTHING_NEW, _("No new Addons found"));
170     }
171   }
172
173   if (!m_addon_manager.has_online_support())
174   {
175     add_inactive(MNID_CHECK_ONLINE, std::string(_("Check Online (disabled)")));
176   }
177   else
178   {
179     add_entry(MNID_CHECK_ONLINE, std::string(_("Check Online")));
180   }
181
182   add_hl();
183   add_back(_("Back"));
184 }
185
186 void
187 AddonMenu::menu_action(MenuItem* item)
188 {
189   if (item->id == MNID_CHECK_ONLINE) // check if "Check Online" was chosen
190   {
191     try
192     {
193       m_addon_manager.check_online();
194       refresh();
195     }
196     catch (std::exception& e)
197     {
198       log_warning << "Check for available Add-ons failed: " << e.what() << std::endl;
199     }
200   }
201   else if (MNID_ADDON_LIST_START <= item->id)
202   {
203     if (IS_INSTALLED_MENU_ID(item->id))
204     {
205       int idx = UNPACK_INSTALLED_MENU_ID(item->id);
206       if (0 <= idx && idx < static_cast<int>(m_installed_addons.size()))
207       {
208         const Addon& addon = m_addon_manager.get_installed_addon(m_installed_addons[idx]);
209         if(addon.is_enabled())
210         {
211           m_addon_manager.disable_addon(addon.get_id());
212           set_toggled(item->id, addon.is_enabled());
213         }
214         else
215         {
216           m_addon_manager.enable_addon(addon.get_id());
217           set_toggled(item->id, addon.is_enabled());
218         }
219       }
220     }
221     else if (IS_REPOSITORY_MENU_ID(item->id))
222     {
223       int idx = UNPACK_REPOSITORY_MENU_ID(item->id);
224       if (0 <= idx && idx < static_cast<int>(m_repository_addons.size()))
225       {
226         const Addon& addon = m_addon_manager.get_repository_addon(m_repository_addons[idx]);
227         try
228         {
229           m_addon_manager.install_addon(addon.get_id());
230           m_addon_manager.enable_addon(addon.get_id());
231         }
232         catch(const std::exception& err)
233         {
234           log_warning << "Enabling addon failed: " << err.what() << std::endl;
235         }
236         refresh();
237       }
238     }
239   }
240   else
241   {
242        log_warning << "Unknown menu item clicked: " << item->id << std::endl;
243   }
244 }
245
246 /* EOF */