diff mbox series

[09/17] kernel-shark-qt: Add centralized context menu for View and Graph widgets

Message ID 20181128151530.21965-10-ykaradzhov@vmware.com (mailing list archive)
State Accepted
Commit 4828dc763fc9295b95d5c8cae9b8fec629c581dd
Headers show
Series More modifications and bug fixes toward KS 1.0 | expand

Commit Message

Yordan Karadzhov Nov. 28, 2018, 3:16 p.m. UTC
This patch redesigns the context menu, used so far by the View
(table) widget. The goal is to make this menu universal and to
make possible to open it from both View and Graph widgets. The
context menu gets open by a right mouse click. The problem here
was, that so far the right mouse click in the Graph widget was
used for deselecting the Active marker. Now the Active marker
can be unset via the menu.

Because the new design of the Context menu requires significant
modification of the flow of signals between the widgets, the
changes cannot be broken into smaller patches. Also the
implementation of the menu is more complex now (much more code),
hence its source code has been moved from KsWidgetsLib into
separate header and source files.

Signed-off-by: Yordan Karadzhov <ykaradzhov@vmware.com>
---
 kernel-shark-qt/src/CMakeLists.txt         |   2 +
 kernel-shark-qt/src/KsGLWidget.cpp         |  46 ++--
 kernel-shark-qt/src/KsGLWidget.hpp         |  22 +-
 kernel-shark-qt/src/KsMainWindow.cpp       |  17 +-
 kernel-shark-qt/src/KsMainWindow.hpp       |   2 +
 kernel-shark-qt/src/KsQuickContextMenu.cpp | 274 +++++++++++++++++++++
 kernel-shark-qt/src/KsQuickContextMenu.hpp | 136 ++++++++++
 kernel-shark-qt/src/KsTraceGraph.cpp       |  94 +++++++
 kernel-shark-qt/src/KsTraceGraph.hpp       |  14 ++
 kernel-shark-qt/src/KsTraceViewer.cpp      |  30 ++-
 kernel-shark-qt/src/KsTraceViewer.hpp      |  32 ++-
 kernel-shark-qt/src/KsWidgetsLib.cpp       | 120 ---------
 kernel-shark-qt/src/KsWidgetsLib.hpp       |  44 ----
 13 files changed, 622 insertions(+), 211 deletions(-)
 create mode 100644 kernel-shark-qt/src/KsQuickContextMenu.cpp
 create mode 100644 kernel-shark-qt/src/KsQuickContextMenu.hpp
diff mbox series

Patch

diff --git a/kernel-shark-qt/src/CMakeLists.txt b/kernel-shark-qt/src/CMakeLists.txt
index ef0aa71..3eddaed 100644
--- a/kernel-shark-qt/src/CMakeLists.txt
+++ b/kernel-shark-qt/src/CMakeLists.txt
@@ -40,6 +40,7 @@  if (Qt5Widgets_FOUND AND Qt5Network_FOUND)
                         KsTraceViewer.hpp
                         KsMainWindow.hpp
                         KsCaptureDialog.hpp
+                        KsQuickContextMenu.hpp
                         KsAdvFilteringDialog.hpp)
 
     QT5_WRAP_CPP(ks-guiLib_hdr_moc ${ks-guiLib_hdr})
@@ -54,6 +55,7 @@  if (Qt5Widgets_FOUND AND Qt5Network_FOUND)
                                                             KsTraceViewer.cpp
                                                             KsMainWindow.cpp
                                                             KsCaptureDialog.cpp
+                                                            KsQuickContextMenu.cpp
                                                             KsAdvFilteringDialog.cpp)
 
     target_link_libraries(kshark-gui kshark-plot
diff --git a/kernel-shark-qt/src/KsGLWidget.cpp b/kernel-shark-qt/src/KsGLWidget.cpp
index 2a0b16b..917e86d 100644
--- a/kernel-shark-qt/src/KsGLWidget.cpp
+++ b/kernel-shark-qt/src/KsGLWidget.cpp
@@ -115,11 +115,6 @@  void KsGLWidget::mousePressEvent(QMouseEvent *event)
 	if (event->button() == Qt::LeftButton) {
 		_posMousePress = _posInRange(event->pos().x());
 		_rangeBoundInit(_posMousePress);
-	} else if (event->button() == Qt::RightButton) {
-		emit deselect();
-		_mState->activeMarker().remove();
-		_mState->updateLabels();
-		_model.update();
 	}
 }
 
@@ -189,8 +184,8 @@  void KsGLWidget::mouseMoveEvent(QMouseEvent *event)
 		_rangeBoundStretched(_posInRange(event->pos().x()));
 
 	bin = event->pos().x() - _hMargin;
-	cpu = _getCPU(event->pos().y());
-	pid = _getPid(event->pos().y());
+	cpu = getPlotCPU(event->pos());
+	pid = getPlotPid(event->pos());
 
 	ret = _find(bin, cpu, pid, 5, false, &row);
 	if (ret) {
@@ -613,18 +608,30 @@  KsPlot::Graph *KsGLWidget::_newTaskGraph(int pid)
 	return graph;
 }
 
-bool KsGLWidget::_find(QMouseEvent *event, int variance, bool joined,
-		       size_t *row)
+/**
+ * @brief Find the KernelShark entry under the the cursor.
+ *
+ * @param point: The position of the cursor.
+ * @param variance: The variance of the position (range) in which an entry will
+ *		    be searched.
+ * @param joined: It True, search also in the associated CPU/Task graph.
+ * @param index: Output location for the index of the entry under the cursor.
+ * 		 If no entry has been found, the outputted value is zero.
+ *
+ * @returns True, if an entry has been found, otherwise False.
+ */
+bool KsGLWidget::find(const QPoint &point, int variance, bool joined,
+		      size_t *index)
 {
 	/*
 	 * Get the bin, pid and cpu numbers.
 	 * Remember that one bin corresponds to one pixel.
 	 */
-	int bin = event->pos().x() - _hMargin;
-	int cpu = _getCPU(event->pos().y());
-	int pid = _getPid(event->pos().y());
+	int bin = point.x() - _hMargin;
+	int cpu = getPlotCPU(point);
+	int pid = getPlotPid(point);
 
-	return _find(bin, cpu, pid, variance, joined, row);
+	return _find(bin, cpu, pid, variance, joined, index);
 }
 
 int KsGLWidget::_getNextCPU(int pid, int bin)
@@ -766,9 +773,8 @@  bool KsGLWidget::_find(int bin, int cpu, int pid,
 bool KsGLWidget::_findAndSelect(QMouseEvent *event)
 {
 	size_t row;
-	bool found = _find(event, 10, true, &row);
+	bool found = find(event->pos(), 10, true, &row);
 
-	emit deselect();
 	if (found) {
 		emit select(row);
 		emit updateView(row, true);
@@ -892,9 +898,10 @@  int KsGLWidget::_posInRange(int x)
 	return posX;
 }
 
-int KsGLWidget::_getCPU(int y)
+/** Get the CPU Id of the Graph plotted at given position. */
+int KsGLWidget::getPlotCPU(const QPoint &point)
 {
-	int cpuId;
+	int cpuId, y = point.y();
 
 	if (_cpuList.count() == 0)
 		return -1;
@@ -906,9 +913,10 @@  int KsGLWidget::_getCPU(int y)
 	return _cpuList[cpuId];
 }
 
-int KsGLWidget::_getPid(int y)
+/** Get the CPU Id of the Graph plotted at given position. */
+int KsGLWidget::getPlotPid(const QPoint &point)
 {
-	int pidId;
+	int pidId, y = point.y();
 
 	if (_taskList.count() == 0)
 		return -1;
diff --git a/kernel-shark-qt/src/KsGLWidget.hpp b/kernel-shark-qt/src/KsGLWidget.hpp
index 662cd26..6fbf534 100644
--- a/kernel-shark-qt/src/KsGLWidget.hpp
+++ b/kernel-shark-qt/src/KsGLWidget.hpp
@@ -98,6 +98,13 @@  public:
 			  int *graphCPU,
 			  int *graphTask);
 
+	bool find(const QPoint &point, int variance, bool joined,
+		  size_t *index);
+
+	int getPlotCPU(const QPoint &point);
+
+	int getPlotPid(const QPoint &point);
+
 	/** CPUs to be plotted. */
 	QVector<int>	_cpuList;
 
@@ -141,13 +148,6 @@  signals:
 	 */
 	void select(size_t pos);
 
-	/**
-	 * This signal is emitted in the case of a right mouse button click or
-	 * in the case of a double click over an empty area (no visible
-	 * KernelShark entries).
-	 */
-	void deselect();
-
 	/**
 	 * This signal is emitted when the KsTraceViewer widget needs to be
 	 * updated.
@@ -195,10 +195,6 @@  private:
 
 	int _posInRange(int x);
 
-	int _getCPU(int y);
-
-	int _getPid(int y);
-
 	void _rangeBoundInit(int x);
 
 	void _rangeBoundStretched(int x);
@@ -207,8 +203,6 @@  private:
 
 	bool _findAndSelect(QMouseEvent *event);
 
-	bool _find(QMouseEvent *event, int variance, bool joined, size_t *row);
-
 	bool _find(int bin, int cpu, int pid,
 		   int variance, bool joined, size_t *row);
 
@@ -217,6 +211,8 @@  private:
 	int _getLastTask(struct kshark_trace_histo *histo, int bin, int cpu);
 
 	int _getLastCPU(struct kshark_trace_histo *histo, int bin, int pid);
+
+	void _deselect();
 };
 
 #endif
diff --git a/kernel-shark-qt/src/KsMainWindow.cpp b/kernel-shark-qt/src/KsMainWindow.cpp
index 8e8484f..5c9a0fa 100644
--- a/kernel-shark-qt/src/KsMainWindow.cpp
+++ b/kernel-shark-qt/src/KsMainWindow.cpp
@@ -93,14 +93,17 @@  KsMainWindow::KsMainWindow(QWidget *parent)
 	connect(&_view,		&KsTraceViewer::select,
 		&_graph,	&KsTraceGraph::markEntry);
 
-	connect(&_view,		&KsTraceViewer::plotTask,
+	connect(&_view,		&KsTraceViewer::addTaskPlot,
 		&_graph,	&KsTraceGraph::addTaskPlot);
 
 	connect(_graph.glPtr(), &KsGLWidget::updateView,
 		&_view,		&KsTraceViewer::showRow);
 
-	connect(_graph.glPtr(), &KsGLWidget::deselect,
-		&_view,		&KsTraceViewer::deselect);
+	connect(&_graph,	&KsTraceGraph::deselect,
+		this,		&KsMainWindow::_deselect);
+
+	connect(&_view,		&KsTraceViewer::deselect,
+		this,		&KsMainWindow::_deselect);
 
 	connect(&_data,		&KsDataStore::updateWidgets,
 		&_view,		&KsTraceViewer::update);
@@ -1040,3 +1043,11 @@  void KsMainWindow::_splitterMoved(int pos, int index)
 {
 	_session.saveSplitterSize(_splitter);
 }
+
+void KsMainWindow::_deselect()
+{
+	_view.clearSelection();
+	_mState.activeMarker().remove();
+	_mState.updateLabels();
+	_graph.glPtr()->model()->update();
+}
diff --git a/kernel-shark-qt/src/KsMainWindow.hpp b/kernel-shark-qt/src/KsMainWindow.hpp
index d711ec1..b9b681f 100644
--- a/kernel-shark-qt/src/KsMainWindow.hpp
+++ b/kernel-shark-qt/src/KsMainWindow.hpp
@@ -210,6 +210,8 @@  private:
 	void _error(const QString &text, const QString &errCode,
 		    bool resize, bool unloadPlugins);
 
+	void _deselect();
+
 private slots:
 	void _captureFinished(int, QProcess::ExitStatus);
 };
diff --git a/kernel-shark-qt/src/KsQuickContextMenu.cpp b/kernel-shark-qt/src/KsQuickContextMenu.cpp
new file mode 100644
index 0000000..0a8e527
--- /dev/null
+++ b/kernel-shark-qt/src/KsQuickContextMenu.cpp
@@ -0,0 +1,274 @@ 
+// SPDX-License-Identifier: LGPL-2.1
+
+/*
+ * Copyright (C) 2018 VMware Inc, Yordan Karadzhov <ykaradzhov@vmware.com>
+ */
+
+/**
+ *  @file    KsQuickContextMenu.cpp
+ *  @brief   Quick Context Menus for KernelShark.
+ */
+
+#include "KsQuickContextMenu.hpp"
+#include "KsTraceGraph.hpp"
+
+/**
+ * @brief Create KsQuickMarkerMenu.
+ *
+ * @param dm: The State machine of the Dual marker.
+ * @param parent: The parent of this widget.
+ */
+KsQuickMarkerMenu::KsQuickMarkerMenu(KsDualMarkerSM *dm, QWidget *parent)
+: QMenu("Context Menu", parent),
+  _dm(dm),
+  _deselectAction(this)
+{
+	if (dm->activeMarker()._isSet) {
+		addSection("Marker menu");
+		_deselectAction.setText("Deselect");
+
+		connect(&_deselectAction,	&QAction::triggered,
+			this,			&KsQuickMarkerMenu::deselect);
+
+		addAction(&_deselectAction);
+	}
+}
+
+/**
+ * @brief Create KsQuickContextMenu.
+ *
+ * @param data: Input location for the KsDataStore object.
+ * @param row: The index of the entry used to initialize the menu.
+ * @param dm: The State machine of the Dual marker.
+ * @param parent: The parent of this widget.
+ */
+KsQuickContextMenu::KsQuickContextMenu(KsDataStore *data, size_t row,
+				       KsDualMarkerSM *dm,
+				       QWidget *parent)
+: KsQuickMarkerMenu(dm, parent),
+  _data(data),
+  _row(row),
+  _hideTaskAction(this),
+  _showTaskAction(this),
+  _hideEventAction(this),
+  _showEventAction(this),
+  _addCPUPlotAction(this),
+  _addTaskPlotAction(this),
+  _removeCPUPlotAction(this),
+  _removeTaskPlotAction(this),
+  _deselectAction(this)
+{
+	typedef void (KsQuickContextMenu::*mfp)();
+	QString taskName, parentName, descr;
+	KsTraceGraph *graphs;
+	int pid, cpu;
+
+	if (!parent || !_data)
+		return;
+
+	taskName = kshark_get_task_easy(_data->rows()[_row]);
+	pid = kshark_get_pid_easy(_data->rows()[_row]);
+	cpu = _data->rows()[_row]->cpu;
+
+	auto lamAddAction = [this, &descr] (QAction *action, mfp mf) {
+		action->setText(descr);
+
+		connect(action,	&QAction::triggered,
+			this,	mf);
+
+		addAction(action);
+	};
+
+	parentName = parent->metaObject()->className();
+
+	addSection("Pointer menu");
+	descr = "Hide task [";
+	descr += taskName;
+	descr += "-";
+	descr += QString("%1").arg(pid);
+	descr += "]";
+	lamAddAction(&_hideTaskAction, &KsQuickContextMenu::_hideTask);
+
+	descr = "Show task [";
+	descr += taskName;
+	descr += "-";
+	descr += QString("%1").arg(pid);
+	descr += "] only";
+	lamAddAction(&_showTaskAction, &KsQuickContextMenu::_showTask);
+
+	descr = "Hide event [";
+	descr += kshark_get_event_name_easy(_data->rows()[_row]);
+	descr += "]";
+	lamAddAction(&_hideEventAction, &KsQuickContextMenu::_hideEvent);
+
+	descr = "Show event [";
+	descr += kshark_get_event_name_easy(_data->rows()[_row]);
+	descr += "] only";
+	lamAddAction(&_showEventAction, &KsQuickContextMenu::_showEvent);
+
+	if (parentName == "KsTraceViewer") {
+		descr = "Add [";
+		descr += taskName;
+		descr += "-";
+		descr += QString("%1").arg(pid);
+		descr += "] plot";
+		lamAddAction(&_addTaskPlotAction,
+			     &KsQuickContextMenu::_addTaskPlot);
+	}
+
+	if (parentName == "KsTraceGraph" &&
+	    (graphs = dynamic_cast<KsTraceGraph *>(parent))) {
+		if (graphs->glPtr()->_taskList.contains(pid)) {
+			descr = "Remove [";
+			descr += taskName;
+			descr += "-";
+			descr += QString("%1").arg(_data->rows()[_row]->pid);
+			descr += "] plot";
+			lamAddAction(&_removeTaskPlotAction,
+				     &KsQuickContextMenu::_removeTaskPlot);
+		} else {
+			descr = "Add [";
+			descr += taskName;
+			descr += "-";
+			descr += QString("%1").arg(_data->rows()[_row]->pid);
+			descr += "] plot";
+			lamAddAction(&_addTaskPlotAction,
+				     &KsQuickContextMenu::_addTaskPlot);
+		}
+
+		if (graphs->glPtr()->_cpuList.contains(cpu)) {
+			descr = "Remove [CPU ";
+			descr += QString("%1").arg(cpu);
+			descr += "] plot";
+			lamAddAction(&_removeCPUPlotAction,
+				     &KsQuickContextMenu::_removeCPUPlot);
+		} else {
+			descr = "Add [CPU ";
+			descr += QString("%1").arg(cpu);
+			descr += "] plot";
+			lamAddAction(&_addCPUPlotAction,
+				     &KsQuickContextMenu::_addCPUPlot);
+		}
+	}
+}
+
+void KsQuickContextMenu::_hideTask()
+{
+	int pid = kshark_get_pid_easy(_data->rows()[_row]);
+	kshark_context *kshark_ctx(nullptr);
+	QVector<int> vec;
+
+	if (!kshark_instance(&kshark_ctx))
+		return;
+
+	vec =_getFilterVector(kshark_ctx->hide_task_filter, pid);
+	_data->applyPosTaskFilter(vec);
+}
+
+void KsQuickContextMenu::_showTask()
+{
+	int pid = kshark_get_pid_easy(_data->rows()[_row]);
+
+	_data->applyPosTaskFilter(QVector<int>(1, pid));
+}
+
+void KsQuickContextMenu::_hideEvent()
+{
+	int eventId = kshark_get_event_id_easy(_data->rows()[_row]);
+	kshark_context *kshark_ctx(nullptr);
+	QVector<int> vec;
+
+	if (!kshark_instance(&kshark_ctx))
+		return;
+
+	vec =_getFilterVector(kshark_ctx->hide_event_filter, eventId);
+	_data->applyNegEventFilter(vec);
+}
+
+void KsQuickContextMenu::_showEvent()
+{
+	int eventId = kshark_get_event_id_easy(_data->rows()[_row]);
+
+	_data->applyPosEventFilter(QVector<int>(1, eventId));
+}
+
+void KsQuickContextMenu::_addTaskPlot()
+{
+	int pid = kshark_get_pid_easy(_data->rows()[_row]);
+
+	emit addTaskPlot(pid);
+}
+
+void KsQuickContextMenu::_addCPUPlot()
+{
+	emit addCPUPlot(_data->rows()[_row]->cpu);
+}
+
+void KsQuickContextMenu::_removeTaskPlot()
+{
+	int pid = kshark_get_pid_easy(_data->rows()[_row]);
+
+	emit removeTaskPlot(pid);
+}
+
+void KsQuickContextMenu::_removeCPUPlot()
+{
+	emit removeCPUPlot(_data->rows()[_row]->cpu);
+}
+
+/**
+ * @brief Create KsRmPlotContextMenu.
+ *
+ * @param dm: The State machine of the Dual marker.
+ * @param parent: The parent of this widget.
+ */
+KsRmPlotContextMenu::KsRmPlotContextMenu(KsDualMarkerSM *dm,
+					 QWidget *parent)
+: KsQuickMarkerMenu(dm, parent),
+  _removePlotAction(this)
+{
+	addSection("Plots");
+
+	connect(&_removePlotAction,	&QAction::triggered,
+		this,			&KsRmPlotContextMenu::removePlot);
+
+	addAction(&_removePlotAction);
+}
+
+/**
+ * @brief Create KsRmCPUPlotMenu.
+ *
+ * @param dm: The State machine of the Dual marker.
+ * @param cpu : CPU Id.
+ * @param parent: The parent of this widget.
+ */
+KsRmCPUPlotMenu::KsRmCPUPlotMenu(KsDualMarkerSM *dm, int cpu,
+				 QWidget *parent)
+: KsRmPlotContextMenu(dm, parent)
+{
+	_removePlotAction.setText(QString("Remove [CPU %1]").arg(cpu));
+}
+
+/**
+ * @brief Create KsRmTaskPlotMenu.
+ *
+ * @param dm: The State machine of the Dual marker.
+ * @param pid: Process Id.
+ * @param parent: The parent of this widget.
+ */
+KsRmTaskPlotMenu::KsRmTaskPlotMenu(KsDualMarkerSM *dm, int pid,
+				   QWidget *parent)
+: KsRmPlotContextMenu(dm, parent)
+{
+	kshark_context *kshark_ctx(nullptr);
+	QString descr("Remove [ ");
+
+	if (!kshark_instance(&kshark_ctx))
+		return;
+
+	descr += tep_data_comm_from_pid(kshark_ctx->pevent, pid);
+	descr += "-";
+	descr += QString("%1").arg(pid);
+	descr += "] plot";
+	_removePlotAction.setText(descr);
+}
diff --git a/kernel-shark-qt/src/KsQuickContextMenu.hpp b/kernel-shark-qt/src/KsQuickContextMenu.hpp
new file mode 100644
index 0000000..040942f
--- /dev/null
+++ b/kernel-shark-qt/src/KsQuickContextMenu.hpp
@@ -0,0 +1,136 @@ 
+/* SPDX-License-Identifier: LGPL-2.1 */
+
+/*
+ * Copyright (C) 2018 VMware Inc, Yordan Karadzhov <ykaradzhov@vmware.com>
+ */
+
+/**
+ *  @file    KsQuickContextMenu.hpp
+ *  @brief   Quick Context Menus for KernelShark.
+ */
+
+#ifndef _KS_QUICK_CTX_MENU_H
+#define _KS_QUICK_CTX_MENU_H
+
+#include "KsDualMarker.hpp"
+#include "KsUtils.hpp"
+#include "KsGLWidget.hpp"
+
+/**
+ * The KsQuickMarkerMenu class provides menu for quick Dual Marker related
+ * actions.
+ */
+class KsQuickMarkerMenu : public QMenu {
+	Q_OBJECT
+public:
+	KsQuickMarkerMenu(KsDualMarkerSM *dm, QWidget *parent = nullptr);
+
+signals:
+	/** Signal to deselect the active marker. */
+	void deselect();
+
+private:
+	KsDualMarkerSM	*_dm;
+
+	QAction _deselectAction;
+};
+
+/**
+ * The KsQuickFilterMenu class provides a menu for easy filtering and plotting.
+ * The menu is initialized from a single kshark_entry and uses the content of
+ * this entry to provides quick actions for filtering and plottin.
+ */
+class KsQuickContextMenu : public KsQuickMarkerMenu {
+	Q_OBJECT
+public:
+	KsQuickContextMenu() = delete;
+
+	KsQuickContextMenu(KsDataStore *data, size_t row,
+			   KsDualMarkerSM *dm,
+			   QWidget *parent = nullptr);
+
+signals:
+	/** Signal to add a task plot. */
+	void addTaskPlot(int);
+
+	/** Signal to add a CPU plot. */
+	void addCPUPlot(int);
+
+	/** Signal to remove a task plot. */
+	void removeTaskPlot(int);
+
+	/** Signal to remove a CPU plot. */
+	void removeCPUPlot(int);
+
+private:
+	void _hideTask();
+
+	void _showTask();
+
+	void _hideEvent();
+
+	void _showEvent();
+
+	void _addCPUPlot();
+
+	void _addTaskPlot();
+
+	void _removeCPUPlot();
+
+	void _removeTaskPlot();
+
+	KsDataStore	*_data;
+
+	size_t		_row;
+
+	QAction _hideTaskAction, _showTaskAction;
+
+	QAction _hideEventAction, _showEventAction;
+
+	QAction _addCPUPlotAction;
+
+	QAction _addTaskPlotAction;
+
+	QAction _removeCPUPlotAction;
+
+	QAction _removeTaskPlotAction;
+
+	QAction _deselectAction;
+};
+
+/**
+ * The KsQuickMarkerMenu is a baser class for Remove Plot menus.
+ */
+class KsRmPlotContextMenu : public KsQuickMarkerMenu {
+	Q_OBJECT
+public:
+	KsRmPlotContextMenu() = delete;
+
+	KsRmPlotContextMenu(KsDualMarkerSM *dm, QWidget *parent = nullptr);
+
+signals:
+	/** Signal to remove a plot. */
+	void removePlot(int);
+
+protected:
+	/** Menu action. */
+	QAction _removePlotAction;
+};
+
+/**
+ * The KsQuickMarkerMenu class provides CPU Plot remove menus.
+ */
+struct KsRmCPUPlotMenu : public KsRmPlotContextMenu {
+	KsRmCPUPlotMenu(KsDualMarkerSM *dm, int cpu,
+			QWidget *parent = nullptr);
+};
+
+/**
+ * The KsQuickMarkerMenu class provides Task Plot remove menus.
+ */
+struct KsRmTaskPlotMenu : public KsRmPlotContextMenu {
+	KsRmTaskPlotMenu(KsDualMarkerSM *dm, int pid,
+			 QWidget *parent = nullptr);
+};
+
+#endif
diff --git a/kernel-shark-qt/src/KsTraceGraph.cpp b/kernel-shark-qt/src/KsTraceGraph.cpp
index 29a5950..09b322a 100644
--- a/kernel-shark-qt/src/KsTraceGraph.cpp
+++ b/kernel-shark-qt/src/KsTraceGraph.cpp
@@ -13,6 +13,7 @@ 
 #include "KsUtils.hpp"
 #include "KsDualMarker.hpp"
 #include "KsTraceGraph.hpp"
+#include "KsQuickContextMenu.hpp"
 
 /** Create a default (empty) Trace graph widget. */
 KsTraceGraph::KsTraceGraph(QWidget *parent)
@@ -134,6 +135,10 @@  KsTraceGraph::KsTraceGraph(QWidget *parent)
 	connect(_glWindow.model(),	&KsGraphModel::modelReset,
 		this,			&KsTraceGraph::_updateTimeLegends);
 
+	_glWindow.setContextMenuPolicy(Qt::CustomContextMenu);
+	connect(&_glWindow,	&QTableView::customContextMenuRequested,
+		this,		&KsTraceGraph::_onCustomContextMenu);
+
 	_scrollArea.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
 	_scrollArea.setWidget(&_drawWindow);
 
@@ -447,6 +452,26 @@  void KsTraceGraph::addTaskPlot(int pid)
 	_selfUpdate();
 }
 
+/** Remove a CPU graph from the existing list of CPU graphs. */
+void KsTraceGraph::removeCPUPlot(int cpu)
+{
+	if (!_glWindow._cpuList.contains(cpu))
+		return;
+
+	_glWindow._cpuList.removeAll(cpu);
+	_selfUpdate();
+}
+
+/** Remove a Task graph from the existing list of Task graphs. */
+void KsTraceGraph::removeTaskPlot(int pid)
+{
+	if (!_glWindow._taskList.contains(pid))
+		return;
+
+	_glWindow._taskList.removeAll(pid);
+	_selfUpdate();
+}
+
 /** Update the content of all graphs. */
 void KsTraceGraph::update(KsDataStore *data)
 {
@@ -689,3 +714,72 @@  void KsTraceGraph::_updateGraphs(GraphActions action)
 		QCoreApplication::processEvents();
 	}
 }
+
+void KsTraceGraph::_onCustomContextMenu(const QPoint &point)
+{
+	KsQuickMarkerMenu *menu(nullptr);
+	int cpu, pid;
+	size_t row;
+	bool found;
+
+	found = _glWindow.find(point, 20, true, &row);
+	if (found) {
+		/* KernelShark entry has been found under the cursor. */
+		KsQuickContextMenu *entryMenu;
+		menu = entryMenu = new KsQuickContextMenu(_data, row,
+							  _mState, this);
+
+		connect(entryMenu,	&KsQuickContextMenu::addTaskPlot,
+			this,		&KsTraceGraph::addTaskPlot);
+
+		connect(entryMenu,	&KsQuickContextMenu::addCPUPlot,
+			this,		&KsTraceGraph::addCPUPlot);
+
+		connect(entryMenu,	&KsQuickContextMenu::removeTaskPlot,
+			this,		&KsTraceGraph::removeTaskPlot);
+
+		connect(entryMenu,	&KsQuickContextMenu::removeCPUPlot,
+			this,		&KsTraceGraph::removeCPUPlot);
+	} else {
+		cpu = _glWindow.getPlotCPU(point);
+		if (cpu >= 0) {
+			/*
+			 * This is a CPU plot, but we do not have an entry
+			 * under the cursor.
+			 */
+			KsRmCPUPlotMenu *rmMenu;
+			menu = rmMenu = new KsRmCPUPlotMenu(_mState, cpu, this);
+
+			auto lamRmPlot = [&cpu, this] () {
+				removeCPUPlot(cpu);
+			};
+
+			connect(rmMenu, &KsRmPlotContextMenu::removePlot,
+				lamRmPlot);
+		}
+
+		pid = _glWindow.getPlotPid(point);
+		if (pid >= 0) {
+			/*
+			 * This is a Task plot, but we do not have an entry
+			 * under the cursor.
+			 */
+			KsRmTaskPlotMenu *rmMenu;
+			menu = rmMenu = new KsRmTaskPlotMenu(_mState, pid, this);
+
+			auto lamRmPlot = [&pid, this] () {
+				removeTaskPlot(pid);
+			};
+
+			connect(rmMenu, &KsRmPlotContextMenu::removePlot,
+				lamRmPlot);
+		}
+	}
+
+	if (menu) {
+		connect(menu,	&KsQuickMarkerMenu::deselect,
+			this,	&KsTraceGraph::deselect);
+
+		menu->exec(mapToGlobal(point));
+	}
+}
diff --git a/kernel-shark-qt/src/KsTraceGraph.hpp b/kernel-shark-qt/src/KsTraceGraph.hpp
index 395cc1b..c53258c 100644
--- a/kernel-shark-qt/src/KsTraceGraph.hpp
+++ b/kernel-shark-qt/src/KsTraceGraph.hpp
@@ -60,6 +60,10 @@  public:
 
 	void addTaskPlot(int);
 
+	void removeCPUPlot(int);
+
+	void removeTaskPlot(int);
+
 	void update(KsDataStore *data);
 
 	void updateGeom();
@@ -68,6 +72,14 @@  public:
 
 	bool eventFilter(QObject* obj, QEvent* evt) override;
 
+signals:
+	/**
+	 * This signal is emitted in the case of a right mouse button click or
+	 * in the case of a double click over an empty area (no visible
+	 * KernelShark entries).
+	 */
+	void deselect();
+
 private:
 
 	void _zoomIn();
@@ -105,6 +117,8 @@  private:
 
 	void _updateGraphs(GraphActions action);
 
+	void _onCustomContextMenu(const QPoint &point);
+
 	QToolBar	_pointerBar, _navigationBar;
 
 	QPushButton	_zoomInButton, _quickZoomInButton;
diff --git a/kernel-shark-qt/src/KsTraceViewer.cpp b/kernel-shark-qt/src/KsTraceViewer.cpp
index 64c9fb7..d52d7e3 100644
--- a/kernel-shark-qt/src/KsTraceViewer.cpp
+++ b/kernel-shark-qt/src/KsTraceViewer.cpp
@@ -14,9 +14,22 @@ 
 #include <future>
 
 // KernelShark
+#include "KsQuickContextMenu.hpp"
 #include "KsTraceViewer.hpp"
 #include "KsWidgetsLib.hpp"
 
+/**
+ * Reimplemented handler for mouse press events. Right mouse click events will
+ * be ignored. This is done because we want the Right click is being used to
+ * open a Context menu.
+ */
+void KsTableView::mousePressEvent(QMouseEvent *e) {
+	if(e->button() == Qt::RightButton)
+		return;
+
+	QTableView::mousePressEvent(e);
+}
+
 /** Create a default (empty) Trace viewer widget. */
 KsTraceViewer::KsTraceViewer(QWidget *parent)
 : QWidget(parent),
@@ -248,10 +261,13 @@  void KsTraceViewer::_onCustomContextMenu(const QPoint &point)
 		 * of the row number in the source model.
 		 */
 		size_t row = _proxyModel.mapRowFromSource(i.row());
-		KsQuickEntryMenu menu(_data, row, this);
+		KsQuickContextMenu menu(_data, row, _mState, this);
 
-		connect(&menu,	&KsQuickEntryMenu::plotTask,
-			this,	&KsTraceViewer::plotTask);
+		connect(&menu,	&KsQuickContextMenu::addTaskPlot,
+			this,	&KsTraceViewer::addTaskPlot);
+
+		connect(&menu,	&KsQuickMarkerMenu::deselect,
+			this,	&KsTraceViewer::deselect);
 
 		menu.exec(mapToGlobal(point));
 	}
@@ -424,8 +440,10 @@  void KsTraceViewer::_clicked(const QModelIndex& i)
 	 */
 	size_t row = _proxyModel.mapRowFromSource(i.row());
 
-	_setSearchIterator(row);
-	_updateSearchCount();
+	if (_searchDone && _matchList.count()) {
+		_setSearchIterator(row);
+		_updateSearchCount();
+	}
 
 	if (_graphFollows)
 		emit select(row); // Send a signal to the Graph widget.
@@ -460,7 +478,7 @@  void KsTraceViewer::showRow(size_t r, bool mark)
 }
 
 /** Deselects the selected items (row) if any. */
-void KsTraceViewer::deselect()
+void KsTraceViewer::clearSelection()
 {
 	_view.clearSelection();
 }
diff --git a/kernel-shark-qt/src/KsTraceViewer.hpp b/kernel-shark-qt/src/KsTraceViewer.hpp
index 50c9115..4e35c17 100644
--- a/kernel-shark-qt/src/KsTraceViewer.hpp
+++ b/kernel-shark-qt/src/KsTraceViewer.hpp
@@ -20,6 +20,21 @@ 
 #include "KsModels.hpp"
 #include "KsDualMarker.hpp"
 
+/**
+ * Table View class, needed in order to reimplemented the handler for mouse
+ * press events.
+ */
+class KsTableView : public QTableView
+{
+	Q_OBJECT
+public:
+	/** Create KsTableView. */
+	explicit KsTableView(QWidget *parent = nullptr)
+	: QTableView(parent) {};
+
+	void mousePressEvent(QMouseEvent *event) override;
+};
+
 /**
  * The KsTraceViewer class provides a widget for browsing in the trace data
  * shown in a text form.
@@ -48,7 +63,7 @@  public:
 
 	void showRow(size_t r, bool mark);
 
-	void deselect();
+	void clearSelection();
 
 	void update(KsDataStore *data);
 
@@ -57,15 +72,21 @@  signals:
 	void select(size_t);
 
 	/**
-	 * This signal is used to re-emitted the plotTask signal of the
-	 * KsQuickEntryMenu.
+	 * This signal is used to re-emitted the addTaskPlot signal of the
+	 * KsQuickContextMenu.
 	 */
-	void plotTask(int pid);
+	void addTaskPlot(int pid);
+
+	/**
+	 * This signal is used to re-emitted the deselect signal of the
+	 * KsQuickMarkerMenu.
+	 */
+	void deselect();
 
 private:
 	QVBoxLayout	_layout;
 
-	QTableView	_view;
+	KsTableView	_view;
 
 	KsViewModel		_model;
 
@@ -145,7 +166,6 @@  private:
 	int _getSelectedDataRow();
 
 private slots:
-
 	void _searchEdit(int);
 };
 
diff --git a/kernel-shark-qt/src/KsWidgetsLib.cpp b/kernel-shark-qt/src/KsWidgetsLib.cpp
index 191ea7d..4b41f86 100644
--- a/kernel-shark-qt/src/KsWidgetsLib.cpp
+++ b/kernel-shark-qt/src/KsWidgetsLib.cpp
@@ -798,123 +798,3 @@  KsPluginCheckBoxWidget::KsPluginCheckBoxWidget(QStringList pluginList,
 
 	_adjustSize();
 }
-
-/**
- * @brief Create KsQuickEntryMenu.
- *
- * @param data: Input location for the KsDataStore object.
- * @param row: The index of the entry used to initialize the menu.
- * @param parent: The parent of this widget.
- */
-KsQuickEntryMenu::KsQuickEntryMenu(KsDataStore *data, size_t row,
-				   QWidget *parent)
-: QMenu("Entry menu", parent),
-  _data(data),
-  _row(row),
-  _hideTaskAction(this),
-  _showTaskAction(this),
-  _hideEventAction(this),
-  _showEventAction(this),
-  _addTaskPlotAction(this)
-{
-	QString descr;
-
-	addSection("Quick Filter menu");
-
-	descr = "Hide task [";
-	descr += kshark_get_task_easy(_data->rows()[_row]);
-	descr += "-";
-	descr += QString("%1").arg(_data->rows()[_row]->pid);
-	descr += "]";
-
-	_hideTaskAction.setText(descr);
-
-	connect(&_hideTaskAction,	&QAction::triggered,
-		this,			&KsQuickEntryMenu::_hideTask);
-
-	addAction(&_hideTaskAction);
-
-	descr = "Show task [";
-	descr += kshark_get_task_easy(_data->rows()[_row]);
-	descr += "-";
-	descr += QString("%1").arg(_data->rows()[_row]->pid);
-	descr += "] only";
-
-	_showTaskAction.setText(descr);
-
-	connect(&_showTaskAction,	&QAction::triggered,
-		this,			&KsQuickEntryMenu::_showTask);
-
-	addAction(&_showTaskAction);
-
-	descr = "Hide event [";
-	descr += kshark_get_event_name_easy(_data->rows()[_row]);
-	descr += "]";
-
-	_hideEventAction.setText(descr);
-
-	connect(&_hideEventAction,	&QAction::triggered,
-		this,			&KsQuickEntryMenu::_hideEvent);
-
-	addAction(&_hideEventAction);
-
-	descr = "Show event [";
-	descr += kshark_get_event_name_easy(_data->rows()[_row]);
-	descr += "] only";
-
-	_showEventAction.setText(descr);
-
-	connect(&_showEventAction,	&QAction::triggered,
-		this,			&KsQuickEntryMenu::_showEvent);
-
-	addAction(&_showEventAction);
-
-	addSection("Quick Plot menu");
-	descr = "Add [";
-	descr += kshark_get_task_easy(_data->rows()[_row]);
-	descr += "-";
-	descr += QString("%1").arg(_data->rows()[_row]->pid);
-	descr += "] plot";
-
-	_addTaskPlotAction.setText(descr);
-
-	connect(&_addTaskPlotAction,	&QAction::triggered,
-		this,			&KsQuickEntryMenu::_addTaskPlot);
-
-	addAction(&_addTaskPlotAction);
-}
-
-void KsQuickEntryMenu::_hideTask()
-{
-	int pid = kshark_get_pid_easy(_data->rows()[_row]);
-
-	_data->applyNegTaskFilter(QVector<int>(1, pid));
-}
-
-void KsQuickEntryMenu::_showTask()
-{
-	int pid = kshark_get_pid_easy(_data->rows()[_row]);
-
-	_data->applyPosTaskFilter(QVector<int>(1, pid));
-}
-
-void KsQuickEntryMenu::_hideEvent()
-{
-	int eventId = kshark_get_event_id_easy(_data->rows()[_row]);
-
-	_data->applyNegEventFilter(QVector<int>(1, eventId));
-}
-
-void KsQuickEntryMenu::_showEvent()
-{
-	int eventId = kshark_get_event_id_easy(_data->rows()[_row]);
-
-	_data->applyPosEventFilter(QVector<int>(1, eventId));
-}
-
-void KsQuickEntryMenu::_addTaskPlot()
-{
-	int pid = kshark_get_pid_easy(_data->rows()[_row]);
-
-	emit plotTask(pid);
-}
diff --git a/kernel-shark-qt/src/KsWidgetsLib.hpp b/kernel-shark-qt/src/KsWidgetsLib.hpp
index c09bcd5..6f22374 100644
--- a/kernel-shark-qt/src/KsWidgetsLib.hpp
+++ b/kernel-shark-qt/src/KsWidgetsLib.hpp
@@ -347,48 +347,4 @@  struct KsPluginCheckBoxWidget : public KsCheckBoxTableWidget
 			       QWidget *parent = nullptr);
 };
 
-class KsDataStore;
-class KsGLWidget;
-
-/**
- * The KsQuickFilterMenu class provides a menu for easy filtering and plotting.
- * The menu is initialized from a single kshark_entry and uses the content of
- * this entry to provides quick actions for filtering and plottin.
- */
-class KsQuickEntryMenu : public QMenu
-{
-	Q_OBJECT
-public:
-	KsQuickEntryMenu() = delete;
-
-	explicit KsQuickEntryMenu(KsDataStore *data,
-				  size_t row,
-				  QWidget *parent = nullptr);
-
-signals:
-	/** Signal to add a task plot. */
-	void plotTask(int);
-
-private:
-	void _hideTask();
-
-	void _showTask();
-
-	void _hideEvent();
-
-	void _showEvent();
-
-	void _addTaskPlot();
-
-	KsDataStore	*_data;
-
-	size_t		 _row;
-
-	QAction _hideTaskAction, _showTaskAction;
-
-	QAction _hideEventAction, _showEventAction;
-
-	QAction _addTaskPlotAction;
-};
-
 #endif