pqEventDispatcher.cxx 12.7 KB
Newer Older
1 2 3 4 5
/*=========================================================================

   Program: ParaView
   Module:    pqEventDispatcher.cxx

6
   Copyright (c) 2005-2008 Sandia Corporation, Kitware Inc.
7 8 9
   All rights reserved.

   ParaView is a free software; you can redistribute it and/or modify it
10
   under the terms of the ParaView license version 1.2. 
11

12
   See License_v1.2.txt for the full ParaView license.
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
   A copy of this license can be obtained by contacting
   Kitware Inc.
   28 Corporate Drive
   Clifton Park, NY 12065
   USA

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

=========================================================================*/

33 34
#include "pqEventDispatcher.h"

35 36 37 38 39
#include "pqEventPlayer.h"
#include "pqEventSource.h"

#include <QAbstractEventDispatcher>
#include <QtDebug>
40
#include <QApplication>
41
#include <QEventLoop>
42
#include <QThread>
43 44
#include <QDialog>
#include <QMainWindow>
45 46 47
#include <QList>
#include <QPointer>
#include <QTimer>
48

49 50
#include <iostream>
using namespace std;
51

52
//-----------------------------------------------------------------------------
53 54 55
namespace
{
  static QList<QPointer<QTimer> > RegisteredTimers;
56
  
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
  void processTimers()
    {
    foreach (QTimer* timer, RegisteredTimers)
      {
      if (timer && timer->isActive())
        {
        QTimerEvent event(timer->timerId());
        qApp->notify(timer, &event);
        }
      }
    }

  static int EventPlaybackDelay = QT_TESTING_EVENT_PLAYBACK_DELAY;
};

72
bool pqEventDispatcher::DeferMenuTimeouts = false;
73
bool pqEventDispatcher::DeferEventsIfBlocked = false;
74
bool pqEventDispatcher::PlayingBlockingEvent = false;
75

76 77 78
//-----------------------------------------------------------------------------
pqEventDispatcher::pqEventDispatcher(QObject* parentObject) :
  Superclass(parentObject)
79
{
80 81
  this->ActiveSource = NULL;
  this->ActivePlayer = NULL;
82 83

  this->PlayBackStatus = true;
84
  this->PlayBackFinished = false;
85 86 87
  this->PlayBackPaused = false;
  this->PlayBackOneStep = false;
  this->PlayBackStoped = false;
88

89 90 91
#ifdef __APPLE__
  this->BlockTimer.setInterval(1000);
#else
92
  this->BlockTimer.setInterval(100);
93
#endif
94 95 96
  this->BlockTimer.setSingleShot(true);
  QObject::connect(&this->BlockTimer, SIGNAL(timeout()),
                   this, SLOT(playEventOnBlocking()));
97 98
}

99
//-----------------------------------------------------------------------------
100 101 102 103
pqEventDispatcher::~pqEventDispatcher()
{
}

104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125

//-----------------------------------------------------------------------------
void pqEventDispatcher::setEventPlaybackDelay(int milliseconds)
{
  EventPlaybackDelay = (milliseconds <= 0)? 0 : milliseconds;
}

//-----------------------------------------------------------------------------
int pqEventDispatcher::eventPlaybackDelay()
{
  return EventPlaybackDelay;
}

//-----------------------------------------------------------------------------
void pqEventDispatcher::registerTimer(QTimer* timer)
{
  if (timer)
    {
    RegisteredTimers.push_back(timer);
    }
}

126
//-----------------------------------------------------------------------------
127
void pqEventDispatcher::deferEventsIfBlocked(bool enable)
128
{
129
  pqEventDispatcher::DeferEventsIfBlocked = enable;
130 131
}

132

133 134 135
//-----------------------------------------------------------------------------
void pqEventDispatcher::aboutToBlock()
{
136
  // if (!pqEventDispatcher::DeferMenuTimeouts)
137 138 139
    {
    if (!this->BlockTimer.isActive())
      {
140
      // cout << "aboutToBlock" << endl;
141 142 143 144 145 146 147 148 149
      // Request a delayed playback for an event.
      this->BlockTimer.start();
      }
    }
}

//-----------------------------------------------------------------------------
void pqEventDispatcher::awake()
{
150 151 152 153 154
  //if (!pqEventDispatcher::DeferMenuTimeouts)
  //  {
  //  // cout << "awake" << endl;
  //  // this->BlockTimer.stop();
  //  }
155 156
}

157 158 159
//-----------------------------------------------------------------------------
void pqEventDispatcher::setTimeStep(int value)
{
160
  EventPlaybackDelay = value;
161 162 163
}

//-----------------------------------------------------------------------------
164
void pqEventDispatcher::run(bool value)
165
{
166 167 168 169 170 171 172 173 174
  this->PlayBackPaused = !value;
  if (value)
    {
    emit this->restarted();
    }
  else
    {
    emit this->paused();
    }
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
}

//-----------------------------------------------------------------------------
void pqEventDispatcher::stop()
{
  this->PlayBackPaused = false;
  this->PlayBackFinished = true;
}

//-----------------------------------------------------------------------------
bool pqEventDispatcher::isPaused() const
{
  return this->PlayBackPaused;
}

//-----------------------------------------------------------------------------
bool pqEventDispatcher::status() const
{
  return this->PlayBackStatus;
}

//-----------------------------------------------------------------------------
void pqEventDispatcher::oneStep()
{
  this->PlayBackOneStep = true;
}

202
//-----------------------------------------------------------------------------
203
bool pqEventDispatcher::playEvents(pqEventSource& source, pqEventPlayer& player)
204
{
205
  if (this->ActiveSource || this->ActivePlayer)
206 207
    {
    qCritical() << "Event dispatcher is already playing";
208
    return false;
209 210
    }

211 212 213
  this->ActiveSource = &source;
  this->ActivePlayer = &player;

214
  QApplication::setEffectEnabled(Qt::UI_General, false);
215 216 217 218 219
  QApplication::setEffectEnabled(Qt::UI_AnimateMenu, false); // Show animated menus.
  QApplication::setEffectEnabled(Qt::UI_FadeMenu, false); //	Show faded menus.
  QApplication::setEffectEnabled(Qt::UI_AnimateCombo, false); // Show animated comboboxes.
  QApplication::setEffectEnabled(Qt::UI_AnimateTooltip, false); //	Show tooltip animations.
  QApplication::setEffectEnabled(Qt::UI_FadeTooltip, false); // Show tooltip fading effects.
220

221 222 223 224 225
  QObject::connect(QAbstractEventDispatcher::instance(), SIGNAL(aboutToBlock()),
                   this, SLOT(aboutToBlock()));
  QObject::connect(QAbstractEventDispatcher::instance(), SIGNAL(awake()),
                   this, SLOT(awake()));

226 227 228 229 230 231 232 233 234 235 236
  // This is how the playback logic works:
  // * In here, we continuously keep on playing one event after another until
  //   we are done processing all the events.
  // * If a modal dialog pops up, then this->aboutToBlock() gets called. To me
  //   accurate, aboutToBlock() is called everytime the sub-event loop is entered
  //   and more modal dialogs that loop is entered after processing of each event
  //   (not merely when the dialog pops up).
  // * In this->aboutToBlock() we start a timer which on timeout processes just 1 event.
  // * After executing that event, if the dialog still is up, them aboutToBlock() will
  //   be called again and the cycle continues. If not, the control returns to this main
  //   playback loop, and it continues.
237 238
  this->PlayBackStatus = true; // success.
  this->PlayBackFinished = false;
239 240
  while (!this->PlayBackFinished)
    {
241 242
    if(!this->PlayBackPaused)
      {
243
    //cerr << "=== playEvent(1) ===" << endl;
244 245
    this->playEvent();
    }
246 247 248 249 250 251 252 253 254 255 256 257
    else
      {
      if (this->PlayBackOneStep)
        {
        this->PlayBackOneStep = false;
        this->playEvent();
        }
      else
        {
        this->processEventsAndWait(100);
        }
      }
258
    }
259 260
  this->ActiveSource = NULL;
  this->ActivePlayer = NULL;
261 262 263 264 265 266

  QObject::disconnect(QAbstractEventDispatcher::instance(), SIGNAL(aboutToBlock()),
                   this, SLOT(aboutToBlock()));
  QObject::disconnect(QAbstractEventDispatcher::instance(), SIGNAL(awake()),
                   this, SLOT(awake()));

267
  return this->PlayBackStatus;
268
}
269

270
//-----------------------------------------------------------------------------
271
void pqEventDispatcher::playEventOnBlocking()
272
{
273 274 275 276 277 278
//  if(this->PlayingBlockingEvent)
//    {
//    qDebug() << "Event blocking already playing ....";
//    return;
//    }

279
  if (pqEventDispatcher::DeferMenuTimeouts || pqEventDispatcher::DeferEventsIfBlocked)
280
    {
281
    //cerr << "=== playEventOnBlocking ===" << endl;
282 283 284 285
    this->BlockTimer.start();
    return;
    }

286
  pqEventDispatcher::PlayingBlockingEvent = true;
287 288
  //cout << "---blocked event: " << endl;
  // if needed for debugging, I can print blocking annotation here.
289
  //cerr << "=== playEvent(2) ===" << endl;
290
  this->playEvent(1);
291

292 293 294 295
  //if (!this->BlockTimer.isActive())
  //  {
  //  this->BlockTimer.start();
  //  }
296
  pqEventDispatcher::PlayingBlockingEvent = false;
297
}
298

299
//-----------------------------------------------------------------------------
300
void pqEventDispatcher::playEvent(int indent)
301 302
{
  this->BlockTimer.stop();
303
  if (this->PlayBackFinished)
304
    {
305
    return;
306
    }
307

308
  if (!this->ActiveSource)
309
    {
310 311 312
    this->PlayBackFinished = true;
    this->PlayBackStatus = false; // failure.
    qCritical("Internal error: playEvent called without a valid event source.");
313 314 315
    return;
    }

316 317 318
  QString object;
  QString command;
  QString arguments;
Mathieu Westphal's avatar
Mathieu Westphal committed
319
  int eventType;
320
  
321
  int result = this->ActiveSource->getNextEvent(object, command, arguments, eventType);
322
  if (result == pqEventSource::DONE)
323
    {
324
    this->PlayBackFinished = true;
325 326
    return;
    }
327 328
  else if(result == pqEventSource::FAILURE)
    {
329 330
    this->PlayBackFinished = true;
    this->PlayBackStatus = false; // failure.
331 332
    return;
    }
333
    
334 335 336 337
  static unsigned long counter=0;
  unsigned long local_counter = counter++;
  QString pretty_name = object.mid(object.lastIndexOf('/'));
  bool print_debug = getenv("PV_DEBUG_TEST") != NULL;
338
#if defined(WIN32) || defined(__APPLE__) // temporary debugging on both platforms.
339 340
  print_debug = true;
#endif
341 342
  if (print_debug)
    {
Mathieu Westphal's avatar
Mathieu Westphal committed
343 344 345 346 347 348
    QString eventString = "Event";
    if (eventType == pqEventTypes::CHECK_EVENT)
      {
      eventString = "Check Event";
      }

349 350
    cout <<  QTime::currentTime().toString("hh:mm:ss").toStdString().c_str()
         << " : "
351 352
         << QString().fill(' ', 4*indent).toStdString().c_str()
         << local_counter << ": Test (" << indent << "): "
353
         << eventString.toUtf8().data() << ": "
354 355 356
         << pretty_name.toStdString().c_str() << ": "
         << command.toStdString().c_str() << " : "
         << arguments.toStdString().c_str() << endl;
357 358
    }

359
  bool error = false;
Mathieu Westphal's avatar
Mathieu Westphal committed
360
  this->ActivePlayer->playEvent(object, command, arguments, eventType, error);
361
  this->BlockTimer.stop();
362 363 364 365 366 367 368 369

  // process any posted events. We call processEvents() so that any slots
  // connected using QueuedConnection also get handled.
  this->processEventsAndWait(EventPlaybackDelay);

  // We explicitly timeout timers that have been registered with us.
  processTimers();

370
  this->BlockTimer.stop();
371

372 373
  if (print_debug)
    {
374 375
    cout << QTime::currentTime().toString("hh:mm:ss").toStdString().c_str()
         << " : "
376 377
         << QString().fill(' ', 4*indent).toStdString().c_str()
         << local_counter << ": Done" << endl;
378
    }
379

380 381 382 383 384 385
  if (error)
    {
    this->PlayBackStatus  = false;
    this->PlayBackFinished = true;
    return;
    }
386

387 388
}

389
//-----------------------------------------------------------------------------
390
void pqEventDispatcher::processEventsAndWait(int ms)
391
{
392 393
  bool prev = pqEventDispatcher::DeferMenuTimeouts;
  pqEventDispatcher::DeferMenuTimeouts = true;
394 395
  if (ms > 0)
    {
396
    QApplication::sendPostedEvents();
397 398
    QEventLoop loop;
    QTimer::singleShot(ms, &loop, SLOT(quit()));
399
    loop.exec();
400
    }
401 402 403 404 405 406 407 408 409
  // When this gets called during playback from a blocking event loop (e.g. a modal dialog)
  // calling `QApplication::processEvents()` has a sideeffect on Qt 5 + OsX where it does
  // not quit the eventloop for the modal dialog until a mouse event (for example) is
  // received by the application. Avoiding calling QApplication::processEvents when already
  // processing a event loop other the apps main event loop avoids that problem.
  if (!pqEventDispatcher::PlayingBlockingEvent)
    {
    QApplication::processEvents();
    }
410
  QApplication::sendPostedEvents();
411 412 413 414
  if (!pqEventDispatcher::PlayingBlockingEvent)
    {
    QApplication::processEvents();
    }
415
  pqEventDispatcher::DeferMenuTimeouts = prev;
416
}
417 418 419 420 421 422 423 424 425

//-----------------------------------------------------------------------------
void pqEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags)
{
  bool prev = pqEventDispatcher::DeferMenuTimeouts;
  pqEventDispatcher::DeferMenuTimeouts = true;
  QApplication::processEvents(flags);
  pqEventDispatcher::DeferMenuTimeouts = prev;
}