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