labplot  2.8.2
About: LabPlot is an application for plotting and analysis of 2D and 3D functions and data. It is a complete rewrite of LabPlot1 and lacks in the first release a lot of features available in the predecessor. On the other hand, the GUI and the usability is more superior.
  Fossies Dox: labplot-2.8.2.tar.gz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

SignallingUndoCommand.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  File : SignallingUndoCommand.cpp
3  Project : SciDAVis / LabPlot
4  --------------------------------------------------------------------
5  Copyright : (C) 2010 Knut Franke
6  Email (use @ for *) : Knut.Franke*gmx.net
7  Description : An undo command calling a method/signal/slot on a
8  QObject on redo/undo.
9 
10  ***************************************************************************/
11 
12 /***************************************************************************
13  * *
14  * This program is free software; you can redistribute it and/or modify *
15  * it under the terms of the GNU General Public License as published by *
16  * the Free Software Foundation; either version 2 of the License, or *
17  * (at your option) any later version. *
18  * *
19  * This program is distributed in the hope that it will be useful, *
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
22  * GNU General Public License for more details. *
23  * *
24  * You should have received a copy of the GNU General Public License *
25  * along with this program; if not, write to the Free Software *
26  * Foundation, Inc., 51 Franklin Street, Fifth Floor, *
27  * Boston, MA 02110-1301 USA *
28  * *
29  ***************************************************************************/
30 
31 #include "SignallingUndoCommand.h"
32 #include <QMetaObject>
33 #include <QMetaType>
34 
35 /**
36  * \class SignallingUndoCommand
37  * \brief An undo command calling a method/signal/slot on a QObject on redo/undo.
38  *
39  * SignallingUndoCommand is a generic undo command which can be used in cases where undo/redo events
40  * need to be forwarded to given methods, signals or slots of a QObject. That is, it behaves like a
41  * cross between an undo command and a Qt signal-slot (or signal-signal) connection. Different
42  * methods can be selected for undo and redo, but they are supposed to have the same signature.
43  *
44  * The intended use case is to have SignallingUndoCommand trigger notification signals before and
45  * after one or more undo commands change some internal state; compare
46  * AbstractAspect::exec(QUndoCommand*,const char*,const char*,QGenericArgument,QGenericArgument,QGenericArgument,QGenericArgument).
47  * The advantage of separating out the signalling into this class is that the names and
48  * arguments of the signals appear in the source code of the Aspect instead of its private class or
49  * commands; this is desirable because signals are conceptually part of the public API rather than
50  * the internal implementation (compare \ref aspect "The Aspect Framework").
51  *
52  * SignallingUndoCommand uses Qt's meta object system to dynamically invoke the target method, so
53  * the class declaring the method needs to inherit from QObject and contain the Q_OBJECT macro;
54  * just as if you wanted it to participate in signal-slot connections (though the methods to be
55  * invoked need to be neither signals nor slots).
56  * Method arguments are given using the macro Q_ARG(typename, const value&). Since
57  * the variable given as "value" will likely be out of scope when undo() is called, a copy needs to
58  * be created. This uses QMetaType, which means that (non-trivial) argument types need to be
59  * registered using qRegisterMetaType() before giving them to a SignallingUndoCommand (in
60  * particular, this also goes for pointers to custom data types). The situation here is analogous
61  * to an asynchronous method invocation using QMetaMethod::invoke() with Qt::QueuedConnection.
62  */
63 
64 /**
65  * \brief Constructor.
66  *
67  * \arg \c text A description of the undo command (compare QUndoCommand::setText()).
68  * \arg \c receiver The object whose methods/signals/slots should be invoked.
69  * \arg \c redo The name of the method to be called when the command is (re-)executed; excluding the signature.
70  * \arg \c undo Analogously to redo, the method to be called when the command is undone.
71  * \arg <tt>val0,val1,val2,val3</tt> Arguments to the undo and redo methods; to be given using Q_ARG().
72  *
73  * Simple example:
74  * \code
75  * QUndoStack stack;
76  * QAction action;
77  * stack.push(new SignallingUndoCommand(i18n("enable action"), &action, "setEnabled", "setDisabled", Q_ARG(bool, true)));
78  * \endcode
79  */
80 SignallingUndoCommand::SignallingUndoCommand(const QString &text, QObject *receiver, const char *redoMethod, const char *undoMethod,
81  QGenericArgument val0, QGenericArgument val1,
82  QGenericArgument val2, QGenericArgument val3)
83  : QUndoCommand(text),
84  m_redo(redoMethod),
85  m_undo(undoMethod),
86  m_receiver(receiver)
87 {
88  // munge arguments
89  const char *type_names[] = { val0.name(), val1.name(), val2.name(), val3.name() };
90  void *argument_data[] = { val0.data(), val1.data(), val2.data(), val3.data() };
91  for (m_argument_count = 0; qstrlen(type_names[m_argument_count]) > 0; ++m_argument_count);
92 
93  // copy arguments (Q_ARG references will often go out of scope before redo/undo are called)
95  Q_CHECK_PTR(m_argument_types);
96  m_argument_data = new void*[m_argument_count];
97  Q_CHECK_PTR(m_argument_data);
98  for (int i = 0; i < m_argument_count; i++) {
99  m_argument_types[i] = QMetaType::type(type_names[i]);
100  if (m_argument_types[i]) // type is known to QMetaType
101  m_argument_data[i] = QMetaType::create(m_argument_types[i], argument_data[i]);
102  else
103  qWarning("SignallingUndoCommand: failed to copy unknown type %s"
104  " (needs to be registered with qRegisterMetaType())!\n", type_names[i]);
105  }
106 }
107 
109  for (int i = 0; i < m_argument_count; ++i)
110  if (m_argument_types[i] && m_argument_data[i])
111  QMetaType::destroy(m_argument_types[i], m_argument_data[i]);
112  delete[] m_argument_types;
113  delete[] m_argument_data;
114 }
115 
116 QGenericArgument SignallingUndoCommand::arg(int index) {
117  if (index >= m_argument_count)
118  return QGenericArgument{};
119 
120  return QGenericArgument{QMetaType::typeName(m_argument_types[index]), m_argument_data[index]};
121 }
122 
124  const QMetaObject *mo = m_receiver->metaObject();
125  if (!mo->invokeMethod(m_receiver, m_redo, arg(0), arg(1), arg(2), arg(3)))
126  qWarning("FAILED to invoke %s on %s\n", m_redo.constData(), mo->className());
127 }
128 
130  const QMetaObject *mo = m_receiver->metaObject();
131  if (!mo->invokeMethod(m_receiver, m_undo, arg(0), arg(1), arg(2), arg(3)))
132  qWarning("FAILED to invoke %s on %s\n", m_undo.constData(), mo->className());
133 }
134 
SignallingUndoCommand(const QString &text, QObject *receiver, const char *redoMethod, const char *undoMethod, QGenericArgument val0=QGenericArgument(), QGenericArgument val1=QGenericArgument(), QGenericArgument val2=QGenericArgument(), QGenericArgument val3=QGenericArgument())
Constructor.
QGenericArgument arg(int index)