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