From: Shamus Hammons Date: Thu, 4 Jul 2013 04:07:33 +0000 (-0500) Subject: Added 1st stab at grouping capability. X-Git-Url: http://shamusworld.gotdns.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ba6723b86d8dd67ebc7b11b245de3e7ff64f06b1;p=architektonas Added 1st stab at grouping capability. --- diff --git a/src/applicationwindow.cpp b/src/applicationwindow.cpp index 2038731..a9108eb 100644 --- a/src/applicationwindow.cpp +++ b/src/applicationwindow.cpp @@ -385,6 +385,52 @@ void ApplicationWindow::Settings(void) 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(); } diff --git a/src/arc.cpp b/src/arc.cpp index a4efa39..c29e38d 100644 --- a/src/arc.cpp +++ b/src/arc.cpp @@ -19,9 +19,11 @@ #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; } @@ -292,6 +294,14 @@ but this is actually more compact and cleaner. state = oldState; } + +/*virtual*/ QRectF Arc::Extents(void) +{ +#warning "!!! Arc extents not calculated !!!" + return QRectF(); +} + + #if 0 /*virtual*/ bool Arc::NeedsUpdate(void) { @@ -300,6 +310,14 @@ but this is actually more compact and cleaner. #endif +#if 0 +/*virtual*/ ObjectType Arc::Type(void) +{ + return OTArc; +} +#endif + + /* start = 350, span = 20, end = 10, angle = 5 angle < start, so angle = 365 diff --git a/src/arc.h b/src/arc.h index 1f554ba..587d0eb 100644 --- a/src/arc.h +++ b/src/arc.h @@ -15,7 +15,9 @@ class Arc: public Object 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); diff --git a/src/circle.cpp b/src/circle.cpp index 0780f62..d6ed306 100644 --- a/src/circle.cpp +++ b/src/circle.cpp @@ -22,6 +22,7 @@ Circle::Circle(Vector p1, double r, Object * p/*= NULL*/): Object(p1, p), radius(r), draggingEdge(false), draggingCenter(false), hitCenter(false), hitCircle(false) { + type = OTCircle; } @@ -114,6 +115,20 @@ Circle::~Circle() } +/*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(); diff --git a/src/circle.h b/src/circle.h index 57cde7b..f420121 100644 --- a/src/circle.h +++ b/src/circle.h @@ -15,7 +15,9 @@ class Circle: public Object 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); diff --git a/src/container.cpp b/src/container.cpp index a8cf0ad..7263663 100644 --- a/src/container.cpp +++ b/src/container.cpp @@ -17,11 +17,13 @@ #include #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; } @@ -30,6 +32,7 @@ Container::Container(const Container & copy): Object(copy.position, copy.parent) { // Use overloaded assignment operator *this = copy; + type = OTContainer; } @@ -65,10 +68,26 @@ Container & Container::operator=(const Container & from) /*virtual*/ void Container::Draw(Painter * painter) { - for(int i=0; i<(int)objects.size(); i++) + QRectF boundary; + + for(std::vector::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); } } @@ -269,6 +288,45 @@ printf("Container: Added object (=$%X). size = %li\n", object, objects.size()); } +#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::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! @@ -281,6 +339,99 @@ printf("Container: Deleting object ($%X)...\n", objects[0]); } +void Container::SelectAll(void) +{ + for(unsigned int i=0; istate = OSSelected; +} + + +void Container::DeselectAll(void) +{ + for(unsigned int i=0; istate = OSInactive; +} + + +int Container::ItemsSelected(void) +{ + int selected = 0; + + for(uint i=0; istate == 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::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; iAdd(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::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 diff --git a/src/container.h b/src/container.h index e7bf355..a532289 100644 --- a/src/container.h +++ b/src/container.h @@ -21,7 +21,17 @@ class Container: public Object 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 diff --git a/src/dimension.cpp b/src/dimension.cpp index 2107d67..29eaf69 100644 --- a/src/dimension.cpp +++ b/src/dimension.cpp @@ -22,16 +22,18 @@ 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; } @@ -360,6 +362,29 @@ about keeping track of old states... } +/*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 diff --git a/src/dimension.h b/src/dimension.h index 4fbcaa8..efd5ea6 100644 --- a/src/dimension.h +++ b/src/dimension.h @@ -24,6 +24,8 @@ class Dimension: public Object 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: @@ -36,7 +38,7 @@ class Dimension: public Object 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 diff --git a/src/fileio.cpp b/src/fileio.cpp index 4a1c25f..b64e5bd 100644 --- a/src/fileio.cpp +++ b/src/fileio.cpp @@ -103,8 +103,8 @@ relationship between them by virtue of their connection. */ -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) @@ -157,17 +157,17 @@ enum ObjectType { OTContainer, OTContainerEnd, OTLine, OTCircle, OTArc, OTDimens // 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(); @@ -203,7 +203,7 @@ printf("Load: Adding object. Container size = %li (%li)\n", drawing->objects.siz 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) { @@ -212,7 +212,7 @@ printf("Load: Adding object. Container size = %li (%li)\n", drawing->objects.siz 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) { @@ -221,7 +221,7 @@ printf("Load: Adding object. Container size = %li (%li)\n", drawing->objects.siz 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) { @@ -230,22 +230,22 @@ printf("Load: Adding object. Container size = %li (%li)\n", drawing->objects.siz 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; diff --git a/src/line.cpp b/src/line.cpp index d37106e..5f3a611 100644 --- a/src/line.cpp +++ b/src/line.cpp @@ -23,11 +23,13 @@ #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; } @@ -230,10 +232,20 @@ Like so: // 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; @@ -446,6 +458,21 @@ you then *definitely* do not want them to have the same reference number. } +/*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. diff --git a/src/line.h b/src/line.h index b7d32f2..bb4d5c1 100644 --- a/src/line.h +++ b/src/line.h @@ -20,6 +20,8 @@ class Line: public Object 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); diff --git a/src/object.cpp b/src/object.cpp index 925d9b4..197fa8d 100644 --- a/src/object.cpp +++ b/src/object.cpp @@ -26,10 +26,14 @@ int Object::viewportHeight = 0; 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) { } @@ -162,6 +166,21 @@ printf("Object: Destroyed!\n"); } +/*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; diff --git a/src/object.h b/src/object.h index 2e2efff..34063a7 100644 --- a/src/object.h +++ b/src/object.h @@ -5,6 +5,7 @@ #include // This is a container #include "vector.h" // This is the mathematical construct #include "connection.h" +#include class Painter; class QFont; @@ -12,6 +13,7 @@ class Dimension; //class FILE; enum ObjectState { OSInactive, OSSelected }; +enum ObjectType { OTNone, OTObject, OTLine, OTCircle, OTArc, OTDimension, OTEllipse, OTContainer }; class Object { @@ -36,6 +38,8 @@ 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); @@ -55,7 +59,10 @@ class Object Object * parent; // Pen pen; // Fill fill; + public: + ObjectType type; ObjectState state; + protected: ObjectState oldState; bool needUpdate; // Dimension * attachedDimension; @@ -69,6 +76,8 @@ class Object static bool deleteActive; static bool dimensionActive; static bool snapToGrid; + static bool ignoreClicks; + static bool dontMove; }; #endif // __OBJECT_H__ diff --git a/src/painter.cpp b/src/painter.cpp index 73e5bf6..7d146b1 100644 --- a/src/painter.cpp +++ b/src/painter.cpp @@ -27,10 +27,12 @@ Painter::Painter(QPainter * p/*= NULL*/): painter(p) { } + Painter::~Painter() { } + Vector Painter::CartesianToQtCoords(Vector v) { // Convert regular Cartesian coordinates to the inverted Y-axis Qt coordinates @@ -38,6 +40,7 @@ Vector Painter::CartesianToQtCoords(Vector v) 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 @@ -66,6 +69,7 @@ The way we calculate the Cartesian to Qt shows the 2nd (origin is cartesian) to */ } + void Painter::SetRenderHint(int hint) { if (!painter) @@ -74,6 +78,7 @@ void Painter::SetRenderHint(int hint) painter->setRenderHint((QPainter::RenderHint)hint); } + void Painter::SetBrush(QBrush brush) { if (!painter) @@ -82,6 +87,7 @@ void Painter::SetBrush(QBrush brush) painter->setBrush(brush); } + void Painter::SetFont(QFont font) { if (!painter) @@ -90,6 +96,7 @@ void Painter::SetFont(QFont font) painter->setFont(font); } + void Painter::SetPen(QPen pen) { if (!painter) @@ -98,6 +105,7 @@ void Painter::SetPen(QPen pen) painter->setPen(pen); } + void Painter::DrawAngledText(Vector center, double angle, QString text) { if (!painter) @@ -143,6 +151,7 @@ void Painter::DrawAngledText(Vector center, double angle, QString text) painter->restore(); } + void Painter::DrawArc(Vector center, double radius, double startAngle, double span) { center = CartesianToQtCoords(center); @@ -155,6 +164,7 @@ void Painter::DrawArc(Vector center, double radius, double startAngle, double sp 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... @@ -162,6 +172,7 @@ void Painter::DrawEllipse(Vector center, double axis1, double axis2) 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) @@ -171,6 +182,7 @@ 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) @@ -181,6 +193,7 @@ void Painter::DrawLine(int x1, int y1, int x2, int y2) painter->drawLine(v1.x, v1.y, v2.x, v2.y); } + void Painter::DrawLine(Vector v1, Vector v2) { if (!painter) @@ -191,6 +204,7 @@ void Painter::DrawLine(Vector v1, Vector v2) painter->drawLine(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y)); } + void Painter::DrawPoint(int x, int y) { if (!painter) @@ -200,6 +214,7 @@ void Painter::DrawPoint(int x, int y) painter->drawPoint(v.x, v.y); } + void Painter::DrawRoundedRect(QRectF rect, double radiusX, double radiusY) { if (!painter) @@ -208,6 +223,19 @@ void Painter::DrawRoundedRect(QRectF rect, double radiusX, double radiusY) 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) @@ -216,6 +244,7 @@ void Painter::DrawText(QRectF rect, int type, QString text) painter->drawText(rect, (Qt::AlignmentFlag)type, text); } + void Painter::DrawArrowhead(Vector head, Vector tail) { QPolygonF arrow; diff --git a/src/painter.h b/src/painter.h index a4d9dc0..aeda1fd 100644 --- a/src/painter.h +++ b/src/painter.h @@ -26,6 +26,7 @@ class Painter 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);