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