File indexing completed on 2024-03-24 03:44:53
0001 /* 0002 KmPlot - a math. function plotter for the KDE-Desktop 0003 0004 SPDX-FileCopyrightText: 1998, 1999, 2000, 2002 Klaus-Dieter Möller <kd.moeller@t-online.de> 0005 SPDX-FileCopyrightText: 2006 David Saxton <david@bluehaze.org> 0006 0007 This file is part of the KDE Project. 0008 KmPlot is part of the KDE-EDU Project. 0009 0010 SPDX-License-Identifier: GPL-2.0-or-later 0011 0012 */ 0013 0014 #include "maindlg.h" 0015 0016 // Qt includes 0017 #include <QClipboard> 0018 #include <QDebug> 0019 #include <QFileDialog> 0020 #include <QIcon> 0021 #include <QImageWriter> 0022 #include <QMainWindow> 0023 #include <QMimeDatabase> 0024 #include <QMimeType> 0025 #include <QPixmap> 0026 #include <QPrintDialog> 0027 #include <QPrintPreviewDialog> 0028 #include <QPrintPreviewWidget> 0029 #include <QSvgGenerator> 0030 #include <QTemporaryFile> 0031 #include <QTimer> 0032 0033 // KDE includes 0034 #include <KActionCollection> 0035 #include <KConfigDialog> 0036 #include <KHelpClient> 0037 #include <KIO/StatJob> 0038 #include <KIO/StoredTransferJob> 0039 #include <KLineEdit> 0040 #include <KMessageBox> 0041 #include <KStandardAction> 0042 #include <KToolBar> 0043 #include <kwidgetsaddons_version.h> 0044 0045 // local includes 0046 #include "calculator.h" 0047 #include "functioneditor.h" 0048 #include "functiontools.h" 0049 #include "kconstanteditor.h" 0050 #include "xparser.h" 0051 0052 #include "ksliderwindow.h" 0053 #include "maindlgadaptor.h" 0054 #include "settings.h" 0055 #include "ui_settingspagecolor.h" 0056 #include "ui_settingspagediagram.h" 0057 #include "ui_settingspagefonts.h" 0058 #include "ui_settingspagegeneral.h" 0059 0060 #include <assert.h> 0061 0062 class XParser; 0063 class KmPlotIO; 0064 0065 class SettingsPageColor : public QWidget, public Ui::SettingsPageColor 0066 { 0067 public: 0068 SettingsPageColor(QWidget *parent = nullptr) 0069 : QWidget(parent) 0070 { 0071 setupUi(this); 0072 layout()->setContentsMargins(0, 0, 0, 0); 0073 } 0074 }; 0075 0076 class SettingsPageFonts : public QWidget, public Ui::SettingsPageFonts 0077 { 0078 public: 0079 SettingsPageFonts(QWidget *parent = nullptr) 0080 : QWidget(parent) 0081 { 0082 setupUi(this); 0083 layout()->setContentsMargins(0, 0, 0, 0); 0084 } 0085 }; 0086 0087 class SettingsPageGeneral : public QWidget, public Ui::SettingsPageGeneral 0088 { 0089 public: 0090 SettingsPageGeneral(QWidget *parent = nullptr) 0091 : QWidget(parent) 0092 { 0093 setupUi(this); 0094 layout()->setContentsMargins(0, 0, 0, 0); 0095 } 0096 }; 0097 0098 class SettingsPageDiagram : public QWidget, public Ui::SettingsPageDiagram 0099 { 0100 public: 0101 SettingsPageDiagram(QWidget *parent = nullptr) 0102 : QWidget(parent) 0103 { 0104 setupUi(this); 0105 layout()->setContentsMargins(0, 0, 0, 0); 0106 } 0107 }; 0108 0109 bool MainDlg::oldfileversion; 0110 MainDlg *MainDlg::m_self = nullptr; 0111 0112 K_PLUGIN_CLASS_WITH_JSON(MainDlg, "kmplot_part.json") 0113 0114 // BEGIN class MainDlg 0115 #if QT_VERSION_MAJOR == 5 0116 MainDlg::MainDlg(QWidget *parentWidget, QObject *parent, const QVariantList &) 0117 : KParts::ReadWritePart(parent) 0118 #else 0119 MainDlg::MainDlg(QWidget *parentWidget, QObject *parent, const KPluginMetaData &data) 0120 : KParts::ReadWritePart(parent, data) 0121 #endif 0122 , m_recentFiles(nullptr) 0123 , m_modified(false) 0124 , m_parent(parentWidget) 0125 , m_rootValue(0) 0126 { 0127 assert(!m_self); // this class should only be constructed once 0128 m_self = this; 0129 0130 qDebug() << "parentWidget->objectName():" << parentWidget->objectName(); 0131 if (QString(parentWidget->objectName()).startsWith("KmPlot")) { 0132 setXMLFile("kmplot_part.rc"); 0133 m_readonly = false; 0134 } else { 0135 setXMLFile("kmplot_part_readonly.rc"); 0136 m_readonly = true; 0137 new BrowserExtension(this); // better integration with Konqueror 0138 } 0139 0140 m_coordsDialog = nullptr; 0141 m_constantEditor = nullptr; 0142 m_popupmenu = new QMenu(parentWidget); 0143 m_newPlotMenu = new QMenu(parentWidget); 0144 (void)new View(m_readonly, m_popupmenu, parentWidget); 0145 connect(View::self(), &View::setStatusBarText, this, &MainDlg::setReadOnlyStatusBarText); 0146 0147 m_functionEditor = nullptr; 0148 if (!m_readonly) { 0149 m_functionEditor = new FunctionEditor(m_newPlotMenu, parentWidget); 0150 static_cast<QMainWindow *>(parentWidget)->addDockWidget(Qt::LeftDockWidgetArea, m_functionEditor); 0151 } 0152 0153 setWidget(View::self()); 0154 View::self()->setFocusPolicy(Qt::ClickFocus); 0155 m_functionTools = new FunctionTools(m_parent); 0156 m_calculator = new Calculator(m_parent); 0157 setupActions(); 0158 XParser::self()->constants()->load(); 0159 kmplotio = new KmPlotIO(); 0160 m_config = KSharedConfig::openConfig(); 0161 m_recentFiles->loadEntries(m_config->group(QString())); 0162 0163 // BEGIN undo/redo stuff 0164 m_currentState = kmplotio->currentState(); 0165 m_saveCurrentStateTimer = new QTimer(this); 0166 m_saveCurrentStateTimer->setSingleShot(true); 0167 connect(m_saveCurrentStateTimer, &QTimer::timeout, this, &MainDlg::saveCurrentState); 0168 // END undo/redo stuff 0169 0170 // Let's create a Configure Dialog 0171 m_settingsDialog = new KConfigDialog(parentWidget, "settings", Settings::self()); 0172 0173 QMetaObject::invokeMethod(m_settingsDialog, 0174 "setHelp", 0175 Qt::DirectConnection, 0176 Q_ARG(QString, QString::fromLatin1("configuration")), 0177 Q_ARG(QString, QString::fromLatin1("kmplot"))); 0178 0179 // create and add the page(s) 0180 m_generalSettings = new SettingsPageGeneral(View::self()); 0181 m_colorSettings = new SettingsPageColor(View::self()); 0182 m_fontsSettings = new SettingsPageFonts(View::self()); 0183 m_diagramSettings = new SettingsPageDiagram(View::self()); 0184 0185 // Make sure the dialog is at a good default size (hmm QDialog should do this automatically?) 0186 QSize minSize = m_generalSettings->layout() 0187 ->minimumSize() 0188 .expandedTo(m_colorSettings->layout()->minimumSize()) 0189 .expandedTo(m_fontsSettings->layout()->minimumSize()) 0190 .expandedTo(m_diagramSettings->layout()->minimumSize()); 0191 m_generalSettings->setMinimumSize(minSize); 0192 0193 m_settingsDialog->addPage(m_generalSettings, i18n("General"), "kmplot", i18n("General Settings")); 0194 m_settingsDialog->addPage(m_diagramSettings, i18n("Diagram"), "coords", i18n("Diagram Appearance")); 0195 m_settingsDialog->addPage(m_colorSettings, i18n("Colors"), "preferences-desktop-color", i18n("Colors")); 0196 m_settingsDialog->addPage(m_fontsSettings, i18n("Fonts"), "preferences-desktop-font", i18n("Fonts")); 0197 // User edited the configuration - update your local copies of the 0198 // configuration data 0199 connect(m_settingsDialog, &KConfigDialog::settingsChanged, View::self(), QOverload<>::of(&View::drawPlot)); 0200 0201 new MainDlgAdaptor(this); 0202 QDBusConnection::sessionBus().registerObject("/maindlg", this); 0203 } 0204 0205 MainDlg::~MainDlg() 0206 { 0207 m_recentFiles->saveEntries(m_config->group(QString())); 0208 XParser::self()->constants()->save(); 0209 delete kmplotio; 0210 } 0211 0212 void MainDlg::setupActions() 0213 { 0214 // standard actions 0215 m_recentFiles = KStandardAction::openRecent(this, SLOT(slotOpenRecent(QUrl)), this); 0216 actionCollection()->addAction("file_open_recent", m_recentFiles); 0217 actionCollection()->addAction(KStandardAction::Print, "file_print", this, SLOT(slotPrint())); 0218 actionCollection()->addAction(KStandardAction::PrintPreview, "file_print_preview", this, SLOT(slotPrintPreview())); 0219 KStandardAction::save(this, SLOT(slotSave()), actionCollection()); 0220 KStandardAction::saveAs(this, SLOT(slotSaveas()), actionCollection()); 0221 0222 QAction *prefs = KStandardAction::preferences(this, SLOT(slotSettings()), actionCollection()); 0223 prefs->setText(i18n("Configure KmPlot...")); 0224 0225 // KmPlot specific actions 0226 0227 // BEGIN file menu 0228 QAction *exportAction = actionCollection()->addAction("export"); 0229 exportAction->setText(i18n("E&xport...")); 0230 exportAction->setIcon(QIcon::fromTheme("document-export")); 0231 connect(exportAction, &QAction::triggered, this, &MainDlg::slotExport); 0232 // END file menu 0233 0234 // BEGIN edit menu 0235 m_undoAction = KStandardAction::undo(this, SLOT(undo()), actionCollection()); 0236 m_undoAction->setEnabled(false); 0237 0238 m_redoAction = KStandardAction::redo(this, SLOT(redo()), actionCollection()); 0239 m_redoAction->setEnabled(false); 0240 0241 QAction *editAxes = actionCollection()->addAction("editaxes"); 0242 editAxes->setText(i18n("&Coordinate System...")); 0243 editAxes->setIcon(QIcon::fromTheme("coords.png")); 0244 connect(editAxes, &QAction::triggered, this, &MainDlg::editAxes); 0245 0246 QAction *editConstants = actionCollection()->addAction("editconstants"); 0247 editConstants->setText(i18n("&Constants...")); 0248 editConstants->setIcon(QIcon::fromTheme("editconstants.png")); 0249 connect(editConstants, &QAction::triggered, this, &MainDlg::editConstants); 0250 // END edit menu 0251 0252 // BEGIN view menu 0253 /// \todo check that new shortcuts work 0254 0255 QAction *zoomIn = actionCollection()->addAction("zoom_in"); 0256 zoomIn->setText(i18n("Zoom &In")); 0257 actionCollection()->setDefaultShortcut(zoomIn, QKeySequence(Qt::ControlModifier | Qt::Key_1)); 0258 zoomIn->setIcon(QIcon::fromTheme("zoom-in")); 0259 connect(zoomIn, &QAction::triggered, View::self(), QOverload<>::of(&View::zoomIn)); 0260 0261 QAction *zoomOut = actionCollection()->addAction("zoom_out"); 0262 zoomOut->setText(i18n("Zoom &Out")); 0263 actionCollection()->setDefaultShortcut(zoomOut, QKeySequence(Qt::ControlModifier | Qt::Key_2)); 0264 zoomOut->setIcon(QIcon::fromTheme("zoom-out")); 0265 connect(zoomOut, &QAction::triggered, View::self(), QOverload<>::of(&View::zoomOut)); 0266 0267 QAction *zoomTrig = actionCollection()->addAction("zoom_trig"); 0268 zoomTrig->setText(i18n("&Fit Widget to Trigonometric Functions")); 0269 connect(zoomTrig, &QAction::triggered, View::self(), &View::zoomToTrigonometric); 0270 0271 QAction *resetView = actionCollection()->addAction("reset_view"); 0272 resetView->setText(i18n("Reset View")); 0273 resetView->setIcon(QIcon::fromTheme("resetview")); 0274 connect(resetView, &QAction::triggered, this, &MainDlg::slotResetView); 0275 0276 View::self()->m_menuSliderAction = actionCollection()->add<KToggleAction>("options_configure_show_sliders"); 0277 View::self()->m_menuSliderAction->setText(i18n("Show Sliders")); 0278 connect(View::self()->m_menuSliderAction, &QAction::toggled, this, &MainDlg::toggleShowSliders); 0279 // END view menu 0280 0281 // BEGIN tools menu 0282 QAction *mnuCalculator = actionCollection()->addAction("calculator"); 0283 mnuCalculator->setText(i18n("Calculator")); 0284 mnuCalculator->setIcon(QIcon::fromTheme("system-run")); 0285 connect(mnuCalculator, &QAction::triggered, this, &MainDlg::calculator); 0286 0287 QAction *mnuArea = actionCollection()->addAction("grapharea"); 0288 mnuArea->setText(i18n("Plot &Area...")); 0289 connect(mnuArea, &QAction::triggered, this, &MainDlg::graphArea); 0290 0291 QAction *mnuMaxValue = actionCollection()->addAction("maximumvalue"); 0292 mnuMaxValue->setText(i18n("Find Ma&ximum...")); 0293 mnuMaxValue->setIcon(QIcon::fromTheme("maximum")); 0294 connect(mnuMaxValue, &QAction::triggered, this, &MainDlg::findMaximumValue); 0295 0296 QAction *mnuMinValue = actionCollection()->addAction("minimumvalue"); 0297 mnuMinValue->setText(i18n("Find Mi&nimum...")); 0298 mnuMinValue->setIcon(QIcon::fromTheme("minimum")); 0299 connect(mnuMinValue, &QAction::triggered, this, &MainDlg::findMinimumValue); 0300 // END tools menu 0301 0302 // BEGIN help menu 0303 QAction *namesAction = actionCollection()->addAction("names"); 0304 namesAction->setText(i18nc("@action:inmenu Help", "Predefined &Math Functions")); 0305 namesAction->setIcon(QIcon::fromTheme("functionhelp")); 0306 connect(namesAction, &QAction::triggered, this, &MainDlg::slotNames); 0307 // END help menu 0308 0309 // BEGIN new plots menu 0310 QAction *newFunction = actionCollection()->addAction("newcartesian"); 0311 newFunction->setText(i18n("Cartesian Plot")); 0312 newFunction->setIcon(QIcon::fromTheme("newfunction")); 0313 connect(newFunction, &QAction::triggered, m_functionEditor, &FunctionEditor::createCartesian); 0314 m_newPlotMenu->addAction(newFunction); 0315 0316 QAction *newParametric = actionCollection()->addAction("newparametric"); 0317 newParametric->setText(i18n("Parametric Plot")); 0318 newParametric->setIcon(QIcon::fromTheme("newparametric")); 0319 connect(newParametric, &QAction::triggered, m_functionEditor, &FunctionEditor::createParametric); 0320 m_newPlotMenu->addAction(newParametric); 0321 0322 QAction *newPolar = actionCollection()->addAction("newpolar"); 0323 newPolar->setText(i18n("Polar Plot")); 0324 newPolar->setIcon(QIcon::fromTheme("newpolar")); 0325 connect(newPolar, &QAction::triggered, m_functionEditor, &FunctionEditor::createPolar); 0326 m_newPlotMenu->addAction(newPolar); 0327 0328 QAction *newImplicit = actionCollection()->addAction("newimplicit"); 0329 newImplicit->setText(i18n("Implicit Plot")); 0330 newImplicit->setIcon(QIcon::fromTheme("newimplicit")); 0331 connect(newImplicit, &QAction::triggered, m_functionEditor, &FunctionEditor::createImplicit); 0332 m_newPlotMenu->addAction(newImplicit); 0333 0334 QAction *newDifferential = actionCollection()->addAction("newdifferential"); 0335 newDifferential->setText(i18n("Differential Plot")); 0336 newDifferential->setIcon(QIcon::fromTheme("newdifferential")); 0337 connect(newDifferential, &QAction::triggered, m_functionEditor, &FunctionEditor::createDifferential); 0338 m_newPlotMenu->addAction(newDifferential); 0339 // END new plots menu 0340 0341 // BEGIN function popup menu 0342 QAction *mnuEdit = actionCollection()->addAction("mnuedit"); 0343 mnuEdit->setText(i18n("&Edit")); 0344 m_firstFunctionAction = mnuEdit; 0345 mnuEdit->setIcon(QIcon::fromTheme("editplots")); 0346 connect(mnuEdit, &QAction::triggered, View::self(), &View::editCurrentPlot); 0347 m_popupmenu->addAction(mnuEdit); 0348 0349 QAction *mnuHide = actionCollection()->addAction("mnuhide"); 0350 mnuHide->setText(i18n("&Hide")); 0351 connect(mnuHide, &QAction::triggered, View::self(), &View::hideCurrentFunction); 0352 m_popupmenu->addAction(mnuHide); 0353 0354 QAction *mnuRemove = actionCollection()->addAction("mnuremove"); 0355 mnuRemove->setText(i18n("&Remove")); 0356 mnuRemove->setIcon(QIcon::fromTheme("edit-delete")); 0357 connect(mnuRemove, &QAction::triggered, View::self(), &View::removeCurrentPlot); 0358 m_popupmenu->addAction(mnuRemove); 0359 0360 m_popupmenu->addSeparator(); 0361 0362 QAction *animateFunction = actionCollection()->addAction("animateFunction"); 0363 animateFunction->setText(i18n("Animate Plot...")); 0364 connect(animateFunction, &QAction::triggered, View::self(), &View::animateFunction); 0365 m_popupmenu->addAction(animateFunction); 0366 m_popupmenu->addSeparator(); 0367 0368 m_popupmenu->addAction(mnuCalculator); 0369 m_popupmenu->addAction(mnuMinValue); 0370 m_popupmenu->addAction(mnuMaxValue); 0371 m_popupmenu->addAction(mnuArea); 0372 0373 QAction *copyXY = actionCollection()->addAction("copyXY"); 0374 copyXY->setText(i18n("Copy (x, y)")); 0375 connect(copyXY, &QAction::triggered, [] { 0376 QClipboard *cb = QApplication::clipboard(); 0377 QPointF currentXY = View::self()->getCrosshairPosition(); 0378 cb->setText( 0379 i18nc("Copied pair of coordinates (x, y)", "(%1, %2)", QLocale().toString(currentXY.x(), 'f', 5), QLocale().toString(currentXY.y(), 'f', 5)), 0380 QClipboard::Clipboard); 0381 }); 0382 m_popupmenu->addAction(copyXY); 0383 0384 QAction *copyRootValue = actionCollection()->addAction("copyRootValue"); 0385 copyRootValue->setText(i18n("Copy Root Value")); 0386 connect(View::self(), &View::updateRootValue, [this, copyRootValue](bool haveRoot, double rootValue) { 0387 copyRootValue->setVisible(haveRoot); 0388 m_rootValue = rootValue; 0389 }); 0390 connect(copyRootValue, &QAction::triggered, [this] { 0391 QClipboard *cb = QApplication::clipboard(); 0392 cb->setText(QLocale().toString(m_rootValue, 'f', 5), QClipboard::Clipboard); 0393 }); 0394 m_popupmenu->addAction(copyRootValue); 0395 0396 // END function popup menu 0397 } 0398 0399 void MainDlg::undo() 0400 { 0401 qDebug(); 0402 0403 if (m_undoStack.isEmpty()) 0404 return; 0405 0406 m_redoStack.push(m_currentState); 0407 m_currentState = m_undoStack.pop(); 0408 0409 kmplotio->restore(m_currentState); 0410 View::self()->drawPlot(); 0411 0412 m_undoAction->setEnabled(!m_undoStack.isEmpty()); 0413 m_redoAction->setEnabled(true); 0414 } 0415 0416 void MainDlg::redo() 0417 { 0418 qDebug(); 0419 0420 if (m_redoStack.isEmpty()) 0421 return; 0422 0423 m_undoStack.push(m_currentState); 0424 m_currentState = m_redoStack.pop(); 0425 0426 kmplotio->restore(m_currentState); 0427 View::self()->drawPlot(); 0428 0429 m_undoAction->setEnabled(true); 0430 m_redoAction->setEnabled(!m_redoStack.isEmpty()); 0431 } 0432 0433 void MainDlg::requestSaveCurrentState() 0434 { 0435 m_saveCurrentStateTimer->start(0); 0436 } 0437 void MainDlg::saveCurrentState() 0438 { 0439 m_redoStack.clear(); 0440 m_undoStack.push(m_currentState); 0441 m_currentState = kmplotio->currentState(); 0442 0443 // limit stack size to 100 items 0444 while (m_undoStack.count() > 100) 0445 m_undoStack.pop_front(); 0446 0447 m_undoAction->setEnabled(true); 0448 m_redoAction->setEnabled(false); 0449 0450 m_modified = true; 0451 } 0452 0453 void MainDlg::resetUndoRedo() 0454 { 0455 m_redoStack.clear(); 0456 m_undoStack.clear(); 0457 m_currentState = kmplotio->currentState(); 0458 m_undoAction->setEnabled(false); 0459 m_redoAction->setEnabled(false); 0460 } 0461 0462 bool MainDlg::checkModified() 0463 { 0464 if (m_modified) { 0465 #if KWIDGETSADDONS_VERSION < QT_VERSION_CHECK(5, 100, 0) 0466 int saveit = KMessageBox::warningYesNoCancel(m_parent, 0467 #else 0468 int saveit = KMessageBox::warningTwoActionsCancel(m_parent, 0469 #endif 0470 i18n("The plot has been modified.\n" 0471 "Do you want to save it?"), 0472 QString(), 0473 KStandardGuiItem::save(), 0474 KStandardGuiItem::discard()); 0475 switch (saveit) { 0476 #if KWIDGETSADDONS_VERSION < QT_VERSION_CHECK(5, 100, 0) 0477 case KMessageBox::Yes: 0478 #else 0479 case KMessageBox::PrimaryAction: 0480 #endif 0481 slotSave(); 0482 if (m_modified) // the user didn't saved the file 0483 return false; 0484 break; 0485 case KMessageBox::Cancel: 0486 return false; 0487 } 0488 } 0489 return true; 0490 } 0491 0492 void MainDlg::slotSave() 0493 { 0494 if (!m_modified || m_readonly) // don't save if no changes are made or readonly is enabled 0495 return; 0496 if (url().isEmpty()) // if there is no file name set yet 0497 slotSaveas(); 0498 else { 0499 if (!m_modified) // don't save if no changes are made 0500 return; 0501 0502 if (oldfileversion) { 0503 if (KMessageBox::warningContinueCancel(m_parent, 0504 i18n("This file is saved with an old file format; if you save it, you cannot open the file with older " 0505 "versions of KmPlot. Are you sure you want to continue?"), 0506 QString(), 0507 KGuiItem(i18n("Save New Format"))) 0508 == KMessageBox::Cancel) 0509 return; 0510 } 0511 kmplotio->save(this->url()); 0512 qDebug() << "saved"; 0513 m_modified = false; 0514 } 0515 } 0516 0517 void MainDlg::slotSaveas() 0518 { 0519 if (m_readonly) 0520 return; 0521 const QUrl url = 0522 QFileDialog::getSaveFileUrl(m_parent, i18n("Save As"), QUrl::fromLocalFile(QDir::currentPath()), i18n("KmPlot Files (*.fkt);;All Files (*)")); 0523 0524 if (url.isEmpty()) 0525 return; 0526 0527 if (!kmplotio->save(url)) 0528 KMessageBox::error(m_parent, i18n("The file could not be saved")); 0529 else { 0530 setUrl(url); 0531 m_recentFiles->addUrl(url); 0532 setWindowCaption(QUrl(this->url()).toString()); 0533 m_modified = false; 0534 } 0535 } 0536 0537 void MainDlg::slotExport() 0538 { 0539 QString filters; 0540 QMimeDatabase mimeDatabase; 0541 for (const QByteArray &mimeType : QImageWriter::supportedMimeTypes()) { 0542 const QString filter = mimeDatabase.mimeTypeForName(QLatin1String(mimeType)).filterString(); 0543 if (!filter.isEmpty()) { 0544 if (mimeType == QByteArrayLiteral("image/png")) { 0545 if (!filters.isEmpty()) { 0546 filters.prepend(QStringLiteral(";;")); 0547 } 0548 filters.prepend(filter); 0549 } else { 0550 if (!filters.isEmpty()) { 0551 filters.append(QStringLiteral(";;")); 0552 } 0553 filters.append(filter); 0554 } 0555 } 0556 } 0557 if (!filters.isEmpty()) { 0558 filters.append(QStringLiteral(";;")); 0559 } 0560 filters.append(i18n("Scalable Vector Graphics (*.svg)")); 0561 0562 QUrl url = QFileDialog::getSaveFileUrl(m_parent, i18nc("@title:window", "Export as Image"), QUrl::fromLocalFile(QDir::currentPath()), filters); 0563 0564 if (!url.isValid()) 0565 return; 0566 0567 QMimeType mimeType = mimeDatabase.mimeTypeForUrl(url); 0568 qDebug() << "mimetype: " << mimeType.name(); 0569 0570 bool isSvg = mimeType.name() == "image/svg+xml"; 0571 0572 bool saveOk = true; 0573 0574 if (isSvg) { 0575 QSvgGenerator img; 0576 img.setViewBox(QRect(QPoint(0, 0), View::self()->size())); 0577 0578 QFile file; 0579 QTemporaryFile tmp; 0580 0581 if (url.isLocalFile()) { 0582 file.setFileName(url.toLocalFile()); 0583 img.setOutputDevice(&file); 0584 } else { 0585 tmp.setFileTemplate(QDir::tempPath() + QLatin1String("/kmplot_XXXXXX") + QLatin1String(".svg")); 0586 img.setOutputDevice(&tmp); 0587 } 0588 0589 View::self()->draw(&img, View::SVG); 0590 0591 if (!url.isLocalFile()) { 0592 Q_CONSTEXPR int permission = -1; 0593 QFile file(tmp.fileName()); 0594 file.open(QIODevice::ReadOnly); 0595 KIO::StoredTransferJob *putjob = KIO::storedPut(file.readAll(), url, permission, KIO::JobFlag::Overwrite); 0596 saveOk &= putjob->exec(); 0597 file.close(); 0598 } 0599 } else { 0600 QPixmap img(View::self()->size()); 0601 View::self()->draw(&img, View::Pixmap); 0602 0603 QStringList types = mimeType.suffixes(); 0604 if (types.isEmpty()) 0605 return; // TODO error dialog? 0606 0607 if (url.isLocalFile()) 0608 saveOk = img.save(url.toLocalFile(), types.at(0).toLatin1()); 0609 else { 0610 QTemporaryFile tmp; 0611 tmp.open(); 0612 img.save(tmp.fileName(), types.at(0).toLatin1()); 0613 Q_CONSTEXPR int permission = -1; 0614 QFile file(tmp.fileName()); 0615 file.open(QIODevice::ReadOnly); 0616 KIO::StoredTransferJob *putjob = KIO::storedPut(file.readAll(), url, permission, KIO::JobFlag::Overwrite); 0617 saveOk = putjob->exec(); 0618 file.close(); 0619 } 0620 } 0621 0622 if (!saveOk) 0623 KMessageBox::error(m_parent, i18n("Sorry, something went wrong while saving to image \"%1\"", url.toString())); 0624 } 0625 0626 bool MainDlg::openFile() 0627 { 0628 if (url() == m_currentfile || !kmplotio->load(url())) { 0629 m_recentFiles->removeUrl(url()); // remove the file from the recent-opened-file-list 0630 setUrl(QUrl()); 0631 return false; 0632 } 0633 0634 m_currentfile = url(); 0635 m_recentFiles->addUrl(url()); 0636 setWindowCaption(url().toDisplayString()); 0637 resetUndoRedo(); 0638 View::self()->updateSliders(); 0639 View::self()->drawPlot(); 0640 return true; 0641 } 0642 0643 bool MainDlg::saveFile() 0644 { 0645 slotSave(); 0646 return !isModified(); 0647 } 0648 0649 void MainDlg::slotOpenRecent(const QUrl &url) 0650 { 0651 if (isModified() || !this->url().isEmpty()) // open the file in a new window 0652 { 0653 QDBusReply<void> reply = QDBusInterface(QDBusConnection::sessionBus().baseService(), "/kmplot", "org.kde.kmplot.KmPlot") 0654 .call(QDBus::Block, "openFileInNewWindow", url.url()); 0655 return; 0656 } 0657 0658 if (!kmplotio->load(url)) // if the loading fails 0659 { 0660 m_recentFiles->removeUrl(url); // remove the file from the recent-opened-file-list 0661 return; 0662 } 0663 m_currentfile = url; 0664 setUrl(url); 0665 m_recentFiles->setCurrentItem(-1); // don't select the item in the open-recent menu 0666 setWindowCaption(QUrl(this->url()).toString()); 0667 resetUndoRedo(); 0668 View::self()->updateSliders(); 0669 View::self()->drawPlot(); 0670 } 0671 0672 void MainDlg::slotPrint() 0673 { 0674 QPrinter prt(QPrinter::PrinterResolution); 0675 prt.setResolution(72); 0676 KPrinterDlg *printdlg = new KPrinterDlg(m_parent); 0677 printdlg->setObjectName("KmPlot page"); 0678 QPointer<QPrintDialog> printDialog = new QPrintDialog(&prt, m_parent); 0679 printDialog->setOptionTabs(QList<QWidget *>() << printdlg); 0680 printDialog->setWindowTitle(i18nc("@title:window", "Print Plot")); 0681 0682 if (printDialog->exec()) { 0683 setupPrinter(printdlg, &prt); 0684 } 0685 delete printDialog; 0686 } 0687 0688 void MainDlg::slotPrintPreview() 0689 { 0690 QPrinter prt(QPrinter::PrinterResolution); 0691 QPointer<QPrintPreviewDialog> preview = new QPrintPreviewDialog(&prt); 0692 QPointer<KPrinterDlg> printdlg = new KPrinterDlg(m_parent); 0693 QList<QToolBar *> toolbarlist = preview->findChildren<QToolBar *>(); 0694 if (!toolbarlist.isEmpty()) { 0695 QAction *printSettings = toolbarlist.first()->addAction(QIcon::fromTheme("configure"), i18n("Print Settings")); 0696 QList<QPrintPreviewWidget *> previewWidgetsList = preview->findChildren<QPrintPreviewWidget *>(); 0697 QPrintPreviewWidget *previewWidget = previewWidgetsList.first(); 0698 connect(printSettings, &QAction::triggered, [preview, previewWidget, printdlg] { 0699 QDialog *printSettingsDialog = new QDialog(preview, Qt::WindowFlags()); 0700 printSettingsDialog->setWindowTitle(i18nc("@title:window", "Print Settings")); 0701 QVBoxLayout *mainLayout = new QVBoxLayout; 0702 printSettingsDialog->setLayout(mainLayout); 0703 mainLayout->addWidget(printdlg); 0704 QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); 0705 connect(buttonBox, &QDialogButtonBox::accepted, [previewWidget, printSettingsDialog] { 0706 previewWidget->updatePreview(); 0707 printSettingsDialog->close(); 0708 }); 0709 connect(buttonBox, &QDialogButtonBox::rejected, printSettingsDialog, &QDialog::reject); 0710 mainLayout->addWidget(buttonBox); 0711 printSettingsDialog->show(); 0712 }); 0713 } 0714 connect(preview, &QPrintPreviewDialog::paintRequested, [this, &printdlg, &prt] { 0715 setupPrinter(printdlg, &prt); 0716 }); 0717 preview->exec(); 0718 delete printdlg; 0719 delete preview; 0720 } 0721 0722 void MainDlg::setupPrinter(KPrinterDlg *printDialog, QPrinter *printer) 0723 { 0724 View::self()->setPrintHeaderTable(printDialog->printHeaderTable()); 0725 View::self()->setPrintBackground(printDialog->printBackground()); 0726 View::self()->setPrintWidth(printDialog->printWidth()); 0727 View::self()->setPrintHeight(printDialog->printHeight()); 0728 View::self()->draw(printer, View::Printer); 0729 } 0730 0731 void MainDlg::editAxes() 0732 { 0733 coordsDialog()->show(); 0734 } 0735 0736 void MainDlg::editConstants() 0737 { 0738 if (!m_constantEditor) 0739 m_constantEditor = new KConstantEditor(m_parent); 0740 0741 m_constantEditor->show(); 0742 } 0743 0744 void MainDlg::editConstantsModal(QWidget *parent) 0745 { 0746 if (m_constantEditor) { 0747 m_constantEditor->hide(); 0748 } else { 0749 m_constantEditor = new KConstantEditor(parent); 0750 } 0751 0752 m_constantEditor->setModal(true); 0753 m_constantEditor->show(); 0754 } 0755 0756 bool MainDlg::fileExists(const QUrl &url) 0757 { 0758 bool fileExists = false; 0759 if (url.isValid()) { 0760 #if QT_VERSION_MAJOR < 6 0761 KIO::StatJob *statjob = KIO::statDetails(url, KIO::StatJob::SourceSide, KIO::StatBasic); 0762 #else 0763 KIO::StatJob *statjob = KIO::stat(url, KIO::StatJob::SourceSide, KIO::StatBasic); 0764 #endif 0765 bool noerror = statjob->exec(); 0766 if (noerror) { 0767 // We want a file 0768 fileExists = !statjob->statResult().isDir(); 0769 } 0770 } 0771 return fileExists; 0772 } 0773 0774 void MainDlg::slotNames() 0775 { 0776 KHelpClient::invokeHelp("func-predefined", "kmplot"); 0777 } 0778 0779 void MainDlg::slotResetView() 0780 { 0781 View::self()->animateZoom(QRectF(-8, -8, 16, 16)); 0782 } 0783 0784 void MainDlg::slotSettings() 0785 { 0786 // An instance of your dialog has already been created and has been cached, 0787 // so we want to display the cached dialog instead of creating 0788 // another one 0789 KConfigDialog::showDialog("settings"); 0790 } 0791 0792 void MainDlg::calculator() 0793 { 0794 m_calculator->show(); 0795 } 0796 0797 void MainDlg::findMinimumValue() 0798 { 0799 m_functionTools->init(FunctionTools::FindMinimum); 0800 m_functionTools->show(); 0801 } 0802 0803 void MainDlg::findMaximumValue() 0804 { 0805 m_functionTools->init(FunctionTools::FindMaximum); 0806 m_functionTools->show(); 0807 } 0808 0809 void MainDlg::graphArea() 0810 { 0811 m_functionTools->init(FunctionTools::CalculateArea); 0812 m_functionTools->show(); 0813 } 0814 0815 void MainDlg::toggleShowSliders() 0816 { 0817 View::self()->updateSliders(); 0818 } 0819 0820 void MainDlg::setReadOnlyStatusBarText(const QString &text) 0821 { 0822 setStatusBarText(text); 0823 } 0824 0825 bool MainDlg::queryClose() 0826 { 0827 return checkModified(); 0828 } 0829 0830 CoordsConfigDialog *MainDlg::coordsDialog() 0831 { 0832 if (!m_coordsDialog) { 0833 m_coordsDialog = new CoordsConfigDialog(m_parent); 0834 connect(m_coordsDialog, &CoordsConfigDialog::settingsChanged, View::self(), QOverload<>::of(&View::drawPlot)); 0835 } 0836 0837 return m_coordsDialog; 0838 } 0839 // END class MainDlg 0840 0841 /// BrowserExtension class 0842 BrowserExtension::BrowserExtension(MainDlg *parent) 0843 #if QT_VERSION_MAJOR == 5 0844 : KParts::BrowserExtension(parent) 0845 #else 0846 : KParts::NavigationExtension(parent) 0847 #endif 0848 { 0849 emit enableAction("print", true); 0850 setURLDropHandlingEnabled(true); 0851 } 0852 0853 void BrowserExtension::print() 0854 { 0855 static_cast<MainDlg *>(parent())->slotPrint(); 0856 } 0857 0858 #include "maindlg.moc" 0859 0860 #include "moc_maindlg.cpp"