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 "drawingview.h"
36 #include "generaltab.h"
38 #include "layerwidget.h"
39 #include "objectwidget.h"
41 #include "penwidget.h"
42 #include "settingsdialog.h"
47 DrawingView * ApplicationWindow::drawing;
49 ApplicationWindow::ApplicationWindow():
50 baseUnitInput(new QLineEdit),
51 dimensionSizeInput(new QLineEdit),
52 settings("Underground Software", "Architektonas")
54 drawing = new DrawingView(this);
55 drawing->setMouseTracking(true); // We want *all* mouse events...!
56 drawing->setFocusPolicy(Qt::StrongFocus);
57 setCentralWidget(drawing);
59 aboutWin = new AboutWindow(this);
61 // ((TTEdit *)qApp)->charWnd = new CharWindow(this);
63 setWindowIcon(QIcon(":/res/atns-icon.png"));
64 setWindowTitle("Architektonas");
70 // Create Dock widgets
71 QDockWidget * dock1 = new QDockWidget(tr("Layers"), this);
72 LayerWidget * lw = new LayerWidget;
74 addDockWidget(Qt::RightDockWidgetArea, dock1);
75 QDockWidget * dock2 = new QDockWidget(tr("Blocks"), this);
76 BlockWidget * bw = new BlockWidget;
78 addDockWidget(Qt::RightDockWidgetArea, dock2);
79 QDockWidget * dock3 = new QDockWidget(tr("Object"), this);
80 ObjectWidget * ow = new ObjectWidget;
82 addDockWidget(Qt::RightDockWidgetArea, dock3);
83 // Needed for saveState()
84 dock1->setObjectName("Layers");
85 dock2->setObjectName("Blocks");
86 dock3->setObjectName("Object");
89 zoomIndicator = new QLabel("Zoom: 100% Grid: 12.0\" BU: Inch");
90 statusBar()->addPermanentWidget(zoomIndicator);
91 statusBar()->showMessage(tr("Ready"));
94 setUnifiedTitleAndToolBarOnMac(true);
95 Global::font = new QFont("Verdana", 15, QFont::Bold);
97 connect(lw, SIGNAL(LayerDeleted(int)), drawing, SLOT(DeleteCurrentLayer(int)));
98 connect(lw, SIGNAL(LayerToggled()), drawing, SLOT(HandleLayerToggle()));
99 connect(lw, SIGNAL(LayersSwapped(int, int)), drawing, SLOT(HandleLayerSwap(int, int)));
100 connect(this, SIGNAL(ReloadLayers()), lw, SLOT(Reload()));
102 connect(drawing, SIGNAL(ObjectHovered(Object *)), ow, SLOT(ShowInfo(Object *)));
103 connect(drawing, SIGNAL(NeedZoomUpdate()), this, SLOT(UpdateZoom()));
106 void ApplicationWindow::closeEvent(QCloseEvent * event)
109 event->accept(); // Use ignore() if can't close for some reason
110 //Do we have a memory leak here if we don't delete the font in the Object???
113 void ApplicationWindow::FileNew(void)
115 // Warn the user if drawing has changed and hasn't been saved...
120 msg.setText("The document has been modified.");
121 msg.setInformativeText("Do you want to save your changes?");
122 msg.setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);
123 msg.setDefaultButton(QMessageBox::Save);
124 int response = msg.exec();
128 case QMessageBox::Save:
132 case QMessageBox::Discard:
133 // Don't Save was clicked
135 case QMessageBox::Cancel:
136 // Cancel was clicked
142 FileIO::ResetLayerVectors();
144 DeleteContents(drawing->document.objects);
145 drawing->document.objects.clear();
146 drawing->dirty = false;
148 documentName.clear();
149 setWindowTitle("Architektonas - Untitled");
150 statusBar()->showMessage(tr("New drawing is ready."));
153 void ApplicationWindow::FileOpen(void)
155 QString filename = QFileDialog::getOpenFileName(this, tr("Open Drawing"),
156 "", tr("Architektonas files (*.drawing)"));
158 // User cancelled open
159 if (filename.isEmpty())
162 FILE * file = fopen(filename.toUtf8().data(), "r");
167 msg.setText(QString(tr("Could not open file \"%1\" for loading!")).arg(filename));
168 msg.setIcon(QMessageBox::Critical);
173 Container container(true); // Make sure it's a top level container...
174 bool successful = FileIO::LoadAtnsFile(file, &container);
180 msg.setText(QString(tr("Could not load file \"%1\"!")).arg(filename));
181 msg.setIcon(QMessageBox::Critical);
183 // Make sure to delete any hanging objects in the container...
184 DeleteContents(container.objects);
188 //printf("FileOpen: container size = %li\n", container.objects.size());
189 // Keep memory leaks from happening by getting rid of the old document
190 DeleteContents(drawing->document.objects);
192 // We can do this because the vector is just a bunch of pointers to our
193 // Objects, and the Containers (non-empty) can be moved this way with no
195 drawing->document = container;
196 drawing->dirty = false;
198 documentName = filename;
199 setWindowTitle(QString("Architektonas - %1").arg(documentName));
200 statusBar()->showMessage(tr("Drawing loaded."));
203 void ApplicationWindow::FileSave(void)
205 if (documentName.isEmpty())
206 documentName = QFileDialog::getSaveFileName(this, tr("Save Drawing"),
207 "", tr("Architektonas drawings (*.drawing)"));
209 FILE * file = fopen(documentName.toUtf8().data(), "w");
214 msg.setText(QString(tr("Could not open file \"%1\" for saving!")).arg(documentName));
215 msg.setIcon(QMessageBox::Critical);
220 bool successful = FileIO::SaveAtnsFile(file, &drawing->document);
226 msg.setText(QString(tr("Could not save file \"%1\"!")).arg(documentName));
227 msg.setIcon(QMessageBox::Critical);
229 // In this case, we should unlink the created file, since it's not
231 // unlink(documentName.toUtf8().data());
232 QFile::remove(documentName);
236 drawing->dirty = false;
237 setWindowTitle(QString("Architektonas - %1").arg(documentName));
238 statusBar()->showMessage(tr("Drawing saved."));
241 void ApplicationWindow::FileSaveAs(void)
243 QString filename = QFileDialog::getSaveFileName(this, tr("Save Drawing As"),
244 documentName, tr("Architektonas drawings (*.drawing)"));
246 if (!filename.isEmpty())
248 documentName = filename;
253 void ApplicationWindow::PrintPreview(void)
255 QPrintPreviewDialog * dlg = new QPrintPreviewDialog(this);
257 connect(dlg, SIGNAL(paintRequested(QPrinter *)), this, SLOT(HandlePrintRequest(QPrinter *)));
262 void ApplicationWindow::HandlePrintRequest(QPrinter * printer)
264 QPainter qtPainter(printer);
265 Painter painter(&qtPainter);
267 // Do object rendering...
268 for(int i=0; i<Global::numLayers; i++)
270 // if (Global::layerHidden[i] == false)
271 drawing->RenderObjects(&painter, drawing->document.objects, i);
275 void ApplicationWindow::contextMenuEvent(QContextMenuEvent * event)
278 menu.addAction(mirrorAct);
279 menu.addAction(rotateAct);
280 menu.addAction(trimAct);
281 menu.exec(event->globalPos());
284 void ApplicationWindow::SnapToGridTool(void)
286 Global::snapToGrid = snapToGridAct->isChecked();
289 void ApplicationWindow::FixAngle(void)
291 Global::fixedAngle = fixAngleAct->isChecked();
294 void ApplicationWindow::FixLength(void)
296 Global::fixedLength = fixLengthAct->isChecked();
299 void ApplicationWindow::DeleteTool(void)
301 // For this tool, we check first to see if anything is selected. If so, we
302 // delete those and *don't* select the delete tool.
303 // if (drawing->numSelected > 0)
304 if (drawing->select.size() > 0)
306 DeleteSelectedObjects(drawing->document.objects);
308 deleteAct->setChecked(false);
312 // Otherwise, toggle the state of the tool
313 ClearUIToolStatesExcept(deleteAct);
314 SetInternalToolStates();
317 void ApplicationWindow::DimensionTool(void)
319 ClearUIToolStatesExcept(addDimensionAct);
320 SetInternalToolStates();
323 void ApplicationWindow::RotateTool(void)
325 ClearUIToolStatesExcept(rotateAct);
327 // Do tear-down if Rotate tool has been turned off
328 if (!rotateAct->isChecked())
329 drawing->RotateHandler(ToolCleanup, Point(0, 0));
331 SetInternalToolStates();
334 void ApplicationWindow::MirrorTool(void)
336 ClearUIToolStatesExcept(mirrorAct);
338 // Do tear-down if Rotate tool has been turned off
339 if (!mirrorAct->isChecked())
340 drawing->MirrorHandler(ToolCleanup, Point(0, 0));
342 SetInternalToolStates();
345 void ApplicationWindow::TrimTool(void)
347 ClearUIToolStatesExcept(trimAct);
348 SetInternalToolStates();
351 void ApplicationWindow::TriangulateTool(void)
353 ClearUIToolStatesExcept(triangulateAct);
354 SetInternalToolStates();
357 void ApplicationWindow::AddLineTool(void)
359 ClearUIToolStatesExcept(addLineAct);
360 SetInternalToolStates();
363 void ApplicationWindow::AddCircleTool(void)
365 ClearUIToolStatesExcept(addCircleAct);
366 SetInternalToolStates();
369 void ApplicationWindow::AddArcTool(void)
371 ClearUIToolStatesExcept(addArcAct);
372 SetInternalToolStates();
375 void ApplicationWindow::AddPolygonTool(void)
377 ClearUIToolStatesExcept(addPolygonAct);
378 SetInternalToolStates();
381 void ApplicationWindow::AddSplineTool(void)
383 ClearUIToolStatesExcept(addSplineAct);
384 SetInternalToolStates();
387 void ApplicationWindow::ZoomInTool(void)
389 double zoomFactor = 1.20;
390 QSize size = drawing->size();
391 Vector center = Painter::QtToCartesianCoords(Vector(size.width() / 2.0, size.height() / 2.0));
393 Global::origin = center - ((center - Global::origin) / zoomFactor);
394 Global::zoom *= zoomFactor;
399 void ApplicationWindow::ZoomOutTool(void)
401 double zoomFactor = 1.20;
402 QSize size = drawing->size();
403 Vector center = Painter::QtToCartesianCoords(Vector(size.width() / 2.0, size.height() / 2.0));
405 Global::origin = center + ((Global::origin - center) * zoomFactor);
406 Global::zoom /= zoomFactor;
411 void ApplicationWindow::UpdateZoom(void)
413 // And now, a bunch of heuristics to select the right grid size--autogrid!
415 if (Global::zoom < 0.25)
416 Global::gridSpacing = 48.0;
417 else if (Global::zoom >= 0.25 && Global::zoom < 0.50)
418 Global::gridSpacing = 36.0;
419 else if (Global::zoom >= 0.50 && Global::zoom < 1.00)
420 Global::gridSpacing = 24.0;
421 else if (Global::zoom >= 1.00 && Global::zoom < 2.00)
422 Global::gridSpacing = 12.0;
423 else if (Global::zoom >= 2.00 && Global::zoom < 4.00)
424 Global::gridSpacing = 6.0;
425 else if (Global::zoom >= 4.00 && Global::zoom < 8.00)
426 Global::gridSpacing = 3.0;
427 else if (Global::zoom >= 8.00 && Global::zoom < 16.00)
428 Global::gridSpacing = 1.0;
429 else if (Global::zoom >= 16.00 && Global::zoom < 32.00)
430 Global::gridSpacing = 0.5;
431 else if (Global::zoom >= 32.00 && Global::zoom < 64.00)
432 Global::gridSpacing = 0.25;
433 else if (Global::zoom >= 64.00 && Global::zoom < 128.00)
434 Global::gridSpacing = 0.125;
435 else if (Global::zoom >= 128.00 && Global::zoom < 256.00)
436 Global::gridSpacing = 0.0625;
437 else if (Global::zoom >= 256.00 && Global::zoom < 512.00)
438 Global::gridSpacing = 0.03125;
440 Global::gridSpacing = 0.015625;
442 // drawing->SetGridSize((double)(Global::gridSpacing * Global::zoom));
445 zoomIndicator->setText(QString("Zoom: %1% Grid: %2\" BU: Inch").arg(Global::zoom * 100.0).arg(Global::gridSpacing));
447 // This is the problem... Changing this causes the state to update itself again, screwing up the origin... !!! FIX !!! (commented out for now)
448 // baseUnitInput->setText(QString("%1").arg(Global::gridSpacing));
451 void ApplicationWindow::ClearUIToolStatesExcept(QAction * exception)
453 QAction * actionList[] = {
454 addArcAct, addLineAct, addCircleAct, addDimensionAct, addPolygonAct,
455 addSplineAct, deleteAct, rotateAct, mirrorAct, trimAct,
459 for(int i=0; actionList[i]!=0; i++)
461 if (actionList[i] != exception)
462 actionList[i]->setChecked(false);
466 void ApplicationWindow::SetInternalToolStates(void)
468 // We can be sure that if we've come here, then either an active tool is
469 // being deactivated, or a new tool is being created. In either case, the
470 // old tool needs to be deleted.
471 Global::toolState = TSNone;
473 if (addLineAct->isChecked())
474 Global::tool = TTLine;
475 else if (addCircleAct->isChecked())
476 Global::tool = TTCircle;
477 else if (addArcAct->isChecked())
478 Global::tool = TTArc;
479 else if (addDimensionAct->isChecked())
480 Global::tool = TTDimension;
481 else if (addSplineAct->isChecked())
482 Global::tool = TTSpline;
483 else if (addPolygonAct->isChecked())
484 Global::tool = TTPolygon;
485 else if (deleteAct->isChecked())
486 Global::tool = TTDelete;
487 else if (mirrorAct->isChecked())
488 Global::tool = TTMirror;
489 else if (rotateAct->isChecked())
490 Global::tool = TTRotate;
491 else if (trimAct->isChecked())
492 Global::tool = TTTrim;
493 else if (triangulateAct->isChecked())
494 Global::tool = TTTriangulate;
496 Global::tool = TTNone;
501 void ApplicationWindow::HelpAbout(void)
506 void ApplicationWindow::Settings(void)
508 SettingsDialog dlg(this);
509 dlg.generalTab->antialiasChk->setChecked(drawing->useAntialiasing);
511 if (dlg.exec() == false)
514 // Deal with stuff here (since user hit "OK" button...)
515 drawing->useAntialiasing = dlg.generalTab->antialiasChk->isChecked();
520 // Group a bunch of selected objects (which can include other groups) together
521 // or ungroup a selected group.
523 void ApplicationWindow::HandleGrouping(void)
525 int numSelected = drawing->select.size();
527 // If nothing selected, do nothing
528 if (numSelected == 0)
530 statusBar()->showMessage(tr("No objects selected to make a group from."));
534 // If it's a group that's selected, ungroup it and leave the objects in a
536 if (numSelected == 1)
538 Object * obj = (Object *)drawing->select[0];
540 if (obj->type != OTContainer)
542 statusBar()->showMessage(tr("A group requires two or more selected objects."));
546 // Need the parent of the group, we're assuming here that the parent is
547 // the drawing's document. Does it matter? Maybe...
548 // Could just stipulate that grouping like this only takes place where
549 // the parent of the group is the drawing's document. Makes life much
551 Container * c = (Container *)obj;
552 SelectAll(c->objects);
553 RemoveSelectedObjects(drawing->document.objects);
554 AddObjectsTo(drawing->document.objects, c->objects);
555 drawing->select.clear();
556 AddObjectsTo(drawing->select, c->objects);
558 statusBar()->showMessage(tr("Objects ungrouped."));
559 //printf("Ungroup: document size = %li\n", drawing->document.objects.size());
561 // Otherwise, if it's a group of 2 or more objects (which can be groups too)
562 // group them and select the group
563 else if (numSelected > 1)
565 Container * c = new Container();
566 MoveSelectedObjectsTo(c->objects, drawing->document.objects);
567 drawing->document.objects.push_back(c);
568 ClearSelected(c->objects);
570 c->layer = Global::activeLayer;
572 Rect r = drawing->GetObjectExtents((Object *)c);
573 c->p[0] = r.TopLeft();
574 c->p[1] = r.BottomRight();
576 drawing->select.clear();
577 drawing->select.push_back(c);
578 statusBar()->showMessage(QString(tr("Grouped %1 objects.")).arg(numSelected));
579 //printf("Group: document size = %li\n", drawing->document.objects.size());
585 void ApplicationWindow::HandleConnection(void)
588 //double tt = Geometry::ParameterOfLineAndPoint(Vector(0, 0), Vector(10, 0), Vector(8, 2));
589 //printf("Parameter of point @ (8,2) of line (0,0), (10,0): %lf\n", tt);
590 int itemsSelected = drawing->document.ItemsSelected();
592 // If nothing selected, do nothing
593 if (itemsSelected == 0)
595 statusBar()->showMessage(tr("No objects selected to connect."));
599 // If one thing selected, do nothing
600 if (itemsSelected == 1)
602 statusBar()->showMessage(tr("Nothing to connect object to."));
606 // This is O(n^2 / 2) :-P
607 for(int i=0; i<itemsSelected; i++)
609 Object * obj1 = drawing->document.SelectedItem(i);
611 for(int j=i+1; j<itemsSelected; j++)
613 Object * obj2 = drawing->document.SelectedItem(j);
616 // if ((obj1->type != OTLine) || (obj2->type != OTLine))
619 if ((obj1->type == OTLine) && (obj2->type == OTLine))
621 //printf("Testing objects for intersection (%X, %X)...\n", obj1, obj2);
622 int intersects = Geometry::Intersects((Line *)obj1, (Line *)obj2, &t, &u);
623 //printf(" (%s) --> t=%lf, u=%lf\n", (intersects ? "true" : "FALSE"), t, u);
627 //printf("Connecting objects (%X, %X)...\n", obj1, obj2);
628 obj1->Connect(obj2, u);
629 obj2->Connect(obj1, t);
632 else if (((obj1->type == OTLine) && (obj2->type == OTDimension))
633 || ((obj2->type == OTLine) && (obj1->type == OTDimension)))
635 printf("Testing Line<->Dimension intersection...\n");
636 Line * line = (Line *)(obj1->type == OTLine ? obj1 : obj2);
637 Dimension * dim = (Dimension *)(obj1->type == OTDimension ? obj1 : obj2);
639 int intersects = Geometry::Intersects(line, dim, &t, &u);
640 printf(" -> intersects = %i, t=%lf, u=%lf\n", intersects, t, u);
644 obj1->Connect(obj2, u);
645 obj2->Connect(obj1, t);
654 void ApplicationWindow::HandleDisconnection(void)
658 void ApplicationWindow::HandleGridSizeInPixels(int /*size*/)
660 // drawing->SetGridSize((uint32_t)size);
664 void ApplicationWindow::HandleGridSizeInBaseUnits(QString text)
668 double value = text.toDouble(&ok);
670 // Nothing parsable to a double, so quit...
671 if (!ok || value == 0)
674 // drawing->gridSpacing = value;
675 // Painter::zoom = drawing->gridPixels / drawing->gridSpacing;
676 Global::gridSpacing = value;
677 Global::zoom = drawing->gridPixels / Global::gridSpacing;
678 // drawing->UpdateGridBackground();
682 void ApplicationWindow::HandleDimensionSize(QString text)
685 This is the third input on the view toolbar; not sure what it was supposed to do... (resize all dimensions in the drawing?)
689 double value = text.toDouble(&ok);
691 // Nothing parsable to a double, so quit...
692 if (!ok || value == 0)
695 // drawing->document.ResizeAllDimensions(value);
699 void ApplicationWindow::EditCopy(void)
701 if (drawing->select.size() > 0)
703 DeleteContents(clipboard);
704 clipboard = CopySelectedObjects(drawing->document.objects);
708 void ApplicationWindow::EditCut(void)
710 if (drawing->select.size() > 0)
712 DeleteContents(clipboard);
713 clipboard = MoveSelectedObjectsFrom(drawing->document.objects);
718 void ApplicationWindow::EditPaste(void)
720 if (clipboard.size() > 0)
722 // We want to maybe make it so that the pasted objects are being moved in a "mouse drag" state...
723 ClearSelected(drawing->document.objects);
724 SelectAll(clipboard);
725 drawing->document.Add(CopyObjects(clipboard));
731 // Select all *visible* objects. If an object is on a layer that is not
734 void ApplicationWindow::SelectAllObjects(void)
736 // Set object's state & update the drawing
737 SelectAll(drawing->document.objects);
741 void ApplicationWindow::CreateActions(void)
743 exitAct = CreateAction(tr("&Quit"), tr("Quit"), tr("Exits the application."),
744 QIcon(":/res/quit.png"), QKeySequence(tr("Ctrl+q")));
745 connect(exitAct, SIGNAL(triggered()), this, SLOT(close()));
747 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);
748 connect(snapToGridAct, SIGNAL(triggered()), this, SLOT(SnapToGridTool()));
750 fixAngleAct = CreateAction(tr("Fix &Angle"), tr("Fix Angle"), tr("Fixes the angle of an object."),
751 QIcon(":/res/fix-angle.png"), QKeySequence(tr("F,A")), true);
752 connect(fixAngleAct, SIGNAL(triggered()), this, SLOT(FixAngle()));
754 fixLengthAct = CreateAction(tr("Fix &Length"), tr("Fix Length"), tr("Fixes the length of an object."),
755 QIcon(":/res/fix-length.png"), QKeySequence(tr("F,L")), true);
756 connect(fixLengthAct, SIGNAL(triggered()), this, SLOT(FixLength()));
758 deleteAct = CreateAction(tr("&Delete"), tr("Delete Object"), tr("Deletes selected objects."), QIcon(":/res/delete-tool.png"), QKeySequence(tr("Delete")), true);
759 connect(deleteAct, SIGNAL(triggered()), this, SLOT(DeleteTool()));
761 addDimensionAct = CreateAction(tr("Add &Dimension"), tr("Add Dimension"), tr("Adds a dimension to the drawing."), QIcon(":/res/dimension-tool.png"), QKeySequence("D,I"), true);
762 connect(addDimensionAct, SIGNAL(triggered()), this, SLOT(DimensionTool()));
764 addLineAct = CreateAction(tr("Add &Line"), tr("Add Line"), tr("Adds lines to the drawing."), QIcon(":/res/add-line-tool.png"), QKeySequence("A,L"), true);
765 connect(addLineAct, SIGNAL(triggered()), this, SLOT(AddLineTool()));
767 addCircleAct = CreateAction(tr("Add &Circle"), tr("Add Circle"), tr("Adds circles to the drawing."), QIcon(":/res/add-circle-tool.png"), QKeySequence("A,C"), true);
768 connect(addCircleAct, SIGNAL(triggered()), this, SLOT(AddCircleTool()));
770 addArcAct = CreateAction(tr("Add &Arc"), tr("Add Arc"), tr("Adds arcs to the drawing."), QIcon(":/res/add-arc-tool.png"), QKeySequence("A,A"), true);
771 connect(addArcAct, SIGNAL(triggered()), this, SLOT(AddArcTool()));
773 addPolygonAct = CreateAction(tr("Add &Polygon"), tr("Add Polygon"), tr("Add polygons to the drawing."), QIcon(":/res/add-polygon-tool.png"), QKeySequence("A,P"), true);
774 connect(addPolygonAct, SIGNAL(triggered()), this, SLOT(AddPolygonTool()));
776 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);
777 connect(addSplineAct, SIGNAL(triggered()), this, SLOT(AddSplineTool()));
779 aboutAct = CreateAction(tr("About &Architektonas"), tr("About Architektonas"), tr("Gives information about this program."), QIcon(":/res/generic-tool.png"), QKeySequence());
780 connect(aboutAct, SIGNAL(triggered()), this, SLOT(HelpAbout()));
782 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);
783 connect(rotateAct, SIGNAL(triggered()), this, SLOT(RotateTool()));
785 zoomInAct = CreateAction(tr("Zoom &In"), tr("Zoom In"), tr("Zoom in on the document."), QIcon(":/res/zoom-in.png"), QKeySequence(tr("+")), QKeySequence(tr("=")));
786 connect(zoomInAct, SIGNAL(triggered()), this, SLOT(ZoomInTool()));
788 zoomOutAct = CreateAction(tr("Zoom &Out"), tr("Zoom Out"), tr("Zoom out of the document."), QIcon(":/res/zoom-out.png"), QKeySequence(tr("-")));
789 connect(zoomOutAct, SIGNAL(triggered()), this, SLOT(ZoomOutTool()));
791 fileNewAct = CreateAction(tr("&New Drawing"), tr("New Drawing"), tr("Creates a new drawing."), QIcon(":/res/file-new.png"), QKeySequence(tr("Ctrl+n")));
792 connect(fileNewAct, SIGNAL(triggered()), this, SLOT(FileNew()));
794 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")));
795 connect(fileOpenAct, SIGNAL(triggered()), this, SLOT(FileOpen()));
797 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")));
798 connect(fileSaveAct, SIGNAL(triggered()), this, SLOT(FileSave()));
800 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")));
801 connect(fileSaveAsAct, SIGNAL(triggered()), this, SLOT(FileSaveAs()));
803 fileCloseAct = CreateAction(tr("&Close Drawing"), tr("Close Drawing"), tr("Closes the current drawing."), QIcon(":/res/file-close.png"), QKeySequence(tr("Ctrl+w")));
805 settingsAct = CreateAction(tr("&Settings"), tr("Settings"), tr("Change certain defaults for Architektonas."), QIcon(":/res/settings.png"), QKeySequence());
806 connect(settingsAct, SIGNAL(triggered()), this, SLOT(Settings()));
808 groupAct = CreateAction(tr("&Group"), tr("Group"), tr("Group/ungroup selected objects."), QIcon(":/res/group-tool.png"), QKeySequence("g"));
809 connect(groupAct, SIGNAL(triggered()), this, SLOT(HandleGrouping()));
811 connectAct = CreateAction(tr("&Connect"), tr("Connect"), tr("Connect objects at point."), QIcon(":/res/connect-tool.png"), QKeySequence("c,c"));
812 connect(connectAct, SIGNAL(triggered()), this, SLOT(HandleConnection()));
814 disconnectAct = CreateAction(tr("&Disconnect"), tr("Disconnect"), tr("Disconnect objects joined at point."), QIcon(":/res/disconnect-tool.png"), QKeySequence("d,d"));
815 connect(disconnectAct, SIGNAL(triggered()), this, SLOT(HandleDisconnection()));
817 mirrorAct = CreateAction(tr("&Mirror"), tr("Mirror"), tr("Mirror selected objects around a line."), QIcon(":/res/mirror-tool.png"), QKeySequence("m,i"), true);
818 connect(mirrorAct, SIGNAL(triggered()), this, SLOT(MirrorTool()));
820 trimAct = CreateAction(tr("&Trim"), tr("Trim"), tr("Trim extraneous lines from selected objects."), QIcon(":/res/trim-tool.png"), QKeySequence("t,r"), true);
821 connect(trimAct, SIGNAL(triggered()), this, SLOT(TrimTool()));
823 triangulateAct = CreateAction(tr("&Triangulate"), tr("Triangulate"), tr("Make triangles from selected lines, preserving their lengths."), QIcon(":/res/triangulate-tool.png"), QKeySequence("t,g"), true);
824 connect(triangulateAct, SIGNAL(triggered()), this, SLOT(TriangulateTool()));
826 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")));
827 connect(editCutAct, SIGNAL(triggered()), this, SLOT(EditCut()));
829 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")));
830 connect(editCopyAct, SIGNAL(triggered()), this, SLOT(EditCopy()));
832 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")));
833 connect(editPasteAct, SIGNAL(triggered()), this, SLOT(EditPaste()));
835 selectAllAct = CreateAction(tr("Select &All"), tr("Select All Objects"), tr("Select all objects in the drawing."), QIcon(), QKeySequence(tr("Ctrl+a")));
836 connect(selectAllAct, SIGNAL(triggered()), this, SLOT(SelectAllObjects()));
838 printPreviewAct = CreateAction(tr("&Print Preview"), tr("Print Preview"), tr("Shows preview of printing operation."), QIcon(":/res/print-preview.png"), QKeySequence(tr("Ctrl+p")));
839 connect(printPreviewAct, SIGNAL(triggered()), this, SLOT(PrintPreview()));
841 //Hm. I think we'll have to have separate logic to do the "Radio Group Toolbar" thing...
842 // Yup, in order to turn them off, we'd have to have an "OFF" toolbar button. Ick.
843 /* QActionGroup * group = new QActionGroup(this);
844 group->addAction(deleteAct);
845 group->addAction(addDimensionAct);
846 group->addAction(addLineAct);
847 group->addAction(addCircleAct);
848 group->addAction(addArcAct);//*/
852 // Consolidates action creation from a multi-step process to a single-step one.
854 QAction * ApplicationWindow::CreateAction(QString name, QString tooltip,
855 QString statustip, QIcon icon, QKeySequence key, bool checkable/*= false*/)
857 QAction * action = new QAction(icon, name, this);
858 action->setToolTip(tooltip);
859 action->setStatusTip(statustip);
860 action->setShortcut(key);
861 action->setCheckable(checkable);
867 // This is essentially the same as the previous function, but this allows more
868 // than one key sequence to be added as key shortcuts.
870 QAction * ApplicationWindow::CreateAction(QString name, QString tooltip,
871 QString statustip, QIcon icon, QKeySequence key1, QKeySequence key2,
872 bool checkable/*= false*/)
874 QAction * action = new QAction(icon, name, this);
875 action->setToolTip(tooltip);
876 action->setStatusTip(statustip);
877 QList<QKeySequence> keyList;
878 keyList.append(key1);
879 keyList.append(key2);
880 action->setShortcuts(keyList);
881 action->setCheckable(checkable);
886 void ApplicationWindow::CreateMenus(void)
888 QMenu * menu = menuBar()->addMenu(tr("&File"));
889 menu->addAction(fileNewAct);
890 menu->addAction(fileOpenAct);
891 menu->addAction(fileSaveAct);
892 menu->addAction(fileSaveAsAct);
893 menu->addAction(fileCloseAct);
894 menu->addSeparator();
895 menu->addAction(printPreviewAct);
896 menu->addSeparator();
897 menu->addAction(exitAct);
899 menu = menuBar()->addMenu(tr("&View"));
900 menu->addAction(zoomInAct);
901 menu->addAction(zoomOutAct);
903 menu = menuBar()->addMenu(tr("&Edit"));
904 menu->addAction(snapToGridAct);
905 menu->addAction(groupAct);
906 menu->addAction(fixAngleAct);
907 menu->addAction(fixLengthAct);
908 menu->addAction(rotateAct);
909 menu->addAction(mirrorAct);
910 menu->addAction(trimAct);
911 menu->addAction(triangulateAct);
912 menu->addAction(connectAct);
913 menu->addAction(disconnectAct);
914 menu->addSeparator();
915 menu->addAction(selectAllAct);
916 menu->addAction(editCutAct);
917 menu->addAction(editCopyAct);
918 menu->addAction(editPasteAct);
919 menu->addAction(deleteAct);
920 menu->addSeparator();
921 menu->addAction(addLineAct);
922 menu->addAction(addCircleAct);
923 menu->addAction(addArcAct);
924 menu->addAction(addPolygonAct);
925 menu->addAction(addSplineAct);
926 menu->addAction(addDimensionAct);
927 menu->addSeparator();
928 menu->addAction(settingsAct);
930 menu = menuBar()->addMenu(tr("&Help"));
931 menu->addAction(aboutAct);
934 void ApplicationWindow::CreateToolbars(void)
936 QToolBar * toolbar = addToolBar(tr("File"));
937 toolbar->setObjectName("File"); // Needed for saveState()
938 toolbar->addAction(fileNewAct);
939 toolbar->addAction(fileOpenAct);
940 toolbar->addAction(fileSaveAct);
941 toolbar->addAction(fileSaveAsAct);
942 toolbar->addAction(fileCloseAct);
943 // toolbar->addAction(exitAct);
945 toolbar = addToolBar(tr("View"));
946 toolbar->setObjectName("View");
947 toolbar->addAction(zoomInAct);
948 toolbar->addAction(zoomOutAct);
950 QSpinBox * spinbox = new QSpinBox;
951 toolbar->addWidget(spinbox);
952 // QLineEdit * lineedit = new QLineEdit;
953 toolbar->addWidget(baseUnitInput);
954 toolbar->addWidget(dimensionSizeInput);
956 toolbar = addToolBar(tr("Edit"));
957 toolbar->setObjectName("Edit");
958 toolbar->addAction(snapToGridAct);
959 toolbar->addAction(groupAct);
960 toolbar->addAction(fixAngleAct);
961 toolbar->addAction(fixLengthAct);
962 toolbar->addAction(rotateAct);
963 toolbar->addAction(mirrorAct);
964 toolbar->addAction(trimAct);
965 toolbar->addAction(triangulateAct);
966 toolbar->addAction(editCutAct);
967 toolbar->addAction(editCopyAct);
968 toolbar->addAction(editPasteAct);
969 toolbar->addAction(deleteAct);
970 toolbar->addAction(connectAct);
971 toolbar->addAction(disconnectAct);
972 toolbar->addSeparator();
973 toolbar->addAction(addLineAct);
974 toolbar->addAction(addCircleAct);
975 toolbar->addAction(addArcAct);
976 toolbar->addAction(addPolygonAct);
977 toolbar->addAction(addSplineAct);
978 toolbar->addAction(addDimensionAct);
980 spinbox->setRange(4, 256);
981 spinbox->setValue(12);
982 baseUnitInput->setText("12");
983 connect(spinbox, SIGNAL(valueChanged(int)), this, SLOT(HandleGridSizeInPixels(int)));
984 connect(baseUnitInput, SIGNAL(textChanged(QString)), this, SLOT(HandleGridSizeInBaseUnits(QString)));
985 connect(dimensionSizeInput, SIGNAL(textChanged(QString)), this, SLOT(HandleDimensionSize(QString)));
987 PenWidget * pw = new PenWidget();
988 toolbar = addToolBar(tr("Pen"));
989 toolbar->setObjectName(tr("Pen"));
990 toolbar->addWidget(pw);
991 connect(drawing, SIGNAL(ObjectSelected(Object *)), pw, SLOT(SetFields(Object *)));
992 connect(pw, SIGNAL(WidthSelected(float)), drawing, SLOT(HandlePenWidth(float)));
993 connect(pw, SIGNAL(StyleSelected(int)), drawing, SLOT(HandlePenStyle(int)));
994 connect(pw, SIGNAL(ColorSelected(uint32_t)), drawing, SLOT(HandlePenColor(uint32_t)));
995 // connect(pw, SIGNAL(StampSelected(void)), drawing, SLOT(HandlePenStamp(QAction *)));
996 connect(pw->tbStamp, SIGNAL(triggered(QAction *)), drawing, SLOT(HandlePenStamp(QAction *)));
997 connect(pw->tbDropper, SIGNAL(triggered(QAction *)), drawing, SLOT(HandlePenDropper(QAction *)));
1000 void ApplicationWindow::ReadSettings(void)
1002 QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint();
1003 QSize size = settings.value("size", QSize(400, 400)).toSize();
1004 drawing->useAntialiasing = settings.value("useAntialiasing", true).toBool();
1005 snapToGridAct->setChecked(settings.value("snapToGrid", true).toBool());
1008 restoreState(settings.value("windowState").toByteArray());
1011 void ApplicationWindow::WriteSettings(void)
1013 settings.setValue("pos", pos());
1014 settings.setValue("size", size());
1015 settings.setValue("windowState", saveState());
1016 settings.setValue("useAntialiasing", drawing->useAntialiasing);
1017 settings.setValue("snapToGrid", snapToGridAct->isChecked());