1 /**
2 * UGENE - Integrated Bioinformatics Tools.
3 * Copyright (C) 2008-2021 UniPro <ugene@unipro.ru>
4 * http://ugene.net
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 * MA 02110-1301, USA.
20 */
21
22 #include "WorkflowInvestigationWidgetsController.h"
23
24 #include <QApplication>
25 #include <QClipboard>
26 #include <QHeaderView>
27 #include <QMenu>
28 #include <QTabWidget>
29 #include <QTableView>
30 #include <QtMath>
31
32 #include "InvestigationDataModel.h"
33
34 const qint16 HEADER_TEXT_MARGIN = 40;
35 const int DEFAULT_SELECTED_COLUMN = -1;
36 const char *CONVERT_TO_DOC_ACTION_NAME = "Convert to document";
37 const char *COPY_TO_CLIPBOARD_ACTION_NAME = "Copy to clipboard";
38 const char *HIDE_SELECTED_COLUMN_ACTION_NAME = "Hide this column";
39 const char *HIDE_ALL_COLUMNS_BUT_SELECTED_ACTION_NAME = "Hide all columns but this";
40 const char *SHOW_ALL_COLUMNS_ACTION_NAME = "Show all columns";
41
42 namespace U2 {
43
WorkflowInvestigationWidgetsController(QWidget * parent)44 WorkflowInvestigationWidgetsController::WorkflowInvestigationWidgetsController(QWidget *parent)
45 : QObject(qobject_cast<QObject *>(parent)),
46 investigationView(nullptr),
47 investigationModel(nullptr),
48 investigatedLink(nullptr),
49 investigatorName(),
50 wasDisplayed(false),
51 exportInvestigationAction(nullptr),
52 copyToClipboardAction(nullptr),
53 hideThisColumnAction(nullptr),
54 hideAllColumnsButThisAction(nullptr),
55 showAllColumnsAction(nullptr),
56 selectedColumn(DEFAULT_SELECTED_COLUMN),
57 columnWidths() {
58 QTabWidget *container = dynamic_cast<QTabWidget *>(parent);
59 Q_ASSERT(nullptr != container);
60 Q_UNUSED(container);
61
62 exportInvestigationAction = new QAction(
63 QIcon(":workflow_designer/images/document_convert.png"),
64 tr(CONVERT_TO_DOC_ACTION_NAME),
65 this);
66 connect(exportInvestigationAction, SIGNAL(triggered()), SLOT(sl_exportInvestigation()));
67
68 copyToClipboardAction = new QAction(QIcon(":workflow_designer/images/clipboard.png"),
69 tr(COPY_TO_CLIPBOARD_ACTION_NAME),
70 this);
71 connect(copyToClipboardAction, SIGNAL(triggered()), SLOT(sl_copyToClipboard()));
72
73 hideThisColumnAction = new QAction(tr(HIDE_SELECTED_COLUMN_ACTION_NAME), this);
74 connect(hideThisColumnAction, SIGNAL(triggered()), SLOT(sl_hideSelectedColumn()));
75
76 hideAllColumnsButThisAction = new QAction(tr(HIDE_ALL_COLUMNS_BUT_SELECTED_ACTION_NAME), this);
77 connect(hideAllColumnsButThisAction, SIGNAL(triggered()), SLOT(sl_hideAllColumnsButSelected()));
78
79 showAllColumnsAction = new QAction(tr(SHOW_ALL_COLUMNS_ACTION_NAME), this);
80 connect(showAllColumnsAction, SIGNAL(triggered()), SLOT(sl_showAllColumns()));
81 }
82
~WorkflowInvestigationWidgetsController()83 WorkflowInvestigationWidgetsController::~WorkflowInvestigationWidgetsController() {
84 deleteBusInvestigations();
85 }
86
eventFilter(QObject * watched,QEvent * event)87 bool WorkflowInvestigationWidgetsController::eventFilter(QObject *watched, QEvent *event) {
88 if (QEvent::Paint == event->type() && nullptr != investigationView && watched == dynamic_cast<QObject *>(investigationView->viewport())) {
89 if (nullptr == investigationView->model() && nullptr != investigatedLink) {
90 createInvestigationModel();
91 investigationView->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive);
92 adjustInvestigationColumnWidth(investigationView);
93 }
94 }
95 return QObject::eventFilter(watched, event);
96 }
97
setCurrentInvestigation(const Workflow::Link * bus)98 void WorkflowInvestigationWidgetsController::setCurrentInvestigation(const Workflow::Link *bus) {
99 QTabWidget *container = dynamic_cast<QTabWidget *>(parent());
100 Q_ASSERT(nullptr != container);
101 const int tabNumberForInvestigation = container->indexOf(investigationView);
102 if (-1 != tabNumberForInvestigation) {
103 deleteBusInvestigations();
104 container->removeTab(tabNumberForInvestigation);
105 }
106 investigatedLink = bus;
107 createNewInvestigation();
108 investigationView->setParent(container);
109 investigatorName = tr("Messages from '") + bus->source()->owner()->getLabel() + tr("' to '") + bus->destination()->owner()->getLabel() + tr("'");
110 container->addTab(investigationView, investigatorName);
111 container->setCurrentWidget(investigationView);
112 if (!container->isVisible()) {
113 container->show();
114 }
115 }
116
deleteBusInvestigations()117 void WorkflowInvestigationWidgetsController::deleteBusInvestigations() {
118 if (nullptr != investigationView && nullptr != investigationModel) {
119 const QBitArray hiddenColumns = investigationModel->getColumnsVisibility();
120 for (int column = 0; investigationModel->columnCount() > column; ++column) {
121 const int absoluteColumnNumber = investigationModel->getAbsoluteNumberOfVisibleColumn(column);
122 columnWidths[investigatedLink][absoluteColumnNumber] = investigationView->columnWidth(column) * static_cast<int>(qPow(-1, hiddenColumns.testBit(absoluteColumnNumber)));
123 }
124 delete investigationModel;
125 investigationModel = nullptr;
126 investigationView->viewport()->removeEventFilter(this);
127 delete investigationView;
128 investigationView = nullptr;
129 }
130 }
131
resetInvestigations()132 void WorkflowInvestigationWidgetsController::resetInvestigations() {
133 investigatedLink = nullptr;
134 investigatorName.clear();
135 columnWidths.clear();
136 }
137
createNewInvestigation()138 void WorkflowInvestigationWidgetsController::createNewInvestigation() {
139 investigationView = new QTableView();
140 investigationView->viewport()->installEventFilter(this);
141 investigationView->setContextMenuPolicy(Qt::CustomContextMenu);
142 connect(investigationView, SIGNAL(customContextMenuRequested(const QPoint &)), SLOT(sl_contextMenuRequested(const QPoint &)));
143 investigationView->horizontalHeader()->setContextMenuPolicy(Qt::CustomContextMenu);
144 connect(investigationView->horizontalHeader(),
145 SIGNAL(customContextMenuRequested(const QPoint &)),
146 SLOT(sl_hotizontalHeaderContextMenuRequested(const QPoint &)));
147 }
148
createInvestigationModel()149 void WorkflowInvestigationWidgetsController::createInvestigationModel() {
150 Q_ASSERT(nullptr != investigatedLink && nullptr != investigationView);
151 investigationModel = new InvestigationDataModel(investigatedLink, this);
152
153 connect(investigationModel, SIGNAL(si_investigationRequested(const Workflow::Link *, int)), SIGNAL(si_updateCurrentInvestigation(const Workflow::Link *, int)));
154 connect(investigationModel, SIGNAL(si_countOfMessagesRequested(const Workflow::Link *)), SIGNAL(si_countOfMessagesRequested(const Workflow::Link *)));
155 connect(investigationModel, SIGNAL(si_columnsVisibilityRequested()), SLOT(sl_columnsVisibilityResponse()));
156
157 investigationView->setModel(investigationModel);
158 }
159
adjustInvestigationColumnWidth(WorkflowInvestigationWidget * investigator)160 void WorkflowInvestigationWidgetsController::adjustInvestigationColumnWidth(
161 WorkflowInvestigationWidget *investigator) {
162 for (int currentColumn = 0; investigationModel->columnCount() > currentColumn; ++currentColumn) {
163 const int absoluteColumnNumber = investigationModel->getAbsoluteNumberOfVisibleColumn(currentColumn);
164 const int width = (columnWidths[investigatedLink].size() <= absoluteColumnNumber || 0 == columnWidths[investigatedLink][absoluteColumnNumber]) ? investigator->fontMetrics().width(investigationModel->headerData(
165 currentColumn, Qt::Horizontal)
166 .toString()) +
167 HEADER_TEXT_MARGIN :
168 columnWidths[investigatedLink][absoluteColumnNumber];
169 Q_ASSERT(0 < width);
170 investigator->setColumnWidth(currentColumn, width);
171 }
172 }
173
setInvestigationWidgetsVisible(bool visible)174 void WorkflowInvestigationWidgetsController::setInvestigationWidgetsVisible(bool visible) {
175 QTabWidget *container = dynamic_cast<QTabWidget *>(parent());
176 Q_ASSERT(nullptr != container);
177 if (!visible && nullptr != investigationView) {
178 WorkflowInvestigationWidget *currentWidget = dynamic_cast<WorkflowInvestigationWidget *>(container->currentWidget());
179 wasDisplayed = (investigationView == currentWidget);
180 container->removeTab(container->indexOf(static_cast<QWidget *>(investigationView)));
181 deleteBusInvestigations();
182 if (wasDisplayed) {
183 container->hide();
184 }
185 } else if (visible && nullptr != investigatedLink) {
186 createNewInvestigation();
187 investigationView->setParent(container);
188 container->addTab(investigationView, investigatorName);
189 if (wasDisplayed) {
190 container->show();
191 container->setCurrentWidget(investigationView);
192 }
193 }
194 }
195
sl_currentInvestigationUpdateResponse(const WorkflowInvestigationData & investigationInfo,const Workflow::Link * bus)196 void WorkflowInvestigationWidgetsController::sl_currentInvestigationUpdateResponse(
197 const WorkflowInvestigationData &investigationInfo,
198 const Workflow::Link *bus) {
199 Q_ASSERT(bus == investigatedLink);
200 Q_UNUSED(bus);
201 if (!investigationInfo.isEmpty()) {
202 const int rowInsertionStartPosition = investigationModel->loadedRowCount();
203 if (!investigationModel->headerData(0, Qt::Horizontal, Qt::DisplayRole).isValid()) {
204 const QList<QString> headerTitles = investigationInfo.keys();
205 for (int i = 0; i < headerTitles.size(); ++i) {
206 investigationModel->setHeaderData(i, Qt::Horizontal, headerTitles[i]);
207 }
208 if (columnWidths[investigatedLink].isEmpty()) {
209 columnWidths[investigatedLink].resize(headerTitles.size());
210 columnWidths[investigatedLink].fill(0);
211 }
212 }
213 for (int columnNumber = 0; columnNumber < investigationInfo.keys().size(); ++columnNumber) {
214 const QString key = investigationInfo.keys()[columnNumber];
215 for (int rowNumber = rowInsertionStartPosition;
216 rowNumber < investigationInfo[key].size() + rowInsertionStartPosition;
217 ++rowNumber) {
218 investigationModel->setData(investigationModel->index(rowNumber, columnNumber),
219 investigationInfo[key][rowNumber - rowInsertionStartPosition]);
220 }
221 }
222 } else if (investigationModel->getColumnsVisibility().isNull()) {
223 investigationModel->setColumnsVisibility(QBitArray(0));
224 }
225 }
226
sl_countOfMessagesResponse(const Workflow::Link *,int countOfMessages)227 void WorkflowInvestigationWidgetsController::sl_countOfMessagesResponse(const Workflow::Link * /*bus*/,
228 int countOfMessages) {
229 investigationModel->insertRows(0, countOfMessages, QModelIndex());
230 }
231
sl_contextMenuRequested(const QPoint & cursorPosition)232 void WorkflowInvestigationWidgetsController::sl_contextMenuRequested(const QPoint &cursorPosition) {
233 const QItemSelectionModel *selectionModel = investigationView->selectionModel();
234 const QModelIndexList selection = selectionModel->selectedIndexes();
235 if (1 == selection.size()) {
236 QMenu contextMenu;
237 contextMenu.addAction(exportInvestigationAction);
238 contextMenu.addSeparator();
239 contextMenu.addAction(copyToClipboardAction);
240 contextMenu.exec(investigationView->viewport()->mapToGlobal(cursorPosition));
241 }
242 }
243
sl_hotizontalHeaderContextMenuRequested(const QPoint & cursorPosition)244 void WorkflowInvestigationWidgetsController::sl_hotizontalHeaderContextMenuRequested(
245 const QPoint &cursorPosition) {
246 QMenu contextMenu;
247 selectedColumn = investigationView->columnAt(cursorPosition.x());
248 if (DEFAULT_SELECTED_COLUMN != selectedColumn) {
249 if (1 < investigationModel->columnCount()) {
250 contextMenu.addAction(hideThisColumnAction);
251 contextMenu.addAction(hideAllColumnsButThisAction);
252 }
253 if (investigationModel->isAnyColumnHidden()) {
254 contextMenu.addAction(showAllColumnsAction);
255 }
256 contextMenu.exec(investigationView->viewport()->mapToGlobal(QPoint(cursorPosition.x(),
257 cursorPosition.y() - investigationView->horizontalHeader()->height())));
258 selectedColumn = DEFAULT_SELECTED_COLUMN;
259 }
260 }
261
sl_exportInvestigation()262 void WorkflowInvestigationWidgetsController::sl_exportInvestigation() {
263 const QItemSelectionModel *selectionModel = investigationView->selectionModel();
264 const QModelIndexList selected = selectionModel->selectedIndexes();
265 Q_ASSERT(1 == selected.size());
266 const QModelIndex item = selected.first();
267 const QString messageType = investigationModel->headerData(item.column(), Qt::Horizontal)
268 .toString();
269 emit si_convertionMessages2DocumentsIsRequested(investigatedLink,
270 messageType,
271 item.row());
272 }
273
sl_copyToClipboard() const274 void WorkflowInvestigationWidgetsController::sl_copyToClipboard() const {
275 const QModelIndexList selected = investigationView->selectionModel()->selectedIndexes();
276 Q_ASSERT(1 == selected.size());
277 QApplication::clipboard()->setText(selected.first().data().toString());
278 }
279
sl_hideSelectedColumn()280 void WorkflowInvestigationWidgetsController::sl_hideSelectedColumn() {
281 const int absoluteColumnNumber = investigationModel->getAbsoluteNumberOfVisibleColumn(selectedColumn);
282 columnWidths[investigatedLink][absoluteColumnNumber] = -investigationView->columnWidth(selectedColumn);
283 investigationModel->removeColumn(selectedColumn);
284 }
285
sl_hideAllColumnsButSelected()286 void WorkflowInvestigationWidgetsController::sl_hideAllColumnsButSelected() {
287 for (int column = 0; investigationModel->columnCount() > column; ++column) {
288 if (selectedColumn != column) {
289 columnWidths[investigatedLink][investigationModel->getAbsoluteNumberOfVisibleColumn(column)] = -investigationView->columnWidth(column);
290 }
291 }
292 investigationModel->removeColumns(0, selectedColumn);
293 investigationModel->removeColumns(1, investigationModel->columnCount() - 1);
294 }
295
sl_showAllColumns()296 void WorkflowInvestigationWidgetsController::sl_showAllColumns() {
297 const int absoluteSelectedColumnNumber = investigationModel->getAbsoluteNumberOfVisibleColumn(selectedColumn);
298 investigationModel->showAllHiddenColumns();
299 for (int column = 0; investigationModel->columnCount() > column; ++column) {
300 const int width = columnWidths[investigatedLink][column];
301 if (absoluteSelectedColumnNumber != column && 0 > width) {
302 columnWidths[investigatedLink][column] = -width;
303 investigationView->setColumnWidth(column, -width);
304 }
305 }
306 }
307
sl_columnsVisibilityResponse()308 void WorkflowInvestigationWidgetsController::sl_columnsVisibilityResponse() {
309 QBitArray hiddenColumns(0);
310 const QVector<int> widths = columnWidths[investigatedLink];
311 if (!widths.isEmpty()) {
312 const int columnCount = widths.size();
313 hiddenColumns.resize(columnCount);
314 for (int column = 0; columnCount > column; ++column) {
315 if (0 > widths[column]) {
316 hiddenColumns.setBit(column, true);
317 }
318 }
319 }
320 investigationModel->setColumnsVisibility(hiddenColumns);
321 }
322
323 } // namespace U2
324