2 // applicationwindow.cpp: Architektonas
4 // Part of the Architektonas Project
5 // (C) 2011 Underground Software
6 // See the README and GPLv3 files for licensing and warranty information
8 // JLH = James Hammons <jlhamm@acm.org>
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
24 // Uncomment this for debugging...
26 //#define DEBUGFOO // Various tool debugging...
27 //#define DEBUGTP // Toolpalette debugging...
29 #include "applicationwindow.h"
32 #include "blockwidget.h"
33 #include "drawingview.h"
35 #include "generaltab.h"
37 #include "layerwidget.h"
38 #include "objectwidget.h"
40 #include "penwidget.h"
41 #include "settingsdialog.h"
47 DrawingView * ApplicationWindow::drawing;
50 ApplicationWindow::ApplicationWindow():
51 baseUnitInput(new QLineEdit),
52 dimensionSizeInput(new QLineEdit),
53 settings("Underground Software", "Architektonas")
55 drawing = new DrawingView(this);
56 drawing->setMouseTracking(true); // We want *all* mouse events...!
57 drawing->setFocusPolicy(Qt::StrongFocus);
58 setCentralWidget(drawing);
60 aboutWin = new AboutWindow(this);
62 // ((TTEdit *)qApp)->charWnd = new CharWindow(this);
64 setWindowIcon(QIcon(":/res/atns-icon.png"));
65 setWindowTitle("Architektonas");
71 // Create Dock widgets
72 QDockWidget * dock1 = new QDockWidget(tr("Layers"), this);
73 LayerWidget * lw = new LayerWidget;
75 addDockWidget(Qt::RightDockWidgetArea, dock1);
76 QDockWidget * dock2 = new QDockWidget(tr("Blocks"), this);
77 BlockWidget * bw = new BlockWidget;
79 addDockWidget(Qt::RightDockWidgetArea, dock2);
80 QDockWidget * dock3 = new QDockWidget(tr("Object"), this);
81 ObjectWidget * ow = new ObjectWidget;
83 addDockWidget(Qt::RightDockWidgetArea, dock3);
84 // Needed for saveState()
85 dock1->setObjectName("Layers");
86 dock2->setObjectName("Blocks");
87 dock3->setObjectName("Object");
90 zoomIndicator = new QLabel("Grid: 12.0\" BU: Inch");
91 statusBar()->addPermanentWidget(zoomIndicator);
92 statusBar()->showMessage(tr("Ready"));
95 setUnifiedTitleAndToolBarOnMac(true);
96 Global::font = new QFont("Verdana", 15, QFont::Bold);
98 connect(lw, SIGNAL(LayerDeleted(int)), drawing, SLOT(DeleteCurrentLayer(int)));
99 connect(lw, SIGNAL(LayerToggled()), drawing, SLOT(HandleLayerToggle()));
100 connect(lw, SIGNAL(LayersSwapped(int, int)), drawing, SLOT(HandleLayerSwap(int, int)));
102 connect(drawing, SIGNAL(ObjectHovered(Object *)), ow, SLOT(ShowInfo(Object *)));
106 void ApplicationWindow::closeEvent(QCloseEvent * event)
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???
114 void ApplicationWindow::FileNew(void)
116 // Should warn the user if drawing hasn't been saved... !!! FIX !!!
117 DeleteContents(drawing->document.objects);
118 drawing->document.objects.clear();
120 documentName.clear();
121 setWindowTitle("Architektonas - Untitled");
122 statusBar()->showMessage(tr("New drawing is ready."));
126 void ApplicationWindow::FileOpen(void)
128 QString filename = QFileDialog::getOpenFileName(this, tr("Open Drawing"),
129 "", tr("Architektonas files (*.drawing)"));
131 // User cancelled open
132 if (filename.isEmpty())
135 FILE * file = fopen(filename.toUtf8().data(), "r");
140 msg.setText(QString(tr("Could not open file \"%1\" for loading!")).arg(filename));
141 msg.setIcon(QMessageBox::Critical);
146 Container container(true); // Make sure it's a top level container...
147 bool successful = FileIO::LoadAtnsFile(file, &container);
153 msg.setText(QString(tr("Could not load file \"%1\"!")).arg(filename));
154 msg.setIcon(QMessageBox::Critical);
156 // Make sure to delete any hanging objects in the container...
157 DeleteContents(container.objects);
161 //printf("FileOpen: container size = %li\n", container.objects.size());
162 // Keep memory leaks from happening by getting rid of the old document
163 DeleteContents(drawing->document.objects);
164 // We can do this because the vector is just a bunch of pointers to our
165 // Objects, and the Containers (non-empty) can be moved way with no problem.
166 drawing->document = container;
168 documentName = filename;
169 setWindowTitle(QString("Architektonas - %1").arg(documentName));
170 statusBar()->showMessage(tr("Drawing loaded."));
174 void ApplicationWindow::FileSave(void)
176 if (documentName.isEmpty())
177 documentName = QFileDialog::getSaveFileName(this, tr("Save Drawing"),
178 "", tr("Architektonas drawings (*.drawing)"));
180 FILE * file = fopen(documentName.toUtf8().data(), "w");
185 msg.setText(QString(tr("Could not open file \"%1\" for saving!")).arg(documentName));
186 msg.setIcon(QMessageBox::Critical);
191 bool successful = FileIO::SaveAtnsFile(file, &drawing->document);
197 msg.setText(QString(tr("Could not save file \"%1\"!")).arg(documentName));
198 msg.setIcon(QMessageBox::Critical);
200 // In this case, we should unlink the created file, since it's not
202 // unlink(documentName.toUtf8().data());
203 QFile::remove(documentName);
207 setWindowTitle(QString("Architektonas - %1").arg(documentName));
208 statusBar()->showMessage(tr("Drawing saved."));
212 void ApplicationWindow::FileSaveAs(void)
214 QString filename = QFileDialog::getSaveFileName(this, tr("Save Drawing As"),
215 documentName, tr("Architektonas drawings (*.drawing)"));
217 if (!filename.isEmpty())
219 documentName = filename;
225 void ApplicationWindow::SnapToGridTool(void)
227 Global::snapToGrid = snapToGridAct->isChecked();
231 void ApplicationWindow::FixAngle(void)
233 Global::fixedAngle = fixAngleAct->isChecked();
237 void ApplicationWindow::FixLength(void)
239 Global::fixedLength = fixLengthAct->isChecked();
243 void ApplicationWindow::DeleteTool(void)
245 // For this tool, we check first to see if anything is selected. If so, we
246 // delete those and *don't* select the delete tool.
247 // if (drawing->numSelected > 0)
248 if (drawing->select.size() > 0)
250 DeleteSelectedObjects(drawing->document.objects);
252 deleteAct->setChecked(false);
256 // Otherwise, toggle the state of the tool
257 ClearUIToolStatesExcept(deleteAct);
258 SetInternalToolStates();
262 void ApplicationWindow::DimensionTool(void)
264 ClearUIToolStatesExcept(addDimensionAct);
265 SetInternalToolStates();
269 void ApplicationWindow::RotateTool(void)
271 ClearUIToolStatesExcept(rotateAct);
273 // Do tear-down if Rotate tool has been turned off
274 if (!rotateAct->isChecked())
275 drawing->RotateHandler(ToolCleanup, Point(0, 0));
277 SetInternalToolStates();
281 void ApplicationWindow::MirrorTool(void)
283 ClearUIToolStatesExcept(mirrorAct);
285 // Do tear-down if Rotate tool has been turned off
286 if (!mirrorAct->isChecked())
287 drawing->MirrorHandler(ToolCleanup, Point(0, 0));
289 SetInternalToolStates();
293 void ApplicationWindow::TrimTool(void)
295 ClearUIToolStatesExcept(trimAct);
296 SetInternalToolStates();
300 void ApplicationWindow::TriangulateTool(void)
302 ClearUIToolStatesExcept(triangulateAct);
303 SetInternalToolStates();
307 void ApplicationWindow::AddLineTool(void)
309 ClearUIToolStatesExcept(addLineAct);
310 SetInternalToolStates();
314 void ApplicationWindow::AddCircleTool(void)
316 ClearUIToolStatesExcept(addCircleAct);
317 SetInternalToolStates();
321 void ApplicationWindow::AddArcTool(void)
323 ClearUIToolStatesExcept(addArcAct);
324 SetInternalToolStates();
328 void ApplicationWindow::AddPolygonTool(void)
330 ClearUIToolStatesExcept(addPolygonAct);
331 SetInternalToolStates();
335 void ApplicationWindow::AddSplineTool(void)
337 ClearUIToolStatesExcept(addSplineAct);
338 SetInternalToolStates();
342 void ApplicationWindow::ZoomInTool(void)
344 double zoomFactor = 2.0;
346 We need to find the center of the screen, then figure out where the new corner
347 will be in the zoomed in window.
349 So we know in Qt coords, the center is found via:
350 size.width() / 2 --> xCenter
351 size.height() / 2 --> yCenter
353 transform x/yCenter to Cartesian coordinates. So far, so good.
355 when zooming in, new origin will be (xCenter - origin.x) / 2, (yCenter - origin.y) / 2
356 (after subtracting from center, that is...)
358 QSize size = drawing->size();
359 Vector center(size.width() / 2.0, size.height() / 2.0);
360 //printf("Zoom in... Center=%.2f,%.2f; ", center.x, center.y);
361 center = Painter::QtToCartesianCoords(center);
362 //printf("(%.2f,%.2f); origin=%.2f,%.2f; ", center.x, center.y, Painter::origin.x, Painter::origin.y);
363 Vector newOrigin = center - ((center - Global::origin) / zoomFactor);
364 //printf("newOrigin=%.2f,%.2f;\n", newOrigin.x, newOrigin.y);
365 Global::origin = newOrigin;
367 //printf("Zoom in... level going from %02f to ", Painter::zoom);
368 // This just zooms leaving origin intact... should zoom in at the current
370 Global::zoom *= zoomFactor;
371 Global::gridSpacing = drawing->gridPixels / Global::zoom;
372 drawing->UpdateGridBackground();
375 zoomIndicator->setText(QString("Grid: %1\", BU: Inch").arg(Global::gridSpacing));
376 baseUnitInput->setText(QString("%1").arg(Global::gridSpacing));
380 void ApplicationWindow::ZoomOutTool(void)
385 origin = (223, 160.5)
386 newOrigin should be (-10, -10)
389 center - origin = (213, 150.5)
390 origin - center = (-213, -150.5)
392 + center = (-10, -10)
395 double zoomFactor = 2.0;
396 QSize size = drawing->size();
397 Vector center(size.width() / 2.0, size.height() / 2.0);
398 //printf("Zoom out... Center=%.2f,%.2f; ", center.x, center.y);
399 center = Painter::QtToCartesianCoords(center);
400 //printf("(%.2f,%.2f); origin=%.2f,%.2f; ", center.x, center.y, Painter::origin.x, Painter::origin.y);
401 // Vector newOrigin = (center - Painter::origin) * zoomFactor;
402 // Vector newOrigin = center - (Painter::origin * zoomFactor);
403 Vector newOrigin = center + ((Global::origin - center) * zoomFactor);
404 //printf("newOrigin=%.2f,%.2f;\n", newOrigin.x, newOrigin.y);
405 Global::origin = newOrigin;
406 //printf("Zoom out...\n");
407 // This just zooms leaving origin intact... should zoom out at the current
409 Global::zoom /= zoomFactor;
410 Global::gridSpacing = drawing->gridPixels / Global::zoom;
411 drawing->UpdateGridBackground();
414 zoomIndicator->setText(QString("Grid: %1\", BU: Inch").arg(Global::gridSpacing));
415 baseUnitInput->setText(QString("%1").arg(Global::gridSpacing));
419 void ApplicationWindow::ClearUIToolStatesExcept(QAction * exception)
421 QAction * actionList[] = {
422 addArcAct, addLineAct, addCircleAct, addDimensionAct, addPolygonAct,
423 addSplineAct, deleteAct, rotateAct, mirrorAct, trimAct, triangulateAct, 0
426 for(int i=0; actionList[i]!=0; i++)
428 if (actionList[i] != exception)
429 actionList[i]->setChecked(false);
434 void ApplicationWindow::SetInternalToolStates(void)
436 // We can be sure that if we've come here, then either an active tool is
437 // being deactivated, or a new tool is being created. In either case, the
438 // old tool needs to be deleted.
439 Global::toolState = TSNone;
441 if (addLineAct->isChecked())
442 Global::tool = TTLine;
443 else if (addCircleAct->isChecked())
444 Global::tool = TTCircle;
445 else if (addArcAct->isChecked())
446 Global::tool = TTArc;
447 else if (addDimensionAct->isChecked())
448 Global::tool = TTDimension;
449 else if (addSplineAct->isChecked())
450 Global::tool = TTSpline;
451 else if (addPolygonAct->isChecked())
452 Global::tool = TTPolygon;
453 else if (deleteAct->isChecked())
454 Global::tool = TTDelete;
455 else if (mirrorAct->isChecked())
456 Global::tool = TTMirror;
457 else if (rotateAct->isChecked())
458 Global::tool = TTRotate;
459 else if (trimAct->isChecked())
460 Global::tool = TTTrim;
461 else if (triangulateAct->isChecked())
462 Global::tool = TTTriangulate;
464 Global::tool = TTNone;
470 void ApplicationWindow::HelpAbout(void)
476 void ApplicationWindow::Settings(void)
478 SettingsDialog dlg(this);
479 dlg.generalTab->antialiasChk->setChecked(drawing->useAntialiasing);
481 if (dlg.exec() == false)
484 // Deal with stuff here (since user hit "OK" button...)
485 drawing->useAntialiasing = dlg.generalTab->antialiasChk->isChecked();
491 // Group a bunch of selected objects (which can include other groups) together
492 // or ungroup a selected group.
494 void ApplicationWindow::HandleGrouping(void)
497 int itemsSelected = drawing->document.ItemsSelected();
499 // If nothing selected, do nothing
500 if (itemsSelected == 0)
502 statusBar()->showMessage(tr("No objects selected to make a group from."));
506 // If it's a group that's selected, ungroup it and leave the objects in a
508 if (itemsSelected == 1)
510 Object * object = drawing->document.SelectedItem(0);
514 printf("SelectedItem = NULL!\n");
516 printf("SelectedItem = %08X, type = %i\n", object, object->type);
519 if (object == NULL || object->type != OTContainer)
521 statusBar()->showMessage(tr("A group requires two or more selected objects."));
525 // Need the parent of the group, we're assuming here that the parent is
526 // the drawing's document. Does it matter? Maybe...
527 // Could just stipulate that grouping like this only takes place where
528 // the parent of the group is the drawing's document. Makes life much
530 ((Container *)object)->SelectAll();
531 ((Container *)object)->MoveContentsTo(&(drawing->document));
532 drawing->document.Delete(object);
533 statusBar()->showMessage(tr("Objects ungrouped."));
535 // Otherwise, if it's a group of 2 or more objects (which can be groups too)
536 // group them and select the group
537 else if (itemsSelected > 1)
539 Container * container = new Container(Vector(), &(drawing->document));
540 drawing->document.MoveSelectedContentsTo(container);
541 drawing->document.Add(container);
542 container->DeselectAll();
543 container->state = OSSelected;
544 statusBar()->showMessage(QString(tr("Grouped %1 objects.")).arg(itemsSelected));
547 int numSelected = drawing->select.size();
549 // If nothing selected, do nothing
550 if (numSelected == 0)
552 statusBar()->showMessage(tr("No objects selected to make a group from."));
556 // If it's a group that's selected, ungroup it and leave the objects in a
558 if (numSelected == 1)
560 Object * obj = (Object *)drawing->select[0];
562 if (obj->type != OTContainer)
564 statusBar()->showMessage(tr("A group requires two or more selected objects."));
568 // Need the parent of the group, we're assuming here that the parent is
569 // the drawing's document. Does it matter? Maybe...
570 // Could just stipulate that grouping like this only takes place where
571 // the parent of the group is the drawing's document. Makes life much
573 // ((Container *)object)->SelectAll();
574 // ((Container *)object)->MoveContentsTo(&(drawing->document));
575 // drawing->document.Delete(object);
576 Container * c = (Container *)obj;
577 //printf("Ungroup: container size = %li\n", c->objects.size());
578 SelectAll(c->objects);
579 //printf("Ungroup: document size = %li (pre-AddObjectsTo)\n", drawing->document.objects.size());
580 RemoveSelectedObjects(drawing->document.objects);
581 AddObjectsTo(drawing->document.objects, c->objects);
582 drawing->select.clear();
583 AddObjectsTo(drawing->select, c->objects);
585 statusBar()->showMessage(tr("Objects ungrouped."));
586 //printf("Ungroup: document size = %li\n", drawing->document.objects.size());
588 // Otherwise, if it's a group of 2 or more objects (which can be groups too)
589 // group them and select the group
590 else if (numSelected > 1)
592 // Container * container = new Container(Vector(), &(drawing->document));
593 // drawing->document.MoveSelectedContentsTo(container);
594 // drawing->document.Add(container);
595 // container->DeselectAll();
596 // container->state = OSSelected;
598 Container * c = new Container();
599 // AddObjectsTo(c->objects, drawing->select);
600 // RemoveSelectedObjects(drawing->document.objects);
601 MoveSelectedObjectsTo(c->objects, drawing->document.objects);
602 drawing->document.objects.push_back(c);
603 ClearSelected(c->objects);
605 c->layer = Global::currentLayer;
606 drawing->select.clear();
607 drawing->select.push_back(c);
608 statusBar()->showMessage(QString(tr("Grouped %1 objects.")).arg(numSelected));
609 //printf("Group: document size = %li\n", drawing->document.objects.size());
617 void ApplicationWindow::HandleConnection(void)
620 //double tt = Geometry::ParameterOfLineAndPoint(Vector(0, 0), Vector(10, 0), Vector(8, 2));
621 //printf("Parameter of point @ (8,2) of line (0,0), (10,0): %lf\n", tt);
622 int itemsSelected = drawing->document.ItemsSelected();
624 // If nothing selected, do nothing
625 if (itemsSelected == 0)
627 statusBar()->showMessage(tr("No objects selected to connect."));
631 // If one thing selected, do nothing
632 if (itemsSelected == 1)
634 statusBar()->showMessage(tr("Nothing to connect object to."));
638 // This is O(n^2 / 2) :-P
639 for(int i=0; i<itemsSelected; i++)
641 Object * obj1 = drawing->document.SelectedItem(i);
643 for(int j=i+1; j<itemsSelected; j++)
645 Object * obj2 = drawing->document.SelectedItem(j);
648 // if ((obj1->type != OTLine) || (obj2->type != OTLine))
651 if ((obj1->type == OTLine) && (obj2->type == OTLine))
653 //printf("Testing objects for intersection (%X, %X)...\n", obj1, obj2);
654 int intersects = Geometry::Intersects((Line *)obj1, (Line *)obj2, &t, &u);
655 //printf(" (%s) --> t=%lf, u=%lf\n", (intersects ? "true" : "FALSE"), t, u);
659 //printf("Connecting objects (%X, %X)...\n", obj1, obj2);
660 obj1->Connect(obj2, u);
661 obj2->Connect(obj1, t);
664 else if (((obj1->type == OTLine) && (obj2->type == OTDimension))
665 || ((obj2->type == OTLine) && (obj1->type == OTDimension)))
667 printf("Testing Line<->Dimension intersection...\n");
668 Line * line = (Line *)(obj1->type == OTLine ? obj1 : obj2);
669 Dimension * dim = (Dimension *)(obj1->type == OTDimension ? obj1 : obj2);
671 int intersects = Geometry::Intersects(line, dim, &t, &u);
672 printf(" -> intersects = %i, t=%lf, u=%lf\n", intersects, t, u);
676 obj1->Connect(obj2, u);
677 obj2->Connect(obj1, t);
687 void ApplicationWindow::HandleDisconnection(void)
692 void ApplicationWindow::HandleGridSizeInPixels(int size)
694 drawing->SetGridSize(size);
699 void ApplicationWindow::HandleGridSizeInBaseUnits(QString text)
703 double value = text.toDouble(&ok);
705 // Nothing parsable to a double, so quit...
706 if (!ok || value == 0)
709 // drawing->gridSpacing = value;
710 // Painter::zoom = drawing->gridPixels / drawing->gridSpacing;
711 Global::gridSpacing = value;
712 Global::zoom = drawing->gridPixels / Global::gridSpacing;
713 drawing->UpdateGridBackground();
718 void ApplicationWindow::HandleDimensionSize(QString text)
722 double value = text.toDouble(&ok);
724 // Nothing parsable to a double, so quit...
725 if (!ok || value == 0)
728 // drawing->document.ResizeAllDimensions(value);
733 void ApplicationWindow::CreateActions(void)
735 exitAct = CreateAction(tr("&Quit"), tr("Quit"), tr("Exits the application."),
736 QIcon(":/res/quit.png"), QKeySequence(tr("Ctrl+q")));
737 connect(exitAct, SIGNAL(triggered()), this, SLOT(close()));
739 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);
740 connect(snapToGridAct, SIGNAL(triggered()), this, SLOT(SnapToGridTool()));
742 fixAngleAct = CreateAction(tr("Fix &Angle"), tr("Fix Angle"), tr("Fixes the angle of an object."),
743 QIcon(":/res/fix-angle.png"), QKeySequence(tr("F,A")), true);
744 connect(fixAngleAct, SIGNAL(triggered()), this, SLOT(FixAngle()));
746 fixLengthAct = CreateAction(tr("Fix &Length"), tr("Fix Length"), tr("Fixes the length of an object."),
747 QIcon(":/res/fix-length.png"), QKeySequence(tr("F,L")), true);
748 connect(fixLengthAct, SIGNAL(triggered()), this, SLOT(FixLength()));
750 deleteAct = CreateAction(tr("&Delete"), tr("Delete Object"), tr("Deletes selected objects."), QIcon(":/res/delete-tool.png"), QKeySequence(tr("Delete")), true);
751 connect(deleteAct, SIGNAL(triggered()), this, SLOT(DeleteTool()));
753 addDimensionAct = CreateAction(tr("Add &Dimension"), tr("Add Dimension"), tr("Adds a dimension to the drawing."), QIcon(":/res/dimension-tool.png"), QKeySequence("D,I"), true);
754 connect(addDimensionAct, SIGNAL(triggered()), this, SLOT(DimensionTool()));
756 addLineAct = CreateAction(tr("Add &Line"), tr("Add Line"), tr("Adds lines to the drawing."), QIcon(":/res/add-line-tool.png"), QKeySequence("A,L"), true);
757 connect(addLineAct, SIGNAL(triggered()), this, SLOT(AddLineTool()));
759 addCircleAct = CreateAction(tr("Add &Circle"), tr("Add Circle"), tr("Adds circles to the drawing."), QIcon(":/res/add-circle-tool.png"), QKeySequence("A,C"), true);
760 connect(addCircleAct, SIGNAL(triggered()), this, SLOT(AddCircleTool()));
762 addArcAct = CreateAction(tr("Add &Arc"), tr("Add Arc"), tr("Adds arcs to the drawing."), QIcon(":/res/add-arc-tool.png"), QKeySequence("A,A"), true);
763 connect(addArcAct, SIGNAL(triggered()), this, SLOT(AddArcTool()));
765 addPolygonAct = CreateAction(tr("Add &Polygon"), tr("Add Polygon"), tr("Add polygons to the drawing."), QIcon(":/res/add-polygon-tool.png"), QKeySequence("A,P"), true);
766 connect(addPolygonAct, SIGNAL(triggered()), this, SLOT(AddPolygonTool()));
768 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);
769 connect(addSplineAct, SIGNAL(triggered()), this, SLOT(AddSplineTool()));
771 aboutAct = CreateAction(tr("About &Architektonas"), tr("About Architektonas"), tr("Gives information about this program."), QIcon(":/res/generic-tool.png"), QKeySequence());
772 connect(aboutAct, SIGNAL(triggered()), this, SLOT(HelpAbout()));
774 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);
775 connect(rotateAct, SIGNAL(triggered()), this, SLOT(RotateTool()));
777 zoomInAct = CreateAction(tr("Zoom &In"), tr("Zoom In"), tr("Zoom in on the document."), QIcon(":/res/zoom-in.png"), QKeySequence(tr("+")), QKeySequence(tr("=")));
778 connect(zoomInAct, SIGNAL(triggered()), this, SLOT(ZoomInTool()));
780 zoomOutAct = CreateAction(tr("Zoom &Out"), tr("Zoom Out"), tr("Zoom out of the document."), QIcon(":/res/zoom-out.png"), QKeySequence(tr("-")));
781 connect(zoomOutAct, SIGNAL(triggered()), this, SLOT(ZoomOutTool()));
783 fileNewAct = CreateAction(tr("&New Drawing"), tr("New Drawing"), tr("Creates a new drawing."), QIcon(":/res/file-new.png"), QKeySequence(tr("Ctrl+n")));
784 connect(fileNewAct, SIGNAL(triggered()), this, SLOT(FileNew()));
786 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")));
787 connect(fileOpenAct, SIGNAL(triggered()), this, SLOT(FileOpen()));
789 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")));
790 connect(fileSaveAct, SIGNAL(triggered()), this, SLOT(FileSave()));
792 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")));
793 connect(fileSaveAsAct, SIGNAL(triggered()), this, SLOT(FileSaveAs()));
795 fileCloseAct = CreateAction(tr("&Close Drawing"), tr("Close Drawing"), tr("Closes the current drawing."), QIcon(":/res/file-close.png"), QKeySequence(tr("Ctrl+w")));
797 settingsAct = CreateAction(tr("&Settings"), tr("Settings"), tr("Change certain defaults for Architektonas."), QIcon(":/res/settings.png"), QKeySequence());
798 connect(settingsAct, SIGNAL(triggered()), this, SLOT(Settings()));
800 groupAct = CreateAction(tr("&Group"), tr("Group"), tr("Group/ungroup selected objects."), QIcon(":/res/group-tool.png"), QKeySequence("g"));
801 connect(groupAct, SIGNAL(triggered()), this, SLOT(HandleGrouping()));
803 connectAct = CreateAction(tr("&Connect"), tr("Connect"), tr("Connect objects at point."), QIcon(":/res/connect-tool.png"), QKeySequence("c,c"));
804 connect(connectAct, SIGNAL(triggered()), this, SLOT(HandleConnection()));
806 disconnectAct = CreateAction(tr("&Disconnect"), tr("Disconnect"), tr("Disconnect objects joined at point."), QIcon(":/res/disconnect-tool.png"), QKeySequence("d,d"));
807 connect(disconnectAct, SIGNAL(triggered()), this, SLOT(HandleDisconnection()));
809 mirrorAct = CreateAction(tr("&Mirror"), tr("Mirror"), tr("Mirror selected objects around a line."), QIcon(":/res/mirror-tool.png"), QKeySequence("m,i"), true);
810 connect(mirrorAct, SIGNAL(triggered()), this, SLOT(MirrorTool()));
812 trimAct = CreateAction(tr("&Trim"), tr("Trim"), tr("Trim extraneous lines from selected objects."), QIcon(":/res/trim-tool.png"), QKeySequence("t,r"), true);
813 connect(trimAct, SIGNAL(triggered()), this, SLOT(TrimTool()));
815 triangulateAct = CreateAction(tr("&Triangulate"), tr("Triangulate"), tr("Make triangles from selected lines, preserving their lengths."), QIcon(":/res/triangulate-tool.png"), QKeySequence("t,g"), true);
816 connect(triangulateAct, SIGNAL(triggered()), this, SLOT(TriangulateTool()));
819 //Hm. I think we'll have to have separate logic to do the "Radio Group Toolbar" thing...
820 // Yup, in order to turn them off, we'd have to have an "OFF" toolbar button. Ick.
821 /* QActionGroup * group = new QActionGroup(this);
822 group->addAction(deleteAct);
823 group->addAction(addDimensionAct);
824 group->addAction(addLineAct);
825 group->addAction(addCircleAct);
826 group->addAction(addArcAct);//*/
831 // Consolidates action creation from a multi-step process to a single-step one.
833 QAction * ApplicationWindow::CreateAction(QString name, QString tooltip, QString statustip,
834 QIcon icon, QKeySequence key, bool checkable/*= false*/)
836 QAction * action = new QAction(icon, name, this);
837 action->setToolTip(tooltip);
838 action->setStatusTip(statustip);
839 action->setShortcut(key);
840 action->setCheckable(checkable);
847 // This is essentially the same as the previous function, but this allows more
848 // than one key sequence to be added as key shortcuts.
850 QAction * ApplicationWindow::CreateAction(QString name, QString tooltip, QString statustip,
851 QIcon icon, QKeySequence key1, QKeySequence key2, bool checkable/*= false*/)
853 QAction * action = new QAction(icon, name, this);
854 action->setToolTip(tooltip);
855 action->setStatusTip(statustip);
856 QList<QKeySequence> keyList;
857 keyList.append(key1);
858 keyList.append(key2);
859 action->setShortcuts(keyList);
860 action->setCheckable(checkable);
866 void ApplicationWindow::CreateMenus(void)
868 QMenu * menu = menuBar()->addMenu(tr("&File"));
869 menu->addAction(fileNewAct);
870 menu->addAction(fileOpenAct);
871 menu->addAction(fileSaveAct);
872 menu->addAction(fileSaveAsAct);
873 menu->addAction(fileCloseAct);
874 menu->addSeparator();
875 menu->addAction(exitAct);
877 menu = menuBar()->addMenu(tr("&View"));
878 menu->addAction(zoomInAct);
879 menu->addAction(zoomOutAct);
881 menu = menuBar()->addMenu(tr("&Edit"));
882 menu->addAction(snapToGridAct);
883 menu->addAction(groupAct);
884 menu->addAction(fixAngleAct);
885 menu->addAction(fixLengthAct);
886 menu->addAction(rotateAct);
887 menu->addAction(mirrorAct);
888 menu->addAction(trimAct);
889 menu->addAction(triangulateAct);
890 menu->addAction(connectAct);
891 menu->addAction(disconnectAct);
892 menu->addSeparator();
893 menu->addAction(deleteAct);
894 menu->addSeparator();
895 menu->addAction(addLineAct);
896 menu->addAction(addCircleAct);
897 menu->addAction(addArcAct);
898 menu->addAction(addPolygonAct);
899 menu->addAction(addSplineAct);
900 menu->addAction(addDimensionAct);
901 menu->addSeparator();
902 menu->addAction(settingsAct);
904 menu = menuBar()->addMenu(tr("&Help"));
905 menu->addAction(aboutAct);
909 void ApplicationWindow::CreateToolbars(void)
911 QToolBar * toolbar = addToolBar(tr("File"));
912 toolbar->setObjectName("File"); // Needed for saveState()
913 toolbar->addAction(fileNewAct);
914 toolbar->addAction(fileOpenAct);
915 toolbar->addAction(fileSaveAct);
916 toolbar->addAction(fileSaveAsAct);
917 toolbar->addAction(fileCloseAct);
918 // toolbar->addAction(exitAct);
920 toolbar = addToolBar(tr("View"));
921 toolbar->setObjectName("View");
922 toolbar->addAction(zoomInAct);
923 toolbar->addAction(zoomOutAct);
925 QSpinBox * spinbox = new QSpinBox;
926 toolbar->addWidget(spinbox);
927 // QLineEdit * lineedit = new QLineEdit;
928 toolbar->addWidget(baseUnitInput);
929 toolbar->addWidget(dimensionSizeInput);
931 toolbar = addToolBar(tr("Edit"));
932 toolbar->setObjectName("Edit");
933 toolbar->addAction(snapToGridAct);
934 toolbar->addAction(groupAct);
935 toolbar->addAction(fixAngleAct);
936 toolbar->addAction(fixLengthAct);
937 toolbar->addAction(rotateAct);
938 toolbar->addAction(mirrorAct);
939 toolbar->addAction(trimAct);
940 toolbar->addAction(triangulateAct);
941 toolbar->addAction(deleteAct);
942 toolbar->addAction(connectAct);
943 toolbar->addAction(disconnectAct);
944 toolbar->addSeparator();
945 toolbar->addAction(addLineAct);
946 toolbar->addAction(addCircleAct);
947 toolbar->addAction(addArcAct);
948 toolbar->addAction(addPolygonAct);
949 toolbar->addAction(addSplineAct);
950 toolbar->addAction(addDimensionAct);
952 spinbox->setRange(4, 256);
953 spinbox->setValue(12);
954 baseUnitInput->setText("12");
955 connect(spinbox, SIGNAL(valueChanged(int)), this, SLOT(HandleGridSizeInPixels(int)));
956 connect(baseUnitInput, SIGNAL(textChanged(QString)), this, SLOT(HandleGridSizeInBaseUnits(QString)));
957 connect(dimensionSizeInput, SIGNAL(textChanged(QString)), this, SLOT(HandleDimensionSize(QString)));
959 PenWidget * pw = new PenWidget();
960 toolbar = addToolBar(tr("Pen"));
961 toolbar->setObjectName(tr("Pen"));
962 toolbar->addWidget(pw);
963 connect(drawing, SIGNAL(ObjectSelected(Object *)), pw, SLOT(SetFields(Object *)));
964 connect(pw, SIGNAL(WidthSelected(float)), drawing, SLOT(HandlePenWidth(float)));
965 connect(pw, SIGNAL(StyleSelected(int)), drawing, SLOT(HandlePenStyle(int)));
966 connect(pw, SIGNAL(ColorSelected(uint32_t)), drawing, SLOT(HandlePenColor(uint32_t)));
970 void ApplicationWindow::ReadSettings(void)
972 QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint();
973 QSize size = settings.value("size", QSize(400, 400)).toSize();
974 drawing->useAntialiasing = settings.value("useAntialiasing", true).toBool();
975 snapToGridAct->setChecked(settings.value("snapToGrid", true).toBool());
978 restoreState(settings.value("windowState").toByteArray());
982 void ApplicationWindow::WriteSettings(void)
984 settings.setValue("pos", pos());
985 settings.setValue("size", size());
986 settings.setValue("windowState", saveState());
987 settings.setValue("useAntialiasing", drawing->useAntialiasing);
988 settings.setValue("snapToGrid", snapToGridAct->isChecked());