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