X-Git-Url: http://shamusworld.gotdns.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Fapplicationwindow.cpp;h=8c114c8d045c43723dbdd81d7e492a4d89f770e3;hb=d00cd5f46252ca0fb3df6e8191dc75f8991b2aa2;hp=cb4f020f5e9ab531217feeef9f62bdd7d076d453;hpb=c58b8a9f8b1ae5494857fc423ed8e33b2bbcf329;p=architektonas diff --git a/src/applicationwindow.cpp b/src/applicationwindow.cpp index cb4f020..8c114c8 100644 --- a/src/applicationwindow.cpp +++ b/src/applicationwindow.cpp @@ -8,7 +8,7 @@ // JLH = James Hammons // // 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 @@ -28,26 +28,26 @@ #include "applicationwindow.h" +#include #include "about.h" #include "blockwidget.h" +#include "commandprocessor.h" +#include "consolewidget.h" #include "drawingview.h" -#include "drawarcaction.h" -#include "drawcircleaction.h" -#include "drawdimensionaction.h" -#include "drawlineaction.h" #include "fileio.h" #include "generaltab.h" +#include "global.h" #include "layerwidget.h" -#include "mirroraction.h" +#include "objectwidget.h" #include "painter.h" -#include "rotateaction.h" +#include "penwidget.h" #include "settingsdialog.h" - +#include "structs.h" +#include "utils.h" // Class variables DrawingView * ApplicationWindow::drawing; - ApplicationWindow::ApplicationWindow(): baseUnitInput(new QLineEdit), dimensionSizeInput(new QLineEdit), @@ -60,10 +60,8 @@ ApplicationWindow::ApplicationWindow(): aboutWin = new AboutWindow(this); -// ((TTEdit *)qApp)->charWnd = new CharWindow(this); - setWindowIcon(QIcon(":/res/atns-icon.png")); - setWindowTitle("Architektonas"); +// setWindowTitle("Architektonas"); CreateActions(); CreateMenus(); @@ -78,47 +76,116 @@ 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); + QDockWidget * dock4 = new QDockWidget(tr("Command"), this); + cw = new ConsoleWidget; + dock4->setWidget(cw); + addDockWidget(Qt::BottomDockWidgetArea, dock4); + // Needed for saveState() dock1->setObjectName("Layers"); dock2->setObjectName("Blocks"); + dock3->setObjectName("Object"); + dock4->setObjectName("Commands"); - // Create status bar - zoomIndicator = new QLabel("Grid: 12.0\" Zoom: 12.5%"); + // Create status bar + zoomIndicator = new QLabel("Zoom: 100% Grid: 12.0\" BU: Inch"); statusBar()->addPermanentWidget(zoomIndicator); statusBar()->showMessage(tr("Ready")); ReadSettings(); + // Make sure the bottom left corner gets owned by the right side: + setCorner(Qt::BottomRightCorner, Qt::RightDockWidgetArea); setUnifiedTitleAndToolBarOnMac(true); - Object::SetFont(new QFont("Verdana", 15, QFont::Bold)); + 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())); + connect(cw->cmdProc, SIGNAL(UpdateNeeded()), this, SLOT(UpdateFromCommand())); +} 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(); + // 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"); +// setWindowTitle("Architektonas - Untitled"); + setWindowFilePath(documentName); statusBar()->showMessage(tr("New drawing is ready.")); } - 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; + + LoadFile(filename); +} + +void ApplicationWindow::FileOpenRecent(void) +{ + QAction * action = qobject_cast(sender()); + + if (!action) + return; + + LoadFile(action->data().toString()); +} + +void ApplicationWindow::LoadFile(QString filename) +{ + FILE * file = fopen(filename.toUtf8().data(), "r"); if (file == 0) { @@ -129,7 +196,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); @@ -139,25 +206,33 @@ 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()); + // 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)); +// setWindowTitle(QString("Architektonas - %1").arg(documentName)); + AdjustMRU(filename); statusBar()->showMessage(tr("Drawing loaded.")); } - void ApplicationWindow::FileSave(void) { if (documentName.isEmpty()) 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) { @@ -177,17 +252,19 @@ 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; } - setWindowTitle(QString("Architektonas - %1").arg(documentName)); + drawing->dirty = false; +// setWindowTitle(QString("Architektonas - %1").arg(documentName)); + AdjustMRU(documentName); statusBar()->showMessage(tr("Drawing saved.")); } - void ApplicationWindow::FileSaveAs(void) { QString filename = QFileDialog::getSaveFileName(this, tr("Save Drawing As"), @@ -200,33 +277,122 @@ void ApplicationWindow::FileSaveAs(void) } } +void ApplicationWindow::PrintPreview(void) +{ + QPrintPreviewDialog * dlg = new QPrintPreviewDialog(this); -void ApplicationWindow::SnapToGridTool(void) + connect(dlg, SIGNAL(paintRequested(QPrinter *)), this, SLOT(HandlePrintRequest(QPrinter *))); + + dlg->exec(); +} + +void ApplicationWindow::HandlePrintRequest(QPrinter * printer) { - Object::SetSnapMode(snapToGridAct->isChecked()); + 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; iRenderObjects(&painter, drawing->document.objects, i); + + // Restore vars + Global::origin = originSave; + Global::zoom = zoomSave; + Global::screenSize = screenSizeSave; } +// +// Right-click menu +// +void ApplicationWindow::contextMenuEvent(QContextMenuEvent * event) +{ + // Proof of concept, still need to code up the real thing + QMenu menu(this); -void ApplicationWindow::FixAngle(void) + VPVector hovered = drawing->GetHovered(); + + if ((drawing->select.size() > 0) || (hovered.size() > 0)) + { + QMenu * layerMenu = menu.addMenu("To layer"); + + layerAct.clear(); + + for(int i=0; isetData(i); + layerMenu->addAction(act); + layerAct.append(act); + connect(act, SIGNAL(triggered()), this, SLOT(MoveToLayer())); + } + } + + menu.addAction(mirrorAct); + menu.addAction(rotateAct); + menu.addAction(trimAct); + menu.exec(event->globalPos()); +} + +void ApplicationWindow::MoveToLayer(void) +{ + QAction * act = qobject_cast(sender()); + + drawing->MoveSelectedToLayer(act->data().toInt()); + drawing->update(); +} + +void ApplicationWindow::UpdateFromCommand(void) { - Object::SetFixedAngle(fixAngleAct->isChecked()); + cw->SetToolPrompt(); + drawing->update(); } +void ApplicationWindow::SnapToGridTool(void) +{ + Global::snapToGrid = snapToGridAct->isChecked(); +} -void ApplicationWindow::FixLength(void) +void ApplicationWindow::FixAngle(void) { - Object::SetFixedLength(fixLengthAct->isChecked()); + 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 (drawing->document.ItemsSelected() > 0) + if (drawing->select.size() > 0) { - drawing->document.DeleteSelectedItems(); + DeleteSelectedObjects(drawing->document.objects); drawing->update(); deleteAct->setChecked(false); return; @@ -235,29 +401,56 @@ void ApplicationWindow::DeleteTool(void) // Otherwise, toggle the state of the tool ClearUIToolStatesExcept(deleteAct); SetInternalToolStates(); + Global::toolSuppressCrosshair = true; } - 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(); + Global::toolSuppressCrosshair = true; +} + +void ApplicationWindow::ParallelTool(void) +{ + ClearUIToolStatesExcept(parallelAct); SetInternalToolStates(); + Global::toolSuppressCrosshair = true; } +void ApplicationWindow::TriangulateTool(void) +{ + ClearUIToolStatesExcept(triangulateAct); + SetInternalToolStates(); +} void ApplicationWindow::AddLineTool(void) { @@ -265,168 +458,153 @@ void ApplicationWindow::AddLineTool(void) 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 - Painter::origin) / zoomFactor); -//printf("newOrigin=%.2f,%.2f;\n", newOrigin.x, newOrigin.y); - Painter::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; - Object::gridSpacing = drawing->gridPixels / Painter::zoom; - drawing->UpdateGridBackground(); - drawing->update(); + Vector center = Painter::QtToCartesianCoords(Vector(size.width() / 2.0, size.height() / 2.0)); - zoomIndicator->setText(QString("Grid: %1\", BU: Inch").arg(Object::gridSpacing)); - baseUnitInput->setText(QString("%1").arg(Object::gridSpacing)); -} + Global::origin = center - ((center - Global::origin) / zoomFactor); + Global::zoom *= zoomFactor; + 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 + ((Painter::origin - center) * zoomFactor); -//printf("newOrigin=%.2f,%.2f;\n", newOrigin.x, newOrigin.y); - Painter::origin = newOrigin; -//printf("Zoom out...\n"); - // This just zooms leaving origin intact... should zoom out at the current - // center! [DONE] - Painter::zoom /= zoomFactor; - Object::gridSpacing = drawing->gridPixels / Painter::zoom; - drawing->UpdateGridBackground(); - drawing->update(); + Vector center = Painter::QtToCartesianCoords(Vector(size.width() / 2.0, size.height() / 2.0)); - zoomIndicator->setText(QString("Grid: %1\", BU: Inch").arg(Object::gridSpacing)); - baseUnitInput->setText(QString("%1").arg(Object::gridSpacing)); -} + Global::origin = center + ((Global::origin - center) * zoomFactor); + Global::zoom /= zoomFactor; + UpdateZoom(); +} -void ApplicationWindow::ClearUIToolStatesExcept(QAction * exception) +void ApplicationWindow::UpdateZoom(void) { - if (exception != addArcAct) - addArcAct->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; - 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 != deleteAct) - deleteAct->setChecked(false); + 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)); } - -void ApplicationWindow::SetInternalToolStates(void) +void ApplicationWindow::ClearUIToolStatesExcept(QAction * exception) { - Object::SetDeleteActive(deleteAct->isChecked()); - Object::SetDimensionActive(addDimensionAct->isChecked()); + QAction * actionList[] = { + addArcAct, addLineAct, addCircleAct, addDimensionAct, addPolygonAct, + addSplineAct, deleteAct, rotateAct, mirrorAct, trimAct, + triangulateAct, parallelAct, 0 + }; - // 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) + for(int i=0; actionList[i]!=0; i++) { - delete drawing->toolAction; - drawing->toolAction = NULL; - Object::ignoreClicks = false; + if (actionList[i] != exception) + actionList[i]->setChecked(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(mirrorAct->isChecked() ? new MirrorAction() : NULL); - drawing->SetToolActive(rotateAct->isChecked() ? new RotateAction() : NULL); - - if (drawing->toolAction) - Object::ignoreClicks = true; - +void ApplicationWindow::SetInternalToolStates(void) +{ + // We can be sure that if we've come here, then either an active tool is + // being deactivated, or a new tool is being activated. In either case, + // the tool state needs to be reset. Also, we reset the crosshair + // suppression flag. + Global::toolState = TSNone; + Global::toolSuppressCrosshair = false; + + 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 (parallelAct->isChecked()) + Global::tool = TTParallel; + else if (triangulateAct->isChecked()) + Global::tool = TTTriangulate; + else + Global::tool = TTNone; + + cw->SetToolPrompt();//cw->cmdline); drawing->update(); } - void ApplicationWindow::HelpAbout(void) { aboutWin->show(); } - void ApplicationWindow::Settings(void) { SettingsDialog dlg(this); @@ -440,17 +618,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) { - 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; @@ -458,18 +635,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); + Object * obj = (Object *)drawing->select[0]; -#if 0 -if (object == NULL) - printf("SelectedItem = NULL!\n"); -else - printf("SelectedItem = %08X, type = %i\n", object, object->type); -#endif - - if (object == NULL || object->type != OTContainer) + if (obj->type != OTContainer) { statusBar()->showMessage(tr("A group requires two or more selected objects.")); return; @@ -477,35 +647,121 @@ 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()); } 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(); -void ApplicationWindow::HandleGridSizeInPixels(int size) + // 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; idocument.SelectedItem(i); + + for(int j=i+1; jdocument.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) { - drawing->SetGridSize(size); - drawing->update(); } +void ApplicationWindow::HandleGridSizeInPixels(int /*size*/) +{ +// drawing->SetGridSize((uint32_t)size); + drawing->update(); +} void ApplicationWindow::HandleGridSizeInBaseUnits(QString text) { @@ -517,17 +773,16 @@ void ApplicationWindow::HandleGridSizeInBaseUnits(QString text) if (!ok || value == 0) return; -// drawing->gridSpacing = value; -// Painter::zoom = drawing->gridPixels / drawing->gridSpacing; - Object::gridSpacing = value; - Painter::zoom = drawing->gridPixels / Object::gridSpacing; - drawing->UpdateGridBackground(); + Global::gridSpacing = value; + Global::zoom = drawing->gridPixels / Global::gridSpacing; 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); @@ -536,13 +791,51 @@ void ApplicationWindow::HandleDimensionSize(QString text) if (!ok || value == 0) return; - drawing->document.ResizeAllDimensions(value); -// drawing->gridSpacing = value; -// Painter::zoom = drawing->gridPixels / drawing->gridSpacing; -// drawing->UpdateGridBackground(); +// drawing->document.ResizeAllDimensions(value); 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) { @@ -579,6 +872,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())); @@ -612,12 +908,37 @@ void ApplicationWindow::CreateActions(void) 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())); + + parallelAct = CreateAction(tr("&Parallel"), tr("Parallel"), tr("Create copies of objects parallel to the original."), QIcon(":/res/parallel-tool.png"), QKeySequence("p,l"), true); + connect(parallelAct, SIGNAL(triggered()), this, SLOT(ParallelTool())); + + 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. @@ -627,14 +948,21 @@ void ApplicationWindow::CreateActions(void) group->addAction(addLineAct); group->addAction(addCircleAct); group->addAction(addArcAct);//*/ -} + for(int i=0; isetVisible(false); + connect(rfa, SIGNAL(triggered()), this, SLOT(FileOpenRecent())); + mruAct.append(rfa); + } +} // // 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); @@ -645,13 +973,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); @@ -665,16 +993,25 @@ QAction * ApplicationWindow::CreateAction(QString name, QString tooltip, QString return action; } - void ApplicationWindow::CreateMenus(void) { QMenu * menu = menuBar()->addMenu(tr("&File")); menu->addAction(fileNewAct); menu->addAction(fileOpenAct); + + QMenu * recentMenu = menu->addMenu(tr("Open &Recent")); + + for(int i=0; iaddAction(mruAct.at(i)); + + UpdateMRUActionList(); + menu->addAction(fileSaveAct); menu->addAction(fileSaveAsAct); menu->addAction(fileCloseAct); menu->addSeparator(); + menu->addAction(printPreviewAct); + menu->addSeparator(); menu->addAction(exitAct); menu = menuBar()->addMenu(tr("&View")); @@ -688,15 +1025,23 @@ void ApplicationWindow::CreateMenus(void) menu->addAction(fixLengthAct); menu->addAction(rotateAct); menu->addAction(mirrorAct); + menu->addAction(trimAct); + menu->addAction(parallelAct); + menu->addAction(triangulateAct); 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); menu->addAction(addCircleAct); menu->addAction(addArcAct); menu->addAction(addPolygonAct); + menu->addAction(addSplineAct); menu->addAction(addDimensionAct); menu->addSeparator(); menu->addAction(settingsAct); @@ -705,7 +1050,6 @@ void ApplicationWindow::CreateMenus(void) menu->addAction(aboutAct); } - void ApplicationWindow::CreateToolbars(void) { QToolBar * toolbar = addToolBar(tr("File")); @@ -715,7 +1059,6 @@ void ApplicationWindow::CreateToolbars(void) toolbar->addAction(fileSaveAct); toolbar->addAction(fileSaveAsAct); toolbar->addAction(fileCloseAct); -// toolbar->addAction(exitAct); toolbar = addToolBar(tr("View")); toolbar->setObjectName("View"); @@ -724,7 +1067,6 @@ void ApplicationWindow::CreateToolbars(void) QSpinBox * spinbox = new QSpinBox; toolbar->addWidget(spinbox); -// QLineEdit * lineedit = new QLineEdit; toolbar->addWidget(baseUnitInput); toolbar->addWidget(dimensionSizeInput); @@ -736,6 +1078,12 @@ void ApplicationWindow::CreateToolbars(void) toolbar->addAction(fixLengthAct); toolbar->addAction(rotateAct); toolbar->addAction(mirrorAct); + toolbar->addAction(trimAct); + toolbar->addAction(parallelAct); + toolbar->addAction(triangulateAct); + toolbar->addAction(editCutAct); + toolbar->addAction(editCopyAct); + toolbar->addAction(editPasteAct); toolbar->addAction(deleteAct); toolbar->addAction(connectAct); toolbar->addAction(disconnectAct); @@ -744,6 +1092,7 @@ void ApplicationWindow::CreateToolbars(void) toolbar->addAction(addCircleAct); toolbar->addAction(addArcAct); toolbar->addAction(addPolygonAct); + toolbar->addAction(addSplineAct); toolbar->addAction(addDimensionAct); spinbox->setRange(4, 256); @@ -752,8 +1101,15 @@ 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->tbStamp, SIGNAL(triggered(QAction *)), drawing, SLOT(HandlePenStamp(QAction *))); + connect(pw->tbDropper, SIGNAL(triggered(QAction *)), drawing, SLOT(HandlePenDropper(QAction *))); +} void ApplicationWindow::ReadSettings(void) { @@ -766,7 +1122,6 @@ void ApplicationWindow::ReadSettings(void) restoreState(settings.value("windowState").toByteArray()); } - void ApplicationWindow::WriteSettings(void) { settings.setValue("pos", pos()); @@ -776,3 +1131,37 @@ void ApplicationWindow::WriteSettings(void) settings.setValue("snapToGrid", snapToGridAct->isChecked()); } +void ApplicationWindow::UpdateMRUActionList(void) +{ + QStringList mruFilePaths = settings.value("recentFiles").toStringList(); + + int mruSize = (mruFilePaths.size() <= MRU_MAX ? mruFilePaths.size() : MRU_MAX); + + for(int i=0; isetText(filename); + mruAct.at(i)->setData(mruFilePaths.at(i)); + mruAct.at(i)->setVisible(true); + } + + for(int i=mruSize; isetVisible(false); +} + +void ApplicationWindow::AdjustMRU(const QString & filePath) +{ + documentName = filePath; + setWindowFilePath(documentName); + + QStringList mruFilePaths = settings.value("recentFiles").toStringList(); + mruFilePaths.removeAll(filePath); + mruFilePaths.prepend(filePath); + + while (mruFilePaths.size() > MRU_MAX) + mruFilePaths.removeLast(); + + settings.setValue("recentFiles", mruFilePaths); + + UpdateMRUActionList(); +}