HAL  v4.5.0-83-g30c8f0afc
The Hardware Analyzer - a comprehensive reverse engineering and manipulation framework for gate-level netlists.
user_action_manager.cpp
Go to the documentation of this file.
2 #include <QFile>
3 #include <QMetaEnum>
4 #include <QDebug>
5 #include <QThread>
6 #include <QCoreApplication>
13 #include "hal_core/utilities/log.h"
14 #include <QTextCursor>
15 #include <QMessageBox>
16 #include <QApplication>
17 
18 namespace hal
19 {
20  UserActionManager* UserActionManager::inst = nullptr;
21 
22  UserActionManager::UserActionManager(QObject *parent)
23  : QObject(parent), mStartRecording(-1),
24  mRecordHashAttribute(true),
25  mDumpAction(nullptr),
26  mThreadedAction(nullptr)
27  {
28  mElapsedTime.start();
29  mSettingDumpAction = new SettingsItemCheckbox(
30  "UserAction Debug",
31  "debug/user_action",
32  false,
33  "eXpert Settings:Debug",
34  "Specifies whether hal opens an extra window to list all executed instances of UserAction"
35  );
36  connect(mSettingDumpAction,&SettingsItemCheckbox::boolChanged,this,&UserActionManager::handleSettingDumpActionChanged);
37  connect(this,&UserActionManager::triggerExecute,this,&UserActionManager::handleTriggerExecute,Qt::BlockingQueuedConnection);
38  }
39 
40  void UserActionManager::executeActionBlockThread(UserAction *act)
41  {
42  if (!act) return;
43  if (dynamic_cast<PythonThread*>(QThread::currentThread()))
44  {
45  mMutex.lock();
46  mThreadedAction = act;
47  Q_EMIT triggerExecute();
48  mMutex.unlock();
49  }
50  else
51  act->exec();
52  }
53 
54  void UserActionManager::handleTriggerExecute()
55  {
56  mThreadedAction->exec();
57  }
58 
59  void UserActionManager::handleSettingDumpActionChanged(bool wantDump)
60  {
61  if (!wantDump && mDumpAction)
62  {
63  mDumpAction->deleteLater();
64  mDumpAction = nullptr;
65  }
66  }
67 
68  void UserActionManager::addExecutedAction(UserAction* act)
69  {
70  mActionHistory.append(act);
71 
72  if (mSettingDumpAction->value().toBool())
73  {
74  if (!mDumpAction)
75  {
76  mDumpAction = new QPlainTextEdit;
77  mDumpAction->show();
78  }
79 
80  mDumpAction->moveCursor (QTextCursor::End);
81  mDumpAction->insertPlainText(act->debugDump());
82  mDumpAction->moveCursor(QTextCursor::End);
83  }
84  testUndo();
85  }
86 
87  void UserActionManager::setStartRecording()
88  {
89  mStartRecording = mActionHistory.size();
90  }
91 
92  void UserActionManager::crashDump(int sig)
93  {
94  mStartRecording = 0;
95  mRecordHashAttribute = false;
96  setStopRecording(QString("hal_crashdump_signal%1.xml").arg(sig));
97  }
98 
99  QMessageBox::StandardButton UserActionManager::setStopRecording(const QString& macroFilename)
100  {
101  int n = mActionHistory.size();
102  if (n>mStartRecording && !macroFilename.isEmpty())
103  {
104  QFile of(macroFilename);
105  if (of.open(QIODevice::WriteOnly))
106  {
107  QXmlStreamWriter xmlOut(&of);
108  xmlOut.setAutoFormatting(true);
109  xmlOut.writeStartDocument();
110  xmlOut.writeStartElement("actions");
111 
112  for (int i=mStartRecording; i<n; i++)
113  {
114  const UserAction* act = mActionHistory.at(i);
115  xmlOut.writeStartElement(act->tagname());
116  // TODO : enable / disable timestamp and crypto hash by user option ?
117  xmlOut.writeAttribute("ts",QString::number(act->timeStamp()));
118  if (mRecordHashAttribute)
119  xmlOut.writeAttribute("sha",act->cryptographicHash(i-mStartRecording));
120  if (act->compoundOrder() >= 0)
121  xmlOut.writeAttribute("compound",QString::number(act->compoundOrder()));
122  act->object().writeToXml(xmlOut);
123 //perhaps put this in all actions that need a parentobject? could be redundant though
124 // if(act->parentObject().type() != UserActionObjectType::None)
125 // {
126 // xmlOut.writeStartElement("parentObj");
127 // act->parentObject().writeToXml(xmlOut);
128 // xmlOut.writeEndElement();
129 // }
130  act->writeToXml(xmlOut);
131  xmlOut.writeEndElement();
132  }
133  xmlOut.writeEndElement();
134  xmlOut.writeEndDocument();
135  }
136  else
137  {
138  log_warning("gui", "Failed to save macro to '{}.", macroFilename.toStdString());
140  QMessageBox::warning(qApp->activeWindow(), "Save Macro Failed", "Cannot save macro to file\n<" + macroFilename + ">",
142  if (retval == QMessageBox::Discard)
143  mStartRecording = -1;
144  return retval;
145  }
146  }
147  mStartRecording = -1;
148  return QMessageBox::Ok;
149  }
150 
151  void UserActionManager::playMacro(const QString& macroFilename)
152  {
153  QFile ff(macroFilename);
154  bool parseActions = false;
155  if (!ff.open(QIODevice::ReadOnly)) return;
156  QXmlStreamReader xmlIn(&ff);
157  mStartRecording = mActionHistory.size();
158  while (!xmlIn.atEnd())
159  {
160  if (xmlIn.readNext())
161  {
162  if (xmlIn.isStartElement())
163  {
164  if (xmlIn.name() == "actions")
165  parseActions = true;
166  else if (parseActions)
167  {
168  UserAction* act = getParsedAction(xmlIn);
169  if (act) mActionHistory.append(act);
170  }
171  }
172  else if (xmlIn.isEndElement())
173  {
174  if (xmlIn.name() == "actions")
175  parseActions = false;
176  }
177  }
178  }
179  if (xmlIn.hasError())
180  {
181  // TODO : error message
182  return;
183  }
184 
185  int endMacro = mActionHistory.size();
186  for (int i=mStartRecording; i<endMacro; i++)
187  {
188  UserAction* act = mActionHistory.at(i);
189  if (!act->exec())
190  {
191  log_warning("gui", "failed to execute user action {}", act->tagname().toStdString());
192  break;
193  }
194  }
195  mStartRecording = -1;
196  }
197 
198  UserAction* UserActionManager::getParsedAction(QXmlStreamReader& xmlIn) const
199  {
200  QString actionTagName = xmlIn.name().toString();
201 
202  UserActionFactory* fac = mActionFactory.value(actionTagName);
203  if (!fac)
204  {
205  qDebug() << "cannot parse user action" << actionTagName;
206  return nullptr;
207  }
208  UserAction* retval = fac->newAction();
209  if (retval)
210  {
211  QStringRef compound = xmlIn.attributes().value("compound");
212  if (!compound.isNull() && !compound.isEmpty())
213  retval->setCompoundOrder(compound.toInt());
214  UserActionObject actObj;
215  actObj.readFromXml(xmlIn);
216  retval->setObject(actObj);
217  retval->readFromXml(xmlIn);
218  }
219 
220  return retval;
221  }
222 
223  bool UserActionManager::hasRecorded() const
224  {
225  return isRecording() && mActionHistory.size() > mStartRecording;
226  }
227 
228  bool UserActionManager::isRecording() const
229  {
230  return mStartRecording >= 0;
231  }
232 
233  void UserActionManager::registerFactory(UserActionFactory* fac)
234  {
235  mActionFactory.insert(fac->tagname(),fac);
236  }
237 
238  UserActionManager* UserActionManager::instance()
239  {
240  if (!inst) inst = new UserActionManager;
241  return inst;
242  }
243 
244  void UserActionManager::testUndo()
245  {
246  bool yesWeCan = true;
247  if (mActionHistory.isEmpty())
248  yesWeCan = false;
249  else
250  {
251  auto it = mActionHistory.end() - 1;
252  // compound can be reversed only if all actions have undo pointer
253  while (it != mActionHistory.begin() &&
254  (*it)->undoAction() &&
255  (*it)->compoundOrder() > 0)
256  --it;
257  if (!(*it)->undoAction())
258  yesWeCan = false;
259  }
260  Q_EMIT canUndoLastAction(yesWeCan);
261  }
262 
263  void UserActionManager::undoLastAction()
264  {
265  if (mActionHistory.isEmpty()) return;
266  QList<UserAction*> undoList;
267  while (!mActionHistory.isEmpty())
268  {
269  UserAction* lastAction = mActionHistory.takeLast();
270  if (!lastAction->undoAction())
271  return;
272 
273  undoList.append(lastAction->undoAction());
274  if (lastAction->compoundOrder() <= 0) break;
275  }
276  int n = mActionHistory.size();
277  for (UserAction* act : undoList)
278  act->exec();
279  while (mActionHistory.size() > n)
280  mActionHistory.takeLast();
281  testUndo();
282  }
283 }
The UserActionFactory is the abstract base class for registration.
Definition: user_action.h:225
virtual UserAction * newAction() const =0
QString tagname() const
Definition: user_action.h:242
The UserAction class is the abstract base class for user interactions.
Definition: user_action.h:57
virtual QString tagname() const =0
int compoundOrder() const
Definition: user_action.h:116
QString debugDump() const
virtual bool exec()
Definition: user_action.cpp:23
QString cryptographicHash(int recordNo) const
Definition: user_action.cpp:38
qint64 timeStamp() const
Definition: user_action.h:130
virtual void setObject(const UserActionObject &obj)
Definition: user_action.cpp:32
void setCompoundOrder(int cmpord)
Definition: user_action.h:123
virtual void readFromXml(QXmlStreamReader &xmlIn)
UserAction * undoAction() const
Definition: user_action.h:152
virtual void writeToXml(QXmlStreamWriter &xmlOut) const
virtual UserActionObject object() const
Definition: user_action.h:102
Handles and manages user actions related proccesses.
The UserActionObject class represents a single object used in UserAction.
void readFromXml(QXmlStreamReader &xmlIn)
void writeToXml(QXmlStreamWriter &xmlOut) const
#define log_warning(channel,...)
Definition: log.h:76
Definition: defines.h:45
virtual bool open(QIODevice::OpenMode mode) override
void append(const T &value)
QMessageBox::StandardButton warning(QWidget *parent, const QString &title, const QString &text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton)
bool isEmpty() const const
QString number(int n, int base)
std::string toStdString() const const
bool isEmpty() const const
bool isNull() const const
int toInt(bool *ok, int base) const const
QString toString() const const
BlockingQueuedConnection
QThread * currentThread()
void show()
QStringRef value(const QString &namespaceUri, const QString &name) const const
bool atEnd() const const
QXmlStreamAttributes attributes() const const
bool hasError() const const
bool isEndElement() const const
bool isStartElement() const const
QStringRef name() const const
QXmlStreamReader::TokenType readNext()
void setAutoFormatting(bool enable)
void writeAttribute(const QString &qualifiedName, const QString &value)
void writeEndDocument()
void writeEndElement()
void writeStartDocument()
void writeStartElement(const QString &qualifiedName)