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