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"
31 #include <QPrintPreviewDialog>
33 #include "blockwidget.h"
34 #include "commandprocessor.h"
35 #include "consolewidget.h"
36 #include "drawingsettingsdlg.h"
37 #include "drawingview.h"
39 #include "generaltab.h"
41 #include "layerwidget.h"
42 #include "objectwidget.h"
44 #include "penwidget.h"
45 #include "settingsdialog.h"
50 DrawingView * ApplicationWindow::drawing;
52 ApplicationWindow::ApplicationWindow():
53 baseUnitInput(new QLineEdit),
54 dimensionSizeInput(new QLineEdit),
55 settings("Underground Software", "Architektonas")
57 drawing = new DrawingView(this);
58 drawing->setMouseTracking(true); // We want *all* mouse events...!
59 drawing->setFocusPolicy(Qt::StrongFocus);
60 setCentralWidget(drawing);
62 aboutWin = new AboutWindow(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 QDockWidget * dock4 = new QDockWidget(tr("Command"), this);
85 cw = new ConsoleWidget;
87 addDockWidget(Qt::BottomDockWidgetArea, dock4);
89 // Needed for saveState()
90 dock1->setObjectName("Layers");
91 dock2->setObjectName("Blocks");
92 dock3->setObjectName("Object");
93 dock4->setObjectName("Commands");
96 zoomIndicator = new QLabel("Zoom: 100% Grid: 12.0\" BU: Inch");
97 statusBar()->addPermanentWidget(zoomIndicator);
98 statusBar()->showMessage(tr("Ready"));
101 // Make sure the bottom left corner gets owned by the right side:
102 setCorner(Qt::BottomRightCorner, Qt::RightDockWidgetArea);
103 setUnifiedTitleAndToolBarOnMac(true);
104 Global::font = new QFont("Verdana", 15, QFont::Bold);
106 connect(lw, SIGNAL(LayerDeleted(int)), drawing, SLOT(DeleteCurrentLayer(int)));
107 connect(lw, SIGNAL(LayerToggled()), drawing, SLOT(HandleLayerToggle()));
108 connect(lw, SIGNAL(LayersSwapped(int, int)), drawing, SLOT(HandleLayerSwap(int, int)));
109 connect(this, SIGNAL(ReloadLayers()), lw, SLOT(Reload()));
111 connect(drawing, SIGNAL(ObjectHovered(Object *)), ow, SLOT(ShowInfo(Object *)));
112 connect(drawing, SIGNAL(NeedZoomUpdate()), this, SLOT(UpdateZoom()));
114 connect(cw->cmdProc, SIGNAL(UpdateNeeded()), this, SLOT(UpdateFromCommand()));
117 void ApplicationWindow::closeEvent(QCloseEvent * event)
120 event->accept(); // Use ignore() if can't close for some reason
121 //Do we have a memory leak here if we don't delete the font in the Object???
124 void ApplicationWindow::FileNew(void)
126 // Warn the user if drawing has changed and hasn't been saved...
131 msg.setText("The document has been modified.");
132 msg.setInformativeText("Do you want to save your changes?");
133 msg.setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);
134 msg.setDefaultButton(QMessageBox::Save);
135 int response = msg.exec();
139 case QMessageBox::Save:
143 case QMessageBox::Discard:
144 // Don't Save was clicked
146 case QMessageBox::Cancel:
147 // Cancel was clicked
153 FileIO::ResetLayerVectors();
155 DeleteContents(drawing->document.objects);
156 drawing->document.objects.clear();
157 drawing->dirty = false;
159 documentName.clear();
160 // setWindowTitle("Architektonas - Untitled");
161 setWindowFilePath(documentName);
162 statusBar()->showMessage(tr("New drawing is ready."));
165 void ApplicationWindow::FileOpen(void)
167 QString filename = QFileDialog::getOpenFileName(this, tr("Open Drawing"),
168 "", tr("Architektonas files (*.drawing)"));
170 // User cancelled open
171 if (filename.isEmpty())
177 void ApplicationWindow::FileOpenRecent(void)
179 QAction * action = qobject_cast<QAction *>(sender());
184 LoadFile(action->data().toString());
187 void ApplicationWindow::LoadFile(QString filename)
189 FILE * file = fopen(filename.toUtf8().data(), "r");
194 msg.setText(QString(tr("Could not open file \"%1\" for loading!")).arg(filename));
195 msg.setIcon(QMessageBox::Critical);
200 Container container(true); // Make sure it's a top level container...
201 bool successful = FileIO::LoadAtnsFile(file, &container);
207 msg.setText(QString(tr("Could not load file \"%1\"!")).arg(filename));
208 msg.setIcon(QMessageBox::Critical);
210 // Make sure to delete any hanging objects in the container...
211 DeleteContents(container.objects);
215 // Keep memory leaks from happening by getting rid of the old document
216 DeleteContents(drawing->document.objects);
218 // We can do this because the vector is just a bunch of pointers to our
219 // Objects, and the Containers (non-empty) can be moved this way with no
221 drawing->document = container;
222 drawing->dirty = false;
224 documentName = filename;
225 // setWindowTitle(QString("Architektonas - %1").arg(documentName));
227 statusBar()->showMessage(tr("Drawing loaded."));
230 void ApplicationWindow::FileSave(void)
232 if (documentName.isEmpty())
233 documentName = QFileDialog::getSaveFileName(this, tr("Save Drawing"),
234 "", tr("Architektonas drawings (*.drawing)"));
236 FILE * file = fopen(documentName.toUtf8().data(), "w");
241 msg.setText(QString(tr("Could not open file \"%1\" for saving!")).arg(documentName));
242 msg.setIcon(QMessageBox::Critical);
247 bool successful = FileIO::SaveAtnsFile(file, &drawing->document);
253 msg.setText(QString(tr("Could not save file \"%1\"!")).arg(documentName));
254 msg.setIcon(QMessageBox::Critical);
256 // In this case, we should unlink the created file, since it's not
258 // unlink(documentName.toUtf8().data());
259 QFile::remove(documentName);
263 drawing->dirty = false;
264 // setWindowTitle(QString("Architektonas - %1").arg(documentName));
265 AdjustMRU(documentName);
266 statusBar()->showMessage(tr("Drawing saved."));
269 void ApplicationWindow::FileSaveAs(void)
271 QString filename = QFileDialog::getSaveFileName(this, tr("Save Drawing As"),
272 documentName, tr("Architektonas drawings (*.drawing)"));
274 if (!filename.isEmpty())
276 documentName = filename;
281 void ApplicationWindow::PrintPreview(void)
283 QPrintPreviewDialog * dlg = new QPrintPreviewDialog(this);
285 connect(dlg, SIGNAL(paintRequested(QPrinter *)), this, SLOT(HandlePrintRequest(QPrinter *)));
290 void ApplicationWindow::HandlePrintRequest(QPrinter * printer)
292 QPainter qtPainter(printer);
293 Painter painter(&qtPainter);
295 // Save vars for screen
296 Point originSave = Global::origin;
297 double zoomSave = Global::zoom;
298 Vector screenSizeSave = Global::screenSize;
300 // Adjust zoom + origin to fit the paper (or NxM pages if we have 'em)
301 Rect r = drawing->GetObjectExtents((Object *)(&(drawing->document)));
303 QPageLayout pageLayout = printer->pageLayout();
304 QRect pageRect = pageLayout.paintRectPixels(printer->resolution());
306 Global::origin = r.BottomLeft();
307 Global::screenSize.x = pageRect.width();
308 Global::screenSize.y = pageRect.height();
310 double xScale = (double)pageRect.width() / r.Width();
311 double yScale = (double)pageRect.height() / r.Height();
312 Global::zoom = qMin(xScale, yScale);
315 Global::origin.y -= (((double)pageRect.height() / Global::zoom) - r.Height()) / 2.0;
317 Global::origin.x -= (((double)pageRect.width() / Global::zoom) - r.Width()) / 2.0;
319 // Do object rendering...
320 for(int i=0; i<Global::numLayers; i++)
321 drawing->RenderObjects(&painter, drawing->document.objects, i);
324 Global::origin = originSave;
325 Global::zoom = zoomSave;
326 Global::screenSize = screenSizeSave;
332 void ApplicationWindow::contextMenuEvent(QContextMenuEvent * event)
334 // Proof of concept, still need to code up the real thing
337 VPVector hovered = drawing->GetHovered();
339 if ((drawing->select.size() > 0) || (hovered.size() > 0))
341 QMenu * layerMenu = menu.addMenu("To layer");
345 for(int i=0; i<Global::numLayers; i++)
347 QAction * act = new QAction(QIcon(), Global::layerName[i].c_str(), this);
349 layerMenu->addAction(act);
350 layerAct.append(act);
351 connect(act, SIGNAL(triggered()), this, SLOT(MoveToLayer()));
355 menu.addAction(mirrorAct);
356 menu.addAction(rotateAct);
357 menu.addAction(trimAct);
358 menu.exec(event->globalPos());
361 void ApplicationWindow::MoveToLayer(void)
363 QAction * act = qobject_cast<QAction *>(sender());
365 drawing->MoveSelectedToLayer(act->data().toInt());
369 void ApplicationWindow::UpdateFromCommand(void)
375 void ApplicationWindow::SnapToGridTool(void)
377 Global::snapToGrid = snapToGridAct->isChecked();
380 void ApplicationWindow::FixAngle(void)
382 Global::fixedAngle = fixAngleAct->isChecked();
385 void ApplicationWindow::FixLength(void)
387 Global::fixedLength = fixLengthAct->isChecked();
390 void ApplicationWindow::DeleteTool(void)
392 // For this tool, we check first to see if anything is selected. If so, we
393 // delete those and *don't* select the delete tool.
394 if (drawing->select.size() > 0)
396 DeleteSelectedObjects(drawing->document.objects);
398 deleteAct->setChecked(false);
402 // Otherwise, toggle the state of the tool
403 ClearUIToolStatesExcept(deleteAct);
404 SetInternalToolStates();
405 Global::toolSuppressCrosshair = true;
408 void ApplicationWindow::DimensionTool(void)
410 ClearUIToolStatesExcept(addDimensionAct);
411 SetInternalToolStates();
414 void ApplicationWindow::RotateTool(void)
416 ClearUIToolStatesExcept(rotateAct);
418 // Do tear-down if Rotate tool has been turned off
419 if (!rotateAct->isChecked())
420 drawing->RotateHandler(ToolCleanup, Point(0, 0));
422 SetInternalToolStates();
425 void ApplicationWindow::MirrorTool(void)
427 ClearUIToolStatesExcept(mirrorAct);
429 // Do tear-down if Rotate tool has been turned off
430 if (!mirrorAct->isChecked())
431 drawing->MirrorHandler(ToolCleanup, Point(0, 0));
433 SetInternalToolStates();
436 void ApplicationWindow::TrimTool(void)
438 ClearUIToolStatesExcept(trimAct);
439 SetInternalToolStates();
440 Global::toolSuppressCrosshair = true;
443 void ApplicationWindow::ParallelTool(void)
445 ClearUIToolStatesExcept(parallelAct);
446 SetInternalToolStates();
447 Global::toolSuppressCrosshair = true;
450 void ApplicationWindow::TriangulateTool(void)
452 ClearUIToolStatesExcept(triangulateAct);
453 SetInternalToolStates();
456 void ApplicationWindow::AddLineTool(void)
458 ClearUIToolStatesExcept(addLineAct);
459 SetInternalToolStates();
462 void ApplicationWindow::AddCircleTool(void)
464 ClearUIToolStatesExcept(addCircleAct);
465 SetInternalToolStates();
468 void ApplicationWindow::AddArcTool(void)
470 ClearUIToolStatesExcept(addArcAct);
471 SetInternalToolStates();
474 void ApplicationWindow::AddPolygonTool(void)
476 ClearUIToolStatesExcept(addPolygonAct);
477 SetInternalToolStates();
480 void ApplicationWindow::AddSplineTool(void)
482 ClearUIToolStatesExcept(addSplineAct);
483 SetInternalToolStates();
486 void ApplicationWindow::ZoomInTool(void)
488 double zoomFactor = 1.20;
489 QSize size = drawing->size();
490 Vector center = Painter::QtToCartesianCoords(Vector(size.width() / 2.0, size.height() / 2.0));
492 Global::origin = center - ((center - Global::origin) / zoomFactor);
493 Global::zoom *= zoomFactor;
498 void ApplicationWindow::ZoomOutTool(void)
500 double zoomFactor = 1.20;
501 QSize size = drawing->size();
502 Vector center = Painter::QtToCartesianCoords(Vector(size.width() / 2.0, size.height() / 2.0));
504 Global::origin = center + ((Global::origin - center) * zoomFactor);
505 Global::zoom /= zoomFactor;
510 void ApplicationWindow::UpdateZoom(void)
512 // And now, a bunch of heuristics to select the right grid size--autogrid!
514 if (Global::zoom < 0.25)
515 Global::gridSpacing = 48.0;
516 else if (Global::zoom >= 0.25 && Global::zoom < 0.50)
517 Global::gridSpacing = 36.0;
518 else if (Global::zoom >= 0.50 && Global::zoom < 1.00)
519 Global::gridSpacing = 24.0;
520 else if (Global::zoom >= 1.00 && Global::zoom < 2.00)
521 Global::gridSpacing = 12.0;
522 else if (Global::zoom >= 2.00 && Global::zoom < 4.00)
523 Global::gridSpacing = 6.0;
524 else if (Global::zoom >= 4.00 && Global::zoom < 8.00)
525 Global::gridSpacing = 3.0;
526 else if (Global::zoom >= 8.00 && Global::zoom < 16.00)
527 Global::gridSpacing = 1.0;
528 else if (Global::zoom >= 16.00 && Global::zoom < 32.00)
529 Global::gridSpacing = 0.5;
530 else if (Global::zoom >= 32.00 && Global::zoom < 64.00)
531 Global::gridSpacing = 0.25;
532 else if (Global::zoom >= 64.00 && Global::zoom < 128.00)
533 Global::gridSpacing = 0.125;
534 else if (Global::zoom >= 128.00 && Global::zoom < 256.00)
535 Global::gridSpacing = 0.0625;
536 else if (Global::zoom >= 256.00 && Global::zoom < 512.00)
537 Global::gridSpacing = 0.03125;
539 Global::gridSpacing = 0.015625;
543 zoomIndicator->setText(QString("Zoom: %1% Grid: %2\" BU: Inch").arg(Global::zoom * 100.0).arg(Global::gridSpacing));
545 // This is the problem... Changing this causes the state to update itself again, screwing up the origin... !!! FIX !!! (commented out for now)
546 // baseUnitInput->setText(QString("%1").arg(Global::gridSpacing));
549 void ApplicationWindow::ClearUIToolStatesExcept(QAction * exception)
551 QAction * actionList[] = {
552 addArcAct, addLineAct, addCircleAct, addDimensionAct, addPolygonAct,
553 addSplineAct, deleteAct, rotateAct, mirrorAct, trimAct,
554 triangulateAct, parallelAct, 0
557 for(int i=0; actionList[i]!=0; i++)
559 if (actionList[i] != exception)
560 actionList[i]->setChecked(false);
564 void ApplicationWindow::SetInternalToolStates(void)
566 // We can be sure that if we've come here, then either an active tool is
567 // being deactivated, or a new tool is being activated. In either case,
568 // the tool state needs to be reset. Also, we reset the crosshair
570 Global::toolState = TSNone;
571 Global::toolSuppressCrosshair = false;
573 if (addLineAct->isChecked())
574 Global::tool = TTLine;
575 else if (addCircleAct->isChecked())
576 Global::tool = TTCircle;
577 else if (addArcAct->isChecked())
578 Global::tool = TTArc;
579 else if (addDimensionAct->isChecked())
580 Global::tool = TTDimension;
581 else if (addSplineAct->isChecked())
582 Global::tool = TTSpline;
583 else if (addPolygonAct->isChecked())
584 Global::tool = TTPolygon;
585 else if (deleteAct->isChecked())
586 Global::tool = TTDelete;
587 else if (mirrorAct->isChecked())
588 Global::tool = TTMirror;
589 else if (rotateAct->isChecked())
590 Global::tool = TTRotate;
591 else if (trimAct->isChecked())
592 Global::tool = TTTrim;
593 else if (parallelAct->isChecked())
594 Global::tool = TTParallel;
595 else if (triangulateAct->isChecked())
596 Global::tool = TTTriangulate;
598 Global::tool = TTNone;
604 void ApplicationWindow::HelpAbout(void)
609 void ApplicationWindow::Settings(void)
611 SettingsDialog dlg(this);
612 dlg.generalTab->antialiasChk->setChecked(drawing->useAntialiasing);
614 if (dlg.exec() == false)
617 // Deal with stuff here (since user hit "OK" button...)
618 drawing->useAntialiasing = dlg.generalTab->antialiasChk->isChecked();
622 void ApplicationWindow::DrawingSettings(void)
624 DrawingSettingsDlg dlg(this);
625 dlg.baseUnit->setCurrentIndex(drawing->document.baseUnit);
626 dlg.unitStyle->setCurrentIndex(drawing->document.unitStyle);
627 dlg.decimalPrecision->setCurrentIndex(drawing->document.decimalPrecision);
628 dlg.fractionalPrecision->setCurrentIndex(drawing->document.fractionalPrecision);
630 if (dlg.exec() == false)
633 // Deal with stuff here (since user hit "OK" button...)
634 drawing->document.baseUnit = dlg.baseUnit->currentIndex();
635 drawing->document.unitStyle = dlg.unitStyle->currentIndex();
636 drawing->document.decimalPrecision= dlg.decimalPrecision->currentIndex();
637 drawing->document.fractionalPrecision = dlg.fractionalPrecision->currentIndex();
642 // Group a bunch of selected objects (which can include other groups) together
643 // or ungroup a selected group.
645 void ApplicationWindow::HandleGrouping(void)
647 int numSelected = drawing->select.size();
649 // If nothing selected, do nothing
650 if (numSelected == 0)
652 statusBar()->showMessage(tr("No objects selected to make a group from."));
656 // If it's a group that's selected, ungroup it and leave the objects in a
658 if (numSelected == 1)
660 Object * obj = (Object *)drawing->select[0];
662 if (obj->type != OTContainer)
664 statusBar()->showMessage(tr("A group requires two or more selected objects."));
668 // Need the parent of the group, we're assuming here that the parent is
669 // the drawing's document. Does it matter? Maybe...
670 // Could just stipulate that grouping like this only takes place where
671 // the parent of the group is the drawing's document. Makes life much
673 Container * c = (Container *)obj;
674 SelectAll(c->objects);
675 RemoveSelectedObjects(drawing->document.objects);
676 AddObjectsTo(drawing->document.objects, c->objects);
677 drawing->select.clear();
678 AddObjectsTo(drawing->select, c->objects);
680 statusBar()->showMessage(tr("Objects ungrouped."));
681 //printf("Ungroup: document size = %li\n", drawing->document.objects.size());
683 // Otherwise, if it's a group of 2 or more objects (which can be groups too)
684 // group them and select the group
685 else if (numSelected > 1)
687 Container * c = new Container();
688 MoveSelectedObjectsTo(c->objects, drawing->document.objects);
689 drawing->document.objects.push_back(c);
690 ClearSelected(c->objects);
692 c->layer = Global::activeLayer;
694 Rect r = drawing->GetObjectExtents((Object *)c);
695 c->p[0] = r.TopLeft();
696 c->p[1] = r.BottomRight();
698 drawing->select.clear();
699 drawing->select.push_back(c);
700 statusBar()->showMessage(QString(tr("Grouped %1 objects.")).arg(numSelected));
701 //printf("Group: document size = %li\n", drawing->document.objects.size());
707 void ApplicationWindow::HandleConnection(void)
710 //double tt = Geometry::ParameterOfLineAndPoint(Vector(0, 0), Vector(10, 0), Vector(8, 2));
711 //printf("Parameter of point @ (8,2) of line (0,0), (10,0): %lf\n", tt);
712 int itemsSelected = drawing->document.ItemsSelected();
714 // If nothing selected, do nothing
715 if (itemsSelected == 0)
717 statusBar()->showMessage(tr("No objects selected to connect."));
721 // If one thing selected, do nothing
722 if (itemsSelected == 1)
724 statusBar()->showMessage(tr("Nothing to connect object to."));
728 // This is O(n^2 / 2) :-P
729 for(int i=0; i<itemsSelected; i++)
731 Object * obj1 = drawing->document.SelectedItem(i);
733 for(int j=i+1; j<itemsSelected; j++)
735 Object * obj2 = drawing->document.SelectedItem(j);
738 // if ((obj1->type != OTLine) || (obj2->type != OTLine))
741 if ((obj1->type == OTLine) && (obj2->type == OTLine))
743 //printf("Testing objects for intersection (%X, %X)...\n", obj1, obj2);
744 int intersects = Geometry::Intersects((Line *)obj1, (Line *)obj2, &t, &u);
745 //printf(" (%s) --> t=%lf, u=%lf\n", (intersects ? "true" : "FALSE"), t, u);
749 //printf("Connecting objects (%X, %X)...\n", obj1, obj2);
750 obj1->Connect(obj2, u);
751 obj2->Connect(obj1, t);
754 else if (((obj1->type == OTLine) && (obj2->type == OTDimension))
755 || ((obj2->type == OTLine) && (obj1->type == OTDimension)))
757 printf("Testing Line<->Dimension intersection...\n");
758 Line * line = (Line *)(obj1->type == OTLine ? obj1 : obj2);
759 Dimension * dim = (Dimension *)(obj1->type == OTDimension ? obj1 : obj2);
761 int intersects = Geometry::Intersects(line, dim, &t, &u);
762 printf(" -> intersects = %i, t=%lf, u=%lf\n", intersects, t, u);
766 obj1->Connect(obj2, u);
767 obj2->Connect(obj1, t);
776 void ApplicationWindow::HandleDisconnection(void)
780 void ApplicationWindow::HandleGridSizeInPixels(int /*size*/)
782 // drawing->SetGridSize((uint32_t)size);
786 void ApplicationWindow::HandleGridSizeInBaseUnits(QString text)
790 double value = text.toDouble(&ok);
792 // Nothing parsable to a double, so quit...
793 if (!ok || value == 0)
796 Global::gridSpacing = value;
797 Global::zoom = drawing->gridPixels / Global::gridSpacing;
801 void ApplicationWindow::HandleDimensionSize(QString text)
804 This is the third input on the view toolbar; not sure what it was supposed to do... (resize all dimensions in the drawing?)
808 double value = text.toDouble(&ok);
810 // Nothing parsable to a double, so quit...
811 if (!ok || value == 0)
814 // drawing->document.ResizeAllDimensions(value);
818 void ApplicationWindow::EditCopy(void)
820 if (drawing->select.size() > 0)
822 DeleteContents(clipboard);
823 clipboard = CopySelectedObjects(drawing->document.objects);
827 void ApplicationWindow::EditCut(void)
829 if (drawing->select.size() > 0)
831 DeleteContents(clipboard);
832 clipboard = MoveSelectedObjectsFrom(drawing->document.objects);
837 void ApplicationWindow::EditPaste(void)
839 if (clipboard.size() > 0)
841 // We want to maybe make it so that the pasted objects are being moved in a "mouse drag" state...
842 ClearSelected(drawing->document.objects);
843 SelectAll(clipboard);
844 drawing->document.Add(CopyObjects(clipboard));
850 // Select all *visible* objects. If an object is on a layer that is not
853 void ApplicationWindow::SelectAllObjects(void)
855 // Set object's state & update the drawing
856 SelectAll(drawing->document.objects);
860 void ApplicationWindow::CreateActions(void)
862 exitAct = CreateAction(tr("&Quit"), tr("Quit"), tr("Exits the application."),
863 QIcon(":/res/quit.png"), QKeySequence(tr("Ctrl+q")));
864 connect(exitAct, SIGNAL(triggered()), this, SLOT(close()));
866 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);
867 connect(snapToGridAct, SIGNAL(triggered()), this, SLOT(SnapToGridTool()));
869 fixAngleAct = CreateAction(tr("Fix &Angle"), tr("Fix Angle"), tr("Fixes the angle of an object."),
870 QIcon(":/res/fix-angle.png"), QKeySequence(tr("F,A")), true);
871 connect(fixAngleAct, SIGNAL(triggered()), this, SLOT(FixAngle()));
873 fixLengthAct = CreateAction(tr("Fix &Length"), tr("Fix Length"), tr("Fixes the length of an object."),
874 QIcon(":/res/fix-length.png"), QKeySequence(tr("F,L")), true);
875 connect(fixLengthAct, SIGNAL(triggered()), this, SLOT(FixLength()));
877 deleteAct = CreateAction(tr("&Delete"), tr("Delete Object"), tr("Deletes selected objects."), QIcon(":/res/delete-tool.png"), QKeySequence(tr("Delete")), true);
878 connect(deleteAct, SIGNAL(triggered()), this, SLOT(DeleteTool()));
880 addDimensionAct = CreateAction(tr("Add &Dimension"), tr("Add Dimension"), tr("Adds a dimension to the drawing."), QIcon(":/res/dimension-tool.png"), QKeySequence("D,I"), true);
881 connect(addDimensionAct, SIGNAL(triggered()), this, SLOT(DimensionTool()));
883 addLineAct = CreateAction(tr("Add &Line"), tr("Add Line"), tr("Adds lines to the drawing."), QIcon(":/res/add-line-tool.png"), QKeySequence("A,L"), true);
884 connect(addLineAct, SIGNAL(triggered()), this, SLOT(AddLineTool()));
886 addCircleAct = CreateAction(tr("Add &Circle"), tr("Add Circle"), tr("Adds circles to the drawing."), QIcon(":/res/add-circle-tool.png"), QKeySequence("A,C"), true);
887 connect(addCircleAct, SIGNAL(triggered()), this, SLOT(AddCircleTool()));
889 addArcAct = CreateAction(tr("Add &Arc"), tr("Add Arc"), tr("Adds arcs to the drawing."), QIcon(":/res/add-arc-tool.png"), QKeySequence("A,A"), true);
890 connect(addArcAct, SIGNAL(triggered()), this, SLOT(AddArcTool()));
892 addPolygonAct = CreateAction(tr("Add &Polygon"), tr("Add Polygon"), tr("Add polygons to the drawing."), QIcon(":/res/add-polygon-tool.png"), QKeySequence("A,P"), true);
893 connect(addPolygonAct, SIGNAL(triggered()), this, SLOT(AddPolygonTool()));
895 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);
896 connect(addSplineAct, SIGNAL(triggered()), this, SLOT(AddSplineTool()));
898 aboutAct = CreateAction(tr("About &Architektonas"), tr("About Architektonas"), tr("Gives information about this program."), QIcon(":/res/generic-tool.png"), QKeySequence());
899 connect(aboutAct, SIGNAL(triggered()), this, SLOT(HelpAbout()));
901 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);
902 connect(rotateAct, SIGNAL(triggered()), this, SLOT(RotateTool()));
904 zoomInAct = CreateAction(tr("Zoom &In"), tr("Zoom In"), tr("Zoom in on the document."), QIcon(":/res/zoom-in.png"), QKeySequence(tr("+")), QKeySequence(tr("=")));
905 connect(zoomInAct, SIGNAL(triggered()), this, SLOT(ZoomInTool()));
907 zoomOutAct = CreateAction(tr("Zoom &Out"), tr("Zoom Out"), tr("Zoom out of the document."), QIcon(":/res/zoom-out.png"), QKeySequence(tr("-")));
908 connect(zoomOutAct, SIGNAL(triggered()), this, SLOT(ZoomOutTool()));
910 fileNewAct = CreateAction(tr("&New Drawing"), tr("New Drawing"), tr("Creates a new drawing."), QIcon(":/res/file-new.png"), QKeySequence(tr("Ctrl+n")));
911 connect(fileNewAct, SIGNAL(triggered()), this, SLOT(FileNew()));
913 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")));
914 connect(fileOpenAct, SIGNAL(triggered()), this, SLOT(FileOpen()));
916 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")));
917 connect(fileSaveAct, SIGNAL(triggered()), this, SLOT(FileSave()));
919 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")));
920 connect(fileSaveAsAct, SIGNAL(triggered()), this, SLOT(FileSaveAs()));
922 fileCloseAct = CreateAction(tr("&Close Drawing"), tr("Close Drawing"), tr("Closes the current drawing."), QIcon(":/res/file-close.png"), QKeySequence(tr("Ctrl+w")));
924 settingsAct = CreateAction(tr("&Settings"), tr("Settings"), tr("Change certain defaults for Architektonas."), QIcon(":/res/settings.png"), QKeySequence());
925 connect(settingsAct, SIGNAL(triggered()), this, SLOT(Settings()));
927 drawingSettingsAct = CreateAction(tr("&Drawing Settings"), tr("Drawing settings"), tr("Change certain defaults for the current drawing."), QIcon(":/res/settings.png"), QKeySequence());
928 connect(drawingSettingsAct, SIGNAL(triggered()), this, SLOT(DrawingSettings()));
930 groupAct = CreateAction(tr("&Group"), tr("Group"), tr("Group/ungroup selected objects."), QIcon(":/res/group-tool.png"), QKeySequence("g"));
931 connect(groupAct, SIGNAL(triggered()), this, SLOT(HandleGrouping()));
933 connectAct = CreateAction(tr("&Connect"), tr("Connect"), tr("Connect objects at point."), QIcon(":/res/connect-tool.png"), QKeySequence("c,c"));
934 connect(connectAct, SIGNAL(triggered()), this, SLOT(HandleConnection()));
936 disconnectAct = CreateAction(tr("&Disconnect"), tr("Disconnect"), tr("Disconnect objects joined at point."), QIcon(":/res/disconnect-tool.png"), QKeySequence("d,d"));
937 connect(disconnectAct, SIGNAL(triggered()), this, SLOT(HandleDisconnection()));
939 mirrorAct = CreateAction(tr("&Mirror"), tr("Mirror"), tr("Mirror selected objects around a line."), QIcon(":/res/mirror-tool.png"), QKeySequence("m,i"), true);
940 connect(mirrorAct, SIGNAL(triggered()), this, SLOT(MirrorTool()));
942 trimAct = CreateAction(tr("&Trim"), tr("Trim"), tr("Trim extraneous lines from selected objects."), QIcon(":/res/trim-tool.png"), QKeySequence("t,r"), true);
943 connect(trimAct, SIGNAL(triggered()), this, SLOT(TrimTool()));
945 parallelAct = CreateAction(tr("&Parallel"), tr("Parallel"), tr("Create copies of objects parallel to the original."), QIcon(":/res/parallel-tool.png"), QKeySequence("p,l"), true);
946 connect(parallelAct, SIGNAL(triggered()), this, SLOT(ParallelTool()));
948 triangulateAct = CreateAction(tr("&Triangulate"), tr("Triangulate"), tr("Make triangles from selected lines, preserving their lengths."), QIcon(":/res/triangulate-tool.png"), QKeySequence("t,g"), true);
949 connect(triangulateAct, SIGNAL(triggered()), this, SLOT(TriangulateTool()));
951 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")));
952 connect(editCutAct, SIGNAL(triggered()), this, SLOT(EditCut()));
954 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")));
955 connect(editCopyAct, SIGNAL(triggered()), this, SLOT(EditCopy()));
957 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")));
958 connect(editPasteAct, SIGNAL(triggered()), this, SLOT(EditPaste()));
960 selectAllAct = CreateAction(tr("Select &All"), tr("Select All Objects"), tr("Select all objects in the drawing."), QIcon(), QKeySequence(tr("Ctrl+a")));
961 connect(selectAllAct, SIGNAL(triggered()), this, SLOT(SelectAllObjects()));
963 printPreviewAct = CreateAction(tr("&Print Preview"), tr("Print Preview"), tr("Shows preview of printing operation."), QIcon(":/res/print-preview.png"), QKeySequence(tr("Ctrl+p")));
964 connect(printPreviewAct, SIGNAL(triggered()), this, SLOT(PrintPreview()));
966 //Hm. I think we'll have to have separate logic to do the "Radio Group Toolbar" thing...
967 // Yup, in order to turn them off, we'd have to have an "OFF" toolbar button. Ick.
968 /* QActionGroup * group = new QActionGroup(this);
969 group->addAction(deleteAct);
970 group->addAction(addDimensionAct);
971 group->addAction(addLineAct);
972 group->addAction(addCircleAct);
973 group->addAction(addArcAct);//*/
975 for(int i=0; i<MRU_MAX; i++)
977 QAction * rfa = new QAction(this);
978 rfa->setVisible(false);
979 connect(rfa, SIGNAL(triggered()), this, SLOT(FileOpenRecent()));
985 // Consolidates action creation from a multi-step process to a single-step one.
987 QAction * ApplicationWindow::CreateAction(QString name, QString tooltip,
988 QString statustip, QIcon icon, QKeySequence key, bool checkable/*= false*/)
990 QAction * action = new QAction(icon, name, this);
991 action->setToolTip(tooltip);
992 action->setStatusTip(statustip);
993 action->setShortcut(key);
994 action->setCheckable(checkable);
1000 // This is essentially the same as the previous function, but this allows more
1001 // than one key sequence to be added as key shortcuts.
1003 QAction * ApplicationWindow::CreateAction(QString name, QString tooltip,
1004 QString statustip, QIcon icon, QKeySequence key1, QKeySequence key2,
1005 bool checkable/*= false*/)
1007 QAction * action = new QAction(icon, name, this);
1008 action->setToolTip(tooltip);
1009 action->setStatusTip(statustip);
1010 QList<QKeySequence> keyList;
1011 keyList.append(key1);
1012 keyList.append(key2);
1013 action->setShortcuts(keyList);
1014 action->setCheckable(checkable);
1019 void ApplicationWindow::CreateMenus(void)
1021 QMenu * menu = menuBar()->addMenu(tr("&File"));
1022 menu->addAction(fileNewAct);
1023 menu->addAction(fileOpenAct);
1025 QMenu * recentMenu = menu->addMenu(tr("Open &Recent"));
1027 for(int i=0; i<MRU_MAX; i++)
1028 recentMenu->addAction(mruAct.at(i));
1030 UpdateMRUActionList();
1032 menu->addAction(fileSaveAct);
1033 menu->addAction(fileSaveAsAct);
1034 menu->addAction(fileCloseAct);
1035 menu->addSeparator();
1036 menu->addAction(printPreviewAct);
1037 menu->addSeparator();
1038 menu->addAction(exitAct);
1040 menu = menuBar()->addMenu(tr("&View"));
1041 menu->addAction(zoomInAct);
1042 menu->addAction(zoomOutAct);
1044 menu = menuBar()->addMenu(tr("&Edit"));
1045 menu->addAction(snapToGridAct);
1046 menu->addAction(groupAct);
1047 menu->addAction(fixAngleAct);
1048 menu->addAction(fixLengthAct);
1049 menu->addAction(rotateAct);
1050 menu->addAction(mirrorAct);
1051 menu->addAction(trimAct);
1052 menu->addAction(parallelAct);
1053 menu->addAction(triangulateAct);
1054 menu->addAction(connectAct);
1055 menu->addAction(disconnectAct);
1056 menu->addSeparator();
1057 menu->addAction(selectAllAct);
1058 menu->addAction(editCutAct);
1059 menu->addAction(editCopyAct);
1060 menu->addAction(editPasteAct);
1061 menu->addAction(deleteAct);
1062 menu->addSeparator();
1063 menu->addAction(addLineAct);
1064 menu->addAction(addCircleAct);
1065 menu->addAction(addArcAct);
1066 menu->addAction(addPolygonAct);
1067 menu->addAction(addSplineAct);
1068 menu->addAction(addDimensionAct);
1069 menu->addSeparator();
1070 menu->addAction(drawingSettingsAct);
1071 menu->addAction(settingsAct);
1073 menu = menuBar()->addMenu(tr("&Help"));
1074 menu->addAction(aboutAct);
1077 void ApplicationWindow::CreateToolbars(void)
1079 QToolBar * toolbar = addToolBar(tr("File"));
1080 toolbar->setObjectName("File"); // Needed for saveState()
1081 toolbar->addAction(fileNewAct);
1082 toolbar->addAction(fileOpenAct);
1083 toolbar->addAction(fileSaveAct);
1084 toolbar->addAction(fileSaveAsAct);
1085 toolbar->addAction(fileCloseAct);
1087 toolbar = addToolBar(tr("View"));
1088 toolbar->setObjectName("View");
1089 toolbar->addAction(zoomInAct);
1090 toolbar->addAction(zoomOutAct);
1092 QSpinBox * spinbox = new QSpinBox;
1093 toolbar->addWidget(spinbox);
1094 toolbar->addWidget(baseUnitInput);
1095 toolbar->addWidget(dimensionSizeInput);
1097 toolbar = addToolBar(tr("Edit"));
1098 toolbar->setObjectName("Edit");
1099 toolbar->addAction(snapToGridAct);
1100 toolbar->addAction(groupAct);
1101 toolbar->addAction(fixAngleAct);
1102 toolbar->addAction(fixLengthAct);
1103 toolbar->addAction(rotateAct);
1104 toolbar->addAction(mirrorAct);
1105 toolbar->addAction(trimAct);
1106 toolbar->addAction(parallelAct);
1107 toolbar->addAction(triangulateAct);
1108 toolbar->addAction(editCutAct);
1109 toolbar->addAction(editCopyAct);
1110 toolbar->addAction(editPasteAct);
1111 toolbar->addAction(deleteAct);
1112 toolbar->addAction(connectAct);
1113 toolbar->addAction(disconnectAct);
1114 toolbar->addSeparator();
1115 toolbar->addAction(addLineAct);
1116 toolbar->addAction(addCircleAct);
1117 toolbar->addAction(addArcAct);
1118 toolbar->addAction(addPolygonAct);
1119 toolbar->addAction(addSplineAct);
1120 toolbar->addAction(addDimensionAct);
1122 spinbox->setRange(4, 256);
1123 spinbox->setValue(12);
1124 baseUnitInput->setText("12");
1125 connect(spinbox, SIGNAL(valueChanged(int)), this, SLOT(HandleGridSizeInPixels(int)));
1126 connect(baseUnitInput, SIGNAL(textChanged(QString)), this, SLOT(HandleGridSizeInBaseUnits(QString)));
1127 connect(dimensionSizeInput, SIGNAL(textChanged(QString)), this, SLOT(HandleDimensionSize(QString)));
1129 PenWidget * pw = new PenWidget();
1130 toolbar = addToolBar(tr("Pen"));
1131 toolbar->setObjectName(tr("Pen"));
1132 toolbar->addWidget(pw);
1133 connect(drawing, SIGNAL(ObjectSelected(Object *)), pw, SLOT(SetFields(Object *)));
1134 connect(pw->tbStamp, SIGNAL(triggered(QAction *)), drawing, SLOT(HandlePenStamp(QAction *)));
1135 connect(pw->tbDropper, SIGNAL(triggered(QAction *)), drawing, SLOT(HandlePenDropper(QAction *)));
1138 void ApplicationWindow::ReadSettings(void)
1140 QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint();
1141 QSize size = settings.value("size", QSize(400, 400)).toSize();
1142 drawing->useAntialiasing = settings.value("useAntialiasing", true).toBool();
1143 snapToGridAct->setChecked(settings.value("snapToGrid", true).toBool());
1146 restoreState(settings.value("windowState").toByteArray());
1149 void ApplicationWindow::WriteSettings(void)
1151 settings.setValue("pos", pos());
1152 settings.setValue("size", size());
1153 settings.setValue("windowState", saveState());
1154 settings.setValue("useAntialiasing", drawing->useAntialiasing);
1155 settings.setValue("snapToGrid", snapToGridAct->isChecked());
1158 void ApplicationWindow::UpdateMRUActionList(void)
1160 QStringList mruFilePaths = settings.value("recentFiles").toStringList();
1162 int mruSize = (mruFilePaths.size() <= MRU_MAX ? mruFilePaths.size() : MRU_MAX);
1164 for(int i=0; i<mruSize; i++)
1166 QString filename = QFileInfo(mruFilePaths.at(i)).fileName();
1167 mruAct.at(i)->setText(filename);
1168 mruAct.at(i)->setData(mruFilePaths.at(i));
1169 mruAct.at(i)->setVisible(true);
1172 for(int i=mruSize; i<MRU_MAX; i++)
1173 mruAct.at(i)->setVisible(false);
1176 void ApplicationWindow::AdjustMRU(const QString & filePath)
1178 documentName = filePath;
1179 setWindowFilePath(documentName);
1181 QStringList mruFilePaths = settings.value("recentFiles").toStringList();
1182 mruFilePaths.removeAll(filePath);
1183 mruFilePaths.prepend(filePath);
1185 while (mruFilePaths.size() > MRU_MAX)
1186 mruFilePaths.removeLast();
1188 settings.setValue("recentFiles", mruFilePaths);
1190 UpdateMRUActionList();