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