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)));
101 connect(this, SIGNAL(ReloadLayers()), lw, SLOT(Reload()));
103 connect(drawing, SIGNAL(ObjectHovered(Object *)), ow, SLOT(ShowInfo(Object *)));
107 void ApplicationWindow::closeEvent(QCloseEvent * event)
110 event->accept(); // Use ignore() if can't close for some reason
111 //Do we have a memory leak here if we don't delete the font in the Object???
115 void ApplicationWindow::FileNew(void)
117 // Warn the user if drawing has changed and hasn't been saved...
122 msg.setText("The document has been modified.");
123 msg.setInformativeText("Do you want to save your changes?");
124 msg.setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);
125 msg.setDefaultButton(QMessageBox::Save);
126 int response = msg.exec();
130 case QMessageBox::Save:
134 case QMessageBox::Discard:
135 // Don't Save was clicked
137 case QMessageBox::Cancel:
138 // Cancel was clicked
144 FileIO::ResetLayerVectors();
146 DeleteContents(drawing->document.objects);
147 drawing->document.objects.clear();
148 drawing->dirty = false;
150 documentName.clear();
151 setWindowTitle("Architektonas - Untitled");
152 statusBar()->showMessage(tr("New drawing is ready."));
156 void ApplicationWindow::FileOpen(void)
158 QString filename = QFileDialog::getOpenFileName(this, tr("Open Drawing"),
159 "", tr("Architektonas files (*.drawing)"));
161 // User cancelled open
162 if (filename.isEmpty())
165 FILE * file = fopen(filename.toUtf8().data(), "r");
170 msg.setText(QString(tr("Could not open file \"%1\" for loading!")).arg(filename));
171 msg.setIcon(QMessageBox::Critical);
176 Container container(true); // Make sure it's a top level container...
177 bool successful = FileIO::LoadAtnsFile(file, &container);
183 msg.setText(QString(tr("Could not load file \"%1\"!")).arg(filename));
184 msg.setIcon(QMessageBox::Critical);
186 // Make sure to delete any hanging objects in the container...
187 DeleteContents(container.objects);
191 //printf("FileOpen: container size = %li\n", container.objects.size());
192 // Keep memory leaks from happening by getting rid of the old document
193 DeleteContents(drawing->document.objects);
195 // We can do this because the vector is just a bunch of pointers to our
196 // Objects, and the Containers (non-empty) can be moved this way with no
198 drawing->document = container;
199 drawing->dirty = false;
201 documentName = filename;
202 setWindowTitle(QString("Architektonas - %1").arg(documentName));
203 statusBar()->showMessage(tr("Drawing loaded."));
207 void ApplicationWindow::FileSave(void)
209 if (documentName.isEmpty())
210 documentName = QFileDialog::getSaveFileName(this, tr("Save Drawing"),
211 "", tr("Architektonas drawings (*.drawing)"));
213 FILE * file = fopen(documentName.toUtf8().data(), "w");
218 msg.setText(QString(tr("Could not open file \"%1\" for saving!")).arg(documentName));
219 msg.setIcon(QMessageBox::Critical);
224 bool successful = FileIO::SaveAtnsFile(file, &drawing->document);
230 msg.setText(QString(tr("Could not save file \"%1\"!")).arg(documentName));
231 msg.setIcon(QMessageBox::Critical);
233 // In this case, we should unlink the created file, since it's not
235 // unlink(documentName.toUtf8().data());
236 QFile::remove(documentName);
240 drawing->dirty = false;
241 setWindowTitle(QString("Architektonas - %1").arg(documentName));
242 statusBar()->showMessage(tr("Drawing saved."));
246 void ApplicationWindow::FileSaveAs(void)
248 QString filename = QFileDialog::getSaveFileName(this, tr("Save Drawing As"),
249 documentName, tr("Architektonas drawings (*.drawing)"));
251 if (!filename.isEmpty())
253 documentName = filename;
259 void ApplicationWindow::contextMenuEvent(QContextMenuEvent * event)
262 menu.addAction(mirrorAct);
263 menu.addAction(rotateAct);
264 menu.addAction(trimAct);
265 menu.exec(event->globalPos());
269 void ApplicationWindow::SnapToGridTool(void)
271 Global::snapToGrid = snapToGridAct->isChecked();
275 void ApplicationWindow::FixAngle(void)
277 Global::fixedAngle = fixAngleAct->isChecked();
281 void ApplicationWindow::FixLength(void)
283 Global::fixedLength = fixLengthAct->isChecked();
287 void ApplicationWindow::DeleteTool(void)
289 // For this tool, we check first to see if anything is selected. If so, we
290 // delete those and *don't* select the delete tool.
291 // if (drawing->numSelected > 0)
292 if (drawing->select.size() > 0)
294 DeleteSelectedObjects(drawing->document.objects);
296 deleteAct->setChecked(false);
300 // Otherwise, toggle the state of the tool
301 ClearUIToolStatesExcept(deleteAct);
302 SetInternalToolStates();
306 void ApplicationWindow::DimensionTool(void)
308 ClearUIToolStatesExcept(addDimensionAct);
309 SetInternalToolStates();
313 void ApplicationWindow::RotateTool(void)
315 ClearUIToolStatesExcept(rotateAct);
317 // Do tear-down if Rotate tool has been turned off
318 if (!rotateAct->isChecked())
319 drawing->RotateHandler(ToolCleanup, Point(0, 0));
321 SetInternalToolStates();
325 void ApplicationWindow::MirrorTool(void)
327 ClearUIToolStatesExcept(mirrorAct);
329 // Do tear-down if Rotate tool has been turned off
330 if (!mirrorAct->isChecked())
331 drawing->MirrorHandler(ToolCleanup, Point(0, 0));
333 SetInternalToolStates();
337 void ApplicationWindow::TrimTool(void)
339 ClearUIToolStatesExcept(trimAct);
340 SetInternalToolStates();
344 void ApplicationWindow::TriangulateTool(void)
346 ClearUIToolStatesExcept(triangulateAct);
347 SetInternalToolStates();
351 void ApplicationWindow::AddLineTool(void)
353 ClearUIToolStatesExcept(addLineAct);
354 SetInternalToolStates();
358 void ApplicationWindow::AddCircleTool(void)
360 ClearUIToolStatesExcept(addCircleAct);
361 SetInternalToolStates();
365 void ApplicationWindow::AddArcTool(void)
367 ClearUIToolStatesExcept(addArcAct);
368 SetInternalToolStates();
372 void ApplicationWindow::AddPolygonTool(void)
374 ClearUIToolStatesExcept(addPolygonAct);
375 SetInternalToolStates();
379 void ApplicationWindow::AddSplineTool(void)
381 ClearUIToolStatesExcept(addSplineAct);
382 SetInternalToolStates();
386 void ApplicationWindow::ZoomInTool(void)
388 double zoomFactor = 2.0;
390 We need to find the center of the screen, then figure out where the new corner
391 will be in the zoomed in window.
393 So we know in Qt coords, the center is found via:
394 size.width() / 2 --> xCenter
395 size.height() / 2 --> yCenter
397 transform x/yCenter to Cartesian coordinates. So far, so good.
399 when zooming in, new origin will be (xCenter - origin.x) / 2, (yCenter - origin.y) / 2
400 (after subtracting from center, that is...)
402 QSize size = drawing->size();
403 Vector center(size.width() / 2.0, size.height() / 2.0);
404 //printf("Zoom in... Center=%.2f,%.2f; ", center.x, center.y);
405 center = Painter::QtToCartesianCoords(center);
406 //printf("(%.2f,%.2f); origin=%.2f,%.2f; ", center.x, center.y, Painter::origin.x, Painter::origin.y);
407 Vector newOrigin = center - ((center - Global::origin) / zoomFactor);
408 //printf("newOrigin=%.2f,%.2f;\n", newOrigin.x, newOrigin.y);
409 Global::origin = newOrigin;
411 //printf("Zoom in... level going from %02f to ", Painter::zoom);
412 // This just zooms leaving origin intact... should zoom in at the current
414 Global::zoom *= zoomFactor;
415 Global::gridSpacing = drawing->gridPixels / Global::zoom;
416 drawing->UpdateGridBackground();
419 zoomIndicator->setText(QString("Grid: %1\", BU: Inch").arg(Global::gridSpacing));
420 baseUnitInput->setText(QString("%1").arg(Global::gridSpacing));
424 void ApplicationWindow::ZoomOutTool(void)
429 origin = (223, 160.5)
430 newOrigin should be (-10, -10)
433 center - origin = (213, 150.5)
434 origin - center = (-213, -150.5)
436 + center = (-10, -10)
439 double zoomFactor = 2.0;
440 QSize size = drawing->size();
441 Vector center(size.width() / 2.0, size.height() / 2.0);
442 //printf("Zoom out... Center=%.2f,%.2f; ", center.x, center.y);
443 center = Painter::QtToCartesianCoords(center);
444 //printf("(%.2f,%.2f); origin=%.2f,%.2f; ", center.x, center.y, Painter::origin.x, Painter::origin.y);
445 // Vector newOrigin = (center - Painter::origin) * zoomFactor;
446 // Vector newOrigin = center - (Painter::origin * zoomFactor);
447 Vector newOrigin = center + ((Global::origin - center) * zoomFactor);
448 //printf("newOrigin=%.2f,%.2f;\n", newOrigin.x, newOrigin.y);
449 Global::origin = newOrigin;
450 //printf("Zoom out...\n");
451 // This just zooms leaving origin intact... should zoom out at the current
453 Global::zoom /= zoomFactor;
454 Global::gridSpacing = drawing->gridPixels / Global::zoom;
455 drawing->UpdateGridBackground();
458 zoomIndicator->setText(QString("Grid: %1\", BU: Inch").arg(Global::gridSpacing));
459 baseUnitInput->setText(QString("%1").arg(Global::gridSpacing));
463 void ApplicationWindow::ClearUIToolStatesExcept(QAction * exception)
465 QAction * actionList[] = {
466 addArcAct, addLineAct, addCircleAct, addDimensionAct, addPolygonAct,
467 addSplineAct, deleteAct, rotateAct, mirrorAct, trimAct, triangulateAct, 0
470 for(int i=0; actionList[i]!=0; i++)
472 if (actionList[i] != exception)
473 actionList[i]->setChecked(false);
478 void ApplicationWindow::SetInternalToolStates(void)
480 // We can be sure that if we've come here, then either an active tool is
481 // being deactivated, or a new tool is being created. In either case, the
482 // old tool needs to be deleted.
483 Global::toolState = TSNone;
485 if (addLineAct->isChecked())
486 Global::tool = TTLine;
487 else if (addCircleAct->isChecked())
488 Global::tool = TTCircle;
489 else if (addArcAct->isChecked())
490 Global::tool = TTArc;
491 else if (addDimensionAct->isChecked())
492 Global::tool = TTDimension;
493 else if (addSplineAct->isChecked())
494 Global::tool = TTSpline;
495 else if (addPolygonAct->isChecked())
496 Global::tool = TTPolygon;
497 else if (deleteAct->isChecked())
498 Global::tool = TTDelete;
499 else if (mirrorAct->isChecked())
500 Global::tool = TTMirror;
501 else if (rotateAct->isChecked())
502 Global::tool = TTRotate;
503 else if (trimAct->isChecked())
504 Global::tool = TTTrim;
505 else if (triangulateAct->isChecked())
506 Global::tool = TTTriangulate;
508 Global::tool = TTNone;
514 void ApplicationWindow::HelpAbout(void)
520 void ApplicationWindow::Settings(void)
522 SettingsDialog dlg(this);
523 dlg.generalTab->antialiasChk->setChecked(drawing->useAntialiasing);
525 if (dlg.exec() == false)
528 // Deal with stuff here (since user hit "OK" button...)
529 drawing->useAntialiasing = dlg.generalTab->antialiasChk->isChecked();
535 // Group a bunch of selected objects (which can include other groups) together
536 // or ungroup a selected group.
538 void ApplicationWindow::HandleGrouping(void)
541 int itemsSelected = drawing->document.ItemsSelected();
543 // If nothing selected, do nothing
544 if (itemsSelected == 0)
546 statusBar()->showMessage(tr("No objects selected to make a group from."));
550 // If it's a group that's selected, ungroup it and leave the objects in a
552 if (itemsSelected == 1)
554 Object * object = drawing->document.SelectedItem(0);
558 printf("SelectedItem = NULL!\n");
560 printf("SelectedItem = %08X, type = %i\n", object, object->type);
563 if (object == NULL || object->type != OTContainer)
565 statusBar()->showMessage(tr("A group requires two or more selected objects."));
569 // Need the parent of the group, we're assuming here that the parent is
570 // the drawing's document. Does it matter? Maybe...
571 // Could just stipulate that grouping like this only takes place where
572 // the parent of the group is the drawing's document. Makes life much
574 ((Container *)object)->SelectAll();
575 ((Container *)object)->MoveContentsTo(&(drawing->document));
576 drawing->document.Delete(object);
577 statusBar()->showMessage(tr("Objects ungrouped."));
579 // Otherwise, if it's a group of 2 or more objects (which can be groups too)
580 // group them and select the group
581 else if (itemsSelected > 1)
583 Container * container = new Container(Vector(), &(drawing->document));
584 drawing->document.MoveSelectedContentsTo(container);
585 drawing->document.Add(container);
586 container->DeselectAll();
587 container->state = OSSelected;
588 statusBar()->showMessage(QString(tr("Grouped %1 objects.")).arg(itemsSelected));
591 int numSelected = drawing->select.size();
593 // If nothing selected, do nothing
594 if (numSelected == 0)
596 statusBar()->showMessage(tr("No objects selected to make a group from."));
600 // If it's a group that's selected, ungroup it and leave the objects in a
602 if (numSelected == 1)
604 Object * obj = (Object *)drawing->select[0];
606 if (obj->type != OTContainer)
608 statusBar()->showMessage(tr("A group requires two or more selected objects."));
612 // Need the parent of the group, we're assuming here that the parent is
613 // the drawing's document. Does it matter? Maybe...
614 // Could just stipulate that grouping like this only takes place where
615 // the parent of the group is the drawing's document. Makes life much
617 // ((Container *)object)->SelectAll();
618 // ((Container *)object)->MoveContentsTo(&(drawing->document));
619 // drawing->document.Delete(object);
620 Container * c = (Container *)obj;
621 //printf("Ungroup: container size = %li\n", c->objects.size());
622 SelectAll(c->objects);
623 //printf("Ungroup: document size = %li (pre-AddObjectsTo)\n", drawing->document.objects.size());
624 RemoveSelectedObjects(drawing->document.objects);
625 AddObjectsTo(drawing->document.objects, c->objects);
626 drawing->select.clear();
627 AddObjectsTo(drawing->select, c->objects);
629 statusBar()->showMessage(tr("Objects ungrouped."));
630 //printf("Ungroup: document size = %li\n", drawing->document.objects.size());
632 // Otherwise, if it's a group of 2 or more objects (which can be groups too)
633 // group them and select the group
634 else if (numSelected > 1)
636 // Container * container = new Container(Vector(), &(drawing->document));
637 // drawing->document.MoveSelectedContentsTo(container);
638 // drawing->document.Add(container);
639 // container->DeselectAll();
640 // container->state = OSSelected;
642 Container * c = new Container();
643 // AddObjectsTo(c->objects, drawing->select);
644 // RemoveSelectedObjects(drawing->document.objects);
645 MoveSelectedObjectsTo(c->objects, drawing->document.objects);
646 drawing->document.objects.push_back(c);
647 ClearSelected(c->objects);
649 c->layer = Global::currentLayer;
651 Rect r = drawing->GetObjectExtents((Object *)c);
652 c->p[0] = r.TopLeft();
653 c->p[1] = r.BottomRight();
655 drawing->select.clear();
656 drawing->select.push_back(c);
657 statusBar()->showMessage(QString(tr("Grouped %1 objects.")).arg(numSelected));
658 //printf("Group: document size = %li\n", drawing->document.objects.size());
666 void ApplicationWindow::HandleConnection(void)
669 //double tt = Geometry::ParameterOfLineAndPoint(Vector(0, 0), Vector(10, 0), Vector(8, 2));
670 //printf("Parameter of point @ (8,2) of line (0,0), (10,0): %lf\n", tt);
671 int itemsSelected = drawing->document.ItemsSelected();
673 // If nothing selected, do nothing
674 if (itemsSelected == 0)
676 statusBar()->showMessage(tr("No objects selected to connect."));
680 // If one thing selected, do nothing
681 if (itemsSelected == 1)
683 statusBar()->showMessage(tr("Nothing to connect object to."));
687 // This is O(n^2 / 2) :-P
688 for(int i=0; i<itemsSelected; i++)
690 Object * obj1 = drawing->document.SelectedItem(i);
692 for(int j=i+1; j<itemsSelected; j++)
694 Object * obj2 = drawing->document.SelectedItem(j);
697 // if ((obj1->type != OTLine) || (obj2->type != OTLine))
700 if ((obj1->type == OTLine) && (obj2->type == OTLine))
702 //printf("Testing objects for intersection (%X, %X)...\n", obj1, obj2);
703 int intersects = Geometry::Intersects((Line *)obj1, (Line *)obj2, &t, &u);
704 //printf(" (%s) --> t=%lf, u=%lf\n", (intersects ? "true" : "FALSE"), t, u);
708 //printf("Connecting objects (%X, %X)...\n", obj1, obj2);
709 obj1->Connect(obj2, u);
710 obj2->Connect(obj1, t);
713 else if (((obj1->type == OTLine) && (obj2->type == OTDimension))
714 || ((obj2->type == OTLine) && (obj1->type == OTDimension)))
716 printf("Testing Line<->Dimension intersection...\n");
717 Line * line = (Line *)(obj1->type == OTLine ? obj1 : obj2);
718 Dimension * dim = (Dimension *)(obj1->type == OTDimension ? obj1 : obj2);
720 int intersects = Geometry::Intersects(line, dim, &t, &u);
721 printf(" -> intersects = %i, t=%lf, u=%lf\n", intersects, t, u);
725 obj1->Connect(obj2, u);
726 obj2->Connect(obj1, t);
736 void ApplicationWindow::HandleDisconnection(void)
741 void ApplicationWindow::HandleGridSizeInPixels(int size)
743 drawing->SetGridSize(size);
748 void ApplicationWindow::HandleGridSizeInBaseUnits(QString text)
752 double value = text.toDouble(&ok);
754 // Nothing parsable to a double, so quit...
755 if (!ok || value == 0)
758 // drawing->gridSpacing = value;
759 // Painter::zoom = drawing->gridPixels / drawing->gridSpacing;
760 Global::gridSpacing = value;
761 Global::zoom = drawing->gridPixels / Global::gridSpacing;
762 drawing->UpdateGridBackground();
767 void ApplicationWindow::HandleDimensionSize(QString text)
770 This is the third input on the view toolbar; not sure what it was supposed to do...
774 double value = text.toDouble(&ok);
776 // Nothing parsable to a double, so quit...
777 if (!ok || value == 0)
780 // drawing->document.ResizeAllDimensions(value);
784 void ApplicationWindow::EditCopy(void)
786 if (drawing->select.size() > 0)
788 DeleteContents(clipboard);
789 CopySelectedObjectsTo(clipboard, drawing->document.objects);
794 void ApplicationWindow::EditCut(void)
796 if (drawing->select.size() > 0)
798 DeleteContents(clipboard);
799 MoveSelectedObjectsTo(clipboard, drawing->document.objects);
805 void ApplicationWindow::EditPaste(void)
807 if (clipboard.size() > 0)
809 // We want to maybe make it so that the pasted objects are being moved in a "mouse drag" state...
810 // This only moves the cut/copied from the clipboard to the drawing.
811 // AddObjectsTo(drawing->document.objects, clipboard);
812 CopyObjects(clipboard, drawing->document.objects);
818 void ApplicationWindow::CreateActions(void)
820 exitAct = CreateAction(tr("&Quit"), tr("Quit"), tr("Exits the application."),
821 QIcon(":/res/quit.png"), QKeySequence(tr("Ctrl+q")));
822 connect(exitAct, SIGNAL(triggered()), this, SLOT(close()));
824 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);
825 connect(snapToGridAct, SIGNAL(triggered()), this, SLOT(SnapToGridTool()));
827 fixAngleAct = CreateAction(tr("Fix &Angle"), tr("Fix Angle"), tr("Fixes the angle of an object."),
828 QIcon(":/res/fix-angle.png"), QKeySequence(tr("F,A")), true);
829 connect(fixAngleAct, SIGNAL(triggered()), this, SLOT(FixAngle()));
831 fixLengthAct = CreateAction(tr("Fix &Length"), tr("Fix Length"), tr("Fixes the length of an object."),
832 QIcon(":/res/fix-length.png"), QKeySequence(tr("F,L")), true);
833 connect(fixLengthAct, SIGNAL(triggered()), this, SLOT(FixLength()));
835 deleteAct = CreateAction(tr("&Delete"), tr("Delete Object"), tr("Deletes selected objects."), QIcon(":/res/delete-tool.png"), QKeySequence(tr("Delete")), true);
836 connect(deleteAct, SIGNAL(triggered()), this, SLOT(DeleteTool()));
838 addDimensionAct = CreateAction(tr("Add &Dimension"), tr("Add Dimension"), tr("Adds a dimension to the drawing."), QIcon(":/res/dimension-tool.png"), QKeySequence("D,I"), true);
839 connect(addDimensionAct, SIGNAL(triggered()), this, SLOT(DimensionTool()));
841 addLineAct = CreateAction(tr("Add &Line"), tr("Add Line"), tr("Adds lines to the drawing."), QIcon(":/res/add-line-tool.png"), QKeySequence("A,L"), true);
842 connect(addLineAct, SIGNAL(triggered()), this, SLOT(AddLineTool()));
844 addCircleAct = CreateAction(tr("Add &Circle"), tr("Add Circle"), tr("Adds circles to the drawing."), QIcon(":/res/add-circle-tool.png"), QKeySequence("A,C"), true);
845 connect(addCircleAct, SIGNAL(triggered()), this, SLOT(AddCircleTool()));
847 addArcAct = CreateAction(tr("Add &Arc"), tr("Add Arc"), tr("Adds arcs to the drawing."), QIcon(":/res/add-arc-tool.png"), QKeySequence("A,A"), true);
848 connect(addArcAct, SIGNAL(triggered()), this, SLOT(AddArcTool()));
850 addPolygonAct = CreateAction(tr("Add &Polygon"), tr("Add Polygon"), tr("Add polygons to the drawing."), QIcon(":/res/add-polygon-tool.png"), QKeySequence("A,P"), true);
851 connect(addPolygonAct, SIGNAL(triggered()), this, SLOT(AddPolygonTool()));
853 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);
854 connect(addSplineAct, SIGNAL(triggered()), this, SLOT(AddSplineTool()));
856 aboutAct = CreateAction(tr("About &Architektonas"), tr("About Architektonas"), tr("Gives information about this program."), QIcon(":/res/generic-tool.png"), QKeySequence());
857 connect(aboutAct, SIGNAL(triggered()), this, SLOT(HelpAbout()));
859 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);
860 connect(rotateAct, SIGNAL(triggered()), this, SLOT(RotateTool()));
862 zoomInAct = CreateAction(tr("Zoom &In"), tr("Zoom In"), tr("Zoom in on the document."), QIcon(":/res/zoom-in.png"), QKeySequence(tr("+")), QKeySequence(tr("=")));
863 connect(zoomInAct, SIGNAL(triggered()), this, SLOT(ZoomInTool()));
865 zoomOutAct = CreateAction(tr("Zoom &Out"), tr("Zoom Out"), tr("Zoom out of the document."), QIcon(":/res/zoom-out.png"), QKeySequence(tr("-")));
866 connect(zoomOutAct, SIGNAL(triggered()), this, SLOT(ZoomOutTool()));
868 fileNewAct = CreateAction(tr("&New Drawing"), tr("New Drawing"), tr("Creates a new drawing."), QIcon(":/res/file-new.png"), QKeySequence(tr("Ctrl+n")));
869 connect(fileNewAct, SIGNAL(triggered()), this, SLOT(FileNew()));
871 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")));
872 connect(fileOpenAct, SIGNAL(triggered()), this, SLOT(FileOpen()));
874 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")));
875 connect(fileSaveAct, SIGNAL(triggered()), this, SLOT(FileSave()));
877 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")));
878 connect(fileSaveAsAct, SIGNAL(triggered()), this, SLOT(FileSaveAs()));
880 fileCloseAct = CreateAction(tr("&Close Drawing"), tr("Close Drawing"), tr("Closes the current drawing."), QIcon(":/res/file-close.png"), QKeySequence(tr("Ctrl+w")));
882 settingsAct = CreateAction(tr("&Settings"), tr("Settings"), tr("Change certain defaults for Architektonas."), QIcon(":/res/settings.png"), QKeySequence());
883 connect(settingsAct, SIGNAL(triggered()), this, SLOT(Settings()));
885 groupAct = CreateAction(tr("&Group"), tr("Group"), tr("Group/ungroup selected objects."), QIcon(":/res/group-tool.png"), QKeySequence("g"));
886 connect(groupAct, SIGNAL(triggered()), this, SLOT(HandleGrouping()));
888 connectAct = CreateAction(tr("&Connect"), tr("Connect"), tr("Connect objects at point."), QIcon(":/res/connect-tool.png"), QKeySequence("c,c"));
889 connect(connectAct, SIGNAL(triggered()), this, SLOT(HandleConnection()));
891 disconnectAct = CreateAction(tr("&Disconnect"), tr("Disconnect"), tr("Disconnect objects joined at point."), QIcon(":/res/disconnect-tool.png"), QKeySequence("d,d"));
892 connect(disconnectAct, SIGNAL(triggered()), this, SLOT(HandleDisconnection()));
894 mirrorAct = CreateAction(tr("&Mirror"), tr("Mirror"), tr("Mirror selected objects around a line."), QIcon(":/res/mirror-tool.png"), QKeySequence("m,i"), true);
895 connect(mirrorAct, SIGNAL(triggered()), this, SLOT(MirrorTool()));
897 trimAct = CreateAction(tr("&Trim"), tr("Trim"), tr("Trim extraneous lines from selected objects."), QIcon(":/res/trim-tool.png"), QKeySequence("t,r"), true);
898 connect(trimAct, SIGNAL(triggered()), this, SLOT(TrimTool()));
900 triangulateAct = CreateAction(tr("&Triangulate"), tr("Triangulate"), tr("Make triangles from selected lines, preserving their lengths."), QIcon(":/res/triangulate-tool.png"), QKeySequence("t,g"), true);
901 connect(triangulateAct, SIGNAL(triggered()), this, SLOT(TriangulateTool()));
903 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")));
904 connect(editCutAct, SIGNAL(triggered()), this, SLOT(EditCut()));
906 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")));
907 connect(editCopyAct, SIGNAL(triggered()), this, SLOT(EditCopy()));
909 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")));
910 connect(editPasteAct, SIGNAL(triggered()), this, SLOT(EditPaste()));
912 //Hm. I think we'll have to have separate logic to do the "Radio Group Toolbar" thing...
913 // Yup, in order to turn them off, we'd have to have an "OFF" toolbar button. Ick.
914 /* QActionGroup * group = new QActionGroup(this);
915 group->addAction(deleteAct);
916 group->addAction(addDimensionAct);
917 group->addAction(addLineAct);
918 group->addAction(addCircleAct);
919 group->addAction(addArcAct);//*/
924 // Consolidates action creation from a multi-step process to a single-step one.
926 QAction * ApplicationWindow::CreateAction(QString name, QString tooltip,
927 QString statustip, QIcon icon, QKeySequence key, bool checkable/*= false*/)
929 QAction * action = new QAction(icon, name, this);
930 action->setToolTip(tooltip);
931 action->setStatusTip(statustip);
932 action->setShortcut(key);
933 action->setCheckable(checkable);
940 // This is essentially the same as the previous function, but this allows more
941 // than one key sequence to be added as key shortcuts.
943 QAction * ApplicationWindow::CreateAction(QString name, QString tooltip,
944 QString statustip, QIcon icon, QKeySequence key1, QKeySequence key2,
945 bool checkable/*= false*/)
947 QAction * action = new QAction(icon, name, this);
948 action->setToolTip(tooltip);
949 action->setStatusTip(statustip);
950 QList<QKeySequence> keyList;
951 keyList.append(key1);
952 keyList.append(key2);
953 action->setShortcuts(keyList);
954 action->setCheckable(checkable);
960 void ApplicationWindow::CreateMenus(void)
962 QMenu * menu = menuBar()->addMenu(tr("&File"));
963 menu->addAction(fileNewAct);
964 menu->addAction(fileOpenAct);
965 menu->addAction(fileSaveAct);
966 menu->addAction(fileSaveAsAct);
967 menu->addAction(fileCloseAct);
968 menu->addSeparator();
969 menu->addAction(exitAct);
971 menu = menuBar()->addMenu(tr("&View"));
972 menu->addAction(zoomInAct);
973 menu->addAction(zoomOutAct);
975 menu = menuBar()->addMenu(tr("&Edit"));
976 menu->addAction(snapToGridAct);
977 menu->addAction(groupAct);
978 menu->addAction(fixAngleAct);
979 menu->addAction(fixLengthAct);
980 menu->addAction(rotateAct);
981 menu->addAction(mirrorAct);
982 menu->addAction(trimAct);
983 menu->addAction(triangulateAct);
984 menu->addAction(connectAct);
985 menu->addAction(disconnectAct);
986 menu->addSeparator();
987 menu->addAction(editCutAct);
988 menu->addAction(editCopyAct);
989 menu->addAction(editPasteAct);
990 menu->addAction(deleteAct);
991 menu->addSeparator();
992 menu->addAction(addLineAct);
993 menu->addAction(addCircleAct);
994 menu->addAction(addArcAct);
995 menu->addAction(addPolygonAct);
996 menu->addAction(addSplineAct);
997 menu->addAction(addDimensionAct);
998 menu->addSeparator();
999 menu->addAction(settingsAct);
1001 menu = menuBar()->addMenu(tr("&Help"));
1002 menu->addAction(aboutAct);
1006 void ApplicationWindow::CreateToolbars(void)
1008 QToolBar * toolbar = addToolBar(tr("File"));
1009 toolbar->setObjectName("File"); // Needed for saveState()
1010 toolbar->addAction(fileNewAct);
1011 toolbar->addAction(fileOpenAct);
1012 toolbar->addAction(fileSaveAct);
1013 toolbar->addAction(fileSaveAsAct);
1014 toolbar->addAction(fileCloseAct);
1015 // toolbar->addAction(exitAct);
1017 toolbar = addToolBar(tr("View"));
1018 toolbar->setObjectName("View");
1019 toolbar->addAction(zoomInAct);
1020 toolbar->addAction(zoomOutAct);
1022 QSpinBox * spinbox = new QSpinBox;
1023 toolbar->addWidget(spinbox);
1024 // QLineEdit * lineedit = new QLineEdit;
1025 toolbar->addWidget(baseUnitInput);
1026 toolbar->addWidget(dimensionSizeInput);
1028 toolbar = addToolBar(tr("Edit"));
1029 toolbar->setObjectName("Edit");
1030 toolbar->addAction(snapToGridAct);
1031 toolbar->addAction(groupAct);
1032 toolbar->addAction(fixAngleAct);
1033 toolbar->addAction(fixLengthAct);
1034 toolbar->addAction(rotateAct);
1035 toolbar->addAction(mirrorAct);
1036 toolbar->addAction(trimAct);
1037 toolbar->addAction(triangulateAct);
1038 toolbar->addAction(editCutAct);
1039 toolbar->addAction(editCopyAct);
1040 toolbar->addAction(editPasteAct);
1041 toolbar->addAction(deleteAct);
1042 toolbar->addAction(connectAct);
1043 toolbar->addAction(disconnectAct);
1044 toolbar->addSeparator();
1045 toolbar->addAction(addLineAct);
1046 toolbar->addAction(addCircleAct);
1047 toolbar->addAction(addArcAct);
1048 toolbar->addAction(addPolygonAct);
1049 toolbar->addAction(addSplineAct);
1050 toolbar->addAction(addDimensionAct);
1052 spinbox->setRange(4, 256);
1053 spinbox->setValue(12);
1054 baseUnitInput->setText("12");
1055 connect(spinbox, SIGNAL(valueChanged(int)), this, SLOT(HandleGridSizeInPixels(int)));
1056 connect(baseUnitInput, SIGNAL(textChanged(QString)), this, SLOT(HandleGridSizeInBaseUnits(QString)));
1057 connect(dimensionSizeInput, SIGNAL(textChanged(QString)), this, SLOT(HandleDimensionSize(QString)));
1059 PenWidget * pw = new PenWidget();
1060 toolbar = addToolBar(tr("Pen"));
1061 toolbar->setObjectName(tr("Pen"));
1062 toolbar->addWidget(pw);
1063 connect(drawing, SIGNAL(ObjectSelected(Object *)), pw, SLOT(SetFields(Object *)));
1064 connect(pw, SIGNAL(WidthSelected(float)), drawing, SLOT(HandlePenWidth(float)));
1065 connect(pw, SIGNAL(StyleSelected(int)), drawing, SLOT(HandlePenStyle(int)));
1066 connect(pw, SIGNAL(ColorSelected(uint32_t)), drawing, SLOT(HandlePenColor(uint32_t)));
1070 void ApplicationWindow::ReadSettings(void)
1072 QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint();
1073 QSize size = settings.value("size", QSize(400, 400)).toSize();
1074 drawing->useAntialiasing = settings.value("useAntialiasing", true).toBool();
1075 snapToGridAct->setChecked(settings.value("snapToGrid", true).toBool());
1078 restoreState(settings.value("windowState").toByteArray());
1082 void ApplicationWindow::WriteSettings(void)
1084 settings.setValue("pos", pos());
1085 settings.setValue("size", size());
1086 settings.setValue("windowState", saveState());
1087 settings.setValue("useAntialiasing", drawing->useAntialiasing);
1088 settings.setValue("snapToGrid", snapToGridAct->isChecked());