]> Shamusworld >> Repos - architektonas/blobdiff - src/applicationwindow.cpp
Added object pane, grouping, load/save functionality.
[architektonas] / src / applicationwindow.cpp
index ef42ca24a6acc9fc2c00089965f7855ff5e13852..a4c39b74fe96eace7ee7376055641138c6c49d1e 100644 (file)
@@ -8,7 +8,7 @@
 // JLH = James Hammons <jlhamm@acm.org>
 //
 // Who  When        What
-// ---  ----------  -------------------------------------------------------------
+// ---  ----------  ------------------------------------------------------------
 // JLH  03/22/2011  Created this file
 // JLH  09/29/2011  Added simple zoom in/out functionality
 // JLH  10/03/2011  Fixed zoom tool to zoom in/out from center of screen
 #include "drawingview.h"
 #include "fileio.h"
 #include "generaltab.h"
+#include "global.h"
 #include "layerwidget.h"
+#include "objectwidget.h"
 #include "painter.h"
 #include "settingsdialog.h"
+#include "structs.h"
+#include "utils.h"
 
 
-ApplicationWindow::ApplicationWindow(): settings("Underground Software", "Architektonas")
+// Class variables
+DrawingView * ApplicationWindow::drawing;
+
+
+ApplicationWindow::ApplicationWindow():
+       baseUnitInput(new QLineEdit),
+       dimensionSizeInput(new QLineEdit),
+       settings("Underground Software", "Architektonas")
 {
        drawing = new DrawingView(this);
        drawing->setMouseTracking(true);                // We want *all* mouse events...!
+       drawing->setFocusPolicy(Qt::StrongFocus);
        setCentralWidget(drawing);
 
        aboutWin = new AboutWindow(this);
@@ -61,33 +73,48 @@ ApplicationWindow::ApplicationWindow(): settings("Underground Software", "Archit
        dock1->setWidget(lw);
        addDockWidget(Qt::RightDockWidgetArea, dock1);
        QDockWidget * dock2 = new QDockWidget(tr("Blocks"), this);
-//     BlockWidget * bw = new BlockWidget;
-//     dock2->setWidget(bw);
+       BlockWidget * bw = new BlockWidget;
+       dock2->setWidget(bw);
        addDockWidget(Qt::RightDockWidgetArea, dock2);
-
-       //      Create status bar
-       zoomIndicator = new QLabel("Grid: 12.0\" Zoom: 12.5%");
+       QDockWidget * dock3 = new QDockWidget(tr("Object"), this);
+       ObjectWidget * ow = new ObjectWidget;
+       dock3->setWidget(ow);
+       addDockWidget(Qt::RightDockWidgetArea, dock3);
+       // Needed for saveState()
+       dock1->setObjectName("Layers");
+       dock2->setObjectName("Blocks");
+       dock3->setObjectName("Object");
+
+       // Create status bar
+       zoomIndicator = new QLabel("Grid: 12.0\" BU: Inch");
        statusBar()->addPermanentWidget(zoomIndicator);
        statusBar()->showMessage(tr("Ready"));
 
        ReadSettings();
        setUnifiedTitleAndToolBarOnMac(true);
-       Object::SetFont(new QFont("Verdana", 15, QFont::Bold));
+       Global::font =  new QFont("Verdana", 15, QFont::Bold);
+
+       connect(lw, SIGNAL(LayerDeleted(int)), drawing, SLOT(DeleteCurrentLayer(int)));
+       connect(lw, SIGNAL(LayerToggled()), drawing, SLOT(HandleLayerToggle()));
+       connect(lw, SIGNAL(LayersSwapped(int, int)), drawing, SLOT(HandleLayerSwap(int, int)));
+
+       connect(drawing, SIGNAL(ObjectHovered(Object *)), ow, SLOT(ShowInfo(Object *)));
 }
 
 
 void ApplicationWindow::closeEvent(QCloseEvent * event)
 {
        WriteSettings();
-       event->accept();                                                        // Use ignore() if can't close for some reason
+       event->accept();                // Use ignore() if can't close for some reason
        //Do we have a memory leak here if we don't delete the font in the Object???
 }
 
 
 void ApplicationWindow::FileNew(void)
 {
-       // Should warn the user if drawing hasn't been saved...
-       drawing->document.Clear();
+       // Should warn the user if drawing hasn't been saved... !!! FIX !!!
+       DeleteContents(drawing->document.objects);
+       drawing->document.objects.clear();
        drawing->update();
        documentName.clear();
        setWindowTitle("Architektonas - Untitled");
@@ -99,7 +126,12 @@ void ApplicationWindow::FileOpen(void)
 {
        QString filename = QFileDialog::getOpenFileName(this, tr("Open Drawing"),
                "", tr("Architektonas files (*.drawing)"));
-       FILE * file = fopen(filename.toAscii().data(), "r");
+
+       // User cancelled open
+       if (filename.isEmpty())
+               return;
+
+       FILE * file = fopen(filename.toUtf8().data(), "r");
 
        if (file == 0)
        {
@@ -110,7 +142,7 @@ void ApplicationWindow::FileOpen(void)
                return;
        }
 
-       Container container(Vector(0, 0));
+       Container container;
        bool successful = FileIO::LoadAtnsFile(file, &container);
        fclose(file);
 
@@ -120,10 +152,16 @@ void ApplicationWindow::FileOpen(void)
                msg.setText(QString(tr("Could not load file \"%1\"!")).arg(filename));
                msg.setIcon(QMessageBox::Critical);
                msg.exec();
+               // Make sure to delete any hanging objects in the container...
+               DeleteContents(container.objects);
                return;
        }
 
-printf("FileOpen: container size = %li\n", container.objects.size());
+//printf("FileOpen: container size = %li\n", container.objects.size());
+       // Keep memory leaks from happening by getting rid of the old document
+       DeleteContents(drawing->document.objects);
+       // We can do this because the vector is just a bunch of pointers to our
+       // Objects, and the Containers (non-empty) can be moved way with no problem.
        drawing->document = container;
        drawing->update();
        documentName = filename;
@@ -138,7 +176,7 @@ void ApplicationWindow::FileSave(void)
                documentName = QFileDialog::getSaveFileName(this, tr("Save Drawing"),
                        "", tr("Architektonas drawings (*.drawing)"));
 
-       FILE * file = fopen(documentName.toAscii().data(), "w");
+       FILE * file = fopen(documentName.toUtf8().data(), "w");
 
        if (file == 0)
        {
@@ -158,8 +196,10 @@ void ApplicationWindow::FileSave(void)
                msg.setText(QString(tr("Could not save file \"%1\"!")).arg(documentName));
                msg.setIcon(QMessageBox::Critical);
                msg.exec();
-               // In this case, we should unlink the created file, since it's not right...
-               unlink(documentName.toAscii().data());
+               // In this case, we should unlink the created file, since it's not
+               // right...
+//             unlink(documentName.toUtf8().data());
+               QFile::remove(documentName);
                return;
        }
 
@@ -183,30 +223,30 @@ void ApplicationWindow::FileSaveAs(void)
 
 void ApplicationWindow::SnapToGridTool(void)
 {
-       Object::SetSnapMode(snapToGridAct->isChecked());
+       Global::snapToGrid = snapToGridAct->isChecked();
 }
 
 
 void ApplicationWindow::FixAngle(void)
 {
-       Object::SetFixedAngle(fixAngleAct->isChecked());
+       Global::fixedAngle = fixAngleAct->isChecked();
 }
 
 
 void ApplicationWindow::FixLength(void)
 {
-       Object::SetFixedLength(fixLengthAct->isChecked());
+       Global::fixedLength = fixLengthAct->isChecked();
 }
 
 
-// We want certain tools to be exclusive, and this approach isn't working correctly...
 void ApplicationWindow::DeleteTool(void)
 {
        // For this tool, we check first to see if anything is selected. If so, we
        // delete those and *don't* select the delete tool.
-       if (drawing->document.ItemsSelected() > 0)
+//     if (drawing->numSelected > 0)
+       if (drawing->select.size() > 0)
        {
-               drawing->document.DeleteSelectedItems();
+               DeleteSelectedObjects(drawing->document.objects);
                drawing->update();
                deleteAct->setChecked(false);
                return;
@@ -228,6 +268,37 @@ void ApplicationWindow::DimensionTool(void)
 void ApplicationWindow::RotateTool(void)
 {
        ClearUIToolStatesExcept(rotateAct);
+
+       // Do tear-down if Rotate tool has been turned off
+       if (!rotateAct->isChecked())
+               drawing->RotateHandler(ToolCleanup, Point(0, 0));
+
+       SetInternalToolStates();
+}
+
+
+void ApplicationWindow::MirrorTool(void)
+{
+       ClearUIToolStatesExcept(mirrorAct);
+
+       // Do tear-down if Rotate tool has been turned off
+       if (!mirrorAct->isChecked())
+               drawing->MirrorHandler(ToolCleanup, Point(0, 0));
+
+       SetInternalToolStates();
+}
+
+
+void ApplicationWindow::TrimTool(void)
+{
+       ClearUIToolStatesExcept(trimAct);
+       SetInternalToolStates();
+}
+
+
+void ApplicationWindow::TriangulateTool(void)
+{
+       ClearUIToolStatesExcept(triangulateAct);
        SetInternalToolStates();
 }
 
@@ -260,6 +331,13 @@ void ApplicationWindow::AddPolygonTool(void)
 }
 
 
+void ApplicationWindow::AddSplineTool(void)
+{
+       ClearUIToolStatesExcept(addSplineAct);
+       SetInternalToolStates();
+}
+
+
 void ApplicationWindow::ZoomInTool(void)
 {
        double zoomFactor = 2.0;
@@ -281,17 +359,20 @@ when zooming in, new origin will be (xCenter - origin.x) / 2, (yCenter - origin.
 //printf("Zoom in... Center=%.2f,%.2f; ", center.x, center.y);
        center = Painter::QtToCartesianCoords(center);
 //printf("(%.2f,%.2f); origin=%.2f,%.2f; ", center.x, center.y, Painter::origin.x, Painter::origin.y);
-       Vector newOrigin = center - ((center - Painter::origin) / zoomFactor);
+       Vector newOrigin = center - ((center - Global::origin) / zoomFactor);
 //printf("newOrigin=%.2f,%.2f;\n", newOrigin.x, newOrigin.y);
-       Painter::origin = newOrigin;
+       Global::origin = newOrigin;
 
 //printf("Zoom in... level going from %02f to ", Painter::zoom);
-       // This just zooms leaving origin intact... should zoom in at the current center! [DONE]
-       Painter::zoom *= zoomFactor;
-       drawing->gridSpacing = 12.0 / Painter::zoom;
-       zoomIndicator->setText(QString("Grid: %2\" Zoom: %1%").arg(Painter::zoom * 100.0 * SCREEN_ZOOM).arg(drawing->gridSpacing));
+       // This just zooms leaving origin intact... should zoom in at the current
+       // center! [DONE]
+       Global::zoom *= zoomFactor;
+       Global::gridSpacing = drawing->gridPixels / Global::zoom;
        drawing->UpdateGridBackground();
        drawing->update();
+
+       zoomIndicator->setText(QString("Grid: %1\", BU: Inch").arg(Global::gridSpacing));
+       baseUnitInput->setText(QString("%1").arg(Global::gridSpacing));
 }
 
 
@@ -318,62 +399,70 @@ x 2 = (-426, -301)
 //printf("(%.2f,%.2f); origin=%.2f,%.2f; ", center.x, center.y, Painter::origin.x, Painter::origin.y);
 //     Vector newOrigin = (center - Painter::origin) * zoomFactor;
 //     Vector newOrigin = center - (Painter::origin * zoomFactor);
-       Vector newOrigin = center + ((Painter::origin - center) * zoomFactor);
+       Vector newOrigin = center + ((Global::origin - center) * zoomFactor);
 //printf("newOrigin=%.2f,%.2f;\n", newOrigin.x, newOrigin.y);
-       Painter::origin = newOrigin;
+       Global::origin = newOrigin;
 //printf("Zoom out...\n");
-       // This just zooms leaving origin intact... should zoom out at the current center! [DONE]
-       Painter::zoom /= zoomFactor;
-       drawing->gridSpacing = 12.0 / Painter::zoom;
-       zoomIndicator->setText(QString("Grid: %2\" Zoom: %1%").arg(Painter::zoom * 100.0 * SCREEN_ZOOM).arg(drawing->gridSpacing));
+       // This just zooms leaving origin intact... should zoom out at the current
+       // center! [DONE]
+       Global::zoom /= zoomFactor;
+       Global::gridSpacing = drawing->gridPixels / Global::zoom;
        drawing->UpdateGridBackground();
        drawing->update();
+
+       zoomIndicator->setText(QString("Grid: %1\", BU: Inch").arg(Global::gridSpacing));
+       baseUnitInput->setText(QString("%1").arg(Global::gridSpacing));
 }
 
 
 void ApplicationWindow::ClearUIToolStatesExcept(QAction * exception)
 {
-       if (exception != addArcAct)
-               addArcAct->setChecked(false);
-
-       if (exception != addCircleAct)
-               addCircleAct->setChecked(false);
-
-       if (exception != addDimensionAct)
-               addDimensionAct->setChecked(false);
-
-       if (exception != addLineAct)
-               addLineAct->setChecked(false);
-
-       if (exception != addPolygonAct)
-               addPolygonAct->setChecked(false);
+       QAction * actionList[] = {
+               addArcAct, addLineAct, addCircleAct, addDimensionAct, addPolygonAct,
+               addSplineAct, deleteAct, rotateAct, mirrorAct, trimAct, triangulateAct, 0
+       };
 
-       if (exception != deleteAct)
-               deleteAct->setChecked(false);
-
-       if (exception != rotateAct)
-               rotateAct->setChecked(false);
+       for(int i=0; actionList[i]!=0; i++)
+       {
+               if (actionList[i] != exception)
+                       actionList[i]->setChecked(false);
+       }
 }
 
 
 void ApplicationWindow::SetInternalToolStates(void)
 {
-       Object::SetDeleteActive(deleteAct->isChecked());
-       Object::SetDimensionActive(addDimensionAct->isChecked());
-       drawing->SetRotateToolActive(rotateAct->isChecked());
-
        // We can be sure that if we've come here, then either an active tool is
        // being deactivated, or a new tool is being created. In either case, the
        // old tool needs to be deleted.
-       if (drawing->toolAction)
-       {
-               delete drawing->toolAction;
-               drawing->toolAction = NULL;
-       }
+       Global::toolState = TSNone;
+
+       if (addLineAct->isChecked())
+               Global::tool = TTLine;
+       else if (addCircleAct->isChecked())
+               Global::tool = TTCircle;
+       else if (addArcAct->isChecked())
+               Global::tool = TTArc;
+       else if (addDimensionAct->isChecked())
+               Global::tool = TTDimension;
+       else if (addSplineAct->isChecked())
+               Global::tool = TTSpline;
+       else if (addPolygonAct->isChecked())
+               Global::tool = TTPolygon;
+       else if (deleteAct->isChecked())
+               Global::tool = TTDelete;
+       else if (mirrorAct->isChecked())
+               Global::tool = TTMirror;
+       else if (rotateAct->isChecked())
+               Global::tool = TTRotate;
+       else if (trimAct->isChecked())
+               Global::tool = TTTrim;
+       else if (triangulateAct->isChecked())
+               Global::tool = TTTriangulate;
+       else
+               Global::tool = TTNone;
 
-       drawing->SetAddLineToolActive(addLineAct->isChecked());
-       drawing->SetAddCircleToolActive(addCircleAct->isChecked());
-       drawing->SetAddDimensionToolActive(addDimensionAct->isChecked());
+       drawing->update();
 }
 
 
@@ -403,6 +492,7 @@ void ApplicationWindow::Settings(void)
 //
 void ApplicationWindow::HandleGrouping(void)
 {
+#if 0
        int itemsSelected = drawing->document.ItemsSelected();
 
        // If nothing selected, do nothing
@@ -433,8 +523,9 @@ else
 
                // Need the parent of the group, we're assuming here that the parent is
                // the drawing's document. Does it matter? Maybe...
-               // Could just stipulate that grouping like this only takes place where the
-               // parent of the group is the drawing's document. Makes life much simpler.
+               // Could just stipulate that grouping like this only takes place where
+               // the parent of the group is the drawing's document. Makes life much
+               // simpler.
                ((Container *)object)->SelectAll();
                ((Container *)object)->MoveContentsTo(&(drawing->document));
                drawing->document.Delete(object);
@@ -451,11 +542,193 @@ else
                container->state = OSSelected;
                statusBar()->showMessage(QString(tr("Grouped %1 objects.")).arg(itemsSelected));
        }
+#else
+       int numSelected = drawing->select.size();
+
+       // If nothing selected, do nothing
+       if (numSelected == 0)
+       {
+               statusBar()->showMessage(tr("No objects selected to make a group from."));
+               return;
+       }
+
+       // If it's a group that's selected, ungroup it and leave the objects in a
+       // selected state
+       if (numSelected == 1)
+       {
+               Object * obj = (Object *)drawing->select[0];
+
+               if (obj->type != OTContainer)
+               {
+                       statusBar()->showMessage(tr("A group requires two or more selected objects."));
+                       return;
+               }
+
+               // Need the parent of the group, we're assuming here that the parent is
+               // the drawing's document. Does it matter? Maybe...
+               // Could just stipulate that grouping like this only takes place where
+               // the parent of the group is the drawing's document. Makes life much
+               // simpler.
+//             ((Container *)object)->SelectAll();
+//             ((Container *)object)->MoveContentsTo(&(drawing->document));
+//             drawing->document.Delete(object);
+               Container * c = (Container *)obj;
+//printf("Ungroup: container size = %li\n", c->objects.size());
+               SelectAll(c->objects);
+//printf("Ungroup: document size = %li (pre-AddObjectsTo)\n", drawing->document.objects.size());
+               RemoveSelectedObjects(drawing->document.objects);
+               AddObjectsTo(drawing->document.objects, c->objects);
+               drawing->select.clear();
+               AddObjectsTo(drawing->select, c->objects);
+               delete c;
+               statusBar()->showMessage(tr("Objects ungrouped."));
+//printf("Ungroup: document size = %li\n", drawing->document.objects.size());
+       }
+       // Otherwise, if it's a group of 2 or more objects (which can be groups too)
+       // group them and select the group
+       else if (numSelected > 1)
+       {
+//             Container * container = new Container(Vector(), &(drawing->document));
+//             drawing->document.MoveSelectedContentsTo(container);
+//             drawing->document.Add(container);
+//             container->DeselectAll();
+//             container->state = OSSelected;
+
+               Container * c = new Container();
+//             AddObjectsTo(c->objects, drawing->select);
+//             RemoveSelectedObjects(drawing->document.objects);
+               MoveSelectedObjectsTo(c->objects, drawing->document.objects);
+               drawing->document.objects.push_back(c);
+               ClearSelected(c->objects);
+               c->selected = true;
+               c->layer = Global::currentLayer;
+               drawing->select.clear();
+               drawing->select.push_back(c);
+               statusBar()->showMessage(QString(tr("Grouped %1 objects.")).arg(numSelected));
+//printf("Group: document size = %li\n", drawing->document.objects.size());
+       }
+#endif
 
        drawing->update();
 }
 
 
+void ApplicationWindow::HandleConnection(void)
+{
+#if 0
+//double tt = Geometry::ParameterOfLineAndPoint(Vector(0, 0), Vector(10, 0), Vector(8, 2));
+//printf("Parameter of point @ (8,2) of line (0,0), (10,0): %lf\n", tt);
+       int itemsSelected = drawing->document.ItemsSelected();
+
+       // If nothing selected, do nothing
+       if (itemsSelected == 0)
+       {
+               statusBar()->showMessage(tr("No objects selected to connect."));
+               return;
+       }
+
+       // If one thing selected, do nothing
+       if (itemsSelected == 1)
+       {
+               statusBar()->showMessage(tr("Nothing to connect object to."));
+               return;
+       }
+
+       // This is O(n^2 / 2) :-P
+       for(int i=0; i<itemsSelected; i++)
+       {
+               Object * obj1 = drawing->document.SelectedItem(i);
+
+               for(int j=i+1; j<itemsSelected; j++)
+               {
+                       Object * obj2 = drawing->document.SelectedItem(j);
+                       double t, u, v, w;
+
+//                     if ((obj1->type != OTLine) || (obj2->type != OTLine))
+//                             continue;
+
+                       if ((obj1->type == OTLine) && (obj2->type == OTLine))
+                       {
+//printf("Testing objects for intersection (%X, %X)...\n", obj1, obj2);
+                               int intersects = Geometry::Intersects((Line *)obj1, (Line *)obj2, &t, &u);
+//printf("  (%s) --> t=%lf, u=%lf\n", (intersects ? "true" : "FALSE"), t, u);
+
+                               if (intersects)
+                               {
+       //printf("Connecting objects (%X, %X)...\n", obj1, obj2);
+                                       obj1->Connect(obj2, u);
+                                       obj2->Connect(obj1, t);
+                               }
+                       }
+                       else if (((obj1->type == OTLine) && (obj2->type == OTDimension))
+                               || ((obj2->type == OTLine) && (obj1->type == OTDimension)))
+                       {
+printf("Testing Line<->Dimension intersection...\n");
+                               Line * line = (Line *)(obj1->type == OTLine ? obj1 : obj2);
+                               Dimension * dim = (Dimension *)(obj1->type == OTDimension ? obj1 : obj2);
+
+                               int intersects = Geometry::Intersects(line, dim, &t, &u);
+printf("   -> intersects = %i, t=%lf, u=%lf\n", intersects, t, u);
+
+                               if (intersects)
+                               {
+                                       obj1->Connect(obj2, u);
+                                       obj2->Connect(obj1, t);
+                               }
+                       }
+               }
+       }
+#else
+#endif
+}
+
+
+void ApplicationWindow::HandleDisconnection(void)
+{
+}
+
+
+void ApplicationWindow::HandleGridSizeInPixels(int size)
+{
+       drawing->SetGridSize(size);
+       drawing->update();
+}
+
+
+void ApplicationWindow::HandleGridSizeInBaseUnits(QString text)
+{
+       // Parse the text...
+       bool ok;
+       double value = text.toDouble(&ok);
+
+       // Nothing parsable to a double, so quit...
+       if (!ok || value == 0)
+               return;
+
+//     drawing->gridSpacing = value;
+//     Painter::zoom = drawing->gridPixels / drawing->gridSpacing;
+       Global::gridSpacing = value;
+       Global::zoom = drawing->gridPixels / Global::gridSpacing;
+       drawing->UpdateGridBackground();
+       drawing->update();
+}
+
+
+void ApplicationWindow::HandleDimensionSize(QString text)
+{
+       // Parse the text...
+       bool ok;
+       double value = text.toDouble(&ok);
+
+       // Nothing parsable to a double, so quit...
+       if (!ok || value == 0)
+               return;
+
+//     drawing->document.ResizeAllDimensions(value);
+       drawing->update();
+}
+
+
 void ApplicationWindow::CreateActions(void)
 {
        exitAct = CreateAction(tr("&Quit"), tr("Quit"), tr("Exits the application."),
@@ -491,6 +764,9 @@ void ApplicationWindow::CreateActions(void)
        addPolygonAct = CreateAction(tr("Add &Polygon"), tr("Add Polygon"), tr("Add polygons to the drawing."), QIcon(":/res/add-polygon-tool.png"), QKeySequence("A,P"), true);
        connect(addPolygonAct, SIGNAL(triggered()), this, SLOT(AddPolygonTool()));
 
+       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);
+       connect(addSplineAct, SIGNAL(triggered()), this, SLOT(AddSplineTool()));
+
        aboutAct = CreateAction(tr("About &Architektonas"), tr("About Architektonas"), tr("Gives information about this program."), QIcon(":/res/generic-tool.png"), QKeySequence());
        connect(aboutAct, SIGNAL(triggered()), this, SLOT(HelpAbout()));
 
@@ -503,26 +779,42 @@ void ApplicationWindow::CreateActions(void)
        zoomOutAct = CreateAction(tr("Zoom &Out"), tr("Zoom Out"), tr("Zoom out of the document."), QIcon(":/res/zoom-out.png"), QKeySequence(tr("-")));
        connect(zoomOutAct, SIGNAL(triggered()), this, SLOT(ZoomOutTool()));
 
-       fileNewAct = CreateAction(tr("&New Drawing"), tr("New Drawing"), tr("Creates a new drawing."), QIcon(":/res/generic-tool.png"), QKeySequence(tr("Ctrl+n")));
+       fileNewAct = CreateAction(tr("&New Drawing"), tr("New Drawing"), tr("Creates a new drawing."), QIcon(":/res/file-new.png"), QKeySequence(tr("Ctrl+n")));
        connect(fileNewAct, SIGNAL(triggered()), this, SLOT(FileNew()));
 
-       fileOpenAct = CreateAction(tr("&Open Drawing"), tr("Open Drawing"), tr("Opens an existing drawing from a file."), QIcon(":/res/generic-tool.png"), QKeySequence(tr("Ctrl+o")));
+       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")));
        connect(fileOpenAct, SIGNAL(triggered()), this, SLOT(FileOpen()));
 
-       fileSaveAct = CreateAction(tr("&Save Drawing"), tr("Save Drawing"), tr("Saves the current drawing to a file."), QIcon(":/res/generic-tool.png"), QKeySequence(tr("Ctrl+s")));
+       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")));
        connect(fileSaveAct, SIGNAL(triggered()), this, SLOT(FileSave()));
 
-       fileSaveAsAct = CreateAction(tr("Save Drawing &As"), tr("Save As"), tr("Saves the current drawing to a file with a different name."), QIcon(":/res/generic-tool.png"), QKeySequence(tr("Ctrl+Shift+s")));
+       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")));
        connect(fileSaveAsAct, SIGNAL(triggered()), this, SLOT(FileSaveAs()));
 
-       fileCloseAct = CreateAction(tr("&Close Drawing"), tr("Close Drawing"), tr("Closes the current drawing."), QIcon(":/res/generic-tool.png"), QKeySequence(tr("Ctrl+w")));
+       fileCloseAct = CreateAction(tr("&Close Drawing"), tr("Close Drawing"), tr("Closes the current drawing."), QIcon(":/res/file-close.png"), QKeySequence(tr("Ctrl+w")));
 
-       settingsAct = CreateAction(tr("&Settings"), tr("Settings"), tr("Change certain defaults for Architektonas."), QIcon(":/res/generic-tool.png"), QKeySequence());
+       settingsAct = CreateAction(tr("&Settings"), tr("Settings"), tr("Change certain defaults for Architektonas."), QIcon(":/res/settings.png"), QKeySequence());
        connect(settingsAct, SIGNAL(triggered()), this, SLOT(Settings()));
 
        groupAct = CreateAction(tr("&Group"), tr("Group"), tr("Group/ungroup selected objects."), QIcon(":/res/group-tool.png"), QKeySequence("g"));
        connect(groupAct, SIGNAL(triggered()), this, SLOT(HandleGrouping()));
 
+       connectAct = CreateAction(tr("&Connect"), tr("Connect"), tr("Connect objects at point."), QIcon(":/res/connect-tool.png"), QKeySequence("c,c"));
+       connect(connectAct, SIGNAL(triggered()), this, SLOT(HandleConnection()));
+
+       disconnectAct = CreateAction(tr("&Disconnect"), tr("Disconnect"), tr("Disconnect objects joined at point."), QIcon(":/res/disconnect-tool.png"), QKeySequence("d,d"));
+       connect(disconnectAct, SIGNAL(triggered()), this, SLOT(HandleDisconnection()));
+
+       mirrorAct = CreateAction(tr("&Mirror"), tr("Mirror"), tr("Mirror selected objects around a line."), QIcon(":/res/mirror-tool.png"), QKeySequence("m,i"), true);
+       connect(mirrorAct, SIGNAL(triggered()), this, SLOT(MirrorTool()));
+
+       trimAct = CreateAction(tr("&Trim"), tr("Trim"), tr("Trim extraneous lines from selected objects."), QIcon(":/res/trim-tool.png"), QKeySequence("t,r"), true);
+       connect(trimAct, SIGNAL(triggered()), this, SLOT(TrimTool()));
+
+       triangulateAct = CreateAction(tr("&Triangulate"), tr("Triangulate"), tr("Make triangles from selected lines, preserving their lengths."), QIcon(":/res/triangulate-tool.png"), QKeySequence("t,g"), true);
+       connect(triangulateAct, SIGNAL(triggered()), this, SLOT(TriangulateTool()));
+
+
 //Hm. I think we'll have to have separate logic to do the "Radio Group Toolbar" thing...
 // Yup, in order to turn them off, we'd have to have an "OFF" toolbar button. Ick.
 /*     QActionGroup * group = new QActionGroup(this);
@@ -591,6 +883,11 @@ void ApplicationWindow::CreateMenus(void)
        menu->addAction(fixAngleAct);
        menu->addAction(fixLengthAct);
        menu->addAction(rotateAct);
+       menu->addAction(mirrorAct);
+       menu->addAction(trimAct);
+       menu->addAction(triangulateAct);
+       menu->addAction(connectAct);
+       menu->addAction(disconnectAct);
        menu->addSeparator();
        menu->addAction(deleteAct);
        menu->addSeparator();
@@ -598,6 +895,7 @@ void ApplicationWindow::CreateMenus(void)
        menu->addAction(addCircleAct);
        menu->addAction(addArcAct);
        menu->addAction(addPolygonAct);
+       menu->addAction(addSplineAct);
        menu->addAction(addDimensionAct);
        menu->addSeparator();
        menu->addAction(settingsAct);
@@ -610,25 +908,52 @@ void ApplicationWindow::CreateMenus(void)
 void ApplicationWindow::CreateToolbars(void)
 {
        QToolBar * toolbar = addToolBar(tr("File"));
-       toolbar->addAction(exitAct);
+       toolbar->setObjectName("File"); // Needed for saveState()
+       toolbar->addAction(fileNewAct);
+       toolbar->addAction(fileOpenAct);
+       toolbar->addAction(fileSaveAct);
+       toolbar->addAction(fileSaveAsAct);
+       toolbar->addAction(fileCloseAct);
+//     toolbar->addAction(exitAct);
 
        toolbar = addToolBar(tr("View"));
+       toolbar->setObjectName("View");
        toolbar->addAction(zoomInAct);
        toolbar->addAction(zoomOutAct);
 
+       QSpinBox * spinbox = new QSpinBox;
+       toolbar->addWidget(spinbox);
+//     QLineEdit * lineedit = new QLineEdit;
+       toolbar->addWidget(baseUnitInput);
+       toolbar->addWidget(dimensionSizeInput);
+
        toolbar = addToolBar(tr("Edit"));
+       toolbar->setObjectName("Edit");
        toolbar->addAction(snapToGridAct);
        toolbar->addAction(groupAct);
        toolbar->addAction(fixAngleAct);
        toolbar->addAction(fixLengthAct);
        toolbar->addAction(rotateAct);
+       toolbar->addAction(mirrorAct);
+       toolbar->addAction(trimAct);
+       toolbar->addAction(triangulateAct);
        toolbar->addAction(deleteAct);
+       toolbar->addAction(connectAct);
+       toolbar->addAction(disconnectAct);
        toolbar->addSeparator();
        toolbar->addAction(addLineAct);
        toolbar->addAction(addCircleAct);
        toolbar->addAction(addArcAct);
        toolbar->addAction(addPolygonAct);
+       toolbar->addAction(addSplineAct);
        toolbar->addAction(addDimensionAct);
+
+       spinbox->setRange(4, 256);
+       spinbox->setValue(12);
+       baseUnitInput->setText("12");
+       connect(spinbox, SIGNAL(valueChanged(int)), this, SLOT(HandleGridSizeInPixels(int)));
+       connect(baseUnitInput, SIGNAL(textChanged(QString)), this, SLOT(HandleGridSizeInBaseUnits(QString)));
+       connect(dimensionSizeInput, SIGNAL(textChanged(QString)), this, SLOT(HandleDimensionSize(QString)));
 }
 
 
@@ -640,10 +965,7 @@ void ApplicationWindow::ReadSettings(void)
        snapToGridAct->setChecked(settings.value("snapToGrid", true).toBool());
        resize(size);
        move(pos);
-//     pos = settings.value("charWndPos", QPoint(0, 0)).toPoint();
-//     size = settings.value("charWndSize", QSize(200, 200)).toSize();
-//     ((TTEdit *)qApp)->charWnd->resize(size);
-//     ((TTEdit *)qApp)->charWnd->move(pos);
+       restoreState(settings.value("windowState").toByteArray());
 }
 
 
@@ -651,9 +973,8 @@ void ApplicationWindow::WriteSettings(void)
 {
        settings.setValue("pos", pos());
        settings.setValue("size", size());
+       settings.setValue("windowState", saveState());
        settings.setValue("useAntialiasing", drawing->useAntialiasing);
        settings.setValue("snapToGrid", snapToGridAct->isChecked());
-//     settings.setValue("charWndPos", ((TTEdit *)qApp)->charWnd->pos());
-//     settings.setValue("charWndSize", ((TTEdit *)qApp)->charWnd->size());
 }