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