// 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 "commandprocessor.h"
+#include "consolewidget.h"
#include "drawingview.h"
#include "fileio.h"
#include "generaltab.h"
+#include "global.h"
#include "layerwidget.h"
+#include "objectwidget.h"
#include "painter.h"
+#include "penwidget.h"
#include "settingsdialog.h"
+#include "structs.h"
+#include "utils.h"
+// Class variables
+DrawingView * ApplicationWindow::drawing;
-ApplicationWindow::ApplicationWindow(): settings("Underground Software", "Architektonas")
+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);
-// ((TTEdit *)qApp)->charWnd = new CharWindow(this);
-
setWindowIcon(QIcon(":/res/atns-icon.png"));
- setWindowTitle("Architektonas");
+// setWindowTitle("Architektonas");
CreateActions();
CreateMenus();
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);
+ 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(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<QAction *>(sender());
+
+ if (!action)
+ return;
+
+ LoadFile(action->data().toString());
+}
+
+void ApplicationWindow::LoadFile(QString filename)
+{
+ FILE * file = fopen(filename.toUtf8().data(), "r");
if (file == 0)
{
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);
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)
{
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"),
}
}
+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; i<Global::numLayers; i++)
+ drawing->RenderObjects(&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; i<Global::numLayers; i++)
+ {
+ QAction * act = new QAction(QIcon(), Global::layerName[i].c_str(), this);
+ act->setData(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)
{
- Object::SetFixedAngle(fixAngleAct->isChecked());
+ QAction * act = qobject_cast<QAction *>(sender());
+
+ drawing->MoveSelectedToLayer(act->data().toInt());
+ drawing->update();
}
+void ApplicationWindow::UpdateFromCommand(void)
+{
+ cw->SetToolPrompt();
+ drawing->update();
+}
-void ApplicationWindow::FixLength(void)
+void ApplicationWindow::SnapToGridTool(void)
+{
+ Global::snapToGrid = snapToGridAct->isChecked();
+}
+
+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;
// 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)
{
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
+ double zoomFactor = 1.20;
+ QSize size = drawing->size();
+ Vector center = Painter::QtToCartesianCoords(Vector(size.width() / 2.0, size.height() / 2.0));
-transform x/yCenter to Cartesian coordinates. So far, so good.
+ Global::origin = center - ((center - Global::origin) / zoomFactor);
+ Global::zoom *= zoomFactor;
-when zooming in, new origin will be (xCenter - origin.x) / 2, (yCenter - origin.y) / 2
-(after subtracting from center, that is...)
-*/
- 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;
- drawing->gridSpacing = 12.0 / Painter::zoom;
- zoomIndicator->setText(QString("Grid: %2\" Zoom: %1%").arg(Painter::zoom * 100.0 * SCREEN_ZOOM).arg(drawing->gridSpacing));
- drawing->UpdateGridBackground();
- drawing->update();
+ UpdateZoom();
}
-
void ApplicationWindow::ZoomOutTool(void)
{
-/*
-Ok, real example.
-center = (436, 311)
-origin = (223, 160.5)
-newOrigin should be (-10, -10)
-Why isn't it?
+ double zoomFactor = 1.20;
+ QSize size = drawing->size();
+ Vector center = Painter::QtToCartesianCoords(Vector(size.width() / 2.0, size.height() / 2.0));
-center - origin = (213, 150.5)
-origin - center = (-213, -150.5)
-x 2 = (-426, -301)
-+ center = (-10, -10)
+ Global::origin = center + ((Global::origin - center) * zoomFactor);
+ Global::zoom /= zoomFactor;
-*/
- double zoomFactor = 2.0;
- 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;
- drawing->gridSpacing = 12.0 / Painter::zoom;
- zoomIndicator->setText(QString("Grid: %2\" Zoom: %1%").arg(Painter::zoom * 100.0 * SCREEN_ZOOM).arg(drawing->gridSpacing));
- drawing->UpdateGridBackground();
- drawing->update();
+ 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);
+ // 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 != addPolygonAct)
- addPolygonAct->setChecked(false);
+ drawing->update();
- if (exception != deleteAct)
- deleteAct->setChecked(false);
+ zoomIndicator->setText(QString("Zoom: %1% Grid: %2\" BU: Inch").arg(Global::zoom * 100.0).arg(Global::gridSpacing));
- if (exception != rotateAct)
- rotateAct->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());
- drawing->SetRotateToolActive(rotateAct->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;
+ if (actionList[i] != exception)
+ actionList[i]->setChecked(false);
}
-
- drawing->SetAddLineToolActive(addLineAct->isChecked());
- drawing->SetAddCircleToolActive(addCircleAct->isChecked());
- drawing->SetAddDimensionToolActive(addDimensionAct->isChecked());
}
+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);
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;
// 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;
// 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 * 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();
+
+ // 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++)
{
- 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));
+ 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((uint32_t)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;
+
+ 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);
+
+ // Nothing parsable to a double, so quit...
+ if (!ok || value == 0)
+ return;
+
+// 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)
{
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()));
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()));
+
+ 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.
/* QActionGroup * group = new QActionGroup(this);
group->addAction(addLineAct);
group->addAction(addCircleAct);
group->addAction(addArcAct);//*/
-}
+ for(int i=0; i<MRU_MAX; i++)
+ {
+ QAction * rfa = new QAction(this);
+ rfa->setVisible(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);
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);
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; i<MRU_MAX; i++)
+ recentMenu->addAction(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"));
menu->addAction(fixAngleAct);
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);
menu->addAction(aboutAct);
}
-
void ApplicationWindow::CreateToolbars(void)
{
QToolBar * toolbar = addToolBar(tr("File"));
toolbar->setObjectName("File"); // Needed for saveState()
- toolbar->addAction(exitAct);
+ toolbar->addAction(fileNewAct);
+ toolbar->addAction(fileOpenAct);
+ toolbar->addAction(fileSaveAct);
+ toolbar->addAction(fileSaveAsAct);
+ toolbar->addAction(fileCloseAct);
toolbar = addToolBar(tr("View"));
toolbar->setObjectName("View");
toolbar->addAction(zoomInAct);
toolbar->addAction(zoomOutAct);
+ QSpinBox * spinbox = new QSpinBox;
+ toolbar->addWidget(spinbox);
+ toolbar->addWidget(baseUnitInput);
+ toolbar->addWidget(dimensionSizeInput);
+
toolbar = addToolBar(tr("Edit"));
toolbar->setObjectName("Edit");
toolbar->addAction(snapToGridAct);
toolbar->addAction(fixAngleAct);
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);
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)));
+
+ 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)
{
resize(size);
move(pos);
restoreState(settings.value("windowState").toByteArray());
-// 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);
}
-
void ApplicationWindow::WriteSettings(void)
{
settings.setValue("pos", pos());
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());
}
+void ApplicationWindow::UpdateMRUActionList(void)
+{
+ QStringList mruFilePaths = settings.value("recentFiles").toStringList();
+
+ int mruSize = (mruFilePaths.size() <= MRU_MAX ? mruFilePaths.size() : MRU_MAX);
+
+ for(int i=0; i<mruSize; i++)
+ {
+ QString filename = QFileInfo(mruFilePaths.at(i)).fileName();
+ mruAct.at(i)->setText(filename);
+ mruAct.at(i)->setData(mruFilePaths.at(i));
+ mruAct.at(i)->setVisible(true);
+ }
+
+ for(int i=mruSize; i<MRU_MAX; i++)
+ mruAct.at(i)->setVisible(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();
+}