]> Shamusworld >> Repos - architektonas/blob - src/applicationwindow.cpp
Converted codebase from Qt4 to Qt5.
[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 "blockwidget.h"
33 #include "dimension.h"
34 #include "drawingview.h"
35 #include "drawarcaction.h"
36 #include "drawcircleaction.h"
37 #include "drawdimensionaction.h"
38 #include "drawlineaction.h"
39 #include "drawsplineaction.h"
40 #include "fileio.h"
41 #include "generaltab.h"
42 #include "geometry.h"
43 #include "layerwidget.h"
44 #include "line.h"
45 #include "mirroraction.h"
46 #include "painter.h"
47 #include "rotateaction.h"
48 #include "settingsdialog.h"
49 #include "triangulateaction.h"
50 #include "trimaction.h"
51
52
53 // Class variables
54 DrawingView * ApplicationWindow::drawing;
55
56
57 ApplicationWindow::ApplicationWindow():
58         baseUnitInput(new QLineEdit),
59         dimensionSizeInput(new QLineEdit),
60         settings("Underground Software", "Architektonas")
61 {
62         drawing = new DrawingView(this);
63         drawing->setMouseTracking(true);                // We want *all* mouse events...!
64         drawing->setFocusPolicy(Qt::StrongFocus);
65         setCentralWidget(drawing);
66
67         aboutWin = new AboutWindow(this);
68
69 //      ((TTEdit *)qApp)->charWnd = new CharWindow(this);
70
71         setWindowIcon(QIcon(":/res/atns-icon.png"));
72         setWindowTitle("Architektonas");
73
74         CreateActions();
75         CreateMenus();
76         CreateToolbars();
77
78         // Create Dock widgets
79         QDockWidget * dock1 = new QDockWidget(tr("Layers"), this);
80         LayerWidget * lw = new LayerWidget;
81         dock1->setWidget(lw);
82         addDockWidget(Qt::RightDockWidgetArea, dock1);
83         QDockWidget * dock2 = new QDockWidget(tr("Blocks"), this);
84         BlockWidget * bw = new BlockWidget;
85         dock2->setWidget(bw);
86         addDockWidget(Qt::RightDockWidgetArea, dock2);
87         // Needed for saveState()
88         dock1->setObjectName("Layers");
89         dock2->setObjectName("Blocks");
90
91         //      Create status bar
92         zoomIndicator = new QLabel("Grid: 12.0\" BU: Inch");
93         statusBar()->addPermanentWidget(zoomIndicator);
94         statusBar()->showMessage(tr("Ready"));
95
96         ReadSettings();
97         setUnifiedTitleAndToolBarOnMac(true);
98         Object::SetFont(new QFont("Verdana", 15, QFont::Bold));
99
100         connect(lw, SIGNAL(LayerSelected(int)), drawing, SLOT(SetCurrentLayer(int)));
101 }
102
103
104 void ApplicationWindow::closeEvent(QCloseEvent * event)
105 {
106         WriteSettings();
107         event->accept();                                                        // Use ignore() if can't close for some reason
108         //Do we have a memory leak here if we don't delete the font in the Object???
109 }
110
111
112 void ApplicationWindow::FileNew(void)
113 {
114         // Should warn the user if drawing hasn't been saved...
115         drawing->document.Clear();
116         drawing->update();
117         documentName.clear();
118         setWindowTitle("Architektonas - Untitled");
119         statusBar()->showMessage(tr("New drawing is ready."));
120 }
121
122
123 void ApplicationWindow::FileOpen(void)
124 {
125         QString filename = QFileDialog::getOpenFileName(this, tr("Open Drawing"),
126                 "", tr("Architektonas files (*.drawing)"));
127
128         // User cancelled open
129         if (filename.isEmpty())
130                 return;
131
132         FILE * file = fopen(filename.toUtf8().data(), "r");
133
134         if (file == 0)
135         {
136                 QMessageBox msg;
137                 msg.setText(QString(tr("Could not open file \"%1\" for loading!")).arg(filename));
138                 msg.setIcon(QMessageBox::Critical);
139                 msg.exec();
140                 return;
141         }
142
143         Container container(Vector(0, 0));
144         bool successful = FileIO::LoadAtnsFile(file, &container);
145         fclose(file);
146
147         if (!successful)
148         {
149                 QMessageBox msg;
150                 msg.setText(QString(tr("Could not load file \"%1\"!")).arg(filename));
151                 msg.setIcon(QMessageBox::Critical);
152                 msg.exec();
153                 return;
154         }
155
156 printf("FileOpen: container size = %li\n", container.objects.size());
157         drawing->document = container;
158         drawing->update();
159         documentName = filename;
160         setWindowTitle(QString("Architektonas - %1").arg(documentName));
161         statusBar()->showMessage(tr("Drawing loaded."));
162 }
163
164
165 void ApplicationWindow::FileSave(void)
166 {
167         if (documentName.isEmpty())
168                 documentName = QFileDialog::getSaveFileName(this, tr("Save Drawing"),
169                         "", tr("Architektonas drawings (*.drawing)"));
170
171         FILE * file = fopen(documentName.toUtf8().data(), "w");
172
173         if (file == 0)
174         {
175                 QMessageBox msg;
176                 msg.setText(QString(tr("Could not open file \"%1\" for saving!")).arg(documentName));
177                 msg.setIcon(QMessageBox::Critical);
178                 msg.exec();
179                 return;
180         }
181
182         bool successful = FileIO::SaveAtnsFile(file, &drawing->document);
183         fclose(file);
184
185         if (!successful)
186         {
187                 QMessageBox msg;
188                 msg.setText(QString(tr("Could not save file \"%1\"!")).arg(documentName));
189                 msg.setIcon(QMessageBox::Critical);
190                 msg.exec();
191                 // In this case, we should unlink the created file, since it's not right...
192 //              unlink(documentName.toUtf8().data());
193                 QFile::remove(documentName);
194                 return;
195         }
196
197         setWindowTitle(QString("Architektonas - %1").arg(documentName));
198         statusBar()->showMessage(tr("Drawing saved."));
199 }
200
201
202 void ApplicationWindow::FileSaveAs(void)
203 {
204         QString filename = QFileDialog::getSaveFileName(this, tr("Save Drawing As"),
205                 documentName, tr("Architektonas drawings (*.drawing)"));
206
207         if (!filename.isEmpty())
208         {
209                 documentName = filename;
210                 FileSave();
211         }
212 }
213
214
215 void ApplicationWindow::SnapToGridTool(void)
216 {
217         Object::SetSnapMode(snapToGridAct->isChecked());
218 }
219
220
221 void ApplicationWindow::FixAngle(void)
222 {
223         Object::SetFixedAngle(fixAngleAct->isChecked());
224 }
225
226
227 void ApplicationWindow::FixLength(void)
228 {
229         Object::SetFixedLength(fixLengthAct->isChecked());
230 }
231
232
233 // We want certain tools to be exclusive, and this approach isn't working correctly...
234 void ApplicationWindow::DeleteTool(void)
235 {
236         // For this tool, we check first to see if anything is selected. If so, we
237         // delete those and *don't* select the delete tool.
238         if (drawing->document.ItemsSelected() > 0)
239         {
240                 drawing->document.DeleteSelectedItems();
241                 drawing->update();
242                 deleteAct->setChecked(false);
243                 return;
244         }
245
246         // Otherwise, toggle the state of the tool
247         ClearUIToolStatesExcept(deleteAct);
248         SetInternalToolStates();
249 }
250
251
252 void ApplicationWindow::DimensionTool(void)
253 {
254         ClearUIToolStatesExcept(addDimensionAct);
255         SetInternalToolStates();
256 }
257
258
259 void ApplicationWindow::RotateTool(void)
260 {
261         ClearUIToolStatesExcept(rotateAct);
262         SetInternalToolStates();
263 }
264
265
266 void ApplicationWindow::MirrorTool(void)
267 {
268         ClearUIToolStatesExcept(mirrorAct);
269         SetInternalToolStates();
270 }
271
272
273 void ApplicationWindow::TrimTool(void)
274 {
275         ClearUIToolStatesExcept(trimAct);
276         SetInternalToolStates();
277 }
278
279
280 void ApplicationWindow::TriangulateTool(void)
281 {
282         ClearUIToolStatesExcept(triangulateAct);
283         SetInternalToolStates();
284 }
285
286
287 void ApplicationWindow::AddLineTool(void)
288 {
289         ClearUIToolStatesExcept(addLineAct);
290         SetInternalToolStates();
291 }
292
293
294 void ApplicationWindow::AddCircleTool(void)
295 {
296         ClearUIToolStatesExcept(addCircleAct);
297         SetInternalToolStates();
298 }
299
300
301 void ApplicationWindow::AddArcTool(void)
302 {
303         ClearUIToolStatesExcept(addArcAct);
304         SetInternalToolStates();
305 }
306
307
308 void ApplicationWindow::AddPolygonTool(void)
309 {
310         ClearUIToolStatesExcept(addPolygonAct);
311         SetInternalToolStates();
312 }
313
314
315 void ApplicationWindow::AddSplineTool(void)
316 {
317         ClearUIToolStatesExcept(addSplineAct);
318         SetInternalToolStates();
319 }
320
321
322 void ApplicationWindow::ZoomInTool(void)
323 {
324         double zoomFactor = 2.0;
325 /*
326 We need to find the center of the screen, then figure out where the new corner
327 will be in the zoomed in window.
328
329 So we know in Qt coords, the center is found via:
330 size.width()  / 2 --> xCenter
331 size.height() / 2 --> yCenter
332
333 transform x/yCenter to Cartesian coordinates. So far, so good.
334
335 when zooming in, new origin will be (xCenter - origin.x) / 2, (yCenter - origin.y) / 2
336 (after subtracting from center, that is...)
337 */
338         QSize size = drawing->size();
339         Vector center(size.width() / 2.0, size.height() / 2.0);
340 //printf("Zoom in... Center=%.2f,%.2f; ", center.x, center.y);
341         center = Painter::QtToCartesianCoords(center);
342 //printf("(%.2f,%.2f); origin=%.2f,%.2f; ", center.x, center.y, Painter::origin.x, Painter::origin.y);
343         Vector newOrigin = center - ((center - Painter::origin) / zoomFactor);
344 //printf("newOrigin=%.2f,%.2f;\n", newOrigin.x, newOrigin.y);
345         Painter::origin = newOrigin;
346
347 //printf("Zoom in... level going from %02f to ", Painter::zoom);
348         // This just zooms leaving origin intact... should zoom in at the current
349         // center! [DONE]
350         Painter::zoom *= zoomFactor;
351         Object::gridSpacing = drawing->gridPixels / Painter::zoom;
352         drawing->UpdateGridBackground();
353         drawing->update();
354
355         zoomIndicator->setText(QString("Grid: %1\", BU: Inch").arg(Object::gridSpacing));
356         baseUnitInput->setText(QString("%1").arg(Object::gridSpacing));
357 }
358
359
360 void ApplicationWindow::ZoomOutTool(void)
361 {
362 /*
363 Ok, real example.
364 center = (436, 311)
365 origin = (223, 160.5)
366 newOrigin should be (-10, -10)
367 Why isn't it?
368
369 center - origin = (213, 150.5)
370 origin - center = (-213, -150.5)
371 x 2 = (-426, -301)
372 + center = (-10, -10)
373
374 */
375         double zoomFactor = 2.0;
376         QSize size = drawing->size();
377         Vector center(size.width() / 2.0, size.height() / 2.0);
378 //printf("Zoom out... Center=%.2f,%.2f; ", center.x, center.y);
379         center = Painter::QtToCartesianCoords(center);
380 //printf("(%.2f,%.2f); origin=%.2f,%.2f; ", center.x, center.y, Painter::origin.x, Painter::origin.y);
381 //      Vector newOrigin = (center - Painter::origin) * zoomFactor;
382 //      Vector newOrigin = center - (Painter::origin * zoomFactor);
383         Vector newOrigin = center + ((Painter::origin - center) * zoomFactor);
384 //printf("newOrigin=%.2f,%.2f;\n", newOrigin.x, newOrigin.y);
385         Painter::origin = newOrigin;
386 //printf("Zoom out...\n");
387         // This just zooms leaving origin intact... should zoom out at the current
388         // center! [DONE]
389         Painter::zoom /= zoomFactor;
390         Object::gridSpacing = drawing->gridPixels / Painter::zoom;
391         drawing->UpdateGridBackground();
392         drawing->update();
393
394         zoomIndicator->setText(QString("Grid: %1\", BU: Inch").arg(Object::gridSpacing));
395         baseUnitInput->setText(QString("%1").arg(Object::gridSpacing));
396 }
397
398
399 void ApplicationWindow::ClearUIToolStatesExcept(QAction * exception)
400 {
401         if (exception != addArcAct)
402                 addArcAct->setChecked(false);
403
404         if (exception != addCircleAct)
405                 addCircleAct->setChecked(false);
406
407         if (exception != addDimensionAct)
408                 addDimensionAct->setChecked(false);
409
410         if (exception != addLineAct)
411                 addLineAct->setChecked(false);
412
413         if (exception != addPolygonAct)
414                 addPolygonAct->setChecked(false);
415
416         if (exception != addSplineAct)
417                 addSplineAct->setChecked(false);
418
419         if (exception != deleteAct)
420                 deleteAct->setChecked(false);
421
422         if (exception != rotateAct)
423                 rotateAct->setChecked(false);
424
425         if (exception != mirrorAct)
426                 mirrorAct->setChecked(false);
427
428         if (exception != trimAct)
429                 trimAct->setChecked(false);
430
431         if (exception != triangulateAct)
432                 triangulateAct->setChecked(false);
433 }
434
435
436 void ApplicationWindow::SetInternalToolStates(void)
437 {
438         Object::SetDeleteActive(deleteAct->isChecked());
439         Object::SetDimensionActive(addDimensionAct->isChecked());
440
441         // We can be sure that if we've come here, then either an active tool is
442         // being deactivated, or a new tool is being created. In either case, the
443         // old tool needs to be deleted.
444         if (drawing->toolAction)
445         {
446                 delete drawing->toolAction;
447                 drawing->toolAction = NULL;
448                 Object::ignoreClicks = false;
449         }
450
451         drawing->SetToolActive(addLineAct->isChecked() ? new DrawLineAction() : NULL);
452         drawing->SetToolActive(addCircleAct->isChecked() ? new DrawCircleAction() : NULL);
453         drawing->SetToolActive(addArcAct->isChecked() ? new DrawArcAction() : NULL);
454         drawing->SetToolActive(addDimensionAct->isChecked() ? new DrawDimensionAction() : NULL);
455         drawing->SetToolActive(addSplineAct->isChecked() ? new DrawSplineAction() : NULL);
456         drawing->SetToolActive(mirrorAct->isChecked() ? new MirrorAction() : NULL);
457         drawing->SetToolActive(rotateAct->isChecked() ? new RotateAction() : NULL);
458         drawing->SetToolActive(trimAct->isChecked() ? new TrimAction() : NULL);
459         drawing->SetToolActive(triangulateAct->isChecked() ? new TriangulateAction() : NULL);
460
461         if (drawing->toolAction)
462                 Object::ignoreClicks = true;
463
464         drawing->update();
465 }
466
467
468 void ApplicationWindow::HelpAbout(void)
469 {
470         aboutWin->show();
471 }
472
473
474 void ApplicationWindow::Settings(void)
475 {
476         SettingsDialog dlg(this);
477         dlg.generalTab->antialiasChk->setChecked(drawing->useAntialiasing);
478
479         if (dlg.exec() == false)
480                 return;
481
482         // Deal with stuff here (since user hit "OK" button...)
483         drawing->useAntialiasing = dlg.generalTab->antialiasChk->isChecked();
484         WriteSettings();
485 }
486
487
488 //
489 // Group a bunch of selected objects (which can include other groups) together
490 // or ungroup a selected group.
491 //
492 void ApplicationWindow::HandleGrouping(void)
493 {
494         int itemsSelected = drawing->document.ItemsSelected();
495
496         // If nothing selected, do nothing
497         if (itemsSelected == 0)
498         {
499                 statusBar()->showMessage(tr("No objects selected to make a group from."));
500                 return;
501         }
502
503         // If it's a group that's selected, ungroup it and leave the objects in a
504         // selected state
505         if (itemsSelected == 1)
506         {
507                 Object * object = drawing->document.SelectedItem(0);
508
509 #if 0
510 if (object == NULL)
511         printf("SelectedItem = NULL!\n");
512 else
513         printf("SelectedItem = %08X, type = %i\n", object, object->type);
514 #endif
515
516                 if (object == NULL || object->type != OTContainer)
517                 {
518                         statusBar()->showMessage(tr("A group requires two or more selected objects."));
519                         return;
520                 }
521
522                 // Need the parent of the group, we're assuming here that the parent is
523                 // the drawing's document. Does it matter? Maybe...
524                 // Could just stipulate that grouping like this only takes place where the
525                 // parent of the group is the drawing's document. Makes life much simpler.
526                 ((Container *)object)->SelectAll();
527                 ((Container *)object)->MoveContentsTo(&(drawing->document));
528                 drawing->document.Delete(object);
529                 statusBar()->showMessage(tr("Objects ungrouped."));
530         }
531         // Otherwise, if it's a group of 2 or more objects (which can be groups too)
532         // group them and select the group
533         else if (itemsSelected > 1)
534         {
535                 Container * container = new Container(Vector(), &(drawing->document));
536                 drawing->document.MoveSelectedContentsTo(container);
537                 drawing->document.Add(container);
538                 container->DeselectAll();
539                 container->state = OSSelected;
540                 statusBar()->showMessage(QString(tr("Grouped %1 objects.")).arg(itemsSelected));
541         }
542
543         drawing->update();
544 }
545
546
547 void ApplicationWindow::HandleConnection(void)
548 {
549 //double tt = Geometry::ParameterOfLineAndPoint(Vector(0, 0), Vector(10, 0), Vector(8, 2));
550 //printf("Parameter of point @ (8,2) of line (0,0), (10,0): %lf\n", tt);
551         int itemsSelected = drawing->document.ItemsSelected();
552
553         // If nothing selected, do nothing
554         if (itemsSelected == 0)
555         {
556                 statusBar()->showMessage(tr("No objects selected to connect."));
557                 return;
558         }
559
560         // If one thing selected, do nothing
561         if (itemsSelected == 1)
562         {
563                 statusBar()->showMessage(tr("Nothing to connect object to."));
564                 return;
565         }
566
567         // This is O(n^2 / 2) :-P
568         for(int i=0; i<itemsSelected; i++)
569         {
570                 Object * obj1 = drawing->document.SelectedItem(i);
571
572                 for(int j=i+1; j<itemsSelected; j++)
573                 {
574                         Object * obj2 = drawing->document.SelectedItem(j);
575                         double t, u, v, w;
576
577 //                      if ((obj1->type != OTLine) || (obj2->type != OTLine))
578 //                              continue;
579
580                         if ((obj1->type == OTLine) && (obj2->type == OTLine))
581                         {
582 //printf("Testing objects for intersection (%X, %X)...\n", obj1, obj2);
583                                 int intersects = Geometry::Intersects((Line *)obj1, (Line *)obj2, &t, &u);
584 //printf("  (%s) --> t=%lf, u=%lf\n", (intersects ? "true" : "FALSE"), t, u);
585
586                                 if (intersects)
587                                 {
588         //printf("Connecting objects (%X, %X)...\n", obj1, obj2);
589                                         obj1->Connect(obj2, u);
590                                         obj2->Connect(obj1, t);
591                                 }
592                         }
593                         else if (((obj1->type == OTLine) && (obj2->type == OTDimension))
594                                 || ((obj2->type == OTLine) && (obj1->type == OTDimension)))
595                         {
596 printf("Testing Line<->Dimension intersection...\n");
597                                 Line * line = (Line *)(obj1->type == OTLine ? obj1 : obj2);
598                                 Dimension * dim = (Dimension *)(obj1->type == OTDimension ? obj1 : obj2);
599
600                                 int intersects = Geometry::Intersects(line, dim, &t, &u);
601 printf("   -> intersects = %i, t=%lf, u=%lf\n", intersects, t, u);
602
603                                 if (intersects)
604                                 {
605                                         obj1->Connect(obj2, u);
606                                         obj2->Connect(obj1, t);
607                                 }
608                         }
609                 }
610         }
611 }
612
613
614 void ApplicationWindow::HandleDisconnection(void)
615 {
616 }
617
618
619 void ApplicationWindow::HandleGridSizeInPixels(int size)
620 {
621         drawing->SetGridSize(size);
622         drawing->update();
623 }
624
625
626 void ApplicationWindow::HandleGridSizeInBaseUnits(QString text)
627 {
628         // Parse the text...
629         bool ok;
630         double value = text.toDouble(&ok);
631
632         // Nothing parsable to a double, so quit...
633         if (!ok || value == 0)
634                 return;
635
636 //      drawing->gridSpacing = value;
637 //      Painter::zoom = drawing->gridPixels / drawing->gridSpacing;
638         Object::gridSpacing = value;
639         Painter::zoom = drawing->gridPixels / Object::gridSpacing;
640         drawing->UpdateGridBackground();
641         drawing->update();
642 }
643
644
645 void ApplicationWindow::HandleDimensionSize(QString text)
646 {
647         // Parse the text...
648         bool ok;
649         double value = text.toDouble(&ok);
650
651         // Nothing parsable to a double, so quit...
652         if (!ok || value == 0)
653                 return;
654
655         drawing->document.ResizeAllDimensions(value);
656 //      drawing->gridSpacing = value;
657 //      Painter::zoom = drawing->gridPixels / drawing->gridSpacing;
658 //      drawing->UpdateGridBackground();
659         drawing->update();
660 }
661
662
663 void ApplicationWindow::CreateActions(void)
664 {
665         exitAct = CreateAction(tr("&Quit"), tr("Quit"), tr("Exits the application."),
666                 QIcon(":/res/quit.png"), QKeySequence(tr("Ctrl+q")));
667         connect(exitAct, SIGNAL(triggered()), this, SLOT(close()));
668
669         snapToGridAct = CreateAction(tr("Snap To &Grid"), tr("Snap To Grid"), tr("Snaps mouse cursor to the visible grid when moving/creating objects."), QIcon(":/res/snap-to-grid-tool.png"), QKeySequence(tr("S")), true);
670         connect(snapToGridAct, SIGNAL(triggered()), this, SLOT(SnapToGridTool()));
671
672         fixAngleAct = CreateAction(tr("Fix &Angle"), tr("Fix Angle"), tr("Fixes the angle of an object."),
673                 QIcon(":/res/fix-angle.png"), QKeySequence(tr("F,A")), true);
674         connect(fixAngleAct, SIGNAL(triggered()), this, SLOT(FixAngle()));
675
676         fixLengthAct = CreateAction(tr("Fix &Length"), tr("Fix Length"), tr("Fixes the length of an object."),
677                 QIcon(":/res/fix-length.png"), QKeySequence(tr("F,L")), true);
678         connect(fixLengthAct, SIGNAL(triggered()), this, SLOT(FixLength()));
679
680         deleteAct = CreateAction(tr("&Delete"), tr("Delete Object"), tr("Deletes selected objects."), QIcon(":/res/delete-tool.png"), QKeySequence(tr("Delete")), true);
681         connect(deleteAct, SIGNAL(triggered()), this, SLOT(DeleteTool()));
682
683         addDimensionAct = CreateAction(tr("Add &Dimension"), tr("Add Dimension"), tr("Adds a dimension to the drawing."), QIcon(":/res/dimension-tool.png"), QKeySequence("D,I"), true);
684         connect(addDimensionAct, SIGNAL(triggered()), this, SLOT(DimensionTool()));
685
686         addLineAct = CreateAction(tr("Add &Line"), tr("Add Line"), tr("Adds lines to the drawing."), QIcon(":/res/add-line-tool.png"), QKeySequence("A,L"), true);
687         connect(addLineAct, SIGNAL(triggered()), this, SLOT(AddLineTool()));
688
689         addCircleAct = CreateAction(tr("Add &Circle"), tr("Add Circle"), tr("Adds circles to the drawing."), QIcon(":/res/add-circle-tool.png"), QKeySequence("A,C"), true);
690         connect(addCircleAct, SIGNAL(triggered()), this, SLOT(AddCircleTool()));
691
692         addArcAct = CreateAction(tr("Add &Arc"), tr("Add Arc"), tr("Adds arcs to the drawing."), QIcon(":/res/add-arc-tool.png"), QKeySequence("A,A"), true);
693         connect(addArcAct, SIGNAL(triggered()), this, SLOT(AddArcTool()));
694
695         addPolygonAct = CreateAction(tr("Add &Polygon"), tr("Add Polygon"), tr("Add polygons to the drawing."), QIcon(":/res/add-polygon-tool.png"), QKeySequence("A,P"), true);
696         connect(addPolygonAct, SIGNAL(triggered()), this, SLOT(AddPolygonTool()));
697
698         addSplineAct = CreateAction(tr("Add &Spline"), tr("Add Spline"), tr("Add a NURB spline to the drawing."), QIcon(":/res/add-spline-tool.png"), QKeySequence("A,S"), true);
699         connect(addSplineAct, SIGNAL(triggered()), this, SLOT(AddSplineTool()));
700
701         aboutAct = CreateAction(tr("About &Architektonas"), tr("About Architektonas"), tr("Gives information about this program."), QIcon(":/res/generic-tool.png"), QKeySequence());
702         connect(aboutAct, SIGNAL(triggered()), this, SLOT(HelpAbout()));
703
704         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);
705         connect(rotateAct, SIGNAL(triggered()), this, SLOT(RotateTool()));
706
707         zoomInAct = CreateAction(tr("Zoom &In"), tr("Zoom In"), tr("Zoom in on the document."), QIcon(":/res/zoom-in.png"), QKeySequence(tr("+")), QKeySequence(tr("=")));
708         connect(zoomInAct, SIGNAL(triggered()), this, SLOT(ZoomInTool()));
709
710         zoomOutAct = CreateAction(tr("Zoom &Out"), tr("Zoom Out"), tr("Zoom out of the document."), QIcon(":/res/zoom-out.png"), QKeySequence(tr("-")));
711         connect(zoomOutAct, SIGNAL(triggered()), this, SLOT(ZoomOutTool()));
712
713         fileNewAct = CreateAction(tr("&New Drawing"), tr("New Drawing"), tr("Creates a new drawing."), QIcon(":/res/file-new.png"), QKeySequence(tr("Ctrl+n")));
714         connect(fileNewAct, SIGNAL(triggered()), this, SLOT(FileNew()));
715
716         fileOpenAct = CreateAction(tr("&Open Drawing"), tr("Open Drawing"), tr("Opens an existing drawing from a file."), QIcon(":/res/file-open.png"), QKeySequence(tr("Ctrl+o")));
717         connect(fileOpenAct, SIGNAL(triggered()), this, SLOT(FileOpen()));
718
719         fileSaveAct = CreateAction(tr("&Save Drawing"), tr("Save Drawing"), tr("Saves the current drawing to a file."), QIcon(":/res/file-save.png"), QKeySequence(tr("Ctrl+s")));
720         connect(fileSaveAct, SIGNAL(triggered()), this, SLOT(FileSave()));
721
722         fileSaveAsAct = CreateAction(tr("Save Drawing &As"), tr("Save As"), tr("Saves the current drawing to a file with a different name."), QIcon(":/res/file-save-as.png"), QKeySequence(tr("Ctrl+Shift+s")));
723         connect(fileSaveAsAct, SIGNAL(triggered()), this, SLOT(FileSaveAs()));
724
725         fileCloseAct = CreateAction(tr("&Close Drawing"), tr("Close Drawing"), tr("Closes the current drawing."), QIcon(":/res/file-close.png"), QKeySequence(tr("Ctrl+w")));
726
727         settingsAct = CreateAction(tr("&Settings"), tr("Settings"), tr("Change certain defaults for Architektonas."), QIcon(":/res/settings.png"), QKeySequence());
728         connect(settingsAct, SIGNAL(triggered()), this, SLOT(Settings()));
729
730         groupAct = CreateAction(tr("&Group"), tr("Group"), tr("Group/ungroup selected objects."), QIcon(":/res/group-tool.png"), QKeySequence("g"));
731         connect(groupAct, SIGNAL(triggered()), this, SLOT(HandleGrouping()));
732
733         connectAct = CreateAction(tr("&Connect"), tr("Connect"), tr("Connect objects at point."), QIcon(":/res/connect-tool.png"), QKeySequence("c,c"));
734         connect(connectAct, SIGNAL(triggered()), this, SLOT(HandleConnection()));
735
736         disconnectAct = CreateAction(tr("&Disconnect"), tr("Disconnect"), tr("Disconnect objects joined at point."), QIcon(":/res/disconnect-tool.png"), QKeySequence("d,d"));
737         connect(disconnectAct, SIGNAL(triggered()), this, SLOT(HandleDisconnection()));
738
739         mirrorAct = CreateAction(tr("&Mirror"), tr("Mirror"), tr("Mirror selected objects around a line."), QIcon(":/res/mirror-tool.png"), QKeySequence("m,i"), true);
740         connect(mirrorAct, SIGNAL(triggered()), this, SLOT(MirrorTool()));
741
742         trimAct = CreateAction(tr("&Trim"), tr("Trim"), tr("Trim extraneous lines from selected objects."), QIcon(":/res/trim-tool.png"), QKeySequence("t,r"), true);
743         connect(trimAct, SIGNAL(triggered()), this, SLOT(TrimTool()));
744
745         triangulateAct = CreateAction(tr("&Triangulate"), tr("Triangulate"), tr("Make triangles from selected lines, preserving their lengths."), QIcon(":/res/triangulate-tool.png"), QKeySequence("t,g"), true);
746         connect(triangulateAct, SIGNAL(triggered()), this, SLOT(TriangulateTool()));
747
748
749 //Hm. I think we'll have to have separate logic to do the "Radio Group Toolbar" thing...
750 // Yup, in order to turn them off, we'd have to have an "OFF" toolbar button. Ick.
751 /*      QActionGroup * group = new QActionGroup(this);
752         group->addAction(deleteAct);
753         group->addAction(addDimensionAct);
754         group->addAction(addLineAct);
755         group->addAction(addCircleAct);
756         group->addAction(addArcAct);//*/
757 }
758
759
760 //
761 // Consolidates action creation from a multi-step process to a single-step one.
762 //
763 QAction * ApplicationWindow::CreateAction(QString name, QString tooltip, QString statustip,
764         QIcon icon, QKeySequence key, bool checkable/*= false*/)
765 {
766         QAction * action = new QAction(icon, name, this);
767         action->setToolTip(tooltip);
768         action->setStatusTip(statustip);
769         action->setShortcut(key);
770         action->setCheckable(checkable);
771
772         return action;
773 }
774
775
776 //
777 // This is essentially the same as the previous function, but this allows more
778 // than one key sequence to be added as key shortcuts.
779 //
780 QAction * ApplicationWindow::CreateAction(QString name, QString tooltip, QString statustip,
781         QIcon icon, QKeySequence key1, QKeySequence key2, bool checkable/*= false*/)
782 {
783         QAction * action = new QAction(icon, name, this);
784         action->setToolTip(tooltip);
785         action->setStatusTip(statustip);
786         QList<QKeySequence> keyList;
787         keyList.append(key1);
788         keyList.append(key2);
789         action->setShortcuts(keyList);
790         action->setCheckable(checkable);
791
792         return action;
793 }
794
795
796 void ApplicationWindow::CreateMenus(void)
797 {
798         QMenu * menu = menuBar()->addMenu(tr("&File"));
799         menu->addAction(fileNewAct);
800         menu->addAction(fileOpenAct);
801         menu->addAction(fileSaveAct);
802         menu->addAction(fileSaveAsAct);
803         menu->addAction(fileCloseAct);
804         menu->addSeparator();
805         menu->addAction(exitAct);
806
807         menu = menuBar()->addMenu(tr("&View"));
808         menu->addAction(zoomInAct);
809         menu->addAction(zoomOutAct);
810
811         menu = menuBar()->addMenu(tr("&Edit"));
812         menu->addAction(snapToGridAct);
813         menu->addAction(groupAct);
814         menu->addAction(fixAngleAct);
815         menu->addAction(fixLengthAct);
816         menu->addAction(rotateAct);
817         menu->addAction(mirrorAct);
818         menu->addAction(trimAct);
819         menu->addAction(triangulateAct);
820         menu->addAction(connectAct);
821         menu->addAction(disconnectAct);
822         menu->addSeparator();
823         menu->addAction(deleteAct);
824         menu->addSeparator();
825         menu->addAction(addLineAct);
826         menu->addAction(addCircleAct);
827         menu->addAction(addArcAct);
828         menu->addAction(addPolygonAct);
829         menu->addAction(addSplineAct);
830         menu->addAction(addDimensionAct);
831         menu->addSeparator();
832         menu->addAction(settingsAct);
833
834         menu = menuBar()->addMenu(tr("&Help"));
835         menu->addAction(aboutAct);
836 }
837
838
839 void ApplicationWindow::CreateToolbars(void)
840 {
841         QToolBar * toolbar = addToolBar(tr("File"));
842         toolbar->setObjectName("File"); // Needed for saveState()
843         toolbar->addAction(fileNewAct);
844         toolbar->addAction(fileOpenAct);
845         toolbar->addAction(fileSaveAct);
846         toolbar->addAction(fileSaveAsAct);
847         toolbar->addAction(fileCloseAct);
848 //      toolbar->addAction(exitAct);
849
850         toolbar = addToolBar(tr("View"));
851         toolbar->setObjectName("View");
852         toolbar->addAction(zoomInAct);
853         toolbar->addAction(zoomOutAct);
854
855         QSpinBox * spinbox = new QSpinBox;
856         toolbar->addWidget(spinbox);
857 //      QLineEdit * lineedit = new QLineEdit;
858         toolbar->addWidget(baseUnitInput);
859         toolbar->addWidget(dimensionSizeInput);
860
861         toolbar = addToolBar(tr("Edit"));
862         toolbar->setObjectName("Edit");
863         toolbar->addAction(snapToGridAct);
864         toolbar->addAction(groupAct);
865         toolbar->addAction(fixAngleAct);
866         toolbar->addAction(fixLengthAct);
867         toolbar->addAction(rotateAct);
868         toolbar->addAction(mirrorAct);
869         toolbar->addAction(trimAct);
870         toolbar->addAction(triangulateAct);
871         toolbar->addAction(deleteAct);
872         toolbar->addAction(connectAct);
873         toolbar->addAction(disconnectAct);
874         toolbar->addSeparator();
875         toolbar->addAction(addLineAct);
876         toolbar->addAction(addCircleAct);
877         toolbar->addAction(addArcAct);
878         toolbar->addAction(addPolygonAct);
879         toolbar->addAction(addSplineAct);
880         toolbar->addAction(addDimensionAct);
881
882         spinbox->setRange(4, 256);
883         spinbox->setValue(12);
884         baseUnitInput->setText("12");
885         connect(spinbox, SIGNAL(valueChanged(int)), this, SLOT(HandleGridSizeInPixels(int)));
886         connect(baseUnitInput, SIGNAL(textChanged(QString)), this, SLOT(HandleGridSizeInBaseUnits(QString)));
887         connect(dimensionSizeInput, SIGNAL(textChanged(QString)), this, SLOT(HandleDimensionSize(QString)));
888 }
889
890
891 void ApplicationWindow::ReadSettings(void)
892 {
893         QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint();
894         QSize size = settings.value("size", QSize(400, 400)).toSize();
895         drawing->useAntialiasing = settings.value("useAntialiasing", true).toBool();
896         snapToGridAct->setChecked(settings.value("snapToGrid", true).toBool());
897         resize(size);
898         move(pos);
899         restoreState(settings.value("windowState").toByteArray());
900 }
901
902
903 void ApplicationWindow::WriteSettings(void)
904 {
905         settings.setValue("pos", pos());
906         settings.setValue("size", size());
907         settings.setValue("windowState", saveState());
908         settings.setValue("useAntialiasing", drawing->useAntialiasing);
909         settings.setValue("snapToGrid", snapToGridAct->isChecked());
910 }
911