00001 #include "constants.h"
00002
00003 #include "automataWorkSimulator.h"
00004 #include "editor.h"
00005 #include "stringProcessor.h"
00006 #include "utils.h"
00007 #include "scopedsetter.h"
00008
00009 #include <QRubberBand>
00010 #include <QPainterPath>
00011 #include <QLabel>
00012 #include <QLineEdit>
00013 #include <QPushButton>
00014 #include <QTableWidget>
00015 #include <QGridLayout>
00016 #include <QList>
00017 #include <QVector>
00018 #include <QTextEdit>
00019 #include <QCheckBox>
00020 #include <QDoubleSpinBox>
00021 #include <QMessageBox>
00022 #include <QTimer>
00023 #include <QCoreApplication>
00024 #include <QUndoStack>
00025 #include <QFileDialog>
00026
00027 #ifdef TESTING_ANIMATIONS
00028 # define DBGLOG_ANIM(x) DBGLOG_("ANIMATIONS", x)
00029 #else
00030 # define DBGLOG_ANIM(x)
00031 #endif
00032
00033
00034
00035
00036
00037
00038
00039 class SimulationMarker : public QRubberBand
00040 {
00041 public:
00042 SimulationMarker(const QPainterPath &path, Shape s, QWidget *p = 0)
00043 : QRubberBand(s, p), m_pos(0.0f), m_path(path), m_moveStep(0.05f)
00044 {
00045 updatePosition();
00046 }
00047
00048 void setPos(float pos)
00049 {
00050 m_pos = pos;
00051 updatePosition();
00052 }
00053
00054 float pos()
00055 {
00056 return m_pos;
00057 }
00058
00059 void setMoveStep(float step)
00060 {
00061 m_moveStep = step;
00062 }
00063
00064 bool moveByStep()
00065 {
00066 m_pos += m_moveStep;
00067 if (m_pos >= 1.0f)
00068 return false;
00069
00070 updatePosition();
00071 return true;
00072 }
00073
00074 void setPath(const QPainterPath &path)
00075 {
00076 m_path = path;
00077
00078 }
00079
00080 public:
00081 static const float width;
00082 static const float height;
00083
00084 protected:
00085 void updatePosition()
00086 {
00087 QPointF point = m_path.pointAtPercent(m_pos);
00088 setGeometry(point.x()-width/2, point.y()-height/2, width, height);
00089 }
00090
00091 void paintEvent(QPaintEvent * )
00092 {
00093
00094 QPainter painter(this);
00095 painter.setPen(Qt::NoPen);
00096
00097 #ifndef DONT_USE_OPENGL_RENDERING // hack due to rendering problems - now seems better
00098 painter.fillRect(QRectF(0, 0, width, height), QColor(255, 255, 255, 255));
00099 #endif
00100
00101 painter.setBrush(QColor(50, 50, 200, 80));
00102 painter.drawEllipse(QRectF(0, 0, width, height));
00103 }
00104
00105 QPointF m_center;
00106 float m_pos;
00107 QPainterPath m_path;
00108 float m_moveStep;
00109 };
00110
00111 const float SimulationMarker::width = 10.0f;
00112 const float SimulationMarker::height = 10.0f;
00113
00114
00115
00116
00117
00118
00119
00120 AutomataWorkSimulator::AutomataWorkSimulator(Editor *editor, QUndoStack &undoStack,
00121 const QSharedPointer<AutomatonImpl> &automaton)
00122 : m_editor(editor), m_undoStack(undoStack), m_undoStackStartIdx(undoStack.index()),
00123 m_automaton(automaton), m_status(Configuration::eNoStatus),
00124 m_inputIndex(0), m_dialog(new SimulationDialog(editor, this)),
00125 m_activeStatesAreMarked(false), m_simulationTimer(new QTimer(this)),
00126 m_playing(false), m_animationTimer(new QTimer(this)),
00127 m_useAnimations(USE_ANIMS_BY_DEFAULT), m_animationSpeed(ANIM_SPEED_DEFAULT)
00128 {
00129 DBGLOG(DBGPAR(m_undoStackStartIdx));
00130
00131 m_simulationTimer->setInterval(SIMULATION_STEP_DEFAULT * 1000);
00132 connect(m_simulationTimer.data(), SIGNAL(timeout()), this, SLOT(processStep()));
00133
00134 m_animationTimer->setInterval(ANIMATION_FRAME_TIME);
00135 connect(m_animationTimer.data(), SIGNAL(timeout()), this, SLOT(animate()));
00136
00137 connect(m_dialog.data(), SIGNAL(closed()), this, SLOT(dialogClosed()));
00138 connect(m_dialog.data(), SIGNAL(useAnimChanged(int)), this, SLOT(useAnimChanged(int)));
00139 connect(m_dialog.data(), SIGNAL(animSpeedChanged(double)), this, SLOT(animSpeedChanged(double)));
00140 connect(m_dialog.data(), SIGNAL(stepSpeedChanged(double)), this, SLOT(stepSpeedChanged(double)));
00141 }
00142
00143 AutomataWorkSimulator::~AutomataWorkSimulator()
00144 {
00145 DBGLOG("called");
00146 foreach(SimulationMarker *sm, m_markers)
00147 {
00148 delete sm;
00149 }
00150
00151 m_markers.clear();
00152 }
00153
00154 bool AutomataWorkSimulator::hasStateBefore() const
00155 {
00156 return (m_inputIndex > 0);
00157 }
00158
00159 bool AutomataWorkSimulator::hasStateAfter() const
00160 {
00161
00162
00163 return (m_inputIndex < m_inputList.count() &&
00164 !(m_status == Configuration::eError && m_backSteps == 0));
00165 }
00166
00167 bool AutomataWorkSimulator::isPlaying() const
00168 {
00169 Q_ASSERT(m_simulationTimer->isActive() == m_playing);
00170 return m_playing;
00171 }
00172
00173 bool AutomataWorkSimulator::isSet() const
00174 {
00175 return !m_inputList.empty();
00176 }
00177
00178 int AutomataWorkSimulator::getPos() const
00179 {
00180 if (m_inputList.isEmpty()) return 0;
00181 if (m_inputIndex == m_inputList.size()) return m_dialog->getInputLength();
00182
00183 int result = m_inputList[m_inputIndex].textIdx;
00184 return result;
00185 }
00186
00187 AutomataWorkSimulator::TConfigurationList AutomataWorkSimulator::getConfigurations() const
00188 {
00189 return m_configurationList.mid(0, m_inputIndex + 1);
00190 }
00191
00192 void AutomataWorkSimulator::run()
00193 {
00194 m_dialog->show();
00195 reset();
00196 }
00197
00198 bool AutomataWorkSimulator::setInput(const QString &text)
00199 {
00200 ITransition::TCharSet alphabet = m_automaton->getAlphabet();
00201
00202 m_inputList = StringProcessor::computeCharacterList(text);
00203 Q_ASSERT(!(text != "" && m_inputList.empty()));
00204
00205 foreach(const StringProcessor::CharacterInfo &c, m_inputList)
00206 {
00207 if (!alphabet.contains(c.character))
00208 {
00209 m_inputList.clear();
00210 return false;
00211 }
00212 }
00213
00214 Q_ASSERT(!m_automaton->getActiveStates().isEmpty());
00215 m_configurationList << Configuration(m_automaton->getActiveStatesNames(),
00216 QStringList(AutomataCreator::defaultEpsilonSymbList[0]),
00217 StringProcessor::parseSymbols(m_inputList),
00218 m_automaton->isConfigurationAccepted() ? Configuration::eAccepted : Configuration::eNoStatus);
00219
00220 m_dialog->updateState();
00221
00222 return true;
00223 }
00224
00225 void AutomataWorkSimulator::stepForward()
00226 {
00227 if (isPlaying())
00228 pause();
00229
00230 Q_ASSERT(!m_animationTimer->isActive());
00231
00232 processStep();
00233 }
00234
00235 void AutomataWorkSimulator::stepBackward()
00236 {
00237 if (isPlaying())
00238 pause();
00239
00240 Q_ASSERT(!m_animationTimer->isActive());
00241
00242 Q_ASSERT(m_inputIndex > 0);
00243 if (m_inputIndex == 0) return;
00244
00245 m_backSteps++;
00246 m_inputIndex--;
00247
00248 m_editor->undo();
00249
00250 if (m_useAnimations)
00251 doAnimation(revertPaths(m_animationPathMemory[m_animationPathMemory.count()-m_backSteps]));
00252
00253 m_editor->undo();
00254
00255 m_activeStates = m_activeStatesMemory[m_activeStatesMemory.count()-m_backSteps];
00256
00257 m_dialog->updateState();
00258 }
00259
00260 void AutomataWorkSimulator::play()
00261 {
00262 Q_ASSERT(!m_animationTimer->isActive());
00263
00264
00265 m_simulationTimer->start();
00266 m_playing = true;
00267
00268 m_dialog->updateState();
00269
00270 processStep();
00271 }
00272
00273 void AutomataWorkSimulator::pause()
00274 {
00275 if (m_playing)
00276 {
00277 m_playing = false;
00278 m_simulationTimer->stop();
00279 }
00280
00281 m_dialog->updateState();
00282 }
00283
00284 void AutomataWorkSimulator::reset()
00285 {
00286 Q_ASSERT(!m_animationTimer->isActive());
00287
00288 m_playing = false;
00289 m_simulationTimer->stop();
00290
00291 #ifdef RESET_UNDO_STACK_AFTER_SIMULATION
00292 m_undoStack.setIndex(m_undoStackStartIdx);
00293 m_activeStatesAreMarked = false;
00294 #else
00295 setActiveStatesMarked(false);
00296 #endif
00297
00298 m_status = Configuration::eNoStatus;
00299 m_inputIndex = 0;
00300 m_inputList.clear();
00301 m_backSteps = 0;
00302 m_animationPathMemory.clear();
00303 m_configurationList.clear();
00304
00305 m_automaton->reset();
00306
00307 m_activeStates = m_automaton->getPrivateActiveStates();
00308 setActiveStatesMarked(true);
00309
00310 m_activeStatesMemory.clear();
00311
00312 m_dialog->updateState();
00313 }
00314
00315 void AutomataWorkSimulator::doAnimation(const QList<QPainterPath> &paths)
00316 {
00317 if (m_useAnimations)
00318 {
00319 m_pathCount = paths.count();
00320 for (int i = 0; i < m_pathCount; ++i)
00321 {
00322 if (i < m_markers.count())
00323 {
00324 m_markers[i]->setPath(paths[i]);
00325 m_markers[i]->setPos(0.0f);
00326 }
00327 else
00328 {
00329 m_markers << new SimulationMarker(paths[i], QRubberBand::Line, m_editor);
00330 }
00331
00332 m_markers[i]->setMoveStep(ANIMATION_FRAME_TIME / m_animationSpeed);
00333 m_markers[i]->show();
00334 }
00335
00336 m_animationState = eMoving;
00337 m_animationTimer->start();
00338 animate();
00339
00340
00341 while (m_animationTimer->isActive()) { QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); }
00342
00343 for (int i = 0; i < paths.count(); ++i)
00344 {
00345 m_markers[i]->hide();
00346 }
00347 }
00348 }
00349
00350 void AutomataWorkSimulator::animate()
00351 {
00352 DBGLOG_ANIM("called");
00353 switch(m_animationState)
00354 {
00355 case eMoving:
00356 if (!animateMove())
00357 m_animationState = eStopped;
00358 break;
00359 case eStopped:
00360 m_animationTimer->stop();
00361 break;
00362 }
00363
00364 m_editor->update();
00365 }
00366
00367 bool AutomataWorkSimulator::animateMove()
00368 {
00369 bool resume = false;
00370 Q_ASSERT(m_pathCount <= m_markers.count());
00371 for(int i=0; i < m_pathCount; ++i)
00372 {
00373 resume |= m_markers[i]->moveByStep();
00374 }
00375
00376 DBGLOG_ANIM(DBGPAR(resume));
00377 return resume;
00378 }
00379
00380 void AutomataWorkSimulator::refreshPlayingStatus()
00381 {
00382 if (!m_playing) return;
00383
00384 if (m_inputIndex != m_inputList.count())
00385 m_simulationTimer->start();
00386 else
00387 m_playing = false;
00388 }
00389
00390 void AutomataWorkSimulator::processStep()
00391 {
00392 m_simulationTimer->stop();
00393
00394 if (m_backSteps > 0)
00395 {
00396 m_editor->redo();
00397
00398 doAnimation(m_animationPathMemory[m_animationPathMemory.count()-m_backSteps]);
00399
00400 m_editor->redo();
00401 m_backSteps--;
00402 m_inputIndex++;
00403
00404 m_activeStates = m_activeStatesMemory[m_activeStatesMemory.count()-1-m_backSteps];
00405
00406 refreshPlayingStatus();
00407
00408 m_dialog->updateState();
00409 return;
00410 }
00411
00412 Q_ASSERT(m_inputIndex < m_inputList.count());
00413 QString currentCharacter = m_inputList[m_inputIndex].character;
00414
00415 QList<QPainterPath> animationPaths;
00416 IState::TIStateNameSet newActiveStatesNames =
00417 m_automaton->processCharacterWithInfo(currentCharacter, animationPaths);
00418 StateImpl::TStateList newActiveStates =
00419 m_automaton->getPrivateActiveStates();
00420
00421 setActiveStatesMarked(false);
00422
00423 for(int i=0; i<animationPaths.count(); ++i)
00424 {
00425 animationPaths[i] = m_editor->mapFromScene(animationPaths[i]);
00426 }
00427 doAnimation(animationPaths);
00428
00429 m_activeStates = newActiveStates;
00430 setActiveStatesMarked(true);
00431
00432 m_inputIndex++;
00433
00434 if (m_activeStates.empty())
00435 {
00436 m_status = Configuration::eError;
00437 }
00438 else if(m_automaton->isConfigurationAccepted())
00439 {
00440 m_status = Configuration::eAccepted;
00441 }
00442 else
00443 {
00444 m_status = Configuration::eNoStatus;
00445 }
00446
00447 QStringList remainingString(m_inputIndex == m_inputList.count() ?
00448 QStringList(AutomataCreator::defaultEpsilonSymbList[0]) :
00449 StringProcessor::parseSymbols((m_inputList.mid(m_inputIndex))));
00450 m_configurationList << Configuration(m_automaton->getActiveStatesNames(),
00451 m_automaton->getProcessedString(),
00452 remainingString,
00453 m_status);
00454
00455 m_activeStatesMemory << m_activeStates;
00456
00457
00458 m_animationPathMemory << animationPaths;
00459
00460 refreshPlayingStatus();
00461
00462 m_dialog->updateState();
00463 }
00464
00465 void AutomataWorkSimulator::setActiveStatesMarked(bool marked)
00466 {
00467 if (m_activeStatesAreMarked == marked) return;
00468
00469 QList<State*> statesToBeMarked;
00470 foreach(const QSharedPointer<StateImpl> &state, m_activeStates)
00471 {
00472 State *graphicsState = state->getGraphicsState();
00473 Q_ASSERT(graphicsState);
00474 statesToBeMarked << graphicsState;
00475 }
00476
00477 emit setStatesMarked(statesToBeMarked, marked);
00478
00479 m_activeStatesAreMarked = marked;
00480 m_editor->update();
00481 }
00482
00483 void AutomataWorkSimulator::stepSpeedChanged(double val)
00484 {
00485 m_simulationTimer->setInterval(val * 1000);
00486 }
00487
00488 void AutomataWorkSimulator::animSpeedChanged(double val)
00489 {
00490 m_animationSpeed = val;
00491 }
00492
00493 void AutomataWorkSimulator::useAnimChanged(int state)
00494 {
00495 m_useAnimations = (state == Qt::Checked);
00496 }
00497
00498 void AutomataWorkSimulator::dialogClosed()
00499 {
00500 #ifdef RESET_UNDO_STACK
00501 m_undoStack.setIndex(m_undoStackStartIdx);
00502 m_activeStatesAreMarked = false;
00503 #else
00504 setActiveStatesMarked(false);
00505 #endif
00506 emit finished();
00507 }
00508
00509 QList<QPainterPath> AutomataWorkSimulator::revertPaths(const QList<QPainterPath> &paths) const
00510 {
00511 QList<QPainterPath> result;
00512
00513 foreach(const QPainterPath &path, paths)
00514 {
00515 result << path.toReversed();
00516 }
00517
00518 return result;
00519 }
00520
00521 bool AutomataWorkSimulator::getFileName(QString &fileName, bool &latexHeader)
00522 {
00523 fileName = "";
00524 latexHeader = false;
00525
00526 QStringList formats;
00527 formats << tr("LaTeX file (*.tex)")
00528 << tr("LaTeX file w/o head and tail (*.tex)")
00529 << tr("VauCanSon-G (*.vcg)");
00530
00531 QString filter;
00532 fileName = QFileDialog::getSaveFileName(m_dialog.data(), "Save file", "slideshow",
00533 formats.join(";;"), &filter);
00534
00535 if (fileName == "") return false;
00536
00537 if (filter == formats[0] || filter == formats[1])
00538 {
00539 appendFilenameSuffix(fileName, "tex");
00540 if (filter == formats[0]) latexHeader = true;
00541 }
00542 else if (filter == formats[2])
00543 {
00544 appendFilenameSuffix(fileName, "vcg");
00545 }
00546 else
00547 {
00548 RELLOG("Incorrect filter, please report bug to developer");
00549 Q_ASSERT(0);
00550 return false;
00551 }
00552
00553 return true;
00554 }
00555
00556 void AutomataWorkSimulator::generateSlideshow()
00557 {
00558
00559 DBGLOG("Slideshow generating ...");
00560
00561 ScopedSetter<bool> scopedSetter(m_useAnimations, false);
00562
00563 reset();
00564 if (!setInput(m_dialog->getInput()))
00565 {
00566 SimulationDialog::showMessageSetInputFailed(m_dialog.data());
00567 return;
00568 }
00569
00570 QString fileName;
00571 bool latexHeader;
00572 if (!getFileName(fileName, latexHeader)) return;
00573
00574 QFile file(fileName);
00575 if (!file.open(QIODevice::WriteOnly))
00576 {
00577 RELLOG("Unable to save file " << fileName << ".");
00578 return;
00579 }
00580 QTextStream out(&file);
00581
00582 if (latexHeader)
00583 m_editor->saveLaTeXHeader(out);
00584
00585 out << "% Global slideshow settings" << endl;
00586 m_editor->saveVCSettings(out);
00587 out << endl;
00588
00589 int frameNum = 0;
00590 out << "% FRAME NUMBER " << ++frameNum << endl;
00591
00592 m_editor->saveHeader(out);
00593 saveInputState(out);
00594 m_editor->saveGraph(out);
00595 m_editor->saveFooter(out);
00596
00597 while (hasStateAfter())
00598 {
00599 processStep();
00600 out << endl;
00601 out << "% FRAME NUMBER " << ++frameNum << endl;
00602 m_editor->saveHeader(out);
00603 saveInputState(out);
00604 m_editor->saveGraph(out);
00605 m_editor->saveFooter(out);
00606 }
00607
00608 if (latexHeader)
00609 m_editor->saveLaTeXFooter(out);
00610
00611 out.setDevice(0);
00612 file.close();
00613
00614 DBGLOG("Slideshow generated sucessfully");
00615 }
00616
00617 void AutomataWorkSimulator::saveInputState(QTextStream &out)
00618 {
00619 const int x_pos = m_editor->getGridRect().x() + 1;
00620 const int y_pos = m_editor->getGridRect().y() + m_editor->getGridRect().height() + 1;
00621
00622 out << "\\VCPut{(" << x_pos << "," << y_pos << ")}" << "{";
00623
00624 DBGLOG(DBGPAR(m_inputIndex));
00625
00626 if (m_inputIndex > 0)
00627 out << "\\textbf{";
00628 int i = 0;
00629 for (; i < m_inputIndex; ++i)
00630 {
00631 Q_ASSERT(i < m_inputList.count());
00632 StringProcessor::CharacterInfo ci = m_inputList[i];
00633 QString symb = StringProcessor::isSpecialSymbol(ci.character) ? "$%1$" : "%1";
00634 out << symb.arg(ci.character);
00635 }
00636 if (m_inputIndex > 0)
00637 out << "}";
00638 for (; i < m_inputList.count(); ++i)
00639 {
00640 StringProcessor::CharacterInfo ci = m_inputList[i];
00641 QString symb = StringProcessor::isSpecialSymbol(ci.character) ? "$%1$" : "%1";
00642 out << symb.arg(ci.character);
00643 }
00644
00645 out << "}" << endl;
00646 }
00647
00648
00649
00650
00651
00652
00653
00654 SimulationDialog::SimulationDialog(Editor *editor, AutomataWorkSimulator *simulator)
00655 : QDialog(editor), m_editor(editor), m_simulator(simulator), m_bUsePrettyPrint(true)
00656 {
00657 m_edtInput = new QLineEdit(this);
00658 m_edtInput->setReadOnly(false);
00659 const QString inputTip = "Input syntax: '[character][character]...' (e.g.: 'abc')\n"
00660 "Note that special symbols is connected as in LaTeX (e.g. '\\alpha\\beta{}c')";
00661 m_edtInput->setToolTip(inputTip);
00662 m_edtInput->setWhatsThis(inputTip);
00663
00664 m_btnStepBack = new QPushButton(QIcon(":images/stepBack.png"), "", this);
00665 m_btnStepBack->setToolTip(tr("Step backward"));
00666 m_btnStepBack->setEnabled(false);
00667 connect(m_btnStepBack, SIGNAL(clicked()), simulator, SLOT(stepBackward()));
00668
00669 m_btnStepFwd = new QPushButton(QIcon(":images/stepFwd.png"), "", this);
00670 m_btnStepFwd->setToolTip(tr("Step forward"));
00671 connect(m_btnStepFwd, SIGNAL(clicked()), this, SLOT(stepForward()));
00672
00673 m_btnPlay = new QPushButton(QIcon(":images/play.png"), "", this);
00674 m_btnPlay->setToolTip(tr("Simulate"));
00675 connect(m_btnPlay, SIGNAL(clicked()), this, SLOT(play()));
00676
00677 m_btnPause = new QPushButton(QIcon(":images/pause.png"), "", this);
00678 m_btnPause->setToolTip(tr("Pause"));
00679 m_btnPause->setEnabled(false);
00680 connect(m_btnPause, SIGNAL(clicked()), simulator, SLOT(pause()));
00681
00682 m_btnReset = new QPushButton(QIcon(":images/stop.png"), "", this);
00683 m_btnReset->setToolTip(tr("Reset"));
00684 m_btnReset->setEnabled(false);
00685 connect(m_btnReset, SIGNAL(clicked()), simulator, SLOT(reset()));
00686
00687 m_textSimulation = new QTextEdit(this);
00688 m_textSimulation->setReadOnly(true);
00689
00690 QLabel *lblStepSpeed = new QLabel("Step speed:", this);
00691 m_stepSpeed = new QDoubleSpinBox(this);
00692 m_stepSpeed->setRange(SIMULATION_STEP_MIN, SIMULATION_STEP_MAX);
00693 m_stepSpeed->setValue(SIMULATION_STEP_DEFAULT);
00694 m_stepSpeed->setSingleStep(0.1);
00695 m_stepSpeed->setDecimals(1);
00696 m_stepSpeed->setSuffix(" s");
00697 connect(m_stepSpeed, SIGNAL(valueChanged(double)), this, SIGNAL(stepSpeedChanged(double)));
00698
00699 QLabel *lblAnimSpeed = new QLabel("Animation speed:", this);
00700 m_animSpeed = new QDoubleSpinBox(this);
00701 m_animSpeed->setRange(ANIM_SPEED_MIN, ANIM_SPEED_MAX);
00702 m_animSpeed->setValue(ANIM_SPEED_DEFAULT);
00703 m_animSpeed->setSingleStep(50);
00704 m_animSpeed->setDecimals(0);
00705 m_animSpeed->setSuffix(" ms");
00706 connect(m_animSpeed, SIGNAL(valueChanged(double)), this, SIGNAL(animSpeedChanged(double)));
00707
00708 m_checkUseAnim = new QCheckBox("Use animations", this);
00709 m_checkUseAnim->setChecked(true);
00710 connect(m_checkUseAnim, SIGNAL(stateChanged(int)), this, SIGNAL(useAnimChanged(int)));
00711
00712 m_checkPrettyPrint = new QCheckBox("Pretty printing", this);
00713 m_checkPrettyPrint->setChecked(true);
00714 connect(m_checkPrettyPrint, SIGNAL(stateChanged(int)), this, SLOT(prettyPrintChanged(int)));
00715
00716 m_btnGenerate = new QPushButton("&Generate slideshow", this);
00717 connect(m_btnGenerate, SIGNAL(clicked()), m_simulator, SLOT(generateSlideshow()));
00718
00719 QPushButton *closeButton = new QPushButton("&Close", this);
00720 connect(closeButton, SIGNAL(clicked()), this, SLOT(close()));
00721
00722 connect(this, SIGNAL(accepted()), this, SIGNAL(closed()));
00723 connect(this, SIGNAL(rejected()), this, SIGNAL(closed()));
00724
00725 QGridLayout *layout = new QGridLayout(this);
00726 int i = 0;
00727 layout->addWidget(m_edtInput, i++, 0, 1, 5);
00728 layout->addWidget(m_btnStepBack, i, 0);
00729 layout->addWidget(m_btnStepFwd, i, 1);
00730 layout->addWidget(m_btnPlay, i, 2);
00731 layout->addWidget(m_btnPause, i, 3);
00732 layout->addWidget(m_btnReset, i++, 4);
00733 layout->addWidget(m_textSimulation, i++, 0, 1, 5);
00734 layout->addWidget(lblAnimSpeed, i, 0);
00735 layout->addWidget(m_animSpeed, i, 1);
00736 layout->addWidget(m_checkUseAnim, i, 2);
00737 layout->addWidget(m_btnGenerate, i++, 3, 1, 2, Qt::AlignRight);
00738 layout->addWidget(lblStepSpeed, i, 0);
00739 layout->addWidget(m_stepSpeed, i, 1);
00740 layout->addWidget(m_checkPrettyPrint, i, 2);
00741 layout->addWidget(closeButton, i++, 3, 1, 2, Qt::AlignRight);
00742
00743 setWindowTitle(tr("Simulation dialog"));
00744 setWindowIcon(QIcon(":images/simulating.png"));
00745 }
00746
00747 SimulationDialog::~SimulationDialog()
00748 {
00749 DBGLOG("called");
00750 }
00751
00752 int SimulationDialog::getInputLength() const
00753 {
00754 return m_edtInput->text().length();
00755 }
00756
00757 QString SimulationDialog::getInput() const
00758 {
00759 return m_edtInput->text();
00760 }
00761
00762 void SimulationDialog::updateState()
00763 {
00764 m_edtInput->setReadOnly(m_simulator->isSet());
00765 m_btnReset->setEnabled(m_simulator->isSet());
00766
00767 m_btnPause->setEnabled(m_simulator->isPlaying());
00768
00769 m_btnStepBack->setEnabled(m_simulator->hasStateBefore());
00770 m_btnStepFwd->setEnabled(!m_simulator->isSet() || m_simulator->hasStateAfter());
00771 m_btnPlay->setEnabled(!m_simulator->isSet() || (m_simulator->hasStateAfter() && !m_simulator->isPlaying()));
00772
00773 int pos = m_simulator->getPos();
00774 m_edtInput->setSelection(0, pos);
00775
00776 m_textSimulation->clear();
00777 AutomataWorkSimulator::TConfigurationList info = m_simulator->getConfigurations();
00778 for(AutomataWorkSimulator::TConfigurationList::ConstIterator confIt = info.begin();
00779 confIt != info.end();
00780 ++confIt)
00781 {
00782 const AutomataWorkSimulator::Configuration &conf = *confIt;
00783
00784 appendSymbol("(");
00785
00786 QStringList symbols = conf.activeStates;
00787 if (symbols.count() > 1) appendSymbol("{");
00788 bool forFirst = true;
00789 foreach(const QString &symb, symbols)
00790 {
00791 if (!forFirst) appendSymbol(",");
00792 else forFirst = false;
00793 appendSymbol(symb);
00794 }
00795 if (symbols.count() > 1) appendSymbol("}");
00796
00797 appendSymbol(",");
00798
00799 symbols = conf.processed;
00800 foreach(const QString &symb, symbols)
00801 {
00802 appendSymbol(symb);
00803 }
00804
00805 appendSymbol(",");
00806
00807 symbols = conf.input;
00808 foreach(const QString &symb, symbols)
00809 {
00810 appendSymbol(symb);
00811 }
00812
00813 appendSymbol(")");
00814 if ((confIt+1 != info.end()) || m_simulator->hasStateAfter())
00815 {
00816 appendSymbol(" ");
00817 appendSymbol("\\vdash");
00818 }
00819
00820 switch(conf.status)
00821 {
00822 case AutomataWorkSimulator::Configuration::eAccepted:
00823 appendSymbol(" ACCEPT");
00824 break;
00825 case AutomataWorkSimulator::Configuration::eError:
00826 appendSymbol(" ERROR");
00827 break;
00828 default:
00829 break;
00830 }
00831 appendSymbol("\n");
00832 }
00833 }
00834
00835 void SimulationDialog::appendSymbol(const QString &symb)
00836 {
00837 if (!m_bUsePrettyPrint)
00838 {
00839 m_textSimulation->textCursor().insertText(symb);
00840 if (StringProcessor::isSpecialSymbol(symb))
00841 m_textSimulation->textCursor().insertText("{}");
00842 return;
00843 }
00844
00845 QString curFamily;
00846 QString symbol;
00847 const QString prevFamily = m_textSimulation->fontFamily();
00848
00849 symbol = StringProcessor::getSymbolPrintInfo(symb, curFamily);
00850
00851 m_textSimulation->setFontFamily(curFamily);
00852 m_textSimulation->textCursor().insertText(symbol);
00853
00854 m_textSimulation->setFontFamily(prevFamily);
00855 }
00856
00857 void SimulationDialog::prettyPrintChanged(int status)
00858 {
00859 m_bUsePrettyPrint = (status == Qt::Checked);
00860 updateState();
00861 }
00862
00863 void SimulationDialog::stepForward()
00864 {
00865 set();
00866
00867 if (!m_simulator->isSet()) return;
00868
00869 m_simulator->stepForward();
00870 }
00871
00872 void SimulationDialog::play()
00873 {
00874 set();
00875
00876 if (!m_simulator->isSet()) return;
00877
00878 m_simulator->play();
00879 }
00880
00881 void SimulationDialog::set()
00882 {
00883 if (m_simulator->isSet()) return;
00884
00885 if (!setInput())
00886 {
00887 showMessageSetInputFailed(this);
00888 return;
00889 }
00890 }
00891
00892 bool SimulationDialog::setInput()
00893 {
00894 if (m_edtInput->text() == "")
00895 return false;
00896
00897 return m_simulator->setInput(m_edtInput->text());
00898 }
00899
00900 void SimulationDialog::showMessageSetInputFailed(QWidget *parent)
00901 {
00902 QMessageBox::information(parent, "Incorrect input",
00903 "Please set correct input before running,\n"
00904 "Be sure to use only characters in alphabet!",
00905 QMessageBox::Ok);
00906 }
00907
00908