]> Shamusworld >> Repos - architektonas/blob - src/applicationwindow.cpp
2049a097ca354b54e69297fcbc48d91753b7d528
[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 "triangulateaction.h"
51 //#include "trimaction.h"
52
53
54 // Class variables
55 DrawingView * ApplicationWindow::drawing;
56
57
58 ApplicationWindow::ApplicationWindow():
59         baseUnitInput(new QLineEdit),
60         dimensionSizeInput(new QLineEdit),
61         settings("Underground Software", "Architektonas")
62 {
63         drawing = new DrawingView(this);
64         drawing->setMouseTracking(true);                // We want *all* mouse events...!
65         drawing->setFocusPolicy(Qt::StrongFocus);
66         setCentralWidget(drawing);
67
68         aboutWin = new AboutWindow(this);
69
70 //      ((TTEdit *)qApp)->charWnd = new CharWindow(this);
71
72         setWindowIcon(QIcon(":/res/atns-icon.png"));
73         setWindowTitle("Architektonas");
74
75         CreateActions();
76         CreateMenus();
77         CreateToolbars();
78
79         // Create Dock widgets
80         QDockWidget * dock1 = new QDockWidget(tr("Layers"), this);
81         LayerWidget * lw = new LayerWidget;
82         dock1->setWidget(lw);
83         addDockWidget(Qt::RightDockWidgetArea, dock1);
84         QDockWidget * dock2 = new QDockWidget(tr("Blocks"), this);
85         BlockWidget * bw = new BlockWidget;
86         dock2->setWidget(bw);
87         addDockWidget(Qt::RightDockWidgetArea, dock2);
88         // Needed for saveState()
89         dock1->setObjectName("Layers");
90         dock2->setObjectName("Blocks");
91
92         //      Create status bar
93         zoomIndicator = new QLabel("Grid: 12.0\" BU: Inch");
94         statusBar()->addPermanentWidget(zoomIndicator);
95         statusBar()->showMessage(tr("Ready"));
96
97         ReadSettings();
98         setUnifiedTitleAndToolBarOnMac(true);
99         Global::font =  new QFont("Verdana", 15, QFont::Bold);
100
101         connect(lw, SIGNAL(LayerSelected(int)), drawing, SLOT(SetCurrentLayer(int)));
102 }
103
104
105 void ApplicationWindow::closeEvent(QCloseEvent * event)
106 {
107         WriteSettings();
108         event->accept();                                                        // Use ignore() if can't close for some reason
109         //Do we have a memory leak here if we don't delete the font in the Object???
110 }
111
112
113 void ApplicationWindow::FileNew(void)
114 {
115         // Should warn the user if drawing hasn't been saved...
116 //      drawing->document.Clear();
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 // We want certain tools to be exclusive, and this approach isn't working correctly...
236 void ApplicationWindow::DeleteTool(void)
237 {
238         // For this tool, we check first to see if anything is selected. If so, we
239         // delete those and *don't* select the delete tool.
240 #if 0
241         if (drawing->document.ItemsSelected() > 0)
242         {
243                 drawing->document.DeleteSelectedItems();
244                 drawing->update();
245                 deleteAct->setChecked(false);
246                 return;
247         }
248 #endif
249
250         // Otherwise, toggle the state of the tool
251         ClearUIToolStatesExcept(deleteAct);
252         SetInternalToolStates();
253 }
254
255
256 void ApplicationWindow::DimensionTool(void)
257 {
258         ClearUIToolStatesExcept(addDimensionAct);
259         SetInternalToolStates();
260 }
261
262
263 void ApplicationWindow::RotateTool(void)
264 {
265         ClearUIToolStatesExcept(rotateAct);
266         SetInternalToolStates();
267 }
268
269
270 void ApplicationWindow::MirrorTool(void)
271 {
272         ClearUIToolStatesExcept(mirrorAct);
273         SetInternalToolStates();
274 }
275
276
277 void ApplicationWindow::TrimTool(void)
278 {
279         ClearUIToolStatesExcept(trimAct);
280         SetInternalToolStates();
281 }
282
283
284 void ApplicationWindow::TriangulateTool(void)
285 {
286         ClearUIToolStatesExcept(triangulateAct);
287         SetInternalToolStates();
288 }
289
290
291 void ApplicationWindow::AddLineTool(void)
292 {
293         ClearUIToolStatesExcept(addLineAct);
294         SetInternalToolStates();
295 }
296
297
298 void ApplicationWindow::AddCircleTool(void)
299 {
300         ClearUIToolStatesExcept(addCircleAct);
301         SetInternalToolStates();
302 }
303
304
305 void ApplicationWindow::AddArcTool(void)
306 {
307         ClearUIToolStatesExcept(addArcAct);
308         SetInternalToolStates();
309 }
310
311
312 void ApplicationWindow::AddPolygonTool(void)
313 {
314         ClearUIToolStatesExcept(addPolygonAct);
315         SetInternalToolStates();
316 }
317
318
319 void ApplicationWindow::AddSplineTool(void)
320 {
321         ClearUIToolStatesExcept(addSplineAct);
322         SetInternalToolStates();
323 }
324
325
326 void ApplicationWindow::ZoomInTool(void)
327 {
328         double zoomFactor = 2.0;
329 /*
330 We need to find the center of the screen, then figure out where the new corner
331 will be in the zoomed in window.
332
333 So we know in Qt coords, the center is found via:
334 size.width()  / 2 --> xCenter
335 size.height() / 2 --> yCenter
336
337 transform x/yCenter to Cartesian coordinates. So far, so good.
338
339 when zooming in, new origin will be (xCenter - origin.x) / 2, (yCenter - origin.y) / 2
340 (after subtracting from center, that is...)
341 */
342         QSize size = drawing->size();
343         Vector center(size.width() / 2.0, size.height() / 2.0);
344 //printf("Zoom in... Center=%.2f,%.2f; ", center.x, center.y);
345         center = Painter::QtToCartesianCoords(center);
346 //printf("(%.2f,%.2f); origin=%.2f,%.2f; ", center.x, center.y, Painter::origin.x, Painter::origin.y);
347         Vector newOrigin = center - ((center - Global::origin) / zoomFactor);
348 //printf("newOrigin=%.2f,%.2f;\n", newOrigin.x, newOrigin.y);
349         Global::origin = newOrigin;
350
351 //printf("Zoom in... level going from %02f to ", Painter::zoom);
352         // This just zooms leaving origin intact... should zoom in at the current
353         // center! [DONE]
354         Global::zoom *= zoomFactor;
355         Global::gridSpacing = drawing->gridPixels / Global::zoom;
356         drawing->UpdateGridBackground();
357         drawing->update();
358
359         zoomIndicator->setText(QString("Grid: %1\", BU: Inch").arg(Global::gridSpacing));
360         baseUnitInput->setText(QString("%1").arg(Global::gridSpacing));
361 }
362
363
364 void ApplicationWindow::ZoomOutTool(void)
365 {
366 /*
367 Ok, real example.
368 center = (436, 311)
369 origin = (223, 160.5)
370 newOrigin should be (-10, -10)
371 Why isn't it?
372
373 center - origin = (213, 150.5)
374 origin - center = (-213, -150.5)
375 x 2 = (-426, -301)
376 + center = (-10, -10)
377
378 */
379         double zoomFactor = 2.0;
380         QSize size = drawing->size();
381         Vector center(size.width() / 2.0, size.height() / 2.0);
382 //printf("Zoom out... Center=%.2f,%.2f; ", center.x, center.y);
383         center = Painter::QtToCartesianCoords(center);
384 //printf("(%.2f,%.2f); origin=%.2f,%.2f; ", center.x, center.y, Painter::origin.x, Painter::origin.y);
385 //      Vector newOrigin = (center - Painter::origin) * zoomFactor;
386 //      Vector newOrigin = center - (Painter::origin * zoomFactor);
387         Vector newOrigin = center + ((Global::origin - center) * zoomFactor);
388 //printf("newOrigin=%.2f,%.2f;\n", newOrigin.x, newOrigin.y);
389         Global::origin = newOrigin;
390 //printf("Zoom out...\n");
391         // This just zooms leaving origin intact... should zoom out at the current
392         // center! [DONE]
393         Global::zoom /= zoomFactor;
394         Global::gridSpacing = drawing->gridPixels / Global::zoom;
395         drawing->UpdateGridBackground();
396         drawing->update();
397
398         zoomIndicator->setText(QString("Grid: %1\", BU: Inch").arg(Global::gridSpacing));
399         baseUnitInput->setText(QString("%1").arg(Global::gridSpacing));
400 }
401
402
403 void ApplicationWindow::ClearUIToolStatesExcept(QAction * exception)
404 {
405         if (exception != addArcAct)
406                 addArcAct->setChecked(false);
407
408         if (exception != addCircleAct)
409                 addCircleAct->setChecked(false);
410
411         if (exception != addDimensionAct)
412                 addDimensionAct->setChecked(false);
413
414         if (exception != addLineAct)
415                 addLineAct->setChecked(false);
416
417         if (exception != addPolygonAct)
418                 addPolygonAct->setChecked(false);
419
420         if (exception != addSplineAct)
421                 addSplineAct->setChecked(false);
422
423         if (exception != deleteAct)
424                 deleteAct->setChecked(false);
425
426         if (exception != rotateAct)
427                 rotateAct->setChecked(false);
428
429         if (exception != mirrorAct)
430                 mirrorAct->setChecked(false);
431
432         if (exception != trimAct)
433                 trimAct->setChecked(false);
434
435         if (exception != triangulateAct)
436                 triangulateAct->setChecked(false);
437 }
438
439
440 void ApplicationWindow::SetInternalToolStates(void)
441 {
442         Global::deleteActive = deleteAct->isChecked();
443         Global::dimensionActive = addDimensionAct->isChecked();
444
445         // We can be sure that if we've come here, then either an active tool is
446         // being deactivated, or a new tool is being created. In either case, the
447         // old tool needs to be deleted.
448 #if 0
449         if (drawing->toolAction)
450         {
451                 delete drawing->toolAction;
452                 drawing->toolAction = NULL;
453                 Global::ignoreClicks = false;
454         }
455
456         drawing->SetToolActive(addLineAct->isChecked() ? new DrawLineAction() : NULL);
457         drawing->SetToolActive(addCircleAct->isChecked() ? new DrawCircleAction() : NULL);
458         drawing->SetToolActive(addArcAct->isChecked() ? new DrawArcAction() : NULL);
459         drawing->SetToolActive(addDimensionAct->isChecked() ? new DrawDimensionAction() : NULL);
460         drawing->SetToolActive(addSplineAct->isChecked() ? new DrawSplineAction() : NULL);
461         drawing->SetToolActive(mirrorAct->isChecked() ? new MirrorAction() : NULL);
462         drawing->SetToolActive(rotateAct->isChecked() ? new RotateAction() : NULL);
463         drawing->SetToolActive(trimAct->isChecked() ? new TrimAction() : NULL);
464         drawing->SetToolActive(triangulateAct->isChecked() ? new TriangulateAction() : NULL);
465
466         if (drawing->toolAction)
467                 Global::ignoreClicks = true;
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