]> Shamusworld >> Repos - architektonas/blob - src/applicationwindow.cpp
5a435a21ae3e6bfe8d82cffdf3408afaa9181f82
[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 #if 0
424         if (drawing->toolAction)
425         {
426                 delete drawing->toolAction;
427                 drawing->toolAction = NULL;
428                 Global::ignoreClicks = false;
429         }
430
431         drawing->SetToolActive(addLineAct->isChecked() ? new DrawLineAction() : NULL);
432         drawing->SetToolActive(addCircleAct->isChecked() ? new DrawCircleAction() : NULL);
433         drawing->SetToolActive(addArcAct->isChecked() ? new DrawArcAction() : NULL);
434         drawing->SetToolActive(addDimensionAct->isChecked() ? new DrawDimensionAction() : NULL);
435         drawing->SetToolActive(addSplineAct->isChecked() ? new DrawSplineAction() : NULL);
436         drawing->SetToolActive(mirrorAct->isChecked() ? new MirrorAction() : NULL);
437         drawing->SetToolActive(rotateAct->isChecked() ? new RotateAction() : NULL);
438         drawing->SetToolActive(trimAct->isChecked() ? new TrimAction() : NULL);
439         drawing->SetToolActive(triangulateAct->isChecked() ? new TriangulateAction() : NULL);
440
441         if (drawing->toolAction)
442                 Global::ignoreClicks = true;
443 #else
444         if (addLineAct->isChecked())
445                 Global::tool = TTLine;
446         else if (addCircleAct->isChecked())
447                 Global::tool = TTCircle;
448         else if (addArcAct->isChecked())
449                 Global::tool = TTArc;
450         else if (addDimensionAct->isChecked())
451                 Global::tool = TTDimension;
452         else if (addSplineAct->isChecked())
453                 Global::tool = TTSpline;
454         else if (addPolygonAct->isChecked())
455                 Global::tool = TTPolygon;
456         else if (deleteAct->isChecked())
457                 Global::tool = TTDelete;
458         else if (mirrorAct->isChecked())
459                 Global::tool = TTMirror;
460         else if (rotateAct->isChecked())
461                 Global::tool = TTRotate;
462         else if (trimAct->isChecked())
463                 Global::tool = TTTrim;
464         else if (triangulateAct->isChecked())
465                 Global::tool = TTTriangulate;
466         else
467                 Global::tool = TTNone;
468 #endif
469
470         drawing->update();
471 }
472
473
474 void ApplicationWindow::HelpAbout(void)
475 {
476         aboutWin->show();
477 }
478
479
480 void ApplicationWindow::Settings(void)
481 {
482         SettingsDialog dlg(this);
483         dlg.generalTab->antialiasChk->setChecked(drawing->useAntialiasing);
484
485         if (dlg.exec() == false)
486                 return;
487
488         // Deal with stuff here (since user hit "OK" button...)
489         drawing->useAntialiasing = dlg.generalTab->antialiasChk->isChecked();
490         WriteSettings();
491 }
492
493
494 //
495 // Group a bunch of selected objects (which can include other groups) together
496 // or ungroup a selected group.
497 //
498 void ApplicationWindow::HandleGrouping(void)
499 {
500 #if 0
501         int itemsSelected = drawing->document.ItemsSelected();
502
503         // If nothing selected, do nothing
504         if (itemsSelected == 0)
505         {
506                 statusBar()->showMessage(tr("No objects selected to make a group from."));
507                 return;
508         }
509
510         // If it's a group that's selected, ungroup it and leave the objects in a
511         // selected state
512         if (itemsSelected == 1)
513         {
514                 Object * object = drawing->document.SelectedItem(0);
515
516 #if 0
517 if (object == NULL)
518         printf("SelectedItem = NULL!\n");
519 else
520         printf("SelectedItem = %08X, type = %i\n", object, object->type);
521 #endif
522
523                 if (object == NULL || object->type != OTContainer)
524                 {
525                         statusBar()->showMessage(tr("A group requires two or more selected objects."));
526                         return;
527                 }
528
529                 // Need the parent of the group, we're assuming here that the parent is
530                 // the drawing's document. Does it matter? Maybe...
531                 // Could just stipulate that grouping like this only takes place where the
532                 // parent of the group is the drawing's document. Makes life much simpler.
533                 ((Container *)object)->SelectAll();
534                 ((Container *)object)->MoveContentsTo(&(drawing->document));
535                 drawing->document.Delete(object);
536                 statusBar()->showMessage(tr("Objects ungrouped."));
537         }
538         // Otherwise, if it's a group of 2 or more objects (which can be groups too)
539         // group them and select the group
540         else if (itemsSelected > 1)
541         {
542                 Container * container = new Container(Vector(), &(drawing->document));
543                 drawing->document.MoveSelectedContentsTo(container);
544                 drawing->document.Add(container);
545                 container->DeselectAll();
546                 container->state = OSSelected;
547                 statusBar()->showMessage(QString(tr("Grouped %1 objects.")).arg(itemsSelected));
548         }
549 #endif
550
551         drawing->update();
552 }
553
554
555 void ApplicationWindow::HandleConnection(void)
556 {
557 #if 0
558 //double tt = Geometry::ParameterOfLineAndPoint(Vector(0, 0), Vector(10, 0), Vector(8, 2));
559 //printf("Parameter of point @ (8,2) of line (0,0), (10,0): %lf\n", tt);
560         int itemsSelected = drawing->document.ItemsSelected();
561
562         // If nothing selected, do nothing
563         if (itemsSelected == 0)
564         {
565                 statusBar()->showMessage(tr("No objects selected to connect."));
566                 return;
567         }
568
569         // If one thing selected, do nothing
570         if (itemsSelected == 1)
571         {
572                 statusBar()->showMessage(tr("Nothing to connect object to."));
573                 return;
574         }
575
576         // This is O(n^2 / 2) :-P
577         for(int i=0; i<itemsSelected; i++)
578         {
579                 Object * obj1 = drawing->document.SelectedItem(i);
580
581                 for(int j=i+1; j<itemsSelected; j++)
582                 {
583                         Object * obj2 = drawing->document.SelectedItem(j);
584                         double t, u, v, w;
585
586 //                      if ((obj1->type != OTLine) || (obj2->type != OTLine))
587 //                              continue;
588
589                         if ((obj1->type == OTLine) && (obj2->type == OTLine))
590                         {
591 //printf("Testing objects for intersection (%X, %X)...\n", obj1, obj2);
592                                 int intersects = Geometry::Intersects((Line *)obj1, (Line *)obj2, &t, &u);
593 //printf("  (%s) --> t=%lf, u=%lf\n", (intersects ? "true" : "FALSE"), t, u);
594
595                                 if (intersects)
596                                 {
597         //printf("Connecting objects (%X, %X)...\n", obj1, obj2);
598                                         obj1->Connect(obj2, u);
599                                         obj2->Connect(obj1, t);
600                                 }
601                         }
602                         else if (((obj1->type == OTLine) && (obj2->type == OTDimension))
603                                 || ((obj2->type == OTLine) && (obj1->type == OTDimension)))
604                         {
605 printf("Testing Line<->Dimension intersection...\n");
606                                 Line * line = (Line *)(obj1->type == OTLine ? obj1 : obj2);
607                                 Dimension * dim = (Dimension *)(obj1->type == OTDimension ? obj1 : obj2);
608
609                                 int intersects = Geometry::Intersects(line, dim, &t, &u);
610 printf("   -> intersects = %i, t=%lf, u=%lf\n", intersects, t, u);
611
612                                 if (intersects)
613                                 {
614                                         obj1->Connect(obj2, u);
615                                         obj2->Connect(obj1, t);
616                                 }
617                         }
618                 }
619         }
620 #endif
621 }
622
623
624 void ApplicationWindow::HandleDisconnection(void)
625 {
626 }
627
628
629 void ApplicationWindow::HandleGridSizeInPixels(int size)
630 {
631         drawing->SetGridSize(size);
632         drawing->update();
633 }
634
635
636 void ApplicationWindow::HandleGridSizeInBaseUnits(QString text)
637 {
638         // Parse the text...
639         bool ok;
640         double value = text.toDouble(&ok);
641
642         // Nothing parsable to a double, so quit...
643         if (!ok || value == 0)
644                 return;
645
646 //      drawing->gridSpacing = value;
647 //      Painter::zoom = drawing->gridPixels / drawing->gridSpacing;
648         Global::gridSpacing = value;
649         Global::zoom = drawing->gridPixels / Global::gridSpacing;
650         drawing->UpdateGridBackground();
651         drawing->update();
652 }
653
654
655 void ApplicationWindow::HandleDimensionSize(QString text)
656 {
657         // Parse the text...
658         bool ok;
659         double value = text.toDouble(&ok);
660
661         // Nothing parsable to a double, so quit...
662         if (!ok || value == 0)
663                 return;
664
665 //      drawing->document.ResizeAllDimensions(value);
666         drawing->update();
667 }
668
669
670 void ApplicationWindow::CreateActions(void)
671 {
672         exitAct = CreateAction(tr("&Quit"), tr("Quit"), tr("Exits the application."),
673                 QIcon(":/res/quit.png"), QKeySequence(tr("Ctrl+q")));
674         connect(exitAct, SIGNAL(triggered()), this, SLOT(close()));
675
676         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);
677         connect(snapToGridAct, SIGNAL(triggered()), this, SLOT(SnapToGridTool()));
678
679         fixAngleAct = CreateAction(tr("Fix &Angle"), tr("Fix Angle"), tr("Fixes the angle of an object."),
680                 QIcon(":/res/fix-angle.png"), QKeySequence(tr("F,A")), true);
681         connect(fixAngleAct, SIGNAL(triggered()), this, SLOT(FixAngle()));
682
683         fixLengthAct = CreateAction(tr("Fix &Length"), tr("Fix Length"), tr("Fixes the length of an object."),
684                 QIcon(":/res/fix-length.png"), QKeySequence(tr("F,L")), true);
685         connect(fixLengthAct, SIGNAL(triggered()), this, SLOT(FixLength()));
686
687         deleteAct = CreateAction(tr("&Delete"), tr("Delete Object"), tr("Deletes selected objects."), QIcon(":/res/delete-tool.png"), QKeySequence(tr("Delete")), true);
688         connect(deleteAct, SIGNAL(triggered()), this, SLOT(DeleteTool()));
689
690         addDimensionAct = CreateAction(tr("Add &Dimension"), tr("Add Dimension"), tr("Adds a dimension to the drawing."), QIcon(":/res/dimension-tool.png"), QKeySequence("D,I"), true);
691         connect(addDimensionAct, SIGNAL(triggered()), this, SLOT(DimensionTool()));
692
693         addLineAct = CreateAction(tr("Add &Line"), tr("Add Line"), tr("Adds lines to the drawing."), QIcon(":/res/add-line-tool.png"), QKeySequence("A,L"), true);
694         connect(addLineAct, SIGNAL(triggered()), this, SLOT(AddLineTool()));
695
696         addCircleAct = CreateAction(tr("Add &Circle"), tr("Add Circle"), tr("Adds circles to the drawing."), QIcon(":/res/add-circle-tool.png"), QKeySequence("A,C"), true);
697         connect(addCircleAct, SIGNAL(triggered()), this, SLOT(AddCircleTool()));
698
699         addArcAct = CreateAction(tr("Add &Arc"), tr("Add Arc"), tr("Adds arcs to the drawing."), QIcon(":/res/add-arc-tool.png"), QKeySequence("A,A"), true);
700         connect(addArcAct, SIGNAL(triggered()), this, SLOT(AddArcTool()));
701
702         addPolygonAct = CreateAction(tr("Add &Polygon"), tr("Add Polygon"), tr("Add polygons to the drawing."), QIcon(":/res/add-polygon-tool.png"), QKeySequence("A,P"), true);
703         connect(addPolygonAct, SIGNAL(triggered()), this, SLOT(AddPolygonTool()));
704
705         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);
706         connect(addSplineAct, SIGNAL(triggered()), this, SLOT(AddSplineTool()));
707
708         aboutAct = CreateAction(tr("About &Architektonas"), tr("About Architektonas"), tr("Gives information about this program."), QIcon(":/res/generic-tool.png"), QKeySequence());
709         connect(aboutAct, SIGNAL(triggered()), this, SLOT(HelpAbout()));
710
711         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);
712         connect(rotateAct, SIGNAL(triggered()), this, SLOT(RotateTool()));
713
714         zoomInAct = CreateAction(tr("Zoom &In"), tr("Zoom In"), tr("Zoom in on the document."), QIcon(":/res/zoom-in.png"), QKeySequence(tr("+")), QKeySequence(tr("=")));
715         connect(zoomInAct, SIGNAL(triggered()), this, SLOT(ZoomInTool()));
716
717         zoomOutAct = CreateAction(tr("Zoom &Out"), tr("Zoom Out"), tr("Zoom out of the document."), QIcon(":/res/zoom-out.png"), QKeySequence(tr("-")));
718         connect(zoomOutAct, SIGNAL(triggered()), this, SLOT(ZoomOutTool()));
719
720         fileNewAct = CreateAction(tr("&New Drawing"), tr("New Drawing"), tr("Creates a new drawing."), QIcon(":/res/file-new.png"), QKeySequence(tr("Ctrl+n")));
721         connect(fileNewAct, SIGNAL(triggered()), this, SLOT(FileNew()));
722
723         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")));
724         connect(fileOpenAct, SIGNAL(triggered()), this, SLOT(FileOpen()));
725
726         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")));
727         connect(fileSaveAct, SIGNAL(triggered()), this, SLOT(FileSave()));
728
729         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")));
730         connect(fileSaveAsAct, SIGNAL(triggered()), this, SLOT(FileSaveAs()));
731
732         fileCloseAct = CreateAction(tr("&Close Drawing"), tr("Close Drawing"), tr("Closes the current drawing."), QIcon(":/res/file-close.png"), QKeySequence(tr("Ctrl+w")));
733
734         settingsAct = CreateAction(tr("&Settings"), tr("Settings"), tr("Change certain defaults for Architektonas."), QIcon(":/res/settings.png"), QKeySequence());
735         connect(settingsAct, SIGNAL(triggered()), this, SLOT(Settings()));
736
737         groupAct = CreateAction(tr("&Group"), tr("Group"), tr("Group/ungroup selected objects."), QIcon(":/res/group-tool.png"), QKeySequence("g"));
738         connect(groupAct, SIGNAL(triggered()), this, SLOT(HandleGrouping()));
739
740         connectAct = CreateAction(tr("&Connect"), tr("Connect"), tr("Connect objects at point."), QIcon(":/res/connect-tool.png"), QKeySequence("c,c"));
741         connect(connectAct, SIGNAL(triggered()), this, SLOT(HandleConnection()));
742
743         disconnectAct = CreateAction(tr("&Disconnect"), tr("Disconnect"), tr("Disconnect objects joined at point."), QIcon(":/res/disconnect-tool.png"), QKeySequence("d,d"));
744         connect(disconnectAct, SIGNAL(triggered()), this, SLOT(HandleDisconnection()));
745
746         mirrorAct = CreateAction(tr("&Mirror"), tr("Mirror"), tr("Mirror selected objects around a line."), QIcon(":/res/mirror-tool.png"), QKeySequence("m,i"), true);
747         connect(mirrorAct, SIGNAL(triggered()), this, SLOT(MirrorTool()));
748
749         trimAct = CreateAction(tr("&Trim"), tr("Trim"), tr("Trim extraneous lines from selected objects."), QIcon(":/res/trim-tool.png"), QKeySequence("t,r"), true);
750         connect(trimAct, SIGNAL(triggered()), this, SLOT(TrimTool()));
751
752         triangulateAct = CreateAction(tr("&Triangulate"), tr("Triangulate"), tr("Make triangles from selected lines, preserving their lengths."), QIcon(":/res/triangulate-tool.png"), QKeySequence("t,g"), true);
753         connect(triangulateAct, SIGNAL(triggered()), this, SLOT(TriangulateTool()));
754
755
756 //Hm. I think we'll have to have separate logic to do the "Radio Group Toolbar" thing...
757 // Yup, in order to turn them off, we'd have to have an "OFF" toolbar button. Ick.
758 /*      QActionGroup * group = new QActionGroup(this);
759         group->addAction(deleteAct);
760         group->addAction(addDimensionAct);
761         group->addAction(addLineAct);
762         group->addAction(addCircleAct);
763         group->addAction(addArcAct);//*/
764 }
765
766
767 //
768 // Consolidates action creation from a multi-step process to a single-step one.
769 //
770 QAction * ApplicationWindow::CreateAction(QString name, QString tooltip, QString statustip,
771         QIcon icon, QKeySequence key, bool checkable/*= false*/)
772 {
773         QAction * action = new QAction(icon, name, this);
774         action->setToolTip(tooltip);
775         action->setStatusTip(statustip);
776         action->setShortcut(key);
777         action->setCheckable(checkable);
778
779         return action;
780 }
781
782
783 //
784 // This is essentially the same as the previous function, but this allows more
785 // than one key sequence to be added as key shortcuts.
786 //
787 QAction * ApplicationWindow::CreateAction(QString name, QString tooltip, QString statustip,
788         QIcon icon, QKeySequence key1, QKeySequence key2, bool checkable/*= false*/)
789 {
790         QAction * action = new QAction(icon, name, this);
791         action->setToolTip(tooltip);
792         action->setStatusTip(statustip);
793         QList<QKeySequence> keyList;
794         keyList.append(key1);
795         keyList.append(key2);
796         action->setShortcuts(keyList);
797         action->setCheckable(checkable);
798
799         return action;
800 }
801
802
803 void ApplicationWindow::CreateMenus(void)
804 {
805         QMenu * menu = menuBar()->addMenu(tr("&File"));
806         menu->addAction(fileNewAct);
807         menu->addAction(fileOpenAct);
808         menu->addAction(fileSaveAct);
809         menu->addAction(fileSaveAsAct);
810         menu->addAction(fileCloseAct);
811         menu->addSeparator();
812         menu->addAction(exitAct);
813
814         menu = menuBar()->addMenu(tr("&View"));
815         menu->addAction(zoomInAct);
816         menu->addAction(zoomOutAct);
817
818         menu = menuBar()->addMenu(tr("&Edit"));
819         menu->addAction(snapToGridAct);
820         menu->addAction(groupAct);
821         menu->addAction(fixAngleAct);
822         menu->addAction(fixLengthAct);
823         menu->addAction(rotateAct);
824         menu->addAction(mirrorAct);
825         menu->addAction(trimAct);
826         menu->addAction(triangulateAct);
827         menu->addAction(connectAct);
828         menu->addAction(disconnectAct);
829         menu->addSeparator();
830         menu->addAction(deleteAct);
831         menu->addSeparator();
832         menu->addAction(addLineAct);
833         menu->addAction(addCircleAct);
834         menu->addAction(addArcAct);
835         menu->addAction(addPolygonAct);
836         menu->addAction(addSplineAct);
837         menu->addAction(addDimensionAct);
838         menu->addSeparator();
839         menu->addAction(settingsAct);
840
841         menu = menuBar()->addMenu(tr("&Help"));
842         menu->addAction(aboutAct);
843 }
844
845
846 void ApplicationWindow::CreateToolbars(void)
847 {
848         QToolBar * toolbar = addToolBar(tr("File"));
849         toolbar->setObjectName("File"); // Needed for saveState()
850         toolbar->addAction(fileNewAct);
851         toolbar->addAction(fileOpenAct);
852         toolbar->addAction(fileSaveAct);
853         toolbar->addAction(fileSaveAsAct);
854         toolbar->addAction(fileCloseAct);
855 //      toolbar->addAction(exitAct);
856
857         toolbar = addToolBar(tr("View"));
858         toolbar->setObjectName("View");
859         toolbar->addAction(zoomInAct);
860         toolbar->addAction(zoomOutAct);
861
862         QSpinBox * spinbox = new QSpinBox;
863         toolbar->addWidget(spinbox);
864 //      QLineEdit * lineedit = new QLineEdit;
865         toolbar->addWidget(baseUnitInput);
866         toolbar->addWidget(dimensionSizeInput);
867
868         toolbar = addToolBar(tr("Edit"));
869         toolbar->setObjectName("Edit");
870         toolbar->addAction(snapToGridAct);
871         toolbar->addAction(groupAct);
872         toolbar->addAction(fixAngleAct);
873         toolbar->addAction(fixLengthAct);
874         toolbar->addAction(rotateAct);
875         toolbar->addAction(mirrorAct);
876         toolbar->addAction(trimAct);
877         toolbar->addAction(triangulateAct);
878         toolbar->addAction(deleteAct);
879         toolbar->addAction(connectAct);
880         toolbar->addAction(disconnectAct);
881         toolbar->addSeparator();
882         toolbar->addAction(addLineAct);
883         toolbar->addAction(addCircleAct);
884         toolbar->addAction(addArcAct);
885         toolbar->addAction(addPolygonAct);
886         toolbar->addAction(addSplineAct);
887         toolbar->addAction(addDimensionAct);
888
889         spinbox->setRange(4, 256);
890         spinbox->setValue(12);
891         baseUnitInput->setText("12");
892         connect(spinbox, SIGNAL(valueChanged(int)), this, SLOT(HandleGridSizeInPixels(int)));
893         connect(baseUnitInput, SIGNAL(textChanged(QString)), this, SLOT(HandleGridSizeInBaseUnits(QString)));
894         connect(dimensionSizeInput, SIGNAL(textChanged(QString)), this, SLOT(HandleDimensionSize(QString)));
895 }
896
897
898 void ApplicationWindow::ReadSettings(void)
899 {
900         QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint();
901         QSize size = settings.value("size", QSize(400, 400)).toSize();
902         drawing->useAntialiasing = settings.value("useAntialiasing", true).toBool();
903         snapToGridAct->setChecked(settings.value("snapToGrid", true).toBool());
904         resize(size);
905         move(pos);
906         restoreState(settings.value("windowState").toByteArray());
907 }
908
909
910 void ApplicationWindow::WriteSettings(void)
911 {
912         settings.setValue("pos", pos());
913         settings.setValue("size", size());
914         settings.setValue("windowState", saveState());
915         settings.setValue("useAntialiasing", drawing->useAntialiasing);
916         settings.setValue("snapToGrid", snapToGridAct->isChecked());
917 }
918