]> Shamusworld >> Repos - architektonas/blobdiff - src/applicationwindow.cpp
Fix for Print Preview for centering of drawing on page.
[architektonas] / src / applicationwindow.cpp
index 2049a097ca354b54e69297fcbc48d91753b7d528..b80130e8989cf77740f5e66ad97dcc9ba801c092 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 "applicationwindow.h"
 
+#include <QPrintPreviewDialog>
 #include "about.h"
 #include "blockwidget.h"
-//#include "dimension.h"
 #include "drawingview.h"
-//#include "drawarcaction.h"
-//#include "drawcircleaction.h"
-//#include "drawdimensionaction.h"
-//#include "drawlineaction.h"
-//#include "drawsplineaction.h"
 #include "fileio.h"
 #include "generaltab.h"
-//#include "geometry.h"
 #include "global.h"
 #include "layerwidget.h"
-//#include "line.h"
-//#include "mirroraction.h"
+#include "objectwidget.h"
 #include "painter.h"
-//#include "rotateaction.h"
+#include "penwidget.h"
 #include "settingsdialog.h"
-//#include "triangulateaction.h"
-//#include "trimaction.h"
-
+#include "structs.h"
+#include "utils.h"
 
 // Class variables
 DrawingView * ApplicationWindow::drawing;
 
-
 ApplicationWindow::ApplicationWindow():
        baseUnitInput(new QLineEdit),
        dimensionSizeInput(new QLineEdit),
@@ -85,12 +76,17 @@ ApplicationWindow::ApplicationWindow():
        BlockWidget * bw = new BlockWidget;
        dock2->setWidget(bw);
        addDockWidget(Qt::RightDockWidgetArea, dock2);
+       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");
+       // Create status bar
+       zoomIndicator = new QLabel("Zoom: 100% Grid: 12.0\" BU: Inch");
        statusBar()->addPermanentWidget(zoomIndicator);
        statusBar()->showMessage(tr("Ready"));
 
@@ -98,30 +94,62 @@ ApplicationWindow::ApplicationWindow():
        setUnifiedTitleAndToolBarOnMac(true);
        Global::font =  new QFont("Verdana", 15, QFont::Bold);
 
-       connect(lw, SIGNAL(LayerSelected(int)), drawing, SLOT(SetCurrentLayer(int)));
-}
+       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(this, SIGNAL(ReloadLayers()), lw, SLOT(Reload()));
 
+       connect(drawing, SIGNAL(ObjectHovered(Object *)), ow, SLOT(ShowInfo(Object *)));
+       connect(drawing, SIGNAL(NeedZoomUpdate()), this, SLOT(UpdateZoom()));
+}
 
 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();
-       drawing->document.objects.empty();
+       // Warn the user if drawing has changed and hasn't been saved...
+       if (drawing->dirty)
+       {
+               QMessageBox msg;
+
+               msg.setText("The document has been modified.");
+               msg.setInformativeText("Do you want to save your changes?");
+               msg.setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);
+               msg.setDefaultButton(QMessageBox::Save);
+               int response = msg.exec();
+
+               switch (response)
+               {
+               case QMessageBox::Save:
+                       // Save was clicked
+                       FileSave();
+                       break;
+               case QMessageBox::Discard:
+                       // Don't Save was clicked
+                       break;
+               case QMessageBox::Cancel:
+                       // Cancel was clicked
+                       return;
+//                     break;
+               }
+       }
+
+       FileIO::ResetLayerVectors();
+       emit ReloadLayers();
+       DeleteContents(drawing->document.objects);
+       drawing->document.objects.clear();
+       drawing->dirty = false;
        drawing->update();
        documentName.clear();
        setWindowTitle("Architektonas - Untitled");
        statusBar()->showMessage(tr("New drawing is ready."));
 }
 
-
 void ApplicationWindow::FileOpen(void)
 {
        QString filename = QFileDialog::getOpenFileName(this, tr("Open Drawing"),
@@ -142,7 +170,7 @@ void ApplicationWindow::FileOpen(void)
                return;
        }
 
-       Container container;//(Vector(0, 0));
+       Container container(true);      // Make sure it's a top level container...
        bool successful = FileIO::LoadAtnsFile(file, &container);
        fclose(file);
 
@@ -152,18 +180,26 @@ 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);
+       emit ReloadLayers();
+       // We can do this because the vector is just a bunch of pointers to our
+       // Objects, and the Containers (non-empty) can be moved this way with no
+       // problem.
        drawing->document = container;
+       drawing->dirty = false;
        drawing->update();
        documentName = filename;
        setWindowTitle(QString("Architektonas - %1").arg(documentName));
        statusBar()->showMessage(tr("Drawing loaded."));
 }
 
-
 void ApplicationWindow::FileSave(void)
 {
        if (documentName.isEmpty())
@@ -190,17 +226,18 @@ 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...
+               // In this case, we should unlink the created file, since it's not
+               // right...
 //             unlink(documentName.toUtf8().data());
                QFile::remove(documentName);
                return;
        }
 
+       drawing->dirty = false;
        setWindowTitle(QString("Architektonas - %1").arg(documentName));
        statusBar()->showMessage(tr("Drawing saved."));
 }
 
-
 void ApplicationWindow::FileSaveAs(void)
 {
        QString filename = QFileDialog::getSaveFileName(this, tr("Save Drawing As"),
@@ -213,270 +250,285 @@ void ApplicationWindow::FileSaveAs(void)
        }
 }
 
+void ApplicationWindow::PrintPreview(void)
+{
+       QPrintPreviewDialog * dlg = new QPrintPreviewDialog(this);
+
+       connect(dlg, SIGNAL(paintRequested(QPrinter *)), this, SLOT(HandlePrintRequest(QPrinter *)));
+
+       dlg->exec();
+}
+
+void ApplicationWindow::HandlePrintRequest(QPrinter * printer)
+{
+       QPainter qtPainter(printer);
+       Painter painter(&qtPainter);
+
+       // Save vars for screen
+       Point originSave = Global::origin;
+       double zoomSave = Global::zoom;
+       Vector screenSizeSave = Global::screenSize;
+
+       // Adjust zoom + origin to fit the paper (or NxM pages if we have 'em)
+       Rect r = drawing->GetObjectExtents((Object *)(&(drawing->document)));
+
+       QPageLayout pageLayout = printer->pageLayout();
+       QRect pageRect = pageLayout.paintRectPixels(printer->resolution());
+
+       Global::origin = r.BottomLeft();
+       Global::screenSize.x = pageRect.width();
+       Global::screenSize.y = pageRect.height();
+
+       double xScale = (double)pageRect.width() / r.Width();
+       double yScale = (double)pageRect.height() / r.Height();
+       Global::zoom = qMin(xScale, yScale);
+
+       if (xScale < yScale)
+               Global::origin.y -= (((double)pageRect.height() / Global::zoom) - r.Height()) / 2.0;
+       else
+               Global::origin.x -= (((double)pageRect.width() / Global::zoom) - r.Width()) / 2.0;
+
+       // Do object rendering...
+       for(int i=0; i<Global::numLayers; i++)
+               drawing->RenderObjects(&painter, drawing->document.objects, i);
+
+       // Restore vars
+       Global::origin = originSave;
+       Global::zoom = zoomSave;
+       Global::screenSize = screenSizeSave;
+}
+
+void ApplicationWindow::contextMenuEvent(QContextMenuEvent * event)
+{
+       QMenu menu(this);
+       menu.addAction(mirrorAct);
+       menu.addAction(rotateAct);
+       menu.addAction(trimAct);
+       menu.exec(event->globalPos());
+}
 
 void ApplicationWindow::SnapToGridTool(void)
 {
        Global::snapToGrid = snapToGridAct->isChecked();
 }
 
-
 void ApplicationWindow::FixAngle(void)
 {
        Global::fixedAngle = fixAngleAct->isChecked();
 }
 
-
 void ApplicationWindow::FixLength(void)
 {
        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 0
-       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;
        }
-#endif
 
        // Otherwise, toggle the state of the tool
        ClearUIToolStatesExcept(deleteAct);
        SetInternalToolStates();
 }
 
-
 void ApplicationWindow::DimensionTool(void)
 {
        ClearUIToolStatesExcept(addDimensionAct);
        SetInternalToolStates();
 }
 
-
 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();
 }
 
-
 void ApplicationWindow::AddLineTool(void)
 {
        ClearUIToolStatesExcept(addLineAct);
        SetInternalToolStates();
 }
 
-
 void ApplicationWindow::AddCircleTool(void)
 {
        ClearUIToolStatesExcept(addCircleAct);
        SetInternalToolStates();
 }
 
-
 void ApplicationWindow::AddArcTool(void)
 {
        ClearUIToolStatesExcept(addArcAct);
        SetInternalToolStates();
 }
 
-
 void ApplicationWindow::AddPolygonTool(void)
 {
        ClearUIToolStatesExcept(addPolygonAct);
        SetInternalToolStates();
 }
 
-
 void ApplicationWindow::AddSplineTool(void)
 {
        ClearUIToolStatesExcept(addSplineAct);
        SetInternalToolStates();
 }
 
-
 void ApplicationWindow::ZoomInTool(void)
 {
-       double zoomFactor = 2.0;
-/*
-We need to find the center of the screen, then figure out where the new corner
-will be in the zoomed in window.
-
-So we know in Qt coords, the center is found via:
-size.width()  / 2 --> xCenter
-size.height() / 2 --> yCenter
-
-transform x/yCenter to Cartesian coordinates. So far, so good.
-
-when zooming in, new origin will be (xCenter - origin.x) / 2, (yCenter - origin.y) / 2
-(after subtracting from center, that is...)
-*/
+       double zoomFactor = 1.20;
        QSize size = drawing->size();
-       Vector center(size.width() / 2.0, size.height() / 2.0);
-//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 - Global::origin) / zoomFactor);
-//printf("newOrigin=%.2f,%.2f;\n", newOrigin.x, newOrigin.y);
-       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]
+       Vector center = Painter::QtToCartesianCoords(Vector(size.width() / 2.0, size.height() / 2.0));
+
+       Global::origin = center - ((center - Global::origin) / zoomFactor);
        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));
+       UpdateZoom();
 }
 
-
 void ApplicationWindow::ZoomOutTool(void)
 {
-/*
-Ok, real example.
-center = (436, 311)
-origin = (223, 160.5)
-newOrigin should be (-10, -10)
-Why isn't it?
-
-center - origin = (213, 150.5)
-origin - center = (-213, -150.5)
-x 2 = (-426, -301)
-+ center = (-10, -10)
-
-*/
-       double zoomFactor = 2.0;
+       double zoomFactor = 1.20;
        QSize size = drawing->size();
-       Vector center(size.width() / 2.0, size.height() / 2.0);
-//printf("Zoom out... 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 - Painter::origin) * zoomFactor;
-//     Vector newOrigin = center - (Painter::origin * zoomFactor);
-       Vector newOrigin = center + ((Global::origin - center) * zoomFactor);
-//printf("newOrigin=%.2f,%.2f;\n", newOrigin.x, newOrigin.y);
-       Global::origin = newOrigin;
-//printf("Zoom out...\n");
-       // This just zooms leaving origin intact... should zoom out at the current
-       // center! [DONE]
+       Vector center = Painter::QtToCartesianCoords(Vector(size.width() / 2.0, size.height() / 2.0));
+
+       Global::origin = center + ((Global::origin - center) * zoomFactor);
        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));
+       UpdateZoom();
 }
 
-
-void ApplicationWindow::ClearUIToolStatesExcept(QAction * exception)
+void ApplicationWindow::UpdateZoom(void)
 {
-       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);
-
-       if (exception != addSplineAct)
-               addSplineAct->setChecked(false);
-
-       if (exception != deleteAct)
-               deleteAct->setChecked(false);
+       // And now, a bunch of heuristics to select the right grid size--autogrid!
+       // :-P
+       if (Global::zoom < 0.25)
+               Global::gridSpacing = 48.0;
+       else if (Global::zoom >= 0.25 && Global::zoom < 0.50)
+               Global::gridSpacing = 36.0;
+       else if (Global::zoom >= 0.50 && Global::zoom < 1.00)
+               Global::gridSpacing = 24.0;
+       else if (Global::zoom >= 1.00 && Global::zoom < 2.00)
+               Global::gridSpacing = 12.0;
+       else if (Global::zoom >= 2.00 && Global::zoom < 4.00)
+               Global::gridSpacing = 6.0;
+       else if (Global::zoom >= 4.00 && Global::zoom < 8.00)
+               Global::gridSpacing = 3.0;
+       else if (Global::zoom >= 8.00 && Global::zoom < 16.00)
+               Global::gridSpacing = 1.0;
+       else if (Global::zoom >= 16.00 && Global::zoom < 32.00)
+               Global::gridSpacing = 0.5;
+       else if (Global::zoom >= 32.00 && Global::zoom < 64.00)
+               Global::gridSpacing = 0.25;
+       else if (Global::zoom >= 64.00 && Global::zoom < 128.00)
+               Global::gridSpacing = 0.125;
+       else if (Global::zoom >= 128.00 && Global::zoom < 256.00)
+               Global::gridSpacing = 0.0625;
+       else if (Global::zoom >= 256.00 && Global::zoom < 512.00)
+               Global::gridSpacing = 0.03125;
+       else
+               Global::gridSpacing = 0.015625;
+
+//     drawing->SetGridSize((double)(Global::gridSpacing * Global::zoom));
+       drawing->update();
 
-       if (exception != rotateAct)
-               rotateAct->setChecked(false);
+       zoomIndicator->setText(QString("Zoom: %1% Grid: %2\" BU: Inch").arg(Global::zoom * 100.0).arg(Global::gridSpacing));
 
-       if (exception != mirrorAct)
-               mirrorAct->setChecked(false);
+       // This is the problem...  Changing this causes the state to update itself again, screwing up the origin...  !!! FIX !!! (commented out for now)
+//     baseUnitInput->setText(QString("%1").arg(Global::gridSpacing));
+}
 
-       if (exception != trimAct)
-               trimAct->setChecked(false);
+void ApplicationWindow::ClearUIToolStatesExcept(QAction * exception)
+{
+       QAction * actionList[] = {
+               addArcAct, addLineAct, addCircleAct, addDimensionAct, addPolygonAct,
+               addSplineAct, deleteAct, rotateAct, mirrorAct, trimAct,
+               triangulateAct, 0
+       };
 
-       if (exception != triangulateAct)
-               triangulateAct->setChecked(false);
+       for(int i=0; actionList[i]!=0; i++)
+       {
+               if (actionList[i] != exception)
+                       actionList[i]->setChecked(false);
+       }
 }
 
-
 void ApplicationWindow::SetInternalToolStates(void)
 {
-       Global::deleteActive = deleteAct->isChecked();
-       Global::dimensionActive = addDimensionAct->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 0
-       if (drawing->toolAction)
-       {
-               delete drawing->toolAction;
-               drawing->toolAction = NULL;
-               Global::ignoreClicks = false;
-       }
-
-       drawing->SetToolActive(addLineAct->isChecked() ? new DrawLineAction() : NULL);
-       drawing->SetToolActive(addCircleAct->isChecked() ? new DrawCircleAction() : NULL);
-       drawing->SetToolActive(addArcAct->isChecked() ? new DrawArcAction() : NULL);
-       drawing->SetToolActive(addDimensionAct->isChecked() ? new DrawDimensionAction() : NULL);
-       drawing->SetToolActive(addSplineAct->isChecked() ? new DrawSplineAction() : NULL);
-       drawing->SetToolActive(mirrorAct->isChecked() ? new MirrorAction() : NULL);
-       drawing->SetToolActive(rotateAct->isChecked() ? new RotateAction() : NULL);
-       drawing->SetToolActive(trimAct->isChecked() ? new TrimAction() : NULL);
-       drawing->SetToolActive(triangulateAct->isChecked() ? new TriangulateAction() : NULL);
-
-       if (drawing->toolAction)
-               Global::ignoreClicks = true;
-#endif
+       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->update();
 }
 
-
 void ApplicationWindow::HelpAbout(void)
 {
        aboutWin->show();
 }
 
-
 void ApplicationWindow::Settings(void)
 {
        SettingsDialog dlg(this);
@@ -490,18 +542,16 @@ void ApplicationWindow::Settings(void)
        WriteSettings();
 }
 
-
 //
 // Group a bunch of selected objects (which can include other groups) together
 // or ungroup a selected group.
 //
 void ApplicationWindow::HandleGrouping(void)
 {
-#if 0
-       int itemsSelected = drawing->document.ItemsSelected();
+       int numSelected = drawing->select.size();
 
        // If nothing selected, do nothing
-       if (itemsSelected == 0)
+       if (numSelected == 0)
        {
                statusBar()->showMessage(tr("No objects selected to make a group from."));
                return;
@@ -509,18 +559,11 @@ void ApplicationWindow::HandleGrouping(void)
 
        // If it's a group that's selected, ungroup it and leave the objects in a
        // selected state
-       if (itemsSelected == 1)
+       if (numSelected == 1)
        {
-               Object * object = drawing->document.SelectedItem(0);
-
-#if 0
-if (object == NULL)
-       printf("SelectedItem = NULL!\n");
-else
-       printf("SelectedItem = %08X, type = %i\n", object, object->type);
-#endif
+               Object * obj = (Object *)drawing->select[0];
 
-               if (object == NULL || object->type != OTContainer)
+               if (obj->type != OTContainer)
                {
                        statusBar()->showMessage(tr("A group requires two or more selected objects."));
                        return;
@@ -528,30 +571,43 @@ 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.
-               ((Container *)object)->SelectAll();
-               ((Container *)object)->MoveContentsTo(&(drawing->document));
-               drawing->document.Delete(object);
+               // 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 * c = (Container *)obj;
+               SelectAll(c->objects);
+               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 (itemsSelected > 1)
+       else if (numSelected > 1)
        {
-               Container * container = new Container(Vector(), &(drawing->document));
-               drawing->document.MoveSelectedContentsTo(container);
-               drawing->document.Add(container);
-               container->DeselectAll();
-               container->state = OSSelected;
-               statusBar()->showMessage(QString(tr("Grouped %1 objects.")).arg(itemsSelected));
+               Container * c = new Container();
+               MoveSelectedObjectsTo(c->objects, drawing->document.objects);
+               drawing->document.objects.push_back(c);
+               ClearSelected(c->objects);
+               c->selected = true;
+               c->layer = Global::activeLayer;
+
+               Rect r = drawing->GetObjectExtents((Object *)c);
+               c->p[0] = r.TopLeft();
+               c->p[1] = r.BottomRight();
+
+               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
@@ -617,22 +673,20 @@ printf("   -> intersects = %i, t=%lf, u=%lf\n", intersects, t, u);
                        }
                }
        }
+#else
 #endif
 }
 
-
 void ApplicationWindow::HandleDisconnection(void)
 {
 }
 
-
-void ApplicationWindow::HandleGridSizeInPixels(int size)
+void ApplicationWindow::HandleGridSizeInPixels(int /*size*/)
 {
-       drawing->SetGridSize(size);
+//     drawing->SetGridSize((uint32_t)size);
        drawing->update();
 }
 
-
 void ApplicationWindow::HandleGridSizeInBaseUnits(QString text)
 {
        // Parse the text...
@@ -647,13 +701,15 @@ void ApplicationWindow::HandleGridSizeInBaseUnits(QString text)
 //     Painter::zoom = drawing->gridPixels / drawing->gridSpacing;
        Global::gridSpacing = value;
        Global::zoom = drawing->gridPixels / Global::gridSpacing;
-       drawing->UpdateGridBackground();
+//     drawing->UpdateGridBackground();
        drawing->update();
 }
 
-
 void ApplicationWindow::HandleDimensionSize(QString text)
 {
+/*
+This is the third input on the view toolbar; not sure what it was supposed to do... (resize all dimensions in the drawing?)
+*/
        // Parse the text...
        bool ok;
        double value = text.toDouble(&ok);
@@ -666,6 +722,47 @@ void ApplicationWindow::HandleDimensionSize(QString text)
        drawing->update();
 }
 
+void ApplicationWindow::EditCopy(void)
+{
+       if (drawing->select.size() > 0)
+       {
+               DeleteContents(clipboard);
+               clipboard = CopySelectedObjects(drawing->document.objects);
+       }
+}
+
+void ApplicationWindow::EditCut(void)
+{
+       if (drawing->select.size() > 0)
+       {
+               DeleteContents(clipboard);
+               clipboard = MoveSelectedObjectsFrom(drawing->document.objects);
+               drawing->update();
+       }
+}
+
+void ApplicationWindow::EditPaste(void)
+{
+       if (clipboard.size() > 0)
+       {
+               // We want to maybe make it so that the pasted objects are being moved in a "mouse drag" state...
+               ClearSelected(drawing->document.objects);
+               SelectAll(clipboard);
+               drawing->document.Add(CopyObjects(clipboard));
+               drawing->update();
+       }
+}
+
+//
+// Select all *visible* objects.  If an object is on a layer that is not
+// visible, skip it.
+//
+void ApplicationWindow::SelectAllObjects(void)
+{
+       // Set object's state & update the drawing
+       SelectAll(drawing->document.objects);
+       drawing->update();
+}
 
 void ApplicationWindow::CreateActions(void)
 {
@@ -752,6 +849,20 @@ void ApplicationWindow::CreateActions(void)
        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()));
 
+       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")));
+       connect(editCutAct, SIGNAL(triggered()), this, SLOT(EditCut()));
+
+       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")));
+       connect(editCopyAct, SIGNAL(triggered()), this, SLOT(EditCopy()));
+
+       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")));
+       connect(editPasteAct, SIGNAL(triggered()), this, SLOT(EditPaste()));
+
+       selectAllAct = CreateAction(tr("Select &All"), tr("Select All Objects"), tr("Select all objects in the drawing."), QIcon(), QKeySequence(tr("Ctrl+a")));
+       connect(selectAllAct, SIGNAL(triggered()), this, SLOT(SelectAllObjects()));
+
+       printPreviewAct = CreateAction(tr("&Print Preview"), tr("Print Preview"), tr("Shows preview of printing operation."), QIcon(":/res/print-preview.png"), QKeySequence(tr("Ctrl+p")));
+       connect(printPreviewAct, SIGNAL(triggered()), this, SLOT(PrintPreview()));
 
 //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.
@@ -763,12 +874,11 @@ void ApplicationWindow::CreateActions(void)
        group->addAction(addArcAct);//*/
 }
 
-
 //
 // Consolidates action creation from a multi-step process to a single-step one.
 //
-QAction * ApplicationWindow::CreateAction(QString name, QString tooltip, QString statustip,
-       QIcon icon, QKeySequence key, bool checkable/*= false*/)
+QAction * ApplicationWindow::CreateAction(QString name, QString tooltip,
+       QString statustip, QIcon icon, QKeySequence key, bool checkable/*= false*/)
 {
        QAction * action = new QAction(icon, name, this);
        action->setToolTip(tooltip);
@@ -779,13 +889,13 @@ QAction * ApplicationWindow::CreateAction(QString name, QString tooltip, QString
        return action;
 }
 
-
 //
 // This is essentially the same as the previous function, but this allows more
 // than one key sequence to be added as key shortcuts.
 //
-QAction * ApplicationWindow::CreateAction(QString name, QString tooltip, QString statustip,
-       QIcon icon, QKeySequence key1, QKeySequence key2, bool checkable/*= false*/)
+QAction * ApplicationWindow::CreateAction(QString name, QString tooltip,
+       QString statustip, QIcon icon, QKeySequence key1, QKeySequence key2,
+       bool checkable/*= false*/)
 {
        QAction * action = new QAction(icon, name, this);
        action->setToolTip(tooltip);
@@ -799,7 +909,6 @@ QAction * ApplicationWindow::CreateAction(QString name, QString tooltip, QString
        return action;
 }
 
-
 void ApplicationWindow::CreateMenus(void)
 {
        QMenu * menu = menuBar()->addMenu(tr("&File"));
@@ -809,6 +918,8 @@ void ApplicationWindow::CreateMenus(void)
        menu->addAction(fileSaveAsAct);
        menu->addAction(fileCloseAct);
        menu->addSeparator();
+       menu->addAction(printPreviewAct);
+       menu->addSeparator();
        menu->addAction(exitAct);
 
        menu = menuBar()->addMenu(tr("&View"));
@@ -827,6 +938,10 @@ void ApplicationWindow::CreateMenus(void)
        menu->addAction(connectAct);
        menu->addAction(disconnectAct);
        menu->addSeparator();
+       menu->addAction(selectAllAct);
+       menu->addAction(editCutAct);
+       menu->addAction(editCopyAct);
+       menu->addAction(editPasteAct);
        menu->addAction(deleteAct);
        menu->addSeparator();
        menu->addAction(addLineAct);
@@ -842,7 +957,6 @@ void ApplicationWindow::CreateMenus(void)
        menu->addAction(aboutAct);
 }
 
-
 void ApplicationWindow::CreateToolbars(void)
 {
        QToolBar * toolbar = addToolBar(tr("File"));
@@ -875,6 +989,9 @@ void ApplicationWindow::CreateToolbars(void)
        toolbar->addAction(mirrorAct);
        toolbar->addAction(trimAct);
        toolbar->addAction(triangulateAct);
+       toolbar->addAction(editCutAct);
+       toolbar->addAction(editCopyAct);
+       toolbar->addAction(editPasteAct);
        toolbar->addAction(deleteAct);
        toolbar->addAction(connectAct);
        toolbar->addAction(disconnectAct);
@@ -892,8 +1009,19 @@ void ApplicationWindow::CreateToolbars(void)
        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)));
-}
 
+       PenWidget * pw = new PenWidget();
+       toolbar = addToolBar(tr("Pen"));
+       toolbar->setObjectName(tr("Pen"));
+       toolbar->addWidget(pw);
+       connect(drawing, SIGNAL(ObjectSelected(Object *)), pw, SLOT(SetFields(Object *)));
+       connect(pw, SIGNAL(WidthSelected(float)), drawing, SLOT(HandlePenWidth(float)));
+       connect(pw, SIGNAL(StyleSelected(int)), drawing, SLOT(HandlePenStyle(int)));
+       connect(pw, SIGNAL(ColorSelected(uint32_t)), drawing, SLOT(HandlePenColor(uint32_t)));
+//     connect(pw, SIGNAL(StampSelected(void)), drawing, SLOT(HandlePenStamp(QAction *)));
+       connect(pw->tbStamp, SIGNAL(triggered(QAction *)), drawing, SLOT(HandlePenStamp(QAction *)));
+       connect(pw->tbDropper, SIGNAL(triggered(QAction *)), drawing, SLOT(HandlePenDropper(QAction *)));
+}
 
 void ApplicationWindow::ReadSettings(void)
 {
@@ -906,7 +1034,6 @@ void ApplicationWindow::ReadSettings(void)
        restoreState(settings.value("windowState").toByteArray());
 }
 
-
 void ApplicationWindow::WriteSettings(void)
 {
        settings.setValue("pos", pos());
@@ -915,4 +1042,3 @@ void ApplicationWindow::WriteSettings(void)
        settings.setValue("useAntialiasing", drawing->useAntialiasing);
        settings.setValue("snapToGrid", snapToGridAct->isChecked());
 }
-