First round of cleanup up the AddonManager a bit
authorIngo Ruhnke <grumbel@gmail.com>
Mon, 18 Aug 2014 21:21:39 +0000 (23:21 +0200)
committerIngo Ruhnke <grumbel@gmail.com>
Mon, 25 Aug 2014 07:52:10 +0000 (09:52 +0200)
src/addon/addon.cpp
src/addon/addon.hpp
src/addon/addon_manager.cpp
src/addon/addon_manager.hpp
src/supertux/menu/addon_menu.cpp
src/supertux/menu/addon_menu.hpp

index 2ab094e..44764ee 100644 (file)
 std::string
 Addon::get_md5() const
 {
-  if (!installed) {
-    if (stored_md5 == "") { log_warning << "Add-on not installed and no stored MD5 available" << std::endl; }
+  if (!installed)
+  {
+    if (stored_md5.empty())
+    {
+      log_warning << "Add-on not installed and no stored MD5 available" << std::endl;
+    }
     return stored_md5;
   }
-
-  if (calculated_md5 != "") return calculated_md5;
-
-  if (installed_physfs_filename == "") throw std::runtime_error("Tried to calculate MD5 of Add-on with unknown filename");
-
-  // TODO: this does not work as expected for some files -- IFileStream seems to not always behave like an ifstream.
-  //IFileStream ifs(installed_physfs_filename);
-  //std::string md5 = MD5(ifs).hex_digest();
-
-  MD5 md5;
-  PHYSFS_file* file;
-  file = PHYSFS_openRead(installed_physfs_filename.c_str());
-  unsigned char buffer[1024];
-  while (true) {
-    PHYSFS_sint64 len = PHYSFS_read(file, buffer, 1, sizeof(buffer));
-    if (len <= 0) break;
-    md5.update(buffer, len);
+  else if (!calculated_md5.empty())
+  {
+    return calculated_md5;
+  }
+  else if (installed_physfs_filename.empty())
+  {
+    throw std::runtime_error("Tried to calculate MD5 of Add-on with unknown filename");
+  }
+  else
+  {
+    // TODO: this does not work as expected for some files -- IFileStream seems to not always behave like an ifstream.
+    //IFileStream ifs(installed_physfs_filename);
+    //std::string md5 = MD5(ifs).hex_digest();
+
+    MD5 md5;
+    PHYSFS_file* file;
+    file = PHYSFS_openRead(installed_physfs_filename.c_str());
+    unsigned char buffer[1024];
+    while (true) {
+      PHYSFS_sint64 len = PHYSFS_read(file, buffer, 1, sizeof(buffer));
+      if (len <= 0) break;
+      md5.update(buffer, len);
+    }
+    PHYSFS_close(file);
+
+    calculated_md5 = md5.hex_digest();
+    log_debug << "MD5 of " << title << ": " << calculated_md5 << std::endl;
+
+    return calculated_md5;
   }
-  PHYSFS_close(file);
-
-  calculated_md5 = md5.hex_digest();
-  log_debug << "MD5 of " << title << ": " << calculated_md5 << std::endl;
-
-  return calculated_md5;
 }
 
 void
 Addon::parse(const Reader& lisp)
 {
-  try {
+  try
+  {
     lisp.get("kind", kind);
     lisp.get("title", title);
     lisp.get("author", author);
@@ -70,52 +81,36 @@ Addon::parse(const Reader& lisp)
     lisp.get("http-url", http_url);
     lisp.get("file", suggested_filename);
     lisp.get("md5", stored_md5);
-  } catch(std::exception& e) {
+  }
+  catch(const std::exception& err)
+  {
     std::stringstream msg;
-    msg << "Problem when parsing addoninfo: " << e.what();
+    msg << "Problem when parsing addoninfo: " << err.what();
     throw std::runtime_error(msg.str());
   }
 }
 
 void
-Addon::parse(std::string fname)
+Addon::parse(const std::string& fname)
 {
-  try {
+  try
+  {
     lisp::Parser parser;
     const lisp::Lisp* root = parser.parse(fname);
     const lisp::Lisp* addon = root->get_lisp("supertux-addoninfo");
     if(!addon) throw std::runtime_error("file is not a supertux-addoninfo file.");
     parse(*addon);
-  } catch(std::exception& e) {
+  }
+  catch(const std::exception& err)
+  {
     std::stringstream msg;
-    msg << "Problem when reading addoninfo '" << fname << "': " << e.what();
+    msg << "Problem when reading addoninfo '" << fname << "': " << err.what();
     throw std::runtime_error(msg.str());
   }
 }
 
-void
-Addon::write(lisp::Writer& writer) const
-{
-  writer.start_list("supertux-addoninfo");
-  if (kind != "") writer.write("kind", kind);
-  if (title != "") writer.write("title", title);
-  if (author != "") writer.write("author", author);
-  if (license != "") writer.write("license", license);
-  if (http_url != "") writer.write("http-url", http_url);
-  if (suggested_filename != "") writer.write("file", suggested_filename);
-  if (stored_md5 != "") writer.write("md5", stored_md5);
-  writer.end_list("supertux-addoninfo");
-}
-
-void
-Addon::write(std::string fname) const
-{
-  lisp::Writer writer(fname);
-  write(writer);
-}
-
 bool
-Addon::operator==(Addon addon2) const
+Addon::operator==(const Addon& addon2) const
 {
   std::string s1 = this->get_md5();
   std::string s2 = addon2.get_md5();
index 69b0bd9..e78990a 100644 (file)
 #include <string>
 
 #include "util/reader_fwd.hpp"
-#include "util/writer_fwd.hpp"
 
-/**
- * Represents an (available or installed) Add-on, e.g. a level set
- */
+/** Represents an (available or installed) Add-on, e.g. a level set */
 class Addon
 {
 public:
+  int id;
   std::string kind;
   std::string title;
   std::string author;
   std::string license;
   std::string http_url;
+
   /** filename suggested by addon author, e.g. "pak0.zip" */
   std::string suggested_filename;
+
   /** PhysFS filename on disk, e.g. "pak0.zip" */
   std::string installed_physfs_filename;
+
   /** complete path and filename on disk, e.g. "/home/sommer/.supertux2/pak0.zip" */
   std::string installed_absolute_filename;
+
   std::string stored_md5;
   bool installed;
   bool loaded;
 
-  /**
-   * Get MD5, based either on installed file's contents or stored value
-   */
+  /** Get MD5, based either on installed file's contents or stored value */
   std::string get_md5() const;
 
-  /**
-   * Read additional information from given contents of a (supertux-addoninfo ...) block
-   */
+  /** Read additional information from given contents of a (supertux-addoninfo ...) block */
   void parse(const Reader& lisp);
 
-  /**
-   * Read additional information from given file
-   */
-  void parse(std::string fname);
+  /** Read additional information from given file */
+  void parse(const std::string& fname);
 
-  /**
-   * Writes out Add-on metainformation to a Lisp Writer
-   */
-  void write(Writer& writer) const;
-
-  /**
-   * Writes out Add-on metainformation to a file
-   */
-  void write(std::string fname) const;
-
-  /**
-   * Checks if Add-on is the same as given one.
-   * If available, checks MD5 sum, else relies on kind, author and title alone.
-   */
-  bool operator==(Addon addon2) const;
+  /** Checks if Add-on is the same as given one. If available, checks
+      MD5 sum, else relies on kind, author and title alone. */
+  bool operator==(const Addon& addon2) const;
 
 protected:
   friend class AddonManager;
 
   mutable std::string calculated_md5;
 
-  Addon() :
+  Addon(int id_) :
+    id(id_),
     kind(),
     title(),
     author(),
@@ -93,6 +78,10 @@ protected:
     loaded(),
     calculated_md5()
   {};
+
+private:
+  Addon(const Addon&) = delete;
+  Addon& operator=(const Addon&) = delete;
 };
 
 #endif
index 5ae64df..e752ff2 100644 (file)
@@ -61,9 +61,9 @@ size_t my_curl_physfs_write(void *ptr, size_t size, size_t nmemb, void *f_p)
 }
 #endif
 
-AddonManager::AddonManager(std::vector<std::string>& ignored_addon_filenames_) :
-  addons(),
-  ignored_addon_filenames(ignored_addon_filenames_)
+AddonManager::AddonManager(std::vector<std::string>& ignored_addon_filenames) :
+  m_addons(),
+  m_ignored_addon_filenames(ignored_addon_filenames)
 {
 #ifdef HAVE_LIBCURL
   curl_global_init(CURL_GLOBAL_ALL);
@@ -75,12 +75,23 @@ AddonManager::~AddonManager()
 #ifdef HAVE_LIBCURL
   curl_global_cleanup();
 #endif
+}
 
-  for (std::vector<Addon*>::iterator i = addons.begin(); i != addons.end(); i++) delete *i;
+Addon&
+AddonManager::get_addon(int id)
+{
+  if (0 <= id && id < static_cast<int>(m_addons.size()))
+  {
+    return *m_addons[id];
+  }
+  else
+  {
+    throw std::runtime_error("AddonManager::get_addon(): id out of range: " + std::to_string(id));
+  }
 }
 
-std::vector<Addon*>
-AddonManager::get_addons()
+const std::vector<std::unique_ptr<Addon> >&
+AddonManager::get_addons() const
 {
   /*
     for (std::vector<Addon>::iterator it = installed_addons.begin(); it != installed_addons.end(); ++it) {
@@ -88,7 +99,17 @@ AddonManager::get_addons()
     if (addon.md5 == "") addon.md5 = calculate_md5(addon);
     }
   */
-  return addons;
+  return m_addons;
+}
+
+bool
+AddonManager::has_online_support() const
+{
+#ifdef HAVE_LIBCURL
+  return true;
+#else
+  return false;
+#endif
 }
 
 void
@@ -136,14 +157,14 @@ AddonManager::check_online()
         log_warning << "Unknown token '" << token << "' in Add-on list" << std::endl;
         continue;
       }
-      std::unique_ptr<Addon> addon(new Addon());
+      std::unique_ptr<Addon> addon(new Addon(m_addons.size()));
       addon->parse(*(iter.lisp()));
       addon->installed = false;
       addon->loaded = false;
 
       // make sure the list of known Add-ons does not already contain this one
       bool exists = false;
-      for (std::vector<Addon*>::const_iterator i = addons.begin(); i != addons.end(); i++) {
+      for (auto i = m_addons.begin(); i != m_addons.end(); ++i) {
         if (**i == *addon) {
           exists = true;
           break;
@@ -161,7 +182,7 @@ AddonManager::check_online()
       }
       else
       {
-        addons.push_back(addon.release());
+        m_addons.push_back(std::move(addon));
       }
     }
   } catch(std::exception& e) {
@@ -174,31 +195,31 @@ AddonManager::check_online()
 }
 
 void
-AddonManager::install(Addon* addon)
+AddonManager::install(Addon& addon)
 {
 #ifdef HAVE_LIBCURL
 
-  if (addon->installed) throw std::runtime_error("Tried installing installed Add-on");
+  if (addon.installed) throw std::runtime_error("Tried installing installed Add-on");
 
   // make sure the Add-on's file name does not contain weird characters
-  if (addon->suggested_filename.find_first_not_of("match.quiz-proxy_gwenblvdjfks0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") != std::string::npos) {
-    throw std::runtime_error("Add-on has unsafe file name (\""+addon->suggested_filename+"\")");
+  if (addon.suggested_filename.find_first_not_of("match.quiz-proxy_gwenblvdjfks0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") != std::string::npos) {
+    throw std::runtime_error("Add-on has unsafe file name (\""+addon.suggested_filename+"\")");
   }
 
-  std::string fileName = addon->suggested_filename;
+  std::string fileName = addon.suggested_filename;
 
   // make sure its file doesn't already exist
   if (PHYSFS_exists(fileName.c_str())) {
-    fileName = addon->stored_md5 + "_" + addon->suggested_filename;
+    fileName = addon.stored_md5 + "_" + addon.suggested_filename;
     if (PHYSFS_exists(fileName.c_str())) {
-      throw std::runtime_error("Add-on of suggested filename already exists (\""+addon->suggested_filename+"\", \""+fileName+"\")");
+      throw std::runtime_error("Add-on of suggested filename already exists (\""+addon.suggested_filename+"\", \""+fileName+"\")");
     }
   }
 
   char error_buffer[CURL_ERROR_SIZE+1];
 
-  char* url = (char*)malloc(addon->http_url.length() + 1);
-  strncpy(url, addon->http_url.c_str(), addon->http_url.length() + 1);
+  char* url = (char*)malloc(addon.http_url.length() + 1);
+  strncpy(url, addon.http_url.c_str(), addon.http_url.length() + 1);
 
   PHYSFS_file* f = PHYSFS_openWrite(fileName.c_str());
 
@@ -227,21 +248,21 @@ AddonManager::install(Addon* addon)
     throw std::runtime_error("Downloading Add-on failed: " + why);
   }
 
-  addon->installed = true;
-  addon->installed_physfs_filename = fileName;
+  addon.installed = true;
+  addon.installed_physfs_filename = fileName;
   static const std::string writeDir = PHYSFS_getWriteDir();
   static const std::string dirSep = PHYSFS_getDirSeparator();
-  addon->installed_absolute_filename = writeDir + dirSep + fileName;
-  addon->loaded = false;
+  addon.installed_absolute_filename = writeDir + dirSep + fileName;
+  addon.loaded = false;
 
-  if (addon->get_md5() != addon->stored_md5) {
-    addon->installed = false;
+  if (addon.get_md5() != addon.stored_md5) {
+    addon.installed = false;
     PHYSFS_delete(fileName.c_str());
     std::string why = "MD5 checksums differ";
     throw std::runtime_error("Downloading Add-on failed: " + why);
   }
 
-  log_debug << "Finished downloading \"" << addon->installed_absolute_filename << "\". Enabling Add-on." << std::endl;
+  log_debug << "Finished downloading \"" << addon.installed_absolute_filename << "\". Enabling Add-on." << std::endl;
 
   enable(addon);
 
@@ -252,88 +273,91 @@ AddonManager::install(Addon* addon)
 }
 
 void
-AddonManager::remove(Addon* addon)
+AddonManager::remove(Addon& addon)
 {
-  if (!addon->installed) throw std::runtime_error("Tried removing non-installed Add-on");
+  if (!addon.installed) throw std::runtime_error("Tried removing non-installed Add-on");
 
   //FIXME: more checks
 
   // make sure the Add-on's file name does not contain weird characters
-  if (addon->installed_physfs_filename.find_first_not_of("match.quiz-proxy_gwenblvdjfks0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") != std::string::npos) {
-    throw std::runtime_error("Add-on has unsafe file name (\""+addon->installed_physfs_filename+"\")");
+  if (addon.installed_physfs_filename.find_first_not_of("match.quiz-proxy_gwenblvdjfks0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") != std::string::npos) {
+    throw std::runtime_error("Add-on has unsafe file name (\""+addon.installed_physfs_filename+"\")");
   }
 
   unload(addon);
 
-  log_debug << "deleting file \"" << addon->installed_absolute_filename << "\"" << std::endl;
-  PHYSFS_delete(addon->installed_absolute_filename.c_str());
-  addon->installed = false;
+  log_debug << "deleting file \"" << addon.installed_absolute_filename << "\"" << std::endl;
+  PHYSFS_delete(addon.installed_absolute_filename.c_str());
+  addon.installed = false;
 
   // FIXME: As we don't know anything more about it (e.g. where to get it), remove it from list of known Add-ons
 }
 
 void
-AddonManager::disable(Addon* addon)
+AddonManager::disable(Addon& addon)
 {
   unload(addon);
 
-  std::string fileName = addon->installed_physfs_filename;
-  if (std::find(ignored_addon_filenames.begin(), ignored_addon_filenames.end(), fileName) == ignored_addon_filenames.end()) {
-    ignored_addon_filenames.push_back(fileName);
+  std::string fileName = addon.installed_physfs_filename;
+  if (std::find(m_ignored_addon_filenames.begin(), m_ignored_addon_filenames.end(), fileName) == m_ignored_addon_filenames.end()) {
+    m_ignored_addon_filenames.push_back(fileName);
   }
 }
 
 void
-AddonManager::enable(Addon* addon)
+AddonManager::enable(Addon& addon)
 {
   load(addon);
 
-  std::string fileName = addon->installed_physfs_filename;
-  std::vector<std::string>::iterator i = std::find(ignored_addon_filenames.begin(), ignored_addon_filenames.end(), fileName);
-  if (i != ignored_addon_filenames.end()) {
-    ignored_addon_filenames.erase(i);
+  std::string fileName = addon.installed_physfs_filename;
+  std::vector<std::string>::iterator i = std::find(m_ignored_addon_filenames.begin(), m_ignored_addon_filenames.end(), fileName);
+  if (i != m_ignored_addon_filenames.end()) {
+    m_ignored_addon_filenames.erase(i);
   }
 }
 
 void
-AddonManager::unload(Addon* addon)
+AddonManager::unload(Addon& addon)
 {
-  if (!addon->installed) throw std::runtime_error("Tried unloading non-installed Add-on");
-  if (!addon->loaded) return;
+  if (!addon.installed) throw std::runtime_error("Tried unloading non-installed Add-on");
+  if (!addon.loaded) return;
 
-  log_debug << "Removing archive \"" << addon->installed_absolute_filename << "\" from search path" << std::endl;
-  if (PHYSFS_removeFromSearchPath(addon->installed_absolute_filename.c_str()) == 0) {
-    log_warning << "Could not remove " << addon->installed_absolute_filename << " from search path. Ignoring." << std::endl;
+  log_debug << "Removing archive \"" << addon.installed_absolute_filename << "\" from search path" << std::endl;
+  if (PHYSFS_removeFromSearchPath(addon.installed_absolute_filename.c_str()) == 0) {
+    log_warning << "Could not remove " << addon.installed_absolute_filename << " from search path. Ignoring." << std::endl;
     return;
   }
 
-  addon->loaded = false;
+  addon.loaded = false;
 }
 
 void
-AddonManager::load(Addon* addon)
+AddonManager::load(Addon& addon)
 {
-  if (!addon->installed) throw std::runtime_error("Tried loading non-installed Add-on");
-  if (addon->loaded) return;
+  if (!addon.installed) throw std::runtime_error("Tried loading non-installed Add-on");
+  if (addon.loaded) return;
 
-  log_debug << "Adding archive \"" << addon->installed_absolute_filename << "\" to search path" << std::endl;
-  if (PHYSFS_addToSearchPath(addon->installed_absolute_filename.c_str(), 0) == 0) {
-    log_warning << "Could not add " << addon->installed_absolute_filename << " to search path. Ignoring." << std::endl;
+  log_debug << "Adding archive \"" << addon.installed_absolute_filename << "\" to search path" << std::endl;
+  if (PHYSFS_addToSearchPath(addon.installed_absolute_filename.c_str(), 0) == 0) {
+    log_warning << "Could not add " << addon.installed_absolute_filename << " to search path. Ignoring." << std::endl;
     return;
   }
 
-  addon->loaded = true;
+  addon.loaded = true;
 }
 
 void
 AddonManager::load_addons()
 {
   // unload all Addons and forget about them
-  for (std::vector<Addon*>::iterator i = addons.begin(); i != addons.end(); i++) {
-    if ((*i)->installed && (*i)->loaded) unload(*i);
-    delete *i;
+  for (auto i = m_addons.begin(); i != m_addons.end(); ++i)
+  {
+    if ((*i)->installed && (*i)->loaded)
+    {
+      unload(**i);
+    }
   }
-  addons.clear();
+  m_addons.clear();
 
   // Search for archives and add them to the search path
   char** rc = PHYSFS_enumerateFiles("/");
@@ -396,27 +420,30 @@ AddonManager::load_addons()
     PHYSFS_freeList(rc2);
 
     // if we have an infoFile, it's an Addon
-    if (infoFileName != "") {
-      try {
-        Addon* addon = new Addon();
+    if (infoFileName != "")
+    {
+      try
+      {
+        std::unique_ptr<Addon> addon(new Addon(m_addons.size()));
         addon->parse(infoFileName);
         addon->installed = true;
         addon->installed_physfs_filename = fileName;
         addon->installed_absolute_filename = fullFilename;
         addon->loaded = true;
-        addons.push_back(addon);
 
         // check if the Addon is disabled
-        if (std::find(ignored_addon_filenames.begin(), ignored_addon_filenames.end(), fileName) != ignored_addon_filenames.end())
+        if (std::find(m_ignored_addon_filenames.begin(), m_ignored_addon_filenames.end(), fileName) != m_ignored_addon_filenames.end())
         {
-          unload(addon);
+          unload(*addon);
         }
 
-      } catch (const std::runtime_error& e) {
+        m_addons.push_back(std::move(addon));
+      }
+      catch (const std::runtime_error& e)
+      {
         log_warning << "Could not load add-on info for " << fullFilename << ", loading as unmanaged:" << e.what() << std::endl;
       }
     }
-
   }
 
   PHYSFS_freeList(rc);
index 9387f4e..50a1fff 100644 (file)
@@ -17,6 +17,7 @@
 #ifndef HEADER_SUPERTUX_ADDON_ADDON_MANAGER_HPP
 #define HEADER_SUPERTUX_ADDON_ADDON_MANAGER_HPP
 
+#include <memory>
 #include <string>
 #include <vector>
 
 
 class Addon;
 
-/**
- * Checks for, installs and removes Add-ons
- */
+typedef int AddonId;
+
+/** Checks for, installs and removes Add-ons */
 class AddonManager : public Currenton<AddonManager>
 {
 public:
   AddonManager(std::vector<std::string>& ignored_addon_filenames_);
   ~AddonManager();
 
-  /**
-   * returns a list of installed Add-ons
-   */
-  std::vector<Addon*> get_addons();
+  /** returns a list of installed Add-ons */
+  const std::vector<std::unique_ptr<Addon> >& get_addons() const;
+
+  /** Returns true if online support is available */
+  bool has_online_support() const;
 
-  /**
-   * downloads list of available Add-ons
-   */
+  /** downloads list of available Add-ons */
   void check_online();
 
-  /**
-   * Download and install Add-on
-   */
-  void install(Addon* addon);
-
-  /**
-   * Physically delete Add-on
-   */
-  void remove(Addon* addon);
-
-  /**
-   * Unload Add-on and mark as not to be loaded automatically
-   */
-  void disable(Addon* addon);
-
-  /**
-   * Load Add-on and mark as to be loaded automatically
-   */
-  void enable(Addon* addon);
-
-  /**
-   * Remove Add-on from search path
-   */
-  void unload(Addon* addon);
-
-  /**
-   * Add Add-on to search path
-   */
-  void load(Addon* addon);
-
-  /**
-   * Loads all enabled Add-ons, i.e. adds them to the search path
-   */
+  /** Download and install Add-on */
+  void install(Addon& addon);
+
+  /** Physically delete Add-on */
+  void remove(Addon& addon);
+
+  /** Unload Add-on and mark as not to be loaded automatically */
+  void disable(Addon& addon);
+
+  /** Load Add-on and mark as to be loaded automatically */
+  void enable(Addon& addon);
+
+  /** Remove Add-on from search path */
+  void unload(Addon& addon);
+
+  /** Add Add-on to search path */
+  void load(Addon& addon);
+
+  /** Loads all enabled Add-ons, i.e. adds them to the search path */
   void load_addons();
 
+  Addon& get_addon(int id);
+  int get_num_addons() const { return static_cast<int>(m_addons.size()); }
+
+private:
+  std::vector<std::unique_ptr<Addon> > m_addons;
+  std::vector<std::string>& m_ignored_addon_filenames;
+
 private:
-  std::vector<Addon*> addons;
-  std::vector<std::string>& ignored_addon_filenames;
+  AddonManager(const AddonManager&) = delete;
+  AddonManager& operator=(const AddonManager&) = delete;
 };
 
 #endif
index a658a96..43ed81c 100644 (file)
 #include "gui/menu_item.hpp"
 #include "util/gettext.hpp"
 
-namespace {
-
-bool generate_addons_menu_sorter(const Addon* a1, const Addon* a2)
-{
-  return a1->title < a2->title;
-}
-
-} // namespace
-
 AddonMenu::AddonMenu() :
-  m_addons()
+  m_addon_manager(*AddonManager::current())
 {
   refresh();
 }
@@ -46,29 +37,38 @@ AddonMenu::refresh()
 {
   clear();
 
-  AddonManager& adm = *AddonManager::current();
-
   // refresh list of addons
-  m_addons = adm.get_addons();
+  const auto& addons_ref = m_addon_manager.get_addons();
+  std::vector<std::reference_wrapper<Addon> > addons;
+  std::transform(addons_ref.begin(), addons_ref.end(), std::back_inserter(addons),
+                 [](const std::unique_ptr<Addon>& addon) -> Addon& {
+                   return *addon.get();
+                 });
 
   // sort list
-  std::sort(m_addons.begin(), m_addons.end(), generate_addons_menu_sorter);
+  std::sort(addons.begin(), addons.end(),
+            [](const Addon& lhs, const Addon& rhs)
+            {
+              return lhs.title < lhs.title;
+            });
 
   add_label(_("Add-ons"));
   add_hl();
 
-  // FIXME: don't use macro, use AddonManager::online_available() or so
-#ifdef HAVE_LIBCURL
-  add_entry(0, std::string(_("Check Online")));
-#else
-  add_inactive(0, std::string(_("Check Online (disabled)")));
-#endif
+  if (!m_addon_manager.has_online_support())
+  {
+    add_inactive(MNID_CHECK_ONLINE, std::string(_("Check Online (disabled)")));
+  }
+  else
+  {
+    add_entry(MNID_CHECK_ONLINE, std::string(_("Check Online")));
+  }
 
   //add_hl();
 
-  for (unsigned int i = 0; i < m_addons.size(); i++)
+  for (auto& addon_ : addons)
   {
-    const Addon& addon = *m_addons[i];
+    Addon& addon = addon_.get();
     std::string text = "";
 
     if (!addon.kind.empty())
@@ -112,7 +112,7 @@ AddonMenu::refresh()
                    % addon.title);
       }
     }
-    add_toggle(ADDON_LIST_START_ID + i, text, addon.loaded);
+    add_toggle(MNID_ADDON_LIST_START + addon.id, text, addon.loaded);
   }
 
   add_hl();
@@ -122,67 +122,58 @@ AddonMenu::refresh()
 void
 AddonMenu::menu_action(MenuItem* item)
 {
-  int index = item->id;
-
-  if (index == -1)
-  {
-    // do nothing
-  }
-  else if (index == 0) // check if "Check Online" was chosen
+  if (item->id == MNID_CHECK_ONLINE) // check if "Check Online" was chosen
   {
     try
     {
-      AddonManager::current()->check_online();
+      m_addon_manager.check_online();
       refresh();
-      set_active_item(index);
+      set_active_item(item->id);
     }
     catch (std::exception& e)
     {
       log_warning << "Check for available Add-ons failed: " << e.what() << std::endl;
     }
   }
-  else
+  else if ((MNID_ADDON_LIST_START <= item->id) && (item->id < MNID_ADDON_LIST_START + m_addon_manager.get_num_addons()))
   {
-    // if one of the Addons listed was chosen, take appropriate action
-    if ((index >= ADDON_LIST_START_ID) && (index < ADDON_LIST_START_ID) + m_addons.size())
+    int addon_id = item->id - MNID_ADDON_LIST_START;
+    Addon& addon = m_addon_manager.get_addon(addon_id);
+    if (!addon.installed)
     {
-      Addon& addon = *m_addons[index - ADDON_LIST_START_ID];
-      if (!addon.installed)
+      try
       {
-        try
-        {
-          AddonManager::current()->install(&addon);
-        }
-        catch (std::exception& e)
-        {
-          log_warning << "Installing Add-on failed: " << e.what() << std::endl;
-        }
-        set_toggled(index, addon.loaded);
+        m_addon_manager.install(addon);
       }
-      else if (!addon.loaded)
+      catch (std::exception& e)
       {
-        try
-        {
-          AddonManager::current()->enable(&addon);
-        }
-        catch (std::exception& e)
-        {
-          log_warning << "Enabling Add-on failed: " << e.what() << std::endl;
-        }
-        set_toggled(index, addon.loaded);
+        log_warning << "Installing Add-on failed: " << e.what() << std::endl;
       }
-      else
+      set_toggled(item->id, addon.loaded);
+    }
+    else if (!addon.loaded)
+    {
+      try
+      {
+        m_addon_manager.enable(addon);
+      }
+      catch (std::exception& e)
+      {
+        log_warning << "Enabling Add-on failed: " << e.what() << std::endl;
+      }
+      set_toggled(item->id, addon.loaded);
+    }
+    else
+    {
+      try
+      {
+        m_addon_manager.disable(addon);
+      }
+      catch (std::exception& e)
       {
-        try
-        {
-          AddonManager::current()->disable(&addon);
-        }
-        catch (std::exception& e)
-        {
-          log_warning << "Disabling Add-on failed: " << e.what() << std::endl;
-        }
-        set_toggled(index, addon.loaded);
+        log_warning << "Disabling Add-on failed: " << e.what() << std::endl;
       }
+      set_toggled(item->id, addon.loaded);
     }
   }
 }
index 640c5bc..3d81aef 100644 (file)
 
 #include "gui/menu.hpp"
 
-enum {
-  ADDON_LIST_START_ID = 10
-};
-
 class Addon;
+class AddonManager;
 
 class AddonMenu : public Menu
 {
 private:
-  std::vector<Addon*> m_addons;
+  enum {
+    MNID_CHECK_ONLINE,
+    MNID_ADDON_LIST_START = 10
+  };
+
+private:
+  AddonManager& m_addon_manager;
 
 public:
   AddonMenu();