Warning, file /education/kmplot/kmplot/maindlg.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

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"