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