1 /* ==========================================================================
2  * ====                   FRACTAL GRAPHICS GENERATOR                     ====
3  * ==========================================================================
4  *
5  * Copyright (C) 2003-2021 by Thomas Dreibholz
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11 
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  *
20  * Contact: dreibh@iem.uni-due.de
21  */
22 
23 #include "fractalgenerator.h"
24 #include "fractalgeneratorview.h"
25 #include "fractalgeneratordoc.h"
26 #include "fractalalgorithminterface.h"
27 #include "colorschemeinterface.h"
28 #include "optionsdialog.h"
29 
30 #include <QtWidgets/QWidget>
31 #include <QtWidgets/QMessageBox>
32 #include <QtWidgets/QStatusBar>
33 #include <QtWidgets/QInputDialog>
34 #include <QtWidgets/QFileDialog>
35 #include <QtWidgets/QMenuBar>
36 #include <QtWidgets/QMenu>
37 #include <QtPrintSupport/QPrintDialog>
38 #include <QPainter>
39 #include <QDir>
40 
41 
42 // ###### Constructor #######################################################
FractalGeneratorApp(QWidget * parent,const QString & fileName)43 FractalGeneratorApp::FractalGeneratorApp(QWidget* parent, const QString& fileName)
44 #ifndef WITH_KDE
45    : QMainWindow(parent)
46 #else
47    : KXmlGuiWindow(parent)
48 #endif
49 {
50    View = new FractalGeneratorView(this);
51    Q_CHECK_PTR(View);
52    setCentralWidget(View);
53    connect(View, SIGNAL(updateFractalAlgorithm()), this, SLOT(slotUpdateFractalAlgorithm()));
54    connect(View, SIGNAL(updateColorScheme()), this, SLOT(slotUpdateColorScheme()));
55    connect(View, SIGNAL(updateZoomBackPossible()), this, SLOT(slotUpdateZoomBackPossible()));
56    connect(View, SIGNAL(updateZoomInPossible()), this, SLOT(slotUpdateZoomInPossible()));
57 
58    Document = new FractalGeneratorDoc(this, View);
59    Q_CHECK_PTR(Document);
60    connect(Document, SIGNAL(updateFileName(const QString&)), this, SLOT(slotUpdateFileName(const QString&)));
61    Document->newDocument();
62 
63    QMenu* fileMenu = menuBar()->addMenu(tr("&File"));
64    Q_CHECK_PTR(fileMenu);
65    fileMenu->addAction(tr("&Open"), this, SLOT(slotFileOpen()), QKeySequence(QKeySequence::Open));
66    fileMenu->addAction(tr("&Save"), this, SLOT(slotFileSave()), QKeySequence(QKeySequence::Save));
67    fileMenu->addAction(tr("Save As ..."), this, SLOT(slotFileSaveAs()));
68    fileMenu->addSeparator();
69    fileMenu->addAction(tr("&Export ..."), this, SLOT(slotFileExportImage()), QKeySequence(Qt::CTRL + Qt::Key_X));
70    fileMenu->addAction(tr("&Print ..."), this, SLOT(slotFilePrint()), QKeySequence(QKeySequence::Print));
71    fileMenu->addSeparator();
72    fileMenu->addAction(tr("&Close"), this, SLOT(slotFileClose()), QKeySequence(QKeySequence::Close));
73    fileMenu->addAction(tr("&Quit"), this, SLOT(slotFileQuit()), QKeySequence(Qt::CTRL + Qt::Key_Q));
74 
75    QMenu* viewMenu = menuBar()->addMenu(tr("&View"));
76    Q_CHECK_PTR(viewMenu);
77    ViewZoomIn = viewMenu->addAction(tr("Zoom &In"), View, SLOT(zoomIn()), QKeySequence(Qt::CTRL + Qt::Key_I));
78    ViewZoomIn->setEnabled(false);
79    ViewZoomBack = viewMenu->addAction(tr("Zoom &Back"), View, SLOT(zoomBack()), QKeySequence(QKeySequence::Undo));
80    ViewZoomBack->setEnabled(false);
81    viewMenu->addAction(tr("&Reset Zoom"), View, SLOT(zoomReset()), QKeySequence(Qt::CTRL + Qt::Key_R));
82    viewMenu->addSeparator();
83    viewMenu->addAction(tr("Image Size"), this, SLOT(slotViewSetImageSize()), QKeySequence(Qt::Key_F3));
84 
85    QMenu* fractalAlgorithmMenu = menuBar()->addMenu(tr("&Algorithm"));
86    Q_CHECK_PTR(fractalAlgorithmMenu);
87    QAction* configureAlgorithmAction = fractalAlgorithmMenu->addAction(tr("Configure Algorithm ..."), this, SLOT(slotViewConfigureAlgorithm()), QKeySequence(Qt::Key_F2));
88    configureAlgorithmAction->setData(1000000);
89    fractalAlgorithmMenu->addSeparator();
90 
91    QActionGroup* fractalAlgorithmGroup = new QActionGroup(this);
92    Q_CHECK_PTR(fractalAlgorithmGroup);
93    FractalAlgorithmInterface* fractalAlgorithm;
94    QStringList                fractalAlgorithmList;
95    unsigned int               fractalAlgorithmID = 0;
96    while((fractalAlgorithm = FractalAlgorithmInterface::getAlgorithm(fractalAlgorithmID))) {
97       QAction* item = fractalAlgorithmMenu->addAction(QString::fromLocal8Bit(fractalAlgorithm->getName()));
98       Q_CHECK_PTR(item);
99       fractalAlgorithmGroup->addAction(item);
100       item->setData(fractalAlgorithmID);
101       item->setCheckable(true);
102       item->setChecked((fractalAlgorithm == View->getAlgorithm()));
103       FractalAlgorithmActionList.append(item);
104       fractalAlgorithmID++;
105    }
106    connect(fractalAlgorithmMenu, SIGNAL(triggered(QAction*)), this, SLOT(slotViewSetFractalAlgorithm(QAction*)));
107 
108    QMenu* colorSchemeMenu = menuBar()->addMenu(tr("&Color Scheme"));
109    Q_CHECK_PTR(colorSchemeMenu);
110    QActionGroup* colorSchemeGroup = new QActionGroup(this);
111    ColorSchemeInterface* colorScheme;
112    QStringList           colorSchemeList;
113    unsigned int          colorSchemeID = 0;
114    while((colorScheme = ColorSchemeInterface::getColorScheme(colorSchemeID))) {
115       QAction* item = colorSchemeMenu->addAction(QString::fromLocal8Bit(colorScheme->getName()));
116       Q_CHECK_PTR(item);
117       colorSchemeGroup->addAction(item);
118       item->setData(colorSchemeID);
119       item->setCheckable(true);
120       item->setChecked((colorScheme == View->getColorScheme()));
121       ColorSchemeActionList.append(item);
122       colorSchemeID++;
123    }
124    connect(colorSchemeMenu, SIGNAL(triggered(QAction*)), this, SLOT(slotViewSetColorScheme(QAction*)));
125 
126    QMenu* helpMenu = menuBar()->addMenu(tr("&Help"));
127    Q_CHECK_PTR(helpMenu);
128    helpMenu->addAction(tr("&About"), this, SLOT(slotHelpAbout()));
129 
130    Printer.setColorMode(QPrinter::Color);
131    Printer.setPageOrientation(QPageLayout::Landscape);
132    Printer.setOutputFileName(tr("Fractal.pdf"));
133 
134    statusBar()->showMessage(tr("Welcome to FractGen!"), 3000);
135 
136    if(!fileName.isEmpty()) {
137       Document->openDocument(fileName);
138    }
139 
140    show();
141 }
142 
143 
144 // ###### Destructor ########################################################
~FractalGeneratorApp()145 FractalGeneratorApp::~FractalGeneratorApp()
146 {
147 }
148 
149 
150 // ###### Open ##############################################################
slotFileOpen()151 void FractalGeneratorApp::slotFileOpen()
152 {
153    statusBar()->showMessage(tr("Opening file ..."));
154    const QString fileName = QFileDialog::getOpenFileName(
155                                this, tr("Open File ..."),
156                                QDir::currentPath(),
157                                tr("FractGen File (*.fsf)"));
158    if(!fileName.isEmpty()) {
159       Document->openDocument(fileName);
160    }
161    statusBar()->showMessage(tr("Ready"));
162 }
163 
164 
165 // ###### Save ##############################################################
slotFileSaveAs()166 void FractalGeneratorApp::slotFileSaveAs()
167 {
168    statusBar()->showMessage(tr("Saving file as ..."));
169    const QString fileName = QFileDialog::getSaveFileName(
170                                this, tr("Save File ..."),
171                                QDir::currentPath(),
172                                tr("FractGen File (*.fsf)"));
173    if(!fileName.isEmpty()) {
174       Document->saveDocument(fileName);
175    }
176    statusBar()->showMessage(tr("Ready"));
177 }
178 
179 
180 // ###### Save as ###########################################################
slotFileSave()181 void FractalGeneratorApp::slotFileSave()
182 {
183    statusBar()->showMessage(tr("Saving file..."));
184    bool overwrite = true;
185    if(QFile::exists(Document->getFileName())) {
186       if(QMessageBox::warning(this, QStringLiteral("FractGen II"),
187                               tr("Overwrite existing file?"),
188                               QMessageBox::Save|QMessageBox::Cancel, QMessageBox::Save) == QMessageBox::Cancel) {
189          overwrite = false;
190       }
191    }
192    if(overwrite) {
193       Document->saveDocument(Document->getFileName());
194    }
195    statusBar()->showMessage(tr("Ready"));
196 }
197 
198 
199 // ###### Export ############################################################
slotFileExportImage()200 void FractalGeneratorApp::slotFileExportImage()
201 {
202    statusBar()->showMessage(tr("Exporting Image ..."));
203 
204    QString name = QFileDialog::getSaveFileName(this, tr("Export Image"),
205                                                QDir::currentPath(),
206                                                QStringLiteral("*.png"));
207    if(!name.isEmpty()) {
208       if(name.right(4).toLower() != QLatin1String(".png")) {
209          name += QLatin1String(".png");
210       }
211       View->getDisplay()->saveImage(name, "PNG");
212    }
213 
214    statusBar()->showMessage(tr("Ready"));
215 }
216 
217 
218 // ###### Close #############################################################
slotFileClose()219 void FractalGeneratorApp::slotFileClose()
220 {
221    close();
222 }
223 
224 
225 // ###### Print #############################################################
slotFilePrint()226 void FractalGeneratorApp::slotFilePrint()
227 {
228    statusBar()->showMessage(tr("Printing..."));
229 
230    QPrintDialog printDialog(&Printer, this);
231    if(printDialog.exec() == QDialog::Accepted) {
232       View->print(&Printer);
233    }
234 
235    statusBar()->showMessage(tr("Ready"));
236 }
237 
238 
239 // ###### Quit ##############################################################
slotFileQuit()240 void FractalGeneratorApp::slotFileQuit()
241 {
242    exit(0);
243 }
244 
245 
246 // ###### About #############################################################
slotHelpAbout()247 void FractalGeneratorApp::slotHelpAbout()
248 {
249    QMessageBox::information(this, QStringLiteral("FractGen"),
250       QStringLiteral("FractGen\nCopyright (C) 2003-2021 by Thomas Dreibholz\nhttps://www.uni-due.de/~be0001/fractalgenerator/"),
251       tr("&Okay"));
252 }
253 
254 
255 // ###### Set picture size ##################################################
slotViewSetImageSize()256 void FractalGeneratorApp::slotViewSetImageSize()
257 {
258    statusBar()->showMessage(tr("Changing Image Size ..."));
259 
260    const QString currentSize =
261       QString().setNum(View->getSizeWidth()) +
262       QLatin1Char('*') +
263       QString().setNum(View->getSizeHeight());
264 
265    bool ok;
266    QString text = QInputDialog::getText(
267                      this,
268                      tr("Image Size"),
269                      tr("Please enter new size in the format x*y:"),
270                      QLineEdit::Normal, currentSize, &ok);
271    if((ok) || (!text.isEmpty())) {
272       const unsigned int newX = text.section(QLatin1Char('*'), 0, 0).toUInt();
273       const unsigned int newY = text.section(QLatin1Char('*'), 1, 1).toUInt();
274 
275       if((0 < newX) && (0 < newY)) {
276          View->changeSize(newX, newY);
277          View->configChanged();
278       }
279       else {
280          QMessageBox::information(this, tr("Image Size"), tr("Invalid image size!"));
281       }
282       ViewZoomBack->setEnabled(View->isZoomBackPossible());
283    }
284 
285    statusBar()->showMessage(tr("Ready"));
286 }
287 
288 
289 // ###### Configure algorithm ###############################################
slotViewConfigureAlgorithm()290 void FractalGeneratorApp::slotViewConfigureAlgorithm()
291 {
292    statusBar()->showMessage(tr("Changing Options ..."));
293    OptionsDialog dialog(this, View->getAlgorithm()->getConfigEntries());
294    dialog.exec();
295    View->configChanged();
296    statusBar()->showMessage(tr("Ready"));
297 }
298 
299 
300 // ###### Update algorithm ##################################################
slotUpdateFractalAlgorithm()301 void FractalGeneratorApp::slotUpdateFractalAlgorithm()
302 {
303    const FractalAlgorithmInterface* currentAlgorithm = View->getAlgorithm();
304    unsigned int i = 0;
305    QListIterator<QAction*> iterator(FractalAlgorithmActionList);
306    while(iterator.hasNext()) {
307       QAction* item = iterator.next();
308       const FractalAlgorithmInterface* algorithm = FractalAlgorithmInterface::getAlgorithm(i);
309       Q_CHECK_PTR(algorithm);
310       item->setChecked( (algorithm == currentAlgorithm) );
311       i++;
312    }
313 }
314 
315 
316 // ###### Update color scheme ###############################################
slotUpdateColorScheme()317 void FractalGeneratorApp::slotUpdateColorScheme()
318 {
319    const ColorSchemeInterface* currentColorScheme = View->getColorScheme();
320    unsigned int i = 0;
321    QListIterator<QAction*> iterator(ColorSchemeActionList);
322    while(iterator.hasNext()) {
323       QAction* item = iterator.next();
324       const ColorSchemeInterface* colorScheme = ColorSchemeInterface::getColorScheme(i);
325       Q_CHECK_PTR(colorScheme);
326       item->setChecked( (colorScheme == currentColorScheme) );
327       i++;
328    }
329 }
330 
331 
332 // ###### Update file name ##################################################
slotUpdateFileName(const QString & fileName)333 void FractalGeneratorApp::slotUpdateFileName(const QString& fileName)
334 {
335    setWindowTitle(fileName + QLatin1String(" - FractGen II"));
336 }
337 
338 
339 // ###### Update Zoon In menu item ##########################################
slotUpdateZoomInPossible()340 void FractalGeneratorApp::slotUpdateZoomInPossible()
341 {
342    const bool zoomInPossible = View->isZoomInPossible();
343    ViewZoomIn->setEnabled(zoomInPossible);
344    if(zoomInPossible) {
345       statusBar()->showMessage(tr("Click middle mouse button or choose \"View -> Zoom In\" to magnify selected area!"));
346    }
347    else {
348       statusBar()->showMessage(tr("Ready"));
349    }
350 }
351 
352 
353 // ###### Update Zoon Back menu item ########################################
slotUpdateZoomBackPossible()354 void FractalGeneratorApp::slotUpdateZoomBackPossible()
355 {
356    ViewZoomBack->setEnabled(View->isZoomBackPossible());
357 }
358 
359 
360 // ###### Set algorithm #####################################################
slotViewSetFractalAlgorithm(QAction * action)361 void FractalGeneratorApp::slotViewSetFractalAlgorithm(QAction* action)
362 {
363    const int algorithmID = action->data().toInt();
364    if(algorithmID < 1000000) {
365       View->changeAlgorithm(algorithmID);
366       View->configChanged();
367       ViewZoomBack->setEnabled(View->isZoomBackPossible());
368    }
369 }
370 
371 
372 // ###### Set color scheme ##################################################
slotViewSetColorScheme(QAction * action)373 void FractalGeneratorApp::slotViewSetColorScheme(QAction* action)
374 {
375    const int colorSchemeID = action->data().toInt();
376    if(colorSchemeID < 1000000) {
377       View->changeColorScheme(colorSchemeID);
378       View->configChanged();
379       ViewZoomBack->setEnabled(View->isZoomBackPossible());
380    }
381 }
382