changeset 305:013a37723c0a

* Add support for remote files to image layer
author Chris Cannam
date Fri, 05 Oct 2007 15:52:52 +0000
parents 4b7e8da8f069
children 90b9cfb5b0bb
files layer/ImageLayer.cpp layer/ImageLayer.h widgets/ImageDialog.cpp widgets/ImageDialog.h
diffstat 4 files changed, 182 insertions(+), 15 deletions(-) [+]
line wrap: on
line diff
--- a/layer/ImageLayer.cpp	Fri Oct 05 13:27:21 2007 +0000
+++ b/layer/ImageLayer.cpp	Fri Oct 05 15:52:52 2007 +0000
@@ -21,12 +21,14 @@
 #include "view/View.h"
 
 #include "data/model/ImageModel.h"
+#include "data/fileio/RemoteFile.h"
 
 #include "widgets/ImageDialog.h"
 
 #include <QPainter>
 #include <QMouseEvent>
 #include <QInputDialog>
+#include <QMutexLocker>
 
 #include <iostream>
 #include <cmath>
@@ -34,6 +36,9 @@
 ImageLayer::ImageMap
 ImageLayer::m_images;
 
+QMutex
+ImageLayer::m_imageMapMutex;
+
 ImageLayer::ImageLayer() :
     Layer(),
     m_model(0),
@@ -42,7 +47,14 @@
     m_editingPoint(0, "", ""),
     m_editingCommand(0)
 {
-    
+}
+
+ImageLayer::~ImageLayer()
+{
+    for (RemoteFileMap::iterator i = m_remoteFiles.begin();
+         i != m_remoteFiles.end(); ++i) {
+        delete i->second;
+    }
 }
 
 void
@@ -51,15 +63,21 @@
     if (m_model == model) return;
     m_model = model;
 
-    connect(m_model, SIGNAL(modelChanged()), this, SIGNAL(modelChanged()));
+    connect(m_model, SIGNAL(modelChanged()),
+            this, SIGNAL(modelChanged()));
     connect(m_model, SIGNAL(modelChanged(size_t, size_t)),
 	    this, SIGNAL(modelChanged(size_t, size_t)));
 
     connect(m_model, SIGNAL(completionChanged()),
 	    this, SIGNAL(modelCompletionChanged()));
 
+//    connect(m_model, SIGNAL(modelChanged()),
+//            this, SLOT(checkAddRemotes()));
+
 //    std::cerr << "ImageLayer::setModel(" << model << ")" << std::endl;
 
+//    checkAddRemotes();
+
     emit modelReplaced();
 }
 
@@ -147,7 +165,7 @@
         int width = 32;
         if (m_scaled[v].find(p.image) != m_scaled[v].end()) {
             width = m_scaled[v][p.image].width();
-            std::cerr << "scaled width = " << width << std::endl;
+//            std::cerr << "scaled width = " << width << std::endl;
         }
 
         if (x >= px && x < px + width) {
@@ -478,6 +496,7 @@
         // Delete the images named in the view's scaled map from the
         // general image map as well.  They can always be re-loaded
         // if it turns out another view still needs them.
+        QMutexLocker locker(&m_imageMapMutex);
         for (ImageMap::iterator i = m_scaled[v].begin();
              i != m_scaled[v].end(); ++i) {
             m_images.erase(i->first);
@@ -491,10 +510,15 @@
 bool
 ImageLayer::getImageOriginalSize(QString name, QSize &size) const
 {
+//    std::cerr << "getImageOriginalSize: \"" << name.toStdString() << "\"" << std::endl;
+
+    QMutexLocker locker(&m_imageMapMutex);
     if (m_images.find(name) == m_images.end()) {
-        m_images[name] = QImage(name);
+//        std::cerr << "don't have, trying to open local" << std::endl;
+        m_images[name] = QImage(getLocalFilename(name));
     }
     if (m_images[name].isNull()) {
+//        std::cerr << "null image" << std::endl;
         return false;
     } else {
         size = m_images[name].size();
@@ -507,24 +531,26 @@
 {
     bool need = false;
 
-    std::cerr << "ImageLayer::getImage(" << v << ", " << name.toStdString() << ", ("
-              << maxSize.width() << "x" << maxSize.height() << "))" << std::endl;
+//    std::cerr << "ImageLayer::getImage(" << v << ", " << name.toStdString() << ", ("
+//              << maxSize.width() << "x" << maxSize.height() << "))" << std::endl;
 
     if (!m_scaled[v][name].isNull()  &&
         ((m_scaled[v][name].width()  == maxSize.width() &&
           m_scaled[v][name].height() <= maxSize.height()) ||
          (m_scaled[v][name].width()  <= maxSize.width() &&
           m_scaled[v][name].height() == maxSize.height()))) {
-        std::cerr << "cache hit" << std::endl;
+//        std::cerr << "cache hit" << std::endl;
         return m_scaled[v][name];
     }
 
+    QMutexLocker locker(&m_imageMapMutex);
+
     if (m_images.find(name) == m_images.end()) {
-        m_images[name] = QImage(name);
+        m_images[name] = QImage(getLocalFilename(name));
     }
 
     if (m_images[name].isNull()) {
-        std::cerr << "null image" << std::endl;
+//        std::cerr << "null image" << std::endl;
         m_scaled[v][name] = QImage();
     } else if (m_images[name].width() <= maxSize.width() &&
                m_images[name].height() <= maxSize.height()) {
@@ -588,7 +614,11 @@
     bool ok = false;
 
     ImageDialog dialog(tr("Select image"), "", tr("<no label>"));
+
     if (dialog.exec() == QDialog::Accepted) {
+
+        checkAddRemote(dialog.getImage());
+
 	ImageModel::ChangeImageCommand *command =
 	    new ImageModel::ChangeImageCommand
             (m_model, m_editingPoint, dialog.getImage(), dialog.getLabel());
@@ -672,9 +702,13 @@
                        label);
 
     if (dialog.exec() == QDialog::Accepted) {
+
+        checkAddRemote(dialog.getImage());
+
 	ImageModel::ChangeImageCommand *command =
 	    new ImageModel::ChangeImageCommand
             (m_model, *points.begin(), dialog.getImage(), dialog.getLabel());
+
         CommandHistory::getInstance()->addCommand(command);
     }
 
@@ -815,6 +849,77 @@
 }
 
 QString
+ImageLayer::getLocalFilename(QString img) const
+{
+    if (m_remoteFiles.find(img) == m_remoteFiles.end()) {
+        checkAddRemote(img);
+        return img;
+    }
+    return m_remoteFiles[img]->getLocalFilename();
+}
+
+void
+ImageLayer::checkAddRemote(QString img) const
+{
+    if (RemoteFile::isRemote(img)) {
+
+        if (m_remoteFiles.find(img) != m_remoteFiles.end()) {
+            return;
+        }
+
+        QUrl url(img);
+        if (RemoteFile::canHandleScheme(url)) {
+            RemoteFile *rf = new RemoteFile(url);
+            if (rf->isOK()) {
+                m_remoteFiles[img] = rf;
+                connect(rf, SIGNAL(ready()), this, SLOT(remoteFileReady()));
+            } else {
+                delete rf;
+            }
+        }
+    }
+}
+
+void
+ImageLayer::checkAddRemotes()
+{
+    const ImageModel::PointList &points(m_model->getPoints());
+
+    for (ImageModel::PointList::const_iterator i = points.begin();
+	 i != points.end(); ++i) {
+        
+        checkAddRemote((*i).image);
+    }
+}
+
+void
+ImageLayer::remoteFileReady()
+{
+//    std::cerr << "ImageLayer::remoteFileReady" << std::endl;
+
+    RemoteFile *rf = dynamic_cast<RemoteFile *>(sender());
+    if (!rf) return;
+
+    QString img;
+    for (RemoteFileMap::const_iterator i = m_remoteFiles.begin();
+         i != m_remoteFiles.end(); ++i) {
+        if (i->second == rf) {
+            img = i->first;
+//            std::cerr << "it's image \"" << img.toStdString() << "\"" << std::endl;
+            break;
+        }
+    }
+    if (img == "") return;
+
+    QMutexLocker locker(&m_imageMapMutex);
+    m_images.erase(img);
+    for (ViewImageMap::iterator i = m_scaled.begin(); i != m_scaled.end(); ++i) {
+        i->second.erase(img);
+        const_cast<View *>(i->first)->update();
+    }
+}
+
+QString
 ImageLayer::toXmlString(QString indent, QString extraAttributes) const
 {
     return Layer::toXmlString(indent, extraAttributes);
--- a/layer/ImageLayer.h	Fri Oct 05 13:27:21 2007 +0000
+++ b/layer/ImageLayer.h	Fri Oct 05 15:52:52 2007 +0000
@@ -22,11 +22,13 @@
 #include <QObject>
 #include <QColor>
 #include <QImage>
+#include <QMutex>
 
 #include <map>
 
 class View;
 class QPainter;
+class RemoteFile;
 
 class ImageLayer : public Layer
 {
@@ -34,6 +36,7 @@
 
 public:
     ImageLayer();
+    virtual ~ImageLayer();
 
     virtual void paint(View *v, QPainter &paint, QRect rect) const;
 
@@ -93,6 +96,10 @@
 
     void setProperties(const QXmlAttributes &attributes);
 
+protected slots:
+    void checkAddRemotes();
+    void remoteFileReady();
+
 protected:
     ImageModel::PointList getLocalPoints(View *v, int x, int y) const;
 
@@ -106,11 +113,15 @@
 
     typedef std::map<QString, QImage> ImageMap;
     typedef std::map<const View *, ImageMap> ViewImageMap;
-
-
+    typedef std::map<QString, RemoteFile *> RemoteFileMap;
 
     static ImageMap m_images;
+    static QMutex m_imageMapMutex;
     mutable ViewImageMap m_scaled;
+    mutable RemoteFileMap m_remoteFiles;
+
+    QString getLocalFilename(QString img) const;
+    void checkAddRemote(QString img) const;
 
     ImageModel *m_model;
     bool m_editing;
--- a/widgets/ImageDialog.cpp	Fri Oct 05 13:27:21 2007 +0000
+++ b/widgets/ImageDialog.cpp	Fri Oct 05 15:52:52 2007 +0000
@@ -23,15 +23,21 @@
 #include <QGroupBox>
 #include <QDesktopWidget>
 #include <QApplication>
+#include <QUrl>
+#include <QMessageBox>
 
+#include "data/fileio/RemoteFile.h"
 #include "data/fileio/FileFinder.h"
 
+#include <iostream>
+
 ImageDialog::ImageDialog(QString title,
                          QString image,
                          QString label,
                          QWidget *parent) :
     QDialog(parent),
-    m_imagePreview(0)
+    m_imagePreview(0),
+    m_remoteFile(0)
 {
     setWindowTitle(title);
     
@@ -52,12 +58,15 @@
 
     ++row;
 
-    subgrid->addWidget(new QLabel(tr("File:")), row, 0);
+    subgrid->addWidget(new QLabel(tr("File or URL:")), row, 0);
 
     m_imageEdit = new QLineEdit;
     subgrid->addWidget(m_imageEdit, row, 1, 1, 1);
+
     connect(m_imageEdit, SIGNAL(textEdited(const QString &)),
             this, SLOT(imageEditEdited(const QString &)));
+    connect(m_imageEdit, SIGNAL(editingFinished()),
+            this, SLOT(imageEditEdited()));
 
     QPushButton *browse = new QPushButton(tr("Browse..."));
     connect(browse, SIGNAL(clicked()), this, SLOT(browseClicked()));
@@ -100,6 +109,7 @@
 
 ImageDialog::~ImageDialog()
 {
+    delete m_remoteFile;
 }
 
 QString
@@ -140,7 +150,16 @@
 }
 
 void
-ImageDialog::imageEditEdited(const QString &)
+ImageDialog::imageEditEdited(const QString &s)
+{
+    if (s.startsWith("http:") || s.startsWith("ftp:")) {
+        return;
+    }
+    updatePreview();
+}
+
+void
+ImageDialog::imageEditEdited()
 {
     updatePreview();
 }
@@ -155,7 +174,35 @@
     m_okButton->setEnabled(img != "");
 
     if (img != m_loadedImageFile) {
-        m_loadedImage = QPixmap(img);
+
+        QString fileName = img;
+        delete m_remoteFile;
+        m_remoteFile = 0;
+
+        if (RemoteFile::isRemote(fileName)) {
+            QUrl url(fileName);
+            if (!RemoteFile::canHandleScheme(url)) {
+                QMessageBox::critical(this, tr("Unsupported scheme in URL"),
+                                      tr("The URL scheme \"%1\" is not supported")
+                                      .arg(url.scheme()));
+            } else {
+                m_remoteFile = new RemoteFile(url);
+                m_remoteFile->wait();
+                if (!m_remoteFile->isOK()) {
+                    QMessageBox::critical(this, tr("File download failed"),
+                                          tr("Failed to download URL \"%1\": %2")
+                                          .arg(url.toString()).arg(m_remoteFile->getErrorString()));
+                    delete m_remoteFile;
+                    m_remoteFile = 0;
+                } else {
+                    fileName = m_remoteFile->getLocalFilename();
+                }
+            }
+        }
+        
+//        std::cerr << "image filename: \"" << fileName.toStdString() << "\"" << std::endl;
+
+        m_loadedImage = QPixmap(fileName);
         m_loadedImageFile = img;
     }
 
--- a/widgets/ImageDialog.h	Fri Oct 05 13:27:21 2007 +0000
+++ b/widgets/ImageDialog.h	Fri Oct 05 15:52:52 2007 +0000
@@ -22,6 +22,7 @@
 class QLineEdit;
 class QLabel;
 class QPushButton;
+class RemoteFile;
 
 class ImageDialog : public QDialog
 {
@@ -50,6 +51,7 @@
 protected slots:
     void browseClicked();
     void imageEditEdited(const QString &);
+    void imageEditEdited();
 
 protected:
     void resizeEvent(QResizeEvent *);
@@ -62,6 +64,8 @@
     QPixmap m_loadedImage;
 
     QPushButton *m_okButton;
+
+    RemoteFile *m_remoteFile;
 };
 
 #endif