diff --git a/CMakeLists.txt.user b/CMakeLists.txt.user index 5ed135144..7a7f04525 100644 --- a/CMakeLists.txt.user +++ b/CMakeLists.txt.user @@ -1,372 +1,377 @@ - + EnvironmentId - {eb09dc4b-4a9c-4f0d-bccf-3569e1217628} + {211905eb-02af-4ace-b764-b4a63e537818} ProjectExplorer.Project.ActiveTarget 0 ProjectExplorer.Project.EditorSettings true false true Cpp CppGlobal QmlJS QmlJSGlobal 2 UTF-8 false 4 false 80 true true 1 true false 0 true true 0 8 true 1 true true true false ProjectExplorer.Project.PluginSettings ProjectExplorer.Project.Target.0 - Desktop - Desktop - {4a9fbf76-e360-439f-8368-fb7d02fba2a2} - 0 + Desktop Qt 5.7.0 GCC 64bit + Desktop Qt 5.7.0 GCC 64bit + qt.57.gcc_64_kit + 1 0 0 - - /home/alex/Projekte/build-labplot_sql-Desktop-Default + + CMAKE_CXX_COMPILER:STRING=%{Compiler:Executable} + QT_QMAKE_EXECUTABLE:FILEPATH=/usr/bin/qmake-qt4 + + /home/krajsz/Programming/GSoC/labplotnew/build-labplot-Desktop_Qt_5_7_0_GCC_64bit-Default all true Make CMakeProjectManager.MakeStep 1 Build ProjectExplorer.BuildSteps.Build clean true Make CMakeProjectManager.MakeStep 1 Clean ProjectExplorer.BuildSteps.Clean 2 false Default Default CMakeProjectManager.CMakeBuildConfiguration CMAKE_BUILD_TYPE:STRING=Debug + CMAKE_CXX_COMPILER:STRING=%{Compiler:Executable} + QT_QMAKE_EXECUTABLE:FILEPATH=/usr/bin/qmake-qt4 - /home/alex/Projekte/build-labplot_sql-Desktop-Debug + /home/krajsz/Programming/GSoC/labplotnew/build-labplot-Desktop_Qt_5_7_0_GCC_64bit-Debug - + all true Make CMakeProjectManager.MakeStep 1 Build ProjectExplorer.BuildSteps.Build clean true Make CMakeProjectManager.MakeStep 1 Clean ProjectExplorer.BuildSteps.Clean 2 false Debug Debug CMakeProjectManager.CMakeBuildConfiguration CMAKE_BUILD_TYPE:STRING=Release - /home/alex/Projekte/build-labplot_sql-Desktop-Release + /home/krajsz/Programming/GSoC/labplotnew/build-labplot-Desktop_Qt_5_7_0_GCC_64bit-Release true Make CMakeProjectManager.MakeStep 1 Build ProjectExplorer.BuildSteps.Build clean true Make CMakeProjectManager.MakeStep 1 Clean ProjectExplorer.BuildSteps.Clean 2 false Release Release CMakeProjectManager.CMakeBuildConfiguration CMAKE_BUILD_TYPE:STRING=RelWithDebInfo - /home/alex/Projekte/build-labplot_sql-Desktop-Release with Debug Information + /home/krajsz/Programming/GSoC/labplotnew/build-labplot-Desktop_Qt_5_7_0_GCC_64bit-Release with Debug Information true Make CMakeProjectManager.MakeStep 1 Build ProjectExplorer.BuildSteps.Build clean true Make CMakeProjectManager.MakeStep 1 Clean ProjectExplorer.BuildSteps.Clean 2 false Release with Debug Information Release with Debug Information CMakeProjectManager.CMakeBuildConfiguration CMAKE_BUILD_TYPE:STRING=MinSizeRel - /home/alex/Projekte/build-labplot_sql-Desktop-Minimum Size Release + /home/krajsz/Programming/GSoC/labplotnew/build-labplot-Desktop_Qt_5_7_0_GCC_64bit-Minimum Size Release true Make CMakeProjectManager.MakeStep 1 Build ProjectExplorer.BuildSteps.Build clean true Make CMakeProjectManager.MakeStep 1 Clean ProjectExplorer.BuildSteps.Clean 2 false Minimum Size Release Minimum Size Release CMakeProjectManager.CMakeBuildConfiguration 5 0 Deploy ProjectExplorer.BuildSteps.Deploy 1 Deploy locally ProjectExplorer.DefaultDeployConfiguration 1 false false 1000 true false false false false true 0.01 10 true 1 25 1 true false true valgrind 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 labplot2 - /home/alex/Projekte/build-labplot_sql-Desktop-Default/src + /home/krajsz/Programming/GSoC/labplotnew/build-labplot-Desktop_Qt_5_7_0_GCC_64bit-Debug/src 2 labplot2 CMakeProjectManager.CMakeRunConfiguration.labplot2 3768 false true false false true 1 ProjectExplorer.Project.TargetCount 1 ProjectExplorer.Project.Updater.FileVersion 18 Version 18 diff --git a/src/backend/datasources/filters/FITSFilter.cpp b/src/backend/datasources/filters/FITSFilter.cpp index 8c3fb6294..4d676afe0 100644 --- a/src/backend/datasources/filters/FITSFilter.cpp +++ b/src/backend/datasources/filters/FITSFilter.cpp @@ -1,1638 +1,1638 @@ /*************************************************************************** File : FITSFilter.cpp Project : LabPlot Description : FITS I/O-filter -------------------------------------------------------------------- Copyright : (C) 2016 by Fabian Kristof (fkristofszabolcs@gmail.com) Copyright : (C) 2017 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 "FITSFilter.h" #include "FITSFilterPrivate.h" #include "backend/datasources/FileDataSource.h" #include "backend/core/column/Column.h" #include "backend/core/column/ColumnStringIO.h" #include "backend/core/datatypes/Double2StringFilter.h" #include "commonfrontend/matrix/MatrixView.h" #include "backend/matrix/MatrixModel.h" #include #include #include /*! \class FITSFilter * \brief Manages the import/export of data from/to a FITS file. * \since 2.2.0 * \ingroup datasources */ FITSFilter::FITSFilter():AbstractFileFilter(), d(new FITSFilterPrivate(this)) { } FITSFilter::~FITSFilter() { delete d; } QVector FITSFilter::readDataFromFile(const QString &fileName, AbstractDataSource *dataSource, AbstractFileFilter::ImportMode importMode, int lines) { Q_UNUSED(lines); return d->readCHDU(fileName, dataSource, importMode); } QVector FITSFilter::readChdu(const QString &fileName, bool* okToMatrix, int lines) { return d->readCHDU(fileName, NULL, AbstractFileFilter::Replace, okToMatrix, lines); } void FITSFilter::write(const QString &fileName, AbstractDataSource *dataSource) { d->writeCHDU(fileName, dataSource); } void FITSFilter::addNewKeyword(const QString &filename, const QList &keywords) { d->addNewKeyword(filename, keywords); } void FITSFilter::updateKeywords(const QString &fileName, const QList& originals, const QVector& updates) { d->updateKeywords(fileName, originals, updates); } void FITSFilter::deleteKeyword(const QString &fileName, const QList& keywords) { d->deleteKeyword(fileName, keywords); } void FITSFilter::addKeywordUnit(const QString &fileName, const QList &keywords) { d->addKeywordUnit(fileName, keywords); } void FITSFilter::removeExtensions(const QStringList &extensions) { d->removeExtensions(extensions); } void FITSFilter::parseHeader(const QString &fileName, QTableWidget *headerEditTable, bool readKeys, const QList &keys) { d->parseHeader(fileName, headerEditTable, readKeys, keys); } void FITSFilter::parseExtensions(const QString &fileName, QTreeWidget *tw, bool checkPrimary) { d->parseExtensions(fileName, tw, checkPrimary); } QList FITSFilter::chduKeywords(const QString &fileName) { return d->chduKeywords(fileName); } void FITSFilter::loadFilterSettings(const QString& fileName) { Q_UNUSED(fileName) } void FITSFilter::saveFilterSettings(const QString& fileName) const { Q_UNUSED(fileName) } /*! * \brief contains the {StandardKeywords \ MandatoryKeywords} keywords * \return A list of keywords */ QStringList FITSFilter::standardKeywords() { return QStringList() << QLatin1String("(blank)") << QLatin1String("CROTA") << QLatin1String("EQUINOX") << QLatin1String("NAXIS") << QLatin1String("TBCOL") << QLatin1String("TUNIT") << QLatin1String("AUTHOR") << QLatin1String("CRPIX") << QLatin1String("EXTEND") << QLatin1String("OBJECT") << QLatin1String("TDIM") << QLatin1String("TZERO") << QLatin1String("BITPIX") << QLatin1String("CRVAL") << QLatin1String("EXTLEVEL") << QLatin1String("OBSERVER") << QLatin1String("TDISP") << QLatin1String("XTENSION") << QLatin1String("BLANK") << QLatin1String("CTYPE") << QLatin1String("EXTNAME") << QLatin1String("ORIGIN") << QLatin1String("TELESCOP") << QLatin1String("BLOCKED") << QLatin1String("DATAMAX") << QLatin1String("EXTVER") << QLatin1String("BSCALE") << QLatin1String("DATAMIN") << QLatin1String("PSCAL") << QLatin1String("TFORM") << QLatin1String("BUNIT") << QLatin1String("DATE") << QLatin1String("GROUPS") << QLatin1String("PTYPE") << QLatin1String("THEAP") << QLatin1String("BZERO") << QLatin1String("DATE-OBS") << QLatin1String("HISTORY") << QLatin1String("PZERO") << QLatin1String("TNULL") << QLatin1String("CDELT") << QLatin1String("INSTRUME") << QLatin1String("REFERENC") << QLatin1String("TSCAL") << QLatin1String("COMMENT") << QLatin1String("EPOCH") << QLatin1String("NAXIS") << QLatin1String("SIMPLE") << QLatin1String("TTYPE"); } /*! * \brief Returns a list of keywords, that are mandatory for an image extension of a FITS file * see: * https://archive.stsci.edu/fits/fits_standard/node64.html * \return A list of keywords */ QStringList FITSFilter::mandatoryImageExtensionKeywords() { return QStringList() << QLatin1String("XTENSION") << QLatin1String("BITPIX") << QLatin1String("NAXIS") << QLatin1String("PCOUNT") << QLatin1String("GCOUNT") << QLatin1String("END"); } /*! * \brief Returns a list of keywords, that are mandatory for a table extension (ascii or bintable) * of a FITS file * see: * https://archive.stsci.edu/fits/fits_standard/node58.html * https://archive.stsci.edu/fits/fits_standard/node68.html * \return A list of keywords */ QStringList FITSFilter::mandatoryTableExtensionKeywords() { return QStringList() << QLatin1String("XTENSION") << QLatin1String("BITPIX") << QLatin1String("NAXIS") << QLatin1String("NAXIS1") << QLatin1String("NAXIS2") << QLatin1String("PCOUNT") << QLatin1String("GCOUNT") << QLatin1String("TFIELDS") << QLatin1String("END"); } /*! * \brief Returns a list of strings that represent units which are used for autocompletion when adding * keyword units to keywords * \return A list of strings that represent units */ QStringList FITSFilter::units() { return QStringList() << QLatin1String("m (Metre)") << QLatin1String("kg (Kilogram)") << QLatin1String("s (Second)") << QString("M☉ (Solar mass)") << QLatin1String("AU (Astronomical unit") << QLatin1String("l.y (Light year)") << QLatin1String("km (Kilometres") << QLatin1String("pc (Parsec)") << QLatin1String("K (Kelvin)") << QLatin1String("mol (Mole)") << QLatin1String("cd (Candela)"); } /*! * \brief Sets the startColumn to \a column * \param column the column to be set */ void FITSFilter::setStartColumn(const int column) { d->startColumn = column; } /*! * \brief Returns startColumn * \return The startColumn */ int FITSFilter::startColumn() const { return d->startColumn; } /*! * \brief Sets the endColumn to \a column * \param column the column to be set */ void FITSFilter::setEndColumn(const int column) { d->endColumn = column; } /*! * \brief Returns endColumn * \return The endColumn */ int FITSFilter::endColumn() const { return d->endColumn; } /*! * \brief Sets the startRow to \a row * \param row the row to be set */ void FITSFilter::setStartRow(const int row) { d->startRow = row; } /*! * \brief Returns startRow * \return The startRow */ int FITSFilter::startRow() const { return d->startRow; } /*! * \brief Sets the endRow to \a row * \param row the row to be set */ void FITSFilter::setEndRow(const int row) { d->endRow = row; } /*! * \brief Returns endRow * \return The endRow */ int FITSFilter::endRow() const { return d->endRow; } /*! * \brief Sets commentsAsUnits to \a commentsAsUnits * * This is used when spreadsheets are exported to FITS table extensions and comments are used as the * units of the table's columns. * \param commentsAsUnits */ void FITSFilter::setCommentsAsUnits(const bool commentsAsUnits) { d->commentsAsUnits = commentsAsUnits; } /*! * \brief Sets exportTo to \a exportTo * * This is used to decide whether the container should be exported to a FITS image or a FITS table * For an image \a exportTo should be 0, for a table 1 * \param exportTo */ void FITSFilter::setExportTo(const int exportTo) { d->exportTo = exportTo; } int FITSFilter::imagesCount(const QString &fileName) { return d->imagesCount(fileName); } int FITSFilter::tablesCount(const QString &fileName) { return d->tablesCount(fileName); } //##################################################################### //################### Private implementation ########################## //##################################################################### FITSFilterPrivate::FITSFilterPrivate(FITSFilter* owner) : q(owner), startRow(-1), endRow(-1), startColumn(-1), endColumn(-1), commentsAsUnits(false), exportTo(0) { #ifdef HAVE_FITS m_fitsFile = 0; #endif } /*! * \brief Read the current header data unit from file \a filename in data source \a dataSource in \a importMode import mode * \param fileName the name of the file to be read * \param dataSource the data source to be filled * \param importMode */ QVector FITSFilterPrivate::readCHDU(const QString& fileName, AbstractDataSource* dataSource, AbstractFileFilter::ImportMode importMode, bool* okToMatrix, int lines) { QVector dataStrings; #ifdef HAVE_FITS int status = 0; if(fits_open_file(&m_fitsFile, fileName.toLatin1(), READONLY, &status)) { qDebug() << fileName; printError(status); return dataStrings; } int chduType; if (fits_get_hdu_type(m_fitsFile, &chduType, &status)) { printError(status); return dataStrings; } long actualRows; int actualCols; int columnOffset = 0; bool noDataSource = (dataSource == NULL); if(chduType == IMAGE_HDU) { int bitpix; int naxis; int maxdim = 2; long naxes[2]; long pixelCount; double* data; if (fits_get_img_param(m_fitsFile, maxdim,&bitpix, &naxis, naxes, &status)) { printError(status); return dataStrings; } if (naxis == 0) return dataStrings; actualRows = naxes[1]; actualCols = naxes[0]; if (lines == -1) lines = actualRows; else { if (lines > actualRows) lines = actualRows; } pixelCount = lines * actualCols; data = new double[pixelCount]; if (!data) { qDebug() << i18n("Not enough memory for data"); return dataStrings; } if (fits_read_img(m_fitsFile, TDOUBLE, 1, pixelCount, NULL, data, NULL, &status)) { printError(status); return dataStrings << (QStringList() << QString("Error")); } QVector dataPointers; if (endRow != -1) { if (!noDataSource) lines = endRow; } if (endColumn != -1) actualCols = endColumn; if (noDataSource) dataStrings.reserve(lines); int i = 0; int j = 0; if (startRow != 1) i = startRow; if (startColumn != 1) j = startColumn; const int jstart = j; if (!noDataSource) { dataPointers.reserve(actualCols - j); columnOffset = dataSource->prepareImport(dataPointers, importMode, lines - i, actualCols - j); } int ii = 0; for (; i < lines; ++i) { int jj = 0; QStringList line; line.reserve(actualCols); for (; j < actualCols; ++j) { if (noDataSource) line << QString::number(data[i*naxes[0] +j]); else ((double*)dataPointers[jj++])[ii] = data[i* naxes[0] + j]; } dataStrings << line; j = jstart; ii++; } delete[] data; Spreadsheet* spreadsheet = dynamic_cast(dataSource); if (spreadsheet) { const QString& comment = i18np("numerical data, %1 element", "numerical data, %1 elements", actualRows); for (int n = 0; n < actualCols; n++) { Column* column = spreadsheet->column(columnOffset + n); column->setComment(comment); //TODO: column->setName(); ? column->setUndoAware(true); if (importMode == AbstractFileFilter::Replace) { column->setSuppressDataChangedSignal(false); column->setChanged(); } } } dataSource->finalizeImport(); fits_close_file(m_fitsFile, &status); return dataStrings; } else if ((chduType == ASCII_TBL) || (chduType == BINARY_TBL)) { if (endColumn != -1) actualCols = endColumn; else fits_get_num_cols(m_fitsFile, &actualCols, &status); if (endRow != -1) actualRows = endRow; else fits_get_num_rows(m_fitsFile, &actualRows, &status); QStringList columnNames; QList columnsWidth; QStringList columnUnits; columnUnits.reserve(actualCols); columnsWidth.reserve(actualCols); columnNames.reserve(actualCols); int colWidth; char keyword[FLEN_KEYWORD]; char value[FLEN_VALUE]; int col = 1; if (startColumn != 1) { if (startColumn != 0) col = startColumn; } for (; col <=actualCols; ++col) { status = 0; fits_make_keyn("TTYPE", col, keyword, &status); fits_read_key(m_fitsFile, TSTRING, keyword, value, NULL, &status); columnNames.append(QLatin1String(value)); fits_make_keyn("TUNIT", col, keyword, &status); fits_read_key(m_fitsFile, TSTRING, keyword, value, NULL, &status); columnUnits.append(QLatin1String(value)); fits_get_col_display_width(m_fitsFile, col, &colWidth, &status); columnsWidth.append(colWidth); } status = 0; if (lines == -1) lines = actualRows; else if (lines > actualRows) lines = actualRows; if (endRow != -1) lines = endRow; QVector stringDataPointers; QVector numericDataPointers; QList columnNumericTypes; int startCol = 0; if (startColumn != 1) startCol = startColumn; int startRrow = 0; if (startRow != 1) startRrow = startRow; columnNumericTypes.reserve(actualCols); int datatype; int c = 1; if (startColumn != 1) { if (startColumn != 0) c = startColumn; } QList matrixNumericColumnIndices; for (; c <= actualCols; ++c) { fits_get_coltype(m_fitsFile, c, &datatype, NULL, NULL, &status); switch (datatype) { case TSTRING: columnNumericTypes.append(false); break; case TSHORT: columnNumericTypes.append(true); break; case TLONG: columnNumericTypes.append(true); break; case TFLOAT: columnNumericTypes.append(true); break; case TDOUBLE: columnNumericTypes.append(true); break; case TLOGICAL: columnNumericTypes.append(false); break; case TBIT: columnNumericTypes.append(true); break; case TBYTE: columnNumericTypes.append(true); break; case TCOMPLEX: columnNumericTypes.append(true); break; default: columnNumericTypes.append(false); break; } if ((datatype != TSTRING) && (datatype != TLOGICAL)) matrixNumericColumnIndices.append(c); } if (noDataSource) *okToMatrix = matrixNumericColumnIndices.isEmpty() ? false : true; if (!noDataSource) { Spreadsheet* spreadsheet = dynamic_cast(dataSource); if(spreadsheet) { numericDataPointers.reserve(actualCols - startCol); stringDataPointers.reserve(actualCols - startCol); spreadsheet->setUndoAware(false); columnOffset = spreadsheet->resize(importMode, columnNames, actualCols - startCol); if (importMode == AbstractFileFilter::Replace) { spreadsheet->clear(); spreadsheet->setRowCount(lines - startRrow); } else { if (spreadsheet->rowCount() < (lines - startRrow)) spreadsheet->setRowCount(lines - startRrow); } for (int n = 0; n < actualCols - startCol; n++) { if (columnNumericTypes.at(n)) { spreadsheet->column(columnOffset+ n)->setColumnMode(AbstractColumn::Numeric); QVector* datap = static_cast* >(spreadsheet->column(columnOffset+n)->data()); numericDataPointers.push_back(datap); if (importMode == AbstractFileFilter::Replace) datap->clear(); } else { spreadsheet->column(columnOffset+ n)->setColumnMode(AbstractColumn::Text); QStringList* list = static_cast(spreadsheet->column(columnOffset+n)->data()); stringDataPointers.push_back(list); if (importMode == AbstractFileFilter::Replace) list->clear(); } } stringDataPointers.squeeze(); } else { numericDataPointers.reserve(matrixNumericColumnIndices.size()); columnOffset = dataSource->prepareImport(numericDataPointers, importMode, lines - startRrow, matrixNumericColumnIndices.size()); } numericDataPointers.squeeze(); } char* array = new char[1000]; //TODO: why 1000? int row = 1; if (startRow != 1) { if (startRow != 0) row = startRow; } int coll = 1; if (startColumn != 1) { if (startColumn != 0) coll = startColumn; } bool isMatrix = false; if (dynamic_cast(dataSource)) { isMatrix = true; coll = matrixNumericColumnIndices.first(); actualCols = matrixNumericColumnIndices.last(); if (importMode == AbstractFileFilter::Replace) { for (auto* col: numericDataPointers) static_cast*>(col)->clear(); } } for (; row <= lines; ++row) { int numericixd = 0; int stringidx = 0; QStringList line; line.reserve(actualCols-coll); for (int col = coll; col <= actualCols; ++col) { if (isMatrix) { if (!matrixNumericColumnIndices.contains(col)) continue; } if(fits_read_col_str(m_fitsFile, col, row, 1, 1, NULL, &array, NULL, &status)) printError(status); if (!noDataSource) { const QString& str = QString::fromLatin1(array); if (str.isEmpty()) { if (columnNumericTypes.at(col-1)) static_cast*>(numericDataPointers[numericixd++])->push_back(0); else stringDataPointers[stringidx++]->append(QLatin1String("NULL")); } else { if (columnNumericTypes.at(col-1)) static_cast*>(numericDataPointers[numericixd++])->push_back(str.toDouble()); else { if (!stringDataPointers.isEmpty()) stringDataPointers[stringidx++]->operator <<( str.simplified()); } } } else { QString tmpColstr = QString::fromLatin1(array); tmpColstr = tmpColstr.simplified(); if (tmpColstr.isEmpty()) line << QLatin1String("NULL"); else line << tmpColstr; } } dataStrings << line; } delete[] array; if (!noDataSource) { Spreadsheet* spreadsheet = dynamic_cast(dataSource); if (spreadsheet) { for ( int n = 0; n < actualCols - startRrow; ++n) { Column* column = spreadsheet->column(columnOffset+n); column->setComment(columnUnits.at(n)); //TODO: column->setName(); ? if (importMode == AbstractFileFilter::Replace) { column->setSuppressDataChangedSignal(false); column->setChanged(); } } } dataSource->finalizeImport(); } fits_close_file(m_fitsFile, &status); return dataStrings; } else qDebug() << i18n("Incorrect header type"); fits_close_file(m_fitsFile, &status); #else Q_UNUSED(fileName) Q_UNUSED(dataSource) Q_UNUSED(importMode) Q_UNUSED(okToMatrix) Q_UNUSED(lines) #endif return dataStrings; } /*! * \brief Export from data source \a dataSource to file \a fileName * \param fileName the name of the file to be exported to * \param dataSource the datasource whose data is exported */ void FITSFilterPrivate::writeCHDU(const QString &fileName, AbstractDataSource *dataSource) { #ifdef HAVE_FITS if (!fileName.endsWith(QLatin1String(".fits"))) return; int status = 0; bool existed = false; if (!QFile::exists(fileName)) { if (fits_create_file(&m_fitsFile, fileName.toLatin1(), &status)) { printError(status); qDebug() << fileName; return; } } else { if (fits_open_file(&m_fitsFile, fileName.toLatin1(), READWRITE, &status )) { printError(status); return; } else existed = true; } Matrix* matrix = dynamic_cast(dataSource); if (matrix) { //FITS image if (exportTo == 0) { long naxes[2] = { matrix->columnCount(), matrix->rowCount() }; if (fits_create_img(m_fitsFile, FLOAT_IMG, 2, naxes, &status)) { printError(status); status = 0; fits_close_file(m_fitsFile, &status); return; } const long nelem = naxes[0] * naxes[1]; double* array = new double[nelem]; const QVector >& data = matrix->data(); for (int row = 0; row < naxes[1]; ++row) { for (int col = 0; col < naxes[0]; ++col) array[row * naxes[0] + col] = data.at(col).at(row); } if (fits_write_img(m_fitsFile, TDOUBLE, 1, nelem, array, &status )) { printError(status); status = 0; } fits_close_file(m_fitsFile, &status); delete[] array; //FITS table } else { const int nrows = matrix->rowCount(); const int tfields = matrix->columnCount(); char* columnNames[tfields]; char* tform[tfields]; char* tunit[tfields]; const QVector >& matrixData = matrix->data(); QVector column; const MatrixModel* matrixModel = static_cast(matrix->view())->model(); const int precision = matrix->precision(); for (int i = 0; i < tfields; ++i) { column = matrixData.at(i); const QString& columnName = matrixModel->headerData(i, Qt::Horizontal).toString(); columnNames[i] = new char[columnName.size()]; strcpy(columnNames[i], columnName.toLatin1().data()); tunit[i] = new char[1]; strcpy(tunit[i], ""); int maxSize = -1; for (int row = 0; row < nrows; ++row) { if (matrix->text(row, i).size() > maxSize) maxSize = matrix->text(row, i).size(); } QString tformn; if (precision > 0) { tformn = QLatin1String("F")+ QString::number(maxSize) + QLatin1String(".") + QString::number(precision); } else tformn = QLatin1String("F")+ QString::number(maxSize) + QLatin1String(".0"); tform[i] = new char[tformn.size()]; strcpy(tform[i], tformn.toLatin1().data()); } //TODO extension name containing[] ? if (fits_create_tbl(m_fitsFile, ASCII_TBL, nrows, tfields, columnNames, tform, tunit, matrix->name().toLatin1().data(),&status )) { printError(status); for (int i = 0; i < tfields; ++i) { delete[] tform[i]; delete[] tunit[i]; delete[] columnNames[i]; } status = 0; fits_close_file(m_fitsFile, &status); if (!existed) { QFile file(fileName); file.remove(); } return; } for (int i = 0; i < tfields; ++i) { delete[] tform[i]; delete[] tunit[i]; delete[] columnNames[i]; } double* columnNumeric = new double[nrows]; for (int col = 1; col <= tfields; ++col) { column = matrixData.at(col-1); for (int r = 0; r < column.size(); ++r) columnNumeric[r] = column.at(r); fits_write_col(m_fitsFile, TDOUBLE, col, 1, 1, nrows, columnNumeric, &status); if (status) { printError(status); delete[] columnNumeric; status = 0; if (!existed) { QFile file(fileName); file.remove(); } fits_close_file(m_fitsFile, &status); return; } } delete[] columnNumeric; fits_close_file(m_fitsFile, &status); } return; } Spreadsheet* spreadsheet = dynamic_cast(dataSource); if (spreadsheet) { //FITS image if (exportTo == 0) { long naxes[2] = { spreadsheet->columnCount(), spreadsheet->rowCount() }; if (fits_create_img(m_fitsFile, FLOAT_IMG, 2, naxes, &status)) { printError(status); status = 0; fits_close_file(m_fitsFile, &status); if (!existed) { QFile file(fileName); file.remove(); } return; } const long nelem = naxes[0] * naxes[1]; double* array = new double[nelem]; for (int row = 0; row < naxes[1]; ++row) { for (int col = 0; col < naxes[0]; ++col) array[row * naxes[0] + col] = spreadsheet->column(col)->valueAt(row); } if (fits_write_img(m_fitsFile, TDOUBLE, 1, nelem, array, &status )) { printError(status); status = 0; fits_close_file(m_fitsFile, &status); if (!existed) { QFile file(fileName); file.remove(); } return; } fits_close_file(m_fitsFile, &status); delete[] array; } else { const int nrows = spreadsheet->rowCount(); const int tfields = spreadsheet->columnCount(); char* columnNames[tfields]; char* tform[tfields]; char* tunit[tfields]; for (int i = 0; i < tfields; ++i) { const Column* column = spreadsheet->column(i); columnNames[i] = new char[column->name().size()]; strcpy(columnNames[i], column->name().toLatin1().data()); if (commentsAsUnits) { tunit[i] = new char[column->comment().size()]; strcpy(tunit[i], column->comment().toLatin1().constData()); } else { tunit[i] = new char[2]; strcpy(tunit[i], ""); } switch (column->columnMode()) { case AbstractColumn::Numeric: { int maxSize = -1; for (int row = 0; row < nrows; ++row) { if (QString::number(column->valueAt(row)).size() > maxSize) maxSize = QString::number(column->valueAt(row)).size(); } Double2StringFilter * filter = static_cast(column->outputFilter()); bool decimals = false; for (int ii = 0; ii < nrows; ++ii) { bool ok; QString cell = column->asStringColumn()->textAt(ii); double val = cell.toDouble(&ok); if (cell.size() > QString::number(val).size() + 1) { decimals = true; break; } } QString tformn; if (decimals) { int maxStringSize = -1; for (int row = 0; row < nrows; ++row) { if (column->asStringColumn()->textAt(row).size() > maxStringSize) maxStringSize = column->asStringColumn()->textAt(row).size(); } const int diff = abs(maxSize - maxStringSize); maxSize+= diff; tformn = QLatin1String("F")+ QString::number(maxSize) + QLatin1String(".") + QString::number(filter->numDigits()); } else tformn = QLatin1String("F")+ QString::number(maxSize) + QLatin1String(".0"); tform[i] = new char[tformn.size()]; strcpy(tform[i], tformn.toLatin1().data()); break; } case AbstractColumn::Text: { int maxSize = -1; for (int row = 0; row < nrows; ++row) { if (column->textAt(row).size() > maxSize) maxSize = column->textAt(row).size(); } const QString& tformn = QLatin1String("A") + QString::number(maxSize); tform[i] = new char[tformn.size()]; strcpy(tform[i], tformn.toLatin1().data()); break; } case AbstractColumn::DateTime: { break; } case AbstractColumn::Day: { break; } case AbstractColumn::Month: { break; } } } //TODO extension name containing[] ? if (fits_create_tbl(m_fitsFile, ASCII_TBL, nrows, tfields, columnNames, tform, tunit, spreadsheet->name().toLatin1().data(),&status )) { printError(status); for (int i = 0; i < tfields; ++i) { delete[] tform[i]; delete[] tunit[i]; delete[] columnNames[i]; } status = 0; fits_close_file(m_fitsFile, &status); if (!existed) { QFile file(fileName); file.remove(); } return; } for (int i = 0; i < tfields; ++i) { delete[] tform[i]; delete[] tunit[i]; delete[] columnNames[i]; } char* column[nrows]; double* columnNumeric = new double[nrows]; bool hadTextColumn = false; for (int col = 1; col <= tfields; ++col) { const Column* c = spreadsheet->column(col-1); AbstractColumn::ColumnMode columnMode = c->columnMode(); if (columnMode == AbstractColumn::Numeric) { for (int row = 0; row < nrows; ++row) columnNumeric[row] = c->valueAt(row); fits_write_col(m_fitsFile, TDOUBLE, col, 1, 1, nrows, columnNumeric, &status); if (status) { printError(status); delete[] columnNumeric; status = 0; fits_close_file(m_fitsFile, &status); if (!existed) { QFile file(fileName); file.remove(); } return; } } else { hadTextColumn = true; for (int row = 0; row < nrows; ++row) { column[row] = new char[c->textAt(row).size()]; strcpy(column[row], c->textAt(row).toLatin1().data()); } fits_write_col(m_fitsFile, TSTRING, col, 1, 1, nrows, column, &status); if (status) { printError(status); for (int i = 0; i < nrows; ++i) delete[] column[i]; status = 0; fits_close_file(m_fitsFile, &status); return; } } } delete[] columnNumeric; if (hadTextColumn) for (int i = 0; i < nrows; ++i) delete[] column[i]; status = 0; fits_close_file(m_fitsFile, &status); } } #else Q_UNUSED(fileName) Q_UNUSED(dataSource) #endif } /*! * \brief Return a map of the available extensions names in file \a filename * The keys of the map are the extension types, the values are the names * \param fileName the name of the FITS file to be analyzed */ QMultiMap FITSFilterPrivate::extensionNames(const QString& fileName) { #ifdef HAVE_FITS QMultiMap extensions; int status = 0; if (fits_open_file(&m_fitsFile, fileName.toLatin1(), READONLY, &status )) { printError(status); return QMultiMap(); } int hduCount; if (fits_get_num_hdus(m_fitsFile, &hduCount, &status)) { printError(status); return QMultiMap(); } int imageCount = 0; int asciiTableCount = 0; int binaryTableCount = 0; for (int currentHDU = 1; (currentHDU <= hduCount) && !status; ++currentHDU) { int hduType; status = 0; fits_get_hdu_type(m_fitsFile, &hduType, &status); switch (hduType) { case IMAGE_HDU: imageCount++; break; case ASCII_TBL: asciiTableCount++; break; case BINARY_TBL: binaryTableCount++; break; } char* keyVal = new char[FLEN_VALUE]; QString extName; if (!fits_read_keyword(m_fitsFile,"EXTNAME", keyVal, NULL, &status)) { extName = QLatin1String(keyVal); extName = extName.mid(1, extName.length() -2).simplified(); } else { status = 0; if (!fits_read_keyword(m_fitsFile,"HDUNAME", keyVal, NULL, &status)) { extName = QLatin1String(keyVal); extName = extName.mid(1, extName.length() -2).simplified(); } else { status = 0; switch (hduType) { case IMAGE_HDU: if (imageCount == 1) extName = i18n("Primary header"); else extName = i18n("IMAGE #%1").arg(imageCount); break; case ASCII_TBL: extName = i18n("ASCII_TBL #%1").arg(asciiTableCount); break; case BINARY_TBL: extName = i18n("BINARY_TBL #%1").arg(binaryTableCount); break; } } } delete[] keyVal; status = 0; extName = extName.trimmed(); switch (hduType) { case IMAGE_HDU: extensions.insert(QLatin1String("IMAGES"), extName); break; case ASCII_TBL: extensions.insert(QLatin1String("TABLES"), extName); break; case BINARY_TBL: extensions.insert(QLatin1String("TABLES"), extName); break; } fits_movrel_hdu(m_fitsFile, 1, NULL, &status); } if (status == END_OF_FILE) status = 0; fits_close_file(m_fitsFile, &status); return extensions; #else Q_UNUSED(fileName) return QMultiMap(); #endif } /*! * \brief Prints the error text corresponding to the status code \a status * \param status the status code of the error */ void FITSFilterPrivate::printError(int status) const { #ifdef HAVE_FITS if (status) { char errorText[FLEN_ERRMSG]; fits_get_errstatus(status, errorText ); qDebug() << QLatin1String(errorText); } #else Q_UNUSED(status) #endif } /*! * \brief Add the keywords \a keywords to the current header unit * \param keywords the keywords to be added * \param fileName the name of the FITS file (extension) in which the keywords are added */ void FITSFilterPrivate::addNewKeyword(const QString& fileName, const QList& keywords) { #ifdef HAVE_FITS int status = 0; if (fits_open_file(&m_fitsFile, fileName.toLatin1(), READWRITE, &status )) { printError(status); return; } - foreach (const FITSFilter::Keyword& keyword, keywords) { + for (const FITSFilter::Keyword& keyword: keywords) { status = 0; if (!keyword.key.compare(QLatin1String("COMMENT"))) { if (fits_write_comment(m_fitsFile, keyword.value.toLatin1(), &status)) printError(status); } else if (!keyword.key.compare(QLatin1String("HISTORY"))) { if (fits_write_history(m_fitsFile, keyword.value.toLatin1(), &status)) printError(status); } else if (!keyword.key.compare(QLatin1String("DATE"))) { if (fits_write_date(m_fitsFile, &status)) printError(status); } else { int ok = 0; if (keyword.key.length() <= FLEN_KEYWORD) { ok++; if (keyword.value.length() <= FLEN_VALUE) { ok++; if(keyword.comment.length() <= FLEN_COMMENT) ok++; } } if (ok == 3) { bool ok; double val = keyword.value.toDouble(&ok); if (ok) { if (fits_write_key(m_fitsFile, TDOUBLE, keyword.key.toLatin1().data(), &val, keyword.comment.toLatin1().data(), &status)) printError(status); } else { if (fits_write_key(m_fitsFile, TSTRING, keyword.key.toLatin1().data(), keyword.value.toLatin1().data(), keyword.comment.toLatin1().data(), &status)) printError(status); } } else if ( ok == 2) { //comment too long } else if ( ok == 1) { //value too long } else { //keyword too long } } } status = 0; fits_close_file(m_fitsFile, &status); #else Q_UNUSED(keywords) Q_UNUSED(fileName) #endif } /*! * \brief Update keywords in the current header unit * \param fileName The name of the FITS file (extension) in which the keywords will be updated * \param originals The original keywords of the FITS file (extension) * \param updates The keywords that contain the updated values */ void FITSFilterPrivate::updateKeywords(const QString& fileName, const QList& originals, const QVector& updates) { #ifdef HAVE_FITS int status = 0; if (fits_open_file(&m_fitsFile, fileName.toLatin1(), READWRITE, &status )) { printError(status); return; } FITSFilter::Keyword updatedKeyword; FITSFilter::Keyword originalKeyword; FITSFilter::KeywordUpdate keywordUpdate; for (int i = 0; i < updates.size(); ++i) { updatedKeyword = updates.at(i); originalKeyword = originals.at(i); keywordUpdate = originals.at(i).updates; if (keywordUpdate.keyUpdated && keywordUpdate.valueUpdated && keywordUpdate.commentUpdated) { if (updatedKeyword.isEmpty()) { if (fits_delete_key(m_fitsFile, originalKeyword.key.toLatin1(), &status)) { printError(status); status = 0; } continue; } } if (!updatedKeyword.key.isEmpty()) { if (fits_modify_name(m_fitsFile, originalKeyword.key.toLatin1(), updatedKeyword.key.toLatin1(), &status )) { printError(status); status = 0; } } if (!updatedKeyword.value.isEmpty()) { bool ok; int intValue; double doubleValue; bool updated = false; doubleValue = updatedKeyword.value.toDouble(&ok); if (ok) { if (fits_update_key(m_fitsFile,TDOUBLE, keywordUpdate.keyUpdated ? updatedKeyword.key.toLatin1() : originalKeyword.key.toLatin1(), &doubleValue, NULL, &status)) printError(status); else updated = true; } if (!updated) { intValue = updatedKeyword.value.toInt(&ok); if (ok) { if (fits_update_key(m_fitsFile,TINT, keywordUpdate.keyUpdated ? updatedKeyword.key.toLatin1() : originalKeyword.key.toLatin1(), &intValue, NULL, &status)) printError(status); else updated = true; } } if (!updated) { if (fits_update_key(m_fitsFile,TSTRING, keywordUpdate.keyUpdated ? updatedKeyword.key.toLatin1() : originalKeyword.key.toLatin1(), updatedKeyword.value.toLatin1().data(), NULL, &status)) printError(status); } } else { if (keywordUpdate.valueUpdated) { if (fits_update_key_null(m_fitsFile, keywordUpdate.keyUpdated ? updatedKeyword.key.toLatin1() : originalKeyword.key.toLatin1(), NULL, &status)) { printError(status); status = 0; } } } if (!updatedKeyword.comment.isEmpty()) { if (fits_modify_comment(m_fitsFile, keywordUpdate.keyUpdated ? updatedKeyword.key.toLatin1() : originalKeyword.key.toLatin1(), updatedKeyword.comment.toLatin1().data(), &status)) { printError(status); status = 0; } } else { if (keywordUpdate.commentUpdated) { if (fits_modify_comment(m_fitsFile, keywordUpdate.keyUpdated ? updatedKeyword.key.toLatin1() : originalKeyword.key.toLatin1(), QString("").toLatin1().data(), &status)) { printError(status); status = 0; } } } } status = 0; fits_close_file(m_fitsFile, &status); #else Q_UNUSED(fileName) Q_UNUSED(originals) Q_UNUSED(updates) #endif } /*! * \brief Delete the keywords \a keywords from the current header unit * \param fileName the name of the FITS file (extension) in which the keywords will be deleted. * \param keywords the keywords to deleted */ void FITSFilterPrivate::deleteKeyword(const QString& fileName, const QList &keywords) { #ifdef HAVE_FITS int status = 0; if (fits_open_file(&m_fitsFile, fileName.toLatin1(), READWRITE, &status )) { printError(status); return; } foreach (const FITSFilter::Keyword& keyword, keywords) { if (!keyword.key.isEmpty()) { status = 0; if (fits_delete_key(m_fitsFile, keyword.key.toLatin1(), &status)) printError(status); } } status = 0; fits_close_file(m_fitsFile, &status); #else Q_UNUSED(keywords) Q_UNUSED(fileName) #endif } /*! * \brief FITSFilterPrivate::addKeywordUnit * \param fileName the FITS file (extension) in which the keyword units are updated/added * \param keywords the keywords whose units were modified/added */ void FITSFilterPrivate::addKeywordUnit(const QString &fileName, const QList &keywords) { #ifdef HAVE_FITS int status = 0; if (fits_open_file(&m_fitsFile, fileName.toLatin1(), READWRITE, &status )) { printError(status); return; } - foreach (const FITSFilter::Keyword& keyword, keywords) { + for(const FITSFilter::Keyword& keyword : keywords) { if (keyword.updates.unitUpdated) { if (fits_write_key_unit(m_fitsFile, keyword.key.toLatin1(), keyword.unit.toLatin1().data(), &status)) { printError(status); status = 0; } } } status = 0; fits_close_file(m_fitsFile, &status); #else Q_UNUSED(fileName) Q_UNUSED(keywords) #endif } /*! * \brief Remove extensions from a FITS file * \param extensions The extensions to be removed from the FITS file */ void FITSFilterPrivate::removeExtensions(const QStringList &extensions) { #ifdef HAVE_FITS int status = 0; foreach (const QString& ext, extensions) { status = 0; if (fits_open_file(&m_fitsFile, ext.toLatin1(), READWRITE, &status )) { printError(status); continue; } if (fits_delete_hdu(m_fitsFile, NULL, &status)) printError(status); status = 0; fits_close_file(m_fitsFile, &status); } #else Q_UNUSED(extensions) #endif } /*! * \brief Returns a list of keywords in the current header of \a fileName * \param fileName the name of the FITS file (extension) to be opened * \return A list of keywords */ QList FITSFilterPrivate::chduKeywords(const QString& fileName) { #ifdef HAVE_FITS int status = 0; if (fits_open_file(&m_fitsFile, fileName.toLatin1(), READONLY, &status )) { printError(status); return QList (); } int numberOfKeys; if (fits_get_hdrspace(m_fitsFile, &numberOfKeys, NULL, &status)) { printError(status); return QList (); } QList keywords; keywords.reserve(numberOfKeys); char* key = new char[FLEN_KEYWORD]; char* value = new char[FLEN_VALUE]; char* comment = new char[FLEN_COMMENT]; char* unit = new char[FLEN_VALUE]; for (int i = 1; i <= numberOfKeys; ++i) { QStringList recordValues; FITSFilter::Keyword keyword; if (fits_read_keyn(m_fitsFile, i, key, value, comment, &status)) { printError(status); status = 0; continue; } fits_read_key_unit(m_fitsFile, key, unit, &status); recordValues << QLatin1String(key) << QLatin1String(value) << QLatin1String(comment) << QLatin1String(unit); keyword.key = recordValues[0].simplified(); keyword.value = recordValues[1].simplified(); keyword.comment = recordValues[2].simplified(); keyword.unit = recordValues[3].simplified(); keywords.append(keyword); } delete[] key; delete[] value; delete[] comment; delete[] unit; fits_close_file(m_fitsFile, &status); return keywords; #else Q_UNUSED(fileName) return QList(); #endif } /*! * \brief Builds the table \a headerEditTable from the keywords \a keys * \param fileName The name of the FITS file from which the keys are read if \a readKeys is \c true * \param headerEditTable The table to be built * \param readKeys It's used to determine whether the keywords are provided or they should be read from * file \a fileName * \param keys The keywords that are provided if the keywords were read already. */ void FITSFilterPrivate::parseHeader(const QString &fileName, QTableWidget *headerEditTable, bool readKeys, const QList& keys) { #ifdef HAVE_FITS QList keywords; if (readKeys) keywords = chduKeywords(fileName); else keywords = keys; headerEditTable->setRowCount(keywords.size()); QTableWidgetItem* item; for (int i = 0; i < keywords.size(); ++i) { const FITSFilter::Keyword keyword = keywords.at(i); const bool mandatory = FITSFilter::mandatoryImageExtensionKeywords().contains(keyword.key) || FITSFilter::mandatoryTableExtensionKeywords().contains(keyword.key); item = new QTableWidgetItem(keyword.key); const QString& itemText = item->text(); const bool notEditableKey = mandatory || itemText.contains(QLatin1String("TFORM")) || itemText.contains(QLatin1String("TTYPE")) || itemText.contains(QLatin1String("TUNIT")) || itemText.contains(QLatin1String("TDISP")) || itemText.contains(QLatin1String("TBCOL")) || itemText.contains(QLatin1String("TZERO")); const bool notEditableValue= mandatory || itemText.contains(QLatin1String("TFORM")) || itemText.contains(QLatin1String("TDISP")) || itemText.contains(QLatin1String("TBCOL")) || itemText.contains(QLatin1String("TZERO")); if (notEditableKey) item->setFlags(item->flags() & ~Qt::ItemIsEditable); else item->setFlags(Qt::ItemIsEditable | Qt::ItemIsSelectable | Qt::ItemIsEnabled); headerEditTable->setItem(i, 0, item ); item = new QTableWidgetItem(keyword.value); if (notEditableValue) item->setFlags(item->flags() & ~Qt::ItemIsEditable); else item->setFlags(Qt::ItemIsEditable | Qt::ItemIsSelectable | Qt::ItemIsEnabled); headerEditTable->setItem(i, 1, item ); QString commentFieldText; if (!keyword.unit.isEmpty()) { if (keyword.updates.unitUpdated) { const QString& comment = keyword.comment.right( keyword.comment.size() - keyword.comment.indexOf(QChar(']'))-1); commentFieldText = QLatin1String("[") + keyword.unit + QLatin1String("] ") + comment; } else { if (keyword.comment.left(1) == QLatin1String("[")) commentFieldText = keyword.comment; else commentFieldText = QLatin1String("[") + keyword.unit + QLatin1String("] ") + keyword.comment; } } else commentFieldText = keyword.comment; item = new QTableWidgetItem(commentFieldText); item->setFlags(Qt::ItemIsEditable | Qt::ItemIsSelectable | Qt::ItemIsEnabled); headerEditTable->setItem(i, 2, item ); } headerEditTable->resizeColumnsToContents(); #else Q_UNUSED(fileName) Q_UNUSED(headerEditTable) Q_UNUSED(readKeys) Q_UNUSED(keys) #endif } /*! * \brief Helper function to return the value of the key \a key * \param fileName The name of the FITS file (extension) in which the keyword with key \a key should exist * \param key The key of the keyword whose value it's returned * \return The value of the keyword as a string */ const QString FITSFilterPrivate::valueOf(const QString& fileName, const char *key) { #ifdef HAVE_FITS int status = 0; if (fits_open_file(&m_fitsFile, fileName.toLatin1(), READONLY, &status )) { printError(status); return QString (); } char* keyVal = new char[FLEN_VALUE]; QString keyValue; if (!fits_read_keyword(m_fitsFile, key, keyVal, NULL, &status)) { keyValue = QLatin1String(keyVal); keyValue = keyValue.simplified(); } else { printError(status); delete[] keyVal; fits_close_file(m_fitsFile, &status); return QString(); } delete[] keyVal; status = 0; fits_close_file(m_fitsFile, &status); return keyValue; #else Q_UNUSED(fileName) Q_UNUSED(key) return QString(); #endif } /*! * \brief Build the extensions tree from FITS file (extension) \a fileName * \param fileName The name of the FITS file to be opened * \param tw The QTreeWidget to be built * \param checkPrimary Used to determine whether the tree will be used for import or the header edit, * if it's \c true and if the primary array it's empty, then the item won't be added to the tree */ void FITSFilterPrivate::parseExtensions(const QString &fileName, QTreeWidget *tw, bool checkPrimary) { #ifdef HAVE_FITS const QMultiMap& extensions = extensionNames(fileName); const QStringList& imageExtensions = extensions.values(QLatin1String("IMAGES")); const QStringList& tableExtensions = extensions.values(QLatin1String("TABLES")); QTreeWidgetItem* root = tw->invisibleRootItem(); QTreeWidgetItem* treeNameItem = new QTreeWidgetItem((QTreeWidgetItem*)0, QStringList() << fileName); root->addChild(treeNameItem); treeNameItem->setExpanded(true); QTreeWidgetItem* imageExtensionItem = new QTreeWidgetItem((QTreeWidgetItem*)0, QStringList() << i18n("Images")); imageExtensionItem->setFlags(imageExtensionItem->flags() & ~Qt::ItemIsSelectable ); QString primaryHeaderNaxis = valueOf(fileName, "NAXIS"); const int naxis = primaryHeaderNaxis.toInt(); bool noImage = false; - foreach (const QString& ext, imageExtensions) { + for (const QString& ext : imageExtensions) { QTreeWidgetItem* treeItem = new QTreeWidgetItem((QTreeWidgetItem*)0, QStringList() << ext); if (ext == i18n("Primary header")) { if (checkPrimary && naxis == 0) continue; } imageExtensionItem->addChild(treeItem); } if (imageExtensionItem->childCount() > 0) { treeNameItem->addChild(imageExtensionItem); imageExtensionItem->setIcon(0,QIcon::fromTheme("view-preview")); imageExtensionItem->setExpanded(true); imageExtensionItem->child(0)->setSelected(true); tw->setCurrentItem(imageExtensionItem->child(0)); } else noImage = true; if (tableExtensions.size() > 0) { QTreeWidgetItem* tableExtensionItem = new QTreeWidgetItem((QTreeWidgetItem*)0, QStringList() << i18n("Tables")); tableExtensionItem->setFlags(tableExtensionItem->flags() & ~Qt::ItemIsSelectable ); - foreach (const QString& ext, tableExtensions) { + for (const QString& ext : tableExtensions) { QTreeWidgetItem* treeItem = new QTreeWidgetItem((QTreeWidgetItem*)0, QStringList() << ext); tableExtensionItem->addChild(treeItem); } if (tableExtensionItem->childCount() > 0) { treeNameItem->addChild(tableExtensionItem); tableExtensionItem->setIcon(0,QIcon::fromTheme("x-office-spreadsheet")); tableExtensionItem->setExpanded(true); if (noImage) { tableExtensionItem->child(0)->setSelected(true); tw->setCurrentItem(tableExtensionItem->child(0)); } } } #else Q_UNUSED(fileName) Q_UNUSED(tw) Q_UNUSED(checkPrimary) #endif } int FITSFilterPrivate::imagesCount(const QString &fileName) { return extensionNames(fileName).values(QLatin1String("IMAGES")).size(); } int FITSFilterPrivate::tablesCount(const QString &fileName) { return extensionNames(fileName).values(QLatin1String("TABLES")).size(); } /*! * \brief FITSFilterPrivate::~FITSFilterPrivate */ FITSFilterPrivate::~FITSFilterPrivate() { } //############################################################################## //################## Serialization/Deserialization ########################### //############################################################################## /*! Saves as XML. */ void FITSFilter::save(QXmlStreamWriter * writer) const { Q_UNUSED(writer) } /*! Loads from XML. */ bool FITSFilter::load(XmlStreamReader * loader) { Q_UNUSED(loader) return false; } diff --git a/src/commonfrontend/matrix/MatrixView.cpp b/src/commonfrontend/matrix/MatrixView.cpp index fe9427820..4d06a85b1 100644 --- a/src/commonfrontend/matrix/MatrixView.cpp +++ b/src/commonfrontend/matrix/MatrixView.cpp @@ -1,1485 +1,1483 @@ /*************************************************************************** File : MatrixView.cpp Project : LabPlot Description : View class for Matrix -------------------------------------------------------------------- Copyright : (C) 2015 Alexander Semke (alexander.semke@web.de) Copyright : (C) 2008-2009 Tilman Benkert (thzs@gmx.net) ***************************************************************************/ /*************************************************************************** * * * 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 "commonfrontend/matrix/MatrixView.h" #include "backend/matrix/Matrix.h" #include "backend/matrix/MatrixModel.h" #include "backend/matrix/matrixcommands.h" #include "backend/lib/macros.h" #include "backend/core/column/Column.h" #include "backend/core/column/ColumnPrivate.h" #include "kdefrontend/matrix/MatrixFunctionDialog.h" #include "kdefrontend/spreadsheet/StatisticsDialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // #include #include #include #include #include #include MatrixView::MatrixView(Matrix* matrix) : QWidget(), m_stackedWidget(new QStackedWidget(this)), m_tableView(new QTableView(this)), m_imageLabel(new QLabel(this)), m_matrix(matrix), m_model(new MatrixModel(matrix)), m_imageIsDirty(true) { init(); //resize the view to show a 10x10 region of the matrix. //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_matrix->isLoading()) { int w = m_tableView->horizontalHeader()->sectionSize(0)*10 + m_tableView->verticalHeader()->width(); int h = m_tableView->verticalHeader()->sectionSize(0)*10 + m_tableView->horizontalHeader()->height(); resize(w+50, h+50); } } MatrixView::~MatrixView() { delete m_model; } MatrixModel* MatrixView::model() const { return m_model; } void MatrixView::init() { initActions(); connectActions(); initMenus(); QHBoxLayout* layout = new QHBoxLayout(this); layout->setContentsMargins(0,0,0,0); setSizePolicy(QSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding)); setFocusPolicy(Qt::StrongFocus); setFocus(); installEventFilter(this); layout->addWidget(m_stackedWidget); //table data view m_tableView->setModel(m_model); m_stackedWidget->addWidget(m_tableView); //horizontal header QHeaderView* h_header = m_tableView->horizontalHeader(); h_header->setMovable(false); h_header->installEventFilter(this); //vertical header QHeaderView* v_header = m_tableView->verticalHeader(); v_header->setMovable(false); v_header->installEventFilter(this); //set the header sizes to the (potentially user customized) sizes stored in Matrix adjustHeaders(); //image view QScrollArea* area = new QScrollArea(this); m_stackedWidget->addWidget(area); area->setWidget(m_imageLabel); //SLOTs connect(m_matrix, SIGNAL(requestProjectContextMenu(QMenu*)), this, SLOT(createContextMenu(QMenu*))); connect(m_model, SIGNAL(changed()), this, SLOT(matrixDataChanged())); //keyboard shortcuts QShortcut* sel_all = new QShortcut(QKeySequence(tr("Ctrl+A", "Matrix: select all")), m_tableView); connect(sel_all, SIGNAL(activated()), m_tableView, SLOT(selectAll())); //TODO: add shortcuts for copy&paste, //for a single shortcut we need to descriminate between copy&paste for columns, rows or selected cells. } void MatrixView::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_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); // matrix related actions QActionGroup* viewActionGroup = new QActionGroup(this); viewActionGroup->setExclusive(true); action_data_view = new QAction(QIcon::fromTheme("labplot-matrix"), i18n("Data"), viewActionGroup); action_data_view->setCheckable(true); action_data_view->setChecked(true); action_image_view = new QAction(QIcon::fromTheme("image-x-generic"), i18n("Image"), viewActionGroup); action_image_view->setCheckable(true); connect(viewActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(switchView(QAction*))); action_fill_function = new QAction(QIcon::fromTheme(""), i18n("Function Values"), this); action_fill_const = new QAction(QIcon::fromTheme(""), i18n("Const Values"), this); action_clear_matrix = new QAction(QIcon::fromTheme("edit-clear"), i18n("Clear Matrix"), this); action_go_to_cell = new QAction(QIcon::fromTheme("go-jump"), i18n("&Go to Cell"), this); action_transpose = new QAction(i18n("&Transpose"), this); action_mirror_horizontally = new QAction(QIcon::fromTheme("object-flip-horizontal"), i18n("Mirror &Horizontally"), this); action_mirror_vertically = new QAction(QIcon::fromTheme("object-flip-vertical"), i18n("Mirror &Vertically"), this); // action_duplicate = new QAction(i18nc("duplicate matrix", "&Duplicate"), this); //TODO //icon QActionGroup* headerFormatActionGroup = new QActionGroup(this); headerFormatActionGroup->setExclusive(true); action_header_format_1= new QAction(i18n("Rows and Columns"), headerFormatActionGroup); action_header_format_1->setCheckable(true); action_header_format_2= new QAction(i18n("xy-Values"), headerFormatActionGroup); action_header_format_2->setCheckable(true); action_header_format_3= new QAction(i18n("Rows, Columns and xy-Values"), headerFormatActionGroup); action_header_format_3->setCheckable(true); connect(headerFormatActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(headerFormatChanged(QAction*))); // column related actions action_add_columns = new QAction(QIcon::fromTheme("edit-table-insert-column-right"), i18n("&Add Columns"), this); 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_statistics_columns = new QAction(QIcon::fromTheme("view-statistics"), i18n("Statisti&cs"), this); // row related actions action_add_rows = new QAction(QIcon::fromTheme("edit-table-insert-row-above"), i18n("&Add Rows"), this); 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_statistics_rows = new QAction(QIcon::fromTheme("view-statistics"), i18n("Statisti&cs"), this); } void MatrixView::connectActions() { // selection related actions 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_clear_selection, SIGNAL(triggered()), this, SLOT(clearSelectedCells())); connect(action_select_all, SIGNAL(triggered()), m_tableView, SLOT(selectAll())); // matrix related actions connect(action_fill_function, SIGNAL(triggered()), this, SLOT(fillWithFunctionValues())); connect(action_fill_const, SIGNAL(triggered()), this, SLOT(fillWithConstValues())); connect(action_go_to_cell, SIGNAL(triggered()), this, SLOT(goToCell())); //connect(action_duplicate, SIGNAL(triggered()), this, SLOT(duplicate())); connect(action_clear_matrix, SIGNAL(triggered()), m_matrix, SLOT(clear())); connect(action_transpose, SIGNAL(triggered()), m_matrix, SLOT(transpose())); connect(action_mirror_horizontally, SIGNAL(triggered()), m_matrix, SLOT(mirrorHorizontally())); connect(action_mirror_vertically, SIGNAL(triggered()), m_matrix, SLOT(mirrorVertically())); // column related actions connect(action_add_columns, SIGNAL(triggered()), this, SLOT(addColumns())); 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_statistics_columns, SIGNAL(triggered()), this, SLOT(showColumnStatistics())); // row related actions connect(action_add_rows, SIGNAL(triggered()), this, SLOT(addRows())); 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_statistics_rows, SIGNAL(triggered()), this, SLOT(showRowStatistics())); } void MatrixView::initMenus() { //selection menu m_selectionMenu = new QMenu(i18n("Selection"), this); 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); //column menu m_columnMenu = new QMenu(this); m_columnMenu->addAction(action_insert_columns); m_columnMenu->addAction(action_remove_columns); m_columnMenu->addAction(action_clear_columns); m_columnMenu->addAction(action_statistics_columns); //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_statistics_rows); //matrix menu m_matrixMenu = new QMenu(this); m_matrixMenu->addMenu(m_selectionMenu); m_matrixMenu->addSeparator(); QMenu* submenu = new QMenu(i18n("Generate Data"), this); submenu->addAction(action_fill_const); submenu->addAction(action_fill_function); m_matrixMenu->addMenu(submenu); m_matrixMenu->addSeparator(); submenu = new QMenu(i18n("View"), this); submenu->addAction(action_data_view); submenu->addAction(action_image_view); m_matrixMenu->addMenu(submenu); m_matrixMenu->addSeparator(); m_matrixMenu->addAction(action_select_all); m_matrixMenu->addAction(action_clear_matrix); m_matrixMenu->addSeparator(); m_matrixMenu->addAction(action_transpose); m_matrixMenu->addAction(action_mirror_horizontally); m_matrixMenu->addAction(action_mirror_vertically); m_matrixMenu->addSeparator(); m_headerFormatMenu = new QMenu(i18n("Header format"), this); m_headerFormatMenu->addAction(action_header_format_1); m_headerFormatMenu->addAction(action_header_format_2); m_headerFormatMenu->addAction(action_header_format_3); m_matrixMenu->addMenu(m_headerFormatMenu); m_matrixMenu->addSeparator(); m_matrixMenu->addAction(action_go_to_cell); } /*! * Populates the menu \c menu with the spreadsheet and spreadsheet view relevant actions. * The menu is used * - as the context menu in MatrixView * - as the "matrix menu" in the main menu-bar (called form MainWin) * - as a part of the matrix context menu in project explorer */ void MatrixView::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->insertMenu(firstAction, m_selectionMenu); menu->insertSeparator(firstAction); QMenu* submenu = new QMenu(i18n("Generate Data"), const_cast(this)); submenu->addAction(action_fill_const); submenu->addAction(action_fill_function); menu->insertMenu(firstAction, submenu); menu->insertSeparator(firstAction); submenu = new QMenu(i18n("View"), const_cast(this)); submenu->addAction(action_data_view); submenu->addAction(action_image_view); menu->insertMenu(firstAction, submenu); menu->insertSeparator(firstAction); menu->insertAction(firstAction, action_select_all); menu->insertAction(firstAction, action_clear_matrix); menu->insertSeparator(firstAction); menu->insertAction(firstAction, action_transpose); menu->insertAction(firstAction, action_mirror_horizontally); menu->insertAction(firstAction, action_mirror_vertically); menu->insertSeparator(firstAction); // menu->insertAction(firstAction, action_duplicate); menu->insertMenu(firstAction, m_headerFormatMenu); menu->insertSeparator(firstAction); menu->insertAction(firstAction, action_go_to_cell); menu->insertSeparator(firstAction); } /*! set the row and column size to the saved sizes. */ void MatrixView::adjustHeaders() { QHeaderView* h_header = m_tableView->horizontalHeader(); QHeaderView* v_header = m_tableView->verticalHeader(); disconnect(v_header, SIGNAL(sectionResized(int,int,int)), this, SLOT(handleVerticalSectionResized(int,int,int))); disconnect(h_header, SIGNAL(sectionResized(int,int,int)), this, SLOT(handleHorizontalSectionResized(int,int,int))); //resize columns to the saved sizes or to fit the contents if the widht is 0 int cols = m_matrix->columnCount(); for (int i=0; icolumnWidth(i) == 0) m_tableView->resizeColumnToContents(i); else m_tableView->setColumnWidth(i, m_matrix->columnWidth(i)); } //resize rows to the saved sizes or to fit the contents if the height is 0 int rows = m_matrix->rowCount(); for (int i=0; irowHeight(i) == 0) m_tableView->resizeRowToContents(i); else m_tableView->setRowHeight(i, m_matrix->rowHeight(i)); } connect(v_header, SIGNAL(sectionResized(int,int,int)), this, SLOT(handleVerticalSectionResized(int,int,int))); connect(h_header, SIGNAL(sectionResized(int,int,int)), this, SLOT(handleHorizontalSectionResized(int,int,int))); } /*! Resizes the headers/columns to fit the new content. Called on changes of the header format in Matrix. */ void MatrixView::resizeHeaders() { m_tableView->resizeColumnsToContents(); m_tableView->resizeRowsToContents(); if (m_matrix->headerFormat() == Matrix::HeaderRowsColumns) action_header_format_1->setChecked(true); else if (m_matrix->headerFormat() == Matrix::HeaderValues) action_header_format_2->setChecked(true); else action_header_format_3->setChecked(true); } /*! Returns how many columns are selected. If full is true, this function only returns the number of fully selected columns. */ int MatrixView::selectedColumnCount(bool full) const { int count = 0; int cols = m_matrix->columnCount(); for (int i=0; iselectionModel()->isColumnSelected(col, QModelIndex()); else return m_tableView->selectionModel()->columnIntersectsSelection(col, QModelIndex()); } /*! Return how many rows are (at least partly) selected If full is true, this function only returns the number of fully selected rows. */ int MatrixView::selectedRowCount(bool full) const { int count = 0; int rows = m_matrix->rowCount(); for (int i=0; i < rows; i++) if (isRowSelected(i, full)) count++; return count; } /*! Returns true if row \c row is selected; otherwise false If full is true, this function only returns true if the whole row is selected. */ bool MatrixView::isRowSelected(int row, bool full) const { 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 full is true, this function only looks for fully selected columns. */ int MatrixView::firstSelectedColumn(bool full) const { int cols = m_matrix->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 full is true, this function only looks for fully selected rows. */ int MatrixView::firstSelectedRow(bool full) const { int rows = m_matrix->rowCount(); for (int i=0; i < rows; i++) { if (isRowSelected(i, full)) return i; } return -1; } /*! Return the index of the last selected row If full is true, this function only looks for fully selected rows. */ int MatrixView::lastSelectedRow(bool full) const { int rows = m_matrix->rowCount(); for (int i=rows-1; i >= 0; i--) if (isRowSelected(i, full)) return i; return -2; } bool MatrixView::isCellSelected(int row, int col) const { if (row < 0 || col < 0 || row >= m_matrix->rowCount() || col >= m_matrix->columnCount()) return false; return m_tableView->selectionModel()->isSelected(m_model->index(row, col)); } void MatrixView::setCellSelected(int row, int col) { m_tableView->selectionModel()->select(m_model->index(row, col), QItemSelectionModel::Select); } void MatrixView::setCellsSelected(int first_row, int first_col, int last_row, int last_col) { 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), QItemSelectionModel::SelectCurrent); } /*! Determine the current cell (-1 if no cell is designated as the current) */ void MatrixView::getCurrentCell(int* row, int* col) const { QModelIndex index = m_tableView->selectionModel()->currentIndex(); if (index.isValid()) { *row = index.row(); *col = index.column(); } else { *row = -1; *col = -1; } } bool MatrixView::eventFilter(QObject * watched, QEvent * event) { if (event->type() == QEvent::ContextMenu) { QContextMenuEvent* cm_event = static_cast(event); QPoint global_pos = cm_event->globalPos(); if (watched == m_tableView->verticalHeader()) m_rowMenu->exec(global_pos); else if (watched == m_tableView->horizontalHeader()) m_columnMenu->exec(global_pos); else if (watched == this) m_matrixMenu->exec(global_pos); else return QWidget::eventFilter(watched, event); return true; } else return QWidget::eventFilter(watched, event); } void MatrixView::keyPressEvent(QKeyEvent* event) { if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) advanceCell(); } //############################################################################## //#################################### SLOTs ################################ //############################################################################## /*! Advance current cell after [Return] or [Enter] was pressed */ void MatrixView::advanceCell() { QModelIndex idx = m_tableView->currentIndex(); if (idx.row()+1 < m_matrix->rowCount()) m_tableView->setCurrentIndex(idx.sibling(idx.row()+1, idx.column())); } void MatrixView::goToCell() { bool ok; int col = QInputDialog::getInteger(0, i18n("Go to Cell"), i18n("Enter column"), 1, 1, m_matrix->columnCount(), 1, &ok); if (!ok) return; int row = QInputDialog::getInteger(0, i18n("Go to Cell"), i18n("Enter row"), 1, 1, m_matrix->rowCount(), 1, &ok); if (!ok) return; goToCell(row-1, col-1); } void MatrixView::goToCell(int row, int col) { QModelIndex index = m_model->index(row, col); m_tableView->scrollTo(index); m_tableView->setCurrentIndex(index); } void MatrixView::handleHorizontalSectionResized(int logicalIndex, int oldSize, int newSize) { Q_UNUSED(oldSize) m_matrix->setColumnWidth(logicalIndex, newSize); } void MatrixView::handleVerticalSectionResized(int logicalIndex, int oldSize, int newSize) { Q_UNUSED(oldSize) m_matrix->setRowHeight(logicalIndex, newSize); } void MatrixView::fillWithFunctionValues() { MatrixFunctionDialog* dlg = new MatrixFunctionDialog(m_matrix); dlg->setAttribute(Qt::WA_DeleteOnClose); dlg->exec(); } void MatrixView::fillWithConstValues() { bool ok = false; double value = QInputDialog::getDouble(this, i18n("Fill the matrix with constant value"), i18n("Value"), 0, -2147483647, 2147483647, 6, &ok); if (ok) { WAIT_CURSOR; QVector > newData = m_matrix->data(); for (int col=0; colcolumnCount(); ++col) { for (int row=0; rowrowCount(); ++row) newData[col][row] = value; } m_matrix->setData(newData); RESET_CURSOR; } } //############################ selection related slots ######################### void MatrixView::cutSelection() { if (firstSelectedRow() < 0) return; WAIT_CURSOR; m_matrix->beginMacro(i18n("%1: cut selected cell(s)", m_matrix->name())); copySelection(); clearSelectedCells(); m_matrix->endMacro(); RESET_CURSOR; } void MatrixView::copySelection() { int first_col = firstSelectedColumn(false); if (first_col == -1) return; int last_col = lastSelectedColumn(false); if (last_col == -2) return; int first_row = firstSelectedRow(false); if (first_row == -1) return; int last_row = lastSelectedRow(false); 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++) { if (isCellSelected(first_row + r, first_col + c)) output_str += QLocale().toString(m_matrix->cell(first_row + r, first_col + c), m_matrix->numericFormat(), 16); // copy with max. precision if (c < cols-1) output_str += '\t'; } if (r < rows-1) output_str += '\n'; } QApplication::clipboard()->setText(output_str); RESET_CURSOR; } void MatrixView::pasteIntoSelection() { if (m_matrix->columnCount() < 1 || m_matrix->rowCount() < 1) return; const QMimeData* mime_data = QApplication::clipboard()->mimeData(); if (!mime_data->hasFormat("text/plain")) return; WAIT_CURSOR; m_matrix->beginMacro(i18n("%1: paste from clipboard", m_matrix->name())); int first_col = firstSelectedColumn(false); int last_col = lastSelectedColumn(false); int first_row = firstSelectedRow(false); int last_row = lastSelectedRow(false); int input_row_count = 0; int input_col_count = 0; int rows, cols; QString input_str = QString(mime_data->data("text/plain")); QList< QStringList > cell_texts; QStringList input_rows(input_str.split('\n')); input_row_count = input_rows.count(); input_col_count = 0; for (int i=0; i < input_row_count; i++) { cell_texts.append(input_rows.at(i).split('\t')); if (cell_texts.at(i).count() > input_col_count) input_col_count = cell_texts.at(i).count(); } // if the is no selection or only one cell selected, the // selection will be expanded to the needed size from the current cell if ( (first_col == -1 || first_row == -1) || (last_row == first_row && last_col == first_col) ) { 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 matrix if necessary if (last_col >= m_matrix->columnCount()) m_matrix->appendColumns(last_col+1-m_matrix->columnCount()); if (last_row >= m_matrix->rowCount()) m_matrix->appendRows(last_row+1-m_matrix->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; rsetCell(first_row + r, first_col + c, cell_texts.at(r).at(c).toDouble()); } } m_matrix->endMacro(); RESET_CURSOR; } void MatrixView::clearSelectedCells() { int first_row = firstSelectedRow(); if (first_row<0) return; int first_col = firstSelectedColumn(); if (first_col<0) return; int last_row = lastSelectedRow(); int last_col = lastSelectedColumn(); WAIT_CURSOR; m_matrix->beginMacro(i18n("%1: clear selected cell(s)", m_matrix->name())); for (int i=first_row; i <= last_row; i++) { for (int j=first_col; j <= last_col; j++) { if (isCellSelected(i, j)) m_matrix->clearCell(i, j); } } m_matrix->endMacro(); RESET_CURSOR; } class UpdateImageTask : public QRunnable { public: UpdateImageTask(int start, int end, QImage& image, const QVector >& matrixData, double scaleFactor, double min) : m_image(image), m_matrixData(matrixData) { m_start = start; m_end = end; m_scaleFactor = scaleFactor; m_min = min; }; void run() { for (int row=m_start; row >& m_matrixData; double m_scaleFactor; double m_min; }; void MatrixView::updateImage() { WAIT_CURSOR; m_image = QImage(m_matrix->columnCount(), m_matrix->rowCount(), QImage::Format_ARGB32); //find min/max value // QTime timer; // timer.start(); double dmax= -DBL_MAX, dmin= DBL_MAX; const QVector >& matrixData = m_matrix->data(); const int width = m_matrix->columnCount(); const int height = m_matrix->rowCount(); for (int col=0; colvalue) dmin=value; } } // qDebug()<<"min/max determined in " << (float)timer.elapsed()/1000 << "s"; //update the image // timer.start(); const double scaleFactor = 255.0/(dmax-dmin); QThreadPool* pool = QThreadPool::globalInstance(); int range = ceil(double(m_image.height())/pool->maxThreadCount()); for (int i=0; imaxThreadCount(); ++i) { const int start = i*range; int end = (i+1)*range; if (end>m_image.height()) end = m_image.height(); UpdateImageTask* task = new UpdateImageTask(start, end, m_image, matrixData, scaleFactor, dmin); pool->start(task); } pool->waitForDone(); // qDebug()<<"image updated in " << (float)timer.elapsed()/1000 << "s"; m_imageLabel->resize(width, height); m_imageLabel->setPixmap(QPixmap::fromImage(m_image)); m_imageIsDirty = false; RESET_CURSOR; } //############################# matrix related slots ########################### void MatrixView::switchView(QAction* action) { if (action == action_data_view) m_stackedWidget->setCurrentIndex(0); else { if (m_imageIsDirty) this->updateImage(); m_stackedWidget->setCurrentIndex(1); } } void MatrixView::matrixDataChanged() { m_imageIsDirty = true; if (m_stackedWidget->currentIndex() == 1) this->updateImage(); } void MatrixView::headerFormatChanged(QAction* action) { if (action == action_header_format_1) m_matrix->setHeaderFormat(Matrix::HeaderRowsColumns); else if (action == action_header_format_2) m_matrix->setHeaderFormat(Matrix::HeaderValues); else m_matrix->setHeaderFormat(Matrix::HeaderRowsColumnsValues); } //############################# column related slots ########################### /*! Append as many columns as are selected. */ void MatrixView::addColumns() { m_matrix->appendColumns(selectedColumnCount(false)); } void MatrixView::insertEmptyColumns() { int first = firstSelectedColumn(); int last = lastSelectedColumn(); if (first < 0) return; int count, current = first; WAIT_CURSOR; m_matrix->beginMacro(i18n("%1: insert empty column(s)", m_matrix->name())); while (current <= last) { current = first+1; while (current <= last && isColumnSelected(current)) current++; count = current-first; m_matrix->insertColumns(first, count); current += count; last += count; while (current <= last && isColumnSelected(current)) current++; first = current; } m_matrix->endMacro(); RESET_CURSOR; } void MatrixView::removeSelectedColumns() { int first = firstSelectedColumn(); int last = lastSelectedColumn(); if (first < 0) return; WAIT_CURSOR; m_matrix->beginMacro(i18n("%1: remove selected column(s)", m_matrix->name())); for (int i=last; i >= first; i--) if (isColumnSelected(i, false)) m_matrix->removeColumns(i, 1); m_matrix->endMacro(); RESET_CURSOR; } void MatrixView::clearSelectedColumns() { WAIT_CURSOR; m_matrix->beginMacro(i18n("%1: clear selected column(s)", m_matrix->name())); for (int i=0; i < m_matrix->columnCount(); i++) { if (isColumnSelected(i, false)) m_matrix->clearColumn(i); } m_matrix->endMacro(); RESET_CURSOR; } //############################## rows related slots ############################ /*! Append as many rows as are selected. */ void MatrixView::addRows() { m_matrix->appendRows(selectedRowCount(false)); } void MatrixView::insertEmptyRows() { int first = firstSelectedRow(); int last = lastSelectedRow(); int count, current = first; if (first < 0) return; WAIT_CURSOR; m_matrix->beginMacro(i18n("%1: insert empty rows(s)", m_matrix->name())); while (current <= last) { current = first+1; while (current <= last && isRowSelected(current)) current++; count = current-first; m_matrix->insertRows(first, count); current += count; last += count; while (current <= last && !isRowSelected(current)) current++; first = current; } m_matrix->endMacro(); RESET_CURSOR; } void MatrixView::removeSelectedRows() { int first = firstSelectedRow(); int last = lastSelectedRow(); if (first < 0) return; WAIT_CURSOR; m_matrix->beginMacro(i18n("%1: remove selected rows(s)", m_matrix->name())); for (int i=last; i >= first; i--) if (isRowSelected(i, false)) m_matrix->removeRows(i, 1); m_matrix->endMacro(); RESET_CURSOR; } void MatrixView::clearSelectedRows() { int first = firstSelectedRow(); int last = lastSelectedRow(); if (first < 0) return; WAIT_CURSOR; m_matrix->beginMacro(i18n("%1: clear selected rows(s)", m_matrix->name())); for (int i=first; i <= last; i++) { if (isRowSelected(i)) m_matrix->clearRow(i); } m_matrix->endMacro(); RESET_CURSOR; } /*! prints the complete matrix to \c printer. */ void MatrixView::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 QVector >& matrixData = m_matrix->data(); const int rows = m_matrix->rowCount(); const int cols = m_matrix->columnCount(); int height = margin; const int vertHeaderWidth = vHeader->width(); int right = margin + vertHeaderWidth; int columnsPerTable = 0; int headerStringWidth = 0; int firstRowStringWidth = vertHeaderWidth; bool tablesNeeded = false; QVector firstRowCeilSizes; firstRowCeilSizes.reserve(matrixData[0].size()); firstRowCeilSizes.resize(matrixData[0].size()); QRect br; for (int i = 0; i < matrixData.size(); ++i) { br = painter.boundingRect(br, Qt::AlignCenter,QString::number(matrixData[i][0]) + '\t'); firstRowCeilSizes[i] = br.width() > m_tableView->columnWidth(i) ? br.width() : m_tableView->columnWidth(i); } for (int col = 0; col < cols; ++col) { headerStringWidth += m_tableView->columnWidth(col); br = painter.boundingRect(br, Qt::AlignCenter,QString::number(matrixData[col][0]) + '\t'); firstRowStringWidth += br.width(); 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++; for (int table = 0; table < tablesCount; ++table) { right = margin + vertHeaderWidth; //Paint the horizontal header first 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; int 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)*/ firstRowCeilSizes[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()); } //first horizontal line painter.drawLine(margin + vertHeaderWidth, height, right-1, height); 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)*/ firstRowCeilSizes[j]; cellText = QString::number(matrixData[j][i]) + '\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 MatrixView::exportToFile(const QString& path, const QString& separator) const { QFile file(path); if (!file.open(QFile::WriteOnly | QFile::Truncate)) return; QTextStream out(&file); QString sep = separator; sep = sep.replace(QLatin1String("TAB"), QLatin1String("\t"), Qt::CaseInsensitive); sep = sep.replace(QLatin1String("SPACE"), QLatin1String(" "), Qt::CaseInsensitive); //export values const int cols = m_matrix->columnCount(); const int rows = m_matrix->rowCount(); const QVector >& matrixData = m_matrix->data(); for (int row=0; row > toExport; int firstSelectedCol = 0; int firstSelectedRowi = 0; int totalRowCount = 0; int cols = 0; if (entire) { cols = m_matrix->columnCount(); totalRowCount = m_matrix->rowCount(); toExport.reserve(totalRowCount); toExport.resize(totalRowCount); for (int row = 0; row < totalRowCount; ++row) { toExport[row].reserve(cols); toExport[row].resize(cols); for (int col = 0; col < cols; ++col) toExport[row][col] = m_matrix->text(row,col); } firstSelectedCol = 0; firstSelectedRowi = 0; } else { cols = selectedColumnCount(); totalRowCount = selectedRowCount(); firstSelectedCol = firstSelectedColumn(); if (firstSelectedCol == -1) return; firstSelectedRowi = firstSelectedRow(); if (firstSelectedRowi == -1) return; const int lastSelectedCol = lastSelectedColumn(); const int lastSelectedRowi = lastSelectedRow(); toExport.reserve(lastSelectedRowi - firstSelectedRowi+1); toExport.resize(lastSelectedRowi - firstSelectedRowi+1); int r = 0; int c = 0; for (int row = firstSelectedRowi; row <= lastSelectedRowi; ++row, ++r) { toExport[r].reserve(lastSelectedCol - firstSelectedCol+1); toExport[r].resize(lastSelectedCol - firstSelectedCol+1); c = 0; for (int col = firstSelectedCol; col <= lastSelectedCol; ++col,++c) toExport[r][c] = m_matrix->text(row, col); } } int columnsStringSize = 0; int headerStringSize = 0; int columnsPerTable = 0; const int firstHHeaderSectionLength = m_tableView->model()->headerData(0, Qt::Horizontal).toString().length(); const int firstSelectedVHeaderSectionLength = m_tableView->model()->headerData(firstSelectedRow(), Qt::Vertical).toString().length(); if (verticalHeaders) { if (entire) headerStringSize += firstHHeaderSectionLength; else headerStringSize += firstSelectedVHeaderSectionLength; } if (!horizontalHeaders && verticalHeaders) { if (entire) columnsStringSize += firstHHeaderSectionLength; else columnsStringSize += firstSelectedVHeaderSectionLength; } for (int col = 0; col < cols; ++col) { int maxSize = -1; for (int row = 0; row < toExport.size(); ++row) { if (toExport.at(row).at(col).size() > maxSize) maxSize = toExport.at(row).at(col).size(); } columnsStringSize += maxSize; if (horizontalHeaders) headerStringSize += m_tableView->model()->headerData(col, Qt::Horizontal).toString().length(); if ((columnsStringSize > 65) || (headerStringSize > 65)) break; ++columnsPerTable; } 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 Matrix Export to \\LaTeX{} } \n"); out << QLatin1String("\\author{LabPlot} \n"); out << QLatin1String("\\date{\\today} \n"); // out << "\\maketitle \n"; } const QString endTabularTable ("\\end{tabular} \n \\end{table} \n"); const QString tableCaption ("\\caption{"+ m_matrix->name() + "} \n"); const QString beginTable ("\\begin{table}[ht] \n"); const QString centeredColumn( gridLines ? QLatin1String(" c |") : QLatin1String(" c ")); int rowCount = 0; const int maxRows = 45; bool captionRemoved = false; if (columnsSeparating) { 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}{"); textable<< (gridLines ?QLatin1String("|") : QLatin1String("")); for (int i = 0; i < columnsPerTable; ++i) textable << centeredColumn; if (verticalHeaders) textable << centeredColumn; textable << QLatin1String("} \n"); if (gridLines) textable << QLatin1String("\\hline \n"); if (horizontalHeaders) { if (latexHeaders) textable << QLatin1String("\\rowcolor{HeaderBgColor} \n"); if (verticalHeaders) textable << QLatin1String(" & "); for (int col = table*columnsPerTable; col < (table * columnsPerTable) + columnsPerTable; ++col) { textable << m_tableView->model()->headerData(col + firstSelectedCol, Qt::Horizontal).toString(); if (col != ((table * columnsPerTable)+ columnsPerTable)-1) textable << QLatin1String(" & "); } textable << QLatin1String("\\\\ \n"); if (gridLines) textable << QLatin1String("\\hline \n"); } foreach (const QString& s, textable) out << s; for (int row = 0; row < totalRowCount; ++row) { if (verticalHeaders) { out << "\\cellcolor{HeaderBgColor} "; out << m_tableView->model()->headerData(row + firstSelectedRowi, Qt::Vertical).toString(); out << QLatin1String(" & "); } for (int col = table*columnsPerTable; col < (table * columnsPerTable) + columnsPerTable; ++col ) { out << toExport.at(row).at(col); if (col != ((table * columnsPerTable)+ columnsPerTable)-1) out << QLatin1String(" & "); } 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; } captionRemoved = false; 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 << centeredColumn; if (verticalHeaders) remainingTable << centeredColumn; remainingTable << QLatin1String("} \n"); if (gridLines) remainingTable << QLatin1String("\\hline \n"); if (horizontalHeaders) { if (latexHeaders) remainingTable << QLatin1String("\\rowcolor{HeaderBgColor} \n"); if (verticalHeaders) remainingTable << QLatin1String(" & "); for (int col = 0; col < remainingColumns; ++col) { remainingTable << m_tableView->model()->headerData(firstSelectedCol+col + (tablesCount * columnsPerTable), Qt::Horizontal).toString(); if (col != remainingColumns-1) remainingTable << QLatin1String(" & "); } remainingTable << QLatin1String("\\\\ \n"); if (gridLines) remainingTable << QLatin1String("\\hline \n"); } foreach (const QString& s, remainingTable) out << s; for (int row = 0; row < totalRowCount; ++row) { if (verticalHeaders) { out << "\\cellcolor{HeaderBgColor}"; out << m_tableView->model()->headerData(row+ firstSelectedRowi, Qt::Vertical).toString(); out << QLatin1String(" & "); } for (int col = 0; col < remainingColumns; ++col ) { out << toExport.at(row).at(col + (tablesCount * columnsPerTable)); if (col != remainingColumns-1) out << QLatin1String(" & "); } 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 << centeredColumn; if (verticalHeaders) textable << centeredColumn; textable << QLatin1String("} \n"); if (gridLines) textable << QLatin1String("\\hline \n"); if (horizontalHeaders) { if (latexHeaders) textable << QLatin1String("\\rowcolor{HeaderBgColor} \n"); if (verticalHeaders) textable << QLatin1String(" & "); for (int col = 0; col < cols ; ++col) { textable << m_tableView->model()->headerData(col+firstSelectedCol, Qt::Horizontal).toString(); if (col != cols-1) textable << QLatin1String(" & "); } textable << QLatin1String("\\\\ \n"); if (gridLines) textable << QLatin1String("\\hline \n"); } foreach (const QString& s, textable) out << s; for (int row = 0; row < totalRowCount; ++row) { if (verticalHeaders) { out << "\\cellcolor{HeaderBgColor}"; out << m_tableView->model()->headerData(row + firstSelectedRowi, Qt::Vertical).toString(); out << QLatin1String(" & "); } for (int col = 0; col < cols; ++col ) { out << toExport.at(row).at(col); if (col != cols-1) out << " & "; } 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; if (!captionRemoved) captionRemoved = true; rowCount = 0; if (!captionRemoved) captionRemoved = true; } } out << endTabularTable; } if (latexHeaders) out << QLatin1String("\\end{document} \n"); } void MatrixView::showColumnStatistics() { if (selectedColumnCount() > 0) { QString dlgTitle (m_matrix->name() + " column statistics"); StatisticsDialog* dlg = new StatisticsDialog(dlgTitle); - dlg->setAttribute(Qt::WA_DeleteOnClose); QList list; for (int col = 0; col < m_matrix->columnCount(); ++col) { if (isColumnSelected(col, false)) { QString headerString = m_tableView->model()->headerData(col, Qt::Horizontal).toString(); list << new Column(headerString, m_matrix->data().at(col)); } } dlg->setColumns(list); if (dlg->exec() == KDialog::Accepted) { qDeleteAll(list); list.clear(); } } } void MatrixView::showRowStatistics() { if (selectedRowCount() > 0) { QString dlgTitle (m_matrix->name() + " row statistics"); StatisticsDialog* dlg = new StatisticsDialog(dlgTitle); - dlg->setAttribute(Qt::WA_DeleteOnClose); - QList list; + QList list; for (int row = 0; row < m_matrix->rowCount(); ++row) { if (isRowSelected(row, false)) { QString headerString = m_tableView->model()->headerData(row, Qt::Vertical).toString(); list << new Column(headerString, m_matrix->rowCells(row, 0, m_matrix->columnCount()-1)); } } dlg->setColumns(list); if (dlg->exec() == KDialog::Accepted) { qDeleteAll(list); list.clear(); } } } void MatrixView::exportToFits(const QString &fileName, const int exportTo) const { FITSFilter* filter = new FITSFilter; filter->setExportTo(exportTo); filter->write(fileName, m_matrix); delete filter; } diff --git a/src/commonfrontend/spreadsheet/SpreadsheetView.cpp b/src/commonfrontend/spreadsheet/SpreadsheetView.cpp index 82d7fe892..ea094aaba 100644 --- a/src/commonfrontend/spreadsheet/SpreadsheetView.cpp +++ b/src/commonfrontend/spreadsheet/SpreadsheetView.cpp @@ -1,2273 +1,2267 @@ /*************************************************************************** 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(), m_tableView(new QTableView(this)), m_spreadsheet(spreadsheet), m_model( new SpreadsheetModel(spreadsheet) ), m_suppressSelectionChangedEvent(false) { 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; icount(); ++i) w += m_horizontalHeader->sectionSize(i); if (m_tableView->verticalHeader()->count()>50) 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*))); foreach(Column* 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; ichildren().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 = 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); //TODO // 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(); 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(); m_spreadsheetMenu->addAction(action_go_to_cell); 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(); 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); 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); 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->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); 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); menu->insertAction(firstAction, action_statistics_columns); action_sort_columns->setVisible(false); action_sort_asc_column->setVisible(true); action_sort_desc_column->setVisible(true); //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; rcolumn(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; } 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; } //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 new_data(rows); for (int i=0; icolumnMode() != AbstractColumn::Numeric) continue; col->replaceValues(0, new_data); } 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; } 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; } 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; } } 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->setAttribute(Qt::WA_DeleteOnClose); dlg->setColumns(selectedColumns()); dlg->exec(); } void SpreadsheetView::fillWithEquidistantValues() { if (selectedColumnCount() < 1) return; EquidistantValuesDialog* dlg = new EquidistantValuesDialog(m_spreadsheet); dlg->setAttribute(Qt::WA_DeleteOnClose); dlg->setColumns(selectedColumns()); dlg->exec(); } void SpreadsheetView::fillWithFunctionValues() { if (selectedColumnCount() < 1) return; FunctionValuesDialog* dlg = new FunctionValuesDialog(m_spreadsheet); dlg->setAttribute(Qt::WA_DeleteOnClose); 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 stringOk = false; double doubleValue = 0; QString stringValue; m_spreadsheet->beginMacro(i18n("%1: fill cells with const values", m_spreadsheet->name())); foreach(Column* 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); 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::Text: { if (!stringOk) stringValue = QInputDialog::getText(this, i18n("Fill the selection with constant value"), 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< Column* > list = selectedColumns(); if (formulaModeActive()) { foreach(Column* ptr, list) { ptr->setSuppressDataChangedSignal(true); ptr->clearFormulas(); ptr->setSuppressDataChangedSignal(false); ptr->setChanged(); } } else { foreach(Column* ptr, list) { ptr->setSuppressDataChangedSignal(true); ptr->clear(); ptr->setSuppressDataChangedSignal(false); ptr->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(); foreach(Column* column, columns) column->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())); foreach(Column* 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->setAttribute(Qt::WA_DeleteOnClose); dlg->setColumns(selectedColumns()); dlg->exec(); } void SpreadsheetView::maskColumnValues() { if (selectedColumnCount() < 1) return; DropValuesDialog* dlg = new DropValuesDialog(m_spreadsheet, true); - dlg->setAttribute(Qt::WA_DeleteOnClose); dlg->setColumns(selectedColumns()); dlg->exec(); } void SpreadsheetView::joinColumns() { } void SpreadsheetView::normalizeSelectedColumns() { WAIT_CURSOR; m_spreadsheet->beginMacro(i18n("%1: normalize columns", m_spreadsheet->name())); QList< Column* > cols = selectedColumns(); foreach(Column* 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; rowrowCount(); 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; rowrowCount(); 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() { - QList< Column* > cols = selectedColumns(); - sortDialog(cols); + 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; - dlg->setAttribute(Qt::WA_DeleteOnClose); 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); - dlg->setAttribute(Qt::WA_DeleteOnClose); 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(); - dlg->setAttribute(Qt::WA_DeleteOnClose); 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) { QStringList 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/kdefrontend/SettingsWorksheetPage.cpp b/src/kdefrontend/SettingsWorksheetPage.cpp index 5e3f80cae..255b785eb 100644 --- a/src/kdefrontend/SettingsWorksheetPage.cpp +++ b/src/kdefrontend/SettingsWorksheetPage.cpp @@ -1,148 +1,148 @@ /*************************************************************************** File : SettingsWorksheetPage.cpp Project : LabPlot Description : settings page for Worksheet -------------------------------------------------------------------- Copyright : (C) 2008-2017 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 "SettingsWorksheetPage.h" #include "tools/TeXRenderer.h" #include "kdefrontend/widgets/ThemesComboBox.h" #include #include #include /** * \brief Page for the 'General' settings of the Labplot settings dialog. */ SettingsWorksheetPage::SettingsWorksheetPage(QWidget* parent) : SettingsPage(parent), m_changed(false) { ui.setupUi(this); - cbThemes = new ThemesComboBox(); - ui.gridLayout->addWidget(cbThemes, 1, 4, 1, 1); + m_cbThemes = new ThemesComboBox(); + ui.gridLayout->addWidget(m_cbThemes, 1, 4, 1, 1); ui.lLatexWarning->setPixmap( QIcon::fromTheme(QLatin1String("state-warning")).pixmap(QSize(48,48)) ); //add available TeX typesetting engines if (TeXRenderer::executableExists(QLatin1String("lualatex"))) ui.cbTexEngine->addItem(QLatin1String("LuaLaTeX"), QLatin1String("lualatex")); if (TeXRenderer::executableExists(QLatin1String("xelatex"))) ui.cbTexEngine->addItem(QLatin1String("XeLaTex"), QLatin1String("xelatex")); if (TeXRenderer::executableExists(QLatin1String("pdflatex"))) ui.cbTexEngine->addItem(QLatin1String("pdfLaTeX"), QLatin1String("pdflatex")); if (TeXRenderer::executableExists(QLatin1String("latex"))) ui.cbTexEngine->addItem(QLatin1String("LaTeX"), QLatin1String("latex")); - connect(cbThemes, SIGNAL(currentThemeChanged(QString)), this, SLOT(changed()) ); + connect(m_cbThemes, SIGNAL(currentThemeChanged(QString)), this, SLOT(changed()) ); connect(ui.chkPresenterModeInteractive, SIGNAL(stateChanged(int)), this, SLOT(changed()) ); connect(ui.chkDoubleBuffering, SIGNAL(stateChanged(int)), this, SLOT(changed()) ); connect(ui.cbTexEngine, SIGNAL(currentIndexChanged(int)), this, SLOT(changed()) ); connect(ui.cbTexEngine, SIGNAL(currentIndexChanged(int)), this, SLOT(checkTeX(int)) ); loadSettings(); } void SettingsWorksheetPage::applySettings() { KConfigGroup group = KSharedConfig::openConfig()->group(QLatin1String("Settings_Worksheet")); - group.writeEntry(QLatin1String("Theme"), cbThemes->currentText()); + group.writeEntry(QLatin1String("Theme"), m_cbThemes->currentText()); group.writeEntry(QLatin1String("PresenterModeInteractive"), ui.chkPresenterModeInteractive->isChecked()); group.writeEntry(QLatin1String("DoubleBuffering"), ui.chkDoubleBuffering->isChecked()); group.writeEntry(QLatin1String("LaTeXEngine"), ui.cbTexEngine->itemData(ui.cbTexEngine->currentIndex())); } void SettingsWorksheetPage::restoreDefaults() { loadSettings(); } void SettingsWorksheetPage::loadSettings() { const KConfigGroup group = KSharedConfig::openConfig()->group(QLatin1String("Settings_Worksheet")); - cbThemes->setItemText(0, group.readEntry(QLatin1String("Theme"), "")); + m_cbThemes->setItemText(0, group.readEntry(QLatin1String("Theme"), "")); ui.chkPresenterModeInteractive->setChecked(group.readEntry(QLatin1String("PresenterModeInteractive"), false)); ui.chkDoubleBuffering->setChecked(group.readEntry(QLatin1String("DoubleBuffering"), true)); QString engine = group.readEntry(QLatin1String("LaTeXEngine"), ""); int index = -1; if (engine.isEmpty()) index = ui.cbTexEngine->findData(QLatin1String("xelatex")); else index = ui.cbTexEngine->findData(engine); ui.cbTexEngine->setCurrentIndex(index); } void SettingsWorksheetPage::changed() { m_changed = true; emit settingsChanged(); } /*! checks whether all tools required for latex typesetting are available. shows a warning if not. \sa TeXRenderer::active() */ void SettingsWorksheetPage::checkTeX(int engineIndex) { if (engineIndex == -1) { ui.lLatexWarning->show(); ui.lLatexWarning->setToolTip(i18n("No LaTeX installation found or selected. LaTeX typesetting not possible.")); return; } //engine found, check the precense of other required tools (s.a. TeXRenderer.cpp): //to convert the generated PDF/PS files to PNG we need 'convert' from the ImageMagic package if (!TeXRenderer::executableExists(QLatin1String("convert"))) { ui.lLatexWarning->show(); ui.lLatexWarning->setToolTip(i18n("No 'convert' found. LaTeX typesetting not possible.")); return; } QString engine = ui.cbTexEngine->itemData(engineIndex).toString(); if (engine=="latex") { //to convert the generated PS files to DVI we need 'dvips' if (!TeXRenderer::executableExists(QLatin1String("dvips"))) { ui.lLatexWarning->show(); ui.lLatexWarning->setToolTip(i18n("No 'dvips' found. LaTeX typesetting not possible.")); return; } } #if defined(_WIN64) if (!TeXRenderer::executableExists(QLatin1String("gswin64c"))) { ui.lLatexWarning->show(); ui.lLatexWarning->setToolTip(i18n("No Ghostscript found. LaTeX typesetting not possible.")); return; } #elif defined(HAVE_WINDOWS) if (!TeXRenderer::executableExists(QLatin1String("gswin32c"))) { ui.lLatexWarning->show(); ui.lLatexWarning->setToolTip(i18n("No Ghostscript found. LaTeX typesetting not possible.")); return; } #endif ui.lLatexWarning->hide(); } diff --git a/src/kdefrontend/SettingsWorksheetPage.h b/src/kdefrontend/SettingsWorksheetPage.h index 54f64665d..e8b9d7d89 100644 --- a/src/kdefrontend/SettingsWorksheetPage.h +++ b/src/kdefrontend/SettingsWorksheetPage.h @@ -1,62 +1,62 @@ /*************************************************************************** File : SettingsWorksheetPage.h Project : LabPlot Description : settings page for Worksheet -------------------------------------------------------------------- -------------------------------------------------------------------- Copyright : (C) 2016-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 * * * ***************************************************************************/ #ifndef SETTINGSWORKSHEETPAGE_H #define SETTINGSWORKSHEETPAGE_H #include "SettingsPage.h" #include "ui_settingsworksheetpage.h" class ThemesComboBox; class SettingsWorksheetPage : public SettingsPage { Q_OBJECT public: explicit SettingsWorksheetPage(QWidget*); virtual void applySettings(); virtual void restoreDefaults(); private: Ui::SettingsWorksheetPage ui; - ThemesComboBox* cbThemes; + ThemesComboBox* m_cbThemes; bool m_changed; void loadSettings(); private slots: void changed(); void checkTeX(int); signals: void settingsChanged(); }; #endif diff --git a/src/kdefrontend/TemplateHandler.cpp b/src/kdefrontend/TemplateHandler.cpp index 044754c51..a8079077e 100644 --- a/src/kdefrontend/TemplateHandler.cpp +++ b/src/kdefrontend/TemplateHandler.cpp @@ -1,219 +1,219 @@ /*************************************************************************** File : TemplateHandler.cpp Project : LabPlot Description : Widget for handling saving and loading of templates -------------------------------------------------------------------- Copyright : (C) 2012 by Stefan Gerlach (stefan.gerlach@uni.kn) Copyright : (C) 2012-2016 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 "TemplateHandler.h" #include #include #include #include #include #include #include #include #include #include #include /*! \class TemplateHandler \brief Provides a widget with buttons for saving and loading of templates. Emits \c loadConfig() and \c saveConfig() signals that have to be connected to the appropriate slots in the ui (mostly in the dock widgets) \ingroup kdefrontend */ TemplateHandler::TemplateHandler(QWidget *parent, ClassName name): QWidget(parent){ QHBoxLayout* horizontalLayout = new QHBoxLayout(this); horizontalLayout->setSpacing(0); horizontalLayout->setMargin(0); QSpacerItem* horizontalSpacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); horizontalLayout->addItem(horizontalSpacer); int size = KIconLoader::global()->currentSize(KIconLoader::MainToolbar); - tbLoad = new QToolButton(this); - tbLoad->setIconSize(QSize(size, size)); - horizontalLayout->addWidget(tbLoad); + m_tbLoad = new QToolButton(this); + m_tbLoad->setIconSize(QSize(size, size)); + horizontalLayout->addWidget(m_tbLoad); - tbSave = new QToolButton(this); - tbSave->setIconSize(QSize(size, size)); - horizontalLayout->addWidget(tbSave); + m_tbSave = new QToolButton(this); + m_tbSave->setIconSize(QSize(size, size)); + horizontalLayout->addWidget(m_tbSave); - tbSaveDefault = new QToolButton(this); - tbSaveDefault->setIconSize(QSize(size, size)); - horizontalLayout->addWidget(tbSaveDefault); + m_tbSaveDefault = new QToolButton(this); + m_tbSaveDefault->setIconSize(QSize(size, size)); + horizontalLayout->addWidget(m_tbSaveDefault); // QSpacerItem* horizontalSpacer2 = new QSpacerItem(10, 20, QSizePolicy::Fixed, QSizePolicy::Minimum); // horizontalLayout->addItem(horizontalSpacer2); - tbCopy = new QToolButton(this); - tbCopy->setIconSize(QSize(size, size)); - tbCopy->setEnabled(false); - horizontalLayout->addWidget(tbCopy); + m_tbCopy = new QToolButton(this); + m_tbCopy->setIconSize(QSize(size, size)); + m_tbCopy->setEnabled(false); + horizontalLayout->addWidget(m_tbCopy); - tbPaste = new QToolButton(this); - tbPaste->setIconSize(QSize(size, size)); - tbPaste->setEnabled(false); - horizontalLayout->addWidget(tbPaste); + m_tbPaste = new QToolButton(this); + m_tbPaste->setIconSize(QSize(size, size)); + m_tbPaste->setEnabled(false); + horizontalLayout->addWidget(m_tbPaste); - tbLoad->setIcon(QIcon::fromTheme("document-open")); - tbSave->setIcon(QIcon::fromTheme("document-save")); - tbSaveDefault->setIcon(QIcon::fromTheme("document-save-as")); - tbCopy->setIcon(QIcon::fromTheme("edit-copy")); - tbPaste->setIcon(QIcon::fromTheme("edit-paste")); + m_tbLoad->setIcon(QIcon::fromTheme("document-open")); + m_tbSave->setIcon(QIcon::fromTheme("document-save")); + m_tbSaveDefault->setIcon(QIcon::fromTheme("document-save-as")); + m_tbCopy->setIcon(QIcon::fromTheme("edit-copy")); + m_tbPaste->setIcon(QIcon::fromTheme("edit-paste")); - connect( tbLoad, SIGNAL(clicked()), this, SLOT(loadMenu())); - connect( tbSave, SIGNAL(clicked()), this, SLOT(saveMenu())); - connect( tbSaveDefault, SIGNAL(clicked()), this, SLOT(saveDefaults())); + connect( m_tbLoad, SIGNAL(clicked()), this, SLOT(loadMenu())); + connect( m_tbSave, SIGNAL(clicked()), this, SLOT(saveMenu())); + connect( m_tbSaveDefault, SIGNAL(clicked()), this, SLOT(saveDefaults())); - className = name; + m_className = name; //synchronize this with the ordering in TemplateHandler::ClassName - dirNames<<"spreadsheet"<<"matrix"<<"worksheet"<<"cartesianplot"<<"cartesianplotlegend"<<"xycurve"<<"axis"<<"custompoint"; + m_dirNames <<"spreadsheet"<<"matrix"<<"worksheet"<<"cartesianplot"<<"cartesianplotlegend"<<"xycurve"<<"axis"<<"custompoint"; this->retranslateUi(); //disable the load-button if no templates are available yet - QStringList list = QStandardPaths::locateAll(QStandardPaths::ApplicationsLocation, "templates/" + dirNames.at(className) + "/*"); - tbLoad->setEnabled(list.size()); + QStringList list = QStandardPaths::locateAll(QStandardPaths::ApplicationsLocation, "templates/" + m_dirNames.at(m_className) + "/*"); + m_tbLoad->setEnabled(list.size()); //TODO: implement copy&paste of properties and activate copy- and paste-buttons again - tbCopy->hide(); - tbPaste->hide(); + m_tbCopy->hide(); + m_tbPaste->hide(); } void TemplateHandler::retranslateUi(){ - tbLoad->setToolTip(i18n("Load properties from a template")); - tbSave->setToolTip(i18n("Save current properties as a template")); - tbSaveDefault->setToolTip(i18n("Save current properties as default")); - tbCopy->setToolTip(i18n("Copy properties")); - tbPaste->setToolTip(i18n("Paste properties")); + m_tbLoad->setToolTip(i18n("Load properties from a template")); + m_tbSave->setToolTip(i18n("Save current properties as a template")); + m_tbSaveDefault->setToolTip(i18n("Save current properties as default")); + m_tbCopy->setToolTip(i18n("Copy properties")); + m_tbPaste->setToolTip(i18n("Paste properties")); } //############################################################################## //################################## Slots #################################### //############################################################################## void TemplateHandler::loadMenu() { QMenu menu; menu.addSection(i18n("Load from")); - QStringList list = QStandardPaths::locateAll(QStandardPaths::ApplicationsLocation, "templates/" + dirNames.at(className) + "/*"); + QStringList list = QStandardPaths::locateAll(QStandardPaths::ApplicationsLocation, "templates/" + m_dirNames.at(m_className) + "/*"); for (int i = 0; i < list.size(); ++i) { QFileInfo fileinfo(list.at(i)); QAction* action = menu.addAction(fileinfo.fileName()); action->setData(QVariant(list.at(i))); } connect(&menu, SIGNAL(triggered(QAction*)), this, SLOT(loadMenuSelected(QAction*))); - QPoint pos(-menu.sizeHint().width()+tbLoad->width(),-menu.sizeHint().height()); - menu.exec(tbLoad->mapToGlobal(pos)); + QPoint pos(-menu.sizeHint().width()+m_tbLoad->width(),-menu.sizeHint().height()); + menu.exec(m_tbLoad->mapToGlobal(pos)); } void TemplateHandler::loadMenuSelected(QAction* action) { KConfig config(action->data().toString(), KConfig::SimpleConfig); emit (loadConfigRequested(config)); emit info( i18n("Template \"%1\" was loaded.", action->text().remove('&')) ); } void TemplateHandler::saveMenu() { QMenu menu; menu.addSection(i18n("Save as")); - QStringList list = QStandardPaths::locateAll(QStandardPaths::ApplicationsLocation, "templates/"+ dirNames.at(className) + "/*"); + QStringList list = QStandardPaths::locateAll(QStandardPaths::ApplicationsLocation, "templates/"+ m_dirNames.at(m_className) + "/*"); for (int i = 0; i < list.size(); ++i) { QFileInfo fileinfo(list.at(i)); QAction* action = menu.addAction(fileinfo.fileName()); menu.addAction(action); action->setShortcut(QKeySequence()); } connect(&menu, SIGNAL(triggered(QAction*)), this, SLOT(saveMenuSelected(QAction*))); // add editable action QWidgetAction* widgetAction = new QWidgetAction(this); QFrame* frame = new QFrame(this); QHBoxLayout* layout = new QHBoxLayout(frame); QLabel* label = new QLabel(i18n("new:"), frame); layout->addWidget(label); KLineEdit* leFilename = new KLineEdit("", frame); layout->addWidget(leFilename); connect(leFilename, SIGNAL(returnPressed(QString)), this, SLOT(saveNewSelected(QString))); connect(leFilename, SIGNAL(returnPressed(QString)), &menu, SLOT(close())); widgetAction->setDefaultWidget(frame); menu.addAction(widgetAction); - QPoint pos(-menu.sizeHint().width()+tbSave->width(),-menu.sizeHint().height()); - menu.exec(tbSave->mapToGlobal(pos)); + QPoint pos(-menu.sizeHint().width()+m_tbSave->width(),-menu.sizeHint().height()); + menu.exec(m_tbSave->mapToGlobal(pos)); //TODO: focus is not set. why? leFilename->setFocus(); } /*! * Is called when the current properties are going to be saved as a new template. * Emits \c saveConfigRequested, the receiver of the signal has to config.sync(). */ void TemplateHandler::saveNewSelected(const QString& filename) { - KConfig config(QStandardPaths::locate(QStandardPaths::ApplicationsLocation, "templates") + '/' + dirNames.at(className) + '/' + filename, KConfig::SimpleConfig); + KConfig config(QStandardPaths::locate(QStandardPaths::ApplicationsLocation, "templates") + '/' + m_dirNames.at(m_className) + '/' + filename, KConfig::SimpleConfig); emit (saveConfigRequested(config)); //we have at least one saved template now -> enable the load button - tbLoad->setEnabled(true); + m_tbLoad->setEnabled(true); emit info( i18n("New template \"%1\" was saved.", filename) ); } /*! * Is called when the current properties are going to be saved in an already available template. * Emits \c saveConfigRequested, the receiver of the signal has to config.sync(). */ void TemplateHandler::saveMenuSelected(QAction* action) { KConfig config(action->data().toString()+'/'+action->text(), KConfig::SimpleConfig); emit (saveConfigRequested(config)); emit info( i18n("Template \"%1\" was saved.", action->text()) ); } /*! * Is called when the current properties are going to be saved as new default properties. * Emits \c saveConfigRequested, the receiver of the signal has to config.sync(). */ void TemplateHandler::saveDefaults() { KConfig config; emit (saveConfigRequested(config)); emit info( i18n("New default template was saved.") ); } diff --git a/src/kdefrontend/TemplateHandler.h b/src/kdefrontend/TemplateHandler.h index 4ec8b2b2a..2c7afada3 100644 --- a/src/kdefrontend/TemplateHandler.h +++ b/src/kdefrontend/TemplateHandler.h @@ -1,72 +1,72 @@ /*************************************************************************** File : TemplateHandler.h Project : LabPlot Description : Widget for handling saving and loading of templates -------------------------------------------------------------------- Copyright : (C) 2012 by Stefan Gerlach (stefan.gerlach@uni-konstanz.de) Copyright : (C) 2012-2016 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 TEMPLATEHANDLER_H #define TEMPLATEHANDLER_H #include class QToolButton; class KConfig; class TemplateHandler : public QWidget { Q_OBJECT public: enum ClassName {Spreadsheet, Matrix, Worksheet, CartesianPlot, CartesianPlotLegend, XYCurve, Axis, CustomPoint}; TemplateHandler(QWidget* parent, ClassName); private: void retranslateUi(); - ClassName className; - QList dirNames; + ClassName m_className; + QList m_dirNames; - QToolButton* tbLoad; - QToolButton* tbSave; - QToolButton* tbSaveDefault; - QToolButton* tbCopy; - QToolButton* tbPaste; + QToolButton* m_tbLoad; + QToolButton* m_tbSave; + QToolButton* m_tbSaveDefault; + QToolButton* m_tbCopy; + QToolButton* m_tbPaste; private slots: void loadMenu(); void saveMenu(); void loadMenuSelected(QAction*); void saveMenuSelected(QAction*); void saveNewSelected(const QString&); void saveDefaults(); signals: void loadConfigRequested(KConfig&); void saveConfigRequested(KConfig&); void info(const QString&); }; #endif diff --git a/src/kdefrontend/ThemeHandler.cpp b/src/kdefrontend/ThemeHandler.cpp index f9475fb58..32864913e 100644 --- a/src/kdefrontend/ThemeHandler.cpp +++ b/src/kdefrontend/ThemeHandler.cpp @@ -1,237 +1,237 @@ /*************************************************************************** File : ThemeHandler.cpp Project : LabPlot Description : Widget for handling saving and loading of themes -------------------------------------------------------------------- Copyright : (C) 2016 Prakriti Bhardwaj (p_bhardwaj14@informatik.uni-kl.de) Copyright : (C) 2016-2017 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 "ThemeHandler.h" #include "widgets/ThemesWidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include // #include #include /*! \class ThemeHandler \brief Provides a widget with buttons for loading of themes. Emits \c loadConfig() signal that have to be connected to the appropriate slots in the backend (plot widgets) \ingroup kdefrontend */ ThemeHandler::ThemeHandler(QWidget* parent) : QWidget(parent) { QHBoxLayout* horizontalLayout = new QHBoxLayout(this); horizontalLayout->setSpacing(0); horizontalLayout->setMargin(0); - pbLoadTheme = new QPushButton(this); - horizontalLayout->addWidget(pbLoadTheme); - pbLoadTheme->setText(i18n("Apply theme")); + m_pbLoadTheme = new QPushButton(this); + horizontalLayout->addWidget(m_pbLoadTheme); + m_pbLoadTheme->setText(i18n("Apply theme")); // pbSaveTheme = new QPushButton(this); // horizontalLayout->addWidget(pbSaveTheme); // pbSaveTheme->setText(i18n("Save theme")); /* pbPublishTheme = new QPushButton(this); horizontalLayout->addWidget(pbPublishTheme); pbPublishTheme->setText("Publish theme"); pbPublishTheme->setEnabled(false); */ - connect( pbLoadTheme, SIGNAL(clicked()), this, SLOT(showPanel())); + connect( m_pbLoadTheme, SIGNAL(clicked()), this, SLOT(showPanel())); // connect( pbSaveTheme, SIGNAL(clicked()), this, SLOT(saveMenu())); // connect( pbPublishTheme, SIGNAL(clicked()), this, SLOT(publishThemes())); //find all available themes files (system wide and user specific local files) //the list m_themeList contains full paths (path + file name) QStringList dirs = QStandardPaths::locateAll(QStandardPaths::DataLocation, "themes", QStandardPaths::LocateDirectory); foreach (const QString& dir, dirs) { QDirIterator it(dir, QStringList() << QStringLiteral("*"), QDir::Files); while (it.hasNext()) m_themeList.append(it.next()); } - pbLoadTheme->setEnabled(!m_themeList.isEmpty()); + m_pbLoadTheme->setEnabled(!m_themeList.isEmpty()); } void ThemeHandler::setCurrentTheme(const QString& name) { if (!name.isEmpty()) { - pbLoadTheme->setText(i18n("Apply theme [active '%1']").arg(name)); - pbLoadTheme->setToolTip(i18n("Theme '%1' is active. Click on the button to change the theme.").arg(name)); + m_pbLoadTheme->setText(i18n("Apply theme [active '%1']").arg(name)); + m_pbLoadTheme->setToolTip(i18n("Theme '%1' is active. Click on the button to change the theme.").arg(name)); } else { - pbLoadTheme->setText(i18n("Apply theme")); - pbLoadTheme->setToolTip(i18n("No theme is active. Click on the button to select a theme.")); + m_pbLoadTheme->setText(i18n("Apply theme")); + m_pbLoadTheme->setToolTip(i18n("No theme is active. Click on the button to select a theme.")); } m_currentTheme = name; } void ThemeHandler::loadSelected(QString name) { emit (loadThemeRequested(name)); this->setCurrentTheme(name); if (!name.isEmpty()) emit info( i18n("Theme \"%1\" was loaded.", name) ); else emit info( i18n("Theming deactivated.") ); //in case a local theme file was loaded (we have write access), allow to publish it //TODO: activate this later // if (KStandardDirs::checkAccess(themeFilePath, W_OK)) { // pbPublishTheme->setEnabled(true); // m_currentLocalTheme = themeFilePath.right(themeFilePath.length() - themeFilePath.lastIndexOf(QDir::separator()) - 1); // } else { // pbPublishTheme->setEnabled(false); // m_currentLocalTheme.clear(); // } } QStringList ThemeHandler::themes() { QStringList dirs = QStandardPaths::locateAll(QStandardPaths::DataLocation, "themes", QStandardPaths::LocateDirectory); QStringList pathList; foreach (const QString& dir, dirs) { QDirIterator it(dir, QStringList() << QStringLiteral("*"), QDir::Files); while (it.hasNext()) pathList.append(it.next()); } QStringList themeList; for (int i = 0; i < pathList.size(); ++i) { QFileInfo fileinfo(pathList.at(i)); themeList.append(fileinfo.fileName().split('.').at(0)); } return themeList; } const QString ThemeHandler::themeFilePath(const QString& name) { QStringList dirs = QStandardPaths::locateAll(QStandardPaths::DataLocation, "themes", QStandardPaths::LocateDirectory); QStringList themes; foreach (const QString& dir, dirs) { QDirIterator it(dir, QStringList() << QStringLiteral("*"), QDir::Files); while (it.hasNext()) themes.append(it.next()); } for (int i = 0; i < themes.size(); ++i) { if (themes.at(i).indexOf(name) != -1) return themes.at(i); } return QString(); } void ThemeHandler::showPanel() { QMenu menu; ThemesWidget themeWidget(&menu); connect(&themeWidget, SIGNAL(themeSelected(QString)), this, SLOT(loadSelected(QString))); connect(&themeWidget, SIGNAL(themeSelected(QString)), &menu, SLOT(close())); connect(&themeWidget, SIGNAL(canceled()), &menu, SLOT(close())); QWidgetAction* widgetAction = new QWidgetAction(this); widgetAction->setDefaultWidget(&themeWidget); menu.addAction(widgetAction); - QPoint pos(-menu.sizeHint().width()+pbLoadTheme->width(),-menu.sizeHint().height()); + QPoint pos(-menu.sizeHint().width()+m_pbLoadTheme->width(),-menu.sizeHint().height()); menu.setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred); - menu.exec(pbLoadTheme->mapToGlobal(pos)); + menu.exec(m_pbLoadTheme->mapToGlobal(pos)); } void ThemeHandler::saveMenu() { QMenu menu; menu.addSection(i18n("Save as")); // add editable action QWidgetAction* widgetAction = new QWidgetAction(this); QFrame* frame = new QFrame(this); QHBoxLayout* layout = new QHBoxLayout(frame); QLabel* label = new QLabel(i18n("Enter name:"), frame); layout->addWidget(label); KLineEdit* leFilename = new KLineEdit("", frame); layout->addWidget(leFilename); connect(leFilename, SIGNAL(returnPressed(QString)), this, SLOT(saveNewSelected(QString))); connect(leFilename, SIGNAL(returnPressed(QString)), &menu, SLOT(close())); widgetAction->setDefaultWidget(frame); menu.addAction(widgetAction); - QPoint pos(-menu.sizeHint().width() + pbSaveTheme->width(), -menu.sizeHint().height()); - menu.exec(pbSaveTheme->mapToGlobal(pos)); + QPoint pos(-menu.sizeHint().width() + m_pbSaveTheme->width(), -menu.sizeHint().height()); + menu.exec(m_pbSaveTheme->mapToGlobal(pos)); leFilename->setFocus(); } void ThemeHandler::saveNewSelected(const QString& filename) { KConfig config(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + '/' + "themes" + '/' + filename, KConfig::SimpleConfig); emit (saveThemeRequested(config)); emit info( i18n("New theme \"%1\" was saved.", filename) ); m_currentLocalTheme = filename; m_themeList.append(config.name()); //enable the publish button so the newly created theme can be published //TODO: enable this later // pbPublishTheme->setEnabled(true); } /*! opens the dialog to upload the currently selected local theme. The publish button is only enabled if a local theme was loaded or one of the themes was modified and saved locally. */ // void ThemeHandler::publishThemes() { // int ret = KMessageBox::questionYesNo(this, // i18n("Do you want to upload your theme %1 to public web server?").arg(m_currentLocalTheme), // i18n("Publish Theme")); // if (ret != KMessageBox::Yes) // return; // // // creating upload dialog // KNS3::UploadDialog dialog("labplot2_themes.knsrc", this); // dialog.setUploadFile(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + '/' + "themes" + '/' + m_currentLocalTheme); // dialog.setUploadName(m_currentLocalTheme); // //dialog.setDescription(); TODO: allow the user to provide a short description for the theme to be uploaded // dialog.exec(); // } diff --git a/src/kdefrontend/ThemeHandler.h b/src/kdefrontend/ThemeHandler.h index 3933c3ab6..c32a369b4 100644 --- a/src/kdefrontend/ThemeHandler.h +++ b/src/kdefrontend/ThemeHandler.h @@ -1,73 +1,73 @@ /*************************************************************************** File : ThemeHandler.h Project : LabPlot Description : Widget for handling saving and loading of themes -------------------------------------------------------------------- Copyright : (C) 2016 Prakriti Bhardwaj (p_bhardwaj14@informatik.uni-kl.de) Copyright : (C) 2016 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 THEMEHANDLER_H #define THEMEHANDLER_H #include class QPushButton; class KConfig; class ThemeHandler : public QWidget { Q_OBJECT public: explicit ThemeHandler(QWidget*); static QStringList themes(); static const QString themeFilePath(const QString&); public slots: void setCurrentTheme(const QString&); private: - QList dirNames; + QList m_dirNames; QStringList m_themeList; QString m_currentTheme; QString m_currentLocalTheme; - QPushButton* pbLoadTheme; - QPushButton* pbSaveTheme; + QPushButton* m_pbLoadTheme; + QPushButton* m_pbSaveTheme; // QPushButton* pbPublishTheme; private slots: void loadSelected(QString); void showPanel(); void saveMenu(); void saveNewSelected(const QString&); // void publishThemes(); signals: void loadThemeRequested(const QString&); void saveThemeRequested(KConfig&); void info(const QString&); void loadPreviewPanel(QStringList, QString); }; #endif diff --git a/src/kdefrontend/spreadsheet/DropValuesDialog.cpp b/src/kdefrontend/spreadsheet/DropValuesDialog.cpp index 6974c19fa..2982f36d5 100644 --- a/src/kdefrontend/spreadsheet/DropValuesDialog.cpp +++ b/src/kdefrontend/spreadsheet/DropValuesDialog.cpp @@ -1,338 +1,338 @@ /*************************************************************************** File : DropValuesDialog.cpp Project : LabPlot Description : Dialog for droping and masking values in columns -------------------------------------------------------------------- Copyright : (C) 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 * * * ***************************************************************************/ #include "DropValuesDialog.h" #include "backend/core/column/Column.h" #include "backend/lib/macros.h" #include "backend/spreadsheet/Spreadsheet.h" #include #include #include /*! \class DropValuesDialog \brief Dialog for generating values from a mathematical function. \ingroup kdefrontend */ DropValuesDialog::DropValuesDialog(Spreadsheet* s, bool mask, QWidget* parent, Qt::WFlags fl) : KDialog(parent, fl), m_spreadsheet(s), m_mask(mask) { setWindowTitle(i18n("Drop values")); QFrame* mainWidget = new QFrame(this); ui.setupUi(mainWidget); setMainWidget( mainWidget ); - + setAttribute(Qt::WA_DeleteOnClose); ui.cbOperator->addItem(i18n("equal to")); ui.cbOperator->addItem(i18n("between (including end points)")); ui.cbOperator->addItem(i18n("between (excluding end points)")); ui.cbOperator->addItem(i18n("greater then")); ui.cbOperator->addItem(i18n("greater then or equal to")); ui.cbOperator->addItem(i18n("lesser then")); ui.cbOperator->addItem(i18n("lesser then or equal to")); ui.leValue1->setValidator( new QDoubleValidator(ui.leValue1) ); ui.leValue2->setValidator( new QDoubleValidator(ui.leValue2) ); setButtons( KDialog::Ok | KDialog::Cancel ); if (m_mask) { setButtonText(KDialog::Ok, i18n("&Mask")); setButtonToolTip(KDialog::Ok, i18n("Mask values in the specified region")); ui.lMode->setText(i18n("Mask values")); setWindowTitle(i18n("Mask values")); } else { setButtonText(KDialog::Ok, i18n("&Drop")); setButtonToolTip(KDialog::Ok, i18n("Drop values in the specified region")); } connect( ui.cbOperator, SIGNAL(currentIndexChanged(int)), this, SLOT(operatorChanged(int)) ); connect(this, SIGNAL(okClicked()), this, SLOT(okClicked())); resize( QSize(400,0).expandedTo(minimumSize()) ); operatorChanged(0); } void DropValuesDialog::setColumns(QList list) { m_columns = list; } void DropValuesDialog::operatorChanged(int index) const { bool value2 = (index==1) || (index==2); ui.lMin->setVisible(value2); ui.lMax->setVisible(value2); ui.lAnd->setVisible(value2); ui.leValue2->setVisible(value2); } void DropValuesDialog::okClicked() const { if (m_mask) maskValues(); else dropValues(); } //TODO: m_column->setMasked() is slow, we need direct access to the masked-container -> redesign class MaskValuesTask : public QRunnable { public: MaskValuesTask(Column* col, int op, double value1, double value2){ m_column = col; m_operator = op; m_value1 = value1; m_value2 = value2; }; void run() { m_column->setSuppressDataChangedSignal(true); bool changed = false; QVector* data = static_cast* >(m_column->data()); //equal to if (m_operator == 0) { for (int i=0; isize(); ++i) { if (data->at(i) == m_value1) { m_column->setMasked(i, true); changed = true; } } } //between (including end points) else if (m_operator == 1) { for (int i=0; isize(); ++i) { if (data->at(i) >= m_value1 && data->at(i) <= m_value2) { m_column->setMasked(i, true); changed = true; } } } //between (excluding end points) else if (m_operator == 2) { for (int i=0; isize(); ++i) { if (data->at(i) > m_value1 && data->at(i) < m_value2) { m_column->setMasked(i, true); changed = true; } } } //greater then else if (m_operator == 3) { for (int i=0; isize(); ++i) { if (data->at(i) > m_value1) { m_column->setMasked(i, true); changed = true; } } } //greater then or equal to else if (m_operator == 4) { for (int i=0; isize(); ++i) { if (data->at(i) >= m_value1) { m_column->setMasked(i, true); changed = true; } } } //lesser then else if (m_operator == 5) { for (int i=0; isize(); ++i) { if (data->at(i) < m_value1) { m_column->setMasked(i, true); changed = true; } } } //lesser then or equal to else if (m_operator == 6) { for (int i=0; isize(); ++i) { if (data->at(i) <= m_value1) { m_column->setMasked(i, true); changed = true; } } } m_column->setSuppressDataChangedSignal(false); if (changed) m_column->setChanged(); } private: Column* m_column; int m_operator; double m_value1; double m_value2; }; class DropValuesTask : public QRunnable { public: DropValuesTask(Column* col, int op, double value1, double value2){ m_column = col; m_operator = op; m_value1 = value1; m_value2 = value2; }; void run() { bool changed = false; QVector* data = static_cast* >(m_column->data()); QVector new_data(*data); //equal to if (m_operator == 0) { for (int i=0; i= m_value1 && new_data[i] <= m_value2) { new_data[i] = NAN; changed = true; } } } //between (excluding end points) else if (m_operator == 2) { for (int i=0; i m_value1 && new_data[i] < m_value2) { new_data[i] = NAN; changed = true; } } } //greater then else if (m_operator == 3) { for (int i=0; i m_value1) { new_data[i] = NAN; changed = true; } } } //greater then or equal to else if (m_operator == 4) { for (int i=0; i= m_value1) { new_data[i] = NAN; changed = true; } } } //lesser then else if (m_operator == 5) { for (int i=0; ireplaceValues(0, new_data); } private: Column* m_column; int m_operator; double m_value1; double m_value2; }; void DropValuesDialog::maskValues() const { Q_ASSERT(m_spreadsheet); WAIT_CURSOR; m_spreadsheet->beginMacro(i18n("%1: mask values", m_spreadsheet->name())); const int op = ui.cbOperator->currentIndex(); const double value1 = ui.leValue1->text().toDouble(); const double value2 = ui.leValue2->text().toDouble(); foreach(Column* col, m_columns) { MaskValuesTask* task = new MaskValuesTask(col, op, value1, value2); task->run(); //TODO: writing to the undo-stack in Column::setMasked() is not tread-safe -> redesign // QThreadPool::globalInstance()->start(task); } //wait until all columns were processed // QThreadPool::globalInstance()->waitForDone(); m_spreadsheet->endMacro(); RESET_CURSOR; } void DropValuesDialog::dropValues() const { Q_ASSERT(m_spreadsheet); WAIT_CURSOR; m_spreadsheet->beginMacro(i18n("%1: drop values", m_spreadsheet->name())); const int op = ui.cbOperator->currentIndex(); const double value1 = ui.leValue1->text().toDouble(); const double value2 = ui.leValue2->text().toDouble(); foreach(Column* col, m_columns) { DropValuesTask* task = new DropValuesTask(col, op, value1, value2); QThreadPool::globalInstance()->start(task); } //wait until all columns were processed QThreadPool::globalInstance()->waitForDone(); m_spreadsheet->endMacro(); RESET_CURSOR; } diff --git a/src/kdefrontend/spreadsheet/SortDialog.cpp b/src/kdefrontend/spreadsheet/SortDialog.cpp index 851993d86..d02ef80f3 100644 --- a/src/kdefrontend/spreadsheet/SortDialog.cpp +++ b/src/kdefrontend/spreadsheet/SortDialog.cpp @@ -1,131 +1,129 @@ /*************************************************************************** File : SortDialog.h Project : LabPlot Description : Sorting options dialog -------------------------------------------------------------------- Copyright : (C) 2011 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 "SortDialog.h" #include #include #include #include #include #include #include #include #include /*! \class SortDialog \brief Dialog for sorting the columns in a spreadsheet. \ingroup kdefrontend */ SortDialog::SortDialog( QWidget* parent, Qt::WFlags fl ) : QDialog( parent, fl ){ setWindowIcon(QIcon::fromTheme("view-sort-ascending")); setWindowTitle(i18n("Sort columns")); setSizeGripEnabled(true); + setAttribute(Qt::WA_DeleteOnClose); QGroupBox* widget = new QGroupBox(i18n("Options")); QGridLayout* layout = new QGridLayout(widget); layout->setSpacing(4); layout->setContentsMargins(4,4,4,4); layout->addWidget( new QLabel( i18n("Order")), 0, 0 ); - cbOrdering = new QComboBox(); - cbOrdering->addItem(QIcon::fromTheme("view-sort-ascending"), i18n("Ascending")); - cbOrdering->addItem(QIcon::fromTheme("view-sort-descending"), i18n("Descending")); - layout->addWidget(cbOrdering, 0, 1 ); + m_cbOrdering = new QComboBox(); + m_cbOrdering->addItem(QIcon::fromTheme("view-sort-ascending"), i18n("Ascending")); + m_cbOrdering->addItem(QIcon::fromTheme("view-sort-descending"), i18n("Descending")); + layout->addWidget(m_cbOrdering, 0, 1 ); - lblType = new QLabel(i18n("Sort columns")); - layout->addWidget( lblType, 1, 0 ); - cbType = new QComboBox(); - cbType->addItem(i18n("Separately")); - cbType->addItem(i18n("Together")); - layout->addWidget(cbType, 1, 1 ); - cbType->setCurrentIndex(Together); - - lblColumns = new QLabel(i18n("Leading column")); - layout->addWidget( lblColumns, 2, 0 ); - cbColumns = new QComboBox(); - layout->addWidget(cbColumns, 2, 1); + m_lType = new QLabel(i18n("Sort columns")); + layout->addWidget( m_lType, 1, 0 ); + m_cbType = new QComboBox(); + m_cbType->addItem(i18n("Separately")); + m_cbType->addItem(i18n("Together")); + layout->addWidget(m_cbType, 1, 1 ); + m_cbType->setCurrentIndex(Together); + + m_lColumns = new QLabel(i18n("Leading column")); + layout->addWidget( m_lColumns, 2, 0 ); + m_cbColumns = new QComboBox(); + layout->addWidget(m_cbColumns, 2, 1); layout->setRowStretch(3, 1); - QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); buttonBox->button(QDialogButtonBox::Ok)->setText(i18n("Sort")); connect(buttonBox, SIGNAL(accepted()), this, SLOT(sort())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); layout->addWidget(buttonBox); setLayout(layout); - connect( cbType, SIGNAL(currentIndexChanged(int)), this, SLOT(changeType(int))); + connect(m_cbType, SIGNAL(currentIndexChanged(int)), this, SLOT(changeType(int))); this->resize(400,0); } void SortDialog::sort(){ Column* leading; - if(cbType->currentIndex() == Together) - leading = m_columns_list.at(cbColumns->currentIndex()); + if(m_cbType->currentIndex() == Together) + leading = m_columns_list.at(m_cbColumns->currentIndex()); else leading = 0; - emit sort(leading, m_columns_list, cbOrdering->currentIndex() == Ascending ); - - accepted(); + emit sort(leading, m_columns_list, m_cbOrdering->currentIndex() == Ascending ); } void SortDialog::setColumnsList(QList list){ m_columns_list = list; for(int i=0; iaddItem( list.at(i)->name() ); + m_cbColumns->addItem( list.at(i)->name() ); - cbColumns->setCurrentIndex(0); + m_cbColumns->setCurrentIndex(0); if (list.size() == 1){ - lblType->hide(); - cbType->hide(); - lblColumns->hide(); - cbColumns->hide(); + m_lType->hide(); + m_cbType->hide(); + m_lColumns->hide(); + m_cbColumns->hide(); } } void SortDialog::changeType(int Type){ if(Type == Together) - cbColumns->setEnabled(true); + m_cbColumns->setEnabled(true); else - cbColumns->setEnabled(false); + m_cbColumns->setEnabled(false); } diff --git a/src/kdefrontend/spreadsheet/SortDialog.h b/src/kdefrontend/spreadsheet/SortDialog.h index 1a8233d3d..36806034b 100644 --- a/src/kdefrontend/spreadsheet/SortDialog.h +++ b/src/kdefrontend/spreadsheet/SortDialog.h @@ -1,65 +1,65 @@ /*************************************************************************** File : SortDialog.h Project : LabPlot Description : Sorting options dialog -------------------------------------------------------------------- Copyright : (C) 2011 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 SORTDIALOG_H #define SORTDIALOG_H #include "backend/core/column/Column.h" #include class QPushButton; class QComboBox; class QLabel; class SortDialog : public QDialog{ Q_OBJECT public: explicit SortDialog( QWidget* parent = 0, Qt::WFlags fl = 0 ); void setColumnsList(QList list); enum { Separately=0, Together=1 }; enum { Ascending=0, Descending=1 }; private slots: void sort(); void changeType(int index); signals: void sort(Column *leading, QList cols, bool ascending); private: QList m_columns_list; - QComboBox* cbOrdering; - QLabel* lblType; - QComboBox* cbType; - QLabel* lblColumns; - QComboBox* cbColumns; + QComboBox* m_cbOrdering; + QLabel* m_lType; + QComboBox* m_cbType; + QLabel* m_lColumns; + QComboBox* m_cbColumns; }; #endif diff --git a/src/kdefrontend/spreadsheet/StatisticsDialog.cpp b/src/kdefrontend/spreadsheet/StatisticsDialog.cpp index cb1a73e6c..d2f8bb0c5 100644 --- a/src/kdefrontend/spreadsheet/StatisticsDialog.cpp +++ b/src/kdefrontend/spreadsheet/StatisticsDialog.cpp @@ -1,219 +1,220 @@ /*************************************************************************** File : StatisticsDialog.cpp Project : LabPlot Description : Dialog showing statistics for column values -------------------------------------------------------------------- Copyright : (C) 2016 by Fabian Kristof (fkristofszabolcs@gmail.com)) Copyright : (C) 2016-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 "StatisticsDialog.h" #include "backend/core/column/Column.h" #include #include #include #include #include StatisticsDialog::StatisticsDialog(const QString& title, QWidget* parent) : KDialog(parent) { - twStatistics = new QTabWidget(this); - setMainWidget(twStatistics); + m_twStatistics = new QTabWidget(this); + setMainWidget(m_twStatistics); setWindowTitle(title); setButtons(KDialog::Ok); + setAttribute(Qt::WA_DeleteOnClose); QString htmlColor = (palette().color(QPalette::Base).lightness() < 128) ? "#5f5f5f" : "#D1D1D1"; m_htmlText = QString("" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "
" + i18n("Location measures")+ "
" + i18n("Minimum")+ "%1
" + i18n("Maximum")+ "%2
" + i18n("Arithmetic mean")+ "%3
" + i18n("Geometric mean")+ "%4
" + i18n("Harmonic mean")+ "%5
" + i18n("Contraharmonic mean")+ "%6
" + i18n("Median")+ "%7
" + i18n("Dispersion measures")+ "
" + i18n("Variance")+ "%8
" + i18n("Standard deviation")+ "%9
" + i18n("Mean absolute deviation around mean")+ "%10
" + i18n("Mean absolute deviation around median")+ "%11
" + i18n("Median absolute deviation")+ "%12
" + i18n("Shape measures")+ "
" + i18n("Skewness")+ "%13
" + i18n("Kurtosis")+ "%14
" + i18n("Entropy")+ "%15
"); - connect(twStatistics, SIGNAL(currentChanged(int)), this, SLOT(currentTabChanged(int))); + connect(m_twStatistics, SIGNAL(currentChanged(int)), this, SLOT(currentTabChanged(int))); connect(this, SIGNAL(okClicked()), this, SLOT(close())); //restore saved settings if available KConfigGroup conf(KSharedConfig::openConfig(), "StatisticsDialog"); if (conf.exists()) KWindowConfig::restoreWindowSize(windowHandle(), conf); else resize(QSize(490, 520)); } StatisticsDialog::~StatisticsDialog() { KConfigGroup conf(KSharedConfig::openConfig(), "StatisticsDialog"); KWindowConfig::saveWindowSize(windowHandle(), conf); } void StatisticsDialog::setColumns(const QList& columns) { if (!columns.size()) return; m_columns = columns; for (int i = 0; i < m_columns.size(); ++i) { QTextEdit* textEdit = new QTextEdit; textEdit->setReadOnly(true); - twStatistics->addTab(textEdit, m_columns[i]->name()); + m_twStatistics->addTab(textEdit, m_columns[i]->name()); } currentTabChanged(0); } const QString StatisticsDialog::isNanValue(const double value) { return (std::isnan(value) ? QLatin1String("-") : QString::number(value,'g', 10)); } void StatisticsDialog::currentTabChanged(int index) { WAIT_CURSOR; const Column::ColumnStatistics& statistics = m_columns[index]->statistics(); RESET_CURSOR; - QTextEdit* textEdit = static_cast(twStatistics->currentWidget()); + QTextEdit* textEdit = static_cast(m_twStatistics->currentWidget()); textEdit->setHtml(m_htmlText.arg(isNanValue(statistics.minimum==INFINITY ? NAN : statistics.minimum), isNanValue(statistics.maximum==-INFINITY ? NAN : statistics.maximum), isNanValue(statistics.arithmeticMean), isNanValue(statistics.geometricMean), isNanValue(statistics.harmonicMean), isNanValue(statistics.contraharmonicMean), isNanValue(statistics.median), isNanValue(statistics.variance), isNanValue(statistics.standardDeviation)). arg(isNanValue(statistics.meanDeviation), isNanValue(statistics.meanDeviationAroundMedian), isNanValue(statistics.medianDeviation), isNanValue(statistics.skewness), isNanValue(statistics.kurtosis), isNanValue(statistics.entropy))); } diff --git a/src/kdefrontend/spreadsheet/StatisticsDialog.h b/src/kdefrontend/spreadsheet/StatisticsDialog.h index 42e690187..abec86293 100644 --- a/src/kdefrontend/spreadsheet/StatisticsDialog.h +++ b/src/kdefrontend/spreadsheet/StatisticsDialog.h @@ -1,56 +1,56 @@ /*************************************************************************** File : StatisticsDialog.h Project : LabPlot Description : Dialog showing statistics for column values -------------------------------------------------------------------- Copyright : (C) 2016 by Fabian Kristof (fkristofszabolcs@gmail.com) Copyright : (C) 2016 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 STATISTICSDIALOG_H #define STATISTICSDIALOG_H #include class Column; class QTabWidget; class StatisticsDialog : public KDialog { Q_OBJECT public: explicit StatisticsDialog(const QString&, QWidget *parent = 0); ~StatisticsDialog(); void setColumns(const QList&); private: const QString isNanValue(const double); - QTabWidget* twStatistics; + QTabWidget* m_twStatistics; QString m_htmlText; QList m_columns; private slots: void currentTabChanged(int); }; #endif diff --git a/src/kdefrontend/widgets/FITSHeaderEditWidget.cpp b/src/kdefrontend/widgets/FITSHeaderEditWidget.cpp index bed7f5417..ce12dc7f1 100644 --- a/src/kdefrontend/widgets/FITSHeaderEditWidget.cpp +++ b/src/kdefrontend/widgets/FITSHeaderEditWidget.cpp @@ -1,612 +1,612 @@ /*************************************************************************** File : FITSHeaderEditWidget.cpp Project : LabPlot Description : Widget for listing/editing FITS header keywords -------------------------------------------------------------------- Copyright : (C) 2016 by 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 "FITSHeaderEditWidget.h" #include "backend/datasources/filters/FITSFilter.h" #include "backend/lib/macros.h" #include "FITSHeaderEditNewKeywordDialog.h" #include "FITSHeaderEditAddUnitDialog.h" #include #include #include #include #include #include #include /*! \class FITSHeaderEditWidget * \brief Widget for listing/editing FITS header keywords * \since 2.4.0 * \ingroup kdefrontend/widgets */ FITSHeaderEditWidget::FITSHeaderEditWidget(QWidget* parent) : QWidget(parent), m_fitsFilter(new FITSFilter()), m_initializingTable(false) { ui.setupUi(this); initActions(); connectActions(); initContextMenus(); connect(ui.bAddUnit, SIGNAL(clicked(bool)), m_action_addmodify_unit, SIGNAL(toggled(bool))); connect(ui.bClose, SIGNAL(clicked(bool)), this, SLOT(closeFile())); ui.bOpen->setIcon(QIcon::fromTheme("document-open")); ui.bAddKey->setIcon(QIcon::fromTheme("list-add")); ui.bAddKey->setEnabled(false); ui.bAddKey->setToolTip(i18n("Add new keyword")); ui.bRemoveKey->setIcon(QIcon::fromTheme("list-remove")); ui.bRemoveKey->setEnabled(false); ui.bRemoveKey->setToolTip(i18n("Remove selected keyword")); ui.bAddUnit->setIcon(QIcon::fromTheme("document-new")); ui.bAddUnit->setEnabled(false); ui.bAddUnit->setToolTip(i18n("Add unit to keyword")); ui.bClose->setIcon(QIcon::fromTheme("document-close")); ui.bClose->setEnabled(false); ui.bClose->setToolTip(i18n("Close file")); ui.twKeywordsTable->setColumnCount(3); ui.twExtensions->setSelectionMode(QAbstractItemView::SingleSelection); ui.twExtensions->headerItem()->setText(0, i18n("Content")); ui.twKeywordsTable->setHorizontalHeaderItem(0, new QTableWidgetItem(i18n("Key"))); ui.twKeywordsTable->setHorizontalHeaderItem(1, new QTableWidgetItem(i18n("Value"))); ui.twKeywordsTable->setHorizontalHeaderItem(2, new QTableWidgetItem(i18n("Comment"))); ui.twKeywordsTable->setAlternatingRowColors(true); ui.twKeywordsTable->horizontalHeader()->resizeSections(QHeaderView::ResizeToContents); ui.twKeywordsTable->horizontalHeader()->setStretchLastSection(true); ui.twKeywordsTable->installEventFilter(this); ui.twExtensions->installEventFilter(this); setAttribute(Qt::WA_DeleteOnClose); connect(ui.bOpen, SIGNAL(clicked()), this, SLOT(openFile())); connect(ui.bAddKey, SIGNAL(clicked()), this, SLOT(addKeyword())); connect(ui.bRemoveKey, SIGNAL(clicked()), this, SLOT(removeKeyword())); connect(ui.twKeywordsTable, SIGNAL(itemChanged(QTableWidgetItem*)), this, SLOT(updateKeyword(QTableWidgetItem*))); connect(ui.twExtensions, SIGNAL(itemClicked(QTreeWidgetItem*,int)), this, SLOT(fillTable(QTreeWidgetItem*,int))); connect(ui.twExtensions, SIGNAL(itemClicked(QTreeWidgetItem*,int)), this, SLOT(enableButtonCloseFile(QTreeWidgetItem*,int))); } /*! * \brief Destructor */ FITSHeaderEditWidget::~FITSHeaderEditWidget() { delete m_fitsFilter; } /*! * \brief Fills the keywords tablewidget. * If the selected extension was not yet selected before, then the keywords are read from the file * and then the table is filled, otherwise the table is filled using the already existing keywords. */ void FITSHeaderEditWidget::fillTable() { m_initializingTable = true; if (!m_extensionDatas.contains(m_seletedExtension)) { m_extensionDatas[m_seletedExtension].keywords = m_fitsFilter->chduKeywords(m_seletedExtension); m_extensionDatas[m_seletedExtension].updates.updatedKeywords.reserve(m_extensionDatas[m_seletedExtension].keywords.size()); m_extensionDatas[m_seletedExtension].updates.updatedKeywords.resize(m_extensionDatas[m_seletedExtension].keywords.size()); m_fitsFilter->parseHeader(m_seletedExtension, ui.twKeywordsTable); } else { QList keywords = m_extensionDatas[m_seletedExtension].keywords; for (int i = 0; i < m_extensionDatas[m_seletedExtension].updates.updatedKeywords.size(); ++i) { FITSFilter::Keyword keyword = m_extensionDatas[m_seletedExtension].updates.updatedKeywords.at(i); if (!keyword.key.isEmpty()) keywords.operator [](i).key = keyword.key; if (!keyword.value.isEmpty()) keywords.operator [](i).value = keyword.value; if (!keyword.comment.isEmpty()) keywords.operator [](i).comment = keyword.comment; } - foreach (const FITSFilter::Keyword& key, m_extensionDatas[m_seletedExtension].updates.newKeywords) + for (const FITSFilter::Keyword& key : m_extensionDatas[m_seletedExtension].updates.newKeywords) keywords.append(key); m_fitsFilter->parseHeader(QString(), ui.twKeywordsTable, false, keywords); } m_initializingTable = false; } /*! * \brief Fills the tablewidget with the keywords of extension \a item * \param item the extension selected * \param col the column of the selected item */ void FITSHeaderEditWidget::fillTable(QTreeWidgetItem *item, int col) { WAIT_CURSOR; const QString& itemText = item->text(col); QString selectedExtension; int extType = 0; if (itemText.contains(QLatin1String("IMAGE #")) || itemText.contains(QLatin1String("ASCII_TBL #")) || itemText.contains(QLatin1String("BINARY_TBL #"))) extType = 1; else if (!itemText.compare(QLatin1String("Primary header"))) extType = 2; if (extType == 0) { if (item->parent() != 0) { if (item->parent()->parent() != 0) selectedExtension = item->parent()->parent()->text(0) + '[' + item->text(col) + ']'; } } else if (extType == 1) { if (item->parent() != 0) { if (item->parent()->parent() != 0) { bool ok; int hduNum = itemText.right(1).toInt(&ok); selectedExtension = item->parent()->parent()->text(0) + '[' + QString::number(hduNum-1) + ']'; } } } else { if (item->parent()->parent() != 0) selectedExtension = item->parent()->parent()->text(col); } if (!selectedExtension.isEmpty()) { if (!(m_seletedExtension == selectedExtension)) { m_seletedExtension = selectedExtension; fillTable(); } } RESET_CURSOR; } /*! * \brief Shows a dialog for opening a FITS file * If the returned file name is not empty (so a FITS file was selected) and it's not opened yet * then the file is parsed, so the treeview for the extensions is built and the table is filled. */ void FITSHeaderEditWidget::openFile() { KConfigGroup conf(KSharedConfig::openConfig(), "FITSHeaderEditWidget"); QString dir = conf.readEntry("LastDir", ""); QString fileName = QFileDialog::getOpenFileName(this, i18n("Open FITS file"), dir, i18n("FITS files (*.fits)")); if (fileName.isEmpty()) return; int pos = fileName.lastIndexOf(QDir::separator()); if (pos!=-1) { QString newDir = fileName.left(pos); if (newDir!=dir) conf.writeEntry("LastDir", newDir); } WAIT_CURSOR; QTreeWidgetItem* root = ui.twExtensions->invisibleRootItem(); const int childCount = root->childCount(); bool opened = false; for (int i = 0; i < childCount; ++i) { if(root->child(i)->text(0) == fileName) { opened = true; break; } } if (!opened) { - foreach (QTreeWidgetItem* item, ui.twExtensions->selectedItems()) + for (QTreeWidgetItem* item : ui.twExtensions->selectedItems()) item->setSelected(false); m_fitsFilter->parseExtensions(fileName, ui.twExtensions); ui.twExtensions->resizeColumnToContents(0); if (ui.twExtensions->selectedItems().size() > 0) fillTable(ui.twExtensions->selectedItems().at(0), 0); ui.bAddKey->setEnabled(true); ui.bRemoveKey->setEnabled(true); ui.bAddUnit->setEnabled(true); ui.bClose->setEnabled(false); } else { KMessageBox::information(this, i18n("Cannot open file, file already opened."), i18n("File already opened")); } RESET_CURSOR; } /*! * \brief Triggered when clicking the Save button * Saves the modifications (new keywords, new keyword units, keyword modifications, * deleted keywords, deleted extensions) to the FITS files. * \return \c true if there was something saved, otherwise false */ bool FITSHeaderEditWidget::save() { bool saved = false; - foreach (const QString& fileName, m_extensionDatas.keys()) { + for (const QString& fileName : m_extensionDatas.keys()) { if (m_extensionDatas[fileName].updates.newKeywords.size() > 0) { m_fitsFilter->addNewKeyword(fileName,m_extensionDatas[fileName].updates.newKeywords); if (!saved) saved = true; } if (m_extensionDatas[fileName].updates.removedKeywords.size() > 0) { m_fitsFilter->deleteKeyword(fileName, m_extensionDatas[fileName].updates.removedKeywords); if (!saved) saved = true; } if (!saved) { - foreach (const FITSFilter::Keyword& key, m_extensionDatas[fileName].updates.updatedKeywords) { + for (const FITSFilter::Keyword& key : m_extensionDatas[fileName].updates.updatedKeywords) { if (!key.isEmpty()) { saved = true; break; } } } m_fitsFilter->updateKeywords(fileName, m_extensionDatas[fileName].keywords, m_extensionDatas[fileName].updates.updatedKeywords); m_fitsFilter->addKeywordUnit(fileName, m_extensionDatas[fileName].keywords); m_fitsFilter->addKeywordUnit(fileName, m_extensionDatas[fileName].updates.newKeywords); } if (m_removedExtensions.size() > 0) { m_fitsFilter->removeExtensions(m_removedExtensions); if (!saved) saved = true; } return saved; } /*! * \brief Initializes the context menu's actions. */ void FITSHeaderEditWidget::initActions() { m_action_add_keyword = new QAction(QIcon::fromTheme("list-add"), i18n("Add new keyword"), this); m_action_remove_keyword = new QAction(QIcon::fromTheme("list-remove"), i18n("Remove keyword"), this); m_action_remove_extension = new QAction(i18n("Delete"), this); m_action_addmodify_unit = new QAction(i18n("Add unit"), this); } /*! * \brief Connects signals of the actions to the appropriate slots. */ void FITSHeaderEditWidget::connectActions() { connect(m_action_add_keyword, SIGNAL(triggered()), this, SLOT(addKeyword())); connect(m_action_remove_keyword, SIGNAL(triggered()), this, SLOT(removeKeyword())); connect(m_action_remove_extension, SIGNAL(triggered()), this, SLOT(removeExtension())); connect(m_action_addmodify_unit, SIGNAL(triggered()), this, SLOT(addModifyKeywordUnit())); } /*! * \brief Initializes the context menus. */ void FITSHeaderEditWidget::initContextMenus() { m_KeywordActionsMenu = new QMenu(this); m_KeywordActionsMenu->addAction(m_action_add_keyword); m_KeywordActionsMenu->addAction(m_action_remove_keyword); m_KeywordActionsMenu->addSeparator(); m_KeywordActionsMenu->addAction(m_action_addmodify_unit); m_ExtensionActionsMenu = new QMenu(this); m_ExtensionActionsMenu->addAction(m_action_remove_extension); } /*! * \brief Shows a FITSHeaderEditNewKeywordDialog and decides whether the new keyword provided in the dialog * can be added to the new keywords or not. Updates the tablewidget if it's needed. */ void FITSHeaderEditWidget::addKeyword() { FITSHeaderEditNewKeywordDialog* newKeywordDialog = new FITSHeaderEditNewKeywordDialog; m_initializingTable = true; if (newKeywordDialog->exec() == KDialog::Accepted) { FITSFilter::Keyword newKeyWord = newKeywordDialog->newKeyword(); QList currentKeywords = m_extensionDatas[m_seletedExtension].keywords; - foreach (const FITSFilter::Keyword& keyword, currentKeywords) { + for(const FITSFilter::Keyword& keyword : currentKeywords) { if (keyword.operator==(newKeyWord)) { KMessageBox::information(this, i18n("Cannot add keyword, keyword already added"), i18n("Cannot add keyword")); return; } } - foreach (const FITSFilter::Keyword& keyword, m_extensionDatas[m_seletedExtension].updates.newKeywords) { + for(const FITSFilter::Keyword& keyword : m_extensionDatas[m_seletedExtension].updates.newKeywords) { if (keyword.operator==(newKeyWord)) { KMessageBox::information(this, i18n("Cannot add keyword, keyword already added"), i18n("Cannot add keyword")); return; } } - foreach (const QString& keyword, mandatoryKeywords()) { + for(const QString& keyword : mandatoryKeywords()) { if (!keyword.compare(newKeyWord.key)) { KMessageBox::information(this, i18n("Cannot add mandatory keyword, they are already present"), i18n("Cannot add keyword")); return; } } /* - Column related keyword (TFIELDS, TTYPEn,TFORMn, etc.) in an image - SIMPLE, EXTEND, or BLOCKED keyword in any extension - BSCALE, BZERO, BUNIT, BLANK, DATAMAX, DATAMIN keywords in a table - Keyword name contains illegal character */ m_extensionDatas[m_seletedExtension].updates.newKeywords.append(newKeyWord); const int lastRow = ui.twKeywordsTable->rowCount(); ui.twKeywordsTable->setRowCount(lastRow + 1); QTableWidgetItem* newKeyWordItem = new QTableWidgetItem(newKeyWord.key); newKeyWordItem->setFlags(Qt::ItemIsEditable | Qt::ItemIsSelectable | Qt::ItemIsEnabled); ui.twKeywordsTable->setItem(lastRow, 0, newKeyWordItem); newKeyWordItem = new QTableWidgetItem(newKeyWord.value); newKeyWordItem->setFlags(Qt::ItemIsEditable | Qt::ItemIsSelectable | Qt::ItemIsEnabled); ui.twKeywordsTable->setItem(lastRow, 1, newKeyWordItem); newKeyWordItem = new QTableWidgetItem(newKeyWord.comment); newKeyWordItem->setFlags(Qt::ItemIsEditable | Qt::ItemIsSelectable | Qt::ItemIsEnabled); ui.twKeywordsTable->setItem(lastRow, 2, newKeyWordItem); } m_initializingTable = false; delete newKeywordDialog; emit changed(true); } /*! * \brief Shows a messagebox whether we want to remove the keyword or not. * Mandatory keywords cannot be deleted. */ void FITSHeaderEditWidget::removeKeyword() { const int row = ui.twKeywordsTable->currentRow(); if (row == -1) return; QString key = ui.twKeywordsTable->item(row, 0)->text(); const int rc = KMessageBox::questionYesNo(this, i18n("Are you sure you want to delete the keyword '%1'?").arg(key), i18n("Confirm deletion")); if (rc == KMessageBox::Yes) { bool remove = true; - foreach (const QString& k, mandatoryKeywords()) { + for(const QString& k : mandatoryKeywords()) { if (!k.compare(key)) { remove = false; break; } } if (remove) { FITSFilter::Keyword toRemove = FITSFilter::Keyword(key, ui.twKeywordsTable->item(row, 1)->text(), ui.twKeywordsTable->item(row, 2)->text()); ui.twKeywordsTable->removeRow(row); m_extensionDatas[m_seletedExtension].keywords.removeAt(row); m_extensionDatas[m_seletedExtension].updates.removedKeywords.append(toRemove); } else KMessageBox::information(this, i18n("Cannot remove mandatory keyword."), i18n("Removing keyword")); } emit changed(true); } /*! * \brief Trigggered when an item was updated by the user in the tablewidget * \param item the item which was updated */ void FITSHeaderEditWidget::updateKeyword(QTableWidgetItem *item) { if (!m_initializingTable) { const int row = item->row(); int idx; bool fromNewKeyword = false; if (row > m_extensionDatas[m_seletedExtension].keywords.size()-1) { idx = row - m_extensionDatas[m_seletedExtension].keywords.size(); fromNewKeyword = true; } else idx = row; if (item->column() == 0) { if (!fromNewKeyword) { m_extensionDatas[m_seletedExtension].updates.updatedKeywords.operator [](idx).key = item->text(); m_extensionDatas[m_seletedExtension].keywords.operator [](idx).updates.keyUpdated = true; } else { m_extensionDatas[m_seletedExtension].updates.newKeywords.operator [](idx).key = item->text(); m_extensionDatas[m_seletedExtension].updates.newKeywords.operator [](idx).updates.keyUpdated = true; } } else if (item->column() == 1) { if (!fromNewKeyword) { m_extensionDatas[m_seletedExtension].updates.updatedKeywords.operator [](idx).value = item->text(); m_extensionDatas[m_seletedExtension].keywords.operator [](idx).updates.valueUpdated = true; } else { m_extensionDatas[m_seletedExtension].updates.newKeywords.operator [](idx).value = item->text(); m_extensionDatas[m_seletedExtension].updates.newKeywords.operator [](idx).updates.valueUpdated = true; } } else { if (!fromNewKeyword) { m_extensionDatas[m_seletedExtension].updates.updatedKeywords.operator [](idx).comment = item->text(); m_extensionDatas[m_seletedExtension].keywords.operator [](idx).updates.commentUpdated = true; } else { m_extensionDatas[m_seletedExtension].updates.newKeywords.operator [](idx).comment = item->text(); m_extensionDatas[m_seletedExtension].updates.newKeywords.operator [](idx).updates.commentUpdated = true; } } } emit changed(true); } /*! * \brief Shows a FITSHeaderEditAddUnitDialog on the selected keyword (provides the keyword's unit to the * dialog if it had one) and if the dialog was accepted then the new keyword unit is set and the tablewidget * is updated (filled with the modifications). */ void FITSHeaderEditWidget::addModifyKeywordUnit() { FITSHeaderEditAddUnitDialog* addUnitDialog; const int selectedRow = ui.twKeywordsTable->currentRow(); int idx; bool fromNewKeyword = false; if (selectedRow > m_extensionDatas[m_seletedExtension].keywords.size()-1) { idx = selectedRow - m_extensionDatas[m_seletedExtension].keywords.size(); fromNewKeyword = true; } else idx = selectedRow; QString unit; if (fromNewKeyword) { if (!m_extensionDatas[m_seletedExtension].updates.newKeywords.at(idx).unit.isEmpty()) unit = m_extensionDatas[m_seletedExtension].updates.newKeywords.at(idx).unit; } else { if (!m_extensionDatas[m_seletedExtension].keywords.at(idx).unit.isEmpty()) unit = m_extensionDatas[m_seletedExtension].keywords.at(idx).unit; } addUnitDialog = new FITSHeaderEditAddUnitDialog(unit); if (addUnitDialog->exec() == KDialog::Accepted) { if (fromNewKeyword) { m_extensionDatas[m_seletedExtension].updates.newKeywords.operator [](idx).unit = addUnitDialog->unit(); if (!m_extensionDatas[m_seletedExtension].updates.newKeywords.at(idx).unit.isEmpty()) { m_extensionDatas[m_seletedExtension].updates.newKeywords.operator [](idx).updates.unitUpdated = true;; } } else { m_extensionDatas[m_seletedExtension].keywords.operator [](idx).unit = addUnitDialog->unit(); if (!m_extensionDatas[m_seletedExtension].keywords.at(idx).unit.isEmpty()) m_extensionDatas[m_seletedExtension].keywords.operator [](idx).updates.unitUpdated = true; } fillTable(); } delete addUnitDialog; emit changed(true); } /*! * \brief Removes the selected extension from the extensions treeview * If the last extension is removed from the tree, then the extension and the file will be removed too. */ void FITSHeaderEditWidget::removeExtension() { QTreeWidgetItem* current = ui.twExtensions->currentItem(); QTreeWidgetItem* newCurrent = ui.twExtensions->itemBelow(current); if (current->parent()) { if (current->parent()->childCount() < 2) delete current->parent(); else delete current; } const QStringList keys = m_extensionDatas.keys(); const int selectedidx = keys.indexOf(m_seletedExtension); if (selectedidx > 0) { const QString& ext = m_seletedExtension; m_extensionDatas.remove(ext); m_removedExtensions.append(ext); m_seletedExtension = keys.at(selectedidx-1); fillTable(); } ui.twExtensions->setCurrentItem(newCurrent); emit changed(true); } /*! * \brief Returns a list of mandatory keywords according to the currently selected extension. * If the currently selected extension is an image then it returns the mandatory keywords of an image, * otherwise the mandatory keywords of a table * \return a list of mandatory keywords */ QList FITSHeaderEditWidget::mandatoryKeywords() const { QList mandatoryKeywords; const QTreeWidgetItem* currentItem = ui.twExtensions->currentItem(); if (currentItem->parent()->text(0).compare(QLatin1String("Images"))) mandatoryKeywords = FITSFilter::mandatoryImageExtensionKeywords(); else mandatoryKeywords = FITSFilter::mandatoryTableExtensionKeywords(); return mandatoryKeywords; } /*! * \brief Manipulates the contextmenu event of the widget * \param watched the object on which the event occurred * \param event the event watched * \return */ bool FITSHeaderEditWidget::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 == ui.twKeywordsTable) { if (ui.twExtensions->selectedItems().size() != 0) m_KeywordActionsMenu->exec(global_pos); } else if (watched == ui.twExtensions) { if (ui.twExtensions->selectedItems().size() != 0) { QTreeWidgetItem* current = ui.twExtensions->currentItem(); int col = ui.twExtensions->currentColumn(); if (current->parent()) { if ((current->text(col) != QLatin1String("Images")) && (current->text(col) != QLatin1String("Tables"))) m_ExtensionActionsMenu->exec(global_pos); } } } else return QWidget::eventFilter(watched, event); return true; } else return QWidget::eventFilter(watched, event); } void FITSHeaderEditWidget::closeFile() { if (ui.twExtensions->currentItem()) { QTreeWidgetItem* current = ui.twExtensions->currentItem(); int idxOfCurrentAsTopLevel = -1; for (int i = 0; i < ui.twExtensions->topLevelItemCount(); ++i) { if (current == ui.twExtensions->topLevelItem(i)) { idxOfCurrentAsTopLevel = i; break; } } QTreeWidgetItem* newCurrent = (QTreeWidgetItem*)0; if (idxOfCurrentAsTopLevel == 0) { if (ui.twExtensions->topLevelItemCount() == 1) { //last file closed, deactivate action buttons, clear keywords table ui.twKeywordsTable->setRowCount(0); ui.bClose->setEnabled(false); ui.bAddUnit->setEnabled(false); ui.bAddKey->setEnabled(false); ui.bRemoveKey->setEnabled(false); } else newCurrent = ui.twExtensions->topLevelItem(idxOfCurrentAsTopLevel + 1); } else newCurrent = ui.twExtensions->topLevelItem(idxOfCurrentAsTopLevel - 1); if (newCurrent) { m_seletedExtension = newCurrent->text(0); fillTable(); } - foreach (const QString& key, m_extensionDatas.keys()) { + for(const QString& key : m_extensionDatas.keys()) { if (key.startsWith(current->text(0))) m_extensionDatas.remove(key); } delete current; emit changed(true); } } void FITSHeaderEditWidget::enableButtonCloseFile(QTreeWidgetItem* item,int col) { Q_UNUSED(col) ui.bClose->setEnabled(item->parent() ? false : true); }