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)
540 int numSelected = drawing->select.size();
542 // If nothing selected, do nothing
543 if (numSelected == 0)
545 statusBar()->showMessage(tr("No objects selected to make a group from."));
549 // If it's a group that's selected, ungroup it and leave the objects in a
551 if (numSelected == 1)
553 Object * obj = (Object *)drawing->select[0];
555 if (obj->type != OTContainer)
557 statusBar()->showMessage(tr("A group requires two or more selected objects."));
561 // Need the parent of the group, we're assuming here that the parent is
562 // the drawing's document. Does it matter? Maybe...
563 // Could just stipulate that grouping like this only takes place where
564 // the parent of the group is the drawing's document. Makes life much
566 Container * c = (Container *)obj;
567 SelectAll(c->objects);
568 RemoveSelectedObjects(drawing->document.objects);
569 AddObjectsTo(drawing->document.objects, c->objects);
570 drawing->select.clear();
571 AddObjectsTo(drawing->select, c->objects);
573 statusBar()->showMessage(tr("Objects ungrouped."));
574 //printf("Ungroup: document size = %li\n", drawing->document.objects.size());
576 // Otherwise, if it's a group of 2 or more objects (which can be groups too)
577 // group them and select the group
578 else if (numSelected > 1)
580 Container * c = new Container();
581 MoveSelectedObjectsTo(c->objects, drawing->document.objects);
582 drawing->document.objects.push_back(c);
583 ClearSelected(c->objects);
585 c->layer = Global::activeLayer;
587 Rect r = drawing->GetObjectExtents((Object *)c);
588 c->p[0] = r.TopLeft();
589 c->p[1] = r.BottomRight();
591 drawing->select.clear();
592 drawing->select.push_back(c);
593 statusBar()->showMessage(QString(tr("Grouped %1 objects.")).arg(numSelected));
594 //printf("Group: document size = %li\n", drawing->document.objects.size());
601 void ApplicationWindow::HandleConnection(void)
604 //double tt = Geometry::ParameterOfLineAndPoint(Vector(0, 0), Vector(10, 0), Vector(8, 2));
605 //printf("Parameter of point @ (8,2) of line (0,0), (10,0): %lf\n", tt);
606 int itemsSelected = drawing->document.ItemsSelected();
608 // If nothing selected, do nothing
609 if (itemsSelected == 0)
611 statusBar()->showMessage(tr("No objects selected to connect."));
615 // If one thing selected, do nothing
616 if (itemsSelected == 1)
618 statusBar()->showMessage(tr("Nothing to connect object to."));
622 // This is O(n^2 / 2) :-P
623 for(int i=0; i<itemsSelected; i++)
625 Object * obj1 = drawing->document.SelectedItem(i);
627 for(int j=i+1; j<itemsSelected; j++)
629 Object * obj2 = drawing->document.SelectedItem(j);
632 // if ((obj1->type != OTLine) || (obj2->type != OTLine))
635 if ((obj1->type == OTLine) && (obj2->type == OTLine))
637 //printf("Testing objects for intersection (%X, %X)...\n", obj1, obj2);
638 int intersects = Geometry::Intersects((Line *)obj1, (Line *)obj2, &t, &u);
639 //printf(" (%s) --> t=%lf, u=%lf\n", (intersects ? "true" : "FALSE"), t, u);
643 //printf("Connecting objects (%X, %X)...\n", obj1, obj2);
644 obj1->Connect(obj2, u);
645 obj2->Connect(obj1, t);
648 else if (((obj1->type == OTLine) && (obj2->type == OTDimension))
649 || ((obj2->type == OTLine) && (obj1->type == OTDimension)))
651 printf("Testing Line<->Dimension intersection...\n");
652 Line * line = (Line *)(obj1->type == OTLine ? obj1 : obj2);
653 Dimension * dim = (Dimension *)(obj1->type == OTDimension ? obj1 : obj2);
655 int intersects = Geometry::Intersects(line, dim, &t, &u);
656 printf(" -> intersects = %i, t=%lf, u=%lf\n", intersects, t, u);
660 obj1->Connect(obj2, u);
661 obj2->Connect(obj1, t);
671 void ApplicationWindow::HandleDisconnection(void)
676 void ApplicationWindow::HandleGridSizeInPixels(int size)
678 drawing->SetGridSize(size);
683 void ApplicationWindow::HandleGridSizeInBaseUnits(QString text)
687 double value = text.toDouble(&ok);
689 // Nothing parsable to a double, so quit...
690 if (!ok || value == 0)
693 // drawing->gridSpacing = value;
694 // Painter::zoom = drawing->gridPixels / drawing->gridSpacing;
695 Global::gridSpacing = value;
696 Global::zoom = drawing->gridPixels / Global::gridSpacing;
697 drawing->UpdateGridBackground();
702 void ApplicationWindow::HandleDimensionSize(QString text)
705 This is the third input on the view toolbar; not sure what it was supposed to do... (resize all dimensions in the drawing?)
709 double value = text.toDouble(&ok);
711 // Nothing parsable to a double, so quit...
712 if (!ok || value == 0)
715 // drawing->document.ResizeAllDimensions(value);
719 void ApplicationWindow::EditCopy(void)
721 if (drawing->select.size() > 0)
723 DeleteContents(clipboard);
724 clipboard = CopySelectedObjects(drawing->document.objects);
729 void ApplicationWindow::EditCut(void)
731 if (drawing->select.size() > 0)
733 DeleteContents(clipboard);
734 clipboard = MoveSelectedObjectsFrom(drawing->document.objects);
740 void ApplicationWindow::EditPaste(void)
742 if (clipboard.size() > 0)
744 // We want to maybe make it so that the pasted objects are being moved in a "mouse drag" state...
745 ClearSelected(drawing->document.objects);
746 SelectAll(clipboard);
747 drawing->document.Add(CopyObjects(clipboard));
754 // Select all *visible* objects. If an object is on a layer that is not
757 void ApplicationWindow::SelectAllObjects(void)
759 // Set object's state & update the drawing
760 SelectAll(drawing->document.objects);
765 void ApplicationWindow::CreateActions(void)
767 exitAct = CreateAction(tr("&Quit"), tr("Quit"), tr("Exits the application."),
768 QIcon(":/res/quit.png"), QKeySequence(tr("Ctrl+q")));
769 connect(exitAct, SIGNAL(triggered()), this, SLOT(close()));
771 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);
772 connect(snapToGridAct, SIGNAL(triggered()), this, SLOT(SnapToGridTool()));
774 fixAngleAct = CreateAction(tr("Fix &Angle"), tr("Fix Angle"), tr("Fixes the angle of an object."),
775 QIcon(":/res/fix-angle.png"), QKeySequence(tr("F,A")), true);
776 connect(fixAngleAct, SIGNAL(triggered()), this, SLOT(FixAngle()));
778 fixLengthAct = CreateAction(tr("Fix &Length"), tr("Fix Length"), tr("Fixes the length of an object."),
779 QIcon(":/res/fix-length.png"), QKeySequence(tr("F,L")), true);
780 connect(fixLengthAct, SIGNAL(triggered()), this, SLOT(FixLength()));
782 deleteAct = CreateAction(tr("&Delete"), tr("Delete Object"), tr("Deletes selected objects."), QIcon(":/res/delete-tool.png"), QKeySequence(tr("Delete")), true);
783 connect(deleteAct, SIGNAL(triggered()), this, SLOT(DeleteTool()));
785 addDimensionAct = CreateAction(tr("Add &Dimension"), tr("Add Dimension"), tr("Adds a dimension to the drawing."), QIcon(":/res/dimension-tool.png"), QKeySequence("D,I"), true);
786 connect(addDimensionAct, SIGNAL(triggered()), this, SLOT(DimensionTool()));
788 addLineAct = CreateAction(tr("Add &Line"), tr("Add Line"), tr("Adds lines to the drawing."), QIcon(":/res/add-line-tool.png"), QKeySequence("A,L"), true);
789 connect(addLineAct, SIGNAL(triggered()), this, SLOT(AddLineTool()));
791 addCircleAct = CreateAction(tr("Add &Circle"), tr("Add Circle"), tr("Adds circles to the drawing."), QIcon(":/res/add-circle-tool.png"), QKeySequence("A,C"), true);
792 connect(addCircleAct, SIGNAL(triggered()), this, SLOT(AddCircleTool()));
794 addArcAct = CreateAction(tr("Add &Arc"), tr("Add Arc"), tr("Adds arcs to the drawing."), QIcon(":/res/add-arc-tool.png"), QKeySequence("A,A"), true);
795 connect(addArcAct, SIGNAL(triggered()), this, SLOT(AddArcTool()));
797 addPolygonAct = CreateAction(tr("Add &Polygon"), tr("Add Polygon"), tr("Add polygons to the drawing."), QIcon(":/res/add-polygon-tool.png"), QKeySequence("A,P"), true);
798 connect(addPolygonAct, SIGNAL(triggered()), this, SLOT(AddPolygonTool()));
800 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);
801 connect(addSplineAct, SIGNAL(triggered()), this, SLOT(AddSplineTool()));
803 aboutAct = CreateAction(tr("About &Architektonas"), tr("About Architektonas"), tr("Gives information about this program."), QIcon(":/res/generic-tool.png"), QKeySequence());
804 connect(aboutAct, SIGNAL(triggered()), this, SLOT(HelpAbout()));
806 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);
807 connect(rotateAct, SIGNAL(triggered()), this, SLOT(RotateTool()));
809 zoomInAct = CreateAction(tr("Zoom &In"), tr("Zoom In"), tr("Zoom in on the document."), QIcon(":/res/zoom-in.png"), QKeySequence(tr("+")), QKeySequence(tr("=")));
810 connect(zoomInAct, SIGNAL(triggered()), this, SLOT(ZoomInTool()));
812 zoomOutAct = CreateAction(tr("Zoom &Out"), tr("Zoom Out"), tr("Zoom out of the document."), QIcon(":/res/zoom-out.png"), QKeySequence(tr("-")));
813 connect(zoomOutAct, SIGNAL(triggered()), this, SLOT(ZoomOutTool()));
815 fileNewAct = CreateAction(tr("&New Drawing"), tr("New Drawing"), tr("Creates a new drawing."), QIcon(":/res/file-new.png"), QKeySequence(tr("Ctrl+n")));
816 connect(fileNewAct, SIGNAL(triggered()), this, SLOT(FileNew()));
818 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")));
819 connect(fileOpenAct, SIGNAL(triggered()), this, SLOT(FileOpen()));
821 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")));
822 connect(fileSaveAct, SIGNAL(triggered()), this, SLOT(FileSave()));
824 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")));
825 connect(fileSaveAsAct, SIGNAL(triggered()), this, SLOT(FileSaveAs()));
827 fileCloseAct = CreateAction(tr("&Close Drawing"), tr("Close Drawing"), tr("Closes the current drawing."), QIcon(":/res/file-close.png"), QKeySequence(tr("Ctrl+w")));
829 settingsAct = CreateAction(tr("&Settings"), tr("Settings"), tr("Change certain defaults for Architektonas."), QIcon(":/res/settings.png"), QKeySequence());
830 connect(settingsAct, SIGNAL(triggered()), this, SLOT(Settings()));
832 groupAct = CreateAction(tr("&Group"), tr("Group"), tr("Group/ungroup selected objects."), QIcon(":/res/group-tool.png"), QKeySequence("g"));
833 connect(groupAct, SIGNAL(triggered()), this, SLOT(HandleGrouping()));
835 connectAct = CreateAction(tr("&Connect"), tr("Connect"), tr("Connect objects at point."), QIcon(":/res/connect-tool.png"), QKeySequence("c,c"));
836 connect(connectAct, SIGNAL(triggered()), this, SLOT(HandleConnection()));
838 disconnectAct = CreateAction(tr("&Disconnect"), tr("Disconnect"), tr("Disconnect objects joined at point."), QIcon(":/res/disconnect-tool.png"), QKeySequence("d,d"));
839 connect(disconnectAct, SIGNAL(triggered()), this, SLOT(HandleDisconnection()));
841 mirrorAct = CreateAction(tr("&Mirror"), tr("Mirror"), tr("Mirror selected objects around a line."), QIcon(":/res/mirror-tool.png"), QKeySequence("m,i"), true);
842 connect(mirrorAct, SIGNAL(triggered()), this, SLOT(MirrorTool()));
844 trimAct = CreateAction(tr("&Trim"), tr("Trim"), tr("Trim extraneous lines from selected objects."), QIcon(":/res/trim-tool.png"), QKeySequence("t,r"), true);
845 connect(trimAct, SIGNAL(triggered()), this, SLOT(TrimTool()));
847 triangulateAct = CreateAction(tr("&Triangulate"), tr("Triangulate"), tr("Make triangles from selected lines, preserving their lengths."), QIcon(":/res/triangulate-tool.png"), QKeySequence("t,g"), true);
848 connect(triangulateAct, SIGNAL(triggered()), this, SLOT(TriangulateTool()));
850 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")));
851 connect(editCutAct, SIGNAL(triggered()), this, SLOT(EditCut()));
853 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")));
854 connect(editCopyAct, SIGNAL(triggered()), this, SLOT(EditCopy()));
856 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")));
857 connect(editPasteAct, SIGNAL(triggered()), this, SLOT(EditPaste()));
859 selectAllAct = CreateAction(tr("Select &All"), tr("Select All Objects"), tr("Select all objects in the drawing."), QIcon(), QKeySequence(tr("Ctrl+a")));
860 connect(selectAllAct, SIGNAL(triggered()), this, SLOT(SelectAllObjects()));
862 //Hm. I think we'll have to have separate logic to do the "Radio Group Toolbar" thing...
863 // Yup, in order to turn them off, we'd have to have an "OFF" toolbar button. Ick.
864 /* QActionGroup * group = new QActionGroup(this);
865 group->addAction(deleteAct);
866 group->addAction(addDimensionAct);
867 group->addAction(addLineAct);
868 group->addAction(addCircleAct);
869 group->addAction(addArcAct);//*/
874 // Consolidates action creation from a multi-step process to a single-step one.
876 QAction * ApplicationWindow::CreateAction(QString name, QString tooltip,
877 QString statustip, QIcon icon, QKeySequence key, bool checkable/*= false*/)
879 QAction * action = new QAction(icon, name, this);
880 action->setToolTip(tooltip);
881 action->setStatusTip(statustip);
882 action->setShortcut(key);
883 action->setCheckable(checkable);
890 // This is essentially the same as the previous function, but this allows more
891 // than one key sequence to be added as key shortcuts.
893 QAction * ApplicationWindow::CreateAction(QString name, QString tooltip,
894 QString statustip, QIcon icon, QKeySequence key1, QKeySequence key2,
895 bool checkable/*= false*/)
897 QAction * action = new QAction(icon, name, this);
898 action->setToolTip(tooltip);
899 action->setStatusTip(statustip);
900 QList<QKeySequence> keyList;
901 keyList.append(key1);
902 keyList.append(key2);
903 action->setShortcuts(keyList);
904 action->setCheckable(checkable);
910 void ApplicationWindow::CreateMenus(void)
912 QMenu * menu = menuBar()->addMenu(tr("&File"));
913 menu->addAction(fileNewAct);
914 menu->addAction(fileOpenAct);
915 menu->addAction(fileSaveAct);
916 menu->addAction(fileSaveAsAct);
917 menu->addAction(fileCloseAct);
918 menu->addSeparator();
919 menu->addAction(exitAct);
921 menu = menuBar()->addMenu(tr("&View"));
922 menu->addAction(zoomInAct);
923 menu->addAction(zoomOutAct);
925 menu = menuBar()->addMenu(tr("&Edit"));
926 menu->addAction(snapToGridAct);
927 menu->addAction(groupAct);
928 menu->addAction(fixAngleAct);
929 menu->addAction(fixLengthAct);
930 menu->addAction(rotateAct);
931 menu->addAction(mirrorAct);
932 menu->addAction(trimAct);
933 menu->addAction(triangulateAct);
934 menu->addAction(connectAct);
935 menu->addAction(disconnectAct);
936 menu->addSeparator();
937 menu->addAction(selectAllAct);
938 menu->addAction(editCutAct);
939 menu->addAction(editCopyAct);
940 menu->addAction(editPasteAct);
941 menu->addAction(deleteAct);
942 menu->addSeparator();
943 menu->addAction(addLineAct);
944 menu->addAction(addCircleAct);
945 menu->addAction(addArcAct);
946 menu->addAction(addPolygonAct);
947 menu->addAction(addSplineAct);
948 menu->addAction(addDimensionAct);
949 menu->addSeparator();
950 menu->addAction(settingsAct);
952 menu = menuBar()->addMenu(tr("&Help"));
953 menu->addAction(aboutAct);
957 void ApplicationWindow::CreateToolbars(void)
959 QToolBar * toolbar = addToolBar(tr("File"));
960 toolbar->setObjectName("File"); // Needed for saveState()
961 toolbar->addAction(fileNewAct);
962 toolbar->addAction(fileOpenAct);
963 toolbar->addAction(fileSaveAct);
964 toolbar->addAction(fileSaveAsAct);
965 toolbar->addAction(fileCloseAct);
966 // toolbar->addAction(exitAct);
968 toolbar = addToolBar(tr("View"));
969 toolbar->setObjectName("View");
970 toolbar->addAction(zoomInAct);
971 toolbar->addAction(zoomOutAct);
973 QSpinBox * spinbox = new QSpinBox;
974 toolbar->addWidget(spinbox);
975 // QLineEdit * lineedit = new QLineEdit;
976 toolbar->addWidget(baseUnitInput);
977 toolbar->addWidget(dimensionSizeInput);
979 toolbar = addToolBar(tr("Edit"));
980 toolbar->setObjectName("Edit");
981 toolbar->addAction(snapToGridAct);
982 toolbar->addAction(groupAct);
983 toolbar->addAction(fixAngleAct);
984 toolbar->addAction(fixLengthAct);
985 toolbar->addAction(rotateAct);
986 toolbar->addAction(mirrorAct);
987 toolbar->addAction(trimAct);
988 toolbar->addAction(triangulateAct);
989 toolbar->addAction(editCutAct);
990 toolbar->addAction(editCopyAct);
991 toolbar->addAction(editPasteAct);
992 toolbar->addAction(deleteAct);
993 toolbar->addAction(connectAct);
994 toolbar->addAction(disconnectAct);
995 toolbar->addSeparator();
996 toolbar->addAction(addLineAct);
997 toolbar->addAction(addCircleAct);
998 toolbar->addAction(addArcAct);
999 toolbar->addAction(addPolygonAct);
1000 toolbar->addAction(addSplineAct);
1001 toolbar->addAction(addDimensionAct);
1003 spinbox->setRange(4, 256);
1004 spinbox->setValue(12);
1005 baseUnitInput->setText("12");
1006 connect(spinbox, SIGNAL(valueChanged(int)), this, SLOT(HandleGridSizeInPixels(int)));
1007 connect(baseUnitInput, SIGNAL(textChanged(QString)), this, SLOT(HandleGridSizeInBaseUnits(QString)));
1008 connect(dimensionSizeInput, SIGNAL(textChanged(QString)), this, SLOT(HandleDimensionSize(QString)));
1010 PenWidget * pw = new PenWidget();
1011 toolbar = addToolBar(tr("Pen"));
1012 toolbar->setObjectName(tr("Pen"));
1013 toolbar->addWidget(pw);
1014 connect(drawing, SIGNAL(ObjectSelected(Object *)), pw, SLOT(SetFields(Object *)));
1015 connect(pw, SIGNAL(WidthSelected(float)), drawing, SLOT(HandlePenWidth(float)));
1016 connect(pw, SIGNAL(StyleSelected(int)), drawing, SLOT(HandlePenStyle(int)));
1017 connect(pw, SIGNAL(ColorSelected(uint32_t)), drawing, SLOT(HandlePenColor(uint32_t)));
1018 connect(pw, SIGNAL(StampSelected(void)), drawing, SLOT(HandlePenStamp(void)));
1022 void ApplicationWindow::ReadSettings(void)
1024 QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint();
1025 QSize size = settings.value("size", QSize(400, 400)).toSize();
1026 drawing->useAntialiasing = settings.value("useAntialiasing", true).toBool();
1027 snapToGridAct->setChecked(settings.value("snapToGrid", true).toBool());
1030 restoreState(settings.value("windowState").toByteArray());
1034 void ApplicationWindow::WriteSettings(void)
1036 settings.setValue("pos", pos());
1037 settings.setValue("size", size());
1038 settings.setValue("windowState", saveState());
1039 settings.setValue("useAntialiasing", drawing->useAntialiasing);
1040 settings.setValue("snapToGrid", snapToGridAct->isChecked());