diff --git a/src/backend/datasources/LiveDataSource.cpp b/src/backend/datasources/LiveDataSource.cpp index e56453ef6..71335895a 100644 --- a/src/backend/datasources/LiveDataSource.cpp +++ b/src/backend/datasources/LiveDataSource.cpp @@ -1,1000 +1,1000 @@ /*************************************************************************** File : LiveDataSource.cpp Project : LabPlot Description : Represents live data source -------------------------------------------------------------------- Copyright : (C) 2009-2017 Alexander Semke (alexander.semke@web.de) Copyright : (C) 2017 Fabian Kristof (fkristofszabolcs@gmail.com) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "backend/datasources/LiveDataSource.h" #include "backend/datasources/filters/AsciiFilter.h" #include "backend/datasources/filters/FITSFilter.h" #include "backend/datasources/filters/BinaryFilter.h" #include "backend/core/Project.h" #include "kdefrontend/spreadsheet/PlotDataDialog.h" #include "commonfrontend/spreadsheet/SpreadsheetView.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /*! \class LiveDataSource \brief Represents data stored in a file. Reading and writing is done with the help of appropriate I/O-filters. \ingroup datasources */ LiveDataSource::LiveDataSource(AbstractScriptingEngine* engine, const QString& name, bool loading) : Spreadsheet(engine, name, loading), m_fileType(Ascii), m_fileWatched(false), m_fileLinked(false), m_paused(false), m_prepared(false), m_keepLastValues(false), m_bytesRead(0), m_filter(nullptr), m_updateTimer(new QTimer(this)), m_fileSystemWatcher(nullptr), m_file(nullptr), m_localSocket(nullptr), m_tcpSocket(nullptr), m_udpSocket(nullptr), m_serialPort(nullptr), m_device(nullptr) { initActions(); connect(m_updateTimer, SIGNAL(timeout()), this, SLOT(read())); } LiveDataSource::~LiveDataSource() { if (m_filter) delete m_filter; if (m_fileSystemWatcher) delete m_fileSystemWatcher; if (m_file) delete m_file; if (m_localSocket) delete m_localSocket; if (m_tcpSocket) delete m_tcpSocket; if (m_serialPort) delete m_serialPort; delete m_updateTimer; } /*! * depending on the update type, periodically or on data changes, starts the timer or activates the file watchers, respectively. */ void LiveDataSource::ready() { if (m_updateType == TimeInterval) m_updateTimer->start(m_updateInterval); else watch(); } void LiveDataSource::initActions() { m_reloadAction = new QAction(QIcon::fromTheme("view-refresh"), i18n("Reload"), this); connect(m_reloadAction, SIGNAL(triggered()), this, SLOT(read())); m_toggleLinkAction = new QAction(i18n("Link the file"), this); m_toggleLinkAction->setCheckable(true); connect(m_toggleLinkAction, SIGNAL(triggered()), this, SLOT(linkToggled())); m_plotDataAction = new QAction(QIcon::fromTheme("office-chart-line"), i18n("Plot data"), this); connect(m_plotDataAction, SIGNAL(triggered()), this, SLOT(plotData())); } //TODO make the view customizable (show as a spreadsheet or as a pure text file in an editor) QWidget *LiveDataSource::view() const { if (!m_view) - m_view = new SpreadsheetView(const_cast(this)); + m_view = new SpreadsheetView(const_cast(this), true); return m_view; } /*! * \brief Returns a list with the names of the available ports */ QStringList LiveDataSource::availablePorts() { QStringList ports; qDebug() << "available ports count:" << QSerialPortInfo::availablePorts().size(); for(const QSerialPortInfo& sp : QSerialPortInfo::availablePorts()) { ports.append(sp.portName()); qDebug() << sp.description(); qDebug() << sp.manufacturer(); qDebug() << sp.portName(); qDebug() << sp.serialNumber(); qDebug() << sp.systemLocation(); } return ports; } /*! * \brief Returns a list with the supported baud rates */ QStringList LiveDataSource::supportedBaudRates() { QStringList baudRates; for(const auto& baud : QSerialPortInfo::standardBaudRates()) baudRates.append(QString::number(baud)); return baudRates; } /*! * \brief Updates this data source at this moment */ void LiveDataSource::updateNow() { m_updateTimer->stop(); read(); //restart the timer after update if (m_updateType == TimeInterval) m_updateTimer->start(m_updateInterval); } /*! * \brief Continue reading from the live data source after it was paused. */ void LiveDataSource::continueReading() { m_paused = false; if (m_updateType == TimeInterval) m_updateTimer->start(m_updateInterval); else if (m_updateType == NewData) connect(m_fileSystemWatcher, SIGNAL(fileChanged(QString)), this, SLOT(read())); } /*! * \brief Pause the reading of the live data source. */ void LiveDataSource::pauseReading() { m_paused = true; if (m_updateType == TimeInterval) m_updateTimer->stop(); else if (m_updateType == NewData) disconnect(m_fileSystemWatcher, SIGNAL(fileChanged(QString)), this, SLOT(read())); } /*! returns the list with all supported data file formats. */ QStringList LiveDataSource::fileTypes() { // see LiveDataSource::FileType return (QStringList()<< i18n("ASCII data") << i18n("Binary data") << i18n("Image") << i18n("Hierarchical Data Format (HDF)") << i18n("Network Common Data Format (NetCDF)") // << "CDF" << i18n("Flexible Image Transport System Data Format (FITS)") // << i18n("Sound") ); } void LiveDataSource::setFileName(const QString& name) { m_fileName = name; } QString LiveDataSource::fileName() const { return m_fileName; } /*! * \brief Sets the local socket's server name to name * \param name */ void LiveDataSource::setLocalSocketName(const QString & name) { m_localSocketName = name; } QString LiveDataSource::localSocketName() const { return m_localSocketName; } void LiveDataSource::setFileType(const FileType type) { m_fileType = type; } LiveDataSource::FileType LiveDataSource::fileType() const { return m_fileType; } void LiveDataSource::setFilter(AbstractFileFilter* f) { m_filter = f; } AbstractFileFilter* LiveDataSource::filter() const { return m_filter; } /*! sets whether the file should be watched or not. In the first case the data source will be automatically updated on file changes. */ void LiveDataSource::setFileWatched(const bool b) { m_fileWatched = b; } bool LiveDataSource::isFileWatched() const { return m_fileWatched; } /*! * \brief Sets whether we'll keep the last values or append it to the previous ones * \param keepLastValues */ void LiveDataSource::setKeepLastValues(const bool keepLastValues) { m_keepLastValues = keepLastValues; } bool LiveDataSource::keepLastValues() const { return m_keepLastValues; } /*! * \brief Sets the serial port's baud rate * \param baudrate */ void LiveDataSource::setBaudRate(const int baudrate) { m_baudRate = baudrate; } int LiveDataSource::baudRate() const { return m_baudRate; } /*! * \brief Sets the source's update interval to \c interval * \param interval */ void LiveDataSource::setUpdateInterval(const int interval) { m_updateInterval = interval; m_updateTimer->start(m_updateInterval); } int LiveDataSource::updateInterval() const { return m_updateInterval; } /*! * \brief Sets how many values we should store * \param keepnvalues */ void LiveDataSource::setKeepNvalues(const int keepnvalues) { m_keepNvalues = keepnvalues; } int LiveDataSource::keepNvalues() const { return m_keepNvalues; } /*! * \brief Sets the network socket's port to port * \param port */ void LiveDataSource::setPort(const int port) { m_port = port; } void LiveDataSource::setBytesRead(const qint64 bytes) { m_bytesRead = bytes; } int LiveDataSource::bytesRead() const { return m_bytesRead; } int LiveDataSource::port() const { return m_port; } /*! * \brief Sets the serial port's name to name * \param name */ void LiveDataSource::setSerialPort(const QString &name) { m_serialPortName = name; } QString LiveDataSource::serialPortName() const { return m_serialPortName; } bool LiveDataSource::isPaused() const { return m_paused; } /*! * \brief Sets the sample rate to samplerate * \param samplerate */ void LiveDataSource::setSampleRate(const int samplerate) { m_sampleRate = samplerate; } int LiveDataSource::sampleRate() const { return m_sampleRate; } /*! * \brief Sets the source's type to sourcetype * \param sourcetype */ void LiveDataSource::setSourceType(const SourceType sourcetype) { m_sourceType = sourcetype; } LiveDataSource::SourceType LiveDataSource::sourceType() const { return m_sourceType; } /*! * \brief Sets the source's reading type to readingType * \param readingType */ void LiveDataSource::setReadingType(const ReadingType readingType) { m_readingType = readingType; } LiveDataSource::ReadingType LiveDataSource::readingType() const { return m_readingType; } /*! * \brief Sets the source's update type to updatetype and handles this change * \param updatetype */ void LiveDataSource::setUpdateType(const UpdateType updatetype) { if (updatetype == NewData) { m_updateTimer->stop(); if (m_fileSystemWatcher == nullptr) watch(); else connect(m_fileSystemWatcher, SIGNAL(fileChanged(QString)), this, SLOT(read())); } else disconnect(m_fileSystemWatcher, SIGNAL(fileChanged(QString)), this, SLOT(read())); m_updateType = updatetype; } LiveDataSource::UpdateType LiveDataSource::updateType() const { return m_updateType; } /*! * \brief Sets the network socket's host * \param host */ void LiveDataSource::setHost(const QString & host) { m_host = host; } QString LiveDataSource::host() const { return m_host; } /*! sets whether only a link to the file is saved in the project file (\c b=true) or the whole content of the file (\c b=false). */ void LiveDataSource::setFileLinked(const bool b) { m_fileLinked = b; } /*! returns \c true if only a link to the file is saved in the project file. \c false otherwise. */ bool LiveDataSource::isFileLinked() const { return m_fileLinked; } QIcon LiveDataSource::icon() const { QIcon icon; if (m_fileType == LiveDataSource::Ascii) icon = QIcon::fromTheme("text-plain"); else if (m_fileType == LiveDataSource::Binary) icon = QIcon::fromTheme("application-octet-stream"); else if (m_fileType == LiveDataSource::Image) icon = QIcon::fromTheme("image-x-generic"); // TODO: HDF, NetCDF, FITS, etc. return icon; } QMenu* LiveDataSource::createContextMenu() { QMenu* menu = AbstractPart::createContextMenu(); QAction* firstAction = 0; // if we're populating the context menu for the project explorer, then //there're already actions available there. Skip the first title-action //and insert the action at the beginning of the menu. if (menu->actions().size()>1) firstAction = menu->actions().at(1); menu->insertAction(firstAction, m_plotDataAction); menu->insertSeparator(firstAction); //TODO: doesnt' always make sense... // if (!m_fileWatched) // menu->insertAction(firstAction, m_reloadAction); // // m_toggleWatchAction->setChecked(m_fileWatched); // menu->insertAction(firstAction, m_toggleWatchAction); // // m_toggleLinkAction->setChecked(m_fileLinked); // menu->insertAction(firstAction, m_toggleLinkAction); return menu; } //############################################################################## //################################# SLOTS #################################### //############################################################################## /* * called periodically or on new data changes (file changed, new data in the socket, etc.) */ void LiveDataSource::read() { if (m_filter == nullptr) return; //initialize the device (file, socket, serial port), when calling this function for the first time if (!m_prepared) { switch (m_sourceType) { case FileOrPipe: m_file = new QFile(m_fileName); m_device = m_file; break; case NetworkTcpSocket: m_tcpSocket = new QTcpSocket; m_device = m_tcpSocket; connect(m_tcpSocket, SIGNAL(readyRead()), this, SLOT(readyRead())); connect(m_tcpSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(tcpSocketError(QAbstractSocket::SocketError))); break; case NetworkUdpSocket: m_udpSocket = new QUdpSocket; m_device = m_udpSocket; connect(m_udpSocket, SIGNAL(readyRead()), this, SLOT(readyRead())); connect(m_udpSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(tcpSocketError(QAbstractSocket::SocketError))); break; case LocalSocket: m_localSocket = new QLocalSocket(this); qDebug() << "socket state before preparing: " << m_localSocket->state(); m_localSocket->connectToServer(m_localSocketName, QLocalSocket::ReadOnly); qDebug() << "socket state after preparing: " << m_localSocket->state(); m_device = m_localSocket; connect(m_localSocket, SIGNAL(readyRead()), this, SLOT(readyRead())); connect(m_localSocket, SIGNAL(error(QLocalSocket::LocalSocketError)), this, SLOT(localSocketError(QLocalSocket::LocalSocketError))); break; case SerialPort: m_serialPort = new QSerialPort; m_device = m_serialPort; m_serialPort->setBaudRate(m_baudRate); m_serialPort->setPortName(m_serialPortName); connect(m_serialPort, SIGNAL(error(QSerialPort::SerialPortError)), this, SLOT(serialPortError(QSerialPort::SerialPortError))); connect(m_serialPort, SIGNAL(readyRead()), this, SLOT(readyRead())); break; } m_prepared = true; } qint64 bytes = 0; switch (m_sourceType) { case FileOrPipe: switch (m_fileType) { case Ascii: qDebug() << "Reading live ascii file.." ; bytes = dynamic_cast(m_filter)->readFromLiveDevice(*m_file, this, m_bytesRead); m_bytesRead += bytes; qDebug() << "Read " << bytes << " bytes, in total: " << m_bytesRead; break; case Binary: //bytes = dynamic_cast(m_filter)->readFromLiveDevice(*m_file, this, m_bytesRead); m_bytesRead += bytes; default: break; } break; case NetworkTcpSocket: DEBUG("reading from a TCP socket"); m_tcpSocket->abort(); m_tcpSocket->connectToHost(m_host, m_port, QIODevice::ReadOnly); break; case NetworkUdpSocket: DEBUG("reading from a UDP socket"); m_udpSocket->abort(); m_udpSocket->connectToHost(m_host, m_port, QIODevice::ReadOnly); break; case LocalSocket: DEBUG("reading from a local socket"); qDebug() << m_localSocket->state(); m_localSocket->abort(); m_localSocket->connectToServer(m_localSocketName, QLocalSocket::ReadOnly); qDebug() << m_localSocket->state(); break; case SerialPort: DEBUG("reading from the serial port"); //TODO break; } } /*! * Slot for the signal that is emitted once every time new data is available for reading from the device. * It will only be emitted again once new data is available, such as when a new payload of network data has arrived on the network socket, * or when a new block of data has been appended to your device. */ void LiveDataSource::readyRead() { DEBUG("Got new data from the device"); if (m_fileType == Ascii) dynamic_cast(m_filter)->readFromLiveDeviceNotFile(*m_device, this); // else if (m_fileType == Binary) // dynamic_cast(m_filter)->readFromLiveDeviceNotFile(*m_device, this); //since we won't have the timer to call read() where we create new connections //for sequencial devices in read() we just request data/connect to servers if (m_updateType == NewData) read(); } void LiveDataSource::localSocketError(QLocalSocket::LocalSocketError socketError) { /*disconnect(m_localSocket, SIGNAL(error(QLocalSocket::LocalSocketError)), this, SLOT(localSocketError(QLocalSocket::LocalSocketError))); disconnect(m_localSocket, SIGNAL(readyRead()), this, SLOT(readyRead()));*/ switch (socketError) { case QLocalSocket::ServerNotFoundError: QMessageBox::critical(0, i18n("Local Socket Error"), i18n("The socket was not found. Please check the socket name.")); break; case QLocalSocket::ConnectionRefusedError: QMessageBox::critical(0, i18n("Local Socket Error"), i18n("The connection was refused by the peer")); break; case QLocalSocket::PeerClosedError: QMessageBox::critical(0, i18n("Local Socket Error"), i18n("The socket has closed the connection.")); break; default: QMessageBox::critical(0, i18n("Local Socket Error"), i18n("The following error occurred: %1.").arg(m_localSocket->errorString())); } } void LiveDataSource::tcpSocketError(QAbstractSocket::SocketError socketError) { switch (socketError) { case QAbstractSocket::ConnectionRefusedError: QMessageBox::critical(0, i18n("TCP Socket Error"), i18n("The connection was refused by the peer. Make sure the server is running and check the host name and port settings.")); break; case QAbstractSocket::RemoteHostClosedError: QMessageBox::critical(0, i18n("TCP Socket Error"), i18n("The remote host closed the connection.")); break; case QAbstractSocket::HostNotFoundError: QMessageBox::critical(0, i18n("TCP Socket Error"), i18n("The host was not found. Please check the host name and port settings.")); break; default: QMessageBox::critical(0, i18n("TCP Socket Error"), i18n("The following error occurred: %1.").arg(m_tcpSocket->errorString())); } } void LiveDataSource::serialPortError(QSerialPort::SerialPortError serialPortError) { switch (serialPortError) { case QSerialPort::DeviceNotFoundError: QMessageBox::critical(0, i18n("Serial Port Error"), i18n("Failed to open the device.")); break; case QSerialPort::PermissionError: QMessageBox::critical(0, i18n("Serial Port Error"), i18n("Failed to open the device. Please check your permissions on this device.")); break; case QSerialPort::OpenError: QMessageBox::critical(0, i18n("Serial Port Error"), i18n("Device already opened.")); break; case QSerialPort::NotOpenError: QMessageBox::critical(0, i18n("Serial Port Error"), i18n("The device is not opened.")); break; case QSerialPort::ReadError: QMessageBox::critical(0, i18n("Serial Port Error"), i18n("Failed to read data.")); break; case QSerialPort::ResourceError: QMessageBox::critical(0, i18n("Serial Port Error"), i18n("Failed to read data. The device is removed.")); break; case QSerialPort::TimeoutError: QMessageBox::critical(0, i18n("Serial Port Error"), i18n("The device timed out.")); break; default: QMessageBox::critical(0, i18n("Serial Port Error"), i18n("The following error occurred: %1.").arg(m_serialPort->errorString())); break; } } void LiveDataSource::watchToggled() { m_fileWatched = !m_fileWatched; watch(); project()->setChanged(true); } void LiveDataSource::linkToggled() { m_fileLinked = !m_fileLinked; project()->setChanged(true); } //watch the file upon reading for changes if required void LiveDataSource::watch() { if (m_fileWatched) { if (!m_fileSystemWatcher) { m_fileSystemWatcher = new QFileSystemWatcher; connect (m_fileSystemWatcher, SIGNAL(fileChanged(QString)), this, SLOT(read())); } if ( !m_fileSystemWatcher->files().contains(m_fileName) ) m_fileSystemWatcher->addPath(m_fileName); } else { if (m_fileSystemWatcher) m_fileSystemWatcher->removePath(m_fileName); } } /*! returns a string containing the general information about the file \c name and some content specific information (number of columns and lines for ASCII, color-depth for images etc.). */ QString LiveDataSource::fileInfoString(const QString &name) { QString infoString; QFileInfo fileInfo; QString fileTypeString; QIODevice *file = new QFile(name); QString fileName; if (name.at(0) != QDir::separator()) fileName = QDir::homePath() + QDir::separator() + name; else fileName = name; if(file==0) file = new QFile(fileName); if (file->open(QIODevice::ReadOnly)) { QStringList infoStrings; //general information about the file infoStrings << "" + fileName + "
"; fileInfo.setFile(fileName); infoStrings << i18n("Readable: %1", fileInfo.isReadable() ? i18n("yes") : i18n("no")); infoStrings << i18n("Writable: %1", fileInfo.isWritable() ? i18n("yes") : i18n("no")); infoStrings << i18n("Executable: %1", fileInfo.isExecutable() ? i18n("yes") : i18n("no")); infoStrings << i18n("Created: %1", fileInfo.created().toString()); infoStrings << i18n("Last modified: %1", fileInfo.lastModified().toString()); infoStrings << i18n("Last read: %1", fileInfo.lastRead().toString()); infoStrings << i18n("Owner: %1", fileInfo.owner()); infoStrings << i18n("Group: %1", fileInfo.group()); infoStrings << i18n("Size: %1", i18np("%1 cByte", "%1 cBytes", fileInfo.size())); #ifdef HAVE_FITS if (fileName.endsWith(QLatin1String(".fits"))) { FITSFilter* fitsFilter = new FITSFilter; infoStrings << i18n("Images: %1", QString::number(fitsFilter->imagesCount(fileName) )); infoStrings << i18n("Tables: %1", QString::number(fitsFilter->tablesCount(fileName) )); delete fitsFilter; } #endif // file type and type specific information about the file #ifdef Q_OS_LINUX QProcess *proc = new QProcess(); QStringList args; args<<"-b"<start( "file", args); if(proc->waitForReadyRead(1000) == false) infoStrings << i18n("Could not open file %1 for reading.", fileName); else { fileTypeString = proc->readLine(); if( fileTypeString.contains(i18n("cannot open")) ) fileTypeString=""; else { fileTypeString.remove(fileTypeString.length()-1,1); // remove '\n' } } infoStrings << i18n("File type: %1", fileTypeString); #endif //TODO depending on the file type, generate additional information about the file: //Number of lines for ASCII, color-depth for images etc. Use the specific filters here. // port the old labplot1.6 code. if( fileTypeString.contains("ASCII")) { infoStrings << "
"; //TODO: consider choosen separator infoStrings << i18n("Number of columns: %1", AsciiFilter::columnNumber(fileName)); infoStrings << i18n("Number of lines: %1", AsciiFilter::lineNumber(fileName)); } infoString += infoStrings.join("
"); } else infoString += i18n("Could not open file %1 for reading.", fileName); return infoString; } void LiveDataSource::plotData() { PlotDataDialog* dlg = new PlotDataDialog(this); dlg->exec(); } //############################################################################## //################## Serialization/Deserialization ########################### //############################################################################## /*! Saves as XML. */ void LiveDataSource::save(QXmlStreamWriter* writer) const { writer->writeStartElement("liveDataSource"); writeBasicAttributes(writer); writeCommentElement(writer); //general writer->writeStartElement("general"); writer->writeAttribute("fileName", m_fileName); writer->writeAttribute("fileType", QString::number(m_fileType)); writer->writeAttribute("fileWatched", QString::number(m_fileWatched)); writer->writeAttribute("fileLinked", QString::number(m_fileLinked)); writer->writeAttribute("updateType", QString::number(m_updateType)); writer->writeAttribute("readingType", QString::number(m_readingType)); writer->writeAttribute("sourceType", QString::number(m_sourceType)); writer->writeAttribute("keepValues", QString::number(m_keepNvalues)); if (m_updateType == TimeInterval) writer->writeAttribute("updateInterval", QString::number(m_updateInterval)); if (m_readingType != TillEnd) writer->writeAttribute("sampleRate", QString::number(m_sampleRate)); switch (m_sourceType) { case SerialPort: writer->writeAttribute("baudRate", QString::number(m_baudRate)); writer->writeAttribute("serialPortName", m_serialPortName); break; case NetworkTcpSocket: case NetworkUdpSocket: writer->writeAttribute("host", m_host); writer->writeAttribute("port", QString::number(m_port)); break; case FileOrPipe: break; case LocalSocket: break; default: break; } writer->writeEndElement(); //filter m_filter->save(writer); //columns if (!m_fileLinked) { foreach (Column * col, children(IncludeHidden)) col->save(writer); } writer->writeEndElement(); // "liveDataSource" } /*! Loads from XML. */ bool LiveDataSource::load(XmlStreamReader* reader) { if(!reader->isStartElement() || reader->name() != "liveDataSource") { reader->raiseError(i18n("no liveDataSource element found")); return false; } if (!readBasicAttributes(reader)) return false; QString attributeWarning = i18n("Attribute '%1' missing or empty, default value is used"); QXmlStreamAttributes attribs; QString str; while (!reader->atEnd()) { reader->readNext(); if (reader->isEndElement() && reader->name() == "LiveDataSource") break; if (!reader->isStartElement()) continue; if (reader->name() == "comment") { if (!readCommentElement(reader)) return false; } else if (reader->name() == "general") { attribs = reader->attributes(); str = attribs.value("fileName").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'fileName'")); else m_fileName = str; str = attribs.value("fileType").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'fileType'")); else m_fileType = (FileType)str.toInt(); str = attribs.value("fileWatched").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'fileWatched'")); else m_fileWatched = str.toInt(); str = attribs.value("fileLinked").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'fileLinked'")); else m_fileLinked = str.toInt(); str = attribs.value("updateType").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'updateType'")); else m_updateType = static_cast(str.toInt()); str = attribs.value("sourceType").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'sourceType'")); else m_sourceType = static_cast(str.toInt()); str = attribs.value("readingType").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'readingType'")); else m_readingType = static_cast(str.toInt()); if (m_updateType == TimeInterval) { str = attribs.value("updateInterval").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'updateInterval'")); else m_updateInterval = str.toInt(); } if (m_readingType != TillEnd) { str = attribs.value("sampleRate").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'sampleRate'")); else m_sampleRate = str.toInt(); } switch (m_sourceType) { case SerialPort: str = attribs.value("baudRate").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'baudRate'")); else m_baudRate = str.toInt(); str = attribs.value("serialPortName").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'serialPortName'")); else m_serialPortName = str; break; case NetworkTcpSocket: case NetworkUdpSocket: str = attribs.value("host").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'host'")); else m_host = str; str = attribs.value("port").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'port'")); else m_host = str; break; case FileOrPipe: break; case LocalSocket: break; default: break; } } else if (reader->name() == "asciiFilter") { m_filter = new AsciiFilter(); if (!m_filter->load(reader)) return false; } else if(reader->name() == "column") { Column* column = new Column("", AbstractColumn::Text); if (!column->load(reader)) { delete column; setColumnCount(0); return false; } addChild(column); } else {// unknown element reader->raiseWarning(i18n("unknown element '%1'", reader->name().toString())); if (!reader->skipToEndElement()) return false; } } //read the content of the file if it was only linked if (m_fileLinked) this->read(); return !reader->hasError(); } diff --git a/src/commonfrontend/spreadsheet/SpreadsheetView.cpp b/src/commonfrontend/spreadsheet/SpreadsheetView.cpp index 4de13c3e4..88270c863 100644 --- a/src/commonfrontend/spreadsheet/SpreadsheetView.cpp +++ b/src/commonfrontend/spreadsheet/SpreadsheetView.cpp @@ -1,2316 +1,2343 @@ /*************************************************************************** File : SpreadsheetView.cpp Project : LabPlot Description : View class for Spreadsheet -------------------------------------------------------------------- Copyright : (C) 2011-2017 by Alexander Semke (alexander.semke@web.de) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "SpreadsheetView.h" #include "backend/spreadsheet/SpreadsheetModel.h" #include "backend/spreadsheet/Spreadsheet.h" #include "commonfrontend/spreadsheet/SpreadsheetItemDelegate.h" #include "commonfrontend/spreadsheet/SpreadsheetHeaderView.h" #include "backend/datasources/filters/FITSFilter.h" #include "backend/lib/macros.h" #include "backend/core/column/Column.h" #include "backend/core/column/ColumnPrivate.h" #include "backend/core/datatypes/SimpleCopyThroughFilter.h" #include "backend/core/datatypes/Double2StringFilter.h" #include "backend/core/datatypes/String2DoubleFilter.h" #include "backend/core/datatypes/DateTime2StringFilter.h" #include "backend/core/datatypes/String2DateTimeFilter.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kdefrontend/spreadsheet/PlotDataDialog.h" #include "kdefrontend/spreadsheet/DropValuesDialog.h" #include "kdefrontend/spreadsheet/SortDialog.h" #include "kdefrontend/spreadsheet/RandomValuesDialog.h" #include "kdefrontend/spreadsheet/EquidistantValuesDialog.h" #include "kdefrontend/spreadsheet/FunctionValuesDialog.h" #include "kdefrontend/spreadsheet/StatisticsDialog.h" #include //for std::reverse /*! \class SpreadsheetView \brief View class for Spreadsheet \ingroup commonfrontend */ -SpreadsheetView::SpreadsheetView(Spreadsheet* spreadsheet) : QWidget(), +SpreadsheetView::SpreadsheetView(Spreadsheet* spreadsheet, bool readOnly) : QWidget(), m_tableView(new QTableView(this)), m_spreadsheet(spreadsheet), m_model( new SpreadsheetModel(spreadsheet) ), - m_suppressSelectionChangedEvent(false) { + m_suppressSelectionChangedEvent(false), + m_readOnly(readOnly) { QHBoxLayout* layout = new QHBoxLayout(this); layout->setContentsMargins(0,0,0,0); layout->addWidget(m_tableView); init(); //resize the view to show alls columns and the first 50 rows. //no need to resize the view when the project is being opened, //all views will be resized to the stored values at the end if (!m_spreadsheet->isLoading()) { int w = m_tableView->verticalHeader()->width(); int h = m_horizontalHeader->height(); for (int i = 0; i < m_horizontalHeader->count(); ++i) w += m_horizontalHeader->sectionSize(i); if (m_tableView->verticalHeader()->count() > 50 || m_tableView->verticalHeader()->count() < 10) h += m_tableView->verticalHeader()->sectionSize(0)*50; else h += m_tableView->verticalHeader()->sectionSize(0)*m_tableView->verticalHeader()->count(); resize(w+50, h); } } SpreadsheetView::~SpreadsheetView() { delete m_model; } void SpreadsheetView::init() { initActions(); initMenus(); m_tableView->setModel(m_model); m_tableView->setItemDelegate(new SpreadsheetItemDelegate(this)); m_tableView->setSelectionMode(QAbstractItemView::ExtendedSelection); //horizontal header m_horizontalHeader = new SpreadsheetHeaderView(this); m_horizontalHeader->setClickable(true); m_horizontalHeader->setHighlightSections(true); m_tableView->setHorizontalHeader(m_horizontalHeader); m_horizontalHeader->setMovable(true); m_horizontalHeader->installEventFilter(this); resizeHeader(); connect(m_horizontalHeader, SIGNAL(sectionMoved(int,int,int)), this, SLOT(handleHorizontalSectionMoved(int,int,int))); connect(m_horizontalHeader, SIGNAL(sectionDoubleClicked(int)), this, SLOT(handleHorizontalHeaderDoubleClicked(int))); connect(m_horizontalHeader, SIGNAL(sectionResized(int,int,int)), this, SLOT(handleHorizontalSectionResized(int,int,int))); connect(m_horizontalHeader, SIGNAL(sectionClicked(int)), this, SLOT(columnClicked(int)) ); // vertical header QHeaderView* v_header = m_tableView->verticalHeader(); v_header->setResizeMode(QHeaderView::Fixed); v_header->setMovable(false); v_header->installEventFilter(this); setFocusPolicy(Qt::StrongFocus); setFocus(); installEventFilter(this); connectActions(); showComments(false); connect(m_model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)), this, SLOT(updateHeaderGeometry(Qt::Orientation,int,int)) ); connect(m_model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)), this, SLOT(handleHeaderDataChanged(Qt::Orientation,int,int)) ); connect(m_spreadsheet, SIGNAL(aspectAdded(const AbstractAspect*)), this, SLOT(handleAspectAdded(const AbstractAspect*))); connect(m_spreadsheet, SIGNAL(aspectAboutToBeRemoved(const AbstractAspect*)), this, SLOT(handleAspectAboutToBeRemoved(const AbstractAspect*))); connect(m_spreadsheet, SIGNAL(requestProjectContextMenu(QMenu*)), this, SLOT(createContextMenu(QMenu*))); for (auto* column: m_spreadsheet->children()) connect(column, SIGNAL(requestProjectContextMenu(QMenu*)), this, SLOT(createColumnContextMenu(QMenu*))); //selection relevant connections QItemSelectionModel* sel_model = m_tableView->selectionModel(); connect(sel_model, SIGNAL(currentColumnChanged(QModelIndex,QModelIndex)), this, SLOT(currentColumnChanged(QModelIndex,QModelIndex))); connect(sel_model, SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(selectionChanged(QItemSelection,QItemSelection))); connect(sel_model, SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(selectionChanged(QItemSelection,QItemSelection)) ); connect(m_spreadsheet, SIGNAL(columnSelected(int)), this, SLOT(selectColumn(int)) ); connect(m_spreadsheet, SIGNAL(columnDeselected(int)), this, SLOT(deselectColumn(int)) ); } /*! set the column sizes to the saved values or resize to content if no size was saved yet */ void SpreadsheetView::resizeHeader() { for (int i = 0; i < m_spreadsheet->children().size(); ++i) { Column* col = m_spreadsheet->child(i); if (col->width() == 0) m_tableView->resizeColumnToContents(i); else m_tableView->setColumnWidth(i, col->width()); } } void SpreadsheetView::initActions() { // selection related actions action_cut_selection = new QAction(QIcon::fromTheme("edit-cut"), i18n("Cu&t"), this); action_copy_selection = new QAction(QIcon::fromTheme("edit-copy"), i18n("&Copy"), this); action_paste_into_selection = new QAction(QIcon::fromTheme("edit-paste"), i18n("Past&e"), this); action_mask_selection = new QAction(QIcon::fromTheme("edit-node"), i18n("&Mask Selection"), this); action_unmask_selection = new QAction(QIcon::fromTheme("format-remove-node"), i18n("&Unmask Selection"), this); action_clear_selection = new QAction(QIcon::fromTheme("edit-clear"), i18n("Clea&r Selection"), this); action_select_all = new QAction(QIcon::fromTheme("edit-select-all"), i18n("Select All"), this); // action_set_formula = new QAction(QIcon::fromTheme(""), i18n("Assign &Formula"), this); // action_recalculate = new QAction(QIcon::fromTheme(""), i18n("Recalculate"), this); action_fill_sel_row_numbers = new QAction(QIcon::fromTheme(""), i18n("Row Numbers"), this); action_fill_row_numbers = new QAction(QIcon::fromTheme(""), i18n("Row Numbers"), this); action_fill_random = new QAction(QIcon::fromTheme(""), i18n("Uniform Random Values"), this); action_fill_random_nonuniform = new QAction(QIcon::fromTheme(""), i18n("Random Values"), this); action_fill_equidistant = new QAction(QIcon::fromTheme(""), i18n("Equidistant Values"), this); action_fill_function = new QAction(QIcon::fromTheme(""), i18n("Function Values"), this); action_fill_const = new QAction(QIcon::fromTheme(""), i18n("Const Values"), this); //spreadsheet related actions action_toggle_comments = new QAction(QIcon::fromTheme("document-properties"), i18n("Show Comments"), this); action_add_column = new QAction(QIcon::fromTheme("edit-table-insert-column-left"), i18n("&Add Column"), this); action_clear_spreadsheet = new QAction(QIcon::fromTheme("edit-clear"), i18n("Clear Spreadsheet"), this); action_clear_masks = new QAction(QIcon::fromTheme("format-remove-node"), i18n("Clear Masks"), this); action_sort_spreadsheet = new QAction(QIcon::fromTheme("view-sort-ascending"), i18n("&Sort Spreadsheet"), this); action_go_to_cell = new QAction(QIcon::fromTheme("go-jump"), i18n("&Go to Cell"), this); action_statistics_all_columns = new QAction(QIcon::fromTheme("view-statistics"), i18n("Statisti&cs"), this ); // column related actions action_insert_columns = new QAction(QIcon::fromTheme("edit-table-insert-column-left"), i18n("&Insert Empty Columns"), this); action_remove_columns = new QAction(QIcon::fromTheme("edit-table-delete-column"), i18n("Remo&ve Columns"), this); action_clear_columns = new QAction(QIcon::fromTheme("edit-clear"), i18n("Clea&r Columns"), this); action_add_columns = new QAction(QIcon::fromTheme("edit-table-insert-column-right"), i18n("&Add Columns"), this); action_set_as_none = new QAction(i18n("None"), this); action_set_as_none->setData(AbstractColumn::NoDesignation); action_set_as_x = new QAction("X", this); action_set_as_x->setData(AbstractColumn::X); action_set_as_y = new QAction("Y", this); action_set_as_y->setData(AbstractColumn::Y); action_set_as_z = new QAction("Z", this); action_set_as_z->setData(AbstractColumn::Z); action_set_as_xerr = new QAction(i18n("X-error"), this); action_set_as_xerr->setData(AbstractColumn::XError); action_set_as_xerr_minus = new QAction(i18n("X-error minus"), this); action_set_as_xerr_minus->setData(AbstractColumn::XErrorMinus); action_set_as_xerr_plus = new QAction(i18n("X-error plus"), this); action_set_as_xerr_plus->setData(AbstractColumn::XErrorPlus); action_set_as_yerr = new QAction(i18n("Y-error"), this); action_set_as_yerr->setData(AbstractColumn::YError); action_set_as_yerr_minus = new QAction(i18n("Y-error minus"), this); action_set_as_yerr_minus->setData(AbstractColumn::YErrorMinus); action_set_as_yerr_plus = new QAction(i18n("Y-error plus"), this); action_set_as_yerr_plus->setData(AbstractColumn::YErrorPlus); action_reverse_columns = new QAction(QIcon::fromTheme(""), i18n("Reverse"), this); action_drop_values = new QAction(QIcon::fromTheme(""), i18n("Drop Values"), this); action_mask_values = new QAction(QIcon::fromTheme(""), i18n("Mask Values"), this); // action_join_columns = new QAction(QIcon::fromTheme(""), i18n("Join"), this); action_normalize_columns = new QAction(QIcon::fromTheme(""), i18n("&Normalize"), this); action_normalize_selection = new QAction(QIcon::fromTheme(""), i18n("&Normalize Selection"), this); action_sort_columns = new QAction(QIcon::fromTheme(""), i18n("&Selected Columns"), this); action_sort_asc_column = new QAction(QIcon::fromTheme("view-sort-ascending"), i18n("&Ascending"), this); action_sort_desc_column = new QAction(QIcon::fromTheme("view-sort-descending"), i18n("&Descending"), this); action_statistics_columns = new QAction(QIcon::fromTheme("view-statistics"), i18n("Column Statisti&cs"), this); // row related actions action_insert_rows = new QAction(QIcon::fromTheme("edit-table-insert-row-above") ,i18n("&Insert Empty Rows"), this); action_remove_rows = new QAction(QIcon::fromTheme("edit-table-delete-row"), i18n("Remo&ve Rows"), this); action_clear_rows = new QAction(QIcon::fromTheme("edit-clear"), i18n("Clea&r Rows"), this); action_add_rows = new QAction(QIcon::fromTheme("edit-table-insert-row-above"), i18n("&Add Rows"), this); action_statistics_rows = new QAction(QIcon::fromTheme("view-statistics"), i18n("Row Statisti&cs"), this); //plotting and analysis related actions action_plot_data = new QAction(QIcon::fromTheme("office-chart-line"), i18n("Plot data"), this); } void SpreadsheetView::initMenus() { //Selection menu m_selectionMenu = new QMenu(i18n("Selection"), this); + QMenu* submenu = nullptr; + + if (!m_readOnly) { + submenu= new QMenu(i18n("Fi&ll Selection with"), this); + submenu->addAction(action_fill_sel_row_numbers); + submenu->addAction(action_fill_const); + // submenu->addAction(action_fill_random); + m_selectionMenu->addMenu(submenu); + m_selectionMenu->addSeparator(); + } - QMenu* submenu = new QMenu(i18n("Fi&ll Selection with"), this); - submenu->addAction(action_fill_sel_row_numbers); - submenu->addAction(action_fill_const); -// submenu->addAction(action_fill_random); - m_selectionMenu ->addMenu(submenu); - m_selectionMenu ->addSeparator(); - - m_selectionMenu ->addAction(action_cut_selection); - m_selectionMenu ->addAction(action_copy_selection); - m_selectionMenu ->addAction(action_paste_into_selection); - m_selectionMenu ->addAction(action_clear_selection); - m_selectionMenu ->addSeparator(); - m_selectionMenu ->addAction(action_mask_selection); - m_selectionMenu ->addAction(action_unmask_selection); - m_selectionMenu ->addSeparator(); - m_selectionMenu ->addAction(action_normalize_selection); + if (!m_readOnly) + m_selectionMenu->addAction(action_cut_selection); + m_selectionMenu->addAction(action_copy_selection); + if (!m_readOnly) { + m_selectionMenu->addAction(action_paste_into_selection); + m_selectionMenu->addAction(action_clear_selection); + m_selectionMenu->addSeparator(); + m_selectionMenu->addAction(action_mask_selection); + m_selectionMenu->addAction(action_unmask_selection); + m_selectionMenu->addSeparator(); + m_selectionMenu->addAction(action_normalize_selection); + } //TODO -// m_selectionMenu ->addSeparator(); + // m_selectionMenu ->addSeparator(); // m_selectionMenu ->addAction(action_set_formula); // m_selectionMenu ->addAction(action_recalculate); //TODO add plot menu to spreadsheet- and column-menu, like in scidavis, origin etc. // Column menu m_columnMenu = new QMenu(this); m_columnMenu->addAction(action_plot_data); m_columnMenu->addSeparator(); - - submenu = new QMenu(i18n("Set Column As")); - submenu->addAction(action_set_as_x); - submenu->addAction(action_set_as_y); - submenu->addAction(action_set_as_z); - submenu->addSeparator(); - submenu->addAction(action_set_as_xerr); - submenu->addAction(action_set_as_xerr_minus); - submenu->addAction(action_set_as_xerr_plus); - submenu->addSeparator(); - submenu->addAction(action_set_as_yerr); - submenu->addAction(action_set_as_yerr_minus); - submenu->addAction(action_set_as_yerr_plus); - submenu->addSeparator(); - submenu->addAction(action_set_as_none); - m_columnMenu->addMenu(submenu); - m_columnMenu->addSeparator(); - - submenu = new QMenu(i18n("Generate Data"), this); - submenu->addAction(action_fill_row_numbers); - submenu->addAction(action_fill_const); -// submenu->addAction(action_fill_random); - submenu->addAction(action_fill_equidistant); - submenu->addAction(action_fill_random_nonuniform); - submenu->addAction(action_fill_function); - m_columnMenu->addMenu(submenu); - m_columnMenu->addSeparator(); - - m_columnMenu->addAction(action_reverse_columns); - m_columnMenu->addAction(action_drop_values); - m_columnMenu->addAction(action_mask_values); -// m_columnMenu->addAction(action_join_columns); - m_columnMenu->addAction(action_normalize_columns); - - submenu = new QMenu(i18n("Sort"), this); - submenu->setIcon(QIcon::fromTheme("view-sort-ascending")); - submenu->addAction(action_sort_asc_column); - submenu->addAction(action_sort_desc_column); - submenu->addAction(action_sort_columns); - m_columnMenu->addMenu(submenu); - m_columnMenu->addSeparator(); - - m_columnMenu->addAction(action_insert_columns); - m_columnMenu->addAction(action_remove_columns); - m_columnMenu->addAction(action_clear_columns); - m_columnMenu->addAction(action_add_columns); - m_columnMenu->addSeparator(); - + if (!m_readOnly) { + submenu = new QMenu(i18n("Set Column As")); + submenu->addAction(action_set_as_x); + submenu->addAction(action_set_as_y); + submenu->addAction(action_set_as_z); + submenu->addSeparator(); + submenu->addAction(action_set_as_xerr); + submenu->addAction(action_set_as_xerr_minus); + submenu->addAction(action_set_as_xerr_plus); + submenu->addSeparator(); + submenu->addAction(action_set_as_yerr); + submenu->addAction(action_set_as_yerr_minus); + submenu->addAction(action_set_as_yerr_plus); + submenu->addSeparator(); + submenu->addAction(action_set_as_none); + m_columnMenu->addMenu(submenu); + m_columnMenu->addSeparator(); + + + submenu = new QMenu(i18n("Generate Data"), this); + submenu->addAction(action_fill_row_numbers); + submenu->addAction(action_fill_const); + // submenu->addAction(action_fill_random); + submenu->addAction(action_fill_equidistant); + submenu->addAction(action_fill_random_nonuniform); + submenu->addAction(action_fill_function); + m_columnMenu->addMenu(submenu); + m_columnMenu->addSeparator(); + + m_columnMenu->addAction(action_reverse_columns); + m_columnMenu->addAction(action_drop_values); + m_columnMenu->addAction(action_mask_values); + // m_columnMenu->addAction(action_join_columns); + m_columnMenu->addAction(action_normalize_columns); + + submenu = new QMenu(i18n("Sort"), this); + submenu->setIcon(QIcon::fromTheme("view-sort-ascending")); + submenu->addAction(action_sort_asc_column); + submenu->addAction(action_sort_desc_column); + submenu->addAction(action_sort_columns); + m_columnMenu->addMenu(submenu); + m_columnMenu->addSeparator(); + + m_columnMenu->addAction(action_insert_columns); + m_columnMenu->addAction(action_remove_columns); + m_columnMenu->addAction(action_clear_columns); + m_columnMenu->addAction(action_add_columns); + m_columnMenu->addSeparator(); + } m_columnMenu->addAction(action_toggle_comments); m_columnMenu->addSeparator(); m_columnMenu->addAction(action_statistics_columns); action_statistics_columns->setVisible(false); //Spreadsheet menu m_spreadsheetMenu = new QMenu(this); m_spreadsheetMenu->addAction(action_plot_data); m_spreadsheetMenu->addSeparator(); m_spreadsheetMenu->addMenu(m_selectionMenu); m_spreadsheetMenu->addAction(action_toggle_comments); m_spreadsheetMenu->addSeparator(); m_spreadsheetMenu->addAction(action_select_all); - m_spreadsheetMenu->addAction(action_clear_spreadsheet); - m_spreadsheetMenu->addAction(action_clear_masks); - m_spreadsheetMenu->addAction(action_sort_spreadsheet); - m_spreadsheetMenu->addSeparator(); - m_spreadsheetMenu->addAction(action_add_column); - m_spreadsheetMenu->addSeparator(); + if (!m_readOnly) { + m_spreadsheetMenu->addAction(action_clear_spreadsheet); + m_spreadsheetMenu->addAction(action_clear_masks); + m_spreadsheetMenu->addAction(action_sort_spreadsheet); + m_spreadsheetMenu->addSeparator(); + + m_spreadsheetMenu->addAction(action_add_column); + m_spreadsheetMenu->addSeparator(); + } m_spreadsheetMenu->addAction(action_go_to_cell); m_spreadsheetMenu->addSeparator(); m_spreadsheetMenu->addAction(action_statistics_all_columns); action_statistics_all_columns->setVisible(true); //Row menu m_rowMenu = new QMenu(this); - m_rowMenu->addAction(action_insert_rows); - m_rowMenu->addAction(action_remove_rows); - m_rowMenu->addAction(action_clear_rows); - m_rowMenu->addAction(action_add_rows); - m_rowMenu->addSeparator(); + if (!m_readOnly) { - submenu = new QMenu(i18n("Fi&ll Selection with"), this); - submenu->addAction(action_fill_sel_row_numbers); -// submenu->addAction(action_fill_random); - submenu->addAction(action_fill_const); - m_rowMenu->addMenu(submenu); + m_rowMenu->addAction(action_insert_rows); + m_rowMenu->addAction(action_remove_rows); + m_rowMenu->addAction(action_clear_rows); + m_rowMenu->addAction(action_add_rows); + m_rowMenu->addSeparator(); - m_rowMenu->addSeparator(); + submenu = new QMenu(i18n("Fi&ll Selection with"), this); + submenu->addAction(action_fill_sel_row_numbers); + // submenu->addAction(action_fill_random); + submenu->addAction(action_fill_const); + m_rowMenu->addMenu(submenu); + + m_rowMenu->addSeparator(); + } m_rowMenu->addAction(action_statistics_rows); action_statistics_rows->setVisible(false); } void SpreadsheetView::connectActions() { connect(action_cut_selection, SIGNAL(triggered()), this, SLOT(cutSelection())); connect(action_copy_selection, SIGNAL(triggered()), this, SLOT(copySelection())); connect(action_paste_into_selection, SIGNAL(triggered()), this, SLOT(pasteIntoSelection())); connect(action_mask_selection, SIGNAL(triggered()), this, SLOT(maskSelection())); connect(action_unmask_selection, SIGNAL(triggered()), this, SLOT(unmaskSelection())); connect(action_clear_selection, SIGNAL(triggered()), this, SLOT(clearSelectedCells())); // connect(action_recalculate, SIGNAL(triggered()), this, SLOT(recalculateSelectedCells())); connect(action_fill_row_numbers, SIGNAL(triggered()), this, SLOT(fillWithRowNumbers())); connect(action_fill_sel_row_numbers, SIGNAL(triggered()), this, SLOT(fillSelectedCellsWithRowNumbers())); // connect(action_fill_random, SIGNAL(triggered()), this, SLOT(fillSelectedCellsWithRandomNumbers())); connect(action_fill_random_nonuniform, SIGNAL(triggered()), this, SLOT(fillWithRandomValues())); connect(action_fill_equidistant, SIGNAL(triggered()), this, SLOT(fillWithEquidistantValues())); connect(action_fill_function, SIGNAL(triggered()), this, SLOT(fillWithFunctionValues())); connect(action_fill_const, SIGNAL(triggered()), this, SLOT(fillSelectedCellsWithConstValues())); connect(action_select_all, SIGNAL(triggered()), m_tableView, SLOT(selectAll())); connect(action_add_column, SIGNAL(triggered()), m_spreadsheet, SLOT(appendColumn())); connect(action_clear_spreadsheet, SIGNAL(triggered()), m_spreadsheet, SLOT(clear())); connect(action_clear_masks, SIGNAL(triggered()), m_spreadsheet, SLOT(clearMasks())); connect(action_sort_spreadsheet, SIGNAL(triggered()), this, SLOT(sortSpreadsheet())); connect(action_go_to_cell, SIGNAL(triggered()), this, SLOT(goToCell())); connect(action_insert_columns, SIGNAL(triggered()), this, SLOT(insertEmptyColumns())); connect(action_remove_columns, SIGNAL(triggered()), this, SLOT(removeSelectedColumns())); connect(action_clear_columns, SIGNAL(triggered()), this, SLOT(clearSelectedColumns())); connect(action_add_columns, SIGNAL(triggered()), this, SLOT(addColumns())); connect(action_set_as_none, SIGNAL(triggered()), this, SLOT(setSelectionAs())); connect(action_set_as_x, SIGNAL(triggered()), this, SLOT(setSelectionAs())); connect(action_set_as_y, SIGNAL(triggered()), this, SLOT(setSelectionAs())); connect(action_set_as_z, SIGNAL(triggered()), this, SLOT(setSelectionAs())); connect(action_set_as_xerr, SIGNAL(triggered()), this, SLOT(setSelectionAs())); connect(action_set_as_xerr_minus, SIGNAL(triggered()), this, SLOT(setSelectionAs())); connect(action_set_as_xerr_plus, SIGNAL(triggered()), this, SLOT(setSelectionAs())); connect(action_set_as_yerr, SIGNAL(triggered()), this, SLOT(setSelectionAs())); connect(action_set_as_yerr_minus, SIGNAL(triggered()), this, SLOT(setSelectionAs())); connect(action_set_as_yerr_plus, SIGNAL(triggered()), this, SLOT(setSelectionAs())); connect(action_reverse_columns, SIGNAL(triggered()), this, SLOT(reverseColumns())); connect(action_drop_values, SIGNAL(triggered()), this, SLOT(dropColumnValues())); connect(action_mask_values, SIGNAL(triggered()), this, SLOT(maskColumnValues())); // connect(action_join_columns, SIGNAL(triggered()), this, SLOT(joinColumns())); connect(action_normalize_columns, SIGNAL(triggered()), this, SLOT(normalizeSelectedColumns())); connect(action_normalize_selection, SIGNAL(triggered()), this, SLOT(normalizeSelection())); connect(action_sort_columns, SIGNAL(triggered()), this, SLOT(sortSelectedColumns())); connect(action_sort_asc_column, SIGNAL(triggered()), this, SLOT(sortColumnAscending())); connect(action_sort_desc_column, SIGNAL(triggered()), this, SLOT(sortColumnDescending())); connect(action_statistics_columns, SIGNAL(triggered()), this, SLOT(showColumnStatistics())); connect(action_statistics_all_columns, SIGNAL(triggered()), this, SLOT(showAllColumnsStatistics())); connect(action_insert_rows, SIGNAL(triggered()), this, SLOT(insertEmptyRows())); connect(action_remove_rows, SIGNAL(triggered()), this, SLOT(removeSelectedRows())); connect(action_clear_rows, SIGNAL(triggered()), this, SLOT(clearSelectedRows())); connect(action_add_rows, SIGNAL(triggered()), this, SLOT(addRows())); connect(action_statistics_rows, SIGNAL(triggered()), this, SLOT(showRowStatistics())); connect(action_toggle_comments, SIGNAL(triggered()), this, SLOT(toggleComments())); connect(action_plot_data, SIGNAL(triggered()), this, SLOT(plotData())); } void SpreadsheetView::fillToolBar(QToolBar* toolBar) { - toolBar->addAction(action_insert_rows); - toolBar->addAction(action_add_rows); - toolBar->addAction(action_remove_rows); + if (!m_readOnly) { + toolBar->addAction(action_insert_rows); + toolBar->addAction(action_add_rows); + toolBar->addAction(action_remove_rows); + } toolBar->addAction(action_statistics_rows); - toolBar->addSeparator(); - toolBar->addAction(action_insert_columns); - toolBar->addAction(action_add_column); - toolBar->addAction(action_remove_columns); - toolBar->addAction(action_statistics_columns); + if (!m_readOnly) { + toolBar->addAction(action_insert_columns); + toolBar->addAction(action_add_column); + toolBar->addAction(action_remove_columns); + } - toolBar->addSeparator(); - toolBar->addAction(action_sort_asc_column); - toolBar->addAction(action_sort_desc_column); + toolBar->addAction(action_statistics_columns); + if (!m_readOnly) { + toolBar->addSeparator(); + toolBar->addAction(action_sort_asc_column); + toolBar->addAction(action_sort_desc_column); + } } /*! * Populates the menu \c menu with the spreadsheet and spreadsheet view relevant actions. * The menu is used * - as the context menu in SpreadsheetView * - as the "spreadsheet menu" in the main menu-bar (called form MainWin) * - as a part of the spreadsheet context menu in project explorer */ void SpreadsheetView::createContextMenu(QMenu* menu) const { Q_ASSERT(menu); QAction* firstAction = 0; // if we're populating the context menu for the project explorer, then //there're already actions available there. Skip the first title-action //and insert the action at the beginning of the menu. if (menu->actions().size()>1) firstAction = menu->actions().at(1); menu->insertAction(firstAction, action_plot_data); menu->insertSeparator(firstAction); menu->insertMenu(firstAction, m_selectionMenu); menu->insertAction(firstAction, action_toggle_comments); menu->insertSeparator(firstAction); menu->insertAction(firstAction, action_select_all); - menu->insertAction(firstAction, action_clear_spreadsheet); - menu->insertAction(firstAction, action_clear_masks); - menu->insertAction(firstAction, action_sort_spreadsheet); - menu->insertSeparator(firstAction); - menu->insertAction(firstAction, action_add_column); - menu->insertSeparator(firstAction); + if (!m_readOnly) { + menu->insertAction(firstAction, action_clear_spreadsheet); + menu->insertAction(firstAction, action_clear_masks); + menu->insertAction(firstAction, action_sort_spreadsheet); + menu->insertSeparator(firstAction); + menu->insertAction(firstAction, action_add_column); + menu->insertSeparator(firstAction); + } + menu->insertAction(firstAction, action_go_to_cell); menu->insertSeparator(firstAction); menu->insertAction(firstAction, action_statistics_all_columns); menu->insertSeparator(firstAction); } /*! * adds column specific actions in SpreadsheetView to the context menu shown in the project explorer. */ void SpreadsheetView::createColumnContextMenu(QMenu* menu) { QAction* firstAction = menu->actions().at(1); QMenu* submenu = new QMenu(i18n("Set Column As")); submenu->addAction(action_set_as_x); submenu->addAction(action_set_as_y); submenu->addAction(action_set_as_z); submenu->addSeparator(); submenu->addAction(action_set_as_xerr); submenu->addAction(action_set_as_xerr_minus); submenu->addAction(action_set_as_xerr_plus); submenu->addSeparator(); submenu->addAction(action_set_as_yerr); submenu->addAction(action_set_as_yerr_minus); submenu->addAction(action_set_as_yerr_plus); submenu->addSeparator(); submenu->addAction(action_set_as_none); menu->insertMenu(firstAction, submenu); - menu->insertSeparator(firstAction); - - submenu = new QMenu(i18n("Generate Data"), this); - submenu->insertAction(firstAction, action_fill_row_numbers); - submenu->insertAction(firstAction, action_fill_const); -// submenu->insertAction(firstAction, action_fill_random); - submenu->insertAction(firstAction, action_fill_equidistant); - submenu->insertAction(firstAction, action_fill_random_nonuniform); - submenu->insertAction(firstAction, action_fill_function); - menu->insertMenu(firstAction, submenu); - menu->insertSeparator(firstAction); - - menu->insertAction(firstAction, action_reverse_columns); - menu->insertAction(firstAction, action_drop_values); - menu->insertAction(firstAction, action_mask_values); -// menu->insertAction(firstAction, action_join_columns); - menu->insertAction(firstAction, action_normalize_columns); - - submenu = new QMenu(i18n("Sort"), this); - submenu->setIcon(QIcon::fromTheme("view-sort-ascending")); - submenu->insertAction(firstAction, action_sort_asc_column); - submenu->insertAction(firstAction, action_sort_desc_column); - submenu->insertAction(firstAction, action_sort_columns); - menu->insertMenu(firstAction, submenu); - menu->insertSeparator(firstAction); - + if (!m_readOnly) { + menu->insertSeparator(firstAction); + + submenu = new QMenu(i18n("Generate Data"), this); + submenu->insertAction(firstAction, action_fill_row_numbers); + submenu->insertAction(firstAction, action_fill_const); + // submenu->insertAction(firstAction, action_fill_random); + submenu->insertAction(firstAction, action_fill_equidistant); + submenu->insertAction(firstAction, action_fill_random_nonuniform); + submenu->insertAction(firstAction, action_fill_function); + menu->insertMenu(firstAction, submenu); + menu->insertSeparator(firstAction); + + menu->insertAction(firstAction, action_reverse_columns); + menu->insertAction(firstAction, action_drop_values); + menu->insertAction(firstAction, action_mask_values); + // menu->insertAction(firstAction, action_join_columns); + menu->insertAction(firstAction, action_normalize_columns); + + submenu = new QMenu(i18n("Sort"), this); + submenu->setIcon(QIcon::fromTheme("view-sort-ascending")); + submenu->insertAction(firstAction, action_sort_asc_column); + submenu->insertAction(firstAction, action_sort_desc_column); + submenu->insertAction(firstAction, action_sort_columns); + menu->insertMenu(firstAction, submenu); + menu->insertSeparator(firstAction); + } else { + action_sort_columns->setVisible(false); + action_sort_asc_column->setVisible(false); + action_sort_desc_column->setVisible(true); + } menu->insertAction(firstAction, action_statistics_columns); action_sort_columns->setVisible(false); - action_sort_asc_column->setVisible(true); - action_sort_desc_column->setVisible(true); + action_sort_asc_column->setVisible(false); + action_sort_desc_column->setVisible(false); //check whether we have non-numeric columns selected and deactivate actions for numeric columns const Column* column = selectedColumns().first(); bool numeric = (column->columnMode() == AbstractColumn::Numeric); action_fill_equidistant->setEnabled(numeric); action_fill_random_nonuniform->setEnabled(numeric); action_fill_function->setEnabled(numeric); action_statistics_columns->setVisible(numeric); } //SLOTS void SpreadsheetView::handleAspectAdded(const AbstractAspect* aspect) { const Column* col = dynamic_cast(aspect); if (!col || col->parentAspect() != m_spreadsheet) return; int index = m_spreadsheet->indexOfChild(col); if (col->width() == 0) m_tableView->resizeColumnToContents(index); else m_tableView->setColumnWidth(index, col->width()); connect(col, SIGNAL(requestProjectContextMenu(QMenu*)), this, SLOT(createColumnContextMenu(QMenu*))); } void SpreadsheetView::handleAspectAboutToBeRemoved(const AbstractAspect* aspect) { const Column* col = dynamic_cast(aspect); if (!col || col->parentAspect() != m_spreadsheet) return; disconnect(col, 0, this, 0); } void SpreadsheetView::handleHorizontalSectionResized(int logicalIndex, int oldSize, int newSize) { Q_UNUSED(logicalIndex); Q_UNUSED(oldSize); //save the new size in the column Column* col = m_spreadsheet->child(logicalIndex); col->setWidth(newSize); } /*! Advance current cell after [Return] or [Enter] was pressed. */ void SpreadsheetView::advanceCell() { QModelIndex idx = m_tableView->currentIndex(); if (idx.row()+1 >= m_spreadsheet->rowCount()) { int new_size = m_spreadsheet->rowCount()+1; m_spreadsheet->setRowCount(new_size); } m_tableView->setCurrentIndex(idx.sibling(idx.row()+1, idx.column())); } void SpreadsheetView::goToCell(int row, int col) { QModelIndex index = m_model->index(row, col); m_tableView->scrollTo(index); m_tableView->setCurrentIndex(index); } void SpreadsheetView::handleHorizontalSectionMoved(int index, int from, int to) { Q_UNUSED(index); static bool inside = false; if (inside) return; Q_ASSERT(index == from); inside = true; m_tableView->horizontalHeader()->moveSection(to, from); inside = false; m_spreadsheet->moveColumn(from, to); } //TODO Implement the "change of the column name"-mode upon a double click void SpreadsheetView::handleHorizontalHeaderDoubleClicked(int index) { Q_UNUSED(index); } /*! Returns whether comments are show currently or not. */ bool SpreadsheetView::areCommentsShown() const { return m_horizontalHeader->areCommentsShown(); } /*! toggles the column comment in the horizontal header */ void SpreadsheetView::toggleComments() { showComments(!areCommentsShown()); //TODO if (areCommentsShown()) action_toggle_comments->setText(i18n("Hide Comments")); else action_toggle_comments->setText(i18n("Show Comments")); } //! Shows (\c on=true) or hides (\c on=false) the column comments in the horizontal header void SpreadsheetView::showComments(bool on) { m_horizontalHeader->showComments(on); } void SpreadsheetView::currentColumnChanged(const QModelIndex & current, const QModelIndex & previous) { Q_UNUSED(previous); int col = current.column(); if (col < 0 || col >= m_spreadsheet->columnCount()) return; } //TODO void SpreadsheetView::handleHeaderDataChanged(Qt::Orientation orientation, int first, int last) { if (orientation != Qt::Horizontal) return; QItemSelectionModel * sel_model = m_tableView->selectionModel(); int col = sel_model->currentIndex().column(); if (col < first || col > last) return; } /*! Returns the number of selected columns. If \c full is \c true, this function only returns the number of fully selected columns. */ int SpreadsheetView::selectedColumnCount(bool full) { int count = 0; int cols = m_spreadsheet->columnCount(); for (int i=0; icolumnCount(); for (int i=0; icolumn(i)->plotDesignation() == pd) ) count++; return count; } /*! Returns \c true if column \param col is selected, otherwise returns \c false. If \param full is \c true, this function only returns true if the whole column is selected. */ bool SpreadsheetView::isColumnSelected(int col, bool full) { if (full) return m_tableView->selectionModel()->isColumnSelected(col, QModelIndex()); else return m_tableView->selectionModel()->columnIntersectsSelection(col, QModelIndex()); } /*! Returns all selected columns. If \param full is true, this function only returns a column if the whole column is selected. */ QList SpreadsheetView::selectedColumns(bool full) { QList list; int cols = m_spreadsheet->columnCount(); for (int i=0; icolumn(i); return list; } /*! Returns \c true if row \param row is selected; otherwise returns \c false If \param full is \c true, this function only returns \c true if the whole row is selected. */ bool SpreadsheetView::isRowSelected(int row, bool full) { if (full) return m_tableView->selectionModel()->isRowSelected(row, QModelIndex()); else return m_tableView->selectionModel()->rowIntersectsSelection(row, QModelIndex()); } /*! Return the index of the first selected column. If \param full is \c true, this function only looks for fully selected columns. */ int SpreadsheetView::firstSelectedColumn(bool full) { int cols = m_spreadsheet->columnCount(); for (int i=0; icolumnCount(); for (int i=cols-1; i>=0; i--) if (isColumnSelected(i, full)) return i; return -2; } /*! Return the index of the first selected row. If \param full is \c true, this function only looks for fully selected rows. */ int SpreadsheetView::firstSelectedRow(bool full) { QModelIndexList indexes; if (!full) indexes = m_tableView->selectionModel()->selectedIndexes(); else indexes = m_tableView->selectionModel()->selectedRows(); if (!indexes.empty()) return indexes.first().row(); else return -1; } /*! Return the index of the last selected row. If \param full is \c true, this function only looks for fully selected rows. */ int SpreadsheetView::lastSelectedRow(bool full) { QModelIndexList indexes; if (!full) indexes = m_tableView->selectionModel()->selectedIndexes(); else indexes = m_tableView->selectionModel()->selectedRows(); if (!indexes.empty()) return indexes.last().row(); else return -2; } /*! Return whether a cell is selected */ bool SpreadsheetView::isCellSelected(int row, int col) { if (row < 0 || col < 0 || row >= m_spreadsheet->rowCount() || col >= m_spreadsheet->columnCount()) return false; return m_tableView->selectionModel()->isSelected(m_model->index(row, col)); } /*! Get the complete set of selected rows. */ IntervalAttribute SpreadsheetView::selectedRows(bool full) { IntervalAttribute result; int rows = m_spreadsheet->rowCount(); for (int i=0; iselectionModel()->select(m_model->index(row, col), select ? QItemSelectionModel::Select : QItemSelectionModel::Deselect); } /*! Select/Deselect a range of cells. */ void SpreadsheetView::setCellsSelected(int first_row, int first_col, int last_row, int last_col, bool select) { QModelIndex top_left = m_model->index(first_row, first_col); QModelIndex bottom_right = m_model->index(last_row, last_col); m_tableView->selectionModel()->select(QItemSelection(top_left, bottom_right), select ? QItemSelectionModel::SelectCurrent : QItemSelectionModel::Deselect); } /*! Determine the current cell (-1 if no cell is designated as the current). */ void SpreadsheetView::getCurrentCell(int * row, int * col) { QModelIndex index = m_tableView->selectionModel()->currentIndex(); if (index.isValid()) { *row = index.row(); *col = index.column(); } else { *row = -1; *col = -1; } } bool SpreadsheetView::eventFilter(QObject* watched, QEvent* event) { if (event->type() == QEvent::ContextMenu) { QContextMenuEvent* cm_event = static_cast(event); const QPoint global_pos = cm_event->globalPos(); if (watched == m_tableView->verticalHeader()) { bool onlyNumeric = true; for (int i = 0; i < m_spreadsheet->columnCount(); ++i) { if (m_spreadsheet->column(i)->columnMode() != AbstractColumn::Numeric) { onlyNumeric = false; break; } } action_statistics_rows->setVisible(onlyNumeric); m_rowMenu->exec(global_pos); } else if (watched == m_horizontalHeader) { const int col = m_horizontalHeader->logicalIndexAt(cm_event->pos()); if (!isColumnSelected(col, true)) { QItemSelectionModel *sel_model = m_tableView->selectionModel(); sel_model->clearSelection(); sel_model->select(QItemSelection(m_model->index(0, col, QModelIndex()), m_model->index(m_model->rowCount()-1, col, QModelIndex())), QItemSelectionModel::Select); } if (selectedColumns().size()==1) { action_sort_columns->setVisible(false); action_sort_asc_column->setVisible(true); action_sort_desc_column->setVisible(true); } else { action_sort_columns->setVisible(true); action_sort_asc_column->setVisible(false); action_sort_desc_column->setVisible(false); } //check whether we have non-numeric columns selected and deactivate actions for numeric columns bool numeric = true; for(const Column* col: selectedColumns()) { if (col->columnMode() != AbstractColumn::Numeric) { numeric = false; break; } } action_fill_equidistant->setEnabled(numeric); action_fill_random_nonuniform->setEnabled(numeric); action_fill_function->setEnabled(numeric); action_statistics_columns->setVisible(numeric); m_columnMenu->exec(global_pos); } else if (watched == this) m_spreadsheetMenu->exec(global_pos); else return QWidget::eventFilter(watched, event); return true; } else return QWidget::eventFilter(watched, event); } bool SpreadsheetView::formulaModeActive() const { return m_model->formulaModeActive(); } void SpreadsheetView::activateFormulaMode(bool on) { m_model->activateFormulaMode(on); } void SpreadsheetView::goToNextColumn() { if (m_spreadsheet->columnCount() == 0) return; QModelIndex idx = m_tableView->currentIndex(); int col = idx.column()+1; if (col >= m_spreadsheet->columnCount()) col = 0; m_tableView->setCurrentIndex(idx.sibling(idx.row(), col)); } void SpreadsheetView::goToPreviousColumn() { if (m_spreadsheet->columnCount() == 0) return; QModelIndex idx = m_tableView->currentIndex(); int col = idx.column()-1; if (col < 0) col = m_spreadsheet->columnCount()-1; m_tableView->setCurrentIndex(idx.sibling(idx.row(), col)); } void SpreadsheetView::cutSelection() { int first = firstSelectedRow(); if ( first < 0 ) return; WAIT_CURSOR; m_spreadsheet->beginMacro(i18n("%1: cut selected cells", m_spreadsheet->name())); copySelection(); clearSelectedCells(); m_spreadsheet->endMacro(); RESET_CURSOR; } void SpreadsheetView::copySelection() { int first_col = firstSelectedColumn(); if (first_col == -1) return; int last_col = lastSelectedColumn(); if (last_col == -2) return; int first_row = firstSelectedRow(); if (first_row == -1) return; int last_row = lastSelectedRow(); if (last_row == -2) return; int cols = last_col - first_col +1; int rows = last_row - first_row +1; WAIT_CURSOR; QString output_str; for (int r = 0; r < rows; r++) { for (int c = 0; c < cols; c++) { Column* col_ptr = m_spreadsheet->column(first_col + c); if (isCellSelected(first_row + r, first_col + c)) { if (formulaModeActive()) output_str += col_ptr->formula(first_row + r); else if (col_ptr->columnMode() == AbstractColumn::Numeric) { Double2StringFilter * out_fltr = static_cast(col_ptr->outputFilter()); output_str += QLocale().toString(col_ptr->valueAt(first_row + r), out_fltr->numericFormat(), 16); // copy with max. precision } else output_str += m_spreadsheet->column(first_col+c)->asStringColumn()->textAt(first_row + r); } if (c < cols-1) output_str += '\t'; } if (r < rows-1) output_str += '\n'; } QApplication::clipboard()->setText(output_str); RESET_CURSOR; } void SpreadsheetView::pasteIntoSelection() { if (m_spreadsheet->columnCount() < 1 || m_spreadsheet->rowCount() < 1) return; WAIT_CURSOR; m_spreadsheet->beginMacro(i18n("%1: paste from clipboard", m_spreadsheet->name())); const QMimeData* mime_data = QApplication::clipboard()->mimeData(); if (mime_data->hasFormat("text/plain")) { int first_col = firstSelectedColumn(); int last_col = lastSelectedColumn(); int first_row = firstSelectedRow(); int last_row = lastSelectedRow(); int input_row_count = 0; int input_col_count = 0; int rows, cols; QString input_str = QString(mime_data->data("text/plain")).trimmed(); QList< QStringList > cellTexts; QStringList input_rows(input_str.split('\n')); input_row_count = input_rows.count(); input_col_count = 0; for (int i=0; i input_col_count) input_col_count = cellTexts.at(i).count(); } if ( (first_col == -1 || first_row == -1) || (last_row == first_row && last_col == first_col) ) // if the is no selection or only one cell selected, the // selection will be expanded to the needed size from the current cell { int current_row, current_col; getCurrentCell(¤t_row, ¤t_col); if (current_row == -1) current_row = 0; if (current_col == -1) current_col = 0; setCellSelected(current_row, current_col); first_col = current_col; first_row = current_row; last_row = first_row + input_row_count -1; last_col = first_col + input_col_count -1; // resize the spreadsheet if necessary if (last_col >= m_spreadsheet->columnCount()) { for (int i=0; icolumnCount(); i++) { Column * new_col = new Column(QString::number(i+1), AbstractColumn::Text); new_col->setPlotDesignation(AbstractColumn::Y); new_col->insertRows(0, m_spreadsheet->rowCount()); m_spreadsheet->addChild(new_col); } } if (last_row >= m_spreadsheet->rowCount()) m_spreadsheet->appendRows(last_row+1-m_spreadsheet->rowCount()); // select the rectangle to be pasted in setCellsSelected(first_row, first_col, last_row, last_col); } rows = last_row - first_row + 1; cols = last_col - first_col + 1; for (int r=0; rsetSuppressDataChangedSignal(true); if (isCellSelected(first_row + r, first_col + c) && (c < cellTexts.at(r).count()) ) { Column * col_ptr = m_spreadsheet->column(first_col + c); if (formulaModeActive()) col_ptr->setFormula(first_row + r, cellTexts.at(r).at(c)); else col_ptr->asStringColumn()->setTextAt(first_row+r, cellTexts.at(r).at(c)); } } } } m_spreadsheet->endMacro(); RESET_CURSOR; } void SpreadsheetView::maskSelection() { int first = firstSelectedRow(); if ( first < 0 ) return; int last = lastSelectedRow(); WAIT_CURSOR; m_spreadsheet->beginMacro(i18n("%1: mask selected cells", m_spreadsheet->name())); QList list = selectedColumns(); foreach(Column * col_ptr, list) { int col = m_spreadsheet->indexOfChild(col_ptr); for (int row=first; row<=last; row++) if (isCellSelected(row, col)) col_ptr->setMasked(row); } m_spreadsheet->endMacro(); RESET_CURSOR; } void SpreadsheetView::unmaskSelection() { int first = firstSelectedRow(); if ( first < 0 ) return; int last = lastSelectedRow(); WAIT_CURSOR; m_spreadsheet->beginMacro(i18n("%1: unmask selected cells", m_spreadsheet->name())); QList list = selectedColumns(); foreach(Column * col_ptr, list) { int col = m_spreadsheet->indexOfChild(col_ptr); for (int row=first; row<=last; row++) if (isCellSelected(row, col)) col_ptr->setMasked(row, false); } m_spreadsheet->endMacro(); RESET_CURSOR; } void SpreadsheetView::plotData() { PlotDataDialog* dlg = new PlotDataDialog(m_spreadsheet); dlg->exec(); } void SpreadsheetView::fillSelectedCellsWithRowNumbers() { if (selectedColumnCount() < 1) return; int first = firstSelectedRow(); if ( first < 0 ) return; int last = lastSelectedRow(); WAIT_CURSOR; m_spreadsheet->beginMacro(i18n("%1: fill cells with row numbers", m_spreadsheet->name())); for (auto* col_ptr: selectedColumns()) { int col = m_spreadsheet->indexOfChild(col_ptr); col_ptr->setSuppressDataChangedSignal(true); switch (col_ptr->columnMode()) { case AbstractColumn::Numeric: { - QVector results(last-first+1); - for (int row = first; row <= last; row++) - if (isCellSelected(row, col)) - results[row-first] = row + 1; - else - results[row-first] = col_ptr->valueAt(row); - col_ptr->replaceValues(first, results); - break; - } + QVector results(last-first+1); + for (int row = first; row <= last; row++) + if (isCellSelected(row, col)) + results[row-first] = row + 1; + else + results[row-first] = col_ptr->valueAt(row); + col_ptr->replaceValues(first, results); + break; + } case AbstractColumn::Integer: { - QVector results(last-first+1); - for (int row = first; row <= last; row++) - if (isCellSelected(row, col)) - results[row-first] = row + 1; - else - results[row-first] = col_ptr->integerAt(row); - col_ptr->replaceInteger(first, results); - break; - } + QVector results(last-first+1); + for (int row = first; row <= last; row++) + if (isCellSelected(row, col)) + results[row-first] = row + 1; + else + results[row-first] = col_ptr->integerAt(row); + col_ptr->replaceInteger(first, results); + break; + } case AbstractColumn::Text: { - QVector results; - for (int row = first; row <= last; row++) - if (isCellSelected(row, col)) - results << QString::number(row+1); - else - results << col_ptr->textAt(row); - col_ptr->replaceTexts(first, results); - break; - } + QVector results; + for (int row = first; row <= last; row++) + if (isCellSelected(row, col)) + results << QString::number(row+1); + else + results << col_ptr->textAt(row); + col_ptr->replaceTexts(first, results); + break; + } //TODO: handle other modes case AbstractColumn::DateTime: case AbstractColumn::Month: case AbstractColumn::Day: break; } col_ptr->setSuppressDataChangedSignal(false); col_ptr->setChanged(); } m_spreadsheet->endMacro(); RESET_CURSOR; } void SpreadsheetView::fillWithRowNumbers() { if (selectedColumnCount() < 1) return; WAIT_CURSOR; m_spreadsheet->beginMacro(i18np("%1: fill column with row numbers", "%1: fill columns with row numbers", m_spreadsheet->name(), selectedColumnCount())); const int rows = m_spreadsheet->rowCount(); QVector double_data(rows); QVector int_data(rows); for (int i = 0; i < rows; ++i) double_data[i] = int_data[i] = i+1; for (auto* col: selectedColumns()) { switch (col->columnMode()) { case AbstractColumn::Numeric: col->replaceValues(0, double_data); break; case AbstractColumn::Integer: col->replaceInteger(0, int_data); break; case AbstractColumn::Text: case AbstractColumn::DateTime: case AbstractColumn::Day: case AbstractColumn::Month: break; } } m_spreadsheet->endMacro(); RESET_CURSOR; } //TODO: this function is not used currently. void SpreadsheetView::fillSelectedCellsWithRandomNumbers() { if (selectedColumnCount() < 1) return; int first = firstSelectedRow(); int last = lastSelectedRow(); if (first < 0) return; WAIT_CURSOR; m_spreadsheet->beginMacro(i18n("%1: fill cells with random values", m_spreadsheet->name())); qsrand(QTime::currentTime().msec()); for (auto* col_ptr: selectedColumns()) { int col = m_spreadsheet->indexOfChild(col_ptr); col_ptr->setSuppressDataChangedSignal(true); switch (col_ptr->columnMode()) { case AbstractColumn::Numeric: { - QVector results(last-first+1); - for (int row = first; row <= last; row++) - if (isCellSelected(row, col)) - results[row-first] = double(qrand())/double(RAND_MAX); - else - results[row-first] = col_ptr->valueAt(row); - col_ptr->replaceValues(first, results); - break; - } + QVector results(last-first+1); + for (int row = first; row <= last; row++) + if (isCellSelected(row, col)) + results[row-first] = double(qrand())/double(RAND_MAX); + else + results[row-first] = col_ptr->valueAt(row); + col_ptr->replaceValues(first, results); + break; + } case AbstractColumn::Integer: { - QVector results(last-first+1); - for (int row = first; row <= last; row++) - if (isCellSelected(row, col)) - results[row-first] = qrand(); - else - results[row-first] = col_ptr->integerAt(row); - col_ptr->replaceInteger(first, results); - break; - } + QVector results(last-first+1); + for (int row = first; row <= last; row++) + if (isCellSelected(row, col)) + results[row-first] = qrand(); + else + results[row-first] = col_ptr->integerAt(row); + col_ptr->replaceInteger(first, results); + break; + } case AbstractColumn::Text: { - QVector results; - for (int row = first; row <= last; row++) - if (isCellSelected(row, col)) - results << QString::number(double(qrand())/double(RAND_MAX)); - else - results << col_ptr->textAt(row); - col_ptr->replaceTexts(first, results); - break; - } + QVector results; + for (int row = first; row <= last; row++) + if (isCellSelected(row, col)) + results << QString::number(double(qrand())/double(RAND_MAX)); + else + results << col_ptr->textAt(row); + col_ptr->replaceTexts(first, results); + break; + } case AbstractColumn::DateTime: case AbstractColumn::Month: case AbstractColumn::Day: { - QVector results; - QDate earliestDate(1,1,1); - QDate latestDate(2999,12,31); - QTime midnight(0,0,0,0); - for (int row = first; row <= last; row++) - if (isCellSelected(row, col)) - results << QDateTime( earliestDate.addDays(((double)qrand())*((double)earliestDate.daysTo(latestDate))/((double)RAND_MAX)), midnight.addMSecs(((qint64)qrand())*1000*60*60*24/RAND_MAX)); - else - results << col_ptr->dateTimeAt(row); - col_ptr->replaceDateTimes(first, results); - break; - } + QVector results; + QDate earliestDate(1,1,1); + QDate latestDate(2999,12,31); + QTime midnight(0,0,0,0); + for (int row = first; row <= last; row++) + if (isCellSelected(row, col)) + results << QDateTime( earliestDate.addDays(((double)qrand())*((double)earliestDate.daysTo(latestDate))/((double)RAND_MAX)), midnight.addMSecs(((qint64)qrand())*1000*60*60*24/RAND_MAX)); + else + results << col_ptr->dateTimeAt(row); + col_ptr->replaceDateTimes(first, results); + break; + } } col_ptr->setSuppressDataChangedSignal(false); col_ptr->setChanged(); } m_spreadsheet->endMacro(); RESET_CURSOR; } void SpreadsheetView::fillWithRandomValues() { if (selectedColumnCount() < 1) return; RandomValuesDialog* dlg = new RandomValuesDialog(m_spreadsheet); dlg->setColumns(selectedColumns()); dlg->exec(); } void SpreadsheetView::fillWithEquidistantValues() { if (selectedColumnCount() < 1) return; EquidistantValuesDialog* dlg = new EquidistantValuesDialog(m_spreadsheet); dlg->setColumns(selectedColumns()); dlg->exec(); } void SpreadsheetView::fillWithFunctionValues() { if (selectedColumnCount() < 1) return; FunctionValuesDialog* dlg = new FunctionValuesDialog(m_spreadsheet); dlg->setColumns(selectedColumns()); dlg->exec(); } void SpreadsheetView::fillSelectedCellsWithConstValues() { if (selectedColumnCount() < 1) return; int first = firstSelectedRow(); int last = lastSelectedRow(); if (first < 0) return; bool doubleOk = false; bool intOk = false; bool stringOk = false; double doubleValue = 0; int intValue = 0; QString stringValue; m_spreadsheet->beginMacro(i18n("%1: fill cells with const values", m_spreadsheet->name())); for (auto* col_ptr: selectedColumns()) { int col = m_spreadsheet->indexOfChild(col_ptr); col_ptr->setSuppressDataChangedSignal(true); switch (col_ptr->columnMode()) { case AbstractColumn::Numeric: if (!doubleOk) doubleValue = QInputDialog::getDouble(this, i18n("Fill the selection with constant value"), - i18n("Value"), 0, -2147483647, 2147483647, 6, &doubleOk); + i18n("Value"), 0, -2147483647, 2147483647, 6, &doubleOk); if (doubleOk) { WAIT_CURSOR; QVector results(last-first+1); for (int row = first; row <= last; row++) { if (isCellSelected(row, col)) results[row-first] = doubleValue; else results[row-first] = col_ptr->valueAt(row); } col_ptr->replaceValues(first, results); RESET_CURSOR; } break; case AbstractColumn::Integer: if (!intOk) intValue = QInputDialog::getInt(this, i18n("Fill the selection with constant value"), - i18n("Value"), 0, -2147483647, 2147483647, 1, &intOk); + i18n("Value"), 0, -2147483647, 2147483647, 1, &intOk); if (intOk) { WAIT_CURSOR; QVector results(last-first+1); for (int row = first; row <= last; row++) { if (isCellSelected(row, col)) results[row-first] = intValue; else results[row-first] = col_ptr->integerAt(row); } col_ptr->replaceInteger(first, results); RESET_CURSOR; } break; case AbstractColumn::Text: if (!stringOk) stringValue = QInputDialog::getText(this, i18n("Fill the selection with constant value"), - i18n("Value"), QLineEdit::Normal, 0, &stringOk); + i18n("Value"), QLineEdit::Normal, 0, &stringOk); if (stringOk && !stringValue.isEmpty()) { WAIT_CURSOR; QVector results; for (int row = first; row <= last; row++) { if (isCellSelected(row, col)) results << stringValue; else results << col_ptr->textAt(row); } col_ptr->replaceTexts(first, results); RESET_CURSOR; } break; //TODO: handle other modes case AbstractColumn::DateTime: case AbstractColumn::Month: case AbstractColumn::Day: break; } col_ptr->setSuppressDataChangedSignal(false); col_ptr->setChanged(); } m_spreadsheet->endMacro(); } /*! Open the sort dialog for all columns. */ void SpreadsheetView::sortSpreadsheet() { sortDialog(m_spreadsheet->children()); } /*! Insert columns depending on the selection. */ void SpreadsheetView::insertEmptyColumns() { int first = firstSelectedColumn(); if ( first < 0 ) return; int last = lastSelectedColumn(); int count, current = first; WAIT_CURSOR; m_spreadsheet->beginMacro(i18n("%1: insert empty columns", m_spreadsheet->name())); int rows = m_spreadsheet->rowCount(); while (current <= last) { current = first+1; while (current <= last && isColumnSelected(current)) current++; count = current-first; Column* first_col = m_spreadsheet->child(first); for (int i = 0; i < count; i++) { Column* new_col = new Column(QString::number(i+1), AbstractColumn::Numeric); new_col->setPlotDesignation(AbstractColumn::Y); new_col->insertRows(0, rows); m_spreadsheet->insertChildBefore(new_col, first_col); } current += count; last += count; while (current <= last && !isColumnSelected(current)) current++; first = current; } m_spreadsheet->endMacro(); RESET_CURSOR; } void SpreadsheetView::removeSelectedColumns() { WAIT_CURSOR; m_spreadsheet->beginMacro(i18n("%1: remove selected columns", m_spreadsheet->name())); QList< Column* > list = selectedColumns(); foreach(Column* ptr, list) m_spreadsheet->removeChild(ptr); m_spreadsheet->endMacro(); RESET_CURSOR; } void SpreadsheetView::clearSelectedColumns() { WAIT_CURSOR; m_spreadsheet->beginMacro(i18n("%1: clear selected columns", m_spreadsheet->name())); QList list = selectedColumns(); if (formulaModeActive()) { for (auto* col: list) { col->setSuppressDataChangedSignal(true); col->clearFormulas(); col->setSuppressDataChangedSignal(false); col->setChanged(); } } else { for (auto* col: list) { col->setSuppressDataChangedSignal(true); col->clear(); col->setSuppressDataChangedSignal(false); col->setChanged(); } } m_spreadsheet->endMacro(); RESET_CURSOR; } void SpreadsheetView::setSelectionAs() { QList columns = selectedColumns(); if (!columns.size()) return; m_spreadsheet->beginMacro(i18n("%1: set plot designation", m_spreadsheet->name())); QAction* action = dynamic_cast(QObject::sender()); if (!action) return; AbstractColumn::PlotDesignation pd = (AbstractColumn::PlotDesignation)action->data().toInt(); for (auto* col: columns) col->setPlotDesignation(pd); m_spreadsheet->endMacro(); } void SpreadsheetView::reverseColumns() { WAIT_CURSOR; QList cols = selectedColumns(); m_spreadsheet->beginMacro(i18np("%1: reverse column", "%1: reverse columns", m_spreadsheet->name(), cols.size())); for (auto* col: cols) { if (col->columnMode() != AbstractColumn::Numeric) continue; QVector* data = static_cast* >(col->data()); QVector new_data(*data); std::reverse(new_data.begin(), new_data.end()); col->replaceValues(0, new_data); } m_spreadsheet->endMacro(); RESET_CURSOR; } void SpreadsheetView::dropColumnValues() { if (selectedColumnCount() < 1) return; DropValuesDialog* dlg = new DropValuesDialog(m_spreadsheet); dlg->setColumns(selectedColumns()); dlg->exec(); } void SpreadsheetView::maskColumnValues() { if (selectedColumnCount() < 1) return; DropValuesDialog* dlg = new DropValuesDialog(m_spreadsheet, true); dlg->setColumns(selectedColumns()); dlg->exec(); } void SpreadsheetView::joinColumns() { //TODO } void SpreadsheetView::normalizeSelectedColumns() { WAIT_CURSOR; m_spreadsheet->beginMacro(i18n("%1: normalize columns", m_spreadsheet->name())); QList< Column* > cols = selectedColumns(); for (auto* col: cols) { if (col->columnMode() == AbstractColumn::Numeric) { col->setSuppressDataChangedSignal(true); double max = col->maximum(); if (max != 0.0) {// avoid division by zero for (int row=0; rowrowCount(); row++) col->setValueAt(row, col->valueAt(row) / max); } col->setSuppressDataChangedSignal(false); col->setChanged(); } } m_spreadsheet->endMacro(); RESET_CURSOR; } void SpreadsheetView::normalizeSelection() { WAIT_CURSOR; m_spreadsheet->beginMacro(i18n("%1: normalize selection", m_spreadsheet->name())); double max = 0.0; for (int col = firstSelectedColumn(); col <= lastSelectedColumn(); col++) if (m_spreadsheet->column(col)->columnMode() == AbstractColumn::Numeric) for (int row = 0; row < m_spreadsheet->rowCount(); row++) { if (isCellSelected(row, col) && m_spreadsheet->column(col)->valueAt(row) > max) max = m_spreadsheet->column(col)->valueAt(row); } if (max != 0.0) { // avoid division by zero //TODO setSuppressDataChangedSignal for (int col = firstSelectedColumn(); col <= lastSelectedColumn(); col++) if (m_spreadsheet->column(col)->columnMode() == AbstractColumn::Numeric) for (int row = 0; row < m_spreadsheet->rowCount(); row++) { if (isCellSelected(row, col)) m_spreadsheet->column(col)->setValueAt(row, m_spreadsheet->column(col)->valueAt(row) / max); } } m_spreadsheet->endMacro(); RESET_CURSOR; } void SpreadsheetView::sortSelectedColumns() { - sortDialog(selectedColumns()); + sortDialog(selectedColumns()); } void SpreadsheetView::showAllColumnsStatistics() { showColumnStatistics(true); } void SpreadsheetView::showColumnStatistics(bool forAll) { QString dlgTitle(m_spreadsheet->name() + " column statistics"); StatisticsDialog* dlg = new StatisticsDialog(dlgTitle); QList list; if (!forAll) dlg->setColumns(selectedColumns()); else if (forAll) { for (int col = 0; col < m_spreadsheet->columnCount(); ++col) { if (m_spreadsheet->column(col)->columnMode() == AbstractColumn::Numeric) list << m_spreadsheet->column(col); } dlg->setColumns(list); } if (dlg->exec() == KDialog::Accepted) { if (forAll) list.clear(); } } void SpreadsheetView::showRowStatistics() { QString dlgTitle(m_spreadsheet->name() + " row statistics"); StatisticsDialog* dlg = new StatisticsDialog(dlgTitle); QList list; for (int i = 0; i < m_spreadsheet->rowCount(); ++i) { if (isRowSelected(i)) { QVector rowValues; for (int j = 0; j < m_spreadsheet->columnCount(); ++j) rowValues << m_spreadsheet->column(j)->valueAt(i); list << new Column(QString::number(i+1), rowValues); } } dlg->setColumns(list); if (dlg->exec() == KDialog::Accepted) { qDeleteAll(list); list.clear(); } } /*! Insert rows depending on the selection. */ void SpreadsheetView::insertEmptyRows() { int first = firstSelectedRow(); if (first < 0) return; int last = lastSelectedRow(); int count, current = first; WAIT_CURSOR; m_spreadsheet->beginMacro(i18n("%1: insert empty rows", m_spreadsheet->name())); while (current <= last) { current = first+1; while (current <= last && isRowSelected(current)) current++; count = current-first; m_spreadsheet->insertRows(first, count); current += count; last += count; while (current <= last && !isRowSelected(current)) current++; first = current; } m_spreadsheet->endMacro(); RESET_CURSOR; } void SpreadsheetView::removeSelectedRows() { if (firstSelectedRow() < 0) return; WAIT_CURSOR; m_spreadsheet->beginMacro(i18n("%1: remove selected rows", m_spreadsheet->name())); //TODO setSuppressDataChangedSignal for (const auto& i: selectedRows().intervals()) m_spreadsheet->removeRows(i.start(), i.size()); m_spreadsheet->endMacro(); RESET_CURSOR; } void SpreadsheetView::clearSelectedRows() { if (firstSelectedRow() < 0) return; WAIT_CURSOR; m_spreadsheet->beginMacro(i18n("%1: clear selected rows", m_spreadsheet->name())); for (auto* col: selectedColumns()) { col->setSuppressDataChangedSignal(true); if (formulaModeActive()) { for (const auto& i: selectedRows().intervals()) col->setFormula(i, ""); } else { for (const auto& i: selectedRows().intervals()) { if (i.end() == col->rowCount()-1) col->removeRows(i.start(), i.size()); else { QVector empties; for (int j = 0; j < i.size(); j++) empties << QString(); col->asStringColumn()->replaceTexts(i.start(), empties); } } } col->setSuppressDataChangedSignal(false); col->setChanged(); } m_spreadsheet->endMacro(); RESET_CURSOR; } void SpreadsheetView::clearSelectedCells() { int first = firstSelectedRow(); int last = lastSelectedRow(); if (first < 0) return; WAIT_CURSOR; m_spreadsheet->beginMacro(i18n("%1: clear selected cells", m_spreadsheet->name())); QList list = selectedColumns(); foreach (Column* col_ptr, list) { col_ptr->setSuppressDataChangedSignal(true); if (formulaModeActive()) { int col = m_spreadsheet->indexOfChild(col_ptr); for (int row = last; row >= first; row--) if (isCellSelected(row, col)) col_ptr->setFormula(row, ""); } else { int col = m_spreadsheet->indexOfChild(col_ptr); for (int row = last; row >= first; row--) if (isCellSelected(row, col)) { if (row < col_ptr->rowCount()) col_ptr->asStringColumn()->setTextAt(row, QString()); } } col_ptr->setSuppressDataChangedSignal(false); col_ptr->setChanged(); } m_spreadsheet->endMacro(); RESET_CURSOR; } void SpreadsheetView::goToCell() { bool ok; int col = QInputDialog::getInteger(0, i18n("Go to Cell"), i18n("Enter column"), 1, 1, m_spreadsheet->columnCount(), 1, &ok); if (!ok) return; int row = QInputDialog::getInteger(0, i18n("Go to Cell"), i18n("Enter row"), 1, 1, m_spreadsheet->rowCount(), 1, &ok); if (!ok) return; goToCell(row-1, col-1); } //! Open the sort dialog for the given columns void SpreadsheetView::sortDialog(QList cols) { if (cols.isEmpty()) return; for (auto* col: cols) col->setSuppressDataChangedSignal(true); SortDialog* dlg = new SortDialog(); connect(dlg, SIGNAL(sort(Column*,QList,bool)), m_spreadsheet, SLOT(sortColumns(Column*,QList,bool))); dlg->setColumnsList(cols); int rc = dlg->exec(); for (auto* col: cols) { col->setSuppressDataChangedSignal(false); if (rc == QDialog::Accepted) col->setChanged(); } } void SpreadsheetView::sortColumnAscending() { QList< Column* > cols = selectedColumns(); for (auto* col: cols) col->setSuppressDataChangedSignal(true); m_spreadsheet->sortColumns(cols.first(), cols, true); for (auto* col: cols) { col->setSuppressDataChangedSignal(false); col->setChanged(); } } void SpreadsheetView::sortColumnDescending() { QList< Column* > cols = selectedColumns(); for (auto* col: cols) col->setSuppressDataChangedSignal(true); m_spreadsheet->sortColumns(cols.first(), cols, false); for (auto* col: cols) { col->setSuppressDataChangedSignal(false); col->setChanged(); } } /*! Append as many columns as are selected. */ void SpreadsheetView::addColumns() { m_spreadsheet->appendColumns(selectedColumnCount(false)); } /*! Append as many rows as are selected. */ void SpreadsheetView::addRows() { m_spreadsheet->appendRows( m_tableView->selectionModel()->selectedRows().size() ); } /*! Cause a repaint of the header. */ void SpreadsheetView::updateHeaderGeometry(Qt::Orientation o, int first, int last) { Q_UNUSED(first) Q_UNUSED(last) //TODO if (o != Qt::Horizontal) return; m_tableView->horizontalHeader()->setStretchLastSection(true); // ugly hack (flaw in Qt? Does anyone know a better way?) m_tableView->horizontalHeader()->updateGeometry(); m_tableView->horizontalHeader()->setStretchLastSection(false); // ugly hack part 2 } void SpreadsheetView::keyPressEvent(QKeyEvent * event) { if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) advanceCell(); } /*! selects the column \c column in the speadsheet view . */ void SpreadsheetView::selectColumn(int column) { QItemSelection selection(m_model->index(0, column), m_model->index(m_spreadsheet->rowCount()-1, column) ); m_suppressSelectionChangedEvent = true; m_tableView->selectionModel()->select(selection, QItemSelectionModel::Select); m_suppressSelectionChangedEvent = false; } /*! deselects the column \c column in the speadsheet view . */ void SpreadsheetView::deselectColumn(int column) { QItemSelection selection(m_model->index(0, column), m_model->index(m_spreadsheet->rowCount()-1, column) ); m_suppressSelectionChangedEvent = true; m_tableView->selectionModel()->select(selection, QItemSelectionModel::Deselect); m_suppressSelectionChangedEvent = false; } /*! called when a column in the speadsheet view was clicked (click in the header). Propagates the selection of the column to the \c Spreadsheet object (a click in the header always selects the column). */ void SpreadsheetView::columnClicked(int column) { m_spreadsheet->setColumnSelectedInView(column, true); } /*! called on selections changes. Propagates the selection/deselection of columns to the \c Spreadsheet object. */ void SpreadsheetView::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) { Q_UNUSED(selected); Q_UNUSED(deselected); if (m_suppressSelectionChangedEvent) return; QItemSelectionModel* selModel = m_tableView->selectionModel(); for (int i=0; icolumnCount(); i++) m_spreadsheet->setColumnSelectedInView(i, selModel->isColumnSelected(i, QModelIndex())); } /*! prints the complete spreadsheet to \c printer. */ void SpreadsheetView::print(QPrinter* printer) const { WAIT_CURSOR; QPainter painter (printer); const int dpiy = printer->logicalDpiY(); const int margin = (int) ( (1/2.54)*dpiy ); // 1 cm margins QHeaderView *hHeader = m_tableView->horizontalHeader(); QHeaderView *vHeader = m_tableView->verticalHeader(); const int rows = m_spreadsheet->rowCount(); const int cols = m_spreadsheet->columnCount(); int height = margin; int i; const int vertHeaderWidth = vHeader->width(); int right = margin + vertHeaderWidth; int columnsPerTable = 0; int headerStringWidth = 0; int firstRowStringWidth = 0; bool tablesNeeded = false; for (int col = 0; col < cols; ++col) { headerStringWidth += m_tableView->columnWidth(col); firstRowStringWidth += m_spreadsheet->column(col)->asStringColumn()->textAt(0).length(); if ((headerStringWidth >= printer->pageRect().width() -2*margin) || (firstRowStringWidth >= printer->pageRect().width() - 2*margin)) { tablesNeeded = true; break; } columnsPerTable++; } int tablesCount = (columnsPerTable != 0) ? cols/columnsPerTable : 0; const int remainingColumns = (columnsPerTable != 0) ? cols % columnsPerTable : cols; if (!tablesNeeded) { tablesCount = 1; columnsPerTable = cols; } if (remainingColumns > 0) tablesCount++; //Paint the horizontal header first for (int table = 0; table < tablesCount; ++table) { right = margin + vertHeaderWidth; painter.setFont(hHeader->font()); QString headerString = m_tableView->model()->headerData(0, Qt::Horizontal).toString(); QRect br; br = painter.boundingRect(br, Qt::AlignCenter, headerString); QRect tr(br); if (table != 0) height += tr.height(); painter.drawLine(right, height, right, height+br.height()); int w; i = table * columnsPerTable; int toI = table * columnsPerTable + columnsPerTable; if ((remainingColumns > 0) && (table == tablesCount-1)) { i = (tablesCount-1)*columnsPerTable; toI = (tablesCount-1)* columnsPerTable + remainingColumns; } for (; imodel()->headerData(i, Qt::Horizontal).toString(); w = m_tableView->columnWidth(i); tr.setTopLeft(QPoint(right,height)); tr.setWidth(w); tr.setHeight(br.height()); painter.drawText(tr, Qt::AlignCenter, headerString); right += w; painter.drawLine(right, height, right, height+tr.height()); } painter.drawLine(margin + vertHeaderWidth, height, right-1, height);//first horizontal line height += tr.height(); painter.drawLine(margin, height, right-1, height); // print table values QString cellText; for (i=0; imodel()->headerData(i, Qt::Vertical).toString()+'\t'; tr = painter.boundingRect(tr, Qt::AlignCenter, cellText); painter.drawLine(right, height, right, height+tr.height()); br.setTopLeft(QPoint(right,height)); br.setWidth(vertHeaderWidth); br.setHeight(tr.height()); painter.drawText(br, Qt::AlignCenter, cellText); right += vertHeaderWidth; painter.drawLine(right, height, right, height+tr.height()); int j = table * columnsPerTable; int toJ = table * columnsPerTable + columnsPerTable; if ((remainingColumns > 0) && (table == tablesCount-1)) { j = (tablesCount-1)*columnsPerTable; toJ = (tablesCount-1)* columnsPerTable + remainingColumns; } for (; j< toJ; j++) { int w = m_tableView->columnWidth(j); cellText = m_spreadsheet->column(j)->isValid(i) ? m_spreadsheet->text(i,j)+'\t': QLatin1String("- \t"); tr = painter.boundingRect(tr,Qt::AlignCenter,cellText); br.setTopLeft(QPoint(right,height)); br.setWidth(w); br.setHeight(tr.height()); painter.drawText(br, Qt::AlignCenter, cellText); right += w; painter.drawLine(right, height, right, height+tr.height()); } height += br.height(); painter.drawLine(margin, height, right-1, height); if (height >= printer->height()-margin ) { printer->newPage(); height = margin; painter.drawLine(margin, height, right, height); } } } RESET_CURSOR; } void SpreadsheetView::exportToFile(const QString& path, const bool exportHeader, const QString& separator) const { QFile file(path); if (!file.open(QFile::WriteOnly | QFile::Truncate)) return; QTextStream out(&file); const int cols = m_spreadsheet->columnCount(); QString sep = separator; sep = sep.replace(QLatin1String("TAB"), QLatin1String("\t"), Qt::CaseInsensitive); sep = sep.replace(QLatin1String("SPACE"), QLatin1String(" "), Qt::CaseInsensitive); //export header (column names) if (exportHeader) { for (int j=0; jcolumn(j)->name(); if (j!=cols-1) out<rowCount(); ++i) { for (int j=0; jcolumn(j)->asStringColumn()->textAt(i); if (j!=cols-1) out< toExport; int cols; int totalRowCount = 0; if (exportEntire) { cols = const_cast(this)->m_spreadsheet->columnCount(); totalRowCount = m_spreadsheet->rowCount(); for (int col = 0; col < cols; ++col) toExport << m_spreadsheet->column(col); } else { cols = const_cast(this)->selectedColumnCount(); const int firtsSelectedCol = const_cast(this)->firstSelectedColumn(); bool rowsCalculated = false; for (int col = firtsSelectedCol; col < firtsSelectedCol + cols; ++col) { QVector textData; for (int row = 0; row < m_spreadsheet->rowCount(); ++row) { if (const_cast(this)->isRowSelected(row)) { textData << m_spreadsheet->column(col)->asStringColumn()->textAt(row); if (!rowsCalculated) totalRowCount++; } } if (!rowsCalculated) rowsCalculated = true; Column* column = new Column(m_spreadsheet->column(col)->name(), textData); toExport << column; } } int columnsStringSize = 0; int columnsPerTable = 0; for (int i = 0; i < cols; ++i) { int maxSize = -1; for (int j = 0; j < toExport.at(i)->asStringColumn()->rowCount(); ++j) { if (toExport.at(i)->asStringColumn()->textAt(j).size() > maxSize) maxSize = toExport.at(i)->asStringColumn()->textAt(j).size(); } columnsStringSize += maxSize; if (!toExport.at(i)->isValid(0)) columnsStringSize+=3; if (columnsStringSize > 65) break; ++columnsPerTable; } const int tablesCount = (columnsPerTable != 0) ? cols/columnsPerTable : 0; const int remainingColumns = (columnsPerTable != 0) ? cols % columnsPerTable : cols; bool columnsSeparating = (cols > columnsPerTable); QTextStream out(&file); QProcess tex; tex.start("latex", QStringList() << "--version", QProcess::ReadOnly); tex.waitForFinished(500); QString texVersionOutput = QString(tex.readAllStandardOutput()); texVersionOutput = texVersionOutput.split('\n')[0]; int yearidx = -1; for (int i = texVersionOutput.size() - 1; i >= 0; --i) { if (texVersionOutput.at(i) == QChar('2')) { yearidx = i; break; } } if (texVersionOutput.at(yearidx+1) == QChar('/')) yearidx-=3; bool ok; texVersionOutput.mid(yearidx, 4).toInt(&ok); int version = -1; if (ok) version = texVersionOutput.mid(yearidx, 4).toInt(&ok); if (latexHeaders) { out << QLatin1String("\\documentclass[11pt,a4paper]{article} \n"); out << QLatin1String("\\usepackage{geometry} \n"); out << QLatin1String("\\usepackage{xcolor,colortbl} \n"); if (version >= 2015) out << QLatin1String("\\extrafloats{1280} \n"); out << QLatin1String("\\definecolor{HeaderBgColor}{rgb}{0.81,0.81,0.81} \n"); out << QLatin1String("\\geometry{ \n"); out << QLatin1String("a4paper, \n"); out << QLatin1String("total={170mm,257mm}, \n"); out << QLatin1String("left=10mm, \n"); out << QLatin1String("top=10mm } \n"); out << QLatin1String("\\begin{document} \n"); out << QLatin1String("\\title{LabPlot Spreadsheet Export to \\LaTeX{} } \n"); out << QLatin1String("\\author{LabPlot} \n"); out << QLatin1String("\\date{\\today} \n"); } QString endTabularTable ("\\end{tabular} \n \\end{table} \n"); QString tableCaption ("\\caption{"+ m_spreadsheet->name()+ "} \n"); QString beginTable ("\\begin{table}[ht] \n"); int rowCount = 0; const int maxRows = 45; bool captionRemoved = false; if (columnsSeparating) { QVector emptyRowIndices; for (int table = 0; table < tablesCount; ++table) { QStringList textable; captionRemoved = false; textable << beginTable; if (captions) textable << tableCaption; textable << QLatin1String("\\centering \n"); textable << QLatin1String("\\begin{tabular}{") << (gridLines ?QLatin1String("|") : QLatin1String("")); for (int i = 0; i < columnsPerTable; ++i) textable << ( gridLines ? QLatin1String(" c |") : QLatin1String(" c ") ); textable << QLatin1String("} \n"); if (gridLines) textable << QLatin1String("\\hline \n"); if (exportHeaders) { if (latexHeaders) textable << QLatin1String("\\rowcolor{HeaderBgColor} \n"); for (int col = table*columnsPerTable; col < (table * columnsPerTable) + columnsPerTable; ++col) { textable << toExport.at(col)->name(); if (col != ((table * columnsPerTable)+ columnsPerTable)-1) textable << QLatin1String(" & "); } textable << QLatin1String("\\\\ \n"); if (gridLines) textable << QLatin1String("\\hline \n"); } foreach(const QString& s, textable) out << s; QStringList values; for (int row = 0; row < totalRowCount; ++row) { values.clear(); bool notEmpty = false; for (int col = table*columnsPerTable; col < (table * columnsPerTable) + columnsPerTable; ++col ) { if (toExport.at(col)->isValid(row)) { notEmpty = true; values << toExport.at(col)->asStringColumn()->textAt(row); } else values << QLatin1String("-"); if (col != ((table * columnsPerTable)+ columnsPerTable)-1) values << QLatin1String(" & "); } if (!notEmpty && skipEmptyRows) { if (!emptyRowIndices.contains(row)) emptyRowIndices << row; } if (emptyRowIndices.contains(row) && notEmpty) emptyRowIndices.remove(emptyRowIndices.indexOf(row)); if (notEmpty || !skipEmptyRows) { foreach(const QString& s, values) out << s; out << QLatin1String("\\\\ \n"); if (gridLines) out << QLatin1String("\\hline \n"); rowCount++; if (rowCount == maxRows) { out << endTabularTable; out << QLatin1String("\\newpage \n"); if (captions) if (!captionRemoved) textable.removeAt(1); foreach(const QString& s, textable) out << s; rowCount = 0; if (!captionRemoved) captionRemoved = true; } } } out << endTabularTable; } //new table for the remaining columns QStringList remainingTable; remainingTable << beginTable; if (captions) remainingTable << tableCaption; remainingTable << QLatin1String("\\centering \n"); remainingTable << QLatin1String("\\begin{tabular}{") << (gridLines ? QLatin1String("|"):QLatin1String("")); for (int c = 0; c < remainingColumns; ++c) remainingTable << ( gridLines ? QLatin1String(" c |") : QLatin1String(" c ") ); remainingTable << QLatin1String("} \n"); if (gridLines) remainingTable << QLatin1String("\\hline \n"); if (exportHeaders) { if (latexHeaders) remainingTable << QLatin1String("\\rowcolor{HeaderBgColor} \n"); for (int col = 0; col < remainingColumns; ++col) { remainingTable << toExport.at(col + (tablesCount * columnsPerTable))->name(); if (col != remainingColumns-1) remainingTable << QLatin1String(" & "); } remainingTable << QLatin1String("\\\\ \n"); if (gridLines) remainingTable << QLatin1String("\\hline \n"); } foreach (const QString& s, remainingTable) out << s; QStringList values; captionRemoved = false; for (int row = 0; row < totalRowCount; ++row) { values.clear(); bool notEmpty = false; for (int col = 0; col < remainingColumns; ++col ) { if (toExport.at(col + (tablesCount * columnsPerTable))->isValid(row)) { notEmpty = true; values << toExport.at(col + (tablesCount * columnsPerTable))->asStringColumn()->textAt(row); } else values << QLatin1String("-"); if (col != remainingColumns-1) values << QLatin1String(" & "); } if (!emptyRowIndices.contains(row) && !notEmpty) notEmpty = true; if (notEmpty || !skipEmptyRows) { foreach (const QString& s, values) out << s; out << QLatin1String("\\\\ \n"); if (gridLines) out << QLatin1String("\\hline \n"); rowCount++; if (rowCount == maxRows) { out << endTabularTable; out << QLatin1String("\\pagebreak[4] \n"); if (captions) if (!captionRemoved) remainingTable.removeAt(1); foreach(const QString& s, remainingTable) out << s; rowCount = 0; if (!captionRemoved) captionRemoved = true; } } } out << endTabularTable; } else { QStringList textable; textable << beginTable; if (captions) textable << tableCaption; textable << QLatin1String("\\centering \n"); textable << QLatin1String("\\begin{tabular}{") << (gridLines ? QLatin1String("|"):QLatin1String("")); for (int c = 0; c < cols; ++c) textable << ( gridLines ? QLatin1String(" c |") : QLatin1String(" c ") ); textable << QLatin1String("} \n"); if (gridLines) textable << QLatin1String("\\hline \n"); if (exportHeaders) { if (latexHeaders) textable << QLatin1String("\\rowcolor{HeaderBgColor} \n"); for (int col = 0; col < cols; ++col) { textable << toExport.at(col)->name(); if (col != cols-1) textable << QLatin1String(" & "); } textable << QLatin1String("\\\\ \n"); if (gridLines) textable << QLatin1String("\\hline \n"); } foreach (const QString& s, textable) out << s; QStringList values; captionRemoved = false; for (int row = 0; row < totalRowCount; ++row) { values.clear(); bool notEmpty = false; for (int col = 0; col < cols; ++col ) { if (toExport.at(col)->isValid(row)) { notEmpty = true; values << toExport.at(col)->asStringColumn()->textAt(row); } else values << "-"; if (col != cols-1) values << " & "; } if (notEmpty || !skipEmptyRows) { foreach (const QString& s, values) out << s; out << QLatin1String("\\\\ \n"); if (gridLines) out << QLatin1String("\\hline \n"); rowCount++; if (rowCount == maxRows) { out << endTabularTable; out << QLatin1String("\\newpage \n"); if (captions) if (!captionRemoved) textable.removeAt(1); foreach (const QString& s, textable) out << s; rowCount = 0; if (!captionRemoved) captionRemoved = true; } } } out << endTabularTable; } if (latexHeaders) out << QLatin1String("\\end{document} \n"); if (!exportEntire) { qDeleteAll(toExport); toExport.clear(); } else toExport.clear(); } void SpreadsheetView::exportToFits(const QString &fileName, const int exportTo, const bool commentsAsUnits) const { FITSFilter* filter = new FITSFilter; filter->setExportTo(exportTo); filter->setCommentsAsUnits(commentsAsUnits); filter->write(fileName, m_spreadsheet); delete filter; } diff --git a/src/commonfrontend/spreadsheet/SpreadsheetView.h b/src/commonfrontend/spreadsheet/SpreadsheetView.h index 6f672bd49..4c23b1e25 100644 --- a/src/commonfrontend/spreadsheet/SpreadsheetView.h +++ b/src/commonfrontend/spreadsheet/SpreadsheetView.h @@ -1,249 +1,249 @@ /*************************************************************************** File : SpreadsheetView.h Project : LabPlot Description : View class for Spreadsheet -------------------------------------------------------------------- Copyright : (C) 2010-2015 by Alexander Semke (alexander.semke@web.de) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #ifndef SPREADSHEETVIEW_H #define SPREADSHEETVIEW_H #include #include "backend/core/AbstractColumn.h" #include "backend/lib/IntervalAttribute.h" class Column; class Spreadsheet; class SpreadsheetModel; class SpreadsheetItemDelegate; class SpreadsheetHeaderView; class AbstractAspect; class QTableView; class QPrinter; class QMenu; class QToolBar; class QModelIndex; class QItemSelection; class SpreadsheetView : public QWidget { Q_OBJECT - public: - explicit SpreadsheetView(Spreadsheet* spreadsheet); - virtual ~SpreadsheetView(); - - void resizeHeader(); - - void showComments(bool on = true); - bool areCommentsShown() const; - - int selectedColumnCount(bool full = false); - int selectedColumnCount(AbstractColumn::PlotDesignation); - bool isColumnSelected(int col, bool full = false); - QList selectedColumns(bool full = false); - int firstSelectedColumn(bool full = false); - int lastSelectedColumn(bool full = false); - - bool isRowSelected(int row, bool full = false); - int firstSelectedRow(bool full = false); - int lastSelectedRow(bool full = false); - IntervalAttribute selectedRows(bool full = false); - - bool isCellSelected(int row, int col); - void setCellSelected(int row, int col, bool select = true); - void setCellsSelected(int first_row, int first_col, int last_row, int last_col, bool select = true); - void getCurrentCell(int* row, int* col); - - void exportToFile(const QString&, const bool, const QString&) const; - void exportToLaTeX(const QString&, const bool exportHeaders, - const bool gridLines, const bool captions, const bool latexHeaders, - const bool skipEmptyRows,const bool exportEntire) const; - void exportToFits(const QString &fileName, const int exportTo, const bool commentsAsUnits) const; - - private: - void init(); - void initActions(); - void initMenus(); - void connectActions(); - - QTableView* m_tableView; - Spreadsheet* m_spreadsheet; - SpreadsheetItemDelegate* m_delegate; - SpreadsheetModel* m_model; - SpreadsheetHeaderView* m_horizontalHeader; - bool m_suppressSelectionChangedEvent; - - bool eventFilter(QObject*, QEvent*); - void keyPressEvent(QKeyEvent*); - - //selection related actions - QAction* action_cut_selection; - QAction* action_copy_selection; - QAction* action_paste_into_selection; - QAction* action_mask_selection; - QAction* action_unmask_selection; - QAction* action_clear_selection; +public: + explicit SpreadsheetView(Spreadsheet* spreadsheet, bool readOnly = false); + virtual ~SpreadsheetView(); + + void resizeHeader(); + + void showComments(bool on = true); + bool areCommentsShown() const; + + int selectedColumnCount(bool full = false); + int selectedColumnCount(AbstractColumn::PlotDesignation); + bool isColumnSelected(int col, bool full = false); + QList selectedColumns(bool full = false); + int firstSelectedColumn(bool full = false); + int lastSelectedColumn(bool full = false); + + bool isRowSelected(int row, bool full = false); + int firstSelectedRow(bool full = false); + int lastSelectedRow(bool full = false); + IntervalAttribute selectedRows(bool full = false); + + bool isCellSelected(int row, int col); + void setCellSelected(int row, int col, bool select = true); + void setCellsSelected(int first_row, int first_col, int last_row, int last_col, bool select = true); + void getCurrentCell(int* row, int* col); + + void exportToFile(const QString&, const bool, const QString&) const; + void exportToLaTeX(const QString&, const bool exportHeaders, + const bool gridLines, const bool captions, const bool latexHeaders, + const bool skipEmptyRows,const bool exportEntire) const; + void exportToFits(const QString &fileName, const int exportTo, const bool commentsAsUnits) const; + +private: + void init(); + void initActions(); + void initMenus(); + void connectActions(); + + QTableView* m_tableView; + Spreadsheet* m_spreadsheet; + SpreadsheetItemDelegate* m_delegate; + SpreadsheetModel* m_model; + SpreadsheetHeaderView* m_horizontalHeader; + bool m_suppressSelectionChangedEvent; + bool m_readOnly; + bool eventFilter(QObject*, QEvent*); + void keyPressEvent(QKeyEvent*); + + //selection related actions + QAction* action_cut_selection; + QAction* action_copy_selection; + QAction* action_paste_into_selection; + QAction* action_mask_selection; + QAction* action_unmask_selection; + QAction* action_clear_selection; // QAction* action_set_formula; // QAction* action_recalculate; - QAction* action_fill_row_numbers; - QAction* action_fill_sel_row_numbers; - QAction* action_fill_random; - QAction* action_fill_equidistant; - QAction* action_fill_random_nonuniform; - QAction* action_fill_const; - QAction* action_fill_function; - - //spreadsheet related actions - QAction* action_toggle_comments; - QAction* action_select_all; - QAction* action_add_column; - QAction* action_clear_spreadsheet; - QAction* action_clear_masks; - QAction* action_sort_spreadsheet; - QAction* action_go_to_cell; - QAction* action_statistics_all_columns; - - //column related actions - QAction* action_insert_columns; - QAction* action_remove_columns; - QAction* action_clear_columns; - QAction* action_add_columns; - QAction* action_set_as_none; - QAction* action_set_as_x; - QAction* action_set_as_y; - QAction* action_set_as_z; - QAction* action_set_as_xerr; - QAction* action_set_as_xerr_plus; - QAction* action_set_as_xerr_minus; - QAction* action_set_as_yerr; - QAction* action_set_as_yerr_plus; - QAction* action_set_as_yerr_minus; - QAction* action_reverse_columns; - QAction* action_drop_values; - QAction* action_mask_values; - QAction* action_join_columns; - QAction* action_normalize_columns; - QAction* action_normalize_selection; - QAction* action_sort_columns; - QAction* action_sort_asc_column; - QAction* action_sort_desc_column; - QAction* action_statistics_columns; - - //row related actions - QAction* action_insert_rows; - QAction* action_remove_rows; - QAction* action_clear_rows; - QAction* action_add_rows; - QAction* action_statistics_rows; - - //plotting and analysis related actions - QAction* action_plot_data; - - //Menus - QMenu* m_selectionMenu; - QMenu* m_columnMenu; - QMenu* m_rowMenu; - QMenu* m_spreadsheetMenu; - - public slots: - void createContextMenu(QMenu*) const; - void fillToolBar(QToolBar*); - void print(QPrinter*) const; - - private slots: - void createColumnContextMenu(QMenu*); - void goToCell(int row, int col); - void toggleComments(); - void goToNextColumn(); - void goToPreviousColumn(); - void goToCell(); - void sortSpreadsheet(); - void sortDialog(QList); - - void cutSelection(); - void copySelection(); - void pasteIntoSelection(); - void clearSelectedCells(); - void maskSelection(); - void unmaskSelection(); + QAction* action_fill_row_numbers; + QAction* action_fill_sel_row_numbers; + QAction* action_fill_random; + QAction* action_fill_equidistant; + QAction* action_fill_random_nonuniform; + QAction* action_fill_const; + QAction* action_fill_function; + + //spreadsheet related actions + QAction* action_toggle_comments; + QAction* action_select_all; + QAction* action_add_column; + QAction* action_clear_spreadsheet; + QAction* action_clear_masks; + QAction* action_sort_spreadsheet; + QAction* action_go_to_cell; + QAction* action_statistics_all_columns; + + //column related actions + QAction* action_insert_columns; + QAction* action_remove_columns; + QAction* action_clear_columns; + QAction* action_add_columns; + QAction* action_set_as_none; + QAction* action_set_as_x; + QAction* action_set_as_y; + QAction* action_set_as_z; + QAction* action_set_as_xerr; + QAction* action_set_as_xerr_plus; + QAction* action_set_as_xerr_minus; + QAction* action_set_as_yerr; + QAction* action_set_as_yerr_plus; + QAction* action_set_as_yerr_minus; + QAction* action_reverse_columns; + QAction* action_drop_values; + QAction* action_mask_values; + QAction* action_join_columns; + QAction* action_normalize_columns; + QAction* action_normalize_selection; + QAction* action_sort_columns; + QAction* action_sort_asc_column; + QAction* action_sort_desc_column; + QAction* action_statistics_columns; + + //row related actions + QAction* action_insert_rows; + QAction* action_remove_rows; + QAction* action_clear_rows; + QAction* action_add_rows; + QAction* action_statistics_rows; + + //plotting and analysis related actions + QAction* action_plot_data; + + //Menus + QMenu* m_selectionMenu; + QMenu* m_columnMenu; + QMenu* m_rowMenu; + QMenu* m_spreadsheetMenu; + +public slots: + void createContextMenu(QMenu*) const; + void fillToolBar(QToolBar*); + void print(QPrinter*) const; + +private slots: + void createColumnContextMenu(QMenu*); + void goToCell(int row, int col); + void toggleComments(); + void goToNextColumn(); + void goToPreviousColumn(); + void goToCell(); + void sortSpreadsheet(); + void sortDialog(QList); + + void cutSelection(); + void copySelection(); + void pasteIntoSelection(); + void clearSelectedCells(); + void maskSelection(); + void unmaskSelection(); // void recalculateSelectedCells(); - void plotData(); - - void fillSelectedCellsWithRowNumbers(); - void fillWithRowNumbers(); - void fillSelectedCellsWithRandomNumbers(); - void fillWithRandomValues(); - void fillWithEquidistantValues(); - void fillWithFunctionValues(); - void fillSelectedCellsWithConstValues(); - - void addRows(); - void insertEmptyRows(); - void removeSelectedRows(); - void clearSelectedRows(); - - void addColumns(); - void insertEmptyColumns(); - void removeSelectedColumns(); - void clearSelectedColumns(); - - void reverseColumns(); - void dropColumnValues(); - void maskColumnValues(); - void joinColumns(); - void normalizeSelectedColumns(); - void normalizeSelection(); - void sortSelectedColumns(); - void sortColumnAscending(); - void sortColumnDescending(); - - void setSelectionAs(); - - void activateFormulaMode(bool on); - bool formulaModeActive() const; - - void showColumnStatistics(bool forAll = false); - void showAllColumnsStatistics(); - void showRowStatistics(); - - void advanceCell(); - void handleHorizontalSectionResized(int logicalIndex, int oldSize, int newSize); - void handleHorizontalSectionMoved(int index, int from, int to); - void handleHorizontalHeaderDoubleClicked(int index); - void handleHeaderDataChanged(Qt::Orientation orientation, int first, int last); - void currentColumnChanged(const QModelIndex& current, const QModelIndex & previous); - void handleAspectAdded(const AbstractAspect*); - void handleAspectAboutToBeRemoved(const AbstractAspect*); - void updateHeaderGeometry(Qt::Orientation o, int first, int last); - - void selectColumn(int); - void deselectColumn(int); - void columnClicked(int); - void selectionChanged(const QItemSelection& selected, const QItemSelection& deselected); + void plotData(); + + void fillSelectedCellsWithRowNumbers(); + void fillWithRowNumbers(); + void fillSelectedCellsWithRandomNumbers(); + void fillWithRandomValues(); + void fillWithEquidistantValues(); + void fillWithFunctionValues(); + void fillSelectedCellsWithConstValues(); + + void addRows(); + void insertEmptyRows(); + void removeSelectedRows(); + void clearSelectedRows(); + + void addColumns(); + void insertEmptyColumns(); + void removeSelectedColumns(); + void clearSelectedColumns(); + + void reverseColumns(); + void dropColumnValues(); + void maskColumnValues(); + void joinColumns(); + void normalizeSelectedColumns(); + void normalizeSelection(); + void sortSelectedColumns(); + void sortColumnAscending(); + void sortColumnDescending(); + + void setSelectionAs(); + + void activateFormulaMode(bool on); + bool formulaModeActive() const; + + void showColumnStatistics(bool forAll = false); + void showAllColumnsStatistics(); + void showRowStatistics(); + + void advanceCell(); + void handleHorizontalSectionResized(int logicalIndex, int oldSize, int newSize); + void handleHorizontalSectionMoved(int index, int from, int to); + void handleHorizontalHeaderDoubleClicked(int index); + void handleHeaderDataChanged(Qt::Orientation orientation, int first, int last); + void currentColumnChanged(const QModelIndex& current, const QModelIndex & previous); + void handleAspectAdded(const AbstractAspect*); + void handleAspectAboutToBeRemoved(const AbstractAspect*); + void updateHeaderGeometry(Qt::Orientation o, int first, int last); + + void selectColumn(int); + void deselectColumn(int); + void columnClicked(int); + void selectionChanged(const QItemSelection& selected, const QItemSelection& deselected); }; #endif