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