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