void ApplicationWindow::HandleGrouping(void)
{
// Group a bunch of selected objects together or ungroup a selected group.
+
+ if (drawing->document.ItemsSelected() == 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 (drawing->document.ItemsSelected() == 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
+
+ if (object == NULL || object->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);
+ }
+ // Otherwise, if it's a group of 2 or more objects (which can be groups too)
+ // group them and select the group
+ else if (drawing->document.ItemsSelected() > 1)
+ {
+ Container * container = new Container(Vector(), &(drawing->document));
+ drawing->document.MoveSelectedContentsTo(container);
+ drawing->document.Add(container);
+ container->DeselectAll();
+ container->state = OSSelected;
+ }
+
+ drawing->update();
}
#include "painter.h"
-Arc::Arc(Vector p1, double r, double a1, double a2, Object * p/*= NULL*/): Object(p1, p),
- radius(r), startAngle(a1), angleSpan(a2)
+Arc::Arc(Vector p1, double r, double a1, double a2, Object * p/*= NULL*/):
+ Object(p1, p), /*type(OTArc),*/ radius(r), startAngle(a1), angleSpan(a2)
{
+ // This is in the base class, why can't we use the contructor to fill it???
+ type = OTArc;
}
state = oldState;
}
+
+/*virtual*/ QRectF Arc::Extents(void)
+{
+#warning "!!! Arc extents not calculated !!!"
+ return QRectF();
+}
+
+
#if 0
/*virtual*/ bool Arc::NeedsUpdate(void)
{
#endif
+#if 0
+/*virtual*/ ObjectType Arc::Type(void)
+{
+ return OTArc;
+}
+#endif
+
+
/*
start = 350, span = 20, end = 10, angle = 5
angle < start, so angle = 365
virtual void PointerMoved(Vector);
virtual void PointerReleased(void);
virtual void Enumerate(FILE *);
+ virtual QRectF Extents(void);
// virtual Object * Copy(void);
+// virtual ObjectType Type(void);
private:
bool AngleInArcSpan(double angle);
Circle::Circle(Vector p1, double r, Object * p/*= NULL*/): Object(p1, p), radius(r),
draggingEdge(false), draggingCenter(false), hitCenter(false), hitCircle(false)
{
+ type = OTCircle;
}
}
+/*virtual*/ QRectF Circle::Extents(void)
+{
+ return QRectF(QPointF(position.x - radius, position.y - radius), QPointF(position.x + radius, position.y + radius));
+}
+
+
+#if 0
+/*virtual*/ ObjectType Circle::Type(void)
+{
+ return OTCircle;
+}
+#endif
+
+
bool Circle::HitTest(Point point)
{
SaveState();
virtual void PointerMoved(Vector);
virtual void PointerReleased(void);
virtual void Enumerate(FILE *);
+ virtual QRectF Extents(void);
// virtual Object * Copy(void);
+// virtual ObjectType Type(void);
protected:
bool HitTest(Point);
#include <QtGui>
#include "dimension.h"
+#include "painter.h"
Container::Container(Vector p1, Object * p/*= NULL*/): Object(p1, p),
dragging(false), draggingHandle1(false), draggingHandle2(false)//, needUpdate(false)
{
+ type = OTContainer;
}
{
// Use overloaded assignment operator
*this = copy;
+ type = OTContainer;
}
/*virtual*/ void Container::Draw(Painter * painter)
{
- for(int i=0; i<(int)objects.size(); i++)
+ QRectF boundary;
+
+ for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
+// for(int i=0; i<(int)objects.size(); i++)
{
+#if 0
//printf("Container: About to draw (object = $%X)\n", objects[i]);
objects[i]->Draw(painter);
+ bounds = bounds.united(objects[i].RectangularExtents());
+#else
+ (*i)->Draw(painter);
+ boundary = boundary.united((*i)->Extents());
+#endif
+ }
+
+ if (state == OSSelected)
+ {
+ painter->SetPen(QPen(Qt::magenta, 2.0, Qt::DashLine));
+ painter->SetBrush(QBrush(Qt::NoBrush));
+ painter->DrawRect(boundary);
}
}
}
+#if 0
+/*virtual*/ ObjectType Container::Type(void)
+{
+ return OTContainer;
+}
+#endif
+
+
+void Container::Delete(Object * objectToDelete)
+{
+#if 0
+ //this is wrong
+ for(unsigned int i=0; i<objects.size(); i++)
+ {
+ if (objects[i] == objectToDelete)
+ {
+ objects.erase(i);
+ delete objectToDelete;
+ return;
+ }
+ }
+#else
+ std::vector<Object *>::iterator i = objects.begin();
+
+ while (i != objects.end())
+ {
+ if (*i == objectToDelete)
+ {
+ objects.erase(i);
+ delete objectToDelete;
+ return;
+ }
+
+ i++;
+ }
+#endif
+}
+
+
void Container::Clear(void)
{
// No memory leaks!
}
+void Container::SelectAll(void)
+{
+ for(unsigned int i=0; i<objects.size(); i++)
+ objects[i]->state = OSSelected;
+}
+
+
+void Container::DeselectAll(void)
+{
+ for(unsigned int i=0; i<objects.size(); i++)
+ objects[i]->state = OSInactive;
+}
+
+
+int Container::ItemsSelected(void)
+{
+ int selected = 0;
+
+ for(uint i=0; i<objects.size(); i++)
+ if (objects[i]->state == OSSelected)
+ selected++;
+
+ return selected;
+}
+
+
+/*ObjectType Container::SelectedItemType(unsigned int index)
+{
+ if (index >= objects.size())
+ return OTNone;
+
+ return objects[index]->Type();
+}*/
+
+
+Object * Container::SelectedItem(unsigned int index)
+{
+// if (index >= objects.size())
+// return NULL;
+
+ unsigned int selectedIndex = 0;
+
+ for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
+ {
+ if ((*i)->state == OSSelected)
+ {
+ if (selectedIndex == index)
+ return *i;
+ else
+ selectedIndex++;
+ }
+ }
+
+ return NULL;
+}
+
+
+void Container::MoveContentsTo(Container * newContainer)
+{
+ // Sanity check
+ if (newContainer == NULL)
+ return;
+
+ // Shuffle the contents of this container to the new one
+ for(unsigned int i=0; i<objects.size(); i++)
+ newContainer->Add(objects[i]);
+
+ // & clear our vector
+ objects.clear();
+}
+
+
+void Container::MoveSelectedContentsTo(Container * newContainer)
+{
+ // Sanity check
+ if (newContainer == NULL)
+ return;
+
+ // Shuffle the contents of this container to the new one
+ for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end();)
+ {
+ if ((*i)->state != OSSelected)
+ {
+ i++;
+ continue;
+ }
+
+ newContainer->Add(*i);
+ objects.erase(i);
+ }
+}
+
+
/*virtual*/ void Container::Enumerate(FILE * file)
{
// Only put "CONTAINER" markers if *not* the top level container
virtual bool NeedsUpdate(void);
virtual void Add(Object *);
virtual void Enumerate(FILE *);
+// virtual ObjectType Type(void);
+ void Delete(Object *);
void Clear(void);
+ void SelectAll(void);
+ void DeselectAll(void);
+ int ItemsSelected(void);
+// ObjectType SelectedItemType(unsigned int);
+ Object * SelectedItem(unsigned int);
+// void ReparentContentsTo(Object *);
+ void MoveContentsTo(Container *);
+ void MoveSelectedContentsTo(Container *);
protected:
Vector oldPoint; // Used for dragging
Dimension::Dimension(Vector p1, Vector p2, DimensionType dt/*= DTLinear*/ ,Object * p/*= NULL*/):
Object(p1, p), endpoint(p2),
dragging(false), draggingHandle1(false), draggingHandle2(false),
- length(p2.Magnitude()), type(dt), point1(NULL), point2(NULL)
+ length(p2.Magnitude()), dimensionType(dt), point1(NULL), point2(NULL)
{
+ type = OTDimension;
}
// This is bad, p1 & p2 could be NULL, causing much consternation...
Dimension::Dimension(Connection p1, Connection p2, DimensionType dt/*= DTLinear*/ , Object * p/*= NULL*/):
dragging(false), draggingHandle1(false), draggingHandle2(false),
- length(0), type(dt), point1(p1), point2(p2)
+ length(0), dimensionType(dt), point1(p1), point2(p2)
{
+ type = OTDimension;
}
}
+/*virtual*/ QRectF Dimension::Extents(void)
+{
+ Point p1 = position;
+ Point p2 = endpoint;
+
+ if (point1.object)
+ p1 = point1.object->GetPointAtParameter(point1.t);
+
+ if (point2.object)
+ p2 = point2.object->GetPointAtParameter(point2.t);
+
+ return QRectF(QPointF(p1.x, p1.y), QPointF(p2.x, p2.y));
+}
+
+
+#if 0
+/*virtual*/ ObjectType Dimension::Type(void)
+{
+ return OTDimension;
+}
+#endif
+
+
void Dimension::FlipSides(void)
{
#if 0
virtual void Connect(Object *, double);
virtual void Disconnect(Object *, double);
virtual void DisconnectAll(Object *);
+ virtual QRectF Extents(void);
+// virtual ObjectType Type(void);
void FlipSides(void);
protected:
bool draggingHandle2;
bool objectWasDragged;
double length;
- DimensionType type;
+ DimensionType dimensionType;
// We use these in lieu of the built-in connected[] array; no reason to
// do it this way especially
*/
-enum ObjectType { OTContainer, OTContainerEnd, OTLine, OTCircle, OTArc, OTDimension,
- OTPolygon, OTText, OTImage, OTBlock, OTEndOfFile };
+enum ObjectTypeFile { OTFContainer, OTFContainerEnd, OTFLine, OTFCircle, OTFArc, OTFDimension,
+ OTFPolygon, OTFText, OTFImage, OTFBlock, OTFEndOfFile };
/*static*/ bool FileIO::SaveAtnsFile(FILE * file, Container * object)
// where the return value tells if it's a valid object, Object * returns the
// reconstructed object and ObjectType * returns the object type.
- if (objectType == OTEndOfFile)
+ if (objectType == OTFEndOfFile)
{
printf("Load: container size = %li\n", drawing->objects.size());
return true;
}
- else if (objectType == OTContainer)
+ else if (objectType == OTFContainer)
{
containerStack.push_back(currentTopContainer);
currentTopContainer = new Container(Vector(0, 0), currentTopContainer);
}
- else if (objectType == OTContainerEnd)
+ else if (objectType == OTFContainerEnd)
{
Container * containerToAdd = currentTopContainer;
currentTopContainer = containerStack.back();
fscanf(file, "(%lf,%lf) (%lf,%lf)", &v1.x, &v1.y, &v2.x, &v2.y);
//printf(" Number of params recognized: %i\n", n);
*object = new Line(v1, v2, parent);
- *objectType = OTLine;
+ *objectType = OTFLine;
}
else if (strcmp(buffer, "CIRCLE") == 0)
{
double r;
fscanf(file, "(%lf,%lf) %lf", &v.x, &v.y, &r);
*object = new Circle(v, r, parent);
- *objectType = OTCircle;
+ *objectType = OTFCircle;
}
else if (strcmp(buffer, "ARC") == 0)
{
double r, a1, a2;
fscanf(file, "(%lf,%lf) %lf, %lf, %lf", &v.x, &v.y, &r, &a1, &a2);
*object = new Arc(v, r, a1, a2, parent);
- *objectType = OTArc;
+ *objectType = OTFArc;
}
else if (strcmp(buffer, "DIMENSION") == 0)
{
DimensionType type;
fscanf(file, "(%lf,%lf) (%lf,%lf) %i", &v1.x, &v1.y, &v2.x, &v2.y, &type);
*object = new Dimension(v1, v2, type, parent);
- *objectType = OTDimension;
+ *objectType = OTFDimension;
}
else if (strcmp(buffer, "CONTAINER") == 0)
{
recognized = true;
- *objectType = OTContainer;
+ *objectType = OTFContainer;
}
else if (strcmp(buffer, "ENDCONTAINER") == 0)
{
recognized = true;
- *objectType = OTContainerEnd;
+ *objectType = OTFContainerEnd;
}
else if (strcmp(buffer, "END") == 0)
{
recognized = true;
- *objectType = OTEndOfFile;
+ *objectType = OTFEndOfFile;
}
return recognized;
#include "painter.h"
-Line::Line(Vector p1, Vector p2, Object * p/*= NULL*/): Object(p1, p), endpoint(p2),
+Line::Line(Vector p1, Vector p2, Object * p/*= NULL*/): Object(p1, p),
+ /*type(OTLine),*/ endpoint(p2),
draggingLine(false), draggingHandle1(false), draggingHandle2(false), //needUpdate(false),
length(Vector::Magnitude(p2, p1)), angle(Vector(endpoint - position).Unit()),
hitPoint1(false), hitPoint2(false), hitLine(false)
{
+ type = OTLine;
}
// state = OSInactive;
oldPoint = point;
draggingLine = true;
+
+ // Toggle selected state if CTRL held
+ if (qApp->keyboardModifiers() == Qt::ControlModifier)
+ state = OSInactive;
+
return true;
}
}
+ // If CTRL is held, then we bypass the "turn off" code. Still didn't hit
+ // *this* object though. :-)
+ if (qApp->keyboardModifiers() == Qt::ControlModifier)
+ return false;
+
// If we got here, we clicked on nothing, so set the object to inactive.
// (Once we can read key modifiers, we can override this to allow multiple selection.)
state = OSInactive;
}
+/*virtual*/ QRectF Line::Extents(void)
+{
+ QRectF rect(QPointF(position.x, position.y), QPointF(endpoint.x, endpoint.y));
+ return rect.normalized();
+}
+
+
+#if 0
+/*virtual*/ ObjectType Line::Type(void)
+{
+ return OTLine;
+}
+#endif
+
+
void Line::SetDimensionOnLine(Dimension * dimension/*=NULL*/)
{
// If they don't pass one in, create it for the caller.
virtual void Enumerate(FILE *);
virtual Object * Copy(void);
virtual Vector GetPointAtParameter(double parameter);
+ virtual QRectF Extents(void);
+// virtual ObjectType Type(void);
void SetDimensionOnLine(Dimension * d = 0);
Object * FindAttachedDimension(void);
bool Object::deleteActive = false;
bool Object::dimensionActive = false;
bool Object::snapToGrid = true;
+//snapToPoints all well here?
+bool Object::ignoreClicks = false;
+bool Object::dontMove = false;
-Object::Object(): position(Vector(0, 0)), parent(0), state(OSInactive), oldState(OSInactive),
- needUpdate(false)//, attachedDimension(0)
+Object::Object(): position(Vector(0, 0)), parent(0), type(OTObject),
+ state(OSInactive), oldState(OSInactive), needUpdate(false)
+ //, attachedDimension(0)
{
}
}
+/*virtual*/ QRectF Object::Extents(void)
+{
+ // Generic object returns an empty rect...
+ return QRectF();
+}
+
+
+#if 0
+/*virtual*/ ObjectType Object::Type(void)
+{
+ return OTObject;
+}
+#endif
+
+
ObjectState Object::GetState(void)
{
return state;
#include <vector> // This is a container
#include "vector.h" // This is the mathematical construct
#include "connection.h"
+#include <QRectF>
class Painter;
class QFont;
//class FILE;
enum ObjectState { OSInactive, OSSelected };
+enum ObjectType { OTNone, OTObject, OTLine, OTCircle, OTArc, OTDimension, OTEllipse, OTContainer };
class Object
{
virtual void Connect(Object *, double);
virtual void Disconnect(Object *, double);
virtual void DisconnectAll(Object *);
+ virtual QRectF Extents(void);
+// virtual ObjectType Type(void);// = 0; // Pure virtual, must be implemented
ObjectState GetState(void);
void Reparent(Object *);
// Dimension * GetAttachedDimension(void);
Object * parent;
// Pen pen;
// Fill fill;
+ public:
+ ObjectType type;
ObjectState state;
+ protected:
ObjectState oldState;
bool needUpdate;
// Dimension * attachedDimension;
static bool deleteActive;
static bool dimensionActive;
static bool snapToGrid;
+ static bool ignoreClicks;
+ static bool dontMove;
};
#endif // __OBJECT_H__
{
}
+
Painter::~Painter()
{
}
+
Vector Painter::CartesianToQtCoords(Vector v)
{
// Convert regular Cartesian coordinates to the inverted Y-axis Qt coordinates
return Vector((v.x - origin.x) * zoom, screenSize.y - ((v.y - origin.y) * zoom));
}
+
Vector Painter::QtToCartesianCoords(Vector v)
{
// Convert screen location, with inverted Y-axis coordinates, to regular
*/
}
+
void Painter::SetRenderHint(int hint)
{
if (!painter)
painter->setRenderHint((QPainter::RenderHint)hint);
}
+
void Painter::SetBrush(QBrush brush)
{
if (!painter)
painter->setBrush(brush);
}
+
void Painter::SetFont(QFont font)
{
if (!painter)
painter->setFont(font);
}
+
void Painter::SetPen(QPen pen)
{
if (!painter)
painter->setPen(pen);
}
+
void Painter::DrawAngledText(Vector center, double angle, QString text)
{
if (!painter)
painter->restore();
}
+
void Painter::DrawArc(Vector center, double radius, double startAngle, double span)
{
center = CartesianToQtCoords(center);
painter->drawArc(rectangle, angle1, angle2);
}
+
void Painter::DrawEllipse(Vector center, double axis1, double axis2)
{
// Need to multiply scalar quantities by the zoom factor as well...
painter->drawEllipse(QPointF(center.x, center.y), axis1 * zoom, axis2 * zoom);
}
+
// This function is for drawing object handles without regard for zoom level;
// we don't want our object handle size to depend on the zoom level!
void Painter::DrawHandle(Vector center)
painter->drawEllipse(QPointF(center.x, center.y), 4.0, 4.0);
}
+
void Painter::DrawLine(int x1, int y1, int x2, int y2)
{
if (!painter)
painter->drawLine(v1.x, v1.y, v2.x, v2.y);
}
+
void Painter::DrawLine(Vector v1, Vector v2)
{
if (!painter)
painter->drawLine(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
}
+
void Painter::DrawPoint(int x, int y)
{
if (!painter)
painter->drawPoint(v.x, v.y);
}
+
void Painter::DrawRoundedRect(QRectF rect, double radiusX, double radiusY)
{
if (!painter)
painter->drawRoundedRect(rect, radiusX, radiusY);
}
+
+void Painter::DrawRect(QRectF rect)
+{
+ if (!painter)
+ return;
+
+ Vector v1 = CartesianToQtCoords(Vector(rect.x(), rect.y()));
+ Vector v2 = CartesianToQtCoords(Vector(rect.right(), rect.bottom()));
+ QRectF screenRect(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
+ painter->drawRect(screenRect);
+}
+
+
void Painter::DrawText(QRectF rect, int type, QString text)
{
if (!painter)
painter->drawText(rect, (Qt::AlignmentFlag)type, text);
}
+
void Painter::DrawArrowhead(Vector head, Vector tail)
{
QPolygonF arrow;
void DrawLine(Vector, Vector);
void DrawPoint(int, int);
void DrawRoundedRect(QRectF, double, double);
+ void DrawRect(QRectF);
void DrawText(QRectF, int, QString);
void DrawArrowhead(Vector, Vector);