1 /*
2     KmPlot - a math. function plotter for the KDE-Desktop
3 
4     SPDX-FileCopyrightText: 1998, 1999, 2000, 2002 Klaus-Dieter Möller <kd.moeller@t-online.de>
5     SPDX-FileCopyrightText: 2006  David Saxton <david@bluehaze.org>
6 
7     This file is part of the KDE Project.
8     KmPlot is part of the KDE-EDU Project.
9 
10     SPDX-License-Identifier: GPL-2.0-or-later
11 
12 */
13 
14 #include "maindlg.h"
15 
16 // Qt includes
17 #include <QClipboard>
18 #include <QDebug>
19 #include <QFileDialog>
20 #include <QIcon>
21 #include <QImageWriter>
22 #include <QMainWindow>
23 #include <QMimeDatabase>
24 #include <QMimeType>
25 #include <QPixmap>
26 #include <QPrintDialog>
27 #include <QPrintPreviewDialog>
28 #include <QPrintPreviewWidget>
29 #include <QSvgGenerator>
30 #include <QTemporaryFile>
31 #include <QTimer>
32 
33 // KDE includes
34 #include <KActionCollection>
35 #include <KConfigDialog>
36 #include <KConfigDialogManager>
37 #include <KHelpClient>
38 #include <KIO/StatJob>
39 #include <KIO/StoredTransferJob>
40 #include <KLineEdit>
41 #include <KMessageBox>
42 #include <KStandardAction>
43 #include <KToolBar>
44 #include <KToolInvocation>
45 
46 // local includes
47 #include "calculator.h"
48 #include "functiontools.h"
49 #include "functioneditor.h"
50 #include "kconstanteditor.h"
51 #include "xparser.h"
52 
53 #include "settings.h"
54 #include "ui_settingspagecolor.h"
55 #include "ui_settingspagefonts.h"
56 #include "ui_settingspagegeneral.h"
57 #include "ui_settingspagediagram.h"
58 #include "ksliderwindow.h"
59 #include "maindlgadaptor.h"
60 
61 #include <assert.h>
62 
63 class XParser;
64 class KmPlotIO;
65 
66 
67 class SettingsPageColor : public QWidget, public Ui::SettingsPageColor
68 {
69     public:
SettingsPageColor(QWidget * parent=0)70         SettingsPageColor( QWidget * parent = 0 )
71     : QWidget( parent )
72         {
73 			setupUi(this);
74 			layout()->setContentsMargins(0, 0, 0, 0);
75 		}
76 };
77 
78 class SettingsPageFonts : public QWidget, public Ui::SettingsPageFonts
79 {
80     public:
SettingsPageFonts(QWidget * parent=0)81         SettingsPageFonts( QWidget * parent = 0 )
82 	: QWidget( parent )
83 		{
84 			setupUi(this);
85 			layout()->setContentsMargins(0, 0, 0, 0);
86 		}
87 };
88 
89 class SettingsPageGeneral : public QWidget, public Ui::SettingsPageGeneral
90 {
91     public:
SettingsPageGeneral(QWidget * parent=0)92         SettingsPageGeneral( QWidget * parent = 0 )
93 	: QWidget( parent )
94 		{
95 			setupUi(this);
96                         layout()->setContentsMargins(0, 0, 0, 0);
97 		}
98 };
99 
100 class SettingsPageDiagram : public QWidget, public Ui::SettingsPageDiagram
101 {
102 	public:
SettingsPageDiagram(QWidget * parent=0)103 		SettingsPageDiagram( QWidget * parent = 0 )
104 	: QWidget( parent )
105 		{
106 			setupUi(this);
107 			layout()->setContentsMargins(0, 0, 0, 0);
108 		}
109 };
110 
111 bool MainDlg::oldfileversion;
112 MainDlg * MainDlg::m_self = 0;
113 
114 K_PLUGIN_CLASS_WITH_JSON(MainDlg, "kmplot_part.json")
115 
116 //BEGIN class MainDlg
MainDlg(QWidget * parentWidget,QObject * parent,const QVariantList &)117 MainDlg::MainDlg(QWidget *parentWidget, QObject *parent, const QVariantList& ) :
118 		KParts::ReadWritePart( parent ),
119 		m_recentFiles( 0 ),
120 		m_modified(false),
121 		m_parent(parentWidget),
122 		m_rootValue( 0 )
123 {
124 	assert( !m_self ); // this class should only be constructed once
125 	m_self = this;
126 
127 	qDebug() << "parentWidget->objectName():" << parentWidget->objectName();
128 	if ( QString(parentWidget->objectName()).startsWith("KmPlot") )
129 	{
130 		setXMLFile("kmplot_part.rc");
131 		m_readonly = false;
132 	}
133 	else
134 	{
135 		setXMLFile("kmplot_part_readonly.rc");
136 		m_readonly = true;
137 		new BrowserExtension(this); // better integration with Konqueror
138 	}
139 
140 	m_coordsDialog = 0;
141 	m_constantEditor = 0;
142 	m_popupmenu = new QMenu( parentWidget );
143 	m_newPlotMenu = new QMenu( parentWidget );
144 	(void) new View( m_readonly, m_popupmenu, parentWidget );
145 	connect( View::self(), &View::setStatusBarText, this, &MainDlg::setReadOnlyStatusBarText );
146 
147 	m_functionEditor = 0;
148 	if ( !m_readonly )
149 	{
150 		m_functionEditor = new FunctionEditor( m_newPlotMenu, parentWidget );
151 		static_cast<QMainWindow*>(parentWidget)->addDockWidget( Qt::LeftDockWidgetArea, m_functionEditor );
152 	}
153 
154 	setWidget( View::self() );
155 	View::self()->setFocusPolicy(Qt::ClickFocus);
156 	m_functionTools = new FunctionTools(m_parent);
157 	m_calculator = new Calculator( m_parent );
158 	setupActions();
159 	XParser::self()->constants()->load();
160 	kmplotio = new KmPlotIO();
161 	m_config = KSharedConfig::openConfig();
162 	m_recentFiles->loadEntries( m_config->group( QString() ) );
163 
164 
165 	//BEGIN undo/redo stuff
166 	m_currentState = kmplotio->currentState();
167 	m_saveCurrentStateTimer = new QTimer( this );
168 	m_saveCurrentStateTimer->setSingleShot( true );
169 	connect( m_saveCurrentStateTimer, &QTimer::timeout, this, &MainDlg::saveCurrentState );
170 	//END undo/redo stuff
171 
172 	// Let's create a Configure Dialog
173 	m_settingsDialog = new KConfigDialog( parentWidget, "settings", Settings::self() );
174 
175 	QMetaObject::invokeMethod(m_settingsDialog, "setHelp", Qt::DirectConnection, Q_ARG(QString, QString::fromLatin1("configuration")), Q_ARG(QString, QString::fromLatin1("kmplot")));
176 
177 	// create and add the page(s)
178 	m_generalSettings = new SettingsPageGeneral( View::self() );
179 	m_colorSettings = new SettingsPageColor( View::self() );
180 	m_fontsSettings = new SettingsPageFonts( View::self() );
181 	m_diagramSettings = new SettingsPageDiagram( View::self() );
182 
183 	// Make sure the dialog is at a good default size (hmm QDialog should do this automatically?)
184 	QSize minSize = m_generalSettings->layout()->minimumSize()
185 			.expandedTo( m_colorSettings->layout()->minimumSize() )
186 			.expandedTo( m_fontsSettings->layout()->minimumSize() )
187 			.expandedTo( m_diagramSettings->layout()->minimumSize() );
188 	m_generalSettings->setMinimumSize( minSize );
189 
190 	m_settingsDialog->addPage( m_generalSettings, i18n("General"), "kmplot", i18n("General Settings") );
191 	m_settingsDialog->addPage( m_diagramSettings, i18n("Diagram"), "coords", i18n("Diagram Appearance") );
192 	m_settingsDialog->addPage( m_colorSettings, i18n("Colors"), "preferences-desktop-color", i18n("Colors") );
193 	m_settingsDialog->addPage( m_fontsSettings, i18n("Fonts"), "preferences-desktop-font", i18n("Fonts") );
194 	// User edited the configuration - update your local copies of the
195 	// configuration data
196 	connect( m_settingsDialog, &KConfigDialog::settingsChanged, View::self(), QOverload<>::of(&View::drawPlot) );
197 
198 
199     new MainDlgAdaptor(this);
200     QDBusConnection::sessionBus().registerObject("/maindlg", this);
201 
202 }
203 
~MainDlg()204 MainDlg::~MainDlg()
205 {
206         m_recentFiles->saveEntries( m_config->group( QString() ) );
207 	XParser::self()->constants()->save();
208 	delete kmplotio;
209 }
210 
setupActions()211 void MainDlg::setupActions()
212 {
213 	// standard actions
214         m_recentFiles = KStandardAction::openRecent( this, SLOT(slotOpenRecent(QUrl)), this );
215         actionCollection()->addAction( "file_open_recent", m_recentFiles );
216 	actionCollection()->addAction( KStandardAction::Print, "file_print", this, SLOT(slotPrint()) );
217 	actionCollection()->addAction( KStandardAction::PrintPreview, "file_print_preview", this, SLOT(slotPrintPreview()) );
218 	KStandardAction::save( this, SLOT(slotSave()), actionCollection() );
219 	KStandardAction::saveAs( this, SLOT(slotSaveas()), actionCollection() );
220 
221 	QAction *prefs  = KStandardAction::preferences( this, SLOT(slotSettings()), actionCollection());
222 	prefs->setText( i18n( "Configure KmPlot..." ) );
223 
224 	// KmPlot specific actions
225 
226 	//BEGIN file menu
227 	QAction * exportAction = actionCollection()->addAction( "export" );
228         exportAction->setText( i18n( "E&xport..." ) );
229         exportAction->setIcon( QIcon::fromTheme( "document-export" ) );
230 	connect( exportAction, &QAction::triggered, this, &MainDlg::slotExport );
231 	//END file menu
232 
233 
234 	//BEGIN edit menu
235 	m_undoAction = KStandardAction::undo( this, SLOT(undo()), actionCollection() );
236 	m_undoAction->setEnabled( false );
237 
238 	m_redoAction = KStandardAction::redo( this, SLOT(redo()), actionCollection() );
239 	m_redoAction->setEnabled( false );
240 
241 	QAction * editAxes = actionCollection()->addAction( "editaxes" );
242         editAxes->setText( i18n( "&Coordinate System..." ) );
243 	editAxes->setIcon( QIcon::fromTheme("coords.png") );
244 	connect( editAxes, &QAction::triggered, this, &MainDlg::editAxes );
245 
246 	QAction * editConstants = actionCollection()->addAction( "editconstants" );
247         editConstants->setText( i18n( "&Constants..." ) );
248 	editConstants->setIcon( QIcon::fromTheme("editconstants.png") );
249 	connect( editConstants, &QAction::triggered, this, &MainDlg::editConstants );
250 	//END edit menu
251 
252 
253 	//BEGIN view menu
254 	/// \todo check that new shortcuts work
255 
256 	QAction * zoomIn = actionCollection()->addAction( "zoom_in" );
257         zoomIn->setText( i18n("Zoom &In") );
258 	actionCollection()->setDefaultShortcut( zoomIn, QKeySequence(Qt::ControlModifier | Qt::Key_1) );
259 	zoomIn->setIcon( QIcon::fromTheme("zoom-in") );
260 	connect( zoomIn, &QAction::triggered, View::self(), QOverload<>::of(&View::zoomIn) );
261 
262 	QAction * zoomOut = actionCollection()->addAction( "zoom_out" );
263         zoomOut->setText(i18n("Zoom &Out"));
264 	actionCollection()->setDefaultShortcut( zoomOut, QKeySequence(Qt::ControlModifier | Qt::Key_2) );
265 	zoomOut->setIcon( QIcon::fromTheme("zoom-out") );
266 	connect( zoomOut, &QAction::triggered, View::self(), QOverload<>::of(&View::zoomOut) );
267 
268 	QAction * zoomTrig = actionCollection()->addAction( "zoom_trig" );
269         zoomTrig->setText( i18n("&Fit Widget to Trigonometric Functions") );
270 	connect( zoomTrig, &QAction::triggered, View::self(), &View::zoomToTrigonometric );
271 
272 	QAction * resetView = actionCollection()->addAction( "reset_view" );
273 	resetView->setText( i18n( "Reset View" ) );
274 	resetView->setIcon( QIcon::fromTheme("resetview") );
275 	connect( resetView, &QAction::triggered, this, &MainDlg::slotResetView );
276 
277 	View::self()->m_menuSliderAction = actionCollection()->add<KToggleAction>( "options_configure_show_sliders" );
278 	View::self()->m_menuSliderAction->setText( i18n( "Show Sliders" ) );
279 	connect( View::self()->m_menuSliderAction, &QAction::toggled, this, &MainDlg::toggleShowSliders );
280 	//END view menu
281 
282 
283 	//BEGIN tools menu
284 	QAction *mnuCalculator = actionCollection()->addAction( "calculator" );
285 	mnuCalculator->setText( i18n( "Calculator") );
286 	mnuCalculator->setIcon( QIcon::fromTheme("system-run") );
287 	connect( mnuCalculator, &QAction::triggered, this, &MainDlg::calculator );
288 
289 	QAction *mnuArea = actionCollection()->addAction( "grapharea" );
290         mnuArea->setText( i18n( "Plot &Area..." ) );
291 	connect( mnuArea, &QAction::triggered, this, &MainDlg::graphArea );
292 
293 	QAction *mnuMaxValue = actionCollection()->addAction( "maximumvalue" );
294         mnuMaxValue->setText( i18n( "Find Ma&ximum..." ) );
295 	mnuMaxValue->setIcon( QIcon::fromTheme("maximum") );
296 	connect( mnuMaxValue, &QAction::triggered, this, &MainDlg::findMaximumValue );
297 
298 	QAction *mnuMinValue = actionCollection()->addAction( "minimumvalue" );
299         mnuMinValue->setText( i18n( "Find Mi&nimum..." ) );
300 	mnuMinValue->setIcon( QIcon::fromTheme("minimum") );
301 	connect( mnuMinValue, &QAction::triggered, this, &MainDlg::findMinimumValue );
302 	//END tools menu
303 
304 
305 	//BEGIN help menu
306 	QAction * namesAction = actionCollection()->addAction( "names" );
307         namesAction->setText( i18nc("@action:inmenu Help", "Predefined &Math Functions") );
308 	namesAction->setIcon( QIcon::fromTheme("functionhelp") );
309 	connect( namesAction, &QAction::triggered, this, &MainDlg::slotNames );
310 	//END help menu
311 
312 
313 	//BEGIN new plots menu
314 	QAction * newFunction = actionCollection()->addAction( "newcartesian" );
315         newFunction->setText( i18n( "Cartesian Plot" ) );
316 	newFunction->setIcon( QIcon::fromTheme("newfunction") );
317 	connect( newFunction, &QAction::triggered, m_functionEditor, &FunctionEditor::createCartesian );
318 	m_newPlotMenu->addAction( newFunction );
319 
320 	QAction * newParametric = actionCollection()->addAction( "newparametric" );
321         newParametric->setText( i18n( "Parametric Plot" ) );
322 	newParametric->setIcon( QIcon::fromTheme("newparametric") );
323 	connect( newParametric, &QAction::triggered, m_functionEditor, &FunctionEditor::createParametric );
324 	m_newPlotMenu->addAction( newParametric );
325 
326 	QAction * newPolar = actionCollection()->addAction( "newpolar" );
327         newPolar->setText( i18n( "Polar Plot" ) );
328 	newPolar->setIcon( QIcon::fromTheme("newpolar") );
329 	connect( newPolar, &QAction::triggered, m_functionEditor, &FunctionEditor::createPolar );
330 	m_newPlotMenu->addAction( newPolar );
331 
332 	QAction * newImplicit = actionCollection()->addAction( "newimplicit" );
333         newImplicit->setText( i18n( "Implicit Plot" ) );
334 	newImplicit->setIcon( QIcon::fromTheme("newimplicit") );
335 	connect( newImplicit, &QAction::triggered, m_functionEditor, &FunctionEditor::createImplicit );
336 	m_newPlotMenu->addAction( newImplicit );
337 
338 	QAction * newDifferential = actionCollection()->addAction( "newdifferential" );
339         newDifferential->setText( i18n( "Differential Plot" ) );
340 	newDifferential->setIcon( QIcon::fromTheme("newdifferential") );
341 	connect( newDifferential, &QAction::triggered, m_functionEditor, &FunctionEditor::createDifferential );
342 	m_newPlotMenu->addAction( newDifferential );
343 	//END new plots menu
344 
345 
346 	//BEGIN function popup menu
347 	QAction *mnuEdit = actionCollection()->addAction( "mnuedit"  );
348         mnuEdit->setText(i18n("&Edit"));
349 	m_firstFunctionAction = mnuEdit;
350 	mnuEdit->setIcon( QIcon::fromTheme("editplots") );
351 	connect(mnuEdit, &QAction::triggered, View::self(), &View::editCurrentPlot );
352 	m_popupmenu->addAction( mnuEdit );
353 
354 	QAction *mnuHide = actionCollection()->addAction( "mnuhide" );
355         mnuHide->setText( i18n("&Hide") );
356 	connect( mnuHide, &QAction::triggered, View::self(), &View::hideCurrentFunction );
357 	m_popupmenu->addAction( mnuHide );
358 
359 	QAction *mnuRemove = actionCollection()->addAction( "mnuremove"  );
360         mnuRemove->setText(i18n("&Remove"));
361 	mnuRemove->setIcon( QIcon::fromTheme("edit-delete") );
362 	connect( mnuRemove, &QAction::triggered, View::self(), &View::removeCurrentPlot );
363 	m_popupmenu->addAction( mnuRemove );
364 
365 	m_popupmenu->addSeparator();
366 
367 	QAction * animateFunction = actionCollection()->addAction( "animateFunction" );
368         animateFunction->setText(i18n("Animate Plot..."));
369 	connect( animateFunction, &QAction::triggered, View::self(), &View::animateFunction );
370 	m_popupmenu->addAction( animateFunction );
371 	m_popupmenu->addSeparator();
372 
373 	m_popupmenu->addAction( mnuCalculator );
374 	m_popupmenu->addAction( mnuMinValue );
375 	m_popupmenu->addAction( mnuMaxValue );
376 	m_popupmenu->addAction( mnuArea );
377 
378 	QAction * copyXY = actionCollection()->addAction( "copyXY" );
379 	copyXY->setText(i18n("Copy (x, y)"));
380 	connect( copyXY, &QAction::triggered, []{
381 		QClipboard * cb = QApplication::clipboard();
382 		QPointF currentXY = View::self()->getCrosshairPosition();
383 		cb->setText( i18nc("Copied pair of coordinates (x, y)", "(%1, %2)", QLocale().toString( currentXY.x(), 'f', 5 ), QLocale().toString( currentXY.y(), 'f', 5 )), QClipboard::Clipboard );
384 	} );
385 	m_popupmenu->addAction( copyXY );
386 
387 	QAction * copyRootValue = actionCollection()->addAction( "copyRootValue" );
388 	copyRootValue->setText(i18n("Copy Root Value"));
389 	connect( View::self(), &View::updateRootValue, [this, copyRootValue]( bool haveRoot, double rootValue ){
390 		copyRootValue->setVisible(haveRoot);
391 		m_rootValue = rootValue;
392 	} );
393 	connect( copyRootValue, &QAction::triggered, [this]{
394 		QClipboard * cb = QApplication::clipboard();
395 		cb->setText( QLocale().toString( m_rootValue, 'f', 5 ), QClipboard::Clipboard );
396 	} );
397 	m_popupmenu->addAction( copyRootValue );
398 
399 	//END function popup menu
400 }
401 
402 
undo()403 void MainDlg::undo()
404 {
405 	qDebug() ;
406 
407 	if ( m_undoStack.isEmpty() )
408 		return;
409 
410 	m_redoStack.push( m_currentState );
411 	m_currentState = m_undoStack.pop();
412 
413 	kmplotio->restore( m_currentState );
414 	View::self()->drawPlot();
415 
416 	m_undoAction->setEnabled( !m_undoStack.isEmpty() );
417 	m_redoAction->setEnabled( true );
418 }
419 
420 
redo()421 void MainDlg::redo()
422 {
423 	qDebug() ;
424 
425 	if ( m_redoStack.isEmpty() )
426 		return;
427 
428 	m_undoStack.push( m_currentState );
429 	m_currentState = m_redoStack.pop();
430 
431 	kmplotio->restore( m_currentState );
432 	View::self()->drawPlot();
433 
434 	m_undoAction->setEnabled( true );
435 	m_redoAction->setEnabled( !m_redoStack.isEmpty() );
436 }
437 
438 
requestSaveCurrentState()439 void MainDlg::requestSaveCurrentState()
440 {
441 	m_saveCurrentStateTimer->start( 0 );
442 }
saveCurrentState()443 void MainDlg::saveCurrentState( )
444 {
445 	m_redoStack.clear();
446 	m_undoStack.push( m_currentState );
447 	m_currentState = kmplotio->currentState();
448 
449 	// limit stack size to 100 items
450 	while ( m_undoStack.count() > 100 )
451 		m_undoStack.pop_front();
452 
453 	m_undoAction->setEnabled( true );
454 	m_redoAction->setEnabled( false );
455 
456 	m_modified = true;
457 }
458 
459 
resetUndoRedo()460 void MainDlg::resetUndoRedo()
461 {
462 	m_redoStack.clear();
463 	m_undoStack.clear();
464 	m_currentState = kmplotio->currentState();
465 	m_undoAction->setEnabled( false );
466 	m_redoAction->setEnabled( false );
467 }
468 
469 
checkModified()470 bool MainDlg::checkModified()
471 {
472 	if( m_modified )
473 	{
474 		int saveit = KMessageBox::warningYesNoCancel( m_parent, i18n( "The plot has been modified.\n"
475 		             "Do you want to save it?" ), QString(), KStandardGuiItem::save(), KStandardGuiItem::discard() );
476 		switch( saveit )
477 		{
478 			case KMessageBox::Yes:
479 				slotSave();
480 				if ( m_modified) // the user didn't saved the file
481 					return false;
482 				break;
483 			case KMessageBox::Cancel:
484 				return false;
485 		}
486 	}
487 	return true;
488 }
489 
490 
slotSave()491 void MainDlg::slotSave()
492 {
493 	if ( !m_modified || m_readonly) //don't save if no changes are made or readonly is enabled
494 		return;
495 	if ( url().isEmpty() )            // if there is no file name set yet
496 		slotSaveas();
497 	else
498 	{
499 		if ( !m_modified) //don't save if no changes are made
500 			return;
501 
502 		if ( oldfileversion)
503 		{
504 			if ( KMessageBox::warningContinueCancel( m_parent, i18n( "This file is saved with an old file format; if you save it, you cannot open the file with older versions of KmPlot. Are you sure you want to continue?" ), QString(), KGuiItem(i18n("Save New Format")) ) == KMessageBox::Cancel)
505 				return;
506 		}
507 		kmplotio->save( this->url() );
508 		qDebug() << "saved";
509 		m_modified = false;
510 	}
511 
512 }
513 
slotSaveas()514 void MainDlg::slotSaveas()
515 {
516 	if (m_readonly)
517 		return;
518 	const QUrl url = QFileDialog::getSaveFileUrl(m_parent, i18n( "Save As" ), QUrl::fromLocalFile(QDir::currentPath()),
519 			    i18n( "KmPlot Files (*.fkt);;All Files (*)" )
520 			      );
521 
522 	if ( url.isEmpty() )
523 		return;
524 
525 	if ( !kmplotio->save( url ) )
526 		KMessageBox::error(m_parent, i18n("The file could not be saved") );
527 	else
528 	{
529 		setUrl(url);
530 		m_recentFiles->addUrl( url );
531 		setWindowCaption( QUrl(this->url()).toString() );
532 		m_modified = false;
533 	}
534 }
535 
slotExport()536 void MainDlg::slotExport()
537 {
538 	QString filters;
539 	QMimeDatabase mimeDatabase;
540 	for (const QByteArray &mimeType : QImageWriter::supportedMimeTypes()) {
541 		const QString filter = mimeDatabase.mimeTypeForName(QLatin1String(mimeType)).filterString();
542 		if (!filter.isEmpty()) {
543 			if (mimeType == QByteArrayLiteral("image/png")) {
544 				if (!filters.isEmpty()) {
545 					filters.prepend(QStringLiteral(";;"));
546 				}
547 				filters.prepend(filter);
548 			} else {
549 				if (!filters.isEmpty()) {
550 					filters.append(QStringLiteral(";;"));
551                                 }
552 				filters.append(filter);
553 			}
554 		}
555 	}
556 	if (!filters.isEmpty()) {
557 		filters.append(QStringLiteral(";;"));
558 	}
559 	filters.append(i18n("Scalable Vector Graphics (*.svg)"));
560 
561 	QUrl url = QFileDialog::getSaveFileUrl(m_parent, i18nc("@title:window", "Export as Image"), QUrl::fromLocalFile(QDir::currentPath()), filters);
562 
563 	if ( !url.isValid() )
564 		return;
565 
566 	QMimeType mimeType = mimeDatabase.mimeTypeForUrl( url );
567 	qDebug() << "mimetype: " << mimeType.name();
568 
569 	bool isSvg = mimeType.name() == "image/svg+xml";
570 
571 	bool saveOk = true;
572 
573 	if ( isSvg )
574 	{
575 		QSvgGenerator img;
576 		img.setViewBox( QRect( QPoint(0, 0), View::self()->size() ) );
577 
578 		QFile file;
579 		QTemporaryFile tmp;
580 
581 		if ( url.isLocalFile() )
582 		{
583 			file.setFileName( url.toLocalFile() );
584 			img.setOutputDevice( &file );
585 		}
586 		else
587 		{
588  			tmp.setFileTemplate(QDir::tempPath() + QLatin1String("/kmplot_XXXXXX") + QLatin1String(".svg"));
589 			img.setOutputDevice( &tmp );
590 		}
591 
592 		View::self()->draw( &img, View::SVG );
593 
594 		if ( !url.isLocalFile() )
595 		{
596 		    Q_CONSTEXPR int permission = -1;
597 		    QFile file(tmp.fileName());
598 		    file.open(QIODevice::ReadOnly);
599 		    KIO::StoredTransferJob *putjob = KIO::storedPut(file.readAll(), url, permission, KIO::JobFlag::Overwrite);
600 		    saveOk &= putjob->exec();
601 		    file.close();
602 		}
603 	}
604 	else
605 	{
606 		QPixmap img( View::self()->size() );
607 		View::self()->draw( & img, View::Pixmap );
608 
609 		QStringList types = mimeType.suffixes();
610 		if ( types.isEmpty() )
611 			return; // TODO error dialog?
612 
613 		if ( url.isLocalFile() )
614 			saveOk = img.save( url.toLocalFile(), types.at(0).toLatin1() );
615 		else
616 		{
617 			QTemporaryFile tmp;
618 			tmp.open();
619 			img.save( tmp.fileName(), types.at(0).toLatin1() );
620 			Q_CONSTEXPR int permission = -1;
621 			QFile file(tmp.fileName());
622 			file.open(QIODevice::ReadOnly);
623 			KIO::StoredTransferJob *putjob = KIO::storedPut(file.readAll(), url, permission, KIO::JobFlag::Overwrite);
624 			saveOk = putjob->exec();
625 			file.close();
626 		}
627 	}
628 
629 	if ( !saveOk )
630 	    KMessageBox::error(m_parent,
631 			       i18n("Sorry, something went wrong while saving to image \"%1\"",
632 				    url.toString()));
633 }
634 
635 
openFile()636 bool MainDlg::openFile()
637 {
638 	if (url()==m_currentfile || !kmplotio->load( url() ) )
639 	{
640 		m_recentFiles->removeUrl( url() ); //remove the file from the recent-opened-file-list
641 		setUrl(QUrl());
642 		return false;
643 	}
644 
645 	m_currentfile = url();
646 	m_recentFiles->addUrl( url() );
647 	setWindowCaption( url().toDisplayString() );
648 	resetUndoRedo();
649 	View::self()->updateSliders();
650 	View::self()->drawPlot();
651 	return true;
652 }
653 
saveFile()654 bool MainDlg::saveFile()
655 {
656     slotSave();
657     return !isModified();
658 }
659 
slotOpenRecent(const QUrl & url)660 void MainDlg::slotOpenRecent( const QUrl &url )
661 {
662  	if( isModified() || !this->url().isEmpty() ) // open the file in a new window
663  	{
664 		QDBusReply<void> reply = QDBusInterface( QDBusConnection::sessionBus().baseService(), "/kmplot", "org.kde.kmplot.KmPlot" ).call( QDBus::Block, "openFileInNewWindow", url.url() );
665 		return;
666 	}
667 
668 	if ( !kmplotio->load( url ) ) //if the loading fails
669 	{
670 		m_recentFiles->removeUrl(url ); //remove the file from the recent-opened-file-list
671 		return;
672 	}
673     m_currentfile = url;
674     setUrl(url);
675     m_recentFiles->setCurrentItem(-1); //don't select the item in the open-recent menu
676     setWindowCaption( QUrl(this->url()).toString() );
677 	resetUndoRedo();
678     View::self()->updateSliders();
679     View::self()->drawPlot();
680 }
681 
slotPrint()682 void MainDlg::slotPrint()
683 {
684 	QPrinter prt( QPrinter::PrinterResolution );
685 	prt.setResolution( 72 );
686 	KPrinterDlg* printdlg = new KPrinterDlg( m_parent );
687 	printdlg->setObjectName( "KmPlot page" );
688 	QPointer<QPrintDialog> printDialog = new QPrintDialog( &prt, m_parent );
689 	printDialog->setOptionTabs( QList<QWidget*>() << printdlg );
690 	printDialog->setWindowTitle( i18nc("@title:window", "Print Plot") );
691 
692 	if (printDialog->exec())
693 	{
694 		setupPrinter(printdlg, &prt);
695 	}
696         delete printDialog;
697 }
698 
slotPrintPreview()699 void MainDlg::slotPrintPreview()
700 {
701 	QPrinter prt( QPrinter::PrinterResolution );
702 	QPointer<QPrintPreviewDialog> preview = new QPrintPreviewDialog( &prt );
703 	QPointer<KPrinterDlg> printdlg = new KPrinterDlg( m_parent );
704 	QList<QToolBar *> toolbarlist = preview->findChildren<QToolBar *>();
705 	if(!toolbarlist.isEmpty())
706 	{
707 		QAction *printSettings = toolbarlist.first()->addAction( QIcon::fromTheme( "configure" ), i18n("Print Settings") );
708 		QList<QPrintPreviewWidget*> previewWidgetsList = preview->findChildren<QPrintPreviewWidget*>();
709 		QPrintPreviewWidget *previewWidget = previewWidgetsList.first();
710 		connect( printSettings, &QAction::triggered, [preview, previewWidget, printdlg]{
711 			QDialog *printSettingsDialog = new QDialog( preview, Qt::WindowFlags() );
712 			printSettingsDialog->setWindowTitle( i18nc("@title:window", "Print Settings") );
713 			QVBoxLayout *mainLayout = new QVBoxLayout;
714 			printSettingsDialog->setLayout(mainLayout);
715 			mainLayout->addWidget(printdlg);
716 			QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok|QDialogButtonBox::Cancel );
717 			connect(buttonBox, &QDialogButtonBox::accepted, [previewWidget, printSettingsDialog]{
718 				previewWidget->updatePreview();
719 				printSettingsDialog->close();
720 			} );
721 			connect(buttonBox, &QDialogButtonBox::rejected, printSettingsDialog, &QDialog::reject);
722 			mainLayout->addWidget(buttonBox);
723 			printSettingsDialog->show();
724 		});
725 	}
726 	connect(preview, &QPrintPreviewDialog::paintRequested, [this, &printdlg, &prt]{
727 		setupPrinter(printdlg, &prt);
728 	} );
729 	preview->exec();
730 	delete printdlg;
731 	delete preview;
732 }
733 
setupPrinter(KPrinterDlg * printDialog,QPrinter * printer)734 void MainDlg::setupPrinter(KPrinterDlg *printDialog, QPrinter *printer)
735 {
736 		View::self()->setPrintHeaderTable( printDialog->printHeaderTable() );
737 		View::self()->setPrintBackground( printDialog->printBackground() );
738 		View::self()->setPrintWidth( printDialog->printWidth() );
739 		View::self()->setPrintHeight( printDialog->printHeight() );
740 		View::self()->draw(printer, View::Printer);
741 }
742 
editAxes()743 void MainDlg::editAxes()
744 {
745 	coordsDialog()->show();
746 }
747 
748 
editConstants()749 void MainDlg::editConstants()
750 {
751 	if ( !m_constantEditor)
752 		m_constantEditor = new KConstantEditor(m_parent);
753 
754 	m_constantEditor->show();
755 }
756 
757 
editConstantsModal(QWidget * parent)758 void MainDlg::editConstantsModal(QWidget *parent)
759 {
760 	if (m_constantEditor) {
761 		m_constantEditor->hide();
762 	}
763 	else {
764 		m_constantEditor = new KConstantEditor(parent);
765 	}
766 
767 	m_constantEditor->setModal(true);
768 	m_constantEditor->show();
769 }
770 
fileExists(const QUrl & url)771 bool MainDlg::fileExists(const QUrl &url)
772 {
773 	bool fileExists = false;
774 	if (url.isValid()) {
775 		short int detailLevel = 0; // Lowest level: file/dir/symlink/none
776 		KIO::StatJob* statjob = KIO::stat(url, KIO::StatJob::SourceSide, detailLevel);
777 		bool noerror = statjob->exec();
778 		if (noerror) {
779 			// We want a file
780 			fileExists = !statjob->statResult().isDir();
781 		}
782 	}
783 	return fileExists;
784 }
785 
slotNames()786 void MainDlg::slotNames()
787 {
788 	KHelpClient::invokeHelp( "func-predefined", "kmplot" );
789 }
790 
791 
slotResetView()792 void MainDlg::slotResetView()
793 {
794 	View::self()->animateZoom( QRectF( -8, -8, 16, 16 ) );
795 }
796 
slotSettings()797 void MainDlg::slotSettings()
798 {
799 	// An instance of your dialog has already been created and has been cached,
800 	// so we want to display the cached dialog instead of creating
801 	// another one
802 	KConfigDialog::showDialog( "settings" );
803 }
804 
805 
calculator()806 void MainDlg::calculator()
807 {
808 	m_calculator->show();
809 }
810 
findMinimumValue()811 void MainDlg::findMinimumValue()
812 {
813 	m_functionTools->init( FunctionTools::FindMinimum );
814 	m_functionTools->show();
815 }
816 
findMaximumValue()817 void MainDlg::findMaximumValue()
818 {
819 	m_functionTools->init( FunctionTools::FindMaximum );
820 	m_functionTools->show();
821 }
822 
graphArea()823 void MainDlg::graphArea()
824 {
825 	m_functionTools->init( FunctionTools::CalculateArea );
826 	m_functionTools->show();
827 }
828 
toggleShowSliders()829 void MainDlg::toggleShowSliders()
830 {
831 	View::self()->updateSliders();
832 }
833 
setReadOnlyStatusBarText(const QString & text)834 void MainDlg::setReadOnlyStatusBarText(const QString &text)
835 {
836 	setStatusBarText(text);
837 }
838 
queryClose()839 bool MainDlg::queryClose()
840 {
841     return checkModified();
842 }
843 
coordsDialog()844 CoordsConfigDialog * MainDlg::coordsDialog( )
845 {
846 	if ( !m_coordsDialog)
847 	{
848 		m_coordsDialog = new CoordsConfigDialog(m_parent);
849 		connect( m_coordsDialog, &CoordsConfigDialog::settingsChanged, View::self(), QOverload<>::of(&View::drawPlot) );
850 	}
851 
852 	return m_coordsDialog;
853 }
854 //END class MainDlg
855 
856 
857 /// BrowserExtension class
BrowserExtension(MainDlg * parent)858 BrowserExtension::BrowserExtension(MainDlg* parent)
859 		: KParts::BrowserExtension( parent )
860 {
861 	emit enableAction("print", true);
862 	setURLDropHandlingEnabled(true);
863 }
864 
print()865 void BrowserExtension::print()
866 {
867 	static_cast<MainDlg*>(parent())->slotPrint();
868 }
869 
870 #include "maindlg.moc"
871