├── src ├── src.pro └── dqml │ ├── dqml.pro │ ├── dqmlglobal.cpp │ ├── dqmlglobal.h │ ├── dqmllocalserver.cpp │ ├── dqmllocalserver.h │ ├── dqmlserver.h │ ├── dqmlfiletracker.h │ ├── dqmlmonitor.h │ ├── dqmlmonitor.cpp │ ├── dqmlfiletracker.cpp │ └── dqmlserver.cpp ├── tools ├── tools.pro └── dqmltool │ ├── dqmltool.pro │ └── dqmlmain.cpp ├── livecoder.pro ├── .qmake.conf ├── sync.profile └── README.md /src/src.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = subdirs 2 | SUBDIRS = dqml 3 | -------------------------------------------------------------------------------- /tools/tools.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = subdirs 2 | SUBDIRS = dqmltool 3 | -------------------------------------------------------------------------------- /livecoder.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = subdirs 2 | SUBDIRS = src tools 3 | 4 | tools.depends = src 5 | -------------------------------------------------------------------------------- /tools/dqmltool/dqmltool.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = app 2 | TARGET = dqml 3 | QT += dqml 4 | SOURCES += dqmlmain.cpp 5 | load(qt_tool) 6 | -------------------------------------------------------------------------------- /.qmake.conf: -------------------------------------------------------------------------------- 1 | load(qt_build_config) 2 | # CONFIG += qt_example_installs 3 | # CONFIG += warning_clean 4 | 5 | MODULE_VERSION = 0.1.0 6 | -------------------------------------------------------------------------------- /src/dqml/dqml.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = lib 2 | TARGET = dqml 3 | 4 | QT += quick 5 | 6 | load(qt_module) 7 | 8 | 9 | CONFIG -= create_cmake 10 | 11 | SOURCES += \ 12 | dqmlfiletracker.cpp \ 13 | dqmlglobal.cpp \ 14 | dqmllocalserver.cpp \ 15 | dqmlmonitor.cpp \ 16 | dqmlserver.cpp \ 17 | 18 | HEADERS += \ 19 | dqmlfiletracker.h \ 20 | dqmlglobal.h \ 21 | dqmllocalserver.h \ 22 | dqmlmonitor.h \ 23 | dqmlserver.h \ 24 | 25 | DEFINES += DQML_BUILD_LIB=1 26 | -------------------------------------------------------------------------------- /sync.profile: -------------------------------------------------------------------------------- 1 | %modules = ( # path to module name map 2 | "dqml" => "$basedir/src/dqml", 3 | ); 4 | %moduleheaders = ( # restrict the module headers to those found in relative path 5 | ); 6 | %deprecatedheaders = ( 7 | ); 8 | # Module dependencies. 9 | # Every module that is required to build this module should have one entry. 10 | # Each of the module version specifiers can take one of the following values: 11 | # - A specific Git revision. 12 | # - any git symbolic ref resolvable from the module's repository (e.g. "refs/heads/master" to track master branch) 13 | # - an empty string to use the same branch under test (dependencies will become "refs/heads/master" if we are in the master branch) 14 | # 15 | %dependencies = ( 16 | "qtbase" => "", 17 | "qtdeclarative" => "", 18 | ); 19 | -------------------------------------------------------------------------------- /src/dqml/dqmlglobal.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2014, Gunnar Sletta 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | #include "dqmlglobal.h" 28 | 29 | Q_LOGGING_CATEGORY(DQML_LOG, "dqml") 30 | -------------------------------------------------------------------------------- /src/dqml/dqmlglobal.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2014, Gunnar Sletta 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | #ifndef DQMLGLOBAL_H 28 | #define DQMLGLOBAL_H 29 | 30 | #include 31 | 32 | #ifdef DQML_BUILD_LIB 33 | Q_DECLARE_LOGGING_CATEGORY(DQML_LOG) 34 | # define DQML_EXPORT Q_DECL_EXPORT 35 | #else 36 | # define DQML_EXPORT Q_DECL_IMPORT 37 | #endif 38 | 39 | QT_BEGIN_NAMESPACE 40 | 41 | 42 | QT_END_NAMESPACE 43 | 44 | #endif // DQMLGLOBAL_H 45 | -------------------------------------------------------------------------------- /src/dqml/dqmllocalserver.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2014, Gunnar Sletta 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | #include "dqmllocalserver.h" 28 | 29 | DQmlLocalServer::DQmlLocalServer(QQmlEngine *engine, QQuickView *view, const QString &file) 30 | : DQmlServer(engine, view, file) 31 | { 32 | connect(&m_tracker, SIGNAL(fileAdded(QString,QString,QString)), this, SLOT(reloadQml())); 33 | connect(&m_tracker, SIGNAL(fileRemoved(QString,QString,QString)), this, SLOT(reloadQml())); 34 | connect(&m_tracker, SIGNAL(fileChanged(QString,QString,QString)), this, SLOT(reloadQml())); 35 | } 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | A simple live coding environment for QML 2 | 3 | 4 | 5 | Standalone use: 6 | 7 | > dqml file.qml 8 | 9 | Will track the directory where file.qml is located and continuously update 10 | the view depending on changes in the filesystem when qmlfiles and images there 11 | are changed. 12 | 13 | To add more directories to be tracked, specify them with --track [path]: 14 | 15 | > dqml --track qml --track images --track . file.qml 16 | 17 | 18 | 19 | Remote use: 20 | 21 | On the host machine, run: 22 | 23 | > dqml --monitor address port 24 | 25 | Will track changes in the current directory and push them to the server. 26 | If the server is disconnected or not yet ready, it will keep trying to 27 | reconnect to the specified address. 28 | 29 | Then on the server, run: 30 | 31 | > dqml --server port file.qml 32 | 33 | To add more directories to track, specify them with --track [id] [path]. 34 | The [id] is used to identify a directory between monitor and server. Say 35 | that your application is located in /home/me/myapp on the host machine 36 | and is deployed to /usr/shared/myapp on the target machine and you are 37 | interested in exposing the subdirectories 'qml' and 'images' the commands 38 | would be as follows: 39 | 40 | On the host machine: 41 | 42 | > dqml --monitor address port --track qmlfiles /home/me/myapp/qml --trace /home/me/myapp/images 43 | 44 | On the target machine: 45 | 46 | > dqml --server port --track qmlfiles /usr/share/myapp/qml --track /usr/share/myapp/images 47 | 48 | 49 | 50 | Limitations: 51 | 52 | - Both the server and monitor operate on files, so QML files and images 53 | inside qrc cannot be updated and worked on. 54 | 55 | - The reevaulation of code in the server is 'dumb'. It clears the QQmlEngne's 56 | component cache and reloads all files. The toplevel QML object is destroyed. 57 | This means that any JS/QML state the application has built up by the time 58 | the reevaluation happens, will be lost. C++ state should be fine, assuming 59 | the C++ code can handle the JS/QML being recreated. 60 | -------------------------------------------------------------------------------- /src/dqml/dqmllocalserver.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2014, Gunnar Sletta 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | #ifndef DQMLLOCALSERVER_H 28 | #define DQMLLOCALSERVER_H 29 | 30 | #include 31 | #include 32 | #include 33 | 34 | QT_BEGIN_NAMESPACE 35 | 36 | class DQML_EXPORT DQmlLocalServer : public DQmlServer 37 | { 38 | Q_OBJECT 39 | public: 40 | DQmlLocalServer(QQmlEngine *engine, QQuickView *view, const QString &file); 41 | 42 | DQmlFileTracker *fileTracker() { return &m_tracker; } 43 | 44 | private: 45 | DQmlFileTracker m_tracker; 46 | 47 | }; 48 | 49 | QT_END_NAMESPACE 50 | 51 | #endif // DQMLLOCALSERVER_H 52 | -------------------------------------------------------------------------------- /src/dqml/dqmlserver.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2014, Gunnar Sletta 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | #ifndef DQMLSERVER_H 28 | #define DQMLSERVER_H 29 | 30 | #include 31 | 32 | #include 33 | 34 | #include 35 | 36 | QT_BEGIN_NAMESPACE 37 | 38 | class QTcpSocket; 39 | class QTcpServer; 40 | class QQmlEngine; 41 | class QQuickView; 42 | 43 | class DQML_EXPORT DQmlServer : public QObject 44 | { 45 | Q_OBJECT 46 | public: 47 | DQmlServer(QQmlEngine *engine, QQuickView *view, const QString &file); 48 | 49 | void setCreateViewIfNeeded(bool createView) { m_createViewIfNeeded = createView; } 50 | bool createsViewIfNeeded() const { return m_createViewIfNeeded; } 51 | 52 | void addTrackerMapping(const QString &id, const QString &path) { m_trackerMapping.insert(id, path); } 53 | 54 | public Q_SLOTS: 55 | void listen(quint16 port); 56 | void reloadQml(); 57 | 58 | private Q_SLOTS: 59 | void newConnection(); 60 | void acceptError(QAbstractSocket::SocketError error); 61 | 62 | void read(); 63 | 64 | private: 65 | QString m_file; 66 | 67 | QQmlEngine *m_engine; 68 | QQuickView *m_view; 69 | QObject *m_contentItem; 70 | 71 | bool m_createViewIfNeeded; 72 | bool m_ownsView; 73 | bool m_pendingReload; 74 | 75 | QTcpServer *m_tcpServer; 76 | QTcpSocket *m_clientSocket; 77 | 78 | QHash m_trackerMapping; 79 | }; 80 | 81 | QT_END_NAMESPACE 82 | 83 | #endif // DQMLSERVER_H 84 | -------------------------------------------------------------------------------- /src/dqml/dqmlfiletracker.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2014, Gunnar Sletta 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | #ifndef DQMLFILETRACKER_H 28 | #define DQMLFILETRACKER_H 29 | 30 | #include 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | QT_BEGIN_NAMESPACE 39 | 40 | class DQML_EXPORT DQmlFileTracker : public QObject 41 | { 42 | Q_OBJECT 43 | public: 44 | struct Entry { 45 | QString path; 46 | QHash content; 47 | }; 48 | 49 | explicit DQmlFileTracker(QObject *parent = Q_NULLPTR); 50 | 51 | QHash trackingSet() const; 52 | 53 | bool track(const QString &id, const QString &path); 54 | bool untrack(const QString &id); 55 | 56 | Q_SIGNALS: 57 | void fileChanged(const QString &id, const QString &path, const QString &fileName); 58 | void fileAdded(const QString &id, const QString &path, const QString &fileName); 59 | void fileRemoved(const QString &id, const QString &path, const QString &fileName); 60 | 61 | private Q_SLOTS: 62 | void onDirChange(const QString &); 63 | void onFileChange(const QString &); 64 | 65 | private: 66 | Entry createEntry(const QFileInfo &info); 67 | QString idFromPath(const QString &path) const; 68 | 69 | QHash m_set; 70 | QSet m_suffixes; 71 | 72 | QFileSystemWatcher m_watcher; 73 | }; 74 | 75 | QT_END_NAMESPACE 76 | 77 | #endif // DQMLFILETRACKER_H 78 | -------------------------------------------------------------------------------- /src/dqml/dqmlmonitor.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2014, Gunnar Sletta 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | #ifndef DQMLMONITOR_H 28 | #define DQMLMONITOR_H 29 | 30 | #include 31 | 32 | #include 33 | 34 | #include 35 | 36 | QT_BEGIN_NAMESPACE 37 | 38 | class DQmlFileTracker; 39 | 40 | class QTcpSocket; 41 | 42 | class DQML_EXPORT DQmlMonitor: public QObject 43 | { 44 | Q_OBJECT 45 | public: 46 | DQmlMonitor(); 47 | ~DQmlMonitor(); 48 | 49 | DQmlFileTracker *fileTracker() { return m_tracker; } 50 | void setSyncAllFilesWhenConnected(bool sync) { m_syncAll = sync; } 51 | 52 | public Q_SLOTS: 53 | void connectToServer(const QString &host, quint16 port); 54 | void syncAllFiles(); 55 | 56 | private Q_SLOTS: 57 | void socketConnected(); 58 | void socketDisconnected(); 59 | void socketError(QAbstractSocket::SocketError error); 60 | 61 | void fileWasChanged(const QString &id, const QString &path, const QString &file); 62 | void fileWasAdded(const QString &id, const QString &path, const QString &file); 63 | void fileWasRemoved(const QString &id, const QString &path, const QString &file); 64 | 65 | protected: 66 | void timerEvent(QTimerEvent *e); 67 | 68 | private: 69 | enum EventType { ChangeEvent = 1, AddEvent = 2, RemoveEvent = 3 }; 70 | void writeEvent(EventType type, const QString &id, const QString &path, const QString &file); 71 | void maybeNoSocketSoTryLater(); 72 | 73 | DQmlFileTracker *m_tracker; 74 | 75 | QTcpSocket *m_socket; 76 | QString m_host; 77 | quint16 m_port; 78 | bool m_connected; 79 | int m_connectTimer; 80 | 81 | bool m_syncAll; 82 | }; 83 | 84 | QT_END_NAMESPACE 85 | 86 | #endif // DQMLMONITOR_H 87 | -------------------------------------------------------------------------------- /src/dqml/dqmlmonitor.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2014, Gunnar Sletta 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | #include "dqmlmonitor.h" 28 | #include "dqmlfiletracker.h" 29 | 30 | #include 31 | #include 32 | 33 | #include 34 | #include 35 | 36 | DQmlMonitor::DQmlMonitor() 37 | : m_socket(0) 38 | , m_port(0) 39 | , m_connected(false) 40 | , m_connectTimer(0) 41 | , m_syncAll(false) 42 | { 43 | m_tracker = new DQmlFileTracker(this); 44 | connect(m_tracker, SIGNAL(fileAdded(QString,QString,QString)), this, SLOT(fileWasAdded(QString,QString,QString))); 45 | connect(m_tracker, SIGNAL(fileRemoved(QString,QString,QString)), this, SLOT(fileWasRemoved(QString,QString,QString))); 46 | connect(m_tracker, SIGNAL(fileChanged(QString,QString,QString)), this, SLOT(fileWasChanged(QString,QString,QString))); 47 | } 48 | 49 | DQmlMonitor::~DQmlMonitor() 50 | { 51 | if (m_socket) { 52 | m_socket->close(); 53 | delete m_socket; 54 | } 55 | } 56 | 57 | void DQmlMonitor::connectToServer(const QString &host, quint16 port) 58 | { 59 | if (m_socket) 60 | delete m_socket; 61 | 62 | m_host = host; 63 | m_port = port; 64 | 65 | qCDebug(DQML_LOG) << "Connecting to: " << host << ":" << port; 66 | m_socket = new QTcpSocket(); 67 | 68 | connect(m_socket, SIGNAL(connected()), this, SLOT(socketConnected())); 69 | connect(m_socket, SIGNAL(disconnected()), this, SLOT(socketDisconnected())); 70 | connect(m_socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(socketError(QAbstractSocket::SocketError))); 71 | 72 | m_connected = false; 73 | m_socket->connectToHost(QHostAddress(host), port, QTcpSocket::WriteOnly); 74 | } 75 | 76 | QByteArray fileContent(const QString &path) 77 | { 78 | QFile file(path); 79 | if (!file.open(QFile::ReadOnly)) { 80 | qWarning() << "failed to read file" << path; 81 | return QByteArray(); 82 | } 83 | return file.readAll(); 84 | } 85 | 86 | void DQmlMonitor::writeEvent(EventType type, const QString &id, const QString &path, const QString &file) 87 | { 88 | // If we're not supposed to be connected, don't try to write.. 89 | if (!m_socket) 90 | return; 91 | 92 | if (m_connected) { 93 | { 94 | QDataStream stream(m_socket); 95 | stream << type << id << file; 96 | if (type != RemoveEvent) { 97 | QByteArray content = fileContent(path + QStringLiteral("/") + file); 98 | stream << content.size(); 99 | stream.writeRawData(content.constData(), content.size()); 100 | } 101 | } 102 | m_socket->flush(); 103 | 104 | qCDebug(DQML_LOG) << " -> event written to server"; 105 | 106 | } else { 107 | qCDebug(DQML_LOG) << "monitored a change while disconnected, will be ignored..." << type << id << path << file; 108 | } 109 | } 110 | 111 | void DQmlMonitor::fileWasChanged(const QString &id, const QString &path, const QString &file) 112 | { 113 | writeEvent(ChangeEvent, id, path, file); 114 | } 115 | 116 | void DQmlMonitor::fileWasAdded(const QString &id, const QString &path, const QString &file) 117 | { 118 | writeEvent(AddEvent, id, path, file); 119 | } 120 | 121 | void DQmlMonitor::fileWasRemoved(const QString &id, const QString &path, const QString &file) 122 | { 123 | writeEvent(RemoveEvent, id, path, file); 124 | } 125 | 126 | 127 | void DQmlMonitor::socketConnected() 128 | { 129 | qCDebug(DQML_LOG) << "connected!"; 130 | m_connected = true; 131 | if (m_connectTimer != 0) { 132 | killTimer(m_connectTimer); 133 | m_connectTimer = 0; 134 | } 135 | if (m_syncAll) 136 | syncAllFiles(); 137 | } 138 | 139 | void DQmlMonitor::syncAllFiles() 140 | { 141 | QHash all = m_tracker->trackingSet(); 142 | for (QHash::const_iterator it = all.constBegin(); 143 | it != m_tracker->trackingSet().constEnd(); ++it) { 144 | const DQmlFileTracker::Entry &e = it.value(); 145 | foreach (const QString &file, e.content.keys()) 146 | fileWasAdded(it.key(), it.value().path, file); 147 | } 148 | } 149 | 150 | void DQmlMonitor::socketDisconnected() 151 | { 152 | qCDebug(DQML_LOG) << "disconnected..."; 153 | maybeNoSocketSoTryLater(); 154 | } 155 | 156 | void DQmlMonitor::socketError(QAbstractSocket::SocketError error) 157 | { 158 | qCDebug(DQML_LOG) << "connection error, code:" << hex << error; 159 | maybeNoSocketSoTryLater(); 160 | } 161 | 162 | void DQmlMonitor::maybeNoSocketSoTryLater() 163 | { 164 | qCDebug(DQML_LOG) << " - socket is in state" << m_socket->state(); 165 | if (m_socket->state() == QAbstractSocket::UnconnectedState 166 | || m_socket->state() == QAbstractSocket::ClosingState) { 167 | m_connected = false; 168 | if (m_connectTimer == 0) { 169 | qCDebug(DQML_LOG) << " -> starting reconnect timer.."; 170 | m_connectTimer = startTimer(10000); 171 | } 172 | } 173 | } 174 | 175 | void DQmlMonitor::timerEvent(QTimerEvent *e) 176 | { 177 | if (e->timerId() == m_connectTimer) 178 | connectToServer(m_host, m_port); 179 | } 180 | 181 | -------------------------------------------------------------------------------- /src/dqml/dqmlfiletracker.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2014, Gunnar Sletta 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | #include "dqmlfiletracker.h" 28 | 29 | #include 30 | #include 31 | #include 32 | 33 | DQmlFileTracker::DQmlFileTracker(QObject *parent) : 34 | QObject(parent) 35 | { 36 | connect(&m_watcher, SIGNAL(directoryChanged(QString)), this, SLOT(onDirChange(QString))); 37 | connect(&m_watcher, SIGNAL(fileChanged(QString)), this, SLOT(onFileChange(QString))); 38 | 39 | m_suffixes << QStringLiteral("qml"); 40 | m_suffixes << QStringLiteral("js"); 41 | m_suffixes << QStringLiteral("png"); 42 | m_suffixes << QStringLiteral("jpg"); 43 | m_suffixes << QStringLiteral("jpeg"); 44 | m_suffixes << QStringLiteral("gif"); 45 | } 46 | 47 | bool DQmlFileTracker::track(const QString &id, const QString &path) 48 | { 49 | QFileInfo i(path); 50 | if (!i.exists()) { 51 | qCDebug(DQML_LOG) << "path does not exist" << path; 52 | return false; 53 | } 54 | if (!i.isDir()) { 55 | qCDebug(DQML_LOG) << "path is not a directory" << path; 56 | return false; 57 | } 58 | QString cp = i.canonicalFilePath(); 59 | qCDebug(DQML_LOG) << "tracking" << id << cp; 60 | m_set[id] = createEntry(i); 61 | m_watcher.addPath(cp); 62 | return true; 63 | } 64 | 65 | bool DQmlFileTracker::untrack(const QString &id) 66 | { 67 | qCDebug(DQML_LOG) << "untracking" << id; 68 | if (m_set.contains(id)) { 69 | QString path = m_set.take(id).path; 70 | m_watcher.removePath(path); 71 | return true; 72 | } 73 | qCWarning(DQML_LOG) << "unknown id"; 74 | return false; 75 | } 76 | 77 | QHash DQmlFileTracker::trackingSet() const 78 | { 79 | return m_set; 80 | } 81 | 82 | QString DQmlFileTracker::idFromPath(const QString &path) const 83 | { 84 | for (QHash::const_iterator it = m_set.constBegin(); 85 | it != m_set.constEnd(); ++it) { 86 | if (it.value().path == path) 87 | return it.key(); 88 | } 89 | return QString(); 90 | } 91 | 92 | void DQmlFileTracker::onDirChange(const QString &path) 93 | { 94 | qCDebug(DQML_LOG) << "change in directory" << path; 95 | QString id = idFromPath(path); 96 | if (id.isEmpty()) { 97 | untrack(id); 98 | qCDebug(DQML_LOG) << " - no entry, cancel tracking..."; 99 | return; 100 | } 101 | 102 | Entry &entry = m_set[id]; 103 | QDir dir(path); 104 | QDirIterator iterator(dir); 105 | QHash currentContent; 106 | while (iterator.hasNext()) { 107 | iterator.next(); 108 | QFileInfo i = iterator.fileInfo(); 109 | if (i.isFile() && m_suffixes.contains(i.suffix().toLower())) { 110 | QString name = i.fileName(); 111 | quint64 time = i.lastModified().toMSecsSinceEpoch(); 112 | currentContent[name] = time; 113 | } 114 | } 115 | 116 | QSet allPaths = QSet::fromList(currentContent.keys()).unite(QSet::fromList(entry.content.keys())); 117 | foreach (QString p, allPaths) { 118 | bool was = entry.content.contains(p); 119 | bool is = currentContent.contains(p); 120 | if (was && is) { 121 | // If file was there before and is still there, check match the last modified 122 | // timestamp and emit fileChange if it has been modified.. 123 | if (currentContent.value(p) > entry.content.value(p)) { 124 | qCDebug(DQML_LOG) << " - changed:" << id << p; 125 | emit fileChanged(id, path, p); 126 | } 127 | } else if (is) { 128 | // File is there now, but wasn't before -> added.. 129 | qCDebug(DQML_LOG) << " - added:" << id << p; 130 | #ifdef Q_OS_LINUX 131 | m_watcher.addPath(QFileInfo(path + QStringLiteral("/") + p).canonicalFilePath()); 132 | #endif 133 | emit fileAdded(id, path, p); 134 | } else if (was) { 135 | // file was there, but isn't anymore -> removed 136 | qCDebug(DQML_LOG) << " - removed:" << id << p; 137 | #ifdef Q_OS_LINUX 138 | // Need to use absoluteFilePath here as the file is gone and 139 | // canonicalFilePath() will return a null string. 140 | m_watcher.removePath(QFileInfo(path + QStringLiteral("/") + p).absoluteFilePath()); 141 | #endif 142 | emit fileRemoved(id, path, p); 143 | } 144 | } 145 | 146 | // use the new content set from now on.. 147 | entry.content = currentContent; 148 | } 149 | 150 | void DQmlFileTracker::onFileChange(const QString &path) 151 | { 152 | QFileInfo info(path); 153 | onDirChange(info.canonicalPath()); 154 | } 155 | 156 | DQmlFileTracker::Entry DQmlFileTracker::createEntry(const QFileInfo &info) 157 | { 158 | Entry e; 159 | e.path = info.canonicalFilePath(); 160 | Q_ASSERT(info.isDir()); 161 | QDir dir(e.path); 162 | QDirIterator iterator(dir); 163 | while (iterator.hasNext()) { 164 | iterator.next(); 165 | QFileInfo i = iterator.fileInfo(); 166 | if (i.isFile() && m_suffixes.contains(i.suffix().toLower())) { 167 | QString name = i.fileName(); 168 | quint64 time = i.lastModified().toMSecsSinceEpoch(); 169 | qCDebug(DQML_LOG) << " - tracking file" << name << time; 170 | e.content[name] = time; 171 | #ifdef Q_OS_LINUX 172 | m_watcher.addPath(i.canonicalFilePath()); 173 | #endif 174 | } else { 175 | qCDebug(DQML_LOG) << " - ignoring" << i.fileName(); 176 | } 177 | } 178 | return e; 179 | } 180 | -------------------------------------------------------------------------------- /src/dqml/dqmlserver.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2014, Gunnar Sletta 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | #include "dqmlserver.h" 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #include 35 | #include 36 | 37 | #include 38 | #include 39 | 40 | DQmlServer::DQmlServer(QQmlEngine *engine, QQuickView *view, const QString &file) 41 | : m_file(file) 42 | , m_engine(engine) 43 | , m_view(view) 44 | , m_contentItem(0) 45 | , m_createViewIfNeeded(false) 46 | , m_ownsView(false) 47 | , m_pendingReload(false) 48 | , m_tcpServer(0) 49 | , m_clientSocket(0) 50 | { 51 | } 52 | 53 | void DQmlServer::listen(quint16 port) 54 | { 55 | if (m_tcpServer || m_clientSocket) { 56 | qCDebug(DQML_LOG) << "asked to listen on port" << port << "when already connected..."; 57 | return; 58 | } 59 | m_tcpServer = new QTcpServer(); 60 | if (m_tcpServer->listen(QHostAddress::Any, port)) { 61 | qCDebug(DQML_LOG) << "server listening on port" << port; 62 | } else { 63 | qCDebug(DQML_LOG) << "Server failed to listen.." << m_tcpServer->errorString(); 64 | } 65 | 66 | connect(m_tcpServer, SIGNAL(newConnection()), this, SLOT(newConnection())); 67 | connect(m_tcpServer, SIGNAL(acceptError(QAbstractSocket::SocketError)), this, SLOT(acceptError(QAbstractSocket::SocketError))); 68 | } 69 | 70 | void DQmlServer::newConnection() 71 | { 72 | if (m_clientSocket) { 73 | qCDebug(DQML_LOG) << " -> already got a connection, ignoring..."; 74 | return; 75 | } 76 | m_clientSocket = m_tcpServer->nextPendingConnection(); 77 | qCDebug(DQML_LOG) << "connecting to client" << m_clientSocket->peerAddress(); 78 | connect(m_clientSocket, SIGNAL(readyRead()), this, SLOT(read())); 79 | } 80 | 81 | void DQmlServer::acceptError(QAbstractSocket::SocketError error) 82 | { 83 | qDebug() << "Network error:" << error; 84 | } 85 | 86 | void DQmlServer::read() 87 | { 88 | QDataStream stream(m_clientSocket); 89 | QString id, file, content; 90 | int type; 91 | 92 | char *data = 0; 93 | int dataLength = 0; 94 | 95 | stream >> type >> id >> file; 96 | 97 | if (type == 1 || type == 2) { 98 | stream >> dataLength; 99 | data = (char *) malloc(dataLength); 100 | 101 | char *d = data; 102 | 103 | int bytesLeft = dataLength; 104 | while (bytesLeft > 0) { 105 | char chunk[1024]; 106 | int actual = stream.readRawData(chunk, qMin(bytesLeft, sizeof(chunk))); 107 | memcpy(d, chunk, actual); 108 | bytesLeft -= actual; 109 | d += actual; 110 | } 111 | } 112 | 113 | if (!m_trackerMapping.contains(id)) { 114 | qCDebug(DQML_LOG) << " -> got data for unknown id, aborting" << id; 115 | qCDebug(DQML_LOG) << " --->" << m_trackerMapping.keys(); 116 | return; 117 | } 118 | QString fileName = m_trackerMapping.value(id) + QStringLiteral("/") + file; 119 | 120 | if (type == 1 || type == 2) { 121 | QFile f(fileName); 122 | if (!f.open(QFile::WriteOnly)) { 123 | qCDebug(DQML_LOG) << " -> failed to write" << QFileInfo(f).absoluteFilePath() << f.errorString(); 124 | return; 125 | } 126 | f.write(data, dataLength); 127 | qCDebug(DQML_LOG) << " -> updated" << id << ":" << file; 128 | } else if (type == 3) { 129 | QFile f(fileName); 130 | bool removed = f.remove(); 131 | if (removed) 132 | qCDebug(DQML_LOG) << " -> removed" << id << ":" << file; 133 | else 134 | qCDebug(DQML_LOG) << " -> failed to remove" << id << ":" << file; 135 | } 136 | 137 | // More commands in the queue, invoke ourselves again.. 138 | if (!m_clientSocket->atEnd()) 139 | QMetaObject::invokeMethod(this, "read", Qt::QueuedConnection); 140 | else if (!m_pendingReload) { 141 | QMetaObject::invokeMethod(this, "reloadQml", Qt::QueuedConnection); 142 | m_pendingReload = true; 143 | } 144 | } 145 | 146 | void DQmlServer::reloadQml() 147 | { 148 | m_pendingReload = false; 149 | qCDebug(DQML_LOG) << "reloading..."; 150 | delete m_contentItem; 151 | m_contentItem = 0; 152 | m_engine->clearComponentCache(); 153 | 154 | QQmlComponent *component = new QQmlComponent(m_engine); 155 | QUrl fileUrl = QUrl::fromLocalFile(m_file); 156 | component->loadUrl(fileUrl); 157 | qCDebug(DQML_LOG) << "loaded url.."; 158 | 159 | if (!component->isReady()) { 160 | qWarning() << component->errorString(); 161 | return; 162 | } 163 | 164 | QPoint winPos(-1, -1); 165 | QSize winSize(-1, -1); 166 | 167 | if (m_view) { 168 | winPos = m_view->position(); 169 | winSize = m_view->size(); 170 | } 171 | 172 | m_contentItem = component->create(); 173 | qCDebug(DQML_LOG) << "created" << m_contentItem; 174 | if (qobject_cast(m_contentItem)) { 175 | if (m_view && m_ownsView) { 176 | delete m_view; 177 | m_view = 0; 178 | m_ownsView = false; 179 | } 180 | } else { 181 | QQuickItem *item = qobject_cast(m_contentItem); 182 | if (item && item->width() > 0 && item->height() > 0 && winSize.width() < 0 && winSize.height() < 0) 183 | winSize = QSize(item->width(), item->height()); 184 | if (!m_view && m_createViewIfNeeded) { 185 | m_view = new QQuickView(); 186 | m_view->setResizeMode(QQuickView::SizeRootObjectToView); 187 | m_ownsView = true; 188 | m_view->show(); 189 | qCDebug(DQML_LOG) << "created a view to hold the QML"; 190 | } 191 | if (m_view) { 192 | m_view->setContent(fileUrl, component, m_contentItem); 193 | if (winPos.x() >= 0 && winPos.y() >= 0) 194 | m_view->setPosition(winPos); 195 | if (winSize.width() > 0 && winSize.height() > 0) 196 | m_view->resize(winSize); 197 | m_view->show(); 198 | } 199 | else 200 | qCDebug(DQML_LOG) << "no view to show qml, set 'setCreatesViewIfNeeded(true)' or supply one."; 201 | } 202 | } 203 | 204 | 205 | 206 | -------------------------------------------------------------------------------- /tools/dqmltool/dqmlmain.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2014, Gunnar Sletta 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | #include 28 | 29 | #include 30 | #include 31 | 32 | #include 33 | #include 34 | 35 | #include 36 | #include 37 | 38 | #include 39 | #include 40 | 41 | #include 42 | #include 43 | #include 44 | #include 45 | 46 | void printHelp() 47 | { 48 | printf("Usage: \n" 49 | " > dqml file.qml (same as --local)\n" 50 | " > dqml --local [--track path] file.qml\n" 51 | " > dqml --server port [--track id path] file.qml\n" 52 | " > dqml --monitor addr port [--track id path] [--sync]\n" 53 | "\n" 54 | "Application modes:\n" 55 | " --local The application runs locally and functions like qmlscene, except\n" 56 | " that it will monitor the directory where 'file.qml' is located\n" 57 | " and all changes in this directory will result in the QML being\n" 58 | " re-evaluated. \n" 59 | "\n" 60 | " --monitor The application runs as a non-gui application, monitoring requested\n" 61 | " files. The --monitor mode is followed by the address and port to the\n" 62 | " server.\n" 63 | "\n" 64 | " --server The application runs in server mode with 'file.qml' as the main qml\n" 65 | " file. The --server mode is followed by the port to accept connections\n" 66 | " on.\n" 67 | "\n" 68 | "Options:\n" 69 | " --track id path The application will track the given path and name it 'id'.\n" 70 | " In server/monitor mode the path is used to map paths between\n" 71 | " monitor and server. The tracking is not recursive, but\n" 72 | " multiple --track arguments can be specified. When no\n" 73 | " arguments are specified, the current directory is tracked\n" 74 | " --sync Sync all files from the monitor to the server when connected.\n" 75 | " Useful to keep files in sync." 76 | "\n" 77 | ); 78 | } 79 | 80 | 81 | 82 | int main(int argc, char **argv) 83 | { 84 | QGuiApplication app(argc, argv); 85 | 86 | enum Mode { 87 | Monitor_Mode, 88 | Server_Mode, 89 | Local_Mode, 90 | } mode = Local_Mode; 91 | 92 | QList > tracking; 93 | QString file; 94 | int port = -1; 95 | QString host; 96 | bool sync = false; 97 | 98 | QStringList args = app.arguments(); 99 | for (int i=1; i(QString::number(tracking.size()), 149 | args.at(i+1)); 150 | i += 1; 151 | } else { 152 | if (args.size() < i + 2) { 153 | qDebug() << "Malformed --track command: requires an 'id' and a 'path'"; 154 | return 1; 155 | } 156 | // Just given them all a unique id, it isn't so it doesn't matter... 157 | tracking << QPair(args.at(i+1), args.at(i+2)); 158 | i += 2; 159 | } 160 | 161 | } else if (a == QStringLiteral("-h") || a == QStringLiteral("--help")) { 162 | printHelp(); 163 | return 0; 164 | 165 | } else if (i == args.size() - 1) { 166 | file = a; 167 | } 168 | } 169 | 170 | QScopedPointer server; 171 | QScopedPointer monitor; 172 | QScopedPointer localServer; 173 | QScopedPointer engine; 174 | DQmlFileTracker *tracker = 0; 175 | 176 | if (mode == Local_Mode) { 177 | if (file.isEmpty()) { 178 | printHelp(); 179 | return 1; 180 | } 181 | 182 | QGuiApplication::setQuitOnLastWindowClosed(false); 183 | 184 | qDebug() << "running in local mode with" << file; 185 | engine.reset(new QQmlEngine()); 186 | localServer.reset(new DQmlLocalServer(engine.data(), 0, file)); 187 | localServer->setCreateViewIfNeeded(true); 188 | localServer->reloadQml(); 189 | tracker = localServer->fileTracker(); 190 | 191 | } else if (mode == Monitor_Mode) { 192 | qDebug() << "running monitor mode"; 193 | monitor.reset(new DQmlMonitor()); 194 | tracker = monitor->fileTracker(); 195 | monitor->setSyncAllFilesWhenConnected(sync); 196 | monitor->connectToServer(host, port); 197 | 198 | } else if (mode == Server_Mode) { 199 | qDebug() << "running server mode with" << file; 200 | engine.reset(new QQmlEngine()); 201 | server.reset(new DQmlServer(engine.data(), 0, file)); 202 | server->setCreateViewIfNeeded(true); 203 | server->reloadQml(); 204 | server->listen(port); 205 | } 206 | 207 | QString current = QStringLiteral("."); 208 | if (mode == Local_Mode || mode == Server_Mode) 209 | current = QFileInfo(file).canonicalPath(); 210 | 211 | if (mode == Local_Mode || mode == Monitor_Mode) { 212 | Q_ASSERT(tracker); 213 | if (tracking.size() == 0) { 214 | tracker->track(QStringLiteral("current-directory"), current); 215 | } else { 216 | for (int i=0; i &pair = tracking.at(i); 218 | tracker->track(pair.first, pair.second); 219 | } 220 | } 221 | 222 | } else { // Server_Mode 223 | if (tracking.size() == 0) { 224 | server->addTrackerMapping(QStringLiteral("current-directory"), current); 225 | } else { 226 | for (int i=0; i &pair = tracking.at(i); 228 | server->addTrackerMapping(pair.first, pair.second); 229 | } 230 | } 231 | } 232 | 233 | return app.exec(); 234 | } 235 | --------------------------------------------------------------------------------