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...
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 CopySelectedObjectsTo(clipboard, drawing->document.objects);
729 void ApplicationWindow::EditCut(void)
731 if (drawing->select.size() > 0)
733 DeleteContents(clipboard);
734 MoveSelectedObjectsTo(clipboard, 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 // This only moves the cut/copied from the clipboard to the drawing.
746 // AddObjectsTo(drawing->document.objects, clipboard);
747 CopyObjects(clipboard, drawing->document.objects);
753 void ApplicationWindow::CreateActions(void)
755 exitAct = CreateAction(tr("&Quit"), tr("Quit"), tr("Exits the application."),
756 QIcon(":/res/quit.png"), QKeySequence(tr("Ctrl+q")));
757 connect(exitAct, SIGNAL(triggered()), this, SLOT(close()));
759 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);
760 connect(snapToGridAct, SIGNAL(triggered()), this, SLOT(SnapToGridTool()));
762 fixAngleAct = CreateAction(tr("Fix &Angle"), tr("Fix Angle"), tr("Fixes the angle of an object."),
763 QIcon(":/res/fix-angle.png"), QKeySequence(tr("F,A")), true);
764 connect(fixAngleAct, SIGNAL(triggered()), this, SLOT(FixAngle()));
766 fixLengthAct = CreateAction(tr("Fix &Length"), tr("Fix Length"), tr("Fixes the length of an object."),
767 QIcon(":/res/fix-length.png"), QKeySequence(tr("F,L")), true);
768 connect(fixLengthAct, SIGNAL(triggered()), this, SLOT(FixLength()));
770 deleteAct = CreateAction(tr("&Delete"), tr("Delete Object"), tr("Deletes selected objects."), QIcon(":/res/delete-tool.png"), QKeySequence(tr("Delete")), true);
771 connect(deleteAct, SIGNAL(triggered()), this, SLOT(DeleteTool()));
773 addDimensionAct = CreateAction(tr("Add &Dimension"), tr("Add Dimension"), tr("Adds a dimension to the drawing."), QIcon(":/res/dimension-tool.png"), QKeySequence("D,I"), true);
774 connect(addDimensionAct, SIGNAL(triggered()), this, SLOT(DimensionTool()));
776 addLineAct = CreateAction(tr("Add &Line"), tr("Add Line"), tr("Adds lines to the drawing."), QIcon(":/res/add-line-tool.png"), QKeySequence("A,L"), true);
777 connect(addLineAct, SIGNAL(triggered()), this, SLOT(AddLineTool()));
779 addCircleAct = CreateAction(tr("Add &Circle"), tr("Add Circle"), tr("Adds circles to the drawing."), QIcon(":/res/add-circle-tool.png"), QKeySequence("A,C"), true);
780 connect(addCircleAct, SIGNAL(triggered()), this, SLOT(AddCircleTool()));
782 addArcAct = CreateAction(tr("Add &Arc"), tr("Add Arc"), tr("Adds arcs to the drawing."), QIcon(":/res/add-arc-tool.png"), QKeySequence("A,A"), true);
783 connect(addArcAct, SIGNAL(triggered()), this, SLOT(AddArcTool()));
785 addPolygonAct = CreateAction(tr("Add &Polygon"), tr("Add Polygon"), tr("Add polygons to the drawing."), QIcon(":/res/add-polygon-tool.png"), QKeySequence("A,P"), true);
786 connect(addPolygonAct, SIGNAL(triggered()), this, SLOT(AddPolygonTool()));
788 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);
789 connect(addSplineAct, SIGNAL(triggered()), this, SLOT(AddSplineTool()));
791 aboutAct = CreateAction(tr("About &Architektonas"), tr("About Architektonas"), tr("Gives information about this program."), QIcon(":/res/generic-tool.png"), QKeySequence());
792 connect(aboutAct, SIGNAL(triggered()), this, SLOT(HelpAbout()));
794 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);
795 connect(rotateAct, SIGNAL(triggered()), this, SLOT(RotateTool()));
797 zoomInAct = CreateAction(tr("Zoom &In"), tr("Zoom In"), tr("Zoom in on the document."), QIcon(":/res/zoom-in.png"), QKeySequence(tr("+")), QKeySequence(tr("=")));
798 connect(zoomInAct, SIGNAL(triggered()), this, SLOT(ZoomInTool()));
800 zoomOutAct = CreateAction(tr("Zoom &Out"), tr("Zoom Out"), tr("Zoom out of the document."), QIcon(":/res/zoom-out.png"), QKeySequence(tr("-")));
801 connect(zoomOutAct, SIGNAL(triggered()), this, SLOT(ZoomOutTool()));
803 fileNewAct = CreateAction(tr("&New Drawing"), tr("New Drawing"), tr("Creates a new drawing."), QIcon(":/res/file-new.png"), QKeySequence(tr("Ctrl+n")));
804 connect(fileNewAct, SIGNAL(triggered()), this, SLOT(FileNew()));
806 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")));
807 connect(fileOpenAct, SIGNAL(triggered()), this, SLOT(FileOpen()));
809 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")));
810 connect(fileSaveAct, SIGNAL(triggered()), this, SLOT(FileSave()));
812 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")));
813 connect(fileSaveAsAct, SIGNAL(triggered()), this, SLOT(FileSaveAs()));
815 fileCloseAct = CreateAction(tr("&Close Drawing"), tr("Close Drawing"), tr("Closes the current drawing."), QIcon(":/res/file-close.png"), QKeySequence(tr("Ctrl+w")));
817 settingsAct = CreateAction(tr("&Settings"), tr("Settings"), tr("Change certain defaults for Architektonas."), QIcon(":/res/settings.png"), QKeySequence());
818 connect(settingsAct, SIGNAL(triggered()), this, SLOT(Settings()));
820 groupAct = CreateAction(tr("&Group"), tr("Group"), tr("Group/ungroup selected objects."), QIcon(":/res/group-tool.png"), QKeySequence("g"));
821 connect(groupAct, SIGNAL(triggered()), this, SLOT(HandleGrouping()));
823 connectAct = CreateAction(tr("&Connect"), tr("Connect"), tr("Connect objects at point."), QIcon(":/res/connect-tool.png"), QKeySequence("c,c"));
824 connect(connectAct, SIGNAL(triggered()), this, SLOT(HandleConnection()));
826 disconnectAct = CreateAction(tr("&Disconnect"), tr("Disconnect"), tr("Disconnect objects joined at point."), QIcon(":/res/disconnect-tool.png"), QKeySequence("d,d"));
827 connect(disconnectAct, SIGNAL(triggered()), this, SLOT(HandleDisconnection()));
829 mirrorAct = CreateAction(tr("&Mirror"), tr("Mirror"), tr("Mirror selected objects around a line."), QIcon(":/res/mirror-tool.png"), QKeySequence("m,i"), true);
830 connect(mirrorAct, SIGNAL(triggered()), this, SLOT(MirrorTool()));
832 trimAct = CreateAction(tr("&Trim"), tr("Trim"), tr("Trim extraneous lines from selected objects."), QIcon(":/res/trim-tool.png"), QKeySequence("t,r"), true);
833 connect(trimAct, SIGNAL(triggered()), this, SLOT(TrimTool()));
835 triangulateAct = CreateAction(tr("&Triangulate"), tr("Triangulate"), tr("Make triangles from selected lines, preserving their lengths."), QIcon(":/res/triangulate-tool.png"), QKeySequence("t,g"), true);
836 connect(triangulateAct, SIGNAL(triggered()), this, SLOT(TriangulateTool()));
838 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")));
839 connect(editCutAct, SIGNAL(triggered()), this, SLOT(EditCut()));
841 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")));
842 connect(editCopyAct, SIGNAL(triggered()), this, SLOT(EditCopy()));
844 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")));
845 connect(editPasteAct, SIGNAL(triggered()), this, SLOT(EditPaste()));
847 //Hm. I think we'll have to have separate logic to do the "Radio Group Toolbar" thing...
848 // Yup, in order to turn them off, we'd have to have an "OFF" toolbar button. Ick.
849 /* QActionGroup * group = new QActionGroup(this);
850 group->addAction(deleteAct);
851 group->addAction(addDimensionAct);
852 group->addAction(addLineAct);
853 group->addAction(addCircleAct);
854 group->addAction(addArcAct);//*/
859 // Consolidates action creation from a multi-step process to a single-step one.
861 QAction * ApplicationWindow::CreateAction(QString name, QString tooltip,
862 QString statustip, QIcon icon, QKeySequence key, bool checkable/*= false*/)
864 QAction * action = new QAction(icon, name, this);
865 action->setToolTip(tooltip);
866 action->setStatusTip(statustip);
867 action->setShortcut(key);
868 action->setCheckable(checkable);
875 // This is essentially the same as the previous function, but this allows more
876 // than one key sequence to be added as key shortcuts.
878 QAction * ApplicationWindow::CreateAction(QString name, QString tooltip,
879 QString statustip, QIcon icon, QKeySequence key1, QKeySequence key2,
880 bool checkable/*= false*/)
882 QAction * action = new QAction(icon, name, this);
883 action->setToolTip(tooltip);
884 action->setStatusTip(statustip);
885 QList<QKeySequence> keyList;
886 keyList.append(key1);
887 keyList.append(key2);
888 action->setShortcuts(keyList);
889 action->setCheckable(checkable);
895 void ApplicationWindow::CreateMenus(void)
897 QMenu * menu = menuBar()->addMenu(tr("&File"));
898 menu->addAction(fileNewAct);
899 menu->addAction(fileOpenAct);
900 menu->addAction(fileSaveAct);
901 menu->addAction(fileSaveAsAct);
902 menu->addAction(fileCloseAct);
903 menu->addSeparator();
904 menu->addAction(exitAct);
906 menu = menuBar()->addMenu(tr("&View"));
907 menu->addAction(zoomInAct);
908 menu->addAction(zoomOutAct);
910 menu = menuBar()->addMenu(tr("&Edit"));
911 menu->addAction(snapToGridAct);
912 menu->addAction(groupAct);
913 menu->addAction(fixAngleAct);
914 menu->addAction(fixLengthAct);
915 menu->addAction(rotateAct);
916 menu->addAction(mirrorAct);
917 menu->addAction(trimAct);
918 menu->addAction(triangulateAct);
919 menu->addAction(connectAct);
920 menu->addAction(disconnectAct);
921 menu->addSeparator();
922 menu->addAction(editCutAct);
923 menu->addAction(editCopyAct);
924 menu->addAction(editPasteAct);
925 menu->addAction(deleteAct);
926 menu->addSeparator();
927 menu->addAction(addLineAct);
928 menu->addAction(addCircleAct);
929 menu->addAction(addArcAct);
930 menu->addAction(addPolygonAct);
931 menu->addAction(addSplineAct);
932 menu->addAction(addDimensionAct);
933 menu->addSeparator();
934 menu->addAction(settingsAct);
936 menu = menuBar()->addMenu(tr("&Help"));
937 menu->addAction(aboutAct);
941 void ApplicationWindow::CreateToolbars(void)
943 QToolBar * toolbar = addToolBar(tr("File"));
944 toolbar->setObjectName("File"); // Needed for saveState()
945 toolbar->addAction(fileNewAct);
946 toolbar->addAction(fileOpenAct);
947 toolbar->addAction(fileSaveAct);
948 toolbar->addAction(fileSaveAsAct);
949 toolbar->addAction(fileCloseAct);
950 // toolbar->addAction(exitAct);
952 toolbar = addToolBar(tr("View"));
953 toolbar->setObjectName("View");
954 toolbar->addAction(zoomInAct);
955 toolbar->addAction(zoomOutAct);
957 QSpinBox * spinbox = new QSpinBox;
958 toolbar->addWidget(spinbox);
959 // QLineEdit * lineedit = new QLineEdit;
960 toolbar->addWidget(baseUnitInput);
961 toolbar->addWidget(dimensionSizeInput);
963 toolbar = addToolBar(tr("Edit"));
964 toolbar->setObjectName("Edit");
965 toolbar->addAction(snapToGridAct);
966 toolbar->addAction(groupAct);
967 toolbar->addAction(fixAngleAct);
968 toolbar->addAction(fixLengthAct);
969 toolbar->addAction(rotateAct);
970 toolbar->addAction(mirrorAct);
971 toolbar->addAction(trimAct);
972 toolbar->addAction(triangulateAct);
973 toolbar->addAction(editCutAct);
974 toolbar->addAction(editCopyAct);
975 toolbar->addAction(editPasteAct);
976 toolbar->addAction(deleteAct);
977 toolbar->addAction(connectAct);
978 toolbar->addAction(disconnectAct);
979 toolbar->addSeparator();
980 toolbar->addAction(addLineAct);
981 toolbar->addAction(addCircleAct);
982 toolbar->addAction(addArcAct);
983 toolbar->addAction(addPolygonAct);
984 toolbar->addAction(addSplineAct);
985 toolbar->addAction(addDimensionAct);
987 spinbox->setRange(4, 256);
988 spinbox->setValue(12);
989 baseUnitInput->setText("12");
990 connect(spinbox, SIGNAL(valueChanged(int)), this, SLOT(HandleGridSizeInPixels(int)));
991 connect(baseUnitInput, SIGNAL(textChanged(QString)), this, SLOT(HandleGridSizeInBaseUnits(QString)));
992 connect(dimensionSizeInput, SIGNAL(textChanged(QString)), this, SLOT(HandleDimensionSize(QString)));
994 PenWidget * pw = new PenWidget();
995 toolbar = addToolBar(tr("Pen"));
996 toolbar->setObjectName(tr("Pen"));
997 toolbar->addWidget(pw);
998 connect(drawing, SIGNAL(ObjectSelected(Object *)), pw, SLOT(SetFields(Object *)));
999 connect(pw, SIGNAL(WidthSelected(float)), drawing, SLOT(HandlePenWidth(float)));
1000 connect(pw, SIGNAL(StyleSelected(int)), drawing, SLOT(HandlePenStyle(int)));
1001 connect(pw, SIGNAL(ColorSelected(uint32_t)), drawing, SLOT(HandlePenColor(uint32_t)));
1005 void ApplicationWindow::ReadSettings(void)
1007 QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint();
1008 QSize size = settings.value("size", QSize(400, 400)).toSize();
1009 drawing->useAntialiasing = settings.value("useAntialiasing", true).toBool();
1010 snapToGridAct->setChecked(settings.value("snapToGrid", true).toBool());
1013 restoreState(settings.value("windowState").toByteArray());
1017 void ApplicationWindow::WriteSettings(void)
1019 settings.setValue("pos", pos());
1020 settings.setValue("size", size());
1021 settings.setValue("windowState", saveState());
1022 settings.setValue("useAntialiasing", drawing->useAntialiasing);
1023 settings.setValue("snapToGrid", snapToGridAct->isChecked());