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