nodeinstanceserverproxy.cpp 30 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
2
**
3
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
hjk's avatar
hjk committed
4
** Contact: http://www.qt-project.org/legal
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
7
**
hjk's avatar
hjk committed
8 9 10 11 12 13 14
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
15 16
**
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17 18 19 20 21 22 23 24 25
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
26 27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
28
****************************************************************************/
29

30 31 32 33 34 35 36
#include "nodeinstanceserverproxy.h"

#include <QLocalServer>
#include <QLocalSocket>
#include <QProcess>
#include <QCoreApplication>
#include <QUuid>
37
#include <QFileInfo>
38
#include <QDir>
39
#include <QTimer>
40
#include <QTextStream>
41
#include <QMessageBox>
42 43 44 45 46

#include "createinstancescommand.h"
#include "createscenecommand.h"
#include "changevaluescommand.h"
#include "changebindingscommand.h"
47
#include "changeauxiliarycommand.h"
48 49 50 51 52 53 54
#include "changefileurlcommand.h"
#include "removeinstancescommand.h"
#include "clearscenecommand.h"
#include "removepropertiescommand.h"
#include "reparentinstancescommand.h"
#include "changeidscommand.h"
#include "changestatecommand.h"
55
#include "completecomponentcommand.h"
56
#include "changenodesourcecommand.h"
57 58 59 60

#include "informationchangedcommand.h"
#include "pixmapchangedcommand.h"
#include "valueschangedcommand.h"
61
#include "childrenchangedcommand.h"
62
#include "statepreviewimagechangedcommand.h"
63
#include "componentcompletedcommand.h"
64
#include "tokencommand.h"
65
#include "removesharedmemorycommand.h"
66
#include "endpuppetcommand.h"
67
#include "synchronizecommand.h"
Marco Bubke's avatar
Marco Bubke committed
68
#include "debugoutputcommand.h"
69

70
#include "nodeinstanceview.h"
71

72 73
#include "puppetdialog.h"

74
#include "import.h"
75

76 77
#include "qmldesignerplugin.h"

78
#include <coreplugin/icore.h>
79
#include <utils/hostosinfo.h>
80 81 82 83
#include <projectexplorer/kit.h>
#include <qtsupport/qtkitinformation.h>
#include <qtsupport/baseqtversion.h>
#include <qtsupport/qtsupportconstants.h>
84

85 86
#include <QMessageBox>

87
namespace {
88
#ifdef Q_OS_MAC
89
#  define SHARE_PATH "/../Resources"
90
#else
91
#  define SHARE_PATH "/../share/qtcreator"
92 93 94 95 96 97 98 99 100 101 102 103 104 105
#endif

static QString applicationDirPath()
{
    return QCoreApplication::applicationDirPath();
}

static inline QString sharedDirPath()
{
    QString appPath = applicationDirPath();

    return QFileInfo(appPath + SHARE_PATH).absoluteFilePath();
}

106 107
static QLatin1String qmlPuppetApplicationDirectoryForTests()
{
108 109 110
    if (Utils::HostOsInfo::isWindowsHost())
        //one more - debug/release dir
        return QLatin1String("/../../../../../../bin/");
111 112 113
    return QLatin1String("/../../../../../bin/");
}
} //namespace
114 115 116

namespace QmlDesigner {

117
static bool hasQtQuick2(NodeInstanceView *nodeInstanceView)
118 119 120
{
    if (nodeInstanceView && nodeInstanceView->model()) {
        foreach (const Import &import ,nodeInstanceView->model()->imports()) {
121
            if (import.url() ==  "QtQuick" && import.version().toDouble() >= 2.0)
122 123 124 125 126 127 128
                return true;
        }
    }

    return false;
}

129 130 131 132 133 134 135 136 137 138 139 140
static bool hasQtQuick1(NodeInstanceView *nodeInstanceView)
{
    if (nodeInstanceView && nodeInstanceView->model()) {
        foreach (const Import &import ,nodeInstanceView->model()->imports()) {
            if (import.url() ==  "QtQuick" && import.version().toDouble() < 2.0)
                return true;
        }
    }

    return false;
}

141 142 143 144 145 146 147 148 149
QString NodeInstanceServerProxy::creatorQmlPuppetPath()
{
    QString applicationPath =  QCoreApplication::applicationDirPath();
    applicationPath = macOSBundlePath(applicationPath);
    applicationPath += QLatin1Char('/') + qmlPuppetApplicationName();

    return applicationPath;
}

150 151 152 153 154 155 156 157 158 159 160
bool NodeInstanceServerProxy::checkPuppetVersion(const QString &qmlPuppetPath)
{
    QProcess qmlPuppetVersionProcess;
    qmlPuppetVersionProcess.start(qmlPuppetPath, QStringList() << "--version");
    qmlPuppetVersionProcess.waitForReadyRead(6000);

    QByteArray versionString = qmlPuppetVersionProcess.readAll();

    bool canConvert;
    unsigned int versionNumber = versionString.toUInt(&canConvert);

161
    return canConvert && versionNumber == 2;
162 163
}

164 165 166 167 168 169 170 171 172 173 174 175
static QString getPathToQt(ProjectExplorer::Kit *kit)
{
    QtSupport::BaseQtVersion *currentQtVersion = QtSupport::QtKitInformation::qtVersion(kit);
    if (currentQtVersion && (currentQtVersion->qtVersion() >= QtSupport::QtVersionNumber(4, 7, 1))
            && (currentQtVersion->type() == QLatin1String(QtSupport::Constants::DESKTOPQT)
                || currentQtVersion->type() == QLatin1String(QtSupport::Constants::SIMULATORQT)))
        return currentQtVersion->qmakeProperty("QT_INSTALL_DATA");

    return QString();
}

NodeInstanceServerProxy::NodeInstanceServerProxy(NodeInstanceView *nodeInstanceView, RunModus runModus, ProjectExplorer::Kit *kit)
176 177 178
    : NodeInstanceServerInterface(nodeInstanceView),
      m_localServer(new QLocalServer(this)),
      m_nodeInstanceView(nodeInstanceView),
179
      m_firstBlockSize(0),
180
      m_secondBlockSize(0),
181
      m_thirdBlockSize(0),
182 183 184 185
      m_writeCommandCounter(0),
      m_firstLastReadCommandCounter(0),
      m_secondLastReadCommandCounter(0),
      m_thirdLastReadCommandCounter(0),
186 187
      m_runModus(runModus),
      m_synchronizeId(-1)
188
{
189 190
   QString pathToQt = getPathToQt(kit);

191 192
   QString applicationPath =  pathToQt + QLatin1String("/bin");
   if (runModus == TestModus) {
193
       applicationPath = QCoreApplication::applicationDirPath()
194 195
           + qmlPuppetApplicationDirectoryForTests()
           + qmlPuppetApplicationName();
196
   } else {
197
       applicationPath = macOSBundlePath(applicationPath);
Robert Loehning's avatar
Robert Loehning committed
198
       applicationPath += QLatin1Char('/') + qmlPuppetApplicationName();
199 200 201


#if defined(QT_NO_DEBUG) || defined(SEARCH_PUPPET_IN_CREATOR_BINPATH) // to prevent of choosing the wrong puppet in debug
202 203
       if (!QFileInfo(applicationPath).exists()) { //No qmlpuppet in Qt
           //We have to find out how to give not too intrusive feedback
204
           applicationPath = creatorQmlPuppetPath();
205
       }
206
#endif
207
   }
208

209

210
   QByteArray envImportPath = qgetenv("QTCREATOR_QMLPUPPET_PATH");
211
   if (!envImportPath.isEmpty())
212 213
       applicationPath = envImportPath;

214
   QProcessEnvironment environment = QProcessEnvironment::systemEnvironment();
215

216
#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0)) && (defined(Q_OS_MAC) || defined(Q_OS_LINUX))
Laurent Montel's avatar
Laurent Montel committed
217
   environment.insert(QLatin1String("DESIGNER_DONT_USE_SHARED_MEMORY"), QLatin1String("1"));
218 219
#endif

220
   if (QFileInfo(applicationPath).exists()) {
221
       if (checkPuppetVersion(applicationPath)) {
222 223 224 225
           QString socketToken(QUuid::createUuid().toString());
           m_localServer->listen(socketToken);
           m_localServer->setMaxPendingConnections(3);

226 227 228 229 230 231
           m_qmlPuppetEditorProcess = new QProcess;
           m_qmlPuppetEditorProcess->setProcessEnvironment(environment);
           m_qmlPuppetEditorProcess->setObjectName("EditorProcess");
           connect(m_qmlPuppetEditorProcess.data(), SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(processFinished(int,QProcess::ExitStatus)));
           connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()), m_qmlPuppetEditorProcess.data(), SLOT(kill()));
           bool fowardQmlpuppetOutput = !qgetenv("FORWARD_QMLPUPPET_OUTPUT").isEmpty();
232
           if (fowardQmlpuppetOutput) {
233 234
               m_qmlPuppetEditorProcess->setProcessChannelMode(QProcess::MergedChannels);
               connect(m_qmlPuppetEditorProcess.data(), SIGNAL(readyRead()), this, SLOT(printEditorProcessOutput()));
235
           }
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259
           m_qmlPuppetEditorProcess->start(applicationPath, QStringList() << socketToken << "editormode" << "-graphicssystem raster");

           if (runModus == NormalModus) {
               m_qmlPuppetPreviewProcess = new QProcess;
               m_qmlPuppetPreviewProcess->setProcessEnvironment(environment);
               m_qmlPuppetPreviewProcess->setObjectName("PreviewProcess");
               connect(m_qmlPuppetPreviewProcess.data(), SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(processFinished(int,QProcess::ExitStatus)));
               connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()), m_qmlPuppetPreviewProcess.data(), SLOT(kill()));
               if (fowardQmlpuppetOutput) {
                   m_qmlPuppetPreviewProcess->setProcessChannelMode(QProcess::MergedChannels);
                   connect(m_qmlPuppetPreviewProcess.data(), SIGNAL(readyRead()), this, SLOT(printPreviewProcessOutput()));
               }
               m_qmlPuppetPreviewProcess->start(applicationPath, QStringList() << socketToken << "previewmode" << "-graphicssystem raster");

               m_qmlPuppetRenderProcess = new QProcess;
               m_qmlPuppetRenderProcess->setProcessEnvironment(environment);
               m_qmlPuppetRenderProcess->setObjectName("RenderProcess");
               connect(m_qmlPuppetRenderProcess.data(), SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(processFinished(int,QProcess::ExitStatus)));
               connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()), m_qmlPuppetRenderProcess.data(), SLOT(kill()));
               if (fowardQmlpuppetOutput) {
                   m_qmlPuppetRenderProcess->setProcessChannelMode(QProcess::MergedChannels);
                   connect(m_qmlPuppetRenderProcess.data(), SIGNAL(readyRead()), this, SLOT(printRenderProcessOutput()));
               }
               m_qmlPuppetRenderProcess->start(applicationPath, QStringList() << socketToken << "rendermode" << "-graphicssystem raster");
260

261
           }
262

263
           connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()), this, SLOT(deleteLater()));
264

265 266
           if (m_qmlPuppetEditorProcess->waitForStarted(10000)) {
               connect(m_qmlPuppetEditorProcess.data(), SIGNAL(finished(int)), m_qmlPuppetEditorProcess.data(),SLOT(deleteLater()));
267

268 269 270
               if (runModus == NormalModus) {
                   m_qmlPuppetPreviewProcess->waitForStarted();
                   connect(m_qmlPuppetPreviewProcess.data(), SIGNAL(finished(int)), m_qmlPuppetPreviewProcess.data(),SLOT(deleteLater()));
271

272 273 274
                   m_qmlPuppetRenderProcess->waitForStarted();
                   connect(m_qmlPuppetRenderProcess.data(), SIGNAL(finished(int)), m_qmlPuppetRenderProcess.data(),SLOT(deleteLater()));
               }
275

276 277
               if (!m_localServer->hasPendingConnections())
                   m_localServer->waitForNewConnection(10000);
278

279 280
               m_firstSocket = m_localServer->nextPendingConnection();
               connect(m_firstSocket.data(), SIGNAL(readyRead()), this, SLOT(readFirstDataStream()));
281

282 283 284
               if (runModus == NormalModus) {
                   if (!m_localServer->hasPendingConnections())
                       m_localServer->waitForNewConnection(10000);
285

286 287
                   m_secondSocket = m_localServer->nextPendingConnection();
                   connect(m_secondSocket.data(), SIGNAL(readyRead()), this, SLOT(readSecondDataStream()));
288

289 290
                   if (!m_localServer->hasPendingConnections())
                       m_localServer->waitForNewConnection(10000);
291

292 293 294
                   m_thirdSocket = m_localServer->nextPendingConnection();
                   connect(m_thirdSocket.data(), SIGNAL(readyRead()), this, SLOT(readThirdDataStream()));
               }
295

296
           } else {
297 298 299 300 301 302 303
               PuppetDialog::warning(Core::ICore::dialogParent(),
                                     tr("Cannot Start QML Puppet Executable"),
                                     missingQmlPuppetErrorMessage(tr("The executable of the QML Puppet process (%1) cannot be started. "
                                                                     "Please check your installation. "
                                                                     "QML Puppet is a process which runs in the background to render the items."
                                                                    ).arg(applicationPath)),
                                     copyAndPasterMessage(pathToQt));
304 305

               QmlDesignerPlugin::instance()->switchToTextModeDeferred();
306
           }
307

308
           m_localServer->close();
309

310
       } else {
311 312 313 314 315
           PuppetDialog::warning(Core::ICore::dialogParent(),
                                 tr("Wrong QML Puppet Executable Version"),
                                 missingQmlPuppetErrorMessage(tr("The QML Puppet version is incompatible with the Qt Creator version.")),
                                 copyAndPasterMessage(pathToQt)
                                 );
316
           QmlDesignerPlugin::instance()->switchToTextModeDeferred();
317 318
       }
   } else {
319 320 321 322
       PuppetDialog::warning(Core::ICore::dialogParent(),
                             tr("Cannot Find QML Puppet Executable"),
                             missingQmlPuppetErrorMessage(tr("The executable of the QML Puppet process (<code>%1</code>) cannot be found. "
                                                             "Check your installation. "
323
                                                            "QML Puppet is a process which runs in the background to render the items.").
324 325
                                                         arg(QDir::toNativeSeparators(applicationPath))),
                             copyAndPasterMessage(pathToQt));
326
       QmlDesignerPlugin::instance()->switchToTextModeDeferred();
327
   }
328 329 330 331 332 333 334

   int indexOfCapturePuppetStream = QCoreApplication::arguments().indexOf("-capture-puppet-stream");
   if (indexOfCapturePuppetStream > 0) {
       m_captureFileForTest.setFileName(QCoreApplication::arguments().at(indexOfCapturePuppetStream + 1));
       bool isOpen = m_captureFileForTest.open(QIODevice::WriteOnly);
       qDebug() << "file is open: " << isOpen;
   }
335 336 337 338
}

NodeInstanceServerProxy::~NodeInstanceServerProxy()
{
339 340
    disconnect(this, SLOT(processFinished(int,QProcess::ExitStatus)));

341 342
    writeCommand(QVariant::fromValue(EndPuppetCommand()));

343 344 345 346 347 348
    if (m_firstSocket)
        m_firstSocket->close();

    if (m_secondSocket)
        m_secondSocket->close();

349
    if (m_thirdSocket)
350 351
        m_thirdSocket->close();

352

353
    if (m_qmlPuppetEditorProcess)
354
        QTimer::singleShot(3000, m_qmlPuppetEditorProcess.data(), SLOT(terminate()));
355

356
    if (m_qmlPuppetPreviewProcess)
357
        QTimer::singleShot(3000, m_qmlPuppetPreviewProcess.data(), SLOT(terminate()));
358 359

    if (m_qmlPuppetRenderProcess)
360
         QTimer::singleShot(3000, m_qmlPuppetRenderProcess.data(), SLOT(terminate()));
361 362 363 364 365 366 367
}

void NodeInstanceServerProxy::dispatchCommand(const QVariant &command)
{
    static const int informationChangedCommandType = QMetaType::type("InformationChangedCommand");
    static const int valuesChangedCommandType = QMetaType::type("ValuesChangedCommand");
    static const int pixmapChangedCommandType = QMetaType::type("PixmapChangedCommand");
368
    static const int childrenChangedCommandType = QMetaType::type("ChildrenChangedCommand");
369
    static const int statePreviewImageChangedCommandType = QMetaType::type("StatePreviewImageChangedCommand");
370
    static const int componentCompletedCommandType = QMetaType::type("ComponentCompletedCommand");
371
    static const int synchronizeCommandType = QMetaType::type("SynchronizeCommand");
372
    static const int tokenCommandType = QMetaType::type("TokenCommand");
Marco Bubke's avatar
Marco Bubke committed
373
    static const int debugOutputCommandType = QMetaType::type("DebugOutputCommand");
374

375
    if (command.userType() ==  informationChangedCommandType) {
376
        nodeInstanceClient()->informationChanged(command.value<InformationChangedCommand>());
377
    } else if (command.userType() ==  valuesChangedCommandType) {
378
        nodeInstanceClient()->valuesChanged(command.value<ValuesChangedCommand>());
379
    } else if (command.userType() ==  pixmapChangedCommandType) {
380
        nodeInstanceClient()->pixmapChanged(command.value<PixmapChangedCommand>());
381
    } else if (command.userType() == childrenChangedCommandType) {
382
        nodeInstanceClient()->childrenChanged(command.value<ChildrenChangedCommand>());
383
    } else if (command.userType() == statePreviewImageChangedCommandType) {
384
        nodeInstanceClient()->statePreviewImagesChanged(command.value<StatePreviewImageChangedCommand>());
385
    } else if (command.userType() == componentCompletedCommandType) {
386
        nodeInstanceClient()->componentCompleted(command.value<ComponentCompletedCommand>());
387
    } else if (command.userType() == tokenCommandType) {
388
        nodeInstanceClient()->token(command.value<TokenCommand>());
389
    } else if (command.userType() == debugOutputCommandType) {
Marco Bubke's avatar
Marco Bubke committed
390
        nodeInstanceClient()->debugOutput(command.value<DebugOutputCommand>());
391
    } else if (command.userType() == synchronizeCommandType) {
392 393 394
        SynchronizeCommand synchronizeCommand = command.value<SynchronizeCommand>();
        m_synchronizeId = synchronizeCommand.synchronizeId();
    }  else
395 396 397 398 399 400 401 402
        Q_ASSERT(false);
}

NodeInstanceClientInterface *NodeInstanceServerProxy::nodeInstanceClient() const
{
    return m_nodeInstanceView.data();
}

403 404 405
static QString generatePuppetCompilingHelp(const QString &puppetName, const QString &pathToQt)
{

406 407 408
    QString buildDirectory =  QDir::toNativeSeparators(QDir::cleanPath(QDir::tempPath() + QStringLiteral("/") + puppetName));
    QString qmakePath = QDir::toNativeSeparators(QDir::cleanPath(pathToQt + QStringLiteral("/bin/qmake -r ")));
    QString projectPath = QDir::toNativeSeparators(QDir::cleanPath(sharedDirPath() + QStringLiteral("/qml/qmlpuppet/%1/%1.pro\n")));
409 410 411 412

    QString puppetCompileHelp;

    puppetCompileHelp.append(QStringLiteral("<p><code><pre>"));
413
    puppetCompileHelp.append(QStringLiteral("<form><input></input></form>"));
414 415 416 417 418 419 420 421 422 423 424
    puppetCompileHelp.append(QStringLiteral("mkdir ") + buildDirectory+ QStringLiteral("\n"));
    puppetCompileHelp.append(QStringLiteral("cd ") + buildDirectory + QStringLiteral("\n"));
    puppetCompileHelp.append(qmakePath + projectPath);
    puppetCompileHelp.append(QStringLiteral("make"));
    puppetCompileHelp.append(QStringLiteral("</pre></code></p>"));

    puppetCompileHelp = puppetCompileHelp.arg(puppetName);

    return puppetCompileHelp;
}

425 426 427 428 429
static void formatQmlPuppetCompilationMessage(const QString &puppetName,
                                              const QString &sharedDirPath,
                                              QTextStream &messageStream)
{
    const QString sourcePath = sharedDirPath + QStringLiteral("/qml/qmlpuppet/") + puppetName + QLatin1Char('/');
430 431 432 433 434 435 436 437

    //: %1 Puppet binary name ("qmlpuppet", "qml2puppet"), %2 source path.
    messageStream << QChar(' ') << NodeInstanceServerProxy::tr("You can build <code>%1</code> yourself with Qt 5.2.0 or higher. "
                                                 "The source can be found in <code>%2</code>.")
                     .arg(puppetName, QDir::toNativeSeparators(sourcePath))
                  << QChar(' ') << NodeInstanceServerProxy::tr("<code>%1</code> will be installed to the <code>bin</code> directory of your Qt version. "
                                                 "Qt Quick Designer will check the <code>bin</code> directory of the currently active Qt version "
                                                 "of your project.").arg(puppetName);
438 439
}

440
QString NodeInstanceServerProxy::missingQmlPuppetErrorMessage(const QString &preMessage) const
441
{
442
    QString message;
443
    QTextStream messageStream(&message);
444 445 446 447

    messageStream << QStringLiteral("<html><head/><body><p>")
                  << preMessage;

448
    if (hasQtQuick2(m_nodeInstanceView.data()))
449
        formatQmlPuppetCompilationMessage(QStringLiteral("qml2puppet"), sharedDirPath(), messageStream);
450
    else if (hasQtQuick1(m_nodeInstanceView.data()))
451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470
        formatQmlPuppetCompilationMessage(QStringLiteral("qmlpuppet"), sharedDirPath(), messageStream);

    messageStream << QStringLiteral("</p></body></html>");

    return message;
}

QString NodeInstanceServerProxy::copyAndPasterMessage(const QString &pathToQt) const
{
    QString message;
    QTextStream messageStream(&message);
    messageStream << QStringLiteral("<html><head/><body>");

    if (hasQtQuick2(m_nodeInstanceView.data()))
        messageStream << generatePuppetCompilingHelp(QStringLiteral("qmlpuppet2"), pathToQt);
    else if (hasQtQuick1(m_nodeInstanceView.data()))
        messageStream << generatePuppetCompilingHelp(QStringLiteral("qmlpuppet"), pathToQt);

    messageStream << QStringLiteral("</body></html>");

471 472 473
    return message;
}

474
static void writeCommandToIODecive(const QVariant &command, QIODevice *ioDevice, unsigned int commandCounter)
475
{
476
    if (ioDevice) {
477 478
        QByteArray block;
        QDataStream out(&block, QIODevice::WriteOnly);
479
        out.setVersion(QDataStream::Qt_4_8);
480
        out << quint32(0);
481
        out << quint32(commandCounter);
482 483 484 485
        out << command;
        out.device()->seek(0);
        out << quint32(block.size() - sizeof(quint32));

486
        ioDevice->write(block);
487
    }
488 489 490 491
}

void NodeInstanceServerProxy::writeCommand(const QVariant &command)
{
492 493 494 495 496 497 498 499 500 501
    writeCommandToIODecive(command, m_firstSocket.data(), m_writeCommandCounter);
    writeCommandToIODecive(command, m_secondSocket.data(), m_writeCommandCounter);
    writeCommandToIODecive(command, m_thirdSocket.data(), m_writeCommandCounter);

    if (m_captureFileForTest.isWritable()) {
        qDebug() << "Write strean to file: " << m_captureFileForTest.fileName();
        writeCommandToIODecive(command, &m_captureFileForTest, m_writeCommandCounter);
        qDebug() << "\twrite file: " << m_captureFileForTest.pos();
    }

502
    m_writeCommandCounter++;
503 504 505 506 507
    if (m_runModus == TestModus) {
        static int synchronizeId = 0;
        synchronizeId++;
        SynchronizeCommand synchronizeCommand(synchronizeId);

508
        writeCommandToIODecive(QVariant::fromValue(synchronizeCommand), m_firstSocket.data(), m_writeCommandCounter);
509
        m_writeCommandCounter++;
510

511
        while (m_firstSocket->waitForReadyRead(100)) {
512 513 514 515 516
                readFirstDataStream();
                if (m_synchronizeId == synchronizeId)
                    return;
        }
    }
517 518
}

519
void NodeInstanceServerProxy::processFinished(int /*exitCode*/, QProcess::ExitStatus exitStatus)
520
{
521
    qDebug() << "Process finished:" << sender();
522

523 524 525
    if (m_captureFileForTest.isOpen()) {
        m_captureFileForTest.close();
        m_captureFileForTest.remove();
526 527 528
        QMessageBox::warning(Core::ICore::dialogParent(), tr("QML Puppet Crashed"),
                             tr("You are recording a puppet stream and the puppet crashed. "
                                "It is recommended to reopen the Qt Quick Designer and start again."));
529 530 531
    }


532 533
    writeCommand(QVariant::fromValue(EndPuppetCommand()));

534 535 536 537
    if (m_firstSocket)
        m_firstSocket->close();
    if (m_secondSocket)
        m_secondSocket->close();
538 539 540
    if (m_thirdSocket)
        m_thirdSocket->close();

541 542
    if (exitStatus == QProcess::CrashExit)
        emit processCrashed();
543 544
}

545

546 547 548 549 550 551 552 553 554
void NodeInstanceServerProxy::readFirstDataStream()
{
    QList<QVariant> commandList;

    while (!m_firstSocket->atEnd()) {
        if (m_firstSocket->bytesAvailable() < int(sizeof(quint32)))
            break;

        QDataStream in(m_firstSocket.data());
555
        in.setVersion(QDataStream::Qt_4_8);
556

557
        if (m_firstBlockSize == 0)
558 559 560 561 562
            in >> m_firstBlockSize;

        if (m_firstSocket->bytesAvailable() < m_firstBlockSize)
            break;

563 564 565 566 567 568 569 570
        quint32 commandCounter;
        in >> commandCounter;
        bool commandLost = !((m_firstLastReadCommandCounter == 0 && commandCounter == 0) || (m_firstLastReadCommandCounter + 1 == commandCounter));
        if (commandLost)
            qDebug() << "server command lost: " << m_firstLastReadCommandCounter <<  commandCounter;
        m_firstLastReadCommandCounter = commandCounter;


571 572 573 574 575 576 577 578 579 580 581 582 583
        QVariant command;
        in >> command;
        m_firstBlockSize = 0;

        commandList.append(command);
    }

    foreach (const QVariant &command, commandList) {
        dispatchCommand(command);
    }
}

void NodeInstanceServerProxy::readSecondDataStream()
584 585 586
{
    QList<QVariant> commandList;

587 588
    while (!m_secondSocket->atEnd()) {
        if (m_secondSocket->bytesAvailable() < int(sizeof(quint32)))
589 590
            break;

591
        QDataStream in(m_secondSocket.data());
592
        in.setVersion(QDataStream::Qt_4_8);
593

594
        if (m_secondBlockSize == 0)
595
            in >> m_secondBlockSize;
596

597
        if (m_secondSocket->bytesAvailable() < m_secondBlockSize)
598 599
            break;

600 601 602 603 604 605 606 607
        quint32 commandCounter;
        in >> commandCounter;
        bool commandLost = !((m_secondLastReadCommandCounter == 0 && commandCounter == 0) || (m_secondLastReadCommandCounter + 1 == commandCounter));
        if (commandLost)
            qDebug() << "server command lost: " << m_secondLastReadCommandCounter <<  commandCounter;
        m_secondLastReadCommandCounter = commandCounter;


608 609
        QVariant command;
        in >> command;
610
        m_secondBlockSize = 0;
611 612 613 614 615 616 617 618 619

        commandList.append(command);
    }

    foreach (const QVariant &command, commandList) {
        dispatchCommand(command);
    }
}

620 621 622 623 624 625 626 627 628
void NodeInstanceServerProxy::readThirdDataStream()
{
    QList<QVariant> commandList;

    while (!m_thirdSocket->atEnd()) {
        if (m_thirdSocket->bytesAvailable() < int(sizeof(quint32)))
            break;

        QDataStream in(m_thirdSocket.data());
629
        in.setVersion(QDataStream::Qt_4_8);
630

631
        if (m_thirdBlockSize == 0)
632 633 634 635 636
            in >> m_thirdBlockSize;

        if (m_thirdSocket->bytesAvailable() < m_thirdBlockSize)
            break;

637 638 639 640 641 642 643 644
        quint32 commandCounter;
        in >> commandCounter;
        bool commandLost = !((m_thirdLastReadCommandCounter == 0 && commandCounter == 0) || (m_thirdLastReadCommandCounter + 1 == commandCounter));
        if (commandLost)
            qDebug() << "server command lost: " << m_thirdLastReadCommandCounter <<  commandCounter;
        m_thirdLastReadCommandCounter = commandCounter;


645 646 647 648 649 650 651 652 653 654 655 656
        QVariant command;
        in >> command;
        m_thirdBlockSize = 0;

        commandList.append(command);
    }

    foreach (const QVariant &command, commandList) {
        dispatchCommand(command);
    }
}

657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687
void NodeInstanceServerProxy::printEditorProcessOutput()
{
    while (m_qmlPuppetEditorProcess->canReadLine()) {
        QByteArray line = m_qmlPuppetEditorProcess->readLine();
        line.chop(1);
        qDebug().nospace() << "Editor Puppet: " << qPrintable(line);
    }
    qDebug() << "\n";
}

void NodeInstanceServerProxy::printPreviewProcessOutput()
{
    while (m_qmlPuppetPreviewProcess->canReadLine()) {
        QByteArray line = m_qmlPuppetPreviewProcess->readLine();
        line.chop(1);
        qDebug().nospace() << "Preview Puppet: " << qPrintable(line);
    }
    qDebug() << "\n";
}

void NodeInstanceServerProxy::printRenderProcessOutput()
{
    while (m_qmlPuppetRenderProcess->canReadLine()) {
        QByteArray line = m_qmlPuppetRenderProcess->readLine();
        line.chop(1);
        qDebug().nospace() << "Render Puppet: " << qPrintable(line);
    }

    qDebug() << "\n";
}

688 689
QString NodeInstanceServerProxy::qmlPuppetApplicationName() const
{
690 691 692
    if (hasQtQuick2(m_nodeInstanceView.data()))
        return QLatin1String("qml2puppet" QTC_HOST_EXE_SUFFIX);
    return QLatin1String("qmlpuppet" QTC_HOST_EXE_SUFFIX);
693 694 695 696 697
}

QString NodeInstanceServerProxy::macOSBundlePath(const QString &path) const
{
    QString applicationPath = path;
698 699 700 701 702 703 704
    if (Utils::HostOsInfo::isMacHost()) {
        if (hasQtQuick2(m_nodeInstanceView.data()))
            applicationPath += QLatin1String("/qml2puppet.app/Contents/MacOS");
        else
            applicationPath += QLatin1String("/qmlpuppet.app/Contents/MacOS");

    }
705 706
   return applicationPath;
}
707

708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747
void NodeInstanceServerProxy::createInstances(const CreateInstancesCommand &command)
{
    writeCommand(QVariant::fromValue(command));
}

void NodeInstanceServerProxy::changeFileUrl(const ChangeFileUrlCommand &command)
{
    writeCommand(QVariant::fromValue(command));
}

void NodeInstanceServerProxy::createScene(const CreateSceneCommand &command)
{
    writeCommand(QVariant::fromValue(command));
}

void NodeInstanceServerProxy::clearScene(const ClearSceneCommand &command)
{
    writeCommand(QVariant::fromValue(command));
}

void NodeInstanceServerProxy::removeInstances(const RemoveInstancesCommand &command)
{
    writeCommand(QVariant::fromValue(command));
}

void NodeInstanceServerProxy::removeProperties(const RemovePropertiesCommand &command)
{
    writeCommand(QVariant::fromValue(command));
}

void NodeInstanceServerProxy::changePropertyBindings(const ChangeBindingsCommand &command)
{
    writeCommand(QVariant::fromValue(command));
}

void NodeInstanceServerProxy::changePropertyValues(const ChangeValuesCommand &command)
{
    writeCommand(QVariant::fromValue(command));
}

748 749 750 751 752
void NodeInstanceServerProxy::changeAuxiliaryValues(const ChangeAuxiliaryCommand &command)
{
    writeCommand(QVariant::fromValue(command));
}

753 754 755 756 757 758 759 760 761 762 763 764 765 766 767
void NodeInstanceServerProxy::reparentInstances(const ReparentInstancesCommand &command)
{
    writeCommand(QVariant::fromValue(command));
}

void NodeInstanceServerProxy::changeIds(const ChangeIdsCommand &command)
{
    writeCommand(QVariant::fromValue(command));
}

void NodeInstanceServerProxy::changeState(const ChangeStateCommand &command)
{
    writeCommand(QVariant::fromValue(command));
}

768 769 770 771
void NodeInstanceServerProxy::completeComponent(const CompleteComponentCommand &command)
{
    writeCommand(QVariant::fromValue(command));
}
772

773
void NodeInstanceServerProxy::changeNodeSource(const ChangeNodeSourceCommand &command)
774 775 776 777
{
    writeCommand(QVariant::fromValue(command));
}

778 779 780 781 782
void NodeInstanceServerProxy::token(const TokenCommand &command)
{
    writeCommand(QVariant::fromValue(command));
}

783 784 785 786 787
void NodeInstanceServerProxy::removeSharedMemory(const RemoveSharedMemoryCommand &command)
{
   writeCommand(QVariant::fromValue(command));
}

788
} // namespace QmlDesigner