]> Shamusworld >> Repos - architektonas/blob - src/applicationwindow.cpp
Initial stab at text object. Nonfunctional ATM.
[architektonas] / src / applicationwindow.cpp
1 //
2 // applicationwindow.cpp: Architektonas
3 //
4 // Part of the Architektonas Project
5 // (C) 2011 Underground Software
6 // See the README and GPLv3 files for licensing and warranty information
7 //
8 // JLH = James Hammons <jlhamm@acm.org>
9 //
10 // Who  When        What
11 // ---  ----------  -------------------------------------------------------------
12 // JLH  03/22/2011  Created this file
13 // JLH  09/29/2011  Added simple zoom in/out functionality
14 // JLH  10/03/2011  Fixed zoom tool to zoom in/out from center of screen
15 //
16
17 // FIXED:
18 //
19 //
20 // STILL TO BE DONE:
21 //
22 //
23
24 // Uncomment this for debugging...
25 //#define DEBUG
26 //#define DEBUGFOO                      // Various tool debugging...
27 //#define DEBUGTP                               // Toolpalette debugging...
28
29 #include "applicationwindow.h"
30
31 #include "about.h"
32 #include "drawingview.h"
33 #include "fileio.h"
34 #include "generaltab.h"
35 #include "painter.h"
36 #include "settingsdialog.h"
37
38
39 ApplicationWindow::ApplicationWindow(): settings("Underground Software", "Architektonas")
40 {
41         drawing = new DrawingView(this);
42         drawing->setMouseTracking(true);                // We want *all* mouse events...!
43         setCentralWidget(drawing);
44
45         aboutWin = new AboutWindow(this);
46
47 //      ((TTEdit *)qApp)->charWnd = new CharWindow(this);
48
49         setWindowIcon(QIcon(":/res/atns-icon.png"));
50         setWindowTitle("Architektonas");
51
52         CreateActions();
53         CreateMenus();
54         CreateToolbars();
55
56         //      Create status bar
57         zoomIndicator = new QLabel("Grid: 12.0\" Zoom: 12.5%");
58         statusBar()->addPermanentWidget(zoomIndicator);
59         statusBar()->showMessage(tr("Ready"));
60
61         ReadSettings();
62
63 //      connect(textEdit->document(), SIGNAL(contentsChanged()),
64 //                      this, SLOT(documentWasModified()));
65
66 //      setCurrentFile("");
67         setUnifiedTitleAndToolBarOnMac(true);
68
69 //      ((TTEdit *)qApp)->charWnd->show();//eh?
70         Object::SetFont(new QFont("Verdana", 15, QFont::Bold));
71 }
72
73
74 void ApplicationWindow::closeEvent(QCloseEvent * event)
75 {
76         WriteSettings();
77         event->accept();                                                        // Use ignore() if can't close for some reason
78         //Do we have a memory leak here if we don't delete the font in the Object???
79 }
80
81
82 void ApplicationWindow::FileNew(void)
83 {
84         // Should warn the user if drawing hasn't been saved...
85         drawing->document.Clear();
86         drawing->update();
87         documentName.clear();
88         setWindowTitle("Architektonas - Untitled");
89         statusBar()->showMessage(tr("New drawing is ready."));
90 }
91
92
93 void ApplicationWindow::FileOpen(void)
94 {
95         QString filename = QFileDialog::getOpenFileName(this, tr("Open Drawing"),
96                 "", tr("Architektonas files (*.drawing)"));
97         FILE * file = fopen(filename.toAscii().data(), "r");
98
99         if (file == 0)
100         {
101                 QMessageBox msg;
102                 msg.setText(QString(tr("Could not open file \"%1\" for loading!")).arg(filename));
103                 msg.setIcon(QMessageBox::Critical);
104                 msg.exec();
105                 return;
106         }
107
108         Container container(Vector(0, 0));
109         bool successful = FileIO::LoadAtnsFile(file, &container);
110         fclose(file);
111
112         if (!successful)
113         {
114                 QMessageBox msg;
115                 msg.setText(QString(tr("Could not load file \"%1\"!")).arg(filename));
116                 msg.setIcon(QMessageBox::Critical);
117                 msg.exec();
118                 return;
119         }
120
121 printf("FileOpen: container size = %li\n", container.objects.size());
122         drawing->document = container;
123         drawing->update();
124         documentName = filename;
125         setWindowTitle(QString("Architektonas - %1").arg(documentName));
126         statusBar()->showMessage(tr("Drawing loaded."));
127 }
128
129
130 void ApplicationWindow::FileSave(void)
131 {
132         if (documentName.isEmpty())
133                 documentName = QFileDialog::getSaveFileName(this, tr("Save Drawing"),
134                         "", tr("Architektonas drawings (*.drawing)"));
135
136         FILE * file = fopen(documentName.toAscii().data(), "w");
137
138         if (file == 0)
139         {
140                 QMessageBox msg;
141                 msg.setText(QString(tr("Could not open file \"%1\" for saving!")).arg(documentName));
142                 msg.setIcon(QMessageBox::Critical);
143                 msg.exec();
144                 return;
145         }
146
147         bool successful = FileIO::SaveAtnsFile(file, &drawing->document);
148         fclose(file);
149
150         if (!successful)
151         {
152                 QMessageBox msg;
153                 msg.setText(QString(tr("Could not save file \"%1\"!")).arg(documentName));
154                 msg.setIcon(QMessageBox::Critical);
155                 msg.exec();
156                 // In this case, we should unlink the created file, since it's not right...
157                 unlink(documentName.toAscii().data());
158                 return;
159         }
160
161         setWindowTitle(QString("Architektonas - %1").arg(documentName));
162         statusBar()->showMessage(tr("Drawing saved."));
163 }
164
165
166 void ApplicationWindow::FileSaveAs(void)
167 {
168         QString filename = QFileDialog::getSaveFileName(this, tr("Save Drawing As"),
169                 documentName, tr("Architektonas drawings (*.drawing)"));
170
171         if (!filename.isEmpty())
172         {
173                 documentName = filename;
174                 FileSave();
175         }
176 }
177
178
179 void ApplicationWindow::SnapToGridTool(void)
180 {
181         Object::SetSnapMode(snapToGridAct->isChecked());
182 }
183
184
185 void ApplicationWindow::FixAngle(void)
186 {
187         Object::SetFixedAngle(fixAngleAct->isChecked());
188 }
189
190
191 void ApplicationWindow::FixLength(void)
192 {
193         Object::SetFixedLength(fixLengthAct->isChecked());
194 }
195
196
197 // We want certain tools to be exclusive, and this approach isn't working correctly...
198 void ApplicationWindow::DeleteTool(void)
199 {
200         
201         ClearUIToolStatesExcept(deleteAct);
202         SetInternalToolStates();
203 }
204
205
206 void ApplicationWindow::DimensionTool(void)
207 {
208         ClearUIToolStatesExcept(addDimensionAct);
209         SetInternalToolStates();
210 }
211
212
213 void ApplicationWindow::RotateTool(void)
214 {
215         ClearUIToolStatesExcept(rotateAct);
216         SetInternalToolStates();
217 }
218
219
220 void ApplicationWindow::AddLineTool(void)
221 {
222         ClearUIToolStatesExcept(addLineAct);
223         SetInternalToolStates();
224 }
225
226
227 void ApplicationWindow::AddCircleTool(void)
228 {
229         ClearUIToolStatesExcept(addCircleAct);
230         SetInternalToolStates();
231 }
232
233
234 void ApplicationWindow::AddArcTool(void)
235 {
236         ClearUIToolStatesExcept(addArcAct);
237         SetInternalToolStates();
238 }
239
240
241 void ApplicationWindow::AddPolygonTool(void)
242 {
243         ClearUIToolStatesExcept(addPolygonAct);
244         SetInternalToolStates();
245 }
246
247
248 void ApplicationWindow::ZoomInTool(void)
249 {
250         double zoomFactor = 2.0;
251 /*
252 We need to find the center of the screen, then figure out where the new corner
253 will be in the zoomed in window.
254
255 So we know in Qt coords, the center is found via:
256 size.width()  / 2 --> xCenter
257 size.height() / 2 --> yCenter
258
259 transform x/yCenter to Cartesian coordinates. So far, so good.
260
261 when zooming in, new origin will be (xCenter - origin.x) / 2, (yCenter - origin.y) / 2
262 (after subtracting from center, that is...)
263 */
264         QSize size = drawing->size();
265         Vector center(size.width() / 2.0, size.height() / 2.0);
266 //printf("Zoom in... Center=%.2f,%.2f; ", center.x, center.y);
267         center = Painter::QtToCartesianCoords(center);
268 //printf("(%.2f,%.2f); origin=%.2f,%.2f; ", center.x, center.y, Painter::origin.x, Painter::origin.y);
269         Vector newOrigin = center - ((center - Painter::origin) / zoomFactor);
270 //printf("newOrigin=%.2f,%.2f;\n", newOrigin.x, newOrigin.y);
271         Painter::origin = newOrigin;
272
273 //printf("Zoom in... level going from %02f to ", Painter::zoom);
274         // This just zooms leaving origin intact... should zoom in at the current center! [DONE]
275         Painter::zoom *= zoomFactor;
276         drawing->gridSpacing = 12.0 / Painter::zoom;
277         zoomIndicator->setText(QString("Grid: %2\" Zoom: %1%").arg(Painter::zoom * 100.0 * SCREEN_ZOOM).arg(drawing->gridSpacing));
278         drawing->UpdateGridBackground();
279         drawing->update();
280 }
281
282
283 void ApplicationWindow::ZoomOutTool(void)
284 {
285 /*
286 Ok, real example.
287 center = (436, 311)
288 origin = (223, 160.5)
289 newOrigin should be (-10, -10)
290 Why isn't it?
291
292 center - origin = (213, 150.5)
293 origin - center = (-213, -150.5)
294 x 2 = (-426, -301)
295 + center = (-10, -10)
296
297 */
298         double zoomFactor = 2.0;
299         QSize size = drawing->size();
300         Vector center(size.width() / 2.0, size.height() / 2.0);
301 //printf("Zoom out... Center=%.2f,%.2f; ", center.x, center.y);
302         center = Painter::QtToCartesianCoords(center);
303 //printf("(%.2f,%.2f); origin=%.2f,%.2f; ", center.x, center.y, Painter::origin.x, Painter::origin.y);
304 //      Vector newOrigin = (center - Painter::origin) * zoomFactor;
305 //      Vector newOrigin = center - (Painter::origin * zoomFactor);
306         Vector newOrigin = center + ((Painter::origin - center) * zoomFactor);
307 //printf("newOrigin=%.2f,%.2f;\n", newOrigin.x, newOrigin.y);
308         Painter::origin = newOrigin;
309 //printf("Zoom out...\n");
310         // This just zooms leaving origin intact... should zoom out at the current center! [DONE]
311         Painter::zoom /= zoomFactor;
312         drawing->gridSpacing = 12.0 / Painter::zoom;
313         zoomIndicator->setText(QString("Grid: %2\" Zoom: %1%").arg(Painter::zoom * 100.0 * SCREEN_ZOOM).arg(drawing->gridSpacing));
314         drawing->UpdateGridBackground();
315         drawing->update();
316 }
317
318
319 void ApplicationWindow::ClearUIToolStatesExcept(QAction * exception)
320 {
321         if (exception != addArcAct)
322                 addArcAct->setChecked(false);
323
324         if (exception != addCircleAct)
325                 addCircleAct->setChecked(false);
326
327         if (exception != addDimensionAct)
328                 addDimensionAct->setChecked(false);
329
330         if (exception != addLineAct)
331                 addLineAct->setChecked(false);
332
333         if (exception != addPolygonAct)
334                 addPolygonAct->setChecked(false);
335
336         if (exception != deleteAct)
337                 deleteAct->setChecked(false);
338
339         if (exception != rotateAct)
340                 rotateAct->setChecked(false);
341 }
342
343
344 void ApplicationWindow::SetInternalToolStates(void)
345 {
346         Object::SetDeleteActive(deleteAct->isChecked());
347         Object::SetDimensionActive(addDimensionAct->isChecked());
348         drawing->SetRotateToolActive(rotateAct->isChecked());
349         drawing->SetAddLineToolActive(addLineAct->isChecked());
350         drawing->SetAddCircleToolActive(addCircleAct->isChecked());
351 }
352
353
354 void ApplicationWindow::HelpAbout(void)
355 {
356         aboutWin->show();
357 }
358
359
360 void ApplicationWindow::Settings(void)
361 {
362         SettingsDialog dlg(this);
363         dlg.generalTab->antialiasChk->setChecked(drawing->useAntialiasing);
364
365         if (dlg.exec() == false)
366                 return;
367
368         // Deal with stuff here (since user hit "OK" button...)
369         drawing->useAntialiasing = dlg.generalTab->antialiasChk->isChecked();
370         WriteSettings();
371 }
372
373
374 void ApplicationWindow::HandleGrouping(void)
375 {
376         // Group a bunch of selected objects together or ungroup a selected group.
377 }
378
379
380 void ApplicationWindow::CreateActions(void)
381 {
382         exitAct = CreateAction(tr("&Quit"), tr("Quit"), tr("Exits the application."),
383                 QIcon(":/res/quit.png"), QKeySequence(tr("Ctrl+q")));
384         connect(exitAct, SIGNAL(triggered()), this, SLOT(close()));
385
386         snapToGridAct = CreateAction(tr("Snap To &Grid"), tr("Snap To Grid"), tr("Snaps mouse cursor to the visible grid when moving/creating objects."), QIcon(":/res/generic-tool.png"), QKeySequence(tr("S")), true);
387         connect(snapToGridAct, SIGNAL(triggered()), this, SLOT(SnapToGridTool()));
388
389         fixAngleAct = CreateAction(tr("Fix &Angle"), tr("Fix Angle"), tr("Fixes the angle of an object."),
390                 QIcon(":/res/fix-angle.png"), QKeySequence(tr("F,A")), true);
391         connect(fixAngleAct, SIGNAL(triggered()), this, SLOT(FixAngle()));
392
393         fixLengthAct = CreateAction(tr("Fix &Length"), tr("Fix Length"), tr("Fixes the length of an object."),
394                 QIcon(":/res/fix-length.png"), QKeySequence(tr("F,L")), true);
395         connect(fixLengthAct, SIGNAL(triggered()), this, SLOT(FixLength()));
396
397         deleteAct = CreateAction(tr("&Delete"), tr("Delete Object"), tr("Deletes selected objects."), QIcon(":/res/delete-tool.png"), QKeySequence(), true);
398         connect(deleteAct, SIGNAL(triggered()), this, SLOT(DeleteTool()));
399
400         addDimensionAct = CreateAction(tr("Add &Dimension"), tr("Add Dimension"), tr("Adds a dimension to the drawing."), QIcon(":/res/dimension-tool.png"), QKeySequence("D,I"), true);
401         connect(addDimensionAct, SIGNAL(triggered()), this, SLOT(DimensionTool()));
402
403         addLineAct = CreateAction(tr("Add &Line"), tr("Add Line"), tr("Adds lines to the drawing."), QIcon(":/res/add-line-tool.png"), QKeySequence("A,L"), true);
404         connect(addLineAct, SIGNAL(triggered()), this, SLOT(AddLineTool()));
405
406         addCircleAct = CreateAction(tr("Add &Circle"), tr("Add Circle"), tr("Adds circles to the drawing."), QIcon(":/res/add-circle-tool.png"), QKeySequence("A,C"), true);
407         connect(addCircleAct, SIGNAL(triggered()), this, SLOT(AddCircleTool()));
408
409         addArcAct = CreateAction(tr("Add &Arc"), tr("Add Arc"), tr("Adds arcs to the drawing."), QIcon(":/res/add-arc-tool.png"), QKeySequence("A,A"), true);
410         connect(addArcAct, SIGNAL(triggered()), this, SLOT(AddArcTool()));
411
412         addPolygonAct = CreateAction(tr("Add &Polygon"), tr("Add Polygon"), tr("Add polygons to the drawing."), QIcon(":/res/add-polygon-tool.png"), QKeySequence("A,P"), true);
413         connect(addPolygonAct, SIGNAL(triggered()), this, SLOT(AddPolygonTool()));
414
415         aboutAct = CreateAction(tr("About &Architektonas"), tr("About Architektonas"), tr("Gives information about this program."), QIcon(":/res/generic-tool.png"), QKeySequence());
416         connect(aboutAct, SIGNAL(triggered()), this, SLOT(HelpAbout()));
417
418         rotateAct = CreateAction(tr("&Rotate Objects"), tr("Rotate"), tr("Rotate object(s) around an arbitrary center."), QIcon(":/res/rotate-tool.png"), QKeySequence(tr("R,O")), true);
419         connect(rotateAct, SIGNAL(triggered()), this, SLOT(RotateTool()));
420
421         zoomInAct = CreateAction(tr("Zoom &In"), tr("Zoom In"), tr("Zoom in on the document."), QIcon(":/res/zoom-in.png"), QKeySequence(tr("+")), QKeySequence(tr("=")));
422         connect(zoomInAct, SIGNAL(triggered()), this, SLOT(ZoomInTool()));
423
424         zoomOutAct = CreateAction(tr("Zoom &Out"), tr("Zoom Out"), tr("Zoom out of the document."), QIcon(":/res/zoom-out.png"), QKeySequence(tr("-")));
425         connect(zoomOutAct, SIGNAL(triggered()), this, SLOT(ZoomOutTool()));
426
427         fileNewAct = CreateAction(tr("&New Drawing"), tr("New Drawing"), tr("Creates a new drawing."), QIcon(":/res/generic-tool.png"), QKeySequence(tr("Ctrl+n")));
428         connect(fileNewAct, SIGNAL(triggered()), this, SLOT(FileNew()));
429
430         fileOpenAct = CreateAction(tr("&Open Drawing"), tr("Open Drawing"), tr("Opens an existing drawing from a file."), QIcon(":/res/generic-tool.png"), QKeySequence(tr("Ctrl+o")));
431         connect(fileOpenAct, SIGNAL(triggered()), this, SLOT(FileOpen()));
432
433         fileSaveAct = CreateAction(tr("&Save Drawing"), tr("Save Drawing"), tr("Saves the current drawing to a file."), QIcon(":/res/generic-tool.png"), QKeySequence(tr("Ctrl+s")));
434         connect(fileSaveAct, SIGNAL(triggered()), this, SLOT(FileSave()));
435
436         fileSaveAsAct = CreateAction(tr("Save Drawing &As"), tr("Save As"), tr("Saves the current drawing to a file with a different name."), QIcon(":/res/generic-tool.png"), QKeySequence(tr("Ctrl+Shift+s")));
437         connect(fileSaveAsAct, SIGNAL(triggered()), this, SLOT(FileSaveAs()));
438
439         fileCloseAct = CreateAction(tr("&Close Drawing"), tr("Close Drawing"), tr("Closes the current drawing."), QIcon(":/res/generic-tool.png"), QKeySequence(tr("Ctrl+w")));
440
441         settingsAct = CreateAction(tr("&Settings"), tr("Settings"), tr("Change certain defaults for Architektonas."), QIcon(":/res/generic-tool.png"), QKeySequence());
442         connect(settingsAct, SIGNAL(triggered()), this, SLOT(Settings()));
443
444         groupAct = CreateAction(tr("&Group"), tr("Group"), tr("Group/ungroup selected objects."), QIcon(":/res/generic-tool.png"), QKeySequence("g"));
445         connect(groupAct, SIGNAL(triggered()), this, SLOT(HandleGrouping()));
446
447 //Hm. I think we'll have to have separate logic to do the "Radio Group Toolbar" thing...
448 // Yup, in order to turn them off, we'd have to have an "OFF" toolbar button. Ick.
449 /*      QActionGroup * group = new QActionGroup(this);
450         group->addAction(deleteAct);
451         group->addAction(addDimensionAct);
452         group->addAction(addLineAct);
453         group->addAction(addCircleAct);
454         group->addAction(addArcAct);//*/
455 }
456
457
458 //
459 // Consolidates action creation from a multi-step process to a single-step one.
460 //
461 QAction * ApplicationWindow::CreateAction(QString name, QString tooltip, QString statustip,
462         QIcon icon, QKeySequence key, bool checkable/*= false*/)
463 {
464         QAction * action = new QAction(icon, name, this);
465         action->setToolTip(tooltip);
466         action->setStatusTip(statustip);
467         action->setShortcut(key);
468         action->setCheckable(checkable);
469
470         return action;
471 }
472
473
474 //
475 // This is essentially the same as the previous function, but this allows more
476 // than one key sequence to be added as key shortcuts.
477 //
478 QAction * ApplicationWindow::CreateAction(QString name, QString tooltip, QString statustip,
479         QIcon icon, QKeySequence key1, QKeySequence key2, bool checkable/*= false*/)
480 {
481         QAction * action = new QAction(icon, name, this);
482         action->setToolTip(tooltip);
483         action->setStatusTip(statustip);
484         QList<QKeySequence> keyList;
485         keyList.append(key1);
486         keyList.append(key2);
487         action->setShortcuts(keyList);
488         action->setCheckable(checkable);
489
490         return action;
491 }
492
493
494 void ApplicationWindow::CreateMenus(void)
495 {
496         QMenu * menu = menuBar()->addMenu(tr("&File"));
497         menu->addAction(fileNewAct);
498         menu->addAction(fileOpenAct);
499         menu->addAction(fileSaveAct);
500         menu->addAction(fileSaveAsAct);
501         menu->addAction(fileCloseAct);
502         menu->addSeparator();
503         menu->addAction(exitAct);
504
505         menu = menuBar()->addMenu(tr("&View"));
506         menu->addAction(zoomInAct);
507         menu->addAction(zoomOutAct);
508
509         menu = menuBar()->addMenu(tr("&Edit"));
510         menu->addAction(snapToGridAct);
511         menu->addAction(groupAct);
512         menu->addAction(fixAngleAct);
513         menu->addAction(fixLengthAct);
514         menu->addAction(rotateAct);
515         menu->addSeparator();
516         menu->addAction(deleteAct);
517         menu->addSeparator();
518         menu->addAction(addLineAct);
519         menu->addAction(addCircleAct);
520         menu->addAction(addArcAct);
521         menu->addAction(addPolygonAct);
522         menu->addAction(addDimensionAct);
523         menu->addSeparator();
524         menu->addAction(settingsAct);
525
526         menu = menuBar()->addMenu(tr("&Help"));
527         menu->addAction(aboutAct);
528 }
529
530
531 void ApplicationWindow::CreateToolbars(void)
532 {
533         QToolBar * toolbar = addToolBar(tr("File"));
534         toolbar->addAction(exitAct);
535
536         toolbar = addToolBar(tr("View"));
537         toolbar->addAction(zoomInAct);
538         toolbar->addAction(zoomOutAct);
539
540         toolbar = addToolBar(tr("Edit"));
541         toolbar->addAction(snapToGridAct);
542         toolbar->addAction(groupAct);
543         toolbar->addAction(fixAngleAct);
544         toolbar->addAction(fixLengthAct);
545         toolbar->addAction(rotateAct);
546         toolbar->addAction(deleteAct);
547         toolbar->addSeparator();
548         toolbar->addAction(addLineAct);
549         toolbar->addAction(addCircleAct);
550         toolbar->addAction(addArcAct);
551         toolbar->addAction(addPolygonAct);
552         toolbar->addAction(addDimensionAct);
553 }
554
555
556 void ApplicationWindow::ReadSettings(void)
557 {
558         QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint();
559         QSize size = settings.value("size", QSize(400, 400)).toSize();
560         drawing->useAntialiasing = settings.value("useAntialiasing", true).toBool();
561         snapToGridAct->setChecked(settings.value("snapToGrid", true).toBool());
562         resize(size);
563         move(pos);
564 //      pos = settings.value("charWndPos", QPoint(0, 0)).toPoint();
565 //      size = settings.value("charWndSize", QSize(200, 200)).toSize();
566 //      ((TTEdit *)qApp)->charWnd->resize(size);
567 //      ((TTEdit *)qApp)->charWnd->move(pos);
568 }
569
570
571 void ApplicationWindow::WriteSettings(void)
572 {
573         settings.setValue("pos", pos());
574         settings.setValue("size", size());
575         settings.setValue("useAntialiasing", drawing->useAntialiasing);
576         settings.setValue("snapToGrid", snapToGridAct->isChecked());
577 //      settings.setValue("charWndPos", ((TTEdit *)qApp)->charWnd->pos());
578 //      settings.setValue("charWndSize", ((TTEdit *)qApp)->charWnd->size());
579 }
580