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