File indexing completed on 2024-05-12 15:27:31

0001 /***************************************************************************
0002     File                 : Image.cpp
0003     Project              : LabPlot
0004     Description          : Worksheet element to draw images
0005     --------------------------------------------------------------------
0006     Copyright            : (C) 2019 Alexander Semke (alexander.semke@web.de)
0007  ***************************************************************************/
0008 
0009 /***************************************************************************
0010  *                                                                         *
0011  *  This program is free software; you can redistribute it and/or modify   *
0012  *  it under the terms of the GNU General Public License as published by   *
0013  *  the Free Software Foundation; either version 2 of the License, or      *
0014  *  (at your option) any later version.                                    *
0015  *                                                                         *
0016  *  This program is distributed in the hope that it will be useful,        *
0017  *  but WITHOUT ANY WARRANTY; without even the implied warranty of         *
0018  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
0019  *  GNU General Public License for more details.                           *
0020  *                                                                         *
0021  *   You should have received a copy of the GNU General Public License     *
0022  *   along with this program; if not, write to the Free Software           *
0023  *   Foundation, Inc., 51 Franklin Street, Fifth Floor,                    *
0024  *   Boston, MA  02110-1301  USA                                           *
0025  *                                                                         *
0026  ***************************************************************************/
0027 
0028 #include "Image.h"
0029 #include "Worksheet.h"
0030 #include "ImagePrivate.h"
0031 #include "backend/lib/commandtemplates.h"
0032 #include "backend/lib/XmlStreamReader.h"
0033 
0034 #include <QPainter>
0035 #include <QGraphicsScene>
0036 #include <QGraphicsSceneMouseEvent>
0037 #include <QMenu>
0038 
0039 #include <QIcon>
0040 #include <KConfig>
0041 #include <KConfigGroup>
0042 #include <KLocalizedString>
0043 
0044 /**
0045  * \class Image
0046  * \brief A label supporting rendering of html- and tex-formatted texts.
0047  *
0048  * The label is aligned relative to the specified position.
0049  * The position can be either specified by providing the x- and y- coordinates
0050  * in parent's coordinate system, or by specifying one of the predefined position
0051  * flags (\c HorizontalPosition, \c VerticalPosition).
0052  */
0053 
0054 
0055 Image::Image(const QString& name)
0056     : WorksheetElement(name, AspectType::Image), d_ptr(new ImagePrivate(this)) {
0057 
0058     init();
0059 }
0060 
0061 Image::Image(const QString &name, ImagePrivate *dd)
0062     : WorksheetElement(name, AspectType::Image), d_ptr(dd) {
0063 
0064     init();
0065 }
0066 
0067 void Image::init() {
0068     Q_D(Image);
0069 
0070     KConfig config;
0071     KConfigGroup group = config.group("Image");
0072 
0073     d->opacity = group.readEntry("opacity", d->opacity);
0074 
0075     //geometry
0076     d->position.point.setX( group.readEntry("PositionXValue", d->position.point.x()) );
0077     d->position.point.setY( group.readEntry("PositionYValue", d->position.point.y()) );
0078     d->position.horizontalPosition = (WorksheetElement::HorizontalPosition) group.readEntry("PositionX", (int)d->position.horizontalPosition);
0079     d->position.verticalPosition = (WorksheetElement::VerticalPosition) group.readEntry("PositionY", (int)d->position.verticalPosition);
0080     d->horizontalAlignment = (WorksheetElement::HorizontalAlignment) group.readEntry("HorizontalAlignment", (int)d->horizontalAlignment);
0081     d->verticalAlignment = (WorksheetElement::VerticalAlignment) group.readEntry("VerticalAlignment", (int)d->verticalAlignment);
0082     d->rotationAngle = group.readEntry("Rotation", d->rotationAngle);
0083 
0084     //border
0085     d->borderPen = QPen(group.readEntry("BorderColor", d->borderPen.color()),
0086                         group.readEntry("BorderWidth", d->borderPen.widthF()),
0087                         (Qt::PenStyle) group.readEntry("BorderStyle", (int)(d->borderPen.style())));
0088     d->borderOpacity = group.readEntry("BorderOpacity", d->borderOpacity);
0089 
0090     //initial placeholder image
0091     d->image = QIcon::fromTheme("viewimage").pixmap(d->width, d->height).toImage();
0092 }
0093 
0094 //no need to delete the d-pointer here - it inherits from QGraphicsItem
0095 //and is deleted during the cleanup in QGraphicsScene
0096 Image::~Image() = default;
0097 
0098 QGraphicsItem* Image::graphicsItem() const {
0099     return d_ptr;
0100 }
0101 
0102 void Image::setParentGraphicsItem(QGraphicsItem* item) {
0103     Q_D(Image);
0104     d->setParentItem(item);
0105     d->updatePosition();
0106 }
0107 
0108 void Image::retransform() {
0109     Q_D(Image);
0110     d->retransform();
0111 }
0112 
0113 void Image::handleResize(double horizontalRatio, double verticalRatio, bool pageResize) {
0114     DEBUG("Image::handleResize()");
0115     Q_UNUSED(pageResize);
0116     Q_UNUSED(horizontalRatio);
0117     Q_UNUSED(verticalRatio);
0118 //  Q_D(Image);
0119 
0120 //  double ratio = 0;
0121 //  if (horizontalRatio > 1.0 || verticalRatio > 1.0)
0122 //      ratio = qMax(horizontalRatio, verticalRatio);
0123 //  else
0124 //      ratio = qMin(horizontalRatio, verticalRatio);
0125 }
0126 
0127 /*!
0128     Returns an icon to be used in the project explorer.
0129 */
0130 QIcon Image::icon() const{
0131     return QIcon::fromTheme("viewimage");
0132 }
0133 
0134 QMenu* Image::createContextMenu() {
0135     QMenu *menu = WorksheetElement::createContextMenu();
0136     QAction* firstAction = menu->actions().at(1); //skip the first action because of the "title-action"
0137 
0138     if (!visibilityAction) {
0139         visibilityAction = new QAction(i18n("Visible"), this);
0140         visibilityAction->setCheckable(true);
0141         connect(visibilityAction, &QAction::triggered, this, &Image::visibilityChanged);
0142     }
0143 
0144     visibilityAction->setChecked(isVisible());
0145     menu->insertAction(firstAction, visibilityAction);
0146     menu->insertSeparator(firstAction);
0147 
0148     return menu;
0149 }
0150 
0151 /* ============================ getter methods ================= */
0152 CLASS_SHARED_D_READER_IMPL(Image, QString, fileName, fileName)
0153 BASIC_SHARED_D_READER_IMPL(Image, qreal, opacity, opacity)
0154 BASIC_SHARED_D_READER_IMPL(Image, int, width, width)
0155 BASIC_SHARED_D_READER_IMPL(Image, int, height, height)
0156 BASIC_SHARED_D_READER_IMPL(Image, bool, keepRatio, keepRatio)
0157 CLASS_SHARED_D_READER_IMPL(Image, WorksheetElement::PositionWrapper, position, position)
0158 BASIC_SHARED_D_READER_IMPL(Image, WorksheetElement::HorizontalAlignment, horizontalAlignment, horizontalAlignment)
0159 BASIC_SHARED_D_READER_IMPL(Image, WorksheetElement::VerticalAlignment, verticalAlignment, verticalAlignment)
0160 BASIC_SHARED_D_READER_IMPL(Image, qreal, rotationAngle, rotationAngle)
0161 
0162 CLASS_SHARED_D_READER_IMPL(Image, QPen, borderPen, borderPen)
0163 BASIC_SHARED_D_READER_IMPL(Image, qreal, borderOpacity, borderOpacity)
0164 
0165 /* ============================ setter methods and undo commands ================= */
0166 STD_SETTER_CMD_IMPL_F_S(Image, SetFileName, QString, fileName, updateImage)
0167 void Image::setFileName(const QString& fileName) {
0168     Q_D(Image);
0169     if (fileName != d->fileName)
0170         exec(new ImageSetFileNameCmd(d, fileName, ki18n("%1: set image")));
0171 }
0172 
0173 STD_SETTER_CMD_IMPL_F_S(Image, SetOpacity, qreal, opacity, update)
0174 void Image::setOpacity(qreal opacity) {
0175     Q_D(Image);
0176     if (opacity != d->opacity)
0177         exec(new ImageSetOpacityCmd(d, opacity, ki18n("%1: set border opacity")));
0178 }
0179 
0180 STD_SETTER_CMD_IMPL_F_S(Image, SetWidth, int, width, scaleImage)
0181 void Image::setWidth(int width) {
0182     Q_D(Image);
0183     if (width != d->width)
0184         exec(new ImageSetWidthCmd(d, width, ki18n("%1: set width")));
0185 }
0186 
0187 STD_SETTER_CMD_IMPL_F_S(Image, SetHeight, int, height, scaleImage)
0188 void Image::setHeight(int height) {
0189     Q_D(Image);
0190     if (height != d->height)
0191         exec(new ImageSetHeightCmd(d, height, ki18n("%1: set height")));
0192 }
0193 
0194 STD_SETTER_CMD_IMPL_S(Image, SetKeepRatio, bool, keepRatio)
0195 void Image::setKeepRatio(bool keepRatio) {
0196     Q_D(Image);
0197     if (keepRatio != d->keepRatio)
0198         exec(new ImageSetKeepRatioCmd(d, keepRatio, ki18n("%1: change keep ratio")));
0199 }
0200 
0201 STD_SETTER_CMD_IMPL_F_S(Image, SetPosition, WorksheetElement::PositionWrapper, position, retransform);
0202 void Image::setPosition(const WorksheetElement::PositionWrapper& pos) {
0203     Q_D(Image);
0204     if (pos.point != d->position.point || pos.horizontalPosition != d->position.horizontalPosition || pos.verticalPosition != d->position.verticalPosition)
0205         exec(new ImageSetPositionCmd(d, pos, ki18n("%1: set position")));
0206 }
0207 
0208 /*!
0209     sets the position without undo/redo-stuff
0210 */
0211 void Image::setPosition(QPointF point) {
0212     Q_D(Image);
0213     if (point != d->position.point) {
0214         d->position.point = point;
0215         retransform();
0216     }
0217 }
0218 
0219 STD_SETTER_CMD_IMPL_F_S(Image, SetRotationAngle, qreal, rotationAngle, recalcShapeAndBoundingRect);
0220 void Image::setRotationAngle(qreal angle) {
0221     Q_D(Image);
0222     if (angle != d->rotationAngle)
0223         exec(new ImageSetRotationAngleCmd(d, angle, ki18n("%1: set rotation angle")));
0224 }
0225 
0226 STD_SETTER_CMD_IMPL_F_S(Image, SetHorizontalAlignment, WorksheetElement::HorizontalAlignment, horizontalAlignment, retransform);
0227 void Image::setHorizontalAlignment(const WorksheetElement::HorizontalAlignment hAlign) {
0228     Q_D(Image);
0229     if (hAlign != d->horizontalAlignment)
0230         exec(new ImageSetHorizontalAlignmentCmd(d, hAlign, ki18n("%1: set horizontal alignment")));
0231 }
0232 
0233 STD_SETTER_CMD_IMPL_F_S(Image, SetVerticalAlignment, WorksheetElement::VerticalAlignment, verticalAlignment, retransform);
0234 void Image::setVerticalAlignment(const WorksheetElement::VerticalAlignment vAlign) {
0235     Q_D(Image);
0236     if (vAlign != d->verticalAlignment)
0237         exec(new ImageSetVerticalAlignmentCmd(d, vAlign, ki18n("%1: set vertical alignment")));
0238 }
0239 
0240 //Border
0241 STD_SETTER_CMD_IMPL_F_S(Image, SetBorderPen, QPen, borderPen, update)
0242 void Image::setBorderPen(const QPen &pen) {
0243     Q_D(Image);
0244     if (pen != d->borderPen)
0245         exec(new ImageSetBorderPenCmd(d, pen, ki18n("%1: set border")));
0246 }
0247 
0248 STD_SETTER_CMD_IMPL_F_S(Image, SetBorderOpacity, qreal, borderOpacity, update)
0249 void Image::setBorderOpacity(qreal opacity) {
0250     Q_D(Image);
0251     if (opacity != d->borderOpacity)
0252         exec(new ImageSetBorderOpacityCmd(d, opacity, ki18n("%1: set border opacity")));
0253 }
0254 
0255 //misc
0256 STD_SWAP_METHOD_SETTER_CMD_IMPL_F(Image, SetVisible, bool, swapVisible, retransform);
0257 void Image::setVisible(bool on) {
0258     Q_D(Image);
0259     exec(new ImageSetVisibleCmd(d, on, on ? ki18n("%1: set visible") : ki18n("%1: set invisible")));
0260 }
0261 
0262 bool Image::isVisible() const {
0263     Q_D(const Image);
0264     return d->isVisible();
0265 }
0266 
0267 void Image::setPrinting(bool on) {
0268     Q_D(Image);
0269     d->m_printing = on;
0270 }
0271 
0272 //##############################################################################
0273 //######  SLOTs for changes triggered via QActions in the context menu  ########
0274 //##############################################################################
0275 void Image::visibilityChanged() {
0276     Q_D(const Image);
0277     this->setVisible(!d->isVisible());
0278 }
0279 
0280 //##############################################################################
0281 //####################### Private implementation ###############################
0282 //##############################################################################
0283 ImagePrivate::ImagePrivate(Image* owner) : q(owner) {
0284     setFlag(QGraphicsItem::ItemIsSelectable);
0285     setFlag(QGraphicsItem::ItemIsMovable);
0286     setFlag(QGraphicsItem::ItemSendsGeometryChanges);
0287     setAcceptHoverEvents(true);
0288 }
0289 
0290 QString ImagePrivate::name() const {
0291     return q->name();
0292 }
0293 
0294 /*!
0295     calculates the position and the bounding box of the label. Called on geometry or text changes.
0296  */
0297 void ImagePrivate::retransform() {
0298     if (suppressRetransform)
0299         return;
0300 
0301     if (position.horizontalPosition != WorksheetElement::HorizontalPosition::Custom
0302             || position.verticalPosition != WorksheetElement::VerticalPosition::Custom)
0303         updatePosition();
0304 
0305     float x = position.point.x();
0306     float y = position.point.y();
0307     float w = image.width();
0308     float h = image.height();
0309 
0310     //depending on the alignment, calculate the new GraphicsItem's position in parent's coordinate system
0311     QPointF itemPos;
0312     switch (horizontalAlignment) {
0313     case WorksheetElement::HorizontalAlignment::Left:
0314         itemPos.setX(x - w/2);
0315         break;
0316     case WorksheetElement::HorizontalAlignment::Center:
0317         itemPos.setX(x);
0318         break;
0319     case WorksheetElement::HorizontalAlignment::Right:
0320         itemPos.setX(x + w/2);
0321         break;
0322     }
0323 
0324     switch (verticalAlignment) {
0325     case WorksheetElement::VerticalAlignment::Top:
0326         itemPos.setY(y - h/2);
0327         break;
0328     case WorksheetElement::VerticalAlignment::Center:
0329         itemPos.setY(y);
0330         break;
0331     case WorksheetElement::VerticalAlignment::Bottom:
0332         itemPos.setY(y + h/2);
0333         break;
0334     }
0335 
0336     suppressItemChangeEvent = true;
0337     setPos(itemPos);
0338     suppressItemChangeEvent = false;
0339 
0340     boundingRectangle.setX(-w/2);
0341     boundingRectangle.setY(-h/2);
0342     boundingRectangle.setWidth(w);
0343     boundingRectangle.setHeight(h);
0344 
0345     updateBorder();
0346 }
0347 
0348 void ImagePrivate::updateImage() {
0349     if (!fileName.isEmpty()) {
0350         image = QImage(fileName);
0351         width = image.width();
0352         height = image.height();
0353         q->widthChanged(width);
0354         q->heightChanged(height);
0355     } else {
0356         width = Worksheet::convertToSceneUnits(2, Worksheet::Unit::Centimeter);
0357         height = Worksheet::convertToSceneUnits(3, Worksheet::Unit::Centimeter);
0358         image = QIcon::fromTheme("viewimage").pixmap(width, height).toImage();
0359         q->widthChanged(width);
0360         q->heightChanged(height);
0361     }
0362 
0363     retransform();
0364 }
0365 
0366 void ImagePrivate::scaleImage() {
0367     if (keepRatio) {
0368         if (width != image.width()) {
0369             //width was changed -> rescale the height to keep the ratio
0370             if (image.width() != 0)
0371                 height = image.height()*width/image.width();
0372             else
0373                 height = 0;
0374             q->heightChanged(height);
0375         } else {
0376             //height was changed -> rescale the width to keep the ratio
0377             if (image.height() != 0)
0378                 width = image.width()*height/image.height();
0379             else
0380                 width = 0;
0381             q->widthChanged(width);
0382         }
0383     }
0384 
0385     if (width != 0 && height != 0)
0386         image = image.scaled(width, height, Qt::KeepAspectRatio, Qt::SmoothTransformation);
0387     retransform();
0388 }
0389 
0390 /*!
0391     calculates the position of the label, when the position relative to the parent was specified (left, right, etc.)
0392 */
0393 void ImagePrivate::updatePosition() {
0394     //determine the parent item
0395     QRectF parentRect;
0396     QGraphicsItem* parent = parentItem();
0397     if (parent) {
0398         parentRect = parent->boundingRect();
0399     } else {
0400         if (!scene())
0401             return;
0402 
0403         parentRect = scene()->sceneRect();
0404     }
0405 
0406     if (position.horizontalPosition != WorksheetElement::HorizontalPosition::Custom) {
0407         if (position.horizontalPosition == WorksheetElement::HorizontalPosition::Left)
0408             position.point.setX( parentRect.x() );
0409         else if (position.horizontalPosition == WorksheetElement::HorizontalPosition::Center)
0410             position.point.setX( parentRect.x() + parentRect.width()/2 );
0411         else if (position.horizontalPosition == WorksheetElement::HorizontalPosition::Right)
0412             position.point.setX( parentRect.x() + parentRect.width() );
0413     }
0414 
0415     if (position.verticalPosition != WorksheetElement::VerticalPosition::Custom) {
0416         if (position.verticalPosition == WorksheetElement::VerticalPosition::Top)
0417             position.point.setY( parentRect.y() );
0418         else if (position.verticalPosition == WorksheetElement::VerticalPosition::Center)
0419             position.point.setY( parentRect.y() + parentRect.height()/2 );
0420         else if (position.verticalPosition == WorksheetElement::VerticalPosition::Bottom)
0421             position.point.setY( parentRect.y() + parentRect.height() );
0422     }
0423 
0424     emit q->positionChanged(position);
0425 }
0426 
0427 void ImagePrivate::updateBorder() {
0428     borderShapePath = QPainterPath();
0429     borderShapePath.addRect(boundingRectangle);
0430     recalcShapeAndBoundingRect();
0431 }
0432 
0433 bool ImagePrivate::swapVisible(bool on) {
0434     bool oldValue = isVisible();
0435     setVisible(on);
0436     emit q->changed();
0437     emit q->visibleChanged(on);
0438     return oldValue;
0439 }
0440 
0441 /*!
0442     Returns the outer bounds of the item as a rectangle.
0443  */
0444 QRectF ImagePrivate::boundingRect() const {
0445     return transformedBoundingRectangle;
0446 }
0447 
0448 /*!
0449     Returns the shape of this item as a QPainterPath in local coordinates.
0450 */
0451 QPainterPath ImagePrivate::shape() const {
0452     return imageShape;
0453 }
0454 
0455 /*!
0456   recalculates the outer bounds and the shape of the label.
0457 */
0458 void ImagePrivate::recalcShapeAndBoundingRect() {
0459     prepareGeometryChange();
0460 
0461     QMatrix matrix;
0462     matrix.rotate(-rotationAngle);
0463     imageShape = QPainterPath();
0464     if (borderPen.style() != Qt::NoPen) {
0465         imageShape.addPath(WorksheetElement::shapeFromPath(borderShapePath, borderPen));
0466         transformedBoundingRectangle = matrix.mapRect(imageShape.boundingRect());
0467     } else {
0468         imageShape.addRect(boundingRectangle);
0469         transformedBoundingRectangle = matrix.mapRect(boundingRectangle);
0470     }
0471 
0472     imageShape = matrix.map(imageShape);
0473 
0474     emit q->changed();
0475 }
0476 
0477 void ImagePrivate::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) {
0478     Q_UNUSED(option)
0479     Q_UNUSED(widget)
0480 
0481     painter->save();
0482 
0483     //draw the image
0484     painter->rotate(-rotationAngle);
0485     painter->setOpacity(opacity);
0486     painter->drawImage(boundingRectangle.topLeft(), image, image.rect());
0487     painter->restore();
0488 
0489     //draw the border
0490     if (borderPen.style() != Qt::NoPen) {
0491         painter->save();
0492         painter->rotate(-rotationAngle);
0493         painter->setPen(borderPen);
0494         painter->setOpacity(borderOpacity);
0495         painter->drawPath(borderShapePath);
0496         painter->restore();
0497     }
0498 
0499     if (m_hovered && !isSelected() && !m_printing) {
0500         painter->setPen(QPen(QApplication::palette().color(QPalette::Shadow), 2, Qt::SolidLine));
0501         painter->drawPath(imageShape);
0502     }
0503 
0504     if (isSelected() && !m_printing) {
0505         painter->setPen(QPen(QApplication::palette().color(QPalette::Highlight), 2, Qt::SolidLine));
0506         painter->drawPath(imageShape);
0507     }
0508 }
0509 
0510 QVariant ImagePrivate::itemChange(GraphicsItemChange change, const QVariant &value) {
0511     if (suppressItemChangeEvent)
0512         return value;
0513 
0514     if (change == QGraphicsItem::ItemPositionChange) {
0515         //convert item's center point in parent's coordinates
0516         WorksheetElement::PositionWrapper tempPosition;
0517         tempPosition.point = positionFromItemPosition(value.toPointF());
0518         tempPosition.horizontalPosition = WorksheetElement::HorizontalPosition::Custom;
0519         tempPosition.verticalPosition = WorksheetElement::VerticalPosition::Custom;
0520 
0521         //emit the signals in order to notify the UI.
0522         //we don't set the position related member variables during the mouse movements.
0523         //this is done on mouse release events only.
0524         emit q->positionChanged(tempPosition);
0525     }
0526 
0527     return QGraphicsItem::itemChange(change, value);
0528 }
0529 
0530 void ImagePrivate::mouseReleaseEvent(QGraphicsSceneMouseEvent* event) {
0531     //convert position of the item in parent coordinates to label's position
0532     QPointF point = positionFromItemPosition(pos());
0533     if (qAbs(point.x()-position.point.x())>20 && qAbs(point.y()-position.point.y())>20 ) {
0534         //position was changed -> set the position related member variables
0535         suppressRetransform = true;
0536         WorksheetElement::PositionWrapper tempPosition;
0537         tempPosition.point = point;
0538         tempPosition.horizontalPosition = WorksheetElement::HorizontalPosition::Custom;
0539         tempPosition.verticalPosition = WorksheetElement::VerticalPosition::Custom;
0540         q->setPosition(tempPosition);
0541         suppressRetransform = false;
0542     }
0543 
0544     QGraphicsItem::mouseReleaseEvent(event);
0545 }
0546 
0547 /*!
0548  *  converts label's position to GraphicsItem's position.
0549  */
0550 QPointF ImagePrivate::positionFromItemPosition(QPointF itemPos) {
0551     float x = itemPos.x();
0552     float y = itemPos.y();
0553     float w = image.width();
0554     float h = image.height();
0555     QPointF tmpPosition;
0556 
0557     //depending on the alignment, calculate the new position
0558     switch (horizontalAlignment) {
0559     case WorksheetElement::HorizontalAlignment::Left:
0560         tmpPosition.setX(x + w/2);
0561         break;
0562     case WorksheetElement::HorizontalAlignment::Center:
0563         tmpPosition.setX(x);
0564         break;
0565     case WorksheetElement::HorizontalAlignment::Right:
0566         tmpPosition.setX(x - w/2);
0567         break;
0568     }
0569 
0570     switch (verticalAlignment) {
0571     case WorksheetElement::VerticalAlignment::Top:
0572         tmpPosition.setY(y + h/2);
0573         break;
0574     case WorksheetElement::VerticalAlignment::Center:
0575         tmpPosition.setY(y);
0576         break;
0577     case WorksheetElement::VerticalAlignment::Bottom:
0578         tmpPosition.setY(y - h/2);
0579         break;
0580     }
0581 
0582     return tmpPosition;
0583 }
0584 
0585 void ImagePrivate::contextMenuEvent(QGraphicsSceneContextMenuEvent* event) {
0586     q->createContextMenu()->exec(event->screenPos());
0587 }
0588 
0589 void ImagePrivate::hoverEnterEvent(QGraphicsSceneHoverEvent*) {
0590     if (!isSelected()) {
0591         m_hovered = true;
0592         emit q->hovered();
0593         update();
0594     }
0595 }
0596 
0597 void ImagePrivate::hoverLeaveEvent(QGraphicsSceneHoverEvent*) {
0598     if (m_hovered) {
0599         m_hovered = false;
0600         emit q->unhovered();
0601         update();
0602     }
0603 }
0604 
0605 //##############################################################################
0606 //##################  Serialization/Deserialization  ###########################
0607 //##############################################################################
0608 //! Save as XML
0609 void Image::save(QXmlStreamWriter* writer) const {
0610     Q_D(const Image);
0611 
0612     writer->writeStartElement("image");
0613     writeBasicAttributes(writer);
0614     writeCommentElement(writer);
0615 
0616     //general
0617     writer->writeStartElement("general");
0618     writer->writeAttribute("fileName", d->fileName);
0619     writer->writeAttribute("opacity", QString::number(d->opacity));
0620     writer->writeEndElement();
0621 
0622     //geometry
0623     writer->writeStartElement("geometry");
0624     writer->writeAttribute("width", QString::number(d->width));
0625     writer->writeAttribute("height", QString::number(d->height));
0626     writer->writeAttribute("keepRatio", QString::number(d->keepRatio));
0627     writer->writeAttribute("x", QString::number(d->position.point.x()));
0628     writer->writeAttribute("y", QString::number(d->position.point.y()));
0629     writer->writeAttribute("horizontalPosition", QString::number(static_cast<int>(d->position.horizontalPosition)));
0630     writer->writeAttribute("verticalPosition", QString::number(static_cast<int>(d->position.verticalPosition)));
0631     writer->writeAttribute("horizontalAlignment", QString::number(static_cast<int>(d->horizontalAlignment)));
0632     writer->writeAttribute("verticalAlignment", QString::number(static_cast<int>(d->verticalAlignment)));
0633     writer->writeAttribute("rotationAngle", QString::number(d->rotationAngle));
0634     writer->writeAttribute("visible", QString::number(d->isVisible()));
0635     writer->writeEndElement();
0636 
0637     //border
0638     writer->writeStartElement("border");
0639     WRITE_QPEN(d->borderPen);
0640     writer->writeAttribute("borderOpacity", QString::number(d->borderOpacity));
0641     writer->writeEndElement();
0642 
0643     writer->writeEndElement(); // close "image" section
0644 }
0645 
0646 //! Load from XML
0647 bool Image::load(XmlStreamReader* reader, bool preview) {
0648     if (!readBasicAttributes(reader))
0649         return false;
0650 
0651     Q_D(Image);
0652     KLocalizedString attributeWarning = ki18n("Attribute '%1' missing or empty, default value is used");
0653     QXmlStreamAttributes attribs;
0654     QString str;
0655 
0656     while (!reader->atEnd()) {
0657         reader->readNext();
0658         if (reader->isEndElement() && reader->name() == "image")
0659             break;
0660 
0661         if (!reader->isStartElement())
0662             continue;
0663 
0664         if (!preview && reader->name() == "comment") {
0665             if (!readCommentElement(reader)) return false;
0666         } else if (!preview && reader->name() == "general") {
0667             attribs = reader->attributes();
0668             d->fileName = attribs.value("fileName").toString();
0669             READ_DOUBLE_VALUE("opacity", opacity);
0670         } else if (!preview && reader->name() == "geometry") {
0671             attribs = reader->attributes();
0672 
0673             READ_INT_VALUE("width", width, int);
0674             READ_INT_VALUE("height", height, int);
0675             READ_INT_VALUE("keepRatio", keepRatio, bool);
0676 
0677             str = attribs.value("x").toString();
0678             if (str.isEmpty())
0679                 reader->raiseWarning(attributeWarning.subs("x").toString());
0680             else
0681                 d->position.point.setX(str.toDouble());
0682 
0683             str = attribs.value("y").toString();
0684             if (str.isEmpty())
0685                 reader->raiseWarning(attributeWarning.subs("y").toString());
0686             else
0687                 d->position.point.setY(str.toDouble());
0688 
0689             READ_INT_VALUE("horizontalPosition", position.horizontalPosition, WorksheetElement::HorizontalPosition);
0690             READ_INT_VALUE("verticalPosition", position.verticalPosition, WorksheetElement::VerticalPosition);
0691             READ_INT_VALUE("horizontalAlignment", horizontalAlignment, WorksheetElement::HorizontalAlignment);
0692             READ_INT_VALUE("verticalAlignment", verticalAlignment, WorksheetElement::VerticalAlignment);
0693             READ_DOUBLE_VALUE("rotationAngle", rotationAngle);
0694 
0695             str = attribs.value("visible").toString();
0696             if (str.isEmpty())
0697                 reader->raiseWarning(attributeWarning.subs("visible").toString());
0698             else
0699                 d->setVisible(str.toInt());
0700         } else if (!preview && reader->name() == "border") {
0701             attribs = reader->attributes();
0702             READ_QPEN(d->borderPen);
0703             READ_DOUBLE_VALUE("borderOpacity", borderOpacity);
0704         } else { // unknown element
0705             reader->raiseWarning(i18n("unknown element '%1'", reader->name().toString()));
0706             if (!reader->skipToEndElement()) return false;
0707         }
0708     }
0709 
0710     if (!preview) {
0711         d->image = QImage(d->fileName);
0712         d->scaleImage();
0713     }
0714 
0715     return true;
0716 }
0717 
0718 //##############################################################################
0719 //#########################  Theme management ##################################
0720 //##############################################################################
0721 void Image::loadThemeConfig(const KConfig& config) {
0722     Q_UNUSED(config)
0723 }
0724 
0725 void Image::saveThemeConfig(const KConfig& config) {
0726     Q_UNUSED(config)
0727 }