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