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