File indexing completed on 2025-11-02 03:43:49
0001 /* 0002 File : ExportWorksheetDialog.cpp 0003 Project : LabPlot 0004 Description : export worksheet dialog 0005 -------------------------------------------------------------------- 0006 SPDX-FileCopyrightText: 2011-2022 Alexander Semke <alexander.semke@web.de> 0007 SPDX-FileCopyrightText: 2021 Stefan Gerlach <stefan.gerlach@uni.kn> 0008 SPDX-License-Identifier: GPL-2.0-or-later 0009 */ 0010 0011 #include "ExportWorksheetDialog.h" 0012 #include "backend/core/Settings.h" 0013 #include "commonfrontend/worksheet/WorksheetView.h" 0014 #include "kdefrontend/GuiTools.h" 0015 #include "ui_exportworksheetwidget.h" 0016 0017 #include <KLocalizedString> 0018 #include <KMessageBox> 0019 0020 #include <KWindowConfig> 0021 #include <kcoreaddons_version.h> 0022 0023 #include <QCompleter> 0024 // see https://gitlab.kitware.com/cmake/cmake/-/issues/21609 0025 #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) 0026 #include <QFileSystemModel> 0027 #else 0028 #include <QDirModel> 0029 #endif 0030 #include <QDialogButtonBox> 0031 #include <QFileDialog> 0032 #include <QScreen> 0033 #include <QWindow> 0034 0035 /*! 0036 \class ExportWorksheetDialog 0037 \brief Dialog for exporting a worksheet to a file. 0038 0039 \ingroup kdefrontend 0040 */ 0041 0042 ExportWorksheetDialog::ExportWorksheetDialog(QWidget* parent) 0043 : QDialog(parent) 0044 , ui(new Ui::ExportWorksheetWidget()) { 0045 ui->setupUi(this); 0046 0047 auto* btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); 0048 m_showOptionsButton = new QPushButton; 0049 0050 connect(btnBox, &QDialogButtonBox::clicked, this, &ExportWorksheetDialog::slotButtonClicked); 0051 0052 btnBox->addButton(m_showOptionsButton, QDialogButtonBox::ActionRole); 0053 ui->verticalLayout->addWidget(btnBox); 0054 0055 m_okButton = btnBox->button(QDialogButtonBox::Ok); 0056 m_cancelButton = btnBox->button(QDialogButtonBox::Cancel); 0057 0058 m_cancelButton->setToolTip(i18n("Close this dialog without exporting.")); 0059 0060 #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) 0061 ui->leFileName->setCompleter(new QCompleter(new QFileSystemModel, this)); 0062 #else 0063 ui->leFileName->setCompleter(new QCompleter(new QDirModel, this)); 0064 #endif 0065 0066 ui->bOpen->setIcon(QIcon::fromTheme(QLatin1String("document-open"))); 0067 0068 // see WorksheetView::ExportFormat 0069 ui->cbFormat->addItem(QIcon::fromTheme(QLatin1String("application-pdf")), i18n("Portable Data Format (PDF)")); 0070 ui->cbFormat->addItem(QIcon::fromTheme(QLatin1String("image-svg+xml")), i18n("Scalable Vector Graphics (SVG)")); 0071 ui->cbFormat->insertSeparator(3); 0072 ui->cbFormat->addItem(QIcon::fromTheme(QLatin1String("image-png")), i18n("Portable Network Graphics (PNG)")); 0073 ui->cbFormat->addItem(QIcon::fromTheme(QLatin1String("image-jpeg")), i18n("Joint Photographic Experts Group (JPG)")); 0074 ui->cbFormat->addItem(QIcon::fromTheme(QLatin1String("image-bmp")), i18n("Windows Bitmap (BMP)")); 0075 ui->cbFormat->addItem(QIcon::fromTheme(QLatin1String("image-x-generic")), i18n("Portable Pixmap (PPM)")); 0076 ui->cbFormat->addItem(QIcon::fromTheme(QLatin1String("image-x-generic")), i18n("X11 Bitmap (XBM)")); 0077 ui->cbFormat->addItem(QIcon::fromTheme(QLatin1String("image-x-generic")), i18n("X11 Bitmap (XPM)")); 0078 0079 ui->cbExportTo->addItem(i18n("File")); 0080 ui->cbExportTo->addItem(i18n("Clipboard")); 0081 0082 ui->cbExportArea->addItem(i18n("Object's Bounding Box")); 0083 ui->cbExportArea->addItem(i18n("Current Selection")); 0084 ui->cbExportArea->addItem(i18n("Complete Worksheet")); 0085 0086 ui->cbResolution->addItem( 0087 i18nc("%1 is the value of DPI of the current screen", "%1 (desktop)", QString::number(QApplication::primaryScreen()->physicalDotsPerInchX()))); 0088 ui->cbResolution->addItem(QLatin1String("100")); 0089 ui->cbResolution->addItem(QLatin1String("150")); 0090 ui->cbResolution->addItem(QLatin1String("200")); 0091 ui->cbResolution->addItem(QLatin1String("300")); 0092 ui->cbResolution->addItem(QLatin1String("600")); 0093 ui->cbResolution->setValidator(new QIntValidator(ui->cbResolution)); 0094 0095 connect(ui->cbFormat, QOverload<int>::of(&KComboBox::currentIndexChanged), this, &ExportWorksheetDialog::formatChanged); 0096 connect(ui->cbExportTo, QOverload<int>::of(&KComboBox::currentIndexChanged), this, &ExportWorksheetDialog::exportToChanged); 0097 connect(ui->bOpen, &QPushButton::clicked, this, &ExportWorksheetDialog::selectFile); 0098 connect(ui->leFileName, &QLineEdit::textChanged, this, &ExportWorksheetDialog::fileNameChanged); 0099 connect(m_showOptionsButton, &QPushButton::clicked, this, &ExportWorksheetDialog::toggleOptions); 0100 ui->leFileName->setFocus(); 0101 0102 setWindowTitle(i18nc("@title:window", "Export Worksheet")); 0103 setWindowIcon(QIcon::fromTheme(QLatin1String("document-export-database"))); 0104 0105 // restore saved settings if available 0106 KConfigGroup conf = Settings::group(QStringLiteral("ExportWorksheetDialog")); 0107 ui->cbFormat->setCurrentIndex(conf.readEntry("Format", 0)); 0108 ui->cbExportTo->setCurrentIndex(conf.readEntry("ExportTo", 0)); 0109 ui->cbExportArea->setCurrentIndex(conf.readEntry("Area", 0)); 0110 ui->chkExportBackground->setChecked(conf.readEntry("Background", true)); 0111 ui->cbResolution->setCurrentIndex(conf.readEntry("Resolution", 0)); 0112 m_showOptions = conf.readEntry("ShowOptions", true); 0113 m_showOptions ? m_showOptionsButton->setText(i18n("Hide Options")) : m_showOptionsButton->setText(i18n("Show Options")); 0114 ui->gbOptions->setVisible(m_showOptions); 0115 0116 create(); // ensure there's a window created 0117 if (conf.exists()) { 0118 KWindowConfig::restoreWindowSize(windowHandle(), conf); 0119 resize(windowHandle()->size()); // workaround for QTBUG-40584 0120 } else 0121 resize(QSize(0, 0).expandedTo(minimumSize())); 0122 } 0123 0124 ExportWorksheetDialog::~ExportWorksheetDialog() { 0125 // save current settings 0126 KConfigGroup conf = Settings::group(QStringLiteral("ExportWorksheetDialog")); 0127 conf.writeEntry("Format", ui->cbFormat->currentIndex()); 0128 conf.writeEntry("ExportTo", ui->cbExportTo->currentIndex()); 0129 conf.writeEntry("Area", ui->cbExportArea->currentIndex()); 0130 conf.writeEntry("Background", ui->chkExportBackground->isChecked()); 0131 conf.writeEntry("Resolution", ui->cbResolution->currentIndex()); 0132 conf.writeEntry("ShowOptions", m_showOptions); 0133 KWindowConfig::saveWindowSize(windowHandle(), conf); 0134 0135 delete ui; 0136 } 0137 0138 /*! 0139 * sets the current project file name. If not empty, the path of the project file 0140 * is determined that is then used as the default location for the exported file. 0141 */ 0142 void ExportWorksheetDialog::setProjectFileName(const QString& name) { 0143 if (name.isEmpty()) 0144 return; 0145 0146 QFileInfo fi(name); 0147 m_projectPath = fi.dir().canonicalPath(); 0148 } 0149 0150 void ExportWorksheetDialog::setFileName(const QString& name) { 0151 if (m_projectPath.isEmpty()) { 0152 // no project folder is available (yet), use the last used directory in this dialog 0153 KConfigGroup conf = Settings::group(QStringLiteral("ExportWorksheetDialog")); 0154 QString dir = conf.readEntry("LastDir", ""); 0155 if (dir.isEmpty()) 0156 dir = QDir::homePath(); 0157 ui->leFileName->setText(dir + QLatin1String("/") + name); 0158 } else 0159 ui->leFileName->setText(m_projectPath + QLatin1String("/") + name); 0160 0161 formatChanged(ui->cbFormat->currentIndex()); 0162 exportToChanged(ui->cbExportTo->currentIndex()); 0163 } 0164 0165 QString ExportWorksheetDialog::path() const { 0166 if (ui->cbExportTo->currentIndex() == 0) 0167 return ui->leFileName->text(); 0168 return {}; 0169 } 0170 0171 WorksheetView::ExportFormat ExportWorksheetDialog::exportFormat() const { 0172 int index = ui->cbFormat->currentIndex(); 0173 0174 // we have a separator in the format combobox at the 3th position -> skip it 0175 if (index > 2) 0176 index--; 0177 0178 return WorksheetView::ExportFormat(index); 0179 } 0180 0181 WorksheetView::ExportArea ExportWorksheetDialog::exportArea() const { 0182 return WorksheetView::ExportArea(ui->cbExportArea->currentIndex()); 0183 } 0184 0185 bool ExportWorksheetDialog::exportBackground() const { 0186 return ui->chkExportBackground->isChecked(); 0187 } 0188 0189 int ExportWorksheetDialog::exportResolution() const { 0190 if (ui->cbResolution->currentIndex() == 0) 0191 return QApplication::primaryScreen()->physicalDotsPerInchX(); 0192 else 0193 return ui->cbResolution->currentText().toInt(); 0194 } 0195 0196 void ExportWorksheetDialog::slotButtonClicked(QAbstractButton* button) { 0197 if (button == m_okButton) 0198 okClicked(); 0199 else if (button == m_cancelButton) 0200 reject(); 0201 } 0202 0203 // SLOTS 0204 void ExportWorksheetDialog::okClicked() { 0205 if (ui->cbExportTo->currentIndex() == 0 /*export to file*/ 0206 && m_askOverwrite && QFile::exists(ui->leFileName->text())) { 0207 #if KCOREADDONS_VERSION >= QT_VERSION_CHECK(5, 100, 0) 0208 int status = KMessageBox::questionTwoActions(this, 0209 i18n("The file already exists. Do you really want to overwrite it?"), 0210 i18n("Export"), 0211 KStandardGuiItem::overwrite(), 0212 KStandardGuiItem::cancel()); 0213 if (status == KMessageBox::SecondaryAction) 0214 #else 0215 int status = KMessageBox::questionYesNo(this, i18n("The file already exists. Do you really want to overwrite it?"), i18n("Export")); 0216 if (status == KMessageBox::No) 0217 #endif 0218 return; 0219 } 0220 0221 KConfigGroup conf = Settings::group(QStringLiteral("ExportWorksheetDialog")); 0222 const auto& path = ui->leFileName->text(); 0223 if (!path.isEmpty()) { 0224 QString dir = conf.readEntry("LastDir", ""); 0225 int pos = path.lastIndexOf(QLatin1String("/")); 0226 if (pos != -1) { 0227 QString newDir = path.left(pos); 0228 if (newDir != dir) 0229 conf.writeEntry("LastDir", newDir); 0230 } 0231 } 0232 0233 accept(); 0234 } 0235 0236 /*! 0237 Shows/hides the GroupBox with export options in this dialog. 0238 */ 0239 void ExportWorksheetDialog::toggleOptions() { 0240 m_showOptions = !m_showOptions; 0241 ui->gbOptions->setVisible(m_showOptions); 0242 m_showOptions ? m_showOptionsButton->setText(i18n("Hide Options")) : m_showOptionsButton->setText(i18n("Show Options")); 0243 0244 // resize the dialog 0245 layout()->activate(); 0246 resize(QSize(this->width(), 0).expandedTo(minimumSize())); 0247 } 0248 0249 /*! 0250 opens a file dialog and lets the user select the file. 0251 */ 0252 void ExportWorksheetDialog::selectFile() { 0253 KConfigGroup conf = Settings::group(QStringLiteral("ExportWorksheetDialog")); 0254 const QString dir = conf.readEntry("LastDir", ""); 0255 0256 DEBUG(Q_FUNC_INFO << ", format" << ui->cbFormat->currentIndex()) 0257 QString format; 0258 int index = ui->cbFormat->currentIndex(); 0259 if (index > 2) // consider separator 0260 index--; 0261 0262 switch ((WorksheetView::ExportFormat)index) { 0263 case WorksheetView::ExportFormat::PDF: 0264 format = i18n("Portable Data Format (*.pdf *.PDF)"); 0265 break; 0266 case WorksheetView::ExportFormat::SVG: 0267 format = i18n("Scalable Vector Graphics (*.svg *.SVG)"); 0268 break; 0269 case WorksheetView::ExportFormat::PNG: 0270 format = i18n("Portable Network Graphics (*.png *.PNG)"); 0271 break; 0272 case WorksheetView::ExportFormat::JPG: 0273 format = i18n("Joint Photographic Experts Group (*.jpg *.jpeg *.JPG *.JPEG)"); 0274 break; 0275 case WorksheetView::ExportFormat::BMP: 0276 format = i18n("Windows Bitmap (*.bmp *.BMP)"); 0277 break; 0278 case WorksheetView::ExportFormat::PPM: 0279 format = i18n("Portable Pixmap (*.ppm *.PPM)"); 0280 break; 0281 case WorksheetView::ExportFormat::XBM: 0282 format = i18n("X11 Bitmap (*.xbm *.XBM)"); 0283 break; 0284 case WorksheetView::ExportFormat::XPM: 0285 format = i18n("X11 Bitmap (*.xpm *.XPM)"); 0286 break; 0287 } 0288 0289 const QString path = QFileDialog::getSaveFileName(this, i18nc("@title:window", "Export to File"), dir, format); 0290 if (!path.isEmpty()) { 0291 // if the file is already existing, the user was already asked 0292 // in QFileDialog whether to overwrite or not. 0293 // Don't ask again when the user click on Ok-button. 0294 m_askOverwrite = false; 0295 0296 m_initializing = true; 0297 ui->leFileName->setText(path); 0298 m_initializing = false; 0299 0300 int pos = path.lastIndexOf(QLatin1String("/")); 0301 if (pos != -1) { 0302 const QString newDir = path.left(pos); 0303 if (newDir != dir && QDir(newDir).exists()) 0304 conf.writeEntry("LastDir", newDir); 0305 } 0306 } 0307 } 0308 0309 /*! 0310 called when the output format was changed. Adjusts the extension for the specified file. 0311 */ 0312 void ExportWorksheetDialog::formatChanged(int index) { 0313 // we have a separator in the format combobox at the 3rd position -> skip it 0314 if (index > 2) 0315 index--; 0316 0317 QStringList extensions; 0318 // see WorksheetView::ExportFormat 0319 extensions << QLatin1String(".pdf") << QLatin1String(".svg") << QLatin1String(".png") << QLatin1String(".jpg") << QLatin1String(".bmp") 0320 << QLatin1String(".ppm") << QLatin1String(".xbm") << QLatin1String(".xpm"); 0321 QString path = ui->leFileName->text(); 0322 int i = path.indexOf(QLatin1Char('.')); 0323 if (i == -1) 0324 path = path + extensions.at(index); 0325 else 0326 path = path.left(i) + extensions.at(index); 0327 0328 ui->leFileName->setText(path); 0329 0330 // show resolution option for png format 0331 bool visible = (index == 2); 0332 ui->lResolution->setVisible(visible); 0333 ui->cbResolution->setVisible(visible); 0334 } 0335 0336 /*! 0337 called when the target destination (file or clipboard) format was changed. 0338 */ 0339 void ExportWorksheetDialog::exportToChanged(int index) { 0340 bool toFile = (index == 0); 0341 ui->lFileName->setVisible(toFile); 0342 ui->leFileName->setVisible(toFile); 0343 ui->bOpen->setVisible(toFile); 0344 0345 if (toFile) { 0346 m_okButton->setToolTip(i18n("Export to file and close the dialog.")); 0347 fileNameChanged(ui->leFileName->text()); // call this to check whether a valid file name was provided 0348 } else { 0349 m_okButton->setToolTip(i18n("Export to clipboard and close the dialog.")); 0350 m_okButton->setEnabled(true); 0351 } 0352 } 0353 0354 void ExportWorksheetDialog::fileNameChanged(const QString& name) { 0355 CONDITIONAL_LOCK_RETURN; 0356 0357 if (name.simplified().isEmpty()) { 0358 m_okButton->setEnabled(false); 0359 return; 0360 } 0361 QString path = ui->leFileName->text(); 0362 int pos = path.lastIndexOf(QLatin1String("/")); 0363 if (pos != -1) { 0364 QString dir = path.left(pos); 0365 bool invalid = !QDir(dir).exists(); 0366 GuiTools::highlight(ui->leFileName, invalid); 0367 if (invalid) { 0368 m_okButton->setEnabled(false); 0369 return; 0370 } 0371 } 0372 0373 m_askOverwrite = true; 0374 m_okButton->setEnabled(true); 0375 }