// 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 "about.h"
#include "blockwidget.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 "painter.h"
-#include "rotateaction.h"
#include "settingsdialog.h"
+#include "structs.h"
+#include "utils.h"
// Class variables
dock2->setObjectName("Blocks");
// Create status bar
- zoomIndicator = new QLabel("Grid: 12.0\" Zoom: 12.5%");
+ 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(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)));
}
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();
drawing->update();
documentName.clear();
setWindowTitle("Architektonas - Untitled");
{
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)
{
return;
}
- Container container(Vector(0, 0));
+ Container container;//(Vector(0, 0));
bool successful = FileIO::LoadAtnsFile(file, &container);
fclose(file);
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)
{
msg.setIcon(QMessageBox::Critical);
msg.exec();
// In this case, we should unlink the created file, since it's not right...
- unlink(documentName.toAscii().data());
+// unlink(documentName.toUtf8().data());
+ QFile::remove(documentName);
return;
}
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)
{
- drawing->document.DeleteSelectedItems();
+// drawing->DeleteSelectedItems();
+ DeleteSelectedObjects(drawing->document.objects);
drawing->update();
deleteAct->setChecked(false);
return;
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::AddSplineTool(void)
+{
+ ClearUIToolStatesExcept(addSplineAct);
+ SetInternalToolStates();
+}
+
+
void ApplicationWindow::ZoomInTool(void)
{
double zoomFactor = 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);
+ 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]
- // This should actually be calculated by drawing->gridPixels / grid size.
- Painter::zoom *= zoomFactor;
-// drawing->gridSpacing = drawing->gridPixels / Painter::zoom;
- Object::gridSpacing = drawing->gridPixels / Painter::zoom;
-// zoomIndicator->setText(QString("Grid: %2\" Zoom: %1%").arg(Painter::zoom * 100.0 * SCREEN_ZOOM).arg(drawing->gridSpacing));
-// zoomIndicator->setText(QString("Grid: %1\", BU: Inch").arg(drawing->gridSpacing));
- zoomIndicator->setText(QString("Grid: %1\", BU: Inch").arg(Object::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();
-// baseUnitInput->setText(QString("%1").arg(drawing->gridSpacing));
- baseUnitInput->setText(QString("%1").arg(Object::gridSpacing));
+ zoomIndicator->setText(QString("Grid: %1\", BU: Inch").arg(Global::gridSpacing));
+ baseUnitInput->setText(QString("%1").arg(Global::gridSpacing));
}
//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 = drawing->gridPixels / Painter::zoom;
- Object::gridSpacing = drawing->gridPixels / Painter::zoom;
-// zoomIndicator->setText(QString("Grid: %2\" Zoom: %1%").arg(Painter::zoom * 100.0 * SCREEN_ZOOM).arg(drawing->gridSpacing));
-// zoomIndicator->setText(QString("Grid: %1\", BU: Inch").arg(drawing->gridSpacing));
- zoomIndicator->setText(QString("Grid: %1\", BU: Inch").arg(Object::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();
-// baseUnitInput->setText(QString("%1").arg(drawing->gridSpacing));
- baseUnitInput->setText(QString("%1").arg(Object::gridSpacing));
+ 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);
-
- if (exception != deleteAct)
- deleteAct->setChecked(false);
-
- if (exception != rotateAct)
- rotateAct->setChecked(false);
+ QAction * actionList[] = {
+ addArcAct, addLineAct, addCircleAct, addDimensionAct, addPolygonAct,
+ addSplineAct, deleteAct, rotateAct, mirrorAct, trimAct, triangulateAct, 0
+ };
- if (exception != mirrorAct)
- mirrorAct->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());
-
// 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;
- Object::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(mirrorAct->isChecked() ? new MirrorAction() : NULL);
- drawing->SetToolActive(rotateAct->isChecked() ? new RotateAction() : NULL);
-
- if (drawing->toolAction)
- Object::ignoreClicks = true;
+ 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::HandleGrouping(void)
{
+#if 0
int itemsSelected = drawing->document.ItemsSelected();
// If nothing selected, do nothing
container->state = OSSelected;
statusBar()->showMessage(QString(tr("Grouped %1 objects.")).arg(itemsSelected));
}
+#else
+#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->gridSpacing = value;
// Painter::zoom = drawing->gridPixels / drawing->gridSpacing;
- Object::gridSpacing = value;
- Painter::zoom = drawing->gridPixels / Object::gridSpacing;
+ Global::gridSpacing = value;
+ Global::zoom = drawing->gridPixels / Global::gridSpacing;
drawing->UpdateGridBackground();
drawing->update();
}
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();
}
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()));
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.
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(addCircleAct);
menu->addAction(addArcAct);
menu->addAction(addPolygonAct);
+ menu->addAction(addSplineAct);
menu->addAction(addDimensionAct);
menu->addSeparator();
menu->addAction(settingsAct);
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->addAction(addCircleAct);
toolbar->addAction(addArcAct);
toolbar->addAction(addPolygonAct);
+ toolbar->addAction(addSplineAct);
toolbar->addAction(addDimensionAct);
spinbox->setRange(4, 256);