diff --git a/resources/images/toolPalette/aristoTool.png b/resources/images/toolPalette/aristoTool.png new file mode 100644 index 00000000..225c0b1e Binary files /dev/null and b/resources/images/toolPalette/aristoTool.png differ diff --git a/resources/library/applications/OpenStreetMap.wgt/index.html b/resources/library/applications/OpenStreetMap.wgt/index.html index 5b7da167..750e1a6e 100755 --- a/resources/library/applications/OpenStreetMap.wgt/index.html +++ b/resources/library/applications/OpenStreetMap.wgt/index.html @@ -68,7 +68,7 @@ * along with this program. If not, see . */ - /* API identifier */ + /* Geonames API identifier */ var geonamesUser = "yimgo"; /* map variable will be used to manipulate the map. This will be initialized like an OpenLayers.Map object. */ @@ -149,6 +149,17 @@ }); } + function importData(data) + { + map.setCenter(new OpenLayers.LonLat(data["center"]["lon"], data["center"]["lat"]), data["zoom"]); + } + + function exportData() + { + if (window.sankore) + sankore.setPreference("osm", JSON.stringify({center: map.getCenter(), zoom: map.getZoom()})); + } + window.onload = function() { map = new OpenLayers.Map({ div: "map" @@ -198,7 +209,21 @@ map.setBaseLayer(newLayer); return false; - }); + }); + + /* importing state from Sankoré preferences */ + if (window.sankore) { + if (sankore.preference("osm","")) { + importData(JSON.parse(sankore.preference("osm",""))); + } + } + + /* exporting state when receiving a leave event */ + if (window.widget) { + window.widget.onleave = function() { + exportData(); + } + } }; --> diff --git a/resources/sankore.qrc b/resources/sankore.qrc index 620e6219..fcefa2e8 100644 --- a/resources/sankore.qrc +++ b/resources/sankore.qrc @@ -165,6 +165,7 @@ images/toolPalette/triangleTool.png images/toolPalette/protractorTool.png images/toolPalette/compassTool.png + images/toolPalette/aristoTool.png images/toolPalette/maskTool.png images/toolPalette/magnifierTool.png images/extraPalette/blackout.png diff --git a/src/board/UBBoardController.cpp b/src/board/UBBoardController.cpp index 57d753d7..949a5b97 100644 --- a/src/board/UBBoardController.cpp +++ b/src/board/UBBoardController.cpp @@ -1363,6 +1363,11 @@ UBItem *UBBoardController::downloadFinished(bool pSuccess, QUrl sourceUrl, QStri mActiveScene->addMask(pPos); UBDrawingController::drawingController()->setStylusTool(UBStylusTool::Selector); } + else if (sourceUrl.toString() == UBToolsManager::manager()->aristo.id) + { + mActiveScene->addAristo(pPos); + UBDrawingController::drawingController()->setStylusTool(UBStylusTool::Selector); + } else { showMessage(tr("Unknown tool type %1").arg(sourceUrl.toString())); diff --git a/src/board/UBBoardView.cpp b/src/board/UBBoardView.cpp index a654c765..24d03735 100644 --- a/src/board/UBBoardView.cpp +++ b/src/board/UBBoardView.cpp @@ -61,6 +61,7 @@ #include "tools/UBGraphicsCache.h" #include "tools/UBGraphicsTriangle.h" #include "tools/UBGraphicsProtractor.h" +#include "tools/UBGraphicsAristo.h" #include "core/memcheck.h" @@ -521,6 +522,7 @@ Here we determines cases when items should to get mouse press event at pressing case UBGraphicsTriangle::Type: case UBGraphicsCompass::Type: case UBGraphicsCache::Type: + case UBGraphicsAristo::Type: return true; case UBGraphicsDelegateFrame::Type: diff --git a/src/core/UB.h b/src/core/UB.h index 716df59c..aeb619d2 100644 --- a/src/core/UB.h +++ b/src/core/UB.h @@ -145,6 +145,7 @@ struct UBGraphicsItemType TriangleItemType, MagnifierItemType, cacheItemType, + AristoItemType, groupContainerType, ToolWidgetItemType, GraphicsWidgetItemType, diff --git a/src/domain/UBGraphicsItemDelegate.cpp b/src/domain/UBGraphicsItemDelegate.cpp index 0b23509f..a1d57202 100644 --- a/src/domain/UBGraphicsItemDelegate.cpp +++ b/src/domain/UBGraphicsItemDelegate.cpp @@ -58,6 +58,7 @@ DelegateButton::DelegateButton(const QString & fileName, QGraphicsItem* pDelegat { setAcceptedMouseButtons(Qt::LeftButton); setData(UBGraphicsItemData::ItemLayerType, QVariant(UBItemLayerType::Control)); + setCacheMode(QGraphicsItem::NoCache); /* because of SANKORE-1017: this allows pixmap to be refreshed when grabbing window, thus teacher screen is synchronized with main screen. */ } DelegateButton::~DelegateButton() diff --git a/src/domain/UBGraphicsScene.cpp b/src/domain/UBGraphicsScene.cpp index cc98b307..22ff3379 100644 --- a/src/domain/UBGraphicsScene.cpp +++ b/src/domain/UBGraphicsScene.cpp @@ -39,6 +39,7 @@ #include "tools/UBGraphicsTriangle.h" #include "tools/UBGraphicsCurtainItem.h" #include "tools/UBGraphicsCache.h" +#include "tools/UBGraphicsAristo.h" #include "document/UBDocumentProxy.h" @@ -1937,6 +1938,22 @@ void UBGraphicsScene::addCompass(QPointF center) compass->setVisible(true); } +void UBGraphicsScene::addAristo(QPointF center) +{ + UBGraphicsAristo* aristo = new UBGraphicsAristo(); + mTools << aristo; + + aristo->setData(UBGraphicsItemData::ItemLayerType, QVariant(UBItemLayerType::Tool)); + + addItem(aristo); + + QPointF itemSceneCenter = aristo->sceneBoundingRect().center(); + aristo->moveBy(center.x() - itemSceneCenter.x(), center.y() - itemSceneCenter.y()); + + aristo->setVisible(true); + setModified(true); +} + void UBGraphicsScene::addCache() { UBGraphicsCache* cache = new UBGraphicsCache(); diff --git a/src/domain/UBGraphicsScene.h b/src/domain/UBGraphicsScene.h index 1c9c847f..6d496369 100644 --- a/src/domain/UBGraphicsScene.h +++ b/src/domain/UBGraphicsScene.h @@ -216,6 +216,7 @@ class UBGraphicsScene: public UBCoreGraphicsScene, public UBItem void addCompass(QPointF center); void addTriangle(QPointF center); void addMagnifier(UBMagnifierParams params); + void addAristo(QPointF center); void addMask(const QPointF ¢er = QPointF()); void addCache(); diff --git a/src/frameworks/UBPlatformUtils_linux.cpp b/src/frameworks/UBPlatformUtils_linux.cpp index 8c10c4c9..5566116a 100644 --- a/src/frameworks/UBPlatformUtils_linux.cpp +++ b/src/frameworks/UBPlatformUtils_linux.cpp @@ -17,6 +17,7 @@ #include +#include #include #include diff --git a/src/tools/UBGraphicsAristo.cpp b/src/tools/UBGraphicsAristo.cpp new file mode 100644 index 00000000..61762774 --- /dev/null +++ b/src/tools/UBGraphicsAristo.cpp @@ -0,0 +1,828 @@ +/* + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "UBGraphicsAristo.h" +#include "board/UBBoardController.h" +#include "board/UBDrawingController.h" +#include "core/UBApplication.h" +#include "domain/UBGraphicsScene.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core/memcheck.h" + +const QRectF UBGraphicsAristo::sDefaultRect = QRectF(0, 0, 800, 500); +const UBGraphicsAristo::Orientation UBGraphicsAristo::sDefaultOrientation = UBGraphicsAristo::Bottom; + +UBGraphicsAristo::UBGraphicsAristo() + : UBAbstractDrawRuler() + , QGraphicsPathItem() + , mMarking(false) + , mResizing(false) + , mRotating(false) + , mOrientation(Undefined) + , mRotatedAngle(0) + , mMarkerAngle(0) + , mStartAngle(0) + , mSpan(180) + , mHFlipSvgItem(0) + , mMarkerSvgItem(0) + , mResizeSvgItem(0) + , mRotateSvgItem(0) +{ + mHFlipSvgItem = new QGraphicsSvgItem(":/images/vflipTool.svg", this); + mHFlipSvgItem->setVisible(false); + mHFlipSvgItem->setData(UBGraphicsItemData::ItemLayerType, QVariant(UBItemLayerType::Control)); + + mResizeSvgItem = new QGraphicsSvgItem(":/images/resizeTool.svg", this); + mResizeSvgItem->setVisible(false); + mResizeSvgItem->setData(UBGraphicsItemData::ItemLayerType, QVariant(UBItemLayerType::Control)); + + mRotateSvgItem = new QGraphicsSvgItem(":/images/rotateTool.svg", this); + mRotateSvgItem->setVisible(false); + mRotateSvgItem->setData(UBGraphicsItemData::ItemLayerType, QVariant(UBItemLayerType::Control)); + + mMarkerSvgItem = new QGraphicsSvgItem(":/images/angleMarker.svg", this); + mMarkerSvgItem->setVisible(false); + mMarkerSvgItem->setData(UBGraphicsItemData::ItemLayerType, QVariant(UBItemLayerType::Tool)); + mMarkerSvgItem->setVisible(true); + + create(*this); + setBoundingRect(sDefaultRect); + setOrientation(sDefaultOrientation); +} + +UBGraphicsAristo::~UBGraphicsAristo() +{ + /* NOOP */ +} + +/* + * setOrientation() modify the tool orientation. + * makeGeometryChange() is called so points are recomputed, control items are positionnated and shape is determined according to this modification. + */ +void UBGraphicsAristo::setOrientation(Orientation orientation) +{ + mOrientation = orientation; + makeGeometryChange(); +} + +/* calculatePoints() is used to calculate polygon's apexes coordinates. + * This function handles orientation changes too. + */ +void UBGraphicsAristo::calculatePoints() +{ + switch (mOrientation) { + case Bottom: + C.setX(boundingRect().center().x()); + C.setY(boundingRect().bottom()); + + A.setX(boundingRect().left()); + A.setY(boundingRect().bottom() - boundingRect().width() / 2); + + B.setX(boundingRect().right()); + B.setY(boundingRect().bottom() - boundingRect().width() / 2); + break; + case Top: + C.setX(boundingRect().center().x()); + C.setY(boundingRect().top()); + + A.setX(boundingRect().left()); + A.setY(boundingRect().top() + boundingRect().width() / 2); + + B.setX(boundingRect().right()); + B.setY(boundingRect().top() + boundingRect().width() / 2); + break; + default: + break; + } +} + +/* + * setItemsPos() places control items according to A, B and C positions. + * Call this function after A, B or C position modification, mostly after calling calculatePoints(). + * These positions has to be set when calling setPath() to allow hover events on items which are not into the main polygon. + */ +void UBGraphicsAristo::setItemsPos() +{ + mCloseSvgItem->setPos(closeButtonRect().topLeft() + rotationCenter()); + mHFlipSvgItem->setPos(hFlipRect().topLeft() + rotationCenter()); + mRotateSvgItem->setPos(rotateRect().topLeft() + rotationCenter()); + mResizeSvgItem->setPos(resizeButtonRect().topLeft() + rotationCenter()); + mMarkerSvgItem->setPos(markerButtonRect().topLeft() + rotationCenter()); +} + +/* + * determinePath() modify the shape according to apexes coordinates and control item positions. + * This is useful when orientation is modified. + * Returns the painter path corresponding to object parameters. + */ +QPainterPath UBGraphicsAristo::determinePath() +{ + QPainterPath path; + + QPolygonF polygon; + polygon << A << B << C; + path.addPolygon(polygon); + + path.addPath(mResizeSvgItem->shape().translated(mResizeSvgItem->pos())); + path.addPath(mMarkerSvgItem->shape().translated(mMarkerSvgItem->pos())); + + return path; +} + +/* + * setBoundingRect() is a helper to set the given rectangle as the new shape to limit apexes coordinates. + * This is useful when instanciating or resizing the object. + * makeGeometryChange() is called so points are recomputed, control items are positionnated and shape is determined according to this modification. + * Setting bounds' width less than 300 is not allowed. + */ +void UBGraphicsAristo::setBoundingRect(QRectF boundingRect) +{ + if (boundingRect.width() < 300) + return; + + QPainterPath path; + path.addRect(boundingRect); + setPath(path); + if (mOrientation != Undefined) + makeGeometryChange(); +} + +void UBGraphicsAristo::makeGeometryChange() +{ + calculatePoints(); + setItemsPos(); + setPath(determinePath()); +} + + +UBItem* UBGraphicsAristo::deepCopy(void) const +{ + UBGraphicsAristo* copy = new UBGraphicsAristo(); + copyItemParameters(copy); + return copy; +} + +void UBGraphicsAristo::copyItemParameters(UBItem *copy) const +{ + UBGraphicsAristo* cp = dynamic_cast(copy); + if (cp) + { + /* TODO: copy all members */ + cp->setPos(this->pos()); + cp->setTransform(this->transform()); + cp->setBoundingRect(boundingRect()); + cp->setOrientation(mOrientation); + cp->mRotatedAngle = mRotatedAngle; + cp->mMarkerAngle = mMarkerAngle; + } +} + + +void UBGraphicsAristo::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) +{ + QPolygonF polygon; + + painter->setBrush(fillBrush()); + painter->setPen(drawColor()); + + polygon << A << B << C; + painter->drawPolygon(polygon); + polygon.clear(); + + paintGraduations(painter); + paintMarker(painter); +} + +QBrush UBGraphicsAristo::fillBrush() const +{ + QColor fillColor = edgeFillColor();// scene()->isDarkBackground() ? sDarkBackgroundFillColor : sFillColor; + QColor fillColorCenter = middleFillColor();//scene()->isDarkBackground() ? sDarkBackgroundFillColorCenter : sFillColorCenter; + QColor transparentWhite = Qt::white; + transparentWhite.setAlpha(scene()->isDarkBackground() ? sDrawTransparency : sFillTransparency); + QRadialGradient radialGradient(boundingRect().center(), radius(), boundingRect().center()); + radialGradient.setColorAt(0, fillColorCenter); + radialGradient.setColorAt(1, fillColor); + return radialGradient; +} + +/* paintGraduations() paints graduations on the ruler side (length graduations) and the two other sides (angle graduation) */ +void UBGraphicsAristo::paintGraduations(QPainter *painter) +{ + paintRulerGraduations(painter); + paintProtractorGraduations(painter); +} + +void UBGraphicsAristo::paintRulerGraduations(QPainter *painter) +{ + /* defining useful constants */ + const int centimeterGraduationHeight = 15; + const int halfCentimeterGraduationHeight = 10; + const int millimeterGraduationHeight = 5; + const int millimetersPerCentimeter = 10; + const int millimetersPerHalfCentimeter = 5; + + painter->save(); + painter->setFont(font()); + QFontMetricsF fontMetrics(painter->font()); + + /* Browsing milliters in half width of ruler side */ + for (int millimeters = 0; millimeters < (boundingRect().width() / 2 - sLeftEdgeMargin - sRoundingRadius) / sPixelsPerMillimeter; millimeters++) + { + /* defining graduationHeight ; values are different to draw bigger lines if millimiter considered is a centimeter or a half centimeter */ + int graduationHeight = (0 == millimeters % millimetersPerCentimeter) ? + centimeterGraduationHeight : + ((0 == millimeters % millimetersPerHalfCentimeter) ? + halfCentimeterGraduationHeight : millimeterGraduationHeight); + + /* correcting graduationHeight: draw the line in the other direction in case ruler is top-oriented, to stay inside the tool and inside the rect */ + graduationHeight = mOrientation == Bottom ? graduationHeight : - graduationHeight; + + /* drawing graduation to the left and to the right of origin, which is the center of graduated side */ + painter->drawLine(QLine(rotationCenter().x() + sPixelsPerMillimeter * millimeters, rotationCenter().y(), rotationCenter().x() + sPixelsPerMillimeter * millimeters, rotationCenter().y() + graduationHeight)); + if (millimeters != 0) + painter->drawLine(QLine(rotationCenter().x() - sPixelsPerMillimeter * millimeters, rotationCenter().y(), rotationCenter().x() - sPixelsPerMillimeter * millimeters, rotationCenter().y() + graduationHeight)); + + /* drawing associated value if considered graduation is a centimeter */ + if (0 == millimeters % millimetersPerCentimeter) + { + /* defining graduation value */ + QString text = QString("%1").arg((int)(millimeters / millimetersPerCentimeter)); + + /* staying inside polygon */ + if (rotationCenter().x() + sPixelsPerMillimeter * millimeters + fontMetrics.width(text) / 2 < boundingRect().right()) + { + qreal textWidth = fontMetrics.width(text); + qreal textHeight = fontMetrics.tightBoundingRect(text).height() + 5; + + /* text y-coordinate is different according to tool's orientation */ + qreal textY = mOrientation == Bottom ? A.y() + 5 + centimeterGraduationHeight : A.y() - 5 - centimeterGraduationHeight + graduationHeight; + + /* if text's rect is not out of polygon's bounds, drawing value below or above graduation */ + QPointF intersectionPoint; + + bool paint = false; + + if (mOrientation == Bottom && QLineF(QPointF(rotationCenter().x() - sPixelsPerMillimeter * millimeters - textWidth / 2, rotationCenter().y()), QPointF(rotationCenter().x() - sPixelsPerMillimeter * millimeters - textWidth / 2, textY + textHeight)).intersect(QLineF(A, C), &intersectionPoint) != QLineF::BoundedIntersection && QLineF(QPointF(rotationCenter().x() - sPixelsPerMillimeter * millimeters + textWidth / 2, rotationCenter().y()), QPointF(rotationCenter().x() - sPixelsPerMillimeter * millimeters + textWidth / 2, textY + textHeight)).intersect(QLineF(A, C), &intersectionPoint) != QLineF::BoundedIntersection) { + paint = true; + } + else if (mOrientation == Top && QLineF(QPointF(rotationCenter().x() - sPixelsPerMillimeter * millimeters - textWidth / 2, rotationCenter().y()), QPointF(rotationCenter().x() - sPixelsPerMillimeter * millimeters - textWidth / 2, textY - textHeight)).intersect(QLineF(A, C), &intersectionPoint) != QLineF::BoundedIntersection && QLineF(QPointF(rotationCenter().x() - sPixelsPerMillimeter * millimeters + textWidth / 2, rotationCenter().y()), QPointF(rotationCenter().x() - sPixelsPerMillimeter * millimeters + textWidth / 2, textY - textHeight)).intersect(QLineF(A, C), &intersectionPoint) != QLineF::BoundedIntersection) { + paint = true; + } + + if (paint) { + painter->drawText( + QRectF(rotationCenter().x() + sPixelsPerMillimeter * millimeters - textWidth / 2, textY, textWidth, textHeight), + Qt::AlignVCenter, text); + if (millimeters != 0) + painter->drawText( + QRectF(rotationCenter().x() - sPixelsPerMillimeter * millimeters - textWidth / 2, textY, textWidth, textHeight), + Qt::AlignVCenter, text); + } + } + } + } + painter->restore(); +} + +void UBGraphicsAristo::paintProtractorGraduations(QPainter* painter) +{ + /* defining useful constants */ + const int tenDegreeGraduationLength = 15; + const int fiveDegreeGraduationLength = 10; + const int oneDegreeGraduationLength = 5; + + painter->save(); + + QFont font1 = painter->font(); +#ifdef Q_WS_MAC + font1.setPointSizeF(font1.pointSizeF() - 3); +#endif + QFontMetricsF fm1(font1); + + //Font for internal arc + QFont font2 = painter->font(); + font2.setPointSizeF(font1.pointSizeF()/1.5); + QFontMetricsF fm2(font2); + + /* defining virtual arc diameter */ + qreal rad = radius(); + + QPointF center = rotationCenter(); + + /* browsing angles */ + for (int angle = 1; angle < mSpan; angle++) + { + int graduationLength = (0 == angle % 10) ? tenDegreeGraduationLength : ((0 == angle % 5) ? fiveDegreeGraduationLength : oneDegreeGraduationLength); + + qreal co = cos(((qreal)angle + mStartAngle) * PI/180); + qreal si = sin(((qreal)angle + mStartAngle) * PI/180); + + /* inverse sinus according to the orientation, to draw graduations on the polygon */ + si = mOrientation == Bottom ? -si : si; + + /* drawing the graduation around the virtual arc */ + if (angle >= sArcAngleMargin && angle <= mSpan - sArcAngleMargin) + painter->drawLine(QLineF(QPointF(center.x()+ rad/2*co, center.y() - rad/2*si), + QPointF(center.x()+ (rad/2 + graduationLength)*co, + center.y() - (rad/2 + graduationLength)*si))); + + + QPointF intersectionPoint; + QLineF referenceLine; + if (angle < 90) + referenceLine.setP1(B); + else + referenceLine.setP1(A); + referenceLine.setP2(C); + + /* if angle is 10-multiple, drawing it's value, rotated to be easily red */ + if (0 == angle % 10) { + QString grad = QString("%1").arg((int)(angle)); + QString grad2 = QString("%1").arg((int)mSpan - angle); + + painter->setFont(font2); + + painter->save(); + painter->translate(center.x() + (rad/2 + graduationLength*1.5)*co, center.y() - (rad/2 + graduationLength*1.5)*si); + int degrees = mOrientation == Bottom ? angle : -angle; + painter->rotate(-90 + degrees); + painter->drawText(- fm2.width(grad)/2, - fm2.height()/2, fm2.width(grad), fm2.height(), Qt::AlignCenter, grad); + painter->restore(); + + painter->setFont(font1); + + + /* drawing the graduation near tool's side */ + if (QLineF(QPointF(center.x()+ rad/2*co, center.y() - rad/2*si), + QPointF(center.x()+ (rad/2 + graduationLength)*co, + center.y() - (rad/2 + graduationLength)*si)).intersect(referenceLine, &intersectionPoint) == QLineF::UnboundedIntersection) + + painter->drawLine(QLineF(QPointF(center.x() + (rad/2 + graduationLength*1.5 + fm2.width(grad)/2)*co, + center.y() - (rad/2 + graduationLength*1.5 + fm2.height()/2)*si), + intersectionPoint)); + + } + + /* drawing the graduation near tool's side */ + else + if (QLineF(QPointF(center.x()+ rad/2*co, center.y() - rad/2*si), + QPointF(center.x()+ (rad/2 + graduationLength)*co, + center.y() - (rad/2 + graduationLength)*si)).intersect(referenceLine, &intersectionPoint) == QLineF::UnboundedIntersection) + + painter->drawLine(QLineF(QPointF(intersectionPoint.x() - (graduationLength*1.5)*co, + intersectionPoint.y() + (graduationLength*1.5)*si), + intersectionPoint)); + } + + painter->restore(); +} + +/* paintMarker() adjust marker button according to the current angle, draw the line allowing user to set precisely the angle, and draw the current angle's value. */ +void UBGraphicsAristo::paintMarker(QPainter *painter) +{ + /* adjusting marker button */ + mMarkerSvgItem->resetTransform(); + mMarkerSvgItem->translate(-markerButtonRect().left(), -markerButtonRect().top()); + mMarkerSvgItem->rotate(mMarkerAngle); + mMarkerSvgItem->translate(markerButtonRect().left(), markerButtonRect().top()); + + + qreal co = cos((mMarkerAngle) * PI/180); + qreal si = sin((mMarkerAngle) * PI/180); + + /* Setting point composing the line (from point C) which intersects the line we want to draw. */ + QPointF referencePoint; + if (mOrientation == Bottom) { + if ((int)mMarkerAngle % 360 < 90) + referencePoint = B; + else + referencePoint = A; + } + else if (mOrientation == Top) { + if ((int)mMarkerAngle % 360 < 270 && (int)mMarkerAngle % 360 > 0) + referencePoint = A; + else + referencePoint = B; + } + + /* getting intersection point to draw the wanted line */ + QLineF intersectedLine(rotationCenter(), QPointF(rotationCenter().x()+co, rotationCenter().y()+si)); + QPointF intersectionPoint; + if (intersectedLine.intersect(QLineF(referencePoint, C), &intersectionPoint)) + painter->drawLine(QLineF(intersectionPoint, rotationCenter())); + + /* drawing angle value */ + qreal rightAngle = mOrientation == Bottom ? mMarkerAngle : 360 - mMarkerAngle; + + + QString angleText = QString("%1°").arg(rightAngle, 0, 'f', 1); + + QFont font1 = painter->font(); +#ifdef Q_WS_MAC + font1.setPointSizeF(font1.pointSizeF() - 3); +#endif + QFontMetricsF fm1(font1); + + if (mOrientation == Bottom) + painter->drawText(rotationCenter().x() - fm1.width(angleText)/2 - radius()/8, rotationCenter().y() + radius()/8 - fm1.height()/2, fm1.width(angleText), fm1.height(), Qt::AlignCenter, angleText); + else + painter->drawText(rotationCenter().x() - fm1.width(angleText)/2 - radius()/8, rotationCenter().y() - radius()/8 - fm1.height()/2, fm1.width(angleText), fm1.height(), Qt::AlignCenter, angleText); +} + + +void UBGraphicsAristo::rotateAroundCenter(qreal angle) +{ + qreal oldAngle = mRotatedAngle; + mRotatedAngle = angle; + QTransform transform; + rotateAroundCenter(transform, rotationCenter()); + setTransform(transform, true); + mRotatedAngle = oldAngle + angle; // We have to store absolute value for FLIP case +} + +void UBGraphicsAristo::rotateAroundCenter(QTransform& transform, QPointF center) +{ + transform.translate(center.x(), center.y()); + transform.rotate(mRotatedAngle); + transform.translate(- center.x(), - center.y()); +} + +void UBGraphicsAristo::resize(qreal factor) +{ + prepareGeometryChange(); + translate(rotationCenter().x(), rotationCenter().y()); + scale(factor, factor); + translate(-rotationCenter().x(), -rotationCenter().y()); +} + + +QPointF UBGraphicsAristo::rotationCenter() const +{ + return QPointF((A.x() + B.x()) / 2, (A.y() + B.y()) / 2); +} + +QRectF UBGraphicsAristo::closeButtonRect() const +{ + qreal y = radius() / 4 + hFlipRect().height() + 3 + rotateRect().height() + 3; + if (mOrientation == Top) + y = -y; + return QRectF(- mCloseSvgItem->boundingRect().width() / 2, y, mCloseSvgItem->boundingRect().width(), mCloseSvgItem->boundingRect().height()); +} + +QRectF UBGraphicsAristo::hFlipRect() const +{ + qreal y = radius() / 4; + if (mOrientation == Top) + y = -y; + + return QRectF(- mHFlipSvgItem->boundingRect().width() / 2, y, mHFlipSvgItem->boundingRect().width(), mHFlipSvgItem->boundingRect().height()); +} + +QRectF UBGraphicsAristo::markerButtonRect() const +{ + return QRectF (radius()/2 - mMarkerSvgItem->boundingRect().width(), - mMarkerSvgItem->boundingRect().height()/2, mMarkerSvgItem->boundingRect().width(), mMarkerSvgItem->boundingRect().height()); +} + +QRectF UBGraphicsAristo::resizeButtonRect() const +{ + return QRectF((B - rotationCenter()).x() - 100 - mResizeSvgItem->boundingRect().width()/2, - mResizeSvgItem->boundingRect().height()/2, mResizeSvgItem->boundingRect().width(), mResizeSvgItem->boundingRect().height()); +} + +QRectF UBGraphicsAristo::rotateRect() const +{ + qreal y = radius() / 4 + hFlipRect().height() + 3; + if (mOrientation == Top) + y = -y; + return QRectF(- mRotateSvgItem->boundingRect().width() / 2, y, mRotateSvgItem->boundingRect().width(), mRotateSvgItem->boundingRect().height()); + +} + +QCursor UBGraphicsAristo::flipCursor() const +{ + return Qt::ArrowCursor; +} + +QCursor UBGraphicsAristo::markerCursor() const +{ + return Qt::ArrowCursor; +} + +QCursor UBGraphicsAristo::resizeCursor() const +{ + return Qt::ArrowCursor; +} + + +void UBGraphicsAristo::mousePressEvent(QGraphicsSceneMouseEvent *event) +{ + switch (toolFromPos(event->pos())) { + case Rotate: + mRotating = true; + event->accept(); + break; + case Resize: + mResizing = true; + event->accept(); + break; + case MoveMarker: + mMarking = true; + event->accept(); + break; + default: + QGraphicsItem::mousePressEvent(event); + break; + } + + mShowButtons = false; + mHFlipSvgItem->setVisible(false); + mCloseSvgItem->setVisible(false); + mRotateSvgItem->setVisible(mRotating); + mResizeSvgItem->setVisible(mResizing); + update(); +} + +void UBGraphicsAristo::mouseMoveEvent(QGraphicsSceneMouseEvent *event) +{ + + if (!mResizing && !mRotating && !mMarking) + { + QGraphicsItem::mouseMoveEvent(event); + } + else + { + QLineF currentLine(rotationCenter(), event->pos()); + QLineF lastLine(rotationCenter(), event->lastPos()); + + if (mRotating) { + + rotateAroundCenter(currentLine.angleTo(lastLine)); + } + else if (mResizing) { + QPointF delta = event->pos() - event->lastPos(); + setBoundingRect(QRectF(boundingRect().topLeft(), QSizeF(boundingRect().width() + delta.x(), boundingRect().height() + delta.x()))); + } + else if(mMarking) { + qreal angle = currentLine.angleTo(lastLine); + + mMarkerAngle += angle; + mMarkerAngle -= (int)(mMarkerAngle/360)*360; + + if (mOrientation == Bottom) { + if (mMarkerAngle >= 270) + mMarkerAngle = 0; + else if (mMarkerAngle > 180) + mMarkerAngle = 180; + } + else if (mOrientation == Top) { + if (mMarkerAngle < 90) + mMarkerAngle = 360; + else if (mMarkerAngle < 180) + mMarkerAngle = 180; + } + update(); + } + + event->accept(); + } +} + +void UBGraphicsAristo::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) +{ + if (mResizing || mRotating || mMarking) + { + mResizing = false; + mRotating = false; + mMarking = false; + event->accept(); + } + else + { + switch (toolFromPos(event->pos())) { + case Close : + hide(); + emit hidden(); + break; + case HorizontalFlip: + /* substracting difference to zero [2pi] twice, to obtain the desired angle */ + mMarkerAngle -= 2 * (mMarkerAngle - (int)(mMarkerAngle/360)*360) - 360; + /* setting new orientation */ + switch(mOrientation) { + case Bottom: + setOrientation(Top); + break; + case Top: + setOrientation(Bottom); + break; + default: + break; + } + default: + QGraphicsItem::mouseReleaseEvent(event); + break; + } + } + + mShowButtons = true; + update(); + if (scene()) + scene()->setModified(true); +} + +void UBGraphicsAristo::hoverEnterEvent(QGraphicsSceneHoverEvent *event) +{ + UBStylusTool::Enum currentTool = (UBStylusTool::Enum)UBDrawingController::drawingController ()->stylusTool (); + + if (currentTool == UBStylusTool::Selector) { + mShowButtons = true; + mHFlipSvgItem->setVisible(true); + mRotateSvgItem->setVisible(true); + mResizeSvgItem->setVisible(true); + mCloseSvgItem->setVisible(true); + + switch (toolFromPos(event->pos())) { + case HorizontalFlip: + setCursor(flipCursor()); + break; + case Rotate: + setCursor(rotateCursor()); + break; + case Resize: + setCursor(resizeCursor()); + break; + case MoveMarker: + setCursor(markerCursor()); + break; + case Close: + setCursor(closeCursor()); + break; + default: + setCursor(moveCursor()); + break; + } + + event->accept(); + update(); + + } else if (UBDrawingController::drawingController()->isDrawingTool()) { + setCursor(drawRulerLineCursor()); + UBDrawingController::drawingController()->mActiveRuler = this; + event->accept(); + } +} + +void UBGraphicsAristo::hoverLeaveEvent(QGraphicsSceneHoverEvent *event) +{ + mShowButtons = false; + setCursor(Qt::ArrowCursor); + mHFlipSvgItem->setVisible(false); + mRotateSvgItem->setVisible(false); + mResizeSvgItem->setVisible(false); + mCloseSvgItem->setVisible(false); + UBDrawingController::drawingController()->mActiveRuler = NULL; + event->accept(); + update(); +} + +void UBGraphicsAristo::hoverMoveEvent(QGraphicsSceneHoverEvent *event) +{ + UBStylusTool::Enum currentTool = (UBStylusTool::Enum)UBDrawingController::drawingController ()->stylusTool (); + + if (currentTool == UBStylusTool::Selector) + { + mShowButtons = true; + mHFlipSvgItem->setVisible(true); + mRotateSvgItem->setVisible(true); + mResizeSvgItem->setVisible(true); + mCloseSvgItem->setVisible(true); + + switch (toolFromPos(event->pos())) { + case HorizontalFlip: + setCursor(flipCursor()); + break; + case Rotate: + setCursor(rotateCursor()); + break; + case Resize: + setCursor(resizeCursor()); + break; + case MoveMarker: + setCursor(markerCursor()); + break; + case Close: + setCursor(closeCursor()); + break; + default: + setCursor(moveCursor()); + break; + } + + event->accept(); + } + else if (UBDrawingController::drawingController()->isDrawingTool()) + event->accept(); +} + +/* + * toolfromPos() returns the item type corresponding to the given position. + * This method is used to reduce the amount of code in each event function and improve class' maintainability. + * pos: event's position ; a rotation is done to counter elements rotation, like the marker button. + */ +UBGraphicsAristo::Tool UBGraphicsAristo::toolFromPos(QPointF pos) +{ + pos = pos - rotationCenter(); + + qreal rotationAngle = mOrientation == Bottom ? - mMarkerAngle : Top ? 360 * (int)(mMarkerAngle / 360 + 1) - mMarkerAngle : 0; + + QTransform t; + t.rotate(rotationAngle); + QPointF p2 = t.map(pos); + + if (resizeButtonRect().contains(pos)) + return Resize; + else if (closeButtonRect().contains(pos)) + return Close; + else if (rotateRect().contains(pos)) + return Rotate; + else if (markerButtonRect().contains(p2)) + return MoveMarker; + else if (hFlipRect().contains(pos)) + return HorizontalFlip; + else if (shape().contains(pos)) + return Move; + else + return None; +} + + +void UBGraphicsAristo::StartLine(const QPointF &scenePos, qreal width) +{ + QPointF itemPos = mapFromScene(scenePos); + + qreal y; + + y = rotationCenter().y(); + + if (itemPos.x() < boundingRect().x() + sLeftEdgeMargin) + itemPos.setX(boundingRect().x() + sLeftEdgeMargin); + if (itemPos.x() > boundingRect().x() + boundingRect().width() - sLeftEdgeMargin) + itemPos.setX(boundingRect().x() + boundingRect().width() - sLeftEdgeMargin); + + itemPos.setY(y); + itemPos = mapToScene(itemPos); + + scene()->moveTo(itemPos); + scene()->drawLineTo(itemPos, width, true); +} + +void UBGraphicsAristo::DrawLine(const QPointF &scenePos, qreal width) +{ + QPointF itemPos = mapFromScene(scenePos); + + qreal y; + + y = rotationCenter().y(); + + if (itemPos.x() < boundingRect().x() + sLeftEdgeMargin) + itemPos.setX(boundingRect().x() + sLeftEdgeMargin); + if (itemPos.x() > boundingRect().x() + boundingRect().width() - sLeftEdgeMargin) + itemPos.setX(boundingRect().x() + boundingRect().width() - sLeftEdgeMargin); + + itemPos.setY(y); + itemPos = mapToScene(itemPos); + + // We have to use "pointed" line for marker tool + scene()->drawLineTo(itemPos, width, + UBDrawingController::drawingController()->stylusTool() != UBStylusTool::Marker); +} + +void UBGraphicsAristo::EndLine() +{ + /* NOOP */ +} + + +UBGraphicsScene* UBGraphicsAristo::scene() const +{ + return static_cast(QGraphicsPathItem::scene()); +} diff --git a/src/tools/UBGraphicsAristo.h b/src/tools/UBGraphicsAristo.h new file mode 100644 index 00000000..8d23d7e5 --- /dev/null +++ b/src/tools/UBGraphicsAristo.h @@ -0,0 +1,156 @@ +/* + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef UBGRAPHICSARISTO_H_ +#define UBGRAPHICSARISTO_H_ + +#include "core/UB.h" +#include "domain/UBItem.h" +#include "domain/UBGraphicsScene.h" +#include "tools/UBAbstractDrawRuler.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class UBGraphicsAristo : public UBAbstractDrawRuler, public QGraphicsPathItem, public UBItem +{ +Q_OBJECT + +public: + UBGraphicsAristo(); + virtual ~UBGraphicsAristo(); + + enum { + Type = UBGraphicsItemType::AristoItemType + }; + + enum Tool { + None, + Move, + Resize, + Rotate, + Close, + MoveMarker, + HorizontalFlip + }; + + enum Orientation + { + Bottom = 0, + Top, + Undefined + }; + + void setOrientation(Orientation orientation); + void setBoundingRect(QRectF boundingRect); + + virtual UBItem* deepCopy() const; + virtual void copyItemParameters(UBItem *copy) const; + + virtual void StartLine(const QPointF& scenePos, qreal width); + virtual void DrawLine(const QPointF& position, qreal width); + virtual void EndLine(); + + virtual int type() const + { + return Type; + } + UBGraphicsScene* scene() const; + +protected: + virtual void paint (QPainter *painter, const QStyleOptionGraphicsItem *styleOption, QWidget *widget); + + virtual void rotateAroundCenter(qreal angle); + virtual void resize(qreal factor); + + virtual QPointF rotationCenter() const; + + virtual QRectF closeButtonRect() const; + QRectF hFlipRect() const; + QRectF markerButtonRect() const; + QRectF resizeButtonRect () const; + QRectF rotateRect() const; + + QCursor flipCursor() const; + QCursor markerCursor() const; + QCursor resizeCursor() const; + + virtual void mousePressEvent(QGraphicsSceneMouseEvent *event); + virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event); + virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event); + virtual void hoverEnterEvent(QGraphicsSceneHoverEvent *event); + virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent *event); + virtual void hoverMoveEvent(QGraphicsSceneHoverEvent *event); + +private: + Tool toolFromPos(QPointF pos); + + QTransform calculateRotationTransform(); + void rotateAroundCenter(QTransform& transform, QPointF center); + + void calculatePoints(); + QPainterPath determinePath(); + void setItemsPos(); + void makeGeometryChange(); + + QBrush fillBrush() const; + void paintGraduations(QPainter *painter); + void paintMarker(QPainter *painter); + void paintProtractorGraduations(QPainter* painter); + void paintRulerGraduations(QPainter *painter); + + inline qreal radius () const + { + return sqrt(((B.x() - A.x())*(B.x() - A.x()))+((B.y() - A.y())*(B.y() - A.y()))) * 9 / 16 - 20; + } + + bool mMarking; + bool mResizing; + bool mRotating; + + Orientation mOrientation; + + qreal mRotatedAngle; + qreal mMarkerAngle; + qreal mStartAngle; + + qreal mSpan; + + QGraphicsSvgItem* mHFlipSvgItem; + QGraphicsSvgItem* mMarkerSvgItem; + QGraphicsSvgItem* mResizeSvgItem; + QGraphicsSvgItem* mRotateSvgItem; + + QPointF A, B, C; + + static const int sArcAngleMargin = 5; + static const Orientation sDefaultOrientation; + static const QRectF sDefaultRect; +}; + +#endif /* UBGRAPHICSARISTO_H_ */ diff --git a/src/tools/UBToolsManager.cpp b/src/tools/UBToolsManager.cpp index 95b40eec..d30e4fa1 100644 --- a/src/tools/UBToolsManager.cpp +++ b/src/tools/UBToolsManager.cpp @@ -91,6 +91,12 @@ UBToolsManager::UBToolsManager(QObject *parent) mDescriptors << cache; // -------------------------------------------------------------------------------- + aristo.id = "uniboardTool://uniboard.mnemis.com/aristo"; + aristo.icon = QPixmap(":/images/toolPalette/aristoTool.png"); + aristo.label = tr("Aristo"); + aristo.version = "1.0"; + mToolsIcon.insert(aristo.id, ":/images/toolPalette/aristoTool.png"); + mDescriptors << aristo; } UBToolsManager::~UBToolsManager() diff --git a/src/tools/UBToolsManager.h b/src/tools/UBToolsManager.h index eee12d61..410b224f 100644 --- a/src/tools/UBToolsManager.h +++ b/src/tools/UBToolsManager.h @@ -76,6 +76,7 @@ class UBToolsManager : public QObject UBToolDescriptor triangle; UBToolDescriptor magnifier; UBToolDescriptor cache; + UBToolDescriptor aristo; QString iconFromToolId(QString id) { return mToolsIcon.value(id);} diff --git a/src/tools/tools.pri b/src/tools/tools.pri index 90e69659..0196f84d 100644 --- a/src/tools/tools.pri +++ b/src/tools/tools.pri @@ -1,20 +1,21 @@ - -HEADERS += src/tools/UBGraphicsRuler.h \ - src/tools/UBGraphicsTriangle.h \ +HEADERS += src/tools/UBGraphicsRuler.h \ + src/tools/UBGraphicsTriangle.h \ src/tools/UBGraphicsProtractor.h \ src/tools/UBGraphicsCompass.h \ + src/tools/UBGraphicsAristo.h \ src/tools/UBToolsManager.h \ src/tools/UBGraphicsCurtainItem.h \ src/tools/UBGraphicsCurtainItemDelegate.h \ src/tools/UBAbstractDrawRuler.h \ - src/tools/UBGraphicsCache.h - -SOURCES += src/tools/UBGraphicsRuler.cpp \ - src/tools/UBGraphicsTriangle.cpp \ + src/tools/UBGraphicsCache.h + +SOURCES += src/tools/UBGraphicsRuler.cpp \ + src/tools/UBGraphicsTriangle.cpp \ src/tools/UBGraphicsProtractor.cpp \ src/tools/UBGraphicsCompass.cpp \ + src/tools/UBGraphicsAristo.cpp \ src/tools/UBToolsManager.cpp \ src/tools/UBGraphicsCurtainItem.cpp \ src/tools/UBGraphicsCurtainItemDelegate.cpp \ src/tools/UBAbstractDrawRuler.cpp \ - src/tools/UBGraphicsCache.cpp + src/tools/UBGraphicsCache.cpp