From f31358e6dd9d47b88e2df3c7503c0411ae3e6db2 Mon Sep 17 00:00:00 2001 From: J-F Martel Date: Wed, 19 Jul 2017 11:04:25 -0400 Subject: [PATCH] =?UTF-8?q?pr=C3=A9-voyage?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Sources/ImageCropper/imagecropper.cpp | 501 +++++++++++++++++++++++ Sources/ImageCropper/imagecropper.h | 85 ++++ Sources/ImageCropper/imagecropper_e.h | 18 + Sources/ImageCropper/imagecropper_p.h | 58 +++ Sources/NetworkDevicesMgr.cpp | 2 + Sources/NetworkProtocol.cpp | 4 +- Sources/Sprinkler/SprinklerDevice.cpp | 62 ++- Sources/Sprinkler/SprinklerDevice.h | 16 + Sources/Sprinkler/SprinklerInterface.cpp | 18 + Sources/Sprinkler/SprinklerMgr.cpp | 25 ++ Sources/Sprinkler/SprinklerMgr.h | 7 + Sources/VoipSMS/VoipMsSMSClient.cpp | 2 +- 12 files changed, 793 insertions(+), 5 deletions(-) create mode 100644 Sources/ImageCropper/imagecropper.cpp create mode 100644 Sources/ImageCropper/imagecropper.h create mode 100644 Sources/ImageCropper/imagecropper_e.h create mode 100644 Sources/ImageCropper/imagecropper_p.h diff --git a/Sources/ImageCropper/imagecropper.cpp b/Sources/ImageCropper/imagecropper.cpp new file mode 100644 index 0000000..9fc7f60 --- /dev/null +++ b/Sources/ImageCropper/imagecropper.cpp @@ -0,0 +1,501 @@ +#include "imagecropper.h" + +#include +#include + +namespace { + static const QSize WIDGET_MINIMUM_SIZE(300, 300); +} + +ImageCropper::ImageCropper(QWidget* parent) : + QWidget(parent), + pimpl(new ImageCropperPrivate) +{ + setMinimumSize(WIDGET_MINIMUM_SIZE); + setMouseTracking(true); +} + +ImageCropper::~ImageCropper() +{ + delete pimpl; +} + +void ImageCropper::setImage(const QPixmap& _image) +{ + pimpl->imageForCropping = _image; + update(); +} + +void ImageCropper::setBackgroundColor(const QColor& _backgroundColor) +{ + pimpl->backgroundColor = _backgroundColor; + update(); +} + +void ImageCropper::setCroppingRectBorderColor(const QColor& _borderColor) +{ + pimpl->croppingRectBorderColor = _borderColor; + update(); +} + +void ImageCropper::setProportion(const QSizeF& _proportion) +{ + // Пропорции хранятся в коэффициентах приращения сторон + // Таким образом, при изменении размера области выделения, + // размеры сторон изменяются на размер зависящий от + // коэффициентов приращения. + + // Сохраним пропорциональную зависимость области выделения в коэффициентах приращения сторон + if (pimpl->proportion != _proportion) { + pimpl->proportion = _proportion; + // ... расчитаем коэффициенты + float heightDelta = (float)_proportion.height() / _proportion.width(); + float widthDelta = (float)_proportion.width() / _proportion.height(); + // ... сохраним коэффициенты + pimpl->deltas.setHeight(heightDelta); + pimpl->deltas.setWidth(widthDelta); + } + + // Обновим пропорции области выделения + if ( pimpl->isProportionFixed ) { + float croppintRectSideRelation = + (float)pimpl->croppingRect.width() / pimpl->croppingRect.height(); + float proportionSideRelation = + (float)pimpl->proportion.width() / pimpl->proportion.height(); + // Если область выделения не соответствует необходимым пропорциям обновим её + if (croppintRectSideRelation != proportionSideRelation) { + bool widthShotrerThenHeight = + pimpl->croppingRect.width() < pimpl->croppingRect.height(); + // ... установим размер той стороны, что длиннее + if (widthShotrerThenHeight) { + pimpl->croppingRect.setHeight( + pimpl->croppingRect.width() * pimpl->deltas.height()); + } else { + pimpl->croppingRect.setWidth( + pimpl->croppingRect.height() * pimpl->deltas.width()); + } + // ... перерисуем виджет + update(); + } + } + +} + +void ImageCropper::setProportionFixed(const bool _isFixed) +{ + if (pimpl->isProportionFixed != _isFixed) { + pimpl->isProportionFixed = _isFixed; + setProportion(pimpl->proportion); + } +} + +const QPixmap ImageCropper::cropImage() +{ + // Получим размер отображаемого изображения + QSize scaledImageSize = + pimpl->imageForCropping.scaled( + this->size(), Qt::KeepAspectRatio, Qt::FastTransformation + ).size(); + // Определим расстояние от левого и верхнего краёв + float leftDelta = 0; + float topDelta = 0; + const float HALF_COUNT = 2; + if (this->size().height() == scaledImageSize.height()) { + leftDelta = (this->width() - scaledImageSize.width()) / HALF_COUNT; + } else { + topDelta = (this->height() - scaledImageSize.height()) / HALF_COUNT; + } + // Определим пропорцию области обрезки по отношению к исходному изображению + float xScale = (float)pimpl->imageForCropping.width() / scaledImageSize.width(); + float yScale = (float)pimpl->imageForCropping.height() / scaledImageSize.height(); + // Расчитаем область обрезки с учётом коррекции размеров исходного изображения + QRectF realSizeRect( + QPointF(pimpl->croppingRect.left() - leftDelta, pimpl->croppingRect.top() - topDelta), + pimpl->croppingRect.size()); + // ... корректируем левый и верхний края + realSizeRect.setLeft((pimpl->croppingRect.left() - leftDelta) * xScale); + realSizeRect.setTop ((pimpl->croppingRect.top() - topDelta) * yScale); + // ... корректируем размер + realSizeRect.setWidth(pimpl->croppingRect.width() * xScale); + realSizeRect.setHeight(pimpl->croppingRect.height() * yScale); + // Получаем обрезанное изображение + return pimpl->imageForCropping.copy(realSizeRect.toRect()); +} + +// ******** +// Protected section + +void ImageCropper::paintEvent(QPaintEvent* _event) +{ + QWidget::paintEvent( _event ); + // + QPainter widgetPainter(this); + // Рисуем изображение по центру виджета + { + // ... подгоним изображение для отображения по размеру виджета + QPixmap scaledImage = + pimpl->imageForCropping.scaled(this->size(), Qt::KeepAspectRatio, Qt::FastTransformation); + // ... заливаем фон + widgetPainter.fillRect( this->rect(), pimpl->backgroundColor ); + // ... рисуем изображение по центру виджета + if ( this->size().height() == scaledImage.height() ) { + widgetPainter.drawPixmap( ( this->width() - scaledImage.width() ) / 2, 0, scaledImage ); + } else { + widgetPainter.drawPixmap( 0, ( this->height() - scaledImage.height() ) / 2, scaledImage ); + } + } + // Рисуем область обрезки + { + // ... если это первое отображение после инициилизации, то центруем областо обрезки + if (pimpl->croppingRect.isNull()) { + const int width = WIDGET_MINIMUM_SIZE.width()/2; + const int height = WIDGET_MINIMUM_SIZE.height()/2; + pimpl->croppingRect.setSize(QSize(width, height)); + float x = (this->width() - pimpl->croppingRect.width())/2; + float y = (this->height() - pimpl->croppingRect.height())/2; + pimpl->croppingRect.moveTo(x, y); + } + + // ... рисуем затемненную область + QPainterPath p; + p.addRect(pimpl->croppingRect); + p.addRect(this->rect()); + widgetPainter.setBrush(QBrush(QColor(0,0,0,120))); + widgetPainter.setPen(Qt::transparent); + widgetPainter.drawPath(p); + // Рамка и контрольные точки + widgetPainter.setPen(pimpl->croppingRectBorderColor); + // ... рисуем прямоугольник области обрезки + { + widgetPainter.setBrush(QBrush(Qt::transparent)); + widgetPainter.drawRect(pimpl->croppingRect); + } + // ... рисуем контрольные точки + { + widgetPainter.setBrush(QBrush(pimpl->croppingRectBorderColor)); + // Вспомогательные X координаты + int leftXCoord = pimpl->croppingRect.left() - 2; + int centerXCoord = pimpl->croppingRect.center().x() - 3; + int rightXCoord = pimpl->croppingRect.right() - 2; + // Вспомогательные Y координаты + int topYCoord = pimpl->croppingRect.top() - 2; + int middleYCoord = pimpl->croppingRect.center().y() - 3; + int bottomYCoord = pimpl->croppingRect.bottom() - 2; + // + const QSize pointSize(6, 6); + // + QVector points; + points + // левая сторона + << QRect( QPoint(leftXCoord, topYCoord), pointSize ) + << QRect( QPoint(leftXCoord, middleYCoord), pointSize ) + << QRect( QPoint(leftXCoord, bottomYCoord), pointSize ) + // центр + << QRect( QPoint(centerXCoord, topYCoord), pointSize ) + << QRect( QPoint(centerXCoord, middleYCoord), pointSize ) + << QRect( QPoint(centerXCoord, bottomYCoord), pointSize ) + // правая сторона + << QRect( QPoint(rightXCoord, topYCoord), pointSize ) + << QRect( QPoint(rightXCoord, middleYCoord), pointSize ) + << QRect( QPoint(rightXCoord, bottomYCoord), pointSize ); + // + widgetPainter.drawRects( points ); + } + // ... рисуем пунктирные линии + { + QPen dashPen(pimpl->croppingRectBorderColor); + dashPen.setStyle(Qt::DashLine); + widgetPainter.setPen(dashPen); + // ... вертикальная + widgetPainter.drawLine( + QPoint(pimpl->croppingRect.center().x(), pimpl->croppingRect.top()), + QPoint(pimpl->croppingRect.center().x(), pimpl->croppingRect.bottom()) ); + // ... горизонтальная + widgetPainter.drawLine( + QPoint(pimpl->croppingRect.left(), pimpl->croppingRect.center().y()), + QPoint(pimpl->croppingRect.right(), pimpl->croppingRect.center().y()) ); + } + } + // + widgetPainter.end(); +} + +void ImageCropper::mousePressEvent(QMouseEvent* _event) +{ + if (_event->button() == Qt::LeftButton) { + pimpl->isMousePressed = true; + pimpl->startMousePos = _event->pos(); + pimpl->lastStaticCroppingRect = pimpl->croppingRect; + } + // + updateCursorIcon(_event->pos()); +} + +void ImageCropper::mouseMoveEvent(QMouseEvent* _event) +{ + QPointF mousePos = _event->pos(); // относительно себя (виджета) + // + if (!pimpl->isMousePressed) { + // Обработка обычного состояния, т.е. не изменяется размер + // области обрезки, и она не перемещается по виджету + pimpl->cursorPosition = cursorPosition(pimpl->croppingRect, mousePos); + updateCursorIcon(mousePos); + } else if (pimpl->cursorPosition != CursorPositionUndefined) { + // Обработка действий над областью обрезки + // ... определим смещение курсора мышки + QPointF mouseDelta; + mouseDelta.setX( mousePos.x() - pimpl->startMousePos.x() ); + mouseDelta.setY( mousePos.y() - pimpl->startMousePos.y() ); + // + if (pimpl->cursorPosition != CursorPositionMiddle) { + // ... изменяем размер области обрезки + QRectF newGeometry = + calculateGeometry( + pimpl->lastStaticCroppingRect, + pimpl->cursorPosition, + mouseDelta); + // ... пользователь пытается вывернуть область обрезки наизнанку + if (!newGeometry.isNull()) { + pimpl->croppingRect = newGeometry; + } + } else { + // ... перемещаем область обрезки + pimpl->croppingRect.moveTo( pimpl->lastStaticCroppingRect.topLeft() + mouseDelta ); + } + // Перерисуем виджет + update(); + } +} + +void ImageCropper::mouseReleaseEvent(QMouseEvent* _event) +{ + pimpl->isMousePressed = false; + updateCursorIcon(_event->pos()); +} + +// ******** +// Private section + +namespace { + // Находится ли точка рядом с координатой стороны + static bool isPointNearSide (const int _sideCoordinate, const int _pointCoordinate) + { + static const int indent = 10; + return (_sideCoordinate - indent) < _pointCoordinate && _pointCoordinate < (_sideCoordinate + indent); + } +} + +CursorPosition ImageCropper::cursorPosition(const QRectF& _cropRect, const QPointF& _mousePosition) +{ + CursorPosition cursorPosition = CursorPositionUndefined; + // + if ( _cropRect.contains( _mousePosition ) ) { + // Двухстороннее направление + if (isPointNearSide(_cropRect.top(), _mousePosition.y()) && + isPointNearSide(_cropRect.left(), _mousePosition.x())) { + cursorPosition = CursorPositionTopLeft; + } else if (isPointNearSide(_cropRect.bottom(), _mousePosition.y()) && + isPointNearSide(_cropRect.left(), _mousePosition.x())) { + cursorPosition = CursorPositionBottomLeft; + } else if (isPointNearSide(_cropRect.top(), _mousePosition.y()) && + isPointNearSide(_cropRect.right(), _mousePosition.x())) { + cursorPosition = CursorPositionTopRight; + } else if (isPointNearSide(_cropRect.bottom(), _mousePosition.y()) && + isPointNearSide(_cropRect.right(), _mousePosition.x())) { + cursorPosition = CursorPositionBottomRight; + // Одностороннее направление + } else if (isPointNearSide(_cropRect.left(), _mousePosition.x())) { + cursorPosition = CursorPositionLeft; + } else if (isPointNearSide(_cropRect.right(), _mousePosition.x())) { + cursorPosition = CursorPositionRight; + } else if (isPointNearSide(_cropRect.top(), _mousePosition.y())) { + cursorPosition = CursorPositionTop; + } else if (isPointNearSide(_cropRect.bottom(), _mousePosition.y())) { + cursorPosition = CursorPositionBottom; + // Без направления + } else { + cursorPosition = CursorPositionMiddle; + } + } + // + return cursorPosition; +} + +void ImageCropper::updateCursorIcon(const QPointF& _mousePosition) +{ + QCursor cursorIcon; + // + switch (cursorPosition(pimpl->croppingRect, _mousePosition)) + { + case CursorPositionTopRight: + case CursorPositionBottomLeft: + cursorIcon = QCursor(Qt::SizeBDiagCursor); + break; + case CursorPositionTopLeft: + case CursorPositionBottomRight: + cursorIcon = QCursor(Qt::SizeFDiagCursor); + break; + case CursorPositionTop: + case CursorPositionBottom: + cursorIcon = QCursor(Qt::SizeVerCursor); + break; + case CursorPositionLeft: + case CursorPositionRight: + cursorIcon = QCursor(Qt::SizeHorCursor); + break; + case CursorPositionMiddle: + cursorIcon = pimpl->isMousePressed ? + QCursor(Qt::ClosedHandCursor) : + QCursor(Qt::OpenHandCursor); + break; + case CursorPositionUndefined: + default: + cursorIcon = QCursor(Qt::ArrowCursor); + break; + } + // + this->setCursor(cursorIcon); +} + +const QRectF ImageCropper::calculateGeometry( + const QRectF& _sourceGeometry, + const CursorPosition _cursorPosition, + const QPointF& _mouseDelta + ) +{ + QRectF resultGeometry; + // + if ( pimpl->isProportionFixed ) { + resultGeometry = + calculateGeometryWithFixedProportions( + _sourceGeometry, _cursorPosition, _mouseDelta, pimpl->deltas); + } else { + resultGeometry = + calculateGeometryWithCustomProportions( + _sourceGeometry, _cursorPosition, _mouseDelta); + } + // Если пользователь пытается вывернуть область обрезки наизнанку, + // возвращаем null-прямоугольник + if ((resultGeometry.left() >= resultGeometry.right()) || + (resultGeometry.top() >= resultGeometry.bottom())) { + resultGeometry = QRect(); + } + // + return resultGeometry; +} + +const QRectF ImageCropper::calculateGeometryWithCustomProportions( + const QRectF& _sourceGeometry, + const CursorPosition _cursorPosition, + const QPointF& _mouseDelta + ) +{ + QRectF resultGeometry = _sourceGeometry; + // + switch ( _cursorPosition ) + { + case CursorPositionTopLeft: + resultGeometry.setLeft( _sourceGeometry.left() + _mouseDelta.x() ); + resultGeometry.setTop ( _sourceGeometry.top() + _mouseDelta.y() ); + break; + case CursorPositionTopRight: + resultGeometry.setTop ( _sourceGeometry.top() + _mouseDelta.y() ); + resultGeometry.setRight( _sourceGeometry.right() + _mouseDelta.x() ); + break; + case CursorPositionBottomLeft: + resultGeometry.setBottom( _sourceGeometry.bottom() + _mouseDelta.y() ); + resultGeometry.setLeft ( _sourceGeometry.left() + _mouseDelta.x() ); + break; + case CursorPositionBottomRight: + resultGeometry.setBottom( _sourceGeometry.bottom() + _mouseDelta.y() ); + resultGeometry.setRight ( _sourceGeometry.right() + _mouseDelta.x() ); + break; + case CursorPositionTop: + resultGeometry.setTop( _sourceGeometry.top() + _mouseDelta.y() ); + break; + case CursorPositionBottom: + resultGeometry.setBottom( _sourceGeometry.bottom() + _mouseDelta.y() ); + break; + case CursorPositionLeft: + resultGeometry.setLeft( _sourceGeometry.left() + _mouseDelta.x() ); + break; + case CursorPositionRight: + resultGeometry.setRight( _sourceGeometry.right() + _mouseDelta.x() ); + break; + default: + break; + } + // + return resultGeometry; +} + +const QRectF ImageCropper::calculateGeometryWithFixedProportions( + const QRectF& _sourceGeometry, + const CursorPosition _cursorPosition, + const QPointF& _mouseDelta, + const QSizeF& _deltas + ) +{ + QRectF resultGeometry = _sourceGeometry; + // + switch (_cursorPosition) + { + case CursorPositionLeft: + resultGeometry.setTop(_sourceGeometry.top() + _mouseDelta.x() * _deltas.height()); + resultGeometry.setLeft(_sourceGeometry.left() + _mouseDelta.x()); + break; + case CursorPositionRight: + resultGeometry.setTop(_sourceGeometry.top() - _mouseDelta.x() * _deltas.height()); + resultGeometry.setRight(_sourceGeometry.right() + _mouseDelta.x()); + break; + case CursorPositionTop: + resultGeometry.setTop(_sourceGeometry.top() + _mouseDelta.y()); + resultGeometry.setRight(_sourceGeometry.right() - _mouseDelta.y() * _deltas.width()); + break; + case CursorPositionBottom: + resultGeometry.setBottom(_sourceGeometry.bottom() + _mouseDelta.y()); + resultGeometry.setRight(_sourceGeometry.right() + _mouseDelta.y() * _deltas.width()); + break; + case CursorPositionTopLeft: + if ((_mouseDelta.x() * _deltas.height()) < (_mouseDelta.y())) { + resultGeometry.setTop(_sourceGeometry.top() + _mouseDelta.x() * _deltas.height()); + resultGeometry.setLeft(_sourceGeometry.left() + _mouseDelta.x()); + } else { + resultGeometry.setTop(_sourceGeometry.top() + _mouseDelta.y()); + resultGeometry.setLeft(_sourceGeometry.left() + _mouseDelta.y() * _deltas.width()); + } + break; + case CursorPositionTopRight: + if ((_mouseDelta.x() * _deltas.height() * -1) < (_mouseDelta.y())) { + resultGeometry.setTop(_sourceGeometry.top() - _mouseDelta.x() * _deltas.height()); + resultGeometry.setRight(_sourceGeometry.right() + _mouseDelta.x() ); + } else { + resultGeometry.setTop(_sourceGeometry.top() + _mouseDelta.y()); + resultGeometry.setRight(_sourceGeometry.right() - _mouseDelta.y() * _deltas.width()); + } + break; + case CursorPositionBottomLeft: + if ((_mouseDelta.x() * _deltas.height()) < (_mouseDelta.y() * -1)) { + resultGeometry.setBottom(_sourceGeometry.bottom() - _mouseDelta.x() * _deltas.height()); + resultGeometry.setLeft(_sourceGeometry.left() + _mouseDelta.x()); + } else { + resultGeometry.setBottom(_sourceGeometry.bottom() + _mouseDelta.y()); + resultGeometry.setLeft(_sourceGeometry.left() - _mouseDelta.y() * _deltas.width()); + } + break; + case CursorPositionBottomRight: + if ((_mouseDelta.x() * _deltas.height()) > (_mouseDelta.y())) { + resultGeometry.setBottom(_sourceGeometry.bottom() + _mouseDelta.x() * _deltas.height()); + resultGeometry.setRight(_sourceGeometry.right() + _mouseDelta.x()); + } else { + resultGeometry.setBottom(_sourceGeometry.bottom() + _mouseDelta.y()); + resultGeometry.setRight(_sourceGeometry.right() + _mouseDelta.y() * _deltas.width()); + } + break; + default: + break; + } + // + return resultGeometry; +} + diff --git a/Sources/ImageCropper/imagecropper.h b/Sources/ImageCropper/imagecropper.h new file mode 100644 index 0000000..2310d7a --- /dev/null +++ b/Sources/ImageCropper/imagecropper.h @@ -0,0 +1,85 @@ +#ifndef IMAGECROPPER_H +#define IMAGECROPPER_H + +#include "imagecropper_p.h" +#include "imagecropper_e.h" + +#include + +class ImageCropper : public QWidget +{ + Q_OBJECT + +public: + ImageCropper(QWidget *parent = 0); + ~ImageCropper(); + +public slots: + // Установить изображение для обрезки + void setImage(const QPixmap& _image); + // Установить цвет фона виджета обрезки + void setBackgroundColor(const QColor& _backgroundColor); + // Установить цвет рамки области обрезки + void setCroppingRectBorderColor(const QColor& _borderColor); + // Установить пропорции области выделения + void setProportion(const QSizeF& _proportion); + // Использовать фиксированные пропорции области виделения + void setProportionFixed(const bool _isFixed); + +public: + // Обрезать изображение + const QPixmap cropImage(); + +protected: + virtual void paintEvent(QPaintEvent* _event); + virtual void mousePressEvent(QMouseEvent* _event); + virtual void mouseMoveEvent(QMouseEvent* _event); + virtual void mouseReleaseEvent(QMouseEvent* _event); + +private: + // Определение местоположения курсора над виджетом + CursorPosition cursorPosition(const QRectF& _cropRect, const QPointF& _mousePosition); + // Обновить иконку курсора соответствующую местоположению мыши + void updateCursorIcon(const QPointF& _mousePosition); + + // Получить размер виджета после его изменения мышью + // -------- + // Контракты: + // 1. Метод должен вызываться, только при зажатой кнопке мыши + // (т.е. при перемещении или изменении размера виджета) + // -------- + // В случае неудачи возвращает null-прямоугольник + const QRectF calculateGeometry( + const QRectF& _sourceGeometry, + const CursorPosition _cursorPosition, + const QPointF& _mouseDelta + ); + // Получить размер виджета после его изменения мышью + // Метод изменяет виджет не сохраняя начальных пропорций сторон + // ------ + // Контракты: + // 1. Метод должен вызываться, только при зажатой кнопке мыши + // (т.е. при перемещении или изменении размера виджета) + const QRectF calculateGeometryWithCustomProportions( + const QRectF& _sourceGeometry, + const CursorPosition _cursorPosition, + const QPointF& _mouseDelta + ); + // Получить размер виджета после его изменения мышью + // Метод изменяет виджет сохраняя начальные пропорции сторон + // ------ + // Контракты: + // 1. Метод должен вызываться, только при зажатой кнопке мыши + // (т.е. при перемещении или изменении размера виджета) + const QRectF calculateGeometryWithFixedProportions(const QRectF &_sourceGeometry, + const CursorPosition _cursorPosition, + const QPointF &_mouseDelta, + const QSizeF &_deltas + ); + +private: + // Private data implementation + ImageCropperPrivate* pimpl; +}; + +#endif // IMAGECROPPER_H diff --git a/Sources/ImageCropper/imagecropper_e.h b/Sources/ImageCropper/imagecropper_e.h new file mode 100644 index 0000000..41b8a66 --- /dev/null +++ b/Sources/ImageCropper/imagecropper_e.h @@ -0,0 +1,18 @@ +#ifndef IMAGECROPPER_E_H +#define IMAGECROPPER_E_H + +enum CursorPosition +{ + CursorPositionUndefined, + CursorPositionMiddle, + CursorPositionTop, + CursorPositionBottom, + CursorPositionLeft, + CursorPositionRight, + CursorPositionTopLeft, + CursorPositionTopRight, + CursorPositionBottomLeft, + CursorPositionBottomRight +}; + +#endif // IMAGECROPPER_E_H diff --git a/Sources/ImageCropper/imagecropper_p.h b/Sources/ImageCropper/imagecropper_p.h new file mode 100644 index 0000000..d3bd109 --- /dev/null +++ b/Sources/ImageCropper/imagecropper_p.h @@ -0,0 +1,58 @@ +#ifndef IMAGECROPPER_P_H +#define IMAGECROPPER_P_H + +#include "imagecropper_e.h" + +#include +#include +#include + +namespace { + const QRect INIT_CROPPING_RECT = QRect(); + const QSizeF INIT_PROPORTION = QSizeF(1.0, 1.0); +} + +class ImageCropperPrivate { +public: + ImageCropperPrivate() : + imageForCropping(QPixmap()), + croppingRect(INIT_CROPPING_RECT), + lastStaticCroppingRect(QRect()), + cursorPosition(CursorPositionUndefined), + isMousePressed(false), + isProportionFixed(false), + startMousePos(QPoint()), + proportion(INIT_PROPORTION), + deltas(INIT_PROPORTION), + backgroundColor(Qt::black), + croppingRectBorderColor(Qt::white) + {} + +public: + // Изображение для обрезки + QPixmap imageForCropping; + // Область обрезки + QRectF croppingRect; + // Последняя фиксированная область обрезки + QRectF lastStaticCroppingRect; + // Позиция курсора относительно области обрезки + CursorPosition cursorPosition; + // Зажата ли левая кнопка мыши + bool isMousePressed; + // Фиксировать пропорции области обрезки + bool isProportionFixed; + // Начальная позиция курсора при изменении размера области обрезки + QPointF startMousePos; + // Пропорции + QSizeF proportion; + // Приращения + // width - приращение по x + // height - приращение по y + QSizeF deltas; + // Цвет заливки фона под изображением + QColor backgroundColor; + // Цвет рамки области обрезки + QColor croppingRectBorderColor; +}; + +#endif // IMAGECROPPER_P_H diff --git a/Sources/NetworkDevicesMgr.cpp b/Sources/NetworkDevicesMgr.cpp index 2a11fc4..2819059 100644 --- a/Sources/NetworkDevicesMgr.cpp +++ b/Sources/NetworkDevicesMgr.cpp @@ -58,6 +58,8 @@ int CNetworkDevicesMgr::CreateNewSprinklerDevice(int Address, CAbstractNetworkCo CSprinklerDevice *SprinklerDevice = new CSprinklerDevice(Address,NetworkIF,mSprinklerMgrHandle); mNetworkDevicesList.append((CNetworkDevice*)SprinklerDevice); + mSprinklerMgrHandle->NewSprinklerDeviceCreated(SprinklerDevice); + return RET_OK; } diff --git a/Sources/NetworkProtocol.cpp b/Sources/NetworkProtocol.cpp index 71ba87a..0bea3e4 100644 --- a/Sources/NetworkProtocol.cpp +++ b/Sources/NetworkProtocol.cpp @@ -51,7 +51,8 @@ QByteArray CNetworkProtocol::GetTxPacket(unsigned char MessageID, unsigned char Frame.append((Size & 0x00FF0000) >> 16); Frame.append((Size & 0x0000FF00) >> 8); Frame.append(Size & 0x000000FF); - Frame.append(Data,Size); + if(Size > 0) + Frame.append(Data,Size); char CRC = CalcCRC(Frame.data(),FrameSize); Frame.append(CRC); @@ -350,6 +351,7 @@ void CNetworkProtocol::PrepareForNewPacket() int CNetworkProtocol::AnalyzeRxBuffer(QByteArray Buffer) { int ret = PROTOCOL_RET_ERROR_EMPTY_BUFFER; + qDebug("Rx %d bytes",Buffer.size()); for(int i = 0; i < Buffer.size(); i++) { ret = RxStateMachine(Buffer.at(i)); diff --git a/Sources/Sprinkler/SprinklerDevice.cpp b/Sources/Sprinkler/SprinklerDevice.cpp index 7c0d127..f97747d 100644 --- a/Sources/Sprinkler/SprinklerDevice.cpp +++ b/Sources/Sprinkler/SprinklerDevice.cpp @@ -7,13 +7,28 @@ CSprinklerDevice::CSprinklerDevice(int Address, CAbstractNetworkCommIF *NetworkInterface, CSprinklerMgr *SprinklerMgr): CNetworkDevice(ID_SPRINKLER_DEVICE,Address,NetworkInterface) { + mSprinklerState = SPRINKLER_UNKNOWN_STATE; + mSprinklerMgr = SprinklerMgr; + mSprinklerStatusTimer = new QTimer(); + + mSprinklerStatusTimer->setInterval(5000); + mSprinklerStatusTimer->setSingleShot(true); + connect(mSprinklerStatusTimer,SIGNAL(timeout()),this,SLOT(StatusTimerExpired())); + mSprinklerStatusTimer->start(); + + } CSprinklerDevice::~CSprinklerDevice() { } +SprinklerState CSprinklerDevice::GetSprinklerState() +{ + return mSprinklerState; +} + int CSprinklerDevice::NewDeviceFrameReceived(int DeviceID, int DeviceAddress, int MessageID, int DataSize, QByteArray Data) { Q_UNUSED(DeviceID) @@ -21,12 +36,31 @@ int CSprinklerDevice::NewDeviceFrameReceived(int DeviceID, int DeviceAddress, in Q_UNUSED(DataSize) Q_UNUSED(Data) - return RET_OK; - switch(MessageID) { case SPRINKLER_DEVICE_STATUS_RESPONSE: { + if(Data.at(0) != (unsigned char)mSprinklerState) + { + if(Data.at(0) == SPRINKLER_ON_STATE || Data.at(0) == SPRINKLER_OFF_STATE) + { + mSprinklerState = (SprinklerState)Data.at(0); + //TODO: emit sprinkler change event. + } + else + { + mSprinklerState = SPRINKLER_UNKNOWN_STATE; + qDebug("Rx Invalid sprinkler state [%d] from device [%d]",Data.at(0),DeviceAddress); + } + } + + mFlowMeter = Data.at(1); + mFlowMeter <<= 8; + mFlowMeter |= (unsigned short)Data.at(2); + + qDebug("Sprinkler status RX. Valve:%d, flow:%d",Data.at(0),mFlowMeter); + + mSprinklerStatusTimer->start(); break; } case SPRINKLER_DEVICE_ACK: @@ -35,14 +69,33 @@ int CSprinklerDevice::NewDeviceFrameReceived(int DeviceID, int DeviceAddress, in } case SPRINKLER_DEVICE_SET_SPRINKLER_STATE_ACK: { + mNetworkInterfacePtr->SendNetworkMessage(ID_SPRINKLER_DEVICE,1,SPRINKLER_DEVICE_GET_SPRINKLER_STATE_REQUEST,0,QByteArray()); break; } case SPRINKLER_DEVICE_GET_SPRINKLER_STATE_RESPONSE: { + if(Data.at(0) != (unsigned char)mSprinklerState) + { + if(Data.at(0) == SPRINKLER_ON_STATE || Data.at(0) == SPRINKLER_OFF_STATE) + { + mSprinklerState = (SprinklerState)Data.at(0); + //TODO: emit sprinkler change event. + } + else + { + mSprinklerState = SPRINKLER_UNKNOWN_STATE; + qDebug("Rx Invalid sprinkler state [%d] from device [%d]",Data.at(0),DeviceAddress); + } + } + qDebug("Valve state RX. Valve:%d",Data.at(0)); break; } case SPRINKLER_DEVICE_GET_WATER_FLOW_RESPONSE: { + mFlowMeter = Data.at(0); + mFlowMeter <<= 8; + mFlowMeter |= (unsigned short)Data.at(1); + qDebug("Flow value RX. flow:%d",mFlowMeter); break; } case SPRINKLER_DEVICE_GET_MOISTURE_RESPONSE: @@ -88,4 +141,7 @@ int CSprinklerDevice::NewDeviceFrameReceived(int DeviceID, int DeviceAddress, in return 0; } - +void CSprinklerDevice::StatusTimerExpired() +{ + mNetworkInterfacePtr->SendNetworkMessage(ID_SPRINKLER_DEVICE,1,SPRINKLER_DEVICE_STATUS_REQUEST,0,QByteArray()); +} diff --git a/Sources/Sprinkler/SprinklerDevice.h b/Sources/Sprinkler/SprinklerDevice.h index 265364d..af9a307 100644 --- a/Sources/Sprinkler/SprinklerDevice.h +++ b/Sources/Sprinkler/SprinklerDevice.h @@ -3,6 +3,14 @@ #include "GlobalDefine.h" #include "NetworkDevice.h" +#include + +typedef enum eSprinklerState +{ + SPRINKLER_OFF_STATE = 0, + SPRINKLER_ON_STATE, + SPRINKLER_UNKNOWN_STATE +}SprinklerState; class CSprinklerMgr; @@ -15,8 +23,16 @@ public: virtual int NewDeviceFrameReceived(int DeviceID, int DeviceAddress, int MessageID, int DataSize, QByteArray Data); + SprinklerState GetSprinklerState(); + unsigned short mFlowMeter; + CSprinklerMgr *mSprinklerMgr; + QTimer *mSprinklerStatusTimer; + SprinklerState mSprinklerState; + +public slots: + void StatusTimerExpired(); }; diff --git a/Sources/Sprinkler/SprinklerInterface.cpp b/Sources/Sprinkler/SprinklerInterface.cpp index d9abd07..11fdda0 100644 --- a/Sources/Sprinkler/SprinklerInterface.cpp +++ b/Sources/Sprinkler/SprinklerInterface.cpp @@ -2,6 +2,7 @@ #include "ProtocolDefs.h" #include #include "SprinklerMgr.h" +#include "SprinklerDevice.h" CSprinklerInterface::CSprinklerInterface(int Address, CAbstractNetworkCommIF *NetworkInterface, CSprinklerMgr *SprinklerMgr): @@ -47,6 +48,23 @@ int CSprinklerInterface::NewDeviceFrameReceived(int DeviceID, int DeviceAddress, } case SPRINKLER_INTERFACE_GET_SPRINKLER_STATE_REQUEST: { +// if(Data.at(0) == 0xFF) +// { +// //send all devices... + +// } +// if(Data.at(0) == 1) +// { + +// } + CSprinklerDevice *Sprinkler = mSprinklerMgr->GetSprinkler(1); + QByteArray Frame, MsgData; + MsgData.append(1); //1 device. + MsgData.append(Sprinkler->mDeviceAddress); //1st device address + MsgData.append((unsigned char)Sprinkler->mSprinklerState); + + mNetworkInterfacePtr->SendNetworkMessage(ID_SPRINKLER_INTERFACE,mDeviceAddress,SPRINKLER_INTERFACE_GET_SPRINKLER_STATE_RESPONSE,MsgData.size(),MsgData); + break; } case SPRINKLER_INTERFACE_SET_SPRINKLER_STATE_REQUEST: diff --git a/Sources/Sprinkler/SprinklerMgr.cpp b/Sources/Sprinkler/SprinklerMgr.cpp index b2b00b3..155cb5e 100644 --- a/Sources/Sprinkler/SprinklerMgr.cpp +++ b/Sources/Sprinkler/SprinklerMgr.cpp @@ -1,6 +1,31 @@ #include "SprinklerMgr.h" +#include "SprinklerDevice.h" CSprinklerMgr::CSprinklerMgr(QObject *parent) : QObject(parent) { mProgramHandle = 0; } + +int CSprinklerMgr::NewSprinklerDeviceCreated(CSprinklerDevice *Device) +{ + mSprinklersList.append(Device); + return RET_OK; +} + +CSprinklerDevice* CSprinklerMgr::GetSprinkler(int SprinklerNetworkAddress) +{ + if(mSprinklersList.isEmpty()) + { + return 0; + } + + for(int i = 0; i < mSprinklersList.size(); i++) + { + if(mSprinklersList.at(i)->mDeviceAddress == SprinklerNetworkAddress) + { + return mSprinklersList.at(i); + } + } + + return 0; +} diff --git a/Sources/Sprinkler/SprinklerMgr.h b/Sources/Sprinkler/SprinklerMgr.h index d5c0b1e..a49ce6b 100644 --- a/Sources/Sprinkler/SprinklerMgr.h +++ b/Sources/Sprinkler/SprinklerMgr.h @@ -2,9 +2,11 @@ #define SPRINKLERMGR_H #include +#include class CMasterCtrl; +class CSprinklerDevice; class CSprinklerMgr : public QObject { @@ -13,6 +15,11 @@ public: explicit CSprinklerMgr(QObject *parent = 0); CMasterCtrl *mProgramHandle; + QList mSprinklersList; + + + int NewSprinklerDeviceCreated(CSprinklerDevice* Device); + CSprinklerDevice* GetSprinkler(int SprinklerNetworkAddress); signals: diff --git a/Sources/VoipSMS/VoipMsSMSClient.cpp b/Sources/VoipSMS/VoipMsSMSClient.cpp index 72b2ea7..2ee1d84 100644 --- a/Sources/VoipSMS/VoipMsSMSClient.cpp +++ b/Sources/VoipSMS/VoipMsSMSClient.cpp @@ -138,7 +138,7 @@ int CVoipMsSMSClient::DownloadNewSMS() int CVoipMsSMSClient::CheckForNewSMS() { // qDebug("Check new SMS"); - qDebug() << QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss") << ":" << "Check New SMS"; + // qDebug() << QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss") << ":" << "Check New SMS"; if(mSMSMessagesList.isEmpty() == true) { return RET_ERROR;