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