File indexing completed on 2025-07-13 03:32:43
0001 /* 0002 File : CursorDock.cpp 0003 Project : LabPlot 0004 Description : This dock represents the data from the cursors in the cartesian plots 0005 -------------------------------------------------------------------- 0006 SPDX-FileCopyrightText: 2019 Martin Marmsoler <martin.marmsoler@gmail.com> 0007 SPDX-FileCopyrightText: 2019-2020 Alexander Semke <alexander.semke@web.de> 0008 SPDX-License-Identifier: GPL-2.0-or-later 0009 */ 0010 0011 #include "CursorDock.h" 0012 #include "backend/worksheet/TreeModel.h" 0013 #include "backend/worksheet/Worksheet.h" 0014 #include "backend/worksheet/WorksheetPrivate.h" 0015 #include "backend/worksheet/plots/cartesian/CartesianPlot.h" 0016 #include "backend/worksheet/plots/cartesian/XYCurve.h" 0017 #include "ui_cursordock.h" 0018 0019 #include <QClipboard> 0020 #include <QKeyEvent> 0021 #include <QMenu> 0022 0023 struct Lock; 0024 0025 CursorDock::CursorDock(QWidget* parent) 0026 : QWidget(parent) 0027 , ui(new Ui::CursorDock) { 0028 ui->setupUi(this); 0029 ui->tvCursorData->setModel(nullptr); 0030 0031 ui->bCollapseAll->setIcon(QIcon::fromTheme(QLatin1String("collapse-all"))); 0032 ui->bExpandAll->setIcon(QIcon::fromTheme(QLatin1String("expand-all"))); 0033 0034 ui->bCollapseAll->setToolTip(i18n("Collapse all curves")); 0035 ui->bExpandAll->setToolTip(i18n("Expand all curves")); 0036 0037 connect(ui->bCollapseAll, &QPushButton::clicked, this, &CursorDock::collapseAll); 0038 connect(ui->bExpandAll, &QPushButton::clicked, this, &CursorDock::expandAll); 0039 connect(ui->cbCursor0en, &QCheckBox::clicked, this, &CursorDock::cursor0EnableChanged); 0040 connect(ui->cbCursor1en, &QCheckBox::clicked, this, &CursorDock::cursor1EnableChanged); 0041 0042 // CTRL+C copies only the last cell in the selection, we want to copy the whole selection. 0043 // install event filters to handle CTRL+C key events. 0044 ui->tvCursorData->installEventFilter(this); 0045 0046 ui->tvCursorData->setContextMenuPolicy(Qt::CustomContextMenu); 0047 connect(ui->tvCursorData, &QTreeView::customContextMenuRequested, this, &CursorDock::contextMenuRequested); 0048 } 0049 0050 void CursorDock::setWorksheet(Worksheet* worksheet) { 0051 CONDITIONAL_LOCK_RETURN; 0052 0053 ui->tvCursorData->setModel(worksheet->cursorModel()); 0054 ui->tvCursorData->resizeColumnToContents(0); 0055 m_plotList = worksheet->children<CartesianPlot>(); 0056 if (m_plotList.isEmpty()) 0057 return; 0058 0059 m_plot = m_plotList.first(); 0060 0061 bool cursor0Enabled = m_plot->cursor0Enable(); 0062 bool cursor1Enabled = m_plot->cursor1Enable(); 0063 ui->cbCursor0en->setChecked(cursor0Enabled); 0064 ui->cbCursor1en->setChecked(cursor1Enabled); 0065 0066 ui->tvCursorData->setColumnHidden(static_cast<int>(WorksheetPrivate::TreeModelColumn::CURSOR0), !cursor0Enabled); 0067 ui->tvCursorData->setColumnHidden(static_cast<int>(WorksheetPrivate::TreeModelColumn::CURSOR1), !cursor1Enabled); 0068 if (cursor0Enabled && cursor1Enabled) 0069 ui->tvCursorData->setColumnHidden(static_cast<int>(WorksheetPrivate::TreeModelColumn::CURSORDIFF), false); 0070 else 0071 ui->tvCursorData->setColumnHidden(static_cast<int>(WorksheetPrivate::TreeModelColumn::CURSORDIFF), true); 0072 0073 ui->tvCursorData->expandAll(); 0074 0075 // connect all plots as a workaround to not be able to know which plot is selected 0076 for (auto& connection : selectedPlotsConnection) 0077 disconnect(connection); 0078 for (const auto* plot : m_plotList) { 0079 selectedPlotsConnection << connect(plot, &CartesianPlot::cursor0EnableChanged, this, &CursorDock::plotCursor0EnableChanged); 0080 selectedPlotsConnection << connect(plot, &CartesianPlot::cursor1EnableChanged, this, &CursorDock::plotCursor1EnableChanged); 0081 selectedPlotsConnection << connect(plot, &CartesianPlot::mousePressCursorModeSignal, this, &CursorDock::cursorUsed); 0082 selectedPlotsConnection << connect(plot, &CartesianPlot::mousePressCursorModeSignal, this, &CursorDock::cursorUsed); 0083 } 0084 } 0085 0086 CursorDock::~CursorDock() { 0087 delete ui; 0088 } 0089 0090 void CursorDock::collapseAll() { 0091 ui->tvCursorData->collapseAll(); 0092 } 0093 0094 void CursorDock::expandAll() { 0095 ui->tvCursorData->expandAll(); 0096 } 0097 0098 void CursorDock::cursor0EnableChanged(bool enable) { 0099 CONDITIONAL_LOCK_RETURN; 0100 0101 for (auto* plot : m_plotList) 0102 plot->setCursor0Enable(enable); 0103 } 0104 0105 void CursorDock::cursor1EnableChanged(bool enable) { 0106 CONDITIONAL_LOCK_RETURN; 0107 0108 for (auto* plot : m_plotList) 0109 plot->setCursor1Enable(enable); 0110 } 0111 0112 // ############################################################# 0113 // back from plot 0114 // ############################################################# 0115 void CursorDock::plotCursor0EnableChanged(bool enable) { 0116 ui->tvCursorData->setColumnHidden(static_cast<int>(WorksheetPrivate::TreeModelColumn::CURSOR0), !enable); 0117 if (enable && ui->cbCursor1en->isChecked()) 0118 ui->tvCursorData->setColumnHidden(static_cast<int>(WorksheetPrivate::TreeModelColumn::CURSORDIFF), false); 0119 else 0120 ui->tvCursorData->setColumnHidden(static_cast<int>(WorksheetPrivate::TreeModelColumn::CURSORDIFF), true); 0121 0122 CONDITIONAL_LOCK_RETURN; 0123 ui->cbCursor0en->setChecked(enable); 0124 } 0125 0126 void CursorDock::plotCursor1EnableChanged(bool enable) { 0127 ui->tvCursorData->setColumnHidden(static_cast<int>(WorksheetPrivate::TreeModelColumn::CURSOR1), !enable); 0128 if (enable && ui->cbCursor0en->isChecked()) 0129 ui->tvCursorData->setColumnHidden(static_cast<int>(WorksheetPrivate::TreeModelColumn::CURSORDIFF), false); 0130 else 0131 ui->tvCursorData->setColumnHidden(static_cast<int>(WorksheetPrivate::TreeModelColumn::CURSORDIFF), true); 0132 0133 CONDITIONAL_LOCK_RETURN; 0134 ui->cbCursor1en->setChecked(enable); 0135 } 0136 0137 bool CursorDock::eventFilter(QObject* obj, QEvent* event) { 0138 if (event->type() == QEvent::KeyPress && obj == ui->tvCursorData) { 0139 auto* key_event = static_cast<QKeyEvent*>(event); 0140 if (key_event->matches(QKeySequence::Copy)) { 0141 resultCopy(); 0142 return true; 0143 } 0144 } 0145 return QWidget::eventFilter(obj, event); 0146 } 0147 0148 void CursorDock::contextMenuRequested(QPoint pos) { 0149 auto* menu = new QMenu(this); 0150 menu->addAction(i18n("Copy Selection"), this, &CursorDock::resultCopy, QKeySequence::Copy); 0151 menu->addAction(i18n("Copy All"), this, &CursorDock::resultCopyAll); 0152 menu->exec(ui->tvCursorData->mapToGlobal(pos)); 0153 } 0154 0155 void CursorDock::resultCopy() { 0156 QString str; 0157 auto* model = ui->tvCursorData->model(); 0158 auto* selection = ui->tvCursorData->selectionModel(); 0159 const auto& indices = selection->selectedRows(); 0160 for (auto& index : indices) { 0161 int row = index.row(); 0162 auto parent = index.parent(); 0163 for (int col = 0; col < model->columnCount(); ++col) { 0164 if (col != 0) 0165 str += QLatin1Char('\t'); 0166 str += model->data(model->index(row, col, parent)).toString(); 0167 } 0168 0169 str += QLatin1Char('\n'); 0170 } 0171 0172 QApplication::clipboard()->setText(str); 0173 } 0174 0175 void CursorDock::resultCopyAll() { 0176 QString str; 0177 auto* model = ui->tvCursorData->model(); 0178 for (int row = 0; row < model->rowCount(); ++row) { 0179 for (int col = 0; col < model->columnCount(); ++col) { 0180 if (col != 0) 0181 str += QLatin1Char('\t'); 0182 str += model->data(model->index(row, col)).toString(); 0183 } 0184 0185 str += QLatin1Char('\n'); 0186 0187 // iterate over all children of the current row 0188 auto index = model->index(row, 0); 0189 for (int row = 0; row < model->rowCount(index); ++row) { 0190 for (int col = 0; col < model->columnCount(); ++col) { 0191 if (col != 0) 0192 str += QLatin1Char('\t'); 0193 str += model->data(model->index(row, col, index)).toString(); 0194 } 0195 0196 str += QLatin1Char('\n'); 0197 } 0198 } 0199 0200 QApplication::clipboard()->setText(str); 0201 }