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