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