diff mbox series

[v2,02/23] kernel-shark-qt: Add Dual Marker for KernelShark GUI.

Message ID 20181016155232.5257-3-ykaradzhov@vmware.com (mailing list archive)
State Accepted
Headers show
Series Add Qt-based GUI for KernelShark | expand

Commit Message

Yordan Karadzhov Oct. 16, 2018, 3:52 p.m. UTC
From: Yordan Karadzhov (VMware) <y.karadz@gmail.com>

This patch implements the Dual Marker used by the KernelShark GUI.
The Dual Marker uses a simple State Machine having only two states
(A and B). In the following patches the State Machine will connect
to the Table and Graph widgets (not introduced yet) and will allow
the user's actions in one of these widgets to have effect in the
other one.

Signed-off-by: Yordan Karadzhov (VMware) <y.karadz@gmail.com>
---
 kernel-shark-qt/src/CMakeLists.txt   |   2 +
 kernel-shark-qt/src/KsDualMarker.cpp | 332 +++++++++++++++++++++++++++
 kernel-shark-qt/src/KsDualMarker.hpp | 188 +++++++++++++++
 3 files changed, 522 insertions(+)
 create mode 100644 kernel-shark-qt/src/KsDualMarker.cpp
 create mode 100644 kernel-shark-qt/src/KsDualMarker.hpp

Comments

Steven Rostedt Oct. 19, 2018, 2:03 a.m. UTC | #1
On Tue, 16 Oct 2018 15:52:58 +0000
Yordan Karadzhov <ykaradzhov@vmware.com> wrote:

> +
> +/** @brief Create a Dual Marker State Machine. */
> +KsDualMarkerSM::KsDualMarkerSM(QWidget *parent)
> +: QWidget(parent),
> +  _buttonA("Marker A", this),
> +  _buttonB("Marker B", this),
> +  _labelDeltaDescr("    A,B Delta: ", this),
> +  _markA(DualMarkerState::A, Qt::darkGreen),
> +  _markB(DualMarkerState::B, Qt::darkCyan),
> +  _scCtrlA(this),
> +  _scCtrlB(this)
> +{
> +	QString styleSheetA, styleSheetB;
> +
> +	_buttonA.setFixedWidth(STRING_WIDTH(" Marker A "));
> +	_buttonB.setFixedWidth(STRING_WIDTH(" Marker B "));
> +
> +	for (auto const &l: {&_labelMA, &_labelMB, &_labelDelta}) {
> +		l->setFrameStyle(QFrame::Panel | QFrame::Sunken);
> +		l->setStyleSheet("QLabel {background-color : white;}");
> +		l->setTextInteractionFlags(Qt::TextSelectableByMouse);
> +		l->setFixedWidth(FONT_WIDTH * 16);

As a general rule, no "magic numbers". Why 16?

> +	}
> +
> +	styleSheetA = "background : " +
> +		      _markA._color.name() +
> +		      "; color : white";
> +
> +	_stateA = new QState;
> +	_stateA->setObjectName("A");
> +	_stateA->assignProperty(&_buttonA,
> +				"styleSheet",
> +				styleSheetA);
> +
> +	_stateA->assignProperty(&_buttonB,
> +				"styleSheet",
> +				"color : rgb(70, 70, 70)");

Same for the 70s.

> +
> +	styleSheetB = "background : " +
> +		      _markB._color.name() +
> +		      "; color : white";
> +
> +	_stateB = new QState;
> +	_stateB->setObjectName("B");
> +	_stateB->assignProperty(&_buttonA,
> +				"styleSheet",
> +				"color : rgb(70, 70, 70)");

Here too.

> +
> +	_stateB->assignProperty(&_buttonB,
> +				"styleSheet",
> +				styleSheetB);
> +
> +	/* Define transitions from State A to State B. */
> +	_stateA->addTransition(this,	&KsDualMarkerSM::machineToB, _stateB);
> +
> +	_scCtrlA.setKey(Qt::CTRL + Qt::Key_A);
> +	_stateA->addTransition(&_scCtrlB, &QShortcut::activated, _stateB);
> +
> +	connect(&_scCtrlA,	&QShortcut::activated,
> +		this,		&KsDualMarkerSM::_doStateA);
> +
> +	_stateA->addTransition(&_buttonB, &QPushButton::clicked, _stateB);
> +
> +	connect(&_buttonB,	&QPushButton::clicked,
> +		this,		&KsDualMarkerSM::_doStateB);
> +
> +	/* Define transitions from State B to State A. */
> +	_stateB->addTransition(this,	&KsDualMarkerSM::machineToA, _stateA);
> +
> +	_scCtrlB.setKey(Qt::CTRL + Qt::Key_B);
> +	_stateB->addTransition(&_scCtrlA, &QShortcut::activated, _stateA);
> +
> +	connect(&_scCtrlB,	&QShortcut::activated,
> +		this,		&KsDualMarkerSM::_doStateB);
> +
> +	_stateB->addTransition(&_buttonA, &QPushButton::clicked, _stateA);
> +
> +	connect(&_buttonA,	&QPushButton::clicked,
> +		this,		&KsDualMarkerSM::_doStateA);
> +
> +	_machine.addState(_stateA);
> +	_machine.addState(_stateB);
> +	_machine.setInitialState(_stateA);
> +	_markState = DualMarkerState::A;
> +	_machine.start();
> +}
> +



> +/**
> + * @brief Use this function to update the labels when the state of the model
> + *	  has changed.
> + */
> +void KsDualMarkerSM::updateLabels()
> +{
> +	QString mark, delta;
> +
> +	// Marker A
> +	if (_markA._isSet) {
> +		mark = KsUtils::Ts2String(_markA._ts, 7);

Same for these 7s.

> +		_labelMA.setText(mark);
> +	} else {
> +		_labelMA.setText("");
> +	}
> +
> +	// Marker B
> +	if (_markB._isSet) {
> +		mark = KsUtils::Ts2String(_markB._ts, 7);
> +		_labelMB.setText(mark);
> +	} else {
> +		_labelMB.setText("");
> +	}
> +
> +	// Delta
> +	if (_markA._isSet && _markB._isSet) {
> +		delta = KsUtils::Ts2String(_markB._ts - _markA._ts, 7);
> +		_labelDelta.setText(delta);
> +	} else {
> +		_labelDelta.setText("");
> +	}
> +}

Don't resend this patch (I'm going to just take it). Send a patch on
top that just replaces the hard coded numbers with meaningful macros.

Thanks!

-- Steve
Steven Rostedt Oct. 19, 2018, 2:05 a.m. UTC | #2
On Tue, 16 Oct 2018 15:52:58 +0000
Yordan Karadzhov <ykaradzhov@vmware.com> wrote:

> +
> +/** Get the Graph marker associated with a given state. */ 

Also be careful about extre whitespace. There's a extra space at the
end of the above line.

(I removed it in the commit).

-- Steve

> +KsGraphMark &KsDualMarkerSM::getMarker(DualMarkerState s)
> +{
> +	if (s == DualMarkerState::A)
> +		return _markA;
> +
> +	return _markB;
> +}
Yordan Karadzhov Oct. 19, 2018, 7:41 a.m. UTC | #3
On 19.10.2018 05:03, Steven Rostedt wrote:
> On Tue, 16 Oct 2018 15:52:58 +0000
> Yordan Karadzhov <ykaradzhov@vmware.com> wrote:
> 
>> +
>> +/** @brief Create a Dual Marker State Machine. */
>> +KsDualMarkerSM::KsDualMarkerSM(QWidget *parent)
>> +: QWidget(parent),
>> +  _buttonA("Marker A", this),
>> +  _buttonB("Marker B", this),
>> +  _labelDeltaDescr("    A,B Delta: ", this),
>> +  _markA(DualMarkerState::A, Qt::darkGreen),
>> +  _markB(DualMarkerState::B, Qt::darkCyan),
>> +  _scCtrlA(this),
>> +  _scCtrlB(this)
>> +{
>> +	QString styleSheetA, styleSheetB;
>> +
>> +	_buttonA.setFixedWidth(STRING_WIDTH(" Marker A "));
>> +	_buttonB.setFixedWidth(STRING_WIDTH(" Marker B "));
>> +
>> +	for (auto const &l: {&_labelMA, &_labelMB, &_labelDelta}) {
>> +		l->setFrameStyle(QFrame::Panel | QFrame::Sunken);
>> +		l->setStyleSheet("QLabel {background-color : white;}");
>> +		l->setTextInteractionFlags(Qt::TextSelectableByMouse);
>> +		l->setFixedWidth(FONT_WIDTH * 16);
> 
> As a general rule, no "magic numbers". Why 16?
> 

> 
> Don't resend this patch (I'm going to just take it). Send a patch on
> top that just replaces the hard coded numbers with meaningful macros.
> 

Hi Steven,

This is one of these cases when I disagree with you.
I don't see anything wrong with the line

l->setFixedWidth(FONT_WIDTH * 16);

or the many other lines in the code which are similar to this one.
It sets the width of the label to be 16 times the width of a typical 
character shown by Qt at the particular resolution of your screen.
The line is perfectly self-explanatory. And there is nothing magic in 
the number 16. It is just a normal number that everyone can understand 
end modify if this is necessary. Why 16? Because I tried different sizes 
and I found that this particular one looks nicer to me. If you find this 
size ugly, it is absolutist trivial to change it.

Now let's look in the alternative that you suggest (if I understand 
correctly):

l->setFixedWidth(DUAL_MARKER_TOOLBAR_LABEL_WIDTH);

This line is a magic word, because it tells you absolutist nothing about 
the actual size of this label. And if we apply this rule everywhere, we 
will need a ton of macros, with very long and in the same time very 
similar names. This will also make the geometry of the GUI completely 
hard-coded and will eliminate any possibility of making these sizes 
configurable via the Json I/O.

I do not believe in "general rules". Each rule has a field of 
applicability. When you program GUIs you need to apply fine tuning. I 
don't think we can avoid this.
Thanks!

Yordan

> Thanks!
> 
> -- Steve
>
diff mbox series

Patch

diff --git a/kernel-shark-qt/src/CMakeLists.txt b/kernel-shark-qt/src/CMakeLists.txt
index 2ac79ca..dc86cdf 100644
--- a/kernel-shark-qt/src/CMakeLists.txt
+++ b/kernel-shark-qt/src/CMakeLists.txt
@@ -32,11 +32,13 @@  if (Qt5Widgets_FOUND AND Qt5Network_FOUND)
 
     message(STATUS "libkshark-gui")
     set (ks-guiLib_hdr  KsUtils.hpp
+                        KsDualMarker.hpp
                         KsWidgetsLib.hpp)
 
     QT5_WRAP_CPP(ks-guiLib_hdr_moc ${ks-guiLib_hdr})
 
     add_library(kshark-gui  SHARED  ${ks-guiLib_hdr_moc}    KsUtils.cpp
+                                                            KsDualMarker.cpp
                                                             KsWidgetsLib.cpp)
 
     target_link_libraries(kshark-gui kshark-plot
diff --git a/kernel-shark-qt/src/KsDualMarker.cpp b/kernel-shark-qt/src/KsDualMarker.cpp
new file mode 100644
index 0000000..ae637aa
--- /dev/null
+++ b/kernel-shark-qt/src/KsDualMarker.cpp
@@ -0,0 +1,332 @@ 
+// SPDX-License-Identifier: LGPL-2.1
+
+/*
+ * Copyright (C) 2017 VMware Inc, Yordan Karadzhov <ykaradzhov@vmware.com>
+ */
+
+/**
+ *  @file    KsDualMarker.cpp
+ *  @brief   KernelShark Dual Marker.
+ */
+
+#include "KsDualMarker.hpp"
+
+/**
+ * @brief Create KsGraphMark object.
+ *
+ * @param s: The Identifier of the marker (state A or state B).
+ */
+KsGraphMark::KsGraphMark(DualMarkerState s)
+: _color(Qt::darkGreen),
+  _state(s)
+{
+	reset();
+	_mark._color << _color;
+}
+
+/**
+ * @brief Create KsGraphMark object.
+ *
+ * @param s: The Identifier of the marker (state A or state B).
+ * @param col: The color of the marker.
+ */
+KsGraphMark::KsGraphMark(DualMarkerState s, QColor col)
+: _color(col),
+  _state(s)
+{
+	reset();
+	_mark._color << _color;
+}
+
+/** Reset the marker. */
+void KsGraphMark::reset()
+{
+	_isSet = false;
+	_bin = -1;
+	_cpu = -1;
+	_task = -1;
+	_pos = 0;
+
+	_mark._visible = false;
+}
+
+/**
+ * @brief Set the marker.
+ *
+ * @param data: Input location for the Data Store object.
+ * @param histo: Input location for the model descriptor.
+ * @param pos: The index inside the data array this marker will points to.
+ * @param cpuGraph: The index of the CPU Graph this marker points to.
+ * @param taskGraph: The index of the Task Graph this marker points to.
+ */
+bool KsGraphMark::set(const KsDataStore &data,
+		      kshark_trace_histo *histo,
+		      size_t pos, int cpuGraph, int taskGraph)
+{
+	_isSet = true;
+	_pos = pos;
+	_ts = data.rows()[_pos]->ts;
+	_cpu = cpuGraph;
+	_task = taskGraph;
+
+	if (_ts > histo->max || _ts < histo->min) {
+		_bin = -1;
+		_mark._visible = false;
+		return false;
+	}
+
+	_bin = (_ts - histo->min)/histo->bin_size;
+	setVisible(true);
+
+	return true;
+}
+
+/**
+ * @brief Use this function to update the marker when the state of the model
+ *	  has changed.
+ *
+ * @param data: Input location for the Data Store object.
+ * @param histo: Input location for the model descriptor.
+ */
+bool KsGraphMark::update(const KsDataStore &data, kshark_trace_histo *histo)
+{
+	if (!_isSet)
+		return false;
+
+	return set(data, histo, this->_pos, this->_cpu, this->_task);
+}
+
+/** Unset the Marker and make it invisible. */
+void KsGraphMark::remove()
+{
+	_isSet = false;
+	setVisible(false);
+}
+
+/** An operator for getting the opposite state of the marker identifier. */
+DualMarkerState operator!(const DualMarkerState &state)
+{
+	if (state == DualMarkerState::B)
+		return DualMarkerState::A;
+
+	return DualMarkerState::B;
+}
+
+/** @brief Create a Dual Marker State Machine. */
+KsDualMarkerSM::KsDualMarkerSM(QWidget *parent)
+: QWidget(parent),
+  _buttonA("Marker A", this),
+  _buttonB("Marker B", this),
+  _labelDeltaDescr("    A,B Delta: ", this),
+  _markA(DualMarkerState::A, Qt::darkGreen),
+  _markB(DualMarkerState::B, Qt::darkCyan),
+  _scCtrlA(this),
+  _scCtrlB(this)
+{
+	QString styleSheetA, styleSheetB;
+
+	_buttonA.setFixedWidth(STRING_WIDTH(" Marker A "));
+	_buttonB.setFixedWidth(STRING_WIDTH(" Marker B "));
+
+	for (auto const &l: {&_labelMA, &_labelMB, &_labelDelta}) {
+		l->setFrameStyle(QFrame::Panel | QFrame::Sunken);
+		l->setStyleSheet("QLabel {background-color : white;}");
+		l->setTextInteractionFlags(Qt::TextSelectableByMouse);
+		l->setFixedWidth(FONT_WIDTH * 16);
+	}
+
+	styleSheetA = "background : " +
+		      _markA._color.name() +
+		      "; color : white";
+
+	_stateA = new QState;
+	_stateA->setObjectName("A");
+	_stateA->assignProperty(&_buttonA,
+				"styleSheet",
+				styleSheetA);
+
+	_stateA->assignProperty(&_buttonB,
+				"styleSheet",
+				"color : rgb(70, 70, 70)");
+
+	styleSheetB = "background : " +
+		      _markB._color.name() +
+		      "; color : white";
+
+	_stateB = new QState;
+	_stateB->setObjectName("B");
+	_stateB->assignProperty(&_buttonA,
+				"styleSheet",
+				"color : rgb(70, 70, 70)");
+
+	_stateB->assignProperty(&_buttonB,
+				"styleSheet",
+				styleSheetB);
+
+	/* Define transitions from State A to State B. */
+	_stateA->addTransition(this,	&KsDualMarkerSM::machineToB, _stateB);
+
+	_scCtrlA.setKey(Qt::CTRL + Qt::Key_A);
+	_stateA->addTransition(&_scCtrlB, &QShortcut::activated, _stateB);
+
+	connect(&_scCtrlA,	&QShortcut::activated,
+		this,		&KsDualMarkerSM::_doStateA);
+
+	_stateA->addTransition(&_buttonB, &QPushButton::clicked, _stateB);
+
+	connect(&_buttonB,	&QPushButton::clicked,
+		this,		&KsDualMarkerSM::_doStateB);
+
+	/* Define transitions from State B to State A. */
+	_stateB->addTransition(this,	&KsDualMarkerSM::machineToA, _stateA);
+
+	_scCtrlB.setKey(Qt::CTRL + Qt::Key_B);
+	_stateB->addTransition(&_scCtrlA, &QShortcut::activated, _stateA);
+
+	connect(&_scCtrlB,	&QShortcut::activated,
+		this,		&KsDualMarkerSM::_doStateB);
+
+	_stateB->addTransition(&_buttonA, &QPushButton::clicked, _stateA);
+
+	connect(&_buttonA,	&QPushButton::clicked,
+		this,		&KsDualMarkerSM::_doStateA);
+
+	_machine.addState(_stateA);
+	_machine.addState(_stateB);
+	_machine.setInitialState(_stateA);
+	_markState = DualMarkerState::A;
+	_machine.start();
+}
+
+/**
+ * Reset the Mark A and Mark B and clear the information shown by the Marker's
+ * toolbar.
+ */
+void KsDualMarkerSM::reset()
+{
+	_markA.reset();
+	_markB.reset();
+	_labelMA.setText("");
+	_labelMB.setText("");
+	_labelDelta.setText("");
+}
+
+/** Restart the Dual Marker State Machine. */
+void KsDualMarkerSM::restart()
+{
+	_machine.stop();
+	reset();
+	_markState = DualMarkerState::A;
+	_machine.start();
+}
+
+void KsDualMarkerSM::_doStateA()
+{
+	if (_markState !=  DualMarkerState::A) {
+		_markState = DualMarkerState::A;
+		emit markSwitchForView();
+	} else if (activeMarker()._isSet) {
+		emit updateView(activeMarker()._pos, true);
+		emit updateGraph(activeMarker()._pos);
+	}
+}
+
+void KsDualMarkerSM::_doStateB()
+{
+	if (_markState !=  DualMarkerState::B) {
+		_markState = DualMarkerState::B;
+		emit markSwitchForView();
+	} else if (activeMarker()._isSet) {
+		emit updateView(activeMarker()._pos, true);
+		emit updateGraph(activeMarker()._pos);
+	}
+}
+
+/** Get the Graph marker associated with a given state. */ 
+KsGraphMark &KsDualMarkerSM::getMarker(DualMarkerState s)
+{
+	if (s == DualMarkerState::A)
+		return _markA;
+
+	return _markB;
+}
+
+/** Position all buttons and labels of the Dual Marker in a toolbar. */
+void KsDualMarkerSM::placeInToolBar(QToolBar *tb)
+{
+	tb->addWidget(new QLabel("   "));
+	tb->addWidget(&_buttonA);
+	tb->addWidget(&_labelMA);
+	tb->addSeparator();
+	tb->addWidget(new QLabel("   "));
+	tb->addWidget(&_buttonB);
+	tb->addWidget(&_labelMB);
+	tb->addSeparator();
+	tb->addWidget(&_labelDeltaDescr);
+	tb->addWidget(&_labelDelta);
+}
+
+/** Set the state of the Dual Marker State Machine. */
+void KsDualMarkerSM::setState(DualMarkerState st) {
+	if (st == _markState)
+		return;
+
+	if (st == DualMarkerState::A) {
+		emit machineToA();
+		_doStateA();
+	}
+
+	if (st == DualMarkerState::B) {
+		emit machineToB();
+		_doStateB();
+	}
+}
+
+/**
+ * @brief Use this function to update the two marker when the state of the
+ *	  model has changed.
+ *
+ * @param data: Input location for the Data Store object.
+ * @param histo: Input location for the model descriptor.
+ */
+void KsDualMarkerSM::updateMarkers(const KsDataStore &data,
+				   kshark_trace_histo *histo)
+{
+	_markA.update(data, histo);
+	_markB.update(data, histo);
+
+	updateLabels();
+}
+
+/**
+ * @brief Use this function to update the labels when the state of the model
+ *	  has changed.
+ */
+void KsDualMarkerSM::updateLabels()
+{
+	QString mark, delta;
+
+	// Marker A
+	if (_markA._isSet) {
+		mark = KsUtils::Ts2String(_markA._ts, 7);
+		_labelMA.setText(mark);
+	} else {
+		_labelMA.setText("");
+	}
+
+	// Marker B
+	if (_markB._isSet) {
+		mark = KsUtils::Ts2String(_markB._ts, 7);
+		_labelMB.setText(mark);
+	} else {
+		_labelMB.setText("");
+	}
+
+	// Delta
+	if (_markA._isSet && _markB._isSet) {
+		delta = KsUtils::Ts2String(_markB._ts - _markA._ts, 7);
+		_labelDelta.setText(delta);
+	} else {
+		_labelDelta.setText("");
+	}
+}
diff --git a/kernel-shark-qt/src/KsDualMarker.hpp b/kernel-shark-qt/src/KsDualMarker.hpp
new file mode 100644
index 0000000..401b41c
--- /dev/null
+++ b/kernel-shark-qt/src/KsDualMarker.hpp
@@ -0,0 +1,188 @@ 
+/* SPDX-License-Identifier: LGPL-2.1 */
+
+/*
+ * Copyright (C) 2017 VMware Inc, Yordan Karadzhov <ykaradzhov@vmware.com>
+ */
+
+/**
+ *  @file    KsDualMarker.hpp
+ *  @brief   KernelShark Dual Marker.
+ */
+
+#ifndef _KS_DUAL_MARKER_H
+#define _KS_DUAL_MARKER_H
+
+// Qt
+#include <QtWidgets>
+
+// KernelShark
+#include "KsUtils.hpp"
+#include "KsPlotTools.hpp"
+
+/** The KsGraphMark represents a marker for KernelShark GUI. */
+class KsGraphMark : public QObject
+{
+	Q_OBJECT
+public:
+	KsGraphMark() = delete;
+
+	KsGraphMark(DualMarkerState s);
+
+	KsGraphMark(DualMarkerState s, QColor col);
+
+	void reset();
+
+	bool set(const KsDataStore &data,
+		 kshark_trace_histo *histo,
+		 size_t pos,
+		 int cpuGraph,
+		 int taskGraph);
+
+	bool update(const KsDataStore &data, kshark_trace_histo *histo);
+
+	/** Is this marker visible. */
+	bool isVisible() const {return _mark._visible;}
+
+	/** Draw this marker. */
+	void draw() const {_mark.draw();}
+
+	/** Set the visiblity of the marker. */
+	void setVisible(bool v) {_mark._visible = v;}
+
+	void remove();
+
+public:
+	/** Is this marker set. */
+	bool		_isSet;
+
+	/** The number of the bin this marker points to. */
+	int		_bin;
+
+	/** The index of the CPU Graph this marker points to. */
+	int		_cpu;
+
+	/** The  index of the Task Graph this marker points to. */
+	int		_task;
+
+	/** The index inside the data array this marker points to. */
+	size_t		_pos;
+
+	/** The timestamp of the marker. */
+	uint64_t	_ts;
+
+	/** The RGB color of the marker. */
+	QColor		_color;
+
+	/** The Identifier of the marker (A or B). */
+	const DualMarkerState	_state;
+
+	/** The graphical element of this marker. */
+	KsPlot::Mark		_mark;
+};
+
+DualMarkerState operator !(const DualMarkerState &state);
+
+/**
+ * The DualMarkerState represents the State Machine of the KernelShark GUI
+ * Dual Marker.
+ */
+class KsDualMarkerSM : public QWidget
+{
+	Q_OBJECT
+public:
+	explicit KsDualMarkerSM(QWidget *parent = nullptr);
+
+	void reset();
+
+	void restart();
+
+	void placeInToolBar(QToolBar *tb);
+
+	/** Get the Identifier of the current state of the State Machine. */
+	DualMarkerState getState() const {return _markState;}
+
+	void setState(DualMarkerState st);
+
+	KsGraphMark &getMarker(DualMarkerState s);
+
+	/** Get the active marker. */
+	KsGraphMark &activeMarker() {return getMarker(_markState);}
+
+	/** Get the passive marker. */
+	KsGraphMark &passiveMarker() {return getMarker(!_markState);}
+
+	/** Get the marker A. */
+	KsGraphMark &markerA() {return _markA;}
+
+	/** Get the marker B. */
+	KsGraphMark &markerB() {return _markB;}
+
+	/** Get a pointer to the State A object. */
+	QState *stateAPtr() {return _stateA;}
+
+	/** Get a pointer to the State B object. */
+	QState *stateBPtr() {return _stateB;}
+
+	void updateMarkers(const KsDataStore &data,
+			   kshark_trace_histo *histo);
+
+	void updateLabels();
+
+signals:
+	/**
+	 * This signal is emitted when the Table View has to switch the color
+	 * of the selected row.
+	 */
+	void markSwitchForView();
+
+	/**
+	 * This signal is emitted when the Table View has to show a different
+	 * entry (row).
+	 */
+	void updateView(size_t pos, bool mark);
+
+	/**
+	 * This signal is emitted when the Graph mark has to show a different
+	 * entry.
+	 */
+	void updateGraph(size_t pos);
+
+	/**
+	 * This signal is emitted when the State Machine has to switch to
+	 * state A.
+	 */
+	void machineToA();
+
+	/**
+	 * This signal is emitted when the State Machine has to switch to
+	 * state B.
+	 */
+	void machineToB();
+
+private:
+	QPushButton	 _buttonA;
+
+	QPushButton	 _buttonB;
+
+	QLabel		 _labelMA, _labelMB, _labelDelta;
+
+	QLabel		 _labelDeltaDescr;
+
+	QState		*_stateA;
+
+	QState		*_stateB;
+
+	QStateMachine	 _machine;
+
+	DualMarkerState	 _markState;
+
+	KsGraphMark	 _markA, _markB;
+
+	QShortcut        _scCtrlA, _scCtrlB;
+
+	void _doStateA();
+
+	void _doStateB();
+};
+
+#endif