]> Shamusworld >> Repos - architektonas/blob - src/applicationwindow.cpp
Fix for Print Preview for centering of drawing on page.
[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 <QPrintPreviewDialog>
32 #include "about.h"
33 #include "blockwidget.h"
34 #include "drawingview.h"
35 #include "fileio.h"
36 #include "generaltab.h"
37 #include "global.h"
38 #include "layerwidget.h"
39 #include "objectwidget.h"
40 #include "painter.h"
41 #include "penwidget.h"
42 #include "settingsdialog.h"
43 #include "structs.h"
44 #include "utils.h"
45
46 // Class variables
47 DrawingView * ApplicationWindow::drawing;
48
49 ApplicationWindow::ApplicationWindow():
50         baseUnitInput(new QLineEdit),
51         dimensionSizeInput(new QLineEdit),
52         settings("Underground Software", "Architektonas")
53 {
54         drawing = new DrawingView(this);
55         drawing->setMouseTracking(true);                // We want *all* mouse events...!
56         drawing->setFocusPolicy(Qt::StrongFocus);
57         setCentralWidget(drawing);
58
59         aboutWin = new AboutWindow(this);
60
61 //      ((TTEdit *)qApp)->charWnd = new CharWindow(this);
62
63         setWindowIcon(QIcon(":/res/atns-icon.png"));
64         setWindowTitle("Architektonas");
65
66         CreateActions();
67         CreateMenus();
68         CreateToolbars();
69
70         // Create Dock widgets
71         QDockWidget * dock1 = new QDockWidget(tr("Layers"), this);
72         LayerWidget * lw = new LayerWidget;
73         dock1->setWidget(lw);
74         addDockWidget(Qt::RightDockWidgetArea, dock1);
75         QDockWidget * dock2 = new QDockWidget(tr("Blocks"), this);
76         BlockWidget * bw = new BlockWidget;
77         dock2->setWidget(bw);
78         addDockWidget(Qt::RightDockWidgetArea, dock2);
79         QDockWidget * dock3 = new QDockWidget(tr("Object"), this);
80         ObjectWidget * ow = new ObjectWidget;
81         dock3->setWidget(ow);
82         addDockWidget(Qt::RightDockWidgetArea, dock3);
83         // Needed for saveState()
84         dock1->setObjectName("Layers");
85         dock2->setObjectName("Blocks");
86         dock3->setObjectName("Object");
87
88         // Create status bar
89         zoomIndicator = new QLabel("Zoom: 100% Grid: 12.0\" BU: Inch");
90         statusBar()->addPermanentWidget(zoomIndicator);
91         statusBar()->showMessage(tr("Ready"));
92
93         ReadSettings();
94         setUnifiedTitleAndToolBarOnMac(true);
95         Global::font =  new QFont("Verdana", 15, QFont::Bold);
96
97         connect(lw, SIGNAL(LayerDeleted(int)), drawing, SLOT(DeleteCurrentLayer(int)));
98         connect(lw, SIGNAL(LayerToggled()), drawing, SLOT(HandleLayerToggle()));
99         connect(lw, SIGNAL(LayersSwapped(int, int)), drawing, SLOT(HandleLayerSwap(int, int)));
100         connect(this, SIGNAL(ReloadLayers()), lw, SLOT(Reload()));
101
102         connect(drawing, SIGNAL(ObjectHovered(Object *)), ow, SLOT(ShowInfo(Object *)));
103         connect(drawing, SIGNAL(NeedZoomUpdate()), this, SLOT(UpdateZoom()));
104 }
105
106 void ApplicationWindow::closeEvent(QCloseEvent * event)
107 {
108         WriteSettings();
109         event->accept();                // Use ignore() if can't close for some reason
110         //Do we have a memory leak here if we don't delete the font in the Object???
111 }
112
113 void ApplicationWindow::FileNew(void)
114 {
115         // Warn the user if drawing has changed and hasn't been saved...
116         if (drawing->dirty)
117         {
118                 QMessageBox msg;
119
120                 msg.setText("The document has been modified.");
121                 msg.setInformativeText("Do you want to save your changes?");
122                 msg.setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);
123                 msg.setDefaultButton(QMessageBox::Save);
124                 int response = msg.exec();
125
126                 switch (response)
127                 {
128                 case QMessageBox::Save:
129                         // Save was clicked
130                         FileSave();
131                         break;
132                 case QMessageBox::Discard:
133                         // Don't Save was clicked
134                         break;
135                 case QMessageBox::Cancel:
136                         // Cancel was clicked
137                         return;
138 //                      break;
139                 }
140         }
141
142         FileIO::ResetLayerVectors();
143         emit ReloadLayers();
144         DeleteContents(drawing->document.objects);
145         drawing->document.objects.clear();
146         drawing->dirty = false;
147         drawing->update();
148         documentName.clear();
149         setWindowTitle("Architektonas - Untitled");
150         statusBar()->showMessage(tr("New drawing is ready."));
151 }
152
153 void ApplicationWindow::FileOpen(void)
154 {
155         QString filename = QFileDialog::getOpenFileName(this, tr("Open Drawing"),
156                 "", tr("Architektonas files (*.drawing)"));
157
158         // User cancelled open
159         if (filename.isEmpty())
160                 return;
161
162         FILE * file = fopen(filename.toUtf8().data(), "r");
163
164         if (file == 0)
165         {
166                 QMessageBox msg;
167                 msg.setText(QString(tr("Could not open file \"%1\" for loading!")).arg(filename));
168                 msg.setIcon(QMessageBox::Critical);
169                 msg.exec();
170                 return;
171         }
172
173         Container container(true);      // Make sure it's a top level container...
174         bool successful = FileIO::LoadAtnsFile(file, &container);
175         fclose(file);
176
177         if (!successful)
178         {
179                 QMessageBox msg;
180                 msg.setText(QString(tr("Could not load file \"%1\"!")).arg(filename));
181                 msg.setIcon(QMessageBox::Critical);
182                 msg.exec();
183                 // Make sure to delete any hanging objects in the container...
184                 DeleteContents(container.objects);
185                 return;
186         }
187
188 //printf("FileOpen: container size = %li\n", container.objects.size());
189         // Keep memory leaks from happening by getting rid of the old document
190         DeleteContents(drawing->document.objects);
191         emit ReloadLayers();
192         // We can do this because the vector is just a bunch of pointers to our
193         // Objects, and the Containers (non-empty) can be moved this way with no
194         // problem.
195         drawing->document = container;
196         drawing->dirty = false;
197         drawing->update();
198         documentName = filename;
199         setWindowTitle(QString("Architektonas - %1").arg(documentName));
200         statusBar()->showMessage(tr("Drawing loaded."));
201 }
202
203 void ApplicationWindow::FileSave(void)
204 {
205         if (documentName.isEmpty())
206                 documentName = QFileDialog::getSaveFileName(this, tr("Save Drawing"),
207                         "", tr("Architektonas drawings (*.drawing)"));
208
209         FILE * file = fopen(documentName.toUtf8().data(), "w");
210
211         if (file == 0)
212         {
213                 QMessageBox msg;
214                 msg.setText(QString(tr("Could not open file \"%1\" for saving!")).arg(documentName));
215                 msg.setIcon(QMessageBox::Critical);
216                 msg.exec();
217                 return;
218         }
219
220         bool successful = FileIO::SaveAtnsFile(file, &drawing->document);
221         fclose(file);
222
223         if (!successful)
224         {
225                 QMessageBox msg;
226                 msg.setText(QString(tr("Could not save file \"%1\"!")).arg(documentName));
227                 msg.setIcon(QMessageBox::Critical);
228                 msg.exec();
229                 // In this case, we should unlink the created file, since it's not
230                 // right...
231 //              unlink(documentName.toUtf8().data());
232                 QFile::remove(documentName);
233                 return;
234         }
235
236         drawing->dirty = false;
237         setWindowTitle(QString("Architektonas - %1").arg(documentName));
238         statusBar()->showMessage(tr("Drawing saved."));
239 }
240
241 void ApplicationWindow::FileSaveAs(void)
242 {
243         QString filename = QFileDialog::getSaveFileName(this, tr("Save Drawing As"),
244                 documentName, tr("Architektonas drawings (*.drawing)"));
245
246         if (!filename.isEmpty())
247         {
248                 documentName = filename;
249                 FileSave();
250         }
251 }
252
253 void ApplicationWindow::PrintPreview(void)
254 {
255         QPrintPreviewDialog * dlg = new QPrintPreviewDialog(this);
256
257         connect(dlg, SIGNAL(paintRequested(QPrinter *)), this, SLOT(HandlePrintRequest(QPrinter *)));
258
259         dlg->exec();
260 }
261
262 void ApplicationWindow::HandlePrintRequest(QPrinter * printer)
263 {
264         QPainter qtPainter(printer);
265         Painter painter(&qtPainter);
266
267         // Save vars for screen
268         Point originSave = Global::origin;
269         double zoomSave = Global::zoom;
270         Vector screenSizeSave = Global::screenSize;
271
272         // Adjust zoom + origin to fit the paper (or NxM pages if we have 'em)
273         Rect r = drawing->GetObjectExtents((Object *)(&(drawing->document)));
274
275         QPageLayout pageLayout = printer->pageLayout();
276         QRect pageRect = pageLayout.paintRectPixels(printer->resolution());
277
278         Global::origin = r.BottomLeft();
279         Global::screenSize.x = pageRect.width();
280         Global::screenSize.y = pageRect.height();
281
282         double xScale = (double)pageRect.width() / r.Width();
283         double yScale = (double)pageRect.height() / r.Height();
284         Global::zoom = qMin(xScale, yScale);
285
286         if (xScale < yScale)
287                 Global::origin.y -= (((double)pageRect.height() / Global::zoom) - r.Height()) / 2.0;
288         else
289                 Global::origin.x -= (((double)pageRect.width() / Global::zoom) - r.Width()) / 2.0;
290
291         // Do object rendering...
292         for(int i=0; i<Global::numLayers; i++)
293                 drawing->RenderObjects(&painter, drawing->document.objects, i);
294
295         // Restore vars
296         Global::origin = originSave;
297         Global::zoom = zoomSave;
298         Global::screenSize = screenSizeSave;
299 }
300
301 void ApplicationWindow::contextMenuEvent(QContextMenuEvent * event)
302 {
303         QMenu menu(this);
304         menu.addAction(mirrorAct);
305         menu.addAction(rotateAct);
306         menu.addAction(trimAct);
307         menu.exec(event->globalPos());
308 }
309
310 void ApplicationWindow::SnapToGridTool(void)
311 {
312         Global::snapToGrid = snapToGridAct->isChecked();
313 }
314
315 void ApplicationWindow::FixAngle(void)
316 {
317         Global::fixedAngle = fixAngleAct->isChecked();
318 }
319
320 void ApplicationWindow::FixLength(void)
321 {
322         Global::fixedLength = fixLengthAct->isChecked();
323 }
324
325 void ApplicationWindow::DeleteTool(void)
326 {
327         // For this tool, we check first to see if anything is selected. If so, we
328         // delete those and *don't* select the delete tool.
329 //      if (drawing->numSelected > 0)
330         if (drawing->select.size() > 0)
331         {
332                 DeleteSelectedObjects(drawing->document.objects);
333                 drawing->update();
334                 deleteAct->setChecked(false);
335                 return;
336         }
337
338         // Otherwise, toggle the state of the tool
339         ClearUIToolStatesExcept(deleteAct);
340         SetInternalToolStates();
341 }
342
343 void ApplicationWindow::DimensionTool(void)
344 {
345         ClearUIToolStatesExcept(addDimensionAct);
346         SetInternalToolStates();
347 }
348
349 void ApplicationWindow::RotateTool(void)
350 {
351         ClearUIToolStatesExcept(rotateAct);
352
353         // Do tear-down if Rotate tool has been turned off
354         if (!rotateAct->isChecked())
355                 drawing->RotateHandler(ToolCleanup, Point(0, 0));
356
357         SetInternalToolStates();
358 }
359
360 void ApplicationWindow::MirrorTool(void)
361 {
362         ClearUIToolStatesExcept(mirrorAct);
363
364         // Do tear-down if Rotate tool has been turned off
365         if (!mirrorAct->isChecked())
366                 drawing->MirrorHandler(ToolCleanup, Point(0, 0));
367
368         SetInternalToolStates();
369 }
370
371 void ApplicationWindow::TrimTool(void)
372 {
373         ClearUIToolStatesExcept(trimAct);
374         SetInternalToolStates();
375 }
376
377 void ApplicationWindow::TriangulateTool(void)
378 {
379         ClearUIToolStatesExcept(triangulateAct);
380         SetInternalToolStates();
381 }
382
383 void ApplicationWindow::AddLineTool(void)
384 {
385         ClearUIToolStatesExcept(addLineAct);
386         SetInternalToolStates();
387 }
388
389 void ApplicationWindow::AddCircleTool(void)
390 {
391         ClearUIToolStatesExcept(addCircleAct);
392         SetInternalToolStates();
393 }
394
395 void ApplicationWindow::AddArcTool(void)
396 {
397         ClearUIToolStatesExcept(addArcAct);
398         SetInternalToolStates();
399 }
400
401 void ApplicationWindow::AddPolygonTool(void)
402 {
403         ClearUIToolStatesExcept(addPolygonAct);
404         SetInternalToolStates();
405 }
406
407 void ApplicationWindow::AddSplineTool(void)
408 {
409         ClearUIToolStatesExcept(addSplineAct);
410         SetInternalToolStates();
411 }
412
413 void ApplicationWindow::ZoomInTool(void)
414 {
415         double zoomFactor = 1.20;
416         QSize size = drawing->size();
417         Vector center = Painter::QtToCartesianCoords(Vector(size.width() / 2.0, size.height() / 2.0));
418
419         Global::origin = center - ((center - Global::origin) / zoomFactor);
420         Global::zoom *= zoomFactor;
421
422         UpdateZoom();
423 }
424
425 void ApplicationWindow::ZoomOutTool(void)
426 {
427         double zoomFactor = 1.20;
428         QSize size = drawing->size();
429         Vector center = Painter::QtToCartesianCoords(Vector(size.width() / 2.0, size.height() / 2.0));
430
431         Global::origin = center + ((Global::origin - center) * zoomFactor);
432         Global::zoom /= zoomFactor;
433
434         UpdateZoom();
435 }
436
437 void ApplicationWindow::UpdateZoom(void)
438 {
439         // And now, a bunch of heuristics to select the right grid size--autogrid!
440         // :-P
441         if (Global::zoom < 0.25)
442                 Global::gridSpacing = 48.0;
443         else if (Global::zoom >= 0.25 && Global::zoom < 0.50)
444                 Global::gridSpacing = 36.0;
445         else if (Global::zoom >= 0.50 && Global::zoom < 1.00)
446                 Global::gridSpacing = 24.0;
447         else if (Global::zoom >= 1.00 && Global::zoom < 2.00)
448                 Global::gridSpacing = 12.0;
449         else if (Global::zoom >= 2.00 && Global::zoom < 4.00)
450                 Global::gridSpacing = 6.0;
451         else if (Global::zoom >= 4.00 && Global::zoom < 8.00)
452                 Global::gridSpacing = 3.0;
453         else if (Global::zoom >= 8.00 && Global::zoom < 16.00)
454                 Global::gridSpacing = 1.0;
455         else if (Global::zoom >= 16.00 && Global::zoom < 32.00)
456                 Global::gridSpacing = 0.5;
457         else if (Global::zoom >= 32.00 && Global::zoom < 64.00)
458                 Global::gridSpacing = 0.25;
459         else if (Global::zoom >= 64.00 && Global::zoom < 128.00)
460                 Global::gridSpacing = 0.125;
461         else if (Global::zoom >= 128.00 && Global::zoom < 256.00)
462                 Global::gridSpacing = 0.0625;
463         else if (Global::zoom >= 256.00 && Global::zoom < 512.00)
464                 Global::gridSpacing = 0.03125;
465         else
466                 Global::gridSpacing = 0.015625;
467
468 //      drawing->SetGridSize((double)(Global::gridSpacing * Global::zoom));
469         drawing->update();
470
471         zoomIndicator->setText(QString("Zoom: %1% Grid: %2\" BU: Inch").arg(Global::zoom * 100.0).arg(Global::gridSpacing));
472
473         // This is the problem...  Changing this causes the state to update itself again, screwing up the origin...  !!! FIX !!! (commented out for now)
474 //      baseUnitInput->setText(QString("%1").arg(Global::gridSpacing));
475 }
476
477 void ApplicationWindow::ClearUIToolStatesExcept(QAction * exception)
478 {
479         QAction * actionList[] = {
480                 addArcAct, addLineAct, addCircleAct, addDimensionAct, addPolygonAct,
481                 addSplineAct, deleteAct, rotateAct, mirrorAct, trimAct,
482                 triangulateAct, 0
483         };
484
485         for(int i=0; actionList[i]!=0; i++)
486         {
487                 if (actionList[i] != exception)
488                         actionList[i]->setChecked(false);
489         }
490 }
491
492 void ApplicationWindow::SetInternalToolStates(void)
493 {
494         // We can be sure that if we've come here, then either an active tool is
495         // being deactivated, or a new tool is being created. In either case, the
496         // old tool needs to be deleted.
497         Global::toolState = TSNone;
498
499         if (addLineAct->isChecked())
500                 Global::tool = TTLine;
501         else if (addCircleAct->isChecked())
502                 Global::tool = TTCircle;
503         else if (addArcAct->isChecked())
504                 Global::tool = TTArc;
505         else if (addDimensionAct->isChecked())
506                 Global::tool = TTDimension;
507         else if (addSplineAct->isChecked())
508                 Global::tool = TTSpline;
509         else if (addPolygonAct->isChecked())
510                 Global::tool = TTPolygon;
511         else if (deleteAct->isChecked())
512                 Global::tool = TTDelete;
513         else if (mirrorAct->isChecked())
514                 Global::tool = TTMirror;
515         else if (rotateAct->isChecked())
516                 Global::tool = TTRotate;
517         else if (trimAct->isChecked())
518                 Global::tool = TTTrim;
519         else if (triangulateAct->isChecked())
520                 Global::tool = TTTriangulate;
521         else
522                 Global::tool = TTNone;
523
524         drawing->update();
525 }
526
527 void ApplicationWindow::HelpAbout(void)
528 {
529         aboutWin->show();
530 }
531
532 void ApplicationWindow::Settings(void)
533 {
534         SettingsDialog dlg(this);
535         dlg.generalTab->antialiasChk->setChecked(drawing->useAntialiasing);
536
537         if (dlg.exec() == false)
538                 return;
539
540         // Deal with stuff here (since user hit "OK" button...)
541         drawing->useAntialiasing = dlg.generalTab->antialiasChk->isChecked();
542         WriteSettings();
543 }
544
545 //
546 // Group a bunch of selected objects (which can include other groups) together
547 // or ungroup a selected group.
548 //
549 void ApplicationWindow::HandleGrouping(void)
550 {
551         int numSelected = drawing->select.size();
552
553         // If nothing selected, do nothing
554         if (numSelected == 0)
555         {
556                 statusBar()->showMessage(tr("No objects selected to make a group from."));
557                 return;
558         }
559
560         // If it's a group that's selected, ungroup it and leave the objects in a
561         // selected state
562         if (numSelected == 1)
563         {
564                 Object * obj = (Object *)drawing->select[0];
565
566                 if (obj->type != OTContainer)
567                 {
568                         statusBar()->showMessage(tr("A group requires two or more selected objects."));
569                         return;
570                 }
571
572                 // Need the parent of the group, we're assuming here that the parent is
573                 // the drawing's document. Does it matter? Maybe...
574                 // Could just stipulate that grouping like this only takes place where
575                 // the parent of the group is the drawing's document. Makes life much
576                 // simpler.
577                 Container * c = (Container *)obj;
578                 SelectAll(c->objects);
579                 RemoveSelectedObjects(drawing->document.objects);
580                 AddObjectsTo(drawing->document.objects, c->objects);
581                 drawing->select.clear();
582                 AddObjectsTo(drawing->select, c->objects);
583                 delete c;
584                 statusBar()->showMessage(tr("Objects ungrouped."));
585 //printf("Ungroup: document size = %li\n", drawing->document.objects.size());
586         }
587         // Otherwise, if it's a group of 2 or more objects (which can be groups too)
588         // group them and select the group
589         else if (numSelected > 1)
590         {
591                 Container * c = new Container();
592                 MoveSelectedObjectsTo(c->objects, drawing->document.objects);
593                 drawing->document.objects.push_back(c);
594                 ClearSelected(c->objects);
595                 c->selected = true;
596                 c->layer = Global::activeLayer;
597
598                 Rect r = drawing->GetObjectExtents((Object *)c);
599                 c->p[0] = r.TopLeft();
600                 c->p[1] = r.BottomRight();
601
602                 drawing->select.clear();
603                 drawing->select.push_back(c);
604                 statusBar()->showMessage(QString(tr("Grouped %1 objects.")).arg(numSelected));
605 //printf("Group: document size = %li\n", drawing->document.objects.size());
606         }
607
608         drawing->update();
609 }
610
611 void ApplicationWindow::HandleConnection(void)
612 {
613 #if 0
614 //double tt = Geometry::ParameterOfLineAndPoint(Vector(0, 0), Vector(10, 0), Vector(8, 2));
615 //printf("Parameter of point @ (8,2) of line (0,0), (10,0): %lf\n", tt);
616         int itemsSelected = drawing->document.ItemsSelected();
617
618         // If nothing selected, do nothing
619         if (itemsSelected == 0)
620         {
621                 statusBar()->showMessage(tr("No objects selected to connect."));
622                 return;
623         }
624
625         // If one thing selected, do nothing
626         if (itemsSelected == 1)
627         {
628                 statusBar()->showMessage(tr("Nothing to connect object to."));
629                 return;
630         }
631
632         // This is O(n^2 / 2) :-P
633         for(int i=0; i<itemsSelected; i++)
634         {
635                 Object * obj1 = drawing->document.SelectedItem(i);
636
637                 for(int j=i+1; j<itemsSelected; j++)
638                 {
639                         Object * obj2 = drawing->document.SelectedItem(j);
640                         double t, u, v, w;
641
642 //                      if ((obj1->type != OTLine) || (obj2->type != OTLine))
643 //                              continue;
644
645                         if ((obj1->type == OTLine) && (obj2->type == OTLine))
646                         {
647 //printf("Testing objects for intersection (%X, %X)...\n", obj1, obj2);
648                                 int intersects = Geometry::Intersects((Line *)obj1, (Line *)obj2, &t, &u);
649 //printf("  (%s) --> t=%lf, u=%lf\n", (intersects ? "true" : "FALSE"), t, u);
650
651                                 if (intersects)
652                                 {
653         //printf("Connecting objects (%X, %X)...\n", obj1, obj2);
654                                         obj1->Connect(obj2, u);
655                                         obj2->Connect(obj1, t);
656                                 }
657                         }
658                         else if (((obj1->type == OTLine) && (obj2->type == OTDimension))
659                                 || ((obj2->type == OTLine) && (obj1->type == OTDimension)))
660                         {
661 printf("Testing Line<->Dimension intersection...\n");
662                                 Line * line = (Line *)(obj1->type == OTLine ? obj1 : obj2);
663                                 Dimension * dim = (Dimension *)(obj1->type == OTDimension ? obj1 : obj2);
664
665                                 int intersects = Geometry::Intersects(line, dim, &t, &u);
666 printf("   -> intersects = %i, t=%lf, u=%lf\n", intersects, t, u);
667
668                                 if (intersects)
669                                 {
670                                         obj1->Connect(obj2, u);
671                                         obj2->Connect(obj1, t);
672                                 }
673                         }
674                 }
675         }
676 #else
677 #endif
678 }
679
680 void ApplicationWindow::HandleDisconnection(void)
681 {
682 }
683
684 void ApplicationWindow::HandleGridSizeInPixels(int /*size*/)
685 {
686 //      drawing->SetGridSize((uint32_t)size);
687         drawing->update();
688 }
689
690 void ApplicationWindow::HandleGridSizeInBaseUnits(QString text)
691 {
692         // Parse the text...
693         bool ok;
694         double value = text.toDouble(&ok);
695
696         // Nothing parsable to a double, so quit...
697         if (!ok || value == 0)
698                 return;
699
700 //      drawing->gridSpacing = value;
701 //      Painter::zoom = drawing->gridPixels / drawing->gridSpacing;
702         Global::gridSpacing = value;
703         Global::zoom = drawing->gridPixels / Global::gridSpacing;
704 //      drawing->UpdateGridBackground();
705         drawing->update();
706 }
707
708 void ApplicationWindow::HandleDimensionSize(QString text)
709 {
710 /*
711 This is the third input on the view toolbar; not sure what it was supposed to do... (resize all dimensions in the drawing?)
712 */
713         // Parse the text...
714         bool ok;
715         double value = text.toDouble(&ok);
716
717         // Nothing parsable to a double, so quit...
718         if (!ok || value == 0)
719                 return;
720
721 //      drawing->document.ResizeAllDimensions(value);
722         drawing->update();
723 }
724
725 void ApplicationWindow::EditCopy(void)
726 {
727         if (drawing->select.size() > 0)
728         {
729                 DeleteContents(clipboard);
730                 clipboard = CopySelectedObjects(drawing->document.objects);
731         }
732 }
733
734 void ApplicationWindow::EditCut(void)
735 {
736         if (drawing->select.size() > 0)
737         {
738                 DeleteContents(clipboard);
739                 clipboard = MoveSelectedObjectsFrom(drawing->document.objects);
740                 drawing->update();
741         }
742 }
743
744 void ApplicationWindow::EditPaste(void)
745 {
746         if (clipboard.size() > 0)
747         {
748                 // We want to maybe make it so that the pasted objects are being moved in a "mouse drag" state...
749                 ClearSelected(drawing->document.objects);
750                 SelectAll(clipboard);
751                 drawing->document.Add(CopyObjects(clipboard));
752                 drawing->update();
753         }
754 }
755
756 //
757 // Select all *visible* objects.  If an object is on a layer that is not
758 // visible, skip it.
759 //
760 void ApplicationWindow::SelectAllObjects(void)
761 {
762         // Set object's state & update the drawing
763         SelectAll(drawing->document.objects);
764         drawing->update();
765 }
766
767 void ApplicationWindow::CreateActions(void)
768 {
769         exitAct = CreateAction(tr("&Quit"), tr("Quit"), tr("Exits the application."),
770                 QIcon(":/res/quit.png"), QKeySequence(tr("Ctrl+q")));
771         connect(exitAct, SIGNAL(triggered()), this, SLOT(close()));
772
773         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);
774         connect(snapToGridAct, SIGNAL(triggered()), this, SLOT(SnapToGridTool()));
775
776         fixAngleAct = CreateAction(tr("Fix &Angle"), tr("Fix Angle"), tr("Fixes the angle of an object."),
777                 QIcon(":/res/fix-angle.png"), QKeySequence(tr("F,A")), true);
778         connect(fixAngleAct, SIGNAL(triggered()), this, SLOT(FixAngle()));
779
780         fixLengthAct = CreateAction(tr("Fix &Length"), tr("Fix Length"), tr("Fixes the length of an object."),
781                 QIcon(":/res/fix-length.png"), QKeySequence(tr("F,L")), true);
782         connect(fixLengthAct, SIGNAL(triggered()), this, SLOT(FixLength()));
783
784         deleteAct = CreateAction(tr("&Delete"), tr("Delete Object"), tr("Deletes selected objects."), QIcon(":/res/delete-tool.png"), QKeySequence(tr("Delete")), true);
785         connect(deleteAct, SIGNAL(triggered()), this, SLOT(DeleteTool()));
786
787         addDimensionAct = CreateAction(tr("Add &Dimension"), tr("Add Dimension"), tr("Adds a dimension to the drawing."), QIcon(":/res/dimension-tool.png"), QKeySequence("D,I"), true);
788         connect(addDimensionAct, SIGNAL(triggered()), this, SLOT(DimensionTool()));
789
790         addLineAct = CreateAction(tr("Add &Line"), tr("Add Line"), tr("Adds lines to the drawing."), QIcon(":/res/add-line-tool.png"), QKeySequence("A,L"), true);
791         connect(addLineAct, SIGNAL(triggered()), this, SLOT(AddLineTool()));
792
793         addCircleAct = CreateAction(tr("Add &Circle"), tr("Add Circle"), tr("Adds circles to the drawing."), QIcon(":/res/add-circle-tool.png"), QKeySequence("A,C"), true);
794         connect(addCircleAct, SIGNAL(triggered()), this, SLOT(AddCircleTool()));
795
796         addArcAct = CreateAction(tr("Add &Arc"), tr("Add Arc"), tr("Adds arcs to the drawing."), QIcon(":/res/add-arc-tool.png"), QKeySequence("A,A"), true);
797         connect(addArcAct, SIGNAL(triggered()), this, SLOT(AddArcTool()));
798
799         addPolygonAct = CreateAction(tr("Add &Polygon"), tr("Add Polygon"), tr("Add polygons to the drawing."), QIcon(":/res/add-polygon-tool.png"), QKeySequence("A,P"), true);
800         connect(addPolygonAct, SIGNAL(triggered()), this, SLOT(AddPolygonTool()));
801
802         addSplineAct = CreateAction(tr("Add &Spline"), tr("Add Spline"), tr("Add a NURB spline to the drawing."), QIcon(":/res/add-spline-tool.png"), QKeySequence("A,S"), true);
803         connect(addSplineAct, SIGNAL(triggered()), this, SLOT(AddSplineTool()));
804
805         aboutAct = CreateAction(tr("About &Architektonas"), tr("About Architektonas"), tr("Gives information about this program."), QIcon(":/res/generic-tool.png"), QKeySequence());
806         connect(aboutAct, SIGNAL(triggered()), this, SLOT(HelpAbout()));
807
808         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);
809         connect(rotateAct, SIGNAL(triggered()), this, SLOT(RotateTool()));
810
811         zoomInAct = CreateAction(tr("Zoom &In"), tr("Zoom In"), tr("Zoom in on the document."), QIcon(":/res/zoom-in.png"), QKeySequence(tr("+")), QKeySequence(tr("=")));
812         connect(zoomInAct, SIGNAL(triggered()), this, SLOT(ZoomInTool()));
813
814         zoomOutAct = CreateAction(tr("Zoom &Out"), tr("Zoom Out"), tr("Zoom out of the document."), QIcon(":/res/zoom-out.png"), QKeySequence(tr("-")));
815         connect(zoomOutAct, SIGNAL(triggered()), this, SLOT(ZoomOutTool()));
816
817         fileNewAct = CreateAction(tr("&New Drawing"), tr("New Drawing"), tr("Creates a new drawing."), QIcon(":/res/file-new.png"), QKeySequence(tr("Ctrl+n")));
818         connect(fileNewAct, SIGNAL(triggered()), this, SLOT(FileNew()));
819
820         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")));
821         connect(fileOpenAct, SIGNAL(triggered()), this, SLOT(FileOpen()));
822
823         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")));
824         connect(fileSaveAct, SIGNAL(triggered()), this, SLOT(FileSave()));
825
826         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")));
827         connect(fileSaveAsAct, SIGNAL(triggered()), this, SLOT(FileSaveAs()));
828
829         fileCloseAct = CreateAction(tr("&Close Drawing"), tr("Close Drawing"), tr("Closes the current drawing."), QIcon(":/res/file-close.png"), QKeySequence(tr("Ctrl+w")));
830
831         settingsAct = CreateAction(tr("&Settings"), tr("Settings"), tr("Change certain defaults for Architektonas."), QIcon(":/res/settings.png"), QKeySequence());
832         connect(settingsAct, SIGNAL(triggered()), this, SLOT(Settings()));
833
834         groupAct = CreateAction(tr("&Group"), tr("Group"), tr("Group/ungroup selected objects."), QIcon(":/res/group-tool.png"), QKeySequence("g"));
835         connect(groupAct, SIGNAL(triggered()), this, SLOT(HandleGrouping()));
836
837         connectAct = CreateAction(tr("&Connect"), tr("Connect"), tr("Connect objects at point."), QIcon(":/res/connect-tool.png"), QKeySequence("c,c"));
838         connect(connectAct, SIGNAL(triggered()), this, SLOT(HandleConnection()));
839
840         disconnectAct = CreateAction(tr("&Disconnect"), tr("Disconnect"), tr("Disconnect objects joined at point."), QIcon(":/res/disconnect-tool.png"), QKeySequence("d,d"));
841         connect(disconnectAct, SIGNAL(triggered()), this, SLOT(HandleDisconnection()));
842
843         mirrorAct = CreateAction(tr("&Mirror"), tr("Mirror"), tr("Mirror selected objects around a line."), QIcon(":/res/mirror-tool.png"), QKeySequence("m,i"), true);
844         connect(mirrorAct, SIGNAL(triggered()), this, SLOT(MirrorTool()));
845
846         trimAct = CreateAction(tr("&Trim"), tr("Trim"), tr("Trim extraneous lines from selected objects."), QIcon(":/res/trim-tool.png"), QKeySequence("t,r"), true);
847         connect(trimAct, SIGNAL(triggered()), this, SLOT(TrimTool()));
848
849         triangulateAct = CreateAction(tr("&Triangulate"), tr("Triangulate"), tr("Make triangles from selected lines, preserving their lengths."), QIcon(":/res/triangulate-tool.png"), QKeySequence("t,g"), true);
850         connect(triangulateAct, SIGNAL(triggered()), this, SLOT(TriangulateTool()));
851
852         editCutAct = CreateAction(tr("&Cut Objects"), tr("Cut Objects"), tr("Cut objects from the drawing to the clipboard."), QIcon(":/res/editcut2.png"), QKeySequence(tr("Ctrl+x")));
853         connect(editCutAct, SIGNAL(triggered()), this, SLOT(EditCut()));
854
855         editCopyAct = CreateAction(tr("&Copy Objects"), tr("Copy Objects"), tr("Copy objects from the drawing to the clipboard."), QIcon(":/res/editcopy2.png"), QKeySequence(tr("Ctrl+c")));
856         connect(editCopyAct, SIGNAL(triggered()), this, SLOT(EditCopy()));
857
858         editPasteAct = CreateAction(tr("&Paste Objects"), tr("Paste Objects"), tr("Paste objects from the clipboard to the drawing."), QIcon(":/res/editpaste2.png"), QKeySequence(tr("Ctrl+v")));
859         connect(editPasteAct, SIGNAL(triggered()), this, SLOT(EditPaste()));
860
861         selectAllAct = CreateAction(tr("Select &All"), tr("Select All Objects"), tr("Select all objects in the drawing."), QIcon(), QKeySequence(tr("Ctrl+a")));
862         connect(selectAllAct, SIGNAL(triggered()), this, SLOT(SelectAllObjects()));
863
864         printPreviewAct = CreateAction(tr("&Print Preview"), tr("Print Preview"), tr("Shows preview of printing operation."), QIcon(":/res/print-preview.png"), QKeySequence(tr("Ctrl+p")));
865         connect(printPreviewAct, SIGNAL(triggered()), this, SLOT(PrintPreview()));
866
867 //Hm. I think we'll have to have separate logic to do the "Radio Group Toolbar" thing...
868 // Yup, in order to turn them off, we'd have to have an "OFF" toolbar button. Ick.
869 /*      QActionGroup * group = new QActionGroup(this);
870         group->addAction(deleteAct);
871         group->addAction(addDimensionAct);
872         group->addAction(addLineAct);
873         group->addAction(addCircleAct);
874         group->addAction(addArcAct);//*/
875 }
876
877 //
878 // Consolidates action creation from a multi-step process to a single-step one.
879 //
880 QAction * ApplicationWindow::CreateAction(QString name, QString tooltip,
881         QString statustip, QIcon icon, QKeySequence key, bool checkable/*= false*/)
882 {
883         QAction * action = new QAction(icon, name, this);
884         action->setToolTip(tooltip);
885         action->setStatusTip(statustip);
886         action->setShortcut(key);
887         action->setCheckable(checkable);
888
889         return action;
890 }
891
892 //
893 // This is essentially the same as the previous function, but this allows more
894 // than one key sequence to be added as key shortcuts.
895 //
896 QAction * ApplicationWindow::CreateAction(QString name, QString tooltip,
897         QString statustip, QIcon icon, QKeySequence key1, QKeySequence key2,
898         bool checkable/*= false*/)
899 {
900         QAction * action = new QAction(icon, name, this);
901         action->setToolTip(tooltip);
902         action->setStatusTip(statustip);
903         QList<QKeySequence> keyList;
904         keyList.append(key1);
905         keyList.append(key2);
906         action->setShortcuts(keyList);
907         action->setCheckable(checkable);
908
909         return action;
910 }
911
912 void ApplicationWindow::CreateMenus(void)
913 {
914         QMenu * menu = menuBar()->addMenu(tr("&File"));
915         menu->addAction(fileNewAct);
916         menu->addAction(fileOpenAct);
917         menu->addAction(fileSaveAct);
918         menu->addAction(fileSaveAsAct);
919         menu->addAction(fileCloseAct);
920         menu->addSeparator();
921         menu->addAction(printPreviewAct);
922         menu->addSeparator();
923         menu->addAction(exitAct);
924
925         menu = menuBar()->addMenu(tr("&View"));
926         menu->addAction(zoomInAct);
927         menu->addAction(zoomOutAct);
928
929         menu = menuBar()->addMenu(tr("&Edit"));
930         menu->addAction(snapToGridAct);
931         menu->addAction(groupAct);
932         menu->addAction(fixAngleAct);
933         menu->addAction(fixLengthAct);
934         menu->addAction(rotateAct);
935         menu->addAction(mirrorAct);
936         menu->addAction(trimAct);
937         menu->addAction(triangulateAct);
938         menu->addAction(connectAct);
939         menu->addAction(disconnectAct);
940         menu->addSeparator();
941         menu->addAction(selectAllAct);
942         menu->addAction(editCutAct);
943         menu->addAction(editCopyAct);
944         menu->addAction(editPasteAct);
945         menu->addAction(deleteAct);
946         menu->addSeparator();
947         menu->addAction(addLineAct);
948         menu->addAction(addCircleAct);
949         menu->addAction(addArcAct);
950         menu->addAction(addPolygonAct);
951         menu->addAction(addSplineAct);
952         menu->addAction(addDimensionAct);
953         menu->addSeparator();
954         menu->addAction(settingsAct);
955
956         menu = menuBar()->addMenu(tr("&Help"));
957         menu->addAction(aboutAct);
958 }
959
960 void ApplicationWindow::CreateToolbars(void)
961 {
962         QToolBar * toolbar = addToolBar(tr("File"));
963         toolbar->setObjectName("File"); // Needed for saveState()
964         toolbar->addAction(fileNewAct);
965         toolbar->addAction(fileOpenAct);
966         toolbar->addAction(fileSaveAct);
967         toolbar->addAction(fileSaveAsAct);
968         toolbar->addAction(fileCloseAct);
969 //      toolbar->addAction(exitAct);
970
971         toolbar = addToolBar(tr("View"));
972         toolbar->setObjectName("View");
973         toolbar->addAction(zoomInAct);
974         toolbar->addAction(zoomOutAct);
975
976         QSpinBox * spinbox = new QSpinBox;
977         toolbar->addWidget(spinbox);
978 //      QLineEdit * lineedit = new QLineEdit;
979         toolbar->addWidget(baseUnitInput);
980         toolbar->addWidget(dimensionSizeInput);
981
982         toolbar = addToolBar(tr("Edit"));
983         toolbar->setObjectName("Edit");
984         toolbar->addAction(snapToGridAct);
985         toolbar->addAction(groupAct);
986         toolbar->addAction(fixAngleAct);
987         toolbar->addAction(fixLengthAct);
988         toolbar->addAction(rotateAct);
989         toolbar->addAction(mirrorAct);
990         toolbar->addAction(trimAct);
991         toolbar->addAction(triangulateAct);
992         toolbar->addAction(editCutAct);
993         toolbar->addAction(editCopyAct);
994         toolbar->addAction(editPasteAct);
995         toolbar->addAction(deleteAct);
996         toolbar->addAction(connectAct);
997         toolbar->addAction(disconnectAct);
998         toolbar->addSeparator();
999         toolbar->addAction(addLineAct);
1000         toolbar->addAction(addCircleAct);
1001         toolbar->addAction(addArcAct);
1002         toolbar->addAction(addPolygonAct);
1003         toolbar->addAction(addSplineAct);
1004         toolbar->addAction(addDimensionAct);
1005
1006         spinbox->setRange(4, 256);
1007         spinbox->setValue(12);
1008         baseUnitInput->setText("12");
1009         connect(spinbox, SIGNAL(valueChanged(int)), this, SLOT(HandleGridSizeInPixels(int)));
1010         connect(baseUnitInput, SIGNAL(textChanged(QString)), this, SLOT(HandleGridSizeInBaseUnits(QString)));
1011         connect(dimensionSizeInput, SIGNAL(textChanged(QString)), this, SLOT(HandleDimensionSize(QString)));
1012
1013         PenWidget * pw = new PenWidget();
1014         toolbar = addToolBar(tr("Pen"));
1015         toolbar->setObjectName(tr("Pen"));
1016         toolbar->addWidget(pw);
1017         connect(drawing, SIGNAL(ObjectSelected(Object *)), pw, SLOT(SetFields(Object *)));
1018         connect(pw, SIGNAL(WidthSelected(float)), drawing, SLOT(HandlePenWidth(float)));
1019         connect(pw, SIGNAL(StyleSelected(int)), drawing, SLOT(HandlePenStyle(int)));
1020         connect(pw, SIGNAL(ColorSelected(uint32_t)), drawing, SLOT(HandlePenColor(uint32_t)));
1021 //      connect(pw, SIGNAL(StampSelected(void)), drawing, SLOT(HandlePenStamp(QAction *)));
1022         connect(pw->tbStamp, SIGNAL(triggered(QAction *)), drawing, SLOT(HandlePenStamp(QAction *)));
1023         connect(pw->tbDropper, SIGNAL(triggered(QAction *)), drawing, SLOT(HandlePenDropper(QAction *)));
1024 }
1025
1026 void ApplicationWindow::ReadSettings(void)
1027 {
1028         QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint();
1029         QSize size = settings.value("size", QSize(400, 400)).toSize();
1030         drawing->useAntialiasing = settings.value("useAntialiasing", true).toBool();
1031         snapToGridAct->setChecked(settings.value("snapToGrid", true).toBool());
1032         resize(size);
1033         move(pos);
1034         restoreState(settings.value("windowState").toByteArray());
1035 }
1036
1037 void ApplicationWindow::WriteSettings(void)
1038 {
1039         settings.setValue("pos", pos());
1040         settings.setValue("size", size());
1041         settings.setValue("windowState", saveState());
1042         settings.setValue("useAntialiasing", drawing->useAntialiasing);
1043         settings.setValue("snapToGrid", snapToGridAct->isChecked());
1044 }