]> git.mxchange.org Git - flightgear.git/commitdiff
Lots of work on aircraft package support
authorJames Turner <zakalawe@mac.com>
Mon, 3 Aug 2015 20:53:56 +0000 (15:53 -0500)
committerJames Turner <zakalawe@mac.com>
Mon, 21 Sep 2015 00:46:35 +0000 (19:46 -0500)
12 files changed:
src/GUI/AddCatalogDialog.cxx
src/GUI/AircraftItemDelegate.cxx
src/GUI/AircraftItemDelegate.hxx
src/GUI/AircraftModel.cxx
src/GUI/AircraftModel.hxx
src/GUI/CatalogListModel.cxx
src/GUI/PathsDialog.cxx
src/GUI/QtLauncher.cxx
src/GUI/QtLauncher.hxx
src/Main/fg_init.cxx
src/Network/HTTPClient.cxx
src/Network/HTTPClient.hxx

index 6d7665a6e61042bdcf0ae3047176f86384f5d91c..abca2c51df6845798ccf7f762439edd0349f995f 100644 (file)
@@ -88,7 +88,7 @@ void AddCatalogDialog::updateUi()
                        "%2 aircraft are included in this hangar.").arg(catDesc).arg(m_result->packages().size());
         ui->resultsSummaryLabel->setText(s);
     } else if (m_state == STATE_DOWNLOAD_FAILED) {
-        Delegate::FailureCode code = m_result->status();
+        Delegate::StatusCode code = m_result->status();
         qWarning() << Q_FUNC_INFO << "failed with code" << code;
         QString s;
         switch (code) {
@@ -98,7 +98,7 @@ void AddCatalogDialog::updateUi()
             break;
 
         case Delegate::FAIL_VERSION:
-            s = tr("The provided hangar is for a different version of FLightGear. "
+            s = tr("The provided hangar is for a different version of FlightGear. "
                    "(This is version %1)").arg(QString::fromUtf8(FLIGHTGEAR_VERSION));
             break;
 
@@ -152,14 +152,14 @@ void AddCatalogDialog::reject()
 
 void AddCatalogDialog::onCatalogStatusChanged(Catalog* cat)
 {
-    Delegate::FailureCode s = cat->status();
+    Delegate::StatusCode s = cat->status();
     qDebug() << Q_FUNC_INFO << "cat status:" << s;
     switch (s) {
-    case Delegate::CATALOG_REFRESHED:
+    case Delegate::STATUS_REFRESHED:
         m_state = STATE_FINISHED;
         break;
 
-    case Delegate::FAIL_IN_PROGRESS:
+    case Delegate::STATUS_IN_PROGRESS:
         // don't jump to STATE_FINISHED
         return;
 
index 51a3701be14e08f1c7381897d938a940f6d4263b..73d1d56023ca437e959686b796123f52bb38c895 100644 (file)
@@ -143,29 +143,79 @@ void AircraftItemDelegate::paint(QPainter * painter, const QStyleOptionViewItem
 
     QVariant v = index.data(AircraftPackageStatusRole);
     AircraftItemStatus status = static_cast<AircraftItemStatus>(v.toInt());
-   // status = PackageNotInstalled;
+    double downloadFraction = 0.0;
+    
     if (status != PackageInstalled) {
+        QString buttonText, infoText;
+        QColor buttonColor(27, 122, 211);
+        
+        double sizeInMBytes = index.data(AircraftPackageSizeRole).toInt();
+        sizeInMBytes /= 0x100000;
+        
+        if (status == PackageDownloading) {
+            buttonText = tr("Cancel");
+            double downloadedMB = index.data(AircraftInstallDownloadedSizeRole).toInt();
+            downloadedMB /= 0x100000;
+            infoText = QStringLiteral("%1 MB of %2 MB").arg(downloadedMB, 0, 'f', 1).arg(sizeInMBytes, 0, 'f', 1);
+            buttonColor = QColor(0xcf, 0xcf, 0xcf);
+            downloadFraction = downloadedMB / sizeInMBytes;
+        } else if (status == PackageQueued) {
+            buttonText = tr("Cancel");
+            infoText = tr("Waiting to download %1 MB").arg(sizeInMBytes, 0, 'f', 1);
+            buttonColor = QColor(0xcf, 0xcf, 0xcf);
+        } else {
+            infoText = QStringLiteral("%1MB").arg(sizeInMBytes, 0, 'f', 1);
+            if (status == PackageNotInstalled) {
+                buttonText = "Install";
+            } else if (status == PackageUpdateAvailable) {
+                buttonText = "Update";
+            }
+        }
+        
         painter->setBrush(Qt::NoBrush);
         QRect buttonRect = packageButtonRect(option.rect, index);
         painter->setPen(Qt::NoPen);
-        painter->setBrush(QColor(27, 122, 211));
+        painter->setBrush(buttonColor);
         painter->drawRoundedRect(buttonRect, 5, 5);
         painter->setPen(Qt::white);
-
-        if (status == PackageNotInstalled) {
-            painter->drawText(buttonRect, Qt::AlignCenter, "Install");
-        } else if (status == PackageUpdateAvailable) {
-            painter->drawText(buttonRect, Qt::AlignCenter, "Update");
+        painter->drawText(buttonRect, Qt::AlignCenter, buttonText);
+        
+        QRect infoTextRect = buttonRect;
+        infoTextRect.setLeft(buttonRect.right() + MARGIN);
+        infoTextRect.setWidth(200);
+        
+        if (status == PackageDownloading) {
+            QRect progressRect = infoTextRect;
+            progressRect.setHeight(6);
+            painter->setPen(QPen(QColor(0xcf, 0xcf, 0xcf), 0));
+            painter->setBrush(Qt::NoBrush);
+            painter->drawRoundedRect(progressRect, 3, 3);
+            infoTextRect.setTop(progressRect.bottom() + 1);
+
+            QRect progressBarRect = progressRect.marginsRemoved(QMargins(2, 2, 2, 2));
+            
+            progressBarRect.setWidth(static_cast<int>(progressBarRect.width() * downloadFraction));
+            
+            painter->setBrush(QColor(27, 122, 211));
+            painter->setPen(Qt::NoPen);
+            painter->drawRoundedRect(progressBarRect, 2, 2);
         }
-    }
+        
+        painter->setPen(Qt::black);
+        painter->drawText(infoTextRect, Qt::AlignLeft | Qt::AlignVCenter, infoText);
+    } // of update / install / download status
 }
 
 QSize AircraftItemDelegate::sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index) const
 {
     QRect contentRect = option.rect.adjusted(MARGIN, MARGIN, -MARGIN, -MARGIN);
-    QPixmap thumbnail = index.data(Qt::DecorationRole).value<QPixmap>();
-    contentRect.setLeft(contentRect.left() + MARGIN + thumbnail.width());
 
+    const int THUMBNAIL_WIDTH = 172;
+    // don't request the thumbnail here for remote sources. Assume the default
+    //QPixmap thumbnail = index.data(Qt::DecorationRole).value<QPixmap>();
+    //contentRect.setLeft(contentRect.left() + MARGIN + thumbnail.width());
+    contentRect.setLeft(contentRect.left() + MARGIN + THUMBNAIL_WIDTH);
+    
     QFont f;
     f.setPointSize(18);
     QFontMetrics metrics(f);
@@ -209,10 +259,10 @@ bool AircraftItemDelegate::eventFilter( QObject*, QEvent* event )
         QModelIndex index = m_view->indexAt( me->pos() );
         int variantCount = index.data(AircraftVariantCountRole).toInt();
         int variantIndex = index.data(AircraftVariantRole).toInt();
+        QRect vr = m_view->visualRect(index);
 
         if ( (event->type() == QEvent::MouseButtonRelease) && (variantCount > 0) )
         {
-            QRect vr = m_view->visualRect(index);
             QRect leftCycleRect = leftCycleArrowRect(vr, index),
                 rightCycleRect = rightCycleArrowRect(vr, index);
 
@@ -226,6 +276,22 @@ bool AircraftItemDelegate::eventFilter( QObject*, QEvent* event )
                 return true;
             }
         }
+        
+        if ((event->type() == QEvent::MouseButtonRelease) &&
+            packageButtonRect(vr, index).contains(me->pos()))
+        {
+            QVariant v = index.data(AircraftPackageStatusRole);
+            AircraftItemStatus status = static_cast<AircraftItemStatus>(v.toInt());
+            if (status == PackageNotInstalled) {
+                emit requestInstall(index);
+            } else if ((status == PackageDownloading) || (status == PackageQueued)) {
+                emit cancelDownload(index);
+            } else if (status == PackageUpdateAvailable) {
+                emit requestInstall(index);
+            }
+            
+            return true;
+        }
     } else if ( event->type() == QEvent::MouseMove ) {
         QMouseEvent* me = static_cast< QMouseEvent* >( event );
         QModelIndex index = m_view->indexAt( me->pos() );
@@ -273,7 +339,8 @@ QRect AircraftItemDelegate::packageButtonRect(const QRect& visualRect, const QMo
     QPixmap thumbnail = index.data(Qt::DecorationRole).value<QPixmap>();
     contentRect.setLeft(contentRect.left() + MARGIN + thumbnail.width());
 
-    return QRect(contentRect.left() + ARROW_SIZE, contentRect.bottom() - 24, 60, BUTTON_HEIGHT);
+    return QRect(contentRect.left() + ARROW_SIZE, contentRect.bottom() - 24,
+                 BUTTON_WIDTH, BUTTON_HEIGHT);
 }
 
 void AircraftItemDelegate::drawRating(QPainter* painter, QString label, const QRect& box, int value) const
index f2a3988721ff34d856eda9908c54437c71535cbb..436e49c352f1c5447fb0a58fe5f5cb9d0232c439 100644 (file)
@@ -32,7 +32,8 @@ public:
     static const int MARGIN = 4;
     static const int ARROW_SIZE = 20;
     static const int BUTTON_HEIGHT = 24;
-
+    static const int BUTTON_WIDTH = 80;
+    
     AircraftItemDelegate(QListView* view);
     
     virtual void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const;
@@ -44,6 +45,9 @@ public:
 Q_SIGNALS:
     void variantChanged(const QModelIndex& index);
 
+    void requestInstall(const QModelIndex& index);
+    
+    void cancelDownload(const QModelIndex& index);
 private:
     QRect leftCycleArrowRect(const QRect& visualRect, const QModelIndex& index) const;
     QRect rightCycleArrowRect(const QRect& visualRect, const QModelIndex& index) const;
index 918a6b04be28fd81e4f94442b696f30f749225d4..85735c05940fcc23c4906738000666f0783e24d8 100644 (file)
@@ -27,6 +27,7 @@
 #include <QDataStream>
 #include <QSettings>
 #include <QDebug>
+#include <QSharedPointer>
 
 // Simgear
 #include <simgear/props/props_io.hxx>
@@ -39,6 +40,8 @@
 // FlightGear
 #include <Main/globals.hxx>
 
+const int STANDARD_THUMBNAIL_HEIGHT = 128;
+
 using namespace simgear::pkg;
 
 AircraftItem::AircraftItem() :
@@ -123,8 +126,8 @@ QPixmap AircraftItem::thumbnail() const
         if (dir.exists("thumbnail.jpg")) {
             m_thumbnail.load(dir.filePath("thumbnail.jpg"));
             // resize to the standard size
-            if (m_thumbnail.height() > 128) {
-                m_thumbnail = m_thumbnail.scaledToHeight(128);
+            if (m_thumbnail.height() > STANDARD_THUMBNAIL_HEIGHT) {
+                m_thumbnail = m_thumbnail.scaledToHeight(STANDARD_THUMBNAIL_HEIGHT);
             }
         }
     }
@@ -150,9 +153,9 @@ public:
     }
 
     /** thread-safe access to items already scanned */
-    QList<AircraftItem*> items()
+    QVector<AircraftItemPtr> items()
     {
-        QList<AircraftItem*> result;
+        QVector<AircraftItemPtr> result;
         QMutexLocker g(&m_lock);
         result.swap(m_items);
         g.unlock();
@@ -196,13 +199,11 @@ private:
             }
 
              for (int i=0; i<count; ++i) {
-                AircraftItem* item = new AircraftItem;
+                AircraftItemPtr item(new AircraftItem);
                 item->fromDataStream(ds);
 
                 QFileInfo finfo(item->path);
-                if (!finfo.exists() || (finfo.lastModified() != item->pathModTime)) {
-                    delete item;
-                } else {
+                if (finfo.exists() && (finfo.lastModified() == item->pathModTime)) {
                     // corresponding -set.xml file still exists and is
                     // unmodified
                     m_cachedItems[item->path] = item;
@@ -220,7 +221,7 @@ private:
             quint32 count = m_nextCache.count();
             ds << CACHE_VERSION << count;
 
-            Q_FOREACH(AircraftItem* item, m_nextCache.values()) {
+            Q_FOREACH(AircraftItemPtr item, m_nextCache.values()) {
                 item->toDataStream(ds);
             }
         }
@@ -237,18 +238,18 @@ private:
         filters << "*-set.xml";
         Q_FOREACH(QFileInfo child, path.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot)) {
             QDir childDir(child.absoluteFilePath());
-            QMap<QString, AircraftItem*> baseAircraft;
-            QList<AircraftItem*> variants;
+            QMap<QString, AircraftItemPtr> baseAircraft;
+            QList<AircraftItemPtr> variants;
 
             Q_FOREACH(QFileInfo xmlChild, childDir.entryInfoList(filters, QDir::Files)) {
                 try {
                     QString absolutePath = xmlChild.absoluteFilePath();
-                    AircraftItem* item = NULL;
+                    AircraftItemPtr item;
 
                     if (m_cachedItems.contains(absolutePath)) {
                         item = m_cachedItems.value(absolutePath);
                     } else {
-                        item = new AircraftItem(childDir, absolutePath);
+                        item = AircraftItemPtr(new AircraftItem(childDir, absolutePath));
                     }
 
                     m_nextCache[absolutePath] = item;
@@ -272,10 +273,9 @@ private:
             } // of set.xml iteration
 
             // bind variants to their principals
-            Q_FOREACH(AircraftItem* item, variants) {
+            Q_FOREACH(AircraftItemPtr item, variants) {
                 if (!baseAircraft.contains(item->variantOf)) {
                     qWarning() << "can't find principal aircraft " << item->variantOf << " for variant:" << item->path;
-                    delete item;
                     continue;
                 }
 
@@ -285,7 +285,7 @@ private:
             // lock mutex while we modify the items array
             {
                 QMutexLocker g(&m_lock);
-                m_items.append(baseAircraft.values());
+                m_items.append(baseAircraft.values().toVector());
             }
 
             emit addedItems();
@@ -294,19 +294,122 @@ private:
 
     QMutex m_lock;
     QStringList m_dirs;
-    QList<AircraftItem*> m_items;
+    QVector<AircraftItemPtr> m_items;
 
-    QMap<QString, AircraftItem* > m_cachedItems;
-    QMap<QString, AircraftItem* > m_nextCache;
+    QMap<QString, AircraftItemPtr > m_cachedItems;
+    QMap<QString, AircraftItemPtr > m_nextCache;
 
     bool m_done;
 };
 
+class PackageDelegate : public simgear::pkg::Delegate
+{
+public:
+    PackageDelegate(AircraftItemModel* model) :
+        m_model(model)
+    {
+        m_model->m_packageRoot->addDelegate(this);
+    }
+    
+    ~PackageDelegate()
+    {
+        m_model->m_packageRoot->removeDelegate(this);
+    }
+    
+protected:
+    virtual void catalogRefreshed(CatalogRef aCatalog, StatusCode aReason)
+    {
+        if (aReason == STATUS_IN_PROGRESS) {
+            qDebug() << "doing refresh of" << QString::fromStdString(aCatalog->url());
+        } else if ((aReason == STATUS_REFRESHED) || (aReason == STATUS_SUCCESS)) {
+            m_model->refreshPackages();
+        } else {
+            qWarning() << "failed refresh of "
+                << QString::fromStdString(aCatalog->url()) << ":" << aReason << endl;
+        }
+    }
+    
+    virtual void startInstall(InstallRef aInstall)
+    {
+        QModelIndex mi(indexForPackage(aInstall->package()));
+        m_model->dataChanged(mi, mi);
+    }
+    
+    virtual void installProgress(InstallRef aInstall, unsigned int bytes, unsigned int total)
+    {
+        Q_UNUSED(bytes);
+        Q_UNUSED(total);
+        QModelIndex mi(indexForPackage(aInstall->package()));
+        m_model->dataChanged(mi, mi);
+    }
+    
+    virtual void finishInstall(InstallRef aInstall, StatusCode aReason)
+    {
+        QModelIndex mi(indexForPackage(aInstall->package()));
+        m_model->dataChanged(mi, mi);
+        
+        if ((aReason != USER_CANCELLED) && (aReason != STATUS_SUCCESS)) {
+            m_model->installFailed(mi, aReason);
+        }
+        
+        if (aReason == STATUS_SUCCESS) {
+            m_model->installSucceeded(mi);
+        }
+    }
+    
+    virtual void dataForThumbnail(const std::string& aThumbnailUrl,
+                                  size_t length, const uint8_t* bytes)
+    {
+        QImage img = QImage::fromData(QByteArray::fromRawData(reinterpret_cast<const char*>(bytes), length));
+        if (img.isNull()) {
+            qWarning() << "failed to load image data for URL:" <<
+                QString::fromStdString(aThumbnailUrl);
+            return;
+        }
+        
+        m_model->m_thumbnailPixmapCache.insert(QString::fromStdString(aThumbnailUrl),
+                                               QPixmap::fromImage(img));
+        
+        // notify any affected items. Linear scan here avoids another map/dict
+        // structure.
+        PackageList::const_iterator it;
+        int i = 0;
+        
+        for (it=m_model->m_packages.begin(); it != m_model->m_packages.end(); ++it, ++i) {
+            const string_list& urls((*it)->thumbnailUrls());
+            string_list::const_iterator cit = std::find(urls.begin(), urls.end(), aThumbnailUrl);
+            if (cit != urls.end()) {
+                QModelIndex mi(m_model->index(i + m_model->m_items.size()));
+                m_model->dataChanged(mi, mi);
+            }
+        } // of packages iteration
+    }
+    
+private:
+    QModelIndex indexForPackage(const PackageRef& ref) const
+    {
+        PackageList::const_iterator it = std::find(m_model->m_packages.begin(),
+                                                   m_model->m_packages.end(),
+                                                   ref);
+        if (it == m_model->m_packages.end()) {
+            return QModelIndex();
+        }
+        
+        size_t offset = it - m_model->m_packages.begin();
+        return m_model->index(offset + m_model->m_items.size());
+    }
+    
+    AircraftItemModel* m_model;
+};
+
 AircraftItemModel::AircraftItemModel(QObject* pr, simgear::pkg::RootRef& rootRef) :
     QAbstractListModel(pr),
     m_scanThread(NULL),
     m_packageRoot(rootRef)
 {
+    new PackageDelegate(this);
+    // packages may already be refreshed, so pull now
+    refreshPackages();
 }
 
 AircraftItemModel::~AircraftItemModel()
@@ -324,7 +427,6 @@ void AircraftItemModel::scanDirs()
     abandonCurrentScan();
 
     beginResetModel();
-    qDeleteAll(m_items);
     m_items.clear();
     m_activeVariant.clear();
     endResetModel();
@@ -345,7 +447,6 @@ void AircraftItemModel::scanDirs()
     connect(m_scanThread, &AircraftScanThread::addedItems,
             this, &AircraftItemModel::onScanResults);
     m_scanThread->start();
-
 }
 
 void AircraftItemModel::abandonCurrentScan()
@@ -358,18 +459,51 @@ void AircraftItemModel::abandonCurrentScan()
     }
 }
 
+void AircraftItemModel::refreshPackages()
+{
+    beginResetModel();
+    m_packages = m_packageRoot->allPackages();
+    m_packageVariant.resize(m_packages.size());
+    endResetModel();
+}
+
+int AircraftItemModel::rowCount(const QModelIndex& parent) const
+{
+    return m_items.size() + m_packages.size();
+}
+
 QVariant AircraftItemModel::data(const QModelIndex& index, int role) const
 {
-    if (role == AircraftVariantRole) {
-        return m_activeVariant.at(index.row());
-    }
+    if (index.row() >= m_items.size()) {
+        quint32 packageIndex = index.row() - m_items.size();
+
+        if (role == AircraftVariantRole) {
+            return m_packageVariant.at(packageIndex);
+        }
+        
+        const PackageRef& pkg(m_packages[packageIndex]);
+        InstallRef ex = pkg->existingInstall();
+        
+        if (role == AircraftInstallPercentRole) {
+            return ex.valid() ? ex->downloadedPercent() : 0;
+        } else if (role == AircraftInstallDownloadedSizeRole) {
+            return static_cast<quint64>(ex.valid() ? ex->downloadedBytes() : 0);
+        }
+        
+        quint32 variantIndex = m_packageVariant.at(packageIndex);
+        return dataFromPackage(pkg, variantIndex, role);
+    } else {
+        if (role == AircraftVariantRole) {
+            return m_activeVariant.at(index.row());
+        }
 
-    const AircraftItem* item(m_items.at(index.row()));
-    quint32 variantIndex = m_activeVariant.at(index.row());
-    return dataFromItem(item, variantIndex, role);
+        quint32 variantIndex = m_activeVariant.at(index.row());
+        const AircraftItemPtr item(m_items.at(index.row()));
+        return dataFromItem(item, variantIndex, role);
+    }
 }
 
-QVariant AircraftItemModel::dataFromItem(const AircraftItem* item, quint32 variantIndex, int role) const
+QVariant AircraftItemModel::dataFromItem(AircraftItemPtr item, quint32 variantIndex, int role) const
 {
     if (role == AircraftVariantCountRole) {
         return item->variants.count();
@@ -410,6 +544,8 @@ QVariant AircraftItemModel::dataFromItem(const AircraftItem* item, quint32 varia
         return PackageInstalled; // always the case
     } else if (role == Qt::ToolTipRole) {
         return item->path;
+    } else if (role == AircraftURIRole) {
+        return QUrl::fromLocalFile(item->path);
     } else if (role == AircraftHasRatingsRole) {
         bool have = false;
         for (int i=0; i<4; ++i) {
@@ -430,19 +566,28 @@ QVariant AircraftItemModel::dataFromItem(const AircraftItem* item, quint32 varia
 
 QVariant AircraftItemModel::dataFromPackage(const PackageRef& item, quint32 variantIndex, int role) const
 {
+    if (role == Qt::DecorationRole) {
+        role = AircraftThumbnailRole; // use first thumbnail
+    }
+    
     if (role == Qt::DisplayRole) {
         return QString::fromStdString(item->name());
     } else if (role == AircraftPathRole) {
-        // can we return the theoretical path?
+        InstallRef i = item->existingInstall();
+        if (i.valid()) {
+            return QString::fromStdString(i->primarySetPath().str());
+        }
     } else if (role == AircraftPackageIdRole) {
         return QString::fromStdString(item->id());
     } else if (role == AircraftPackageStatusRole) {
-        bool installed = item->isInstalled();
-        if (installed) {
-            InstallRef i = item->existingInstall();
+        InstallRef i = item->existingInstall();
+        if (i.valid()) {
             if (i->isDownloading()) {
                 return PackageDownloading;
             }
+            if (i->isQueued()) {
+                return PackageQueued;
+            }
             if (i->hasUpdate()) {
                 return PackageUpdateAvailable;
             }
@@ -451,13 +596,72 @@ QVariant AircraftItemModel::dataFromPackage(const PackageRef& item, quint32 vari
         } else {
             return PackageNotInstalled;
         }
+    } else if (role >= AircraftThumbnailRole) {
+        return packageThumbnail(item , role - AircraftThumbnailRole);
+    } else if (role == AircraftAuthorsRole) {
+        SGPropertyNode* authors = item->properties()->getChild("author");
+        if (authors) {
+            return QString::fromStdString(authors->getStringValue());
+        }
     } else if (role == AircraftLongDescriptionRole) {
         return QString::fromStdString(item->description());
+    } else if (role == AircraftPackageSizeRole) {
+        return static_cast<int>(item->fileSizeBytes());
+    } else if (role == AircraftURIRole) {
+        return QUrl("package:" + QString::fromStdString(item->qualifiedId()));
+    } else if (role == AircraftHasRatingsRole) {
+        return item->properties()->hasChild("rating");
+    } else if ((role >= AircraftRatingRole) && (role < AircraftVariantDescriptionRole)) {
+        int ratingIndex = role - AircraftRatingRole;
+        SGPropertyNode* ratings = item->properties()->getChild("rating");
+        if (!ratings) {
+            return QVariant();
+        }
+        return ratings->getChild(ratingIndex)->getIntValue();
     }
 
     return QVariant();
 }
 
+QVariant AircraftItemModel::packageThumbnail(PackageRef p, int index) const
+{
+    const string_list& thumbnails(p->thumbnailUrls());
+    if (index >= thumbnails.size()) {
+        return QVariant();
+    }
+    
+    std::string thumbnailUrl = thumbnails.at(index);
+    QString urlQString(QString::fromStdString(thumbnailUrl));
+    if (m_thumbnailPixmapCache.contains(urlQString)) {
+        // cache hit, easy
+        return m_thumbnailPixmapCache.value(urlQString);
+    }
+// check the on-disk store. This relies on the order of thumbnails in the
+// results of thumbnailUrls and thumbnails corresponding
+    InstallRef ex = p->existingInstall();
+    if (ex.valid()) {
+        const string_list& thumbNames(p->thumbnails());
+        if (!thumbNames.empty()) {
+            SGPath path(ex->path());
+            path.append(p->thumbnails()[index]);
+            if (path.exists()) {
+                QPixmap pix;
+                pix.load(QString::fromStdString(path.str()));
+                // resize to the standard size
+                if (pix.height() > STANDARD_THUMBNAIL_HEIGHT) {
+                    pix = pix.scaledToHeight(STANDARD_THUMBNAIL_HEIGHT);
+                }
+                m_thumbnailPixmapCache[urlQString] = pix;
+                return pix;
+            }
+        } // of have thumbnail file names
+    } // of have existing install
+    
+    m_packageRoot->requestThumbnailData(thumbnailUrl);
+    return QVariant();
+}
+
 bool AircraftItemModel::setData(const QModelIndex &index, const QVariant &value, int role)
   {
       if (role == AircraftVariantRole) {
@@ -469,21 +673,36 @@ bool AircraftItemModel::setData(const QModelIndex &index, const QVariant &value,
       return false;
   }
 
-QModelIndex AircraftItemModel::indexOfAircraftPath(QString path) const
+QModelIndex AircraftItemModel::indexOfAircraftURI(QUrl uri) const
 {
-    for (int row=0; row <m_items.size(); ++row) {
-        const AircraftItem* item(m_items.at(row));
-        if (item->path == path) {
-            return index(row);
+    if (uri.isLocalFile()) {
+        QString path = uri.toLocalFile();
+        for (int row=0; row <m_items.size(); ++row) {
+            const AircraftItemPtr item(m_items.at(row));
+            if (item->path == path) {
+                return index(row);
+            }
+        }
+    } else if (uri.scheme() == "package") {
+        QString ident = uri.path();
+        PackageRef pkg = m_packageRoot->getPackageById(ident.toStdString());
+        if (pkg) {
+            for (int i=0; i < m_packages.size(); ++i) {
+                if (m_packages[i] == pkg) {
+                    return index(m_items.size() + i);
+                }
+            } // of linear package scan
         }
+    } else {
+        qWarning() << "Unknown aircraft URI scheme" << uri << uri.scheme();
     }
-
+    
     return QModelIndex();
 }
 
 void AircraftItemModel::onScanResults()
 {
-    QList<AircraftItem*> newItems = m_scanThread->items();
+    QVector<AircraftItemPtr> newItems = m_scanThread->items();
     if (newItems.isEmpty())
         return;
 
@@ -505,4 +724,51 @@ void AircraftItemModel::onScanFinished()
     m_scanThread = NULL;
 }
 
+void AircraftItemModel::installFailed(QModelIndex index, simgear::pkg::Delegate::StatusCode reason)
+{
+    Q_ASSERT(index.row() >= m_items.size());
+    
+    QString msg;
+    switch (reason) {
+        case Delegate::FAIL_CHECKSUM:
+            msg = tr("Invalid package checksum"); break;
+        case Delegate::FAIL_DOWNLOAD:
+            msg = tr("Download failed"); break;
+        case Delegate::FAIL_EXTRACT:
+            msg = tr("Package could not be extracted"); break;
+        case Delegate::FAIL_FILESYSTEM:
+            msg = tr("A local file-system error occurred"); break;
+        case Delegate::FAIL_UNKNOWN:
+        default:
+            msg = tr("Unknown reason");
+    }
+    
+    quint32 packageIndex = index.row() - m_items.size();
+    const PackageRef& pkg(m_packages[packageIndex]);
+    QString packageName = QString::fromStdString(pkg->description());
+    emit aircraftInstallFailed(index, tr("Failed installation of package '%1': %2").arg(packageName).arg(msg));
+}
+
+void AircraftItemModel::installSucceeded(QModelIndex index)
+{
+    qDebug() << Q_FUNC_INFO << index;
+    emit aircraftInstallCompleted(index);
+}
+
+bool AircraftItemModel::isIndexRunnable(const QModelIndex& index) const
+{
+    if (index.row() < m_items.size()) {
+        return true; // local file, always runnable
+    }
+    
+    quint32 packageIndex = index.row() - m_items.size();
+    const PackageRef& pkg(m_packages[packageIndex]);
+    InstallRef ex = pkg->existingInstall();
+    if (!ex.valid()) {
+        return false; // not installed
+    }
+    
+    return !ex->isDownloading();
+}
+
 #include "AircraftModel.moc"
index c1bb77cf07b7147186521806f894d0b164b5d14c..bb85158c80b55148d271889343b94277fd762b64 100644 (file)
@@ -26,6 +26,8 @@
 #include <QDir>
 #include <QPixmap>
 #include <QStringList>
+#include <QSharedPointer>
+#include <QUrl>
 
 #include <simgear/package/Root.hxx>
 
@@ -39,6 +41,10 @@ const int AircraftPackageStatusRole = Qt::UserRole + 7;
 const int AircraftPackageProgressRole = Qt::UserRole + 8;
 const int AircraftLongDescriptionRole = Qt::UserRole + 9;
 const int AircraftHasRatingsRole = Qt::UserRole + 10;
+const int AircraftInstallPercentRole = Qt::UserRole + 11;
+const int AircraftPackageSizeRole = Qt::UserRole + 12;
+const int AircraftInstallDownloadedSizeRole = Qt::UserRole + 13;
+const int AircraftURIRole = Qt::UserRole + 14;
 
 const int AircraftRatingRole = Qt::UserRole + 100;
 const int AircraftVariantDescriptionRole = Qt::UserRole + 200;
@@ -47,6 +53,9 @@ const int AircraftThumbnailRole = Qt::UserRole + 300;
 class AircraftScanThread;
 class QDataStream;
 
+struct AircraftItem;
+typedef QSharedPointer<AircraftItem> AircraftItemPtr;
+
 struct AircraftItem
 {
     AircraftItem();
@@ -70,7 +79,7 @@ struct AircraftItem
     QString variantOf;
     QDateTime pathModTime;
 
-    QList<AircraftItem*> variants;
+    QList<AircraftItemPtr> variants;
 private:
     mutable QPixmap m_thumbnail;
 };
@@ -80,6 +89,7 @@ enum AircraftItemStatus {
     PackageNotInstalled,
     PackageInstalled,
     PackageUpdateAvailable,
+    PackageQueued,
     PackageDownloading
 };
 
@@ -95,11 +105,8 @@ public:
 
     void scanDirs();
 
-    virtual int rowCount(const QModelIndex& parent) const
-    {
-        return m_items.size();
-    }
-
+    virtual int rowCount(const QModelIndex& parent) const;
+    
     virtual QVariant data(const QModelIndex& index, int role) const;
     
     virtual bool setData(const QModelIndex &index, const QVariant &value, int role);
@@ -108,26 +115,53 @@ public:
      * given a -set.xml path, return the corresponding model index, if one
      * exists.
      */
-    QModelIndex indexOfAircraftPath(QString path) const;
+  //  QModelIndex indexOfAircraftPath(QString path) const;
 
+    QModelIndex indexOfAircraftURI(QUrl uri) const;
+    
+    /**
+     * return if a given aircraft is ready to be run, or not. Aircraft which
+     * are not installed, or are downloading, are not runnable.
+     */
+    bool isIndexRunnable(const QModelIndex& index) const;
+    
+signals:
+    void aircraftInstallFailed(QModelIndex index, QString errorMessage);
+    
+    void aircraftInstallCompleted(QModelIndex index);
+    
 private slots:
     void onScanResults();
     
     void onScanFinished();
 
 private:
-    QVariant dataFromItem(const AircraftItem* item, quint32 variantIndex, int role) const;
+    friend class PackageDelegate;
+    
+    QVariant dataFromItem(AircraftItemPtr item, quint32 variantIndex, int role) const;
 
     QVariant dataFromPackage(const simgear::pkg::PackageRef& item,
                              quint32 variantIndex, int role) const;
 
+    QVariant packageThumbnail(simgear::pkg::PackageRef p, int index) const;
+    
     void abandonCurrentScan();
-
+    void refreshPackages();
+    
+    void installSucceeded(QModelIndex index);
+    void installFailed(QModelIndex index, simgear::pkg::Delegate::StatusCode reason);
+    
     QStringList m_paths;
     AircraftScanThread* m_scanThread;
-    QList<AircraftItem*> m_items;
-    QList<quint32> m_activeVariant;
+    QVector<AircraftItemPtr> m_items;
+    
+    QVector<quint32> m_activeVariant;
+    QVector<quint32> m_packageVariant;
+    
     simgear::pkg::RootRef m_packageRoot;
+    simgear::pkg::PackageList m_packages;
+        
+    mutable QHash<QString, QPixmap> m_thumbnailPixmapCache;
 };
 
 #endif // of FG_GUI_AIRCRAFT_MODEL
index a6533efb96df2cf0701329511aaa16a3bea72030..9fd830293d64b946e4fa55421792906a58f6df3f 100644 (file)
@@ -59,7 +59,7 @@ QVariant CatalogListModel::data(const QModelIndex& index, int role) const
     simgear::pkg::CatalogRef cat = m_packageRoot->catalogs().at(index.row());
 
     if (role == Qt::DisplayRole) {
-        return QString::fromStdString(cat->description());
+        return QString::fromStdString(cat->description()).trimmed();
     } else if (role == Qt::ToolTipRole) {
         return QString::fromStdString(cat->url());
     } else if (role == CatalogUrlRole) {
index 725d38d900d7f6c24cf5d3a0d29f919f377d4e2d..3abc281a88093fd10f3cfdbd0af79803558c8068 100644 (file)
@@ -3,6 +3,7 @@
 
 #include <QSettings>
 #include <QFileDialog>
+#include <QMessageBox>
 
 #include "CatalogListModel.hxx"
 #include "AddCatalogDialog.hxx"
@@ -128,7 +129,17 @@ void PathsDialog::onAddCatalog()
 
 void PathsDialog::onRemoveCatalog()
 {
-    
+    QModelIndex mi = m_ui->catalogsList->currentIndex();
+    if (mi.isValid()) {
+        QMessageBox mb;
+        mb.setText(QStringLiteral("Remove aircraft hangar '%1'?").arg(mi.data(Qt::DisplayRole).toString()));
+        mb.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
+        mb.setDefaultButton(QMessageBox::No);
+        mb.exec();
+        
+        QString pkgId = mi.data(CatalogIdRole).toString();
+        m_packageRoot->removeCatalogById(pkgId.toStdString());
+    }
 }
 
 void PathsDialog::onChangeDownloadDir()
index b67ea3639e3584570e3bd6008171df2f9e7d2b20..adcac26373097590e6dfdaa77b5c76f56cb457b3 100644 (file)
@@ -48,6 +48,9 @@
 #include <simgear/structure/exception.hxx>
 #include <simgear/structure/subsystem_mgr.hxx>
 #include <simgear/misc/sg_path.hxx>
+#include <simgear/package/Catalog.hxx>
+#include <simgear/package/Package.hxx>
+#include <simgear/package/Install.hxx>
 
 #include "ui_Launcher.h"
 #include "EditRatingsFilterDialog.hxx"
@@ -65,6 +68,7 @@
 #include <Network/HTTPClient.hxx>
 
 using namespace flightgear;
+using namespace simgear::pkg;
 
 const int MAX_RECENT_AIRPORTS = 32;
 const int MAX_RECENT_AIRCRAFT = 20;
@@ -457,8 +461,6 @@ QtLauncher::QtLauncher() :
     connect(m_ui->aircraftHistory, &QPushButton::clicked,
           this, &QtLauncher::onPopupAircraftHistory);
 
-    restoreSettings();
-
     QAction* qa = new QAction(this);
     qa->setShortcut(QKeySequence("Ctrl+Q"));
     connect(qa, &QAction::triggered, this, &QtLauncher::onQuit);
@@ -495,7 +497,7 @@ QtLauncher::QtLauncher() :
     updateSettingsSummary();
 
     fgInitPackageRoot();
-    simgear::pkg::RootRef r(globals->packageRoot());
+    RootRef r(globals->packageRoot());
 
     FGHTTPClient* http = new FGHTTPClient;
     globals->add_subsystem("http", http);
@@ -521,10 +523,21 @@ QtLauncher::QtLauncher() :
             this, &QtLauncher::onAircraftSelected);
     connect(delegate, &AircraftItemDelegate::variantChanged,
             this, &QtLauncher::onAircraftSelected);
-
+    connect(delegate, &AircraftItemDelegate::requestInstall,
+            this, &QtLauncher::onRequestPackageInstall);
+    connect(delegate, &AircraftItemDelegate::cancelDownload,
+            this, &QtLauncher::onCancelDownload);
+
+    connect(m_aircraftModel, &AircraftItemModel::aircraftInstallCompleted,
+            this, &QtLauncher::onAircraftInstalledCompleted);
+    connect(m_aircraftModel, &AircraftItemModel::aircraftInstallFailed,
+            this, &QtLauncher::onAircraftInstallFailed);
+    
     connect(m_ui->pathsButton, &QPushButton::clicked,
             this, &QtLauncher::onEditPaths);
 
+    restoreSettings();
+
     QSettings settings;
     m_aircraftModel->setPaths(settings.value("aircraft-paths").toStringList());
     m_aircraftModel->scanDirs();
@@ -566,6 +579,7 @@ void QtLauncher::initApp(int& argc, char** argv)
 
 bool QtLauncher::runLauncherDialog()
 {
+    sglog().setLogLevels( SG_ALL, SG_INFO );
     Q_INIT_RESOURCE(resources);
 
     // startup the nav-cache now. This pre-empts normal startup of
@@ -598,7 +612,7 @@ void QtLauncher::restoreSettings()
     m_ui->seasonCombo->setCurrentIndex(settings.value("season", 0).toInt());
 
     // full paths to -set.xml files
-    m_recentAircraft = settings.value("recent-aircraft").toStringList();
+    m_recentAircraft = QUrl::fromStringList(settings.value("recent-aircraft").toStringList());
 
     if (!m_recentAircraft.empty()) {
         m_selectedAircraft = m_recentAircraft.front();
@@ -638,7 +652,7 @@ void QtLauncher::saveSettings()
     settings.setValue("enable-realwx", m_ui->fetchRealWxrCheckbox->isChecked());
     settings.setValue("start-paused", m_ui->startPausedCheck->isChecked());
     settings.setValue("ratings-filter", m_ui->ratingsFilterCheck->isChecked());
-    settings.setValue("recent-aircraft", m_recentAircraft);
+    settings.setValue("recent-aircraft", QUrl::toStringList(m_recentAircraft));
     settings.setValue("recent-airports", m_recentAirports);
     settings.setValue("timeofday", m_ui->timeOfDayCombo->currentIndex());
     settings.setValue("season", m_ui->seasonCombo->currentIndex());
@@ -679,13 +693,22 @@ void QtLauncher::onRun()
 
     // aircraft
     if (!m_selectedAircraft.isEmpty()) {
-        QFileInfo setFileInfo(m_selectedAircraft);
-        opt->addOption("aircraft-dir", setFileInfo.dir().absolutePath().toStdString());
-        QString setFile = setFileInfo.fileName();
-        Q_ASSERT(setFile.endsWith("-set.xml"));
-        setFile.truncate(setFile.count() - 8); // drop the '-set.xml' portion
-        opt->addOption("aircraft", setFile.toStdString());
-
+        if (m_selectedAircraft.isLocalFile()) {
+            QFileInfo setFileInfo(m_selectedAircraft.toLocalFile());
+            opt->addOption("aircraft-dir", setFileInfo.dir().absolutePath().toStdString());
+            QString setFile = setFileInfo.fileName();
+            Q_ASSERT(setFile.endsWith("-set.xml"));
+            setFile.truncate(setFile.count() - 8); // drop the '-set.xml' portion
+            opt->addOption("aircraft", setFile.toStdString());
+        } else if (m_selectedAircraft.scheme() == "package") {
+            PackageRef pkg = packageForAircraftURI(m_selectedAircraft);
+            // no need to set aircraft-dir, handled by the corresponding code
+            // in fgInitAircraft
+            opt->addOption("aircraft", pkg->qualifiedId());
+        } else {
+            qWarning() << "unsupported aircraft launch URL" << m_selectedAircraft;
+        }
+        
       // manage aircraft history
         if (m_recentAircraft.contains(m_selectedAircraft))
           m_recentAircraft.removeOne(m_selectedAircraft);
@@ -902,6 +925,29 @@ void QtLauncher::onToggleTerrasync(bool enabled)
     } // of is enabled
 }
 
+void QtLauncher::onAircraftInstalledCompleted(QModelIndex index)
+{
+    qDebug() << Q_FUNC_INFO;
+    QUrl u = index.data(AircraftURIRole).toUrl();
+    if (u == m_selectedAircraft) {
+        // potentially enable the run button now!
+        updateSelectedAircraft();
+        qDebug() << "updating selected aircraft" << index.data();
+    }
+}
+
+void QtLauncher::onAircraftInstallFailed(QModelIndex index, QString errorMessage)
+{
+    qWarning() << Q_FUNC_INFO << index.data(AircraftURIRole) << errorMessage;
+    
+    QMessageBox msg;
+    msg.setWindowTitle(tr("Aircraft insallation failed"));
+    msg.setText(tr("An error occurred installing the aircraft %1: %2").
+                arg(index.data(Qt::DisplayRole).toString()).arg(errorMessage));
+    msg.addButton(QMessageBox::Ok);
+    msg.exec();
+}
+
 void QtLauncher::updateAirportDescription()
 {
     if (!m_selectedAirport) {
@@ -944,20 +990,42 @@ void QtLauncher::onAirportChoiceSelected(const QModelIndex& index)
 
 void QtLauncher::onAircraftSelected(const QModelIndex& index)
 {
-    m_selectedAircraft = index.data(AircraftPathRole).toString();
+    m_selectedAircraft = index.data(AircraftURIRole).toUrl();
     updateSelectedAircraft();
 }
 
+void QtLauncher::onRequestPackageInstall(const QModelIndex& index)
+{
+    QString pkg = index.data(AircraftPackageIdRole).toString();
+    qDebug() << "request install of" << pkg;
+    simgear::pkg::PackageRef pref = globals->packageRoot()->getPackageById(pkg.toStdString());
+    pref->install();
+}
+
+void QtLauncher::onCancelDownload(const QModelIndex& index)
+{
+    QString pkg = index.data(AircraftPackageIdRole).toString();
+    qDebug() << "cancel download of" << pkg;
+    simgear::pkg::PackageRef pref = globals->packageRoot()->getPackageById(pkg.toStdString());
+    simgear::pkg::InstallRef i = pref->existingInstall();
+    i->cancelDownload();
+}
+
 void QtLauncher::updateSelectedAircraft()
 {
-    try {
-        QFileInfo info(m_selectedAircraft);
-        AircraftItem item(info.dir(), m_selectedAircraft);
-        m_ui->thumbnail->setPixmap(item.thumbnail());
-        m_ui->aircraftDescription->setText(item.description);
-    } catch (sg_exception& e) {
+    QModelIndex index = m_aircraftModel->indexOfAircraftURI(m_selectedAircraft);
+    if (index.isValid()) {
+        QPixmap pm = index.data(Qt::DecorationRole).value<QPixmap>();
+        m_ui->thumbnail->setPixmap(pm);
+        m_ui->aircraftDescription->setText(index.data(Qt::DisplayRole).toString());
+        
+        int status = index.data(AircraftPackageStatusRole).toInt();
+        bool canRun = (status == PackageInstalled);
+        m_ui->runButton->setEnabled(canRun);
+    } else {
         m_ui->thumbnail->setPixmap(QPixmap());
         m_ui->aircraftDescription->setText("");
+        m_ui->runButton->setEnabled(false);
     }
 }
 
@@ -985,16 +1053,16 @@ void QtLauncher::onPopupAirportHistory()
     }
 }
 
-QModelIndex QtLauncher::proxyIndexForAircraftPath(QString path) const
+QModelIndex QtLauncher::proxyIndexForAircraftURI(QUrl uri) const
 {
-  return m_aircraftProxy->mapFromSource(sourceIndexForAircraftPath(path));
+  return m_aircraftProxy->mapFromSource(sourceIndexForAircraftURI(uri));
 }
 
-QModelIndex QtLauncher::sourceIndexForAircraftPath(QString path) const
+QModelIndex QtLauncher::sourceIndexForAircraftURI(QUrl uri) const
 {
     AircraftItemModel* sourceModel = qobject_cast<AircraftItemModel*>(m_aircraftProxy->sourceModel());
     Q_ASSERT(sourceModel);
-    return sourceModel->indexOfAircraftPath(path);
+    return sourceModel->indexOfAircraftURI(uri);
 }
 
 void QtLauncher::onPopupAircraftHistory()
@@ -1004,21 +1072,21 @@ void QtLauncher::onPopupAircraftHistory()
     }
 
     QMenu m;
-    Q_FOREACH(QString path, m_recentAircraft) {
-        QModelIndex index = sourceIndexForAircraftPath(path);
+    Q_FOREACH(QUrl uri, m_recentAircraft) {
+        QModelIndex index = sourceIndexForAircraftURI(uri);
         if (!index.isValid()) {
             // not scanned yet
             continue;
         }
         QAction* act = m.addAction(index.data(Qt::DisplayRole).toString());
-        act->setData(path);
+        act->setData(uri);
     }
 
     QPoint popupPos = m_ui->aircraftHistory->mapToGlobal(m_ui->aircraftHistory->rect().bottomLeft());
     QAction* triggered = m.exec(popupPos);
     if (triggered) {
-        m_selectedAircraft = triggered->data().toString();
-        QModelIndex index = proxyIndexForAircraftPath(m_selectedAircraft);
+        m_selectedAircraft = triggered->data().toUrl();
+        QModelIndex index = proxyIndexForAircraftURI(m_selectedAircraft);
         m_ui->aircraftList->selectionModel()->setCurrentIndex(index,
                                                               QItemSelectionModel::ClearAndSelect);
         m_ui->aircraftFilter->clear();
@@ -1129,5 +1197,17 @@ void QtLauncher::onEditPaths()
     }
 }
 
+simgear::pkg::PackageRef QtLauncher::packageForAircraftURI(QUrl uri) const
+{
+    if (uri.scheme() != "package") {
+        qWarning() << "invalid URL scheme:" << uri;
+        return simgear::pkg::PackageRef();
+    }
+    
+    QString ident = uri.path();
+    qDebug() << Q_FUNC_INFO << uri << ident;
+    return globals->packageRoot()->getPackageById(ident.toStdString());
+}
+
 #include "QtLauncher.moc"
 
index 3e582813d213d5097748f7612c04280cdba915c1..3dad6b4bb19aa3265dc33795cb71fa6dc81d90f7 100644 (file)
 #include <QStringList>
 #include <QModelIndex>
 #include <QTimer>
+#include <QUrl>
+
 
 #include <Airports/airport.hxx>
+#include <simgear/package/Package.hxx>
+#include <simgear/package/Catalog.hxx>
 
 namespace Ui
 {
@@ -62,7 +66,9 @@ private slots:
 
     void onAirportChoiceSelected(const QModelIndex& index);
     void onAircraftSelected(const QModelIndex& index);
-
+    void onRequestPackageInstall(const QModelIndex& index);
+    void onCancelDownload(const QModelIndex& index);
+    
     void onPopupAirportHistory();
     void onPopupAircraftHistory();
 
@@ -81,6 +87,9 @@ private slots:
     void onEditPaths();
     
     void onAirportDiagramClicked(FGRunwayRef rwy);
+
+    void onAircraftInstalledCompleted(QModelIndex index);
+    void onAircraftInstallFailed(QModelIndex index, QString errorMessage);
 private:
     void setAirport(FGAirportRef ref);
     void updateSelectedAircraft();
@@ -88,20 +97,22 @@ private:
     void restoreSettings();
     void saveSettings();
     
-    QModelIndex proxyIndexForAircraftPath(QString path) const;
-    QModelIndex sourceIndexForAircraftPath(QString path) const;
+    QModelIndex proxyIndexForAircraftURI(QUrl uri) const;
+    QModelIndex sourceIndexForAircraftURI(QUrl uri) const;
 
     void setEnableDisableOptionFromCheckbox(QCheckBox* cbox, QString name) const;
 
+    simgear::pkg::PackageRef packageForAircraftURI(QUrl uri) const;
+    
     QScopedPointer<Ui::Launcher> m_ui;
     AirportSearchModel* m_airportsModel;
     AircraftProxyModel* m_aircraftProxy;
     AircraftItemModel* m_aircraftModel;
     FGAirportRef m_selectedAirport;
 
-    QString m_selectedAircraft;
-    QStringList m_recentAircraft,
-        m_recentAirports;
+    QUrl m_selectedAircraft;
+    QList<QUrl> m_recentAircraft;
+    QStringList m_recentAirports;
     QTimer* m_subsystemIdleTimer;
 
     int m_ratingFilters[4];
index 30ffa0e67a580fbb5464f38354f970941923baff..3f5682f03abdd0bfd5dd820f54398aadcaf928a5 100644 (file)
@@ -546,11 +546,9 @@ int fgInitAircraft(bool reinit)
             // code in FindAndCacheAircraft works as normal
             // note since we may be using a variant, we can't use the package ID
             size_t lastDot = aircraftId.rfind('.');
-            if (lastDot != std::string::npos) {
-                aircraftId = aircraftId.substr(lastDot + 1);
-                aircraftProp->setStringValue(aircraftId);
-
-            }
+            assert(lastDot != std::string::npos);
+            aircraftId = aircraftId.substr(lastDot + 1);
+            aircraftProp->setStringValue(aircraftId);
             // run the traditional-code path below
         } else {
 #if 0
index f60817877fa7018995dccb722039d5d79059ffda..19c963797c8b2a56227e31ed7a0c061d4369f5d7 100644 (file)
@@ -47,9 +47,8 @@ typedef nasal::Ghost<pkg::PackageRef> NasalPackage;
 typedef nasal::Ghost<pkg::CatalogRef> NasalCatalog;
 typedef nasal::Ghost<pkg::InstallRef> NasalInstall;
 
-namespace {
-  
-  class FGDelegate : public pkg::Delegate
+
+class FGHTTPClient::FGDelegate : public pkg::Delegate
 {
 public:
   virtual void refreshComplete()
@@ -67,48 +66,52 @@ public:
       r->scheduleToUpdate((*it)->install());
     }
   }
-
-  virtual void failedRefresh(pkg::Catalog* aCat, FailureCode aReason)
+    
+  virtual void catalogRefreshed(pkg::CatalogRef aCat, StatusCode aReason)
   {
+      if (aCat.ptr() == NULL) {
+          SG_LOG(SG_IO, SG_INFO, "refresh of all catalogs done");
+          return;
+      }
+      
     switch (aReason) {
-    case pkg::Delegate::FAIL_SUCCESS:
-        SG_LOG(SG_IO, SG_WARN, "refresh of Catalog done");
+    case pkg::Delegate::STATUS_SUCCESS:
+    case pkg::Delegate::STATUS_REFRESHED:
+        SG_LOG(SG_IO, SG_INFO, "refresh of Catalog done:" << aCat->url());
         break;
-        
+            
+    case pkg::Delegate::STATUS_IN_PROGRESS:
+        SG_LOG(SG_IO, SG_INFO, "refresh of Catalog started:" << aCat->url());
+        break;
+            
     default:
         SG_LOG(SG_IO, SG_WARN, "refresh of Catalog " << aCat->url() << " failed:" << aReason);
     }
   }
   
-  virtual void startInstall(pkg::Install* aInstall)
+  virtual void startInstall(pkg::InstallRef aInstall)
   {
     SG_LOG(SG_IO, SG_INFO, "beginning install of:" << aInstall->package()->id()
            << " to local path:" << aInstall->path());
 
   }
   
-  virtual void installProgress(pkg::Install* aInstall, unsigned int aBytes, unsigned int aTotal)
+  virtual void installProgress(pkg::InstallRef aInstall, unsigned int aBytes, unsigned int aTotal)
   {
-    SG_LOG(SG_IO, SG_INFO, "installing:" << aInstall->package()->id() << ":"
-           << aBytes << " of " << aTotal);
   }
   
-  virtual void finishInstall(pkg::Install* aInstall)
+  virtual void finishInstall(pkg::InstallRef aInstall, StatusCode aReason)
   {
+      if (aReason == STATUS_SUCCESS) {
     SG_LOG(SG_IO, SG_INFO, "finished install of:" << aInstall->package()->id()
            << " to local path:" << aInstall->path());
+      } else {
+          SG_LOG(SG_IO, SG_WARN, "install failed of:" << aInstall->package()->id()
+                 << " to local path:" << aInstall->path());
+      }
 
   }
-  
-  virtual void failedInstall(pkg::Install* aInstall, FailureCode aReason)
-  {
-    SG_LOG(SG_IO, SG_WARN, "install failed of:" << aInstall->package()->id()
-           << " to local path:" << aInstall->path());
-  }
-
-};
-
-} // of anonymous namespace
+}; // of FGHTTPClient::FGDelegate
 
 FGHTTPClient::FGHTTPClient() :
     _inited(false)
@@ -142,11 +145,12 @@ void FGHTTPClient::init()
     // package system needs access to the HTTP engine too
     packageRoot->setHTTPClient(_http.get());
     
-    packageRoot->setDelegate(new FGDelegate);
+    _packageDelegate.reset(new FGDelegate);
+    packageRoot->addDelegate(_packageDelegate.get());
 
-    const char * defaultCatalogId = fgGetString("/sim/package-system/default-catalog/id", "org.flightgear.default" );
+    const char * defaultCatalogId = fgGetString("/sim/package-system/default-catalog/id", "org.flightgear.official" );
     const char * defaultCatalogUrl = fgGetString("/sim/package-system/default-catalog/url",
-            "http://fgfs.goneabitbursar.com/pkg/" FLIGHTGEAR_VERSION "/default-catalog.xml");
+            "http://fgfs.goneabitbursar.com/pkg/" FLIGHTGEAR_VERSION "/catalog.xml");
     // setup default catalog if not present
     pkg::Catalog* defaultCatalog = packageRoot->getCatalogById( defaultCatalogId );
     if (!defaultCatalog) {
@@ -299,7 +303,13 @@ void FGHTTPClient::postinit()
 
 void FGHTTPClient::shutdown()
 {
-  _http.reset();
+    pkg::Root* packageRoot = globals->packageRoot();
+    if (packageRoot && _packageDelegate.get()) {
+        packageRoot->removeDelegate(_packageDelegate.get());
+    }
+
+    _packageDelegate.reset();
+    _http.reset();
 }
 
 void FGHTTPClient::update(double)
index 262a2e40b9daa00574d4cb5d9d5808762bcb098c..a203501f0caa1bba9a1dbd4490dcaaa80c161ee4 100644 (file)
@@ -42,8 +42,11 @@ public:
     virtual void update(double);
 
 private:
+    class FGDelegate;
+    
     bool _inited;
     std::auto_ptr<simgear::HTTP::Client> _http;
+    std::auto_ptr<FGDelegate> _packageDelegate;
 };
 
 #endif // FG_HTTP_CLIENT_HXX