Separate Viewer from MainWindow and implement PDF embedding
Stefan Bund [Thu, 21 Oct 2010 11:51:31 +0000 (13:51 +0200)]
13 files changed:
MainWindow.cc
PDFWidget.cc [new file with mode: 0644]
PDFWidget.hh [new file with mode: 0644]
Publisher.cc
Publisher.hh
PythonPublisher.cc
PythonPublisher.hh
SConscript
SConstruct
Viewer.cc [new file with mode: 0644]
Viewer.hh [new file with mode: 0644]
main.cc
project.el [new file with mode: 0644]

index 59913c7..543752d 100644 (file)
 //#include "MainWindow.ih"
 
 // Custom includes
-#include <QWebView>
-#include "Publisher.hh"
+#include "Viewer.hh"
 
 //#include "MainWindow.mpp"
 #define prefix_
 ///////////////////////////////cc.p////////////////////////////////////////
 
-prefix_ pykit::MainWindow::MainWindow(QUrl const & url, Publisher * publisher, 
-                                           QWidget * parent)
-  : QMainWindow (parent)
+prefix_ pykit::MainWindow::MainWindow(QUrl const & url, Publisher * publisher,
+                                      QWidget * parent)
+    : QMainWindow (parent)
 {
-    //QWebSettings::globalSettings()->setAttribute(QWebSettings::PluginsEnabled, true);
-
-    QWebView *view = new QWebView;
-    view->page()->setNetworkAccessManager(
-        new InternalNetworkAccessManager(view->page()->networkAccessManager(), view, publisher));
-
-    view->load(url);
-
+    Viewer * view = new Viewer(url, publisher, this);
     setCentralWidget(view);
 }
 
diff --git a/PDFWidget.cc b/PDFWidget.cc
new file mode 100644 (file)
index 0000000..76c559d
--- /dev/null
@@ -0,0 +1,73 @@
+// $Id$
+//
+// Copyright (C) 2010
+//     Stefan Bund <info@j32.de>
+
+/** \file
+    \brief PDFWidget non-inline non-template implementation */
+
+#include "PDFWidget.hh"
+//#include "PDFWidget.ih"
+
+// Custom includes
+#include <iostream>
+#include <QNetworkRequest>
+#include <QNetworkReply>
+
+//#include "PDFWidget.mpp"
+#define prefix_
+///////////////////////////////cc.p////////////////////////////////////////
+
+prefix_ pykit::PDFWidget::PDFWidget(QNetworkAccessManager * manager, QWidget * parent)
+    : QLabel(parent), manager_ (manager), currentPage_ (0)
+{}
+
+prefix_ pykit::PDFWidget::PDFWidget(QString const & document, QNetworkAccessManager * manager,
+                                    QWidget * parent)
+    : QLabel(parent), manager_ (manager), currentPage_ (0)
+{
+    document_.reset(Poppler::Document::load(document));
+    showPage();
+}
+
+prefix_ void pykit::PDFWidget::load(QUrl const & url)
+{
+    QNetworkRequest request (url);
+    QNetworkReply * reply (manager_->get(request));
+    connect(reply, SIGNAL(finished()), this, SLOT(netLoadDocument()));
+}
+
+prefix_ void pykit::PDFWidget::netLoadDocument()
+{
+    QNetworkReply * reply = static_cast<QNetworkReply*>(sender());
+    if (reply->error() != QNetworkReply::NoError)
+        return;
+    QByteArray data (reply->read(reply->size()));
+    document_.reset(Poppler::Document::loadFromData(data));
+    showPage();
+    reply->deleteLater();
+}
+
+prefix_ void pykit::PDFWidget::showPage()
+{
+    if (! document_)
+        return;
+    QImage image (document_->page(currentPage_)->renderToImage(
+                      physicalDpiX(), physicalDpiY()));
+    setPixmap(QPixmap::fromImage(image));
+}
+
+///////////////////////////////cc.e////////////////////////////////////////
+#undef prefix_
+//#include "PDFWidget.mpp"
+
+\f
+// Local Variables:
+// mode: c++
+// fill-column: 100
+// comment-column: 40
+// c-file-style: "j32"
+// indent-tabs-mode: nil
+// ispell-local-dictionary: "american"
+// compile-command: "scons -U"
+// End:
diff --git a/PDFWidget.hh b/PDFWidget.hh
new file mode 100644 (file)
index 0000000..a6212ff
--- /dev/null
@@ -0,0 +1,64 @@
+// $Id$
+//
+// Copyright (C) 2010
+//     Stefan Bund <info@j32.de>
+
+/** \file
+    \brief PDFWidget public header */
+
+#ifndef HH_PyKit_PDFWidget_
+#define HH_PyKit_PDFWidget_ 1
+
+// Custom includes
+#include <poppler/qt4/poppler-qt4.h>
+#include <boost/scoped_ptr.hpp>
+#include <QLabel>
+#include <QString>
+#include <QWidget>
+#include <QNetworkAccessManager>
+#include <QUrl>
+
+//#include "PDFWidget.mpp"
+///////////////////////////////hh.p////////////////////////////////////////
+
+namespace pykit {
+
+    class PDFWidget : public QLabel
+    {
+        Q_OBJECT;
+    public:
+        explicit PDFWidget(QNetworkAccessManager * manager, QWidget * parent = 0);
+        PDFWidget(QString const & document, QNetworkAccessManager * manager,
+                  QWidget * parent = 0);
+
+        void load(QUrl const & url);
+
+    private slots:
+        void netLoadDocument();
+
+    private:
+        void showPage();
+
+        QNetworkAccessManager * manager_;
+        boost::scoped_ptr<Poppler::Document> document_;
+        int currentPage_;
+    };
+
+}
+
+///////////////////////////////hh.e////////////////////////////////////////
+//#include "PDFWidget.cci"
+//#include "PDFWidget.ct"
+//#include "PDFWidget.cti"
+#endif
+
+\f
+// Local Variables:
+// mode: c++
+// fill-column: 100
+// comment-column: 40
+// c-file-style: "j32"
+// indent-tabs-mode: nil
+// ispell-local-dictionary: "american"
+// compile-command: "scons -U"
+// End:
index 02c3521..7176b7c 100644 (file)
@@ -54,8 +54,8 @@ namespace detail {
         void clearResponse();
 
     protected:
-        qint64 readData(char * data, qint64 maxSize);
-        qint64 writeData(char const * data, qint64 maxSize);
+        virtual qint64 readData(char * data, qint64 maxSize);
+        virtual qint64 writeData(char const * data, qint64 maxSize);
 
     signals:
         void initSignal();
@@ -160,7 +160,7 @@ prefix_ void pykit::detail::InternalServerReply::clearResponse()
 
 prefix_ void pykit::Request::write(std::string const & data)
 {
-    reply_.write(data.c_str());
+    reply_.write(QByteArray(data.data(),data.size()));
 }
 
 prefix_ void pykit::Request::reset()
index 5e95625..2fd4cba 100644 (file)
@@ -14,6 +14,7 @@
 #include <QNetworkRequest>
 #include <QNetworkReply>
 #include <QBuffer>
+#include <boost/noncopyable.hpp>
 
 //#include "Publisher.mpp"
 ///////////////////////////////hh.p////////////////////////////////////////
@@ -42,6 +43,7 @@ namespace pykit {
     };
 
     class Publisher
+        : boost::noncopyable
     {
     public:
         virtual ~Publisher() {}
index c75fc88..56d7bc4 100644 (file)
@@ -10,6 +10,8 @@
 //#include "PythonPublisher.ih"
 
 // Custom includes
+#include <iostream>
+#include <boost/python.hpp>
 #include "Publisher.hh"
 
 //#include "PythonPublisher.mpp"
 
 namespace py = boost::python;
 
+struct pykit::PythonPublisher::Impl
+{
+    boost::python::dict mainNamespace;
+    Publisher * pythonPublisher;
+};
+
+#define PYTHON_PREPARE_IMPORT(module) \
+    PyImport_AppendInittab(const_cast<char*>(#module), init ## module)
+
 namespace {
 
     struct PublisherPyWrapper
@@ -34,7 +45,7 @@ namespace {
             static PyObject * convert(QString const & s)
                 {
                     std::wstring ws (s.toStdWString());
-                    return PyUnicode_FromWideChar(ws.c_str(), ws.length());
+                    return PyUnicode_FromWideChar(ws.data(), ws.length());
                 }
         };
 
@@ -88,10 +99,11 @@ namespace {
             static void construct(PyObject * o,
                                   py::converter::rvalue_from_python_stage1_data * data)
                 {
+                    unsigned length (PyString_Size(o));
                     const char * value (PyString_AsString(o));
                     void * storage (((py::converter::rvalue_from_python_storage<QByteArray>*)
                                      data)->storage.bytes);
-                    new (storage) QByteArray(value);
+                    new (storage) QByteArray(value,length);
                     data->convertible = storage;
                 }
 
@@ -420,22 +432,32 @@ BOOST_PYTHON_MODULE(_pykit)
 }
 
 prefix_ pykit::PythonPublisher::PythonPublisher(std::string initPy)
+    : impl_ (new Impl)
 {
-    PYTHON_PREPARE_IMPORT(_pykit);
-    PYTHON_PREPARE_IMPORT(_qt);
-    Py_Initialize();
-    py::object mainModule_ = py::import("__main__");
-    mainNamespace_ = py::extract<py::dict>(mainModule_.attr("__dict__"));
-    mainNamespace_["__file__"] = py::str(initPy.c_str());
-    py::object ignored (
-        py::exec_file(initPy.c_str(), mainNamespace_, mainNamespace_));
-    pythonPublisher_ = py::extract<Publisher*>(mainNamespace_["publisher"]);
+    try {
+        PYTHON_PREPARE_IMPORT(_pykit);
+        PYTHON_PREPARE_IMPORT(_qt);
+        Py_Initialize();
+        py::object mainModule_ = py::import("__main__");
+        impl_->mainNamespace = py::extract<py::dict>(mainModule_.attr("__dict__"));
+        impl_->mainNamespace["__file__"] = py::str(initPy.c_str());
+        py::object ignored (
+            py::exec_file(initPy.c_str(), impl_->mainNamespace, impl_->mainNamespace));
+        impl_->pythonPublisher = py::extract<Publisher*>(impl_->mainNamespace["publisher"]);
+    }
+    catch (boost::python::error_already_set & ex) {
+        PyErr_Print();
+        throw;
+    }
 }
 
+prefix_ pykit::PythonPublisher::~PythonPublisher()
+{}
+
 prefix_ void pykit::PythonPublisher::publish(Request & request)
 {
     try {
-        pythonPublisher_->publish(request);
+        impl_->pythonPublisher->publish(request);
     }
     catch (py::error_already_set & ex) {
         PyErr_Print();
index b00c983..b6b6a5e 100644 (file)
@@ -10,7 +10,7 @@
 #define HH_PyKit_PythonPublisher_ 1
 
 // Custom includes
-#include <boost/python.hpp>
+#include <boost/scoped_ptr.hpp>
 #include "Publisher.hh"
 
 //#include "PythonPublisher.mpp"
@@ -22,18 +22,16 @@ namespace pykit {
         : public Publisher
     {
     public:
-        PythonPublisher(std::string initPy);
+        explicit PythonPublisher(std::string initPy);
+        ~PythonPublisher();
 
         void publish(Request & request);
 
     private:
-        boost::python::dict mainNamespace_;
-        Publisher * pythonPublisher_;
+        struct Impl;
+        boost::scoped_ptr<Impl> impl_;
     };
 
-#   define PYTHON_PREPARE_IMPORT(module) \
-        PyImport_AppendInittab(const_cast<char*>(#module), init ## module)
-
 }
 
 ///////////////////////////////hh.e////////////////////////////////////////
index 56a2e28..2614eb2 100644 (file)
@@ -9,12 +9,9 @@ env.Tool('qt4', [ os.path.join('site_scons','qtscons') ])
 env.EnableQt4Modules([ 'QtWebKit', 'QtNetwork', 'QtSvg', 'QtXml', 'QtGui', 'QtCore' ])
 
 env.Append(
-    CXXFLAGS          = [ '-Wall', '-Wextra' ],
-    CPPPATH           = [ '$LIBPYTHONINCLUDES' ],
-    LIBS              = [ '$LIBBOOSTPYTHON', '$LIBPYTHON' ]
+    CXXFLAGS = [ '-Wall', '-Wextra' ],
+    LIBS     = [ '$BOOSTPYLIBS', '$POPPLERLIBS' ],
 )
 
-sources = env.Qt4Glob()
-
-pykit = env.Program('pykit', sources)
+pykit = env.Program('pykit', env.Qt4Glob())
 env.Default(pykit)
index beeaf1b..a5b3878 100644 (file)
@@ -5,17 +5,12 @@ import os
 env = Environment(ENV = os.environ)
 
 env.SetDefault(
-    PYTHON            = 'python',
-    LIBPYTHON         = 'python$PYTHONVERSION',
-    LIBPYTHONINCLUDES = '/usr/include/python$PYTHONVERSION',
-    LIBBOOSTPYTHON    = 'boost_python'
+    BOOSTPYLIBS = [ 'boost_python' ],
+    POPPLERLIBS = [ 'poppler', 'poppler-qt4' ],
 )
 
-env.SetDefault(
-    PYTHONVERSION     = os.popen(env.subst(
-            """$PYTHON -c 'import sys; print".".join(map(str,sys.version_info[:2]))'""")).read(),
-)
+env.MergeFlags('!python-config --cflags')
+env.MergeFlags('!python-config --ldflags')
 
 Export('env')
-
 env.SConscript("SConscript")
diff --git a/Viewer.cc b/Viewer.cc
new file mode 100644 (file)
index 0000000..abb1b2f
--- /dev/null
+++ b/Viewer.cc
@@ -0,0 +1,106 @@
+// $Id$
+//
+// Copyright (C) 2010
+//     Stefan Bund <info@j32.de>
+
+/** \file
+    \brief Viewer non-inline non-template implementation */
+
+#include "Viewer.hh"
+//#include "Viewer.ih"
+
+// Custom includes
+#include <iostream>
+#include <QWebPluginFactory>
+#include "Publisher.hh"
+#include "PDFWidget.hh"
+
+//#include "Viewer.mpp"
+#define prefix_
+///////////////////////////////cc.p////////////////////////////////////////
+
+namespace {
+
+    class PDFWebPluginFactory
+        : public QWebPluginFactory
+    {
+    public:
+        PDFWebPluginFactory(QNetworkAccessManager * manager, QObject * parent = 0);
+
+        QObject * create(QString const & mimeType, QUrl const & url,
+                         QStringList const & argumentNames, QStringList const & argumentValues)
+            const;
+        QList<QWebPluginFactory::Plugin> plugins() const;
+
+    private:
+        QNetworkAccessManager * manager_;
+    };
+
+}
+
+prefix_ PDFWebPluginFactory::PDFWebPluginFactory(QNetworkAccessManager * manager,
+                                                 QObject * parent)
+    : QWebPluginFactory(parent), manager_ (manager)
+{}
+
+prefix_ QObject * PDFWebPluginFactory::create(QString const & mimeType, QUrl const & url,
+                                              QStringList const & /* argumentNames */,
+                                              QStringList const & /* argumentValues */)
+    const
+{
+    if (mimeType == "application/x-pdf") {
+        pykit::PDFWidget * plugin = new pykit::PDFWidget(manager_);
+        plugin->load(url);
+        return plugin;
+    }
+    return 0;
+}
+
+prefix_ QList<QWebPluginFactory::Plugin> PDFWebPluginFactory::plugins()
+    const
+{
+    QList<QWebPluginFactory::Plugin> plugins;
+
+    {
+        QWebPluginFactory::Plugin plugin;
+        plugin.name = "PDF viewer";
+        plugin.description = "View PDF files";
+        {
+            QWebPluginFactory::MimeType mimeType;
+            mimeType.name = "application/x-pdf";
+            mimeType.description = "PDF file";
+            plugin.mimeTypes += mimeType;
+        }
+        plugins += plugin;
+    }
+
+    return plugins;
+}
+
+///////////////////////////////////////////////////////////////////////////
+// pykit::Viewer
+
+prefix_ pykit::Viewer::Viewer(QUrl const & url, Publisher * publisher, QWidget * parent)
+    : QWebView (parent)
+{
+    QWebSettings::globalSettings()->setAttribute(QWebSettings::PluginsEnabled, true);
+    page()->setNetworkAccessManager(
+        new InternalNetworkAccessManager(page()->networkAccessManager(), this, publisher));
+    page()->setPluginFactory(new PDFWebPluginFactory (page()->networkAccessManager(), this));
+    load(url);
+}
+
+///////////////////////////////cc.e////////////////////////////////////////
+#undef prefix_
+//#include "Viewer.mpp"
+
+\f
+// Local Variables:
+// mode: c++
+// fill-column: 100
+// comment-column: 40
+// c-file-style: "j32"
+// indent-tabs-mode: nil
+// ispell-local-dictionary: "american"
+// compile-command: "scons -U"
+// End:
diff --git a/Viewer.hh b/Viewer.hh
new file mode 100644 (file)
index 0000000..21ba371
--- /dev/null
+++ b/Viewer.hh
@@ -0,0 +1,48 @@
+// $Id$
+//
+// Copyright (C) 2010
+//     Stefan Bund <info@j32.de>
+
+/** \file
+    \brief Viewer public header */
+
+#ifndef HH_PyKit_Viewer_
+#define HH_PyKit_Viewer_ 1
+
+// Custom includes
+#include <QWebView>
+#include <QUrl>
+
+//#include "Viewer.mpp"
+///////////////////////////////hh.p////////////////////////////////////////
+
+namespace pykit {
+
+    class Publisher;
+
+    class Viewer
+        : public QWebView
+    {
+        Q_OBJECT;
+    public:
+        Viewer(QUrl const & url, Publisher * publisher, QWidget * parent = 0);
+    };
+
+}
+
+///////////////////////////////hh.e////////////////////////////////////////
+//#include "Viewer.cci"
+//#include "Viewer.ct"
+//#include "Viewer.cti"
+#endif
+
+\f
+// Local Variables:
+// mode: c++
+// fill-column: 100
+// comment-column: 40
+// c-file-style: "j32"
+// indent-tabs-mode: nil
+// ispell-local-dictionary: "american"
+// compile-command: "scons -U"
+// End:
diff --git a/main.cc b/main.cc
index 84a675f..fcebd20 100644 (file)
--- a/main.cc
+++ b/main.cc
@@ -15,7 +15,6 @@
 #include <QApplication>
 #include <QSplashScreen>
 #include <QSettings>
-#include <boost/python.hpp>
 #include <boost/scoped_ptr.hpp>
 #include "MainWindow.hh"
 #include "PythonPublisher.hh"
@@ -68,10 +67,6 @@ int main(int argc, char *argv[])
 
         return app.exec();
     }
-    catch (boost::python::error_already_set & ex) {
-        PyErr_Print();
-        throw;
-    }
     catch (std::exception & ex) {
         std::cerr << "Exception:\n" << ex.what() << "\n";
         throw;
diff --git a/project.el b/project.el
new file mode 100644 (file)
index 0000000..ae81838
--- /dev/null
@@ -0,0 +1,59 @@
+;; Configuration file for cc-ide.el (Emacs C++ IDE extension, see http://g0dil.de)
+
+(defun check-namespace-indent (arg)
+  (save-excursion
+    (back-to-indentation)
+    (if (and (looking-at "namespace")
+             (looking-at ".*{")
+             (not (looking-at ".*}")))
+        [0] '+)))
+
+(defconst j32-c-style
+  '((c-basic-offset              . 4)
+    (c-access-key                . "\\(public\\|protected\\|private\\|signals\\|public\\s-*slots\\|protected\\s-*slots\\|private\\s-slots\\)\\s-*:")
+    (c-backslash-column          . 98)
+    (c-cleanup-list              . (empty-defun-braces
+                                    defun-close-semi
+                                    list-close-comma
+                                    scope-operator))
+    (c-hanging-braces-alist      . ((namespace-open after)
+                                    (namespace-close before after)
+                                    (brace-list-open)
+                                    (brace-entry-open)
+                                    (substatement-open after)
+                                    (block-close . c-snug-do-while)
+                                    (extern-lang-open after)
+                                    (inexpr-class-open after)
+                                    (inexpr-class-close before)))
+    (c-offsets-alist             . ((namespace-open . [0])
+                                    (namespace-close . [0])
+                                    (innamespace . check-namespace-indent)
+                                    (statement-block-intro . +)
+                                    (substatement-open . 0)
+                                    (label . 0)
+                                    (statement-cont . +))) ))
+
+(c-add-style "j32" j32-c-style)
+
+(set (make-local-variable 'ccide-file-vars)
+     '((fill-column  . 100)
+       (comment-column . 40)
+       (c-file-style . "j32")
+       (indent-tabs-mode . nil)
+       (ispell-local-dictionary . "american")
+       (compile-command . "scons -U")))
+
+(set (make-local-variable 'ccide-project-name) "PyKit")
+
+(let ((local-conf (ccide-project-search-upwards "project-local.el")))
+  (if local-conf
+      (load-file local-conf)))
+
+(setq indent-tabs-mode nil) ;; needed since whitespace-mode caches this value ...
+(whitespace-mode 1)
+
+(message "Loaded project settings.")
+\f
+;; Local Variables:
+;; indent-tabs-mode: nil
+;; End: