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