"Fossies" - the Fresh Open Source Software Archive

Member "flightgear-2020.3.8/src/GUI/CatalogListModel.cxx" (24 Mar 2021, 10958 Bytes) of package /linux/privat/flightgear-2020.3.8.tar.bz2:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "CatalogListModel.cxx" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 2020.3.7_vs_2020.3.8.

    1 // CatalogListModel.cxx - part of GUI launcher using Qt5
    2 //
    3 // Written by James Turner, started March 2015.
    4 //
    5 // Copyright (C) 2015 James Turner <zakalawe@mac.com>
    6 //
    7 // This program is free software; you can redistribute it and/or
    8 // modify it under the terms of the GNU General Public License as
    9 // published by the Free Software Foundation; either version 2 of the
   10 // License, or (at your option) any later version.
   11 //
   12 // This program is distributed in the hope that it will be useful, but
   13 // WITHOUT ANY WARRANTY; without even the implied warranty of
   14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   15 // General Public License for more details.
   16 //
   17 // You should have received a copy of the GNU General Public License
   18 // along with this program; if not, write to the Free Software
   19 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
   20 
   21 #include "CatalogListModel.hxx"
   22 
   23 #include <QDebug>
   24 #include <QUrl>
   25 #include <QTimer>
   26 
   27 // Simgear
   28 #include <simgear/props/props_io.hxx>
   29 #include <simgear/structure/exception.hxx>
   30 #include <simgear/misc/sg_path.hxx>
   31 
   32 #include <simgear/package/Package.hxx>
   33 #include <simgear/package/Install.hxx>
   34 
   35 // FlightGear
   36 #include <Main/globals.hxx>
   37 #include <Network/HTTPClient.hxx>
   38 #include <Main/sentryIntegration.hxx>
   39 
   40 using namespace simgear::pkg;
   41 
   42 class CatalogDelegate : public simgear::pkg::Delegate
   43 {
   44 public:
   45     CatalogDelegate(CatalogListModel* outer) : p(outer) {}
   46 
   47     void catalogRefreshed(CatalogRef catalog, StatusCode) override
   48     {
   49         p->onCatalogStatusChanged(catalog);
   50     }
   51 
   52     void startInstall(InstallRef) override {}
   53     void installProgress(InstallRef, unsigned int, unsigned int) override {}
   54     void finishInstall(InstallRef, StatusCode ) override {}
   55 private:
   56     CatalogListModel* p = nullptr;
   57 };
   58 
   59 
   60 CatalogListModel::CatalogListModel(QObject* pr, const
   61                                    simgear::pkg::RootRef& rootRef) :
   62     QAbstractListModel(pr),
   63     m_packageRoot(rootRef)
   64 {
   65     m_delegate = new CatalogDelegate(this);
   66     m_packageRoot->addDelegate(m_delegate);
   67 
   68     resetData();
   69 }
   70 
   71 CatalogListModel::~CatalogListModel()
   72 {
   73     m_packageRoot->removeDelegate(m_delegate);
   74 }
   75 
   76 void CatalogListModel::resetData()
   77 {
   78     CatalogList updatedCatalogs = m_packageRoot->allCatalogs();
   79     std::sort(updatedCatalogs.begin(), updatedCatalogs.end(),
   80               [](const CatalogRef& catA, const CatalogRef& catB)
   81     {   // lexicographic ordering
   82         return catA->name() < catB->name();
   83     });
   84 
   85     if (updatedCatalogs == m_catalogs)
   86         return;
   87 
   88     beginResetModel();
   89     m_catalogs = updatedCatalogs;
   90     endResetModel();
   91 
   92     emit catalogsChanged();
   93 }
   94 
   95 int CatalogListModel::rowCount(const QModelIndex& parent) const
   96 {
   97     Q_UNUSED(parent)
   98     return static_cast<int>(m_catalogs.size());
   99 }
  100 
  101 QVariant CatalogListModel::data(const QModelIndex& index, int role) const
  102 {
  103     const auto cat = m_catalogs.at(static_cast<size_t>(index.row()));
  104     if (role == Qt::DisplayRole) {
  105         QString name = QString::fromStdString(cat->name());
  106         QString desc;
  107         if (cat->isEnabled()) {
  108             desc = QString::fromStdString(cat->description()).simplified();
  109         } else {
  110             switch (cat->status()) {
  111             case Delegate::FAIL_NOT_FOUND:
  112                 desc = tr("The catalog data was not found on the server at the expected location (URL)");
  113                 break;
  114             case Delegate::FAIL_VERSION:
  115                 desc =  tr("The catalog is not compatible with the version of FlightGear");
  116                 break;
  117             case Delegate::FAIL_HTTP_FORBIDDEN:
  118                 desc = tr("The catalog server is blocking access from some reason (forbidden)");
  119                 break;
  120             default:
  121                 desc = tr("disabled due to an internal error");
  122             }
  123         }
  124         return tr("%1 - %2").arg(name).arg(desc);
  125     } else if (role == CatalogDescriptionRole) {
  126         return QString::fromStdString(cat->description());
  127     } else if (role == CatalogNameRole) {
  128         return QString::fromStdString(cat->name());
  129     } else if (role == Qt::ToolTipRole) {
  130         return QString::fromStdString(cat->url());
  131     } else if (role == CatalogUrlRole) {
  132         return QUrl(QString::fromStdString(cat->url()));
  133     } else if (role == CatalogIdRole) {
  134         return QString::fromStdString(cat->id());
  135     } else if (role == CatalogPackageCountRole) {
  136         return static_cast<quint32>(cat->packages().size());
  137     } else if (role == CatalogInstallCountRole) {
  138         return static_cast<quint32>(cat->installedPackages().size());
  139     } else if (role == CatalogStatusRole) {
  140         return translateStatusForCatalog(cat);
  141     } else if (role == CatalogIsNewlyAdded) {
  142         return (cat == m_newlyAddedCatalog);
  143     } else if (role == CatalogEnabled) {
  144         return cat->isUserEnabled();
  145     }
  146 
  147     return QVariant();
  148 }
  149 
  150 bool CatalogListModel::setData(const QModelIndex &index, const QVariant &value, int role)
  151 {
  152     auto cat = m_catalogs.at(static_cast<size_t>(index.row()));
  153     if (role == CatalogEnabled) {
  154         cat->setUserEnabled(value.toBool());
  155         return true;
  156     }
  157     return false;
  158 }
  159 
  160 Qt::ItemFlags CatalogListModel::flags(const QModelIndex &index) const
  161 {
  162     Qt::ItemFlags r = Qt::ItemIsSelectable;
  163     const auto cat = m_catalogs.at(static_cast<size_t>(index.row()));
  164     if (cat->isEnabled()) {
  165         r |= Qt::ItemIsEnabled;
  166     }
  167     return r;
  168 }
  169 
  170 QHash<int, QByteArray> CatalogListModel::roleNames() const
  171 {
  172     QHash<int, QByteArray> result = QAbstractListModel::roleNames();
  173     result[CatalogUrlRole] = "url";
  174     result[CatalogIdRole] = "id";
  175     result[CatalogDescriptionRole] = "description";
  176     result[CatalogNameRole] = "name";
  177     result[CatalogStatusRole] = "status";
  178     result[CatalogIsNewlyAdded] = "isNewlyAdded";
  179     result[CatalogEnabled] = "enabled";
  180     return result;
  181 }
  182 
  183 void CatalogListModel::removeCatalog(int index)
  184 {
  185     if ((index < 0) || (index >= static_cast<int>(m_catalogs.size()))) {
  186         return;
  187     }
  188 
  189     const std::string removeId = m_catalogs.at(static_cast<size_t>(index))->id();
  190     m_packageRoot->removeCatalogById(removeId);
  191     resetData();
  192 }
  193 
  194 void CatalogListModel::refreshCatalog(int index)
  195 {
  196     if ((index < 0) || (index >= static_cast<int>(m_catalogs.size()))) {
  197         return;
  198     }
  199     m_catalogs.at(static_cast<size_t>(index))->refresh();
  200 }
  201 
  202 void CatalogListModel::installDefaultCatalog(bool showAddFeedback)
  203 {
  204     FGHTTPClient* http = globals->get_subsystem<FGHTTPClient>();
  205     CatalogRef cat = Catalog::createFromUrl(m_packageRoot, http->getDefaultCatalogUrl());
  206     if (showAddFeedback) {
  207       m_newlyAddedCatalog = cat;
  208       emit isAddingCatalogChanged();
  209       emit statusOfAddingCatalogChanged();
  210     }
  211 
  212     resetData();
  213 }
  214 
  215 void CatalogListModel::addCatalogByUrl(QUrl url)
  216 {
  217     if (m_newlyAddedCatalog) {
  218         qWarning() << Q_FUNC_INFO << "already adding a catalog";
  219         return;
  220     }
  221 
  222     m_newlyAddedCatalog = Catalog::createFromUrl(m_packageRoot, url.toString().toStdString());
  223     flightgear::addSentryBreadcrumb("CatalogListModel: Adding catalog " + url.toString().toStdString(), "info");
  224     resetData();
  225     emit isAddingCatalogChanged();
  226 }
  227 
  228 int CatalogListModel::indexOf(QUrl url)
  229 {
  230     std::string urlString = url.toString().toStdString();
  231     auto it = std::find_if(m_catalogs.begin(), m_catalogs.end(),
  232                            [urlString](simgear::pkg::CatalogRef cat) { return cat->url() == urlString;});
  233     if (it == m_catalogs.end())
  234         return -1;
  235 
  236     return static_cast<int>(std::distance(m_catalogs.begin(), it));
  237 }
  238 
  239 void CatalogListModel::finalizeAddCatalog()
  240 {
  241     if (!m_newlyAddedCatalog) {
  242         qWarning() << Q_FUNC_INFO << "no catalog add in progress";
  243         return;
  244     }
  245 
  246     auto it = std::find(m_catalogs.begin(), m_catalogs.end(), m_newlyAddedCatalog);
  247     if (it == m_catalogs.end()) {
  248         qWarning() << Q_FUNC_INFO << "couldn't find new catalog in m_catalogs" << QString::fromStdString(m_newlyAddedCatalog->url());
  249         return;
  250     }
  251 
  252     flightgear::addSentryBreadcrumb("CatalogListModel: finalziing add of:" + m_newlyAddedCatalog->id(), "info");
  253 
  254     const int row = static_cast<int>(std::distance(m_catalogs.begin(), it));
  255     m_newlyAddedCatalog.clear();
  256     emit isAddingCatalogChanged();
  257     emit statusOfAddingCatalogChanged();
  258     emit dataChanged(index(row), index(row));
  259 }
  260 
  261 void CatalogListModel::abandonAddCatalog()
  262 {
  263     if (!m_newlyAddedCatalog)
  264         return;
  265 
  266     m_packageRoot->removeCatalog(m_newlyAddedCatalog);
  267 
  268     m_newlyAddedCatalog.clear();
  269     emit isAddingCatalogChanged();
  270     emit statusOfAddingCatalogChanged();
  271 
  272     resetData();
  273 }
  274 
  275 bool CatalogListModel::isAddingCatalog() const
  276 {
  277     return m_newlyAddedCatalog.get() != nullptr;
  278 }
  279 
  280 void CatalogListModel::onCatalogStatusChanged(Catalog* cat)
  281 {
  282     if (cat == nullptr) {
  283         resetData();
  284         return;
  285     }
  286 
  287     //qInfo() << Q_FUNC_INFO << "for" << QString::fromStdString(cat->url()) << translateStatusForCatalog(cat);
  288 
  289     // download the official catalog often fails with a 404 due to how we
  290     // compute the version-specific URL. This is the logic which bounces the UI
  291     // to the fallback URL.
  292     if (cat->status() == Delegate::FAIL_NOT_FOUND) {
  293         FGHTTPClient* http = globals->get_subsystem<FGHTTPClient>();
  294         if (cat->url() == http->getDefaultCatalogUrl()) {
  295             cat->setUrl(http->getDefaultCatalogFallbackUrl());
  296             cat->refresh(); // and trigger another refresh
  297             return;
  298         }
  299     }
  300 
  301     if (cat == m_newlyAddedCatalog) {
  302         // defer this signal slightly so that QML calling finalizeAdd or
  303         // abandonAdd in response, doesn't re-enter the package code
  304         QTimer::singleShot(0, this, &CatalogListModel::statusOfAddingCatalogChanged);
  305         return;
  306     }
  307 
  308     auto it = std::find(m_catalogs.begin(), m_catalogs.end(), cat);
  309     if (it == m_catalogs.end())
  310         return;
  311 
  312     int row = std::distance(m_catalogs.begin(), it);
  313     emit dataChanged(index(row), index(row));
  314 }
  315 
  316 CatalogListModel::CatalogStatus CatalogListModel::translateStatusForCatalog(CatalogRef cat) const
  317 {
  318     switch (cat->status()) {
  319     case Delegate::STATUS_SUCCESS:
  320     case Delegate::STATUS_REFRESHED:
  321         return Ok;
  322 
  323     case Delegate::FAIL_DOWNLOAD:       return NetworkError;
  324     case Delegate::STATUS_IN_PROGRESS:  return Refreshing;
  325     case Delegate::FAIL_NOT_FOUND:      return NotFoundOnServer;
  326     case Delegate::FAIL_VERSION:        return IncompatibleVersion;
  327     case Delegate::FAIL_HTTP_FORBIDDEN: return HTTPForbidden;
  328     case Delegate::FAIL_VALIDATION:
  329     case Delegate::FAIL_EXTRACT:
  330         return InvalidData;
  331     default:
  332         return UnknownError;
  333     }
  334 }
  335 
  336 CatalogListModel::CatalogStatus CatalogListModel::statusOfAddingCatalog() const
  337 {
  338     if (!m_newlyAddedCatalog.get()) {
  339         return NoAddInProgress;
  340     }
  341 
  342     return translateStatusForCatalog(m_newlyAddedCatalog);
  343 }