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 }