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 "dimension.h"
34 #include "drawingview.h"
35 //#include "drawarcaction.h"
36 //#include "drawcircleaction.h"
37 //#include "drawdimensionaction.h"
38 //#include "drawlineaction.h"
39 //#include "drawsplineaction.h"
41 #include "generaltab.h"
42 //#include "geometry.h"
44 #include "layerwidget.h"
46 //#include "mirroraction.h"
48 //#include "rotateaction.h"
49 #include "settingsdialog.h"
50 //#include "triangulateaction.h"
51 //#include "trimaction.h"
55 DrawingView * ApplicationWindow::drawing;
58 ApplicationWindow::ApplicationWindow():
59 baseUnitInput(new QLineEdit),
60 dimensionSizeInput(new QLineEdit),
61 settings("Underground Software", "Architektonas")
63 drawing = new DrawingView(this);
64 drawing->setMouseTracking(true); // We want *all* mouse events...!
65 drawing->setFocusPolicy(Qt::StrongFocus);
66 setCentralWidget(drawing);
68 aboutWin = new AboutWindow(this);
70 // ((TTEdit *)qApp)->charWnd = new CharWindow(this);
72 setWindowIcon(QIcon(":/res/atns-icon.png"));
73 setWindowTitle("Architektonas");
79 // Create Dock widgets
80 QDockWidget * dock1 = new QDockWidget(tr("Layers"), this);
81 LayerWidget * lw = new LayerWidget;
83 addDockWidget(Qt::RightDockWidgetArea, dock1);
84 QDockWidget * dock2 = new QDockWidget(tr("Blocks"), this);
85 BlockWidget * bw = new BlockWidget;
87 addDockWidget(Qt::RightDockWidgetArea, dock2);
88 // Needed for saveState()
89 dock1->setObjectName("Layers");
90 dock2->setObjectName("Blocks");
93 zoomIndicator = new QLabel("Grid: 12.0\" BU: Inch");
94 statusBar()->addPermanentWidget(zoomIndicator);
95 statusBar()->showMessage(tr("Ready"));
98 setUnifiedTitleAndToolBarOnMac(true);
99 Global::font = new QFont("Verdana", 15, QFont::Bold);
101 connect(lw, SIGNAL(LayerSelected(int)), drawing, SLOT(SetCurrentLayer(int)));
105 void ApplicationWindow::closeEvent(QCloseEvent * event)
108 event->accept(); // Use ignore() if can't close for some reason
109 //Do we have a memory leak here if we don't delete the font in the Object???
113 void ApplicationWindow::FileNew(void)
115 // Should warn the user if drawing hasn't been saved...
116 // drawing->document.Clear();
117 drawing->document.objects.empty();
119 documentName.clear();
120 setWindowTitle("Architektonas - Untitled");
121 statusBar()->showMessage(tr("New drawing is ready."));
125 void ApplicationWindow::FileOpen(void)
127 QString filename = QFileDialog::getOpenFileName(this, tr("Open Drawing"),
128 "", tr("Architektonas files (*.drawing)"));
130 // User cancelled open
131 if (filename.isEmpty())
134 FILE * file = fopen(filename.toUtf8().data(), "r");
139 msg.setText(QString(tr("Could not open file \"%1\" for loading!")).arg(filename));
140 msg.setIcon(QMessageBox::Critical);
145 Container container;//(Vector(0, 0));
146 bool successful = FileIO::LoadAtnsFile(file, &container);
152 msg.setText(QString(tr("Could not load file \"%1\"!")).arg(filename));
153 msg.setIcon(QMessageBox::Critical);
158 printf("FileOpen: container size = %li\n", container.objects.size());
159 drawing->document = container;
161 documentName = filename;
162 setWindowTitle(QString("Architektonas - %1").arg(documentName));
163 statusBar()->showMessage(tr("Drawing loaded."));
167 void ApplicationWindow::FileSave(void)
169 if (documentName.isEmpty())
170 documentName = QFileDialog::getSaveFileName(this, tr("Save Drawing"),
171 "", tr("Architektonas drawings (*.drawing)"));
173 FILE * file = fopen(documentName.toUtf8().data(), "w");
178 msg.setText(QString(tr("Could not open file \"%1\" for saving!")).arg(documentName));
179 msg.setIcon(QMessageBox::Critical);
184 bool successful = FileIO::SaveAtnsFile(file, &drawing->document);
190 msg.setText(QString(tr("Could not save file \"%1\"!")).arg(documentName));
191 msg.setIcon(QMessageBox::Critical);
193 // In this case, we should unlink the created file, since it's not right...
194 // unlink(documentName.toUtf8().data());
195 QFile::remove(documentName);
199 setWindowTitle(QString("Architektonas - %1").arg(documentName));
200 statusBar()->showMessage(tr("Drawing saved."));
204 void ApplicationWindow::FileSaveAs(void)
206 QString filename = QFileDialog::getSaveFileName(this, tr("Save Drawing As"),
207 documentName, tr("Architektonas drawings (*.drawing)"));
209 if (!filename.isEmpty())
211 documentName = filename;
217 void ApplicationWindow::SnapToGridTool(void)
219 Global::snapToGrid = snapToGridAct->isChecked();
223 void ApplicationWindow::FixAngle(void)
225 Global::fixedAngle = fixAngleAct->isChecked();
229 void ApplicationWindow::FixLength(void)
231 Global::fixedLength = fixLengthAct->isChecked();
235 // We want certain tools to be exclusive, and this approach isn't working correctly...
236 void ApplicationWindow::DeleteTool(void)
238 // For this tool, we check first to see if anything is selected. If so, we
239 // delete those and *don't* select the delete tool.
241 if (drawing->document.ItemsSelected() > 0)
243 drawing->document.DeleteSelectedItems();
245 deleteAct->setChecked(false);
250 // Otherwise, toggle the state of the tool
251 ClearUIToolStatesExcept(deleteAct);
252 SetInternalToolStates();
256 void ApplicationWindow::DimensionTool(void)
258 ClearUIToolStatesExcept(addDimensionAct);
259 SetInternalToolStates();
263 void ApplicationWindow::RotateTool(void)
265 ClearUIToolStatesExcept(rotateAct);
266 SetInternalToolStates();
270 void ApplicationWindow::MirrorTool(void)
272 ClearUIToolStatesExcept(mirrorAct);
273 SetInternalToolStates();
277 void ApplicationWindow::TrimTool(void)
279 ClearUIToolStatesExcept(trimAct);
280 SetInternalToolStates();
284 void ApplicationWindow::TriangulateTool(void)
286 ClearUIToolStatesExcept(triangulateAct);
287 SetInternalToolStates();
291 void ApplicationWindow::AddLineTool(void)
293 ClearUIToolStatesExcept(addLineAct);
294 SetInternalToolStates();
298 void ApplicationWindow::AddCircleTool(void)
300 ClearUIToolStatesExcept(addCircleAct);
301 SetInternalToolStates();
305 void ApplicationWindow::AddArcTool(void)
307 ClearUIToolStatesExcept(addArcAct);
308 SetInternalToolStates();
312 void ApplicationWindow::AddPolygonTool(void)
314 ClearUIToolStatesExcept(addPolygonAct);
315 SetInternalToolStates();
319 void ApplicationWindow::AddSplineTool(void)
321 ClearUIToolStatesExcept(addSplineAct);
322 SetInternalToolStates();
326 void ApplicationWindow::ZoomInTool(void)
328 double zoomFactor = 2.0;
330 We need to find the center of the screen, then figure out where the new corner
331 will be in the zoomed in window.
333 So we know in Qt coords, the center is found via:
334 size.width() / 2 --> xCenter
335 size.height() / 2 --> yCenter
337 transform x/yCenter to Cartesian coordinates. So far, so good.
339 when zooming in, new origin will be (xCenter - origin.x) / 2, (yCenter - origin.y) / 2
340 (after subtracting from center, that is...)
342 QSize size = drawing->size();
343 Vector center(size.width() / 2.0, size.height() / 2.0);
344 //printf("Zoom in... Center=%.2f,%.2f; ", center.x, center.y);
345 center = Painter::QtToCartesianCoords(center);
346 //printf("(%.2f,%.2f); origin=%.2f,%.2f; ", center.x, center.y, Painter::origin.x, Painter::origin.y);
347 Vector newOrigin = center - ((center - Global::origin) / zoomFactor);
348 //printf("newOrigin=%.2f,%.2f;\n", newOrigin.x, newOrigin.y);
349 Global::origin = newOrigin;
351 //printf("Zoom in... level going from %02f to ", Painter::zoom);
352 // This just zooms leaving origin intact... should zoom in at the current
354 Global::zoom *= zoomFactor;
355 Global::gridSpacing = drawing->gridPixels / Global::zoom;
356 drawing->UpdateGridBackground();
359 zoomIndicator->setText(QString("Grid: %1\", BU: Inch").arg(Global::gridSpacing));
360 baseUnitInput->setText(QString("%1").arg(Global::gridSpacing));
364 void ApplicationWindow::ZoomOutTool(void)
369 origin = (223, 160.5)
370 newOrigin should be (-10, -10)
373 center - origin = (213, 150.5)
374 origin - center = (-213, -150.5)
376 + center = (-10, -10)
379 double zoomFactor = 2.0;
380 QSize size = drawing->size();
381 Vector center(size.width() / 2.0, size.height() / 2.0);
382 //printf("Zoom out... Center=%.2f,%.2f; ", center.x, center.y);
383 center = Painter::QtToCartesianCoords(center);
384 //printf("(%.2f,%.2f); origin=%.2f,%.2f; ", center.x, center.y, Painter::origin.x, Painter::origin.y);
385 // Vector newOrigin = (center - Painter::origin) * zoomFactor;
386 // Vector newOrigin = center - (Painter::origin * zoomFactor);
387 Vector newOrigin = center + ((Global::origin - center) * zoomFactor);
388 //printf("newOrigin=%.2f,%.2f;\n", newOrigin.x, newOrigin.y);
389 Global::origin = newOrigin;
390 //printf("Zoom out...\n");
391 // This just zooms leaving origin intact... should zoom out at the current
393 Global::zoom /= zoomFactor;
394 Global::gridSpacing = drawing->gridPixels / Global::zoom;
395 drawing->UpdateGridBackground();
398 zoomIndicator->setText(QString("Grid: %1\", BU: Inch").arg(Global::gridSpacing));
399 baseUnitInput->setText(QString("%1").arg(Global::gridSpacing));
403 void ApplicationWindow::ClearUIToolStatesExcept(QAction * exception)
405 if (exception != addArcAct)
406 addArcAct->setChecked(false);
408 if (exception != addCircleAct)
409 addCircleAct->setChecked(false);
411 if (exception != addDimensionAct)
412 addDimensionAct->setChecked(false);
414 if (exception != addLineAct)
415 addLineAct->setChecked(false);
417 if (exception != addPolygonAct)
418 addPolygonAct->setChecked(false);
420 if (exception != addSplineAct)
421 addSplineAct->setChecked(false);
423 if (exception != deleteAct)
424 deleteAct->setChecked(false);
426 if (exception != rotateAct)
427 rotateAct->setChecked(false);
429 if (exception != mirrorAct)
430 mirrorAct->setChecked(false);
432 if (exception != trimAct)
433 trimAct->setChecked(false);
435 if (exception != triangulateAct)
436 triangulateAct->setChecked(false);
440 void ApplicationWindow::SetInternalToolStates(void)
442 Global::deleteActive = deleteAct->isChecked();
443 Global::dimensionActive = addDimensionAct->isChecked();
445 // We can be sure that if we've come here, then either an active tool is
446 // being deactivated, or a new tool is being created. In either case, the
447 // old tool needs to be deleted.
449 if (drawing->toolAction)
451 delete drawing->toolAction;
452 drawing->toolAction = NULL;
453 Global::ignoreClicks = false;
456 drawing->SetToolActive(addLineAct->isChecked() ? new DrawLineAction() : NULL);
457 drawing->SetToolActive(addCircleAct->isChecked() ? new DrawCircleAction() : NULL);
458 drawing->SetToolActive(addArcAct->isChecked() ? new DrawArcAction() : NULL);
459 drawing->SetToolActive(addDimensionAct->isChecked() ? new DrawDimensionAction() : NULL);
460 drawing->SetToolActive(addSplineAct->isChecked() ? new DrawSplineAction() : NULL);
461 drawing->SetToolActive(mirrorAct->isChecked() ? new MirrorAction() : NULL);
462 drawing->SetToolActive(rotateAct->isChecked() ? new RotateAction() : NULL);
463 drawing->SetToolActive(trimAct->isChecked() ? new TrimAction() : NULL);
464 drawing->SetToolActive(triangulateAct->isChecked() ? new TriangulateAction() : NULL);
466 if (drawing->toolAction)
467 Global::ignoreClicks = true;
474 void ApplicationWindow::HelpAbout(void)
480 void ApplicationWindow::Settings(void)
482 SettingsDialog dlg(this);
483 dlg.generalTab->antialiasChk->setChecked(drawing->useAntialiasing);
485 if (dlg.exec() == false)
488 // Deal with stuff here (since user hit "OK" button...)
489 drawing->useAntialiasing = dlg.generalTab->antialiasChk->isChecked();
495 // Group a bunch of selected objects (which can include other groups) together
496 // or ungroup a selected group.
498 void ApplicationWindow::HandleGrouping(void)
501 int itemsSelected = drawing->document.ItemsSelected();
503 // If nothing selected, do nothing
504 if (itemsSelected == 0)
506 statusBar()->showMessage(tr("No objects selected to make a group from."));
510 // If it's a group that's selected, ungroup it and leave the objects in a
512 if (itemsSelected == 1)
514 Object * object = drawing->document.SelectedItem(0);
518 printf("SelectedItem = NULL!\n");
520 printf("SelectedItem = %08X, type = %i\n", object, object->type);
523 if (object == NULL || object->type != OTContainer)
525 statusBar()->showMessage(tr("A group requires two or more selected objects."));
529 // Need the parent of the group, we're assuming here that the parent is
530 // the drawing's document. Does it matter? Maybe...
531 // Could just stipulate that grouping like this only takes place where the
532 // parent of the group is the drawing's document. Makes life much simpler.
533 ((Container *)object)->SelectAll();
534 ((Container *)object)->MoveContentsTo(&(drawing->document));
535 drawing->document.Delete(object);
536 statusBar()->showMessage(tr("Objects ungrouped."));
538 // Otherwise, if it's a group of 2 or more objects (which can be groups too)
539 // group them and select the group
540 else if (itemsSelected > 1)
542 Container * container = new Container(Vector(), &(drawing->document));
543 drawing->document.MoveSelectedContentsTo(container);
544 drawing->document.Add(container);
545 container->DeselectAll();
546 container->state = OSSelected;
547 statusBar()->showMessage(QString(tr("Grouped %1 objects.")).arg(itemsSelected));
555 void ApplicationWindow::HandleConnection(void)
558 //double tt = Geometry::ParameterOfLineAndPoint(Vector(0, 0), Vector(10, 0), Vector(8, 2));
559 //printf("Parameter of point @ (8,2) of line (0,0), (10,0): %lf\n", tt);
560 int itemsSelected = drawing->document.ItemsSelected();
562 // If nothing selected, do nothing
563 if (itemsSelected == 0)
565 statusBar()->showMessage(tr("No objects selected to connect."));
569 // If one thing selected, do nothing
570 if (itemsSelected == 1)
572 statusBar()->showMessage(tr("Nothing to connect object to."));
576 // This is O(n^2 / 2) :-P
577 for(int i=0; i<itemsSelected; i++)
579 Object * obj1 = drawing->document.SelectedItem(i);
581 for(int j=i+1; j<itemsSelected; j++)
583 Object * obj2 = drawing->document.SelectedItem(j);
586 // if ((obj1->type != OTLine) || (obj2->type != OTLine))
589 if ((obj1->type == OTLine) && (obj2->type == OTLine))
591 //printf("Testing objects for intersection (%X, %X)...\n", obj1, obj2);
592 int intersects = Geometry::Intersects((Line *)obj1, (Line *)obj2, &t, &u);
593 //printf(" (%s) --> t=%lf, u=%lf\n", (intersects ? "true" : "FALSE"), t, u);
597 //printf("Connecting objects (%X, %X)...\n", obj1, obj2);
598 obj1->Connect(obj2, u);
599 obj2->Connect(obj1, t);
602 else if (((obj1->type == OTLine) && (obj2->type == OTDimension))
603 || ((obj2->type == OTLine) && (obj1->type == OTDimension)))
605 printf("Testing Line<->Dimension intersection...\n");
606 Line * line = (Line *)(obj1->type == OTLine ? obj1 : obj2);
607 Dimension * dim = (Dimension *)(obj1->type == OTDimension ? obj1 : obj2);
609 int intersects = Geometry::Intersects(line, dim, &t, &u);
610 printf(" -> intersects = %i, t=%lf, u=%lf\n", intersects, t, u);
614 obj1->Connect(obj2, u);
615 obj2->Connect(obj1, t);
624 void ApplicationWindow::HandleDisconnection(void)
629 void ApplicationWindow::HandleGridSizeInPixels(int size)
631 drawing->SetGridSize(size);
636 void ApplicationWindow::HandleGridSizeInBaseUnits(QString text)
640 double value = text.toDouble(&ok);
642 // Nothing parsable to a double, so quit...
643 if (!ok || value == 0)
646 // drawing->gridSpacing = value;
647 // Painter::zoom = drawing->gridPixels / drawing->gridSpacing;
648 Global::gridSpacing = value;
649 Global::zoom = drawing->gridPixels / Global::gridSpacing;
650 drawing->UpdateGridBackground();
655 void ApplicationWindow::HandleDimensionSize(QString text)
659 double value = text.toDouble(&ok);
661 // Nothing parsable to a double, so quit...
662 if (!ok || value == 0)
665 // drawing->document.ResizeAllDimensions(value);
670 void ApplicationWindow::CreateActions(void)
672 exitAct = CreateAction(tr("&Quit"), tr("Quit"), tr("Exits the application."),
673 QIcon(":/res/quit.png"), QKeySequence(tr("Ctrl+q")));
674 connect(exitAct, SIGNAL(triggered()), this, SLOT(close()));
676 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);
677 connect(snapToGridAct, SIGNAL(triggered()), this, SLOT(SnapToGridTool()));
679 fixAngleAct = CreateAction(tr("Fix &Angle"), tr("Fix Angle"), tr("Fixes the angle of an object."),
680 QIcon(":/res/fix-angle.png"), QKeySequence(tr("F,A")), true);
681 connect(fixAngleAct, SIGNAL(triggered()), this, SLOT(FixAngle()));
683 fixLengthAct = CreateAction(tr("Fix &Length"), tr("Fix Length"), tr("Fixes the length of an object."),
684 QIcon(":/res/fix-length.png"), QKeySequence(tr("F,L")), true);
685 connect(fixLengthAct, SIGNAL(triggered()), this, SLOT(FixLength()));
687 deleteAct = CreateAction(tr("&Delete"), tr("Delete Object"), tr("Deletes selected objects."), QIcon(":/res/delete-tool.png"), QKeySequence(tr("Delete")), true);
688 connect(deleteAct, SIGNAL(triggered()), this, SLOT(DeleteTool()));
690 addDimensionAct = CreateAction(tr("Add &Dimension"), tr("Add Dimension"), tr("Adds a dimension to the drawing."), QIcon(":/res/dimension-tool.png"), QKeySequence("D,I"), true);
691 connect(addDimensionAct, SIGNAL(triggered()), this, SLOT(DimensionTool()));
693 addLineAct = CreateAction(tr("Add &Line"), tr("Add Line"), tr("Adds lines to the drawing."), QIcon(":/res/add-line-tool.png"), QKeySequence("A,L"), true);
694 connect(addLineAct, SIGNAL(triggered()), this, SLOT(AddLineTool()));
696 addCircleAct = CreateAction(tr("Add &Circle"), tr("Add Circle"), tr("Adds circles to the drawing."), QIcon(":/res/add-circle-tool.png"), QKeySequence("A,C"), true);
697 connect(addCircleAct, SIGNAL(triggered()), this, SLOT(AddCircleTool()));
699 addArcAct = CreateAction(tr("Add &Arc"), tr("Add Arc"), tr("Adds arcs to the drawing."), QIcon(":/res/add-arc-tool.png"), QKeySequence("A,A"), true);
700 connect(addArcAct, SIGNAL(triggered()), this, SLOT(AddArcTool()));
702 addPolygonAct = CreateAction(tr("Add &Polygon"), tr("Add Polygon"), tr("Add polygons to the drawing."), QIcon(":/res/add-polygon-tool.png"), QKeySequence("A,P"), true);
703 connect(addPolygonAct, SIGNAL(triggered()), this, SLOT(AddPolygonTool()));
705 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);
706 connect(addSplineAct, SIGNAL(triggered()), this, SLOT(AddSplineTool()));
708 aboutAct = CreateAction(tr("About &Architektonas"), tr("About Architektonas"), tr("Gives information about this program."), QIcon(":/res/generic-tool.png"), QKeySequence());
709 connect(aboutAct, SIGNAL(triggered()), this, SLOT(HelpAbout()));
711 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);
712 connect(rotateAct, SIGNAL(triggered()), this, SLOT(RotateTool()));
714 zoomInAct = CreateAction(tr("Zoom &In"), tr("Zoom In"), tr("Zoom in on the document."), QIcon(":/res/zoom-in.png"), QKeySequence(tr("+")), QKeySequence(tr("=")));
715 connect(zoomInAct, SIGNAL(triggered()), this, SLOT(ZoomInTool()));
717 zoomOutAct = CreateAction(tr("Zoom &Out"), tr("Zoom Out"), tr("Zoom out of the document."), QIcon(":/res/zoom-out.png"), QKeySequence(tr("-")));
718 connect(zoomOutAct, SIGNAL(triggered()), this, SLOT(ZoomOutTool()));
720 fileNewAct = CreateAction(tr("&New Drawing"), tr("New Drawing"), tr("Creates a new drawing."), QIcon(":/res/file-new.png"), QKeySequence(tr("Ctrl+n")));
721 connect(fileNewAct, SIGNAL(triggered()), this, SLOT(FileNew()));
723 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")));
724 connect(fileOpenAct, SIGNAL(triggered()), this, SLOT(FileOpen()));
726 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")));
727 connect(fileSaveAct, SIGNAL(triggered()), this, SLOT(FileSave()));
729 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")));
730 connect(fileSaveAsAct, SIGNAL(triggered()), this, SLOT(FileSaveAs()));
732 fileCloseAct = CreateAction(tr("&Close Drawing"), tr("Close Drawing"), tr("Closes the current drawing."), QIcon(":/res/file-close.png"), QKeySequence(tr("Ctrl+w")));
734 settingsAct = CreateAction(tr("&Settings"), tr("Settings"), tr("Change certain defaults for Architektonas."), QIcon(":/res/settings.png"), QKeySequence());
735 connect(settingsAct, SIGNAL(triggered()), this, SLOT(Settings()));
737 groupAct = CreateAction(tr("&Group"), tr("Group"), tr("Group/ungroup selected objects."), QIcon(":/res/group-tool.png"), QKeySequence("g"));
738 connect(groupAct, SIGNAL(triggered()), this, SLOT(HandleGrouping()));
740 connectAct = CreateAction(tr("&Connect"), tr("Connect"), tr("Connect objects at point."), QIcon(":/res/connect-tool.png"), QKeySequence("c,c"));
741 connect(connectAct, SIGNAL(triggered()), this, SLOT(HandleConnection()));
743 disconnectAct = CreateAction(tr("&Disconnect"), tr("Disconnect"), tr("Disconnect objects joined at point."), QIcon(":/res/disconnect-tool.png"), QKeySequence("d,d"));
744 connect(disconnectAct, SIGNAL(triggered()), this, SLOT(HandleDisconnection()));
746 mirrorAct = CreateAction(tr("&Mirror"), tr("Mirror"), tr("Mirror selected objects around a line."), QIcon(":/res/mirror-tool.png"), QKeySequence("m,i"), true);
747 connect(mirrorAct, SIGNAL(triggered()), this, SLOT(MirrorTool()));
749 trimAct = CreateAction(tr("&Trim"), tr("Trim"), tr("Trim extraneous lines from selected objects."), QIcon(":/res/trim-tool.png"), QKeySequence("t,r"), true);
750 connect(trimAct, SIGNAL(triggered()), this, SLOT(TrimTool()));
752 triangulateAct = CreateAction(tr("&Triangulate"), tr("Triangulate"), tr("Make triangles from selected lines, preserving their lengths."), QIcon(":/res/triangulate-tool.png"), QKeySequence("t,g"), true);
753 connect(triangulateAct, SIGNAL(triggered()), this, SLOT(TriangulateTool()));
756 //Hm. I think we'll have to have separate logic to do the "Radio Group Toolbar" thing...
757 // Yup, in order to turn them off, we'd have to have an "OFF" toolbar button. Ick.
758 /* QActionGroup * group = new QActionGroup(this);
759 group->addAction(deleteAct);
760 group->addAction(addDimensionAct);
761 group->addAction(addLineAct);
762 group->addAction(addCircleAct);
763 group->addAction(addArcAct);//*/
768 // Consolidates action creation from a multi-step process to a single-step one.
770 QAction * ApplicationWindow::CreateAction(QString name, QString tooltip, QString statustip,
771 QIcon icon, QKeySequence key, bool checkable/*= false*/)
773 QAction * action = new QAction(icon, name, this);
774 action->setToolTip(tooltip);
775 action->setStatusTip(statustip);
776 action->setShortcut(key);
777 action->setCheckable(checkable);
784 // This is essentially the same as the previous function, but this allows more
785 // than one key sequence to be added as key shortcuts.
787 QAction * ApplicationWindow::CreateAction(QString name, QString tooltip, QString statustip,
788 QIcon icon, QKeySequence key1, QKeySequence key2, bool checkable/*= false*/)
790 QAction * action = new QAction(icon, name, this);
791 action->setToolTip(tooltip);
792 action->setStatusTip(statustip);
793 QList<QKeySequence> keyList;
794 keyList.append(key1);
795 keyList.append(key2);
796 action->setShortcuts(keyList);
797 action->setCheckable(checkable);
803 void ApplicationWindow::CreateMenus(void)
805 QMenu * menu = menuBar()->addMenu(tr("&File"));
806 menu->addAction(fileNewAct);
807 menu->addAction(fileOpenAct);
808 menu->addAction(fileSaveAct);
809 menu->addAction(fileSaveAsAct);
810 menu->addAction(fileCloseAct);
811 menu->addSeparator();
812 menu->addAction(exitAct);
814 menu = menuBar()->addMenu(tr("&View"));
815 menu->addAction(zoomInAct);
816 menu->addAction(zoomOutAct);
818 menu = menuBar()->addMenu(tr("&Edit"));
819 menu->addAction(snapToGridAct);
820 menu->addAction(groupAct);
821 menu->addAction(fixAngleAct);
822 menu->addAction(fixLengthAct);
823 menu->addAction(rotateAct);
824 menu->addAction(mirrorAct);
825 menu->addAction(trimAct);
826 menu->addAction(triangulateAct);
827 menu->addAction(connectAct);
828 menu->addAction(disconnectAct);
829 menu->addSeparator();
830 menu->addAction(deleteAct);
831 menu->addSeparator();
832 menu->addAction(addLineAct);
833 menu->addAction(addCircleAct);
834 menu->addAction(addArcAct);
835 menu->addAction(addPolygonAct);
836 menu->addAction(addSplineAct);
837 menu->addAction(addDimensionAct);
838 menu->addSeparator();
839 menu->addAction(settingsAct);
841 menu = menuBar()->addMenu(tr("&Help"));
842 menu->addAction(aboutAct);
846 void ApplicationWindow::CreateToolbars(void)
848 QToolBar * toolbar = addToolBar(tr("File"));
849 toolbar->setObjectName("File"); // Needed for saveState()
850 toolbar->addAction(fileNewAct);
851 toolbar->addAction(fileOpenAct);
852 toolbar->addAction(fileSaveAct);
853 toolbar->addAction(fileSaveAsAct);
854 toolbar->addAction(fileCloseAct);
855 // toolbar->addAction(exitAct);
857 toolbar = addToolBar(tr("View"));
858 toolbar->setObjectName("View");
859 toolbar->addAction(zoomInAct);
860 toolbar->addAction(zoomOutAct);
862 QSpinBox * spinbox = new QSpinBox;
863 toolbar->addWidget(spinbox);
864 // QLineEdit * lineedit = new QLineEdit;
865 toolbar->addWidget(baseUnitInput);
866 toolbar->addWidget(dimensionSizeInput);
868 toolbar = addToolBar(tr("Edit"));
869 toolbar->setObjectName("Edit");
870 toolbar->addAction(snapToGridAct);
871 toolbar->addAction(groupAct);
872 toolbar->addAction(fixAngleAct);
873 toolbar->addAction(fixLengthAct);
874 toolbar->addAction(rotateAct);
875 toolbar->addAction(mirrorAct);
876 toolbar->addAction(trimAct);
877 toolbar->addAction(triangulateAct);
878 toolbar->addAction(deleteAct);
879 toolbar->addAction(connectAct);
880 toolbar->addAction(disconnectAct);
881 toolbar->addSeparator();
882 toolbar->addAction(addLineAct);
883 toolbar->addAction(addCircleAct);
884 toolbar->addAction(addArcAct);
885 toolbar->addAction(addPolygonAct);
886 toolbar->addAction(addSplineAct);
887 toolbar->addAction(addDimensionAct);
889 spinbox->setRange(4, 256);
890 spinbox->setValue(12);
891 baseUnitInput->setText("12");
892 connect(spinbox, SIGNAL(valueChanged(int)), this, SLOT(HandleGridSizeInPixels(int)));
893 connect(baseUnitInput, SIGNAL(textChanged(QString)), this, SLOT(HandleGridSizeInBaseUnits(QString)));
894 connect(dimensionSizeInput, SIGNAL(textChanged(QString)), this, SLOT(HandleDimensionSize(QString)));
898 void ApplicationWindow::ReadSettings(void)
900 QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint();
901 QSize size = settings.value("size", QSize(400, 400)).toSize();
902 drawing->useAntialiasing = settings.value("useAntialiasing", true).toBool();
903 snapToGridAct->setChecked(settings.value("snapToGrid", true).toBool());
906 restoreState(settings.value("windowState").toByteArray());
910 void ApplicationWindow::WriteSettings(void)
912 settings.setValue("pos", pos());
913 settings.setValue("size", size());
914 settings.setValue("windowState", saveState());
915 settings.setValue("useAntialiasing", drawing->useAntialiasing);
916 settings.setValue("snapToGrid", snapToGridAct->isChecked());