HAL
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>
12 #include "hal_core/utilities/log.h"
13 #include <QTextCursor>
14 #include <QMessageBox>
15 #include <QApplication>
16 
17 namespace hal
18 {
19  UserActionManager* UserActionManager::inst = nullptr;
20 
21  UserActionManager::UserActionManager(QObject *parent)
22  : QObject(parent), mStartRecording(-1),
23  mRecordHashAttribute(true),
24  mDumpAction(nullptr),
25  mThreadedAction(nullptr)
26  {
27  mElapsedTime.start();
28  mSettingDumpAction = new SettingsItemCheckbox(
29  "UserAction Debug",
30  "debug/user_action",
31  false,
32  "eXpert Settings:Debug",
33  "Specifies whether hal opens an extra window to list all executed instances of UserAction"
34  );
35  connect(mSettingDumpAction,&SettingsItemCheckbox::boolChanged,this,&UserActionManager::handleSettingDumpActionChanged);
36  connect(this,&UserActionManager::triggerExecute,this,&UserActionManager::handleTriggerExecute,Qt::BlockingQueuedConnection);
37  }
38 
39  void UserActionManager::executeActionBlockThread(UserAction *act)
40  {
41  if (!act) return;
42  mMutex.lock();
43  mThreadedAction = act;
44  Q_EMIT triggerExecute();
45  mMutex.unlock();
46  }
47 
48  void UserActionManager::handleTriggerExecute()
49  {
50  mThreadedAction->exec();
51  }
52 
53  void UserActionManager::handleSettingDumpActionChanged(bool wantDump)
54  {
55  if (!wantDump && mDumpAction)
56  {
57  mDumpAction->deleteLater();
58  mDumpAction = nullptr;
59  }
60  }
61 
62  void UserActionManager::addExecutedAction(UserAction* act)
63  {
64  mActionHistory.append(act);
65 
66  if (mSettingDumpAction->value().toBool())
67  {
68  if (!mDumpAction)
69  {
70  mDumpAction = new QPlainTextEdit;
71  mDumpAction->show();
72  }
73 
74  mDumpAction->moveCursor (QTextCursor::End);
75  mDumpAction->insertPlainText(act->debugDump());
76  mDumpAction->moveCursor(QTextCursor::End);
77  }
78  testUndo();
79  }
80 
81  void UserActionManager::setStartRecording()
82  {
83  mStartRecording = mActionHistory.size();
84  }
85 
86  void UserActionManager::crashDump(int sig)
87  {
88  mStartRecording = 0;
89  mRecordHashAttribute = false;
90  setStopRecording(QString("hal_crashdump_signal%1.xml").arg(sig));
91  }
92 
93  QMessageBox::StandardButton UserActionManager::setStopRecording(const QString& macroFilename)
94  {
95  int n = mActionHistory.size();
96  if (n>mStartRecording && !macroFilename.isEmpty())
97  {
98  QFile of(macroFilename);
99  if (of.open(QIODevice::WriteOnly))
100  {
101  QXmlStreamWriter xmlOut(&of);
102  xmlOut.setAutoFormatting(true);
103  xmlOut.writeStartDocument();
104  xmlOut.writeStartElement("actions");
105 
106  for (int i=mStartRecording; i<n; i++)
107  {
108  const UserAction* act = mActionHistory.at(i);
109  xmlOut.writeStartElement(act->tagname());
110  // TODO : enable / disable timestamp and crypto hash by user option ?
111  xmlOut.writeAttribute("ts",QString::number(act->timeStamp()));
112  if (mRecordHashAttribute)
113  xmlOut.writeAttribute("sha",act->cryptographicHash(i-mStartRecording));
114  if (act->compoundOrder() >= 0)
115  xmlOut.writeAttribute("compound",QString::number(act->compoundOrder()));
116  act->object().writeToXml(xmlOut);
117 //perhaps put this in all actions that need a parentobject? could be redundant though
118 // if(act->parentObject().type() != UserActionObjectType::None)
119 // {
120 // xmlOut.writeStartElement("parentObj");
121 // act->parentObject().writeToXml(xmlOut);
122 // xmlOut.writeEndElement();
123 // }
124  act->writeToXml(xmlOut);
125  xmlOut.writeEndElement();
126  }
127  xmlOut.writeEndElement();
128  xmlOut.writeEndDocument();
129  }
130  else
131  {
132  log_warning("gui", "Failed to save macro to '{}.", macroFilename.toStdString());
134  QMessageBox::warning(qApp->activeWindow(), "Save Macro Failed", "Cannot save macro to file\n<" + macroFilename + ">",
136  if (retval == QMessageBox::Discard)
137  mStartRecording = -1;
138  return retval;
139  }
140  }
141  mStartRecording = -1;
142  return QMessageBox::Ok;
143  }
144 
145  void UserActionManager::playMacro(const QString& macroFilename)
146  {
147  QFile ff(macroFilename);
148  bool parseActions = false;
149  if (!ff.open(QIODevice::ReadOnly)) return;
150  QXmlStreamReader xmlIn(&ff);
151  mStartRecording = mActionHistory.size();
152  while (!xmlIn.atEnd())
153  {
154  if (xmlIn.readNext())
155  {
156  if (xmlIn.isStartElement())
157  {
158  if (xmlIn.name() == "actions")
159  parseActions = true;
160  else if (parseActions)
161  {
162  UserAction* act = getParsedAction(xmlIn);
163  if (act) mActionHistory.append(act);
164  }
165  }
166  else if (xmlIn.isEndElement())
167  {
168  if (xmlIn.name() == "actions")
169  parseActions = false;
170  }
171  }
172  }
173  if (xmlIn.hasError())
174  {
175  // TODO : error message
176  return;
177  }
178 
179  int endMacro = mActionHistory.size();
180  for (int i=mStartRecording; i<endMacro; i++)
181  {
182  UserAction* act = mActionHistory.at(i);
183  if (!act->exec())
184  {
185  log_warning("gui", "failed to execute user action {}", act->tagname().toStdString());
186  break;
187  }
188  }
189  mStartRecording = -1;
190  }
191 
192  UserAction* UserActionManager::getParsedAction(QXmlStreamReader& xmlIn) const
193  {
194  QString actionTagName = xmlIn.name().toString();
195 
196  UserActionFactory* fac = mActionFactory.value(actionTagName);
197  if (!fac)
198  {
199  qDebug() << "cannot parse user action" << actionTagName;
200  return nullptr;
201  }
202  UserAction* retval = fac->newAction();
203  if (retval)
204  {
205  QStringRef compound = xmlIn.attributes().value("compound");
206  if (!compound.isNull() && !compound.isEmpty())
207  retval->setCompoundOrder(compound.toInt());
208  UserActionObject actObj;
209  actObj.readFromXml(xmlIn);
210  retval->setObject(actObj);
211  retval->readFromXml(xmlIn);
212  }
213 
214  return retval;
215  }
216 
217  bool UserActionManager::hasRecorded() const
218  {
219  return isRecording() && mActionHistory.size() > mStartRecording;
220  }
221 
222  bool UserActionManager::isRecording() const
223  {
224  return mStartRecording >= 0;
225  }
226 
227  void UserActionManager::registerFactory(UserActionFactory* fac)
228  {
229  mActionFactory.insert(fac->tagname(),fac);
230  }
231 
232  UserActionManager* UserActionManager::instance()
233  {
234  if (!inst) inst = new UserActionManager;
235  return inst;
236  }
237 
238  void UserActionManager::testUndo()
239  {
240  bool yesWeCan = true;
241  if (mActionHistory.isEmpty())
242  yesWeCan = false;
243  else
244  {
245  auto it = mActionHistory.end() - 1;
246  // compound can be reversed only if all actions have undo pointer
247  while (it != mActionHistory.begin() &&
248  (*it)->undoAction() &&
249  (*it)->compoundOrder() > 0)
250  --it;
251  if (!(*it)->undoAction())
252  yesWeCan = false;
253  }
254  Q_EMIT canUndoLastAction(yesWeCan);
255  }
256 
257  void UserActionManager::undoLastAction()
258  {
259  if (mActionHistory.isEmpty()) return;
260  QList<UserAction*> undoList;
261  while (!mActionHistory.isEmpty())
262  {
263  UserAction* lastAction = mActionHistory.takeLast();
264  if (!lastAction->undoAction())
265  return;
266 
267  undoList.append(lastAction->undoAction());
268  if (lastAction->compoundOrder() <= 0) break;
269  }
270  int n = mActionHistory.size();
271  for (UserAction* act : undoList)
272  act->exec();
273  while (mActionHistory.size() > n)
274  mActionHistory.takeLast();
275  testUndo();
276  }
277 }
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for and distribution as defined by Sections through of this document Licensor shall mean the copyright owner or entity authorized by the copyright owner that is granting the License Legal Entity shall mean the union of the acting entity and all other entities that control are controlled by or are under common control with that entity For the purposes of this definition control direct or to cause the direction or management of such whether by contract or including but not limited to software source documentation and configuration files Object form shall mean any form resulting from mechanical transformation or translation of a Source including but not limited to compiled object generated and conversions to other media types Work shall mean the work of whether in Source or Object made available under the as indicated by a copyright notice that is included in or attached to the whether in Source or Object that is based or other modifications as a an original work of authorship For the purposes of this Derivative Works shall not include works that remain separable or merely the Work and Derivative Works thereof Contribution shall mean any work of including the original version of the Work and any modifications or additions to that Work or Derivative Works that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner For the purposes of this submitted means any form of or written communication sent to the Licensor or its including but not limited to communication on electronic mailing source code control and issue tracking systems that are managed or on behalf of
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
n
Definition: test.py:6
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
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)